summaryrefslogtreecommitdiffstats
path: root/comm/third_party/botan
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 17:32:43 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 17:32:43 +0000
commit6bf0a5cb5034a7e684dcc3500e841785237ce2dd (patch)
treea68f146d7fa01f0134297619fbe7e33db084e0aa /comm/third_party/botan
parentInitial commit. (diff)
downloadthunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.tar.xz
thunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.zip
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'comm/third_party/botan')
-rw-r--r--comm/third_party/botan/Makefile.in12
-rw-r--r--comm/third_party/botan/botan.mozbuild238
-rwxr-xr-xcomm/third_party/botan/botan_configure.py121
-rwxr-xr-xcomm/third_party/botan/configure.py3454
-rw-r--r--comm/third_party/botan/doc/abi.rst21
-rw-r--r--comm/third_party/botan/doc/api_ref/bigint.rst279
-rw-r--r--comm/third_party/botan/doc/api_ref/block_cipher.rst364
-rw-r--r--comm/third_party/botan/doc/api_ref/cipher_modes.rst384
-rw-r--r--comm/third_party/botan/doc/api_ref/compression.rst90
-rw-r--r--comm/third_party/botan/doc/api_ref/contents.rst39
-rw-r--r--comm/third_party/botan/doc/api_ref/credentials_manager.rst186
-rw-r--r--comm/third_party/botan/doc/api_ref/cryptobox.rst32
-rw-r--r--comm/third_party/botan/doc/api_ref/ecc.rst284
-rw-r--r--comm/third_party/botan/doc/api_ref/env_vars.rst20
-rw-r--r--comm/third_party/botan/doc/api_ref/ffi.rst1203
-rw-r--r--comm/third_party/botan/doc/api_ref/filters.rst733
-rw-r--r--comm/third_party/botan/doc/api_ref/fpe.rst98
-rw-r--r--comm/third_party/botan/doc/api_ref/hash.rst351
-rw-r--r--comm/third_party/botan/doc/api_ref/kdf.rst109
-rw-r--r--comm/third_party/botan/doc/api_ref/keywrap.rst60
-rw-r--r--comm/third_party/botan/doc/api_ref/message_auth_codes.rst268
-rw-r--r--comm/third_party/botan/doc/api_ref/otp.rst98
-rw-r--r--comm/third_party/botan/doc/api_ref/passhash.rst219
-rw-r--r--comm/third_party/botan/doc/api_ref/pbkdf.rst190
-rw-r--r--comm/third_party/botan/doc/api_ref/pkcs11.rst1419
-rw-r--r--comm/third_party/botan/doc/api_ref/psk_db.rst110
-rw-r--r--comm/third_party/botan/doc/api_ref/pubkey.rst954
-rw-r--r--comm/third_party/botan/doc/api_ref/python.rst668
-rw-r--r--comm/third_party/botan/doc/api_ref/rng.rst281
-rw-r--r--comm/third_party/botan/doc/api_ref/roughtime.rst6
-rw-r--r--comm/third_party/botan/doc/api_ref/secmem.rst31
-rw-r--r--comm/third_party/botan/doc/api_ref/srp.rst77
-rw-r--r--comm/third_party/botan/doc/api_ref/stream_ciphers.rst211
-rw-r--r--comm/third_party/botan/doc/api_ref/tls.rst1926
-rw-r--r--comm/third_party/botan/doc/api_ref/tpm.rst113
-rw-r--r--comm/third_party/botan/doc/api_ref/tss.rst45
-rw-r--r--comm/third_party/botan/doc/api_ref/versions.rst100
-rw-r--r--comm/third_party/botan/doc/api_ref/x509.rst914
-rw-r--r--comm/third_party/botan/doc/authors.txt102
-rw-r--r--comm/third_party/botan/doc/building.rst1019
-rw-r--r--comm/third_party/botan/doc/cli.rst406
-rw-r--r--comm/third_party/botan/doc/contents.rst25
-rw-r--r--comm/third_party/botan/doc/credits.rst156
-rw-r--r--comm/third_party/botan/doc/deprecated.rst302
-rw-r--r--comm/third_party/botan/doc/dev_ref/configure.rst407
-rw-r--r--comm/third_party/botan/doc/dev_ref/contents.rst20
-rw-r--r--comm/third_party/botan/doc/dev_ref/continuous_integration.rst75
-rw-r--r--comm/third_party/botan/doc/dev_ref/contributing.rst268
-rw-r--r--comm/third_party/botan/doc/dev_ref/fuzzing.rst91
-rw-r--r--comm/third_party/botan/doc/dev_ref/mistakes.rst77
-rw-r--r--comm/third_party/botan/doc/dev_ref/oids.rst43
-rw-r--r--comm/third_party/botan/doc/dev_ref/os.rst61
-rw-r--r--comm/third_party/botan/doc/dev_ref/reading_list.rst93
-rw-r--r--comm/third_party/botan/doc/dev_ref/release_process.rst129
-rw-r--r--comm/third_party/botan/doc/dev_ref/test_framework.rst314
-rw-r--r--comm/third_party/botan/doc/dev_ref/todo.rst199
-rw-r--r--comm/third_party/botan/doc/goals.rst131
-rw-r--r--comm/third_party/botan/doc/index.rst58
-rw-r--r--comm/third_party/botan/doc/old_news.rst4336
-rw-r--r--comm/third_party/botan/doc/packaging.rst59
-rw-r--r--comm/third_party/botan/doc/pgpkey.txt198
-rw-r--r--comm/third_party/botan/doc/roadmap.rst54
-rw-r--r--comm/third_party/botan/doc/security.rst352
-rw-r--r--comm/third_party/botan/doc/side_channels.rst449
-rw-r--r--comm/third_party/botan/doc/support.rst70
-rw-r--r--comm/third_party/botan/license.txt24
-rw-r--r--comm/third_party/botan/moz.build14
-rw-r--r--comm/third_party/botan/news.rst1879
-rw-r--r--comm/third_party/botan/readme.rst135
-rw-r--r--comm/third_party/botan/src/bogo_shim/bogo_shim.cpp1680
-rw-r--r--comm/third_party/botan/src/bogo_shim/config.json129
-rw-r--r--comm/third_party/botan/src/build-data/arch/alpha.txt7
-rw-r--r--comm/third_party/botan/src/build-data/arch/arm32.txt21
-rw-r--r--comm/third_party/botan/src/build-data/arch/arm64.txt20
-rw-r--r--comm/third_party/botan/src/build-data/arch/generic.txt4
-rw-r--r--comm/third_party/botan/src/build-data/arch/hppa.txt8
-rw-r--r--comm/third_party/botan/src/build-data/arch/ia64.txt6
-rw-r--r--comm/third_party/botan/src/build-data/arch/llvm.txt1
-rw-r--r--comm/third_party/botan/src/build-data/arch/m68k.txt6
-rw-r--r--comm/third_party/botan/src/build-data/arch/mips32.txt6
-rw-r--r--comm/third_party/botan/src/build-data/arch/mips64.txt5
-rw-r--r--comm/third_party/botan/src/build-data/arch/powerpcspe.txt3
-rw-r--r--comm/third_party/botan/src/build-data/arch/ppc32.txt12
-rw-r--r--comm/third_party/botan/src/build-data/arch/ppc64.txt17
-rw-r--r--comm/third_party/botan/src/build-data/arch/riscv32.txt2
-rw-r--r--comm/third_party/botan/src/build-data/arch/riscv64.txt3
-rw-r--r--comm/third_party/botan/src/build-data/arch/s390.txt1
-rw-r--r--comm/third_party/botan/src/build-data/arch/s390x.txt2
-rw-r--r--comm/third_party/botan/src/build-data/arch/sparc32.txt7
-rw-r--r--comm/third_party/botan/src/build-data/arch/sparc64.txt3
-rw-r--r--comm/third_party/botan/src/build-data/arch/superh.txt4
-rw-r--r--comm/third_party/botan/src/build-data/arch/x32.txt16
-rw-r--r--comm/third_party/botan/src/build-data/arch/x86_32.txt32
-rw-r--r--comm/third_party/botan/src/build-data/arch/x86_64.txt25
-rw-r--r--comm/third_party/botan/src/build-data/bakefile.in51
-rw-r--r--comm/third_party/botan/src/build-data/botan.doxy.in226
-rw-r--r--comm/third_party/botan/src/build-data/botan.pc.in12
-rw-r--r--comm/third_party/botan/src/build-data/buildh.in268
-rw-r--r--comm/third_party/botan/src/build-data/cc/clang.txt85
-rw-r--r--comm/third_party/botan/src/build-data/cc/ekopath.txt17
-rw-r--r--comm/third_party/botan/src/build-data/cc/gcc.txt99
-rw-r--r--comm/third_party/botan/src/build-data/cc/hpcc.txt18
-rw-r--r--comm/third_party/botan/src/build-data/cc/icc.txt24
-rw-r--r--comm/third_party/botan/src/build-data/cc/msvc.txt84
-rw-r--r--comm/third_party/botan/src/build-data/cc/pgi.txt15
-rw-r--r--comm/third_party/botan/src/build-data/cc/sunstudio.txt39
-rw-r--r--comm/third_party/botan/src/build-data/cc/xlc.txt26
-rw-r--r--comm/third_party/botan/src/build-data/cmake.in73
-rw-r--r--comm/third_party/botan/src/build-data/detect_arch.cpp76
-rw-r--r--comm/third_party/botan/src/build-data/detect_version.cpp60
-rw-r--r--comm/third_party/botan/src/build-data/innosetup.in73
-rw-r--r--comm/third_party/botan/src/build-data/makefile.in146
-rw-r--r--comm/third_party/botan/src/build-data/oids.txt335
-rw-r--r--comm/third_party/botan/src/build-data/os/aix.txt18
-rw-r--r--comm/third_party/botan/src/build-data/os/android.txt26
-rw-r--r--comm/third_party/botan/src/build-data/os/cygwin.txt20
-rw-r--r--comm/third_party/botan/src/build-data/os/dragonfly.txt17
-rw-r--r--comm/third_party/botan/src/build-data/os/emscripten.txt17
-rw-r--r--comm/third_party/botan/src/build-data/os/freebsd.txt22
-rw-r--r--comm/third_party/botan/src/build-data/os/haiku.txt25
-rw-r--r--comm/third_party/botan/src/build-data/os/hpux.txt20
-rw-r--r--comm/third_party/botan/src/build-data/os/hurd.txt19
-rw-r--r--comm/third_party/botan/src/build-data/os/includeos.txt5
-rw-r--r--comm/third_party/botan/src/build-data/os/ios.txt23
-rw-r--r--comm/third_party/botan/src/build-data/os/linux.txt26
-rw-r--r--comm/third_party/botan/src/build-data/os/llvm.txt15
-rw-r--r--comm/third_party/botan/src/build-data/os/macos.txt32
-rw-r--r--comm/third_party/botan/src/build-data/os/mingw.txt33
-rw-r--r--comm/third_party/botan/src/build-data/os/nacl.txt6
-rw-r--r--comm/third_party/botan/src/build-data/os/netbsd.txt21
-rw-r--r--comm/third_party/botan/src/build-data/os/none.txt4
-rw-r--r--comm/third_party/botan/src/build-data/os/openbsd.txt25
-rw-r--r--comm/third_party/botan/src/build-data/os/qnx.txt18
-rw-r--r--comm/third_party/botan/src/build-data/os/solaris.txt21
-rw-r--r--comm/third_party/botan/src/build-data/os/uwp.txt25
-rw-r--r--comm/third_party/botan/src/build-data/os/windows.txt48
-rw-r--r--comm/third_party/botan/src/build-data/policy/bsi.txt188
-rw-r--r--comm/third_party/botan/src/build-data/policy/modern.txt131
-rw-r--r--comm/third_party/botan/src/build-data/policy/nist.txt187
-rw-r--r--comm/third_party/botan/src/build-data/version.txt11
-rw-r--r--comm/third_party/botan/src/cli/argon2.cpp78
-rw-r--r--comm/third_party/botan/src/cli/argparse.h280
-rw-r--r--comm/third_party/botan/src/cli/asn1.cpp89
-rw-r--r--comm/third_party/botan/src/cli/bcrypt.cpp89
-rw-r--r--comm/third_party/botan/src/cli/cc_enc.cpp189
-rw-r--r--comm/third_party/botan/src/cli/cli.cpp349
-rw-r--r--comm/third_party/botan/src/cli/cli.h219
-rw-r--r--comm/third_party/botan/src/cli/cli_exceptions.h47
-rw-r--r--comm/third_party/botan/src/cli/cli_rng.cpp146
-rw-r--r--comm/third_party/botan/src/cli/codec.cpp268
-rw-r--r--comm/third_party/botan/src/cli/compress.cpp190
-rw-r--r--comm/third_party/botan/src/cli/encryption.cpp127
-rw-r--r--comm/third_party/botan/src/cli/entropy.cpp104
-rw-r--r--comm/third_party/botan/src/cli/hash.cpp78
-rw-r--r--comm/third_party/botan/src/cli/hmac.cpp78
-rw-r--r--comm/third_party/botan/src/cli/main.cpp37
-rw-r--r--comm/third_party/botan/src/cli/math.cpp269
-rw-r--r--comm/third_party/botan/src/cli/pbkdf.cpp99
-rw-r--r--comm/third_party/botan/src/cli/pk_crypt.cpp229
-rw-r--r--comm/third_party/botan/src/cli/psk.cpp106
-rw-r--r--comm/third_party/botan/src/cli/pubkey.cpp554
-rw-r--r--comm/third_party/botan/src/cli/roughtime.cpp215
-rw-r--r--comm/third_party/botan/src/cli/sandbox.cpp115
-rw-r--r--comm/third_party/botan/src/cli/sandbox.h32
-rw-r--r--comm/third_party/botan/src/cli/socket_utils.h105
-rw-r--r--comm/third_party/botan/src/cli/speed.cpp2342
-rw-r--r--comm/third_party/botan/src/cli/timing_tests.cpp617
-rw-r--r--comm/third_party/botan/src/cli/tls_client.cpp436
-rw-r--r--comm/third_party/botan/src/cli/tls_helpers.h244
-rw-r--r--comm/third_party/botan/src/cli/tls_http_server.cpp579
-rw-r--r--comm/third_party/botan/src/cli/tls_proxy.cpp526
-rw-r--r--comm/third_party/botan/src/cli/tls_server.cpp364
-rw-r--r--comm/third_party/botan/src/cli/tls_utils.cpp226
-rw-r--r--comm/third_party/botan/src/cli/tss.cpp138
-rw-r--r--comm/third_party/botan/src/cli/utils.cpp391
-rw-r--r--comm/third_party/botan/src/cli/x509.cpp417
-rw-r--r--comm/third_party/botan/src/configs/astyle.rc14
-rw-r--r--comm/third_party/botan/src/configs/coverage.rc18
-rw-r--r--comm/third_party/botan/src/configs/eclipse.xml167
-rw-r--r--comm/third_party/botan/src/configs/indent.el55
-rw-r--r--comm/third_party/botan/src/configs/pylint.rc379
-rw-r--r--comm/third_party/botan/src/configs/sonar-project.properties18
-rw-r--r--comm/third_party/botan/src/configs/sphinx/conf.py220
-rw-r--r--comm/third_party/botan/src/configs/sphinx/templates/layout.html9
-rw-r--r--comm/third_party/botan/src/fuzzer/asn1.cpp43
-rw-r--r--comm/third_party/botan/src/fuzzer/barrett.cpp49
-rw-r--r--comm/third_party/botan/src/fuzzer/bn_cmp.cpp74
-rw-r--r--comm/third_party/botan/src/fuzzer/bn_sqr.cpp24
-rw-r--r--comm/third_party/botan/src/fuzzer/cert.cpp22
-rw-r--r--comm/third_party/botan/src/fuzzer/crl.cpp19
-rw-r--r--comm/third_party/botan/src/fuzzer/divide.cpp52
-rw-r--r--comm/third_party/botan/src/fuzzer/ecc_bp256.cpp16
-rw-r--r--comm/third_party/botan/src/fuzzer/ecc_helper.h107
-rw-r--r--comm/third_party/botan/src/fuzzer/ecc_p256.cpp15
-rw-r--r--comm/third_party/botan/src/fuzzer/ecc_p384.cpp15
-rw-r--r--comm/third_party/botan/src/fuzzer/ecc_p521.cpp15
-rw-r--r--comm/third_party/botan/src/fuzzer/fuzzers.h150
-rw-r--r--comm/third_party/botan/src/fuzzer/invert.cpp82
-rw-r--r--comm/third_party/botan/src/fuzzer/mem_pool.cpp193
-rw-r--r--comm/third_party/botan/src/fuzzer/mode_padding.cpp169
-rw-r--r--comm/third_party/botan/src/fuzzer/oaep.cpp102
-rw-r--r--comm/third_party/botan/src/fuzzer/ocsp.cpp17
-rw-r--r--comm/third_party/botan/src/fuzzer/os2ecp.cpp44
-rw-r--r--comm/third_party/botan/src/fuzzer/pkcs1.cpp75
-rw-r--r--comm/third_party/botan/src/fuzzer/pkcs8.cpp27
-rw-r--r--comm/third_party/botan/src/fuzzer/pow_mod.cpp72
-rw-r--r--comm/third_party/botan/src/fuzzer/redc_p192.cpp31
-rw-r--r--comm/third_party/botan/src/fuzzer/redc_p224.cpp31
-rw-r--r--comm/third_party/botan/src/fuzzer/redc_p256.cpp31
-rw-r--r--comm/third_party/botan/src/fuzzer/redc_p384.cpp31
-rw-r--r--comm/third_party/botan/src/fuzzer/redc_p521.cpp31
-rw-r--r--comm/third_party/botan/src/fuzzer/ressol.cpp44
-rw-r--r--comm/third_party/botan/src/fuzzer/tls_client.cpp130
-rw-r--r--comm/third_party/botan/src/fuzzer/tls_client_hello.cpp18
-rw-r--r--comm/third_party/botan/src/fuzzer/tls_server.cpp227
-rw-r--r--comm/third_party/botan/src/fuzzer/uri.cpp20
-rw-r--r--comm/third_party/botan/src/fuzzer/x509_dn.cpp41
-rw-r--r--comm/third_party/botan/src/lib/asn1/alg_id.cpp109
-rw-r--r--comm/third_party/botan/src/lib/asn1/alg_id.h14
-rw-r--r--comm/third_party/botan/src/lib/asn1/asn1_obj.cpp238
-rw-r--r--comm/third_party/botan/src/lib/asn1/asn1_obj.h475
-rw-r--r--comm/third_party/botan/src/lib/asn1/asn1_oid.cpp216
-rw-r--r--comm/third_party/botan/src/lib/asn1/asn1_oid.h14
-rw-r--r--comm/third_party/botan/src/lib/asn1/asn1_print.cpp327
-rw-r--r--comm/third_party/botan/src/lib/asn1/asn1_print.h125
-rw-r--r--comm/third_party/botan/src/lib/asn1/asn1_str.cpp153
-rw-r--r--comm/third_party/botan/src/lib/asn1/asn1_str.h14
-rw-r--r--comm/third_party/botan/src/lib/asn1/asn1_time.cpp290
-rw-r--r--comm/third_party/botan/src/lib/asn1/asn1_time.h14
-rw-r--r--comm/third_party/botan/src/lib/asn1/ber_dec.cpp549
-rw-r--r--comm/third_party/botan/src/lib/asn1/ber_dec.h418
-rw-r--r--comm/third_party/botan/src/lib/asn1/der_enc.cpp405
-rw-r--r--comm/third_party/botan/src/lib/asn1/der_enc.h227
-rw-r--r--comm/third_party/botan/src/lib/asn1/info.txt7
-rw-r--r--comm/third_party/botan/src/lib/asn1/oid_maps.cpp510
-rw-r--r--comm/third_party/botan/src/lib/asn1/oids.cpp134
-rw-r--r--comm/third_party/botan/src/lib/asn1/oids.h98
-rw-r--r--comm/third_party/botan/src/lib/base/botan.h41
-rw-r--r--comm/third_party/botan/src/lib/base/buf_comp.cpp54
-rw-r--r--comm/third_party/botan/src/lib/base/buf_comp.h178
-rw-r--r--comm/third_party/botan/src/lib/base/info.txt17
-rw-r--r--comm/third_party/botan/src/lib/base/init.h35
-rw-r--r--comm/third_party/botan/src/lib/base/key_spec.h14
-rw-r--r--comm/third_party/botan/src/lib/base/lookup.h179
-rw-r--r--comm/third_party/botan/src/lib/base/scan_name.cpp149
-rw-r--r--comm/third_party/botan/src/lib/base/scan_name.h124
-rw-r--r--comm/third_party/botan/src/lib/base/secmem.h136
-rw-r--r--comm/third_party/botan/src/lib/base/sym_algo.cpp24
-rw-r--r--comm/third_party/botan/src/lib/base/sym_algo.h190
-rw-r--r--comm/third_party/botan/src/lib/base/symkey.cpp134
-rw-r--r--comm/third_party/botan/src/lib/base/symkey.h150
-rw-r--r--comm/third_party/botan/src/lib/block/aes/aes.cpp1017
-rw-r--r--comm/third_party/botan/src/lib/block/aes/aes.h131
-rw-r--r--comm/third_party/botan/src/lib/block/aes/aes_armv8/aes_armv8.cpp484
-rw-r--r--comm/third_party/botan/src/lib/block/aes/aes_armv8/info.txt12
-rw-r--r--comm/third_party/botan/src/lib/block/aes/aes_ni/aes_ni.cpp780
-rw-r--r--comm/third_party/botan/src/lib/block/aes/aes_ni/info.txt9
-rw-r--r--comm/third_party/botan/src/lib/block/aes/aes_power8/aes_power8.cpp529
-rw-r--r--comm/third_party/botan/src/lib/block/aes/aes_power8/info.txt11
-rw-r--r--comm/third_party/botan/src/lib/block/aes/aes_vperm/aes_vperm.cpp627
-rw-r--r--comm/third_party/botan/src/lib/block/aes/aes_vperm/info.txt36
-rw-r--r--comm/third_party/botan/src/lib/block/aes/info.txt3
-rw-r--r--comm/third_party/botan/src/lib/block/aria/aria.cpp506
-rw-r--r--comm/third_party/botan/src/lib/block/aria/aria.h84
-rw-r--r--comm/third_party/botan/src/lib/block/aria/info.txt7
-rw-r--r--comm/third_party/botan/src/lib/block/block_cipher.cpp363
-rw-r--r--comm/third_party/botan/src/lib/block/block_cipher.h254
-rw-r--r--comm/third_party/botan/src/lib/block/blowfish/blowfish.cpp456
-rw-r--r--comm/third_party/botan/src/lib/block/blowfish/blowfish.h62
-rw-r--r--comm/third_party/botan/src/lib/block/blowfish/info.txt3
-rw-r--r--comm/third_party/botan/src/lib/block/camellia/camellia.cpp924
-rw-r--r--comm/third_party/botan/src/lib/block/camellia/camellia.h73
-rw-r--r--comm/third_party/botan/src/lib/block/camellia/info.txt7
-rw-r--r--comm/third_party/botan/src/lib/block/cascade/cascade.cpp93
-rw-r--r--comm/third_party/botan/src/lib/block/cascade/cascade.h57
-rw-r--r--comm/third_party/botan/src/lib/block/cascade/info.txt7
-rw-r--r--comm/third_party/botan/src/lib/block/cast128/cast128.cpp471
-rw-r--r--comm/third_party/botan/src/lib/block/cast128/cast128.h42
-rw-r--r--comm/third_party/botan/src/lib/block/cast128/cast_sboxes.h197
-rw-r--r--comm/third_party/botan/src/lib/block/cast128/info.txt12
-rw-r--r--comm/third_party/botan/src/lib/block/cast256/cast256.cpp232
-rw-r--r--comm/third_party/botan/src/lib/block/cast256/cast256.h38
-rw-r--r--comm/third_party/botan/src/lib/block/cast256/info.txt7
-rw-r--r--comm/third_party/botan/src/lib/block/des/des.cpp410
-rw-r--r--comm/third_party/botan/src/lib/block/des/des.h67
-rw-r--r--comm/third_party/botan/src/lib/block/des/des_tab.cpp372
-rw-r--r--comm/third_party/botan/src/lib/block/des/desx.cpp65
-rw-r--r--comm/third_party/botan/src/lib/block/des/desx.h37
-rw-r--r--comm/third_party/botan/src/lib/block/des/info.txt3
-rw-r--r--comm/third_party/botan/src/lib/block/gost_28147/gost_28147.cpp189
-rw-r--r--comm/third_party/botan/src/lib/block/gost_28147/gost_28147.h95
-rw-r--r--comm/third_party/botan/src/lib/block/gost_28147/info.txt3
-rw-r--r--comm/third_party/botan/src/lib/block/idea/idea.cpp240
-rw-r--r--comm/third_party/botan/src/lib/block/idea/idea.h45
-rw-r--r--comm/third_party/botan/src/lib/block/idea/idea_sse2/idea_sse2.cpp208
-rw-r--r--comm/third_party/botan/src/lib/block/idea/idea_sse2/info.txt7
-rw-r--r--comm/third_party/botan/src/lib/block/idea/info.txt3
-rw-r--r--comm/third_party/botan/src/lib/block/info.txt7
-rw-r--r--comm/third_party/botan/src/lib/block/kasumi/info.txt3
-rw-r--r--comm/third_party/botan/src/lib/block/kasumi/kasumi.cpp238
-rw-r--r--comm/third_party/botan/src/lib/block/kasumi/kasumi.h37
-rw-r--r--comm/third_party/botan/src/lib/block/lion/info.txt8
-rw-r--r--comm/third_party/botan/src/lib/block/lion/lion.cpp138
-rw-r--r--comm/third_party/botan/src/lib/block/lion/lion.h66
-rw-r--r--comm/third_party/botan/src/lib/block/misty1/info.txt3
-rw-r--r--comm/third_party/botan/src/lib/block/misty1/misty1.cpp263
-rw-r--r--comm/third_party/botan/src/lib/block/misty1/misty1.h37
-rw-r--r--comm/third_party/botan/src/lib/block/noekeon/info.txt3
-rw-r--r--comm/third_party/botan/src/lib/block/noekeon/noekeon.cpp267
-rw-r--r--comm/third_party/botan/src/lib/block/noekeon/noekeon.h49
-rw-r--r--comm/third_party/botan/src/lib/block/noekeon/noekeon_simd/info.txt8
-rw-r--r--comm/third_party/botan/src/lib/block/noekeon/noekeon_simd/noekeon_simd.cpp143
-rw-r--r--comm/third_party/botan/src/lib/block/seed/info.txt3
-rw-r--r--comm/third_party/botan/src/lib/block/seed/seed.cpp328
-rw-r--r--comm/third_party/botan/src/lib/block/seed/seed.h37
-rw-r--r--comm/third_party/botan/src/lib/block/serpent/info.txt11
-rw-r--r--comm/third_party/botan/src/lib/block/serpent/serpent.cpp299
-rw-r--r--comm/third_party/botan/src/lib/block/serpent/serpent.h53
-rw-r--r--comm/third_party/botan/src/lib/block/serpent/serpent_avx2/info.txt17
-rw-r--r--comm/third_party/botan/src/lib/block/serpent/serpent_avx2/serpent_avx2.cpp169
-rw-r--r--comm/third_party/botan/src/lib/block/serpent/serpent_sbox.h446
-rw-r--r--comm/third_party/botan/src/lib/block/serpent/serpent_simd/info.txt7
-rw-r--r--comm/third_party/botan/src/lib/block/serpent/serpent_simd/serpent_simd.cpp169
-rw-r--r--comm/third_party/botan/src/lib/block/shacal2/info.txt5
-rw-r--r--comm/third_party/botan/src/lib/block/shacal2/shacal2.cpp280
-rw-r--r--comm/third_party/botan/src/lib/block/shacal2/shacal2.h54
-rw-r--r--comm/third_party/botan/src/lib/block/shacal2/shacal2_avx2/info.txt11
-rw-r--r--comm/third_party/botan/src/lib/block/shacal2/shacal2_avx2/shacal2_avx2.cpp122
-rw-r--r--comm/third_party/botan/src/lib/block/shacal2/shacal2_simd/info.txt8
-rw-r--r--comm/third_party/botan/src/lib/block/shacal2/shacal2_simd/shacal2_simd.cpp119
-rw-r--r--comm/third_party/botan/src/lib/block/shacal2/shacal2_x86/info.txt20
-rw-r--r--comm/third_party/botan/src/lib/block/shacal2/shacal2_x86/shacal2_x86.cpp118
-rw-r--r--comm/third_party/botan/src/lib/block/sm4/info.txt3
-rw-r--r--comm/third_party/botan/src/lib/block/sm4/sm4.cpp341
-rw-r--r--comm/third_party/botan/src/lib/block/sm4/sm4.h45
-rw-r--r--comm/third_party/botan/src/lib/block/sm4/sm4_armv8/info.txt11
-rw-r--r--comm/third_party/botan/src/lib/block/sm4/sm4_armv8/sm4_armv8.cpp174
-rw-r--r--comm/third_party/botan/src/lib/block/threefish_512/info.txt3
-rw-r--r--comm/third_party/botan/src/lib/block/threefish_512/threefish.h17
-rw-r--r--comm/third_party/botan/src/lib/block/threefish_512/threefish_512.cpp273
-rw-r--r--comm/third_party/botan/src/lib/block/threefish_512/threefish_512.h57
-rw-r--r--comm/third_party/botan/src/lib/block/threefish_512/threefish_512_avx2/info.txt14
-rw-r--r--comm/third_party/botan/src/lib/block/threefish_512/threefish_512_avx2/threefish_512_avx2.cpp444
-rw-r--r--comm/third_party/botan/src/lib/block/twofish/info.txt3
-rw-r--r--comm/third_party/botan/src/lib/block/twofish/twofish.cpp326
-rw-r--r--comm/third_party/botan/src/lib/block/twofish/twofish.h47
-rw-r--r--comm/third_party/botan/src/lib/block/twofish/twofish_tab.cpp293
-rw-r--r--comm/third_party/botan/src/lib/block/xtea/info.txt3
-rw-r--r--comm/third_party/botan/src/lib/block/xtea/xtea.cpp134
-rw-r--r--comm/third_party/botan/src/lib/block/xtea/xtea.h37
-rw-r--r--comm/third_party/botan/src/lib/codec/base32/base32.cpp233
-rw-r--r--comm/third_party/botan/src/lib/codec/base32/base32.h127
-rw-r--r--comm/third_party/botan/src/lib/codec/base32/info.txt3
-rw-r--r--comm/third_party/botan/src/lib/codec/base58/base58.cpp189
-rw-r--r--comm/third_party/botan/src/lib/codec/base58/base58.h76
-rw-r--r--comm/third_party/botan/src/lib/codec/base58/info.txt8
-rw-r--r--comm/third_party/botan/src/lib/codec/base64/base64.cpp248
-rw-r--r--comm/third_party/botan/src/lib/codec/base64/base64.h141
-rw-r--r--comm/third_party/botan/src/lib/codec/base64/info.txt3
-rw-r--r--comm/third_party/botan/src/lib/codec/hex/hex.cpp210
-rw-r--r--comm/third_party/botan/src/lib/codec/hex/hex.h148
-rw-r--r--comm/third_party/botan/src/lib/codec/hex/info.txt3
-rw-r--r--comm/third_party/botan/src/lib/compat/sodium/info.txt17
-rw-r--r--comm/third_party/botan/src/lib/compat/sodium/sodium.h1453
-rw-r--r--comm/third_party/botan/src/lib/compat/sodium/sodium_25519.cpp60
-rw-r--r--comm/third_party/botan/src/lib/compat/sodium/sodium_aead.cpp359
-rw-r--r--comm/third_party/botan/src/lib/compat/sodium/sodium_auth.cpp131
-rw-r--r--comm/third_party/botan/src/lib/compat/sodium/sodium_box.cpp100
-rw-r--r--comm/third_party/botan/src/lib/compat/sodium/sodium_chacha.cpp109
-rw-r--r--comm/third_party/botan/src/lib/compat/sodium/sodium_salsa.cpp124
-rw-r--r--comm/third_party/botan/src/lib/compat/sodium/sodium_secretbox.cpp123
-rw-r--r--comm/third_party/botan/src/lib/compat/sodium/sodium_utils.cpp160
-rw-r--r--comm/third_party/botan/src/lib/compression/bzip2/bzip2.cpp110
-rw-r--r--comm/third_party/botan/src/lib/compression/bzip2/bzip2.h40
-rw-r--r--comm/third_party/botan/src/lib/compression/bzip2/info.txt9
-rw-r--r--comm/third_party/botan/src/lib/compression/compress_utils.cpp196
-rw-r--r--comm/third_party/botan/src/lib/compression/compress_utils.h89
-rw-r--r--comm/third_party/botan/src/lib/compression/compression.cpp116
-rw-r--r--comm/third_party/botan/src/lib/compression/compression.h238
-rw-r--r--comm/third_party/botan/src/lib/compression/info.txt11
-rw-r--r--comm/third_party/botan/src/lib/compression/lzma/info.txt9
-rw-r--r--comm/third_party/botan/src/lib/compression/lzma/lzma.cpp95
-rw-r--r--comm/third_party/botan/src/lib/compression/lzma/lzma.h42
-rw-r--r--comm/third_party/botan/src/lib/compression/zlib/info.txt10
-rw-r--r--comm/third_party/botan/src/lib/compression/zlib/zlib.cpp173
-rw-r--r--comm/third_party/botan/src/lib/compression/zlib/zlib.h89
-rw-r--r--comm/third_party/botan/src/lib/entropy/dev_random/dev_random.cpp122
-rw-r--r--comm/third_party/botan/src/lib/entropy/dev_random/dev_random.h37
-rw-r--r--comm/third_party/botan/src/lib/entropy/dev_random/info.txt11
-rw-r--r--comm/third_party/botan/src/lib/entropy/entropy_src.h87
-rw-r--r--comm/third_party/botan/src/lib/entropy/entropy_srcs.cpp235
-rw-r--r--comm/third_party/botan/src/lib/entropy/getentropy/getentropy.cpp35
-rw-r--r--comm/third_party/botan/src/lib/entropy/getentropy/getentropy.h28
-rw-r--r--comm/third_party/botan/src/lib/entropy/getentropy/info.txt11
-rw-r--r--comm/third_party/botan/src/lib/entropy/info.txt7
-rw-r--r--comm/third_party/botan/src/lib/entropy/proc_walk/info.txt11
-rw-r--r--comm/third_party/botan/src/lib/entropy/proc_walk/proc_walk.cpp154
-rw-r--r--comm/third_party/botan/src/lib/entropy/proc_walk/proc_walk.h45
-rw-r--r--comm/third_party/botan/src/lib/entropy/rdseed/info.txt19
-rw-r--r--comm/third_party/botan/src/lib/entropy/rdseed/rdseed.cpp97
-rw-r--r--comm/third_party/botan/src/lib/entropy/rdseed/rdseed.h28
-rw-r--r--comm/third_party/botan/src/lib/entropy/win32_stats/es_win32.cpp59
-rw-r--r--comm/third_party/botan/src/lib/entropy/win32_stats/es_win32.h27
-rw-r--r--comm/third_party/botan/src/lib/entropy/win32_stats/info.txt15
-rw-r--r--comm/third_party/botan/src/lib/ffi/ffi.cpp297
-rw-r--r--comm/third_party/botan/src/lib/ffi/ffi.h1778
-rw-r--r--comm/third_party/botan/src/lib/ffi/ffi_block.cpp112
-rw-r--r--comm/third_party/botan/src/lib/ffi/ffi_cert.cpp503
-rw-r--r--comm/third_party/botan/src/lib/ffi/ffi_cipher.cpp233
-rw-r--r--comm/third_party/botan/src/lib/ffi/ffi_fpe.cpp94
-rw-r--r--comm/third_party/botan/src/lib/ffi/ffi_hash.cpp91
-rw-r--r--comm/third_party/botan/src/lib/ffi/ffi_hotp.cpp99
-rw-r--r--comm/third_party/botan/src/lib/ffi/ffi_kdf.cpp189
-rw-r--r--comm/third_party/botan/src/lib/ffi/ffi_keywrap.cpp50
-rw-r--r--comm/third_party/botan/src/lib/ffi/ffi_mac.cpp85
-rw-r--r--comm/third_party/botan/src/lib/ffi/ffi_mp.cpp312
-rw-r--r--comm/third_party/botan/src/lib/ffi/ffi_mp.h19
-rw-r--r--comm/third_party/botan/src/lib/ffi/ffi_pk_op.cpp255
-rw-r--r--comm/third_party/botan/src/lib/ffi/ffi_pkey.cpp279
-rw-r--r--comm/third_party/botan/src/lib/ffi/ffi_pkey.h20
-rw-r--r--comm/third_party/botan/src/lib/ffi/ffi_pkey_algs.cpp980
-rw-r--r--comm/third_party/botan/src/lib/ffi/ffi_rng.cpp183
-rw-r--r--comm/third_party/botan/src/lib/ffi/ffi_rng.h19
-rw-r--r--comm/third_party/botan/src/lib/ffi/ffi_totp.cpp94
-rw-r--r--comm/third_party/botan/src/lib/ffi/ffi_util.h182
-rw-r--r--comm/third_party/botan/src/lib/ffi/info.txt31
-rw-r--r--comm/third_party/botan/src/lib/filters/algo_filt.cpp96
-rw-r--r--comm/third_party/botan/src/lib/filters/b64_filt.cpp182
-rw-r--r--comm/third_party/botan/src/lib/filters/b64_filt.h14
-rw-r--r--comm/third_party/botan/src/lib/filters/basefilt.cpp52
-rw-r--r--comm/third_party/botan/src/lib/filters/basefilt.h18
-rw-r--r--comm/third_party/botan/src/lib/filters/buf_filt.cpp103
-rw-r--r--comm/third_party/botan/src/lib/filters/buf_filt.h14
-rw-r--r--comm/third_party/botan/src/lib/filters/cipher_filter.cpp103
-rw-r--r--comm/third_party/botan/src/lib/filters/cipher_filter.h14
-rw-r--r--comm/third_party/botan/src/lib/filters/comp_filter.cpp122
-rw-r--r--comm/third_party/botan/src/lib/filters/comp_filter.h15
-rw-r--r--comm/third_party/botan/src/lib/filters/data_snk.cpp75
-rw-r--r--comm/third_party/botan/src/lib/filters/data_snk.h76
-rw-r--r--comm/third_party/botan/src/lib/filters/fd_unix/fd_unix.cpp55
-rw-r--r--comm/third_party/botan/src/lib/filters/fd_unix/fd_unix.h35
-rw-r--r--comm/third_party/botan/src/lib/filters/fd_unix/info.txt7
-rw-r--r--comm/third_party/botan/src/lib/filters/filter.cpp129
-rw-r--r--comm/third_party/botan/src/lib/filters/filter.h175
-rw-r--r--comm/third_party/botan/src/lib/filters/filters.h741
-rw-r--r--comm/third_party/botan/src/lib/filters/hex_filt.cpp170
-rw-r--r--comm/third_party/botan/src/lib/filters/hex_filt.h14
-rw-r--r--comm/third_party/botan/src/lib/filters/info.txt31
-rw-r--r--comm/third_party/botan/src/lib/filters/key_filt.h14
-rw-r--r--comm/third_party/botan/src/lib/filters/out_buf.cpp121
-rw-r--r--comm/third_party/botan/src/lib/filters/out_buf.h44
-rw-r--r--comm/third_party/botan/src/lib/filters/pipe.cpp311
-rw-r--r--comm/third_party/botan/src/lib/filters/pipe.h379
-rw-r--r--comm/third_party/botan/src/lib/filters/pipe_io.cpp47
-rw-r--r--comm/third_party/botan/src/lib/filters/pipe_rw.cpp181
-rw-r--r--comm/third_party/botan/src/lib/filters/secqueue.cpp232
-rw-r--r--comm/third_party/botan/src/lib/filters/secqueue.h74
-rw-r--r--comm/third_party/botan/src/lib/filters/threaded_fork.cpp153
-rw-r--r--comm/third_party/botan/src/lib/hash/blake2/blake2b.cpp207
-rw-r--r--comm/third_party/botan/src/lib/hash/blake2/blake2b.h60
-rw-r--r--comm/third_party/botan/src/lib/hash/blake2/info.txt3
-rw-r--r--comm/third_party/botan/src/lib/hash/checksum/adler32/adler32.cpp86
-rw-r--r--comm/third_party/botan/src/lib/hash/checksum/adler32/adler32.h40
-rw-r--r--comm/third_party/botan/src/lib/hash/checksum/adler32/info.txt3
-rw-r--r--comm/third_party/botan/src/lib/hash/checksum/crc24/crc24.cpp252
-rw-r--r--comm/third_party/botan/src/lib/hash/checksum/crc24/crc24.h41
-rw-r--r--comm/third_party/botan/src/lib/hash/checksum/crc24/info.txt3
-rw-r--r--comm/third_party/botan/src/lib/hash/checksum/crc32/crc32.cpp111
-rw-r--r--comm/third_party/botan/src/lib/hash/checksum/crc32/crc32.h40
-rw-r--r--comm/third_party/botan/src/lib/hash/checksum/crc32/info.txt3
-rw-r--r--comm/third_party/botan/src/lib/hash/checksum/info.txt4
-rw-r--r--comm/third_party/botan/src/lib/hash/comb4p/comb4p.cpp110
-rw-r--r--comm/third_party/botan/src/lib/hash/comb4p/comb4p.h61
-rw-r--r--comm/third_party/botan/src/lib/hash/comb4p/info.txt3
-rw-r--r--comm/third_party/botan/src/lib/hash/gost_3411/gost_3411.cpp248
-rw-r--r--comm/third_party/botan/src/lib/hash/gost_3411/gost_3411.h47
-rw-r--r--comm/third_party/botan/src/lib/hash/gost_3411/info.txt7
-rw-r--r--comm/third_party/botan/src/lib/hash/hash.cpp360
-rw-r--r--comm/third_party/botan/src/lib/hash/hash.h91
-rw-r--r--comm/third_party/botan/src/lib/hash/info.txt7
-rw-r--r--comm/third_party/botan/src/lib/hash/keccak/info.txt7
-rw-r--r--comm/third_party/botan/src/lib/hash/keccak/keccak.cpp68
-rw-r--r--comm/third_party/botan/src/lib/hash/keccak/keccak.h51
-rw-r--r--comm/third_party/botan/src/lib/hash/md4/info.txt7
-rw-r--r--comm/third_party/botan/src/lib/hash/md4/md4.cpp144
-rw-r--r--comm/third_party/botan/src/lib/hash/md4/md4.h45
-rw-r--r--comm/third_party/botan/src/lib/hash/md5/info.txt7
-rw-r--r--comm/third_party/botan/src/lib/hash/md5/md5.cpp140
-rw-r--r--comm/third_party/botan/src/lib/hash/md5/md5.h50
-rw-r--r--comm/third_party/botan/src/lib/hash/mdx_hash/info.txt5
-rw-r--r--comm/third_party/botan/src/lib/hash/mdx_hash/mdx_hash.cpp121
-rw-r--r--comm/third_party/botan/src/lib/hash/mdx_hash/mdx_hash.h73
-rw-r--r--comm/third_party/botan/src/lib/hash/par_hash/info.txt3
-rw-r--r--comm/third_party/botan/src/lib/hash/par_hash/par_hash.cpp86
-rw-r--r--comm/third_party/botan/src/lib/hash/par_hash/par_hash.h50
-rw-r--r--comm/third_party/botan/src/lib/hash/rmd160/info.txt7
-rw-r--r--comm/third_party/botan/src/lib/hash/rmd160/rmd160.cpp221
-rw-r--r--comm/third_party/botan/src/lib/hash/rmd160/rmd160.h41
-rw-r--r--comm/third_party/botan/src/lib/hash/sha1/info.txt7
-rw-r--r--comm/third_party/botan/src/lib/hash/sha1/sha160.cpp190
-rw-r--r--comm/third_party/botan/src/lib/hash/sha1/sha160.h75
-rw-r--r--comm/third_party/botan/src/lib/hash/sha1/sha1_armv8/info.txt12
-rw-r--r--comm/third_party/botan/src/lib/hash/sha1/sha1_armv8/sha1_armv8.cpp207
-rw-r--r--comm/third_party/botan/src/lib/hash/sha1/sha1_sse2/info.txt7
-rw-r--r--comm/third_party/botan/src/lib/hash/sha1/sha1_sse2/sha1_sse2.cpp336
-rw-r--r--comm/third_party/botan/src/lib/hash/sha1/sha1_x86/info.txt16
-rw-r--r--comm/third_party/botan/src/lib/hash/sha1/sha1_x86/sha1_x86.cpp216
-rw-r--r--comm/third_party/botan/src/lib/hash/sha2_32/info.txt7
-rw-r--r--comm/third_party/botan/src/lib/hash/sha2_32/sha2_32.cpp278
-rw-r--r--comm/third_party/botan/src/lib/hash/sha2_32/sha2_32.h95
-rw-r--r--comm/third_party/botan/src/lib/hash/sha2_32/sha2_32_armv8/info.txt12
-rw-r--r--comm/third_party/botan/src/lib/hash/sha2_32/sha2_32_armv8/sha2_32_armv8.cpp204
-rw-r--r--comm/third_party/botan/src/lib/hash/sha2_32/sha2_32_bmi2/info.txt12
-rw-r--r--comm/third_party/botan/src/lib/hash/sha2_32/sha2_32_bmi2/sha2_32_bmi2.cpp140
-rw-r--r--comm/third_party/botan/src/lib/hash/sha2_32/sha2_32_x86/info.txt16
-rw-r--r--comm/third_party/botan/src/lib/hash/sha2_32/sha2_32_x86/sha2_32_x86.cpp215
-rw-r--r--comm/third_party/botan/src/lib/hash/sha2_64/info.txt7
-rw-r--r--comm/third_party/botan/src/lib/hash/sha2_64/sha2_64.cpp281
-rw-r--r--comm/third_party/botan/src/lib/hash/sha2_64/sha2_64.h102
-rw-r--r--comm/third_party/botan/src/lib/hash/sha2_64/sha2_64_bmi2/info.txt17
-rw-r--r--comm/third_party/botan/src/lib/hash/sha2_64/sha2_64_bmi2/sha2_64_bmi2.cpp153
-rw-r--r--comm/third_party/botan/src/lib/hash/sha3/info.txt3
-rw-r--r--comm/third_party/botan/src/lib/hash/sha3/sha3.cpp293
-rw-r--r--comm/third_party/botan/src/lib/hash/sha3/sha3.h136
-rw-r--r--comm/third_party/botan/src/lib/hash/sha3/sha3_bmi2/info.txt17
-rw-r--r--comm/third_party/botan/src/lib/hash/sha3/sha3_bmi2/sha3_bmi2.cpp133
-rw-r--r--comm/third_party/botan/src/lib/hash/shake/info.txt7
-rw-r--r--comm/third_party/botan/src/lib/hash/shake/shake.cpp97
-rw-r--r--comm/third_party/botan/src/lib/hash/shake/shake.h85
-rw-r--r--comm/third_party/botan/src/lib/hash/skein/info.txt7
-rw-r--r--comm/third_party/botan/src/lib/hash/skein/skein_512.cpp178
-rw-r--r--comm/third_party/botan/src/lib/hash/skein/skein_512.h72
-rw-r--r--comm/third_party/botan/src/lib/hash/sm3/info.txt7
-rw-r--r--comm/third_party/botan/src/lib/hash/sm3/sm3.cpp259
-rw-r--r--comm/third_party/botan/src/lib/hash/sm3/sm3.h49
-rw-r--r--comm/third_party/botan/src/lib/hash/streebog/info.txt3
-rw-r--r--comm/third_party/botan/src/lib/hash/streebog/streebog.cpp207
-rw-r--r--comm/third_party/botan/src/lib/hash/streebog/streebog.h72
-rw-r--r--comm/third_party/botan/src/lib/hash/streebog/streebog_precalc.cpp866
-rw-r--r--comm/third_party/botan/src/lib/hash/tiger/info.txt7
-rw-r--r--comm/third_party/botan/src/lib/hash/tiger/tig_tab.cpp364
-rw-r--r--comm/third_party/botan/src/lib/hash/tiger/tiger.cpp190
-rw-r--r--comm/third_party/botan/src/lib/hash/tiger/tiger.h59
-rw-r--r--comm/third_party/botan/src/lib/hash/whirlpool/info.txt7
-rw-r--r--comm/third_party/botan/src/lib/hash/whirlpool/whirlpool.cpp150
-rw-r--r--comm/third_party/botan/src/lib/hash/whirlpool/whrl_tab.cpp540
-rw-r--r--comm/third_party/botan/src/lib/hash/whirlpool/whrlpool.h50
-rw-r--r--comm/third_party/botan/src/lib/kdf/hkdf/hkdf.cpp122
-rw-r--r--comm/third_party/botan/src/lib/kdf/hkdf/hkdf.h117
-rw-r--r--comm/third_party/botan/src/lib/kdf/hkdf/info.txt7
-rw-r--r--comm/third_party/botan/src/lib/kdf/info.txt12
-rw-r--r--comm/third_party/botan/src/lib/kdf/kdf.cpp255
-rw-r--r--comm/third_party/botan/src/lib/kdf/kdf.h196
-rw-r--r--comm/third_party/botan/src/lib/kdf/kdf1/info.txt7
-rw-r--r--comm/third_party/botan/src/lib/kdf/kdf1/kdf1.cpp33
-rw-r--r--comm/third_party/botan/src/lib/kdf/kdf1/kdf1.h43
-rw-r--r--comm/third_party/botan/src/lib/kdf/kdf1_iso18033/info.txt7
-rw-r--r--comm/third_party/botan/src/lib/kdf/kdf1_iso18033/kdf1_iso18033.cpp38
-rw-r--r--comm/third_party/botan/src/lib/kdf/kdf1_iso18033/kdf1_iso18033.h43
-rw-r--r--comm/third_party/botan/src/lib/kdf/kdf2/info.txt7
-rw-r--r--comm/third_party/botan/src/lib/kdf/kdf2/kdf2.cpp38
-rw-r--r--comm/third_party/botan/src/lib/kdf/kdf2/kdf2.h43
-rw-r--r--comm/third_party/botan/src/lib/kdf/prf_tls/info.txt8
-rw-r--r--comm/third_party/botan/src/lib/kdf/prf_tls/prf_tls.cpp96
-rw-r--r--comm/third_party/botan/src/lib/kdf/prf_tls/prf_tls.h70
-rw-r--r--comm/third_party/botan/src/lib/kdf/prf_x942/info.txt8
-rw-r--r--comm/third_party/botan/src/lib/kdf/prf_x942/prf_x942.cpp92
-rw-r--r--comm/third_party/botan/src/lib/kdf/prf_x942/prf_x942.h42
-rw-r--r--comm/third_party/botan/src/lib/kdf/sp800_108/info.txt8
-rw-r--r--comm/third_party/botan/src/lib/kdf/sp800_108/sp800_108.cpp170
-rw-r--r--comm/third_party/botan/src/lib/kdf/sp800_108/sp800_108.h135
-rw-r--r--comm/third_party/botan/src/lib/kdf/sp800_56a/info.txt7
-rw-r--r--comm/third_party/botan/src/lib/kdf/sp800_56a/sp800_56a.cpp98
-rw-r--r--comm/third_party/botan/src/lib/kdf/sp800_56a/sp800_56a.h103
-rw-r--r--comm/third_party/botan/src/lib/kdf/sp800_56c/info.txt8
-rw-r--r--comm/third_party/botan/src/lib/kdf/sp800_56c/sp800_56c.cpp28
-rw-r--r--comm/third_party/botan/src/lib/kdf/sp800_56c/sp800_56c.h61
-rw-r--r--comm/third_party/botan/src/lib/mac/cbc_mac/cbc_mac.cpp99
-rw-r--r--comm/third_party/botan/src/lib/mac/cbc_mac/cbc_mac.h50
-rw-r--r--comm/third_party/botan/src/lib/mac/cbc_mac/info.txt7
-rw-r--r--comm/third_party/botan/src/lib/mac/cmac/cmac.cpp139
-rw-r--r--comm/third_party/botan/src/lib/mac/cmac/cmac.h67
-rw-r--r--comm/third_party/botan/src/lib/mac/cmac/info.txt8
-rw-r--r--comm/third_party/botan/src/lib/mac/gmac/gmac.cpp134
-rw-r--r--comm/third_party/botan/src/lib/mac/gmac/gmac.h64
-rw-r--r--comm/third_party/botan/src/lib/mac/gmac/info.txt8
-rw-r--r--comm/third_party/botan/src/lib/mac/hmac/hmac.cpp150
-rw-r--r--comm/third_party/botan/src/lib/mac/hmac/hmac.h52
-rw-r--r--comm/third_party/botan/src/lib/mac/hmac/info.txt7
-rw-r--r--comm/third_party/botan/src/lib/mac/info.txt7
-rw-r--r--comm/third_party/botan/src/lib/mac/mac.cpp171
-rw-r--r--comm/third_party/botan/src/lib/mac/mac.h143
-rw-r--r--comm/third_party/botan/src/lib/mac/poly1305/info.txt7
-rw-r--r--comm/third_party/botan/src/lib/mac/poly1305/poly1305.cpp211
-rw-r--r--comm/third_party/botan/src/lib/mac/poly1305/poly1305.h50
-rw-r--r--comm/third_party/botan/src/lib/mac/siphash/info.txt3
-rw-r--r--comm/third_party/botan/src/lib/mac/siphash/siphash.cpp136
-rw-r--r--comm/third_party/botan/src/lib/mac/siphash/siphash.h47
-rw-r--r--comm/third_party/botan/src/lib/mac/x919_mac/info.txt7
-rw-r--r--comm/third_party/botan/src/lib/mac/x919_mac/x919_mac.cpp99
-rw-r--r--comm/third_party/botan/src/lib/mac/x919_mac/x919_mac.h51
-rw-r--r--comm/third_party/botan/src/lib/math/bigint/big_code.cpp200
-rw-r--r--comm/third_party/botan/src/lib/math/bigint/big_io.cpp62
-rw-r--r--comm/third_party/botan/src/lib/math/bigint/big_ops2.cpp314
-rw-r--r--comm/third_party/botan/src/lib/math/bigint/big_ops3.cpp214
-rw-r--r--comm/third_party/botan/src/lib/math/bigint/big_rand.cpp64
-rw-r--r--comm/third_party/botan/src/lib/math/bigint/bigint.cpp551
-rw-r--r--comm/third_party/botan/src/lib/math/bigint/bigint.h1153
-rw-r--r--comm/third_party/botan/src/lib/math/bigint/divide.cpp236
-rw-r--r--comm/third_party/botan/src/lib/math/bigint/divide.h101
-rw-r--r--comm/third_party/botan/src/lib/math/bigint/info.txt14
-rw-r--r--comm/third_party/botan/src/lib/math/mp/info.txt10
-rw-r--r--comm/third_party/botan/src/lib/math/mp/mp_asmi.h611
-rw-r--r--comm/third_party/botan/src/lib/math/mp/mp_comba.cpp2211
-rw-r--r--comm/third_party/botan/src/lib/math/mp/mp_core.h819
-rw-r--r--comm/third_party/botan/src/lib/math/mp/mp_karat.cpp408
-rw-r--r--comm/third_party/botan/src/lib/math/mp/mp_madd.h146
-rw-r--r--comm/third_party/botan/src/lib/math/mp/mp_monty.cpp133
-rw-r--r--comm/third_party/botan/src/lib/math/mp/mp_monty.h31
-rw-r--r--comm/third_party/botan/src/lib/math/mp/mp_monty_n.cpp2614
-rw-r--r--comm/third_party/botan/src/lib/math/numbertheory/curve_nistp.h49
-rw-r--r--comm/third_party/botan/src/lib/math/numbertheory/dsa_gen.cpp136
-rw-r--r--comm/third_party/botan/src/lib/math/numbertheory/info.txt22
-rw-r--r--comm/third_party/botan/src/lib/math/numbertheory/jacobi.cpp52
-rw-r--r--comm/third_party/botan/src/lib/math/numbertheory/make_prm.cpp293
-rw-r--r--comm/third_party/botan/src/lib/math/numbertheory/mod_inv.cpp356
-rw-r--r--comm/third_party/botan/src/lib/math/numbertheory/monty.cpp444
-rw-r--r--comm/third_party/botan/src/lib/math/numbertheory/monty.h191
-rw-r--r--comm/third_party/botan/src/lib/math/numbertheory/monty_exp.cpp254
-rw-r--r--comm/third_party/botan/src/lib/math/numbertheory/monty_exp.h54
-rw-r--r--comm/third_party/botan/src/lib/math/numbertheory/mp_numth.cpp84
-rw-r--r--comm/third_party/botan/src/lib/math/numbertheory/nistp_redc.cpp583
-rw-r--r--comm/third_party/botan/src/lib/math/numbertheory/numthry.cpp268
-rw-r--r--comm/third_party/botan/src/lib/math/numbertheory/numthry.h296
-rw-r--r--comm/third_party/botan/src/lib/math/numbertheory/pow_mod.cpp328
-rw-r--r--comm/third_party/botan/src/lib/math/numbertheory/pow_mod.h122
-rw-r--r--comm/third_party/botan/src/lib/math/numbertheory/primality.cpp203
-rw-r--r--comm/third_party/botan/src/lib/math/numbertheory/primality.h100
-rw-r--r--comm/third_party/botan/src/lib/math/numbertheory/primes.cpp609
-rw-r--r--comm/third_party/botan/src/lib/math/numbertheory/reducer.cpp119
-rw-r--r--comm/third_party/botan/src/lib/math/numbertheory/reducer.h69
-rw-r--r--comm/third_party/botan/src/lib/math/numbertheory/ressol.cpp100
-rw-r--r--comm/third_party/botan/src/lib/misc/aont/info.txt9
-rw-r--r--comm/third_party/botan/src/lib/misc/aont/package.cpp125
-rw-r--r--comm/third_party/botan/src/lib/misc/aont/package.h49
-rw-r--r--comm/third_party/botan/src/lib/misc/cryptobox/cryptobox.cpp180
-rw-r--r--comm/third_party/botan/src/lib/misc/cryptobox/cryptobox.h79
-rw-r--r--comm/third_party/botan/src/lib/misc/cryptobox/info.txt14
-rw-r--r--comm/third_party/botan/src/lib/misc/fpe_fe1/fpe_fe1.cpp218
-rw-r--r--comm/third_party/botan/src/lib/misc/fpe_fe1/fpe_fe1.h123
-rw-r--r--comm/third_party/botan/src/lib/misc/fpe_fe1/info.txt10
-rw-r--r--comm/third_party/botan/src/lib/misc/hotp/hotp.cpp63
-rw-r--r--comm/third_party/botan/src/lib/misc/hotp/hotp.h14
-rw-r--r--comm/third_party/botan/src/lib/misc/hotp/info.txt9
-rw-r--r--comm/third_party/botan/src/lib/misc/hotp/otp.h117
-rw-r--r--comm/third_party/botan/src/lib/misc/hotp/totp.cpp63
-rw-r--r--comm/third_party/botan/src/lib/misc/hotp/totp.h13
-rw-r--r--comm/third_party/botan/src/lib/misc/nist_keywrap/info.txt7
-rw-r--r--comm/third_party/botan/src/lib/misc/nist_keywrap/nist_keywrap.cpp209
-rw-r--r--comm/third_party/botan/src/lib/misc/nist_keywrap/nist_keywrap.h67
-rw-r--r--comm/third_party/botan/src/lib/misc/rfc3394/info.txt8
-rw-r--r--comm/third_party/botan/src/lib/misc/rfc3394/rfc3394.cpp44
-rw-r--r--comm/third_party/botan/src/lib/misc/rfc3394/rfc3394.h39
-rw-r--r--comm/third_party/botan/src/lib/misc/roughtime/info.txt10
-rw-r--r--comm/third_party/botan/src/lib/misc/roughtime/roughtime.cpp466
-rw-r--r--comm/third_party/botan/src/lib/misc/roughtime/roughtime.h167
-rw-r--r--comm/third_party/botan/src/lib/misc/srp6/info.txt8
-rw-r--r--comm/third_party/botan/src/lib/misc/srp6/srp6.cpp193
-rw-r--r--comm/third_party/botan/src/lib/misc/srp6/srp6.h155
-rw-r--r--comm/third_party/botan/src/lib/misc/tss/info.txt10
-rw-r--r--comm/third_party/botan/src/lib/misc/tss/tss.cpp333
-rw-r--r--comm/third_party/botan/src/lib/misc/tss/tss.h104
-rw-r--r--comm/third_party/botan/src/lib/modes/aead/aead.cpp176
-rw-r--r--comm/third_party/botan/src/lib/modes/aead/aead.h147
-rw-r--r--comm/third_party/botan/src/lib/modes/aead/ccm/ccm.cpp279
-rw-r--r--comm/third_party/botan/src/lib/modes/aead/ccm/ccm.h130
-rw-r--r--comm/third_party/botan/src/lib/modes/aead/ccm/info.txt7
-rw-r--r--comm/third_party/botan/src/lib/modes/aead/chacha20poly1305/chacha20poly1305.cpp171
-rw-r--r--comm/third_party/botan/src/lib/modes/aead/chacha20poly1305/chacha20poly1305.h104
-rw-r--r--comm/third_party/botan/src/lib/modes/aead/chacha20poly1305/info.txt8
-rw-r--r--comm/third_party/botan/src/lib/modes/aead/eax/eax.cpp194
-rw-r--r--comm/third_party/botan/src/lib/modes/aead/eax/eax.h119
-rw-r--r--comm/third_party/botan/src/lib/modes/aead/eax/info.txt8
-rw-r--r--comm/third_party/botan/src/lib/modes/aead/gcm/gcm.cpp179
-rw-r--r--comm/third_party/botan/src/lib/modes/aead/gcm/gcm.h117
-rw-r--r--comm/third_party/botan/src/lib/modes/aead/gcm/info.txt9
-rw-r--r--comm/third_party/botan/src/lib/modes/aead/info.txt3
-rw-r--r--comm/third_party/botan/src/lib/modes/aead/ocb/info.txt8
-rw-r--r--comm/third_party/botan/src/lib/modes/aead/ocb/ocb.cpp533
-rw-r--r--comm/third_party/botan/src/lib/modes/aead/ocb/ocb.h137
-rw-r--r--comm/third_party/botan/src/lib/modes/aead/siv/info.txt9
-rw-r--r--comm/third_party/botan/src/lib/modes/aead/siv/siv.cpp211
-rw-r--r--comm/third_party/botan/src/lib/modes/aead/siv/siv.h129
-rw-r--r--comm/third_party/botan/src/lib/modes/cbc/cbc.cpp323
-rw-r--r--comm/third_party/botan/src/lib/modes/cbc/cbc.h157
-rw-r--r--comm/third_party/botan/src/lib/modes/cbc/info.txt8
-rw-r--r--comm/third_party/botan/src/lib/modes/cfb/cfb.cpp229
-rw-r--r--comm/third_party/botan/src/lib/modes/cfb/cfb.h106
-rw-r--r--comm/third_party/botan/src/lib/modes/cfb/info.txt7
-rw-r--r--comm/third_party/botan/src/lib/modes/cipher_mode.cpp205
-rw-r--r--comm/third_party/botan/src/lib/modes/cipher_mode.h198
-rw-r--r--comm/third_party/botan/src/lib/modes/info.txt9
-rw-r--r--comm/third_party/botan/src/lib/modes/mode_pad/info.txt3
-rw-r--r--comm/third_party/botan/src/lib/modes/mode_pad/mode_pad.cpp333
-rw-r--r--comm/third_party/botan/src/lib/modes/mode_pad/mode_pad.h160
-rw-r--r--comm/third_party/botan/src/lib/modes/stream_mode.h84
-rw-r--r--comm/third_party/botan/src/lib/modes/xts/info.txt8
-rw-r--r--comm/third_party/botan/src/lib/modes/xts/xts.cpp248
-rw-r--r--comm/third_party/botan/src/lib/modes/xts/xts.h103
-rw-r--r--comm/third_party/botan/src/lib/passhash/bcrypt/bcrypt.cpp181
-rw-r--r--comm/third_party/botan/src/lib/passhash/bcrypt/bcrypt.h49
-rw-r--r--comm/third_party/botan/src/lib/passhash/bcrypt/info.txt9
-rw-r--r--comm/third_party/botan/src/lib/passhash/passhash9/info.txt9
-rw-r--r--comm/third_party/botan/src/lib/passhash/passhash9/passhash9.cpp142
-rw-r--r--comm/third_party/botan/src/lib/passhash/passhash9/passhash9.h52
-rw-r--r--comm/third_party/botan/src/lib/pbkdf/argon2/argon2.cpp443
-rw-r--r--comm/third_party/botan/src/lib/pbkdf/argon2/argon2.h118
-rw-r--r--comm/third_party/botan/src/lib/pbkdf/argon2/argon2fmt.cpp125
-rw-r--r--comm/third_party/botan/src/lib/pbkdf/argon2/argon2pwhash.cpp154
-rw-r--r--comm/third_party/botan/src/lib/pbkdf/argon2/info.txt9
-rw-r--r--comm/third_party/botan/src/lib/pbkdf/bcrypt_pbkdf/bcrypt_pbkdf.cpp183
-rw-r--r--comm/third_party/botan/src/lib/pbkdf/bcrypt_pbkdf/bcrypt_pbkdf.h77
-rw-r--r--comm/third_party/botan/src/lib/pbkdf/bcrypt_pbkdf/info.txt8
-rw-r--r--comm/third_party/botan/src/lib/pbkdf/info.txt13
-rw-r--r--comm/third_party/botan/src/lib/pbkdf/pbkdf.cpp133
-rw-r--r--comm/third_party/botan/src/lib/pbkdf/pbkdf.h246
-rw-r--r--comm/third_party/botan/src/lib/pbkdf/pbkdf1/info.txt7
-rw-r--r--comm/third_party/botan/src/lib/pbkdf/pbkdf1/pbkdf1.cpp54
-rw-r--r--comm/third_party/botan/src/lib/pbkdf/pbkdf1/pbkdf1.h53
-rw-r--r--comm/third_party/botan/src/lib/pbkdf/pbkdf2/info.txt7
-rw-r--r--comm/third_party/botan/src/lib/pbkdf/pbkdf2/pbkdf2.cpp228
-rw-r--r--comm/third_party/botan/src/lib/pbkdf/pbkdf2/pbkdf2.h117
-rw-r--r--comm/third_party/botan/src/lib/pbkdf/pgp_s2k/info.txt7
-rw-r--r--comm/third_party/botan/src/lib/pbkdf/pgp_s2k/pgp_s2k.cpp219
-rw-r--r--comm/third_party/botan/src/lib/pbkdf/pgp_s2k/pgp_s2k.h164
-rw-r--r--comm/third_party/botan/src/lib/pbkdf/pwdhash.cpp118
-rw-r--r--comm/third_party/botan/src/lib/pbkdf/pwdhash.h162
-rw-r--r--comm/third_party/botan/src/lib/pbkdf/scrypt/info.txt10
-rw-r--r--comm/third_party/botan/src/lib/pbkdf/scrypt/scrypt.cpp249
-rw-r--r--comm/third_party/botan/src/lib/pbkdf/scrypt/scrypt.h127
-rw-r--r--comm/third_party/botan/src/lib/pk_pad/eme.cpp94
-rw-r--r--comm/third_party/botan/src/lib/pk_pad/eme.h94
-rw-r--r--comm/third_party/botan/src/lib/pk_pad/eme_oaep/info.txt7
-rw-r--r--comm/third_party/botan/src/lib/pk_pad/eme_oaep/oaep.cpp168
-rw-r--r--comm/third_party/botan/src/lib/pk_pad/eme_oaep/oaep.h62
-rw-r--r--comm/third_party/botan/src/lib/pk_pad/eme_pkcs1/eme_pkcs.cpp109
-rw-r--r--comm/third_party/botan/src/lib/pk_pad/eme_pkcs1/eme_pkcs.h35
-rw-r--r--comm/third_party/botan/src/lib/pk_pad/eme_pkcs1/info.txt4
-rw-r--r--comm/third_party/botan/src/lib/pk_pad/eme_raw/eme_raw.cpp31
-rw-r--r--comm/third_party/botan/src/lib/pk_pad/eme_raw/eme_raw.h33
-rw-r--r--comm/third_party/botan/src/lib/pk_pad/eme_raw/info.txt3
-rw-r--r--comm/third_party/botan/src/lib/pk_pad/emsa.cpp207
-rw-r--r--comm/third_party/botan/src/lib/pk_pad/emsa.h107
-rw-r--r--comm/third_party/botan/src/lib/pk_pad/emsa1/emsa1.cpp133
-rw-r--r--comm/third_party/botan/src/lib/pk_pad/emsa1/emsa1.h55
-rw-r--r--comm/third_party/botan/src/lib/pk_pad/emsa1/info.txt3
-rw-r--r--comm/third_party/botan/src/lib/pk_pad/emsa_pkcs1/emsa_pkcs1.cpp166
-rw-r--r--comm/third_party/botan/src/lib/pk_pad/emsa_pkcs1/emsa_pkcs1.h94
-rw-r--r--comm/third_party/botan/src/lib/pk_pad/emsa_pkcs1/info.txt7
-rw-r--r--comm/third_party/botan/src/lib/pk_pad/emsa_pssr/info.txt7
-rw-r--r--comm/third_party/botan/src/lib/pk_pad/emsa_pssr/pssr.cpp291
-rw-r--r--comm/third_party/botan/src/lib/pk_pad/emsa_pssr/pssr.h103
-rw-r--r--comm/third_party/botan/src/lib/pk_pad/emsa_raw/emsa_raw.cpp92
-rw-r--r--comm/third_party/botan/src/lib/pk_pad/emsa_raw/emsa_raw.h47
-rw-r--r--comm/third_party/botan/src/lib/pk_pad/emsa_raw/info.txt3
-rw-r--r--comm/third_party/botan/src/lib/pk_pad/emsa_x931/emsa_x931.cpp102
-rw-r--r--comm/third_party/botan/src/lib/pk_pad/emsa_x931/emsa_x931.h52
-rw-r--r--comm/third_party/botan/src/lib/pk_pad/emsa_x931/info.txt7
-rw-r--r--comm/third_party/botan/src/lib/pk_pad/hash_id/hash_id.cpp163
-rw-r--r--comm/third_party/botan/src/lib/pk_pad/hash_id/hash_id.h34
-rw-r--r--comm/third_party/botan/src/lib/pk_pad/hash_id/info.txt3
-rw-r--r--comm/third_party/botan/src/lib/pk_pad/info.txt18
-rw-r--r--comm/third_party/botan/src/lib/pk_pad/iso9796/info.txt9
-rw-r--r--comm/third_party/botan/src/lib/pk_pad/iso9796/iso9796.cpp322
-rw-r--r--comm/third_party/botan/src/lib/pk_pad/iso9796/iso9796.h98
-rw-r--r--comm/third_party/botan/src/lib/pk_pad/mgf1/info.txt3
-rw-r--r--comm/third_party/botan/src/lib/pk_pad/mgf1/mgf1.cpp36
-rw-r--r--comm/third_party/botan/src/lib/pk_pad/mgf1/mgf1.h31
-rw-r--r--comm/third_party/botan/src/lib/pk_pad/padding.cpp44
-rw-r--r--comm/third_party/botan/src/lib/pk_pad/padding.h36
-rw-r--r--comm/third_party/botan/src/lib/prov/commoncrypto/commoncrypto.h62
-rw-r--r--comm/third_party/botan/src/lib/prov/commoncrypto/commoncrypto_block.cpp164
-rw-r--r--comm/third_party/botan/src/lib/prov/commoncrypto/commoncrypto_hash.cpp146
-rw-r--r--comm/third_party/botan/src/lib/prov/commoncrypto/commoncrypto_mode.cpp247
-rw-r--r--comm/third_party/botan/src/lib/prov/commoncrypto/commoncrypto_utils.cpp196
-rw-r--r--comm/third_party/botan/src/lib/prov/commoncrypto/commoncrypto_utils.h35
-rw-r--r--comm/third_party/botan/src/lib/prov/commoncrypto/info.txt19
-rw-r--r--comm/third_party/botan/src/lib/prov/openssl/info.txt21
-rw-r--r--comm/third_party/botan/src/lib/prov/openssl/openssl.h118
-rw-r--r--comm/third_party/botan/src/lib/prov/openssl/openssl_block.cpp234
-rw-r--r--comm/third_party/botan/src/lib/prov/openssl/openssl_ec.cpp383
-rw-r--r--comm/third_party/botan/src/lib/prov/openssl/openssl_hash.cpp136
-rw-r--r--comm/third_party/botan/src/lib/prov/openssl/openssl_mode.cpp233
-rw-r--r--comm/third_party/botan/src/lib/prov/openssl/openssl_rc4.cpp93
-rw-r--r--comm/third_party/botan/src/lib/prov/openssl/openssl_rsa.cpp312
-rw-r--r--comm/third_party/botan/src/lib/prov/pkcs11/info.txt36
-rw-r--r--comm/third_party/botan/src/lib/prov/pkcs11/p11.cpp767
-rw-r--r--comm/third_party/botan/src/lib/prov/pkcs11/p11.h2930
-rw-r--r--comm/third_party/botan/src/lib/prov/pkcs11/p11_ecc_key.cpp136
-rw-r--r--comm/third_party/botan/src/lib/prov/pkcs11/p11_ecc_key.h223
-rw-r--r--comm/third_party/botan/src/lib/prov/pkcs11/p11_ecdh.cpp125
-rw-r--r--comm/third_party/botan/src/lib/prov/pkcs11/p11_ecdh.h127
-rw-r--r--comm/third_party/botan/src/lib/prov/pkcs11/p11_ecdsa.cpp213
-rw-r--r--comm/third_party/botan/src/lib/prov/pkcs11/p11_ecdsa.h133
-rw-r--r--comm/third_party/botan/src/lib/prov/pkcs11/p11_mechanism.cpp278
-rw-r--r--comm/third_party/botan/src/lib/prov/pkcs11/p11_mechanism.h118
-rw-r--r--comm/third_party/botan/src/lib/prov/pkcs11/p11_module.cpp53
-rw-r--r--comm/third_party/botan/src/lib/prov/pkcs11/p11_module.h15
-rw-r--r--comm/third_party/botan/src/lib/prov/pkcs11/p11_object.cpp227
-rw-r--r--comm/third_party/botan/src/lib/prov/pkcs11/p11_object.h773
-rw-r--r--comm/third_party/botan/src/lib/prov/pkcs11/p11_randomgenerator.cpp31
-rw-r--r--comm/third_party/botan/src/lib/prov/pkcs11/p11_randomgenerator.h70
-rw-r--r--comm/third_party/botan/src/lib/prov/pkcs11/p11_rsa.cpp373
-rw-r--r--comm/third_party/botan/src/lib/prov/pkcs11/p11_rsa.h229
-rw-r--r--comm/third_party/botan/src/lib/prov/pkcs11/p11_session.cpp96
-rw-r--r--comm/third_party/botan/src/lib/prov/pkcs11/p11_session.h15
-rw-r--r--comm/third_party/botan/src/lib/prov/pkcs11/p11_slot.cpp60
-rw-r--r--comm/third_party/botan/src/lib/prov/pkcs11/p11_slot.h15
-rw-r--r--comm/third_party/botan/src/lib/prov/pkcs11/p11_types.h209
-rw-r--r--comm/third_party/botan/src/lib/prov/pkcs11/p11_x509.cpp37
-rw-r--r--comm/third_party/botan/src/lib/prov/pkcs11/p11_x509.h117
-rw-r--r--comm/third_party/botan/src/lib/prov/pkcs11/pkcs11.h264
-rw-r--r--comm/third_party/botan/src/lib/prov/pkcs11/pkcs11f.h938
-rw-r--r--comm/third_party/botan/src/lib/prov/pkcs11/pkcs11t.h2002
-rw-r--r--comm/third_party/botan/src/lib/prov/tpm/info.txt16
-rw-r--r--comm/third_party/botan/src/lib/prov/tpm/tpm.cpp460
-rw-r--r--comm/third_party/botan/src/lib/prov/tpm/tpm.h194
-rw-r--r--comm/third_party/botan/src/lib/psk_db/info.txt11
-rw-r--r--comm/third_party/botan/src/lib/psk_db/psk_db.cpp105
-rw-r--r--comm/third_party/botan/src/lib/psk_db/psk_db.h166
-rw-r--r--comm/third_party/botan/src/lib/psk_db/psk_db_sql.cpp75
-rw-r--r--comm/third_party/botan/src/lib/psk_db/psk_db_sql.h13
-rw-r--r--comm/third_party/botan/src/lib/pubkey/blinding.cpp66
-rw-r--r--comm/third_party/botan/src/lib/pubkey/blinding.h80
-rw-r--r--comm/third_party/botan/src/lib/pubkey/cecpq1/cecpq1.cpp51
-rw-r--r--comm/third_party/botan/src/lib/pubkey/cecpq1/cecpq1.h38
-rw-r--r--comm/third_party/botan/src/lib/pubkey/cecpq1/info.txt9
-rw-r--r--comm/third_party/botan/src/lib/pubkey/curve25519/curve25519.cpp143
-rw-r--r--comm/third_party/botan/src/lib/pubkey/curve25519/curve25519.h123
-rw-r--r--comm/third_party/botan/src/lib/pubkey/curve25519/donna.cpp464
-rw-r--r--comm/third_party/botan/src/lib/pubkey/curve25519/info.txt8
-rw-r--r--comm/third_party/botan/src/lib/pubkey/dh/dh.cpp142
-rw-r--r--comm/third_party/botan/src/lib/pubkey/dh/dh.h81
-rw-r--r--comm/third_party/botan/src/lib/pubkey/dh/info.txt13
-rw-r--r--comm/third_party/botan/src/lib/pubkey/dl_algo/dl_algo.cpp84
-rw-r--r--comm/third_party/botan/src/lib/pubkey/dl_algo/dl_algo.h140
-rw-r--r--comm/third_party/botan/src/lib/pubkey/dl_algo/info.txt10
-rw-r--r--comm/third_party/botan/src/lib/pubkey/dl_group/dl_group.cpp646
-rw-r--r--comm/third_party/botan/src/lib/pubkey/dl_group/dl_group.h357
-rw-r--r--comm/third_party/botan/src/lib/pubkey/dl_group/dl_named.cpp175
-rw-r--r--comm/third_party/botan/src/lib/pubkey/dl_group/info.txt10
-rw-r--r--comm/third_party/botan/src/lib/pubkey/dlies/dlies.cpp219
-rw-r--r--comm/third_party/botan/src/lib/pubkey/dlies/dlies.h163
-rw-r--r--comm/third_party/botan/src/lib/pubkey/dlies/info.txt10
-rw-r--r--comm/third_party/botan/src/lib/pubkey/dsa/dsa.cpp230
-rw-r--r--comm/third_party/botan/src/lib/pubkey/dsa/dsa.h87
-rw-r--r--comm/third_party/botan/src/lib/pubkey/dsa/info.txt12
-rw-r--r--comm/third_party/botan/src/lib/pubkey/ec_group/curve_gfp.cpp568
-rw-r--r--comm/third_party/botan/src/lib/pubkey/ec_group/curve_gfp.h265
-rw-r--r--comm/third_party/botan/src/lib/pubkey/ec_group/ec_group.cpp796
-rw-r--r--comm/third_party/botan/src/lib/pubkey/ec_group/ec_group.h398
-rw-r--r--comm/third_party/botan/src/lib/pubkey/ec_group/ec_named.cpp301
-rw-r--r--comm/third_party/botan/src/lib/pubkey/ec_group/info.txt20
-rw-r--r--comm/third_party/botan/src/lib/pubkey/ec_group/point_gfp.cpp731
-rw-r--r--comm/third_party/botan/src/lib/pubkey/ec_group/point_gfp.h447
-rw-r--r--comm/third_party/botan/src/lib/pubkey/ec_group/point_mul.cpp431
-rw-r--r--comm/third_party/botan/src/lib/pubkey/ec_group/point_mul.h85
-rw-r--r--comm/third_party/botan/src/lib/pubkey/ecc_key/ecc_key.cpp205
-rw-r--r--comm/third_party/botan/src/lib/pubkey/ecc_key/ecc_key.h172
-rw-r--r--comm/third_party/botan/src/lib/pubkey/ecc_key/info.txt11
-rw-r--r--comm/third_party/botan/src/lib/pubkey/ecdh/ecdh.cpp87
-rw-r--r--comm/third_party/botan/src/lib/pubkey/ecdh/ecdh.h106
-rw-r--r--comm/third_party/botan/src/lib/pubkey/ecdh/info.txt10
-rw-r--r--comm/third_party/botan/src/lib/pubkey/ecdsa/ecdsa.cpp308
-rw-r--r--comm/third_party/botan/src/lib/pubkey/ecdsa/ecdsa.h117
-rw-r--r--comm/third_party/botan/src/lib/pubkey/ecdsa/info.txt14
-rw-r--r--comm/third_party/botan/src/lib/pubkey/ecgdsa/ecgdsa.cpp155
-rw-r--r--comm/third_party/botan/src/lib/pubkey/ecgdsa/ecgdsa.h96
-rw-r--r--comm/third_party/botan/src/lib/pubkey/ecgdsa/info.txt15
-rw-r--r--comm/third_party/botan/src/lib/pubkey/ecies/ecies.cpp415
-rw-r--r--comm/third_party/botan/src/lib/pubkey/ecies/ecies.h314
-rw-r--r--comm/third_party/botan/src/lib/pubkey/ecies/info.txt10
-rw-r--r--comm/third_party/botan/src/lib/pubkey/eckcdsa/eckcdsa.cpp208
-rw-r--r--comm/third_party/botan/src/lib/pubkey/eckcdsa/eckcdsa.h96
-rw-r--r--comm/third_party/botan/src/lib/pubkey/eckcdsa/info.txt17
-rw-r--r--comm/third_party/botan/src/lib/pubkey/ed25519/ed25519.cpp102
-rw-r--r--comm/third_party/botan/src/lib/pubkey/ed25519/ed25519.h113
-rw-r--r--comm/third_party/botan/src/lib/pubkey/ed25519/ed25519_fe.cpp754
-rw-r--r--comm/third_party/botan/src/lib/pubkey/ed25519/ed25519_fe.h227
-rw-r--r--comm/third_party/botan/src/lib/pubkey/ed25519/ed25519_internal.h119
-rw-r--r--comm/third_party/botan/src/lib/pubkey/ed25519/ed25519_key.cpp288
-rw-r--r--comm/third_party/botan/src/lib/pubkey/ed25519/ge.cpp2174
-rw-r--r--comm/third_party/botan/src/lib/pubkey/ed25519/info.txt17
-rw-r--r--comm/third_party/botan/src/lib/pubkey/ed25519/sc_muladd.cpp221
-rw-r--r--comm/third_party/botan/src/lib/pubkey/ed25519/sc_reduce.cpp159
-rw-r--r--comm/third_party/botan/src/lib/pubkey/elgamal/elgamal.cpp213
-rw-r--r--comm/third_party/botan/src/lib/pubkey/elgamal/elgamal.h85
-rw-r--r--comm/third_party/botan/src/lib/pubkey/elgamal/info.txt10
-rw-r--r--comm/third_party/botan/src/lib/pubkey/gost_3410/gost_3410.cpp253
-rw-r--r--comm/third_party/botan/src/lib/pubkey/gost_3410/gost_3410.h104
-rw-r--r--comm/third_party/botan/src/lib/pubkey/gost_3410/info.txt12
-rw-r--r--comm/third_party/botan/src/lib/pubkey/info.txt31
-rw-r--r--comm/third_party/botan/src/lib/pubkey/keypair/info.txt6
-rw-r--r--comm/third_party/botan/src/lib/pubkey/keypair/keypair.cpp85
-rw-r--r--comm/third_party/botan/src/lib/pubkey/keypair/keypair.h85
-rw-r--r--comm/third_party/botan/src/lib/pubkey/mce/code_based_key_gen.cpp298
-rw-r--r--comm/third_party/botan/src/lib/pubkey/mce/code_based_util.h57
-rw-r--r--comm/third_party/botan/src/lib/pubkey/mce/gf2m_rootfind_dcmp.cpp314
-rw-r--r--comm/third_party/botan/src/lib/pubkey/mce/gf2m_small_m.cpp126
-rw-r--r--comm/third_party/botan/src/lib/pubkey/mce/gf2m_small_m.h221
-rw-r--r--comm/third_party/botan/src/lib/pubkey/mce/goppa_code.cpp234
-rw-r--r--comm/third_party/botan/src/lib/pubkey/mce/info.txt18
-rw-r--r--comm/third_party/botan/src/lib/pubkey/mce/mce_internal.h53
-rw-r--r--comm/third_party/botan/src/lib/pubkey/mce/mce_workfactor.cpp112
-rw-r--r--comm/third_party/botan/src/lib/pubkey/mce/mceliece.cpp139
-rw-r--r--comm/third_party/botan/src/lib/pubkey/mce/mceliece.h141
-rw-r--r--comm/third_party/botan/src/lib/pubkey/mce/mceliece_key.cpp386
-rw-r--r--comm/third_party/botan/src/lib/pubkey/mce/polyn_gf2m.cpp806
-rw-r--r--comm/third_party/botan/src/lib/pubkey/mce/polyn_gf2m.h174
-rw-r--r--comm/third_party/botan/src/lib/pubkey/mceies/info.txt10
-rw-r--r--comm/third_party/botan/src/lib/pubkey/mceies/mceies.cpp110
-rw-r--r--comm/third_party/botan/src/lib/pubkey/mceies/mceies.h46
-rw-r--r--comm/third_party/botan/src/lib/pubkey/newhope/info.txt12
-rw-r--r--comm/third_party/botan/src/lib/pubkey/newhope/newhope.cpp800
-rw-r--r--comm/third_party/botan/src/lib/pubkey/newhope/newhope.h85
-rw-r--r--comm/third_party/botan/src/lib/pubkey/pbes2/info.txt10
-rw-r--r--comm/third_party/botan/src/lib/pubkey/pbes2/pbes2.cpp339
-rw-r--r--comm/third_party/botan/src/lib/pubkey/pbes2/pbes2.h87
-rw-r--r--comm/third_party/botan/src/lib/pubkey/pem/info.txt7
-rw-r--r--comm/third_party/botan/src/lib/pubkey/pem/pem.cpp169
-rw-r--r--comm/third_party/botan/src/lib/pubkey/pem/pem.h91
-rw-r--r--comm/third_party/botan/src/lib/pubkey/pk_algs.cpp426
-rw-r--r--comm/third_party/botan/src/lib/pubkey/pk_algs.h46
-rw-r--r--comm/third_party/botan/src/lib/pubkey/pk_keys.cpp145
-rw-r--r--comm/third_party/botan/src/lib/pubkey/pk_keys.h329
-rw-r--r--comm/third_party/botan/src/lib/pubkey/pk_ops.cpp173
-rw-r--r--comm/third_party/botan/src/lib/pubkey/pk_ops.h161
-rw-r--r--comm/third_party/botan/src/lib/pubkey/pk_ops_fwd.h27
-rw-r--r--comm/third_party/botan/src/lib/pubkey/pk_ops_impl.h231
-rw-r--r--comm/third_party/botan/src/lib/pubkey/pkcs8.cpp490
-rw-r--r--comm/third_party/botan/src/lib/pubkey/pkcs8.h288
-rw-r--r--comm/third_party/botan/src/lib/pubkey/pubkey.cpp388
-rw-r--r--comm/third_party/botan/src/lib/pubkey/pubkey.h800
-rw-r--r--comm/third_party/botan/src/lib/pubkey/rfc6979/info.txt8
-rw-r--r--comm/third_party/botan/src/lib/pubkey/rfc6979/rfc6979.cpp59
-rw-r--r--comm/third_party/botan/src/lib/pubkey/rfc6979/rfc6979.h55
-rw-r--r--comm/third_party/botan/src/lib/pubkey/rsa/info.txt10
-rw-r--r--comm/third_party/botan/src/lib/pubkey/rsa/rsa.cpp753
-rw-r--r--comm/third_party/botan/src/lib/pubkey/rsa/rsa.h180
-rw-r--r--comm/third_party/botan/src/lib/pubkey/sm2/info.txt14
-rw-r--r--comm/third_party/botan/src/lib/pubkey/sm2/sm2.cpp306
-rw-r--r--comm/third_party/botan/src/lib/pubkey/sm2/sm2.h124
-rw-r--r--comm/third_party/botan/src/lib/pubkey/sm2/sm2_enc.cpp267
-rw-r--r--comm/third_party/botan/src/lib/pubkey/sm2/sm2_enc.h15
-rw-r--r--comm/third_party/botan/src/lib/pubkey/workfactor.cpp66
-rw-r--r--comm/third_party/botan/src/lib/pubkey/workfactor.h51
-rw-r--r--comm/third_party/botan/src/lib/pubkey/x509_key.cpp106
-rw-r--r--comm/third_party/botan/src/lib/pubkey/x509_key.h80
-rw-r--r--comm/third_party/botan/src/lib/pubkey/xmss/atomic.h55
-rw-r--r--comm/third_party/botan/src/lib/pubkey/xmss/info.txt40
-rw-r--r--comm/third_party/botan/src/lib/pubkey/xmss/xmss.h459
-rw-r--r--comm/third_party/botan/src/lib/pubkey/xmss/xmss_address.h405
-rw-r--r--comm/third_party/botan/src/lib/pubkey/xmss/xmss_common_ops.cpp74
-rw-r--r--comm/third_party/botan/src/lib/pubkey/xmss/xmss_common_ops.h83
-rw-r--r--comm/third_party/botan/src/lib/pubkey/xmss/xmss_hash.cpp80
-rw-r--r--comm/third_party/botan/src/lib/pubkey/xmss/xmss_hash.h156
-rw-r--r--comm/third_party/botan/src/lib/pubkey/xmss/xmss_index_registry.cpp84
-rw-r--r--comm/third_party/botan/src/lib/pubkey/xmss/xmss_index_registry.h105
-rw-r--r--comm/third_party/botan/src/lib/pubkey/xmss/xmss_key_pair.h49
-rw-r--r--comm/third_party/botan/src/lib/pubkey/xmss/xmss_parameters.cpp184
-rw-r--r--comm/third_party/botan/src/lib/pubkey/xmss/xmss_parameters.h119
-rw-r--r--comm/third_party/botan/src/lib/pubkey/xmss/xmss_privatekey.cpp405
-rw-r--r--comm/third_party/botan/src/lib/pubkey/xmss/xmss_privatekey.h13
-rw-r--r--comm/third_party/botan/src/lib/pubkey/xmss/xmss_publickey.cpp129
-rw-r--r--comm/third_party/botan/src/lib/pubkey/xmss/xmss_publickey.h14
-rw-r--r--comm/third_party/botan/src/lib/pubkey/xmss/xmss_signature.cpp92
-rw-r--r--comm/third_party/botan/src/lib/pubkey/xmss/xmss_signature.h127
-rw-r--r--comm/third_party/botan/src/lib/pubkey/xmss/xmss_signature_operation.cpp120
-rw-r--r--comm/third_party/botan/src/lib/pubkey/xmss/xmss_signature_operation.h89
-rw-r--r--comm/third_party/botan/src/lib/pubkey/xmss/xmss_tools.h108
-rw-r--r--comm/third_party/botan/src/lib/pubkey/xmss/xmss_verification_operation.cpp138
-rw-r--r--comm/third_party/botan/src/lib/pubkey/xmss/xmss_verification_operation.h71
-rw-r--r--comm/third_party/botan/src/lib/pubkey/xmss/xmss_wots.h752
-rw-r--r--comm/third_party/botan/src/lib/pubkey/xmss/xmss_wots_addressed_privatekey.h68
-rw-r--r--comm/third_party/botan/src/lib/pubkey/xmss/xmss_wots_addressed_publickey.h96
-rw-r--r--comm/third_party/botan/src/lib/pubkey/xmss/xmss_wots_parameters.cpp137
-rw-r--r--comm/third_party/botan/src/lib/pubkey/xmss/xmss_wots_parameters.h14
-rw-r--r--comm/third_party/botan/src/lib/pubkey/xmss/xmss_wots_privatekey.cpp99
-rw-r--r--comm/third_party/botan/src/lib/pubkey/xmss/xmss_wots_privatekey.h15
-rw-r--r--comm/third_party/botan/src/lib/pubkey/xmss/xmss_wots_publickey.cpp72
-rw-r--r--comm/third_party/botan/src/lib/pubkey/xmss/xmss_wots_publickey.h14
-rw-r--r--comm/third_party/botan/src/lib/rng/auto_rng/auto_rng.cpp112
-rw-r--r--comm/third_party/botan/src/lib/rng/auto_rng/auto_rng.h102
-rw-r--r--comm/third_party/botan/src/lib/rng/auto_rng/info.txt8
-rw-r--r--comm/third_party/botan/src/lib/rng/chacha_rng/chacha_rng.cpp87
-rw-r--r--comm/third_party/botan/src/lib/rng/chacha_rng/chacha_rng.h125
-rw-r--r--comm/third_party/botan/src/lib/rng/chacha_rng/info.txt10
-rw-r--r--comm/third_party/botan/src/lib/rng/hmac_drbg/hmac_drbg.cpp197
-rw-r--r--comm/third_party/botan/src/lib/rng/hmac_drbg/hmac_drbg.h150
-rw-r--r--comm/third_party/botan/src/lib/rng/hmac_drbg/info.txt8
-rw-r--r--comm/third_party/botan/src/lib/rng/info.txt3
-rw-r--r--comm/third_party/botan/src/lib/rng/processor_rng/info.txt20
-rw-r--r--comm/third_party/botan/src/lib/rng/processor_rng/processor_rng.cpp157
-rw-r--r--comm/third_party/botan/src/lib/rng/processor_rng/processor_rng.h52
-rw-r--r--comm/third_party/botan/src/lib/rng/rdrand_rng/info.txt13
-rw-r--r--comm/third_party/botan/src/lib/rng/rdrand_rng/rdrand_rng.cpp67
-rw-r--r--comm/third_party/botan/src/lib/rng/rdrand_rng/rdrand_rng.h68
-rw-r--r--comm/third_party/botan/src/lib/rng/rng.cpp91
-rw-r--r--comm/third_party/botan/src/lib/rng/rng.h297
-rw-r--r--comm/third_party/botan/src/lib/rng/stateful_rng/info.txt3
-rw-r--r--comm/third_party/botan/src/lib/rng/stateful_rng/stateful_rng.cpp190
-rw-r--r--comm/third_party/botan/src/lib/rng/stateful_rng/stateful_rng.h166
-rw-r--r--comm/third_party/botan/src/lib/rng/system_rng/info.txt18
-rw-r--r--comm/third_party/botan/src/lib/rng/system_rng/system_rng.cpp289
-rw-r--r--comm/third_party/botan/src/lib/rng/system_rng/system_rng.h43
-rw-r--r--comm/third_party/botan/src/lib/stream/chacha/chacha.cpp384
-rw-r--r--comm/third_party/botan/src/lib/stream/chacha/chacha.h82
-rw-r--r--comm/third_party/botan/src/lib/stream/chacha/chacha_avx2/chacha_avx2.cpp207
-rw-r--r--comm/third_party/botan/src/lib/stream/chacha/chacha_avx2/info.txt11
-rw-r--r--comm/third_party/botan/src/lib/stream/chacha/chacha_simd32/chacha_simd32.cpp205
-rw-r--r--comm/third_party/botan/src/lib/stream/chacha/chacha_simd32/info.txt7
-rw-r--r--comm/third_party/botan/src/lib/stream/chacha/info.txt3
-rw-r--r--comm/third_party/botan/src/lib/stream/ctr/ctr.cpp256
-rw-r--r--comm/third_party/botan/src/lib/stream/ctr/ctr.h65
-rw-r--r--comm/third_party/botan/src/lib/stream/ctr/info.txt7
-rw-r--r--comm/third_party/botan/src/lib/stream/info.txt7
-rw-r--r--comm/third_party/botan/src/lib/stream/ofb/info.txt7
-rw-r--r--comm/third_party/botan/src/lib/stream/ofb/ofb.cpp92
-rw-r--r--comm/third_party/botan/src/lib/stream/ofb/ofb.h56
-rw-r--r--comm/third_party/botan/src/lib/stream/rc4/info.txt3
-rw-r--r--comm/third_party/botan/src/lib/stream/rc4/rc4.cpp133
-rw-r--r--comm/third_party/botan/src/lib/stream/rc4/rc4.h57
-rw-r--r--comm/third_party/botan/src/lib/stream/salsa20/info.txt3
-rw-r--r--comm/third_party/botan/src/lib/stream/salsa20/salsa20.cpp302
-rw-r--r--comm/third_party/botan/src/lib/stream/salsa20/salsa20.h54
-rw-r--r--comm/third_party/botan/src/lib/stream/shake_cipher/info.txt7
-rw-r--r--comm/third_party/botan/src/lib/stream/shake_cipher/shake_cipher.cpp90
-rw-r--r--comm/third_party/botan/src/lib/stream/shake_cipher/shake_cipher.h57
-rw-r--r--comm/third_party/botan/src/lib/stream/stream_cipher.cpp149
-rw-r--r--comm/third_party/botan/src/lib/stream/stream_cipher.h147
-rw-r--r--comm/third_party/botan/src/lib/tls/asio/asio_async_ops.h355
-rw-r--r--comm/third_party/botan/src/lib/tls/asio/asio_context.h120
-rw-r--r--comm/third_party/botan/src/lib/tls/asio/asio_error.h151
-rw-r--r--comm/third_party/botan/src/lib/tls/asio/asio_stream.h835
-rw-r--r--comm/third_party/botan/src/lib/tls/asio/info.txt15
-rw-r--r--comm/third_party/botan/src/lib/tls/credentials_manager.cpp105
-rw-r--r--comm/third_party/botan/src/lib/tls/credentials_manager.h196
-rw-r--r--comm/third_party/botan/src/lib/tls/info.txt54
-rw-r--r--comm/third_party/botan/src/lib/tls/msg_cert_req.cpp156
-rw-r--r--comm/third_party/botan/src/lib/tls/msg_cert_status.cpp71
-rw-r--r--comm/third_party/botan/src/lib/tls/msg_cert_verify.cpp110
-rw-r--r--comm/third_party/botan/src/lib/tls/msg_certificate.cpp109
-rw-r--r--comm/third_party/botan/src/lib/tls/msg_client_hello.cpp465
-rw-r--r--comm/third_party/botan/src/lib/tls/msg_client_kex.cpp404
-rw-r--r--comm/third_party/botan/src/lib/tls/msg_finished.cpp91
-rw-r--r--comm/third_party/botan/src/lib/tls/msg_hello_verify.cpp69
-rw-r--r--comm/third_party/botan/src/lib/tls/msg_server_hello.cpp251
-rw-r--r--comm/third_party/botan/src/lib/tls/msg_server_kex.cpp334
-rw-r--r--comm/third_party/botan/src/lib/tls/msg_session_ticket.cpp56
-rw-r--r--comm/third_party/botan/src/lib/tls/sessions_sql/info.txt7
-rw-r--r--comm/third_party/botan/src/lib/tls/sessions_sql/tls_session_manager_sql.cpp213
-rw-r--r--comm/third_party/botan/src/lib/tls/sessions_sql/tls_session_manager_sql.h81
-rw-r--r--comm/third_party/botan/src/lib/tls/sessions_sqlite3/info.txt8
-rw-r--r--comm/third_party/botan/src/lib/tls/sessions_sqlite3/tls_session_manager_sqlite.cpp29
-rw-r--r--comm/third_party/botan/src/lib/tls/sessions_sqlite3/tls_session_manager_sqlite.h53
-rw-r--r--comm/third_party/botan/src/lib/tls/tls_10/info.txt10
-rw-r--r--comm/third_party/botan/src/lib/tls/tls_alert.cpp126
-rw-r--r--comm/third_party/botan/src/lib/tls/tls_alert.h116
-rw-r--r--comm/third_party/botan/src/lib/tls/tls_algos.cpp426
-rw-r--r--comm/third_party/botan/src/lib/tls/tls_algos.h171
-rw-r--r--comm/third_party/botan/src/lib/tls/tls_blocking.cpp97
-rw-r--r--comm/third_party/botan/src/lib/tls/tls_blocking.h103
-rw-r--r--comm/third_party/botan/src/lib/tls/tls_callbacks.cpp191
-rw-r--r--comm/third_party/botan/src/lib/tls/tls_callbacks.h484
-rw-r--r--comm/third_party/botan/src/lib/tls/tls_cbc/info.txt12
-rw-r--r--comm/third_party/botan/src/lib/tls/tls_cbc/tls_cbc.cpp499
-rw-r--r--comm/third_party/botan/src/lib/tls/tls_cbc/tls_cbc.h186
-rw-r--r--comm/third_party/botan/src/lib/tls/tls_channel.cpp795
-rw-r--r--comm/third_party/botan/src/lib/tls/tls_channel.h318
-rw-r--r--comm/third_party/botan/src/lib/tls/tls_ciphersuite.cpp253
-rw-r--r--comm/third_party/botan/src/lib/tls/tls_ciphersuite.h189
-rw-r--r--comm/third_party/botan/src/lib/tls/tls_client.cpp780
-rw-r--r--comm/third_party/botan/src/lib/tls/tls_client.h169
-rw-r--r--comm/third_party/botan/src/lib/tls/tls_exceptn.h52
-rw-r--r--comm/third_party/botan/src/lib/tls/tls_extensions.cpp660
-rw-r--r--comm/third_party/botan/src/lib/tls/tls_extensions.h551
-rw-r--r--comm/third_party/botan/src/lib/tls/tls_handshake_hash.cpp34
-rw-r--r--comm/third_party/botan/src/lib/tls/tls_handshake_hash.h44
-rw-r--r--comm/third_party/botan/src/lib/tls/tls_handshake_io.cpp480
-rw-r--r--comm/third_party/botan/src/lib/tls/tls_handshake_io.h218
-rw-r--r--comm/third_party/botan/src/lib/tls/tls_handshake_msg.h51
-rw-r--r--comm/third_party/botan/src/lib/tls/tls_handshake_state.cpp580
-rw-r--r--comm/third_party/botan/src/lib/tls/tls_handshake_state.h206
-rw-r--r--comm/third_party/botan/src/lib/tls/tls_magic.h72
-rw-r--r--comm/third_party/botan/src/lib/tls/tls_messages.h653
-rw-r--r--comm/third_party/botan/src/lib/tls/tls_policy.cpp616
-rw-r--r--comm/third_party/botan/src/lib/tls/tls_policy.h616
-rw-r--r--comm/third_party/botan/src/lib/tls/tls_reader.h231
-rw-r--r--comm/third_party/botan/src/lib/tls/tls_record.cpp534
-rw-r--r--comm/third_party/botan/src/lib/tls/tls_record.h188
-rw-r--r--comm/third_party/botan/src/lib/tls/tls_seq_numbers.h174
-rw-r--r--comm/third_party/botan/src/lib/tls/tls_server.cpp1025
-rw-r--r--comm/third_party/botan/src/lib/tls/tls_server.h169
-rw-r--r--comm/third_party/botan/src/lib/tls/tls_server_info.h104
-rw-r--r--comm/third_party/botan/src/lib/tls/tls_session.cpp299
-rw-r--r--comm/third_party/botan/src/lib/tls/tls_session.h210
-rw-r--r--comm/third_party/botan/src/lib/tls/tls_session_key.cpp101
-rw-r--r--comm/third_party/botan/src/lib/tls/tls_session_key.h82
-rw-r--r--comm/third_party/botan/src/lib/tls/tls_session_manager.h160
-rw-r--r--comm/third_party/botan/src/lib/tls/tls_session_manager_memory.cpp132
-rw-r--r--comm/third_party/botan/src/lib/tls/tls_suite_info.cpp212
-rw-r--r--comm/third_party/botan/src/lib/tls/tls_text_policy.cpp319
-rw-r--r--comm/third_party/botan/src/lib/tls/tls_version.cpp88
-rw-r--r--comm/third_party/botan/src/lib/tls/tls_version.h156
-rw-r--r--comm/third_party/botan/src/lib/utils/assert.cpp54
-rw-r--r--comm/third_party/botan/src/lib/utils/assert.h157
-rw-r--r--comm/third_party/botan/src/lib/utils/bit_ops.h171
-rw-r--r--comm/third_party/botan/src/lib/utils/boost/info.txt9
-rw-r--r--comm/third_party/botan/src/lib/utils/bswap.h108
-rw-r--r--comm/third_party/botan/src/lib/utils/calendar.cpp124
-rw-r--r--comm/third_party/botan/src/lib/utils/calendar.h91
-rw-r--r--comm/third_party/botan/src/lib/utils/charset.cpp283
-rw-r--r--comm/third_party/botan/src/lib/utils/charset.h80
-rw-r--r--comm/third_party/botan/src/lib/utils/codec_base.h220
-rw-r--r--comm/third_party/botan/src/lib/utils/compiler.h225
-rw-r--r--comm/third_party/botan/src/lib/utils/cpuid/cpuid.cpp231
-rw-r--r--comm/third_party/botan/src/lib/utils/cpuid/cpuid.h484
-rw-r--r--comm/third_party/botan/src/lib/utils/cpuid/cpuid_arm.cpp237
-rw-r--r--comm/third_party/botan/src/lib/utils/cpuid/cpuid_ppc.cpp132
-rw-r--r--comm/third_party/botan/src/lib/utils/cpuid/cpuid_x86.cpp214
-rw-r--r--comm/third_party/botan/src/lib/utils/cpuid/info.txt7
-rw-r--r--comm/third_party/botan/src/lib/utils/ct_utils.cpp83
-rw-r--r--comm/third_party/botan/src/lib/utils/ct_utils.h418
-rw-r--r--comm/third_party/botan/src/lib/utils/data_src.cpp214
-rw-r--r--comm/third_party/botan/src/lib/utils/data_src.h181
-rw-r--r--comm/third_party/botan/src/lib/utils/database.h88
-rw-r--r--comm/third_party/botan/src/lib/utils/donna128.h143
-rw-r--r--comm/third_party/botan/src/lib/utils/dyn_load/dyn_load.cpp82
-rw-r--r--comm/third_party/botan/src/lib/utils/dyn_load/dyn_load.h68
-rw-r--r--comm/third_party/botan/src/lib/utils/dyn_load/info.txt18
-rw-r--r--comm/third_party/botan/src/lib/utils/exceptn.cpp183
-rw-r--r--comm/third_party/botan/src/lib/utils/exceptn.h441
-rw-r--r--comm/third_party/botan/src/lib/utils/filesystem.cpp144
-rw-r--r--comm/third_party/botan/src/lib/utils/filesystem.h33
-rw-r--r--comm/third_party/botan/src/lib/utils/ghash/ghash.cpp236
-rw-r--r--comm/third_party/botan/src/lib/utils/ghash/ghash.h110
-rw-r--r--comm/third_party/botan/src/lib/utils/ghash/ghash_cpu/ghash_cpu.cpp207
-rw-r--r--comm/third_party/botan/src/lib/utils/ghash/ghash_cpu/info.txt34
-rw-r--r--comm/third_party/botan/src/lib/utils/ghash/ghash_vperm/ghash_vperm.cpp62
-rw-r--r--comm/third_party/botan/src/lib/utils/ghash/ghash_vperm/info.txt8
-rw-r--r--comm/third_party/botan/src/lib/utils/ghash/info.txt3
-rw-r--r--comm/third_party/botan/src/lib/utils/http_util/http_util.cpp267
-rw-r--r--comm/third_party/botan/src/lib/utils/http_util/http_util.h107
-rw-r--r--comm/third_party/botan/src/lib/utils/http_util/info.txt11
-rw-r--r--comm/third_party/botan/src/lib/utils/info.txt43
-rw-r--r--comm/third_party/botan/src/lib/utils/loadstor.h701
-rw-r--r--comm/third_party/botan/src/lib/utils/locking_allocator/info.txt12
-rw-r--r--comm/third_party/botan/src/lib/utils/locking_allocator/locking_allocator.cpp75
-rw-r--r--comm/third_party/botan/src/lib/utils/locking_allocator/locking_allocator.h45
-rw-r--r--comm/third_party/botan/src/lib/utils/mem_ops.cpp68
-rw-r--r--comm/third_party/botan/src/lib/utils/mem_ops.h365
-rw-r--r--comm/third_party/botan/src/lib/utils/mem_pool/info.txt7
-rw-r--r--comm/third_party/botan/src/lib/utils/mem_pool/mem_pool.cpp435
-rw-r--r--comm/third_party/botan/src/lib/utils/mem_pool/mem_pool.h58
-rw-r--r--comm/third_party/botan/src/lib/utils/mul128.h125
-rw-r--r--comm/third_party/botan/src/lib/utils/mutex.h60
-rw-r--r--comm/third_party/botan/src/lib/utils/os_utils.cpp757
-rw-r--r--comm/third_party/botan/src/lib/utils/os_utils.h200
-rw-r--r--comm/third_party/botan/src/lib/utils/parsing.cpp458
-rw-r--r--comm/third_party/botan/src/lib/utils/parsing.h181
-rw-r--r--comm/third_party/botan/src/lib/utils/poly_dbl/info.txt7
-rw-r--r--comm/third_party/botan/src/lib/utils/poly_dbl/poly_dbl.cpp115
-rw-r--r--comm/third_party/botan/src/lib/utils/poly_dbl/poly_dbl.h39
-rw-r--r--comm/third_party/botan/src/lib/utils/prefetch.h39
-rw-r--r--comm/third_party/botan/src/lib/utils/read_cfg.cpp63
-rw-r--r--comm/third_party/botan/src/lib/utils/read_kv.cpp85
-rw-r--r--comm/third_party/botan/src/lib/utils/rotate.h112
-rw-r--r--comm/third_party/botan/src/lib/utils/rounding.h56
-rw-r--r--comm/third_party/botan/src/lib/utils/safeint.h41
-rw-r--r--comm/third_party/botan/src/lib/utils/simd/info.txt27
-rw-r--r--comm/third_party/botan/src/lib/utils/simd/simd_32.h621
-rw-r--r--comm/third_party/botan/src/lib/utils/simd/simd_avx2/info.txt21
-rw-r--r--comm/third_party/botan/src/lib/utils/simd/simd_avx2/simd_avx2.h299
-rw-r--r--comm/third_party/botan/src/lib/utils/socket/info.txt18
-rw-r--r--comm/third_party/botan/src/lib/utils/socket/socket.cpp371
-rw-r--r--comm/third_party/botan/src/lib/utils/socket/socket.h64
-rw-r--r--comm/third_party/botan/src/lib/utils/socket/socket_udp.cpp344
-rw-r--r--comm/third_party/botan/src/lib/utils/socket/socket_udp.h73
-rw-r--r--comm/third_party/botan/src/lib/utils/socket/uri.cpp188
-rw-r--r--comm/third_party/botan/src/lib/utils/socket/uri.h49
-rw-r--r--comm/third_party/botan/src/lib/utils/sqlite3/info.txt13
-rw-r--r--comm/third_party/botan/src/lib/utils/sqlite3/sqlite3.cpp166
-rw-r--r--comm/third_party/botan/src/lib/utils/sqlite3/sqlite3.h58
-rw-r--r--comm/third_party/botan/src/lib/utils/stl_compatibility.h80
-rw-r--r--comm/third_party/botan/src/lib/utils/stl_util.h110
-rw-r--r--comm/third_party/botan/src/lib/utils/thread_utils/barrier.cpp36
-rw-r--r--comm/third_party/botan/src/lib/utils/thread_utils/barrier.h42
-rw-r--r--comm/third_party/botan/src/lib/utils/thread_utils/info.txt14
-rw-r--r--comm/third_party/botan/src/lib/utils/thread_utils/rwlock.cpp58
-rw-r--r--comm/third_party/botan/src/lib/utils/thread_utils/rwlock.h42
-rw-r--r--comm/third_party/botan/src/lib/utils/thread_utils/semaphore.cpp38
-rw-r--r--comm/third_party/botan/src/lib/utils/thread_utils/semaphore.h34
-rw-r--r--comm/third_party/botan/src/lib/utils/thread_utils/thread_pool.cpp103
-rw-r--r--comm/third_party/botan/src/lib/utils/thread_utils/thread_pool.h82
-rw-r--r--comm/third_party/botan/src/lib/utils/timer.cpp150
-rw-r--r--comm/third_party/botan/src/lib/utils/timer.h185
-rw-r--r--comm/third_party/botan/src/lib/utils/types.h112
-rw-r--r--comm/third_party/botan/src/lib/utils/uuid/info.txt8
-rw-r--r--comm/third_party/botan/src/lib/utils/uuid/uuid.cpp82
-rw-r--r--comm/third_party/botan/src/lib/utils/uuid/uuid.h69
-rw-r--r--comm/third_party/botan/src/lib/utils/version.cpp100
-rw-r--r--comm/third_party/botan/src/lib/utils/version.h101
-rw-r--r--comm/third_party/botan/src/lib/x509/asn1_alt_name.cpp265
-rw-r--r--comm/third_party/botan/src/lib/x509/asn1_alt_name.h11
-rw-r--r--comm/third_party/botan/src/lib/x509/asn1_attribute.h11
-rw-r--r--comm/third_party/botan/src/lib/x509/cert_status.cpp125
-rw-r--r--comm/third_party/botan/src/lib/x509/cert_status.h11
-rw-r--r--comm/third_party/botan/src/lib/x509/certstor.cpp233
-rw-r--r--comm/third_party/botan/src/lib/x509/certstor.h165
-rw-r--r--comm/third_party/botan/src/lib/x509/certstor_flatfile/certstor_flatfile.cpp148
-rw-r--r--comm/third_party/botan/src/lib/x509/certstor_flatfile/certstor_flatfile.h77
-rw-r--r--comm/third_party/botan/src/lib/x509/certstor_flatfile/info.txt12
-rw-r--r--comm/third_party/botan/src/lib/x509/certstor_sql/certstor_sql.cpp335
-rw-r--r--comm/third_party/botan/src/lib/x509/certstor_sql/certstor_sql.h119
-rw-r--r--comm/third_party/botan/src/lib/x509/certstor_sql/info.txt3
-rw-r--r--comm/third_party/botan/src/lib/x509/certstor_sqlite3/certstor_sqlite.cpp19
-rw-r--r--comm/third_party/botan/src/lib/x509/certstor_sqlite3/certstor_sqlite.h34
-rw-r--r--comm/third_party/botan/src/lib/x509/certstor_sqlite3/info.txt8
-rw-r--r--comm/third_party/botan/src/lib/x509/certstor_system/certstor_system.cpp70
-rw-r--r--comm/third_party/botan/src/lib/x509/certstor_system/certstor_system.h42
-rw-r--r--comm/third_party/botan/src/lib/x509/certstor_system/info.txt8
-rw-r--r--comm/third_party/botan/src/lib/x509/certstor_system_macos/certstor_macos.cpp470
-rw-r--r--comm/third_party/botan/src/lib/x509/certstor_system_macos/certstor_macos.h81
-rw-r--r--comm/third_party/botan/src/lib/x509/certstor_system_macos/info.txt15
-rw-r--r--comm/third_party/botan/src/lib/x509/certstor_system_windows/certstor_windows.cpp258
-rw-r--r--comm/third_party/botan/src/lib/x509/certstor_system_windows/certstor_windows.h70
-rw-r--r--comm/third_party/botan/src/lib/x509/certstor_system_windows/info.txt16
-rw-r--r--comm/third_party/botan/src/lib/x509/crl_ent.cpp140
-rw-r--r--comm/third_party/botan/src/lib/x509/crl_ent.h11
-rw-r--r--comm/third_party/botan/src/lib/x509/datastor.cpp205
-rw-r--r--comm/third_party/botan/src/lib/x509/datastor.h85
-rw-r--r--comm/third_party/botan/src/lib/x509/info.txt12
-rw-r--r--comm/third_party/botan/src/lib/x509/key_constraint.cpp106
-rw-r--r--comm/third_party/botan/src/lib/x509/key_constraint.h11
-rw-r--r--comm/third_party/botan/src/lib/x509/name_constraint.cpp273
-rw-r--r--comm/third_party/botan/src/lib/x509/name_constraint.h11
-rw-r--r--comm/third_party/botan/src/lib/x509/ocsp.cpp363
-rw-r--r--comm/third_party/botan/src/lib/x509/ocsp.h282
-rw-r--r--comm/third_party/botan/src/lib/x509/ocsp_types.cpp105
-rw-r--r--comm/third_party/botan/src/lib/x509/ocsp_types.h11
-rw-r--r--comm/third_party/botan/src/lib/x509/pkcs10.cpp304
-rw-r--r--comm/third_party/botan/src/lib/x509/pkcs10.h148
-rw-r--r--comm/third_party/botan/src/lib/x509/pkix_enums.h143
-rw-r--r--comm/third_party/botan/src/lib/x509/pkix_types.h613
-rw-r--r--comm/third_party/botan/src/lib/x509/x509_attribute.cpp58
-rw-r--r--comm/third_party/botan/src/lib/x509/x509_ca.cpp338
-rw-r--r--comm/third_party/botan/src/lib/x509/x509_ca.h261
-rw-r--r--comm/third_party/botan/src/lib/x509/x509_crl.cpp268
-rw-r--r--comm/third_party/botan/src/lib/x509/x509_crl.h209
-rw-r--r--comm/third_party/botan/src/lib/x509/x509_dn.cpp428
-rw-r--r--comm/third_party/botan/src/lib/x509/x509_dn.h11
-rw-r--r--comm/third_party/botan/src/lib/x509/x509_dn_ub.cpp60
-rw-r--r--comm/third_party/botan/src/lib/x509/x509_ext.cpp1023
-rw-r--r--comm/third_party/botan/src/lib/x509/x509_ext.h529
-rw-r--r--comm/third_party/botan/src/lib/x509/x509_obj.cpp424
-rw-r--r--comm/third_party/botan/src/lib/x509/x509_obj.h144
-rw-r--r--comm/third_party/botan/src/lib/x509/x509cert.cpp956
-rw-r--r--comm/third_party/botan/src/lib/x509/x509cert.h461
-rw-r--r--comm/third_party/botan/src/lib/x509/x509opt.cpp100
-rw-r--r--comm/third_party/botan/src/lib/x509/x509path.cpp1088
-rw-r--r--comm/third_party/botan/src/lib/x509/x509path.h475
-rw-r--r--comm/third_party/botan/src/lib/x509/x509self.cpp152
-rw-r--r--comm/third_party/botan/src/lib/x509/x509self.h222
-rwxr-xr-xcomm/third_party/botan/src/python/botan2.py1787
-rw-r--r--comm/third_party/botan/src/scripts/Dockerfile.android17
-rwxr-xr-xcomm/third_party/botan/src/scripts/bench.py216
-rwxr-xr-xcomm/third_party/botan/src/scripts/build_docs.py242
-rw-r--r--comm/third_party/botan/src/scripts/check.py89
-rw-r--r--comm/third_party/botan/src/scripts/ci/appveyor.yml90
-rw-r--r--comm/third_party/botan/src/scripts/ci/codecov.yml15
-rw-r--r--comm/third_party/botan/src/scripts/ci/lgtm.yml31
-rw-r--r--comm/third_party/botan/src/scripts/ci/setup_appveyor.bat19
-rwxr-xr-xcomm/third_party/botan/src/scripts/ci/setup_gh_actions.sh69
-rwxr-xr-xcomm/third_party/botan/src/scripts/ci/setup_travis.sh89
-rw-r--r--comm/third_party/botan/src/scripts/ci/travis.yml50
-rwxr-xr-xcomm/third_party/botan/src/scripts/ci_build.py620
-rwxr-xr-xcomm/third_party/botan/src/scripts/ci_check_install.py104
-rwxr-xr-xcomm/third_party/botan/src/scripts/cleanup.py133
-rwxr-xr-xcomm/third_party/botan/src/scripts/comba.py126
-rwxr-xr-xcomm/third_party/botan/src/scripts/create_corpus_zip.py48
-rwxr-xr-xcomm/third_party/botan/src/scripts/dist.py466
-rwxr-xr-xcomm/third_party/botan/src/scripts/docker-android.sh11
-rwxr-xr-xcomm/third_party/botan/src/scripts/ffi_decls.py113
-rw-r--r--comm/third_party/botan/src/scripts/fuzzer.xml17
-rwxr-xr-xcomm/third_party/botan/src/scripts/gen_os_features.py95
-rwxr-xr-xcomm/third_party/botan/src/scripts/install.py261
-rwxr-xr-xcomm/third_party/botan/src/scripts/macro_checks.py42
-rwxr-xr-xcomm/third_party/botan/src/scripts/monty.py98
-rwxr-xr-xcomm/third_party/botan/src/scripts/oids.py337
-rwxr-xr-xcomm/third_party/botan/src/scripts/python_unittests.py224
-rwxr-xr-xcomm/third_party/botan/src/scripts/python_unittests_unix.py67
-rwxr-xr-xcomm/third_party/botan/src/scripts/run_tls_attacker.py138
-rwxr-xr-xcomm/third_party/botan/src/scripts/run_tls_fuzzer.py98
-rwxr-xr-xcomm/third_party/botan/src/scripts/show_dependencies.py213
-rwxr-xr-xcomm/third_party/botan/src/scripts/test_all_configs.py136
-rwxr-xr-xcomm/third_party/botan/src/scripts/test_cli.py1429
-rwxr-xr-xcomm/third_party/botan/src/scripts/test_cli_crypt.py220
-rwxr-xr-xcomm/third_party/botan/src/scripts/test_fuzzers.py187
-rw-r--r--comm/third_party/botan/src/scripts/test_python.py695
-rw-r--r--comm/third_party/botan/src/scripts/tls_scanner/boa.txt1
-rw-r--r--comm/third_party/botan/src/scripts/tls_scanner/policy.txt19
-rw-r--r--comm/third_party/botan/src/scripts/tls_scanner/readme.txt5
-rwxr-xr-xcomm/third_party/botan/src/scripts/tls_scanner/tls_scanner.py60
-rw-r--r--comm/third_party/botan/src/scripts/tls_scanner/urls.txt58
-rwxr-xr-xcomm/third_party/botan/src/scripts/tls_suite_info.py342
-rwxr-xr-xcomm/third_party/botan/src/scripts/website.py166
1316 files changed, 226310 insertions, 0 deletions
diff --git a/comm/third_party/botan/Makefile.in b/comm/third_party/botan/Makefile.in
new file mode 100644
index 0000000000..2aaf56941f
--- /dev/null
+++ b/comm/third_party/botan/Makefile.in
@@ -0,0 +1,12 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+include $(topsrcdir)/config/config.mk
+include $(topsrcdir)/config/rules.mk
+
+ifdef COMPILE_ENVIRONMENT
+ifndef MZLA_SYSTEM_BOTAN
+export:: build/build.h
+endif
+endif
diff --git a/comm/third_party/botan/botan.mozbuild b/comm/third_party/botan/botan.mozbuild
new file mode 100644
index 0000000000..50ca96d6f7
--- /dev/null
+++ b/comm/third_party/botan/botan.mozbuild
@@ -0,0 +1,238 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+include('../rnpdefs.mozbuild')
+
+DEFINES['BOTAN_IS_BEING_BUILT'] = 1
+DEFINES['_REENTRANT'] = 1
+
+if CONFIG['CC_TYPE'] == 'clang-cl':
+ DEFINES['_ENABLE_EXTENDED_ALIGNED_STORAGE'] = 1
+
+ CXXFLAGS += [
+ '-bigobj',
+ '-clang:-fno-force-enable-int128',
+ '/EHs',
+ ]
+else:
+ CXXFLAGS += [
+ '-fexceptions',
+ '-fstack-protector'
+ ]
+
+if CONFIG['OS_ARCH'] == 'WINNT':
+ botan_os = 'windows'
+elif CONFIG['OS_ARCH'] == 'Linux':
+ botan_os = 'linux'
+ CXXFLAGS += ['-pthread']
+else:
+ botan_os = CONFIG['OS_ARCH'].lower()
+
+# Run Botan's configure.py to generate build.h. Use --with-cmake to avoid
+# writing a Makefile that would overwrite our own.
+botan_generated = [
+ 'build/build.h',
+ 'build/build_config.json',
+ 'CMakeLists.txt',
+ ]
+
+if CONFIG['COMPILE_ENVIRONMENT']:
+ GENERATED_FILES += botan_generated
+ botan_build = GENERATED_FILES['build/build.h']
+ botan_build.script = 'botan_configure.py'
+ botan_build.flags = [
+ '--cc-bin={}'.format(CONFIG['CXX']),
+ '--cpu={}'.format(CONFIG['target_cpu']),
+ '--os={}'.format(botan_os),
+ '--with-build-dir={}'.format(OBJDIR),
+ '--minimized-build',
+ '--disable-shared-library',
+ '--link-method=copy',
+ '--without-documentation',
+ '--distribution-info={}'.format(rnp_dist_info),
+ '--with-cmake',
+ ]
+
+
+LOCAL_INCLUDES = ['!build/include']
+
+# This list was obtained by running Botan's configure script in CMake mode
+# with the desired options and extracting the information from CMakeLists.txt.
+SOURCES += [
+ 'src/lib/asn1/alg_id.cpp',
+ 'src/lib/asn1/asn1_obj.cpp',
+ 'src/lib/asn1/asn1_oid.cpp',
+ 'src/lib/asn1/asn1_print.cpp',
+ 'src/lib/asn1/asn1_str.cpp',
+ 'src/lib/asn1/asn1_time.cpp',
+ 'src/lib/asn1/ber_dec.cpp',
+ 'src/lib/asn1/der_enc.cpp',
+ 'src/lib/asn1/oid_maps.cpp',
+ 'src/lib/asn1/oids.cpp',
+ 'src/lib/base/buf_comp.cpp',
+ 'src/lib/base/scan_name.cpp',
+ 'src/lib/base/sym_algo.cpp',
+ 'src/lib/base/symkey.cpp',
+ 'src/lib/block/aes/aes.cpp',
+ 'src/lib/block/block_cipher.cpp',
+ 'src/lib/block/blowfish/blowfish.cpp',
+ 'src/lib/block/camellia/camellia.cpp',
+ 'src/lib/block/cast128/cast128.cpp',
+ 'src/lib/block/des/des.cpp',
+ 'src/lib/block/des/des_tab.cpp',
+ 'src/lib/block/des/desx.cpp',
+ 'src/lib/block/idea/idea.cpp',
+ 'src/lib/block/sm4/sm4.cpp',
+ 'src/lib/block/twofish/twofish.cpp',
+ 'src/lib/block/twofish/twofish_tab.cpp',
+ 'src/lib/codec/base64/base64.cpp',
+ 'src/lib/codec/hex/hex.cpp',
+ 'src/lib/entropy/entropy_srcs.cpp',
+ 'src/lib/ffi/ffi.cpp',
+ 'src/lib/ffi/ffi_block.cpp',
+ 'src/lib/ffi/ffi_cert.cpp',
+ 'src/lib/ffi/ffi_cipher.cpp',
+ 'src/lib/ffi/ffi_fpe.cpp',
+ 'src/lib/ffi/ffi_hash.cpp',
+ 'src/lib/ffi/ffi_hotp.cpp',
+ 'src/lib/ffi/ffi_kdf.cpp',
+ 'src/lib/ffi/ffi_keywrap.cpp',
+ 'src/lib/ffi/ffi_mac.cpp',
+ 'src/lib/ffi/ffi_mp.cpp',
+ 'src/lib/ffi/ffi_pk_op.cpp',
+ 'src/lib/ffi/ffi_pkey.cpp',
+ 'src/lib/ffi/ffi_pkey_algs.cpp',
+ 'src/lib/ffi/ffi_rng.cpp',
+ 'src/lib/ffi/ffi_totp.cpp',
+ 'src/lib/hash/checksum/crc24/crc24.cpp',
+ 'src/lib/hash/hash.cpp',
+ 'src/lib/hash/md5/md5.cpp',
+ 'src/lib/hash/mdx_hash/mdx_hash.cpp',
+ 'src/lib/hash/rmd160/rmd160.cpp',
+ 'src/lib/hash/sha1/sha160.cpp',
+ 'src/lib/hash/sha2_32/sha2_32.cpp',
+ 'src/lib/hash/sha2_64/sha2_64.cpp',
+ 'src/lib/hash/sha3/sha3.cpp',
+ 'src/lib/hash/sm3/sm3.cpp',
+ 'src/lib/kdf/kdf.cpp',
+ 'src/lib/kdf/kdf2/kdf2.cpp',
+ 'src/lib/kdf/sp800_56a/sp800_56a.cpp',
+ 'src/lib/mac/cmac/cmac.cpp',
+ 'src/lib/mac/hmac/hmac.cpp',
+ 'src/lib/mac/mac.cpp',
+ 'src/lib/math/bigint/big_code.cpp',
+ 'src/lib/math/bigint/big_io.cpp',
+ 'src/lib/math/bigint/big_ops2.cpp',
+ 'src/lib/math/bigint/big_ops3.cpp',
+ 'src/lib/math/bigint/big_rand.cpp',
+ 'src/lib/math/bigint/bigint.cpp',
+ 'src/lib/math/bigint/divide.cpp',
+ 'src/lib/math/mp/mp_comba.cpp',
+ 'src/lib/math/mp/mp_karat.cpp',
+ 'src/lib/math/mp/mp_monty.cpp',
+ 'src/lib/math/mp/mp_monty_n.cpp',
+ 'src/lib/math/numbertheory/dsa_gen.cpp',
+ 'src/lib/math/numbertheory/jacobi.cpp',
+ 'src/lib/math/numbertheory/make_prm.cpp',
+ 'src/lib/math/numbertheory/mod_inv.cpp',
+ 'src/lib/math/numbertheory/monty.cpp',
+ 'src/lib/math/numbertheory/monty_exp.cpp',
+ 'src/lib/math/numbertheory/mp_numth.cpp',
+ 'src/lib/math/numbertheory/nistp_redc.cpp',
+ 'src/lib/math/numbertheory/numthry.cpp',
+ 'src/lib/math/numbertheory/pow_mod.cpp',
+ 'src/lib/math/numbertheory/primality.cpp',
+ 'src/lib/math/numbertheory/primes.cpp',
+ 'src/lib/math/numbertheory/reducer.cpp',
+ 'src/lib/math/numbertheory/ressol.cpp',
+ 'src/lib/misc/nist_keywrap/nist_keywrap.cpp',
+ 'src/lib/misc/rfc3394/rfc3394.cpp',
+ 'src/lib/modes/aead/aead.cpp',
+ 'src/lib/modes/aead/eax/eax.cpp',
+ 'src/lib/modes/aead/ocb/ocb.cpp',
+ 'src/lib/modes/cbc/cbc.cpp',
+ 'src/lib/modes/cfb/cfb.cpp',
+ 'src/lib/modes/cipher_mode.cpp',
+ 'src/lib/modes/mode_pad/mode_pad.cpp',
+ 'src/lib/pbkdf/pbkdf.cpp',
+ 'src/lib/pbkdf/pgp_s2k/pgp_s2k.cpp',
+ 'src/lib/pbkdf/pwdhash.cpp',
+ 'src/lib/pk_pad/eme.cpp',
+ 'src/lib/pk_pad/eme_pkcs1/eme_pkcs.cpp',
+ 'src/lib/pk_pad/emsa.cpp',
+ 'src/lib/pk_pad/emsa1/emsa1.cpp',
+ 'src/lib/pk_pad/emsa_pkcs1/emsa_pkcs1.cpp',
+ 'src/lib/pk_pad/emsa_pssr/pssr.cpp',
+ 'src/lib/pk_pad/emsa_raw/emsa_raw.cpp',
+ 'src/lib/pk_pad/hash_id/hash_id.cpp',
+ 'src/lib/pk_pad/mgf1/mgf1.cpp',
+ 'src/lib/pk_pad/padding.cpp',
+ 'src/lib/pubkey/blinding.cpp',
+ 'src/lib/pubkey/curve25519/curve25519.cpp',
+ 'src/lib/pubkey/curve25519/donna.cpp',
+ 'src/lib/pubkey/dl_algo/dl_algo.cpp',
+ 'src/lib/pubkey/dl_group/dl_group.cpp',
+ 'src/lib/pubkey/dl_group/dl_named.cpp',
+ 'src/lib/pubkey/dsa/dsa.cpp',
+ 'src/lib/pubkey/ec_group/curve_gfp.cpp',
+ 'src/lib/pubkey/ec_group/ec_group.cpp',
+ 'src/lib/pubkey/ec_group/ec_named.cpp',
+ 'src/lib/pubkey/ec_group/point_gfp.cpp',
+ 'src/lib/pubkey/ec_group/point_mul.cpp',
+ 'src/lib/pubkey/ecc_key/ecc_key.cpp',
+ 'src/lib/pubkey/ecdh/ecdh.cpp',
+ 'src/lib/pubkey/ecdsa/ecdsa.cpp',
+ 'src/lib/pubkey/ed25519/ed25519.cpp',
+ 'src/lib/pubkey/ed25519/ed25519_fe.cpp',
+ 'src/lib/pubkey/ed25519/ed25519_key.cpp',
+ 'src/lib/pubkey/ed25519/ge.cpp',
+ 'src/lib/pubkey/ed25519/sc_muladd.cpp',
+ 'src/lib/pubkey/ed25519/sc_reduce.cpp',
+ 'src/lib/pubkey/elgamal/elgamal.cpp',
+ 'src/lib/pubkey/keypair/keypair.cpp',
+ 'src/lib/pubkey/pem/pem.cpp',
+ 'src/lib/pubkey/pk_algs.cpp',
+ 'src/lib/pubkey/pk_keys.cpp',
+ 'src/lib/pubkey/pk_ops.cpp',
+ 'src/lib/pubkey/pkcs8.cpp',
+ 'src/lib/pubkey/pubkey.cpp',
+ 'src/lib/pubkey/rsa/rsa.cpp',
+ 'src/lib/pubkey/sm2/sm2.cpp',
+ 'src/lib/pubkey/sm2/sm2_enc.cpp',
+ 'src/lib/pubkey/workfactor.cpp',
+ 'src/lib/pubkey/x509_key.cpp',
+ 'src/lib/rng/auto_rng/auto_rng.cpp',
+ 'src/lib/rng/hmac_drbg/hmac_drbg.cpp',
+ 'src/lib/rng/rng.cpp',
+ 'src/lib/rng/stateful_rng/stateful_rng.cpp',
+ 'src/lib/rng/system_rng/system_rng.cpp',
+ 'src/lib/stream/ctr/ctr.cpp',
+ 'src/lib/stream/stream_cipher.cpp',
+ 'src/lib/utils/assert.cpp',
+ 'src/lib/utils/calendar.cpp',
+ 'src/lib/utils/charset.cpp',
+ 'src/lib/utils/cpuid/cpuid.cpp',
+ 'src/lib/utils/cpuid/cpuid_arm.cpp',
+ 'src/lib/utils/cpuid/cpuid_ppc.cpp',
+ 'src/lib/utils/cpuid/cpuid_x86.cpp',
+ 'src/lib/utils/ct_utils.cpp',
+ 'src/lib/utils/data_src.cpp',
+ 'src/lib/utils/exceptn.cpp',
+ 'src/lib/utils/filesystem.cpp',
+ 'src/lib/utils/mem_ops.cpp',
+ 'src/lib/utils/os_utils.cpp',
+ 'src/lib/utils/parsing.cpp',
+ 'src/lib/utils/poly_dbl/poly_dbl.cpp',
+ 'src/lib/utils/read_cfg.cpp',
+ 'src/lib/utils/read_kv.cpp',
+ 'src/lib/utils/timer.cpp',
+ 'src/lib/utils/version.cpp',
+]
+
+if CONFIG['CC_TYPE'] == 'clang-cl':
+ SOURCES += [
+ 'src/lib/utils/dyn_load/dyn_load.cpp',
+ ]
diff --git a/comm/third_party/botan/botan_configure.py b/comm/third_party/botan/botan_configure.py
new file mode 100755
index 0000000000..858c1e63d3
--- /dev/null
+++ b/comm/third_party/botan/botan_configure.py
@@ -0,0 +1,121 @@
+#!python3
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+from __future__ import print_function, absolute_import, unicode_literals
+import os
+import sys
+import subprocess
+from mozbuild.util import system_encoding
+
+# This script is a wrapper for Botan's configure.py to adapt it for moz.build.
+# Its main purpose is to return some output on stdout for mozbuild to handle,
+# but secondary to that is to set --enable-modules. Mozbuild/Make mangle
+# the list otherwise due to the embedded commas.
+
+botan_modules = ",".join(
+ (
+ "aead",
+ "aes",
+ "auto_rng",
+ "bigint",
+ "blowfish",
+ "camellia",
+ "cast128",
+ "cbc",
+ "cfb",
+ "crc24",
+ "curve25519",
+ "des",
+ "dl_group",
+ "dsa",
+ "eax",
+ "ec_group",
+ "ecdh",
+ "ecdsa",
+ "ed25519",
+ "elgamal",
+ "eme_pkcs1",
+ "emsa_pkcs1",
+ "emsa_raw",
+ "ffi",
+ "hash",
+ "hmac",
+ "hmac_drbg",
+ "idea",
+ "kdf",
+ "md5",
+ "ocb",
+ "pgp_s2k",
+ "pubkey",
+ "rfc3394",
+ "rmd160",
+ "rsa",
+ "sha1",
+ "sha2_32",
+ "sha2_64",
+ "sha3",
+ "sm2",
+ "sm3",
+ "sm4",
+ "sp800_56a",
+ "system_rng",
+ "twofish",
+ )
+)
+
+##
+here = os.path.abspath(os.path.dirname(__file__))
+configure = os.path.join(here, "configure.py")
+
+
+# A wrapper to obtain a process' output and return code.
+# Returns a tuple (retcode, stdout, stderr).
+# from build/moz.configure/util.configure
+def get_cmd_output(*args, **kwargs):
+ proc = subprocess.Popen(
+ args,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ close_fds=os.name != "nt",
+ encoding=system_encoding,
+ errors="replace",
+ )
+ stdout, stderr = proc.communicate()
+ return proc.wait(), stdout, stderr
+
+
+def _run_configure(argv):
+ """Call Botan's configure.py. Arguments are passed "shell-style"."""
+ args = [sys.executable] + [configure] + list(argv) # passed as a tuple
+ botan_modules_arg = "--enable-modules={}".format(botan_modules)
+ args.append(botan_modules_arg)
+
+ try:
+ rv = get_cmd_output(*args)
+ except Exception:
+ raise
+
+ return rv
+
+
+def main(output, *args):
+ rv = _run_configure(args)
+ if rv[0] == 0:
+ # GENERATED_FILES expects this script to write something back to output
+ if os.path.isfile(output.name):
+ with open(output.name, "r") as fp:
+ data = fp.read()
+ output.write(data)
+ else:
+ # Probably an error
+ raise Exception("Unable to locate real output at {}".format(output.name))
+ else:
+ return rv
+
+ return rv[0]
+
+
+if __name__ == "__main__":
+ main(*sys.argv)
diff --git a/comm/third_party/botan/configure.py b/comm/third_party/botan/configure.py
new file mode 100755
index 0000000000..dd1928aa97
--- /dev/null
+++ b/comm/third_party/botan/configure.py
@@ -0,0 +1,3454 @@
+#!/usr/bin/env python
+
+"""
+Configuration program for botan
+
+(C) 2009-2020 Jack Lloyd
+(C) 2015,2016,2017 Simon Warta (Kullo GmbH)
+
+Botan is released under the Simplified BSD License (see license.txt)
+
+This script is regularly tested with CPython 2.7 and 3.5, and
+occasionally tested with CPython 2.6 and PyPy 4.
+
+Support for CPython 2.6 will be dropped eventually, but is kept up for as
+long as reasonably convenient.
+
+CPython 2.5 and earlier are not supported.
+
+On Jython target detection does not work (use --os and --cpu).
+"""
+
+import collections
+import copy
+import json
+import sys
+import os
+import os.path
+import platform
+import re
+import shlex
+import shutil
+import subprocess
+import traceback
+import logging
+import time
+import errno
+import optparse # pylint: disable=deprecated-module
+
+# An error caused by and to be fixed by the user, e.g. invalid command line argument
+class UserError(Exception):
+ pass
+
+
+# An error caused by bugs in this script or when reading/parsing build data files
+# Those are not expected to be fixed by the user of this script
+class InternalError(Exception):
+ pass
+
+
+def flatten(l):
+ return sum(l, [])
+
+def normalize_source_path(source):
+ """
+ cmake needs this, and nothing else minds
+ """
+ return os.path.normpath(source).replace('\\', '/')
+
+def parse_version_file(version_path):
+ version_file = open(version_path)
+ key_and_val = re.compile(r"([a-z_]+) = ([a-zA-Z0-9:\-\']+)")
+
+ results = {}
+ for line in version_file.readlines():
+ if not line or line[0] == '#':
+ continue
+ match = key_and_val.match(line)
+ if match:
+ key = match.group(1)
+ val = match.group(2)
+
+ if val == 'None':
+ val = None
+ elif val.startswith("'") and val.endswith("'"):
+ val = val[1:len(val)-1]
+ else:
+ val = int(val)
+
+ results[key] = val
+ return results
+
+class Version(object):
+ """
+ Version information are all static members
+ """
+ data = {}
+
+ @staticmethod
+ def get_data():
+ if not Version.data:
+ root_dir = os.path.dirname(os.path.realpath(__file__))
+ Version.data = parse_version_file(os.path.join(root_dir, 'src/build-data/version.txt'))
+
+ suffix = Version.data["release_suffix"]
+ if suffix != "":
+ suffix_re = re.compile('-(alpha|beta|rc)[0-9]+')
+
+ if not suffix_re.match(suffix):
+ raise Exception("Unexpected version suffix '%s'" % (suffix))
+ return Version.data
+
+ @staticmethod
+ def major():
+ return Version.get_data()["release_major"]
+
+ @staticmethod
+ def minor():
+ return Version.get_data()["release_minor"]
+
+ @staticmethod
+ def patch():
+ return Version.get_data()["release_patch"]
+
+ @staticmethod
+ def suffix():
+ return Version.get_data()["release_suffix"]
+
+ @staticmethod
+ def packed():
+ # Used on macOS for dylib versioning
+ return Version.major() * 1000 + Version.minor()
+
+ @staticmethod
+ def so_rev():
+ return Version.get_data()["release_so_abi_rev"]
+
+ @staticmethod
+ def release_type():
+ return Version.get_data()["release_type"]
+
+ @staticmethod
+ def datestamp():
+ return Version.get_data()["release_datestamp"]
+
+ @staticmethod
+ def as_string():
+ return '%d.%d.%d%s' % (Version.major(), Version.minor(), Version.patch(), Version.suffix())
+
+ @staticmethod
+ def vc_rev():
+ # Lazy load to ensure _local_repo_vc_revision() does not run before logger is set up
+ if Version.get_data()["release_vc_rev"] is None:
+ Version.data["release_vc_rev"] = Version._local_repo_vc_revision()
+ return Version.get_data()["release_vc_rev"]
+
+ @staticmethod
+ def _local_repo_vc_revision():
+ vc_command = ['git', 'rev-parse', 'HEAD']
+ cmdname = vc_command[0]
+
+ try:
+ vc = subprocess.Popen(
+ vc_command,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ universal_newlines=True)
+ (stdout, stderr) = vc.communicate()
+
+ if vc.returncode != 0:
+ logging.debug('Error getting rev from %s - %d (%s)',
+ cmdname, vc.returncode, stderr)
+ return 'unknown'
+
+ rev = str(stdout).strip()
+ logging.debug('%s reported revision %s', cmdname, rev)
+
+ return '%s:%s' % (cmdname, rev)
+ except OSError as e:
+ logging.debug('Error getting rev from %s - %s' % (cmdname, e.strerror))
+ return 'unknown'
+
+
+
+class SourcePaths(object):
+ """
+ A collection of paths defined by the project structure and
+ independent of user configurations.
+ All paths are relative to the base_dir, which may be relative as well (e.g. ".")
+ """
+
+ def __init__(self, base_dir):
+ self.base_dir = base_dir
+ self.doc_dir = os.path.join(self.base_dir, 'doc')
+ self.src_dir = os.path.join(self.base_dir, 'src')
+
+ # dirs in src/
+ self.build_data_dir = os.path.join(self.src_dir, 'build-data')
+ self.configs_dir = os.path.join(self.src_dir, 'configs')
+ self.lib_dir = os.path.join(self.src_dir, 'lib')
+ self.python_dir = os.path.join(self.src_dir, 'python')
+ self.scripts_dir = os.path.join(self.src_dir, 'scripts')
+
+ # subdirs of src/
+ self.test_data_dir = os.path.join(self.src_dir, 'tests/data')
+ self.sphinx_config_dir = os.path.join(self.configs_dir, 'sphinx')
+
+
+class BuildPaths(object): # pylint: disable=too-many-instance-attributes
+ """
+ Constructor
+ """
+ def __init__(self, source_paths, options, modules):
+ self.build_dir = os.path.join(options.with_build_dir, 'build')
+
+ self.libobj_dir = os.path.join(self.build_dir, 'obj', 'lib')
+ self.cliobj_dir = os.path.join(self.build_dir, 'obj', 'cli')
+ self.testobj_dir = os.path.join(self.build_dir, 'obj', 'test')
+
+ self.doc_output_dir = os.path.join(self.build_dir, 'docs')
+ self.handbook_output_dir = os.path.join(self.doc_output_dir, 'handbook')
+ self.doc_output_dir_doxygen = os.path.join(self.doc_output_dir, 'doxygen') if options.with_doxygen else None
+
+ self.include_dir = os.path.join(self.build_dir, 'include')
+ self.botan_include_dir = os.path.join(self.include_dir, 'botan')
+ self.internal_include_dir = os.path.join(self.botan_include_dir, 'internal')
+ self.external_include_dir = os.path.join(self.include_dir, 'external')
+
+ self.internal_headers = sorted(flatten([m.internal_headers() for m in modules]))
+ self.external_headers = sorted(flatten([m.external_headers() for m in modules]))
+
+ # this is overwritten if amalgamation is used
+ self.lib_sources = [normalize_source_path(s) for s in sorted(flatten([mod.sources() for mod in modules]))]
+
+ self.public_headers = sorted(flatten([m.public_headers() for m in modules]))
+
+ def find_sources_in(basedir, srcdir):
+ for (dirpath, _, filenames) in os.walk(os.path.join(basedir, srcdir)):
+ for filename in filenames:
+ if filename.endswith('.cpp') and not filename.startswith('.'):
+ yield os.path.join(dirpath, filename)
+
+ def find_headers_in(basedir, srcdir):
+ for (dirpath, _, filenames) in os.walk(os.path.join(basedir, srcdir)):
+ for filename in filenames:
+ if filename.endswith('.h') and not filename.startswith('.'):
+ yield os.path.join(dirpath, filename)
+
+ self.cli_sources = [normalize_source_path(s) for s in find_sources_in(source_paths.src_dir, 'cli')]
+ self.cli_headers = [normalize_source_path(s) for s in find_headers_in(source_paths.src_dir, 'cli')]
+ self.test_sources = [normalize_source_path(s) for s in find_sources_in(source_paths.src_dir, 'tests')]
+
+ if options.build_fuzzers:
+ self.fuzzer_sources = list(find_sources_in(source_paths.src_dir, 'fuzzer'))
+ self.fuzzer_output_dir = os.path.join(self.build_dir, 'fuzzer')
+ self.fuzzobj_dir = os.path.join(self.build_dir, 'obj', 'fuzzer')
+ else:
+ self.fuzzer_sources = None
+ self.fuzzer_output_dir = None
+ self.fuzzobj_dir = None
+
+ def build_dirs(self):
+ out = [
+ self.libobj_dir,
+ self.cliobj_dir,
+ self.testobj_dir,
+ self.botan_include_dir,
+ self.internal_include_dir,
+ self.external_include_dir,
+ self.handbook_output_dir,
+ ]
+ if self.doc_output_dir_doxygen:
+ out += [self.doc_output_dir_doxygen]
+ if self.fuzzer_output_dir:
+ out += [self.fuzzobj_dir]
+ out += [self.fuzzer_output_dir]
+ return out
+
+ def format_include_paths(self, cc, external_includes):
+ dash_i = cc.add_include_dir_option
+ output = dash_i + self.include_dir
+ if self.external_headers:
+ output += ' ' + dash_i + self.external_include_dir
+ for external_include in external_includes:
+ output += ' ' + dash_i + external_include
+ return output
+
+ def src_info(self, typ):
+ if typ == 'lib':
+ return (self.lib_sources, self.libobj_dir)
+ elif typ == 'cli':
+ return (self.cli_sources, self.cliobj_dir)
+ elif typ == 'test':
+ return (self.test_sources, self.testobj_dir)
+ elif typ == 'fuzzer':
+ return (self.fuzzer_sources, self.fuzzobj_dir)
+ else:
+ raise InternalError("Unknown src info type '%s'" % (typ))
+
+ACCEPTABLE_BUILD_TARGETS = ["static", "shared", "cli", "tests", "bogo_shim"]
+
+def process_command_line(args): # pylint: disable=too-many-locals,too-many-statements
+ """
+ Handle command line options
+ Do not use logging in this method as command line options need to be
+ available before logging is setup.
+ """
+
+ parser = optparse.OptionParser(
+ formatter=optparse.IndentedHelpFormatter(max_help_position=50),
+ version=Version.as_string())
+
+ parser.add_option('--verbose', action='store_true', default=False,
+ help='Show debug messages')
+ parser.add_option('--quiet', action='store_true', default=False,
+ help='Show only warnings and errors')
+
+ target_group = optparse.OptionGroup(parser, 'Target options')
+
+ target_group.add_option('--cpu', help='set the target CPU architecture')
+
+ target_group.add_option('--os', help='set the target operating system')
+
+ target_group.add_option('--cc', dest='compiler', help='set the desired build compiler')
+
+ target_group.add_option('--cc-min-version', dest='cc_min_version', default=None,
+ metavar='MAJOR.MINOR',
+ help='Set the minimal version of the target compiler. ' \
+ 'Use --cc-min-version=0.0 to support all compiler versions. ' \
+ 'Default is auto detection.')
+
+ target_group.add_option('--cc-bin', dest='compiler_binary', metavar='BINARY',
+ help='set path to compiler binary')
+
+ target_group.add_option('--cc-abi-flags', metavar='FLAGS', default='',
+ help='set compiler ABI flags')
+
+ target_group.add_option('--cxxflags', metavar='FLAGS', default=None,
+ help='override all compiler flags')
+
+ target_group.add_option('--extra-cxxflags', metavar='FLAGS', default=[], action='append',
+ help='set extra compiler flags')
+
+ target_group.add_option('--ldflags', metavar='FLAGS',
+ help='set linker flags', default=None)
+
+ target_group.add_option('--extra-libs', metavar='LIBS',
+ help='specify extra libraries to link against', default='')
+
+ target_group.add_option('--ar-command', dest='ar_command', metavar='AR', default=None,
+ help='set path to static archive creator')
+
+ target_group.add_option('--ar-options', dest='ar_options', metavar='AR_OPTIONS', default=None,
+ help='set options for ar')
+
+ target_group.add_option('--msvc-runtime', metavar='RT', default=None,
+ help='specify MSVC runtime (MT, MD, MTd, MDd)')
+
+ 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('--with-os-features', action='append', metavar='FEAT',
+ help='specify OS features to use')
+ target_group.add_option('--without-os-features', action='append', metavar='FEAT',
+ help='specify OS features to disable')
+
+ isa_extensions = [
+ 'SSE2', 'SSSE3', 'SSE4.1', 'SSE4.2', 'AVX2', 'BMI2', 'RDRAND', 'RDSEED',
+ 'AES-NI', 'SHA-NI',
+ 'AltiVec', 'NEON', 'ARMv8 Crypto', 'POWER Crypto']
+
+ for isa_extn_name in isa_extensions:
+ isa_extn = isa_extn_name.lower().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(' ', ''),
+ dest='disable_intrinsics')
+
+ build_group = optparse.OptionGroup(parser, 'Build options')
+
+ build_group.add_option('--system-cert-bundle', metavar='PATH', default=None,
+ help='set path to trusted CA bundle')
+
+ build_group.add_option('--with-debug-info', action='store_true', default=False, dest='with_debug_info',
+ help='include debug symbols')
+
+ build_group.add_option('--with-sanitizers', action='store_true', default=False, dest='with_sanitizers',
+ help='enable ASan/UBSan checks')
+
+ build_group.add_option('--enable-sanitizers', metavar='SAN', default='',
+ help='enable specific sanitizers')
+
+ build_group.add_option('--with-stack-protector', dest='with_stack_protector',
+ action='store_false', default=None, help=optparse.SUPPRESS_HELP)
+
+ build_group.add_option('--without-stack-protector', dest='with_stack_protector',
+ action='store_false', help='disable stack smashing protections')
+
+ build_group.add_option('--with-coverage', action='store_true', default=False, dest='with_coverage',
+ help='add coverage info and disable opts')
+
+ build_group.add_option('--with-coverage-info', action='store_true', default=False, dest='with_coverage_info',
+ help='add coverage info')
+
+ build_group.add_option('--enable-shared-library', dest='build_shared_lib',
+ action='store_true', default=None,
+ help=optparse.SUPPRESS_HELP)
+ build_group.add_option('--disable-shared-library', dest='build_shared_lib',
+ action='store_false',
+ help='disable building shared library')
+
+ build_group.add_option('--enable-static-library', dest='build_static_lib',
+ action='store_true', default=None,
+ help=optparse.SUPPRESS_HELP)
+ build_group.add_option('--disable-static-library', dest='build_static_lib',
+ action='store_false',
+ help='disable building static library')
+
+ build_group.add_option('--optimize-for-size', dest='optimize_for_size',
+ action='store_true', default=False,
+ help='optimize for code size')
+
+ build_group.add_option('--no-optimizations', dest='no_optimizations',
+ action='store_true', default=False,
+ help='disable all optimizations (for debugging)')
+
+ build_group.add_option('--debug-mode', action='store_true', default=False, dest='debug_mode',
+ help='enable debug info, disable optimizations')
+
+ build_group.add_option('--amalgamation', dest='amalgamation',
+ default=False, action='store_true',
+ help='use amalgamation to build')
+
+ build_group.add_option('--name-amalgamation', metavar='NAME', default='botan_all',
+ help='specify alternate name for amalgamation files')
+
+ build_group.add_option('--with-build-dir', metavar='DIR', default='',
+ help='setup the build in DIR')
+
+ build_group.add_option('--with-external-includedir', metavar='DIR', default=[],
+ help='use DIR for external includes', action='append')
+
+ build_group.add_option('--with-external-libdir', metavar='DIR', default=[],
+ help='use DIR for external libs', action='append')
+
+ build_group.add_option('--define-build-macro', metavar='DEFINE', default=[],
+ help='set compile-time pre-processor definition like KEY[=VALUE]', action='append')
+
+ build_group.add_option('--with-sysroot-dir', metavar='DIR', default='',
+ help='use DIR for system root while cross-compiling')
+
+ build_group.add_option('--with-openmp', default=False, action='store_true',
+ help='enable use of OpenMP')
+
+ link_methods = ['symlink', 'hardlink', 'copy']
+ build_group.add_option('--link-method', default=None, metavar='METHOD',
+ 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')
+
+ build_group.add_option('--maintainer-mode', dest='maintainer_mode',
+ action='store_true', default=False,
+ help=optparse.SUPPRESS_HELP)
+
+ build_group.add_option('--werror-mode', dest='werror_mode',
+ 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)')
+
+ build_group.add_option('--disable-cc-tests', dest='enable_cc_tests',
+ default=True, action='store_false',
+ help=optparse.SUPPRESS_HELP)
+
+ build_group.add_option('--with-valgrind', help='use valgrind API',
+ dest='with_valgrind', action='store_true', default=False)
+
+ # Cmake and bakefile options are hidden as they should not be used by end users
+ build_group.add_option('--with-cmake', action='store_true',
+ default=False, help=optparse.SUPPRESS_HELP)
+
+ build_group.add_option('--with-bakefile', action='store_true',
+ default=False, help=optparse.SUPPRESS_HELP)
+
+ build_group.add_option('--unsafe-fuzzer-mode', action='store_true', default=False,
+ help='Disable essential checks for testing')
+
+ build_group.add_option('--build-fuzzers', dest='build_fuzzers',
+ metavar='TYPE', default=None,
+ help='Build fuzzers (afl, libfuzzer, klee, test)')
+
+ build_group.add_option('--with-fuzzer-lib', metavar='LIB', default=None, dest='fuzzer_lib',
+ help='additionally link in LIB')
+
+ build_group.add_option('--test-mode', action='store_true', default=False,
+ help=optparse.SUPPRESS_HELP)
+
+ build_group.add_option('--with-debug-asserts', action='store_true', default=False,
+ help=optparse.SUPPRESS_HELP)
+
+ build_group.add_option('--build-targets', default=None, dest="build_targets", action='append',
+ help="build specific targets and tools (%s)" % ', '.join(ACCEPTABLE_BUILD_TARGETS))
+
+ build_group.add_option('--with-pkg-config', action='store_true', default=None,
+ help=optparse.SUPPRESS_HELP)
+ build_group.add_option('--without-pkg-config', dest='with_pkg_config', action='store_false',
+ help=optparse.SUPPRESS_HELP)
+ build_group.add_option('--boost-library-name', dest='boost_libnames', default=[],
+ help="file name of some boost library to link", action='append')
+
+ docs_group = optparse.OptionGroup(parser, 'Documentation Options')
+
+ docs_group.add_option('--with-documentation', action='store_true',
+ help=optparse.SUPPRESS_HELP)
+
+ docs_group.add_option('--without-documentation', action='store_false',
+ default=True, dest='with_documentation',
+ help='Skip building/installing documentation')
+
+ docs_group.add_option('--with-sphinx', action='store_true',
+ default=None, help='Use Sphinx')
+
+ docs_group.add_option('--without-sphinx', action='store_false',
+ dest='with_sphinx', help=optparse.SUPPRESS_HELP)
+
+ docs_group.add_option('--with-pdf', action='store_true',
+ default=False, help='Use Sphinx to generate PDF doc')
+
+ docs_group.add_option('--without-pdf', action='store_false',
+ dest='with_pdf', help=optparse.SUPPRESS_HELP)
+
+ docs_group.add_option('--with-rst2man', action='store_true',
+ default=None, help='Use rst2man to generate man page')
+
+ docs_group.add_option('--without-rst2man', action='store_false',
+ dest='with_rst2man', help=optparse.SUPPRESS_HELP)
+
+ docs_group.add_option('--with-doxygen', action='store_true',
+ default=False, help='Use Doxygen')
+
+ docs_group.add_option('--without-doxygen', action='store_false',
+ dest='with_doxygen', help=optparse.SUPPRESS_HELP)
+
+ mods_group = optparse.OptionGroup(parser, 'Module selection')
+
+ mods_group.add_option('--module-policy', dest='module_policy',
+ help="module policy file (see src/build-data/policy)",
+ metavar='POL', default=None)
+
+ mods_group.add_option('--enable-modules', dest='enabled_modules',
+ metavar='MODS', action='append',
+ help='enable specific modules')
+ mods_group.add_option('--disable-modules', dest='disabled_modules',
+ metavar='MODS', action='append',
+ help='disable specific modules')
+ mods_group.add_option('--no-autoload', action='store_true', default=False,
+ help=optparse.SUPPRESS_HELP)
+ mods_group.add_option('--minimized-build', action='store_true', dest='no_autoload',
+ help='minimize build')
+
+ # Should be derived from info.txt but this runs too early
+ third_party = ['boost', 'bzip2', 'lzma', 'openssl', 'commoncrypto', 'sqlite3', 'zlib', 'tpm']
+
+ for mod in third_party:
+ mods_group.add_option('--with-%s' % (mod),
+ help=('use %s' % (mod)) if mod in third_party else optparse.SUPPRESS_HELP,
+ action='append_const',
+ const=mod,
+ dest='enabled_modules')
+
+ mods_group.add_option('--without-%s' % (mod),
+ help=optparse.SUPPRESS_HELP,
+ action='append_const',
+ const=mod,
+ dest='disabled_modules')
+
+ mods_group.add_option('--with-everything', help=optparse.SUPPRESS_HELP,
+ action='store_true', default=False)
+
+ install_group = optparse.OptionGroup(parser, 'Installation options')
+
+ install_group.add_option('--program-suffix', metavar='SUFFIX',
+ help='append string to program names')
+ install_group.add_option('--library-suffix', metavar='SUFFIX', default='',
+ help='append string to library names')
+
+ install_group.add_option('--prefix', metavar='DIR',
+ help='set the install prefix')
+ install_group.add_option('--docdir', metavar='DIR',
+ help='set the doc install dir')
+ install_group.add_option('--bindir', metavar='DIR',
+ help='set the binary install dir')
+ install_group.add_option('--libdir', metavar='DIR',
+ help='set the library install dir')
+ install_group.add_option('--mandir', metavar='DIR',
+ help='set the install dir for man pages')
+ install_group.add_option('--includedir', metavar='DIR',
+ help='set the include file install dir')
+
+ info_group = optparse.OptionGroup(parser, 'Informational')
+
+ info_group.add_option('--list-modules', dest='list_modules',
+ action='store_true',
+ help='list available modules and exit')
+
+ info_group.add_option('--list-os-features', dest='list_os_features',
+ action='store_true',
+ help='list available OS features and exit')
+
+ parser.add_option_group(target_group)
+ parser.add_option_group(build_group)
+ parser.add_option_group(docs_group)
+ parser.add_option_group(mods_group)
+ parser.add_option_group(install_group)
+ parser.add_option_group(info_group)
+
+ # These exist only for autoconf compatibility (requested by zw for mtn)
+ compat_with_autoconf_options = [
+ 'datadir',
+ 'datarootdir',
+ 'dvidir',
+ 'exec-prefix',
+ 'htmldir',
+ 'infodir',
+ 'libexecdir',
+ 'localedir',
+ 'localstatedir',
+ 'oldincludedir',
+ 'pdfdir',
+ 'psdir',
+ 'sbindir',
+ 'sharedstatedir',
+ 'sysconfdir'
+ ]
+
+ for opt in compat_with_autoconf_options:
+ parser.add_option('--' + opt, help=optparse.SUPPRESS_HELP)
+
+ (options, args) = parser.parse_args(args)
+
+ 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
+
+ if options.with_coverage:
+ options.with_coverage_info = True
+ options.no_optimizations = True
+
+ def parse_multiple_enable(modules):
+ if modules is None:
+ return []
+
+ return sorted({m for m in flatten([s.split(',') for s in modules]) if m != ''})
+
+ options.enabled_modules = parse_multiple_enable(options.enabled_modules)
+ options.disabled_modules = parse_multiple_enable(options.disabled_modules)
+
+ 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)
+
+ return options
+
+def take_options_from_env(options):
+ # Take some values from environment, if not set on command line
+
+ def update_from_env(val, var, name):
+ if val is None:
+ val = os.getenv(var)
+ if val is not None:
+ logging.info('Implicit --%s=%s due to environment variable %s', name, val, var)
+
+ return val
+
+ if os.getenv('CXX') and options.compiler_binary is None and options.compiler is not None:
+ logging.info('CXX environment variable is set which will override compiler path')
+
+ options.ar_command = update_from_env(options.ar_command, 'AR', 'ar-command')
+ options.ar_options = update_from_env(options.ar_options, 'AR_OPTIONS', 'ar-options')
+ options.compiler_binary = update_from_env(options.compiler_binary, 'CXX', 'cc-bin')
+ options.cxxflags = update_from_env(options.cxxflags, 'CXXFLAGS', 'cxxflags')
+ options.ldflags = update_from_env(options.ldflags, 'LDFLAGS', 'ldflags')
+
+class LexResult(object):
+ pass
+
+
+class LexerError(InternalError):
+ def __init__(self, msg, lexfile, line):
+ super(LexerError, self).__init__(msg)
+ self.msg = msg
+ self.lexfile = lexfile
+ self.line = line
+
+ def __str__(self):
+ return '%s at %s:%d' % (self.msg, self.lexfile, self.line)
+
+def parse_lex_dict(as_list, map_name, infofile):
+ if len(as_list) % 3 != 0:
+ raise InternalError("Lex dictionary has invalid format (input not divisible by 3): %s" % as_list)
+
+ result = {}
+ for key, sep, value in [as_list[3*i:3*i+3] for i in range(0, len(as_list)//3)]:
+ if sep != '->':
+ raise InternalError("Map %s in %s has invalid format" % (map_name, infofile))
+ if key in result:
+ raise InternalError("Duplicate map entry %s in map %s file %s" % (key, map_name, infofile))
+ result[key] = value
+ return result
+
+def lex_me_harder(infofile, allowed_groups, allowed_maps, name_val_pairs):
+ """
+ Generic lexer function for info.txt and src/build-data files
+ """
+ out = LexResult()
+
+ # Format as a nameable Python variable
+ def py_var(group):
+ return group.replace(':', '_')
+
+ lexer = shlex.shlex(open(infofile), infofile, posix=True)
+ lexer.wordchars += '=:.<>/,-!?+*' # handle various funky chars in info.txt
+
+ groups = allowed_groups + allowed_maps
+ for group in groups:
+ out.__dict__[py_var(group)] = []
+ for (key, val) in name_val_pairs.items():
+ out.__dict__[key] = val
+
+ def lexed_tokens(): # Convert to an iterator
+ while True:
+ token = lexer.get_token()
+ if token != lexer.eof:
+ yield token
+ else:
+ return
+
+ for token in lexed_tokens():
+ match = re.match('<(.*)>', token)
+
+ # Check for a grouping
+ if match is not None:
+ group = match.group(1)
+
+ if group not in groups:
+ raise LexerError('Unknown group "%s"' % (group),
+ infofile, lexer.lineno)
+
+ end_marker = '</' + group + '>'
+
+ token = lexer.get_token()
+ while token != end_marker:
+ out.__dict__[py_var(group)].append(token)
+ token = lexer.get_token()
+ if token is None:
+ raise LexerError('Group "%s" not terminated' % (group),
+ infofile, lexer.lineno)
+
+ elif token in name_val_pairs.keys():
+ if isinstance(out.__dict__[token], list):
+ out.__dict__[token].append(lexer.get_token())
+ else:
+ out.__dict__[token] = lexer.get_token()
+
+ else: # No match -> error
+ raise LexerError('Bad token "%s"' % (token), infofile, lexer.lineno)
+
+ for group in allowed_maps:
+ out.__dict__[group] = parse_lex_dict(out.__dict__[group], group, infofile)
+
+ return out
+
+class InfoObject(object):
+ def __init__(self, infofile):
+ """
+ Constructor sets members `infofile`, `lives_in`, `parent_module` and `basename`
+ """
+
+ self.infofile = infofile
+ (dirname, basename) = os.path.split(infofile)
+ self.lives_in = dirname
+ if basename == 'info.txt':
+ (obj_dir, self.basename) = os.path.split(dirname)
+ if os.access(os.path.join(obj_dir, 'info.txt'), os.R_OK):
+ self.parent_module = os.path.basename(obj_dir)
+ else:
+ self.parent_module = None
+ else:
+ self.basename = basename.replace('.txt', '')
+
+
+class ModuleInfo(InfoObject):
+ """
+ Represents the information about a particular module
+ """
+
+ def __init__(self, infofile):
+ # pylint: disable=too-many-statements
+ super(ModuleInfo, self).__init__(infofile)
+ lex = lex_me_harder(
+ infofile,
+ ['header:internal', 'header:public', 'header:external', 'requires',
+ 'os_features', 'arch', 'isa', 'cc', 'comment', 'warning'],
+ ['defines', 'libs', 'frameworks'],
+ {
+ 'load_on': 'auto',
+ 'endian': 'any',
+ })
+
+ def check_header_duplicates(header_list_public, header_list_internal):
+ pub_header = set(header_list_public)
+ int_header = set(header_list_internal)
+ if not pub_header.isdisjoint(int_header):
+ logging.error("Module %s header contains same header in public and internal sections" % self.infofile)
+
+ check_header_duplicates(lex.header_public, lex.header_internal)
+
+ all_source_files = []
+ all_header_files = []
+
+ for fspath in os.listdir(self.lives_in):
+ if fspath.endswith('.cpp'):
+ all_source_files.append(fspath)
+ elif fspath.endswith('.h'):
+ all_header_files.append(fspath)
+
+ self.source = all_source_files
+
+ # If not entry for the headers, all are assumed public
+ if lex.header_internal == [] and lex.header_public == []:
+ self.header_public = list(all_header_files)
+ self.header_internal = []
+ else:
+ self.header_public = lex.header_public
+ self.header_internal = lex.header_internal
+ self.header_external = lex.header_external
+
+ def convert_lib_list(libs):
+ out = {}
+ for (os_name, lib_list) in libs.items():
+ out[os_name] = lib_list.split(',')
+ return out
+
+ def combine_lines(c):
+ return ' '.join(c) if c else None
+
+ # Convert remaining lex result to members
+ self.arch = lex.arch
+ self.cc = lex.cc
+ self.comment = combine_lines(lex.comment)
+ self._defines = lex.defines
+ self._validate_defines_content(self._defines)
+ self.frameworks = convert_lib_list(lex.frameworks)
+ self.libs = convert_lib_list(lex.libs)
+ self.load_on = lex.load_on
+ self.isa = lex.isa
+ self.os_features = lex.os_features
+ self.requires = lex.requires
+ self.warning = combine_lines(lex.warning)
+ self.endian = lex.endian
+
+ # Modify members
+ self.source = [normalize_source_path(os.path.join(self.lives_in, s)) for s in self.source]
+ self.header_internal = [os.path.join(self.lives_in, s) for s in self.header_internal]
+ self.header_public = [os.path.join(self.lives_in, s) for s in self.header_public]
+ self.header_external = [os.path.join(self.lives_in, s) for s in self.header_external]
+
+ # Filesystem read access check
+ for src in self.source + self.header_internal + self.header_public + self.header_external:
+ if not os.access(src, os.R_OK):
+ logging.error("Missing file %s in %s" % (src, infofile))
+
+ # Check for duplicates
+ def intersect_check(type_a, list_a, type_b, list_b):
+ intersection = set.intersection(set(list_a), set(list_b))
+ if intersection:
+ logging.error('Headers %s marked both %s and %s' % (' '.join(intersection), type_a, type_b))
+
+ intersect_check('public', self.header_public, 'internal', self.header_internal)
+ intersect_check('public', self.header_public, 'external', self.header_external)
+ intersect_check('external', self.header_external, 'internal', self.header_internal)
+
+ @staticmethod
+ def _validate_defines_content(defines):
+ for key, value in defines.items():
+ if not re.match('^[0-9A-Za-z_]{3,30}$', key):
+ raise InternalError('Module defines key has invalid format: "%s"' % key)
+ if not re.match('^20[0-9]{6}$', value):
+ raise InternalError('Module defines value has invalid format: "%s"' % value)
+
+ def cross_check(self, arch_info, cc_info, all_os_features, all_isa_extn):
+
+ for feat in set(flatten([o.split(',') for o in self.os_features])):
+ if feat not in all_os_features:
+ logging.error("Module %s uses an OS feature (%s) which no OS supports", self.infofile, feat)
+
+ for supp_cc in self.cc:
+ if supp_cc not in cc_info:
+ colon_idx = supp_cc.find(':')
+ # a versioned compiler dependency
+ if colon_idx > 0 and supp_cc[0:colon_idx] in cc_info:
+ pass
+ else:
+ raise InternalError('Module %s mentions unknown compiler %s' % (self.infofile, supp_cc))
+
+ for supp_arch in self.arch:
+ if supp_arch not in arch_info:
+ raise InternalError('Module %s mentions unknown arch %s' % (self.infofile, supp_arch))
+
+ def known_isa(isa):
+ if isa in all_isa_extn:
+ return True
+
+ compound_isa = isa.split(':')
+ if len(compound_isa) == 2 and compound_isa[0] in arch_info and compound_isa[1] in all_isa_extn:
+ return True
+ return False
+
+ for isa in self.isa:
+ if not known_isa(isa):
+ raise InternalError('Module %s uses unknown ISA extension %s' % (self.infofile, isa))
+
+ def sources(self):
+ return self.source
+
+ def public_headers(self):
+ return self.header_public
+
+ def internal_headers(self):
+ return self.header_internal
+
+ def external_headers(self):
+ return self.header_external
+
+ def isas_needed(self, arch):
+ isas = []
+
+ for isa in self.isa:
+ if isa.find(':') == -1:
+ isas.append(isa)
+ elif isa.startswith(arch + ':'):
+ isas.append(isa[len(arch)+1:])
+
+ return isas
+
+ def defines(self):
+ return [(key + ' ' + value) for key, value in self._defines.items()]
+
+ def compatible_cpu(self, archinfo, options):
+ arch_name = archinfo.basename
+ cpu_name = options.cpu
+
+ 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(':')
+
+ if arch != arch_name:
+ continue
+
+ if isa in options.disable_intrinsics:
+ return False # explicitly disabled
+
+ if isa not in archinfo.isa_extensions:
+ return False
+
+ if self.arch != []:
+ if arch_name not in self.arch and cpu_name not in self.arch:
+ return False
+
+ return True
+
+ def compatible_os(self, os_data, options):
+ if not self.os_features:
+ return True
+
+ def has_all(needed, provided):
+ for n in needed:
+ if n not in provided:
+ return False
+ return True
+
+ provided_features = os_data.enabled_features(options)
+
+ for feature_set in self.os_features:
+ if has_all(feature_set.split(','), provided_features):
+ return True
+
+ return False
+
+ def compatible_compiler(self, ccinfo, cc_min_version, arch):
+ # Check if this compiler supports the flags we need
+ def supported_isa_flags(ccinfo, arch):
+ for isa in self.isa:
+ if ccinfo.isa_flags_for(isa, arch) is None:
+ return False
+ return True
+
+ # Check if module gives explicit compiler dependencies
+ def supported_compiler(ccinfo, cc_min_version):
+ if self.cc == []:
+ # no compiler restriction
+ return True
+
+ if ccinfo.basename in self.cc:
+ # compiler is supported, independent of version
+ return True
+
+ # Maybe a versioned compiler dep
+ for cc in self.cc:
+ try:
+ name, version = cc.split(":")
+ if name == ccinfo.basename:
+ min_cc_version = [int(v) for v in version.split('.')]
+ cur_cc_version = [int(v) for v in cc_min_version.split('.')]
+ # With lists of ints, this does what we want
+ return cur_cc_version >= min_cc_version
+ except ValueError:
+ # No version part specified
+ pass
+
+ return False # compiler not listed
+
+ return supported_isa_flags(ccinfo, arch) and supported_compiler(ccinfo, cc_min_version)
+
+ def dependencies(self, osinfo):
+ # base is an implicit dep for all submodules
+ deps = ['base']
+ if self.parent_module is not None:
+ deps.append(self.parent_module)
+
+ for req in self.requires:
+ if req.find('?') != -1:
+ (cond, dep) = req.split('?')
+ if osinfo is None or cond in osinfo.target_features:
+ deps.append(dep)
+ else:
+ deps.append(req)
+
+ return deps
+
+ def dependencies_exist(self, modules):
+ """
+ Ensure that all dependencies of this module actually exist, warning
+ about any that do not
+ """
+
+ missing = [s for s in self.dependencies(None) if s not in modules]
+
+ if missing:
+ logging.error("Module '%s', dep of '%s', does not exist" % (
+ missing, self.basename))
+
+
+class ModulePolicyInfo(InfoObject):
+ def __init__(self, infofile):
+ super(ModulePolicyInfo, self).__init__(infofile)
+ lex = lex_me_harder(
+ infofile,
+ ['required', 'if_available', 'prohibited'],
+ [],
+ {})
+
+ self.if_available = lex.if_available
+ self.required = lex.required
+ self.prohibited = lex.prohibited
+
+ def cross_check(self, modules):
+ def check(tp, lst):
+ 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)
+
+
+class ArchInfo(InfoObject):
+ def __init__(self, infofile):
+ super(ArchInfo, self).__init__(infofile)
+ lex = lex_me_harder(
+ infofile,
+ ['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]+$')
+ for isa in self.isa_extensions:
+ if alphanumeric.match(isa) is None:
+ logging.error('Invalid name for ISA extension "%s"', isa)
+
+ def supported_isa_extensions(self, cc, options):
+ isas = []
+
+ for isa in self.isa_extensions:
+ if isa not in options.disable_intrinsics:
+ if cc.isa_flags_for(isa, self.basename) is not None:
+ isas.append(isa)
+
+ return sorted(isas)
+
+
+class CompilerInfo(InfoObject): # pylint: disable=too-many-instance-attributes
+ def __init__(self, infofile):
+ super(CompilerInfo, self).__init__(infofile)
+ lex = lex_me_harder(
+ infofile,
+ [],
+ ['cpu_flags', 'cpu_flags_no_debug', 'so_link_commands', 'binary_link_commands',
+ 'mach_abi_linking', 'isa_flags', 'sanitizers', 'lib_flags'],
+ {
+ 'binary_name': None,
+ 'linker_name': None,
+ 'macro_name': None,
+ 'output_to_object': '-o ',
+ 'output_to_exe': '-o ',
+ 'add_include_dir_option': '-I',
+ 'add_lib_dir_option': '-L',
+ 'add_compile_definition_option': '-D',
+ 'add_sysroot_option': '',
+ 'add_lib_option': '-l%s',
+ 'add_framework_option': '-framework ',
+ 'preproc_flags': '-E',
+ 'compile_flags': '-c',
+ 'debug_info_flags': '-g',
+ 'optimization_flags': '',
+ 'size_optimization_flags': '',
+ 'sanitizer_optimization_flags': '',
+ 'coverage_flags': '',
+ 'stack_protector_flags': '',
+ 'shared_flags': '',
+ 'lang_flags': '',
+ 'warning_flags': '',
+ 'maintainer_warning_flags': '',
+ 'visibility_build_flags': '',
+ 'visibility_attribute': '',
+ 'ar_command': '',
+ 'ar_options': '',
+ 'ar_output_to': '',
+ 'werror_flags': '',
+ })
+
+ self.add_framework_option = lex.add_framework_option
+ self.add_include_dir_option = lex.add_include_dir_option
+ self.add_lib_dir_option = lex.add_lib_dir_option
+ self.add_lib_option = lex.add_lib_option
+ self.add_compile_definition_option = lex.add_compile_definition_option
+ self.add_sysroot_option = lex.add_sysroot_option
+ self.ar_command = lex.ar_command
+ self.ar_options = lex.ar_options
+ self.ar_output_to = lex.ar_output_to
+ self.binary_link_commands = lex.binary_link_commands
+ self.binary_name = lex.binary_name
+ self.cpu_flags = lex.cpu_flags
+ self.cpu_flags_no_debug = lex.cpu_flags_no_debug
+ self.compile_flags = lex.compile_flags
+ self.coverage_flags = lex.coverage_flags
+ self.debug_info_flags = lex.debug_info_flags
+ self.isa_flags = lex.isa_flags
+ self.lang_flags = lex.lang_flags
+ self.lib_flags = lex.lib_flags
+ self.linker_name = lex.linker_name
+ self.mach_abi_linking = lex.mach_abi_linking
+ self.macro_name = lex.macro_name
+ self.maintainer_warning_flags = lex.maintainer_warning_flags
+ self.optimization_flags = lex.optimization_flags
+ self.output_to_exe = lex.output_to_exe
+ self.output_to_object = lex.output_to_object
+ self.preproc_flags = lex.preproc_flags
+ self.sanitizers = lex.sanitizers
+ self.sanitizer_types = []
+ self.sanitizer_optimization_flags = lex.sanitizer_optimization_flags
+ self.shared_flags = lex.shared_flags
+ self.size_optimization_flags = lex.size_optimization_flags
+ self.so_link_commands = lex.so_link_commands
+ self.stack_protector_flags = lex.stack_protector_flags
+ self.visibility_attribute = lex.visibility_attribute
+ self.visibility_build_flags = lex.visibility_build_flags
+ self.warning_flags = lex.warning_flags
+ self.werror_flags = lex.werror_flags
+
+ def cross_check(self, os_info, arch_info, all_isas):
+
+ for isa in self.isa_flags:
+ if ":" in isa:
+ (arch, isa) = isa.split(":")
+ if isa not in all_isas:
+ raise InternalError('Compiler %s has flags for unknown ISA %s' % (self.infofile, isa))
+ if arch not in arch_info:
+ raise InternalError('Compiler %s has flags for unknown arch/ISA %s:%s' % (self.infofile, arch, isa))
+
+ for os_name in self.binary_link_commands:
+ if os_name in ["default", "default-debug"]:
+ continue
+ if os_name not in os_info:
+ raise InternalError("Compiler %s has binary_link_command for unknown OS %s" % (self.infofile, os_name))
+
+ for os_name in self.so_link_commands:
+ if os_name in ["default", "default-debug"]:
+ continue
+ if os_name not in os_info:
+ raise InternalError("Compiler %s has so_link_command for unknown OS %s" % (self.infofile, os_name))
+
+ def isa_flags_for(self, isa, arch):
+ if isa.find(':') > 0:
+ (isa_arch, isa) = isa.split(':')
+ if isa_arch != arch:
+ return ''
+ if isa in self.isa_flags:
+ return self.isa_flags[isa]
+
+ if isa in self.isa_flags:
+ return self.isa_flags[isa]
+ arch_isa = '%s:%s' % (arch, isa)
+ if arch_isa in self.isa_flags:
+ return self.isa_flags[arch_isa]
+
+ 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
+ (vs the cli or tests)
+ """
+
+ def flag_builder():
+ 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:
+ yield process_template_string(self.lib_flags['debug'], variables, self.infofile)
+
+
+ return ' '.join(list(flag_builder()))
+
+ def gen_visibility_attribute(self, options):
+ if options.build_shared_lib:
+ return self.visibility_attribute
+ return ''
+
+ def mach_abi_link_flags(self, options, debug_mode=None):
+ #pylint: disable=too-many-branches
+
+ """
+ Return the machine specific ABI flags
+ """
+
+ if debug_mode is None:
+ debug_mode = options.debug_mode
+
+ def mach_abi_groups():
+
+ yield 'all'
+
+ if options.msvc_runtime is None:
+ if debug_mode:
+ yield 'rt-debug'
+ else:
+ yield 'rt'
+
+ for all_except in [s for s in self.mach_abi_linking.keys() if s.startswith('all!')]:
+ exceptions = all_except[4:].split(',')
+ if options.os not in exceptions and options.arch not in exceptions:
+ yield all_except
+
+ yield options.os
+ yield options.cpu
+
+ abi_link = set()
+ for what in mach_abi_groups():
+ if what in self.mach_abi_linking:
+ flag = self.mach_abi_linking.get(what)
+ if flag is not None and flag != '' and flag not in abi_link:
+ abi_link.add(flag)
+
+ if options.msvc_runtime:
+ abi_link.add("/" + options.msvc_runtime)
+
+ if options.with_stack_protector and self.stack_protector_flags != '':
+ abi_link.add(self.stack_protector_flags)
+
+ if options.with_coverage_info:
+ if self.coverage_flags == '':
+ raise UserError('No coverage handling for %s' % (self.basename))
+ abi_link.add(self.coverage_flags)
+
+ if options.with_sanitizers or options.enable_sanitizers != '':
+ if not self.sanitizers:
+ raise UserError('No sanitizer handling for %s' % (self.basename))
+
+ default_san = self.sanitizers['default'].split(',')
+
+ if options.enable_sanitizers:
+ san = options.enable_sanitizers.split(',')
+ else:
+ san = default_san
+
+ for s in san:
+ if s not in self.sanitizers:
+ raise UserError('No flags defined for sanitizer %s in %s' % (s, self.basename))
+
+ if s == 'default':
+ abi_link.update([self.sanitizers[x] for x in default_san])
+ else:
+ abi_link.add(self.sanitizers[s])
+
+ self.sanitizer_types = san
+
+ if options.with_openmp:
+ if 'openmp' not in self.mach_abi_linking:
+ raise UserError('No support for OpenMP for %s' % (self.basename))
+ abi_link.add(self.mach_abi_linking['openmp'])
+
+ abi_flags = ' '.join(sorted(abi_link))
+
+ if options.cc_abi_flags != '':
+ abi_flags += ' ' + options.cc_abi_flags
+
+ return abi_flags
+
+ def cc_warning_flags(self, options):
+ def gen_flags():
+ yield self.warning_flags
+ if options.werror_mode or options.maintainer_mode:
+ yield self.werror_flags
+ if options.maintainer_mode:
+ yield self.maintainer_warning_flags
+
+ return (' '.join(gen_flags())).strip()
+
+ def cc_lang_flags(self):
+ return self.lang_flags
+
+ def cc_compile_flags(self, options, with_debug_info=None, enable_optimizations=None):
+ #pylint: disable=too-many-branches
+
+ def gen_flags(with_debug_info, enable_optimizations):
+
+ sanitizers_enabled = options.with_sanitizers or (len(options.enable_sanitizers) > 0)
+
+ if with_debug_info is None:
+ with_debug_info = options.with_debug_info
+ if enable_optimizations is None:
+ enable_optimizations = not options.no_optimizations
+
+ if with_debug_info:
+ yield self.debug_info_flags
+
+ if enable_optimizations:
+ if options.optimize_for_size:
+ if self.size_optimization_flags != '':
+ yield self.size_optimization_flags
+ else:
+ logging.warning("No size optimization flags set for current compiler")
+ yield self.optimization_flags
+ elif sanitizers_enabled and self.sanitizer_optimization_flags != '':
+ yield self.sanitizer_optimization_flags
+ else:
+ yield self.optimization_flags
+
+ if options.arch in self.cpu_flags:
+ yield self.cpu_flags[options.arch]
+
+ if options.arch in self.cpu_flags_no_debug:
+
+ # Only enable these if no debug/sanitizer options enabled
+
+ if not (options.debug_mode or sanitizers_enabled):
+ yield self.cpu_flags_no_debug[options.arch]
+
+ for flag in options.extra_cxxflags:
+ yield flag
+
+ for definition in options.define_build_macro:
+ yield self.add_compile_definition_option + definition
+
+ return (' '.join(gen_flags(with_debug_info, enable_optimizations))).strip()
+
+ @staticmethod
+ 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
+ return so_link_typ
+
+ def so_link_command_for(self, osname, options):
+ """
+ Return the command needed to link a shared object
+ """
+
+ for s in self._so_link_search(osname, options.with_debug_info):
+ if s in self.so_link_commands:
+ return self.so_link_commands[s]
+
+ raise InternalError(
+ "No shared library link command found for target '%s' in compiler settings '%s'" %
+ (osname, self.infofile))
+
+ def binary_link_command_for(self, osname, options):
+ """
+ Return the command needed to link an app/test object
+ """
+
+ for s in self._so_link_search(osname, options.with_debug_info):
+ if s in self.binary_link_commands:
+ return self.binary_link_commands[s]
+
+ return '$(LINKER)'
+
+class OsInfo(InfoObject): # pylint: disable=too-many-instance-attributes
+ def __init__(self, infofile):
+ super(OsInfo, self).__init__(infofile)
+ lex = lex_me_harder(
+ infofile,
+ ['aliases', 'target_features', 'feature_macros'],
+ [],
+ {
+ 'program_suffix': '',
+ 'obj_suffix': 'o',
+ 'soname_suffix': '',
+ 'soname_pattern_patch': '',
+ 'soname_pattern_abi': '',
+ 'soname_pattern_base': '',
+ 'static_suffix': 'a',
+ 'ar_command': 'ar',
+ 'ar_options': '',
+ 'ar_output_to': '',
+ 'install_root': '/usr/local',
+ 'header_dir': 'include',
+ 'bin_dir': 'bin',
+ 'lib_dir': 'lib',
+ 'doc_dir': 'share/doc',
+ 'man_dir': 'share/man',
+ 'use_stack_protector': 'true',
+ 'cli_exe_name': 'botan',
+ 'lib_prefix': 'lib',
+ 'library_name': 'botan{suffix}-{major}',
+ 'shared_lib_symlinks': 'yes',
+ 'default_compiler': 'gcc',
+ 'uses_pkg_config': 'yes',
+ })
+
+ if lex.ar_command == 'ar' and lex.ar_options == '':
+ lex.ar_options = 'crs'
+
+ if lex.soname_pattern_base:
+ self.soname_pattern_base = lex.soname_pattern_base
+ if lex.soname_pattern_patch == '' and lex.soname_pattern_abi == '':
+ self.soname_pattern_patch = lex.soname_pattern_base
+ self.soname_pattern_abi = lex.soname_pattern_base
+ elif lex.soname_pattern_patch != '' and lex.soname_pattern_abi != '':
+ self.soname_pattern_patch = lex.soname_pattern_patch
+ self.soname_pattern_abi = lex.soname_pattern_abi
+ else:
+ # base set, only one of patch/abi set
+ raise InternalError("Invalid soname_patterns in %s" % (self.infofile))
+ else:
+ if lex.soname_suffix:
+ self.soname_pattern_base = "libbotan{lib_suffix}-{version_major}.%s" % (lex.soname_suffix)
+ self.soname_pattern_abi = self.soname_pattern_base + ".{abi_rev}"
+ self.soname_pattern_patch = self.soname_pattern_abi + ".{version_minor}.{version_patch}"
+ else:
+ # Could not calculate soname_pattern_*
+ # This happens for OSs without shared library support (e.g. nacl, mingw, includeos, cygwin)
+ self.soname_pattern_base = None
+ self.soname_pattern_abi = None
+ self.soname_pattern_patch = None
+
+ self._aliases = lex.aliases
+ self.ar_command = lex.ar_command
+ self.ar_options = lex.ar_options
+ self.bin_dir = lex.bin_dir
+ self.cli_exe_name = lex.cli_exe_name
+ self.doc_dir = lex.doc_dir
+ self.header_dir = lex.header_dir
+ self.install_root = lex.install_root
+ self.lib_dir = lex.lib_dir
+ self.lib_prefix = lex.lib_prefix
+ self.library_name = lex.library_name
+ self.man_dir = lex.man_dir
+ self.obj_suffix = lex.obj_suffix
+ self.program_suffix = lex.program_suffix
+ self.static_suffix = lex.static_suffix
+ self.target_features = lex.target_features
+ self.use_stack_protector = (lex.use_stack_protector == "true")
+ self.shared_lib_uses_symlinks = (lex.shared_lib_symlinks == 'yes')
+ self.default_compiler = lex.default_compiler
+ self.uses_pkg_config = (lex.uses_pkg_config == 'yes')
+ self.feature_macros = lex.feature_macros
+
+ def matches_name(self, nm):
+ if nm in self._aliases:
+ return True
+
+ for alias in self._aliases:
+ if re.match(alias, nm):
+ return True
+ return False
+
+ def building_shared_supported(self):
+ return self.soname_pattern_base is not None
+
+ def enabled_features(self, options):
+ feats = []
+ for feat in self.target_features:
+ if feat not in options.without_os_features:
+ feats.append(feat)
+ for feat in options.with_os_features:
+ if feat not in self.target_features:
+ feats.append(feat)
+
+ return sorted(feats)
+
+ def macros(self, cc):
+ value = [cc.add_compile_definition_option + define
+ for define in self.feature_macros]
+
+ return ' '.join(value)
+
+def fixup_proc_name(proc):
+ proc = proc.lower().replace(' ', '')
+ for junk in ['(tm)', '(r)']:
+ proc = proc.replace(junk, '')
+ return proc
+
+def canon_processor(archinfo, proc):
+ proc = fixup_proc_name(proc)
+
+ # First, try to search for an exact match
+ for ainfo in archinfo.values():
+ if ainfo.basename == proc or proc in ainfo.aliases:
+ return ainfo.basename
+
+ return None
+
+def system_cpu_info():
+
+ cpu_info = []
+
+ if platform.machine() != '':
+ cpu_info.append(platform.machine())
+
+ if platform.processor() != '':
+ cpu_info.append(platform.processor())
+
+ if 'uname' in os.__dict__:
+ cpu_info.append(os.uname()[4])
+
+ return cpu_info
+
+def guess_processor(archinfo):
+ for info_part in system_cpu_info():
+ if info_part:
+ match = canon_processor(archinfo, info_part)
+ if match is not None:
+ logging.debug("Matched '%s' to processor '%s'" % (info_part, match))
+ return match, info_part
+ else:
+ logging.debug("Failed to deduce CPU from '%s'" % info_part)
+
+ raise UserError('Could not determine target CPU; set with --cpu')
+
+
+def read_textfile(filepath):
+ """
+ Read a whole file into memory as a string
+ """
+ if filepath is None:
+ return ''
+
+ with open(filepath) as f:
+ return ''.join(f.readlines())
+
+
+def process_template_string(template_text, variables, template_source):
+ # pylint: disable=too-many-branches,too-many-statements
+
+ """
+ Perform template substitution
+
+ The template language supports (un-nested) conditionals.
+ """
+ class SimpleTemplate(object):
+
+ def __init__(self, vals):
+ self.vals = vals
+ self.value_pattern = re.compile(r'%{([a-z][a-z_0-9\|]+)}')
+ self.cond_pattern = re.compile('%{(if|unless) ([a-z][a-z_0-9]+)}')
+ self.for_pattern = re.compile('(.*)%{for ([a-z][a-z_0-9]+)}')
+ self.join_pattern = re.compile('(.*)%{join ([a-z][a-z_0-9]+)}')
+
+ def substitute(self, template):
+ # pylint: disable=too-many-locals
+ def insert_value(match):
+ v = match.group(1)
+ if v in self.vals:
+ return str(self.vals.get(v))
+ if v.endswith('|upper'):
+ v = v.replace('|upper', '')
+ if v in self.vals:
+ return str(self.vals.get(v)).upper()
+
+ raise KeyError(v)
+
+ lines = template.splitlines()
+
+ output = ""
+ idx = 0
+
+ while idx < len(lines):
+ cond_match = self.cond_pattern.match(lines[idx])
+ join_match = self.join_pattern.match(lines[idx])
+ for_match = self.for_pattern.match(lines[idx])
+
+ if cond_match:
+ cond_type = cond_match.group(1)
+ cond_var = cond_match.group(2)
+
+ include_cond = False
+
+ if cond_type == 'if' and cond_var in self.vals and self.vals.get(cond_var):
+ include_cond = True
+ elif cond_type == 'unless' and (cond_var not in self.vals or (not self.vals.get(cond_var))):
+ include_cond = True
+
+ idx += 1
+ while idx < len(lines):
+ if lines[idx] == '%{endif}':
+ break
+ if include_cond:
+ output += lines[idx] + "\n"
+ idx += 1
+ elif join_match:
+ join_var = join_match.group(2)
+ join_str = ' '
+ join_line = '%%{join %s}' % (join_var)
+ output += lines[idx].replace(join_line, join_str.join(self.vals[join_var])) + "\n"
+ elif for_match:
+ for_prefix = for_match.group(1)
+ output += for_prefix
+ for_var = for_match.group(2)
+
+ if for_var not in self.vals:
+ raise InternalError("Unknown for loop iteration variable '%s'" % (for_var))
+
+ var = self.vals[for_var]
+ if not isinstance(var, list):
+ raise InternalError("For loop iteration variable '%s' is not a list" % (for_var))
+ idx += 1
+
+ for_body = ""
+ while idx < len(lines):
+ if lines[idx] == '%{endfor}':
+ break
+ for_body += lines[idx] + "\n"
+ idx += 1
+
+ for v in var:
+ if isinstance(v, dict):
+ for_val = for_body
+ for ik, iv in v.items():
+ for_val = for_val.replace('%{' + ik + '}', iv)
+ output += for_val + "\n"
+ else:
+ output += for_body.replace('%{i}', v).replace('%{i|upper}', v.upper())
+ output += "\n"
+ else:
+ output += lines[idx] + "\n"
+ idx += 1
+
+ return self.value_pattern.sub(insert_value, output) + '\n'
+
+ try:
+ return SimpleTemplate(variables).substitute(template_text)
+ except KeyError as e:
+ logging.error('Unbound var %s in template %s' % (e, template_source))
+ except Exception as e: # pylint: disable=broad-except
+ logging.error('Exception %s during template processing file %s' % (e, template_source))
+
+def process_template(template_file, variables):
+ return process_template_string(read_textfile(template_file), variables, template_file)
+
+def yield_objectfile_list(sources, obj_dir, obj_suffix, options):
+ obj_suffix = '.' + obj_suffix
+
+ for src in sources:
+ (directory, filename) = os.path.split(os.path.normpath(src))
+ parts = directory.split(os.sep)
+
+ if 'src' in parts:
+ parts = parts[parts.index('src')+2:]
+ elif options.amalgamation and filename.find(options.name_amalgamation) != -1:
+ parts = []
+ else:
+ raise InternalError("Unexpected file '%s/%s'" % (directory, filename))
+
+ if parts != []:
+ # Handle src/X/X.cpp -> X.o
+ if filename == parts[-1] + '.cpp':
+ name = '_'.join(parts) + '.cpp'
+ else:
+ name = '_'.join(parts) + '_' + filename
+
+ def fixup_obj_name(name):
+ def remove_dups(parts):
+ last = None
+ for part in parts:
+ if last is None or part != last:
+ last = part
+ yield part
+
+ return '_'.join(remove_dups(name.split('_')))
+
+ name = fixup_obj_name(name)
+ else:
+ name = filename
+
+ name = name.replace('.cpp', obj_suffix)
+ yield os.path.join(obj_dir, name)
+
+def generate_build_info(build_paths, modules, cc, arch, osinfo, options):
+ # pylint: disable=too-many-locals
+
+ # first create a map of src_file->owning module
+
+ module_that_owns = {}
+
+ for mod in modules:
+ 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 == 'fuzzer':
+ fuzz_basename = os.path.basename(obj_file).replace('.' + osinfo.obj_suffix, '')
+ info['exe'] = os.path.join(build_paths.fuzzer_output_dir, fuzz_basename)
+
+ output.append(info)
+
+ return output
+
+ out = {}
+
+ targets = ['lib', 'cli', 'test', 'fuzzer']
+
+ out['isa_build_info'] = []
+
+ fuzzer_bin = []
+ for t in targets:
+ src_list, src_dir = build_paths.src_info(t)
+
+ src_key = '%s_srcs' % (t)
+ obj_key = '%s_objs' % (t)
+ build_key = '%s_build_info' % (t)
+
+ objects = []
+ build_info = []
+
+ if src_list is not None:
+ src_list.sort()
+ 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]
+
+ out[src_key] = src_list if src_list else []
+ out[obj_key] = objects
+ out[build_key] = build_info
+
+ out['fuzzer_bin'] = ' '.join(fuzzer_bin)
+ out['cli_headers'] = build_paths.cli_headers
+
+ return out
+
+def create_template_vars(source_paths, build_paths, options, modules, cc, arch, osinfo):
+ #pylint: disable=too-many-locals,too-many-branches,too-many-statements
+
+ """
+ Create the template variables needed to process the makefile, build.h, etc
+ """
+
+ def external_link_cmd():
+ return ' '.join([cc.add_lib_dir_option + libdir for libdir in options.with_external_libdir])
+
+ def adjust_library_name(info_txt_libname):
+ """
+ Apply custom library name mappings where necessary
+ """
+
+ # potentially map boost library names to the associated name provided
+ # via ./configure.py --boost-library-name <build/platform specific name>
+ #
+ # We assume that info.txt contains the library name's "stem", i.e.
+ # 'boost_system'. While the user-provided (actual) library will contain
+ # the same stem plus a set of prefixes and/or suffixes, e.g.
+ # libboost_system-vc140-mt-x64-1_69.lib. We use the stem for selecting
+ # the correct user-provided library name override.
+ if options.boost_libnames and 'boost_' in info_txt_libname:
+ adjusted_libnames = [chosen_libname for chosen_libname in options.boost_libnames \
+ if info_txt_libname in chosen_libname]
+
+ if len(adjusted_libnames) > 1:
+ logging.warning('Ambiguous boost library names: %s' % ', '.join(adjusted_libnames))
+ if len(adjusted_libnames) == 1:
+ logging.debug('Replacing boost library name %s -> %s' % (info_txt_libname, adjusted_libnames[0]))
+ return adjusted_libnames[0]
+
+ return info_txt_libname
+
+ def link_to(module_member_name):
+ """
+ Figure out what external libraries/frameworks are needed based on selected modules
+ """
+ if module_member_name not in ['libs', 'frameworks']:
+ raise InternalError("Invalid argument")
+
+ libs = set()
+ for module in modules:
+ for (osname, module_link_to) in getattr(module, module_member_name).items():
+ if osname in ['all', osinfo.basename]:
+ libs |= set(module_link_to)
+ else:
+ match = re.match('^all!(.*)', osname)
+ if match is not None:
+ exceptions = match.group(1).split(',')
+ if osinfo.basename not in exceptions:
+ libs |= set(module_link_to)
+
+ return sorted([adjust_library_name(lib) for lib in 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 innosetup_arch(os_name, arch):
+ if os_name == 'windows':
+ inno_arch = {'x86_32': '',
+ 'x86_64': 'x64',
+ 'ia64': 'ia64'}
+ if arch in inno_arch:
+ return inno_arch[arch]
+ else:
+ logging.warning('Unknown arch %s in innosetup_arch' % (arch))
+ return None
+
+ 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
+ main_executable = os.path.basename(sys.argv[0])
+ return ' '.join([main_executable] + sys.argv[1:])
+
+ def cmake_escape(s):
+ return s.replace('(', '\\(').replace(')', '\\)')
+
+ def sysroot_option():
+ if options.with_sysroot_dir == '':
+ return ''
+ if cc.add_sysroot_option == '':
+ logging.error("This compiler doesn't support --sysroot option")
+ return cc.add_sysroot_option + options.with_sysroot_dir
+
+ def ar_command():
+ if options.ar_command:
+ return options.ar_command
+
+ if cc.ar_command:
+ if cc.ar_command == cc.binary_name:
+ return options.compiler_binary or cc.binary_name
+ else:
+ return cc.ar_command
+
+ return osinfo.ar_command
+
+ build_dir = options.with_build_dir or os.path.curdir
+ program_suffix = options.program_suffix or osinfo.program_suffix
+
+ def join_with_build_dir(path):
+ # For some unknown reason MinGW doesn't like ./foo
+ if build_dir == os.path.curdir and options.os == 'mingw':
+ return path
+ return os.path.join(build_dir, path)
+
+ def all_targets(options):
+ yield 'libs'
+ if 'cli' in options.build_targets:
+ yield 'cli'
+ if 'tests' in options.build_targets:
+ yield 'tests'
+ if options.build_fuzzers:
+ yield 'fuzzers'
+ if 'bogo_shim' in options.build_targets:
+ yield 'bogo_shim'
+ if options.with_documentation:
+ yield 'docs'
+
+ def install_targets(options):
+ yield 'libs'
+ if 'cli' in options.build_targets:
+ yield 'cli'
+ if options.with_documentation:
+ yield 'docs'
+
+ def absolute_install_dir(p):
+ if os.path.isabs(p):
+ return p
+ return os.path.join(options.prefix or osinfo.install_root, p)
+
+ def choose_python_exe():
+ exe = sys.executable
+
+ if options.os == 'mingw': # mingw doesn't handle the backslashes in the absolute path well
+ return exe.replace('\\', '/')
+
+ return exe
+
+ def choose_cxx_exe():
+ cxx = options.compiler_binary or cc.binary_name
+
+ if options.compiler_cache is None:
+ return cxx
+ else:
+ return '%s %s' % (options.compiler_cache, cxx)
+
+ def extra_libs(libs, cc):
+ if libs is None:
+ return ''
+
+ return ' '.join([(cc.add_lib_option % lib) for lib in libs.split(',') if lib != ''])
+
+ variables = {
+ '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(),
+ 'abi_rev': Version.so_rev(),
+
+ 'version': Version.as_string(),
+ 'release_type': Version.release_type(),
+ 'version_datestamp': Version.datestamp(),
+
+ 'distribution_info': options.distribution_info,
+
+ '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()),
+
+ 'all_targets': ' '.join(all_targets(options)),
+ 'install_targets': ' '.join(install_targets(options)),
+
+ 'public_headers': sorted([os.path.basename(h) for h in build_paths.public_headers]),
+ 'internal_headers': sorted([os.path.basename(h) for h in build_paths.internal_headers]),
+ 'external_headers': sorted([os.path.basename(h) for h in build_paths.external_headers]),
+
+ 'abs_root_dir': os.path.dirname(os.path.realpath(__file__)),
+
+ 'base_dir': source_paths.base_dir,
+ 'src_dir': source_paths.src_dir,
+ 'test_data_dir': source_paths.test_data_dir,
+ 'doc_dir': source_paths.doc_dir,
+ 'scripts_dir': normalize_source_path(source_paths.scripts_dir),
+ 'python_dir': source_paths.python_dir,
+
+ 'cli_exe_name': osinfo.cli_exe_name + program_suffix,
+ 'cli_exe': join_with_build_dir(osinfo.cli_exe_name + program_suffix),
+ 'build_cli_exe': bool('cli' in options.build_targets),
+ 'test_exe': join_with_build_dir('botan-test' + program_suffix),
+
+ 'lib_prefix': osinfo.lib_prefix,
+ 'static_suffix': osinfo.static_suffix,
+ 'lib_suffix': options.library_suffix,
+ 'libname': osinfo.library_name.format(major=Version.major(),
+ minor=Version.minor(),
+ suffix=options.library_suffix),
+
+ 'command_line': configure_command_line(),
+ 'local_config': read_textfile(options.local_config),
+
+ 'program_suffix': program_suffix,
+
+ 'prefix': options.prefix or osinfo.install_root,
+ '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,
+ 'docdir': options.docdir or osinfo.doc_dir,
+
+ 'with_documentation': options.with_documentation,
+ 'with_sphinx': options.with_sphinx,
+ 'with_pdf': options.with_pdf,
+ 'with_rst2man': options.with_rst2man,
+ 'sphinx_config_dir': source_paths.sphinx_config_dir,
+ 'with_doxygen': options.with_doxygen,
+ 'maintainer_mode': options.maintainer_mode,
+
+ 'out_dir': build_dir,
+ 'build_dir': build_paths.build_dir,
+
+ 'doc_stamp_file': os.path.join(build_paths.build_dir, 'doc.stamp'),
+ 'makefile_path': os.path.join(build_paths.build_dir, '..', 'Makefile'),
+
+ 'build_static_lib': options.build_static_lib,
+ 'build_shared_lib': options.build_shared_lib,
+
+ 'build_fuzzers': options.build_fuzzers,
+
+ 'build_coverage' : options.with_coverage_info or options.with_coverage,
+
+ 'symlink_shared_lib': options.build_shared_lib and osinfo.shared_lib_uses_symlinks,
+
+ 'libobj_dir': build_paths.libobj_dir,
+ 'cliobj_dir': build_paths.cliobj_dir,
+ 'testobj_dir': build_paths.testobj_dir,
+ 'fuzzobj_dir': build_paths.fuzzobj_dir,
+
+ 'fuzzer_output_dir': build_paths.fuzzer_output_dir if build_paths.fuzzer_output_dir else '',
+ 'doc_output_dir': build_paths.doc_output_dir,
+ 'handbook_output_dir': build_paths.handbook_output_dir,
+ 'doc_output_dir_doxygen': build_paths.doc_output_dir_doxygen,
+
+ 'compiler_include_dirs': '%s %s' % (build_paths.include_dir, build_paths.external_include_dir),
+
+ 'os': options.os,
+ 'arch': options.arch,
+ 'compiler': options.compiler,
+ 'cpu_family': arch.family,
+ 'endian': options.with_endian,
+ 'cpu_is_64bit': arch.wordsize == 64,
+
+ 'bakefile_arch': 'x86' if options.arch == 'x86_32' else 'x86_64',
+
+ 'innosetup_arch': innosetup_arch(options.os, options.arch),
+
+ 'mp_bits': choose_mp_bits(),
+
+ 'python_exe': choose_python_exe(),
+ 'python_version': options.python_version,
+ 'install_python_module': not options.no_install_python_module,
+
+ 'cxx': choose_cxx_exe(),
+ 'cxx_abi_flags': cc.mach_abi_link_flags(options),
+ 'linker': cc.linker_name or '$(CXX)',
+ 'make_supports_phony': osinfo.basename != 'windows',
+
+ 'sanitizer_types' : sorted(cc.sanitizer_types),
+
+ 'cc_compile_opt_flags': cc.cc_compile_flags(options, False, True),
+ 'cc_compile_debug_flags': cc.cc_compile_flags(options, True, False),
+
+ # These are for CMake
+ 'cxx_abi_opt_flags': cc.mach_abi_link_flags(options, False),
+ 'cxx_abi_debug_flags': cc.mach_abi_link_flags(options, True),
+
+ 'dash_o': cc.output_to_object,
+ 'dash_c': cc.compile_flags,
+
+ 'cc_lang_flags': cc.cc_lang_flags(),
+ 'os_feature_macros': osinfo.macros(cc),
+ 'cc_sysroot': sysroot_option(),
+ 'cc_compile_flags': options.cxxflags or cc.cc_compile_flags(options),
+ 'ldflags': options.ldflags or '',
+ 'extra_libs': extra_libs(options.extra_libs, cc),
+ 'cc_warning_flags': cc.cc_warning_flags(options),
+ 'output_to_exe': cc.output_to_exe,
+ 'cc_macro': cc.macro_name,
+
+ 'visibility_attribute': cc.gen_visibility_attribute(options),
+
+ 'lib_link_cmd': cc.so_link_command_for(osinfo.basename, options),
+ 'exe_link_cmd': cc.binary_link_command_for(osinfo.basename, options),
+ 'external_link_cmd': external_link_cmd(),
+
+ 'ar_command': ar_command(),
+ 'ar_options': options.ar_options or cc.ar_options or osinfo.ar_options,
+ 'ar_output_to': cc.ar_output_to,
+
+ 'link_to': ' '.join(
+ [(cc.add_lib_option % lib) for lib in link_to('libs')] +
+ [cc.add_framework_option + fw for fw in link_to('frameworks')]
+ ),
+
+ 'cmake_link_to': ' '.join(
+ link_to('libs') +
+ [('"' + cc.add_framework_option + fw + '"') for fw in link_to('frameworks')]
+ ),
+
+ 'fuzzer_lib': (cc.add_lib_option % options.fuzzer_lib) if options.fuzzer_lib else '',
+ 'libs_used': [lib.replace('.lib', '') for lib in link_to('libs')],
+
+ 'include_paths': build_paths.format_include_paths(cc, options.with_external_includedir),
+ 'module_defines': sorted(flatten([m.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'),
+
+ 'os_features': osinfo.enabled_features(options),
+ 'os_name': osinfo.basename,
+ 'cpu_features': arch.supported_isa_extensions(cc, options),
+ 'system_cert_bundle': options.system_cert_bundle,
+
+ 'fuzzer_mode': options.unsafe_fuzzer_mode,
+ 'fuzzer_type': options.build_fuzzers.upper() if options.build_fuzzers else '',
+
+ 'with_valgrind': options.with_valgrind,
+ 'with_openmp': options.with_openmp,
+ 'with_debug_asserts': options.with_debug_asserts,
+ 'test_mode': options.test_mode,
+ 'optimize_for_size': options.optimize_for_size,
+
+ 'mod_list': sorted([m.basename for m in modules])
+ }
+
+ variables['installed_include_dir'] = os.path.join(
+ variables['prefix'],
+ variables['includedir'],
+ 'botan-%d' % (Version.major()), 'botan')
+
+ 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
+ variables['cc_compile_flags'] = '%s %s' % (variables['cxx_abi_flags'], variables['cc_compile_flags'])
+ variables['cxx_abi_flags'] = ''
+
+ variables['lib_flags'] = cc.gen_lib_flags(options, variables)
+ variables['cmake_lib_flags'] = cmake_escape(variables['lib_flags'])
+
+ if options.with_pkg_config:
+ variables['botan_pkgconfig'] = os.path.join(build_paths.build_dir, 'botan-%d.pc' % (Version.major()))
+
+ # The name is always set because Windows build needs it
+ variables['static_lib_name'] = '%s%s.%s' % (variables['lib_prefix'], variables['libname'],
+ variables['static_suffix'])
+
+ if options.build_shared_lib:
+ if osinfo.soname_pattern_base is not None:
+ variables['soname_base'] = osinfo.soname_pattern_base.format(**variables)
+ variables['shared_lib_name'] = variables['soname_base']
+
+ if osinfo.soname_pattern_abi is not None:
+ variables['soname_abi'] = osinfo.soname_pattern_abi.format(**variables)
+ variables['shared_lib_name'] = variables['soname_abi']
+
+ if osinfo.soname_pattern_patch is not None:
+ variables['soname_patch'] = osinfo.soname_pattern_patch.format(**variables)
+
+ variables['lib_link_cmd'] = variables['lib_link_cmd'].format(**variables)
+
+ lib_targets = []
+ if options.build_static_lib:
+ lib_targets.append('static_lib_name')
+ if options.build_shared_lib:
+ lib_targets.append('shared_lib_name')
+
+ variables['library_targets'] = ' '.join([join_with_build_dir(variables[t]) for t in lib_targets])
+
+ if options.os == 'llvm' or options.compiler == 'msvc':
+ # llvm-link and msvc require just naming the file directly
+ variables['link_to_botan'] = os.path.join(build_dir, variables['static_lib_name'])
+ else:
+ variables['link_to_botan'] = '%s%s %s' % (cc.add_lib_dir_option, build_dir,
+ (cc.add_lib_option % variables['libname']))
+
+ return variables
+
+class ModulesChooser(object):
+ """
+ Determine which modules to load based on options, target, etc
+ """
+
+ def __init__(self, modules, module_policy, archinfo, osinfo, ccinfo, cc_min_version, options):
+ self._modules = modules
+ self._module_policy = module_policy
+ self._archinfo = archinfo
+ self._osinfo = osinfo
+ self._ccinfo = ccinfo
+ self._cc_min_version = cc_min_version
+ self._options = options
+
+ self._maybe_dep = set()
+ self._to_load = set()
+ # string to set mapping with reasons as key and modules as value
+ self._not_using_because = collections.defaultdict(set)
+
+ ModulesChooser._validate_dependencies_exist(self._modules)
+ ModulesChooser._validate_user_selection(
+ self._modules, self._options.enabled_modules, self._options.disabled_modules)
+
+ def _check_usable(self, module, modname):
+ if not module.compatible_cpu(self._archinfo, self._options):
+ self._not_using_because['incompatible CPU'].add(modname)
+ return False
+ elif not module.compatible_os(self._osinfo, self._options):
+ self._not_using_because['incompatible OS'].add(modname)
+ return False
+ elif not module.compatible_compiler(self._ccinfo, self._cc_min_version, self._archinfo.basename):
+ self._not_using_because['incompatible compiler'].add(modname)
+ return False
+ return True
+
+ @staticmethod
+ def _display_module_information_unused(skipped_modules):
+ for reason in sorted(skipped_modules.keys()):
+ disabled_mods = sorted(skipped_modules[reason])
+ if disabled_mods:
+ logging.info('Skipping (%s): %s' % (reason, ' '.join(disabled_mods)))
+
+ @staticmethod
+ def _display_module_information_to_load(all_modules, modules_to_load):
+ sorted_modules_to_load = sorted(modules_to_load)
+
+ for modname in sorted_modules_to_load:
+ if all_modules[modname].comment:
+ logging.info('%s: %s' % (modname, all_modules[modname].comment))
+ if all_modules[modname].warning:
+ logging.warning('%s: %s' % (modname, all_modules[modname].warning))
+ if all_modules[modname].load_on == 'vendor':
+ logging.info('Enabling use of external dependency %s' % modname)
+
+ if sorted_modules_to_load:
+ logging.info('Loading modules: %s', ' '.join(sorted_modules_to_load))
+ else:
+ logging.error('This configuration disables every submodule and is invalid')
+
+ @staticmethod
+ def _validate_state(used_modules, unused_modules):
+ for reason, unused_for_reason in unused_modules.items():
+ intersection = unused_for_reason & used_modules
+ if intersection:
+ raise InternalError(
+ "Disabled modules (%s) and modules to load have common elements: %s"
+ % (reason, intersection))
+
+ @staticmethod
+ def _validate_dependencies_exist(modules):
+ for module in modules.values():
+ module.dependencies_exist(modules)
+
+ @staticmethod
+ def _validate_user_selection(modules, enabled_modules, disabled_modules):
+ for modname in enabled_modules:
+ if modname not in modules:
+ logging.error("Module not found: %s" % modname)
+
+ for modname in disabled_modules:
+ if modname not in modules:
+ logging.warning("Disabled module not found: %s" % modname)
+
+ def _handle_by_module_policy(self, modname, usable):
+ if self._module_policy is not None:
+ if modname in self._module_policy.required:
+ if not usable:
+ logging.error('Module policy requires module %s not usable on this platform' % (modname))
+ elif modname in self._options.disabled_modules:
+ logging.error('Module %s was disabled but is required by policy' % (modname))
+ self._to_load.add(modname)
+ return True
+ elif modname in self._module_policy.if_available:
+ if modname in self._options.disabled_modules:
+ self._not_using_because['disabled by user'].add(modname)
+ elif usable:
+ logging.debug('Enabling optional module %s' % (modname))
+ self._to_load.add(modname)
+ return True
+ elif modname in self._module_policy.prohibited:
+ if modname in self._options.enabled_modules:
+ logging.error('Module %s was requested but is prohibited by policy' % (modname))
+ self._not_using_because['prohibited by module policy'].add(modname)
+ return True
+
+ return False
+
+ @staticmethod
+ def resolve_dependencies(available_modules, dependency_table, module, loaded_modules=None):
+ """
+ Parameters
+ - available_modules: modules to choose from. Constant.
+ - dependency_table: module to dependencies map. Constant.
+ - module: name of the module to resolve dependencies. Constant.
+ - loaded_modules: modules already loaded. Defensive copy in order to not change value for caller.
+ """
+ if loaded_modules is None:
+ loaded_modules = set([])
+ else:
+ loaded_modules = copy.copy(loaded_modules)
+
+ if module not in available_modules:
+ return False, None
+
+ loaded_modules.add(module)
+ for dependency in dependency_table[module]:
+ dependency_choices = set(dependency.split('|'))
+
+ dependency_met = False
+
+ if not set(dependency_choices).isdisjoint(loaded_modules):
+ dependency_met = True
+ else:
+ possible_mods = dependency_choices.intersection(available_modules)
+
+ for mod in possible_mods:
+ ok, dependency_modules = ModulesChooser.resolve_dependencies(
+ available_modules, dependency_table, mod, loaded_modules)
+ if ok:
+ dependency_met = True
+ loaded_modules.add(mod)
+ loaded_modules.update(dependency_modules)
+ break
+
+ if not dependency_met:
+ return False, None
+
+ return True, loaded_modules
+
+ def _modules_dependency_table(self):
+ out = {}
+ for modname in self._modules:
+ out[modname] = self._modules[modname].dependencies(self._osinfo)
+ return out
+
+ def _resolve_dependencies_for_all_modules(self):
+ available_modules = set(self._to_load) | set(self._maybe_dep)
+ dependency_table = self._modules_dependency_table()
+
+ successfully_loaded = set()
+
+ for modname in self._to_load:
+ # This will try to recursively load all dependencies of modname
+ ok, modules = self.resolve_dependencies(available_modules, dependency_table, modname)
+ if ok:
+ successfully_loaded.add(modname)
+ successfully_loaded.update(modules)
+ else:
+ # Skip this module
+ pass
+
+ self._not_using_because['dependency failure'].update(self._to_load - successfully_loaded)
+ self._to_load = successfully_loaded
+ self._maybe_dep -= successfully_loaded
+
+ def _handle_by_load_on(self, module): # pylint: disable=too-many-branches
+ modname = module.basename
+ if module.load_on == 'never':
+ self._not_using_because['disabled as buggy'].add(modname)
+ elif module.load_on == 'request':
+ if self._options.with_everything:
+ self._to_load.add(modname)
+ else:
+ self._not_using_because['by request only'].add(modname)
+ elif module.load_on == 'vendor':
+ if self._options.with_everything:
+ self._to_load.add(modname)
+ else:
+ self._not_using_because['requires external dependency'].add(modname)
+ elif module.load_on == 'dep':
+ self._maybe_dep.add(modname)
+
+ elif module.load_on == 'always':
+ self._to_load.add(modname)
+
+ elif module.load_on == 'auto':
+ if self._options.no_autoload or self._module_policy is not None:
+ self._maybe_dep.add(modname)
+ else:
+ self._to_load.add(modname)
+ else:
+ logging.error('Unknown load_on %s in %s' % (
+ module.load_on, modname))
+
+ def choose(self):
+ for (modname, module) in self._modules.items():
+ usable = self._check_usable(module, modname)
+
+ module_handled = self._handle_by_module_policy(modname, usable)
+ if module_handled:
+ continue
+
+ if modname in self._options.disabled_modules:
+ self._not_using_because['disabled by user'].add(modname)
+ elif usable:
+ if modname in self._options.enabled_modules:
+ self._to_load.add(modname) # trust the user
+ else:
+ self._handle_by_load_on(module)
+
+ if 'compression' in self._to_load:
+ # Confirm that we have at least one compression library enabled
+ # Otherwise we leave a lot of useless support code compiled in, plus a
+ # make_compressor call that always fails
+ if 'zlib' not in self._to_load and 'bzip2' not in self._to_load and 'lzma' not in self._to_load:
+ self._to_load.remove('compression')
+ self._not_using_because['no enabled compression schemes'].add('compression')
+
+ self._resolve_dependencies_for_all_modules()
+
+ for not_a_dep in self._maybe_dep:
+ self._not_using_because['not requested'].add(not_a_dep)
+
+ ModulesChooser._validate_state(self._to_load, self._not_using_because)
+ ModulesChooser._display_module_information_unused(self._not_using_because)
+ ModulesChooser._display_module_information_to_load(self._modules, self._to_load)
+
+ return self._to_load
+
+def choose_link_method(options):
+ """
+ Choose the link method based on system availability and user request
+ """
+
+ req = options.link_method
+
+ def useable_methods():
+
+ # Symbolic link support on Windows was introduced in Windows 6.0 (Vista)
+ # and Python 3.2. Furthermore, the SeCreateSymbolicLinkPrivilege is
+ # required in order to successfully create symlinks. So only try to use
+ # symlinks on Windows if explicitly requested.
+
+ # MinGW declares itself as 'Windows'
+ host_is_windows = python_platform_identifier() in ['windows', 'cygwin']
+
+ if 'symlink' in os.__dict__:
+ if host_is_windows:
+ if req == 'symlink':
+ yield 'symlink'
+ else:
+ yield 'symlink'
+
+ if 'link' in os.__dict__:
+ yield 'hardlink'
+
+ yield 'copy'
+
+ for method in useable_methods():
+ if req is None or req == method:
+ logging.info('Using %s to link files into build dir ' \
+ '(use --link-method to change)' % (method))
+ return method
+
+ logging.warning('Could not use link method "%s", will copy instead' % (req))
+ return 'copy'
+
+def portable_symlink(file_path, target_dir, method):
+ """
+ Copy or link the file, depending on what the platform offers
+ """
+
+ if not os.access(file_path, os.R_OK):
+ logging.warning('Missing file %s' % (file_path))
+ return
+
+ if method == 'symlink':
+ rel_file_path = os.path.relpath(file_path, start=target_dir)
+ os.symlink(rel_file_path, os.path.join(target_dir, os.path.basename(file_path)))
+ elif method == 'hardlink':
+ os.link(file_path, os.path.join(target_dir, os.path.basename(file_path)))
+ elif method == 'copy':
+ shutil.copy(file_path, target_dir)
+ else:
+ raise UserError('Unknown link method %s' % (method))
+
+
+class AmalgamationHelper(object):
+ # All include types may have trailing comment like e.g. '#include <vector> // IWYU pragma: export'
+ _any_include = re.compile(r'#include <(.*)>')
+ _botan_include = re.compile(r'#include <botan/(.*)>')
+
+ # Only matches at the beginning of the line. By convention, this means that the include
+ # is not wrapped by condition macros
+ _unconditional_any_include = re.compile(r'^#include <(.*)>')
+ # stddef.h is included in ffi.h
+ _unconditional_std_include = re.compile(r'^#include <([^/\.]+|stddef.h)>')
+
+ @staticmethod
+ def is_any_include(cpp_source_line):
+ match = AmalgamationHelper._any_include.search(cpp_source_line)
+ if match:
+ return match.group(1)
+ else:
+ return None
+
+ @staticmethod
+ def is_botan_include(cpp_source_line):
+ match = AmalgamationHelper._botan_include.search(cpp_source_line)
+ if match:
+ return match.group(1)
+ else:
+ return None
+
+ @staticmethod
+ def is_unconditional_any_include(cpp_source_line):
+ match = AmalgamationHelper._unconditional_any_include.search(cpp_source_line)
+ if match:
+ return match.group(1)
+ else:
+ return None
+
+ @staticmethod
+ def is_unconditional_std_include(cpp_source_line):
+ match = AmalgamationHelper._unconditional_std_include.search(cpp_source_line)
+ if match:
+ return match.group(1)
+ else:
+ return None
+
+ @staticmethod
+ def write_banner(fd):
+ fd.write("""/*
+* Botan %s Amalgamation
+* (C) 1999-2020 The Botan Authors
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+""" % (Version.as_string()))
+
+
+class AmalgamationHeader(object):
+ def __init__(self, input_filepaths):
+
+ self.included_already = set()
+ self.all_std_includes = set()
+
+ self.file_contents = {}
+ for filepath in sorted(input_filepaths):
+ try:
+ contents = AmalgamationGenerator.read_header(filepath)
+ self.file_contents[os.path.basename(filepath)] = contents
+ except IOError as e:
+ logging.error('Error processing file %s for amalgamation: %s' % (filepath, e))
+
+ self.contents = ''
+ for name in sorted(self.file_contents):
+ self.contents += ''.join(list(self.header_contents(name)))
+
+ self.header_includes = ''
+ for std_header in sorted(self.all_std_includes):
+ self.header_includes += '#include <%s>\n' % (std_header)
+ self.header_includes += '\n'
+
+ def header_contents(self, name):
+ name = name.replace('internal/', '')
+
+ if name in self.included_already:
+ return
+
+ self.included_already.add(name)
+
+ if name not in self.file_contents:
+ return
+
+ depr_marker = 'BOTAN_DEPRECATED_HEADER(%s)\n' % (name)
+ if depr_marker in self.file_contents[name]:
+ logging.debug("Ignoring deprecated header %s", name)
+ return
+
+ for line in self.file_contents[name]:
+ header = AmalgamationHelper.is_botan_include(line)
+ if header:
+ for c in self.header_contents(header):
+ yield c
+ else:
+ std_header = AmalgamationHelper.is_unconditional_std_include(line)
+
+ if std_header:
+ self.all_std_includes.add(std_header)
+ else:
+ yield line
+
+ def write_to_file(self, filepath, include_guard):
+ with open(filepath, 'w') as f:
+ AmalgamationHelper.write_banner(f)
+ f.write("\n#ifndef %s\n#define %s\n\n" % (include_guard, include_guard))
+ f.write(self.header_includes)
+ f.write(self.contents)
+ f.write("\n#endif // %s\n" % (include_guard))
+
+
+class AmalgamationGenerator(object):
+ _header_guard_pattern = re.compile(r'^#define BOTAN_.*_H_\s*$')
+ _header_endif_pattern = re.compile(r'^#endif.*$')
+
+ @staticmethod
+ def read_header(filepath):
+ encoding_kwords = {}
+ if sys.version_info[0] == 3:
+ encoding_kwords['encoding'] = 'utf8'
+ with open(filepath, **encoding_kwords) as f:
+ raw_content = f.readlines()
+ return AmalgamationGenerator.strip_header_goop(filepath, raw_content)
+
+ @staticmethod
+ def strip_header_goop(header_name, header_lines):
+ lines = copy.copy(header_lines) # defensive copy
+
+ start_header_guard_index = None
+ for index, line in enumerate(lines):
+ if AmalgamationGenerator._header_guard_pattern.match(line):
+ start_header_guard_index = index
+ break
+ if start_header_guard_index is None:
+ raise InternalError("No header guard start found in " + header_name)
+
+ end_header_guard_index = None
+ for index, line in enumerate(lines):
+ if AmalgamationGenerator._header_endif_pattern.match(line):
+ end_header_guard_index = index # override with last found
+ if end_header_guard_index is None:
+ raise InternalError("No header guard end found in " + header_name)
+
+ lines = lines[start_header_guard_index+1 : end_header_guard_index]
+
+ # Strip leading and trailing empty lines
+ while lines[0].strip() == "":
+ lines = lines[1:]
+ while lines[-1].strip() == "":
+ lines = lines[0:-1]
+
+ return lines
+
+ def __init__(self, prefix, build_paths, modules, options):
+ self._filename_prefix = prefix
+ self._build_paths = build_paths
+ self._modules = modules
+ self._options = options
+
+ def generate(self):
+ encoding_kwords = {}
+ if sys.version_info[0] == 3:
+ encoding_kwords['encoding'] = 'utf8'
+
+ pub_header_amalag = AmalgamationHeader(self._build_paths.public_headers)
+ amalgamation_header_fsname = '%s.h' % (self._filename_prefix)
+ logging.info('Writing amalgamation header to %s' % (amalgamation_header_fsname))
+ pub_header_amalag.write_to_file(amalgamation_header_fsname, "BOTAN_AMALGAMATION_H_")
+
+ internal_headers_list = []
+
+ for hdr in self._build_paths.internal_headers:
+ internal_headers_list.append(hdr)
+
+ # file descriptors for all `amalgamation_sources`
+ amalgamation_fsname = '%s.cpp' % (self._filename_prefix)
+ logging.info('Writing amalgamation source to %s' % (amalgamation_fsname))
+
+ amalgamation_file = open(amalgamation_fsname, 'w', **encoding_kwords)
+
+ AmalgamationHelper.write_banner(amalgamation_file)
+ amalgamation_file.write('\n#include "%s"\n\n' % (amalgamation_header_fsname))
+
+ internal_headers = AmalgamationHeader(internal_headers_list)
+ amalgamation_file.write(internal_headers.header_includes)
+ amalgamation_file.write(internal_headers.contents)
+
+ unconditional_headers = set([])
+
+ for mod in sorted(self._modules, key=lambda module: module.basename):
+ for src in sorted(mod.source):
+ with open(src, 'r', **encoding_kwords) as f:
+ for line in f:
+ if AmalgamationHelper.is_botan_include(line):
+ # Botan headers are inlined in amalgamation headers
+ continue
+
+ if AmalgamationHelper.is_any_include(line) in unconditional_headers:
+ # This include (conditional or unconditional) was unconditionally added before
+ continue
+
+ amalgamation_file.write(line)
+ unconditional_header = AmalgamationHelper.is_unconditional_any_include(line)
+ if unconditional_header:
+ unconditional_headers.add(unconditional_header)
+
+ amalgamation_file.close()
+
+ return ([amalgamation_fsname], [amalgamation_header_fsname])
+
+
+def have_program(program):
+ """
+ Test for the existence of a program
+ """
+
+ def exe_test(path, program):
+ exe_file = os.path.join(path, program)
+
+ if os.path.exists(exe_file) and os.access(exe_file, os.X_OK):
+ logging.debug('Found program %s in %s' % (program, path))
+ return True
+ else:
+ return False
+
+ exe_suffixes = ['', '.exe']
+
+ for path in os.environ['PATH'].split(os.pathsep):
+ for suffix in exe_suffixes:
+ if exe_test(path, program + suffix):
+ return True
+
+ logging.debug('Program %s not found' % (program))
+ return False
+
+
+class BotanConfigureLogHandler(logging.StreamHandler, object):
+ def emit(self, record):
+ # Do the default stuff first
+ super(BotanConfigureLogHandler, self).emit(record)
+ # Exit script if and ERROR or worse occurred
+ if record.levelno >= logging.ERROR:
+ sys.exit(1)
+
+
+def setup_logging(options):
+ if options.verbose:
+ log_level = logging.DEBUG
+ elif options.quiet:
+ log_level = logging.WARNING
+ else:
+ log_level = logging.INFO
+
+ lh = BotanConfigureLogHandler(sys.stdout)
+ lh.setFormatter(logging.Formatter('%(levelname) 7s: %(message)s'))
+ logging.getLogger().addHandler(lh)
+ logging.getLogger().setLevel(log_level)
+
+
+def load_info_files(search_dir, descr, filename_matcher, class_t):
+ info = {}
+
+ def filename_matches(filename):
+ if isinstance(filename_matcher, str):
+ return filename == filename_matcher
+ else:
+ return filename_matcher.match(filename) is not None
+
+ for (dirpath, _, filenames) in os.walk(search_dir):
+ for filename in filenames:
+ filepath = os.path.join(dirpath, filename)
+ if filename_matches(filename):
+ info_obj = class_t(filepath)
+ info[info_obj.basename] = info_obj
+
+ if info:
+ infotxt_basenames = ' '.join(sorted(info.keys()))
+ logging.debug('Loaded %d %s files: %s' % (len(info), descr, infotxt_basenames))
+ else:
+ logging.warning('Failed to load any %s files' % (descr))
+
+ return info
+
+
+def load_build_data_info_files(source_paths, descr, subdir, class_t):
+ matcher = re.compile(r'[_a-z0-9]+\.txt$')
+ return load_info_files(os.path.join(source_paths.build_data_dir, subdir), descr, matcher, class_t)
+
+
+# Workaround for Windows systems where antivirus is enabled GH #353
+def robust_rmtree(path, max_retries=5):
+ for _ in range(max_retries):
+ try:
+ shutil.rmtree(path)
+ return
+ except OSError:
+ time.sleep(0.1)
+
+ # Final attempt, pass any exceptions up to caller.
+ shutil.rmtree(path)
+
+
+# Workaround for Windows systems where antivirus is enabled GH #353
+def robust_makedirs(directory, max_retries=5):
+ for _ in range(max_retries):
+ try:
+ os.makedirs(directory)
+ return
+ except OSError as e:
+ if e.errno == errno.EEXIST:
+ raise
+
+ time.sleep(0.1)
+
+ # Final attempt, pass any exceptions up to caller.
+ os.makedirs(directory)
+
+def python_platform_identifier():
+ system_from_python = platform.system().lower()
+ if re.match('^cygwin_.*', system_from_python):
+ return 'cygwin'
+ else:
+ return system_from_python
+
+# This is for otions that have --with-XYZ and --without-XYZ. If user does not
+# set any of those, we choose a default here.
+# Mutates `options`
+def set_defaults_for_unset_options(options, info_arch, info_cc, info_os): # pylint: disable=too-many-branches
+ if options.os is None:
+ options.os = python_platform_identifier()
+ logging.info('Guessing target OS is %s (use --os to set)' % (options.os))
+
+ if options.os not in info_os:
+ def find_canonical_os_name(os_name_variant):
+ for (canonical_os_name, os_info) in info_os.items():
+ if os_info.matches_name(os_name_variant):
+ return canonical_os_name
+ return os_name_variant # not found
+ options.os = find_canonical_os_name(options.os)
+
+ def deduce_compiler_type_from_cc_bin(cc_bin):
+ 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:
+ 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)
+
+ if options.compiler is None:
+ logging.error("Could not figure out what compiler type '%s' is, use --cc to set" % (
+ options.compiler_binary))
+
+ if options.compiler is None and options.os in info_os:
+ options.compiler = info_os[options.os].default_compiler
+
+ if not have_program(info_cc[options.compiler].binary_name):
+ logging.error("Default compiler for system is %s but could not find binary '%s'; use --cc to set" % (
+ options.compiler, info_cc[options.compiler].binary_name))
+
+ logging.info('Guessing to use compiler %s (use --cc or CXX to set)' % (options.compiler))
+
+ if options.cpu is None:
+ (arch, cpu) = guess_processor(info_arch)
+ options.arch = arch
+ 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)')
+ options.with_sphinx = True
+ if options.with_rst2man is None and have_program('rst2man'):
+ logging.info('Found rst2man (use --without-rst2man to disable)')
+ options.with_rst2man = True
+
+ if options.with_pkg_config is None and options.os in info_os:
+ options.with_pkg_config = info_os[options.os].uses_pkg_config
+
+ if options.system_cert_bundle is None:
+ default_paths = [
+ '/etc/ssl/certs/ca-certificates.crt', # Ubuntu, Debian, Arch, Gentoo
+ '/etc/pki/tls/certs/ca-bundle.crt', # RHEL
+ '/etc/ssl/ca-bundle.pem', # SuSE
+ '/etc/ssl/cert.pem', # OpenBSD, FreeBSD, Alpine
+ '/etc/certs/ca-certificates.crt', # Solaris
+ ]
+
+ for path in default_paths:
+ if os.access(path, os.R_OK):
+ logging.info('Using %s as system certificate store', path)
+ options.system_cert_bundle = path
+ break
+ else:
+ if not os.access(options.system_cert_bundle, os.R_OK):
+ logging.warning('Provided system cert bundle path %s not found, ignoring', options.system_cert_bundle)
+ options.system_cert_bundle = None
+
+# Mutates `options`
+def canonicalize_options(options, info_os, info_arch):
+ # pylint: disable=too-many-branches
+
+ # canonical ARCH/CPU
+ options.arch = canon_processor(info_arch, options.cpu)
+ if options.arch is None:
+ raise UserError('Unknown or unidentifiable processor "%s"' % (options.cpu))
+
+ if options.cpu != options.arch:
+ logging.info('Canonicalized CPU target %s to %s', options.cpu, options.arch)
+
+ # select and sanity check build targets
+ def canonicalize_build_targets(options):
+ # --build-targets was not provided: build default targets
+ if options.build_targets is None:
+ return ["cli", "tests"]
+
+ # flatten the list of multiple --build-targets="" and comma separation
+ build_targets = [t.strip().lower() for ts in options.build_targets for t in ts.split(",")]
+
+ # validate that all requested build targets are available
+ for build_target in build_targets:
+ if build_target not in ACCEPTABLE_BUILD_TARGETS:
+ raise UserError("unknown build target: %s" % build_target)
+
+ # building the shared lib desired and without contradiction?
+ if options.build_shared_lib is None:
+ options.build_shared_lib = "shared" in build_targets
+ elif bool(options.build_shared_lib) != bool("shared" in build_targets):
+ raise UserError("inconsistent usage of --enable/disable-shared-library and --build-targets")
+
+ # building the static lib desired and without contradiction?
+ if options.build_static_lib is None:
+ options.build_static_lib = "static" in build_targets
+ elif bool(options.build_static_lib) != bool("static" in build_targets):
+ raise UserError("inconsistent usage of --enable/disable-static-library and --build-targets")
+
+ return build_targets
+
+ options.build_targets = canonicalize_build_targets(options)
+
+ shared_libs_supported = options.os in info_os and info_os[options.os].building_shared_supported()
+
+ if not shared_libs_supported:
+ if options.build_shared_lib is True:
+ logging.warning('Shared libs not supported on %s, disabling shared lib support' % (options.os))
+ options.build_shared_lib = False
+ elif options.build_shared_lib is None:
+ logging.info('Shared libs not supported on %s, disabling shared lib support' % (options.os))
+
+ if options.os == 'windows' and options.build_shared_lib is None and options.build_static_lib is None:
+ options.build_shared_lib = True
+
+ if options.with_stack_protector is None:
+ if options.os in info_os:
+ options.with_stack_protector = info_os[options.os].use_stack_protector
+
+ if options.build_shared_lib is None:
+ if options.os == 'windows' and options.build_static_lib:
+ pass
+ else:
+ options.build_shared_lib = shared_libs_supported
+
+ if options.build_static_lib is None:
+ if options.os == 'windows' and options.build_shared_lib:
+ pass
+ else:
+ options.build_static_lib = True
+
+ # Set default fuzzing lib
+ if options.build_fuzzers == 'libfuzzer' and options.fuzzer_lib is None:
+ options.fuzzer_lib = 'Fuzzer'
+
+ if options.ldflags is not None:
+ extra_libs = []
+ link_to_lib = re.compile('^-l(.*)')
+ for flag in options.ldflags.split(' '):
+ match = link_to_lib.match(flag)
+ if match:
+ extra_libs.append(match.group(1))
+
+ options.extra_libs += ','.join(extra_libs)
+
+# Checks user options for consistency
+# This method DOES NOT change options on behalf of the user but explains
+# why the given configuration does not work.
+def validate_options(options, info_os, info_cc, available_module_policies):
+ # pylint: disable=too-many-branches,too-many-statements
+
+ if options.name_amalgamation != 'botan_all':
+ if options.name_amalgamation == '':
+ raise UserError('Amalgamation basename must be non-empty')
+
+ acceptable_name_re = re.compile('^[a-zA-Z0-9_]+$')
+ if acceptable_name_re.match(options.name_amalgamation) is None:
+ raise UserError("Amalgamation basename must match [a-zA-Z0-9_]+")
+
+ if options.os == "java":
+ raise UserError("Jython detected: need --os and --cpu to set target")
+
+ if options.os not in info_os:
+ raise UserError('Unknown OS "%s"; available options: %s' % (
+ options.os, ' '.join(sorted(info_os.keys()))))
+
+ if options.compiler not in info_cc:
+ raise UserError('Unknown compiler "%s"; available options: %s' % (
+ options.compiler, ' '.join(sorted(info_cc.keys()))))
+
+ if options.cc_min_version is not None and not re.match(r'^[0-9]+\.[0-9]+$', options.cc_min_version):
+ raise UserError("--cc-min-version must have the format MAJOR.MINOR")
+
+ if options.module_policy and options.module_policy not in available_module_policies:
+ raise UserError("Unknown module set %s" % options.module_policy)
+
+ if options.cpu == 'llvm' or options.os in ['llvm', 'emscripten']:
+ if options.compiler != 'clang':
+ raise UserError('LLVM target requires using Clang')
+
+ if options.cpu != 'llvm':
+ raise UserError('LLVM target requires CPU target set to LLVM bitcode (llvm)')
+
+ if options.os not in ['llvm', 'emscripten']:
+ raise UserError('Target OS is not an LLVM bitcode target')
+
+ if options.build_fuzzers is not None:
+ if options.build_fuzzers not in ['libfuzzer', 'afl', 'klee', 'test']:
+ raise UserError('Bad value to --build-fuzzers')
+
+ if options.build_fuzzers == 'klee' and options.os != 'llvm':
+ raise UserError('Building for KLEE requires targeting LLVM')
+
+ if options.build_static_lib is False and options.build_shared_lib is False:
+ raise UserError('With both --disable-static-library and --disable-shared-library, nothing to do')
+
+ if options.os == 'windows' and options.build_static_lib is True and options.build_shared_lib is True:
+ raise UserError('On Windows only one of static lib and DLL can be selected')
+
+ if options.with_documentation is False:
+ if options.with_doxygen:
+ raise UserError('Using --with-doxygen plus --without-documentation makes no sense')
+ if options.with_sphinx:
+ 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_pdf and not options.with_sphinx:
+ raise UserError('Option --with-pdf requires --with-sphinx')
+
+ if options.with_bakefile:
+ if options.os != 'windows' or options.compiler != 'msvc' or options.build_shared_lib is False:
+ raise UserError("Building via bakefile is only supported for MSVC DLL build")
+
+ if options.arch not in ['x86_64', 'x86_32']:
+ raise UserError("Bakefile only supports x86 targets")
+
+ # Warnings
+ if options.os == 'windows' and options.compiler != 'msvc':
+ 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':
+ raise UserError("Makes no sense to specify MSVC runtime for %s" % (options.compiler))
+
+ if options.msvc_runtime not in ['MT', 'MD', 'MTd', 'MDd']:
+ logging.warning("MSVC runtime option '%s' not known", (options.msvc_runtime))
+
+def run_compiler_preproc(options, ccinfo, source_file, default_return, extra_flags=None):
+ if extra_flags is None:
+ extra_flags = []
+
+ cc_bin = options.compiler_binary or ccinfo.binary_name
+
+ cmd = cc_bin.split(' ') + ccinfo.preproc_flags.split(' ') + extra_flags + [source_file]
+
+ try:
+ logging.debug("Running '%s'", ' '.join(cmd))
+ stdout, _ = subprocess.Popen(
+ cmd,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ universal_newlines=True).communicate()
+ cc_output = stdout
+ except OSError as e:
+ logging.warning('Could not execute %s: %s' % (cmd, e))
+ return default_return
+
+ def cleanup_output(output):
+ return ('\n'.join([l for l in output.splitlines() if l.startswith('#') is False])).strip()
+
+ return cleanup_output(cc_output)
+
+def calculate_cc_min_version(options, ccinfo, source_paths):
+ version_patterns = {
+ 'msvc': r'^ *MSVC ([0-9]{2})([0-9]{2})$',
+ 'gcc': r'^ *GCC ([0-9]+) ([0-9]+)$',
+ 'clang': r'^ *CLANG ([0-9]+) ([0-9]+)$',
+ 'xlc': r'^ *XLC ([0-9]+) ([0-9]+)$',
+ }
+
+ unknown_pattern = r'UNKNOWN 0 0'
+
+ if ccinfo.basename not in version_patterns:
+ logging.info("No compiler version detection available for %s" % (ccinfo.basename))
+ return "0.0"
+
+ detect_version_source = os.path.join(source_paths.build_data_dir, "detect_version.cpp")
+
+ cc_output = run_compiler_preproc(options, ccinfo, detect_version_source, "0.0")
+
+ if re.search(unknown_pattern, cc_output) is not None:
+ logging.warning('Failed to get version for %s from macro check' % (ccinfo.basename))
+ return "0.0"
+
+ match = re.search(version_patterns[ccinfo.basename], cc_output, flags=re.MULTILINE)
+ if match is None:
+ logging.warning("Tried to get %s version, but output '%s' does not match expected version format" % (
+ ccinfo.basename, cc_output))
+ return "0.0"
+
+ major_version = int(match.group(1), 0)
+ minor_version = int(match.group(2), 0)
+ cc_version = "%d.%d" % (major_version, minor_version)
+ logging.info('Auto-detected compiler version %s' % (cc_version))
+
+ return cc_version
+
+def check_compiler_arch(options, ccinfo, archinfo, source_paths):
+ detect_version_source = os.path.join(source_paths.build_data_dir, 'detect_arch.cpp')
+
+ abi_flags = ccinfo.mach_abi_link_flags(options).split(' ')
+ cc_output = run_compiler_preproc(options, ccinfo, detect_version_source, 'UNKNOWN', abi_flags).lower()
+
+ if cc_output == '':
+ cc_output = run_compiler_preproc(options, ccinfo, detect_version_source, 'UNKNOWN').lower()
+
+ if cc_output == 'unknown':
+ logging.warning('Unable to detect target architecture via compiler macro checks')
+ return None
+
+ if cc_output not in archinfo:
+ # Should not happen
+ logging.warning("Error detecting compiler target arch: '%s'", cc_output)
+ return None
+
+ logging.info('Auto-detected compiler arch %s' % (cc_output))
+ return cc_output
+
+def do_io_for_build(cc, arch, osinfo, using_mods, build_paths, source_paths, template_vars, options):
+ # pylint: disable=too-many-locals,too-many-branches,too-many-statements
+
+ try:
+ robust_rmtree(build_paths.build_dir)
+ except OSError as e:
+ if e.errno != errno.ENOENT:
+ logging.error('Problem while removing build dir: %s' % (e))
+
+ for build_dir in build_paths.build_dirs():
+ try:
+ robust_makedirs(build_dir)
+ except OSError as e:
+ if e.errno != errno.EEXIST:
+ logging.error('Error while creating "%s": %s' % (build_dir, e))
+
+ def write_template(sink, template):
+ with open(sink, 'w') as f:
+ f.write(process_template(template, template_vars))
+
+ def in_build_dir(p):
+ return os.path.join(build_paths.build_dir, p)
+ def in_build_data(p):
+ return os.path.join(source_paths.build_data_dir, p)
+
+ write_template(in_build_dir('build.h'), in_build_data('buildh.in'))
+ write_template(in_build_dir('botan.doxy'), in_build_data('botan.doxy.in'))
+
+ if 'botan_pkgconfig' in template_vars:
+ write_template(template_vars['botan_pkgconfig'], in_build_data('botan.pc.in'))
+
+ if options.os == 'windows':
+ write_template(in_build_dir('botan.iss'), in_build_data('innosetup.in'))
+
+ link_method = choose_link_method(options)
+
+ def link_headers(headers, visibility, directory):
+ logging.debug('Linking %d %s header files in %s' % (len(headers), visibility, directory))
+
+ for header_file in headers:
+ try:
+ portable_symlink(header_file, directory, link_method)
+ except OSError as e:
+ if e.errno != errno.EEXIST:
+ raise UserError('Error linking %s into %s: %s' % (header_file, directory, e))
+
+ link_headers(build_paths.public_headers, 'public',
+ build_paths.botan_include_dir)
+
+ link_headers(build_paths.internal_headers, 'internal',
+ build_paths.internal_include_dir)
+
+ link_headers(build_paths.external_headers, 'external',
+ build_paths.external_include_dir)
+
+ if options.amalgamation:
+ (amalg_cpp_files, amalg_headers) = AmalgamationGenerator(
+ options.name_amalgamation, build_paths, using_mods, options).generate()
+ build_paths.lib_sources = amalg_cpp_files
+ template_vars['generated_files'] = ' '.join(amalg_cpp_files + amalg_headers)
+
+ # Inserting an amalgamation generated using DLL visibility flags into a
+ # binary project will either cause errors (on Windows) or unnecessary overhead.
+ # Provide a hint
+ 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))
+
+ with open(os.path.join(build_paths.build_dir, 'build_config.json'), 'w') as f:
+ json.dump(template_vars, f, sort_keys=True, indent=2)
+
+ if options.with_cmake:
+ logging.warning("CMake build is only for development: use make for production builds")
+ cmake_template = os.path.join(source_paths.build_data_dir, 'cmake.in')
+ write_template('CMakeLists.txt', cmake_template)
+ elif options.with_bakefile:
+ logging.warning("Bakefile build is only for development: use make for production builds")
+ bakefile_template = os.path.join(source_paths.build_data_dir, 'bakefile.in')
+ write_template('botan.bkl', bakefile_template)
+ else:
+ makefile_template = os.path.join(source_paths.build_data_dir, 'makefile.in')
+ write_template(template_vars['makefile_path'], makefile_template)
+
+ if options.with_rst2man:
+ rst2man_file = os.path.join(build_paths.build_dir, 'botan.rst')
+ cli_doc = os.path.join(source_paths.doc_dir, 'cli.rst')
+
+ cli_doc_contents = open(cli_doc).readlines()
+
+ while cli_doc_contents[0] != "\n":
+ cli_doc_contents.pop(0)
+
+ rst2man_header = """
+botan
+=============================
+
+:Subtitle: Botan command line util
+:Manual section: 1
+
+ """.strip()
+
+ with open(rst2man_file, 'w') as f:
+ f.write(rst2man_header)
+ f.write("\n")
+ for line in cli_doc_contents:
+ f.write(line)
+
+ logging.info('Botan %s (revision %s) (%s %s) build setup is complete' % (
+ Version.as_string(),
+ Version.vc_rev(),
+ Version.release_type(),
+ ('dated %d' % (Version.datestamp())) if Version.datestamp() != 0 else 'undated'))
+
+ if options.unsafe_fuzzer_mode:
+ logging.warning("The fuzzer mode flag is labeled unsafe for a reason, this version is for testing only")
+
+def list_os_features(all_os_features, info_os):
+ for feat in all_os_features:
+ os_with_feat = [o for o in info_os.keys() if feat in info_os[o].target_features]
+ os_without_feat = [o for o in info_os.keys() if feat not in info_os[o].target_features]
+
+ if len(os_with_feat) < len(os_without_feat):
+ print("%s: %s" % (feat, ' '.join(sorted(os_with_feat))))
+ else:
+ print("%s: %s" % (feat, '!' + ' !'.join(sorted(os_without_feat))))
+ return 0
+
+
+def main(argv):
+ """
+ Main driver
+ """
+
+ # pylint: disable=too-many-locals,too-many-statements
+
+ options = process_command_line(argv[1:])
+
+ setup_logging(options)
+
+ source_paths = SourcePaths(os.path.dirname(argv[0]))
+
+ info_modules = load_info_files(source_paths.lib_dir, 'Modules', "info.txt", ModuleInfo)
+
+ if options.list_modules:
+ for mod in sorted(info_modules.keys()):
+ print(mod)
+ return 0
+
+ info_arch = load_build_data_info_files(source_paths, 'CPU info', 'arch', ArchInfo)
+ info_os = load_build_data_info_files(source_paths, 'OS info', 'os', OsInfo)
+ info_cc = load_build_data_info_files(source_paths, 'compiler info', 'cc', CompilerInfo)
+ info_module_policies = load_build_data_info_files(source_paths, 'module policy', 'policy', ModulePolicyInfo)
+
+ all_os_features = sorted(set(flatten([o.target_features for o in info_os.values()])))
+ all_defined_isas = set(flatten([a.isa_extensions for a in info_arch.values()]))
+
+ if options.list_os_features:
+ return list_os_features(all_os_features, info_os)
+
+ for mod in info_modules.values():
+ mod.cross_check(info_arch, info_cc, all_os_features, all_defined_isas)
+
+ for cc in info_cc.values():
+ cc.cross_check(info_os, info_arch, all_defined_isas)
+
+ for policy in info_module_policies.values():
+ policy.cross_check(info_modules)
+
+ logging.info('%s invoked with options "%s"', argv[0], ' '.join(argv[1:]))
+ logging.info('Configuring to build Botan %s (revision %s)' % (
+ Version.as_string(), Version.vc_rev()))
+ logging.info('Running under %s', sys.version.replace('\n', ''))
+
+ take_options_from_env(options)
+
+ logging.info('Autodetected platform information: OS="%s" machine="%s" proc="%s"',
+ platform.system(), platform.machine(), platform.processor())
+
+ logging.debug('Known CPU names: ' + ' '.join(
+ sorted(flatten([[ainfo.basename] + ainfo.aliases for ainfo in info_arch.values()]))))
+
+ 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 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:
+ cc_min_version = options.cc_min_version or "0.0"
+
+ 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'
+ elif 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
+
+ options.with_endian = choose_endian(arch, options)
+
+ chooser = ModulesChooser(info_modules, module_policy, arch, osinfo, cc, cc_min_version, options)
+ loaded_module_names = chooser.choose()
+ using_mods = [info_modules[modname] for modname in loaded_module_names]
+
+ build_paths = BuildPaths(source_paths, options, using_mods)
+ build_paths.public_headers.append(os.path.join(build_paths.build_dir, 'build.h'))
+
+ template_vars = create_template_vars(source_paths, build_paths, options, using_mods, cc, arch, osinfo)
+
+ # Now we start writing to disk
+ do_io_for_build(cc, arch, osinfo, using_mods, build_paths, source_paths, template_vars, options)
+
+ return 0
+
+if __name__ == '__main__':
+ try:
+ sys.exit(main(argv=sys.argv))
+ except UserError as e:
+ logging.debug(traceback.format_exc())
+ logging.error(e)
+ except Exception as e: # pylint: disable=broad-except
+ # error() will stop script, so wrap all information into one call
+ logging.error("""%s
+An internal error occurred.
+
+Don't panic, this is probably not your fault! Please open an issue
+with the entire output at https://github.com/randombit/botan
+
+You'll meet friendly people happy to help!""" % traceback.format_exc())
+
+ sys.exit(0)
diff --git a/comm/third_party/botan/doc/abi.rst b/comm/third_party/botan/doc/abi.rst
new file mode 100644
index 0000000000..faf7bcc44a
--- /dev/null
+++ b/comm/third_party/botan/doc/abi.rst
@@ -0,0 +1,21 @@
+
+ABI Stability
+====================
+
+Botan uses semantic versioning for the API; if API features are added the minor
+version increases, whereas if API compatibility breaks occur the major version
+is increased.
+
+However no guarantees about ABI are made between releases. Maintaining an ABI
+compatible release in a complex C++ API is exceedingly expensive in development
+time; just adding a single member variable or virtual function is enough to
+cause ABI issues.
+
+If ABI changes, the soname revision will increase to prevent applications from
+linking against a potentially incompatible version at runtime.
+
+If you are concerned about long-term ABI issues, considering using the C API
+instead; this subset *is* ABI stable.
+
+You can review a report on ABI changes to Botan at
+https://abi-laboratory.pro/tracker/timeline/botan/
diff --git a/comm/third_party/botan/doc/api_ref/bigint.rst b/comm/third_party/botan/doc/api_ref/bigint.rst
new file mode 100644
index 0000000000..364844fb53
--- /dev/null
+++ b/comm/third_party/botan/doc/api_ref/bigint.rst
@@ -0,0 +1,279 @@
+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``.
+
+.. cpp:class:: BigInt
+
+ .. cpp:function:: BigInt()
+
+ Create a BigInt with value zero
+
+ .. cpp:function:: BigInt(uint64_t n)
+
+ Create a BigInt with value *n*
+
+ .. cpp:function:: BigInt(const std::string& str)
+
+ Create a BigInt from a string. By default decimal is expected. With an 0x
+ prefix instead it is treated as hexadecimal.
+
+ .. cpp:function:: BigInt(const uint8_t buf[], size_t length)
+
+ Create a BigInt from a binary array (big-endian encoding).
+
+ .. cpp:function:: BigInt(RandomNumberGenerator& rng, size_t bits, bool set_high_bit = true)
+
+ Create a random BigInt of the specified size.
+
+ .. cpp:function:: BigInt operator+(const BigInt& x, const BigInt& y)
+
+ Add ``x`` and ``y`` and return result.
+
+ .. cpp:function:: BigInt operator+(const BigInt& x, word y)
+
+ Add ``x`` and ``y`` and return result.
+
+ .. cpp:function:: BigInt operator+(word x, const BigInt& y)
+
+ Add ``x`` and ``y`` and return result.
+
+ .. cpp:function:: BigInt operator-(const BigInt& x, const BigInt& y)
+
+ Subtract ``y`` from ``x`` and return result.
+
+ .. cpp:function:: BigInt operator-(const BigInt& x, word y)
+
+ Subtract ``y`` from ``x`` and return result.
+
+ .. cpp:function:: BigInt operator*(const BigInt& x, const BigInt& y)
+
+ Multiply ``x`` and ``y`` and return result.
+
+ .. cpp:function:: BigInt operator/(const BigInt& x, const BigInt& y)
+
+ Divide ``x`` by ``y`` and return result.
+
+ .. cpp:function:: BigInt operator%(const BigInt& x, const BigInt& y)
+
+ Divide ``x`` by ``y`` and return remainder.
+
+ .. cpp:function:: word operator%(const BigInt& x, word y)
+
+ Divide ``x`` by ``y`` and return remainder.
+
+ .. cpp:function:: word operator<<(const BigInt& x, size_t n)
+
+ Left shift ``x`` by ``n`` and return result.
+
+ .. cpp:function:: word operator>>(const BigInt& x, size_t n)
+
+ Right shift ``x`` by ``n`` and return result.
+
+ .. cpp:function:: BigInt& operator+=(const BigInt& y)
+
+ Add y to ``*this``
+
+ .. cpp:function:: BigInt& operator+=(word y)
+
+ Add y to ``*this``
+
+ .. cpp:function:: BigInt& operator-=(const BigInt& y)
+
+ Subtract y from ``*this``
+
+ .. cpp:function:: BigInt& operator-=(word y)
+
+ Subtract y from ``*this``
+
+ .. cpp:function:: BigInt& operator*=(const BigInt& y)
+
+ Multiply ``*this`` with y
+
+ .. cpp:function:: BigInt& operator*=(word y)
+
+ Multiply ``*this`` with y
+
+ .. cpp:function:: BigInt& operator/=(const BigInt& y)
+
+ Divide ``*this`` by y
+
+ .. cpp:function:: BigInt& operator%=(const BigInt& y)
+
+ Divide ``*this`` by y and set ``*this`` to the remainder.
+
+ .. cpp:function:: word operator%=(word y)
+
+ Divide ``*this`` by y and set ``*this`` to the remainder.
+
+ .. cpp:function:: word operator<<=(size_t shift)
+
+ Left shift ``*this`` by *shift* bits
+
+ .. cpp:function:: word operator>>=(size_t shift)
+
+ Right shift ``*this`` by *shift* bits
+
+ .. cpp:function:: BigInt& operator++()
+
+ Increment ``*this`` by 1
+
+ .. cpp:function:: BigInt& operator--()
+
+ Decrement ``*this`` by 1
+
+ .. cpp:function:: BigInt operator++(int)
+
+ Postfix increment ``*this`` by 1
+
+ .. cpp:function:: BigInt operator--(int)
+
+ Postfix decrement ``*this`` by 1
+
+ .. cpp:function:: BigInt operator-() const
+
+ Negation operator
+
+ .. cpp:function:: bool operator !() const
+
+ Return true unless ``*this`` is zero
+
+ .. cpp:function:: void clear()
+
+ 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
+
+ Return number of bits need to represent value of ``*this``
+
+ .. cpp:function:: bool is_even() const
+
+ Return true if ``*this`` is even
+
+ .. cpp:function:: bool is_odd() const
+
+ Return true if ``*this`` is odd
+
+ .. cpp:function:: bool is_nonzero() const
+
+ Return true if ``*this`` is not zero
+
+ .. cpp:function:: bool is_zero() const
+
+ 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
+
+ .. cpp:function:: bool is_positive() const
+
+ Return true if ``*this`` is negative
+
+ .. cpp:function:: BigInt abs() const
+
+ Return absolute value of ``*this``
+
+ .. cpp:function:: void binary_encode(uint8_t buf[]) const
+
+ Encode this BigInt as a big-endian integer. The sign is ignored.
+
+ .. cpp:function:: void binary_encode(uint8_t buf[], size_t len) const
+
+ Encode this BigInt as a big-endian integer. The sign is ignored.
+ If ``len`` is less than ``bytes()`` then only the low ``len``
+ bytes are output. If ``len`` is greater than ``bytes()`` then
+ the output is padded with leading zeros.
+
+ .. cpp:function:: void binary_decode(uint8_t buf[])
+
+ Decode this BigInt as a big-endian integer.
+
+ .. 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.
+
+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 --git a/comm/third_party/botan/doc/api_ref/block_cipher.rst b/comm/third_party/botan/doc/api_ref/block_cipher.rst
new file mode 100644
index 0000000000..fc2b26e1d3
--- /dev/null
+++ b/comm/third_party/botan/doc/api_ref/block_cipher.rst
@@ -0,0 +1,364 @@
+Block Ciphers
+=======================
+
+Block ciphers are a n-bit permutation for some small n, typically 64 or 128
+bits. They are a cryptographic primitive used to generate higher level
+operations such as authenticated encryption.
+
+.. warning::
+
+ In almost all cases, a bare block cipher is not what you should be using.
+ You probably want an authenticated cipher mode instead (see :ref:`cipher_modes`)
+ This interface is used to build higher level operations (such as cipher
+ modes or MACs), or in the very rare situation where ECB is required,
+ eg for compatibility with an existing system.
+
+.. cpp:class:: BlockCipher
+
+ .. cpp:function:: static std::unique_ptr<BlockCipher> create(const std::string& algo_spec, \
+ const std::string& provider = "")
+
+ Create a new block cipher object, or else return null.
+
+ .. cpp:function:: static std::unique_ptr<BlockCipher> create_or_throw(const std::string& algo_spec, \
+ const std::string& provider = "")
+
+ Like ``create``, except instead of returning null an exception is thrown
+ if the cipher is not known.
+
+ .. cpp:function:: void set_key(const uint8_t* key, size_t length)
+
+ This sets the key to the value specified. Most algorithms only accept keys
+ of certain lengths. If you attempt to call ``set_key`` with a key length
+ that is not supported, the exception ``Invalid_Key_Length`` will be
+ thrown.
+
+ In all cases, ``set_key`` must be called on an object before any data
+ processing (encryption, decryption, etc) is done by that object. If this
+ is not done, an exception will be thrown.
+ thrown.
+
+ .. cpp:function:: bool valid_keylength(size_t length) const
+
+ This function returns true if and only if *length* is a valid keylength for
+ this algorithm.
+
+ .. cpp:function:: size_t minimum_keylength() const
+
+ Return the smallest key length (in bytes) that is acceptable for the
+ algorithm.
+
+ .. cpp:function:: size_t maximum_keylength() const
+
+ Return the largest key length (in bytes) that is acceptable for the
+ algorithm.
+
+ .. cpp:function:: std::string name() const
+
+ Return a human readable name for this algorithm. This is guaranteed to round-trip with
+ ``create`` and ``create_or_throw`` calls, ie create("Foo")->name() == "Foo"
+
+ .. cpp:function:: void clear()
+
+ Zero out the key. The key must be reset before the cipher object can be used.
+
+ .. cpp:function:: BlockCipher* clone() const
+
+ Return a newly allocated BlockCipher object of the same type as this one.
+
+ .. cpp:function:: size_t block_size() const
+
+ Return the size (in *bytes*) of the cipher.
+
+ .. cpp:function:: size_t parallelism() const
+
+ Return the parallelism underlying this implementation of the cipher. This
+ value can vary across versions and machines. A return value of N means that
+ encrypting or decrypting with N blocks can operate in parallel.
+
+ .. cpp:function:: size_t parallel_bytes() const
+
+ Returns ``parallelism`` multiplied by the block size as well as a small
+ fudge factor. That's because even ciphers that have no implicit parallelism
+ typically see a small speedup for being called with several blocks due to
+ caching effects.
+
+ .. cpp:function:: std::string provider() const
+
+ Return the provider type. Default value is "base" but can be any arbitrary string.
+ Other example values are "sse2", "avx2", "openssl".
+
+ .. cpp:function:: void encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+
+ Encrypt *blocks* blocks of data, taking the input from the array *in* and
+ placing the ciphertext into *out*. The two pointers may be identical, but
+ should not overlap ranges.
+
+ .. cpp:function:: void decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+
+ Decrypt *blocks* blocks of data, taking the input from the array *in* and
+ placing the plaintext into *out*. The two pointers may be identical, but
+ should not overlap ranges.
+
+ .. cpp:function:: void encrypt(const uint8_t in[], uint8_t out[]) const
+
+ Encrypt a single block. Equivalent to :cpp:func:`encrypt_n`\ (in, out, 1).
+
+ .. cpp:function:: void encrypt(uint8_t block[]) const
+
+ Encrypt a single block. Equivalent to :cpp:func:`encrypt_n`\ (block, block, 1)
+
+ .. cpp:function:: void decrypt(const uint8_t in[], uint8_t out[]) const
+
+ Decrypt a single block. Equivalent to :cpp:func:`decrypt_n`\ (in, out, 1)
+
+ .. cpp:function:: void decrypt(uint8_t block[]) const
+
+ Decrypt a single block. Equivalent to :cpp:func:`decrypt_n`\ (block, block, 1)
+
+ .. cpp:function:: template<typename Alloc> void encrypt(std::vector<uint8_t, Alloc>& block) const
+
+ Assumes ``block`` is of a multiple of the block size.
+
+ .. cpp:function:: template<typename Alloc> void decrypt(std::vector<uint8_t, Alloc>& block) const
+
+ Assumes ``block`` is of a multiple of the block size.
+
+Code Example
+-----------------
+
+For sheer demonstrative purposes, the following code encrypts a provided single
+block of plaintext with AES-256 using two different keys.
+
+.. code-block:: cpp
+
+ #include <botan/block_cipher.h>
+ #include <botan/hex.h>
+ #include <iostream>
+ int main ()
+ {
+ std::vector<uint8_t> key = Botan::hex_decode("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F");
+ std::vector<uint8_t> block = Botan::hex_decode("00112233445566778899AABBCCDDEEFF");
+ std::unique_ptr<Botan::BlockCipher> cipher(Botan::BlockCipher::create("AES-256"));
+ cipher->set_key(key);
+ cipher->encrypt(block);
+ std::cout << std::endl <<cipher->name() << "single block encrypt: " << Botan::hex_encode(block);
+
+ //clear cipher for 2nd encryption with other key
+ cipher->clear();
+ key = Botan::hex_decode("1337133713371337133713371337133713371337133713371337133713371337");
+ cipher->set_key(key);
+ cipher->encrypt(block);
+
+ std::cout << std::endl << cipher->name() << "single block encrypt: " << Botan::hex_encode(block);
+ return 0;
+ }
+
+Available Ciphers
+---------------------
+
+Botan includes a number of block ciphers that are specific to particular countries, as
+well as a few that are included mostly due to their use in specific protocols such as PGP
+but not widely used elsewhere. If you are developing new code and have no particular
+opinion, use AES-256. If you desire an alternative to AES, consider Serpent, SHACAL2 or
+Threefish.
+
+.. warning:: Avoid any 64-bit block cipher in new designs. There are
+ combinatoric issues that affect any 64-bit cipher that render it
+ insecure when large amounts of data are processed.
+
+AES
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Comes in three variants, AES-128, AES-192, and AES-256.
+
+The standard 128-bit block cipher. Many modern platforms offer hardware
+acceleration. However, on platforms without hardware support, AES
+implementations typically are vulnerable to side channel attacks. For x86
+systems with SSSE3 but without AES-NI, Botan has an implementation which avoids
+known side channels.
+
+Available if ``BOTAN_HAS_AES`` is defined.
+
+ARIA
+~~~~~~
+
+South Korean cipher used in industry there. No reason to use it otherwise.
+
+Available if ``BOTAN_HAS_ARIA`` is defined.
+
+Blowfish
+~~~~~~~~~
+
+A 64-bit cipher popular in the pre-AES era. Very slow key setup. Also used (with
+bcrypt) for password hashing.
+
+Available if ``BOTAN_HAS_BLOWFISH`` is defined.
+
+CAST-128
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+A 64-bit cipher, commonly used in OpenPGP.
+
+Available if ``BOTAN_HAS_CAST128`` is defined.
+
+CAST-256
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+A 128-bit cipher that was a contestant in the NIST AES competition.
+Almost never used in practice. Prefer AES or Serpent.
+
+Available if ``BOTAN_HAS_CAST256`` is defined.
+
+.. warning::
+ Support for CAST-256 is deprecated and will be removed in a future major release.
+
+Camellia
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Comes in three variants, Camellia-128, Camellia-192, and Camellia-256.
+
+A Japanese design standardized by ISO, NESSIE and CRYPTREC.
+Rarely used outside of Japan.
+
+Available if ``BOTAN_HAS_CAMELLIA`` is defined.
+
+Cascade
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Creates a block cipher cascade, where each block is encrypted by two ciphers
+with independent keys. Useful if you're very paranoid. In practice any single
+good cipher (such as Serpent, SHACAL2, or AES-256) is more than sufficient.
+
+Available if ``BOTAN_HAS_CASCADE`` is defined.
+
+DES, 3DES, DESX
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Originally designed by IBM and NSA in the 1970s. Today, DES's 56-bit key renders
+it insecure to any well-resourced attacker. DESX and 3DES extend the key length,
+and are still thought to be secure, modulo the limitation of a 64-bit block.
+All are somewhat common in some industries such as finance. Avoid in new code.
+
+.. warning::
+ Support for DESX is deprecated and it will be removed in a future major release.
+
+Available if ``BOTAN_HAS_DES`` is defined.
+
+GOST-28147-89
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Aka "Magma". An old 64-bit Russian cipher. Possible security issues, avoid
+unless compatibility is needed.
+
+Available if ``BOTAN_HAS_GOST_28147_89`` is defined.
+
+.. warning::
+ Support for this cipher is deprecated and will be removed in a future major release.
+
+IDEA
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+An older but still unbroken 64-bit cipher with a 128-bit key. Somewhat common
+due to its use in PGP. Avoid in new designs.
+
+Available if ``BOTAN_HAS_IDEA`` is defined.
+
+Kasumi
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+A 64-bit cipher used in 3GPP mobile phone protocols. There is no reason to use
+it outside of this context.
+
+Available if ``BOTAN_HAS_KASUMI`` is defined.
+
+.. warning::
+ Support for Kasumi is deprecated and will be removed in a future major release.
+
+Lion
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+A "block cipher construction" which can encrypt blocks of nearly arbitrary
+length. Built from a stream cipher and a hash function. Useful in certain
+protocols where being able to encrypt large or arbitrary length blocks is
+necessary.
+
+Available if ``BOTAN_HAS_LION`` is defined.
+
+MISTY1
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+A 64-bit Japanese cipher standardized by NESSIE and ISO. Seemingly secure, but
+quite slow and saw little adoption. No reason to use it in new code.
+
+Available if ``BOTAN_HAS_MISTY1`` is defined.
+
+.. warning::
+ Support for MISTY1 is deprecated and will be removed in a future major release.
+
+Noekeon
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+A fast 128-bit cipher by the designers of AES. Easily secured against side
+channels.
+
+Available if ``BOTAN_HAS_NOEKEON`` is defined.
+
+.. warning::
+ Support for Noekeon is deprecated and will be removed in a future major release.
+
+SEED
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+A older South Korean cipher, widely used in industry there. No reason to choose it otherwise.
+
+Available if ``BOTAN_HAS_SEED`` is defined.
+
+SHACAL2
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The 256-bit block cipher used inside SHA-256. Accepts up to a 512-bit key.
+Fast, especially when SIMD or SHA-2 acceleration instructions are available.
+Standardized by NESSIE but otherwise obscure.
+
+Available if ``BOTAN_HAS_SHACAL2`` is defined.
+
+SM4
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+A 128-bit Chinese national cipher, required for use in certain commercial
+applications in China. Quite slow. Probably no reason to use it outside of legal
+requirements.
+
+Available if ``BOTAN_HAS_SM4`` is defined.
+
+Serpent
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+An AES contender. Widely considered the most conservative design. Fairly slow
+unless SIMD instructions are available.
+
+Available if ``BOTAN_HAS_SERPENT`` is defined.
+
+Threefish-512
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+A 512-bit tweakable block cipher that was used in the Skein hash function.
+Very fast on 64-bit processors.
+
+Available if ``BOTAN_HAS_THREEFISH_512`` is defined.
+
+Twofish
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+A 128-bit block cipher that was one of the AES finalists. Has a somewhat complicated key
+setup and a "kitchen sink" design.
+
+Available if ``BOTAN_HAS_TWOFISH`` is defined.
+
+XTEA
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+A 64-bit cipher popular for its simple implementation. Avoid in new code.
+
+Available if ``BOTAN_HAS_XTEA`` is defined.
diff --git a/comm/third_party/botan/doc/api_ref/cipher_modes.rst b/comm/third_party/botan/doc/api_ref/cipher_modes.rst
new file mode 100644
index 0000000000..413db70b57
--- /dev/null
+++ b/comm/third_party/botan/doc/api_ref/cipher_modes.rst
@@ -0,0 +1,384 @@
+.. _cipher_modes:
+
+Cipher Modes
+=====================
+
+A block cipher by itself, is only able to securely encrypt a single data block.
+To be able to securely encrypt data of arbitrary length, a mode of operation
+applies the block cipher's single block operation repeatedly to encrypt
+an entire message.
+
+All cipher mode implementations are are derived from the base class
+:cpp:class:`Cipher_Mode`, which is declared in ``botan/cipher_mode.h``.
+
+.. warning::
+ Using an unauthenticted cipher mode without combining it with a
+ :ref:`mac` is insecure. Prefer using an :ref:`aead`.
+
+.. cpp:class:: Cipher_Mode
+
+ .. cpp:function:: void set_key(const uint8_t* key, size_t length)
+
+ Set the symmetric key to be used.
+
+ .. cpp:function:: bool valid_keylength(size_t length) const
+
+ This function returns true if and only if *length* is a valid
+ keylength for the algorithm.
+
+ .. cpp:function:: size_t minimum_keylength() const
+
+ Return the smallest key length (in bytes) that is acceptable for the
+ algorithm.
+
+ .. cpp:function:: size_t maximum_keylength() const
+
+ Return the largest key length (in bytes) that is acceptable for the
+ algorithm.
+
+ .. cpp:function:: size_t default_nonce_length() const
+
+ Return the default (preferable) nonce size for this cipher mode.
+
+ .. cpp:function:: bool valid_nonce_length(size_t nonce_len) const
+
+ Return true if *nonce_len* is a valid length for a nonce with this
+ algorithm.
+
+ .. cpp:function:: bool authenticated() const
+
+ Return true if this cipher mode is authenticated
+
+ .. cpp:function:: size_t tag_size() const
+
+ Return the length in bytes of the authentication tag this algorithm
+ generates. If the mode is not authenticated, this will return 0. If the mode
+ is authenticated, it will return some positive value (typically somewhere
+ between 8 and 16).
+
+ .. cpp:function:: void clear()
+
+ Clear all internal state. The object will act exactly like one which was
+ just allocated.
+
+ .. cpp:function:: void reset()
+
+ Reset all message state. For example if you called :cpp:func:`start_msg`,
+ then :cpp:func:`process` to process some ciphertext, but then encounter an
+ IO error and must abandon the current message, you can call `reset`. The
+ object will retain the key (unlike calling :cpp:func:`clear` which also
+ resets the key) but the nonce and current message state will be erased.
+
+ .. cpp:function:: void start_msg(const uint8_t* nonce, size_t nonce_len)
+
+ Set up for processing a new message. This function must be called with a new
+ random value for each message. For almost all modes (excepting SIV), if the
+ same nonce is ever used twice with the same key, the encryption scheme loses
+ its confidentiality and/or authenticity properties.
+
+ .. cpp:function:: void start(const std::vector<uint8_t> nonce)
+
+ Acts like :cpp:func:`start_msg`\ (nonce.data(), nonce.size()).
+
+ .. cpp:function:: void start(const uint8_t* nonce, size_t nonce_len)
+
+ Acts like :cpp:func:`start_msg`\ (nonce, nonce_len).
+
+ .. cpp:function:: virtual size_t update_granularity() const
+
+ The :cpp:class:`Cipher_Mode` interface requires message processing in multiples of the block size.
+ Returns size of required blocks to update and 1, if the mode can process messages of any length.
+
+ .. cpp:function:: virtual size_t process(uint8_t* msg, size_t msg_len)
+
+ Process msg in place and returns the number of bytes written. *msg* must
+ be a multiple of :cpp:func:`update_granularity`.
+
+ .. cpp:function:: void update(secure_vector<uint8_t>& buffer, size_t offset = 0)
+
+ Continue processing a message in the buffer in place. The passed buffer's
+ size must be a multiple of :cpp:func:`update_granularity`. The first
+ *offset* bytes of the buffer will be ignored.
+
+ .. cpp:function:: size_t minimum_final_size() const
+
+ Returns the minimum size needed for :cpp:func:`finish`.
+
+ .. cpp:function:: void finish(secure_vector<uint8_t>& final_block, size_t offset = 0)
+
+ Finalize the message processing with a final block of at least :cpp:func:`minimum_final_size` size.
+ The first *offset* bytes of the passed final block will be ignored.
+
+Code Example
+---------------------
+
+The following code encrypts the specified plaintext using AES-128/CBC
+with PKCS#7 padding.
+
+.. warning::
+ This example ignores the requirement to authenticate the ciphertext
+
+.. note::
+ Simply replacing the string "AES-128/CBC/PKCS7" string in the example below
+ with "AES-128/GCM" suffices to use authenticated encryption.
+
+.. code-block:: cpp
+
+ #include <botan/rng.h>
+ #include <botan/auto_rng.h>
+ #include <botan/cipher_mode.h>
+ #include <botan/hex.h>
+ #include <iostream>
+
+ int main()
+ {
+ Botan::AutoSeeded_RNG rng;
+
+ const std::string 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 std::vector<uint8_t> key = Botan::hex_decode("2B7E151628AED2A6ABF7158809CF4F3C");
+
+ std::unique_ptr<Botan::Cipher_Mode> enc = Botan::Cipher_Mode::create("AES-128/CBC/PKCS7", Botan::ENCRYPTION);
+ enc->set_key(key);
+
+ //generate fresh nonce (IV)
+ Botan::secure_vector<uint8_t> iv = rng.random_vec(enc->default_nonce_length());
+
+ // Copy input data to a buffer that will be encrypted
+ Botan::secure_vector<uint8_t> pt(plaintext.data(), plaintext.data()+plaintext.length());
+
+ enc->start(iv);
+ enc->finish(pt);
+
+ std::cout << enc->name() << " with iv " << Botan::hex_encode(iv) << " " << Botan::hex_encode(pt) << "\n";
+ return 0;
+ }
+
+
+Available Unauthenticated Cipher Modes
+-----------------------------------------
+
+.. note::
+ CTR and OFB modes are also implemented, but these are treated as
+ :cpp:class:`Stream_Cipher`\s instead.
+
+CBC
+~~~~~~~~~~~~
+
+Available if ``BOTAN_HAS_MODE_CBC`` is defined.
+
+CBC requires the plaintext be padded using a reversible rule. The following
+padding schemes are implemented
+
+PKCS#7 (RFC5652)
+ The last byte in the padded block defines the padding length p, the remaining padding bytes are set to p as well.
+ANSI X9.23
+ The last byte in the padded block defines the padding length, the remaining padding is filled with 0x00.
+OneAndZeros (ISO/IEC 7816-4)
+ The first padding byte is set to 0x80, the remaining padding bytes are set to 0x00.
+
+Ciphertext stealing (CTS) is also implemented. This scheme allows the
+ciphertext to have the same length as the plaintext, however using CTS
+requires the input be at least one full block plus one byte. It is
+also less commonly implemented.
+
+CFB
+~~~~~~~~~~~~
+
+Available if ``BOTAN_HAS_MODE_CFB`` is defined.
+
+CFB uses a block cipher to create a self-synchronizing stream cipher. It is used
+for example in the OpenPGP protocol. There is no reason to prefer it, as it has
+worse performance characteristics than modes such as CTR or CBC.
+
+XTS
+~~~~~~~~~
+
+Available if ``BOTAN_HAS_MODE_XTS`` is defined.
+
+XTS is a mode specialized for encrypting disk or database storage
+where ciphertext expansion is not possible. XTS requires all inputs be
+at least one full block (16 bytes for AES), however for any acceptable
+input length, there is no ciphertext expansion.
+
+.. _aead:
+
+AEAD Mode
+---------------------------
+
+AEAD (Authenticated Encryption with Associated Data) modes provide message
+encryption, message authentication, and the ability to authenticate additional
+data that is not included in the ciphertext (such as a sequence number or
+header). It is a subclass of :cpp:class:`Cipher_Mode`.
+
+.. cpp:class:: AEAD_Mode
+
+ .. cpp:function:: void set_key(const SymmetricKey& key)
+
+ Set the key
+
+ .. cpp:function:: Key_Length_Specification key_spec() const
+
+ Return the key length specification
+
+ .. cpp:function:: void set_associated_data(const uint8_t ad[], size_t ad_len)
+
+ Set any associated data for this message. For maximum portability between
+ different modes, this must be called after :cpp:func:`set_key` and before
+ :cpp:func:`start`.
+
+ If the associated data does not change, it is not necessary to call this
+ function more than once, even across multiple calls to :cpp:func:`start`
+ and :cpp:func:`finish`.
+
+ .. cpp:function:: void start(const uint8_t nonce[], size_t nonce_len)
+
+ Start processing a message, using *nonce* as the unique per-message
+ value. It does not need to be random, simply unique (per key).
+
+ .. warning::
+ With almost all AEADs, if the same nonce is ever used to encrypt two
+ different messages under the same key, all security is lost. If
+ reliably generating unique nonces is difficult in your environment,
+ use SIV mode which retains security even if nonces are repeated.
+
+ .. cpp:function:: void update(secure_vector<uint8_t>& buffer, size_t offset = 0)
+
+ Continue processing a message. The *buffer* is an in/out parameter and
+ may be resized. In particular, some modes require that all input be
+ consumed before any output is produced; with these modes, *buffer* will
+ be returned empty.
+
+ On input, the buffer must be sized in blocks of size
+ :cpp:func:`update_granularity`. For instance if the update granularity
+ was 64, then *buffer* could be 64, 128, 192, ... bytes.
+
+ The first *offset* bytes of *buffer* will be ignored (this allows in
+ place processing of a buffer that contains an initial plaintext header)
+
+ .. cpp:function:: void finish(secure_vector<uint8_t>& buffer, size_t offset = 0)
+
+ Complete processing a message with a final input of *buffer*, which is
+ treated the same as with :cpp:func:`update`. It must contain at least
+ :cpp:func:`final_minimum_size` bytes.
+
+ Note that if you have the entire message in hand, calling finish without
+ ever calling update is both efficient and convenient.
+
+ .. note::
+
+ During decryption, if the supplied authentication tag does not
+ validate, finish will throw an instance of Invalid_Authentication_Tag
+ (aka Integrity_Failure, which was the name for this exception in
+ versions before 2.10, a typedef is included for compatability).
+
+ If this occurs, all plaintext previously output via calls to update
+ must be destroyed and not used in any way that an attacker could
+ observe the effects of. This could be anything from echoing the
+ plaintext back (perhaps in an error message), or by making an external
+ RPC whose destination or contents depend on the plaintext. The only
+ thing you can do is buffer it, and in the event of an invalid tag,
+ erase the previously decrypted content from memory.
+
+ One simply way to assure this could never happen is to never
+ call update, and instead always marshal the entire message
+ into a single buffer and call finish on it when decrypting.
+
+ .. cpp:function:: size_t update_granularity() const
+
+ The AEAD interface requires :cpp:func:`update` be called with blocks of
+ this size. This will be 1, if the mode can process any length inputs.
+
+ .. cpp:function:: size_t final_minimum_size() const
+
+ The AEAD interface requires :cpp:func:`finish` be called with at least
+ this many bytes (which may be zero, or greater than
+ :cpp:func:`update_granularity`)
+
+ .. cpp:function:: bool valid_nonce_length(size_t nonce_len) const
+
+ Returns true if *nonce_len* is a valid nonce length for this scheme. For
+ EAX and GCM, any length nonces are allowed. OCB allows any value between
+ 8 and 15 bytes.
+
+ .. cpp:function:: size_t default_nonce_length() const
+
+ Returns a reasonable length for the nonce, typically either 96
+ bits, or the only supported length for modes which don't
+ support 96 bit nonces.
+
+
+Available AEAD Modes
+-------------------------
+
+If in doubt about what to use, pick ChaCha20Poly1305, AES-256/GCM, or AES-256/SIV.
+Both ChaCha20Poly1305 and AES with GCM are widely implemented. SIV is somewhat
+more obscure (and is slower than either GCM or ChaCha20Poly1305), but has
+excellent security properties.
+
+ChaCha20Poly1305
+~~~~~~~~~~~~~~~~~~
+
+Available if ``BOTAN_HAS_AEAD_CHACHA20_POLY1305`` is defined.
+
+Unlike the other AEADs which are based on block ciphers, this mode is based on
+the ChaCha stream cipher and the Poly1305 authentication code. It is very fast
+on all modern platforms.
+
+ChaCha20Poly1305 supports 64-bit, 96-bit, and (since 2.8) 192-bit nonces. 64-bit nonces
+are the "classic" ChaCha20Poly1305 design. 96-bit nonces are used by the IETF standard
+version of ChaCha20Poly1305. And 192-bit nonces is the XChaCha20Poly1305 construction,
+which is somewhat less common.
+
+For best interop use the IETF version with 96-bit nonces. However 96 bits is small enough
+that it can be dangerous to generate nonces randomly if more than ~ 2^32 messages are
+encrypted under a single key, since if a nonce is ever reused ChaCha20Poly1305 becomes
+insecure. It is better to use a counter for the nonce in this case.
+
+If you are encrypting many messages under a single key and cannot maintain a counter for
+the nonce, prefer XChaCha20Poly1305 since a 192 bit nonce is large enough that randomly
+chosen nonces are extremely unlikely to repeat.
+
+GCM
+~~~~~
+
+Available if ``BOTAN_HAS_AEAD_GCM`` is defined.
+
+NIST standard, commonly used. Requires a 128-bit block cipher. Fairly slow,
+unless hardware support for carryless multiplies is available.
+
+OCB
+~~~~~
+
+Available if ``BOTAN_HAS_AEAD_OCB`` is defined.
+
+A block cipher based AEAD. Supports 128-bit, 256-bit and 512-bit block ciphers.
+This mode is very fast and easily secured against side channels. Adoption has
+been poor because it is patented in the United States, though a license is
+available allowing it to be freely used by open source software.
+
+EAX
+~~~~~
+
+Available if ``BOTAN_HAS_AEAD_EAX`` is defined.
+
+A secure composition of CTR mode and CMAC. Supports 128-bit, 256-bit and 512-bit
+block ciphers.
+
+SIV
+~~~~~~
+
+Available if ``BOTAN_HAS_AEAD_SIV`` is defined.
+
+Requires a 128-bit block cipher. Unlike other AEADs, SIV is "misuse resistant";
+if a nonce is repeated, SIV retains security, with the exception that if the
+same nonce is used to encrypt the same message multiple times, an attacker can
+detect the fact that the message was duplicated (this is simply because if both
+the nonce and the message are reused, SIV will output identical ciphertexts).
+
+CCM
+~~~~~
+
+Available if ``BOTAN_HAS_AEAD_CCM`` is defined.
+
+A composition of CTR mode and CBC-MAC. Requires a 128-bit block cipher. This is
+a NIST standard mode, but that is about all to recommend it. Prefer EAX.
diff --git a/comm/third_party/botan/doc/api_ref/compression.rst b/comm/third_party/botan/doc/api_ref/compression.rst
new file mode 100644
index 0000000000..5637a5a682
--- /dev/null
+++ b/comm/third_party/botan/doc/api_ref/compression.rst
@@ -0,0 +1,90 @@
+Lossless Data Compression
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Some lossless data compression algorithms are available in botan, currently all
+via third party libraries - these include zlib (including deflate and gzip
+formats), bzip2, and lzma. Support for these must be enabled at build time;
+you can check for them using the macros ``BOTAN_HAS_ZLIB``, ``BOTAN_HAS_BZIP2``,
+and ``BOTAN_HAS_LZMA``.
+
+.. note::
+ You should always compress *before* you encrypt, because encryption seeks to
+ hide the redundancy that compression is supposed to try to find and remove.
+
+Compression is done through the ``Compression_Algorithm`` and
+``Decompression_Algorithm`` classes, both defined in `compression.h`
+
+Compression and decompression both work in three stages: starting a
+message (``start``), continuing to process it (``update``), and then
+finally completing processing the stream (``finish``).
+
+.. cpp:class:: Compression_Algorithm
+
+ .. cpp:function:: void start(size_t level)
+
+ Initialize the compression engine. This must be done before calling
+ ``update`` or ``finish``. The meaning of the `level` parameter varies by
+ the algorithm but generally takes a value between 1 and 9, with higher
+ values implying typically better compression from and more memory and/or
+ CPU time consumed by the compression process. The decompressor can always
+ handle input from any compressor.
+
+ .. cpp:function:: void update(secure_vector<uint8_t>& buf, \
+ size_t offset = 0, bool flush = false)
+
+ Compress the material in the in/out parameter ``buf``. The leading
+ ``offset`` bytes of ``buf`` are ignored and remain untouched; this can be
+ useful for ignoring packet headers. If ``flush`` is true, the
+ compression state is flushed, allowing the decompressor to recover the
+ entire message up to this point without having the see the rest of the
+ compressed stream.
+
+ .. cpp::function:: void finish(secure_vector<uint8_t>& buf, size_t offset = 0)
+
+ Finish compressing a message. The ``buf`` and ``offset`` parameters are
+ treated as in ``update``. It is acceptable to call ``start`` followed by
+ ``finish`` with the entire message, without any intervening call to
+ ``update``.
+
+.. cpp:class:: Decompression_Algorithm
+
+ .. cpp:function:: void start()
+
+ Initialize the decompression engine. This must be done before calling
+ ``update`` or ``finish``. No level is provided here; the decompressor
+ can accept input generated by any compression parameters.
+
+ .. cpp:function:: void update(secure_vector<uint8_t>& buf, \
+ size_t offset = 0)
+
+ Decompress the material in the in/out parameter ``buf``. The leading
+ ``offset`` bytes of ``buf`` are ignored and remain untouched; this can be
+ useful for ignoring packet headers.
+
+ This function may throw if the data seems to be invalid.
+
+ .. cpp::function:: void finish(secure_vector<uint8_t>& buf, size_t offset = 0)
+
+ Finish decompressing a message. The ``buf`` and ``offset`` parameters are
+ treated as in ``update``. It is acceptable to call ``start`` followed by
+ ``finish`` with the entire message, without any intervening call to
+ ``update``.
+
+ This function may throw if the data seems to be invalid.
+
+The easiest way to get a compressor is via the functions
+
+.. cpp:function:: Compression_Algorithm* make_compressor(std::string type)
+.. cpp:function:: Decompression_Algorithm* make_decompressor(std::string type)
+
+Supported values for `type` include `zlib` (raw zlib with no checksum),
+`deflate` (zlib's deflate format), `gzip`, `bz2`, and `lzma`. A null pointer
+will be returned if the algorithm is unavailable.
+
+To use a compression algorithm in a `Pipe` use the adapter types
+`Compression_Filter` and `Decompression_Filter` from `comp_filter.h`. The
+constructors of both filters take a `std::string` argument (passed to
+`make_compressor` or `make_decompressor`), the compression filter also takes a
+`level` parameter. Finally both constructors have a parameter `buf_sz` which
+specifies the size of the internal buffer that will be used - inputs will be
+broken into blocks of this size. The default is 4096.
diff --git a/comm/third_party/botan/doc/api_ref/contents.rst b/comm/third_party/botan/doc/api_ref/contents.rst
new file mode 100644
index 0000000000..24fb274808
--- /dev/null
+++ b/comm/third_party/botan/doc/api_ref/contents.rst
@@ -0,0 +1,39 @@
+
+API Reference
+===================
+
+.. toctree::
+ :maxdepth: 1
+
+ versions
+ secmem
+ rng
+ hash
+ block_cipher
+ stream_ciphers
+ message_auth_codes
+ cipher_modes
+ pubkey
+ x509
+ tls
+ credentials_manager
+ bigint
+ kdf
+ pbkdf
+ keywrap
+ passhash
+ cryptobox
+ srp
+ psk_db
+ filters
+ fpe
+ tss
+ ecc
+ compression
+ pkcs11
+ tpm
+ otp
+ roughtime
+ ffi
+ env_vars
+ python
diff --git a/comm/third_party/botan/doc/api_ref/credentials_manager.rst b/comm/third_party/botan/doc/api_ref/credentials_manager.rst
new file mode 100644
index 0000000000..8f78970dbf
--- /dev/null
+++ b/comm/third_party/botan/doc/api_ref/credentials_manager.rst
@@ -0,0 +1,186 @@
+
+Credentials Manager
+==================================================
+
+A ``Credentials_Manager`` is a way to abstract how the application
+stores credentials. The main user is the :doc:`tls` implementation.
+
+.. cpp:class:: Credentials_Manager
+
+ .. cpp:function:: std::vector<Certificate_Store*> \
+ trusted_certificate_authorities( \
+ const std::string& type, \
+ const std::string& context)
+
+ Return the list of certificate stores, each of which is assumed
+ to contain (only) trusted certificate authorities. The
+ ``Credentials_Manager`` retains ownership of the
+ Certificate_Store pointers.
+
+ .. note::
+
+ It would have been a better API to return a vector of
+ ``shared_ptr`` here. This may change in a future major release.
+
+ When *type* is "tls-client", *context* will be the hostname of
+ the server, or empty if the hostname is not known. This allows
+ using a different set of certificate stores in different contexts,
+ for example using the system certificate store unless contacting
+ one particular server which uses a cert issued by an internal CA.
+
+ When *type* is "tls-server", the *context* will again be the
+ hostname of the server, or empty if the client did not send a
+ server name indicator. For TLS servers, these CAs are the ones
+ trusted for signing of client certificates. If you do not want
+ the TLS server to ask for a client cert,
+ ``trusted_certificate_authorities`` should return an empty list
+ for *type* "tls-server".
+
+ The default implementation returns an empty list.
+
+ .. cpp:function:: std::vector<X509_Certificate> find_cert_chain( \
+ const std::vector<std::string>& cert_key_types, \
+ const std::vector<X509_DN>& acceptable_CAs, \
+ const std::string& type, \
+ const std::string& context)
+
+ Return the certificate chain to use to identify ourselves. The
+ ``acceptable_CAs`` parameter gives a list of CAs the peer trusts.
+ This may be empty.
+
+ .. warning::
+ If this function returns a certificate that is not one of the
+ types given in ``cert_key_types`` confusing handshake
+ failures will result.
+
+ .. cpp:function:: std::vector<X509_Certificate> cert_chain( \
+ const std::vector<std::string>& cert_key_types, \
+ const std::string& type, \
+ const std::string& context)
+
+ Return the certificate chain to use to identify ourselves. Starting in
+ 2.5, prefer ``find_cert_chain`` which additionally provides the CA list.
+
+ .. cpp:function:: std::vector<X509_Certificate> cert_chain_single_type( \
+ const std::string& cert_key_type, \
+ const std::string& type, \
+ const std::string& context)
+
+ Return the certificate chain to use to identifier ourselves, if
+ we have one of type *cert_key_type* and we would like to use a
+ certificate in this *type*/*context*.
+
+ .. cpp:function:: Private_Key* private_key_for(const X509_Certificate& cert, \
+ const std::string& type, \
+ const std::string& context)
+
+ Return the private key for this certificate. The *cert* will be
+ the leaf cert of a chain returned previously by ``cert_chain``
+ or ``cert_chain_single_type``.
+
+In versions before 1.11.34, there was an additional function on `Credentials_Manager`
+
+ .. cpp::function:: void verify_certificate_chain( \
+ const std::string& type, \
+ const std::string& hostname, \
+ const std::vector<X509_Certificate>& cert_chain)
+
+This function has been replaced by `TLS::Callbacks::tls_verify_cert_chain`.
+
+SRP Authentication
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+``Credentials_Manager`` contains the hooks used by TLS clients and
+servers for SRP authentication.
+
+.. note::
+
+ Support for TLS-SRP is deprecated, and will be removed in a future
+ major release. When that occurs these APIs will be removed. Prefer
+ instead performing a standard TLS handshake, then perform a PAKE
+ authentication inside of (and cryptographically bound to) the TLS
+ channel.
+
+.. cpp:function:: bool attempt_srp(const std::string& type, \
+ const std::string& context)
+
+ Returns if we should consider using SRP for authentication
+
+.. cpp:function:: std::string srp_identifier(const std::string& type, \
+ const std::string& context)
+
+ Returns the SRP identifier we'd like to use (used by client)
+
+.. cpp:function:: std::string srp_password(const std::string& type, \
+ const std::string& context, \
+ const std::string& identifier)
+
+ Returns the password for *identifier* (used by client)
+
+.. cpp:function:: bool srp_verifier(const std::string& type, \
+ const std::string& context, \
+ const std::string& identifier, \
+ std::string& group_name, \
+ BigInt& verifier, \
+ std::vector<uint8_t>& salt, \
+ bool generate_fake_on_unknown)
+
+ Returns the SRP verifier information for *identifier* (used by server)
+
+Preshared Keys
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+TLS supports the use of pre shared keys for authentication.
+
+.. cpp:function:: SymmetricKey psk(const std::string& type, \
+ const std::string& context, \
+ const std::string& identity)
+
+ Return a symmetric key for use with *identity*
+
+ One important special case for ``psk`` is where *type* is
+ "tls-server", *context* is "session-ticket" and *identity* is an
+ empty string. If a key is returned for this case, a TLS server
+ will offer session tickets to clients who can use them, and the
+ returned key will be used to encrypt the ticket. The server is
+ allowed to change the key at any time (though changing the key
+ means old session tickets can no longer be used for resumption,
+ forcing a full re-handshake when the client next connects). One
+ simple approach to add support for session tickets in your server
+ is to generate a random key the first time ``psk`` is called to
+ retrieve the session ticket key, cache it for later use in the
+ ``Credentials_Manager``, and simply let it be thrown away when the
+ process terminates. See :rfc:`4507` for more information about TLS
+ session tickets.
+
+ A similar special case exists for DTLS cookie verification. In
+ this case *type* will be "tls-server" and *context* is
+ "dtls-cookie-secret". If no key is returned, then DTLS cookies are
+ not used. Similar to the session ticket key, the DTLS cookie
+ secret can be chosen during server startup and rotated at any time
+ with no ill effect.
+
+ .. warning::
+
+ If DTLS cookies are not used then the server is prone to be
+ abused as a DoS amplifier, where the attacker sends a
+ relatively small client hello in a UDP packet with a forged
+ return address, and then the server replies to the victim with
+ several messages that are larger. This not only hides the
+ attackers address from the victim, but increases their
+ effective bandwidth. This is not an issue when using DTLS over
+ SCTP or TCP.
+
+.. cpp:function:: std::string psk_identity_hint(const std::string& type, \
+ const std::string& context)
+
+ Returns an identity hint which may be provided to the client. This
+ can help a client understand what PSK to use.
+
+.. cpp:function:: std::string psk_identity(const std::string& type, \
+ const std::string& context, \
+ const std::string& identity_hint)
+
+ Returns the identity we would like to use given this *type* and
+ *context* and the optional *identity_hint*. Not all servers or
+ protocols will provide a hint.
diff --git a/comm/third_party/botan/doc/api_ref/cryptobox.rst b/comm/third_party/botan/doc/api_ref/cryptobox.rst
new file mode 100644
index 0000000000..dbade88af5
--- /dev/null
+++ b/comm/third_party/botan/doc/api_ref/cryptobox.rst
@@ -0,0 +1,32 @@
+
+Cryptobox
+========================================
+
+Encryption using a passphrase
+----------------------------------------
+
+.. versionadded:: 1.8.6
+
+This is a set of simple routines that encrypt some data using a
+passphrase. There are defined in the header `cryptobox.h`, inside
+namespace `Botan::CryptoBox`.
+
+It generates cipher and MAC keys using 8192 iterations of PBKDF2 with
+HMAC(SHA-512), then encrypts using Serpent in CTR mode and authenticates using a
+HMAC(SHA-512) mac of the ciphertext, truncated to 160 bits.
+
+ .. cpp:function:: std::string encrypt(const uint8_t input[], size_t input_len, \
+ const std::string& passphrase, \
+ RandomNumberGenerator& rng)
+
+ Encrypt the contents using *passphrase*.
+
+ .. cpp:function:: std::string decrypt(const uint8_t input[], size_t input_len, \
+ const std::string& passphrase)
+
+ Decrypts something encrypted with encrypt.
+
+ .. cpp:function:: std::string decrypt(const std::string& input, \
+ const std::string& passphrase)
+
+ Decrypts something encrypted with encrypt.
diff --git a/comm/third_party/botan/doc/api_ref/ecc.rst b/comm/third_party/botan/doc/api_ref/ecc.rst
new file mode 100644
index 0000000000..f522bbe3df
--- /dev/null
+++ b/comm/third_party/botan/doc/api_ref/ecc.rst
@@ -0,0 +1,284 @@
+Elliptic Curve Operations
+============================
+
+In addition to high level operations for signatures, key agreement,
+and message encryption using elliptic curve cryptography, the library
+contains lower level interfaces for performing operations such as
+elliptic curve point multiplication.
+
+Only curves over prime fields are supported.
+
+Many of these functions take a workspace, either a vector of words or
+a vector of BigInts. These are used to minimize memory allocations
+during common operations.
+
+.. warning::
+ You should only use these interfaces if you know what you are doing.
+
+.. cpp:class:: EC_Group
+
+ .. cpp:function:: EC_Group(const OID& oid)
+
+ Initialize an ``EC_Group`` using an OID referencing the curve
+ parameters.
+
+ .. cpp:function:: EC_Group(const std::string& name)
+
+ Initialize an ``EC_Group`` using a name or OID (for example
+ "secp256r1", or "1.2.840.10045.3.1.7")
+
+ .. cpp:function:: EC_Group(const BigInt& p, \
+ const BigInt& a, \
+ const BigInt& b, \
+ const BigInt& base_x, \
+ const BigInt& base_y, \
+ const BigInt& order, \
+ const BigInt& cofactor, \
+ const OID& oid = OID())
+
+ Initialize an elliptic curve group from the relevant parameters. This
+ is used for example to create custom (application-specific) curves.
+
+ .. cpp:function:: EC_Group(const std::vector<uint8_t>& ber_encoding)
+
+ Initialize an ``EC_Group`` by decoding a DER encoded parameter block.
+
+ .. cpp:function:: std::vector<uint8_t> DER_encode(EC_Group_Encoding form) const
+
+ Return the DER encoding of this group.
+
+ .. cpp:function:: std::string PEM_encode() const
+
+ Return the PEM encoding of this group (base64 of DER encoding plus
+ header/trailer).
+
+ .. cpp:function:: bool a_is_minus_3() const
+
+ Return true if the ``a`` parameter is congruent to -3 mod p.
+
+ .. cpp:function:: bool a_is_zero() const
+
+ Return true if the ``a`` parameter is congruent to 0 mod p.
+
+ .. cpp:function:: size_t get_p_bits() const
+
+ Return size of the prime in bits.
+
+ .. cpp:function:: size_t get_p_bytes() const
+
+ Return size of the prime in bytes.
+
+ .. cpp:function:: size_t get_order_bits() const
+
+ Return size of the group order in bits.
+
+ .. cpp:function:: size_t get_order_bytes() const
+
+ Return size of the group order in bytes.
+
+ .. cpp:function:: const BigInt& get_p() const
+
+ Return the prime modulus.
+
+ .. cpp:function:: const BigInt& get_a() const
+
+ Return the ``a`` parameter of the elliptic curve equation.
+
+ .. cpp:function:: const BigInt& get_b() const
+
+ Return the ``b`` parameter of the elliptic curve equation.
+
+ .. cpp:function:: const PointGFp& get_base_point() const
+
+ Return the groups base point element.
+
+ .. cpp:function:: const BigInt& get_g_x() const
+
+ Return the x coordinate of the base point element.
+
+ .. cpp:function:: const BigInt& get_g_y() const
+
+ Return the y coordinate of the base point element.
+
+ .. cpp:function:: const BigInt& get_order() const
+
+ Return the order of the group generated by the base point.
+
+ .. cpp:function:: const BigInt& get_cofactor() const
+
+ Return the cofactor of the curve. In most cases this will be 1.
+
+ .. cpp:function:: BigInt mod_order(const BigInt& x) const
+
+ Reduce argument ``x`` modulo the curve order.
+
+ .. cpp:function:: BigInt inverse_mod_order(const BigInt& x) const
+
+ Return inverse of argument ``x`` modulo the curve order.
+
+ .. cpp:function:: BigInt multiply_mod_order(const BigInt& x, const BigInt& y) const
+
+ Multiply ``x`` and ``y`` and reduce the result modulo the curve order.
+
+ .. cpp:function:: bool verify_public_element(const PointGFp& y) const
+
+ Return true if ``y`` seems to be a valid group element.
+
+ .. cpp:function:: const OID& get_curve_oid() const
+
+ Return the OID used to identify the curve. May be empty.
+
+ .. cpp:function:: PointGFp point(const BigInt& x, const BigInt& y) const
+
+ Create and return a point with affine elements ``x`` and ``y``. Note
+ this function *does not* verify that ``x`` and ``y`` satisfy the curve
+ equation.
+
+ .. cpp:function:: PointGFp point_multiply(const BigInt& x, const PointGFp& pt, const BigInt& y) const
+
+ Multi-exponentiation. Returns base_point*x + pt*y. Not constant time.
+ (Ordinarily used for signature verification.)
+
+ .. cpp:function:: PointGFp blinded_base_point_multiply(const BigInt& k, \
+ RandomNumberGenerator& rng, \
+ std::vector<BigInt>& ws) const
+
+ Return ``base_point*k`` in a way that attempts to resist side channels.
+
+ .. cpp:function:: BigInt blinded_base_point_multiply_x(const BigInt& k, \
+ RandomNumberGenerator& rng, \
+ std::vector<BigInt>& ws) const
+
+ Like `blinded_base_point_multiply` but returns only the x coordinate.
+
+ .. cpp:function:: PointGFp blinded_var_point_multiply(const PointGFp& point, \
+ const BigInt& k, \
+ RandomNumberGenerator& rng, \
+ std::vector<BigInt>& ws) const
+
+ Return ``point*k`` in a way that attempts to resist side channels.
+
+ .. cpp:function:: BigInt random_scalar(RandomNumberGenerator& rng) const
+
+ Return a random scalar (ie an integer between 1 and the group order).
+
+ .. cpp:function:: PointGFp zero_point() const
+
+ Return the zero point (aka the point at infinity).
+
+ .. cpp:function:: PointGFp OS2ECP(const uint8_t bits[], size_t len) const
+
+ Decode a point from the binary encoding. This function verifies that
+ the decoded point is a valid element on the curve.
+
+ .. cpp:function:: bool verify_group(RandomNumberGenerator& rng, bool strong = false) const
+
+ Attempt to verify the group seems valid.
+
+ .. cpp:function:: static const std::set<std::string>& known_named_groups()
+
+ Return a list of known groups, ie groups for which ``EC_Group(name)``
+ will succeed.
+
+.. cpp:class:: PointGFp
+
+ Stores elliptic curve points in Jacobian representation.
+
+ .. cpp:function:: std::vector<uint8_t> encode(PointGFp::Compression_Type format) const
+
+ Encode a point in a way that can later be decoded with `EC_Group::OS2ECP`.
+
+ .. cpp:function:: PointGFp& operator+=(const PointGFp& rhs)
+
+ Point addition.
+
+ .. cpp:function:: PointGFp& operator-=(const PointGFp& rhs)
+
+ Point subtraction.
+
+ .. cpp:function:: PointGFp& operator*=(const BigInt& scalar)
+
+ Point multiplication using Montgomery ladder.
+
+ .. warning::
+ Prefer the blinded functions in ``EC_Group``
+
+ .. cpp:function:: PointGFp& negate()
+
+ Negate this point.
+
+ .. cpp:function:: BigInt get_affine_x() const
+
+ Return the affine ``x`` coordinate of the point.
+
+ .. cpp:function:: BigInt get_affine_y() const
+
+ Return the affine ``y`` coordinate of the point.
+
+ .. cpp:function:: void force_affine()
+
+ Convert the point to its equivalent affine coordinates. Throws
+ if this is the point at infinity.
+
+ .. cpp:function:: static void force_all_affine(std::vector<PointGFp>& points, \
+ secure_vector<word>& ws)
+
+ Force several points to be affine at once. Uses Montgomery's
+ trick to reduce number of inversions required, so this is much
+ faster than calling ``force_affine`` on each point in sequence.
+
+ .. cpp:function:: bool is_affine() const
+
+ Return true if this point is in affine coordinates.
+
+ .. cpp:function:: bool is_zero() const
+
+ Return true if this point is zero (aka point at infinity).
+
+ .. cpp:function:: bool on_the_curve() const
+
+ Return true if this point is on the curve.
+
+ .. cpp:function:: void randomize_repr(RandomNumberGenerator& rng)
+
+ Randomize the point representation.
+
+ .. cpp:function:: bool operator==(const PointGFp& other) const
+
+ Point equality. This compares the affine representations.
+
+ .. cpp:function:: void add(const PointGFp& other, std::vector<BigInt>& workspace)
+
+ Point addition, taking a workspace.
+
+ .. cpp:function:: void add_affine(const PointGFp& other, std::vector<BigInt>& workspace)
+
+ Mixed (Jacobian+affine) addition, taking a workspace.
+
+ .. warning::
+
+ This function assumes that ``other`` is affine, if this is
+ not correct the result will be invalid.
+
+ .. cpp:function:: void mult2(std::vector<BigInt>& workspace)
+
+ Point doubling.
+
+ .. cpp:function:: void mult2i(size_t i, std::vector<BigInt>& workspace)
+
+ Repeated point doubling.
+
+ .. cpp:function:: PointGFp plus(const PointGFp& other, std::vector<BigInt>& workspace) const
+
+ Point addition, returning the result.
+
+ .. cpp:function:: PointGFp double_of(std::vector<BigInt>& workspace) const
+
+ Point doubling, returning the result.
+
+ .. cpp:function:: PointGFp zero() const
+
+ Return the point at infinity
+
+
+
diff --git a/comm/third_party/botan/doc/api_ref/env_vars.rst b/comm/third_party/botan/doc/api_ref/env_vars.rst
new file mode 100644
index 0000000000..221a225453
--- /dev/null
+++ b/comm/third_party/botan/doc/api_ref/env_vars.rst
@@ -0,0 +1,20 @@
+Environment Variables
+======================
+
+Certain environment variables can affect or tune the behavior of the
+library. The variables and their behavior are described here.
+
+* ``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 then it defaults to the number of CPUs available on the system.
+
+* ``BOTAN_MLOCK_POOL_SIZE`` controls the total amount of memory which will be
+ locked in memory using ``mlock`` or ``VirtualLock`` and managed in a memory
+ pool. If set to ``0`` (or indeed any value smaller than the system page size),
+ then the memory pool is disabled.
+
+* ``BOTAN_FFI_PRINT_EXCEPTIONS`` if this variable is set (to any value), then
+ if an exception is caught by the FFI layer, before returning an error code, it
+ will print the text message of the exception to stderr. This is primarily
+ intended for debugging.
+
diff --git a/comm/third_party/botan/doc/api_ref/ffi.rst b/comm/third_party/botan/doc/api_ref/ffi.rst
new file mode 100644
index 0000000000..faee6e23d3
--- /dev/null
+++ b/comm/third_party/botan/doc/api_ref/ffi.rst
@@ -0,0 +1,1203 @@
+
+FFI (C Binding)
+========================================
+
+.. versionadded:: 1.11.14
+
+Botan's ffi module provides a C89 binding intended to be easily usable with other
+language's foreign function interface (FFI) libraries. For instance the included
+Python wrapper uses Python's ``ctypes`` module and the C89 API. This API is of
+course also useful for programs written directly in C.
+
+Code examples can be found in
+`the tests <https://github.com/randombit/botan/blob/master/src/tests/test_ffi.cpp>`_.
+
+Return Codes
+---------------
+
+Almost all functions in the Botan C interface return an ``int`` error code. The
+only exceptions are a handful of functions (like
+:cpp:func:`botan_ffi_api_version`) which cannot fail in any circumstances.
+
+The FFI functions return a non-negative integer (usually 0) to indicate success,
+or a negative integer to represent an error. A few functions (like
+:cpp:func:`botan_block_cipher_block_size`) return positive integers instead of
+zero on success.
+
+The error codes returned in certain error situations may change over time. This
+especially applies to very generic errors like
+:cpp:enumerator:`BOTAN_FFI_ERROR_EXCEPTION_THROWN` and
+:cpp:enumerator:`BOTAN_FFI_ERROR_UNKNOWN_ERROR`. For instance, before 2.8, setting
+an invalid key length resulted in :cpp:enumerator:`BOTAN_FFI_ERROR_EXCEPTION_THROWN`
+but now this is specially handled and returns
+:cpp:enumerator:`BOTAN_FFI_ERROR_INVALID_KEY_LENGTH` instead.
+
+The following enum values are defined in the FFI header:
+
+.. cpp:enumerator:: BOTAN_FFI_SUCCESS = 0
+
+ Generally returned to indicate success
+
+.. cpp:enumerator:: BOTAN_FFI_INVALID_VERIFIER = 1
+
+ Note this value is positive, but still represents an error condition. In
+ indicates that the function completed successfully, but the value provided
+ was not correct. For example :cpp:func:`botan_bcrypt_is_valid` returns this
+ value if the password did not match the hash.
+
+.. cpp:enumerator:: BOTAN_FFI_ERROR_INVALID_INPUT = -1
+
+ The input was invalid. (Currently this error return is not used.)
+
+.. cpp:enumerator:: BOTAN_FFI_ERROR_BAD_MAC = -2
+
+ While decrypting in an AEAD mode, the tag failed to verify.
+
+.. cpp:enumerator:: BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE = -10
+
+ Functions which write a variable amount of space return this if the indicated
+ buffer length was insufficient to write the data. In that case, the output
+ length parameter is set to the size that is required.
+
+.. cpp:enumerator:: BOTAN_FFI_ERROR_EXCEPTION_THROWN = -20
+
+ An exception was thrown while processing this request, but no further
+ details are available.
+
+ .. note::
+
+ If the environment variable ``BOTAN_FFI_PRINT_EXCEPTIONS`` is set to any
+ non-empty value, then any exception which is caught by the FFI layer will
+ first print the exception message to stderr before returning an
+ error. This is sometimes useful for debugging.
+
+.. cpp:enumerator:: BOTAN_FFI_ERROR_OUT_OF_MEMORY = -21
+
+ Memory allocation failed
+
+.. cpp:enumerator:: BOTAN_FFI_ERROR_BAD_FLAG = -30
+
+ A value provided in a `flag` variable was unknown.
+
+.. cpp:enumerator:: BOTAN_FFI_ERROR_NULL_POINTER = -31
+
+ A null pointer was provided as an argument where that is not allowed.
+
+.. cpp:enumerator:: BOTAN_FFI_ERROR_BAD_PARAMETER = -32
+
+ An argument did not match the function.
+
+.. cpp:enumerator:: BOTAN_FFI_ERROR_KEY_NOT_SET = -33
+
+ An object that requires a key normally must be keyed before use (eg before
+ encrypting or MACing data). If this is not done, the operation will fail and
+ return this error code.
+
+.. cpp:enumerator:: BOTAN_FFI_ERROR_INVALID_KEY_LENGTH = -34
+
+ An invalid key length was provided with a call to ``x_set_key``.
+
+.. cpp:enumerator:: BOTAN_FFI_ERROR_NOT_IMPLEMENTED = -40
+
+ This is returned if the functionality is not available for some reason. For
+ example if you call :cpp:func:`botan_hash_init` with a named hash function
+ which is not enabled, this error is returned.
+
+.. cpp:enumerator:: BOTAN_FFI_ERROR_INVALID_OBJECT = -50
+
+ This is used if an object provided did not match the function. For example
+ calling :cpp:func:`botan_hash_destroy` on a ``botan_rng_t`` object will cause
+ this return.
+
+.. cpp:enumerator:: BOTAN_FFI_ERROR_UNKNOWN_ERROR = -100
+
+ Something bad happened, but we are not sure why or how.
+
+Versioning
+----------------------------------------
+
+.. cpp:function:: uint32_t botan_ffi_api_version()
+
+ Returns the version of the currently supported FFI API. This is
+ expressed in the form YYYYMMDD of the release date of this version
+ of the API.
+
+.. cpp:function:: int botan_ffi_supports_api(uint32_t version)
+
+ Returns 0 iff the FFI version specified is supported by this
+ library. Otherwise returns -1. The expression
+ botan_ffi_supports_api(botan_ffi_api_version()) will always
+ evaluate to 0. A particular version of the library may also support
+ other (older) versions of the FFI API.
+
+.. cpp:function:: const char* botan_version_string()
+
+ Returns a free-form string describing the version. The return
+ value is a statically allocated string.
+
+.. cpp:function:: uint32_t botan_version_major()
+
+ Returns the major version of the library
+
+.. cpp:function:: uint32_t botan_version_minor()
+
+ Returns the minor version of the library
+
+.. cpp:function:: uint32_t botan_version_patch()
+
+ Returns the patch version of the library
+
+.. cpp:function:: uint32_t botan_version_datestamp()
+
+ Returns the date this version was released as an integer YYYYMMDD,
+ or 0 if an unreleased version
+
+
+FFI Versions
+^^^^^^^^^^^^^
+
+This maps the FFI API version to the first version of the library that
+supported it.
+
+============== ===================
+FFI Version Supported Starting
+============== ===================
+20191214 2.13.0
+20180713 2.8.0
+20170815 2.3.0
+20170327 2.1.0
+20150515 2.0.0
+============== ===================
+
+Utility Functions
+----------------------------------------
+
+.. const char* botan_error_description(int err)
+
+ Return a string representation of the provided error code. If the error code
+ is unknown, returns the string "Unknown error". The return values are static
+ constant strings and should not be freed.
+
+.. cpp:function:: int botan_same_mem(const uint8_t* x, const uint8_t* y, size_t len)
+
+ Returns 0 if `x[0..len] == y[0..len]`, -1 otherwise.
+
+.. cpp:function:: int botan_hex_encode(const uint8_t* x, size_t len, char* out, uint32_t flags)
+
+ Performs hex encoding of binary data in *x* of size *len* bytes.
+ The output buffer *out* must be of at least *x*2* bytes in size.
+ If *flags* contains ``BOTAN_FFI_HEX_LOWER_CASE``, hex encoding
+ will only contain lower-case letters, upper-case letters otherwise.
+ Returns 0 on success, 1 otherwise.
+
+.. cpp:function:: int botan_hex_decode(const char* hex_str, size_t in_len, uint8_t* out, size_t* out_len)
+
+ Hex decode some data
+
+Random Number Generators
+----------------------------------------
+
+.. cpp:type:: opaque* botan_rng_t
+
+ An opaque data type for a random number generator. Don't mess with it.
+
+.. cpp:function:: int botan_rng_init(botan_rng_t* rng, const char* rng_type)
+
+ Initialize a random number generator object from the given
+ *rng_type*: "system" (or ``nullptr``): ``System_RNG``,
+ "user": ``AutoSeeded_RNG``,
+ "user-threadsafe": serialized ``AutoSeeded_RNG``,
+ "null": ``Null_RNG`` (always fails),
+ "hwrnd" or "rdrand": ``Processor_RNG`` (if available)
+
+.. cpp:function:: int botan_rng_get(botan_rng_t rng, uint8_t* out, size_t out_len)
+
+ Get random bytes from a random number generator.
+
+.. cpp:function:: int botan_rng_reseed(botan_rng_t rng, size_t bits)
+
+ Reseeds the random number generator with *bits* number of bits
+ from the `System_RNG`.
+
+.. cpp:function:: int botan_rng_reseed_from_rng(botan_rng_t rng, botan_rng_t src, size_t bits)
+
+ Reseeds the random number generator with *bits* number of bits
+ taken from the given source RNG.
+
+.. cpp:function:: int botan_rng_add_entropy(botan_rng_t rng, const uint8_t seed[], size_t len)
+
+ Adds the provided seed material to the internal RNG state.
+
+ This call may be ignored by certain RNG instances (such as RDRAND
+ or, on some systems, the system RNG).
+
+.. cpp:function:: int botan_rng_destroy(botan_rng_t rng)
+
+ Destroy the object created by :cpp:func:`botan_rng_init`.
+
+Block Ciphers
+----------------------------------------
+
+.. versionadded:: 2.1.0
+
+This is a 'raw' interface to ECB mode block ciphers. Most applications
+want the higher level cipher API which provides authenticated
+encryption. This API exists as an escape hatch for applications which
+need to implement custom primitives using a PRP.
+
+.. cpp:type:: opaque* botan_block_cipher_t
+
+ An opaque data type for a block cipher. Don't mess with it.
+
+.. cpp:function:: int botan_block_cipher_init(botan_block_cipher_t* bc, const char* cipher_name)
+
+ Create a new cipher mode object, `cipher_name` should be for example "AES-128" or "Threefish-512"
+
+.. cpp:function:: int botan_block_cipher_block_size(botan_block_cipher_t bc)
+
+ Return the block size of this cipher.
+
+.. cpp:function:: int botan_block_cipher_name(botan_block_cipher_t cipher, \
+ char* name, size_t* name_len)
+
+ Return the name of this block cipher algorithm, which may nor may not exactly
+ match what was passed to :cpp:func:`botan_block_cipher_init`.
+
+.. cpp:function:: int botan_block_cipher_get_keyspec(botan_block_cipher_t cipher, \
+ size_t* out_minimum_keylength, \
+ size_t* out_maximum_keylength, \
+ size_t* out_keylength_modulo)
+
+ Return the limits on the key which can be provided to this cipher. If any of the
+ parameters are null, no output is written to that field. This allows retrieving only
+ (say) the maximum supported keylength, if that is the only information needed.
+
+.. cpp:function:: int botan_block_cipher_clear(botan_block_cipher_t bc)
+
+ Clear the internal state (such as keys) of this cipher object, but do not deallocate it.
+
+.. cpp:function:: int botan_block_cipher_set_key(botan_block_cipher_t bc, const uint8_t key[], size_t key_len)
+
+ Set the cipher key, which is required before encrypting or decrypting.
+
+.. cpp:function:: int botan_block_cipher_encrypt_blocks(botan_block_cipher_t bc, const uint8_t in[], uint8_t out[], size_t blocks)
+
+ The key must have been set first with :cpp:func:`botan_block_cipher_set_key`.
+ Encrypt *blocks* blocks of data stored in *in* and place the ciphertext into *out*.
+ The two parameters may be the same buffer, but must not overlap.
+
+.. cpp:function:: int botan_block_cipher_decrypt_blocks(botan_block_cipher_t bc, const uint8_t in[], uint8_t out[], size_t blocks)
+
+ The key must have been set first with :cpp:func:`botan_block_cipher_set_key`.
+ Decrypt *blocks* blocks of data stored in *in* and place the ciphertext into *out*.
+ The two parameters may be the same buffer, but must not overlap.
+
+.. cpp:function:: int botan_block_cipher_destroy(botan_block_cipher_t rng)
+
+ Destroy the object created by :cpp:func:`botan_block_cipher_init`.
+
+
+Hash Functions
+----------------------------------------
+
+.. cpp:type:: opaque* botan_hash_t
+
+ An opaque data type for a hash. Don't mess with it.
+
+.. cpp:function:: botan_hash_t botan_hash_init(const char* hash, uint32_t flags)
+
+ Creates a hash of the given name, e.g., "SHA-384".
+ Returns null on failure. Flags should always be zero in this version of the API.
+
+.. cpp:function:: int botan_hash_destroy(botan_hash_t hash)
+
+ Destroy the object created by :cpp:func:`botan_hash_init`.
+
+.. cpp:function:: int botan_hash_name(botan_hash_t hash, char* name, size_t* name_len)
+
+ Write the name of the hash function to the provided buffer.
+
+.. cpp:function:: int botan_hash_copy_state(botan_hash_t* dest, const botan_hash_t source)
+
+ Copies the state of the hash object to a new hash object.
+
+.. cpp:function:: int botan_hash_clear(botan_hash_t hash)
+
+ Reset the state of this object back to clean, as if no input has
+ been supplied.
+
+.. cpp:function:: size_t botan_hash_output_length(botan_hash_t hash)
+
+ Return the output length of the hash function.
+
+.. cpp:function:: int botan_hash_update(botan_hash_t hash, const uint8_t* input, size_t len)
+
+ Add input to the hash computation.
+
+.. cpp:function:: int botan_hash_final(botan_hash_t hash, uint8_t out[])
+
+ Finalize the hash and place the output in out. Exactly
+ :cpp:func:`botan_hash_output_length` bytes will be written.
+
+Message Authentication Codes
+----------------------------------------
+.. cpp:type:: opaque* botan_mac_t
+
+ An opaque data type for a MAC. Don't mess with it, but do remember
+ to set a random key first.
+
+.. cpp:function:: botan_mac_t botan_mac_init(const char* mac, uint32_t flags)
+
+ Creates a MAC of the given name, e.g., "HMAC(SHA-384)".
+ Returns null on failure. Flags should always be zero in this version of the API.
+
+.. cpp:function:: int botan_mac_destroy(botan_mac_t mac)
+
+ Destroy the object created by :cpp:func:`botan_mac_init`.
+
+.. cpp:function:: int botan_mac_clear(botan_mac_t mac)
+
+ Reset the state of this object back to clean, as if no key and input have
+ been supplied.
+
+.. cpp:function:: size_t botan_mac_output_length(botan_mac_t mac)
+
+ Return the output length of the MAC.
+
+.. cpp:function:: int botan_mac_set_key(botan_mac_t mac, const uint8_t* key, size_t key_len)
+
+ Set the random key.
+
+.. cpp:function:: int botan_mac_update(botan_mac_t mac, uint8_t buf[], size_t len)
+
+ Add input to the MAC computation.
+
+.. cpp:function:: int botan_mac_final(botan_mac_t mac, uint8_t out[], size_t* out_len)
+
+ Finalize the MAC and place the output in out. Exactly
+ :cpp:func:`botan_mac_output_length` bytes will be written.
+
+Symmetric Ciphers
+----------------------------------------
+
+.. cpp:type:: opaque* botan_cipher_t
+
+ An opaque data type for a symmetric cipher object. Don't mess with it, but do remember
+ to set a random key first. And please use an AEAD.
+
+.. cpp:function:: botan_cipher_t botan_cipher_init(const char* cipher_name, uint32_t flags)
+
+ Create a cipher object from a name such as "AES-256/GCM" or "Serpent/OCB".
+
+ Flags is a bitfield; the low bitof ``flags`` specifies if encrypt or decrypt,
+ ie use 0 for encryption and 1 for decryption.
+
+.. cpp:function:: int botan_cipher_destroy(botan_cipher_t cipher)
+
+.. cpp:function:: int botan_cipher_clear(botan_cipher_t hash)
+
+.. cpp:function:: int botan_cipher_set_key(botan_cipher_t cipher, \
+ const uint8_t* key, size_t key_len)
+
+.. cpp:function:: int botan_cipher_is_authenticated(botan_cipher_t cipher)
+
+.. cpp:function:: size_t botan_cipher_get_tag_length(botan_cipher_t cipher, size_t* tag_len)
+
+ Write the tag length of the cipher to ``tag_len``. This will be zero for non-authenticated
+ ciphers.
+
+.. cpp:function:: int botan_cipher_valid_nonce_length(botan_cipher_t cipher, size_t nl)
+
+ Returns 1 if the nonce length is valid, or 0 otherwise. Returns -1 on error (such as
+ the cipher object being invalid).
+
+.. cpp:function:: size_t botan_cipher_get_default_nonce_length(botan_cipher_t cipher, size_t* nl)
+
+ Return the default nonce length
+
+.. cpp:function:: int botan_cipher_set_associated_data(botan_cipher_t cipher, \
+ const uint8_t* ad, size_t ad_len)
+
+ Set associated data. Will fail unless the cipher is an AEAD.
+
+.. cpp:function:: int botan_cipher_start(botan_cipher_t cipher, \
+ const uint8_t* nonce, size_t nonce_len)
+
+ Start processing a message using the provided nonce.
+
+.. cpp:function:: int botan_cipher_update(botan_cipher_t cipher, \
+ uint32_t flags, \
+ uint8_t output[], \
+ size_t output_size, \
+ size_t* output_written, \
+ const uint8_t input_bytes[], \
+ size_t input_size, \
+ size_t* input_consumed)
+
+ Encrypt or decrypt data.
+
+PBKDF
+----------------------------------------
+
+.. cpp:function:: int botan_pbkdf(const char* pbkdf_algo, \
+ uint8_t out[], size_t out_len, \
+ const char* passphrase, \
+ const uint8_t salt[], size_t salt_len, \
+ size_t iterations)
+
+ Derive a key from a passphrase for a number of iterations
+ using the given PBKDF algorithm, e.g., "PBKDF2".
+
+.. cpp:function:: int botan_pbkdf_timed(const char* pbkdf_algo, \
+ uint8_t out[], size_t out_len, \
+ const char* passphrase, \
+ const uint8_t salt[], size_t salt_len, \
+ size_t milliseconds_to_run, \
+ size_t* out_iterations_used)
+
+ Derive a key from a passphrase using the given PBKDF algorithm,
+ e.g., "PBKDF2". If *out_iterations_used* is zero, instead the
+ PBKDF is run until *milliseconds_to_run* milliseconds have passed.
+ In this case, the number of iterations run will be written to
+ *out_iterations_used*.
+
+KDF
+----------------------------------------
+
+.. cpp:function:: int botan_kdf(const char* kdf_algo, \
+ uint8_t out[], size_t out_len, \
+ const uint8_t secret[], size_t secret_len, \
+ const uint8_t salt[], size_t salt_len, \
+ const uint8_t label[], size_t label_len)
+
+ Derive a key using the given KDF algorithm, e.g., "SP800-56C".
+ The derived key of length *out_len* bytes will be placed in *out*.
+
+Multiple Precision Integers
+----------------------------------------
+
+.. versionadded: 2.1.0
+
+.. cpp:type:: opaque* botan_mp_t
+
+ An opaque data type for a multiple precision integer. Don't mess with it.
+
+.. cpp:function:: int botan_mp_init(botan_mp_t* mp)
+
+ Initialize a ``botan_mp_t``. Initial value is zero, use `botan_mp_set_X` to load a value.
+
+.. cpp:function:: int botan_mp_destroy(botan_mp_t mp)
+
+ Free a ``botan_mp_t``
+
+.. 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
+
+.. cpp:function:: int botan_mp_to_str(botan_mp_t mp, uint8_t base, char* out, size_t* out_len)
+
+ Base can be either 10 or 16.
+
+.. cpp:function:: int botan_mp_set_from_int(botan_mp_t mp, int initial_value)
+
+ Set ``botan_mp_t`` from an integer value.
+
+.. cpp:function:: int botan_mp_set_from_mp(botan_mp_t dest, botan_mp_t source)
+
+ Set ``botan_mp_t`` from another MP.
+
+.. cpp:function:: int botan_mp_set_from_str(botan_mp_t dest, const char* str)
+
+ Set ``botan_mp_t`` from a string. Leading prefix of "0x" is accepted.
+
+.. cpp:function:: int botan_mp_num_bits(botan_mp_t n, size_t* bits)
+
+ Return the size of ``n`` in bits.
+
+.. cpp:function:: int botan_mp_num_bytes(botan_mp_t n, size_t* uint8_ts)
+
+ Return the size of ``n`` in bytes.
+
+.. cpp:function:: int botan_mp_to_bin(botan_mp_t mp, uint8_t vec[])
+
+ Writes exactly ``botan_mp_num_bytes(mp)`` to ``vec``.
+
+.. 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``).
+
+.. cpp:function:: int botan_mp_is_negative(botan_mp_t mp)
+
+ Return 1 if ``mp`` is negative, otherwise 0.
+
+.. cpp:function:: int botan_mp_flip_sign(botan_mp_t mp)
+
+ Flip the sign of ``mp``.
+
+.. cpp:function:: int botan_mp_add(botan_mp_t result, botan_mp_t x, botan_mp_t y)
+
+ Add two ``botan_mp_t`` and store the output in ``result``.
+
+.. cpp:function:: int botan_mp_sub(botan_mp_t result, botan_mp_t x, botan_mp_t y)
+
+ Subtract two ``botan_mp_t`` and store the output in ``result``.
+
+.. cpp:function:: int botan_mp_mul(botan_mp_t result, botan_mp_t x, botan_mp_t y)
+
+ Multiply two ``botan_mp_t`` and store the output in ``result``.
+
+.. cpp:function:: int botan_mp_div(botan_mp_t quotient, botan_mp_t remainder, \
+ botan_mp_t x, botan_mp_t y)
+
+ Divide ``x`` by ``y`` and store the output in ``quotient`` and ``remainder``.
+
+.. cpp:function:: int botan_mp_mod_mul(botan_mp_t result, botan_mp_t x, botan_mp_t y, botan_mp_t mod)
+
+ Set ``result`` to ``x`` times ``y`` modulo ``mod``.
+
+.. cpp:function:: int botan_mp_equal(botan_mp_t x, botan_mp_t y)
+
+ Return 1 if ``x`` is equal to ``y``, 0 if ``x`` is not equal to ``y``
+
+.. cpp:function:: int botan_mp_is_zero(const botan_mp_t x)
+
+ Return 1 if ``x`` is equal to zero, otherwise 0.
+
+.. cpp:function:: int botan_mp_is_odd(const botan_mp_t x)
+
+ Return 1 if ``x`` is odd, otherwise 0.
+
+.. cpp:function:: int botan_mp_is_even(const botan_mp_t x)
+
+ Return 1 if ``x`` is even, otherwise 0.
+
+.. cpp:function:: int botan_mp_is_positive(const botan_mp_t x)
+
+ Return 1 if ``x`` is greater than or equal to zero.
+
+.. cpp:function:: int botan_mp_is_negative(const botan_mp_t x)
+
+ Return 1 if ``x`` is less than zero.
+
+.. cpp:function:: int botan_mp_to_uint32(const botan_mp_t x, uint32_t* val)
+
+ If x fits in a 32-bit integer, set val to it and return 0. If x is out of
+ range an error is returned.
+
+.. cpp:function:: int botan_mp_cmp(int* result, botan_mp_t x, botan_mp_t y)
+
+ Three way comparison: set result to -1 if ``x`` is less than ``y``,
+ 0 if ``x`` is equal to ``y``, and 1 if ``x`` is greater than ``y``.
+
+.. cpp:function:: int botan_mp_swap(botan_mp_t x, botan_mp_t y)
+
+ Swap two ``botan_mp_t`` values.
+
+.. cpp:function:: int botan_mp_powmod(botan_mp_t out, botan_mp_t base, botan_mp_t exponent, botan_mp_t modulus)
+
+ Modular exponentiation.
+
+.. cpp:function:: int botan_mp_lshift(botan_mp_t out, botan_mp_t in, size_t shift)
+
+ Left shift by specified bit count, place result in ``out``.
+
+.. cpp:function:: int botan_mp_rshift(botan_mp_t out, botan_mp_t in, size_t shift)
+
+ Right shift by specified bit count, place result in ``out``.
+
+.. cpp:function:: int botan_mp_mod_inverse(botan_mp_t out, botan_mp_t in, botan_mp_t modulus)
+
+ Compute modular inverse. If no modular inverse exists (for instance because ``in`` and
+ ``modulus`` are not relatively prime), then sets ``out`` to -1.
+
+.. cpp:function:: int botan_mp_rand_bits(botan_mp_t rand_out, botan_rng_t rng, size_t bits)
+
+ Create a random ``botan_mp_t`` of the specified bit size.
+
+.. cpp:function:: int botan_mp_rand_range(botan_mp_t rand_out, botan_rng_t rng, \
+ botan_mp_t lower_bound, botan_mp_t upper_bound)
+
+ Create a random ``botan_mp_t`` within the provided range.
+
+.. cpp:function:: int botan_mp_gcd(botan_mp_t out, botan_mp_t x, botan_mp_t y)
+
+ Compute the greatest common divisor of ``x`` and ``y``.
+
+.. cpp:function:: int botan_mp_is_prime(botan_mp_t n, botan_rng_t rng, size_t test_prob)
+
+ Test if ``n`` is prime. The algorithm used (Miller-Rabin) is probabilistic,
+ set ``test_prob`` to the desired assurance level. For example if
+ ``test_prob`` is 64, then sufficient Miller-Rabin iterations will run to
+ assure there is at most a ``1/2**64`` chance that ``n`` is composite.
+
+.. cpp:function:: int botan_mp_get_bit(botan_mp_t n, size_t bit)
+
+ Returns 0 if the specified bit of ``n`` is not set, 1 if it is set.
+
+.. cpp:function:: int botan_mp_set_bit(botan_mp_t n, size_t bit)
+
+ Set the specified bit of ``n``
+
+.. cpp:function:: int botan_mp_clear_bit(botan_mp_t n, size_t bit)
+
+ Clears the specified bit of ``n``
+
+
+Password Hashing
+----------------------------------------
+
+.. cpp:function:: int botan_bcrypt_generate(uint8_t* out, size_t* out_len, \
+ const char* password, \
+ botan_rng_t rng, \
+ size_t work_factor, \
+ uint32_t flags)
+
+ Create a password hash using Bcrypt.
+ The output buffer *out* should be of length 64 bytes.
+ The output is formatted bcrypt $2a$...
+
+.. cpp:function:: int botan_bcrypt_is_valid(const char* pass, const char* hash)
+
+ Check a previously created password hash. Returns
+ :cpp:enumerator:`BOTAN_SUCCESS` if if this password/hash
+ combination is valid, :cpp:enumerator:`BOTAN_FFI_INVALID_VERIFIER`
+ if the combination is not valid (but otherwise well formed),
+ negative on error.
+
+Public Key Creation, Import and Export
+----------------------------------------
+
+.. cpp:type:: opaque* botan_privkey_t
+
+ An opaque data type for a private key. Don't mess with it.
+
+.. cpp:function:: int botan_privkey_create(botan_privkey_t* key, \
+ const char* algo_name, \
+ const char* algo_params, \
+ 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
+
+.. cpp:function:: int botan_privkey_create_ecdsa(botan_privkey_t* key, botan_rng_t rng, const char* curve)
+
+ Create a ECDSA key of using a named curve
+
+.. cpp:function:: int botan_privkey_create_ecdh(botan_privkey_t* key, botan_rng_t rng, const char* curve)
+
+ Create a ECDH key of using a named curve
+
+.. cpp:function:: int botan_privkey_create_mceliece(botan_privkey_t* key, botan_rng_t rng, size_t n, size_t t)
+
+ Create a McEliece key using the specified parameters. See
+ :ref:`mceliece` for details on choosing parameters.
+
+.. cpp:function:: int botan_privkey_create_dh(botan_privkey_t* key, botan_rng_t rng, const char* params)
+
+ Create a finite field Diffie-Hellman key using the specified named group, for example
+ "modp/ietf/3072".
+
+.. cpp:function:: int botan_privkey_load(botan_privkey_t* key, botan_rng_t rng, \
+ const uint8_t bits[], size_t len, \
+ const char* password)
+
+ Load a private key. If the key is encrypted, ``password`` will be
+ used to attempt decryption.
+
+.. cpp:function:: int botan_privkey_destroy(botan_privkey_t key)
+
+ Destroy the object.
+
+.. cpp:function:: int botan_privkey_export(botan_privkey_t key, \
+ uint8_t out[], size_t* out_len, \
+ uint32_t flags)
+
+ Export a public key. If flags is 1 then PEM format is used.
+
+.. cpp:function:: int botan_privkey_export_encrypted(botan_privkey_t key, \
+ uint8_t out[], size_t* out_len, \
+ botan_rng_t rng, \
+ const char* passphrase, \
+ const char* encryption_algo, \
+ uint32_t flags)
+
+ 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,
+ uint8_t out[], size_t* out_len, \
+ botan_rng_t rng, \
+ const char* passphrase, \
+ uint32_t pbkdf_msec_runtime, \
+ size_t* pbkdf_iterations_out, \
+ const char* cipher_algo, \
+ const char* pbkdf_algo, \
+ uint32_t flags);
+
+ Encrypt a key, running the key derivation function for ``pbkdf_msec_runtime`` milliseconds.
+ Returns the number of iterations used in ``pbkdf_iterations_out``.
+
+ ``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, \
+ uint8_t out[], size_t* out_len, \
+ botan_rng_t rng, \
+ const char* passphrase, \
+ size_t pbkdf_iterations, \
+ const char* cipher_algo, \
+ const char* pbkdf_algo, \
+ uint32_t flags);
+
+ Encrypt a private key. The PBKDF function runs for the specified number of iterations.
+ At least 100,000 is recommended.
+
+.. cpp:function:: int botan_privkey_export_pubkey(botan_pubkey_t* out, botan_privkey_t in)
+
+.. cpp:function:: int botan_privkey_get_field(botan_mp_t output, \
+ botan_privkey_t key, \
+ const char* field_name)
+
+ 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:type:: opaque* botan_pubkey_t
+
+ An opaque data type for a public key. Don't mess with it.
+
+.. cpp:function:: int botan_pubkey_load(botan_pubkey_t* key, const uint8_t bits[], size_t len)
+
+.. cpp:function:: int botan_pubkey_export(botan_pubkey_t key, uint8_t out[], size_t* out_len, uint32_t flags)
+
+.. cpp:function:: int botan_pubkey_algo_name(botan_pubkey_t key, char out[], size_t* out_len)
+
+.. cpp:function:: int botan_pubkey_estimated_strength(botan_pubkey_t key, size_t* estimate)
+
+.. cpp:function:: int botan_pubkey_fingerprint(botan_pubkey_t key, const char* hash, \
+ uint8_t out[], size_t* out_len)
+
+.. cpp:function:: int botan_pubkey_destroy(botan_pubkey_t key)
+
+.. cpp:function:: int botan_pubkey_get_field(botan_mp_t output, \
+ botan_pubkey_t key, \
+ const char* field_name)
+
+ 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.
+
+RSA specific functions
+----------------------------------------
+
+.. cpp:function:: int botan_privkey_rsa_get_p(botan_mp_t p, botan_privkey_t rsa_key)
+
+ Set ``p`` to the first RSA prime.
+
+.. cpp:function:: int botan_privkey_rsa_get_q(botan_mp_t q, botan_privkey_t rsa_key)
+
+ Set ``q`` to the second RSA prime.
+
+.. cpp:function:: int botan_privkey_rsa_get_d(botan_mp_t d, botan_privkey_t rsa_key)
+
+ Set ``d`` to the RSA private exponent.
+
+.. cpp:function:: int botan_privkey_rsa_get_n(botan_mp_t n, botan_privkey_t rsa_key)
+
+ Set ``n`` to the RSA modulus.
+
+.. cpp:function:: int botan_privkey_rsa_get_e(botan_mp_t e, botan_privkey_t rsa_key)
+
+ Set ``e`` to the RSA public exponent.
+
+.. cpp:function:: int botan_pubkey_rsa_get_e(botan_mp_t e, botan_pubkey_t rsa_key)
+
+ Set ``e`` to the RSA public exponent.
+
+.. cpp:function:: int botan_pubkey_rsa_get_n(botan_mp_t n, botan_pubkey_t rsa_key)
+
+ Set ``n`` to the RSA modulus.
+
+.. cpp:function:: int botan_privkey_load_rsa(botan_privkey_t* key, \
+ botan_mp_t p, botan_mp_t q, botan_mp_t e)
+
+ Initialize a private RSA key using parameters p, q, and e.
+
+.. cpp:function:: int botan_pubkey_load_rsa(botan_pubkey_t* key, \
+ botan_mp_t n, botan_mp_t e)
+
+ Initialize a public RSA key using parameters n and e.
+
+DSA specific functions
+----------------------------------------
+
+.. cpp:function:: 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)
+
+ Initialize a private DSA key using group parameters p, q, and g and private key x.
+
+.. cpp:function:: 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)
+
+ Initialize a private DSA key using group parameters p, q, and g and public key y.
+
+ElGamal specific functions
+----------------------------------------
+
+.. cpp:function:: int botan_privkey_load_elgamal(botan_privkey_t* key, \
+ botan_mp_t p, botan_mp_t g, botan_mp_t x)
+
+ Initialize a private ElGamal key using group parameters p and g and private key x.
+
+.. cpp:function:: int botan_pubkey_load_elgamal(botan_pubkey_t* key, \
+ botan_mp_t p, botan_mp_t g, botan_mp_t y)
+
+ Initialize a public ElGamal key using group parameters p and g and public key y.
+
+Diffie-Hellman specific functions
+----------------------------------------
+
+.. cpp:function:: int botan_privkey_load_dh(botan_privkey_t* key, \
+ botan_mp_t p, botan_mp_t g, botan_mp_t x)
+
+ Initialize a private Diffie-Hellman key using group parameters p and g and private key x.
+
+.. cpp:function:: int botan_pubkey_load_dh(botan_pubkey_t* key, \
+ botan_mp_t p, botan_mp_t g, botan_mp_t y)
+
+ Initialize a public Diffie-Hellman key using group parameters p and g and public key y.
+
+Public Key Encryption/Decryption
+----------------------------------------
+
+.. cpp:type:: opaque* botan_pk_op_encrypt_t
+
+ An opaque data type for an encryption operation. Don't mess with it.
+
+.. cpp:function:: int botan_pk_op_encrypt_create(botan_pk_op_encrypt_t* op, \
+ botan_pubkey_t key, \
+ const char* padding, \
+ uint32_t flags)
+
+ Create a new operation object which can be used to encrypt using the provided
+ key and the specified padding scheme (such as "OAEP(SHA-256)" for use with
+ RSA). Flags should be 0 in this version.
+
+.. cpp:function:: int botan_pk_op_encrypt_destroy(botan_pk_op_encrypt_t op)
+
+ Destroy the object.
+
+.. cpp:function:: int botan_pk_op_encrypt_output_length(botan_pk_op_encrypt_t op, \
+ size_t ptext_len, size_t* ctext_len)
+
+ Returns an upper bound on the output length if a plaintext of length ``ptext_len``
+ is encrypted with this key/parameter setting. This allows correctly sizing the
+ buffer that is passed to :cpp:func:`botan_pk_op_encrypt`.
+
+.. cpp:function:: int botan_pk_op_encrypt(botan_pk_op_encrypt_t op, \
+ botan_rng_t rng, \
+ uint8_t out[], size_t* out_len, \
+ const uint8_t plaintext[], size_t plaintext_len)
+
+ Encrypt the provided data using the key, placing the output in `out`. If
+ `out` is NULL, writes the length of what the ciphertext would have been to
+ `*out_len`. However this is computationally expensive (the encryption
+ actually occurs, then the result is discarded), so it is better to use
+ :cpp:func:`botan_pk_op_encrypt_output_length` to correctly size the buffer.
+
+.. cpp:type:: opaque* botan_pk_op_decrypt_t
+
+ An opaque data type for a decryption operation. Don't mess with it.
+
+.. cpp:function:: int botan_pk_op_decrypt_create(botan_pk_op_decrypt_t* op, \
+ botan_privkey_t key, \
+ const char* padding, \
+ uint32_t flags)
+
+.. cpp:function:: int botan_pk_op_decrypt_destroy(botan_pk_op_decrypt_t op)
+
+.. cpp:function:: int botan_pk_op_decrypt_output_length(botan_pk_op_decrypt_t op, \
+ size_t ctext_len, size_t* ptext_len)
+
+ For a given ciphertext length, returns the upper bound on the size of the
+ plaintext that might be enclosed. This allows properly sizing the output
+ buffer passed to :cpp:func:`botan_pk_op_decrypt`.
+
+.. cpp:function:: int botan_pk_op_decrypt(botan_pk_op_decrypt_t op, \
+ uint8_t out[], size_t* out_len, \
+ uint8_t ciphertext[], size_t ciphertext_len)
+
+Signature Generation
+----------------------------------------
+
+.. cpp:type:: opaque* botan_pk_op_sign_t
+
+ An opaque data type for a signature generation operation. Don't mess with it.
+
+.. cpp:function:: int botan_pk_op_sign_create(botan_pk_op_sign_t* op, \
+ botan_privkey_t key, \
+ const char* hash_and_padding, \
+ uint32_t flags)
+
+ Create a signature operator for the provided key. The padding string
+ specifies what hash function and padding should be used, for example
+ "PKCS1v15(SHA-256)" or "EMSA1(SHA-384)".
+
+.. cpp:function:: int botan_pk_op_sign_destroy(botan_pk_op_sign_t op)
+
+ Destroy an object created by :cpp:func:`botan_pk_op_sign_create`.
+
+.. cpp:function:: int botan_pk_op_sign_output_length(botan_pk_op_sign_t op, size_t* sig_len)
+
+ Writes the length of the signatures that this signer will produce. This
+ allows properly sizing the buffer passed to
+ :cpp:func:`botan_pk_op_sign_finish`.
+
+.. cpp:function:: int botan_pk_op_sign_update(botan_pk_op_sign_t op, \
+ const uint8_t in[], size_t in_len)
+
+ Add bytes of the message to be signed.
+
+.. cpp:function:: int botan_pk_op_sign_finish(botan_pk_op_sign_t op, botan_rng_t rng, \
+ uint8_t sig[], size_t* sig_len)
+
+ Produce a signature over all of the bytes passed to :cpp:func:`botan_pk_op_sign_update`.
+ Afterwards, the sign operator is reset and may be used to sign a new message.
+
+Signature Verification
+----------------------------------------
+
+.. cpp:type:: opaque* botan_pk_op_verify_t
+
+ An opaque data type for a signature verification operation. Don't mess with it.
+
+.. cpp:function:: int botan_pk_op_verify_create(botan_pk_op_verify_t* op, \
+ botan_pubkey_t key, \
+ const char* hash_and_padding, \
+ uint32_t flags)
+
+.. cpp:function:: int botan_pk_op_verify_destroy(botan_pk_op_verify_t op)
+
+.. cpp:function:: int botan_pk_op_verify_update(botan_pk_op_verify_t op, \
+ const uint8_t in[], size_t in_len)
+
+ Add bytes of the message to be verified
+
+.. cpp:function:: int botan_pk_op_verify_finish(botan_pk_op_verify_t op, \
+ const uint8_t sig[], size_t sig_len)
+
+ Verify if the signature provided matches with the message provided as calls
+ to :cpp:func:`botan_pk_op_verify_update`.
+
+Key Agreement
+----------------------------------------
+
+.. cpp:type:: opaque* botan_pk_op_ka_t
+
+ An opaque data type for a key agreement operation. Don't mess with it.
+
+.. cpp:function:: int botan_pk_op_key_agreement_create(botan_pk_op_ka_t* op, \
+ botan_privkey_t key, \
+ const char* kdf, \
+ uint32_t flags)
+
+.. cpp:function:: int botan_pk_op_key_agreement_destroy(botan_pk_op_ka_t op)
+
+.. cpp:function:: int botan_pk_op_key_agreement_export_public(botan_privkey_t key, \
+ uint8_t out[], size_t* out_len)
+
+.. cpp:function:: int botan_pk_op_key_agreement(botan_pk_op_ka_t op, \
+ uint8_t out[], size_t* out_len, \
+ const uint8_t other_key[], size_t other_key_len, \
+ const uint8_t salt[], size_t salt_len)
+
+.. cpp:function:: int botan_mceies_encrypt(botan_pubkey_t mce_key, \
+ botan_rng_t rng, \
+ const char* aead, \
+ const uint8_t pt[], size_t pt_len, \
+ const uint8_t ad[], size_t ad_len, \
+ uint8_t ct[], size_t* ct_len)
+
+.. cpp:function:: int botan_mceies_decrypt(botan_privkey_t mce_key, \
+ const char* aead, \
+ const uint8_t ct[], size_t ct_len, \
+ const uint8_t ad[], size_t ad_len, \
+ uint8_t pt[], size_t* pt_len)
+
+X.509 Certificates
+----------------------------------------
+
+.. cpp:type:: opaque* botan_x509_cert_t
+
+ An opaque data type for an X.509 certificate. 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)
+
+ Load a certificate from the DER or PEM representation
+
+.. cpp:function:: int botan_x509_cert_load_file(botan_x509_cert_t* cert_obj, const char* filename)
+
+ Load a certificate from a file.
+
+.. cpp:function:: int botan_x509_cert_dup(botan_x509_cert_t* cert_obj, botan_x509_cert_t cert)
+
+ Create a new object that refers to the same certificate.
+
+.. cpp:function:: int botan_x509_cert_destroy(botan_x509_cert_t cert)
+
+ Destroy the certificate object
+
+.. cpp:function:: int botan_x509_cert_gen_selfsigned(botan_x509_cert_t* cert, \
+ botan_privkey_t key, \
+ botan_rng_t rng, \
+ const char* common_name, \
+ const char* org_name)
+
+.. 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
+ "YYYYMMDDHHMMSSZ" where Z is a literal character reflecting that this time is
+ relative to UTC. Prefer :cpp:func:`botan_x509_cert_not_before`.
+
+.. cpp:function:: int botan_x509_cert_get_time_expires(botan_x509_cert_t cert, char out[], size_t* out_len)
+
+ 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. Prefer :cpp:func:`botan_x509_cert_not_after`.
+
+.. cpp:function:: int botan_x509_cert_not_before(botan_x509_cert_t cert, uint64_t* time_since_epoch)
+
+ Return the time the certificate becomes valid, as seconds since epoch.
+
+.. cpp:function:: int botan_x509_cert_not_after(botan_x509_cert_t cert, uint64_t* time_since_epoch)
+
+ Return the time the certificate expires, as seconds since epoch.
+
+.. cpp:function:: int botan_x509_cert_get_fingerprint(botan_x509_cert_t cert, const char* hash, uint8_t out[], size_t* out_len)
+
+.. 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.
+
+.. cpp:function:: int botan_x509_cert_get_authority_key_id(botan_x509_cert_t cert, uint8_t out[], size_t* out_len)
+
+ Return the authority key ID set in the certificate, which may be empty.
+
+.. cpp:function:: int botan_x509_cert_get_subject_key_id(botan_x509_cert_t cert, uint8_t out[], size_t* out_len)
+
+ Return the subject key ID set in the certificate, which may be empty.
+
+.. cpp:function:: int botan_x509_cert_get_public_key_bits(botan_x509_cert_t cert, \
+ uint8_t out[], size_t* out_len)
+
+ Get the serialized representation of the public key included in this certificate
+
+.. cpp:function:: int botan_x509_cert_get_public_key(botan_x509_cert_t cert, botan_pubkey_t* key)
+
+ Get the public key included in this certificate as a newly allocated object
+
+.. cpp:function:: 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)
+
+ Get a value from 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.
+
+.. cpp:function:: int botan_x509_cert_to_string(botan_x509_cert_t cert, char out[], size_t* out_len)
+
+ Format the certificate as a free-form string.
+
+.. cpp:enum:: botan_x509_cert_key_constraints
+
+ Certificate key usage constraints. Allowed values: `NO_CONSTRAINTS`,
+ `DIGITAL_SIGNATURE`, `NON_REPUDIATION`, `KEY_ENCIPHERMENT`,
+ `DATA_ENCIPHERMENT`, `KEY_AGREEMENT`, `KEY_CERT_SIGN`,
+ `CRL_SIGN`, `ENCIPHER_ONLY`, `DECIPHER_ONLY`.
+
+.. cpp:function:: int botan_x509_cert_allowed_usage(botan_x509_cert_t cert, unsigned int key_usage)
+
+
+.. cpp:function:: int botan_x509_cert_verify(int* validation_result, \
+ botan_x509_cert_t cert, \
+ const botan_x509_cert_t* intermediates, \
+ size_t intermediates_len, \
+ const botan_x509_cert_t* trusted, \
+ size_t trusted_len, \
+ const char* trusted_path, \
+ size_t required_strength, \
+ const char* hostname, \
+ uint64_t reference_time)
+
+ Verify a certificate. Returns 0 if validation was successful, 1 if
+ unsuccessful, or negative on error.
+
+ Sets ``validation_result`` to a code that provides more information.
+
+ If not needed, set ``intermediates`` to NULL and ``intermediates_len`` to
+ zero.
+
+ If not needed, set ``trusted`` to NULL and ``trusted_len`` to zero.
+
+ The ``trusted_path`` refers to a directory where one or more trusted CA
+ certificates are stored. It may be NULL if not needed.
+
+ 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.
+
+ Set ``reference_time`` to be the time which the certificate chain is
+ validated against. Use zero to use the current system clock.
+
+.. cpp:function:: int botan_x509_cert_verify_with_crl(int* validation_result, \
+ botan_x509_cert_t cert, \
+ const botan_x509_cert_t* intermediates, \
+ size_t intermediates_len, \
+ const botan_x509_cert_t* trusted, \
+ size_t trusted_len, \
+ const botan_x509_crl_t* crls, \
+ size_t crls_len, \
+ const char* trusted_path, \
+ size_t required_strength, \
+ const char* hostname, \
+ uint64_t reference_time)
+
+ Certificate path validation supporting Certificate Revocation Lists.
+
+ Works the same as ``botan_x509_cert_cerify``.
+
+ ``crls`` is an array of ``botan_x509_crl_t`` objects, ``crls_len`` is its length.
+
+.. cpp:function:: const char* botan_x509_cert_validation_status(int code)
+
+ Return a (statically allocated) string associated with the verification
+ result.
+
+X.509 Certificate Revocation Lists
+----------------------------------------
+
+.. cpp:type:: opaque* botan_x509_crl_t
+
+ An opaque data type for an X.509 CRL.
+
+.. cpp:function:: int botan_x509_crl_load(botan_x509_crl_t* crl_obj, \
+ const uint8_t crl[], size_t crl_len)
+
+ Load a CRL from the DER or PEM representation.
+
+.. cpp:function:: int botan_x509_crl_load_file(botan_x509_crl_t* crl_obj, const char* filename)
+
+ Load a CRL from a file.
+
+.. cpp:function:: int botan_x509_crl_destroy(botan_x509_crl_t crl)
+
+ Destroy the CRL object.
+
+.. 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.
diff --git a/comm/third_party/botan/doc/api_ref/filters.rst b/comm/third_party/botan/doc/api_ref/filters.rst
new file mode 100644
index 0000000000..04baebf5d0
--- /dev/null
+++ b/comm/third_party/botan/doc/api_ref/filters.rst
@@ -0,0 +1,733 @@
+
+Pipe/Filter Message Processing
+========================================
+
+.. note::
+
+ The system described below provides a message processing system with a
+ straightforward API. However it makes many extra memory copies and
+ allocations than would otherwise be required, and also tends to make
+ applications using it somewhat opaque because it is not obvious what this or
+ that Pipe& object actually does (type of operation, number of messages
+ output (if any!), and so on), whereas using say a HashFunction or AEAD_Mode
+ provides a much better idea in the code of what operation is occurring.
+
+ This filter interface is no longer used within the library itself
+ (outside a few dusty corners) and will likely not see any further major
+ development. However it will remain included because the API is often
+ convenient and many applications use it.
+
+Many common uses of cryptography involve processing one or more
+streams of data. Botan provides services that make setting up data
+flows through various operations, such as compression, encryption, and
+base64 encoding. Each of these operations is implemented in what are
+called *filters* in Botan. A set of filters are created and placed into
+a *pipe*, and information "flows" 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.
+
+Here is an example that uses a pipe to base64 encode some strings::
+
+ Pipe pipe(new Base64_Encoder); // pipe owns the pointer
+ pipe.start_msg();
+ pipe.write("message 1");
+ pipe.end_msg(); // flushes buffers, increments message number
+
+ // process_msg(x) is start_msg() && write(x) && end_msg()
+ pipe.process_msg("message2");
+
+ std::string m1 = pipe.read_all_as_string(0); // "message1"
+ std::string m2 = pipe.read_all_as_string(1); // "message2"
+
+Byte streams in the pipe are grouped into messages; blocks of data that
+are processed in an identical fashion (ie, with the same sequence of
+filter operations). Messages are delimited by calls to ``start_msg``
+and ``end_msg``. Each message in a pipe has its own identifier, which
+currently is an integer that increments up from zero.
+
+The ``Base64_Encoder`` was allocated using ``new``; but where was it
+deallocated? When a filter object is passed to a ``Pipe``, the pipe
+takes ownership of the object, and will deallocate it when it is no
+longer needed.
+
+There are two different ways to make use of messages. One is to send
+several messages through a ``Pipe`` without changing the ``Pipe``
+configuration, so you end up with a sequence of messages; one use of
+this would be to send a sequence of identically encrypted UDP packets,
+for example (note that the *data* need not be identical; it is just
+that each is encrypted, encoded, signed, etc in an identical
+fashion). Another is to change the filters that are used in the
+``Pipe`` between each message, by adding or removing filters;
+functions that let you do this are documented in the Pipe API section.
+
+Botan has about 40 filters that perform different operations on data.
+Here's code that uses one of them to encrypt a string with AES::
+
+ AutoSeeded_RNG rng,
+ SymmetricKey key(rng, 16); // a random 128-bit key
+ InitializationVector iv(rng, 16); // a random 128-bit IV
+
+ // The algorithm we want is specified by a string
+ Pipe pipe(get_cipher("AES-128/CBC", key, iv, ENCRYPTION));
+
+ pipe.process_msg("secrets");
+ pipe.process_msg("more secrets");
+
+ secure_vector<uint8_t> c1 = pipe.read_all(0);
+
+ uint8_t c2[4096] = { 0 };
+ size_t got_out = pipe.read(c2, sizeof(c2), 1);
+ // use c2[0...got_out]
+
+Note the use of ``AutoSeeded_RNG``, which is a random number
+generator. If you want to, you can explicitly set up the random number
+generators and entropy sources you want to, however for 99% of cases
+``AutoSeeded_RNG`` is preferable.
+
+``Pipe`` also has convenience methods for dealing with ``std::iostream``.
+Here is an example of this, using the bzip2 compression filter::
+
+ std::ifstream in("data.bin", std::ios::binary)
+ std::ofstream out("data.bin.bz2", std::ios::binary)
+
+ Pipe pipe(new Compression_Filter("bzip2", 9));
+
+ pipe.start_msg();
+ in >> pipe;
+ pipe.end_msg();
+ out << pipe;
+
+However there is a hitch to the code above; the complete contents of
+the compressed data will be held in memory until the entire message
+has been compressed, at which time the statement ``out << pipe`` is
+executed, and the data is freed as it is read from the pipe and
+written to the file. But if the file is very large, we might not have
+enough physical memory (or even enough virtual memory!) for that to be
+practical. So instead of storing the compressed data in the pipe for
+reading it out later, we divert it directly to the file::
+
+ std::ifstream in("data.bin", std::ios::binary)
+ std::ofstream out("data.bin.bz2", std::ios::binary)
+
+ Pipe pipe(new Compression_Filter("bzip2", 9), new DataSink_Stream(out));
+
+ pipe.start_msg();
+ in >> pipe;
+ pipe.end_msg();
+
+This is the first code we've seen so far that uses more than one
+filter in a pipe. The output of the compressor is sent to the
+``DataSink_Stream``. Anything written to a ``DataSink_Stream`` is
+written to a file; the filter produces no output. As soon as the
+compression algorithm finishes up a block of data, it will send it
+along to the sink filter, which will immediately write it to the
+stream; if you were to call ``pipe.read_all()`` after
+``pipe.end_msg()``, you'd get an empty vector out. This is
+particularly useful for cases where you are processing a large amount
+of data, as it means you don't have to store everything in memory at
+once.
+
+Here's an example using two computational filters::
+
+ AutoSeeded_RNG rng,
+ SymmetricKey key(rng, 32);
+ InitializationVector iv(rng, 16);
+
+ Pipe encryptor(get_cipher("AES/CBC/PKCS7", key, iv, ENCRYPTION),
+ new Base64_Encoder);
+
+ encryptor.start_msg();
+ file >> encryptor;
+ encryptor.end_msg(); // flush buffers, complete computations
+ std::cout << encryptor;
+
+You can read from a pipe while you are still writing to it, which
+allows you to bound the amount of memory that is in use at any one
+time. A common idiom for this is::
+
+ pipe.start_msg();
+ std::vector<uint8_t> buffer(4096); // arbitrary size
+ while(infile.good())
+ {
+ infile.read((char*)&buffer[0], buffer.size());
+ const size_t got_from_infile = infile.gcount();
+ pipe.write(buffer, got_from_infile);
+
+ if(infile.eof())
+ pipe.end_msg();
+
+ while(pipe.remaining() > 0)
+ {
+ const size_t buffered = pipe.read(buffer, buffer.size());
+ outfile.write((const char*)&buffer[0], buffered);
+ }
+ }
+ if(infile.bad() || (infile.fail() && !infile.eof()))
+ throw Some_Exception();
+
+Fork
+---------------------------------
+
+It is common that you might receive some data and want to perform more
+than one operation on it (ie, encrypt it with Serpent and calculate
+the SHA-256 hash of the plaintext at the same time). That's where
+``Fork`` comes in. ``Fork`` is a filter that takes input and passes it
+on to *one or more* filters that are attached to it. ``Fork`` changes
+the nature of the pipe system completely: instead of being a linked
+list, it becomes a tree or acyclic graph.
+
+Each filter in the fork is given its own output buffer, and thus its
+own message. For example, if you had previously written two messages
+into a pipe, then you start a new one with a fork that has three
+paths of filter's inside it, you add three new messages to the
+pipe. The data you put into the pipe is duplicated and sent
+into each set of filter and the eventual output is placed into a
+dedicated message slot in the pipe.
+
+Messages in the pipe are allocated in a depth-first manner. This is only
+interesting if you are using more than one fork in a single pipe.
+As an example, consider the following::
+
+ Pipe pipe(new Fork(
+ new Fork(
+ new Base64_Encoder,
+ new Fork(
+ NULL,
+ new Base64_Encoder
+ )
+ ),
+ new Hex_Encoder
+ )
+ );
+
+In this case, message 0 will be the output of the first
+``Base64_Encoder``, message 1 will be a copy of the input (see below
+for how fork interprets NULL pointers), message 2 will be the output
+of the second ``Base64_Encoder``, and message 3 will be the output of
+the ``Hex_Encoder``. This results in message numbers being allocated
+in a top to bottom fashion, when looked at on the screen. However,
+note that there could be potential for bugs if this is not
+anticipated. For example, if your code is passed a filter, and you
+assume it is a "normal" one that only uses one message, your message
+offsets would be wrong, leading to some confusion during output.
+
+If Fork's first argument is a null pointer, but a later argument is
+not, then Fork will feed a copy of its input directly through. Here's
+a case where that is useful::
+
+ // have std::string ciphertext, auth_code, key, iv, mac_key;
+
+ Pipe pipe(new Base64_Decoder,
+ get_cipher("AES-128", key, iv, DECRYPTION),
+ new Fork(
+ 0, // this message gets plaintext
+ new MAC_Filter("HMAC(SHA-1)", mac_key)
+ )
+ );
+
+ pipe.process_msg(ciphertext);
+ std::string plaintext = pipe.read_all_as_string(0);
+ secure_vector<uint8_t> mac = pipe.read_all(1);
+
+ if(mac != auth_code)
+ error();
+
+Here we wanted to not only decrypt the message, but send the decrypted
+text through an additional computation, in order to compute the
+authentication code.
+
+Any filters that are attached to the pipe after the fork are
+implicitly attached onto the first branch created by the fork. For
+example, let's say you created this pipe::
+
+ Pipe pipe(new Fork(new Hash_Filter("SHA-256"),
+ new Hash_Filter("SHA-512")),
+ new Hex_Encoder);
+
+And then called ``start_msg``, inserted some data, then
+``end_msg``. Then ``pipe`` would contain two messages. The first one
+(message number 0) would contain the SHA-256 sum of the input in hex
+encoded form, and the other would contain the SHA-512 sum of the input
+in raw binary. In many situations you'll want to perform a sequence of
+operations on multiple branches of the fork; in which case, use
+the filter described in :ref:`chain`.
+
+There is also a ``Threaded_Fork`` which acts the same as ``Fork``,
+except it runs each of the filters in its own thread.
+
+.. _chain:
+
+Chain
+---------------------------------
+
+A ``Chain`` filter creates a chain of filters and encapsulates them
+inside a single filter (itself). This allows a sequence of filters to
+become a single filter, to be passed into or out of a function, or to
+a ``Fork`` constructor.
+
+You can call ``Chain``'s constructor with up to four ``Filter``
+pointers (they will be added in order), or with an array of filter
+pointers and a ``size_t`` that tells ``Chain`` how many filters are in
+the array (again, they will be attached in order). Here's the example
+from the last section, using chain instead of relying on the implicit
+pass through the other version used::
+
+ Pipe pipe(new Fork(
+ new Chain(new Hash_Filter("SHA-256"), new Hex_Encoder),
+ new Hash_Filter("SHA-512")
+ )
+ );
+
+Sources and Sinks
+----------------------------------------
+
+Data Sources
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+A ``DataSource`` is a simple abstraction for a thing that stores
+bytes. This type is used heavily in the areas of the API related to
+ASN.1 encoding/decoding. The following types are ``DataSource``:
+``Pipe``, ``SecureQueue``, and a couple of special purpose ones:
+``DataSource_Memory`` and ``DataSource_Stream``.
+
+You can create a ``DataSource_Memory`` with an array of bytes and a
+length field. The object will make a copy of the data, so you don't
+have to worry about keeping that memory allocated. This is mostly for
+internal use, but if it comes in handy, feel free to use it.
+
+A ``DataSource_Stream`` is probably more useful than the memory based
+one. Its constructors take either a ``std::istream`` or a
+``std::string``. If it's a stream, the data source will use the
+``istream`` to satisfy read requests (this is particularly useful to
+use with ``std::cin``). If the string version is used, it will attempt
+to open up a file with that name and read from it.
+
+Data Sinks
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+A ``DataSink`` (in ``data_snk.h``) is a ``Filter`` that takes
+arbitrary amounts of input, and produces no output. This means it's
+doing something with the data outside the realm of what
+``Filter``/``Pipe`` can handle, for example, writing it to a file
+(which is what the ``DataSink_Stream`` does). There is no need for
+``DataSink``s that write to a ``std::string`` or memory buffer,
+because ``Pipe`` can handle that by itself.
+
+Here's a quick example of using a ``DataSink``, which encrypts
+``in.txt`` and sends the output to ``out.txt``. There is
+no explicit output operation; the writing of ``out.txt`` is
+implicit::
+
+ DataSource_Stream in("in.txt");
+ Pipe pipe(get_cipher("AES-128/CTR-BE", key, iv),
+ new DataSink_Stream("out.txt"));
+ pipe.process_msg(in);
+
+A real advantage of this is that even if "in.txt" is large, only as
+much memory is needed for internal I/O buffers will be used.
+
+The Pipe API
+---------------------------------
+
+Initializing Pipe
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+By default, ``Pipe`` will do nothing at all; any input placed into the
+``Pipe`` will be read back unchanged. Obviously, this has limited
+utility, and presumably you want to use one or more filters to somehow
+process the data. First, you can choose a set of filters to initialize
+the ``Pipe`` via the constructor. You can pass it either a set of up
+to four filter pointers, or a pre-defined array and a length::
+
+ Pipe pipe1(new Filter1(/*args*/), new Filter2(/*args*/),
+ new Filter3(/*args*/), new Filter4(/*args*/));
+ Pipe pipe2(new Filter1(/*args*/), new Filter2(/*args*/));
+
+ Filter* filters[5] = {
+ new Filter1(/*args*/), new Filter2(/*args*/), new Filter3(/*args*/),
+ new Filter4(/*args*/), new Filter5(/*args*/) /* more if desired... */
+ };
+ Pipe pipe3(filters, 5);
+
+This is by far the most common way to initialize a ``Pipe``. However,
+occasionally a more flexible initialization strategy is necessary;
+this is supported by 4 member functions. These functions may only be
+used while the pipe in question is not in use; that is, either before
+calling ``start_msg``, or after ``end_msg`` has been called (and no
+new calls to ``start_msg`` have been made yet).
+
+.. cpp:function:: void Pipe::prepend(Filter* filter)
+
+ Calling ``prepend`` will put the passed filter first in the list of
+ transformations. For example, if you prepend a filter implementing
+ encryption, and the pipe already had a filter that hex encoded the
+ input, then the next message processed would be first encrypted,
+ and *then* hex encoded.
+
+.. cpp:function:: void Pipe::append(Filter* filter)
+
+ Like ``prepend``, but places the filter at the end of the message
+ flow. This doesn't always do what you expect if there is a fork.
+
+.. cpp:function:: void Pipe::pop()
+
+ Removes the first filter in the flow.
+
+.. cpp:function:: void Pipe::reset()
+
+ Removes all the filters that the pipe currently holds - it is reset
+ to an empty/no-op state. Any data that is being retained by the
+ pipe is retained after a ``reset``, and ``reset`` does not affect
+ message numbers (discussed later).
+
+Giving Data to a Pipe
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Input to a ``Pipe`` is delimited into messages, which can be read from
+independently (ie, you can read 5 bytes from one message, and then all of
+another message, without either read affecting any other messages).
+
+.. cpp:function:: void Pipe::start_msg()
+
+ Starts a new message; if a message was already running, an exception is
+ thrown. After this function returns, you can call ``write``.
+
+.. cpp:function:: void Pipe::write(const uint8_t* input, size_t length)
+
+.. cpp:function:: void Pipe::write(const std::vector<uint8_t>& input)
+
+.. cpp:function:: void Pipe::write(const std::string& input)
+
+.. cpp:function:: void Pipe::write(DataSource& input)
+
+.. cpp:function:: void Pipe::write(uint8_t input)
+
+ All versions of ``write`` write the input into the filter sequence.
+ If a message is not currently active, an exception is thrown.
+
+.. cpp:function:: void Pipe::end_msg()
+
+ End the currently active message
+
+Sometimes, you may want to do only a single write per message. In this
+case, you can use the ``process_msg`` series of functions, which start
+a message, write their argument into the pipe, and then end the
+message. In this case you would not make any explicit calls to
+``start_msg``/``end_msg``.
+
+Pipes can also be used with the ``>>`` operator, and will accept a
+``std::istream``, or on Unix systems with the ``fd_unix`` module, a
+Unix file descriptor. In either case, the entire contents of the file
+will be read into the pipe.
+
+Getting Output from a Pipe
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Retrieving the processed data from a pipe is a bit more complicated,
+for various reasons. The pipe will separate each message into a
+separate buffer, and you have to retrieve data from each message
+independently. Each of the reader functions has a final parameter that
+specifies what message to read from. If this parameter is set to
+``Pipe::DEFAULT_MESSAGE``, it will read the current default message
+(``DEFAULT_MESSAGE`` is also the default value of this parameter).
+
+Functions in ``Pipe`` related to reading include:
+
+.. cpp:function:: size_t Pipe::read(uint8_t* out, size_t len)
+
+ Reads up to ``len`` bytes into ``out``, and returns the number of
+ bytes actually read.
+
+.. cpp:function:: size_t Pipe::peek(uint8_t* out, size_t len)
+
+ Acts exactly like `read`, except the data is not actually read; the
+ next read will return the same data.
+
+.. cpp:function:: secure_vector<uint8_t> Pipe::read_all()
+
+ Reads the entire message into a buffer and returns it
+
+.. cpp:function:: std::string Pipe::read_all_as_string()
+
+ Like ``read_all``, but it returns the data as a ``std::string``.
+ No encoding is done; if the message contains raw binary, so will
+ the string.
+
+.. cpp:function:: size_t Pipe::remaining()
+
+ Returns how many bytes are left in the message
+
+.. cpp:function:: Pipe::message_id Pipe::default_msg()
+
+ Returns the current default message number
+
+.. cpp:function:: Pipe::message_id Pipe::message_count()
+
+ Returns the total number of messages currently in the pipe
+
+.. cpp:function:: Pipe::set_default_msg(Pipe::message_id msgno)
+
+ Sets the default message number (which must be a valid message
+ number for that pipe). The ability to set the default message number
+ is particularly important in the case of using the file output
+ operations (``<<`` with a ``std::ostream`` or Unix file descriptor),
+ because there is no way to specify the message explicitly when using
+ the output operator.
+
+Pipe I/O for Unix File Descriptors
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+This is a minor feature, but it comes in handy sometimes. In all
+installations of the library, Botan's ``Pipe`` object overloads the
+``<<`` and ``>>`` operators for C++ iostream objects,
+which is usually more than sufficient for doing I/O.
+
+However, there are cases where the iostream hierarchy does not map well to
+local 'file types', so there is also the ability to do I/O directly with Unix
+file descriptors. This is most useful when you want to read from or write to
+something like a TCP or Unix-domain socket, or a pipe, since for simple file
+access it's usually easier to just use C++'s file streams.
+
+If ``BOTAN_EXT_PIPE_UNIXFD_IO`` is defined, then you can use the
+overloaded I/O operators with Unix file descriptors. For an example of this,
+check out the ``hash_fd`` example, included in the Botan distribution.
+
+Filter Catalog
+---------------------------------
+
+This section documents most of the useful filters included in the
+library.
+
+Keyed Filters
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+A few sections ago, it was mentioned that ``Pipe`` can process
+multiple messages, treating each of them the same. Well, that was a
+bit of a lie. There are some algorithms (in particular, block ciphers
+not in ECB mode, and all stream ciphers) that change their state as
+data is put through them.
+
+Naturally, you might well want to reset the keys or (in the case of
+block cipher modes) IVs used by such filters, so multiple messages can
+be processed using completely different keys, or new IVs, or new keys
+and IVs, or whatever. And in fact, even for a MAC or an ECB block
+cipher, you might well want to change the key used from message to
+message.
+
+Enter ``Keyed_Filter``, which acts as an abstract interface for any
+filter that is uses keys: block cipher modes, stream ciphers, MACs,
+and so on. It has two functions, ``set_key`` and ``set_iv``. Calling
+``set_key`` will set (or reset) the key used by the algorithm. Setting
+the IV only makes sense in certain algorithms -- a call to ``set_iv``
+on an object that doesn't support IVs will cause an exception. You
+must call ``set_key`` *before* calling ``set_iv``.
+
+Here's a example::
+
+ Keyed_Filter *aes, *hmac;
+ Pipe pipe(new Base64_Decoder,
+ // Note the assignments to the cast and hmac variables
+ aes = get_cipher("AES-128/CBC", aes_key, iv),
+ new Fork(
+ 0, // Read the section 'Fork' to understand this
+ new Chain(
+ hmac = new MAC_Filter("HMAC(SHA-1)", mac_key, 12),
+ new Base64_Encoder
+ )
+ )
+ );
+ pipe.start_msg();
+ // use pipe for a while, decrypt some stuff, derive new keys and IVs
+ pipe.end_msg();
+
+ aes->set_key(aes_key2);
+ aes->set_iv(iv2);
+ hmac->set_key(mac_key2);
+
+ pipe.start_msg();
+ // use pipe for some other things
+ pipe.end_msg();
+
+There are some requirements to using ``Keyed_Filter`` that you must
+follow. If you call ``set_key`` or ``set_iv`` on a filter that is
+owned by a ``Pipe``, you must do so while the ``Pipe`` is
+"unlocked". This refers to the times when no messages are being
+processed by ``Pipe`` -- either before ``Pipe``'s ``start_msg`` is
+called, or after ``end_msg`` is called (and no new call to
+``start_msg`` has happened yet). Doing otherwise will result in
+undefined behavior, probably silently getting invalid output.
+
+And remember: if you're resetting both values, reset the key *first*.
+
+Cipher Filters
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Getting a hold of a ``Filter`` implementing a cipher is very
+easy. Make sure you're including the header ``lookup.h``, and
+then call ``get_cipher``. You will pass the return value
+directly into a ``Pipe``. There are a couple different functions
+which do varying levels of initialization:
+
+.. cpp:function:: Keyed_Filter* get_cipher(std::string cipher_spec, \
+ SymmetricKey key, InitializationVector iv, Cipher_Dir dir)
+
+.. cpp:function:: Keyed_Filter* get_cipher(std::string cipher_spec, \
+ SymmetricKey key, Cipher_Dir dir)
+
+The version that doesn't take an IV is useful for things that don't
+use them, like block ciphers in ECB mode, or most stream ciphers. If
+you specify a cipher spec that does want a IV, and you use the version
+that doesn't take one, an exception will be thrown. The ``dir``
+argument can be either ``ENCRYPTION`` or ``DECRYPTION``.
+
+The cipher_spec is a string that specifies what cipher is to be
+used. The general syntax for "cipher_spec" is "STREAM_CIPHER",
+"BLOCK_CIPHER/MODE", or "BLOCK_CIPHER/MODE/PADDING". In the case of
+stream ciphers, no mode is necessary, so just the name is
+sufficient. A block cipher requires a mode of some sort, which can be
+"ECB", "CBC", "CFB(n)", "OFB", "CTR-BE", or "EAX(n)". The argument to
+CFB mode is how many bits of feedback should be used. If you just use
+"CFB" with no argument, it will default to using a feedback equal to
+the block size of the cipher. EAX mode also takes an optional bit
+argument, which tells EAX how large a tag size to use~--~generally
+this is the size of the block size of the cipher, which is the default
+if you don't specify any argument.
+
+In the case of the ECB and CBC modes, a padding method can also be
+specified. If it is not supplied, ECB defaults to not padding, and CBC
+defaults to using PKCS #5/#7 compatible padding. The padding methods
+currently available are "NoPadding", "PKCS7", "OneAndZeros", and
+"CTS". CTS padding is currently only available for CBC mode, but the
+others can also be used in ECB mode.
+
+Some example "cipher_spec arguments are: "AES-128/CBC",
+"Blowfish/CTR-BE", "Serpent/XTS", and "AES-256/EAX".
+
+"CTR-BE" refers to counter mode where the counter is incremented as if
+it were a big-endian encoded integer. This is compatible with most
+other implementations, but it is possible some will use the
+incompatible little endian convention. This version would be denoted
+as "CTR-LE" if it were supported.
+
+"EAX" is a new cipher mode designed by Wagner, Rogaway, and
+Bellare. It is an authenticated cipher mode (that is, no separate
+authentication is needed), has provable security, and is free from
+patent entanglements. It runs about half as fast as most of the other
+cipher modes (like CBC, OFB, or CTR), which is not bad considering you
+don't need to use an authentication code.
+
+Hashes and MACs
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Hash functions and MACs don't need anything special when it comes to
+filters. Both just take their input and produce no output until
+``end_msg`` is called, at which time they complete the hash or MAC and
+send that as output.
+
+These filters take a string naming the type to be used. If for some
+reason you name something that doesn't exist, an exception will be thrown.
+
+.. cpp:function:: Hash_Filter::Hash_Filter(std::string hash, size_t outlen = 0)
+
+ This constructor creates a filter that hashes its input with
+ ``hash``. When ``end_msg`` is called on the owning pipe, the hash is
+ completed and the digest is sent on to the next filter in the
+ pipeline. The parameter ``outlen`` specifies how many bytes of the
+ hash output will be passed along to the next filter when ``end_msg``
+ is called. By default, it will pass the entire hash.
+
+ Examples of names for ``Hash_Filter`` are "SHA-1" and "Whirlpool".
+
+.. cpp:function:: MAC_Filter::MAC_Filter(std::string mac, SymmetricKey key, size_t outlen = 0)
+
+ This constructor takes a name for a mac, such as "HMAC(SHA-1)" or
+ "CMAC(AES-128)", along with a key to use. The optional ``outlen``
+ works the same as in ``Hash_Filter``.
+
+Encoders
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Often you want your data to be in some form of text (for sending over
+channels that aren't 8-bit clean, printing it, etc). The filters
+``Hex_Encoder`` and ``Base64_Encoder`` will convert arbitrary binary
+data into hex or base64 formats. Not surprisingly, you can use
+``Hex_Decoder`` and ``Base64_Decoder`` to convert it back into its
+original form.
+
+Both of the encoders can take a few options about how the data should
+be formatted (all of which have defaults). The first is a ``bool``
+which says if the encoder should insert line breaks. This defaults to
+false. Line breaks don't matter either way to the decoder, but it
+makes the output a bit more appealing to the human eye, and a few
+transport mechanisms (notably some email systems) limit the maximum
+line length.
+
+The second encoder option is an integer specifying how long such lines
+will be (obviously this will be ignored if line-breaking isn't being
+used). The default tends to be in the range of 60-80 characters, but
+is not specified. If you want a specific value, set it. Otherwise the
+default should be fine.
+
+Lastly, ``Hex_Encoder`` takes an argument of type ``Case``, which can
+be ``Uppercase`` or ``Lowercase`` (default is ``Uppercase``). This
+specifies what case the characters A-F should be output as. The base64
+encoder has no such option, because it uses both upper and lower case
+letters for its output.
+
+You can find the declarations for these types in ``hex_filt.h`` and
+``b64_filt.h``.
+
+Writing New Filters
+---------------------------------
+
+The system of filters and pipes was designed in an attempt to make it
+as simple as possible to write new filter types. There are four
+functions that need to be implemented by a class deriving from
+``Filter``:
+
+.. cpp:function:: std::string Filter::name() const
+
+ This should just return a useful decription of the filter object.
+
+.. cpp:function:: void Filter::write(const uint8_t* input, size_t length)
+
+ This function is what is called when a filter receives input for it
+ to process. The filter is not required to process the data right
+ away; many filters buffer their input before producing any output. A
+ filter will usually have ``write`` called many times during its
+ lifetime.
+
+.. cpp:function:: void Filter::send(uint8_t* output, size_t length)
+
+ Eventually, a filter will want to produce some output to send along
+ to the next filter in the pipeline. It does so by calling ``send``
+ with whatever it wants to send along to the next filter. There is
+ also a version of ``send`` taking a single byte argument, as a
+ convenience.
+
+ .. note::
+
+ Normally a filter does not need to override ``send``, though it
+ can for special handling. It does however need to call this
+ function whenever it wants to produce output.
+
+.. cpp:function:: void Filter::start_msg()
+
+ Implementing this function is optional. Implement it if your filter
+ would like to do some processing or setup at the start of each
+ message, such as allocating a data structure.
+
+.. cpp:function:: void Filter::end_msg()
+
+ Implementing this function is optional. It is called when it has
+ been requested that filters finish up their computations. The filter
+ should finish up with whatever computation it is working on (for
+ example, a compressing filter would flush the compressor and
+ ``send`` the final block), and empty any buffers in preparation for
+ processing a fresh new set of input.
+
+Additionally, if necessary, filters can define a constructor that
+takes any needed arguments, and a destructor to deal with deallocating
+memory, closing files, etc.
+
diff --git a/comm/third_party/botan/doc/api_ref/fpe.rst b/comm/third_party/botan/doc/api_ref/fpe.rst
new file mode 100644
index 0000000000..9d77a40864
--- /dev/null
+++ b/comm/third_party/botan/doc/api_ref/fpe.rst
@@ -0,0 +1,98 @@
+Format Preserving Encryption
+========================================
+
+Format preserving encryption (FPE) refers to a set of techniques for
+encrypting data such that the ciphertext has the same format as the
+plaintext. For instance, you can use FPE to encrypt credit card
+numbers with valid checksums such that the ciphertext is also an
+credit card number with a valid checksum, or similarly for bank
+account numbers, US Social Security numbers, or even more general
+mappings like English words onto other English words.
+
+The scheme currently implemented in botan is called FE1, and described
+in the paper `Format Preserving Encryption
+<https://eprint.iacr.org/2009/251>`_ by Mihir Bellare, Thomas
+Ristenpart, Phillip Rogaway, and Till Stegers. FPE is an area of
+ongoing standardization and it is likely that other schemes will be
+included in the future.
+
+To encrypt an arbitrary value using FE1, you need to use a ranking
+method. Basically, the idea is to assign an integer to every value you
+might encrypt. For instance, a 16 digit credit card number consists of
+a 15 digit code plus a 1 digit checksum. So to encrypt a credit card
+number, you first remove the checksum, encrypt the 15 digit value
+modulo 10\ :sup:`15`, and then calculate what the checksum is for the
+new (ciphertext) number. Or, if you were encrypting words in a
+dictionary, you could rank the words by their lexicographical order,
+and choose the modulus to be the number of words in the dictionary.
+
+The interfaces for FE1 are defined in the header ``fpe_fe1.h``:
+
+.. versionadded:: 2.5.0
+
+.. cpp:class:: FPE_FE1
+
+ .. cpp:function:: FPE_FE1(const BigInt& n, size_t rounds = 5, \
+ bool compat_mode = false, \
+ std::string mac_algo = "HMAC(SHA-256)")
+
+ Initialize an FPE operation to encrypt/decrypt integers less
+ than *n*. It is expected that *n* is trivially factorable into
+ small integers. Common usage would be n to be a power of 10.
+
+ Note that the default parameters to this constructor are
+ **incompatible** with the ``fe1_encrypt`` and ``fe1_decrypt``
+ function originally added in 1.9.17. For compatibility, use
+ 3 rounds and set ``compat_mode`` to true.
+
+ .. cpp:function:: BigInt encrypt(const BigInt& x, const uint8_t tweak[], size_t tweak_len) const
+
+ Encrypts the value *x* modulo the value *n* using the *key* and *tweak*
+ specified. Returns an integer less than *n*. The *tweak* is a value that
+ does not need to be secret that parameterizes the encryption function. For
+ instance, if you were encrypting a database column with a single key, you
+ could use a per-row-unique integer index value as the tweak. The same
+ tweak value must be used during decryption.
+
+ .. cpp:function:: BigInt decrypt(const BigInt& x, const uint8_t tweak[], size_t tweak_len) const
+
+ Decrypts an FE1 ciphertext. The *tweak* must be the same as that provided
+ to the encryption function. Returns the plaintext integer.
+
+ Note that there is not any implicit authentication or checking of data in
+ FE1, so if you provide an incorrect key or tweak the result is simply a
+ random integer.
+
+ .. cpp:function:: BigInt encrypt(const BigInt& x, uint64_t tweak)
+
+ Convenience version of encrypt taking an integer tweak.
+
+ .. cpp:function:: BigInt decrypt(const BigInt& x, uint64_t tweak)
+
+ Convenience version of decrypt taking an integer tweak.
+
+There are two functions that handle the entire FE1 encrypt/decrypt operation.
+These are the original interface to FE1, first added in 1.9.17. However because
+they do the entire setup cost for each operation, they are significantly slower
+than the class-based API presented above.
+
+.. warning:: These functions are hardcoded to use 3 rounds, which may be
+ insufficient depending on the chosen modulus.
+
+.. cpp:function:: BigInt FPE::fe1_encrypt(const BigInt& n, const BigInt& X, \
+ const SymmetricKey& key, const std::vector<uint8_t>& tweak)
+
+ This creates an FPE_FE1 object, sets the key, and encrypts *X* using
+ the provided tweak.
+
+.. cpp:function:: BigInt FPE::fe1_decrypt(const BigInt& n, const BigInt& X, \
+ const SymmetricKey& key, const std::vector<uint8_t>& tweak)
+
+ This creates an FPE_FE1 object, sets the key, and decrypts *X* using
+ the provided tweak.
+
+This example encrypts a credit card number with a valid `Luhn checksum
+<https://en.wikipedia.org/wiki/Luhn_algorithm>`_ to another number with the same
+format, including a correct checksum.
+
+.. literalinclude:: ../../src/cli/cc_enc.cpp
diff --git a/comm/third_party/botan/doc/api_ref/hash.rst b/comm/third_party/botan/doc/api_ref/hash.rst
new file mode 100644
index 0000000000..6198535f3c
--- /dev/null
+++ b/comm/third_party/botan/doc/api_ref/hash.rst
@@ -0,0 +1,351 @@
+Hash Functions and Checksums
+=============================
+
+Hash functions are one-way functions, which map data of arbitrary size to a
+fixed output length. Most of the hash functions in Botan are designed to be
+cryptographically secure, which means that it is computationally infeasible to
+create a collision (finding two inputs with the same hash) or preimages (given a
+hash output, generating an arbitrary input with the same hash). But note that
+not all such hash functions meet their goals, in particular MD4 and MD5 are
+trivially broken. However they are still included due to their wide adoption in
+various protocols.
+
+The class :cpp:class:`HashFunction` is defined in `botan/hash.h`.
+
+Using a hash function is typically split into three stages: initialization,
+update, and finalization (often referred to as a IUF interface). The
+initialization stage is implicit: after creating a hash function object, it is
+ready to process data. Then update is called one or more times. Calling update
+several times is equivalent to calling it once with all of the arguments
+concatenated. After completing a hash computation (eg using ``final``), the
+internal state is reset to begin hashing a new message.
+
+.. cpp:class:: HashFunction
+
+ .. cpp:function:: static std::unique_ptr<HashFunction> create(const std::string& name)
+
+ Return a newly allocated hash function object, or nullptr if the
+ name is not recognized.
+
+ .. cpp:function:: static std::unique_ptr<HashFunction> create_or_throw(const std::string& name)
+
+ Like ``create`` except that it will throw an exception instead of
+ returning nullptr.
+
+ .. cpp:function:: size_t output_length()
+
+ Return the size (in *bytes*) of the output of this function.
+
+ .. cpp:function:: void update(const uint8_t* input, size_t length)
+
+ Updates the computation with *input*.
+
+ .. cpp:function:: void update(uint8_t input)
+
+ Updates the computation with *input*.
+
+ .. cpp:function:: void update(const std::vector<uint8_t>& input)
+
+ Updates the computation with *input*.
+
+ .. cpp:function:: void update(const std::string& input)
+
+ Updates the computation with *input*.
+
+ .. cpp:function:: void final(uint8_t* out)
+
+ Finalize the calculation and place the result into ``out``.
+ For the argument taking an array, exactly ``output_length`` bytes will
+ be written. After you call ``final``, the algorithm is reset to
+ its initial state, so it may be reused immediately.
+
+ .. cpp:function:: secure_vector<uint8_t> final()
+
+ Similar to the other function of the same name, except it returns
+ the result in a newly allocated vector.
+
+ .. cpp:function:: secure_vector<uint8_t> process(const uint8_t in[], size_t length)
+
+ Equivalent to calling ``update`` followed by ``final``.
+
+ .. cpp:function:: secure_vector<uint8_t> process(const std::string& in)
+
+ Equivalent to calling ``update`` followed by ``final``.
+
+Code Example
+------------
+
+Assume we want to calculate the SHA-256, SHA-384, and SHA-3 hash digests of the STDIN stream using the Botan library.
+
+.. code-block:: cpp
+
+ #include <botan/hash.h>
+ #include <botan/hex.h>
+ #include <iostream>
+ int main ()
+ {
+ std::unique_ptr<Botan::HashFunction> hash1(Botan::HashFunction::create("SHA-256"));
+ std::unique_ptr<Botan::HashFunction> hash2(Botan::HashFunction::create("SHA-384"));
+ std::unique_ptr<Botan::HashFunction> hash3(Botan::HashFunction::create("SHA-3"));
+ std::vector<uint8_t> buf(2048);
+
+ while(std::cin.good())
+ {
+ //read STDIN to buffer
+ std::cin.read(reinterpret_cast<char*>(buf.data()), buf.size());
+ size_t readcount = std::cin.gcount();
+ //update hash computations with read data
+ hash1->update(buf.data(),readcount);
+ hash2->update(buf.data(),readcount);
+ hash3->update(buf.data(),readcount);
+ }
+ std::cout << "SHA-256: " << Botan::hex_encode(hash1->final()) << std::endl;
+ std::cout << "SHA-384: " << Botan::hex_encode(hash2->final()) << std::endl;
+ std::cout << "SHA-3: " << Botan::hex_encode(hash3->final()) << std::endl;
+ return 0;
+ }
+
+Available Hash Functions
+------------------------------
+
+The following cryptographic hash functions are implemented. If in doubt,
+any of SHA-384, SHA-3, or BLAKE2b are fine choices.
+
+BLAKE2b
+^^^^^^^^^
+
+Available if ``BOTAN_HAS_BLAKE2B`` is defined.
+
+A recently designed hash function. Very fast on 64-bit processors. Can output a
+hash of any length between 1 and 64 bytes, this is specified by passing a value
+to the constructor with the desired length.
+
+Named like "Blake2b" which selects default 512-bit output, or as
+"Blake2b(256)" to select 256 bits of output.
+
+GOST-34.11
+^^^^^^^^^^^^^^^
+
+.. deprecated:: 2.11
+
+Available if ``BOTAN_HAS_GOST_34_11`` is defined.
+
+Russian national standard hash. It is old, slow, and has some weaknesses. Avoid
+it unless you must.
+
+.. warning::
+ As this hash function is no longer approved by the latest Russian standards,
+ support for GOST 34.11 hash is deprecated and will be removed in a future
+ major release.
+
+Keccak-1600
+^^^^^^^^^^^^^^^
+
+Available if ``BOTAN_HAS_KECCAK`` is defined.
+
+An older (and incompatible) variant of SHA-3, but sometimes used. Prefer SHA-3 in
+new code.
+
+MD4
+^^^^^^^^^
+
+Available if ``BOTAN_HAS_MD4`` is defined.
+
+An old hash function that is now known to be trivially breakable. It is very
+fast, and may still be suitable as a (non-cryptographic) checksum.
+
+.. warning::
+ Support for MD4 is deprecated and will be removed in a future major release.
+
+MD5
+^^^^^^^^^
+
+Available if ``BOTAN_HAS_MD5`` is defined.
+
+Widely used, now known to be broken.
+
+RIPEMD-160
+^^^^^^^^^^^^^^^
+
+Available if ``BOTAN_HAS_RIPEMD160`` is defined.
+
+A 160 bit hash function, quite old but still thought to be secure (up to the
+limit of 2**80 computation required for a collision which is possible with any
+160 bit hash function). Somewhat deprecated these days.
+
+SHA-1
+^^^^^^^^^^^^^^^
+
+Available if ``BOTAN_HAS_SHA1`` is defined.
+
+Widely adopted NSA designed hash function. Starting to show significant signs of
+weakness, and collisions can now be generated. Avoid in new designs.
+
+SHA-256
+^^^^^^^^^^^^^^^
+
+Available if ``BOTAN_HAS_SHA2_32`` is defined.
+
+Relatively fast 256 bit hash function, thought to be secure.
+
+Also includes the variant SHA-224. There is no real reason to use SHA-224.
+
+SHA-512
+^^^^^^^^^^^^^^^
+
+Available if ``BOTAN_HAS_SHA2_64`` is defined.
+
+SHA-512 is faster than SHA-256 on 64-bit processors. Also includes the
+truncated variants SHA-384 and SHA-512/256, which have the advantage
+of avoiding message extension attacks.
+
+SHA-3
+^^^^^^^^^^^^^^^
+
+Available if ``BOTAN_HAS_SHA3`` is defined.
+
+The new NIST standard hash. Fairly slow.
+
+Supports 224, 256, 384 or 512 bit outputs. SHA-3 is faster with
+smaller outputs. Use as "SHA-3(256)" or "SHA-3(512)". Plain "SHA-3"
+selects default 512 bit output.
+
+SHAKE (SHAKE-128, SHAKE-256)
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Available if ``BOTAN_HAS_SHAKE`` is defined.
+
+These are actually XOFs (extensible output functions) based on SHA-3, which can
+output a value of any byte length. For example "SHAKE-128(1024)" will produce
+1024 bits of output. The specified length must be a multiple of 8. Not
+specifying an output length, "SHAKE-128" defaults to a 128-bit output and
+"SHAKE-256" defaults to a 256-bit output.
+
+.. warning::
+ In the case of SHAKE-128, the default output length in insufficient
+ to ensure security. The choice of default lengths was a bug which is
+ currently retained for compatability; they should have been 256 and
+ 512 bits resp to match SHAKE's security level. Using the default
+ lengths with SHAKE is deprecated and will be removed in a future major
+ release. Instead, always specify the desired output length.
+
+SM3
+^^^^^^^^^^^^^^^
+
+Available if ``BOTAN_HAS_SM3`` is defined.
+
+Chinese national hash function, 256 bit output. Widely used in industry there.
+Fast and seemingly secure, but no reason to prefer it over SHA-2 or SHA-3 unless
+required.
+
+Skein-512
+^^^^^^^^^^^^^^^
+
+Available if ``BOTAN_HAS_SKEIN_512`` is defined.
+
+A contender for the NIST SHA-3 competition. Very fast on 64-bit systems. Can
+output a hash of any length between 1 and 64 bytes. It also accepts an optional
+"personalization string" which can create variants of the hash. This is useful
+for domain separation.
+
+To set a personalization string set the second param to any value,
+typically ASCII strings are used. Examples "Skein-512(256)" or
+"Skein-512(384,personalization_string)".
+
+Streebog (Streebog-256, Streebog-512)
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Available if ``BOTAN_HAS_STREEBOG`` is defined.
+
+Newly designed Russian national hash function. Due to use of input-dependent
+table lookups, it is vulnerable to side channels. There is no reason to use it
+unless compatibility is needed.
+
+.. warning::
+ The Streebog Sbox has recently been revealed to have a hidden structure which
+ interacts with its linear layer in a way which may provide a backdoor when
+ used in certain ways. Avoid Streebog if at all possible.
+
+Tiger
+^^^^^^^^^^^^^^^
+
+.. deprecated:: 2.15
+
+Available if ``BOTAN_HAS_TIGER`` is defined.
+
+An older 192-bit hash function, optimized for 64-bit systems. Possibly
+vulnerable to side channels due to its use of table lookups.
+
+Tiger supports variable length output (16, 20 or 24 bytes) and
+variable rounds (which must be at least 3). Default is 24 byte output
+and 3 rounds. Specify with names like "Tiger" or "Tiger(20,5)".
+
+.. warning::
+ There are documented (albeit impractical) attacks on the full Tiger
+ hash leading to preimage attacks. This indicates possibility of a
+ serious weakness in the hash and for this reason it is deprecated
+ and will be removed in a future major release of the library.
+
+Whirlpool
+^^^^^^^^^^^^^^^
+
+Available if ``BOTAN_HAS_WHIRLPOOL`` is defined.
+
+A 512-bit hash function standardized by ISO and NESSIE. Relatively slow, and due
+to the table based implementation it is potentially vulnerable to cache based
+side channels.
+
+Hash Function Combiners
+---------------------------
+
+These are functions which combine multiple hash functions to create a new hash
+function. They are typically only used in specialized applications.
+
+Parallel
+^^^^^^^^^^^^^
+
+Available if ``BOTAN_HAS_PARALLEL_HASH`` is defined.
+
+Parallel simply concatenates multiple hash functions. For example
+"Parallel(SHA-256,SHA-512)" outputs a 256+512 bit hash created by hashing the
+input with both SHA-256 and SHA-512 and concatenating the outputs.
+
+Note that due to the "multicollision attack" it turns out that generating a
+collision for multiple parallel hash functions is no harder than generating a
+collision for the strongest hash function.
+
+Comp4P
+^^^^^^^^^^^^^
+
+Available if ``BOTAN_HAS_COMB4P`` is defined.
+
+This combines two cryptographic hashes in such a way that preimage and collision
+attacks are provably at least as hard as a preimage or collision attack on the
+strongest hash.
+
+Checksums
+----------------
+
+.. note:: Checksums are not suitable for cryptographic use, but can be used for
+ error checking purposes.
+
+Adler32
+^^^^^^^^^^^
+
+Available if ``BOTAN_HAS_ADLER32`` is defined.
+
+The Adler32 checksum is used in the zlib format. 32 bit output.
+
+CRC24
+^^^^^^^^^^^
+
+Available if ``BOTAN_HAS_CRC24`` is defined.
+
+This is the CRC function used in OpenPGP. 24 bit output.
+
+CRC32
+^^^^^^^^^^^
+
+Available if ``BOTAN_HAS_CRC32`` is defined.
+
+This is the 32-bit CRC used in protocols such as Ethernet, gzip, PNG, etc.
diff --git a/comm/third_party/botan/doc/api_ref/kdf.rst b/comm/third_party/botan/doc/api_ref/kdf.rst
new file mode 100644
index 0000000000..f0975a8cc3
--- /dev/null
+++ b/comm/third_party/botan/doc/api_ref/kdf.rst
@@ -0,0 +1,109 @@
+
+.. _key_derivation_function:
+
+Key Derivation Functions
+========================================
+
+Key derivation functions are used to turn some amount of shared secret
+material into uniform random keys suitable for use with symmetric
+algorithms. An example of an input which is useful for a KDF is a
+shared secret created using Diffie-Hellman key agreement.
+
+.. cpp:class:: KDF
+
+ .. cpp:function:: secure_vector<uint8_t> derive_key( \
+ size_t key_len, const std::vector<uint8_t>& secret, \
+ const std::string& salt = "") const
+
+ .. cpp:function:: secure_vector<uint8_t> derive_key( \
+ size_t key_len, const std::vector<uint8_t>& secret, \
+ const std::vector<uint8_t>& salt) const
+
+ .. cpp:function:: secure_vector<uint8_t> derive_key( \
+ size_t key_len, const std::vector<uint8_t>& secret, \
+ const uint8_t* salt, size_t salt_len) const
+
+ .. cpp:function:: secure_vector<uint8_t> derive_key( \
+ size_t key_len, const uint8_t* secret, size_t secret_len, \
+ const std::string& salt) const
+
+ All variations on the same theme. Deterministically creates a
+ uniform random value from *secret* and *salt*. Typically *salt* is
+ a label or identifier, such as a session id.
+
+You can create a :cpp:class:`KDF` using
+
+.. cpp:function:: KDF* get_kdf(const std::string& algo_spec)
+
+
+Available KDFs
+-------------------
+
+Botan includes many different KDFs simply because different protocols and
+standards have created subtly different approaches to this problem. For new
+code, use HKDF which is conservative, well studied, widely implemented and NIST
+approved.
+
+HKDF
+~~~~~
+
+Defined in RFC 5869, HKDF uses HMAC to process inputs. Also available
+are variants HKDF-Extract and HKDF-Expand. HKDF is the combined
+Extract+Expand operation. Use the combined HKDF unless you need
+compatibility with some other system.
+
+Available if ``BOTAN_HAS_HKDF`` is defined.
+
+KDF2
+~~~~~
+
+KDF2 comes from IEEE 1363. It uses a hash function.
+
+Available if ``BOTAN_HAS_KDF2`` is defined.
+
+KDF1-18033
+~~~~~~~~~~~~
+
+KDF1 from ISO 18033-2. Very similar to (but incompatible with) KDF2.
+
+Available if ``BOTAN_HAS_KDF1_18033`` is defined.
+
+KDF1
+~~~~~~
+
+KDF1 from IEEE 1363. It can only produce an output at most the length
+of the hash function used.
+
+Available if ``BOTAN_HAS_KDF1`` is defined.
+
+X9.42 PRF
+~~~~~~~~~~
+
+A KDF from ANSI X9.42. Sometimes used for Diffie-Hellman.
+
+Available if ``BOTAN_HAS_X942_PRF`` is defined.
+
+.. warning::
+ Support for X9.42 KDF is deprecated and will be removed in a future major release.
+
+SP800-108
+~~~~~~~~~~
+
+KDFs from NIST SP 800-108. Variants include "SP800-108-Counter",
+"SP800-108-Feedback" and "SP800-108-Pipeline".
+
+Available if ``BOTAN_HAS_SP800_108`` is defined.
+
+SP800-56A
+~~~~~~~~~~
+
+KDF from NIST SP 800-56A.
+
+Available if ``BOTAN_HAS_SP800_56A`` is defined.
+
+SP800-56C
+~~~~~~~~~~
+
+KDF from NIST SP 800-56C.
+
+Available if ``BOTAN_HAS_SP800_56C`` is defined.
diff --git a/comm/third_party/botan/doc/api_ref/keywrap.rst b/comm/third_party/botan/doc/api_ref/keywrap.rst
new file mode 100644
index 0000000000..5c3aac0a3e
--- /dev/null
+++ b/comm/third_party/botan/doc/api_ref/keywrap.rst
@@ -0,0 +1,60 @@
+AES Key Wrapping
+=================================
+
+NIST specifies two mechanisms for wrapping (encrypting) symmetric keys using
+another key. The first (and older, more widely supported) method requires the
+input be a multiple of 8 bytes long. The other allows any length input, though
+only up to 2**32 bytes.
+
+These algorithms are described in NIST SP 800-38F, and RFCs 3394 and 5649.
+
+This API, defined in ``nist_keywrap.h``, first became available in version 2.4.0
+
+These functions take an arbitrary 128-bit block cipher object, which must
+already have been keyed with the key encryption key. NIST only allows these
+functions with AES, but any 128-bit cipher will do and some other implementations
+(such as in OpenSSL) do also allow other ciphers. Use AES for best interop.
+
+.. cpp:function:: std::vector<uint8_t> nist_key_wrap(const uint8_t input[], \
+ size_t input_len, const BlockCipher& bc)
+
+ This performs KW (key wrap) mode. The input must be a multiple of 8 bytes long.
+
+.. cpp:function:: secure_vector<uint8_t> nist_key_unwrap(const uint8_t input[], \
+ size_t input_len, const BlockCipher& bc)
+
+ This unwraps the result of nist_key_wrap, or throw Invalid_Authentication_Tag on error.
+
+.. cpp:function:: std::vector<uint8_t> nist_key_wrap_padded(const uint8_t input[], \
+ size_t input_len, const BlockCipher& bc)
+
+ This performs KWP (key wrap with padding) mode. The input can be any length.
+
+.. cpp:function:: secure_vector<uint8_t> nist_key_unwrap_padded(const uint8_t input[], \
+ size_t input_len, const BlockCipher& bc)
+
+ This unwraps the result of nist_key_wrap_padded, or throws Invalid_Authentication_Tag
+ on error.
+
+RFC 3394 Interface
+-----------------------------
+
+This is an older interface that was first available (with slight changes) in
+1.10, and available in its current form since 2.0 release. It uses a 128-bit,
+192-bit, or 256-bit key to encrypt an input key. AES is always used. The input
+must be a multiple of 8 bytes; if not an exception is thrown.
+
+This interface is defined in ``rfc3394.h``.
+
+.. cpp:function:: secure_vector<uint8_t> rfc3394_keywrap(const secure_vector<uint8_t>& key, \
+ const SymmetricKey& kek)
+
+ Wrap the input key using kek (the key encryption key), and return the result. It will
+ be 8 bytes longer than the input key.
+
+.. cpp:function:: secure_vector<uint8_t> rfc3394_keyunwrap(const secure_vector<uint8_t>& key, \
+ const SymmetricKey& kek)
+
+ Unwrap a key wrapped with rfc3394_keywrap.
+
+
diff --git a/comm/third_party/botan/doc/api_ref/message_auth_codes.rst b/comm/third_party/botan/doc/api_ref/message_auth_codes.rst
new file mode 100644
index 0000000000..a3391a3105
--- /dev/null
+++ b/comm/third_party/botan/doc/api_ref/message_auth_codes.rst
@@ -0,0 +1,268 @@
+
+.. _mac:
+
+Message Authentication Codes (MAC)
+===================================
+
+A Message Authentication Code algorithm computes a tag over a message utilizing
+a shared secret key. Thus a valid tag confirms the authenticity and integrity of
+the message. Only entities in possession of the shared secret key are able to
+verify the tag.
+
+.. note::
+
+ When combining a MAC with unauthenticated encryption mode, prefer to first
+ encrypt the message and then MAC the ciphertext. The alternative is to MAC
+ the plaintext, which depending on exact usage can suffer serious security
+ issues. For a detailed discussion of this issue see the paper "The Order of
+ Encryption and Authentication for Protecting Communications" by Hugo
+ Krawczyk
+
+The Botan MAC computation is split into five stages.
+
+#. Instantiate the MAC algorithm.
+#. Set the secret key.
+#. Process IV.
+#. Process data.
+#. Finalize the MAC computation.
+
+.. cpp:class:: MessageAuthenticationCode
+
+ .. cpp:function:: std::string name() const
+
+ Returns a human-readable string of the name of this algorithm.
+
+ .. cpp:function:: void clear()
+
+ Clear the key.
+
+ .. cpp:function:: MessageAuthenticationCode* clone() const
+
+ Return a newly allocated object of the same type as this one.
+
+ .. cpp:function:: void set_key(const uint8_t* key, size_t length)
+
+ Set the shared MAC key for the calculation. This function has to be called before the data is processed.
+
+ .. cpp:function:: bool valid_keylength(size_t length) const
+
+ This function returns true if and only if *length* is a valid
+ keylength for the algorithm.
+
+ .. cpp:function:: size_t minimum_keylength() const
+
+ Return the smallest key length (in bytes) that is acceptable for the
+ algorithm.
+
+ .. cpp:function:: size_t maximum_keylength() const
+
+ Return the largest key length (in bytes) that is acceptable for the
+ algorithm.
+
+ .. cpp:function:: void start(const uint8_t* nonce, size_t nonce_len)
+
+ Set the IV for the MAC calculation. Note that not all MAC algorithms require an IV.
+ If an IV is required, the function has to be called before the data is processed.
+ For algorithms that don't require it, the call can be omitted, or else called
+ with ``nonce_len`` of zero.
+
+ .. cpp:function:: void update(const uint8_t* input, size_t length)
+
+ Process the passed data.
+
+ .. cpp:function:: void update(const secure_vector<uint8_t>& in)
+
+ Process the passed data.
+
+ .. cpp:function:: void update(uint8_t in)
+
+ Process a single byte.
+
+ .. cpp:function:: void final(uint8_t* out)
+
+ Complete the MAC computation and write the calculated tag to the passed byte array.
+
+ .. cpp:function:: secure_vector<uint8_t> final()
+
+ Complete the MAC computation and return the calculated tag.
+
+ .. cpp:function:: bool verify_mac(const uint8_t* mac, size_t length)
+
+ Finalize the current MAC computation and compare the result to the passed
+ ``mac``. Returns ``true``, if the verification is successful and false
+ otherwise.
+
+
+Code Examples
+------------------------
+
+The following example computes an HMAC with a random key then verifies the tag.
+
+ #include <botan/mac.h>
+ #include <botan/hex.h>
+ #include <botan/system_rng.h>
+ #include <assert.h>
+
+ std::string compute_mac(const std::string& msg, const Botan::secure_vector<uint8_t>& key)
+ {
+ auto hmac = Botan::MessageAuthenticationCode::create_or_throw("HMAC(SHA-256)");
+
+ hmac->set_key(key);
+ hmac->update(msg);
+
+ return Botan::hex_encode(hmac->final());
+ }
+
+ int main()
+ {
+ Botan::System_RNG rng;
+
+ 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);
+ assert(tag1 != tag2);
+
+ // Recomputing with original input message results in identical tag
+ std::string tag3 = compute_mac("Message", key);
+ assert(tag1 == tag3);
+ }
+
+
+The following example code computes a AES-256 GMAC and subsequently verifies the
+tag. Unlike most other MACs, GMAC requires a nonce *which must not repeat or
+all security is lost*.
+
+.. code-block:: cpp
+
+ #include <botan/mac.h>
+ #include <botan/hex.h>
+ #include <iostream>
+
+ int main()
+ {
+ const std::vector<uint8_t> key = Botan::hex_decode("1337133713371337133713371337133713371337133713371337133713371337");
+ const std::vector<uint8_t> nonce = Botan::hex_decode("FFFFFFFFFFFFFFFFFFFFFFFF");
+ const std::vector<uint8_t> data = Botan::hex_decode("6BC1BEE22E409F96E93D7E117393172A");
+ std::unique_ptr<Botan::MessageAuthenticationCode> mac(Botan::MessageAuthenticationCode::create("GMAC(AES-256)"));
+ if(!mac)
+ return 1;
+ mac->set_key(key);
+ mac->start(nonce);
+ mac->update(data);
+ Botan::secure_vector<uint8_t> tag = mac->final();
+ std::cout << mac->name() << ": " << Botan::hex_encode(tag) << std::endl;
+
+ //Verify created MAC
+ mac->start(nonce);
+ mac->update(data);
+ std::cout << "Verification: " << (mac->verify_mac(tag) ? "success" : "failure");
+ return 0;
+ }
+
+The following example code computes a valid AES-128 CMAC tag and modifies the
+data to demonstrate a MAC verification failure.
+
+.. code-block:: cpp
+
+ #include <botan/mac.h>
+ #include <botan/hex.h>
+ #include <iostream>
+
+ int main()
+ {
+ const std::vector<uint8_t> key = Botan::hex_decode("2B7E151628AED2A6ABF7158809CF4F3C");
+ std::vector<uint8_t> data = Botan::hex_decode("6BC1BEE22E409F96E93D7E117393172A");
+ std::unique_ptr<Botan::MessageAuthenticationCode> mac(Botan::MessageAuthenticationCode::create("CMAC(AES-128)"));
+ if(!mac)
+ return 1;
+ mac->set_key(key);
+ mac->update(data);
+ Botan::secure_vector<uint8_t> tag = mac->final();
+ //Corrupting data
+ data.back()++;
+ //Verify with corrupted data
+ mac->update(data);
+ std::cout << "Verification with malformed data: " << (mac->verify_mac(tag) ? "success" : "failure");
+ return 0;
+ }
+
+Available MACs
+------------------------------------------
+
+Currently the following MAC algorithms are available in Botan. In new code,
+default to HMAC with a strong hash like SHA-256 or SHA-384.
+
+CBC-MAC
+~~~~~~~~~~~~
+
+An older authentication code based on a block cipher. Serious security problems,
+in particular **insecure** if messages of several different lengths are
+authenticated. Avoid unless required for compatibility.
+
+Available if ``BOTAN_HAS_CBC_MAC`` is defined.
+
+.. warning::
+ CBC-MAC support is deprecated and will be removed in a future major release.
+
+CMAC
+~~~~~~~~~~~~
+
+A modern CBC-MAC variant that avoids the security problems of plain CBC-MAC.
+Approved by NIST. Also sometimes called OMAC.
+
+Available if ``BOTAN_HAS_CMAC`` is defined.
+
+GMAC
+~~~~~~~~~~~~
+
+GMAC is related to the GCM authenticated cipher mode. It is quite slow unless
+hardware support for carryless multiplications is available. A new nonce
+must be used with **each** message authenticated, or otherwise all security is
+lost.
+
+Available if ``BOTAN_HAS_GMAC`` is defined.
+
+.. warning::
+ Due to the nonce requirement, GMAC is exceptionally fragile. Avoid it unless
+ absolutely required.
+
+HMAC
+~~~~~~~~~~~~
+
+A message authentication code based on a hash function. Very commonly used.
+
+Available if ``BOTAN_HAS_HMAC`` is defined.
+
+Poly1305
+~~~~~~~~~~~~
+
+A polynomial mac (similar to GMAC). Very fast, but tricky to use safely. Forms
+part of the ChaCha20Poly1305 AEAD mode. A new key must be used for **each**
+message, or all security is lost.
+
+Available if ``BOTAN_HAS_POLY1305`` is defined.
+
+.. warning::
+ Due to the nonce requirement, Poly1305 is exceptionally fragile. Avoid it unless
+ absolutely required.
+
+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.
+
+Available if ``BOTAN_HAS_SIPHASH`` is defined.
+
+X9.19-MAC
+~~~~~~~~~~~~
+
+A CBC-MAC variant sometimes used in finance. Always uses DES.
+Sometimes called the "DES retail MAC", also standardized in ISO 9797-1.
+
+It is slow and has known attacks. Avoid unless required.
+
+Available if ``BOTAN_HAS_X919_MAC`` is defined.
diff --git a/comm/third_party/botan/doc/api_ref/otp.rst b/comm/third_party/botan/doc/api_ref/otp.rst
new file mode 100644
index 0000000000..133201b4e6
--- /dev/null
+++ b/comm/third_party/botan/doc/api_ref/otp.rst
@@ -0,0 +1,98 @@
+One Time Passwords
+========================
+
+.. versionadded:: 2.2.0
+
+One time password schemes are a user authentication method that relies on a
+fixed secret key which is used to derive a sequence of short passwords, each of
+which is accepted only once. Commonly this is used to implement two-factor
+authentication (2FA), where the user authenticates using both a conventional
+password (or a public key signature) and an OTP generated by a small device such
+as a mobile phone.
+
+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
+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.
+
+HOTP
+^^^^^^
+
+HOTP generates OTPs that are 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
+decremented. Thus both client and server must keep track of the next counter
+expected.
+
+Anyone with access to the client-specific secret key can authenticate as that
+client, so it should be treated with the same security consideration as would be
+given to any other symmetric key or plaintext password.
+
+.. cpp:class:: HOTP
+
+ Implement counter-based OTP
+
+ .. cpp:function:: HOTP(const SymmetricKey& key, const std::string& hash_algo = "SHA-1", size_t digits = 6)
+
+ Initialize an HOTP instance with a secret key (specific to each client),
+ a hash algorithm (must be SHA-1, SHA-256, or SHA-512), and the number of
+ digits with each OTP (must be 6, 7, or 8).
+
+ In RFC 4226, HOTP is only defined with SHA-1, but many HOTP
+ implementations support SHA-256 as an extension. The collision attacks
+ on SHA-1 do not have any known effect on HOTP's security.
+
+ .. cpp:function:: uint32_t generate_hotp(uint64_t counter)
+
+ Return the OTP associated with a specific counter value.
+
+ .. cpp:function:: std::pair<bool,uint64_t> verify_hotp(uint32_t otp, \
+ uint64_t starting_counter, size_t resync_range = 0)
+
+ Check if a provided OTP matches the one that should be generated for
+ the specified counter.
+
+ The *starting_counter* should be the counter of the last successful
+ authentication plus 1. If *resync_resync* is greater than 0, some number
+ of counter values above *starting_counter* will also be checked if
+ necessary. This is useful for instance when a client mistypes an OTP on
+ entry; the authentication will fail so the server will not update its
+ counter, but the client device will subsequently show the OTP for the
+ next counter. Depending on the environment a *resync_range* of 3 to 10
+ might be appropriate.
+
+ Returns a pair of (is_valid,next_counter_to_use). If the OTP is invalid
+ then always returns (false,starting_counter), since the last successful
+ authentication counter has not changed.
+
+
+TOTP
+^^^^^^^^^^
+
+TOTP is based on the same algorithm as HOTP, but instead of a counter a
+timestamp is used.
+
+.. cpp:class:: TOTP
+
+ .. cpp:function:: TOTP(const SymmetricKey& key, const std::string& hash_algo = "SHA-1", \
+ size_t digits = 6, size_t time_step = 30)
+
+ Setup to perform TOTP authentication using secret key *key*.
+
+ .. cpp:function:: uint32_t generate_totp(std::chrono::system_clock::time_point time_point)
+
+ .. cpp:function:: uint32_t generate_totp(uint64_t unix_time)
+
+ Generate and return a TOTP code based on a timestamp.
+
+ .. cpp:function:: bool verify_totp(uint32_t otp, std::chrono::system_clock::time_point time, \
+ size_t clock_drift_accepted = 0)
+
+ .. cpp:function:: bool verify_totp(uint32_t otp, uint64_t unix_time, \
+ size_t clock_drift_accepted = 0)
+
+ Return true if the provided OTP code is correct for the provided
+ timestamp. If required, use *clock_drift_accepted* to deal with
+ the client and server having slightly different clocks.
diff --git a/comm/third_party/botan/doc/api_ref/passhash.rst b/comm/third_party/botan/doc/api_ref/passhash.rst
new file mode 100644
index 0000000000..4ef26f7bea
--- /dev/null
+++ b/comm/third_party/botan/doc/api_ref/passhash.rst
@@ -0,0 +1,219 @@
+Password Hashing
+========================================
+
+Storing passwords for user authentication purposes in plaintext is the
+simplest but least secure method; when an attacker compromises the
+database in which the passwords are stored, they immediately gain
+access to all of them. Often passwords are reused among multiple
+services or machines, meaning once a password to a single service is
+known an attacker has a substantial head start on attacking other
+machines.
+
+The general approach is to store, instead of the password, the output
+of a one way function of the password. Upon receiving an
+authentication request, the authenticating party can recompute the one way
+function and compare the value just computed with the one that was
+stored. If they match, then the authentication request succeeds. But
+when an attacker gains access to the database, they only have the
+output of the one way function, not the original password.
+
+Common hash functions such as SHA-256 are one way, but used alone they
+have problems for this purpose. What an attacker can do, upon gaining
+access to such a stored password database, is hash common dictionary
+words and other possible passwords, storing them in a list. Then he
+can search through his list; if a stored hash and an entry in his list
+match, then he has found the password. Even worse, this can happen
+*offline*: an attacker can begin hashing common passwords days,
+months, or years before ever gaining access to the database. In
+addition, if two users choose the same password, the one way function
+output will be the same for both of them, which will be visible upon
+inspection of the database.
+
+There are two solutions to these problems: salting and
+iteration. Salting refers to including, along with the password, a
+randomly chosen value which perturbs the one way function. Salting can
+reduce the effectiveness of offline dictionary generation, because for
+each potential password, an attacker would have to compute the one way
+function output for all possible salts. It also prevents the same
+password from producing the same output, as long as the salts do not
+collide. Choosing n-bit salts randomly, salt collisions become likely
+only after about 2\ :sup:\ `(n/2)` salts have been generated. Choosing a
+large salt (say 80 to 128 bits) ensures this is very unlikely. Note
+that in password hashing salt collisions are unfortunate, but not
+fatal - it simply allows the attacker to attack those two passwords in
+parallel easier than they would otherwise be able to.
+
+The other approach, iteration, refers to the general technique of
+forcing multiple one way function evaluations when computing the
+output, to slow down the operation. For instance if hashing a single
+password requires running SHA-256 100,000 times instead of just once,
+that will slow down user authentication by a factor of 100,000, but
+user authentication happens quite rarely, and usually there are more
+expensive operations that need to occur anyway (network and database
+I/O, etc). On the other hand, an attacker who is attempting to break a
+database full of stolen password hashes will be seriously
+inconvenienced by a factor of 100,000 slowdown; they will be able to
+only test at a rate of .0001% of what they would without iterations
+(or, equivalently, will require 100,000 times as many zombie botnet
+hosts).
+
+Memory usage while checking a password is also a consideration; if the
+computation requires using a certain minimum amount of memory, then an
+attacker can become memory-bound, which may in particular make
+customized cracking hardware more expensive. Some password hashing
+designs, such as scrypt, explicitly attempt to provide this. The
+bcrypt approach requires over 4 KiB of RAM (for the Blowfish key
+schedule) and may also make some hardware attacks more expensive.
+
+Botan provides three techniques for password hashing: Argon2, bcrypt, and
+passhash9 (based on PBKDF2).
+
+Argon2
+----------------------------------------
+
+.. versionadded:: 2.11.0
+
+Argon2 is the winner of the PHC (Password Hashing Competition) and provides
+a tunable memory hard password hash. It has a standard string encoding, which looks like::
+
+ "$argon2i$v=19$m=8192,t=10,p=3$YWFhYWFhYWE$itkWB9ODqTd85wUsoib7pfpVTNGMOu0ZJan1odl25V8"
+
+Argon2 has three tunable parameters: ``M``, ``p``, and ``t``. ``M`` gives the
+total memory consumption of the algorithm in kilobytes. Increasing ``p``
+increases the available parallelism of the computation. The ``t`` parameter
+gives the number of passes which are made over the data.
+
+.. note::
+ Currently Botan does not make use of ``p`` > 1, so it is best to set it to 1
+ to minimize any advantage to highly parallel cracking attempts.
+
+There are three variants of Argon2, namely Argon2d, Argon2i and Argon2id.
+Argon2d uses data dependent table lookups with may leak information about the
+password via side channel attacks, and is **not recommended** for password
+hashing. Argon2i uses data independent table lookups and is immune to these
+attacks, but at the cost of requiring higher ``t`` for security. Argon2id uses a
+hybrid approach which is thought to be highly secure. The algorithm designers
+recommend using Argon2id with ``t`` and ``p`` both equal to 1 and ``M`` set to
+the largest amount of memory usable in your environment.
+
+.. cpp:function:: std::string argon2_generate_pwhash(const char* password, size_t password_len, \
+ RandomNumberGenerator& rng, \
+ size_t p, size_t M, size_t t, \
+ size_t y = 2, size_t salt_len = 16, size_t output_len = 32)
+
+ Generate an Argon2 hash of the specified password. The ``y`` parameter specifies
+ the variant: 0 for Argon2d, 1 for Argon2i, and 2 for Argon2id.
+
+.. cpp:function:: bool argon2_check_pwhash(const char* password, size_t password_len, \
+ const std::string& hash)
+
+ Verify an Argon2 password hash against the provided password. Returns false if
+ the input hash seems malformed or if the computed hash does not match.
+
+Bcrypt
+----------------------------------------
+
+`Bcrypt <https://www.usenix.org/legacy/event/usenix99/provos/provos.pdf>`_ is a
+password hashing scheme originally designed for use in OpenBSD, but numerous
+other implementations exist. It is made available by including ``bcrypt.h``.
+
+It has the advantage that it requires a small amount (4K) of fast RAM
+to compute, which can make hardware password cracking somewhat more
+expensive.
+
+Bcrypt provides outputs that look like this::
+
+ "$2a$12$7KIYdyv8Bp32WAvc.7YvI.wvRlyVn0HP/EhPmmOyMQA4YKxINO0p2"
+
+.. note::
+
+ Due to the design of bcrypt, the password is effectively truncated at 72
+ characters; further characters are ignored and do not change the hash. To
+ support longer passwords, one common approach is to pre-hash the password
+ with SHA-256, then run bcrypt using the hex or base64 encoding of the hash as
+ the password. (Many bcrypt implementations truncate the password at the first
+ NULL character, so hashing the raw binary SHA-256 may cause problems. Botan's
+ bcrypt implementation will hash whatever values are given in the
+ ``std::string`` including any embedded NULLs so this is not an issue, but
+ might cause interop problems if another library needs to validate the
+ password hashes.)
+
+.. cpp:function:: std::string generate_bcrypt(const std::string& password, \
+ RandomNumberGenerator& rng, \
+ uint16_t work_factor = 12, \
+ char bcrypt_version = "a")
+
+ Takes the password to hash, a rng, and a work factor.
+ The resulting password hash is returned as a string.
+
+ Higher work factors increase the amount of time the algorithm runs,
+ increasing the cost of cracking attempts. The increase is exponential, so a
+ work factor of 12 takes roughly twice as long as work factor 11. The default
+ work factor was set to 10 up until the 2.8.0 release.
+
+ It is recommended to set the work factor as high as your system can tolerate
+ (from a performance and latency perspective) since higher work factors greatly
+ improve the security against GPU-based attacks. For example, for protecting
+ high value administrator passwords, consider using work factor 15 or 16; at
+ these work factors each bcrypt computation takes several seconds. Since admin
+ logins will be relatively uncommon, it might be acceptable for each login
+ attempt to take some time. As of 2018, a good password cracking rig (with 8
+ NVIDIA 1080 cards) can attempt about 1 billion bcrypt computations per month
+ for work factor 13. For work factor 12, it can do twice as many. For work
+ factor 15, it can do only one quarter as many attempts.
+
+ Due to bugs affecting various implementations of bcrypt, several different
+ variants of the algorithm are defined. As of 2.7.0 Botan supports generating
+ (or checking) the 2a, 2b, and 2y variants. Since Botan has never been
+ affected by any of the bugs which necessitated these version upgrades, all
+ three versions are identical beyond the version identifier. Which variant to
+ use is controlled by the ``bcrypt_version`` argument.
+
+ The bcrypt work factor must be at least 4 (though at this work factor bcrypt
+ is not very secure). The bcrypt format allows up to 31, but Botan currently
+ rejects all work factors greater than 18 since even that work factor requires
+ roughly 15 seconds of computation on a fast machine.
+
+.. cpp:function:: bool check_bcrypt(const std::string& password, \
+ const std::string& hash)
+
+ Takes a password and a bcrypt output and returns true if the
+ password is the same as the one that was used to generate the
+ bcrypt hash.
+
+.. _passhash9:
+
+Passhash9
+----------------------------------------
+
+Botan also provides a password hashing technique called passhash9, in
+``passhash9.h``, which is based on PBKDF2.
+
+Passhash9 hashes look like::
+
+ "$9$AAAKxwMGNPSdPkOKJS07Xutm3+1Cr3ytmbnkjO6LjHzCMcMQXvcT"
+
+This function should be secure with the proper parameters, and will remain in
+the library for the foreseeable future, but it is specific to Botan rather than
+being a widely used password hash. Prefer bcrypt or Argon2.
+
+.. warning::
+
+ This password format string ("$9$") conflicts with the format used
+ for scrypt password hashes on Cisco systems.
+
+.. cpp:function:: std::string generate_passhash9(const std::string& password, \
+ RandomNumberGenerator& rng, uint16_t work_factor = 15, uint8_t alg_id = 4)
+
+ Functions much like ``generate_bcrypt``. The last parameter,
+ ``alg_id``, specifies which PRF to use. Currently defined values are
+ 0: HMAC(SHA-1), 1: HMAC(SHA-256), 2: CMAC(Blowfish), 3: HMAC(SHA-384), 4: HMAC(SHA-512)
+
+ The work factor must be greater than zero and less than 512. This performs
+ 10000 * ``work_factor`` PBKDF2 iterations, using 96 bits of salt taken from
+ ``rng``. Using work factor of 10 or more is recommended.
+
+.. cpp:function:: bool check_passhash9(const std::string& password, \
+ const std::string& hash)
+
+ Functions much like ``check_bcrypt``
diff --git a/comm/third_party/botan/doc/api_ref/pbkdf.rst b/comm/third_party/botan/doc/api_ref/pbkdf.rst
new file mode 100644
index 0000000000..92f59f2780
--- /dev/null
+++ b/comm/third_party/botan/doc/api_ref/pbkdf.rst
@@ -0,0 +1,190 @@
+
+.. _pbkdf:
+
+Password Based Key Derivation
+========================================
+
+Often one needs to convert a human readable password into a cryptographic
+key. It is useful to slow down the computation of these computations in order to
+reduce the speed of brute force search, thus they are parameterized in some
+way which allows their required computation to be tuned.
+
+PBKDF
+---------
+
+:cpp:class:`PBKDF` is the older API for this functionality, presented in header
+``pbkdf.h``. It does not support Scrypt, nor will it be able to support other
+future hashes (such as Argon2) that may be added in the future. In addition,
+this API requires the passphrase be entered as a ``std::string``, which means
+the secret will be stored in memory that will not be zeroed.
+
+.. cpp:class:: PBKDF
+
+ .. cpp:function:: void pbkdf_iterations(uint8_t out[], size_t out_len, \
+ const std::string& passphrase, \
+ const uint8_t salt[], size_t salt_len, \
+ size_t iterations) const
+
+ Run the PBKDF algorithm for the specified number of iterations,
+ with the given salt, and write output to the buffer.
+
+ .. cpp:function:: void pbkdf_timed(uint8_t out[], size_t out_len, \
+ const std::string& passphrase, \
+ const uint8_t salt[], size_t salt_len, \
+ std::chrono::milliseconds msec, \
+ size_t& iterations) const
+
+ Choose (via short run-time benchmark) how many iterations to perform
+ in order to run for roughly msec milliseconds. Writes the number
+ of iterations used to reference argument.
+
+ .. cpp:function:: OctetString derive_key( \
+ size_t output_len, const std::string& passphrase, \
+ const uint8_t* salt, size_t salt_len, \
+ size_t iterations) const
+
+ Computes a key from *passphrase* and the *salt* (of length
+ *salt_len* bytes) using an algorithm-specific interpretation of
+ *iterations*, producing a key of length *output_len*.
+
+ Use an iteration count of at least 10000. The salt should be
+ randomly chosen by a good random number generator (see
+ :ref:`random_number_generators` for how), or at the very least
+ unique to this usage of the passphrase.
+
+ If you call this function again with the same parameters, you will
+ get the same key.
+
+PasswordHash
+--------------
+
+.. versionadded:: 2.8.0
+
+This API has two classes, one representing the algorithm (such as
+"PBKDF2(SHA-256)", or "Scrypt") and the other representing a specific instance
+of the problem which is fully specified (say "Scrypt" with N=8192,r=64,p=8).
+
+.. cpp:class:: PasswordHash
+
+ .. cpp:function:: void derive_key(uint8_t out[], size_t out_len, \
+ const char* password, const size_t password_len, \
+ const uint8_t salt[], size_t salt_len) const
+
+ Derive a key, placing it into output
+
+ .. cpp:function:: std::string to_string() const
+
+ Return a descriptive string including the parameters (iteration count, etc)
+
+The ``PasswordHashFamily`` creates specific instances of ``PasswordHash``:
+
+.. cpp:class:: PasswordHashFamily
+
+ .. cpp:function:: static std::unique_ptr<PasswordHashFamily> create(const std::string& what)
+
+ For example "PBKDF2(SHA-256)", "Scrypt", "OpenPGP-S2K(SHA-384)". Returns
+ null if not available.
+
+ .. cpp:function:: std::unique_ptr<PasswordHash> default_params() const
+
+ 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<PasswordHash> tune(size_t output_len, std::chrono::milliseconds msec) const
+
+ Return a password hash instance tuned to run for approximately ``msec``
+ milliseconds when producing an output of length ``output_len``. (Accuracy
+ may vary, use the command line utility ``botan pbkdf_tune`` to check.)
+
+ .. cpp:function:: std::unique_ptr<PasswordHash> from_params( \
+ size_t i1, size_t i2 = 0, size_t i3 = 0) const
+
+ Create a password hash using some scheme specific format.
+ Eg PBKDF2 and PGP-S2K set iterations in i1
+ Scrypt uses N,r,p in i{1-3}
+ Bcrypt-PBKDF just has iterations
+ Argon2{i,d,id} would use iterations, memory, parallelism for i{1-3}, and Argon2 type is part of the family.
+
+ Values not needed should be set to 0.
+
+Available Schemes
+----------------------
+
+PBKDF2
+^^^^^^^^^^^^
+
+PBKDF2 is the "standard" password derivation scheme, widely implemented in many
+different libraries. It uses HMAC internally.
+
+Scrypt
+^^^^^^^^^^
+
+Scrypt is a relatively newer design which is "memory hard" - in
+addition to requiring large amounts of CPU power it uses a large block
+of memory to compute the hash. This makes brute force attacks using
+ASICs substantially more expensive.
+
+Scrypt is not supported through :cpp:class:`PBKDF`, only :cpp:class:`PasswordHash`,
+starting in 2.8.0. In addition, starting in version 2.7.0, scrypt is available
+with this function:
+
+.. cpp:function:: void scrypt(uint8_t output[], size_t output_len, \
+ const std::string& password, \
+ const uint8_t salt[], size_t salt_len, \
+ size_t N, size_t r, size_t p)
+
+ Computes the Scrypt using the password and salt, and produces an output
+ of arbitrary length.
+
+ The N, r, p parameters control how much work and memory Scrypt
+ uses. N is the primary control of the workfactor, and must be a
+ power of 2. For interactive logins use 32768, for protection of
+ secret keys or backups use 1048576.
+
+ The r parameter controls how 'wide' the internal hashing operation
+ is. It also increases the amount of memory that is used. Values
+ from 1 to 8 are reasonable.
+
+ Setting p parameter to greater than one splits up the work in a way
+ that up to p processors can work in parallel.
+
+ As a general recommendation, use N=32768, r=8, p=1
+
+Argon2
+^^^^^^^^^^
+
+.. versionadded:: 2.11.0
+
+Argon2 is the winner of the PHC (Password Hashing Competition) and
+provides a tunable memory hard PBKDF.
+
+OpenPGP S2K
+^^^^^^^^^^^^
+
+.. warning::
+
+ The OpenPGP algorithm is weak and strange, and should be avoided unless
+ implementing OpenPGP.
+
+There are some oddities about OpenPGP's S2K algorithms that are documented
+here. For one thing, it uses the iteration count in a strange manner; instead of
+specifying how many times to iterate the hash, it tells how many *bytes* should
+be hashed in total (including the salt). So the exact iteration count will
+depend on the size of the salt (which is fixed at 8 bytes by the OpenPGP
+standard, though the implementation will allow any salt size) and the size of
+the passphrase.
+
+To get what OpenPGP calls "Simple S2K", set iterations to 0, and do not specify
+a salt. To get "Salted S2K", again leave the iteration count at 0, but give an
+8-byte salt. "Salted and Iterated S2K" requires an 8-byte salt and some
+iteration count (this should be significantly larger than the size of the
+longest passphrase that might reasonably be used; somewhere from 1024 to 65536
+would probably be about right). Using both a reasonably sized salt and a large
+iteration count is highly recommended to prevent password guessing attempts.
+
+PBKDF1
+^^^^^^^^^^^^
+
+PBKDF1 is an old scheme that can only produce an output length at most
+as long as the hash function. It is deprecated and will be removed in
+a future release. It is not supported through :cpp:class:`PasswordHash`.
diff --git a/comm/third_party/botan/doc/api_ref/pkcs11.rst b/comm/third_party/botan/doc/api_ref/pkcs11.rst
new file mode 100644
index 0000000000..9e0ddfa679
--- /dev/null
+++ b/comm/third_party/botan/doc/api_ref/pkcs11.rst
@@ -0,0 +1,1419 @@
+.. _pkcs11:
+
+PKCS#11
+========================================
+
+.. versionadded:: 1.11.31
+
+|
+
+PKCS#11 is a platform-independent interface for accessing smart cards and
+hardware security modules (HSM). Vendors of PKCS#11 compatible devices usually
+provide a so called middleware or "PKCS#11 module" which implements the PKCS#11
+standard. This middleware translates calls from the platform-independent PKCS#11
+API to device specific calls. So application developers don't have to write smart card
+or HSM specific code for each device they want to support.
+
+ .. note::
+
+ The Botan PKCS#11 interface is implemented against version v2.40 of the standard.
+
+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
+a high level API (see :ref:`pkcs11_high_level`). The low level API provides
+access to all functions that are specified by the standard. The high level API
+represents an object oriented approach to use PKCS#11 compatible devices but
+only provides a subset of the functions described in the standard.
+
+To use the PKCS#11 implementation the ``pkcs11`` module has to be enabled.
+
+ .. note::
+
+ Both PKCS#11 APIs live in the namespace ``Botan::PKCS11``
+
+.. _pkcs11_low_level:
+
+Low Level API
+----------------------------------------
+
+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
+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.
+
+The low level API is implemented by the :cpp:class:`LowLevel` class and can be accessed by
+including the header ``botan/p11.h``.
+
+Preface
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+All constants that belong together in the PKCS#11 standard are grouped into C++
+enum classes. For example the different user types are grouped in the
+:cpp:enum:`UserType` enumeration:
+
+.. cpp:enum-class:: UserType : CK_USER_TYPE
+
+ .. cpp:enumerator:: UserType::SO = CKU_SO
+ .. cpp:enumerator:: UserType::User = CKU_USER
+ .. cpp:enumerator:: UserType::ContextSpecific = CKU_CONTEXT_SPECIFIC
+
+Additionally, all types that are used by the low or high level API are mapped by
+type aliases to more C++ like names. For instance:
+
+.. cpp:type:: FunctionListPtr = CK_FUNCTION_LIST_PTR
+
+.. rubric:: C-API Wrapping
+
+There is at least one method in the :cpp:class:`LowLevel` class that corresponds to a PKCS#11
+function. For example the :cpp:func:`C_GetSlotList` method in the :cpp:class:`LowLevel` class is defined as follows:
+
+.. cpp:class:: LowLevel
+
+ .. cpp:function:: bool C_GetSlotList(Bbool token_present, SlotId* slot_list_ptr, Ulong* count_ptr, ReturnValue* return_value = ThrowException) const
+
+The :cpp:class:`LowLevel` class calls the PKCS#11 function from the function list of the PKCS#11 module:
+
+ .. code-block:: c
+
+ CK_DEFINE_FUNCTION(CK_RV, C_GetSlotList)( CK_BBOOL tokenPresent, CK_SLOT_ID_PTR pSlotList,
+ CK_ULONG_PTR pulCount )
+
+Where it makes sense there is also an overload of the :cpp:class:`LowLevel` method to make usage easier and safer:
+
+ .. cpp:function:: bool C_GetSlotList( bool token_present, std::vector<SlotId>& slot_ids, ReturnValue* return_value = ThrowException ) const
+
+With this overload the user of this API just has to pass a vector of :cpp:type:`SlotId` instead of pointers
+to preallocated memory for the slot list and the number of elements. Additionally, there is no need
+to call the method twice in order to determine the number of elements first.
+
+Another example is the :cpp:func:`C_InitPIN` overload:
+
+ .. cpp:function:: template<typename Talloc> bool C_InitPIN( SessionHandle session, const std::vector<uint8_t, TAlloc>& pin, ReturnValue* return_value = ThrowException ) const
+
+The templated ``pin`` parameter allows to pass the PIN as a ``std::vector<uint8_t>`` or a ``secure_vector<uint8_t>``.
+If used with a ``secure_vector`` it is assured that the memory is securely erased when the ``pin`` object is no longer needed.
+
+Error Handling
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+All possible PKCS#11 return values are represented by the enum class:
+
+.. cpp:enum-class:: ReturnValue : CK_RV
+
+All methods of the :cpp:class:`LowLevel` class have a default parameter ``ReturnValue* return_value = ThrowException``.
+This parameter controls the error handling of all :cpp:class:`LowLevel` methods. The default
+behavior ``return_value = ThrowException`` is to throw an exception if the method does
+not complete successfully. If a non-``NULL`` pointer is passed, ``return_value`` receives the
+return value of the PKCS#11 function and no exception is thrown. In case ``nullptr`` is
+passed as ``return_value``, the exact return value is ignored and the method just returns
+``true`` if the function succeeds and ``false`` otherwise.
+
+Getting started
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+An object of this class can be instantiated by providing a :cpp:type:`FunctionListPtr` to the :cpp:class:`LowLevel` constructor:
+
+ .. cpp:function:: explicit LowLevel(FunctionListPtr ptr)
+
+The :cpp:class:`LowLevel` class provides a static method to retrieve a :cpp:type:`FunctionListPtr`
+from a PKCS#11 module file:
+
+ .. cpp:function:: static bool C_GetFunctionList(Dynamically_Loaded_Library& pkcs11_module, FunctionListPtr* function_list_ptr_ptr, ReturnValue* return_value = ThrowException)
+
+----------
+
+Code Example: Object Instantiation
+
+ .. code-block:: cpp
+
+ Botan::Dynamically_Loaded_Library pkcs11_module( "C:\\pkcs11-middleware\\library.dll" );
+ Botan::PKCS11::FunctionListPtr func_list = nullptr;
+ Botan::PKCS11::LowLevel::C_GetFunctionList( pkcs11_module, &func_list );
+ Botan::PKCS11::LowLevel p11_low_level( func_list );
+
+----------
+
+Code Example: PKCS#11 Module Initialization
+
+ .. code-block:: cpp
+
+ Botan::PKCS11::LowLevel p11_low_level(func_list);
+
+ Botan::PKCS11::C_InitializeArgs init_args = { nullptr, nullptr, nullptr, nullptr,
+ static_cast<CK_FLAGS>(Botan::PKCS11::Flag::OsLockingOk), nullptr };
+
+ p11_low_level.C_Initialize(&init_args);
+
+ // work with the token
+
+ p11_low_level.C_Finalize(nullptr);
+
+More code examples can be found in the test suite in the ``test_pkcs11_low_level.cpp`` file.
+
+.. _pkcs11_high_level:
+
+High Level API
+----------------------------------------
+
+The high level API provides access to the most commonly used PKCS#11 functionality in an
+object oriented manner. Functionality of the high level API includes:
+
+* Loading/unloading of PKCS#11 modules
+* Initialization of tokens
+* Change of PIN/SO-PIN
+* Session management
+* Random number generation
+* Enumeration of objects on the token (certificates, public keys, private keys)
+* Import/export/deletion of certificates
+* Generation/import/export/deletion of RSA and EC public and private keys
+* Encryption/decryption using RSA with support for OAEP and PKCS1-v1_5 (and raw)
+* Signature generation/verification using RSA with support for PSS and PKCS1-v1_5 (and raw)
+* Signature generation/verification using ECDSA
+* Key derivation using ECDH
+
+Module
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The :cpp:class:`Module` class represents a PKCS#11 shared library (module) and is defined in
+``botan/p11_module.h``.
+
+It is constructed from a a file path to a PKCS#11 module and optional :cpp:type:`C_InitializeArgs`:
+
+.. cpp:class:: Module
+
+ .. code-block:: cpp
+
+ Module(const std::string& file_path, C_InitializeArgs init_args =
+ { nullptr, nullptr, nullptr, nullptr, static_cast<CK_FLAGS>(Flag::OsLockingOk), nullptr })
+
+ It loads the shared library and calls :cpp:func:`C_Initialize` with the provided :cpp:type:`C_InitializeArgs`.
+ On destruction of the object :cpp:func:`C_Finalize` is called.
+
+There are two more methods in this class. One is for reloading the shared library
+and reinitializing the PKCS#11 module:
+
+ .. code-block:: cpp
+
+ void reload(C_InitializeArgs init_args =
+ { nullptr, nullptr, nullptr, nullptr, static_cast< CK_FLAGS >(Flag::OsLockingOk), nullptr });
+
+The other one is for getting general information about the PKCS#11 module:
+
+ .. cpp:function:: Info get_info() const
+
+ This function calls :cpp:func:`C_GetInfo` internally.
+
+----------
+
+Code example:
+
+ .. code-block:: cpp
+
+ Botan::PKCS11::Module module( "C:\\pkcs11-middleware\\library.dll" );
+
+ // Sometimes useful if a newly connected token is not detected by the PKCS#11 module
+ module.reload();
+
+ Botan::PKCS11::Info info = module.get_info();
+
+ // print library version
+ std::cout << std::to_string( info.libraryVersion.major ) << "."
+ << std::to_string( info.libraryVersion.minor ) << std::endl;
+
+Slot
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The :cpp:class:`Slot` class represents a PKCS#11 slot and is defined in
+``botan/p11_slot.h``.
+
+A PKCS#11 slot is usually a smart card reader that potentially contains a token.
+
+.. cpp:class:: Slot
+
+ .. cpp:function:: Slot(Module& module, SlotId slot_id)
+
+ To instantiate this class a reference to a :cpp:class:`Module` object and a ``slot_id`` have to be passed
+ to the constructor.
+
+ .. cpp:function:: static std::vector<SlotId> get_available_slots(Module& module, bool token_present)
+
+ Retrieve available slot ids by calling this static method.
+
+ The parameter ``token_present`` controls whether all slots or only slots with a
+ token attached are returned by this method. This method calls :cpp:func:`C_GetSlotList()`.
+
+ .. cpp:function:: SlotInfo get_slot_info() const
+
+ Returns information about the slot. Calls :cpp:func:`C_GetSlotInfo`.
+
+ .. cpp:function:: TokenInfo get_token_info() const
+
+ Obtains information about a particular token in the system. Calls :cpp:func:`C_GetTokenInfo`.
+
+ .. cpp:function:: std::vector<MechanismType> get_mechanism_list() const
+
+ Obtains a list of mechanism types supported by the slot. Calls :cpp:func:`C_GetMechanismList`.
+
+ .. cpp:function:: MechanismInfo get_mechanism_info(MechanismType mechanism_type) const
+
+ Obtains information about a particular mechanism possibly supported by a slot.
+ Calls :cpp:func:`C_GetMechanismInfo`.
+
+ .. cpp:function:: void initialize(const std::string& label, const secure_string& so_pin) const
+
+ Calls :cpp:func:`C_InitToken` to initialize the token. The ``label`` must not exceed 32 bytes.
+ The current PIN of the security officer must be passed in ``so_pin`` if the token
+ is reinitialized or if it's a factory new token, the ``so_pin`` that is passed will initially be set.
+
+----------
+
+Code example:
+
+ .. code-block:: cpp
+
+ // only slots with connected token
+ std::vector<Botan::PKCS11::SlotId> slots = Botan::PKCS11::Slot::get_available_slots( module, true );
+
+ // use first slot
+ Botan::PKCS11::Slot slot( module, slots.at( 0 ) );
+
+ // print firmware version of the slot
+ 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 ) << std::endl;
+
+ // print firmware version of the token
+ 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 ) << std::endl;
+
+ // retrieve all mechanisms supported by the token
+ std::vector<Botan::PKCS11::MechanismType> mechanisms = slot.get_mechanism_list();
+
+ // retrieve information about a particular mechanism
+ Botan::PKCS11::MechanismInfo mech_info =
+ slot.get_mechanism_info( Botan::PKCS11::MechanismType::RsaPkcsOaep );
+
+ // maximum RSA key length supported:
+ std::cout << mech_info.ulMaxKeySize << std::endl;
+
+ // initialize the token
+ Botan::PKCS11::secure_string so_pin( 8, '0' );
+ slot.initialize( "Botan PKCS11 documentation test label", so_pin );
+
+Session
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The :cpp:class:`Session` class represents a PKCS#11 session and is defined in ``botan/p11_session.h``.
+
+A session is a logical connection between an application and a token.
+
+.. cpp:class:: Session
+
+ There are two constructors to create a new session and one constructor to
+ take ownership of an existing session. The destructor calls
+ :cpp:func:`C_Logout` if a user is logged in to this session and always
+ :cpp:func:`C_CloseSession`.
+
+ .. cpp:function:: Session(Slot& slot, bool read_only)
+
+ To initialize a session object a :cpp:class:`Slot` has to be specified on which the session
+ should operate. ``read_only`` specifies whether the session should be read only or read write.
+ Calls :cpp:func:`C_OpenSession`.
+
+ .. cpp:function:: Session(Slot& slot, Flags flags, VoidPtr callback_data, Notify notify_callback)
+
+ Creates a new session by passing a :cpp:class:`Slot`, session ``flags``, ``callback_data`` and a
+ ``notify_callback``. Calls :cpp:func:`C_OpenSession`.
+
+ .. cpp:function:: Session(Slot& slot, SessionHandle handle)
+
+ Takes ownership of an existing session by passing :cpp:class:`Slot` and a session ``handle``.
+
+ .. cpp:function:: SessionHandle release()
+
+ Returns the released :cpp:type:`SessionHandle`
+
+ .. cpp:function:: void login(UserType userType, const secure_string& pin)
+
+ Login to this session by passing :cpp:enum:`UserType` and ``pin``. Calls :cpp:func:`C_Login`.
+
+ .. cpp:function:: void logoff()
+
+ Logout from this session. Not mandatory because on destruction of the :cpp:class:`Session` object
+ this is done automatically.
+
+ .. cpp:function:: SessionInfo get_info() const
+
+ Returns information about this session. Calls :cpp:func:`C_GetSessionInfo`.
+
+ .. cpp:function:: void set_pin(const secure_string& old_pin, const secure_string& new_pin) const
+
+ Calls :cpp:func:`C_SetPIN` to change the PIN of the logged in user using the ``old_pin``.
+
+ .. cpp:function:: void init_pin(const secure_string& new_pin)
+
+ Calls :cpp:func:`C_InitPIN` to change or initialize the PIN using the SO_PIN (requires a logged in session).
+
+----------
+
+Code example:
+
+ .. code-block:: cpp
+
+ // open read only session
+ {
+ Botan::PKCS11::Session read_only_session( slot, true );
+ }
+
+ // open read write session
+ {
+ Botan::PKCS11::Session read_write_session( slot, false );
+ }
+
+ // open read write session by passing flags
+ {
+ 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 );
+ }
+
+ // move ownership of a session
+ {
+ Botan::PKCS11::Session session( slot, false );
+ Botan::PKCS11::SessionHandle handle = session.release();
+
+ Botan::PKCS11::Session session2( slot, handle );
+ }
+
+ Botan::PKCS11::Session session( slot, false );
+
+ // get session info
+ Botan::PKCS11::SessionInfo info = session.get_info();
+ std::cout << info.slotID << std::endl;
+
+ // login
+ 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' };
+ 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' };
+ session.login( Botan::PKCS11::UserType::SO, so_pin );
+
+ // change pin to old pin
+ session.init_pin( pin );
+
+Objects
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+PKCS#11 objects consist of various attributes (:c:type:`CK_ATTRIBUTE`). For example :c:macro:`CKA_TOKEN`
+describes if a PKCS#11 object is a session object or a token object. The helper class :cpp:class:`AttributeContainer`
+helps with storing these attributes. The class is defined in ``botan/p11_object.h``.
+
+.. cpp:class:: AttributeContainer
+
+Attributes can be set in an :cpp:class:`AttributeContainer` by various ``add_`` methods:
+
+ .. cpp:function:: void add_class(ObjectClass object_class)
+
+ Add a class attribute (:c:macro:`CKA_CLASS` / :cpp:enumerator:`AttributeType::Class`)
+
+ .. cpp:function:: void add_string(AttributeType attribute, const std::string& value)
+
+ Add a string attribute (e.g. :c:macro:`CKA_LABEL` / :cpp:enumerator:`AttributeType::Label`).
+
+ .. cpp:function:: void AttributeContainer::add_binary(AttributeType attribute, const uint8_t* value, size_t length)
+
+ Add a binary attribute (e.g. :c:macro:`CKA_ID` / :cpp:enumerator:`AttributeType::Id`).
+
+ .. cpp:function:: template<typename TAlloc> void AttributeContainer::add_binary(AttributeType attribute, const std::vector<uint8_t, TAlloc>& binary)
+
+ Add a binary attribute by passing a ``vector``/``secure_vector`` (e.g. :c:macro:`CKA_ID` / :cpp:enumerator:`AttributeType::Id`).
+
+ .. cpp:function:: void AttributeContainer::add_bool(AttributeType attribute, bool value)
+
+ Add a bool attribute (e.g. :c:macro:`CKA_SENSITIVE` / :cpp:enumerator:`AttributeType::Sensitive`).
+
+ .. cpp:function:: template<typename T> void AttributeContainer::add_numeric(AttributeType attribute, T value)
+
+ Add a numeric attribute (e.g. :c:macro:`CKA_MODULUS_BITS` / :cpp:enumerator:`AttributeType::ModulusBits`).
+
+.. rubric:: Object Properties
+
+The PKCS#11 standard defines the mandatory and optional attributes for each object class.
+The mandatory and optional attribute requirements are mapped in so called property classes.
+Mandatory attributes are set in the constructor, optional attributes can be set via ``set_`` methods.
+
+In the top hierarchy is the :cpp:class:`ObjectProperties` class which inherits from the :cpp:class:`AttributeContainer`.
+This class represents the common attributes of all PKCS#11 objects.
+
+.. cpp:class:: ObjectProperties : public AttributeContainer
+
+The constructor is defined as follows:
+
+ .. cpp:function:: ObjectProperties(ObjectClass object_class)
+
+ Every PKCS#11 object needs an object class attribute.
+
+The next level defines the :cpp:class:`StorageObjectProperties` class which inherits from
+:cpp:class:`ObjectProperties`.
+
+.. cpp:class:: StorageObjectProperties : public ObjectProperties
+
+The only mandatory attribute is the object class, so the constructor is
+defined as follows:
+
+ .. cpp:function:: StorageObjectProperties(ObjectClass object_class)
+
+But in contrast to the :cpp:class:`ObjectProperties` class there are various setter methods. For example to
+set the :cpp:enumerator:`AttributeType::Label`:
+
+ .. cpp:function:: void set_label(const std::string& label)
+
+ Sets the label description of the object (RFC2279 string).
+
+The remaining hierarchy is defined as follows:
+
+* :cpp:class:`DataObjectProperties` inherits from :cpp:class:`StorageObjectProperties`
+* :cpp:class:`CertificateProperties` inherits from :cpp:class:`StorageObjectProperties`
+* :cpp:class:`DomainParameterProperties` inherits from :cpp:class:`StorageObjectProperties`
+* :cpp:class:`KeyProperties` inherits from :cpp:class:`StorageObjectProperties`
+* :cpp:class:`PublicKeyProperties` inherits from :cpp:class:`KeyProperties`
+* :cpp:class:`PrivateKeyProperties` inherits from :cpp:class:`KeyProperties`
+* :cpp:class:`SecretKeyProperties` inherits from :cpp:class:`KeyProperties`
+
+PKCS#11 objects themselves are represented by the :cpp:class:`Object` class.
+
+.. cpp:class:: Object
+
+Following constructors are defined:
+
+ .. cpp:function:: Object(Session& session, ObjectHandle handle)
+
+ Takes ownership over an existing object.
+
+ .. cpp:function:: Object(Session& session, const ObjectProperties& obj_props)
+
+ Creates a new object with the :cpp:class:`ObjectProperties` provided in ``obj_props``.
+
+The other methods are:
+
+ .. cpp:function:: secure_vector<uint8_t> get_attribute_value(AttributeType attribute) const
+
+ Returns the value of the given attribute (using :cpp:func:`C_GetAttributeValue`)
+
+ .. cpp:function:: void set_attribute_value(AttributeType attribute, const secure_vector<uint8_t>& value) const
+
+ Sets the given value for the attribute (using :cpp:func:`C_SetAttributeValue`)
+
+ .. cpp:function:: void destroy() const
+
+ Destroys the object.
+
+ .. cpp:function:: ObjectHandle copy(const AttributeContainer& modified_attributes) const
+
+ Allows to copy the object with modified attributes.
+
+And static methods to search for objects:
+
+ .. cpp:function:: template<typename T> static std::vector<T> search(Session& session, const std::vector<Attribute>& search_template)
+
+ Searches for all objects of the given type that match ``search_template``.
+
+ .. cpp:function:: template<typename T> static std::vector<T> search(Session& session, const std::string& label)
+
+ Searches for all objects of the given type using the label (:c:macro:`CKA_LABEL`).
+
+ .. cpp:function:: template<typename T> static std::vector<T> search(Session& session, const std::vector<uint8_t>& id)
+
+ Searches for all objects of the given type using the id (:c:macro:`CKA_ID`).
+
+ .. cpp:function:: template<typename T> static std::vector<T> search(Session& session, const std::string& label, const std::vector<uint8_t>& id)
+
+ Searches for all objects of the given type using the label (:c:macro:`CKA_LABEL`) and id (:c:macro:`CKA_ID`).
+
+ .. cpp:function:: template<typename T> static std::vector<T> search(Session& session)
+
+ Searches for all objects of the given type.
+
+.. rubric:: The ObjectFinder
+
+Another way for searching objects is to use the :cpp:class:`ObjectFinder` class. This class
+manages calls to the ``C_FindObjects*`` functions: :cpp:func:`C_FindObjectsInit`, :cpp:func:`C_FindObjects`
+and :cpp:func:`C_FindObjectsFinal`.
+
+.. cpp:class:: ObjectFinder
+
+The constructor has the following signature:
+
+ .. cpp:function:: ObjectFinder(Session& session, const std::vector<Attribute>& search_template)
+
+ A search can be prepared with an :cpp:class:`ObjectSearcher` by passing a :cpp:class:`Session` and a ``search_template``.
+
+The actual search operation is started by calling the :cpp:func:`find` method:
+
+ .. cpp:function:: std::vector<ObjectHandle> find(std::uint32_t max_count = 100) const
+
+ Starts or continues a search for token and session objects that match a template. ``max_count``
+ specifies the maximum number of search results (object handles) that are returned.
+
+ .. cpp:function:: void finish()
+
+ Finishes the search operation manually to allow a new :cpp:class:`ObjectFinder` to exist.
+ Otherwise the search is finished by the destructor.
+
+----------
+
+Code example:
+
+ .. code-block:: cpp
+
+ // create an simple data object
+ Botan::secure_vector<uint8_t> value = { 0x00, 0x01 ,0x02, 0x03 };
+ std::size_t id = 1337;
+ std::string label = "test data object";
+
+ // set properties of the new object
+ Botan::PKCS11::DataObjectProperties data_obj_props;
+ data_obj_props.set_label( label );
+ data_obj_props.set_value( value );
+ data_obj_props.set_token( true );
+ data_obj_props.set_modifiable( true );
+ data_obj_props.set_object_id( Botan::DER_Encoder().encode( id ).get_contents_unlocked() );
+
+ // create the object
+ 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 );
+
+ // set a new label
+ 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" );
+ Botan::PKCS11::ObjectHandle copied_obj_handle = data_obj.copy( copy_attributes );
+
+ // search for an object
+ Botan::PKCS11::AttributeContainer search_template;
+ search_template.add_string( Botan::PKCS11::AttributeType::Label, "Botan" );
+ auto found_objs =
+ Botan::PKCS11::Object::search<Botan::PKCS11::Object>( session, search_template.attributes() );
+
+ // destroy the object
+ data_obj.destroy();
+
+RSA
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+PKCS#11 RSA support is implemented in ``<botan/p11_rsa.h>``.
+
+.. rubric:: RSA Public Keys
+
+PKCS#11 RSA public keys are provided by the class :cpp:class:`PKCS11_RSA_PublicKey`. This class
+inherits from :cpp:class:`RSA_PublicKey` and :cpp:class:`Object`. Furthermore there are two property classes defined
+to generate and import RSA public keys analogous to the other property classes described
+before: :cpp:class:`RSA_PublicKeyGenerationProperties` and :cpp:class:`RSA_PublicKeyImportProperties`.
+
+.. cpp:class:: PKCS11_RSA_PublicKey : public RSA_PublicKey, public Object
+
+ .. cpp:function:: PKCS11_RSA_PublicKey(Session& session, ObjectHandle handle)
+
+ Existing PKCS#11 RSA public keys can be used by providing an :cpp:type:`ObjectHandle` to the
+ constructor.
+
+ .. cpp:function:: PKCS11_RSA_PublicKey(Session& session, const RSA_PublicKeyImportProperties& pubkey_props)
+
+ This constructor can be used to import an existing RSA public key with the :cpp:class:`RSA_PublicKeyImportProperties`
+ passed in ``pubkey_props`` to the token.
+
+.. rubric:: RSA Private Keys
+
+The support for PKCS#11 RSA private keys is implemented in a similar way. There are two property
+classes: :cpp:class:`RSA_PrivateKeyGenerationProperties` and :cpp:class:`RSA_PrivateKeyImportProperties`. The :cpp:class:`PKCS11_RSA_PrivateKey`
+class implements the actual support for PKCS#11 RSA private keys. This class inherits from :cpp:class:`Private_Key`,
+:cpp:class:`RSA_PublicKey` and :cpp:class:`Object`. In contrast to the public key class there is a third constructor
+to generate private keys directly on the token or in the session and one method to export private keys.
+
+.. cpp:class:: PKCS11_RSA_PrivateKey : public Private_Key, public RSA_PublicKey, public Object
+
+ .. cpp:function:: PKCS11_RSA_PrivateKey(Session& session, ObjectHandle handle)
+
+ Existing PKCS#11 RSA private keys can be used by providing an :cpp:type:`ObjectHandle` to the
+ constructor.
+
+ .. cpp:function:: PKCS11_RSA_PrivateKey(Session& session, const RSA_PrivateKeyImportProperties& priv_key_props)
+
+ This constructor can be used to import an existing RSA private key with the :cpp:class:`RSA_PrivateKeyImportProperties`
+ passed in ``priv_key_props`` to the token.
+
+ .. cpp:function:: PKCS11_RSA_PrivateKey(Session& session, uint32_t bits, const RSA_PrivateKeyGenerationProperties& priv_key_props)
+
+ Generates a new PKCS#11 RSA private key with bit length provided in ``bits`` and the :cpp:class:`RSA_PrivateKeyGenerationProperties`
+ passed in ``priv_key_props``.
+
+ .. cpp:function:: RSA_PrivateKey export_key() const
+
+ Returns the exported :cpp:class:`RSA_PrivateKey`.
+
+PKCS#11 RSA key pairs can be generated with the following free function:
+
+ .. cpp:function:: PKCS11_RSA_KeyPair PKCS11::generate_rsa_keypair(Session& session, const RSA_PublicKeyGenerationProperties& pub_props, const RSA_PrivateKeyGenerationProperties& priv_props)
+
+----------
+
+Code example:
+
+ .. code-block:: cpp
+
+ 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 );
+
+ // set the private key import properties
+ Botan::PKCS11::RSA_PrivateKeyImportProperties
+ priv_import_props( priv_key_sw.get_n(), priv_key_sw.get_d() );
+
+ priv_import_props.set_pub_exponent( priv_key_sw.get_e() );
+ priv_import_props.set_prime_1( priv_key_sw.get_p() );
+ priv_import_props.set_prime_2( priv_key_sw.get_q() );
+ priv_import_props.set_coefficient( priv_key_sw.get_c() );
+ priv_import_props.set_exponent_1( priv_key_sw.get_d1() );
+ priv_import_props.set_exponent_2( priv_key_sw.get_d2() );
+
+ priv_import_props.set_token( true );
+ priv_import_props.set_private( true );
+ priv_import_props.set_decrypt( true );
+ priv_import_props.set_sign( true );
+
+ // import
+ 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();
+
+ /************ import RSA public key *************/
+
+ // set the public key import properties
+ Botan::PKCS11::RSA_PublicKeyImportProperties pub_import_props( priv_key.get_n(), priv_key.get_e() );
+ pub_import_props.set_token( true );
+ pub_import_props.set_encrypt( true );
+ pub_import_props.set_private( false );
+
+ // import
+ Botan::PKCS11::PKCS11_RSA_PublicKey public_key( session, pub_import_props );
+
+ /************ generate RSA private key *************/
+
+ Botan::PKCS11::RSA_PrivateKeyGenerationProperties priv_generate_props;
+ priv_generate_props.set_token( true );
+ priv_generate_props.set_private( true );
+ priv_generate_props.set_sign( true );
+ 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 );
+
+ /************ generate RSA key pair *************/
+
+ Botan::PKCS11::RSA_PublicKeyGenerationProperties pub_generate_props( 2048UL );
+ pub_generate_props.set_pub_exponent();
+ pub_generate_props.set_label( "BOTAN_TEST_RSA_PUB_KEY" );
+ pub_generate_props.set_token( true );
+ pub_generate_props.set_encrypt( true );
+ pub_generate_props.set_verify( true );
+ pub_generate_props.set_private( false );
+
+ Botan::PKCS11::PKCS11_RSA_KeyPair rsa_keypair =
+ Botan::PKCS11::generate_rsa_keypair( session, pub_generate_props, priv_generate_props );
+
+ /************ RSA encrypt *************/
+
+ Botan::secure_vector<uint8_t> plaintext = { 0x00, 0x01, 0x02, 0x03 };
+ 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" );
+ plaintext = decryptor.decrypt( ciphertext );
+
+ /************ RSA sign *************/
+
+ Botan::PK_Signer signer( rsa_keypair.second, rng, "EMSA4(SHA-256)", Botan::IEEE_1363 );
+ auto signature = signer.sign_message( plaintext, rng );
+
+ /************ RSA verify *************/
+
+ Botan::PK_Verifier verifier( rsa_keypair.first, "EMSA4(SHA-256)", Botan::IEEE_1363 );
+ auto ok = verifier.verify_message( plaintext, signature );
+
+ECDSA
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+PKCS#11 ECDSA support is implemented in ``<botan/p11_ecdsa.h>``.
+
+.. rubric:: ECDSA Public Keys
+
+PKCS#11 ECDSA public keys are provided by the class :cpp:class:`PKCS11_ECDSA_PublicKey`. This class
+inherits from :cpp:class:`PKCS11_EC_PublicKey` and :cpp:class:`ECDSA_PublicKey`. The necessary property classes
+are defined in ``<botan/p11_ecc_key.h>``. For public keys there are :cpp:class:`EC_PublicKeyGenerationProperties`
+and :cpp:class:`EC_PublicKeyImportProperties`.
+
+.. cpp:class:: PKCS11_ECDSA_PublicKey : public PKCS11_EC_PublicKey, public virtual ECDSA_PublicKey
+
+ .. cpp:function:: PKCS11_ECDSA_PublicKey(Session& session, ObjectHandle handle)
+
+ Existing PKCS#11 ECDSA private keys can be used by providing an :cpp:type:`ObjectHandle` to the
+ constructor.
+
+ .. cpp:function:: PKCS11_ECDSA_PublicKey(Session& session, const EC_PublicKeyImportProperties& props)
+
+ This constructor can be used to import an existing ECDSA public key with the :cpp:class:`EC_PublicKeyImportProperties`
+ passed in ``props`` to the token.
+
+ .. cpp:function:: ECDSA_PublicKey PKCS11_ECDSA_PublicKey::export_key() const
+
+ Returns the exported :cpp:class:`ECDSA_PublicKey`.
+
+.. rubric:: ECDSA Private Keys
+
+The class :cpp:class:`PKCS11_ECDSA_PrivateKey` inherits from :cpp:class:`PKCS11_EC_PrivateKey` and implements support
+for PKCS#11 ECDSA private keys. There are two property classes for key generation
+and import: :cpp:class:`EC_PrivateKeyGenerationProperties` and :cpp:class:`EC_PrivateKeyImportProperties`.
+
+.. cpp:class:: PKCS11_ECDSA_PrivateKey : public PKCS11_EC_PrivateKey
+
+ .. cpp:function:: PKCS11_ECDSA_PrivateKey(Session& session, ObjectHandle handle)
+
+ Existing PKCS#11 ECDSA private keys can be used by providing an :cpp:type:`ObjectHandle` to the
+ constructor.
+
+ .. cpp:function:: PKCS11_ECDSA_PrivateKey(Session& session, const EC_PrivateKeyImportProperties& props)
+
+ This constructor can be used to import an existing ECDSA private key with the :cpp:class:`EC_PrivateKeyImportProperties`
+ passed in ``props`` to the token.
+
+ .. cpp:function:: PKCS11_ECDSA_PrivateKey(Session& session, const std::vector<uint8_t>& ec_params, const EC_PrivateKeyGenerationProperties& props)
+
+ This constructor can be used to generate a new ECDSA private key with the :cpp:class:`EC_PrivateKeyGenerationProperties`
+ passed in ``props`` on the token. The ``ec_params`` parameter is the DER-encoding of an
+ ANSI X9.62 Parameters value.
+
+ .. cpp:function:: ECDSA_PrivateKey export_key() const
+
+ Returns the exported :cpp:class:`ECDSA_PrivateKey`.
+
+PKCS#11 ECDSA key pairs can be generated with the following free function:
+
+ .. cpp:function:: PKCS11_ECDSA_KeyPair PKCS11::generate_ecdsa_keypair(Session& session, const EC_PublicKeyGenerationProperties& pub_props, const EC_PrivateKeyGenerationProperties& priv_props)
+
+----------
+
+Code example:
+
+ .. code-block:: cpp
+
+ Botan::PKCS11::secure_string pin = { '1', '2', '3', '4', '5', '6' };
+ session.login( Botan::PKCS11::UserType::User, pin );
+
+ /************ import ECDSA private key *************/
+
+ // create private key in software
+ Botan::AutoSeeded_RNG rng;
+
+ Botan::ECDSA_PrivateKey priv_key_sw( rng, Botan::EC_Group( "secp256r1" ) );
+ priv_key_sw.set_parameter_encoding( Botan::EC_Group_Encoding::EC_DOMPAR_ENC_OID );
+
+ // set the private key import properties
+ Botan::PKCS11::EC_PrivateKeyImportProperties priv_import_props(
+ priv_key_sw.DER_domain(), priv_key_sw.private_value() );
+
+ priv_import_props.set_token( true );
+ priv_import_props.set_private( true );
+ priv_import_props.set_sign( true );
+ priv_import_props.set_extractable( true );
+
+ // label
+ std::string label = "test ECDSA key";
+ priv_import_props.set_label( label );
+
+ // import to card
+ 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();
+
+ /************ import ECDSA public key *************/
+
+ // import to card
+ Botan::PKCS11::EC_PublicKeyImportProperties pub_import_props( priv_key_sw.DER_domain(),
+ Botan::DER_Encoder().encode( EC2OSP( priv_key_sw.public_point(), Botan::PointGFp::UNCOMPRESSED ),
+ Botan::OCTET_STRING ).get_contents_unlocked() );
+
+ pub_import_props.set_token( true );
+ pub_import_props.set_verify( true );
+ pub_import_props.set_private( false );
+
+ // label
+ label = "test ECDSA pub key";
+ pub_import_props.set_label( label );
+
+ 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();
+
+ /************ generate PKCS#11 ECDSA private key *************/
+ Botan::PKCS11::EC_PrivateKeyGenerationProperties priv_generate_props;
+ priv_generate_props.set_token( true );
+ priv_generate_props.set_private( true );
+ priv_generate_props.set_sign( true );
+
+ Botan::PKCS11::PKCS11_ECDSA_PrivateKey pk( session,
+ Botan::EC_Group( "secp256r1" ).DER_encode( Botan::EC_Group_Encoding::EC_DOMPAR_ENC_OID ),
+ priv_generate_props );
+
+ /************ generate PKCS#11 ECDSA key pair *************/
+
+ Botan::PKCS11::EC_PublicKeyGenerationProperties pub_generate_props(
+ Botan::EC_Group( "secp256r1" ).DER_encode(Botan::EC_Group_Encoding::EC_DOMPAR_ENC_OID ) );
+
+ pub_generate_props.set_label( "BOTAN_TEST_ECDSA_PUB_KEY" );
+ pub_generate_props.set_token( true );
+ pub_generate_props.set_verify( true );
+ pub_generate_props.set_private( false );
+ pub_generate_props.set_modifiable( true );
+
+ 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 *************/
+
+ std::vector<uint8_t> plaintext( 20, 0x01 );
+
+ Botan::PK_Signer signer( key_pair.second, rng, "Raw", Botan::IEEE_1363, "pkcs11" );
+ auto signature = signer.sign_message( plaintext, rng );
+
+ Botan::PK_Verifier token_verifier( key_pair.first, "Raw", Botan::IEEE_1363, "pkcs11" );
+ bool ecdsa_ok = token_verifier.verify_message( plaintext, signature );
+
+ECDH
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+PKCS#11 ECDH support is implemented in ``<botan/p11_ecdh.h>``.
+
+.. rubric:: ECDH Public Keys
+
+PKCS#11 ECDH public keys are provided by the class :cpp:class:`PKCS11_ECDH_PublicKey`. This class
+inherits from :cpp:class:`PKCS11_EC_PublicKey`. The necessary property classes
+are defined in ``<botan/p11_ecc_key.h>``. For public keys there are :cpp:class:`EC_PublicKeyGenerationProperties`
+and :cpp:class:`EC_PublicKeyImportProperties`.
+
+.. cpp:class:: PKCS11_ECDH_PublicKey : public PKCS11_EC_PublicKey
+
+ .. cpp:function:: PKCS11_ECDH_PublicKey(Session& session, ObjectHandle handle)
+
+ Existing PKCS#11 ECDH private keys can be used by providing an :cpp:type:`ObjectHandle` to the
+ constructor.
+
+ .. cpp:function:: PKCS11_ECDH_PublicKey(Session& session, const EC_PublicKeyImportProperties& props)
+
+ This constructor can be used to import an existing ECDH public key with the :cpp:class:`EC_PublicKeyImportProperties`
+ passed in ``props`` to the token.
+
+ .. cpp:function:: ECDH_PublicKey export_key() const
+
+ Returns the exported :cpp:class:`ECDH_PublicKey`.
+
+.. rubric:: ECDH Private Keys
+
+The class :cpp:class:`PKCS11_ECDH_PrivateKey` inherits from :cpp:class:`PKCS11_EC_PrivateKey` and :cpp:class:`PK_Key_Agreement_Key`
+and implements support for PKCS#11 ECDH private keys. There are two
+property classes. One for key generation and one for import: :cpp:class:`EC_PrivateKeyGenerationProperties` and
+:cpp:class:`EC_PrivateKeyImportProperties`.
+
+.. cpp:class:: PKCS11_ECDH_PrivateKey : public virtual PKCS11_EC_PrivateKey, public virtual PK_Key_Agreement_Key
+
+ .. cpp:function:: PKCS11_ECDH_PrivateKey(Session& session, ObjectHandle handle)
+
+ Existing PKCS#11 ECDH private keys can be used by providing an :cpp:type:`ObjectHandle` to the
+ constructor.
+
+ .. cpp:function:: PKCS11_ECDH_PrivateKey(Session& session, const EC_PrivateKeyImportProperties& props)
+
+ This constructor can be used to import an existing ECDH private key with the :cpp:class:`EC_PrivateKeyImportProperties`
+ passed in ``props`` to the token.
+
+ .. cpp:function:: PKCS11_ECDH_PrivateKey(Session& session, const std::vector<uint8_t>& ec_params, const EC_PrivateKeyGenerationProperties& props)
+
+ This constructor can be used to generate a new ECDH private key with the :cpp:class:`EC_PrivateKeyGenerationProperties`
+ passed in ``props`` on the token. The ``ec_params`` parameter is the DER-encoding of an
+ ANSI X9.62 Parameters value.
+
+ .. cpp:function:: ECDH_PrivateKey export_key() const
+
+ Returns the exported :cpp:class:`ECDH_PrivateKey`.
+
+PKCS#11 ECDH key pairs can be generated with the following free function:
+
+.. cpp:function:: PKCS11_ECDH_KeyPair PKCS11::generate_ecdh_keypair(Session& session, const EC_PublicKeyGenerationProperties& pub_props, const EC_PrivateKeyGenerationProperties& priv_props)
+
+----------
+
+Code example:
+
+ .. code-block:: cpp
+
+ Botan::PKCS11::secure_string pin = { '1', '2', '3', '4', '5', '6' };
+ session.login( Botan::PKCS11::UserType::User, pin );
+
+ /************ import ECDH private key *************/
+
+ Botan::AutoSeeded_RNG rng;
+
+ // create private key in software
+ Botan::ECDH_PrivateKey priv_key_sw( rng, Botan::EC_Group( "secp256r1" ) );
+ priv_key_sw.set_parameter_encoding( Botan::EC_Group_Encoding::EC_DOMPAR_ENC_OID );
+
+ // set import properties
+ Botan::PKCS11::EC_PrivateKeyImportProperties priv_import_props(
+ priv_key_sw.DER_domain(), priv_key_sw.private_value() );
+
+ priv_import_props.set_token( true );
+ priv_import_props.set_private( true );
+ priv_import_props.set_derive( true );
+ priv_import_props.set_extractable( true );
+
+ // label
+ std::string label = "test ECDH key";
+ priv_import_props.set_label( label );
+
+ // import to card
+ Botan::PKCS11::PKCS11_ECDH_PrivateKey priv_key( session, priv_import_props );
+
+ /************ export ECDH private key *************/
+ Botan::ECDH_PrivateKey exported = priv_key.export_key();
+
+ /************ import ECDH public key *************/
+
+ // set import properties
+ Botan::PKCS11::EC_PublicKeyImportProperties pub_import_props( priv_key_sw.DER_domain(),
+ Botan::DER_Encoder().encode( EC2OSP( priv_key_sw.public_point(), Botan::PointGFp::UNCOMPRESSED ),
+ Botan::OCTET_STRING ).get_contents_unlocked() );
+
+ pub_import_props.set_token( true );
+ pub_import_props.set_private( false );
+ pub_import_props.set_derive( true );
+
+ // label
+ label = "test ECDH pub key";
+ pub_import_props.set_label( label );
+
+ // import
+ Botan::PKCS11::PKCS11_ECDH_PublicKey pub_key( session, pub_import_props );
+
+ /************ export ECDH private key *************/
+ Botan::ECDH_PublicKey exported_pub = pub_key.export_key();
+
+ /************ generate ECDH private key *************/
+
+ Botan::PKCS11::EC_PrivateKeyGenerationProperties priv_generate_props;
+ priv_generate_props.set_token( true );
+ priv_generate_props.set_private( true );
+ priv_generate_props.set_derive( true );
+
+ Botan::PKCS11::PKCS11_ECDH_PrivateKey priv_key2( session,
+ Botan::EC_Group( "secp256r1" ).DER_encode( Botan::EC_Group_Encoding::EC_DOMPAR_ENC_OID ),
+ priv_generate_props );
+
+ /************ generate ECDH key pair *************/
+
+ Botan::PKCS11::EC_PublicKeyGenerationProperties pub_generate_props(
+ Botan::EC_Group( "secp256r1" ).DER_encode( Botan::EC_Group_Encoding::EC_DOMPAR_ENC_OID ) );
+
+ pub_generate_props.set_label( label + "_PUB_KEY" );
+ pub_generate_props.set_token( true );
+ pub_generate_props.set_derive( true );
+ pub_generate_props.set_private( false );
+ pub_generate_props.set_modifiable( true );
+
+ 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 = 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" );
+
+ Botan::SymmetricKey alice_key = ka.derive_key( 32,
+ Botan::unlock( Botan::EC2OSP( key_pair_other.first.public_point(),
+ Botan::PointGFp::UNCOMPRESSED ) ) );
+
+ Botan::SymmetricKey bob_key = kb.derive_key( 32,
+ Botan::unlock( Botan::EC2OSP( key_pair.first.public_point(),
+ Botan::PointGFp::UNCOMPRESSED ) ) );
+
+ bool eq = alice_key == bob_key;
+
+RNG
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The PKCS#11 RNG is defined in ``<botan/p11_randomgenerator.h>``. The class :cpp:class:`PKCS11_RNG`
+implements the :cpp:class:`Hardware_RNG` interface.
+
+.. cpp:class:: PKCS11_RNG : public Hardware_RNG
+
+ .. cpp:function:: PKCS11_RNG(Session& session)
+
+ A PKCS#11 :cpp:class:`Session` must be passed to instantiate a ``PKCS11_RNG``.
+
+ .. cpp:function:: void randomize(uint8_t output[], std::size_t length) override
+
+ Calls :cpp:func:`C_GenerateRandom` to generate random data.
+
+ .. cpp:function:: void add_entropy(const uint8_t in[], std::size_t length) override
+
+ Calls :cpp:func:`C_SeedRandom` to add entropy to the random generation function of the token/middleware.
+
+----------
+
+Code example:
+
+ .. code-block:: cpp
+
+ Botan::PKCS11::PKCS11_RNG p11_rng( session );
+
+ /************ generate random data *************/
+ std::vector<uint8_t> random( 20 );
+ p11_rng.randomize( random.data(), random.size() );
+
+ /************ add entropy *************/
+ Botan::AutoSeeded_RNG auto_rng;
+ auto auto_rng_random = auto_rng.random_vec( 20 );
+ p11_rng.add_entropy( auto_rng_random.data(), auto_rng_random.size() );
+
+ /************ use PKCS#11 RNG to seed HMAC_DRBG *************/
+ Botan::HMAC_DRBG drbg( Botan::MessageAuthenticationCode::create( "HMAC(SHA-512)" ), p11_rng );
+ drbg.randomize( random.data(), random.size() );
+
+Token Management Functions
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The header file ``<botan/p11.h>`` also defines some free functions for token management:
+
+ .. cpp:function:: void initialize_token(Slot& slot, const std::string& label, const secure_string& so_pin, const secure_string& pin)
+
+ Initializes a token by passing a :cpp:class:`Slot`, a ``label`` and the ``so_pin`` of the security officer.
+
+ .. cpp:function:: void change_pin(Slot& slot, const secure_string& old_pin, const secure_string& new_pin)
+
+ Change PIN with ``old_pin`` to ``new_pin``.
+
+ .. cpp:function:: void change_so_pin(Slot& slot, const secure_string& old_so_pin, const secure_string& new_so_pin)
+
+ Change SO_PIN with ``old_so_pin`` to new ``new_so_pin``.
+
+ .. cpp:function:: void set_pin(Slot& slot, const secure_string& so_pin, const secure_string& pin)
+
+ Sets user ``pin`` with ``so_pin``.
+
+----------
+
+Code example:
+
+ .. code-block:: cpp
+
+ /************ set pin *************/
+
+ Botan::PKCS11::Module module( Middleware_path );
+
+ // only slots with connected token
+ std::vector<Botan::PKCS11::SlotId> slots = Botan::PKCS11::Slot::get_available_slots( module, true );
+
+ // 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' };
+
+ // set pin
+ Botan::PKCS11::set_pin( slot, so_pin, test_pin );
+
+ // change back
+ Botan::PKCS11::set_pin( slot, so_pin, pin );
+
+ /************ initialize *************/
+ Botan::PKCS11::initialize_token( slot, "Botan handbook example", so_pin, pin );
+
+ /************ change pin *************/
+ Botan::PKCS11::change_pin( slot, pin, test_pin );
+
+ // change back
+ Botan::PKCS11::change_pin( slot, test_pin, pin );
+
+ /************ change security officer pin *************/
+ Botan::PKCS11::change_so_pin( slot, so_pin, test_pin );
+
+ // change back
+ Botan::PKCS11::change_so_pin( slot, test_pin, so_pin );
+
+X.509
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The header file ``<botan/p11_x509.h>`` defines the property class :cpp:class:`X509_CertificateProperties`
+and the class :cpp:class:`PKCS11_X509_Certificate`.
+
+.. cpp:class:: PKCS11_X509_Certificate : public Object, public X509_Certificate
+
+ .. cpp:function:: PKCS11_X509_Certificate(Session& session, ObjectHandle handle)
+
+ Allows to use existing certificates on the token by passing a valid :cpp:type:`ObjectHandle`.
+
+ .. cpp:function:: PKCS11_X509_Certificate(Session& session, const X509_CertificateProperties& props)
+
+ Allows to import an existing X.509 certificate to the token with the :cpp:class:`X509_CertificateProperties`
+ passed in ``props``.
+
+----------
+
+Code example:
+
+ .. code-block:: cpp
+
+ // load existing certificate
+ Botan::X509_Certificate root( "test.crt" );
+
+ // set props
+ Botan::PKCS11::X509_CertificateProperties props(
+ Botan::DER_Encoder().encode( root.subject_dn() ).get_contents_unlocked(), root.BER_encode() );
+
+ props.set_label( "Botan PKCS#11 test certificate" );
+ props.set_private( false );
+ props.set_token( true );
+
+ // import
+ Botan::PKCS11::PKCS11_X509_Certificate pkcs11_cert( session, props );
+
+ // load by handle
+ Botan::PKCS11::PKCS11_X509_Certificate pkcs11_cert2( session, pkcs11_cert.handle() );
+
+Tests
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The PKCS#11 tests are not executed automatically because the depend on an external
+PKCS#11 module/middleware. The test tool has to be executed with ``--pkcs11-lib=``
+followed with the path of the PKCS#11 module and a second argument which controls the
+PKCS#11 tests that are executed. Passing ``pkcs11`` will execute all PKCS#11 tests but it's
+also possible to execute only a subset with the following arguments:
+
+- pkcs11-ecdh
+- pkcs11-ecdsa
+- pkcs11-lowlevel
+- pkcs11-manage
+- pkcs11-module
+- pkcs11-object
+- pkcs11-rng
+- pkcs11-rsa
+- pkcs11-session
+- pkcs11-slot
+- pkcs11-x509
+
+The following PIN and SO-PIN/PUK values are used in tests:
+
+- PIN 123456
+- SO-PIN/PUK 12345678
+
+ .. warning::
+
+ 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
+ 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:
+
+ .. code-block:: cpp
+
+ // SO PIN is expected to be set to "12345678" prior to running the tests
+ const std::string SO_PIN = "12345678";
+ const auto SO_PIN_SECVEC = Botan::PKCS11::secure_string(SO_PIN.begin(), SO_PIN.end());
+
+
+Tested/Supported Smartcards
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+You are very welcome to contribute your own test results for other testing environments or other cards.
+
+
+Test results
+
++-------------------------------------+-------------------------------------------+---------------------------------------------------+---------------------------------------------------+---------------------------------------------------+---------------------------------------------------+
+| Smartcard | Status | OS | Midleware | 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]_ |
++-------------------------------------+-------------------------------------------+---------------------------------------------------+---------------------------------------------------+---------------------------------------------------+---------------------------------------------------+
+| CardOS 5.0 | mostly works | Windows 10, 64-bit, version 1709 | API Version 5.4.9.77 (Cryptoki v2.11) | 2.4.0, Cryptoki v2.40 | [51]_ |
++-------------------------------------+-------------------------------------------+---------------------------------------------------+---------------------------------------------------+---------------------------------------------------+---------------------------------------------------+
+| CardOS 5.3 | mostly works | Windows 10, 64-bit, version 1709 | API Version 5.4.9.77 (Cryptoki v2.11) | 2.4.0, Cryptoki v2.40 | [52]_ |
++-------------------------------------+-------------------------------------------+---------------------------------------------------+---------------------------------------------------+---------------------------------------------------+---------------------------------------------------+
+| CardOS 5.3 | mostly works | Windows 10, 64-bit, version 1903 | API Version 5.5.1 (Cryptoki v2.11) | 2.12.0 unreleased, Cryptoki v2.40 | [53]_ |
++-------------------------------------+-------------------------------------------+---------------------------------------------------+---------------------------------------------------+---------------------------------------------------+---------------------------------------------------+
+| Gemalto IDPrime MD 3840 | mostly works | Windows 10, 64-bit, version 1709 | IDGo 800, v1.2.4 (Cryptoki v2.20) | 2.4.0, Cryptoki v2.40 | [54]_ |
++-------------------------------------+-------------------------------------------+---------------------------------------------------+---------------------------------------------------+---------------------------------------------------+---------------------------------------------------+
+| SoftHSM 2.3.0 (OpenSSL 1.0.2g) | works | Windows 10, 64-bit, version 1709 | Cryptoki v2.40 | 2.4.0, Cryptoki v2.40 | |
++-------------------------------------+-------------------------------------------+---------------------------------------------------+---------------------------------------------------+---------------------------------------------------+---------------------------------------------------+
+| SoftHSM 2.5.0 (OpenSSL 1.1.1) | works | Windows 10, 64-bit, version 1803 | Cryptoki v2.40 | 2.11.0, Cryptoki v2.40 | |
++-------------------------------------+-------------------------------------------+---------------------------------------------------+---------------------------------------------------+---------------------------------------------------+---------------------------------------------------+
+
+.. [50] Failing operations for CardOS 4.4:
+
+ - object_copy [20]_
+
+ - rsa_privkey_export [21]_
+ - rsa_generate_private_key [22]_
+ - rsa_sign_verify [23]_
+
+ - ecdh_privkey_import [3]_
+ - ecdh_privkey_export [2]_
+ - ecdh_pubkey_import [4]_
+ - ecdh_pubkey_export [4]_
+ - ecdh_generate_private_key [3]_
+ - ecdh_generate_keypair [3]_
+ - ecdh_derive [3]_
+
+ - ecdsa_privkey_import [3]_
+ - ecdsa_privkey_export [2]_
+ - ecdsa_pubkey_import [4]_
+ - ecdsa_pubkey_export [4]_
+ - ecdsa_generate_private_key [3]_
+ - ecdsa_generate_keypair [3]_
+ - ecdsa_sign_verify [3]_
+
+ - rng_add_entropy [5]_
+
+
+.. [51] Failing operations for CardOS 5.0
+
+ - object_copy [20]_
+
+ - rsa_privkey_export [21]_
+ - rsa_generate_private_key [22]_
+ - rsa_sign_verify [23]_
+
+ - ecdh_privkey_export [2]_
+ - ecdh_pubkey_import [4]_
+ - ecdh_generate_private_key [32]_
+ - ecdh_generate_keypair [3]_
+ - ecdh_derive [33]_
+
+ - ecdsa_privkey_export [2]_
+ - ecdsa_generate_private_key [30]_
+ - ecdsa_generate_keypair [30]_
+ - ecdsa_sign_verify [30]_
+
+ - rng_add_entropy [5]_
+
+.. [52] Failing operations for CardOS 5.3
+
+ - object_copy [20]_
+
+ - rsa_privkey_export [21]_
+ - rsa_generate_private_key [22]_
+ - rsa_sign_verify [23]_
+
+ - ecdh_privkey_export [2]_
+ - ecdh_pubkey_import [6]_
+ - ecdh_pubkey_export [6]_
+ - ecdh_generate_private_key [30]_
+ - ecdh_generate_keypair [31]_
+ - ecdh_derive [30]_
+
+ - ecdsa_privkey_export [2]_
+ - ecdsa_pubkey_import [6]_
+ - ecdsa_pubkey_export [6]_
+ - ecdsa_generate_private_key [31]_
+ - ecdsa_generate_keypair [31]_
+ - ecdsa_sign_verify [34]_
+
+ - rng_add_entropy [5]_
+
+.. [53] Failing operations for CardOS 5.3 (middelware 5.5.1)
+
+ - ecdh_privkey_export [2]_
+ - ecdh_generate_private_key [35]_
+ - ecdsa_privkey_export [2]_
+ - ecdsa_generate_private_key [36]_
+ - c_copy_object [4]_
+
+ - object_copy [4]_
+
+ - rng_add_entropy [5]_
+
+ - rsa_sign_verify [3]_
+ - rsa_privkey_export [2]_
+ - rsa_generate_private_key [9]_
+
+.. [54] Failing operations for Gemalto IDPrime MD 3840
+
+ - session_login_logout [2]_
+ - session_info [2]_
+ - set_pin [2]_
+ - initialize [2]_
+ - change_so_pin [2]_
+
+ - object_copy [20]_
+
+ - rsa_generate_private_key [7]_
+ - rsa_encrypt_decrypt [8]_
+ - rsa_sign_verify [2]_
+
+ - rng_add_entropy [5]_
+
+Error descriptions
+
+.. [2] CKR_ARGUMENTS_BAD (0x7=7)
+.. [3] CKR_MECHANISM_INVALID (0x70=112)
+.. [4] CKR_FUNCTION_NOT_SUPPORTED (0x54=84)
+.. [5] CKR_RANDOM_SEED_NOT_SUPPORTED (0x120=288)
+.. [6] CKM_X9_42_DH_KEY_PAIR_GEN | CKR_DEVICE_ERROR (0x30=48)
+.. [7] CKR_TEMPLATE_INCONSISTENT (0xD1=209)
+.. [8] CKR_ENCRYPTED_DATA_INVALID | CKM_SHA256_RSA_PKCS (0x40=64)
+.. [9] CKR_TEMPLATE_INCOMPLETE (0xD0=208)
+
+.. [20] Test fails due to unsupported copy function (CKR_FUNCTION_NOT_SUPPORTED)
+.. [21] Generating private key for extraction with property extractable fails (CKR_ARGUMENTS_BAD)
+.. [22] Generate rsa private key operation fails (CKR_TEMPLATE_INCOMPLETE)
+.. [23] Raw RSA sign-verify fails (CKR_MECHANISM_INVALID)
+
+.. [30] Invalid argument Decoding error: BER: Value truncated
+.. [31] Invalid argument Decoding error: BER: Length field is to large
+.. [32] Invalid argument OS2ECP: Unknown format type 155
+.. [33] Invalid argument OS2ECP: Unknown format type 92
+.. [34] Invalid argument OS2ECP: Unknown format type 57
+.. [35] Invalid argument OS2ECP: Unknown format type 82
+.. [36] Invalid argument OS2ECP: Unknown format type 102
diff --git a/comm/third_party/botan/doc/api_ref/psk_db.rst b/comm/third_party/botan/doc/api_ref/psk_db.rst
new file mode 100644
index 0000000000..d9f8bf79be
--- /dev/null
+++ b/comm/third_party/botan/doc/api_ref/psk_db.rst
@@ -0,0 +1,110 @@
+PSK Database
+======================
+
+.. versionadded:: 2.4.0
+
+Many applications need to store pre-shared keys (hereafter PSKs) for
+authentication purposes.
+
+An abstract interface to PSK stores, along with some implementations
+of same, are provided in ``psk_db.h``
+
+.. cpp:class:: PSK_Database
+
+ .. cpp:function:: bool is_encrypted() const
+
+ Returns true if (at least) the PSKs themselves are encrypted. Returns
+ false if PSKs are stored in plaintext.
+
+ .. cpp:function:: std::set<std::string> list_names() const
+
+ Return the set of valid names stored in the database, ie values for which
+ ``get`` will return a value.
+
+ .. cpp:function:: void set(const std::string& name, const uint8_t psk[], size_t psk_len)
+
+ Save a PSK. If ``name`` already exists, the current value will be
+ overwritten.
+
+ .. cpp:function:: secure_vector<uint8_t> get(const std::string& name) const
+
+ Return a value saved with ``set``. Throws an exception if ``name`` doesn't
+ exist.
+
+ .. cpp:function:: void remove(const std::string& name)
+
+ Remove ``name`` from the database. If ``name`` doesn't exist, ignores the request.
+
+ .. cpp::function:: std::string get_str(const std::string& name) const
+
+ Like ``get`` but casts the return value to a string.
+
+ .. cpp:function:: void set_str(const std::string& name, const std::string& psk)
+
+ Like ``set`` but accepts the psk as a string (eg for a password).
+
+ .. cpp:function:: template<typename Alloc> void set_vec(const std::string& name, \
+ const std::vector<uint8_t, Alloc>& psk)
+
+ Like ``set`` but accepting a vector.
+
+The same header also provides a specific instantiation of ``PSK_Database`` which
+encrypts both names and PSKs. It must be subclassed to provide the storage.
+
+.. cpp:class:: Encrypted_PSK_Database : public PSK_Database
+
+ .. cpp:function:: Encrypted_PSK_Database(const secure_vector<uint8_t>& master_key)
+
+ Initializes or opens a PSK database. The master key is used the secure the
+ contents. It may be of any length. If encrypting PSKs under a passphrase,
+ use a suitable key derivation scheme (such as PBKDF2) to derive the secret
+ key. If the master key is lost, all PSKs stored are unrecoverable.
+
+ Both names and values are encrypted using NIST key wrapping (see NIST
+ SP800-38F) with AES-256. First the master key is used with HMAC(SHA-256)
+ to derive two 256-bit keys, one for encrypting all names and the other to
+ key an instance of HMAC(SHA-256). Values are each encrypted under an
+ individual key created by hashing the encrypted name with HMAC. This
+ associates the encrypted key with the name, and prevents an attacker with
+ write access to the data store from taking an encrypted key associated
+ with one entity and copying it to another entity.
+
+ Names and PSKs are both padded to the next multiple of 8 bytes, providing
+ some obfuscation of the length.
+
+ One artifact of the names being encrypted is that is is possible to use
+ multiple different master keys with the same underlying storage. Each
+ master key will be responsible for a subset of the keys. An attacker who
+ knows one of the keys will be able to tell there are other values
+ encrypted under another key, but will not be able to tell how many other
+ master keys are in use.
+
+ .. cpp:function:: virtual void kv_set(const std::string& index, const std::string& value) = 0
+
+ Save an encrypted value. Both ``index`` and ``value`` will be non-empty
+ base64 encoded strings.
+
+ .. cpp:function:: virtual std::string kv_get(const std::string& index) const = 0
+
+ Return a value saved with ``kv_set``, or return the empty string.
+
+ .. cpp:function:: virtual void kv_del(const std::string& index) = 0
+
+ Remove a value saved with ``kv_set``.
+
+ .. cpp:function:: virtual std::set<std::string> kv_get_all() const = 0
+
+ Return all active names (ie values for which ``kv_get`` will return a
+ non-empty string).
+
+A subclass of ``Encrypted_PSK_Database`` which stores data in a SQL database
+is also available.
+
+.. cpp:class:: Encrypted_PSK_Database_SQL : public Encrypted_PSK_Database
+
+ .. cpp:function:: Encrypted_PSK_Database_SQL(const secure_vector<uint8_t>& master_key, \
+ std::shared_ptr<SQL_Database> db, \
+ const std::string& table_name)
+
+ Creates or uses the named table in ``db``. The SQL schema of the table is
+ ``(psk_name TEXT PRIMARY KEY, psk_value TEXT)``.
diff --git a/comm/third_party/botan/doc/api_ref/pubkey.rst b/comm/third_party/botan/doc/api_ref/pubkey.rst
new file mode 100644
index 0000000000..a8b93b4573
--- /dev/null
+++ b/comm/third_party/botan/doc/api_ref/pubkey.rst
@@ -0,0 +1,954 @@
+Public Key Cryptography
+=================================
+
+Public key cryptography (also called asymmetric cryptography) is a collection
+of techniques allowing for encryption, signatures, and key agreement.
+
+Key Objects
+----------------------------------------
+
+Public and private keys are represented by classes ``Public_Key`` and it's
+subclass ``Private_Key``. The use of inheritance here means that a
+``Private_Key`` can be converted into a reference to a public key.
+
+None of the functions on ``Public_Key`` and ``Private_Key`` itself are
+particularly useful for users of the library, because 'bare' public key
+operations are *very insecure*. The only purpose of these functions is to
+provide a clean interface that higher level operations can be built on. So
+really the only thing you need to know is that when a function takes a
+reference to a ``Public_Key``, it can take any public key or private key, and
+similarly for ``Private_Key``.
+
+Types of ``Public_Key`` include ``RSA_PublicKey``, ``DSA_PublicKey``,
+``ECDSA_PublicKey``, ``ECKCDSA_PublicKey``, ``ECGDSA_PublicKey``, ``DH_PublicKey``, ``ECDH_PublicKey``,
+``Curve25519_PublicKey``, ``ElGamal_PublicKey``, ``McEliece_PublicKey``, ``XMSS_PublicKey``
+and ``GOST_3410_PublicKey``. There are corresponding ``Private_Key`` classes for each of these algorithms.
+
+.. _creating_new_private_keys:
+
+Creating New Private Keys
+----------------------------------------
+
+Creating a new private key requires two things: a source of random numbers
+(see :ref:`random_number_generators`) and some algorithm specific parameters
+that define the *security level* of the resulting key. For instance, the
+security level of an RSA key is (at least in part) defined by the length of
+the public key modulus in bits. So to create a new RSA private key, you would
+call
+
+.. cpp:function:: RSA_PrivateKey::RSA_PrivateKey(RandomNumberGenerator& rng, size_t bits)
+
+ A constructor that creates a new random RSA private key with a modulus
+ of length *bits*.
+
+ RSA key generation is relatively slow, and can take an unpredictable
+ amount of time. Generating a 2048 bit RSA key might take 5 to 10
+ seconds on a slow machine like a Raspberry Pi 2. Even on a fast
+ desktop it might take up to half a second. In a GUI blocking for
+ that long can be a problem. The usual approach is to perform key
+ generation in a new thread, with a animated modal UI element so the
+ user knows the application is still alive. If you wish to provide a
+ progress estimate things get a bit complicated but some library
+ users documented their approach in
+ `a blog post <https://medium.com/nexenio/indicating-progress-of-rsa-key-pair-generation-the-practical-approach-a049ba829dbe>`_.
+
+Algorithms based on the discrete-logarithm problem use what is called a
+*group*; a group can safely be used with many keys, and for some operations,
+like key agreement, the two keys *must* use the same group. There are
+currently two kinds of discrete logarithm groups supported in botan: the
+integers modulo a prime, represented by :ref:`dl_group`, and elliptic curves
+in GF(p), represented by :ref:`ec_group`. A rough generalization is that the
+larger the group is, the more secure the algorithm is, but correspondingly the
+slower the operations will be.
+
+Given a ``DL_Group``, you can create new DSA, Diffie-Hellman and ElGamal key pairs with
+
+.. cpp:function:: DSA_PrivateKey::DSA_PrivateKey(RandomNumberGenerator& rng, \
+ const DL_Group& group, const BigInt& x = 0)
+
+.. cpp:function:: DH_PrivateKey::DH_PrivateKey(RandomNumberGenerator& rng, \
+ const DL_Group& group, const BigInt& x = 0)
+
+.. cpp:function:: ElGamal_PrivateKey::ElGamal_PrivateKey(RandomNumberGenerator& rng, \
+ const DL_Group& group, const BigInt& x = 0)
+
+ The optional *x* parameter to each of these constructors is a private key
+ value. This allows you to create keys where the private key is formed by
+ some special technique; for instance you can use the hash of a password (see
+ :ref:`pbkdf` for how to do that) as a private key value. Normally, you would
+ leave the value as zero, letting the class generate a new random key.
+
+Finally, given an ``EC_Group`` object, you can create a new ECDSA, ECKCDSA, ECGDSA,
+ECDH, or GOST 34.10-2001 private key with
+
+.. cpp:function:: ECDSA_PrivateKey::ECDSA_PrivateKey(RandomNumberGenerator& rng, \
+ const EC_Group& domain, const BigInt& x = 0)
+
+.. cpp:function:: ECKCDSA_PrivateKey::ECKCDSA_PrivateKey(RandomNumberGenerator& rng, \
+ const EC_Group& domain, const BigInt& x = 0)
+
+.. cpp:function:: ECGDSA_PrivateKey::ECGDSA_PrivateKey(RandomNumberGenerator& rng, \
+ const EC_Group& domain, const BigInt& x = 0)
+
+.. cpp:function:: ECDH_PrivateKey::ECDH_PrivateKey(RandomNumberGenerator& rng, \
+ const EC_Group& domain, const BigInt& x = 0)
+
+.. cpp:function:: GOST_3410_PrivateKey::GOST_3410_PrivateKey(RandomNumberGenerator& rng, \
+ const EC_Group& domain, const BigInt& x = 0)
+
+.. _serializing_private_keys:
+
+Serializing Private Keys Using PKCS #8
+----------------------------------------
+
+The standard format for serializing a private key is PKCS #8, the operations
+for which are defined in ``pkcs8.h``. It supports both unencrypted and
+encrypted storage.
+
+.. cpp:function:: secure_vector<uint8_t> PKCS8::BER_encode(const Private_Key& key, \
+ RandomNumberGenerator& rng, const std::string& password, const std::string& pbe_algo = "")
+
+ Takes any private key object, serializes it, encrypts it using
+ *password*, and returns a binary structure representing the private
+ key.
+
+ The final (optional) argument, *pbe_algo*, specifies a particular
+ password based encryption (or PBE) algorithm. If you don't specify a
+ PBE, a sensible default will be used.
+
+ The currently supported PBE is PBES2 from PKCS5. Format is as follows:
+ ``PBE-PKCS5v20(CIPHER,PBKDF)``. Since 2.8.0, ``PBES2(CIPHER,PBKDF)`` also works.
+ Cipher can be any block cipher with /CBC or /GCM appended, for example
+ "AES-128/CBC" or "Camellia-256/GCM". For best interop with other systems, use
+ AES in CBC mode. The PBKDF can be either the name of a hash function (in which
+ case PBKDF2 is used with that hash) or "Scrypt", which causes the scrypt
+ memory hard password hashing function to be used. Scrypt is supported since
+ version 2.7.0.
+
+ Use `PBE-PKCS5v20(AES-256/CBC,SHA-256)` if you want to ensure the keys can
+ be imported by different software packages. Use
+ `PBE-PKCS5v20(AES-256/GCM,Scrypt)` for best security assuming you do not
+ care about interop.
+
+ For ciphers you can use anything which has an OID defined for CBC, GCM or SIV
+ modes. Currently this includes AES, Camellia, Serpent, Twofish, and SM4. Most
+ other libraries only support CBC mode for private key encryption. GCM has
+ been supported in PBES2 since 1.11.10. SIV has been supported since 2.8.
+
+.. cpp:function:: std::string PKCS8::PEM_encode(const Private_Key& key, \
+ RandomNumberGenerator& rng, const std::string& pass, const std::string& pbe_algo = "")
+
+ This formats the key in the same manner as ``BER_encode``, but additionally
+ encodes it into a text format with identifying headers. Using PEM encoding
+ is *highly* recommended for many reasons, including compatibility with other
+ software, for transmission over 8-bit unclean channels, because it can be
+ identified by a human without special tools, and because it sometimes allows
+ more sane behavior of tools that process the data.
+
+Unencrypted serialization is also supported.
+
+.. warning::
+
+ In most situations, using unencrypted private key storage is a bad idea,
+ because anyone can come along and grab the private key without having to
+ know any passwords or other secrets. Unless you have very particular
+ security requirements, always use the versions that encrypt the key based on
+ a passphrase, described above.
+
+.. cpp:function:: secure_vector<uint8_t> PKCS8::BER_encode(const Private_Key& key)
+
+ Serializes the private key and returns the result.
+
+.. cpp:function:: std::string PKCS8::PEM_encode(const Private_Key& key)
+
+ Serializes the private key, base64 encodes it, and returns the
+ result.
+
+Last but not least, there are some functions that will load (and
+decrypt, if necessary) a PKCS #8 private key:
+
+.. cpp:function:: Private_Key* PKCS8::load_key(DataSource& in, \
+ RandomNumberGenerator& rng, const User_Interface& ui)
+
+.. cpp:function:: Private_Key* PKCS8::load_key(DataSource& in, \
+ RandomNumberGenerator& rng, std::string passphrase = "")
+
+.. cpp:function:: Private_Key* PKCS8::load_key(const std::string& filename, \
+ RandomNumberGenerator& rng, const User_Interface& ui)
+
+.. cpp:function:: Private_Key* PKCS8::load_key(const std::string& filename, \
+ RandomNumberGenerator& rng, const std::string& passphrase = "")
+
+These functions will return an object allocated key object based on the data
+from whatever source it is using (assuming, of course, the source is in fact
+storing a representation of a private key, and the decryption was
+successful). The encoding used (PEM or BER) need not be specified; the format
+will be detected automatically. The key is allocated with ``new``, and should
+be released with ``delete`` when you are done with it. The first takes a
+generic ``DataSource`` that you have to create - the other is a simple wrapper
+functions that take either a filename or a memory buffer and create the
+appropriate ``DataSource``.
+
+The versions taking a ``std::string`` attempt to decrypt using the password
+given (if the key is encrypted; if it is not, the passphase value will be
+ignored). If the passphrase does not decrypt the key, an exception will be
+thrown.
+
+The ones taking a ``User_Interface`` provide a simple callback interface which
+makes handling incorrect passphrases and such a bit simpler. A
+``User_Interface`` has very little to do with talking to users; it's just a
+way to glue together Botan and whatever user interface you happen to be using.
+
+.. note::
+
+ In a future version, it is likely that ``User_Interface`` will be
+ replaced by a simple callback using ``std::function``.
+
+To use ``User_Interface``, derive a subclass and implement:
+
+.. cpp:function:: std::string User_Interface::get_passphrase(const std::string& what, \
+ const std::string& source, UI_Result& result) const
+
+ The ``what`` argument specifies what the passphrase is needed for (for
+ example, PKCS #8 key loading passes ``what`` as "PKCS #8 private key"). This
+ lets you provide the user with some indication of *why* your application is
+ asking for a passphrase; feel free to pass the string through ``gettext(3)``
+ or moral equivalent for i18n purposes. Similarly, ``source`` specifies where
+ the data in question came from, if available (for example, a file name). If
+ the source is not available for whatever reason, then ``source`` will be an
+ empty string; be sure to account for this possibility.
+
+ The function returns the passphrase as the return value, and a status code
+ in ``result`` (either ``OK`` or ``CANCEL_ACTION``). If ``CANCEL_ACTION`` is
+ returned in ``result``, then the return value will be ignored, and the
+ caller will take whatever action is necessary (typically, throwing an
+ exception stating that the passphrase couldn't be determined). In the
+ specific case of PKCS #8 key decryption, a ``Decoding_Error`` exception will
+ be thrown; your UI should assume this can happen, and provide appropriate
+ error handling (such as putting up a dialog box informing the user of the
+ situation, and canceling the operation in progress).
+
+.. _serializing_public_keys:
+
+Serializing Public Keys
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+To import and export public keys, use:
+
+.. cpp:function:: std::vector<uint8_t> X509::BER_encode(const Public_Key& key)
+
+.. cpp:function:: std::string X509::PEM_encode(const Public_Key& key)
+
+.. cpp:function:: Public_Key* X509::load_key(DataSource& in)
+
+.. cpp:function:: Public_Key* X509::load_key(const secure_vector<uint8_t>& buffer)
+
+.. cpp:function:: Public_Key* X509::load_key(const std::string& filename)
+
+ These functions operate in the same way as the ones described in
+ :ref:`serializing_private_keys`, except that no encryption option is
+ available.
+
+.. _dl_group:
+
+DL_Group
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+As described in :ref:`creating_new_private_keys`, a discrete logarithm group
+can be shared among many keys, even keys created by users who do not trust
+each other. However, it is necessary to trust the entity who created the
+group; that is why organization like NIST use algorithms which generate groups
+in a deterministic way such that creating a bogus group would require breaking
+some trusted cryptographic primitive like SHA-2.
+
+Instantiating a ``DL_Group`` simply requires calling
+
+.. cpp:function:: DL_Group::DL_Group(const std::string& name)
+
+ The *name* parameter is a specially formatted string that consists of three
+ things, the type of the group ("modp" or "dsa"), the creator of the group,
+ and the size of the group in bits, all delimited by '/' characters.
+
+ Currently all "modp" groups included in botan are ones defined by the
+ Internet Engineering Task Force, so the provider is "ietf", and the strings
+ look like "modp/ietf/N" where N can be any of 1024, 1536, 2048, 3072,
+ 4096, 6144, or 8192. This group type is used for Diffie-Hellman and ElGamal
+ algorithms.
+
+ The other type, "dsa" is used for DSA keys. They can also be used with
+ Diffie-Hellman and ElGamal, but this is less common. The currently available
+ groups are "dsa/jce/1024" and "dsa/botan/N" with N being 2048 or 3072. The
+ "jce" groups are the standard DSA groups used in the Java Cryptography
+ Extensions, while the "botan" groups were randomly generated using the
+ FIPS 186-3 algorithm by the library maintainers.
+
+You can generate a new random group using
+
+.. cpp:function:: DL_Group::DL_Group(RandomNumberGenerator& rng, \
+ PrimeType type, size_t pbits, size_t qbits = 0)
+
+ The *type* can be either ``Strong``, ``Prime_Subgroup``, or
+ ``DSA_Kosherizer``. *pbits* specifies the size of the prime in
+ bits. If the *type* is ``Prime_Subgroup`` or ``DSA_Kosherizer``,
+ then *qbits* specifies the size of the subgroup.
+
+You can serialize a ``DL_Group`` using
+
+.. cpp:function:: secure_vector<uint8_t> DL_Group::DER_Encode(Format format)
+
+or
+
+.. cpp:function:: std::string DL_Group::PEM_encode(Format format)
+
+where *format* is any of
+
+* ``ANSI_X9_42`` (or ``DH_PARAMETERS``) for modp groups
+* ``ANSI_X9_57`` (or ``DSA_PARAMETERS``) for DSA-style groups
+* ``PKCS_3`` is an older format for modp groups; it should only
+ be used for backwards compatibility.
+
+You can reload a serialized group using
+
+.. cpp:function:: void DL_Group::BER_decode(DataSource& source, Format format)
+
+.. cpp:function:: void DL_Group::PEM_decode(DataSource& source)
+
+Code Example
+"""""""""""""""""
+The example below creates a new 2048 bit ``DL_Group``, prints the generated
+parameters and ANSI_X9_42 encodes the created group for further usage with DH.
+
+.. code-block:: cpp
+
+ #include <botan/dl_group.h>
+ #include <botan/auto_rng.h>
+ #include <botan/rng.h>
+ #include <iostream>
+
+ int main()
+ {
+ std::unique_ptr<Botan::RandomNumberGenerator> rng(new Botan::AutoSeeded_RNG);
+ std::unique_ptr<Botan::DL_Group> group(new Botan::DL_Group(*rng.get(), Botan::DL_Group::Strong, 2048));
+ std::cout << std::endl << "p: " << group->get_p();
+ std::cout << std::endl << "q: " << group->get_q();
+ std::cout << std::endl << "g: " << group->get_q();
+ std::cout << std::endl << "ANSI_X9_42: " << std::endl << group->PEM_encode(Botan::DL_Group::ANSI_X9_42);
+
+ return 0;
+ }
+
+
+.. _ec_group:
+
+EC_Group
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+An ``EC_Group`` is initialized by passing the name of the
+group to be used to the constructor. These groups have
+semi-standardized names like "secp256r1" and "brainpool512r1".
+
+Key Checking
+---------------------------------
+
+Most public key algorithms have limitations or restrictions on their
+parameters. For example RSA requires an odd exponent, and algorithms
+based on the discrete logarithm problem need a generator > 1.
+
+Each public key type has a function
+
+.. cpp:function:: bool Public_Key::check_key(RandomNumberGenerator& rng, bool strong)
+
+ This function performs a number of algorithm-specific tests that the key
+ seems to be mathematically valid and consistent, and returns true if all of
+ the tests pass.
+
+ It does not have anything to do with the validity of the key for any
+ particular use, nor does it have anything to do with certificates that link
+ a key (which, after all, is just some numbers) with a user or other
+ entity. If *strong* is ``true``, then it does "strong" checking, which
+ includes expensive operations like primality checking.
+
+As key checks are not automatically performed they must be called
+manually after loading keys from untrusted sources. If a key from an untrusted source
+is not checked, the implementation might be vulnerable to algorithm specific attacks.
+
+The following example loads the Subject Public Key from the x509 certificate ``cert.pem`` and checks the
+loaded key. If the key check fails a respective error is thrown.
+
+.. code-block:: cpp
+
+ #include <botan/x509cert.h>
+ #include <botan/auto_rng.h>
+ #include <botan/rng.h>
+
+ int main()
+ {
+ Botan::X509_Certificate cert("cert.pem");
+ std::unique_ptr<Botan::RandomNumberGenerator> rng(new Botan::AutoSeeded_RNG);
+ std::unique_ptr<Botan::Public_Key> key(cert.subject_public_key());
+ if(!key->check_key(*rng.get(), false))
+ {
+ throw std::invalid_argument("Loaded key is invalid");
+ }
+ }
+
+Encryption
+---------------------------------
+
+Safe public key encryption requires the use of a padding scheme which hides
+the underlying mathematical properties of the algorithm. Additionally, they
+will add randomness, so encrypting the same plaintext twice produces two
+different ciphertexts.
+
+The primary interface for encryption is
+
+.. cpp:class:: PK_Encryptor
+
+ .. cpp:function:: secure_vector<uint8_t> encrypt( \
+ const uint8_t* in, size_t length, RandomNumberGenerator& rng) const
+
+ .. cpp:function:: secure_vector<uint8_t> encrypt( \
+ const std::vector<uint8_t>& in, RandomNumberGenerator& rng) const
+
+ These encrypt a message, returning the ciphertext.
+
+ .. cpp:function:: size_t maximum_input_size() const
+
+ Returns the maximum size of the message that can be processed, in
+ bytes. If you call :cpp:func:`PK_Encryptor::encrypt` with a value larger
+ than this the operation will fail with an exception.
+
+:cpp:class:`PK_Encryptor` is only an interface - to actually encrypt you have
+to create an implementation, of which there are currently three available in the
+library, :cpp:class:`PK_Encryptor_EME`, :cpp:class:`DLIES_Encryptor` and
+:cpp:class:`ECIES_Encryptor`. DLIES is a hybrid encryption scheme (from
+IEEE 1363) that uses the DH key agreement technique in combination with a KDF, a
+MAC and a symmetric encryption algorithm to perform message encryption. ECIES is
+similar to DLIES, but uses ECDH for the key agreement. Normally, public key
+encryption is done using algorithms which support it directly, such as RSA or
+ElGamal; these use the EME class:
+
+.. cpp:class:: PK_Encryptor_EME
+
+ .. cpp:function:: PK_Encryptor_EME(const Public_Key& key, std::string eme)
+
+ With *key* being the key you want to encrypt messages to. The padding
+ method to use is specified in *eme*.
+
+ The recommended values for *eme* is "EME1(SHA-1)" or "EME1(SHA-256)". If
+ you need compatibility with protocols using the PKCS #1 v1.5 standard,
+ you can also use "EME-PKCS1-v1_5".
+
+.. cpp:class:: DLIES_Encryptor
+
+ Available in the header ``dlies.h``
+
+ .. cpp:function:: DLIES_Encryptor(const DH_PrivateKey& own_priv_key, \
+ RandomNumberGenerator& rng, KDF* kdf, MessageAuthenticationCode* mac, \
+ size_t mac_key_len = 20)
+
+ Where *kdf* is a key derivation function (see
+ :ref:`key_derivation_function`) and *mac* is a
+ MessageAuthenticationCode. The encryption is performed by XORing the
+ message with a stream of bytes provided by the KDF.
+
+ .. cpp:function:: DLIES_Encryptor(const DH_PrivateKey& own_priv_key, \
+ RandomNumberGenerator& rng, KDF* kdf, Cipher_Mode* cipher, \
+ size_t cipher_key_len, MessageAuthenticationCode* mac, \
+ size_t mac_key_len = 20)
+
+ Instead of XORing the message a block cipher can be specified.
+
+.. cpp:class:: ECIES_Encryptor
+
+ Available in the header ``ecies.h``.
+
+ Parameters for encryption and decryption are set by the
+ :cpp:class:`ECIES_System_Params` class which stores the EC domain parameters,
+ the KDF (see :ref:`key_derivation_function`), the cipher (see
+ :ref:`cipher_modes`) and the MAC.
+
+ .. cpp:function:: ECIES_Encryptor(const PK_Key_Agreement_Key& private_key, \
+ const ECIES_System_Params& ecies_params, \
+ RandomNumberGenerator& rng)
+
+ Where *private_key* is the key to use for the key agreement. The system
+ parameters are specified in *ecies_params* and the RNG to use is passed in
+ *rng*.
+
+ .. cpp:function:: ECIES_Encryptor(RandomNumberGenerator& rng, \
+ const ECIES_System_Params& ecies_params)
+
+ Creates an ephemeral private key which is used for the key agreement.
+
+The decryption classes are named :cpp:class:`PK_Decryptor`,
+:cpp:class:`PK_Decryptor_EME`, :cpp:class:`DLIES_Decryptor` and
+:cpp:class:`ECIES_Decryptor`. They are created in the exact same way, except
+they take the private key, and the processing function is named ``decrypt``.
+
+
+Botan implements the following encryption algorithms and padding schemes:
+
+1. RSA
+ - "PKCS1v15" || "EME-PKCS1-v1_5"
+ - "OAEP" || "EME-OAEP" || "EME1" || "EME1(SHA-1)" || "EME1(SHA-256)"
+#. DLIES
+#. ECIES
+#. SM2
+
+Code Example
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+The following Code sample reads a PKCS #8 keypair from the passed location and
+subsequently encrypts a fixed plaintext with the included public key, using EME1
+with SHA-256. For the sake of completeness, the ciphertext is then decrypted using
+the private key.
+
+.. code-block:: cpp
+
+ #include <botan/pkcs8.h>
+ #include <botan/hex.h>
+ #include <botan/pk_keys.h>
+ #include <botan/pubkey.h>
+ #include <botan/auto_rng.h>
+ #include <botan/rng.h>
+ #include <iostream>
+ int main (int argc, char* argv[])
+ {
+ if(argc!=2)
+ return 1;
+ std::string 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.");
+ std::vector<uint8_t> pt(plaintext.data(),plaintext.data()+plaintext.length());
+ std::unique_ptr<Botan::RandomNumberGenerator> rng(new Botan::AutoSeeded_RNG);
+
+ //load keypair
+ std::unique_ptr<Botan::Private_Key> kp(Botan::PKCS8::load_key(argv[1],*rng.get()));
+
+ //encrypt with pk
+ Botan::PK_Encryptor_EME enc(*kp,*rng.get(), "EME1(SHA-256)");
+ std::vector<uint8_t> ct = enc.encrypt(pt,*rng.get());
+
+ //decrypt with sk
+ Botan::PK_Decryptor_EME dec(*kp,*rng.get(), "EME1(SHA-256)");
+ std::cout << std::endl << "enc: " << Botan::hex_encode(ct) << std::endl << "dec: "<< Botan::hex_encode(dec.decrypt(ct));
+
+ return 0;
+ }
+
+
+Signatures
+---------------------------------
+
+Signature generation is performed using
+
+.. cpp:class:: PK_Signer
+
+ .. cpp:function:: PK_Signer(const Private_Key& key, \
+ const std::string& emsa, \
+ Signature_Format format = IEEE_1363)
+
+ Constructs a new signer object for the private key *key* using the
+ signature format *emsa*. The key must support signature operations. In
+ the current version of the library, this includes RSA, DSA, ECDSA, ECKCDSA,
+ ECGDSA, GOST 34.10-2001. Other signature schemes may be supported in the future.
+
+ .. note::
+
+ Botan both supports non-deterministic and deterministic (as per RFC
+ 6979) DSA and ECDSA signatures. Deterministic signatures are compatible
+ in the way that they can be verified with a non-deterministic implementation.
+ If the ``rfc6979`` module is enabled, deterministic DSA and ECDSA signatures
+ will be generated.
+
+ Currently available values for *emsa* include EMSA1, EMSA2, EMSA3, EMSA4,
+ and Raw. All of them, except Raw, take a parameter naming a message
+ digest function to hash the message with. The Raw encoding signs the
+ input directly; if the message is too big, the signing operation will
+ fail. Raw is not useful except in very specialized applications. Examples
+ are "EMSA1(SHA-1)" and "EMSA4(SHA-256)".
+
+ For RSA, use EMSA4 (also called PSS) unless you need compatibility with
+ software that uses the older PKCS #1 v1.5 standard, in which case use
+ EMSA3 (also called "EMSA-PKCS1-v1_5"). For DSA, ECDSA, ECKCDSA, ECGDSA and
+ GOST 34.10-2001 you should use EMSA1.
+
+ The *format* defaults to ``IEEE_1363`` which is the only available
+ format for RSA. For DSA, ECDSA, ECGDSA and ECKCDSA you can also use
+ ``DER_SEQUENCE``, which will format the signature as an ASN.1
+ SEQUENCE value.
+
+ .. cpp:function:: void update(const uint8_t* in, size_t length)
+ .. cpp:function:: void update(const std::vector<uint8_t>& in)
+ .. cpp:function:: void update(uint8_t in)
+
+ These add more data to be included in the signature
+ computation. Typically, the input will be provided directly to a
+ hash function.
+
+ .. cpp:function:: secure_vector<uint8_t> signature(RandomNumberGenerator& rng)
+
+ Creates the signature and returns it
+
+ .. cpp:function:: secure_vector<uint8_t> sign_message( \
+ const uint8_t* in, size_t length, RandomNumberGenerator& rng)
+
+ .. cpp:function:: secure_vector<uint8_t> sign_message( \
+ const std::vector<uint8_t>& in, RandomNumberGenerator& rng)
+
+ These functions are equivalent to calling
+ :cpp:func:`PK_Signer::update` and then
+ :cpp:func:`PK_Signer::signature`. Any data previously provided
+ using ``update`` will be included.
+
+Signatures are verified using
+
+.. cpp:class:: PK_Verifier
+
+ .. cpp:function:: PK_Verifier(const Public_Key& pub_key, \
+ const std::string& emsa, Signature_Format format = IEEE_1363)
+
+ Construct a new verifier for signatures associated with public
+ key *pub_key*. The *emsa* and *format* should be the same as
+ that used by the signer.
+
+ .. cpp:function:: void update(const uint8_t* in, size_t length)
+ .. cpp:function:: void update(const std::vector<uint8_t>& in)
+ .. cpp:function:: void update(uint8_t in)
+
+ Add further message data that is purportedly associated with the
+ signature that will be checked.
+
+ .. cpp:function:: bool check_signature(const uint8_t* sig, size_t length)
+ .. cpp:function:: bool check_signature(const std::vector<uint8_t>& sig)
+
+ Check to see if *sig* is a valid signature for the message data
+ that was written in. Return true if so. This function clears the
+ internal message state, so after this call you can call
+ :cpp:func:`PK_Verifier::update` to start verifying another
+ message.
+
+ .. cpp:function:: bool verify_message(const uint8_t* msg, size_t msg_length, \
+ const uint8_t* sig, size_t sig_length)
+
+ .. cpp:function:: bool verify_message(const std::vector<uint8_t>& msg, \
+ const std::vector<uint8_t>& sig)
+
+ These are equivalent to calling :cpp:func:`PK_Verifier::update`
+ on *msg* and then calling :cpp:func:`PK_Verifier::check_signature`
+ on *sig*.
+
+
+Botan implements the following signature algorithms:
+
+1. RSA
+#. DSA
+#. ECDSA
+#. ECGDSA
+#. ECKDSA
+#. GOST 34.10-2001
+#. Ed25519
+#. SM2
+
+Code Example
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The following sample program below demonstrates the generation of a new ECDSA keypair over the curve secp512r1
+and a ECDSA signature using EMSA1 with SHA-256. Subsequently the computed signature is validated.
+
+.. code-block:: cpp
+
+ #include <botan/auto_rng.h>
+ #include <botan/ecdsa.h>
+ #include <botan/ec_group.h>
+ #include <botan/pubkey.h>
+ #include <botan/hex.h>
+ #include <iostream>
+
+ int main()
+ {
+ Botan::AutoSeeded_RNG rng;
+ // Generate ECDSA keypair
+ Botan::ECDSA_PrivateKey key(rng, Botan::EC_Group("secp521r1"));
+
+ std::string text("This is a tasty burger!");
+ std::vector<uint8_t> data(text.data(),text.data()+text.length());
+ // sign data
+ Botan::PK_Signer signer(key, rng, "EMSA1(SHA-256)");
+ signer.update(data);
+ std::vector<uint8_t> signature = signer.signature(rng);
+ std::cout << "Signature:" << std::endl << Botan::hex_encode(signature);
+ // verify signature
+ Botan::PK_Verifier verifier(key, "EMSA1(SHA-256)");
+ verifier.update(data);
+ std::cout << std::endl << "is " << (verifier.check_signature(signature)? "valid" : "invalid");
+ return 0;
+ }
+
+
+Ed25519 Variants
+^^^^^^^^^^^^^^^^^^
+
+Most signature schemes in Botan follow a hash-then-sign paradigm. That is, the
+entire message is digested to a fixed length representative using a collision
+resistant hash function, and then the digest is signed. Ed25519 instead signs
+the message directly. This is beneficial, in that the Ed25519 design should
+remain secure even in the (extremely unlikely) event that a collision attack on
+SHA-512 is found. However it means the entire message must be buffered in
+memory, which can be a problem for many applications which might need to sign
+large inputs. To use this variety of Ed25519, use a padding name of "Pure".
+
+Ed25519ph (pre-hashed) instead hashes the message with SHA-512 and then signs
+the digest plus a special prefix specified in RFC 8032. To use it, specify
+padding name "Ed25519ph".
+
+Another variant of pre-hashing is used by GnuPG. There the message is digested
+with any hash function, then the digest is signed. To use it, specify any valid
+hash function. Even if SHA-512 is used, this variant is not compatible with
+Ed25519ph.
+
+For best interop with other systems, prefer "Ed25519ph".
+
+Key Agreement
+---------------------------------
+
+You can get a hold of a ``PK_Key_Agreement_Scheme`` object by calling
+``get_pk_kas`` with a key that is of a type that supports key
+agreement (such as a Diffie-Hellman key stored in a ``DH_PrivateKey``
+object), and the name of a key derivation function. This can be "Raw",
+meaning the output of the primitive itself is returned as the key, or
+"KDF1(hash)" or "KDF2(hash)" where "hash" is any string you happen to
+like (hopefully you like strings like "SHA-256" or "RIPEMD-160"), or
+"X9.42-PRF(keywrap)", which uses the PRF specified in ANSI X9.42. It
+takes the name or OID of the key wrap algorithm that will be used to
+encrypt a content encryption key.
+
+How key agreement works is that you trade public values with some
+other party, and then each of you runs a computation with the other's
+value and your key (this should return the same result to both
+parties). This computation can be called by using
+``derive_key`` with either a byte array/length pair, or a
+``secure_vector<uint8_t>`` than holds the public value of the other
+party. The last argument to either call is a number that specifies how
+long a key you want.
+
+Depending on the KDF you're using, you *might not* get back a key
+of the size you requested. In particular "Raw" will return a number
+about the size of the Diffie-Hellman modulus, and KDF1 can only return
+a key that is the same size as the output of the hash. KDF2, on the
+other hand, will always give you a key exactly as long as you request,
+regardless of the underlying hash used with it. The key returned is a
+``SymmetricKey``, ready to pass to a block cipher, MAC, or other
+symmetric algorithm.
+
+The public value that should be used can be obtained by calling
+``public_data``, which exists for any key that is associated with a
+key agreement algorithm. It returns a ``secure_vector<uint8_t>``.
+
+"KDF2(SHA-256)" is by far the preferred algorithm for key derivation
+in new applications. The X9.42 algorithm may be useful in some
+circumstances, but unless you need X9.42 compatibility, KDF2 is easier
+to use.
+
+
+Botan implements the following key agreement methods:
+
+1. ECDH over GF(p) Weierstrass curves
+#. ECDH over x25519
+#. DH over prime fields
+#. McEliece
+#. NewHope
+
+Code Example
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The code below performs an unauthenticated ECDH key agreement using the secp521r elliptic curve and
+applies the key derivation function KDF2(SHA-256) with 256 bit output length to the computed shared secret.
+
+.. code-block:: cpp
+
+ #include <botan/auto_rng.h>
+ #include <botan/ecdh.h>
+ #include <botan/ec_group.h>
+ #include <botan/pubkey.h>
+ #include <botan/hex.h>
+ #include <iostream>
+
+ int main()
+ {
+ Botan::AutoSeeded_RNG rng;
+ // ec domain and
+ Botan::EC_Group domain("secp521r1");
+ std::string kdf = "KDF2(SHA-256)";
+ // generate ECDH keys
+ Botan::ECDH_PrivateKey keyA(rng, domain);
+ Botan::ECDH_PrivateKey keyB(rng, domain);
+ // Construct key agreements
+ Botan::PK_Key_Agreement ecdhA(keyA,rng,kdf);
+ Botan::PK_Key_Agreement ecdhB(keyB,rng,kdf);
+ // Agree on shared secret and derive symmetric key of 256 bit length
+ Botan::secure_vector<uint8_t> sA = ecdhA.derive_key(32,keyB.public_value()).bits_of();
+ Botan::secure_vector<uint8_t> sB = ecdhB.derive_key(32,keyA.public_value()).bits_of();
+
+ if(sA != sB)
+ return 1;
+
+ std::cout << "agreed key: " << std::endl << Botan::hex_encode(sA);
+ return 0;
+ }
+
+
+.. _mceliece:
+
+McEliece
+--------------------------
+
+McEliece is a cryptographic scheme based on error correcting codes which is
+thought to be resistant to quantum computers. First proposed in 1978, it is fast
+and patent-free. Variants have been proposed and broken, but with suitable
+parameters the original scheme remains secure. However the public keys are quite
+large, which has hindered deployment in the past.
+
+The implementation of McEliece in Botan was contributed by cryptosource GmbH. It
+is based on the implementation HyMES, with the kind permission of Nicolas
+Sendrier and INRIA to release a C++ adaption of their original C code under the
+Botan license. It was then modified by Falko Strenzke to add side channel and
+fault attack countermeasures. You can read more about the implementation at
+http://www.cryptosource.de/docs/mceliece_in_botan.pdf
+
+Encryption in the McEliece scheme consists of choosing a message block of size
+`n`, encoding it in the error correcting code which is the public key, then
+adding `t` bit errors. The code is created such that knowing only the public
+key, decoding `t` errors is intractable, but with the additional knowledge of
+the secret structure of the code a fast decoding technique exists.
+
+The McEliece implementation in HyMES, and also in Botan, uses an optimization to
+reduce the public key size, by converting the public key into a systemic code.
+This means a portion of the public key is a identity matrix, and can be excluded
+from the published public key. However it also means that in McEliece the
+plaintext is represented directly in the ciphertext, with only a small number of
+bit errors. Thus it is absolutely essential to only use McEliece with a CCA2
+secure scheme.
+
+One such scheme, KEM, is provided in Botan currently. It it a somewhat unusual
+scheme in that it outputs two values, a symmetric key for use with an AEAD, and
+an encrypted key. It does this by choosing a random plaintext (n - log2(n)*t
+bits) using ``McEliece_PublicKey::random_plaintext_element``. Then a random
+error mask is chosen and the message is coded and masked. The symmetric key is
+SHA-512(plaintext || error_mask). As long as the resulting key is used with a
+secure AEAD scheme (which can be used for transporting arbitrary amounts of
+data), CCA2 security is provided.
+
+In ``mcies.h`` there are functions for this combination:
+
+.. cpp:function:: secure_vector<uint8_t> mceies_encrypt(const McEliece_PublicKey& pubkey, \
+ const secure_vector<uint8_t>& pt, \
+ uint8_t ad[], size_t ad_len, \
+ RandomNumberGenerator& rng, \
+ const std::string& aead = "AES-256/OCB")
+
+.. cpp:function:: secure_vector<uint8_t> mceies_decrypt(const McEliece_PrivateKey& privkey, \
+ const secure_vector<uint8_t>& ct, \
+ uint8_t ad[], size_t ad_len, \
+ const std::string& aead = "AES-256/OCB")
+
+For a given security level (SL) a McEliece key would use
+parameters n and t, and have the corresponding key sizes listed:
+
++-----+------+-----+---------------+----------------+
+| SL | n | t | public key KB | private key KB |
++=====+======+=====+===============+================+
+| 80 | 1632 | 33 | 59 | 140 |
++-----+------+-----+---------------+----------------+
+| 107 | 2280 | 45 | 128 | 300 |
++-----+------+-----+---------------+----------------+
+| 128 | 2960 | 57 | 195 | 459 |
++-----+------+-----+---------------+----------------+
+| 147 | 3408 | 67 | 265 | 622 |
++-----+------+-----+---------------+----------------+
+| 191 | 4624 | 95 | 516 | 1234 |
++-----+------+-----+---------------+----------------+
+| 256 | 6624 | 115 | 942 | 2184 |
++-----+------+-----+---------------+----------------+
+
+You can check the speed of McEliece with the suggested parameters above
+using ``botan speed McEliece``
+
+
+eXtended Merkle Signature Scheme (XMSS)
+----------------------------------------
+
+Botan implements the single tree version of the eXtended Merkle Signature
+Scheme (XMSS) using Winternitz One Time Signatures+ (WOTS+). The implementation
+is based on `RFC 8391 "XMSS: eXtended Merkle Signature Scheme"
+<https://tools.ietf.org/html/rfc8391>`_.
+
+XMSS uses the Botan interfaces for public key cryptography.
+The following algorithms are implemented:
+
+1. XMSS-SHA2_10_256
+# XMSS-SHA2_16_256
+# XMSS-SHA2_20_256
+# XMSS-SHA2_10_512
+# XMSS-SHA2_16_512
+# XMSS-SHA2_20_512
+# XMSS-SHAKE_10_256
+# XMSS-SHAKE_16_256
+# XMSS-SHAKE_20_256
+# XMSS-SHAKE_10_512
+# XMSS-SHAKE_16_512
+# XMSS-SHAKE_20_512
+
+The algorithm name contains the hash function name, tree height and digest
+width defined by the corresponding parameter set. Choosing `XMSS-SHA2_10_256`
+for instance will use the SHA2-256 hash function to generate a tree of height
+ten.
+
+Code Example
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The following code snippet shows a minimum example on how to create an XMSS
+public/private key pair and how to use these keys to create and verify a
+signature:
+
+.. code-block:: cpp
+
+ #include <iostream>
+ #include <botan/secmem.h>
+ #include <botan/auto_rng.h>
+ #include <botan/xmss.h>
+
+ int main()
+ {
+ // Create a random number generator used for key generation.
+ Botan::AutoSeeded_RNG rng;
+
+ // 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);
+ Botan::XMSS_PublicKey public_key(private_key);
+
+ // create signature operation using the private key.
+ std::unique_ptr<Botan::PK_Ops::Signature> sig_op =
+ private_key.create_signature_op(rng, "", "");
+
+ // create and sign a message using the signature operation.
+ Botan::secure_vector<uint8_t> msg { 0x01, 0x02, 0x03, 0x04 };
+ sig_op->update(msg.data(), msg.size());
+ Botan::secure_vector<uint8_t> sig = sig_op->sign(rng);
+
+ // create verification operation using the public key
+ std::unique_ptr<Botan::PK_Ops::Verification> ver_op =
+ public_key.create_verification_op("", "");
+
+ // verify the signature for the previously generated message.
+ ver_op->update(msg.data(), msg.size());
+ if(ver_op->is_valid_signature(sig.data(), sig.size()))
+ {
+ std::cout << "Success." << std::endl;
+ }
+ else
+ {
+ std::cout << "Error." << std::endl;
+ }
+ }
diff --git a/comm/third_party/botan/doc/api_ref/python.rst b/comm/third_party/botan/doc/api_ref/python.rst
new file mode 100644
index 0000000000..e863b648b4
--- /dev/null
+++ b/comm/third_party/botan/doc/api_ref/python.rst
@@ -0,0 +1,668 @@
+
+Python Binding
+========================================
+
+.. versionadded:: 1.11.14
+
+.. highlight:: python
+
+.. py:module:: botan
+
+The Python binding is based on the `ffi` module of botan and the
+`ctypes` module of the Python standard library.
+
+Starting in 2.8, the class names were renamed to match Python standard
+conventions. However aliases are defined which allow older code to
+continue to work; the older names are mentioned as "previously X".
+These aliases will be removed in a future major release.
+
+Versioning
+----------------------------------------
+.. py:function:: version_major()
+
+ Returns the major number of the library version.
+
+.. py:function:: version_minor()
+
+ Returns the minor number of the library version.
+
+.. py:function:: version_patch()
+
+ Returns the patch number of the library version.
+
+.. py:function:: version_string()
+
+ Returns a free form version string for the library
+
+Random Number Generators
+----------------------------------------
+
+.. py:class:: RandomNumberGenerator(rng_type = 'system')
+
+ 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)`.
+
+ .. py:method:: get(length)
+
+ Return some bytes
+
+ .. py:method:: reseed(bits = 256)
+
+ Meaningless on system RNG, on userspace RNG causes a reseed/rekey
+
+ .. py:method:: reseed_from_rng(source_rng, bits = 256)
+
+ Take bits from the source RNG and use it to seed ``self``
+
+ .. py:method:: add_entropy(seed)
+
+ Add some unpredictable seed data to the RNG
+
+Hash Functions
+----------------------------------------
+
+.. py:class:: HashFunction(algo)
+
+ Previously ``hash_function``
+
+ The ``algo`` param is a string (eg 'SHA-1', 'SHA-384', 'BLAKE2b')
+
+ .. py:method:: algo_name()
+
+ Returns the name of this algorithm
+
+ .. py:method:: clear()
+
+ Clear state
+
+ .. py:method:: output_length()
+
+ Return output length in bytes
+
+ .. py:method:: update(x)
+
+ Add some input
+
+ .. py:method:: final()
+
+ Returns the hash of all input provided, resets
+ for another message.
+
+Message Authentication Codes
+----------------------------------------
+
+.. py:class:: MsgAuthCode(algo)
+
+ Previously ``message_authentication_code``
+
+ Algo is a string (eg 'HMAC(SHA-256)', 'Poly1305', 'CMAC(AES-256)')
+
+ .. py:method:: algo_name()
+
+ Returns the name of this algorithm
+
+ .. py:method:: clear()
+
+ Clear internal state including the key
+
+ .. py:method:: output_length()
+
+ Return the output length in bytes
+
+ .. py:method:: set_key(key)
+
+ Set the key
+
+ .. py:method:: update(x)
+
+ Add some input
+
+ .. py:method:: final()
+
+ Returns the MAC of all input provided, resets
+ for another message with the same key.
+
+Ciphers
+----------------------------------------
+
+.. py:class:: SymmetricCipher(object, algo, encrypt = True)
+
+ Previously ``cipher``
+
+ The algorithm is spcified as a string (eg 'AES-128/GCM',
+ 'Serpent/OCB(12)', 'Threefish-512/EAX').
+
+ Set the second param to False for decryption
+
+ .. py:method:: algo_name()
+
+ Returns the name of this algorithm
+
+ .. py:method:: tag_length()
+
+ Returns the tag length (0 for unauthenticated modes)
+
+ .. py:method:: default_nonce_length()
+
+ Returns default nonce length
+
+ .. py:method:: update_granularity()
+
+ Returns update block size. Call to update() must provide input
+ of exactly this many bytes
+
+ .. py:method:: is_authenticated()
+
+ Returns True if this is an AEAD mode
+
+ .. py:method:: valid_nonce_length(nonce_len)
+
+ Returns True if nonce_len is a valid nonce len for this mode
+
+ .. py:method:: clear()
+
+ Resets all state
+
+ .. py:method:: set_key(key)
+
+ Set the key
+
+ .. py:method:: set_assoc_data(ad)
+
+ Sets the associated data. Fails if this is not an AEAD mode
+
+ .. py:method:: start(nonce)
+
+ Start processing a message using nonce
+
+ .. py:method:: update(txt)
+
+ 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
+
+ .. py:method:: finish(txt = 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
+
+Bcrypt
+----------------------------------------
+
+.. py:function:: bcrypt(passwd, rng, work_factor = 10)
+
+ Provided the password and an RNG object, returns a bcrypt string
+
+.. py:function:: check_bcrypt(passwd, bcrypt)
+
+ Check a bcrypt hash against the provided password, returning True
+ iff the password matches.
+
+PBKDF
+----------------------------------------
+
+.. py:function:: pbkdf(algo, password, out_len, iterations = 100000, salt = None)
+
+ 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
+
+.. py:function:: pbkdf_timed(algo, password, out_len, ms_to_run = 300, salt = rng().get(12))
+
+ 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
+
+Scrypt
+---------------
+
+.. versionadded:: 2.8.0
+
+.. py:function:: scrypt(out_len, password, salt, N=1024, r=8, p=8)
+
+ Runs Scrypt key derivation function over the specified password
+ and salt using Scrypt parameters N, r, p.
+
+KDF
+----------------------------------------
+
+.. py:function:: kdf(algo, secret, out_len, salt)
+
+ Performs a key derviation function (such as "HKDF(SHA-384)") over
+ the provided secret and salt values. Returns a value of the
+ specified length.
+
+Public Key
+----------------------------------------
+
+.. py:class:: PublicKey(object)
+
+ Previously ``public_key``
+
+ .. py:classmethod:: load(val)
+
+ Load a public key. The value should be a PEM or DER blob.
+
+ .. py:classmethod:: load_rsa(n, e)
+
+ Load an RSA public key giving the modulus and public exponent
+ as integers.
+
+ .. py:classmethod:: load_dsa(p, q, g, y)
+
+ Load an DSA public key giving the parameters and public value
+ as integers.
+
+ .. py:classmethod:: load_dh(p, g, y)
+
+ Load an Diffie-Hellman public key giving the parameters and
+ public value as integers.
+
+ .. py:classmethod:: load_elgamal(p, q, g, y)
+
+ Load an ElGamal public key giving the parameters and
+ public value as integers.
+
+ .. py:classmethod:: load_ecdsa(curve, pub_x, pub_y)
+
+ 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.
+
+ .. py:classmethod:: load_ecdh(curve, pub_x, pub_y)
+
+ 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.
+
+ .. py:classmethod:: load_sm2(curve, pub_x, pub_y)
+
+ 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.
+
+ .. py:method:: check_key(rng_obj, strong=True):
+
+ Test the key for consistency. If ``strong`` is ``True`` then
+ more expensive tests are performed.
+
+ .. py:method:: export(pem=False)
+
+ 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.
+
+ .. py:method:: to_der()
+
+ Like ``self.export(False)``
+
+ .. py:method:: to_pem()
+
+ Like ``self.export(True)``
+
+ .. py:method:: get_field(field_name)
+
+ 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")``.
+
+ .. py:method:: fingerprint(hash = 'SHA-256')
+
+ Returns a hash of the public key
+
+ .. py:method:: algo_name()
+
+ Returns the algorithm name
+
+ .. py:method:: estimated_strength()
+
+ Returns the estimated strength of this key against known attacks
+ (NFS, Pollard's rho, etc)
+
+Private Key
+----------------------------------------
+
+.. py:class:: PrivateKey
+
+ Previously ``private_key``
+
+ .. py:classmethod:: create(algo, param, rng)
+
+ 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 group
+ "curve25519" (which is actually a completely distinct key type
+ with a non-standard encoding).
+
+ .. py:classmethod:: load(val, passphrase="")
+
+ Return a private key (DER or PEM formats accepted)
+
+ .. py:classmethod:: load_rsa(p, q, e)
+
+ Return a private RSA key
+
+ .. py:classmethod:: load_dsa(p, q, g, x)
+
+ Return a private DSA key
+
+ .. py:classmethod:: load_dh(p, g, x)
+
+ Return a private DH key
+
+ .. py:classmethod:: load_elgamal(p, q, g, x)
+
+ Return a private ElGamal key
+
+ .. py:classmethod:: load_ecdsa(curve, x)
+
+ Return a private ECDSA key
+
+ .. py:classmethod:: load_ecdh(curve, x)
+
+ Return a private ECDH key
+
+ .. py:classmethod:: load_sm2(curve, x)
+
+ Return a private SM2 key
+
+ .. py:method:: get_public_key()
+
+ Return a public_key object
+
+ .. py:method:: to_pem()
+
+ Return the PEM encoded private key (unencrypted). Like ``self.export(True)``
+
+ .. py:method:: to_der()
+
+ Return the PEM encoded private key (unencrypted). Like ``self.export(False)``
+
+ .. py:method:: check_key(rng_obj, strong=True):
+
+ Test the key for consistency. If ``strong`` is ``True`` then
+ more expensive tests are performed.
+
+ .. py:method:: algo_name()
+
+ Returns the algorithm name
+
+ .. py:method:: export(pem=False)
+
+ 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.
+
+ .. py:method:: export_encrypted(passphrase, rng, pem=False, msec=300, cipher=None, pbkdf=None)
+
+ 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.
+
+ .. py:method:: get_field(field_name)
+
+ 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.
+
+Public Key Operations
+----------------------------------------
+
+.. py:class:: PKEncrypt(pubkey, padding)
+
+ Previously ``pk_op_encrypt``
+
+ .. py:method:: encrypt(msg, rng)
+
+.. py:class:: PKDecrypt(privkey, padding)
+
+ Previously ``pk_op_decrypt``
+
+ .. py:method:: decrypt(msg)
+
+.. py:class:: PKSign(privkey, hash_w_padding)
+
+ Previously ``pk_op_sign``
+
+ .. py:method:: update(msg)
+ .. py:method:: finish(rng)
+
+.. py:class:: PKVerify(pubkey, hash_w_padding)
+
+ Previously ``pk_op_verify``
+
+ .. py:method:: update(msg)
+ .. py:method:: check_signature(signature)
+
+.. py:class:: PKKeyAgreement(privkey, kdf)
+
+ Previously ``pk_op_key_agreement``
+
+ .. py:method:: public_value()
+
+ Returns the public value to be passed to the other party
+
+ .. py:method:: agree(other, key_len, salt)
+
+ Returns a key derived by the KDF.
+
+Multiple Precision Integers (MPI)
+-------------------------------------
+.. versionadded:: 2.8.0
+
+.. py:class:: MPI(initial_value=None, radix=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.
+
+
+ Most of the usual arithmetic operators (``__add__``, ``__mul__``, etc) are
+ defined.
+
+ .. py:method:: inverse_mod(modulus)
+
+ Return the inverse of ``self`` modulo ``modulus``, or zero if no inverse exists
+
+ .. py:method:: is_prime(rng, prob=128)
+
+ Test if ``self`` is prime
+
+ .. py:method:: pow_mod(exponent, modulus):
+
+ Return ``self`` to the ``exponent`` power modulo ``modulus``
+
+ .. py:method:: mod_mul(other, modulus):
+
+ Return the multiplication product of ``self`` and ``other`` modulo ``modulus``
+
+ .. py:method:: gcd(other):
+
+ Return the greatest common divisor of ``self`` and ``other``
+
+
+Format Preserving Encryption (FE1 scheme)
+-----------------------------------------
+.. versionadded:: 2.8.0
+
+.. py:class:: FormatPreservingEncryptionFE1(modulus, key, rounds=5, compat_mode=False)
+
+ Initialize an instance for format preserving encryption
+
+ .. py:method:: encrypt(msg, tweak)
+
+ The msg should be a botan2.MPI or an object which can be converted to one
+
+ .. py:method:: decrypt(msg, tweak)
+
+ The msg should be a botan2.MPI or an object which can be converted to one
+
+HOTP
+-----------------------------------------
+.. versionadded:: 2.8.0
+
+.. py:class:: HOTP(key, hash="SHA-1", digits=6)
+
+ .. py:method:: generate(counter)
+
+ Generate an HOTP code for the provided counter
+
+ .. py:method:: check(code, counter, resync_range=0)
+
+ 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.
+
+X509Cert
+-----------------------------------------
+
+.. py:class:: X509Cert(filename=None, buf=None)
+
+ .. py:method:: time_starts()
+
+ 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.
+
+ .. py:method:: time_expires()
+
+ 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.
+
+ .. py:method:: to_string()
+
+ Format the certificate as a free-form string.
+
+ .. py:method:: fingerprint(hash_algo='SHA-256')
+
+ 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.
+
+ .. py:method:: serial_number()
+
+ Return the serial number of the certificate.
+
+ .. py:method:: authority_key_id()
+
+ Return the authority key ID set in the certificate, which may be empty.
+
+ .. py:method:: subject_key_id()
+
+ Return the subject key ID set in the certificate, which may be empty.
+
+ .. py:method:: subject_public_key_bits()
+
+ Get the serialized representation of the public key included in this certificate.
+
+ .. py:method:: subject_public_key()
+
+ Get the public key included in this certificate as an object of class ``PublicKey``.
+
+ .. py:method:: subject_dn(key, index)
+
+ Get a value from the subject DN field.
+
+ ``key`` specifies a value to get, for instance ``"Name"`` or `"Country"`.
+
+ .. py:method:: issuer_dn(key, index)
+
+ Get a value from the issuer DN field.
+
+ ``key`` specifies a value to get, for instance ``"Name"`` or `"Country"`.
+
+ .. py:method:: hostname_match(hostname)
+
+ Return True if the Common Name (CN) field of the certificate matches a given ``hostname``.
+
+ .. py:method:: not_before()
+
+ Return the time the certificate becomes valid, as seconds since epoch.
+
+ .. py:method:: not_after()
+
+ Return the time the certificate expires, as seconds since epoch.
+
+ .. py:method:: allowed_usage(usage_list)
+
+ 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"``.
+
+ .. py:method:: verify(intermediates=None, \
+ trusted=None, \
+ trusted_path=None, \
+ required_strength=0, \
+ hostname=None, \
+ reference_time=0 \
+ crls=None)
+
+ Verify a certificate. Returns 0 if validation was successful, returns a positive error code
+ if the validation was unsuccesful.
+
+ ``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.
+
+ .. py:classmethod:: validation_status(error_code)
+
+ Return an informative string associated with the verification return code.
+
+ .. py:method:: is_revoked(self, crl)
+
+ Check if the certificate (``self``) is revoked on the given ``crl``.
+
+X509CRL
+-----------------------------------------
+
+.. py:class:: X509CRL(filename=None, buf=None)
+
+ Class representing an X.509 Certificate Revocation List.
+
+ 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.
+
+
+
+
+
+
diff --git a/comm/third_party/botan/doc/api_ref/rng.rst b/comm/third_party/botan/doc/api_ref/rng.rst
new file mode 100644
index 0000000000..01c27f73cf
--- /dev/null
+++ b/comm/third_party/botan/doc/api_ref/rng.rst
@@ -0,0 +1,281 @@
+.. _random_number_generators:
+
+Random Number Generators
+========================================
+
+.. cpp:class:: RandomNumberGenerator
+
+ The base class for all RNG objects, is declared in ``rng.h``.
+
+ .. cpp:function:: void randomize(uint8_t* output_array, size_t length)
+
+ Places *length* random bytes into the provided buffer.
+
+ .. cpp:function:: void randomize_with_input(uint8_t* data, size_t length, \
+ const uint8_t* extra_input, size_t extra_input_len)
+
+ Like randomize, but first incorporates the additional input field into the
+ state of the RNG. The additional input could be anything which
+ parameterizes this request. Not all RNG types accept additional inputs,
+ the value will be silently ignored when not supported.
+
+ .. cpp:function:: void randomize_with_ts_input(uint8_t* data, size_t length)
+
+ Creates a buffer with some timestamp values and calls ``randomize_with_input``
+
+ .. note::
+
+ When RDRAND is enabled and available at runtime, instead of timestamps
+ the output of RDRAND is used as the additional data.
+
+ .. cpp:function:: uint8_t next_byte()
+
+ Generates a single random byte and returns it. Note that calling this
+ function several times is much slower than calling ``randomize`` once to
+ produce multiple bytes at a time.
+
+ .. cpp:function:: void add_entropy(const uint8_t* data, size_t length)
+
+ Incorporates provided data into the state of the PRNG, if at all possible.
+ This works for most RNG types, including the system and TPM RNGs. But if
+ the RNG doesn't support this operation, the data is dropped, no error is
+ indicated.
+
+ .. cpp:function:: bool accepts_input() const
+
+ This function returns ``false`` if it is known that this RNG object cannot
+ accept external inputs. In this case, any calls to
+ :cpp:func:`RandomNumberGenerator::add_entropy` will be ignored.
+
+ .. cpp:function:: void reseed_from_rng(RandomNumberGenerator& rng, \
+ size_t poll_bits = BOTAN_RNG_RESEED_POLL_BITS)
+
+ Reseed by calling ``rng`` to acquire ``poll_bits`` data.
+
+
+RNG Types
+----------------------------------------
+
+Several different RNG types are implemented. Some access hardware RNGs, which
+are only available on certain platforms. Others are mostly useful in specific
+situations.
+
+Generally prefer using the system RNG, or if not available use ``AutoSeeded_RNG``
+which is intended to provide best possible behavior in a userspace PRNG.
+
+System_RNG
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+On systems which support it, in ``system_rng.h`` you can access a shared
+reference to a process global instance of the system PRNG (using interfaces such
+as ``/dev/urandom``, ``getrandom``, ``arc4random``, or ``RtlGenRandom``):
+
+.. cpp:function:: RandomNumberGenerator& system_rng()
+
+ Returns a reference to the system RNG
+
+There is also a wrapper class ``System_RNG`` which simply invokes on
+the return value of ``system_rng()``. This is useful in situations where
+you may sometimes want to use the system RNG and a userspace RNG in others,
+for example::
+
+ std::unique_ptr<Botan::RandomNumberGenerator> rng;
+ #if defined(BOTAN_HAS_SYSTEM_RNG)
+ rng.reset(new System_RNG);
+ #else
+ rng.reset(new AutoSeeded_RNG);
+ #endif
+
+Unlike nearly any other object in Botan it is acceptable to share a single
+instance of ``System_RNG`` between threads, because the underlying RNG is itself
+thread safe due to being serialized by a mutex in the kernel itself.
+
+AutoSeeded_RNG
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+AutoSeeded_RNG is type naming a 'best available' userspace PRNG. The
+exact definition of this has changed over time and may change in the
+future, fortunately there is no compatibility concerns when changing
+any RNG since the only expectation is it produces bits
+indistinguishable from random.
+
+.. note:: Starting in 2.16.0, AutoSeeded_RNG uses an internal lock and so is
+ safe to share among threads. However if possible it is still better to
+ use a RNG per thread as otherwise the RNG object needlessly creates a
+ point of contention. In previous versions, the RNG does not have an
+ internal lock and all access to it must be serialized.
+
+The current version uses HMAC_DRBG with either SHA-384 or SHA-256. The
+initial seed is generated either by the system PRNG (if available) or
+a default set of entropy sources. These are also used for periodic
+reseeding of the RNG state.
+
+HMAC_DRBG
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+HMAC DRBG is a random number generator designed by NIST and specified
+in SP 800-90A. It seems to be the most conservative generator of the
+NIST approved options.
+
+It can be instantiated with any HMAC but is typically used with
+SHA-256, SHA-384, or SHA-512, as these are the hash functions approved
+for this use by NIST.
+
+HMAC_DRBG's constructors are:
+
+.. cpp:class:: HMAC_DRBG
+
+ .. cpp:function:: HMAC_DRBG(std::unique_ptr<MessageAuthenticationCode> prf, \
+ RandomNumberGenerator& underlying_rng, \
+ size_t reseed_interval = BOTAN_RNG_DEFAULT_RESEED_INTERVAL, \
+ size_t max_number_of_bytes_per_request = 64 * 1024)
+
+ Creates a DRBG which will automatically reseed as required by making
+ calls to ``underlying_rng`` either after being invoked
+ ``reseed_interval`` times, or if use of ``fork`` system call is
+ detected.
+
+ You can disable automatic reseeding by setting ``reseed_interval`` to
+ zero, in which case ``underlying_rng`` will only be invoked in the case
+ of ``fork``.
+
+ The specification of HMAC DRBG requires that each invocation produce no
+ more than 64 kibibytes of data. However, the RNG interface allows
+ producing arbitrary amounts of data in a single request. To accommodate
+ this, ``HMAC_DRBG`` treats requests for more data as if they were
+ multiple requests each of (at most) the maximum size. You can specify a
+ smaller maximum size with ``max_number_of_bytes_per_request``. There is
+ normally no reason to do this.
+
+ .. cpp:function:: HMAC_DRBG(std::unique_ptr<MessageAuthenticationCode> prf, \
+ Entropy_Sources& entropy_sources, \
+ size_t reseed_interval = BOTAN_RNG_DEFAULT_RESEED_INTERVAL, \
+ size_t max_number_of_bytes_per_request = 64 * 1024)
+
+ Like above function, but instead of an RNG taking a set of entropy
+ sources to seed from as required.
+
+ .. cpp:function:: HMAC_DRBG(std::unique_ptr<MessageAuthenticationCode> prf, \
+ RandomNumberGenerator& underlying_rng, \
+ Entropy_Sources& entropy_sources, \
+ size_t reseed_interval = BOTAN_RNG_DEFAULT_RESEED_INTERVAL, \
+ size_t max_number_of_bytes_per_request = 64 * 1024)
+
+ Like above function, but taking both an RNG and a set of entropy
+ sources to seed from as required.
+
+ .. cpp:function:: HMAC_DRBG(std::unique_ptr<MessageAuthenticationCode> prf)
+
+ Creates an unseeded DRBG. You must explicitly provide seed data later
+ on in order to use this RNG. This is primarily useful for deterministic
+ key generation.
+
+ Since no source of data is available to automatically reseed, automatic
+ reseeding is disabled when this constructor is used. If the RNG object
+ detects that ``fork`` system call was used without it being
+ subsequently reseeded, it will throw an exception.
+
+ .. cpp:function:: HMAC_DRBG(const std::string& hmac_hash)
+
+ Like the constructor just taking a PRF, except instead of a PRF object,
+ a string specifying what hash to use with HMAC is provided.
+
+ChaCha_RNG
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+This is a very fast userspace PRNG based on ChaCha20 and HMAC(SHA-256). The key
+for ChaCha is derived by hashing entropy inputs with HMAC. Then the ChaCha
+keystream generator is run, first to generate the new HMAC key (used for any
+future entropy additions), then the desired RNG outputs.
+
+This RNG composes two primitives thought to be secure (ChaCha and HMAC) in a
+simple and well studied way (the extract-then-expand paradigm), but is still an
+ad-hoc and non-standard construction. It is included because it is roughly 20x
+faster then HMAC_DRBG (basically running as fast as ChaCha can generate
+keystream bits), and certain applications need access to a very fast RNG.
+
+One thing applications using ``ChaCha_RNG`` need to be aware of is that for
+performance reasons, no backtracking resistance is implemented in the RNG
+design. An attacker who recovers the ``ChaCha_RNG`` state can recover the output
+backwards in time to the last rekey and forwards to the next rekey.
+
+An explicit reseeding (:cpp:func:`RandomNumberGenerator::add_entropy`) or
+providing any input to the RNG
+(:cpp:func:`RandomNumberGenerator::randomize_with_ts_input`,
+:cpp:func:`RandomNumberGenerator::randomize_with_input`) is sufficient to cause
+a reseeding. Or, if a RNG or entropy source was provided to the ``ChaCha_RNG``
+constructor, then reseeding will be performed automatically after a certain
+interval of requests.
+
+Processor_RNG
+^^^^^^^^^^^^^^^^^
+
+This RNG type directly invokes a CPU instruction capable of generating
+a cryptographically secure random number. On x86 it uses ``rdrand``,
+on POWER ``darn``. If the relevant instruction is not available, the
+constructor of the class will throw at runtime. You can test
+beforehand by checking the result of ``Processor_RNG::available()``.
+
+TPM_RNG
+^^^^^^^^^^^^^^^^^
+
+This RNG type allows using the RNG exported from a TPM chip.
+
+PKCS11_RNG
+^^^^^^^^^^^^^^^^^
+
+This RNG type allows using the RNG exported from a hardware token accessed via PKCS11.
+
+Entropy Sources
+---------------------------------
+
+An ``EntropySource`` is an abstract representation of some method of
+gather "real" entropy. This tends to be very system dependent. The
+*only* way you should use an ``EntropySource`` is to pass it to a PRNG
+that will extract entropy from it -- never use the output directly for
+any kind of key or nonce generation!
+
+``EntropySource`` has a pair of functions for getting entropy from
+some external source, called ``fast_poll`` and ``slow_poll``. These
+pass a buffer of bytes to be written; the functions then return how
+many bytes of entropy were gathered.
+
+Note for writers of ``EntropySource`` subclasses: it isn't necessary
+to use any kind of cryptographic hash on your output. The data
+produced by an EntropySource is only used by an application after it
+has been hashed by the ``RandomNumberGenerator`` that asked for the
+entropy, thus any hashing you do will be wasteful of both CPU cycles
+and entropy.
+
+The following entropy sources are currently used:
+
+ * The system RNG (``arc4random``, ``/dev/urandom``, or ``RtlGenRandom``).
+ * RDRAND and RDSEED are used if available, but not counted as contributing entropy
+ * ``/dev/random`` and ``/dev/urandom``. This may be redundant with the system RNG
+ * ``getentropy``, only used on OpenBSD currently
+ * ``/proc`` walk: read files in ``/proc``. Last ditch protection against
+ flawed system RNG.
+ * Win32 stats: takes snapshot of current system processes. Last ditch
+ protection against flawed system RNG.
+
+Fork Safety
+---------------------------------
+
+On Unix platforms, the ``fork()`` and ``clone()`` system calls can
+be used to spawn a new child process. Fork safety ensures that the
+child process doesn't see the same output of random bytes as the
+parent process. Botan tries to ensure fork safety by feeding the
+process ID into the internal state of the random generator and by
+automatically reseeding the random generator if the process ID
+changed between two requests of random bytes. However, this does
+not protect against PID wrap around. The process ID is usually
+implemented as a 16 bit integer. In this scenario, a process will
+spawn a new child process, which exits the parent process and
+spawns a new child process himself. If the PID wrapped around, the
+second child process may get assigned the process ID of it's
+grandparent and the fork safety can not be ensured.
+
+Therefore, it is strongly recommended to explicitly reseed any
+userspace random generators after forking a new process. If this is
+not possible in your application, prefer using the system PRNG
+instead.
diff --git a/comm/third_party/botan/doc/api_ref/roughtime.rst b/comm/third_party/botan/doc/api_ref/roughtime.rst
new file mode 100644
index 0000000000..8e6cd7c20d
--- /dev/null
+++ b/comm/third_party/botan/doc/api_ref/roughtime.rst
@@ -0,0 +1,6 @@
+Roughtime
+===========
+
+.. versionadded:: 2.13.0
+
+Botan includes a Roughtime client, available in ``botan/roughtime.h``
diff --git a/comm/third_party/botan/doc/api_ref/secmem.rst b/comm/third_party/botan/doc/api_ref/secmem.rst
new file mode 100644
index 0000000000..8dd479b7e8
--- /dev/null
+++ b/comm/third_party/botan/doc/api_ref/secmem.rst
@@ -0,0 +1,31 @@
+
+Memory container
+========================================
+
+A major concern with mixing modern multi-user OSes and cryptographic
+code is that at any time the code (including secret keys) could be
+swapped to disk, where it can later be read by an attacker, or left
+floating around in memory for later retrieval.
+
+For this reason the library uses a ``std::vector`` with a custom
+allocator that will zero memory before deallocation, named via typedef
+as ``secure_vector``. Because it is simply a STL vector with a custom
+allocator, it has an identical API to the ``std::vector`` you know and
+love.
+
+Some operating systems offer the ability to lock memory into RAM,
+preventing swapping from occurring. Typically this operation is
+restricted to privileged users (root or admin), however some OSes
+including Linux and FreeBSD allow normal users to lock a small amount
+of memory. On these systems, allocations first attempt to allocate out
+of this small locked pool, and then if that fails will fall back to
+normal heap allocations.
+
+The ``secure_vector`` template is only meant for primitive data types
+(bytes or ints): if you want a container of higher level Botan
+objects, you can just use a ``std::vector``, since these objects know
+how to clear themselves when they are destroyed. You cannot, however,
+have a ``std::vector`` (or any other container) of ``Pipe`` objects or
+filters, because these types have pointers to other filters, and
+implementing copy constructors for these types would be both hard and
+quite expensive (vectors of pointers to such objects is fine, though).
diff --git a/comm/third_party/botan/doc/api_ref/srp.rst b/comm/third_party/botan/doc/api_ref/srp.rst
new file mode 100644
index 0000000000..cf0386b539
--- /dev/null
+++ b/comm/third_party/botan/doc/api_ref/srp.rst
@@ -0,0 +1,77 @@
+Secure Remote Password
+========================================
+
+The library contains an implementation of the
+`SRP6-a <http://srp.stanford.edu/design.html>`_ password authenticated
+key exchange protocol in ``srp6.h``.
+
+A SRP client provides what is called a SRP *verifier* to the server.
+This verifier is based on a password, but the password cannot be
+easily derived from the verifier (however brute force attacks are
+possible). Later, the client and server can perform an SRP exchange,
+which results in a shared secret key. This key can be used for mutual
+authentication and/or encryption.
+
+SRP works in a discrete logarithm group. Special parameter sets for
+SRP6 are defined, denoted in the library as "modp/srp/<size>", for
+example "modp/srp/2048".
+
+.. warning::
+
+ While knowledge of the verifier does not easily allow an attacker
+ to get the raw password, they could still use the verifier to
+ impersonate the server to the client, so verifiers should be
+ protected as carefully as a plaintext password would be.
+
+.. cpp:function:: BigInt generate_srp6_verifier( \
+ const std::string& username, \
+ const std::string& password, \
+ const std::vector<uint8_t>& salt, \
+ const std::string& group_id, \
+ const std::string& hash_id)
+
+ Generates a new verifier using the specified password and salt.
+ This is stored by the server. The salt must also be stored. Later,
+ the given username and password are used to by the client during
+ the key agreement step.
+
+.. cpp:function:: std::string srp6_group_identifier( \
+ const BigInt& N, const BigInt& g)
+
+.. cpp:class:: SRP6_Server_Session
+
+ .. cpp:function:: BigInt step1(const BigInt& v, \
+ const std::string& group_id, \
+ const std::string& hash_id, \
+ RandomNumberGenerator& rng)
+
+ Takes a verifier (generated by generate_srp6_verifier) along
+ with the group_id, and output a value `B` which is provided to
+ the client.
+
+ .. cpp:function:: SymmetricKey step2(const BigInt& A)
+
+ Takes the parameter A generated by srp6_client_agree,
+ and return the shared secret key.
+
+ In the event of an impersonation attack (or wrong username/password, etc)
+ no error occurs, but the key returned will be different on the two sides.
+ The two sides must verify each other, for example by using the shared
+ secret to key an HMAC and then exchanging authenticated messages.
+
+.. cpp:function:: std::pair<BigInt,SymmetricKey> srp6_client_agree( \
+ const std::string& username, \
+ const std::string& password, \
+ const std::string& group_id, \
+ const std::string& hash_id, \
+ const std::vector<uint8_t>& salt, \
+ const BigInt& B, \
+ RandomNumberGenerator& rng)
+
+ The client receives these parameters from the server, except for
+ the username and password which are provided by the user. The
+ parameter B is the output of `step1`.
+
+ The client agreement step outputs a shared symmetric key along
+ with the parameter A which is returned to the server (and allows
+ it the compute the shared key).
diff --git a/comm/third_party/botan/doc/api_ref/stream_ciphers.rst b/comm/third_party/botan/doc/api_ref/stream_ciphers.rst
new file mode 100644
index 0000000000..dfee40970f
--- /dev/null
+++ b/comm/third_party/botan/doc/api_ref/stream_ciphers.rst
@@ -0,0 +1,211 @@
+Stream Ciphers
+========================
+
+In contrast to block ciphers, stream ciphers operate on a plaintext stream
+instead of blocks. Thus encrypting data results in changing the internal state
+of the cipher and encryption of plaintext with arbitrary length is possible in
+one go (in byte amounts). All implemented stream ciphers derive from the base
+class :cpp:class:`StreamCipher` (`botan/stream_cipher.h`).
+
+.. warning::
+
+ Using a stream cipher without an authentication code is extremely insecure,
+ because an attacker can trivially modify messages. Prefer using an
+ authenticated cipher mode such as GCM or SIV.
+
+.. warning::
+
+ Encrypting more than one message with the same key requires careful management
+ of initialization vectors. Otherwise the keystream will be reused, which causes
+ the security of the cipher to completely fail.
+
+.. cpp:class:: StreamCipher
+
+ .. cpp:function:: std::string name() const
+
+ Returns a human-readable string of the name of this algorithm.
+
+ .. cpp:function:: void clear()
+
+ Clear the key.
+
+ .. cpp:function:: StreamCipher* clone() const
+
+ Return a newly allocated object of the same type as this one.
+
+ .. cpp:function:: void set_key(const uint8_t* key, size_t length)
+
+ Set the stream cipher key. If the length is not accepted, an
+ ``Invalid_Key_Length`` exception is thrown.
+
+ .. cpp:function:: bool valid_keylength(size_t length) const
+
+ This function returns true if and only if *length* is a valid
+ keylength for the algorithm.
+
+ .. cpp:function:: size_t minimum_keylength() const
+
+ Return the smallest key length (in bytes) that is acceptable for the
+ algorithm.
+
+ .. cpp:function:: size_t maximum_keylength() const
+
+ Return the largest key length (in bytes) that is acceptable for the
+ algorithm.
+
+ .. cpp:function:: bool valid_iv_length(size_t iv_len) const
+
+ This function returns true if and only if *length* is a valid IV length for
+ the stream cipher. Some ciphers do not support IVs at all, and will return
+ false for any value except zero.
+
+ .. cpp:function:: size_t default_iv_length() const
+
+ Returns some default IV size, normally the largest IV supported by the cipher.
+ If this function returns zero, then IVs are not supported and any call to
+ ``set_iv`` with a non-empty value will fail.
+
+ .. cpp:function:: void set_iv(const uint8_t*, size_t len)
+
+ Load IV into the stream cipher state. This should happen after the key is
+ set and before any operation (encrypt/decrypt/seek) is called.
+
+ If the cipher does not support IVs, then a call with ``len`` equal to zero
+ will be accepted and any other length will cause a ``Invalid_IV_Length``
+ exception.
+
+ .. cpp:function:: void seek(uint64_t offset)
+
+ Sets the state of the stream cipher and keystream according to the passed
+ *offset*, exactly as if *offset* bytes had first been encrypted. The key
+ and (if required) the IV have to be set before this can be called. Not all
+ ciphers support seeking; such objects will throw ``Not_Implemented`` in
+ this case.
+
+ .. cpp:function:: void cipher(const uint8_t* in, uint8_t* out, size_t n)
+
+ Processes *n* bytes plain/ciphertext from *in* and writes the result to *out*.
+
+ .. cpp:function:: void cipher1(uint8_t* inout, size_t n)
+
+ Processes *n* bytes plain/ciphertext in place. Acts like :cpp:func:`cipher`\ (inout, inout, n).
+
+ .. cpp:function:: void encipher(std::vector<uint8_t> inout)
+ .. cpp:function:: void encrypt(std::vector<uint8_t> inout)
+ .. cpp:function:: void decrypt(std::vector<uint8_t> inout)
+
+ Processes plain/ciphertext *inout* in place. Acts like :cpp:func:`cipher`\ (inout.data(), inout.data(), inout.size()).
+
+Code Example
+-----------------
+
+The following code encrypts a provided plaintext using ChaCha20.
+
+.. code-block:: cpp
+
+ #include <botan/stream_cipher.h>
+ #include <botan/auto_rng.h>
+ #include <botan/hex.h>
+ #include <iostream>
+
+ int main()
+ {
+ std::string plaintext("This is a tasty burger!");
+ std::vector<uint8_t> pt(plaintext.data(),plaintext.data()+plaintext.length());
+ const std::vector<uint8_t> key = Botan::hex_decode("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F");
+ std::unique_ptr<Botan::StreamCipher> cipher(Botan::StreamCipher::create("ChaCha(20)"));
+
+ //generate fresh nonce (IV)
+ std::unique_ptr<Botan::RandomNumberGenerator> rng(new Botan::AutoSeeded_RNG);
+ std::vector<uint8_t> iv(8);
+ rng->randomize(iv.data(),iv.size());
+
+ //set key and IV
+ cipher->set_key(key);
+ cipher->set_iv(iv.data(),iv.size());
+ cipher->encipher(pt);
+
+ std::cout << cipher->name() << " with iv " << Botan::hex_encode(iv) << ": "
+ << Botan::hex_encode(pt) << "\n";
+ return 0;
+ }
+
+Available Stream Ciphers
+----------------------------
+
+Botan provides the following stream ciphers. If in doubt use ChaCha20 or CTR(AES-256).
+
+CTR-BE
+~~~~~~~
+
+A cipher mode that converts a block cipher into a stream cipher. It offers
+parallel execution and can seek within the output stream, both useful
+properties.
+
+CTR mode requires an IV which can be any length up to the block size of the
+underlying cipher. If it is shorter than the block size, sufficient zero bytes
+are appended.
+
+It is possible to choose the width of the counter portion, which can improve
+performance somewhat, but limits the maximum number of bytes that can safely be
+encrypted. Different protocols have different conventions for the width of the
+counter portion. This is done by specifying with width (which must be at least 4
+bytes, allowing to encrypt 2\ :sup:`32` blocks of data) for example
+"CTR(AES-256,8)" to select a 64-bit counter.
+
+(The ``-BE`` suffix refers to big-endian convention for the counter.
+This is the most common case.)
+
+OFB
+~~~~~
+
+Another stream cipher based on a block cipher. Unlike CTR mode, it does not
+allow parallel execution or seeking within the output stream. Prefer CTR.
+
+Available if ``BOTAN_HAS_OFB`` is defined.
+
+ChaCha
+~~~~~~~~
+
+A very fast cipher, now widely deployed in TLS as part of the ChaCha20Poly1305
+AEAD. Can be used with 8 (fast but dangerous), 12 (balance), or 20 rounds
+(conservative). Even with 20 rounds, ChaCha is very fast. Use 20 rounds.
+
+ChaCha supports an optional IV (which defaults to all zeros). It can be of
+length 64, 96 or (since 2.8) 192 bits. Using ChaCha with a 192 bit nonce is also
+known as XChaCha.
+
+Available if ``BOTAN_HAS_CHACHA`` is defined.
+
+Salsa20
+~~~~~~~~~
+
+An earlier iteration of the ChaCha design, this cipher is popular due to its use
+in the libsodium library. Prefer ChaCha.
+
+Salsa supports an optional IV (which defaults to all zeros). It can be of length
+64 or 192 bits. Using Salsa with a 192 bit nonce is also known as XSalsa.
+
+Available if ``BOTAN_HAS_SALSA20`` is defined.
+
+SHAKE-128
+~~~~~~~~~~~~
+
+This is the SHAKE-128 XOF exposed as a stream cipher. It is slower than ChaCha
+and somewhat obscure. It does not support IVs or seeking within the cipher
+stream.
+
+Available if ``BOTAN_HAS_SHAKE_CIPHER`` is defined.
+
+RC4
+~~~~
+
+An old and very widely deployed stream cipher notable for its simplicity. It
+does not support IVs or seeking within the cipher stream.
+
+.. warning::
+
+ RC4 is now badly broken. **Avoid in new code** and use only if required for
+ compatibility with existing systems.
+
+Available if ``BOTAN_HAS_RC4`` is defined.
diff --git a/comm/third_party/botan/doc/api_ref/tls.rst b/comm/third_party/botan/doc/api_ref/tls.rst
new file mode 100644
index 0000000000..fdffeda26a
--- /dev/null
+++ b/comm/third_party/botan/doc/api_ref/tls.rst
@@ -0,0 +1,1926 @@
+Transport Layer Security (TLS)
+========================================
+
+.. versionadded:: 1.11.0
+
+Botan has client and server implementations of various versions of the
+TLS protocol, including TLS v1.0, TLS v1.1, and TLS v1.2. As of
+version 1.11.13, support for the insecure SSLv3 protocol has been
+removed.
+
+There is also support for DTLS (v1.0 and v1.2), a variant of TLS
+adapted for operation on datagram transports such as UDP and
+SCTP. DTLS support should be considered as beta quality and further
+testing is invited.
+
+The TLS implementation does not know anything about sockets or the
+network layer. Instead, it calls a user provided callback (hereafter
+``output_fn``) whenever it has data that it would want to send to the
+other party (for instance, by writing it to a network socket), and
+whenever the application receives some data from the counterparty (for
+instance, by reading from a network socket) it passes that information
+to TLS using :cpp:func:`TLS::Channel::received_data`. If the data
+passed in results in some change in the state, such as a handshake
+completing, or some data or an alert being received from the other
+side, then the appropriate user provided callback will be invoked.
+
+If the reader is familiar with OpenSSL's BIO layer, it might be analogous
+to saying the only way of interacting with Botan's TLS is via a `BIO_mem` I/O
+abstraction. This makes the library completely agnostic to how you
+write your network layer, be it blocking sockets, libevent, asio, a
+message queue, lwIP on RTOS, some carrier pigeons, etc.
+
+Starting in 1.11.31, the application callbacks are encapsulated as the class
+``TLS::Callbacks`` with the following members. The first four (``tls_emit_data``,
+``tls_record_received``, ``tls_alert``, and ``tls_session_established``) are
+mandatory for using TLS, all others are optional and provide additional
+information about the connection.
+
+ .. cpp:function:: void tls_emit_data(const uint8_t data[], size_t data_len)
+
+ Mandatory. The TLS stack requests that all bytes of *data* be queued up to send to the
+ counterparty. After this function returns, the buffer containing *data* will
+ be overwritten, so a copy of the input must be made if the callback
+ cannot send the data immediately.
+
+ As an example you could ``send`` to perform a blocking write on a socket,
+ or append the data to a queue managed by your application, and initiate
+ an asynchronous write.
+
+ For TLS all writes must occur *in the order requested*.
+ For DTLS this ordering is not strictly required, but is still recommended.
+
+ .. cpp:function:: void tls_record_received(uint64_t rec_no, const uint8_t data[], size_t data_len)
+
+ Mandatory. Called once for each application_data record which is received, with the
+ matching (TLS level) record sequence number.
+
+ Currently empty records are ignored and do not instigate a callback,
+ but this may change in a future release.
+
+ As with ``tls_emit_data``, the array will be overwritten sometime after
+ the callback returns, so a copy should be made if needed.
+
+ For TLS the record number will always increase.
+
+ For DTLS, it is possible to receive records with the `rec_no` field out of
+ order, or with gaps, corresponding to reordered or lost datagrams.
+
+ .. cpp:function:: void tls_alert(Alert alert)
+
+ Mandatory. Called when an alert is received from the peer. Note that alerts
+ received before the handshake is complete are not authenticated and
+ could have been inserted by a MITM attacker.
+
+ .. cpp:function:: bool tls_session_established(const TLS::Session& session)
+
+ Mandatory. Called whenever a negotiation completes. This can happen more
+ than once on any connection, if renegotiation occurs. The *session* parameter
+ provides information about the session which was just established.
+
+ If this function returns false, the session will not be cached
+ for later resumption.
+
+ If this function wishes to cancel the handshake, it can throw an
+ exception which will send a close message to the counterparty and
+ reset the connection state.
+
+ .. cpp:function:: void tls_verify_cert_chain(const std::vector<X509_Certificate>& cert_chain, \
+ const std::vector<std::shared_ptr<const OCSP::Response>>& ocsp_responses, \
+ const std::vector<Certificate_Store*>& trusted_roots, \
+ Usage_Type usage, \
+ const std::string& hostname, \
+ const Policy& policy)
+
+ Optional - default implementation should work for many users.
+ It can be overridden for implementing extra validation routines
+ such as public key pinning.
+
+ Verifies the certificate chain in *cert_chain*, assuming the leaf
+ certificate is the first element. Throws an exception if any
+ error makes this certificate chain unacceptable.
+
+ If usage is `Usage_Type::TLS_SERVER_AUTH`, then *hostname* should
+ match the information in the server certificate. If usage is
+ `TLS_CLIENT_AUTH`, then *hostname* specifies the host the client
+ is authenticating against (from SNI); the callback can use this for
+ any special site specific auth logic.
+
+ The `ocsp_responses` is a possibly empty list of OCSP responses provided by
+ the server. In the current implementation of TLS OCSP stapling, only a
+ single OCSP response can be returned. A existing TLS extension allows the
+ server to send multiple OCSP responses, this extension may be supported in
+ the future in which case more than one OCSP response may be given during
+ this callback.
+
+ The `trusted_roots` parameter was returned by a call from the associated
+ `Credentials_Manager`.
+
+ The `policy` provided is the policy for the TLS session which is
+ being authenticated using this certificate chain. It can be consulted
+ for values such as allowable signature methods and key sizes.
+
+ .. cpp:function:: std::chrono::milliseconds tls_verify_cert_chain_ocsp_timeout() const
+
+ Called by default `tls_verify_cert_chain` to set timeout for online OCSP requests
+ on the certificate chain. Return 0 to disable OCSP. Current default is 0.
+
+ .. cpp:function:: std::string tls_server_choose_app_protocol(const std::vector<std::string>& client_protos)
+
+ Optional. Called by the server when a client includes a list of protocols in the ALPN extension.
+ The server then choose which protocol to use, or "" to disable sending any ALPN response.
+ The default implementation returns the empty string all of the time, effectively disabling
+ ALPN responses.
+
+ .. cpp:function:: void tls_session_activated()
+
+ Optional. By default does nothing. This is called when the session is
+ activated, that is once it is possible to send or receive data on the
+ channel. In particular it is possible for an implementation of this
+ function to perform an initial write on the channel.
+
+ .. cpp:function:: std::vector<uint8_t> tls_provide_cert_status(const std::vector<X509_Certificate>& chain, \
+ const Certificate_Status_Request& csr)
+
+ Optional. This can return a cached OCSP response. This is only
+ used on the server side, and only if the client requests OCSP
+ stapling.
+
+ .. cpp:function:: std::string tls_peer_network_identity()
+
+ Optional. Return a string that identifies the peer in some unique way
+ (for example, by formatting the remote IP and port into a string).
+ This is currently used to bind DTLS cookies to the network identity.
+
+ .. cpp:function:: void tls_inspect_handshake_msg(const Handshake_Message&)
+
+ This callback is optional, and can be used to inspect all handshake messages
+ while the session establishment occurs.
+
+ .. cpp:function:: void tls_modify_extensions(Extensions& extn, Connection_Side which_side)
+
+ This callback is optional, and can be used to modify extensions before they
+ are sent to the peer. For example this enables adding a custom extension,
+ or replacing or removing an extension set by the library.
+
+ .. cpp:function:: void tls_examine_extensions(const Extensions& extn, Connection_Side which_side)
+
+ This callback is optional, and can be used to examine extensions sent by
+ the peer.
+
+ .. cpp:function:: void tls_log_error(const char* msg)
+
+ Optional logging for an error message. (Not currently used)
+
+ .. cpp:function:: void tls_log_debug(const char* msg)
+
+ Optional logging for an debug message. (Not currently used)
+
+ .. cpp:function:: void tls_log_debug_bin(const char* descr, const uint8_t val[], size_t len)
+
+ Optional logging for an debug value. (Not currently used)
+
+ .. cpp:function:: std::string tls_decode_group_param(TLS::Group_Params group_param)
+
+ Optional. Called by the server when a client hello includes a list of supported groups in the
+ supported_groups extension and by the client when decoding the server key exchange including the selected curve identifier.
+ The function should return the name of the DH group or elliptic curve the passed
+ TLS group identifier should be mapped to. Therefore this callback enables the use of custom
+ elliptic curves or DH groups in TLS, if both client and server map the custom identifiers correctly.
+ Please note that it is required to allow the group TLS identifier in
+ in the used :cpp:class:`TLS::Policy`.
+
+Versions from 1.11.0 to 1.11.30 did not have ``TLS::Callbacks`` and instead
+used independent std::functions to pass the various callback functions.
+This interface is currently still included but is deprecated and will be removed
+in a future release. For the documentation for this interface, please check
+the docs for 1.11.30. This version of the manual only documents the new interface
+added in 1.11.31.
+
+TLS Channels
+----------------------------------------
+
+TLS servers and clients share an interface called `TLS::Channel`. A
+TLS channel (either client or server object) has these methods
+available:
+
+.. cpp:class:: TLS::Channel
+
+ .. cpp:function:: size_t received_data(const uint8_t buf[], size_t buf_size)
+ .. cpp:function:: size_t received_data(const std::vector<uint8_t>& buf)
+
+ This function is used to provide data sent by the counterparty
+ (eg data that you read off the socket layer). Depending on the
+ current protocol state and the amount of data provided this may
+ result in one or more callback functions that were provided to
+ the constructor being called.
+
+ The return value of ``received_data`` specifies how many more
+ bytes of input are needed to make any progress, unless the end of
+ the data fell exactly on a message boundary, in which case it
+ will return 0 instead.
+
+ .. cpp:function:: void send(const uint8_t buf[], size_t buf_size)
+ .. cpp:function:: void send(const std::string& str)
+ .. cpp:function:: void send(const std::vector<uint8_t>& vec)
+
+ Create one or more new TLS application records containing the
+ provided data and send them. This will eventually result in at
+ least one call to the ``output_fn`` callback before ``send``
+ returns.
+
+ If the current TLS connection state is unable to transmit new
+ application records (for example because a handshake has not
+ yet completed or the connection has already ended due to an
+ error) an exception will be thrown.
+
+ .. cpp:function:: void close()
+
+ A close notification is sent to the counterparty, and the
+ internal state is cleared.
+
+ .. cpp:function:: void send_alert(const Alert& alert)
+
+ Some other alert is sent to the counterparty. If the alert is
+ fatal, the internal state is cleared.
+
+ .. cpp:function:: bool is_active()
+
+ Returns true if and only if a handshake has been completed on
+ this connection and the connection has not been subsequently
+ closed.
+
+ .. cpp:function:: bool is_closed()
+
+ Returns true if and only if either a close notification or a
+ fatal alert message have been either sent or received.
+
+ .. cpp:function:: bool timeout_check()
+
+ This function does nothing unless the channel represents a DTLS
+ connection and a handshake is actively in progress. In this case
+ it will check the current timeout state and potentially initiate
+ retransmission of handshake packets. Returns true if a timeout
+ condition occurred.
+
+ .. cpp:function:: void renegotiate(bool force_full_renegotiation = false)
+
+ Initiates a renegotiation. The counterparty is allowed by the
+ protocol to ignore this request. If a successful renegotiation
+ occurs, the *handshake_cb* callback will be called again.
+
+ If *force_full_renegotiation* is false, then the client will
+ attempt to simply renew the current session - this will refresh
+ the symmetric keys but will not change the session master
+ secret. Otherwise it will initiate a completely new session.
+
+ For a server, if *force_full_renegotiation* is false, then a
+ session resumption will be allowed if the client attempts
+ it. Otherwise the server will prevent resumption and force the
+ creation of a new session.
+
+ .. cpp:function:: std::vector<X509_Certificate> peer_cert_chain()
+
+ Returns the certificate chain of the counterparty. When acting
+ as a client, this value will be non-empty unless the client's
+ policy allowed anonymous connections and the server then chose
+ an anonymous ciphersuite. Acting as a server, this value will
+ ordinarily be empty, unless the server requested a certificate
+ and the client responded with one.
+
+ .. cpp:function:: SymmetricKey key_material_export( \
+ const std::string& label, \
+ const std::string& context, \
+ size_t length)
+
+ Returns an exported key of *length* bytes derived from *label*,
+ *context*, and the session's master secret and client and server
+ random values. This key will be unique to this connection, and
+ as long as the session master secret remains secure an attacker
+ should not be able to guess the key.
+
+ Per :rfc:`5705`, *label* should begin with "EXPERIMENTAL" unless
+ the label has been standardized in an RFC.
+
+.. _tls_client:
+
+TLS Clients
+----------------------------------------
+
+.. cpp:class:: TLS::Client
+
+ .. cpp:function:: Client( \
+ Callbacks& callbacks, \
+ Session_Manager& session_manager, \
+ Credentials_Manager& creds, \
+ const Policy& policy, \
+ RandomNumberGenerator& rng, \
+ const Server_Information& server_info = Server_Information(), \
+ const Protocol_Version offer_version = Protocol_Version::latest_tls_version(), \
+ const std::vector<std::string>& next_protocols = std::vector<std::string>(), \
+ size_t reserved_io_buffer_size = 16*1024 \
+ )
+
+ Initialize a new TLS client. The constructor will immediately
+ initiate a new session.
+
+ The *callbacks* parameter specifies the various application callbacks
+ which pertain to this particular client connection.
+
+ The *session_manager* is an interface for storing TLS sessions,
+ which allows for session resumption upon reconnecting to a server.
+ In the absence of a need for persistent sessions, use
+ :cpp:class:`TLS::Session_Manager_In_Memory` which caches
+ connections for the lifetime of a single process. See
+ :ref:`tls_session_managers` for more about session managers.
+
+ The *credentials_manager* is an interface that will be called to
+ retrieve any certificates, secret keys, pre-shared keys, or SRP
+ information; see :doc:`credentials_manager` for more information.
+
+ Use the optional *server_info* to specify the DNS name of the
+ server you are attempting to connect to, if you know it. This helps
+ the server select what certificate to use and helps the client
+ validate the connection.
+
+ Note that the server name indicator name must be a FQDN. IP
+ addresses are not allowed by RFC 6066 and may lead to interoperability
+ problems.
+
+ Use the optional *offer_version* to control the version of TLS you
+ wish the client to offer. Normally, you'll want to offer the most
+ recent version of (D)TLS that is available, however some broken
+ servers are intolerant of certain versions being offered, and for
+ classes of applications that have to deal with such servers
+ (typically web browsers) it may be necessary to implement a version
+ backdown strategy if the initial attempt fails.
+
+ .. warning::
+
+ Implementing such a backdown strategy allows an attacker to
+ downgrade your connection to the weakest protocol that both you
+ and the server support.
+
+ Setting *offer_version* is also used to offer DTLS instead of TLS;
+ use :cpp:func:`TLS::Protocol_Version::latest_dtls_version`.
+
+ Optionally, the client will advertise *app_protocols* to the
+ server using the ALPN extension.
+
+ The optional *reserved_io_buffer_size* specifies how many bytes to
+ pre-allocate in the I/O buffers. Use this if you want to control
+ how much memory the channel uses initially (the buffers will be
+ resized as needed to process inputs). Otherwise some reasonable
+ default is used.
+
+Code Example
+^^^^^^^^^^^^
+A minimal example of a TLS client is provided below.
+The full code for a TLS client using BSD sockets is in `src/cli/tls_client.cpp`
+
+.. code-block:: cpp
+
+ #include <botan/tls_client.h>
+ #include <botan/tls_callbacks.h>
+ #include <botan/tls_session_manager.h>
+ #include <botan/tls_policy.h>
+ #include <botan/auto_rng.h>
+ #include <botan/certstor.h>
+
+ /**
+ * @brief Callbacks invoked by TLS::Channel.
+ *
+ * Botan::TLS::Callbacks is an abstract class.
+ * For improved readability, only the functions that are mandatory
+ * to implement are listed here. See src/lib/tls/tls_callbacks.h.
+ */
+ class Callbacks : public Botan::TLS::Callbacks
+ {
+ public:
+ void tls_emit_data(const uint8_t data[], size_t size) override
+ {
+ // send data to tls server, e.g., using BSD sockets or boost asio
+ }
+
+ void tls_record_received(uint64_t seq_no, const uint8_t data[], size_t size) 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
+ {
+ // handle a tls alert received from the tls server
+ }
+
+ bool tls_session_established(const Botan::TLS::Session& session) override
+ {
+ // the session with the tls server was established
+ // return false to prevent the session from being cached, true to
+ // cache the session in the configured session manager
+ return false;
+ }
+ };
+
+ /**
+ * @brief Credentials storage for the tls client.
+ *
+ * It returns a list of trusted CA certificates from a local directory.
+ * TLS client authentication is disabled. See src/lib/tls/credentials_manager.h.
+ */
+ class Client_Credentials : public Botan::Credentials_Manager
+ {
+ public:
+ Client_Credentials()
+ {
+ // Here we base trust on the system managed trusted CA list
+ m_stores.push_back(new Botan::System_Certificate_Store);
+ }
+
+ std::vector<Botan::Certificate_Store*> trusted_certificate_authorities(
+ const std::string& type,
+ 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_stores;
+ }
+
+ std::vector<Botan::X509_Certificate> cert_chain(
+ const std::vector<std::string>& cert_key_types,
+ const std::string& type,
+ 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::vector<Botan::X509_Certificate>();
+ }
+
+ Botan::Private_Key* private_key_for(const Botan::X509_Certificate& cert,
+ const std::string& type,
+ const std::string& context) override
+ {
+ // when returning a chain in cert_chain(), return the private key
+ // associated with the leaf certificate here
+ return nullptr;
+ }
+
+ private:
+ std::vector<Botan::Certificate_Store*> m_stores;
+ };
+
+ int main()
+ {
+ // prepare all the parameters
+ Callbacks callbacks;
+ Botan::AutoSeeded_RNG rng;
+ Botan::TLS::Session_Manager_In_Memory session_mgr(rng);
+ Client_Credentials creds;
+ Botan::TLS::Strict_Policy policy;
+
+ // 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);
+
+ while(!client.is_closed())
+ {
+ // read data received from the tls server, e.g., using BSD sockets or boost asio
+ // ...
+
+ // send data to the tls server using client.send_data()
+ }
+ }
+
+.. _tls_server:
+
+TLS Servers
+----------------------------------------
+
+.. cpp:class:: TLS::Server
+
+ .. cpp:function:: Server( \
+ Callbacks& callbacks, \
+ Session_Manager& session_manager, \
+ Credentials_Manager& creds, \
+ const Policy& policy, \
+ RandomNumberGenerator& rng, \
+ bool is_datagram = false, \
+ size_t reserved_io_buffer_size = 16*1024 \
+ )
+
+The first 5 arguments as well as the final argument
+*reserved_io_buffer_size*, are treated similarly to the :ref:`client
+<tls_client>`.
+
+If a client sends the ALPN extension, the ``callbacks`` function
+``tls_server_choose_app_protocol`` will be called and the result
+sent back to the client. If the empty string is returned, the server
+will not send an ALPN response. The function can also throw an exception
+to abort the handshake entirely, the ALPN specification says that if this
+occurs the alert should be of type `NO_APPLICATION_PROTOCOL`.
+
+The optional argument *is_datagram* specifies if this is a TLS or DTLS
+server; unlike clients, which know what type of protocol (TLS vs DTLS)
+they are negotiating from the start via the *offer_version*, servers
+would not until they actually received a client hello.
+
+Code Example
+^^^^^^^^^^^^
+A minimal example of a TLS server is provided below.
+The full code for a TLS server using asio is in `src/cli/tls_proxy.cpp`.
+
+.. code-block:: cpp
+
+ #include <botan/tls_client.h>
+ #include <botan/tls_callbacks.h>
+ #include <botan/tls_session_manager.h>
+ #include <botan/tls_policy.h>
+ #include <botan/auto_rng.h>
+ #include <botan/certstor.h>
+ #include <botan/pk_keys.h>
+
+ #include <memory>
+
+ /**
+ * @brief Callbacks invoked by TLS::Channel.
+ *
+ * Botan::TLS::Callbacks is an abstract class.
+ * For improved readability, only the functions that are mandatory
+ * to implement are listed here. See src/lib/tls/tls_callbacks.h.
+ */
+ class Callbacks : public Botan::TLS::Callbacks
+ {
+ public:
+ void tls_emit_data(const uint8_t data[], size_t size) override
+ {
+ // send data to tls client, e.g., using BSD sockets or boost asio
+ }
+
+ void tls_record_received(uint64_t seq_no, const uint8_t data[], size_t size) override
+ {
+ // process full TLS record received by tls client, e.g.,
+ // by passing it to the application
+ }
+
+ void tls_alert(Botan::TLS::Alert alert) override
+ {
+ // handle a tls alert received from the tls server
+ }
+
+ bool tls_session_established(const Botan::TLS::Session& session) override
+ {
+ // the session with the tls client was established
+ // return false to prevent the session from being cached, true to
+ // cache the session in the configured session manager
+ return false;
+ }
+ };
+
+ /**
+ * @brief Credentials storage for the tls server.
+ *
+ * It returns a certificate and the associated private key to
+ * authenticate the tls server to the client.
+ * TLS client authentication is not requested.
+ * See src/lib/tls/credentials_manager.h.
+ */
+ class Server_Credentials : public Botan::Credentials_Manager
+ {
+ public:
+ Server_Credentials() : m_key(Botan::PKCS8::load_key("botan.randombit.net.key"))
+ {
+ }
+
+ std::vector<Botan::Certificate_Store*> trusted_certificate_authorities(
+ const std::string& type,
+ 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
+ return std::vector<Certificate_Store*>();
+ }
+
+ std::vector<Botan::X509_Certificate> cert_chain(
+ const std::vector<std::string>& cert_key_types,
+ const std::string& type,
+ 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") };
+ }
+
+ Botan::Private_Key* private_key_for(const Botan::X509_Certificate& cert,
+ const std::string& type,
+ 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;
+ }
+
+ private:
+ std::unique_ptr<Botan::Private_Key> m_key;
+ };
+
+ int main()
+ {
+ // prepare all the parameters
+ Callbacks callbacks;
+ Botan::AutoSeeded_RNG rng;
+ Botan::TLS::Session_Manager_In_Memory session_mgr(rng);
+ Server_Credentials creds;
+ Botan::TLS::Strict_Policy policy;
+
+ // accept tls connection from client
+ 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().
+ // ...
+
+ // send data to the tls client using server.send_data()
+ // ...
+ }
+
+.. _tls_sessions:
+
+TLS Sessions
+----------------------------------------
+
+TLS allows clients and servers to support *session resumption*, where
+the end point retains some information about an established session
+and then reuse that information to bootstrap a new session in way that
+is much cheaper computationally than a full handshake.
+
+Every time your handshake callback is called, a new session has been
+established, and a ``TLS::Session`` is included that provides
+information about that session:
+
+.. note::
+
+ The serialization format of Session is not considered stable and is allowed
+ to change even across minor releases. In the event of such a change, old
+ sessions will no longer be able to be resumed.
+
+.. cpp:class:: TLS::Session
+
+ .. cpp:function:: Protocol_Version version() const
+
+ Returns the :cpp:class:`protocol version <TLS::Protocol_Version>`
+ that was negotiated
+
+ .. cpp:function:: Ciphersuite ciphersite() const
+
+ Returns the :cpp:class:`ciphersuite <TLS::Ciphersuite>` that
+ was negotiated.
+
+ .. cpp:function:: Server_Information server_info() const
+
+ Returns information that identifies the server side of the
+ connection. This is useful for the client in that it
+ identifies what was originally passed to the constructor. For
+ the server, it includes the name the client specified in the
+ server name indicator extension.
+
+ .. cpp:function:: std::vector<X509_Certificate> peer_certs() const
+
+ Returns the certificate chain of the peer
+
+ .. cpp:function:: std::string srp_identifier() const
+
+ If an SRP ciphersuite was used, then this is the identifier
+ that was used for authentication.
+
+ .. cpp:function:: bool secure_renegotiation() const
+
+ Returns ``true`` if the connection was negotiated with the
+ correct extensions to prevent the renegotiation attack.
+
+ .. cpp:function:: std::vector<uint8_t> encrypt(const SymmetricKey& key, \
+ RandomNumberGenerator& rng)
+
+ Encrypts a session using a symmetric key *key* and returns a raw
+ binary value that can later be passed to ``decrypt``. The key
+ may be of any length. The format is described in
+ :ref:`tls_session_encryption`.
+
+ .. cpp:function:: static Session decrypt(const uint8_t ciphertext[], \
+ size_t length, \
+ const SymmetricKey& key)
+
+ Decrypts a session that was encrypted previously with ``encrypt`` and
+ ``key``, or throws an exception if decryption fails.
+
+ .. cpp:function:: secure_vector<uint8_t> DER_encode() const
+
+ Returns a serialized version of the session.
+
+ .. warning:: The return value of ``DER_encode`` contains the
+ master secret for the session, and an attacker who
+ recovers it could recover plaintext of previous
+ sessions or impersonate one side to the other.
+
+.. _tls_session_managers:
+
+TLS Session Managers
+----------------------------------------
+
+You may want sessions stored in a specific format or storage type. To
+do so, implement the ``TLS::Session_Manager`` interface and pass your
+implementation to the ``TLS::Client`` or ``TLS::Server`` constructor.
+
+.. cpp:class:: TLS::Session_Mananger
+
+ .. cpp:function:: void save(const Session& session)
+
+ Save a new *session*. It is possible that this sessions session
+ ID will replicate a session ID already stored, in which case the
+ new session information should overwrite the previous information.
+
+ .. cpp:function:: void remove_entry(const std::vector<uint8_t>& session_id)
+
+ Remove the session identified by *session_id*. Future attempts
+ at resumption should fail for this session.
+
+ .. cpp:function:: bool load_from_session_id(const std::vector<uint8_t>& session_id, \
+ Session& session)
+
+ Attempt to resume a session identified by *session_id*. If
+ located, *session* is set to the session data previously passed
+ to *save*, and ``true`` is returned. Otherwise *session* is not
+ modified and ``false`` is returned.
+
+ .. cpp:function:: bool load_from_server_info(const Server_Information& server, \
+ Session& session)
+
+ Attempt to resume a session with a known server.
+
+ .. cpp:function:: std::chrono::seconds session_lifetime() const
+
+ Returns the expected maximum lifetime of a session when using
+ this session manager. Will return 0 if the lifetime is unknown
+ or has no explicit expiration policy.
+
+.. _tls_session_manager_inmem:
+
+In Memory Session Manager
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The ``TLS::Session_Manager_In_Memory`` implementation saves sessions
+in memory, with an upper bound on the maximum number of sessions and
+the lifetime of a session.
+
+It is safe to share a single object across many threads as it uses a
+lock internally.
+
+.. cpp:class:: TLS::Session_Managers_In_Memory
+
+ .. cpp:function:: Session_Manager_In_Memory(RandomNumberGenerator& rng, \
+ size_t max_sessions = 1000, \
+ std::chrono::seconds session_lifetime = 7200)
+
+ Limits the maximum number of saved sessions to *max_sessions*, and
+ expires all sessions older than *session_lifetime*.
+
+Noop Session Mananger
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The ``TLS::Session_Manager_Noop`` implementation does not save
+sessions at all, and thus session resumption always fails. Its
+constructor has no arguments.
+
+SQLite3 Session Manager
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+This session manager is only available if support for SQLite3 was
+enabled at build time. If the macro
+``BOTAN_HAS_TLS_SQLITE3_SESSION_MANAGER`` is defined, then
+``botan/tls_session_manager_sqlite.h`` contains
+``TLS::Session_Manager_SQLite`` which stores sessions persistently to
+a sqlite3 database. The session data is encrypted using a passphrase,
+and stored in two tables, named ``tls_sessions`` (which holds the
+actual session information) and ``tls_sessions_metadata`` (which holds
+the PBKDF information).
+
+.. warning:: The hostnames associated with the saved sessions are
+ stored in the database in plaintext. This may be a
+ serious privacy risk in some applications.
+
+.. cpp:class:: TLS::Session_Manager_SQLite
+
+ .. cpp:function:: Session_Manager_SQLite( \
+ const std::string& passphrase, \
+ RandomNumberGenerator& rng, \
+ const std::string& db_filename, \
+ size_t max_sessions = 1000, \
+ std::chrono::seconds session_lifetime = 7200)
+
+ Uses the sqlite3 database named by *db_filename*.
+
+TLS Policies
+----------------------------------------
+
+``TLS::Policy`` is how an application can control details of what will
+be negotiated during a handshake. The base class acts as the default
+policy. There is also a ``Strict_Policy`` (which forces only secure
+options, reducing compatibility) and ``Text_Policy`` which reads
+policy settings from a file.
+
+.. cpp:class:: TLS::Policy
+
+ .. cpp:function:: std::vector<std::string> allowed_ciphers() const
+
+ Returns the list of ciphers we are willing to negotiate, in order
+ of preference.
+
+ Clients send a list of ciphersuites in order of preference,
+ servers are free to choose any of them. Some servers will use the
+ clients preferences, others choose from the clients list
+ prioritizing based on its preferences.
+
+ 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.
+
+ Cipher names without an explicit mode refers to CBC+HMAC ciphersuites.
+
+ Default value: "ChaCha20Poly1305", "AES-256/GCM", "AES-128/GCM"
+
+ 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",
+ "Camellia-256", "Camellia-128"
+
+ Also allowed (though currently experimental): "AES-128/OCB(12)",
+ "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.
+
+ Also allowed (although **not recommended**): "SEED", "3DES"
+
+ .. note::
+
+ Before 1.11.30 only the non-standard ChaCha20Poly1305 ciphersuite
+ was implemented. The RFC 7905 ciphersuites are supported in 1.11.30
+ onwards.
+
+ .. note::
+
+ Support for the broken RC4 cipher was removed in 1.11.17
+
+ .. note::
+
+ SEED and 3DES are deprecated and will be removed in a future release.
+
+ .. cpp:function:: std::vector<std::string> allowed_macs() const
+
+ Returns the list of algorithms we are willing to use for
+ message authentication, in order of preference.
+
+ Default: "AEAD", "SHA-256", "SHA-384", "SHA-1"
+
+ A plain hash function indicates HMAC
+
+ .. note::
+
+ SHA-256 is preferred over SHA-384 in CBC mode because the
+ protections against the Lucky13 attack are somewhat more
+ effective for SHA-256 than SHA-384.
+
+ .. cpp:function:: std::vector<std::string> allowed_key_exchange_methods() const
+
+ Returns the list of key exchange methods we are willing to use,
+ in order of preference.
+
+ Default: "CECPQ1", "ECDH", "DH"
+
+ .. note::
+
+ CECPQ1 key exchange provides post-quantum security to the key exchange
+ by combining NewHope with a standard x25519 ECDH exchange. This prevents
+ an attacker, even one with a quantum computer, from later decrypting the
+ contents of a recorded TLS transcript. The NewHope algorithm is very
+ fast, but adds roughly 4 KiB of additional data transfer to every TLS
+ handshake. And even if NewHope ends up completely broken, the 'extra'
+ x25519 exchange secures the handshake.
+
+ For applications where the additional data transfer size is unacceptable,
+ simply allow only ECDH key exchange in the application policy. DH
+ exchange also often involves transferring several additional Kb (without
+ the benefit of post quantum security) so if CECPQ1 is being disabled for
+ traffic overhead reasons, DH should also be avoided.
+
+ Also allowed: "RSA", "SRP_SHA", "ECDHE_PSK", "DHE_PSK", "PSK"
+
+ .. note::
+
+ Static RSA ciphersuites are disabled by default since 1.11.34.
+ In addition to not providing forward security, any server which is
+ willing to negotiate these ciphersuites exposes themselves to a variety
+ of chosen ciphertext oracle attacks which are all easily avoided by
+ signing (as in PFS) instead of decrypting.
+
+ .. note::
+
+ In order to enable RSA, SRP, or PSK ciphersuites one must also enable
+ authentication method "IMPLICIT", see :cpp:func:`allowed_signature_methods`.
+
+ .. cpp:function:: std::vector<std::string> allowed_signature_hashes() const
+
+ Returns the list of hash algorithms we are willing to use for
+ public key signatures, in order of preference.
+
+ Default: "SHA-512", "SHA-384", "SHA-256"
+
+ Also allowed (although **not recommended**): "SHA-1"
+
+ .. note::
+
+ This is only used with TLS v1.2. In earlier versions of the
+ protocol, signatures are fixed to using only SHA-1 (for
+ DSA/ECDSA) or a MD5/SHA-1 pair (for RSA).
+
+ .. cpp:function:: std::vector<std::string> allowed_signature_methods() const
+
+ Default: "ECDSA", "RSA"
+
+ Also allowed (disabled by default): "DSA", "IMPLICIT", "ANONYMOUS"
+
+ "IMPLICIT" enables ciphersuites which are authenticated not by a signature
+ but through a side-effect of the key exchange. In particular this setting
+ is required to enable PSK, SRP, and static RSA ciphersuites.
+
+ "ANONYMOUS" allows purely anonymous DH/ECDH key exchanges. **Enabling this
+ is not recommended**
+
+ .. note::
+
+ Both DSA authentication and anonymous DH ciphersuites are deprecated,
+ and will be removed in a future release.
+
+ .. cpp:function:: std::vector<Group_Params> 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.
+
+ .. cpp:function:: bool use_ecc_point_compression() const
+
+ Prefer ECC point compression.
+
+ Signals that we prefer ECC points to be compressed when transmitted to us.
+ The other party may not support ECC point compression and therefore may still
+ send points uncompressed.
+
+ Note that the certificate used during authentication must also follow the other
+ party's preference.
+
+ Default: false
+
+ .. note::
+
+ Support for EC point compression is deprecated and will be removed in a
+ future major release.
+
+ .. cpp:function:: bool acceptable_protocol_version(Protocol_Version version)
+
+ Return true if this version of the protocol is one that we are
+ willing to negotiate.
+
+ Default: Accepts TLS v1.2 and DTLS v1.2, and rejects all older versions.
+
+ .. cpp:function:: bool server_uses_own_ciphersuite_preferences() const
+
+ If this returns true, a server will pick the cipher it prefers the
+ most out of the client's list. Otherwise, it will negotiate the
+ first cipher in the client's ciphersuite list that it supports.
+
+ Default: true
+
+ .. cpp:function:: bool allow_client_initiated_renegotiation() const
+
+ If this function returns true, a server will accept a
+ client-initiated renegotiation attempt. Otherwise it will send
+ the client a non-fatal ``no_renegotiation`` alert.
+
+ Default: false
+
+ .. cpp:function:: bool allow_server_initiated_renegotiation() const
+
+ If this function returns true, a client will accept a
+ server-initiated renegotiation attempt. Otherwise it will send
+ the server a non-fatal ``no_renegotiation`` alert.
+
+ Default: false
+
+ .. cpp:function:: bool abort_connection_on_undesired_renegotiation() const
+
+ If a renegotiation attempt is being rejected due to the configuration of
+ :cpp:func:`TLS::Policy::allow_client_initiated_renegotiation` or
+ :cpp:func:`TLS::Policy::allow_server_initiated_renegotiation`, and
+ this function returns true then the connection is closed with a fatal
+ alert instead of the default warning alert.
+
+ Default: false
+
+ .. cpp:function:: bool allow_insecure_renegotiation() const
+
+ If this function returns true, we will allow renegotiation attempts
+ even if the counterparty does not support the RFC 5746 extensions.
+
+ .. warning:: Returning true here could expose you to attacks
+
+ Default: false
+
+ .. cpp:function:: size_t minimum_signature_strength() const
+
+ Return the minimum strength (as ``n``, representing ``2**n`` work)
+ we will accept for a signature algorithm on any certificate.
+
+ Use 80 to enable RSA-1024 (*not recommended*), or 128 to require
+ either ECC or large (~3000 bit) RSA keys.
+
+ Default: 110 (allowing 2048 bit RSA)
+
+ .. cpp:function:: bool require_cert_revocation_info() const
+
+ If this function returns true, and a ciphersuite using certificates was
+ negotiated, then we must have access to a valid CRL or OCSP response in
+ order to trust the certificate.
+
+ .. warning:: Returning false here could expose you to attacks
+
+ Default: true
+
+ .. cpp:function:: Group_Params default_dh_group() const
+
+ For ephemeral Diffie-Hellman key exchange, the server sends a
+ group parameter. Return the 2 Byte TLS group identifier specifying the group parameter a
+ server should use.
+
+ Default: 2048 bit IETF IPsec group ("modp/ietf/2048")
+
+ .. cpp:function:: size_t minimum_dh_group_size() const
+
+ Return the minimum size in bits for a Diffie-Hellman group that a
+ client will accept. Due to the design of the protocol the client
+ has only two options - accept the group, or reject it with a
+ fatal alert then attempt to reconnect after disabling ephemeral
+ Diffie-Hellman.
+
+ Default: 2048 bits
+
+ .. cpp:function:: bool allow_tls10() const
+
+ Return true from here to allow TLS v1.0. Since 2.8.0, returns
+ ``false`` by default.
+
+ .. cpp:function:: bool allow_tls11() const
+
+ Return true from here to allow TLS v1.1. Since 2.8.0, returns
+ ``false`` by default.
+
+ .. cpp:function:: bool allow_tls12() const
+
+ Return true from here to allow TLS v1.2. Returns ``true`` by default.
+
+ .. cpp:function:: size_t minimum_rsa_bits() const
+
+ Minimum accepted RSA key size. Default 2048 bits.
+
+ .. cpp:function:: size_t minimum_dsa_group_size() const
+
+ Minimum accepted DSA key size. Default 2048 bits.
+
+ .. cpp:function:: size_t minimum_ecdsa_group_size() const
+
+ Minimum size for ECDSA keys (256 bits).
+
+ .. cpp:function:: size_t minimum_ecdh_group_size() const
+
+ Minimum size for ECDH keys (255 bits).
+
+ .. cpp:function:: void check_peer_key_acceptable(const Public_Key& public_key) const
+
+ Allows the policy to examine peer public keys. Throw an exception
+ if the key should be rejected. Default implementation checks
+ against policy values `minimum_dh_group_size`, `minimum_rsa_bits`,
+ `minimum_ecdsa_group_size`, and `minimum_ecdh_group_size`.
+
+ .. cpp:function:: bool hide_unknown_users() const
+
+ The SRP and PSK suites work using an identifier along with a
+ shared secret. If this function returns true, when an identifier
+ that the server does not recognize is provided by a client, a
+ random shared secret will be generated in such a way that a
+ client should not be able to tell the difference between the
+ identifier not being known and the secret being wrong. This can
+ help protect against some username probing attacks. If it
+ returns false, the server will instead send an
+ ``unknown_psk_identity`` alert when an unknown identifier is
+ used.
+
+ Default: false
+
+ .. cpp:function:: u32bit session_ticket_lifetime() const
+
+ Return the lifetime of session tickets. Each session includes the
+ start time. Sessions resumptions using tickets older than
+ ``session_ticket_lifetime`` seconds will fail, forcing a full
+ renegotiation.
+
+ Default: 86400 seconds (1 day)
+
+TLS Ciphersuites
+----------------------------------------
+
+.. cpp:class:: TLS::Ciphersuite
+
+ .. cpp:function:: uint16_t ciphersuite_code() const
+
+ Return the numerical code for this ciphersuite
+
+ .. cpp:function:: std::string to_string() const
+
+ Return the full name of ciphersuite (for example
+ "RSA_WITH_RC4_128_SHA" or "ECDHE_RSA_WITH_AES_128_GCM_SHA256")
+
+ .. cpp:function:: std::string kex_algo() const
+
+ Return the key exchange algorithm of this ciphersuite
+
+ .. cpp:function:: std::string sig_algo() const
+
+ Return the signature algorithm of this ciphersuite
+
+ .. cpp:function:: std::string cipher_algo() const
+
+ Return the cipher algorithm of this ciphersuite
+
+ .. cpp:function:: std::string mac_algo() const
+
+ Return the authentication algorithm of this ciphersuite
+
+ .. cpp:function:: bool acceptable_ciphersuite(const Ciphersuite& suite) const
+
+ Return true if ciphersuite is accepted by the policy.
+
+ Allows an application to reject any ciphersuites, which are
+ undesirable for whatever reason without having to reimplement
+ :cpp:func:`TLS::Ciphersuite::ciphersuite_list`
+
+ .. cpp:function:: std::vector<uint16_t> ciphersuite_list(Protocol_Version version, bool have_srp) const
+
+ Return allowed ciphersuites in order of preference
+
+ Allows an application to have full control over ciphersuites
+ by returning desired ciphersuites in preference order.
+
+.. _tls_alerts:
+
+TLS Alerts
+----------------------------------------
+
+A ``TLS::Alert`` is passed to every invocation of a channel's *alert_cb*.
+
+.. cpp:class:: TLS::Alert
+
+ .. cpp:function:: is_valid() const
+
+ Return true if this alert is not a null alert
+
+ .. cpp:function:: is_fatal() const
+
+ Return true if this alert is fatal. A fatal alert causes the
+ connection to be immediately disconnected. Otherwise, the alert
+ is a warning and the connection remains valid.
+
+ .. cpp:function:: Type type() const
+
+ Returns the type of the alert as an enum
+
+ .. cpp:function:: std::string type_string()
+
+ Returns the type of the alert as a string
+
+TLS Protocol Version
+----------------------------------------
+
+TLS has several different versions with slightly different behaviors.
+The ``TLS::Protocol_Version`` class represents a specific version:
+
+.. cpp:class:: TLS::Protocol_Version
+
+ .. cpp:enum:: Version_Code
+
+ ``TLS_V10``, ``TLS_V11``, ``TLS_V12``, ``DTLS_V10``, ``DTLS_V12``
+
+ .. cpp:function:: Protocol_Version(Version_Code named_version)
+
+ Create a specific version
+
+ .. cpp:function:: uint8_t major_version() const
+
+ Returns major number of the protocol version
+
+ .. cpp:function:: uint8_t minor_version() const
+
+ Returns minor number of the protocol version
+
+ .. cpp:function:: std::string to_string() const
+
+ Returns string description of the version, for instance "TLS
+ v1.1" or "DTLS v1.0".
+
+ .. cpp:function:: static Protocol_Version latest_tls_version()
+
+ Returns the latest version of the TLS protocol known to the library
+ (currently TLS v1.2)
+
+ .. cpp:function:: static Protocol_Version latest_dtls_version()
+
+ Returns the latest version of the DTLS protocol known to the
+ library (currently DTLS v1.2)
+
+TLS Custom Curves
+----------------------------------------
+
+The supported_groups TLS extension is used in the client hello to advertise a list of supported elliptic curves
+and DH groups. The server subsequently selects one of the groups, which is supported by both endpoints.
+The groups are represented by their TLS identifier. This 2 Byte identifier is standardized for commonly used groups and curves.
+In addition, the standard reserves the identifiers 0xFE00 to 0xFEFF for custom groups or curves.
+
+Using non standardized custom curves is however not recommended and can be a serious risk if an
+insecure curve is used. Still, it might be desired in some scenarios to use custom curves or groups in the TLS handshake.
+
+To use custom curves with the Botan :cpp:class:`TLS::Client` or :cpp:class:`TLS::Server` the following additional adjustments have to be implemented
+as shown in the following code examples.
+
+1. Registration of the custom curve
+2. Implementation TLS callback ``tls_decode_group_param``
+3. Adjustment of the TLS policy by allowing the custom curve
+
+Client Code Example
+^^^^^^^^^^^^^^^^^^^^
+
+.. code-block:: cpp
+
+ #include <botan/tls_client.h>
+ #include <botan/tls_callbacks.h>
+ #include <botan/tls_session_manager.h>
+ #include <botan/tls_policy.h>
+ #include <botan/auto_rng.h>
+ #include <botan/certstor.h>
+
+ #include <botan/ec_group.h>
+ #include <botan/oids.h>
+
+
+ /**
+ * @brief Callbacks invoked by TLS::Channel.
+ *
+ * Botan::TLS::Callbacks is an abstract class.
+ * For improved readability, only the functions that are mandatory
+ * to implement are listed here. See src/lib/tls/tls_callbacks.h.
+ */
+ class Callbacks : public Botan::TLS::Callbacks
+ {
+ public:
+ void tls_emit_data(const uint8_t data[], size_t size) override
+ {
+ // send data to tls server, e.g., using BSD sockets or boost asio
+ }
+
+ void tls_record_received(uint64_t seq_no, const uint8_t data[], size_t size) 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
+ {
+ // handle a tls alert received from the tls server
+ }
+
+ bool tls_session_established(const Botan::TLS::Session& session) override
+ {
+ // the session with the tls server was established
+ // return false to prevent the session from being cached, true to
+ // cache the session in the configured session manager
+ return false;
+ }
+ std::string tls_decode_group_param(Botan::TLS::Group_Params group_param) override
+ {
+ // handle TLS group identifier decoding and return name as string
+ // return empty string to indicate decoding failure
+
+ switch(static_cast<uint16_t>(group_param))
+ {
+ case 0xFE00:
+ return "testcurve1102";
+ default:
+ //decode non-custom groups
+ return Botan::TLS::Callbacks::tls_decode_group_param(group_param);
+ }
+ }
+ };
+
+ /**
+ * @brief Credentials storage for the tls client.
+ *
+ * It returns a list of trusted CA certificates from a local directory.
+ * TLS client authentication is disabled. See src/lib/tls/credentials_manager.h.
+ */
+ class Client_Credentials : public Botan::Credentials_Manager
+ {
+ public:
+ std::vector<Botan::Certificate_Store*> trusted_certificate_authorities(
+ const std::string& type,
+ 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 { new Botan::Certificate_Store_In_Memory("cas") };
+ }
+
+ std::vector<Botan::X509_Certificate> cert_chain(
+ const std::vector<std::string>& cert_key_types,
+ const std::string& type,
+ 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::vector<Botan::X509_Certificate>();
+ }
+
+ Botan::Private_Key* private_key_for(const Botan::X509_Certificate& cert,
+ const std::string& type,
+ const std::string& context) override
+ {
+ // when returning a chain in cert_chain(), return the private key
+ // associated with the leaf certificate here
+ return nullptr;
+ }
+ };
+
+ class Client_Policy : public Botan::TLS::Strict_Policy
+ {
+ public:
+ std::vector<Botan::TLS::Group_Params> key_exchange_groups() const override
+ {
+ // modified strict policy to allow our custom curves
+ return
+ {
+ static_cast<Botan::TLS::Group_Params>(0xFE00)
+ };
+ }
+ };
+
+ int main()
+ {
+ // prepare rng
+ Botan::AutoSeeded_RNG rng;
+
+ // prepare custom curve
+
+ // prepare curve parameters
+ const Botan::BigInt p("0x92309a3e88b94312f36891a2055725bb35ab51af96b3a651d39321b7bbb8c51575a76768c9b6b323");
+ const Botan::BigInt a("0x4f30b8e311f6b2dce62078d70b35dacb96aa84b758ab5a8dff0c9f7a2a1ff466c19988aa0acdde69");
+ const Botan::BigInt b("0x9045A513CFFF9AE1F1CC84039D852D240344A1D5C9DB203C844089F855C387823EB6FCDDF49C909C");
+
+ const Botan::BigInt x("0x9120f3779a31296cefcb5a5a08831f1a6d438ad5a3f2ce60585ac19c74eebdc65cadb96bb92622c7");
+ const Botan::BigInt y("0x836db8251c152dfee071b72c6b06c5387d82f1b5c30c5a5b65ee9429aa2687e8426d5d61276a4ede");
+ const Botan::BigInt order("0x248c268fa22e50c4bcda24688155c96ecd6ad46be5c82d7a6be6e7068cb5d1ca72b2e07e8b90d853");
+
+ const Botan::BigInt cofactor(4);
+
+ const Botan::OID oid("1.2.3.1");
+
+ // create EC_Group object to register the curve
+ Botan::EC_Group testcurve1102(p, a, b, x, y, order, cofactor, oid);
+
+ if(!testcurve1102.verify_group(rng))
+ {
+ // Warning: if verify_group returns false the curve parameters are insecure
+ }
+
+ // register name to specified oid
+ Botan::OIDS::add_oid(oid, "testcurve1102");
+
+ // prepare all the parameters
+ Callbacks callbacks;
+ Botan::TLS::Session_Manager_In_Memory session_mgr(rng);
+ Client_Credentials creds;
+ Client_Policy policy;
+
+ // 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);
+
+
+ while(!client.is_closed())
+ {
+ // read data received from the tls server, e.g., using BSD sockets or boost asio
+ // ...
+
+ // send data to the tls server using client.send_data()
+
+ }
+ }
+
+Server Code Example
+^^^^^^^^^^^^^^^^^^^^^
+
+.. code-block:: cpp
+
+ #include <botan/tls_server.h>
+ #include <botan/tls_callbacks.h>
+ #include <botan/tls_session_manager.h>
+ #include <botan/tls_policy.h>
+ #include <botan/auto_rng.h>
+ #include <botan/certstor.h>
+ #include <botan/pk_keys.h>
+ #include <botan/pkcs8.h>
+
+ #include <botan/ec_group.h>
+ #include <botan/oids.h>
+
+ #include <memory>
+
+ /**
+ * @brief Callbacks invoked by TLS::Channel.
+ *
+ * Botan::TLS::Callbacks is an abstract class.
+ * For improved readability, only the functions that are mandatory
+ * to implement are listed here. See src/lib/tls/tls_callbacks.h.
+ */
+ class Callbacks : public Botan::TLS::Callbacks
+ {
+ public:
+ void tls_emit_data(const uint8_t data[], size_t size) override
+ {
+ // send data to tls client, e.g., using BSD sockets or boost asio
+ }
+
+ void tls_record_received(uint64_t seq_no, const uint8_t data[], size_t size) override
+ {
+ // process full TLS record received by tls client, e.g.,
+ // by passing it to the application
+ }
+
+ void tls_alert(Botan::TLS::Alert alert) override
+ {
+ // handle a tls alert received from the tls server
+ }
+
+ bool tls_session_established(const Botan::TLS::Session& session) override
+ {
+ // the session with the tls client was established
+ // return false to prevent the session from being cached, true to
+ // cache the session in the configured session manager
+ return false;
+ }
+
+ std::string tls_decode_group_param(Botan::TLS::Group_Params group_param) override
+ {
+ // handle TLS group identifier decoding and return name as string
+ // return empty string to indicate decoding failure
+
+ switch(static_cast<uint16_t>(group_param))
+ {
+ case 0xFE00:
+ return "testcurve1102";
+ default:
+ //decode non-custom groups
+ return Botan::TLS::Callbacks::tls_decode_group_param(group_param);
+ }
+ }
+ };
+
+ /**
+ * @brief Credentials storage for the tls server.
+ *
+ * It returns a certificate and the associated private key to
+ * authenticate the tls server to the client.
+ * TLS client authentication is not requested.
+ * See src/lib/tls/credentials_manager.h.
+ */
+ class Server_Credentials : public Botan::Credentials_Manager
+ {
+ public:
+ Server_Credentials() : m_key(Botan::PKCS8::load_key("botan.randombit.net.key")
+ {
+ }
+
+ std::vector<Botan::Certificate_Store*> trusted_certificate_authorities(
+ const std::string& type,
+ 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
+ return std::vector<Botan::Certificate_Store*>();
+ }
+
+ std::vector<Botan::X509_Certificate> cert_chain(
+ const std::vector<std::string>& cert_key_types,
+ const std::string& type,
+ 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") };
+ }
+
+ Botan::Private_Key* private_key_for(const Botan::X509_Certificate& cert,
+ const std::string& type,
+ 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.get();
+ }
+
+ private:
+ std::unique_ptr<Botan::Private_Key> m_key;
+ };
+
+ class Server_Policy : public Botan::TLS::Strict_Policy
+ {
+ public:
+ std::vector<Botan::TLS::Group_Params> key_exchange_groups() const override
+ {
+ // modified strict policy to allow our custom curves
+ return
+ {
+ static_cast<Botan::TLS::Group_Params>(0xFE00)
+ };
+ }
+ };
+
+ int main()
+ {
+
+ // prepare rng
+ Botan::AutoSeeded_RNG rng;
+
+ // prepare custom curve
+
+ // prepare curve parameters
+ const Botan::BigInt p("0x92309a3e88b94312f36891a2055725bb35ab51af96b3a651d39321b7bbb8c51575a76768c9b6b323");
+ const Botan::BigInt a("0x4f30b8e311f6b2dce62078d70b35dacb96aa84b758ab5a8dff0c9f7a2a1ff466c19988aa0acdde69");
+ const Botan::BigInt b("0x9045A513CFFF9AE1F1CC84039D852D240344A1D5C9DB203C844089F855C387823EB6FCDDF49C909C");
+
+ const Botan::BigInt x("0x9120f3779a31296cefcb5a5a08831f1a6d438ad5a3f2ce60585ac19c74eebdc65cadb96bb92622c7");
+ const Botan::BigInt y("0x836db8251c152dfee071b72c6b06c5387d82f1b5c30c5a5b65ee9429aa2687e8426d5d61276a4ede");
+ const Botan::BigInt order("0x248c268fa22e50c4bcda24688155c96ecd6ad46be5c82d7a6be6e7068cb5d1ca72b2e07e8b90d853");
+
+ const Botan::BigInt cofactor(4);
+
+ const Botan::OID oid("1.2.3.1");
+
+ // create EC_Group object to register the curve
+ Botan::EC_Group testcurve1102(p, a, b, x, y, order, cofactor, oid);
+
+ if(!testcurve1102.verify_group(rng))
+ {
+ // Warning: if verify_group returns false the curve parameters are insecure
+ }
+
+ // register name to specified oid
+ Botan::OIDS::add_oid(oid, "testcurve1102");
+
+ // prepare all the parameters
+ Callbacks callbacks;
+ Botan::TLS::Session_Manager_In_Memory session_mgr(rng);
+ Server_Credentials creds;
+ Server_Policy policy;
+
+ // accept tls connection from client
+ 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().
+ // ...
+
+ // send data to the tls client using server.send_data()
+ // ...
+ }
+
+TLS Stream
+----------------------------------------
+
+:cpp:class:`TLS::Stream` offers a Boost.Asio compatible wrapper around :cpp:class:`TLS::Client` and :cpp:class:`TLS::Server`.
+It can be used as an alternative to Boost.Asio's `ssl::stream <https://www.boost.org/doc/libs/1_66_0/doc/html/boost_asio/reference/ssl__stream.html>`_ with minor adjustments to the using code.
+It offers the following interface:
+
+.. cpp:class:: template <class StreamLayer, class ChannelT> TLS::Stream
+
+ *StreamLayer* specifies the type of the stream's *next layer*, for example a `Boost.Asio TCP socket <https://www.boost.org/doc/libs/1_66_0/doc/html/boost_asio/reference/ip__tcp/socket.html>`_.
+ *ChannelT* is the type of the stream's *native handle*; it defaults to :cpp:class:`TLS::Channel` and should not be specified manually.
+
+ .. cpp:function:: template <typename... Args> \
+ explicit Stream(Context& context, Args&& ... args)
+
+ Construct a new TLS stream.
+ The *context* parameter will be used to initialize the underlying *native handle*, i.e. the :ref:`TLS::Client <tls_client>` or :ref:`TLS::Server <tls_server>`, when :cpp:func:`handshake` is called.
+ Using code must ensure the context is kept alive for the lifetime of the stream.
+ The further *args* will be forwarded to the *next layer*'s constructor.
+
+ .. cpp:function:: template <typename... Args> \
+ explicit Stream(Arg&& arg, Context& context)
+
+ Convenience constructor for :cpp:class:`boost::asio::ssl::stream` compatibility.
+ The parameters have the same meaning as for the first constructor, but their order is changed and only one argument can be passed to the *next layer* constructor.
+
+
+ .. cpp:function:: void handshake(Connection_Side side, boost::system::error_code& ec)
+
+ Set up the *native handle* and perform the TLS handshake.
+
+ .. cpp:function:: void handshake(Connection_Side side)
+
+ Overload of :cpp:func:`handshake` that throws an exception if an error occurs.
+
+ .. cpp:function:: template <typename HandshakeHandler> \
+ DEDUCED async_handshake(Connection_Side side, HandshakeHandler&& handler)
+
+ Asynchronous variant of :cpp:func:`handshake`.
+ The function returns immediately and calls the *handler* callback function after performing asynchronous I/O to complete the TLS handshake.
+ The return type is an automatically deduced specialization of :cpp:class:`boost::asio::async_result`, depending on the *HandshakeHandler* type.
+
+
+ .. cpp:function:: void shutdown(boost::system::error_code& ec)
+
+ Calls :cpp:func:`TLS::Channel::close` on the native handle and writes the TLS alert to the *next layer*.
+
+ .. cpp:function:: void shutdown()
+
+ Overload of :cpp:func:`shutdown` that throws an exception if an error occurs.
+
+ .. cpp:function:: template <typename ShutdownHandler> \
+ void async_shutdown(ShutdownHandler&& handler)
+
+ Asynchronous variant of :cpp:func:`shutdown`.
+ The function returns immediately and calls the *handler* callback function after performing asynchronous I/O to complete the TLS shutdown.
+
+
+ .. cpp:function:: template <typename MutableBufferSequence> \
+ std::size_t read_some(const MutableBufferSequence& buffers, boost::system::error_code& ec)
+
+ Reads encrypted data from the *next layer*, decrypts it, and writes it into the provided *buffers*.
+ If an error occurs, *error_code* is set.
+ Returns the number of bytes read.
+
+ .. cpp:function:: template <typename MutableBufferSequence> \
+ std::size_t read_some(const MutableBufferSequence& buffers)
+
+ Overload of :cpp:func:`read_some` that throws an exception if an error occurs.
+
+ .. cpp:function:: template <typename MutableBufferSequence, typename ReadHandler> \
+ DEDUCED async_read_some(const MutableBufferSequence& buffers, ReadHandler&& handler)
+
+ Asynchronous variant of :cpp:func:`read_some`.
+ The function returns immediately and calls the *handler* callback function after writing the decrypted data into the provided *buffers*.
+ The return type is an automatically deduced specialization of :cpp:class:`boost::asio::async_result`, depending on the *ReadHandler* type.
+ *ReadHandler* should suffice the `requirements to a Boost.Asio read handler <https://www.boost.org/doc/libs/1_66_0/doc/html/boost_asio/reference/ReadHandler.html>`_.
+
+
+ .. cpp:function:: template <typename ConstBufferSequence> \
+ std::size_t write_some(const ConstBufferSequence& buffers, boost::system::error_code& ec)
+
+ Encrypts data from the provided *buffers* and writes it to the *next layer*.
+ If an error occurs, *error_code* is set.
+ Returns the number of bytes written.
+
+ .. cpp:function:: template <typename ConstBufferSequence> \
+ std::size_t write_some(const ConstBufferSequence& buffers)
+
+ Overload of :cpp:func:`write_some` that throws an exception rather than setting an error code.
+
+ .. cpp:function:: template <typename ConstBufferSequence, typename WriteHandler> \
+ DEDUCED async_write_some(const ConstBufferSequence& buffers, WriteHandler&& handler)
+
+ Asynchronous variant of :cpp:func:`write_some`.
+ The function returns immediately and calls the *handler* callback function after writing the encrypted data to the *next layer*.
+ The return type is an automatically deduced specialization of :cpp:class:`boost::asio::async_result`, depending on the *WriteHandler* type.
+ *WriteHandler* should suffice the `requirements to a Boost.Asio write handler <https://www.boost.org/doc/libs/1_66_0/doc/html/boost_asio/reference/WriteHandler.html>`_.
+
+.. cpp:class:: TLS::Context
+
+ A helper class to initialize and configure the Stream's underlying *native handle* (see :cpp:class:`TLS::Client` and :cpp:class:`TLS::Server`).
+
+ .. cpp:function:: Context(Credentials_Manager& credentialsManager, \
+ RandomNumberGenerator& randomNumberGenerator, \
+ Session_Manager& sessionManager, \
+ Policy& policy, \
+ Server_Information serverInfo = Server_Information())
+
+ Constructor for TLS::Context.
+
+ .. cpp:function:: void set_verify_callback(Verify_Callback_T callback)
+
+ Set a user-defined callback function for certificate chain verification. This
+ will cause the stream to override the default implementation of the
+ :cpp:func:`tls_verify_cert_chain` callback.
+
+TLS Stream Client Code Example
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The code below illustrates how to build a simple HTTPS client based on the TLS Stream and Boost.Beast. When run, it fetches the content of `https://botan.randombit.net/news.html` and prints it to stdout.
+
+.. code-block:: cpp
+
+ #include <iostream>
+
+ #include <botan/asio_stream.h>
+ #include <botan/auto_rng.h>
+ #include <botan/certstor_system.h>
+
+ #include <boost/asio.hpp>
+ #include <boost/beast.hpp>
+ #include <boost/bind.hpp>
+
+ namespace http = boost::beast::http;
+ namespace _ = boost::asio::placeholders;
+
+ // very basic credentials manager
+ class Credentials_Manager : public Botan::Credentials_Manager
+ {
+ public:
+ Credentials_Manager() {}
+
+ std::vector<Botan::Certificate_Store*>
+ trusted_certificate_authorities(const std::string&, const std::string&) override
+ {
+ return {&cert_store_};
+ }
+
+ private:
+ Botan::System_Certificate_Store cert_store_;
+ };
+
+ // a simple https client based on TLS::Stream
+ class client
+ {
+ public:
+ client(boost::asio::io_context& io_context,
+ boost::asio::ip::tcp::resolver::iterator endpoint_iterator,
+ http::request<http::string_body> req)
+ : request_(req)
+ , ctx_(credentials_mgr_,
+ rng_,
+ session_mgr_,
+ policy_,
+ Botan::TLS::Server_Information())
+ , stream_(io_context, ctx_)
+ {
+ boost::asio::async_connect(stream_.lowest_layer(), endpoint_iterator,
+ boost::bind(&client::handle_connect, this, _::error));
+ }
+
+ void handle_connect(const boost::system::error_code& error)
+ {
+ if(error)
+ {
+ std::cout << "Connect failed: " << error.message() << "\n";
+ return;
+ }
+ stream_.async_handshake(Botan::TLS::Connection_Side::CLIENT,
+ boost::bind(&client::handle_handshake, this, _::error));
+ }
+
+ void handle_handshake(const boost::system::error_code& error)
+ {
+ if(error)
+ {
+ std::cout << "Handshake failed: " << error.message() << "\n";
+ return;
+ }
+ http::async_write(stream_, request_,
+ boost::bind(&client::handle_write, this, _::error, _::bytes_transferred));
+ }
+
+ void handle_write(const boost::system::error_code& error, size_t)
+ {
+ if(error)
+ {
+ std::cout << "Write failed: " << error.message() << "\n";
+ return;
+ }
+ http::async_read(stream_, reply_, response_,
+ boost::bind(&client::handle_read, this, _::error, _::bytes_transferred));
+ }
+
+ void handle_read(const boost::system::error_code& error, size_t)
+ {
+ if(!error)
+ {
+ std::cout << "Reply: ";
+ std::cout << response_.body() << "\n";
+ }
+ else
+ {
+ std::cout << "Read failed: " << error.message() << "\n";
+ }
+ }
+
+ private:
+ http::request<http::dynamic_body> request_;
+ http::response<http::string_body> response_;
+ boost::beast::flat_buffer reply_;
+
+ Botan::TLS::Session_Manager_Noop session_mgr_;
+ Botan::AutoSeeded_RNG rng_;
+ Credentials_Manager credentials_mgr_;
+ Botan::TLS::Policy policy_;
+
+ Botan::TLS::Context ctx_;
+ Botan::TLS::Stream<boost::asio::ip::tcp::socket> stream_;
+ };
+
+ int main()
+ {
+ boost::asio::io_context io_context;
+
+ boost::asio::ip::tcp::resolver resolver(io_context);
+ boost::asio::ip::tcp::resolver::query query("botan.randombit.net", "443");
+ boost::asio::ip::tcp::resolver::iterator iterator = resolver.resolve(query);
+
+ http::request<http::string_body> req;
+ req.version(11);
+ req.method(http::verb::get);
+ req.target("/news.html");
+ req.set(http::field::host, "botan.randombit.net");
+
+ client c(io_context, iterator, req);
+
+ io_context.run();
+ }
+
+.. _tls_session_encryption:
+
+TLS Session Encryption
+-------------------------
+
+A unified format is used for encrypting TLS sessions either for durable storage
+(on client or server) or when creating TLS session tickets. This format is *not
+stable* even across the same major version.
+
+The current session encryption scheme was introduced in 2.13.0, replacing the
+format previously used since 1.11.13.
+
+Session encryption accepts a key of any length, though for best security a key
+of 256 bits should be used. This master key is used to key an instance of HMAC
+using the SHA-512/256 hash.
+
+First a "key name" or identifier is created, by HMAC'ing the fixed string "BOTAN
+TLS SESSION KEY NAME" and truncating to 4 bytes. This is the initial prefix of
+the encrypted session, and will remain fixed as long as the same ticket key is
+used. This allows quickly rejecting sessions which are encrypted using an
+unknown or incorrect key.
+
+Then a key used for AES-256 in GCM mode is created by first choosing a 128 bit
+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
+reuse).
+
+A random 96-bit nonce is created and included in the header.
+
+AES in GCM is used to encrypt and authenticate the serialized session. The
+key name, key seed, and AEAD nonce are all included as additional data.
diff --git a/comm/third_party/botan/doc/api_ref/tpm.rst b/comm/third_party/botan/doc/api_ref/tpm.rst
new file mode 100644
index 0000000000..7598c4bd81
--- /dev/null
+++ b/comm/third_party/botan/doc/api_ref/tpm.rst
@@ -0,0 +1,113 @@
+Trusted Platform Module (TPM)
+==========================================
+
+.. versionadded:: 1.11.26
+
+Some computers come with a TPM, which is a small side processor which can
+perform certain operations which include RSA key generation and signing, a
+random number generator, accessing a small amount of NVRAM, and a set of PCRs
+which can be used to measure software state (this is TPMs most famous use, for
+authenticating a boot sequence).
+
+The TPM NVRAM and PCR APIs are not supported by Botan at this time, patches welcome.
+
+Currently only v1.2 TPMs are supported, and the only TPM library supported is
+TrouSerS (http://trousers.sourceforge.net/). Hopefully both of these limitations
+will be removed in a future release, in order to support newer TPM v2.0 systems.
+The current code has been tested with an ST TPM running in a Lenovo laptop.
+
+Test for TPM support with the macro ``BOTAN_HAS_TPM``, include ``<botan/tpm.h>``.
+
+First, create a connection to the TPM with a ``TPM_Context``. The context is
+passed to all other TPM operations, and should remain alive as long as any other
+TPM object which the context was passed to is still alive, otherwise errors or
+even an application crash are possible. In the future, the API may change to
+using ``shared_ptr`` to remove this problem.
+
+.. cpp:class:: TPM_Context
+
+ .. cpp:function:: TPM_Context(pin_cb cb, const char* srk_password)
+
+ The (somewhat improperly named) pin_cb callback type takes a std::string as
+ an argument, which is an informative message for the user. It should return
+ a string containing the password entered by the user.
+
+ Normally the SRK password is null. Use nullptr to signal this.
+
+The TPM contains a RNG of unknown design or quality. If that doesn't scare you
+off, you can use it with ``TPM_RNG`` which implements the standard
+``RandomNumberGenerator`` interface.
+
+.. cpp:class:: TPM_RNG
+
+ .. cpp:function:: TPM_RNG(TPM_Context& ctx)
+
+ Initialize a TPM RNG object. After initialization, reading from
+ this RNG reads from the hardware? RNG on the TPM.
+
+The v1.2 TPM uses only RSA, but because this key is implemented completely in
+hardware it uses a different private key type, with a somewhat different API to
+match the TPM's behavior.
+
+.. cpp:class:: TPM_PrivateKey
+
+ .. cpp:function:: TPM_PrivateKey(TPM_Context& ctx, size_t bits, const char* key_password)
+
+ Create a new RSA key stored on the TPM. The bits should be either 1024
+ or 2048; the TPM interface hypothetically allows larger keys but in
+ practice no v1.2 TPM hardware supports them.
+
+ The TPM processor is not fast, be prepared for this to take a while.
+
+ The key_password is the password to the TPM key ?
+
+ .. cpp:function:: std::string register_key(TPM_Storage_Type storage_type)
+
+ Registers a key with the TPM. The storage_type can be either
+ `TPM_Storage_Type::User` or `TPM_Storage_Type::System`. If System, the
+ key is stored on the TPM itself. If User, it is stored on the local hard
+ drive in a database maintained by an intermediate piece of system
+ software (which actual interacts with the physical TPM on behalf of any
+ number of applications calling the TPM API).
+
+ The TPM has only some limited space to store private keys and may reject
+ requests to store the key.
+
+ In either case the key is encrypted with an RSA key which was generated
+ on the TPM and which it will not allow to be exported. Thus (so goes the
+ theory) without physically attacking the TPM
+
+ Returns a UUID which can be passed back to constructor below.
+
+ .. cpp:function:: TPM_PrivateKey(TPM_Context& ctx, const std::string& uuid, \
+ TPM_Storage_Type storage_type)
+
+ Load a registered key. The UUID was returned by the ``register_key`` function.
+
+ .. cpp:function:: std::vector<uint8_t> export_blob() const
+
+ Export the key as an encrypted blob. This blob can later be presented
+ back to the same TPM to load the key.
+
+ .. cpp:function:: TPM_PrivateKey(TPM_Context& ctx, const std::vector<uint8_t>& blob)
+
+ Load a TPM key previously exported as a blob with ``export_blob``.
+
+ .. cpp:function:: std::unique_ptr<Public_Key> public_key() const
+
+ Return the public key associated with this TPM private key.
+
+ TPM does not store public keys, nor does it support signature verification.
+
+ .. cpp:function:: TSS_HKEY handle() const
+
+ Returns the bare TSS key handle. Use if you need to call the raw TSS API.
+
+A ``TPM_PrivateKey`` can be passed to a ``PK_Signer`` constructor and used to
+sign messages just like any other key. Only PKCS #1 v1.5 signatures are supported
+by the v1.2 TPM.
+
+.. cpp:function:: std::vector<std::string> TPM_PrivateKey::registered_keys(TPM_Context& ctx)
+
+ This static function returns the list of all keys (in URL format)
+ registered with the system
diff --git a/comm/third_party/botan/doc/api_ref/tss.rst b/comm/third_party/botan/doc/api_ref/tss.rst
new file mode 100644
index 0000000000..947b835d0a
--- /dev/null
+++ b/comm/third_party/botan/doc/api_ref/tss.rst
@@ -0,0 +1,45 @@
+Threshold Secret Sharing
+========================================
+
+.. versionadded:: 1.9.1
+
+Threshold secret sharing allows splitting a secret into ``N`` shares such that
+``M`` (for specified ``M`` <= ``N``) is sufficient to recover the secret, but an
+attacker with ``M - 1`` shares cannot derive any information about the secret.
+
+The implementation in Botan follows an expired Internet draft
+"draft-mcgrew-tss-03". Several other implementations of this TSS format exist.
+
+.. cpp:class:: RTSS_Share
+
+ .. cpp:function:: static std::vector<RTSS_Share> split(uint8_t M, uint8_t N, \
+ const uint8_t secret[], uint16_t secret_len, \
+ const std::vector<uint8_t>& identifier, \
+ const std::string& hash_fn, \
+ RandomNumberGenerator& rng)
+
+ Split a secret. The identifier is an optional key identifier which may be
+ up to 16 bytes long. Shorter identifiers are padded with zeros.
+
+ The hash function must be either "SHA-1", "SHA-256", or "None" to disable
+ the checksum.
+
+ This will return a vector of length ``N``, any ``M`` of these shares is
+ sufficient to reconstruct the data.
+
+ .. cpp:function:: static secure_vector<uint8_t> reconstruct(const std::vector<RTSS_Share>& shares)
+
+ Given a sufficient number of shares, reconstruct a secret.
+
+ .. cpp:function:: RTSS_Share(const uint8_t data[], size_t len)
+
+ Read a TSS share as a sequence of bytes.
+
+ .. cpp:function:: const secure_vector<uint8>& data() const
+
+ Return the data of this share.
+
+ .. cpp:function:: uint8_t share_id() const
+
+ Return the share ID which will be in the range 1...255
+
diff --git a/comm/third_party/botan/doc/api_ref/versions.rst b/comm/third_party/botan/doc/api_ref/versions.rst
new file mode 100644
index 0000000000..511141b75f
--- /dev/null
+++ b/comm/third_party/botan/doc/api_ref/versions.rst
@@ -0,0 +1,100 @@
+
+Versioning
+========================================
+
+All versions are of the tuple (major,minor,patch).
+
+As of Botan 2.0.0, Botan uses semantic versioning. The minor number increases if
+any feature addition is made. The patch version is used to indicate a release
+where only bug fixes were applied. If an incompatible API change is required,
+the major version will be increased.
+
+The library has functions for checking compile-time and runtime versions.
+
+The build-time version information is defined in `botan/build.h`
+
+.. c:macro:: BOTAN_VERSION_MAJOR
+
+ The major version of the release.
+
+.. c:macro:: BOTAN_VERSION_MINOR
+
+ The minor version of the release.
+
+.. c:macro:: BOTAN_VERSION_PATCH
+
+ The patch version of the release.
+
+.. 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.
+
+.. c:macro:: BOTAN_DISTRIBUTION_INFO
+
+ .. versionadded:: 1.9.3
+
+ 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".
+
+.. 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.
+
+The runtime version information, and some helpers for compile time
+version checks, are included in `botan/version.h`
+
+.. cpp:function:: std::string version_string()
+
+ Returns a single-line string containing relevant information about
+ this build and version of the library in an unspecified format.
+
+.. cpp:function:: uint32_t version_major()
+
+ Returns the major part of the version.
+
+.. cpp:function:: uint32_t version_minor()
+
+ Returns the minor part of the version.
+
+.. cpp:function:: uint32_t version_patch()
+
+ Returns the patch part of the version.
+
+.. cpp:function:: uint32_t version_datestamp()
+
+ 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)
+
+ Call this function with the compile-time version being built against, eg::
+
+ Botan::runtime_version_check(BOTAN_VERSION_MAJOR, BOTAN_VERSION_MINOR, BOTAN_VERSION_PATCH)
+
+ 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.
+
+.. 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::
+
+ #if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(2,1,0)
+ // 2.1+ code path
+ #else
+ // code path for older versions
+ #endif
+
diff --git a/comm/third_party/botan/doc/api_ref/x509.rst b/comm/third_party/botan/doc/api_ref/x509.rst
new file mode 100644
index 0000000000..cbf3d531e1
--- /dev/null
+++ b/comm/third_party/botan/doc/api_ref/x509.rst
@@ -0,0 +1,914 @@
+.. _x509_certificates:
+
+X.509 Certificates and CRLs
+=================================
+
+A certificate is a binding between some identifying information
+(called a *subject*) and a public key. This binding is asserted by a
+signature on the certificate, which is placed there by some authority
+(the *issuer*) that at least claims that it knows the subject named in
+the certificate really "owns" the private key corresponding to the
+public key in the certificate.
+
+The major certificate format in use today is X.509v3, used for instance in the
+:doc:`tls` protocol. A X.509 certificate is represented by the class
+``X509_Certificate``. The data of an X.509 certificate is stored as a
+``shared_ptr`` to a structure containing the decoded information. So copying
+``X509_Certificate`` objects is quite cheap.
+
+
+.. cpp:class:: X509_Certificate
+
+ .. cpp:function:: X509_Certificate(const std::string& filename)
+
+ Load a certificate from a file. PEM or DER is accepted.
+
+ .. cpp:function:: X509_Certificate(const std::vector<uint8_t>& in)
+
+ Load a certificate from a byte string.
+
+ .. cpp:function:: X509_Certificate(DataSource& source)
+
+ Load a certificate from an abstract ``DataSource``.
+
+ .. cpp:function:: X509_DN subject_dn() const
+
+ Returns the distinguished name (DN) of the certificate's subject. This is
+ the primary place where information about the subject of the certificate is
+ stored. However "modern" information that doesn't fit in the X.500
+ framework, such as DNS name, email, IP address, or XMPP address, appears
+ instead in the subject alternative name.
+
+ .. cpp:function:: X509_DN issuer_dn() const
+
+ Returns the distinguished name (DN) of the certificate's issuer, ie the CA
+ that issued this certificate.
+
+ .. cpp:function:: const AlternativeName& subject_alt_name() const
+
+ Return the subjects alternative name. This is used to store
+ values like associated URIs, DNS addresses, and email addresses.
+
+ .. cpp:function:: const AlternativeName& issuer_alt_name() const
+
+ Return alternative names for the issuer.
+
+ .. cpp:function:: std::unique_ptr<Public_Key> load_subject_public_key() const
+
+ Deserialize the stored public key and return a new object. This
+ might throw, if it happens that the public key object stored in
+ the certificate is malformed in some way, or in the case that the
+ public key algorithm used is not supported by the library.
+
+ See :ref:`serializing_public_keys` for more information about what to do
+ with the returned object. It may be any type of key, in principle, though
+ RSA and ECDSA are most common.
+
+ .. cpp:function:: std::vector<uint8_t> subject_public_key_bits() const
+
+ Return the binary encoding of the subject public key. This value (or a hash of
+ it) is used in various protocols, eg for public key pinning.
+
+ .. cpp:function:: AlgorithmIdentifier subject_public_key_algo() const
+
+ Return an algorithm identifier that identifies the algorithm used in the
+ subject's public key.
+
+ .. cpp:function:: std::vector<uint8_t> serial_number() const
+
+ Return the certificates serial number. The tuple of issuer DN and
+ serial number should be unique.
+
+ .. cpp:function:: std::vector<uint8> raw_subject_dn() const
+
+ Return the binary encoding of the subject DN.
+
+ .. cpp:function:: std::vector<uint8> raw_issuer_dn() const
+
+ Return the binary encoding of the issuer DN.
+
+ .. cpp:function:: X509_Time not_before() const
+
+ Returns the point in time the certificate becomes valid
+
+ .. cpp:function:: X509_Time not_after() const
+
+ Returns the point in time the certificate expires
+
+ .. cpp:function:: const Extensions& v3_extensions() const
+
+ Returns all extensions of this certificate. You can use this
+ to examine any extension data associated with the certificate,
+ including custom extensions the library doesn't know about.
+
+ .. cpp:function:: std::vector<uint8_t> authority_key_id() const
+
+ Return the authority key id, if set. This is an arbitrary string; in the
+ issuing certificate this will be the subject key id.
+
+ .. cpp:function:: std::vector<uint8_t> subject_key_id() const
+
+ Return the subject key id, if set.
+
+ .. cpp:function:: bool allowed_extended_usage(const OID& usage) const
+
+ Return true if and only if the usage OID appears in the extended key usage
+ extension. Also will return true if the extended key usage extension is
+ not used in the current certificate.
+
+ .. cpp:function:: std::vector<OID> extended_key_usage() const
+
+ Return the list of extended key usages. May be empty.
+
+ .. cpp:function:: std::string fingerprint(const std::string& hash_fn = "SHA-1") const
+
+ 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.
+
+ .. cpp:function:: Key_Constraints constraints() const
+
+ Returns either an enumeration listing key constraints (what the
+ associated key can be used for) or ``NO_CONSTRAINTS`` if the
+ relevant extension was not included. Example values are
+ ``DIGITAL_SIGNATURE`` and ``KEY_CERT_SIGN``. More than one value
+ might be specified.
+
+ .. cpp:function:: bool matches_dns_name(const std::string& name) const
+
+ Check if the certificate's subject alternative name DNS fields
+ match ``name``. This function also handles wildcard certificates.
+
+ .. cpp:function:: std::string to_string() const
+
+ Returns a free-form human readable string describing the certificate.
+
+ .. cpp:function:: std::string PEM_encode() const
+
+ Returns the PEM encoding of the certificate
+
+ .. cpp:function:: std::vector<uint8_t> BER_encode() const
+
+ Returns the DER/BER encoding of the certificate
+
+X.509 Distinguished Names
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. cpp:class:: X509_DN
+
+ .. cpp:function:: bool has_field(const std::string& attr) const
+
+ Returns true if ``get_attribute`` or ``get_first_attribute`` will return a value.
+
+ .. cpp:function:: std::vector<std::string> get_attribute(const std::string& attr) const
+
+ Return all attributes associated with a certain attribute type.
+
+ .. cpp:function:: std::string get_first_attribute(const std::string& attr) const
+
+ Like ``get_attribute`` but returns just the first attribute, or
+ empty if the DN has no attribute of the specified type.
+
+ .. cpp:function:: std::multimap<OID, std::string> get_attributes() const
+
+ Get all attributes of the DN. The OID maps to a DN component such as
+ 2.5.4.10 ("Organization"), and the strings are UTF-8 encoded.
+
+ .. cpp:function:: std::multimap<std::string, std::string> contents() const
+
+ Similar to ``get_attributes``, but the OIDs are decoded to strings.
+
+ .. cpp:function:: void add_attribute(const std::string& key, const std::string& val)
+
+ Add an attribute to a DN.
+
+ .. cpp:function:: void add_attribute(const OID& oid, const std::string& val)
+
+ 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.
+
+X.509v3 Extensions
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+X.509v3 specifies a large number of possible extensions. Botan supports some,
+but by no means all of them. The following listing lists which X.509v3
+extensions are supported and notes areas where there may be problems with the
+handling.
+
+ - Key Usage and Extended Key Usage: No problems known.
+
+ - Basic Constraints: No problems known. A self-signed v1 certificate
+ is assumed to be a CA, while a v3 certificate is marked as a CA if
+ and only if the basic constraints extension is present and set for
+ a CA cert.
+
+ - Subject Alternative Names: Only the "rfc822Name", "dNSName", and
+ "uniformResourceIdentifier" and raw IPv4 fields will be stored; all
+ others are ignored.
+
+ - Issuer Alternative Names: Same restrictions as the Subject
+ Alternative Names extension. New certificates generated by Botan
+ never include the issuer alternative name.
+
+ - Authority Key Identifier: Only the version using KeyIdentifier is
+ supported. If the GeneralNames version is used and the extension is
+ critical, an exception is thrown. If both the KeyIdentifier and GeneralNames
+ versions are present, then the KeyIdentifier will be used, and the
+ GeneralNames ignored.
+
+ - Subject Key Identifier: No problems known.
+
+ - Name Constraints: No problems known (though encoding is not supported).
+
+Any unknown critical extension in a certificate will lead to an
+exception during path validation.
+
+Extensions are handled by a special class taking care of encoding
+and decoding. It also supports encoding and decoding of custom extensions.
+To do this, it internally keeps two lists of extensions. Different lookup
+functions are provided to search them.
+
+.. note::
+
+ Validation of custom extensions during path validation is currently not supported.
+
+.. cpp:class:: Extensions
+
+ .. cpp:function:: void add(Certificate_Extension* extn, bool critical = false)
+
+ Adds a new extension to the extensions object. If an extension of the same
+ type already exists, ``extn`` will replace it. If ``critical`` is true the
+ extension will be marked as critical in the encoding.
+
+ .. cpp:function:: bool add_new(Certificate_Extension* extn, bool critical = false)
+
+ Like ``add`` but an existing extension will not be replaced. Returns true if the
+ extension was used, false if an extension of the same type was already in place.
+
+ .. cpp:function:: void replace(Certificate_Extension* extn, bool critical = false)
+
+ Adds an extension to the list or replaces it, if the same
+ extension was already added
+
+ .. cpp:function:: std::unique_ptr<Certificate_Extension> get(const OID& oid) const
+
+ Searches for an extension by OID and returns the result
+
+ .. cpp:function:: template<typename T> \
+ std::unique_ptr<T> get_raw(const OID& oid)
+
+ Searches for an extension by OID and returns the result.
+ Only the unknown extensions, that is, extensions types that are not
+ listed above, are searched for by this function.
+
+ .. cpp:function:: std::vector<std::pair<std::unique_ptr<Certificate_Extension>, bool>> extensions() const
+
+ Returns the list of extensions together with the corresponding
+ criticality flag. Only contains the supported extension types
+ listed above.
+
+ .. cpp:function:: std::map<OID, std::pair<std::vector<uint8_t>, bool>> extensions_raw() const
+
+ Returns the list of extensions as raw, encoded bytes
+ together with the corresponding criticality flag.
+ Contains all extensions, known as well as unknown extensions.
+
+Certificate Revocation Lists
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+It will occasionally happen that a certificate must be revoked before
+its expiration date. Examples of this happening include the private
+key being compromised, or the user to which it has been assigned
+leaving an organization. Certificate revocation lists are an answer to
+this problem (though online certificate validation techniques are
+starting to become somewhat more popular). Every once in a while the
+CA will release a new CRL, listing all certificates that have been
+revoked. Also included is various pieces of information like what time
+a particular certificate was revoked, and for what reason. In most
+systems, it is wise to support some form of certificate revocation,
+and CRLs handle this easily.
+
+For most users, processing a CRL is quite easy. All you have to do is
+call the constructor, which will take a filename (or a
+``DataSource&``). The CRLs can either be in raw BER/DER, or in PEM
+format; the constructor will figure out which format without any extra
+information. For example::
+
+ X509_CRL crl1("crl1.der");
+
+ DataSource_Stream in("crl2.pem");
+ X509_CRL crl2(in);
+
+After that, pass the ``X509_CRL`` object to a ``Certificate_Store`` object
+with
+
+.. cpp:function:: void Certificate_Store::add_crl(const X509_CRL& crl)
+
+and all future verifications will take into account the provided CRL.
+
+Certificate Stores
+----------------------------------------
+
+An object of type ``Certificate_Store`` is a generalized interface to
+an external source for certificates (and CRLs). Examples of such a
+store would be one that looked up the certificates in a SQL database,
+or by contacting a CGI script running on a HTTP server. There are
+currently three mechanisms for looking up a certificate, and one for
+retrieving CRLs. By default, most of these mechanisms will return an
+empty ``std::shared_ptr`` of ``X509_Certificate``. This storage mechanism
+is *only* queried when doing certificate validation: it allows you to
+distribute only the root key with an application, and let some online
+method handle getting all the other certificates that are needed to
+validate an end entity certificate. In particular, the search routines
+will not attempt to access the external database.
+
+The certificate lookup methods are ``find_cert`` (by Subject
+Distinguished Name and optional Subject Key Identifier) and
+``find_cert_by_pubkey_sha1`` (by SHA-1 hash of the certificate's
+public key). The Subject Distinguished Name is given as a ``X509_DN``,
+while the SKID parameter takes a ``std::vector<uint8_t>`` containing
+the subject key identifier in raw binary. Both lookup methods are
+mandatory to implement.
+
+Finally, there is a method for finding a CRL, called ``find_crl_for``,
+that takes an ``X509_Certificate`` object, and returns a
+``std::shared_ptr`` of ``X509_CRL``. The ``std::shared_ptr`` return
+type makes it easy to return no CRLs by returning ``nullptr``
+(eg, if the certificate store doesn't support retrieving CRLs).
+Implementing the function is optional, and by default will return
+``nullptr``.
+
+Certificate stores are used in the :doc:`tls` module to store a
+list of trusted certificate authorities.
+
+In Memory Certificate Store
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The in memory certificate store keeps all objects in memory only.
+Certificates can be loaded from disk initially, but also added
+later.
+
+.. cpp:class:: Certificate_Store_In_Memory
+
+ .. cpp:function:: Certificate_Store_In_Memory(const std::string& dir)
+
+ Attempt to parse all files in ``dir`` (including subdirectories)
+ as certificates. Ignores errors.
+
+ .. cpp:function:: Certificate_Store_In_Memory(const X509_Certificate& cert)
+
+ Adds given certificate to the store
+
+ .. cpp:function:: Certificate_Store_In_Memory()
+
+ Create an empty store
+
+ .. cpp:function:: void add_certificate(const X509_Certificate& cert)
+
+ Add a certificate to the store
+
+ .. cpp:function:: void add_certificate(std::shared_ptr<const X509_Certificate> cert)
+
+ Add a certificate already in a shared_ptr to the store
+
+ .. cpp:function:: void add_crl(const X509_CRL& crl)
+
+ Add a certificate revocation list (CRL) to the store.
+
+ .. cpp:function:: void add_crl(std::shared_ptr<const X509_CRL> crl)
+
+ Add a certificate revocation list (CRL) to the store as a shared_ptr
+
+SQL-backed Certificate Stores
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The SQL-backed certificate stores store all objects in an SQL database. They
+also additionally provide private key storage and revocation of individual
+certificates.
+
+.. cpp:class:: Certificate_Store_In_SQL
+
+ .. cpp:function:: Certificate_Store_In_SQL(const std::shared_ptr<SQL_Database> db, \
+ const std::string& passwd, RandomNumberGenerator& rng, const std::string& table_prefix = "")
+
+ Create or open an existing certificate store from an SQL database.
+ The password in ``passwd`` will be used to encrypt private keys.
+
+ .. cpp:function:: bool insert_cert(const X509_Certificate& cert)
+
+ Inserts ``cert`` into the store. Returns `false` if the certificate is
+ already known and `true` if insertion was successful.
+
+ .. cpp:function:: remove_cert(const X509_Certificate& cert)
+
+ Removes ``cert`` from the store. Returns `false` if the certificate could not
+ be found and `true` if removal was successful.
+
+ .. cpp:function:: std::shared_ptr<const Private_Key> find_key(const X509_Certificate&) const
+
+ Returns the private key for "cert" or an empty shared_ptr if none was found
+
+ .. cpp:function:: std::vector<std::shared_ptr<const X509_Certificate>> \
+ find_certs_for_key(const Private_Key& key) const
+
+ Returns all certificates for private key ``key``
+
+ .. cpp:function:: bool insert_key(const X509_Certificate& cert, const Private_Key& key)
+
+ Inserts ``key`` for ``cert`` into the store, returns `false` if the key is
+ already known and `true` if insertion was successful.
+
+ .. cpp:function:: void remove_key(const Private_Key& key)
+
+ Removes ``key`` from the store
+
+ .. cpp:function:: void revoke_cert(const X509_Certificate&, CRL_Code, \
+ const X509_Time& time = X509_Time())
+
+ Marks ``cert`` as revoked starting from ``time``
+
+ .. cpp:function:: void affirm_cert(const X509_Certificate&)
+
+ Reverses the revocation for ``cert``
+
+ .. cpp:function:: std::vector<X509_CRL> generate_crls() const
+
+ Generates CRLs for all certificates marked as revoked.
+ A CRL is returned for each unique issuer DN.
+
+The ``Certificate_Store_In_SQL`` class operates on an abstract ``SQL_Database``
+object. If support for sqlite3 was enabled at build time, Botan includes an
+implementation of this interface for sqlite3, and a subclass of
+``Certificate_Store_In_SQL`` which creates or opens a sqlite3 database.
+
+.. cpp:class:: Certificate_Store_In_SQLite
+
+ .. cpp:function:: Certificate_Store_In_SQLite(const std::string& db_path, \
+ const std::string& passwd, RandomNumberGenerator& rng, const std::string& table_prefix = "")
+
+ Create or open an existing certificate store from an sqlite database file.
+ The password in ``passwd`` will be used to encrypt private keys.
+
+Path Validation
+----------------------------------------
+
+The process of validating a certificate chain up to a trusted root is
+called `path validation`, and in botan that operation is handled by a
+set of functions in ``x509path.h`` named ``x509_path_validate``:
+
+.. cpp:function:: Path_Validation_Result \
+ x509_path_validate(const X509_Certificate& end_cert, \
+ const Path_Validation_Restrictions& restrictions, \
+ const Certificate_Store& store, const std::string& hostname = "", \
+ Usage_Type usage = Usage_Type::UNSPECIFIED, \
+ std::chrono::system_clock::time_point validation_time = std::chrono::system_clock::now(), \
+ std::chrono::milliseconds ocsp_timeout = std::chrono::milliseconds(0), \
+ const std::vector<std::shared_ptr<const OCSP::Response>>& ocsp_resp = std::vector<std::shared_ptr<const OCSP::Response>>())
+
+ The last five parameters are optional. ``hostname`` specifies a hostname which is
+ matched against the subject DN in ``end_cert`` according to RFC 6125.
+ An empty hostname disables hostname validation.
+ ``usage`` specifies key usage restrictions that are compared
+ to the key usage fields in `end_cert` according to RFC 5280, if not set to
+ ``UNSPECIFIED``. ``validation_time`` allows setting the time point at which all certificates
+ are validated. This is really only useful for testing. The default is the
+ current system clock's current time. ``ocsp_timeout`` sets the timeout for
+ OCSP requests. The default of 0 disables OCSP checks completely.
+ ``ocsp_resp`` allows adding additional OCSP responses retrieved from outside
+ of the path validation. Note that OCSP online checks are done only
+ as long as the http_util module was compiled in. Availability of online
+ OCSP checks can be checked using the macro BOTAN_HAS_ONLINE_REVOCATION_CHECKS.
+
+ For the different flavors of ``x509_path_validate``, check ``x509path.h``.
+
+The result of the validation is returned as a class:
+
+.. cpp:class:: Path_Validation_Result
+
+ Specifies the result of the validation
+
+ .. cpp:function:: bool successful_validation() const
+
+ Returns true if a certificate path from *end_cert* to a trusted
+ root was found and all path validation checks passed.
+
+ .. cpp:function:: std::string result_string() const
+
+ Returns a descriptive string of the validation status (for
+ instance "Verified", "Certificate is not yet valid", or
+ "Signature error"). This is the string value of
+ the `result` function below.
+
+ .. cpp:function:: const X509_Certificate& trust_root() const
+
+ If the validation was successful, returns the certificate which
+ is acting as the trust root for *end_cert*.
+
+ .. cpp:function:: const std::vector<X509_Certificate>& cert_path() const
+
+ Returns the full certificate path starting with the end entity
+ certificate and ending in the trust root.
+
+ .. cpp:function:: Certificate_Status_Code result() const
+
+ Returns the 'worst' error that occurred during validation. For
+ instance, we do not want an expired certificate with an invalid
+ signature to be reported to the user as being simply expired (a
+ relatively innocuous and common error) when the signature isn't
+ even valid.
+
+ .. cpp:function:: const std::vector<std::set<Certificate_Status_Code>>& all_statuses() const
+
+ For each certificate in the chain, returns a set of status which
+ indicate all errors which occurred during validation. This is
+ primarily useful for diagnostic purposes.
+
+ .. cpp:function:: std::set<std::string> trusted_hashes() const
+
+ Returns the set of all cryptographic hash functions which are
+ implicitly trusted for this validation to be correct.
+
+
+A ``Path_Validation_Restrictions`` is passed to the path
+validator and specifies restrictions and options for the validation
+step. The two constructors are:
+
+ .. cpp:function:: Path_Validation_Restrictions(bool require_rev, \
+ size_t minimum_key_strength, \
+ bool ocsp_all_intermediates, \
+ const std::set<std::string>& trusted_hashes)
+
+ If `require_rev` is true, then any path without revocation
+ information (CRL or OCSP check) is rejected with the code
+ `NO_REVOCATION_DATA`. The `minimum_key_strength` parameter
+ specifies the minimum strength of public key signature we will
+ accept is. The set of hash names `trusted_hashes` indicates which
+ hash functions we'll accept for cryptographic signatures. Any
+ untrusted hash will cause the error case `UNTRUSTED_HASH`.
+
+ .. cpp:function:: Path_Validation_Restrictions(bool require_rev = false, \
+ size_t minimum_key_strength = 80, \
+ bool ocsp_all_intermediates = false)
+
+ A variant of the above with some convenient defaults. The current
+ default `minimum_key_strength` of 80 roughly corresponds to 1024
+ bit RSA. The set of trusted hashes is set to all SHA-2 variants,
+ and, if `minimum_key_strength` is less than or equal to 80, then
+ SHA-1 signatures will also be accepted.
+
+Creating New Certificates
+---------------------------------
+
+A CA is represented by the type ``X509_CA``, which can be found in
+``x509_ca.h``. A CA always needs its own certificate, which can either
+be a self-signed certificate (see below on how to create one) or one
+issued by another CA (see the section on PKCS #10 requests). Creating
+a CA object is done by the following constructor:
+
+.. cpp:function:: X509_CA::X509_CA(const X509_Certificate& cert, \
+ const Private_Key& key, \
+ const std::string& hash_fn, \
+ RandomNumberGenerator& rng)
+
+The private ``key`` is the private key corresponding to the public key in the
+CA's certificate. ``hash_fn`` is the name of the hash function to use
+for signing, e.g., `SHA-256`. ``rng`` is queried for random during signing.
+
+There is an alternative constructor that lets you set additional options, namely
+the padding scheme that will be used by the X509_CA object to sign certificates
+and certificate revocation lists. If the padding is not set explicitly, the CA
+will use the padding scheme that was used when signing the CA certificate.
+
+.. cpp:function:: X509_CA::X509_CA(const X509_Certificate& cert, \
+ const Private_Key& key, \
+ const std::map<std::string,std::string>& opts, \
+ const std::string& hash_fn, \
+ RandomNumberGenerator& rng)
+
+The only option valid at this moment is "padding". The supported padding schemes
+can be found in src/lib/pubkey/padding.cpp. Some alternative names for the
+padding schemes are understood, as well.
+
+Requests for new certificates are supplied to a CA in the form of PKCS
+#10 certificate requests (called a ``PKCS10_Request`` object in
+Botan). These are decoded in a similar manner to
+certificates/CRLs/etc. A request is vetted by humans (who somehow
+verify that the name in the request corresponds to the name of the
+entity who requested it), and then signed by a CA key, generating a
+new certificate:
+
+.. cpp:function:: X509_Certificate \
+ X509_CA::sign_request(const PKCS10_Request& req, \
+ RandomNumberGenerator& rng, \
+ const X509_Time& not_before, \
+ const X509_Time& not_after)
+
+Generating CRLs
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+As mentioned previously, the ability to process CRLs is highly
+important in many PKI systems. In fact, according to strict X.509
+rules, you must not validate any certificate if the appropriate CRLs
+are not available (though hardly any systems are that strict). In any
+case, a CA should have a valid CRL available at all times.
+
+Of course, you might be wondering what to do if no certificates have
+been revoked. Never fear; empty CRLs, which revoke nothing at all, can
+be issued. To generate a new, empty CRL, just call
+
+.. cpp:function:: X509_CRL X509_CA::new_crl(RandomNumberGenerator& rng, \
+ uint32_t next_update = 0)
+
+ This function will return a new, empty CRL. The ``next_update`` parameter is
+ the number of seconds before the CRL expires. If it is set to the (default)
+ value of zero, then a reasonable default (currently 7 days) will be used.
+
+On the other hand, you may have issued a CRL before. In that case, you will
+want to issue a new CRL that contains all previously revoked
+certificates, along with any new ones. This is done by calling
+
+.. cpp:function:: X509_CRL X509_CA::update_crl(const X509_CRL& last_crl, \
+ std::vector<CRL_Entry> new_entries, RandomNumberGenerator& rng, \
+ size_t next_update = 0)
+
+ Where ``last_crl`` is the last CRL this CA issued, and
+ ``new_entries`` is a list of any newly revoked certificates. The
+ function returns a new ``X509_CRL`` to make available for
+ clients.
+
+The ``CRL_Entry`` type is a structure that contains, at a minimum, the serial
+number of the revoked certificate. As serial numbers are never repeated, the
+pairing of an issuer and a serial number (should) distinctly identify any
+certificate. In this case, we represent the serial number as a
+``secure_vector<uint8_t>`` called ``serial``. There are two additional (optional)
+values, an enumeration called ``CRL_Code`` that specifies the reason for
+revocation (``reason``), and an object that represents the time that the
+certificate became invalid (if this information is known).
+
+If you wish to remove an old entry from the CRL, insert a new entry for the
+same cert, with a ``reason`` code of ``REMOVE_FROM_CRL``. For example, if a
+revoked certificate has expired 'normally', there is no reason to continue to
+explicitly revoke it, since clients will reject the cert as expired in any
+case.
+
+Self-Signed Certificates
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Generating a new self-signed certificate can often be useful, for
+example when setting up a new root CA, or for use in specialized
+protocols. The library provides a utility function for this:
+
+.. cpp:function:: X509_Certificate create_self_signed_cert( \
+ const X509_Cert_Options& opts, const Private_Key& key, \
+ const std::string& hash_fn, RandomNumberGenerator& rng)
+
+ Where ``key`` is the private key you wish to use (the public key,
+ used in the certificate itself is extracted from the private key),
+ and ``opts`` is an structure that has various bits of information
+ that will be used in creating the certificate (this structure, and
+ its use, is discussed below).
+
+Creating PKCS #10 Requests
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Also in ``x509self.h``, there is a function for generating new PKCS #10
+certificate requests:
+
+.. cpp:function:: PKCS10_Request create_cert_req( \
+ const X509_Cert_Options& opts, const Private_Key& key, \
+ const std::string& hash_fn, RandomNumberGenerator& rng)
+
+This function acts quite similarly to
+:cpp:func:`create_self_signed_cert`, except it instead returns a PKCS
+#10 certificate request. After creating it, one would typically
+transmit it to a CA, who signs it and returns a freshly minted X.509
+certificate.
+
+.. cpp:function:: PKCS10_Request PKCS10_Request::create(const Private_Key& key, \
+ const X509_DN& subject_dn, \
+ const Extensions& extensions, \
+ const std::string& hash_fn, \
+ RandomNumberGenerator& rng, \
+ const std::string& padding_scheme = "", \
+ const std::string& challenge = "")
+
+ This function (added in 2.5) is similar to ``create_cert_req`` but allows
+ specifying all the parameters directly. In fact ``create_cert_req`` just
+ creates the DN and extensions from the options, then uses this call to
+ actually create the ``PKCS10_Request`` object.
+
+
+Certificate Options
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+What is this ``X509_Cert_Options`` thing we've been passing around?
+It's a class representing a bunch of information that will end up
+being stored into the certificate. This information comes in 3 major
+flavors: information about the subject (CA or end-user), the validity
+period of the certificate, and restrictions on the usage of the
+certificate. For special cases, you can also add custom X.509v3
+extensions.
+
+First and foremost is a number of ``std::string`` members, which
+contains various bits of information about the user: ``common_name``,
+``serial_number``, ``country``, ``organization``, ``org_unit``,
+``locality``, ``state``, ``email``, ``dns_name``, and ``uri``. As many
+of these as possible should be filled it (especially an email
+address), though the only required ones are ``common_name`` and
+``country``.
+
+Additionally there are a small selection of ``std::vector<std::string>``
+members, which allow space for repeating elements:
+``more_org_units`` and ``more_dns``.
+
+There is another value that is only useful when creating a PKCS #10
+request, which is called ``challenge``. This is a challenge password,
+which you can later use to request certificate revocation (*if* the CA
+supports doing revocations in this manner).
+
+Then there is the validity period; these are set with ``not_before``
+and ``not_after``. Both of these functions also take a
+``std::string``, which specifies when the certificate should start
+being valid, and when it should stop being valid. If you don't set the
+starting validity period, it will automatically choose the current
+time. If you don't set the ending time, it will choose the starting
+time plus a default time period. The arguments to these functions
+specify the time in the following format: "2002/11/27 1:50:14". The
+time is in 24-hour format, and the date is encoded as
+year/month/day. The date must be specified, but you can omit the time
+or trailing parts of it, for example "2002/11/27 1:50" or
+"2002/11/27".
+
+Third, you can set constraints on a key. The one you're mostly likely
+to want to use is to create (or request) a CA certificate, which can
+be done by calling the member function ``CA_key``. This should only be
+used when needed.
+
+Moreover, you can specify the padding scheme to be used when digital signatures
+are computed by calling function ``set_padding_scheme`` with a string
+representing the padding scheme. This way, you can control the padding scheme
+for self-signed certificates and PKCS #10 requests. The padding scheme used by
+a CA when building a certificate or a certificate revocation list can be set in
+the ``X509_CA`` constructor. The supported padding schemes can be found in
+src/lib/pubkey/padding.cpp. Some alternative names for the padding schemes are
+understood, as well.
+
+Other constraints can be set by calling the member functions
+``add_constraints`` and ``add_ex_constraints``. The first takes a
+``Key_Constraints`` value, and replaces any previously set value. If
+no value is set, then the certificate key is marked as being valid for
+any usage. You can set it to any of the following (for more than one
+usage, OR them together): ``DIGITAL_SIGNATURE``, ``NON_REPUDIATION``,
+``KEY_ENCIPHERMENT``, ``DATA_ENCIPHERMENT``, ``KEY_AGREEMENT``,
+``KEY_CERT_SIGN``, ``CRL_SIGN``, ``ENCIPHER_ONLY``,
+``DECIPHER_ONLY``. Many of these have quite special semantics, so you
+should either consult the appropriate standards document (such as RFC
+5280), or just not call ``add_constraints``, in which case the
+appropriate values will be chosen for you.
+
+The second function, ``add_ex_constraints``, allows you to specify an
+OID that has some meaning with regards to restricting the key to
+particular usages. You can, if you wish, specify any OID you like, but
+there is a set of standard ones that other applications will be able
+to understand. These are the ones specified by the PKIX standard, and
+are named "PKIX.ServerAuth" (for TLS server authentication),
+"PKIX.ClientAuth" (for TLS client authentication), "PKIX.CodeSigning",
+"PKIX.EmailProtection" (most likely for use with S/MIME),
+"PKIX.IPsecUser", "PKIX.IPsecTunnel", "PKIX.IPsecEndSystem", and
+"PKIX.TimeStamping". You can call "add_ex_constraints" any number of
+times - each new OID will be added to the list to include in the
+certificate.
+
+Lastly, you can add any X.509v3 extensions in the `extensions` member, which is
+useful if you want to encode a custom extension, or encode an extension in a way
+differently from how Botan defaults.
+
+OCSP Requests
+----------------------------------------
+
+A client makes an OCSP request to what is termed an 'OCSP responder'. This
+responder returns a signed response attesting that the certificate in question
+has not been revoked. The most recent OCSP specification is as of this
+writing :rfc:`6960`.
+
+Normally OCSP validation happens automatically as part of X.509 certificate
+validation, as long as OCSP is enabled (by setting a non-zero ``ocsp_timeout``
+in the call to ``x509_path_validate``, or for TLS by implementing the related
+``tls_verify_cert_chain_ocsp_timeout`` callback and returning a non-zero value
+from that). So most applications should not need to directly manipulate OCSP
+request and response objects.
+
+For those that do, the primary ocsp interface is in ``ocsp.h``. First a request
+must be formed, using information contained in the subject certificate and in
+the subject's issuing certificate.
+
+.. cpp:class:: OCSP::Request
+
+ .. cpp:function:: OCSP::Request(const X509_Certificate& issuer_cert, \
+ const BigInt& subject_serial)
+
+ Create a new OCSP request
+
+ .. cpp:function:: OCSP::Request(const X509_Certificate& issuer_cert, \
+ const X509_Certificate& subject_cert)
+
+ Variant of the above, using serial number from ``subject_cert``.
+
+ .. cpp:function:: std::vector<uint8_t> BER_encode() const
+
+ Encode the current OCSP request as a binary string.
+
+ .. cpp:function:: std::string base64_encode() const
+
+ Encode the current OCSP request as a base64 string.
+
+Then the response is parsed and validated, and if valid, can be consulted
+for certificate status information.
+
+.. cpp:class:: OCSP::Response
+
+ .. cpp:function:: OCSP::Response(const uint8_t response_bits[], size_t response_bits_len)
+
+ Attempts to parse ``response_bits`` as an OCSP response. Throws an
+ exception if parsing fails. Note that this does not verify that the OCSP
+ response is valid (ie that the signature is correct), merely that the
+ ASN.1 structure matches an OCSP response.
+
+ .. cpp:function:: Certificate_Status_Code check_signature( \
+ const std::vector<Certificate_Store*>& trust_roots, \
+ const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path = const std::vector<std::shared<const X509_Certificate>>()) const
+
+ Find the issuing certificate of the OCSP response, and check the signature.
+
+ If possible, pass the full certificate path being validated in
+ the optional ``cert_path`` argument: this additional information
+ helps locate the OCSP signer's certificate in some cases. If this
+ does not return ``Certificate_Status_Code::OCSP_SIGNATURE_OK``,
+ then the request must not be be used further.
+
+ .. cpp:function:: Certificate_Status_Code verify_signature(const X509_Certificate& issuing_cert) const
+
+ If the certificate that issued the OCSP response is already known (eg,
+ because in some specific application all the OCSP responses will always
+ be signed by a single trusted issuer whose cert is baked into the code)
+ this provides an alternate version of `check_signature`.
+
+ .. cpp:function:: Certificate_Status_Code status_for(const X509_Certificate& issuer, \
+ const X509_Certificate& subject, \
+ std::chrono::system_clock::time_point ref_time = std::chrono::system_clock::now()) const
+
+ Assuming the signature is valid, returns the status for the subject certificate.
+ Make sure to get the ordering of the issuer and subject certificates correct.
+
+ The ``ref_time`` is normally just the system clock, but can be used if
+ validation against some other reference time is desired (such as for
+ testing, to verify an old previously valid OCSP response, or to use an
+ alternate time source such as the Roughtime protocol instead of the local
+ client system clock).
+
+ .. cpp:function:: const X509_Time& produced_at() const
+
+ Return the time this OCSP response was (claimed to be) produced at.
+
+ .. cpp:function:: const X509_DN& signer_name() const
+
+ Return the distinguished name of the signer. This is used to help
+ find the issuing certificate.
+
+ This field is optional in OCSP responses, and may not be set.
+
+ .. cpp:function:: const std::vector<uint8_t>& signer_key_hash() const
+
+ Return the SHA-1 hash of the public key of the signer. This is used to
+ help find the issuing certificate. The ``Certificate_Store`` API
+ ``find_cert_by_pubkey_sha1`` can search on this value.
+
+ This field is optional in OCSP responses, and may not be set.
+
+ .. cpp:function:: const std::vector<uint8_t>& raw_bits() const
+
+ Return the entire raw ASN.1 blob (for debugging or specialized decoding needs)
+
+One common way of making OCSP requests is via HTTP, see :rfc:`2560`
+Appendix A for details. A basic implementation of this is the function
+``online_check``, which is available as long as the ``http_util`` module
+was compiled in; check by testing for the macro ``BOTAN_HAS_HTTP_UTIL``.
+
+.. cpp:function:: OCSP::Response online_check(const X509_Certificate& issuer, \
+ const BigInt& subject_serial, \
+ const std::string& ocsp_responder, \
+ const Certificate_Store* trusted_roots)
+
+ Assemble a OCSP request for serial number ``subject_serial`` and attempt to request
+ it to responder at URI ``ocsp_responder`` over a new HTTP socket, parses and returns
+ the response. If trusted_roots is not null, then the response is additionally
+ validated using OCSP response API ``check_signature``. Otherwise, this call must be
+ performed later by the application.
+
+.. cpp:function:: OCSP::Response online_check(const X509_Certificate& issuer, \
+ const X509_Certificate& subject, \
+ const Certificate_Store* trusted_roots)
+
+ Variant of the above but uses serial number and OCSP responder URI from ``subject``.
diff --git a/comm/third_party/botan/doc/authors.txt b/comm/third_party/botan/doc/authors.txt
new file mode 100644
index 0000000000..abaaab024a
--- /dev/null
+++ b/comm/third_party/botan/doc/authors.txt
@@ -0,0 +1,102 @@
+Alastair Houghton
+Alexander Bluhm (genua GmbH)
+Alex Gaynor
+Alf-André Walla
+Allan L. Bazinet
+Alon Bar-Lev
+Andrew Moon
+Antonio Coratelli
+Atanas Filyanov
+Baruch Burstein
+Bhaskar Biswas
+Bi11
+Bogdan Gusiev
+Chris Desjardins
+Christian Mainka (Hackmanit GmbH)
+Christopher Bläsius
+Christoph Ludwig
+cryptosource GmbH
+cynecx
+Dan Brown
+Daniel Neus (Rohde & Schwarz Cybersecurity)
+Daniel Seither (Kullo GmbH)
+Daniel Wyatt
+Eric Cornelius
+Erwan Chaussy
+etcimon
+Evgeny Pokhilko
+Fabian Weissberg
+Falko Strenzke (cryptosource GmbH)
+Felix Yan
+FlexSecure GmbH
+Florent Le Coz
+Francis Dupont
+Frank Schoenmann
+Google Inc
+Gustavo Serra Scalet
+guywithcrookedface
+Hannes Rantzsch
+Harry Reimann
+Hegedüs Márton Csaba
+Hubert Bugaj
+ilovezfs
+J08nY
+Jack Lloyd
+Jeffrey Walton
+Joel Low
+joerg
+Jose Luis Pereira (Fyde Inc.)
+Juraj Somorovsky (Hackmanit GmbH)
+Justin Karneges
+Kai Michaelis (Rohde & Schwarz Cybersecurity)
+Kirill A. Korinsky
+Konstantinos Kolelis
+Krzysztof Kwiatkowski
+Lauri Nurmi
+Luca Piccarreta
+Manuel Hartl
+Marcus Brinkmann
+Markus Wanner
+Martin Doering
+Matej Kenda (TopIT d.o.o.)
+Mathieu Souchaud
+Matthew Gregan
+Matthias Gierlings (Hackmanit GmbH)
+Matt Johnston
+Nathan Hourt
+Nicolas Sendrier
+Nuno Goncalves
+Ori Peleg
+Patrick Sona
+Patrick Wildt
+Patrik Fiedler
+Peter J Jones
+Philippe Lieser (Rohde & Schwarz Cybersecurity)
+Philipp Weber (Rohde & Schwarz Cybersecurity)
+Projet SECRET, INRIA, Rocquencourt
+René Korthaus (Rohde & Schwarz Cybersecurity)
+René Meusel
+Ribose Inc
+Robert Dailey
+Ryuhei Mori
+schregger
+Sergii Cherkavskyi
+seu
+Shlomi Fish
+Simon Cogliani
+Simon Warta (Kullo GmbH)
+slaviber
+souch
+t0b3
+tcely
+Technische Universitat Darmstadt
+Tim Oesterreich
+Tobias @neverhub
+Tomasz Frydrych
+Uri Blumenthal
+Vaclav Ovsik
+Volker Aßmann
+Yuri
+Yves Jerschow
+Zoltan Gyarmati
+0xdefaced
diff --git a/comm/third_party/botan/doc/building.rst b/comm/third_party/botan/doc/building.rst
new file mode 100644
index 0000000000..c5f5925f16
--- /dev/null
+++ b/comm/third_party/botan/doc/building.rst
@@ -0,0 +1,1019 @@
+.. _building:
+
+Building The Library
+=================================
+
+This document describes how to build Botan on Unix/POSIX and Windows
+systems. The POSIX oriented descriptions should apply to most
+common Unix systems (including OS X), along with POSIX-ish systems
+like BeOS, QNX, and Plan 9. Currently, systems other than Windows and
+POSIX (such as VMS, MacOS 9, OS/390, OS/400, ...) are not supported by
+the build system, primarily due to lack of access. Please contact the
+maintainer if you would like to build Botan on such a system.
+
+Botan's build is controlled by configure.py, which is a `Python
+<https://www.python.org>`_ script. Python 2.6 or later is required.
+
+.. highlight:: none
+
+For the impatient, this works for most systems::
+
+ $ ./configure.py [--prefix=/some/directory]
+ $ make
+ $ make install
+
+Or using ``nmake``, if you're compiling on Windows with Visual C++. On
+platforms that do not understand the '#!' convention for beginning
+script files, or that have Python installed in an unusual spot, you
+might need to prefix the ``configure.py`` command with ``python`` or
+``/path/to/python``::
+
+ $ python ./configure.py [arguments]
+
+Configuring the Build
+---------------------------------
+
+The first step is to run ``configure.py``, which is a Python script
+that creates various directories, config files, and a Makefile for
+building everything. This script should run under a vanilla install of
+Python 2.6, 2.7, or 3.x.
+
+The script will attempt to guess what kind of system you are trying to
+compile for (and will print messages telling you what it guessed).
+You can override this process by passing the options ``--cc``,
+``--os``, and ``--cpu``.
+
+You can pass basically anything reasonable with ``--cpu``: the script
+knows about a large number of different architectures, their
+sub-models, and common aliases for them. You should only select the
+64-bit version of a CPU (such as "sparc64" or "mips64") if your
+operating system knows how to handle 64-bit object code - a 32-bit
+kernel on a 64-bit CPU will generally not like 64-bit code.
+
+By default the script tries to figure out what will work on your
+system, and use that. It will print a display at the end showing which
+algorithms have and have not been enabled. For instance on one system
+we might see lines like::
+
+ INFO: Skipping (dependency failure): certstor_sqlite3 sessions_sqlite3
+ INFO: Skipping (incompatible CPU): aes_power8
+ INFO: Skipping (incompatible OS): darwin_secrandom getentropy win32_stats
+ INFO: Skipping (incompatible compiler): aes_armv8 pmull sha1_armv8 sha2_32_armv8
+ INFO: Skipping (no enabled compression schemes): compression
+ INFO: Skipping (requires external dependency): boost bzip2 lzma openssl sqlite3 tpm zlib
+
+The ones that are skipped because they are require an external
+dependency have to be explicitly asked for, because they rely on third
+party libraries which your system might not have or that you might not
+want the resulting binary to depend on. For instance to enable zlib
+support, add ``--with-zlib`` to your invocation of ``configure.py``.
+All available modules can be listed with ``--list-modules``.
+
+You can control which algorithms and modules are built using the
+options ``--enable-modules=MODS`` and ``--disable-modules=MODS``, for
+instance ``--enable-modules=zlib`` and ``--disable-modules=xtea,idea``.
+Modules not listed on the command line will simply be loaded if needed
+or if configured to load by default. If you use ``--minimized-build``,
+only the most core modules will be included; you can then explicitly
+enable things that you want to use with ``--enable-modules``. This is
+useful for creating a minimal build targeting to a specific
+application, especially in conjunction with the amalgamation option;
+see :ref:`amalgamation` and :ref:`minimized_builds`.
+
+For instance::
+
+ $ ./configure.py --minimized-build --enable-modules=rsa,eme_oaep,emsa_pssr
+
+will set up a build that only includes RSA, OAEP, PSS along with any
+required dependencies. Note that a minimized build does not by default
+include any random number generator, which is needed for example to
+generate keys, nonces and IVs. See :doc:`api_ref/rng` on which random number
+generators are available.
+
+Cross Compiling
+---------------------
+
+Cross compiling refers to building software on one type of host (say Linux
+x86-64) but creating a binary for some other type (say MinGW x86-32). This is
+completely supported by the build system. To extend the example, we must tell
+`configure.py` to use the MinGW tools::
+
+ $ ./configure.py --os=mingw --cpu=x86_32 --cc-bin=i686-w64-mingw32-g++ --ar-command=i686-w64-mingw32-ar
+ ...
+ $ make
+ ...
+ $ file botan.exe
+ botan.exe: PE32 executable (console) Intel 80386, for MS Windows
+
+.. note::
+ For whatever reason, some distributions of MinGW lack support for
+ threading or mutexes in the C++ standard library. You can work around
+ this by disabling thread support using ``--without-os-feature=threads``
+
+You can also specify the alternate tools by setting the `CXX` and `AR`
+environment variables (instead of the `--cc-bin` and `--ar-command` options), as
+is commonly done with autoconf builds.
+
+On Unix
+----------------
+
+The basic build procedure on Unix and Unix-like systems is::
+
+ $ ./configure.py [--enable-modules=<list>] [--cc=CC]
+ $ make
+ $ make check
+
+If the tests look OK, install::
+
+ $ make install
+
+On Unix systems the script will default to using GCC; use ``--cc`` if
+you want something else. For instance use ``--cc=icc`` for Intel C++
+and ``--cc=clang`` for Clang.
+
+The ``make install`` target has a default directory in which it will
+install Botan (typically ``/usr/local``). You can override this by
+using the ``--prefix`` argument to ``configure.py``, like so::
+
+ $ ./configure.py --prefix=/opt <other arguments>
+
+On some systems shared libraries might not be immediately visible to
+the runtime linker. For example, on Linux you may have to edit
+``/etc/ld.so.conf`` and run ``ldconfig`` (as root) in order for new
+shared libraries to be picked up by the linker. An alternative is to
+set your ``LD_LIBRARY_PATH`` shell variable to include the directory
+that the Botan libraries were installed into.
+
+On macOS
+--------------
+
+A build on macOS works much like that on any other Unix-like system.
+
+To build a universal binary for macOS, you need to set some additional
+build flags. Do this with the `configure.py` flag `--cc-abi-flags`::
+
+ --cc-abi-flags="-force_cpusubtype_ALL -mmacosx-version-min=10.4 -arch i386 -arch ppc"
+
+On Windows
+--------------
+
+.. note::
+
+ The earliest versions of Windows supported are Windows 7 and Windows 2008 R2
+
+You need to have a copy of Python installed, and have both Python and
+your chosen compiler in your path. Open a command shell (or the SDK
+shell), and run::
+
+ $ python configure.py --cc=msvc --os=windows
+ $ nmake
+ $ nmake check
+ $ nmake install
+
+Botan supports the nmake replacement `Jom <https://wiki.qt.io/Jom>`_
+which enables you to run multiple build jobs in parallel.
+
+For MinGW, use::
+
+ $ python configure.py --cc=gcc --os=mingw
+ $ make
+
+By default the install target will be ``C:\botan``; you can modify
+this with the ``--prefix`` option.
+
+When building your applications, all you have to do is tell the
+compiler to look for both include files and library files in
+``C:\botan``, and it will find both. Or you can move them to a
+place where they will be in the default compiler search paths (consult
+your documentation and/or local expert for details).
+
+
+For iOS using XCode
+-------------------------
+
+For iOS, you typically build for 3 architectures: armv7 (32 bit, older
+iOS devices), armv8-a (64 bit, recent iOS devices) and x86_64 for
+the iPhone simulator. You can build for these 3 architectures and then
+create a universal binary containing code for all of these
+architectures, so you can link to Botan for the simulator as well as
+for an iOS device.
+
+To cross compile for armv7, configure and make with::
+
+ $ ./configure.py --os=ios --prefix="iphone-32" --cpu=armv7 --cc=clang \
+ --cc-abi-flags="-arch armv7"
+ $ xcrun --sdk iphoneos make install
+
+To cross compile for armv8-a, configure and make with::
+
+ $ ./configure.py --os=ios --prefix="iphone-64" --cpu=armv8-a --cc=clang \
+ --cc-abi-flags="-arch arm64"
+ $ xcrun --sdk iphoneos make install
+
+To compile for the iPhone Simulator, configure and make with::
+
+ $ ./configure.py --os=ios --prefix="iphone-simulator" --cpu=x86_64 --cc=clang \
+ --cc-abi-flags="-arch x86_64"
+ $ xcrun --sdk iphonesimulator make install
+
+Now create the universal binary and confirm the library is compiled
+for all three architectures::
+
+ $ xcrun --sdk iphoneos lipo -create -output libbotan-2.a \
+ iphone-32/lib/libbotan-2.a \
+ iphone-64/lib/libbotan-2.a \
+ iphone-simulator/lib/libbotan-2.a
+ $ xcrun --sdk iphoneos lipo -info libbotan-2.a
+ Architectures in the fat file: libbotan-2.a are: armv7 x86_64 armv64
+
+The resulting static library can be linked to your app in Xcode.
+
+For Android
+---------------------
+
+Modern versions of Android NDK use Clang and support C++11. Simply
+configure using the appropriate NDK compiler::
+
+ $ export CXX=/opt/android-ndk/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android28-clang++
+ $ ./configure.py --os=android --cc=clang --cpu=arm64
+
+Docker
+^^^^^^^^^^^
+
+To build android version, there is the possibility to use
+the docker way::
+
+ sudo ANDROID_SDK_VER=21 ANDROID_ARCH=arm64 src/scripts/docker-android.sh
+
+This will produce the docker-builds/android folder containing
+each architecture compiled.
+
+Emscripten (WebAssembly)
+---------------------------
+
+To build for WebAssembly using Emscripten, try::
+
+ CXX=em++ ./configure.py --cc=clang --cpu=llvm --os=emscripten
+ make
+
+This will produce bitcode files ``botan-test.bc`` and ``botan.bc``
+along with a static archive ``libbotan-2.a`` which can linked with
+other modules. To convert the tests into a WASM file which can be
+executed on a browser, use::
+
+ em++ -s ALLOW_MEMORY_GROWTH=1 -s DISABLE_EXCEPTION_CATCHING=0 -s WASM=1 \
+ --preload-file src/tests/data botan-test.bc -o botan-test.html
+
+Supporting Older Distros
+--------------------------
+
+Some "stable" distributions, notably RHEL/CentOS, ship very obsolete
+versions of binutils, which do not support more recent CPU instructions.
+As a result when building you may receive errors like::
+
+ Error: no such instruction: `sha256rnds2 %xmm0,%xmm4,%xmm3'
+
+Depending on how old your binutils is, you may need to disable BMI2,
+AVX2, SHA-NI, and/or RDSEED. These can be disabled by passing the
+flags ``--disable-bmi2``, ``--disable-avx2``, ``--disable-sha-ni``,
+and ``--disable-rdseed`` to ``configure.py``.
+
+Other Build-Related Tasks
+----------------------------------------
+
+.. _building_docs:
+
+Building The Documentation
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+There are two documentation options available, Sphinx and Doxygen.
+Sphinx will be used if ``sphinx-build`` is detected in the PATH, or if
+``--with-sphinx`` is used at configure time. Doxygen is only enabled
+if ``--with-doxygen`` is used. Both are generated by the makefile
+target ``docs``.
+
+
+.. _amalgamation:
+
+The Amalgamation Build
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+You can also configure Botan to be built using only a single source file; this
+is quite convenient if you plan to embed the library into another application.
+
+To generate the amalgamation, run ``configure.py`` with whatever options you
+would ordinarily use, along with the option ``--amalgamation``. This will create
+two (rather large) files, ``botan_all.h`` and ``botan_all.cpp``.
+
+.. note::
+
+ The library will as usual be configured to target some specific operating
+ system and CPU architecture. You can use the CPU target "generic" if you need
+ to target multiple CPU architectures, but this has the effect of disabling
+ *all* CPU specific features such as SIMD, AES instruction sets, or inline
+ assembly. If you need to ship amalgamations for multiple targets, it would be
+ better to create different amalgamation files for each individual target.
+
+Whenever you would have included a botan header, you can then include
+``botan_all.h``, and include ``botan_all.cpp`` along with the rest of the source
+files in your build. If you want to be able to easily switch between amalgamated
+and non-amalgamated versions (for instance to take advantage of prepackaged
+versions of botan on operating systems that support it), you can instead ignore
+``botan_all.h`` and use the headers from ``build/include`` as normal.
+
+You can also build the library using Botan's build system (as normal) but
+utilizing the amalgamation instead of the individual source files by running
+something like ``./configure.py --amalgamation && make``. This is essentially a
+very simple form of link time optimization; because the entire library source is
+visible to the compiler, it has more opportunities for interprocedural
+optimizations. Additionally (assuming you are not making use of a compiler
+cache such as ``ccache`` or ``sccache``) amalgamation builds usually have
+significantly shorter compile times for full rebuilds.
+
+Modules Relying on Third Party Libraries
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Currently ``configure.py`` cannot detect if external libraries are
+available, so using them is controlled explicitly at build time
+by the user using
+
+ - ``--with-bzip2`` enables the filters providing bzip2 compression and
+ decompression. Requires the bzip2 development libraries to be installed.
+
+ - ``--with-zlib`` enables the filters providing zlib compression and
+ decompression. Requires the zlib development libraries to be installed.
+
+ - ``--with-lzma`` enables the filters providing lzma compression and
+ decompression. Requires the lzma development libraries to be installed.
+
+ - ``--with-sqlite3`` enables using sqlite3 databases in various contexts
+ (TLS session cache, PSK database, etc).
+
+ - ``--with-openssl`` adds an engine that uses OpenSSL for some ciphers, hashes,
+ and public key operations. OpenSSL 1.0.2 or later is supported. LibreSSL can
+ also be used.
+
+ - ``--with-tpm`` adds support for using TPM hardware via the TrouSerS library.
+
+ - ``--with-boost`` enables using some Boost libraries. In particular
+ Boost.Filesystem is used for a few operations (but on most platforms, a
+ native API equivalent is available), and Boost.Asio is used to provide a few
+ extra TLS related command line utilities.
+
+Multiple Builds
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+It may be useful to run multiple builds with different configurations.
+Specify ``--with-build-dir=<dir>`` to set up a build environment in a
+different directory.
+
+Setting Distribution Info
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The build allows you to set some information about what distribution
+this build of the library comes from. It is particularly relevant to
+people packaging the library for wider distribution, to signify what
+distribution this build is from. Applications can test this value by
+checking the string value of the macro ``BOTAN_DISTRIBUTION_INFO``. It
+can be set using the ``--distribution-info`` flag to ``configure.py``,
+and otherwise defaults to "unspecified". For instance, a `Gentoo
+<https://www.gentoo.org>`_ ebuild might set it with
+``--distribution-info="Gentoo ${PVR}"`` where ``${PVR}`` is an ebuild
+variable automatically set to a combination of the library and ebuild
+versions.
+
+Local Configuration Settings
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+You may want to do something peculiar with the configuration; to
+support this there is a flag to ``configure.py`` called
+``--with-local-config=<file>``. The contents of the file are
+inserted into ``build/build.h`` which is (indirectly) included
+into every Botan header and source file.
+
+Enabling or Disabling Use of Certain OS Features
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Botan uses compile-time flags to enable or disable use of certain operating
+specific functions. You can also override these at build time if desired.
+
+The default feature flags are given in the files in ``src/build-data/os`` in the
+``target_features`` block. For example Linux defines flags like ``proc_fs``,
+``getauxval``, and ``sockets``. The ``configure.py`` option
+``--list-os-features`` will display all the feature flags for all operating
+system targets.
+
+To disable a default-enabled flag, use ``--without-os-feature=feat1,feat2,...``
+
+To enable a flag that isn't otherwise enabled, use ``--with-os-feature=feat``.
+For example, modern Linux systems support the ``getentropy`` call, but it is not
+enabled by default because many older systems lack it. However if you know you
+will only deploy to recently updated systems you can use
+``--with-os-feature=getentropy`` to enable it.
+
+A special case if dynamic loading, which applications for certain environments
+will want to disable. There is no specific feature flag for this, but
+``--disable-modules=dyn_load`` will prevent it from being used.
+
+.. note:: Disabling ``dyn_load`` module will also disable the PKCS #11
+ wrapper, which relies on dynamic loading.
+
+Configuration Parameters
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+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.
+
+Building Applications
+----------------------------------------
+
+Unix
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Botan usually links in several different system libraries (such as
+``librt`` or ``libz``), depending on which modules are configured at
+compile time. In many environments, particularly ones using static
+libraries, an application has to link against the same libraries as
+Botan for the linking step to succeed. But how does it figure out what
+libraries it *is* linked against?
+
+The answer is to ask the ``botan`` command line tool using
+the ``config`` and ``version`` commands.
+
+``botan version``: Print the Botan version number.
+
+``botan config prefix``: If no argument, print the prefix where Botan is
+installed (such as ``/opt`` or ``/usr/local``).
+
+``botan config cflags``: Print options that should be passed to the
+compiler whenever a C++ file is compiled. Typically this is used for
+setting include paths.
+
+``botan config libs``: Print options for which libraries to link to
+(this will include a reference to the botan library itself).
+
+Your ``Makefile`` can run ``botan config`` and get the options
+necessary for getting your application to compile and link, regardless
+of whatever crazy libraries Botan might be linked against.
+
+Windows
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+No special help exists for building applications on Windows. However,
+given that typically Windows software is distributed as binaries, this
+is less of a problem - only the developer needs to worry about it. As
+long as they can remember where they installed Botan, they just have
+to set the appropriate flags in their Makefile/project file.
+
+Language Wrappers
+----------------------------------------
+
+Building the Python wrappers
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The Python wrappers for Botan use ctypes and the C89 API so no special
+build step is required, just import botan2.py
+
+See :doc:`Python Bindings <api_ref/python>` for more information about
+the Python bindings.
+
+.. _minimized_builds:
+
+Minimized Builds
+--------------------
+
+Many developers wish to configure a minimized build which contains only the
+specific features their application will use. In general this is straighforward:
+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.
+
+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
+if you attempt to use ``AutoSeeded_RNG``, it will fail. The easiest resolution
+is to also enable ``system_rng`` which can act as either an entropy source or
+used directly as the RNG.
+
+If you are building for x86, ARM, or POWER, it can be beneficial to enable
+hardware support for the relevant instruction sets with modules such as
+``aes_ni`` and ``clmul`` for x86, or ``aes_armv8``, ``pmull``, and
+``sha2_32_armv8`` on ARMv8. SIMD optimizations such as ``chacha_avx2`` also can
+provide substantial performance improvements.
+
+.. note::
+ In a future release, hardware specific modules will be enabled by default if
+ the underlying "base" module is enabled.
+
+If you are building a TLS application, you may (or may not) want to include
+``tls_cbc`` which enables support for CBC ciphersuites. If ``tls_cbc`` is
+disabled, then it will not be possible to negotiate TLS v1.0/v1.1. In general
+this should be considered a feature; only enable this if you need backward
+compatability with obsolete clients or servers.
+
+For TLS another useful feature which is not enabled by default is the
+ChaCha20Poly1305 ciphersuites. To enable these, add ``chacha20poly1305``.
+
+
+Configure Script Options
+---------------------------
+
+``--cpu=CPU``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Set the target CPU architecture. If not used, the arch of the current
+system is detected (using Python's platform module) and used.
+
+``--os=OS``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Set the target operating system.
+
+``--cc=COMPILER``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Set the desired build compiler
+
+``--cc-min-version=MAJOR.MINOR``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Set the minimal version of the target
+compiler. Use --cc-min-version=0.0 to support all compiler
+versions. Default is auto detection.
+
+``--cc-bin=BINARY``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Set path to compiler binary
+
+If not provided, the value of the ``CXX`` environment variable is used if set.
+
+``--cc-abi-flags=FLAGS``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Set ABI flags, which for the purposes of this option mean options
+which should be passed to both the compiler and linker.
+
+``--cxxflags=FLAGS``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Override all compiler flags. This is equivalent to setting ``CXXFLAGS``
+in the environment.
+
+``--extra-cxxflags=FLAGS``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Set extra compiler flags, which are appended to the default set. This
+is useful if you want to set just one or two additional options but
+leave the normal logic for selecting flags alone.
+
+``--ldflags=FLAGS``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Set flags to pass to the linker. This is equivalent to setting ``LDFLAGS``
+
+``--ar-command=AR``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Set the path to the tool to use to create static archives (``ar``).
+This is normally only used for cross-compilation.
+
+If not provided, the value of the ``AR`` environment variable is used if set.
+
+``--ar-options=AR_OPTIONS``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Specify the options to pass to ``ar``.
+
+If not provided, the value of the ``AR_OPTIONS`` environment variable is used if set.
+
+``--msvc-runtime=RT``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Specify the MSVC runtime to use (MT, MD, MTd, or MDd). If not specified,
+picks either MD or MDd depending on if debug mode is set.
+
+``--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``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Specify an OS feature to enable. See ``src/build-data/os`` and
+``doc/os.rst`` for more information.
+
+``--without-os-features=FEAT``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Specify an OS feature to disable.
+
+``--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
+
+``--with-debug-info``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Include debug symbols.
+
+``--with-sanitizers``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Enable some default set of sanitizer checks. What exactly is enabled
+depends on the compiler.
+
+``--enable-sanitizers=SAN``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Enable specific sanitizers. See ``src/build-data/cc`` for more information.
+
+``--without-stack-protector``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Disable stack smashing protections. **not recommended**
+
+``--with-coverage``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Add coverage info and disable optimizations
+
+``--with-coverage-info``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Add coverage info, but leave optimizations alone
+
+``--disable-shared-library``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Disable building a shared library
+
+``--disable-static-library``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Disable building static library
+
+``--optimize-for-size``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Optimize for code size.
+
+``--no-optimizations``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Disable all optimizations for debugging.
+
+``--debug-mode``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Enable debug info and disable optimizations
+
+``--amalgamation``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Use amalgamation to build
+
+``--system-cert-bundle=PATH``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Set a path to a file containing one or more trusted CA certificates in
+PEM format. If not given, some default locations are checked.
+
+``--with-build-dir=DIR``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Setup the build in a specified directory instead of ``./build``
+
+``--with-external-includedir=DIR``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Search for includes in this directory. Provide this parameter multiple times to
+define multiple additional include directories.
+
+``--with-external-libdir=DIR``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Add DIR to the link path. Provide this parameter multiple times to define
+multiple additional library link directories.
+
+``--define-build-macro``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Set a compile-time pre-processor definition (i.e. add a -D... to the compiler
+invocations). Provide this parameter multiple times to add multiple compile-time
+definitions. Both KEY=VALUE and KEY (without specific value) are supported.
+
+``--with-sysroot-dir=DIR``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Use specified dir for system root while cross-compiling
+
+``--with-openmp``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Enable use of OpenMP
+
+``--link-method=METHOD``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+During build setup a directory linking to each header file is created.
+Choose how the links are performed (options are "symlink", "hardlink",
+or "copy").
+
+``--with-local-config=FILE``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Include the contents of FILE into the generated build.h
+
+``--distribution-info=STRING``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Set distribution specific version information
+
+``--maintainer-mode``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+A build configuration used by library developers, which enables extra
+warnings and turns most warnings into errors.
+
+.. warning::
+
+ When this option is used, all relevant warnings available in the
+ most recent release of GCC/Clang are enabled, so it may fail to
+ build if your compiler is not sufficiently recent. In addition
+ there may be non-default configurations or unusual platforms which
+ cause warnings which are converted to errors. Patches addressing
+ such warnings are welcome, but otherwise no support is available
+ when using this option.
+
+``--werror-mode``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Turns most warnings into errors.
+
+``--no-install-python-module``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Skip installing Python module.
+
+``--with-python-versions=N.M``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Where to install botan2.py. By default this is chosen to be the
+version of Python that is running ``configure.py``.
+
+``--with-valgrind``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Use valgrind API to perform additional checks. Not needed by end users.
+
+``--unsafe-fuzzer-mode``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Disable essential checks for testing. **UNSAFE FOR PRODUCTION**
+
+``--build-fuzzers=TYPE``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Select which interface the fuzzer uses. Options are "afl",
+"libfuzzer", "klee", or "test". The "test" mode builds fuzzers that
+read one input from stdin and then exit.
+
+``--with-fuzzer-lib=LIB``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Specify an additional library that fuzzer binaries must link with.
+
+``--build-targets=BUILD_TARGETS``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Build only the specific targets and tools
+(``static``, ``shared``, ``cli``, ``tests``, ``bogo_shim``).
+
+``--boost-library-name``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Provide an alternative name for a boost library. Depending on the platform and
+boost's build configuration these library names differ significantly (see `Boost docs
+<https://www.boost.org/doc/libs/1_70_0/more/getting_started/unix-variants.html#library-naming>`_).
+The provided library name must be suitable as identifier in a linker parameter,
+e.g on unix: ``boost_system`` or windows: ``libboost_regex-vc71-x86-1_70``.
+
+``--without-documentation``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Skip building/installing documentation
+
+``--with-sphinx``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Use Sphinx to generate the handbook
+
+``--with-pdf``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Use Sphinx to generate PDF doc
+
+``--with-rst2man``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Use rst2man to generate a man page for the CLI
+
+``--with-doxygen``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Use Doxygen to generate API reference
+
+``--module-policy=POL``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The option ``--module-policy=POL`` enables modules required by and
+disables modules prohibited by a text policy in ``src/build-data/policy``.
+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
+
+``--enable-modules=MODS``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Enable some specific modules
+
+``--disable-modules=MODS``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Disable some specific modules
+
+``--minimized-build``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Start with the bare minimum. This is mostly useful in conjuction with
+``--enable-modules`` to get a build that has just the features a
+particular application requires.
+
+``--with-boost``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Use Boost.Asio for networking support. This primarily affects the
+command line utils.
+
+``--with-bzip2``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Enable bzip2 compression
+
+``--with-lzma``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Enable lzma compression
+
+``--with-zlib``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Enable using zlib compression
+
+``--with-openssl``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Enable using OpenSSL for certain operations
+
+``--with-commoncrypto``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Enable using CommonCrypto for certain operations
+
+``--with-sqlite3``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Enable using sqlite3 for data storage
+
+``--with-tpm``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Enable support for TPM
+
+``--program-suffix=SUFFIX``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+A string to append to all program binaries.
+
+``--library-suffix=SUFFIX``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+A string to append to all library names.
+
+``--prefix=DIR``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Set the install prefix.
+
+``--docdir=DIR``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Set the documentation installation dir.
+
+``--bindir=DIR``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Set the binary installation dir.
+
+``--libdir=DIR``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Set the library installation dir.
+
+``--mandir=DIR``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Set the man page installation dir.
+
+``--includedir=DIR``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Set the include file installation dir.
diff --git a/comm/third_party/botan/doc/cli.rst b/comm/third_party/botan/doc/cli.rst
new file mode 100644
index 0000000000..98f9f06d4c
--- /dev/null
+++ b/comm/third_party/botan/doc/cli.rst
@@ -0,0 +1,406 @@
+Command Line Interface
+========================================
+.. highlight:: sh
+
+Outline
+------------
+
+The ``botan`` program is a command line tool for using a broad variety
+of functions of the Botan library in the shell.
+
+All commands follow the syntax ``botan <command> <command-options>``.
+
+If ``botan`` is run with an unknown command, or without any command, or with the
+``--help`` option, all available commands will be printed. If a particular
+command is run with the ``--help`` option (like ``botan <command> --help``)
+some information about the usage of the command is printed.
+
+Starting in version 2.9, commands that take a passphrase (such as
+``gen_bcrypt`` or ``pkcs8``) will also accept the literal ``-`` to mean
+ask for the passphrase on the terminal. If supported by the operating
+system, echo will be disabled while reading the passphrase.
+
+Most arguments that take a path to a file will also accept the literal ``-``
+to mean the file content should be read from STDIN instead.
+
+Hash Function
+----------------
+``hash --algo=SHA-256 --buf-size=4096 --no-fsname --format=hex *files``
+ Compute the *algo* digest over the data in any number of *files*. If
+ no files are listed on the command line, the input source defaults
+ to standard input. Unless the ``--no-fsname`` option is given, the
+ filename is printed alongside the hash, in the style of tools such
+ as ``sha256sum``.
+
+Password Hash
+----------------
+``gen_argon2 --mem=65536 --p=1 --t=1 password``
+ Calculate the Argon2 password digest of *password*. *mem* is the amount of
+ memory to use in Kb, *p* the parallelization parameter and *t* the number of
+ iterations to use.
+
+``check_argon2 password hash``
+ Checks if the Argon2 hash of the passed *password* equals the passed *hash* value.
+
+``gen_bcrypt --work-factor=12 password``
+ Calculate the bcrypt password digest of *password*. *work-factor* is an
+ integer between 4 and 18. A higher *work-factor* value results in a
+ more expensive hash calculation.
+
+``check_bcrypt password hash``
+ Checks if the bcrypt hash of the passed *password* equals the passed *hash* value.
+
+``pbkdf_tune --algo=Scrypt --max-mem=256 --output-len=32 --check *times``
+ Tunes the PBKDF algorithm specified with ``--algo=`` for the given *times*.
+
+HMAC
+----------------
+``hmac --hash=SHA-256 --buf-size=4096 --no-fsname key files``
+ Compute the HMAC tag with the cryptographic hash function *hash*
+ using the key in file *key* over the data in *files*. *files*
+ defaults to STDIN. Unless the ``--no-fsname`` option is given, the
+ filename is printed alongside the HMAC value.
+
+Encryption
+----------------
+``encryption --buf-size=4096 --decrypt --mode= --key= --iv= --ad=``
+ Encrypt a given file with the specified *mode*. If ``--decrypt`` is provided
+ the file is decrypted instead.
+
+Public Key Cryptography
+-------------------------------------
+``keygen --algo=RSA --params= --passphrase= --pbe= --pbe-millis=300 --provider= --der-out``
+ Generate a PKCS #8 *algo* private key. If *der-out* is passed, the pair is BER
+ encoded. Otherwise, PEM encoding is used. To protect the PKCS #8 formatted
+ key, it is recommended to encrypt it with a provided *passphrase*. *pbe* is
+ the name of the desired encryption algorithm, which uses *pbe-millis*
+ milliseconds to derive the encryption key from the passed
+ *passphrase*. Algorithm specific parameters, as the desired bit length of an
+ RSA key, can be passed with *params*.
+
+ - For RSA *params* specifies the bit length of the RSA modulus. It defaults to 3072.
+ - For DH *params* specifies the DH parameters. It defaults to modp/ietf/2048.
+ - For DSA *params* specifies the DSA parameters. It defaults to dsa/botan/2048.
+ - For EC algorithms *params* specifies the elliptic curve. It defaults to secp256r1.
+
+ The default *pbe* algorithm is "PBES2(AES-256/CBC,SHA-256)".
+
+ With PBES2 scheme, you can select any CBC or GCM mode cipher which has an OID
+ defined (such as 3DES, Camellia, SM4, Twofish or Serpent). However most other
+ implementations support only AES or 3DES in CBC mode. You can also choose
+ Scrypt instead of PBKDF2, by using "Scrypt" instead of the name of a hash
+ function, for example "PBES2(AES-256/CBC,Scrypt)". Scrypt is also supported by
+ some other implementations including OpenSSL.
+
+``pkcs8 --pass-in= --pub-out --der-out --pass-out= --pbe= --pbe-millis=300 key``
+ Open a PKCS #8 formatted key at *key*. If *key* is encrypted, the passphrase
+ must be passed as *pass-in*. It is possible to (re)encrypt the read key with
+ the passphrase passed as *pass-out*. The parameters *pbe-millis* and *pbe*
+ work similarly to ``keygen``.
+
+``sign --der-format --passphrase= --hash=SHA-256 --emsa= --provider= key file``
+ Sign the data in *file* using the PKCS #8 private key *key*. If *key* is
+ encrypted, the used passphrase must be passed as *pass-in*. *emsa* specifies
+ the signature scheme and *hash* the cryptographic hash function used in the
+ scheme.
+
+ - For RSA signatures EMSA4 (RSA-PSS) is the default scheme.
+ - For ECDSA and DSA *emsa* defaults to EMSA1 (signing the hash directly)
+
+ For ECDSA and DSA, the option ``--der-format`` outputs the signature as an
+ ASN.1 encoded blob. Some other tools (including ``openssl``) default to this
+ format.
+
+ The signature is formatted for your screen using base64.
+
+``verify --der-format --hash=SHA-256 --emsa= pubkey file signature``
+ Verify the authenticity of the data in *file* with the provided signature
+ *signature* and the public key *pubkey*. Similarly to the signing process,
+ *emsa* specifies the signature scheme and *hash* the cryptographic hash
+ function used in the scheme.
+
+``gen_dl_group --pbits=1024 --qbits=0 --seed= --type=subgroup``
+ Generate ANSI X9.42 encoded Diffie-Hellman group parameters.
+
+ - If *type=subgroup* is passed, the size of the prime subgroup q is sampled
+ as a prime of *qbits* length and p is *pbits* long. If *qbits* is not
+ passed, its length is estimated from *pbits* as described in RFC 3766.
+ - If *type=strong* is passed, p is sampled as a safe prime with length
+ *pbits* and the prime subgroup has size q with *pbits*-1 length.
+ - If *type=dsa* is used, p and q are generated by the algorithm specified in
+ FIPS 186-4. If the ``--seed`` parameter is used, it allows to select the
+ seed value, instead of one being randomly generated. If the seed does not
+ in fact generate a valid DSA group, the command will fail.
+
+``dl_group_info --pem name``
+ Print raw Diffie-Hellman parameters (p,g) of the standardized DH group
+ *name*. If *pem* is set, the X9.42 encoded group is printed.
+
+``ec_group_info --pem name``
+ Print raw elliptic curve domain parameters of the standardized curve *name*. If
+ *pem* is set, the encoded domain is printed.
+
+``pk_encrypt --aead=AES-256/GCM rsa_pubkey datafile``
+ Encrypts ``datafile`` using the specified AEAD algorithm, under a key protected
+ by the specified RSA public key.
+
+``pk_decrypt rsa_privkey datafile``
+ Decrypts a file encrypted with ``pk_encrypt``. If the key is encrypted using a
+ password, it will be prompted for on the terminal.
+
+``fingerprint --no-fsname --algo=SHA-256 *keys``
+ Calculate the public key fingerprint of the *keys*.
+
+``pk_workfactor --type=rsa bits``
+ Provide an estimate of the strength of a public key based on it's size.
+ ``--type=`` can be "rsa", "dl" or "dl_exp".
+
+X.509
+----------------------------------------------
+
+``gen_pkcs10 key CN --country= --organization= --ca --path-limit=1 --email= --dns= --ext-ku= --key-pass= --hash=SHA-256 --emsa=``
+ 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.
+
+ - For RSA keys EMSA4 (RSA-PSS) is the default scheme.
+ - For ECDSA, DSA, ECGDSA, ECKCDSA and GOST-34.10 keys *emsa* defaults to EMSA1.
+
+``gen_self_signed key CN --country= --dns= --organization= --email= --path-limit=1 --days=365 --key-pass= --ca --hash=SHA-256 --emsa= --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.
+
+ - For RSA keys EMSA4 (RSA-PSS) is the default scheme.
+ - For ECDSA, DSA, ECGDSA, ECKCDSA and GOST-34.10 keys *emsa* defaults to EMSA1.
+
+``sign_cert --ca-key-pass= --hash=SHA-256 --duration=365 --emsa= 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.
+
+``ocsp_check --timeout=3000 subject issuer``
+ Verify an X.509 certificate against the issuers OCSP responder. Pass the
+ certificate to validate as *subject* and the CA certificate as *issuer*.
+
+``cert_info --fingerprint file``
+ Parse X.509 PEM certificate and display data fields. If ``--fingerprint`` is
+ used, the certificate's fingerprint is also printed.
+
+``cert_verify subject *ca_certs``
+ Verify if the provided X.509 certificate *subject* can be successfully
+ validated. The list of trusted CA certificates is passed with *ca_certs*,
+ which is a list of one or more certificates.
+
+``trust_roots --dn --dn-only --display``
+ List the certificates in the system trust store.
+
+TLS Server/Client
+-----------------------
+
+The ``--policy=`` argument of the TLS commands specifies the TLS policy to use.
+The policy can be any of the the strings "default", "suiteb_128", "suiteb_192",
+"bsi", "strict", or "all" to denote built-in policies, or it can name a file
+from which a policy description will be read.
+
+``tls_ciphers --policy=default --version=tls1.2``
+ Prints the list of ciphersuites that will be offered under a particular
+ policy/version.
+
+``tls_client host --port=443 --print-certs --policy=default --tls1.0 --tls1.1 --tls1.2 --skip-system-cert-store --trusted-cas= --session-db= --session-db-pass= --next-protocols= --type=tcp``
+ Implements a testing TLS client, which connects to *host* via TCP or UDP on
+ port *port*. The TLS version can be set with the flags *tls1.0*, *tls1.1* and
+ *tls1.2* of which the lowest specified version is automatically chosen. If
+ none of the TLS version flags is set, the latest supported version is
+ chosen. The client honors the TLS policy specified with *policy* and
+ prints all certificates in the chain, if *print-certs* is passed.
+ *next-protocols* is a comma separated list and specifies the protocols to
+ advertise with Application-Layer Protocol Negotiation (ALPN).
+
+``tls_server cert key --port=443 --type=tcp --policy=default --dump-traces= --max-clients=0 --socket-id=0``
+ Implements a testing TLS server, which allows TLS clients to connect and which
+ echos any data that is sent to it. Binds to either TCP or UDP on port
+ *port*. The server uses the certificate *cert* and the respective PKCS #8
+ private key *key*. The server honors the TLS policy specified with *policy*.
+ *socket-id* is only available on FreeBSD and sets the *so_user_cookie* value
+ of the used socket.
+
+``tls_http_server cert key --port=443 --policy=default --threads=0 --max-clients=0 --session-db --session-db-pass=``
+ Only available if Boost.Asio support was enabled. Provides a simple HTTP server
+ which replies to all requests with an informational text output. The server
+ honors the TLS policy specified with *policy*.
+
+``tls_proxy listen_port target_host target_port server_cert server_key--policy=default --threads=0 --max-clients=0 --session-db= --session-db-pass=``
+ Only available if Boost.Asio support was enabled. Listens on a port and
+ forwards all connects to a target server specified at
+ ``target_host`` and ``target_port``.
+
+``tls_client_hello --hex input``
+ Parse and print a TLS client hello message.
+
+Number Theory
+-----------------------
+``is_prime --prob=56 n``
+ Test if the integer *n* is composite or prime with a Miller-Rabin primality test with *(prob+2)/2* iterations.
+
+``factor n``
+ Factor the integer *n* using a combination of trial division by small primes, and Pollard's Rho algorithm.
+ It can in reasonable time factor integers up to 110 bits or so.
+
+``gen_prime --count=1 bits``
+ Samples *count* primes with a length of *bits* bits.
+
+``mod_inverse n mod``
+ Calculates a modular inverse.
+
+PSK Database
+--------------------
+
+The PSK database commands are only available if sqlite3 support was compiled in.
+
+``psk_set db db_key name psk``
+ Using the PSK database named db and encrypting under the (hex) key ``db_key``,
+ save the provided psk (also hex) under ``name``::
+
+ $ botan psk_set psk.db deadba55 bunny f00fee
+
+``psk_get db db_key name``
+ Get back a value saved with ``psk_set``::
+
+ $ botan psk_get psk.db deadba55 bunny
+ f00fee
+
+``psk_list db db_key``
+ List all values saved to the database under the given key::
+
+ $ botan psk_list psk.db deadba55
+ bunny
+
+Secret Sharing
+------------------
+
+Split a file into several shares.
+
+``tss_split M N data_file --id= --share-prefix=share --share-suffix=tss --hash=SHA-256``
+ Split a file into ``N`` pieces any ``M`` of which suffices to
+ recover the original input. The ID allows specifying a unique key ID
+ which may be up to 16 bytes long, this ensures that shares can be
+ uniquely matched. If not specified a random 16 byte value is
+ used. A checksum can be appended to the data to help verify correct
+ recovery, this can be disabled using ``--hash=None``.
+
+``tss_recover *shares``
+ Recover some data split by ``tss_split``. If insufficient number of
+ shares are provided an error is printed.
+
+Data Encoding/Decoding
+------------------------
+
+``base32_dec file``
+ Encode *file* to Base32.
+
+``base32_enc file``
+ Decode Base32 encoded *file*.
+
+``base58_enc --check file``
+ Encode *file* to Base58. If ``--check`` is provided Base58Check is used.
+
+``base58_dec --check file``
+ Decode Base58 encoded *file*. If ``--check`` is provided Base58Check is used.
+
+``base64_dec file``
+ Encode *file* to Base64.
+
+``base64_enc file``
+ Decode Base64 encoded *file*.
+
+``hex_dec file``
+ Encode *file* to Hex.
+
+``hex_enc file``
+ Decode Hex encoded *file*.
+
+Miscellaneous Commands
+-------------------------------------
+``version --full``
+ Print the version number. If option ``--full`` is provided,
+ additional details are printed.
+
+``has_command cmd``
+ Test if the command *cmd* is available.
+
+``config info_type``
+ Prints build information, useful for applications which want to
+ build against the library. The ``info_type`` argument can be any of
+ ``prefix``, ``cflags``, ``ldflags``, or ``libs``. This is
+ similar to information provided by the ``pkg-config`` tool.
+
+``cpuid``
+ List available processor flags (AES-NI, SIMD extensions, ...).
+
+``cpu_clock --test-duration=500``
+ Estimate the speed of the CPU cycle counter.
+
+``asn1print --skip-context-specific --print-limit=4096 --bin-limit=2048 --max-depth=64 --pem file```
+ Decode and print *file* with ASN.1 Basic Encoding Rules (BER). If flag ``--pem`` is
+ used, or the filename ends in ``.pem``, then PEM encoding is assumed. Otherwise
+ the input is assumed to be binary DER/BER.
+
+``http_get --redirects=1 --timeout=3000 url``
+ Retrieve resource from the passed http *url*.
+
+``speed --msec=500 --format=default --ecc-groups= --provider= --buf-size=1024 --clear-cpuid= --cpu-clock-speed=0 --cpu-clock-ratio=1.0 *algos``
+ Measures the speed of the passed *algos*. If no *algos* are passed all
+ available speed tests are executed. *msec* (in milliseconds) sets the period
+ of measurement for each algorithm. The *buf-size* option allows testing the
+ same algorithm on one or more input sizes, for example
+ ``speed --buf-size=136,1500 AES-128/GCM`` tests the performance of GCM for
+ small and large packet sizes.
+ *format* can be "default", "table" or "json".
+
+``timing_test test_type --test-data-file= --test-data-dir=src/tests/data/timing --warmup-runs=1000 --measurement-runs=10000``
+ Run various timing side channel tests.
+
+``rng --format=hex --system --rdrand --auto --entropy --drbg --drbg-seed= *bytes``
+ Sample *bytes* random bytes from the specified random number generator. If
+ *system* is set, the system RNG is used. If *rdrand* is set, the hardware
+ RDRAND instruction is used. If *auto* is set, AutoSeeded_RNG is used, seeded
+ with the system RNG if available or the global entropy source otherwise. If
+ *entropy* is set, AutoSeeded_RNG is used, seeded with the global entropy
+ source. If *drbg* is set, HMAC_DRBG is used seeded with *drbg-seed*.
+
+``entropy --truncate-at=128 source``
+ Sample a raw entropy source.
+
+``cc_encrypt CC passphrase --tweak=``
+ Encrypt the passed valid credit card number *CC* using FPE encryption and the
+ passphrase *passphrase*. The key is derived from the passphrase using PBKDF2
+ with SHA256. Due to the nature of FPE, the ciphertext is also a credit card
+ number with a valid checksum. *tweak* is public and parameterizes the
+ encryption function.
+
+``cc_decrypt CC passphrase --tweak=``
+ Decrypt the passed valid ciphertext *CC* using FPE decryption with
+ the passphrase *passphrase* and the tweak *tweak*.
+
+``roughtime_check --raw-time chain-file``
+ Parse and validate a Roughtime chain file.
+
+``roughtime --raw-time --chain-file=roughtime-chain --max-chain-size=128 --check-local-clock=60 --host= --pubkey= --servers-file=``
+ Retrieve time from a Roughtime server and store it in a chain file.
+
+``uuid``
+ Generate and print a random UUID.
+
+``compress --type=gzip --level=6 --buf-size=8192 file``
+ Compress a given file.
+
+``decompress --buf-size=8192 file``
+ Decompress a given compressed archive.
diff --git a/comm/third_party/botan/doc/contents.rst b/comm/third_party/botan/doc/contents.rst
new file mode 100644
index 0000000000..dadbd49475
--- /dev/null
+++ b/comm/third_party/botan/doc/contents.rst
@@ -0,0 +1,25 @@
+
+Contents
+========================================
+
+.. toctree::
+
+ index
+ goals
+ support
+ building
+ api_ref/contents
+ cli
+ deprecated
+ roadmap
+ credits
+ abi
+ packaging
+ security
+ side_channels
+ dev_ref/contents
+
+.. toctree::
+ :hidden:
+
+ old_news
diff --git a/comm/third_party/botan/doc/credits.rst b/comm/third_party/botan/doc/credits.rst
new file mode 100644
index 0000000000..a2b48bf243
--- /dev/null
+++ b/comm/third_party/botan/doc/credits.rst
@@ -0,0 +1,156 @@
+
+Credits
+========================================
+
+This is at least a partial credits-file of people that have contributed
+to botan. It is sorted by name and formatted to allow easy grepping
+and beautification by scripts. The fields are name (N), email (E),
+web-address (W), PGP key ID and fingerprint (P), description (D),
+snail-mail address (S), and Bitcoin address (B).
+
+.. highlight:: none
+
+::
+
+ N: Alexander Bluhm
+ W: https://www.genua.de/
+ P: 1E3B BEA4 6C20 EA00 2FFC DE4D C5F4 83AD DEE8 6380
+ D: improve support for OpenBSD
+ S: Kirchheim, Germany
+
+ N: Charles Brockman
+ W: http://www.securitygenetics.com/
+ D: documentation editing
+ S: Oregon, USA
+
+ 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
+
+ N: Martin Doering
+ E: doering@cdc.informatik.tu-darmstadt.de
+ D: GF(p) arithmetic
+
+ N: Olivier de Gaalon
+ D: SQLite encryption codec (src/contrib/sqlite)
+
+ N: Matthias Gierlings
+ E: matthias.gierlings@hackmanit.de
+ W: https://www.hackmanit.de/
+ P: 39E0 D270 19A4 B356 05D0 29AE 1BD3 49CF 744A 02FF
+ D: GMAC, Extended Hash-Based Signatures (XMSS)
+ S: Bochum, Germany
+
+ N: Matthew Gregan
+ D: Binary file I/O support, allocator fixes
+
+ N: Hany Greiss
+ D: Windows porting
+
+ N: Manuel Hartl
+ E: hartl@flexsecure.de
+ W: http://www.flexsecure.de/
+ D: ECDSA, ECDH
+
+ N: Yves Jerschow
+ E: yves.jerschow@uni-duesseldorf.de
+ D: Optimizations for memory load/store and HMAC
+ D: Support for IPv4 addresses in X.509 alternative names
+ S: Germany
+
+ N: Matt Johnston
+ D: Allocator fixes and optimizations, decompressor fixes
+
+ N: Peter J. Jones
+ E: pjones@pmade.org
+ D: Bzip2 compression module
+ S: Colorado, USA
+
+ N: Justin Karneges
+ D: Qt support modules (mutexes and types), X.509 API design
+
+ N: Vojtech Kral
+ E: vojtech@kral.hk
+ D: LZMA compression module
+ S: Czech Republic
+
+ N: Matej Kenda
+ E: matej.kenda@topit.si
+ D: Locking in Algo_Registry for Windows OS
+ S: Slovenia
+
+ N: René Korthaus
+ E: r.korthaus@sirrix.com
+ W: https://www.sirrix.com
+ P: C196 FF9D 3DDC A5E7 F98C E745 9AD0 F9FA 587E 74D6
+ D: CI, ECGDSA, ECKCDSA
+ S: Bochum, Germany
+
+ N: Adam Langley
+ E: agl@imperialviolet.org
+ D: Curve25519
+
+ N: Jack Lloyd
+ 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
+
+ N: Joel Low
+ D: DLL symbol visibility and Windows DLL support in general
+ D: Threaded_Fork
+
+ N: Christoph Ludwig
+ E: ludwig@fh-worms.de
+ D: GP(p) arithmetic
+
+ N: Vaclav Ovsik
+ E: vaclav.ovsik@i.cz
+ D: Perl XS module (src/contrib/perl-xs)
+
+ N: Luca Piccarreta
+ E: luca.piccarreta@gmail.com
+ D: x86/amd64 assembler, BigInt optimizations, Win32 mutex module
+ S: Italy
+
+ N: Daniel Seither
+ E: post@tiwoc.de
+ D: iOS support, improved Android support, improved MSVC support
+
+ N: Falko Strenzke
+ E: fstrenzke@cryptosource.de
+ W: http://www.cryptosource.de
+ D: McEliece, GF(p) arithmetic, CVC, Shanks-Tonelli algorithm
+ S: Darmstadt, Germany
+
+ N: Simon Warta
+ E: simon@kullo.net
+ W: https://www.kullo.net
+ D: Build system
+ S: Germany
+
+ N: Philipp Weber
+ E: p.weber@sirrix.com
+ W: https://sirrix.com/
+ D: KDF1-18033, ECIES
+ S: Saarland, Germany
+
+ N: Daniel Neus
+ E: d.neus@sirrix.com
+ W: https://sirrix.com/
+ D: CI, PKCS#11, RdSeed, BSI module policy
+ S: Bochum, Germany
+
+ N: Erwan Chaussy
+ D: Base32, Base64 matching Base32 implementation
+ S: France
+
+ N: Daniel Wyatt (on behalf of Ribose Inc)
+ E: daniel.wyatt@ribose.com
+ W: https://www.ribose.com/
+ D: SM3, Streebog, various minor contributions
diff --git a/comm/third_party/botan/doc/deprecated.rst b/comm/third_party/botan/doc/deprecated.rst
new file mode 100644
index 0000000000..e306786201
--- /dev/null
+++ b/comm/third_party/botan/doc/deprecated.rst
@@ -0,0 +1,302 @@
+Deprecated Features
+========================
+
+Certain functionality is deprecated and is likely to be removed in
+a future major release.
+
+To help warn users, macros are used to annotate deprecated functions
+and headers. These warnings are enabled by default, but can be
+disabled by defining the macro ``BOTAN_NO_DEPRECATED_WARNINGS`` prior
+to including any Botan headers.
+
+.. warning::
+ Not all of the functionality which is currently deprecated has an
+ associated warning.
+
+If you are using something which is currently deprecated and there
+doesn't seem to be an obvious alternative, contact the developers to
+explain your use case if you want to make sure your code continues to
+work.
+
+TLS Protocol Deprecations
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The following TLS protocol features are deprecated and will be removed
+in a future major release:
+
+- Support for TLSv1.0/v1.1 and DTLS v1.0
+
+- All support for DSA ciphersuites/certificates
+
+- Support for point compression in TLS. This is supported in v1.2 but
+ removed in v1.3. For simplicity it will be removed in v1.2 also.
+
+- Support for using SHA-1 to sign TLS v1.2 ServerKeyExchange.
+
+- All CBC mode ciphersuites. This includes all available 3DES and SEED
+ ciphersuites. This implies also removing Encrypt-then-MAC extension.
+
+- All ciphersuites using DH key exchange (DHE-DSS, DHE-RSA, DHE-PSK, anon DH)
+
+- Support for renegotiation in TLS v1.2
+
+- All ciphersuites using static RSA key exchange
+
+- All anonymous (DH/ECDH) ciphersuites. This does not include PSK and
+ ECDHE-PSK, which will be retained.
+
+- SRP ciphersuites. This is implied by the removal of CBC mode, since
+ all available SRP ciphersuites use CBC. To avoid use of obsolete
+ ciphers, it would be better to instead perform a standard TLS
+ negotiation, then a PAKE authentication within (and bound to) the
+ TLS channel.
+
+- OCB ciphersuites using 128-bit keys
+
+Deprecated Functionality
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+This section lists cryptographic functionality which will be removed
+in a future major release.
+
+- Block ciphers CAST-256, GOST 28147, Kasumi, MISTY1, DESX, XTEA, Noekeon
+
+- Hash functions GOST 34.11-94, Tiger, MD4
+
+- X9.42 KDF
+
+- DLIES
+
+- MCEIES
+
+- CBC-MAC
+
+- PBKDF1 key derivation
+
+- GCM support for 64-bit tags
+
+- Weak or rarely used ECC builtin groups including "secp160k1", "secp160r1",
+ "secp160r2", "secp192k1", "secp224k1",
+ "brainpool160r1", "brainpool192r1", "brainpool224r1", "brainpool320r1",
+ "x962_p192v2", "x962_p192v3", "x962_p239v1", "x962_p239v2", "x962_p239v3".
+
+- All built in MODP groups < 2048 bits
+
+- Support for explicit ECC curve parameters and ImplicitCA encoded parameters in
+ EC_Group and all users (including X.509 certificates and PKCS#8 private keys).
+
+- All pre-created DSA groups
+
+- All support for loading, generating or using RSA keys with a public
+ exponent larger than 2**64-1
+
+- All or nothing package transform (``package.h``)
+
+
+Deprecated Headers
+^^^^^^^^^^^^^^^^^^^^^^
+
+* The following headers and all functionality contained within them
+ are outright deprecated, and will be removed entirely in a future
+ major release. Most are either simply forwarding includes to another
+ (still public) header, or contain functionality which is entirely
+ deprecated. Consult the relevent file for more information.
+ ``basefilt.h``, ``botan.h``, ``buf_filt.h``, ``cipher_filter.h``, ``comp_filter.h``,
+ ``compiler.h``, ``init.h``, ``key_filt.h``, ``lookup.h``, ``sm2_enc.h``, ``threefish.h``,
+ ``xmss_key_pair.h``
+
+* The following headers have useful functionality but which we wish to
+ hide from applications to allow easier library evolution. They will
+ be made internal in a future major release, and will only be
+ available to the library itself. In most cases, there is an
+ alternative available. For example instead of using algorithm
+ specific interfaces, use X::create to create the object dynamically.
+
+ Block cipher headers (interact using BlockCipher interface):
+ ``aes.h``,
+ ``aria.h``,
+ ``blowfish.h``,
+ ``camellia.h``,
+ ``cascade.h``,
+ ``cast128.h``,
+ ``cast256.h``,
+ ``des.h``,
+ ``desx.h``,
+ ``gost_28147.h``,
+ ``idea.h``,
+ ``kasumi.h``,
+ ``lion.h``,
+ ``misty1.h``,
+ ``noekeon.h``,
+ ``seed.h``,
+ ``serpent.h``,
+ ``shacal2.h``,
+ ``sm4.h``,
+ ``threefish_512.h``,
+ ``twofish.h``,
+ ``xtea.h``,
+
+ Hash function headers (interact using HashFunction interface):
+ ``adler32.h``,
+ ``blake2b.h``,
+ ``comb4p.h``,
+ ``crc24.h``,
+ ``crc32.h``,
+ ``gost_3411.h``,
+ ``keccak.h``,
+ ``md4.h``,
+ ``md5.h``,
+ ``par_hash.h``,
+ ``rmd160.h``,
+ ``sha160.h``,
+ ``sha2_32.h``,
+ ``sha2_64.h``,
+ ``sha3.h``,
+ ``shake.h``,
+ ``skein_512.h``,
+ ``sm3.h``,
+ ``streebog.h``,
+ ``tiger.h``,
+ ``whrlpool.h``,
+
+ MAC headers:
+ ``cbc_mac.h``,
+ ``cmac.h``,
+ ``gmac.h``,
+ ``hmac.h``,
+ ``poly1305.h``,
+ ``siphash.h``,
+ ``x919_mac.h``,
+
+ Stream cipher headers:
+ ``chacha.h``,
+ ``ctr.h``,
+ ``ofb.h``,
+ ``rc4.h``,
+ ``salsa20.h``,
+
+ Cipher mode headers:
+ ``cbc.h``,
+ ``ccm.h``,
+ ``cfb.h``,
+ ``chacha20poly1305.h``,
+ ``eax.h``,
+ ``gcm.h``,
+ ``ocb.h``,
+ ``shake_cipher.h``,
+ ``siv.h``,
+ ``xts.h``,
+
+ KDF headers:
+ ``hkdf.h``,
+ ``kdf1.h``,
+ ``kdf1_iso18033.h``,
+ ``kdf2.h``,
+ ``prf_tls.h``,
+ ``prf_x942.h``,
+ ``sp800_108.h``,
+ ``sp800_56a.h``,
+ ``sp800_56c.h``,
+
+ PBKDF headers:
+ ``bcrypt_pbkdf.h``,
+ ``pbkdf1.h``,
+ ``pbkdf2.h``,
+ ``pgp_s2k.h``,
+ ``scrypt.h``,
+
+ Internal implementation headers - seemingly no reason for applications to use:
+ ``blinding.h``,
+ ``curve_gfp.h``,
+ ``curve_nistp.h``,
+ ``datastor.h``,
+ ``divide.h``,
+ ``eme.h``,
+ ``eme_pkcs.h``,
+ ``eme_raw.h``,
+ ``emsa.h``,
+ ``emsa1.h``,
+ ``emsa_pkcs1.h``,
+ ``emsa_raw.h``,
+ ``emsa_x931.h``,
+ ``gf2m_small_m.h``,
+ ``ghash.h``,
+ ``iso9796.h``,
+ ``keypair.h``,
+ ``mdx_hash.h``,
+ ``mode_pad.h``,
+ ``mul128.h``,
+ ``oaep.h``,
+ ``pbes2.h``,
+ ``polyn_gf2m.h``,
+ ``pow_mod.h``,
+ ``pssr.h``,
+ ``reducer.h``,
+ ``rfc6979.h``,
+ ``scan_name.h``,
+ ``stream_mode.h``,
+ ``tls_algos.h``,
+ ``tls_magic.h``,
+ ``xmss_common_ops.h``,
+ ``xmss_hash.h``,
+ ``xmss_index_registry.h``,
+ ``xmss_tools.h``,
+
+ Utility headers, nominally useful in applications but not a core part of
+ the library API and most are just sufficient for what the library needs
+ to implement other functionality.
+ ``atomic.h``,
+ ``bswap.h``,
+ ``charset.h``,
+ ``compiler.h``,
+ ``cpuid.h``,
+ ``http_util.h``,
+ ``loadstor.h``,
+ ``locking_allocator.h``,
+ ``parsing.h``,
+ ``rotate.h``,
+ ``secqueue.h``,
+ ``stl_compatibility.h``,
+ ``uuid.h``,
+
+ Merged into other headers:
+ ``alg_id.h``, ``asn1_oid.h``, ``asn1_str.h``, and ``asn1_time.h`` - use ``asn1_obj.h``
+
+Other API deprecations
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+- Directly accessing the member variables of types ``calendar_point``,
+ ``ASN1_Attribute``, ``AlgorithmIdentifier``, and ``BER_Object``
+
+- Using a default output length for "SHAKE-128" and "SHAKE-256". Instead,
+ always specify the desired output length.
+
+- Currently, for certain KDFs, if KDF interface is invoked with a
+ requested output length larger than supported by the KDF, it returns
+ instead a truncated key. In a future major release, instead if KDF
+ is called with a length larger than it supports an exception will be
+ thrown.
+
+- The TLS constructors taking ``std::function`` for callbacks. Instead
+ use the ``TLS::Callbacks`` interface.
+
+- Using ``X509_Certificate::subject_info`` and ``issuer_info`` to access any
+ information that is not included in the DN or subject alternative name. Prefer
+ using the specific assessor functions for other data, eg instead of
+ ``cert.subject_info("X509.Certificate.serial")`` use ``cert.serial_number()``.
+
+- The ``Buffered_Computation`` base class. In a future release the
+ class will be removed, and all of member functions instead declared
+ directly on ``MessageAuthenticationCode`` and ``HashFunction``. So
+ this only affects you if you are directly referencing
+ ``Botan::Buffered_Computation`` in some way.
+
+Deprecated Build Targets
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+- Configuring a build (with ``configure.py``) using Python2. In a future
+ major release, Python3 will be required.
+
+- Platform support for Google Native Client
+
+- Support for PathScale and HP compilers
diff --git a/comm/third_party/botan/doc/dev_ref/configure.rst b/comm/third_party/botan/doc/dev_ref/configure.rst
new file mode 100644
index 0000000000..7eb8358edf
--- /dev/null
+++ b/comm/third_party/botan/doc/dev_ref/configure.rst
@@ -0,0 +1,407 @@
+Understanding configure.py
+============================
+
+.. highlight:: none
+
+Botan's build is handled with a custom Python script, ``configure.py``.
+This document tries to explain how configure works.
+
+.. note::
+ You only need to read this if you are modifying the library,
+ or debugging some problem with your build. For how to use it,
+ see :ref:`building`.
+
+Build Structure
+--------------------
+
+Modules are a group of related source and header files, which can be
+individually enabled or disabled at build time. Modules can depend on
+other modules; if a dependency is not available then the module itself
+is also removed from the list. Examples of modules in the existing
+codebase are ``asn1`` and ``x509``, Since ``x509`` depends on (among
+other things) ``asn1``, disabling ``asn1`` will also disable ``x509``.
+
+Most modules define one or more macros, which application code can use
+to detect the modules presence or absence. The value of each macro is
+a datestamp, in the form YYYYMMDD which indicates the last time this
+module changed in a way that would be visible to an application. For
+example if a class gains a new function, the datestamp should be
+incremented. That allows applications to detect if the new feature is
+available.
+
+What ``configure.py`` does
+-----------------------------
+
+First, all command line options are parsed.
+
+Then all of the files giving information about target CPUs, compilers,
+etc are parsed and sanity checked.
+
+In ``calculate_cc_min_version`` the compiler version is detected using
+the preprocessor.
+
+Then in ``check_compiler_arch`` the target architecture are detected, again
+using the preprocessor.
+
+Now that the target is identified and options have been parsed, the modules to
+include into the artifact are picked, in ``ModulesChooser``.
+
+In ``create_template_vars``, a dictionary of variables is created which describe
+different aspects of the build. These are serialized to
+``build/build_config.json``.
+
+Up until this point no changes have been made on disk. This occurs in
+``do_io_for_build``. Build output directories are created, and header files are
+linked into ``build/include/botan``. Templates are processed to create the
+Makefile, ``build.h`` and other artifacts.
+
+When Modifying ``configure.py``
+--------------------------------
+
+For now, any changes to ``configure.py`` must work under both CPython 2.7 and
+CPython 3.x. In a future major release, support for CPython2 will be dropped,
+but until then if making modifications verify the code works as expected on
+both versions.
+
+Run ``./src/scripts/ci_build.py lint`` to run Pylint checks after any change.
+
+Template Language
+--------------------
+
+Various output files are generated by processing input files using a simple
+template language. All input files are stored in ``src/build-data`` and use the
+suffix ``.in``. Anything not recognized as a template command is passed through
+to the output unmodified. The template elements are:
+
+ * Variable substitution, ``%{variable_name}``. The configure script creates
+ many variables for various purposes, this allows getting their value within
+ the output. If a variable is not defined, an error occurs.
+
+ If a variable reference ends with ``|upper``, the value is uppercased before
+ being inserted into the template output.
+
+ * Iteration, ``%{for variable} block %{endfor}``. This iterates over a list and
+ repeats the block as many times as it is included. Variables within the block
+ are expanded. The two template elements ``%{for ...}`` and ``%{endfor}`` must
+ appear on lines with no text before or after.
+
+ * Conditional inclusion, ``%{if variable} block %{endif}``. If the variable
+ named is defined and true (in the Python sense of the word; if the variable
+ is empty or zero it is considered false), then the block will be included and
+ any variables expanded. As with the for loop syntax, both the start and end
+ of the conditional must be on their own lines with no additional text.
+
+Adding a new module
+--------------------
+
+Create a directory in the appropriate place and create a info.txt file.
+
+Syntax of ``info.txt``
+------------------------
+
+.. warning::
+
+ The syntax described here is documented to make it easier to use
+ and understand, but it is not considered part of the public API
+ contract. That is, the developers are allowed to change the syntax
+ at any time on the assumption that all users are contained within
+ the library itself. If that happens this document will be updated.
+
+Modules and files describing information about the system use the same
+parser and have common syntactical elements.
+
+Comments begin with '#' and continue to end of line.
+
+There are three main types: maps, lists, and variables.
+
+A map has a syntax like::
+
+ <MAP_NAME>
+ NAME1 -> VALUE1
+ NAME2 -> VALUE2
+ ...
+ </MAP_NAME>
+
+The interpretation of the names and values will depend on the map's name
+and what type of file is being parsed.
+
+A list has similar syntax, it just doesn't have values::
+
+ <LIST_NAME>
+ ELEM1
+ ELEM2
+ ...
+ </LIST_NAME>
+
+Lastly there are single value variables like::
+
+ VAR1 SomeValue
+ VAR2 "Quotes Can Be Used (And will be stripped out)"
+ VAR3 42
+
+Variables can have string, integer or boolean values. Boolean values
+are specified with 'yes' or 'no'.
+
+Module Syntax
+---------------------
+
+The ``info.txt`` files have the following elements. Not all are required; a minimal
+file for a module with no dependencies might just contain a macro define.
+
+Lists:
+ * ``comment`` and ``warning`` provides block-comments which
+ are displayed to the user at build time.
+ * ``requires`` is a list of module dependencies. An ``os_features`` can be
+ specified as a condition for needing the dependency by writing it before
+ the module name and separated by a ``?``, e.g. ``rtlgenrandom?dyn_load``.
+ * ``header:internal`` is the list of headers (from the current module)
+ which are internal-only.
+ * ``header:public`` is a the list of headers (from the
+ current module) which should be exported for public use. If neither
+ ``header:internal`` nor ``header:public`` are used then all headers
+ in the current directory are assumed public.
+
+ .. note:: If you omit a header from both internal and public lists, it will
+ be ignored.
+
+ * ``header:external`` is used when naming headers which are included
+ in the source tree but might be replaced by an external version. This is used
+ for the PKCS11 headers.
+ * ``arch`` is a list of architectures this module may be used on.
+ * ``isa`` lists ISA features which must be enabled to use this module.
+ Can be proceeded by an ``arch`` name followed by a ``:`` if it is only needed
+ on a specific architecture, e.g. ``x86_64:ssse3``.
+ * ``cc`` is a list of compilers which can be used with this module. If the
+ compiler name is suffixed with a version (like "gcc:5.0") then only compilers
+ with that minimum version can use the module.
+ * ``os_features`` is a list of OS features which are required in order to use this
+ module. Each line can specify one or more features combined with ','. Alternatives
+ can be specified on additional lines.
+
+Maps:
+ * ``defines`` is a map from macros to datestamps. These macros will be defined in
+ the generated ``build.h``.
+ * ``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).
+ * ``frameworks`` is a macOS/iOS specific feature which maps from an OS name to
+ a framework.
+
+Variables:
+ * ``load_on`` Can take on values ``never``, ``always``, ``auto``, ``dep`` or ``vendor``.
+ TODO describe the behavior of these
+ * ``endian`` Required endian for the module (``any`` (default), ``little``, ``big``)
+
+An example::
+
+ # Disable this by default
+ load_on never
+
+ <isa>
+ sse2
+ </isa>
+
+ <defines>
+ DEFINE1 -> 20180104
+ DEFINE2 -> 20190301
+ </defines>
+
+ <comment>
+ I have eaten
+ the plums
+ that were in
+ the icebox
+ </comment>
+
+ <warning>
+ There are no more plums
+ </warning>
+
+ <header:public>
+ header1.h
+ </header:public>
+
+ <header:internal>
+ header_helper.h
+ whatever.h
+ </header:internal>
+
+ <arch>
+ x86_64
+ </arch>
+
+ <cc>
+ gcc:4.9 # gcc 4.8 doesn't work for <reasons>
+ clang
+ </cc>
+
+ # Can work with POSIX+getentropy or Win32
+ <os_features>
+ posix1,getentropy
+ win32
+ </os_features>
+
+ <frameworks>
+ macos -> FramyMcFramerson
+ </frameworks>
+
+ <libs>
+ qnx -> foo,bar,baz
+ solaris -> socket
+ </libs>
+
+Supporting a new CPU type
+---------------------------
+
+CPU information is stored in ``src/build-data/arch``.
+
+There is also a file ``src/build-data/detect_arch.cpp`` which is used
+for build-time architecture detection using the compiler preprocessor.
+Supporting this is optional but recommended.
+
+Lists:
+ * ``aliases`` is a list of alternative names for the CPU architecture.
+ * ``isa_extensions`` is a list of possible ISA extensions that can be used on
+ this architecture. For example x86-64 has extensions "sse2", "ssse3",
+ "avx2", "aesni", ...
+
+Variables:
+ * ``endian`` if defined should be "little" or "big". This can also be
+ controlled or overridden at build time.
+ * ``family`` can specify a family group for several related architecture.
+ For example both x86_32 and x86_64 use ``family`` of "x86".
+ * ``wordsize`` is the default wordsize, which controls the size of limbs
+ in the multi precision integers. If not set, defaults to 32.
+
+Supporting a new compiler
+---------------------------
+
+Compiler information is stored in ``src/build-data/cc``. Looking over
+those files will probably help understanding, especially the ones for
+GCC and Clang which are most complete.
+
+In addition to the info file, for compilers there is a file
+``src/build-data/detect_version.cpp``. The ``configure.py`` script runs the
+preprocessor over this file to attempt to detect the compiler
+version. Supporting this is not strictly necessary.
+
+Maps:
+ * ``binary_link_commands`` gives the command to use to run the linker,
+ it maps from operating system name to the command to use. It uses
+ the entry "default" for any OS not otherwise listed.
+ * ``cpu_flags_no_debug`` unused, will be removed
+ * ``cpu_flags`` used to emit CPU specific flags, for example LLVM
+ bitcode target uses ``-emit-llvm`` flag. Rarely needed.
+ * ``isa_flags`` maps from CPU extensions (like NEON or AES-NI) to
+ compiler flags which enable that extension. These have the same name
+ as the ISA flags listed in the architecture files.
+ * ``lib_flags`` has a single possible entry "debug" which if set maps
+ to additional flags to pass when building a debug library.
+ Rarely needed.
+ * ``mach_abi_linking`` specifies flags to enable when building and
+ linking on a particular CPU. This is usually flags that modify
+ ABI. There is a special syntax supported here
+ "all!os1,arch1,os2,arch2" which allows setting ABI flags which are
+ used for all but the named operating systems and/or architectures.
+ * ``sanitizers`` is a map of sanitizers the compiler supports. It must
+ include "default" which is a list of sanitizers to include by default
+ when sanitizers are requested. The other keys should map to compiler
+ flags.
+ * ``so_link_commands`` maps from operating system to the command to
+ use to build a shared object.
+
+Variables:
+ * ``binary_name`` the default name of the compiler binary.
+ * ``linker_name`` the name of the linker to use with this compiler.
+ * ``macro_name`` a macro of the for ``BOTAN_BUILD_COMPILER_IS_XXX``
+ will be defined.
+ * ``output_to_object`` (default "-o") gives the compiler option used to
+ name the output object.
+ * ``output_to_exe`` (default "-o") gives the compiler option used to
+ name the output object.
+ * ``add_include_dir_option`` (default "-I") gives the compiler option used
+ to specify an additional include dir.
+ * ``add_lib_dir_option`` (default "-L") gives the compiler option used
+ to specify an additional library dir.
+ * ``add_sysroot_option`` gives the compiler option used to specify the sysroot.
+ * ``add_lib_option`` (default "-l%s") gives the compiler option to
+ link in a library. ``%s`` will be replaced with the library name.
+ * ``add_framework_option`` (default "-framework") gives the compiler option
+ to add a macOS framework.
+ * ``preproc_flags`` (default "-E") gives the compiler option used to run
+ the preprocessor.
+ * ``compile_flags`` (default "-c") gives the compiler option used to compile a file.
+ * ``debug_info_flags`` (default "-g") gives the compiler option used to enable debug info.
+ * ``optimization_flags`` gives the compiler optimization flags to use.
+ * ``size_optimization_flags`` gives compiler optimization flags to use when
+ compiling for size. If not set then ``--optimize-for-size`` will use
+ the default optimization flags.
+ * ``sanitizer_optimization_flags`` gives compiler optimization flags to use
+ when building with sanitizers.
+ * ``coverage_flags`` gives the compiler flags to use when generating coverage
+ information.
+ * ``stack_protector_flags`` gives compiler flags to enable stack overflow checking.
+ * ``shared_flags`` gives compiler flags to use when generation shared libraries.
+ * ``lang_flags`` gives compiler flags used to enable the required version of C++.
+ * ``warning_flags`` gives warning flags to enable.
+ * ``maintainer_warning_flags`` gives extra warning flags to enable during maintainer
+ mode builds.
+ * ``visibility_build_flags`` gives compiler flags to control symbol visibility
+ when generation shared libraries.
+ * ``visibility_attribute`` gives the attribute to use in the ``BOTAN_DLL`` macro
+ to specify visibility when generation shared libraries.
+ * ``ar_command`` gives the command to build static libraries
+ * ``ar_options`` gives the options to pass to ``ar_command``, if not set here
+ 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.
+
+Supporting a new OS
+---------------------------
+
+Operating system information is stored in ``src/build-data/os``.
+
+Lists:
+ * ``aliases`` is a list of alternative names which will be accepted
+ * ``target_features`` is a list of target specific OS features. Some of these
+ are supported by many OSes (for example "posix1") others are specific to
+ just one or two OSes (such as "getauxval"). Adding a value here causes a new
+ macro ``BOTAN_TARGET_OS_HAS_XXX`` to be defined at build time. Use
+ ``configure.py --list-os-features`` to list the currently defined OS
+ features.
+ * ``feature_macros`` is a list of macros to define.
+
+Variables:
+ * ``ar_command`` gives the command to build static libraries
+ * ``ar_options`` gives the options to pass to ``ar_command``
+ * ``ar_output_to`` gives the flag to pass to ``ar_command`` to specify where to
+ output the static library.
+ * ``bin_dir`` (default "bin") specifies where binaries should be installed,
+ relative to install_root.
+ * ``cli_exe_name`` (default "botan") specifies the name of the command line utility.
+ * ``default_compiler`` specifies the default compiler to use for this OS.
+ * ``doc_dir`` (default "doc") specifies where documentation should be installed,
+ relative to install_root
+ * ``header_dir`` (default "include") specifies where include files
+ should be installed, relative to install_root
+ * ``install_root`` (default "/usr/local") specifies where to install
+ by default.
+ * ``lib_dir`` (default "lib") specifies where library should be installed,
+ relative to install_root.
+ * ``lib_prefix`` (default "lib") prefix to add to the library name
+ * ``library_name``
+ * ``man_dir`` specifies where man files should be installed, relative to install_root
+ * ``obj_suffix`` (default "o") specifies the suffix used for object files
+ * ``program_suffix`` (default "") specifies the suffix used for executables
+ * ``shared_lib_symlinks`` (default "yes) specifies if symbolic names should be
+ created from the base and patch soname to the library name.
+ * ``soname_pattern_abi``
+ * ``soname_pattern_base``
+ * ``soname_pattern_patch``
+ * ``soname_suffix`` file extension to use for shared library if ``soname_pattern_base``
+ is not specified.
+ * ``static_suffix`` (default "a") file extension to use for static library.
+ * ``use_stack_protector`` (default "true") specify if by default stack smashing
+ protections should be enabled.
+ * ``uses_pkg_config`` (default "yes") specify if by default a pkg-config file
+ should be created.
diff --git a/comm/third_party/botan/doc/dev_ref/contents.rst b/comm/third_party/botan/doc/dev_ref/contents.rst
new file mode 100644
index 0000000000..6505762e1f
--- /dev/null
+++ b/comm/third_party/botan/doc/dev_ref/contents.rst
@@ -0,0 +1,20 @@
+Developer Reference
+=====================
+
+This section contains information useful to people making
+contributions to the library
+
+.. toctree::
+ :maxdepth: 1
+
+ contributing
+ configure
+ test_framework
+ continuous_integration
+ fuzzing
+ release_process
+ todo
+ os
+ oids
+ reading_list
+ mistakes
diff --git a/comm/third_party/botan/doc/dev_ref/continuous_integration.rst b/comm/third_party/botan/doc/dev_ref/continuous_integration.rst
new file mode 100644
index 0000000000..a109301e66
--- /dev/null
+++ b/comm/third_party/botan/doc/dev_ref/continuous_integration.rst
@@ -0,0 +1,75 @@
+Continuous Integration and Automated Testing
+===============================================
+
+CI Build Script
+----------------
+
+The Travis and AppVeyor builds are orchestrated using a script
+``src/scripts/ci_build.py``. This allows one to easily reproduce the CI process
+on a local machine.
+
+Travis CI
+-----------
+
+https://travis-ci.com/github/randombit/botan
+
+This is the primary CI, and tests the Linux, macOS, and iOS builds. Among other
+things it runs tests using valgrind, compilation on various architectures
+(currently including ARM, PPC64, and S390x), MinGW build, and a build that
+produces the coverage report.
+
+The Travis configurations is in ``src/scripts/ci/travis.yml``, which executes a
+setup script ``src/scripts/ci/setup_travis.sh`` to install needed packages.
+Then ``src/scripts/ci_build.py`` is invoked.
+
+AppVeyor
+----------
+
+https://ci.appveyor.com/project/randombit/botan
+
+Runs a build/test cycle using MSVC on Windows. Like Travis it uses
+``src/scripts/ci_build.py``. The AppVeyor setup script is in
+``src/scripts/ci/setup_appveyor.bat``
+
+The AppVeyor build uses `sccache <https://github.com/mozilla/sccache>`_ as a
+compiler cache. Since that is not available in the AppVeyor images, the setup
+script downloads a release binary from the upstream repository.
+
+LGTM
+---------
+
+https://lgtm.com/projects/g/randombit/botan/
+
+An automated linter that is integrated with Github. It automatically checks each
+incoming PR. It also supports custom queries/alerts, which likely would be worth
+investigating but is not something currently in use.
+
+Coverity
+---------
+
+https://scan.coverity.com/projects/624
+
+An automated source code scanner. Use of Coverity scanner is rate-limited,
+sometimes it is very slow to produce a new report, and occasionally the service
+goes offline for days or weeks at a time. New reports are kicked off manually by
+rebasing branch ``coverity_scan`` against the most recent master and force
+pushing it.
+
+Sonar
+-------
+
+https://sonarcloud.io/dashboard?id=botan
+
+Sonar scanner is another software quality scanner. Unfortunately a recent update
+of their scanner caused it to take over an hour to produce a report which caused
+Travis CI timeouts, so it has been disabled. It should be re-enabled to run on
+demand in the same way Coverity is.
+
+OSS-Fuzz
+----------
+
+https://github.com/google/oss-fuzz/
+
+OSS-Fuzz is a distributed fuzzer run by Google. Every night, each library fuzzers
+in ``src/fuzzer`` are built and run on many machines, with any findings reported
+to the developers via email.
diff --git a/comm/third_party/botan/doc/dev_ref/contributing.rst b/comm/third_party/botan/doc/dev_ref/contributing.rst
new file mode 100644
index 0000000000..f6effb3f36
--- /dev/null
+++ b/comm/third_party/botan/doc/dev_ref/contributing.rst
@@ -0,0 +1,268 @@
+
+Notes for New Contributors
+===================================
+
+Source Code Layout
+-------------------------------------------------
+
+Under ``src`` there are directories
+
+* ``lib`` is the library itself, more on that below
+* ``cli`` is the command line application ``botan``
+* ``tests`` contain what you would expect. Input files go under ``tests/data``.
+* ``build-data`` contains files read by the configure script. For
+ example ``build-data/cc/gcc.txt`` describes various gcc options.
+* ``scripts`` contains misc scripts: install, distribution, various
+ codegen things. Scripts controlling CI go under ``scripts/ci``.
+* ``configs`` contains configuration files for emacs, astyle, pylint, etc
+* ``python/botan2.py`` is the Python ctypes wrapper
+
+Library Layout
+----------------------------------------
+
+* ``base`` defines some high level types
+* ``utils`` contains various utility functions and types
+* ``codec`` has hex, base64
+* ``block`` contains the block cipher implementations
+* ``modes`` contains block cipher modes (CBC, GCM, etc)
+* ``stream`` contains the stream ciphers
+* ``hash`` contains the hash function implementations
+* ``passhash`` contains password hashing algorithms for authentication
+* ``kdf`` contains the key derivation functions
+* ``mac`` contains the message authentication codes
+* ``pbkdf`` contains password hashing algorithms for key derivation
+* ``math`` is the big integer math library. It is divided into three parts:
+ ``mp`` which are the low level algorithms; ``bigint`` which is a C++ wrapper
+ around ``mp``, and ``numbertheory`` which contains higher level algorithms like
+ primality testing and exponentiation
+* ``pubkey`` contains the public key algorithms
+* ``pk_pad`` contains padding schemes for public key algorithms
+* ``rng`` contains the random number generators
+* ``entropy`` has various entropy sources used by some of the RNGs
+* ``asn1`` is the DER encoder/decoder
+* ``x509`` is X.509 certificates, PKCS #10 requests, OCSP
+* ``tls`` contains the TLS implementation
+* ``filters`` is a filter/pipe API for data transforms
+* ``compression`` has the compression wrappers (zlib, bzip2, lzma)
+* ``ffi`` is the C99 API
+* ``prov`` contains bindings to external libraries like OpenSSL and PKCS #11
+* ``misc`` contains odds and ends: format preserving encryption, SRP, threshold
+ secret sharing, all or nothing transform, and others
+
+Sending patches
+----------------------------------------
+
+All contributions should be submitted as pull requests via GitHub
+(https://github.com/randombit/botan). If you are planning a large
+change email the mailing list or open a discussion ticket on github
+before starting out to make sure you are on the right path. And once
+you have something written, free to open a [WIP] PR for early review
+and comment.
+
+If possible please sign your git commits using a PGP key.
+See https://git-scm.com/book/en/v2/Git-Tools-Signing-Your-Work for
+instructions on how to set this up.
+
+Depending on what your change is, your PR should probably also include an update
+to ``news.rst`` with a note explaining the change. If your change is a
+simple bug fix, a one sentence description is perhaps sufficient. If there is an
+existing ticket on GitHub with discussion or other information, reference it in
+your change note as 'GH #000'.
+
+Update ``doc/credits.txt`` with your information so people know what you did!
+
+If you are interested in contributing but don't know where to start check out
+``doc/dev_ref/todo.rst`` for some ideas - these are changes we would almost
+certainly accept once they've passed code review.
+
+Also, try building and testing it on whatever hardware you have handy,
+especially unusual platforms, or using C++ compilers other than the regularly
+tested GCC, Clang, and Visual Studio.
+
+FFI Additions
+----------------
+
+If adding a new function declaration to ``ffi.h``, the same PR must
+also add the same declaration in the Python binding ``botan2.py``, in
+addition the new API functionality must be exposed to Python and a
+test written in Python.
+
+Git Usage
+----------------------------------------
+
+Do *NOT* merge ``master`` into your topic branch, this creates
+needless commits and noise in history. Instead, as needed, rebase your
+branch against master (``git rebase -i master``) and force push the
+branch to update the PR. If the GitHub PR page does not report any
+merge conflicts and nobody asks you to rebase, you don't need to
+rebase.
+
+Try to keep your history clean and use rebase to squash your commits
+as needed. If your diff is less than roughly 100 lines, it should
+probably be a single commit. Only split commits as needed to help with
+review/understanding of the change.
+
+Python
+----------------------------------------
+
+Scripts should be in Python whenever possible.
+
+For configure.py (and helper scripts install.py, cleanup.py and build_docs.py)
+the target is stock (no modules outside the standard library) CPython 2.7 plus
+latest CPython 3.x. Support for CPython 2.6, PyPy, etc is great when viable (in
+the sense of not causing problems for 2.7 or 3.x, and not requiring huge blocks
+of version dependent code). As running this program successfully is required for
+a working build, making it as portable as possible is considered key.
+
+The python wrapper botan2.py targets CPython 2.7, 3.x, and latest PyPy. Note that
+a single file is used to avoid dealing with any of Python's various crazy module
+distribution issues.
+
+For random scripts not typically run by an end-user (codegen, visualization, and
+so on) there isn't any need to worry about 2.6 and even just running under
+Python2 xor Python3 is acceptable if needed. Here it's fine to depend on any
+useful modules such as graphviz or matplotlib, regardless if it is available
+from a stock CPython install. Since Python2 is now end of life, prefer Python3
+for new scripts of this sort.
+
+Build Tools and Hints
+----------------------------------------
+
+If you don't already use it for all your C/C++ development, install ``ccache``
+(or on Windows, ``sccache``) right now, and configure a large cache on a fast
+disk. It allows for very quick rebuilds by caching the compiler output.
+
+Use ``--enable-sanitizers=`` flag to enable various sanitizer checks. Supported
+values including "address" and "undefined" for GCC and Clang. GCC also supports
+"iterator" (checked iterators), and Clang supports "memory" (MSan) and
+"coverage" (for fuzzing).
+
+On Linux if you have the ``lcov`` and ``gcov`` tools installed, then running
+``./src/scripts/ci_build.py coverage`` will produce a coverage enabled build,
+run the tests, test the fuzzers against a corpus, and produce an HTML report
+of total coverage. This coverage build requires the development headers for
+zlib, bzip2, liblzma, OpenSSL, TrouSerS (libtspi), and Sqlite3.
+
+Copyright Notice
+----------------------------------------
+
+At the top of any new file add a comment with a copyright and a reference to the
+license, for example::
+
+ /*
+ * (C) 20xx Copyright Holder
+ * Botan is released under the Simplified BSD License (see license.txt)
+ */
+
+If you are making a substantial or non-trivial change to an existing file, add
+or update your own copyright statement at the top of each file.
+
+Style Conventions
+----------------------------------------
+
+When writing your code remember the need for it to be easily understood by
+reviewers and auditors, both at the time of the patch submission and in the
+future.
+
+Avoid complicated template metaprogramming where possible. It has its places but
+should be used judiciously.
+
+When designing a new API (for use either by library users or just internally)
+try writing out the calling code first. That is, write out some code calling
+your idealized API, then just implement that API. This can often help avoid
+cut-and-paste by creating the correct abstractions needed to solve the problem
+at hand.
+
+The C++11 ``auto`` keyword is very convenient but only use it when the type
+truly is obvious (considering also the potential for unexpected integer
+conversions and the like, such as an apparent uint8_t being promoted to an int).
+
+If a variable is defined and not modified, declare it ``const``. Some exception
+for very short-lived variables, but generally speaking being able to read the
+declaration and know it will not be modified is useful.
+
+Use ``override`` annotations whenever overriding a virtual function. If
+introducing a new type that is not intended for derivation, mark it ``final``.
+
+Avoid explicit ``delete`` - use RAII.
+
+Use ``m_`` prefix on all member variables.
+
+For formatting, there are configs for emacs and astyle in ``src/configs``.
+No tabs, and remove trailing whitespace.
+
+Prefer using braces on both sides of if/else blocks, even if only using a single
+statement. The current code doesn't always do this.
+
+Avoid ``using namespace`` declarations, even inside of single functions. One
+allowed exception is ``using namespace std::placeholders`` in functions which
+use ``std::bind``. (But, don't use ``std::bind`` - use a lambda instead).
+
+Use ``::`` to explicitly refer to the global namespace (eg, when calling an OS
+or external library function like ``::select`` or ``::sqlite3_open``).
+
+Use of External Dependencies
+----------------------------------------
+
+Compiler Dependencies
+~~~~~~~~~~~~~~~~~~~~~~~
+
+The library should always be as functional as possible when compiled with just
+C++11. However, feel free to use the full C++11 language. No accomodations are
+made for compilers that are incomplete or buggy.
+
+Use of compiler extensions is fine whenever appropriate; this is typically
+restricted to a single file or an internal header. Compiler extensions used
+currently include native uint128_t, SIMD intrinsics, inline asm syntax and so
+on, so there are some existing examples of appropriate use.
+
+Generally intrinsics or inline asm is preferred over bare assembly to avoid
+calling convention issues among different platforms; the improvement in
+maintainability is seen as worth any potential performance tradeoff. One risk
+with intrinsics is that the compiler might rewrite your clever const-time SIMD
+into something with a conditional jump, but code intended to be const-time
+should in any case be annotated so it can be checked at runtime with tools.
+
+Operating System Dependencies
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If you're adding a small OS dependency in some larger piece of code, try to
+contain the actual non-portable operations to utils/os_utils.* and then call
+them from there.
+
+As a policy, operating systems which are not supported by their original vendor
+are not supported by Botan either. Patches that complicate the code in order to
+support obsolete operating systems will likely be rejected. In writing OS
+specific code, feel free to assume roughly POSIX 2008, or for Windows, Windows 8
+/Server 2012 (which are as of this writing the oldest versions still supported
+by Microsoft).
+
+Some operating systems, such as OpenBSD, only support the latest release. For
+such cases, it's acceptable to add code that requires APIs added in the most
+recent release of that OS as soon as the release is available.
+
+Library Dependencies
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Any external library dependency - even optional ones - is met with as one PR
+submitter put it "great skepticism".
+
+At every API boundary there is potential for confusion that does not exist when
+the call stack is all contained within the boundary. So the additional API
+really needs to pull its weight. For example a simple text parser or such which
+can be trivially implemented is not really for consideration. As a rough idea of
+the bar, equate the viewed cost of an external dependency as at least 1000
+additional lines of code in the library. That is, if the library really does
+need this functionality, and it can be done in the library for less than that,
+then it makes sense to just write the code. Yup.
+
+Currently the (optional) external dependencies of the library are OpenSSL (for
+access to fast and side channel hardened RSA, ECDSA, AES), some compression
+libraries (zlib, bzip2, lzma), sqlite3 database, Trousers (TPM integration),
+plus various operating system utilities like basic filesystem operations. These
+provide major pieces of functionality which seem worth the trouble of
+maintaining an integration with.
+
+At this point the most plausible examples of an appropriate new external
+dependency are all deeper integrations with system level cryptographic systems
+(CommonCrypto, CryptoAPI, /dev/crypto, iOS keychain, TPM 2.0, etc)
diff --git a/comm/third_party/botan/doc/dev_ref/fuzzing.rst b/comm/third_party/botan/doc/dev_ref/fuzzing.rst
new file mode 100644
index 0000000000..46e60fb533
--- /dev/null
+++ b/comm/third_party/botan/doc/dev_ref/fuzzing.rst
@@ -0,0 +1,91 @@
+Fuzzing The Library
+============================
+
+Botan comes with a set of fuzzing endpoints which can be used to test
+the library.
+
+.. highlight:: shell
+
+Fuzzing with libFuzzer
+------------------------
+
+To fuzz with libFuzzer (https://llvm.org/docs/LibFuzzer.html), you'll first
+need to compile libFuzzer::
+
+ $ svn co https://llvm.org/svn/llvm-project/compiler-rt/trunk/lib/fuzzer libFuzzer
+ $ cd libFuzzer && clang -c -g -O2 -std=c++11 *.cpp
+ $ ar cr libFuzzer.a libFuzzer/*.o
+
+Then build the fuzzers::
+
+ $ ./configure.py --cc=clang --build-fuzzer=libfuzzer --unsafe-fuzzer-mode \
+ --enable-sanitizers=coverage,address,undefined
+ $ make fuzzers
+
+Enabling 'coverage' sanitizer flags is required for libFuzzer to work.
+Address sanitizer and undefined sanitizer are optional.
+
+The fuzzer binaries will be in `build/fuzzer`. Simply pick one and run it, optionally
+also passing a directory containing corpus inputs.
+
+Using `libfuzzer` build mode implicitly assumes the fuzzers need to
+link with `libFuzzer`; if another library is needed (for example in
+OSS-Fuzz, which uses `libFuzzingEngine`), use the flag
+`--with-fuzzer-lib` to specify the desired name.
+
+Fuzzing with AFL
+--------------------
+
+To fuzz with AFL (http://lcamtuf.coredump.cx/afl/)::
+
+ $ ./configure.py --with-sanitizers --build-fuzzer=afl --unsafe-fuzzer-mode --cc-bin=afl-g++
+ $ make fuzzers
+
+For AFL sanitizers are optional. You can also use `afl-clang-fast++`
+or `afl-clang++`, be sure to set `--cc=clang` also.
+
+The fuzzer binaries will be in `build/fuzzer`. To run them you need to
+run under `afl-fuzz`::
+
+ $ afl-fuzz -i corpus_path -o output_path ./build/fuzzer/binary
+
+Fuzzing with TLS-Attacker
+--------------------------
+
+TLS-Attacker (https://github.com/RUB-NDS/TLS-Attacker) includes a mode for fuzzing
+TLS servers. A prebuilt copy of TLS-Attacker is available in a git repository::
+
+ $ git clone --depth 1 https://github.com/randombit/botan-ci-tools.git
+
+To run it against Botan's server::
+
+ $ ./configure.py --with-sanitizers
+ $ make botan
+ $ ./src/scripts/run_tls_attacker.py ./botan ./botan-ci-tools
+
+Output and logs from the fuzzer are placed into `/tmp`. See the
+TLS-Attacker documentation for more information about how to use this
+tool.
+
+Input Corpus
+-----------------------
+
+AFL requires an input corpus, and libFuzzer can certainly make good
+use of it.
+
+Some crypto corpus repositories include
+
+* 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
+---------------------
+
+New fuzzers are created by adding a source file to `src/fuzzers` which
+have the signature:
+
+``void fuzz(const uint8_t in[], size_t len)``
+
+After adding your fuzzer, rerun ``./configure.py`` and build.
diff --git a/comm/third_party/botan/doc/dev_ref/mistakes.rst b/comm/third_party/botan/doc/dev_ref/mistakes.rst
new file mode 100644
index 0000000000..6ea9ea927e
--- /dev/null
+++ b/comm/third_party/botan/doc/dev_ref/mistakes.rst
@@ -0,0 +1,77 @@
+
+Mistakes Were Made
+===================
+
+These are mistakes made early on in the project's history which are difficult to
+fix now, but mentioned in the hope they may serve as an example for others.
+
+C++ API
+---------
+
+As an implementation language, I still think C++ is the best choice (or at least
+the best choice available in early '00s) at offering good performance,
+reasonable abstractions, and low overhead. But the user API should have been
+pure C with opaque structs (rather like the FFI layer, which was added much
+later). Then an expressive C++ API could be built on top of the C API. This
+would have given us a stable ABI, allowed C applications to use the library, and
+(these days) make it easier to progressively rewrite the library in Rust.
+
+Public Algorithm Specific Classes
+------------------------------------
+
+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
+interfaces (here BlockCipher and HashFunction). This would substantially reduce
+the overall API and ABI surface.
+
+These interfaces are now deprecated, and perhaps will be able to be
+removed eventually.
+
+Header Directories
+-------------------
+
+It would have been better to install all headers as ``X/header.h``
+where ``X`` is the base dir in the source, eg ``block/aes128.h``,
+``hash/md5.h``, ...
+
+Exceptions
+-----------
+
+Constant ABI headaches from this, and it impacts performance and makes APIs
+harder to understand. Should have been handled with a result<> type instead.
+
+Virtual inheritance
+---------------------
+
+This was used in the public key interfaces and the hierarchy is a tangle.
+Public and private keys should be distinct classes, with a function on private
+keys that creates a new object corresponding to the public key.
+
+Cipher Interface
+------------------
+
+The cipher interface taking a secure_vector that it reads from and writes to was
+an artifact of an earlier design which supported both compression and encryption
+in a single API. But it leads to inefficient copies.
+
+(I am hoping this issue can be somewhat fixed by introducing a new cipher API
+and implementing the old API in terms of the new one.)
+
+Pipe Interface
+----------------
+
+On the surface this API seems very convenient and easy to use. And it is. But
+the downside is it makes the application code totally opaque; some bytes go into
+a Pipe object and then come out the end transformed in some way. What happens in
+between? Unless the Pipe was built in the same function and you can see the
+parameters to the constructor, there is no way to find out.
+
+The problems with the Pipe API are documented, and it is no longer used within
+the library itself. But since many people seem to like it and many applications
+use it, we are stuck at least with maintaining it as it currently exists.
+
+License
+---------
+
+MIT is more widely used and doesn't have the ambiguity surrounding the
+various flavors of BSD.
diff --git a/comm/third_party/botan/doc/dev_ref/oids.rst b/comm/third_party/botan/doc/dev_ref/oids.rst
new file mode 100644
index 0000000000..6aac1a5a72
--- /dev/null
+++ b/comm/third_party/botan/doc/dev_ref/oids.rst
@@ -0,0 +1,43 @@
+Private OID Assignments
+==========================
+
+The library uses some OIDs under a private arc assigned by IANA,
+1.3.6.1.4.1.25258
+
+Values currently assigned are::
+
+ randombit OBJECT IDENTIFIER ::= { 1 3 6 1 4 1 25258 }
+
+ publicKey OBJECT IDENTIFIER ::= { randombit 1 }
+
+ mceliece OBJECT IDENTIFIER ::= { publicKey 3 }
+ -- { publicKey 4 } previously used as private X25519
+ -- { publicKey 5 } previously used for XMSS draft 6
+ gost-3410-with-sha256 OBJECT IDENTIFIER ::= { publicKey 6 1 }
+ kyber OBJECT IDENTIFIER ::= { publicKey 7 }
+ xmss OBJECT IDENTIFIER ::= { publicKey 8 }
+
+ symmetricKey OBJECT IDENTIFIER ::= { randombit 3 }
+
+ ocbModes OBJECT IDENTIFIER ::= { symmetricKey 2 }
+
+ aes-128-ocb OBJECT IDENTIFIER ::= { ocbModes 1 }
+ aes-192-ocb OBJECT IDENTIFIER ::= { ocbModes 2 }
+ aes-256-ocb OBJECT IDENTIFIER ::= { ocbModes 3 }
+ serpent-256-ocb OBJECT IDENTIFIER ::= { ocbModes 4 }
+ twofish-256-ocb OBJECT IDENTIFIER ::= { ocbModes 5 }
+ camellia-128-ocb OBJECT IDENTIFIER ::= { ocbModes 6 }
+ camellia-192-ocb OBJECT IDENTIFIER ::= { ocbModes 7 }
+ camellia-256-ocb OBJECT IDENTIFIER ::= { ocbModes 8 }
+
+ sivModes OBJECT IDENTIFIER ::= { symmetricKey 4 }
+
+ aes-128-siv OBJECT IDENTIFIER ::= { sivModes 1 }
+ aes-192-siv OBJECT IDENTIFIER ::= { sivModes 2 }
+ aes-256-siv OBJECT IDENTIFIER ::= { sivModes 3 }
+ serpent-256-siv OBJECT IDENTIFIER ::= { sivModes 4 }
+ twofish-256-siv OBJECT IDENTIFIER ::= { sivModes 5 }
+ camellia-128-siv OBJECT IDENTIFIER ::= { sivModes 6 }
+ camellia-192-siv OBJECT IDENTIFIER ::= { sivModes 7 }
+ camellia-256-siv OBJECT IDENTIFIER ::= { sivModes 8 }
+ sm4-128-siv OBJECT IDENTIFIER ::= { sivModes 9 }
diff --git a/comm/third_party/botan/doc/dev_ref/os.rst b/comm/third_party/botan/doc/dev_ref/os.rst
new file mode 100644
index 0000000000..eee3e09b76
--- /dev/null
+++ b/comm/third_party/botan/doc/dev_ref/os.rst
@@ -0,0 +1,61 @@
+OS Features
+========================================
+
+A summary of OS features as defined in ``src/build-data/os``.
+
+::
+
+ a: aix
+ a: android
+ c: cygwin
+ d: dragonfly
+ e: emscripten
+ f: freebsd
+ h: haiku
+ h: hpux
+ h: hurd
+ i: includeos
+ i: ios
+ l: linux
+ l: llvm
+ m: macos
+ m: mingw
+ n: nacl
+ n: netbsd
+ o: openbsd
+ q: qnx
+ s: solaris
+ u: uwp
+ w: windows
+
+.. csv-table::
+ :header: "Feature", "a", "a", "c", "d", "e", "f", "h", "h", "h", "i", "i", "l", "l", "m", "m", "n", "n", "o", "q", "s", "u", "w"
+
+ "apple_keychain", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", "X", " ", " ", " ", " ", " ", " ", " ", " "
+ "arc4random", " ", "X", " ", "X", " ", "X", " ", " ", " ", " ", "X", " ", " ", "X", " ", " ", "X", "X", " ", " ", " ", " "
+ "cap_enter", " ", " ", " ", " ", " ", "X", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "
+ "certificate_store", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", "X"
+ "clock_gettime", "X", "X", " ", "X", " ", "X", "X", "X", "X", " ", " ", "X", " ", "X", " ", " ", "X", "X", "X", "X", " ", " "
+ "commoncrypto", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", "X", " ", " ", "X", " ", " ", " ", " ", " ", " ", " ", " "
+ "crypto_ng", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", "X", " "
+ "dev_random", "X", "X", "X", "X", "X", "X", "X", "X", "X", "X", " ", "X", " ", "X", " ", " ", "X", "X", "X", "X", " ", " "
+ "elf_aux_info", " ", " ", " ", " ", " ", "X", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "
+ "explicit_bzero", " ", " ", " ", " ", " ", "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"
+ "getauxval", " ", "X", " ", " ", " ", " ", " ", " ", " ", " ", " ", "X", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "
+ "getentropy", " ", " ", " ", " ", " ", "X", " ", " ", " ", " ", " ", " ", " ", "X", " ", " ", " ", "X", " ", " ", " ", " "
+ "pledge", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", "X", " ", " ", " ", " "
+ "posix1", "X", "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", " ", " "
+ "proc_fs", "X", " ", " ", "X", " ", " ", " ", " ", " ", " ", " ", "X", " ", " ", " ", " ", " ", " ", " ", "X", " ", " "
+ "rtlgenrandom", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", "X", " ", " ", " ", " ", " ", " ", "X"
+ "rtlsecurezeromemory", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", "X", "X"
+ "sockets", "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 --git a/comm/third_party/botan/doc/dev_ref/reading_list.rst b/comm/third_party/botan/doc/dev_ref/reading_list.rst
new file mode 100644
index 0000000000..1b27d05d69
--- /dev/null
+++ b/comm/third_party/botan/doc/dev_ref/reading_list.rst
@@ -0,0 +1,93 @@
+Reading List
+================
+
+These are papers, articles and books that are interesting or useful from the
+perspective of crypto implementation.
+
+Papers
+--------
+
+Implementation Techniques
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+* "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
+ 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
+ for aes_ssse3.
+
+* "Elliptic curves and their implementation" Langley
+ http://www.imperialviolet.org/2010/12/04/ecc.html
+ Describes sparse representations for ECC math
+
+Random Number Generation
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+* "On Extract-then-Expand Key Derivation Functions and an HMAC-based KDF"
+ Hugo Krawczyk http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.131.8254
+ RNG design underlying HMAC_RNG
+
+AES Side Channels
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+* "Software mitigations to hedge AES against cache-based software side
+ channel vulnerabilities" https://eprint.iacr.org/2006/052.pdf
+
+* "Cache Games - Bringing Access-Based Cache Attacks on AES to Practice"
+ http://www.ieee-security.org/TC/SP2011/PAPERS/2011/paper031.pdf
+
+* "Cache-Collision Timing Attacks Against AES" Bonneau, Mironov
+ http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.88.4753
+
+Public Key Side Channels
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+* "Fast Elliptic Curve Multiplications Resistant against Side Channel Attacks"
+ http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.98.1028&rep=rep1&type=pdf
+
+* "Resistance against Differential Power Analysis for Elliptic Curve Cryptosystems"
+ Coron http://www.jscoron.fr/publications/dpaecc.pdf
+
+* "Further Results and Considerations on Side Channel Attacks on RSA"
+ Klima, Rosa https://eprint.iacr.org/2002/071
+ Side channel attacks on RSA-KEM and MGF1-SHA1
+
+* "Side-Channel Attacks on the McEliece and Niederreiter Public-Key Cryptosystems"
+ Avanzi, Hoerder, Page, and Tunstall https://eprint.iacr.org/2010/479
+
+* "Minimum Requirements for Evaluating Side-Channel Attack Resistance
+ of Elliptic Curve Implementations" BSI
+ https://www.bsi.bund.de/SharedDocs/Downloads/DE/BSI/Zertifizierung/Interpretationen/AIS_46_ECCGuide_e_pdf.pdf
+
+Books
+------
+
+* "Handbook of Elliptic and Hyperelliptic Curve Cryptography"
+ Cohen and Frey https://www.hyperelliptic.org/HEHCC/
+ An excellent reference for ECC math, algorithms, and side channels
+
+* "Post-Quantum Cryptography" Bernstein, Buchmann, Dahmen
+ Covers code, lattice, and hash based cryptography
+
+Standards
+-----------
+
+* IEEE 1363 http://grouper.ieee.org/groups/1363/
+ Very influential early in the library lifetime, so a lot of terminology used
+ in the public key (such as "EME" for message encoding) code comes from here.
+
+* ISO/IEC 18033-2 http://www.shoup.net/iso/std4.pdf
+ RSA-KEM, PSEC-KEM
+
+* NIST SP 800-108
+ http://csrc.nist.gov/publications/nistpubs/800-108/sp800-108.pdf
+ KDF schemes
+
+* NIST SP 800-90A
+ http://csrc.nist.gov/publications/nistpubs/800-90A/SP800-90A.pdf
+ HMAC_DRBG, Hash_DRBG, CTR_DRBG, maybe one other thing?
+
diff --git a/comm/third_party/botan/doc/dev_ref/release_process.rst b/comm/third_party/botan/doc/dev_ref/release_process.rst
new file mode 100644
index 0000000000..1dd0842643
--- /dev/null
+++ b/comm/third_party/botan/doc/dev_ref/release_process.rst
@@ -0,0 +1,129 @@
+Release Process and Checklist
+========================================
+
+Releases are done quarterly, normally on the first non-holiday Monday
+of January, April, July and October. A feature freeze goes into effect
+starting 9 days before the release.
+
+.. highlight:: shell
+
+.. note::
+
+ This information is only useful if you are a developer of botan who
+ is creating a new release of the library.
+
+Pre Release Testing
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Kick off a Coverity scan a day or so before the planned release.
+
+Do maintainer-mode builds with Clang and GCC to catch any warnings
+that should be corrected. Also check Visual C++ build logs for any
+warnings that should be addressed.
+
+And remember that CI doesn't test everything. In particular, not all
+tests run under valgrind or on the qemu cross builds due to time
+constraints. So before release:
+
+ - Run under valgrind, building with ``--with-valgrind`` flag
+ - Using Clang sanitizers (ASan + UbSan)
+ - Native compile on FreeBSD x86-64
+ - Native compile on at least one unusual platform (AIX, NetBSD, ...)
+ - Build the website content to detect any Doxygen problems
+ - Test many build configurations (using `src/scripts/test_all_configs.py`)
+ - Build/test SoftHSM
+
+Confirm that the release notes in ``news.rst`` are accurate and
+complete and that the version number in ``version.txt`` is correct.
+
+Tag the Release
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Update the release date in the release notes and change the entry for
+the appropriate branch in ``readme.rst`` to point to the new release.
+
+Now check in, and backport changes to the release branch::
+
+ $ git commit readme.rst news.rst -m "Update for 2.6.13 release"
+ $ git checkout release-2
+ $ git merge master
+ $ git tag 2.6.13
+
+Build The Release Tarballs
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The release script is ``src/scripts/dist.py`` and must be run from a
+git workspace.
+
+ $ src/scripts/dist.py 2.6.13
+
+One useful option is ``--output-dir``, which specifies where the
+output will be placed.
+
+Now do a final build/test of the released tarball.
+
+The ``--pgp-key-id`` option is used to specify a PGP keyid. If set,
+the script assumes that it can execute GnuPG and will attempt to
+create signatures for the tarballs. The default value is ``EFBADFBC``,
+which is the official signing key. You can use ``--pgp-key-id=none``
+to avoid creating any signature, though official distributed releases
+*should not* be released without signatures.
+
+The releases served on the official site are taken from the contents
+in a git repository::
+
+ $ git checkout git@botan.randombit.net:/srv/git/botan-releases.git
+ $ src/scripts/dist.py 2.6.13 --output-dir=botan-releases
+ $ cd botan-releases
+ $ sha256sum Botan-2.6.13.tgz >> sha256sums.txt
+ $ git add .
+ $ git commit -m "Release version 2.6.13"
+ $ git push origin master
+
+A cron job updates the live site every 10 minutes.
+
+Push to GitHub
+^^^^^^^^^^^^^^^^^^
+
+Don't forget to also push tags::
+
+ $ git push origin --tags release-2 master
+
+Build The Windows Installer
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. note::
+ We haven't distributed Windows binaries for some time.
+
+On Windows, run ``configure.py`` to setup a build::
+
+ $ python ./configure.py --cc=msvc --cpu=$ARCH --distribution-info=unmodified
+
+After completing the build (and running the tests), use `InnoSetup
+<http://www.jrsoftware.org/isinfo.php>`_ to create the installer. A
+InnoSetup script is created from ``src/build-data/innosetup.in`` and
+placed in ``build/botan.iss`` by ``configure.py``. Create the
+installer either via the InnoSetup GUI by opening the ``iss`` file and
+selecting the 'Compile' option, or using the ``iscc`` command line
+tool. If all goes well it will produce an executable with a name like
+``botan-2.6.13-x86_64.exe``. Sign the installers with GPG.
+
+Update The Website
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The website content is created by ``src/scripts/website.py``.
+
+The website is mirrored automatically from a git repository which must be updated::
+
+ $ git checkout git@botan.randombit.net:/srv/git/botan-website.git
+ $ ./src/scripts/website.py --output botan-website
+ $ cd botan-website
+ $ git add .
+ $ git commit -m "Update for 2.6.13"
+ $ git push origin master
+
+Announce The Release
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Send an email to the botan-announce and botan-devel mailing lists
+noting that a new release is available.
diff --git a/comm/third_party/botan/doc/dev_ref/test_framework.rst b/comm/third_party/botan/doc/dev_ref/test_framework.rst
new file mode 100644
index 0000000000..241c51bd1a
--- /dev/null
+++ b/comm/third_party/botan/doc/dev_ref/test_framework.rst
@@ -0,0 +1,314 @@
+Test Framework
+================
+
+Botan uses a custom-built test framework. Some portions of it are
+quite similar to assertion-based test frameworks such as Catch or
+Gtest, but it also includes many features which are well suited for
+testing cryptographic algorithms.
+
+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
+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``.
+
+Namespaces in Test
+-------------------
+
+The test code lives in a distinct namespace (``Botan_Tests``) and all
+code in the tests which calls into the library should use the
+namespace prefix ``Botan::`` rather than a ``using namespace``
+declaration. This makes it easier to see where the test is actually
+invoking the library, and makes it easier to reuse test code for
+applications.
+
+Test Data
+-----------
+
+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
+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
+--------
+
+.. cpp:class:: Test
+
+ .. cpp:function:: virtual std::vector<Test::Result> run() = 0
+
+ This is the key function of a ``Test``: it executes and returns a
+ list of results. Almost all other functions on ``Test`` are
+ static functions which just serve as helper functions for ``run``.
+
+ .. cpp:function:: static std::string read_data_file(const std::string& path)
+
+ Return the contents of a data file and return it as a string.
+
+ .. cpp:function:: static std::vector<uint8_t> read_binary_data_file(const std::string& path)
+
+ Return the contents of a data file and return it as a vector of
+ bytes.
+
+ .. cpp:function:: static std::string data_file(const std::string& what)
+
+ An alternative to ``read_data_file`` and ``read_binary_file``,
+ use only as a last result, typically for library APIs which
+ themselves accept a filename rather than a data blob.
+
+ .. cpp:function:: static bool run_long_tests() const
+
+ Returns true if the user gave option ``--run-long-tests``. Use
+ this to gate particularly time-intensive tests.
+
+ .. cpp:function:: static Botan::RandomNumberGenerator& rng()
+
+ Returns a reference to a fast, not cryptographically secure
+ random number generator. It is deterministicly seeded with the
+ seed logged by the test runner, so it is possible to reproduce
+ results in "random" tests.
+
+Tests are registered using the macro ``BOTAN_REGISTER_TEST`` which
+takes 2 arguments: the name of the test and the name of the test class.
+For example given a ``Test`` instance named ``MyTest``, use::
+
+ BOTAN_REGISTER_TEST("mytest", MyTest);
+
+All test names should contain only lowercase letters, numbers, and
+underscore.
+
+Test::Result
+-------------
+
+.. cpp:class:: Test::Result
+
+ A ``Test::Result`` records one or more tests on a particular topic
+ (say "AES-128/CBC" or "ASN.1 date parsing"). Most of the test functions
+ 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))
+ {
+ // further tests that rely on the initial test being correct
+ }
+
+ Only the most commonly used functions on ``Test::Result`` are documented here,
+ see the header ``tests.h`` for more.
+
+ .. cpp:function:: Test::Result(const std::string& who)
+
+ Create a test report on a particular topic. This will be displayed in the
+ test results.
+
+ .. cpp:function:: bool test_success()
+
+ Report a test that was successful.
+
+ .. cpp:function:: bool test_success(const std::string& note)
+
+ Report a test that was successful, including some comment.
+
+ .. cpp:function:: bool test_failure(const std::string& 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)
+
+ 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)
+
+ 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
+ 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)
+
+ Compare to strings for equality.
+
+ .. cpp:function:: bool test_ne(const std::string& what, const std::string& produced, const std::string& expected)
+
+ Compare to 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)
+
+ 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)
+
+ Compare two arrays for non-equality.
+
+ .. cpp:function:: bool test_eq(const std::string& producer, const std::string& what, \
+ const std::vector<uint8_t>& produced, \
+ const std::vector<uint8_t>& expected)
+
+ Compare two vectors for equality.
+
+ .. cpp:function:: bool test_ne(const std::string& producer, const std::string& what, \
+ const std::vector<uint8_t>& produced, \
+ const std::vector<uint8_t>& expected)
+
+ Compare two vectors for non-equality.
+
+ .. cpp:function:: bool confirm(const std::string& what, bool expr)
+
+ Test that some expression evaluates to ``true``.
+
+ .. cpp:function:: template<typename T> bool test_not_null(const std::string& what, T* ptr)
+
+ Verify that the pointer is not null.
+
+ .. cpp:function:: bool test_lt(const std::string& 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)
+
+ Test that ``produced`` <= ``expected``.
+
+ .. cpp:function:: bool test_gt(const std::string& 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)
+
+ Test that ``produced`` >= ``expected``.
+
+ .. cpp:function:: bool test_throws(const std::string& what, std::function<void ()> 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<void ()> fn)
+
+ Call a function and verify it throws an exception of some kind
+ and that the exception message exactly equals ``expected``.
+
+Text_Based_Test
+-----------------
+
+A ``Text_Based_Text`` runs tests that are produced from a text file
+with a particular format which looks somewhat like an INI-file::
+
+ # Comments begin with # and continue to end of line
+ [Header]
+ # Test 1
+ Key1 = Value1
+ Key2 = Value2
+
+ # Test 2
+ Key1 = Value1
+ Key2 = Value2
+
+.. cpp:class:: VarMap
+
+ An object of this type is passed to each invocation of the text-based test.
+ It is used to access the test variables. All access takes a key, which is
+ one of the strings which was passed to the constructor of ``Text_Based_Text``.
+ Accesses are either required (``get_req_foo``), in which case an exception is
+ throwing if the key is not set, or optional (``get_opt_foo``) in which case
+ 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<uint8_t> get_req_bin(const std::string& key) const
+
+ Return a required binary string. The input is assumed to be hex encoded.
+
+ .. cpp:function:: std::vector<uint8_t> get_opt_bin(const std::string& key) const
+
+ Return an optional binary string. The input is assumed to be hex encoded.
+
+ .. cpp:function:: std::vector<std::vector<uint8_t>> get_req_bin_list(const std::string& key) const
+
+ .. cpp:function:: Botan::BigInt get_req_bn(const std::string& 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
+
+ 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
+
+ Return a required text string.
+
+ .. cpp:function:: std::string get_opt_str(const std::string& key, const std::string& def_value) const
+
+ Return an optional text string.
+
+ .. cpp:function:: size_t get_req_sz(const std::string& 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
+
+ Return an optional integer. The input should be decimal.
+
+.. cpp:class:: Text_Based_Test : public Test
+
+ .. cpp:function:: Text_Based_Test(const std::string& input_file, \
+ 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.
+ When this key is seen, ``run_one_test`` will be invoked. In the
+ test input file, this key must always appear least for any particular
+ test. All the other keys may appear in any order.
+
+ .. cpp:function:: Test::Result run_one_test(const std::string& header, \
+ const VarMap& vars)
+
+ Runs a single test and returns the result of it. The ``header``
+ parameter gives the value (if any) set in a ``[Header]`` block.
+ This can be useful to distinguish several types of tests within a
+ single file, for example "[Valid]" and "[Invalid]".
+
+ .. cpp:function:: bool clear_between_callbacks() const
+
+ By default this function returns ``false``. If it returns
+ ``true``, then when processing the data in the file, variables
+ are not cleared between tests. This can be useful when several
+ tests all use some common parameters.
+
+Test Runner
+-------------
+
+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 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.
+
+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
+useful when tracing a memory corruption bug.
diff --git a/comm/third_party/botan/doc/dev_ref/todo.rst b/comm/third_party/botan/doc/dev_ref/todo.rst
new file mode 100644
index 0000000000..891475caa9
--- /dev/null
+++ b/comm/third_party/botan/doc/dev_ref/todo.rst
@@ -0,0 +1,199 @@
+Todo List
+========================================
+
+Feel free to take one of these on if it interests you. Before starting
+out on something, send an email to the dev list or open a discussion
+ticket on GitHub to make sure you're on the right track.
+
+Request a new feature by opening a pull request to update this file.
+
+Ciphers, Hashes, PBKDF
+----------------------------------------
+
+* Stiched AES/GCM mode for CPUs supporting both AES and CLMUL
+* Combine AES-NI, ARMv8 and POWER AES implementations (as already done for CLMUL)
+* Vector permute AES only supports little-endian systems; fix for big-endian
+* SM4 using AES-NI (https://github.com/mjosaarinen/sm4ni) or vector permute
+* Poly1305 using AVX2
+* ChaCha using SSSE3
+* Skein-MAC
+* PMAC
+* SIV-PMAC
+* GCM-SIV (RFC 8452)
+* EME* tweakable block cipher (https://eprint.iacr.org/2004/125)
+* FFX format preserving encryption (NIST 800-38G)
+* SHA-512 using BMI2+AVX2
+* Constant time DES using bitslicing and/or BMI2
+* Threefish-1024
+* SIMD evaluation of SHA-2 and SHA-3 compression functions
+* Adiantum (https://eprint.iacr.org/2018/720)
+* CRC using clmul/pmull
+
+Public Key Crypto, Math
+----------------------------------------
+
+* Short vector optimization for BigInt
+* Abstract representation of ECC point elements to allow specific
+ implementations of the field arithmetic depending upon the curve.
+* Use NAF (joint sparse form) for ECC multi-exponentiation
+* Curves for pairings (BN-256, BLS12-381)
+* Identity based encryption
+* Paillier homomorphic cryptosystem
+* Socialist Millionaires Protocol (needed for OTRv3)
+* Hashing onto an elliptic curve (draft-irtf-cfrg-hash-to-curve)
+* New PAKEs (pending CFRG bakeoff results)
+* New post quantum schemes (pending NIST contest results)
+* SPHINX password store (https://eprint.iacr.org/2018/695)
+* X448 and Ed448
+* Use GLV decomposition to speed up secp256k1 operations
+
+Utility Functions
+------------------
+
+* Add a memory span type
+* Make Memory_Pool more concurrent (currently uses a global lock)
+* Guarded integer type to prevent overflow bugs
+* Add logging callbacks
+* Add latency tracing framework
+
+Multiparty Protocols
+----------------------
+
+* Distributed key generation for DL, RSA
+* Threshold signing, decryption
+
+External Providers, Hardware Support
+----------------------------------------
+
+* Add support ARMv8.4-A SHA-512, SHA-3, SM3 and RNG
+* Aarch64 inline asm for BigInt
+* Extend OpenSSL provider (DH, HMAC, CMAC, GCM)
+* Support using BoringSSL instead of OpenSSL or LibreSSL
+* /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
+* POWER8 SHA-2 extensions (GH #1486 + #1487)
+* Add support VPSUM on big-endian PPC64 (GH #2252)
+* Better TPM support: NVRAM, PCR measurements, sealing
+* Add support for TPM 2.0 hardware
+* Support Intel QuickAssist accelerator cards
+
+TLS
+----------------------------------------
+
+* Make DTLS support optional at build time
+* Improve/optimize DTLS defragmentation and retransmission
+* Implement logging callbacks for TLS
+* Make RSA optional at build time
+* Make finite field DH optional at build time
+* Authentication using TOFU (sqlite3 storage)
+* Certificate pinning (using TACK?)
+* Certificate Transparency extensions
+* TLS supplemental authorization data (RFC 4680, RFC 5878)
+* DTLS-SCTP (RFC 6083)
+
+PKIX
+----------------------------------------
+
+* Further tests of validation API (see GH #785)
+* Test suite for validation of 'real world' cert chains (GH #611)
+* Improve output of X509_Certificate::to_string
+ This is a free-form string for human consumption so the only constraints
+ are being informative and concise. (GH #656)
+* X.509 policy constraints
+* OCSP responder logic
+
+New Protocols / Formats
+----------------------------------------
+
+* ACME protocol
+* PKCS7 / Cryptographic Message Syntax
+* PKCS12 / PFX
+* Off-The-Record v3 https://otr.cypherpunks.ca/
+* Certificate Management Protocol (RFC 5273); requires CMS
+* Fernet symmetric encryption (https://cryptography.io/en/latest/fernet/)
+* RNCryptor format (https://github.com/RNCryptor/RNCryptor)
+* 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
+ simple to process for decryption. Require AEAD mode (EAX/OCB).
+* Useful OpenPGP subset 2: Process OpenPGP public keys
+* Useful OpenPGP subset 3: Verification of OpenPGP signatures
+
+Cleanups
+-----------
+
+* Split test_ffi.cpp into multiple files
+* Unicode path support on Windows (GH #1615)
+* The X.509 path validation tests have much duplicated logic
+
+Compat Headers
+----------------
+
+* OpenSSL compatible API headers: EVP, TLS, certificates, etc
+
+New C APIs
+----------------------------------------
+
+* PKCS10 requests
+* Certificate signing
+* Expose TLS
+* Expose NIST key wrap with padding
+* Expose secret sharing
+* Expose deterministic PRNG
+* base32
+* base58
+* DL_Group
+* EC_Group
+
+Python
+----------------
+
+* Anywhere Pylint warnings too-many-locals, too-many-branches, or
+ too-many-statements are skipped, fix the code so Pylint no longer warns.
+
+* Write a CLI or HTTPS client in Python
+
+Build/Test
+----------------------------------------
+
+* Start using GitHub Actions for CI, especially Windows builds
+* Create Docker image for Travis that runs 18.04 and has all
+ the tools we need pre-installed.
+* Code signing for Windows installers
+* Test runner python script that captures backtraces and other
+ debug info during CI
+* Support hardcoding all test vectors into the botan-test binary
+ 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
+* Run the TPM tests against an emulator
+ (https://github.com/PeterHuewe/tpm-emulator)
+* Add clang-tidy, clang-analyzer, cppcheck to CI
+* Add support for vxWorks
+* Add support for Fuschia OS
+* Add support for CloudABI
+* Add support for SGX
+
+CLI
+----------------------------------------
+
+* Add a ``--completion`` option to dump autocomplete info, write
+ support for autocompletion in bash/zsh.
+* Refactor ``speed``
+* Change `tls_server` to be a tty<->socket app, like `tls_client` is,
+ instead of a bogus echo server.
+* `encrypt` / `decrypt` tools providing password based file encryption
+* Add ECM factoring
+* Clone of `minisign` signature utility
+* Implementation of `tlsdate`
+* Password store utility
+* TOTP calculator
+
+Documentation
+----------------------------------------
+
+* X.509 certs, path validation
+* Specific docs covering one major topic (RSA, ECDSA, AES/GCM, ...)
+* Some howto style docs (setting up CA, ...)
diff --git a/comm/third_party/botan/doc/goals.rst b/comm/third_party/botan/doc/goals.rst
new file mode 100644
index 0000000000..840e1bd818
--- /dev/null
+++ b/comm/third_party/botan/doc/goals.rst
@@ -0,0 +1,131 @@
+
+Project Goals
+================================
+
+Botan seeks to be a broadly applicable library that can be used to implement a
+range of secure distributed systems.
+
+The library has the following project goals guiding changes. It does not succeed
+in all of these areas in every way just yet, but it describes the system that is
+the desired end result. Over time further progress is made in each.
+
+* Secure and reliable. The implementations must of course be correct and well
+ tested, and attacks such as side channels and fault attacks should be
+ accounted for where necessary. The library should never crash, or invoke
+ undefined behavior, regardless of circumstances.
+
+* 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
+ the set of applications which can easily adopt Botan.
+
+* Ease of use. It should be straightforward for an application programmer to do
+ whatever it is they need to do. There should be one obvious way to perform any
+ operation. The API should be predicable, and follow the "principle of least
+ astonishment" in its design. This is not just a nicety; confusing APIs often
+ result in errors that end up compromising security.
+
+* Simplicity of design, clarity of code, ease of review. The code should be easy
+ to read and understand by other library developers, users seeking to better
+ understand the behavior of the code, and by professional reviewers looking for
+ bugs. This is important because bugs in convoluted code can easily escape
+ multiple expert reviews, and end up living on for years.
+
+* Well tested. The code should be correct against the spec, with as close to
+ 100% test coverage as possible. All available static and dynamic analysis
+ tools at our disposal should be used, including fuzzers, symbolic execution,
+ and protocol specific tools. Within reason, all warnings from compilers and
+ static analyzers should be addressed, even if they seem like false positives,
+ because that maximizes the signal value of new warnings from the tool.
+
+* Safe defaults. Policies should aim to be highly restrictive by default, and if
+ they must be made less restrictive by certain applications, it should be
+ obvious to the developer that they are doing something unsafe.
+
+* Post quantum security. Possibly a practical quantum computer that can break
+ RSA and ECC will never be built, but the future is notoriously hard to predict.
+ It seems prudent to begin designing and deploying systems now which have at
+ 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.
+
+* 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
+ asynchronous operations easy to handle. This both insulates Botan from
+ system-specific details and allows the application to use whatever networking
+ style they please.
+
+* Portability to modern systems. Botan does not run everywhere, and we actually
+ do not want it to (see non-goals below). But we do want it to run on anything
+ that someone is deploying new applications on. That includes both major
+ platforms like Windows, Linux, Android and iOS, and also promising new systems
+ such as IncludeOS and Fuchsia.
+
+* Well documented. Ideally every public API would have some place in the manual
+ describing its usage.
+
+* Useful command line utility. The botan command line tool should be flexible
+ and featured enough to replace similar tools such as ``openssl`` for everyday
+ users.
+
+Non-Goals
+-------------------------
+
+There are goals some crypto libraries have, but which Botan actively does not
+seek to address.
+
+* Deep embedded support. Botan requires a heap, C++ exceptions, and RTTI, and at
+ least in terms of performance optimizations effectively assumes a 32 or 64 bit
+ processor. It is not suitable for deploying on, say FreeRTOS running on a
+ MSP430, or smartcard with an 8 bit CPU and 256 bytes RAM. A larger SoC, such
+ as a Cortex-A7 running Linux, is entirely within scope.
+
+* Implementing every crypto scheme in existence. The focus is on algorithms
+ which are in practical use in systems deployed now, as well as promising
+ algorithms for future deployment. Many algorithms which were of interest
+ in the past but never saw widespread deployment and have no compelling
+ benefit over other designs have been removed to simplify the codebase.
+
+* Portable to obsolete systems. There is no reason for crypto software to
+ support ancient OS platforms like SunOS or Windows 2000, since these unpatched
+ systems are completely unsafe anyway. The additional complexity supporting
+ such platforms just creates more room for bugs.
+
+* Portable to every C++ compiler ever made. Over time Botan moves forward to
+ both take advantage of new language/compiler features, and to shed workarounds
+ for dealing with bugs in ancient compilers, allowing further simplifications
+ in the codebase. The set of supported compilers is fixed for each new release
+ branch, for example Botan 2.x will always support GCC 4.8. But a future 3.x
+ release version will likely increase the required versions for all compilers.
+
+* FIPS 140 validation. The primary developer was (long ago) a consultant with a
+ NIST approved testing lab. He does not have a positive view of the process or
+ results, particularly when it comes to Level 1 software validations. The only
+ benefit of a Level 1 validation is to allow for government sales, and the cost
+ of validation includes enormous amounts of time and money, adding 'checks'
+ that are useless or actively harmful, then freezing the software so security
+ updates cannot be applied in the future. It does force a certain minimum
+ standard (ie, FIPS Level 1 does assure AES and RSA are probably implemented
+ correctly) but this is an issue of interop not security since Level 1 does not
+ seriously consider attacks of any kind. Any security budget would be far
+ better spent on a review from a specialized crypto consultancy, who would look
+ for actual flaws.
+
+ That said it would be easy to add a "FIPS 140" build mode to Botan, which just
+ disabled all the builtin crypto and wrapped whatever the most recent OpenSSL
+ FIPS module exports.
+
+* Educational purposes. The library code is intended to be easy to read and
+ review, and so might be useful in an educational context. However it does not
+ contain any toy ciphers (unless you count DES and RC4) nor any tools for
+ simple cryptanalysis. Generally the manual and source comments assume previous
+ knowledge on the basic concepts involved.
+
+* User proof. Some libraries provide a very high level API in an attempt to save
+ the user from themselves. Occasionally they succeed. It would be appropriate
+ and useful to build such an API on top of Botan, but Botan itself wants to
+ cover a broad set of uses cases and some of these involve having pointy things
+ within reach.
diff --git a/comm/third_party/botan/doc/index.rst b/comm/third_party/botan/doc/index.rst
new file mode 100644
index 0000000000..50808c2714
--- /dev/null
+++ b/comm/third_party/botan/doc/index.rst
@@ -0,0 +1,58 @@
+
+Getting Started
+========================================
+
+If you need to build the library first, start with :doc:`building`.
+Some Linux distributions include packages for Botan, so building from
+source may not be required on your system.
+
+.. only:: html
+
+ The :ref:`genindex` and :ref:`search` may be useful to get started.
+
+.. only:: html and website
+
+ You can also download this manual as a `PDF <https://botan.randombit.net/handbook/botan.pdf>`_.
+
+Examples
+----------
+
+Some examples of usage are included in this documentation. However a better
+source for example code is in the implementation of the
+`command line interface <https://github.com/randombit/botan/tree/master/src/cli>`_,
+which was intentionally written to act as practical examples of usage.
+
+Books and other references
+----------------------------
+
+You should have some knowledge of cryptography *before* trying to use
+the library. This is an area where it is very easy to make mistakes,
+and where things are often subtle and/or counterintuitive. Obviously
+the library tries to provide things at a high level precisely to
+minimize the number of ways things can go wrong, but naive use will
+almost certainly not result in a secure system.
+
+Especially recommended are:
+
+- *Cryptography Engineering*
+ by Niels Ferguson, Bruce Schneier, and Tadayoshi Kohno
+
+- `Security Engineering -- A Guide to Building Dependable Distributed Systems
+ <https://www.cl.cam.ac.uk/~rja14/book.html>`_ by Ross Anderson
+
+- `Handbook of Applied Cryptography <http://www.cacr.math.uwaterloo.ca/hac/>`_
+ by Alfred J. Menezes, Paul C. Van Oorschot, and Scott A. Vanstone
+
+If you're doing something non-trivial or unique, you might want to at
+the very least ask for review/input at a place such as the
+`metzdowd <http://www.metzdowd.com/mailman/listinfo/cryptography>`_ or
+`randombit <https://lists.randombit.net/mailman/listinfo/cryptography>`_
+mailing lists or the
+`cryptography stack exchange <https://crypto.stackexchange.com/>`_.
+And (if possible) pay a professional cryptographer or security company
+to review your design and code.
+
+
+.. toctree::
+ :maxdepth: 1
+ :numbered:
diff --git a/comm/third_party/botan/doc/old_news.rst b/comm/third_party/botan/doc/old_news.rst
new file mode 100644
index 0000000000..161bf487bc
--- /dev/null
+++ b/comm/third_party/botan/doc/old_news.rst
@@ -0,0 +1,4336 @@
+Release Notes: 0.7.0 to 1.11.34
+========================================
+
+Version 1.10.17, 2017-10-02
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Address a side channel affecting modular exponentiation. An attacker
+ capable of a local or cross-VM cache analysis attack may be able
+ to recover bits of secret exponents as used in RSA, DH, etc.
+ CVE-2017-14737
+
+* Workaround a miscompilation bug in GCC 7 on x86-32 affecting GOST-34.11
+ hash function. (GH #1192 #1148 #882)
+
+* Add SecureVector::data() function which returns the start of the
+ buffer. This makes it slightly simpler to support both 1.10 and 2.x
+ APIs in the same codebase.
+
+* When compiled by a C++11 (or later) compiler, a template typedef of
+ SecureVector, secure_vector, is added. In 2.x this class is a
+ std::vector with a custom allocator, so has a somewhat different
+ interface than SecureVector in 1.10. But this makes it slightly
+ simpler to support both 1.10 and 2.x APIs in the same codebase.
+
+* Fix a bug that prevented `configure.py` from running under Python3
+
+* Botan 1.10.x does not support the OpenSSL 1.1 API. Now the build
+ will `#error` if OpenSSL 1.1 is detected. Avoid `--with-openssl`
+ if compiling against 1.1 or later. (GH #753)
+
+* Import patches from Debian adding basic support for building on
+ aarch64, ppc64le, or1k, and mipsn32 platforms.
+
+Version 1.10.16, 2017-04-04
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Fix a bug in X509 DN string comparisons that could result in out of bound
+ reads. This could result in information leakage, denial of service, or
+ potentially incorrect certificate validation results. (CVE-2017-2801)
+
+* Avoid throwing during a destructor since this is undefined in C++11
+ and rarely a good idea. (GH #930)
+
+Version 1.10.15, 2017-01-12
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Fix a bug causing modular exponentiations done modulo even numbers
+ to almost always be incorrect, unless the values were small. This
+ bug is not known to affect any cryptographic operation in Botan. (GH #754)
+
+* Avoid use of C++11 std::to_string in some code added in 1.10.14 (GH #747 #834)
+
+Version 1.11.34, 2016-11-28
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Fix integer overflow during BER decoding, found by Falko Strenzke.
+ This bug is not thought to be directly exploitable but upgrading ASAP
+ is advised. (CVE-2016-9132)
+
+* Add post-quantum signature scheme XMSS. Provides either 128 or 256 bit
+ (post-quantum) security, with small public and private keys, fast
+ verification, and reasonably small signatures (2500 bytes for 128-bit
+ security). Signature generation is very slow, on the order of seconds. And
+ very importantly the signature scheme is stateful: each leaf index must only
+ be used once, or all security is lost. In the appropriate system where
+ signatures are rarely generated (such as code signing) XMSS makes an excellent
+ choice. (GH #717 #736)
+
+* Add support for CECPQ1 TLS ciphersuites. These use a combination of x25519
+ ECDH and NewHope to provide post-quantum security. The ciphersuites are not
+ IETF standard, but is compatible with BoringSSL. (GH #729)
+
+* Add support for client-side OCSP stapling to TLS. (GH #738)
+
+* Previously both public and private keys performed automatic self testing after
+ generation or loading. However this often caused unexpected application
+ performance problems, and so has been removed. Instead applications must call
+ check_key explicitly. (GH #704)
+
+* 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
+ output lengths.
+
+* Add MessageAuthenticationCode::start_msg interface, for MACs which require or
+ can use a nonce (GH #691)
+
+* Add GMAC, a MAC based on GCM (GH #488 / #691)
+
+* Add ESP block cipher padding from RFC 4304. GH #724
+
+* Incompatible change to HKDF: previously the HKDF type in Botan was only the
+ Expand half of HKDF. Now HKDF is the full Extract-then-Expand KDF, and
+ HKDF_Extract and HKDF_Expand are available. If you previously used HKDF, you
+ must switch to using HKDF_Expand. (GH #723)
+
+* Add Cipher_Mode::reset which resets message-specific state, allowing
+ discarding state but allowing continued processing under the same key. (GH #552)
+
+* The ability to add OIDs at runtime has been removed. This additionally removes
+ a global lock which was acquired on each OID lookup. (GH #706)
+
+* The default TLS policy now disables static RSA ciphersuites, all DSA
+ ciphersuites, and the AES CCM-8 ciphersuites. Disabling static RSA by default
+ protects servers from oracle attacks, as well as enforcing a forward secure
+ ciphersuite. Some applications may be forced to re-enable RSA for interop
+ reasons. DSA and CCM-8 are rarely used, and likely should not be negotiated
+ outside of special circumstances.
+
+* The default TLS policy now prefers ChaCha20Poly1305 cipher over any AES mode.
+
+* The default TLS policy now orders ECC curve preferences in order by performance,
+ with x25519 first, then P-256, then P-521, then the rest.
+
+* Add a BSD sockets version of the HTTP client code used for OCSP. GH #699
+
+* Export the public key workfactor functions (GH #734) and add tests for them.
+
+* HMAC_DRBG allows configuring maximum number of bytes before reseed check (GH #690)
+
+* Salsa20 now accepts a null IV as equivalent to an all-zero one (GH #697)
+
+* Optimize ECKCDSA verification (GH #700 #701 #702)
+
+* The deprecated RNGs HMAC_RNG and X9.31 RNG have been removed. Now the only
+ userspace PRNG included in the library is HMAC_DRBG. (GH #692)
+
+* The entropy sources for EGD and BeOS, as well as the Unix entropy source which
+ executed processes to get statistical data have been removed. (GH #692)
+
+* The openpgp module (which just implemented OpenPGP compatible base64 encoding
+ and decoding, nothing else) has been removed.
+
+* Added new configure.py argument `--optimize-for-size`. Currently just sets
+ the flag for code size optimizations with the compiler, but may have other
+ effects in the future.
+
+* Fixed bug in Threaded_Fork causing incorrect computations (GH #695 #716)
+
+* Add DSA deterministic parameter generation test from FIPS 186-3.
+
+* Fix PKCS11_ECDSA_PrivateKey::check_key (GH #712)
+
+* Fixed problems running configure.py outside of the base directory
+
+* The BOTAN_ENTROPY_PROC_FS_PATH value in build.h was being ignored (GH #708)
+
+* Add speed tests for ECGDSA and ECKCDSA (GH #696)
+
+* Fix a crash in speed command for Salsa20 (GH #697)
+
+* Allow a custom ECC curve to be specified at build time, for application or
+ system specific curves. (GH #636 #710)
+
+* Use NOMINMAX on Windows to avoid problems in amalgamation build. (GH #740)
+
+* Add support to output bakefiles with new `configure.py` option `--with-bakefile`.
+ (GH #360 #720)
+
+* The function `zero_mem` has been renamed `secure_scrub_memory`
+
+* More tests for pipe/filter (GH #689 #693), AEADs (GH #552), KDF::name (GH #727),
+
+* Add a test suite for timing analysis for TLS CBC decryption, OAEP decryption,
+ and PKCS #1 v1.5 decryption. These operations all have the feature that if an
+ attacker can distinguish internal operations, such as through a variance in
+ timing, they can use this oracle to decrypt arbitrary ciphertexts. GH #733
+
+* Add a test suite for testing and fuzzing with TLS-Attacker, a tool for
+ analyzing TLS libraries. (https://github.com/RUB-NDS/TLS-Attacker)
+
+* Add a fuzzing framework. Supports fuzzing some APIs using AFL and libFuzzer.
+
+* Added documentation for PKCS #11 (GH #725)
+
+* The LibraryInitializer type is no longer needed and is now deprecated.
+
+* The license and news files were moved from doc to the top level directory.
+ There should not be any other visible change (eg, to the installed version)
+ as a result of this move.
+
+* Fixed some problems when running configure.py outside of the base directory,
+ especially when using relative paths.
+
+* Add (back) the Perl XS wrapper and sqlite encryption code.
+
+Version 1.10.14, 2016-11-28
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* NOTE WELL: Botan 1.10.x is supported for security patches only until
+ 2017-12-31
+
+* Fix integer overflow during BER decoding, found by Falko Strenzke.
+ This bug is not thought to be directly exploitable but upgrading ASAP
+ is advised. (CVE-2016-9132)
+
+* Fix two cases where (in error situations) an exception would be
+ thrown from a destructor, causing a call to std::terminate.
+
+* When RC4 is disabled in the build, also prevent it from being
+ included in the OpenSSL provider. (GH #638)
+
+Version 1.11.33, 2016-10-26
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Avoid side channel during OAEP decryption. (CVE-2016-8871)
+
+* A countermeasure for the Lucky13 timing attack against CBC-based TLS
+ ciphersuites has been added. (GH #675)
+
+* Added X25519-based key exchange for TLS (GH #673)
+
+* Add Certificate_Store_In_SQL which supports storing certs, keys, and
+ revocation information in a SQL database. Subclass Certificate_Store_In_SQLite
+ specializes with support for SQLite3 databases. (GH #631)
+
+* The Certificate_Store interface has been changed to deal with
+ ``std::shared_ptrs`` instead of raw pointers (GH #471 #631)
+
+* Add support for official SHA-3. Keccak-1600 was already supported
+ but used different padding from FIPS 202. (GH #669)
+
+* Add SHAKE-128 based stream cipher. (GH #669)
+
+* NewHope key exchange now supports the SHA-256/AES-128-CTR scheme
+ used by BoringSSL in addition to the SHA-3/SHAKE-128 parameters used
+ by the reference implementation. (GH #669)
+
+* Add support for the TLS Supported Point Formats Extension from RFC 4492. Adds
+ ``TLS::Policy::use_ecc_point_compression`` policy option. If supported on both
+ sides, ECC points can be sent in compressed format which saves a few bytes
+ during the handshake. (GH #645)
+
+* Fix entropy source selection bug on Windows, which caused the CryptoAPI
+ entropy source to be not available under its normal name "win32_cryptoapi" but
+ instead "dev_random". GH #644
+
+* Accept read-only access to ``/dev/urandom``. System_RNG previously required
+ read-write access, to allow applications to provide inputs to the system
+ PRNG. But local security policies might only allow read-only access, as is the
+ case with Ubuntu's AppArmor profile for applications in the Snappy binary
+ format. If opening read/write fails, System_RNG silently backs down to
+ read-only, in which case calls to ``add_entropy`` on that object will fail.
+ (GH #647 #648)
+
+* Fix use of Win32 CryptoAPI RNG as an entropy source, which was accidentally
+ disabled due to empty list of acceptable providers being specified. Typically
+ the library would fall back to gathering entropy from OS functions returning
+ statistical information, but if this functionality was disabled in the build a
+ ``PRNG_Unseeded`` exception would result. (GH #655)
+
+* Add support for building the library as part of the IncludeOS unikernel.
+ This included making filesystem and threading support optional. (GH #665)
+
+* Added ISA annotations so that with GCC (all supported versions) and
+ Clang (since 3.7) it is no longer required to compile amalgamation
+ files with ABI specific flags such as ``-maes``. (GH #665)
+
+* Internal cleanups to TLS CBC record handling. TLS CBC ciphersuites
+ can now be disabled by disabling ``tls_cbc`` module. (GH #642 #659)
+
+* Internal cleanups to the object lookup code eliminates most global locks and
+ all use of static initializers (GH #668 #465)
+
+* Avoid ``static_assert`` triggering under MSVC debug builds (GH #646)
+
+* The antique PBKDF1 password hashing scheme is deprecated and will be
+ removed in a future release. It was only used to support the equally
+ ancient PBES1 private key encryption scheme, which was removed in 1.11.8.
+
+* Added MSVC debug/checked iterator builds (GH #666 #667)
+
+* Added Linux ppc64le cross compile target to Travis CI (GH #654)
+
+* If RC4 is disabled, also disable it coming from the OpenSSL provider (GH #641)
+
+* Add TLS message parsing tests (GH #640)
+
+* Updated BSI policy to prohibit DES, HKDF, HMAC_RNG (GH #649)
+
+* Documentation improvements (GH #660 #662 #663 #670)
+
+Version 1.11.32, 2016-09-28
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Add support for the NewHope Ring-LWE key encapsulation algorithm. This scheme
+ provides an estimated ~200 bit security level against a quantum attacker while
+ also being very fast and requiring only modest message sizes of 1824 and 2048
+ bytes for initiator and responder, resp. This version is tested as having
+ bit-for-bit identical output as the reference implementation by the authors.
+
+ Be warned that NewHope is still a very new scheme and may yet fall to analysis.
+ For best assurance, NewHope should be used only in combination with another
+ key exchange mechanism, such as ECDH.
+
+* New TLS callbacks API. Instead of numerous std::function callbacks, the
+ application passes an object implementing the TLS::Callbacks interface, which
+ has virtual functions matching the previous callbacks (plus some extras).
+ Full source compatability with previous versions is maintained for now, but
+ the old interface is deprecated and will be removed in a future release. The
+ manual has been updated to reflect the changes. (GH #457 and #567)
+
+* Add support for TLS Encrypt-then-MAC extension (GH #492 and #578), which fixes
+ the known issues in the TLS CBC-HMAC construction.
+
+* The format of the TLS session struct has changed (to support EtM), so old
+ TLS session caches will be invalidated.
+
+* How the library presents optimized algorithm implementations has changed. For
+ example with the algorithm AES-128, previously there were three BlockCipher
+ classes AES_128, AES_128_SSSE3, and AES_128_NI which used (resp) a table-based
+ implementation vulnerable to side channels, a constant time version using
+ SSSE3 SIMD extensions on modern x86, and x86 AES-NI instructions. Using the
+ correct version at runtime required using ``BlockCipher::create``. Now, only
+ the class AES_128 is presented, and the best available version is always used
+ based on CPUID checks. The tests have been extended to selectively disable
+ CPUID bits to ensure all available versions are tested. (GH #477 #623)
+
+ Removes API classes AES_128_NI, AES_192_NI, AES_256_NI, AES_128_SSSE3,
+ AES_192_SSSE3 AES_256_SSSE3, IDEA_SSE2, Noekeon_SIMD, Serpent_SIMD,
+ Threefish_512_AVX2, SHA_160_SSE2
+
+* The deprecated algorithms Rabin-Williams, Nyberg-Rueppel, MARS, RC2, RC5, RC6,
+ SAFER-SK, TEA, MD2, HAS-160, and RIPEMD-128 have been removed. (GH #580)
+
+* A new Cipher_Mode interface ``process`` allows encryption/decryption of
+ buffers without requiring copying into ``secure_vector`` first. (GH #516)
+
+* Fix verification of self-issued certificates (GH #634)
+
+* SSE2 optimizations for ChaCha, 60% faster on both Westmere and Skylake (GH #616)
+
+* The HMAC_RNG constructor added in 1.11.31 that took both an RNG and an
+ entropy source list ignored the entropy sources.
+
+* The configure option ``--via-amalgamation`` was renamed to ``--amalgamation``.
+ The configure option ``--gen-amalgamation`` was removed. It did generate
+ amalgamations but build Botan without amalgamation. Users should migrate to
+ ``--amalgamation``. (GH #621)
+
+* DH keys did not automatically self-test after being generated, contrary to
+ the current behavior for other key types.
+
+* Add tests for TLS 1.2 PRF (GH #628)
+
+Version 1.11.31, 2016-08-30
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Fix undefined behavior in Curve25519 on platforms without a native 128-bit
+ integer type. This was known to produce incorrect results on 32-bit ARM
+ under Clang. GH #532 (CVE-2016-6878)
+
+* If X509_Certificate::allowed_usage was called with more than one Key_Usage
+ set in the enum value, the function would return true if *any* of the allowed
+ usages were set, instead of if *all* of the allowed usages are set.
+ GH #591 (CVE-2016-6879)
+
+* Incompatible changes in DLIES: Previously the input to the KDF was
+ the concatenation of the (ephemeral) public key and the secret value
+ derived by the key agreement operation. Now the input is only the
+ secret value obtained by the key agreement operation. That's how it
+ is specified in the original paper "DHIES: An encryption scheme
+ based on Diffie-Hellman Problem" or in BSI technical guideline
+ TR-02102-1 for example. In addition to the already present
+ XOR-encrypion/decryption mode it's now possible to use DLIES with a
+ block cipher. Furthermore the order of the output was changed from
+ {public key, tag, ciphertext} to {public key, ciphertext, tag}. Both
+ modes are compatible with BouncyCastle.
+
+* Add initial PKCS #11 support (GH #507). Currently includes a low level
+ wrapper to all of PKCS #11 (p11.h) and high level code for RSA and ECDSA
+ signatures and hardware RNG access.
+
+* Add ECIES encryption scheme, compatible with BouncyCastle (GH #483)
+
+* Add ECKCDSA signature algorithm (GH #504)
+
+* Add KDF1 from ISO 18033 (GH #483)
+
+* Add FRP256v1 curve (GH #551)
+
+* Changes for userspace PRNGs HMAC_DRBG and HMAC_RNG (GH #520 and #593)
+
+ These RNGs now derive from Stateful_RNG which handles issues like periodic
+ reseeding and (on Unix) detecting use of fork. Previously these measures were
+ included only in HMAC_RNG.
+
+ Stateful_RNG allows reseeding from another RNG and/or a specified set of
+ entropy sources. For example it is possible to configure a HMAC_DRBG to reseed
+ using a PKCS #11 token RNG, the CPU's RDSEED instruction, and the system RNG
+ but disabling all other entropy polls.
+
+* AutoSeeded_RNG now uses NIST SP800-90a HMAC_DRBG(SHA-384). (GH #520)
+
+* On Windows and Unix systems, the system PRNG is used as the sole reseeding
+ source for a default AutoSeeded_RNG, completely skipping the standard entropy
+ polling code. New constructors allow specifying the reseed RNG and/or entropy
+ sources. (GH #520)
+
+* The `hres_timer` entropy source module has been removed. Timestamp inputs to
+ the RNG are now handled as additional_data inputs to HMAC_DRBG.
+
+* Add RDRAND_RNG which directly exposes the CPU RNG (GH #543)
+
+* Add PKCS #1 v1.5 id for SHA-512/256 (GH #554)
+
+* Add X509_Time::to_std_timepoint (GH #560)
+
+* Fix a bug in ANSI X9.23 padding mode, which returned one byte more
+ than the given block size (GH #529).
+
+* Fix bug in SipHash::clear, which did not reset all state (GH #547)
+
+* Fixes for FreeBSD (GH #517) and OpenBSD (GH #523). The compiler defaults
+ to Clang on FreeBSD now.
+
+* SonarQube static analysis integration (GH #592)
+
+* Switched Travis CI to Ubuntu 14.04 LTS (GH #592)
+
+* Added ARM32, ARM64, PPC32, PPC64, and MinGW x86 cross compile targets to Travis CI (GH #608)
+
+* Clean up in TLS ciphersuite handling (GH #583)
+
+* Threefish-512 AVX2 optimization work (GH #581)
+
+* Remove build configuration host and timestamp from build.h
+ This makes this header reproducible and allows using ccache's direct mode
+ (GH #586 see also #587)
+
+* Prevent building for x86-64 with x86-32 compiler and the reverse (GH #585)
+
+* Avoid build problem on 32-bit userspace ARMv8 (GH #563)
+
+* Refactor of internal MP headers (GH #549)
+
+* Avoid MSVC C4100 warning (GH #525)
+
+* Change botan.exe to botan-cli.exe on Windows to workaround VC issue (GH #584)
+
+* More tests for RSA-KEM (GH #538), DH (GH #556), EME (GH #553),
+ cipher mode padding (GH #529), CTS mode (GH #531),
+ KDF1/ISO18033 (GH #537), OctetString (GH #545), OIDs (GH #546),
+ parallel hash (GH #548), charset handling (GH #555),
+ BigInt (GH #558), HMAC_DRBG (GH #598 #600)
+
+* New deprecations. See the full list in doc/deprecated.txt
+
+ The X9.31 and HMAC_RNG RNGs are deprecated.
+ If you need a userspace PRNG, use HMAC_DRBG (or AutoSeeded_RNG
+ which is HMAC_DRBG with defaults).
+
+ Support for getting entropy from EGD is deprecated, and will be
+ removed in a future release. The developers believe that it is
+ unlikely that any modern system requires EGD and so the code is now
+ dead weight. If you rely on EGD support, you should contact the
+ developers by email or GitHub ASAP.
+
+ The TLS ciphersuites using 3DES and SEED are deprecated and will be
+ removed in a future release.
+
+ ECB mode Cipher_Mode is deprecated and will be removed in a future
+ release.
+
+ Support for BeOS/Haiku has not been tested in 5+ years and is in an
+ unknown state. Unless reports are received of successful builds and
+ use on this platform, support for BeOS/Haiku will be removed in a
+ future release.
+
+Version 1.11.30, 2016-06-19
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* In 1.11.23 a bug was introduced such that CBC-encrypted TLS packets
+ containing no plaintext bytes at all were incorrectly rejected with
+ a MAC failure. Records like this are used by OpenSSL in TLS 1.0
+ connections in order to randomize the IV.
+
+* A bug in GCM caused incorrect results if the 32-bit counter field
+ overflowed. This bug has no implications on the security but affects
+ interoperability.
+
+ With a 96-bit nonce, this could only occur if at least 2**32 128-bit
+ blocks (64 GiB) were encrypted. This actually exceeds the maximum
+ allowable length of a GCM plaintext; when messages longer than
+ 2**32 - 2 blocks are encrypted, GCM loses its security properties.
+
+ In addition to 96-bit nonces, GCM also supports nonces of arbitrary
+ length using a different method which hashes the provided nonce
+ under the authentication key. When using such a nonce, the last 4
+ bytes of the resulting CTR input might be near the overflow
+ boundary, with the probability of incorrect overflow increasing with
+ longer messages. when encrypting 256 MiB of data under a random 128
+ bit nonce, an incorrect result would be produced about 1/256 of the
+ time. With 1 MiB texts, the probability of error is reduced to 1/65536.
+
+ Since TLS uses GCM with 96 bit nonces and limits the length of any
+ record to far less than 64 GiB, TLS GCM ciphersuites are not
+ affected by this bug.
+
+ Reported by Juraj Somorovsky, described also in "Nonce-Disrespecting
+ Adversaries: Practical Forgery Attacks on GCM in TLS"
+ (https://eprint.iacr.org/2016/475.pdf)
+
+* Previously when generating a new self-signed certificate or PKCS #10
+ request, the subject DN was required to contain both common name
+ (CN) and country (C) fields. These restrictions have been removed.
+ GH #496
+
+* The Transform and Keyed_Transform interfaces has been removed. The
+ two concrete implementations of these interfaces were Cipher_Mode
+ and Compressor_Transform. The Cipher_Mode interface remains unchanged
+ as the Transform and Keyed_Transform signatures have moved to it;
+ no changes to Cipher_Mode usage should be necessary. Any uses of
+ Transform& or Keyed_Transform& to refer to a cipher should be replaced
+ by Cipher_Mode&. The compression algorithm interface has changed; the start
+ function now takes the per-message compression ratio to use. Previously the
+ compression level to use had to be set once, at creation time, and
+ the required ``secure_vector`` argument to ``start`` was required to be empty.
+ The new API is documented in `compression.rst` in the manual.
+
+* Add IETF versions of the ChaCha20Poly1305 TLS ciphersuites from
+ draft-ietf-tls-chacha20-poly1305-04. The previously implemented
+ (non-standard) ChaCha20Poly1305 ciphersuites from
+ draft-agl-tls-chacha20poly1305 remain but are deprecated.
+
+* The OCB TLS ciphersuites have been updated to use the new nonce
+ scheme from draft-zauner-tls-aes-ocb-04. This is incompatible with
+ previous versions of the draft, and the ciphersuite numbers used for
+ the (still experimental) OCB ciphersuites have changed.
+
+* Previously an unknown critical extension caused X.509 certificate
+ parsing to fail; such a cert could not be created at all. Now
+ parsing succeeds and the certificate validation fails with
+ an error indicating an unknown critical extension. GH #469
+
+* X509_CRL previously had an option to cause it to ignore unknown
+ critical extensions. This has been removed.
+
+* Added StreamCipher::seek allowing seeking to arbitrary position
+ in the key stream. Currently only implemented for ChaCha. (GH #497)
+
+* Added support for ChaCha stream cipher with 8 or 12 rounds.
+
+* Add ECGDSA signature algorithm (GH #479)
+
+* Add support for label argument to KDFs (GH #495)
+
+* Add NIST SP800-108 and 56C KDFs (GH #481)
+
+* Support for Card Verifiable Certificates and the obsolete EMSA1_BSI
+ signature padding scheme have been removed. (GH #487)
+
+* A bug in the IETF version of ChaCha20Poly1305 (with 96 bit nonces)
+ caused incorrect computation when the plaintext or AAD was exactly
+ a multiple of 16 bytes.
+
+* Fix return type of TLS_Reader::get_u32bit, which was truncated to
+ 16 bits. This only affected decoding of session ticket lifetimes.
+ GH #478
+
+* Fix OS X dylib naming problem (GH #468 #467)
+
+* Fix bcrypt function under Python 3 (GH #461)
+
+* The ``unix_procs`` entropy source is deprecated and will be removed
+ in a future release. This entropy source attempts to get entropy by
+ running Unix programs like ``arp``, ``netstat``, and ``dmesg`` which
+ produce information which may be difficult for a remote attacker to
+ guess. This exists primarily as a last-ditch for Unix systems
+ without ``/dev/random``. But at this point such systems effectively
+ no longer exist, and the use of ``fork`` and ``exec`` by the library
+ complicates effective application sandboxing.
+
+* Changes to avoid implicit cast warnings in Visual C++ (GH #484)
+
+Version 1.10.13, 2016-04-23
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Use constant time modular inverse algorithm to avoid possible
+ side channel attack against ECDSA (CVE-2016-2849)
+
+* Use constant time PKCS #1 unpadding to avoid possible side channel
+ attack against RSA decryption (CVE-2015-7827)
+
+* Avoid a compilation problem in OpenSSL engine when ECDSA was
+ disabled. Gentoo bug 542010
+
+Version 1.11.29, 2016-03-20
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* CVE-2016-2849 DSA and ECDSA used a modular inverse function which
+ had input dependent loops. It is possible a side channel attack on
+ this function could be used to recover sufficient information about
+ the nonce k to mount a lattice attack and recover the private key.
+ Found by Sean Devlin.
+
+* CVE-2016-2850 The TLS client did not check that the signature
+ algorithm or ECC curve a v1.2 server used was actually acceptable by
+ the policy. This would allow a server who ignored the preferences
+ indicated in the client to use a weak algorithm, and may allow MITM
+ attacks by an attacker who can break MD5 signatures or 160 bit ECC
+ in real time. The server similarly failed to check on the hash a
+ client used during client certificate authentication.
+
+* Reject empty TLS records at the record processing layer since such a
+ record is not valid regardless of the record type. Later checks
+ already correctly rejected empty records, but during processing such
+ a record, a pointer to the end of the vector was created, causing a
+ assertion failure under checked iterators. Found by Juraj Somorovsky.
+
+* Add PK_Decryptor::decrypt_or_random which allows an application to
+ atomically (in constant time) check that a decrypted ciphertext has
+ the expected length and/or apply content checks on the result. This
+ is used by the TLS server for decrypting PKCS #1 v1.5 RSA ciphertexts.
+ Previously the server used a implementation which was potentially
+ vulnerable to side channels.
+
+* Add support for processing X.509 name constraint extension during
+ path validation. GH #454
+
+* Add X509_Certificate::v3_extensions which allows retreiving the
+ raw binary of all certificate extensions, including those which
+ are not known to the library. This allows processing of custom
+ extensions. GH #437
+
+* Add support for module policies which are a preconfigured set of
+ acceptable or prohibited modules. A policy based on BSI TR-02102-1
+ is included. GH #439 #446
+
+* Support for the deprecated TLS heartbeat extension has been removed.
+
+* Support for the deprecated TLS minimum fragment length extension has
+ been removed.
+
+* 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,
+ SHA-224 is rarely used.
+
+* Support for negotiating ECC curves secp160r1, secp160r2, secp160k1,
+ secp192k1, secp192r1 (P-192), secp224k1, secp224r1 (P-224), and
+ secp256k1 have been removed from the TLS implementation. All were
+ already disabled in the default policy.
+
+* HMAC_RNG now has an explicit check for fork using pid comparisons.
+ It also includes the pid and system and CPU clocks into the PRF
+ computation to help reduce the risk of pid wraparound. Even so,
+ applications using fork and userspace RNGs should explicitly reseed
+ all such RNGs whenever possible.
+
+* Deprecation warning: support for DSA certificates in TLS is
+ deprecated and will be removed in a future release.
+
+* Deprecation warning: in addition to the algorithms deprecated in
+ 1.11.26, the following algorithms are now deprecated and will be
+ removed in a future release: Rabin-Williams signatures, TEA, XTEA.
+
+* Deprecation warning: the library has a number of compiled in MODP
+ and ECC DL parameters. All MODP parameter sets under 2048 bits and
+ all ECC parameters under 256 bits are deprecated and will be removed
+ in a future release. This includes the MODP groups "modp/ietf/1024",
+ "modp/srp/1024", "modp/ietf/1536", "modp/srp/1536" and the ECC
+ groups "secp160k1", "secp160r1", "secp160r2", "secp192k1",
+ "secp192r1", "secp224k1", "secp224r1", "brainpool160r1",
+ "brainpool192r1", "brainpool224r1", "x962_p192v2", "x962_p192v3",
+ "x962_p239v1", "x962_p239v2" and "x962_p239v3". Additionally all
+ compiled in DSA parameter sets ("dsa/jce/1024", "dsa/botan/2048",
+ and "dsa/botan/3072") are also deprecated.
+
+* RDSEED/RDRAND polling now retries if the operation fails. GH #373
+
+* Fix various minor bugs found by static analysis with PVS-Studio (GH#421),
+ Clang analyzer (GH #441), cppcheck (GH #444, #445), and Coverity.
+
+* Add --with-valgrind configure option to enable building against the
+ valgrind client API. This currently enables checking of const time
+ operations using memcheck.
+
+* Fix remaining Wshadow warnings. Enable Wshadow in build. GH #427
+
+* Use noexcept in VS 2015 GH #429
+
+* On Windows allow the user to explicitly request symlinks be used
+ as part of the build. Likely only useful for someone working on
+ 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
+ which is still an option even in VS 2015
+
+* MCEIES requires KDF1 at runtime but did not require it be enabled
+ in the build. GH #369
+
+* Small optimizations to Keccak hash
+
+* Support for locking allocator on Windows using VirtualLock. GH #450
+
+Version 1.8.15, 2016-02-13
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+* NOTE WELL: Botan 1.8 is not supported for security issues anymore.
+ Moving to 1.10 or 1.11 is certainly recommended.
+* Fix CVE-2014-9742: Insufficient randomness in Miller-Rabin primality check
+* Fix CVE-2016-2194: Infinite loop in modulur square root algorithm
+* Fix CVE-2015-5726: Crash in BER decoder
+* Fix CVE-2015-5727: Excess memory allocation in BER decoder
+ Note: Unlike the fix in 1.10 which checks that the source actually
+ contains enough data to satisfy the read before allocating the
+ memory, 1.8.15 simply rejects all ASN.1 blocks larger than 1 MiB.
+ This simpler check avoids the problem without breaking ABI.
+
+Version 1.10.12, 2016-02-03
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* In 1.10.11, the check in PointGFp intended to check the affine y
+ argument actually checked the affine x again. Reported by Remi Gacogne
+
+ The CVE-2016-2195 overflow is not exploitable in 1.10.11 due to an
+ additional check in the multiplication function itself which was
+ also added in that release, so there are no security implications
+ from the missed check. However to avoid confusion the change was
+ pushed in a new release immediately.
+
+ The 1.10.11 release notes incorrectly identified CVE-2016-2195 as CVE-2016-2915
+
+Version 1.10.11, 2016-02-01
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Resolve heap overflow in ECC point decoding. CVE-2016-2195
+
+* Resolve infinite loop in modular square root algorithm.
+ CVE-2016-2194
+
+* Correct BigInt::to_u32bit to not fail on integers of exactly 32 bits.
+ GH #239
+
+Version 1.11.28, 2016-02-01
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* One of the checks added while addressing CVE-2016-2195 was incorrect
+ and could cause needless assertion failures.
+
+Version 1.11.27, 2016-02-01
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* SECURITY: Avoid heap overflow in ECC point decoding. This could
+ likely result in remote code execution. CVE-2016-2195
+
+* SECURITY: Avoid one word heap overflow in P-521 reduction function.
+ This could potentially lead to remote code execution or other
+ attack. CVE-2016-2196.
+
+* SECURITY: Avoid infinite or near-infinite loop during modular square
+ root algorithm with invalid inputs. CVE-2016-2194
+
+* Add Blake2b hash function. GH #413
+
+* Use ``m_`` prefix on all member variables. GH #398 and #407
+
+* Use final qualifier on many classes. GH #408
+
+* Use noreturn attribute on assertion failure function to assist
+ static analysis. GH #403
+
+* 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
+ no longer patched by the vendor. GH #365
+
+Version 1.11.26, 2016-01-04
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Deprecation warnings: Nyberg-Rueppel signatures, MARS, RC2, RC5,
+ RC6, SAFER, HAS-160, RIPEMD-128, MD2 and support for the TLS minimum
+ fragment length extensions are all being considered for removal in a
+ future release. If there is a compelling use case for keeping any of
+ them in the library, please open a discussion ticket on GitHub.
+
+* Support for the TLS extended master secret extension (RFC 7627) has
+ been added.
+
+* The format of serialized TLS sessions has changed to add a flag
+ indicating support for the extended master secret flag, which is
+ needed for proper handling of the extension.
+
+* Root all exceptions thrown by the library in the ``Botan::Exception`` class.
+ Previously the library would in many cases throw ``std::runtime_error``
+ or ``std::invalid_argument`` exceptions which would make it hard to
+ determine the source of the error in some cases.
+
+* The command line interface has been mostly rewritten. The syntax of
+ many of the sub-programs has changed, and a number have been
+ 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
+ compute it as if the scalar was 1 instead.
+
+* Enable RdRand entropy source on Windows/MSVC. GH #364
+
+* Add Intel's RdSeed as entropy source. GH #370
+
+* Add preliminary support for accessing TPM v1.2 devices. Currently
+ random number generation, RSA key generation, and signing are
+ supported. Tested using Trousers and an ST TPM
+
+* Add generalized interface for KEM (key encapsulation) techniques. Convert
+ McEliece KEM to use it. The previous interfaces McEliece_KEM_Encryptor and
+ McEliece_KEM_Decryptor have been removed. The new KEM interface now uses a KDF
+ to hash the resulting keys; to get the same output as previously provided by
+ McEliece_KEM_Encryptor, use "KDF1(SHA-512)" and request exactly 64 bytes.
+
+* Add support for RSA-KEM from ISO 18033-2
+
+* Add support for ECDH in the OpenSSL provider
+
+* Fix a bug in DataSource::discard_next() which could cause either an
+ infinite loop or the discarding of an incorrect number of bytes.
+ Reported on mailing list by Falko Strenzke.
+
+* Previously if BOTAN_TARGET_UNALIGNED_MEMORY_ACCESS_OK was defined,
+ the code doing low level loads/stores would use pointer casts to
+ access larger words out of a (potentially misaligned) byte array,
+ rather than using byte-at-a-time accesses. However even on platforms
+ such as x86 where this works, it triggers UBSan errors under Clang.
+ Instead use memcpy, which the C standard says is usable for such
+ purposes even with misaligned values. With recent GCC and Clang, the
+ same code seems to be emitted for either approach.
+
+* Avoid calling memcpy, memset, or memmove with a length of zero to
+ avoid undefined behavior, as calling these functions with an invalid
+ or null pointer, even with a length of zero, is invalid. Often there
+ are corner cases where this can occur, such as pointing to the very
+ end of a buffer.
+
+* The function ``RandomNumberGenerator::gen_mask`` (added in 1.11.20)
+ had undefined behavior when called with a bits value of 32 or
+ higher, and was tested to behave in unpleasant ways (such as
+ returning zero) when compiled by common compilers. This function was
+ not being used anywhere in the library and rather than support
+ something without a use case to justify it it seemed simpler to
+ remove it. Undefined behavior found by Daniel Neus.
+
+* Support for using ``ctgrind`` for checking const time blocks has
+ been replaced by calling the valgrind memcheck APIs directly. This
+ allows const-time behavior to be tested without requiring a modified
+ valgrind binary. Adding the appropriate calls requires defining
+ BOTAN_HAS_VALGRIND in build.h. A binary compiled with this flag set
+ can still run normally (though with some slight runtime overhead).
+
+* Export MGF1 function mgf1_mask GH #380
+
+* 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
+ on such systems. GH #353
+
+* Let ``configure.py`` run under CPython 2.6. GH #362
+
+Version 1.11.25, 2015-12-07
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* In this release the test suite has been largely rewritten. Previously the
+ tests had internally used several different test helper frameworks created or
+ adopted over time, each of which was insufficient on its own for testing the
+ entire library. These have been fully converged on a new framework which
+ suffices for all of the tests. There should be no user-visible change as a
+ result of this, except that the output format of `botan-test` has changed.
+
+* Improved side channel countermeasures for the table based AES implementation.
+ The 4K T tables are computed (once) at runtime to avoid various cache based
+ attacks which are possible due to shared VMM mappings of read only tables.
+ Additionally every cache line of the table is read from prior to processing
+ the block(s).
+
+* Support for the insecure ECC groups secp112r1, secp112r2, secp128r1, and
+ secp128r2 has been removed.
+
+* The portable version of GCM has been changed to run using only
+ constant time operations.
+
+* Work around a bug in MSVC 2013 std::mutex which on some Windows
+ versions can result in a deadlock during static initialization. On
+ Windows a CriticalSection is used instead. Analysis and patch from
+ Matej Kenda (TopIT d.o.o.). GH #321
+
+* The OpenSSL implementation of RC4 would return the wrong value from `name` if
+ leading bytes of the keystream had been skipped in the output.
+
+* 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
+ 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.
+
+* Fixed a bug that would cause the TLS client to occasionally reject a valid
+ server key exchange message as having an invalid signature. This only affected
+ DHE and SRP ciphersuites.
+
+* Support for negotiating use of SHA-224 in TLS has been disabled in the
+ default policy.
+
+* Added `remove_all` function to the `TLS::Session_Manager` interface
+
+* Avoid GCC warning in pedantic mode when including bigint.h GH #330
+
+Version 1.11.24, 2015-11-04
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* When the bugs affecting X.509 path validation were fixed in 1.11.23, a check
+ in Credentials_Manager::verify_certificate_chain was accidentally removed
+ which caused path validation failures not to be signaled to the TLS layer.
+ Thus in 1.11.23 certificate authentication in TLS is bypassed.
+ Reported by Florent Le Coz in GH #324
+
+* Fixed an endian dependency in McEliece key generation which caused
+ keys to be generated differently on big and little endian systems,
+ 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
+ `--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.
+
+Version 1.11.23, 2015-10-26
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* CVE-2015-7824: An information leak allowed padding oracle attacks against
+ TLS CBC decryption. Found in a review by Sirrix AG and 3curity GmbH.
+
+* CVE-2015-7825: Validating a malformed certificate chain could cause an
+ infinite loop. Found in a review by Sirrix AG and 3curity GmbH.
+
+* CVE-2015-7826: X.509 path validation violated RFC 6125 and would accept
+ certificates which should not validate under those rules. In particular botan
+ would accept wildcard certificates as matching in situations where it should
+ not (for example it would erroneously accept ``*.example.com`` as a valid
+ wildcard for ``foo.bar.example.com``)
+
+* CVE-2015-7827: The routines for decoding PKCS #1 encryption and OAEP blocks
+ have been rewritten to run without secret indexes or branches. These
+ cryptographic operations are vulnerable to oracle attacks, including via side
+ channels such as timing or cache-based analysis. In theory it would be
+ possible to attack the previous implementations using such a side channel,
+ which could allow an attacker to mount a plaintext recovery attack.
+
+ By writing the code such that it does not depend on secret inputs for branch
+ or memory indexes, such a side channel would be much less likely to exist.
+
+ The OAEP code has previously made an attempt at constant time operation, but
+ it used a construct which many compilers converted into a conditional jump.
+
+* Add support for using ctgrind (https://github.com/agl/ctgrind) to test that
+ sections of code do not use secret inputs to decide branches or memory indexes.
+ The testing relies on dynamic checking using valgrind.
+
+ So far PKCS #1 decoding, OAEP decoding, Montgomery reduction, IDEA, and
+ Curve25519 have been notated and confirmed to be constant time on Linux/x86-64
+ when compiled by gcc.
+
+* Public key operations can now be used with specified providers by passing an
+ additional parameter to the constructor of the PK operation.
+
+* OpenSSL RSA provider now supports signature creation and verification.
+
+* The blinding code used for RSA, Diffie-Hellman, ElGamal and Rabin-Williams now
+ periodically reinitializes the sequence of blinding values instead of always
+ deriving the next value by squaring the previous ones. The reinitializion
+ interval can be controlled by the build.h parameter BOTAN_BLINDING_REINIT_INTERVAL.
+
+* A bug decoding DTLS client hellos prevented session resumption for succeeding.
+
+* DL_Group now prohibits creating a group smaller than 1024 bits.
+
+* Add System_RNG type. Previously the global system RNG was only accessible via
+ `system_rng` which returned a reference to the object. However is at times
+ useful to have a unique_ptr<RandomNumberGenerator> which will be either the
+ system RNG or an AutoSeeded_RNG, depending on availability, which this
+ additional type allows.
+
+* New command line tools `dl_group` and `prime`
+
+* The `configure.py` option `--no-autoload` is now also available
+ under the more understandable name `--minimized-build`.
+
+* Note: 1.11.22 was briefly released on 2015-10-26. The only difference between
+ the two was a fix for a compilation problem in the OpenSSL RSA code. As the
+ 1.11.22 release had already been tagged it was simpler to immediately release
+ 1.11.23 rather than redo the release.
+
+Version 1.11.21, 2015-10-11
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Add new methods for creating types such as BlockCiphers or HashFunctions,
+ T::providers() returning list of provider for a type, and T::create() creating
+ a new object of a specified provider. The functions in lookup.h forward to
+ these new APIs. A change to the lookup system in 1.11.14 had caused problems
+ with static libraries (GH #52). These problems have been fixed as part of these
+ changes. GH #279
+
+* Fix loading McEliece public or private keys with PKCS::load_key / X509::load_key
+
+* Add `mce` command line tool for McEliece key generation and file encryption
+
+* Add Darwin_SecRandom entropy source which uses `SecRandomCopyBytes`
+ API call for OS X and iOS, as this call is accessible even from a
+ sandboxed application. GH #288
+
+* Add new HMAC_DRBG constructor taking a name for the MAC to use, rather
+ than a pointer to an object.
+
+* The OCaml module is now a separate project at
+ https://github.com/randombit/botan-ocaml
+
+* The encrypted sqlite database support in contrib has moved to
+ https://github.com/randombit/botan-sqlite
+
+* The Perl XS module has been removed as it was no longer maintained.
+
+Version 1.11.20, 2015-09-07
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Additional countermeasures were added to ECC point multiplications
+ including exponent blinding and randomization of the point
+ representation to help protect against side channel attacks.
+
+* An ECDSA provider using OpenSSL has been added.
+
+* The ordering of algorithm priorities has been reversed. Previously
+ 255 was the lowest priority and 0 was the highest priority. Now it
+ is the reverse, with 0 being lowest priority and 255 being highest.
+ The default priority for the base algorithms is 100. This only
+ affects external providers or applications which directly set
+ provider preferences.
+
+* On OS X, rename libs to avoid trailing version numbers, e.g.
+ libbotan-1.11.dylib.19 -> libbotan-1.11.19.dylib. This was requested
+ by the Homebrew project package audit. GH #241, #260
+
+* Enable use of CPUID interface with clang. GH #232
+
+* Add support for MSVC 2015 debug builds by satisfying C++ allocator
+ requirements. SO 31802806, GH #236
+
+* Make `X509_Time` string parsing and `to_u32bit()` more strict to avoid
+ integer overflows and other potentially dangerous misinterpretations.
+ GH #240, #243
+
+* Remove all 'extern "C"' declarations from src/lib/math/mp/ because some
+ of those did throw exceptions and thus cannot be C methods. GH #249
+
+* Fix build configuration for clang debug on Linux. GH #250
+
+* Fix zlib error when compressing an empty buffer. GH #265
+
+* Fix iOS builds by allowing multiple compiler flags with the same name.
+ GH #266
+
+* Fix Solaris build issue caused by `RLIMIT_MEMLOCK`. GH #262
+
+Version 1.11.19, 2015-08-03
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* SECURITY: The BER decoder would crash due to reading from offset 0
+ of an empty vector if it encountered a BIT STRING which did not
+ contain any data at all. As the type requires a 1 byte field this is
+ not valid BER but could occur in malformed data. Found with afl.
+ CVE-2015-5726
+
+* SECURITY: The BER decoder would allocate a fairly arbitrary amount
+ of memory in a length field, even if there was no chance the read
+ request would succeed. This might cause the process to run out of
+ memory or invoke the OOM killer. Found with afl.
+ CVE-2015-5727
+
+* The TLS heartbeat extension is deprecated and unless strong arguments
+ are raised in its favor it will be removed in a future release.
+ Comment at https://github.com/randombit/botan/issues/187
+
+* The x86-32 assembly versions of MD4, MD5, SHA-1, and Serpent and the
+ x86-64 version of SHA-1 have been removed. With compilers from this
+ decade the C++ versions are significantly faster. The SSE2 versions
+ of SHA-1 and Serpent remain, as they are still the fastest version
+ for processors with SIMD extensions. GH #216
+
+* BigInt::to_u32bit would fail if the value was exactly 32 bits.
+ GH #220
+
+* Botan is now fully compaitible with _GLIBCXX_DEBUG. GH #73
+
+* BigInt::random_integer distribution was not uniform. GH #108
+
+* Added unit testing framework Catch. GH #169
+
+* Fix `make install`. GH #181, #186
+
+* Public header `fs.h` moved to `internal/filesystem.h`. Added filesystem
+ support for MSVC 2013 when boost is not available, allowing tests to run on
+ those systems. GH #198, #199
+
+* Added os "android" and fix Android compilation issues. GH #203
+
+* Drop support for Python 2.6 for all Botan Python scripts. GH #217
+
+Version 1.10.10, 2015-08-03
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* SECURITY: The BER decoder would crash due to reading from offset 0
+ of an empty vector if it encountered a BIT STRING which did not
+ contain any data at all. As the type requires a 1 byte field this is
+ not valid BER but could occur in malformed data. Found with afl.
+ CVE-2015-5726
+
+* SECURITY: The BER decoder would allocate a fairly arbitrary amount
+ of memory in a length field, even if there was no chance the read
+ request would succeed. This might cause the process to run out of
+ memory or invoke the OOM killer. Found with afl.
+ CVE-2015-5727
+
+* Due to an ABI incompatible (though not API incompatible) change in
+ this release, the version number of the shared object has been
+ increased.
+
+* The default TLS policy no longer allows RC4.
+
+* Fix a signed integer overflow in Blue Midnight Wish that may cause
+ incorrect computations or undefined behavior.
+
+Version 1.11.18, 2015-07-05
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* In this release Botan has switched VCS from ``monotone`` to ``git``,
+ and is now hosted on github at https://github.com/randombit/botan
+
+* The TLS client called ``std::set_difference`` on an invalid iterator
+ pair. This could potentially lead to a crash depending on the
+ compiler and STL implementation. It also would trigger assertion
+ failures when using checked iterators. GH #73
+
+* Remove code constructs which triggered errors under MSVC and GCC
+ debug iterators. The primary of these was an idiom of ``&vec[x]`` to
+ 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
+ analysis tools. From Simon Warta and Daniel Seither. GH #125
+
+* Several incorrect or missing module dependencies have been fixed. These
+ often prevented a successful build of a minimized amalgamation when
+ only a small set of algorithms were specified. GH #71
+ From Simon Warta.
+
+* Add an initial binding to OCaml. Currently only hashes, RNGs, and
+ bcrypt are supported.
+
+* The default key size generated by the ``keygen`` tool has increased
+ to 2048 bits. From Rene Korthaus.
+
+* The ``Botan_types`` namespace, which contained ``using`` declarations
+ for (just) ``Botan::byte`` and ``Botan::u32bit``, has been removed.
+ Any use should be replaced by ``using`` declarations for those types
+ directly.
+
+Version 1.11.17, 2015-06-18
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* All support for the insecure RC4 stream cipher has been removed
+ from the TLS implementation.
+
+* Fix decoding of TLS maximum fragment length. Regardless of what
+ value was actually negotiated, TLS would treat it as a negotiated
+ limit of 4096.
+
+* Fix the configure.py flag ``--disable-aes-ni`` which did nothing of
+ the sort.
+
+* Fixed nmake clean target. GitHub #104
+
+* Correct buffering logic in ``Compression_Filter``. GitHub #93 and #95
+
+Version 1.11.16, 2015-03-29
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* TLS has changed from using the non-standard NPN extension to the IETF
+ standardized ALPN extension for negotiating an application-level protocol.
+ Unfortunately the semantics of the exchange have changed with ALPN. Using
+ NPN, the server offered a list of protocols it advertised, and then the
+ client chose its favorite. With ALPN, the client offers a list of protocols
+ and the server chooses. The the signatures of both the TLS::Client and
+ TLS::Server constructors have changed to support this new flow.
+
+* Optimized ECDSA signature verification thanks to an observation by
+ Dr. Falko Strenzke. On some systems verifications are between 1.5
+ and 2 times faster than in 1.11.15.
+
+* RSA encrypt and decrypt operations using OpenSSL have been added.
+
+* Public key operation types now handle all aspects of the operation,
+ such as hashing and padding for signatures. This change allows
+ supporting specialized implementations which only support particular
+ padding types.
+
+* 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
+ on a specific poll with the new API call reseed_with_timeout.
+
+* Fixed Python cipher update_granularity() and default_nonce_length()
+ functions
+
+* The library now builds on Visual C++ 2013
+
+* The GCM update granularity was reduced from 4096 to 16 bytes.
+
+* Fix a bug that prevented building the amalgamation until a non-amalgamation
+ configuration was performed first in the same directory.
+
+* Add Travis CI integration. Github pull 60.
+
+Version 1.11.15, 2015-03-08
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Support for RC4 in TLS, already disabled by default, is now deprecated.
+ The RC4 ciphersuites will be removed entirely in a future release.
+
+* A bug in ffi.cpp meant Python could only encrypt. Github issue 53.
+
+* When comparing two ASN.1 algorithm identifiers, consider empty and
+ NULL parameters the same.
+
+* Fixed memory leaks in TLS and cipher modes introduced in 1.11.14
+
+* MARK-4 failed when OpenSSL was enabled in the build in 1.11.14
+ because the OpenSSL version ignored the skip parameter.
+
+* Fix compilation problem on OS X/clang
+
+* Use BOTAN_NOEXCEPT macro to work around lack of noexcept in VS 2013
+
+Version 1.11.14, 2015-02-27
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* The global state object previously used by the library has been removed.
+ This includes the global PRNG. The library can be safely initialized
+ multiple times without harm.
+
+ The engine code has also been removed, replaced by a much lighter-weight
+ object registry system which provides lookups in faster time and with less
+ memory overhead than the previous approach.
+
+ One caveat of the current system with regards to static linking: because only
+ symbols already mentioned elsewhere in the program are included in the final
+ link step, few algorithms will be available through the lookup system by
+ default, even though they were compiled into the library. Your application
+ must explicitly reference the types you require or they will not end up
+ being available in the final binary. See also Github issue #52
+
+ If you intend to build your application against a static library and don't
+ want to explicitly reference each algo object you might attempt to look up by
+ string, consider either building with ``--via-amalgamation``, or else (much
+ simpler) using the amalgamation directly.
+
+* The new ``ffi`` submodule provides a simple C API/ABI for a number of useful
+ operations (hashing, ciphers, public key operations, etc) which is easily
+ accessed using the FFI modules included in many languages.
+
+* A new Python wrapper (in ``src/lib/python/botan.py``) using ``ffi`` and the Python
+ ``ctypes`` module is available. The old Boost.Python wrapper has been removed.
+
+* Add specialized reducers for P-192, P-224, P-256, and P-384
+
+* OCB mode, which provides a fast and constant time AEAD mode without requiring
+ hardware support, is now supported in TLS, following
+ draft-zauner-tls-aes-ocb-01. Because this specification is not yet finalized
+ is not yet enabled by the default policy, and the ciphersuite numbers used are
+ in the experimental range and may conflict with other uses.
+
+* Add ability to read TLS policy from a text file using ``TLS::Text_Policy``.
+
+* The amalgamation now splits off any ISA specific code (for instance, that
+ requiring SSSE3 instruction sets) into a new file named (for instance)
+ ``botan_all_ssse3.cpp``. This allows the main amalgamation file to be compiled
+ without any special flags, so ``--via-amalgamation`` builds actually work now.
+ This is disabled with the build option ``--single-amalgamation-file``
+
+* PBKDF and KDF operations now provide a way to write the desired output
+ directly to an application-specified area rather than always allocating a new
+ heap buffer.
+
+* HKDF, previously provided using a non-standard interface, now uses the
+ standard KDF interface and is retrievable using get_kdf.
+
+* It is once again possible to build the complete test suite without requiring
+ any boost libraries. This is currently only supported on systems supporting
+ the readdir interface.
+
+* Remove use of memset_s which caused problems with amalgamation on OS X.
+ Github 42, 45
+
+* The memory usage of the counter mode implementation has been reduced.
+ Previously it encrypted 256 blocks in parallel as this leads to a slightly
+ faster counter increment operation. Instead CTR_BE simply encrypts a buffer
+ equal in size to the advertised parallelism of the cipher implementation.
+ This is not measurably slower, and dramatically reduces the memory use of
+ CTR mode.
+
+* The memory allocator available on Unix systems which uses mmap and mlock to
+ lock a pool of memory now checks environment variable BOTAN_MLOCK_POOL_SIZE
+ and interprets it as an integer. If the value set to a smaller value then the
+ library would originally have allocated (based on resource limits) the user
+ specified size is used instead. You can also set the variable to 0 to
+ disable the pool entirely. Previously the allocator would consume all
+ available mlocked memory, this allows botan to coexist with an application
+ which wants to mlock memory for its own uses.
+
+* The botan-config script previously installed on Unix systems has been
+ removed. Its functionality is replaced by the ``config`` command of the
+ ``botan`` tool executable, for example ``botan config cflags`` instead of
+ ``botan-config --cflags``.
+
+* Added a target for POWER8 processors
+
+Version 1.11.13, 2015-01-11
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* All support for the insecure SSLv3 protocol and the server support
+ for processing SSLv2 client hellos has been removed.
+
+* The command line tool now has ``tls_proxy`` which negotiates TLS with
+ clients and forwards the plaintext to a specified port.
+
+* Add MCEIES, a McEliece-based integrated encryption system using
+ AES-256 in OCB mode for message encryption/authentication.
+
+* Add DTLS-SRTP negotiation defined in RFC 5764
+
+* Add SipHash
+
+* Add SHA-512/256
+
+* The format of serialized TLS sessions has changed. Additiionally, 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
+ CBC+HMAC construction.
+
+* The cryptobox_psk module added in 1.11.4 and previously used for TLS session
+ encryption has been removed.
+
+* When sending a TLS heartbeat message, the number of pad bytes to use can now
+ be specified, making it easier to use for PMTU discovery.
+
+* If available, zero_mem now uses RtlSecureZeroMemory or memset_s instead of a
+ byte-at-a-time loop.
+
+* The functions base64_encode and base64_decode would erroneously
+ throw an exception if passed a zero-length input. Github issue 37.
+
+* The Python install script added in version 1.11.10 failed to place the
+ headers into a versioned subdirectory.
+
+* Fix the install script when running under Python3.
+
+* Avoid code that triggers iterator debugging asserts under MSVC 2013. Github
+ pull 36 from Simon Warta.
+
+Version 1.11.12, 2015-01-02
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Add Curve25519. The implementation is based on curve25519-donna-c64.c
+ by Adam Langley. New (completely non-standard) OIDs and formats for
+ encrypting Curve25519 keys under PKCS #8 and including them in
+ certificates and CRLs have been defined.
+
+* Add Poly1305, based on the implementation poly1305-donna by Andrew Moon.
+
+* Add the ChaCha20Poly1305 AEADs defined in draft-irtf-cfrg-chacha20-poly1305-03
+ and draft-agl-tls-chacha20poly1305-04.
+
+* Add ChaCha20Poly1305 ciphersuites for TLS compatible with Google's servers
+ following draft-agl-tls-chacha20poly1305-04
+
+* When encrypted as PKCS #8 structures, Curve25519 and McEliece
+ private keys default to using AES-256/GCM instead of AES-256/CBC
+
+* Define OIDs for OCB mode with AES, Serpent and Twofish.
+
+Version 1.11.11, 2014-12-21
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* The Sqlite3 wrapper has been abstracted to a simple interface for
+ SQL dbs in general, though Sqlite3 remains the only implementation.
+ The main logic of the TLS session manager which stored encrypted
+ sessions to a Sqlite3 database (``TLS::Session_Manager_SQLite``) has
+ been moved to the new ``TLS::Session_Manager_SQL``. The Sqlite3
+ manager API remains the same but now just subclasses
+ ``TLS::Session_Manager_SQL`` and has a constructor instantiate the
+ concrete database instance.
+
+ Applications which would like to use a different db can now do so
+ without having to reimplement the session cache logic simply by
+ implementing a database wrapper subtype.
+
+* The CryptGenRandom entropy source is now also used on MinGW.
+
+* The system_rng API is now also available on systems with CryptGenRandom
+
+* With GCC use -fstack-protector for linking as well as compiling,
+ as this is required on MinGW. Github issue 34.
+
+* Fix missing dependency in filters that caused compilation problem
+ in amalgamation builds. Github issue 33.
+
+* SSLv3 support is officially deprecated and will be removed in a
+ future release.
+
+Version 1.10.9, 2014-12-13
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Fixed EAX tag verification to run in constant time
+
+* The default TLS policy now disables SSLv3.
+
+* A crash could occur when reading from a blocking random device if
+ the device initially indicated that entropy was available but
+ a concurrent process drained the entropy pool before the
+ read was initiated.
+
+* Fix decoding indefinite length BER constructs that contain a context
+ sensitive tag of zero. Github pull 26 from Janusz Chorko.
+
+* The ``botan-config`` script previously tried to guess its prefix from
+ the location of the binary. However this was error prone, and now
+ the script assumes the final installation prefix matches the value
+ set during the build. Github issue 29.
+
+Version 1.11.10, 2014-12-10
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* An implementation of McEliece code-based public key encryption based
+ on INRIA's HyMES and secured against a variety of side-channels was
+ contributed by cryptosource GmbH. The original version is LGPL but
+ cryptosource has secured permission to release an adaptation under a
+ BSD license. A CCA2-secure KEM scheme is also included.
+
+ The implementation is further described in
+ http://www.cryptosource.de/docs/mceliece_in_botan.pdf and
+ http://cryptosource.de/news_mce_in_botan_en.html
+
+* DSA and ECDSA now create RFC 6979 deterministic signatures.
+
+* Add support for TLS fallback signaling (draft-ietf-tls-downgrade-scsv-00).
+ Clients will send a fallback SCSV if the version passed to the Client
+ constructor is less than the latest version supported by local policy, so
+ applications implementing fallback are protected. Servers always check the
+ SCSV.
+
+* In previous versions a TLS::Server could service either TLS or DTLS
+ connections depending on policy settings and what type of client hello it
+ received. This has changed and now a Server object is initialized for
+ either TLS or DTLS operation. The default policy previously prohibited
+ DTLS, precisely to prevent a TCP server from being surprised by a DTLS
+ connection. The default policy now allows TLS v1.0 or higher or DTLS v1.2.
+
+* 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.
+ Bugzilla 270.
+
+* DTLS now supports timeouts and handshake retransmits. Timeout checking
+ is triggered by the application calling the new TLS::Channel::timeout_check.
+
+* Add a TLS policy hook to disable putting the value of the local clock in hello
+ random fields.
+
+* All compression operations previously available as Filters are now
+ performed via the Transformation API, which minimizes memory copies.
+ Compression operations are still available through the Filter API
+ using new general compression/decompression filters in comp_filter.h
+
+* The zlib module now also supports gzip compression and decompression.
+
+* Avoid a crash in low-entropy situations when reading from /dev/random, when
+ select indicated the device was readable but by the time we start the read the
+ entropy pool had been depleted.
+
+* The Miller-Rabin primality test function now takes a parameter allowing the
+ user to directly specify the maximum false negative probability they are
+ willing to accept.
+
+* PKCS #8 private keys can now be encrypted using GCM mode instead of
+ unauthenticated CBC. The default remains CBC for compatibility.
+
+* The default PKCS #8 encryption scheme has changed to use PBKDF2 with
+ SHA-256 instead of SHA-1
+
+* A specialized reducer for P-521 was added.
+
+* On Linux the mlock allocator will use MADV_DONTDUMP on the pool so
+ that the contents are not included in coredumps.
+
+* A new interface for directly using a system-provided PRNG is
+ available in system_rng.h. Currently only systems with /dev/urandom
+ are supported.
+
+* Fix decoding indefinite length BER constructs that contain a context sensitive
+ tag of zero. Github pull 26 from Janusz Chorko.
+
+* The GNU MP engine has been removed.
+
+* Added AltiVec detection for POWER8 processors.
+
+* Add a new install script written in Python which replaces shell hackery in the
+ makefiles.
+
+* Various modifications to better support Visual C++ 2013 and 2015. Github
+ issues 11, 17, 18, 21, 22.
+
+Version 1.10.8, 2014-04-10
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* SECURITY: Fix a bug in primality testing introduced in 1.8.3 which
+ caused only a single random base, rather than a sequence of random
+ bases, to be used in the Miller-Rabin test. This increased the
+ probability that a non-prime would be accepted, for instance a 1024
+ bit number would be incorrectly classed as prime with probability
+ around 2^-40. Reported by Jeff Marrison. CVE-2014-9742
+
+* The key length limit on HMAC has been raised to 512 bytes, allowing
+ the use of very long passphrases with PBKDF2.
+
+Version 1.11.9, 2014-04-10
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* SECURITY: Fix a bug in primality testing introduced in 1.8.3 which
+ caused only a single random base, rather than a sequence of random
+ bases, to be used in the Miller-Rabin test. This increased the
+ probability that a non-prime would be accepted, for instance a 1024
+ bit number would be incorrectly classed as prime with probability
+ around 2^-40. Reported by Jeff Marrison. CVE-2014-9742
+
+* X.509 path validation now returns a set of all errors that occurred
+ during validation, rather than immediately returning the first
+ detected error. This prevents a seemingly innocuous error (such as
+ an expired certificate) from hiding an obviously serious error
+ (such as an invalid signature). The Certificate_Status_Code enum is
+ now ordered by severity, and the most severe error is returned by
+ Path_Validation_Result::result(). The entire set of status codes is
+ available with the new all_statuses call.
+
+* Fixed a bug in OCSP response decoding which would cause an error
+ when attempting to decode responses from some widely used
+ responders.
+
+* An implementation of HMAC_DRBG RNG from NIST SP800-90A has been
+ added. Like the X9.31 PRNG implementation, it uses another
+ underlying RNG for seeding material.
+
+* An implementation of the RFC 6979 deterministic nonce generator has
+ been added.
+
+* Fix a bug in certificate path validation which prevented successful
+ validation if intermediate certificates were presented out of order.
+
+* Fix a bug introduced in 1.11.5 which could cause crashes or other
+ incorrect behavior when a cipher mode filter was followed in the
+ pipe by another filter, and that filter had a non-empty start_msg.
+
+* The types.h header now uses stdint.h rather than cstdint to avoid
+ problems with Clang on OS X.
+
+Version 1.11.8, 2014-02-13
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* The ``botan`` command line application introduced in 1.11.7 is now
+ installed along with the library.
+
+* A bug in certificate path validation introduced in 1.11.6 which
+ caused all CRL signature checks to fail has been corrected.
+
+* The ChaCha20 stream cipher has been added.
+
+* The ``Transformation`` class no longer implements an interface for keying,
+ this has been moved to a new subclass ``Keyed_Transformation``.
+
+* The ``Algorithm`` class, which previously acted as a global base for
+ various types (ciphers, hashes, etc) has been removed.
+
+* CMAC now supports 256 and 512 bit block ciphers, which also allows
+ the use of larger block ciphers with EAX mode. In particular this
+ allows using Threefish in EAX mode.
+
+* The antique PBES1 private key encryption scheme (which only supports
+ DES or 64-bit RC2) has been removed.
+
+* The Square, Skipjack, and Luby-Rackoff block ciphers have been removed.
+
+* The Blue Midnight Wish hash function has been removed.
+
+* Skein-512 no longer supports output lengths greater than 512 bits.
+
+* Skein did not reset its internal state properly if clear() was
+ called, causing it to produce incorrect results for the following
+ message. It was reset correctly in final() so most usages should not
+ be affected.
+
+* A number of public key padding schemes have been renamed to match
+ the most common notation; for instance EME1 is now called OAEP and
+ EMSA4 is now called PSSR. Aliases are set which should allow all
+ current applications to continue to work unmodified.
+
+* A bug in CFB encryption caused a few bytes past the end of the final
+ block to be read. The actual output was not affected.
+
+* Fix compilation errors in the tests that occurred with minimized
+ builds. Contributed by Markus Wanner.
+
+* Add a new ``--destdir`` option to ``configure.py`` which controls
+ where the install target will place the output. The ``--prefix``
+ option continues to set the location where the library expects to be
+ eventually installed.
+
+* Many class destructors which previously deleted memory have been
+ removed in favor of using ``unique_ptr``.
+
+* Various portability fixes for Clang, Windows, Visual C++ 2013, OS X,
+ and x86-32.
+
+Version 1.11.7, 2014-01-10
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Botan's basic numeric types are now defined in terms of the
+ C99/C++11 standard integer types. For instance ``u32bit`` is now a
+ typedef for ``uint32_t``, and both names are included in the library
+ namespace. This should not result in any application-visible
+ changes.
+
+* There are now two executable outputs of the build, ``botan-test``,
+ which runs the tests, and ``botan`` which is used as a driver to call
+ into various subcommands which can also act as examples of library
+ use, much in the manner of the ``openssl`` command. It understands the
+ commands ``base64``, ``asn1``, ``x509``, ``tls_client``, ``tls_server``,
+ ``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
+ form is welcome.
+
+* Fix a bug in Lion, which was broken by a change in 1.11.0. The
+ problem was not noticed before as Lion was also missing a test vector
+ in previous releases.
+
+Version 1.10.7, 2013-12-29
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* OAEP had two bugs, one of which allowed it to be used even if the
+ key was too small, and the other of which would cause a crash during
+ decryption if the EME data was too large for the associated key.
+
+Version 1.11.6, 2013-12-29
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* The Boost filesystem and asio libraries are now being used by default.
+ Pass ``--without-boost`` to ``configure.py`` to disable.
+
+* The default TLS policy no longer allows SSLv3 or RC4.
+
+* OAEP had two bugs, one of which allowed it to be used even if the
+ key was too small, and the other of which would cause a crash during
+ decryption if the EME data was too large for the associated key.
+
+* GCM mode now uses the Intel clmul instruction when available
+
+* Add the Threefish-512 tweakable block cipher, including an AVX2 version
+
+* Add SIV (from :rfc:`5297`) as a nonce-based AEAD
+
+* Add HKDF (from :rfc:`5869`) using an experimental PRF interface
+
+* Add HTTP utility functions and OCSP online checking
+
+* Add TLS::Policy::acceptable_ciphersuite hook to disable ciphersuites
+ on an ad-hoc basis.
+
+* TLS::Session_Manager_In_Memory's constructor now requires a RNG
+
+Version 1.10.6, 2013-11-10
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* The device reading entropy source now attempts to read from all
+ available devices. Previously it would break out early if a partial
+ read from a blocking source occurred, not continuing to read from a
+ non-blocking device. This would cause the library to fall back on
+ slower and less reliable techniques for collecting PRNG seed
+ material. Reported by Rickard Bellgrim.
+
+* HMAC_RNG (the default PRNG implementation) now automatically reseeds
+ itself periodically. Previously reseeds only occurred on explicit
+ application request.
+
+* Fix an encoding error in EC_Group when encoding using EC_DOMPAR_ENC_OID.
+ Reported by fxdupont on github.
+
+* In EMSA2 and Randpool, avoid calling name() on objects after deleting them if
+ the provided algorithm objects are not suitable for use. Found by Clang
+ analyzer, reported by Jeffrey Walton.
+
+* If X509_Store was copied, the u32bit containing how long to cache validation
+ results was not initialized, potentially causing results to be cached for
+ significant amounts of time. This could allow a certificate to be considered
+ valid after its issuing CA's cert expired. Expiration of the end-entity cert
+ is always checked, and reading a CRL always causes the status to be reset, so
+ this issue does not affect revocation. Found by Coverity scanner.
+
+* Avoid off by one causing a potentially unterminated string to be passed to
+ the connect system call if the library was configured to use a very long path
+ 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
+ on the key object given. Found by Coverity scanner.
+
+* Avoid leaking a file descriptor in the /dev/random and EGD entropy sources if
+ stdin (file descriptor 0) was closed. Found by Coverity scanner.
+
+* Avoid a potentially undefined operation in the bit rotation operations. Not
+ known to have caused problems under any existing compiler, but might have
+ caused problems in the future. Caught by Clang sanitizer, reported by Jeffrey
+ Walton.
+
+* Increase default hash iterations from 10000 to 50000 in PBES1 and PBES2
+
+* Add a fix for mips64el builds from Brad Smith.
+
+Version 1.11.5, 2013-11-10
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* The TLS callback signatures have changed - there are now two distinct
+ callbacks for application data and alerts. TLS::Client and TLS::Server have
+ constructors which continue to accept the old callback and use it for both
+ operations.
+
+* The entropy collector that read from randomness devices had two bugs - it
+ would break out of the poll as soon as any read succeeded, and it selected on
+ each device individually. When a blocking source was first in the device list
+ and the entropy pool was running low, the reader might either block in select
+ until eventually timing out (continuing on to read from /dev/urandom instead),
+ or read just a few bytes, skip /dev/urandom, fail to satisfy the entropy
+ target, and the poll would continue using other (slower) sources. This caused
+ substantial performance/latency problems in RNG heavy applications. Now all
+ devices are selected over at once, with the effect that a full read from
+ urandom always occurs, along with however much (if any) output is available
+ from blocking sources.
+
+* Previously AutoSeeded_RNG referenced a globally shared PRNG instance.
+ Now each instance has distinct state.
+
+* The entropy collector that runs Unix programs to collect statistical
+ data now runs multiple processes in parallel, greatly reducing poll
+ times on some systems.
+
+* The Randpool RNG implementation was removed.
+
+* All existing cipher mode implementations (such as CBC and XTS) have been
+ converted from filters to using the interface previously provided by
+ AEAD modes which allows for in-place message
+ processing. Code which directly references the filter objects will break, but
+ an adaptor filter allows usage through get_cipher as usual.
+
+* An implementation of CCM mode from RFC 3601 has been added, as well as CCM
+ ciphersuites for TLS.
+
+* The implementation of OCB mode now supports 64 and 96 bit tags
+
+* Optimized computation of XTS tweaks, producing a substantial speedup
+
+* Add support for negotiating Brainpool ECC curves in TLS
+
+* TLS v1.2 will not negotiate plain SHA-1 signatures by default.
+
+* TLS channels now support sending a ``std::vector``
+
+* Add a generic 64x64->128 bit multiply instruction operation in mul128.h
+
+* Avoid potentially undefined operations in the bit rotation operations. Not
+ known to have caused problems under existing compilers but might break in the
+ future. Found by Clang sanitizer, reported by Jeffrey Walton.
+
+Version 1.11.4, 2013-07-25
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* CPU specific extensions are now always compiled if support for the
+ operations is available at build time, and flags enabling use of
+ extra operations (such as SSE2) are only included when compiling
+ files which specifically request support. This means, for instance,
+ that the SSSE3 and AES-NI implementations of AES are always included
+ in x86 builds, relying on runtime cpuid checking to prevent their
+ use on CPUs that do not support those operations.
+
+* The default TLS policy now only accepts TLS, to minimize surprise
+ for servers which might not expect to negotiate DTLS. Previously a
+ server would by default negotiate either protocol type (clients
+ would only accept the same protocol type as they
+ offered). Applications which use DTLS or combined TLS/DTLS need to
+ override ``Policy::acceptable_protocol_version``.
+
+* The TLS channels now accept a new parameter specifying how many
+ bytes to preallocate for the record handling buffers, which allows
+ an application some control over how much memory is used at runtime
+ for a particular connection.
+
+* Applications can now send arbitrary TLS alert messages using
+ ``TLS::Channel::send_alert``
+
+* A new TLS policy ``NSA_Suite_B_128`` is available, which
+ will negotiate only the 128-bit security NSA Suite B. See
+ :rfc:`6460` for more information about Suite B.
+
+* Adds a new interface for benchmarking, ``time_algorithm_ops``,
+ which returns a map of operations to operations per second. For
+ instance now both encrypt and decrypt speed of a block cipher can be
+ checked, as well as the key schedule of all keyed algorithms. It
+ additionally supports AEAD modes.
+
+* Rename ARC4 to RC4
+
+Version 1.11.3, 2013-04-11
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Add a new interface for AEAD modes (``AEAD_Mode``).
+
+* Implementations of the OCB and GCM authenticated cipher modes are
+ now included.
+
+* Support for TLS GCM ciphersuites is now available.
+
+* A new TLS policy mechanism
+ ``TLS::Policy::server_uses_own_ciphersuite_preferences``
+ controls how a server chooses a ciphersuite. Previously it always
+ chose its most preferred cipher out of the client's list, but this
+ can allow configuring a server to choose by the client's preferences
+ instead.
+
+* ``Keyed_Filter`` now supports returning a
+ ``Key_Length_Specification`` so the full details of what
+ keylengths are supported is now available in keyed filters.
+
+* The experimental and rarely used Turing and WiderWAKE stream ciphers
+ have been removed
+
+* New functions for symmetric encryption are included in cryptobox.h
+ though interfaces and formats are subject to change.
+
+* A new function ``algorithm_kat_detailed`` returns a string
+ providing information about failures, instead of just a pass/fail
+ indicator as in ``algorithm_kat``.
+
+Version 1.10.5, 2013-03-02
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* A potential crash in the AES-NI implementation of the AES-192 key
+ schedule (caused by misaligned loads) has been fixed.
+
+* A previously conditional operation in Montgomery multiplication and
+ squaring is now always performed, removing a possible timing
+ channel.
+
+* Use correct flags for creating a shared library on OS X under Clang.
+
+* Fix a compile time incompatibility with Visual C++ 2012.
+
+Version 1.11.2, 2013-03-02
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* A bug in the release script caused the ``botan_version.py`` included
+ in 1.11.1`` to be invalid, which required a manual edit to fix
+ (Bugzilla 226)
+
+* Previously ``clear_mem`` was implemented by an inlined call to
+ ``std::memset``. However an optimizing compiler might notice cases
+ where the memset could be skipped in cases allowed by the standard.
+ Now ``clear_mem`` calls ``zero_mem`` which is compiled separately and
+ which zeros out the array through a volatile pointer. It is possible
+ some compiler with some optimization setting (especially with
+ something like LTO) might still skip the writes. It would be nice if
+ there was an automated way to test this.
+
+* The new filter ``Threaded_Fork`` acts like a normal
+ ``Fork``, sending its input to a number of different
+ filters, but each subchain of filters in the fork runs in its own
+ thread. Contributed by Joel Low.
+
+* The default TLS policy formerly preferred AES over RC4, and allowed
+ 3DES by default. Now the default policy is to negotiate only either
+ AES or RC4, and to prefer RC4.
+
+* New TLS ``Blocking_Client`` provides a thread per
+ connection style API similar to that provided in 1.10
+
+* The API of ``Credentials_Manager::trusted_certificate_authorities``
+ has changed to return a vector of ``Certificate_Store*`` instead of
+ ``X509_Certificate``. This allows the list of trusted CAs to be
+ more easily updated dynamically or loaded lazily.
+
+* The ``asn1_int.h`` header was split into ``asn1_alt_name.h``,
+ ``asn1_attribute.h`` and ``asn1_time.h``.
+
+Version 1.10.4, 2013-01-07
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Avoid a conditional operation in the power mod implementations on if
+ a nibble of the exponent was zero or not. This may help protect
+ against certain forms of side channel attacks.
+
+* The SRP6 code was checking for invalid values as specified in RFC
+ 5054, specifically values equal to zero mod p. However SRP would
+ accept negative A/B values, or ones larger than p, neither of which
+ should occur in a normal run of the protocol. These values are now
+ rejected. Credits to Timothy Prepscius for pointing out these values
+ are not normally used and probably signal something fishy.
+
+* The return value of version_string is now a compile time constant
+ string, so version information can be more easily extracted from
+ binaries.
+
+Version 1.11.1, 2012-10-30
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Initial support for DTLS (both v1.0 and v1.2) is available in this
+release, though it should be considered highly experimental. Currently
+timeouts and retransmissions are not handled.
+
+The ``TLS::Client`` constructor now takes the version to
+offer to the server. The policy hook ``TLS::Policy`` function
+`pref_version``, which previously controlled this, has been removed.
+
+`TLS::Session_Manager_In_Memory`` now chooses a random
+256-bit key at startup and encrypts all sessions (using the existing
+`TLS::Session::encrypt`` mechanism) while they are stored in
+memory. This is primarily to reduce pressure on locked memory, as each
+session normally requires 48 bytes of locked memory for the master
+secret, whereas now only 32 bytes are needed total. This change may
+also make it slightly harder for an attacker to extract session data
+from memory dumps (eg with a cold boot attack).
+
+The keys used in TLS session encryption were previously uniquely
+determined by the master key. Now the encrypted session blob includes
+two 80 bit salts which are used in the derivation of the cipher and
+MAC keys.
+
+The ``secure_renegotiation`` flag is now considered an aspect of the
+connection rather than the session, which matches the behavior of
+other implementations. As the format has changed, sessions saved to
+persistent storage by 1.11.0 will not load in this version and vice
+versa. In either case this will not cause any errors, the session will
+simply not resume and instead a full handshake will occur.
+
+New policy hooks ``TLS::Policy::acceptable_protocol_version``,
+`TLS::Policy::allow_server_initiated_renegotiation``, and
+`TLS::Policy::negotiate_heartbeat_support`` were added.
+
+TLS clients were not sending a next protocol message during a session
+resumption, which would cause resumption failures with servers that
+support NPN if NPN was being offered by the client.
+
+A bug caused heartbeat requests sent by the counterparty during a
+handshake to be passed to the application callback as if they were
+heartbeat responses.
+
+Support for TLS key material export as specified in :rfc:`5705` has
+been added, available via ``TLS::Channel::key_material_export``
+
+A new function ``Public_Key::estimated_strength`` returns
+an estimate for the upper bound of the strength of the key. For
+instance for an RSA key, it will return an estimate of how many
+operations GNFS would take to factor the key.
+
+A new ``Path_Validation_Result`` code has been added
+``SIGNATURE_METHOD_TOO_WEAK``. By default signatures created with keys
+below 80 bits of strength (as estimated by ``estimated_strength``) are
+rejected. This level can be modified using a parameter to the
+``Path_Validation_Restrictions`` constructor.
+
+The SRP6 code was checking for invalid values as specified in
+:rfc:`5054`, ones equal to zero mod p, however it would accept
+negative A/B values, or ones larger than p, neither of which should
+occur in a normal run of the protocol. These values are now
+rejected. Credits to Timothy Prepscius for pointing out these values
+are not normally used and probably signal something fishy.
+
+Several ``BigInt`` functions have been removed, including
+``operator[]``, ``assign``, ``get_reg``, and ``grow_reg``. The version
+of ``data`` that returns a mutable pointer has been renamed
+``mutable_data``. Support for octal conversions has been removed.
+
+The constructor ``BigInt(NumberType type, size_t n)`` has been
+removed, replaced by ``BigInt::power_of_2``.
+
+In 1.11.0, when compiled by GCC, the AES-NI implementation of AES-192
+would crash if the mlock-based allocator was used due to an alignment
+issue.
+
+Version 1.11.0, 2012-07-19
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. note::
+
+ In this release, many new features of C++11 are being used in the
+ library. Currently GCC 4.7 and Clang 3.1 are known to work well.
+ This version of the library cannot be compiled by or used with a
+ C++98 compiler.
+
+There have been many changes and improvements to TLS. The interface
+is now purely event driven and does not directly interact with
+sockets. New TLS features include TLS v1.2 support, client
+certificate authentication, renegotiation, session tickets, and
+session resumption. Session information can be saved in memory or to
+an encrypted SQLite3 database. Newly supported TLS ciphersuite
+algorithms include using SHA-2 for message authentication, pre shared
+keys and SRP for authentication and key exchange, ECC algorithms for
+key exchange and signatures, and anonymous DH/ECDH key exchange.
+
+Support for OCSP has been added. Currently only client-side support
+exists.
+
+The API for X.509 path validation has changed, with
+``x509_path_validate`` in x509path.h now handles path validation and
+``Certificate_Store`` handles storage of certificates and CRLs.
+
+The memory container types have changed substantially. The
+``MemoryVector`` and ``SecureVector`` container types have been
+removed, and an alias of ``std::vector`` using an allocator that
+clears memory named ``secure_vector`` is used for key material, with
+plain ``std::vector`` being used for everything else.
+
+The technique used for mlock'ing memory on Linux and BSD systems is
+much improved. Now a single page-aligned block of memory (the exact
+limit of what we can mlock) is mmap'ed, with allocations being done
+using a best-fit allocator and all metadata held outside the mmap'ed
+range, in an effort to make best use of the very limited amount of
+memory current Linux kernels allow unpriveledged users to lock.
+
+A filter using LZMA was contributed by Vojtech Kral. It is available
+if LZMA support was enabled at compilation time by passing
+``--with-lzma`` to ``configure.py``.
+
+:rfc:`5915` adds some extended information which can be included in
+ECC private keys which the ECC key decoder did not expect, causing an
+exception when such a key was loaded. In particular, recent versions
+of OpenSSL use these fields. Now these fields are decoded properly,
+and if the public key value is included it is used, as otherwise the
+public key needs to be rederived from the private key. However the
+library does not include these fields on encoding keys for
+compatibility with software that does not expect them (including older
+versions of botan).
+
+Version 1.8.14, 2012-07-18
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* The malloc allocator would return null instead of throwing in the
+ event of an allocation failure, which could cause an application
+ crash due to null pointer dereference where normally an exception
+ would occur.
+
+* Recent versions of OpenSSL include extra information in ECC private
+ keys, the presence of which caused an exception when such a key was
+ loaded by botan. The decoding of ECC private keys has been changed to
+ ignore these fields if they are set.
+
+* AutoSeeded_RNG has been changed to prefer ``/dev/random`` over
+ ``/dev/urandom``
+
+* Fix detection of s390x (Debian bug 638347)
+
+Version 1.10.3, 2012-07-10
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+A change in 1.10.2 accidentally broke ABI compatibility with 1.10.1
+and earlier versions, causing programs compiled against 1.10.1 to
+crash if linked with 1.10.2 at runtime.
+
+Recent versions of OpenSSL include extra information in ECC private
+keys, the presence of which caused an exception when such a key was
+loaded by botan. The decoding of ECC private keys has been changed to
+ignore these fields if they are set.
+
+Version 1.10.2, 2012-06-17
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Several TLS bugs were fixed in this release, including a major
+omission that the renegotiation extension was not being used. As the
+1.10 implementation of TLS does not properly support renegotiation,
+the approach in this release is simply to send the renegotiation
+extension SCSV, which should protect the client against any handshake
+splicing. In addition renegotiation attempts are handled properly
+instead of causing handshake failures - all hello requests, and all
+client hellos after the initial negotiation, are ignored. Some
+bugs affecting DSA server authentication were also fixed.
+
+By popular request, ``Pipe::reset`` no longer requires that message
+processing be completed, a requirement that caused problems when a
+Filter's end_msg call threw an exception, after which point the Pipe
+object was no longer usable.
+
+Support for getting entropy using the rdrand instruction introduced in
+Intel's Ivy Bridge processors has been added. In previous releases,
+the ``CPUID::has_rdrand`` function was checking the wrong cpuid bit,
+and would false positive on AMD Bulldozer processors.
+
+An implementation of SRP-6a compatible with the specification in RFC
+5054 is now available in ``srp6.h``. In 1.11, this is being used for
+TLS-SRP, but may be useful in other environments as well.
+
+An implementation of the Camellia block cipher was added, again largely
+for use in TLS.
+
+If ``clock_gettime`` is available on the system, hres_timer will poll all
+the available clock types.
+
+AltiVec is now detected on IBM POWER7 processors and on OpenBSD systems.
+The OpenBSD support was contributed by Brad Smith.
+
+The Qt mutex wrapper was broken and would not compile with any recent
+version of Qt. Taking this as a clear indication that it is not in use,
+it has been removed.
+
+Avoid setting the soname on OpenBSD, as it doesn't support it (Bugzilla 158)
+
+A compilation problem in the dynamic loader that prevented using
+dyn_load under MinGW GCC has been fixed.
+
+A common error for people using MinGW is to target GCC on Windows,
+however the 'Windows' target assumes the existence of Visual C++
+runtime functions which do not exist in MinGW. Now, configuring for
+GCC on Windows will cause the configure.py to warn that likely you
+wanted to configure for either MinGW or Cygwin, not the generic
+Windows target.
+
+A bug in configure.py would cause it to interpret ``--cpu=s390x`` as
+``s390``. This may have affected other CPUs as well. Now configure.py
+searches for an exact match, and only if no exact match is found will
+it search for substring matches.
+
+An incompatibility in configure.py with the subprocess module included
+in Python 3.1 has been fixed (Bugzilla 157).
+
+The exception catching syntax of configure.py has been changed to the
+Python 3.x syntax. This syntax also works with Python 2.6 and 2.7, but
+not with any earlier Python 2 release. A simple search and replace
+will allow running it under Python 2.5:
+``perl -pi -e 's/except (.*) as (.*):/except $1, $2:/g' configure.py``
+
+Note that Python 2.4 is not supported at all.
+
+Version 1.10.1, 2011-07-11
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* A race condition in ``Algorithm_Factory`` could cause crashes in
+ multithreaded code.
+
+* The return value of ``name`` has changed for GOST 28147-89 and
+ Skein-512. GOST's ``name`` now includes the name of the sbox, and
+ Skein's includes the personalization string (if nonempty). This
+ allows an object to be properly roundtripped, which is necessary to
+ fix the race condition described above.
+
+* A new distribution script is now included, as
+ ``src/build-data/scripts/dist.py``
+
+* The ``build.h`` header now includes, if available, an identifier of
+ the source revision that was used. This identifier is also included
+ in the result of ``version_string``.
+
+Version 1.8.13, 2011-07-02
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* A race condition in ``Algorithm_Factory`` could cause crashes in
+ multithreaded code.
+
+Version 1.10.0, 2011-06-20
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Detection for the rdrand instruction being added to upcoming Intel
+ Ivy Bridge processors has been added.
+
+* A template specialization of std::swap was added for the memory
+ container types.
+
+Version 1.8.12, 2011-06-20
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+* If EMSA3(Raw) was used for more than one signature, it would produce
+ incorrect output.
+
+* Fix the --enable-debug option to configure.py
+
+* Improve OS detection on Cygwin
+
+* Fix compilation under Sun Studio 12 on Solaris
+
+* Fix a memory leak in the constructors of DataSource_Stream and
+ DataSink_Stream which would occur if opening the file failed (Bugzilla 144)
+
+Version 1.9.18, 2011-06-03
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Fourth release candidate for 1.10.0
+
+* The GOST 34.10 verification operation was not ensuring that s and r
+ were both greater than zero. This could potentially have meant it
+ would have accepted an invalid all-zero signature as valid for any
+ message. Due to how ECC points are internally represented it instead
+ resulted in an exception being thrown.
+
+* A simple multiexponentation algorithm is now used in ECDSA and
+ GOST-34.10 signature verification, leading to 20 to 25% improvements
+ in ECDSA and 25% to 40% improvements in GOST-34.10 verification
+ performance.
+
+* The internal representation of elliptic curve points has been
+ modified to use Montgomery representation exclusively, resulting in
+ reduced memory usage and a 10 to 20% performance improvement for
+ ECDSA and ECDH.
+
+* In OAEP decoding, scan for the delimiter bytes using a loop that is
+ written without conditionals so as to help avoid timing analysis.
+ Unfortunately GCC at least is 'smart' enough to compile it to
+ jumps anyway.
+
+* The SSE2 implementation of IDEA did not work correctly when compiled
+ by Clang, because the trick it used to emulate a 16 bit unsigned
+ compare in SSE (which doesn't contain one natively) relied on signed
+ overflow working in the 'usual' way. A different method that doesn't
+ rely on signed overflow is now used.
+
+* Add support for compiling SSL using Visual C++ 2010's TR1
+ implementation.
+
+* Fix a bug under Visual C++ 2010 which would cause ``hex_encode`` to
+ crash if given a zero-sized input to encode.
+
+* A new build option ``--via-amalgamation`` will first generate the
+ single-file amalgamation, then build the library from that single
+ file. This option requires a lot of memory and does not parallelize,
+ but the resulting library is smaller and may be faster.
+
+* On Unix, the library and header paths have been changed to allow
+ parallel installation of different versions of the library. Headers
+ are installed into ``<prefix>/include/botan-1.9/botan``, libraries
+ are named ``libbotan-1.9``, and ``botan-config`` is now namespaced
+ (so in this release ``botan-config-1.9``). All of these embedded
+ versions will be 1.10 in the upcoming stable release.
+
+* The soname system has been modified. In this release the library
+ soname is ``libbotan-1.9.so.0``, with the full library being named
+ ``libbotan-1.9.so.0.18``. The ``0`` is the ABI version, and will be
+ incremented whenever a breaking ABI change is made.
+
+* TR1 support is not longer automatically assumed under older versions
+ of GCC
+
+* Functions for base64 decoding that work standalone (without needing
+ to use a pipe) have been added to ``base64.h``
+
+* The function ``BigInt::to_u32bit`` was inadvertently removed in 1.9.11
+ and has been added back.
+
+* The function ``BigInt::get_substring`` did not work correctly with a
+ *length* argument of 32.
+
+* The implementation of ``FD_ZERO`` on Solaris uses ``memset`` and
+ assumes the caller included ``string.h`` on its behalf. Do so to
+ fix compilation in the ``dev_random`` and ``unix_procs`` entropy
+ sources. Patch from Jeremy C. Reed.
+
+* Add two different configuration targets for Atom, since some are
+ 32-bit and some are 64-bit. The 'atom' target now refers to the
+ 64-bit implementations, use 'atom32' to target the 32-bit
+ processors.
+
+* The (incomplete) support for CMS and card verifiable certificates
+ are disabled by default; add ``--enable-modules=cms`` or
+ ``--enable-modules=cvc`` during configuration to turn them back on.
+
+Version 1.9.17, 2011-04-29
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Third release candidate for 1.10.0
+
+* The format preserving encryption method currently available was
+ presented in the header ``fpe.h`` and the functions ``fpe_encrypt``
+ and ``fpe_decrypt``. These were renamed as it is likely that other
+ FPE schemes will be included in the future. The header is now
+ ``fpe_fe1.h``, and the functions are named ``fe1_encrypt`` and
+ ``fe1_decrypt``.
+
+* New options to ``configure.py`` control what tools are used for
+ documentation generation. The ``--with-sphinx`` option enables using
+ Sphinx to convert ReST into HTML; otherwise the ReST sources are
+ installed directly. If ``--with-doxygen`` is used, Doxygen will run
+ as well. Documentation generation can be triggered via the ``docs``
+ target in the makefile; it will also be installed by the install
+ target on Unix.
+
+* A bug in 1.9.16 effectively disabled support for runtime CPU feature
+ detection on x86 under GCC in that release.
+
+* A mostly internal change, all references to "ia32" and "amd64" have
+ been changed to the vendor neutral and probably easier to understand
+ "x86-32" and "x86-64". For instance, the "mp_amd64" module has been
+ renamed "mp_x86_64", and the macro indicating x86-32 has changed
+ from ``BOTAN_TARGET_ARCH_IS_IA32`` to
+ ``BOTAN_TARGET_ARCH_IS_X86_32``. The classes calling assembly have
+ also been renamed.
+
+* Similiarly to the above change, the AES implemenations using the
+ AES-NI instruction set have been renamed from AES_XXX_Intel to
+ AES_XXX_NI.
+
+* Systems that are identified as ``sun4u`` will default to compiling for
+ 32-bit SPARCv9 code rather than 64-bit. This matches the still
+ common convention for 32-bit SPARC userspaces. If you want 64-bit
+ code on such as system, use ``--cpu=sparc64``.
+
+* Some minor fixes for compiling botan under the BeOS
+ clone/continuation `Haiku <http://haiku-os.org>`_.
+
+* Further updates to the documentation
+
+Version 1.9.16, 2011-04-11
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Second release candidate for 1.10.0
+
+* The documentation, previously written in LaTeX, is now in
+ reStructuredText suitable for processing by `Sphinx
+ <http://sphinx.pocoo.org>`_, which can generate nicely formatted
+ HTML and PDFs. The documentation has also been greatly updated and
+ expanded.
+
+* The class ``EC_Domain_Params`` has been renamed ``EC_Group``, with a
+ typedef for backwards compatibility.
+
+* The ``EC_Group`` string constructor didn't understand the standard
+ names like "secp160r1", forcing use of the OIDs.
+
+* Two constructors for ECDSA private keys, the one that creates a new
+ random key, and the one that provides a preset private key as a
+ ``BigInt``, have been merged. This matches the existing interface
+ for DSA and DH keys. If you previously used the version taking a
+ ``BigInt`` private key, you'll have to additionally pass in a
+ ``RandomNumberGenerator`` object starting in this release.
+
+* It is now possible to create ECDH keys with a preset ``BigInt``
+ private key; previously no method for this was available.
+
+* The overload of ``generate_passhash9`` that takes an explicit
+ algorithm identifier has been merged with the one that does not.
+ The algorithm identifier code has been moved from the second
+ parameter to the fourth.
+
+* Change shared library versioning to match the normal Unix
+ conventions. Instead of ``libbotan-X.Y.Z.so``, the shared lib is
+ named ``libbotan-X.Y.so.Z``; this allows the runtime linker to do
+ its runtime linky magic. It can be safely presumed that any change
+ in the major or minor version indicates ABI incompatibility.
+
+* Remove the socket wrapper code; it was not actually used by anything
+ in the library, only in the examples, and you can use whatever kind
+ of (blocking) socket interface you like with the SSL/TLS code. It's
+ available as socket.h in the examples directory if you want to use
+ it.
+
+* Disable the by-default 'strong' checking of private keys that are
+ loaded from storage. You can always request key material sanity
+ checking using Private_Key::check_key.
+
+* Bring back removed functions ``min_keylength_of``,
+ ``max_keylength_of``, ``keylength_multiple_of`` in ``lookup.h`` to
+ avoid breaking applications written against 1.8
+
+Version 1.9.15, 2011-03-21
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* First release candidate for 1.10.0
+
+* Modify how message expansion is done in SHA-256 and SHA-512.
+ Instead of expanding the entire message at the start, compute them
+ in the minimum number of registers. Values are computed 15 rounds
+ before they are needed. On a Core i7-860, GCC 4.5.2, went from 143
+ to 157 MiB/s in SHA-256, and 211 to 256 MiB/s in SHA-512.
+
+* Pipe will delete empty output queues as soon as they are no longer
+ needed, even if earlier messages still have data unread. However an
+ (empty) entry in a deque of pointers will remain until all prior
+ messages are completely emptied.
+
+* Avoid reading the SPARC ``%tick`` register on OpenBSD as unlike the
+ Linux and NetBSD kernels, it will not trap and emulate it for us,
+ causing a illegal instruction crash.
+
+* Improve detection and autoconfiguration for ARM processors. Thanks
+ go out to the the `Tahoe-LAFS Software Foundation
+ <http://tahoe-lafs.org>`_, who donated a Sheevaplug that I'll be
+ using to figure out how to make the cryptographic primitives
+ Tahoe-LAFS relies on faster, particularly targeting the ARMv5TE.
+
+Version 1.9.14, 2011-03-01
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Add support for bcrypt, OpenBSD's password hashing scheme.
+
+* Add support for NIST's AES key wrapping algorithm, as described in
+ :rfc:`3394`. It is available by including ``rfc3394.h``.
+
+* Fix an infinite loop in zlib filters introduced in 1.9.11 (Bugzilla 142)
+
+Version 1.9.13, 2011-02-19
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+GOST 34.10 signatures were being formatted in a way that was not
+compatible with other implemenations, and specifically how GOST is
+used in DNSSEC.
+
+The Keccak hash function was updated to the tweaked variant proposed
+for round 3 of the NIST hash competition. This version is not
+compatible with the previous algorithm.
+
+A new option ``--distribution-info`` was added to the configure
+script. It allows the user building the library to set any
+distribution-specific notes on the build, which are available as a
+macro ``BOTAN_DISTRIBUTION_INFO``. The default value is
+'unspecified'. If you are building an unmodified version of botan
+(especially for distribution), and want to indicate to applications
+that this is the case, consider using
+``--distribution-info=pristine``. If you are making any patches or
+modifications, it is recommended to use
+``--distribution-info=[Distribution Name] [Version]``, for instance
+'FooNix 1.9.13-r3'.
+
+Some bugs preventing compilation under Clang 2.9 and Sun Studio 12
+were fixed.
+
+The DER/BER codecs use ``size_t`` instead of ``u32bit`` for small
+integers
+
+Version 1.9.12, 2010-12-13
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Add the Keccak hash function
+* Fix compilation problems in Python wrappers
+* Fix compilation problem in OpenSSL engine
+* Update SQLite3 database encryption codec
+
+Version 1.9.11, 2010-11-29
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* The TLS API has changed substantially and now relies heavily on
+ TR1's ``std::function`` is now required. Additionally, it is
+ required that all callers derive a subclass of TLS_Policy and pass
+ it to a client or server object. Please remember that the TLS
+ interface/API is currently unstable and will very likely change
+ further before TLS is included in a stable release. A handshake
+ failure that occurred when RC4 was negotiated has also been fixed.
+
+* Some possible timing channels in the implementations of Montgomery
+ reduction and the IDEA key schedule were removed. The table-based
+ AES implementation uses smaller tables in the first round to help
+ make some timing/cache attacks harder.
+
+* The library now uses size_t instead of u32bit to represent
+ lengths. Also the interfaces for the memory containers have changed
+ substantially to better match STL container interfaces;
+ MemoryRegion::append, MemoryRegion::destroy, and MemoryRegion::set
+ were all removed, and several other functions, like clear and
+ resize, have changed meaning.
+
+* Update Skein-512 to match the v1.3 specification
+* Fix a number of CRL encoding and decoding bugs
+* Counter mode now always encrypts 256 blocks in parallel
+* Use small tables in the first round of AES
+* Removed AES class: app must choose AES-128, AES-192, or AES-256
+* Add hex encoding/decoding functions that can be used without a Pipe
+* Add base64 encoding functions that can be used without a Pipe
+* Add to_string function to X509_Certificate
+* Add support for dynamic engine loading on Windows
+* Replace BlockCipher::BLOCK_SIZE attribute with function block_size()
+* Replace HashFunction::HASH_BLOCK_SIZE attribute with hash_block_size()
+* Move PBKDF lookup to engine system
+* The IDEA key schedule has been changed to run in constant time
+* Add Algorithm and Key_Length_Specification classes
+* Switch default PKCS #8 encryption algorithm from AES-128 to AES-256
+* Allow using PBKDF2 with empty passphrases
+* Add compile-time deprecation warnings for GCC, Clang, and MSVC
+* Support use of HMAC(SHA-256) and CMAC(Blowfish) in passhash9
+* Improve support for Intel Atom processors
+* Fix compilation problems under Sun Studio and Clang
+
+Version 1.8.11, 2010-11-02
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Fix a number of CRL encoding and decoding bugs
+* When building a debug library under VC++, use the debug runtime
+* Fix compilation under Sun Studio on Linux and Solaris
+* Add several functions for compatibility with 1.9
+* In the examples, read most input files as binary
+* The Perl build script has been removed in this release
+
+Version 1.8.10, 2010-08-31
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Switch default PKCS #8 encryption algorithm from 3DES to AES-256
+* Increase default hash iterations from 2048 to 10000 in PBES1 and PBES2
+* Use small tables in the first round of AES
+* Add PBKDF typedef and get_pbkdf for better compatibility with 1.9
+* Add version of S2K::derive_key taking salt and iteration count
+* Enable the /proc-walking entropy source on NetBSD
+* Fix the doxygen makefile target
+
+Version 1.9.10, 2010-08-12
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Add a constant-time AES implementation using SSSE3. This code is
+ based on public domain assembly written by `Mike Hamburg
+ <http://crypto.stanford.edu/vpaes/>`_, and described in his CHES
+ 2009 paper "Accelerating AES with Vector Permute Instructions". In
+ addition to being constant time, it is also significantly faster
+ than the table-based implementation on some processors. The current
+ code has been tested with GCC 4.5, Visual C++ 2008, and Clang 2.8.
+
+* Support for dynamically loading Engine objects at runtime was also
+ added. Currently only system that use ``dlopen``-style dynamic
+ linking are supported.
+
+* On GCC 4.3 and later, use the byteswap intrinsic functions.
+
+* Drop support for building with Python 2.4
+
+* Fix benchmarking of block ciphers in ECB mode
+
+* Consolidate the two x86 assembly engines
+
+* Rename S2K to PBKDF
+
+Version 1.9.9, 2010-06-28
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+A new pure virtual function has been added to ``Filter``, ``name``
+which simply returns some useful identifier for the object. Any
+out-of-tree ``Filter`` implementations will need to be updated.
+
+Add ``Keyed_Filter::valid_iv_length`` which makes it possible to query
+as to what IV length(s) a particular filter allows. Previously,
+partially because there was no such query mechanism, if a filter did
+not support IVs at all, then calls to ``set_iv`` would be silently
+ignored. Now an exception about the invalid IV length will be thrown.
+
+The default iteration count for the password based encryption schemes
+has been increased from 2048 to 10000. This should make
+password-guessing attacks against private keys encrypted with versions
+after this release somewhat harder.
+
+New functions for encoding public and private keys to binary,
+``X509::BER_encode`` and ``PKCS8::BER_encode`` have been added.
+
+Problems compiling under Apple's version of GCC 4.2.1 and on 64-bit
+MIPS systems using GCC 4.4 or later were fixed.
+
+The coverage of Doxygen documentation comments has significantly
+improved in this release.
+
+Version 1.8.9, 2010-06-16
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Use constant time multiplication in IDEA
+
+* Avoid possible timing attack against OAEP decoding
+
+* Add new X509::BER_encode and PKCS8::BER_encode
+
+* Enable DLL builds under Windows
+
+* Add Win32 installer support
+
+* Add support for the Clang compiler
+
+* Fix problem in semcem.h preventing build under Clang or GCC 3.4
+
+* Fix bug that prevented creation of DSA groups under 1024 bits
+
+* Fix crash in GMP_Engine if library is shutdown and reinitialized and
+ a PK algorithm was used after the second init
+
+* Work around problem with recent binutils in x86-64 SHA-1
+
+* The Perl build script is no longer supported and refuses to run by
+ default. If you really want to use it, pass
+ ``--i-know-this-is-broken`` to the script.
+
+Version 1.9.8, 2010-06-14
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Add support for wide multiplications on 64-bit Windows
+* Use constant time multiplication in IDEA
+* Avoid possible timing attack against OAEP decoding
+* Removed FORK-256; rarely used and it has been broken
+* Rename ``--use-boost-python`` to ``--with-boost-python``
+* Skip building shared libraries on MinGW/Cygwin
+* Fix creation of 512 and 768 bit DL groups using the DSA kosherizer
+* Fix compilation on GCC versions before 4.3 (missing cpuid.h)
+* Fix compilation under the Clang compiler
+
+Version 1.9.7, 2010-04-27
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* TLS: Support reading SSLv2 client hellos
+* TLS: Add support for SEED ciphersuites (RFC 4162)
+* Add Comb4P hash combiner function
+
+* Fix checking of EMSA_Raw signatures with leading 0 bytes, valid
+ signatures could be rejected in certain scenarios.
+
+Version 1.9.6, 2010-04-09
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* TLS: Add support for TLS v1.1
+* TLS: Support server name indicator extension
+* TLS: Fix server handshake
+* TLS: Fix server using DSA certificates
+* TLS: Avoid timing channel between CBC padding check and MAC verification
+
+Version 1.9.5, 2010-03-29
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Numerous ECC optimizations
+* Fix GOST 34.10-2001 X.509 key loading
+* Allow PK_Signer's fault protection checks to be toggled off
+* Avoid using pool-based locking allocator if we can't mlock
+* Remove all runtime options
+* New BER_Decoder::{decode_and_check, decode_octet_string_bigint}
+* Remove SecureBuffer in favor of SecureVector length parameter
+* HMAC_RNG: Perform a poll along with user-supplied entropy
+* Fix crash in MemoryRegion if Allocator::get failed
+* Fix small compilation problem on FreeBSD
+
+Version 1.9.4, 2010-03-09
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Add the Ajisai SSLv3/TLSv1.0 implementation
+
+* Add GOST 34.10-2001 public key signature scheme
+* Add SIMD implementation of Noekeon
+
+* Add SSE2 implementation of IDEA
+
+* Extend Salsa20 to support longer IVs (XSalsa20)
+
+* Perform XTS encryption and decryption in parallel where possible
+
+* Perform CBC decryption in parallel where possible
+
+* Add SQLite3 db encryption codec, contributed by Olivier de Gaalon
+
+* Add a block cipher cascade construction
+
+* Add support for password hashing for authentication (passhash9.h)
+
+* Add support for Win32 high resolution system timers
+
+* Major refactoring and API changes in the public key code
+
+* PK_Signer class now verifies all signatures before releasing them to
+ the caller; this should help prevent a wide variety of fault
+ attacks, though it does have the downside of hurting signature
+ performance, particularly for DSA/ECDSA.
+
+* Changed S2K interface: derive_key now takes salt, iteration count
+
+* Remove dependency on TR1 shared_ptr in ECC and CVC code
+
+* Renamed ECKAEG to its more usual name, ECDH
+
+* Fix crash in GMP_Engine if library is shutdown and reinitialized
+
+* Fix an invalid memory read in MD4
+
+* Fix Visual C++ static builds
+
+* Remove Timer class entirely
+
+* Switch default PKCS #8 encryption algorithm from 3DES to AES-128
+
+* New configuration option, ``--gen-amalgamation``, creates a pair of
+ files (``botan_all.cpp`` and ``botan_all.h``) which contain the
+ contents of the library as it would have normally been compiled
+ based on the set configuration.
+
+* Many headers are now explicitly internal-use-only and are not installed
+
+* Greatly improve the Win32 installer
+
+* Several fixes for Visual C++ debug builds
+
+Version 1.9.3, 2009-11-19
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Add new AES implementation using Intel's AES instruction intrinsics
+* Add an implementation of format preserving encryption
+* Allow use of any hash function in X.509 certificate creation
+* Optimizations for MARS, Skipjack, and AES
+* Set macros for available SIMD instructions in build.h
+* Add support for using InnoSetup to package Windows builds
+* By default build a DLL on Windows
+
+Version 1.8.8, 2009-11-03
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Alter Skein-512 to match the tweaked 1.2 specification
+* Fix use of inline asm for access to x86 bswap function
+* Allow building the library without AES enabled
+* Add 'powerpc64' alias to ppc64 arch for Gentoo ebuild
+
+Version 1.9.2, 2009-11-03
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Add SIMD version of XTEA
+* Support both SSE2 and AltiVec SIMD for Serpent and XTEA
+* Optimizations for SHA-1 and SHA-2
+* Add AltiVec runtime detection
+* Fix x86 CPU identification with Intel C++ and Visual C++
+
+Version 1.9.1, 2009-10-23
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Better support for Python and Perl wrappers
+* Add an implementation of Blue Midnight Wish (Round 2 tweak version)
+* Modify Skein-512 to match the tweaked 1.2 specification
+* Add threshold secret sharing (draft-mcgrew-tss-02)
+* Add runtime cpu feature detection for x86/x86-64
+* Add code for general runtime self testing for hashes, MACs, and ciphers
+* Optimize XTEA; twice as fast as before on Core2 and Opteron
+* Convert CTR_BE and OFB from filters to stream ciphers
+* New parsing code for SCAN algorithm names
+* Enable SSE2 optimizations under Visual C++
+* Remove all use of C++ exception specifications
+* Add support for GNU/Hurd and Clang/LLVM
+
+Version 1.8.7, 2009-09-09
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Fix processing multiple messages in XTS mode
+* Add --no-autoload option to configure.py, for minimized builds
+
+Version 1.9.0, 2009-09-09
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Add support for parallel invocation of block ciphers where possible
+* Add SSE2 implementation of Serpent
+* Add Rivest's package transform (an all or nothing transform)
+* Minor speedups to the Turing key schedule
+* Fix processing multiple messages in XTS mode
+* Add --no-autoload option to configure.py, for minimized builds
+* The previously used configure.pl script is no longer supported
+
+Version 1.8.6, 2009-08-13
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Add Cryptobox, a set of simple password-based encryption routines
+* Only read world-readable files when walking /proc for entropy
+* Fix building with TR1 disabled
+* Fix x86 bswap support for Visual C++
+* Fixes for compilation under Sun C++
+* Add support for Dragonfly BSD (contributed by Patrick Georgi)
+* Add support for the Open64 C++ compiler
+* Build fixes for MIPS systems running Linux
+* Minor changes to license, now equivalent to the FreeBSD/NetBSD license
+
+Version 1.8.5, 2009-07-23
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Change configure.py to work on stock Python 2.4
+* Avoid a crash in Skein_512::add_data processing a zero-length input
+* Small build fixes for SPARC, ARM, and HP-PA processors
+* The test suite now returns an error code from main() if any tests failed
+
+Version 1.8.4, 2009-07-12
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Fix a bug in nonce generation in the Miller-Rabin test
+
+Version 1.8.3, 2009-07-11
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Add a new Python configuration script
+* Add the Skein-512 SHA-3 candidate hash function
+* Add the XTS block cipher mode from IEEE P1619
+* Fix random_prime when generating a prime of less than 7 bits
+* Improve handling of low-entropy situations during PRNG seeding
+* Change random device polling to prefer /dev/urandom over /dev/random
+* Use an input insensitive implementation of same_mem instead of memcmp
+* Correct DataSource::discard_next to return the number of discarded bytes
+* Provide a default value for AutoSeeded_RNG::reseed
+* Fix Gentoo bug 272242
+
+Version 1.8.2, 2009-04-07
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Make entropy polling more flexible and in most cases faster
+* GOST 28147 now supports multiple sbox parameters
+* Added the GOST 34.11 hash function
+* Fix botan-config problems on MacOS X
+
+Version 1.8.1, 2009-01-20
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Avoid a valgrind warning in es_unix.cpp on 32-bit Linux
+* Fix memory leak in PKCS8 load_key and encrypt_key
+* Relicense api.tex from CC-By-SA 2.5 to BSD
+* Fix botan-config on MacOS X, Solaris
+
+Version 1.8.0, 2008-12-08
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Fix compilation on Solaris with GCC
+
+Version 1.7.24, 2008-12-01
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Fix a compatibility problem with SHA-512/EMSA3 signature padding
+* Fix bug preventing EGD/PRNGD entropy poller from working
+* Fix integer overflow in Pooling_Allocator::get_more_core (bug id #27)
+* Add EMSA3_Raw, a variant of EMSA3 called CKM_RSA_PKCS in PKCS #11
+* Add support for SHA-224 in EMSA2 and EMSA3 PK signature padding schemes
+* Add many more test vectors for RSA with EMSA2, EMSA3, and EMSA4
+* Wrap private structs in SSE2 SHA-1 code in anonymous namespace
+* Change configure.pl's CPU autodetection output to be more consistent
+* Disable using OpenSSL's AES due to crashes of unknown cause
+* Fix warning in /proc walking entropy poller
+* Fix compilation with IBM XLC for Cell 0.9-200709
+
+Version 1.7.23, 2008-11-23
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Change to use TR1 (thus enabling ECDSA) with GCC and ICC
+* Optimize almost all hash functions, especially MD4 and Tiger
+* Add configure.pl options --{with,without}-{bzip2,zlib,openssl,gnump}
+* Change Timer to be pure virtual, and add ANSI_Clock_Timer
+* Cache socket descriptors in the EGD entropy source
+* Avoid bogging down startup in /proc walking entropy source
+* Remove Buffered_EntropySource helper class
+* Add a Default_Benchmark_Timer typedef in benchmark.h
+* Add examples using benchmark.h and Algorithm_Factory
+* Add ECC tests from InSiTo
+* Minor documentation updates
+
+Version 1.7.22, 2008-11-17
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Add provider preferences to Algorithm_Factory
+* Fix memory leaks in PBE_PKCS5v20 and get_pbe introduced in 1.7.21
+* Optimize AES encryption and decryption (about 10% faster)
+* Enable SSE2 optimized SHA-1 implementation on Intel Prescott CPUs
+* Fix nanoseconds overflow in benchmark code
+* Remove Engine::add_engine
+
+Version 1.7.21, 2008-11-11
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Make algorithm lookup much more configuable
+* Add facilities for runtime performance testing of algorithms
+* Drop use of entropy estimation in the PRNGs
+* Increase intervals between HMAC_RNG automatic reseeding
+* Drop InitializerOptions class, all options but thread safety
+
+Version 1.7.20, 2008-11-09
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Namespace pkg-config file by major and minor versions
+* Cache device descriptors in Device_EntropySource
+* Split base.h into {block_cipher,stream_cipher,mac,hash}.h
+* Removed get_mgf function from lookup.h
+
+Version 1.7.19, 2008-11-06
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Add HMAC_RNG, based on a design by Hugo Krawczyk
+* Optimized the Turing stream cipher (about 20% faster on x86-64)
+* Modify Randpool's reseeding algorithm to poll more sources
+* Add a new AutoSeeded_RNG in auto_rng.h
+* OpenPGP_S2K changed to take hash object instead of name
+* Add automatic identification for Intel's Prescott processors
+
+Version 1.7.18, 2008-10-22
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Add Doxygen comments from InSiTo
+* Add ECDSA and ECKAEG benchmarks
+* Add configure.pl switch --with-tr1-implementation
+* Fix configure.pl's --with-endian and --with-unaligned-mem options
+* Added support for pkg-config
+* Optimize byteswap with x86 inline asm for Visual C++ by Yves Jerschow
+* Use const references to avoid copying overhead in CurveGFp, GFpModulus
+
+Version 1.7.17, 2008-10-12
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Add missing ECDSA object identifiers
+* Fix error in x86 and x86-64 assembler affecting GF(p) math
+* Remove Boost dependency from GF(p) math
+* Modify botan-config to not print -L/usr/lib or -L/usr/local/lib
+* Add BOTAN_DLL macro to over 30 classes missing it
+* Rename the two SHA-2 base classes for consistency
+
+Version 1.7.16, 2008-10-09
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Add several missing pieces needed for ECDSA and ECKAEG
+* Add Card Verifiable Certificates from InSiTo
+* Add SHA-224 from InSiTo
+* Add BSI variant of EMSA1 from InSiTo
+* Add GF(p) and ECDSA tests from InSiTo
+* Split ECDSA and ECKAEG into distinct modules
+* Allow OpenSSL and GNU MP engines to be built with public key algos disabled
+* Rename sha256.h to sha2_32.h and sha_64.h to sha2_64.h
+
+Version 1.7.15, 2008-10-07
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Add GF(p) arithmetic from InSiTo
+* Add ECDSA and ECKAEG implementations from InSiTo
+* Minimize internal dependencies, allowing for smaller build configurations
+* Add new User Manual and Architecture Guide from FlexSecure GmbH
+* Alter configure.pl options for better autotools compatibility
+* Update build instructions for recent changes to configure.pl
+* Fix CPU detection using /proc/cpuinfo
+
+Version 1.7.14, 2008-09-30
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Split library into parts allowing modular builds
+* Add (very preliminary) CMS support to the main library
+* Some constructors now require object pointers instead of names
+* Support multiple implementations of the same algorithm
+* Build support for Pentium-M processors, from Derek Scherger
+* Build support for MinGW/MSYS, from Zbigniew Zagorski
+* Use inline assembly for bswap on 32-bit x86
+
+Version 1.7.13, 2008-09-27
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Add SSLv3 MAC, SSLv3 PRF, and TLS v1.0 PRF from Ajisai
+* Allow all examples to compile even if compression not enabled
+* Make CMAC's polynomial doubling operation a public class method
+* Use the -m64 flag when compiling with Sun Forte on x86-64
+* Clean up and slightly optimize CMAC::final_result
+
+Version 1.7.12, 2008-09-18
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Add x86 assembly for Visual Studio C++, by Luca Piccarreta
+* Add a Perl XS module, by Vaclav Ovsik
+* Add SWIG-based wrapper for Botan
+* Add SSE2 implementation of SHA-1, by Dean Gaudet
+* Remove the BigInt::sig_words cache due to bugs
+* Combined the 4 Blowfish sboxes, suggested by Yves Jerschow
+* Changed BigInt::grow_by and BigInt::grow_to to be non-const
+* Add private assignment operators to classes that don't support assignment
+* Benchmark RSA encryption and signatures
+* Added test programs for random_prime and ressol
+* Add high resolution timers for IA-64, HP-PA, S390x
+* Reduce use of the RNG during benchmarks
+* Fix builds on STI Cell PPU
+* Add support for IBM's XLC compiler
+* Add IETF 8192 bit MODP group
+
+Version 1.7.11, 2008-09-11
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Added the Salsa20 stream cipher
+* Optimized Montgomery reduction, Karatsuba squaring
+* Added 16x16->32 word Comba multiplication and squaring
+* Use a much larger Karatsuba cutoff point
+* Remove bigint_mul_add_words
+* Inlined several BigInt functions
+* Add useful information to the generated build.h
+* Rename alg_{ia32,amd64} modules to asm_{ia32,amd64}
+* Fix the Windows build
+
+Version 1.7.10, 2008-09-05
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Public key benchmarks run using a selection of random keys
+* New benchmark timer options are clock_gettime, gettimeofday, times, clock
+* Including reinterpret_cast optimization for xor_buf in default header
+* Split byte swapping and word rotation functions into distinct headers
+* Add IETF modp 6144 group and 2048 and 3072 bit DSS groups
+* Optimizes BigInt right shift
+* Add aliases in DL_Group::Format enum
+* BigInt now caches the significant word count
+
+Version 1.6.5, 2008-08-27
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Add noexec stack marker for GNU linker in assembly code
+* Fix autoconfiguration problem on x86 with GCC 4.2 and 4.3
+
+Version 1.7.9, 2008-08-27
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Make clear() in most algorithm base classes a pure virtual
+* Add noexec stack marker for GNU linker in assembly code
+* Avoid string operations in ressol
+* Compilation fixes for MinGW and Visual Studio C++ 2008
+* Some autoconfiguration fixes for Windows
+
+Version 1.7.8, 2008-07-15
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Added the block cipher Noekeon
+* Remove global deref_alias function
+* X509_Store takes timeout options as constructor arguments
+* Add Shanks-Tonelli algorithm, contributed by FlexSecure GmbH
+* Extend random_prime() for generating primes of any bit length
+* Remove Config class
+* Allow adding new entropy via base RNG interface
+* Reseeding a X9.31 PRNG also reseeds the underlying PRNG
+
+Version 1.7.7, 2008-06-28
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Remove the global PRNG object
+* The PK filter objects were removed
+* Add a test suite for the ANSI X9.31 PRNG
+* Much cleaner and (mostly) thread-safe reimplementation of es_ftw
+* Remove both default arguments to ANSI_X931_RNG's constructor
+* Remove the randomizing version of OctetString::change
+* Make the cipher and MAC to use in Randpool configurable
+* Move RandomNumberGenerator declaration to rng.h
+* RSA_PrivateKey will not generate keys smaller than 1024 bits
+* Fix an error decoding BER UNIVERSAL types with special taggings
+
+Version 1.7.6, 2008-05-05
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Initial support for Windows DLLs, from Joel Low
+* Reset the position pointer when a new block is generated in X9.32 PRNG
+* Timer objects are now treated as entropy sources
+* Moved several ASN.1-related enums from enums.h to an appropriate header
+* Removed the AEP module, due to inability to test
+* Removed Global_RNG and rng.h
+* Removed system_clock
+* Removed Library_State::UI and the pulse callback logic
+
+Version 1.7.5, 2008-04-12
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* The API of X509_CA::sign_request was altered to avoid race conditions
+* New type Pipe::message_id to represent the Pipe message number
+* Remove the Named_Mutex_Holder for a small performance gain
+* Removed several unused or rarely used functions from Config
+* Ignore spaces inside of a decimal string in BigInt::decode
+* Allow using a std::istream to initialize a DataSource_Stream object
+* Fix compilation problem in zlib compression module
+* The chunk sized used by Pooling_Allocator is now a compile time setting
+* The size of random blinding factors is now a compile time setting
+* The install target no longer tries to set a particular owner/group
+
+Version 1.7.4, 2008-03-10
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Use unaligned memory read/writes on systems that allow it, for performance
+* Assembly for x86-64 for accessing the bswap instruction
+* Use larger buffers in ARC4 and WiderWAKE for significant throughput increase
+* Unroll loops in SHA-160 for a few percent increase in performance
+* Fix compilation with GCC 3.2 in es_ftw and es_unix
+* Build fix for NetBSD systems
+* Prevent es_dev from being built except on Unix systems
+
+Version 1.6.4, 2008-03-08
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Fix a compilation problem with Visual Studio C++ 2003
+
+Version 1.7.3, 2008-01-23
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* New invocation syntax for configure.pl with several new options
+* Support for IPv4 addresses in a subject alternative name
+* New fast poll for the generic Unix entropy source (es_unix)
+* The es_file entropy source has been replaced by the es_dev module
+* The malloc allocator does not inherit from Pooling_Allocator anymore
+* The path that es_unix will search in are now fully user-configurable
+* Truncate X9.42 PRF output rather than allow counter overflow
+* PowerPC is now assumed to be big-endian
+
+Version 1.7.2, 2007-10-13
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Initialize the global library state lazily
+* Add plain CBC-MAC for backwards compatibility with old systems
+* Clean up some of the self test code
+* Throw a sensible exception if a DL_Group is not found
+* Truncate KDF2 output rather than allowing counter overflow
+* Add newly assigned OIDs for SHA-2 and DSA with SHA-224/256
+* Fix a Visual Studio compilation problem in x509stat.cpp
+
+Version 1.6.3, 2007-07-23
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Fix a race condition in the algorithm lookup cache
+* Fix problems building the memory pool on some versions of Visual C++
+
+Version 1.7.1, 2007-07-23
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Fix a race condition in the algorithm object cache
+* HMAC key schedule optimization
+* The build header sets a macro defining endianness, if known
+* New word load/store abstraction allowing further optimization
+* Modify most of the library to avoid use the C-style casts
+* Use higher resolution timers in symmetric benchmarks
+
+Version 1.7.0, 2007-05-19
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* DSA parameter generation now follows FIPS 186-3
+* Added OIDs for Rabin-Williams and Nyberg-Rueppel
+* Somewhat better support for out of tree builds
+* Minor optimizations for RC2 and Tiger
+* Documentation updates
+* Update the todo list
+
+Version 1.6.2, 2007-03-24
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Fix autodection on Athlon64s running Linux
+* Fix builds on QNX and compilers using STLport
+* Remove a call to abort() that crept into production
+
+Version 1.6.1, 2007-01-20
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Fix some base64 decoder bugs
+* Add a new option to base64 encoding, to always append a newline
+* Fix some build problems under Visual Studio with debug enabled
+* Fix a bug in BER_Decoder that was triggered under some compilers
+
+Version 1.6.0, 2006-12-17
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Minor cleanups versus 1.5.13
+
+Version 1.5.13, 2006-12-10
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Compilation fixes for the bzip2, zlib, and GNU MP modules
+* Better support for Intel C++ and EKOpath C++ on x86-64
+
+Version 1.5.12, 2006-10-27
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Cleanups in the initialization routines
+* Add some x86-64 assembly for multiply-add
+* Fix problems generating very small (below 384 bit) RSA keys
+* Support out of tree builds
+* Bring some of the documentation up to date
+* More improvements to the Python bindings
+
+Version 1.5.11, 2006-09-10
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Removed the Algorithm base class
+* Various cleanups in the public key inheritance hierarchy
+* Major overhaul of the configure/build setup
+* Added x86 assembler implementations of Serpent and low-level MPI code
+* Optimizations for the SHA-1 x86 assembler
+* Various improvements to the Python wrappers
+* Work around a Visual Studio compiler bug
+
+Version 1.5.10, 2006-08-13
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Add x86 assembler versions of MD4, MD5, and SHA-1
+* Expand InitializerOptions' language to support on/off switches
+* Fix definition of OID 2.5.4.8; was accidentally changed in 1.5.9
+* Fix possible resource leaks in the mmap allocator
+* Slightly optimized buffering in MDx_HashFunction
+* 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
+* Some updates to the documentation
+
+Version 1.5.9, 2006-07-12
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Fixed bitrot in the AEP engine
+* Fix support for marking certificate/CRL extensions as critical
+* Significant cleanups in the library state / initialization code
+* LibraryInitializer takes an explicit InitializerOptions object
+* Make Mutex_Factory an abstract class, add Default_Mutex_Factory
+* Change configuration access to using global_state()
+* Add support for global named mutexes throughout the library
+* Add some STL wrappers for the delete operator
+* Change how certificates are created to be more flexible and general
+
+Version 1.5.8, 2006-06-23
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Many internal cleanups to the X.509 cert/CRL code
+* Allow for application code to support new X.509 extensions
+* Change the return type of X509_Certificate::{subject,issuer}_info
+* Allow for alternate character set handling mechanisms
+* Fix a bug that was slowing squaring performance somewhat
+* Fix a very hard to hit overflow bug in the C version of word3_muladd
+* Minor cleanups to the assembler modules
+* Disable es_unix module on FreeBSD due to build problem on FreeBSD 6.1
+* Support for GCC 2.95.x has been dropped in this release
+
+Version 1.5.7, 2006-05-28
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Further, major changes to the BER/DER coding system
+* Updated the Qt mutex module to use Mutex_Factory
+* Moved the library global state object into an anonymous namespace
+* Drop the Visual C++ x86 assembly module due to bugs
+
+Version 1.5.6, 2006-03-01
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* The low-level DER/BER coding system was redesigned and rewritten
+* Portions of the certificate code were cleaned up internally
+* Use macros to substantially clean up the GCC assembly code
+* Added 32-bit x86 assembly for Visual C++ (by Luca Piccarreta)
+* Avoid a couple of spurious warnings under Visual C++
+* Some slight cleanups in X509_PublicKey::key_id
+
+Version 1.5.5, 2006-02-04
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Fixed a potential infinite loop in the memory pool code (Matt Johnston)
+* Made Pooling_Allocator::Memory_Block an actual class of sorts
+* Some small optimizations to the division and modulo computations
+* Cleaned up the implementation of some of the BigInt operators
+* Reduced use of dynamic memory allocation in low-level BigInt functions
+* A few simplifications in the Randpool mixing function
+* Removed power(), as it was not particularly useful (or fast)
+* Fixed some annoying bugs in the benchmark code
+* Added a real credits file
+
+Version 1.5.4, 2006-01-29
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Integrated x86 and amd64 assembly code, contributed by Luca Piccarreta
+* Fixed a memory access off-by-one in the Karatsuba code
+* Changed Pooling_Allocator's free list search to a log(N) algorithm
+* Merged ModularReducer with its only subclass, Barrett_Reducer
+* Fixed sign-handling bugs in some of the division and modulo code
+* Renamed the module description files to modinfo.txt
+* Further cleanups in the initialization code
+* Removed BigInt::add and BigInt::sub
+* Merged all the division-related functions into just divide()
+* Modified the <mp_asmi.h> functions to allow for better optimizations
+* Made the number of bits polled from an EntropySource user configurable
+* Avoid including <algorithm> in <botan/secmem.h>
+* Fixed some build problems with Sun Forte
+* Removed some dead code from bigint_modop
+* Fix the definition of same_mem
+
+Version 1.5.3, 2006-01-24
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Many optimizations in the low-level multiple precision integer code
+* Added hooks for assembly implementations of the MPI code
+* Support for the X.509 issuer alternative name extension in new certs
+* Fixed a bug in the decompression modules; found and patched by Matt Johnston
+* New Windows mutex module (mux_win32), by Luca Piccarreta
+* Changed the Windows timer module to use QueryPerformanceCounter
+* mem_pool.cpp was using std::set iterators instead of std::multiset ones
+* Fixed a bug in X509_CA preventing users from disabling particular extensions
+* Fixed the mp_asm64 module, which was entirely broken in 1.5.2
+* Fixed some module build problems on FreeBSD and Tru64
+
+Version 1.4.12, 2006-01-15
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Fixed an off-by-one memory read in MISTY1::key()
+* Fixed a nasty memory leak in Output_Buffers::retire()
+* Changed maximum HMAC keylength to 1024 bits
+* Fixed a build problem in the hardware timer module on 64-bit PowerPC
+
+Version 1.5.2, 2006-01-15
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Fixed an off-by-one memory read in MISTY1::key()
+* Fixed a nasty memory leak in Output_Buffers::retire()
+* Reimplemented the memory allocator from scratch
+* Improved memory caching in Montgomery exponentiation
+* Optimizations for multiple precision addition and subtraction
+* Fixed a build problem in the hardware timer module on 64-bit PowerPC
+* Changed default Karatsuba cutoff to 12 words (was 14)
+* Removed MemoryRegion::bits(), which was unused and incorrect
+* Changed maximum HMAC keylength to 1024 bits
+* Various minor Makefile and build system changes
+* Avoid using std::min in <secmem.h> to bypass Windows libc macro pollution
+* Switched checks/clock.cpp back to using clock() by default
+* Enabled the symmetric algorithm tests, which were accidentally off in 1.5.1
+* Removed the Default_Mutex's unused clone() member function
+
+Version 1.5.1, 2006-01-08
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Implemented Montgomery exponentiation
+* Implemented generalized Karatsuba multiplication and squaring
+* Implemented Comba squaring for 4, 6, and 8 word inputs
+* Added new Modular_Exponentiator and Power_Mod classes
+* Removed FixedBase_Exp and FixedExponent_Exp
+* Fixed a performance regression in get_allocator introduced in 1.5.0
+* Engines can now offer S2K algorithms and block cipher padding methods
+* Merged the remaining global 'algolist' code into Default_Engine
+* The low-level MPI code is linked as C again
+* Replaced BigInt's get_nibble with the more general get_substring
+* Some documentation updates
+
+Version 1.5.0, 2006-01-01
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Moved all global/shared library state into a single object
+* Mutex objects are created through mutex factories instead of a global
+* Removed ::get_mutex(), ::initialize_mutex(), and Mutex::clone()
+* Removed the RNG_Quality enum entirely
+* There is now only a single global-use PRNG
+* Removed the no_aliases and no_oids options for LibraryInitializer
+* Removed the deprecated algorithms SEAL, ISAAC, and HAVAL
+* Change es_ftw to use unbuffered I/O
+
+Version 1.4.11, 2005-12-31
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Changed Whirlpool diffusion matrix to match updated algorithm spec
+* Fixed several engine module build errors introduced in 1.4.10
+* Fixed two build problems in es_capi; reported by Matthew Gregan
+* Added a constructor to DataSource_Memory taking a std::string
+* Placing the same Filter in multiple Pipes triggers an exception
+* The configure script accepts --docdir and --libdir
+* Merged doc/rngs.txt into the main API document
+* Thanks to Joel Low for several bug reports on early tarballs of 1.4.11
+
+Version 1.4.10, 2005-12-18
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Added an implementation of KASUMI, the block cipher used in 3G phones
+* Refactored Pipe; output queues are now managed by a distinct class
+* Made certain Filter facilities only available to subclasses of Fanout_Filter
+* There is no longer any overhead in Pipe for a message that has been read out
+* It is now possible to generate RSA keys as small as 128 bits
+* Changed some of the core classes to derive from Algorithm as a virtual base
+* Changed Randpool to use HMAC instead of a plain hash as the mixing function
+* Fixed a bug in the allocators; found and fixed by Matthew Gregan
+* Enabled the use of binary file I/O, when requested by the application
+* The OpenSSL engine's block cipher code was missing some deallocation calls
+* Disabled the es_ftw module on NetBSD, due to header problems there
+* Fixed a problem preventing tm_hard from building on MacOS X on PowerPC
+* Some cleanups for the modules that use inline assembler
+* config.h is now stored in build/ instead of build/include/botan/
+* The header util.h was split into bit_ops.h, parsing.h, and util.h
+* Cleaned up some redundant include directives
+
+Version 1.4.9, 2005-11-06
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Added the IBM-created AES candidate algorithm MARS
+* Added the South Korean block cipher SEED
+* Added the stream cipher Turing
+* Added the new hash function FORK-256
+* Deprecated the ISAAC stream cipher
+* Twofish and RC6 are significantly faster with GCC
+* Much better support for 64-bit PowerPC
+* Added support for high-resolution PowerPC timers
+* Fixed a bug in the configure script causing problems on FreeBSD
+* Changed ANSI X9.31 to support arbitrary block ciphers
+* Make the configure script a bit less noisy
+* Added more test vectors for some algorithms, including all the AES finalists
+* Various cosmetic source code cleanups
+
+Version 1.4.8, 2005-10-16
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Resolved a bad performance problem in the allocators; fix by Matt Johnston
+* Worked around a Visual Studio 2003 compilation problem introduced in 1.4.7
+* Renamed OMAC to CMAC to match the official NIST naming
+* Added single byte versions of update() to PK_Signer and PK_Verifier
+* Removed the unused reverse_bits and reverse_bytes functions
+
+Version 1.4.7, 2005-09-25
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Fixed major performance problems with recent versions of GNU C++
+* Added an implementation of the X9.31 PRNG
+* Removed the X9.17 and FIPS 186-2 PRNG algorithms
+* Changed defaults to use X9.31 PRNGs as global PRNG objects
+* Documentation updates to reflect the PRNG changes
+* Some cleanups related to the engine code
+* Removed two useless headers, base_eng.h and secalloc.h
+* Removed PK_Verifier::valid_signature
+* Fixed configure/build system bugs affecting MacOS X builds
+* Added support for the EKOPath x86-64 compiler
+* Added missing destructor for BlockCipherModePaddingMethod
+* Fix some build problems with Visual C++ 2005 beta
+* Fix some build problems with Visual C++ 2003 Workshop
+
+Version 1.4.6, 2005-03-13
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Fix an error in the shutdown code introduced in 1.4.5
+* Setting base/pkcs8_tries to 0 disables the builtin fail-out
+* Support for XMPP identifiers in X.509 certificates
+* Duplicate entries in X.509 DNs are removed
+* More fixes for Borland C++, from Friedemann Kleint
+* Add a workaround for buggy iostreams
+
+Version 1.4.5, 2005-02-26
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Add support for AES encryption of private keys
+* Minor fixes for PBES2 parameter decoding
+* Internal cleanups for global state variables
+* GCC 3.x version detection was broken in non-English locales
+* Work around a Sun Forte bug affecting mem_pool.h
+* Several fixes for Borland C++ 5.5, from Friedemann Kleint
+* Removed inclusion of init.h into base.h
+* Fixed a major bug in reading from certificate stores
+* Cleaned up a couple of mutex leaks
+* Removed some left-over debugging code
+* Removed SSL3_MAC, SSL3_PRF, and TLS_PRF
+
+Version 1.4.4, 2004-12-02
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Further tweaks to the pooling allocator
+* Modified EMSA3 to support SSL/TLS signatures
+* Changes to support Qt/QCA, from Justin Karneges
+* Moved mux_qt module code into mod_qt
+* Fixes for HP-UX from Mike Desjardins
+
+Version 1.4.3, 2004-11-06
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Split up SecureAllocator into Allocator and Pooling_Allocator
+* Memory locking allocators are more likely to be used
+* Fixed the placement of includes in some modules
+* Fixed broken installation procedure
+* Fixes in configure script to support alternate install programs
+* Modules can specify the minimum version they support
+
+Version 1.4.2, 2004-10-31
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Fixed a major CRL handling bug
+* Cipher and hash operations can be offloaded to engines
+* Added support for cipher and hash offload in OpenSSL engine
+* Improvements for 64-bit CPUs without a widening multiply instruction
+* Support for SHA2-* and Whirlpool with EMSA2
+* Fixed a long-standing build problem with conflicting include files
+* Fixed some examples that hadn't been updated for 1.4.x
+* Portability fixes for Solaris, BSD, HP-UX, and others
+* Lots of fixes and cleanups in the configure script
+* Updated the Gentoo ebuild file
+
+Version 1.4.1, 2004-10-10
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Fixed major errors in the X.509 and PKCS #8 copy_key functions
+* Added a LAST_MESSAGE meta-message number for Pipe
+* Added new aliases (3DES and DES-EDE) for Triple-DES
+* Added some new functions to PK_Verifier
+* Cleaned up the KDF interface
+* Disabled tm_posix on BSD due to header issues
+* Fixed a build problem on PowerPC with GNU C++ pre-3.4
+
+Version 1.4.0, 2004-06-26
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Added the FIPS 186 RNG back
+* Added copy_key functions for X.509 public keys and PKCS #8 private keys
+* Fixed PKCS #1 signatures with RIPEMD-128
+* Moved some code around to avoid warnings with Sun ONE compiler
+* Fixed a bug in botan-config affecting OpenBSD
+* Fixed some build problems on Tru64, HP-UX
+* Fixed compile problems with Intel C++, Compaq C++
+
+Version 1.3.14, 2004-06-12
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Added support for AEP's AEP1000/AEP2000 crypto cards
+* Added a Mutex module using Qt, from Justin Karneges
+* Added support for engine loading in LibraryInitializer
+* Tweaked SecureAllocator, giving 20% better performance under heavy load
+* Added timer and memory locking modules for Win32 (tm_win32, ml_win32)
+* Renamed PK_Engine to Engine_Core
+* Improved the Karatsuba cutoff points
+* Fixes for compiling with GCC 3.4 and Sun C++ 5.5
+* Fixes for Linux/s390, OpenBSD, and Solaris
+* Added support for Linux/s390x
+* The configure script was totally broken for 'generic' OS
+* Removed Montgomery reduction due to bugs
+* Removed an unused header, pkcs8alg.h
+* check --validate returns an error code if any tests failed
+* Removed duplicate entry in Unix command list for es_unix
+* Moved the Cert_Usage enumeration into X509_Store
+* Added new timing methods for PK benchmarks, clock_gettime and RDTSC
+* Fixed a few minor bugs in the configure script
+* Removed some deprecated functions from x509cert.h and pkcs10.h
+* Removed the 'minimal' module, has to be updated for Engine support
+* Changed MP_WORD_BITS macro to BOTAN_MP_WORD_BITS to clean up namespace
+* Documentation updates
+
+Version 1.3.13, 2004-05-15
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Major fixes for Cygwin builds
+* Minor MacOS X install fixes
+* The configure script is a little better at picking the right modules
+* Removed ml_unix from the 'unix' module set for Cygwin compatibility
+* Fixed a stupid compile problem in pkcs10.h
+
+Version 1.3.12, 2004-05-02
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Added ability to remove old entries from CRLs
+* Swapped the first two arguments of X509_CA::update_crl()
+* Added an < operator for MemoryRegion, so it can be used as a std::map key
+* Changed X.509 searching by DNS name from substring to full string compares
+* Renamed a few X509_Certificate and PKCS10_Request member functions
+* Fixed a problem when decoding some PKCS #10 requests
+* Hex_Decoder would not check inputs, reported by Vaclav Ovsik
+* Changed default CRL expire time from 30 days to 7 days
+* X509_CRL's default PEM header is now "X509 CRL", for OpenSSL compatibility
+* Corrected errors in the API doc, fixes from Ken Perano
+* More documentation about the Pipe/Filter code
+
+Version 1.3.11, 2004-04-01
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Fixed two show-stopping bugs in PKCS10_Request
+* Added some sanity checks in Pipe/Filter
+* The DNS and URI entries would get swapped in subjectAlternativeNames
+* MAC_Filter is now willing to not take a key at creation time
+* Setting the expiration times of certs and CRLs is more flexible
+* Fixed problems building on AIX with GCC
+* Fixed some problems in the tutorial pointed out by Dominik Vogt
+* Documentation updates
+
+Version 1.3.10, 2004-03-27
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Added support for OpenPGP's ASCII armor format
+* Cleaned up the RNG system; seeding is much more flexible
+* Added simple autoconfiguration abilities to configure.pl
+* Fixed a GCC 2.95.x compile problem
+* Updated the example configuration file
+* Documentation updates
+
+Version 1.3.9, 2004-03-07
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Added an engine using OpenSSL (requires 0.9.7 or later)
+* X509_Certificate would lose email addresses stored in the DN
+* Fixed a missing initialization in a BigInt constructor
+* Fixed several Visual C++ compile problems
+* Fixed some BeOS build problems
+* Fixed the WiderWake benchmark
+
+Version 1.3.8, 2003-12-30
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Initial introduction of engine support, which separates PK keys from
+ the underlying operations. An engine using GNU MP was added.
+
+* DSA, DH, NR, and ElGamal constructors accept taking just the private
+ key again since the public key is easily derived from it.
+
+* Montgomery reduction support was added.
+* ElGamal keys now support being imported/exported as ASN.1 objects
+* Added Montgomery reductions
+* Added an engine that uses GNU MP (requires 4.1 or later)
+* Removed the obsolete mp_gmp module
+* Moved several initialization/shutdown functions to init.h
+* Major refactoring of the memory containers
+* New non-locking container, MemoryVector
+* Fixed 64-bit problems in BigInt::set_bit/clear_bit
+* Renamed PK_Key::check_params() to check_key()
+* Some incompatible changes to OctetString
+* Added version checking macros in version.h
+* Removed the fips140 module pending rewrite
+* Added some functions and hooks to help GUIs
+* Moved more shared code into MDx_HashFunction
+* Added a policy hook for specifying the encoding of X.509 strings
+
+Version 1.3.7, 2003-12-12
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Fixed a big security problem in es_unix (use of untrusted PATH)
+* Fixed several stability problems in es_unix
+* Expanded the list of programs es_unix will try to use
+* SecureAllocator now only preallocates blocks in special cases
+* Added a special case in Global_RNG::seed for forcing a full poll
+* Removed the FIPS 186 RNG added in 1.3.5 pending further testing
+* Configure updates for PowerPC CPUs
+* Removed the (never tested) VAX support
+* Added support for S/390 Linux
+
+Version 1.3.6, 2003-12-07
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Added a new module 'minimal', which disables most algorithms
+* SecureAllocator allocates a few blocks at startup
+* A few minor MPI cleanups
+* RPM spec file cleanups and fixes
+
+Version 1.3.5, 2003-11-30
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Major improvements in ASN.1 string handling
+* Added partial support for ASN.1 UTF8 STRINGs and BMP STRINGs
+* Added partial support for the X.509v3 certificate policies extension
+* Centralized the handling of character set information
+* Added FIPS 140-2 startup self tests
+* Added a module (fips140) for doing extra FIPS 140-2 tests
+* Added FIPS 186-2 RNG
+* Improved ASN.1 BIT STRING handling
+* Removed a memory leak in PKCS10_Request
+* The encoding of DirectoryString now follows PKIX guidelines
+* Fixed some of the character set dependencies
+* Fixed a DER encoding error for tags greater than 30
+* The BER decoder can now handle tags larger than 30
+* Fixed tm_hard.cpp to recognize SPARC on more systems
+* Workarounds for a GCC 2.95.x bug in x509find.cpp
+* RPM changed to install into /usr instead of /usr/local
+* Added support for QNX
+
+Version 1.2.8, 2003-11-21
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Merged several important bug fixes from 1.3.x
+
+Version 1.3.4, 2003-11-21
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Added a module that does certain MPI operations using GNU MP
+* Added the X9.42 Diffie-Hellman PRF
+* The Zlib and Bzip2 objects now use custom allocators
+* Added member functions for directly hashing/MACing SecureVectors
+* Minor optimizations to the MPI addition and subtraction algorithms
+* Some cleanups in the low-level MPI code
+* Created separate AES-{128,192,256} objects
+
+Version 1.3.3, 2003-11-17
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* The library can now be repeatedly initialized and shutdown without crashing
+* Fixed an off-by-one error in the CTS code
+* Fixed an error in the EMSA4 verification code
+* Fixed a memory leak in mutex.cpp (pointed out by James Widener)
+* Fixed a memory leak in Pthread_Mutex
+* Fixed several memory leaks in the testing code
+* Bulletproofed the EMSA/EME/KDF/MGF retrieval functions
+* Minor cleanups in SecureAllocator
+* Removed a needless mutex guarding the (stateless) global timer
+* Fixed a piece of bash-specific code in botan-config
+* X.509 objects report more information about decoding errors
+* Cleaned up some of the exception handling
+* Updated the example config file with new OIDSs
+* Moved the build instructions into a separate document, building.tex
+
+Version 1.3.2, 2003-11-13
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Fixed a bug preventing DSA signatures from verifying on X.509 objects
+* Made the X509_Store search routines more efficient and flexible
+* Added a function to X509_PublicKey to do easy public/private key matching
+* Added support for decoding indefinite length BER data
+* Changed Pipe's peek() to take an offset
+* Removed Filter::set_owns in favor of the new incr_owns function
+* Removed BigInt::zero() and BigInt::one()
+* Renamed the PEM related options from base/pem_* to pem/*
+* Added an option to specify the line width when encoding PEM
+* Removed the "rng/safe_longterm" option; it's always on now
+* Changed the cipher used for RNG super-encryption from ARC4 to WiderWake4+1
+* Cleaned up the base64/hex encoders and decoders
+* Added an ASN.1/BER decoder as an example
+* AES had its internals marked 'public' in previous versions
+* Changed the value of the ASN.1 NO_OBJECT enum
+* Various new hacks in the configure script
+* Removed the already nominal support for SunOS
+
+Version 1.3.1, 2003-11-04
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Generalized a few pieces of the DER encoder
+* PKCS8::load_key would fail if handed an unencrypted key
+* Added a failsafe so PKCS #8 key decoding can't go into an infinite loop
+
+Version 1.3.0, 2003-11-02
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Major redesign of the PKCS #8 private key import/export system
+* Added a small amount of UI interface code for getting passphrases
+* Added heuristics that tell if a key, cert, etc is stored as PEM or BER
+* Removed CS-Cipher, SHARK, ThreeWay, MD5-MAC, and EMAC
+* Removed certain deprecated constructors of RSA, DSA, DH, RW, NR
+* Made PEM decoding more forgiving of extra text before the header
+
+Version 1.2.7, 2003-10-31
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Added support for reading configuration files
+* Added constructors so NR and RW keys can be imported easily
+* Fixed mp_asm64, which was completely broken in 1.2.6
+* Removed tm_hw_ia32 module; replaced by tm_hard
+* Added support for loading certain oddly formed RSA certificates
+* Fixed spelling of NON_REPUDIATION enum
+* Renamed the option default_to_ca to v1_assume_ca
+* Fixed a minor bug in X.509 certificate generation
+* Fixed a latent bug in the OID lookup code
+* Updated the RPM spec file
+* Added to the tutorial
+
+Version 1.2.6, 2003-07-04
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Major performance increase for PK algorithms on most 64-bit systems
+* Cleanups in the low-level MPI code to support asm implementations
+* Fixed build problems with some versions of Compaq's C++ compiler
+* Removed useless constructors for NR public and private keys
+* Removed support for the patch_file directive in module files
+* Removed several deprecated functions
+
+Version 1.2.5, 2003-06-22
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Fixed a tricky and long-standing memory leak in Pipe
+* Major cleanups and fixes in the memory allocation system
+* Removed alloc_mlock, which has been superseded by the ml_unix module
+* Removed a denial of service vulnerability in X509_Store
+* Fixed compilation problems with VS .NET 2003 and Codewarrior 8
+* Added another variant of PKCS8::load_key, taking a memory buffer
+* Fixed various minor/obscure bugs which occurred when MP_WORD_BITS != 32
+* BigInt::operator%=(word) was a no-op if the input was a power of 2
+* Fixed portability problems in BigInt::to_u32bit
+* Fixed major bugs in SSL3-MAC
+* Cleaned up some messes in the PK algorithms
+* Cleanups and extensions for OMAC and EAX
+* Made changes to the entropy estimation function
+* Added a 'beos' module set for use on BeOS
+* Officially deprecated a few X509:: and PKCS8:: functions
+* Moved the contents of primes.h to numthry.h
+* Moved the contents of x509opt.h to x509self.h
+* Removed the (empty) desx.h header
+* Documentation updates
+
+Version 1.2.4, 2003-05-29
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Fixed a bug in EMSA1 affecting NR signature verification
+* Fixed a few latent bugs in BigInt related to word size
+* Removed an unused function, mp_add2_nc, from the MPI implementation
+* Reorganized the core MPI files
+
+Version 1.2.3, 2003-05-20
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Fixed a bug that prevented DSA/NR key generation
+* Fixed a bug that prevented importing some root CA certs
+* Fixed a bug in the BER decoder when handing optional bit or byte strings
+* Fixed the encoding of authorityKeyIdentifier in X509_CA
+* Added a sanity check in PBKDF2 for zero length passphrases
+* Added versions of X509::load_key and PKCS8::load_key that take a file name
+* X509_CA generates 128 bit serial numbers now
+* Added tests to check PK key generation
+* Added a simplistic X.509 CA example
+* Cleaned up some of the examples
+
+Version 1.2.2, 2003-05-13
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Add checks to prevent any BigInt bugs from revealing an RSA or RW key
+* Changed the interface of Global_RNG::seed
+* Major improvements for the es_unix module
+* Added another Win32 entropy source, es_win32
+* The Win32 CryptoAPI entropy source can now poll multiple providers
+* Improved the BeOS entropy source
+* Renamed pipe_unixfd module to fd_unix
+* Fixed a file descriptor leak in the EGD module
+* Fixed a few locking bugs
+
+Version 1.2.1, 2003-05-06
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Added ANSI X9.23 compatible CBC padding
+* Added an entropy source using Win32 CryptoAPI
+* Removed the Pipe I/O operators taking a FILE*
+* Moved the BigInt encoding/decoding functions into the BigInt class
+* Integrated several fixes for VC++ 7 (from Hany Greiss)
+* Fixed the configure.pl script for Windows builds
+
+Version 1.2.0, 2003-04-28
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Tweaked the Karatsuba cut-off points
+* Increased the allowed keylength of HMAC and Blowfish
+* Removed the 'mpi_ia32' module, pending rewrite
+* Workaround a GCC 2.95.x bug in eme1.cpp
+
+Version 1.1.13, 2003-04-22
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Added OMAC
+* Added EAX authenticated cipher mode
+* Diffie-Hellman would not do blinding in some cases
+* Optimized the OFB and CTR modes
+* Corrected Skipjack's word ordering, as per NIST clarification
+* Support for all subject/issuer attribute types required by RFC 3280
+* The removeFromCRL CRL reason code is now handled correctly
+* Increased the flexibility of the allocators
+* 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
+
+Version 1.1.12, 2003-04-15
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Fixed a ASN.1 string encoding bug
+* Fixed a pair of X509_DN encoding problems
+* Base64_Decoder and Hex_Decoder can now validate input
+* Removed support for the LibraryInitializer option 'egd_path'
+* Added tests for DSA X.509 and PKCS #8 key formats
+* Removed a long deprecated feature of DH_PrivateKey's constructor
+* Updated the RPM .spec file
+* Major documentation updates
+
+Version 1.1.11, 2003-04-07
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Added PKCS #10 certificate requests
+* Changed X509_Store searching interface to be more flexible
+* Added a generic Certificate_Store interface
+* Added a function for generating self-signed X.509 certs
+* Cleanups and changes to X509_CA
+* New examples for PKCS #10 and self-signed certificates
+* Some documentation updates
+
+Version 1.1.10, 2003-04-03
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* X509_CA can now generate new X.509 CRLs
+* Added blinding for RSA, RW, DH, and ElGamal to prevent timing attacks
+* More certificate and CRL extensions/attributes are supported
+* Better DN handling in X.509 certificates/CRLs
+* Added a DataSink hierarchy (suggested by Jim Darby)
+* Consolidated SecureAllocator and ManagedAllocator
+* Many cleanups and generalizations
+* Added a (slow) pthreads based EntropySource
+* Fixed some threading bugs
+
+Version 1.1.9, 2003-02-25
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Added support for using X.509v2 CRLs
+* Fixed several bugs in the path validation algorithm
+* Certificates can be verified for a particular usage
+* Algorithm for comparing distinguished names now follows X.509
+* Cleaned up the code for the es_beos, es_ftw, es_unix modules
+* Documentation updates
+
+Version 1.1.8, 2003-01-29
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Fixes for the certificate path validation algorithm in X509_Store
+* Fixed a bug affecting X509_Certificate::is_ca_cert()
+* Added a general configuration interface for policy issues
+* Cleanups and API changes in the X.509 CA, cert, and store code
+* Made various options available for X509_CA users
+* Changed X509_Time's interface to work around time_t problems
+* Fixed a theoretical weakness in Randpool's entropy mixing function
+* Fixed problems compiling with GCC 2.95.3 and GCC 2.96
+* Fixed a configure bug (reported by Jon Wilson) affecting MinGW
+
+Version 1.0.2, 2003-01-12
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Fixed an obscure SEGFAULT causing bug in Pipe
+* Fixed an obscure but dangerous bug in SecureVector::swap
+
+Version 1.1.7, 2003-01-12
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Fixed an obscure but dangerous bug in SecureVector::swap
+* Consolidated SHA-384 and SHA-512 to save code space
+* Added SSL3-MAC and SSL3-PRF
+* Documentation updates, including a new tutorial
+
+Version 1.1.6, 2002-12-10
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* 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
+* Documented the generic cipher lookup interface
+* Added an (untested) entropy source for BeOS
+* Various cleanups and bug fixes
+
+Version 1.1.5, 2002-11-17
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Added the discrete logarithm integrated encryption system (DLIES)
+* Various optimizations for BigInt
+* Added support for assembler optimizations in modules
+* Added BigInt x86 optimizations module (mpi_ia32)
+
+Version 1.1.4, 2002-11-10
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Speedup of 15-30% for PK algorithms
+* Implemented the PBES2 encryption scheme
+* Fixed a potential bug in decoding RSA and RW private keys
+* Changed the DL_Group class interface to handle different formats better
+* Added support for PKCS #3 encoded DH parameters
+* X9.42 DH parameters use a PEM label of 'X942 DH PARAMETERS'
+* Added key pair consistency checking
+* Fixed a compatibility problem with gcc 2.96 (pointed out by Hany Greiss)
+* A botan-config script is generated at configure time
+* Documentation updates
+
+Version 1.1.3, 2002-11-03
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Added a generic public/private key loading interface
+* Fixed a small encoding bug in RSA, RW, and DH
+* Changed the PK encryption/decryption interface classes
+* ECB supports using padding methods
+* Added a function-based interface for library initialization
+* Added support for RIPEMD-128 and Tiger PKCS#1 v1.5 signatures
+* The cipher mode benchmarks now use 128-bit AES instead of DES
+* Removed some obsolete typedefs
+* Removed OpenCL support (opencl.h, the OPENCL_* macros, etc)
+* Added tests for PKCS #8 encoding/decoding
+* Added more tests for ECB and CBC
+
+Version 1.1.2, 2002-10-21
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Support for PKCS #8 encoded RSA, DSA, and DH private keys
+* Support for Diffie-Hellman X.509 public keys
+* Major reorganization of how X.509 keys are handled
+* Added PKCS #5 v2.0's PBES1 encryption scheme
+* Added a generic cipher lookup interface
+* Added the WiderWake4+1 stream cipher
+* Added support for sync-able stream ciphers
+* Added a 'paranoia level' option for the LibraryInitializer
+* More security for RNG output meant for long term keys
+* Added documentation for some of the new 1.1.x features
+* CFB's feedback argument is now specified in bits
+* Renamed CTR class to CTR_BE
+* Updated the RSA and DSA examples to use X.509 and PKCS #8 key formats
+
+Version 1.1.1, 2002-10-15
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Added the Korean hash function HAS-160
+* Partial support for RSA and DSA X.509 public keys
+* Added a mostly functional BER encoder/decoder
+* Added support for non-deterministic MAC functions
+* Initial support for PEM encoding/decoding
+* Internal cleanups in the PK algorithms
+* Several new convenience functions in Pipe
+* Fixed two nasty bugs in Pipe
+* Messed with the entropy sources for es_unix
+* Discrete logarithm groups are checked for safety more closely now
+* For compatibility with GnuPG, ElGamal now supports DSA-style groups
+
+Version 1.0.1, 2002-09-14
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Fixed a minor bug in Randpool::random()
+* Added some new aliases and typedefs for 1.1.x compatibility
+* The 4096-bit RSA benchmark key was decimal instead of hex
+* EMAC was returning an incorrect name
+
+Version 1.1.0, 2002-09-14
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Added entropy estimation to the RNGs
+* Improved the overall design of both Randpool and ANSI_X917_RNG
+* Added a separate RNG for nonce generation
+* Added window exponentiation support in power_mod
+* Added a get_s2k function and the PKCS #5 S2K algorithms
+* Added the TLSv1 PRF
+* Replaced BlockCipherModeIV typedef with InitializationVector class
+* Renamed PK_Key_Agreement_Scheme to PK_Key_Agreement
+* Renamed SHA1 -> SHA_160 and SHA2_x -> SHA_x
+* Added support for RIPEMD-160 PKCS#1 v1.5 signatures
+* Changed the key agreement scheme interface
+* Changed the S2K and KDF interfaces
+* Better SCAN compatibility for HAVAL, Tiger, MISTY1, SEAL, RC5, SAFER-SK
+* Added support for variable-pass Tiger
+* Major speedup for Rabin-Williams key generation
+
+Version 1.0.0, 2002-08-26
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Octal I/O of BigInt is now supported
+* Fixed portability problems in the es_egd module
+* Generalized IV handling in the block cipher modes
+* Added Karatsuba multiplication and k-ary exponentiation
+* Fixed a problem in the multiplication routines
+
+Version 0.9.2, 2002-08-18
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* DH_PrivateKey::public_value() was returning the wrong value
+* Various BigInt optimizations
+* The filters.h header now includes hex.h and base64.h
+* Moved Counter mode to ctr.h
+* Fixed a couple minor problems with VC++ 7
+* Fixed problems with the RPM spec file
+
+Version 0.9.1, 2002-08-10
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Grand rename from OpenCL to Botan
+* Major optimizations for the PK algorithms
+* Added ElGamal encryption
+* Added Whirlpool
+* Tweaked memory allocation parameters
+* Improved the method of seeding the global RNG
+* Moved pkcs1.h to eme_pkcs.h
+* Added more test vectors for some algorithms
+* Fixed error reporting in the BigInt tests
+* Removed Default_Timer, it was pointless
+* Added some new example applications
+* Removed some old examples that weren't that interesting
+* Documented the compression modules
+
+Version 0.9.0, 2002-08-03
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* EMSA4 supports variable salt size
+* PK_* can take a string naming the encoding method to use
+* Started writing some internals documentation
+
+Version 0.8.7, 2002-07-30
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Fixed bugs in EME1 and EMSA4
+* Fixed a potential crash at shutdown
+* Cipher modes returned an ill-formed name
+* Removed various deprecated types and headers
+* Cleaned up the Pipe interface a bit
+* Minor additions to the documentation
+* First stab at a Visual C++ makefile (doc/Makefile.vc7)
+
+Version 0.8.6, 2002-07-25
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Added EMSA4 (aka PSS)
+* Brought the manual up to date; many corrections and additions
+* Added a parallel hash function construction
+* Lookup supports all available algorithms now
+* Lazy initialization of the lookup tables
+* Made more discrete logarithm groups available through get_dl_group()
+* StreamCipher_Filter supports seeking (if the underlying cipher does)
+* Minor optimization for GCD calculations
+* Renamed SAFER_SK128 to SAFER_SK
+* Removed many previously deprecated functions
+* Some now-obsolete functions, headers, and types have been deprecated
+* Fixed some bugs in DSA prime generation
+* DL_Group had a constructor for DSA-style prime gen but it wasn't defined
+* Reversed the ordering of the two arguments to SEAL's constructor
+* Fixed a threading problem in the PK algorithms
+* Fixed a minor memory leak in lookup.cpp
+* Fixed pk_types.h (it was broken in 0.8.5)
+* Made validation tests more verbose
+* Updated the check and example applications
+
+Version 0.8.5, 2002-07-21
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Major changes to constructors for DL-based cryptosystems (DSA, NR, DH)
+* Added a DL_Group class
+* Reworking of the pubkey internals
+* Support in lookup for aliases and PK algorithms
+* Renamed CAST5 to CAST_128 and CAST256 to CAST_256
+* Added EMSA1
+* Reorganization of header files
+* LibraryInitializer will install new allocator types if requested
+* Fixed a bug in Diffie-Hellman key generation
+* Did a workaround in pipe.cpp for GCC 2.95.x on Linux
+* Removed some debugging code from init.cpp that made FTW ES useless
+* Better checking for invalid arguments in the PK algorithms
+* Reduced Base64 and Hex default line length (if line breaking is used)
+* Fixes for HP's aCC compiler
+* Cleanups in BigInt
+
+Version 0.8.4, 2002-07-14
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Added Nyberg-Rueppel signatures
+* Added Diffie-Hellman key exchange (kex interface is subject to change)
+* Added KDF2
+* Enhancements to the lookup API
+* Many things formerly taking pointers to algorithms now take names
+* Speedups for prime generation
+* LibraryInitializer has support for seeding the global RNG
+* Reduced SAFER-SK128 memory consumption
+* Reversed the ordering of public and private key values in DSA constructor
+* Fixed serious bugs in MemoryMapping_Allocator
+* Fixed memory leak in Lion
+* FTW_EntropySource was not closing the files it read
+* Fixed line breaking problem in Hex_Encoder
+
+Version 0.8.3, 2002-06-09
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Added DSA and Rabin-Williams signature schemes
+* Added EMSA3
+* Added PKCS#1 v1.5 encryption padding
+* Added Filters for PK algorithms
+* Added a Keyed_Filter class
+* LibraryInitializer processes arguments now
+* Major revamp of the PK interface classes
+* Changed almost all of the Filters for non-template operation
+* Changed HMAC, Lion, Luby-Rackoff to non-template classes
+* Some fairly minor BigInt optimizations
+* Added simple benchmarking for PK algorithms
+* Added hooks for fixed base and fixed exponent modular exponentiation
+* Added some examples for using RSA
+* Numerous bugfixes and cleanups
+* Documentation updates
+
+Version 0.8.2, 2002-05-18
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Added an (experimental) algorithm lookup interface
+* Added code for directly testing BigInt
+* Added SHA2-384
+* Optimized SHA2-512
+* Major optimization for Adler32 (thanks to Dan Nicolaescu)
+* Various minor optimizations in BigInt and related areas
+* Fixed two bugs in X9.19 MAC, both reported by Darren Starsmore
+* Fixed a bug in BufferingFilter
+* Made a few fixes for MacOS X
+* Added a workaround in configure.pl for GCC 2.95.x
+* Better support for PowerPC, ARM, and Alpha
+* Some more cleanups
+
+Version 0.8.1, 2002-05-06
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Major code cleanup (check doc/deprecated.txt)
+* Various bugs fixed, including several portability problems
+* Renamed MessageAuthCode to MessageAuthenticationCode
+* A replacement for X917 is in x917_rng.h
+* Changed EMAC to non-template class
+* Added ANSI X9.19 compatible CBC-MAC
+* TripleDES now supports 128 bit keys
+
+Version 0.8.0, 2002-04-24
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Merged BigInt: many bugfixes and optimizations since alpha2
+* Added RSA (rsa.h)
+* Added EMSA2 (emsa2.h)
+* Lots of new interface code for public key algorithms (pk_base.h, pubkey.h)
+* Changed some interfaces, including SymmetricKey, to support the global rng
+* Fixed a serious bug in ManagedAllocator
+* Renamed RIPEMD128 to RIPEMD_128 and RIPEMD160 to RIPEMD_160
+* Removed some deprecated stuff
+* Added a global random number generator (rng.h)
+* Added clone functions to most of the basic algorithms
+* Added a library initializer class (init.h)
+* Version macros in version.h
+* Moved the base classes from opencl.h to base.h
+* Renamed the bzip2 module to comp_bzip2 and zlib to comp_zlib
+* Documentation updates for the new stuff (still incomplete)
+* Many new deprecated things: check doc/deprecated.txt
+
+Version 0.7.10, 2002-04-07
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Added EGD_EntropySource module (es_egd)
+* Added a file tree walking EntropySource (es_ftw)
+* Added MemoryLocking_Allocator module (alloc_mlock)
+* Renamed the pthr_mux, unix_rnd, and mmap_mem modules
+* Changed timer mechanism; the clock method can be switched on the fly.
+* Renamed MmapDisk_Allocator to MemoryMapping_Allocator
+* Renamed ent_file.h to es_file.h (ent_file.h is around, but deprecated)
+* Fixed several bugs in MemoryMapping_Allocator
+* Added more default sources for Unix_EntropySource
+* Changed SecureBuffer to use same allocation methods as SecureVector
+* Added bigint_divcore into mp_core to support BigInt alpha2 release
+* Removed some Pipe functions deprecated since 0.7.8
+* Some fixes for the configure program
+
+Version 0.7.9, 2002-03-19
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Memory allocation substantially revamped
+* Added memory allocation method based on mmap(2) in the mmap_mem module
+* 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)
+* 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
+* Fixed a buffering bug in Bzip_Decompress and Zlib_Decompress
+* Made X917 safer (and about 1/3 as fast)
+* Documentation updates
+
+Version 0.7.8, 2002-02-28
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* More capabilities for Pipe, inspired by SysV STREAMS, including peeking,
+ better buffering, and stack ops. NOT BACKWARDS COMPATIBLE: SEE DOCUMENTATION
+* Added a BufferingFilter class
+* Added popen() based EntropySource for generic Unix systems (unix_rnd)
+* Moved 'devrand' module into main distribution (ent_file.h), renamed to
+ File_EntropySource, and changed interface somewhat.
+* Made Randpool somewhat more conservative and also 25% faster
+* Minor fixes and updates for the configure script
+* Added some tweaks for memory allocation
+* Documentation updates for the new Pipe interface
+* Fixed various minor bugs
+* Added a couple of new example programs (stack and hasher2)
+
+Version 0.7.7, 2001-11-24
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Filter::send now works in the constructor of a Filter subclass
+* You may now have to include <opencl/pipe.h> explicitly in some code
+* Added preliminary PK infrastructure classes in pubkey.h and pkbase.h
+* Enhancements to SecureVector (append, destroy functions)
+* New infrastructure for secure memory allocation
+* Added IEEE P1363 primitives MGF1, EME1, KDF1
+* Rijndael optimizations and cleanups
+* Changed CipherMode<B> to BlockCipherMode(B*)
+* Fixed a nasty bug in pipe_unixfd
+* Added portions of the BigInt code into the main library
+* Support for VAX, SH, POWER, PowerPC-64, Intel C++
+
+Version 0.7.6, 2001-10-14
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Fixed several serious bugs in SecureVector created in 0.7.5
+* Square optimizations
+* Fixed shared objects on MacOS X and HP-UX
+* Fixed static libs for KCC 4.0; works with KCC 3.4g as well
+* Full support for Athlon and K6 processors using GCC
+* Added a table of prime numbers < 2**16 (primes.h)
+* Some minor documentation updates
+
+Version 0.7.5, 2001-08-19
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Split checksum.h into adler32.h, crc24.h, and crc32.h
+* Split modes.h into cbc.h, cfb.h, and ofb.h
+* CBC_wPadding* has been replaced by CBC_Encryption and CBC_Decryption
+* Added OneAndZeros and NoPadding methods for CBC
+* Added Lion, a very fast block cipher construction
+* Added an S2K base class (s2k.h) and an OpenPGP_S2K class (pgp_s2k.h)
+* Basic types (ciphers, hashes, etc) know their names now (call name())
+* Changed the EntropySource type somewhat
+* Big speed-ups for ISAAC, Adler32, CRC24, and CRC32
+* Optimized CAST-256, DES, SAFER-SK, Serpent, SEAL, MD2, and RIPEMD-160
+* Some semantics of SecureVector have changed slightly
+* The mlock module has been removed for the time being
+* Added string handling functions for hashes and MACs
+* Various non-user-visible cleanups
+* Shared library soname is now set to the full version number
+
+Version 0.7.4, 2001-07-15
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* New modules: Zlib, gettimeofday and x86 RTC timers, Unix I/O for Pipe
+* Fixed a vast number of errors in the config script/makefile/specfile
+* Pipe now has a stdio(3) interface as well as C++ iostreams
+* ARC4 supports skipping the first N bytes of the cipher stream (ala MARK4)
+* Bzip2 supports decompressing multiple concatenated streams, and flushing
+* Added a simple 'overall average' score to the benchmarks
+* Fixed a small bug in the POSIX timer module
+* Removed a very-unlikely-to-occur bug in most of the hash functions
+* filtbase.h now includes <iosfwd>, not <iostream>
+* Minor documentation updates
+
+Version 0.7.3, 2001-06-08
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Fix build problems on Solaris/SPARC
+* Fix build problems with Perl versions < 5.6
+* Fixed some stupid code that broke on a few compilers
+* Added string handling functions to Pipe
+* MISTY1 optimizations
+
+Version 0.7.2, 2001-06-03
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Build system supports modules
+* Added modules for mlock, a /dev/random EntropySource, POSIX1.b timers
+* Added Bzip2 compression filter, contributed by Peter Jones
+* GNU make no longer required (tested with 4.4BSD pmake and Solaris make)
+* Fixed minor bug in several of the hash functions
+* Various other minor fixes and changes
+* Updates to the documentation
+
+Version 0.7.1, 2001-05-16
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Rewrote configure script: more consistent and complete
+* Made it easier to find out parameters of types at run time (opencl.h)
+* New functions for finding the version being used (version.h)
+* New SymmetricKey interface for Filters (symkey.h)
+* InvalidKeyLength now records what the invalid key length was
+* Optimized DES, CS-Cipher, MISTY1, Skipjack, XTEA
+* Changed GOST to use correct S-box ordering (incompatible change)
+* Benchmark code was almost totally rewritten
+* Many more entries in the test vector file
+* Fixed minor and idiotic bug in check.cpp
+
+Version 0.7.0, 2001-03-01
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* First public release
diff --git a/comm/third_party/botan/doc/packaging.rst b/comm/third_party/botan/doc/packaging.rst
new file mode 100644
index 0000000000..f77000b89b
--- /dev/null
+++ b/comm/third_party/botan/doc/packaging.rst
@@ -0,0 +1,59 @@
+Notes for Distributors
+========================
+
+This document has information for anyone who is packaging copies of Botan for
+use by downstream developers, such as through a Linux distribution or other
+package management system.
+
+Recommended Options
+------------------------
+
+In most environments, zlib, bzip2, and sqlite are already installed, so there is
+no reason to not include support for them in Botan as well. Build with options
+``--with-zlib --with-bzip2 --with-sqlite3`` to enable these features.
+
+Even though OpenSSL is also typically already installed, using
+``--with-openssl`` by default is *not recommended*. OpenSSL is sometimes faster
+and sometimes slower than Botan, and the relative speeds vary depending on the
+algorithm and CPU.
+
+Set Path to the System CA bundle
+---------------------------------
+
+Most Unix/Linux systems maintain a list of trusted CA certificates at some well
+known path like ``/etc/ssl/certs/ca-certificates.crt`` or
+``/etc/ssl/cert.pem``. Unfortunately the exact path varies between systems. Use
+``--system-cert-bundle=PATH`` to set this path. If the option is not used,
+``configure.py`` tries a list of known locations.
+
+Set Distribution Info
+------------------------
+
+If your distribution of Botan involves creating library binaries, use the
+configure.py flag ``--distribution-info=`` to set the version of your
+packaging. For example Foonix OS might distribute its 4th revision of the
+package for Botan 2.1.3 using ``--distribution-info='Foonix 2.1.3-4'``. The
+string is completely free-form, since it depends on how the distribution numbers
+releases and packages.
+
+Any value set with ``--distribution-info`` flag will be included in the version
+string, and can read through the ``BOTAN_DISTRIBUTION_INFO`` macro.
+
+Minimize Distribution Patches
+------------------------------
+
+We (Botan upstream) *strongly* prefer that downstream distributions maintain no
+long-term patches against Botan. Even if it is a build problem which probably
+only affects your environment, please open an issue on github and include the
+patch you are using. Perhaps the issue does affect other users, and even if not
+it would be better for everyone if the library were improved so it were not
+necessary for the patch to be created in the first place. For example, having to
+modify or remove a build data file, or edit the makefile after generation,
+suggests an area where the build system is insufficiently flexible.
+
+Obviously nothing in the BSD-2 license prevents you from distributing patches or
+modified versions of Botan however you please. But long term patches by
+downstream distributors have a tendency to bitrot and sometimes even result in
+security problems (such as in the Debian OpenSSL RNG fiasco) because the patches
+are never reviewed by the library developers. So we try to discourage them, and
+work to ensure they are never necessary.
diff --git a/comm/third_party/botan/doc/pgpkey.txt b/comm/third_party/botan/doc/pgpkey.txt
new file mode 100644
index 0000000000..78a8a2dc52
--- /dev/null
+++ b/comm/third_party/botan/doc/pgpkey.txt
@@ -0,0 +1,198 @@
+The following PGP key is used to sign all releases:
+
+pub 2048R/EFBADFBC 2004-10-30
+ Key fingerprint = 621D AF64 11E1 851C 4CF9 A2E1 6211 EBF1 EFBA DFBC
+uid Botan Distribution Key
+
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v2.0.17 (GNU/Linux)
+
+mQELBEGD1j0BCADHxPJkPcjJE+4Dlisx2hVc0Dj6JI1MSLrkM8R+2bOhVUSferxP
+T1EMPhfrAdOHTAloyvRThJztnZsNKqfLL49GGcBLdEGAVNks1pG37Teze5Lx1XIu
+zJFrozL2sqBy5C6nHpFgd1tcD68Rah2wp0u2cR9owXf1IqKdEfuo661+MTv7wTB1
+4hKV75nB7ZO6676SEZRILYM+7RJwKAKEmEPJc6hEf94VXn9ecNzaTlHgYkjhz9db
+LOd3od9XvuUw+LMR1dwBqMxbvR90MiXjbedDEkbArcZB9YOAIvEX/lC3qaW4XJt4
+iwHWl/YVZEfALcvQywe2CDrH5hO794wd9MpBAAYptBZCb3RhbiBEaXN0cmlidXRp
+b24gS2V5iQEqBBMBAgAUAhsDAh4BAheABQJKfFpnBBUKCQgACgkQYhHr8e+637xk
+PQf/aOi78XenwwvFrwXOVIVTdZIf8rK1zJksf26h09UD8uVV6z5iiTcpn86+eN9p
+6Ar8IH3tD+JuFnPSwZ/r9MNC2XZwenYo4Gb14jqM6/9hBe328vmeM4Y1G7bD4HrL
+kgV5WEyokqm3zbp3FBLr3Vh68TAC5JB9aHevra+cCA2u3vBNI3YUM5z4TdO150P3
+J00whkqImQEUni8bgxvllBLFM+uhucsX3HZWkoDEpotbg8yd0bqMkiPEyMr1OnJq
+eDVDMrB5wnyLgLFfRAAw3mopM0C1PNOAHr/BIYiaDHX2OwnOfep8rMDoRVf2Ge0D
+DBgsJJ6LduQHLeg403SHWL2F6YkCHAQTAQIABgUCQYPWUgAKCRBcD5boTsFta+r9
+EACWVis7YcaGkKKgRB/5ox8rM36XVhMXdh/hnnGHt5rapbbRRkRHRcWU8WIcFO1A
+59+TfwNNd8gN1MEt/5aX5KHWVKHBDexJgIxm6Dm1pisYHf/dnYQPM18hmqqwNlKY
+97hFkPpHd7enrtc/SvGbQhhLXYlpwBrdMl76e9xJLnnrRQksxegGPo8cr+C9HTs1
+Lwa8zzBxyBwYBYX+0moDkDShEhuXx6mEOXrGvQanJuIvpoIwGH+62E65MbJGlwWp
+w/MAtm2jFhBIhGV0bqJCFp9zIgdNgfskBaPr0oilbuJQZqP0Iqe/6CCt4XkS51yW
+ZqxjLAFpEpvDec4PGw3witKf/koGon9X8C035+nEjLBrWy18Q91vw2USyLI+mm9d
+iMAS8pY2gomfxBO2VwYHJryZykjCYQkccRA1tHteRj4gqTObo0Ak47y5MnplTWwi
+40oP7K2cfhCRBmMioxmYES4xsHEupfRBo3xr1Jq9q0t688WTT1NXHPMPoueF9mKZ
+Cf2pa9aHsqBmWTm3sCaNQKGubCDBEUmJUyndmSatJyYM7NVYoUp6EfqMACFuTNdB
+sjKMh7aWVikQpbJDfA1BIU3lZeqgjgrghVAWkEOBfhG0IVZj+RVCJpsqoTJ8asY2
+VreArSCyr/VnLEnfuH/QpgvCiCbepo3E34DJt4SaAOO2ZohGBBARAgAGBQJMGVc1
+AAoJEKY/LL36AvvMgsoAn2G7kXd09BF7ffk1Sfh174SVrvM9AKC7+R7x0+yV3SCd
+JkkUOo3xR5cOxw==
+=1QuR
+-----END PGP PUBLIC KEY BLOCK-----
+
+This key can be used to contact the primary maintainer:
+
+pub rsa3072/57123B60 2015-03-23
+ Key fingerprint = 4E60 C735 51AF 2188 DF0A 5A62 78E9 8043 5712 3B60
+ uid Jack Lloyd <jack@randombit.net>
+
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+mQGNBFUQXRMBDACZJvcSkr+GNDtIdP9fQWRXByriiIKvuKbqU8KGdhTcPeKwl3y3
+l1W9XsWA2DJ8QDKo4ZcV0lycszIvwBLZllJJWSVNFKxJK2IW33xcIo9dhNqj+hcz
+LxKtBlBU3QKXdQ9+VKSY4EpO6gt/ar21PV+EQcFA9UtT1mRKVqY0pGGxqfQjrOss
+rJKoJyA+1trH4ir7+0/524HNzsBj3B1GmrYfstspqetXyVQ1DoFiThUnj/zJGes5
+uW9laI9VBgrtMTBbYrylBytXiF0Flzx+bd21krgL37NH2uU0EHPjSx571q/XGG2U
+4iOEPvPu7vtV8Rpqd0xQyaHcpoHNklcfND1c/6uZG1Sx9atDScRYHinUZvtTRtN+
+OY5vW+H7LJqT6CeMjh6Ev53V+0JCDZFQLaBdP/NanSQBUhPkyfyQSiqWOSuaMD6n
+Eu+BigmzwDlsauuReTJ65gdIGI9Egt7Ax/ooKpBvPkWeT+GORKTs+qGy6sbKXrTe
+crFFN/HZPWAJ+c8AEQEAAbQhSmFjayBMbG95ZCA8amFjay5sbG95ZEBnbWFpbC5j
+b20+iQG9BBMBCAAnAhsDBQsJCAcCBhUICQoLAgQWAgMBAh4BAheABQJXT0JHBQkE
+IBi0AAoJEHjpgENXEjtgqIQL/28NiIuK+yZNvfEDifjatDUnmgaCVoF2cy02Wa1z
+pKg7l2ccQBKzAoqN3j4GkgL1gxi96dp+rwgL5bwpRpVIrNLs1gFyIYDGxf/XZlbG
+m8ezOA62uz8ErIdAURK+LvCpIabth7++mnUwQioGRZCjNBp6Nx7QLM2cK8n+PqMh
+oLl6UZ4p3PZM76ygxOdukTXD9ExDVoiQIFCy+eh0g3JnIP6s0oAsnAl407UCqbXf
+ahXI0jnaioAfpBKcWV8TGG/KQ1Ln2v5Xt56SDdYKogt0xaY8u8RAELI9Gwkhhuf/
+t5kJmA/8J0qEUKUv7r8X/52PcZdjOGMnawT0mtWb13zqbAHGMhq+vW1HNwfprfes
+lqfbiJ5iUC6bammgOaUao14T3wzzuk0jP2VK7owpfLTgycAkbAis5M4gmfZwbiU2
++tZVU/h7E0THG7OQxbvMPeD5lcp2o4DlYqmpn07tGymiqUyqWdpHsCWS/EQYT+oN
+uZQ8GeVGJq+A/WIUzDzGjQp+JokB1AQTAQoAPgIbAwULCQgHAgYVCAkKCwIEFgID
+AQIeAQIXgBYhBE5gxzVRryGI3wpaYnjpgENXEjtgBQJeII8fBQkK8WWMAAoJEHjp
+gENXEjtgk8EL/3HCYNwfdn2XWcbOcvGC2avq0yzxKkhtk+7fu7LbyEp7EL+ipA+f
+gB7o/72ZeebVQHG8CYuYVvQzKI891hccyfQ/DfVuBrNvHq06+On6/YJuaKriP4i8
+YewgJj2LKjoE1+aL6/3HmoTrlXD0h8CBuKtVptGOVxsMfVK2fkIH5pGZajMP9egV
+ETIgDxLdO7FeJ4jro2fg8w+e/PqqruBxwQrdzzvjW+EqxcFNnql4CzP8JpQVuNMp
+Sh1QxilrnMDIGo8l8cZ4kMIeS3V+WJuNmTo/0hbH30qMKn/vKC9CwmTxazG12X7N
+9n0u0wuN8TFoAZsiYwGKiPygGxjtOzLbK9nDVd7ShFreEoFlOuTCMbc8FSk7FEGX
+prgybH0if7vXM0D5dA5MYhQi8yDmgCyg8vFF3kvMqVvIidOBre0xmZI35tgufduw
+ZppI4+PzrTMyLe0V6f13Sa6vE8S/hqq0FByGWUsRyfqbvmXYINxOqNxEuJ+mLhSE
+pyP0Qh0c9EmQLbQgSmFjayBMbG95ZCA8bGxveWRAcmFuZG9tYml0Lm5ldD6JAbkE
+EwECACMFAlUQXRMCGwMHCwkIBwMCAQYVCAIJCgsEFgIDAQIeAQIXgAAKCRB46YBD
+VxI7YI8aC/9GY4DNepqtopq1YlY1XrtyLg3tGzKvJVHXw07kGZiuvFOYXJcDzuKh
+FaGuIKxJ+7PvreOXycD/9/WFXyCwvhczMgbRf3lFqkjQdmvnwmIGUfZL3pqorJDd
+SjKiaXk8/NJBEBHlTN199bFECTzBr48keGHrzUPUYh2U3wo8CNW5ZsHHHmibjeoO
+pfdLgK+dnUNwOk3/nEZtWUd9cTwCnd5vyxt7I1p1ntb3JLAEd4z5wd2afJHbjQZ3
+uiTQBXDUd1PFH6I6fI7L+UeU+tGNPe3fe6G+zNhxmJPKBPEwzTA/r6iuLrQwNBHY
+PYm6J/fKBDU117Hnwuz4W9RSVVrtizCWIba6EptFencigruCHaO4CWbFl71Cu8n6
+ibEINHdKpE9qQzSD5kHfwsJ6FVnl9Qk8yJqh9U7NF/C8hHLbpF9J5n4WfN3qZyFp
+UMbsEVp6Rhv/ObnxwaqNYWCqCyiCawsNk//ks4Xr+HmePJ3XA9lzvgVCoMEra+n1
+RbHCEGImpUmJARwEEAEIAAYFAlYaUEwACgkQYhHr8e+637zLeQf+OdP/xE2YyFUJ
+L1+xEKHpvAeN+98Vn1C2sTmotNIaPwVBY9FLeA484IWdFwnJfXx1gQyybxlytz4B
+ZuC7Jzu60OEmk5IFRIqQoVywEXWCOUg/UEBWZm+ZcRzIFciqj9PcOfpt6s/aSZd5
++Rcm5HUGALYCqek2s9nGO8a1Wnk4m9d1u/RAGlxFM2the1v5p597ItGhcOP3tjWV
+PPOuTe0E+/FI3ZxpotKGdfS6F/GB2bP7kma2iVO621Cs9wsYmrZEamKpax7X7p9m
+yaAG0YRdCTslFd/EOTLOllPhy58DTr7qyswBPEI0x8WEDTE0G0IdQYNmLq4kuzeO
+aIlcUMULzYkBvwQTAQIAKQIbAwcLCQgHAwIBBhUIAgkKCwQWAgMBAh4BAheABQJX
+T0JHBQkEIBi0AAoJEHjpgENXEjtg6wEL/0Eh78RRRk5DjWpbPuIQZFbScNlPHuVT
+oNL3YNgtPvyF576aExl7ObpwsRAXPr6XxCYWhNW18ffgP42kZccGp6ACq8bhzh7g
+lbjnaAR0+78Of8DHWkCYwV00kEF3QKkNRUNDSgqLC5m+QWl1os5qRhmBcfNU4/zz
+51RzHqXcEPEZuHeInmrz496BNwzl/2eXnRGYENpc3FCBNsdvWwzaSotNJzdh4jFF
+1eSSUFpJ6FMKRHxvtx1G8+1FjJ/NHMBjf/ulnuukl/Hf5KB/n8VWSiCD5c8X5hED
+0lj9YHSJH1Q6rft5iWO+R1MyWlxZ2xJzZD5wfZ7gBEp8b32NH5mMmZqBhh5u/M7b
+kpgr5ETrKlTCuARFdjzkXVfG0VszBRCFMpCmO4WAucLYEcJvqJhxOayVoFP10taC
+Ouo+hPWM6v4WGMaWydcmcygscoF7u/K69om4h6kw/b2eB4DqMUe4GsaYbZHFfN/n
+0FHUgQ7flpX2O5rnTrQ++Yle+fc8g36KSIkB1gQTAQoAQAIbAwcLCQgHAwIBBhUI
+AgkKCwQWAgMBAh4BAheAFiEETmDHNVGvIYjfClpieOmAQ1cSO2AFAl4gjywFCQrx
+ZYwACgkQeOmAQ1cSO2DZbwv/Z1+f7l71wUh44A4ovbpmK+IqhNMqDwdpltWOzT2V
+UJQOrcWNIwtnycZ61S31/8XuDwOeh0xYIPYN7uayjwLpqLaPQPnU51ETH4/beCAh
+ExgK9KCPrxuKmdzkTkmgsw1IFlE+iPSN58Bz0oX0KXs31JXb27RfPMK1StZqaJj3
+zyq+TVnokpw8IQlWNyDuER50m0q1khim4qguXpKBKNJecmnaJVWZUwYjfoP8aOQB
+3LybmGpsMshkwirueeoRBcGiqdPofTxBQYbeRVa3JWdBBuWKh6m4eYl6mvHIEdzG
+7KmPG/bWCJSf8fVBcm/MH65aa7CcoKkCPuN+vDq9w3/Yp8+pxQlQFaRqvHGhQkxb
+xvn2kmgZt7CDRJYjYaD++I598S6YecA2hstNh8lj1YyPgU4/wFJ8N27+pI13koda
+n7tXx8LO6WjH45SPDgTEaO3i65rUQ3NHhQ8gGa8GUrTc1G+kzCG6+WvhYRrwn5gC
+Z+39H2CXLtWzgx0qvyXNqToGtB9KYWNrIExsb3lkIDxqYWNrQHJhbmRvbWJpdC5u
+ZXQ+iQG9BBMBCAAnBQJYCsPpAhsDBQkEIBi0BQsJCAcCBhUICQoLAgQWAgMBAh4B
+AheAAAoJEHjpgENXEjtgqiML/3u7F/QhLb9FabkuicFrxmsHrNs2M6EKlSB3ZCjM
+1krH/Ca59PpK4tjG8pC1FzJyb95vwE93qwl0yR+/K1PU94fudN9FFEHpcDIgh/rH
+QEZg9fZWZe6EmizJA7kbYrJvqZr7x/cmMV17EQHSJMPKiboAkaKejm7m7CiPpoU4
+fPATesU2wbN5uifSVsKWJYv28O0SawkoUC1aTG1HTxAblEriivQ5c2R45VcylcGd
+BG17xztSttiREG5nuzk26ZeB/19kLhgSAYEack+EQegFkzKxGQ2R4ScaZulqjjaB
+mQ688P56R2E+ly+Vjga3tck9ydxu/3KBvMqdEs1NSjx+74ULS4XwpPr+lVffKBOK
+r/5RC+jr5Z692rN3+IukkBG3a2iKJdRPcTSCeq4qZ75ZdYb5KCZK83Mh8jAaqd7g
+utimOTbywFBJeBWlDfAIolrQTMe8+wuOhWq48sdevxUMospb1RQ/oqq+DPGyNFN9
+hMdAxEvJaffEdSge/QyrTiMll4kB1AQTAQoAPgIbAwULCQgHAgYVCAkKCwIEFgID
+AQIeAQIXgBYhBE5gxzVRryGI3wpaYnjpgENXEjtgBQJeII8sBQkK8WWMAAoJEHjp
+gENXEjtgHqsL/0+CKqak4LUcrXuHUj0pcbNsr8RNRqHon+ICVQhOyYcZkUkGmUic
+4CVl3Gsus8kEfQmM9yP4T/+7CqDUFMDBihKnZ7evYJ2/SLmrjbmfdnQFIC03gjUa
+i2QlInpBU2CFWJOmlm8tJt3fnQ6whAIj9380kJFkLDP7XBP5LN6YkCiCvopT3Jr8
+sunOxxG/uT2oy+mXDsySPAr0NRvkymc7tGqO62Qst0ZYsrR4wnVGZNdmsdYN6jt8
+/2JQasOaIEFgYXLB4XqxjYrfcZryeESCeDorXSRvpreokJoOZpeThdDpGC32JHkI
+CBz1BaH5j8DJSedZN/LJdWi63E+eR6V8sFPvpt6oPgCELATskVZzOepFUlpr+5Vi
++/aCv2TvFfUkMh/Oa3dOn2k83a5kSuT66ODkfs8IOEII51tApSsUngs/JlNvD8iX
+8INgBnb8DJUen1FucmAwOhl9Uzh3VuBbWW3mB2uCwwQVHHL49NURP/S3TvoFrWbF
+IXM7NLfQA+nubLkBjQRVEF0TAQwA3o0T99H866uziNzpJpWhwpJn7+kZdWvFD9kW
+htruQmrT0MtBnbW8diSrvAysC1r0PqAflstn3TEjpJzJH19hNZgNd0MfHxKDsyPd
+kqHGOvW1CJxE9PoE/hYuoEgJ2VRZX/84JEWTXcbx94M75lPxg/91VSuPef3+bB84
+ebs2lvs8df8sW4PKj/URdlnKrDf8uUj7P7W4EoVgPZarMvDxKb9T5qPM/rLTjBSR
+/jlWMuQUjZs+ToJVg23ZO84TMg7fMEA3oNItIU5Nif1TBHa+um+gmwOONJGNyNtn
+/y4UJZ8uGBPAx5BwfSPEevjtCzZygCkcEAgHnt5Lpn/LhWNrdjQA2lvnUW7swM9d
+BdbiDz0YxMgQq7b8pQ3+icYhiHomz1Fg1/xIn7BpDQ1QtcAyTUAB+SeXYCmX2ApE
+MfoS969Bc9UjoSU5NnDiCobP3EaoL6BuaHZSZLmfwH+crZ3QAw37V6VkBqlMUV04
+yEx0N4GT7UZFD+3/OwtNQ9JJFUsXABEBAAGJAZ8EGAECAAkFAlUQXRMCGwwACgkQ
+eOmAQ1cSO2DeuAwAgMmCe4Rjud64kwjMfI7n1rxf72Kn1d94M3CNqomTSsiipJ2Q
+iqMbwjoLiVt4vSmcbOWK528SZCKPiGdI185STnygbJF3JR4r14LYp5n4ezeyoy4C
+GVgiH5FHqJ+jmSrFH+B6jqJcpLxWoNBGKqhJKsuqEhTuRCIVxBZzfBhpI5Rc2lnO
++VOUxgzio/1ivO7x0bW0pJPd+ZaLyX39OYcg+2ySAHR3NN2Qp7aRmkkUWq5i2ita
+0JDAX7Ca0DTY0wfCtDPCH9Go3P3BQTCFBUFr8DynTB0SyQsVBle3c+djwYdBXPn2
+0CuiXDeR6zT9Wu2AJQVLu2+af3EjqUnG95CI3oRzbPmBvAoFKGRK+imLSDzdgt5I
+0+sIgGYtII6bUCOxcXexBrMRioAaEHqqJsKy55vGemurxBr+PTCyrufxk7trpx1y
+eG7h8Xdh1ZxpaUJQrPNZvxZdeh4Jo4rGYPkiCwaIDGc8q+wGB7WsyGUKgkmEkSZS
+XDGg/rs23aYdtiH9
+=M25I
+-----END PGP PUBLIC KEY BLOCK-----
+
+
+This key is used for signing git commits:
+
+pub rsa2048/AB50F90D 2016-03-03 [SC] [expires: 2020-03-02]
+ Key fingerprint = 1175 1014 9DF4 18AB D19C B06D 9FFD 596F AB50 F90D
+ uid Jack Lloyd (Git Signing Key) <jack@randombit.net>
+
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+mQENBFbYWi4BCACjgz3gYgWMybPLiovNLnLonG0ex2y9kJgsR+Pm08L2fwCVCaHx
+wjlK5Oq04x3bvujZk7P0TThqS1WonYtEiVxz3Hcvt7rlU5fSCj/1uYmZV+mbPBdY
+f/yXMx3UD1UkZrdzbM21L78dfOoLZ2ybyuk4QOEad/AkouDCUA2pY3DJdNyX8Aee
+dBdQ+sQqF6DyDz4SDY+KAq3nFmmT04bdkiL9sZb2HUokxIdeA6HIR+CxxFaZzrYW
+Ky4iNPS1Zxv5D1KmpZzUOfN9RTgkbdRltgWxjpB+DFNUkpu3hYm/Y9lTPqGBt2C8
+JfIS5OaHxUVz0A+DtIGy+lk8/O5ek4suQBZ3ABEBAAG0MUphY2sgTGxveWQgKEdp
+dCBTaWduaW5nIEtleSkgPGphY2tAcmFuZG9tYml0Lm5ldD6JAVcEEwEIAEECGwMF
+CwkIBwIGFQgJCgsCBBYCAwECHgECF4ACGQEWIQQRdRAUnfQYq9GcsG2f/Vlvq1D5
+DQUCXl5bBQUJDSmbVwAKCRCf/Vlvq1D5DcTHB/9t0LZYlso93Jdq+ozRNTuo/SJl
+MdViPlZ+upLi/5DqXBzUsNp9S4NkaaCendOIPbugSiGwWMk8LQpb7pTlbVFUJoem
+Y0BUwuS5uk6YK4gxrTFl1P+VCbLA3UjXch1GWfh8JSp7SMcC0vcPd3WmuI06VdiU
+sMeWThP56UA9SJbwWM735bosgYtdE3BMnEjDmvnmLcM3cn+saW+TkTFr/kkGjFHA
+1Fg62vRfvsPokq5S1PdUjE7eY5u2W2r94mMcS/kY/Xg470XsBSbyjIviqX2irXgf
+Ln0bmqsxNWiedocdVrdsk4n98eLXosKq4HKSWO9fwlwbF6P78K5Ygu1z2eGXiQE9
+BBMBCAAnBQJYD2ybAhsDBQkHhM4ABQsJCAcCBhUICQoLAgQWAgMBAh4BAheAAAoJ
+EJ/9WW+rUPkNXfIIAIOgepYeZN2HL2T3A8pk+5JR2cfBoRufYgnG6mVHPejgZDW+
+9PH1tPDgidnePw3/jfD1oskt1VwOCkqaSBAcG2QeOBEklcsrQuvOfW7B7XFydYGw
+T9Fm8/sxekiGjDQQWQEEH4j3Vt1GlI2sJrpqMe/WMXISsEnLfw0kDspUSlFOn8oy
+rBU9Pd8KaSi38lp05a9Mcun5enJ3JpQHQ5RlNXDNh/Sn9NZ+MKWggytZqIy82jnv
+6zo0raZDup4hLKopjen/SINR5XcwRK2lUtYVwwQH8FHG9IkeVJNWJkVCdZLB08sq
+4WHRbtVjy6mpvEHTmolAtHjLSiV5W50LDKLwX260MkphY2sgTGxveWQgKEdpdCBT
+aWduaW5nIEtleSkgPGxsb3lkQHJhbmRvbWJpdC5uZXQ+iQE9BBMBCAAnBQJW2FwD
+AhsDBQkHhM4ABQsJCAcCBhUICQoLAgQWAgMBAh4BAheAAAoJEJ/9WW+rUPkNK3cI
+AI+6M5JixbjdyoVSDor0RW4u5h6UESe2mRZ5YFzh8F5uGms8PE5HrIZAfNVy9lJr
+DoLSPpeb5KLAdGpaJ1NuJJ2glYzZFYr05gVHgCKJgfo6jkW22XnwO+vyXK+6TB6A
+ExgO0RdN5aZcHL8KCfCWncVsWvF8d2cvbdb3sTfqtZoZ0l1DyYA62pz5BZd+iySj
+nWGsL9YM53pC6hhq62onSqfv0X0fqzZ3+zIqTgVMOJxIFCXBiUBkHEuizUULfPrP
+xfAiaEQcaOcpEos+/OsG3e3nFmXEAlDa9ZsS2VEaZ/nFJPIaKeDXt80ptScnxp2t
+lecw8hR/OfJJ6MFMw4W/J8WJAZwEEAEIAAYFAlbYXDcACgkQeOmAQ1cSO2C8Kwv/
+ebJNrhMoknG5dSPVJpJxL0ta/m4KYBjX94VsB6Ofz96UmnNlCe9Exbi02ecLygQf
+B9UUv7nsrORGOSoIEIV/RSYgN2eYUM21DHddlgVG3DTRrG//iV6rzAdgkZdRkxBh
+nzeFJRv4TMmnRQXM2x+gjognDWLpjgdvkRFcv5l6fTH24IldcdymnvmqBmsLvqoK
+Fj13CtsDjngp5gGgp/ieIW6sPmgQX1pJCWHFDe/0qcjvuy0fKMq0Oyd0/8dXCnNa
+wPb/9xlQY+lCpHILfvFsVty5djqHqDNKEBuYxyPjHICPGRukSK+zCS0EE8nz/oxo
+r4OIr5svawfIO5Zw69USo01jC+K1OSRJQthFft5GqTwkfHniYJSVh7K1rPI/H35t
+f+XZLBwlafWs7vQb8XZfAPzhqw9blc5RS478rqzNQ0F1PHITjbaSCkMCgaNa2aea
+oIw9uQjp6uB2T4xGAeqsd6JPrr5g1VZFPqthXdiDu6JmN4aVksG/SCx2Cwv2qiuS
+=kPHM
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/comm/third_party/botan/doc/roadmap.rst b/comm/third_party/botan/doc/roadmap.rst
new file mode 100644
index 0000000000..6a5808d562
--- /dev/null
+++ b/comm/third_party/botan/doc/roadmap.rst
@@ -0,0 +1,54 @@
+
+Development Roadmap
+========================================
+
+Near Term Plans
+----------------------------------------
+
+Here is an outline for the development plans over the next 12-18 months, as of
+June 2019.
+
+TLS Hardening/Testing
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Leverage TLS-Attacker better, for example using custom workflows. Add
+interop testing with OpenSSL as part of CI. Improve fuzzer coverage.
+
+Expose TLS at FFI layer
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Exposing TLS to C would allow for many new applications to make use of Botan.
+
+TLS v1.3
+^^^^^^^^^^^^^^^
+
+A complete implementation of TLS v1.3 is planned. DTLS v1.3 may or may not be
+supported as well.
+
+Botan 3.x
+----------------------------------------
+
+Botan 3 is currently planned for release in 2021. Botan 2 will remain
+supported for several years past that, to allow plenty of time for
+applications to switch over.
+
+This version will adopt C++17 and use new std types such as string_view,
+optional, and any, along with adopting memory span and guarded integer
+types. All deprecated features/APIs of 2.x (which notably includes TLS v1.0/v1.1
+support) will be removed. Beyond explicitly deprecated functionality, there
+should be no breaking API changes in the transition to 3.x
+
+Features currently targeted for Botan 3 include
+
+* New post-quantum algorithms: especially a CCA2 secure encryption scheme and a
+ lattice-based signature scheme are of interest.
+
+* Password Authenticated Key Exchanges: one or more modern PAKEs
+ (such as SPAKE2+ or OPAQUE) to replace SRP.
+
+* Elliptic Curve Pairings: useful in many interesting protocols.
+ BN-256 and BLS12-381 seem the most likely.
+
+* New ASN.1 library
+
+Some of these features may end being backported to Botan 2 as well.
diff --git a/comm/third_party/botan/doc/security.rst b/comm/third_party/botan/doc/security.rst
new file mode 100644
index 0000000000..3a2059879c
--- /dev/null
+++ b/comm/third_party/botan/doc/security.rst
@@ -0,0 +1,352 @@
+
+.. highlight:: none
+
+Security Advisories
+========================================
+
+If you think you have found a security bug in Botan please contact
+Jack Lloyd (jack@randombit.net). If you would like to encrypt your
+mail please use::
+
+ pub rsa3072/57123B60 2015-03-23
+ Key fingerprint = 4E60 C735 51AF 2188 DF0A 5A62 78E9 8043 5712 3B60
+ uid Jack Lloyd <jack@randombit.net>
+
+This key can be found in the file ``doc/pgpkey.txt`` or online at
+https://keybase.io/jacklloyd and on most PGP keyservers.
+
+2020
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* 2020-07-05: Failure to enforce name constraints on alternative names
+
+ The path validation algorithm enforced name constraints on the primary DN
+ included in the certificate but failed to do so against alternative DNs which
+ may be included in the subject alternative name. This would allow a corrupted
+ sub-CA which was constrained by a name constraints extension in its own
+ certificate to issue a certificate containing a prohibited DN. Until 2.15.0,
+ there was no API to access these alternative name DNs so it is unlikely that
+ any application would make incorrect access control decisions on the basis of
+ the incorrect DN. Reported by Mario Korth of Ruhr-Universität Bochum.
+
+ Introduced in 1.11.29, fixed in 2.15.0
+
+* 2020-03-24: Side channel during CBC padding
+
+ The CBC padding operations were not constant time and as a result would leak
+ the length of the plaintext values which were being padded to an attacker
+ running a side channel attack via shared resources such as cache or branch
+ predictor. No information about the contents was leaked, but the length alone
+ might be used to make inferences about the contents. This issue affects TLS
+ CBC ciphersuites as well as CBC encryption using PKCS7 or other similar padding
+ mechanisms. In all cases, the unpadding operations were already constant time
+ and are not affected. Reported by Maximilian Blochberger of Universität
+ Hamburg.
+
+ Fixed in 2.14.0, all prior versions affected.
+
+2018
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* 2018-12-17 (CVE-2018-20187): Side channel during ECC key generation
+
+ A timing side channel during ECC key generation could leak information about
+ the high bits of the secret scalar. Such information allows an attacker to
+ perform a brute force attack on the key somewhat more efficiently than they
+ would otherwise. Found by Ján Jančár using ECTester.
+
+ Introduced in 1.11.20, fixed in 2.8.0.
+
+* 2018-06-13 (CVE-2018-12435): ECDSA side channel
+
+ A side channel in the ECDSA signature operation could allow a local attacker
+ to recover the secret key. Found by Keegan Ryan of NCC Group.
+
+ Bug introduced in 2.5.0, fixed in 2.7.0. The 1.10 branch is not affected.
+
+* 2018-04-10 (CVE-2018-9860): Memory overread in TLS CBC decryption
+
+ An off by one error in TLS CBC decryption meant that for a particular
+ malformed ciphertext, the receiver would miscompute a length field and HMAC
+ exactly 64K bytes of data following the record buffer as if it was part of the
+ message. This cannot be used to leak information since the MAC comparison will
+ subsequently fail and the connection will be closed. However it might be used
+ for denial of service. Found by OSS-Fuzz.
+
+ Bug introduced in 1.11.32, fixed in 2.6.0
+
+* 2018-03-29 (CVE-2018-9127): Invalid wildcard match
+
+ RFC 6125 wildcard matching was incorrectly implemented, so that a wildcard
+ certificate such as ``b*.domain.com`` would match any hosts ``*b*.domain.com``
+ instead of just server names beginning with ``b``. The host and certificate
+ would still have to be in the same domain name. Reported by Fabian Weißberg of
+ Rohde and Schwarz Cybersecurity.
+
+ Bug introduced in 2.2.0, fixed in 2.5.0
+
+2017
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* 2017-10-02 (CVE-2017-14737): Potential side channel using cache information
+
+ In the Montgomery exponentiation code, a table of precomputed values
+ is used. An attacker able to analyze which cache lines were accessed
+ (perhaps via an active attack such as Prime+Probe) could recover
+ information about the exponent. Identified in "CacheD: Identifying
+ Cache-Based Timing Channels in Production Software" by Wang, Wang,
+ Liu, Zhang, and Wu (Usenix Security 2017).
+
+ Fixed in 1.10.17 and 2.3.0, all prior versions affected.
+
+* 2017-07-16: Failure to fully zeroize memory before free
+
+ The secure_allocator type attempts to zeroize memory before freeing it. Due to
+ a error sometimes only a portion of the memory would be zeroed, because of a
+ confusion between the number of elements vs the number of bytes that those
+ elements use. So byte vectors would always be fully zeroed (since the two
+ notions result in the same value), but for example with an array of 32-bit
+ integers, only the first 1/4 of the elements would be zeroed before being
+ deallocated. This may result in information leakage, if an attacker can access
+ memory on the heap. Reported by Roman Pozlevich.
+
+ Bug introduced in 1.11.10, fixed in 2.2.0
+
+* 2017-04-04 (CVE-2017-2801): Incorrect comparison in X.509 DN strings
+
+ Botan's implementation of X.509 name comparisons had a flaw which
+ could result in an out of bound memory read while processing a
+ specially formed DN. This could potentially be exploited for
+ information disclosure or denial of service, or result in incorrect
+ validation results. Found independently by Aleksandar Nikolic of
+ Cisco Talos, and OSS-Fuzz automated fuzzing infrastructure.
+
+ Bug introduced in 1.6.0 or earlier, fixed in 2.1.0 and 1.10.16
+
+* 2017-03-23 (CVE-2017-7252): Incorrect bcrypt computation
+
+ Botan's implementation of bcrypt password hashing scheme truncated long
+ passwords at 56 characters, instead of at bcrypt's standard 72 characters
+ limit. Passwords with lengths between these two bounds could be cracked more
+ easily than should be the case due to the final password bytes being ignored.
+ Found and reported by Solar Designer.
+
+ Bug introduced in 1.11.0, fixed in 2.1.0.
+
+2016
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* 2016-11-27 (CVE-2016-9132) Integer overflow in BER decoder
+
+ While decoding BER length fields, an integer overflow could occur. This could
+ occur while parsing untrusted inputs such as X.509 certificates. The overflow
+ does not seem to lead to any obviously exploitable condition, but exploitation
+ cannot be positively ruled out. Only 32-bit platforms are likely affected; to
+ cause an overflow on 64-bit the parsed data would have to be many gigabytes.
+ Bug found by Falko Strenzke, cryptosource GmbH.
+
+ Fixed in 1.10.14 and 1.11.34, all prior versions affected.
+
+* 2016-10-26 (CVE-2016-8871) OAEP side channel
+
+ A side channel in OAEP decoding could be used to distinguish RSA ciphertexts
+ that did or did not have a leading 0 byte. For an attacker capable of
+ precisely measuring the time taken for OAEP decoding, this could be used as an
+ oracle allowing decryption of arbitrary RSA ciphertexts. Remote exploitation
+ seems difficult as OAEP decoding is always paired with RSA decryption, which
+ takes substantially more (and variable) time, and so will tend to mask the
+ timing channel. This attack does seems well within reach of a local attacker
+ capable of a cache or branch predictor based side channel attack. Finding,
+ analysis, and patch by Juraj Somorovsky.
+
+ Introduced in 1.11.29, fixed in 1.11.33
+
+* 2016-08-30 (CVE-2016-6878) Undefined behavior in Curve25519
+
+ On systems without a native 128-bit integer type, the Curve25519 code invoked
+ undefined behavior. This was known to produce incorrect results on 32-bit ARM
+ when compiled by Clang.
+
+ Introduced in 1.11.12, fixed in 1.11.31
+
+* 2016-08-30 (CVE-2016-6879) Bad result from X509_Certificate::allowed_usage
+
+ If allowed_usage was called with more than one Key_Usage set in the enum
+ value, the function would return true if *any* of the allowed usages were set,
+ instead of if *all* of the allowed usages are set. This could be used to
+ bypass an application key usage check. Credit to Daniel Neus of Rohde &
+ Schwarz Cybersecurity for finding this issue.
+
+ Introduced in 1.11.0, fixed in 1.11.31
+
+* 2016-03-17 (CVE-2016-2849): ECDSA side channel
+
+ ECDSA (and DSA) signature algorithms perform a modular inverse on the
+ signature nonce `k`. The modular inverse algorithm used had input dependent
+ loops, and it is possible a side channel attack could recover sufficient
+ information about the nonce to eventually recover the ECDSA secret key. Found
+ by Sean Devlin.
+
+ Introduced in 1.7.15, fixed in 1.10.13 and 1.11.29
+
+* 2016-03-17 (CVE-2016-2850): Failure to enforce TLS policy
+
+ TLS v1.2 allows negotiating which signature algorithms and hash functions each
+ side is willing to accept. However received signatures were not actually
+ checked against the specified policy. This had the effect of allowing a
+ server to use an MD5 or SHA-1 signature, even though the default policy
+ prohibits it. The same issue affected client cert authentication.
+
+ The TLS client also failed to verify that the ECC curve the server chose to
+ use was one which was acceptable by the client policy.
+
+ Introduced in 1.11.0, fixed in 1.11.29
+
+* 2016-02-01 (CVE-2016-2196): Overwrite in P-521 reduction
+
+ The P-521 reduction function would overwrite zero to one word
+ following the allocated block. This could potentially result
+ in remote code execution or a crash. Found with AFL
+
+ Introduced in 1.11.10, fixed in 1.11.27
+
+* 2016-02-01 (CVE-2016-2195): Heap overflow on invalid ECC point
+
+ The PointGFp constructor did not check that the affine coordinate
+ arguments were less than the prime, but then in curve multiplication
+ assumed that both arguments if multiplied would fit into an integer
+ twice the size of the prime.
+
+ The bigint_mul and bigint_sqr functions received the size of the
+ output buffer, but only used it to dispatch to a faster algorithm in
+ cases where there was sufficient output space to call an unrolled
+ multiplication function.
+
+ The result is a heap overflow accessible via ECC point decoding,
+ which accepted untrusted inputs. This is likely exploitable for
+ remote code execution.
+
+ On systems which use the mlock pool allocator, it would allow an
+ attacker to overwrite memory held in secure_vector objects. After
+ this point the write will hit the guard page at the end of the
+ mmap'ed region so it probably could not be used for code execution
+ directly, but would allow overwriting adjacent key material.
+
+ Found by Alex Gaynor fuzzing with AFL
+
+ Introduced in 1.9.18, fixed in 1.11.27 and 1.10.11
+
+* 2016-02-01 (CVE-2016-2194): Infinite loop in modular square root algorithm
+
+ The ressol function implements the Tonelli-Shanks algorithm for
+ finding square roots could be sent into a nearly infinite loop due
+ to a misplaced conditional check. This could occur if a composite
+ modulus is provided, as this algorithm is only defined for primes.
+ This function is exposed to attacker controlled input via the OS2ECP
+ function during ECC point decompression. Found by AFL
+
+ Introduced in 1.7.15, fixed in 1.11.27 and 1.10.11
+
+2015
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* 2015-11-04: TLS certificate authentication bypass
+
+ When the bugs affecting X.509 path validation were fixed in 1.11.22, a check
+ in Credentials_Manager::verify_certificate_chain was accidentally removed
+ which caused path validation failures not to be signaled to the TLS layer. So
+ for affected versions, certificate authentication in TLS is bypassed. As a
+ workaround, applications can override the call and implement the correct
+ check. Reported by Florent Le Coz in GH #324
+
+ Introduced in 1.11.22, fixed in 1.11.24
+
+* 2015-10-26 (CVE-2015-7824): Padding oracle attack on TLS
+
+ A padding oracle attack was possible against TLS CBC ciphersuites because if a
+ certain length check on the packet fields failed, a different alert type than
+ one used for message authentication failure would be returned to the sender.
+ This check triggering would leak information about the value of the padding
+ bytes and could be used to perform iterative decryption.
+
+ As with most such oracle attacks, the danger depends on the underlying
+ protocol - HTTP servers are particularly vulnerable. The current analysis
+ suggests that to exploit it an attacker would first have to guess several
+ bytes of plaintext, but again this is quite possible in many situations
+ including HTTP.
+
+ Found in a review by Sirrix AG and 3curity GmbH.
+
+ Introduced in 1.11.0, fixed in 1.11.22
+
+* 2015-10-26 (CVE-2015-7825): Infinite loop during certificate path validation
+
+ When evaluating a certificate path, if a loop in the certificate chain
+ was encountered (for instance where C1 certifies C2, which certifies C1)
+ an infinite loop would occur eventually resulting in memory exhaustion.
+ Found in a review by Sirrix AG and 3curity GmbH.
+
+ Introduced in 1.11.6, fixed in 1.11.22
+
+* 2015-10-26 (CVE-2015-7826): Acceptance of invalid certificate names
+
+ RFC 6125 specifies how to match a X.509v3 certificate against a DNS name
+ for application usage.
+
+ Otherwise valid certificates using wildcards would be accepted as matching
+ certain hostnames that should they should not according to RFC 6125. For
+ example a certificate issued for ``*.example.com`` should match
+ ``foo.example.com`` but not ``example.com`` or ``bar.foo.example.com``. Previously
+ Botan would accept such a certificate as also valid for ``bar.foo.example.com``.
+
+ RFC 6125 also requires that when matching a X.509 certificate against a DNS
+ name, the CN entry is only compared if no subjectAlternativeName entry is
+ available. Previously X509_Certificate::matches_dns_name would always check
+ both names.
+
+ Found in a review by Sirrix AG and 3curity GmbH.
+
+ Introduced in 1.11.0, fixed in 1.11.22
+
+* 2015-10-26 (CVE-2015-7827): PKCS #1 v1.5 decoding was not constant time
+
+ During RSA decryption, how long decoding of PKCS #1 v1.5 padding took was
+ input dependent. If these differences could be measured by an attacker, it
+ could be used to mount a Bleichenbacher million-message attack. PKCS #1 v1.5
+ decoding has been rewritten to use a sequence of operations which do not
+ contain any input-dependent indexes or jumps. Notations for checking constant
+ time blocks with ctgrind (https://github.com/agl/ctgrind) were added to PKCS
+ #1 decoding among other areas. Found in a review by Sirrix AG and 3curity GmbH.
+
+ Fixed in 1.11.22 and 1.10.13. Affected all previous versions.
+
+* 2015-08-03 (CVE-2015-5726): Crash in BER decoder
+
+ The BER decoder would crash due to reading from offset 0 of an empty vector if
+ it encountered a BIT STRING which did not contain any data at all. This can be
+ used to easily crash applications reading untrusted ASN.1 data, but does not
+ seem exploitable for code execution. Found with afl.
+
+ Fixed in 1.11.19 and 1.10.10, affected all previous versions of 1.10 and 1.11
+
+* 2015-08-03 (CVE-2015-5727): Excess memory allocation in BER decoder
+
+ The BER decoder would allocate a fairly arbitrary amount of memory in a length
+ field, even if there was no chance the read request would succeed. This might
+ cause the process to run out of memory or invoke the OOM killer. Found with afl.
+
+ Fixed in 1.11.19 and 1.10.10, affected all previous versions of 1.10 and 1.11
+
+2014
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* 2014-04-10 (CVE-2014-9742): Insufficient randomness in Miller-Rabin primality check
+
+ A bug in the Miller-Rabin primality test resulted in only a single random base
+ being used instead of a sequence of such bases. This increased the probability
+ that a non-prime would be accepted by is_prime or that a randomly generated
+ prime might actually be composite. The probability of a random 1024 bit
+ number being incorrectly classed as prime with a single base is around 2^-40.
+ Reported by Jeff Marrison.
+
+ Introduced in 1.8.3, fixed in 1.10.8 and 1.11.9
diff --git a/comm/third_party/botan/doc/side_channels.rst b/comm/third_party/botan/doc/side_channels.rst
new file mode 100644
index 0000000000..1f94874f07
--- /dev/null
+++ b/comm/third_party/botan/doc/side_channels.rst
@@ -0,0 +1,449 @@
+Side Channels
+=========================
+
+Many cryptographic systems can be easily broken by side channels. This document
+notes side channel protections which are currently implemented, as well as areas
+of the code which are known to be vulnerable to side channels. The latter are
+obviously all open for future improvement.
+
+The following text assumes the reader is already familiar with cryptographic
+implementations, side channel attacks, and common countermeasures.
+
+Modular Exponentiation
+------------------------
+
+Modular exponentiation uses a fixed window algorithm with Montgomery
+representation. A side channel silent table lookup is used to access the
+precomputed powers. The caller provides the maximum possible bit length of the
+exponent, and the exponent is zero-padded as required. For example, in a DSA
+signature with 256-bit q, the caller will specify a maximum length of exponent
+of 256 bits, even if the k that was generated was 250 bits. This avoids leaking
+the length of the exponent through the number of loop iterations.
+See monty_exp.cpp and monty.cpp
+
+Karatsuba multiplication algorithm avoids any conditional branches; in
+cases where different operations must be performed it instead uses masked
+operations. See mp_karat.cpp for details.
+
+The Montgomery reduction is written to run in constant time.
+The final reduction is handled with a masked subtraction. See mp_monty.cpp.
+
+Barrett Reduction
+--------------------
+
+The Barrett reduction code is written to avoid input dependent branches. The
+Barrett algorithm only works for inputs up to a certain size, and larger values
+fall back on a different (slower) division algorithm. This secondary algorithm
+is also const time, but the branch allows detecting when a value larger than
+2^{2k} was reduced, where k is the word length of the modulus. This leaks only
+the size of the two values, and not anything else about their value.
+
+RSA
+----------------------
+
+Blinding is always used to protect private key operations (there is no way to
+turn it off). Both base blinding and exponent blinding are used.
+
+For base blinding, as an optimization, instead of choosing a new random mask and
+inverse with each decryption, both the mask and its inverse are simply squared
+to choose the next blinding factor. This is much faster than computing a fresh
+value each time, and the additional relation is thought to provide only minimal
+useful information for an attacker. Every BOTAN_BLINDING_REINIT_INTERVAL
+(default 64) operations, a new starting point is chosen.
+
+Exponent blinding uses new values for each signature, with 64 bit masks.
+
+RSA signing uses the CRT optimization, which is much faster but vulnerable to
+trivial fault attacks [RsaFault] which can result in the key being entirely
+compromised. To protect against this (or any other computational error which
+would have the same effect as a fault attack in this case), after every private
+key operation the result is checked for consistency with the public key. This
+introduces only slight additional overhead and blocks most fault attacks; it is
+possible to use a second fault attack to bypass this verification, but such a
+double fault attack requires significantly more control on the part of an
+attacker than a BellCore style attack, which is possible if any error at all
+occurs during either modular exponentiation involved in the RSA signature
+operation.
+
+See blinding.cpp and rsa.cpp.
+
+If the OpenSSL provider is enabled, then no explicit blinding is done; we assume
+OpenSSL handles this. See openssl_rsa.cpp.
+
+Decryption of PKCS #1 v1.5 Ciphertexts
+----------------------------------------
+
+This padding scheme is used with RSA, and is very vulnerable to errors. In a
+scenario where an attacker can repeatedly present RSA ciphertexts, and a
+legitimate key holder will attempt to decrypt each ciphertext and simply
+indicates to the attacker if the PKCS padding was valid or not (without
+revealing any additional information), the attacker can use this behavior as an
+oracle to perform iterative decryption of arbitrary RSA ciphertexts encrypted
+under that key. This is the famous million message attack [MillionMsg]. A side
+channel such as a difference in time taken to handle valid and invalid RSA
+ciphertexts is enough to mount the attack [MillionMsgTiming].
+
+As a first step, the PKCS v1.5 decoding operation runs without any
+conditional jumps or indexes, with the only variance in runtime being
+based on the length of the public modulus, which is public information.
+
+Preventing the attack in full requires some application level changes. In
+protocols which know the expected length of the encrypted key, PK_Decryptor
+provides the function `decrypt_or_random` which first generates a random fake
+key, then decrypts the presented ciphertext, then in constant time either copies
+out the random key or the decrypted plaintext depending on if the ciphertext was
+valid or not (valid padding and expected plaintext length). Then in the case of
+an attack, the protocol will carry on with a randomly chosen key, which will
+presumably cause total failure in a way that does not allow an attacker to
+distinguish (via any timing or other side channel, nor any error messages
+specific to the one situation vs the other) if the RSA padding was valid or
+invalid.
+
+One very important user of PKCS #1 v1.5 encryption is the TLS protocol. In TLS,
+some extra versioning information is embedded in the plaintext message, along
+with the key. It turns out that this version information must be treated in an
+identical (constant-time) way with the PKCS padding, or again the system is
+broken. [VersionOracle]. This is supported by a special version of
+PK_Decryptor::decrypt_or_random that additionally allows verifying one or more
+content bytes, in addition to the PKCS padding.
+
+See eme_pkcs.cpp and pubkey.cpp.
+
+Verification of PKCS #1 v1.5 Signatures
+----------------------------------------
+
+One way of verifying PKCS #1 v1.5 signature padding is to decode it with an
+ASN.1 BER parser. However such a design commonly leads to accepting signatures
+besides the (single) valid RSA PKCS #1 v1.5 signature for any given message,
+because often the BER parser accepts variations of the encoding which are
+actually invalid. It also needlessly exposes the BER parser to untrusted inputs.
+
+It is safer and simpler to instead re-encode the hash value we are expecting
+using the PKCS #1 v1.5 encoding rules, and const time compare our expected
+encoding with the output of the RSA operation. So that is what Botan does.
+
+See emsa_pkcs.cpp.
+
+OAEP
+----------------------
+
+RSA OAEP is (PKCS#1 v2) is the recommended version of RSA encoding standard,
+because it is not directly vulnerable to Bleichenbacher attack. However, if
+implemented incorrectly, a side channel can be presented to an attacker and
+create an oracle for decrypting RSA ciphertexts [OaepTiming].
+
+This attack is avoided in Botan by making the OAEP decoding operation run
+without any conditional jumps or indexes, with the only variance in runtime
+coming from the length of the RSA key (which is public information).
+
+See eme_oaep.cpp.
+
+ECC point decoding
+----------------------
+
+The API function OS2ECP, which is used to convert byte strings to ECC points,
+verifies that all points satisfy the ECC curve equation. Points that do not
+satisfy the equation are invalid, and can sometimes be used to break
+protocols ([InvalidCurve] [InvalidCurveTLS]). See point_gfp.cpp.
+
+ECC scalar multiply
+----------------------
+
+There are several different implementations of ECC scalar multiplications which
+depend on the API invoked. This include ``PointGFp::operator*``,
+``EC_Group::blinded_base_point_multiply`` and
+``EC_Group::blinded_var_point_multiply``.
+
+The ``PointGFp::operator*`` implementation uses the Montgomery ladder, which is
+fairly resistant to side channels. However it leaks the size of the scalar,
+because the loop iterations are bounded by the scalar size. It should not be
+used in cases when the scalar is a secret.
+
+Both ``blinded_base_point_multiply`` and ``blinded_var_point_multiply`` apply
+side channel countermeasures. The scalar is masked by a multiple of the group
+order (this is commonly called Coron's first countermeasure [CoronDpa]),
+currently the mask is an 80 bit random value.
+
+Botan stores all ECC points in Jacobian representation. This form allows faster
+computation by representing points (x,y) as (X,Y,Z) where x=X/Z^2 and
+y=Y/Z^3. As the representation is redundant, for any randomly chosen non-zero r,
+(X*r^2,Y*r^3,Z*r) is an equivalent point. Changing the point values prevents an
+attacker from mounting attacks based on the input point remaining unchanged over
+multiple executions. This is commonly called Coron's third countermeasure, see
+again [CoronDpa].
+
+The base point multiplication algorithm is a comb-like technique which
+precomputes ``P^i,(2*P)^i,(3*P)^i`` for all ``i`` in the range of valid scalars.
+This means the scalar multiplication involves only point additions and no
+doublings, which may help against attacks which rely on distinguishing between
+point doublings and point additions. The elements of the table are accessed by
+masked lookups, so as not to leak information about bits of the scalar via a
+cache side channel. However, whenever 3 sequential bits of the (masked) scalar
+are all 0, no operation is performed in that iteration of the loop. This exposes
+the scalar multiply to a cache-based side channel attack; scalar blinding is
+necessary to prevent this attack from leaking information about the scalar.
+
+The variable point multiplication algorithm uses a fixed-window algorithm. Since
+this is normally invoked using untrusted points (eg during ECDH key exchange) it
+randomizes all inputs to prevent attacks which are based on chosen input
+points. The table of precomputed multiples is accessed using a masked lookup
+which should not leak information about the secret scalar to an attacker who can
+mount a cache-based side channel attack.
+
+See point_gfp.cpp and point_mul.cpp
+
+ECDH
+----------------------
+
+ECDH verifies (through its use of OS2ECP) that all input points received from
+the other party satisfy the curve equation. This prevents twist attacks. The
+same check is performed on the output point, which helps prevent fault attacks.
+
+ECDSA
+----------------------
+
+Inversion of the ECDSA nonce k must be done in constant time, as any leak of
+even a single bit of the nonce can be sufficient to allow recovering the private
+key. In Botan all inverses modulo an odd number are performed using a constant
+time algorithm due to Niels Möller.
+
+x25519
+----------------------
+
+The x25519 code is independent of the main Weierstrass form ECC code, instead
+based on curve25519-donna-c64.c by Adam Langley. The code seems immune to cache
+based side channels. It does make use of integer multiplications; on some old
+CPUs these multiplications take variable time and might allow a side channel
+attack. This is not considered a problem on modern processors.
+
+TLS CBC ciphersuites
+----------------------
+
+The original TLS v1.0 CBC Mac-then-Encrypt mode is vulnerable to an oracle
+attack. If an attacker can distinguish padding errors through different error
+messages [TlsCbcOracle] or via a side channel attack like [Lucky13], they can
+abuse the server as a decryption oracle.
+
+The side channel protection for Lucky13 follows the approach proposed in the
+Lucky13 paper. It is not perfectly constant time, but does hide the padding
+oracle in practice. Tools to test TLS CBC decoding are included in the timing
+tests. See https://github.com/randombit/botan/pull/675 for more information.
+
+The Encrypt-then-MAC extension, which completely avoids the side channel, is
+implemented and used by default for CBC ciphersuites.
+
+CBC mode padding
+----------------------
+
+In theory, any good protocol protects CBC ciphertexts with a MAC. But in
+practice, some protocols are not good and cannot be fixed immediately. To avoid
+making a bad problem worse, the code to handle decoding CBC ciphertext padding
+bytes runs in constant time, depending only on the block size of the cipher.
+
+AES
+----------------------
+
+Some x86, ARMv8 and POWER processors support AES instructions which
+are fast and are thought to be side channel silent. These instructions
+are used when available.
+
+On CPUs which do not have hardware AES instructions but do support SIMD vectors
+with a byte shuffle (including x86's SSSE3, ARM's NEON and PowerPC AltiVec), a
+version of AES is implemented which is side channel silent. This implementation
+is based on code by Mike Hamburg [VectorAes], see aes_vperm.cpp.
+
+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
+as CTR, GCM or XTS are relatively fast, but others such as CBC encryption
+suffer.
+
+GCM
+---------------------
+
+On platforms that support a carryless multiply instruction (ARMv8 and recent x86),
+GCM is fast and constant time.
+
+On all other platforms, GCM uses an algorithm based on precomputing all powers
+of H from 1 to 128. Then for every bit of the input a mask is formed which
+allows conditionally adding that power without leaking information via a cache
+side channel. There is also an SSSE3 variant of this algorithm which is somewhat
+faster on processors which have SSSE3 but no AES-NI instructions.
+
+OCB
+-----------------------
+
+It is straightforward to implement OCB mode in a efficient way that does not
+depend on any secret branches or lookups. See ocb.cpp for the implementation.
+
+Poly1305
+----------------------
+
+The Poly1305 implementation does not have any secret lookups or conditionals.
+The code is based on the public domain version by Andrew Moon.
+
+DES/3DES
+----------------------
+
+The DES implementation uses table lookups, and is likely vulnerable to side
+channel attacks. DES or 3DES should be avoided in new systems. The proper fix
+would be a scalar bitsliced implementation, this is not seen as worth the
+engineering investment given these algorithms end of life status.
+
+Twofish
+------------------------
+
+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.
+
+ChaCha20, Serpent, Threefish, ...
+-----------------------------------
+
+Some algorithms including ChaCha, Salsa, Serpent and Threefish are 'naturally'
+silent to cache and timing side channels on all recent processors.
+
+IDEA
+---------------
+
+IDEA encryption, decryption, and key schedule are implemented to take constant
+time regardless of their inputs.
+
+Hash Functions
+-------------------------
+
+Most hash functions included in Botan such as MD5, SHA-1, SHA-2, SHA-3, Skein,
+and BLAKE2 do not require any input-dependent memory lookups, and so seem to not be
+affected by common CPU side channels. However the implementations of Whirlpool
+and Streebog use table lookups and probably can be attacked by side channels.
+
+Memory comparisons
+----------------------
+
+The function same_mem in header mem_ops.h provides a constant-time comparison
+function. It is used when comparing MACs or other secret values. It is also
+exposed for application use.
+
+Memory zeroizing
+----------------------
+
+There is no way in portable C/C++ to zero out an array before freeing it, in
+such a way that it is guaranteed that the compiler will not elide the
+'additional' (seemingly unnecessary) writes to zero out the memory.
+
+The function secure_scrub_memory (in mem_ops.cpp) uses some system specific
+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
+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.
+
+Memory allocation
+----------------------
+
+Botan's secure_vector type is a std::vector with a custom allocator. The
+allocator calls secure_scrub_memory before freeing memory.
+
+Some operating systems support an API call to lock a range of pages
+into memory, such that they will never be swapped out (``mlock`` on POSIX,
+``VirtualLock`` on Windows). On many POSIX systems ``mlock`` is only usable by
+root, but on Linux, FreeBSD and possibly other systems a small amount
+of memory can be locked by processes without extra credentials.
+
+If available, Botan uses such a region for storing key material. A page-aligned
+block of memory is allocated and locked, then the memory is scrubbed before
+freeing. This memory pool is used by secure_vector when available. It can be
+disabled at runtime setting the environment variable BOTAN_MLOCK_POOL_SIZE to 0.
+
+Automated Analysis
+---------------------
+
+Currently the main tool used by the Botan developers for testing for side
+channels at runtime is valgrind; valgrind's runtime API is used to taint memory
+values, and any jumps or indexes using data derived from these values will cause
+a valgrind warning. This technique was first used by Adam Langley in ctgrind.
+See header ct_utils.h.
+
+To check, install valgrind, configure the build with --with-valgrind, and run
+the tests.
+
+.. highlight:: shell
+
+There is also a test utility built into the command line util, `timing_test`,
+which runs an operation on several different inputs many times in order to
+detect simple timing differences. The output can be processed using the
+Mona timing report library (https://github.com/seecurity/mona-timing-report).
+To run a timing report (here for example pow_mod)::
+
+ $ ./botan timing_test pow_mod > pow_mod.raw
+
+This must be run from a checkout of the source, or otherwise ``--test-data-dir=``
+must be used to point to the expected input files.
+
+Build and run the Mona report as::
+
+ $ git clone https://github.com/seecurity/mona-timing-report.git
+ $ cd mona-timing-report
+ $ ant
+ $ java -jar ReportingTool.jar --lowerBound=0.4 --upperBound=0.5 --inputFile=pow_mod.raw --name=PowMod
+
+This will produce plots and an HTML file in subdirectory starting with
+``reports_`` followed by a representation of the current date and time.
+
+References
+---------------
+
+[Aes256Sc] Neve, Tiri "On the complexity of side-channel attacks on AES-256"
+(https://eprint.iacr.org/2007/318.pdf)
+
+[AesCacheColl] Bonneau, Mironov "Cache-Collision Timing Attacks Against AES"
+(http://www.jbonneau.com/doc/BM06-CHES-aes_cache_timing.pdf)
+
+[CoronDpa] Coron,
+"Resistance against Differential Power Analysis for Elliptic Curve Cryptosystems"
+(https://citeseer.ist.psu.edu/viewdoc/summary?doi=10.1.1.1.5695)
+
+[InvalidCurve] Biehl, Meyer, Müller: Differential fault attacks on
+elliptic curve cryptosystems
+(https://www.iacr.org/archive/crypto2000/18800131/18800131.pdf)
+
+[InvalidCurveTLS] Jager, Schwenk, Somorovsky: Practical Invalid Curve
+Attacks on TLS-ECDH
+(https://www.nds.rub.de/research/publications/ESORICS15/)
+
+[SafeCurves] Bernstein, Lange: SafeCurves: choosing safe curves for
+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)
+
+[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)
+
+[MillionMsgTiming] Meyer, Somorovsky, Weiss, Schwenk, Schinzel, Tews: Revisiting
+SSL/TLS Implementations: New Bleichenbacher Side Channels and Attacks
+(https://www.nds.rub.de/research/publications/mswsst2014-bleichenbacher-usenix14/)
+
+[OaepTiming] Manger, "A Chosen Ciphertext Attack on RSA Optimal Asymmetric
+Encryption Padding (OAEP) as Standardized in PKCS #1 v2.0"
+(http://archiv.infsec.ethz.ch/education/fs08/secsem/Manger01.pdf)
+
+[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)
+
+[RandomMonty] Le, Tan, Tunstall "Randomizing the Montgomery Powering Ladder"
+(https://eprint.iacr.org/2015/657)
+
+[VectorAes] Hamburg, "Accelerating AES with Vector Permute Instructions"
+https://shiftleft.org/papers/vector_aes/vector_aes.pdf
+
+[VersionOracle] Klíma, Pokorný, Rosa "Attacking RSA-based Sessions in SSL/TLS"
+(https://eprint.iacr.org/2003/052)
diff --git a/comm/third_party/botan/doc/support.rst b/comm/third_party/botan/doc/support.rst
new file mode 100644
index 0000000000..7f0ffc2a05
--- /dev/null
+++ b/comm/third_party/botan/doc/support.rst
@@ -0,0 +1,70 @@
+Support Information
+=======================
+
+Supported Platforms
+------------------------
+
+For Botan 2, the tier-1 supported platforms are
+
+* Linux x86-64, GCC 4.8 or higher
+* Linux x86-64, Clang 3.5 or higher
+* Linux aarch64, GCC 4.8+
+* Linux ppc64le, GCC 4.8+
+* Windows x86-64, Visual C++ 2015 and 2017
+
+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 2, the tier-2 supported platforms are
+
+* Linux x86-32, GCC 4.8+
+* Linux arm32, GCC 4.8+
+* Windows x86-64, MinGW GCC
+* macOS x86-64, XCode Clang
+* iOS aarch64, XCode Clang
+* Android aarch64, NDK Clang
+* FreeBSD x86-64, Clang 3.8+
+
+Some (but not all) of the tier-2 platforms are tested by CI. Everything should
+work, and if problems are encountered, the developers will probably be able to
+help. But they are not as carefully tested as tier-1.
+
+Of course most other modern OSes such as QNX, AIX, OpenBSD, NetBSD, and Solaris
+also work just fine. Some are tested occasionally, usually just before a new
+release. But very little code specific to these platforms is written by the
+primary developers. For example, any functionality in the library which
+utilizes OpenBSD specific APIs was likely contributed by someone interested in
+that platform.
+
+In theory any working C++11 compiler is fine but in practice, we only regularly
+test with GCC, Clang, and Visual C++. Recent versions of IBM XLC can compile
+the library but occasionally codegen bugs occur. Several other compilers (such
+as Intel and PGI) 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
+September 2020. Any branch not listed here (including 1.11) is no
+longer supported. Dates in the future are approximate.
+
+============== ============== ========================== ============
+Branch First Release End of Active Development End of Life
+============== ============== ========================== ============
+1.8 2008-12-08 2010-08-31 2016-02-13
+1.10 2011-06-20 2012-07-10 2018-12-31
+2.x 2017-01-06 2020-10-05 2024-01-01 or later
+3.x 2021? ? ?
+============== ============== ========================== ============
+
+"Active development" refers to adding new features and optimizations. At the
+conclusion of the active development phase, only bugfixes are applied.
+
+Getting Help
+------------------
+
+To get help with Botan, open an issue on
+`GitHub <https://github.com/randombit/botan/issues>`_
diff --git a/comm/third_party/botan/license.txt b/comm/third_party/botan/license.txt
new file mode 100644
index 0000000000..f586fb88f0
--- /dev/null
+++ b/comm/third_party/botan/license.txt
@@ -0,0 +1,24 @@
+Copyright (C) 1999-2020 The Botan Authors
+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.
diff --git a/comm/third_party/botan/moz.build b/comm/third_party/botan/moz.build
new file mode 100644
index 0000000000..93692a6b2b
--- /dev/null
+++ b/comm/third_party/botan/moz.build
@@ -0,0 +1,14 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+Library("botan")
+FINAL_LIBRARY = "rnp"
+
+# Honor --with-system-botan
+if CONFIG["MZLA_SYSTEM_BOTAN"]:
+ OS_LIBS += CONFIG["MZLA_BOTAN_LIBS"]
+else:
+ include("./botan.mozbuild")
diff --git a/comm/third_party/botan/news.rst b/comm/third_party/botan/news.rst
new file mode 100644
index 0000000000..7da4db1890
--- /dev/null
+++ b/comm/third_party/botan/news.rst
@@ -0,0 +1,1879 @@
+Release Notes
+========================================
+
+Version 2.18.2, 2021-10-25
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Avoid using short exponents when encrypting in ElGamal, as some PGP
+ implementations generate keys with parameters that are weak when
+ short exponents are used (GH #2794)
+
+* Fix a low risk OAEP decryption side channel (GH #2797)
+
+* Work around a miscompilation of SHA-3 caused by a bug in Clang 12
+ and XCode 13. (GH #2826)
+
+* Remove support in OpenSSL provider for algorithms which are
+ disabled by default in OpenSSL 3.0 (GH #2823, #2814)
+
+* Add CI based on GitHub actions to replace Travis CI (GH #2632)
+
+* Fix the online OCSP test, as the certificate involved had expired.
+ (GH #2799)
+
+* Fix some test failures induced by the expiration of the trust root
+ "DST Root CA X3" (GH #2820)
+
+Version 2.18.1, 2021-05-09
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Fix a build regression in 2.18.0 which caused linker flags which
+ contain ``-l`` within them (such as ``-fuse-linker-plugin``) to
+ be misinterpreted. (GH #2715)
+
+* Fix a bug which caused decoding a certificate which contained
+ more than one name in a single RDN. (GH #2611 #2630 #2724)
+
+* Fix a bug which caused OID lookup failures when run in a locale
+ which uses thousands separators (pt_BR was reported as having
+ this issue). (GH #2732 #2730 #2237)
+
+* DNS names in name constraints were compared with case sensitivity, which
+ could cause valid certificates to be rejected. (GH #2739 #2735)
+
+* X.509 name constraint extensions were rejected if non-critical. RFC 5280
+ requires conforming CAs issue such extensions as critical, but not all
+ certificates are compliant, and all other known implementations do not
+ require this. (GH #2739 #2736)
+
+* X.509 name constraints were incorrectly applied to the certificate which
+ included the constraint. (GH #2739 #2737)
+
+Version 2.18.0, 2021-04-15
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Add support for implementing custom RNG objects through the
+ FFI interface (GH #2627 #2600)
+
+* Improve safegcd bounds, improving runtime performance (GH #2628 #2619)
+
+* Fix a bug introduced in 2.9.0 where BigInt::operator< would return
+ an incorrect result if both operands were negative. (GH #2641 #2638)
+
+* Reject non-TLS messages as quickly as possible without waiting for
+ a full record. (GH #2676)
+
+* Add build support for RISC-V 32
+
+* Fixes for TLS::Stream::async_shutdown (GH #2673)
+
+* Fix a regression introduced in 2.17.0 where LDFLAGS which add an extra
+ library (such as ``-latomic`` needed on SPARC) were not always applied
+ effectively. (GH #2622 #2623 #2625)
+
+Version 2.17.3, 2020-12-21
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* CVE-2021-24115 Change base64, base58, base32, and hex encoding and
+ decoding operations to run in constant time (GH #2549)
+
+* Fix a build problem on PPC64 building with Clang (GH #2547)
+
+* Fix an install problem introduced in 2.17.2 affecting MSVC 2015
+
+* Fix use of -L flag in linking when configured using ``--with-external-libdir``
+ (GH #2496)
+
+* Fix a build problem on big-endian PowerPC related to VSX instructions
+ in the AES code. (GH #2515)
+
+Version 2.17.2, 2020-11-13
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Fix an build problem on ppc64 introduced with certain recent
+ versions of GCC or binutils where using the DARN instruction
+ requires using an appropriate -mcpu flag to enable the instruction
+ in the assembler. (GH #2481 2463)
+
+* Resolve an issue in the modular square root algorithm where a loop
+ to find a quadratic non-residue could, for a carefully chosen
+ composite modulus, not terminate in a timely manner. (GH #2482 #2476)
+
+* Fix a regression in MinGW builds introduced in 2.17.1
+
+Version 2.17.1, 2020-11-07
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Fix a build problem that could occur if Python was not in the PATH.
+ This was known to occur on some installations of macOS.
+
+* Re-enable support for the x86 CLMUL instruction on Visual C++, which was
+ accidentally disabled starting in 2.12.0. (GH #2460)
+
+Version 2.17.0, 2020-11-05
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Fix a bug in ECDSA which could occur when the group size and hash length
+ differ. In this case, on occasion the generated signature would not be
+ accepted by other ECDSA implementations. This was particularly likely to
+ affect users of 160-bit or 239-bit curves. (GH #2433 #2415)
+
+* Fix a bug in ECDSA verification when the public key was chosen to be
+ a small multiple of the group generator. In that case, verification
+ would fail even if the signature was actually valid. (GH #2425)
+
+* SIV's functionality of supporting multiple associated data inputs has been
+ generalized onto the AEAD_Mode interface. However at the moment SIV is the
+ only AEAD implemented which supports more than one AD. (GH #2440)
+
+* The contents of ASN.1 headers ``asn1_str.h``, ``asn1_time.h``, ``asn1_oid.h``
+ and ``alg_id.h`` have been moved to ``asn1_obj.h``. The header files remain
+ but simply forward the include to ``asn1_obj.h``. These now-empty header files
+ are deprecated, and will be removed in a future major release. (GH #2441)
+
+* The contents of X.509/PKIX headers ``asn1_attribute.h`` ``asn1_alt_name.h``
+ ``name_constraint.h`` ``x509_dn.h`` ``cert_status.h`` and ``key_constraint.h``
+ have been merged into ``pkix_enums.h`` (for enumerations) and ``pkix_types.h``
+ (for all other definitions). The previous header files remain but simply
+ forward the include to the new header containing the definition. These
+ now-empty header files are deprecated, and will be removed in a future major
+ release. (GH #2441)
+
+* A number of other headers including those related to HOTP/TOTP, XMSS,
+ PKCS11, PSK_DB have also been merged. Any now deprecated/empty headers
+ simply include the new header and issue a deprecation warning.
+ (GH #2443 #2446 #2447 2448 #2449)
+
+* Small optimizations in the non-hardware assisted AES key generation
+ code path (GH #2417 #2418)
+
+* Move the GHASH code to a new module in utils, making it possible
+ to build GMAC support without requiring GCM (GH #2416)
+
+* Add more detection logic for AVX-512 features (GH #2430)
+
+* Avoid std::is_pod which is deprecated in C++20 (GH #2429)
+
+* Fix a bug parsing deeply nested cipher names (GH #2426)
+
+* Add support for ``aarch64_be`` target CPU (GH #2422)
+
+* Fix order of linker flags so they are always applied effectively (GH #2420)
+
+* Prevent requesting DER encoding of signatures when the algorithm
+ did not support it (GH #2419)
+
+Version 2.16.0, 2020-10-06
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Now userspace PRNG objects (such as AutoSeeded_RNG and HMAC_DRBG)
+ use an internal lock, which allows safe concurrent use. This however
+ is purely a precaution in case of accidental sharing of such RNG
+ objects; for performance reasons it is always preferable to use
+ a RNG per thread if a userspace RNG is needed. (GH #2399)
+
+* DL_Group and EC_Group objects now track if they were created from a
+ known trusted group (such as P-256 or an IPsec DH parameter). If
+ so, then verification tests can be relaxed, as compared to
+ parameters which may have been maliciously constructed in order to
+ pass primality checks. (GH #2409)
+
+* RandomNumberGenerator::add_entropy_T assumed its input was a POD
+ type but did not verify this. (GH #2403)
+
+* Support OCSP responders that live on a non-standard port (GH #2401)
+
+* Add support for Solaris sandbox (GH #2385)
+
+* Support suffixes on release numbers for alpha/beta releases (GH #2404)
+
+* Fix a bug in EAX which allowed requesting a 0 length tag, which had
+ the effect of using a full length tag. Instead omit the length field,
+ or request the full tag length explicitly. (GH #2392 #2390)
+
+* Fix a memory leak in GCM where if passed an unsuitable block cipher
+ (eg not 128 bit) it would throw an exception and leak the cipher
+ object. (GH #2392 #2388)
+
+Version 2.15.0, 2020-07-07
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Fix a bug where the name constraint extension did not constrain the
+ alternative DN field which can be included in a subject alternative name. This
+ would allow a corrupted sub-CA which was otherwise constrained by a name
+ constraint to issue a certificate with a prohibited DN.
+
+* Fix a bug in the TLS server during client authentication where where
+ if a (disabled by default) static RSA ciphersuite was selected, then
+ no certificate request would be sent. This would have an equivalent
+ effect to a client which simply replied with an empty Certificate
+ message. (GH #2367)
+
+* Replace the T-Tables implementation of AES with a 32-bit bitsliced
+ version. As a result AES is now constant time on all processors.
+ (GH #2346 #2348 #2353 #2329 #2355)
+
+* In TLS, enforce that the key usage given in the server certificate
+ allows the operation being performed in the ciphersuite. (GH #2367)
+
+* In X.509 certificates, verify that the algorithm parameters are
+ the expected NULL or empty. (GH #2367)
+
+* Change the HMAC key schedule to attempt to reduce the information
+ leaked from the key schedule with regards to the length of the key,
+ as this is at times (as for example in PBKDF2) sensitive information.
+ (GH #2362)
+
+* Add Processor_RNG which wraps RDRAND or the POWER DARN RNG
+ instructions. The previous RDRAND_RNG interface is deprecated.
+ (GH #2352)
+
+* The documentation claimed that mlocked pages were created with a
+ guard page both before and after. However only a trailing guard page
+ was used. Add a leading guard page. (GH #2334)
+
+* Add support for generating and verifying DER-encoded ECDSA signatures
+ in the C and Python interfaces. (GH #2357 #2356)
+
+* Workaround a bug in GCC's UbSan which triggered on a code sequence
+ in XMSS (GH #2322)
+
+* When building documentation using Sphinx avoid parallel builds with
+ version 3.0 due to a bug in that version (GH #2326 #2324)
+
+* Fix a memory leak in the CommonCrypto block cipher calls (GH #2371)
+
+* Fix a flaky test that would occasionally fail when running the tests
+ with a large number of threads. (GH #2325 #2197)
+
+* Additional algorithms are now deprecated: XTEA, GOST, and Tiger.
+ They will be removed in a future major release.
+
+Version 2.14.0, 2020-04-06
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Add support for using POWER8+ VPSUMD instruction to accelerate GCM
+ (GH #2247)
+
+* Optimize the vector permute AES implementation, especially improving
+ performance on ARMv7, Aarch64, and POWER. (GH #2243)
+
+* Use a new algorithm for modular inversions which is both faster and
+ more resistant to side channel attacks. (GH #2287 #2296 #2301)
+
+* Address an issue in CBC padding which would leak the length of the
+ plaintext which was being padded. Unpadding during decryption was
+ not affected. Thanks to Maximilian Blochberger for reporting this.
+ (GH #2312)
+
+* Optimize NIST prime field reductions, improving ECDSA by 3-9% (GH #2295)
+
+* Increase the size of the ECC blinding mask and scale it based on the
+ size of the group order. (GH #880 #893 #2308)
+
+* Add server side support for the TLS asio wrapper. (GH #2229)
+
+* Add support for using Windows certificate store on MinGW (GH #2280)
+
+* Use the library thread pool instead of a new thread for RSA computations,
+ improving signature performance by up to 20%. (GH #2257)
+
+* Precompute and cache additional fields in ``X509_Certificate`` (GH #2250)
+
+* Add a CLI utility ``cpu_clock`` which estimates the speed of the
+ processor cycle counter. (GH #2251)
+
+* Fix a bug which prevented using DER-encoded ECDSA signatures with a PKCS11
+ key (GH #2293)
+
+* Enable use of raw block ciphers from CommonCrypto (GH #2278)
+
+* Support for splitting up the amalgamation file by ABI extension has
+ been removed. Instead only ``botan_all.cpp`` and ``botan_all.h`` are
+ generated. (GH #2246)
+
+* Improve support for baremetal systems with no underlying OS, with
+ target OS ``none`` (GH #2303 #2304 #2305)
+
+* The build system now avoids using ``-rpath=$ORIGIN`` or (on macOS)
+ install_name which allowed running the tests from the build
+ directory without setting ``LD_LIBRARY_PATH``/``DYLD_LIBRARY_PATH``
+ environment variables. Instead set the dynamic linker variables
+ appropriately, or use ``make check``. (GH #2294 #2302)
+
+* Add new option ``--name-amalgamation`` which allows naming the
+ amalgamation output, instead of the default ``botan_all``. (GH #2246)
+
+* Avoid using symbolic links on Windows (GH #2288 #2286 #2285)
+
+* Fix a bug that prevented compilation of the amalgamation on ARM and
+ POWER processors (GH #2245 #2241)
+
+* Fix some build problems under Intel C++ (GH #2260)
+
+* Remove use of Toolhelp Windows library, which was known to trigger
+ false positives under some antivirus systems. (GH #2261)
+
+* Fix a compilation problem when building on Windows in Unicode mode.
+ Add Unicode build to CI to prevent regressions. (GH #2254 #2256)
+
+* Work around a GCC bug affecting old libc (GH #2235)
+
+* Workaround a bug in macOS 10.15 which caused a test to crash.
+ (GH #2279 #2268)
+
+* Avoid a crash in PKCS8::load_key due to a bug in Clang 8.
+ (GH #2277)
+
+Version 2.13.0, 2020-01-06
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Add Roughtime client (GH #2143 #1842)
+
+* Add support for XMSS X.509 certificates (GH #2172)
+
+* Add support for X.509 CRLs in FFI layer and Python wrapper (GH #2213)
+
+* It is now possible to disable TLS v1.0/v1.1 and DTLS v1.0 at build time.
+ (GH #2188)
+
+* The format of encrypted TLS sessions has changed, which will invalidate all
+ existing session tickets. The new format will make it easier to support ticket
+ key rotation in the future. (GH #2225)
+
+* Improve RSA key generation performance (GH #2148)
+
+* Make gcd computation constant-time (GH #2147)
+
+* Add AVX2 implementation of SHACAL2 (GH #2196)
+
+* Update BSI policy to reflect 2019 update of TR 02102-2 (GH #2195)
+
+* Support more functionality for X.509 in the Python API (GH #2165)
+
+* Add ``generic`` CPU target useful when building for some new or unusual
+ platform.
+
+* Disable MD5 in BSI or NIST modes (GH #2188)
+
+* Disable stack protector on MinGW as it causes crashes with some recent
+ versions. (GH #2187)
+
+* On Windows the DLL is now installed into the binary directory (GH #2233)
+
+* Previously Windows required an explicit ``.lib`` suffix be added when
+ providing an explicit library name, as is used for example for Boost.
+ Now the ``.lib`` suffix is implicit, and should be omitted.
+
+* Remove the 32-bit x86 inline asm for Visual C++ as it seemed to not offer
+ much in the way of improved performance. (GH #2204 #256)
+
+* Resolve all compile time warnings generated by GCC, Clang and MSVC.
+ Modify CI to compile with warnings-as-errors. (GH #2170 #2206 #2211 #2212)
+
+* Fix bugs linking to 3rd party libraries on Windows due to invalid
+ link specifiers. (GH #2210 #2215)
+
+* Add long input and NIST Monte-Carlo hash function tests.
+
+* Fix a bug introduced in 2.12.0 where ``TLS::Channel::is_active`` and
+ ``TLS::Channel::is_closed`` could simultaneously return true.
+ (GH #2174 #2171)
+
+* Use ``std::shared_ptr`` instead of ``boost::shared_ptr`` in some examples.
+ (GH #2155)
+
+Version 2.12.1, 2019-10-14
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Fix a bug that prevented building with nmake (GH #2142 #2141)
+
+* Fix an issue where make install would attempt to build targets which
+ were disabled. (GH #2140)
+
+* If the option ``--without-documentation`` is used, avoid invoking the
+ documentation build script. (GH #2138)
+
+* Fix a bug that prevented compilation on x86-32 using GCC 4.9 (GH #2139)
+
+* Fix a bug in CCM encryption, where it was possible to call ``finish`` without
+ ever setting a nonce (GH #2151 #2150)
+
+* Improve ECIES/DLIES interfaces. If no initialization vector was set, they
+ would typically produce hard to understand exceptions. (GH #2151 #2150)
+
+Version 2.12.0, 2019-10-07
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Many currently public headers are being deprecated. If any such header is
+ included by an application, a warning is issued at compile time. Headers
+ issuing this warning will be made internal in a future major release.
+ (GH #2061)
+
+* RSA signature performance improvements (GH #2068 #2070)
+
+* Performance improvements for GCM (GH #2024 #2099 #2119), OCB (#2122),
+ XTS (#2123) and ChaCha20Poly1305 (GH #2117), especially for small messages.
+
+* Add support for constant time AES using NEON and AltiVec (GH #2093 #2095 #2100)
+
+* Improve performance of POWER8 AES instructions (GH #2096)
+
+* Add support for the POWER9 hardware random number generator (GH #2026)
+
+* Add support for 64-bit version of RDRAND, doubling performance on x86-64 (GH #934 #2022)
+
+* In DTLS server, support a client crashing and then reconnecting from
+ the same source port, as described in RFC 6347 sec 4.2.8 (GH #2029)
+
+* Optimize DTLS MTU splitting to split precisely to the set MTU (GH #2042)
+
+* Add support for the TLS v1.3 downgrade indicator. (GH #2027)
+
+* Improve the error messages generated when an invalid TLS state transition occurs
+ (GH #2030)
+
+* Fix some edge cases around TLS close_notify support. (GH #2054)
+
+* Modifications to support GOST 34.10-2012 signatures (GH #2055 #2056 #1860 #1897)
+
+* Add some new APIs on ``OID`` objects (GH #2057)
+
+* Properly decode OCSP responses which indicate an error (GH #2110)
+
+* Add a function to remove an X.509 extension from an Extensions object.
+ (GH #2101 #2073 #2065)
+
+* Support Argon2 outputs longer than 64 bytes (GH #2079 #2078)
+
+* Correct a bug in CAST-128 which caused incorrect computation using
+ 11, 13, 14, or 15 byte keys. (GH #2081)
+
+* Fix a bug which would cause Streebog to produce incorrect outputs for
+ certain messages (GH #2082 #2083)
+
+* Fix a bug that prevented loading EC points with an affine x or y
+ value of 0. For certain curves such points can exist. (GH #2102)
+
+* Fix a bug which would cause PBKDF2 to go into a very long loop if
+ it was requested to use an iteration count of 0. (GH #2090 #2088)
+
+* The BearSSL provider has been removed (GH #2020)
+
+* Add a new ``entropy`` cli which allows sampling the output of the entropy sources.
+
+* Add new ``base32_enc`` and ``base32_dec`` cli for base32 encoding operations. (GH #2111)
+
+* Support setting TLS policies in CLIs like ``tls_client`` and ``tls_proxy_server`` (GH #2047)
+
+* The tests now run in multithreaded mode by default. Provide option ``--test-threads=1`` to
+ return to previous single-threaded behavior. (GH #2071 #2075)
+
+* Cleanups in TLS record layer (GH #2021)
+
+* Fix typos in some OCSP enums which used "OSCP" instead. (GH #2048)
+
+* In the Python module, avoid trying to load DLLs for names that don't match the current
+ platform (GH #2062 #2059)
+
+* In the Python module, also look for ``botan.dll`` so Python wrapper can run on Windows.
+ (GH #2059 #2060)
+
+* Add support for TOTP algorithm to the Python module. (GH #2112)
+
+* Now the minimum Windows target is set to Windows 7 (GH #2036 #2028)
+
+* Add ``BOTAN_FORCE_INLINE`` macro to resolve a performance issue with BLAKE2b on MSVC
+ (GH #2092 #2089)
+
+* Avoid using ``__GNUG__`` in headers that may be consumed by a C compiler (GH #2013)
+
+* Improve the PKCS11 tests (GH #2115)
+
+* Fix a warning from Klocwork (GH #2128 #2129)
+
+* Fix a bug which caused amalgamation builds to fail on iOS (GH #2045)
+
+* Support disabling thread local storage, needed for building on old iOS (GH #2045)
+
+* Add a script to help with building for Android, using Docker (GH #2016 #2033 #513)
+
+* Add Android NDK build to Travis CI (GH #2017)
+
+Version 2.11.0, 2019-07-01
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Add Argon2 PBKDF and password hash (GH #459 #1981 #1987)
+
+* Add Bcrypt-PBKDF (GH #1990)
+
+* Add a libsodium compat layer in sodium.h (GH #1996)
+
+* XMSS now follows RFC 8391 which is incompatible with previous versions, which
+ had followed draft 6. (GH #1858 #2003)
+
+* Add server side support for issuing DTLS HelloVerifyRequest messages
+ (GH #1999)
+
+* Add a shim allowing testing Botan against the BoringSSL test suite,
+ and fix a number of bugs in TLS found using it.
+ (GH #1954 #1955 #1956 #1959 #1966 #1970)
+
+* Add support for the TLS v1.3 supported_versions extension. (GH #1976)
+
+* Add Ed25519ph compatible with RFC 8032 (GH #1699 #2000)
+
+* Add support for OCSP stapling on server side. (GH #1703 #1967)
+
+* Add a ``boost::asio`` TLS stream compatible with ``boost::asio::ssl``.
+ (GH #1839 #1927 #1992)
+
+* Add a certificate store for Linux/Unix systems. (GH #1885 #1936)
+
+* Add a certificate store for Windows systems. (GH #1931)
+
+* Add a generic ``System_Certificate_Store`` which wraps Windows, macOS,
+ and Linux certificate stores. (GH #1893)
+
+* Fix verification rooted in a v1 certificate which previously would fail.
+ (GH #1890)
+
+* Add ability to specify the maximum age of an OCSP response which does not
+ have the nextUpdate field set. (GH #1974 #1995)
+
+* Fix X509_DN::operator< which could erroneously return true in both
+ directions (ie, DN1 < DN2 && DN2 < DN1). This would break STL
+ containers using a DN as the key. (GH #1938)
+
+* It is now possible to create intermediate CA certificates using the
+ command line interface. (GH #1879 #1889)
+
+* Add a new build time option to set where the system stores trusted
+ certificates. (GH #1888)
+
+* New ``trust_roots`` CLI that examines the system certificate store.
+ (GH #1893)
+
+* Fix bugs and add many new features in the Python wrapper.
+ (GH #1899 #1900 #1901 #1902 #1903 #1904 #1906 #1907 #1915)
+
+* Various FFI interfaces which are redundant with other APIs are now
+ deprecated. The deprecation message suggests the alternate API to use.
+ (GH #1915)
+
+* Fix decoding of RSA-OAEP certificates. (GH #1943 #1944)
+
+* Allow setting multiple organization unit fields in a certificate or
+ certificate request. (GH #1939)
+
+* Increase the maximum allowed year in ASN1_Time to 3100. This works
+ around a problem parsing certs in AppVeyor's trust store.
+
+* Add ``--format`` option to ``rng`` CLI command allowing to format
+ as base64, base58 or binary in addition to hex. (GH #1945)
+
+* Remove use of table lookups for IP/FP transforms in DES (GH #1928)
+
+* Improve the tests for SRP6 (GH #1917 #1923)
+
+* Document the build system
+
+* When available use POSIX ``sysconf`` to detect the number of CPUs (GH #1877)
+
+* Add functionality to handle Boost naming conventions on different platforms,
+ especially affecting Windows. Enable Boost in AppVeyor builds. (GH #1964)
+
+* Add alternate implementation of ``getauxval`` for older Android (GH #1962)
+
+* Add ``configure.py`` option allowing to set arbitrary macros during build.
+ (GH #1960)
+
+* Use FreeBSD's ``elf_aux_info`` to detect ARM and POWER CPU features
+ (GH #1895)
+
+* Use FreeBSD's ``PROT_MAX`` to prevent mmap regions from being made executable
+ later. (GH #2001)
+
+* Fix a memory leak in the tests (GH #1886)
+
+* Fix an issue building with the new Boost 1.70 (GH #1881 #1880)
+
+* Fix an issue with UbSan in the tests (GH #1892)
+
+* Remove use of ``-mabi`` flag when building on MIPS64 (GH #1918)
+
+* Make it possible to specify additional libraries in ``LDFLAGS`` (GH #1916)
+
+* Fix some warnings from Clang 8 (GH #1941)
+
+* Fix the makefile .PHONY syntax (GH #1874)
+
+* Fix build issue with SoftHSM 2.5.0 (GH #1986)
+
+Version 2.10.0, 2019-03-30
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Notice: the developers plan to switch from gzip to xz compression for
+ releases starting in 2.11. If this is a problem please comment at
+ https://github.com/randombit/botan/issues/1872
+
+* Warning: XMSS currently implements draft-06 which is not compatible with the
+ final RFC 8391 specification. A PR is open to fix this, however it will break
+ all current uses of XMSS. If you are currently using XMSS please comment at
+ https://github.com/randombit/botan/pull/1858. Otherwise the PR will be merged
+ and support for draft-06 will be removed starting in 2.11.
+
+* Added a new certificate store implementation that can access the
+ MacOS keychain certificate store. (GH #1830)
+
+* Redesigned ``Memory_Pool`` class, which services allocations out of a
+ set of pages locked into memory (using ``mlock``/``VirtualLock``). It is now
+ faster and with improved exploit mitigations. (GH #1800)
+
+* Add BMI2 implementations of SHA-512 and SHA-3 which improve performance by
+ 25-35% on common CPUs. (GH #1815)
+
+* Unroll SHA-3 computation improving performance by 10-12% (GH #1838)
+
+* Add a ``Thread_Pool`` class. It is now possible to run the tests in multiple
+ threads with ``--test-threads=N`` flag to select the number of threads to use.
+ Use ``--test-threads=0`` to run with as many CPU cores as are available on the
+ current system. The default remains single threaded. (GH #1819)
+
+* XMSS signatures now uses a global thread pool instead of spawning new threads
+ for each usage. This improves signature generation performance by between 10%
+ and 60% depending on architecture and core count. (GH #1864)
+
+* Some functions related to encoding and decoding BigInts have been deprecated.
+ (GH #1817)
+
+* Binary encoding and decoding of BigInts has been optimized by performing
+ word-size operations when possible. (GH #1817)
+
+* Rename the exception ``Integrity_Failure`` to ``Invalid_Authentication_Tag`` to make
+ its meaning and usage more clear. The old name remains as a typedef. (GH #1816)
+
+* Support for using Boost ``filesystem`` and MSVC's ``std::filesystem`` have been
+ removed, since already POSIX and Win32 versions had to be maintained for
+ portability. (GH #1814)
+
+* Newly generated McEliece and XMSS keys now default to being encrypted using
+ SIV mode, support for which was added in 2.8.0. Previously GCM was used by
+ default for these algorithms.
+
+* Use ``arc4random`` on Android systems (GH #1851)
+
+* Fix the encoding of PGP-S2K iteration counts (GH #1853 #1854)
+
+* Add a facility for sandboxing the command line util. Currently FreeBSD
+ (Capsicum) and OpenBSD (``pledge``) sandboxes are supported. (GH #1808)
+
+* Use ``if constexpr`` when available.
+
+* Disable building shared libs on iOS as it was broken and it is not clear shared
+ libraries are ever useful on iOS (GH #1865)
+
+* Renamed the ``darwin`` build target to ``macos``. This should not cause any
+ user-visible change. (GH #1866)
+
+* Add support for using ``sccache`` to cache the Windows CI build (GH #1807)
+
+* Add ``--extra-cxxflags`` option which allows adding compilation flags without
+ overriding the default set. (GH #1826)
+
+* Add ``--format=`` option to the ``hash`` cli which allows formatting the output
+ as base64 or base58, default output remains hex.
+
+* Add ``base58_enc`` and ``base58_dec`` cli utils for base58 encoding/decoding.
+ (GH #1848)
+
+* Enable ``getentropy`` by default on macOS (GH #1862)
+
+* Avoid using ``-momit-leaf-frame-pointer`` flags, since ``-fomit-frame-pointer``
+ is already the default with recent versions of GCC.
+
+* Fix XLC sanitizer flags.
+
+* Rename ``Blake2b`` class to ``BLAKE2b`` to match the official name. There is
+ a typedef for compat.
+
+* Fix a bug where loading a raw ``Ed25519_PublicKey`` of incorrect length would
+ lead to a crash. (GH #1850)
+
+* Fix a bug that caused compilation problems using CryptoNG PRNG. (GH #1832)
+
+* Extended SHAKE-128 cipher to support any key between 1 and 160 bytes, instead
+ of only multiples of 8 bytes.
+
+* Minor HMAC optimizations.
+
+* Build fixes for GNU/Hurd.
+
+* Fix a bug that prevented generating or verifying Ed25519 signatures in the CLI
+ (GH #1828 #1829)
+
+* Fix a compilation error when building the amalgamation outside of the original
+ source directory when AVX2 was enabled. (GH #1812)
+
+* Fix a crash when creating the amalgamation if a header file was edited on
+ Windows but then the amalgamation was built on Linux (GH #1763)
+
+Version 2.9.0, 2019-01-04
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* CVE-2018-20187 Address a side channel during ECC key generation,
+ which used an unblinded Montgomery ladder. As a result, a timing
+ attack can reveal information about the high bits of the secret key.
+
+* Fix bugs in TLS which caused negotiation failures when the client
+ used an unknown signature algorithm or version (GH #1711 #1709 #1708)
+
+* Fix bug affecting GCM, EAX and ChaCha20Poly1305 where if the associated data
+ was set after starting a message, the new AD was not reflected in the produced
+ tag. Now with these modes setting an AD after beginning a message throws an
+ exception.
+
+* Use a smaller sieve which improves performance of prime generation.
+
+* Fixed a bug that caused ChaCha to produce incorrect output after encrypting
+ 256 GB. (GH #1728)
+
+* Add NEON and AltiVec implementations of ChaCha (GH #1719 #1728 #1729)
+
+* Optimize AVX2 ChaCha (GH #1730)
+
+* Many more operations in BigInt, ECC and RSA code paths are either fully const time
+ or avoid problematic branches that could potentially be exploited in a side
+ channel attack. (GH #1738 #1750 #1754 #1755 #1757 #1758 #1759 #1762 #1765
+ #1770 #1773 #1774 #1779 #1780 #1794 #1795 #1796 #1797)
+
+* Several optimizations for BigInt and ECC, improving ECDSA performance by as
+ much as 30%. (GH #1734 #1737 #1777 #1750 #1737 #1788)
+
+* Support recovering an ECDSA public key from a message/signature pair (GH #664 #1784)
+
+* Add base58 encoding/decoding functions (GH #1783)
+
+* In the command line interface, add support for reading passphrases from the
+ terminal with echo disabled (GH #1756)
+
+* Add ``CT::Mask`` type to simplify const-time programming (GH #1751)
+
+* Add new configure options ``--disable-bmi2``, ``--disable-rdrand``,
+ and ``--disable-rdseed`` to prevent use of those instruction sets.
+
+* Add ``error_type`` and ``error_code`` functions to Exception type (GH #1744)
+
+* Now on POSIX systems ``posix_memalign`` is used instead of ``mmap`` for
+ allocating the page-locked memory pool. This avoids issues with ``fork``.
+ (GH #602 #1798)
+
+* When available, use RDRAND to generate the additional data in
+ ``Stateful_RNG::randomize_with_ts_input``
+
+* Use vzeroall/vzeroupper intrinsics to avoid AVX2/SSE transition penalties.
+
+* Support for Visual C++ 2013 has been removed (GH #1557 #1697)
+
+* Resolve a memory leak when verifying ECDSA signatures with versions
+ of OpenSSL before 1.1.0 (GH #1698)
+
+* Resolve a memory leak using ECDH via OpenSSL (GH #1767)
+
+* Fix an error in XTS which prohibited encrypting values which were
+ exactly the same length as the underlying block size. Messages of
+ this size are allowed by the standard and other XTS implementations.
+ (GH #1706)
+
+* Resolve a bug in TSS which resulted in it using an incorrect length
+ field in the shares. Now the correct length is encoded, but either
+ correct or buggy lengths are accepted when decoding. (GH #1722)
+
+* Correct a bug when reducing a negative ``BigInt`` modulo a small power of 2.
+ (GH #1755)
+
+* Add CLI utils for threshold secret splitting. (GH #1722)
+
+* Fix a bug introduced in 2.8.0 that caused compilation failure if using
+ a single amalgamation file with AVX2 enabled. (GH #1700)
+
+* Add an explicit OS target for Emscripten and improve support for it.
+ (GH #1702)
+
+* Fix small issues when building for QNX
+
+* Switch the Travis CI build to using Ubuntu 16.04 (GH #1767)
+
+* Add options to ``configure.py`` to disable generation of ``pkg-config``
+ file, and (for systems where ``pkg-config`` support defaults to off,
+ like Windows), to enable generating it. (GH #1268)
+
+* Modify ``configure.py`` to accept empty lists or trailing/extra commas.
+ (GH #1705)
+
+Version 2.8.0, 2018-10-01
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Add support for using Apple CommonCrypto library for hashing (GH #1667),
+ cipher modes (GH #1674) and block ciphers (GH #1673).
+
+* Support for negotiating TLS versions 1.0 and 1.1 is disabled in the default
+ TLS policy. In addition, support for negotiating TLS ciphersuites using CBC or
+ CCM mode is disabled by default. Applications which need to interop with old
+ peers must enable these in their TLS policy object. (GH #1651)
+
+* During primality testing, use a Lucas test in addition to Miller-Rabin. It is
+ possible to construct a composite integer which passes n Miller-Rabin tests
+ with probability (1/4)^n. So for a incautious verifier using a small number
+ of tests (under 16 or so) it is possible if unlikely they would accept such a
+ composite as prime. Adding a Lucas test precludes such an attack. (GH #1636)
+
+* Add XChaCha and XChaCha20Poly1305 (GH #1640)
+
+* Add AVX2 implementations of ChaCha (GH #1662) and Serpent (GH #1660)
+
+* Add a new password hashing interface in pwdhash.h (GH #1670)
+
+* C binding improvements. Added functions to get name and supported
+ keylengths of cipher, hash and MAC objects, support for FE1 format
+ preserving encryption (GH #1625 #1646), functions to load and save
+ RSA keys in PKCS #1 format (GH #1621), HOTP and TOTP algorithms,
+ scrypt, certificate verification (GH #1647), functions to get the
+ output length of public key operations (GH #1642), and functions for
+ loading and serializing X25519 keys (GH #1681)
+
+* Support for building with BOTAN_MP_WORD_BITS set to 8 or 16 has been removed.
+
+* Previously SM2 had two distinct key types, one for signatures and another for
+ encryption. They have now been merged into a single key type since in practice
+ it seems the same key is at times used for both operations. (GH #1637)
+
+* The ``Cipher_Mode`` class now derives from ``SymmetricAlgorithm`` (GH #1639)
+
+* Add support for using the ARMv8 instructions for SM4 encryption (GH #1622)
+
+* The entropy source using ``SecRandomCopyBytes`` has been removed as it was
+ redundant with other entropy sources (GH #1668)
+
+* The Python module has much better error checking and reporting, and offers new
+ functionality such as scrypt, MPI and FPE. (GH #1643 #1646)
+
+* Fixed a bug that caused CCM to fail with an exception when used with L=8
+ (GH #1631 #1632)
+
+* The default bcrypt work factor has been increased from 10 to 12.
+
+* The default algorithm used in passhash9 has changed from SHA-256 to SHA-512,
+ and the default work factor increased from 10 to 15.
+
+* In ECC private keys, include the public key data for compatibility with
+ GnuTLS (GH #1634 #1635)
+
+* Add support for using Linux ``getrandom`` syscall to access the system PRNG.
+ This is disabled by default, use ``--with-os-feature=getrandom`` to enable.
+
+* It is now possible to encrypt private keys using SIV mode.
+
+* The FFI function botan_privkey_load now ignores its rng argument.
+
+* Resolve a problem when building under Visual C++ 15.8 (GH #1624)
+
+* Fix a bug in XSalsa20 (192-bit Salsa nonces) where if set_iv was called twice
+ without calling set_key, the resulting encryption was incorrect. (GH #1640)
+
+* Handle an error seen when verifying invalid ECDSA signatures using LibreSSL
+ on non x86-64 platforms (GH #1627 #1628)
+
+* Fix bugs in PKCS7 and X9.23 CBC padding schemes, which would ignore
+ the first byte in the event the padding took up the entire block. (GH #1690)
+
+* Correct bugs which would cause CFB, OCB, and GCM modes to crash when they
+ were used in an unkeyed state. (GH #1639)
+
+* Optimizations for SM4 and Poly1305
+
+* Avoid a cache side channel in the AES key schedule
+
+* Add ``pk_encrypt`` and ``pk_decrypt`` CLI operations
+
+* Now ``asn1print`` CLI defaults to printing context-specific fields.
+
+* Use codec_base for Base64, which matches how Base32 is implemented (GH #1597)
+
+* The ``cast`` module has been split up into ``cast128`` and ``cast256`` (GH #1685)
+
+* When building under Visual C++ 2013, the user must acknowledge the upcoming
+ removal of support using the configure.py flag ``--ack-vc2013-deprecated``
+ (GH #1557)
+
+Version 2.7.0, 2018-07-02
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* CVE-2018-12435 Avoid a side channel in ECDSA signature generation (GH #1604)
+
+* Avoid a side channel in RSA key generation due to use of a non-constant time
+ gcd algorithm. (GH #1542 #1556)
+
+* Optimize prime generation, especially improving RSA key generation. (GH #1542)
+
+* Make Karatsuba multiplication, Montgomery field operations, Barrett reduction
+ and Montgomery exponentiation const time (GH #1540 #1606 #1609 #1610)
+
+* Optimizations for elliptic curve operations especially improving reductions
+ and inversions modulo NIST primes (GH #1534 #1538 #1545 #1546 #1547 #1550)
+
+* Add 24 word wide Comba multiplication, improving 3072-bit RSA and DH by ~25%.
+ (GH #1564)
+
+* Unroll Montgomery reduction for specific sizes (GH #1603)
+
+* Improved performance of signature verification in ECGDSA, ECKCDSA,
+ SM2 and GOST by 10-15%.
+
+* XMSS optimizations (GH #1583 #1585)
+
+* Fix an error that meant XMSS would only sign half as many signatures as is
+ allowed (GH #1582)
+
+* Add support for base32 encoding/decoding (GH #1541)
+
+* Add BMI2 optimized version of SHA-256, 40% faster on Skylake (GH #1584)
+
+* Allow the year to be up to 2200 in ASN.1 time objects. Previously this
+ was limited to 2100. (GH #1536)
+
+* Add support for Scrypt password hashing (GH #1570)
+
+* Add support for using Scrypt for private key encryption (GH #1574)
+
+* Optimizations for DES/3DES, approx 50% faster when used in certain modes such
+ as CBC decrypt or CTR.
+
+* XMSS signature verification did not check that the signature was of
+ the expected length which could lead to a crash. (GH #1537)
+
+* The bcrypt variants 2b and 2y are now supported.
+
+* Support for 192-bit Suite B TLS profile is now implemented, as the 128-bit
+ Suite B is since 2015 not allowed anymore.
+
+* Previously botan allowed GCM to be used with an empty nonce, which is not
+ allowed by the specification. Now such nonces are rejected.
+
+* Avoid problems on Windows when compiling in Unicode mode (GH #1615 #1616)
+
+* Previously for ASN.1 encoded signatures (eg ECDSA) Botan would accept any
+ valid BER encoding. Now only the single valid DER encoding is accepted.
+
+* Correct an error that could in rare cases cause an internal error exception
+ when doing computations with the P-224 curve.
+
+* Optimizations to reduce allocations/copies during DER encoding and BER
+ decoding (GH #1571 #1572 #1600)
+
+* Botan generates X.509 subject key IDs by hashing the public key with whatever
+ hash function is being used to sign the certificate. However especially for
+ SHA-512 this caused SKIDs that were far longer than necessary. Now all SKIDs
+ are truncated to 192 bits.
+
+* In the test suite use ``mkstemp`` to create temporary files instead of
+ creating them in the current working directory. (GH #1533 #1530)
+
+* It is now possible to safely override ``CXX`` when invoking make in addition
+ to when ``configure.py`` is run. (GH #1579)
+
+* OIDs for Camellia and SM4 in CBC and GCM mode are now defined, making it
+ possible to use this algorithms for private key encryption.
+
+* Avoid creating symlinks to the shared object on OpenBSD (#1535)
+
+* The ``factor`` command runs much faster on larger inputs now.
+
+* Support for Windows Phone/UWP was deprecated starting in 2.5. This deprecation
+ has been reversed as it seems UWP is still actively used. (GH #1586 #1587)
+
+* Support for Visual C++ 2013 is deprecated, and will be removed in Jan 2019.
+
+* Added support for GCC's --sysroot option to configure.py for cross-compiling.
+
+Version 2.6.0, 2018-04-10
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* CVE-2018-9860 Fix a bug decrypting TLS CBC ciphertexts which could
+ for a malformed ciphertext cause the decryptor to read and HMAC an
+ additional 64K bytes of data which is not part of the record. This
+ could cause a crash if the read went into unmapped memory. No
+ information leak or out of bounds write occurs.
+
+* Add support for OAEP labels (GH #1508)
+
+* RSA signing is about 15% faster (GH #1523) and RSA verification is
+ about 50% faster.
+
+* Add exponent blinding to RSA (GH #1523)
+
+* Add ``Cipher_Mode::create`` and ``AEAD_Mode::create`` (GH #1527)
+
+* Fix bug in TLS server introduced in 2.5 which caused connection to
+ fail if the client offered any signature algorithm not known to the
+ server (for example RSA/SHA-224).
+
+* Fix a bug in inline asm that would with GCC 7.3 cause incorrect
+ computations and an infinite loop during the tests. (GH #1524 #1529)
+
+Version 2.5.0, 2018-04-02
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Fix error in certificate wildcard matching (CVE-2018-9127), where a
+ wildcard cert for ``b*.example.com`` would be accepted as a match for
+ any host with name ``*b*.example.com`` (GH #1519)
+
+* Add support for RSA-PSS signatures in TLS (GH #1285)
+
+* Ed25519 certificates are now supported (GH #1501)
+
+* Many optimizations in ECC operations. ECDSA signatures are 8-10 times faster.
+ ECDSA verification is about twice as fast. ECDH key agreement is 3-4 times
+ faster. (GH #1457 #1478)
+
+* Implement product scanning Montgomery reduction, which improves Diffie-Hellman
+ and RSA performance by 10 to 20% on most platforms. (GH #1472)
+
+* DSA signing and verification performance has improved by 30-50%.
+
+* Add a new Credentials_Manager callback that specifies which CAs the server
+ has indicated it trusts (GH #1395 fixing #1261)
+
+* Add new TLS::Callbacks methods that allow creating or removing extensions,
+ as well as examining extensions sent by the peer (GH #1394 #1186)
+
+* Add new TLS::Callbacks methods that allow an application to
+ negotiate use of custom elliptic curves. (GH #1448)
+
+* Add ability to create custom elliptic curves (GH #1441 #1444)
+
+* Add support for POWER8 AES instructions (GH #1459 #1393 #1206)
+
+* Fix DSA/ECDSA handling of hashes longer than the group order (GH #1502 #986)
+
+* The default encoding of ECC public keys has changed from compressed
+ to uncompressed point representation. This improves compatibility with
+ some common software packages including Golang's standard library.
+ (GH #1480 #1483)
+
+* It is now possible to create DNs with custom components. (GH #1490 #1492)
+
+* It is now possible to specify the serial number of created certificates,
+ instead of using the default 128-bit random integer. (GH #1489 #1491)
+
+* Change DL_Group and EC_Group to store their data as shared_ptr for
+ fast copying. Also both classes precompute additional useful values
+ (eg for modular reductions). (GH #1435 #1454)
+
+* On Windows platforms RtlGenRandom is now used in preference to CryptoAPI
+ or CryptoNG libraries. (GH #1494)
+
+* Make it possible for PKCS10 requests to include custom extensions. This also
+ makes it possible to use multiple SubjectAlternativeNames of a single type in
+ a request, which was previously not possible. (GH #1429 #1428)
+
+* Add new optimized interface for FE1 format preserving encryption. By caching a
+ number of values computed in the course of the FPE calculation, it provides a
+ 6-7x speedup versus the old API. (GH #1469)
+
+* Add DSA and ElGamal keygen functions to FFI (#1426)
+
+* Add ``Pipe::prepend_filter`` to replace deprecated ``Pipe::prepend`` (GH #1402)
+
+* Fix a memory leak in the OpenSSL block cipher integration, introduced in 2.2.0
+
+* Use an improved algorithm for generating safe primes which is several tens of
+ times faster. Also, fix a bug in the prime sieving algorithm which caused
+ standard prime generation (like for RSA keys) to be slower than necessary.
+ (GH #1413 #1411)
+
+* Correct the return value of ``PK_Encryptor::maximum_input_size`` which
+ reported a much too small value (GH #1410)
+
+* Remove use of CPU specific optimization flags, instead the user should set
+ these via CXXFLAGS if desired. (GH #1392)
+
+* Resolve an issue that would cause a crash in the tests if they were run on
+ a machine without SSE2/NEON/VMX instructions. (GH #1495)
+
+* The Python module now tries to load DLLs from a list of names and
+ uses the first one which successfully loads and indicates it
+ supports the desired API level. (GH #1497)
+
+* Various minor optimizations for SHA-3 (GH #1433 #1434)
+
+* The output of ``botan --help`` has been improved (GH #1387)
+
+* Add ``--der-format`` flag to command line utils, making it possible verify
+ DSA/ECDSA signatures generated by OpenSSL command line (GH #1409)
+
+* Add support for ``--library-suffix`` option to ``configure.py`` (GH #1405 #1404)
+
+* Use feature flags to enable/disable system specific code (GH #1378)
+
+* Add ``--msvc-runtime`` option to allow using static runtime (GH #1499 #210)
+
+* Add ``--enable-sanitizers=`` option to allow specifying which sanitizers to
+ enable. The existing ``--with-sanitizers`` option just enables some default
+ set which is known to work with the minimum required compiler versions.
+
+* Use either ``rst2man`` or ``rst2man.py`` for generating man page as
+ distributions differ on where this program is installed (GH #1516)
+
+* The threefish module has been renamed threefish_512 since that is the
+ algorithm it provides. (GH #1477)
+
+* The Perl XS based wrapper has been removed, as it was unmaintained and
+ broken. (GH #1412)
+
+* The sqlite3 encryption patch under ``contrib`` has been removed. It
+ is still maintained by the original author at
+ https://github.com/OlivierJG/botansqlite3
+
+* Support for Windows Phone is deprecated.
+
+Version 2.4.0, 2018-01-08
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Several build improvements requested by downstream packagers, including the
+ ability to disable building the static library. All makefile constructs that
+ were specific to nmake or GNU make have been eliminated, thus the option
+ ``--makefile-style`` which was previously used to select the makefile type has
+ also been removed. (GH #1230 #1237 #1300 #1318 #1319 #1324 #1325 #1346)
+
+* Support for negotiating the DH group as specified in RFC 7919 is now available
+ in TLS (GH #1263)
+
+* Support for ARIA-GCM ciphersuites are now available in TLS. They are disabled
+ by default. (GH #1284)
+
+* Add support for generating and verifying X.509 objects (certificates, CRLs,
+ etc) using RSA-PSS signatures (GH #1270 and #1368)
+
+* Add support for AES key wrapping with padding, as specified in RFC 5649 and
+ NIST SP 800-38F (GH #1301)
+
+* OCSP requests made during certificate verification had the potential to hang
+ forever. Now the sockets are non-blocking and a timeout is enforced. (GH #1360
+ fixing GH #1326)
+
+* Add ``Public_Key::fingerprint_public`` which allows fingerprinting the public key.
+ The previously available ``Private_Key::fingerprint`` is deprecated, now
+ ``Private_Key::fingerprint_private`` should be used if this is required.
+ (GH #1357)
+
+* ECC certificates generated by Botan used an invalid encoding for the
+ parameters field, which was rejected by some certificate validation libraries
+ notably BouncyCastle. (GH #1367)
+
+* Loading an ECC key which used OID encoding for the domain parameters, then
+ saving it, would result in a key using the explicit parameters encoding.
+ Now the OID encoding is retained. (GH #1365)
+
+* Correct various problems in certificate path validation that arose when
+ multiple paths could be constructed leading to a trusted root but due to
+ other constraints only some of them validated. (GH #1363)
+
+* It is now possible for certificate validation to return warning indicators,
+ such as that the distinguished name is not within allowed limits or that a
+ certificate with a negative serial number was observed. (GH #1363 #1359)
+
+* XMSS signatures now are multi-threaded for improved performance (GH #1267)
+
+* Fix a bug that caused the TLS peer cert list to be empty on a resumed session.
+ (GH #1303 #1342)
+
+* Increase the maximum HMAC key length from 512 bytes to 4096 bytes. This allows
+ using a DH key exchange in TLS with a group greater than 4096 bits. (GH #1316)
+
+* Fix a bug in the TLS server where, on receiving an SSLv3 client hello, it
+ would attempt to negotiate TLS v1.2. Now a protocol_version alert is sent.
+ Found with tlsfuzzer. (GH #1316)
+
+* Fix several bugs related to sending the wrong TLS alert type in various error
+ scenarios, caught with tlsfuzzer.
+
+* Add support for a ``tls_http_server`` command line utility which responds to
+ simple GET requests. This is useful for testing against a browser, or various
+ TLS test tools which expect the underlying protocol to be HTTP. (GH #1315)
+
+* Add an interface for generic PSK data stores, as well as an implementation
+ which encrypts stored values with AES key wrapping. (GH #1302)
+
+* Optimize GCM mode on systems both with and without carryless multiply
+ support. This includes a new base case implementation (still constant time), a
+ new SSSE3 implementation for systems with SSSE3 but not clmul, and better
+ algorithms for systems with clmul and pmull. (GH #1253 #1263)
+
+* Various optimizations for OCB, CFB, CTR, SM3, SM4, GMAC, BLAKE2b, Blowfish,
+ Twofish, CAST-128, and CRC24 (GH #1281)
+
+* Salsa20 now supports the seek operation.
+
+* Add ``EC_Group::known_named_groups`` (GH #1339)
+
+* Symmetric algorithms (block ciphers, stream ciphers, MACs) now verify that a
+ key was set before accepting data. Previously attempting to use an unkeyed
+ object would instead result in either a crash or invalid outputs. (GH #1279)
+
+* The X509 certificate, CRL and PKCS10 types have been heavily refactored
+ internally. Previously all data of these types was serialized to strings, then
+ in the event a more complicated data structure (such as X509_DN) was needed,
+ it would be recreated from the string representation. However the round trip
+ process was not perfect and could cause fields to become lost. This approach
+ is no longer used, fixing several bugs (GH #1010 #1089 #1242 #1252). The
+ internal data is now stored in a ``shared_ptr``, so copying such objects is
+ now very cheap. (GH #884)
+
+* ASN.1 string objects previously held their contents as ISO 8859-1 codepoints.
+ However this led to certificates which contained strings outside of this
+ character set (eg in Cyrillic, Greek, or Chinese) being rejected. Now the
+ strings are always converted to UTF-8, which allows representing any
+ character. In addition, UCS-4 strings are now supported.
+ (GH #1113 #1250 #1287 #1289)
+
+* It is now possible to create an uninitialized X509_Certificate object. Such an
+ object will throw if any attempt to access its members is made. (GH #1335)
+
+* In BER decoder, avoid unbounded stack recursion when parsing nested indefinite
+ length values. Now at most 16 nested indefinite length values are accepted,
+ anything deeper resulting in a decoding error. (GH #1304 OSS-Fuzz 4353).
+
+* A new ASN.1 printer API allows generating a string representation of arbitrary
+ BER data. This is used in the ``asn1print`` command line utility and may be
+ useful in other applications, for instance for debugging.
+
+* New functions for bit rotations that distinguish rotating by a compile-time
+ constant vs a runtime variable rotation. This allows better optimizations in
+ both cases. Notably performance of CAST-128 and CAST-256 are substantially
+ improved. (GH #1247)
+
+* TLS CBC ciphersuites now are implemented using the standard CBC code, instead
+ of reimplementing CBC inside the TLS stack. This allows for parallel
+ decryption of TLS CBC ciphertexts, and improves performance especially when
+ using AES hardware support. (GH #1269)
+
+* Add callbacks to make it possible for an application using TLS to provide
+ custom implementations of signature schemes, eg when offloading the
+ computations to another device. (GH #1332)
+
+* Use a direct calculation for calendar computations instead of relying on
+ non-portable operating system interfaces. (GH #1336)
+
+* Fix a bug in the amalgamation generation which could cause build failures on
+ some systems including macOS. (GH #1264 #1265)
+
+* A particular code sequence in TLS handshake would always (with an ECC
+ ciphersuite) result in an exception being thrown and then caught. This has
+ changed so no exception is thrown. (GH #1275)
+
+* The code for byteswapping has been improved for ARMv7 and for Windows x86-64
+ systems using MSVC. (GH #1274)
+
+* The GMAC class no longer derives from GHASH. This should not cause any
+ noticeable change for applications. (GH #1253)
+
+* The base implementation of AES now uses a single 4K table, instead of 4 such
+ tables. This offers a significant improvement against cache-based side
+ channels without hurting performance too much. In addition the table is now
+ guaranteed to be aligned on a cache line, which ensures the additional
+ countermeasure of reading each cache line works as expected. (GH #1255)
+
+* In TLS client resumption, avoid sending a OCSP stapling request. This caused
+ resumption failures with some servers. (GH #1276)
+
+* The overhead of making a call through the FFI layer has been reduced.
+
+* The IDs for SHA-3 PKCSv1.5 signatures added in 2.3.0 were incorrect. They have
+ been changed to use the correct encoding, and a test added to ensure such
+ errors do not recur.
+
+* Counter mode allows setting a configurable width of the counter. Previously it
+ was allowed for a counter of even 8 bits wide, which would mean the keystream
+ would repeat after just 256 blocks. Now it requires the width be at least 32
+ bits. The only way this feature could be used was by manually constructing a
+ ``CTR_BE`` object and setting the second parameter to something in the range
+ of 1 to 3.
+
+* A new mechanism for formatting ASN.1 data is included in ``asn1_print.h``.
+ This is the same functionality used by the command line ``asn1print`` util,
+ now cleaned up and moved to the library.
+
+* Add ``Pipe::append_filter``. This is like the existing (deprecated)
+ ``Pipe::append``, the difference being that ``append_filter`` only
+ allows modification before the first call to ``start_msg``. (GH #1306 #1307)
+
+* The size of ASN1_Tag is increased to 32 bits. This avoids a problem
+ with UbSan (GH #751)
+
+* Fix a bug affecting bzip2 compression. In certain circumstances, compression
+ would fail with ``BZ_SEQUENCE_ERROR`` due to calling bzlib in an way it does
+ not support. (GH #1308 #1309)
+
+* In 2.3.0, final annotations were added to many classes including the TLS
+ policies (like ``Strict_Policy`` and ``BSI_TR_02102_2``). However it is
+ reasonable and useful for an application to derive from one of these policies, so
+ as to create an application specific policy that is based on a library-provided
+ policy, but with a few tweaks. So the final annotations have been removed on
+ these classes. (GH #1292)
+
+* A new option ``--with-pdf`` enables building a PDF copy of the handbook.
+ (GH #1337)
+
+* A new option ``--with-rst2man`` enables building a man page for the
+ command line util using Docutils rst2man. (GH #1349)
+
+* Support for NEON is now enabled under Clang.
+
+* Now the compiler version is detected using the preprocessor, instead of trying
+ to parse the output of the compiler's version string, which was subject to
+ problems with localization. (GH #1358)
+
+* By default the gzip compressor will not include a timestamp in the header.
+ The timestamp can be set by passing it to the ``Gzip_Compression``
+ constructor.
+
+* Resolve a performance regression on Windows involving the system stats
+ entropy source. (GH #1369)
+
+* Add an OID for RIPEMD-160
+
+* Fixes for CMake build (GH #1251)
+
+* Avoid some signed overflow warnings (GH #1220 #1245)
+
+* As upstream support for Native Client has been deprecated by Google, support
+ is now also deprecated in Botan and will be removed in a future release.
+
+* The Perl-XS wrapper has not been maintained in many years. It is now deprecated,
+ and if no attempts are made to revive it, it will be removed in a future release.
+
+* Support for building on IRIX has been removed.
+
+Version 2.3.0, 2017-10-02
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Address a side channel affecting modular exponentiation. An attacker
+ capable of a local or cross-VM cache analysis attack may be able
+ to recover bits of secret exponents as used in RSA, DH, etc.
+ CVE-2017-14737
+
+* Add the SHACAL2 block cipher, including optimizations using SIMD and SHA-NI
+ instructions. (GH #1151)
+
+* Add the ARIA block cipher (GH #1004 and #1157)
+
+* Add support for the ARMv8 AES instructions (GH #1182 and #1146)
+
+* Add support for the ARMv8 PMULL instruction (GH #1181 and #842)
+
+* On macOS and iOS the ``System_RNG`` class is now implemented using ``arc4random``.
+ Previously the system RNG class was not available on iOS. (GH #1219)
+
+* Optimized the CMAC polynomial doubling operation, and removed a small timing
+ channel due to a conditional operation.
+
+* Added support for the ECDHE_PSK AEAD TLS ciphersuites from
+ draft-ietf-tls-ecdhe-psk-aead-05.
+
+* SM2 encryption and signature schemes were previously hardcoded to use SM3
+ hash, now any hash is allowed. (GH #1188)
+
+* SM2 encryption in 2.2.0 followed an obsolete version of the standard. The
+ format of the ciphertext changed in a more recent revision of the standard,
+ and now uses an ASN.1 encoding. Botan has changed to reflect this format,
+ which is compatible with GmSSL (GH #1218)
+
+* OCB mode now supports 192, 256 and 512 bit block ciphers. (GH #1205)
+
+* XTS mode now supports 256-bit and 512-bit block ciphers.
+
+* Add ids to allow SHA-3 signatures with PKCSv1.5 (GH #1184)
+
+* Add support for ``PSSR_Raw`` signatures which PSS sign an externally derived
+ hash. (GH #1212 #1211)
+
+* GCM now supports truncated tags in the range 96...128 bits. GCM had
+ previously supported 64-bit truncated tags, but these are known to
+ be insecure and are now deprecated. (GH #1210 #1207)
+
+* Add a new TLS policy hook ``allow_client_initiated_renegotiation`` which is the
+ parallel of the existing ``allow_server_initiated_renegotiation``. If set to
+ false, servers will reject attempts by the client to renegotiation the
+ session, instead sending a ``no_renegotiation`` warning alert. Note that the
+ default is ``false``, ie that client renegotiation is now prohibited by default.
+ (GH #872)
+
+* Add HKDF-Expand-Label function which is used in TLS v1.3 and QUIC protocols.
+ (GH #1226)
+
+* Fix decoding of ECC keys that use extensions from RFC 5915 (GH #1208)
+
+* The entropy source that called CryptGenRandom has been removed, and
+ replaced by a version which invokes the system PRNG, which may
+ be CryptGenRandom or some other source. (GH #1180)
+
+* Add support for gathering entropy using the Crypt-NG BCryptGenRandom
+ API. This is necessary to build for Windows Phone/Windows Store. (GH #1180)
+
+* Extend "Raw" signature padding (which allows signing a hash computed
+ externally) to optionally take a hash function name. In this case, it will be
+ verified that the input matches the expected hash size. This also will
+ control the hash algorithm used for RFC 6979 deterministic nonces; previously
+ SHA-512 was always used for RFC 6979 nonces with "Raw". (GH #1153)
+
+* The advertised FFI API version has increased. This should have happened
+ already in 2.2 but was neglected. The ``botan_ffi_supports_api`` call will
+ return true for either the current or older versions of the API version since
+ no backwards incompatible changes have occurred.
+
+* Add new C89 API functions ``botan_hex_decode``, ``botan_base64_encode``,
+ ``botan_base64_decode``, ``botan_constant_time_compare``.
+
+* Add new C89 API functions ``botan_privkey_load_dh``, ``botan_pubkey_load_dh``,
+ and ``botan_privkey_create_dh`` (GH #1155)
+
+* Add ``is_passhash9_alg_supported`` (GH #1154)
+
+* The ``power_mod`` function now supports negative bases (GH #1179 #1168)
+
+* Add a new command line utility for examining TLS client hellos.
+
+* Added a new target for LLVM bitcode (GH #1169)
+
+* Improve support for Windows Phone (GH #1180 #796 #794)
+
+* Correct return value of ``botan_pk_op_verify_finish``. In 2.2.0 this function
+ returned -1 on invalid signature, instead of 1 which was used in 2.0, 2.1, and
+ now again in 2.3. (GH #1189 #1187)
+
+* Allow loading unencrypted private keys via FFI API (GH #1197)
+
+* Add new command line options ``--rng-type=drbg`` and ``--drbg-seed`` which
+ allow running commands with a deterministic RNG. (GH #1169)
+
+* Fix a number of warnings seen under Visual C++ (GH #1171 #795)
+
+* Workaround a GCC 7 bug that caused miscompilation of the GOST-34.11 hash
+ function on x86-32. (GH #882 #1148)
+
+* Fix a bug in SIMD_4x32 which affected little-endian PowerPC processors.
+ This would cause test failures for Serpent, among other problems.
+
+* Fix Altivec runtime detection, which was broken starting in Botan 2.1.0
+
+* Optimized the verification of TLS CBC padding bytes. Previously the check
+ examined every byte of the record, even though at most 256 bytes of padding
+ may be appended. (GH #1227)
+
+* Simplified definition of ``Botan::secure_allocator``. In particular, not
+ defining the ``construct`` and ``destroy`` methods avoids a performance problem
+ under MSVC. (GH #1228 and #1229)
+
+* The ``secure_allocator`` class now uses ``calloc`` and ``free`` instead of
+ ``new`` and ``delete``. In addition the actual allocation operation is hidden
+ inside of compiled functions, which significantly reduces code size. (GH #1231)
+
+* The ``secure_scrub_memory`` function now uses ``explicit_bzero`` on OpenBSD.
+
+* Previously ARM feature detection (NEON, AES, ...) relied on getauxval, which
+ is only supported on Linux and Android. Now iOS is supported, by checking the
+ model name/version and matching it against known versions. Unfortunately this
+ is the best available technique on iOS. On Aarch64 systems that are not iOS or
+ Linux/Android, a technique based on trial execution while catching SIGILL is
+ used. (GH #1213)
+
+* The output of ``botan config libs`` was incorrect, it produced ``-lbotan-2.X``
+ where X is the minor version, instead of the actual lib name ``-lbotan-2``.
+
+* Add ``constant_time_compare`` as better named equivalent of ``same_mem``.
+
+* Silence a Clang warning in ``create_private_key`` (GH #1150)
+
+* The fuzzers have been better integrated with the main build. See the
+ handbook for details. (GH #1158)
+
+* The Travis CI and AppVeyor CI builds are now run via a Python script. This
+ makes it easier to replicate the behavior of the CI build locally. Also a
+ number of changes were made to improve the turnaround time of CI builds.
+ (GH #1162 #1199)
+
+* Add support for Win32 filesystem operation, so the tests pass completely
+ on MinGW now (GH #1203)
+
+* Added a script to automate running TLS-Attacker tests.
+
+* The distribution script now creates reproducible outputs, by
+ forcing all modification times, uids, etc to values fixed by the release date.
+ (GH #1217)
+
+* The ``BOTAN_DLL`` macro has been split up into ``BOTAN_PUBLIC_API``,
+ ``BOTAN_UNSTABLE_API`` and ``BOTAN_TEST_API`` which allows
+ indicating in the header the API stability of the export. All three
+ are defined as ``BOTAN_DLL`` so overriding just that macro continues
+ to work as before. (GH #1216)
+
+* Optimize ``bigint_divop`` when a double-word type is available. (GH #494)
+
+* Fix several memory leaks in the tests. Additionally a false positive
+ leak seen under ``valgrind`` in the ``fork`` tests for the RNG was resolved.
+
+* Export ``CurveGFp_Repr`` type (only used internally) to resolve a
+ long standing UBSan warning. (GH #453)
+
+* Now ``-fstack-protector`` and similar flags that affect linking are exported
+ in ``botan config ldflags`` as they already were in the ``pkg-config`` output.
+ (GH #863)
+
+* Remove double underscore in header guards to avoid using names
+ reserved by ISO C++. (GH #512)
+
+* Additions to the SRP documentation (GH #1029)
+
+* The package transform (in ``package.h``) is now deprecated, and will be
+ removed in a future release. (GH #1215)
+
+* Add more tests for the const-time utils (GH #1214)
+
+* Fix a bug in FFI tests that caused the test files not to be found when using
+ ``--data-dir`` option (GH #1149)
+
+* C++ ``final`` annotations have been added to classes which are not
+ intended for derivation. This keyword was already in use but was not
+ applied consistently.
+
+* A typedef ``SecureVector`` has been added for the ``secure_vector`` type.
+ This makes porting code from 1.10 to 2.x API slightly simpler.
+
+* Header files have been cleaned up to remove unnecessary inclusions. In some
+ cases it may be required to include additional botan headers to get all the
+ declarations that were previously visible. For example, ``bigint.h`` no longer
+ includes ``rng.h``, but just forward declares ``RandomNumberGenerator``.
+
+* Improved support for IBM xlc compiler.
+
+Version 2.2.0, 2017-08-07
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Add the Ed25519 signature scheme (GH #1066)
+
+* The format of x25519 keys, which previously used a non-standard encoding,
+ has changed to match the upcoming IETF specification. (GH #1076)
+
+* Add the SM2 signature scheme (GH #1082)
+
+* Add the SM2 public key encryption scheme (GH #1142)
+
+* Add the SM3 hash function (GH #996)
+
+* Add the Streebog (GOST R 34.11-2012) hash function (GH #1114)
+
+* Add the SM4 block cipher (GH #1080)
+
+* Add the PGP S2K algorithm (GH #1060)
+
+* Add SP 800-56A KDF (GH #1040)
+
+* Add ChaCha_RNG which is a very fast and completely non-standard
+ random bit generator (GH #1137)
+
+* Add support for SHA-1 and SHA-2 instructions added in Intel Goldmont
+ (GH #826)
+
+* Add support for SHA-1 and SHA-2 instructions added in ARMv8 (GH #844)
+
+* Add support for HOTP (RFC 4226) and TOTP (RFC 6238)
+ one-time-password algorithms (GH #1054)
+
+* Fix a bug that caused secure_allocator to not fully zeroize blocks
+ when sizeof(T) was greater than 1.
+
+* Add HashFunction::copy_state which allows efficiently computing the
+ hash of several messages with a common prefix (GH #1056 #1037)
+
+* ECC keys now encode their parameters using an OID instead of a literal
+ encoding of the domain parameters. This will lead to smaller public and
+ private keys in most instances. (GH #1093)
+
+* The OpenSSL backend now supports the 1.1.0 API (GH #1056)
+
+* Add a preliminary provider using BearSSL, currently EC and hashes supported
+ (GH #1094)
+
+* Fix a bug in certificate path length checking that could cause valid
+ chains to be rejected. (GH #1053)
+
+* It is possible for CBC, CFB, and stream ciphers to carry over the
+ nonce from the previous message, which is needed by some applications.
+ This worked in 1.10 but broke in 2.0. (GH #1044 fixing GH #864)
+
+* Avoid recursion in BER_Decoder::get_next_object which could cause
+ stack exhaustion. (GH #989)
+
+* Fix missing flush in DataSink_Stream::end_msg. (GH #972 fixing GH #972)
+
+* Allow to seek in the big endian counter mode of operation (GH #999)
+
+* Support loading ElGamal keys through FFI interface (GH #1008)
+
+* Support Windows sockets in ``http_util`` (allowing OCSP checks on Windows),
+ as well as in the TLS command line utils (GH #1138).
+
+* The ``--destdir`` flag to ``configure.py`` has been removed. Instead use
+ the ``DESTDIR`` environment variable at install time. This change was
+ done to more closely match how autoconf handles this case.
+ (GH #1139 #1111 #997 #996).
+
+* Many changes to configure.py and botan2.py to make them pylint clean
+ (GH #1041 #1002 #984)
+
+* Add command line utils ``hmac`` (GH #1001), ``encryption`` (GH #359),
+ ``hex_enc``, and ``hex_dec``.
+
+* Fix an error in ``sign_cert`` command line util, which ignored the
+ ``--ca-key-pass`` option. (GH #1106)
+
+* The ``speed`` util can now benchmark multiple buffer sizes (GH #1084)
+
+* Fix return value of FFI botan_bcrypt_is_valid (GH #1033)
+
+* Support generating RSA keys using OpenSSL (GH #1035)
+
+* Add new FFI functions botan_hash_block_size (GH #1036),
+ botan_hash_copy_state (GH #1059), botan_scrub_mem
+
+* Add support for RFC 3394 keywrap through FFI (GH #1135)
+
+* Support AES-CBC ciphers via OpenSSL (GH #1022)
+
+* Add function to return certificates included in OCSP response (GH #1123)
+
+* Complete wildcard handling for X.509 certificates (GH #1017)
+
+* Add some missing functions to TLS::Text_Policy (GH #1023)
+
+* It was previously possible to use ``--single-amalgamation-file``
+ without ``--amalgamation``, though it did not do anything useful. Now
+ ``--single-amalgamation-file`` requires ``--amalgamation`` also be set
+ on the command line.
+
+Version 2.1.0, 2017-04-04
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Fix incorrect truncation in Bcrypt. Passwords in length between 56 and 72
+ characters were truncated at 56 characters. Found and reported by Solar Designer.
+ (CVE-2017-7252) (GH #938)
+
+* Fix a bug in X509 DN string comparisons that could result in out of bound
+ reads. This could result in information leakage, denial of service, or
+ potentially incorrect certificate validation results. Found independently
+ by Cisco Talos team and OSS-Fuzz. (CVE-2017-2801)
+
+* Correct minimum work factor for Bcrypt password hashes. All other
+ implementations require the work factor be at least 4. Previously Botan simply
+ required it be greater than zero. (GH #938)
+
+* Converge on a single side channel silent EC blinded multiply algorithm.
+ Uses Montgomery ladder with order/2 bits scalar blinding and point randomization
+ now by default. (GH #893)
+
+* Add ability to search for certificates using the SHA-256 of the distinguished name.
+ (GH #900)
+
+* Support a 0-length IV in ChaCha stream cipher. Such an IV is treated
+ identically to an 8-byte IV of all zeros.
+
+* Add new interfaces to the C API including multiple precision integers, key
+ validity tests, block ciphers, and extracting algorithm specific key parameters
+ (such as the modulus and public exponent from RSA public keys). GH #899 #944
+ #946 #961 #964
+
+* The PKCS11 module did not require any external dependencies, so it
+ has been enabled by default. The ``--with-pkcs11`` and ``--without-pkcs11``
+ flags to ``configure.py`` have been removed. PKCS11 can still be disabled
+ 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.
+
+* Add support for ARM NEON in the SIMD_4x32 type
+
+* Add support for ARM CPU feature detection using getauxval (GH #843)
+
+* Previously Botan forbid any use of times past 2037 to avoid Y2038 issues.
+ Now this restriction is only in place on systems which have a 32-bit
+ ``time_t``. (GH #933 fixing #917)
+
+* Add generic type decoder function to BER decoder (GH #897)
+
+* Fix portability or build problems affecting Sun Studio compiler (GH #846),
+ Solaris, ppc64le, DragonflyBSD (GH #887)
+
+* Add ``--with-external-libdir`` to configure.py (GH #857 fixing #19 #767)
+
+* Add ``OS::get_high_resolution_clock`` which returns the best resolution
+ clock available on the system.
+
+* Change ``OS::get_processor_timestamp`` to return 0 if no hardware
+ cycle counter is available. Previously it silently fell back on some
+ other clock type.
+
+* Report cycles/byte in the output of ``botan speed``.
+
+* Add speed tests for modular exponentiations and ECC scalar multiplies.
+
+* Avoid using IP address for SNI in ``tls_client``. (GH #942)
+
+* Add command line util ``timing_test`` which enables running
+ timing-based side channel analysis of TLS CBC decryption, ECC scalar
+ multiplies, OAEP decoding, and other operations which are prone to
+ providing an oracle via side channel. This replaces the standalone
+ timing test suite added in 1.11.34, which has been removed.
+
+* Various cleanups and refactorings (GH #965)
+
+* Add wrapper of C++14 make_unique (GH #974)
+
+* Fix pkg-config output when --build-dir was used (GH #936)
+
+* Make it possible to disable `-fstack-protector` using a build-time flag.
+ GH #863
+
+* Add tests for TLS DSA ciphersuites, more Noekeon tests, others.
+
+* Avoid a GCC warning that triggered on the public key types (GH #849)
+
+* Fix various warnings flagged by pylint and pyflakes linters in
+ configure.py and botan.py (GH #832 #836 #839 #962 #975)
+
+* Improve support for OpenBSD including using getentropy (GH #954)
+ for PRNG seeding, and arc4random to access system RNG (GH #953)
+
+* Add ability to build through CMake. As of now this is only supported
+ for development rather than production builds. (GH #967)
+
+* Rename python wrapper to botan2.py (GH #847)
+
+* Change name constraint test to use a fixed reference time. Test certs have expired.
+
+* Increase Miller-Rabin iterations for DSA primes to match FIPS 186-4. (GH #881)
+
+* Fix possible ISO 9796-2 padding side channel, and add a missing length check (GH #891)
+
+* In command line utility, prefer the system RNG if it is available.
+
+Version 2.0.1, 2017-01-09
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Change an unintended behavior of 2.0.0, which named the include
+ directory ``botan-2.0``. Since future release of Botan-2 should be
+ compatible with code written against old versions, there does not
+ seem to be any reason to version the include directory with the
+ minor number. (GH #830 #833)
+
+* Fix a bug which caused an error when building on Cygwin or
+ other platforms where shared libraries are not supported.
+ (GH #821)
+
+* Enable use of readdir on Cygwin, which allows the tests to run (GH #824)
+
+* Switch to readthedocs Sphinx theme by default (GH #822 #823)
+
+Version 2.0.0, 2017-01-06
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* With this release the project adopts Semantic Versioning (GH #766)
+
+* Fix a longstanding bug in modular exponentiation which caused most
+ exponentiations modulo an even number to have an incorrect result; such moduli
+ occur only rarely in cryptographic contexts. (GH #754)
+
+* Fix a bug in BigInt multiply operation, introduced in 1.11.30, which could
+ cause incorrect results. Found by OSS-Fuzz fuzzing the ressol function, where
+ the bug manifested as an incorrect modular exponentiation. OSS-Fuzz bug #287
+
+* Fix a bug that meant the "ietf/modp/6144" and "ietf/modp/8192" discrete log
+ groups used an incorrect value for the generator, specifically the value
+ (p-1)/2 was used instead of the correct value of 2.
+
+* The DL_Group enum value X942_DH_PARAMETERS has been renamed
+ ANSI_X9_42_DH_PARAMETERS to avoid a conflict with Windows headers (GH #482)
+
+* Change default PEM header for X942 DH to match OpenSSL. Either version is
+ accepted on reading. (GH #818)
+
+* DL_Group strong generation previously set the generator to 2. However
+ sometimes 2 generates the entire group mod p, rather than the subgroup mod q.
+ This is invalid by X9.42 standard, and exposes incautious applications to
+ small subgroup attacks. Now DL_Group uses the smallest g which is a quadratic
+ residue. (GH #818)
+
+* Add iOS build target instead of piggybacking on OS X configuration. (GH #793)
+
+* Changes all Public_Key derived class ctors to take a std::vector instead of a
+ secure_vector for the DER encoded public key bits. (GH #768)
+
+* Allow use of custom extensions when creating X.509 certificates (GH #744)
+
+* The default TLS policy now requires 2048 or larger DH groups by default.
+
+* Add BSI_TR_02102_2 TLS::Policy subclass representing BSI TR-02102-2 recommendations.
+
+* The default Path_Validation_Restrictions constructor has changed to
+ require at least 110 bit signature strength. This means 1024 bit RSA
+ certificates and also SHA-1 certificates are rejected by default.
+ Both settings were already the default for certificate validation in
+ TLS handshake, but this changes it for applications also.
+
+* Add ISO 9796-2 signature padding schemes DS2 and DS3. These schemes provide
+ message recovery (part or all of the plaintext message can be recovered from
+ the signature alone) and are used by some industry protocols. (GH #759)
+
+* Rewrite all the code that handles parsing CBC padding bytes to run without
+ conditional jumps or loads. (GH #765 #728)
+
+* Fix deref of invalid memory location in TLS client when the server chooses a
+ ciphersuite value larger than the largest TLS ciphersuite ID compiled into the
+ table. This might conceivably cause a crash in rare circumstances, but does
+ not seem to be further exploitable. (GH #758)
+
+* Rename Public_Key::x509_subject_public_key, which does not return a
+ X.509 SubjectPublicKey, to public_key_bits. Add a new non-virtual function
+ Public_Key::subject_public_key which does exactly that. (GH #685 #757)
+
+* Rename Private_Key::pkcs8_private_key, which does not return a
+ PKCS#8 private key, to private_key_bits. Add a new non-virtual function
+ Private_Key::private_key_info which does exactly that. (GH #685 #757)
+
+* The deprecated ECB Cipher_Mode class has been removed (GH #756)
+
+* The class SRP6_Authenticator_File (in srp6_files.h) was meant to parse GnuTLS
+ SRP files. But it was completely untested, and it turns out due to several
+ problems it was completely unable to parse any SRP file correctly. It has
+ been removed, with a future replacement planned that can handle both
+ flat files (in the actual SRP format) or using a SQL database.
+
+* Fix tests errors when write access to /dev/urandom is prohibited (GH #748)
+
+* Add more Diffie-Hellman tests (GH #790), tests for RSA blinding, others.
+
+* Add `tls_ciphers` command which prints the ciphersuites a client
+ hello will contain, depending on the policy specified.
+
+* Prevent TLS from negotiating SHA-2 ciphersuites in TLS v1.0/v1.1. These
+ ciphersuites are technically not defined except for v1.2, so disable
+ them in older protocols. (GH #496)
+
+* Documentation: add project goals (GH #788) and side channel info (GH #787)
+
+Older Versions
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* The release notes for versions 0.7.0 through 1.11.34 can be found in
+ ``doc/old_news.rst``
diff --git a/comm/third_party/botan/readme.rst b/comm/third_party/botan/readme.rst
new file mode 100644
index 0000000000..019b772d58
--- /dev/null
+++ b/comm/third_party/botan/readme.rst
@@ -0,0 +1,135 @@
+Botan: Crypto and TLS for Modern C++
+========================================
+
+Botan (Japanese for peony flower) is a C++ cryptography library released under the
+permissive `Simplified BSD <https://botan.randombit.net/license.txt>`_ license.
+
+Botan's goal is to be the best option for cryptography in C++ by offering the
+tools necessary to implement a range of practical systems, such as TLS protocol,
+X.509 certificates, modern AEAD ciphers, PKCS#11 and TPM hardware support,
+password hashing, and post quantum crypto schemes. A Python binding is included,
+and several other `language bindings
+<https://github.com/randombit/botan/wiki/Language-Bindings>`_ are available.
+It is used in many `open source and commercial products <https://github.com/randombit/botan/wiki/Users>`_.
+The library is accompanied by a featureful
+`command line interface <https://botan.randombit.net/handbook/cli.html>`_.
+
+See the `documentation <https://botan.randombit.net/handbook>`_ for more
+information about included features.
+
+Development is coordinated on `GitHub <https://github.com/randombit/botan>`_
+and contributions are welcome. If you need help, please open an issue on
+`GitHub <https://github.com/randombit/botan/issues>`_ or email the
+`botan-devel mailing list <https://lists.randombit.net/mailman/listinfo/botan-devel/>`_.
+New releases are announced on the `botan-announce mailing list
+<https://lists.randombit.net/mailman/listinfo/botan-announce/>`_.
+If you think you have found a security issue, see the `security page
+<https://botan.randombit.net/security.html>`_ for contact information.
+
+The latest release is
+`2.18.2 <https://botan.randombit.net/releases/Botan-2.18.2.tar.xz>`_
+`(sig) <https://botan.randombit.net/releases/Botan-2.18.2.tar.xz.asc>`_,
+released on 2021-10-25.
+All releases are signed with a `PGP key <https://botan.randombit.net/pgpkey.txt>`_.
+See the `release notes <https://botan.randombit.net/news.html>`_ for
+what is new. Botan is also available through most
+`distributions <https://github.com/randombit/botan/wiki/Distros>`_
+such as Fedora, Debian, Arch and Homebrew.
+
+.. image:: https://api.travis-ci.com/randombit/botan.svg?branch=master
+ :target: https://travis-ci.com/github/randombit/botan
+ :alt: Travis CI status
+
+.. image:: https://ci.appveyor.com/api/projects/status/n9f94dljd03j2lce/branch/master?svg=true
+ :target: https://ci.appveyor.com/project/randombit/botan/branch/master
+ :alt: AppVeyor CI status
+
+.. image:: https://codecov.io/github/randombit/botan/coverage.svg?branch=master
+ :target: https://codecov.io/github/randombit/botan
+ :alt: Code coverage report
+
+.. image:: https://img.shields.io/lgtm/alerts/g/randombit/botan.svg
+ :target: https://lgtm.com/projects/g/randombit/botan/alerts/
+ :alt: LGTM alerts
+
+.. image:: https://oss-fuzz-build-logs.storage.googleapis.com/badges/botan.svg
+ :target: https://oss-fuzz.com/coverage-report/job/libfuzzer_asan_botan/latest
+ :alt: OSS-Fuzz status
+
+.. image:: https://scan.coverity.com/projects/624/badge.svg
+ :target: https://scan.coverity.com/projects/624
+ :alt: Coverity results
+
+.. image:: https://repology.org/badge/tiny-repos/botan.svg
+ :target: https://repology.org/project/botan/versions
+ :alt: Packaging status
+
+.. image:: https://bestpractices.coreinfrastructure.org/projects/531/badge
+ :target: https://bestpractices.coreinfrastructure.org/projects/531
+ :alt: CII Best Practices statement
+
+Find Enclosed
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Transport Layer Security (TLS) Protocol
+----------------------------------------
+
+* TLS v1.0, v1.1, and v1.2. The broken SSLv3 protocol is no longer supported.
+* DTLS v1.0 and v1.2 are adaptations of TLS to datagram operation.
+* Supported extensions include session tickets, SNI, ALPN, OCSP stapling,
+ encrypt-then-mac CBC, and extended master secret.
+* Supports authentication using preshared keys (PSK) or passwords (SRP)
+* Supports record encryption with ChaCha20Poly1305, AES/OCB, AES/GCM, AES/CCM,
+ Camellia/GCM as well as legacy CBC ciphersuites.
+* Key exchange using CECPQ1, ECDH, FFDHE, or RSA
+
+Public Key Infrastructure
+----------------------------------------
+
+* X.509v3 certificates and CRL creation and handling
+* PKIX certificate path validation, including name constraints.
+* OCSP request creation and response handling
+* PKCS #10 certificate request generation and processing
+* Access to Windows, macOS and Unix system certificate stores
+* SQL database backed certificate store
+
+Public Key Cryptography
+----------------------------------------
+
+* RSA signatures and encryption
+* DH and ECDH key agreement
+* Signature schemes ECDSA, DSA, Ed25519, ECGDSA, ECKCDSA, SM2, GOST 34.10
+* Post-quantum signature scheme XMSS
+* Post-quantum key agreement schemes McEliece and NewHope
+* ElGamal encryption
+* Padding schemes OAEP, PSS, PKCS #1 v1.5, X9.31
+
+Ciphers, hashes, MACs, and checksums
+----------------------------------------
+
+* Authenticated cipher modes EAX, OCB, GCM, SIV, CCM, (X)ChaCha20Poly1305
+* Cipher modes CTR, CBC, XTS, CFB, OFB
+* Block ciphers AES, ARIA, Blowfish, Camellia, CAST-128, DES/3DES, IDEA,
+ Lion, Noekeon, SEED, Serpent, SHACAL2, SM4, Threefish-512, Twofish
+* Stream ciphers (X)ChaCha20, (X)Salsa20, SHAKE-128, RC4
+* Hash functions SHA-1, SHA-2, SHA-3, MD4, MD5, RIPEMD-160, BLAKE2b,
+ Skein-512, SM3, Streebog, Whirlpool
+* Authentication codes HMAC, CMAC, Poly1305, SipHash, GMAC, X9.19 DES-MAC
+* Non-cryptographic checksums Adler32, CRC24, CRC32
+
+Other Useful Things
+----------------------------------------
+
+* Full C++ PKCS #11 API wrapper
+* Interfaces for TPM v1.2 device access
+* Simple compression API wrapping zlib, bzip2, and lzma libraries
+* RNG wrappers for system RNG and hardware RNGs
+* HMAC_DRBG and entropy collection system for userspace RNGs
+* Password hashing schemes PBKDF2, Argon2, Scrypt, bcrypt
+* SRP-6a password authenticated key exchange
+* Key derivation functions including HKDF, KDF2, SP 800-108, SP 800-56A, SP 800-56C
+* HOTP and TOTP algorithms
+* Format preserving encryption scheme FE1
+* Threshold secret sharing
+* NIST key wrapping
+* Boost.Asio compatible TLS client stream
diff --git a/comm/third_party/botan/src/bogo_shim/bogo_shim.cpp b/comm/third_party/botan/src/bogo_shim/bogo_shim.cpp
new file mode 100644
index 0000000000..1f09cd5fa4
--- /dev/null
+++ b/comm/third_party/botan/src/bogo_shim/bogo_shim.cpp
@@ -0,0 +1,1680 @@
+/*
+* (C) 2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+/*
+* This is a shim for testing Botan against BoringSSL's test TLS stack (BoGo).
+*
+* Instructions on use should go here.
+*/
+
+#include <botan/tls_client.h>
+#include <botan/tls_server.h>
+#include <botan/tls_exceptn.h>
+#include <botan/tls_algos.h>
+#include <botan/data_src.h>
+#include <botan/pkcs8.h>
+#include <botan/loadstor.h>
+#include <botan/oids.h>
+#include <botan/chacha_rng.h>
+#include <botan/base64.h>
+#include <botan/hex.h>
+#include <botan/parsing.h>
+#include <botan/mem_ops.h>
+#include <iostream>
+#include <vector>
+#include <string>
+#include <map>
+#include <set>
+#include <ctime>
+#include <unordered_map>
+
+#if defined(BOTAN_TARGET_OS_HAS_SOCKETS)
+ #include <sys/socket.h>
+ #include <sys/time.h>
+ #include <netinet/in.h>
+ #include <netdb.h>
+ #include <string.h>
+ #include <unistd.h>
+ #include <errno.h>
+ #include <fcntl.h>
+#endif
+
+namespace {
+
+int shim_output(const std::string& s, int rc = 0)
+ {
+ std::cout << s << "\n";
+ return rc;
+ }
+
+void shim_log(const std::string& s)
+ {
+ if(::getenv("BOTAN_BOGO_SHIM_LOG"))
+ {
+ static FILE* log = std::fopen("/tmp/bogo_shim.log", "w");
+ struct timeval tv;
+ ::gettimeofday(&tv, nullptr);
+ std::fprintf(log, "%lld.%lu: %s\n", static_cast<unsigned long long>(tv.tv_sec), tv.tv_usec, s.c_str());
+ std::fflush(log);
+ }
+ }
+
+void BOTAN_NORETURN shim_exit_with_error(const std::string& s, int rc = 1)
+ {
+ shim_log("Exiting with " + s);
+ std::cerr << s << "\n";
+ std::exit(rc);
+ }
+
+std::string map_to_bogo_error(const std::string& e)
+ {
+ shim_log("Original error " + e);
+
+ static const std::unordered_map<std::string, std::string> 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 extension size", ":DECODE_ERROR:" },
+ { "Bad length in hello verify request", ":DECODE_ERROR:" },
+ { "Bad lengths in DTLS header", ":BAD_HANDSHAKE_RECORD:" },
+ { "Bad signature on server key exchange", ":BAD_SIGNATURE:" },
+ { "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:" },
+ { "Can't agree on a ciphersuite with client", ":NO_SHARED_CIPHER:" },
+ { "Can't interleave application and handshake data", ":UNEXPECTED_RECORD:" },
+ { "Certificate chain exceeds policy specified maximum size", ":EXCESSIVE_MESSAGE_SIZE:" },
+ { "Certificate key type did not match ciphersuite", ":WRONG_CERTIFICATE_TYPE:" },
+ { "Certificate usage constraints do not allow this ciphersuite", ":KEY_USAGE_BIT_INCORRECT:" },
+ { "Certificate: Message malformed", ":DECODE_ERROR:" },
+ { "Channel::key_material_export cannot export during renegotiation", "failed to export keying material" },
+ { "Client cert verify failed", ":BAD_SIGNATURE:" },
+ { "Client did not offer NULL compression", ":INVALID_COMPRESSION_LIST:" },
+ { "Client offered TLS version with major version under 3", ":UNSUPPORTED_PROTOCOL:" },
+ { "Client offered DTLS version with major version 0xFF", ":UNSUPPORTED_PROTOCOL:" },
+ { "Client policy prohibits insecure renegotiation", ":RENEGOTIATION_MISMATCH:" },
+ { "Client policy prohibits renegotiation", ":NO_RENEGOTIATION:" },
+ { "Client resumed extended ms session without sending extension", ":RESUMED_EMS_SESSION_WITHOUT_EMS_EXTENSION:" },
+ { "Client signalled fallback SCSV, possible attack", ":INAPPROPRIATE_FALLBACK:" },
+ { "Client version DTLS v1.0 is unacceptable by policy", ":UNSUPPORTED_PROTOCOL:" },
+ { "Client version TLS v1.0 is unacceptable by policy", ":UNSUPPORTED_PROTOCOL:" },
+ { "Client version TLS v1.1 is unacceptable by policy", ":UNSUPPORTED_PROTOCOL:" },
+ { "Client: No certificates sent by server", ":DECODE_ERROR:" },
+ { "Counterparty sent inconsistent key and sig types", ":WRONG_SIGNATURE_TYPE:" },
+ { "Downgrade attack detected", ":TLS13_DOWNGRADE:" },
+ { "Empty ALPN protocol not allowed", ":PARSE_TLSEXT:" },
+ { "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:" },
+ { "Finished message didn't verify", ":DIGEST_CHECK_FAILED:" },
+ { "Got unexpected TLS record version", ":WRONG_VERSION_NUMBER:" },
+ { "Inconsistent length in certificate request", ":DECODE_ERROR:" },
+ { "Inconsistent values in fragmented DTLS handshake header", ":FRAGMENT_MISMATCH:" },
+ { "Invalid CertificateRequest: Length field outside parameters", ":DECODE_ERROR:" },
+ { "Invalid CertificateVerify: Extra bytes at end of message", ":DECODE_ERROR:" },
+ { "Invalid Certificate_Status: invalid length field", ":DECODE_ERROR:" },
+ { "Invalid ChangeCipherSpec", ":BAD_CHANGE_CIPHER_SPEC:" },
+ { "Invalid ClientHello: Length field outside parameters", ":DECODE_ERROR:" },
+ { "Invalid ClientKeyExchange: Extra bytes at end of message", ":DECODE_ERROR:" },
+ { "Invalid ServerKeyExchange: Extra bytes at end of message", ":DECODE_ERROR:" },
+ { "Invalid SessionTicket: Extra bytes at end of message", ":DECODE_ERROR:" },
+ { "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:" },
+ { "Message authentication failure", ":DECRYPTION_FAILED_OR_BAD_RECORD_MAC:" },
+ { "No shared TLS version", ":UNSUPPORTED_PROTOCOL:" },
+ { "No shared DTLS version", ":UNSUPPORTED_PROTOCOL:" },
+ { "OS2ECP: Unknown format type 251", ":BAD_ECPOINT:" },
+ { "Policy forbids all available TLS version", ":NO_SUPPORTED_VERSIONS_ENABLED:" },
+ { "Policy forbids all available DTLS version", ":NO_SUPPORTED_VERSIONS_ENABLED:" },
+ { "Policy refuses to accept signing with any hash supported by peer", ":NO_COMMON_SIGNATURE_ALGORITHMS:" },
+ { "Policy requires client send a certificate, but it did not", ":PEER_DID_NOT_RETURN_A_CERTIFICATE:" },
+ { "Received a record that exceeds maximum size", ":ENCRYPTED_LENGTH_TOO_LONG:" },
+ { "Received unexpected record version in initial record", ":WRONG_VERSION_NUMBER:" },
+ { "Received unexpected record version", ":WRONG_VERSION_NUMBER:" },
+ { "Received application data after connection closure", ":APPLICATION_DATA_ON_SHUTDOWN:" },
+ { "Received handshake data after connection closure", ":NO_RENEGOTIATION:" },
+ { "Server certificate changed during renegotiation", ":SERVER_CERT_CHANGED:" },
+ { "Server changed its mind about extended master secret", ":RENEGOTIATION_EMS_MISMATCH:" },
+ { "Server changed its mind about secure renegotiation", ":RENEGOTIATION_MISMATCH:" },
+ { "Server changed version after renegotiation", ":WRONG_SSL_VERSION:" },
+ { "Server downgraded version after renegotiation", ":WRONG_SSL_VERSION:" },
+ { "Server policy prohibits renegotiation", ":NO_RENEGOTIATION:" },
+ { "Server replied using a ciphersuite not allowed in version it offered", ":WRONG_CIPHER_RETURNED:" },
+ { "Server replied with DTLS-SRTP alg we did not send", ":BAD_SRTP_PROTECTION_PROFILE_LIST:" },
+ { "Server replied with ciphersuite we didn't send", ":WRONG_CIPHER_RETURNED:" },
+ { "Server replied with later version than client offered", ":UNSUPPORTED_PROTOCOL:" },
+ { "Server replied with non-null compression method", ":UNSUPPORTED_COMPRESSION_ALGORITHM:" },
+ { "Server replied with some unknown ciphersuite", ":UNKNOWN_CIPHER_RETURNED:" },
+ { "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: 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:" },
+ { "Server resumed session but added extended master secret", ":RESUMED_NON_EMS_SESSION_WITH_EMS_EXTENSION:" },
+ { "Server resumed session but with wrong version", ":OLD_SESSION_VERSION_NOT_RETURNED:" },
+ { "Server sent ECC curve prohibited by policy", ":WRONG_CURVE:" },
+ { "Server sent an unsupported extension", ":UNEXPECTED_EXTENSION:" },
+ { "Server sent bad values for secure renegotiation", ":RENEGOTIATION_MISMATCH:" },
+ { "Server version DTLS v1.0 is unacceptable by policy", ":UNSUPPORTED_PROTOCOL:" },
+ { "Server version TLS v1.0 is unacceptable by policy", ":UNSUPPORTED_PROTOCOL:" },
+ { "Server version TLS v1.1 is unacceptable by policy", ":UNSUPPORTED_PROTOCOL:" },
+ { "Server_Hello_Done: Must be empty, and is not", ":DECODE_ERROR:" },
+ { "Simulated OCSP callback failure", ":OCSP_CB_ERROR:" },
+ { "Simulating cert verify callback failure", ":CERT_CB_ERROR:" },
+ { "Simulating failure from OCSP response callback", ":OCSP_CB_ERROR:" },
+ { "TLS plaintext record is larger than allowed maximum", ":DATA_LENGTH_TOO_LONG:" },
+ { "TLS signature extension did not allow for RSA/SHA-256 signature", ":WRONG_SIGNATURE_TYPE:", },
+ { "Test requires rejecting cert", ":CERTIFICATE_VERIFY_FAILED:" },
+ { "Unexpected ALPN protocol", ":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:" },
+ { "Unexpected state transition in handshake got a certificate_request expected server_key_exchange|server_hello_done seen server_hello", ":UNEXPECTED_MESSAGE:" },
+ { "Unexpected state transition in handshake got a certificate_status expected certificate seen server_hello", ":UNEXPECTED_MESSAGE:" },
+ { "Unexpected state transition in handshake got a change_cipher_spec expected certificate_verify seen client_hello+certificate+client_key_exchange", ":UNEXPECTED_RECORD:" },
+ { "Unexpected state transition in handshake got a change_cipher_spec expected client_key_exchange seen client_hello", ":UNEXPECTED_RECORD:" },
+ { "Unexpected state transition in handshake got a change_cipher_spec expected new_session_ticket seen server_hello+certificate+certificate_status+server_key_exchange+server_hello_done", ":UNEXPECTED_RECORD:" },
+ { "Unexpected state transition in handshake got a client_key_exchange expected certificate seen client_hello", ":UNEXPECTED_MESSAGE:" },
+ { "Unexpected state transition in handshake got a finished expected change_cipher_spec seen client_hello", ":UNEXPECTED_RECORD:" },
+ { "Unexpected state transition in handshake got a finished expected change_cipher_spec seen client_hello+client_key_exchange", ":UNEXPECTED_RECORD:" },
+ { "Unexpected state transition in handshake got a finished expected change_cipher_spec seen server_hello", ":UNEXPECTED_RECORD:" },
+ { "Unexpected state transition in handshake got a finished expected change_cipher_spec seen server_hello+certificate+certificate_status+server_key_exchange+server_hello_done+new_session_ticket", ":UNEXPECTED_RECORD:" },
+ { "Unexpected state transition in handshake got a hello_request expected server_hello", ":UNEXPECTED_MESSAGE:" },
+ { "Unexpected state transition in handshake got a server_hello_done expected server_key_exchange seen server_hello+certificate+certificate_status", ":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 server_key_exchange expected certificate_request|server_hello_done seen server_hello+certificate+certificate_status", ":UNEXPECTED_MESSAGE:" },
+
+ { "Unknown TLS handshake message type 43", ":UNEXPECTED_MESSAGE:" },
+ { "Unknown TLS handshake message type 44", ":UNEXPECTED_MESSAGE:" },
+ { "Unknown TLS handshake message type 45", ":UNEXPECTED_MESSAGE:" },
+ { "Unknown TLS handshake message type 46", ":UNEXPECTED_MESSAGE:" },
+ { "Unknown TLS handshake message type 53", ":UNEXPECTED_MESSAGE:" },
+ { "Unknown TLS handshake message type 54", ":UNEXPECTED_MESSAGE:" },
+ { "Unknown TLS handshake message type 55", ":UNEXPECTED_MESSAGE:" },
+ { "Unknown TLS handshake message type 56", ":UNEXPECTED_MESSAGE:" },
+ { "Unknown TLS handshake message type 57", ":UNEXPECTED_MESSAGE:" },
+ { "Unknown TLS handshake message type 58", ":UNEXPECTED_MESSAGE:" },
+ { "Unknown TLS handshake message type 6", ":UNEXPECTED_MESSAGE:" },
+ { "Unknown TLS handshake message type 62", ":UNEXPECTED_MESSAGE:" },
+ { "Unknown TLS handshake message type 64", ":UNEXPECTED_MESSAGE:" },
+ { "signature_algorithm_of_scheme: Unknown signature algorithm enum", ":WRONG_SIGNATURE_TYPE:" },
+ };
+
+ auto err_map_i = err_map.find(e);
+ if(err_map_i != err_map.end())
+ return err_map_i->second;
+
+ return "Unmapped error: '" + e + "'";
+ }
+
+class Shim_Exception final : public std::exception
+ {
+ public:
+ Shim_Exception(const std::string& msg, int rc = 1) :
+ m_msg(msg), m_rc(rc) {}
+
+ const char* what() const noexcept override { return m_msg.c_str(); }
+
+ int rc() const { return m_rc; }
+ private:
+ const std::string m_msg;
+ int m_rc;
+ };
+
+#if defined(BOTAN_TARGET_OS_HAS_SOCKETS)
+
+class Shim_Socket final
+ {
+ private:
+ typedef int socket_type;
+ typedef ssize_t socket_op_ret_type;
+ static void close_socket(socket_type s) { ::close(s); }
+ static std::string get_last_socket_error() { return ::strerror(errno); }
+
+ public:
+ Shim_Socket(const std::string& hostname, int port) : m_socket(-1)
+ {
+ addrinfo hints;
+ std::memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = AI_NUMERICSERV;
+ addrinfo* res;
+
+ const std::string service = std::to_string(port);
+ int rc = ::getaddrinfo(hostname.c_str(), service.c_str(), &hints, &res);
+ shim_log("Connecting " + hostname + ":" + service);
+
+ if(rc != 0)
+ {
+ throw Shim_Exception("Name resolution failed for " + hostname);
+ }
+
+ for(addrinfo* rp = res; (m_socket == -1) && (rp != nullptr); rp = rp->ai_next)
+ {
+ if(rp->ai_family != AF_INET && rp->ai_family != AF_INET6)
+ continue;
+
+ m_socket = ::socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
+
+ if(m_socket == -1)
+ {
+ // unsupported socket type?
+ continue;
+ }
+
+ int err = ::connect(m_socket, rp->ai_addr, rp->ai_addrlen);
+
+ if(err != 0)
+ {
+ ::close(m_socket);
+ m_socket = -1;
+ }
+ }
+
+ if(m_socket < 0)
+ throw Shim_Exception("Failed to connect to host");
+ }
+
+ ~Shim_Socket()
+ {
+ ::close(m_socket);
+ m_socket = -1;
+ }
+
+ void write(const uint8_t buf[], size_t len)
+ {
+ if(m_socket < 0)
+ throw Shim_Exception("Socket was bad on write");
+ size_t sent_so_far = 0;
+ while(sent_so_far != len)
+ {
+ const size_t left = len - sent_so_far;
+ 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)
+ return;
+ else
+ throw Shim_Exception("Socket write failed", errno);
+ }
+ else
+ sent_so_far += static_cast<size_t>(sent);
+ }
+ }
+
+ size_t read(uint8_t buf[], size_t len)
+ {
+ 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);
+
+ if(got < 0)
+ {
+ if(errno == ECONNRESET)
+ return 0;
+ throw Shim_Exception("Socket read failed: " + std::string(strerror(errno)));
+ }
+
+ return static_cast<size_t>(got);
+ }
+
+ void read_exactly(uint8_t buf[], size_t len)
+ {
+ if(m_socket < 0)
+ throw Shim_Exception("Socket was bad on read");
+
+ while(len > 0)
+ {
+ 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");
+ else if(got < 0)
+ throw Shim_Exception("Socket read failed: " + std::string(strerror(errno)));
+
+ buf += static_cast<size_t>(got);
+ len -= static_cast<size_t>(got);
+ }
+ }
+
+ private:
+ socket_type m_socket;
+ };
+
+#endif
+
+std::set<std::string> combine_options(
+ const std::set<std::string>& a,
+ const std::set<std::string>& b,
+ const std::set<std::string>& c,
+ const std::set<std::string>& d)
+ {
+ std::set<std::string> combined;
+
+ for(auto i : a)
+ combined.insert(i);
+ for(auto i : b)
+ combined.insert(i);
+ for(auto i : c)
+ combined.insert(i);
+ for(auto i : d)
+ combined.insert(i);
+
+ return combined;
+ }
+
+class Shim_Arguments final
+ {
+ public:
+ Shim_Arguments(const std::set<std::string>& flags,
+ const std::set<std::string>& string_opts,
+ const std::set<std::string>& base64_opts,
+ const std::set<std::string>& int_opts,
+ const std::set<std::string>& int_vec_opts) :
+ m_flags(flags),
+ m_string_opts(string_opts),
+ m_base64_opts(base64_opts),
+ m_int_opts(int_opts),
+ m_int_vec_opts(int_vec_opts),
+ m_all_options(combine_options(string_opts, base64_opts, int_opts, int_vec_opts))
+ {}
+
+ void parse_args(char* argv[]);
+
+ bool flag_set(const std::string& flag) const
+ {
+ if(m_flags.count(flag) == 0)
+ throw Shim_Exception("Unknown bool flag " + flag);
+
+ return m_parsed_flags.count(flag);
+ }
+
+ std::string test_name() const
+ {
+ return get_string_opt("test-name");
+ }
+
+ std::string get_string_opt(const std::string& key) const
+ {
+ if(m_string_opts.count(key) == 0)
+ throw Shim_Exception("Unknown string key " + key);
+ return get_opt(key);
+ }
+
+ std::string get_string_opt_or_else(const std::string& key, const std::string& def) const
+ {
+ if(m_string_opts.count(key) == 0)
+ throw Shim_Exception("Unknown string key " + key);
+ if(!option_used(key))
+ return def;
+ return get_opt(key);
+ }
+
+ std::vector<uint8_t> get_b64_opt(const std::string& key) const
+ {
+ if(m_base64_opts.count(key) == 0)
+ throw Shim_Exception("Unknown base64 key " + key);
+ return Botan::unlock(Botan::base64_decode(get_opt(key)));
+ }
+
+ size_t get_int_opt(const std::string& key) const
+ {
+ if(m_int_opts.count(key) == 0)
+ throw Shim_Exception("Unknown int key " + key);
+ return Botan::to_u32bit(get_opt(key));
+ }
+
+ size_t get_int_opt_or_else(const std::string& key, size_t def) const
+ {
+ if(m_int_opts.count(key) == 0)
+ throw Shim_Exception("Unknown int key " + key);
+ if(!option_used(key))
+ return def;
+
+ return Botan::to_u32bit(get_opt(key));
+ }
+
+ std::vector<size_t> get_int_vec_opt(const std::string& key) const
+ {
+ if(m_int_vec_opts.count(key) == 0)
+ throw Shim_Exception("Unknown int vec key " + key);
+
+ auto i = m_parsed_int_vec_opts.find(key);
+ if(i == m_parsed_int_vec_opts.end())
+ return std::vector<size_t>();
+ else
+ return i->second;
+ }
+
+ std::vector<std::string> 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;
+
+ if(option_used(option))
+ return Botan::split_on(get_string_opt(option), delim);
+ else
+ return std::vector<std::string>();
+ }
+
+ bool option_used(const std::string& key) const
+ {
+ if(m_all_options.count(key) == 0)
+ throw Shim_Exception("Invalid option " + key);
+ if(m_parsed_opts.find(key) != m_parsed_opts.end())
+ return true;
+ if(m_parsed_int_vec_opts.find(key) != m_parsed_int_vec_opts.end())
+ return true;
+ return false;
+ }
+
+ private:
+ std::string get_opt(const std::string& key) const
+ {
+ auto i = m_parsed_opts.find(key);
+ if(i == m_parsed_opts.end())
+ throw Shim_Exception("Option " + key + " was not provided");
+ return i->second;
+ }
+
+ const std::set<std::string> m_flags;
+ const std::set<std::string> m_string_opts;
+ const std::set<std::string> m_base64_opts;
+ const std::set<std::string> m_int_opts;
+ const std::set<std::string> m_int_vec_opts;
+ const std::set<std::string> m_all_options;
+
+ std::set<std::string> m_parsed_flags;
+ std::map<std::string, std::string> m_parsed_opts;
+ std::map<std::string, std::vector<size_t>> m_parsed_int_vec_opts;
+ };
+
+void Shim_Arguments::parse_args(char* argv[])
+ {
+ int i = 1; // skip argv[0]
+
+ while(argv[i] != nullptr)
+ {
+ const std::string param(argv[i]);
+
+ if(param.find("-") == 0)
+ {
+ const std::string flag_name = param.substr(1, std::string::npos);
+
+ if(m_flags.count(flag_name))
+ {
+ shim_log("flag " + flag_name);
+ m_parsed_flags.insert(flag_name);
+ i += 1;
+ }
+ else if(m_all_options.count(flag_name))
+ {
+ if(argv[i+1] == nullptr)
+ throw Shim_Exception("Expected argument following " + param);
+ std::string val(argv[i+1]);
+ shim_log("param " + flag_name + "=" + val);
+
+ if(m_int_vec_opts.count(flag_name))
+ {
+ const size_t v = Botan::to_u32bit(val);
+ m_parsed_int_vec_opts[flag_name].push_back(v);
+ }
+ else
+ {
+ m_parsed_opts[flag_name] = val;
+ }
+ i += 2;
+ }
+ else
+ {
+ shim_log("Unknown option " + param);
+ throw Shim_Exception("Unknown option " + param, 89);
+ }
+ }
+ else
+ {
+ shim_log("Unknown option " + param);
+ throw Shim_Exception("Unknown option " + param, 89);
+ }
+ }
+ }
+
+std::unique_ptr<Shim_Arguments> parse_options(char* argv[])
+ {
+ const std::set<std::string> bogo_shim_flags = {
+ "allow-false-start-without-alpn",
+ "allow-unknown-alpn-protos",
+ "async",
+ "cbc-record-splitting",
+ "check-close-notify",
+ "decline-alpn",
+ "decline-ocsp-callback",
+ "dtls",
+ "enable-all-curves",
+ "enable-channel-id",
+ "enable-early-data",
+ "enable-ed25519",
+ "enable-grease",
+ "enable-ocsp-stapling",
+ "enable-signed-cert-timestamps",
+ "enforce-rsa-key-usage",
+ //"expect-accept-early-data",
+ "expect-extended-master-secret",
+ "expect-no-offer-early-data",
+ "expect-no-secure-renegotiation",
+ "expect-no-session",
+ "expect-no-session-id",
+ //"expect-reject-early-data",
+ "expect-secure-renegotiation",
+ "expect-session-id",
+ "expect-session-miss",
+ "expect-sha256-client-cert",
+ "expect-ticket-renewal",
+ "expect-ticket-supports-early-data",
+ //"expect-tls13-downgrade",
+ "expect-verify-result",
+ "expect-no-hrr",
+ //"export-traffic-secrets",
+ "fail-cert-callback",
+ //"fail-ddos-callback",
+ //"fail-early-callback",
+ "fail-ocsp-callback",
+ "fallback-scsv",
+ //"false-start",
+ "forbid-renegotiation-after-handshake",
+ "handoff",
+ "handshake-never-done",
+ "handshake-twice",
+ "handshaker-resume",
+ //"ignore-tls13-downgrade",
+ "implicit-handshake",
+ "install-cert-compression-algs",
+ "install-ddos-callback",
+ "is-handshaker-supported",
+ //"jdk11-workaround",
+ //"key-update",
+ "no-op-extra-handshake",
+ "no-rsa-pss-rsae-certs",
+ "no-ticket",
+ "no-tls1",
+ "no-tls11",
+ "no-tls12",
+ "no-tls13", // implict due to 1.3 not being implemented
+ "on-resume-no-ticket",
+ //"on-resume-verify-fail",
+ //"partial-write",
+ //"peek-then-read",
+ //"read-with-unfinished-write",
+ "renegotiate-freely",
+ "renegotiate-ignore",
+ "renegotiate-once",
+ //"renew-ticket",
+ "require-any-client-certificate",
+ "retain-only-sha256-client-cert",
+ //"reverify-on-resume",
+ "select-empty-alpn",
+ "send-alert",
+ "server",
+ "server-preference",
+ "set-ocsp-in-callback",
+ "shim-shuts-down",
+ "shim-writes-first",
+ //"tls-unique",
+ "use-custom-verify-callback",
+ "use-early-callback",
+ "use-export-context",
+ "use-exporter-between-reads",
+ "use-ocsp-callback",
+ //"use-old-client-cert-callback",
+ //"use-ticket-callback",
+ "verify-fail",
+ "verify-peer",
+ //"verify-peer-if-no-obc",
+ "write-different-record-sizes",
+ };
+
+ const std::set<std::string> bogo_shim_string_opts = {
+ "advertise-alpn",
+ //"advertise-npn",
+ "cert-file",
+ "cipher",
+ //"delegated-credential",
+ "expect-advertised-alpn",
+ "expect-alpn",
+ "expect-client-ca-list",
+ "expect-late-alpn",
+ "expect-msg-callback",
+ //"expect-next-proto",
+ "expect-peer-cert-file",
+ "expect-server-name",
+ "export-context",
+ "export-label",
+ "handshaker-path",
+ "host-name",
+ "key-file",
+ "psk",
+ "psk-identity",
+ "select-alpn",
+ "select-next-proto",
+ "srtp-profiles",
+ "test-name",
+ "use-client-ca-list",
+ //"send-channel-id",
+ //"write-settings",
+ };
+
+ const std::set<std::string> bogo_shim_base64_opts = {
+ "expect-certificate-types",
+ //"expect-channel-id",
+ "expect-ocsp-response",
+ //"expect-quic-transport-params",
+ //"expect-signed-cert-timestamps",
+ "ocsp-response",
+ //"quic-transport-params",
+ //"signed-cert-timestamps",
+ //"ticket-key", /* we use a different ticket format from Boring */
+ //"token-binding-params",
+ };
+
+ const std::set<std::string> bogo_shim_int_opts {
+ "expect-cipher-aes",
+ "expect-cipher-no-aes",
+ "expect-curve-id",
+ "expect-peer-signature-algorithm",
+ "expect-ticket-age-skew",
+ "expect-token-binding-param",
+ "expect-total-renegotiations",
+ "expect-version",
+ //"export-early-keying-material",
+ "export-keying-material",
+ "initial-timeout-duration-ms",
+ "max-cert-list",
+ //"max-send-fragment",
+ "max-version",
+ "min-version",
+ "mtu",
+ "port",
+ "read-size",
+ "resume-count",
+ "resumption-delay",
+ };
+
+ const std::set<std::string> bogo_shim_int_vec_opts {
+ "curves",
+ "expect-peer-verify-pref",
+ "signing-prefs",
+ "verify-prefs",
+ };
+
+ std::unique_ptr<Shim_Arguments> args(
+ new Shim_Arguments(bogo_shim_flags,
+ bogo_shim_string_opts,
+ bogo_shim_base64_opts,
+ bogo_shim_int_opts,
+ bogo_shim_int_vec_opts));
+
+ // may throw:
+ args->parse_args(argv);
+
+ return args;
+ }
+
+class Shim_Policy final : public Botan::TLS::Policy
+ {
+ public:
+ Shim_Policy(const Shim_Arguments& args) : m_args(args), m_sessions(0) {}
+
+ void incr_session_established() { m_sessions += 1; }
+
+ std::vector<std::string> allowed_ciphers() const override
+ {
+ return {
+ "AES-256/OCB(12)",
+ "AES-128/OCB(12)",
+ "ChaCha20Poly1305",
+ "AES-256/GCM",
+ "AES-128/GCM",
+ "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",
+ "AES-256",
+ "AES-128",
+ "Camellia-256",
+ "Camellia-128",
+ "SEED",
+ "3DES",
+ };
+
+ }
+
+ std::vector<std::string> allowed_signature_hashes() const override
+ {
+ if(m_args.option_used("signing-prefs"))
+ {
+ std::vector<std::string> pref_hash;
+ for(size_t pref : m_args.get_int_vec_opt("signing-prefs"))
+ {
+ const auto scheme = static_cast<Botan::TLS::Signature_Scheme>(pref);
+ if(Botan::TLS::signature_scheme_is_known(scheme) == false)
+ continue;
+ pref_hash.push_back(Botan::TLS::hash_function_of_scheme(scheme));
+ }
+
+ if(m_args.flag_set("server"))
+ pref_hash.push_back("SHA-256");
+ return pref_hash;
+ }
+ else
+ {
+ return { "SHA-512", "SHA-384", "SHA-256", "SHA-1" };
+ }
+ }
+
+ //std::vector<std::string> allowed_macs() const override;
+
+ std::vector<std::string> allowed_key_exchange_methods() const override
+ {
+ return {
+ "ECDHE_PSK",
+ "DHE_PSK",
+ "PSK",
+ "CECPQ1",
+ "ECDH",
+ "DH",
+ "RSA",
+ };
+ }
+
+ std::vector<std::string> allowed_signature_methods() const override
+ {
+ return {
+ "ECDSA",
+ "RSA",
+ "IMPLICIT",
+ };
+
+ }
+
+ std::vector<Botan::TLS::Signature_Scheme> allowed_signature_schemes() const override
+ {
+ if(m_args.option_used("signing-prefs"))
+ {
+ std::vector<Botan::TLS::Signature_Scheme> schemes;
+ for(size_t pref : m_args.get_int_vec_opt("signing-prefs"))
+ {
+ schemes.push_back(static_cast<Botan::TLS::Signature_Scheme>(pref));
+ }
+
+ // BoGo gets sad if these are not included in our signature_algorithms extension
+ if(!m_args.flag_set("server"))
+ {
+ schemes.push_back(Botan::TLS::Signature_Scheme::RSA_PKCS1_SHA256);
+ schemes.push_back(Botan::TLS::Signature_Scheme::ECDSA_SHA256);
+ }
+
+ return schemes;
+ }
+
+ if(m_args.option_used("verify-prefs"))
+ {
+ std::vector<Botan::TLS::Signature_Scheme> schemes;
+ for(size_t pref : m_args.get_int_vec_opt("verify-prefs"))
+ {
+ schemes.push_back(static_cast<Botan::TLS::Signature_Scheme>(pref));
+ }
+
+ return schemes;
+ }
+
+ return Botan::TLS::Policy::allowed_signature_schemes();
+ }
+
+ //size_t minimum_signature_strength() const override;
+
+ //bool require_cert_revocation_info() const override;
+
+ std::vector<Botan::TLS::Group_Params> key_exchange_groups() const override
+ {
+ if(m_args.option_used("curves"))
+ {
+ std::vector<Botan::TLS::Group_Params> groups;
+ for(size_t pref : m_args.get_int_vec_opt("curves"))
+ {
+ groups.push_back(static_cast<Botan::TLS::Group_Params>(pref));
+ }
+
+ return groups;
+ }
+
+ return Botan::TLS::Policy::key_exchange_groups();
+ }
+
+ bool use_ecc_point_compression() const override { return false; } // BoGo expects this
+
+ //Botan::TLS::Group_Params choose_key_exchange_group(const std::vector<Botan::TLS::Group_Params>& peer_groups) const override;
+
+ bool require_client_certificate_authentication() const override
+ {
+ return m_args.flag_set("require-any-client-certificate");
+ }
+
+ bool request_client_certificate_authentication() const override
+ {
+ return m_args.flag_set("verify-peer") ||
+ m_args.flag_set("fail-cert-callback") ||
+ 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 include_time_in_hello_random() const override;
+
+ bool allow_client_initiated_renegotiation() const override
+ {
+ if(m_args.flag_set("renegotiate-freely"))
+ return true;
+
+ if(m_args.flag_set("renegotiate-once") && m_sessions <= 1)
+ return true;
+
+ return false;
+ }
+
+ bool allow_server_initiated_renegotiation() const override
+ {
+ return allow_client_initiated_renegotiation(); // same logic
+ }
+
+ bool allow_version(Botan::TLS::Protocol_Version version) const
+ {
+ if(m_args.option_used("min-version"))
+ {
+ const uint16_t min_version_16 = static_cast<uint16_t>(m_args.get_int_opt("min-version"));
+ Botan::TLS::Protocol_Version min_version(min_version_16 >> 8, min_version_16 & 0xFF);
+ if(min_version > version)
+ return false;
+ }
+
+ if(m_args.option_used("max-version"))
+ {
+ const uint16_t max_version_16 = static_cast<uint16_t>(m_args.get_int_opt("max-version"));
+ Botan::TLS::Protocol_Version max_version(max_version_16 >> 8, max_version_16 & 0xFF);
+ if(version > max_version)
+ return false;
+ }
+
+ return version.known_version();
+ }
+
+ bool allow_tls10() const override
+ {
+ return !m_args.flag_set("dtls") &&
+ !m_args.flag_set("no-tls1") &&
+ allow_version(Botan::TLS::Protocol_Version::TLS_V10);
+ }
+
+ bool allow_tls11() const override
+ {
+ return !m_args.flag_set("dtls") && !m_args.flag_set("no-tls11") && allow_version(Botan::TLS::Protocol_Version::TLS_V11);
+ }
+
+ bool allow_tls12() const override
+ {
+ return !m_args.flag_set("dtls") && !m_args.flag_set("no-tls12") && allow_version(Botan::TLS::Protocol_Version::TLS_V12);
+ }
+
+ bool allow_dtls10() const override
+ {
+ return m_args.flag_set("dtls") && !m_args.flag_set("no-tls1") && allow_version(Botan::TLS::Protocol_Version::DTLS_V10);
+ }
+
+ bool allow_dtls12() const override
+ {
+ return m_args.flag_set("dtls") && !m_args.flag_set("no-tls12") && allow_version(Botan::TLS::Protocol_Version::DTLS_V12);
+ }
+
+ //Botan::TLS::Group_Params default_dh_group() const override;
+
+ //size_t minimum_dh_group_size() const override;
+
+ size_t minimum_ecdsa_group_size() const override { return 224; }
+
+ size_t minimum_ecdh_group_size() const override { return 224; }
+
+ //size_t minimum_rsa_bits() const override;
+
+ //size_t minimum_dsa_group_size() const override;
+
+ //void check_peer_key_acceptable(const Botan::Public_Key& public_key) const override;
+
+ //bool hide_unknown_users() const override;
+
+ //uint32_t session_ticket_lifetime() const override;
+
+ std::vector<uint16_t> srtp_profiles() const override
+ {
+ if(m_args.option_used("srtp-profiles"))
+ {
+ 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};
+ else if(srtp == "SRTP_AES128_CM_SHA1_80")
+ return {1};
+ else
+ shim_exit_with_error("unknown srtp-profiles");
+ }
+ else
+ return {};
+ }
+
+ bool only_resume_with_exact_version() const override
+ {
+ return false;
+ }
+
+ bool send_fallback_scsv(Botan::TLS::Protocol_Version) const override
+ {
+ return m_args.flag_set("fallback-scsv");
+ }
+
+ //bool server_uses_own_ciphersuite_preferences() const override;
+
+ //bool negotiate_encrypt_then_mac() const override;
+
+ bool support_cert_status_message() const override
+ {
+ if(m_args.flag_set("server"))
+ {
+ if(!m_args.option_used("ocsp-response"))
+ return false;
+ if(m_args.flag_set("decline-ocsp-callback"))
+ return false;
+ }
+ return true;
+ }
+
+ std::vector<uint16_t> ciphersuite_list(Botan::TLS::Protocol_Version version,
+ bool have_srp) const override;
+
+ size_t dtls_default_mtu() const override
+ {
+ return m_args.get_int_opt_or_else("mtu", 1500);
+ }
+
+ //size_t dtls_initial_timeout() const override;
+
+ //size_t dtls_maximum_timeout() const override;
+
+ bool allow_resumption_for_renegotiation() const override
+ {
+ return false; // BoGo expects this
+ }
+
+ bool abort_connection_on_undesired_renegotiation() const override
+ {
+ if(m_args.flag_set("renegotiate-ignore"))
+ return false;
+ else
+ return true;
+ }
+
+ size_t maximum_certificate_chain_size() const override
+ {
+ return m_args.get_int_opt_or_else("max-cert-list", 0);
+ }
+
+ private:
+ const Shim_Arguments& m_args;
+ size_t m_sessions;
+ };
+
+std::vector<uint16_t> Shim_Policy::ciphersuite_list(Botan::TLS::Protocol_Version version,
+ bool have_srp) const
+ {
+ std::vector<uint16_t> ciphersuite_codes;
+
+ 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<std::string> suites = {
+ "ECDHE_RSA_WITH_AES_128_GCM_SHA256",
+ "ECDHE_RSA_WITH_AES_256_GCM_SHA384",
+ "ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
+ "ECDHE_RSA_WITH_AES_256_CBC_SHA",
+ "RSA_WITH_AES_256_GCM_SHA384",
+ "RSA_WITH_AES_256_CBC_SHA",
+ };
+
+ for(auto suite_name : suites)
+ {
+ const auto suite = Botan::TLS::Ciphersuite::from_name(suite_name);
+ if(suite.valid() == false)
+ shim_exit_with_error("Bad ciphersuite name " + suite_name);
+ ciphersuite_codes.push_back(suite.ciphersuite_code());
+ }
+ }
+ else
+ {
+ // Hack: go in reverse order to avoid preferring 3DES
+ auto ciphersuites = Botan::TLS::Ciphersuite::all_known_ciphersuites();
+ for(auto i = ciphersuites.rbegin(); i != ciphersuites.rend(); ++i)
+ {
+ const auto suite = *i;
+ // Can we use it?
+ if(suite.valid() == false)
+ continue;
+
+ // Are we doing SRP?
+ if(!have_srp && suite.kex_method() == Botan::TLS::Kex_Algo::SRP_SHA)
+ continue;
+
+ if(cipher_limit != "")
+ {
+ if(cipher_limit == "DEFAULT:!AES")
+ {
+ const std::string suite_algo = suite.cipher_algo();
+
+ if(suite_algo == "AES-128" || suite_algo == "AES-256" ||
+ suite_algo == "AES-128/GCM" || suite_algo == "AES-256/GCM" ||
+ suite_algo == "AES-128/CCM" || suite_algo == "AES-256/CCM" ||
+ suite_algo == "AES-128/CCM(8)" || suite_algo == "AES-256/CCM(8)" ||
+ suite_algo == "AES-128/OCB(12)" || suite_algo == "AES-256/OCB(12)")
+ {
+ continue;
+ }
+ }
+ else
+ {
+ shim_exit_with_error("Unknown cipher " + cipher_limit);
+ }
+ }
+
+ if(!version.supports_aead_modes())
+ {
+ // Are we doing AEAD in a non-AEAD version?
+ if(suite.mac_algo() == "AEAD")
+ continue;
+
+ // Older (v1.0/v1.1) versions also do not support any hash but SHA-1
+ if(suite.mac_algo() != "SHA-1")
+ continue;
+ }
+
+ ciphersuite_codes.push_back(suite.ciphersuite_code());
+ }
+ }
+
+ return ciphersuite_codes;
+ }
+
+class Shim_Credentials final : public Botan::Credentials_Manager
+ {
+ public:
+ Shim_Credentials(const Shim_Arguments& args) : m_args(args)
+ {
+ m_psk_identity = m_args.get_string_opt_or_else("psk-identity", "");
+
+ const std::string psk_str = m_args.get_string_opt_or_else("psk", "");
+ m_psk = Botan::SymmetricKey(reinterpret_cast<const uint8_t*>(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 = Botan::PKCS8::load_key(key_stream);
+
+ 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(...) {}
+ }
+ }
+ }
+
+ std::string psk_identity(const std::string& /*type*/,
+ const std::string& /*context*/,
+ const std::string& /*identity_hint*/) override
+ {
+ return m_psk_identity;
+ }
+
+ std::string psk_identity_hint(const std::string& /*type*/,
+ const std::string& /*context*/) override
+ {
+ return m_psk_identity;
+ }
+
+ Botan::SymmetricKey psk(const std::string& type,
+ const std::string& context,
+ const std::string& identity) override
+ {
+ if(type == "tls-server" && context == "session-ticket")
+ {
+ if(!m_args.flag_set("no-ticket") && !m_args.flag_set("on-resume-no-ticket"))
+ return Botan::SymmetricKey("ABCDEF0123456789");
+ }
+
+ if(type == "tls-server" && context == "dtls-cookie-secret")
+ {
+ return Botan::SymmetricKey("F00FB00FD00F100F700F");
+ }
+
+ if(identity != m_psk_identity)
+ throw Shim_Exception("Unexpected PSK identity");
+ return m_psk;
+ }
+
+ std::vector<Botan::X509_Certificate> cert_chain(
+ const std::vector<std::string>& cert_key_types,
+ 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.size() > 0)
+ {
+ for(std::string t : cert_key_types)
+ {
+ if(t == m_key->algo_name())
+ return m_cert_chain;
+ }
+ }
+
+ return {};
+ }
+
+ Botan::Private_Key* private_key_for(const Botan::X509_Certificate& /*cert*/,
+ const std::string& /*type*/,
+ const std::string& /*context*/) override
+ {
+ // assumes cert == m_cert
+ return m_key.get();
+ }
+
+ private:
+ const Shim_Arguments& m_args;
+ Botan::SymmetricKey m_psk;
+ std::string m_psk_identity;
+ std::unique_ptr<Botan::Private_Key> m_key;
+ std::vector<Botan::X509_Certificate> m_cert_chain;
+ };
+
+class Shim_Callbacks final : public Botan::TLS::Callbacks
+ {
+ public:
+ Shim_Callbacks(const Shim_Arguments& args, Shim_Socket& socket, Shim_Policy& policy) :
+ m_channel(nullptr),
+ m_args(args),
+ m_policy(policy),
+ m_socket(socket),
+ m_is_datagram(args.flag_set("dtls")),
+ m_warning_alerts(0),
+ m_empty_records(0),
+ m_sessions_established(0),
+ m_got_close(false)
+ {}
+
+ size_t sessions_established() const { return m_sessions_established; }
+
+ void set_channel(Botan::TLS::Channel* channel)
+ {
+ m_channel = channel;
+ }
+
+ bool saw_close_notify() const { return m_got_close; }
+
+ void tls_emit_data(const uint8_t data[], size_t size) override
+ {
+ shim_log("sending record of len " + std::to_string(size));
+
+ if(m_is_datagram)
+ {
+ std::vector<uint8_t> packet(size + 5);
+
+ packet[0] = 'P';
+ for(size_t i = 0; i != 4; ++i)
+ packet[i+1] = static_cast<uint8_t>((size >> (24-8*i)) & 0xFF);
+ std::memcpy(packet.data() + 5, data, size);
+
+ m_socket.write(packet.data(), packet.size());
+ }
+ else
+ {
+ m_socket.write(data, size);
+ }
+ }
+
+ std::vector<uint8_t> tls_provide_cert_status(const std::vector<Botan::X509_Certificate>&,
+ const Botan::TLS::Certificate_Status_Request&) 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");
+
+ if(m_args.flag_set("decline-ocsp-callback"))
+ return {};
+
+ if(m_args.option_used("ocsp-response"))
+ {
+ return m_args.get_b64_opt("ocsp-response");
+ }
+
+ return {};
+ }
+
+ void tls_record_received(uint64_t /*seq_no*/, const uint8_t data[], size_t size) override
+ {
+ if(size == 0)
+ {
+ m_empty_records += 1;
+ if(m_empty_records > 32)
+ shim_exit_with_error(":TOO_MANY_EMPTY_FRAGMENTS:");
+ }
+ else
+ {
+ m_empty_records = 0;
+ }
+
+ shim_log("Reflecting application_data len " + std::to_string(size));
+
+ std::vector<uint8_t> buf(data, data + size);
+ for(size_t i = 0; i != size; ++i)
+ buf[i] ^= 0xFF;
+
+ m_channel->send(buf);
+ }
+
+ bool tls_verify_message(const Botan::Public_Key& key,
+ const std::string& emsa,
+ Botan::Signature_Format format,
+ const std::vector<uint8_t>& msg,
+ const std::vector<uint8_t>& sig) override
+ {
+ if(m_args.option_used("expect-peer-signature-algorithm"))
+ {
+ const auto scheme = static_cast<Botan::TLS::Signature_Scheme>(m_args.get_int_opt("expect-peer-signature-algorithm"));
+ if(scheme != Botan::TLS::Signature_Scheme::NONE)
+ {
+ const std::string exp_emsa = Botan::TLS::padding_string_for_scheme(scheme);
+ if(emsa != exp_emsa)
+ shim_exit_with_error("Unexpected signature scheme got " + emsa + " expected " + exp_emsa);
+ }
+ }
+ return Botan::TLS::Callbacks::tls_verify_message(key, emsa, format, msg, sig);
+ }
+
+ void tls_verify_cert_chain(const std::vector<Botan::X509_Certificate>& /*cert_chain*/,
+ const std::vector<std::shared_ptr<const Botan::OCSP::Response>>& /*ocsp_responses*/,
+ const std::vector<Botan::Certificate_Store*>& /*trusted_roots*/,
+ Botan::Usage_Type /*usage*/,
+ const std::string& /*hostname*/,
+ const Botan::TLS::Policy& /*policy*/) override
+ {
+ if(m_args.flag_set("enable-ocsp-stapling") &&
+ m_args.flag_set("use-ocsp-callback") &&
+ m_args.flag_set("fail-ocsp-callback"))
+ {
+ throw Botan::TLS::TLS_Exception(Botan::TLS::Alert::BAD_CERTIFICATE_STATUS_RESPONSE,
+ "Simulated OCSP callback failure");
+ }
+
+ if(m_args.flag_set("verify-peer") && m_args.flag_set("verify-fail"))
+ {
+ throw Botan::TLS::TLS_Exception(Botan::TLS::Alert::BAD_CERTIFICATE,
+ "Test requires rejecting cert");
+ }
+ }
+
+ std::string tls_server_choose_app_protocol(const std::vector<std::string>& client_protos) override
+ {
+ if(client_protos.empty())
+ return ""; // shouldn't happen?
+
+ if(m_args.flag_set("decline-alpn"))
+ return "";
+
+ if(m_args.option_used("expect-advertised-alpn"))
+ {
+ const std::vector<std::string> expected = m_args.get_alpn_string_vec_opt("expect-advertised-alpn");
+
+ if(client_protos != expected)
+ shim_exit_with_error("Bad ALPN from client");
+ }
+
+ if(m_args.option_used("select-alpn"))
+ return m_args.get_string_opt("select-alpn");
+
+ return client_protos[0]; // if not configured just pick something
+ }
+
+ void tls_alert(Botan::TLS::Alert alert) override
+ {
+ if(alert.is_fatal())
+ shim_log("Got a fatal alert " + alert.type_string());
+ else
+ shim_log("Got a warning alert " + alert.type_string());
+
+ if(alert.type() == Botan::TLS::Alert::RECORD_OVERFLOW)
+ {
+ shim_exit_with_error(":TLSV1_ALERT_RECORD_OVERFLOW:");
+ }
+
+ if(alert.type() == Botan::TLS::Alert::DECOMPRESSION_FAILURE)
+ {
+ shim_exit_with_error(":SSLV3_ALERT_DECOMPRESSION_FAILURE:");
+ }
+
+ if(!alert.is_fatal())
+ {
+ m_warning_alerts++;
+ if(m_warning_alerts > 5)
+ shim_exit_with_error(":TOO_MANY_WARNING_ALERTS:");
+ }
+
+ if(alert.type() == Botan::TLS::Alert::CLOSE_NOTIFY)
+ {
+ if(m_got_close == false && !m_args.flag_set("shim-shuts-down"))
+ {
+ shim_log("Sending return close notify");
+ m_channel->send_alert(alert);
+ }
+ m_got_close = true;
+ }
+ else if(alert.is_fatal())
+ {
+ shim_exit_with_error("Unexpected fatal alert " + alert.type_string());
+ }
+ }
+
+ bool tls_session_established(const Botan::TLS::Session& session) override
+ {
+ shim_log("Session established: " + Botan::hex_encode(session.session_id()) +
+ " version " + session.version().to_string() +
+ " cipher " + session.ciphersuite().to_string() +
+ " EMS " + std::to_string(session.supports_extended_master_secret()));
+ // probably need tests here?
+
+ m_policy.incr_session_established();
+ m_sessions_established++;
+
+ if(m_args.flag_set("expect-no-session-id"))
+ {
+ // BoGo expects that ticket issuance implies no stateful session...
+ if(!m_args.flag_set("server") && session.session_id().size() > 0)
+ shim_exit_with_error("Unexpectedly got a session ID");
+ }
+ else if(m_args.flag_set("expect-session-id"))
+ {
+ if(session.session_id().empty())
+ shim_exit_with_error("Unexpectedly got no session ID");
+ }
+
+ if(m_args.option_used("expect-version"))
+ {
+ if(session.version().version_code() != m_args.get_int_opt("expect-version"))
+ shim_exit_with_error("Unexpected version");
+ }
+
+ if(m_args.flag_set("expect-secure-renegotiation"))
+ {
+ if(m_channel->secure_renegotiation_supported() == false)
+ 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_args.flag_set("expect-extended-master-secret"))
+ {
+ if(session.supports_extended_master_secret() == false)
+ shim_exit_with_error("Expected extended maseter secret");
+ }
+
+ return true;
+ }
+
+ void tls_session_activated() override
+ {
+ if(m_args.flag_set("send-alert"))
+ {
+ m_channel->send_fatal_alert(Botan::TLS::Alert::DECOMPRESSION_FAILURE);
+ return;
+ }
+
+ if(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);
+ shim_log("Sending " + std::to_string(length) + " bytes of key material");
+ m_channel->send(exported.bits_of());
+ }
+
+ const std::string alpn = m_channel->application_protocol();
+
+ if(m_args.option_used("expect-alpn"))
+ {
+ if(alpn != m_args.get_string_opt("expect-alpn"))
+ shim_exit_with_error("Got unexpected ALPN");
+ }
+
+ if(alpn == "baz" && !m_args.flag_set("allow-unknown-alpn-protos"))
+ {
+ throw Botan::TLS::TLS_Exception(Botan::TLS::Alert::ILLEGAL_PARAMETER,
+ "Unexpected ALPN protocol");
+ }
+
+ if(m_args.flag_set("shim-shuts-down"))
+ {
+ shim_log("Shim shutting down");
+ m_channel->close();
+ }
+
+ if(m_args.flag_set("write-different-record-sizes"))
+ {
+ static const size_t record_sizes[] = {
+ 0, 1, 255, 256, 257, 16383, 16384, 16385, 32767, 32768, 32769
+ };
+
+ std::vector<uint8_t> buf(32769, 0x42);
+
+ for(size_t sz : record_sizes)
+ {
+ m_channel->send(buf.data(), sz);
+ }
+
+ m_channel->close();
+ }
+ }
+
+ private:
+ Botan::TLS::Channel* m_channel;
+ const Shim_Arguments& m_args;
+ Shim_Policy& m_policy;
+ Shim_Socket& m_socket;
+ const bool m_is_datagram;
+ size_t m_warning_alerts;
+ size_t m_empty_records;
+ size_t m_sessions_established;
+ bool m_got_close;
+ };
+
+}
+
+int main(int /*argc*/, char* argv[])
+ {
+ try
+ {
+ std::unique_ptr<Shim_Arguments> args = parse_options(argv);
+
+ if(args->flag_set("is-handshaker-supported"))
+ {
+ return shim_output("No\n");
+ }
+
+ const uint16_t port = static_cast<uint16_t>(args->get_int_opt("port"));
+ const size_t resume_count = args->get_int_opt_or_else("resume-count", 0);
+ const bool is_server = args->flag_set("server");
+ const bool is_datagram = args->flag_set("dtls");
+
+ const size_t buf_size = args->get_int_opt_or_else("read-size", 18*1024);
+
+ Botan::ChaCha_RNG rng(Botan::secure_vector<uint8_t>(64));
+ Botan::TLS::Session_Manager_In_Memory session_manager(rng, 1024);
+ Shim_Credentials creds(*args);
+
+ for(size_t i = 0; i != resume_count+1; ++i)
+ {
+ Shim_Socket socket("localhost", port);
+
+ shim_log("Connection " + std::to_string(i+1) + "/" + std::to_string(resume_count+1));
+
+ Shim_Policy policy(*args);
+ Shim_Callbacks callbacks(*args, socket, policy);
+
+ std::unique_ptr<Botan::TLS::Channel> chan;
+
+ if(is_server)
+ {
+ chan.reset(new Botan::TLS::Server(callbacks, session_manager, creds, policy, rng, is_datagram));
+ }
+ else
+ {
+ 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", "localhost");
+ if(args->test_name().find("UnsolicitedServerNameAck-TLS1") == 0)
+ host_name = ""; // avoid sending SNI for this test
+
+ Botan::TLS::Server_Information server_info(host_name, port);
+ const std::vector<std::string> next_protocols = args->get_alpn_string_vec_opt("advertise-alpn");
+ chan.reset(new Botan::TLS::Client(callbacks, session_manager, creds, policy, rng,
+ server_info, offer_version, next_protocols));
+ }
+
+ callbacks.set_channel(chan.get());
+
+ std::vector<uint8_t> buf(buf_size);
+
+ for(;;)
+ {
+ if(is_datagram)
+ {
+ uint8_t opcode;
+ size_t got = socket.read(&opcode, 1);
+ if(got == 0)
+ {
+ shim_log("EOF on socket");
+ break;
+ }
+
+ if(opcode == 'P')
+ {
+ uint8_t len_bytes[4];
+ socket.read_exactly(len_bytes, sizeof(len_bytes));
+
+ size_t packet_len = Botan::load_be<uint32_t>(len_bytes, 0);
+
+ if(buf.size() < packet_len)
+ buf.resize(packet_len);
+ socket.read_exactly(buf.data(), packet_len);
+
+ chan->received_data(buf.data(), packet_len);
+ }
+ else if(opcode == 'T')
+ {
+ uint8_t timeout_ack = 't';
+
+ uint8_t timeout_bytes[8];
+ socket.read_exactly(timeout_bytes, sizeof(timeout_bytes));
+
+ const uint64_t nsec = Botan::load_be<uint64_t>(timeout_bytes, 0);
+
+ shim_log("Timeout nsec " + std::to_string(nsec));
+
+ // FIXME handle this!
+
+ socket.write(&timeout_ack, 1); // ack it anyway
+ }
+ else
+ shim_exit_with_error("Unknown opcode " + std::to_string(opcode));
+ }
+ else
+ {
+ size_t got = socket.read(buf.data(), buf.size());
+ if(got == 0)
+ {
+ shim_log("EOF on socket");
+ break;
+ }
+
+ shim_log("Got packet of " + std::to_string(got));
+
+ if(args->flag_set("use-exporter-between-reads") && chan->is_active())
+ {
+ chan->key_material_export("some label", "some context", 42);
+ }
+ const size_t needed = chan->received_data(buf.data(), got);
+
+ if(needed)
+ shim_log("Short read still need " + std::to_string(needed));
+ }
+ }
+
+ if(args->flag_set("check-close-notify"))
+ {
+ if(!callbacks.saw_close_notify())
+ throw Shim_Exception("Unexpected SSL_shutdown result: -1 != 1");
+ }
+
+ if(args->option_used("expect-total-renegotiations"))
+ {
+ const size_t exp = args->get_int_opt("expect-total-renegotiations");
+
+ if(exp != callbacks.sessions_established() - 1)
+ throw Shim_Exception("Unexpected number of renegotiations: saw " +
+ std::to_string(callbacks.sessions_established() - 1) +
+ " exp " + std::to_string(exp));
+ }
+ shim_log("End of resume loop");
+ }
+
+ }
+ catch(Shim_Exception& e)
+ {
+ shim_exit_with_error(e.what(), e.rc());
+ }
+ catch(std::exception& e)
+ {
+ shim_exit_with_error(map_to_bogo_error(e.what()));
+ }
+ catch(...)
+ {
+ shim_exit_with_error("Unknown exception", 3);
+ }
+ return 0;
+ }
diff --git a/comm/third_party/botan/src/bogo_shim/config.json b/comm/third_party/botan/src/bogo_shim/config.json
new file mode 100644
index 0000000000..24295a868a
--- /dev/null
+++ b/comm/third_party/botan/src/bogo_shim/config.json
@@ -0,0 +1,129 @@
+{
+ "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"
+ },
+
+ "DisabledTests": {
+ "*KeyUpdate*": "No TLS 1.3",
+ "*TLS13*": "No TLS 1.3",
+ "Server-JDK11*": "No TLS 1.3",
+ "*Binder*": "No TLS 1.3",
+ "PartialEncryptedExtensionsWithServerHello": "No TLS 1.3",
+ "Client-RejectJDK11DowngradeRandom": "No TLS 1.3",
+ "FragmentedClientVersion": "No TLS 1.3",
+ "NoExportEarlyKeyingMaterial*": "No TLS 1.3",
+ "EarlyDataEnabled*": "No TLS 1.3",
+ "DelegatedCredentials*": "No TLS 1.3",
+ "ExportTrafficSecrets-*": "No TLS 1.3",
+ "IgnoreClientVersionOrder": "No TLS 1.3",
+ "Resume-Server-OmitPSKsOnSecondClientHello": "No TLS 1.3",
+ "Http*": "No support for HTTP detection",
+
+ "DuplicateCertCompressionExt*": "No support for 1.3 cert compression extension",
+
+ "SupportedVersionSelection-TLS12": "We just ignore the version extension in this case",
+
+ "Downgrade-*-Client-Ignore": "Not possible to ignore downgrade indicator",
+ "Downgrade-TLS12-*": "Not a downgrade when we don't support v1.3",
+
+ "*SSL3*": "No SSLv3",
+ "*SSLv3*": "No SSLv3",
+
+ "*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-TLS*": "No support for SCT",
+
+ "*NULL-SHA*": "No support for NULL ciphers",
+ "*WITH_NULL*": "No support for NULL ciphers",
+ "*GREASE*": "No support for GREASE",
+ "QUICTransportParams*": "No support for QUIC",
+ "*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",
+ "Http*": "Stack does not have detection logic for HTTP",
+ "*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",
+
+ "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",
+
+ "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": "BoGo expects resumption attempt sends latest version",
+ "Resume-Client-NoResume-TLS1-TLS12": "BoGo expects resumption attempt sends latest version",
+ "Resume-Client-NoResume-TLS11-TLS12": "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": "BoGo expects resumption attempt sends latest version",
+ "Resume-Client-Mismatch-TLS1-TLS12": "BoGo expects resumption attempt sends latest version",
+ "Resume-Client-Mismatch-TLS11-TLS12": "BoGo expects resumption attempt sends latest version",
+ "Resume-Client-Mismatch-TLS1-TLS12-DTLS": "BoGo expects resumption attempt sends latest version",
+
+ "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-*-UnenforcedTLS*": "We always enforce key usage"
+ }
+}
diff --git a/comm/third_party/botan/src/build-data/arch/alpha.txt b/comm/third_party/botan/src/build-data/arch/alpha.txt
new file mode 100644
index 0000000000..c251cbee9c
--- /dev/null
+++ b/comm/third_party/botan/src/build-data/arch/alpha.txt
@@ -0,0 +1,7 @@
+endian little
+wordsize 64
+
+<aliases>
+axp
+alphaaxp
+</aliases>
diff --git a/comm/third_party/botan/src/build-data/arch/arm32.txt b/comm/third_party/botan/src/build-data/arch/arm32.txt
new file mode 100644
index 0000000000..6e2f70bcf9
--- /dev/null
+++ b/comm/third_party/botan/src/build-data/arch/arm32.txt
@@ -0,0 +1,21 @@
+endian little
+family arm
+
+<aliases>
+arm
+armeb
+armel # For Debian
+armhf # For Debian
+evbarm # For NetBSD
+
+armv7
+armv7l
+armv7a
+armv7-a
+
+armv8l # For AlpineLinux
+</aliases>
+
+<isa_extensions>
+neon
+</isa_extensions>
diff --git a/comm/third_party/botan/src/build-data/arch/arm64.txt b/comm/third_party/botan/src/build-data/arch/arm64.txt
new file mode 100644
index 0000000000..5205295125
--- /dev/null
+++ b/comm/third_party/botan/src/build-data/arch/arm64.txt
@@ -0,0 +1,20 @@
+endian little
+wordsize 64
+
+family arm
+
+<aliases>
+aarch64
+aarch64_be
+armv8
+armv8-a
+</aliases>
+
+<isa_extensions>
+neon
+armv8crypto
+armv8sm3
+armv8sm4
+armv8sha3
+armv8sha512
+</isa_extensions>
diff --git a/comm/third_party/botan/src/build-data/arch/generic.txt b/comm/third_party/botan/src/build-data/arch/generic.txt
new file mode 100644
index 0000000000..0b5e7e45c8
--- /dev/null
+++ b/comm/third_party/botan/src/build-data/arch/generic.txt
@@ -0,0 +1,4 @@
+
+# This target can be used when building an amalgamation which must
+# be built on multiple architectures, or when targetting a CPU
+# which the build system doesn't know about.
diff --git a/comm/third_party/botan/src/build-data/arch/hppa.txt b/comm/third_party/botan/src/build-data/arch/hppa.txt
new file mode 100644
index 0000000000..8828126b65
--- /dev/null
+++ b/comm/third_party/botan/src/build-data/arch/hppa.txt
@@ -0,0 +1,8 @@
+<aliases>
+hp-pa
+parisc
+parisc64
+pa-risc
+hp-parisc
+hp-pa-risc
+</aliases>
diff --git a/comm/third_party/botan/src/build-data/arch/ia64.txt b/comm/third_party/botan/src/build-data/arch/ia64.txt
new file mode 100644
index 0000000000..8a448ff881
--- /dev/null
+++ b/comm/third_party/botan/src/build-data/arch/ia64.txt
@@ -0,0 +1,6 @@
+wordsize 64
+
+<aliases>
+itanium
+itanic
+</aliases>
diff --git a/comm/third_party/botan/src/build-data/arch/llvm.txt b/comm/third_party/botan/src/build-data/arch/llvm.txt
new file mode 100644
index 0000000000..3b8c13ffd5
--- /dev/null
+++ b/comm/third_party/botan/src/build-data/arch/llvm.txt
@@ -0,0 +1 @@
+wordsize 64
diff --git a/comm/third_party/botan/src/build-data/arch/m68k.txt b/comm/third_party/botan/src/build-data/arch/m68k.txt
new file mode 100644
index 0000000000..f171f4534f
--- /dev/null
+++ b/comm/third_party/botan/src/build-data/arch/m68k.txt
@@ -0,0 +1,6 @@
+endian big
+
+<aliases>
+680x0
+68k
+</aliases>
diff --git a/comm/third_party/botan/src/build-data/arch/mips32.txt b/comm/third_party/botan/src/build-data/arch/mips32.txt
new file mode 100644
index 0000000000..d9849e8484
--- /dev/null
+++ b/comm/third_party/botan/src/build-data/arch/mips32.txt
@@ -0,0 +1,6 @@
+<aliases>
+mips
+mipsbe # RedHat
+mipsle # RedHat
+mipsel # Debian
+</aliases>
diff --git a/comm/third_party/botan/src/build-data/arch/mips64.txt b/comm/third_party/botan/src/build-data/arch/mips64.txt
new file mode 100644
index 0000000000..6d67128ede
--- /dev/null
+++ b/comm/third_party/botan/src/build-data/arch/mips64.txt
@@ -0,0 +1,5 @@
+wordsize 64
+
+<aliases>
+mips64el
+</aliases>
diff --git a/comm/third_party/botan/src/build-data/arch/powerpcspe.txt b/comm/third_party/botan/src/build-data/arch/powerpcspe.txt
new file mode 100644
index 0000000000..37d3b3c0f6
--- /dev/null
+++ b/comm/third_party/botan/src/build-data/arch/powerpcspe.txt
@@ -0,0 +1,3 @@
+endian big
+
+family ppc
diff --git a/comm/third_party/botan/src/build-data/arch/ppc32.txt b/comm/third_party/botan/src/build-data/arch/ppc32.txt
new file mode 100644
index 0000000000..da8b7654ad
--- /dev/null
+++ b/comm/third_party/botan/src/build-data/arch/ppc32.txt
@@ -0,0 +1,12 @@
+endian big
+
+family ppc
+
+<aliases>
+powerpc
+ppc
+</aliases>
+
+<isa_extensions>
+altivec
+</isa_extensions>
diff --git a/comm/third_party/botan/src/build-data/arch/ppc64.txt b/comm/third_party/botan/src/build-data/arch/ppc64.txt
new file mode 100644
index 0000000000..4b871e85e1
--- /dev/null
+++ b/comm/third_party/botan/src/build-data/arch/ppc64.txt
@@ -0,0 +1,17 @@
+endian big
+
+family ppc
+wordsize 64
+
+<aliases>
+powerpc64
+powerpc64le
+ppc64le
+ppc64el
+</aliases>
+
+<isa_extensions>
+altivec
+powercrypto
+power9
+</isa_extensions>
diff --git a/comm/third_party/botan/src/build-data/arch/riscv32.txt b/comm/third_party/botan/src/build-data/arch/riscv32.txt
new file mode 100644
index 0000000000..c8c258a00e
--- /dev/null
+++ b/comm/third_party/botan/src/build-data/arch/riscv32.txt
@@ -0,0 +1,2 @@
+family riscv
+endian little
diff --git a/comm/third_party/botan/src/build-data/arch/riscv64.txt b/comm/third_party/botan/src/build-data/arch/riscv64.txt
new file mode 100644
index 0000000000..8aa90eddf3
--- /dev/null
+++ b/comm/third_party/botan/src/build-data/arch/riscv64.txt
@@ -0,0 +1,3 @@
+family riscv
+endian little
+wordsize 64
diff --git a/comm/third_party/botan/src/build-data/arch/s390.txt b/comm/third_party/botan/src/build-data/arch/s390.txt
new file mode 100644
index 0000000000..64a1abdd37
--- /dev/null
+++ b/comm/third_party/botan/src/build-data/arch/s390.txt
@@ -0,0 +1 @@
+endian big
diff --git a/comm/third_party/botan/src/build-data/arch/s390x.txt b/comm/third_party/botan/src/build-data/arch/s390x.txt
new file mode 100644
index 0000000000..eb6a87d69b
--- /dev/null
+++ b/comm/third_party/botan/src/build-data/arch/s390x.txt
@@ -0,0 +1,2 @@
+endian big
+wordsize 64
diff --git a/comm/third_party/botan/src/build-data/arch/sparc32.txt b/comm/third_party/botan/src/build-data/arch/sparc32.txt
new file mode 100644
index 0000000000..0680fdfc3d
--- /dev/null
+++ b/comm/third_party/botan/src/build-data/arch/sparc32.txt
@@ -0,0 +1,7 @@
+endian big
+
+family sparc
+
+<aliases>
+sparc
+</aliases>
diff --git a/comm/third_party/botan/src/build-data/arch/sparc64.txt b/comm/third_party/botan/src/build-data/arch/sparc64.txt
new file mode 100644
index 0000000000..b01c8277fd
--- /dev/null
+++ b/comm/third_party/botan/src/build-data/arch/sparc64.txt
@@ -0,0 +1,3 @@
+family sparc
+wordsize 64
+endian big
diff --git a/comm/third_party/botan/src/build-data/arch/superh.txt b/comm/third_party/botan/src/build-data/arch/superh.txt
new file mode 100644
index 0000000000..6af6dbe682
--- /dev/null
+++ b/comm/third_party/botan/src/build-data/arch/superh.txt
@@ -0,0 +1,4 @@
+
+<aliases>
+sh4
+</aliases>
diff --git a/comm/third_party/botan/src/build-data/arch/x32.txt b/comm/third_party/botan/src/build-data/arch/x32.txt
new file mode 100644
index 0000000000..d69e1247d7
--- /dev/null
+++ b/comm/third_party/botan/src/build-data/arch/x32.txt
@@ -0,0 +1,16 @@
+endian little
+
+family x86
+
+<isa_extensions>
+aesni
+avx2
+bmi2
+rdrand
+rdseed
+sha
+sse2
+sse41
+sse42
+ssse3
+</isa_extensions>
diff --git a/comm/third_party/botan/src/build-data/arch/x86_32.txt b/comm/third_party/botan/src/build-data/arch/x86_32.txt
new file mode 100644
index 0000000000..cd4ede4e9a
--- /dev/null
+++ b/comm/third_party/botan/src/build-data/arch/x86_32.txt
@@ -0,0 +1,32 @@
+endian little
+
+family x86
+
+<aliases>
+ia32
+x86
+ix86
+80x86
+i86pc # for Solaris
+x86pc # for QNX
+bepc # for Haiku
+
+i686-at386 # for Hurd
+
+i686
+i586
+i386
+</aliases>
+
+<isa_extensions>
+aesni
+avx2
+bmi2
+rdrand
+rdseed
+sha
+sse2
+sse41
+sse42
+ssse3
+</isa_extensions>
diff --git a/comm/third_party/botan/src/build-data/arch/x86_64.txt b/comm/third_party/botan/src/build-data/arch/x86_64.txt
new file mode 100644
index 0000000000..729363e6fa
--- /dev/null
+++ b/comm/third_party/botan/src/build-data/arch/x86_64.txt
@@ -0,0 +1,25 @@
+endian little
+wordsize 64
+
+family x86
+
+<aliases>
+amd64
+x86-64
+em64t
+x64
+x86_amd64
+</aliases>
+
+<isa_extensions>
+aesni
+avx2
+bmi2
+rdrand
+rdseed
+sha
+sse2
+sse41
+sse42
+ssse3
+</isa_extensions>
diff --git a/comm/third_party/botan/src/build-data/bakefile.in b/comm/third_party/botan/src/build-data/bakefile.in
new file mode 100644
index 0000000000..a1c0ff134e
--- /dev/null
+++ b/comm/third_party/botan/src/build-data/bakefile.in
@@ -0,0 +1,51 @@
+toolsets = vs2013;
+shared-library botan {
+ defines = "BOTAN_DLL=__declspec(dllexport)";
+ sources {
+%{for lib_srcs}
+ %{i}
+%{endfor}
+ }
+}
+
+program cli {
+ deps = botan;
+ sources {
+%{for cli_srcs}
+ %{i}
+%{endfor}
+ }
+
+ headers {
+%{for cli_headers}
+ %{i}
+%{endfor}
+ }
+
+}
+
+program tests {
+ deps = botan;
+ sources {
+%{for test_srcs}
+ %{i}
+%{endfor}
+ }
+}
+
+includedirs += build/include/;
+includedirs += build/include/external;
+
+%{for libs_used}
+libs += "%{i}";
+%{endfor}
+
+archs = %{bakefile_arch};
+
+vs2013.option.ClCompile.DisableSpecificWarnings = "4250;4251;4275";
+vs2013.option.ClCompile.WarningLevel = Level4;
+vs2013.option.ClCompile.ExceptionHandling = SyncCThrow;
+vs2013.option.ClCompile.RuntimeTypeInfo = true;
+if ( $(config) == Release ) {
+ vs2013.option.Configuration.WholeProgramOptimization = true;
+}
diff --git a/comm/third_party/botan/src/build-data/botan.doxy.in b/comm/third_party/botan/src/build-data/botan.doxy.in
new file mode 100644
index 0000000000..0fb801f55a
--- /dev/null
+++ b/comm/third_party/botan/src/build-data/botan.doxy.in
@@ -0,0 +1,226 @@
+# Doxyfile 1.5.4
+
+PROJECT_NAME = Botan
+PROJECT_NUMBER = %{version}
+PROJECT_BRIEF = Crypto&nbsp;and&nbsp;TLS&nbsp;for&nbsp;C&#43;&#43;11
+OUTPUT_DIRECTORY = %{doc_output_dir}/doxygen
+DOXYFILE_ENCODING = UTF-8
+CREATE_SUBDIRS = NO
+OUTPUT_LANGUAGE = English
+BRIEF_MEMBER_DESC = YES
+REPEAT_BRIEF = YES
+ABBREVIATE_BRIEF =
+ALWAYS_DETAILED_SEC = NO
+INLINE_INHERITED_MEMB = YES
+FULL_PATH_NAMES = YES
+STRIP_FROM_PATH =
+STRIP_FROM_INC_PATH =
+SHORT_NAMES = NO
+JAVADOC_AUTOBRIEF = NO
+QT_AUTOBRIEF = NO
+MULTILINE_CPP_IS_BRIEF = NO
+INHERIT_DOCS = YES
+SEPARATE_MEMBER_PAGES = NO
+TAB_SIZE = 8
+ALIASES =
+OPTIMIZE_OUTPUT_FOR_C = NO
+OPTIMIZE_OUTPUT_JAVA = NO
+BUILTIN_STL_SUPPORT = NO
+CPP_CLI_SUPPORT = NO
+SIP_SUPPORT = NO
+DISTRIBUTE_GROUP_DOC = NO
+SUBGROUPING = YES
+TYPEDEF_HIDES_STRUCT = NO
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# Set this to NO to get warnings about undocumented members/classes
+EXTRACT_ALL = YES
+EXTRACT_PRIVATE = NO
+EXTRACT_STATIC = NO
+EXTRACT_LOCAL_CLASSES = NO
+EXTRACT_LOCAL_METHODS = NO
+EXTRACT_ANON_NSPACES = NO
+HIDE_UNDOC_MEMBERS = NO
+HIDE_UNDOC_CLASSES = NO
+HIDE_FRIEND_COMPOUNDS = NO
+HIDE_IN_BODY_DOCS = NO
+INTERNAL_DOCS = NO
+CASE_SENSE_NAMES = YES
+HIDE_SCOPE_NAMES = NO
+SHOW_INCLUDE_FILES = YES
+INLINE_INFO = YES
+SORT_MEMBER_DOCS = YES
+SORT_BRIEF_DOCS = YES
+SORT_BY_SCOPE_NAME = NO
+GENERATE_TODOLIST = YES
+GENERATE_TESTLIST = YES
+GENERATE_BUGLIST = YES
+GENERATE_DEPRECATEDLIST= YES
+ENABLED_SECTIONS =
+MAX_INITIALIZER_LINES = 30
+SHOW_USED_FILES = YES
+FILE_VERSION_FILTER =
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+QUIET = YES
+WARNINGS = YES
+WARN_IF_UNDOCUMENTED = YES
+WARN_IF_DOC_ERROR = YES
+WARN_NO_PARAMDOC = YES
+WARN_FORMAT = "$file:$line: $text"
+WARN_LOGFILE =
+
+%{if maintainer_mode}
+WARN_AS_ERROR = YES
+%{endif}
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+INPUT = %{src_dir}/lib
+INPUT_ENCODING = UTF-8
+FILE_PATTERNS =
+RECURSIVE = YES
+EXCLUDE =
+EXCLUDE_SYMLINKS = NO
+EXCLUDE_PATTERNS = */wrap/*
+EXCLUDE_SYMBOLS =
+EXAMPLE_PATH =
+EXAMPLE_PATTERNS =
+EXAMPLE_RECURSIVE = NO
+IMAGE_PATH =
+INPUT_FILTER =
+FILTER_PATTERNS =
+FILTER_SOURCE_FILES = NO
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+SOURCE_BROWSER = YES
+INLINE_SOURCES = YES
+STRIP_CODE_COMMENTS = NO
+REFERENCED_BY_RELATION = YES
+REFERENCES_RELATION = YES
+REFERENCES_LINK_SOURCE = YES
+USE_HTAGS = NO
+VERBATIM_HEADERS = YES
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+ALPHABETICAL_INDEX = YES
+COLS_IN_ALPHA_INDEX = 5
+IGNORE_PREFIX =
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+GENERATE_HTML = YES
+HTML_OUTPUT = .
+HTML_FILE_EXTENSION = .html
+HTML_HEADER =
+HTML_FOOTER =
+HTML_STYLESHEET =
+GENERATE_HTMLHELP = NO
+HTML_DYNAMIC_SECTIONS = NO
+CHM_FILE =
+HHC_LOCATION =
+GENERATE_CHI = NO
+BINARY_TOC = NO
+TOC_EXPAND = NO
+DISABLE_INDEX = NO
+ENUM_VALUES_PER_LINE = 4
+GENERATE_TREEVIEW = NO
+TREEVIEW_WIDTH = 250
+
+#---------------------------------------------------------------------------
+# Configuration options related to other output formats
+#---------------------------------------------------------------------------
+GENERATE_LATEX = NO
+GENERATE_MAN = NO
+GENERATE_RTF = NO
+GENERATE_XML = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+ENABLE_PREPROCESSING = YES
+MACRO_EXPANSION = YES
+EXPAND_ONLY_PREDEF = NO
+SEARCH_INCLUDES = YES
+INCLUDE_PATH =
+INCLUDE_FILE_PATTERNS =
+PREDEFINED = BOTAN_HAS_AES_ARMV8 \
+ BOTAN_HAS_AES_NI \
+ BOTAN_HAS_AES_POWER8 \
+ BOTAN_HAS_AES_VPERM \
+ BOTAN_HAS_CHACHA_SIMD32 \
+ BOTAN_HAS_CHACHA_AVX2 \
+ BOTAN_HAS_IDEA_SSE2 \
+ BOTAN_HAS_NOEKEON_SIMD \
+ BOTAN_HAS_SERPENT_SIMD \
+ BOTAN_HAS_SERPENT_AVX2 \
+ BOTAN_HAS_SHA1_SSE2 \
+ BOTAN_HAS_SHA2_32_X86 \
+ BOTAN_HAS_SHA2_32_X86_BMI2 \
+ BOTAN_HAS_SHA2_64_BMI2 \
+ BOTAN_HAS_SHA3_BMI2 \
+ BOTAN_HAS_SHACAL2_SIMD \
+ BOTAN_HAS_SHACAL2_AVX2 \
+ BOTAN_HAS_SHACAL2_X86 \
+ BOTAN_HAS_SM4_ARMV8 \
+ BOTAN_HAS_THREEFISH_512_AVX2 \
+ BOTAN_HAS_GHASH_CLMUL_CPU \
+ BOTAN_HAS_GHASH_CLMUL_VPERM \
+ BOTAN_DEPRECATED(msg)= \
+ BOTAN_DEPRECATED_API(msg)= \
+ BOTAN_PUBLIC_API(maj,min)= \
+ BOTAN_HAS_TLS \
+ BOTAN_HAS_BOOST_ASIO \
+ BOOST_VERSION=106600
+
+
+EXPAND_AS_DEFINED =
+SKIP_FUNCTION_MACROS = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references
+#---------------------------------------------------------------------------
+TAGFILES =
+GENERATE_TAGFILE =
+ALLEXTERNALS = NO
+EXTERNAL_GROUPS = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+CLASS_DIAGRAMS = YES
+HIDE_UNDOC_RELATIONS = YES
+HAVE_DOT = NO
+CLASS_GRAPH = YES
+COLLABORATION_GRAPH = YES
+GROUP_GRAPHS = YES
+UML_LOOK = NO
+TEMPLATE_RELATIONS = NO
+INCLUDE_GRAPH = YES
+INCLUDED_BY_GRAPH = YES
+CALL_GRAPH = NO
+CALLER_GRAPH = NO
+GRAPHICAL_HIERARCHY = YES
+DIRECTORY_GRAPH = YES
+DOT_IMAGE_FORMAT = png
+DOT_PATH =
+DOTFILE_DIRS =
+DOT_GRAPH_MAX_NODES = 50
+MAX_DOT_GRAPH_DEPTH = 0
+DOT_TRANSPARENT = YES
+DOT_MULTI_TARGETS = NO
+GENERATE_LEGEND = YES
+DOT_CLEANUP = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to the search engine
+#---------------------------------------------------------------------------
+SEARCHENGINE = NO
diff --git a/comm/third_party/botan/src/build-data/botan.pc.in b/comm/third_party/botan/src/build-data/botan.pc.in
new file mode 100644
index 0000000000..7139436f93
--- /dev/null
+++ b/comm/third_party/botan/src/build-data/botan.pc.in
@@ -0,0 +1,12 @@
+prefix=%{prefix}
+exec_prefix=${prefix}
+libdir=%{libdir}
+includedir=${prefix}/include/botan-%{version_major}
+
+Name: Botan
+Description: Crypto and TLS for C++11
+Version: %{version}
+
+Libs: -L${libdir} -l%{libname} %{cxx_abi_flags}
+Libs.private: %{link_to}
+Cflags: -I${includedir}
diff --git a/comm/third_party/botan/src/build-data/buildh.in b/comm/third_party/botan/src/build-data/buildh.in
new file mode 100644
index 0000000000..cede20ace7
--- /dev/null
+++ b/comm/third_party/botan/src/build-data/buildh.in
@@ -0,0 +1,268 @@
+#ifndef BOTAN_BUILD_CONFIG_H_
+#define BOTAN_BUILD_CONFIG_H_
+
+/*
+* 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}
+*/
+
+#define BOTAN_VERSION_MAJOR %{version_major}
+#define BOTAN_VERSION_MINOR %{version_minor}
+#define BOTAN_VERSION_PATCH %{version_patch}
+#define BOTAN_VERSION_DATESTAMP %{version_datestamp}
+
+%{if version_suffix}
+#define BOTAN_VERSION_SUFFIX %{version_suffix}
+#define BOTAN_VERSION_SUFFIX_STR "%{version_suffix}"
+%{endif}
+
+#define BOTAN_VERSION_RELEASE_TYPE "%{release_type}"
+
+#define BOTAN_VERSION_VC_REVISION "%{version_vc_rev}"
+
+#define BOTAN_DISTRIBUTION_INFO "%{distribution_info}"
+
+/* How many bits per limb in a BigInt */
+#define BOTAN_MP_WORD_BITS %{mp_bits}
+
+%{if fuzzer_mode}
+#define BOTAN_UNSAFE_FUZZER_MODE
+%{endif}
+%{if fuzzer_type}
+#define BOTAN_FUZZER_IS_%{fuzzer_type}
+%{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}
+#define BOTAN_TARGET_OS_HAS_%{i|upper}
+%{endfor}
+
+#define BOTAN_BUILD_COMPILER_IS_%{cc_macro}
+
+%{for sanitizer_types}
+#define BOTAN_HAS_SANITIZER_%{i|upper}
+%{endfor}
+
+%{if test_mode}
+#define BOTAN_TEST_MODE
+%{endif}
+
+#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_openmp}
+#define BOTAN_TARGET_HAS_OPENMP
+%{endif}
+
+%{if with_debug_asserts}
+#define BOTAN_ENABLE_DEBUG_ASSERTS
+%{endif}
+
+%{if optimize_for_size}
+#define BOTAN_OPTIMIZE_FOR_SIZE
+%{endif}
+
+/*
+* Module availability definitions
+*/
+%{for module_defines}
+#define BOTAN_HAS_%{i}
+%{endfor}
+
+/*
+* Local/misc configuration options (if any) follow
+*/
+%{local_config}
+
+/*
+* Things you can edit (but probably shouldn't)
+*/
+
+#if !defined(BOTAN_DEPRECATED_PUBLIC_MEMBER_VARIABLES)
+
+ #if defined(BOTAN_NO_DEPRECATED)
+ #define BOTAN_DEPRECATED_PUBLIC_MEMBER_VARIABLES private
+ #else
+ #define BOTAN_DEPRECATED_PUBLIC_MEMBER_VARIABLES public
+ #endif
+
+#endif
+
+/* How much to allocate for a buffer of no particular size */
+#define BOTAN_DEFAULT_BUFFER_SIZE 1024
+
+/*
+* Total maximum amount of RAM (in KiB) we will lock into memory, even
+* if the OS would let us lock more
+*/
+#define BOTAN_MLOCK_ALLOCATOR_MAX_LOCKED_KB 512
+
+/*
+* 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.
+*/
+#if defined(BOTAN_HAS_VALGRIND) || defined(BOTAN_ENABLE_DEBUG_ASSERTS)
+ #define BOTAN_MEM_POOL_USE_MMU_PROTECTIONS
+#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
+*
+* 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
+#define BOTAN_RNG_RESEED_POLL_BITS 256
+
+#define BOTAN_RNG_AUTO_RESEED_TIMEOUT std::chrono::milliseconds(10)
+#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.
+*/
+#define BOTAN_ENTROPY_DEFAULT_SOURCES \
+ { "rdseed", "hwrng", "p9_darn", "getentropy", "dev_random", \
+ "system_rng", "proc_walk", "system_stats" }
+
+/* Multiplier on a block cipher's native parallelism */
+#define BOTAN_BLOCK_CIPHER_PAR_MULT 4
+
+/*
+* These control the RNG used by the system RNG interface
+*/
+#define BOTAN_SYSTEM_RNG_DEVICE "/dev/urandom"
+#define BOTAN_SYSTEM_RNG_POLL_DEVICES { "/dev/urandom", "/dev/random" }
+
+/*
+* This directory will be monitored by ProcWalking_EntropySource and
+* the contents provided as entropy inputs to the RNG. May also be
+* usefully set to something like "/sys", depending on the system being
+* deployed to. Set to an empty string to disable.
+*/
+#define BOTAN_ENTROPY_PROC_FS_PATH "/proc"
+
+/*
+* These paramaters control how many bytes to read from the system
+* PRNG, and how long to block if applicable. The timeout only applies
+* to reading /dev/urandom and company.
+*/
+#define BOTAN_SYSTEM_RNG_POLL_REQUEST 64
+#define BOTAN_SYSTEM_RNG_POLL_TIMEOUT_MS 20
+
+/*
+* When a PBKDF is self-tuning parameters, it will attempt to take about this
+* amount of time to self-benchmark.
+*/
+#define BOTAN_PBKDF_TUNING_TIME std::chrono::milliseconds(10)
+
+/*
+* If no way of dynamically determining the cache line size for the
+* system exists, this value is used as the default. Used by the side
+* channel countermeasures rather than for alignment purposes, so it is
+* better to be on the smaller side if the exact value cannot be
+* determined. Typically 32 or 64 bytes on modern CPUs.
+*/
+#if !defined(BOTAN_TARGET_CPU_DEFAULT_CACHE_LINE_SIZE)
+ #define BOTAN_TARGET_CPU_DEFAULT_CACHE_LINE_SIZE 32
+#endif
+
+/**
+* Controls how AutoSeeded_RNG is instantiated
+*/
+#if !defined(BOTAN_AUTO_RNG_HMAC)
+
+ #if defined(BOTAN_HAS_SHA2_64)
+ #define BOTAN_AUTO_RNG_HMAC "HMAC(SHA-384)"
+ #elif defined(BOTAN_HAS_SHA2_32)
+ #define BOTAN_AUTO_RNG_HMAC "HMAC(SHA-256)"
+ #elif defined(BOTAN_HAS_SHA3)
+ #define BOTAN_AUTO_RNG_HMAC "HMAC(SHA-3(256))"
+ #elif defined(BOTAN_HAS_SHA1)
+ #define BOTAN_AUTO_RNG_HMAC "HMAC(SHA-1)"
+ #endif
+ /* Otherwise, no hash found: leave BOTAN_AUTO_RNG_HMAC undefined */
+
+#endif
+
+/* 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
+
+#include <botan/compiler.h>
+
+#endif
diff --git a/comm/third_party/botan/src/build-data/cc/clang.txt b/comm/third_party/botan/src/build-data/cc/clang.txt
new file mode 100644
index 0000000000..a9c9be87f9
--- /dev/null
+++ b/comm/third_party/botan/src/build-data/cc/clang.txt
@@ -0,0 +1,85 @@
+macro_name CLANG
+
+binary_name clang++
+
+lang_flags "-std=c++11 -D_REENTRANT"
+
+warning_flags "-Wall -Wextra -Wpedantic -Wshadow -Wstrict-aliasing -Wstrict-overflow=5 -Wcast-align -Wmissing-declarations -Wpointer-arith -Wcast-qual"
+
+werror_flags "-Werror -Wno-error=unused-parameter -Wno-error=unreachable-code -Wno-error=unused-lambda-capture"
+
+maintainer_warning_flags "-Wunreachable-code -Wdocumentation -Qunused-arguments"
+
+optimization_flags "-O3"
+sanitizer_optimization_flags "-O1 -fno-optimize-sibling-calls -fno-omit-frame-pointer"
+size_optimization_flags "-Os"
+
+add_sysroot_option "--sysroot="
+
+<sanitizers>
+default -> address,undefined
+
+address -> "-fsanitize=address"
+undefined -> "-fsanitize=undefined -fno-sanitize-recover=undefined"
+coverage -> "-fsanitize=fuzzer-no-link"
+memory -> "-fsanitize=memory"
+</sanitizers>
+
+shared_flags "-fPIC"
+coverage_flags "--coverage"
+stack_protector_flags "-fstack-protector"
+
+visibility_build_flags "-fvisibility=hidden"
+visibility_attribute '__attribute__((visibility("default")))'
+
+<so_link_commands>
+macos -> "$(CXX) -dynamiclib -fPIC -install_name $(INSTALLED_LIB_DIR)/{soname_abi} -current_version {macos_so_current_ver} -compatibility_version {macos_so_compat_ver}"
+
+# The default works for GNU ld and several other Unix linkers
+default -> "$(CXX) -shared -fPIC -Wl,-soname,{soname_abi}"
+</so_link_commands>
+
+<binary_link_commands>
+default -> "$(LINKER)"
+llvm -> "llvm-link"
+emscripten -> "em++"
+</binary_link_commands>
+
+<isa_flags>
+sse2 -> "-msse2"
+ssse3 -> "-mssse3"
+sse41 -> "-msse4.1"
+sse42 -> "-msse4.2"
+avx2 -> "-mavx2"
+bmi2 -> "-mbmi -mbmi2"
+aesni -> "-maes -mpclmul"
+rdrand -> "-mrdrnd"
+rdseed -> "-mrdseed"
+sha -> "-msha"
+altivec -> "-maltivec"
+
+ppc64:powercrypto -> "-mcrypto -mvsx"
+ppc64:power9 -> "-mcpu=power9"
+
+arm64:armv8crypto -> "-march=armv8+crypto"
+
+arm32:neon -> "-mfpu=neon"
+arm64:neon -> ""
+</isa_flags>
+
+<cpu_flags>
+llvm -> "-emit-llvm -fno-use-cxa-atexit"
+</cpu_flags>
+
+<mach_abi_linking>
+all!haiku,llvm -> "-pthread"
+
+openmp -> "-fopenmp"
+
+x86_32 -> "-m32"
+x86_64 -> "-m64"
+ppc64 -> "-m64"
+
+macos -> "-stdlib=libc++"
+ios -> "-stdlib=libc++"
+</mach_abi_linking>
diff --git a/comm/third_party/botan/src/build-data/cc/ekopath.txt b/comm/third_party/botan/src/build-data/cc/ekopath.txt
new file mode 100644
index 0000000000..490396ac43
--- /dev/null
+++ b/comm/third_party/botan/src/build-data/cc/ekopath.txt
@@ -0,0 +1,17 @@
+macro_name PATHSCALE
+
+binary_name pathCC
+
+optimization_flags "-O3"
+
+lang_flags "-D_REENTRANT -ansi -Wno-long-long"
+warning_flags "-W -Wall"
+
+ar_command pathCC
+ar_options "-ar -o"
+
+shared_flags "-fPIC"
+
+<so_link_commands>
+default -> "$(CXX) -shared -fPIC -Wl,-soname,{soname_abi}"
+</so_link_commands>
diff --git a/comm/third_party/botan/src/build-data/cc/gcc.txt b/comm/third_party/botan/src/build-data/cc/gcc.txt
new file mode 100644
index 0000000000..711c48f76c
--- /dev/null
+++ b/comm/third_party/botan/src/build-data/cc/gcc.txt
@@ -0,0 +1,99 @@
+macro_name GCC
+
+binary_name g++
+
+lang_flags "-std=c++11 -D_REENTRANT"
+
+# This should only contain flags which are included in GCC 4.8
+warning_flags "-Wall -Wextra -Wpedantic -Wstrict-aliasing -Wcast-align -Wmissing-declarations -Wpointer-arith -Wcast-qual -Wzero-as-null-pointer-constant -Wnon-virtual-dtor"
+
+# Boost headers have 0 as nullptr and non-virtual-dtor issues so we can't werror on them
+werror_flags "-Werror -Wno-error=strict-overflow -Wno-error=zero-as-null-pointer-constant -Wno-error=non-virtual-dtor"
+
+maintainer_warning_flags "-Wstrict-overflow=5 -Wold-style-cast -Wsuggest-override -Wshadow -Wextra-semi"
+
+optimization_flags "-O3"
+sanitizer_optimization_flags "-O1 -fno-optimize-sibling-calls -fno-omit-frame-pointer"
+size_optimization_flags "-Os"
+
+shared_flags "-fPIC"
+coverage_flags "--coverage"
+stack_protector_flags "-fstack-protector"
+
+add_sysroot_option "--sysroot="
+
+<sanitizers>
+default -> iterator,address
+
+iterator -> "-D_GLIBCXX_DEBUG"
+address -> "-fsanitize=address"
+undefined -> "-fsanitize=undefined -fno-sanitize-recover=undefined"
+</sanitizers>
+
+visibility_build_flags "-fvisibility=hidden"
+visibility_attribute '__attribute__((visibility("default")))'
+
+<so_link_commands>
+# The default works for GNU ld and several other Unix linkers
+default -> "$(CXX) -shared -fPIC -Wl,-soname,{soname_abi}"
+
+# macOS, HP-UX, and Solaris linkers use different syntax
+macos -> "$(CXX) -dynamiclib -fPIC -install_name $(INSTALLED_LIB_DIR)/{soname_abi}"
+hpux -> "$(CXX) -shared -fPIC -Wl,+h,{soname_abi}"
+solaris -> "$(CXX) -shared -fPIC -Wl,-h,{soname_abi}"
+
+# AIX and OpenBSD don't use sonames at all
+aix -> "$(CXX) -shared -fPIC"
+openbsd -> "$(CXX) -shared -fPIC"
+</so_link_commands>
+
+<binary_link_commands>
+default -> "$(LINKER)"
+</binary_link_commands>
+
+<isa_flags>
+sse2 -> "-msse2"
+ssse3 -> "-mssse3"
+sse41 -> "-msse4.1"
+sse42 -> "-msse4.2"
+avx2 -> "-mavx2"
+bmi2 -> "-mbmi -mbmi2"
+aesni -> "-maes -mpclmul"
+rdrand -> "-mrdrnd"
+rdseed -> "-mrdseed"
+sha -> "-msha"
+altivec -> "-maltivec"
+
+powercrypto -> "-mcrypto -mvsx"
+ppc64:power9 -> "-mcpu=power9"
+
+arm64:armv8crypto -> ""
+arm64:armv8sm3 -> "-march=armv8.2-a+sm4"
+arm64:armv8sm4 -> "-march=armv8.2-a+sm4"
+arm64:armv8sha512 -> "-march=armv8.2-a+sha3"
+arm64:armv8sha3 -> "-march=armv8.2-a+sha3"
+
+# For Aarch32 -mfpu=neon is required
+# For Aarch64 NEON is enabled by default
+arm32:neon -> "-mfpu=neon"
+arm64:neon -> ""
+</isa_flags>
+
+# Flags set here are included at compile and link time
+<mach_abi_linking>
+all!haiku,qnx,none -> "-pthread"
+
+openmp -> "-fopenmp"
+
+s390 -> "-m31"
+s390x -> "-m64"
+sparc32 -> "-m32 -mno-app-regs"
+sparc64 -> "-m64 -mno-app-regs"
+ppc64 -> "-m64"
+x86_32 -> "-m32"
+x86_64 -> "-m64"
+x32 -> "-mx32"
+
+qnx -> "-fexceptions"
+cygwin -> "-U__STRICT_ANSI__"
+</mach_abi_linking>
diff --git a/comm/third_party/botan/src/build-data/cc/hpcc.txt b/comm/third_party/botan/src/build-data/cc/hpcc.txt
new file mode 100644
index 0000000000..cbe50c37d9
--- /dev/null
+++ b/comm/third_party/botan/src/build-data/cc/hpcc.txt
@@ -0,0 +1,18 @@
+macro_name HP_ACC
+
+binary_name aCC
+
+lang_flags "-AA -ext +eh -z"
+optimization_flags "+O2"
+warning_flags "+w"
+shared_flags "+Z"
+
+<mach_abi_linking>
+hppa1.0 -> "+DAportable"
+hppa1.1 -> "+DA1.1"
+hppa2.0 -> "+DA2.0W"
+</mach_abi_linking>
+
+<so_link_commands>
+default -> "$(CXX) +Z -b -Wl,+h,{soname_abi}" # Documented in cc(1), but not CC(1) (?)
+</so_link_commands>
diff --git a/comm/third_party/botan/src/build-data/cc/icc.txt b/comm/third_party/botan/src/build-data/cc/icc.txt
new file mode 100644
index 0000000000..c8a1aa3dc7
--- /dev/null
+++ b/comm/third_party/botan/src/build-data/cc/icc.txt
@@ -0,0 +1,24 @@
+macro_name INTEL
+
+binary_name icpc
+
+optimization_flags "-O2"
+size_optimization_flags "-Os"
+
+lang_flags "-std=c++11"
+warning_flags "-w1"
+shared_flags "-fPIC"
+
+<isa_flags>
+sse2 -> "-msse2"
+ssse3 -> "-mssse3"
+sse41 -> "-msse4.1"
+sse42 -> "-msse4.2"
+avx2 -> "-march=core-avx2"
+aesni -> "-march=corei7"
+rdrand -> "-march=core-avx-i"
+</isa_flags>
+
+<so_link_commands>
+default -> "$(CXX) -fPIC -shared -Wl,-soname,{soname_abi}"
+</so_link_commands>
diff --git a/comm/third_party/botan/src/build-data/cc/msvc.txt b/comm/third_party/botan/src/build-data/cc/msvc.txt
new file mode 100644
index 0000000000..25e39d2227
--- /dev/null
+++ b/comm/third_party/botan/src/build-data/cc/msvc.txt
@@ -0,0 +1,84 @@
+macro_name MSVC
+
+binary_name cl
+linker_name link
+
+output_to_object "/Fo"
+output_to_exe "/OUT:"
+
+add_include_dir_option "/I"
+add_lib_dir_option "/LIBPATH:"
+add_compile_definition_option "/D"
+add_lib_option "%s.lib"
+
+compile_flags "/nologo /c"
+
+optimization_flags "/O2 /Oi"
+size_optimization_flags "/O1 /Os"
+
+# for debug info in the object file:
+#debug_info_flags "/Z7"
+
+# for using a PDB file:
+debug_info_flags "/Zi /FS"
+
+preproc_flags "/nologo /EP"
+
+lang_flags "/EHs /GR"
+
+# 4250: diamond inheritence warning
+# 4251: STL types used in DLL interface
+# 4275: ???
+# 4127: conditional expression is constant, consider using if constexpr
+warning_flags "/W4 /wd4250 /wd4251 /wd4275 /wd4127"
+
+werror_flags "/WX"
+
+visibility_build_flags "/DBOTAN_DLL=__declspec(dllexport)"
+visibility_attribute "__declspec(dllimport)"
+
+ar_command lib
+ar_options "/nologo"
+ar_output_to "/OUT:"
+
+<sanitizers>
+default -> iterator
+
+iterator -> "/D_ITERATOR_DEBUG_LEVEL=1"
+</sanitizers>
+
+<isa_flags>
+sse2 -> ""
+ssse3 -> ""
+sse41 -> ""
+sse42 -> ""
+x86_64:avx2 -> "/arch:AVX"
+bmi2 -> ""
+aesni -> ""
+clmul -> ""
+rdrand -> ""
+rdseed -> ""
+sha -> ""
+</isa_flags>
+
+<lib_flags>
+debug -> "/Fd%{build_dir}/%{libname}%{lib_suffix}.pdb"
+</lib_flags>
+
+<so_link_commands>
+default -> "$(LINKER) /DLL"
+default-debug -> "$(LINKER) /DLL /DEBUG"
+</so_link_commands>
+
+<binary_link_commands>
+default -> "$(LINKER)"
+default-debug -> "$(LINKER) /DEBUG"
+</binary_link_commands>
+
+<mach_abi_linking>
+all -> "/bigobj"
+
+# These can be overridden with --msvc-runtime option
+rt -> "/MD"
+rt-debug -> "/MDd"
+</mach_abi_linking>
diff --git a/comm/third_party/botan/src/build-data/cc/pgi.txt b/comm/third_party/botan/src/build-data/cc/pgi.txt
new file mode 100644
index 0000000000..811578cdca
--- /dev/null
+++ b/comm/third_party/botan/src/build-data/cc/pgi.txt
@@ -0,0 +1,15 @@
+macro_name PGI
+
+binary_name pgc++
+
+lang_flags "-std=c++11"
+
+optimization_flags "-O3"
+shared_flags "-fPIC"
+
+visibility_build_flags "-fvisibility=hidden"
+visibility_attribute '__attribute__((visibility("default")))'
+
+<so_link_commands>
+default -> "$(CXX) -shared -fPIC -Wl,-soname,{soname_abi}"
+</so_link_commands>
diff --git a/comm/third_party/botan/src/build-data/cc/sunstudio.txt b/comm/third_party/botan/src/build-data/cc/sunstudio.txt
new file mode 100644
index 0000000000..3dd8e00b02
--- /dev/null
+++ b/comm/third_party/botan/src/build-data/cc/sunstudio.txt
@@ -0,0 +1,39 @@
+macro_name SUN_STUDIO
+
+binary_name CC
+
+optimization_flags "-xO2"
+
+shared_flags "-KPIC"
+warning_flags "+w -erroff=truncwarn,wnoretvalue,wlessrestrictedthrow"
+lang_flags "-std=c++11 +p -features=extensions"
+
+ar_command CC
+ar_options "-xar -o"
+
+<so_link_commands>
+default -> "$(CXX) -G -h{soname_abi}"
+</so_link_commands>
+
+<mach_abi_linking>
+# Needed on some Linux distros
+linux -> "-library=stlport4"
+
+sparc64 -> "-m64 -xarch=sparc"
+x86_64 -> "-m64"
+</mach_abi_linking>
+
+<isa_flags>
+# 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"
+sse42 -> "-xarch=sse4.2"
+aesni -> "-xarch=aes"
+avx -> "-xarch=avx"
+rdrand -> "-xarch=avx_i"
+avx2 -> "-xarch=avx2"
+</isa_flags>
diff --git a/comm/third_party/botan/src/build-data/cc/xlc.txt b/comm/third_party/botan/src/build-data/cc/xlc.txt
new file mode 100644
index 0000000000..a54b7f91f0
--- /dev/null
+++ b/comm/third_party/botan/src/build-data/cc/xlc.txt
@@ -0,0 +1,26 @@
+macro_name XLC
+
+binary_name xlC
+
+optimization_flags "-O2"
+
+lang_flags "-std=c++11"
+
+visibility_build_flags "-fvisibility=hidden"
+visibility_attribute '__attribute__((visibility("default")))'
+
+<isa_flags>
+altivec -> "-qaltivec"
+</isa_flags>
+
+<so_link_commands>
+default -> "$(CXX) -qmkshrobj"
+</so_link_commands>
+
+<sanitizers>
+default -> address
+
+all -> "-qcheck=all"
+address -> "-qcheck=bounds:stackclobber:unset"
+undefined -> "-qcheck=nullptr:divzero"
+</sanitizers>
diff --git a/comm/third_party/botan/src/build-data/cmake.in b/comm/third_party/botan/src/build-data/cmake.in
new file mode 100644
index 0000000000..2e4ea86448
--- /dev/null
+++ b/comm/third_party/botan/src/build-data/cmake.in
@@ -0,0 +1,73 @@
+cmake_minimum_required(VERSION 2.8.0)
+project(botan)
+
+if(POLICY CMP0042)
+cmake_policy(SET CMP0042 NEW)
+endif()
+
+set(BOTAN_SOURCES
+%{for lib_srcs}
+ "%{i}"
+%{endfor}
+)
+
+set(BOTAN_CLI
+%{for cli_srcs}
+ "%{i}"
+%{endfor}
+)
+
+set(BOTAN_TESTS
+%{for test_srcs}
+ "%{i}"
+%{endfor}
+)
+
+%{for isa_build_info}
+set_source_files_properties("%{src}" PROPERTIES COMPILE_FLAGS "%{isa_flags}")
+%{endfor}
+
+option(ENABLED_OPTIONAL_WARINIGS "If enabled more strict warning policy will be used" OFF)
+option(ENABLED_LTO "If enabled link time optimization will be used" OFF)
+
+set(COMPILER_FEATURES_RELEASE %{cc_lang_flags} %{cc_compile_opt_flags} %{cxx_abi_opt_flags})
+set(COMPILER_FEATURES_DEBUG %{cc_lang_flags} %{cc_compile_debug_flags} %{cxx_abi_debug_flags})
+set(COMPILER_FEATURES $<$<NOT:$<CONFIG:DEBUG>>:${COMPILER_FEATURES_RELEASE}> $<$<CONFIG:DEBUG>:${COMPILER_FEATURES_DEBUG}>)
+set(SHARED_FEATURES %{cmake_lib_flags})
+set(STATIC_FEATURES -DBOTAN_DLL=)
+set(COMPILER_WARNINGS %{cc_warning_flags})
+set(COMPILER_INCLUDE_DIRS %{compiler_include_dirs})
+if(ENABLED_LTO)
+ set(COMPILER_FEATURES ${COMPILER_FEATURES} -lto)
+endif()
+if(ENABLED_OPTIONAL_WARINIGS)
+ set(COMPILER_OPTIONAL_WARNINGS -Wsign-promo -Wctor-dtor-privacy -Wdeprecated -Winit-self -Wnon-virtual-dtor -Wunused-macros -Wold-style-cast -Wuninitialized)
+endif()
+
+add_library(${PROJECT_NAME} STATIC ${BOTAN_SOURCES})
+target_link_libraries(${PROJECT_NAME} PUBLIC %{cmake_link_to})
+target_compile_options(${PROJECT_NAME} PUBLIC ${COMPILER_WARNINGS} ${COMPILER_FEATURES} ${COMPILER_OPTIONAL_WARNINGS} PRIVATE ${STATIC_FEATURES})
+target_include_directories(${PROJECT_NAME} PUBLIC ${COMPILER_INCLUDE_DIRS})
+
+set_target_properties(${PROJECT_NAME} PROPERTIES OUTPUT_NAME ${PROJECT_NAME}-static)
+
+add_library(${PROJECT_NAME}_shared SHARED ${BOTAN_SOURCES})
+target_link_libraries(${PROJECT_NAME}_shared PUBLIC %{cmake_link_to})
+target_compile_options(${PROJECT_NAME}_shared PUBLIC ${COMPILER_WARNINGS} ${COMPILER_FEATURES} ${COMPILER_OPTIONAL_WARNINGS} PRIVATE ${SHARED_FEATURES})
+target_include_directories(${PROJECT_NAME}_shared PUBLIC ${COMPILER_INCLUDE_DIRS})
+set_target_properties(${PROJECT_NAME}_shared PROPERTIES OUTPUT_NAME ${PROJECT_NAME})
+
+add_executable(${PROJECT_NAME}_cli ${BOTAN_CLI})
+target_link_libraries(${PROJECT_NAME}_cli PRIVATE ${PROJECT_NAME}_shared )
+set_target_properties(${PROJECT_NAME}_cli PROPERTIES OUTPUT_NAME ${PROJECT_NAME}-cli)
+
+add_executable(${PROJECT_NAME}_tests ${BOTAN_TESTS})
+target_link_libraries(${PROJECT_NAME}_tests PRIVATE ${PROJECT_NAME}_shared )
+set_target_properties(${PROJECT_NAME}_tests PROPERTIES OUTPUT_NAME botan-test)
+
+set(GLOBAL_CONFIGURATION_FILES configure.py .gitignore news.rst readme.rst)
+file(GLOB_RECURSE CONFIGURATION_FILES src/configs/* )
+file(GLOB_RECURSE DOCUMENTATION_FILES doc/* )
+file(GLOB_RECURSE HEADER_FILES src/*.h )
+file(GLOB_RECURSE INFO_FILES src/lib/*info.txt )
+add_custom_target(CONFIGURATION_DUMMY SOURCES ${GLOBAL_CONFIGURATION_FILES} ${CONFIGURATION_FILES} ${DOCUMENTATION_FILES} ${INFO_FILES} ${HEADER_FILES})
diff --git a/comm/third_party/botan/src/build-data/detect_arch.cpp b/comm/third_party/botan/src/build-data/detect_arch.cpp
new file mode 100644
index 0000000000..4de58922fe
--- /dev/null
+++ b/comm/third_party/botan/src/build-data/detect_arch.cpp
@@ -0,0 +1,76 @@
+
+#if defined(__x86_64__) && defined(__ILP32__)
+ X32
+
+#elif defined(__x86_64__) || defined(_M_X64)
+ X86_64
+
+#elif defined(__i386__) || defined(__i386) || defined(_M_IX86)
+ X86_32
+
+#elif defined(__aarch64__) || defined(__ARM_ARCH_ISA_A64)
+ ARM64
+
+#elif defined(__arm__) || defined(_M_ARM) || defined(__ARM_ARCH_7A__)
+ ARM32
+
+#elif defined(__powerpc64__) || defined(__ppc64__) || defined(_ARCH_PPC64)
+ PPC64
+
+#elif defined(__powerpc__) || defined(__ppc__) || defined(_ARCH_PPC)
+
+ #if defined(__SPE__)
+ POWERPCSPE
+ #else
+ PPC32
+ #endif
+
+#elif defined(__mips__) || defined(__mips)
+
+ #if defined(__LP64__) || defined(_LP64)
+ MIPS64
+ #else
+ MIPS32
+ #endif
+
+#elif defined(__sparc__)
+
+ #if defined(__LP64__) || defined(_LP64)
+ SPARC64
+ #else
+ SPARC32
+ #endif
+
+#elif defined(__alpha__)
+ ALPHA
+
+#elif defined(__hppa__) || defined(__hppa)
+ HPPA
+
+#elif defined(__ia64__)
+ IA64
+
+#elif defined(__m68k__)
+ M68K
+
+#elif defined(__sh__)
+ SH
+
+#elif defined(__s390x__)
+ S390X
+
+#elif defined(__s390__)
+ S390
+
+#elif defined(__riscv)
+
+ #if defined(__LP64__)
+ RISCV64
+ #else
+ RISCV32
+ #endif
+
+#else
+ UNKNOWN
+
+#endif
diff --git a/comm/third_party/botan/src/build-data/detect_version.cpp b/comm/third_party/botan/src/build-data/detect_version.cpp
new file mode 100644
index 0000000000..8e08c63b91
--- /dev/null
+++ b/comm/third_party/botan/src/build-data/detect_version.cpp
@@ -0,0 +1,60 @@
+/*
+* This file is preprocessed to produce output that is examined by
+* configure.py to determine the compilers version number.
+*/
+
+#if defined(_MSC_VER)
+
+ /*
+ _MSC_VER Defined as an integer literal that encodes the major and
+ minor number elements of the compiler's version number. The major
+ number is the first element of the period-delimited version number
+ and the minor number is the second element. For example, if the
+ version number of the Visual C++ compiler is 17.00.51106.1, the
+ _MSC_VER macro evaluates to 1700.
+ https://msdn.microsoft.com/en-us/library/b0084kay.aspx
+ */
+ MSVC _MSC_VER
+
+#elif defined(__ibmxl__)
+
+ XLC __ibmxl_version__ __ibmxl_release__
+
+#elif defined(__clang__) && defined(__apple_build_version__)
+
+ /*
+ Map Apple LLVM versions as used in XCode back to standard Clang.
+ This is not exact since the versions used in XCode are actually
+ forks of Clang and do not coorespond perfectly to standard Clang
+ releases. In addition we don't bother mapping very old versions
+ (anything before XCode 7 is treated like Clang 3.5, which is the
+ oldest version we support) and for "future" versions we simply
+ treat them as Clang 4.0, since we don't currenly rely on any
+ features not included in 4.0
+ */
+
+ #if __clang_major__ >= 9
+ CLANG 4 0
+ #elif __clang_major__ == 8
+ CLANG 3 9
+ #elif __clang_major__ == 7 && __clang_minor__ == 3
+ CLANG 3 8
+ #elif __clang_major__ == 7
+ CLANG 3 7
+ #else
+ CLANG 3 5
+ #endif
+
+#elif defined(__clang__)
+
+ CLANG __clang_major__ __clang_minor__
+
+#elif defined(__GNUG__)
+
+ GCC __GNUC__ __GNUC_MINOR__
+
+#else
+
+ UNKNOWN 0 0
+
+#endif
diff --git a/comm/third_party/botan/src/build-data/innosetup.in b/comm/third_party/botan/src/build-data/innosetup.in
new file mode 100644
index 0000000000..86df5f27bf
--- /dev/null
+++ b/comm/third_party/botan/src/build-data/innosetup.in
@@ -0,0 +1,73 @@
+; A script for packaging botan with InnoSetup
+
+[Setup]
+AppName=Botan
+AppVerName=Botan %{version}
+
+AppPublisher=Jack Lloyd
+AppPublisherURL=https://botan.randombit.net/
+AppVersion=%{version}
+
+VersionInfoCopyright=Copyright (C) 1999-2012 Jack Lloyd and others
+VersionInfoVersion=%{version_major}.%{version_minor}.%{version_patch}.0
+
+; Require at least Windows XP
+MinVersion=5.1
+
+ArchitecturesAllowed=%{innosetup_arch}
+ArchitecturesInstallIn64BitMode=%{innosetup_arch}
+
+DefaultDirName={pf}\botan
+DefaultGroupName=botan
+
+SolidCompression=yes
+
+OutputDir=.
+OutputBaseFilename=botan-%{version}-%{arch}
+
+[Types]
+Name: "user"; Description: "User"
+Name: "devel"; Description: "Developer"
+Name: "custom"; Description: "Custom"; Flags: iscustom
+
+[Components]
+name: "dll"; Description: "Runtime DLLs"; Types: user devel custom; Flags: fixed
+name: "implib"; Description: "Import Library"; Types: devel
+name: "includes"; Description: "Include Files"; Types: devel
+name: "docs"; Description: "Developer Documentation"; Types: devel
+
+[Files]
+; DLL and license file is always included
+Source: "..\doc\license.rst"; DestDir: "{app}"; Components: dll; AfterInstall: ConvertLineEndings
+Source: "..\botan.dll"; DestDir: "{app}"; Components: dll
+Source: "..\botan.dll.manifest"; DestDir: "{app}"; Components: dll; Flags: skipifsourcedoesntexist
+
+Source: "include\botan\*"; DestDir: "{app}\include\botan"; Components: includes; AfterInstall: ConvertLineEndings
+
+Source: "..\doc\*.rst"; DestDir: "{app}\doc"; Excludes: "license.rst"; Components: docs; AfterInstall: ConvertLineEndings
+
+Source: "..\doc\examples\*.cpp"; DestDir: "{app}\doc\examples"; Components: docs; AfterInstall: ConvertLineEndings
+
+Source: "..\botan.exp"; DestDir: "{app}"; Components: implib
+Source: "..\botan.lib"; DestDir: "{app}"; Components: implib
+
+[Code]
+const
+ LF = #10;
+ CR = #13;
+ CRLF = CR + LF;
+
+procedure ConvertLineEndings();
+ var
+ FilePath : String;
+ FileContents : String;
+begin
+ FilePath := ExpandConstant(CurrentFileName)
+
+ if ExtractFileName(CurrentFileName) <> 'build.h' then
+ begin
+ LoadStringFromFile(FilePath, FileContents);
+ StringChangeEx(FileContents, LF, CRLF, False);
+ SaveStringToFile(FilePath, FileContents, False);
+ end;
+end;
diff --git a/comm/third_party/botan/src/build-data/makefile.in b/comm/third_party/botan/src/build-data/makefile.in
new file mode 100644
index 0000000000..16d077e82a
--- /dev/null
+++ b/comm/third_party/botan/src/build-data/makefile.in
@@ -0,0 +1,146 @@
+
+# This makefile was generated using '%{command_line}'
+
+# Paths to relevant programs
+
+CXX = %{cxx}
+LINKER = %{linker}
+AR = %{ar_command}
+AR_OPTIONS = %{ar_options}
+PYTHON_EXE = %{python_exe}
+
+# Compiler Flags
+
+ABI_FLAGS = %{cc_sysroot} %{cxx_abi_flags}
+LANG_FLAGS = %{cc_lang_flags} %{os_feature_macros}
+CXXFLAGS = %{cc_compile_flags} -DBOTAN_IS_BEING_BUILT
+WARN_FLAGS = %{cc_warning_flags}
+LIB_FLAGS = %{lib_flags}
+LDFLAGS = %{ldflags}
+
+EXE_LINK_CMD = %{exe_link_cmd}
+
+LIB_LINKS_TO = %{external_link_cmd} %{link_to}
+EXE_LINKS_TO = %{link_to_botan} $(LIB_LINKS_TO) %{extra_libs}
+
+BUILD_FLAGS = $(ABI_FLAGS) $(LANG_FLAGS) $(CXXFLAGS) $(WARN_FLAGS)
+
+SCRIPTS_DIR = %{scripts_dir}
+INSTALLED_LIB_DIR = %{libdir}
+
+# The primary target
+all: %{all_targets}
+
+# Executable targets
+CLI = %{cli_exe}
+TEST = %{test_exe}
+LIBRARIES = %{library_targets}
+
+cli: $(CLI)
+tests: $(TEST)
+libs: $(LIBRARIES)
+docs: %{doc_stamp_file}
+
+# Misc targets
+
+%{if make_supports_phony}
+.PHONY: all cli libs tests check docs clean distclean install
+%{endif}
+
+%{doc_stamp_file}: %{doc_dir}/*.rst %{doc_dir}/api_ref/*.rst %{doc_dir}/dev_ref/*.rst
+ "$(PYTHON_EXE)" "$(SCRIPTS_DIR)/build_docs.py" --build-dir="%{build_dir}"
+
+clean:
+ "$(PYTHON_EXE)" "$(SCRIPTS_DIR)/cleanup.py" --build-dir="%{build_dir}"
+
+distclean:
+ "$(PYTHON_EXE)" "$(SCRIPTS_DIR)/cleanup.py" --build-dir="%{build_dir}" --distclean
+
+install: %{install_targets}
+ "$(PYTHON_EXE)" "$(SCRIPTS_DIR)/install.py" --prefix="%{prefix}" --build-dir="%{build_dir}" --bindir="%{bindir}" --libdir="%{libdir}" --docdir="%{docdir}" --includedir="%{includedir}"
+
+check: tests
+ "$(PYTHON_EXE)" "$(SCRIPTS_DIR)/check.py" --build-dir="%{build_dir}"
+
+# Object Files
+LIBOBJS = %{join lib_objs}
+
+CLIOBJS = %{join cli_objs}
+
+TESTOBJS = %{join test_objs}
+
+# Executable targets
+
+$(CLI): $(LIBRARIES) $(CLIOBJS)
+ $(EXE_LINK_CMD) $(ABI_FLAGS) $(CLIOBJS) $(LDFLAGS) $(EXE_LINKS_TO) %{output_to_exe}$@
+
+$(TEST): $(LIBRARIES) $(TESTOBJS)
+ $(EXE_LINK_CMD) $(ABI_FLAGS) $(TESTOBJS) $(LDFLAGS) $(EXE_LINKS_TO) %{output_to_exe}$@
+
+%{if build_fuzzers}
+
+FUZZERS = %{fuzzer_bin}
+
+fuzzers: $(LIBRARIES) $(FUZZERS)
+
+fuzzer_corpus:
+ git clone --depth=1 https://github.com/randombit/crypto-corpus.git fuzzer_corpus
+
+fuzzer_corpus_zip: fuzzer_corpus
+ ./src/scripts/create_corpus_zip.py fuzzer_corpus %{fuzzobj_dir}
+
+%{endif}
+
+%{if build_bogo_shim}
+
+bogo_shim: %{out_dir}/botan_bogo_shim
+
+# BoGo shim
+%{out_dir}/botan_bogo_shim: %{bogo_shim_src} $(LIBRARIES)
+ $(CXX) $(BUILD_FLAGS) %{include_paths} %{bogo_shim_src} $(LDFLAGS) $(EXE_LINKS_TO) %{output_to_exe}$@
+
+%{endif}
+
+# Library targets
+
+%{if build_static_lib}
+
+%{out_dir}/%{static_lib_name}: $(LIBOBJS)
+ $(AR) $(AR_OPTIONS) %{ar_output_to}$@ $(LIBOBJS)
+
+%{endif}
+
+%{if build_shared_lib}
+
+%{out_dir}/%{shared_lib_name}: $(LIBOBJS)
+ %{lib_link_cmd} $(ABI_FLAGS) $(LDFLAGS) $(LIBOBJS) $(LIB_LINKS_TO) %{output_to_exe}$@
+%{endif}
+%{if symlink_shared_lib}
+ cd %{out_dir} && ln -fs %{shared_lib_name} %{soname_base}
+ cd %{out_dir} && ln -fs %{shared_lib_name} %{soname_patch}
+%{endif}
+
+# Build Commands
+
+%{for lib_build_info}
+%{obj}: %{src}
+ $(CXX) $(LIB_FLAGS) $(BUILD_FLAGS) %{isa_flags} %{include_paths} %{dash_c} %{src} %{dash_o}$@
+%{endfor}
+
+%{for cli_build_info}
+%{obj}: %{src}
+ $(CXX) $(BUILD_FLAGS) %{isa_flags} %{include_paths} %{dash_c} %{src} %{dash_o}$@
+%{endfor}
+
+%{for test_build_info}
+%{obj}: %{src}
+ $(CXX) $(BUILD_FLAGS) %{isa_flags} %{include_paths} %{dash_c} %{src} %{dash_o}$@
+%{endfor}
+
+%{for fuzzer_build_info}
+%{obj}: %{src}
+ $(CXX) $(BUILD_FLAGS) %{isa_flags} %{include_paths} %{dash_c} %{src} %{dash_o}$@
+
+%{exe}: %{obj} $(LIBRARIES)
+ $(EXE_LINK_CMD) $(ABI_FLAGS) %{obj} $(EXE_LINKS_TO) %{fuzzer_lib} %{output_to_exe}$@
+%{endfor}
diff --git a/comm/third_party/botan/src/build-data/oids.txt b/comm/third_party/botan/src/build-data/oids.txt
new file mode 100644
index 0000000000..03ce80a843
--- /dev/null
+++ b/comm/third_party/botan/src/build-data/oids.txt
@@ -0,0 +1,335 @@
+# Regenerate with ./src/scripts/oids.py oids > src/lib/asn1/oid_maps.cpp
+# AND ./src/scripts/oids.py dn_ub > src/lib/x509/x509_dn_ub.cpp
+# (if you modified something under [dn]
+# AND ./src/scripts/oids.py pads > src/lib/pk_pad/padding.cpp
+# (if you modified something under [signature]
+
+# 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
+1.3.101.110 = Curve25519
+1.3.101.112 = Ed25519
+
+# XMSS
+1.3.6.1.4.1.25258.1.5 = XMSS-draft6
+1.3.6.1.4.1.25258.1.8 = XMSS-draft12
+# draft-vangeest-x509-hash-sigs-03
+0.4.0.127.0.15.1.1.13.0 = XMSS
+
+# X9.62 ecPublicKey, valid for ECDSA and ECDH (RFC 3279 sec 2.3.5)
+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.2 = SM2_Kex
+1.2.156.10197.1.301.3 = SM2_Enc
+
+# ecgPublicKey (see https://www.teletrust.de/projekte/oid/)
+1.3.36.3.3.2.5.2.1 = ECGDSA
+
+# EC-KCDSA mechanism (Elliptic Curve KCDSA)
+1.0.14888.3.0.5 = ECKCDSA
+
+1.2.643.2.2.19 = GOST-34.10
+1.2.643.7.1.1.1.1 = GOST-34.10-2012-256
+1.2.643.7.1.1.1.2 = GOST-34.10-2012-512
+
+# OpenPGP (RFC4880bis)
+1.3.6.1.4.1.11591.15.1 = OpenPGP.Ed25519
+1.3.6.1.4.1.3029.1.5.1 = OpenPGP.Curve25519
+
+[cipher]
+# Cipher modes
+1.3.14.3.2.7 = DES/CBC
+1.2.840.113549.3.7 = TripleDES/CBC
+1.2.840.113533.7.66.10 = CAST-128/CBC
+2.16.840.1.101.3.4.1.2 = AES-128/CBC
+2.16.840.1.101.3.4.1.22 = AES-192/CBC
+2.16.840.1.101.3.4.1.42 = AES-256/CBC
+1.2.410.200004.1.4 = SEED/CBC
+1.2.156.10197.1.104.2 = SM4/CBC
+
+1.2.840.113549.1.9.16.3.18 = ChaCha20Poly1305
+
+2.16.840.1.101.3.4.1.6 = AES-128/GCM
+2.16.840.1.101.3.4.1.26 = AES-192/GCM
+2.16.840.1.101.3.4.1.46 = AES-256/GCM
+
+2.16.840.1.101.3.4.1.7 = AES-128/CCM
+2.16.840.1.101.3.4.1.27 = AES-192/CCM
+2.16.840.1.101.3.4.1.47 = AES-256/CCM
+
+1.2.392.200011.61.1.1.1.2 = Camellia-128/CBC
+1.2.392.200011.61.1.1.1.3 = Camellia-192/CBC
+1.2.392.200011.61.1.1.1.4 = Camellia-256/CBC
+
+0.3.4401.5.3.1.9.6 = Camellia-128/GCM
+0.3.4401.5.3.1.9.26 = Camellia-192/GCM
+0.3.4401.5.3.1.9.46 = Camellia-256/GCM
+
+1.2.156.10197.1.104.8 = SM4/GCM
+
+1.3.6.1.4.1.25258.3.1 = Serpent/CBC
+1.3.6.1.4.1.25258.3.2 = Threefish-512/CBC
+1.3.6.1.4.1.25258.3.3 = Twofish/CBC
+
+1.3.6.1.4.1.25258.3.101 = Serpent/GCM
+1.3.6.1.4.1.25258.3.102 = Twofish/GCM
+
+1.3.6.1.4.1.25258.3.2.1 = AES-128/OCB
+1.3.6.1.4.1.25258.3.2.2 = AES-192/OCB
+1.3.6.1.4.1.25258.3.2.3 = AES-256/OCB
+1.3.6.1.4.1.25258.3.2.4 = Serpent/OCB
+1.3.6.1.4.1.25258.3.2.5 = Twofish/OCB
+1.3.6.1.4.1.25258.3.2.6 = Camellia-128/OCB
+1.3.6.1.4.1.25258.3.2.7 = Camellia-192/OCB
+1.3.6.1.4.1.25258.3.2.8 = Camellia-256/OCB
+
+1.2.156.10197.1.104.100 = SM4/OCB
+
+1.3.6.1.4.1.25258.3.4.1 = AES-128/SIV
+1.3.6.1.4.1.25258.3.4.2 = AES-192/SIV
+1.3.6.1.4.1.25258.3.4.3 = AES-256/SIV
+1.3.6.1.4.1.25258.3.4.4 = Serpent/SIV
+1.3.6.1.4.1.25258.3.4.5 = Twofish/SIV
+1.3.6.1.4.1.25258.3.4.6 = Camellia-128/SIV
+1.3.6.1.4.1.25258.3.4.7 = Camellia-192/SIV
+1.3.6.1.4.1.25258.3.4.8 = Camellia-256/SIV
+1.3.6.1.4.1.25258.3.4.9 = SM4/SIV
+
+[hash]
+# Hash functions
+1.2.840.113549.2.5 = MD5
+1.3.6.1.4.1.11591.12.2 = Tiger(24,3)
+1.2.156.10197.1.401 = SM3
+1.3.14.3.2.26 = SHA-160
+1.3.36.3.2.1 = RIPEMD-160
+1.2.643.7.1.1.2.2 = Streebog-256
+1.2.643.7.1.1.2.3 = Streebog-512
+
+# From NIST:
+2.16.840.1.101.3.4.2.1 = SHA-256
+2.16.840.1.101.3.4.2.2 = SHA-384
+2.16.840.1.101.3.4.2.3 = SHA-512
+2.16.840.1.101.3.4.2.4 = SHA-224
+2.16.840.1.101.3.4.2.6 = SHA-512-256
+2.16.840.1.101.3.4.2.7 = SHA-3(224)
+2.16.840.1.101.3.4.2.8 = SHA-3(256)
+2.16.840.1.101.3.4.2.9 = SHA-3(384)
+2.16.840.1.101.3.4.2.10 = SHA-3(512)
+2.16.840.1.101.3.4.2.11 = SHAKE-128
+2.16.840.1.101.3.4.2.12 = SHAKE-256
+
+[mac]
+# MACs
+1.2.840.113549.2.7 = HMAC(SHA-160)
+1.2.840.113549.2.8 = HMAC(SHA-224)
+1.2.840.113549.2.9 = HMAC(SHA-256)
+1.2.840.113549.2.10 = HMAC(SHA-384)
+1.2.840.113549.2.11 = HMAC(SHA-512)
+1.2.840.113549.2.13 = HMAC(SHA-512-256)
+
+[keywrap]
+# Keywrap algorithms
+1.2.840.113549.1.9.16.3.6 = KeyWrap.TripleDES
+1.2.840.113533.7.66.15 = KeyWrap.CAST-128
+2.16.840.1.101.3.4.1.5 = KeyWrap.AES-128
+2.16.840.1.101.3.4.1.25 = KeyWrap.AES-192
+2.16.840.1.101.3.4.1.45 = KeyWrap.AES-256
+
+[compression]
+1.2.840.113549.1.9.16.3.8 = Compression.Zlib
+
+# Signature algos
+[signature]
+1.2.840.113549.1.1.4 = RSA/EMSA3(MD5)
+1.2.840.113549.1.1.5 = RSA/EMSA3(SHA-160)
+1.2.840.113549.1.1.8 = MGF1
+1.2.840.113549.1.1.10 = RSA/EMSA4
+1.2.840.113549.1.1.11 = RSA/EMSA3(SHA-256)
+1.2.840.113549.1.1.12 = RSA/EMSA3(SHA-384)
+1.2.840.113549.1.1.13 = RSA/EMSA3(SHA-512)
+1.2.840.113549.1.1.14 = RSA/EMSA3(SHA-224)
+1.2.840.113549.1.1.16 = RSA/EMSA3(SHA-512-256)
+1.3.36.3.3.1.2 = RSA/EMSA3(RIPEMD-160)
+
+1.2.156.10197.1.501 = SM2_Sig/SM3
+1.2.156.10197.1.504 = RSA/EMSA3(SM3)
+
+1.2.840.10040.4.3 = DSA/EMSA1(SHA-160)
+
+2.16.840.1.101.3.4.3.1 = DSA/EMSA1(SHA-224)
+2.16.840.1.101.3.4.3.2 = DSA/EMSA1(SHA-256)
+2.16.840.1.101.3.4.3.3 = DSA/EMSA1(SHA-384)
+2.16.840.1.101.3.4.3.4 = DSA/EMSA1(SHA-512)
+2.16.840.1.101.3.4.3.5 = DSA/EMSA1(SHA-3(224))
+2.16.840.1.101.3.4.3.6 = DSA/EMSA1(SHA-3(256))
+2.16.840.1.101.3.4.3.7 = DSA/EMSA1(SHA-3(384))
+2.16.840.1.101.3.4.3.8 = DSA/EMSA1(SHA-3(512))
+
+2.16.840.1.101.3.4.3.9 = ECDSA/EMSA1(SHA-3(224))
+2.16.840.1.101.3.4.3.10 = ECDSA/EMSA1(SHA-3(256))
+2.16.840.1.101.3.4.3.11 = ECDSA/EMSA1(SHA-3(384))
+2.16.840.1.101.3.4.3.12 = ECDSA/EMSA1(SHA-3(512))
+
+2.16.840.1.101.3.4.3.13 = RSA/EMSA3(SHA-3(224))
+2.16.840.1.101.3.4.3.14 = RSA/EMSA3(SHA-3(256))
+2.16.840.1.101.3.4.3.15 = RSA/EMSA3(SHA-3(384))
+2.16.840.1.101.3.4.3.16 = RSA/EMSA3(SHA-3(512))
+
+1.2.840.10045.4.1 = ECDSA/EMSA1(SHA-160)
+1.2.840.10045.4.3.1 = ECDSA/EMSA1(SHA-224)
+1.2.840.10045.4.3.2 = ECDSA/EMSA1(SHA-256)
+1.2.840.10045.4.3.3 = ECDSA/EMSA1(SHA-384)
+1.2.840.10045.4.3.4 = ECDSA/EMSA1(SHA-512)
+
+1.3.36.3.3.2.5.4.1 = ECGDSA/EMSA1(RIPEMD-160)
+1.3.36.3.3.2.5.4.2 = ECGDSA/EMSA1(SHA-160)
+1.3.36.3.3.2.5.4.3 = ECGDSA/EMSA1(SHA-224)
+1.3.36.3.3.2.5.4.4 = ECGDSA/EMSA1(SHA-256)
+1.3.36.3.3.2.5.4.5 = ECGDSA/EMSA1(SHA-384)
+1.3.36.3.3.2.5.4.6 = ECGDSA/EMSA1(SHA-512)
+
+1.2.410.200004.1.100.4.3 = ECKCDSA/EMSA1(SHA-1)
+1.2.410.200004.1.100.4.4 = ECKCDSA/EMSA1(SHA-224)
+1.2.410.200004.1.100.4.5 = ECKCDSA/EMSA1(SHA-256)
+
+1.2.643.2.2.3 = GOST-34.10/EMSA1(GOST-R-34.11-94)
+
+1.2.643.7.1.1.3.2 = GOST-34.10-2012-256/EMSA1(Streebog-256)
+1.2.643.7.1.1.3.3 = GOST-34.10-2012-512/EMSA1(Streebog-512)
+
+1.3.6.1.4.1.25258.1.6.1 = GOST-34.10-2012-256/EMSA1(SHA-256)
+
+# Encryption algos
+[encryption]
+1.2.840.113549.1.1.7 = RSA/OAEP
+
+# 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
+
+[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.3.6.1.4.1.11591.4.11 = Scrypt
+
+[pkcs9]
+1.2.840.113549.1.9.1 = PKCS9.EmailAddress
+1.2.840.113549.1.9.2 = PKCS9.UnstructuredName
+1.2.840.113549.1.9.3 = PKCS9.ContentType
+1.2.840.113549.1.9.4 = PKCS9.MessageDigest
+1.2.840.113549.1.9.7 = PKCS9.ChallengePassword
+1.2.840.113549.1.9.14 = PKCS9.ExtensionRequest
+
+[pkix]
+2.5.29.14 = X509v3.SubjectKeyIdentifier
+2.5.29.15 = X509v3.KeyUsage
+2.5.29.16 = X509v3.PrivateKeyUsagePeriod
+2.5.29.17 = X509v3.SubjectAlternativeName
+2.5.29.18 = X509v3.IssuerAlternativeName
+2.5.29.19 = X509v3.BasicConstraints
+2.5.29.20 = X509v3.CRLNumber
+2.5.29.21 = X509v3.ReasonCode
+2.5.29.23 = X509v3.HoldInstructionCode
+2.5.29.24 = X509v3.InvalidityDate
+2.5.29.28 = X509v3.CRLIssuingDistributionPoint
+2.5.29.30 = X509v3.NameConstraints
+2.5.29.31 = X509v3.CRLDistributionPoints
+2.5.29.32 = X509v3.CertificatePolicies
+2.5.29.35 = X509v3.AuthorityKeyIdentifier
+2.5.29.36 = X509v3.PolicyConstraints
+2.5.29.37 = X509v3.ExtendedKeyUsage
+1.3.6.1.5.5.7.1.1 = PKIX.AuthorityInformationAccess
+
+2.5.29.32.0 = X509v3.AnyPolicy
+
+1.2.643.100.111 = GOST.SubjectSigningTool
+1.2.643.100.112 = GOST.IssuerSigningTool
+
+1.2.643.100.1 = GOST.OGRN
+1.2.643.3.131.1.1 = GOST.INN
+
+1.3.6.1.5.5.7.3.1 = PKIX.ServerAuth
+1.3.6.1.5.5.7.3.2 = PKIX.ClientAuth
+1.3.6.1.5.5.7.3.3 = PKIX.CodeSigning
+1.3.6.1.5.5.7.3.4 = PKIX.EmailProtection
+1.3.6.1.5.5.7.3.5 = PKIX.IPsecEndSystem
+1.3.6.1.5.5.7.3.6 = PKIX.IPsecTunnel
+1.3.6.1.5.5.7.3.7 = PKIX.IPsecUser
+1.3.6.1.5.5.7.3.8 = PKIX.TimeStamping
+1.3.6.1.5.5.7.3.9 = PKIX.OCSPSigning
+
+1.3.6.1.5.5.7.8.5 = PKIX.XMPPAddr
+
+1.3.6.1.5.5.7.48.1 = PKIX.OCSP
+1.3.6.1.5.5.7.48.1.1 = PKIX.OCSP.BasicResponse
+1.3.6.1.5.5.7.48.2 = PKIX.CertificateAuthorityIssuers
+
+1.3.6.1.4.1.311.20.2.2 = Microsoft SmartcardLogon
+1.3.6.1.4.1.311.20.2.3 = Microsoft UPN
+
+2.16.840.1.113730.1.13 = Certificate Comment
+
+# ECC param sets
+[ecc_param]
+1.3.132.0.8 = secp160r1
+1.3.132.0.9 = secp160k1
+1.3.132.0.10 = secp256k1
+1.3.132.0.30 = secp160r2
+1.3.132.0.31 = secp192k1
+1.3.132.0.32 = secp224k1
+1.3.132.0.33 = secp224r1
+1.3.132.0.34 = secp384r1
+1.3.132.0.35 = secp521r1
+1.3.6.1.4.1.8301.3.1.2.9.0.38 = secp521r1
+
+1.2.840.10045.3.1.1 = secp192r1
+1.2.840.10045.3.1.2 = x962_p192v2
+1.2.840.10045.3.1.3 = x962_p192v3
+1.2.840.10045.3.1.4 = x962_p239v1
+1.2.840.10045.3.1.5 = x962_p239v2
+1.2.840.10045.3.1.6 = x962_p239v3
+1.2.840.10045.3.1.7 = secp256r1
+
+1.2.156.10197.1.301 = sm2p256v1
+
+1.3.36.3.3.2.8.1.1.1 = brainpool160r1
+1.3.36.3.3.2.8.1.1.3 = brainpool192r1
+1.3.36.3.3.2.8.1.1.5 = brainpool224r1
+1.3.36.3.3.2.8.1.1.7 = brainpool256r1
+1.3.36.3.3.2.8.1.1.9 = brainpool320r1
+1.3.36.3.3.2.8.1.1.11 = brainpool384r1
+1.3.36.3.3.2.8.1.1.13 = brainpool512r1
+
+1.2.250.1.223.101.256.1 = frp256v1
+
+1.2.643.7.1.2.1.1.1 = gost_256A
+1.2.643.7.1.2.1.1.2 = gost_256B
+1.2.643.7.1.2.1.2.1 = gost_512A
+1.2.643.7.1.2.1.2.2 = gost_512B
+1.2.643.2.2.35.1 = gost_256A
+1.2.643.2.2.36.0 = gost_256A
diff --git a/comm/third_party/botan/src/build-data/os/aix.txt b/comm/third_party/botan/src/build-data/os/aix.txt
new file mode 100644
index 0000000000..fd8cf2eb1e
--- /dev/null
+++ b/comm/third_party/botan/src/build-data/os/aix.txt
@@ -0,0 +1,18 @@
+
+soname_suffix "so"
+
+use_stack_protector no
+
+<target_features>
+posix1
+posix_mlock
+clock_gettime
+dev_random
+proc_fs
+
+atomics
+sockets
+threads
+thread_local
+filesystem
+</target_features>
diff --git a/comm/third_party/botan/src/build-data/os/android.txt b/comm/third_party/botan/src/build-data/os/android.txt
new file mode 100644
index 0000000000..f8f61f7b42
--- /dev/null
+++ b/comm/third_party/botan/src/build-data/os/android.txt
@@ -0,0 +1,26 @@
+
+soname_suffix "so"
+
+<target_features>
+posix1
+posix_mlock
+clock_gettime
+
+# arc4random_buf preferably backed-up by Chacha20 rather
+# than RC4. can possibly be disabled by --without-os-feature=arc4random
+arc4random
+dev_random
+
+# getauxval is available in Android NDK for min API 18 and in Crystax NDK
+# for all min API levels. Use --without-os-feature=getauxval to disable
+getauxval
+
+# Added in API 28
+#getentropy
+
+atomics
+sockets
+threads
+thread_local
+filesystem
+</target_features>
diff --git a/comm/third_party/botan/src/build-data/os/cygwin.txt b/comm/third_party/botan/src/build-data/os/cygwin.txt
new file mode 100644
index 0000000000..dabc018c3d
--- /dev/null
+++ b/comm/third_party/botan/src/build-data/os/cygwin.txt
@@ -0,0 +1,20 @@
+
+program_suffix .exe
+
+# Cygwin supports shared libs fine, but there are problems with making a Botan
+# shared library when libraries it depends on are static-only (such as libz).
+# So until I can figure out a work-around, it's disabled.
+
+install_root c:\Botan
+doc_dir docs
+
+<target_features>
+posix1
+dev_random
+
+atomics
+sockets
+threads
+thread_local
+filesystem
+</target_features>
diff --git a/comm/third_party/botan/src/build-data/os/dragonfly.txt b/comm/third_party/botan/src/build-data/os/dragonfly.txt
new file mode 100644
index 0000000000..f2cc1cb76f
--- /dev/null
+++ b/comm/third_party/botan/src/build-data/os/dragonfly.txt
@@ -0,0 +1,17 @@
+
+soname_suffix "so"
+
+<target_features>
+posix1
+posix_mlock
+clock_gettime
+proc_fs
+dev_random
+arc4random
+
+atomics
+sockets
+threads
+thread_local
+filesystem
+</target_features>
diff --git a/comm/third_party/botan/src/build-data/os/emscripten.txt b/comm/third_party/botan/src/build-data/os/emscripten.txt
new file mode 100644
index 0000000000..0068fb171a
--- /dev/null
+++ b/comm/third_party/botan/src/build-data/os/emscripten.txt
@@ -0,0 +1,17 @@
+
+obj_suffix bc
+
+static_suffix a
+program_suffix .bc
+
+ar_command emar
+ar_options cr
+
+use_stack_protector no
+
+<target_features>
+atomics
+filesystem
+dev_random
+posix1
+</target_features>
diff --git a/comm/third_party/botan/src/build-data/os/freebsd.txt b/comm/third_party/botan/src/build-data/os/freebsd.txt
new file mode 100644
index 0000000000..1b9b3817de
--- /dev/null
+++ b/comm/third_party/botan/src/build-data/os/freebsd.txt
@@ -0,0 +1,22 @@
+
+soname_suffix "so"
+
+default_compiler clang
+
+<target_features>
+posix1
+posix_mlock
+clock_gettime
+dev_random
+arc4random
+explicit_bzero
+cap_enter
+elf_aux_info
+getentropy
+
+atomics
+sockets
+threads
+thread_local
+filesystem
+</target_features>
diff --git a/comm/third_party/botan/src/build-data/os/haiku.txt b/comm/third_party/botan/src/build-data/os/haiku.txt
new file mode 100644
index 0000000000..7a4bcbe892
--- /dev/null
+++ b/comm/third_party/botan/src/build-data/os/haiku.txt
@@ -0,0 +1,25 @@
+
+soname_suffix "so"
+
+install_root /boot
+header_dir develop/headers
+lib_dir system/lib
+doc_dir system/documentation
+
+use_stack_protector no
+
+<target_features>
+posix1
+clock_gettime
+dev_random
+
+atomics
+sockets
+threads
+thread_local
+filesystem
+</target_features>
+
+<aliases>
+beos
+</aliases>
diff --git a/comm/third_party/botan/src/build-data/os/hpux.txt b/comm/third_party/botan/src/build-data/os/hpux.txt
new file mode 100644
index 0000000000..ea699910cb
--- /dev/null
+++ b/comm/third_party/botan/src/build-data/os/hpux.txt
@@ -0,0 +1,20 @@
+
+# It is "sl" on HP-PA, but HP-UX on PA is EOL
+soname_suffix "so"
+
+<target_features>
+posix1
+posix_mlock
+clock_gettime
+dev_random
+
+atomics
+sockets
+threads
+thread_local
+filesystem
+</target_features>
+
+<aliases>
+hp-ux
+</aliases>
diff --git a/comm/third_party/botan/src/build-data/os/hurd.txt b/comm/third_party/botan/src/build-data/os/hurd.txt
new file mode 100644
index 0000000000..589b99e2d9
--- /dev/null
+++ b/comm/third_party/botan/src/build-data/os/hurd.txt
@@ -0,0 +1,19 @@
+
+soname_suffix "so"
+
+<target_features>
+posix1
+posix_mlock
+dev_random
+clock_gettime
+
+atomics
+sockets
+threads
+thread_local
+filesystem
+</target_features>
+
+<aliases>
+gnu
+</aliases>
diff --git a/comm/third_party/botan/src/build-data/os/includeos.txt b/comm/third_party/botan/src/build-data/os/includeos.txt
new file mode 100644
index 0000000000..7e45eb47c2
--- /dev/null
+++ b/comm/third_party/botan/src/build-data/os/includeos.txt
@@ -0,0 +1,5 @@
+<target_features>
+posix1
+dev_random
+atomics
+</target_features>
diff --git a/comm/third_party/botan/src/build-data/os/ios.txt b/comm/third_party/botan/src/build-data/os/ios.txt
new file mode 100644
index 0000000000..c65f243058
--- /dev/null
+++ b/comm/third_party/botan/src/build-data/os/ios.txt
@@ -0,0 +1,23 @@
+
+default_compiler clang
+
+uses_pkg_config no
+
+doc_dir doc
+
+<target_features>
+posix1
+posix_mlock
+arc4random
+
+commoncrypto
+
+atomics
+sockets
+threads
+thread_local
+filesystem
+</target_features>
+
+<aliases>
+</aliases>
diff --git a/comm/third_party/botan/src/build-data/os/linux.txt b/comm/third_party/botan/src/build-data/os/linux.txt
new file mode 100644
index 0000000000..daf98f2b61
--- /dev/null
+++ b/comm/third_party/botan/src/build-data/os/linux.txt
@@ -0,0 +1,26 @@
+
+soname_suffix "so"
+
+<target_features>
+posix1
+posix_mlock
+
+dev_random
+proc_fs
+clock_gettime
+getauxval
+
+# these are not enabled by default as only available in newer kernel/glibc
+#getrandom
+#getentropy
+
+atomics
+sockets
+threads
+thread_local
+filesystem
+</target_features>
+
+<aliases>
+linux-gnu
+</aliases>
diff --git a/comm/third_party/botan/src/build-data/os/llvm.txt b/comm/third_party/botan/src/build-data/os/llvm.txt
new file mode 100644
index 0000000000..0cda2f7e80
--- /dev/null
+++ b/comm/third_party/botan/src/build-data/os/llvm.txt
@@ -0,0 +1,15 @@
+
+obj_suffix bc
+
+static_suffix bc
+program_suffix .bc
+
+ar_command llvm-link
+ar_options -o
+
+use_stack_protector no
+
+<target_features>
+filesystem
+atomics
+</target_features>
diff --git a/comm/third_party/botan/src/build-data/os/macos.txt b/comm/third_party/botan/src/build-data/os/macos.txt
new file mode 100644
index 0000000000..b603d18dfc
--- /dev/null
+++ b/comm/third_party/botan/src/build-data/os/macos.txt
@@ -0,0 +1,32 @@
+
+default_compiler clang
+
+soname_pattern_base "lib{libname}.dylib"
+soname_pattern_abi "lib{libname}.{abi_rev}.dylib"
+soname_pattern_patch "lib{libname}.{abi_rev}.{version_minor}.{version_patch}.dylib"
+
+doc_dir doc
+
+<target_features>
+posix1
+posix_mlock
+arc4random
+getentropy
+dev_random
+clock_gettime
+
+commoncrypto
+apple_keychain
+
+atomics
+sockets
+threads
+thread_local
+filesystem
+</target_features>
+
+<aliases>
+darwin
+macosx
+osx
+</aliases>
diff --git a/comm/third_party/botan/src/build-data/os/mingw.txt b/comm/third_party/botan/src/build-data/os/mingw.txt
new file mode 100644
index 0000000000..b2cc2f0b80
--- /dev/null
+++ b/comm/third_party/botan/src/build-data/os/mingw.txt
@@ -0,0 +1,33 @@
+
+program_suffix .exe
+obj_suffix o
+static_suffix a
+
+install_root /mingw
+header_dir include
+lib_dir lib
+doc_dir share/doc
+
+# see https://sourceforge.net/p/mingw-w64/bugs/755/
+use_stack_protector no
+
+<feature_macros>
+_WIN32_WINNT=0x0600
+</feature_macros>
+
+<aliases>
+msys
+mingw32.*
+</aliases>
+
+<target_features>
+win32
+rtlgenrandom
+virtual_lock
+
+atomics
+threads
+thread_local
+filesystem
+certificate_store
+</target_features>
diff --git a/comm/third_party/botan/src/build-data/os/nacl.txt b/comm/third_party/botan/src/build-data/os/nacl.txt
new file mode 100644
index 0000000000..0cb08ab936
--- /dev/null
+++ b/comm/third_party/botan/src/build-data/os/nacl.txt
@@ -0,0 +1,6 @@
+
+
+<target_features>
+threads
+thread_local
+</target_features>
diff --git a/comm/third_party/botan/src/build-data/os/netbsd.txt b/comm/third_party/botan/src/build-data/os/netbsd.txt
new file mode 100644
index 0000000000..9be8a1f8ac
--- /dev/null
+++ b/comm/third_party/botan/src/build-data/os/netbsd.txt
@@ -0,0 +1,21 @@
+
+soname_suffix "so"
+
+<target_features>
+posix1
+posix_mlock
+clock_gettime
+dev_random
+arc4random
+explicit_memset
+
+atomics
+sockets
+threads
+thread_local
+filesystem
+</target_features>
+
+<feature_macros>
+_NETBSD_SOURCE
+</feature_macros>
diff --git a/comm/third_party/botan/src/build-data/os/none.txt b/comm/third_party/botan/src/build-data/os/none.txt
new file mode 100644
index 0000000000..d514a5ef7c
--- /dev/null
+++ b/comm/third_party/botan/src/build-data/os/none.txt
@@ -0,0 +1,4 @@
+
+<target_features>
+</target_features>
+
diff --git a/comm/third_party/botan/src/build-data/os/openbsd.txt b/comm/third_party/botan/src/build-data/os/openbsd.txt
new file mode 100644
index 0000000000..70f49e6ac8
--- /dev/null
+++ b/comm/third_party/botan/src/build-data/os/openbsd.txt
@@ -0,0 +1,25 @@
+
+soname_pattern_base "lib{libname}.so"
+soname_pattern_abi "lib{libname}.so.{abi_rev}.{version_minor}"
+soname_pattern_patch "lib{libname}.so.{abi_rev}.{version_minor}"
+
+shared_lib_symlinks no
+
+default_compiler clang
+
+<target_features>
+posix1
+posix_mlock
+clock_gettime
+dev_random
+arc4random
+getentropy
+explicit_bzero
+pledge
+
+atomics
+sockets
+threads
+thread_local
+filesystem
+</target_features>
diff --git a/comm/third_party/botan/src/build-data/os/qnx.txt b/comm/third_party/botan/src/build-data/os/qnx.txt
new file mode 100644
index 0000000000..feedf5f753
--- /dev/null
+++ b/comm/third_party/botan/src/build-data/os/qnx.txt
@@ -0,0 +1,18 @@
+soname_suffix "so"
+
+<target_features>
+posix1
+posix_mlock
+clock_gettime
+dev_random
+
+atomics
+sockets
+threads
+thread_local
+filesystem
+</target_features>
+
+<feature_macros>
+_QNX_SOURCE
+</feature_macros>
diff --git a/comm/third_party/botan/src/build-data/os/solaris.txt b/comm/third_party/botan/src/build-data/os/solaris.txt
new file mode 100644
index 0000000000..4ca9b0e8ca
--- /dev/null
+++ b/comm/third_party/botan/src/build-data/os/solaris.txt
@@ -0,0 +1,21 @@
+
+soname_suffix "so"
+
+<target_features>
+posix1
+posix_mlock
+clock_gettime
+dev_random
+proc_fs
+
+atomics
+threads
+thread_local
+sockets
+filesystem
+setppriv
+</target_features>
+
+<aliases>
+sunos
+</aliases>
diff --git a/comm/third_party/botan/src/build-data/os/uwp.txt b/comm/third_party/botan/src/build-data/os/uwp.txt
new file mode 100644
index 0000000000..eb79c65ed2
--- /dev/null
+++ b/comm/third_party/botan/src/build-data/os/uwp.txt
@@ -0,0 +1,25 @@
+
+# ?
+program_suffix .exe
+obj_suffix obj
+static_suffix lib
+
+install_root c:\\Botan
+doc_dir docs
+
+<target_features>
+win32
+winsock2
+crypto_ng
+
+rtlsecurezeromemory
+
+atomics
+threads
+thread_local
+filesystem
+</target_features>
+
+<aliases>
+winphone
+</aliases>
diff --git a/comm/third_party/botan/src/build-data/os/windows.txt b/comm/third_party/botan/src/build-data/os/windows.txt
new file mode 100644
index 0000000000..07bfefb13b
--- /dev/null
+++ b/comm/third_party/botan/src/build-data/os/windows.txt
@@ -0,0 +1,48 @@
+
+cli_exe_name botan-cli
+
+program_suffix .exe
+obj_suffix obj
+static_suffix lib
+lib_prefix ''
+
+shared_lib_symlinks no
+
+default_compiler msvc
+
+uses_pkg_config no
+
+# For historical reasons? the library does not have the major number on Windows
+# This should probably be fixed in a future major release.
+library_name 'botan{suffix}'
+
+soname_pattern_base "{libname}.dll"
+
+install_root c:\\Botan
+doc_dir docs
+
+<feature_macros>
+_WIN32_WINNT=0x0600
+</feature_macros>
+
+<target_features>
+win32
+winsock2
+
+rtlgenrandom
+rtlsecurezeromemory
+
+virtual_lock
+
+atomics
+threads
+thread_local
+filesystem
+
+certificate_store
+</target_features>
+
+<aliases>
+win32
+MSWin32
+</aliases>
diff --git a/comm/third_party/botan/src/build-data/policy/bsi.txt b/comm/third_party/botan/src/build-data/policy/bsi.txt
new file mode 100644
index 0000000000..719afc8368
--- /dev/null
+++ b/comm/third_party/botan/src/build-data/policy/bsi.txt
@@ -0,0 +1,188 @@
+<required>
+# block
+aes
+
+# modes
+ccm
+gcm
+cbc
+mode_pad
+
+# stream
+ctr
+
+# hash
+sha2_32
+sha2_64
+sha3
+
+# mac
+cmac
+hmac
+gmac
+
+# kdf
+kdf1_iso18033
+sp800_108
+sp800_56c
+
+# pk_pad
+eme_oaep
+emsa_pssr
+emsa1
+iso9796
+
+# pubkey
+dlies
+dh
+rsa
+dsa
+ecdsa
+ecgdsa
+ecies
+eckcdsa
+ecdh
+xmss
+
+# rng
+auto_rng
+hmac_drbg
+</required>
+
+<if_available>
+# block
+aes_ni
+aes_vperm
+aes_armv8
+aes_power8
+
+# modes
+ghash_cpu
+ghash_vperm
+
+# hash
+sha2_32_x86
+sha2_32_armv8
+sha2_32_bmi2
+sha2_64_bmi2
+sha3_bmi2
+
+# entropy sources
+dev_random
+proc_walk
+rdseed
+win32_stats
+
+# rng
+processor_rng
+system_rng
+
+# utils
+http_util # needed by x509 for OCSP online checks
+locking_allocator
+simd
+</if_available>
+
+<prohibited>
+# block
+aria
+blowfish
+camellia
+cascade
+cast128
+cast256
+des
+gost_28147
+idea
+idea_sse2
+kasumi
+lion
+misty1
+noekeon
+noekeon_simd
+seed
+serpent
+serpent_simd
+serpent_avx2
+shacal2
+shacal2_x86
+shacal2_simd
+sm4
+threefish_512
+threefish_512_avx2
+twofish
+xtea
+
+# modes
+chacha20poly1305
+eax
+ocb
+siv
+cfb
+
+# stream
+chacha
+chacha_simd32
+chacha_avx2
+ofb
+rc4
+salsa20
+shake_cipher
+
+# kdf
+hkdf
+kdf1
+kdf2
+prf_x942
+sp800_56a
+
+# pubkey
+cecpq1
+curve25519
+ed25519
+elgamal
+gost_3410
+mce
+mceies
+rfc6979
+newhope
+sm2
+
+# pk_pad
+#eme_pkcs1 // needed for tls
+#emsa_pkcs1 // needed for tls
+emsa_raw
+emsa_x931
+
+# hash
+blake2
+comb4p
+gost_3411
+md4
+md5
+rmd160
+shake
+skein
+#sha1 // needed for x509
+sm3
+streebog
+tiger
+whirlpool
+keccak
+
+# rng
+chacha_rng
+
+# mac
+cbc_mac
+poly1305
+siphash
+x919_mac
+
+# misc
+bcrypt
+
+# tls
+tls_10
+
+</prohibited>
diff --git a/comm/third_party/botan/src/build-data/policy/modern.txt b/comm/third_party/botan/src/build-data/policy/modern.txt
new file mode 100644
index 0000000000..ce2b3fd2ab
--- /dev/null
+++ b/comm/third_party/botan/src/build-data/policy/modern.txt
@@ -0,0 +1,131 @@
+<required>
+aes
+serpent
+threefish_512
+chacha
+
+sha2_32
+sha2_64
+blake2
+skein
+keccak
+sha3
+
+gcm
+ocb
+chacha20poly1305
+
+kdf2
+hkdf
+cmac
+hmac
+poly1305
+siphash
+
+pbkdf2
+bcrypt
+
+# required for private key encryption
+pbes2
+
+ed25519
+curve25519
+ecdh
+ecdsa
+rsa
+rfc6979
+
+eme_oaep
+emsa_pssr
+emsa1
+
+auto_rng
+hmac_drbg
+</required>
+
+<if_available>
+ffi
+
+tls
+prf_tls
+newhope
+ed25519
+
+ghash_cpu
+ghash_vperm
+
+locking_allocator
+http_util # needed by x509 for OCSP online checks
+
+aes_ni
+aes_vperm
+aes_armv8
+aes_power8
+serpent_simd
+serpent_avx2
+threefish_512_avx2
+chacha_simd32
+chacha_avx2
+
+sha1_sse2
+sha1_x86
+sha1_armv8
+sha2_32_x86
+sha2_32_armv8
+sha2_32_bmi2
+sha2_64_bmi2
+sha3_bmi2
+
+simd
+
+sessions_sql
+certstor_sql
+
+system_rng
+processor_rng
+
+# entropy sources
+dev_random
+proc_walk
+rdseed
+win32_stats
+</if_available>
+
+<prohibited>
+# Just say no to TLS 1.0
+tls_cbc
+
+cast128
+cast256
+des
+gost_28147
+idea
+idea_sse2
+kasumi
+lion
+misty1
+rc4
+seed
+xtea
+
+cbc_mac
+x919_mac
+
+# MD5 and SHA1 are broken but not prohibited. They are widely in use
+# in non-crypto contexts and are required by TLS currently
+md4
+gost_3411
+
+cfb
+ofb
+
+elgamal
+gost_3410
+
+emsa_x931
+pbkdf1
+prf_x942
+
+passhash9
+cryptobox
+</prohibited>
diff --git a/comm/third_party/botan/src/build-data/policy/nist.txt b/comm/third_party/botan/src/build-data/policy/nist.txt
new file mode 100644
index 0000000000..e4a19b4fea
--- /dev/null
+++ b/comm/third_party/botan/src/build-data/policy/nist.txt
@@ -0,0 +1,187 @@
+<required>
+des
+aes
+
+gcm
+ccm
+ctr
+cbc
+mode_pad
+
+# hash
+sha2_32
+sha2_64
+sha3
+
+# mac
+cmac
+hmac
+gmac
+
+# kdf
+sp800_108
+sp800_56a
+sp800_56c
+
+shake
+
+# pk_pad
+eme_oaep
+emsa_pssr
+emsa1
+
+# pubkey
+dh
+rsa
+dsa
+ecdsa
+ecdh
+
+# rng
+auto_rng
+hmac_drbg
+
+# keywrap
+rfc3394
+</required>
+
+<if_available>
+# block
+aes_ni
+aes_vperm
+aes_armv8
+aes_power8
+
+# hash
+sha2_32_x86
+sha2_32_armv8
+sha2_32_bmi2
+sha2_64_bmi2
+sha3_bmi2
+
+# modes
+ghash_cpu
+ghash_vperm
+
+# hash
+sha2_32_x86
+sha2_32_armv8
+
+# entropy sources
+dev_random
+proc_walk
+rdseed
+win32_stats
+
+# rng
+system_rng
+
+# utils
+http_util # needed by x509 for OCSP online checks
+locking_allocator
+simd
+</if_available>
+
+<prohibited>
+# block
+aria
+blowfish
+camellia
+cascade
+cast128
+cast256
+gost_28147
+idea
+idea_sse2
+kasumi
+lion
+misty1
+noekeon
+noekeon_simd
+seed
+serpent
+serpent_simd
+serpent_avx2
+sm4
+shacal2
+shacal2_x86
+shacal2_simd
+threefish_512
+threefish_512_avx2
+twofish
+xtea
+
+# modes
+chacha20poly1305
+eax
+ocb
+siv
+cfb
+
+# stream
+chacha
+chacha_simd32
+chacha_avx2
+shake_cipher
+ofb
+rc4
+salsa20
+
+# kdf
+hkdf
+kdf1
+kdf2
+prf_x942
+
+# pubkey
+curve25519
+ed25519
+ecgdsa
+eckcdsa
+elgamal
+gost_3410
+mce
+mceies
+rfc6979
+newhope
+cecpq1
+xmss
+sm2
+
+# pk_pad
+#eme_pkcs1 // needed for tls
+#emsa_pkcs1 // needed for tls
+emsa_raw
+emsa_x931
+
+# hash
+blake2
+comb4p
+gost_3411
+md5
+md4
+rmd160
+skein
+#sha1 // needed for x509
+sm3
+streebog
+tiger
+whirlpool
+
+# rng
+chacha_rng
+
+# mac
+cbc_mac
+poly1305
+siphash
+x919_mac
+
+# misc
+bcrypt
+
+# tls
+tls_10
+tls_cbc
+
+</prohibited>
diff --git a/comm/third_party/botan/src/build-data/version.txt b/comm/third_party/botan/src/build-data/version.txt
new file mode 100644
index 0000000000..e6fa783442
--- /dev/null
+++ b/comm/third_party/botan/src/build-data/version.txt
@@ -0,0 +1,11 @@
+
+release_major = 2
+release_minor = 18
+release_patch = 2
+release_suffix = ''
+release_so_abi_rev = 18
+
+# These are set by the distribution script
+release_vc_rev = 'git:a44f1489239e80937ca67564ff103421e5584069'
+release_datestamp = 20211025
+release_type = 'release'
diff --git a/comm/third_party/botan/src/cli/argon2.cpp b/comm/third_party/botan/src/cli/argon2.cpp
new file mode 100644
index 0000000000..2b07027c33
--- /dev/null
+++ b/comm/third_party/botan/src/cli/argon2.cpp
@@ -0,0 +1,78 @@
+/*
+* (C) 2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "cli.h"
+
+#if defined(BOTAN_HAS_ARGON2)
+ #include <botan/argon2.h>
+#endif
+
+namespace Botan_CLI {
+
+#if defined(BOTAN_HAS_ARGON2)
+
+class Generate_Argon2 final : public Command
+ {
+ public:
+ Generate_Argon2() : Command("gen_argon2 --mem=65536 --p=1 --t=1 password") {}
+
+ std::string group() const override
+ {
+ return "passhash";
+ }
+
+ std::string description() const override
+ {
+ return "Calculate Argon2 password hash";
+ }
+
+ void go() override
+ {
+ const std::string password = get_passphrase_arg("Passphrase to hash", "password");
+ const size_t M = get_arg_sz("mem");
+ const size_t p = get_arg_sz("p");
+ const size_t t = get_arg_sz("t");
+
+ output() << Botan::argon2_generate_pwhash(password.data(), password.size(), rng(), p, M, t) << "\n";
+ }
+ };
+
+BOTAN_REGISTER_COMMAND("gen_argon2", Generate_Argon2);
+
+class Check_Argon2 final : public Command
+ {
+ public:
+ Check_Argon2() : Command("check_argon2 password hash") {}
+
+ std::string group() const override
+ {
+ return "passhash";
+ }
+
+ std::string description() const override
+ {
+ return "Verify Argon2 password hash";
+ }
+
+ void go() override
+ {
+ const std::string password = get_passphrase_arg("Password to check", "password");
+ const std::string hash = get_arg("hash");
+
+ const bool ok = Botan::argon2_check_pwhash(password.data(), password.size(), hash);
+
+ output() << "Password is " << (ok ? "valid" : "NOT valid") << std::endl;
+
+ if(ok == false)
+ set_return_code(1);
+ }
+ };
+
+BOTAN_REGISTER_COMMAND("check_argon2", Check_Argon2);
+
+#endif // argon2
+
+}
diff --git a/comm/third_party/botan/src/cli/argparse.h b/comm/third_party/botan/src/cli/argparse.h
new file mode 100644
index 0000000000..4df4a10c83
--- /dev/null
+++ b/comm/third_party/botan/src/cli/argparse.h
@@ -0,0 +1,280 @@
+/*
+* (C) 2015,2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_CLI_ARGPARSE_H_
+#define BOTAN_CLI_ARGPARSE_H_
+
+#include <string>
+#include <map>
+#include <set>
+#include <vector>
+#include <botan/parsing.h>
+#include "cli_exceptions.h"
+
+namespace Botan_CLI {
+
+class Argument_Parser final
+ {
+ public:
+ Argument_Parser(const std::string& spec,
+ const std::vector<std::string>& extra_flags = {},
+ const std::vector<std::string>& extra_opts = {});
+
+ void parse_args(const std::vector<std::string>& params);
+
+ bool flag_set(const std::string& flag) const;
+
+ bool has_arg(const std::string& opt_name) const;
+ std::string get_arg(const std::string& option) const;
+
+ std::string get_arg_or(const std::string& option, const std::string& otherwise) const;
+
+ size_t get_arg_sz(const std::string& option) const;
+
+ std::vector<std::string> get_arg_list(const std::string& what) const;
+
+ private:
+ // set in constructor
+ std::vector<std::string> m_spec_args;
+ std::set<std::string> m_spec_flags;
+ std::map<std::string, std::string> m_spec_opts;
+ std::string m_spec_rest;
+
+ // set in parse_args()
+ std::map<std::string, std::string> m_user_args;
+ std::set<std::string> m_user_flags;
+ std::vector<std::string> m_user_rest;
+ };
+
+bool Argument_Parser::flag_set(const std::string& flag_name) const
+ {
+ return m_user_flags.count(flag_name) > 0;
+ }
+
+bool Argument_Parser::has_arg(const std::string& opt_name) const
+ {
+ return m_user_args.count(opt_name) > 0;
+ }
+
+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
+ throw CLI_Error("Unknown option " + opt_name + " used (program bug)");
+ }
+ return i->second;
+ }
+
+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;
+ }
+ return i->second;
+ }
+
+size_t Argument_Parser::get_arg_sz(const std::string& opt_name) const
+ {
+ const std::string s = get_arg(opt_name);
+
+ try
+ {
+ return static_cast<size_t>(std::stoul(s));
+ }
+ catch(std::exception&)
+ {
+ throw CLI_Usage_Error("Invalid integer value '" + s + "' for option " + opt_name);
+ }
+ }
+
+std::vector<std::string> Argument_Parser::get_arg_list(const std::string& what) const
+ {
+ if(what == m_spec_rest)
+ return m_user_rest;
+
+ return Botan::split_on(get_arg(what), ',');
+ }
+
+void Argument_Parser::parse_args(const std::vector<std::string>& params)
+ {
+ std::vector<std::string> args;
+ for(auto const& param : params)
+ {
+ if(param.find("--") == 0)
+ {
+ // option
+ const auto eq = param.find('=');
+
+ if(eq == std::string::npos)
+ {
+ const std::string opt_name = param.substr(2, std::string::npos);
+
+ if(m_spec_flags.count(opt_name) == 0)
+ {
+ if(m_spec_opts.count(opt_name))
+ {
+ throw CLI_Usage_Error("Invalid usage of option --" + opt_name +
+ " without value");
+ }
+ else
+ {
+ throw CLI_Usage_Error("Unknown flag --" + opt_name);
+ }
+ }
+ m_user_flags.insert(opt_name);
+ }
+ else
+ {
+ const std::string opt_name = param.substr(2, eq - 2);
+ const std::string opt_val = param.substr(eq + 1, std::string::npos);
+
+ if(m_spec_opts.count(opt_name) == 0)
+ {
+ throw CLI_Usage_Error("Unknown option --" + opt_name);
+ }
+
+ m_user_args.insert(std::make_pair(opt_name, opt_val));
+ }
+ }
+ else
+ {
+ // argument
+ args.push_back(param);
+ }
+ }
+
+ if(flag_set("help"))
+ return;
+
+ if(args.size() < m_spec_args.size())
+ {
+ // not enough arguments
+ throw CLI_Usage_Error("Invalid argument count, got " +
+ std::to_string(args.size()) +
+ " expected " +
+ std::to_string(m_spec_args.size()));
+ }
+
+ bool seen_stdin_flag = false;
+ size_t arg_i = 0;
+ for(auto const& arg : m_spec_args)
+ {
+ m_user_args.insert(std::make_pair(arg, args[arg_i]));
+
+ if(args[arg_i] == "-")
+ {
+ if(seen_stdin_flag)
+ {
+ throw CLI_Usage_Error("Cannot specify '-' (stdin) more than once");
+ }
+ seen_stdin_flag = true;
+ }
+
+ ++arg_i;
+ }
+
+ if(m_spec_rest.empty())
+ {
+ if(arg_i != args.size())
+ {
+ throw CLI_Usage_Error("Too many arguments");
+ }
+ }
+ else
+ {
+ m_user_rest.assign(args.begin() + arg_i, args.end());
+ }
+
+ // Now insert any defaults for options not supplied by the user
+ for(auto const& opt : m_spec_opts)
+ {
+ if(m_user_args.count(opt.first) == 0)
+ {
+ m_user_args.insert(opt);
+ }
+ }
+ }
+
+Argument_Parser::Argument_Parser(const std::string& spec,
+ const std::vector<std::string>& extra_flags,
+ const std::vector<std::string>& extra_opts)
+ {
+ class CLI_Error_Invalid_Spec final : public CLI_Error
+ {
+ public:
+ explicit CLI_Error_Invalid_Spec(const std::string& bad_spec)
+ : CLI_Error("Invalid command spec '" + bad_spec + "'") {}
+ };
+
+ const std::vector<std::string> parts = Botan::split_on(spec, ' ');
+
+ if(parts.size() == 0)
+ {
+ throw CLI_Error_Invalid_Spec(spec);
+ }
+
+ for(size_t i = 1; i != parts.size(); ++i)
+ {
+ const std::string s = parts[i];
+
+ if(s.empty()) // ?!? (shouldn't happen)
+ {
+ throw CLI_Error_Invalid_Spec(spec);
+ }
+
+ if(s.size() > 2 && s[0] == '-' && s[1] == '-')
+ {
+ // option or flag
+
+ auto eq = s.find('=');
+
+ if(eq == std::string::npos)
+ {
+ m_spec_flags.insert(s.substr(2, std::string::npos));
+ }
+ else
+ {
+ m_spec_opts.insert(std::make_pair(s.substr(2, eq - 2), s.substr(eq + 1, std::string::npos)));
+ }
+ }
+ else if(s[0] == '*')
+ {
+ // rest argument
+ if(m_spec_rest.empty() && s.size() > 2)
+ {
+ m_spec_rest = s.substr(1, std::string::npos);
+ }
+ else
+ {
+ throw CLI_Error_Invalid_Spec(spec);
+ }
+ }
+ else
+ {
+ // named argument
+ if(!m_spec_rest.empty()) // rest arg wasn't last
+ {
+ throw CLI_Error_Invalid_Spec(spec);
+ }
+
+ m_spec_args.push_back(s);
+ }
+ }
+
+ for(std::string flag : extra_flags)
+ m_spec_flags.insert(flag);
+ for(std::string opt : extra_opts)
+ m_spec_opts.insert(std::make_pair(opt, ""));
+ }
+
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/cli/asn1.cpp b/comm/third_party/botan/src/cli/asn1.cpp
new file mode 100644
index 0000000000..32cec2c253
--- /dev/null
+++ b/comm/third_party/botan/src/cli/asn1.cpp
@@ -0,0 +1,89 @@
+/*
+* (C) 2014,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "cli.h"
+
+#if defined(BOTAN_HAS_ASN1)
+
+#include <botan/asn1_print.h>
+#include <botan/data_src.h>
+
+#if defined(BOTAN_HAS_PEM_CODEC)
+ #include <botan/pem.h>
+#endif
+
+namespace Botan_CLI {
+
+class ASN1_Printer final : public Command
+ {
+ public:
+ ASN1_Printer() : Command("asn1print --skip-context-specific --print-limit=4096 --bin-limit=2048 --max-depth=64 --pem file") {}
+
+ std::string group() const override
+ {
+ return "codec";
+ }
+
+ std::string description() const override
+ {
+ return "Decode and print file with ASN.1 Basic Encoding Rules (BER)";
+ }
+
+ bool first_n(const std::vector<uint8_t>& data, size_t n, uint8_t b)
+ {
+ if(data.size() < n)
+ return false;
+
+ for(size_t i = 0; i != n; ++i)
+ if(data[i] != b)
+ return false;
+
+ return true;
+ }
+
+ void go() override
+ {
+ 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 size_t max_depth = get_arg_sz("max-depth");
+
+ const size_t value_column = 60;
+ const size_t initial_level = 0;
+
+ std::vector<uint8_t> file_contents = slurp_file(input);
+ std::vector<uint8_t> data;
+
+ if(flag_set("pem") ||
+ (input.size() > 4 && input.substr(input.size() - 4) == ".pem") ||
+ (file_contents.size() > 20 && first_n(file_contents, 5, '-')))
+ {
+#if defined(BOTAN_HAS_PEM_CODEC)
+ std::string pem_label;
+ Botan::DataSource_Memory src(file_contents);
+ data = unlock(Botan::PEM_Code::decode(src, pem_label));
+#else
+ throw CLI_Error_Unsupported("PEM decoding not available in this build");
+#endif
+ }
+ else
+ {
+ data.swap(file_contents);
+ }
+
+ 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());
+ }
+ };
+
+BOTAN_REGISTER_COMMAND("asn1print", ASN1_Printer);
+
+}
+
+#endif // BOTAN_HAS_ASN1 && BOTAN_HAS_PEM_CODEC
diff --git a/comm/third_party/botan/src/cli/bcrypt.cpp b/comm/third_party/botan/src/cli/bcrypt.cpp
new file mode 100644
index 0000000000..68e77b8e60
--- /dev/null
+++ b/comm/third_party/botan/src/cli/bcrypt.cpp
@@ -0,0 +1,89 @@
+/*
+* (C) 2009,2010,2014,2015,2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "cli.h"
+
+#if defined(BOTAN_HAS_BCRYPT)
+ #include <botan/bcrypt.h>
+#endif
+
+namespace Botan_CLI {
+
+#if defined(BOTAN_HAS_BCRYPT)
+
+class Generate_Bcrypt final : public Command
+ {
+ public:
+ Generate_Bcrypt() : Command("gen_bcrypt --work-factor=12 password") {}
+
+ std::string group() const override
+ {
+ return "passhash";
+ }
+
+ std::string description() const override
+ {
+ return "Calculate bcrypt password hash";
+ }
+
+ void go() override
+ {
+ const std::string password = get_passphrase_arg("Passphrase to hash", "password");
+ const size_t wf = get_arg_sz("work-factor");
+
+ if(wf < 4 || wf > 18)
+ {
+ error_output() << "Invalid bcrypt work factor\n";
+ }
+ else
+ {
+ const uint16_t wf16 = static_cast<uint16_t>(wf);
+ output() << Botan::generate_bcrypt(password, rng(), wf16) << "\n";
+ }
+ }
+ };
+
+BOTAN_REGISTER_COMMAND("gen_bcrypt", Generate_Bcrypt);
+
+class Check_Bcrypt final : public Command
+ {
+ public:
+ Check_Bcrypt() : Command("check_bcrypt password hash") {}
+
+ std::string group() const override
+ {
+ return "passhash";
+ }
+
+ std::string description() const override
+ {
+ return "Verify bcrypt password hash";
+ }
+
+ void go() override
+ {
+ const std::string password = get_passphrase_arg("Password to check", "password");
+ const std::string hash = get_arg("hash");
+
+ if(hash.length() != 60)
+ {
+ error_output() << "Note: bcrypt '" << hash << "' has wrong length and cannot be valid\n";
+ }
+
+ const bool ok = Botan::check_bcrypt(password, hash);
+
+ output() << "Password is " << (ok ? "valid" : "NOT valid") << std::endl;
+
+ if(ok == false)
+ set_return_code(1);
+ }
+ };
+
+BOTAN_REGISTER_COMMAND("check_bcrypt", Check_Bcrypt);
+
+#endif // bcrypt
+
+}
diff --git a/comm/third_party/botan/src/cli/cc_enc.cpp b/comm/third_party/botan/src/cli/cc_enc.cpp
new file mode 100644
index 0000000000..509b996012
--- /dev/null
+++ b/comm/third_party/botan/src/cli/cc_enc.cpp
@@ -0,0 +1,189 @@
+/*
+* (C) 2014,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "cli.h"
+#include <botan/hex.h>
+
+#if defined(BOTAN_HAS_FPE_FE1) && defined(BOTAN_HAS_PBKDF)
+
+#include <botan/fpe_fe1.h>
+#include <botan/pbkdf.h>
+
+namespace Botan_CLI {
+
+namespace {
+
+uint8_t luhn_checksum(uint64_t cc_number)
+ {
+ uint8_t sum = 0;
+
+ bool alt = false;
+ while(cc_number)
+ {
+ uint8_t digit = cc_number % 10;
+ if(alt)
+ {
+ digit *= 2;
+ if(digit > 9)
+ {
+ digit -= 9;
+ }
+ }
+
+ sum += digit;
+
+ cc_number /= 10;
+ alt = !alt;
+ }
+
+ return (sum % 10);
+ }
+
+bool luhn_check(uint64_t cc_number)
+ {
+ return (luhn_checksum(cc_number) == 0);
+ }
+
+uint64_t cc_rank(uint64_t cc_number)
+ {
+ // Remove Luhn checksum
+ return cc_number / 10;
+ }
+
+uint64_t cc_derank(uint64_t cc_number)
+ {
+ for(size_t i = 0; i != 10; ++i)
+ {
+ if(luhn_check(cc_number * 10 + i))
+ {
+ return (cc_number * 10 + i);
+ }
+ }
+
+ return 0;
+ }
+
+uint64_t encrypt_cc_number(uint64_t cc_number,
+ const Botan::secure_vector<uint8_t>& key,
+ const std::vector<uint8_t>& tweak)
+ {
+ 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);
+
+ if(c.bits() > 50)
+ {
+ throw Botan::Internal_Error("FPE produced a number too large");
+ }
+
+ uint64_t enc_cc = 0;
+ for(size_t i = 0; i != 7; ++i)
+ {
+ enc_cc = (enc_cc << 8) | c.byte_at(6 - i);
+ }
+ return cc_derank(enc_cc);
+ }
+
+uint64_t decrypt_cc_number(uint64_t enc_cc,
+ const Botan::secure_vector<uint8_t>& key,
+ const std::vector<uint8_t>& tweak)
+ {
+ const Botan::BigInt n = 1000000000000000;
+
+ const uint64_t cc_ranked = cc_rank(enc_cc);
+
+ const Botan::BigInt c = Botan::FPE::fe1_decrypt(n, cc_ranked, key, tweak);
+
+ if(c.bits() > 50)
+ {
+ throw CLI_Error("FPE produced a number too large");
+ }
+
+ uint64_t dec_cc = 0;
+ for(size_t i = 0; i != 7; ++i)
+ {
+ dec_cc = (dec_cc << 8) | c.byte_at(6 - i);
+ }
+ return cc_derank(dec_cc);
+ }
+
+}
+
+class CC_Encrypt final : public Command
+ {
+ public:
+ CC_Encrypt() : Command("cc_encrypt CC passphrase --tweak=") {}
+
+ std::string group() const override
+ {
+ return "misc";
+ }
+
+ std::string description() const override
+ {
+ return "Encrypt the passed valid credit card number using FPE encryption";
+ }
+
+ void go() override
+ {
+ const uint64_t cc_number = std::stoull(get_arg("CC"));
+ const std::vector<uint8_t> tweak = Botan::hex_decode(get_arg("tweak"));
+ const std::string pass = get_arg("passphrase");
+
+ std::unique_ptr<Botan::PBKDF> pbkdf(Botan::PBKDF::create("PBKDF2(SHA-256)"));
+ if(!pbkdf)
+ {
+ throw CLI_Error_Unsupported("PBKDF", "PBKDF2(SHA-256)");
+ }
+
+ Botan::secure_vector<uint8_t> key = pbkdf->pbkdf_iterations(32, pass, tweak.data(), tweak.size(), 100000);
+
+ output() << encrypt_cc_number(cc_number, key, tweak) << "\n";
+ }
+ };
+
+BOTAN_REGISTER_COMMAND("cc_encrypt", CC_Encrypt);
+
+class CC_Decrypt final : public Command
+ {
+ public:
+ CC_Decrypt() : Command("cc_decrypt CC passphrase --tweak=") {}
+
+ std::string group() const override
+ {
+ return "misc";
+ }
+
+ std::string description() const override
+ {
+ return "Decrypt the passed valid ciphertext credit card number using FPE decryption";
+ }
+
+ void go() override
+ {
+ const uint64_t cc_number = std::stoull(get_arg("CC"));
+ const std::vector<uint8_t> tweak = Botan::hex_decode(get_arg("tweak"));
+ const std::string pass = get_arg("passphrase");
+
+ std::unique_ptr<Botan::PBKDF> pbkdf(Botan::PBKDF::create("PBKDF2(SHA-256)"));
+ if(!pbkdf)
+ {
+ throw CLI_Error_Unsupported("PBKDF", "PBKDF2(SHA-256)");
+ }
+
+ Botan::secure_vector<uint8_t> key = pbkdf->pbkdf_iterations(32, pass, tweak.data(), tweak.size(), 100000);
+
+ output() << decrypt_cc_number(cc_number, key, tweak) << "\n";
+ }
+ };
+
+BOTAN_REGISTER_COMMAND("cc_decrypt", CC_Decrypt);
+
+}
+
+#endif // FPE && PBKDF
diff --git a/comm/third_party/botan/src/cli/cli.cpp b/comm/third_party/botan/src/cli/cli.cpp
new file mode 100644
index 0000000000..1fc5ed116e
--- /dev/null
+++ b/comm/third_party/botan/src/cli/cli.cpp
@@ -0,0 +1,349 @@
+/*
+* (C) 2015,2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "cli.h"
+#include "argparse.h"
+#include <botan/rng.h>
+#include <botan/parsing.h>
+#include <botan/internal/os_utils.h>
+#include <iostream>
+#include <fstream>
+
+#if defined(BOTAN_HAS_HEX_CODEC)
+ #include <botan/hex.h>
+#endif
+
+#if defined(BOTAN_HAS_BASE64_CODEC)
+ #include <botan/base64.h>
+#endif
+
+#if defined(BOTAN_HAS_BASE58_CODEC)
+ #include <botan/base58.h>
+#endif
+
+namespace Botan_CLI {
+
+Command::Command(const std::string& cmd_spec) : m_spec(cmd_spec)
+ {
+ // for checking all spec strings at load time
+ //m_args.reset(new Argument_Parser(m_spec));
+ }
+
+Command::~Command() { /* for unique_ptr */ }
+
+std::string Command::cmd_name() const
+ {
+ return m_spec.substr(0, m_spec.find(' '));
+ }
+
+std::string Command::help_text() const
+ {
+ return "Usage: " + m_spec;
+ }
+
+int Command::run(const std::vector<std::string>& params)
+ {
+ try
+ {
+ m_args.reset(new Argument_Parser(m_spec,
+ {"verbose", "help"},
+ {"output", "error-output", "rng-type", "drbg-seed"}));
+
+ m_args->parse_args(params);
+
+ if(m_args->has_arg("output"))
+ {
+ const std::string output_file = get_arg("output");
+
+ if(output_file != "")
+ {
+ m_output_stream.reset(new std::ofstream(output_file, std::ios::binary));
+ if(!m_output_stream->good())
+ throw CLI_IO_Error("opening", output_file);
+ }
+ }
+
+ if(m_args->has_arg("error-output"))
+ {
+ const std::string output_file = get_arg("error-output");
+
+ if(output_file != "")
+ {
+ m_error_output_stream.reset(new std::ofstream(output_file, std::ios::binary));
+ if(!m_error_output_stream->good())
+ throw CLI_IO_Error("opening", output_file);
+ }
+ }
+
+ if(flag_set("help"))
+ {
+ output() << help_text() << "\n";
+ return 2;
+ }
+
+ this->go();
+ return m_return_code;
+ }
+ catch(CLI_Usage_Error& e)
+ {
+ error_output() << "Usage error: " << e.what() << "\n";
+ error_output() << help_text() << "\n";
+ return 1;
+ }
+ catch(std::exception& e)
+ {
+ error_output() << "Error: " << e.what() << "\n";
+ return 2;
+ }
+ catch(...)
+ {
+ error_output() << "Error: unknown exception\n";
+ return 2;
+ }
+ }
+
+bool Command::flag_set(const std::string& flag_name) const
+ {
+ return m_args->flag_set(flag_name);
+ }
+
+std::string Command::get_arg(const std::string& opt_name) const
+ {
+ return m_args->get_arg(opt_name);
+ }
+
+/*
+* Like get_arg() but if the argument was not specified or is empty, returns otherwise
+*/
+std::string Command::get_arg_or(const std::string& opt_name, const std::string& otherwise) const
+ {
+ return m_args->get_arg_or(opt_name, otherwise);
+ }
+
+size_t Command::get_arg_sz(const std::string& opt_name) const
+ {
+ return m_args->get_arg_sz(opt_name);
+ }
+
+uint16_t Command::get_arg_u16(const std::string& opt_name) const
+ {
+ const size_t val = get_arg_sz(opt_name);
+ if(static_cast<uint16_t>(val) != val)
+ throw CLI_Usage_Error("Argument " + opt_name + " has value out of allowed range");
+ return static_cast<uint16_t>(val);
+ }
+
+uint32_t Command::get_arg_u32(const std::string& opt_name) const
+ {
+ const size_t val = get_arg_sz(opt_name);
+ if(static_cast<uint32_t>(val) != val)
+ throw CLI_Usage_Error("Argument " + opt_name + " has value out of allowed range");
+ return static_cast<uint32_t>(val);
+ }
+
+std::vector<std::string> Command::get_arg_list(const std::string& what) const
+ {
+ return m_args->get_arg_list(what);
+ }
+
+std::ostream& Command::output()
+ {
+ if(m_output_stream.get())
+ {
+ return *m_output_stream;
+ }
+ return std::cout;
+ }
+
+std::ostream& Command::error_output()
+ {
+ if(m_error_output_stream.get())
+ {
+ return *m_error_output_stream;
+ }
+ return std::cerr;
+ }
+
+std::vector<uint8_t> Command::slurp_file(const std::string& input_file,
+ size_t buf_size) const
+ {
+ std::vector<uint8_t> buf;
+ auto insert_fn = [&](const uint8_t b[], size_t l)
+ {
+ buf.insert(buf.end(), b, b + l);
+ };
+ this->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
+ {
+ std::string str;
+ auto insert_fn = [&](const uint8_t b[], size_t l)
+ {
+ str.append(reinterpret_cast<const char*>(b), l);
+ };
+ this->read_file(input_file, insert_fn, buf_size);
+ return str;
+ }
+
+void Command::read_file(const std::string& input_file,
+ std::function<void (uint8_t[], size_t)> consumer_fn,
+ size_t buf_size) const
+ {
+ if(input_file == "-")
+ {
+ do_read_file(std::cin, consumer_fn, buf_size);
+ }
+ else
+ {
+ std::ifstream in(input_file, std::ios::binary);
+ if(!in)
+ {
+ throw CLI_IO_Error("reading file", input_file);
+ }
+ do_read_file(in, consumer_fn, buf_size);
+ }
+ }
+
+void Command::do_read_file(std::istream& in,
+ std::function<void (uint8_t[], size_t)> consumer_fn,
+ size_t buf_size) const
+ {
+ // Avoid an infinite loop on --buf-size=0
+ std::vector<uint8_t> buf(buf_size == 0 ? 4096 : buf_size);
+
+ while(in.good())
+ {
+ in.read(reinterpret_cast<char*>(buf.data()), buf.size());
+ const size_t got = static_cast<size_t>(in.gcount());
+ consumer_fn(buf.data(), got);
+ }
+ }
+
+Botan::RandomNumberGenerator& Command::rng()
+ {
+ if(m_rng == nullptr)
+ {
+ m_rng = cli_make_rng(get_arg("rng-type"), get_arg("drbg-seed"));
+ }
+
+ return *m_rng.get();
+ }
+
+std::string Command::get_passphrase_arg(const std::string& prompt, const std::string& opt_name)
+ {
+ const std::string s = get_arg(opt_name);
+ if(s != "-")
+ return s;
+ return get_passphrase(prompt);
+ }
+
+namespace {
+
+bool echo_suppression_supported()
+ {
+ auto echo = Botan::OS::suppress_echo_on_terminal();
+ return (echo != nullptr);
+ }
+
+}
+
+std::string Command::get_passphrase(const std::string& prompt)
+ {
+ if(echo_suppression_supported() == false)
+ error_output() << "Warning: terminal echo suppression not enabled for this platform\n";
+
+ error_output() << prompt << ": " << std::flush;
+ std::string pass;
+
+ auto echo_suppress = Botan::OS::suppress_echo_on_terminal();
+
+ std::getline(std::cin, pass);
+
+ return pass;
+ }
+
+//static
+std::string Command::format_blob(const std::string& format,
+ const uint8_t bits[], size_t len)
+ {
+#if defined(BOTAN_HAS_HEX_CODEC)
+ if(format == "hex")
+ {
+ return Botan::hex_encode(bits, len);
+ }
+#endif
+
+#if defined(BOTAN_HAS_BASE64_CODEC)
+ if(format == "base64")
+ {
+ return Botan::base64_encode(bits, len);
+ }
+#endif
+
+#if defined(BOTAN_HAS_BASE58_CODEC)
+ if(format == "base58")
+ {
+ return Botan::base58_encode(bits, len);
+ }
+ if(format == "base58check")
+ {
+ return Botan::base58_check_encode(bits, len);
+ }
+#endif
+
+ // If we supported format, we would have already returned
+ throw CLI_Usage_Error("Unknown or unsupported format type");
+ }
+
+// Registration code
+
+Command::Registration::Registration(const std::string& name, Command::cmd_maker_fn maker_fn)
+ {
+ std::map<std::string, Command::cmd_maker_fn>& reg = Command::global_registry();
+
+ if(reg.count(name) > 0)
+ {
+ throw CLI_Error("Duplicated registration of command " + name);
+ }
+
+ reg.insert(std::make_pair(name, maker_fn));
+ }
+
+//static
+std::map<std::string, Command::cmd_maker_fn>& Command::global_registry()
+ {
+ static std::map<std::string, Command::cmd_maker_fn> g_cmds;
+ return g_cmds;
+ }
+
+//static
+std::vector<std::string> Command::registered_cmds()
+ {
+ std::vector<std::string> cmds;
+ for(auto& cmd : Command::global_registry())
+ cmds.push_back(cmd.first);
+ return cmds;
+ }
+
+//static
+std::unique_ptr<Command> Command::get_cmd(const std::string& name)
+ {
+ const std::map<std::string, Command::cmd_maker_fn>& reg = Command::global_registry();
+
+ std::unique_ptr<Command> r;
+ auto i = reg.find(name);
+ if(i != reg.end())
+ {
+ r.reset(i->second());
+ }
+
+ return r;
+ }
+
+}
diff --git a/comm/third_party/botan/src/cli/cli.h b/comm/third_party/botan/src/cli/cli.h
new file mode 100644
index 0000000000..6ddf34d025
--- /dev/null
+++ b/comm/third_party/botan/src/cli/cli.h
@@ -0,0 +1,219 @@
+/*
+* (C) 2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_CLI_H_
+#define BOTAN_CLI_H_
+
+#include <botan/build.h>
+#include <functional>
+#include <ostream>
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+#include "cli_exceptions.h"
+
+namespace Botan {
+
+class RandomNumberGenerator;
+
+}
+
+namespace Botan_CLI {
+
+class Argument_Parser;
+
+/* Declared in cli_rng.cpp */
+std::unique_ptr<Botan::RandomNumberGenerator>
+cli_make_rng(const std::string& type = "", const std::string& hex_drbg_seed = "");
+
+class Command
+ {
+ public:
+
+ /**
+ * Get a registered command
+ */
+ static std::unique_ptr<Command> get_cmd(const std::string& name);
+
+ static std::vector<std::string> registered_cmds();
+
+ /**
+ * The spec string specifies the format of the command line, eg for
+ * a somewhat complicated command:
+ * cmd_name --flag --option1= --option2=opt2val input1 input2 *rest
+ *
+ * By default this is the value returned by help_text()
+ *
+ * The first value is always the command name. Options may appear
+ * in any order. Named arguments are taken from the command line
+ * in the order they appear in the spec.
+ *
+ * --flag can optionally be specified, and takes no value.
+ * Check for it in go() with flag_set()
+ *
+ * --option1 is an option whose default value (if the option
+ * does not appear on the command line) is the empty string.
+ *
+ * --option2 is an option whose default value is opt2val
+ * Read the values in go() using get_arg or get_arg_sz.
+ *
+ * The values input1 and input2 specify named arguments which must
+ * be provided. They are also access via get_arg/get_arg_sz
+ * Because options and arguments for a single command share the same
+ * namespace you can't have a spec like:
+ * cmd --input input
+ * but you hopefully didn't want to do that anyway.
+ *
+ * The leading '*' on '*rest' specifies that all remaining arguments
+ * should be packaged in a list which is available as get_arg_list("rest").
+ * This can only appear on a single value and should be the final
+ * named argument.
+ *
+ * Every command has implicit flags --help, --verbose and implicit
+ * options --output= and --error-output= which override the default
+ * use of std::cout and std::cerr.
+ *
+ * Use of --help is captured in run() and returns help_text().
+ * Use of --verbose can be checked with verbose() or flag_set("verbose")
+ */
+ explicit Command(const std::string& cmd_spec);
+
+ virtual ~Command();
+
+ int run(const std::vector<std::string>& params);
+
+ virtual std::string group() const = 0;
+
+ virtual std::string description() const = 0;
+
+ virtual std::string help_text() const;
+
+ const std::string& cmd_spec() const
+ {
+ return m_spec;
+ }
+
+ std::string cmd_name() const;
+
+ protected:
+
+ /*
+ * The actual functionality of the cli command implemented in subclass.
+ * The return value from main will be zero.
+ */
+ virtual void go() = 0;
+
+ void set_return_code(int rc) { m_return_code = rc; }
+
+ std::ostream& output();
+
+ std::ostream& error_output();
+
+ bool verbose() const
+ {
+ return flag_set("verbose");
+ }
+
+ std::string get_passphrase(const std::string& prompt);
+
+ bool flag_set(const std::string& flag_name) const;
+
+ static std::string format_blob(const std::string& format, const uint8_t bits[], size_t len);
+
+ template<typename Alloc>
+ static std::string format_blob(const std::string& format,
+ const std::vector<uint8_t, Alloc>& vec)
+ {
+ return format_blob(format, vec.data(), vec.size());
+ }
+
+ std::string get_arg(const std::string& opt_name) const;
+
+ /**
+ * Like get_arg but if the value is '-' then reads a passphrase from
+ * the terminal with echo suppressed.
+ */
+ std::string get_passphrase_arg(const std::string& prompt,
+ const std::string& opt_name);
+
+ /*
+ * Like get_arg() but if the argument was not specified or is empty, returns otherwise
+ */
+ std::string get_arg_or(const std::string& opt_name, const std::string& otherwise) const;
+
+ size_t get_arg_sz(const std::string& opt_name) const;
+
+ uint16_t get_arg_u16(const std::string& opt_name) const;
+
+ uint32_t get_arg_u32(const std::string& opt_name) const;
+
+ std::vector<std::string> get_arg_list(const std::string& what) const;
+
+ /*
+ * Read an entire file into memory and return the contents
+ */
+ std::vector<uint8_t> slurp_file(const std::string& input_file,
+ size_t buf_size = 0) const;
+
+ std::string slurp_file_as_str(const std::string& input_file,
+ size_t buf_size = 0) const;
+
+ /*
+ * Read a file calling consumer_fn() with the inputs
+ */
+ void read_file(const std::string& input_file,
+ std::function<void (uint8_t[], size_t)> consumer_fn,
+ size_t buf_size = 0) const;
+
+
+ void do_read_file(std::istream& in,
+ std::function<void (uint8_t[], size_t)> consumer_fn,
+ size_t buf_size = 0) const;
+
+ template<typename Alloc>
+ void write_output(const std::vector<uint8_t, Alloc>& vec)
+ {
+ output().write(reinterpret_cast<const char*>(vec.data()), vec.size());
+ }
+
+ Botan::RandomNumberGenerator& rng();
+
+ private:
+ typedef std::function<Command* ()> cmd_maker_fn;
+ static std::map<std::string, cmd_maker_fn>& global_registry();
+
+ void parse_spec();
+
+ // set in constructor
+ std::string m_spec;
+
+ std::unique_ptr<Argument_Parser> m_args;
+ std::unique_ptr<std::ostream> m_output_stream;
+ std::unique_ptr<std::ostream> m_error_output_stream;
+
+ std::unique_ptr<Botan::RandomNumberGenerator> m_rng;
+
+ // possibly set by calling set_return_code()
+ int m_return_code = 0;
+
+ public:
+ // the registry interface:
+
+ class Registration final
+ {
+ public:
+ Registration(const std::string& name, cmd_maker_fn maker_fn);
+ };
+ };
+
+#define BOTAN_REGISTER_COMMAND(name, CLI_Class) \
+ Botan_CLI::Command::Registration reg_cmd_ ## CLI_Class(name, \
+ []() -> Botan_CLI::Command* { return new CLI_Class; })
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/cli/cli_exceptions.h b/comm/third_party/botan/src/cli/cli_exceptions.h
new file mode 100644
index 0000000000..c88d170271
--- /dev/null
+++ b/comm/third_party/botan/src/cli/cli_exceptions.h
@@ -0,0 +1,47 @@
+/*
+* (C) 2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_CLI_EXCEPTIONS_H_
+#define BOTAN_CLI_EXCEPTIONS_H_
+
+namespace Botan_CLI {
+
+class CLI_Error : public std::runtime_error
+ {
+ public:
+ explicit CLI_Error(const std::string& s) : std::runtime_error(s) {}
+ };
+
+class CLI_IO_Error final : public CLI_Error
+ {
+ public:
+ CLI_IO_Error(const std::string& op, const std::string& who) :
+ CLI_Error("Error " + op + " " + who) {}
+ };
+
+class CLI_Usage_Error final : public CLI_Error
+ {
+ public:
+ explicit CLI_Usage_Error(const std::string& what) : CLI_Error(what) {}
+ };
+
+/* Thrown eg when a requested feature was compiled out of the library
+ or is not available, eg hashing with MD2
+*/
+class CLI_Error_Unsupported final : public CLI_Error
+ {
+ public:
+
+ 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") {}
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/cli/cli_rng.cpp b/comm/third_party/botan/src/cli/cli_rng.cpp
new file mode 100644
index 0000000000..e3eee0c035
--- /dev/null
+++ b/comm/third_party/botan/src/cli/cli_rng.cpp
@@ -0,0 +1,146 @@
+/*
+* (C) 2015,2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "cli.h"
+#include <botan/rng.h>
+#include <botan/entropy_src.h>
+#include <botan/hex.h>
+#include <botan/parsing.h>
+
+#if defined(BOTAN_HAS_AUTO_SEEDING_RNG)
+ #include <botan/auto_rng.h>
+#endif
+
+#if defined(BOTAN_HAS_SYSTEM_RNG)
+ #include <botan/system_rng.h>
+#endif
+
+#if defined(BOTAN_HAS_PROCESSOR_RNG)
+ #include <botan/processor_rng.h>
+#endif
+
+#if defined(BOTAN_HAS_HMAC_DRBG)
+ #include <botan/hmac_drbg.h>
+#endif
+
+namespace Botan_CLI {
+
+std::unique_ptr<Botan::RandomNumberGenerator>
+cli_make_rng(const std::string& rng_type, const std::string& hex_drbg_seed)
+ {
+#if defined(BOTAN_HAS_SYSTEM_RNG)
+ if(rng_type == "system" || rng_type.empty())
+ {
+ return std::unique_ptr<Botan::RandomNumberGenerator>(new Botan::System_RNG);
+ }
+#endif
+
+ const std::vector<uint8_t> drbg_seed = Botan::hex_decode(hex_drbg_seed);
+
+#if defined(BOTAN_HAS_AUTO_SEEDING_RNG)
+ if(rng_type == "auto" || rng_type == "entropy" || rng_type.empty())
+ {
+ std::unique_ptr<Botan::RandomNumberGenerator> rng;
+
+ if(rng_type == "entropy")
+ rng.reset(new Botan::AutoSeeded_RNG(Botan::Entropy_Sources::global_sources()));
+ else
+ rng.reset(new Botan::AutoSeeded_RNG);
+
+ if(drbg_seed.size() > 0)
+ rng->add_entropy(drbg_seed.data(), drbg_seed.size());
+ return rng;
+ }
+#endif
+
+#if defined(BOTAN_HAS_HMAC_DRBG) && defined(BOTAN_HAS_SHA2_32)
+ if(rng_type == "drbg" || (rng_type.empty() && drbg_seed.empty() == false))
+ {
+ std::unique_ptr<Botan::MessageAuthenticationCode> mac =
+ Botan::MessageAuthenticationCode::create_or_throw("HMAC(SHA-256)");
+ std::unique_ptr<Botan::Stateful_RNG> rng(new Botan::HMAC_DRBG(std::move(mac)));
+ rng->add_entropy(drbg_seed.data(), drbg_seed.size());
+
+ if(rng->is_seeded() == false)
+ throw CLI_Error("For " + rng->name() + " a seed of at least " +
+ std::to_string(rng->security_level()/8) +
+ " bytes must be provided");
+
+ return std::unique_ptr<Botan::RandomNumberGenerator>(rng.release());
+ }
+#endif
+
+#if defined(BOTAN_HAS_PROCESSOR_RNG)
+ if(rng_type == "rdrand" || rng_type == "cpu" || rng_type.empty())
+ {
+ if(Botan::Processor_RNG::available())
+ return std::unique_ptr<Botan::RandomNumberGenerator>(new Botan::Processor_RNG);
+ else if(rng_type.empty() == false)
+ throw CLI_Error("RNG instruction not supported on this processor");
+ }
+#endif
+
+ if(rng_type.empty())
+ throw CLI_Error_Unsupported("No random number generator seems to be available in the current build");
+ else
+ throw CLI_Error_Unsupported("RNG", rng_type);
+ }
+
+class RNG final : public Command
+ {
+ public:
+ RNG() : Command("rng --format=hex --system --rdrand --auto --entropy --drbg --drbg-seed= *bytes") {}
+
+ std::string group() const override
+ {
+ return "misc";
+ }
+
+ std::string description() const override
+ {
+ return "Sample random bytes from the specified rng";
+ }
+
+ void go() override
+ {
+ const std::string format = get_arg("format");
+ std::string type = get_arg("rng-type");
+
+ if(type.empty())
+ {
+ for(std::string flag : { "system", "rdrand", "auto", "entropy", "drbg" })
+ {
+ if(flag_set(flag))
+ {
+ type = flag;
+ break;
+ }
+ }
+ }
+
+ const std::string drbg_seed = get_arg("drbg-seed");
+ std::unique_ptr<Botan::RandomNumberGenerator> rng = cli_make_rng(type, drbg_seed);
+
+ for(const std::string& req : get_arg_list("bytes"))
+ {
+ const size_t req_len = Botan::to_u32bit(req);
+ const auto blob = rng->random_vec(req_len);
+
+ if(format == "binary" || format == "raw")
+ {
+ output().write(reinterpret_cast<const char*>(blob.data()), blob.size());
+ }
+ else
+ {
+ output() << format_blob(format, blob) << "\n";
+ }
+ }
+ }
+ };
+
+BOTAN_REGISTER_COMMAND("rng", RNG);
+
+}
diff --git a/comm/third_party/botan/src/cli/codec.cpp b/comm/third_party/botan/src/cli/codec.cpp
new file mode 100644
index 0000000000..48388d1a7a
--- /dev/null
+++ b/comm/third_party/botan/src/cli/codec.cpp
@@ -0,0 +1,268 @@
+/*
+* (C) 2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "cli.h"
+
+#if defined(BOTAN_HAS_HEX_CODEC)
+ #include <botan/hex.h>
+#endif
+
+#if defined(BOTAN_HAS_BASE32_CODEC)
+ #include <botan/base32.h>
+#endif
+
+#if defined(BOTAN_HAS_BASE58_CODEC)
+ #include <botan/base58.h>
+#endif
+
+#if defined(BOTAN_HAS_BASE64_CODEC)
+ #include <botan/base64.h>
+#endif
+
+namespace Botan_CLI {
+
+#if defined(BOTAN_HAS_HEX_CODEC)
+
+class Hex_Encode final : public Command
+ {
+ public:
+ Hex_Encode() : Command("hex_enc file") {}
+
+ std::string group() const override
+ {
+ return "codec";
+ }
+
+ std::string description() const override
+ {
+ return "Hex encode a given file";
+ }
+
+ void go() override
+ {
+ auto hex_enc_f = [&](const uint8_t b[], size_t l) { output() << Botan::hex_encode(b, l); };
+ this->read_file(get_arg("file"), hex_enc_f, 2);
+ }
+ };
+
+BOTAN_REGISTER_COMMAND("hex_enc", Hex_Encode);
+
+class Hex_Decode final : public Command
+ {
+ public:
+ Hex_Decode() : Command("hex_dec file") {}
+
+ std::string group() const override
+ {
+ return "codec";
+ }
+
+ std::string description() const override
+ {
+ return "Hex decode a given file";
+ }
+
+ void go() override
+ {
+ auto hex_dec_f = [&](const uint8_t b[], size_t l)
+ {
+ std::vector<uint8_t> bin = Botan::hex_decode(reinterpret_cast<const char*>(b), l);
+ output().write(reinterpret_cast<const char*>(bin.data()), bin.size());
+ };
+
+ this->read_file(get_arg("file"), hex_dec_f, 2);
+ }
+ };
+
+BOTAN_REGISTER_COMMAND("hex_dec", Hex_Decode);
+
+#endif
+
+#if defined(BOTAN_HAS_BASE58_CODEC)
+
+class Base58_Encode final : public Command
+ {
+ public:
+ Base58_Encode() : Command("base58_enc --check file") {}
+
+ std::string group() const override
+ {
+ return "codec";
+ }
+
+ std::string description() const override
+ {
+ return "Encode given file to Base58";
+ }
+
+ void go() override
+ {
+ auto data = slurp_file(get_arg("file"));
+
+ if(flag_set("check"))
+ output() << Botan::base58_check_encode(data);
+ else
+ output() << Botan::base58_encode(data);
+ }
+ };
+
+BOTAN_REGISTER_COMMAND("base58_enc", Base58_Encode);
+
+class Base58_Decode final : public Command
+ {
+ public:
+ Base58_Decode() : Command("base58_dec --check file") {}
+
+ std::string group() const override
+ {
+ return "codec";
+ }
+
+ std::string description() const override
+ {
+ return "Decode Base58 encoded file";
+ }
+
+ void go() override
+ {
+ auto data = slurp_file_as_str(get_arg("file"));
+
+ std::vector<uint8_t> bin;
+
+ if(flag_set("check"))
+ bin = Botan::base58_check_decode(data);
+ else
+ bin = Botan::base58_decode(data);
+
+ output().write(reinterpret_cast<const char*>(bin.data()), bin.size());
+ }
+ };
+
+BOTAN_REGISTER_COMMAND("base58_dec", Base58_Decode);
+
+#endif // base58
+
+#if defined(BOTAN_HAS_BASE32_CODEC)
+
+class Base32_Encode final : public Command
+ {
+ public:
+ Base32_Encode() : Command("base32_enc file") {}
+
+ std::string group() const override
+ {
+ return "codec";
+ }
+
+ std::string description() const override
+ {
+ return "Encode given file to Base32";
+ }
+
+ void go() override
+ {
+ auto onData = [&](const uint8_t b[], size_t l)
+ {
+ output() << Botan::base32_encode(b, l);
+ };
+ this->read_file(get_arg("file"), onData, 768);
+ }
+ };
+
+BOTAN_REGISTER_COMMAND("base32_enc", Base32_Encode);
+
+class Base32_Decode final : public Command
+ {
+ public:
+ Base32_Decode() : Command("base32_dec file") {}
+
+ std::string group() const override
+ {
+ return "codec";
+ }
+
+ std::string description() const override
+ {
+ return "Decode Base32 encoded file";
+ }
+
+ void go() override
+ {
+ auto write_bin = [&](const uint8_t b[], size_t l)
+ {
+ Botan::secure_vector<uint8_t> bin = Botan::base32_decode(reinterpret_cast<const char*>(b), l);
+ output().write(reinterpret_cast<const char*>(bin.data()), bin.size());
+ };
+
+ this->read_file(get_arg("file"), write_bin, 1024);
+ }
+ };
+
+BOTAN_REGISTER_COMMAND("base32_dec", Base32_Decode);
+
+#endif // base32
+
+#if defined(BOTAN_HAS_BASE64_CODEC)
+
+class Base64_Encode final : public Command
+ {
+ public:
+ Base64_Encode() : Command("base64_enc file") {}
+
+ std::string group() const override
+ {
+ return "codec";
+ }
+
+ std::string description() const override
+ {
+ return "Encode given file to Base64";
+ }
+
+ void go() override
+ {
+ auto onData = [&](const uint8_t b[], size_t l)
+ {
+ output() << Botan::base64_encode(b, l);
+ };
+ this->read_file(get_arg("file"), onData, 768);
+ }
+ };
+
+BOTAN_REGISTER_COMMAND("base64_enc", Base64_Encode);
+
+class Base64_Decode final : public Command
+ {
+ public:
+ Base64_Decode() : Command("base64_dec file") {}
+
+ std::string group() const override
+ {
+ return "codec";
+ }
+
+ std::string description() const override
+ {
+ return "Decode Base64 encoded file";
+ }
+
+ void go() override
+ {
+ auto write_bin = [&](const uint8_t b[], size_t l)
+ {
+ Botan::secure_vector<uint8_t> bin = Botan::base64_decode(reinterpret_cast<const char*>(b), l);
+ output().write(reinterpret_cast<const char*>(bin.data()), bin.size());
+ };
+
+ this->read_file(get_arg("file"), write_bin, 1024);
+ }
+ };
+
+BOTAN_REGISTER_COMMAND("base64_dec", Base64_Decode);
+
+#endif // base64
+
+}
diff --git a/comm/third_party/botan/src/cli/compress.cpp b/comm/third_party/botan/src/cli/compress.cpp
new file mode 100644
index 0000000000..e62acd7636
--- /dev/null
+++ b/comm/third_party/botan/src/cli/compress.cpp
@@ -0,0 +1,190 @@
+/*
+* (C) 2014,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "cli.h"
+
+#if defined(BOTAN_HAS_COMPRESSION)
+ #include <botan/compression.h>
+ #include <fstream>
+#endif
+
+namespace Botan_CLI {
+
+#if defined(BOTAN_HAS_COMPRESSION)
+
+class Compress final : public Command
+ {
+ 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)
+ {
+ const std::map<std::string, std::string> suffixes =
+ {
+ { "zlib", "zlib" },
+ { "gzip", "gz" },
+ { "bzip2", "bz2" },
+ { "lzma", "xz" },
+ };
+
+ auto suffix_info = suffixes.find(comp_type);
+ if(suffixes.count(comp_type) == 0)
+ {
+ throw CLI_Error_Unsupported("Compressing", comp_type);
+ }
+
+ return input_fsname + "." + suffix_info->second;
+ }
+
+ std::string group() const override
+ {
+ return "compression";
+ }
+
+ std::string description() const override
+ {
+ return "Compress a given file";
+ }
+
+ void go() override
+ {
+ const std::string comp_type = get_arg("type");
+ const size_t buf_size = get_arg_sz("buf-size");
+ const size_t comp_level = get_arg_sz("level");
+
+ std::unique_ptr<Botan::Compression_Algorithm> compress;
+
+ compress.reset(Botan::make_compressor(comp_type));
+
+ if(!compress)
+ {
+ throw CLI_Error_Unsupported("Compression", comp_type);
+ }
+
+ const std::string in_file = get_arg("file");
+ std::ifstream in(in_file, std::ios::binary);
+
+ if(!in.good())
+ {
+ throw CLI_IO_Error("reading", in_file);
+ }
+
+ const std::string out_file = output_filename(in_file, comp_type);
+ std::ofstream out(out_file, std::ios::binary);
+ if(!out.good())
+ {
+ throw CLI_IO_Error("writing", out_file);
+ }
+
+ Botan::secure_vector<uint8_t> buf;
+
+ compress->start(comp_level);
+
+ while(in.good())
+ {
+ buf.resize(buf_size);
+ in.read(reinterpret_cast<char*>(buf.data()), buf.size());
+ buf.resize(in.gcount());
+
+ compress->update(buf);
+
+ out.write(reinterpret_cast<const char*>(buf.data()), buf.size());
+ }
+
+ buf.clear();
+ compress->finish(buf);
+ out.write(reinterpret_cast<const char*>(buf.data()), buf.size());
+ out.close();
+ }
+ };
+
+BOTAN_REGISTER_COMMAND("compress", Compress);
+
+class Decompress final : public Command
+ {
+ public:
+ Decompress() : Command("decompress --buf-size=8192 file") {}
+
+ 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 + "'");
+ }
+
+ out_file = in_file.substr(0, last_dot);
+ suffix = in_file.substr(last_dot + 1, std::string::npos);
+ }
+
+ std::string group() const override
+ {
+ return "compression";
+ }
+
+ std::string description() const override
+ {
+ return "Decompress a given compressed archive";
+ }
+
+ 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;
+ parse_extension(in_file, out_file, suffix);
+
+ std::ifstream in(in_file, std::ios::binary);
+
+ if(!in.good())
+ {
+ throw CLI_IO_Error("reading", in_file);
+ }
+
+ std::unique_ptr<Botan::Decompression_Algorithm> decompress;
+
+ decompress.reset(Botan::make_decompressor(suffix));
+
+ if(!decompress)
+ {
+ throw CLI_Error_Unsupported("Decompression", suffix);
+ }
+
+ std::ofstream out(out_file, std::ios::binary);
+ if(!out.good())
+ {
+ throw CLI_IO_Error("writing", out_file);
+ }
+
+ Botan::secure_vector<uint8_t> buf;
+
+ decompress->start();
+
+ while(in.good())
+ {
+ buf.resize(buf_size);
+ in.read(reinterpret_cast<char*>(buf.data()), buf.size());
+ buf.resize(in.gcount());
+
+ decompress->update(buf);
+
+ out.write(reinterpret_cast<const char*>(buf.data()), buf.size());
+ }
+
+ buf.clear();
+ decompress->finish(buf);
+ out.write(reinterpret_cast<const char*>(buf.data()), buf.size());
+ out.close();
+ }
+ };
+
+BOTAN_REGISTER_COMMAND("decompress", Decompress);
+
+#endif
+
+}
diff --git a/comm/third_party/botan/src/cli/encryption.cpp b/comm/third_party/botan/src/cli/encryption.cpp
new file mode 100644
index 0000000000..fa8de7cfdb
--- /dev/null
+++ b/comm/third_party/botan/src/cli/encryption.cpp
@@ -0,0 +1,127 @@
+/*
+* (C) 2015,2017 Simon Warta (Kullo GmbH)
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "cli.h"
+
+#if (defined(BOTAN_HAS_AES) || defined(BOTAN_HAS_AEAD_CHACHA20_POLY1305)) && defined(BOTAN_HAS_AEAD_MODES)
+
+#include <botan/aead.h>
+#include <botan/hex.h>
+#include <sstream>
+
+namespace Botan_CLI {
+
+namespace {
+
+auto VALID_MODES = std::map<std::string, std::string>{
+ // Don't add algorithms here without extending tests
+ // in `src/scripts/test_cli_crypt.py`
+ { "aes-128-cfb", "AES-128/CFB" },
+ { "aes-192-cfb", "AES-192/CFB" },
+ { "aes-256-cfb", "AES-256/CFB" },
+ { "aes-128-gcm", "AES-128/GCM" },
+ { "aes-192-gcm", "AES-192/GCM" },
+ { "aes-256-gcm", "AES-256/GCM" },
+ { "aes-128-ocb", "AES-128/OCB" },
+ { "aes-128-xts", "AES-128/XTS" },
+ { "aes-256-xts", "AES-256/XTS" },
+ { "chacha20poly1305", "ChaCha20Poly1305" },
+};
+
+Botan::secure_vector<uint8_t>
+do_crypt(const std::string &cipher,
+ const std::vector<uint8_t> &input,
+ const Botan::SymmetricKey &key,
+ const Botan::InitializationVector &iv,
+ const std::vector<uint8_t>& ad,
+ Botan::Cipher_Dir direction)
+ {
+ if(iv.size() == 0)
+ throw CLI_Usage_Error("IV must not be empty");
+
+ // TODO: implement streaming
+
+ std::unique_ptr<Botan::Cipher_Mode> processor(Botan::Cipher_Mode::create(cipher, direction));
+ if(!processor)
+ throw CLI_Error("Cipher algorithm not found");
+
+ // Set key
+ processor->set_key(key);
+
+ if(Botan::AEAD_Mode* aead = dynamic_cast<Botan::AEAD_Mode*>(processor.get()))
+ {
+ aead->set_ad(ad);
+ }
+ else if(ad.size() != 0)
+ {
+ throw CLI_Usage_Error("Cannot specify associated data with non-AEAD mode");
+ }
+
+ // Set IV
+ processor->start(iv.bits_of());
+
+ Botan::secure_vector<uint8_t> buf(input.begin(), input.end());
+ processor->finish(buf);
+
+ return buf;
+ }
+
+}
+
+class Encryption final : public Command
+ {
+ public:
+ Encryption() : Command("encryption --buf-size=4096 --decrypt --mode= --key= --iv= --ad=") {}
+
+ std::string group() const override
+ {
+ return "encryption";
+ }
+
+ std::string description() const override
+ {
+ return "Encrypt or decrypt a given file";
+ }
+
+ void go() override
+ {
+ std::string mode = get_arg_or("mode", "");
+ if (!VALID_MODES.count(mode))
+ {
+ std::ostringstream error;
+ error << "Invalid mode: '" << mode << "'\n"
+ << "valid modes are:";
+ for (auto valid_mode : VALID_MODES) error << " " << valid_mode.first;
+
+ throw CLI_Usage_Error(error.str());
+ }
+
+ const std::string key_hex = get_arg("key");
+ const std::string iv_hex = get_arg("iv");
+ const std::string ad_hex = get_arg_or("ad", "");
+ const size_t buf_size = get_arg_sz("buf-size");
+
+ const std::vector<uint8_t> input = this->slurp_file("-", buf_size);
+
+ if (verbose())
+ {
+ error_output() << "Got " << input.size() << " bytes of input data.\n";
+ }
+
+ const Botan::SymmetricKey key(key_hex);
+ const Botan::InitializationVector iv(iv_hex);
+ const std::vector<uint8_t> ad = Botan::hex_decode(ad_hex);
+
+ auto direction = flag_set("decrypt") ? Botan::Cipher_Dir::DECRYPTION : Botan::Cipher_Dir::ENCRYPTION;
+ write_output(do_crypt(VALID_MODES[mode], input, key, iv, ad, direction));
+ }
+ };
+
+BOTAN_REGISTER_COMMAND("encryption", Encryption);
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/cli/entropy.cpp b/comm/third_party/botan/src/cli/entropy.cpp
new file mode 100644
index 0000000000..0404afb995
--- /dev/null
+++ b/comm/third_party/botan/src/cli/entropy.cpp
@@ -0,0 +1,104 @@
+/*
+* (C) 2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "cli.h"
+#include "../tests/test_rng.h" // FIXME
+
+#include <botan/entropy_src.h>
+
+#if defined(BOTAN_HAS_COMPRESSION)
+#include <botan/compression.h>
+#endif
+
+namespace Botan_CLI {
+
+class Entropy final : public Command
+ {
+ public:
+ Entropy() : Command("entropy --truncate-at=128 source") {}
+
+ std::string group() const override
+ {
+ return "misc";
+ }
+
+ std::string description() const override
+ {
+ return "Sample a raw entropy source";
+ }
+
+ void go() override
+ {
+ const std::string req_source = get_arg("source");
+ const size_t truncate_sample = get_arg_sz("truncate-at");
+
+ auto& entropy_sources = Botan::Entropy_Sources::global_sources();
+
+ std::vector<std::string> sources;
+ if(req_source == "all")
+ sources = entropy_sources.enabled_sources();
+ else
+ sources.push_back(req_source);
+
+ for(std::string source : sources)
+ {
+ Botan_Tests::SeedCapturing_RNG rng;
+ const size_t entropy_estimate = entropy_sources.poll_just(rng, source);
+
+ if(rng.samples() == 0)
+ {
+ output() << "Source " << source << " is unavailable\n";
+ continue;
+ }
+
+ const auto& sample = rng.seed_material();
+
+ output() << "Polling " << source << " gathered " << sample.size()
+ << " bytes in " << rng.samples() << " outputs with estimated entropy "
+ << entropy_estimate << "\n";
+
+#if defined(BOTAN_HAS_COMPRESSION)
+ if(!sample.empty())
+ {
+ std::unique_ptr<Botan::Compression_Algorithm> comp(Botan::make_compressor("zlib"));
+ if(comp)
+ {
+ try
+ {
+ Botan::secure_vector<uint8_t> compressed;
+ compressed.assign(sample.begin(), sample.end());
+ comp->start(9);
+ comp->finish(compressed);
+
+ if(compressed.size() < sample.size())
+ {
+ output() << "Sample from " << source << " was zlib compressed from " << sample.size()
+ << " bytes to " << compressed.size() << " bytes\n";
+ }
+ }
+ catch(std::exception& e)
+ {
+ error_output() << "Error while attempting to compress: " << e.what() << "\n";
+ }
+ }
+ }
+#endif
+
+ 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";
+ }
+ }
+ }
+ };
+
+BOTAN_REGISTER_COMMAND("entropy", Entropy);
+
+}
diff --git a/comm/third_party/botan/src/cli/hash.cpp b/comm/third_party/botan/src/cli/hash.cpp
new file mode 100644
index 0000000000..8e59b2ab56
--- /dev/null
+++ b/comm/third_party/botan/src/cli/hash.cpp
@@ -0,0 +1,78 @@
+/*
+* (C) 2009,2010,2014,2015,2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "cli.h"
+
+#if defined(BOTAN_HAS_HASH)
+ #include <botan/hash.h>
+#endif
+
+namespace Botan_CLI {
+
+#if defined(BOTAN_HAS_HASH)
+
+class Hash final : public Command
+ {
+ public:
+ Hash() : Command("hash --algo=SHA-256 --buf-size=4096 --no-fsname --format=hex *files") {}
+
+ std::string group() const override
+ {
+ return "hash";
+ }
+
+ std::string description() const override
+ {
+ return "Compute the message digest of given file(s)";
+ }
+
+ void go() override
+ {
+ const std::string hash_algo = get_arg("algo");
+ const std::string format = get_arg("format");
+ const size_t buf_size = get_arg_sz("buf-size");
+ const bool no_fsname = flag_set("no-fsname");
+
+ std::unique_ptr<Botan::HashFunction> hash_fn(Botan::HashFunction::create(hash_algo));
+
+ if(!hash_fn)
+ {
+ throw CLI_Error_Unsupported("hashing", hash_algo);
+ }
+
+ std::vector<std::string> files = get_arg_list("files");
+ if(files.empty())
+ {
+ files.push_back("-");
+ } // read stdin if no arguments on command line
+
+ for(const std::string& fsname : files)
+ {
+ try
+ {
+ auto update_hash = [&](const uint8_t b[], size_t l) { hash_fn->update(b, l); };
+ read_file(fsname, update_hash, buf_size);
+
+ const std::string digest = format_blob(format, hash_fn->final());
+
+ if(no_fsname)
+ output() << digest << "\n";
+ else
+ output() << digest << " " << fsname << "\n";
+ }
+ catch(CLI_IO_Error& e)
+ {
+ error_output() << e.what() << "\n";
+ }
+ }
+ }
+ };
+
+BOTAN_REGISTER_COMMAND("hash", Hash);
+
+#endif
+
+}
diff --git a/comm/third_party/botan/src/cli/hmac.cpp b/comm/third_party/botan/src/cli/hmac.cpp
new file mode 100644
index 0000000000..5b7345c50e
--- /dev/null
+++ b/comm/third_party/botan/src/cli/hmac.cpp
@@ -0,0 +1,78 @@
+/*
+* (C) 2009,2010,2014,2015 Jack Lloyd
+* (C) 2017 René Korthaus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "cli.h"
+
+#include <botan/hex.h>
+
+#if defined(BOTAN_HAS_MAC)
+ #include <botan/mac.h>
+#endif
+
+namespace Botan_CLI {
+
+#if defined(BOTAN_HAS_HMAC)
+
+class HMAC final : public Command
+ {
+ public:
+ HMAC() : Command("hmac --hash=SHA-256 --buf-size=4096 --no-fsname key *files") {}
+
+ std::string group() const override
+ {
+ return "hmac";
+ }
+
+ std::string description() const override
+ {
+ return "Compute the HMAC tag of given file(s)";
+ }
+
+ void go() override
+ {
+ const bool no_fsname = flag_set("no-fsname");
+ const std::string hash_algo = get_arg("hash");
+ std::unique_ptr<Botan::MessageAuthenticationCode> hmac =
+ Botan::MessageAuthenticationCode::create("HMAC(" + hash_algo + ")");
+
+ if(!hmac)
+ { throw CLI_Error_Unsupported("HMAC", hash_algo); }
+
+ hmac->set_key(slurp_file(get_arg("key")));
+
+ const size_t buf_size = get_arg_sz("buf-size");
+
+ std::vector<std::string> files = get_arg_list("files");
+ if(files.empty())
+ { files.push_back("-"); } // read stdin if no arguments on command line
+
+ for(const std::string& fsname : files)
+ {
+ try
+ {
+ auto update_hmac = [&](const uint8_t b[], size_t l) { hmac->update(b, l); };
+ read_file(fsname, update_hmac, buf_size);
+ output() << Botan::hex_encode(hmac->final());
+
+ if(no_fsname == false)
+ output() << " " << fsname;
+
+ output() << "\n";
+ }
+ catch(CLI_IO_Error& e)
+ {
+ error_output() << e.what() << "\n";
+ }
+ }
+ }
+ };
+
+BOTAN_REGISTER_COMMAND("hmac", HMAC);
+
+#endif // hmac
+
+}
diff --git a/comm/third_party/botan/src/cli/main.cpp b/comm/third_party/botan/src/cli/main.cpp
new file mode 100644
index 0000000000..1f806a9066
--- /dev/null
+++ b/comm/third_party/botan/src/cli/main.cpp
@@ -0,0 +1,37 @@
+/*
+* (C) 2009,2014,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "cli.h"
+#include <botan/version.h>
+#include <iostream>
+#include <algorithm>
+
+int main(int argc, char* argv[])
+ {
+ std::cerr << Botan::runtime_version_check(BOTAN_VERSION_MAJOR, BOTAN_VERSION_MINOR, BOTAN_VERSION_PATCH);
+
+ std::string cmd_name = "help";
+
+ if(argc >= 2)
+ {
+ cmd_name = argv[1];
+ if(cmd_name == "--help" || cmd_name == "-h")
+ cmd_name = "help";
+ if(cmd_name == "--version" || cmd_name == "-V")
+ cmd_name = "version";
+ }
+
+ std::unique_ptr<Botan_CLI::Command> cmd(Botan_CLI::Command::get_cmd(cmd_name));
+
+ if(!cmd)
+ {
+ std::cout << "Unknown command " << cmd_name << " (try --help)\n";
+ return 1;
+ }
+
+ std::vector<std::string> args(argv + std::min(argc, 2), argv + argc);
+ return cmd->run(args);
+ }
diff --git a/comm/third_party/botan/src/cli/math.cpp b/comm/third_party/botan/src/cli/math.cpp
new file mode 100644
index 0000000000..1268cd3e53
--- /dev/null
+++ b/comm/third_party/botan/src/cli/math.cpp
@@ -0,0 +1,269 @@
+/*
+* (C) 2009,2010,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "cli.h"
+
+#if defined(BOTAN_HAS_NUMBERTHEORY)
+
+#include <botan/numthry.h>
+#include <botan/monty.h>
+#include <iterator>
+
+namespace Botan_CLI {
+
+class Modular_Inverse final : public Command
+ {
+ public:
+ Modular_Inverse() : Command("mod_inverse n mod") {}
+
+ std::string group() const override
+ {
+ return "numtheory";
+ }
+
+ std::string description() const override
+ {
+ return "Calculates a modular inverse";
+ }
+
+ void go() override
+ {
+ const Botan::BigInt n(get_arg("n"));
+ const Botan::BigInt mod(get_arg("mod"));
+
+ output() << Botan::inverse_mod(n, mod) << "\n";
+ }
+ };
+
+BOTAN_REGISTER_COMMAND("mod_inverse", Modular_Inverse);
+
+class Gen_Prime final : public Command
+ {
+ public:
+ Gen_Prime() : Command("gen_prime --count=1 bits") {}
+
+ std::string group() const override
+ {
+ return "numtheory";
+ }
+
+ std::string description() const override
+ {
+ return "Samples one or more primes";
+ }
+
+ void go() override
+ {
+ const size_t bits = get_arg_sz("bits");
+ const size_t cnt = get_arg_sz("count");
+
+ for(size_t i = 0; i != cnt; ++i)
+ {
+ const Botan::BigInt p = Botan::random_prime(rng(), bits);
+ output() << p << "\n";
+ }
+ }
+ };
+
+BOTAN_REGISTER_COMMAND("gen_prime", Gen_Prime);
+
+class Is_Prime final : public Command
+ {
+ public:
+ Is_Prime() : Command("is_prime --prob=56 n") {}
+
+ std::string group() const override
+ {
+ return "numtheory";
+ }
+
+ 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 size_t prob = get_arg_sz("prob");
+ const bool prime = Botan::is_prime(n, rng(), prob);
+
+ output() << n << " is " << (prime ? "probably prime" : "composite") << "\n";
+ }
+ };
+
+BOTAN_REGISTER_COMMAND("is_prime", Is_Prime);
+
+/*
+* Factor integers using a combination of trial division by small
+* primes, and Pollard's Rho algorithm
+*/
+class Factor final : public Command
+ {
+ public:
+ Factor() : Command("factor n") {}
+
+ std::string group() const override
+ {
+ return "numtheory";
+ }
+
+ std::string description() const override
+ {
+ return "Factor a given integer";
+ }
+
+ void go() override
+ {
+ Botan::BigInt n(get_arg("n"));
+
+ std::vector<Botan::BigInt> factors = factorize(n, rng());
+ std::sort(factors.begin(), factors.end());
+
+ output() << n << ": ";
+ std::copy(factors.begin(), factors.end(), std::ostream_iterator<Botan::BigInt>(output(), " "));
+ output() << std::endl;
+ }
+
+ private:
+
+ std::vector<Botan::BigInt> factorize(const Botan::BigInt& n_in,
+ Botan::RandomNumberGenerator& rng)
+ {
+ Botan::BigInt n = n_in;
+ std::vector<Botan::BigInt> factors = remove_small_factors(n);
+
+ while(n != 1)
+ {
+ if(Botan::is_prime(n, rng))
+ {
+ factors.push_back(n);
+ break;
+ }
+
+ Botan::BigInt a_factor = 0;
+ while(a_factor == 0)
+ {
+ a_factor = rho(n, rng);
+ }
+
+ std::vector<Botan::BigInt> rho_factored = factorize(a_factor, rng);
+ for(size_t j = 0; j != rho_factored.size(); j++)
+ {
+ factors.push_back(rho_factored[j]);
+ }
+
+ n /= a_factor;
+ }
+
+ return factors;
+ }
+
+ /*
+ * Pollard's Rho algorithm, as described in the MIT algorithms book.
+ * Uses Brent's cycle finding
+ */
+ Botan::BigInt rho(const Botan::BigInt& n, Botan::RandomNumberGenerator& rng)
+ {
+ auto monty_n = std::make_shared<Botan::Montgomery_Params>(n);
+
+ const Botan::Montgomery_Int one(monty_n, monty_n->R1(), false);
+
+ Botan::Montgomery_Int x(monty_n, Botan::BigInt::random_integer(rng, 2, n - 3), false);
+ Botan::Montgomery_Int y = x;
+ Botan::Montgomery_Int z = one;
+ Botan::Montgomery_Int t(monty_n);
+ Botan::BigInt d;
+
+ Botan::secure_vector<Botan::word> ws;
+
+ size_t i = 1, k = 2;
+
+ while(true)
+ {
+ i++;
+
+ if(i >= 0xFFFF0000) // bad seed? too slow? bail out
+ {
+ break;
+ }
+
+ x.square_this(ws); // x = x^2
+ x.add(one, ws);
+
+ t = y;
+ t.sub(x, ws);
+
+ z.mul_by(t, ws);
+
+ if(i == k || i % 128 == 0)
+ {
+ d = Botan::gcd(z.value(), n);
+ z = one;
+
+ if(d == n)
+ {
+ // TODO Should rewind here
+ break;
+ }
+
+ if(d != 1)
+ return d;
+ }
+
+ if(i == k)
+ {
+ y = x;
+ k = 2 * k;
+ }
+ }
+
+ // failed
+ return 0;
+ }
+
+ // Remove (and return) any small (< 2^16) factors
+ std::vector<Botan::BigInt> remove_small_factors(Botan::BigInt& n)
+ {
+ std::vector<Botan::BigInt> factors;
+
+ while(n.is_even())
+ {
+ factors.push_back(2);
+ n /= 2;
+ }
+
+ for(size_t j = 0; j != Botan::PRIME_TABLE_SIZE; j++)
+ {
+ uint16_t prime = Botan::PRIMES[j];
+ if(n < prime)
+ {
+ break;
+ }
+
+ Botan::BigInt x = Botan::gcd(n, prime);
+
+ if(x != 1)
+ {
+ n /= x;
+
+ while(x != 1)
+ {
+ x /= prime;
+ factors.push_back(prime);
+ }
+ }
+ }
+
+ return factors;
+ }
+ };
+
+BOTAN_REGISTER_COMMAND("factor", Factor);
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/cli/pbkdf.cpp b/comm/third_party/botan/src/cli/pbkdf.cpp
new file mode 100644
index 0000000000..d17c492203
--- /dev/null
+++ b/comm/third_party/botan/src/cli/pbkdf.cpp
@@ -0,0 +1,99 @@
+/*
+* (C) 2018 Ribose Inc
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "cli.h"
+
+#if defined(BOTAN_HAS_PBKDF)
+ #include <botan/pwdhash.h>
+ #include <botan/internal/os_utils.h>
+#endif
+
+namespace Botan_CLI {
+
+#if defined(BOTAN_HAS_PBKDF)
+
+class PBKDF_Tune final : public Command
+ {
+ public:
+ PBKDF_Tune() : Command("pbkdf_tune --algo=Scrypt --max-mem=256 --output-len=32 --check *times") {}
+
+ std::string group() const override
+ {
+ return "passhash";
+ }
+
+ std::string description() const override
+ {
+ return "Tune a PBKDF algo";
+ }
+
+ void go() override
+ {
+ const size_t output_len = get_arg_sz("output-len");
+ const std::string algo = get_arg("algo");
+ const size_t max_mem = get_arg_sz("max-mem");
+ const bool check_time = flag_set("check");
+
+ std::unique_ptr<Botan::PasswordHashFamily> pwdhash_fam =
+ Botan::PasswordHashFamily::create(algo);
+
+ if(!pwdhash_fam)
+ throw CLI_Error_Unsupported("Password hashing", algo);
+
+ for(const std::string& time : get_arg_list("times"))
+ {
+ std::unique_ptr<Botan::PasswordHash> pwhash;
+
+ if(time == "default")
+ {
+ pwhash = pwdhash_fam->default_params();
+ }
+ else
+ {
+ size_t msec = 0;
+ try
+ {
+ 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);
+ }
+
+ output() << "For " << time << " ms selected " << pwhash->to_string();
+
+ if(pwhash->total_memory_usage() > 0)
+ {
+ output() << " using " << pwhash->total_memory_usage()/(1024*1024) << " MiB";
+ }
+
+ if(check_time)
+ {
+ std::vector<uint8_t> outbuf(output_len);
+ const uint8_t salt[8] = { 0 };
+
+ const uint64_t start_ns = Botan::OS::get_system_timestamp_ns();
+ pwhash->derive_key(outbuf.data(), outbuf.size(),
+ "test", 4, salt, sizeof(salt));
+ 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() << "\n";
+ }
+ }
+ };
+
+BOTAN_REGISTER_COMMAND("pbkdf_tune", PBKDF_Tune);
+
+#endif
+
+}
diff --git a/comm/third_party/botan/src/cli/pk_crypt.cpp b/comm/third_party/botan/src/cli/pk_crypt.cpp
new file mode 100644
index 0000000000..111a60129f
--- /dev/null
+++ b/comm/third_party/botan/src/cli/pk_crypt.cpp
@@ -0,0 +1,229 @@
+/*
+* (C) 2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "cli.h"
+
+#if defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_AEAD_MODES) && defined(BOTAN_HAS_EME_OAEP) && defined(BOTAN_HAS_SHA2_32) && defined(BOTAN_HAS_PEM_CODEC) && defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
+
+#include <botan/pubkey.h>
+#include <botan/x509_key.h>
+#include <botan/pkcs8.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
+#include <botan/oids.h>
+#include <botan/aead.h>
+#include <botan/pem.h>
+#include <botan/rng.h>
+
+namespace Botan_CLI {
+
+namespace {
+
+class PK_Encrypt final : public Command
+ {
+ public:
+ PK_Encrypt() : Command("pk_encrypt --aead=AES-256/GCM pubkey datafile") {}
+
+ std::string group() const override
+ {
+ return "pubkey";
+ }
+
+ std::string description() const override
+ {
+ return "Encrypt a file using a RSA public key";
+ }
+
+ void go() override
+ {
+ std::unique_ptr<Botan::Public_Key> key(Botan::X509::load_key(get_arg("pubkey")));
+ if(!key)
+ {
+ throw CLI_Error("Unable to load public key");
+ }
+
+ if(key->algo_name() != "RSA")
+ {
+ throw CLI_Usage_Error("This function requires an RSA key");
+ }
+
+ const std::string OAEP_HASH = "SHA-256";
+ const std::string aead_algo = get_arg("aead");
+
+ std::unique_ptr<Botan::AEAD_Mode> aead =
+ Botan::AEAD_Mode::create(aead_algo, Botan::ENCRYPTION);
+
+ if(!aead)
+ throw CLI_Usage_Error("The AEAD '" + aead_algo + "' is not available");
+
+ const Botan::OID aead_oid = Botan::OID::from_string(aead_algo);
+ if(aead_oid.empty())
+ throw CLI_Usage_Error("No OID defined for AEAD '" + aead_algo + "'");
+
+ Botan::secure_vector<uint8_t> data;
+ auto insert_fn = [&](const uint8_t b[], size_t l)
+ {
+ data.insert(data.end(), b, b + l);
+ };
+ this->read_file(get_arg("datafile"), insert_fn);
+
+ 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::secure_vector<uint8_t> file_key = rng().random_vec(aead->key_spec().maximum_keylength());
+
+ const std::vector<uint8_t> encrypted_key = enc.encrypt(file_key, rng());
+
+ const Botan::secure_vector<uint8_t> nonce = rng().random_vec(aead->default_nonce_length());
+ aead->set_key(file_key);
+ aead->set_associated_data_vec(encrypted_key);
+ aead->start(nonce);
+
+ aead->finish(data);
+
+ std::vector<uint8_t> buf;
+ Botan::DER_Encoder der(buf);
+
+ der.start_cons(Botan::SEQUENCE)
+ .encode(pk_alg_id)
+ .encode(encrypted_key, Botan::OCTET_STRING)
+ .encode(aead_oid)
+ .encode(nonce, Botan::OCTET_STRING)
+ .encode(data, Botan::OCTET_STRING)
+ .end_cons();
+
+ output() << Botan::PEM_Code::encode(buf, "PUBKEY ENCRYPTED MESSAGE", 72);
+ }
+ };
+
+BOTAN_REGISTER_COMMAND("pk_encrypt", PK_Encrypt);
+
+class PK_Decrypt final : public Command
+ {
+ public:
+ PK_Decrypt() : Command("pk_decrypt privkey datafile") {}
+
+ std::string group() const override
+ {
+ return "pubkey";
+ }
+
+ std::string description() const override
+ {
+ return "Decrypt a file using a RSA private key";
+ }
+
+ void go() override
+ {
+ Botan::DataSource_Stream input_stream(get_arg("privkey"));
+ auto get_pass = [this]() { return get_passphrase("Password"); };
+ std::unique_ptr<Botan::Private_Key> key = Botan::PKCS8::load_key(input_stream, get_pass);
+
+ if(!key)
+ {
+ throw CLI_Error("Unable to load public key");
+ }
+
+ if(key->algo_name() != "RSA")
+ {
+ throw CLI_Usage_Error("This function requires an RSA key");
+ }
+
+ Botan::secure_vector<uint8_t> data;
+ std::vector<uint8_t> encrypted_key;
+ std::vector<uint8_t> nonce;
+ Botan::AlgorithmIdentifier pk_alg_id;
+ Botan::OID aead_oid;
+
+ try
+ {
+ Botan::DataSource_Stream input(get_arg("datafile"));
+
+ Botan::BER_Decoder(Botan::PEM_Code::decode_check_label(input, "PUBKEY ENCRYPTED MESSAGE"))
+ .start_cons(Botan::SEQUENCE)
+ .decode(pk_alg_id)
+ .decode(encrypted_key, Botan::OCTET_STRING)
+ .decode(aead_oid)
+ .decode(nonce, Botan::OCTET_STRING)
+ .decode(data, Botan::OCTET_STRING)
+ .end_cons();
+ }
+ catch(Botan::Decoding_Error&)
+ {
+ error_output() << "Parsing input file failed: invalid format?\n";
+ return set_return_code(1);
+ }
+
+ const std::string aead_algo = Botan::OIDS::oid2str_or_empty(aead_oid);
+ if(aead_algo == "")
+ {
+ error_output() << "Ciphertext was encrypted with an unknown algorithm";
+ return set_return_code(1);
+ }
+
+ if(pk_alg_id.get_oid() != Botan::OID::from_string("RSA/OAEP"))
+ {
+ error_output() << "Ciphertext was encrypted with something other than RSA/OAEP";
+ return set_return_code(1);
+ }
+
+ Botan::AlgorithmIdentifier oaep_hash_id;
+ Botan::BER_Decoder(pk_alg_id.get_parameters()).decode(oaep_hash_id);
+
+ const std::string oaep_hash = Botan::OIDS::oid2str_or_empty(oaep_hash_id.get_oid());
+
+ if(oaep_hash.empty())
+ {
+ error_output() << "Unknown hash function used with OAEP, OID " << oaep_hash_id.get_oid().to_string() << "\n";
+ return set_return_code(1);
+ }
+
+ if(oaep_hash_id.get_parameters().empty() == false)
+ {
+ error_output() << "Unknown OAEP parameters used\n";
+ return set_return_code(1);
+ }
+
+ std::unique_ptr<Botan::AEAD_Mode> aead =
+ Botan::AEAD_Mode::create_or_throw(aead_algo, Botan::DECRYPTION);
+
+ const size_t expected_keylen = aead->key_spec().maximum_keylength();
+
+ Botan::PK_Decryptor_EME dec(*key, rng(), "OAEP(" + oaep_hash + ")");
+
+ const Botan::secure_vector<uint8_t> file_key =
+ dec.decrypt_or_random(encrypted_key.data(),
+ encrypted_key.size(),
+ expected_keylen,
+ rng());
+
+ aead->set_key(file_key);
+ aead->set_associated_data_vec(encrypted_key);
+ aead->start(nonce);
+
+ try
+ {
+ aead->finish(data);
+
+ output().write(reinterpret_cast<const char*>(data.data()), data.size());
+ }
+ catch(Botan::Integrity_Failure&)
+ {
+ error_output() << "Message authentication failure, possible ciphertext tampering\n";
+ return set_return_code(1);
+ }
+ }
+ };
+
+BOTAN_REGISTER_COMMAND("pk_decrypt", PK_Decrypt);
+
+}
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/cli/psk.cpp b/comm/third_party/botan/src/cli/psk.cpp
new file mode 100644
index 0000000000..35e38292d9
--- /dev/null
+++ b/comm/third_party/botan/src/cli/psk.cpp
@@ -0,0 +1,106 @@
+/*
+* (C) 2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "cli.h"
+
+#if defined(BOTAN_HAS_PSK_DB) && defined(BOTAN_HAS_SQLITE3)
+
+#include <botan/psk_db.h>
+#include <botan/sqlite3.h>
+#include <botan/hex.h>
+
+namespace Botan_CLI {
+
+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
+ {
+ const std::string db_filename = get_arg("db");
+ const Botan::secure_vector<uint8_t> db_key = Botan::hex_decode_locked(get_passphrase_arg("Database key", "db_key"));
+
+ std::shared_ptr<Botan::SQL_Database> db = std::make_shared<Botan::Sqlite3_Database>(db_filename);
+ Botan::Encrypted_PSK_Database_SQL psk(db_key, db, "psk");
+
+ psk_operation(psk);
+ }
+
+ private:
+ virtual void psk_operation(Botan::PSK_Database& db) = 0;
+ };
+
+class PSK_Tool_Set final : public PSK_Tool_Base
+ {
+ public:
+ PSK_Tool_Set() : PSK_Tool_Base("psk_set db db_key name psk") {}
+
+ std::string description() const override
+ {
+ return "Save a PSK encrypted in the database";
+ }
+
+ private:
+ void psk_operation(Botan::PSK_Database& db) override
+ {
+ const std::string name = get_arg("name");
+ const Botan::secure_vector<uint8_t> psk = Botan::hex_decode_locked(get_passphrase_arg("PSK", "psk"));
+ db.set_vec(name, psk);
+ }
+ };
+
+class PSK_Tool_Get final : public PSK_Tool_Base
+ {
+ public:
+ PSK_Tool_Get() : PSK_Tool_Base("psk_get db db_key name") {}
+
+ std::string description() const override
+ {
+ return "Read a value saved with psk_set";
+ }
+
+ private:
+ void psk_operation(Botan::PSK_Database& db) override
+ {
+ const std::string name = get_arg("name");
+ const Botan::secure_vector<uint8_t> val = db.get(name);
+ output() << Botan::hex_encode(val) << "\n";
+ }
+ };
+
+class PSK_Tool_List final : public PSK_Tool_Base
+ {
+ public:
+ PSK_Tool_List() : PSK_Tool_Base("psk_list db db_key") {}
+
+ std::string description() const override
+ {
+ return "List all values saved to the database";
+ }
+
+ private:
+ void psk_operation(Botan::PSK_Database& db) override
+ {
+ const std::set<std::string> names = db.list_names();
+
+ for(std::string name : names)
+ output() << name << "\n";
+ }
+ };
+
+BOTAN_REGISTER_COMMAND("psk_set", PSK_Tool_Set);
+BOTAN_REGISTER_COMMAND("psk_get", PSK_Tool_Get);
+BOTAN_REGISTER_COMMAND("psk_list", PSK_Tool_List);
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/cli/pubkey.cpp b/comm/third_party/botan/src/cli/pubkey.cpp
new file mode 100644
index 0000000000..7c7e1bfc0d
--- /dev/null
+++ b/comm/third_party/botan/src/cli/pubkey.cpp
@@ -0,0 +1,554 @@
+/*
+* (C) 2010,2014,2015,2019 Jack Lloyd
+* (C) 2019 Matthias Gierlings
+* (C) 2015 René Korthaus
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "cli.h"
+
+#if defined(BOTAN_HAS_PUBLIC_KEY_CRYPTO)
+
+#include <botan/base64.h>
+#include <botan/hex.h>
+#include <botan/rng.h>
+
+#include <botan/pk_keys.h>
+#include <botan/x509_key.h>
+#include <botan/pk_algs.h>
+#include <botan/pkcs8.h>
+#include <botan/pubkey.h>
+#include <botan/workfactor.h>
+#include <botan/data_src.h>
+
+#include <fstream>
+
+#if defined(BOTAN_HAS_DL_GROUP)
+ #include <botan/dl_group.h>
+#endif
+
+#if defined(BOTAN_HAS_ECC_GROUP)
+ #include <botan/ec_group.h>
+#endif
+
+namespace Botan_CLI {
+
+class PK_Keygen final : public Command
+ {
+ public:
+ PK_Keygen() : Command("keygen --algo=RSA --params= --passphrase= --pbe= --pbe-millis=300 --provider= --der-out") {}
+
+ std::string group() const override
+ {
+ return "pubkey";
+ }
+
+ std::string description() const override
+ {
+ return "Generate a PKCS #8 private key";
+ }
+
+ void go() override
+ {
+ const std::string algo = get_arg("algo");
+ const std::string params = get_arg("params");
+ const std::string provider = get_arg("provider");
+
+ std::unique_ptr<Botan::Private_Key> key =
+ Botan::create_private_key(algo, rng(), params, provider);
+
+ if(!key)
+ {
+ throw CLI_Error_Unsupported("keygen", algo);
+ }
+
+ const std::string pass = get_passphrase_arg("Key passphrase", "passphrase");
+ const bool der_out = flag_set("der-out");
+
+ const std::chrono::milliseconds pbe_millis(get_arg_sz("pbe-millis"));
+ const std::string pbe = get_arg("pbe");
+
+ if(der_out)
+ {
+ if(pass.empty())
+ {
+ write_output(Botan::PKCS8::BER_encode(*key));
+ }
+ else
+ {
+ write_output(Botan::PKCS8::BER_encode(*key, rng(), pass, pbe_millis, pbe));
+ }
+ }
+ else
+ {
+ if(pass.empty())
+ {
+ output() << Botan::PKCS8::PEM_encode(*key);
+ }
+ else
+ {
+ output() << Botan::PKCS8::PEM_encode(*key, rng(), pass, pbe_millis, pbe);
+ }
+ }
+ }
+ };
+
+BOTAN_REGISTER_COMMAND("keygen", PK_Keygen);
+
+#if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
+
+namespace {
+
+std::string choose_sig_padding(const std::string& key, const std::string& emsa, const std::string& hash)
+ {
+ std::string emsa_or_default = [&]() -> std::string
+ {
+ if(!emsa.empty())
+ {
+ return emsa;
+ }
+
+ if(key == "RSA")
+ {
+ return "EMSA4";
+ } // PSS
+ else if(key == "ECDSA" || key == "DSA")
+ {
+ return "EMSA1";
+ }
+ else if(key == "Ed25519")
+ {
+ return "";
+ }
+ else
+ {
+ return "EMSA1";
+ }
+ }();
+
+ if(emsa_or_default.empty())
+ {
+ return hash;
+ }
+
+ return emsa_or_default + "(" + hash + ")";
+ }
+
+}
+
+class PK_Fingerprint final : public Command
+ {
+ public:
+ PK_Fingerprint() : Command("fingerprint --no-fsname --algo=SHA-256 *keys") {}
+
+ std::string group() const override
+ {
+ return "pubkey";
+ }
+
+ std::string description() const override
+ {
+ return "Calculate a public key fingerprint";
+ }
+
+ void go() override
+ {
+ const std::string hash_algo = get_arg("algo");
+ const bool no_fsname = flag_set("no-fsname");
+
+ for(std::string key_file : get_arg_list("keys"))
+ {
+ std::unique_ptr<Botan::Public_Key> key(
+ key_file == "-"
+ ? Botan::X509::load_key(this->slurp_file("-", 4096))
+ : Botan::X509::load_key(key_file));
+
+ const std::string fprint = key->fingerprint_public(hash_algo);
+
+ if(no_fsname || key_file == "-")
+ { output() << fprint << "\n"; }
+ else
+ { output() << key_file << ": " << fprint << "\n"; }
+ }
+ }
+ };
+
+BOTAN_REGISTER_COMMAND("fingerprint", PK_Fingerprint);
+
+class PK_Sign final : public Command
+ {
+ public:
+ PK_Sign() : Command("sign --der-format --passphrase= --hash=SHA-256 --emsa= --provider= key file") {}
+
+ std::string group() const override
+ {
+ return "pubkey";
+ }
+
+ std::string description() const override
+ {
+ return "Sign arbitrary data";
+ }
+
+ void go() override
+ {
+ const std::string key_file = get_arg("key");
+ const std::string passphrase = get_passphrase_arg("Passphrase for " + key_file, "passphrase");
+
+ Botan::DataSource_Stream input(key_file);
+ std::unique_ptr<Botan::Private_Key> key = Botan::PKCS8::load_key(input, passphrase);
+
+ if(!key)
+ {
+ throw CLI_Error("Unable to load private key");
+ }
+
+ const std::string sig_padding =
+ choose_sig_padding(key->algo_name(), get_arg("emsa"), get_arg("hash"));
+
+ const Botan::Signature_Format format =
+ flag_set("der-format") ? Botan::DER_SEQUENCE : Botan::IEEE_1363;
+
+ const std::string provider = get_arg("provider");
+
+ Botan::PK_Signer signer(*key, rng(), sig_padding, format, provider);
+
+ auto onData = [&signer](const uint8_t b[], size_t l)
+ {
+ signer.update(b, l);
+ };
+ this->read_file(get_arg("file"), onData);
+
+ std::vector<uint8_t> sig { signer.signature(rng()) };
+
+ if(key->stateful_operation())
+ {
+ std::ofstream updated_key(key_file);
+ if(passphrase.empty())
+ { updated_key << Botan::PKCS8::PEM_encode(*key); }
+ else
+ { updated_key << Botan::PKCS8::PEM_encode(*key, rng(), passphrase); }
+ }
+
+ output() << Botan::base64_encode(sig) << "\n";
+ }
+ };
+
+BOTAN_REGISTER_COMMAND("sign", PK_Sign);
+
+class PK_Verify final : public Command
+ {
+ public:
+ PK_Verify() : Command("verify --der-format --hash=SHA-256 --emsa= pubkey file signature") {}
+
+ std::string group() const override
+ {
+ return "pubkey";
+ }
+
+ std::string description() const override
+ {
+ return "Verify the authenticity of the given file with the provided signature";
+ }
+
+ void go() override
+ {
+ std::unique_ptr<Botan::Public_Key> key(Botan::X509::load_key(get_arg("pubkey")));
+ if(!key)
+ {
+ throw CLI_Error("Unable to load public key");
+ }
+
+ const std::string sig_padding =
+ choose_sig_padding(key->algo_name(), get_arg("emsa"), get_arg("hash"));
+
+ const Botan::Signature_Format format =
+ flag_set("der-format") ? Botan::DER_SEQUENCE : Botan::IEEE_1363;
+
+ Botan::PK_Verifier verifier(*key, sig_padding, format);
+ auto onData = [&verifier](const uint8_t b[], size_t l)
+ {
+ verifier.update(b, l);
+ };
+ this->read_file(get_arg("file"), onData);
+
+ const Botan::secure_vector<uint8_t> signature =
+ Botan::base64_decode(this->slurp_file_as_str(get_arg("signature")));
+
+ const bool valid = verifier.check_signature(signature);
+
+ output() << "Signature is " << (valid ? "valid" : "invalid") << "\n";
+ }
+ };
+
+BOTAN_REGISTER_COMMAND("verify", PK_Verify);
+
+class PKCS8_Tool final : public Command
+ {
+ public:
+ PKCS8_Tool() : Command("pkcs8 --pass-in= --pub-out --der-out --pass-out= --pbe= --pbe-millis=300 key") {}
+
+ std::string group() const override
+ {
+ return "pubkey";
+ }
+
+ std::string description() const override
+ {
+ return "Open a PKCS #8 formatted key";
+ }
+
+ void go() override
+ {
+ const std::string key_file = get_arg("key");
+ const std::string pass_in = get_passphrase_arg("Password for " + key_file, "pass-in");
+
+ Botan::DataSource_Memory key_src(slurp_file(key_file));
+ std::unique_ptr<Botan::Private_Key> key;
+
+ if(pass_in.empty())
+ {
+ key.reset(Botan::PKCS8::load_key(key_src, rng()));
+ }
+ else
+ {
+ key.reset(Botan::PKCS8::load_key(key_src, rng(), pass_in));
+ }
+
+ const std::chrono::milliseconds pbe_millis(get_arg_sz("pbe-millis"));
+ const std::string pbe = get_arg("pbe");
+ const bool der_out = flag_set("der-out");
+
+ if(flag_set("pub-out"))
+ {
+ if(der_out)
+ {
+ write_output(Botan::X509::BER_encode(*key));
+ }
+ else
+ {
+ output() << Botan::X509::PEM_encode(*key);
+ }
+ }
+ else
+ {
+ const std::string pass_out = get_passphrase_arg("Passphrase to encrypt key", "pass-out");
+
+ if(der_out)
+ {
+ if(pass_out.empty())
+ {
+ write_output(Botan::PKCS8::BER_encode(*key));
+ }
+ else
+ {
+ write_output(Botan::PKCS8::BER_encode(*key, rng(), pass_out, pbe_millis, pbe));
+ }
+ }
+ else
+ {
+ if(pass_out.empty())
+ {
+ output() << Botan::PKCS8::PEM_encode(*key);
+ }
+ else
+ {
+ output() << Botan::PKCS8::PEM_encode(*key, rng(), pass_out, pbe_millis, pbe);
+ }
+ }
+ }
+ }
+ };
+
+BOTAN_REGISTER_COMMAND("pkcs8", PKCS8_Tool);
+
+#endif
+
+#if defined(BOTAN_HAS_ECC_GROUP)
+
+class EC_Group_Info final : public Command
+ {
+ public:
+ EC_Group_Info() : Command("ec_group_info --pem name") {}
+
+ std::string group() const override
+ {
+ return "pubkey";
+ }
+
+ std::string description() const override
+ {
+ return "Print raw elliptic curve domain parameters of the standardized curve name";
+ }
+
+ void go() override
+ {
+ Botan::EC_Group ec_group(get_arg("name"));
+
+ if(flag_set("pem"))
+ {
+ output() << ec_group.PEM_encode();
+ }
+ else
+ {
+ output() << "P = " << std::hex << ec_group.get_p() << "\n"
+ << "A = " << std::hex << ec_group.get_a() << "\n"
+ << "B = " << std::hex << ec_group.get_b() << "\n"
+ << "N = " << std::hex << ec_group.get_order() << "\n"
+ << "G = " << ec_group.get_g_x() << "," << ec_group.get_g_y() << "\n";
+ }
+
+ }
+ };
+
+BOTAN_REGISTER_COMMAND("ec_group_info", EC_Group_Info);
+
+#endif
+
+#if defined(BOTAN_HAS_DL_GROUP)
+
+class DL_Group_Info final : public Command
+ {
+ public:
+ DL_Group_Info() : Command("dl_group_info --pem name") {}
+
+ std::string group() const override
+ {
+ return "pubkey";
+ }
+
+ std::string description() const override
+ {
+ return "Print raw Diffie-Hellman parameters (p,g) of the standardized DH group name";
+ }
+
+ void go() override
+ {
+ Botan::DL_Group dl_group(get_arg("name"));
+
+ if(flag_set("pem"))
+ {
+ output() << dl_group.PEM_encode(Botan::DL_Group::ANSI_X9_42_DH_PARAMETERS);
+ }
+ else
+ {
+ output() << "P = " << std::hex << dl_group.get_p() << "\n"
+ << "G = " << dl_group.get_g() << "\n";
+ }
+
+ }
+ };
+
+BOTAN_REGISTER_COMMAND("dl_group_info", DL_Group_Info);
+
+class PK_Workfactor final : public Command
+ {
+ public:
+ PK_Workfactor() : Command("pk_workfactor --type=rsa bits") {}
+
+ std::string group() const override
+ {
+ return "pubkey";
+ }
+
+ std::string description() const override
+ {
+ return "Provide estimate of strength of public key based on size";
+ }
+
+ void go() override
+ {
+ const size_t bits = get_arg_sz("bits");
+ const std::string type = get_arg("type");
+
+ if(type == "rsa")
+ { output() << Botan::if_work_factor(bits) << "\n"; }
+ else if(type == "dl")
+ { output() << Botan::dl_work_factor(bits) << "\n"; }
+ else if(type == "dl_exp")
+ { output() << Botan::dl_exponent_size(bits) << "\n"; }
+ else
+ { throw CLI_Usage_Error("Unknown type for pk_workfactor"); }
+ }
+ };
+
+BOTAN_REGISTER_COMMAND("pk_workfactor", PK_Workfactor);
+
+class Gen_DL_Group final : public Command
+ {
+ public:
+ Gen_DL_Group() : Command("gen_dl_group --pbits=1024 --qbits=0 --seed= --type=subgroup") {}
+
+ std::string group() const override
+ {
+ return "pubkey";
+ }
+
+ std::string description() const override
+ {
+ return "Generate ANSI X9.42 encoded Diffie-Hellman group parameters";
+ }
+
+ void go() override
+ {
+ const size_t pbits = get_arg_sz("pbits");
+ const size_t qbits = get_arg_sz("qbits");
+
+ const std::string type = get_arg("type");
+ const std::string seed_str = get_arg("seed");
+
+ if(type == "strong")
+ {
+ if(seed_str.size() > 0)
+ { throw CLI_Usage_Error("Seed only supported for DSA param gen"); }
+ Botan::DL_Group grp(rng(), Botan::DL_Group::Strong, pbits);
+ output() << grp.PEM_encode(Botan::DL_Group::ANSI_X9_42);
+ }
+ else if(type == "subgroup")
+ {
+ if(seed_str.size() > 0)
+ { throw CLI_Usage_Error("Seed only supported for DSA param gen"); }
+ Botan::DL_Group grp(rng(), Botan::DL_Group::Prime_Subgroup, pbits, qbits);
+ output() << grp.PEM_encode(Botan::DL_Group::ANSI_X9_42);
+ }
+ else if(type == "dsa")
+ {
+ size_t dsa_qbits = qbits;
+ if(dsa_qbits == 0)
+ {
+ if(pbits == 1024)
+ { dsa_qbits = 160; }
+ else if(pbits == 2048 || pbits == 3072)
+ { dsa_qbits = 256; }
+ else
+ { throw CLI_Usage_Error("Invalid DSA p/q sizes"); }
+ }
+
+ if(seed_str.empty())
+ {
+ Botan::DL_Group grp(rng(), Botan::DL_Group::DSA_Kosherizer, pbits, dsa_qbits);
+ output() << grp.PEM_encode(Botan::DL_Group::ANSI_X9_42);
+ }
+ else
+ {
+ const std::vector<uint8_t> seed = Botan::hex_decode(seed_str);
+ Botan::DL_Group grp(rng(), seed, pbits, dsa_qbits);
+ output() << grp.PEM_encode(Botan::DL_Group::ANSI_X9_42);
+ }
+
+ }
+ else
+ {
+ throw CLI_Usage_Error("Invalid DL type '" + type + "'");
+ }
+ }
+ };
+
+BOTAN_REGISTER_COMMAND("gen_dl_group", Gen_DL_Group);
+
+#endif
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/cli/roughtime.cpp b/comm/third_party/botan/src/cli/roughtime.cpp
new file mode 100644
index 0000000000..ff38fe1c43
--- /dev/null
+++ b/comm/third_party/botan/src/cli/roughtime.cpp
@@ -0,0 +1,215 @@
+/*
+* Roughtime
+* (C) 2019 Nuno Goncalves <nunojpg@gmail.com>
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "cli.h"
+
+#if defined(BOTAN_HAS_ROUGHTIME)
+
+#include <botan/roughtime.h>
+#include <botan/hex.h>
+#include <botan/rng.h>
+#include <botan/base64.h>
+#include <botan/ed25519.h>
+#include <botan/hash.h>
+#include <botan/calendar.h>
+
+#include <iomanip>
+#include <fstream>
+
+namespace Botan_CLI {
+
+class RoughtimeCheck final : public Command
+ {
+ public:
+ RoughtimeCheck() : Command("roughtime_check --raw-time chain-file") {}
+
+ std::string group() const override
+ {
+ return "misc";
+ }
+
+ std::string description() const override
+ {
+ return "Parse and validate Roughtime chain file";
+ }
+
+ void go() override
+ {
+ const auto chain = Botan::Roughtime::Chain(slurp_file_as_str(get_arg("chain-file")));
+ unsigned i = 0;
+ for(const auto& response : chain.responses())
+ {
+ output() << std::setw(3) << ++i << ": UTC ";
+ if(flag_set("raw-time"))
+ { output() << Botan::Roughtime::Response::sys_microseconds64(response.utc_midpoint()).time_since_epoch().count(); }
+ else
+ { output() << Botan::calendar_value(response.utc_midpoint()).to_string(); }
+ output() << " (+-" << Botan::Roughtime::Response::microseconds32(response.utc_radius()).count() << "us)\n";
+ }
+ }
+ };
+
+BOTAN_REGISTER_COMMAND("roughtime_check", RoughtimeCheck);
+
+class Roughtime final : public Command
+ {
+ public:
+ Roughtime() :
+ Command("roughtime --raw-time --chain-file=roughtime-chain --max-chain-size=128 --check-local-clock=60 --host= --pubkey= --servers-file=") {}
+
+ std::string help_text() const override
+ {
+ return Command::help_text() + R"(
+
+--servers-file=<filename>
+ List of servers that will queried in sequence.
+
+ File contents syntax:
+ <name> <key type> <base 64 encoded public key> <protocol> <host:port>
+
+ Example servers:
+ Cloudflare-Roughtime ed25519 gD63hSj3ScS+wuOeGrubXlq35N1c5Lby/S+T7MNTjxo= udp roughtime.cloudflare.com:2002
+ Google-Sandbox-Roughtime ed25519 etPaaIxcBMY1oUeGpwvPMCJMwlRVNxv51KK/tktoJTQ= udp roughtime.sandbox.google.com:2002
+
+--chain-file=<filename>
+ Succesfull 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 <chain-file>.
+
+ File contents syntax:
+ <key type> <base 64 encoded public key> <base 64 encoded blind or nonce> <base 64 encoded server response>
+)";
+ }
+
+ std::string group() const override
+ {
+ return "misc";
+ }
+
+ std::string description() const override
+ {
+ return "Retrieve time from Roughtime server";
+ }
+
+ void query(std::unique_ptr<Botan::Roughtime::Chain>& chain,
+ const size_t max_chain_size,
+ const std::string& address,
+ const Botan::Ed25519_PublicKey& public_key)
+ {
+ Botan::Roughtime::Nonce nonce;
+ Botan::Roughtime::Nonce blind;
+ if(chain)
+ {
+ blind = Botan::Roughtime::Nonce(rng());
+ nonce = chain->next_nonce(blind);
+ }
+ else
+ {
+ nonce = Botan::Roughtime::Nonce(rng());
+ }
+ const auto response_raw = Botan::Roughtime::online_request(address, nonce, std::chrono::seconds(5));
+ const auto response = Botan::Roughtime::Response::from_bits(response_raw, nonce);
+ if(flag_set("raw-time"))
+ { output() << "UTC " << Botan::Roughtime::Response::sys_microseconds64(response.utc_midpoint()).time_since_epoch().count(); }
+ else
+ { output() << "UTC " << Botan::calendar_value(response.utc_midpoint()).to_string(); }
+ output() << " (+-" << Botan::Roughtime::Response::microseconds32(response.utc_radius()).count() << "us)";
+ if(!response.validate(public_key))
+ {
+ error_output() << "ERROR: Public key does not match!\n";
+ set_return_code(1);
+ return;
+ }
+ const auto tolerance = get_arg_sz("check-local-clock");
+ if(tolerance)
+ {
+ const auto now = std::chrono::system_clock::now();
+ const auto diff_abs = now >= response.utc_midpoint() ? now - response.utc_midpoint() : response.utc_midpoint() - now;
+ if(diff_abs > (response.utc_radius() + std::chrono::seconds(tolerance)))
+ {
+ error_output() << "ERROR: Local clock mismatch\n";
+ set_return_code(1);
+ return;
+ }
+ output() << " Local clock match";
+ }
+ if(chain)
+ chain->append({response_raw, public_key, blind}, max_chain_size);
+ output() << '\n';
+ }
+
+ void go() override
+ {
+
+ const auto max_chain_size = get_arg_sz("max-chain-size");
+ const auto chain_file = get_arg("chain-file");
+ const auto servers_file = get_arg_or("servers-file", "");
+ const auto host = get_arg_or("host", "");
+ const auto pk = get_arg_or("pubkey", "");
+
+ std::unique_ptr<Botan::Roughtime::Chain> chain;
+ if(!chain_file.empty() && max_chain_size >= 1)
+ {
+ try
+ {
+ chain.reset(new Botan::Roughtime::Chain(slurp_file_as_str(chain_file)));
+ }
+ catch(const CLI_IO_Error&)
+ {
+ chain.reset(new Botan::Roughtime::Chain()); //file is to still be created
+ }
+ }
+
+ const bool from_servers_file = !servers_file.empty();
+ const bool from_host_and_pk = !host.empty() && !pk.empty();
+ if(from_servers_file == from_host_and_pk)
+ {
+ error_output() << "Please specify either --servers-file or --host and --pubkey\n";
+ set_return_code(1);
+ return;
+ }
+
+ if(!servers_file.empty())
+ {
+ const auto servers = Botan::Roughtime::servers_from_str(slurp_file_as_str(servers_file));
+
+ for(const auto& s : servers)
+ {
+ output() << std::setw(25) << std::left << s.name() << ": ";
+ for(const auto& a : s.addresses())
+ {
+ try
+ {
+ query(chain, max_chain_size, a, s.public_key());
+ break;
+ }
+ catch(const std::exception& ex) //network error, try next address
+ {
+ error_output() << ex.what() << '\n';
+ }
+ }
+ }
+
+ }
+ else
+ {
+ query(chain, max_chain_size, host, Botan::Ed25519_PublicKey(Botan::base64_decode(pk)));
+ }
+
+ if(chain)
+ {
+ std::ofstream out(chain_file);
+ out << chain->to_string();
+ }
+ }
+ };
+
+BOTAN_REGISTER_COMMAND("roughtime", Roughtime);
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/cli/sandbox.cpp b/comm/third_party/botan/src/cli/sandbox.cpp
new file mode 100644
index 0000000000..6ac8007af2
--- /dev/null
+++ b/comm/third_party/botan/src/cli/sandbox.cpp
@@ -0,0 +1,115 @@
+/*
+* (C) 2019 David Carlier <devnexen@gmail.com>
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "sandbox.h"
+#include <botan/mem_ops.h>
+
+#if defined(BOTAN_TARGET_OS_HAS_PLEDGE)
+ #include <unistd.h>
+#elif defined(BOTAN_TARGET_OS_HAS_CAP_ENTER)
+ #include <sys/capsicum.h>
+ #include <unistd.h>
+#elif defined(BOTAN_TARGET_OS_HAS_SETPPRIV)
+ #include <priv.h>
+#endif
+
+namespace Botan_CLI {
+
+#if defined(BOTAN_TARGET_OS_HAS_SETPPRIV)
+struct SandboxPrivDelete {
+ void operator()(priv_set_t *ps)
+ {
+ ::priv_emptyset(ps);
+ ::priv_freeset(ps);
+ }
+};
+#endif
+
+Sandbox::Sandbox()
+ {
+#if defined(BOTAN_TARGET_OS_HAS_PLEDGE)
+ m_name = "pledge";
+#elif defined(BOTAN_TARGET_OS_HAS_CAP_ENTER)
+ m_name = "capsicum";
+#elif defined(BOTAN_TARGET_OS_HAS_SETPPRIV)
+ m_name = "privilege";
+#else
+ m_name = "<none>";
+#endif
+ }
+
+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)
+ cap_rights_t wt, rd;
+
+ if (::cap_rights_init(&wt, CAP_READ, CAP_WRITE) == nullptr)
+ {
+ return false;
+ }
+
+ if (::cap_rights_init(&rd, CAP_FCNTL, CAP_EVENT, CAP_READ) == nullptr)
+ {
+ return false;
+ }
+
+ if (::cap_rights_limit(STDOUT_FILENO, &wt) == -1)
+ {
+ return false;
+ }
+
+ if (::cap_rights_limit(STDERR_FILENO, &wt) == -1)
+ {
+ return false;
+ }
+
+ if (::cap_rights_limit(STDIN_FILENO, &rd) == -1)
+ {
+ return false;
+ }
+
+ return (::cap_enter() == 0);
+#elif defined(BOTAN_TARGET_OS_HAS_SETPPRIV)
+ priv_set_t *tmp;
+ std::unique_ptr<priv_set_t, SandboxPrivDelete> ps;
+ const char *const priv_perms[] = {
+ PRIV_PROC_FORK,
+ PRIV_PROC_EXEC,
+ PRIV_PROC_INFO,
+ PRIV_PROC_SESSION,
+ };
+
+ if ((tmp = ::priv_allocset()) == nullptr)
+ {
+ return false;
+ }
+
+ ps = std::unique_ptr<priv_set_t, SandboxPrivDelete>(tmp);
+ ::priv_basicset(ps.get());
+
+ for (auto perm: priv_perms)
+ {
+ if (::priv_delset(ps.get(), perm) == -1)
+ {
+ return false;
+ }
+ }
+
+ return true;
+#else
+ return true;
+#endif
+ }
+
+Sandbox::~Sandbox()
+ {
+ }
+}
diff --git a/comm/third_party/botan/src/cli/sandbox.h b/comm/third_party/botan/src/cli/sandbox.h
new file mode 100644
index 0000000000..2d6f5c4df8
--- /dev/null
+++ b/comm/third_party/botan/src/cli/sandbox.h
@@ -0,0 +1,32 @@
+/*
+* (C) 2019 David Carlier <devnexen@gmail.com>
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_CLI_SANDBOX_H_
+#define BOTAN_CLI_SANDBOX_H_
+
+#include <string>
+
+namespace Botan_CLI {
+
+class Sandbox
+ {
+ public:
+ explicit Sandbox();
+ virtual ~Sandbox();
+
+ bool init();
+
+ const std::string& name() const
+ {
+ return m_name;
+ }
+
+ private:
+ std::string m_name;
+ };
+}
+
+#endif
diff --git a/comm/third_party/botan/src/cli/socket_utils.h b/comm/third_party/botan/src/cli/socket_utils.h
new file mode 100644
index 0000000000..d52b5a0e7c
--- /dev/null
+++ b/comm/third_party/botan/src/cli/socket_utils.h
@@ -0,0 +1,105 @@
+/*
+* (C) 2014,2017 Jack Lloyd
+* 2017 René Korthaus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_CLI_SOCKET_UTILS_H_
+#define BOTAN_CLI_SOCKET_UTILS_H_
+
+#include <botan/build.h>
+#include "cli_exceptions.h"
+
+#if defined(BOTAN_TARGET_OS_HAS_WINSOCK2)
+
+#include <winsock2.h>
+#include <WS2tcpip.h>
+
+typedef SOCKET socket_type;
+
+inline socket_type invalid_socket() { return INVALID_SOCKET; }
+
+typedef size_t ssize_t;
+typedef int sendrecv_len_type;
+
+inline void close_socket(socket_type s) { ::closesocket(s); }
+
+#define STDIN_FILENO _fileno(stdin)
+
+inline void init_sockets()
+ {
+ WSAData wsa_data;
+ WORD wsa_version = MAKEWORD(2, 2);
+
+ if(::WSAStartup(wsa_version, &wsa_data) != 0)
+ {
+ throw Botan_CLI::CLI_Error("WSAStartup() failed: " + std::to_string(WSAGetLastError()));
+ }
+
+ if(LOBYTE(wsa_data.wVersion) != 2 || HIBYTE(wsa_data.wVersion) != 2)
+ {
+ ::WSACleanup();
+ throw Botan_CLI::CLI_Error("Could not find a usable version of Winsock.dll");
+ }
+ }
+
+inline void stop_sockets()
+ {
+ ::WSACleanup();
+ }
+
+inline std::string err_to_string(int e)
+ {
+ // TODO use strerror_s here
+ return "Error code " + std::to_string(e);
+ }
+
+inline int close(int fd)
+ {
+ return ::closesocket(fd);
+ }
+
+inline int read(int s, void* buf, size_t len)
+ {
+ return ::recv(s, reinterpret_cast<char*>(buf), static_cast<int>(len), 0);
+ }
+
+inline int send(int s, const uint8_t* buf, size_t len, int flags)
+ {
+ return ::send(s, reinterpret_cast<const char*>(buf), static_cast<int>(len), flags);
+ }
+
+#elif defined(BOTAN_TARGET_OS_HAS_POSIX1)
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+
+typedef int socket_type;
+typedef size_t sendrecv_len_type;
+
+inline socket_type invalid_socket() { return -1; }
+inline void close_socket(socket_type s) { ::close(s); }
+
+inline void init_sockets() {}
+inline void stop_sockets() {}
+
+inline std::string err_to_string(int e)
+ {
+ return std::strerror(e);
+ }
+
+#endif
+
+#if !defined(MSG_NOSIGNAL)
+ #define MSG_NOSIGNAL 0
+#endif
+
+#endif
diff --git a/comm/third_party/botan/src/cli/speed.cpp b/comm/third_party/botan/src/cli/speed.cpp
new file mode 100644
index 0000000000..b8454d2a7a
--- /dev/null
+++ b/comm/third_party/botan/src/cli/speed.cpp
@@ -0,0 +1,2342 @@
+/*
+* (C) 2009,2010,2014,2015,2017,2018 Jack Lloyd
+* (C) 2015 Simon Warta (Kullo GmbH)
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "cli.h"
+#include "../tests/test_rng.h" // FIXME
+
+#include <sstream>
+#include <iomanip>
+#include <chrono>
+#include <functional>
+#include <algorithm>
+#include <map>
+#include <set>
+
+// Always available:
+#include <botan/entropy_src.h>
+#include <botan/parsing.h>
+#include <botan/cpuid.h>
+#include <botan/internal/os_utils.h>
+#include <botan/internal/timer.h>
+#include <botan/version.h>
+
+#if defined(BOTAN_HAS_BIGINT)
+ #include <botan/bigint.h>
+ #include <botan/divide.h>
+#endif
+
+#if defined(BOTAN_HAS_BLOCK_CIPHER)
+ #include <botan/block_cipher.h>
+#endif
+
+#if defined(BOTAN_HAS_STREAM_CIPHER)
+ #include <botan/stream_cipher.h>
+#endif
+
+#if defined(BOTAN_HAS_HASH)
+ #include <botan/hash.h>
+#endif
+
+#if defined(BOTAN_HAS_CIPHER_MODES)
+ #include <botan/cipher_mode.h>
+#endif
+
+#if defined(BOTAN_HAS_MAC)
+ #include <botan/mac.h>
+#endif
+
+#if defined(BOTAN_HAS_AUTO_SEEDING_RNG)
+ #include <botan/auto_rng.h>
+#endif
+
+#if defined(BOTAN_HAS_SYSTEM_RNG)
+ #include <botan/system_rng.h>
+#endif
+
+#if defined(BOTAN_HAS_HMAC_DRBG)
+ #include <botan/hmac_drbg.h>
+#endif
+
+#if defined(BOTAN_HAS_PROCESSOR_RNG)
+ #include <botan/processor_rng.h>
+#endif
+
+#if defined(BOTAN_HAS_CHACHA_RNG)
+ #include <botan/chacha_rng.h>
+#endif
+
+#if defined(BOTAN_HAS_FPE_FE1)
+ #include <botan/fpe_fe1.h>
+#endif
+
+#if defined(BOTAN_HAS_RFC3394_KEYWRAP)
+ #include <botan/rfc3394.h>
+#endif
+
+#if defined(BOTAN_HAS_COMPRESSION)
+ #include <botan/compression.h>
+#endif
+
+#if defined(BOTAN_HAS_POLY_DBL)
+ #include <botan/internal/poly_dbl.h>
+#endif
+
+#if defined(BOTAN_HAS_PUBLIC_KEY_CRYPTO)
+ #include <botan/pkcs8.h>
+ #include <botan/pubkey.h>
+ #include <botan/pk_algs.h>
+ #include <botan/x509_key.h>
+ #include <botan/workfactor.h>
+#endif
+
+#if defined(BOTAN_HAS_NUMBERTHEORY)
+ #include <botan/numthry.h>
+ #include <botan/reducer.h>
+ #include <botan/curve_nistp.h>
+ #include <botan/internal/primality.h>
+#endif
+
+#if defined(BOTAN_HAS_ECC_GROUP)
+ #include <botan/ec_group.h>
+#endif
+
+#if defined(BOTAN_HAS_DL_GROUP)
+ #include <botan/dl_group.h>
+#endif
+
+#if defined(BOTAN_HAS_MCELIECE)
+ #include <botan/mceliece.h>
+#endif
+
+#if defined(BOTAN_HAS_ECDSA)
+ #include <botan/ecdsa.h>
+#endif
+
+#if defined(BOTAN_HAS_NEWHOPE)
+ #include <botan/newhope.h>
+#endif
+
+#if defined(BOTAN_HAS_SCRYPT)
+ #include <botan/scrypt.h>
+#endif
+
+#if defined(BOTAN_HAS_ARGON2)
+ #include <botan/argon2.h>
+#endif
+
+#if defined(BOTAN_HAS_BCRYPT)
+ #include <botan/bcrypt.h>
+#endif
+
+#if defined(BOTAN_HAS_PASSHASH9)
+ #include <botan/passhash9.h>
+#endif
+
+namespace Botan_CLI {
+
+using Botan::Timer;
+
+namespace {
+
+class JSON_Output final
+ {
+ public:
+ void add(const Timer& timer) { m_results.push_back(timer); }
+
+ std::string print() const
+ {
+ std::ostringstream out;
+
+ out << "[\n";
+
+ for(size_t i = 0; i != m_results.size(); ++i)
+ {
+ if(i != 0)
+ out << ",";
+
+ const Timer& t = m_results[i];
+
+ out << '{';
+ out << "\"algo\": \"" << t.get_name() << "\", ";
+ out << "\"op\": \"" << t.doing() << "\", ";
+
+ out << "\"events\": " << t.events() << ", ";
+ if(t.cycles_consumed() > 0)
+ out << "\"cycles\": " << t.cycles_consumed() << ", ";
+ if(t.buf_size() > 0)
+ {
+ out << "\"bps\": " << static_cast<uint64_t>(t.events() / (t.value() / 1000000000.0)) << ", ";
+ out << "\"buf_size\": " << t.buf_size() << ", ";
+ }
+
+ out << "\"nanos\": " << t.value();
+
+ out << "}\n";
+ }
+ out << "]\n";
+
+ return out.str();
+ }
+ private:
+ std::vector<Timer> m_results;
+ };
+
+class Summary final
+ {
+ public:
+ Summary() {}
+
+ void add(const Timer& t)
+ {
+ if(t.buf_size() == 0)
+ {
+ m_ops_entries.push_back(t);
+ }
+ else
+ {
+ m_bps_entries[std::make_pair(t.doing(), t.get_name())].push_back(t);
+ }
+ }
+
+ std::string print()
+ {
+ const size_t name_padding = 35;
+ const size_t op_name_padding = 16;
+ const size_t op_padding = 16;
+
+ std::ostringstream result_ss;
+ result_ss << std::fixed;
+
+ if(m_bps_entries.size() > 0)
+ {
+ result_ss << "\n";
+
+ // add table header
+ result_ss << std::setw(name_padding) << std::left << "algo"
+ << std::setw(op_name_padding) << std::left << "operation";
+
+ for(const Timer& t : m_bps_entries.begin()->second)
+ {
+ result_ss << std::setw(op_padding) << std::right << (std::to_string(t.buf_size()) + " bytes");
+ }
+ result_ss << "\n";
+
+ // add table entries
+ for(const auto& entry : m_bps_entries)
+ {
+ if(entry.second.empty())
+ continue;
+
+ result_ss << std::setw(name_padding) << std::left << (entry.first.second)
+ << std::setw(op_name_padding) << std::left << (entry.first.first);
+
+ for(const Timer& t : entry.second)
+ {
+
+ if(t.events() == 0)
+ {
+ result_ss << std::setw(op_padding) << std::right << "N/A";
+ }
+ else
+ {
+ result_ss << std::setw(op_padding) << std::right
+ << std::setprecision(2) << (t.bytes_per_second() / 1000.0);
+ }
+ }
+
+ result_ss << "\n";
+ }
+
+ result_ss << "\n[results are the number of 1000s bytes processed per second]\n";
+ }
+
+ if(m_ops_entries.size() > 0)
+ {
+ result_ss << std::setprecision(6) << "\n";
+
+ // sort entries
+ std::sort(m_ops_entries.begin(), m_ops_entries.end());
+
+ // add table header
+ result_ss << std::setw(name_padding) << std::left << "algo"
+ << std::setw(op_name_padding) << std::left << "operation"
+ << std::setw(op_padding) << std::right << "sec/op"
+ << std::setw(op_padding) << std::right << "op/sec"
+ << "\n";
+
+ // add table entries
+ for(const Timer& entry : m_ops_entries)
+ {
+ result_ss << std::setw(name_padding) << std::left << entry.get_name()
+ << std::setw(op_name_padding) << std::left << entry.doing()
+ << std::setw(op_padding) << std::right << entry.seconds_per_event()
+ << std::setw(op_padding) << std::right << entry.events_per_second()
+ << "\n";
+ }
+ }
+
+ return result_ss.str();
+ }
+
+ private:
+ std::map<std::pair<std::string, std::string>, std::vector<Timer>> m_bps_entries;
+ std::vector<Timer> m_ops_entries;
+ };
+
+std::vector<size_t> unique_buffer_sizes(const std::string& cmdline_arg)
+ {
+ const size_t MAX_BUF_SIZE = 64*1024*1024;
+
+ std::set<size_t> buf;
+ for(std::string size_str : Botan::split_on(cmdline_arg, ','))
+ {
+ size_t x = 0;
+ try
+ {
+ size_t converted = 0;
+ x = static_cast<size_t>(std::stoul(size_str, &converted, 0));
+
+ if(converted != size_str.size())
+ throw CLI_Usage_Error("Invalid integer");
+ }
+ catch(std::exception&)
+ {
+ throw CLI_Usage_Error("Invalid integer value '" + size_str + "' for option buf-size");
+ }
+
+ if(x == 0)
+ throw CLI_Usage_Error("Cannot have a zero-sized buffer");
+
+ if(x > MAX_BUF_SIZE)
+ throw CLI_Usage_Error("Specified buffer size is too large");
+
+ buf.insert(x);
+ }
+
+ return std::vector<size_t>(buf.begin(), buf.end());
+ }
+
+}
+
+class Speed final : public Command
+ {
+ public:
+ Speed()
+ : Command("speed --msec=500 --format=default --ecc-groups= --provider= --buf-size=1024 --clear-cpuid= --cpu-clock-speed=0 --cpu-clock-ratio=1.0 *algos") {}
+
+ std::vector<std::string> default_benchmark_list()
+ {
+ /*
+ This is not intended to be exhaustive: it just hits the high
+ points of the most interesting or widely used algorithms.
+ */
+
+ return {
+ /* Block ciphers */
+ "AES-128",
+ "AES-192",
+ "AES-256",
+ "ARIA-128",
+ "ARIA-192",
+ "ARIA-256",
+ "Blowfish",
+ "CAST-128",
+ "CAST-256",
+ "Camellia-128",
+ "Camellia-192",
+ "Camellia-256",
+ "DES",
+ "TripleDES",
+ "GOST-28147-89",
+ "IDEA",
+ "KASUMI",
+ "MISTY1",
+ "Noekeon",
+ "SHACAL2",
+ "SM4",
+ "Serpent",
+ "Threefish-512",
+ "Twofish",
+ "XTEA",
+
+ /* Cipher modes */
+ "AES-128/CBC",
+ "AES-128/CTR-BE",
+ "AES-128/EAX",
+ "AES-128/OCB",
+ "AES-128/GCM",
+ "AES-128/XTS",
+ "AES-128/SIV",
+
+ "Serpent/CBC",
+ "Serpent/CTR-BE",
+ "Serpent/EAX",
+ "Serpent/OCB",
+ "Serpent/GCM",
+ "Serpent/XTS",
+ "Serpent/SIV",
+
+ "ChaCha20Poly1305",
+
+ /* Stream ciphers */
+ "RC4",
+ "Salsa20",
+ "ChaCha20",
+
+ /* Hashes */
+ "SHA-160",
+ "SHA-256",
+ "SHA-512",
+ "SHA-3(256)",
+ "SHA-3(512)",
+ "RIPEMD-160",
+ "Skein-512",
+ "Blake2b",
+ "Tiger",
+ "Whirlpool",
+
+ /* MACs */
+ "CMAC(AES-128)",
+ "HMAC(SHA-256)",
+
+ /* pubkey */
+ "RSA",
+ "DH",
+ "ECDH",
+ "ECDSA",
+ "Ed25519",
+ "Curve25519",
+ "NEWHOPE",
+ "McEliece",
+ };
+ }
+
+ std::string group() const override
+ {
+ return "misc";
+ }
+
+ std::string description() const override
+ {
+ return "Measures the speed of algorithms";
+ }
+
+ void go() override
+ {
+ std::chrono::milliseconds msec(get_arg_sz("msec"));
+ const std::string provider = get_arg("provider");
+ std::vector<std::string> ecc_groups = Botan::split_on(get_arg("ecc-groups"), ',');
+ const std::string format = get_arg("format");
+ const std::string clock_ratio = get_arg("cpu-clock-ratio");
+ m_clock_speed = get_arg_sz("cpu-clock-speed");
+
+ m_clock_cycle_ratio = std::strtod(clock_ratio.c_str(), nullptr);
+
+ /*
+ * This argument is intended to be the ratio between the cycle counter
+ * and the actual machine cycles. It is extremely unlikely that there is
+ * any machine where the cycle counter increments faster than the actual
+ * clock.
+ */
+ if(m_clock_cycle_ratio < 0.0 || m_clock_cycle_ratio > 1.0)
+ throw CLI_Usage_Error("Unlikely CPU clock ratio of " + clock_ratio);
+
+ m_clock_cycle_ratio = 1.0 / m_clock_cycle_ratio;
+
+ if(m_clock_speed != 0 && Botan::OS::get_cpu_cycle_counter() != 0)
+ {
+ error_output() << "The --cpu-clock-speed option is only intended to be used on "
+ "platforms without access to a cycle counter.\n"
+ "Expected incorrect results\n\n";
+ }
+
+ if(format == "table")
+ m_summary.reset(new Summary);
+ else if(format == "json")
+ m_json.reset(new JSON_Output);
+ else if(format != "default")
+ throw CLI_Usage_Error("Unknown --format type '" + format + "'");
+
+#if defined(BOTAN_HAS_ECC_GROUP)
+ if(ecc_groups.empty())
+ {
+ ecc_groups = { "secp256r1", "brainpool256r1",
+ "secp384r1", "brainpool384r1",
+ "secp521r1", "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
+
+ std::vector<std::string> algos = get_arg_list("algos");
+
+ const std::vector<size_t> buf_sizes = unique_buffer_sizes(get_arg("buf-size"));
+
+ for(std::string cpuid_to_clear : Botan::split_on(get_arg("clear-cpuid"), ','))
+ {
+ auto bits = Botan::CPUID::bit_from_string(cpuid_to_clear);
+ if(bits.empty())
+ {
+ error_output() << "Warning don't know CPUID flag '" << cpuid_to_clear << "'\n";
+ }
+
+ for(auto bit : bits)
+ {
+ Botan::CPUID::clear_cpuid_bit(bit);
+ }
+ }
+
+ if(verbose() || m_summary)
+ {
+ output() << Botan::version_string() << "\n"
+ << "CPUID: " << Botan::CPUID::to_string() << "\n\n";
+ }
+
+ const bool using_defaults = (algos.empty());
+ if(using_defaults)
+ {
+ algos = default_benchmark_list();
+ }
+
+ for(auto algo : algos)
+ {
+ using namespace std::placeholders;
+
+ if(false)
+ {
+ // Since everything might be disabled, need a block to else if from
+ }
+#if defined(BOTAN_HAS_HASH)
+ else if(Botan::HashFunction::providers(algo).size() > 0)
+ {
+ bench_providers_of<Botan::HashFunction>(
+ algo, provider, msec, buf_sizes,
+ std::bind(&Speed::bench_hash, this, _1, _2, _3, _4));
+ }
+#endif
+#if defined(BOTAN_HAS_BLOCK_CIPHER)
+ else if(Botan::BlockCipher::providers(algo).size() > 0)
+ {
+ bench_providers_of<Botan::BlockCipher>(
+ algo, provider, msec, buf_sizes,
+ std::bind(&Speed::bench_block_cipher, this, _1, _2, _3, _4));
+ }
+#endif
+#if defined(BOTAN_HAS_STREAM_CIPHER)
+ else if(Botan::StreamCipher::providers(algo).size() > 0)
+ {
+ bench_providers_of<Botan::StreamCipher>(
+ algo, provider, msec, buf_sizes,
+ std::bind(&Speed::bench_stream_cipher, this, _1, _2, _3, _4));
+ }
+#endif
+#if defined(BOTAN_HAS_CIPHER_MODES)
+ else if(auto enc = Botan::Cipher_Mode::create(algo, Botan::ENCRYPTION, provider))
+ {
+ auto dec = Botan::Cipher_Mode::create_or_throw(algo, Botan::DECRYPTION, provider);
+ bench_cipher_mode(*enc, *dec, msec, buf_sizes);
+ }
+#endif
+#if defined(BOTAN_HAS_MAC)
+ else if(Botan::MessageAuthenticationCode::providers(algo).size() > 0)
+ {
+ bench_providers_of<Botan::MessageAuthenticationCode>(
+ algo, provider, msec, buf_sizes,
+ std::bind(&Speed::bench_mac, this, _1, _2, _3, _4));
+ }
+#endif
+#if defined(BOTAN_HAS_RSA)
+ else if(algo == "RSA")
+ {
+ bench_rsa(provider, msec);
+ }
+ else if(algo == "RSA_keygen")
+ {
+ bench_rsa_keygen(provider, msec);
+ }
+#endif
+#if defined(BOTAN_HAS_ECDSA)
+ else if(algo == "ECDSA")
+ {
+ bench_ecdsa(ecc_groups, provider, msec);
+ }
+ else if(algo == "ecdsa_recovery")
+ {
+ bench_ecdsa_recovery(ecc_groups, provider, msec);
+ }
+#endif
+#if defined(BOTAN_HAS_SM2)
+ else if(algo == "SM2")
+ {
+ bench_sm2(ecc_groups, provider, msec);
+ }
+#endif
+#if defined(BOTAN_HAS_ECKCDSA)
+ else if(algo == "ECKCDSA")
+ {
+ bench_eckcdsa(ecc_groups, provider, msec);
+ }
+#endif
+#if defined(BOTAN_HAS_GOST_34_10_2001)
+ else if(algo == "GOST-34.10")
+ {
+ bench_gost_3410(provider, msec);
+ }
+#endif
+#if defined(BOTAN_HAS_ECGDSA)
+ else if(algo == "ECGDSA")
+ {
+ bench_ecgdsa(ecc_groups, provider, msec);
+ }
+#endif
+#if defined(BOTAN_HAS_ED25519)
+ else if(algo == "Ed25519")
+ {
+ bench_ed25519(provider, msec);
+ }
+#endif
+#if defined(BOTAN_HAS_DIFFIE_HELLMAN)
+ else if(algo == "DH")
+ {
+ bench_dh(provider, msec);
+ }
+#endif
+#if defined(BOTAN_HAS_DSA)
+ else if(algo == "DSA")
+ {
+ bench_dsa(provider, msec);
+ }
+#endif
+#if defined(BOTAN_HAS_ELGAMAL)
+ else if(algo == "ElGamal")
+ {
+ bench_elgamal(provider, msec);
+ }
+#endif
+#if defined(BOTAN_HAS_ECDH)
+ else if(algo == "ECDH")
+ {
+ bench_ecdh(ecc_groups, provider, msec);
+ }
+#endif
+#if defined(BOTAN_HAS_CURVE_25519)
+ else if(algo == "Curve25519")
+ {
+ bench_curve25519(provider, msec);
+ }
+#endif
+#if defined(BOTAN_HAS_MCELIECE)
+ else if(algo == "McEliece")
+ {
+ bench_mceliece(provider, msec);
+ }
+#endif
+#if defined(BOTAN_HAS_XMSS_RFC8391)
+ else if(algo == "XMSS")
+ {
+ bench_xmss(provider, msec);
+ }
+#endif
+#if defined(BOTAN_HAS_NEWHOPE) && defined(BOTAN_HAS_CHACHA_RNG)
+ else if(algo == "NEWHOPE")
+ {
+ bench_newhope(provider, msec);
+ }
+#endif
+#if defined(BOTAN_HAS_SCRYPT)
+ else if(algo == "scrypt")
+ {
+ bench_scrypt(provider, msec);
+ }
+#endif
+#if defined(BOTAN_HAS_ARGON2)
+ else if(algo == "argon2")
+ {
+ bench_argon2(provider, msec);
+ }
+#endif
+#if defined(BOTAN_HAS_BCRYPT)
+ else if(algo == "bcrypt")
+ {
+ bench_bcrypt();
+ }
+#endif
+#if defined(BOTAN_HAS_PASSHASH9)
+ else if(algo == "passhash9")
+ {
+ bench_passhash9();
+ }
+#endif
+#if defined(BOTAN_HAS_POLY_DBL)
+ else if(algo == "poly_dbl")
+ {
+ bench_poly_dbl(msec);
+ }
+#endif
+
+#if defined(BOTAN_HAS_DL_GROUP)
+ else if(algo == "modexp")
+ {
+ bench_modexp(msec);
+ }
+#endif
+
+#if defined(BOTAN_HAS_BIGINT)
+ else if(algo == "mp_mul")
+ {
+ bench_mp_mul(msec);
+ }
+ else if(algo == "mp_div")
+ {
+ bench_mp_div(msec);
+ }
+ else if(algo == "mp_div10")
+ {
+ bench_mp_div10(msec);
+ }
+#endif
+
+#if defined(BOTAN_HAS_NUMBERTHEORY)
+ else if(algo == "primality_test")
+ {
+ bench_primality_tests(msec);
+ }
+ else if(algo == "random_prime")
+ {
+ bench_random_prime(msec);
+ }
+ else if(algo == "inverse_mod")
+ {
+ bench_inverse_mod(msec);
+ }
+ else if(algo == "bn_redc")
+ {
+ bench_bn_redc(msec);
+ }
+ else if(algo == "nistp_redc")
+ {
+ bench_nistp_redc(msec);
+ }
+#endif
+
+#if defined(BOTAN_HAS_FPE_FE1)
+ else if(algo == "fpe_fe1")
+ {
+ bench_fpe_fe1(msec);
+ }
+#endif
+
+#if defined(BOTAN_HAS_RFC3394_KEYWRAP)
+ else if(algo == "rfc3394")
+ {
+ bench_rfc3394(msec);
+ }
+#endif
+
+#if defined(BOTAN_HAS_ECC_GROUP)
+ else if(algo == "ecc_mult")
+ {
+ bench_ecc_mult(ecc_groups, msec);
+ }
+ else if(algo == "ecc_ops")
+ {
+ bench_ecc_ops(ecc_groups, msec);
+ }
+ else if(algo == "ecc_init")
+ {
+ bench_ecc_init(ecc_groups, msec);
+ }
+ else if(algo == "os2ecp")
+ {
+ bench_os2ecp(ecc_groups, msec);
+ }
+#endif
+ else if(algo == "RNG")
+ {
+#if defined(BOTAN_HAS_AUTO_SEEDING_RNG)
+ Botan::AutoSeeded_RNG auto_rng;
+ bench_rng(auto_rng, "AutoSeeded_RNG (with reseed)", msec, buf_sizes);
+#endif
+
+#if defined(BOTAN_HAS_SYSTEM_RNG)
+ bench_rng(Botan::system_rng(), "System_RNG", msec, buf_sizes);
+#endif
+
+#if defined(BOTAN_HAS_PROCESSOR_RNG)
+ if(Botan::Processor_RNG::available())
+ {
+ Botan::Processor_RNG hwrng;
+ bench_rng(hwrng, "Processor_RNG", msec, buf_sizes);
+ }
+#endif
+
+#if defined(BOTAN_HAS_HMAC_DRBG)
+ for(std::string hash : { "SHA-256", "SHA-384", "SHA-512" })
+ {
+ Botan::HMAC_DRBG hmac_drbg(hash);
+ bench_rng(hmac_drbg, hmac_drbg.name(), msec, buf_sizes);
+ }
+#endif
+
+#if defined(BOTAN_HAS_CHACHA_RNG)
+ // Provide a dummy seed
+ Botan::ChaCha_RNG chacha_rng(Botan::secure_vector<uint8_t>(32));
+ bench_rng(chacha_rng, "ChaCha_RNG", msec, buf_sizes);
+#endif
+
+ }
+ else if(algo == "entropy")
+ {
+ bench_entropy_sources(msec);
+ }
+ else
+ {
+ if(verbose() || !using_defaults)
+ {
+ error_output() << "Unknown algorithm '" << algo << "'\n";
+ }
+ }
+ }
+
+ if(m_json)
+ {
+ output() << m_json->print();
+ }
+ if(m_summary)
+ {
+ output() << m_summary->print() << "\n";
+ }
+
+ if(verbose() && m_clock_speed == 0 && m_cycles_consumed > 0 && m_ns_taken > 0)
+ {
+ const double seconds = static_cast<double>(m_ns_taken) / 1000000000;
+ const double Hz = static_cast<double>(m_cycles_consumed) / seconds;
+ const double MHz = Hz / 1000000;
+ output() << "\nEstimated clock speed " << MHz << " MHz\n";
+ }
+ }
+
+ private:
+
+ size_t m_clock_speed = 0;
+ double m_clock_cycle_ratio = 0.0;
+ uint64_t m_cycles_consumed = 0;
+ uint64_t m_ns_taken = 0;
+ std::unique_ptr<Summary> m_summary;
+ std::unique_ptr<JSON_Output> m_json;
+
+ void record_result(const std::unique_ptr<Timer>& t)
+ {
+ m_ns_taken += t->value();
+ m_cycles_consumed += t->cycles_consumed();
+ if(m_json)
+ {
+ m_json->add(*t);
+ }
+ else
+ {
+ output() << t->to_string() << std::flush;
+ if(m_summary)
+ m_summary->add(*t);
+ }
+ }
+
+ template<typename T>
+ using bench_fn = std::function<void (T&,
+ std::string,
+ std::chrono::milliseconds,
+ const std::vector<size_t>&)>;
+
+ template<typename T>
+ void bench_providers_of(const std::string& algo,
+ const std::string& provider, /* user request, if any */
+ const std::chrono::milliseconds runtime,
+ const std::vector<size_t>& buf_sizes,
+ bench_fn<T> bench_one)
+ {
+ for(auto const& prov : T::providers(algo))
+ {
+ if(provider.empty() || provider == prov)
+ {
+ auto p = T::create(algo, prov);
+
+ if(p)
+ {
+ bench_one(*p, prov, runtime, buf_sizes);
+ }
+ }
+ }
+ }
+
+ std::unique_ptr<Timer> make_timer(const std::string& name,
+ uint64_t event_mult = 1,
+ const std::string& what = "",
+ const std::string& provider = "",
+ size_t buf_size = 0)
+ {
+ return std::unique_ptr<Timer>(
+ new Timer(name, provider, what, event_mult, buf_size,
+ m_clock_cycle_ratio, m_clock_speed));
+ }
+
+ std::unique_ptr<Timer> make_timer(const std::string& algo,
+ const std::string& provider,
+ const std::string& what)
+ {
+ return make_timer(algo, 1, what, provider, 0);
+ }
+
+#if defined(BOTAN_HAS_BLOCK_CIPHER)
+ void bench_block_cipher(Botan::BlockCipher& cipher,
+ const std::string& provider,
+ std::chrono::milliseconds runtime,
+ const std::vector<size_t>& buf_sizes)
+ {
+ std::unique_ptr<Timer> ks_timer = make_timer(cipher.name(), provider, "key schedule");
+
+ const Botan::SymmetricKey key(rng(), cipher.maximum_keylength());
+ ks_timer->run([&]() { cipher.set_key(key); });
+
+ const size_t bs = cipher.block_size();
+ std::set<size_t> buf_sizes_in_blocks;
+ for(size_t buf_size : buf_sizes)
+ {
+ if(buf_size % bs == 0)
+ buf_sizes_in_blocks.insert(buf_size);
+ else
+ buf_sizes_in_blocks.insert(buf_size + bs - (buf_size % bs));
+ }
+
+ for(size_t buf_size : buf_sizes_in_blocks)
+ {
+ std::vector<uint8_t> buffer(buf_size);
+ const size_t blocks = buf_size / bs;
+
+ std::unique_ptr<Timer> encrypt_timer = make_timer(cipher.name(), buffer.size(), "encrypt", provider, buf_size);
+ std::unique_ptr<Timer> decrypt_timer = make_timer(cipher.name(), buffer.size(), "decrypt", provider, buf_size);
+
+ encrypt_timer->run_until_elapsed(runtime, [&]() { cipher.encrypt_n(&buffer[0], &buffer[0], blocks); });
+ record_result(encrypt_timer);
+
+ decrypt_timer->run_until_elapsed(runtime, [&]() { cipher.decrypt_n(&buffer[0], &buffer[0], blocks); });
+ record_result(decrypt_timer);
+ }
+ }
+#endif
+
+#if defined(BOTAN_HAS_STREAM_CIPHER)
+ void bench_stream_cipher(
+ Botan::StreamCipher& cipher,
+ const std::string& provider,
+ const std::chrono::milliseconds runtime,
+ const std::vector<size_t>& buf_sizes)
+ {
+ for(auto buf_size : buf_sizes)
+ {
+ Botan::secure_vector<uint8_t> buffer = rng().random_vec(buf_size);
+
+ std::unique_ptr<Timer> encrypt_timer = make_timer(cipher.name(), buffer.size(), "encrypt", provider, buf_size);
+
+ const Botan::SymmetricKey key(rng(), cipher.maximum_keylength());
+ cipher.set_key(key);
+
+ if(cipher.valid_iv_length(12))
+ {
+ const Botan::InitializationVector iv(rng(), 12);
+ cipher.set_iv(iv.begin(), iv.size());
+ }
+
+ while(encrypt_timer->under(runtime))
+ {
+ encrypt_timer->run([&]() { cipher.encipher(buffer); });
+ }
+
+ record_result(encrypt_timer);
+ }
+ }
+#endif
+
+#if defined(BOTAN_HAS_HASH)
+ void bench_hash(
+ Botan::HashFunction& hash,
+ const std::string& provider,
+ const std::chrono::milliseconds runtime,
+ const std::vector<size_t>& buf_sizes)
+ {
+ std::vector<uint8_t> output(hash.output_length());
+
+ for(auto buf_size : buf_sizes)
+ {
+ Botan::secure_vector<uint8_t> buffer = rng().random_vec(buf_size);
+
+ std::unique_ptr<Timer> timer = make_timer(hash.name(), buffer.size(), "hash", provider, buf_size);
+ timer->run_until_elapsed(runtime, [&]() { hash.update(buffer); hash.final(output.data()); });
+ record_result(timer);
+ }
+ }
+#endif
+
+#if defined(BOTAN_HAS_MAC)
+ void bench_mac(
+ Botan::MessageAuthenticationCode& mac,
+ const std::string& provider,
+ const std::chrono::milliseconds runtime,
+ const std::vector<size_t>& buf_sizes)
+ {
+ std::vector<uint8_t> output(mac.output_length());
+
+ for(auto buf_size : buf_sizes)
+ {
+ Botan::secure_vector<uint8_t> buffer = rng().random_vec(buf_size);
+
+ const Botan::SymmetricKey key(rng(), mac.maximum_keylength());
+ mac.set_key(key);
+ mac.start(nullptr, 0);
+
+ std::unique_ptr<Timer> timer = make_timer(mac.name(), buffer.size(), "mac", provider, buf_size);
+ timer->run_until_elapsed(runtime, [&]() { mac.update(buffer); });
+ timer->run([&]() { mac.final(output.data()); });
+ record_result(timer);
+ }
+ }
+#endif
+
+#if defined(BOTAN_HAS_CIPHER_MODES)
+ void bench_cipher_mode(
+ Botan::Cipher_Mode& enc,
+ Botan::Cipher_Mode& dec,
+ const std::chrono::milliseconds runtime,
+ const std::vector<size_t>& buf_sizes)
+ {
+ std::unique_ptr<Timer> ks_timer = make_timer(enc.name(), enc.provider(), "key schedule");
+
+ const Botan::SymmetricKey key(rng(), enc.key_spec().maximum_keylength());
+
+ ks_timer->run([&]() { enc.set_key(key); });
+ ks_timer->run([&]() { dec.set_key(key); });
+
+ record_result(ks_timer);
+
+ for(auto buf_size : buf_sizes)
+ {
+ Botan::secure_vector<uint8_t> buffer = rng().random_vec(buf_size);
+
+ std::unique_ptr<Timer> encrypt_timer = make_timer(enc.name(), buffer.size(), "encrypt", enc.provider(), buf_size);
+ std::unique_ptr<Timer> decrypt_timer = make_timer(dec.name(), buffer.size(), "decrypt", dec.provider(), buf_size);
+
+ Botan::secure_vector<uint8_t> iv = rng().random_vec(enc.default_nonce_length());
+
+ if(buf_size >= enc.minimum_final_size())
+ {
+ while(encrypt_timer->under(runtime) && decrypt_timer->under(runtime))
+ {
+ // Must run in this order, or AEADs will reject the ciphertext
+ encrypt_timer->run([&]() { enc.start(iv); enc.finish(buffer); });
+
+ decrypt_timer->run([&]() { dec.start(iv); dec.finish(buffer); });
+
+ if(iv.size() > 0)
+ {
+ iv[iv.size()-1] += 1;
+ }
+ }
+ }
+
+ record_result(encrypt_timer);
+ record_result(decrypt_timer);
+ }
+ }
+#endif
+
+ void bench_rng(
+ Botan::RandomNumberGenerator& rng,
+ const std::string& rng_name,
+ const std::chrono::milliseconds runtime,
+ const std::vector<size_t>& buf_sizes)
+ {
+ for(auto buf_size : buf_sizes)
+ {
+ Botan::secure_vector<uint8_t> buffer(buf_size);
+
+#if defined(BOTAN_HAS_SYSTEM_RNG)
+ rng.reseed_from_rng(Botan::system_rng(), 256);
+#endif
+
+ std::unique_ptr<Timer> timer = make_timer(rng_name, buffer.size(), "generate", "", buf_size);
+ timer->run_until_elapsed(runtime, [&]() { rng.randomize(buffer.data(), buffer.size()); });
+ record_result(timer);
+ }
+ }
+
+ void bench_entropy_sources(const std::chrono::milliseconds)
+ {
+ Botan::Entropy_Sources& srcs = Botan::Entropy_Sources::global_sources();
+
+ for(auto src : srcs.enabled_sources())
+ {
+ size_t entropy_bits = 0;
+ Botan_Tests::SeedCapturing_RNG rng;
+
+ std::unique_ptr<Timer> timer = make_timer(src, "", "bytes");
+ timer->run([&]() { entropy_bits = srcs.poll_just(rng, src); });
+
+ size_t compressed_size = 0;
+
+#if defined(BOTAN_HAS_ZLIB)
+ std::unique_ptr<Botan::Compression_Algorithm> comp(Botan::make_compressor("zlib"));
+
+ if(comp)
+ {
+ Botan::secure_vector<uint8_t> compressed;
+ compressed.assign(rng.seed_material().begin(), rng.seed_material().end());
+ comp->start(9);
+ comp->finish(compressed);
+
+ compressed_size = compressed.size();
+ }
+#endif
+
+ std::ostringstream msg;
+
+ msg << "Entropy source " << src << " output " << rng.seed_material().size() << " bytes"
+ << " estimated entropy " << entropy_bits << " in " << timer->milliseconds() << " ms";
+
+ if(compressed_size > 0)
+ {
+ msg << " output compressed to " << compressed_size << " bytes";
+ }
+
+ msg << " total samples " << rng.samples() << "\n";
+
+ timer->set_custom_msg(msg.str());
+
+ record_result(timer);
+ }
+ }
+
+#if defined(BOTAN_HAS_ECC_GROUP)
+ void bench_ecc_ops(const std::vector<std::string>& groups, const std::chrono::milliseconds runtime)
+ {
+ for(std::string group_name : groups)
+ {
+ const Botan::EC_Group ec_group(group_name);
+
+ std::unique_ptr<Timer> add_timer = make_timer(group_name + " add");
+ std::unique_ptr<Timer> addf_timer = make_timer(group_name + " addf");
+ std::unique_ptr<Timer> dbl_timer = make_timer(group_name + " dbl");
+
+ const Botan::PointGFp& base_point = ec_group.get_base_point();
+ Botan::PointGFp non_affine_pt = ec_group.get_base_point() * 1776; // create a non-affine point
+ Botan::PointGFp pt = ec_group.get_base_point();
+
+ std::vector<Botan::BigInt> ws(Botan::PointGFp::WORKSPACE_SIZE);
+
+ while(add_timer->under(runtime) && addf_timer->under(runtime) && dbl_timer->under(runtime))
+ {
+ dbl_timer->run([&]() { pt.mult2(ws); });
+ add_timer->run([&]() { pt.add(non_affine_pt, ws); });
+ addf_timer->run([&]() { pt.add_affine(base_point, ws); });
+ }
+
+ record_result(dbl_timer);
+ record_result(add_timer);
+ record_result(addf_timer);
+ }
+ }
+
+ void bench_ecc_init(const std::vector<std::string>& groups, const std::chrono::milliseconds runtime)
+ {
+ for(std::string group_name : groups)
+ {
+ std::unique_ptr<Timer> timer = make_timer(group_name + " initialization");
+
+ while(timer->under(runtime))
+ {
+ Botan::EC_Group::clear_registered_curve_data();
+ timer->run([&]() { Botan::EC_Group group(group_name); });
+ }
+
+ record_result(timer);
+ }
+ }
+
+ void bench_ecc_mult(const std::vector<std::string>& groups, const std::chrono::milliseconds runtime)
+ {
+ for(std::string group_name : groups)
+ {
+ const Botan::EC_Group ec_group(group_name);
+
+ std::unique_ptr<Timer> mult_timer = make_timer(group_name + " Montgomery ladder");
+ std::unique_ptr<Timer> blinded_mult_timer = make_timer(group_name + " blinded comb");
+ std::unique_ptr<Timer> blinded_var_mult_timer = make_timer(group_name + " blinded window");
+
+ const Botan::PointGFp& base_point = ec_group.get_base_point();
+
+ std::vector<Botan::BigInt> ws;
+
+ while(mult_timer->under(runtime) &&
+ blinded_mult_timer->under(runtime) &&
+ blinded_var_mult_timer->under(runtime))
+ {
+ const Botan::BigInt scalar(rng(), ec_group.get_p_bits());
+
+ const Botan::PointGFp r1 = mult_timer->run([&]() { return base_point * scalar; });
+
+ const Botan::PointGFp r2 = blinded_mult_timer->run(
+ [&]() { return ec_group.blinded_base_point_multiply(scalar, rng(), ws); });
+
+ const Botan::PointGFp r3 = blinded_var_mult_timer->run(
+ [&]() { return ec_group.blinded_var_point_multiply(base_point, scalar, rng(), ws); });
+
+ BOTAN_ASSERT_EQUAL(r1, r2, "Same point computed by Montgomery and comb");
+ BOTAN_ASSERT_EQUAL(r1, r3, "Same point computed by Montgomery and window");
+ }
+
+ record_result(mult_timer);
+ record_result(blinded_mult_timer);
+ record_result(blinded_var_mult_timer);
+ }
+ }
+
+ void bench_os2ecp(const std::vector<std::string>& groups, const std::chrono::milliseconds runtime)
+ {
+ std::unique_ptr<Timer> uncmp_timer = make_timer("OS2ECP uncompressed");
+ std::unique_ptr<Timer> cmp_timer = make_timer("OS2ECP compressed");
+
+ for(std::string group_name : groups)
+ {
+ const Botan::EC_Group ec_group(group_name);
+
+ while(uncmp_timer->under(runtime) && cmp_timer->under(runtime))
+ {
+ const Botan::BigInt k(rng(), 256);
+ const Botan::PointGFp p = ec_group.get_base_point() * k;
+ const std::vector<uint8_t> os_cmp = p.encode(Botan::PointGFp::COMPRESSED);
+ const std::vector<uint8_t> os_uncmp = p.encode(Botan::PointGFp::UNCOMPRESSED);
+
+ uncmp_timer->run([&]() { ec_group.OS2ECP(os_uncmp); });
+ cmp_timer->run([&]() { ec_group.OS2ECP(os_cmp); });
+ }
+
+ record_result(uncmp_timer);
+ record_result(cmp_timer);
+ }
+ }
+
+#endif
+
+#if defined(BOTAN_HAS_FPE_FE1)
+
+ void bench_fpe_fe1(const std::chrono::milliseconds runtime)
+ {
+ const Botan::BigInt n = 1000000000000000;
+
+ std::unique_ptr<Timer> enc_timer = make_timer("FPE_FE1 encrypt");
+ std::unique_ptr<Timer> dec_timer = make_timer("FPE_FE1 decrypt");
+
+ const Botan::SymmetricKey key(rng(), 32);
+ const std::vector<uint8_t> tweak(8); // 8 zeros
+
+ Botan::BigInt x = 1;
+
+ Botan::FPE_FE1 fpe_fe1(n);
+ fpe_fe1.set_key(key);
+
+ while(enc_timer->under(runtime))
+ {
+ enc_timer->start();
+ x = fpe_fe1.encrypt(x, tweak.data(), tweak.size());
+ enc_timer->stop();
+ }
+
+ for(size_t i = 0; i != enc_timer->events(); ++i)
+ {
+ dec_timer->start();
+ x = fpe_fe1.decrypt(x, tweak.data(), tweak.size());
+ dec_timer->stop();
+ }
+
+ BOTAN_ASSERT(x == 1, "FPE works");
+
+ record_result(enc_timer);
+ record_result(dec_timer);
+ }
+#endif
+
+#if defined(BOTAN_HAS_RFC3394_KEYWRAP)
+
+ void bench_rfc3394(const std::chrono::milliseconds runtime)
+ {
+ std::unique_ptr<Timer> wrap_timer = make_timer("RFC3394 AES-256 key wrap");
+ std::unique_ptr<Timer> unwrap_timer = make_timer("RFC3394 AES-256 key unwrap");
+
+ const Botan::SymmetricKey kek(rng(), 32);
+ Botan::secure_vector<uint8_t> key(64, 0);
+
+ while(wrap_timer->under(runtime))
+ {
+ wrap_timer->start();
+ key = Botan::rfc3394_keywrap(key, kek);
+ wrap_timer->stop();
+
+ unwrap_timer->start();
+ key = Botan::rfc3394_keyunwrap(key, kek);
+ unwrap_timer->stop();
+
+ key[0] += 1;
+ }
+
+ record_result(wrap_timer);
+ record_result(unwrap_timer);
+ }
+#endif
+
+#if defined(BOTAN_HAS_BIGINT)
+
+ void bench_mp_mul(const std::chrono::milliseconds runtime)
+ {
+ std::chrono::milliseconds runtime_per_size = runtime;
+ for(size_t bits : { 256, 384, 512, 768, 1024, 1536, 2048, 3072, 4096 })
+ {
+ std::unique_ptr<Timer> mul_timer = make_timer("BigInt mul " + std::to_string(bits));
+ std::unique_ptr<Timer> sqr_timer = make_timer("BigInt sqr " + std::to_string(bits));
+
+ const Botan::BigInt y(rng(), bits);
+ Botan::secure_vector<Botan::word> ws;
+
+ while(mul_timer->under(runtime_per_size))
+ {
+ Botan::BigInt x(rng(), bits);
+
+ sqr_timer->start();
+ x.square(ws);
+ sqr_timer->stop();
+
+ x.mask_bits(bits);
+
+ mul_timer->start();
+ x.mul(y, ws);
+ mul_timer->stop();
+ }
+
+ record_result(mul_timer);
+ record_result(sqr_timer);
+ }
+
+ }
+
+ void bench_mp_div(const std::chrono::milliseconds runtime)
+ {
+ std::chrono::milliseconds runtime_per_size = runtime;
+
+ for(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);
+
+ std::unique_ptr<Timer> div_timer = make_timer("BigInt div " + bit_descr);
+ std::unique_ptr<Timer> ct_div_timer = make_timer("BigInt ct_div " + bit_descr);
+
+ Botan::BigInt y;
+ Botan::BigInt x;
+ Botan::secure_vector<Botan::word> ws;
+
+ Botan::BigInt q1, r1, q2, r2;
+
+ while(ct_div_timer->under(runtime_per_size))
+ {
+ x.randomize(rng(), n_bits);
+ y.randomize(rng(), q_bits);
+
+ div_timer->start();
+ Botan::vartime_divide(x, y, q1, r1);
+ div_timer->stop();
+
+ ct_div_timer->start();
+ Botan::ct_divide(x, y, q2, r2);
+ ct_div_timer->stop();
+
+ BOTAN_ASSERT_EQUAL(q1, q2, "Quotient ok");
+ BOTAN_ASSERT_EQUAL(r1, r2, "Remainder ok");
+ }
+
+ record_result(div_timer);
+ record_result(ct_div_timer);
+ }
+ }
+
+ void bench_mp_div10(const std::chrono::milliseconds runtime)
+ {
+ std::chrono::milliseconds runtime_per_size = runtime;
+
+ for(size_t n_bits : { 256, 384, 512, 768, 1024, 1536, 2048, 3072, 4096 })
+ {
+ const std::string bit_descr = std::to_string(n_bits) + "/10";
+
+ std::unique_ptr<Timer> div_timer = make_timer("BigInt div " + bit_descr);
+ std::unique_ptr<Timer> ct_div_timer = make_timer("BigInt ct_div " + bit_descr);
+
+ Botan::BigInt x;
+ Botan::secure_vector<Botan::word> ws;
+
+ const Botan::BigInt ten(10);
+ Botan::BigInt q1, r1, q2;
+ uint8_t r2;
+
+ while(ct_div_timer->under(runtime_per_size))
+ {
+ x.randomize(rng(), n_bits);
+
+ div_timer->start();
+ Botan::vartime_divide(x, ten, q1, r1);
+ div_timer->stop();
+
+ ct_div_timer->start();
+ Botan::ct_divide_u8(x, 10, q2, r2);
+ ct_div_timer->stop();
+
+ BOTAN_ASSERT_EQUAL(q1, q2, "Quotient ok");
+ BOTAN_ASSERT_EQUAL(r1, r2, "Remainder ok");
+ }
+
+ record_result(div_timer);
+ record_result(ct_div_timer);
+ }
+ }
+
+#endif
+
+#if defined(BOTAN_HAS_DL_GROUP)
+
+ void bench_modexp(const std::chrono::milliseconds runtime)
+ {
+ for(size_t group_bits : { 1024, 1536, 2048, 3072, 4096 })
+ {
+ const std::string group_bits_str = std::to_string(group_bits);
+ const Botan::DL_Group group("modp/srp/" + group_bits_str);
+
+ const size_t e_bits = Botan::dl_exponent_size(group_bits);
+ const size_t f_bits = group_bits - 1;
+
+ const Botan::BigInt random_e(rng(), e_bits);
+ const Botan::BigInt random_f(rng(), f_bits);
+
+ std::unique_ptr<Timer> e_timer = make_timer(group_bits_str + " short exponent");
+ std::unique_ptr<Timer> f_timer = make_timer(group_bits_str + " full exponent");
+
+ while(f_timer->under(runtime))
+ {
+ e_timer->run([&]() { group.power_g_p(random_e); });
+ f_timer->run([&]() { group.power_g_p(random_f); });
+ }
+
+ record_result(e_timer);
+ record_result(f_timer);
+ }
+ }
+#endif
+
+#if defined(BOTAN_HAS_NUMBERTHEORY)
+ void bench_nistp_redc(const std::chrono::milliseconds runtime)
+ {
+ Botan::secure_vector<Botan::word> ws;
+
+ std::unique_ptr<Timer> p192_timer = make_timer("P-192 redc");
+ Botan::BigInt r192(rng(), 192*2 - 1);
+ while(p192_timer->under(runtime))
+ {
+ Botan::BigInt r = r192;
+ p192_timer->run([&]() { Botan::redc_p192(r, ws); });
+ r192 += 1;
+ }
+ record_result(p192_timer);
+
+ std::unique_ptr<Timer> p224_timer = make_timer("P-224 redc");
+ Botan::BigInt r224(rng(), 224*2 - 1);
+ while(p224_timer->under(runtime))
+ {
+ Botan::BigInt r = r224;
+ p224_timer->run([&]() { Botan::redc_p224(r, ws); });
+ r224 += 1;
+ }
+ record_result(p224_timer);
+
+ std::unique_ptr<Timer> p256_timer = make_timer("P-256 redc");
+ Botan::BigInt r256(rng(), 256*2 - 1);
+ while(p256_timer->under(runtime))
+ {
+ Botan::BigInt r = r256;
+ p256_timer->run([&]() { Botan::redc_p256(r, ws); });
+ r256 += 1;
+ }
+ record_result(p256_timer);
+
+ std::unique_ptr<Timer> p384_timer = make_timer("P-384 redc");
+ Botan::BigInt r384(rng(), 384*2 - 1);
+ while(p384_timer->under(runtime))
+ {
+ Botan::BigInt r = r384;
+ p384_timer->run([&]() { Botan::redc_p384(r384, ws); });
+ r384 += 1;
+ }
+ record_result(p384_timer);
+
+ std::unique_ptr<Timer> p521_timer = make_timer("P-521 redc");
+ Botan::BigInt r521(rng(), 521*2 - 1);
+ while(p521_timer->under(runtime))
+ {
+ Botan::BigInt r = r521;
+ p521_timer->run([&]() { Botan::redc_p521(r521, ws); });
+ r521 += 1;
+ }
+ record_result(p521_timer);
+ }
+
+ void bench_bn_redc(const std::chrono::milliseconds runtime)
+ {
+ for(size_t bitsize : { 512, 1024, 2048, 4096 })
+ {
+ Botan::BigInt p(rng(), bitsize);
+
+ std::string bit_str = std::to_string(bitsize);
+ std::unique_ptr<Timer> barrett_timer = make_timer("Barrett-" + bit_str);
+ std::unique_ptr<Timer> schoolbook_timer = make_timer("Schoolbook-" + bit_str);
+
+ Botan::Modular_Reducer mod_p(p);
+
+ while(schoolbook_timer->under(runtime))
+ {
+ const Botan::BigInt x(rng(), p.bits() * 2 - 2);
+
+ const Botan::BigInt r1 = barrett_timer->run(
+ [&] { return mod_p.reduce(x); });
+ const Botan::BigInt r2 = schoolbook_timer->run(
+ [&] { return x % p; });
+
+ BOTAN_ASSERT(r1 == r2, "Computed different results");
+ }
+
+ record_result(barrett_timer);
+ record_result(schoolbook_timer);
+ }
+ }
+
+ void bench_inverse_mod(const std::chrono::milliseconds runtime)
+ {
+ for(size_t bits : { 256, 384, 512, 1024, 2048 })
+ {
+ const std::string bit_str = std::to_string(bits);
+
+ std::unique_ptr<Timer> timer = make_timer("inverse_mod-" + bit_str);
+
+ while(timer->under(runtime))
+ {
+ const Botan::BigInt x(rng(), bits - 1);
+ Botan::BigInt mod(rng(), bits);
+
+ const Botan::BigInt x_inv = timer->run(
+ [&] { return Botan::inverse_mod(x, mod); });
+
+ if(x_inv == 0)
+ {
+ const Botan::BigInt g = gcd(x, mod);
+ BOTAN_ASSERT(g != 1, "Inversion only fails if gcd(x, mod) > 1");
+ }
+ else
+ {
+ const Botan::BigInt check = (x_inv*x) % mod;
+ BOTAN_ASSERT_EQUAL(check, 1, "Const time inversion correct");
+ }
+ }
+
+ record_result(timer);
+ }
+ }
+
+ void bench_primality_tests(const std::chrono::milliseconds runtime)
+ {
+ for(size_t bits : { 256, 512, 1024 })
+ {
+ std::unique_ptr<Timer> mr_timer = make_timer("Miller-Rabin-" + std::to_string(bits));
+ std::unique_ptr<Timer> bpsw_timer = make_timer("Bailie-PSW-" + std::to_string(bits));
+ std::unique_ptr<Timer> lucas_timer = make_timer("Lucas-" + std::to_string(bits));
+
+ Botan::BigInt n = Botan::random_prime(rng(), bits);
+
+ while(lucas_timer->under(runtime))
+ {
+ Botan::Modular_Reducer mod_n(n);
+
+ mr_timer->run([&]() {
+ return Botan::is_miller_rabin_probable_prime(n, mod_n, rng(), 2); });
+
+ bpsw_timer->run([&]() {
+ return Botan::is_bailie_psw_probable_prime(n, mod_n); });
+
+ lucas_timer->run([&]() {
+ return Botan::is_lucas_probable_prime(n, mod_n); });
+
+ n += 2;
+ }
+
+ record_result(mr_timer);
+ record_result(bpsw_timer);
+ record_result(lucas_timer);
+ }
+ }
+
+ void bench_random_prime(const std::chrono::milliseconds runtime)
+ {
+ const size_t coprime = 65537; // simulates RSA key gen
+
+ for(size_t bits : { 256, 384, 512, 768, 1024, 1536 })
+ {
+ std::unique_ptr<Timer> genprime_timer = make_timer("random_prime " + std::to_string(bits));
+ std::unique_ptr<Timer> gensafe_timer = make_timer("random_safe_prime " + std::to_string(bits));
+ std::unique_ptr<Timer> is_prime_timer = make_timer("is_prime " + std::to_string(bits));
+
+ while(gensafe_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); }))
+ {
+ 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); }))
+ {
+ 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); }))
+ {
+ 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); });
+ }
+ }
+
+ record_result(genprime_timer);
+ record_result(gensafe_timer);
+ record_result(is_prime_timer);
+ }
+ }
+#endif
+
+#if defined(BOTAN_HAS_PUBLIC_KEY_CRYPTO)
+ void bench_pk_enc(
+ const Botan::Private_Key& key,
+ const std::string& nm,
+ const std::string& provider,
+ const std::string& padding,
+ std::chrono::milliseconds msec)
+ {
+ std::vector<uint8_t> plaintext, ciphertext;
+
+ Botan::PK_Encryptor_EME enc(key, rng(), padding, provider);
+ Botan::PK_Decryptor_EME dec(key, rng(), padding, provider);
+
+ std::unique_ptr<Timer> enc_timer = make_timer(nm + " " + padding, provider, "encrypt");
+ std::unique_ptr<Timer> dec_timer = make_timer(nm + " " + padding, provider, "decrypt");
+
+ while(enc_timer->under(msec) || dec_timer->under(msec))
+ {
+ // Generate a new random ciphertext to decrypt
+ if(ciphertext.empty() || enc_timer->under(msec))
+ {
+ rng().random_vec(plaintext, enc.maximum_input_size());
+ ciphertext = enc_timer->run([&]() { return enc.encrypt(plaintext, rng()); });
+ }
+
+ if(dec_timer->under(msec))
+ {
+ const auto dec_pt = dec_timer->run([&]() { return dec.decrypt(ciphertext); });
+
+ if(!(dec_pt == plaintext)) // sanity check
+ {
+ error_output() << "Bad roundtrip in PK encrypt/decrypt bench\n";
+ }
+ }
+ }
+
+ record_result(enc_timer);
+ record_result(dec_timer);
+ }
+
+ void bench_pk_ka(const std::string& algo,
+ const std::string& nm,
+ const std::string& params,
+ const std::string& provider,
+ std::chrono::milliseconds msec)
+ {
+ const std::string kdf = "KDF2(SHA-256)"; // arbitrary choice
+
+ std::unique_ptr<Timer> keygen_timer = make_timer(nm, provider, "keygen");
+
+ std::unique_ptr<Botan::Private_Key> key1(keygen_timer->run([&]
+ {
+ return Botan::create_private_key(algo, rng(), params);
+ }));
+ std::unique_ptr<Botan::Private_Key> key2(keygen_timer->run([&]
+ {
+ return Botan::create_private_key(algo, rng(), params);
+ }));
+
+ record_result(keygen_timer);
+
+ const Botan::PK_Key_Agreement_Key& ka_key1 = dynamic_cast<const Botan::PK_Key_Agreement_Key&>(*key1);
+ const Botan::PK_Key_Agreement_Key& ka_key2 = dynamic_cast<const Botan::PK_Key_Agreement_Key&>(*key2);
+
+ Botan::PK_Key_Agreement ka1(ka_key1, rng(), kdf, provider);
+ Botan::PK_Key_Agreement ka2(ka_key2, rng(), kdf, provider);
+
+ const std::vector<uint8_t> ka1_pub = ka_key1.public_value();
+ const std::vector<uint8_t> ka2_pub = ka_key2.public_value();
+
+ std::unique_ptr<Timer> ka_timer = make_timer(nm, provider, "key agreements");
+
+ while(ka_timer->under(msec))
+ {
+ Botan::SymmetricKey symkey1 = ka_timer->run([&]() { return ka1.derive_key(32, ka2_pub); });
+ Botan::SymmetricKey symkey2 = ka_timer->run([&]() { return ka2.derive_key(32, ka1_pub); });
+
+ if(symkey1 != symkey2)
+ {
+ error_output() << "Key agreement mismatch in PK bench\n";
+ }
+ }
+
+ record_result(ka_timer);
+ }
+
+ void bench_pk_kem(const Botan::Private_Key& key,
+ const std::string& nm,
+ const std::string& provider,
+ const std::string& kdf,
+ std::chrono::milliseconds msec)
+ {
+ Botan::PK_KEM_Decryptor dec(key, rng(), kdf, provider);
+ Botan::PK_KEM_Encryptor enc(key, rng(), kdf, provider);
+
+ std::unique_ptr<Timer> kem_enc_timer = make_timer(nm, provider, "KEM encrypt");
+ std::unique_ptr<Timer> kem_dec_timer = make_timer(nm, provider, "KEM decrypt");
+
+ while(kem_enc_timer->under(msec) && kem_dec_timer->under(msec))
+ {
+ Botan::secure_vector<uint8_t> encap_key, enc_shared_key;
+ Botan::secure_vector<uint8_t> salt = rng().random_vec(16);
+
+ kem_enc_timer->start();
+ enc.encrypt(encap_key, enc_shared_key, 64, rng(), salt);
+ kem_enc_timer->stop();
+
+ kem_dec_timer->start();
+ Botan::secure_vector<uint8_t> dec_shared_key = dec.decrypt(encap_key, 64, salt);
+ kem_dec_timer->stop();
+
+ if(enc_shared_key != dec_shared_key)
+ {
+ error_output() << "KEM mismatch in PK bench\n";
+ }
+ }
+
+ record_result(kem_enc_timer);
+ record_result(kem_dec_timer);
+ }
+
+ void bench_pk_sig_ecc(const std::string& algo,
+ const std::string& emsa,
+ const std::string& provider,
+ const std::vector<std::string>& params,
+ std::chrono::milliseconds msec)
+ {
+ for(std::string grp : params)
+ {
+ const std::string nm = grp.empty() ? algo : (algo + "-" + grp);
+
+ std::unique_ptr<Timer> keygen_timer = make_timer(nm, provider, "keygen");
+
+ std::unique_ptr<Botan::Private_Key> key(keygen_timer->run([&]
+ {
+ return Botan::create_private_key(algo, rng(), grp);
+ }));
+
+ record_result(keygen_timer);
+ bench_pk_sig(*key, nm, provider, emsa, msec);
+ }
+ }
+
+ size_t bench_pk_sig(const Botan::Private_Key& key,
+ const std::string& nm,
+ const std::string& provider,
+ const std::string& padding,
+ std::chrono::milliseconds msec)
+ {
+ std::vector<uint8_t> message, signature, bad_signature;
+
+ Botan::PK_Signer sig(key, rng(), padding, Botan::IEEE_1363, provider);
+ Botan::PK_Verifier ver(key, padding, Botan::IEEE_1363, provider);
+
+ std::unique_ptr<Timer> sig_timer = make_timer(nm + " " + padding, provider, "sign");
+ std::unique_ptr<Timer> ver_timer = make_timer(nm + " " + padding, provider, "verify");
+
+ size_t invalid_sigs = 0;
+
+ while(ver_timer->under(msec) || sig_timer->under(msec))
+ {
+ if(signature.empty() || sig_timer->under(msec))
+ {
+ /*
+ Length here is kind of arbitrary, but 48 bytes fits into a single
+ hash block so minimizes hashing overhead versus the PK op itself.
+ */
+ rng().random_vec(message, 48);
+
+ signature = sig_timer->run([&]() { return sig.sign_message(message, rng()); });
+
+ bad_signature = signature;
+ bad_signature[rng().next_byte() % bad_signature.size()] ^= rng().next_nonzero_byte();
+ }
+
+ if(ver_timer->under(msec))
+ {
+ const bool verified = ver_timer->run([&]
+ {
+ return ver.verify_message(message, signature);
+ });
+
+ if(!verified)
+ {
+ invalid_sigs += 1;
+ }
+
+ const bool verified_bad = ver_timer->run([&]
+ {
+ return ver.verify_message(message, bad_signature);
+ });
+
+ if(verified_bad)
+ {
+ error_output() << "Bad signature accepted in PK signature bench\n";
+ }
+ }
+ }
+
+ if(invalid_sigs > 0)
+ error_output() << invalid_sigs << " generated signatures rejected in PK signature bench\n";
+
+ const size_t events = static_cast<size_t>(std::min(sig_timer->events(), ver_timer->events()));
+
+ record_result(sig_timer);
+ record_result(ver_timer);
+
+ return events;
+ }
+#endif
+
+#if defined(BOTAN_HAS_RSA)
+ void bench_rsa_keygen(const std::string& provider,
+ std::chrono::milliseconds msec)
+ {
+ for(size_t keylen : { 1024, 2048, 3072, 4096 })
+ {
+ const std::string nm = "RSA-" + std::to_string(keylen);
+ std::unique_ptr<Timer> keygen_timer = make_timer(nm, provider, "keygen");
+
+ while(keygen_timer->under(msec))
+ {
+ std::unique_ptr<Botan::Private_Key> key(keygen_timer->run([&] {
+ return Botan::create_private_key("RSA", rng(), std::to_string(keylen));
+ }));
+
+ BOTAN_ASSERT(key->check_key(rng(), true), "Key is ok");
+ }
+
+ record_result(keygen_timer);
+ }
+ }
+
+ void bench_rsa(const std::string& provider,
+ std::chrono::milliseconds msec)
+ {
+ for(size_t keylen : { 1024, 2048, 3072, 4096 })
+ {
+ const std::string nm = "RSA-" + std::to_string(keylen);
+
+ std::unique_ptr<Timer> keygen_timer = make_timer(nm, provider, "keygen");
+
+ std::unique_ptr<Botan::Private_Key> key(keygen_timer->run([&]
+ {
+ return Botan::create_private_key("RSA", rng(), std::to_string(keylen));
+ }));
+
+ record_result(keygen_timer);
+
+ // Using PKCS #1 padding so OpenSSL provider can play along
+ bench_pk_sig(*key, nm, provider, "EMSA-PKCS1-v1_5(SHA-256)", msec);
+
+ //bench_pk_sig(*key, nm, provider, "PSSR(SHA-256)", msec);
+ //bench_pk_enc(*key, nm, provider, "EME-PKCS1-v1_5", msec);
+ //bench_pk_enc(*key, nm, provider, "OAEP(SHA-1)", msec);
+ }
+ }
+#endif
+
+#if defined(BOTAN_HAS_ECDSA)
+ void bench_ecdsa(const std::vector<std::string>& groups,
+ const std::string& provider,
+ std::chrono::milliseconds msec)
+ {
+ return bench_pk_sig_ecc("ECDSA", "EMSA1(SHA-256)", provider, groups, msec);
+ }
+
+ void bench_ecdsa_recovery(const std::vector<std::string>& groups,
+ const std::string&,
+ std::chrono::milliseconds msec)
+ {
+ for(std::string group_name : groups)
+ {
+ Botan::EC_Group group(group_name);
+ std::unique_ptr<Timer> recovery_timer = make_timer("ECDSA recovery " + group_name);
+
+ while(recovery_timer->under(msec))
+ {
+ Botan::ECDSA_PrivateKey key(rng(), group);
+
+ std::vector<uint8_t> message(group.get_order_bits() / 8);
+ rng().randomize(message.data(), message.size());
+
+ Botan::PK_Signer signer(key, rng(), "Raw");
+ signer.update(message);
+ std::vector<uint8_t> signature = signer.signature(rng());
+
+ Botan::PK_Verifier verifier(key, "Raw", Botan::IEEE_1363, "base");
+ verifier.update(message);
+ BOTAN_ASSERT(verifier.check_signature(signature), "Valid signature");
+
+ Botan::BigInt r(signature.data(), signature.size()/2);
+ Botan::BigInt s(signature.data() + signature.size()/2, signature.size()/2);
+
+ const uint8_t v = key.recovery_param(message, r, s);
+
+ recovery_timer->run([&]() {
+ Botan::ECDSA_PublicKey pubkey(group, message, r, s, v);
+ BOTAN_ASSERT(pubkey.public_point() == key.public_point(), "Recovered public key");
+ });
+ }
+
+ record_result(recovery_timer);
+ }
+
+ }
+
+#endif
+
+#if defined(BOTAN_HAS_ECKCDSA)
+ void bench_eckcdsa(const std::vector<std::string>& groups,
+ const std::string& provider,
+ std::chrono::milliseconds msec)
+ {
+ return bench_pk_sig_ecc("ECKCDSA", "EMSA1(SHA-256)", provider, groups, msec);
+ }
+#endif
+
+#if defined(BOTAN_HAS_GOST_34_10_2001)
+ void bench_gost_3410(const std::string& provider,
+ std::chrono::milliseconds msec)
+ {
+ return bench_pk_sig_ecc("GOST-34.10", "EMSA1(GOST-34.11)", provider, {"gost_256A"}, msec);
+ }
+#endif
+
+#if defined(BOTAN_HAS_SM2)
+ void bench_sm2(const std::vector<std::string>& groups,
+ const std::string& provider,
+ std::chrono::milliseconds msec)
+ {
+ return bench_pk_sig_ecc("SM2_Sig", "SM3", provider, groups, msec);
+ }
+#endif
+
+#if defined(BOTAN_HAS_ECGDSA)
+ void bench_ecgdsa(const std::vector<std::string>& groups,
+ const std::string& provider,
+ std::chrono::milliseconds msec)
+ {
+ return bench_pk_sig_ecc("ECGDSA", "EMSA1(SHA-256)", provider, groups, msec);
+ }
+#endif
+
+#if defined(BOTAN_HAS_ED25519)
+ void bench_ed25519(const std::string& provider,
+ std::chrono::milliseconds msec)
+ {
+ return bench_pk_sig_ecc("Ed25519", "Pure", provider, std::vector<std::string>{""}, msec);
+ }
+#endif
+
+#if defined(BOTAN_HAS_DIFFIE_HELLMAN)
+ void bench_dh(const std::string& provider,
+ std::chrono::milliseconds msec)
+ {
+ for(size_t bits : { 1024, 1536, 2048, 3072, 4096, 6144, 8192 })
+ {
+ bench_pk_ka("DH",
+ "DH-" + std::to_string(bits),
+ "modp/ietf/" + std::to_string(bits),
+ provider, msec);
+ }
+ }
+#endif
+
+#if defined(BOTAN_HAS_DSA)
+ void bench_dsa(const std::string& provider, std::chrono::milliseconds msec)
+ {
+ for(size_t bits : { 1024, 2048, 3072 })
+ {
+ const std::string nm = "DSA-" + std::to_string(bits);
+
+ const std::string params =
+ (bits == 1024) ? "dsa/jce/1024" : ("dsa/botan/" + std::to_string(bits));
+
+ std::unique_ptr<Timer> keygen_timer = make_timer(nm, provider, "keygen");
+
+ std::unique_ptr<Botan::Private_Key> key(keygen_timer->run([&]
+ {
+ return Botan::create_private_key("DSA", rng(), params);
+ }));
+
+ record_result(keygen_timer);
+
+ bench_pk_sig(*key, nm, provider, "EMSA1(SHA-256)", msec);
+ }
+ }
+#endif
+
+#if defined(BOTAN_HAS_ELGAMAL)
+ void bench_elgamal(const std::string& provider, std::chrono::milliseconds msec)
+ {
+ for(size_t keylen : { 1024, 2048, 3072, 4096 })
+ {
+ const std::string nm = "ElGamal-" + std::to_string(keylen);
+
+ const std::string params = "modp/ietf/" + std::to_string(keylen);
+
+ std::unique_ptr<Timer> keygen_timer = make_timer(nm, provider, "keygen");
+
+ std::unique_ptr<Botan::Private_Key> key(keygen_timer->run([&]
+ {
+ return Botan::create_private_key("ElGamal", rng(), params);
+ }));
+
+ record_result(keygen_timer);
+
+ bench_pk_enc(*key, nm, provider, "EME-PKCS1-v1_5", msec);
+ }
+ }
+#endif
+
+#if defined(BOTAN_HAS_ECDH)
+ void bench_ecdh(const std::vector<std::string>& groups,
+ const std::string& provider,
+ std::chrono::milliseconds msec)
+ {
+ for(std::string grp : groups)
+ {
+ bench_pk_ka("ECDH", "ECDH-" + grp, grp, provider, msec);
+ }
+ }
+#endif
+
+#if defined(BOTAN_HAS_CURVE_25519)
+ void bench_curve25519(const std::string& provider,
+ std::chrono::milliseconds msec)
+ {
+ bench_pk_ka("Curve25519", "Curve25519", "", provider, msec);
+ }
+#endif
+
+#if defined(BOTAN_HAS_MCELIECE)
+ void bench_mceliece(const std::string& provider,
+ std::chrono::milliseconds msec)
+ {
+ /*
+ SL=80 n=1632 t=33 - 59 KB pubkey 140 KB privkey
+ SL=107 n=2480 t=45 - 128 KB pubkey 300 KB privkey
+ SL=128 n=2960 t=57 - 195 KB pubkey 459 KB privkey
+ SL=147 n=3408 t=67 - 265 KB pubkey 622 KB privkey
+ SL=191 n=4624 t=95 - 516 KB pubkey 1234 KB privkey
+ SL=256 n=6624 t=115 - 942 KB pubkey 2184 KB privkey
+ */
+
+ const std::vector<std::pair<size_t, size_t>> mce_params =
+ {
+ { 2480, 45 },
+ { 2960, 57 },
+ { 3408, 67 },
+ { 4624, 95 },
+ { 6624, 115 }
+ };
+
+ for(auto params : mce_params)
+ {
+ size_t n = params.first;
+ size_t t = params.second;
+
+ const std::string nm = "McEliece-" + std::to_string(n) + "," + std::to_string(t) +
+ " (WF=" + std::to_string(Botan::mceliece_work_factor(n, t)) + ")";
+
+ std::unique_ptr<Timer> keygen_timer = make_timer(nm, provider, "keygen");
+
+ std::unique_ptr<Botan::Private_Key> key(keygen_timer->run([&]
+ {
+ return new Botan::McEliece_PrivateKey(rng(), n, t);
+ }));
+
+ record_result(keygen_timer);
+ bench_pk_kem(*key, nm, provider, "KDF2(SHA-256)", msec);
+ }
+ }
+#endif
+
+#if defined(BOTAN_HAS_XMSS_RFC8391)
+ void bench_xmss(const std::string& provider,
+ std::chrono::milliseconds msec)
+ {
+ /*
+ 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
+ minutes to generate and H20 signatures take 5-10 minutes to generate
+ */
+ std::vector<std::string> xmss_params
+ {
+ "XMSS-SHA2_10_256",
+ "XMSS-SHAKE_10_256",
+ "XMSS-SHA2_10_512",
+ "XMSS-SHAKE_10_512",
+ };
+
+ for(std::string params : xmss_params)
+ {
+ std::unique_ptr<Timer> keygen_timer = make_timer(params, provider, "keygen");
+
+ std::unique_ptr<Botan::Private_Key> key(keygen_timer->run([&]
+ {
+ return Botan::create_private_key("XMSS", rng(), params);
+ }));
+
+ record_result(keygen_timer);
+ if(bench_pk_sig(*key, params, provider, "", msec) == 1)
+ break;
+ }
+ }
+#endif
+
+#if defined(BOTAN_HAS_POLY_DBL)
+ void bench_poly_dbl(std::chrono::milliseconds msec)
+ {
+ for(size_t sz : { 8, 16, 24, 32, 64, 128 })
+ {
+ std::unique_ptr<Timer> be_timer = make_timer("poly_dbl_be_" + std::to_string(sz));
+ std::unique_ptr<Timer> le_timer = make_timer("poly_dbl_le_" + std::to_string(sz));
+
+ std::vector<uint8_t> buf(sz);
+ rng().randomize(buf.data(), sz);
+
+ be_timer->run_until_elapsed(msec, [&]() { Botan::poly_double_n(buf.data(), buf.data(), sz); });
+ le_timer->run_until_elapsed(msec, [&]() { Botan::poly_double_n_le(buf.data(), buf.data(), sz); });
+
+ record_result(be_timer);
+ record_result(le_timer);
+ }
+ }
+#endif
+
+#if defined(BOTAN_HAS_BCRYPT)
+
+ void bench_bcrypt()
+ {
+ const std::string password = "not a very good password";
+
+ for(uint8_t work_factor = 4; work_factor <= 14; ++work_factor)
+ {
+ std::unique_ptr<Timer> timer = make_timer("bcrypt wf=" + std::to_string(work_factor));
+
+ timer->run([&] {
+ Botan::generate_bcrypt(password, rng(), work_factor);
+ });
+
+ record_result(timer);
+ }
+ }
+#endif
+
+#if defined(BOTAN_HAS_PASSHASH9)
+
+ void bench_passhash9()
+ {
+ 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)
+ continue;
+
+ for(auto work_factor : { 10, 15 })
+ {
+ std::unique_ptr<Timer> timer = make_timer("passhash9 alg=" + std::to_string(alg) +
+ " wf=" + std::to_string(work_factor));
+
+ timer->run([&] {
+ Botan::generate_passhash9(password, rng(), static_cast<uint8_t>(work_factor), alg);
+ });
+
+ record_result(timer);
+ }
+ }
+ }
+#endif
+
+#if defined(BOTAN_HAS_SCRYPT)
+
+ void bench_scrypt(const std::string& /*provider*/,
+ std::chrono::milliseconds msec)
+ {
+
+ for(size_t N : { 8192, 16384, 32768, 65536 })
+ {
+ for(size_t r : { 1, 8, 16 })
+ {
+ for(size_t p : { 1, 4 })
+ {
+ std::unique_ptr<Timer> scrypt_timer = make_timer(
+ "scrypt-" + std::to_string(N) + "-" +
+ std::to_string(r) + "-" + std::to_string(p) +
+ " (" + std::to_string(Botan::scrypt_memory_usage(N, r, p) / (1024*1024)) + " MiB)");
+
+ uint8_t out[64];
+ uint8_t salt[8];
+ rng().randomize(salt, sizeof(salt));
+
+ while(scrypt_timer->under(msec))
+ {
+ scrypt_timer->run([&] {
+ Botan::scrypt(out, sizeof(out), "password",
+ salt, sizeof(salt), N, r, p);
+ });
+ }
+
+ record_result(scrypt_timer);
+
+ if(scrypt_timer->events() == 1)
+ break;
+ }
+ }
+ }
+
+ }
+
+#endif
+
+#if defined(BOTAN_HAS_ARGON2)
+
+ void bench_argon2(const std::string& /*provider*/,
+ std::chrono::milliseconds msec)
+ {
+ const uint8_t mode = 2; // Argon2id
+
+ for(size_t M : { 8*1024, 64*1024, 256*1024 })
+ {
+ for(size_t t : { 1, 2, 4 })
+ {
+ for(size_t p : { 1 })
+ {
+ std::unique_ptr<Timer> timer = make_timer(
+ "Argon2id M=" + std::to_string(M) + " t=" + std::to_string(t) + " p=" + std::to_string(p));
+
+ uint8_t out[64];
+ uint8_t salt[16];
+ rng().randomize(salt, sizeof(salt));
+
+ while(timer->under(msec))
+ {
+ timer->run([&] {
+ Botan::argon2(out, sizeof(out), "password", 8,
+ salt, sizeof(salt), nullptr, 0, nullptr, 0,
+ mode, p, M, t);
+ });
+ }
+
+ record_result(timer);
+ }
+ }
+ }
+ }
+
+#endif
+
+#if defined(BOTAN_HAS_NEWHOPE) && defined(BOTAN_HAS_CHACHA_RNG)
+ void bench_newhope(const std::string& /*provider*/,
+ std::chrono::milliseconds msec)
+ {
+ const std::string nm = "NEWHOPE";
+
+ std::unique_ptr<Timer> keygen_timer = make_timer(nm, "", "keygen");
+ std::unique_ptr<Timer> shareda_timer = make_timer(nm, "", "shareda");
+ std::unique_ptr<Timer> sharedb_timer = make_timer(nm, "", "sharedb");
+
+ Botan::ChaCha_RNG nh_rng(Botan::secure_vector<uint8_t>(32));
+
+ while(sharedb_timer->under(msec))
+ {
+ std::vector<uint8_t> send_a(Botan::NEWHOPE_SENDABYTES), send_b(Botan::NEWHOPE_SENDBBYTES);
+ std::vector<uint8_t> shared_a(32), shared_b(32);
+
+ Botan::newhope_poly sk_a;
+
+ keygen_timer->start();
+ Botan::newhope_keygen(send_a.data(), &sk_a, nh_rng);
+ keygen_timer->stop();
+
+ sharedb_timer->start();
+ Botan::newhope_sharedb(shared_b.data(), send_b.data(), send_a.data(), nh_rng);
+ sharedb_timer->stop();
+
+ shareda_timer->start();
+ Botan::newhope_shareda(shared_a.data(), &sk_a, send_b.data());
+ shareda_timer->stop();
+
+ BOTAN_ASSERT(shared_a == shared_b, "Same derived key");
+ }
+
+ record_result(keygen_timer);
+ record_result(shareda_timer);
+ record_result(sharedb_timer);
+ }
+#endif
+
+ };
+
+BOTAN_REGISTER_COMMAND("speed", Speed);
+
+}
diff --git a/comm/third_party/botan/src/cli/timing_tests.cpp b/comm/third_party/botan/src/cli/timing_tests.cpp
new file mode 100644
index 0000000000..a9904ae2e8
--- /dev/null
+++ b/comm/third_party/botan/src/cli/timing_tests.cpp
@@ -0,0 +1,617 @@
+/*
+* Timing Analysis Tests
+*
+* These tests are not for performance, but verifying that two inputs are not handled
+* in a way that is vulnerable to simple timing attacks.
+*
+* Produces output which can be analyzed with the Mona reporting library
+*
+* $ git clone https://github.com/seecurity/mona-timing-report.git
+* $ cd mona-timing-report && ant
+* $ java -jar ReportingTool.jar --lowerBound=0.4 --upperBound=0.5 --inputFile=$file --name=$file
+*
+* (C) 2016 Juraj Somorovsky - juraj.somorovsky@hackmanit.de
+* (C) 2017 Neverhub
+* (C) 2017,2018,2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "cli.h"
+#include <botan/hex.h>
+#include <sstream>
+#include <fstream>
+
+#include <botan/rng.h>
+#include <botan/internal/os_utils.h>
+
+#if defined(BOTAN_HAS_BIGINT)
+ #include <botan/bigint.h>
+#endif
+
+#if defined(BOTAN_HAS_NUMBERTHEORY)
+ #include <botan/numthry.h>
+#endif
+
+#if defined(BOTAN_HAS_ECC_GROUP)
+ #include <botan/ec_group.h>
+#endif
+
+#if defined(BOTAN_HAS_DL_GROUP)
+ #include <botan/dl_group.h>
+#endif
+
+#if defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_EME_RAW)
+ #include <botan/pubkey.h>
+ #include <botan/rsa.h>
+#endif
+
+#if defined(BOTAN_HAS_TLS_CBC)
+ #include <botan/internal/tls_cbc.h>
+ #include <botan/tls_exceptn.h>
+#endif
+
+#if defined(BOTAN_HAS_ECDSA)
+ #include <botan/pubkey.h>
+ #include <botan/ecdsa.h>
+#endif
+
+namespace Botan_CLI {
+
+typedef uint64_t ticks;
+
+class Timing_Test
+ {
+ public:
+ Timing_Test()
+ {
+ /*
+ 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
+ }
+
+ virtual ~Timing_Test() = default;
+
+ std::vector<std::vector<ticks>> execute_evaluation(
+ const std::vector<std::string>& inputs,
+ size_t warmup_runs,
+ size_t measurement_runs);
+
+ virtual std::vector<uint8_t> prepare_input(const std::string& input)
+ {
+ return Botan::hex_decode(input);
+ }
+
+ virtual ticks measure_critical_function(const std::vector<uint8_t>& input) = 0;
+
+ protected:
+ static ticks get_ticks()
+ {
+ // Returns CPU counter or best approximation (monotonic clock of some kind)
+ //return Botan::OS::get_high_resolution_clock();
+ return Botan::OS::get_system_timestamp_ns();
+ }
+
+ Botan::RandomNumberGenerator& timing_test_rng()
+ {
+ return (*m_rng);
+ }
+
+ private:
+ std::unique_ptr<Botan::RandomNumberGenerator> m_rng;
+ };
+
+#if defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_EME_PKCS1) && defined(BOTAN_HAS_EME_RAW)
+
+class Bleichenbacker_Timing_Test final : public Timing_Test
+ {
+ public:
+ 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_dec(m_privkey, timing_test_rng(), "PKCS1v15") {}
+
+ std::vector<uint8_t> prepare_input(const std::string& input) override
+ {
+ const std::vector<uint8_t> input_vector = Botan::hex_decode(input);
+ const std::vector<uint8_t> encrypted = m_enc.encrypt(input_vector, timing_test_rng());
+ return encrypted;
+ }
+
+ ticks measure_critical_function(const std::vector<uint8_t>& input) override
+ {
+ const ticks start = get_ticks();
+ m_dec.decrypt_or_random(input.data(), m_ctext_length, m_expected_content_size, timing_test_rng());
+ const ticks end = get_ticks();
+ return (end - start);
+ }
+
+ private:
+ 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;
+ Botan::PK_Encryptor_EME m_enc;
+ Botan::PK_Decryptor_EME m_dec;
+ };
+
+#endif
+
+#if defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_EME_OAEP) && defined(BOTAN_HAS_EME_RAW)
+
+/*
+* Test Manger OAEP side channel
+*
+* "A Chosen Ciphertext Attack on RSA Optimal Asymmetric Encryption
+* Padding (OAEP) as Standardized in PKCS #1 v2.0" James Manger
+* http://archiv.infsec.ethz.ch/education/fs08/secsem/Manger01.pdf
+*/
+class Manger_Timing_Test final : public Timing_Test
+ {
+ public:
+ 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_dec(m_privkey, timing_test_rng(), m_decrypt_padding) {}
+
+ std::vector<uint8_t> prepare_input(const std::string& input) override
+ {
+ const std::vector<uint8_t> input_vector = Botan::hex_decode(input);
+ const std::vector<uint8_t> encrypted = m_enc.encrypt(input_vector, timing_test_rng());
+ return encrypted;
+ }
+
+ ticks measure_critical_function(const std::vector<uint8_t>& input) override
+ {
+ ticks start = get_ticks();
+ try
+ {
+ m_dec.decrypt(input.data(), m_ctext_length);
+ }
+ catch(Botan::Decoding_Error&)
+ {
+ }
+ ticks end = get_ticks();
+
+ return (end - start);
+ }
+
+ private:
+ const std::string m_encrypt_padding = "Raw";
+ 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;
+ Botan::PK_Encryptor_EME m_enc;
+ Botan::PK_Decryptor_EME m_dec;
+ };
+
+#endif
+
+#if defined(BOTAN_HAS_TLS_CBC)
+
+/*
+* Test handling of countermeasure to the Lucky13 attack
+*/
+class Lucky13_Timing_Test final : public Timing_Test
+ {
+ public:
+ Lucky13_Timing_Test(const std::string& mac_name, size_t mac_keylen)
+ : m_mac_algo(mac_name)
+ , m_mac_keylen(mac_keylen)
+ , m_dec(Botan::BlockCipher::create_or_throw("AES-128"),
+ Botan::MessageAuthenticationCode::create_or_throw("HMAC(" + m_mac_algo + ")"),
+ 16, m_mac_keylen, Botan::TLS::Protocol_Version::TLS_V11, false) {}
+
+ std::vector<uint8_t> prepare_input(const std::string& input) override;
+ ticks measure_critical_function(const std::vector<uint8_t>& input) override;
+
+ private:
+ const std::string m_mac_algo;
+ const size_t m_mac_keylen;
+ Botan::TLS::TLS_CBC_HMAC_AEAD_Decryption m_dec;
+ };
+
+std::vector<uint8_t> Lucky13_Timing_Test::prepare_input(const std::string& input)
+ {
+ const std::vector<uint8_t> input_vector = Botan::hex_decode(input);
+ const std::vector<uint8_t> key(16);
+ const std::vector<uint8_t> iv(16);
+
+ std::unique_ptr<Botan::Cipher_Mode> enc(Botan::Cipher_Mode::create("AES-128/CBC/NoPadding", Botan::ENCRYPTION));
+ enc->set_key(key);
+ enc->start(iv);
+ Botan::secure_vector<uint8_t> buf(input_vector.begin(), input_vector.end());
+ enc->finish(buf);
+
+ return unlock(buf);
+ }
+
+ticks Lucky13_Timing_Test::measure_critical_function(const std::vector<uint8_t>& input)
+ {
+ Botan::secure_vector<uint8_t> data(input.begin(), input.end());
+ Botan::secure_vector<uint8_t> aad(13);
+ const Botan::secure_vector<uint8_t> iv(16);
+ Botan::secure_vector<uint8_t> key(16 + m_mac_keylen);
+
+ m_dec.set_key(unlock(key));
+ m_dec.set_ad(unlock(aad));
+ m_dec.start(unlock(iv));
+
+ ticks start = get_ticks();
+ try
+ {
+ m_dec.finish(data);
+ }
+ catch(Botan::TLS::TLS_Exception&)
+ {
+ }
+ ticks end = get_ticks();
+ return (end - start);
+ }
+
+#endif
+
+#if defined(BOTAN_HAS_ECDSA)
+
+class ECDSA_Timing_Test final : public Timing_Test
+ {
+ public:
+ ECDSA_Timing_Test(std::string ecgroup);
+
+ ticks measure_critical_function(const std::vector<uint8_t>& input) override;
+
+ private:
+ const Botan::EC_Group m_group;
+ const Botan::ECDSA_PrivateKey m_privkey;
+ const Botan::BigInt& m_x;
+ std::vector<Botan::BigInt> m_ws;
+ Botan::BigInt m_b, m_b_inv;
+ };
+
+ECDSA_Timing_Test::ECDSA_Timing_Test(std::string ecgroup)
+ : m_group(ecgroup)
+ , m_privkey(timing_test_rng(), m_group)
+ , m_x(m_privkey.private_value())
+ {
+ m_b = m_group.random_scalar(timing_test_rng());
+ m_b_inv = m_group.inverse_mod_order(m_b);
+ }
+
+ticks ECDSA_Timing_Test::measure_critical_function(const std::vector<uint8_t>& input)
+ {
+ const Botan::BigInt k(input.data(), input.size());
+ Botan::BigInt m(5); // fixed message to minimize noise
+
+ ticks start = get_ticks();
+
+ // the following ECDSA operations involve and should not leak any information about k
+ const Botan::BigInt r = m_group.mod_order(
+ m_group.blinded_base_point_multiply_x(k, timing_test_rng(), m_ws));
+ const Botan::BigInt k_inv = m_group.inverse_mod_order(k);
+
+ m_b = m_group.square_mod_order(m_b);
+ m_b_inv = m_group.square_mod_order(m_b_inv);
+
+ m = m_group.multiply_mod_order(m_b, m_group.mod_order(m));
+ const Botan::BigInt xr_m = m_group.mod_order(m_group.multiply_mod_order(m_x, m_b, r) + m);
+
+ const Botan::BigInt s = m_group.multiply_mod_order(k_inv, xr_m, m_b_inv);
+
+ BOTAN_UNUSED(r, s);
+
+ ticks end = get_ticks();
+
+ return (end - start);
+ }
+
+#endif
+
+#if defined(BOTAN_HAS_ECC_GROUP)
+
+class ECC_Mul_Timing_Test final : public Timing_Test
+ {
+ public:
+ ECC_Mul_Timing_Test(std::string ecgroup) :
+ m_group(ecgroup)
+ {}
+
+ ticks measure_critical_function(const std::vector<uint8_t>& input) override;
+
+ private:
+ const Botan::EC_Group m_group;
+ std::vector<Botan::BigInt> m_ws;
+ };
+
+ticks ECC_Mul_Timing_Test::measure_critical_function(const std::vector<uint8_t>& input)
+ {
+ const Botan::BigInt k(input.data(), input.size());
+
+ ticks start = get_ticks();
+
+ const Botan::PointGFp k_times_P = m_group.blinded_base_point_multiply(k, timing_test_rng(), m_ws);
+
+ ticks end = get_ticks();
+
+ return (end - start);
+ }
+
+#endif
+
+#if defined(BOTAN_HAS_DL_GROUP)
+
+class Powmod_Timing_Test final : public Timing_Test
+ {
+ public:
+ Powmod_Timing_Test(const std::string& dl_group) : m_group(dl_group)
+ {
+ }
+
+ ticks measure_critical_function(const std::vector<uint8_t>& input) override;
+ private:
+ Botan::DL_Group m_group;
+ };
+
+ticks Powmod_Timing_Test::measure_critical_function(const std::vector<uint8_t>& input)
+ {
+ const Botan::BigInt x(input.data(), input.size());
+ const size_t max_x_bits = m_group.p_bits();
+
+ ticks start = get_ticks();
+
+ const Botan::BigInt g_x_p = m_group.power_g_p(x, max_x_bits);
+
+ ticks end = get_ticks();
+
+ return (end - start);
+ }
+
+#endif
+
+#if defined(BOTAN_HAS_NUMBERTHEORY)
+
+class Invmod_Timing_Test final : public Timing_Test
+ {
+ public:
+ Invmod_Timing_Test(size_t p_bits)
+ {
+ m_p = Botan::random_prime(timing_test_rng(), p_bits);
+ }
+
+ ticks measure_critical_function(const std::vector<uint8_t>& input) override;
+
+ private:
+ Botan::BigInt m_p;
+ };
+
+ticks Invmod_Timing_Test::measure_critical_function(const std::vector<uint8_t>& input)
+ {
+ const Botan::BigInt k(input.data(), input.size());
+
+ ticks start = get_ticks();
+
+ const Botan::BigInt inv = inverse_mod(k, m_p);
+
+ ticks end = get_ticks();
+
+ return (end - start);
+ }
+
+#endif
+
+std::vector<std::vector<ticks>> Timing_Test::execute_evaluation(
+ const std::vector<std::string>& raw_inputs,
+ size_t warmup_runs, size_t measurement_runs)
+ {
+ std::vector<std::vector<ticks>> all_results(raw_inputs.size());
+ std::vector<std::vector<uint8_t>> inputs(raw_inputs.size());
+
+ for(auto& result : all_results)
+ {
+ result.reserve(measurement_runs);
+ }
+
+ for(size_t i = 0; i != inputs.size(); ++i)
+ {
+ inputs[i] = prepare_input(raw_inputs[i]);
+ }
+
+ // arbitrary upper bounds of 1 and 10 million resp
+ if(warmup_runs > 1000000 || measurement_runs > 100000000)
+ {
+ throw CLI_Error("Requested execution counts too large, rejecting");
+ }
+
+ size_t total_runs = 0;
+ std::vector<ticks> results(inputs.size());
+
+ while(total_runs < (warmup_runs + measurement_runs))
+ {
+ for(size_t i = 0; i != inputs.size(); ++i)
+ {
+ results[i] = measure_critical_function(inputs[i]);
+ }
+
+ total_runs++;
+
+ if(total_runs > warmup_runs)
+ {
+ for(size_t i = 0; i != results.size(); ++i)
+ {
+ all_results[i].push_back(results[i]);
+ }
+ }
+ }
+
+ return all_results;
+ }
+
+class Timing_Test_Command final : public Command
+ {
+ public:
+ Timing_Test_Command()
+ : Command("timing_test test_type --test-data-file= --test-data-dir=src/tests/data/timing "
+ "--warmup-runs=5000 --measurement-runs=50000") {}
+
+ std::string group() const override
+ {
+ return "misc";
+ }
+
+ std::string description() const override
+ {
+ return "Run various timing side channel tests";
+ }
+
+ void go() override
+ {
+ const std::string test_type = get_arg("test_type");
+ const size_t warmup_runs = get_arg_sz("warmup-runs");
+ const size_t measurement_runs = get_arg_sz("measurement-runs");
+
+ std::unique_ptr<Timing_Test> test = lookup_timing_test(test_type);
+
+ if(!test)
+ {
+ throw CLI_Error("Unknown or unavailable test type '" + test_type + "'");
+ }
+
+ std::string filename = get_arg_or("test-data-file", "");
+
+ if(filename.empty())
+ {
+ const std::string test_data_dir = get_arg("test-data-dir");
+ filename = test_data_dir + "/" + test_type + ".vec";
+ }
+
+ std::vector<std::string> lines = read_testdata(filename);
+
+ std::vector<std::vector<ticks>> results = test->execute_evaluation(lines, warmup_runs, measurement_runs);
+
+ size_t unique_id = 0;
+ std::ostringstream oss;
+ for(size_t secret_id = 0; secret_id != results.size(); ++secret_id)
+ {
+ for(size_t i = 0; i != results[secret_id].size(); ++i)
+ {
+ oss << unique_id++ << ";" << secret_id << ";" << results[secret_id][i] << "\n";
+ }
+ }
+
+ output() << oss.str();
+ }
+ private:
+
+ std::vector<std::string> read_testdata(const std::string& filename)
+ {
+ std::vector<std::string> lines;
+ std::ifstream infile(filename);
+ if(infile.good() == false)
+ {
+ throw CLI_Error("Error reading test data from '" + filename + "'");
+ }
+ std::string line;
+ while(std::getline(infile, line))
+ {
+ if(line.size() > 0 && line.at(0) != '#')
+ {
+ lines.push_back(line);
+ }
+ }
+ return lines;
+ }
+
+ std::unique_ptr<Timing_Test> lookup_timing_test(const std::string& test_type);
+
+ std::string help_text() const override
+ {
+ // TODO check feature macros
+ return (Command::help_text() +
+ "\ntest_type can take on values "
+ "bleichenbacher "
+ "manger "
+ "ecdsa "
+ "ecc_mul "
+ "inverse_mod "
+ "pow_mod "
+ "lucky13sec3 "
+ "lucky13sec4sha1 "
+ "lucky13sec4sha256 "
+ "lucky13sec4sha384 "
+ );
+ }
+ };
+
+BOTAN_REGISTER_COMMAND("timing_test", Timing_Test_Command);
+
+std::unique_ptr<Timing_Test> Timing_Test_Command::lookup_timing_test(const std::string& test_type)
+ {
+#if defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_EME_PKCS1) && defined(BOTAN_HAS_EME_RAW)
+ if(test_type == "bleichenbacher")
+ {
+ return std::unique_ptr<Timing_Test>(new Bleichenbacker_Timing_Test(2048));
+ }
+#endif
+
+#if defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_EME_OAEP) && defined(BOTAN_HAS_EME_RAW)
+ if(test_type == "manger")
+ {
+ return std::unique_ptr<Timing_Test>(new Manger_Timing_Test(2048));
+ }
+#endif
+
+#if defined(BOTAN_HAS_ECDSA)
+ if(test_type == "ecdsa")
+ {
+ return std::unique_ptr<Timing_Test>(new ECDSA_Timing_Test("secp384r1"));
+ }
+#endif
+
+#if defined(BOTAN_HAS_ECC_GROUP)
+ if(test_type == "ecc_mul")
+ {
+ return std::unique_ptr<Timing_Test>(new ECC_Mul_Timing_Test("brainpool512r1"));
+ }
+#endif
+
+#if defined(BOTAN_HAS_NUMBERTHEORY)
+ if(test_type == "inverse_mod")
+ {
+ return std::unique_ptr<Timing_Test>(new Invmod_Timing_Test(512));
+ }
+#endif
+
+#if defined(BOTAN_HAS_DL_GROUP)
+ if(test_type == "pow_mod")
+ {
+ return std::unique_ptr<Timing_Test>(new Powmod_Timing_Test("modp/ietf/1024"));
+ }
+#endif
+
+#if defined(BOTAN_HAS_TLS_CBC)
+ if(test_type == "lucky13sec3" || test_type == "lucky13sec4sha1")
+ {
+ return std::unique_ptr<Timing_Test>(new Lucky13_Timing_Test("SHA-1", 20));
+ }
+ if(test_type == "lucky13sec4sha256")
+ {
+ return std::unique_ptr<Timing_Test>(new Lucky13_Timing_Test("SHA-256", 32));
+ }
+ if(test_type == "lucky13sec4sha384")
+ {
+ return std::unique_ptr<Timing_Test>(new Lucky13_Timing_Test("SHA-384", 48));
+ }
+#endif
+
+ BOTAN_UNUSED(test_type);
+
+ return nullptr;
+ }
+
+
+}
diff --git a/comm/third_party/botan/src/cli/tls_client.cpp b/comm/third_party/botan/src/cli/tls_client.cpp
new file mode 100644
index 0000000000..9541f8fbc4
--- /dev/null
+++ b/comm/third_party/botan/src/cli/tls_client.cpp
@@ -0,0 +1,436 @@
+/*
+* (C) 2014,2015 Jack Lloyd
+* 2016 Matthias Gierlings
+* 2017 René Korthaus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "cli.h"
+
+#if defined(BOTAN_HAS_TLS) && defined(BOTAN_TARGET_OS_HAS_FILESYSTEM) && defined(BOTAN_TARGET_OS_HAS_SOCKETS)
+
+#include <botan/tls_client.h>
+#include <botan/tls_policy.h>
+#include <botan/x509path.h>
+#include <botan/ocsp.h>
+#include <botan/hex.h>
+#include <botan/parsing.h>
+#include <fstream>
+
+#if defined(BOTAN_HAS_TLS_SQLITE3_SESSION_MANAGER)
+ #include <botan/tls_session_manager_sqlite.h>
+#endif
+
+#include <string>
+#include <memory>
+
+#include "socket_utils.h"
+#include "tls_helpers.h"
+
+namespace Botan_CLI {
+
+class CLI_Policy final : public Botan::TLS::Policy
+ {
+ public:
+
+ CLI_Policy(Botan::TLS::Protocol_Version req_version) : m_version(req_version) {}
+
+ std::vector<std::string> allowed_ciphers() const override
+ {
+ // Allow CBC mode only in versions which don't support AEADs
+ if(m_version.supports_aead_modes() == false)
+ {
+ return { "AES-256", "AES-128" };
+ }
+
+ return Botan::TLS::Policy::allowed_ciphers();
+ }
+
+ bool allow_tls10() const override { return m_version == Botan::TLS::Protocol_Version::TLS_V10; }
+ bool allow_tls11() const override { return m_version == Botan::TLS::Protocol_Version::TLS_V11; }
+ bool allow_tls12() const override { return m_version == Botan::TLS::Protocol_Version::TLS_V12; }
+
+ private:
+ Botan::TLS::Protocol_Version m_version;
+ };
+
+class TLS_Client final : public Command, public Botan::TLS::Callbacks
+ {
+ public:
+ TLS_Client()
+ : Command("tls_client host --port=443 --print-certs --policy=default "
+ "--tls1.0 --tls1.1 --tls1.2 "
+ "--skip-system-cert-store --trusted-cas= "
+ "--session-db= --session-db-pass= --next-protocols= --type=tcp")
+ {
+ init_sockets();
+ }
+
+ ~TLS_Client()
+ {
+ stop_sockets();
+ }
+
+ std::string group() const override
+ {
+ return "tls";
+ }
+
+ std::string description() const override
+ {
+ return "Connect to a host using TLS/DTLS";
+ }
+
+ void go() override
+ {
+ // TODO client cert auth
+
+ std::unique_ptr<Botan::TLS::Session_Manager> session_mgr;
+
+ const std::string sessions_db = get_arg("session-db");
+ const std::string host = get_arg("host");
+ 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 std::string trusted_CAs = get_arg("trusted-cas");
+
+ if(!sessions_db.empty())
+ {
+#if defined(BOTAN_HAS_TLS_SQLITE3_SESSION_MANAGER)
+ const std::string sessions_passphrase = get_passphrase_arg("Session DB passphrase", "session-db-pass");
+ session_mgr.reset(new Botan::TLS::Session_Manager_SQLite(sessions_passphrase, rng(), sessions_db));
+#else
+ error_output() << "Ignoring session DB file, sqlite not enabled\n";
+#endif
+ }
+
+ if(!session_mgr)
+ {
+ session_mgr.reset(new Botan::TLS::Session_Manager_In_Memory(rng()));
+ }
+
+ auto policy = load_tls_policy(get_arg("policy"));
+
+ if(transport != "tcp" && transport != "udp")
+ {
+ throw CLI_Usage_Error("Invalid transport type '" + transport + "' for TLS");
+ }
+
+ const bool use_tcp = (transport == "tcp");
+
+ const std::vector<std::string> protocols_to_offer = Botan::split_on(next_protos, ',');
+
+ Botan::TLS::Protocol_Version version =
+ use_tcp ? Botan::TLS::Protocol_Version::TLS_V12 : Botan::TLS::Protocol_Version::DTLS_V12;
+
+ if(flag_set("tls1.0"))
+ {
+ version = Botan::TLS::Protocol_Version::TLS_V10;
+ if(!policy)
+ policy.reset(new CLI_Policy(version));
+ }
+ else if(flag_set("tls1.1"))
+ {
+ version = Botan::TLS::Protocol_Version::TLS_V11;
+ if(!policy)
+ policy.reset(new CLI_Policy(version));
+ }
+ else if(flag_set("tls1.2"))
+ {
+ version = Botan::TLS::Protocol_Version::TLS_V12;
+ if(!policy)
+ policy.reset(new CLI_Policy(version));
+ }
+ else if(!policy)
+ {
+ policy.reset(new Botan::TLS::Policy);
+ }
+
+ if(policy->acceptable_protocol_version(version) == false)
+ {
+ throw CLI_Usage_Error("The policy specified does not allow the requested TLS version");
+ }
+
+ struct sockaddr_storage addrbuf;
+ std::string hostname;
+ if(!host.empty() &&
+ inet_pton(AF_INET, host.c_str(), &addrbuf) != 1 &&
+ inet_pton(AF_INET6, host.c_str(), &addrbuf) != 1)
+ {
+ hostname = host;
+ }
+
+ m_sockfd = connect_to_host(host, port, use_tcp);
+
+ Basic_Credentials_Manager creds(use_system_cert_store, trusted_CAs);
+
+ Botan::TLS::Client client(*this, *session_mgr, creds, *policy, rng(),
+ Botan::TLS::Server_Information(hostname, port),
+ version, protocols_to_offer);
+
+ bool first_active = true;
+
+ while(!client.is_closed())
+ {
+ fd_set readfds;
+ FD_ZERO(&readfds);
+ FD_SET(m_sockfd, &readfds);
+
+ if(client.is_active())
+ {
+ FD_SET(STDIN_FILENO, &readfds);
+ if(first_active && !protocols_to_offer.empty())
+ {
+ std::string app = client.application_protocol();
+ if(app != "")
+ {
+ output() << "Server choose protocol: " << client.application_protocol() << "\n";
+ }
+ first_active = false;
+ }
+ }
+
+ struct timeval timeout = { 1, 0 };
+
+ ::select(static_cast<int>(m_sockfd + 1), &readfds, nullptr, nullptr, &timeout);
+
+ if(FD_ISSET(m_sockfd, &readfds))
+ {
+ uint8_t buf[4 * 1024] = { 0 };
+
+ ssize_t got = ::read(m_sockfd, buf, sizeof(buf));
+
+ if(got == 0)
+ {
+ output() << "EOF on socket\n";
+ break;
+ }
+ else if(got == -1)
+ {
+ output() << "Socket error: " << errno << " " << err_to_string(errno) << "\n";
+ continue;
+ }
+
+ client.received_data(buf, got);
+ }
+
+ if(FD_ISSET(STDIN_FILENO, &readfds))
+ {
+ uint8_t buf[1024] = { 0 };
+ ssize_t got = read(STDIN_FILENO, buf, sizeof(buf));
+
+ if(got == 0)
+ {
+ output() << "EOF on stdin\n";
+ client.close();
+ break;
+ }
+ else if(got == -1)
+ {
+ output() << "Stdin error: " << errno << " " << err_to_string(errno) << "\n";
+ continue;
+ }
+
+ if(got == 2 && buf[1] == '\n')
+ {
+ char cmd = buf[0];
+
+ if(cmd == 'R' || cmd == 'r')
+ {
+ output() << "Client initiated renegotiation\n";
+ client.renegotiate(cmd == 'R');
+ }
+ else if(cmd == 'Q')
+ {
+ output() << "Client initiated close\n";
+ client.close();
+ }
+ }
+ else
+ {
+ client.send(buf, got);
+ }
+ }
+
+ if(client.timeout_check())
+ {
+ output() << "Timeout detected\n";
+ }
+ }
+
+ ::close(m_sockfd);
+ }
+
+ private:
+ socket_type connect_to_host(const std::string& host, uint16_t port, bool tcp)
+ {
+ addrinfo hints;
+ Botan::clear_mem(&hints, 1);
+ 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)
+ {
+ throw CLI_Error("getaddrinfo failed for " + host);
+ }
+
+ socket_type fd = 0;
+
+ for(rp = res; rp != nullptr; rp = rp->ai_next)
+ {
+ fd = ::socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
+
+ if(fd == invalid_socket())
+ {
+ continue;
+ }
+
+ if(::connect(fd, rp->ai_addr, static_cast<socklen_t>(rp->ai_addrlen)) != 0)
+ {
+ ::close(fd);
+ continue;
+ }
+
+ break;
+ }
+
+ ::freeaddrinfo(res);
+
+ if(rp == nullptr) // no address succeeded
+ {
+ throw CLI_Error("connect failed");
+ }
+
+ return fd;
+ }
+
+ void tls_verify_cert_chain(
+ const std::vector<Botan::X509_Certificate>& cert_chain,
+ const std::vector<std::shared_ptr<const Botan::OCSP::Response>>& ocsp,
+ const std::vector<Botan::Certificate_Store*>& trusted_roots,
+ Botan::Usage_Type usage,
+ const std::string& hostname,
+ const Botan::TLS::Policy& policy) override
+ {
+ if(cert_chain.empty())
+ {
+ throw Botan::Invalid_Argument("Certificate chain was empty");
+ }
+
+ Botan::Path_Validation_Restrictions restrictions(
+ policy.require_cert_revocation_info(),
+ policy.minimum_signature_strength());
+
+ auto ocsp_timeout = std::chrono::milliseconds(1000);
+
+ Botan::Path_Validation_Result result = Botan::x509_path_validate(
+ cert_chain,
+ restrictions,
+ trusted_roots,
+ hostname,
+ usage,
+ std::chrono::system_clock::now(),
+ ocsp_timeout,
+ ocsp);
+
+ output() << "Certificate validation status: " << result.result_string() << "\n";
+ if(result.successful_validation())
+ {
+ auto status = result.all_statuses();
+
+ if(status.size() > 0 && status[0].count(Botan::Certificate_Status_Code::OCSP_RESPONSE_GOOD))
+ {
+ output() << "Valid OCSP response for this server\n";
+ }
+ }
+ }
+
+ bool tls_session_established(const Botan::TLS::Session& session) override
+ {
+ output() << "Handshake complete, " << session.version().to_string()
+ << " using " << session.ciphersuite().to_string() << "\n";
+
+ if(!session.session_id().empty())
+ {
+ output() << "Session ID " << Botan::hex_encode(session.session_id()) << "\n";
+ }
+
+ if(!session.session_ticket().empty())
+ {
+ output() << "Session ticket " << Botan::hex_encode(session.session_ticket()) << "\n";
+ }
+
+ if(flag_set("print-certs"))
+ {
+ const std::vector<Botan::X509_Certificate>& certs = session.peer_certs();
+
+ for(size_t i = 0; i != certs.size(); ++i)
+ {
+ output() << "Certificate " << i + 1 << "/" << certs.size() << "\n";
+ output() << certs[i].to_string();
+ output() << certs[i].PEM_encode();
+ }
+ }
+
+ return true;
+ }
+
+ static void dgram_socket_write(int sockfd, const uint8_t buf[], size_t length)
+ {
+ int r = ::send(sockfd, buf, length, MSG_NOSIGNAL);
+
+ if(r == -1)
+ {
+ throw CLI_Error("Socket write failed errno=" + std::to_string(errno));
+ }
+ }
+
+ void tls_emit_data(const uint8_t buf[], size_t length) override
+ {
+ size_t offset = 0;
+
+ while(length)
+ {
+ ssize_t sent = ::send(m_sockfd, buf + offset, length, MSG_NOSIGNAL);
+
+ if(sent == -1)
+ {
+ if(errno == EINTR)
+ {
+ sent = 0;
+ }
+ else
+ {
+ throw CLI_Error("Socket write failed errno=" + std::to_string(errno));
+ }
+ }
+
+ offset += sent;
+ length -= sent;
+ }
+ }
+
+ void tls_alert(Botan::TLS::Alert alert) override
+ {
+ output() << "Alert: " << alert.type_string() << "\n";
+ }
+
+ void tls_record_received(uint64_t /*seq_no*/, const uint8_t buf[], size_t buf_size) override
+ {
+ for(size_t i = 0; i != buf_size; ++i)
+ {
+ output() << buf[i];
+ }
+ }
+
+ socket_type m_sockfd = invalid_socket();
+ };
+
+BOTAN_REGISTER_COMMAND("tls_client", TLS_Client);
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/cli/tls_helpers.h b/comm/third_party/botan/src/cli/tls_helpers.h
new file mode 100644
index 0000000000..653a106e0b
--- /dev/null
+++ b/comm/third_party/botan/src/cli/tls_helpers.h
@@ -0,0 +1,244 @@
+/*
+* (C) 2014,2015,2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_CLI_TLS_HELPERS_H_
+#define BOTAN_CLI_TLS_HELPERS_H_
+
+#include <botan/pkcs8.h>
+#include <botan/credentials_manager.h>
+#include <botan/tls_policy.h>
+#include <botan/x509self.h>
+#include <botan/data_src.h>
+#include <memory>
+#include <fstream>
+
+#include "cli_exceptions.h"
+
+#if defined(BOTAN_HAS_CERTSTOR_SYSTEM)
+ #include <botan/certstor_system.h>
+#endif
+
+inline bool value_exists(const std::vector<std::string>& vec,
+ const std::string& val)
+ {
+ for(size_t i = 0; i != vec.size(); ++i)
+ {
+ if(vec[i] == val)
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+class Basic_Credentials_Manager : public Botan::Credentials_Manager
+ {
+ public:
+ Basic_Credentials_Manager(bool use_system_store,
+ const std::string& ca_path)
+ {
+ if(ca_path.empty() == false)
+ {
+ m_certstores.push_back(std::make_shared<Botan::Certificate_Store_In_Memory>(ca_path));
+ }
+
+#if defined(BOTAN_HAS_CERTSTOR_SYSTEM)
+ if(use_system_store)
+ {
+ m_certstores.push_back(std::make_shared<Botan::System_Certificate_Store>());
+ }
+#else
+ BOTAN_UNUSED(use_system_store);
+#endif
+ }
+
+ Basic_Credentials_Manager(Botan::RandomNumberGenerator& rng,
+ const std::string& server_crt,
+ const std::string& server_key)
+ {
+ Certificate_Info cert;
+
+ cert.key.reset(Botan::PKCS8::load_key(server_key, rng));
+
+ Botan::DataSource_Stream in(server_crt);
+ while(!in.end_of_data())
+ {
+ try
+ {
+ cert.certs.push_back(Botan::X509_Certificate(in));
+ }
+ catch(std::exception&)
+ {
+ }
+ }
+
+ // TODO: attempt to validate chain ourselves
+
+ m_creds.push_back(cert);
+ }
+
+ std::vector<Botan::Certificate_Store*>
+ trusted_certificate_authorities(const std::string& type,
+ const std::string& /*hostname*/) override
+ {
+ std::vector<Botan::Certificate_Store*> v;
+
+ // don't ask for client certs
+ if(type == "tls-server")
+ {
+ return v;
+ }
+
+ for(auto const& cs : m_certstores)
+ {
+ v.push_back(cs.get());
+ }
+
+ return v;
+ }
+
+ std::vector<Botan::X509_Certificate> cert_chain(
+ const std::vector<std::string>& algos,
+ const std::string& type,
+ const std::string& hostname) override
+ {
+ BOTAN_UNUSED(type);
+
+ for(auto const& i : m_creds)
+ {
+ if(std::find(algos.begin(), algos.end(), i.key->algo_name()) == algos.end())
+ {
+ continue;
+ }
+
+ if(hostname != "" && !i.certs[0].matches_dns_name(hostname))
+ {
+ continue;
+ }
+
+ return i.certs;
+ }
+
+ return std::vector<Botan::X509_Certificate>();
+ }
+
+ Botan::Private_Key* private_key_for(const Botan::X509_Certificate& cert,
+ const std::string& /*type*/,
+ const std::string& /*context*/) override
+ {
+ for(auto const& i : m_creds)
+ {
+ if(cert == i.certs[0])
+ {
+ return i.key.get();
+ }
+ }
+
+ return nullptr;
+ }
+
+ private:
+ struct Certificate_Info
+ {
+ std::vector<Botan::X509_Certificate> certs;
+ std::shared_ptr<Botan::Private_Key> key;
+ };
+
+ std::vector<Certificate_Info> m_creds;
+ std::vector<std::shared_ptr<Botan::Certificate_Store>> m_certstores;
+ };
+
+class TLS_All_Policy final : public Botan::TLS::Policy
+ {
+ public:
+ std::vector<std::string> allowed_ciphers() const override
+ {
+ return std::vector<std::string>
+ {
+ "ChaCha20Poly1305",
+ "AES-256/OCB(12)",
+ "AES-128/OCB(12)",
+ "AES-256/GCM",
+ "AES-128/GCM",
+ "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",
+ "AES-256",
+ "AES-128",
+ "Camellia-256",
+ "Camellia-128",
+ "SEED"
+ "3DES"
+ };
+ }
+
+ std::vector<std::string> allowed_key_exchange_methods() const override
+ {
+ return { "SRP_SHA", "ECDHE_PSK", "DHE_PSK", "PSK", "CECPQ1", "ECDH", "DH", "RSA" };
+ }
+
+ std::vector<std::string> allowed_signature_methods() const override
+ {
+ return { "ECDSA", "RSA", "DSA", "IMPLICIT" };
+ }
+
+ bool allow_tls10() const override { return true; }
+ bool allow_tls11() const override { return true; }
+ bool allow_tls12() const override { return true; }
+ };
+
+inline std::unique_ptr<Botan::TLS::Policy> load_tls_policy(const std::string policy_type)
+ {
+ std::unique_ptr<Botan::TLS::Policy> policy;
+
+ if(policy_type == "default" || policy_type == "")
+ {
+ policy.reset(new Botan::TLS::Policy);
+ }
+ else if(policy_type == "suiteb_128")
+ {
+ policy.reset(new Botan::TLS::NSA_Suite_B_128);
+ }
+ else if(policy_type == "suiteb_192" || policy_type == "suiteb")
+ {
+ policy.reset(new Botan::TLS::NSA_Suite_B_192);
+ }
+ else if(policy_type == "strict")
+ {
+ policy.reset(new Botan::TLS::Strict_Policy);
+ }
+ else if(policy_type == "bsi")
+ {
+ policy.reset(new Botan::TLS::BSI_TR_02102_2);
+ }
+ else if(policy_type == "datagram")
+ {
+ policy.reset(new Botan::TLS::Strict_Policy);
+ }
+ else if(policy_type == "all" || policy_type == "everything")
+ {
+ policy.reset(new TLS_All_Policy);
+ }
+ else
+ {
+ // assume it's a file
+ std::ifstream policy_stream(policy_type);
+ if(!policy_stream.good())
+ {
+ throw Botan_CLI::CLI_Usage_Error("Unknown TLS policy: not a file or known short name");
+ }
+ policy.reset(new Botan::TLS::Text_Policy(policy_stream));
+ }
+
+ return policy;
+ }
+
+#endif
diff --git a/comm/third_party/botan/src/cli/tls_http_server.cpp b/comm/third_party/botan/src/cli/tls_http_server.cpp
new file mode 100644
index 0000000000..aaf740fcf3
--- /dev/null
+++ b/comm/third_party/botan/src/cli/tls_http_server.cpp
@@ -0,0 +1,579 @@
+/*
+* (C) 2014,2015,2017,2019 Jack Lloyd
+* (C) 2016 Matthias Gierlings
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "cli.h"
+
+#if defined(BOTAN_HAS_TLS) && defined(BOTAN_HAS_BOOST_ASIO) && defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
+
+#include <iostream>
+#include <fstream>
+#include <iomanip>
+#include <string>
+#include <vector>
+#include <thread>
+#include <atomic>
+
+#define _GLIBCXX_HAVE_GTHR_DEFAULT
+#include <boost/asio.hpp>
+#include <boost/bind.hpp>
+#include <botan/internal/os_utils.h>
+
+#include <botan/tls_server.h>
+#include <botan/tls_messages.h>
+#include <botan/x509cert.h>
+#include <botan/pkcs8.h>
+#include <botan/version.h>
+#include <botan/hex.h>
+#include <botan/rng.h>
+
+#if defined(BOTAN_HAS_TLS_SQLITE3_SESSION_MANAGER)
+ #include <botan/tls_session_manager_sqlite.h>
+#endif
+
+#include "tls_helpers.h"
+
+#if BOOST_VERSION >= 107000
+#define GET_IO_SERVICE(s) (static_cast<boost::asio::io_context&>((s).get_executor().context()))
+#else
+#define GET_IO_SERVICE(s) ((s).get_io_service())
+#endif
+
+namespace Botan_CLI {
+
+namespace {
+
+using boost::asio::ip::tcp;
+
+inline void log_exception(const char* where, const std::exception& e)
+ {
+ std::cout << where << ' ' << e.what() << std::endl;
+ }
+
+class ServerStatus
+ {
+ public:
+ ServerStatus(size_t max_clients) : m_max_clients(max_clients), m_clients_serviced(0) {}
+
+ bool should_exit() const
+ {
+ if(m_max_clients == 0)
+ return false;
+
+ return clients_serviced() >= m_max_clients;
+ }
+
+ void client_serviced() { m_clients_serviced++; }
+
+ size_t clients_serviced() const { return m_clients_serviced.load(); }
+ private:
+ size_t m_max_clients;
+ std::atomic<size_t> m_clients_serviced;
+ };
+
+/*
+* This is an incomplete and highly buggy HTTP request parser. It is just
+* barely sufficient to handle a GET request sent by a browser.
+*/
+class HTTP_Parser final
+ {
+ public:
+ class Request
+ {
+ public:
+ const std::string& verb() const { return m_verb; }
+ const std::string& location() const { return m_location; }
+ const std::map<std::string, std::string>& headers() const { return m_headers; }
+
+ Request(const std::string& verb,
+ const std::string& location,
+ const std::map<std::string, std::string>& headers) :
+ m_verb(verb),
+ m_location(location),
+ m_headers(headers)
+ {}
+
+ private:
+ std::string m_verb;
+ std::string m_location;
+ std::map<std::string, std::string> m_headers;
+ };
+
+ class Callbacks
+ {
+ public:
+ virtual void handle_http_request(const Request& request) = 0;
+ virtual ~Callbacks() = default;
+ };
+
+ HTTP_Parser(Callbacks& cb) : m_cb(cb) {}
+
+ void consume_input(const uint8_t buf[], size_t buf_len)
+ {
+ m_req_buf.append(reinterpret_cast<const char*>(buf), buf_len);
+
+ std::istringstream strm(m_req_buf);
+
+ std::string http_version;
+ std::string verb;
+ std::string location;
+ std::map<std::string, std::string> headers;
+
+ strm >> verb >> location >> http_version;
+
+ if(verb.empty() || location.empty())
+ return;
+
+ while(true)
+ {
+ std::string header_line;
+ std::getline(strm, header_line);
+
+ if(header_line == "\r")
+ {
+ continue;
+ }
+
+ auto delim = header_line.find(": ");
+ if(delim == std::string::npos)
+ {
+ break;
+ }
+
+ const std::string hdr_name = header_line.substr(0, delim);
+ const std::string hdr_val = header_line.substr(delim + 2, std::string::npos);
+
+ headers[hdr_name] = hdr_val;
+
+ if(headers.size() > 1024)
+ throw Botan::Invalid_Argument("Too many HTTP headers sent in request");
+ }
+
+ if(verb != "" && location != "")
+ {
+ Request req(verb, location, headers);
+ m_cb.handle_http_request(req);
+ m_req_buf.clear();
+ }
+ else
+ printf("ignoring\n");
+ }
+ private:
+ Callbacks& m_cb;
+ std::string m_req_buf;
+ };
+
+static const size_t READBUF_SIZE = 4096;
+
+class TLS_Asio_HTTP_Session final : public std::enable_shared_from_this<TLS_Asio_HTTP_Session>,
+ public Botan::TLS::Callbacks,
+ public HTTP_Parser::Callbacks
+ {
+ public:
+ typedef std::shared_ptr<TLS_Asio_HTTP_Session> pointer;
+
+ static pointer create(
+ boost::asio::io_service& io,
+ Botan::TLS::Session_Manager& session_manager,
+ Botan::Credentials_Manager& credentials,
+ Botan::TLS::Policy& policy)
+ {
+ return pointer(new TLS_Asio_HTTP_Session(io, session_manager, credentials, policy));
+ }
+
+ tcp::socket& client_socket()
+ {
+ return m_client_socket;
+ }
+
+ void start()
+ {
+ m_c2s.resize(READBUF_SIZE);
+ client_read(boost::system::error_code(), 0); // start read loop
+ }
+
+ void stop()
+ {
+ m_tls.close();
+ }
+
+ private:
+ TLS_Asio_HTTP_Session(boost::asio::io_service& io,
+ Botan::TLS::Session_Manager& session_manager,
+ Botan::Credentials_Manager& credentials,
+ Botan::TLS::Policy& policy)
+ : m_strand(io)
+ , m_client_socket(io)
+ , m_rng(cli_make_rng())
+ , m_tls(*this, session_manager, credentials, policy, *m_rng) {}
+
+ void client_read(const boost::system::error_code& error,
+ size_t bytes_transferred)
+ {
+ if(error)
+ {
+ return stop();
+ }
+
+ try
+ {
+ m_tls.received_data(&m_c2s[0], bytes_transferred);
+ }
+ catch(Botan::Exception& e)
+ {
+ log_exception("TLS connection failed", e);
+ return stop();
+ }
+
+ m_client_socket.async_read_some(
+ boost::asio::buffer(&m_c2s[0], m_c2s.size()),
+ m_strand.wrap(
+ boost::bind(
+ &TLS_Asio_HTTP_Session::client_read, shared_from_this(),
+ boost::asio::placeholders::error,
+ boost::asio::placeholders::bytes_transferred)));
+ }
+
+ void handle_client_write_completion(const boost::system::error_code& error)
+ {
+ if(error)
+ {
+ return stop();
+ }
+
+ m_s2c.clear();
+
+ if(m_s2c_pending.empty() && m_tls.is_closed())
+ {
+ m_client_socket.close();
+ }
+ tls_emit_data(nullptr, 0); // initiate another write if needed
+ }
+
+ std::string tls_server_choose_app_protocol(const std::vector<std::string>& /*client_protos*/) override
+ {
+ return "http/1.1";
+ }
+
+ void tls_record_received(uint64_t /*rec_no*/, const uint8_t buf[], size_t buf_len) override
+ {
+ if(!m_http_parser)
+ m_http_parser.reset(new HTTP_Parser(*this));
+
+ m_http_parser->consume_input(buf, buf_len);
+ }
+
+ std::string summarize_request(const HTTP_Parser::Request& request)
+ {
+ std::ostringstream strm;
+
+ strm << "Client " << client_socket().remote_endpoint().address().to_string()
+ << " requested " << request.verb() << " " << request.location() << "\n";
+
+ if(request.headers().empty() == false)
+ {
+ strm << "Client HTTP headers:\n";
+ for(auto kv : request.headers())
+ strm << " " << kv.first << ": " << kv.second << "\n";
+ }
+
+ return strm.str();
+ }
+
+ void handle_http_request(const HTTP_Parser::Request& request) override
+ {
+ std::ostringstream response;
+ if(request.verb() == "GET")
+ {
+ if(request.location() == "/" || request.location() == "/status")
+ {
+ const std::string http_summary = summarize_request(request);
+
+ const std::string report = m_session_summary + m_chello_summary + http_summary;
+
+ response << "HTTP/1.0 200 OK\r\n";
+ response << "Server: " << Botan::version_string() << "\r\n";
+ response << "Content-Type: text/plain\r\n";
+ response << "Content-Length: " << report.size() << "\r\n";
+ response << "\r\n";
+
+ response << report;
+ }
+ else
+ {
+ response << "HTTP/1.0 404 Not Found\r\n\r\n";
+ }
+ }
+ else
+ {
+ response << "HTTP/1.0 405 Method Not Allowed\r\n\r\n";
+ }
+
+ const std::string response_str = response.str();
+ m_tls.send(response_str);
+ m_tls.close();
+ }
+
+ void tls_emit_data(const uint8_t buf[], size_t buf_len) override
+ {
+ if(buf_len > 0)
+ {
+ m_s2c_pending.insert(m_s2c_pending.end(), buf, buf + buf_len);
+ }
+
+ // no write now active and we still have output pending
+ if(m_s2c.empty() && !m_s2c_pending.empty())
+ {
+ std::swap(m_s2c_pending, m_s2c);
+
+ boost::asio::async_write(
+ m_client_socket,
+ boost::asio::buffer(&m_s2c[0], m_s2c.size()),
+ m_strand.wrap(
+ boost::bind(
+ &TLS_Asio_HTTP_Session::handle_client_write_completion,
+ shared_from_this(),
+ boost::asio::placeholders::error)));
+ }
+ }
+
+ bool tls_session_established(const Botan::TLS::Session& session) override
+ {
+ std::ostringstream strm;
+
+ strm << "TLS negotiation with " << Botan::version_string() << " test server\n\n";
+
+ strm << "Version: " << session.version().to_string() << "\n";
+ strm << "Ciphersuite: " << session.ciphersuite().to_string() << "\n";
+ if(session.session_id().empty() == false)
+ {
+ strm << "SessionID: " << Botan::hex_encode(session.session_id()) << "\n";
+ }
+ if(session.server_info().hostname() != "")
+ {
+ strm << "SNI: " << session.server_info().hostname() << "\n";
+ }
+
+ m_session_summary = strm.str();
+ return true;
+ }
+
+ void tls_inspect_handshake_msg(const Botan::TLS::Handshake_Message& message) override
+ {
+ if(message.type() == Botan::TLS::CLIENT_HELLO)
+ {
+ const Botan::TLS::Client_Hello& client_hello = dynamic_cast<const Botan::TLS::Client_Hello&>(message);
+
+ std::ostringstream strm;
+
+ strm << "Client random: " << Botan::hex_encode(client_hello.random()) << "\n";
+
+ strm << "Client offered following ciphersuites:\n";
+ for(uint16_t suite_id : client_hello.ciphersuites())
+ {
+ Botan::TLS::Ciphersuite ciphersuite = Botan::TLS::Ciphersuite::by_id(suite_id);
+
+ strm << " - 0x"
+ << std::hex << std::setfill('0') << std::setw(4) << suite_id
+ << std::dec << std::setfill(' ') << std::setw(0) << " ";
+
+ if(ciphersuite.valid())
+ strm << ciphersuite.to_string() << "\n";
+ else if(suite_id == 0x00FF)
+ strm << "Renegotiation SCSV\n";
+ else
+ strm << "Unknown ciphersuite\n";
+ }
+
+ m_chello_summary = strm.str();
+ }
+
+ }
+
+ void tls_alert(Botan::TLS::Alert alert) override
+ {
+ if(alert.type() == Botan::TLS::Alert::CLOSE_NOTIFY)
+ {
+ m_tls.close();
+ return;
+ }
+ else
+ {
+ std::cout << "Alert " << alert.type_string() << std::endl;
+ }
+ }
+
+ boost::asio::io_service::strand m_strand;
+
+ tcp::socket m_client_socket;
+
+ std::unique_ptr<Botan::RandomNumberGenerator> m_rng;
+ Botan::TLS::Server m_tls;
+ std::string m_chello_summary;
+ std::string m_session_summary;
+ std::unique_ptr<HTTP_Parser> m_http_parser;
+
+ std::vector<uint8_t> m_c2s;
+ std::vector<uint8_t> m_s2c;
+ std::vector<uint8_t> m_s2c_pending;
+ };
+
+class TLS_Asio_HTTP_Server final
+ {
+ public:
+ typedef TLS_Asio_HTTP_Session session;
+
+ TLS_Asio_HTTP_Server(
+ boost::asio::io_service& io, unsigned short port,
+ Botan::Credentials_Manager& creds,
+ Botan::TLS::Policy& policy,
+ Botan::TLS::Session_Manager& session_mgr,
+ size_t max_clients)
+ : m_acceptor(io, tcp::endpoint(tcp::v4(), port))
+ , m_creds(creds)
+ , m_policy(policy)
+ , m_session_manager(session_mgr)
+ , m_status(max_clients)
+ {
+ session::pointer new_session = make_session();
+
+ m_acceptor.async_accept(
+ new_session->client_socket(),
+ boost::bind(
+ &TLS_Asio_HTTP_Server::handle_accept,
+ this,
+ new_session,
+ boost::asio::placeholders::error));
+ }
+
+ private:
+ session::pointer make_session()
+ {
+ return session::create(
+ GET_IO_SERVICE(m_acceptor),
+ m_session_manager,
+ m_creds,
+ m_policy);
+ }
+
+ void handle_accept(session::pointer new_session,
+ const boost::system::error_code& error)
+ {
+ if(!error)
+ {
+ new_session->start();
+ new_session = make_session();
+
+ m_status.client_serviced();
+
+ if(m_status.should_exit() == false)
+ {
+ m_acceptor.async_accept(
+ new_session->client_socket(),
+ boost::bind(
+ &TLS_Asio_HTTP_Server::handle_accept,
+ this,
+ new_session,
+ boost::asio::placeholders::error));
+ }
+ }
+ }
+
+ tcp::acceptor m_acceptor;
+
+ Botan::Credentials_Manager& m_creds;
+ Botan::TLS::Policy& m_policy;
+ Botan::TLS::Session_Manager& m_session_manager;
+ ServerStatus m_status;
+ };
+
+}
+
+class TLS_HTTP_Server final : public Command
+ {
+ public:
+ TLS_HTTP_Server() : Command("tls_http_server server_cert server_key "
+ "--port=443 --policy=default --threads=0 --max-clients=0 "
+ "--session-db= --session-db-pass=") {}
+
+ std::string group() const override
+ {
+ return "tls";
+ }
+
+ std::string description() const override
+ {
+ return "Provides a simple HTTP server";
+ }
+
+ size_t thread_count() const
+ {
+ if(size_t t = get_arg_sz("threads"))
+ return t;
+ if(size_t t = Botan::OS::get_cpu_available())
+ return t;
+ return 2;
+ }
+
+ void go() override
+ {
+ const uint16_t listen_port = get_arg_u16("port");
+
+ const std::string server_crt = get_arg("server_cert");
+ const std::string server_key = get_arg("server_key");
+
+ const size_t num_threads = thread_count();
+ const size_t max_clients = get_arg_sz("max-clients");
+
+ Basic_Credentials_Manager creds(rng(), server_crt, server_key);
+
+ auto policy = load_tls_policy(get_arg("policy"));
+
+ std::unique_ptr<Botan::TLS::Session_Manager> session_mgr;
+
+ const std::string sessions_db = get_arg("session-db");
+
+ if(!sessions_db.empty())
+ {
+#if defined(BOTAN_HAS_TLS_SQLITE3_SESSION_MANAGER)
+ const std::string sessions_passphrase = get_passphrase_arg("Session DB passphrase", "session-db-pass");
+ session_mgr.reset(new Botan::TLS::Session_Manager_SQLite(sessions_passphrase, rng(), sessions_db));
+#else
+ throw CLI_Error_Unsupported("Sqlite3 support not available");
+#endif
+ }
+
+ if(!session_mgr)
+ {
+ session_mgr.reset(new Botan::TLS::Session_Manager_In_Memory(rng()));
+ }
+
+ boost::asio::io_service io;
+
+ TLS_Asio_HTTP_Server server(io, listen_port, creds, *policy, *session_mgr, max_clients);
+
+ std::vector<std::shared_ptr<std::thread>> threads;
+
+ // run forever... first thread is main calling io.run below
+ for(size_t i = 2; i <= num_threads; ++i)
+ {
+ threads.push_back(std::make_shared<std::thread>([&io]() { io.run(); }));
+ }
+
+ io.run();
+
+ for(size_t i = 0; i < threads.size(); ++i)
+ {
+ threads[i]->join();
+ }
+ }
+ };
+
+BOTAN_REGISTER_COMMAND("tls_http_server", TLS_HTTP_Server);
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/cli/tls_proxy.cpp b/comm/third_party/botan/src/cli/tls_proxy.cpp
new file mode 100644
index 0000000000..bd96530c20
--- /dev/null
+++ b/comm/third_party/botan/src/cli/tls_proxy.cpp
@@ -0,0 +1,526 @@
+/*
+* TLS Server Proxy
+* (C) 2014,2015,2019 Jack Lloyd
+* (C) 2016 Matthias Gierlings
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "cli.h"
+
+#if defined(BOTAN_HAS_TLS) && defined(BOTAN_HAS_BOOST_ASIO) && defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
+
+#include <iostream>
+#include <string>
+#include <vector>
+#include <thread>
+#include <atomic>
+
+#define _GLIBCXX_HAVE_GTHR_DEFAULT
+#include <boost/asio.hpp>
+#include <boost/bind.hpp>
+#include <botan/internal/os_utils.h>
+
+#include <botan/tls_server.h>
+#include <botan/x509cert.h>
+#include <botan/pkcs8.h>
+#include <botan/hex.h>
+#include <botan/rng.h>
+
+#if defined(BOTAN_HAS_TLS_SQLITE3_SESSION_MANAGER)
+ #include <botan/tls_session_manager_sqlite.h>
+#endif
+
+#include "tls_helpers.h"
+
+#if BOOST_VERSION >= 107000
+#define GET_IO_SERVICE(s) (static_cast<boost::asio::io_context&>((s).get_executor().context()))
+#else
+#define GET_IO_SERVICE(s) ((s).get_io_service())
+#endif
+
+namespace Botan_CLI {
+
+namespace {
+
+using boost::asio::ip::tcp;
+
+void log_exception(const char* where, const std::exception& e)
+ {
+ std::cout << where << ' ' << e.what() << std::endl;
+ }
+
+void log_error(const char* where, const boost::system::error_code& error)
+ {
+ std::cout << where << ' ' << error.message() << std::endl;
+ }
+
+void log_binary_message(const char* where, const uint8_t buf[], size_t buf_len)
+ {
+ BOTAN_UNUSED(where, buf, buf_len);
+ //std::cout << where << ' ' << Botan::hex_encode(buf, buf_len) << std::endl;
+ }
+
+void log_text_message(const char* where, const uint8_t buf[], size_t buf_len)
+ {
+ BOTAN_UNUSED(where, buf, buf_len);
+ //const char* c = reinterpret_cast<const char*>(buf);
+ //std::cout << where << ' ' << std::string(c, c + buf_len) << std::endl;
+ }
+
+class ServerStatus
+ {
+ public:
+ ServerStatus(size_t max_clients) : m_max_clients(max_clients), m_clients_serviced(0) {}
+
+ bool should_exit() const
+ {
+ if(m_max_clients == 0)
+ return false;
+
+ return clients_serviced() >= m_max_clients;
+ }
+
+ void client_serviced() { m_clients_serviced++; }
+
+ size_t clients_serviced() const { return m_clients_serviced.load(); }
+ private:
+ size_t m_max_clients;
+ std::atomic<size_t> m_clients_serviced;
+ };
+
+class tls_proxy_session final : public std::enable_shared_from_this<tls_proxy_session>,
+ public Botan::TLS::Callbacks
+ {
+ public:
+ enum { readbuf_size = 17 * 1024 };
+
+ typedef std::shared_ptr<tls_proxy_session> pointer;
+
+ static pointer create(
+ boost::asio::io_service& io,
+ Botan::TLS::Session_Manager& session_manager,
+ Botan::Credentials_Manager& credentials,
+ Botan::TLS::Policy& policy,
+ tcp::resolver::iterator endpoints)
+ {
+ return pointer(
+ new tls_proxy_session(
+ io,
+ session_manager,
+ credentials,
+ policy,
+ endpoints)
+ );
+ }
+
+ tcp::socket& client_socket()
+ {
+ return m_client_socket;
+ }
+
+ void start()
+ {
+ m_c2p.resize(readbuf_size);
+ client_read(boost::system::error_code(), 0); // start read loop
+ }
+
+ void stop()
+ {
+ if(m_is_closed == false)
+ {
+ /*
+ Don't need to talk to the server anymore
+ Client socket is closed during write callback
+ */
+ m_server_socket.close();
+ m_tls.close();
+ m_is_closed = true;
+ }
+ }
+
+ private:
+ tls_proxy_session(
+ boost::asio::io_service& io,
+ Botan::TLS::Session_Manager& session_manager,
+ Botan::Credentials_Manager& credentials,
+ Botan::TLS::Policy& policy,
+ tcp::resolver::iterator endpoints)
+ : m_strand(io)
+ , m_server_endpoints(endpoints)
+ , m_client_socket(io)
+ , m_server_socket(io)
+ , m_rng(cli_make_rng())
+ , m_tls(*this,
+ session_manager,
+ credentials,
+ policy,
+ *m_rng) {}
+
+ void client_read(const boost::system::error_code& error,
+ size_t bytes_transferred)
+ {
+ if(error)
+ {
+ log_error("Read failed", error);
+ stop();
+ return;
+ }
+
+ try
+ {
+ if(!m_tls.is_active())
+ {
+ log_binary_message("From client", &m_c2p[0], bytes_transferred);
+ }
+ m_tls.received_data(&m_c2p[0], 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)));
+ }
+
+ void handle_client_write_completion(const boost::system::error_code& error)
+ {
+ if(error)
+ {
+ log_error("Client write", error);
+ stop();
+ return;
+ }
+
+ m_p2c.clear();
+
+ if(m_p2c_pending.empty() && m_tls.is_closed())
+ {
+ m_client_socket.close();
+ }
+ tls_emit_data(nullptr, 0); // initiate another write if needed
+ }
+
+ void handle_server_write_completion(const boost::system::error_code& error)
+ {
+ if(error)
+ {
+ log_error("Server write", error);
+ stop();
+ return;
+ }
+
+ m_p2s.clear();
+ proxy_write_to_server(nullptr, 0); // initiate another write if needed
+ }
+
+ void tls_record_received(uint64_t /*rec_no*/, const uint8_t buf[], size_t buf_len) override
+ {
+ // Immediately bounce message to server
+ proxy_write_to_server(buf, buf_len);
+ }
+
+ void tls_emit_data(const uint8_t buf[], size_t buf_len) override
+ {
+ if(buf_len > 0)
+ {
+ m_p2c_pending.insert(m_p2c_pending.end(), buf, buf + buf_len);
+ }
+
+ // no write now active and we still have output pending
+ 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());
+
+ 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)));
+ }
+ }
+
+ void proxy_write_to_server(const uint8_t buf[], size_t buf_len)
+ {
+ if(buf_len > 0)
+ {
+ m_p2s_pending.insert(m_p2s_pending.end(), buf, buf + buf_len);
+ }
+
+ // no write now active and we still have output pending
+ 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());
+
+ 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)));
+ }
+ }
+
+ void server_read(const boost::system::error_code& error,
+ size_t bytes_transferred)
+ {
+ if(error)
+ {
+ log_error("Server read failed", error);
+ stop();
+ return;
+ }
+
+ 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);
+ }
+ }
+ catch(Botan::Exception& e)
+ {
+ log_exception("TLS connection failed", e);
+ stop();
+ return;
+ }
+
+ 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)));
+ }
+
+ bool tls_session_established(const Botan::TLS::Session& session) override
+ {
+ m_hostname = session.server_info().hostname();
+
+ auto onConnect = [this](boost::system::error_code ec, tcp::resolver::iterator /*endpoint*/)
+ {
+ if(ec)
+ {
+ log_error("Server connection", ec);
+ return;
+ }
+ server_read(boost::system::error_code(), 0); // start read loop
+ proxy_write_to_server(nullptr, 0);
+ };
+ async_connect(m_server_socket, m_server_endpoints, onConnect);
+ return true;
+ }
+
+ void tls_alert(Botan::TLS::Alert alert) override
+ {
+ if(alert.type() == Botan::TLS::Alert::CLOSE_NOTIFY)
+ {
+ m_tls.close();
+ return;
+ }
+ }
+
+ boost::asio::io_service::strand m_strand;
+
+ tcp::resolver::iterator m_server_endpoints;
+
+ tcp::socket m_client_socket;
+ tcp::socket m_server_socket;
+
+ std::unique_ptr<Botan::RandomNumberGenerator> m_rng;
+ Botan::TLS::Server m_tls;
+ std::string m_hostname;
+
+ std::vector<uint8_t> m_c2p;
+ std::vector<uint8_t> m_p2c;
+ std::vector<uint8_t> m_p2c_pending;
+
+ std::vector<uint8_t> m_s2p;
+ std::vector<uint8_t> m_p2s;
+ std::vector<uint8_t> m_p2s_pending;
+
+ bool m_is_closed = false;
+ };
+
+class tls_proxy_server final
+ {
+ public:
+ typedef tls_proxy_session session;
+
+ tls_proxy_server(
+ boost::asio::io_service& io, unsigned short port,
+ tcp::resolver::iterator endpoints,
+ Botan::Credentials_Manager& creds,
+ Botan::TLS::Policy& policy,
+ Botan::TLS::Session_Manager& session_mgr,
+ size_t max_clients)
+ : m_acceptor(io, tcp::endpoint(tcp::v4(), port))
+ , m_server_endpoints(endpoints)
+ , m_creds(creds)
+ , m_policy(policy)
+ , m_session_manager(session_mgr)
+ , m_status(max_clients)
+ {
+ session::pointer new_session = make_session();
+
+ m_acceptor.async_accept(
+ new_session->client_socket(),
+ boost::bind(
+ &tls_proxy_server::handle_accept,
+ this,
+ new_session,
+ boost::asio::placeholders::error));
+ }
+
+ private:
+ session::pointer make_session()
+ {
+ return session::create(
+ GET_IO_SERVICE(m_acceptor),
+ m_session_manager,
+ m_creds,
+ m_policy,
+ m_server_endpoints);
+ }
+
+ void handle_accept(session::pointer new_session,
+ const boost::system::error_code& error)
+ {
+ if(!error)
+ {
+ new_session->start();
+ new_session = make_session();
+
+ m_status.client_serviced();
+
+ if(m_status.should_exit() == false)
+ {
+ m_acceptor.async_accept(
+ new_session->client_socket(),
+ boost::bind(
+ &tls_proxy_server::handle_accept,
+ this,
+ new_session,
+ boost::asio::placeholders::error));
+ }
+ }
+ }
+
+ tcp::acceptor m_acceptor;
+ tcp::resolver::iterator m_server_endpoints;
+
+ Botan::Credentials_Manager& m_creds;
+ Botan::TLS::Policy& m_policy;
+ Botan::TLS::Session_Manager& m_session_manager;
+ ServerStatus m_status;
+ };
+
+}
+
+class TLS_Proxy final : public Command
+ {
+ public:
+ TLS_Proxy() : Command("tls_proxy listen_port target_host target_port server_cert server_key "
+ "--policy=default --threads=0 --max-clients=0 --session-db= --session-db-pass=") {}
+
+ std::string group() const override
+ {
+ return "tls";
+ }
+
+ 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"))
+ return t;
+ if(size_t t = Botan::OS::get_cpu_available())
+ return t;
+ return 2;
+ }
+
+ void go() override
+ {
+ const uint16_t listen_port = get_arg_u16("listen_port");
+ const std::string target = get_arg("target_host");
+ const std::string target_port = get_arg("target_port");
+
+ const std::string server_crt = get_arg("server_cert");
+ const std::string server_key = get_arg("server_key");
+
+ const size_t num_threads = thread_count();
+ const size_t max_clients = get_arg_sz("max-clients");
+
+ Basic_Credentials_Manager creds(rng(), server_crt, server_key);
+
+ auto policy = load_tls_policy(get_arg("policy"));
+
+ boost::asio::io_service io;
+
+ tcp::resolver resolver(io);
+ auto server_endpoint_iterator = resolver.resolve({ target, target_port });
+
+ std::unique_ptr<Botan::TLS::Session_Manager> session_mgr;
+
+#if defined(BOTAN_HAS_TLS_SQLITE3_SESSION_MANAGER)
+ const std::string sessions_passphrase = get_passphrase_arg("Session DB passphrase", "session-db-pass");
+ const std::string sessions_db = get_arg("session-db");
+
+ if(!sessions_db.empty())
+ {
+ session_mgr.reset(new Botan::TLS::Session_Manager_SQLite(sessions_passphrase, rng(), sessions_db));
+ }
+#endif
+ if(!session_mgr)
+ {
+ session_mgr.reset(new Botan::TLS::Session_Manager_In_Memory(rng()));
+ }
+
+ tls_proxy_server server(io, listen_port, server_endpoint_iterator, creds, *policy, *session_mgr, max_clients);
+
+ std::vector<std::shared_ptr<std::thread>> threads;
+
+ // run forever... first thread is main calling io.run below
+ for(size_t i = 2; i <= num_threads; ++i)
+ {
+ threads.push_back(std::make_shared<std::thread>([&io]() { io.run(); }));
+ }
+
+ io.run();
+
+ for(size_t i = 0; i < threads.size(); ++i)
+ {
+ threads[i]->join();
+ }
+ }
+ };
+
+BOTAN_REGISTER_COMMAND("tls_proxy", TLS_Proxy);
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/cli/tls_server.cpp b/comm/third_party/botan/src/cli/tls_server.cpp
new file mode 100644
index 0000000000..c39061e64d
--- /dev/null
+++ b/comm/third_party/botan/src/cli/tls_server.cpp
@@ -0,0 +1,364 @@
+/*
+* TLS echo server using BSD sockets
+* (C) 2014 Jack Lloyd
+* 2017 René Korthaus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "cli.h"
+#include "sandbox.h"
+
+#if defined(BOTAN_HAS_TLS) && defined(BOTAN_TARGET_OS_HAS_FILESYSTEM) && \
+ defined(BOTAN_TARGET_OS_HAS_SOCKETS)
+
+#if defined(SO_USER_COOKIE)
+#define SOCKET_ID 1
+#else
+#define SOCKET_ID 0
+#endif
+
+#include <botan/tls_server.h>
+#include <botan/tls_policy.h>
+#include <botan/hex.h>
+#include <botan/internal/os_utils.h>
+#include <botan/mem_ops.h>
+
+#include <list>
+#include <fstream>
+
+#include "tls_helpers.h"
+#include "socket_utils.h"
+
+namespace Botan_CLI {
+
+class TLS_Server final : public Command, public Botan::TLS::Callbacks
+ {
+ public:
+#if SOCKET_ID
+ TLS_Server() : Command("tls_server cert key --port=443 --type=tcp --policy=default --dump-traces= --max-clients=0 --socket-id=0")
+#else
+ TLS_Server() : Command("tls_server cert key --port=443 --type=tcp --policy=default --dump-traces= --max-clients=0")
+#endif
+ {
+ init_sockets();
+ }
+
+ ~TLS_Server()
+ {
+ stop_sockets();
+ }
+
+ std::string group() const override
+ {
+ return "tls";
+ }
+
+ std::string description() const override
+ {
+ return "Accept TLS/DTLS connections from TLS/DTLS clients";
+ }
+
+ void go() override
+ {
+ const std::string server_crt = get_arg("cert");
+ const std::string server_key = get_arg("key");
+ const uint16_t port = get_arg_u16("port");
+ const size_t max_clients = get_arg_sz("max-clients");
+ const std::string transport = get_arg("type");
+ const std::string dump_traces_to = get_arg("dump-traces");
+#if SOCKET_ID
+ m_socket_id = get_arg_sz("socket-id");
+#endif
+
+ if(transport != "tcp" && transport != "udp")
+ {
+ throw CLI_Usage_Error("Invalid transport type '" + transport + "' for TLS");
+ }
+
+ m_is_tcp = (transport == "tcp");
+
+ auto policy = load_tls_policy(get_arg("policy"));
+
+ Botan::TLS::Session_Manager_In_Memory session_manager(rng()); // TODO sqlite3
+
+ Basic_Credentials_Manager creds(rng(), server_crt, server_key);
+
+ output() << "Listening for new connections on " << transport << " port " << port << std::endl;
+
+ if(!m_sandbox.init())
+ {
+ error_output() << "Failed sandboxing\n";
+ return;
+ }
+
+ socket_type server_fd = make_server_socket(port);
+ size_t clients_served = 0;
+
+ while(true)
+ {
+ if(max_clients > 0 && clients_served >= max_clients)
+ break;
+
+ if(m_is_tcp)
+ {
+ m_socket = ::accept(server_fd, nullptr, nullptr);
+ }
+ else
+ {
+ struct sockaddr_in from;
+ socklen_t from_len = sizeof(sockaddr_in);
+
+ void* peek_buf = nullptr;
+ size_t peek_len = 0;
+
+#if defined(BOTAN_TARGET_OS_IS_MACOS)
+ // macOS handles zero size buffers differently - it will return 0 even if there's no incoming data,
+ // and after that connect() will fail as sockaddr_in from is not initialized
+ int dummy;
+ peek_buf = &dummy;
+ peek_len = sizeof(dummy);
+#endif
+
+ if(::recvfrom(server_fd, static_cast<char*>(peek_buf), static_cast<sendrecv_len_type>(peek_len),
+ MSG_PEEK, reinterpret_cast<struct sockaddr*>(&from), &from_len) != 0)
+ {
+ throw CLI_Error("Could not peek next packet");
+ }
+
+ if(::connect(server_fd, reinterpret_cast<struct sockaddr*>(&from), from_len) != 0)
+ {
+ throw CLI_Error("Could not connect UDP socket");
+ }
+ m_socket = server_fd;
+ }
+
+ clients_served++;
+
+ Botan::TLS::Server server(
+ *this,
+ session_manager,
+ creds,
+ *policy,
+ rng(),
+ m_is_tcp == false);
+
+ std::unique_ptr<std::ostream> dump_stream;
+
+ if(!dump_traces_to.empty())
+ {
+ uint64_t timestamp = Botan::OS::get_high_resolution_clock();
+ const std::string dump_file =
+ dump_traces_to + "/tls_" + std::to_string(timestamp) + ".bin";
+ dump_stream.reset(new std::ofstream(dump_file.c_str()));
+ }
+
+ try
+ {
+ 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);
+
+ if(got == -1)
+ {
+ error_output() << "Error in socket read - " << err_to_string(errno) << std::endl;
+ break;
+ }
+
+ if(got == 0)
+ {
+ error_output() << "EOF on socket" << std::endl;
+ break;
+ }
+
+ if(dump_stream)
+ {
+ dump_stream->write(reinterpret_cast<const char*>(buf), got);
+ }
+
+ server.received_data(buf, got);
+
+ while(server.is_active() && !m_pending_output.empty())
+ {
+ std::string output = m_pending_output.front();
+ m_pending_output.pop_front();
+ server.send(output);
+
+ if(output == "quit\n")
+ {
+ server.close();
+ }
+ }
+ }
+ catch(std::exception& e)
+ {
+ error_output() << "Connection problem: " << e.what() << std::endl;
+ if(m_is_tcp)
+ {
+ close_socket(m_socket);
+ m_socket = invalid_socket();
+ }
+ }
+ }
+ }
+ catch(Botan::Exception& e)
+ {
+ error_output() << "Connection failed: " << e.what() << "\n";
+ }
+
+ if(m_is_tcp)
+ {
+ close_socket(m_socket);
+ m_socket = invalid_socket();
+ }
+ }
+
+ close_socket(server_fd);
+ }
+ private:
+ 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);
+ if(fd == invalid_socket())
+ {
+ throw CLI_Error("Unable to acquire socket");
+ }
+
+ sockaddr_in socket_info;
+ Botan::clear_mem(&socket_info, 1);
+ socket_info.sin_family = AF_INET;
+ socket_info.sin_port = htons(port);
+
+ // FIXME: support limiting listeners
+ socket_info.sin_addr.s_addr = INADDR_ANY;
+
+ if(::bind(fd, reinterpret_cast<struct sockaddr*>(&socket_info), sizeof(struct sockaddr)) != 0)
+ {
+ close_socket(fd);
+ throw CLI_Error("server bind failed");
+ }
+
+ if(m_is_tcp)
+ {
+ if(::listen(fd, 100) != 0)
+ {
+ close_socket(fd);
+ throw CLI_Error("listen failed");
+ }
+ }
+ if(m_socket_id > 0)
+ {
+#if SOCKET_ID
+ // Other oses could have other means to trace sockets
+#if defined(SO_USER_COOKIE)
+ if(::setsockopt(fd, SOL_SOCKET, SO_USER_COOKIE, reinterpret_cast<const void *>(&m_socket_id), sizeof(m_socket_id)) != 0)
+ {
+ // Failed but not world-ending issue
+ output() << "set socket cookie id failed" << std::endl;
+ }
+#endif
+#endif
+ }
+ return fd;
+ }
+
+ bool tls_session_established(const Botan::TLS::Session& session) override
+ {
+ output() << "Handshake complete, " << session.version().to_string()
+ << " using " << session.ciphersuite().to_string() << std::endl;
+
+ if(!session.session_id().empty())
+ {
+ output() << "Session ID " << Botan::hex_encode(session.session_id()) << std::endl;
+ }
+
+ if(!session.session_ticket().empty())
+ {
+ output() << "Session ticket " << Botan::hex_encode(session.session_ticket()) << std::endl;
+ }
+
+ return true;
+ }
+
+ void tls_record_received(uint64_t, const uint8_t input[], size_t input_len) override
+ {
+ for(size_t i = 0; i != input_len; ++i)
+ {
+ const char c = static_cast<char>(input[i]);
+ m_line_buf += c;
+ if(c == '\n')
+ {
+ m_pending_output.push_back(m_line_buf);
+ m_line_buf.clear();
+ }
+ }
+ }
+
+ void tls_emit_data(const uint8_t buf[], size_t length) override
+ {
+ if(m_is_tcp)
+ {
+ ssize_t sent = ::send(m_socket, buf, static_cast<sendrecv_len_type>(length), MSG_NOSIGNAL);
+
+ if(sent == -1)
+ {
+ error_output() << "Error writing to socket - " << err_to_string(errno) << std::endl;
+ }
+ else if(sent != static_cast<ssize_t>(length))
+ {
+ error_output() << "Packet of length " << length << " truncated to " << sent << std::endl;
+ }
+ }
+ else
+ {
+ while(length)
+ {
+ ssize_t sent = ::send(m_socket, buf, static_cast<sendrecv_len_type>(length), MSG_NOSIGNAL);
+
+ if(sent == -1)
+ {
+ if(errno == EINTR)
+ {
+ sent = 0;
+ }
+ else
+ {
+ throw CLI_Error("Socket write failed");
+ }
+ }
+
+ buf += sent;
+ length -= sent;
+ }
+ }
+ }
+
+ void tls_alert(Botan::TLS::Alert alert) override
+ {
+ output() << "Alert: " << alert.type_string() << std::endl;
+ }
+
+ std::string tls_server_choose_app_protocol(const std::vector<std::string>&) override
+ {
+ // we ignore whatever the client sends here
+ return "echo/0.1";
+ }
+
+ socket_type m_socket = invalid_socket();
+ bool m_is_tcp = false;
+ uint32_t m_socket_id = 0;
+ std::string m_line_buf;
+ std::list<std::string> m_pending_output;
+ Sandbox m_sandbox;
+ };
+
+BOTAN_REGISTER_COMMAND("tls_server", TLS_Server);
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/cli/tls_utils.cpp b/comm/third_party/botan/src/cli/tls_utils.cpp
new file mode 100644
index 0000000000..c98cbc50f4
--- /dev/null
+++ b/comm/third_party/botan/src/cli/tls_utils.cpp
@@ -0,0 +1,226 @@
+/*
+* (C) 2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "cli.h"
+
+#if defined(BOTAN_HAS_TLS) && defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
+
+#include <botan/tls_policy.h>
+#include <botan/tls_version.h>
+#include <botan/tls_messages.h>
+#include <botan/loadstor.h>
+#include <botan/hex.h>
+#include <sstream>
+
+#include "tls_helpers.h"
+
+namespace Botan_CLI {
+
+class TLS_Ciphersuites final : public Command
+ {
+ public:
+ TLS_Ciphersuites()
+ : Command("tls_ciphers --policy=default --version=tls1.2") {}
+
+ static Botan::TLS::Protocol_Version::Version_Code tls_version_from_str(const std::string& str)
+ {
+ if(str == "tls1.2" || str == "TLS1.2" || str == "TLS-1.2")
+ {
+ return Botan::TLS::Protocol_Version::TLS_V12;
+ }
+ else if(str == "tls1.1" || str == "TLS1.1" || str == "TLS-1.1")
+ {
+ return Botan::TLS::Protocol_Version::TLS_V11;
+ }
+ else if(str == "tls1.0" || str == "TLS1.1" || str == "TLS-1.1")
+ {
+ return Botan::TLS::Protocol_Version::TLS_V10;
+ }
+ if(str == "dtls1.2" || str == "DTLS1.2" || str == "DTLS-1.2")
+ {
+ return Botan::TLS::Protocol_Version::DTLS_V12;
+ }
+ else if(str == "dtls1.0" || str == "DTLS1.0" || str == "DTLS-1.0")
+ {
+ return Botan::TLS::Protocol_Version::DTLS_V10;
+ }
+ else
+ {
+ throw CLI_Error("Unknown TLS version '" + str + "'");
+ }
+ }
+
+ std::string group() const override
+ {
+ return "tls";
+ }
+
+ std::string description() const override
+ {
+ return "Lists all ciphersuites for a policy and TLS version";
+ }
+
+ void go() override
+ {
+ const std::string policy_type = get_arg("policy");
+ const Botan::TLS::Protocol_Version version(tls_version_from_str(get_arg("version")));
+ const bool with_srp = false; // fixme
+
+ auto policy = load_tls_policy(policy_type);
+
+ if(policy->acceptable_protocol_version(version) == false)
+ {
+ error_output() << "Error: the policy specified does not allow the given TLS version\n";
+ return;
+ }
+
+ for(uint16_t suite_id : policy->ciphersuite_list(version, with_srp))
+ {
+ const Botan::TLS::Ciphersuite suite(Botan::TLS::Ciphersuite::by_id(suite_id));
+ output() << suite.to_string() << "\n";
+ }
+ }
+ };
+
+BOTAN_REGISTER_COMMAND("tls_ciphers", TLS_Ciphersuites);
+
+class TLS_Client_Hello_Reader final : public Command
+ {
+ public:
+ TLS_Client_Hello_Reader()
+ : Command("tls_client_hello --hex input") {}
+
+ std::string group() const override
+ {
+ return "tls";
+ }
+
+ std::string description() const override
+ {
+ return "Parse a TLS client hello message";
+ }
+
+ void go() override
+ {
+ const std::string input_file = get_arg("input");
+ std::vector<uint8_t> input;
+
+ if(flag_set("hex"))
+ {
+ input = Botan::hex_decode(slurp_file_as_str(input_file));
+ }
+ else
+ {
+ input = slurp_file(input_file);
+ }
+
+ if(input.size() < 45)
+ {
+ error_output() << "Input too short to be valid\n";
+ return;
+ }
+
+ // Input also contains the record layer header, strip it
+ if(input[0] == 22)
+ {
+ const size_t len = Botan::make_uint16(input[3], input[4]);
+
+ if(input.size() != len + 5)
+ {
+ error_output() << "Record layer length invalid\n";
+ return;
+ }
+
+ input = std::vector<uint8_t>(input.begin() + 5, input.end());
+ }
+
+ // Assume the handshake header is there, strip it
+ if(input[0] == 1)
+ {
+ const size_t hs_len = Botan::make_uint32(0, input[1], input[2], input[3]);
+
+ if(input.size() != hs_len + 4)
+ {
+ error_output() << "Handshake layer length invalid\n";
+ return;
+ }
+
+ input = std::vector<uint8_t>(input.begin() + 4, input.end());
+ }
+
+ try
+ {
+ Botan::TLS::Client_Hello hello(input);
+
+ output() << format_hello(hello);
+ }
+ catch(std::exception& e)
+ {
+ error_output() << "Parsing client hello failed: " << e.what() << "\n";
+ }
+ }
+
+ private:
+ std::string format_hello(const Botan::TLS::Client_Hello& hello)
+ {
+ std::ostringstream oss;
+ oss << "Version: " << hello.version().to_string() << "\n"
+ << "Random: " << Botan::hex_encode(hello.random()) << "\n";
+
+ if(!hello.session_id().empty())
+ oss << "SessionID: " << Botan::hex_encode(hello.session_id()) << "\n";
+ for(uint16_t csuite_id : hello.ciphersuites())
+ {
+ auto csuite = Botan::TLS::Ciphersuite::by_id(csuite_id);
+ if(csuite.valid())
+ oss << "Cipher: " << csuite.to_string() << "\n";
+ else if(csuite_id == 0x00FF)
+ oss << "Cipher: EMPTY_RENEGOTIATION_INFO_SCSV\n";
+ else
+ oss << "Cipher: Unknown (" << std::hex << csuite_id << ")\n";
+ }
+
+ oss << "Supported signature schemes: ";
+
+ if(hello.signature_schemes().empty())
+ {
+ oss << "Did not send signature_algorithms extension\n";
+ }
+ else
+ {
+ for(Botan::TLS::Signature_Scheme scheme : hello.signature_schemes())
+ {
+ try
+ {
+ auto s = sig_scheme_to_string(scheme);
+ oss << s << " ";
+ }
+ catch(...)
+ {
+ oss << "(" << std::hex << static_cast<uint16_t>(scheme) << ") ";
+ }
+ }
+ oss << "\n";
+ }
+
+ std::map<std::string, bool> hello_flags;
+ hello_flags["ALPN"] = hello.supports_alpn();
+ hello_flags["Encrypt Then Mac"] = hello.supports_encrypt_then_mac();
+ hello_flags["Extended Master Secret"] = hello.supports_extended_master_secret();
+ hello_flags["Session Ticket"] = hello.supports_session_ticket();
+
+ for(auto&& i : hello_flags)
+ oss << "Supports " << i.first << "? " << (i.second ? "yes" : "no") << "\n";
+
+ return oss.str();
+ }
+ };
+
+BOTAN_REGISTER_COMMAND("tls_client_hello", TLS_Client_Hello_Reader);
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/cli/tss.cpp b/comm/third_party/botan/src/cli/tss.cpp
new file mode 100644
index 0000000000..0756616b2a
--- /dev/null
+++ b/comm/third_party/botan/src/cli/tss.cpp
@@ -0,0 +1,138 @@
+/*
+* (C) 2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "cli.h"
+
+#if defined(BOTAN_HAS_THRESHOLD_SECRET_SHARING)
+ #include <botan/tss.h>
+ #include <botan/hex.h>
+ #include <botan/rng.h>
+ #include <fstream>
+#endif
+
+namespace Botan_CLI {
+
+#if defined(BOTAN_HAS_THRESHOLD_SECRET_SHARING)
+
+class TSS_Split final : public Command
+ {
+ public:
+ TSS_Split() : Command("tss_split M N input --id= --share-prefix=share --share-suffix=tss --hash=SHA-256") {}
+
+ std::string group() const override
+ {
+ return "tss";
+ }
+
+ std::string description() const override
+ {
+ return "Split a secret into parts";
+ }
+
+ void go() override
+ {
+ const std::string hash_algo = get_arg("hash");
+ const std::string input = get_arg("input");
+ const std::string id_str = get_arg("id");
+ const std::string share_prefix = get_arg("share-prefix");
+ const std::string share_suffix = get_arg("share-suffix");
+ const size_t N = get_arg_sz("N");
+ const size_t M = get_arg_sz("M");
+
+ if(M <= 1 || N <= 1 || M > N || N >= 255)
+ throw CLI_Usage_Error("Invalid N/M parameters for secret splitting");
+
+ Botan::secure_vector<uint8_t> secret = slurp_file_lvec(input);
+
+ if(secret.size() > 0xFFFF)
+ throw CLI_Usage_Error("Secret is too large for this TSS format");
+
+ std::vector<uint8_t> id = Botan::hex_decode(id_str);
+
+ if(id.empty())
+ {
+ id.resize(16);
+ rng().randomize(id.data(), id.size());
+ }
+
+ std::vector<Botan::RTSS_Share> shares =
+ Botan::RTSS_Share::split(static_cast<uint8_t>(M), static_cast<uint8_t>(N),
+ secret.data(), static_cast<uint16_t>(secret.size()),
+ id, hash_algo, rng());
+
+ for(size_t i = 0; i != shares.size(); ++i)
+ {
+ const std::string share_name = share_prefix + std::to_string(i + 1) + "." + share_suffix;
+ std::ofstream out(share_name.c_str());
+ if(!out)
+ throw CLI_Error("Failed to open output file " + share_name);
+
+ out.write(reinterpret_cast<const char*>(shares[i].data().data()), shares[i].data().size());
+ }
+
+ }
+
+ private:
+ Botan::secure_vector<uint8_t> slurp_file_lvec(const std::string& input_file)
+ {
+ Botan::secure_vector<uint8_t> buf;
+ auto insert_fn = [&](const uint8_t b[], size_t l)
+ {
+ buf.insert(buf.end(), b, b + l);
+ };
+ this->read_file(input_file, insert_fn, 4096);
+ return buf;
+ }
+ };
+
+BOTAN_REGISTER_COMMAND("tss_split", TSS_Split);
+
+class TSS_Recover final : public Command
+ {
+ public:
+ TSS_Recover() : Command("tss_recover *shares") {}
+
+ std::string group() const override
+ {
+ return "tss";
+ }
+
+ std::string description() const override
+ {
+ return "Recover a split secret";
+ }
+
+ void go() override
+ {
+ const std::vector<std::string> share_names = get_arg_list("shares");
+
+ if(share_names.empty())
+ {
+ output() << help_text() << "\n";
+ this->set_return_code(1);
+ return;
+ }
+
+ std::vector<Botan::RTSS_Share> shares;
+
+ for(std::string share_fsname : get_arg_list("shares"))
+ {
+ auto v = slurp_file(share_fsname);
+ shares.push_back(Botan::RTSS_Share(v.data(), v.size()));
+ }
+
+ Botan::secure_vector<uint8_t> rec = Botan::RTSS_Share::reconstruct(shares);
+
+ output().write(Botan::cast_uint8_ptr_to_char(rec.data()), rec.size());
+ }
+ };
+
+BOTAN_REGISTER_COMMAND("tss_recover", TSS_Recover);
+
+#endif
+
+}
+
diff --git a/comm/third_party/botan/src/cli/utils.cpp b/comm/third_party/botan/src/cli/utils.cpp
new file mode 100644
index 0000000000..c6d013029a
--- /dev/null
+++ b/comm/third_party/botan/src/cli/utils.cpp
@@ -0,0 +1,391 @@
+/*
+* (C) 2009,2010,2014,2015 Jack Lloyd
+* (C) 2017 René Korthaus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "cli.h"
+
+#include <botan/version.h>
+#include <botan/cpuid.h>
+#include <botan/internal/stl_util.h>
+#include <botan/internal/os_utils.h>
+#include <sstream>
+#include <iomanip>
+
+#if defined(BOTAN_HAS_HTTP_UTIL)
+ #include <botan/http_util.h>
+#endif
+
+#if defined(BOTAN_HAS_UUID)
+ #include <botan/uuid.h>
+#endif
+
+namespace Botan_CLI {
+
+class Print_Help final : public Command
+ {
+ public:
+ Print_Help() : Command("help") {}
+
+ std::string help_text() const override
+ {
+ std::map<std::string, std::vector<std::unique_ptr<Command>>> grouped_commands;
+
+ auto reg_commands = Command::registered_cmds();
+ for(const auto& cmd_name : reg_commands)
+ {
+ auto cmd = Command::get_cmd(cmd_name);
+ if(cmd)
+ {
+ grouped_commands[cmd->group()].push_back(std::move(cmd));
+ }
+ }
+
+ const std::map<std::string, std::string> groups_description {
+ { "encryption", "Encryption" },
+ { "compression", "Compression" },
+ { "codec", "Encoders/Decoders" },
+ { "hash", "Hash Functions" },
+ { "hmac", "HMAC" },
+ { "info", "Informational" },
+ { "numtheory", "Number Theory" },
+ { "passhash", "Password Hashing" },
+ { "psk", "PSK Database" },
+ { "pubkey", "Public Key Cryptography" },
+ { "tls", "TLS" },
+ { "tss", "Secret Sharing" },
+ { "x509", "X.509" },
+ { "misc", "Miscellaneous" }
+ };
+
+ std::ostringstream oss;
+
+ oss << "Usage: botan <cmd> <cmd-options>\n";
+ oss << "All commands support --verbose --help --output= --error-output= --rng-type= --drbg-seed=\n\n";
+ oss << "Available commands:\n\n";
+
+ for(const auto& commands : grouped_commands)
+ {
+ std::string desc = commands.first;
+ if(desc.empty())
+ {
+ continue;
+ }
+
+ oss << Botan::search_map(groups_description, desc, desc) << ":\n";
+ for(auto& cmd : commands.second)
+ {
+ oss << " " << std::setw(16) << std::left << cmd->cmd_name() << " " << cmd->description() << "\n";
+ }
+ oss << "\n";
+ }
+
+ return oss.str();
+ }
+
+ std::string group() const override
+ {
+ return "";
+ }
+
+ std::string description() const override
+ {
+ return "Prints a help string";
+ }
+
+ void go() override
+ {
+ this->set_return_code(1);
+ output() << help_text();
+ }
+ };
+
+BOTAN_REGISTER_COMMAND("help", Print_Help);
+
+class Has_Command final : public Command
+ {
+ public:
+ Has_Command() : Command("has_command cmd") {}
+
+ std::string group() const override
+ {
+ return "info";
+ }
+
+ std::string description() const override
+ {
+ return "Test if a command is available";
+ }
+
+ void go() override
+ {
+ const std::string cmd = get_arg("cmd");
+
+ bool exists = false;
+ for(auto registered_cmd : Command::registered_cmds())
+ {
+ if(cmd == registered_cmd)
+ {
+ exists = true;
+ break;
+ }
+ }
+
+ if(verbose())
+ {
+ output() << "Command '" << cmd << "' is "
+ << (exists ? "": "not ") << "available\n";
+ }
+
+ if(exists == false)
+ this->set_return_code(1);
+ }
+ };
+
+BOTAN_REGISTER_COMMAND("has_command", Has_Command);
+
+class Config_Info final : public Command
+ {
+ public:
+ Config_Info() : Command("config info_type") {}
+
+ std::string help_text() const override
+ {
+ return "Usage: config info_type\n"
+ " prefix: Print install prefix\n"
+ " cflags: Print include params\n"
+ " ldflags: Print linker params\n"
+ " libs: Print libraries\n";
+ }
+
+ std::string group() const override
+ {
+ return "info";
+ }
+
+ std::string description() const override
+ {
+ return "Print the used prefix, cflags, ldflags or libs";
+ }
+
+ void go() override
+ {
+ const std::string arg = get_arg("info_type");
+
+ if(arg == "prefix")
+ {
+ output() << BOTAN_INSTALL_PREFIX << "\n";
+ }
+ else if(arg == "cflags")
+ {
+ output() << "-I" << BOTAN_INSTALL_PREFIX << "/" << BOTAN_INSTALL_HEADER_DIR << "\n";
+ }
+ else if(arg == "ldflags")
+ {
+ if(*BOTAN_LINK_FLAGS)
+ output() << BOTAN_LINK_FLAGS << ' ';
+ output() << "-L" << BOTAN_INSTALL_LIB_DIR << "\n";
+ }
+ else if(arg == "libs")
+ {
+ output() << "-lbotan-" << Botan::version_major() << " " << BOTAN_LIB_LINK << "\n";
+ }
+ else
+ {
+ throw CLI_Usage_Error("Unknown option to botan config " + arg);
+ }
+ }
+ };
+
+BOTAN_REGISTER_COMMAND("config", Config_Info);
+
+class Version_Info final : public Command
+ {
+ public:
+ Version_Info() : Command("version --full") {}
+
+ std::string group() const override
+ {
+ return "info";
+ }
+
+ std::string description() const override
+ {
+ return "Print version info";
+ }
+
+ void go() override
+ {
+ if(flag_set("full"))
+ {
+ output() << Botan::version_string() << "\n";
+ }
+ else
+ {
+ output() << Botan::short_version_string() << "\n";
+ }
+ }
+ };
+
+BOTAN_REGISTER_COMMAND("version", Version_Info);
+
+class Print_Cpuid final : public Command
+ {
+ public:
+ Print_Cpuid() : Command("cpuid") {}
+
+ std::string group() const override
+ {
+ return "info";
+ }
+
+ std::string description() const override
+ {
+ return "List available processor flags (aes_ni, SIMD extensions, ...)";
+ }
+
+ void go() override
+ {
+ output() << "CPUID flags: " << Botan::CPUID::to_string() << "\n";
+ }
+ };
+
+BOTAN_REGISTER_COMMAND("cpuid", Print_Cpuid);
+
+class Cycle_Counter final : public Command
+ {
+ public:
+ Cycle_Counter() : Command("cpu_clock --test-duration=500") {}
+
+ std::string group() const override
+ {
+ return "info";
+ }
+
+ std::string description() const override
+ {
+ return "Estimate the speed of the CPU cycle counter";
+ }
+
+ void go() override
+ {
+ if(Botan::OS::get_cpu_cycle_counter() == 0)
+ {
+ output() << "No CPU cycle counter on this machine\n";
+ return;
+ }
+
+ const uint64_t test_duration_ns = get_arg_sz("test-duration") * 1000000;
+
+ if(test_duration_ns == 0)
+ {
+ output() << "Invalid test duration\n";
+ return;
+ }
+
+ const uint64_t cc_start = Botan::OS::get_cpu_cycle_counter();
+ const uint64_t ns_start = Botan::OS::get_system_timestamp_ns();
+
+ uint64_t cc_end = 0;
+ uint64_t ns_end = ns_start;
+
+ while((ns_end - ns_start) < test_duration_ns)
+ {
+ ns_end = Botan::OS::get_system_timestamp_ns();
+ cc_end = Botan::OS::get_cpu_cycle_counter();
+ }
+
+ if(cc_end <= cc_start)
+ {
+ output() << "Cycle counter seems to have wrapped, try again\n";
+ return;
+ }
+
+ if(ns_end <= ns_start)
+ {
+ output() << "System clock seems to have wrapped (?!?)\n";
+ return;
+ }
+
+ const uint64_t ns_duration = ns_end - ns_start;
+ const uint64_t cc_duration = cc_end - cc_start;
+
+ const double ratio = static_cast<double>(cc_duration) / ns_duration;
+
+ if(ratio >= 1.0)
+ {
+ // GHz
+ output() << "Estimated CPU clock " << std::setprecision(2) << ratio << " GHz\n";
+ }
+ else
+ {
+ // MHz
+ output() << "Estimated CPU clock " << static_cast<size_t>(ratio * 1000) << " MHz\n";
+ }
+ }
+ };
+
+BOTAN_REGISTER_COMMAND("cpu_clock", Cycle_Counter);
+
+#if defined(BOTAN_HAS_UUID)
+
+class Print_UUID final : public Command
+ {
+ public:
+ Print_UUID() : Command("uuid") {}
+
+ std::string group() const override
+ {
+ return "misc";
+ }
+
+ std::string description() const override
+ {
+ return "Print a random UUID";
+ }
+
+ void go() override
+ {
+ Botan::UUID uuid(rng());
+ output() << uuid.to_string() << "\n";
+ }
+ };
+
+BOTAN_REGISTER_COMMAND("uuid", Print_UUID);
+
+#endif
+
+#if defined(BOTAN_HAS_HTTP_UTIL)
+
+class HTTP_Get final : public Command
+ {
+ public:
+ HTTP_Get() : Command("http_get --redirects=1 --timeout=3000 url") {}
+
+ std::string group() const override
+ {
+ return "misc";
+ }
+
+ std::string description() const override
+ {
+ return "Retrieve resource from the passed http/https url";
+ }
+
+ void go() override
+ {
+ const std::string url = get_arg("url");
+ const std::chrono::milliseconds timeout(get_arg_sz("timeout"));
+ const size_t redirects = get_arg_sz("redirects");
+
+ output() << Botan::HTTP::GET_sync(url, redirects, timeout) << "\n";
+ }
+ };
+
+BOTAN_REGISTER_COMMAND("http_get", HTTP_Get);
+
+#endif // http_util
+
+}
diff --git a/comm/third_party/botan/src/cli/x509.cpp b/comm/third_party/botan/src/cli/x509.cpp
new file mode 100644
index 0000000000..a92ec1309e
--- /dev/null
+++ b/comm/third_party/botan/src/cli/x509.cpp
@@ -0,0 +1,417 @@
+/*
+* (C) 2010,2014,2015,2018 Jack Lloyd
+* (C) 2017 René Korthaus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "cli.h"
+
+#if defined(BOTAN_HAS_X509_CERTIFICATES) && defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
+
+#include <botan/certstor.h>
+#include <botan/pk_keys.h>
+#include <botan/pkcs8.h>
+#include <botan/x509_ca.h>
+#include <botan/x509cert.h>
+#include <botan/x509path.h>
+#include <botan/x509self.h>
+#include <botan/data_src.h>
+#include <botan/parsing.h>
+
+#if defined(BOTAN_HAS_OCSP)
+ #include <botan/ocsp.h>
+#endif
+
+#if defined(BOTAN_HAS_CERTSTOR_SYSTEM)
+ #include <botan/certstor_system.h>
+#endif
+
+namespace Botan_CLI {
+
+#if defined(BOTAN_HAS_CERTSTOR_SYSTEM)
+
+class Trust_Root_Info final : public Command
+ {
+ public:
+ Trust_Root_Info() : Command("trust_roots --dn --dn-only --display") {}
+
+ std::string group() const override
+ {
+ return "x509";
+ }
+
+ std::string description() const override
+ {
+ return "List certs in the system trust store";
+ }
+
+ void go() override
+ {
+ Botan::System_Certificate_Store trust_roots;
+
+ const auto dn_list = trust_roots.all_subjects();
+
+ if(flag_set("dn-only"))
+ {
+ for(auto dn : dn_list)
+ output() << dn << "\n";
+ }
+ else
+ {
+ for(auto dn : dn_list)
+ {
+ // Some certstores have more than one cert with a particular DN
+ for(auto cert : trust_roots.find_all_certs(dn, std::vector<uint8_t>()))
+ {
+ if(flag_set("dn"))
+ output() << "# " << dn << "\n";
+
+ if(flag_set("display"))
+ output() << cert->to_string() << "\n";
+
+ output() << cert->PEM_encode() << "\n";
+ }
+ }
+
+ }
+ }
+
+ };
+
+BOTAN_REGISTER_COMMAND("trust_roots", Trust_Root_Info);
+
+#endif
+
+class Sign_Cert final : public Command
+ {
+ public:
+ Sign_Cert()
+ : Command("sign_cert --ca-key-pass= --hash=SHA-256 "
+ "--duration=365 --emsa= 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 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");
+ const std::string hash = get_arg("hash");
+
+ std::unique_ptr<Botan::Private_Key> key;
+ if(!pass.empty())
+ {
+ key.reset(Botan::PKCS8::load_key(key_file, rng(), pass));
+ }
+ else
+ {
+ key.reset(Botan::PKCS8::load_key(key_file, rng()));
+ }
+
+ if(!key)
+ {
+ throw CLI_Error("Failed to load key from " + key_file);
+ }
+
+ std::map<std::string, std::string> options;
+ if(emsa.empty() == false)
+ options["padding"] = emsa;
+
+ Botan::X509_CA ca(ca_cert, *key, options, hash, rng());
+
+ Botan::PKCS10_Request req(get_arg("pkcs10_req"));
+
+ auto now = std::chrono::system_clock::now();
+
+ Botan::X509_Time start_time(now);
+
+ typedef std::chrono::duration<int, std::ratio<86400>> days;
+
+ 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);
+
+ output() << new_cert.PEM_encode();
+ }
+ };
+
+BOTAN_REGISTER_COMMAND("sign_cert", Sign_Cert);
+
+class Cert_Info final : public Command
+ {
+ public:
+ Cert_Info() : Command("cert_info --fingerprint file") {}
+
+ std::string group() const override
+ {
+ return "x509";
+ }
+
+ std::string description() const override
+ {
+ return "Parse X.509 certificate and display data fields";
+ }
+
+ void go() override
+ {
+ const std::string arg_file = get_arg("file");
+
+ std::vector<uint8_t> data = slurp_file(get_arg("file"));
+
+ Botan::DataSource_Memory in(data);
+
+ while(!in.end_of_data())
+ {
+ try
+ {
+ Botan::X509_Certificate cert(in);
+
+ try
+ {
+ output() << cert.to_string() << std::endl;
+ }
+ 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;
+ }
+ catch(Botan::Exception& e)
+ {
+ if(!in.end_of_data())
+ {
+ output() << "X509_Certificate parsing failed " << e.what() << "\n";
+ }
+ }
+ }
+ }
+ };
+
+BOTAN_REGISTER_COMMAND("cert_info", Cert_Info);
+
+#if defined(BOTAN_HAS_OCSP) && defined(BOTAN_HAS_HTTP_UTIL)
+
+class OCSP_Check final : public Command
+ {
+ public:
+ OCSP_Check() : Command("ocsp_check --timeout=3000 subject issuer") {}
+
+ std::string group() const override
+ {
+ return "x509";
+ }
+
+ std::string description() const override
+ {
+ return "Verify an X.509 certificate against the issuers OCSP responder";
+ }
+
+ 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"));
+
+ Botan::Certificate_Store_In_Memory cas;
+ cas.add_certificate(issuer);
+ Botan::OCSP::Response resp = Botan::OCSP::online_check(issuer, subject, &cas, timeout);
+
+ auto status = resp.status_for(issuer, subject, std::chrono::system_clock::now());
+
+ if(status == Botan::Certificate_Status_Code::OCSP_RESPONSE_GOOD)
+ {
+ output() << "OCSP check OK\n";
+ }
+ else
+ {
+ output() << "OCSP check failed " << Botan::Path_Validation_Result::status_string(status) << "\n";
+ }
+ }
+ };
+
+BOTAN_REGISTER_COMMAND("ocsp_check", OCSP_Check);
+
+#endif // OCSP && HTTP
+
+class Cert_Verify final : public Command
+ {
+ public:
+ Cert_Verify() : Command("cert_verify subject *ca_certs") {}
+
+ std::string group() const override
+ {
+ return "x509";
+ }
+
+ std::string description() const override
+ {
+ return "Verify if the passed X.509 certificate passes path validation";
+ }
+
+ void go() override
+ {
+ Botan::X509_Certificate subject_cert(get_arg("subject"));
+ Botan::Certificate_Store_In_Memory trusted;
+
+ for(auto const& certfile : get_arg_list("ca_certs"))
+ {
+ trusted.add_certificate(Botan::X509_Certificate(certfile));
+ }
+
+ Botan::Path_Validation_Restrictions restrictions;
+
+ Botan::Path_Validation_Result result =
+ Botan::x509_path_validate(subject_cert,
+ restrictions,
+ trusted);
+
+ if(result.successful_validation())
+ {
+ output() << "Certificate passes validation checks\n";
+ }
+ else
+ {
+ output() << "Certificate did not validate - " << result.result_string() << "\n";
+ }
+ }
+ };
+
+BOTAN_REGISTER_COMMAND("cert_verify", Cert_Verify);
+
+class Gen_Self_Signed final : public Command
+ {
+ public:
+ Gen_Self_Signed()
+ : Command("gen_self_signed key CN --country= --dns= "
+ "--organization= --email= --path-limit=1 --days=365 --key-pass= --ca --hash=SHA-256 --emsa= --der") {}
+
+ std::string group() const override
+ {
+ return "x509";
+ }
+
+ std::string description() const override
+ {
+ return "Generate a self signed X.509 certificate";
+ }
+
+ void go() override
+ {
+ const std::string key_file = get_arg("key");
+ const std::string passphrase = get_passphrase_arg("Passphrase for " + key_file, "key-pass");
+ std::unique_ptr<Botan::Private_Key> key(Botan::PKCS8::load_key(key_file, rng(), passphrase));
+
+ if(!key)
+ {
+ throw CLI_Error("Failed to load key from " + get_arg("key"));
+ }
+
+ const uint32_t lifetime = static_cast<uint32_t>(get_arg_sz("days") * 24 * 60 * 60);
+
+ Botan::X509_Cert_Options opts("", lifetime);
+
+ opts.common_name = get_arg("CN");
+ opts.country = get_arg("country");
+ opts.organization = get_arg("organization");
+ opts.email = get_arg("email");
+ opts.more_dns = Botan::split_on(get_arg("dns"), ',');
+ const bool der_format = flag_set("der");
+
+ std::string emsa = get_arg("emsa");
+
+ if(emsa.empty() == false)
+ opts.set_padding_scheme(emsa);
+
+ 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());
+
+ if(der_format)
+ {
+ auto der = cert.BER_encode();
+ output().write(reinterpret_cast<const char*>(der.data()), der.size());
+ }
+ else
+ output() << cert.PEM_encode();
+ }
+ };
+
+BOTAN_REGISTER_COMMAND("gen_self_signed", Gen_Self_Signed);
+
+class Generate_PKCS10 final : public Command
+ {
+ public:
+ Generate_PKCS10()
+ : Command("gen_pkcs10 key CN --country= --organization= "
+ "--ca --path-limit=1 --email= --dns= --ext-ku= --key-pass= --hash=SHA-256 --emsa=") {}
+
+ std::string group() const override
+ {
+ return "x509";
+ }
+
+ std::string description() const override
+ {
+ return "Generate a PKCS #10 certificate signing request (CSR)";
+ }
+
+ void go() override
+ {
+ std::unique_ptr<Botan::Private_Key> key(Botan::PKCS8::load_key(get_arg("key"), rng(), get_arg("key-pass")));
+
+ if(!key)
+ {
+ throw CLI_Error("Failed to load key from " + get_arg("key"));
+ }
+
+ Botan::X509_Cert_Options opts;
+
+ opts.common_name = get_arg("CN");
+ opts.country = get_arg("country");
+ opts.organization = get_arg("organization");
+ opts.email = get_arg("email");
+ opts.more_dns = Botan::split_on(get_arg("dns"), ',');
+
+ if(flag_set("ca"))
+ {
+ opts.CA_key(get_arg_sz("path-limit"));
+ }
+
+ for(std::string ext_ku : Botan::split_on(get_arg("ext-ku"), ','))
+ {
+ opts.add_ex_constraint(ext_ku);
+ }
+
+ std::string emsa = get_arg("emsa");
+
+ if(emsa.empty() == false)
+ opts.set_padding_scheme(emsa);
+
+ Botan::PKCS10_Request req = Botan::X509::create_cert_req(opts, *key, get_arg("hash"), rng());
+
+ output() << req.PEM_encode();
+ }
+ };
+
+BOTAN_REGISTER_COMMAND("gen_pkcs10", Generate_PKCS10);
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/configs/astyle.rc b/comm/third_party/botan/src/configs/astyle.rc
new file mode 100644
index 0000000000..dd2c7a35db
--- /dev/null
+++ b/comm/third_party/botan/src/configs/astyle.rc
@@ -0,0 +1,14 @@
+--style=whitesmith
+--indent=spaces=3
+--attach-namespaces
+--break-one-line-headers
+--convert-tabs
+--max-code-length=120
+--indent-preproc-block
+#--pad-oper
+--pad-comma
+--unpad-paren
+--align-pointer=type
+--align-reference=type
+-j # --add-braces / --add-brackets
+-J # --add-one-line-braces / --add-one-line-brackets
diff --git a/comm/third_party/botan/src/configs/coverage.rc b/comm/third_party/botan/src/configs/coverage.rc
new file mode 100644
index 0000000000..d93af43e2e
--- /dev/null
+++ b/comm/third_party/botan/src/configs/coverage.rc
@@ -0,0 +1,18 @@
+# .coveragerc to control coverage.py
+[run]
+branch = True
+
+[report]
+# Regexes for lines to exclude from consideration
+exclude_lines =
+ # Have to re-enable the standard pragma
+ pragma: no cover
+
+ # Don't complain if non-runnable code isn't run:
+ if 0:
+ def main
+ if __name__ == .__main__.:
+
+ # Exclude tests
+ def test
+
diff --git a/comm/third_party/botan/src/configs/eclipse.xml b/comm/third_party/botan/src/configs/eclipse.xml
new file mode 100644
index 0000000000..3fff4b0968
--- /dev/null
+++ b/comm/third_party/botan/src/configs/eclipse.xml
@@ -0,0 +1,167 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<profiles version="1">
+<profile kind="CodeFormatterProfile" name="Botan" version="1">
+<setting id="org.eclipse.cdt.core.formatter.insert_space_before_opening_paren_in_method_declaration" value="do not insert"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_after_opening_paren_in_for" value="do not insert"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_new_line_in_empty_block" value="insert"/>
+<setting id="org.eclipse.cdt.core.formatter.lineSplit" value="80"/>
+<setting id="org.eclipse.cdt.core.formatter.alignment_for_member_access" value="0"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_before_comma_in_base_types" value="do not insert"/>
+<setting id="org.eclipse.cdt.core.formatter.keep_else_statement_on_same_line" value="false"/>
+<setting id="org.eclipse.cdt.core.formatter.indent_switchstatements_compare_to_switch" value="false"/>
+<setting id="org.eclipse.cdt.core.formatter.alignment_for_constructor_initializer_list" value="0"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_after_opening_brace_in_array_initializer" value="insert"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters" value="do not insert"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_after_opening_paren_in_if" value="do not insert"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression" value="do not insert"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_after_opening_paren_in_exception_specification" value="do not insert"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_after_comma_in_base_types" value="insert"/>
+<setting id="org.eclipse.cdt.core.formatter.indent_body_declarations_compare_to_access_specifier" value="true"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_before_closing_paren_in_exception_specification" value="do not insert"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_after_comma_in_template_arguments" value="insert"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_before_opening_brace_in_block" value="insert"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_before_closing_paren_in_method_declaration" value="do not insert"/>
+<setting id="org.eclipse.cdt.core.formatter.use_tabs_only_for_leading_indentations" value="false"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_before_colon_in_labeled_statement" value="do not insert"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_after_colon_in_case" value="insert"/>
+<setting id="org.eclipse.cdt.core.formatter.comment.min_distance_between_code_and_line_comment" value="1"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_after_comma_in_array_initializer" value="insert"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_after_comma_in_enum_declarations" value="insert"/>
+<setting id="org.eclipse.cdt.core.formatter.alignment_for_expressions_in_array_initializer" value="48"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_after_comma_in_declarator_list" value="insert"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_before_opening_bracket" value="do not insert"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_before_closing_paren_in_for" value="do not insert"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_before_prefix_operator" value="do not insert"/>
+<setting id="org.eclipse.cdt.core.formatter.tabulation.size" value="3"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_new_line_before_else_in_if_statement" value="insert"/>
+<setting id="org.eclipse.cdt.core.formatter.alignment_for_enumerator_list" value="49"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression" value="do not insert"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_between_empty_parens_in_method_declaration" value="do not insert"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_before_closing_paren_in_switch" value="do not insert"/>
+<setting id="org.eclipse.cdt.core.formatter.alignment_for_declarator_list" value="16"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression" value="do not insert"/>
+<setting id="org.eclipse.cdt.core.formatter.indent_empty_lines" value="false"/>
+<setting id="org.eclipse.cdt.core.formatter.indent_switchstatements_compare_to_cases" value="true"/>
+<setting id="org.eclipse.cdt.core.formatter.keep_empty_array_initializer_on_one_line" value="false"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_before_opening_brace_in_method_declaration" value="insert"/>
+<setting id="org.eclipse.cdt.core.formatter.put_empty_statement_on_new_line" value="true"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_before_opening_brace_in_switch" value="insert"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_before_closing_paren_in_cast" value="do not insert"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_between_empty_braces_in_array_initializer" value="do not insert"/>
+<setting id="org.eclipse.cdt.core.formatter.brace_position_for_method_declaration" value="next_line_shifted"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_before_closing_paren_in_while" value="do not insert"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_after_question_in_conditional" value="insert"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_before_semicolon" value="do not insert"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_after_closing_angle_bracket_in_template_arguments" value="insert"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_before_colon_in_base_clause" value="do not insert"/>
+<setting id="org.eclipse.cdt.core.formatter.indent_breaks_compare_to_cases" value="true"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_before_unary_operator" value="do not insert"/>
+<setting id="org.eclipse.cdt.core.formatter.join_wrapped_lines" value="true"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_before_comma_in_declarator_list" value="do not insert"/>
+<setting id="org.eclipse.cdt.core.formatter.alignment_for_arguments_in_method_invocation" value="16"/>
+<setting id="org.eclipse.cdt.core.formatter.comment.never_indent_line_comments_on_first_column" value="true"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_before_opening_paren_in_while" value="insert"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_between_empty_brackets" value="do not insert"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_after_opening_bracket" value="do not insert"/>
+<setting id="org.eclipse.cdt.core.formatter.alignment_for_parameters_in_method_declaration" value="16"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer" value="insert"/>
+<setting id="org.eclipse.cdt.core.formatter.number_of_empty_lines_to_preserve" value="1"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_after_opening_paren_in_method_invocation" value="do not insert"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_before_closing_brace_in_array_initializer" value="insert"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_before_semicolon_in_for" value="do not insert"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_before_colon_in_conditional" value="insert"/>
+<setting id="org.eclipse.cdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments" value="true"/>
+<setting id="org.eclipse.cdt.core.formatter.brace_position_for_block" value="next_line_shifted"/>
+<setting id="org.eclipse.cdt.core.formatter.brace_position_for_type_declaration" value="next_line_shifted"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_before_assignment_operator" value="insert"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_before_opening_angle_bracket_in_template_arguments" value="do not insert"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_before_comma_in_expression_list" value="do not insert"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_after_opening_angle_bracket_in_template_parameters" value="do not insert"/>
+<setting id="org.eclipse.cdt.core.formatter.continuation_indentation" value="2"/>
+<setting id="org.eclipse.cdt.core.formatter.alignment_for_expression_list" value="0"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_after_opening_paren_in_method_declaration" value="do not insert"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_before_comma_in_template_parameters" value="do not insert"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_before_colon_in_default" value="do not insert"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_after_binary_operator" value="insert"/>
+<setting id="org.eclipse.cdt.core.formatter.alignment_for_conditional_expression" value="34"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_between_empty_parens_in_method_invocation" value="do not insert"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_before_comma_in_array_initializer" value="do not insert"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_before_closing_paren_in_if" value="do not insert"/>
+<setting id="org.eclipse.cdt.core.formatter.format_guardian_clause_on_one_line" value="false"/>
+<setting id="org.eclipse.cdt.core.formatter.indent_access_specifier_extra_spaces" value="0"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_after_opening_paren_in_cast" value="do not insert"/>
+<setting id="org.eclipse.cdt.core.formatter.indent_access_specifier_compare_to_type_header" value="false"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_before_opening_brace_in_type_declaration" value="insert"/>
+<setting id="org.eclipse.cdt.core.formatter.continuation_indentation_for_array_initializer" value="2"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_after_colon_in_labeled_statement" value="insert"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters" value="insert"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_after_semicolon_in_for" value="insert"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_before_closing_paren_in_method_invocation" value="do not insert"/>
+<setting id="org.eclipse.cdt.core.formatter.indent_body_declarations_compare_to_namespace_header" value="false"/>
+<setting id="org.eclipse.cdt.core.formatter.alignment_for_compact_if" value="16"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_after_assignment_operator" value="insert"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_after_closing_brace_in_block" value="insert"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_before_opening_brace_in_array_initializer" value="insert"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_new_line_at_end_of_file_if_missing" value="do not insert"/>
+<setting id="org.eclipse.cdt.core.formatter.alignment_for_assignment" value="16"/>
+<setting id="org.eclipse.cdt.core.formatter.alignment_for_conditional_expression_chain" value="18"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_after_comma_in_template_parameters" value="insert"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_after_comma_in_expression_list" value="insert"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_before_question_in_conditional" value="insert"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_before_opening_paren_in_exception_specification" value="insert"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_before_binary_operator" value="insert"/>
+<setting id="org.eclipse.cdt.core.formatter.alignment_for_base_clause_in_type_declaration" value="80"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_new_line_before_identifier_in_function_declaration" value="do not insert"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_before_comma_in_method_declaration_throws" value="do not insert"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_between_empty_parens_in_exception_specification" value="do not insert"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments" value="do not insert"/>
+<setting id="org.eclipse.cdt.core.formatter.indent_declaration_compare_to_template_header" value="false"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_after_unary_operator" value="do not insert"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_before_opening_paren_in_switch" value="insert"/>
+<setting id="org.eclipse.cdt.core.formatter.indent_statements_compare_to_body" value="false"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_after_comma_in_method_declaration_throws" value="insert"/>
+<setting id="org.eclipse.cdt.core.formatter.alignment_for_binary_expression" value="16"/>
+<setting id="org.eclipse.cdt.core.formatter.indent_statements_compare_to_block" value="false"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_before_comma_in_template_arguments" value="do not insert"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_new_line_before_catch_in_try_statement" value="insert"/>
+<setting id="org.eclipse.cdt.core.formatter.alignment_for_throws_clause_in_method_declaration" value="16"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_before_opening_paren_in_method_invocation" value="do not insert"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_before_closing_paren_in_catch" value="do not insert"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_after_closing_paren_in_cast" value="insert"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_before_opening_angle_bracket_in_template_parameters" value="do not insert"/>
+<setting id="org.eclipse.cdt.core.formatter.tabulation.char" value="space"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_before_closing_angle_bracket_in_template_parameters" value="do not insert"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_new_line_before_colon_in_constructor_initializer_list" value="do not insert"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_after_opening_paren_in_while" value="do not insert"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments" value="insert"/>
+<setting id="org.eclipse.cdt.core.formatter.brace_position_for_block_in_case" value="next_line_shifted"/>
+<setting id="org.eclipse.cdt.core.formatter.compact_else_if" value="true"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_after_postfix_operator" value="do not insert"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_new_line_after_template_declaration" value="do not insert"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_after_colon_in_base_clause" value="insert"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_after_opening_paren_in_catch" value="do not insert"/>
+<setting id="org.eclipse.cdt.core.formatter.keep_then_statement_on_same_line" value="false"/>
+<setting id="org.eclipse.cdt.core.formatter.brace_position_for_switch" value="next_line_shifted"/>
+<setting id="org.eclipse.cdt.core.formatter.alignment_for_overloaded_left_shift_chain" value="16"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_before_opening_paren_in_if" value="insert"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_after_opening_paren_in_switch" value="do not insert"/>
+<setting id="org.eclipse.cdt.core.formatter.keep_imple_if_on_one_line" value="false"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer" value="insert"/>
+<setting id="org.eclipse.cdt.core.formatter.indentation.size" value="8"/>
+<setting id="org.eclipse.cdt.core.formatter.brace_position_for_namespace_declaration" value="next_line_shifted"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_after_colon_in_conditional" value="insert"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_before_comma_in_enum_declarations" value="do not insert"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_after_prefix_operator" value="do not insert"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_before_closing_angle_bracket_in_template_arguments" value="do not insert"/>
+<setting id="org.eclipse.cdt.core.formatter.brace_position_for_array_initializer" value="next_line_shifted"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_before_colon_in_case" value="do not insert"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_before_opening_paren_in_catch" value="insert"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_before_opening_brace_in_namespace_declaration" value="insert"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_before_postfix_operator" value="do not insert"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_before_closing_bracket" value="do not insert"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_new_line_before_while_in_do_statement" value="insert"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_before_opening_paren_in_for" value="insert"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_after_closing_angle_bracket_in_template_parameters" value="insert"/>
+<setting id="org.eclipse.cdt.core.formatter.insert_space_after_opening_angle_bracket_in_template_arguments" value="do not insert"/>
+</profile>
+</profiles>
diff --git a/comm/third_party/botan/src/configs/indent.el b/comm/third_party/botan/src/configs/indent.el
new file mode 100644
index 0000000000..7fa2540b0d
--- /dev/null
+++ b/comm/third_party/botan/src/configs/indent.el
@@ -0,0 +1,55 @@
+; This Emacs Lips code defines the indentation style used in Botan. If doesn't
+; get everything perfectly correct, but it's pretty close. Copy this code into
+; your .emacs file, or use M-x eval-buffer. Make sure to also set
+; indent-tabs-mode to nil so spaces are inserted instead.
+;
+; This style is basically Whitesmiths style with 3 space indents (the Emacs
+; "whitesmith" style seems more like a weird Whitesmiths/Allman mutant style).
+;
+; To activate using this style, open the file you want to edit and run this:
+; M-x c-set-style <RET> and then enter "botan".
+
+(setq botan-style '(
+ (c-basic-offset . 3)
+ (c-comment-only-line-offset . 0)
+ (c-offsets-alist
+ (c . 0)
+ (comment-intro . 0)
+
+ (statement-block-intro . 0)
+ (statement-cont . +)
+
+ (substatement . +)
+ (substatement-open . +)
+
+ (block-open . +)
+ (block-close . 0)
+
+ (defun-open . +)
+ (defun-close . 0)
+ (defun-block-intro . 0)
+ (func-decl-cont . +)
+
+ (class-open . +)
+ (class-close . +)
+ (inclass . +)
+ (access-label . -)
+ (inline-open . +)
+ (inline-close . 0)
+
+ (extern-lang-open . 0)
+ (extern-lang-close . 0)
+ (inextern-lang . 0)
+
+ (statement-case-open +)
+
+ (namespace-open . 0)
+ (namespace-close . 0)
+ (innamespace . 0)
+
+ (label . 0)
+ )
+))
+
+(add-hook 'c++-mode-common-hook
+ (function (lambda () (c-add-style "botan" botan-style nil))))
diff --git a/comm/third_party/botan/src/configs/pylint.rc b/comm/third_party/botan/src/configs/pylint.rc
new file mode 100644
index 0000000000..dd7e907474
--- /dev/null
+++ b/comm/third_party/botan/src/configs/pylint.rc
@@ -0,0 +1,379 @@
+[MASTER]
+
+# Specify a configuration file.
+#rcfile=
+
+# Python code to execute, usually for sys.path manipulation such as
+# pygtk.require().
+#init-hook=
+
+# Add files or directories to the blacklist. They should be base names, not
+# paths.
+ignore=CVS
+
+# Pickle collected data for later comparisons.
+persistent=yes
+
+# List of plugins (as comma separated values of python modules names) to load,
+# usually to register additional checkers.
+load-plugins=
+
+# Use multiple processes to speed up Pylint.
+jobs=3
+
+# Allow loading of arbitrary C extensions. Extensions are imported into the
+# active Python interpreter and may run arbitrary code.
+unsafe-load-any-extension=no
+
+# A comma-separated list of package or module names from where C extensions may
+# be loaded. Extensions are loading into the active Python interpreter and may
+# run arbitrary code
+extension-pkg-whitelist=
+
+# Allow optimization of some AST trees. This will activate a peephole AST
+# optimizer, which will apply various small optimizations. For instance, it can
+# be used to obtain the result of joining multiple strings with the addition
+# operator. Joining a lot of strings can lead to a maximum recursion error in
+# Pylint and this flag can prevent that. It has one side effect, the resulting
+# AST will be different than the one from reality.
+optimize-ast=no
+
+
+[MESSAGES CONTROL]
+
+# Only show warnings with the listed confidence levels. Leave empty to show
+# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED
+confidence=
+
+# Enable the message, report, category or checker with the given id(s). You can
+# either give multiple identifier separated by comma (,) or put this option
+# multiple time. See also the "--disable" option for examples.
+#enable=
+
+# Disable the message, report, category or checker with the given id(s). You
+# can either give multiple identifiers separated by comma (,) or put this
+# option multiple times (only on the command line, not in the configuration
+# file where it should appear only once).You can also use "--disable=all" to
+# disable everything first and then reenable specific checks. For example, if
+# you want to run only the similarities checker, you can use "--disable=all
+# --enable=similarities". If you want to run only the classes checker, but have
+# no Warning level messages displayed, use"--disable=all --enable=classes
+# --disable=W"
+
+disable=missing-docstring,no-else-return,logging-not-lazy,locally-disabled,import-outside-toplevel,super-with-arguments,raise-missing-from
+
+
+[REPORTS]
+
+# Set the output format. Available formats are text, parseable, colorized, msvs
+# (visual studio) and html. You can also give a reporter class, eg
+# mypackage.mymodule.MyReporterClass.
+output-format=text
+
+# Put messages in a separate file for each module / package specified on the
+# command line instead of printing them on stdout. Reports (if any) will be
+# written in a file name "pylint_global.[txt|html]".
+files-output=no
+
+# Tells whether to display a full report or only the messages
+reports=no
+
+# Python expression which should return a note less than 10 (10 is the highest
+# note). You have access to the variables errors warning, statement which
+# respectively contain the number of errors / warnings messages and the total
+# number of statements analyzed. This is used by the global evaluation report
+# (RP0004).
+evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
+
+# Template used to display messages. This is a python new-style format string
+# used to format the message information. See doc for all details
+#msg-template=
+
+
+[LOGGING]
+
+# Logging modules to check that the string format arguments are in logging
+# function parameter format
+logging-modules=logging
+
+
+[BASIC]
+
+# List of builtins function names that should not be used, separated by a comma
+bad-functions=map,filter
+
+# Good variable names which should always be accepted, separated by a comma
+good-names=i,j,k,ex,Run,_
+
+# Bad variable names which should always be refused, separated by a comma
+bad-names=foo,bar,baz,toto,tutu,tata
+
+# Colon-delimited sets of names that determine each other's naming style when
+# the name regexes allow several styles.
+name-group=
+
+# Include a hint for the correct naming format with invalid-name
+include-naming-hint=no
+
+# Regular expression matching correct argument names
+argument-rgx=[a-z_][a-z0-9_]{0,30}$
+
+# Naming hint for argument names
+argument-name-hint=[a-z_][a-z0-9_]{0,30}$
+
+# Regular expression matching correct function names
+function-rgx=[a-z_][a-z0-9_]{2,45}$
+
+# Naming hint for function names
+function-name-hint=[a-z_][a-z0-9_]{2,45}$
+
+# Regular expression matching correct variable names
+variable-rgx=[a-z_][a-z0-9_]{0,45}$
+
+# Naming hint for variable names
+variable-name-hint=[a-z_][a-z0-9_]{0,45}$
+
+# Regular expression matching correct attribute names
+attr-rgx=[a-z_][a-z0-9_]{1,30}$
+
+# Naming hint for attribute names
+attr-name-hint=[a-z_][a-z0-9_]{1,30}$
+
+# Regular expression matching correct class names
+class-rgx=[A-Z_][a-zA-Z0-9]+$
+
+# Naming hint for class names
+class-name-hint=[A-Z_][a-zA-Z0-9]+$
+
+# Regular expression matching correct inline iteration names
+inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$
+
+# Naming hint for inline iteration names
+inlinevar-name-hint=[A-Za-z_][A-Za-z0-9_]*$
+
+# Regular expression matching correct method names
+method-rgx=[a-z_][a-z0-9_]{2,45}$
+
+# Naming hint for method names
+method-name-hint=[a-z_][a-z0-9_]{2,45}$
+
+# Regular expression matching correct constant names
+const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$
+
+# Naming hint for constant names
+const-name-hint=(([A-Z_][A-Z0-9_]*)|(__.*__))$
+
+# Regular expression matching correct class attribute names
+class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$
+
+# Naming hint for class attribute names
+class-attribute-name-hint=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$
+
+# Regular expression matching correct module names
+module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$
+
+# Naming hint for module names
+module-name-hint=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$
+
+# Regular expression which should only match function or class names that do
+# not require a docstring.
+no-docstring-rgx=^_
+
+# Minimum line length for functions/classes that require docstrings, shorter
+# ones are exempt.
+docstring-min-length=-1
+
+
+[ELIF]
+
+# Maximum number of nested blocks for function / method body
+max-nested-blocks=5
+
+
+[MISCELLANEOUS]
+
+# List of note tags to take in consideration, separated by a comma.
+notes=FIXME,XXX,TODO
+
+
+[SIMILARITIES]
+
+# Minimum lines number of a similarity.
+min-similarity-lines=4
+
+# Ignore comments when computing similarities.
+ignore-comments=yes
+
+# Ignore docstrings when computing similarities.
+ignore-docstrings=yes
+
+# Ignore imports when computing similarities.
+ignore-imports=no
+
+
+[TYPECHECK]
+
+# Tells whether missing members accessed in mixin class should be ignored. A
+# mixin class is detected if its name ends with "mixin" (case insensitive).
+ignore-mixin-members=yes
+
+# List of module names for which member attributes should not be checked
+# (useful for modules/projects where namespaces are manipulated during runtime
+# and thus existing member attributes cannot be deduced by static analysis. It
+# supports qualified module names, as well as Unix pattern matching.
+ignored-modules=
+
+# List of classes names for which member attributes should not be checked
+# (useful for classes with attributes dynamically set). This supports can work
+# with qualified names.
+ignored-classes=LexResult,argv
+
+# List of members which are set dynamically and missed by pylint inference
+# system, and so shouldn't trigger E1101 when accessed. Python regular
+# expressions are accepted.
+generated-members=
+
+
+[VARIABLES]
+
+# Tells whether we should check for unused import in __init__ files.
+init-import=no
+
+# A regular expression matching the name of dummy variables (i.e. expectedly
+# not used).
+dummy-variables-rgx=_[a-z]*$
+
+# List of additional names supposed to be defined in builtins. Remember that
+# you should avoid to define new builtins when possible.
+additional-builtins=
+
+# List of strings which can identify a callback function by name. A callback
+# name must start or end with one of those strings.
+callbacks=cb_,_cb
+
+
+[FORMAT]
+
+# Maximum number of characters on a single line.
+max-line-length=180
+
+# Regexp for a line that is allowed to be longer than the limit.
+ignore-long-lines=^\s*(# )?<?https?://\S+>?$
+
+# Allow the body of an if to be on the same line as the test if there is no
+# else.
+single-line-if-stmt=no
+
+# List of optional constructs for which whitespace checking is disabled. `dict-
+# separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}.
+# `trailing-comma` allows a space between comma and closing bracket: (a, ).
+# `empty-line` allows space-only lines.
+no-space-check=trailing-comma,dict-separator
+
+# Maximum number of lines in a module
+max-module-lines=3500
+
+# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
+# tab).
+indent-string=' '
+
+# Number of spaces of indent required inside a hanging or continued line.
+indent-after-paren=4
+
+# Expected format of line ending, e.g. empty (any line ending), LF or CRLF.
+expected-line-ending-format=
+
+
+[SPELLING]
+
+# Spelling dictionary name. Available dictionaries: none. To make it working
+# install python-enchant package.
+spelling-dict=
+
+# List of comma separated words that should not be checked.
+spelling-ignore-words=
+
+# A path to a file that contains private dictionary; one word per line.
+spelling-private-dict-file=
+
+# Tells whether to store unknown words to indicated private dictionary in
+# --spelling-private-dict-file option instead of raising a message.
+spelling-store-unknown-words=no
+
+
+[IMPORTS]
+
+# Deprecated modules which should not be used, separated by a comma
+deprecated-modules=optparse
+
+# Create a graph of every (i.e. internal and external) dependencies in the
+# given file (report RP0402 must not be disabled)
+import-graph=
+
+# Create a graph of external dependencies in the given file (report RP0402 must
+# not be disabled)
+ext-import-graph=
+
+# Create a graph of internal dependencies in the given file (report RP0402 must
+# not be disabled)
+int-import-graph=
+
+
+[CLASSES]
+
+# List of method names used to declare (i.e. assign) instance attributes.
+defining-attr-methods=__init__,__new__,setUp
+
+# List of valid names for the first argument in a class method.
+valid-classmethod-first-arg=cls
+
+# List of valid names for the first argument in a metaclass class method.
+valid-metaclass-classmethod-first-arg=mcs
+
+# List of member names, which should be excluded from the protected access
+# warning.
+exclude-protected=_asdict,_fields,_replace,_source,_make
+
+
+[DESIGN]
+
+# Maximum number of arguments for function / method
+max-args=8
+
+# Argument names that match this expression will be ignored. Default to name
+# with leading underscore
+ignored-argument-names=_.*
+
+# Maximum number of locals for function / method body
+max-locals=15
+
+# Maximum number of return / yield for function / method body
+max-returns=6
+
+# Maximum number of branch for function / method body
+max-branches=32
+
+# Maximum number of statements in function / method body
+max-statements=50
+
+# Maximum number of parents for a class (see R0901).
+max-parents=7
+
+# Maximum number of attributes for a class (see R0902).
+max-attributes=32
+
+# Minimum number of public methods for a class (see R0903).
+min-public-methods=0
+
+# Maximum number of public methods for a class (see R0904).
+max-public-methods=20
+
+# Maximum number of boolean expressions in a if statement
+max-bool-expr=5
+
+
+[EXCEPTIONS]
+
+# Exceptions that will emit a warning when being caught. Defaults to
+# "Exception"
+overgeneral-exceptions=Exception
diff --git a/comm/third_party/botan/src/configs/sonar-project.properties b/comm/third_party/botan/src/configs/sonar-project.properties
new file mode 100644
index 0000000000..38c214642f
--- /dev/null
+++ b/comm/third_party/botan/src/configs/sonar-project.properties
@@ -0,0 +1,18 @@
+sonar.host.url=https://sonarcloud.io
+
+sonar.organization=randombit-github
+sonar.projectKey=botan
+
+sonar.cfamily.threads=3
+
+sonar.projectName=Botan
+sonar.sources=src
+sonar.language=cpp
+sonar.cpp.file.suffixes=.cpp,.h
+sonar.cfamily.build-wrapper-output=bw-outputs
+sonar.sourceEncoding=UTF-8
+
+# Sonar thinks these are duplicates
+sonar.exclusions=build/include/botan/*.h,build/include/botan/internal/*.h,build/include/external/*.h
+
+sonar.cfamily.llvm-cov.reportPath=build/cov_report.txt
diff --git a/comm/third_party/botan/src/configs/sphinx/conf.py b/comm/third_party/botan/src/configs/sphinx/conf.py
new file mode 100644
index 0000000000..cc39de1966
--- /dev/null
+++ b/comm/third_party/botan/src/configs/sphinx/conf.py
@@ -0,0 +1,220 @@
+# -* coding: utf-8 -*-
+# Sphinx configuration file
+
+import sys
+import re
+
+#import sphinx
+
+def check_for_tag(tag):
+ # Nasty hack :(
+ try:
+ opt_t = sys.argv.index('-t')
+ opt_tag = sys.argv.index(tag)
+ return opt_t + 1 == opt_tag
+ except ValueError:
+ return False
+
+
+def parse_version_file(version_path):
+ version_file = open(version_path)
+ key_and_val = re.compile(r"([a-z_]+) = ([a-zA-Z0-9:\-\']+)")
+
+ results = {}
+ for line in version_file.readlines():
+ if not line or line[0] == '#':
+ continue
+ match = key_and_val.match(line)
+ if match:
+ key = match.group(1)
+ val = match.group(2)
+
+ if val == 'None':
+ val = None
+ elif val.startswith("'") and val.endswith("'"):
+ val = val[1:len(val)-1]
+ else:
+ val = int(val)
+
+ results[key] = val
+ return results
+
+version_info = parse_version_file('../../build-data/version.txt')
+
+version_major = version_info['release_major']
+version_minor = version_info['release_minor']
+version_patch = version_info['release_patch']
+
+is_website_build = check_for_tag('website')
+
+needs_sphinx = '1.2'
+
+templates_path = ['templates']
+
+source_suffix = '.rst'
+
+source_encoding = 'utf-8-sig'
+
+master_doc = 'contents'
+
+project = u'botan'
+copyright = u'2000-2017, The Botan Authors'
+
+version = '%d.%d' % (version_major, version_minor)
+release = '%d.%d.%d' % (version_major, version_minor, version_patch)
+
+#today = ''
+today_fmt = '%Y-%m-%d'
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+exclude_patterns = []
+
+# The reST default role (used for this markup: `text`) to use for all documents.
+#default_role = None
+
+# If true, '()' will be appended to :func: etc. cross-reference text.
+add_function_parentheses = False
+
+# If true, the current module name will be prepended to all description
+# unit titles (such as .. function::).
+#add_module_names = True
+
+# If true, sectionauthor and moduleauthor directives will be shown in the
+# output. They are ignored by default.
+#show_authors = False
+
+highlight_language = 'cpp'
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+# A list of ignored prefixes for module index sorting.
+#modindex_common_prefix = []
+
+
+# -- Options for HTML output ---------------------------------------------------
+
+# The theme to use for HTML and HTML Help pages. See the documentation for
+# a list of builtin themes.
+
+try:
+ # On Arch this is python-sphinx_rtd_theme
+ import sphinx_rtd_theme
+ html_theme = "sphinx_rtd_theme"
+ html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
+except ImportError as e:
+ html_theme = 'agogo'
+ html_theme_path = []
+ print("Ignoring ImportError and using old theme")
+
+# The name for this set of Sphinx documents. If None, it defaults to
+# "<project> v<release> documentation".
+html_title = 'Botan'
+
+# A shorter title for the navigation bar. Default is the same as html_title.
+#html_short_title = None
+
+# The name of an image file (relative to this directory) to place at the top
+# of the sidebar.
+#html_logo = None
+
+# The name of an image file (within the static path) to use as favicon of the
+# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
+# pixels large.
+#html_favicon = None
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = []
+
+# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
+# using the given strftime format.
+html_last_updated_fmt = '%Y-%m-%d'
+
+# If true, SmartyPants will be used to convert quotes and dashes to
+# typographically correct entities.
+#html_use_smartypants = True
+
+# Custom sidebar templates, maps document names to template names.
+#html_sidebars = {}
+
+# Additional templates that should be rendered to pages, maps page names to
+# template names.
+#html_additional_pages = {}
+
+# If false, no module index is generated.
+#html_domain_indices = True
+
+# If false, no index is generated.
+#html_use_index = True
+
+# If true, the index is split into individual pages for each letter.
+#html_split_index = False
+
+# If true, links to the reST sources are added to the pages.
+html_show_sourcelink = False
+
+# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
+html_show_sphinx = False
+
+# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
+html_show_copyright = False
+
+# If true, an OpenSearch description file will be output, and all pages will
+# contain a <link> tag referring to it. The value of this option must be the
+# base URL from which the finished HTML is served.
+if is_website_build:
+ html_use_opensearch = 'https://botan.randombit.net/'
+else:
+ html_use_opensearch = ''
+
+# This is the file name suffix for HTML files (e.g. ".xhtml").
+#html_file_suffix = None
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'botandoc'
+
+# -- Options for LaTeX output --------------------------------------------------
+
+# The paper size ('letter' or 'a4').
+#latex_paper_size = 'letter'
+
+# The font size ('10pt', '11pt' or '12pt').
+#latex_font_size = '10pt'
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title, author, documentclass [howto/manual]).
+
+authors = u'Jack Lloyd \\and Daniel Neus \\and Ren\u00e9 Korthaus \\and Juraj Somorovsky \\and Tobias Niemann'
+latex_documents = [
+ ('contents', 'botan.tex', u'Botan Reference Guide', authors, 'manual'),
+]
+
+# The name of an image file (relative to this directory) to place at the top of
+# the title page.
+#latex_logo = None
+
+# For "manual" documents, if this is true, then toplevel headings are parts,
+# not chapters.
+#latex_use_parts = False
+
+# If true, show page references after internal links.
+latex_show_pagerefs = False
+
+# If true, show URL addresses after external links.
+latex_show_urls = 'inline'
+
+# Additional stuff for the LaTeX preamble.
+#latex_preamble = ''
+
+# Documents to append as an appendix to all manuals.
+#latex_appendices = []
+
+# If false, no module index is generated.
+latex_domain_indices = False
+
+latex_elements = {
+ 'printindex': '\\footnotesize\\raggedright\\printindex'
+}
diff --git a/comm/third_party/botan/src/configs/sphinx/templates/layout.html b/comm/third_party/botan/src/configs/sphinx/templates/layout.html
new file mode 100644
index 0000000000..c907c75974
--- /dev/null
+++ b/comm/third_party/botan/src/configs/sphinx/templates/layout.html
@@ -0,0 +1,9 @@
+{% extends "!layout.html" %}
+
+{% block header %}
+ <div class="header-wrapper">
+ <div class="header">
+ <h1>{{ shorttitle|e }}</h1>
+ </div>
+ </div>
+{% endblock %}
diff --git a/comm/third_party/botan/src/fuzzer/asn1.cpp b/comm/third_party/botan/src/fuzzer/asn1.cpp
new file mode 100644
index 0000000000..89073991d1
--- /dev/null
+++ b/comm/third_party/botan/src/fuzzer/asn1.cpp
@@ -0,0 +1,43 @@
+/*
+* (C) 2016,2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "fuzzers.h"
+#include <botan/asn1_print.h>
+#include <fstream>
+
+class ASN1_Parser final : public Botan::ASN1_Formatter
+ {
+ public:
+ ASN1_Parser() : Botan::ASN1_Formatter(true, 64) {}
+
+ protected:
+ std::string format(Botan::ASN1_Tag, Botan::ASN1_Tag, size_t, size_t,
+ const std::string&) const override
+ {
+ return "";
+ }
+
+ std::string format_bin(Botan::ASN1_Tag, Botan::ASN1_Tag,
+ const std::vector<uint8_t>&) const override
+ {
+ return "";
+ }
+ };
+
+void fuzz(const uint8_t in[], size_t len)
+ {
+ try
+ {
+ /*
+ * Here we use an uninitialized ofstream so the fuzzer doesn't spend time
+ * on actual output formatting, no memory is allocated, etc.
+ */
+ std::ofstream out;
+ ASN1_Parser printer;
+ printer.print_to_stream(out, in, len);
+ }
+ catch(Botan::Exception& e) { }
+ }
diff --git a/comm/third_party/botan/src/fuzzer/barrett.cpp b/comm/third_party/botan/src/fuzzer/barrett.cpp
new file mode 100644
index 0000000000..ebc6001971
--- /dev/null
+++ b/comm/third_party/botan/src/fuzzer/barrett.cpp
@@ -0,0 +1,49 @@
+/*
+* (C) 2018,2020 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "fuzzers.h"
+#include <botan/numthry.h>
+#include <botan/reducer.h>
+#include <botan/divide.h>
+
+void fuzz(const uint8_t in[], size_t len)
+ {
+ static const size_t max_bits = 4096;
+
+ if(len <= 4)
+ return;
+
+ if(len > 2*(max_bits/8))
+ return;
+
+ const size_t x_len = 2 * ((len + 2) / 3);
+
+ Botan::BigInt x = Botan::BigInt::decode(in, x_len);
+ const Botan::BigInt p = Botan::BigInt::decode(in + x_len, len - 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 << "\n"
+ << "P = " << p << "\n"
+ << "Barrett = " << z << "\n"
+ << "Ct = " << ct << "\n"
+ << "Ref = " << ref << "\n");
+ }
+ }
diff --git a/comm/third_party/botan/src/fuzzer/bn_cmp.cpp b/comm/third_party/botan/src/fuzzer/bn_cmp.cpp
new file mode 100644
index 0000000000..48c25f3247
--- /dev/null
+++ b/comm/third_party/botan/src/fuzzer/bn_cmp.cpp
@@ -0,0 +1,74 @@
+/*
+* (C) 2021 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "fuzzers.h"
+
+#include <botan/bigint.h>
+
+void fuzz(const uint8_t in[], size_t len)
+ {
+ const size_t max_bits = 512;
+
+ if(len < 3 || len > 1 + 2*(max_bits/8))
+ return;
+
+ const uint8_t signs = in[0];
+ const size_t x_len = (len - 1) / 2;
+
+ Botan::BigInt x = Botan::BigInt::decode(in + 1, x_len);
+ Botan::BigInt y = Botan::BigInt::decode(in + 1 + x_len, len - x_len - 1);
+
+ if(signs & 1)
+ x.flip_sign();
+ if(signs & 2)
+ y.flip_sign();
+
+ const Botan::BigInt d1 = x - y;
+ const Botan::BigInt d2 = y - x;
+
+ FUZZER_ASSERT_TRUE(d1.cmp(d2, false) == 0);
+
+ const bool is_eq = (x == y);
+ const bool is_lt = (x < y);
+ const bool is_gt = (x > y);
+ const bool is_lte = (x <= y);
+ const bool is_gte = (x >= y);
+
+ if(is_eq)
+ {
+ FUZZER_ASSERT_TRUE(d1.is_zero());
+ FUZZER_ASSERT_TRUE(d2.is_zero());
+ }
+
+ if(is_lte)
+ {
+ FUZZER_ASSERT_TRUE(is_lt || is_eq);
+ }
+
+ if(is_gte)
+ {
+ FUZZER_ASSERT_TRUE(is_gt || is_eq);
+ }
+
+ 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());
+ }
+
+ 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());
+ }
+ }
+
diff --git a/comm/third_party/botan/src/fuzzer/bn_sqr.cpp b/comm/third_party/botan/src/fuzzer/bn_sqr.cpp
new file mode 100644
index 0000000000..f507c4a79c
--- /dev/null
+++ b/comm/third_party/botan/src/fuzzer/bn_sqr.cpp
@@ -0,0 +1,24 @@
+/*
+* (C) 2015,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "fuzzers.h"
+
+#include <botan/bigint.h>
+#include <botan/numthry.h>
+
+void fuzz(const uint8_t in[], size_t len)
+ {
+ if(len > 8192/8)
+ return;
+
+ Botan::BigInt x = Botan::BigInt::decode(in, len);
+
+ Botan::BigInt x_sqr = square(x);
+ Botan::BigInt x_mul = x * x;
+
+ FUZZER_ASSERT_EQUAL(x_sqr, x_mul);
+ }
+
diff --git a/comm/third_party/botan/src/fuzzer/cert.cpp b/comm/third_party/botan/src/fuzzer/cert.cpp
new file mode 100644
index 0000000000..215a294002
--- /dev/null
+++ b/comm/third_party/botan/src/fuzzer/cert.cpp
@@ -0,0 +1,22 @@
+/*
+* (C) 2015,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "fuzzers.h"
+#include <botan/x509cert.h>
+#include <botan/data_src.h>
+
+void fuzz(const uint8_t in[], size_t len)
+ {
+ if(len > max_fuzzer_input_size)
+ return;
+
+ try
+ {
+ Botan::DataSource_Memory input(in, len);
+ Botan::X509_Certificate cert(input);
+ }
+ catch(Botan::Exception& e) { }
+ }
diff --git a/comm/third_party/botan/src/fuzzer/crl.cpp b/comm/third_party/botan/src/fuzzer/crl.cpp
new file mode 100644
index 0000000000..e41d523a91
--- /dev/null
+++ b/comm/third_party/botan/src/fuzzer/crl.cpp
@@ -0,0 +1,19 @@
+/*
+* (C) 2015,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "fuzzers.h"
+#include <botan/x509_crl.h>
+#include <botan/data_src.h>
+
+void fuzz(const uint8_t in[], size_t len)
+ {
+ try
+ {
+ Botan::DataSource_Memory input(in, len);
+ Botan::X509_CRL crl(input);
+ }
+ catch(Botan::Exception& e) {}
+ }
diff --git a/comm/third_party/botan/src/fuzzer/divide.cpp b/comm/third_party/botan/src/fuzzer/divide.cpp
new file mode 100644
index 0000000000..b6342ff7d8
--- /dev/null
+++ b/comm/third_party/botan/src/fuzzer/divide.cpp
@@ -0,0 +1,52 @@
+/*
+* (C) 2015,2016,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+#include "fuzzers.h"
+#include <botan/divide.h>
+
+void fuzz(const uint8_t in[], size_t len)
+ {
+ if(len % 2 == 1 || len > 2*4096/8)
+ return;
+
+ // Save on allocations by making these static
+ static Botan::BigInt x, y, q, r, ct_q, ct_r, z;
+
+ x = Botan::BigInt::decode(in, len / 2);
+ y = Botan::BigInt::decode(in + len / 2, len / 2);
+
+ if(y == 0)
+ return;
+
+ Botan::divide(x, y, q, r);
+
+ FUZZER_ASSERT_TRUE(r < y);
+
+ z = q*y + r;
+
+ FUZZER_ASSERT_EQUAL(z, x);
+
+ Botan::ct_divide(x, y, ct_q, ct_r);
+
+ FUZZER_ASSERT_EQUAL(q, ct_q);
+ FUZZER_ASSERT_EQUAL(r, ct_r);
+
+ // Now divide by just low byte of y
+
+ y = y.byte_at(0);
+ if(y == 0)
+ y = 251;
+ Botan::divide(x, y, q, r);
+
+ z = q*y + r;
+ FUZZER_ASSERT_EQUAL(z, x);
+
+ uint8_t r8;
+ Botan::ct_divide_u8(x, y.byte_at(0), ct_q, r8);
+ FUZZER_ASSERT_EQUAL(ct_q, q);
+ FUZZER_ASSERT_EQUAL(r8, r.byte_at(0));
+
+ }
+
diff --git a/comm/third_party/botan/src/fuzzer/ecc_bp256.cpp b/comm/third_party/botan/src/fuzzer/ecc_bp256.cpp
new file mode 100644
index 0000000000..4c1186f06a
--- /dev/null
+++ b/comm/third_party/botan/src/fuzzer/ecc_bp256.cpp
@@ -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(const uint8_t in[], size_t len)
+ {
+ if(len > 2*256/8)
+ return;
+
+ static Botan::EC_Group bp256("brainpool256r1");
+ return check_ecc_math(bp256, in, len);
+ }
diff --git a/comm/third_party/botan/src/fuzzer/ecc_helper.h b/comm/third_party/botan/src/fuzzer/ecc_helper.h
new file mode 100644
index 0000000000..4df23a5722
--- /dev/null
+++ b/comm/third_party/botan/src/fuzzer/ecc_helper.h
@@ -0,0 +1,107 @@
+/*
+* (C) 2015,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef ECC_HELPERS_H_
+#define ECC_HELPERS_H_
+
+#include "fuzzers.h"
+#include <botan/ec_group.h>
+#include <botan/reducer.h>
+#include <botan/numthry.h>
+
+namespace {
+
+inline std::ostream& operator<<(std::ostream& o, const Botan::PointGFp& point)
+ {
+ o << point.get_affine_x() << "," << point.get_affine_y();
+ return o;
+ }
+
+Botan::BigInt decompress_point(bool yMod2,
+ const Botan::BigInt& x,
+ const Botan::BigInt& curve_p,
+ const Botan::BigInt& curve_a,
+ const Botan::BigInt& curve_b)
+ {
+ Botan::BigInt xpow3 = x * x * x;
+
+ Botan::BigInt g = curve_a * x;
+ g += xpow3;
+ g += curve_b;
+ g = g % curve_p;
+
+ Botan::BigInt z = ressol(g, curve_p);
+
+ if(z < 0)
+ throw Botan::Exception("Could not perform square root");
+
+ if(z.get_bit(0) != yMod2)
+ z = curve_p - z;
+
+ return z;
+ }
+
+void check_ecc_math(const Botan::EC_Group& group,
+ const uint8_t in[], size_t len)
+ {
+ // These depend only on the group, which is also static
+ static const Botan::PointGFp base_point = group.get_base_point();
+
+ // This is shared across runs to reduce overhead
+ static std::vector<Botan::BigInt> ws(Botan::PointGFp::WORKSPACE_SIZE);
+
+ const size_t hlen = len / 2;
+ const Botan::BigInt a = Botan::BigInt::decode(in, hlen);
+ const Botan::BigInt b = Botan::BigInt::decode(in + hlen, len - hlen);
+ const Botan::BigInt c = a + b;
+
+ const Botan::PointGFp P1 = base_point * a;
+ const Botan::PointGFp Q1 = base_point * b;
+ const Botan::PointGFp R1 = base_point * c;
+
+ const Botan::PointGFp S1 = P1 + Q1;
+ const Botan::PointGFp T1 = Q1 + P1;
+
+ FUZZER_ASSERT_EQUAL(S1, R1);
+ FUZZER_ASSERT_EQUAL(T1, R1);
+
+ const Botan::PointGFp P2 = group.blinded_base_point_multiply(a, fuzzer_rng(), ws);
+ const Botan::PointGFp Q2 = group.blinded_base_point_multiply(b, fuzzer_rng(), ws);
+ const Botan::PointGFp R2 = group.blinded_base_point_multiply(c, fuzzer_rng(), ws);
+ const Botan::PointGFp S2 = P2 + Q2;
+ const Botan::PointGFp T2 = Q2 + P2;
+
+ FUZZER_ASSERT_EQUAL(S2, R2);
+ FUZZER_ASSERT_EQUAL(T2, R2);
+
+ const Botan::PointGFp P3 = group.blinded_var_point_multiply(base_point, a, fuzzer_rng(), ws);
+ const Botan::PointGFp Q3 = group.blinded_var_point_multiply(base_point, b, fuzzer_rng(), ws);
+ const Botan::PointGFp R3 = group.blinded_var_point_multiply(base_point, c, fuzzer_rng(), ws);
+ const Botan::PointGFp S3 = P3 + Q3;
+ const Botan::PointGFp T3 = Q3 + P3;
+
+ FUZZER_ASSERT_EQUAL(S3, R3);
+ FUZZER_ASSERT_EQUAL(T3, R3);
+
+ FUZZER_ASSERT_EQUAL(S1, S2);
+ FUZZER_ASSERT_EQUAL(S1, S3);
+
+ try
+ {
+ const auto yp = decompress_point(true, a, group.get_p(), group.get_a(), group.get_b());
+ const auto pt_p = group.blinded_var_point_multiply(group.point(a, yp), b, fuzzer_rng(), ws);
+
+ const auto yn = -yp;
+ const auto pt_n = group.blinded_var_point_multiply(group.point(a, yn), b, fuzzer_rng(), ws);
+
+ FUZZER_ASSERT_EQUAL(pt_p, -pt_n);
+ }
+ catch(...) {}
+ }
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/fuzzer/ecc_p256.cpp b/comm/third_party/botan/src/fuzzer/ecc_p256.cpp
new file mode 100644
index 0000000000..c00be71b62
--- /dev/null
+++ b/comm/third_party/botan/src/fuzzer/ecc_p256.cpp
@@ -0,0 +1,15 @@
+/*
+* (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(const uint8_t in[], size_t len)
+ {
+ if(len > 2*256/8)
+ return;
+ static Botan::EC_Group p256("secp256r1");
+ return check_ecc_math(p256, in, len);
+ }
diff --git a/comm/third_party/botan/src/fuzzer/ecc_p384.cpp b/comm/third_party/botan/src/fuzzer/ecc_p384.cpp
new file mode 100644
index 0000000000..1b58da9584
--- /dev/null
+++ b/comm/third_party/botan/src/fuzzer/ecc_p384.cpp
@@ -0,0 +1,15 @@
+/*
+* (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(const uint8_t in[], size_t len)
+ {
+ if(len > 2*384/8)
+ return;
+ static Botan::EC_Group p384("secp384r1");
+ return check_ecc_math(p384, in, len);
+ }
diff --git a/comm/third_party/botan/src/fuzzer/ecc_p521.cpp b/comm/third_party/botan/src/fuzzer/ecc_p521.cpp
new file mode 100644
index 0000000000..3b9ed2d5c9
--- /dev/null
+++ b/comm/third_party/botan/src/fuzzer/ecc_p521.cpp
@@ -0,0 +1,15 @@
+/*
+* (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(const uint8_t in[], size_t len)
+ {
+ if(len > 2*(521+7)/8)
+ return;
+ static Botan::EC_Group p521("secp521r1");
+ return check_ecc_math(p521, in, len);
+ }
diff --git a/comm/third_party/botan/src/fuzzer/fuzzers.h b/comm/third_party/botan/src/fuzzer/fuzzers.h
new file mode 100644
index 0000000000..ee78f7a6dd
--- /dev/null
+++ b/comm/third_party/botan/src/fuzzer/fuzzers.h
@@ -0,0 +1,150 @@
+/*
+* (C) 2015,2016,2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_FUZZER_DRIVER_H_
+#define BOTAN_FUZZER_DRIVER_H_
+
+#include <stdint.h>
+#include <stdlib.h> // for setenv
+#include <iostream>
+#include <vector>
+#include <botan/exceptn.h>
+#include <botan/chacha_rng.h>
+
+static const size_t max_fuzzer_input_size = 8192;
+
+extern void fuzz(const uint8_t in[], size_t len);
+
+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 ***)
+ {
+ /*
+ * This disables the mlock pool, as overwrites within the pool are
+ * opaque to ASan or other instrumentation.
+ */
+ ::setenv("BOTAN_MLOCK_POOL_SIZE", "0", 1);
+ return 0;
+ }
+
+// Called by main() in libFuzzer or in main for AFL below
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t in[], size_t len)
+ {
+ if(len <= max_fuzzer_input_size)
+ {
+ fuzz(in, len);
+ }
+ return 0;
+ }
+
+// Some helpers for the fuzzer jigs
+
+inline Botan::RandomNumberGenerator& fuzzer_rng()
+ {
+ static Botan::ChaCha_RNG rng(Botan::secure_vector<uint8_t>(32));
+ return rng;
+ }
+
+#define FUZZER_WRITE_AND_CRASH(expr) \
+ do { std::cerr << expr; abort(); } while(0)
+
+#define FUZZER_ASSERT_EQUAL(x, y) do { \
+ if(x != y) { \
+ FUZZER_WRITE_AND_CRASH(#x << " = " << x << " !=\n" \
+ << #y << " = " << y << "\n"); \
+ } } while(0)
+
+#define FUZZER_ASSERT_TRUE(e) \
+ do { \
+ 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 <fstream>
+
+namespace {
+
+int fuzz_files(char* files[])
+ {
+ for(size_t i = 0; files[i]; ++i)
+ {
+ std::ifstream in(files[i]);
+
+ if(in.good())
+ {
+ std::vector<uint8_t> buf(max_fuzzer_input_size);
+ in.read((char*)buf.data(), buf.size());
+ const size_t got = in.gcount();
+ buf.resize(got);
+ buf.shrink_to_fit();
+
+ LLVMFuzzerTestOneInput(buf.data(), got);
+ }
+ }
+
+ return 0;
+ }
+
+}
+
+#endif
+
+int main(int argc, char* argv[])
+ {
+ LLVMFuzzerInitialize(&argc, &argv);
+
+#if defined(BOTAN_FUZZER_IS_TEST)
+ if(argc > 1)
+ {
+ return fuzz_files(&argv[1]);
+ }
+#endif
+
+#if defined(__AFL_LOOP)
+ while(__AFL_LOOP(1000))
+#endif
+ {
+ std::vector<uint8_t> buf(max_fuzzer_input_size);
+ std::cin.read((char*)buf.data(), buf.size());
+ const size_t got = std::cin.gcount();
+
+ buf.resize(got);
+ buf.shrink_to_fit();
+
+ LLVMFuzzerTestOneInput(buf.data(), got);
+ }
+ }
+
+#elif defined(BOTAN_FUZZER_IS_KLEE)
+
+#include <klee/klee.h>
+
+int main(int argc, char* argv[])
+ {
+ LLVMFuzzerInitialize(&argc, &argv);
+
+ uint8_t input[max_fuzzer_input_size] = { 0 };
+ klee_make_symbolic(&input, sizeof(input), "input");
+
+ size_t input_len = klee_range(0, sizeof(input), "input_len");
+
+ LLVMFuzzerTestOneInput(input, input_len);
+ }
+
+#endif
+
+#endif
diff --git a/comm/third_party/botan/src/fuzzer/invert.cpp b/comm/third_party/botan/src/fuzzer/invert.cpp
new file mode 100644
index 0000000000..5d34512f30
--- /dev/null
+++ b/comm/third_party/botan/src/fuzzer/invert.cpp
@@ -0,0 +1,82 @@
+/*
+* (C) 2015,2016,2020 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+#include "fuzzers.h"
+#include <botan/numthry.h>
+
+namespace {
+
+Botan::BigInt ref_inverse_mod(const Botan::BigInt& n, const Botan::BigInt& mod)
+ {
+ if(n == 0 || mod < 2)
+ return 0;
+ 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;
+
+ while(u.is_nonzero())
+ {
+ 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)
+ {
+ if(A.is_odd() || B.is_odd())
+ { A += n; B -= mod; }
+ A >>= 1; B >>= 1;
+ }
+
+ const size_t v_zero_bits = Botan::low_zero_bits(v);
+ v >>= v_zero_bits;
+ for(size_t i = 0; i != v_zero_bits; ++i)
+ {
+ if(C.is_odd() || D.is_odd())
+ { C += n; D -= mod; }
+ C >>= 1; D >>= 1;
+ }
+
+ if(u >= v) { u -= v; A -= C; B -= D; }
+ else { v -= u; C -= A; D -= B; }
+ }
+
+ if(v != 1)
+ return 0; // no modular inverse
+
+ while(D.is_negative()) D += mod;
+ while(D >= mod) D -= mod;
+
+ return D;
+ }
+
+}
+
+void fuzz(const uint8_t in[], size_t len)
+ {
+ static const size_t max_bits = 4096;
+
+ if(len > 2*max_bits/8)
+ return;
+
+ const Botan::BigInt x = Botan::BigInt::decode(in, len / 2);
+ Botan::BigInt mod = Botan::BigInt::decode(in + len / 2, len - len / 2);
+
+ if(mod < 2)
+ return;
+
+ const Botan::BigInt lib = Botan::inverse_mod(x, mod);
+ const Botan::BigInt ref = ref_inverse_mod(x, mod);
+
+ if(ref != lib)
+ {
+ FUZZER_WRITE_AND_CRASH("X = " << x << "\n"
+ << "Mod = " << mod << "\n"
+ << "GCD(X,Mod) = " << gcd(x, mod) << "\n"
+ << "RefInv(X,Mod) = " << ref << "\n"
+ << "LibInv(X,Mod) = " << lib << "\n"
+ << "RefCheck = " << (x*ref)%mod << "\n"
+ << "LibCheck = " << (x*lib)%mod << "\n");
+ }
+ }
+
diff --git a/comm/third_party/botan/src/fuzzer/mem_pool.cpp b/comm/third_party/botan/src/fuzzer/mem_pool.cpp
new file mode 100644
index 0000000000..7227462887
--- /dev/null
+++ b/comm/third_party/botan/src/fuzzer/mem_pool.cpp
@@ -0,0 +1,193 @@
+/*
+* (C) 2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "fuzzers.h"
+#include <botan/internal/mem_pool.h>
+#include <botan/internal/bit_ops.h>
+#include <vector>
+#include <map>
+#include <utility>
+
+#include <stdlib.h>
+
+namespace {
+
+size_t compute_expected_alignment(size_t plen)
+ {
+ if(Botan::is_power_of_2(plen))
+ {
+ return plen;
+ }
+ else
+ {
+ return 8;
+ }
+ }
+
+struct RawPage
+ {
+ public:
+ RawPage(void* p) : m_p(p) {}
+ ~RawPage() { std::free(m_p); }
+
+ RawPage(const RawPage& other) = default;
+ RawPage& operator=(const RawPage& other) = default;
+
+ RawPage(RawPage&& other) : m_p(nullptr)
+ {
+ std::swap(m_p, other.m_p);
+ }
+
+ RawPage& operator=(RawPage&& other)
+ {
+ if(this != &other)
+ {
+ std::swap(m_p, other.m_p);
+ }
+ return (*this);
+ }
+
+ void* ptr() const { return m_p; }
+ private:
+ void* m_p;
+ };
+
+std::vector<RawPage> allocate_raw_pages(size_t count, size_t page_size)
+ {
+ std::vector<RawPage> pages;
+ pages.reserve(count);
+
+ for(size_t i = 0; i != count; ++i)
+ {
+ void* ptr = nullptr;
+
+ int rc = ::posix_memalign(&ptr, page_size, page_size);
+ FUZZER_ASSERT_EQUAL(rc, 0);
+
+ if(ptr)
+ {
+ pages.push_back(RawPage(ptr));
+ }
+ }
+
+ return pages;
+ }
+
+}
+
+void fuzz(const uint8_t in[], size_t in_len)
+ {
+ const size_t page_count = 4;
+ const size_t page_size = 4096;
+
+ // static to avoid repeated allocations
+ static std::vector<RawPage> raw_mem = allocate_raw_pages(page_count, page_size);
+
+ std::vector<void*> 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());
+
+ Botan::Memory_Pool pool(mem_pages, page_size);
+ std::map<uint8_t*, size_t> ptrs;
+
+ while(in_len > 0)
+ {
+ const uint8_t op = in[0] % 2;
+ size_t idx = (in[0] >> 1);
+ in += 1;
+ in_len -= 1;
+
+ if(in_len > 0 && idx < 4)
+ {
+ idx = idx * 256 + in[0];
+ in += 1;
+ in_len -= 1;
+ }
+
+ //printf("%d %d\n", op, idx);
+
+ if(op == 0)
+ {
+ const size_t plen = idx + 1; // ensure non-zero
+ uint8_t* p = static_cast<uint8_t*>(pool.allocate(plen));
+
+ if(p)
+ {
+ const size_t expected_alignment = compute_expected_alignment(plen);
+ const size_t alignment = reinterpret_cast<uintptr_t>(p) % expected_alignment;
+ if(alignment != 0)
+ {
+ FUZZER_WRITE_AND_CRASH("Pointer allocated non-aligned pointer " << static_cast<void*>(p) << " for len " << plen
+ << " expected " << expected_alignment << " got " << alignment);
+ }
+
+ //printf("alloc %d -> %p\n", plen, p);
+
+ for(size_t i = 0; i != plen; ++i)
+ {
+ if(p[i] != 0)
+ {
+ FUZZER_WRITE_AND_CRASH("Pool gave out non-zeroed memory");
+ }
+ }
+
+ // verify it becomes zeroed later
+ std::memset(p, idx, plen);
+
+ auto insert = ptrs.insert(std::make_pair(p, plen));
+ if(insert.second == false)
+ {
+ FUZZER_WRITE_AND_CRASH("Pointer " << static_cast<void*>(p) << " already existed\n");
+ }
+
+ auto itr = insert.first;
+
+ // Verify this pointer doesn't overlap with the one before it
+ if(itr != ptrs.begin())
+ {
+ auto before = std::prev(itr);
+ auto ptr_before = *before;
+
+ if(ptr_before.first + ptr_before.second > p)
+ {
+ FUZZER_WRITE_AND_CRASH("Previous " << static_cast<void*>(ptr_before.first) << "/" << ptr_before.second <<
+ " overlaps with new " << static_cast<void*>(p));
+ }
+ }
+
+ auto after = std::next(itr);
+
+ if(after != ptrs.end())
+ {
+ if(p + plen > after->first)
+ {
+ FUZZER_WRITE_AND_CRASH("New " << static_cast<void*>(p) << "/" << plen
+ << " overlaps following " << static_cast<void*>(after->first));
+ }
+ }
+ }
+ }
+ else if(op == 1)
+ {
+ if(ptrs.empty())
+ return;
+
+ size_t which_ptr = idx % ptrs.size();
+
+ auto itr = ptrs.begin();
+
+ while(which_ptr-- > 0)
+ {
+ ++itr;
+ }
+
+ //printf("free %p %d\n", itr->first, itr->second);
+ FUZZER_ASSERT_TRUE(pool.deallocate(itr->first, itr->second));
+ ptrs.erase(itr);
+ }
+ }
+ }
diff --git a/comm/third_party/botan/src/fuzzer/mode_padding.cpp b/comm/third_party/botan/src/fuzzer/mode_padding.cpp
new file mode 100644
index 0000000000..c366530dd1
--- /dev/null
+++ b/comm/third_party/botan/src/fuzzer/mode_padding.cpp
@@ -0,0 +1,169 @@
+/*
+* (C) 2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "fuzzers.h"
+#include <botan/mode_pad.h>
+#include <botan/internal/tls_cbc.h>
+
+namespace {
+
+size_t ref_pkcs7_unpad(const uint8_t in[], size_t len)
+ {
+ if(len <= 2)
+ return len;
+
+ const size_t padding_length = in[len-1];
+
+ if(padding_length == 0 || padding_length > len)
+ return len;
+
+ const size_t padding_start = len - padding_length;
+
+ for(size_t i = padding_start; i != len; ++i)
+ {
+ if(in[i] != padding_length)
+ return len;
+ }
+
+ return len - padding_length;
+ }
+
+size_t ref_x923_unpad(const uint8_t in[], size_t len)
+ {
+ if(len <= 2)
+ return len;
+
+ const size_t padding_length = in[len-1];
+
+ if(padding_length == 0 || padding_length > len)
+ return len;
+ const size_t padding_start = len - padding_length;
+
+ for(size_t i = padding_start; i != len - 1; ++i)
+ {
+ if(in[i] != 0)
+ {
+ return len;
+ }
+ }
+
+ return len - padding_length;
+ }
+
+size_t ref_oneandzero_unpad(const uint8_t in[], size_t len)
+ {
+ if(len <= 2)
+ return len;
+
+ size_t idx = len - 1;
+
+ for(;;)
+ {
+ if(in[idx] == 0)
+ {
+ if(idx == 0)
+ return len;
+ idx -= 1;
+ continue;
+ }
+ else if(in[idx] == 0x80)
+ {
+ return idx;
+ }
+ else
+ return len;
+ }
+
+ return len;
+ }
+
+size_t ref_esp_unpad(const uint8_t in[], size_t len)
+ {
+ if(len <= 2)
+ return len;
+
+ const size_t padding_bytes = in[len - 1];
+
+ if(padding_bytes == 0 || padding_bytes > len)
+ {
+ return len;
+ }
+
+ const size_t padding_start = len - padding_bytes;
+ for(size_t i = padding_start; i != len; ++i)
+ {
+ if(in[i] != (i - padding_start + 1))
+ {
+ return len;
+ }
+ }
+
+ return len - padding_bytes;
+ }
+
+uint16_t ref_tls_cbc_unpad(const uint8_t in[], size_t len)
+ {
+ if(len == 0)
+ return 0;
+
+ const size_t padding_length = in[(len-1)];
+
+ if(padding_length >= len)
+ return 0;
+
+ /*
+ * TLS v1.0 and up require all the padding bytes be the same value
+ * and allows up to 255 bytes.
+ */
+ for(size_t i = 0; i != 1 + padding_length; ++i)
+ {
+ if(in[(len-i-1)] != padding_length)
+ return 0;
+ }
+ return padding_length + 1;
+ }
+
+}
+
+void fuzz(const uint8_t in[], size_t len)
+ {
+ static Botan::PKCS7_Padding pkcs7;
+ static Botan::ANSI_X923_Padding x923;
+ static Botan::OneAndZeros_Padding oneandzero;
+ static Botan::ESP_Padding esp;
+
+ if(pkcs7.valid_blocksize(len))
+ {
+ const size_t ct_pkcs7 = pkcs7.unpad(in, len);
+ const size_t ref_pkcs7 = ref_pkcs7_unpad(in, len);
+ FUZZER_ASSERT_EQUAL(ct_pkcs7, ref_pkcs7);
+ }
+
+ if(x923.valid_blocksize(len))
+ {
+ const size_t ct_x923 = x923.unpad(in, len);
+ const size_t ref_x923 = ref_x923_unpad(in, len);
+ FUZZER_ASSERT_EQUAL(ct_x923, ref_x923);
+ }
+
+ if(oneandzero.valid_blocksize(len))
+ {
+ const size_t ct_oneandzero = oneandzero.unpad(in, len);
+ const size_t ref_oneandzero = ref_oneandzero_unpad(in, len);
+ FUZZER_ASSERT_EQUAL(ct_oneandzero, ref_oneandzero);
+ }
+
+ if(esp.valid_blocksize(len))
+ {
+ const size_t ct_esp = esp.unpad(in, len);
+ const size_t ref_esp = ref_esp_unpad(in, len);
+ FUZZER_ASSERT_EQUAL(ct_esp, ref_esp);
+ }
+
+ const uint16_t ct_cbc = Botan::TLS::check_tls_cbc_padding(in, len);
+ const uint16_t ref_cbc = ref_tls_cbc_unpad(in, len);
+ FUZZER_ASSERT_EQUAL(ct_cbc, ref_cbc);
+ }
diff --git a/comm/third_party/botan/src/fuzzer/oaep.cpp b/comm/third_party/botan/src/fuzzer/oaep.cpp
new file mode 100644
index 0000000000..3d8275bded
--- /dev/null
+++ b/comm/third_party/botan/src/fuzzer/oaep.cpp
@@ -0,0 +1,102 @@
+/*
+* (C) 2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+#include "fuzzers.h"
+
+#include <botan/oaep.h>
+#include <botan/hex.h>
+
+namespace {
+
+Botan::secure_vector<uint8_t>
+ref_oaep_unpad(uint8_t& valid_mask,
+ const uint8_t in[], size_t len,
+ const Botan::secure_vector<uint8_t>& Phash)
+ {
+ const size_t hlen = Phash.size();
+
+ if(len < 2*hlen + 1)
+ {
+ return Botan::secure_vector<uint8_t>();
+ }
+
+ for(size_t i = hlen; i != 2*hlen; ++i)
+ {
+ if(in[i] != Phash[i-hlen])
+ {
+ return Botan::secure_vector<uint8_t>();
+ }
+ }
+
+ for(size_t i = 2*hlen; i != len; ++i)
+ {
+ if(in[i] != 0x00 && in[i] != 0x01)
+ {
+ return Botan::secure_vector<uint8_t>();
+ }
+
+ if(in[i] == 0x01)
+ {
+ valid_mask = 0xFF;
+ return Botan::secure_vector<uint8_t>(in + i + 1, in + len);
+ }
+ }
+
+ return Botan::secure_vector<uint8_t>();
+ }
+
+inline bool all_zeros(const Botan::secure_vector<uint8_t>& v)
+ {
+ for(size_t i = 0; i != v.size(); ++i)
+ {
+ if(v[i] != 0)
+ return false;
+ }
+ return true;
+ }
+
+}
+
+void fuzz(const uint8_t in[], size_t len)
+ {
+ static const Botan::secure_vector<uint8_t> Phash = { 1, 2, 3, 4 };
+
+ uint8_t lib_valid_mask = 0;
+ const Botan::secure_vector<uint8_t> lib_output = Botan::oaep_find_delim(lib_valid_mask, in, len, Phash);
+ FUZZER_ASSERT_TRUE(lib_valid_mask == 0 || lib_valid_mask == 0xFF);
+
+ uint8_t ref_valid_mask = 0;
+ const Botan::secure_vector<uint8_t> ref_output = ref_oaep_unpad(ref_valid_mask, in, len, Phash);
+ FUZZER_ASSERT_TRUE(ref_valid_mask == 0 || ref_valid_mask == 0xFF);
+
+ if(ref_valid_mask == 0xFF && lib_valid_mask == 0x00)
+ {
+ FUZZER_WRITE_AND_CRASH("Ref accepted but library rejected, output " << Botan::hex_encode(ref_output) << "\n");
+ }
+ else if(ref_valid_mask == 0x00 && lib_valid_mask == 0xFF)
+ {
+ FUZZER_WRITE_AND_CRASH("Lib accepted but ref rejected, output = " << Botan::hex_encode(lib_output) << "\n");
+ }
+
+ if(ref_valid_mask == 0x00)
+ {
+ FUZZER_ASSERT_TRUE(all_zeros(ref_output));
+ }
+
+ if(lib_valid_mask == 0x00)
+ {
+ FUZZER_ASSERT_TRUE(all_zeros(lib_output));
+ }
+
+ if(ref_valid_mask && lib_valid_mask)
+ {
+ if(ref_output != lib_output)
+ {
+ FUZZER_WRITE_AND_CRASH("Ref and lib both accepted but produced different output:"
+ << " ref = " << Botan::hex_encode(ref_output)
+ << " lib = " << Botan::hex_encode(lib_output));
+ }
+ }
+ }
diff --git a/comm/third_party/botan/src/fuzzer/ocsp.cpp b/comm/third_party/botan/src/fuzzer/ocsp.cpp
new file mode 100644
index 0000000000..0db265b8df
--- /dev/null
+++ b/comm/third_party/botan/src/fuzzer/ocsp.cpp
@@ -0,0 +1,17 @@
+/*
+* (C) 2015,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "fuzzers.h"
+#include <botan/ocsp.h>
+
+void fuzz(const uint8_t in[], size_t len)
+ {
+ try
+ {
+ Botan::OCSP::Response response(in, len);
+ }
+ catch(Botan::Exception& e) { }
+ }
diff --git a/comm/third_party/botan/src/fuzzer/os2ecp.cpp b/comm/third_party/botan/src/fuzzer/os2ecp.cpp
new file mode 100644
index 0000000000..cb4a50b474
--- /dev/null
+++ b/comm/third_party/botan/src/fuzzer/os2ecp.cpp
@@ -0,0 +1,44 @@
+/*
+* (C) 2015,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+#include "fuzzers.h"
+#include <botan/ec_group.h>
+#include <botan/point_gfp.h>
+
+namespace {
+
+void check_os2ecp(const Botan::EC_Group& group, const uint8_t in[], size_t len)
+ {
+ try
+ {
+ Botan::PointGFp point = group.OS2ECP(in, len);
+ }
+ catch(Botan::Exception& e) {}
+ }
+
+}
+
+void fuzz(const uint8_t in[], size_t len)
+ {
+ if(len >= 256)
+ return;
+
+ static Botan::EC_Group p192("secp192r1");
+ static Botan::EC_Group p224("secp224r1");
+ static Botan::EC_Group p256("secp256r1");
+ static Botan::EC_Group p384("secp384r1");
+ static Botan::EC_Group p521("secp521r1");
+ static Botan::EC_Group bp256("brainpool256r1");
+ static Botan::EC_Group bp512("brainpool512r1");
+
+ check_os2ecp(p192, in, len);
+ check_os2ecp(p224, in, len);
+ check_os2ecp(p256, in, len);
+ check_os2ecp(p384, in, len);
+ check_os2ecp(p521, in, len);
+ check_os2ecp(p521, in, len);
+ check_os2ecp(bp256, in, len);
+ check_os2ecp(bp512, in, len);
+ }
diff --git a/comm/third_party/botan/src/fuzzer/pkcs1.cpp b/comm/third_party/botan/src/fuzzer/pkcs1.cpp
new file mode 100644
index 0000000000..8a297ff7f6
--- /dev/null
+++ b/comm/third_party/botan/src/fuzzer/pkcs1.cpp
@@ -0,0 +1,75 @@
+/*
+* (C) 2015,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+#include "fuzzers.h"
+
+#include <botan/eme_pkcs.h>
+#include <botan/hex.h>
+
+namespace {
+
+std::vector<uint8_t> simple_pkcs1_unpad(const uint8_t in[], size_t len)
+ {
+ if(len < 10)
+ throw Botan::Decoding_Error("bad len");
+
+ if(in[0] != 0 || in[1] != 2)
+ throw Botan::Decoding_Error("bad header field");
+
+ for(size_t i = 2; i < len; ++i)
+ {
+ if(in[i] == 0)
+ {
+ if(i < 10) // at least 8 padding bytes required
+ throw Botan::Decoding_Error("insufficient padding bytes");
+ return std::vector<uint8_t>(in + i + 1, in + len);
+ }
+ }
+
+ throw Botan::Decoding_Error("delim not found");
+ }
+
+}
+
+void fuzz(const uint8_t in[], size_t len)
+ {
+ static Botan::EME_PKCS1v15 pkcs1;
+
+ Botan::secure_vector<uint8_t> lib_result;
+ std::vector<uint8_t> ref_result;
+ bool lib_rejected = false, ref_rejected = false;
+
+ try
+ {
+ uint8_t valid_mask = 0;
+ Botan::secure_vector<uint8_t> decoded = (static_cast<Botan::EME*>(&pkcs1))->unpad(valid_mask, in, len);
+
+ if(valid_mask == 0)
+ lib_rejected = true;
+ else if(valid_mask == 0xFF)
+ lib_rejected = false;
+ else
+ FUZZER_WRITE_AND_CRASH("Invalid valid_mask from unpad");
+ }
+ catch(Botan::Decoding_Error&) { lib_rejected = true; }
+
+ try
+ {
+ ref_result = simple_pkcs1_unpad(in, len);
+ }
+ catch(Botan::Decoding_Error& e) { ref_rejected = true; }
+
+ if(lib_rejected == true && ref_rejected == false)
+ {
+ FUZZER_WRITE_AND_CRASH("Library rejected input accepted by ref "
+ << Botan::hex_encode(ref_result));
+ }
+ else if(ref_rejected == true && lib_rejected == false)
+ {
+ FUZZER_WRITE_AND_CRASH("Library accepted input rejected by ref "
+ << Botan::hex_encode(lib_result));
+ }
+ // otherwise the two implementations agree
+ }
diff --git a/comm/third_party/botan/src/fuzzer/pkcs8.cpp b/comm/third_party/botan/src/fuzzer/pkcs8.cpp
new file mode 100644
index 0000000000..72a3046494
--- /dev/null
+++ b/comm/third_party/botan/src/fuzzer/pkcs8.cpp
@@ -0,0 +1,27 @@
+/*
+* (C) 2015,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "fuzzers.h"
+#include <botan/pk_keys.h>
+#include <botan/pkcs8.h>
+#include <botan/data_src.h>
+#include <botan/ec_group.h>
+
+void fuzz(const uint8_t in[], size_t len)
+ {
+ try
+ {
+ Botan::DataSource_Memory input(in, len);
+ std::unique_ptr<Botan::Private_Key> key = Botan::PKCS8::load_key(input);
+ }
+ catch(Botan::Exception& e) { }
+
+ /*
+ * This avoids OOMs in OSS-Fuzz caused by storing precomputations
+ * for thousands of curves randomly generated by the fuzzer.
+ */
+ Botan::EC_Group::clear_registered_curve_data();
+ }
diff --git a/comm/third_party/botan/src/fuzzer/pow_mod.cpp b/comm/third_party/botan/src/fuzzer/pow_mod.cpp
new file mode 100644
index 0000000000..28350480cb
--- /dev/null
+++ b/comm/third_party/botan/src/fuzzer/pow_mod.cpp
@@ -0,0 +1,72 @@
+/*
+* (C) 2016,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "fuzzers.h"
+#include <botan/numthry.h>
+#include <botan/reducer.h>
+
+namespace {
+
+Botan::BigInt simple_power_mod(Botan::BigInt x,
+ Botan::BigInt n,
+ const Botan::BigInt& p)
+ {
+ if(n == 0)
+ {
+ if(p == 1)
+ return 0;
+ return 1;
+ }
+
+ Botan::Modular_Reducer mod_p(p);
+ Botan::BigInt y = 1;
+
+ while(n > 1)
+ {
+ if(n.is_odd())
+ {
+ y = mod_p.multiply(x, y);
+ }
+ x = mod_p.square(x);
+ n >>= 1;
+ }
+ return mod_p.multiply(x, y);
+ }
+
+}
+
+void fuzz(const uint8_t in[], size_t len)
+ {
+ static const size_t max_bits = 2048;
+
+ if(len % 3 != 0)
+ return;
+
+ const size_t part_size = len / 3;
+
+ if(part_size * 8 > max_bits)
+ return;
+
+ const Botan::BigInt g = Botan::BigInt::decode(in, part_size);
+ const Botan::BigInt x = Botan::BigInt::decode(in + part_size, part_size);
+ const Botan::BigInt p = Botan::BigInt::decode(in + 2*part_size, part_size);
+
+ try
+ {
+ const Botan::BigInt ref = simple_power_mod(g, x, p);
+ const Botan::BigInt z = Botan::power_mod(g, x, p);
+
+ if(ref != z)
+ {
+ FUZZER_WRITE_AND_CRASH("G = " << g << "\n"
+ << "X = " << x << "\n"
+ << "P = " << p << "\n"
+ << "Z = " << z << "\n"
+ << "R = " << ref << "\n");
+ }
+ }
+ catch(Botan::Exception& e) {}
+ }
diff --git a/comm/third_party/botan/src/fuzzer/redc_p192.cpp b/comm/third_party/botan/src/fuzzer/redc_p192.cpp
new file mode 100644
index 0000000000..6898cdbb95
--- /dev/null
+++ b/comm/third_party/botan/src/fuzzer/redc_p192.cpp
@@ -0,0 +1,31 @@
+/*
+* (C) 2015,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "fuzzers.h"
+#include <botan/reducer.h>
+#include <botan/curve_nistp.h>
+
+void fuzz(const uint8_t in[], size_t len)
+ {
+ if(len > 2*192/8)
+ return;
+
+ static const Botan::BigInt& prime = Botan::prime_p192();
+ static const Botan::BigInt prime_2 = prime * prime;
+ static Botan::Modular_Reducer prime_redc(prime);
+
+ Botan::BigInt input = Botan::BigInt::decode(in, len);
+
+ if(input < prime_2)
+ {
+ const Botan::BigInt ref = prime_redc.reduce(input);
+
+ Botan::secure_vector<Botan::word> ws;
+ Botan::redc_p192(input, ws);
+
+ FUZZER_ASSERT_EQUAL(ref, input);
+ }
+ }
diff --git a/comm/third_party/botan/src/fuzzer/redc_p224.cpp b/comm/third_party/botan/src/fuzzer/redc_p224.cpp
new file mode 100644
index 0000000000..b2dbac16e5
--- /dev/null
+++ b/comm/third_party/botan/src/fuzzer/redc_p224.cpp
@@ -0,0 +1,31 @@
+/*
+* (C) 2015,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "fuzzers.h"
+#include <botan/reducer.h>
+#include <botan/curve_nistp.h>
+
+void fuzz(const uint8_t in[], size_t len)
+ {
+ if(len > 2*224/8)
+ return;
+
+ static const Botan::BigInt& prime = Botan::prime_p224();
+ static const Botan::BigInt prime_2 = prime * prime;
+ static Botan::Modular_Reducer prime_redc(prime);
+
+ Botan::BigInt input = Botan::BigInt::decode(in, len);
+
+ if(input < prime_2)
+ {
+ const Botan::BigInt ref = prime_redc.reduce(input);
+
+ Botan::secure_vector<Botan::word> ws;
+ Botan::redc_p224(input, ws);
+
+ FUZZER_ASSERT_EQUAL(ref, input);
+ }
+ }
diff --git a/comm/third_party/botan/src/fuzzer/redc_p256.cpp b/comm/third_party/botan/src/fuzzer/redc_p256.cpp
new file mode 100644
index 0000000000..4c3809f089
--- /dev/null
+++ b/comm/third_party/botan/src/fuzzer/redc_p256.cpp
@@ -0,0 +1,31 @@
+/*
+* (C) 2015,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "fuzzers.h"
+#include <botan/reducer.h>
+#include <botan/curve_nistp.h>
+
+void fuzz(const uint8_t in[], size_t len)
+ {
+ if(len > 2*256/8)
+ return;
+
+ static const Botan::BigInt& prime = Botan::prime_p256();
+ static const Botan::BigInt prime_2 = prime * prime;
+ static Botan::Modular_Reducer prime_redc(prime);
+
+ Botan::BigInt input = Botan::BigInt::decode(in, len);
+
+ if(input < prime_2)
+ {
+ const Botan::BigInt ref = prime_redc.reduce(input);
+
+ Botan::secure_vector<Botan::word> ws;
+ Botan::redc_p256(input, ws);
+
+ FUZZER_ASSERT_EQUAL(ref, input);
+ }
+ }
diff --git a/comm/third_party/botan/src/fuzzer/redc_p384.cpp b/comm/third_party/botan/src/fuzzer/redc_p384.cpp
new file mode 100644
index 0000000000..1c3a777a0a
--- /dev/null
+++ b/comm/third_party/botan/src/fuzzer/redc_p384.cpp
@@ -0,0 +1,31 @@
+/*
+* (C) 2015,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "fuzzers.h"
+#include <botan/reducer.h>
+#include <botan/curve_nistp.h>
+
+void fuzz(const uint8_t in[], size_t len)
+ {
+ if(len > 2*384/8)
+ return;
+
+ static const Botan::BigInt& prime = Botan::prime_p384();
+ static const Botan::BigInt prime_2 = prime * prime;
+ static Botan::Modular_Reducer prime_redc(prime);
+
+ Botan::BigInt input = Botan::BigInt::decode(in, len);
+
+ if(input < prime_2)
+ {
+ const Botan::BigInt ref = prime_redc.reduce(input);
+
+ Botan::secure_vector<Botan::word> ws;
+ Botan::redc_p384(input, ws);
+
+ FUZZER_ASSERT_EQUAL(ref, input);
+ }
+ }
diff --git a/comm/third_party/botan/src/fuzzer/redc_p521.cpp b/comm/third_party/botan/src/fuzzer/redc_p521.cpp
new file mode 100644
index 0000000000..e148c94bb7
--- /dev/null
+++ b/comm/third_party/botan/src/fuzzer/redc_p521.cpp
@@ -0,0 +1,31 @@
+/*
+* (C) 2015,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "fuzzers.h"
+#include <botan/reducer.h>
+#include <botan/curve_nistp.h>
+
+void fuzz(const uint8_t in[], size_t len)
+ {
+ if(len > 2*(521+7)/8)
+ return;
+
+ static const Botan::BigInt& prime = Botan::prime_p521();
+ static const Botan::BigInt prime_2 = prime * prime;
+ static Botan::Modular_Reducer prime_redc(prime);
+
+ Botan::BigInt input = Botan::BigInt::decode(in, len);
+
+ if(input < prime_2)
+ {
+ const Botan::BigInt ref = prime_redc.reduce(input);
+
+ Botan::secure_vector<Botan::word> ws;
+ Botan::redc_p521(input, ws);
+
+ FUZZER_ASSERT_EQUAL(ref, input);
+ }
+ }
diff --git a/comm/third_party/botan/src/fuzzer/ressol.cpp b/comm/third_party/botan/src/fuzzer/ressol.cpp
new file mode 100644
index 0000000000..99d48f98be
--- /dev/null
+++ b/comm/third_party/botan/src/fuzzer/ressol.cpp
@@ -0,0 +1,44 @@
+/*
+* (C) 2015,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "fuzzers.h"
+#include <botan/numthry.h>
+#include <botan/reducer.h>
+
+void fuzz(const uint8_t in[], size_t len)
+ {
+ // 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);
+
+ if(len > p_bits / 8)
+ return;
+
+ try
+ {
+ const Botan::BigInt a = Botan::BigInt::decode(in, len);
+ Botan::BigInt a_sqrt = Botan::ressol(a, p);
+
+ if(a_sqrt > 0)
+ {
+ const Botan::BigInt a_redc = mod_p.reduce(a);
+ const Botan::BigInt z = mod_p.square(a_sqrt);
+
+ if(z != a_redc)
+ {
+ FUZZER_WRITE_AND_CRASH("A = " << a << "\n"
+ << "P = " << p << "\n"
+ << "R = " << a_sqrt << "\n"
+ << "Z = " << z << "\n");
+ }
+ }
+ }
+ catch(Botan::Exception& e) {}
+
+ return;
+ }
+
diff --git a/comm/third_party/botan/src/fuzzer/tls_client.cpp b/comm/third_party/botan/src/fuzzer/tls_client.cpp
new file mode 100644
index 0000000000..a5c8137bfc
--- /dev/null
+++ b/comm/third_party/botan/src/fuzzer/tls_client.cpp
@@ -0,0 +1,130 @@
+/*
+* (C) 2015,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "fuzzers.h"
+#include <botan/tls_client.h>
+
+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(const std::string&, const std::string&, const std::string&) override { return "psk_id"; }
+ Botan::SymmetricKey psk(const std::string&, const std::string&, const std::string&) override
+ {
+ return Botan::SymmetricKey("AABBCCDDEEFF00112233445566778899");
+ }
+ };
+
+class Fuzzer_TLS_Policy : public Botan::TLS::Policy
+ {
+ public:
+ std::vector<uint16_t> ciphersuite_list(Botan::TLS::Protocol_Version version,
+ bool have_srp) const
+ {
+ std::vector<uint16_t> ciphersuites;
+
+ for(auto&& suite : Botan::TLS::Ciphersuite::all_known_ciphersuites())
+ {
+ if(suite.valid() == false)
+ continue;
+
+ // Are we doing SRP?
+ if(!have_srp && suite.kex_method() == Botan::TLS::Kex_Algo::SRP_SHA)
+ continue;
+
+ if(!version.supports_aead_modes())
+ {
+ // Are we doing AEAD in a non-AEAD version?
+ if(suite.mac_algo() == "AEAD")
+ continue;
+
+ // Older (v1.0/v1.1) versions also do not support any hash but SHA-1
+ if(suite.mac_algo() != "SHA-1")
+ continue;
+ }
+
+ ciphersuites.push_back(suite.ciphersuite_code());
+ }
+
+ return ciphersuites;
+ }
+ };
+
+class Fuzzer_TLS_Client_Callbacks : public Botan::TLS::Callbacks
+ {
+ public:
+ void tls_emit_data(const uint8_t[], size_t) override
+ {
+ // discard
+ }
+
+ void tls_record_received(uint64_t, const uint8_t[], size_t) override
+ {
+ // ignore peer data
+ }
+
+ void tls_alert(Botan::TLS::Alert) override
+ {
+ // ignore alert
+ }
+
+ bool tls_session_established(const Botan::TLS::Session&) override
+ {
+ return true; // cache it
+ }
+
+ void tls_verify_cert_chain(
+ const std::vector<Botan::X509_Certificate>& cert_chain,
+ const std::vector<std::shared_ptr<const Botan::OCSP::Response>>& ocsp_responses,
+ const std::vector<Botan::Certificate_Store*>& trusted_roots,
+ Botan::Usage_Type usage,
+ const std::string& hostname,
+ const Botan::TLS::Policy& policy) override
+ {
+ try
+ {
+ // try to validate to exercise those code paths
+ Botan::TLS::Callbacks::tls_verify_cert_chain(cert_chain, ocsp_responses,
+ trusted_roots, usage, hostname, policy);
+ }
+ catch(...)
+ {
+ // ignore validation result
+ }
+ }
+
+ };
+
+void fuzz(const uint8_t in[], size_t len)
+ {
+ if(len == 0)
+ return;
+
+ Botan::TLS::Session_Manager_Noop session_manager;
+ Fuzzer_TLS_Policy policy;
+ Botan::TLS::Protocol_Version client_offer = Botan::TLS::Protocol_Version::TLS_V12;
+ Botan::TLS::Server_Information info("server.name", 443);
+ Fuzzer_TLS_Client_Callbacks callbacks;
+ Fuzzer_TLS_Client_Creds creds;
+
+ Botan::TLS::Client client(callbacks,
+ session_manager,
+ creds,
+ policy,
+ fuzzer_rng(),
+ info,
+ client_offer);
+
+ try
+ {
+ client.received_data(in, len);
+ }
+ catch(std::exception& e)
+ {
+ }
+
+ }
+
diff --git a/comm/third_party/botan/src/fuzzer/tls_client_hello.cpp b/comm/third_party/botan/src/fuzzer/tls_client_hello.cpp
new file mode 100644
index 0000000000..28c77c9b6d
--- /dev/null
+++ b/comm/third_party/botan/src/fuzzer/tls_client_hello.cpp
@@ -0,0 +1,18 @@
+/*
+* (C) 2015,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "fuzzers.h"
+#include <botan/tls_messages.h>
+
+void fuzz(const uint8_t in[], size_t len)
+ {
+ try
+ {
+ std::vector<uint8_t> v(in, in + len);
+ Botan::TLS::Client_Hello ch(v);
+ }
+ catch(Botan::Exception& e) {}
+ }
diff --git a/comm/third_party/botan/src/fuzzer/tls_server.cpp b/comm/third_party/botan/src/fuzzer/tls_server.cpp
new file mode 100644
index 0000000000..d45ff5f5ce
--- /dev/null
+++ b/comm/third_party/botan/src/fuzzer/tls_server.cpp
@@ -0,0 +1,227 @@
+/*
+* (C) 2015,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "fuzzers.h"
+#include <botan/tls_server.h>
+#include <botan/data_src.h>
+
+const char* fixed_rsa_key =
+ "-----BEGIN PRIVATE KEY-----\n"
+ "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCe6qqpMQVJ7zCJ\n"
+ "oSnpxia0yO6M7Ie3FGqPcd0DzueC+kWPvuHQ+PpP5vfO6qqRaDVII37PFX5NUZQm\n"
+ "GK/rAm7spjIHTCMgqSZ8pN13LU8m1gDwIdu9al16LXN9zZjB67uLlFn2trtLi234\n"
+ "i0cnyeF8IC0cz7tgCOzMSVEBcqJjkdgGrZ3WUgOXecVm2lXVrYlEiaSxFp4VOE9k\n"
+ "RFeVrELCjmNtc4hRd1yJsF+vObCtvyqGYQE1Qcb0MVSQDBHMkiUVmO6zuW7td5ef\n"
+ "O/1OyntQJGyVa+SnWbkSLCybta2J7MreHENrF5GA0K1KL140SNRHeWifRMuNQua7\n"
+ "qmKXMBTFAgMBAAECggEAIk3fxyQI0zvpy1vZ01ft1QqmzA7nAPNMSWi33/GS8iga\n"
+ "SfxXfKeySPs/tQ/dAARxs//NiOBH4mLgyxR7LQzaawU5OXALCSraXv+ruuUx990s\n"
+ "WKnGaG4EfbJAAwEVn47Gbkv425P4fEc91vAhzQn8PbIoatbAyOtESpjs/pYDTeC/\n"
+ "mnJId8gqO90cqyRECEMjk9sQ8iEjWPlik4ayGlUVbeeMu6/pJ9F8IZEgkLZiNDAB\n"
+ "4anmOFaT7EmqUjI4IlcaqfbbXyDXlvWUYukidEss+CNvPuqbQHBDnpFVvBxdDR2N\n"
+ "Uj2D5Xd5blcIe2/+1IVRnznjoQ5zvutzb7ThBmMehQKBgQDOITKG0ht2kXLxjVoR\n"
+ "r/pVpx+f3hs3H7wE0+vrLHoQgkVjpMWXQ47YuZTT9rCOOYNI2cMoH2D27t1j78/B\n"
+ "9kGYABUVpvQQ+6amqJDI1eYI6e68TPueEDjeALfSCdmPNiI3lZZrCIK9XLpkoy8K\n"
+ "tGYBRRJ+JJxjj1zPXj9SGshPgwKBgQDFXUtoxY3mCStH3+0b1qxGG9r1L5goHEmd\n"
+ "Am8WBYDheNpL0VqPNzouhuM/ZWMGyyAs/py6aLATe+qhR1uX5vn7LVZwjCSONZ4j\n"
+ "7ieEEUh1BHetPI1oI5PxgokRYfVuckotqVseanI/536Er3Yf2FXNQ1/ceVp9WykX\n"
+ "3mYTKMhQFwKBgQDKakcXpZNaZ5IcKdZcsBZ/rdGcR5sqEnursf9lvRNQytwg8Vkn\n"
+ "JSxNHlBLpV/TCh8lltHRwJ6TXhUBYij+KzhWbx5FWOErHDOWTMmArqtp7W6GcoJT\n"
+ "wVJWjxXzp8CApYQMWVSQXpckJL7UvHohZO0WKiHyxTjde5aD++TqV2qEyQKBgBbD\n"
+ "jvoTpy08K4DLxCZs2Uvw1I1pIuylbpwsdrGciuP2s38BM6fHH+/T4Qwj3osfDKQD\n"
+ "7gHWJ1Dn/wUBHQBlRLoC3bB3iZPZfVb5lhc2gxv0GvWhQVIcoGi/vJ2DpfJKPmIL\n"
+ "4ZWdg3X5dm9JaZ98rVDSj5D3ckd5J0E4hp95GbmbAoGBAJJHM4O9lx60tIjw9Sf/\n"
+ "QmKWyUk0NLnt8DcgRMW7fVxtzPNDy9DBKGIkDdWZ2s+ForICA3C9WSxBC1EOEHGG\n"
+ "xkg2xKt66CeutGroP6M191mHQrRClt1VbEYzQFX21BCk5kig9i/BURyoTHtFiV+t\n"
+ "kbf4VLg8Vk9u/R3RU1HsYWhe\n"
+ "-----END PRIVATE KEY-----\n";
+
+const char* fixed_rsa_cert =
+ "-----BEGIN CERTIFICATE-----\n"
+ "MIIDUDCCAjgCCQD7pIb1ZsoafjANBgkqhkiG9w0BAQsFADBqMQswCQYDVQQGEwJW\n"
+ "VDEQMA4GA1UECAwHVmVybW9udDEWMBQGA1UEBwwNVGhlIEludGVybmV0czEUMBIG\n"
+ "A1UECgwLTWFuZ29zIFIgVXMxGzAZBgNVBAMMEnNlcnZlci5leGFtcGxlLmNvbTAe\n"
+ "Fw0xNjAxMDYxNzQ3MjNaFw0yNjAxMDMxNzQ3MjNaMGoxCzAJBgNVBAYTAlZUMRAw\n"
+ "DgYDVQQIDAdWZXJtb250MRYwFAYDVQQHDA1UaGUgSW50ZXJuZXRzMRQwEgYDVQQK\n"
+ "DAtNYW5nb3MgUiBVczEbMBkGA1UEAwwSc2VydmVyLmV4YW1wbGUuY29tMIIBIjAN\n"
+ "BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnuqqqTEFSe8wiaEp6cYmtMjujOyH\n"
+ "txRqj3HdA87ngvpFj77h0Pj6T+b3zuqqkWg1SCN+zxV+TVGUJhiv6wJu7KYyB0wj\n"
+ "IKkmfKTddy1PJtYA8CHbvWpdei1zfc2Yweu7i5RZ9ra7S4tt+ItHJ8nhfCAtHM+7\n"
+ "YAjszElRAXKiY5HYBq2d1lIDl3nFZtpV1a2JRImksRaeFThPZERXlaxCwo5jbXOI\n"
+ "UXdcibBfrzmwrb8qhmEBNUHG9DFUkAwRzJIlFZjus7lu7XeXnzv9Tsp7UCRslWvk\n"
+ "p1m5Eiwsm7WtiezK3hxDaxeRgNCtSi9eNEjUR3lon0TLjULmu6pilzAUxQIDAQAB\n"
+ "MA0GCSqGSIb3DQEBCwUAA4IBAQA1eZGc/4V7z/E/6eG0hVkzoAZeuTcSP7WqBSx+\n"
+ "OP2yh0163UYjoa6nehmkKYQQ9PbYPZGzIcl+dBFyYzy6jcp0NdtzpWnTFrjl4rMq\n"
+ "akcQ1D0LTYjJXVP9G/vF/SvatOFeVTnQmLlLt/a8ZtRUINqejeZZPzH8ifzFW6tu\n"
+ "mlhTVIEKyPHpxClh5Y3ubw/mZYygekFTqMkTx3FwJxKU8J6rYGZxanWAODUIvCUo\n"
+ "Fxer1qC5Love3uWl3vXPLEZWZdORnExSRByzz2immBP2vX4zYZoeZRhTQ9ae1TIV\n"
+ "Dk02a/1AOJZdZReDbgXhlqaUx5pk/rzo4mDzvu5HSCeXmClz\n"
+ "-----END CERTIFICATE-----\n";
+
+class Fuzzer_TLS_Server_Creds : public Botan::Credentials_Manager
+ {
+ public:
+ Fuzzer_TLS_Server_Creds()
+ {
+ Botan::DataSource_Memory cert_in(fixed_rsa_cert);
+ Botan::DataSource_Memory key_in(fixed_rsa_key);
+
+ m_rsa_cert.reset(new Botan::X509_Certificate(cert_in));
+ //m_rsa_key.reset(Botan::PKCS8::load_key(key_in, fuzzer_rng());
+ }
+
+ std::vector<Botan::X509_Certificate> cert_chain(
+ const std::vector<std::string>& algos,
+ const std::string& /*type*/,
+ const std::string& /*hostname*/) override
+ {
+ std::vector<Botan::X509_Certificate> v;
+
+ for(auto algo : algos)
+ {
+ if(algo == "RSA")
+ {
+ v.push_back(*m_rsa_cert);
+ break;
+ }
+ }
+
+ return v;
+ }
+
+ Botan::Private_Key* private_key_for(const Botan::X509_Certificate& /*cert*/,
+ const std::string& /*type*/,
+ const std::string& /*context*/) override
+ {
+ return m_rsa_key.get();
+ }
+
+ std::string psk_identity_hint(const std::string&, const std::string&) override { return "psk_hint"; }
+ std::string psk_identity(const std::string&, const std::string&, const std::string&) override { return "psk_id"; }
+ Botan::SymmetricKey psk(const std::string&, const std::string&, const std::string&) override
+ {
+ return Botan::SymmetricKey("AABBCCDDEEFF00112233445566778899");
+ }
+ private:
+ std::unique_ptr<Botan::X509_Certificate> m_rsa_cert;
+ std::unique_ptr<Botan::Private_Key> m_rsa_key;
+ };
+
+class Fuzzer_TLS_Policy : public Botan::TLS::Policy
+ {
+ public:
+ std::vector<uint16_t> ciphersuite_list(Botan::TLS::Protocol_Version version,
+ bool have_srp) const
+ {
+ std::vector<uint16_t> ciphersuites;
+
+ for(auto&& suite : Botan::TLS::Ciphersuite::all_known_ciphersuites())
+ {
+ if(suite.valid() == false)
+ continue;
+
+ // Are we doing SRP?
+ if(!have_srp && suite.kex_method() == Botan::TLS::Kex_Algo::SRP_SHA)
+ continue;
+
+ if(!version.supports_aead_modes())
+ {
+ // Are we doing AEAD in a non-AEAD version?
+ if(suite.mac_algo() == "AEAD")
+ continue;
+
+ // Older (v1.0/v1.1) versions also do not support any hash but SHA-1
+ if(suite.mac_algo() != "SHA-1")
+ continue;
+ }
+
+ ciphersuites.push_back(suite.ciphersuite_code());
+ }
+
+ return ciphersuites;
+ }
+ };
+
+class Fuzzer_TLS_Server_Callbacks : public Botan::TLS::Callbacks
+ {
+ public:
+ void tls_emit_data(const uint8_t[], size_t) override
+ {
+ // discard
+ }
+
+ void tls_record_received(uint64_t, const uint8_t[], size_t) override
+ {
+ // ignore peer data
+ }
+
+ void tls_alert(Botan::TLS::Alert) override
+ {
+ // ignore alert
+ }
+
+ bool tls_session_established(const Botan::TLS::Session&) override
+ {
+ return true; // cache it
+ }
+
+ std::string tls_server_choose_app_protocol(const std::vector<std::string>& client_protos) override
+ {
+ if(client_protos.size() > 1)
+ return client_protos[0];
+ else
+ return "fuzzy";
+ }
+
+ void tls_verify_cert_chain(
+ const std::vector<Botan::X509_Certificate>& cert_chain,
+ const std::vector<std::shared_ptr<const Botan::OCSP::Response>>& ocsp_responses,
+ const std::vector<Botan::Certificate_Store*>& trusted_roots,
+ Botan::Usage_Type usage,
+ const std::string& hostname,
+ const Botan::TLS::Policy& policy) override
+ {
+ try
+ {
+ // try to validate to exercise those code paths
+ Botan::TLS::Callbacks::tls_verify_cert_chain(cert_chain, ocsp_responses,
+ trusted_roots, usage, hostname, policy);
+ }
+ catch(...)
+ {
+ // ignore validation result
+ }
+ }
+
+ };
+
+void fuzz(const uint8_t in[], size_t len)
+ {
+ if(len <= 1)
+ return;
+
+ Botan::TLS::Session_Manager_Noop session_manager;
+ Fuzzer_TLS_Policy policy;
+ Botan::TLS::Server_Information info("server.name", 443);
+ Fuzzer_TLS_Server_Creds creds;
+ Fuzzer_TLS_Server_Callbacks callbacks;
+
+ const bool is_datagram = in[0] & 1;
+
+ Botan::TLS::Server server(callbacks,
+ session_manager,
+ creds,
+ policy,
+ fuzzer_rng(),
+ is_datagram);
+
+ try
+ {
+ server.received_data(in + 1, len - 1);
+ }
+ catch(std::exception& e)
+ {
+ }
+ }
diff --git a/comm/third_party/botan/src/fuzzer/uri.cpp b/comm/third_party/botan/src/fuzzer/uri.cpp
new file mode 100644
index 0000000000..89066d283e
--- /dev/null
+++ b/comm/third_party/botan/src/fuzzer/uri.cpp
@@ -0,0 +1,20 @@
+/*
+* (C) 2019 Nuno Goncalves <nunojpg@gmail.com>
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "fuzzers.h"
+#include <botan/internal/uri.h>
+
+void fuzz(const uint8_t in[], size_t len)
+ {
+ if(len > max_fuzzer_input_size)
+ return;
+
+ try
+ {
+ Botan::URI::fromAny(std::string(reinterpret_cast<const char*>(in), len));
+ }
+ catch(Botan::Exception& e) { }
+ }
diff --git a/comm/third_party/botan/src/fuzzer/x509_dn.cpp b/comm/third_party/botan/src/fuzzer/x509_dn.cpp
new file mode 100644
index 0000000000..9fc062cfb4
--- /dev/null
+++ b/comm/third_party/botan/src/fuzzer/x509_dn.cpp
@@ -0,0 +1,41 @@
+/*
+* (C) 2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "fuzzers.h"
+#include <botan/pkix_types.h>
+#include <botan/ber_dec.h>
+#include <botan/hex.h>
+
+void fuzz(const uint8_t in[], size_t len)
+ {
+ Botan::X509_DN dn1;
+ Botan::X509_DN dn2;
+
+ try
+ {
+ Botan::BER_Decoder ber(in, len);
+ dn1.decode_from(ber);
+ dn2.decode_from(ber);
+ }
+ catch(...) { return; }
+
+ const bool eq = dn1 == dn2;
+ const bool lt1 = dn1 < dn2;
+ const bool lt2 = dn2 < dn1;
+
+ if(lt1 == false && lt2 == false)
+ {
+ FUZZER_ASSERT_TRUE(eq);
+ }
+ else
+ {
+ // one is less than the other
+ FUZZER_ASSERT_TRUE(lt1 || lt2);
+
+ // it is not the case that both are less than the other
+ FUZZER_ASSERT_TRUE(!lt1 || !lt2);
+ }
+ }
diff --git a/comm/third_party/botan/src/lib/asn1/alg_id.cpp b/comm/third_party/botan/src/lib/asn1/alg_id.cpp
new file mode 100644
index 0000000000..1e82f29955
--- /dev/null
+++ b/comm/third_party/botan/src/lib/asn1/alg_id.cpp
@@ -0,0 +1,109 @@
+/*
+* Algorithm Identifier
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/asn1_obj.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
+#include <botan/oids.h>
+
+namespace Botan {
+
+/*
+* Create an AlgorithmIdentifier
+*/
+AlgorithmIdentifier::AlgorithmIdentifier(const OID& alg_id,
+ const std::vector<uint8_t>& param) :
+ oid(alg_id),
+ parameters(param)
+ {}
+
+/*
+* Create an AlgorithmIdentifier
+*/
+AlgorithmIdentifier::AlgorithmIdentifier(const std::string& alg_id,
+ const std::vector<uint8_t>& param) :
+ AlgorithmIdentifier(OID::from_string(alg_id), param)
+ {}
+
+/*
+* Create an AlgorithmIdentifier
+*/
+AlgorithmIdentifier::AlgorithmIdentifier(const OID& alg_id,
+ Encoding_Option option) :
+ oid(alg_id),
+ parameters()
+ {
+ const uint8_t DER_NULL[] = { 0x05, 0x00 };
+
+ if(option == USE_NULL_PARAM)
+ parameters.assign(DER_NULL, DER_NULL + 2);
+ }
+
+/*
+* Create an AlgorithmIdentifier
+*/
+AlgorithmIdentifier::AlgorithmIdentifier(const std::string& alg_id,
+ Encoding_Option option) :
+ oid(OID::from_string(alg_id)),
+ parameters()
+ {
+ const uint8_t DER_NULL[] = { 0x05, 0x00 };
+
+ if(option == USE_NULL_PARAM)
+ parameters.assign(DER_NULL, DER_NULL + 2);
+ }
+
+bool AlgorithmIdentifier::parameters_are_null() const
+ {
+ return (parameters.size() == 2 && (parameters[0] == 0x05) && (parameters[1] == 0x00));
+ }
+
+bool operator==(const AlgorithmIdentifier& a1, const AlgorithmIdentifier& a2)
+ {
+ if(a1.get_oid() != a2.get_oid())
+ return false;
+
+ /*
+ * Treat NULL and empty as equivalent
+ */
+ if(a1.parameters_are_null_or_empty() &&
+ a2.parameters_are_null_or_empty())
+ {
+ return true;
+ }
+
+ return (a1.get_parameters() == a2.get_parameters());
+ }
+
+bool operator!=(const AlgorithmIdentifier& a1, const AlgorithmIdentifier& a2)
+ {
+ return !(a1 == a2);
+ }
+
+/*
+* DER encode an AlgorithmIdentifier
+*/
+void AlgorithmIdentifier::encode_into(DER_Encoder& codec) const
+ {
+ codec.start_cons(SEQUENCE)
+ .encode(get_oid())
+ .raw_bytes(get_parameters())
+ .end_cons();
+ }
+
+/*
+* Decode a BER encoded AlgorithmIdentifier
+*/
+void AlgorithmIdentifier::decode_from(BER_Decoder& codec)
+ {
+ codec.start_cons(SEQUENCE)
+ .decode(oid)
+ .raw_bytes(parameters)
+ .end_cons();
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/asn1/alg_id.h b/comm/third_party/botan/src/lib/asn1/alg_id.h
new file mode 100644
index 0000000000..88e54466d3
--- /dev/null
+++ b/comm/third_party/botan/src/lib/asn1/alg_id.h
@@ -0,0 +1,14 @@
+/*
+* Algorithm Identifier
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_ALGORITHM_IDENTIFIER_H_
+#define BOTAN_ALGORITHM_IDENTIFIER_H_
+
+#include <botan/asn1_obj.h>
+BOTAN_DEPRECATED_HEADER(alg_id.h)
+
+#endif
diff --git a/comm/third_party/botan/src/lib/asn1/asn1_obj.cpp b/comm/third_party/botan/src/lib/asn1/asn1_obj.cpp
new file mode 100644
index 0000000000..8243552648
--- /dev/null
+++ b/comm/third_party/botan/src/lib/asn1/asn1_obj.cpp
@@ -0,0 +1,238 @@
+/*
+* ASN.1 Internals
+* (C) 1999-2007,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/asn1_obj.h>
+#include <botan/der_enc.h>
+#include <botan/data_src.h>
+#include <botan/internal/stl_util.h>
+#include <sstream>
+
+namespace Botan {
+
+std::vector<uint8_t> ASN1_Object::BER_encode() const
+ {
+ std::vector<uint8_t> output;
+ DER_Encoder der(output);
+ this->encode_into(der);
+ return output;
+ }
+
+/*
+* Check a type invariant on BER data
+*/
+void BER_Object::assert_is_a(ASN1_Tag type_tag_, ASN1_Tag class_tag_,
+ const std::string& descr) const
+ {
+ if(this->is_a(type_tag_, class_tag_) == false)
+ {
+ std::stringstream msg;
+
+ msg << "Tag mismatch when decoding " << descr << " got ";
+
+ if(class_tag == NO_OBJECT && type_tag == NO_OBJECT)
+ {
+ msg << "EOF";
+ }
+ else
+ {
+ if(class_tag == UNIVERSAL || class_tag == CONSTRUCTED)
+ {
+ msg << asn1_tag_to_string(type_tag);
+ }
+ else
+ {
+ msg << std::to_string(type_tag);
+ }
+
+ msg << "/" << asn1_class_to_string(class_tag);
+ }
+
+ msg << " expected ";
+
+ if(class_tag_ == UNIVERSAL || class_tag_ == CONSTRUCTED)
+ {
+ msg << asn1_tag_to_string(type_tag_);
+ }
+ else
+ {
+ msg << std::to_string(type_tag_);
+ }
+
+ msg << "/" << asn1_class_to_string(class_tag_);
+
+ throw BER_Decoding_Error(msg.str());
+ }
+ }
+
+bool BER_Object::is_a(ASN1_Tag type_tag_, ASN1_Tag class_tag_) const
+ {
+ return (type_tag == type_tag_ && class_tag == class_tag_);
+ }
+
+bool BER_Object::is_a(int type_tag_, ASN1_Tag class_tag_) const
+ {
+ return is_a(ASN1_Tag(type_tag_), class_tag_);
+ }
+
+void BER_Object::set_tagging(ASN1_Tag t, ASN1_Tag c)
+ {
+ type_tag = t;
+ class_tag = c;
+ }
+
+std::string asn1_class_to_string(ASN1_Tag type)
+ {
+ switch(type)
+ {
+ case UNIVERSAL:
+ return "UNIVERSAL";
+ case CONSTRUCTED:
+ return "CONSTRUCTED";
+ case CONTEXT_SPECIFIC:
+ return "CONTEXT_SPECIFIC";
+ case APPLICATION:
+ return "APPLICATION";
+ case CONSTRUCTED | CONTEXT_SPECIFIC:
+ return "PRIVATE";
+ case Botan::NO_OBJECT:
+ return "NO_OBJECT";
+ default:
+ return "CLASS(" + std::to_string(static_cast<size_t>(type)) + ")";
+ }
+ }
+
+std::string asn1_tag_to_string(ASN1_Tag type)
+ {
+ switch(type)
+ {
+ case Botan::SEQUENCE:
+ return "SEQUENCE";
+
+ case Botan::SET:
+ return "SET";
+
+ case Botan::PRINTABLE_STRING:
+ return "PRINTABLE STRING";
+
+ case Botan::NUMERIC_STRING:
+ return "NUMERIC STRING";
+
+ case Botan::IA5_STRING:
+ return "IA5 STRING";
+
+ case Botan::T61_STRING:
+ return "T61 STRING";
+
+ case Botan::UTF8_STRING:
+ return "UTF8 STRING";
+
+ case Botan::VISIBLE_STRING:
+ return "VISIBLE STRING";
+
+ case Botan::BMP_STRING:
+ return "BMP STRING";
+
+ case Botan::UNIVERSAL_STRING:
+ return "UNIVERSAL STRING";
+
+ case Botan::UTC_TIME:
+ return "UTC TIME";
+
+ case Botan::GENERALIZED_TIME:
+ return "GENERALIZED TIME";
+
+ case Botan::OCTET_STRING:
+ return "OCTET STRING";
+
+ case Botan::BIT_STRING:
+ return "BIT STRING";
+
+ case Botan::ENUMERATED:
+ return "ENUMERATED";
+
+ case Botan::INTEGER:
+ return "INTEGER";
+
+ case Botan::NULL_TAG:
+ return "NULL";
+
+ case Botan::OBJECT_ID:
+ return "OBJECT";
+
+ case Botan::BOOLEAN:
+ return "BOOLEAN";
+
+ case Botan::NO_OBJECT:
+ return "NO_OBJECT";
+
+ default:
+ return "TAG(" + std::to_string(static_cast<size_t>(type)) + ")";
+ }
+ }
+
+/*
+* BER Decoding Exceptions
+*/
+BER_Decoding_Error::BER_Decoding_Error(const std::string& str) :
+ Decoding_Error("BER: " + str) {}
+
+BER_Bad_Tag::BER_Bad_Tag(const std::string& str, ASN1_Tag tag) :
+ BER_Decoding_Error(str + ": " + std::to_string(tag)) {}
+
+BER_Bad_Tag::BER_Bad_Tag(const std::string& str,
+ ASN1_Tag tag1, ASN1_Tag tag2) :
+ BER_Decoding_Error(str + ": " + std::to_string(tag1) + "/" + std::to_string(tag2)) {}
+
+namespace ASN1 {
+
+/*
+* Put some arbitrary bytes into a SEQUENCE
+*/
+std::vector<uint8_t> put_in_sequence(const std::vector<uint8_t>& contents)
+ {
+ return ASN1::put_in_sequence(contents.data(), contents.size());
+ }
+
+std::vector<uint8_t> put_in_sequence(const uint8_t bits[], size_t len)
+ {
+ std::vector<uint8_t> output;
+ DER_Encoder(output)
+ .start_cons(SEQUENCE)
+ .raw_bytes(bits, len)
+ .end_cons();
+ return output;
+ }
+
+/*
+* 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());
+ }
+
+/*
+* Do heuristic tests for BER data
+*/
+bool maybe_BER(DataSource& source)
+ {
+ uint8_t first_u8;
+ if(!source.peek_byte(first_u8))
+ {
+ BOTAN_ASSERT_EQUAL(source.read_byte(first_u8), 0, "Expected EOF");
+ throw Stream_IO_Error("ASN1::maybe_BER: Source was empty");
+ }
+
+ if(first_u8 == (SEQUENCE | CONSTRUCTED))
+ return true;
+ return false;
+ }
+
+}
+
+}
diff --git a/comm/third_party/botan/src/lib/asn1/asn1_obj.h b/comm/third_party/botan/src/lib/asn1/asn1_obj.h
new file mode 100644
index 0000000000..0ce4437712
--- /dev/null
+++ b/comm/third_party/botan/src/lib/asn1/asn1_obj.h
@@ -0,0 +1,475 @@
+/*
+* (C) 1999-2007,2018,2020 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_ASN1_OBJECT_TYPES_H_
+#define BOTAN_ASN1_OBJECT_TYPES_H_
+
+#include <botan/secmem.h>
+#include <botan/exceptn.h>
+#include <vector>
+#include <string>
+#include <chrono>
+
+namespace Botan {
+
+class BER_Decoder;
+class DER_Encoder;
+
+/**
+* ASN.1 Type and Class Tags
+* This will become an enum class in a future major release
+*/
+enum ASN1_Tag : uint32_t {
+ UNIVERSAL = 0x00,
+ APPLICATION = 0x40,
+ CONTEXT_SPECIFIC = 0x80,
+
+ CONSTRUCTED = 0x20,
+
+ PRIVATE = CONSTRUCTED | CONTEXT_SPECIFIC,
+
+ EOC = 0x00,
+ BOOLEAN = 0x01,
+ INTEGER = 0x02,
+ BIT_STRING = 0x03,
+ OCTET_STRING = 0x04,
+ NULL_TAG = 0x05,
+ OBJECT_ID = 0x06,
+ ENUMERATED = 0x0A,
+ SEQUENCE = 0x10,
+ SET = 0x11,
+
+ UTF8_STRING = 0x0C,
+ NUMERIC_STRING = 0x12,
+ PRINTABLE_STRING = 0x13,
+ T61_STRING = 0x14,
+ IA5_STRING = 0x16,
+ VISIBLE_STRING = 0x1A,
+ UNIVERSAL_STRING = 0x1C,
+ BMP_STRING = 0x1E,
+
+ UTC_TIME = 0x17,
+ GENERALIZED_TIME = 0x18,
+ UTC_OR_GENERALIZED_TIME = 0x19,
+
+ NO_OBJECT = 0xFF00,
+ DIRECTORY_STRING = 0xFF01
+};
+
+std::string BOTAN_UNSTABLE_API asn1_tag_to_string(ASN1_Tag type);
+std::string BOTAN_UNSTABLE_API asn1_class_to_string(ASN1_Tag type);
+
+/**
+* Basic ASN.1 Object Interface
+*/
+class BOTAN_PUBLIC_API(2,0) ASN1_Object
+ {
+ public:
+ /**
+ * Encode whatever this object is into to
+ * @param to the DER_Encoder that will be written to
+ */
+ virtual void encode_into(DER_Encoder& to) const = 0;
+
+ /**
+ * Decode whatever this object is from from
+ * @param from the BER_Decoder that will be read from
+ */
+ virtual void decode_from(BER_Decoder& from) = 0;
+
+ /**
+ * Return the encoding of this object. This is a convenience
+ * method when just one object needs to be serialized. Use
+ * DER_Encoder for complicated encodings.
+ */
+ std::vector<uint8_t> BER_encode() const;
+
+ ASN1_Object() = default;
+ ASN1_Object(const ASN1_Object&) = default;
+ ASN1_Object & operator=(const ASN1_Object&) = default;
+ virtual ~ASN1_Object() = default;
+ };
+
+/**
+* BER Encoded Object
+*/
+class BOTAN_PUBLIC_API(2,0) BER_Object final
+ {
+ public:
+ BER_Object() : type_tag(NO_OBJECT), class_tag(UNIVERSAL) {}
+
+ BER_Object(const BER_Object& other) = default;
+
+ BER_Object& operator=(const BER_Object& other) = default;
+
+ BER_Object(BER_Object&& other) = default;
+
+ BER_Object& operator=(BER_Object&& other) = default;
+
+ bool is_set() const { return type_tag != NO_OBJECT; }
+
+ ASN1_Tag tagging() const { return ASN1_Tag(type() | get_class()); }
+
+ ASN1_Tag type() const { return type_tag; }
+ ASN1_Tag get_class() const { return class_tag; }
+
+ const uint8_t* bits() const { return value.data(); }
+
+ size_t length() const { return value.size(); }
+
+ void assert_is_a(ASN1_Tag type_tag, ASN1_Tag class_tag,
+ const std::string& descr = "object") const;
+
+ bool is_a(ASN1_Tag type_tag, ASN1_Tag class_tag) const;
+
+ bool is_a(int type_tag, ASN1_Tag class_tag) const;
+
+ BOTAN_DEPRECATED_PUBLIC_MEMBER_VARIABLES:
+ /*
+ * The following member variables are public for historical reasons, but
+ * will be made private in a future major release. Use the accessor
+ * functions above.
+ */
+ ASN1_Tag type_tag, class_tag;
+ secure_vector<uint8_t> value;
+
+ private:
+
+ friend class BER_Decoder;
+
+ void set_tagging(ASN1_Tag type_tag, ASN1_Tag class_tag);
+
+ uint8_t* mutable_bits(size_t length)
+ {
+ value.resize(length);
+ return value.data();
+ }
+ };
+
+/*
+* ASN.1 Utility Functions
+*/
+class DataSource;
+
+namespace ASN1 {
+
+std::vector<uint8_t> put_in_sequence(const std::vector<uint8_t>& val);
+std::vector<uint8_t> put_in_sequence(const uint8_t bits[], size_t len);
+std::string to_string(const BER_Object& obj);
+
+/**
+* Heuristics tests; is this object possibly BER?
+* @param src a data source that will be peeked at but not modified
+*/
+bool maybe_BER(DataSource& src);
+
+}
+
+/**
+* General BER Decoding Error Exception
+*/
+class BOTAN_PUBLIC_API(2,0) BER_Decoding_Error : public Decoding_Error
+ {
+ public:
+ explicit BER_Decoding_Error(const std::string&);
+ };
+
+/**
+* Exception For Incorrect BER Taggings
+*/
+class BOTAN_PUBLIC_API(2,0) BER_Bad_Tag final : public BER_Decoding_Error
+ {
+ public:
+ BER_Bad_Tag(const std::string& msg, ASN1_Tag tag);
+ BER_Bad_Tag(const std::string& msg, ASN1_Tag tag1, ASN1_Tag tag2);
+ };
+
+/**
+* This class represents ASN.1 object identifiers.
+*/
+class BOTAN_PUBLIC_API(2,0) OID final : public ASN1_Object
+ {
+ public:
+
+ /**
+ * Create an uninitialied OID object
+ */
+ explicit OID() {}
+
+ /**
+ * Construct an OID from a string.
+ * @param str a string in the form "a.b.c" etc., where a,b,c are numbers
+ */
+ explicit OID(const std::string& str);
+
+ /**
+ * Initialize an OID from a sequence of integer values
+ */
+ explicit OID(std::initializer_list<uint32_t> init) : m_id(init) {}
+
+ /**
+ * Initialize an OID from a vector of integer values
+ */
+ explicit OID(std::vector<uint32_t>&& init) : m_id(init) {}
+
+ /**
+ * Construct an OID from a string.
+ * @param str a string in the form "a.b.c" etc., where a,b,c are numbers
+ * or any known OID name (for example "RSA" or "X509v3.SubjectKeyIdentifier")
+ */
+ static OID from_string(const std::string& str);
+
+ void encode_into(class DER_Encoder&) const override;
+ void decode_from(class BER_Decoder&) override;
+
+ /**
+ * Find out whether this OID is empty
+ * @return true is no OID value is set
+ */
+ bool empty() const { return m_id.empty(); }
+
+ /**
+ * Find out whether this OID has a value
+ * @return true is this OID has a value
+ */
+ bool has_value() const { return (m_id.empty() == false); }
+
+ /**
+ * Get this OID as list (vector) of its components.
+ * @return vector representing this OID
+ */
+ const std::vector<uint32_t>& get_components() const { return m_id; }
+
+ const std::vector<uint32_t>& get_id() const { return get_components(); }
+
+ /**
+ * Get this OID as a string
+ * @return string representing this OID
+ */
+ std::string BOTAN_DEPRECATED("Use OID::to_string") as_string() const
+ {
+ return this->to_string();
+ }
+
+ /**
+ * Get this OID as a dotted-decimal string
+ * @return string representing this OID
+ */
+ std::string to_string() const;
+
+ /**
+ * If there is a known name associated with this OID, return that.
+ * Otherwise return the result of to_string
+ */
+ std::string to_formatted_string() const;
+
+ /**
+ * Compare two OIDs.
+ * @return true if they are equal, false otherwise
+ */
+ bool operator==(const OID& other) const
+ {
+ return m_id == other.m_id;
+ }
+
+ /**
+ * Reset this instance to an empty OID.
+ */
+ void BOTAN_DEPRECATED("Avoid mutation of OIDs") clear() { m_id.clear(); }
+
+ /**
+ * Add a component to this OID.
+ * @param new_comp the new component to add to the end of this OID
+ * @return reference to *this
+ */
+ BOTAN_DEPRECATED("Avoid mutation of OIDs") OID& operator+=(uint32_t new_comp)
+ {
+ m_id.push_back(new_comp);
+ return (*this);
+ }
+
+ private:
+ std::vector<uint32_t> m_id;
+ };
+
+/**
+* Append another component onto the OID.
+* @param oid the OID to add the new component to
+* @param new_comp the new component to add
+*/
+OID BOTAN_PUBLIC_API(2,0) operator+(const OID& oid, uint32_t new_comp);
+
+/**
+* Compare two OIDs.
+* @param a the first OID
+* @param b the second OID
+* @return true if a is not equal to b
+*/
+inline bool operator!=(const OID& a, const OID& b)
+ {
+ return !(a == b);
+ }
+
+/**
+* Compare two OIDs.
+* @param a the first OID
+* @param b the second OID
+* @return true if a is lexicographically smaller than b
+*/
+bool BOTAN_PUBLIC_API(2,0) 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(const std::string& t_spec, ASN1_Tag 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(const std::string& t_spec, ASN1_Tag);
+ 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_Tag m_tag = NO_OBJECT;
+ };
+
+/*
+* Comparison Operations
+*/
+bool BOTAN_PUBLIC_API(2,0) operator==(const ASN1_Time&, const ASN1_Time&);
+bool BOTAN_PUBLIC_API(2,0) operator!=(const ASN1_Time&, const ASN1_Time&);
+bool BOTAN_PUBLIC_API(2,0) operator<=(const ASN1_Time&, const ASN1_Time&);
+bool BOTAN_PUBLIC_API(2,0) operator>=(const ASN1_Time&, const ASN1_Time&);
+bool BOTAN_PUBLIC_API(2,0) operator<(const ASN1_Time&, const ASN1_Time&);
+bool BOTAN_PUBLIC_API(2,0) 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(class DER_Encoder&) const override;
+ void decode_from(class BER_Decoder&) override;
+
+ ASN1_Tag tagging() const { return m_tag; }
+
+ const std::string& value() const { return m_utf8_str; }
+
+ size_t size() const { return value().size(); }
+
+ bool empty() const { return m_utf8_str.empty(); }
+
+ std::string BOTAN_DEPRECATED("Use value() to get UTF-8 string instead")
+ iso_8859() const;
+
+ /**
+ * Return true iff this is a tag for a known string type we can handle.
+ * This ignores string types that are not supported, eg teletexString
+ */
+ static bool is_string_type(ASN1_Tag tag);
+
+ bool operator==(const ASN1_String& other) const
+ { return value() == other.value(); }
+
+ explicit ASN1_String(const std::string& utf8 = "");
+ ASN1_String(const std::string& utf8, ASN1_Tag tag);
+ private:
+ std::vector<uint8_t> m_data;
+ std::string m_utf8_str;
+ ASN1_Tag m_tag;
+ };
+
+/**
+* Algorithm Identifier
+*/
+class BOTAN_PUBLIC_API(2,0) AlgorithmIdentifier final : public ASN1_Object
+ {
+ public:
+ enum Encoding_Option { USE_NULL_PARAM, USE_EMPTY_PARAM };
+
+ void encode_into(class DER_Encoder&) const override;
+ void decode_from(class BER_Decoder&) override;
+
+ AlgorithmIdentifier() = default;
+
+ AlgorithmIdentifier(const OID& oid, Encoding_Option enc);
+ AlgorithmIdentifier(const std::string& oid_name, Encoding_Option enc);
+
+ AlgorithmIdentifier(const OID& oid, const std::vector<uint8_t>& params);
+ AlgorithmIdentifier(const std::string& oid_name, const std::vector<uint8_t>& params);
+
+ const OID& get_oid() const { return oid; }
+ const std::vector<uint8_t>& get_parameters() const { return parameters; }
+
+ bool parameters_are_null() const;
+ bool parameters_are_empty() const { return parameters.empty(); }
+
+ bool parameters_are_null_or_empty() const
+ {
+ return parameters_are_empty() || parameters_are_null();
+ }
+
+ BOTAN_DEPRECATED_PUBLIC_MEMBER_VARIABLES:
+ /*
+ * These values are public for historical reasons, but in a future release
+ * they will be made private. Do not access them.
+ */
+ OID oid;
+ std::vector<uint8_t> parameters;
+ };
+
+/*
+* Comparison Operations
+*/
+bool BOTAN_PUBLIC_API(2,0) operator==(const AlgorithmIdentifier&,
+ const AlgorithmIdentifier&);
+bool BOTAN_PUBLIC_API(2,0) operator!=(const AlgorithmIdentifier&,
+ const AlgorithmIdentifier&);
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/asn1/asn1_oid.cpp b/comm/third_party/botan/src/lib/asn1/asn1_oid.cpp
new file mode 100644
index 0000000000..cbbe3a4cbb
--- /dev/null
+++ b/comm/third_party/botan/src/lib/asn1/asn1_oid.cpp
@@ -0,0 +1,216 @@
+/*
+* ASN.1 OID
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/asn1_obj.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
+#include <botan/internal/bit_ops.h>
+#include <botan/parsing.h>
+#include <botan/oids.h>
+#include <algorithm>
+#include <sstream>
+
+namespace Botan {
+
+namespace {
+
+// returns empty on invalid
+std::vector<uint32_t> parse_oid_str(const std::string& oid)
+ {
+ try
+ {
+ std::string elem;
+ std::vector<uint32_t> oid_elems;
+
+ for(char c : oid)
+ {
+ if(c == '.')
+ {
+ if(elem.empty())
+ return std::vector<uint32_t>();
+ oid_elems.push_back(to_u32bit(elem));
+ elem.clear();
+ }
+ else
+ {
+ elem += c;
+ }
+ }
+
+ if(elem.empty())
+ return std::vector<uint32_t>();
+ oid_elems.push_back(to_u32bit(elem));
+
+ if(oid_elems.size() < 2)
+ return std::vector<uint32_t>();
+
+ return oid_elems;
+ }
+ catch(Invalid_Argument&) // thrown by to_u32bit
+ {
+ return std::vector<uint32_t>();
+ }
+ }
+
+}
+
+//static
+OID OID::from_string(const std::string& str)
+ {
+ if(str.empty())
+ throw Invalid_Argument("OID::from_string argument must be non-empty");
+
+ const OID o = OIDS::str2oid_or_empty(str);
+ if(o.has_value())
+ return o;
+
+ std::vector<uint32_t> raw = parse_oid_str(str);
+
+ if(raw.size() > 0)
+ return OID(std::move(raw));
+
+ throw Lookup_Error("No OID associated with name " + str);
+ }
+
+/*
+* ASN.1 OID Constructor
+*/
+OID::OID(const std::string& oid_str)
+ {
+ if(!oid_str.empty())
+ {
+ m_id = parse_oid_str(oid_str);
+
+ if(m_id.size() < 2 || m_id[0] > 2)
+ throw Invalid_OID(oid_str);
+ if((m_id[0] == 0 || m_id[0] == 1) && m_id[1] > 39)
+ throw Invalid_OID(oid_str);
+ }
+ }
+
+/*
+* Return this OID as a string
+*/
+std::string OID::to_string() const
+ {
+ std::ostringstream oss;
+ oss.imbue(std::locale("C"));
+ for(size_t i = 0; i != m_id.size(); ++i)
+ {
+ oss << m_id[i];
+ if(i != m_id.size() - 1)
+ oss << ".";
+ }
+ return oss.str();
+ }
+
+std::string OID::to_formatted_string() const
+ {
+ const std::string s = OIDS::oid2str_or_empty(*this);
+ if(!s.empty())
+ return s;
+ return this->to_string();
+ }
+
+/*
+* Append another component to the OID
+*/
+OID operator+(const OID& oid, uint32_t new_component)
+ {
+ std::vector<uint32_t> val = oid.get_components();
+ val.push_back(new_component);
+ return OID(std::move(val));
+ }
+
+/*
+* Compare two OIDs
+*/
+bool operator<(const OID& a, const OID& b)
+ {
+ const std::vector<uint32_t>& oid1 = a.get_components();
+ const std::vector<uint32_t>& oid2 = b.get_components();
+
+ return std::lexicographical_compare(oid1.begin(), oid1.end(),
+ oid2.begin(), oid2.end());
+ }
+
+/*
+* DER encode an OBJECT IDENTIFIER
+*/
+void OID::encode_into(DER_Encoder& der) const
+ {
+ if(m_id.size() < 2)
+ throw Invalid_Argument("OID::encode_into: OID is invalid");
+
+ std::vector<uint8_t> encoding;
+
+ if(m_id[0] > 2 || m_id[1] >= 40)
+ throw Encoding_Error("Invalid OID prefix, cannot encode");
+
+ encoding.push_back(static_cast<uint8_t>(40 * m_id[0] + m_id[1]));
+
+ for(size_t i = 2; i != m_id.size(); ++i)
+ {
+ if(m_id[i] == 0)
+ encoding.push_back(0);
+ else
+ {
+ size_t blocks = high_bit(m_id[i]) + 6;
+ blocks = (blocks - (blocks % 7)) / 7;
+
+ BOTAN_ASSERT(blocks > 0, "Math works");
+
+ for(size_t j = 0; j != blocks - 1; ++j)
+ encoding.push_back(0x80 | ((m_id[i] >> 7*(blocks-j-1)) & 0x7F));
+ encoding.push_back(m_id[i] & 0x7F);
+ }
+ }
+ der.add_object(OBJECT_ID, UNIVERSAL, encoding);
+ }
+
+/*
+* Decode a BER encoded OBJECT IDENTIFIER
+*/
+void OID::decode_from(BER_Decoder& decoder)
+ {
+ BER_Object obj = decoder.get_next_object();
+ if(obj.tagging() != OBJECT_ID)
+ throw BER_Bad_Tag("Error decoding OID, unknown tag", obj.tagging());
+
+ const size_t length = obj.length();
+ const uint8_t* bits = obj.bits();
+
+ if(length < 2 && !(length == 1 && bits[0] == 0))
+ {
+ throw BER_Decoding_Error("OID encoding is too short");
+ }
+
+ m_id.clear();
+ m_id.push_back(bits[0] / 40);
+ m_id.push_back(bits[0] % 40);
+
+ size_t i = 0;
+ while(i != length - 1)
+ {
+ uint32_t component = 0;
+ while(i != length - 1)
+ {
+ ++i;
+
+ if(component >> (32-7))
+ throw Decoding_Error("OID component overflow");
+
+ component = (component << 7) + (bits[i] & 0x7F);
+
+ if(!(bits[i] & 0x80))
+ break;
+ }
+ m_id.push_back(component);
+ }
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/asn1/asn1_oid.h b/comm/third_party/botan/src/lib/asn1/asn1_oid.h
new file mode 100644
index 0000000000..91c5da9d8d
--- /dev/null
+++ b/comm/third_party/botan/src/lib/asn1/asn1_oid.h
@@ -0,0 +1,14 @@
+/*
+* ASN.1 OID
+* (C) 1999-2007,2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_ASN1_OID_H_
+#define BOTAN_ASN1_OID_H_
+
+#include <botan/asn1_obj.h>
+BOTAN_DEPRECATED_HEADER(asn1_oid.h)
+
+#endif
diff --git a/comm/third_party/botan/src/lib/asn1/asn1_print.cpp b/comm/third_party/botan/src/lib/asn1/asn1_print.cpp
new file mode 100644
index 0000000000..faadad02b6
--- /dev/null
+++ b/comm/third_party/botan/src/lib/asn1/asn1_print.cpp
@@ -0,0 +1,327 @@
+/*
+* (C) 2014,2015,2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/asn1_print.h>
+#include <botan/bigint.h>
+#include <botan/hex.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
+#include <botan/oids.h>
+#include <iomanip>
+#include <sstream>
+#include <cctype>
+
+namespace Botan {
+
+namespace {
+
+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)
+ return false;
+ }
+ return true;
+ }
+
+/*
+* Special hack to handle GeneralName [2] and [6] (DNS name and URI)
+*/
+bool possibly_a_general_name(const uint8_t bits[], size_t bits_len)
+ {
+ if(bits_len <= 2)
+ return false;
+
+ if(bits[0] != 0x82 && bits[0] != 0x86)
+ return false;
+
+ if(bits[1] != bits_len - 2)
+ return false;
+
+ if(all_printable_chars(bits + 2, bits_len - 2) == false)
+ return false;
+
+ return true;
+ }
+
+}
+
+std::string ASN1_Formatter::print(const uint8_t in[], size_t len) const
+ {
+ std::ostringstream output;
+ print_to_stream(output, in, len);
+ return output.str();
+ }
+
+void ASN1_Formatter::print_to_stream(std::ostream& output,
+ const uint8_t in[],
+ size_t len) const
+ {
+ BER_Decoder dec(in, len);
+ decode(output, dec, 0);
+ }
+
+void ASN1_Formatter::decode(std::ostream& output,
+ BER_Decoder& decoder,
+ size_t level) const
+ {
+ BER_Object obj = decoder.get_next_object();
+
+ const bool recurse_deeper = (m_max_depth == 0 || level < m_max_depth);
+
+ while(obj.is_set())
+ {
+ const ASN1_Tag type_tag = obj.type();
+ const ASN1_Tag class_tag = obj.get_class();
+ const size_t length = obj.length();
+
+ /* hack to insert the tag+length back in front of the stuff now
+ that we've gotten the type info */
+ std::vector<uint8_t> bits;
+ DER_Encoder(bits).add_object(type_tag, class_tag, obj.bits(), obj.length());
+
+ BER_Decoder data(bits);
+
+ if(class_tag & CONSTRUCTED)
+ {
+ BER_Decoder cons_info(obj.bits(), obj.length());
+
+ if(recurse_deeper)
+ {
+ output << format(type_tag, class_tag, level, length, "");
+ decode(output, cons_info, level + 1); // recurse
+ }
+ else
+ {
+ output << format(type_tag, class_tag, level, length,
+ format_bin(type_tag, class_tag, bits));
+ }
+ }
+ else if((class_tag & APPLICATION) || (class_tag & CONTEXT_SPECIFIC))
+ {
+ bool success_parsing_cs = false;
+
+ 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));
+ success_parsing_cs = true;
+ }
+ else if(recurse_deeper)
+ {
+ std::vector<uint8_t> inner_bits;
+ data.decode(inner_bits, type_tag);
+
+ BER_Decoder inner(inner_bits);
+ std::ostringstream inner_data;
+ decode(inner_data, inner, level + 1); // recurse
+ output << inner_data.str();
+ success_parsing_cs = true;
+ }
+ }
+ catch(...)
+ {
+ }
+ }
+
+ if(success_parsing_cs == false)
+ {
+ output << format(type_tag, class_tag, level, length,
+ format_bin(type_tag, class_tag, bits));
+ }
+ }
+ else if(type_tag == OBJECT_ID)
+ {
+ OID oid;
+ data.decode(oid);
+
+ std::string out = OIDS::oid2str_or_empty(oid);
+ if(out.empty())
+ {
+ out = oid.to_string();
+ }
+ else
+ {
+ out += " [" + oid.to_string() + "]";
+ }
+
+ output << format(type_tag, class_tag, level, length, out);
+ }
+ else if(type_tag == INTEGER || type_tag == ENUMERATED)
+ {
+ BigInt number;
+
+ if(type_tag == INTEGER)
+ {
+ data.decode(number);
+ }
+ else if(type_tag == ENUMERATED)
+ {
+ data.decode(number, ENUMERATED, class_tag);
+ }
+
+ std::vector<uint8_t> rep = BigInt::encode(number);
+ if(rep.empty()) // if zero
+ rep.resize(1);
+
+ output << format(type_tag, class_tag, level, length, hex_encode(rep));
+ }
+ else if(type_tag == BOOLEAN)
+ {
+ bool boolean;
+ data.decode(boolean);
+ output << format(type_tag, class_tag, level, length, (boolean ? "true" : "false"));
+ }
+ else if(type_tag == NULL_TAG)
+ {
+ output << format(type_tag, class_tag, level, length, "");
+ }
+ else if(type_tag == OCTET_STRING || type_tag == BIT_STRING)
+ {
+ std::vector<uint8_t> decoded_bits;
+ data.decode(decoded_bits, type_tag);
+ bool printing_octet_string_worked = false;
+
+ if(recurse_deeper)
+ {
+ try
+ {
+ BER_Decoder inner(decoded_bits);
+
+ std::ostringstream inner_data;
+ decode(inner_data, inner, level + 1); // recurse
+
+ output << format(type_tag, class_tag, level, length, "");
+ output << inner_data.str();
+ printing_octet_string_worked = true;
+ }
+ catch(...)
+ {
+ }
+ }
+
+ if(!printing_octet_string_worked)
+ {
+ output << format(type_tag, class_tag, level, length,
+ format_bin(type_tag, class_tag, decoded_bits));
+ }
+ }
+ else if(ASN1_String::is_string_type(type_tag))
+ {
+ ASN1_String str;
+ data.decode(str);
+ output << format(type_tag, class_tag, level, length, str.value());
+ }
+ else if(type_tag == UTC_TIME || type_tag == GENERALIZED_TIME)
+ {
+ ASN1_Time time;
+ data.decode(time);
+ output << format(type_tag, class_tag, level, length, time.readable_string());
+ }
+ else
+ {
+ output << "Unknown ASN.1 tag class=" << static_cast<int>(class_tag)
+ << " type=" << static_cast<int>(type_tag) << "\n";
+ }
+
+ obj = decoder.get_next_object();
+ }
+ }
+
+namespace {
+
+std::string format_type(ASN1_Tag type_tag, ASN1_Tag class_tag)
+ {
+ if(class_tag == UNIVERSAL)
+ return asn1_tag_to_string(type_tag);
+
+ if(class_tag == CONSTRUCTED && (type_tag == SEQUENCE || type_tag == SET))
+ return asn1_tag_to_string(type_tag);
+
+ std::string name;
+
+ if(class_tag & CONSTRUCTED)
+ name += "cons ";
+
+ name += "[" + std::to_string(type_tag) + "]";
+
+ if(class_tag & APPLICATION)
+ {
+ name += " appl";
+ }
+ if(class_tag & CONTEXT_SPECIFIC)
+ {
+ name += " context";
+ }
+
+ return name;
+ }
+
+}
+
+std::string ASN1_Pretty_Printer::format(ASN1_Tag type_tag,
+ ASN1_Tag class_tag,
+ size_t level,
+ size_t length,
+ const std::string& value) const
+ {
+ bool should_skip = false;
+
+ if(value.length() > m_print_limit)
+ {
+ should_skip = true;
+ }
+
+ if((type_tag == OCTET_STRING || type_tag == BIT_STRING) &&
+ value.length() > m_print_binary_limit)
+ {
+ should_skip = true;
+ }
+
+ level += m_initial_level;
+
+ std::ostringstream oss;
+
+ oss << " d=" << std::setw(2) << level
+ << ", l=" << std::setw(4) << length << ":"
+ << std::string(level + 1, ' ') << format_type(type_tag, class_tag);
+
+ if(value != "" && !should_skip)
+ {
+ const size_t current_pos = static_cast<size_t>(oss.tellp());
+ const size_t spaces_to_align =
+ (current_pos >= m_value_column) ? 1 : (m_value_column - current_pos);
+
+ oss << std::string(spaces_to_align, ' ') << value;
+ }
+
+ oss << "\n";
+
+ return oss.str();
+ }
+
+std::string ASN1_Pretty_Printer::format_bin(ASN1_Tag /*type_tag*/,
+ ASN1_Tag /*class_tag*/,
+ const std::vector<uint8_t>& vec) const
+ {
+ if(all_printable_chars(vec.data(), vec.size()))
+ {
+ return std::string(cast_uint8_ptr_to_char(vec.data()), vec.size());
+ }
+ else
+ return hex_encode(vec);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/asn1/asn1_print.h b/comm/third_party/botan/src/lib/asn1/asn1_print.h
new file mode 100644
index 0000000000..a6bc6b15f2
--- /dev/null
+++ b/comm/third_party/botan/src/lib/asn1/asn1_print.h
@@ -0,0 +1,125 @@
+/*
+* (C) 2014,2015,2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_ASN1_PRINT_H_
+#define BOTAN_ASN1_PRINT_H_
+
+#include <botan/asn1_obj.h>
+#include <string>
+#include <vector>
+#include <iosfwd>
+
+namespace Botan {
+
+class BER_Decoder;
+
+/**
+* Format ASN.1 data and call a virtual to format
+*/
+class BOTAN_PUBLIC_API(2,4) ASN1_Formatter
+ {
+ public:
+ virtual ~ASN1_Formatter() = default;
+
+ /**
+ * @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.
+ */
+ ASN1_Formatter(bool print_context_specific, size_t max_depth) :
+ m_print_context_specific(print_context_specific),
+ m_max_depth(max_depth)
+ {}
+
+ void print_to_stream(std::ostream& out,
+ const uint8_t in[],
+ size_t len) const;
+
+ std::string print(const uint8_t in[], size_t len) const;
+
+ template<typename Alloc>
+ std::string print(const std::vector<uint8_t, Alloc>& vec) const
+ {
+ return print(vec.data(), vec.size());
+ }
+
+ protected:
+ /**
+ * This is called for each element
+ */
+ virtual std::string format(ASN1_Tag type_tag,
+ ASN1_Tag class_tag,
+ size_t level,
+ size_t length,
+ const std::string& value) const = 0;
+
+ /**
+ * 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.
+ */
+ virtual std::string format_bin(ASN1_Tag type_tag,
+ ASN1_Tag class_tag,
+ const std::vector<uint8_t>& vec) const = 0;
+
+ private:
+ void decode(std::ostream& output,
+ BER_Decoder& decoder,
+ size_t level) const;
+
+ const bool m_print_context_specific;
+ const size_t m_max_depth;
+ };
+
+/**
+* Format ASN.1 data into human readable output. The exact form of the output for
+* any particular input is not guaranteed and may change from release to release.
+*/
+class BOTAN_PUBLIC_API(2,4) ASN1_Pretty_Printer final : public ASN1_Formatter
+ {
+ public:
+ /**
+ * @param print_limit strings larger than this are not printed
+ * @param print_binary_limit binary strings larger than this are not printed
+ * @param print_context_specific if true, try to parse nested context specific data.
+ * @param initial_level the initial depth (0 or 1 are the only reasonable values)
+ * @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.
+ */
+ 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),
+ m_print_limit(print_limit),
+ m_print_binary_limit(print_binary_limit),
+ m_initial_level(initial_level),
+ m_value_column(value_column)
+ {}
+
+ private:
+ std::string format(ASN1_Tag type_tag,
+ ASN1_Tag class_tag,
+ size_t level,
+ size_t length,
+ const std::string& value) const override;
+
+ std::string format_bin(ASN1_Tag type_tag,
+ ASN1_Tag class_tag,
+ const std::vector<uint8_t>& vec) const override;
+
+ const size_t m_print_limit;
+ const size_t m_print_binary_limit;
+ const size_t m_initial_level;
+ const size_t m_value_column;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/asn1/asn1_str.cpp b/comm/third_party/botan/src/lib/asn1/asn1_str.cpp
new file mode 100644
index 0000000000..6a31c5bb2f
--- /dev/null
+++ b/comm/third_party/botan/src/lib/asn1/asn1_str.cpp
@@ -0,0 +1,153 @@
+/*
+* Simple ASN.1 String Types
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/asn1_obj.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
+#include <botan/charset.h>
+
+namespace Botan {
+
+namespace {
+
+/*
+* Choose an encoding for the string
+*/
+ASN1_Tag choose_encoding(const std::string& str)
+ {
+ static const uint8_t IS_PRINTABLE[256] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00,
+ 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00 };
+
+ for(size_t i = 0; i != str.size(); ++i)
+ {
+ if(!IS_PRINTABLE[static_cast<uint8_t>(str[i])])
+ {
+ return UTF8_STRING;
+ }
+ }
+ return PRINTABLE_STRING;
+ }
+
+void assert_is_string_type(ASN1_Tag tag)
+ {
+ if(!ASN1_String::is_string_type(tag))
+ {
+ throw Invalid_Argument("ASN1_String: Unknown string type " +
+ std::to_string(tag));
+ }
+ }
+
+}
+
+//static
+bool ASN1_String::is_string_type(ASN1_Tag tag)
+ {
+ return (tag == NUMERIC_STRING ||
+ tag == PRINTABLE_STRING ||
+ tag == VISIBLE_STRING ||
+ tag == T61_STRING ||
+ tag == IA5_STRING ||
+ tag == UTF8_STRING ||
+ tag == BMP_STRING ||
+ tag == UNIVERSAL_STRING);
+ }
+
+
+/*
+* Create an ASN1_String
+*/
+ASN1_String::ASN1_String(const std::string& str, ASN1_Tag t) : m_utf8_str(str), m_tag(t)
+ {
+ if(m_tag == DIRECTORY_STRING)
+ {
+ m_tag = choose_encoding(m_utf8_str);
+ }
+
+ assert_is_string_type(m_tag);
+ }
+
+/*
+* Create an ASN1_String
+*/
+ASN1_String::ASN1_String(const std::string& str) :
+ m_utf8_str(str),
+ m_tag(choose_encoding(m_utf8_str))
+ {}
+
+/*
+* Return this string in ISO 8859-1 encoding
+*/
+std::string ASN1_String::iso_8859() const
+ {
+ return utf8_to_latin1(m_utf8_str);
+ }
+
+/*
+* DER encode an ASN1_String
+*/
+void ASN1_String::encode_into(DER_Encoder& encoder) const
+ {
+ if(m_data.empty())
+ {
+ encoder.add_object(tagging(), UNIVERSAL, m_utf8_str);
+ }
+ else
+ {
+ // If this string was decoded, reserialize using original encoding
+ encoder.add_object(tagging(), UNIVERSAL, m_data.data(), m_data.size());
+ }
+ }
+
+/*
+* Decode a BER encoded ASN1_String
+*/
+void ASN1_String::decode_from(BER_Decoder& source)
+ {
+ BER_Object obj = source.get_next_object();
+
+ assert_is_string_type(obj.type());
+
+ m_tag = obj.type();
+ m_data.assign(obj.bits(), obj.bits() + obj.length());
+
+ if(m_tag == BMP_STRING)
+ {
+ m_utf8_str = ucs2_to_utf8(m_data.data(), m_data.size());
+ }
+ else if(m_tag == UNIVERSAL_STRING)
+ {
+ m_utf8_str = ucs4_to_utf8(m_data.data(), m_data.size());
+ }
+ else
+ {
+ // All other supported string types are UTF-8 or some subset thereof
+ m_utf8_str = ASN1::to_string(obj);
+ }
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/asn1/asn1_str.h b/comm/third_party/botan/src/lib/asn1/asn1_str.h
new file mode 100644
index 0000000000..fed4950cc3
--- /dev/null
+++ b/comm/third_party/botan/src/lib/asn1/asn1_str.h
@@ -0,0 +1,14 @@
+/*
+* ASN.1 string type
+* (C) 1999-2010 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_ASN1_STRING_H_
+#define BOTAN_ASN1_STRING_H_
+
+#include <botan/asn1_obj.h>
+BOTAN_DEPRECATED_HEADER(asn1_str.h)
+
+#endif
diff --git a/comm/third_party/botan/src/lib/asn1/asn1_time.cpp b/comm/third_party/botan/src/lib/asn1/asn1_time.cpp
new file mode 100644
index 0000000000..004be27b97
--- /dev/null
+++ b/comm/third_party/botan/src/lib/asn1/asn1_time.cpp
@@ -0,0 +1,290 @@
+/*
+* X.509 Time Types
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/asn1_obj.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
+#include <botan/exceptn.h>
+#include <botan/parsing.h>
+#include <botan/calendar.h>
+#include <sstream>
+#include <iomanip>
+
+namespace Botan {
+
+ASN1_Time::ASN1_Time(const std::chrono::system_clock::time_point& time)
+ {
+ calendar_point cal = calendar_value(time);
+
+ m_year = cal.get_year();
+ m_month = cal.get_month();
+ m_day = cal.get_day();
+ m_hour = cal.get_hour();
+ m_minute = cal.get_minutes();
+ m_second = cal.get_seconds();
+
+ m_tag = (m_year >= 2050) ? GENERALIZED_TIME : UTC_TIME;
+ }
+
+ASN1_Time::ASN1_Time(const std::string& t_spec, ASN1_Tag tag)
+ {
+ set_to(t_spec, tag);
+ }
+
+void ASN1_Time::encode_into(DER_Encoder& der) const
+ {
+ BOTAN_ARG_CHECK(m_tag == UTC_TIME || m_tag == GENERALIZED_TIME,
+ "ASN1_Time: Bad encoding tag");
+
+ der.add_object(m_tag, UNIVERSAL, to_string());
+ }
+
+void ASN1_Time::decode_from(BER_Decoder& source)
+ {
+ BER_Object ber_time = source.get_next_object();
+
+ set_to(ASN1::to_string(ber_time), ber_time.type());
+ }
+
+std::string ASN1_Time::to_string() const
+ {
+ if(time_is_set() == false)
+ throw Invalid_State("ASN1_Time::to_string: No time set");
+
+ uint32_t full_year = m_year;
+
+ if(m_tag == UTC_TIME)
+ {
+ if(m_year < 1950 || m_year >= 2050)
+ throw Encoding_Error("ASN1_Time: The time " + readable_string() +
+ " cannot be encoded as a UTCTime");
+
+ full_year = (m_year >= 2000) ? (m_year - 2000) : (m_year - 1900);
+ }
+
+ const uint64_t YEAR_FACTOR = 10000000000ULL;
+ const uint64_t MON_FACTOR = 100000000;
+ const uint64_t DAY_FACTOR = 1000000;
+ const uint64_t HOUR_FACTOR = 10000;
+ const uint64_t MIN_FACTOR = 100;
+
+ 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";
+
+ uint32_t desired_size = (m_tag == UTC_TIME) ? 13 : 15;
+
+ while(repr.size() < desired_size)
+ repr = "0" + repr;
+
+ return repr;
+ }
+
+std::string ASN1_Time::readable_string() const
+ {
+ if(time_is_set() == false)
+ throw Invalid_State("ASN1_Time::readable_string: No time set");
+
+ // desired format: "%04d/%02d/%02d %02d:%02d:%02d UTC"
+ std::stringstream output;
+ output << std::setfill('0')
+ << std::setw(4) << m_year << "/"
+ << std::setw(2) << m_month << "/"
+ << std::setw(2) << m_day
+ << " "
+ << std::setw(2) << m_hour << ":"
+ << std::setw(2) << m_minute << ":"
+ << std::setw(2) << m_second
+ << " UTC";
+
+ return output.str();
+ }
+
+bool ASN1_Time::time_is_set() const
+ {
+ return (m_year != 0);
+ }
+
+int32_t ASN1_Time::cmp(const ASN1_Time& other) const
+ {
+ if(time_is_set() == false)
+ throw Invalid_State("ASN1_Time::cmp: No time set");
+
+ const int32_t EARLIER = -1, LATER = 1, SAME_TIME = 0;
+
+ if(m_year < other.m_year) return EARLIER;
+ if(m_year > other.m_year) return LATER;
+ if(m_month < other.m_month) return EARLIER;
+ if(m_month > other.m_month) return LATER;
+ if(m_day < other.m_day) return EARLIER;
+ if(m_day > other.m_day) return LATER;
+ if(m_hour < other.m_hour) return EARLIER;
+ if(m_hour > other.m_hour) return LATER;
+ if(m_minute < other.m_minute) return EARLIER;
+ if(m_minute > other.m_minute) return LATER;
+ if(m_second < other.m_second) return EARLIER;
+ if(m_second > other.m_second) return LATER;
+
+ return SAME_TIME;
+ }
+
+void ASN1_Time::set_to(const std::string& t_spec, ASN1_Tag spec_tag)
+ {
+ if(spec_tag == UTC_OR_GENERALIZED_TIME)
+ {
+ try
+ {
+ set_to(t_spec, GENERALIZED_TIME);
+ return;
+ }
+ catch(Invalid_Argument&) {} // Not a generalized time. Continue
+
+ try
+ {
+ set_to(t_spec, UTC_TIME);
+ return;
+ }
+ catch(Invalid_Argument&) {} // Not a UTC time. Continue
+
+ throw Invalid_Argument("Time string could not be parsed as GeneralizedTime or UTCTime.");
+ }
+
+ BOTAN_ASSERT(spec_tag == UTC_TIME || spec_tag == GENERALIZED_TIME, "Invalid tag.");
+
+ BOTAN_ARG_CHECK(t_spec.size() > 0, "Time string must not be empty.");
+
+ BOTAN_ARG_CHECK(t_spec.back() == 'Z', "Botan does not support times with timezones other than Z");
+
+ if(spec_tag == GENERALIZED_TIME)
+ {
+ BOTAN_ARG_CHECK(t_spec.size() == 15, "Invalid GeneralizedTime string");
+ }
+ else if(spec_tag == UTC_TIME)
+ {
+ BOTAN_ARG_CHECK(t_spec.size() == 13, "Invalid UTCTime string");
+ }
+
+ const size_t YEAR_SIZE = (spec_tag == UTC_TIME) ? 2 : 4;
+
+ std::vector<std::string> params;
+ std::string current;
+
+ for(size_t j = 0; j != YEAR_SIZE; ++j)
+ current += t_spec[j];
+ params.push_back(current);
+ current.clear();
+
+ for(size_t j = YEAR_SIZE; j != t_spec.size() - 1; ++j)
+ {
+ current += t_spec[j];
+ if(current.size() == 2)
+ {
+ params.push_back(current);
+ current.clear();
+ }
+ }
+
+ m_year = to_u32bit(params[0]);
+ m_month = to_u32bit(params[1]);
+ m_day = to_u32bit(params[2]);
+ m_hour = to_u32bit(params[3]);
+ m_minute = to_u32bit(params[4]);
+ m_second = (params.size() == 6) ? to_u32bit(params[5]) : 0;
+ m_tag = spec_tag;
+
+ if(spec_tag == UTC_TIME)
+ {
+ if(m_year >= 50) m_year += 1900;
+ else m_year += 2000;
+ }
+
+ if(!passes_sanity_check())
+ throw Invalid_Argument("Time " + t_spec + " does not seem to be valid");
+ }
+
+/*
+* Do a general sanity check on the time
+*/
+bool ASN1_Time::passes_sanity_check() const
+ {
+ // AppVeyor's trust store includes a cert with expiration date in 3016 ...
+ if(m_year < 1950 || m_year > 3100)
+ return false;
+ if(m_month == 0 || m_month > 12)
+ return false;
+
+ const uint32_t days_in_month[12] = { 31, 28+1, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+
+ if(m_day == 0 || m_day > days_in_month[m_month-1])
+ return false;
+
+ if(m_month == 2 && m_day == 29)
+ {
+ if(m_year % 4 != 0)
+ return false; // not a leap year
+
+ if(m_year % 100 == 0 && m_year % 400 != 0)
+ return false;
+ }
+
+ if(m_hour >= 24 || m_minute >= 60 || m_second > 60)
+ return false;
+
+ if (m_tag == UTC_TIME)
+ {
+ /*
+ UTCTime limits the value of components such that leap seconds
+ are not covered. See "UNIVERSAL 23" in "Information technology
+ Abstract Syntax Notation One (ASN.1): Specification of basic notation"
+
+ http://www.itu.int/ITU-T/studygroups/com17/languages/
+ */
+ if(m_second > 59)
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+std::chrono::system_clock::time_point ASN1_Time::to_std_timepoint() const
+ {
+ return calendar_point(m_year, m_month, m_day, m_hour, m_minute, m_second).to_std_timepoint();
+ }
+
+uint64_t ASN1_Time::time_since_epoch() const
+ {
+ auto tp = this->to_std_timepoint();
+ return std::chrono::duration_cast<std::chrono::seconds>(tp.time_since_epoch()).count();
+ }
+
+/*
+* Compare two ASN1_Times for in various ways
+*/
+bool operator==(const ASN1_Time& t1, const ASN1_Time& t2)
+ { return (t1.cmp(t2) == 0); }
+bool operator!=(const ASN1_Time& t1, const ASN1_Time& t2)
+ { return (t1.cmp(t2) != 0); }
+
+bool operator<=(const ASN1_Time& t1, const ASN1_Time& t2)
+ { return (t1.cmp(t2) <= 0); }
+bool operator>=(const ASN1_Time& t1, const ASN1_Time& t2)
+ { return (t1.cmp(t2) >= 0); }
+
+bool operator<(const ASN1_Time& t1, const ASN1_Time& t2)
+ { return (t1.cmp(t2) < 0); }
+bool operator>(const ASN1_Time& t1, const ASN1_Time& t2)
+ { return (t1.cmp(t2) > 0); }
+
+}
diff --git a/comm/third_party/botan/src/lib/asn1/asn1_time.h b/comm/third_party/botan/src/lib/asn1/asn1_time.h
new file mode 100644
index 0000000000..55ef82e052
--- /dev/null
+++ b/comm/third_party/botan/src/lib/asn1/asn1_time.h
@@ -0,0 +1,14 @@
+/*
+* ASN.1 Time Representation
+* (C) 1999-2007,2012 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_ASN1_TIME_H_
+#define BOTAN_ASN1_TIME_H_
+
+#include <botan/asn1_obj.h>
+BOTAN_DEPRECATED_HEADER(asn1_time.h)
+
+#endif
diff --git a/comm/third_party/botan/src/lib/asn1/ber_dec.cpp b/comm/third_party/botan/src/lib/asn1/ber_dec.cpp
new file mode 100644
index 0000000000..c5af2f9338
--- /dev/null
+++ b/comm/third_party/botan/src/lib/asn1/ber_dec.cpp
@@ -0,0 +1,549 @@
+/*
+* BER Decoder
+* (C) 1999-2008,2015,2017,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/ber_dec.h>
+#include <botan/bigint.h>
+#include <botan/loadstor.h>
+#include <botan/internal/safeint.h>
+
+namespace Botan {
+
+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;
+
+/*
+* BER decode an ASN.1 type tag
+*/
+size_t decode_tag(DataSource* ber, ASN1_Tag& type_tag, ASN1_Tag& class_tag)
+ {
+ uint8_t b;
+ if(!ber->read_byte(b))
+ {
+ class_tag = type_tag = NO_OBJECT;
+ return 0;
+ }
+
+ if((b & 0x1F) != 0x1F)
+ {
+ type_tag = ASN1_Tag(b & 0x1F);
+ class_tag = ASN1_Tag(b & 0xE0);
+ return 1;
+ }
+
+ size_t tag_bytes = 1;
+ class_tag = ASN1_Tag(b & 0xE0);
+
+ size_t tag_buf = 0;
+ while(true)
+ {
+ if(!ber->read_byte(b))
+ throw BER_Decoding_Error("Long-form tag truncated");
+ if(tag_buf & 0xFF000000)
+ throw BER_Decoding_Error("Long-form tag overflowed 32 bits");
+ ++tag_bytes;
+ tag_buf = (tag_buf << 7) | (b & 0x7F);
+ if((b & 0x80) == 0) break;
+ }
+ type_tag = ASN1_Tag(tag_buf);
+ return tag_bytes;
+ }
+
+/*
+* Find the EOC marker
+*/
+size_t find_eoc(DataSource* src, size_t allow_indef);
+
+/*
+* 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))
+ throw BER_Decoding_Error("Length field not found");
+ field_size = 1;
+ if((b & 0x80) == 0)
+ return b;
+
+ field_size += (b & 0x7F);
+ if(field_size > 5)
+ throw BER_Decoding_Error("Length field is too large");
+
+ if(field_size == 1)
+ {
+ 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);
+ }
+ }
+
+ 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))
+ throw BER_Decoding_Error("Corrupted length field");
+ length = (length << 8) | b;
+ }
+ return length;
+ }
+
+/*
+* Find the EOC marker
+*/
+size_t find_eoc(DataSource* ber, size_t allow_indef)
+ {
+ secure_vector<uint8_t> buffer(BOTAN_DEFAULT_BUFFER_SIZE), data;
+
+ while(true)
+ {
+ const size_t got = ber->peek(buffer.data(), buffer.size(), data.size());
+ if(got == 0)
+ break;
+
+ data += std::make_pair(buffer.data(), got);
+ }
+
+ DataSource_Memory source(data);
+ data.clear();
+
+ size_t length = 0;
+ while(true)
+ {
+ ASN1_Tag type_tag, class_tag;
+ size_t tag_size = decode_tag(&source, type_tag, class_tag);
+ if(type_tag == NO_OBJECT)
+ break;
+
+ size_t length_size = 0;
+ size_t item_size = decode_length(&source, length_size, allow_indef);
+ source.discard_next(item_size);
+
+ length = BOTAN_CHECKED_ADD(length, item_size);
+ length = BOTAN_CHECKED_ADD(length, tag_size);
+ length = BOTAN_CHECKED_ADD(length, length_size);
+
+ if(type_tag == EOC && class_tag == UNIVERSAL)
+ break;
+ }
+ return length;
+ }
+
+class DataSource_BERObject final : public DataSource
+ {
+ public:
+ size_t read(uint8_t out[], size_t length) override
+ {
+ BOTAN_ASSERT_NOMSG(m_offset <= m_obj.length());
+ const size_t got = std::min<size_t>(m_obj.length() - m_offset, length);
+ copy_mem(out, m_obj.bits() + m_offset, got);
+ m_offset += got;
+ return got;
+ }
+
+ size_t peek(uint8_t out[], size_t length, size_t peek_offset) const override
+ {
+ BOTAN_ASSERT_NOMSG(m_offset <= m_obj.length());
+ const size_t bytes_left = m_obj.length() - m_offset;
+
+ if(peek_offset >= bytes_left)
+ return 0;
+
+ const size_t got = std::min(bytes_left - peek_offset, length);
+ copy_mem(out, m_obj.bits() + peek_offset, got);
+ return got;
+ }
+
+ bool check_available(size_t n) override
+ {
+ BOTAN_ASSERT_NOMSG(m_offset <= m_obj.length());
+ return (n <= (m_obj.length() - m_offset));
+ }
+
+ bool end_of_data() const override
+ {
+ return get_bytes_read() == m_obj.length();
+ }
+
+ size_t get_bytes_read() const override { return m_offset; }
+
+ explicit DataSource_BERObject(BER_Object&& obj) : m_obj(std::move(obj)), m_offset(0) {}
+
+ private:
+ BER_Object m_obj;
+ size_t m_offset;
+ };
+
+}
+
+/*
+* Check if more objects are there
+*/
+bool BER_Decoder::more_items() const
+ {
+ if(m_source->end_of_data() && !m_pushed.is_set())
+ return false;
+ return true;
+ }
+
+/*
+* Verify that no bytes remain in the source
+*/
+BER_Decoder& BER_Decoder::verify_end()
+ {
+ return verify_end("BER_Decoder::verify_end called, but data remains");
+ }
+
+/*
+* Verify that no bytes remain in the source
+*/
+BER_Decoder& BER_Decoder::verify_end(const std::string& err)
+ {
+ if(!m_source->end_of_data() || m_pushed.is_set())
+ throw Decoding_Error(err);
+ return (*this);
+ }
+
+/*
+* Discard all the bytes remaining in the source
+*/
+BER_Decoder& BER_Decoder::discard_remaining()
+ {
+ uint8_t buf;
+ while(m_source->read_byte(buf))
+ {}
+ return (*this);
+ }
+
+/*
+* Return the BER encoding of the next object
+*/
+BER_Object BER_Decoder::get_next_object()
+ {
+ BER_Object next;
+
+ if(m_pushed.is_set())
+ {
+ std::swap(next, m_pushed);
+ return next;
+ }
+
+ for(;;)
+ {
+ ASN1_Tag type_tag, class_tag;
+ 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))
+ throw BER_Decoding_Error("Value truncated");
+
+ uint8_t* out = next.mutable_bits(length);
+ if(m_source->read(out, length) != length)
+ throw BER_Decoding_Error("Value truncated");
+
+ if(next.tagging() == EOC)
+ continue;
+ else
+ break;
+ }
+
+ return next;
+ }
+
+/*
+* Push a object back into the stream
+*/
+void BER_Decoder::push_back(const BER_Object& obj)
+ {
+ if(m_pushed.is_set())
+ throw Invalid_State("BER_Decoder: Only one push back is allowed");
+ m_pushed = obj;
+ }
+
+void BER_Decoder::push_back(BER_Object&& obj)
+ {
+ if(m_pushed.is_set())
+ throw Invalid_State("BER_Decoder: Only one push back is allowed");
+ m_pushed = std::move(obj);
+ }
+
+BER_Decoder BER_Decoder::start_cons(ASN1_Tag type_tag, ASN1_Tag class_tag)
+ {
+ BER_Object obj = get_next_object();
+ obj.assert_is_a(type_tag, ASN1_Tag(class_tag | CONSTRUCTED));
+ return BER_Decoder(std::move(obj), this);
+ }
+
+/*
+* Finish decoding a CONSTRUCTED type
+*/
+BER_Decoder& BER_Decoder::end_cons()
+ {
+ if(!m_parent)
+ throw Invalid_State("BER_Decoder::end_cons called with null parent");
+ if(!m_source->end_of_data())
+ throw Decoding_Error("BER_Decoder::end_cons called with data left");
+ return (*m_parent);
+ }
+
+BER_Decoder::BER_Decoder(BER_Object&& obj, BER_Decoder* parent)
+ {
+ m_data_src.reset(new DataSource_BERObject(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 Constructor
+ */
+BER_Decoder::BER_Decoder(const uint8_t data[], size_t length)
+ {
+ m_data_src.reset(new DataSource_Memory(data, length));
+ m_source = m_data_src.get();
+ }
+
+/*
+* BER_Decoder Constructor
+*/
+BER_Decoder::BER_Decoder(const secure_vector<uint8_t>& data)
+ {
+ m_data_src.reset(new DataSource_Memory(data));
+ m_source = m_data_src.get();
+ }
+
+/*
+* BER_Decoder Constructor
+*/
+BER_Decoder::BER_Decoder(const std::vector<uint8_t>& data)
+ {
+ m_data_src.reset(new DataSource_Memory(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;
+ }
+
+/*
+* Request for an object to decode itself
+*/
+BER_Decoder& BER_Decoder::decode(ASN1_Object& obj,
+ ASN1_Tag, ASN1_Tag)
+ {
+ obj.decode_from(*this);
+ return (*this);
+ }
+
+/*
+* Decode a BER encoded NULL
+*/
+BER_Decoder& BER_Decoder::decode_null()
+ {
+ BER_Object obj = get_next_object();
+ obj.assert_is_a(NULL_TAG, UNIVERSAL);
+ if(obj.length() > 0)
+ throw BER_Decoding_Error("NULL object had nonzero size");
+ return (*this);
+ }
+
+BER_Decoder& BER_Decoder::decode_octet_string_bigint(BigInt& out)
+ {
+ secure_vector<uint8_t> out_vec;
+ decode(out_vec, OCTET_STRING);
+ out = BigInt::decode(out_vec.data(), out_vec.size());
+ return (*this);
+ }
+
+/*
+* Decode a BER encoded BOOLEAN
+*/
+BER_Decoder& BER_Decoder::decode(bool& out,
+ ASN1_Tag type_tag, ASN1_Tag class_tag)
+ {
+ 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;
+ return (*this);
+ }
+
+/*
+* Decode a small BER encoded INTEGER
+*/
+BER_Decoder& BER_Decoder::decode(size_t& out,
+ ASN1_Tag type_tag,
+ ASN1_Tag class_tag)
+ {
+ 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() > 32)
+ throw BER_Decoding_Error("Decoded integer value larger than expected");
+
+ out = 0;
+ for(size_t i = 0; i != 4; ++i)
+ out = (out << 8) | integer.byte_at(3-i);
+
+ return (*this);
+ }
+
+/*
+* Decode a small BER encoded INTEGER
+*/
+uint64_t BER_Decoder::decode_constrained_integer(ASN1_Tag type_tag,
+ ASN1_Tag class_tag,
+ size_t T_bytes)
+ {
+ if(T_bytes > 8)
+ throw BER_Decoding_Error("Can't decode small integer over 8 bytes");
+
+ BigInt integer;
+ decode(integer, type_tag, class_tag);
+
+ if(integer.bits() > 8*T_bytes)
+ throw BER_Decoding_Error("Decoded integer value larger than expected");
+
+ uint64_t out = 0;
+ for(size_t i = 0; i != 8; ++i)
+ out = (out << 8) | integer.byte_at(7-i);
+
+ return out;
+ }
+
+/*
+* Decode a BER encoded INTEGER
+*/
+BER_Decoder& BER_Decoder::decode(BigInt& out,
+ ASN1_Tag type_tag,
+ ASN1_Tag class_tag)
+ {
+ BER_Object obj = get_next_object();
+ obj.assert_is_a(type_tag, class_tag);
+
+ if(obj.length() == 0)
+ {
+ out = 0;
+ }
+ else
+ {
+ const bool negative = (obj.bits()[0] & 0x80) ? true : false;
+
+ if(negative)
+ {
+ secure_vector<uint8_t> vec(obj.bits(), obj.bits() + obj.length());
+ for(size_t i = obj.length(); i > 0; --i)
+ if(vec[i-1]--)
+ break;
+ for(size_t i = 0; i != obj.length(); ++i)
+ vec[i] = ~vec[i];
+ out = BigInt(vec.data(), vec.size());
+ out.flip_sign();
+ }
+ else
+ {
+ out = BigInt(obj.bits(), obj.length());
+ }
+ }
+
+ return (*this);
+ }
+
+namespace {
+
+template<typename Alloc>
+void asn1_decode_binary_string(std::vector<uint8_t, Alloc>& buffer,
+ const BER_Object& obj,
+ ASN1_Tag real_type,
+ ASN1_Tag type_tag,
+ ASN1_Tag class_tag)
+ {
+ obj.assert_is_a(type_tag, class_tag);
+
+ if(real_type == OCTET_STRING)
+ {
+ 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)
+ throw BER_Decoding_Error("Bad number of unused bits in BIT STRING");
+
+ buffer.resize(obj.length() - 1);
+
+ if(obj.length() > 1)
+ copy_mem(buffer.data(), obj.bits() + 1, obj.length() - 1);
+ }
+ }
+
+}
+
+/*
+* BER decode a BIT STRING or OCTET STRING
+*/
+BER_Decoder& BER_Decoder::decode(secure_vector<uint8_t>& buffer,
+ ASN1_Tag real_type,
+ ASN1_Tag type_tag, ASN1_Tag class_tag)
+ {
+ if(real_type != OCTET_STRING && real_type != BIT_STRING)
+ throw BER_Bad_Tag("Bad tag for {BIT,OCTET} STRING", real_type);
+
+ asn1_decode_binary_string(buffer, get_next_object(), real_type, type_tag, class_tag);
+ return (*this);
+ }
+
+BER_Decoder& BER_Decoder::decode(std::vector<uint8_t>& buffer,
+ ASN1_Tag real_type,
+ ASN1_Tag type_tag, ASN1_Tag class_tag)
+ {
+ if(real_type != OCTET_STRING && real_type != BIT_STRING)
+ throw BER_Bad_Tag("Bad tag for {BIT,OCTET} STRING", real_type);
+
+ asn1_decode_binary_string(buffer, get_next_object(), real_type, type_tag, class_tag);
+ return (*this);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/asn1/ber_dec.h b/comm/third_party/botan/src/lib/asn1/ber_dec.h
new file mode 100644
index 0000000000..1fb8c4a9da
--- /dev/null
+++ b/comm/third_party/botan/src/lib/asn1/ber_dec.h
@@ -0,0 +1,418 @@
+/*
+* BER Decoder
+* (C) 1999-2010,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_BER_DECODER_H_
+#define BOTAN_BER_DECODER_H_
+
+#include <botan/asn1_obj.h>
+#include <botan/data_src.h>
+
+namespace Botan {
+
+class BigInt;
+
+/**
+* BER Decoding Object
+*/
+class BOTAN_PUBLIC_API(2,0) BER_Decoder final
+ {
+ public:
+ /**
+ * Set up to BER decode the data in buf of length len
+ */
+ BER_Decoder(const uint8_t buf[], size_t len);
+
+ /**
+ * Set up to BER decode the data in vec
+ */
+ explicit BER_Decoder(const secure_vector<uint8_t>& vec);
+
+ /**
+ * Set up to BER decode the data in vec
+ */
+ explicit BER_Decoder(const std::vector<uint8_t>& vec);
+
+ /**
+ * Set up to BER decode the data in src
+ */
+ explicit BER_Decoder(DataSource& src);
+
+ /**
+ * Set up to BER decode the data in obj
+ */
+ BER_Decoder(const BER_Object& obj) :
+ BER_Decoder(obj.bits(), obj.length()) {}
+
+ /**
+ * Set up to BER decode the data in obj
+ */
+ BER_Decoder(BER_Object&& obj) :
+ BER_Decoder(std::move(obj), nullptr) {}
+
+ BER_Decoder(const BER_Decoder& other);
+
+ BER_Decoder& operator=(const BER_Decoder&) = delete;
+
+ /**
+ * Get the next object in the data stream.
+ * If EOF, returns an object with type NO_OBJECT.
+ */
+ BER_Object get_next_object();
+
+ BER_Decoder& get_next(BER_Object& ber)
+ {
+ ber = get_next_object();
+ return (*this);
+ }
+
+ /**
+ * Push an object back onto the stream. Throws if another
+ * object was previously pushed and has not been subsequently
+ * read out.
+ */
+ void push_back(const BER_Object& obj);
+
+ /**
+ * Push an object back onto the stream. Throws if another
+ * object was previously pushed and has not been subsequently
+ * read out.
+ */
+ void push_back(BER_Object&& obj);
+
+ /**
+ * Return true if there is at least one more item remaining
+ */
+ bool more_items() const;
+
+ /**
+ * Verify the stream is concluded, throws otherwise.
+ * Returns (*this)
+ */
+ BER_Decoder& verify_end();
+
+ /**
+ * Verify the stream is concluded, throws otherwise.
+ * Returns (*this)
+ */
+ BER_Decoder& verify_end(const std::string& err_msg);
+
+ /**
+ * Discard any data that remains unread
+ * Returns (*this)
+ */
+ BER_Decoder& discard_remaining();
+
+ /**
+ * Start decoding a constructed data (sequence or set)
+ */
+ BER_Decoder start_cons(ASN1_Tag type_tag, ASN1_Tag class_tag = UNIVERSAL);
+
+ /**
+ * Finish decoding a constructed data, throws if any data remains.
+ * Returns the parent of *this (ie the object on which start_cons was called).
+ */
+ BER_Decoder& end_cons();
+
+ /**
+ * Get next object and copy value to POD type
+ * Asserts value length is equal to POD type sizeof.
+ * Asserts Type tag and optional Class tag according to parameters.
+ * Copy value to POD type (struct, union, C-style array, std::array, etc.).
+ * @param out POD type reference where to copy object value
+ * @param type_tag ASN1_Tag enum to assert type on object read
+ * @param class_tag ASN1_Tag enum to assert class on object read (default: CONTEXT_SPECIFIC)
+ * @return this reference
+ */
+ template <typename T>
+ BER_Decoder& get_next_value(T &out,
+ ASN1_Tag type_tag,
+ ASN1_Tag class_tag = CONTEXT_SPECIFIC)
+ {
+ static_assert(std::is_standard_layout<T>::value && std::is_trivial<T>::value, "Type must be POD");
+
+ 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)));
+
+ copy_mem(reinterpret_cast<uint8_t*>(&out), obj.bits(), obj.length());
+
+ return (*this);
+ }
+
+ /*
+ * Save all the bytes remaining in the source
+ */
+ template<typename Alloc>
+ BER_Decoder& raw_bytes(std::vector<uint8_t, Alloc>& out)
+ {
+ out.clear();
+ uint8_t buf;
+ while(m_source->read_byte(buf))
+ out.push_back(buf);
+ return (*this);
+ }
+
+ BER_Decoder& decode_null();
+
+ /**
+ * Decode a BER encoded BOOLEAN
+ */
+ BER_Decoder& decode(bool& out)
+ {
+ return decode(out, BOOLEAN, UNIVERSAL);
+ }
+
+ /*
+ * Decode a small BER encoded INTEGER
+ */
+ BER_Decoder& decode(size_t& out)
+ {
+ return decode(out, INTEGER, UNIVERSAL);
+ }
+
+ /*
+ * Decode a BER encoded INTEGER
+ */
+ BER_Decoder& decode(BigInt& out)
+ {
+ return decode(out, INTEGER, UNIVERSAL);
+ }
+
+ std::vector<uint8_t> get_next_octet_string()
+ {
+ std::vector<uint8_t> out_vec;
+ decode(out_vec, OCTET_STRING);
+ return out_vec;
+ }
+
+ /*
+ * BER decode a BIT STRING or OCTET STRING
+ */
+ template<typename Alloc>
+ BER_Decoder& decode(std::vector<uint8_t, Alloc>& out, ASN1_Tag real_type)
+ {
+ return decode(out, real_type, real_type, UNIVERSAL);
+ }
+
+ BER_Decoder& decode(bool& v,
+ ASN1_Tag type_tag,
+ ASN1_Tag class_tag = CONTEXT_SPECIFIC);
+
+ BER_Decoder& decode(size_t& v,
+ ASN1_Tag type_tag,
+ ASN1_Tag class_tag = CONTEXT_SPECIFIC);
+
+ BER_Decoder& decode(BigInt& v,
+ ASN1_Tag type_tag,
+ ASN1_Tag class_tag = CONTEXT_SPECIFIC);
+
+ BER_Decoder& decode(std::vector<uint8_t>& v,
+ ASN1_Tag real_type,
+ ASN1_Tag type_tag,
+ ASN1_Tag class_tag = CONTEXT_SPECIFIC);
+
+ BER_Decoder& decode(secure_vector<uint8_t>& v,
+ ASN1_Tag real_type,
+ ASN1_Tag type_tag,
+ ASN1_Tag class_tag = CONTEXT_SPECIFIC);
+
+ BER_Decoder& decode(class ASN1_Object& obj,
+ ASN1_Tag type_tag = NO_OBJECT,
+ ASN1_Tag class_tag = NO_OBJECT);
+
+ /**
+ * Decode an integer value which is typed as an octet string
+ */
+ BER_Decoder& decode_octet_string_bigint(BigInt& b);
+
+ uint64_t decode_constrained_integer(ASN1_Tag type_tag,
+ ASN1_Tag class_tag,
+ size_t T_bytes);
+
+ template<typename T> BER_Decoder& decode_integer_type(T& out)
+ {
+ return decode_integer_type<T>(out, INTEGER, UNIVERSAL);
+ }
+
+ template<typename T>
+ BER_Decoder& decode_integer_type(T& out,
+ ASN1_Tag type_tag,
+ ASN1_Tag class_tag = CONTEXT_SPECIFIC)
+ {
+ out = static_cast<T>(decode_constrained_integer(type_tag, class_tag, sizeof(out)));
+ return (*this);
+ }
+
+ template<typename T>
+ BER_Decoder& decode_optional(T& out,
+ ASN1_Tag type_tag,
+ ASN1_Tag class_tag,
+ const T& default_value = T());
+
+ template<typename T>
+ BER_Decoder& decode_optional_implicit(
+ T& out,
+ ASN1_Tag type_tag,
+ ASN1_Tag class_tag,
+ ASN1_Tag real_type,
+ ASN1_Tag real_class,
+ const T& default_value = T());
+
+ template<typename T>
+ BER_Decoder& decode_list(std::vector<T>& out,
+ ASN1_Tag type_tag = SEQUENCE,
+ ASN1_Tag class_tag = UNIVERSAL);
+
+ template<typename T>
+ BER_Decoder& decode_and_check(const T& expected,
+ const std::string& error_msg)
+ {
+ T actual;
+ decode(actual);
+
+ if(actual != expected)
+ throw Decoding_Error(error_msg);
+
+ return (*this);
+ }
+
+ /*
+ * Decode an OPTIONAL string type
+ */
+ template<typename Alloc>
+ BER_Decoder& decode_optional_string(std::vector<uint8_t, Alloc>& out,
+ ASN1_Tag real_type,
+ uint16_t type_no,
+ ASN1_Tag class_tag = CONTEXT_SPECIFIC)
+ {
+ BER_Object obj = get_next_object();
+
+ ASN1_Tag type_tag = static_cast<ASN1_Tag>(type_no);
+
+ if(obj.is_a(type_tag, class_tag))
+ {
+ if((class_tag & CONSTRUCTED) && (class_tag & CONTEXT_SPECIFIC))
+ {
+ BER_Decoder(std::move(obj)).decode(out, real_type).verify_end();
+ }
+ else
+ {
+ push_back(std::move(obj));
+ decode(out, real_type, type_tag, class_tag);
+ }
+ }
+ else
+ {
+ out.clear();
+ push_back(std::move(obj));
+ }
+
+ return (*this);
+ }
+
+ private:
+ BER_Decoder(BER_Object&& obj, BER_Decoder* parent);
+
+ 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<DataSource> m_data_src;
+ };
+
+/*
+* Decode an OPTIONAL or DEFAULT element
+*/
+template<typename T>
+BER_Decoder& BER_Decoder::decode_optional(T& out,
+ ASN1_Tag type_tag,
+ ASN1_Tag class_tag,
+ const T& default_value)
+ {
+ BER_Object obj = get_next_object();
+
+ if(obj.is_a(type_tag, class_tag))
+ {
+ if((class_tag & CONSTRUCTED) && (class_tag & CONTEXT_SPECIFIC))
+ {
+ BER_Decoder(std::move(obj)).decode(out).verify_end();
+ }
+ else
+ {
+ push_back(std::move(obj));
+ decode(out, type_tag, class_tag);
+ }
+ }
+ else
+ {
+ out = default_value;
+ push_back(std::move(obj));
+ }
+
+ return (*this);
+ }
+
+/*
+* Decode an OPTIONAL or DEFAULT element
+*/
+template<typename T>
+BER_Decoder& BER_Decoder::decode_optional_implicit(
+ T& out,
+ ASN1_Tag type_tag,
+ ASN1_Tag class_tag,
+ ASN1_Tag real_type,
+ ASN1_Tag real_class,
+ const T& default_value)
+ {
+ BER_Object obj = get_next_object();
+
+ if(obj.is_a(type_tag, class_tag))
+ {
+ obj.set_tagging(real_type, real_class);
+ push_back(std::move(obj));
+ decode(out, real_type, real_class);
+ }
+ else
+ {
+ // Not what we wanted, push it back on the stream
+ out = default_value;
+ push_back(std::move(obj));
+ }
+
+ return (*this);
+ }
+/*
+* Decode a list of homogenously typed values
+*/
+template<typename T>
+BER_Decoder& BER_Decoder::decode_list(std::vector<T>& vec,
+ ASN1_Tag type_tag,
+ ASN1_Tag class_tag)
+ {
+ BER_Decoder list = start_cons(type_tag, class_tag);
+
+ while(list.more_items())
+ {
+ T value;
+ list.decode(value);
+ vec.push_back(std::move(value));
+ }
+
+ list.end_cons();
+
+ return (*this);
+ }
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/asn1/der_enc.cpp b/comm/third_party/botan/src/lib/asn1/der_enc.cpp
new file mode 100644
index 0000000000..056766f435
--- /dev/null
+++ b/comm/third_party/botan/src/lib/asn1/der_enc.cpp
@@ -0,0 +1,405 @@
+/*
+* DER Encoder
+* (C) 1999-2007,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/der_enc.h>
+#include <botan/asn1_obj.h>
+#include <botan/bigint.h>
+#include <botan/loadstor.h>
+#include <botan/internal/bit_ops.h>
+#include <algorithm>
+
+namespace Botan {
+
+namespace {
+
+/*
+* DER encode an ASN.1 type tag
+*/
+void encode_tag(std::vector<uint8_t>& encoded_tag,
+ ASN1_Tag type_tag, ASN1_Tag class_tag)
+ {
+ if((class_tag | 0xE0) != 0xE0)
+ throw Encoding_Error("DER_Encoder: Invalid class tag " +
+ std::to_string(class_tag));
+
+ if(type_tag <= 30)
+ {
+ encoded_tag.push_back(static_cast<uint8_t>(type_tag | class_tag));
+ }
+ else
+ {
+ size_t blocks = high_bit(static_cast<uint32_t>(type_tag)) + 6;
+ blocks = (blocks - (blocks % 7)) / 7;
+
+ BOTAN_ASSERT_NOMSG(blocks > 0);
+
+ encoded_tag.push_back(static_cast<uint8_t>(class_tag | 0x1F));
+ for(size_t i = 0; i != blocks - 1; ++i)
+ encoded_tag.push_back(0x80 | ((type_tag >> 7*(blocks-i-1)) & 0x7F));
+ encoded_tag.push_back(type_tag & 0x7F);
+ }
+ }
+
+/*
+* DER encode an ASN.1 length field
+*/
+void encode_length(std::vector<uint8_t>& encoded_length, size_t length)
+ {
+ if(length <= 127)
+ {
+ encoded_length.push_back(static_cast<uint8_t>(length));
+ }
+ else
+ {
+ const size_t bytes_needed = significant_bytes(length);
+
+ encoded_length.push_back(static_cast<uint8_t>(0x80 | bytes_needed));
+
+ for(size_t i = sizeof(length) - bytes_needed; i < sizeof(length); ++i)
+ encoded_length.push_back(get_byte(i, length));
+ }
+ }
+
+}
+
+DER_Encoder::DER_Encoder(secure_vector<uint8_t>& vec)
+ {
+ m_append_output = [&vec](const uint8_t b[], size_t l)
+ {
+ vec.insert(vec.end(), b, b + l);
+ };
+ }
+
+DER_Encoder::DER_Encoder(std::vector<uint8_t>& vec)
+ {
+ m_append_output = [&vec](const uint8_t b[], size_t l)
+ {
+ vec.insert(vec.end(), b, b + l);
+ };
+ }
+
+/*
+* Push the encoded SEQUENCE/SET to the encoder stream
+*/
+void DER_Encoder::DER_Sequence::push_contents(DER_Encoder& der)
+ {
+ const ASN1_Tag real_class_tag = ASN1_Tag(m_class_tag | CONSTRUCTED);
+
+ if(m_type_tag == SET)
+ {
+ std::sort(m_set_contents.begin(), m_set_contents.end());
+ for(size_t i = 0; i != m_set_contents.size(); ++i)
+ m_contents += m_set_contents[i];
+ m_set_contents.clear();
+ }
+
+ der.add_object(m_type_tag, real_class_tag, m_contents.data(), m_contents.size());
+ m_contents.clear();
+ }
+
+/*
+* 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 == SET)
+ m_set_contents.push_back(secure_vector<uint8_t>(data, data + length));
+ else
+ m_contents += std::make_pair(data, length);
+ }
+
+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 == SET)
+ {
+ secure_vector<uint8_t> m;
+ m.reserve(hdr_len + val_len);
+ m += std::make_pair(hdr, hdr_len);
+ m += std::make_pair(val, val_len);
+ m_set_contents.push_back(std::move(m));
+ }
+ else
+ {
+ m_contents += std::make_pair(hdr, hdr_len);
+ m_contents += std::make_pair(val, val_len);
+ }
+ }
+
+/*
+* Return the type and class taggings
+*/
+ASN1_Tag DER_Encoder::DER_Sequence::tag_of() const
+ {
+ return ASN1_Tag(m_type_tag | m_class_tag);
+ }
+
+/*
+* DER_Sequence Constructor
+*/
+DER_Encoder::DER_Sequence::DER_Sequence(ASN1_Tag t1, ASN1_Tag t2) :
+ m_type_tag(t1), m_class_tag(t2)
+ {
+ }
+
+/*
+* Return the encoded contents
+*/
+secure_vector<uint8_t> DER_Encoder::get_contents()
+ {
+ if(m_subsequences.size() != 0)
+ throw Invalid_State("DER_Encoder: Sequence hasn't been marked done");
+
+ if(m_append_output)
+ throw Invalid_State("DER_Encoder Cannot get contents when using output vector");
+
+ secure_vector<uint8_t> output;
+ std::swap(output, m_default_outbuf);
+ return output;
+ }
+
+std::vector<uint8_t> DER_Encoder::get_contents_unlocked()
+ {
+ if(m_subsequences.size() != 0)
+ throw Invalid_State("DER_Encoder: Sequence hasn't been marked done");
+
+ if(m_append_output)
+ throw Invalid_State("DER_Encoder Cannot get contents when using output vector");
+
+ std::vector<uint8_t> output(m_default_outbuf.begin(), m_default_outbuf.end());
+ m_default_outbuf.clear();
+ return output;
+ }
+
+/*
+* Start a new ASN.1 SEQUENCE/SET/EXPLICIT
+*/
+DER_Encoder& DER_Encoder::start_cons(ASN1_Tag type_tag,
+ ASN1_Tag class_tag)
+ {
+ m_subsequences.push_back(DER_Sequence(type_tag, class_tag));
+ return (*this);
+ }
+
+/*
+* Finish the current ASN.1 SEQUENCE/SET/EXPLICIT
+*/
+DER_Encoder& DER_Encoder::end_cons()
+ {
+ if(m_subsequences.empty())
+ throw Invalid_State("DER_Encoder::end_cons: No such sequence");
+
+ DER_Sequence last_seq = std::move(m_subsequences[m_subsequences.size()-1]);
+ m_subsequences.pop_back();
+ last_seq.push_contents(*this);
+
+ return (*this);
+ }
+
+/*
+* Start a new ASN.1 EXPLICIT encoding
+*/
+DER_Encoder& DER_Encoder::start_explicit(uint16_t type_no)
+ {
+ ASN1_Tag type_tag = static_cast<ASN1_Tag>(type_no);
+
+ // This would confuse DER_Sequence
+ if(type_tag == SET)
+ throw Internal_Error("DER_Encoder.start_explicit(SET) not supported");
+
+ return start_cons(type_tag, CONTEXT_SPECIFIC);
+ }
+
+/*
+* Finish the current ASN.1 EXPLICIT encoding
+*/
+DER_Encoder& DER_Encoder::end_explicit()
+ {
+ return end_cons();
+ }
+
+/*
+* Write raw bytes into the stream
+*/
+DER_Encoder& DER_Encoder::raw_bytes(const uint8_t bytes[], size_t length)
+ {
+ if(m_subsequences.size())
+ {
+ m_subsequences[m_subsequences.size()-1].add_bytes(bytes, length);
+ }
+ else if(m_append_output)
+ {
+ m_append_output(bytes, length);
+ }
+ else
+ {
+ m_default_outbuf += std::make_pair(bytes, length);
+ }
+
+ return (*this);
+ }
+
+/*
+* Write the encoding of the byte(s)
+*/
+DER_Encoder& DER_Encoder::add_object(ASN1_Tag type_tag, ASN1_Tag class_tag,
+ const uint8_t rep[], size_t length)
+ {
+ std::vector<uint8_t> hdr;
+ encode_tag(hdr, type_tag, class_tag);
+ encode_length(hdr, length);
+
+ if(m_subsequences.size())
+ {
+ m_subsequences[m_subsequences.size()-1].add_bytes(hdr.data(), hdr.size(), rep, length);
+ }
+ else if(m_append_output)
+ {
+ m_append_output(hdr.data(), hdr.size());
+ m_append_output(rep, length);
+ }
+ else
+ {
+ m_default_outbuf += hdr;
+ m_default_outbuf += std::make_pair(rep, length);
+ }
+
+ return (*this);
+ }
+
+/*
+* Encode a NULL object
+*/
+DER_Encoder& DER_Encoder::encode_null()
+ {
+ return add_object(NULL_TAG, UNIVERSAL, nullptr, 0);
+ }
+
+/*
+* DER encode a BOOLEAN
+*/
+DER_Encoder& DER_Encoder::encode(bool is_true)
+ {
+ return encode(is_true, BOOLEAN, UNIVERSAL);
+ }
+
+/*
+* DER encode a small INTEGER
+*/
+DER_Encoder& DER_Encoder::encode(size_t n)
+ {
+ return encode(BigInt(n), INTEGER, UNIVERSAL);
+ }
+
+/*
+* DER encode a small INTEGER
+*/
+DER_Encoder& DER_Encoder::encode(const BigInt& n)
+ {
+ return encode(n, INTEGER, UNIVERSAL);
+ }
+
+/*
+* Encode this object
+*/
+DER_Encoder& DER_Encoder::encode(const uint8_t bytes[], size_t length,
+ ASN1_Tag real_type)
+ {
+ return encode(bytes, length, real_type, real_type, UNIVERSAL);
+ }
+
+/*
+* DER encode a BOOLEAN
+*/
+DER_Encoder& DER_Encoder::encode(bool is_true,
+ ASN1_Tag type_tag, ASN1_Tag class_tag)
+ {
+ uint8_t val = is_true ? 0xFF : 0x00;
+ return add_object(type_tag, class_tag, &val, 1);
+ }
+
+/*
+* DER encode a small INTEGER
+*/
+DER_Encoder& DER_Encoder::encode(size_t n,
+ ASN1_Tag type_tag, ASN1_Tag class_tag)
+ {
+ return encode(BigInt(n), type_tag, class_tag);
+ }
+
+/*
+* DER encode an INTEGER
+*/
+DER_Encoder& DER_Encoder::encode(const BigInt& n,
+ ASN1_Tag type_tag, ASN1_Tag class_tag)
+ {
+ if(n == 0)
+ return add_object(type_tag, class_tag, 0);
+
+ const size_t extra_zero = (n.bits() % 8 == 0) ? 1 : 0;
+ secure_vector<uint8_t> contents(extra_zero + n.bytes());
+ n.binary_encode(&contents[extra_zero]);
+ if(n < 0)
+ {
+ for(size_t i = 0; i != contents.size(); ++i)
+ contents[i] = ~contents[i];
+ for(size_t i = contents.size(); i > 0; --i)
+ if(++contents[i-1])
+ break;
+ }
+
+ return add_object(type_tag, class_tag, contents);
+ }
+
+/*
+* DER encode an OCTET STRING or BIT STRING
+*/
+DER_Encoder& DER_Encoder::encode(const uint8_t bytes[], size_t length,
+ ASN1_Tag real_type,
+ ASN1_Tag type_tag, ASN1_Tag class_tag)
+ {
+ if(real_type != OCTET_STRING && real_type != BIT_STRING)
+ throw Invalid_Argument("DER_Encoder: Invalid tag for byte/bit string");
+
+ if(real_type == BIT_STRING)
+ {
+ secure_vector<uint8_t> encoded;
+ encoded.push_back(0);
+ encoded += std::make_pair(bytes, length);
+ return add_object(type_tag, class_tag, encoded);
+ }
+ else
+ return add_object(type_tag, class_tag, bytes, length);
+ }
+
+DER_Encoder& DER_Encoder::encode(const ASN1_Object& obj)
+ {
+ obj.encode_into(*this);
+ return (*this);
+ }
+
+/*
+* Write the encoding of the byte(s)
+*/
+DER_Encoder& DER_Encoder::add_object(ASN1_Tag type_tag, ASN1_Tag class_tag,
+ const std::string& 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);
+ }
+
+/*
+* Write the encoding of the byte
+*/
+DER_Encoder& DER_Encoder::add_object(ASN1_Tag type_tag,
+ ASN1_Tag class_tag, uint8_t rep)
+ {
+ return add_object(type_tag, class_tag, &rep, 1);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/asn1/der_enc.h b/comm/third_party/botan/src/lib/asn1/der_enc.h
new file mode 100644
index 0000000000..93d53f4b91
--- /dev/null
+++ b/comm/third_party/botan/src/lib/asn1/der_enc.h
@@ -0,0 +1,227 @@
+/*
+* DER Encoder
+* (C) 1999-2007,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_DER_ENCODER_H_
+#define BOTAN_DER_ENCODER_H_
+
+#include <botan/asn1_obj.h>
+#include <vector>
+#include <functional>
+
+namespace Botan {
+
+class BigInt;
+
+/**
+* General DER Encoding Object
+*/
+class BOTAN_PUBLIC_API(2,0) DER_Encoder final
+ {
+ public:
+ typedef std::function<void (const uint8_t[], size_t)> append_fn;
+
+ /**
+ * DER encode, writing to an internal buffer
+ * Use get_contents or get_contents_unlocked to read the results
+ * after all encoding is completed.
+ */
+ DER_Encoder() = default;
+
+ /**
+ * DER encode, writing to @param vec
+ * If this constructor is used, get_contents* may not be called.
+ */
+ DER_Encoder(secure_vector<uint8_t>& vec);
+
+ /**
+ * DER encode, writing to @param vec
+ * If this constructor is used, get_contents* may not be called.
+ */
+ DER_Encoder(std::vector<uint8_t>& 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(append) {}
+
+ secure_vector<uint8_t> get_contents();
+
+ /**
+ * 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.
+ */
+ std::vector<uint8_t> BOTAN_DEPRECATED("Use DER_Encoder(vector) instead") get_contents_unlocked();
+
+ DER_Encoder& start_cons(ASN1_Tag type_tag,
+ ASN1_Tag class_tag = UNIVERSAL);
+ DER_Encoder& end_cons();
+
+ DER_Encoder& start_explicit(uint16_t type_tag);
+ DER_Encoder& end_explicit();
+
+ /**
+ * Insert raw bytes directly into the output stream
+ */
+ DER_Encoder& raw_bytes(const uint8_t val[], size_t len);
+
+ template<typename Alloc>
+ DER_Encoder& raw_bytes(const std::vector<uint8_t, Alloc>& val)
+ {
+ return raw_bytes(val.data(), val.size());
+ }
+
+ DER_Encoder& encode_null();
+ DER_Encoder& encode(bool b);
+ DER_Encoder& encode(size_t s);
+ DER_Encoder& encode(const BigInt& n);
+ DER_Encoder& encode(const uint8_t val[], size_t len, ASN1_Tag real_type);
+
+ template<typename Alloc>
+ DER_Encoder& encode(const std::vector<uint8_t, Alloc>& vec, ASN1_Tag real_type)
+ {
+ return encode(vec.data(), vec.size(), real_type);
+ }
+
+ DER_Encoder& encode(bool b,
+ ASN1_Tag type_tag,
+ ASN1_Tag class_tag = CONTEXT_SPECIFIC);
+
+ DER_Encoder& encode(size_t s,
+ ASN1_Tag type_tag,
+ ASN1_Tag class_tag = CONTEXT_SPECIFIC);
+
+ DER_Encoder& encode(const BigInt& n,
+ ASN1_Tag type_tag,
+ ASN1_Tag class_tag = CONTEXT_SPECIFIC);
+
+ DER_Encoder& encode(const uint8_t v[], size_t len,
+ ASN1_Tag real_type,
+ ASN1_Tag type_tag,
+ ASN1_Tag class_tag = CONTEXT_SPECIFIC);
+
+ template<typename Alloc>
+ DER_Encoder& encode(const std::vector<uint8_t, Alloc>& bytes,
+ ASN1_Tag real_type,
+ ASN1_Tag type_tag, ASN1_Tag class_tag)
+ {
+ return encode(bytes.data(), bytes.size(),
+ real_type, type_tag, class_tag);
+ }
+
+ template<typename T>
+ DER_Encoder& encode_optional(const T& value, const T& default_value)
+ {
+ if(value != default_value)
+ encode(value);
+ return (*this);
+ }
+
+ template<typename T>
+ DER_Encoder& encode_list(const std::vector<T>& values)
+ {
+ for(size_t i = 0; i != values.size(); ++i)
+ encode(values[i]);
+ return (*this);
+ }
+
+ /*
+ * Request for an object to encode itself to this stream
+ */
+ DER_Encoder& encode(const ASN1_Object& obj);
+
+ /*
+ * Conditionally write some values to the stream
+ */
+ DER_Encoder& encode_if(bool pred, DER_Encoder& enc)
+ {
+ if(pred)
+ return raw_bytes(enc.get_contents());
+ return (*this);
+ }
+
+ DER_Encoder& encode_if(bool pred, const ASN1_Object& obj)
+ {
+ if(pred)
+ encode(obj);
+ return (*this);
+ }
+
+ DER_Encoder& add_object(ASN1_Tag type_tag, ASN1_Tag class_tag,
+ const uint8_t rep[], size_t length);
+
+ DER_Encoder& add_object(ASN1_Tag type_tag, ASN1_Tag class_tag,
+ const std::vector<uint8_t>& rep)
+ {
+ return add_object(type_tag, class_tag, rep.data(), rep.size());
+ }
+
+ DER_Encoder& add_object(ASN1_Tag type_tag, ASN1_Tag class_tag,
+ const secure_vector<uint8_t>& rep)
+ {
+ return add_object(type_tag, class_tag, rep.data(), rep.size());
+ }
+
+ DER_Encoder& add_object(ASN1_Tag type_tag, ASN1_Tag class_tag,
+ const std::string& str);
+
+ DER_Encoder& add_object(ASN1_Tag type_tag, ASN1_Tag class_tag,
+ uint8_t val);
+
+ private:
+ class DER_Sequence final
+ {
+ public:
+ ASN1_Tag tag_of() const;
+
+ void push_contents(DER_Encoder& der);
+
+ void add_bytes(const uint8_t val[], size_t len);
+
+ void add_bytes(const uint8_t hdr[], size_t hdr_len,
+ const uint8_t val[], size_t val_len);
+
+ DER_Sequence(ASN1_Tag, ASN1_Tag);
+
+ DER_Sequence(DER_Sequence&& seq)
+ {
+ std::swap(m_type_tag, seq.m_type_tag);
+ std::swap(m_class_tag, seq.m_class_tag);
+ std::swap(m_contents, seq.m_contents);
+ std::swap(m_set_contents, seq.m_set_contents);
+ }
+
+ DER_Sequence& operator=(DER_Sequence&& seq)
+ {
+ std::swap(m_type_tag, seq.m_type_tag);
+ std::swap(m_class_tag, seq.m_class_tag);
+ std::swap(m_contents, seq.m_contents);
+ std::swap(m_set_contents, seq.m_set_contents);
+ return (*this);
+ }
+
+ DER_Sequence(const DER_Sequence& seq) = default;
+
+ DER_Sequence& operator=(const DER_Sequence& seq) = default;
+
+ private:
+ ASN1_Tag m_type_tag, m_class_tag;
+ secure_vector<uint8_t> m_contents;
+ std::vector< secure_vector<uint8_t> > m_set_contents;
+ };
+
+ append_fn m_append_output;
+ secure_vector<uint8_t> m_default_outbuf;
+ std::vector<DER_Sequence> m_subsequences;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/asn1/info.txt b/comm/third_party/botan/src/lib/asn1/info.txt
new file mode 100644
index 0000000000..4772e1ca70
--- /dev/null
+++ b/comm/third_party/botan/src/lib/asn1/info.txt
@@ -0,0 +1,7 @@
+<defines>
+ASN1 -> 20171109
+</defines>
+
+<requires>
+bigint
+</requires>
diff --git a/comm/third_party/botan/src/lib/asn1/oid_maps.cpp b/comm/third_party/botan/src/lib/asn1/oid_maps.cpp
new file mode 100644
index 0000000000..d385dfd840
--- /dev/null
+++ b/comm/third_party/botan/src/lib/asn1/oid_maps.cpp
@@ -0,0 +1,510 @@
+/*
+* OID maps
+*
+* This file was automatically generated by ./src/scripts/oids.py on 2019-10-21
+*
+* 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 <botan/oids.h>
+#include <unordered_map>
+
+namespace Botan {
+
+std::unordered_map<std::string, std::string> OIDS::load_oid2str_map()
+ {
+ return std::unordered_map<std::string,std::string>{
+ { "0.3.4401.5.3.1.9.26", "Camellia-192/GCM" },
+ { "0.3.4401.5.3.1.9.46", "Camellia-256/GCM" },
+ { "0.3.4401.5.3.1.9.6", "Camellia-128/GCM" },
+ { "0.4.0.127.0.15.1.1.13.0", "XMSS" },
+ { "1.0.14888.3.0.5", "ECKCDSA" },
+ { "1.2.156.10197.1.104.100", "SM4/OCB" },
+ { "1.2.156.10197.1.104.2", "SM4/CBC" },
+ { "1.2.156.10197.1.104.8", "SM4/GCM" },
+ { "1.2.156.10197.1.301", "sm2p256v1" },
+ { "1.2.156.10197.1.301.1", "SM2" },
+ { "1.2.156.10197.1.301.2", "SM2_Kex" },
+ { "1.2.156.10197.1.301.3", "SM2_Enc" },
+ { "1.2.156.10197.1.401", "SM3" },
+ { "1.2.156.10197.1.501", "SM2_Sig/SM3" },
+ { "1.2.156.10197.1.504", "RSA/EMSA3(SM3)" },
+ { "1.2.250.1.223.101.256.1", "frp256v1" },
+ { "1.2.392.200011.61.1.1.1.2", "Camellia-128/CBC" },
+ { "1.2.392.200011.61.1.1.1.3", "Camellia-192/CBC" },
+ { "1.2.392.200011.61.1.1.1.4", "Camellia-256/CBC" },
+ { "1.2.410.200004.1.100.4.3", "ECKCDSA/EMSA1(SHA-1)" },
+ { "1.2.410.200004.1.100.4.4", "ECKCDSA/EMSA1(SHA-224)" },
+ { "1.2.410.200004.1.100.4.5", "ECKCDSA/EMSA1(SHA-256)" },
+ { "1.2.410.200004.1.4", "SEED/CBC" },
+ { "1.2.643.100.1", "GOST.OGRN" },
+ { "1.2.643.100.111", "GOST.SubjectSigningTool" },
+ { "1.2.643.100.112", "GOST.IssuerSigningTool" },
+ { "1.2.643.2.2.19", "GOST-34.10" },
+ { "1.2.643.2.2.3", "GOST-34.10/EMSA1(GOST-R-34.11-94)" },
+ { "1.2.643.2.2.35.1", "gost_256A" },
+ { "1.2.643.2.2.36.0", "gost_256A" },
+ { "1.2.643.3.131.1.1", "GOST.INN" },
+ { "1.2.643.7.1.1.1.1", "GOST-34.10-2012-256" },
+ { "1.2.643.7.1.1.1.2", "GOST-34.10-2012-512" },
+ { "1.2.643.7.1.1.2.2", "Streebog-256" },
+ { "1.2.643.7.1.1.2.3", "Streebog-512" },
+ { "1.2.643.7.1.1.3.2", "GOST-34.10-2012-256/EMSA1(Streebog-256)" },
+ { "1.2.643.7.1.1.3.3", "GOST-34.10-2012-512/EMSA1(Streebog-512)" },
+ { "1.2.643.7.1.2.1.1.1", "gost_256A" },
+ { "1.2.643.7.1.2.1.1.2", "gost_256B" },
+ { "1.2.643.7.1.2.1.2.1", "gost_512A" },
+ { "1.2.643.7.1.2.1.2.2", "gost_512B" },
+ { "1.2.840.10040.4.1", "DSA" },
+ { "1.2.840.10040.4.3", "DSA/EMSA1(SHA-160)" },
+ { "1.2.840.10045.2.1", "ECDSA" },
+ { "1.2.840.10045.3.1.1", "secp192r1" },
+ { "1.2.840.10045.3.1.2", "x962_p192v2" },
+ { "1.2.840.10045.3.1.3", "x962_p192v3" },
+ { "1.2.840.10045.3.1.4", "x962_p239v1" },
+ { "1.2.840.10045.3.1.5", "x962_p239v2" },
+ { "1.2.840.10045.3.1.6", "x962_p239v3" },
+ { "1.2.840.10045.3.1.7", "secp256r1" },
+ { "1.2.840.10045.4.1", "ECDSA/EMSA1(SHA-160)" },
+ { "1.2.840.10045.4.3.1", "ECDSA/EMSA1(SHA-224)" },
+ { "1.2.840.10045.4.3.2", "ECDSA/EMSA1(SHA-256)" },
+ { "1.2.840.10045.4.3.3", "ECDSA/EMSA1(SHA-384)" },
+ { "1.2.840.10045.4.3.4", "ECDSA/EMSA1(SHA-512)" },
+ { "1.2.840.10046.2.1", "DH" },
+ { "1.2.840.113533.7.66.10", "CAST-128/CBC" },
+ { "1.2.840.113533.7.66.15", "KeyWrap.CAST-128" },
+ { "1.2.840.113549.1.1.1", "RSA" },
+ { "1.2.840.113549.1.1.10", "RSA/EMSA4" },
+ { "1.2.840.113549.1.1.11", "RSA/EMSA3(SHA-256)" },
+ { "1.2.840.113549.1.1.12", "RSA/EMSA3(SHA-384)" },
+ { "1.2.840.113549.1.1.13", "RSA/EMSA3(SHA-512)" },
+ { "1.2.840.113549.1.1.14", "RSA/EMSA3(SHA-224)" },
+ { "1.2.840.113549.1.1.16", "RSA/EMSA3(SHA-512-256)" },
+ { "1.2.840.113549.1.1.4", "RSA/EMSA3(MD5)" },
+ { "1.2.840.113549.1.1.5", "RSA/EMSA3(SHA-160)" },
+ { "1.2.840.113549.1.1.7", "RSA/OAEP" },
+ { "1.2.840.113549.1.1.8", "MGF1" },
+ { "1.2.840.113549.1.5.12", "PKCS5.PBKDF2" },
+ { "1.2.840.113549.1.5.13", "PBE-PKCS5v20" },
+ { "1.2.840.113549.1.9.1", "PKCS9.EmailAddress" },
+ { "1.2.840.113549.1.9.14", "PKCS9.ExtensionRequest" },
+ { "1.2.840.113549.1.9.16.3.18", "ChaCha20Poly1305" },
+ { "1.2.840.113549.1.9.16.3.6", "KeyWrap.TripleDES" },
+ { "1.2.840.113549.1.9.16.3.8", "Compression.Zlib" },
+ { "1.2.840.113549.1.9.2", "PKCS9.UnstructuredName" },
+ { "1.2.840.113549.1.9.3", "PKCS9.ContentType" },
+ { "1.2.840.113549.1.9.4", "PKCS9.MessageDigest" },
+ { "1.2.840.113549.1.9.7", "PKCS9.ChallengePassword" },
+ { "1.2.840.113549.2.10", "HMAC(SHA-384)" },
+ { "1.2.840.113549.2.11", "HMAC(SHA-512)" },
+ { "1.2.840.113549.2.13", "HMAC(SHA-512-256)" },
+ { "1.2.840.113549.2.5", "MD5" },
+ { "1.2.840.113549.2.7", "HMAC(SHA-160)" },
+ { "1.2.840.113549.2.8", "HMAC(SHA-224)" },
+ { "1.2.840.113549.2.9", "HMAC(SHA-256)" },
+ { "1.2.840.113549.3.7", "TripleDES/CBC" },
+ { "1.3.101.110", "Curve25519" },
+ { "1.3.101.112", "Ed25519" },
+ { "1.3.132.0.10", "secp256k1" },
+ { "1.3.132.0.30", "secp160r2" },
+ { "1.3.132.0.31", "secp192k1" },
+ { "1.3.132.0.32", "secp224k1" },
+ { "1.3.132.0.33", "secp224r1" },
+ { "1.3.132.0.34", "secp384r1" },
+ { "1.3.132.0.35", "secp521r1" },
+ { "1.3.132.0.8", "secp160r1" },
+ { "1.3.132.0.9", "secp160k1" },
+ { "1.3.132.1.12", "ECDH" },
+ { "1.3.14.3.2.26", "SHA-160" },
+ { "1.3.14.3.2.7", "DES/CBC" },
+ { "1.3.36.3.2.1", "RIPEMD-160" },
+ { "1.3.36.3.3.1.2", "RSA/EMSA3(RIPEMD-160)" },
+ { "1.3.36.3.3.2.5.2.1", "ECGDSA" },
+ { "1.3.36.3.3.2.5.4.1", "ECGDSA/EMSA1(RIPEMD-160)" },
+ { "1.3.36.3.3.2.5.4.2", "ECGDSA/EMSA1(SHA-160)" },
+ { "1.3.36.3.3.2.5.4.3", "ECGDSA/EMSA1(SHA-224)" },
+ { "1.3.36.3.3.2.5.4.4", "ECGDSA/EMSA1(SHA-256)" },
+ { "1.3.36.3.3.2.5.4.5", "ECGDSA/EMSA1(SHA-384)" },
+ { "1.3.36.3.3.2.5.4.6", "ECGDSA/EMSA1(SHA-512)" },
+ { "1.3.36.3.3.2.8.1.1.1", "brainpool160r1" },
+ { "1.3.36.3.3.2.8.1.1.11", "brainpool384r1" },
+ { "1.3.36.3.3.2.8.1.1.13", "brainpool512r1" },
+ { "1.3.36.3.3.2.8.1.1.3", "brainpool192r1" },
+ { "1.3.36.3.3.2.8.1.1.5", "brainpool224r1" },
+ { "1.3.36.3.3.2.8.1.1.7", "brainpool256r1" },
+ { "1.3.36.3.3.2.8.1.1.9", "brainpool320r1" },
+ { "1.3.6.1.4.1.11591.12.2", "Tiger(24,3)" },
+ { "1.3.6.1.4.1.11591.15.1", "OpenPGP.Ed25519" },
+ { "1.3.6.1.4.1.11591.4.11", "Scrypt" },
+ { "1.3.6.1.4.1.25258.1.3", "McEliece" },
+ { "1.3.6.1.4.1.25258.1.5", "XMSS-draft6" },
+ { "1.3.6.1.4.1.25258.1.6.1", "GOST-34.10-2012-256/EMSA1(SHA-256)" },
+ { "1.3.6.1.4.1.25258.1.8", "XMSS-draft12" },
+ { "1.3.6.1.4.1.25258.3.1", "Serpent/CBC" },
+ { "1.3.6.1.4.1.25258.3.101", "Serpent/GCM" },
+ { "1.3.6.1.4.1.25258.3.102", "Twofish/GCM" },
+ { "1.3.6.1.4.1.25258.3.2", "Threefish-512/CBC" },
+ { "1.3.6.1.4.1.25258.3.2.1", "AES-128/OCB" },
+ { "1.3.6.1.4.1.25258.3.2.2", "AES-192/OCB" },
+ { "1.3.6.1.4.1.25258.3.2.3", "AES-256/OCB" },
+ { "1.3.6.1.4.1.25258.3.2.4", "Serpent/OCB" },
+ { "1.3.6.1.4.1.25258.3.2.5", "Twofish/OCB" },
+ { "1.3.6.1.4.1.25258.3.2.6", "Camellia-128/OCB" },
+ { "1.3.6.1.4.1.25258.3.2.7", "Camellia-192/OCB" },
+ { "1.3.6.1.4.1.25258.3.2.8", "Camellia-256/OCB" },
+ { "1.3.6.1.4.1.25258.3.3", "Twofish/CBC" },
+ { "1.3.6.1.4.1.25258.3.4.1", "AES-128/SIV" },
+ { "1.3.6.1.4.1.25258.3.4.2", "AES-192/SIV" },
+ { "1.3.6.1.4.1.25258.3.4.3", "AES-256/SIV" },
+ { "1.3.6.1.4.1.25258.3.4.4", "Serpent/SIV" },
+ { "1.3.6.1.4.1.25258.3.4.5", "Twofish/SIV" },
+ { "1.3.6.1.4.1.25258.3.4.6", "Camellia-128/SIV" },
+ { "1.3.6.1.4.1.25258.3.4.7", "Camellia-192/SIV" },
+ { "1.3.6.1.4.1.25258.3.4.8", "Camellia-256/SIV" },
+ { "1.3.6.1.4.1.25258.3.4.9", "SM4/SIV" },
+ { "1.3.6.1.4.1.3029.1.2.1", "ElGamal" },
+ { "1.3.6.1.4.1.3029.1.5.1", "OpenPGP.Curve25519" },
+ { "1.3.6.1.4.1.311.20.2.2", "Microsoft SmartcardLogon" },
+ { "1.3.6.1.4.1.311.20.2.3", "Microsoft UPN" },
+ { "1.3.6.1.4.1.8301.3.1.2.9.0.38", "secp521r1" },
+ { "1.3.6.1.5.5.7.1.1", "PKIX.AuthorityInformationAccess" },
+ { "1.3.6.1.5.5.7.3.1", "PKIX.ServerAuth" },
+ { "1.3.6.1.5.5.7.3.2", "PKIX.ClientAuth" },
+ { "1.3.6.1.5.5.7.3.3", "PKIX.CodeSigning" },
+ { "1.3.6.1.5.5.7.3.4", "PKIX.EmailProtection" },
+ { "1.3.6.1.5.5.7.3.5", "PKIX.IPsecEndSystem" },
+ { "1.3.6.1.5.5.7.3.6", "PKIX.IPsecTunnel" },
+ { "1.3.6.1.5.5.7.3.7", "PKIX.IPsecUser" },
+ { "1.3.6.1.5.5.7.3.8", "PKIX.TimeStamping" },
+ { "1.3.6.1.5.5.7.3.9", "PKIX.OCSPSigning" },
+ { "1.3.6.1.5.5.7.48.1", "PKIX.OCSP" },
+ { "1.3.6.1.5.5.7.48.1.1", "PKIX.OCSP.BasicResponse" },
+ { "1.3.6.1.5.5.7.48.2", "PKIX.CertificateAuthorityIssuers" },
+ { "1.3.6.1.5.5.7.8.5", "PKIX.XMPPAddr" },
+ { "2.16.840.1.101.3.4.1.2", "AES-128/CBC" },
+ { "2.16.840.1.101.3.4.1.22", "AES-192/CBC" },
+ { "2.16.840.1.101.3.4.1.25", "KeyWrap.AES-192" },
+ { "2.16.840.1.101.3.4.1.26", "AES-192/GCM" },
+ { "2.16.840.1.101.3.4.1.27", "AES-192/CCM" },
+ { "2.16.840.1.101.3.4.1.42", "AES-256/CBC" },
+ { "2.16.840.1.101.3.4.1.45", "KeyWrap.AES-256" },
+ { "2.16.840.1.101.3.4.1.46", "AES-256/GCM" },
+ { "2.16.840.1.101.3.4.1.47", "AES-256/CCM" },
+ { "2.16.840.1.101.3.4.1.5", "KeyWrap.AES-128" },
+ { "2.16.840.1.101.3.4.1.6", "AES-128/GCM" },
+ { "2.16.840.1.101.3.4.1.7", "AES-128/CCM" },
+ { "2.16.840.1.101.3.4.2.1", "SHA-256" },
+ { "2.16.840.1.101.3.4.2.10", "SHA-3(512)" },
+ { "2.16.840.1.101.3.4.2.11", "SHAKE-128" },
+ { "2.16.840.1.101.3.4.2.12", "SHAKE-256" },
+ { "2.16.840.1.101.3.4.2.2", "SHA-384" },
+ { "2.16.840.1.101.3.4.2.3", "SHA-512" },
+ { "2.16.840.1.101.3.4.2.4", "SHA-224" },
+ { "2.16.840.1.101.3.4.2.6", "SHA-512-256" },
+ { "2.16.840.1.101.3.4.2.7", "SHA-3(224)" },
+ { "2.16.840.1.101.3.4.2.8", "SHA-3(256)" },
+ { "2.16.840.1.101.3.4.2.9", "SHA-3(384)" },
+ { "2.16.840.1.101.3.4.3.1", "DSA/EMSA1(SHA-224)" },
+ { "2.16.840.1.101.3.4.3.10", "ECDSA/EMSA1(SHA-3(256))" },
+ { "2.16.840.1.101.3.4.3.11", "ECDSA/EMSA1(SHA-3(384))" },
+ { "2.16.840.1.101.3.4.3.12", "ECDSA/EMSA1(SHA-3(512))" },
+ { "2.16.840.1.101.3.4.3.13", "RSA/EMSA3(SHA-3(224))" },
+ { "2.16.840.1.101.3.4.3.14", "RSA/EMSA3(SHA-3(256))" },
+ { "2.16.840.1.101.3.4.3.15", "RSA/EMSA3(SHA-3(384))" },
+ { "2.16.840.1.101.3.4.3.16", "RSA/EMSA3(SHA-3(512))" },
+ { "2.16.840.1.101.3.4.3.2", "DSA/EMSA1(SHA-256)" },
+ { "2.16.840.1.101.3.4.3.3", "DSA/EMSA1(SHA-384)" },
+ { "2.16.840.1.101.3.4.3.4", "DSA/EMSA1(SHA-512)" },
+ { "2.16.840.1.101.3.4.3.5", "DSA/EMSA1(SHA-3(224))" },
+ { "2.16.840.1.101.3.4.3.6", "DSA/EMSA1(SHA-3(256))" },
+ { "2.16.840.1.101.3.4.3.7", "DSA/EMSA1(SHA-3(384))" },
+ { "2.16.840.1.101.3.4.3.8", "DSA/EMSA1(SHA-3(512))" },
+ { "2.16.840.1.101.3.4.3.9", "ECDSA/EMSA1(SHA-3(224))" },
+ { "2.16.840.1.113730.1.13", "Certificate Comment" },
+ { "2.5.29.14", "X509v3.SubjectKeyIdentifier" },
+ { "2.5.29.15", "X509v3.KeyUsage" },
+ { "2.5.29.16", "X509v3.PrivateKeyUsagePeriod" },
+ { "2.5.29.17", "X509v3.SubjectAlternativeName" },
+ { "2.5.29.18", "X509v3.IssuerAlternativeName" },
+ { "2.5.29.19", "X509v3.BasicConstraints" },
+ { "2.5.29.20", "X509v3.CRLNumber" },
+ { "2.5.29.21", "X509v3.ReasonCode" },
+ { "2.5.29.23", "X509v3.HoldInstructionCode" },
+ { "2.5.29.24", "X509v3.InvalidityDate" },
+ { "2.5.29.28", "X509v3.CRLIssuingDistributionPoint" },
+ { "2.5.29.30", "X509v3.NameConstraints" },
+ { "2.5.29.31", "X509v3.CRLDistributionPoints" },
+ { "2.5.29.32", "X509v3.CertificatePolicies" },
+ { "2.5.29.32.0", "X509v3.AnyPolicy" },
+ { "2.5.29.35", "X509v3.AuthorityKeyIdentifier" },
+ { "2.5.29.36", "X509v3.PolicyConstraints" },
+ { "2.5.29.37", "X509v3.ExtendedKeyUsage" },
+ { "2.5.4.10", "X520.Organization" },
+ { "2.5.4.11", "X520.OrganizationalUnit" },
+ { "2.5.4.12", "X520.Title" },
+ { "2.5.4.3", "X520.CommonName" },
+ { "2.5.4.4", "X520.Surname" },
+ { "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.5", "X520.SerialNumber" },
+ { "2.5.4.6", "X520.Country" },
+ { "2.5.4.65", "X520.Pseudonym" },
+ { "2.5.4.7", "X520.Locality" },
+ { "2.5.4.8", "X520.State" },
+ { "2.5.4.9", "X520.StreetAddress" },
+ { "2.5.8.1.1", "RSA" }
+ };
+ }
+
+std::unordered_map<std::string, OID> OIDS::load_str2oid_map()
+ {
+ return std::unordered_map<std::string,OID>{
+ { "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}) },
+ { "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/EMSA1(SHA-160)", OID({1,2,840,10040,4,3}) },
+ { "DSA/EMSA1(SHA-224)", OID({2,16,840,1,101,3,4,3,1}) },
+ { "DSA/EMSA1(SHA-256)", OID({2,16,840,1,101,3,4,3,2}) },
+ { "DSA/EMSA1(SHA-3(224))", OID({2,16,840,1,101,3,4,3,5}) },
+ { "DSA/EMSA1(SHA-3(256))", OID({2,16,840,1,101,3,4,3,6}) },
+ { "DSA/EMSA1(SHA-3(384))", OID({2,16,840,1,101,3,4,3,7}) },
+ { "DSA/EMSA1(SHA-3(512))", OID({2,16,840,1,101,3,4,3,8}) },
+ { "DSA/EMSA1(SHA-384)", OID({2,16,840,1,101,3,4,3,3}) },
+ { "DSA/EMSA1(SHA-512)", OID({2,16,840,1,101,3,4,3,4}) },
+ { "ECDH", OID({1,3,132,1,12}) },
+ { "ECDSA", OID({1,2,840,10045,2,1}) },
+ { "ECDSA/EMSA1(SHA-160)", OID({1,2,840,10045,4,1}) },
+ { "ECDSA/EMSA1(SHA-224)", OID({1,2,840,10045,4,3,1}) },
+ { "ECDSA/EMSA1(SHA-256)", OID({1,2,840,10045,4,3,2}) },
+ { "ECDSA/EMSA1(SHA-3(224))", OID({2,16,840,1,101,3,4,3,9}) },
+ { "ECDSA/EMSA1(SHA-3(256))", OID({2,16,840,1,101,3,4,3,10}) },
+ { "ECDSA/EMSA1(SHA-3(384))", OID({2,16,840,1,101,3,4,3,11}) },
+ { "ECDSA/EMSA1(SHA-3(512))", OID({2,16,840,1,101,3,4,3,12}) },
+ { "ECDSA/EMSA1(SHA-384)", OID({1,2,840,10045,4,3,3}) },
+ { "ECDSA/EMSA1(SHA-512)", OID({1,2,840,10045,4,3,4}) },
+ { "ECGDSA", OID({1,3,36,3,3,2,5,2,1}) },
+ { "ECGDSA/EMSA1(RIPEMD-160)", OID({1,3,36,3,3,2,5,4,1}) },
+ { "ECGDSA/EMSA1(SHA-160)", OID({1,3,36,3,3,2,5,4,2}) },
+ { "ECGDSA/EMSA1(SHA-224)", OID({1,3,36,3,3,2,5,4,3}) },
+ { "ECGDSA/EMSA1(SHA-256)", OID({1,3,36,3,3,2,5,4,4}) },
+ { "ECGDSA/EMSA1(SHA-384)", OID({1,3,36,3,3,2,5,4,5}) },
+ { "ECGDSA/EMSA1(SHA-512)", OID({1,3,36,3,3,2,5,4,6}) },
+ { "ECKCDSA", OID({1,0,14888,3,0,5}) },
+ { "ECKCDSA/EMSA1(SHA-1)", OID({1,2,410,200004,1,100,4,3}) },
+ { "ECKCDSA/EMSA1(SHA-224)", OID({1,2,410,200004,1,100,4,4}) },
+ { "ECKCDSA/EMSA1(SHA-256)", OID({1,2,410,200004,1,100,4,5}) },
+ { "Ed25519", OID({1,3,101,112}) },
+ { "ElGamal", OID({1,3,6,1,4,1,3029,1,2,1}) },
+ { "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/EMSA1(SHA-256)", OID({1,3,6,1,4,1,25258,1,6,1}) },
+ { "GOST-34.10-2012-256/EMSA1(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/EMSA1(Streebog-512)", OID({1,2,643,7,1,1,3,3}) },
+ { "GOST-34.10/EMSA1(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-160)", 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}) },
+ { "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}) },
+ { "MD5", OID({1,2,840,113549,2,5}) },
+ { "MGF1", OID({1,2,840,113549,1,1,8}) },
+ { "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.OCSPSigning", OID({1,3,6,1,5,5,7,3,9}) },
+ { "PKIX.ServerAuth", OID({1,3,6,1,5,5,7,3,1}) },
+ { "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(MD5)", OID({1,2,840,113549,1,1,4}) },
+ { "RSA/EMSA3(RIPEMD-160)", OID({1,3,36,3,3,1,2}) },
+ { "RSA/EMSA3(SHA-160)", 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}) },
+ { "SEED/CBC", OID({1,2,410,200004,1,4}) },
+ { "SHA-160", 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}) },
+ { "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}) },
+ { "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}) },
+ { "Tiger(24,3)", OID({1,3,6,1,4,1,11591,12,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}) },
+ { "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}) },
+ { "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}) },
+ { "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}) }
+ };
+ }
+
+}
+
diff --git a/comm/third_party/botan/src/lib/asn1/oids.cpp b/comm/third_party/botan/src/lib/asn1/oids.cpp
new file mode 100644
index 0000000000..bece7a9b47
--- /dev/null
+++ b/comm/third_party/botan/src/lib/asn1/oids.cpp
@@ -0,0 +1,134 @@
+/*
+* OID Registry
+* (C) 1999-2008,2013 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/oids.h>
+#include <botan/mutex.h>
+
+namespace Botan {
+
+namespace {
+
+class OID_Map final
+ {
+ public:
+ void add_oid(const OID& oid, const std::string& str)
+ {
+ add_str2oid(oid, str);
+ add_oid2str(oid, str);
+ }
+
+ void add_str2oid(const OID& oid, const std::string& str)
+ {
+ lock_guard_type<mutex_type> lock(m_mutex);
+ auto i = m_str2oid.find(str);
+ if(i == m_str2oid.end())
+ m_str2oid.insert(std::make_pair(str, oid));
+ }
+
+ void add_oid2str(const OID& oid, const std::string& str)
+ {
+ const std::string oid_str = oid.to_string();
+ lock_guard_type<mutex_type> lock(m_mutex);
+ auto i = m_oid2str.find(oid_str);
+ if(i == m_oid2str.end())
+ m_oid2str.insert(std::make_pair(oid_str, str));
+ }
+
+ std::string oid2str(const OID& oid)
+ {
+ const std::string oid_str = oid.to_string();
+
+ lock_guard_type<mutex_type> lock(m_mutex);
+
+ auto i = m_oid2str.find(oid_str);
+ if(i != m_oid2str.end())
+ return i->second;
+
+ return "";
+ }
+
+ OID str2oid(const std::string& str)
+ {
+ lock_guard_type<mutex_type> lock(m_mutex);
+ auto i = m_str2oid.find(str);
+ if(i != m_str2oid.end())
+ return i->second;
+
+ return OID();
+ }
+
+ bool have_oid(const std::string& str)
+ {
+ lock_guard_type<mutex_type> lock(m_mutex);
+ return m_str2oid.find(str) != m_str2oid.end();
+ }
+
+ static OID_Map& global_registry()
+ {
+ static OID_Map g_map;
+ return g_map;
+ }
+
+ private:
+
+ OID_Map()
+ {
+ m_str2oid = OIDS::load_str2oid_map();
+ m_oid2str = OIDS::load_oid2str_map();
+ }
+
+ mutex_type m_mutex;
+ std::unordered_map<std::string, OID> m_str2oid;
+ std::unordered_map<std::string, std::string> m_oid2str;
+ };
+
+}
+
+void OIDS::add_oid(const OID& oid, const std::string& name)
+ {
+ OID_Map::global_registry().add_oid(oid, name);
+ }
+
+void OIDS::add_oidstr(const char* oidstr, const char* name)
+ {
+ add_oid(OID(oidstr), name);
+ }
+
+void OIDS::add_oid2str(const OID& oid, const std::string& name)
+ {
+ OID_Map::global_registry().add_oid2str(oid, name);
+ }
+
+void OIDS::add_str2oid(const OID& oid, const std::string& name)
+ {
+ OID_Map::global_registry().add_str2oid(oid, name);
+ }
+
+std::string OIDS::oid2str_or_empty(const OID& oid)
+ {
+ return OID_Map::global_registry().oid2str(oid);
+ }
+
+OID OIDS::str2oid_or_empty(const std::string& name)
+ {
+ return OID_Map::global_registry().str2oid(name);
+ }
+
+std::string OIDS::oid2str_or_throw(const OID& oid)
+ {
+ const std::string s = OIDS::oid2str_or_empty(oid);
+ if(s.empty())
+ throw Lookup_Error("No name associated with OID " + oid.to_string());
+ return s;
+ }
+
+bool OIDS::have_oid(const std::string& name)
+ {
+ return OID_Map::global_registry().have_oid(name);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/asn1/oids.h b/comm/third_party/botan/src/lib/asn1/oids.h
new file mode 100644
index 0000000000..9af451fe41
--- /dev/null
+++ b/comm/third_party/botan/src/lib/asn1/oids.h
@@ -0,0 +1,98 @@
+/*
+* OID Registry
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_OIDS_H_
+#define BOTAN_OIDS_H_
+
+#include <botan/asn1_obj.h>
+#include <unordered_map>
+
+namespace Botan {
+
+namespace OIDS {
+
+/**
+* Register an OID to string mapping.
+* @param oid the oid to register
+* @param name the name to be associated with the oid
+*/
+BOTAN_UNSTABLE_API void add_oid(const OID& oid, const std::string& name);
+
+BOTAN_UNSTABLE_API void add_oid2str(const OID& oid, const std::string& name);
+BOTAN_UNSTABLE_API void add_str2oid(const OID& oid, const std::string& name);
+
+BOTAN_UNSTABLE_API void add_oidstr(const char* oidstr, const char* name);
+
+std::unordered_map<std::string, std::string> load_oid2str_map();
+std::unordered_map<std::string, OID> load_str2oid_map();
+
+/**
+* Resolve an OID
+* @param oid the OID to look up
+* @return name associated with this OID, or an empty string
+*/
+BOTAN_UNSTABLE_API std::string oid2str_or_empty(const OID& oid);
+
+/**
+* Find the OID to a name. The lookup will be performed in the
+* general OID section of the configuration.
+* @param name the name to resolve
+* @return OID associated with the specified name
+*/
+BOTAN_UNSTABLE_API OID str2oid_or_empty(const std::string& name);
+
+BOTAN_UNSTABLE_API std::string oid2str_or_throw(const OID& oid);
+
+/**
+* See if an OID exists in the internal table.
+* @param oid the oid to check for
+* @return true if the oid is registered
+*/
+BOTAN_UNSTABLE_API bool BOTAN_DEPRECATED("Just lookup the value instead") have_oid(const std::string& oid);
+
+/**
+* Tests whether the specified OID stands for the specified name.
+* @param oid the OID to check
+* @param name the name to check
+* @return true if the specified OID stands for the specified name
+*/
+inline bool BOTAN_DEPRECATED("Use oid == OID::from_string(name)") name_of(const OID& oid, const std::string& name)
+ {
+ return (oid == str2oid_or_empty(name));
+ }
+
+/**
+* Prefer oid2str_or_empty
+*/
+inline std::string lookup(const OID& oid)
+ {
+ return oid2str_or_empty(oid);
+ }
+
+/**
+* Prefer str2oid_or_empty
+*/
+inline OID lookup(const std::string& name)
+ {
+ return str2oid_or_empty(name);
+ }
+
+inline std::string BOTAN_DEPRECATED("Use oid2str_or_empty") oid2str(const OID& oid)
+ {
+ return oid2str_or_empty(oid);
+ }
+
+inline OID BOTAN_DEPRECATED("Use str2oid_or_empty") str2oid(const std::string& name)
+ {
+ return str2oid_or_empty(name);
+ }
+
+}
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/base/botan.h b/comm/third_party/botan/src/lib/base/botan.h
new file mode 100644
index 0000000000..a473e8fbf2
--- /dev/null
+++ b/comm/third_party/botan/src/lib/base/botan.h
@@ -0,0 +1,41 @@
+/*
+* A vague catch all include file for Botan
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_BOTAN_H_
+#define BOTAN_BOTAN_H_
+
+/*
+* There is no real reason for this header to exist beyond historical
+* reasons. The application should instead include the specific header
+* files that define the interfaces it intends to use.
+*
+* This header file will be removed in Botan 3.x
+*/
+
+#include <botan/lookup.h>
+#include <botan/version.h>
+#include <botan/parsing.h>
+#include <botan/init.h>
+#include <botan/rng.h>
+#include <botan/secmem.h>
+
+#if defined(BOTAN_HAS_AUTO_SEEDING_RNG)
+ #include <botan/auto_rng.h>
+#endif
+
+#if defined(BOTAN_HAS_FILTERS)
+ #include <botan/filters.h>
+#endif
+
+#if defined(BOTAN_HAS_PUBLIC_KEY_CRYPTO)
+ #include <botan/x509_key.h>
+ #include <botan/pkcs8.h>
+#endif
+
+BOTAN_DEPRECATED_HEADER(botan.h)
+
+#endif
diff --git a/comm/third_party/botan/src/lib/base/buf_comp.cpp b/comm/third_party/botan/src/lib/base/buf_comp.cpp
new file mode 100644
index 0000000000..e9a33c9d7f
--- /dev/null
+++ b/comm/third_party/botan/src/lib/base/buf_comp.cpp
@@ -0,0 +1,54 @@
+/*
+* (C) 2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/buf_comp.h>
+#include <botan/loadstor.h>
+
+namespace Botan {
+
+void Buffered_Computation::update_be(uint16_t val)
+ {
+ uint8_t inb[sizeof(val)];
+ store_be(val, inb);
+ add_data(inb, sizeof(inb));
+ }
+
+void Buffered_Computation::update_be(uint32_t val)
+ {
+ uint8_t inb[sizeof(val)];
+ store_be(val, inb);
+ add_data(inb, sizeof(inb));
+ }
+
+void Buffered_Computation::update_be(uint64_t val)
+ {
+ uint8_t inb[sizeof(val)];
+ store_be(val, inb);
+ add_data(inb, sizeof(inb));
+ }
+
+void Buffered_Computation::update_le(uint16_t val)
+ {
+ uint8_t inb[sizeof(val)];
+ store_le(val, inb);
+ add_data(inb, sizeof(inb));
+ }
+
+void Buffered_Computation::update_le(uint32_t val)
+ {
+ uint8_t inb[sizeof(val)];
+ store_le(val, inb);
+ add_data(inb, sizeof(inb));
+ }
+
+void Buffered_Computation::update_le(uint64_t val)
+ {
+ uint8_t inb[sizeof(val)];
+ store_le(val, inb);
+ add_data(inb, sizeof(inb));
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/base/buf_comp.h b/comm/third_party/botan/src/lib/base/buf_comp.h
new file mode 100644
index 0000000000..31bf485295
--- /dev/null
+++ b/comm/third_party/botan/src/lib/base/buf_comp.h
@@ -0,0 +1,178 @@
+/*
+* Buffered Computation
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_BUFFERED_COMPUTATION_H_
+#define BOTAN_BUFFERED_COMPUTATION_H_
+
+#include <botan/secmem.h>
+#include <string>
+
+namespace Botan {
+
+/**
+* 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
+ {
+ public:
+ /**
+ * @return length of the output of this function in bytes
+ */
+ virtual size_t output_length() const = 0;
+
+ /**
+ * Add new input to process.
+ * @param in the input to process as a byte array
+ * @param length of param in in bytes
+ */
+ void update(const uint8_t in[], size_t length) { add_data(in, length); }
+
+ /**
+ * Add new input to process.
+ * @param in the input to process as a secure_vector
+ */
+ void update(const secure_vector<uint8_t>& in)
+ {
+ add_data(in.data(), in.size());
+ }
+
+ /**
+ * Add new input to process.
+ * @param in the input to process as a std::vector
+ */
+ void update(const std::vector<uint8_t>& in)
+ {
+ add_data(in.data(), in.size());
+ }
+
+ void update_be(uint16_t val);
+ void update_be(uint32_t val);
+ void update_be(uint64_t val);
+
+ void update_le(uint16_t val);
+ void update_le(uint32_t val);
+ void update_le(uint64_t val);
+
+ /**
+ * Add new input to process.
+ * @param str the input to process as a std::string. Will be interpreted
+ * as a byte array based on the strings encoding.
+ */
+ void update(const std::string& str)
+ {
+ add_data(cast_char_ptr_to_uint8(str.data()), str.size());
+ }
+
+ /**
+ * Process a single byte.
+ * @param in the byte to process
+ */
+ void update(uint8_t in) { add_data(&in, 1); }
+
+ /**
+ * Complete the computation and retrieve the
+ * final result.
+ * @param out The byte array to be filled with the result.
+ * Must be of length output_length()
+ */
+ void final(uint8_t out[]) { final_result(out); }
+
+ /**
+ * Complete the computation and retrieve the
+ * final result.
+ * @return secure_vector holding the result
+ */
+ secure_vector<uint8_t> final()
+ {
+ secure_vector<uint8_t> output(output_length());
+ final_result(output.data());
+ return output;
+ }
+
+ std::vector<uint8_t> final_stdvec()
+ {
+ std::vector<uint8_t> output(output_length());
+ final_result(output.data());
+ return output;
+ }
+
+ template<typename Alloc>
+ void final(std::vector<uint8_t, Alloc>& out)
+ {
+ out.resize(output_length());
+ final_result(out.data());
+ }
+
+ /**
+ * Update and finalize computation. Does the same as calling update()
+ * and final() consecutively.
+ * @param in the input to process as a byte array
+ * @param length the length of the byte array
+ * @result the result of the call to final()
+ */
+ secure_vector<uint8_t> process(const uint8_t in[], size_t length)
+ {
+ add_data(in, length);
+ return final();
+ }
+
+ /**
+ * Update and finalize computation. Does the same as calling update()
+ * and final() consecutively.
+ * @param in the input to process
+ * @result the result of the call to final()
+ */
+ secure_vector<uint8_t> process(const secure_vector<uint8_t>& in)
+ {
+ add_data(in.data(), in.size());
+ return final();
+ }
+
+ /**
+ * Update and finalize computation. Does the same as calling update()
+ * and final() consecutively.
+ * @param in the input to process
+ * @result the result of the call to final()
+ */
+ secure_vector<uint8_t> process(const std::vector<uint8_t>& in)
+ {
+ add_data(in.data(), in.size());
+ return final();
+ }
+
+ /**
+ * Update and finalize computation. Does the same as calling update()
+ * and final() consecutively.
+ * @param in the input to process as a string
+ * @result the result of the call to final()
+ */
+ secure_vector<uint8_t> process(const std::string& in)
+ {
+ update(in);
+ return final();
+ }
+
+ virtual ~Buffered_Computation() = default;
+ private:
+ /**
+ * Add more data to the computation
+ * @param input is an input buffer
+ * @param length is the length of input in bytes
+ */
+ virtual void add_data(const uint8_t input[], size_t length) = 0;
+
+ /**
+ * Write the final output to out
+ * @param out is an output buffer of output_length()
+ */
+ virtual void final_result(uint8_t out[]) = 0;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/base/info.txt b/comm/third_party/botan/src/lib/base/info.txt
new file mode 100644
index 0000000000..fd3f7b8905
--- /dev/null
+++ b/comm/third_party/botan/src/lib/base/info.txt
@@ -0,0 +1,17 @@
+<header:public>
+botan.h
+buf_comp.h
+init.h
+key_spec.h
+lookup.h
+secmem.h
+scan_name.h
+sym_algo.h
+symkey.h
+</header:public>
+
+<requires>
+hex
+rng
+utils
+</requires>
diff --git a/comm/third_party/botan/src/lib/base/init.h b/comm/third_party/botan/src/lib/base/init.h
new file mode 100644
index 0000000000..668650846b
--- /dev/null
+++ b/comm/third_party/botan/src/lib/base/init.h
@@ -0,0 +1,35 @@
+/*
+* Library Initialization
+* (C) 1999-2008,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_LIBRARY_INITIALIZER_H_
+#define BOTAN_LIBRARY_INITIALIZER_H_
+
+#include <botan/types.h>
+#include <string>
+
+namespace Botan {
+
+BOTAN_DEPRECATED_HEADER(init.h)
+
+/*
+* Previously botan had state whose lifetime had to be explicitly
+* managed by the application. As of 1.11.14 this is no longer the
+* case, and this class is no longer needed and kept only for backwards
+* compatibility.
+*/
+class BOTAN_PUBLIC_API(2,0) BOTAN_DEPRECATED("LibraryInitializer is no longer required") LibraryInitializer final
+ {
+ public:
+ explicit LibraryInitializer(const std::string& /*ignored*/ = "") { }
+
+ static void initialize(const std::string& /*ignored*/ = "") {}
+ static void deinitialize() {}
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/base/key_spec.h b/comm/third_party/botan/src/lib/base/key_spec.h
new file mode 100644
index 0000000000..85dcebe37e
--- /dev/null
+++ b/comm/third_party/botan/src/lib/base/key_spec.h
@@ -0,0 +1,14 @@
+/*
+* Symmetric Key Length Specification
+* (C) 2010 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_KEY_LEN_SPECIFICATION_H_
+#define BOTAN_KEY_LEN_SPECIFICATION_H_
+
+#include <botan/sym_algo.h>
+BOTAN_DEPRECATED_HEADER(key_spec.h)
+
+#endif
diff --git a/comm/third_party/botan/src/lib/base/lookup.h b/comm/third_party/botan/src/lib/base/lookup.h
new file mode 100644
index 0000000000..4a14230db0
--- /dev/null
+++ b/comm/third_party/botan/src/lib/base/lookup.h
@@ -0,0 +1,179 @@
+/*
+* Algorithm Lookup
+* (C) 1999-2007,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_LOOKUP_H_
+#define BOTAN_LOOKUP_H_
+
+#include <botan/build.h>
+#include <botan/exceptn.h>
+#include <string>
+#include <vector>
+#include <memory>
+
+#if defined(BOTAN_HAS_BLOCK_CIPHER)
+ #include <botan/block_cipher.h>
+#endif
+
+#if defined(BOTAN_HAS_STREAM_CIPHER)
+ #include <botan/stream_cipher.h>
+#endif
+
+#if defined(BOTAN_HAS_HASH)
+ #include <botan/hash.h>
+#endif
+
+#if defined(BOTAN_HAS_MAC)
+ #include <botan/mac.h>
+#endif
+
+namespace Botan {
+
+BOTAN_DEPRECATED_HEADER(lookup.h)
+
+/*
+* As of 1.11.26 this header is deprecated. Instead use the calls T::create and
+* T::providers (as demonstrated in the implementation below).
+*/
+
+/*
+* Get an algorithm object
+* NOTE: these functions create and return new objects, letting the
+* caller assume ownership of them
+*/
+
+#if defined(BOTAN_HAS_BLOCK_CIPHER)
+
+/**
+* Block cipher factory method.
+*
+* @param algo_spec the name of the desired block cipher
+* @param provider the provider to use
+* @return pointer to the block cipher object
+*/
+BOTAN_DEPRECATED("Use BlockCipher::create")
+inline BlockCipher* get_block_cipher(const std::string& algo_spec,
+ const std::string& provider = "")
+ {
+ return BlockCipher::create(algo_spec, provider).release();
+ }
+
+BOTAN_DEPRECATED("Use BlockCipher::create_or_throw")
+inline std::unique_ptr<BlockCipher> make_block_cipher(const std::string& algo_spec,
+ const std::string& provider = "")
+ {
+ return BlockCipher::create_or_throw(algo_spec, provider);
+ }
+
+BOTAN_DEPRECATED("Use BlockCipher::providers")
+inline std::vector<std::string> get_block_cipher_providers(const std::string& algo_spec)
+ {
+ return BlockCipher::providers(algo_spec);
+ }
+
+#endif
+
+#if defined(BOTAN_HAS_STREAM_CIPHER)
+
+/**
+* Stream cipher factory method.
+*
+* @param algo_spec the name of the desired stream cipher
+* @param provider the provider to use
+* @return pointer to the stream cipher object
+*/
+BOTAN_DEPRECATED("Use StreamCipher::create")
+inline StreamCipher* get_stream_cipher(const std::string& algo_spec,
+ const std::string& provider = "")
+ {
+ return StreamCipher::create(algo_spec, provider).release();
+ }
+
+BOTAN_DEPRECATED("Use StreamCipher::create_or_throw")
+inline std::unique_ptr<StreamCipher> make_stream_cipher(const std::string& algo_spec,
+ const std::string& provider = "")
+ {
+ return StreamCipher::create_or_throw(algo_spec, provider);
+ }
+
+BOTAN_DEPRECATED("Use StreamCipher::providers")
+inline std::vector<std::string> get_stream_cipher_providers(const std::string& algo_spec)
+ {
+ return StreamCipher::providers(algo_spec);
+ }
+
+#endif
+
+#if defined(BOTAN_HAS_HASH)
+
+/**
+* Hash function factory method.
+*
+* @param algo_spec the name of the desired hash function
+* @param provider the provider to use
+* @return pointer to the hash function object
+*/
+BOTAN_DEPRECATED("Use HashFunction::create")
+inline HashFunction* get_hash_function(const std::string& algo_spec,
+ const std::string& provider = "")
+ {
+ return HashFunction::create(algo_spec, provider).release();
+ }
+
+BOTAN_DEPRECATED("Use HashFunction::create_or_throw")
+inline std::unique_ptr<HashFunction> make_hash_function(const std::string& algo_spec,
+ const std::string& provider = "")
+ {
+ return HashFunction::create_or_throw(algo_spec, provider);
+ }
+
+BOTAN_DEPRECATED("Use HashFunction::create")
+inline HashFunction* get_hash(const std::string& algo_spec,
+ const std::string& provider = "")
+ {
+ return HashFunction::create(algo_spec, provider).release();
+ }
+
+BOTAN_DEPRECATED("Use HashFunction::providers")
+inline std::vector<std::string> get_hash_function_providers(const std::string& algo_spec)
+ {
+ return HashFunction::providers(algo_spec);
+ }
+
+#endif
+
+#if defined(BOTAN_HAS_MAC)
+/**
+* MAC factory method.
+*
+* @param algo_spec the name of the desired MAC
+* @param provider the provider to use
+* @return pointer to the MAC object
+*/
+BOTAN_DEPRECATED("MessageAuthenticationCode::create")
+inline MessageAuthenticationCode* get_mac(const std::string& algo_spec,
+ const std::string& provider = "")
+ {
+ return MessageAuthenticationCode::create(algo_spec, provider).release();
+ }
+
+BOTAN_DEPRECATED("MessageAuthenticationCode::create_or_throw")
+inline std::unique_ptr<MessageAuthenticationCode> make_message_auth(const std::string& algo_spec,
+ const std::string& provider = "")
+ {
+ return MessageAuthenticationCode::create(algo_spec, provider);
+ }
+
+BOTAN_DEPRECATED("MessageAuthenticationCode::providers")
+inline std::vector<std::string> get_mac_providers(const std::string& algo_spec)
+ {
+ return MessageAuthenticationCode::providers(algo_spec);
+ }
+#endif
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/base/scan_name.cpp b/comm/third_party/botan/src/lib/base/scan_name.cpp
new file mode 100644
index 0000000000..f79866ba61
--- /dev/null
+++ b/comm/third_party/botan/src/lib/base/scan_name.cpp
@@ -0,0 +1,149 @@
+/*
+* SCAN Name Abstraction
+* (C) 2008-2009,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/scan_name.h>
+#include <botan/parsing.h>
+#include <botan/exceptn.h>
+
+namespace Botan {
+
+namespace {
+
+std::string make_arg(const std::vector<std::pair<size_t, std::string>>& name, size_t start)
+ {
+ std::string output = name[start].second;
+ size_t level = name[start].first;
+
+ size_t paren_depth = 0;
+
+ for(size_t i = start + 1; i != name.size(); ++i)
+ {
+ if(name[i].first <= name[start].first)
+ break;
+
+ if(name[i].first > level)
+ {
+ output += "(" + name[i].second;
+ ++paren_depth;
+ }
+ else if(name[i].first < level)
+ {
+ for (size_t j = name[i].first; j < level; j++) {
+ output += ")";
+ --paren_depth;
+ }
+ output += "," + name[i].second;
+ }
+ else
+ {
+ if(output[output.size() - 1] != '(')
+ output += ",";
+ output += name[i].second;
+ }
+
+ level = name[i].first;
+ }
+
+ for(size_t i = 0; i != paren_depth; ++i)
+ output += ")";
+
+ return output;
+ }
+
+}
+
+SCAN_Name::SCAN_Name(const char* algo_spec) : SCAN_Name(std::string(algo_spec))
+ {
+ }
+
+SCAN_Name::SCAN_Name(std::string algo_spec) : m_orig_algo_spec(algo_spec), m_alg_name(), m_args(), m_mode_info()
+ {
+ if(algo_spec.size() == 0)
+ throw Invalid_Argument("Expected algorithm name, got empty string");
+
+ std::vector<std::pair<size_t, std::string>> name;
+ size_t level = 0;
+ std::pair<size_t, std::string> accum = std::make_pair(level, "");
+
+ const std::string decoding_error = "Bad SCAN name '" + algo_spec + "': ";
+
+ for(size_t i = 0; i != algo_spec.size(); ++i)
+ {
+ char c = algo_spec[i];
+
+ if(c == '/' || c == ',' || c == '(' || c == ')')
+ {
+ if(c == '(')
+ ++level;
+ else if(c == ')')
+ {
+ if(level == 0)
+ throw Decoding_Error(decoding_error + "Mismatched parens");
+ --level;
+ }
+
+ if(c == '/' && level > 0)
+ accum.second.push_back(c);
+ else
+ {
+ if(accum.second != "")
+ name.push_back(accum);
+ accum = std::make_pair(level, "");
+ }
+ }
+ else
+ accum.second.push_back(c);
+ }
+
+ if(accum.second != "")
+ name.push_back(accum);
+
+ if(level != 0)
+ throw Decoding_Error(decoding_error + "Missing close paren");
+
+ if(name.size() == 0)
+ throw Decoding_Error(decoding_error + "Empty name");
+
+ m_alg_name = name[0].second;
+
+ bool in_modes = false;
+
+ for(size_t i = 1; i != name.size(); ++i)
+ {
+ if(name[i].first == 0)
+ {
+ m_mode_info.push_back(make_arg(name, i));
+ in_modes = true;
+ }
+ else if(name[i].first == 1 && !in_modes)
+ m_args.push_back(make_arg(name, i));
+ }
+ }
+
+std::string SCAN_Name::arg(size_t i) const
+ {
+ if(i >= arg_count())
+ throw Invalid_Argument("SCAN_Name::arg " + std::to_string(i) +
+ " out of range for '" + to_string() + "'");
+ return m_args[i];
+ }
+
+std::string SCAN_Name::arg(size_t i, const std::string& def_value) const
+ {
+ if(i >= arg_count())
+ return def_value;
+ return m_args[i];
+ }
+
+size_t SCAN_Name::arg_as_integer(size_t i, size_t def_value) const
+ {
+ if(i >= arg_count())
+ return def_value;
+ return to_u32bit(m_args[i]);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/base/scan_name.h b/comm/third_party/botan/src/lib/base/scan_name.h
new file mode 100644
index 0000000000..069783d1bb
--- /dev/null
+++ b/comm/third_party/botan/src/lib/base/scan_name.h
@@ -0,0 +1,124 @@
+/*
+* SCAN Name Abstraction
+* (C) 2008,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_SCAN_NAME_H_
+#define BOTAN_SCAN_NAME_H_
+
+#include <botan/types.h>
+#include <string>
+#include <vector>
+
+BOTAN_FUTURE_INTERNAL_HEADER(scan_name.h)
+
+namespace Botan {
+
+/**
+A class encapsulating a SCAN name (similar to JCE conventions)
+http://www.users.zetnet.co.uk/hopwood/crypto/scan/
+*/
+class BOTAN_PUBLIC_API(2,0) SCAN_Name final
+ {
+ public:
+ /**
+ * Create a SCAN_Name
+ * @param algo_spec A SCAN-format name
+ */
+ explicit SCAN_Name(const char* algo_spec);
+
+ /**
+ * Create a SCAN_Name
+ * @param algo_spec A SCAN-format name
+ */
+ explicit SCAN_Name(std::string algo_spec);
+
+ /**
+ * @return original input string
+ */
+ const std::string& to_string() const { return m_orig_algo_spec; }
+
+ BOTAN_DEPRECATED("Use SCAN_Name::to_string") const std::string& as_string() const
+ {
+ return this->to_string();
+ }
+
+ /**
+ * @return algorithm name
+ */
+ const std::string& algo_name() const { return m_alg_name; }
+
+ /**
+ * @return number of arguments
+ */
+ size_t arg_count() const { return m_args.size(); }
+
+ /**
+ * @param lower is the lower bound
+ * @param upper is the upper bound
+ * @return if the number of arguments is between lower and upper
+ */
+ bool arg_count_between(size_t lower, size_t upper) const
+ { return ((arg_count() >= lower) && (arg_count() <= upper)); }
+
+ /**
+ * @param i which argument
+ * @return ith argument
+ */
+ std::string arg(size_t i) const;
+
+ /**
+ * @param i which argument
+ * @param def_value the default value
+ * @return ith argument or the default value
+ */
+ std::string arg(size_t i, const std::string& def_value) const;
+
+ /**
+ * @param i which argument
+ * @param def_value the default value
+ * @return ith argument as an integer, or the default value
+ */
+ size_t arg_as_integer(size_t i, size_t def_value) const;
+
+ /**
+ * @return cipher mode (if any)
+ */
+ std::string cipher_mode() const
+ { return (m_mode_info.size() >= 1) ? m_mode_info[0] : ""; }
+
+ /**
+ * @return cipher mode padding (if any)
+ */
+ std::string cipher_mode_pad() const
+ { return (m_mode_info.size() >= 2) ? m_mode_info[1] : ""; }
+
+ private:
+ std::string m_orig_algo_spec;
+ std::string m_alg_name;
+ std::vector<std::string> m_args;
+ std::vector<std::string> m_mode_info;
+ };
+
+// This is unrelated but it is convenient to stash it here
+template<typename T>
+std::vector<std::string> probe_providers_of(const std::string& algo_spec,
+ const std::vector<std::string>& possible)
+ {
+ std::vector<std::string> providers;
+ for(auto&& prov : possible)
+ {
+ std::unique_ptr<T> o(T::create(algo_spec, prov));
+ if(o)
+ {
+ providers.push_back(prov); // available
+ }
+ }
+ return providers;
+ }
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/base/secmem.h b/comm/third_party/botan/src/lib/base/secmem.h
new file mode 100644
index 0000000000..a5ba5857a9
--- /dev/null
+++ b/comm/third_party/botan/src/lib/base/secmem.h
@@ -0,0 +1,136 @@
+/*
+* Secure Memory Buffers
+* (C) 1999-2007,2012 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_SECURE_MEMORY_BUFFERS_H_
+#define BOTAN_SECURE_MEMORY_BUFFERS_H_
+
+#include <botan/types.h> // IWYU pragma: export
+#include <botan/mem_ops.h> // IWYU pragma: export
+#include <vector> // IWYU pragma: export
+#include <algorithm>
+#include <deque>
+#include <type_traits>
+
+namespace Botan {
+
+template<typename T>
+class secure_allocator
+ {
+ public:
+ /*
+ * Assert exists to prevent someone from doing something that will
+ * probably crash anyway (like secure_vector<non_POD_t> 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.
+ */
+#if !defined(_ITERATOR_DEBUG_LEVEL) || _ITERATOR_DEBUG_LEVEL == 0
+ static_assert(std::is_integral<T>::value, "secure_allocator supports only integer types");
+#endif
+
+ typedef T value_type;
+ typedef std::size_t size_type;
+
+ secure_allocator() noexcept = default;
+ secure_allocator(const secure_allocator&) noexcept = default;
+ secure_allocator& operator=(const secure_allocator&) noexcept = default;
+ ~secure_allocator() noexcept = default;
+
+ template<typename U>
+ secure_allocator(const secure_allocator<U>&) noexcept {}
+
+ T* allocate(std::size_t n)
+ {
+ return static_cast<T*>(allocate_memory(n, sizeof(T)));
+ }
+
+ void deallocate(T* p, std::size_t n)
+ {
+ deallocate_memory(p, n, sizeof(T));
+ }
+ };
+
+template<typename T, typename U> inline bool
+operator==(const secure_allocator<T>&, const secure_allocator<U>&)
+ { return true; }
+
+template<typename T, typename U> inline bool
+operator!=(const secure_allocator<T>&, const secure_allocator<U>&)
+ { return false; }
+
+template<typename T> using secure_vector = std::vector<T, secure_allocator<T>>;
+template<typename T> using secure_deque = std::deque<T, secure_allocator<T>>;
+
+// For better compatibility with 1.10 API
+template<typename T> using SecureVector = secure_vector<T>;
+
+template<typename T>
+std::vector<T> unlock(const secure_vector<T>& in)
+ {
+ return std::vector<T>(in.begin(), in.end());
+ }
+
+template<typename T, typename Alloc, typename Alloc2>
+std::vector<T, Alloc>&
+operator+=(std::vector<T, Alloc>& out,
+ const std::vector<T, Alloc2>& in)
+ {
+ out.reserve(out.size() + in.size());
+ out.insert(out.end(), in.begin(), in.end());
+ return out;
+ }
+
+template<typename T, typename Alloc>
+std::vector<T, Alloc>& operator+=(std::vector<T, Alloc>& out, T in)
+ {
+ out.push_back(in);
+ return out;
+ }
+
+template<typename T, typename Alloc, typename L>
+std::vector<T, Alloc>& operator+=(std::vector<T, Alloc>& out,
+ const std::pair<const T*, L>& in)
+ {
+ out.reserve(out.size() + in.second);
+ out.insert(out.end(), in.first, in.first + in.second);
+ return out;
+ }
+
+template<typename T, typename Alloc, typename L>
+std::vector<T, Alloc>& operator+=(std::vector<T, Alloc>& out,
+ const std::pair<T*, L>& in)
+ {
+ out.reserve(out.size() + in.second);
+ out.insert(out.end(), in.first, in.first + in.second);
+ return out;
+ }
+
+/**
+* Zeroise the values; length remains unchanged
+* @param vec the vector to zeroise
+*/
+template<typename T, typename Alloc>
+void zeroise(std::vector<T, Alloc>& vec)
+ {
+ clear_mem(vec.data(), vec.size());
+ }
+
+/**
+* Zeroise the values then free the memory
+* @param vec the vector to zeroise and free
+*/
+template<typename T, typename Alloc>
+void zap(std::vector<T, Alloc>& vec)
+ {
+ zeroise(vec);
+ vec.clear();
+ vec.shrink_to_fit();
+ }
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/base/sym_algo.cpp b/comm/third_party/botan/src/lib/base/sym_algo.cpp
new file mode 100644
index 0000000000..fff4afbd14
--- /dev/null
+++ b/comm/third_party/botan/src/lib/base/sym_algo.cpp
@@ -0,0 +1,24 @@
+/*
+* (C) 2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/sym_algo.h>
+#include <botan/exceptn.h>
+
+namespace Botan {
+
+void SymmetricAlgorithm::throw_key_not_set_error() const
+ {
+ throw Key_Not_Set(name());
+ }
+
+void SymmetricAlgorithm::set_key(const uint8_t key[], size_t length)
+ {
+ if(!valid_keylength(length))
+ throw Invalid_Key_Length(name(), length);
+ key_schedule(key, length);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/base/sym_algo.h b/comm/third_party/botan/src/lib/base/sym_algo.h
new file mode 100644
index 0000000000..41d9992927
--- /dev/null
+++ b/comm/third_party/botan/src/lib/base/sym_algo.h
@@ -0,0 +1,190 @@
+/*
+* Symmetric Algorithm Base Class
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_SYMMETRIC_ALGORITHM_H_
+#define BOTAN_SYMMETRIC_ALGORITHM_H_
+
+#include <botan/symkey.h>
+#include <botan/types.h>
+
+namespace Botan {
+
+/**
+* Represents the length requirements on an algorithm key
+*/
+class BOTAN_PUBLIC_API(2,0) Key_Length_Specification final
+ {
+ public:
+ /**
+ * Constructor for fixed length keys
+ * @param keylen the supported key length
+ */
+ explicit Key_Length_Specification(size_t keylen) :
+ m_min_keylen(keylen),
+ m_max_keylen(keylen),
+ m_keylen_mod(1)
+ {
+ }
+
+ /**
+ * Constructor for variable length keys
+ * @param min_k the smallest supported key length
+ * @param max_k the largest supported key length
+ * @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)
+ {
+ }
+
+ /**
+ * @param length is a key length in bytes
+ * @return true iff this length is a valid length for this algo
+ */
+ bool valid_keylength(size_t length) const
+ {
+ return ((length >= m_min_keylen) &&
+ (length <= m_max_keylen) &&
+ (length % m_keylen_mod == 0));
+ }
+
+ /**
+ * @return minimum key length in bytes
+ */
+ size_t minimum_keylength() const
+ {
+ return m_min_keylen;
+ }
+
+ /**
+ * @return maximum key length in bytes
+ */
+ size_t maximum_keylength() const
+ {
+ return m_max_keylen;
+ }
+
+ /**
+ * @return key length multiple in bytes
+ */
+ size_t keylength_multiple() const
+ {
+ return m_keylen_mod;
+ }
+
+ /*
+ * Multiplies all length requirements with the given factor
+ * @param n the multiplication factor
+ * @return a key length specification multiplied by the factor
+ */
+ Key_Length_Specification multiple(size_t n) const
+ {
+ return Key_Length_Specification(n * m_min_keylen,
+ n * m_max_keylen,
+ n * m_keylen_mod);
+ }
+
+ private:
+ size_t m_min_keylen, m_max_keylen, m_keylen_mod;
+ };
+
+/**
+* This class represents a symmetric algorithm object.
+*/
+class BOTAN_PUBLIC_API(2,0) SymmetricAlgorithm
+ {
+ public:
+ virtual ~SymmetricAlgorithm() = default;
+
+ /**
+ * Reset the state.
+ */
+ virtual void clear() = 0;
+
+ /**
+ * @return object describing limits on key size
+ */
+ virtual Key_Length_Specification key_spec() const = 0;
+
+ /**
+ * @return maximum allowed key length
+ */
+ size_t maximum_keylength() const
+ {
+ return key_spec().maximum_keylength();
+ }
+
+ /**
+ * @return minimum allowed key length
+ */
+ size_t minimum_keylength() const
+ {
+ return key_spec().minimum_keylength();
+ }
+
+ /**
+ * Check whether a given key length is valid for this algorithm.
+ * @param length the key length to be checked.
+ * @return true if the key length is valid.
+ */
+ bool valid_keylength(size_t length) const
+ {
+ return key_spec().valid_keylength(length);
+ }
+
+ /**
+ * Set the symmetric key of this object.
+ * @param key the SymmetricKey to be set.
+ */
+ void set_key(const SymmetricKey& key)
+ {
+ set_key(key.begin(), key.length());
+ }
+
+ template<typename Alloc>
+ void set_key(const std::vector<uint8_t, Alloc>& key)
+ {
+ set_key(key.data(), key.size());
+ }
+
+ /**
+ * Set the symmetric key of this object.
+ * @param key the to be set as a byte array.
+ * @param length in bytes of key param
+ */
+ void set_key(const uint8_t key[], size_t length);
+
+ /**
+ * @return the algorithm name
+ */
+ virtual std::string name() const = 0;
+
+ protected:
+ void verify_key_set(bool cond) const
+ {
+ if(cond == false)
+ throw_key_not_set_error();
+ }
+
+ private:
+ void throw_key_not_set_error() const;
+
+ /**
+ * Run the key schedule
+ * @param key the key
+ * @param length of key
+ */
+ virtual void key_schedule(const uint8_t key[], size_t length) = 0;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/base/symkey.cpp b/comm/third_party/botan/src/lib/base/symkey.cpp
new file mode 100644
index 0000000000..1e1781c675
--- /dev/null
+++ b/comm/third_party/botan/src/lib/base/symkey.cpp
@@ -0,0 +1,134 @@
+/*
+* OctetString
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/symkey.h>
+#include <botan/rng.h>
+#include <botan/hex.h>
+#include <algorithm>
+
+namespace Botan {
+
+/*
+* Create an OctetString from RNG output
+*/
+OctetString::OctetString(RandomNumberGenerator& rng,
+ size_t len)
+ {
+ rng.random_vec(m_data, len);
+ }
+
+/*
+* Create an OctetString from a hex string
+*/
+OctetString::OctetString(const std::string& hex_string)
+ {
+ if(!hex_string.empty())
+ {
+ m_data.resize(1 + hex_string.length() / 2);
+ m_data.resize(hex_decode(m_data.data(), hex_string));
+ }
+ }
+
+/*
+* Create an OctetString from a byte string
+*/
+OctetString::OctetString(const uint8_t in[], size_t n)
+ {
+ m_data.assign(in, in + n);
+ }
+
+/*
+* Set the parity of each key byte to odd
+*/
+void OctetString::set_odd_parity()
+ {
+ const uint8_t ODD_PARITY[256] = {
+ 0x01, 0x01, 0x02, 0x02, 0x04, 0x04, 0x07, 0x07, 0x08, 0x08, 0x0B, 0x0B,
+ 0x0D, 0x0D, 0x0E, 0x0E, 0x10, 0x10, 0x13, 0x13, 0x15, 0x15, 0x16, 0x16,
+ 0x19, 0x19, 0x1A, 0x1A, 0x1C, 0x1C, 0x1F, 0x1F, 0x20, 0x20, 0x23, 0x23,
+ 0x25, 0x25, 0x26, 0x26, 0x29, 0x29, 0x2A, 0x2A, 0x2C, 0x2C, 0x2F, 0x2F,
+ 0x31, 0x31, 0x32, 0x32, 0x34, 0x34, 0x37, 0x37, 0x38, 0x38, 0x3B, 0x3B,
+ 0x3D, 0x3D, 0x3E, 0x3E, 0x40, 0x40, 0x43, 0x43, 0x45, 0x45, 0x46, 0x46,
+ 0x49, 0x49, 0x4A, 0x4A, 0x4C, 0x4C, 0x4F, 0x4F, 0x51, 0x51, 0x52, 0x52,
+ 0x54, 0x54, 0x57, 0x57, 0x58, 0x58, 0x5B, 0x5B, 0x5D, 0x5D, 0x5E, 0x5E,
+ 0x61, 0x61, 0x62, 0x62, 0x64, 0x64, 0x67, 0x67, 0x68, 0x68, 0x6B, 0x6B,
+ 0x6D, 0x6D, 0x6E, 0x6E, 0x70, 0x70, 0x73, 0x73, 0x75, 0x75, 0x76, 0x76,
+ 0x79, 0x79, 0x7A, 0x7A, 0x7C, 0x7C, 0x7F, 0x7F, 0x80, 0x80, 0x83, 0x83,
+ 0x85, 0x85, 0x86, 0x86, 0x89, 0x89, 0x8A, 0x8A, 0x8C, 0x8C, 0x8F, 0x8F,
+ 0x91, 0x91, 0x92, 0x92, 0x94, 0x94, 0x97, 0x97, 0x98, 0x98, 0x9B, 0x9B,
+ 0x9D, 0x9D, 0x9E, 0x9E, 0xA1, 0xA1, 0xA2, 0xA2, 0xA4, 0xA4, 0xA7, 0xA7,
+ 0xA8, 0xA8, 0xAB, 0xAB, 0xAD, 0xAD, 0xAE, 0xAE, 0xB0, 0xB0, 0xB3, 0xB3,
+ 0xB5, 0xB5, 0xB6, 0xB6, 0xB9, 0xB9, 0xBA, 0xBA, 0xBC, 0xBC, 0xBF, 0xBF,
+ 0xC1, 0xC1, 0xC2, 0xC2, 0xC4, 0xC4, 0xC7, 0xC7, 0xC8, 0xC8, 0xCB, 0xCB,
+ 0xCD, 0xCD, 0xCE, 0xCE, 0xD0, 0xD0, 0xD3, 0xD3, 0xD5, 0xD5, 0xD6, 0xD6,
+ 0xD9, 0xD9, 0xDA, 0xDA, 0xDC, 0xDC, 0xDF, 0xDF, 0xE0, 0xE0, 0xE3, 0xE3,
+ 0xE5, 0xE5, 0xE6, 0xE6, 0xE9, 0xE9, 0xEA, 0xEA, 0xEC, 0xEC, 0xEF, 0xEF,
+ 0xF1, 0xF1, 0xF2, 0xF2, 0xF4, 0xF4, 0xF7, 0xF7, 0xF8, 0xF8, 0xFB, 0xFB,
+ 0xFD, 0xFD, 0xFE, 0xFE };
+
+ for(size_t j = 0; j != m_data.size(); ++j)
+ m_data[j] = ODD_PARITY[m_data[j]];
+ }
+
+/*
+* Hex encode an OctetString
+*/
+std::string OctetString::to_string() const
+ {
+ return hex_encode(m_data.data(), m_data.size());
+ }
+
+/*
+* XOR Operation for OctetStrings
+*/
+OctetString& OctetString::operator^=(const OctetString& k)
+ {
+ if(&k == this) { zeroise(m_data); return (*this); }
+ xor_buf(m_data.data(), k.begin(), std::min(length(), k.length()));
+ return (*this);
+ }
+
+/*
+* Equality Operation for OctetStrings
+*/
+bool operator==(const OctetString& s1, const OctetString& s2)
+ {
+ return (s1.bits_of() == s2.bits_of());
+ }
+
+/*
+* Unequality Operation for OctetStrings
+*/
+bool operator!=(const OctetString& s1, const OctetString& s2)
+ {
+ return !(s1 == s2);
+ }
+
+/*
+* Append Operation for OctetStrings
+*/
+OctetString operator+(const OctetString& k1, const OctetString& k2)
+ {
+ secure_vector<uint8_t> out;
+ out += k1.bits_of();
+ out += k2.bits_of();
+ return OctetString(out);
+ }
+
+/*
+* XOR Operation for OctetStrings
+*/
+OctetString operator^(const OctetString& k1, const OctetString& k2)
+ {
+ secure_vector<uint8_t> out(std::max(k1.length(), k2.length()));
+
+ copy_mem(out.data(), k1.begin(), k1.length());
+ xor_buf(out.data(), k2.begin(), k2.length());
+ return OctetString(out);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/base/symkey.h b/comm/third_party/botan/src/lib/base/symkey.h
new file mode 100644
index 0000000000..69becdd4e0
--- /dev/null
+++ b/comm/third_party/botan/src/lib/base/symkey.h
@@ -0,0 +1,150 @@
+/*
+* OctetString
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_SYMKEY_H_
+#define BOTAN_SYMKEY_H_
+
+#include <botan/secmem.h>
+#include <string>
+
+namespace Botan {
+
+/**
+* Octet String
+*/
+class BOTAN_PUBLIC_API(2,0) OctetString final
+ {
+ public:
+ /**
+ * @return size of this octet string in bytes
+ */
+ size_t length() const { return m_data.size(); }
+ size_t size() const { return m_data.size(); }
+
+ /**
+ * @return this object as a secure_vector<uint8_t>
+ */
+ secure_vector<uint8_t> bits_of() const { return m_data; }
+
+ /**
+ * @return start of this string
+ */
+ const uint8_t* begin() const { return m_data.data(); }
+
+ /**
+ * @return end of this string
+ */
+ const uint8_t* end() const { return begin() + m_data.size(); }
+
+ /**
+ * @return this encoded as hex
+ */
+ std::string to_string() const;
+
+ std::string BOTAN_DEPRECATED("Use OctetString::to_string") as_string() const
+ {
+ return this->to_string();
+ }
+
+ /**
+ * XOR the contents of another octet string into this one
+ * @param other octet string
+ * @return reference to this
+ */
+ OctetString& operator^=(const OctetString& other);
+
+ /**
+ * Force to have odd parity
+ */
+ void set_odd_parity();
+
+ /**
+ * Create a new OctetString
+ * @param str is a hex encoded string
+ */
+ explicit OctetString(const std::string& str = "");
+
+ /**
+ * Create a new random OctetString
+ * @param rng is a random number generator
+ * @param len is the desired length in bytes
+ */
+ OctetString(class RandomNumberGenerator& rng, size_t len);
+
+ /**
+ * Create a new OctetString
+ * @param in is an array
+ * @param len is the length of in in bytes
+ */
+ OctetString(const uint8_t in[], size_t len);
+
+ /**
+ * Create a new OctetString
+ * @param in a bytestring
+ */
+ OctetString(const secure_vector<uint8_t>& in) : m_data(in) {}
+
+ /**
+ * Create a new OctetString
+ * @param in a bytestring
+ */
+ OctetString(const std::vector<uint8_t>& in) : m_data(in.begin(), in.end()) {}
+
+ private:
+ secure_vector<uint8_t> m_data;
+ };
+
+/**
+* Compare two strings
+* @param x an octet string
+* @param y an octet string
+* @return if x is equal to y
+*/
+BOTAN_PUBLIC_API(2,0) bool operator==(const OctetString& x,
+ const OctetString& y);
+
+/**
+* Compare two strings
+* @param x an octet string
+* @param y an octet string
+* @return if x is not equal to y
+*/
+BOTAN_PUBLIC_API(2,0) bool operator!=(const OctetString& x,
+ const OctetString& y);
+
+/**
+* Concatenate two strings
+* @param x an octet string
+* @param y an octet string
+* @return x concatenated with y
+*/
+BOTAN_PUBLIC_API(2,0) OctetString operator+(const OctetString& x,
+ const OctetString& y);
+
+/**
+* XOR two strings
+* @param x an octet string
+* @param y an octet string
+* @return x XORed with y
+*/
+BOTAN_PUBLIC_API(2,0) OctetString operator^(const OctetString& x,
+ const OctetString& y);
+
+
+/**
+* Alternate name for octet string showing intent to use as a key
+*/
+using SymmetricKey = OctetString;
+
+/**
+* Alternate name for octet string showing intent to use as an IV
+*/
+using InitializationVector = OctetString;
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/block/aes/aes.cpp b/comm/third_party/botan/src/lib/block/aes/aes.cpp
new file mode 100644
index 0000000000..88d6e9027f
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/aes/aes.cpp
@@ -0,0 +1,1017 @@
+/*
+* (C) 1999-2010,2015,2017,2018,2020 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/aes.h>
+#include <botan/loadstor.h>
+#include <botan/cpuid.h>
+#include <botan/rotate.h>
+#include <botan/internal/bit_ops.h>
+#include <botan/internal/ct_utils.h>
+
+namespace Botan {
+
+#if defined(BOTAN_HAS_AES_POWER8) || defined(BOTAN_HAS_AES_ARMV8) || defined(BOTAN_HAS_AES_NI)
+ #define BOTAN_HAS_HW_AES_SUPPORT
+#endif
+
+/*
+* One of three AES implementation strategies are used to get a constant time
+* implementation which is 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. This uses 32-bit words resulting in 2 blocks being processed
+* in parallel. Moving to 4 blocks (with 64-bit words) would approximately
+* double performance on 64-bit CPUs. Likewise moving to 128 bit SIMD would
+* again approximately double performance vs 64-bit. However the assumption is
+* that most 64-bit CPUs either have hardware AES or SIMD shuffle support and
+* that the majority of users falling back to this code will be 32-bit cores.
+* If this assumption proves to be unsound, the bitsliced code can easily be
+* extended to operate on either 32 or 64 bit words depending on the native
+* wordsize of the target processor.
+*
+* Useful references
+*
+* - "Accelerating AES with Vector Permute Instructions" Mike Hamburg
+* https://www.shiftleft.org/papers/vector_aes/vector_aes.pdf
+*
+* - "Faster and Timing-Attack Resistant AES-GCM" Käsper and Schwabe
+* https://eprint.iacr.org/2009/129.pdf
+*
+* - "A new combinational logic minimization technique with applications to cryptology."
+* Boyar and Peralta https://eprint.iacr.org/2009/191.pdf
+*
+* - "A depth-16 circuit for the AES S-box" Boyar and Peralta
+* https://eprint.iacr.org/2011/332.pdf
+*
+* - "A Very Compact S-box for AES" Canright
+* https://www.iacr.org/archive/ches2005/032.pdf
+* https://core.ac.uk/download/pdf/36694529.pdf (extended)
+*/
+
+namespace {
+
+/*
+This is an AES sbox circuit which can execute in bitsliced mode up to 32x in
+parallel.
+
+The circuit is from the "Circuit Minimization Team" group
+http://www.cs.yale.edu/homes/peralta/CircuitStuff/CMT.html
+http://www.cs.yale.edu/homes/peralta/CircuitStuff/SLP_AES_113.txt
+
+This circuit has size 113 and depth 27. In software it is much faster than
+circuits which are considered faster for hardware purposes (where circuit depth
+is the critical constraint), because unlike in hardware, on common CPUs we can
+only execute - at best - 3 or 4 logic operations per cycle. So a smaller circuit
+is superior. On an x86-64 machine this circuit is about 15% faster than the
+circuit of size 128 and depth 16 given in "A depth-16 circuit for the AES S-box".
+
+Another circuit for AES Sbox of size 102 and depth 24 is describted in "New
+Circuit Minimization Techniques for Smaller and Faster AES SBoxes"
+[https://eprint.iacr.org/2019/802] however it relies on "non-standard" gates
+like MUX, NOR, NAND, etc and so in practice in bitsliced software, its size is
+actually a bit larger than this circuit, as few CPUs have such instructions and
+otherwise they must be emulated using a sequence of available bit operations.
+*/
+void AES_SBOX(uint32_t V[8])
+ {
+ const uint32_t U0 = V[0];
+ const uint32_t U1 = V[1];
+ const uint32_t U2 = V[2];
+ const uint32_t U3 = V[3];
+ const uint32_t U4 = V[4];
+ const uint32_t U5 = V[5];
+ const uint32_t U6 = V[6];
+ const uint32_t U7 = V[7];
+
+ const uint32_t y14 = U3 ^ U5;
+ const uint32_t y13 = U0 ^ U6;
+ const uint32_t y9 = U0 ^ U3;
+ const uint32_t y8 = U0 ^ U5;
+ const uint32_t t0 = U1 ^ U2;
+ const uint32_t y1 = t0 ^ U7;
+ const uint32_t y4 = y1 ^ U3;
+ const uint32_t y12 = y13 ^ y14;
+ const uint32_t y2 = y1 ^ U0;
+ const uint32_t y5 = y1 ^ U6;
+ const uint32_t y3 = y5 ^ y8;
+ const uint32_t t1 = U4 ^ y12;
+ const uint32_t y15 = t1 ^ U5;
+ const uint32_t y20 = t1 ^ U1;
+ const uint32_t y6 = y15 ^ U7;
+ const uint32_t y10 = y15 ^ t0;
+ const uint32_t y11 = y20 ^ y9;
+ const uint32_t y7 = U7 ^ y11;
+ const uint32_t y17 = y10 ^ y11;
+ const uint32_t y19 = y10 ^ y8;
+ const uint32_t y16 = t0 ^ y11;
+ const uint32_t y21 = y13 ^ y16;
+ const uint32_t y18 = U0 ^ y16;
+ const uint32_t t2 = y12 & y15;
+ const uint32_t t3 = y3 & y6;
+ const uint32_t t4 = t3 ^ t2;
+ const uint32_t t5 = y4 & U7;
+ const uint32_t t6 = t5 ^ t2;
+ const uint32_t t7 = y13 & y16;
+ const uint32_t t8 = y5 & y1;
+ const uint32_t t9 = t8 ^ t7;
+ const uint32_t t10 = y2 & y7;
+ const uint32_t t11 = t10 ^ t7;
+ const uint32_t t12 = y9 & y11;
+ const uint32_t t13 = y14 & y17;
+ const uint32_t t14 = t13 ^ t12;
+ const uint32_t t15 = y8 & y10;
+ const uint32_t t16 = t15 ^ t12;
+ const uint32_t t17 = t4 ^ y20;
+ const uint32_t t18 = t6 ^ t16;
+ const uint32_t t19 = t9 ^ t14;
+ const uint32_t t20 = t11 ^ t16;
+ const uint32_t t21 = t17 ^ t14;
+ const uint32_t t22 = t18 ^ y19;
+ const uint32_t t23 = t19 ^ y21;
+ const uint32_t t24 = t20 ^ y18;
+ const uint32_t t25 = t21 ^ t22;
+ const uint32_t t26 = t21 & t23;
+ const uint32_t t27 = t24 ^ t26;
+ const uint32_t t28 = t25 & t27;
+ const uint32_t t29 = t28 ^ t22;
+ const uint32_t t30 = t23 ^ t24;
+ const uint32_t t31 = t22 ^ t26;
+ const uint32_t t32 = t31 & t30;
+ const uint32_t t33 = t32 ^ t24;
+ const uint32_t t34 = t23 ^ t33;
+ const uint32_t t35 = t27 ^ t33;
+ const uint32_t t36 = t24 & t35;
+ const uint32_t t37 = t36 ^ t34;
+ const uint32_t t38 = t27 ^ t36;
+ const uint32_t t39 = t29 & t38;
+ const uint32_t t40 = t25 ^ t39;
+ const uint32_t t41 = t40 ^ t37;
+ const uint32_t t42 = t29 ^ t33;
+ const uint32_t t43 = t29 ^ t40;
+ const uint32_t t44 = t33 ^ t37;
+ const uint32_t t45 = t42 ^ t41;
+ const uint32_t z0 = t44 & y15;
+ const uint32_t z1 = t37 & y6;
+ const uint32_t z2 = t33 & U7;
+ const uint32_t z3 = t43 & y16;
+ const uint32_t z4 = t40 & y1;
+ const uint32_t z5 = t29 & y7;
+ const uint32_t z6 = t42 & y11;
+ const uint32_t z7 = t45 & y17;
+ const uint32_t z8 = t41 & y10;
+ const uint32_t z9 = t44 & y12;
+ const uint32_t z10 = t37 & y3;
+ const uint32_t z11 = t33 & y4;
+ const uint32_t z12 = t43 & y13;
+ const uint32_t z13 = t40 & y5;
+ const uint32_t z14 = t29 & y2;
+ const uint32_t z15 = t42 & y9;
+ const uint32_t z16 = t45 & y14;
+ const uint32_t z17 = t41 & y8;
+ const uint32_t tc1 = z15 ^ z16;
+ const uint32_t tc2 = z10 ^ tc1;
+ const uint32_t tc3 = z9 ^ tc2;
+ const uint32_t tc4 = z0 ^ z2;
+ const uint32_t tc5 = z1 ^ z0;
+ const uint32_t tc6 = z3 ^ z4;
+ const uint32_t tc7 = z12 ^ tc4;
+ const uint32_t tc8 = z7 ^ tc6;
+ const uint32_t tc9 = z8 ^ tc7;
+ const uint32_t tc10 = tc8 ^ tc9;
+ const uint32_t tc11 = tc6 ^ tc5;
+ const uint32_t tc12 = z3 ^ z5;
+ const uint32_t tc13 = z13 ^ tc1;
+ const uint32_t tc14 = tc4 ^ tc12;
+ const uint32_t S3 = tc3 ^ tc11;
+ const uint32_t tc16 = z6 ^ tc8;
+ const uint32_t tc17 = z14 ^ tc10;
+ const uint32_t tc18 = ~tc13 ^ tc14;
+ const uint32_t S7 = z12 ^ tc18;
+ const uint32_t tc20 = z15 ^ tc16;
+ const uint32_t tc21 = tc2 ^ z11;
+ const uint32_t S0 = tc3 ^ tc16;
+ const uint32_t S6 = tc10 ^ tc18;
+ const uint32_t S4 = tc14 ^ S3;
+ const uint32_t S1 = ~(S3 ^ tc16);
+ const uint32_t tc26 = tc17 ^ tc20;
+ const uint32_t S2 = ~(tc26 ^ z17);
+ const uint32_t S5 = tc21 ^ tc17;
+
+ V[0] = S0;
+ V[1] = S1;
+ V[2] = S2;
+ V[3] = S3;
+ V[4] = S4;
+ V[5] = S5;
+ V[6] = S6;
+ V[7] = S7;
+ }
+
+/*
+A circuit for inverse AES Sbox of size 121 and depth 21 from
+http://www.cs.yale.edu/homes/peralta/CircuitStuff/CMT.html
+http://www.cs.yale.edu/homes/peralta/CircuitStuff/Sinv.txt
+*/
+void AES_INV_SBOX(uint32_t V[8])
+ {
+ const uint32_t U0 = V[0];
+ const uint32_t U1 = V[1];
+ const uint32_t U2 = V[2];
+ const uint32_t U3 = V[3];
+ const uint32_t U4 = V[4];
+ const uint32_t U5 = V[5];
+ const uint32_t U6 = V[6];
+ const uint32_t U7 = V[7];
+
+ const uint32_t Y0 = U0 ^ U3;
+ const uint32_t Y2 = ~(U1 ^ U3);
+ const uint32_t Y4 = U0 ^ Y2;
+ const uint32_t RTL0 = U6 ^ U7;
+ const uint32_t Y1 = Y2 ^ RTL0;
+ const uint32_t Y7 = ~(U2 ^ Y1);
+ const uint32_t RTL1 = U3 ^ U4;
+ const uint32_t Y6 = ~(U7 ^ RTL1);
+ const uint32_t Y3 = Y1 ^ RTL1;
+ const uint32_t RTL2 = ~(U0 ^ U2);
+ const uint32_t Y5 = U5 ^ RTL2;
+ const uint32_t sa1 = Y0 ^ Y2;
+ const uint32_t sa0 = Y1 ^ Y3;
+ const uint32_t sb1 = Y4 ^ Y6;
+ const uint32_t sb0 = Y5 ^ Y7;
+ const uint32_t ah = Y0 ^ Y1;
+ const uint32_t al = Y2 ^ Y3;
+ const uint32_t aa = sa0 ^ sa1;
+ const uint32_t bh = Y4 ^ Y5;
+ const uint32_t bl = Y6 ^ Y7;
+ const uint32_t bb = sb0 ^ sb1;
+ const uint32_t ab20 = sa0 ^ sb0;
+ const uint32_t ab22 = al ^ bl;
+ const uint32_t ab23 = Y3 ^ Y7;
+ const uint32_t ab21 = sa1 ^ sb1;
+ const uint32_t abcd1 = ah & bh;
+ const uint32_t rr1 = Y0 & Y4;
+ const uint32_t ph11 = ab20 ^ abcd1;
+ const uint32_t t01 = Y1 & Y5;
+ const uint32_t ph01 = t01 ^ abcd1;
+ const uint32_t abcd2 = al & bl;
+ const uint32_t r1 = Y2 & Y6;
+ const uint32_t pl11 = ab22 ^ abcd2;
+ const uint32_t r2 = Y3 & Y7;
+ const uint32_t pl01 = r2 ^ abcd2;
+ const uint32_t r3 = sa0 & sb0;
+ const uint32_t vr1 = aa & bb;
+ const uint32_t pr1 = vr1 ^ r3;
+ const uint32_t wr1 = sa1 & sb1;
+ const uint32_t qr1 = wr1 ^ r3;
+ const uint32_t ab0 = ph11 ^ rr1;
+ const uint32_t ab1 = ph01 ^ ab21;
+ const uint32_t ab2 = pl11 ^ r1;
+ const uint32_t ab3 = pl01 ^ qr1;
+ const uint32_t cp1 = ab0 ^ pr1;
+ const uint32_t cp2 = ab1 ^ qr1;
+ const uint32_t cp3 = ab2 ^ pr1;
+ const uint32_t cp4 = ab3 ^ ab23;
+ const uint32_t tinv1 = cp3 ^ cp4;
+ const uint32_t tinv2 = cp3 & cp1;
+ const uint32_t tinv3 = cp2 ^ tinv2;
+ const uint32_t tinv4 = cp1 ^ cp2;
+ const uint32_t tinv5 = cp4 ^ tinv2;
+ const uint32_t tinv6 = tinv5 & tinv4;
+ const uint32_t tinv7 = tinv3 & tinv1;
+ const uint32_t d2 = cp4 ^ tinv7;
+ const uint32_t d0 = cp2 ^ tinv6;
+ const uint32_t tinv8 = cp1 & cp4;
+ const uint32_t tinv9 = tinv4 & tinv8;
+ const uint32_t tinv10 = tinv4 ^ tinv2;
+ const uint32_t d1 = tinv9 ^ tinv10;
+ const uint32_t tinv11 = cp2 & cp3;
+ const uint32_t tinv12 = tinv1 & tinv11;
+ const uint32_t tinv13 = tinv1 ^ tinv2;
+ 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 dh = d2 ^ d3;
+ const uint32_t dd = sd0 ^ sd1;
+ const uint32_t abcd3 = dh & bh;
+ const uint32_t rr2 = d3 & Y4;
+ const uint32_t t02 = d2 & Y5;
+ const uint32_t abcd4 = dl & bl;
+ const uint32_t r4 = d1 & Y6;
+ const uint32_t r5 = d0 & Y7;
+ const uint32_t r6 = sd0 & sb0;
+ const uint32_t vr2 = dd & bb;
+ const uint32_t wr2 = sd1 & sb1;
+ const uint32_t abcd5 = dh & ah;
+ const uint32_t r7 = d3 & Y0;
+ const uint32_t r8 = d2 & Y1;
+ const uint32_t abcd6 = dl & al;
+ const uint32_t r9 = d1 & Y2;
+ const uint32_t r10 = d0 & Y3;
+ const uint32_t r11 = sd0 & sa0;
+ const uint32_t vr3 = dd & aa;
+ const uint32_t wr3 = sd1 & sa1;
+ const uint32_t ph12 = rr2 ^ abcd3;
+ const uint32_t ph02 = t02 ^ abcd3;
+ const uint32_t pl12 = r4 ^ abcd4;
+ const uint32_t pl02 = r5 ^ abcd4;
+ const uint32_t pr2 = vr2 ^ r6;
+ const uint32_t qr2 = wr2 ^ r6;
+ const uint32_t p0 = ph12 ^ pr2;
+ const uint32_t p1 = ph02 ^ qr2;
+ const uint32_t p2 = pl12 ^ pr2;
+ const uint32_t p3 = pl02 ^ qr2;
+ const uint32_t ph13 = r7 ^ abcd5;
+ const uint32_t ph03 = r8 ^ abcd5;
+ const uint32_t pl13 = r9 ^ abcd6;
+ const uint32_t pl03 = r10 ^ abcd6;
+ const uint32_t pr3 = vr3 ^ r11;
+ const uint32_t qr3 = wr3 ^ r11;
+ const uint32_t p4 = ph13 ^ pr3;
+ const uint32_t S7 = ph03 ^ qr3;
+ const uint32_t p6 = pl13 ^ pr3;
+ const uint32_t p7 = pl03 ^ qr3;
+ const uint32_t S3 = p1 ^ p6;
+ const uint32_t S6 = p2 ^ p6;
+ const uint32_t S0 = p3 ^ p6;
+ const uint32_t X11 = p0 ^ p2;
+ const uint32_t S5 = S0 ^ X11;
+ const uint32_t X13 = p4 ^ p7;
+ const uint32_t X14 = X11 ^ X13;
+ const uint32_t S1 = S3 ^ X14;
+ const uint32_t X16 = p1 ^ S7;
+ const uint32_t S2 = X14 ^ X16;
+ const uint32_t X18 = p0 ^ p4;
+ const uint32_t X19 = S5 ^ X16;
+ const uint32_t S4 = X18 ^ X19;
+
+ V[0] = S0;
+ V[1] = S1;
+ V[2] = S2;
+ V[3] = S3;
+ V[4] = S4;
+ V[5] = S5;
+ V[6] = S6;
+ V[7] = S7;
+ }
+
+inline void bit_transpose(uint32_t B[8])
+ {
+ swap_bits<uint32_t>(B[1], B[0], 0x55555555, 1);
+ swap_bits<uint32_t>(B[3], B[2], 0x55555555, 1);
+ swap_bits<uint32_t>(B[5], B[4], 0x55555555, 1);
+ swap_bits<uint32_t>(B[7], B[6], 0x55555555, 1);
+
+ swap_bits<uint32_t>(B[2], B[0], 0x33333333, 2);
+ swap_bits<uint32_t>(B[3], B[1], 0x33333333, 2);
+ swap_bits<uint32_t>(B[6], B[4], 0x33333333, 2);
+ swap_bits<uint32_t>(B[7], B[5], 0x33333333, 2);
+
+ swap_bits<uint32_t>(B[4], B[0], 0x0F0F0F0F, 4);
+ swap_bits<uint32_t>(B[5], B[1], 0x0F0F0F0F, 4);
+ swap_bits<uint32_t>(B[6], B[2], 0x0F0F0F0F, 4);
+ swap_bits<uint32_t>(B[7], B[3], 0x0F0F0F0F, 4);
+ }
+
+inline void ks_expand(uint32_t B[8], const uint32_t K[], size_t r)
+ {
+ /*
+ This is bit_transpose of K[r..r+4] || K[r..r+4], we can save some computation
+ due to knowing the first and second halves are the same data.
+ */
+ for(size_t i = 0; i != 4; ++i)
+ B[i] = K[r + i];
+
+ swap_bits<uint32_t>(B[1], B[0], 0x55555555, 1);
+ swap_bits<uint32_t>(B[3], B[2], 0x55555555, 1);
+
+ swap_bits<uint32_t>(B[2], B[0], 0x33333333, 2);
+ swap_bits<uint32_t>(B[3], B[1], 0x33333333, 2);
+
+ B[4] = B[0];
+ B[5] = B[1];
+ B[6] = B[2];
+ B[7] = B[3];
+
+ swap_bits<uint32_t>(B[4], B[0], 0x0F0F0F0F, 4);
+ swap_bits<uint32_t>(B[5], B[1], 0x0F0F0F0F, 4);
+ swap_bits<uint32_t>(B[6], B[2], 0x0F0F0F0F, 4);
+ swap_bits<uint32_t>(B[7], B[3], 0x0F0F0F0F, 4);
+ }
+
+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<uint64_t>(B[i]) << 32) | B[i+1];
+ x = bit_permute_step<uint64_t>(x, 0x0022331100223311, 2);
+ x = bit_permute_step<uint64_t>(x, 0x0055005500550055, 1);
+ B[i] = static_cast<uint32_t>(x >> 32);
+ B[i+1] = static_cast<uint32_t>(x);
+ }
+#else
+ for(size_t i = 0; i != 8; ++i)
+ {
+ uint32_t x = B[i];
+ x = bit_permute_step<uint32_t>(x, 0x00223311, 2);
+ x = bit_permute_step<uint32_t>(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<uint64_t>(B[i]) << 32) | B[i+1];
+ x = bit_permute_step<uint64_t>(x, 0x0055005500550055, 1);
+ x = bit_permute_step<uint64_t>(x, 0x0022331100223311, 2);
+ B[i] = static_cast<uint32_t>(x >> 32);
+ B[i+1] = static_cast<uint32_t>(x);
+ }
+#else
+ for(size_t i = 0; i != 8; ++i)
+ {
+ uint32_t x = B[i];
+ x = bit_permute_step<uint32_t>(x, 0x00550055, 1);
+ x = bit_permute_step<uint32_t>(x, 0x00223311, 2);
+ B[i] = x;
+ }
+#endif
+ }
+
+inline void mix_columns(uint32_t B[8])
+ {
+ // carry high bits in B[0] to positions in 0x1b == 0b11011
+ const uint32_t X2[8] = {
+ B[1],
+ B[2],
+ B[3],
+ B[4] ^ B[0],
+ B[5] ^ B[0],
+ B[6],
+ B[7] ^ B[0],
+ B[0],
+ };
+
+ for(size_t i = 0; i != 8; i++)
+ {
+ const uint32_t X3 = B[i] ^ X2[i];
+ B[i] = X2[i] ^ rotr<8>(B[i]) ^ rotr<16>(B[i]) ^ rotr<24>(X3);
+ }
+ }
+
+void inv_mix_columns(uint32_t B[8])
+ {
+ /*
+ OpenSSL's bsaes implementation credits Jussi Kivilinna with the lovely
+ matrix decomposition
+
+ | 0e 0b 0d 09 | | 02 03 01 01 | | 05 00 04 00 |
+ | 09 0e 0b 0d | = | 01 02 03 01 | x | 00 05 00 04 |
+ | 0d 09 0e 0b | | 01 01 02 03 | | 04 00 05 00 |
+ | 0b 0d 09 0e | | 03 01 01 02 | | 00 04 00 05 |
+
+ Notice the first component is simply the MixColumns matrix. So we can
+ multiply first by (05,00,04,00) then perform MixColumns to get the equivalent
+ of InvMixColumn.
+ */
+ const uint32_t X4[8] = {
+ B[2],
+ B[3],
+ B[4] ^ B[0],
+ B[5] ^ B[0] ^ B[1],
+ B[6] ^ B[1],
+ B[7] ^ B[0],
+ B[0] ^ B[1],
+ B[1],
+ };
+
+ for(size_t i = 0; i != 8; i++)
+ {
+ const uint32_t X5 = X4[i] ^ B[i];
+ B[i] = X5 ^ rotr<16>(X4[i]);
+ }
+
+ mix_columns(B);
+ }
+
+/*
+* AES Encryption
+*/
+void aes_encrypt_n(const uint8_t in[], uint8_t out[],
+ size_t blocks,
+ const secure_vector<uint32_t>& EK)
+ {
+ BOTAN_ASSERT(EK.size() == 44 || EK.size() == 52 || EK.size() == 60, "Key was set");
+
+ const size_t rounds = (EK.size() - 4) / 4;
+
+ uint32_t KS[13*8] = { 0 }; // actual maximum is (rounds - 1) * 8
+ for(size_t i = 0; i < rounds - 1; i += 1)
+ {
+ ks_expand(&KS[8*i], EK.data(), 4*i + 4);
+ }
+
+ const size_t BLOCK_SIZE = 16;
+ const size_t BITSLICED_BLOCKS = 8*sizeof(uint32_t) / BLOCK_SIZE;
+
+ while(blocks > 0)
+ {
+ const size_t this_loop = std::min(blocks, BITSLICED_BLOCKS);
+
+ uint32_t B[8] = { 0 };
+
+ load_be(B, in, this_loop*4);
+
+ for(size_t i = 0; i != 8; ++i)
+ B[i] ^= EK[i % 4];
+
+ bit_transpose(B);
+
+ for(size_t r = 0; r != rounds - 1; ++r)
+ {
+ AES_SBOX(B);
+ shift_rows(B);
+ mix_columns(B);
+
+ for(size_t i = 0; i != 8; ++i)
+ B[i] ^= KS[8*r + i];
+ }
+
+ // Final round:
+ AES_SBOX(B);
+ shift_rows(B);
+ bit_transpose(B);
+
+ for(size_t i = 0; i != 8; ++i)
+ B[i] ^= EK[4*rounds + i % 4];
+
+ copy_out_be(out, this_loop*4*sizeof(uint32_t), B);
+
+ in += this_loop * BLOCK_SIZE;
+ out += this_loop * BLOCK_SIZE;
+ blocks -= this_loop;
+ }
+ }
+
+/*
+* AES Decryption
+*/
+void aes_decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks,
+ const secure_vector<uint32_t>& DK)
+ {
+ BOTAN_ASSERT(DK.size() == 44 || DK.size() == 52 || DK.size() == 60, "Key was set");
+
+ const size_t rounds = (DK.size() - 4) / 4;
+
+ uint32_t KS[13*8] = { 0 }; // actual maximum is (rounds - 1) * 8
+ for(size_t i = 0; i < rounds - 1; i += 1)
+ {
+ ks_expand(&KS[8*i], DK.data(), 4*i + 4);
+ }
+
+ const size_t BLOCK_SIZE = 16;
+ const size_t BITSLICED_BLOCKS = 8*sizeof(uint32_t) / BLOCK_SIZE;
+
+ while(blocks > 0)
+ {
+ const size_t this_loop = std::min(blocks, BITSLICED_BLOCKS);
+
+ uint32_t B[8] = { 0 };
+
+ load_be(B, in, this_loop*4);
+
+ for(size_t i = 0; i != 8; ++i)
+ B[i] ^= DK[i % 4];
+
+ bit_transpose(B);
+
+ for(size_t r = 0; r != rounds - 1; ++r)
+ {
+ AES_INV_SBOX(B);
+ inv_shift_rows(B);
+ inv_mix_columns(B);
+
+ for(size_t i = 0; i != 8; ++i)
+ B[i] ^= KS[8*r + i];
+ }
+
+ // Final round:
+ AES_INV_SBOX(B);
+ inv_shift_rows(B);
+ bit_transpose(B);
+
+ for(size_t i = 0; i != 8; ++i)
+ B[i] ^= DK[4*rounds + i % 4];
+
+ copy_out_be(out, this_loop*4*sizeof(uint32_t), B);
+
+ in += this_loop * BLOCK_SIZE;
+ out += this_loop * BLOCK_SIZE;
+ blocks -= this_loop;
+ }
+ }
+
+inline uint32_t xtime32(uint32_t s)
+ {
+ const uint32_t lo_bit = 0x01010101;
+ const uint32_t mask = 0x7F7F7F7F;
+ const uint32_t poly = 0x1B;
+
+ return ((s & mask) << 1) ^ (((s >> 7) & lo_bit) * poly);
+ }
+
+inline uint32_t InvMixColumn(uint32_t s1)
+ {
+ const uint32_t s2 = xtime32(s1);
+ const uint32_t s4 = xtime32(s2);
+ const uint32_t s8 = xtime32(s4);
+ const uint32_t s9 = s8 ^ s1;
+ const uint32_t s11 = s9 ^ s2;
+ const uint32_t s13 = s9 ^ s4;
+ const uint32_t s14 = s8 ^ s4 ^ s2;
+
+ return s14 ^ rotr<8>(s9) ^ rotr<16>(s13) ^ rotr<24>(s11);
+ }
+
+void InvMixColumn_x4(uint32_t x[4])
+ {
+ x[0] = InvMixColumn(x[0]);
+ x[1] = InvMixColumn(x[1]);
+ x[2] = InvMixColumn(x[2]);
+ x[3] = InvMixColumn(x[3]);
+ }
+
+uint32_t SE_word(uint32_t x)
+ {
+ uint32_t I[8] = { 0 };
+
+ for(size_t i = 0; i != 8; ++i)
+ I[i] = (x >> (7-i)) & 0x01010101;
+
+ AES_SBOX(I);
+
+ x = 0;
+
+ for(size_t i = 0; i != 8; ++i)
+ x |= ((I[i] & 0x01010101) << (7-i));
+
+ return x;
+ }
+
+void aes_key_schedule(const uint8_t key[], size_t length,
+ secure_vector<uint32_t>& EK,
+ secure_vector<uint32_t>& DK,
+ bool bswap_keys = false)
+ {
+ static const uint32_t RC[10] = {
+ 0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000,
+ 0x20000000, 0x40000000, 0x80000000, 0x1B000000, 0x36000000 };
+
+ const size_t X = length / 4;
+
+ // Can't happen, but make static analyzers happy
+ BOTAN_ASSERT_NOMSG(X == 4 || X == 6 || X == 8);
+
+ const size_t rounds = (length / 4) + 6;
+
+ // Help the optimizer
+ BOTAN_ASSERT_NOMSG(rounds == 10 || rounds == 12 || rounds == 14);
+
+ CT::poison(key, length);
+
+ EK.resize(length + 28);
+ DK.resize(length + 28);
+
+ for(size_t i = 0; i != X; ++i)
+ EK[i] = load_be<uint32_t>(key, i);
+
+ for(size_t i = X; i < 4*(rounds+1); i += X)
+ {
+ EK[i] = EK[i-X] ^ RC[(i-X)/X] ^ rotl<8>(SE_word(EK[i-1]));
+
+ for(size_t j = 1; j != X && (i+j) < EK.size(); ++j)
+ {
+ EK[i+j] = EK[i+j-X];
+
+ if(X == 8 && j == 4)
+ EK[i+j] ^= SE_word(EK[i+j-1]);
+ else
+ EK[i+j] ^= EK[i+j-1];
+ }
+ }
+
+ for(size_t i = 0; i != 4*(rounds+1); i += 4)
+ {
+ DK[i ] = EK[4*rounds - i ];
+ DK[i+1] = EK[4*rounds - i+1];
+ DK[i+2] = EK[4*rounds - i+2];
+ DK[i+3] = EK[4*rounds - i+3];
+ }
+
+ for(size_t i = 4; i != 4*rounds; i += 4)
+ {
+ InvMixColumn_x4(&DK[i]);
+ }
+
+ if(bswap_keys)
+ {
+ // HW AES on little endian needs the subkeys to be byte reversed
+ for(size_t i = 0; i != EK.size(); ++i)
+ EK[i] = reverse_bytes(EK[i]);
+ for(size_t i = 0; i != DK.size(); ++i)
+ DK[i] = reverse_bytes(DK[i]);
+ }
+
+ CT::unpoison(EK.data(), EK.size());
+ CT::unpoison(DK.data(), DK.size());
+ CT::unpoison(key, length);
+ }
+
+size_t aes_parallelism()
+ {
+#if defined(BOTAN_HAS_HW_AES_SUPPORT)
+ if(CPUID::has_hw_aes())
+ {
+ return 4; // pipelined
+ }
+#endif
+
+#if defined(BOTAN_HAS_AES_VPERM)
+ if(CPUID::has_vperm())
+ {
+ return 2; // pipelined
+ }
+#endif
+
+ // bitsliced:
+ return 2;
+ }
+
+const char* aes_provider()
+ {
+#if defined(BOTAN_HAS_HW_AES_SUPPORT)
+ if(CPUID::has_hw_aes())
+ {
+ return "cpu";
+ }
+#endif
+
+#if defined(BOTAN_HAS_AES_VPERM)
+ if(CPUID::has_vperm())
+ {
+ return "vperm";
+ }
+#endif
+
+ return "base";
+ }
+
+}
+
+std::string AES_128::provider() const { return aes_provider(); }
+std::string AES_192::provider() const { return aes_provider(); }
+std::string AES_256::provider() const { return aes_provider(); }
+
+size_t AES_128::parallelism() const { return aes_parallelism(); }
+size_t AES_192::parallelism() const { return aes_parallelism(); }
+size_t AES_256::parallelism() const { return aes_parallelism(); }
+
+void AES_128::encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+ {
+ verify_key_set(m_EK.empty() == false);
+
+#if defined(BOTAN_HAS_HW_AES_SUPPORT)
+ if(CPUID::has_hw_aes())
+ {
+ return hw_aes_encrypt_n(in, out, blocks);
+ }
+#endif
+
+#if defined(BOTAN_HAS_AES_VPERM)
+ if(CPUID::has_vperm())
+ {
+ return vperm_encrypt_n(in, out, blocks);
+ }
+#endif
+
+ aes_encrypt_n(in, out, blocks, m_EK);
+ }
+
+void AES_128::decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+ {
+ verify_key_set(m_DK.empty() == false);
+
+#if defined(BOTAN_HAS_HW_AES_SUPPORT)
+ if(CPUID::has_hw_aes())
+ {
+ return hw_aes_decrypt_n(in, out, blocks);
+ }
+#endif
+
+#if defined(BOTAN_HAS_AES_VPERM)
+ if(CPUID::has_vperm())
+ {
+ return vperm_decrypt_n(in, out, blocks);
+ }
+#endif
+
+ aes_decrypt_n(in, out, blocks, m_DK);
+ }
+
+void AES_128::key_schedule(const uint8_t key[], size_t length)
+ {
+#if defined(BOTAN_HAS_AES_NI)
+ if(CPUID::has_aes_ni())
+ {
+ return aesni_key_schedule(key, length);
+ }
+#endif
+
+#if defined(BOTAN_HAS_HW_AES_SUPPORT)
+ if(CPUID::has_hw_aes())
+ {
+ return aes_key_schedule(key, length, m_EK, m_DK, CPUID::is_little_endian());
+ }
+#endif
+
+#if defined(BOTAN_HAS_AES_VPERM)
+ if(CPUID::has_vperm())
+ {
+ return vperm_key_schedule(key, length);
+ }
+#endif
+
+ aes_key_schedule(key, length, m_EK, m_DK);
+ }
+
+void AES_128::clear()
+ {
+ zap(m_EK);
+ zap(m_DK);
+ }
+
+void AES_192::encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+ {
+ verify_key_set(m_EK.empty() == false);
+
+#if defined(BOTAN_HAS_HW_AES_SUPPORT)
+ if(CPUID::has_hw_aes())
+ {
+ return hw_aes_encrypt_n(in, out, blocks);
+ }
+#endif
+
+#if defined(BOTAN_HAS_AES_VPERM)
+ if(CPUID::has_vperm())
+ {
+ return vperm_encrypt_n(in, out, blocks);
+ }
+#endif
+
+ aes_encrypt_n(in, out, blocks, m_EK);
+ }
+
+void AES_192::decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+ {
+ verify_key_set(m_DK.empty() == false);
+
+#if defined(BOTAN_HAS_HW_AES_SUPPORT)
+ if(CPUID::has_hw_aes())
+ {
+ return hw_aes_decrypt_n(in, out, blocks);
+ }
+#endif
+
+#if defined(BOTAN_HAS_AES_VPERM)
+ if(CPUID::has_vperm())
+ {
+ return vperm_decrypt_n(in, out, blocks);
+ }
+#endif
+
+ aes_decrypt_n(in, out, blocks, m_DK);
+ }
+
+void AES_192::key_schedule(const uint8_t key[], size_t length)
+ {
+#if defined(BOTAN_HAS_AES_NI)
+ if(CPUID::has_aes_ni())
+ {
+ return aesni_key_schedule(key, length);
+ }
+#endif
+
+#if defined(BOTAN_HAS_HW_AES_SUPPORT)
+ if(CPUID::has_hw_aes())
+ {
+ return aes_key_schedule(key, length, m_EK, m_DK, CPUID::is_little_endian());
+ }
+#endif
+
+#if defined(BOTAN_HAS_AES_VPERM)
+ if(CPUID::has_vperm())
+ {
+ return vperm_key_schedule(key, length);
+ }
+#endif
+
+ aes_key_schedule(key, length, m_EK, m_DK);
+ }
+
+void AES_192::clear()
+ {
+ zap(m_EK);
+ zap(m_DK);
+ }
+
+void AES_256::encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+ {
+ verify_key_set(m_EK.empty() == false);
+
+#if defined(BOTAN_HAS_HW_AES_SUPPORT)
+ if(CPUID::has_hw_aes())
+ {
+ return hw_aes_encrypt_n(in, out, blocks);
+ }
+#endif
+
+#if defined(BOTAN_HAS_AES_VPERM)
+ if(CPUID::has_vperm())
+ {
+ return vperm_encrypt_n(in, out, blocks);
+ }
+#endif
+
+ aes_encrypt_n(in, out, blocks, m_EK);
+ }
+
+void AES_256::decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+ {
+ verify_key_set(m_DK.empty() == false);
+
+#if defined(BOTAN_HAS_HW_AES_SUPPORT)
+ if(CPUID::has_hw_aes())
+ {
+ return hw_aes_decrypt_n(in, out, blocks);
+ }
+#endif
+
+#if defined(BOTAN_HAS_AES_VPERM)
+ if(CPUID::has_vperm())
+ {
+ return vperm_decrypt_n(in, out, blocks);
+ }
+#endif
+
+ aes_decrypt_n(in, out, blocks, m_DK);
+ }
+
+void AES_256::key_schedule(const uint8_t key[], size_t length)
+ {
+#if defined(BOTAN_HAS_AES_NI)
+ if(CPUID::has_aes_ni())
+ {
+ return aesni_key_schedule(key, length);
+ }
+#endif
+
+#if defined(BOTAN_HAS_HW_AES_SUPPORT)
+ if(CPUID::has_hw_aes())
+ {
+ return aes_key_schedule(key, length, m_EK, m_DK, CPUID::is_little_endian());
+ }
+#endif
+
+#if defined(BOTAN_HAS_AES_VPERM)
+ if(CPUID::has_vperm())
+ {
+ return vperm_key_schedule(key, length);
+ }
+#endif
+
+ aes_key_schedule(key, length, m_EK, m_DK);
+ }
+
+void AES_256::clear()
+ {
+ zap(m_EK);
+ zap(m_DK);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/block/aes/aes.h b/comm/third_party/botan/src/lib/block/aes/aes.h
new file mode 100644
index 0000000000..76248200d4
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/aes/aes.h
@@ -0,0 +1,131 @@
+/*
+* AES
+* (C) 1999-2010 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_AES_H_
+#define BOTAN_AES_H_
+
+#include <botan/block_cipher.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(aes.h)
+
+namespace Botan {
+
+/**
+* AES-128
+*/
+class BOTAN_PUBLIC_API(2,0) AES_128 final : public Block_Cipher_Fixed_Params<16, 16>
+ {
+ public:
+ void encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+ 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 "AES-128"; }
+ BlockCipher* clone() const override { return new AES_128; }
+ size_t parallelism() const override;
+
+ private:
+ void key_schedule(const uint8_t key[], size_t length) override;
+
+#if defined(BOTAN_HAS_AES_VPERM)
+ void vperm_encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const;
+ void vperm_decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const;
+ void vperm_key_schedule(const uint8_t key[], size_t length);
+#endif
+
+#if defined(BOTAN_HAS_AES_NI)
+ void aesni_key_schedule(const uint8_t key[], size_t length);
+#endif
+
+#if defined(BOTAN_HAS_AES_POWER8) || defined(BOTAN_HAS_AES_ARMV8) || defined(BOTAN_HAS_AES_NI)
+ void hw_aes_encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const;
+ void hw_aes_decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const;
+#endif
+
+ secure_vector<uint32_t> m_EK, m_DK;
+ };
+
+/**
+* AES-192
+*/
+class BOTAN_PUBLIC_API(2,0) AES_192 final : public Block_Cipher_Fixed_Params<16, 24>
+ {
+ public:
+ void encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+ 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 "AES-192"; }
+ BlockCipher* clone() const override { return new AES_192; }
+ size_t parallelism() const override;
+
+ private:
+#if defined(BOTAN_HAS_AES_VPERM)
+ void vperm_encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const;
+ void vperm_decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const;
+ void vperm_key_schedule(const uint8_t key[], size_t length);
+#endif
+
+#if defined(BOTAN_HAS_AES_NI)
+ void aesni_key_schedule(const uint8_t key[], size_t length);
+#endif
+
+#if defined(BOTAN_HAS_AES_POWER8) || defined(BOTAN_HAS_AES_ARMV8) || defined(BOTAN_HAS_AES_NI)
+ void hw_aes_encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const;
+ void hw_aes_decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const;
+#endif
+
+ void key_schedule(const uint8_t key[], size_t length) override;
+
+ secure_vector<uint32_t> m_EK, m_DK;
+ };
+
+/**
+* AES-256
+*/
+class BOTAN_PUBLIC_API(2,0) AES_256 final : public Block_Cipher_Fixed_Params<16, 32>
+ {
+ public:
+ void encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+ 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 "AES-256"; }
+ BlockCipher* clone() const override { return new AES_256; }
+ size_t parallelism() const override;
+
+ private:
+#if defined(BOTAN_HAS_AES_VPERM)
+ void vperm_encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const;
+ void vperm_decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const;
+ void vperm_key_schedule(const uint8_t key[], size_t length);
+#endif
+
+#if defined(BOTAN_HAS_AES_NI)
+ void aesni_key_schedule(const uint8_t key[], size_t length);
+#endif
+
+#if defined(BOTAN_HAS_AES_POWER8) || defined(BOTAN_HAS_AES_ARMV8) || defined(BOTAN_HAS_AES_NI)
+ void hw_aes_encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const;
+ void hw_aes_decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const;
+#endif
+
+ void key_schedule(const uint8_t key[], size_t length) override;
+
+ secure_vector<uint32_t> m_EK, m_DK;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/block/aes/aes_armv8/aes_armv8.cpp b/comm/third_party/botan/src/lib/block/aes/aes_armv8/aes_armv8.cpp
new file mode 100644
index 0000000000..9766bf88c9
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/aes/aes_armv8/aes_armv8.cpp
@@ -0,0 +1,484 @@
+/*
+* AES using ARMv8
+* Contributed by Jeffrey Walton
+*
+* Further changes
+* (C) 2017,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/aes.h>
+#include <botan/loadstor.h>
+#include <arm_neon.h>
+
+namespace Botan {
+
+#define AES_ENC_4_ROUNDS(K) \
+ do \
+ { \
+ 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)); \
+ } while(0)
+
+#define AES_ENC_4_LAST_ROUNDS(K, K2) \
+ do \
+ { \
+ 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); \
+ } while(0)
+
+#define AES_DEC_4_ROUNDS(K) \
+ do \
+ { \
+ 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)); \
+ } while(0)
+
+#define AES_DEC_4_LAST_ROUNDS(K, K2) \
+ do \
+ { \
+ 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); \
+ } while(0)
+
+/*
+* AES-128 Encryption
+*/
+BOTAN_FUNC_ISA("+crypto")
+void AES_128::hw_aes_encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+ {
+ const uint8_t *skey = reinterpret_cast<const uint8_t*>(m_EK.data());
+
+ const uint8x16_t K0 = vld1q_u8(skey + 0*16);
+ const uint8x16_t K1 = vld1q_u8(skey + 1*16);
+ const uint8x16_t K2 = vld1q_u8(skey + 2*16);
+ const uint8x16_t K3 = vld1q_u8(skey + 3*16);
+ const uint8x16_t K4 = vld1q_u8(skey + 4*16);
+ const uint8x16_t K5 = vld1q_u8(skey + 5*16);
+ const uint8x16_t K6 = vld1q_u8(skey + 6*16);
+ const uint8x16_t K7 = vld1q_u8(skey + 7*16);
+ const uint8x16_t K8 = vld1q_u8(skey + 8*16);
+ const uint8x16_t K9 = vld1q_u8(skey + 9*16);
+ const uint8x16_t K10 = vld1q_u8(skey + 10*16);
+
+ while(blocks >= 4)
+ {
+ uint8x16_t B0 = vld1q_u8(in);
+ uint8x16_t B1 = vld1q_u8(in+16);
+ uint8x16_t B2 = vld1q_u8(in+32);
+ uint8x16_t B3 = vld1q_u8(in+48);
+
+ AES_ENC_4_ROUNDS(K0);
+ AES_ENC_4_ROUNDS(K1);
+ AES_ENC_4_ROUNDS(K2);
+ AES_ENC_4_ROUNDS(K3);
+ AES_ENC_4_ROUNDS(K4);
+ AES_ENC_4_ROUNDS(K5);
+ AES_ENC_4_ROUNDS(K6);
+ AES_ENC_4_ROUNDS(K7);
+ AES_ENC_4_ROUNDS(K8);
+ AES_ENC_4_LAST_ROUNDS(K9, K10);
+
+ vst1q_u8(out, B0);
+ vst1q_u8(out+16, B1);
+ vst1q_u8(out+32, B2);
+ vst1q_u8(out+48, B3);
+
+ in += 16*4;
+ out += 16*4;
+ blocks -= 4;
+ }
+
+ for(size_t i = 0; i != blocks; ++i)
+ {
+ uint8x16_t B = vld1q_u8(in+16*i);
+ B = vaesmcq_u8(vaeseq_u8(B, K0));
+ B = vaesmcq_u8(vaeseq_u8(B, K1));
+ B = vaesmcq_u8(vaeseq_u8(B, K2));
+ B = vaesmcq_u8(vaeseq_u8(B, K3));
+ B = vaesmcq_u8(vaeseq_u8(B, K4));
+ B = vaesmcq_u8(vaeseq_u8(B, K5));
+ B = vaesmcq_u8(vaeseq_u8(B, K6));
+ B = vaesmcq_u8(vaeseq_u8(B, K7));
+ B = vaesmcq_u8(vaeseq_u8(B, K8));
+ B = veorq_u8(vaeseq_u8(B, K9), K10);
+ vst1q_u8(out+16*i, B);
+ }
+ }
+
+/*
+* AES-128 Decryption
+*/
+BOTAN_FUNC_ISA("+crypto")
+void AES_128::hw_aes_decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+ {
+ const uint8_t *skey = reinterpret_cast<const uint8_t*>(m_DK.data());
+
+ const uint8x16_t K0 = vld1q_u8(skey + 0*16);
+ const uint8x16_t K1 = vld1q_u8(skey + 1*16);
+ const uint8x16_t K2 = vld1q_u8(skey + 2*16);
+ const uint8x16_t K3 = vld1q_u8(skey + 3*16);
+ const uint8x16_t K4 = vld1q_u8(skey + 4*16);
+ const uint8x16_t K5 = vld1q_u8(skey + 5*16);
+ const uint8x16_t K6 = vld1q_u8(skey + 6*16);
+ const uint8x16_t K7 = vld1q_u8(skey + 7*16);
+ const uint8x16_t K8 = vld1q_u8(skey + 8*16);
+ const uint8x16_t K9 = vld1q_u8(skey + 9*16);
+ const uint8x16_t K10 = vld1q_u8(skey + 10*16);
+
+ while(blocks >= 4)
+ {
+ uint8x16_t B0 = vld1q_u8(in);
+ uint8x16_t B1 = vld1q_u8(in+16);
+ uint8x16_t B2 = vld1q_u8(in+32);
+ uint8x16_t B3 = vld1q_u8(in+48);
+
+ AES_DEC_4_ROUNDS(K0);
+ AES_DEC_4_ROUNDS(K1);
+ AES_DEC_4_ROUNDS(K2);
+ AES_DEC_4_ROUNDS(K3);
+ AES_DEC_4_ROUNDS(K4);
+ AES_DEC_4_ROUNDS(K5);
+ AES_DEC_4_ROUNDS(K6);
+ AES_DEC_4_ROUNDS(K7);
+ AES_DEC_4_ROUNDS(K8);
+ AES_DEC_4_LAST_ROUNDS(K9, K10);
+
+ vst1q_u8(out, B0);
+ vst1q_u8(out+16, B1);
+ vst1q_u8(out+32, B2);
+ vst1q_u8(out+48, B3);
+
+ in += 16*4;
+ out += 16*4;
+ blocks -= 4;
+ }
+
+ for(size_t i = 0; i != blocks; ++i)
+ {
+ uint8x16_t B = vld1q_u8(in+16*i);
+ B = vaesimcq_u8(vaesdq_u8(B, K0));
+ B = vaesimcq_u8(vaesdq_u8(B, K1));
+ B = vaesimcq_u8(vaesdq_u8(B, K2));
+ B = vaesimcq_u8(vaesdq_u8(B, K3));
+ B = vaesimcq_u8(vaesdq_u8(B, K4));
+ B = vaesimcq_u8(vaesdq_u8(B, K5));
+ B = vaesimcq_u8(vaesdq_u8(B, K6));
+ B = vaesimcq_u8(vaesdq_u8(B, K7));
+ B = vaesimcq_u8(vaesdq_u8(B, K8));
+ B = veorq_u8(vaesdq_u8(B, K9), K10);
+ vst1q_u8(out+16*i, B);
+ }
+ }
+
+/*
+* AES-192 Encryption
+*/
+BOTAN_FUNC_ISA("+crypto")
+void AES_192::hw_aes_encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+ {
+ const uint8_t *skey = reinterpret_cast<const uint8_t*>(m_EK.data());
+
+ const uint8x16_t K0 = vld1q_u8(skey + 0*16);
+ const uint8x16_t K1 = vld1q_u8(skey + 1*16);
+ const uint8x16_t K2 = vld1q_u8(skey + 2*16);
+ const uint8x16_t K3 = vld1q_u8(skey + 3*16);
+ const uint8x16_t K4 = vld1q_u8(skey + 4*16);
+ const uint8x16_t K5 = vld1q_u8(skey + 5*16);
+ const uint8x16_t K6 = vld1q_u8(skey + 6*16);
+ const uint8x16_t K7 = vld1q_u8(skey + 7*16);
+ const uint8x16_t K8 = vld1q_u8(skey + 8*16);
+ const uint8x16_t K9 = vld1q_u8(skey + 9*16);
+ const uint8x16_t K10 = vld1q_u8(skey + 10*16);
+ const uint8x16_t K11 = vld1q_u8(skey + 11*16);
+ const uint8x16_t K12 = vld1q_u8(skey + 12*16);
+
+ while(blocks >= 4)
+ {
+ uint8x16_t B0 = vld1q_u8(in);
+ uint8x16_t B1 = vld1q_u8(in+16);
+ uint8x16_t B2 = vld1q_u8(in+32);
+ uint8x16_t B3 = vld1q_u8(in+48);
+
+ AES_ENC_4_ROUNDS(K0);
+ AES_ENC_4_ROUNDS(K1);
+ AES_ENC_4_ROUNDS(K2);
+ AES_ENC_4_ROUNDS(K3);
+ AES_ENC_4_ROUNDS(K4);
+ AES_ENC_4_ROUNDS(K5);
+ AES_ENC_4_ROUNDS(K6);
+ AES_ENC_4_ROUNDS(K7);
+ AES_ENC_4_ROUNDS(K8);
+ AES_ENC_4_ROUNDS(K9);
+ AES_ENC_4_ROUNDS(K10);
+ AES_ENC_4_LAST_ROUNDS(K11, K12);
+
+ vst1q_u8(out, B0);
+ vst1q_u8(out+16, B1);
+ vst1q_u8(out+32, B2);
+ vst1q_u8(out+48, B3);
+
+ in += 16*4;
+ out += 16*4;
+ blocks -= 4;
+ }
+
+ for(size_t i = 0; i != blocks; ++i)
+ {
+ uint8x16_t B = vld1q_u8(in+16*i);
+ B = vaesmcq_u8(vaeseq_u8(B, K0));
+ B = vaesmcq_u8(vaeseq_u8(B, K1));
+ B = vaesmcq_u8(vaeseq_u8(B, K2));
+ B = vaesmcq_u8(vaeseq_u8(B, K3));
+ B = vaesmcq_u8(vaeseq_u8(B, K4));
+ B = vaesmcq_u8(vaeseq_u8(B, K5));
+ B = vaesmcq_u8(vaeseq_u8(B, K6));
+ B = vaesmcq_u8(vaeseq_u8(B, K7));
+ B = vaesmcq_u8(vaeseq_u8(B, K8));
+ B = vaesmcq_u8(vaeseq_u8(B, K9));
+ B = vaesmcq_u8(vaeseq_u8(B, K10));
+ B = veorq_u8(vaeseq_u8(B, K11), K12);
+ vst1q_u8(out+16*i, B);
+ }
+ }
+
+/*
+* AES-192 Decryption
+*/
+BOTAN_FUNC_ISA("+crypto")
+void AES_192::hw_aes_decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+ {
+ const uint8_t *skey = reinterpret_cast<const uint8_t*>(m_DK.data());
+
+ const uint8x16_t K0 = vld1q_u8(skey + 0*16);
+ const uint8x16_t K1 = vld1q_u8(skey + 1*16);
+ const uint8x16_t K2 = vld1q_u8(skey + 2*16);
+ const uint8x16_t K3 = vld1q_u8(skey + 3*16);
+ const uint8x16_t K4 = vld1q_u8(skey + 4*16);
+ const uint8x16_t K5 = vld1q_u8(skey + 5*16);
+ const uint8x16_t K6 = vld1q_u8(skey + 6*16);
+ const uint8x16_t K7 = vld1q_u8(skey + 7*16);
+ const uint8x16_t K8 = vld1q_u8(skey + 8*16);
+ const uint8x16_t K9 = vld1q_u8(skey + 9*16);
+ const uint8x16_t K10 = vld1q_u8(skey + 10*16);
+ const uint8x16_t K11 = vld1q_u8(skey + 11*16);
+ const uint8x16_t K12 = vld1q_u8(skey + 12*16);
+
+ while(blocks >= 4)
+ {
+ uint8x16_t B0 = vld1q_u8(in);
+ uint8x16_t B1 = vld1q_u8(in+16);
+ uint8x16_t B2 = vld1q_u8(in+32);
+ uint8x16_t B3 = vld1q_u8(in+48);
+
+ AES_DEC_4_ROUNDS(K0);
+ AES_DEC_4_ROUNDS(K1);
+ AES_DEC_4_ROUNDS(K2);
+ AES_DEC_4_ROUNDS(K3);
+ AES_DEC_4_ROUNDS(K4);
+ AES_DEC_4_ROUNDS(K5);
+ AES_DEC_4_ROUNDS(K6);
+ AES_DEC_4_ROUNDS(K7);
+ AES_DEC_4_ROUNDS(K8);
+ AES_DEC_4_ROUNDS(K9);
+ AES_DEC_4_ROUNDS(K10);
+ AES_DEC_4_LAST_ROUNDS(K11, K12);
+
+ vst1q_u8(out, B0);
+ vst1q_u8(out+16, B1);
+ vst1q_u8(out+32, B2);
+ vst1q_u8(out+48, B3);
+
+ in += 16*4;
+ out += 16*4;
+ blocks -= 4;
+ }
+
+ for(size_t i = 0; i != blocks; ++i)
+ {
+ uint8x16_t B = vld1q_u8(in+16*i);
+ B = vaesimcq_u8(vaesdq_u8(B, K0));
+ B = vaesimcq_u8(vaesdq_u8(B, K1));
+ B = vaesimcq_u8(vaesdq_u8(B, K2));
+ B = vaesimcq_u8(vaesdq_u8(B, K3));
+ B = vaesimcq_u8(vaesdq_u8(B, K4));
+ B = vaesimcq_u8(vaesdq_u8(B, K5));
+ B = vaesimcq_u8(vaesdq_u8(B, K6));
+ B = vaesimcq_u8(vaesdq_u8(B, K7));
+ B = vaesimcq_u8(vaesdq_u8(B, K8));
+ B = vaesimcq_u8(vaesdq_u8(B, K9));
+ B = vaesimcq_u8(vaesdq_u8(B, K10));
+ B = veorq_u8(vaesdq_u8(B, K11), K12);
+ vst1q_u8(out+16*i, B);
+ }
+ }
+
+/*
+* AES-256 Encryption
+*/
+BOTAN_FUNC_ISA("+crypto")
+void AES_256::hw_aes_encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+ {
+ const uint8_t *skey = reinterpret_cast<const uint8_t*>(m_EK.data());
+
+ const uint8x16_t K0 = vld1q_u8(skey + 0*16);
+ const uint8x16_t K1 = vld1q_u8(skey + 1*16);
+ const uint8x16_t K2 = vld1q_u8(skey + 2*16);
+ const uint8x16_t K3 = vld1q_u8(skey + 3*16);
+ const uint8x16_t K4 = vld1q_u8(skey + 4*16);
+ const uint8x16_t K5 = vld1q_u8(skey + 5*16);
+ const uint8x16_t K6 = vld1q_u8(skey + 6*16);
+ const uint8x16_t K7 = vld1q_u8(skey + 7*16);
+ const uint8x16_t K8 = vld1q_u8(skey + 8*16);
+ const uint8x16_t K9 = vld1q_u8(skey + 9*16);
+ const uint8x16_t K10 = vld1q_u8(skey + 10*16);
+ const uint8x16_t K11 = vld1q_u8(skey + 11*16);
+ const uint8x16_t K12 = vld1q_u8(skey + 12*16);
+ const uint8x16_t K13 = vld1q_u8(skey + 13*16);
+ const uint8x16_t K14 = vld1q_u8(skey + 14*16);
+
+ while(blocks >= 4)
+ {
+ uint8x16_t B0 = vld1q_u8(in);
+ uint8x16_t B1 = vld1q_u8(in+16);
+ uint8x16_t B2 = vld1q_u8(in+32);
+ uint8x16_t B3 = vld1q_u8(in+48);
+
+ AES_ENC_4_ROUNDS(K0);
+ AES_ENC_4_ROUNDS(K1);
+ AES_ENC_4_ROUNDS(K2);
+ AES_ENC_4_ROUNDS(K3);
+ AES_ENC_4_ROUNDS(K4);
+ AES_ENC_4_ROUNDS(K5);
+ AES_ENC_4_ROUNDS(K6);
+ AES_ENC_4_ROUNDS(K7);
+ AES_ENC_4_ROUNDS(K8);
+ AES_ENC_4_ROUNDS(K9);
+ AES_ENC_4_ROUNDS(K10);
+ AES_ENC_4_ROUNDS(K11);
+ AES_ENC_4_ROUNDS(K12);
+ AES_ENC_4_LAST_ROUNDS(K13, K14);
+
+ vst1q_u8(out, B0);
+ vst1q_u8(out+16, B1);
+ vst1q_u8(out+32, B2);
+ vst1q_u8(out+48, B3);
+
+ in += 16*4;
+ out += 16*4;
+ blocks -= 4;
+ }
+
+ for(size_t i = 0; i != blocks; ++i)
+ {
+ uint8x16_t B = vld1q_u8(in+16*i);
+ B = vaesmcq_u8(vaeseq_u8(B, K0));
+ B = vaesmcq_u8(vaeseq_u8(B, K1));
+ B = vaesmcq_u8(vaeseq_u8(B, K2));
+ B = vaesmcq_u8(vaeseq_u8(B, K3));
+ B = vaesmcq_u8(vaeseq_u8(B, K4));
+ B = vaesmcq_u8(vaeseq_u8(B, K5));
+ B = vaesmcq_u8(vaeseq_u8(B, K6));
+ B = vaesmcq_u8(vaeseq_u8(B, K7));
+ B = vaesmcq_u8(vaeseq_u8(B, K8));
+ B = vaesmcq_u8(vaeseq_u8(B, K9));
+ B = vaesmcq_u8(vaeseq_u8(B, K10));
+ B = vaesmcq_u8(vaeseq_u8(B, K11));
+ B = vaesmcq_u8(vaeseq_u8(B, K12));
+ B = veorq_u8(vaeseq_u8(B, K13), K14);
+ vst1q_u8(out+16*i, B);
+ }
+ }
+
+/*
+* AES-256 Decryption
+*/
+BOTAN_FUNC_ISA("+crypto")
+void AES_256::hw_aes_decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+ {
+ const uint8_t *skey = reinterpret_cast<const uint8_t*>(m_DK.data());
+
+ const uint8x16_t K0 = vld1q_u8(skey + 0*16);
+ const uint8x16_t K1 = vld1q_u8(skey + 1*16);
+ const uint8x16_t K2 = vld1q_u8(skey + 2*16);
+ const uint8x16_t K3 = vld1q_u8(skey + 3*16);
+ const uint8x16_t K4 = vld1q_u8(skey + 4*16);
+ const uint8x16_t K5 = vld1q_u8(skey + 5*16);
+ const uint8x16_t K6 = vld1q_u8(skey + 6*16);
+ const uint8x16_t K7 = vld1q_u8(skey + 7*16);
+ const uint8x16_t K8 = vld1q_u8(skey + 8*16);
+ const uint8x16_t K9 = vld1q_u8(skey + 9*16);
+ const uint8x16_t K10 = vld1q_u8(skey + 10*16);
+ const uint8x16_t K11 = vld1q_u8(skey + 11*16);
+ const uint8x16_t K12 = vld1q_u8(skey + 12*16);
+ const uint8x16_t K13 = vld1q_u8(skey + 13*16);
+ const uint8x16_t K14 = vld1q_u8(skey + 14*16);
+
+ while(blocks >= 4)
+ {
+ uint8x16_t B0 = vld1q_u8(in);
+ uint8x16_t B1 = vld1q_u8(in+16);
+ uint8x16_t B2 = vld1q_u8(in+32);
+ uint8x16_t B3 = vld1q_u8(in+48);
+
+ AES_DEC_4_ROUNDS(K0);
+ AES_DEC_4_ROUNDS(K1);
+ AES_DEC_4_ROUNDS(K2);
+ AES_DEC_4_ROUNDS(K3);
+ AES_DEC_4_ROUNDS(K4);
+ AES_DEC_4_ROUNDS(K5);
+ AES_DEC_4_ROUNDS(K6);
+ AES_DEC_4_ROUNDS(K7);
+ AES_DEC_4_ROUNDS(K8);
+ AES_DEC_4_ROUNDS(K9);
+ AES_DEC_4_ROUNDS(K10);
+ AES_DEC_4_ROUNDS(K11);
+ AES_DEC_4_ROUNDS(K12);
+ AES_DEC_4_LAST_ROUNDS(K13, K14);
+
+ vst1q_u8(out, B0);
+ vst1q_u8(out+16, B1);
+ vst1q_u8(out+32, B2);
+ vst1q_u8(out+48, B3);
+
+ in += 16*4;
+ out += 16*4;
+ blocks -= 4;
+ }
+
+ for(size_t i = 0; i != blocks; ++i)
+ {
+ uint8x16_t B = vld1q_u8(in+16*i);
+ B = vaesimcq_u8(vaesdq_u8(B, K0));
+ B = vaesimcq_u8(vaesdq_u8(B, K1));
+ B = vaesimcq_u8(vaesdq_u8(B, K2));
+ B = vaesimcq_u8(vaesdq_u8(B, K3));
+ B = vaesimcq_u8(vaesdq_u8(B, K4));
+ B = vaesimcq_u8(vaesdq_u8(B, K5));
+ B = vaesimcq_u8(vaesdq_u8(B, K6));
+ B = vaesimcq_u8(vaesdq_u8(B, K7));
+ B = vaesimcq_u8(vaesdq_u8(B, K8));
+ B = vaesimcq_u8(vaesdq_u8(B, K9));
+ B = vaesimcq_u8(vaesdq_u8(B, K10));
+ B = vaesimcq_u8(vaesdq_u8(B, K11));
+ B = vaesimcq_u8(vaesdq_u8(B, K12));
+ B = veorq_u8(vaesdq_u8(B, K13), K14);
+ vst1q_u8(out+16*i, B);
+ }
+ }
+
+#undef AES_ENC_4_ROUNDS
+#undef AES_ENC_4_LAST_ROUNDS
+#undef AES_DEC_4_ROUNDS
+#undef AES_DEC_4_LAST_ROUNDS
+
+}
diff --git a/comm/third_party/botan/src/lib/block/aes/aes_armv8/info.txt b/comm/third_party/botan/src/lib/block/aes/aes_armv8/info.txt
new file mode 100644
index 0000000000..1864f215b4
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/aes/aes_armv8/info.txt
@@ -0,0 +1,12 @@
+<defines>
+AES_ARMV8 -> 20170903
+</defines>
+
+<isa>
+armv8crypto
+</isa>
+
+<cc>
+gcc:5
+clang:3.8
+</cc>
diff --git a/comm/third_party/botan/src/lib/block/aes/aes_ni/aes_ni.cpp b/comm/third_party/botan/src/lib/block/aes/aes_ni/aes_ni.cpp
new file mode 100644
index 0000000000..76c695f32c
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/aes/aes_ni/aes_ni.cpp
@@ -0,0 +1,780 @@
+/*
+* AES using AES-NI instructions
+* (C) 2009,2012 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/aes.h>
+#include <botan/loadstor.h>
+#include <wmmintrin.h>
+
+namespace Botan {
+
+namespace {
+
+BOTAN_FUNC_ISA("ssse3")
+__m128i aes_128_key_expansion(__m128i key, __m128i key_with_rcon)
+ {
+ 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));
+ key = _mm_xor_si128(key, _mm_slli_si128(key, 4));
+ key = _mm_xor_si128(key, _mm_slli_si128(key, 4));
+ return _mm_xor_si128(key, key_with_rcon);
+ }
+
+BOTAN_FUNC_ISA("ssse3")
+void aes_192_key_expansion(__m128i* K1, __m128i* K2, __m128i key2_with_rcon,
+ uint32_t out[], bool last)
+ {
+ __m128i key1 = *K1;
+ __m128i key2 = *K2;
+
+ key2_with_rcon = _mm_shuffle_epi32(key2_with_rcon, _MM_SHUFFLE(1,1,1,1));
+ key1 = _mm_xor_si128(key1, _mm_slli_si128(key1, 4));
+ key1 = _mm_xor_si128(key1, _mm_slli_si128(key1, 4));
+ key1 = _mm_xor_si128(key1, _mm_slli_si128(key1, 4));
+ key1 = _mm_xor_si128(key1, key2_with_rcon);
+
+ *K1 = key1;
+ _mm_storeu_si128(reinterpret_cast<__m128i*>(out), key1);
+
+ if(last)
+ return;
+
+ key2 = _mm_xor_si128(key2, _mm_slli_si128(key2, 4));
+ key2 = _mm_xor_si128(key2, _mm_shuffle_epi32(key1, _MM_SHUFFLE(3,3,3,3)));
+
+ *K2 = key2;
+ out[4] = _mm_cvtsi128_si32(key2);
+ out[5] = _mm_cvtsi128_si32(_mm_srli_si128(key2, 4));
+ }
+
+/*
+* 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)
+ {
+ __m128i key_with_rcon = _mm_aeskeygenassist_si128(key2, 0x00);
+ key_with_rcon = _mm_shuffle_epi32(key_with_rcon, _MM_SHUFFLE(2,2,2,2));
+
+ key = _mm_xor_si128(key, _mm_slli_si128(key, 4));
+ key = _mm_xor_si128(key, _mm_slli_si128(key, 4));
+ key = _mm_xor_si128(key, _mm_slli_si128(key, 4));
+ return _mm_xor_si128(key, key_with_rcon);
+ }
+
+}
+
+#define AES_ENC_4_ROUNDS(K) \
+ do \
+ { \
+ B0 = _mm_aesenc_si128(B0, K); \
+ B1 = _mm_aesenc_si128(B1, K); \
+ B2 = _mm_aesenc_si128(B2, K); \
+ B3 = _mm_aesenc_si128(B3, K); \
+ } while(0)
+
+#define AES_ENC_4_LAST_ROUNDS(K) \
+ do \
+ { \
+ B0 = _mm_aesenclast_si128(B0, K); \
+ B1 = _mm_aesenclast_si128(B1, K); \
+ B2 = _mm_aesenclast_si128(B2, K); \
+ B3 = _mm_aesenclast_si128(B3, K); \
+ } while(0)
+
+#define AES_DEC_4_ROUNDS(K) \
+ do \
+ { \
+ B0 = _mm_aesdec_si128(B0, K); \
+ B1 = _mm_aesdec_si128(B1, K); \
+ B2 = _mm_aesdec_si128(B2, K); \
+ B3 = _mm_aesdec_si128(B3, K); \
+ } while(0)
+
+#define AES_DEC_4_LAST_ROUNDS(K) \
+ do \
+ { \
+ B0 = _mm_aesdeclast_si128(B0, K); \
+ B1 = _mm_aesdeclast_si128(B1, K); \
+ B2 = _mm_aesdeclast_si128(B2, K); \
+ B3 = _mm_aesdeclast_si128(B3, K); \
+ } while(0)
+
+/*
+* 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
+ {
+ const __m128i* in_mm = reinterpret_cast<const __m128i*>(in);
+ __m128i* out_mm = reinterpret_cast<__m128i*>(out);
+
+ const __m128i* key_mm = reinterpret_cast<const __m128i*>(m_EK.data());
+
+ const __m128i K0 = _mm_loadu_si128(key_mm);
+ const __m128i K1 = _mm_loadu_si128(key_mm + 1);
+ const __m128i K2 = _mm_loadu_si128(key_mm + 2);
+ const __m128i K3 = _mm_loadu_si128(key_mm + 3);
+ const __m128i K4 = _mm_loadu_si128(key_mm + 4);
+ const __m128i K5 = _mm_loadu_si128(key_mm + 5);
+ const __m128i K6 = _mm_loadu_si128(key_mm + 6);
+ const __m128i K7 = _mm_loadu_si128(key_mm + 7);
+ const __m128i K8 = _mm_loadu_si128(key_mm + 8);
+ const __m128i K9 = _mm_loadu_si128(key_mm + 9);
+ const __m128i K10 = _mm_loadu_si128(key_mm + 10);
+
+ while(blocks >= 4)
+ {
+ __m128i B0 = _mm_loadu_si128(in_mm + 0);
+ __m128i B1 = _mm_loadu_si128(in_mm + 1);
+ __m128i B2 = _mm_loadu_si128(in_mm + 2);
+ __m128i B3 = _mm_loadu_si128(in_mm + 3);
+
+ B0 = _mm_xor_si128(B0, K0);
+ B1 = _mm_xor_si128(B1, K0);
+ B2 = _mm_xor_si128(B2, K0);
+ B3 = _mm_xor_si128(B3, K0);
+
+ AES_ENC_4_ROUNDS(K1);
+ AES_ENC_4_ROUNDS(K2);
+ AES_ENC_4_ROUNDS(K3);
+ AES_ENC_4_ROUNDS(K4);
+ AES_ENC_4_ROUNDS(K5);
+ AES_ENC_4_ROUNDS(K6);
+ AES_ENC_4_ROUNDS(K7);
+ AES_ENC_4_ROUNDS(K8);
+ AES_ENC_4_ROUNDS(K9);
+ AES_ENC_4_LAST_ROUNDS(K10);
+
+ _mm_storeu_si128(out_mm + 0, B0);
+ _mm_storeu_si128(out_mm + 1, B1);
+ _mm_storeu_si128(out_mm + 2, B2);
+ _mm_storeu_si128(out_mm + 3, B3);
+
+ blocks -= 4;
+ in_mm += 4;
+ out_mm += 4;
+ }
+
+ for(size_t i = 0; i != blocks; ++i)
+ {
+ __m128i B = _mm_loadu_si128(in_mm + i);
+
+ B = _mm_xor_si128(B, K0);
+
+ B = _mm_aesenc_si128(B, K1);
+ B = _mm_aesenc_si128(B, K2);
+ B = _mm_aesenc_si128(B, K3);
+ B = _mm_aesenc_si128(B, K4);
+ B = _mm_aesenc_si128(B, K5);
+ B = _mm_aesenc_si128(B, K6);
+ B = _mm_aesenc_si128(B, K7);
+ B = _mm_aesenc_si128(B, K8);
+ B = _mm_aesenc_si128(B, K9);
+ B = _mm_aesenclast_si128(B, K10);
+
+ _mm_storeu_si128(out_mm + i, B);
+ }
+ }
+
+/*
+* 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
+ {
+ const __m128i* in_mm = reinterpret_cast<const __m128i*>(in);
+ __m128i* out_mm = reinterpret_cast<__m128i*>(out);
+
+ const __m128i* key_mm = reinterpret_cast<const __m128i*>(m_DK.data());
+
+ const __m128i K0 = _mm_loadu_si128(key_mm);
+ const __m128i K1 = _mm_loadu_si128(key_mm + 1);
+ const __m128i K2 = _mm_loadu_si128(key_mm + 2);
+ const __m128i K3 = _mm_loadu_si128(key_mm + 3);
+ const __m128i K4 = _mm_loadu_si128(key_mm + 4);
+ const __m128i K5 = _mm_loadu_si128(key_mm + 5);
+ const __m128i K6 = _mm_loadu_si128(key_mm + 6);
+ const __m128i K7 = _mm_loadu_si128(key_mm + 7);
+ const __m128i K8 = _mm_loadu_si128(key_mm + 8);
+ const __m128i K9 = _mm_loadu_si128(key_mm + 9);
+ const __m128i K10 = _mm_loadu_si128(key_mm + 10);
+
+ while(blocks >= 4)
+ {
+ __m128i B0 = _mm_loadu_si128(in_mm + 0);
+ __m128i B1 = _mm_loadu_si128(in_mm + 1);
+ __m128i B2 = _mm_loadu_si128(in_mm + 2);
+ __m128i B3 = _mm_loadu_si128(in_mm + 3);
+
+ B0 = _mm_xor_si128(B0, K0);
+ B1 = _mm_xor_si128(B1, K0);
+ B2 = _mm_xor_si128(B2, K0);
+ B3 = _mm_xor_si128(B3, K0);
+
+ AES_DEC_4_ROUNDS(K1);
+ AES_DEC_4_ROUNDS(K2);
+ AES_DEC_4_ROUNDS(K3);
+ AES_DEC_4_ROUNDS(K4);
+ AES_DEC_4_ROUNDS(K5);
+ AES_DEC_4_ROUNDS(K6);
+ AES_DEC_4_ROUNDS(K7);
+ AES_DEC_4_ROUNDS(K8);
+ AES_DEC_4_ROUNDS(K9);
+ AES_DEC_4_LAST_ROUNDS(K10);
+
+ _mm_storeu_si128(out_mm + 0, B0);
+ _mm_storeu_si128(out_mm + 1, B1);
+ _mm_storeu_si128(out_mm + 2, B2);
+ _mm_storeu_si128(out_mm + 3, B3);
+
+ blocks -= 4;
+ in_mm += 4;
+ out_mm += 4;
+ }
+
+ for(size_t i = 0; i != blocks; ++i)
+ {
+ __m128i B = _mm_loadu_si128(in_mm + i);
+
+ B = _mm_xor_si128(B, K0);
+
+ B = _mm_aesdec_si128(B, K1);
+ B = _mm_aesdec_si128(B, K2);
+ B = _mm_aesdec_si128(B, K3);
+ B = _mm_aesdec_si128(B, K4);
+ B = _mm_aesdec_si128(B, K5);
+ B = _mm_aesdec_si128(B, K6);
+ B = _mm_aesdec_si128(B, K7);
+ B = _mm_aesdec_si128(B, K8);
+ B = _mm_aesdec_si128(B, K9);
+ B = _mm_aesdeclast_si128(B, K10);
+
+ _mm_storeu_si128(out_mm + i, B);
+ }
+ }
+
+/*
+* AES-128 Key Schedule
+*/
+BOTAN_FUNC_ISA("ssse3,aes")
+void AES_128::aesni_key_schedule(const uint8_t key[], size_t)
+ {
+ m_EK.resize(44);
+ m_DK.resize(44);
+
+ #define AES_128_key_exp(K, RCON) \
+ aes_128_key_expansion(K, _mm_aeskeygenassist_si128(K, RCON))
+
+ const __m128i K0 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(key));
+ const __m128i K1 = AES_128_key_exp(K0, 0x01);
+ const __m128i K2 = AES_128_key_exp(K1, 0x02);
+ const __m128i K3 = AES_128_key_exp(K2, 0x04);
+ const __m128i K4 = AES_128_key_exp(K3, 0x08);
+ const __m128i K5 = AES_128_key_exp(K4, 0x10);
+ const __m128i K6 = AES_128_key_exp(K5, 0x20);
+ const __m128i K7 = AES_128_key_exp(K6, 0x40);
+ const __m128i K8 = AES_128_key_exp(K7, 0x80);
+ const __m128i K9 = AES_128_key_exp(K8, 0x1B);
+ const __m128i K10 = AES_128_key_exp(K9, 0x36);
+
+ __m128i* EK_mm = reinterpret_cast<__m128i*>(m_EK.data());
+ _mm_storeu_si128(EK_mm , K0);
+ _mm_storeu_si128(EK_mm + 1, K1);
+ _mm_storeu_si128(EK_mm + 2, K2);
+ _mm_storeu_si128(EK_mm + 3, K3);
+ _mm_storeu_si128(EK_mm + 4, K4);
+ _mm_storeu_si128(EK_mm + 5, K5);
+ _mm_storeu_si128(EK_mm + 6, K6);
+ _mm_storeu_si128(EK_mm + 7, K7);
+ _mm_storeu_si128(EK_mm + 8, K8);
+ _mm_storeu_si128(EK_mm + 9, K9);
+ _mm_storeu_si128(EK_mm + 10, K10);
+
+ // Now generate decryption keys
+
+ __m128i* DK_mm = reinterpret_cast<__m128i*>(m_DK.data());
+ _mm_storeu_si128(DK_mm , K10);
+ _mm_storeu_si128(DK_mm + 1, _mm_aesimc_si128(K9));
+ _mm_storeu_si128(DK_mm + 2, _mm_aesimc_si128(K8));
+ _mm_storeu_si128(DK_mm + 3, _mm_aesimc_si128(K7));
+ _mm_storeu_si128(DK_mm + 4, _mm_aesimc_si128(K6));
+ _mm_storeu_si128(DK_mm + 5, _mm_aesimc_si128(K5));
+ _mm_storeu_si128(DK_mm + 6, _mm_aesimc_si128(K4));
+ _mm_storeu_si128(DK_mm + 7, _mm_aesimc_si128(K3));
+ _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);
+ }
+
+/*
+* 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
+ {
+ const __m128i* in_mm = reinterpret_cast<const __m128i*>(in);
+ __m128i* out_mm = reinterpret_cast<__m128i*>(out);
+
+ const __m128i* key_mm = reinterpret_cast<const __m128i*>(m_EK.data());
+
+ const __m128i K0 = _mm_loadu_si128(key_mm);
+ const __m128i K1 = _mm_loadu_si128(key_mm + 1);
+ const __m128i K2 = _mm_loadu_si128(key_mm + 2);
+ const __m128i K3 = _mm_loadu_si128(key_mm + 3);
+ const __m128i K4 = _mm_loadu_si128(key_mm + 4);
+ const __m128i K5 = _mm_loadu_si128(key_mm + 5);
+ const __m128i K6 = _mm_loadu_si128(key_mm + 6);
+ const __m128i K7 = _mm_loadu_si128(key_mm + 7);
+ const __m128i K8 = _mm_loadu_si128(key_mm + 8);
+ const __m128i K9 = _mm_loadu_si128(key_mm + 9);
+ const __m128i K10 = _mm_loadu_si128(key_mm + 10);
+ const __m128i K11 = _mm_loadu_si128(key_mm + 11);
+ const __m128i K12 = _mm_loadu_si128(key_mm + 12);
+
+ while(blocks >= 4)
+ {
+ __m128i B0 = _mm_loadu_si128(in_mm + 0);
+ __m128i B1 = _mm_loadu_si128(in_mm + 1);
+ __m128i B2 = _mm_loadu_si128(in_mm + 2);
+ __m128i B3 = _mm_loadu_si128(in_mm + 3);
+
+ B0 = _mm_xor_si128(B0, K0);
+ B1 = _mm_xor_si128(B1, K0);
+ B2 = _mm_xor_si128(B2, K0);
+ B3 = _mm_xor_si128(B3, K0);
+
+ AES_ENC_4_ROUNDS(K1);
+ AES_ENC_4_ROUNDS(K2);
+ AES_ENC_4_ROUNDS(K3);
+ AES_ENC_4_ROUNDS(K4);
+ AES_ENC_4_ROUNDS(K5);
+ AES_ENC_4_ROUNDS(K6);
+ AES_ENC_4_ROUNDS(K7);
+ AES_ENC_4_ROUNDS(K8);
+ AES_ENC_4_ROUNDS(K9);
+ AES_ENC_4_ROUNDS(K10);
+ AES_ENC_4_ROUNDS(K11);
+ AES_ENC_4_LAST_ROUNDS(K12);
+
+ _mm_storeu_si128(out_mm + 0, B0);
+ _mm_storeu_si128(out_mm + 1, B1);
+ _mm_storeu_si128(out_mm + 2, B2);
+ _mm_storeu_si128(out_mm + 3, B3);
+
+ blocks -= 4;
+ in_mm += 4;
+ out_mm += 4;
+ }
+
+ for(size_t i = 0; i != blocks; ++i)
+ {
+ __m128i B = _mm_loadu_si128(in_mm + i);
+
+ B = _mm_xor_si128(B, K0);
+
+ B = _mm_aesenc_si128(B, K1);
+ B = _mm_aesenc_si128(B, K2);
+ B = _mm_aesenc_si128(B, K3);
+ B = _mm_aesenc_si128(B, K4);
+ B = _mm_aesenc_si128(B, K5);
+ B = _mm_aesenc_si128(B, K6);
+ B = _mm_aesenc_si128(B, K7);
+ B = _mm_aesenc_si128(B, K8);
+ B = _mm_aesenc_si128(B, K9);
+ B = _mm_aesenc_si128(B, K10);
+ B = _mm_aesenc_si128(B, K11);
+ B = _mm_aesenclast_si128(B, K12);
+
+ _mm_storeu_si128(out_mm + i, B);
+ }
+ }
+
+/*
+* 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
+ {
+ const __m128i* in_mm = reinterpret_cast<const __m128i*>(in);
+ __m128i* out_mm = reinterpret_cast<__m128i*>(out);
+
+ const __m128i* key_mm = reinterpret_cast<const __m128i*>(m_DK.data());
+
+ const __m128i K0 = _mm_loadu_si128(key_mm);
+ const __m128i K1 = _mm_loadu_si128(key_mm + 1);
+ const __m128i K2 = _mm_loadu_si128(key_mm + 2);
+ const __m128i K3 = _mm_loadu_si128(key_mm + 3);
+ const __m128i K4 = _mm_loadu_si128(key_mm + 4);
+ const __m128i K5 = _mm_loadu_si128(key_mm + 5);
+ const __m128i K6 = _mm_loadu_si128(key_mm + 6);
+ const __m128i K7 = _mm_loadu_si128(key_mm + 7);
+ const __m128i K8 = _mm_loadu_si128(key_mm + 8);
+ const __m128i K9 = _mm_loadu_si128(key_mm + 9);
+ const __m128i K10 = _mm_loadu_si128(key_mm + 10);
+ const __m128i K11 = _mm_loadu_si128(key_mm + 11);
+ const __m128i K12 = _mm_loadu_si128(key_mm + 12);
+
+ while(blocks >= 4)
+ {
+ __m128i B0 = _mm_loadu_si128(in_mm + 0);
+ __m128i B1 = _mm_loadu_si128(in_mm + 1);
+ __m128i B2 = _mm_loadu_si128(in_mm + 2);
+ __m128i B3 = _mm_loadu_si128(in_mm + 3);
+
+ B0 = _mm_xor_si128(B0, K0);
+ B1 = _mm_xor_si128(B1, K0);
+ B2 = _mm_xor_si128(B2, K0);
+ B3 = _mm_xor_si128(B3, K0);
+
+ AES_DEC_4_ROUNDS(K1);
+ AES_DEC_4_ROUNDS(K2);
+ AES_DEC_4_ROUNDS(K3);
+ AES_DEC_4_ROUNDS(K4);
+ AES_DEC_4_ROUNDS(K5);
+ AES_DEC_4_ROUNDS(K6);
+ AES_DEC_4_ROUNDS(K7);
+ AES_DEC_4_ROUNDS(K8);
+ AES_DEC_4_ROUNDS(K9);
+ AES_DEC_4_ROUNDS(K10);
+ AES_DEC_4_ROUNDS(K11);
+ AES_DEC_4_LAST_ROUNDS(K12);
+
+ _mm_storeu_si128(out_mm + 0, B0);
+ _mm_storeu_si128(out_mm + 1, B1);
+ _mm_storeu_si128(out_mm + 2, B2);
+ _mm_storeu_si128(out_mm + 3, B3);
+
+ blocks -= 4;
+ in_mm += 4;
+ out_mm += 4;
+ }
+
+ for(size_t i = 0; i != blocks; ++i)
+ {
+ __m128i B = _mm_loadu_si128(in_mm + i);
+
+ B = _mm_xor_si128(B, K0);
+
+ B = _mm_aesdec_si128(B, K1);
+ B = _mm_aesdec_si128(B, K2);
+ B = _mm_aesdec_si128(B, K3);
+ B = _mm_aesdec_si128(B, K4);
+ B = _mm_aesdec_si128(B, K5);
+ B = _mm_aesdec_si128(B, K6);
+ B = _mm_aesdec_si128(B, K7);
+ B = _mm_aesdec_si128(B, K8);
+ B = _mm_aesdec_si128(B, K9);
+ B = _mm_aesdec_si128(B, K10);
+ B = _mm_aesdec_si128(B, K11);
+ B = _mm_aesdeclast_si128(B, K12);
+
+ _mm_storeu_si128(out_mm + i, B);
+ }
+ }
+
+/*
+* AES-192 Key Schedule
+*/
+BOTAN_FUNC_ISA("ssse3,aes")
+void AES_192::aesni_key_schedule(const uint8_t key[], size_t)
+ {
+ m_EK.resize(52);
+ m_DK.resize(52);
+
+ __m128i K0 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(key));
+ __m128i K1 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(key + 8));
+ K1 = _mm_srli_si128(K1, 8);
+
+ load_le(m_EK.data(), key, 6);
+
+ #define AES_192_key_exp(RCON, EK_OFF) \
+ aes_192_key_expansion(&K0, &K1, \
+ _mm_aeskeygenassist_si128(K1, RCON), \
+ &m_EK[EK_OFF], EK_OFF == 48)
+
+ AES_192_key_exp(0x01, 6);
+ AES_192_key_exp(0x02, 12);
+ AES_192_key_exp(0x04, 18);
+ AES_192_key_exp(0x08, 24);
+ AES_192_key_exp(0x10, 30);
+ AES_192_key_exp(0x20, 36);
+ AES_192_key_exp(0x40, 42);
+ AES_192_key_exp(0x80, 48);
+
+ #undef AES_192_key_exp
+
+ // Now generate decryption keys
+ const __m128i* EK_mm = reinterpret_cast<const __m128i*>(m_EK.data());
+
+ __m128i* DK_mm = reinterpret_cast<__m128i*>(m_DK.data());
+ _mm_storeu_si128(DK_mm , _mm_loadu_si128(EK_mm + 12));
+ _mm_storeu_si128(DK_mm + 1, _mm_aesimc_si128(_mm_loadu_si128(EK_mm + 11)));
+ _mm_storeu_si128(DK_mm + 2, _mm_aesimc_si128(_mm_loadu_si128(EK_mm + 10)));
+ _mm_storeu_si128(DK_mm + 3, _mm_aesimc_si128(_mm_loadu_si128(EK_mm + 9)));
+ _mm_storeu_si128(DK_mm + 4, _mm_aesimc_si128(_mm_loadu_si128(EK_mm + 8)));
+ _mm_storeu_si128(DK_mm + 5, _mm_aesimc_si128(_mm_loadu_si128(EK_mm + 7)));
+ _mm_storeu_si128(DK_mm + 6, _mm_aesimc_si128(_mm_loadu_si128(EK_mm + 6)));
+ _mm_storeu_si128(DK_mm + 7, _mm_aesimc_si128(_mm_loadu_si128(EK_mm + 5)));
+ _mm_storeu_si128(DK_mm + 8, _mm_aesimc_si128(_mm_loadu_si128(EK_mm + 4)));
+ _mm_storeu_si128(DK_mm + 9, _mm_aesimc_si128(_mm_loadu_si128(EK_mm + 3)));
+ _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));
+ }
+
+/*
+* 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
+ {
+ const __m128i* in_mm = reinterpret_cast<const __m128i*>(in);
+ __m128i* out_mm = reinterpret_cast<__m128i*>(out);
+
+ const __m128i* key_mm = reinterpret_cast<const __m128i*>(m_EK.data());
+
+ const __m128i K0 = _mm_loadu_si128(key_mm);
+ const __m128i K1 = _mm_loadu_si128(key_mm + 1);
+ const __m128i K2 = _mm_loadu_si128(key_mm + 2);
+ const __m128i K3 = _mm_loadu_si128(key_mm + 3);
+ const __m128i K4 = _mm_loadu_si128(key_mm + 4);
+ const __m128i K5 = _mm_loadu_si128(key_mm + 5);
+ const __m128i K6 = _mm_loadu_si128(key_mm + 6);
+ const __m128i K7 = _mm_loadu_si128(key_mm + 7);
+ const __m128i K8 = _mm_loadu_si128(key_mm + 8);
+ const __m128i K9 = _mm_loadu_si128(key_mm + 9);
+ const __m128i K10 = _mm_loadu_si128(key_mm + 10);
+ const __m128i K11 = _mm_loadu_si128(key_mm + 11);
+ const __m128i K12 = _mm_loadu_si128(key_mm + 12);
+ const __m128i K13 = _mm_loadu_si128(key_mm + 13);
+ const __m128i K14 = _mm_loadu_si128(key_mm + 14);
+
+ while(blocks >= 4)
+ {
+ __m128i B0 = _mm_loadu_si128(in_mm + 0);
+ __m128i B1 = _mm_loadu_si128(in_mm + 1);
+ __m128i B2 = _mm_loadu_si128(in_mm + 2);
+ __m128i B3 = _mm_loadu_si128(in_mm + 3);
+
+ B0 = _mm_xor_si128(B0, K0);
+ B1 = _mm_xor_si128(B1, K0);
+ B2 = _mm_xor_si128(B2, K0);
+ B3 = _mm_xor_si128(B3, K0);
+
+ AES_ENC_4_ROUNDS(K1);
+ AES_ENC_4_ROUNDS(K2);
+ AES_ENC_4_ROUNDS(K3);
+ AES_ENC_4_ROUNDS(K4);
+ AES_ENC_4_ROUNDS(K5);
+ AES_ENC_4_ROUNDS(K6);
+ AES_ENC_4_ROUNDS(K7);
+ AES_ENC_4_ROUNDS(K8);
+ AES_ENC_4_ROUNDS(K9);
+ AES_ENC_4_ROUNDS(K10);
+ AES_ENC_4_ROUNDS(K11);
+ AES_ENC_4_ROUNDS(K12);
+ AES_ENC_4_ROUNDS(K13);
+ AES_ENC_4_LAST_ROUNDS(K14);
+
+ _mm_storeu_si128(out_mm + 0, B0);
+ _mm_storeu_si128(out_mm + 1, B1);
+ _mm_storeu_si128(out_mm + 2, B2);
+ _mm_storeu_si128(out_mm + 3, B3);
+
+ blocks -= 4;
+ in_mm += 4;
+ out_mm += 4;
+ }
+
+ for(size_t i = 0; i != blocks; ++i)
+ {
+ __m128i B = _mm_loadu_si128(in_mm + i);
+
+ B = _mm_xor_si128(B, K0);
+
+ B = _mm_aesenc_si128(B, K1);
+ B = _mm_aesenc_si128(B, K2);
+ B = _mm_aesenc_si128(B, K3);
+ B = _mm_aesenc_si128(B, K4);
+ B = _mm_aesenc_si128(B, K5);
+ B = _mm_aesenc_si128(B, K6);
+ B = _mm_aesenc_si128(B, K7);
+ B = _mm_aesenc_si128(B, K8);
+ B = _mm_aesenc_si128(B, K9);
+ B = _mm_aesenc_si128(B, K10);
+ B = _mm_aesenc_si128(B, K11);
+ B = _mm_aesenc_si128(B, K12);
+ B = _mm_aesenc_si128(B, K13);
+ B = _mm_aesenclast_si128(B, K14);
+
+ _mm_storeu_si128(out_mm + i, B);
+ }
+ }
+
+/*
+* 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
+ {
+ const __m128i* in_mm = reinterpret_cast<const __m128i*>(in);
+ __m128i* out_mm = reinterpret_cast<__m128i*>(out);
+
+ const __m128i* key_mm = reinterpret_cast<const __m128i*>(m_DK.data());
+
+ const __m128i K0 = _mm_loadu_si128(key_mm);
+ const __m128i K1 = _mm_loadu_si128(key_mm + 1);
+ const __m128i K2 = _mm_loadu_si128(key_mm + 2);
+ const __m128i K3 = _mm_loadu_si128(key_mm + 3);
+ const __m128i K4 = _mm_loadu_si128(key_mm + 4);
+ const __m128i K5 = _mm_loadu_si128(key_mm + 5);
+ const __m128i K6 = _mm_loadu_si128(key_mm + 6);
+ const __m128i K7 = _mm_loadu_si128(key_mm + 7);
+ const __m128i K8 = _mm_loadu_si128(key_mm + 8);
+ const __m128i K9 = _mm_loadu_si128(key_mm + 9);
+ const __m128i K10 = _mm_loadu_si128(key_mm + 10);
+ const __m128i K11 = _mm_loadu_si128(key_mm + 11);
+ const __m128i K12 = _mm_loadu_si128(key_mm + 12);
+ const __m128i K13 = _mm_loadu_si128(key_mm + 13);
+ const __m128i K14 = _mm_loadu_si128(key_mm + 14);
+
+ while(blocks >= 4)
+ {
+ __m128i B0 = _mm_loadu_si128(in_mm + 0);
+ __m128i B1 = _mm_loadu_si128(in_mm + 1);
+ __m128i B2 = _mm_loadu_si128(in_mm + 2);
+ __m128i B3 = _mm_loadu_si128(in_mm + 3);
+
+ B0 = _mm_xor_si128(B0, K0);
+ B1 = _mm_xor_si128(B1, K0);
+ B2 = _mm_xor_si128(B2, K0);
+ B3 = _mm_xor_si128(B3, K0);
+
+ AES_DEC_4_ROUNDS(K1);
+ AES_DEC_4_ROUNDS(K2);
+ AES_DEC_4_ROUNDS(K3);
+ AES_DEC_4_ROUNDS(K4);
+ AES_DEC_4_ROUNDS(K5);
+ AES_DEC_4_ROUNDS(K6);
+ AES_DEC_4_ROUNDS(K7);
+ AES_DEC_4_ROUNDS(K8);
+ AES_DEC_4_ROUNDS(K9);
+ AES_DEC_4_ROUNDS(K10);
+ AES_DEC_4_ROUNDS(K11);
+ AES_DEC_4_ROUNDS(K12);
+ AES_DEC_4_ROUNDS(K13);
+ AES_DEC_4_LAST_ROUNDS(K14);
+
+ _mm_storeu_si128(out_mm + 0, B0);
+ _mm_storeu_si128(out_mm + 1, B1);
+ _mm_storeu_si128(out_mm + 2, B2);
+ _mm_storeu_si128(out_mm + 3, B3);
+
+ blocks -= 4;
+ in_mm += 4;
+ out_mm += 4;
+ }
+
+ for(size_t i = 0; i != blocks; ++i)
+ {
+ __m128i B = _mm_loadu_si128(in_mm + i);
+
+ B = _mm_xor_si128(B, K0);
+
+ B = _mm_aesdec_si128(B, K1);
+ B = _mm_aesdec_si128(B, K2);
+ B = _mm_aesdec_si128(B, K3);
+ B = _mm_aesdec_si128(B, K4);
+ B = _mm_aesdec_si128(B, K5);
+ B = _mm_aesdec_si128(B, K6);
+ B = _mm_aesdec_si128(B, K7);
+ B = _mm_aesdec_si128(B, K8);
+ B = _mm_aesdec_si128(B, K9);
+ B = _mm_aesdec_si128(B, K10);
+ B = _mm_aesdec_si128(B, K11);
+ B = _mm_aesdec_si128(B, K12);
+ B = _mm_aesdec_si128(B, K13);
+ B = _mm_aesdeclast_si128(B, K14);
+
+ _mm_storeu_si128(out_mm + i, B);
+ }
+ }
+
+/*
+* AES-256 Key Schedule
+*/
+BOTAN_FUNC_ISA("ssse3,aes")
+void AES_256::aesni_key_schedule(const uint8_t key[], size_t)
+ {
+ m_EK.resize(60);
+ m_DK.resize(60);
+
+ const __m128i K0 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(key));
+ const __m128i K1 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(key + 16));
+
+ const __m128i K2 = aes_128_key_expansion(K0, _mm_aeskeygenassist_si128(K1, 0x01));
+ const __m128i K3 = aes_256_key_expansion(K1, K2);
+
+ const __m128i K4 = aes_128_key_expansion(K2, _mm_aeskeygenassist_si128(K3, 0x02));
+ const __m128i K5 = aes_256_key_expansion(K3, K4);
+
+ const __m128i K6 = aes_128_key_expansion(K4, _mm_aeskeygenassist_si128(K5, 0x04));
+ const __m128i K7 = aes_256_key_expansion(K5, K6);
+
+ const __m128i K8 = aes_128_key_expansion(K6, _mm_aeskeygenassist_si128(K7, 0x08));
+ const __m128i K9 = aes_256_key_expansion(K7, K8);
+
+ const __m128i K10 = aes_128_key_expansion(K8, _mm_aeskeygenassist_si128(K9, 0x10));
+ const __m128i K11 = aes_256_key_expansion(K9, K10);
+
+ const __m128i K12 = aes_128_key_expansion(K10, _mm_aeskeygenassist_si128(K11, 0x20));
+ const __m128i K13 = aes_256_key_expansion(K11, K12);
+
+ const __m128i K14 = aes_128_key_expansion(K12, _mm_aeskeygenassist_si128(K13, 0x40));
+
+ __m128i* EK_mm = reinterpret_cast<__m128i*>(m_EK.data());
+ _mm_storeu_si128(EK_mm , K0);
+ _mm_storeu_si128(EK_mm + 1, K1);
+ _mm_storeu_si128(EK_mm + 2, K2);
+ _mm_storeu_si128(EK_mm + 3, K3);
+ _mm_storeu_si128(EK_mm + 4, K4);
+ _mm_storeu_si128(EK_mm + 5, K5);
+ _mm_storeu_si128(EK_mm + 6, K6);
+ _mm_storeu_si128(EK_mm + 7, K7);
+ _mm_storeu_si128(EK_mm + 8, K8);
+ _mm_storeu_si128(EK_mm + 9, K9);
+ _mm_storeu_si128(EK_mm + 10, K10);
+ _mm_storeu_si128(EK_mm + 11, K11);
+ _mm_storeu_si128(EK_mm + 12, K12);
+ _mm_storeu_si128(EK_mm + 13, K13);
+ _mm_storeu_si128(EK_mm + 14, K14);
+
+ // Now generate decryption keys
+ __m128i* DK_mm = reinterpret_cast<__m128i*>(m_DK.data());
+ _mm_storeu_si128(DK_mm , K14);
+ _mm_storeu_si128(DK_mm + 1, _mm_aesimc_si128(K13));
+ _mm_storeu_si128(DK_mm + 2, _mm_aesimc_si128(K12));
+ _mm_storeu_si128(DK_mm + 3, _mm_aesimc_si128(K11));
+ _mm_storeu_si128(DK_mm + 4, _mm_aesimc_si128(K10));
+ _mm_storeu_si128(DK_mm + 5, _mm_aesimc_si128(K9));
+ _mm_storeu_si128(DK_mm + 6, _mm_aesimc_si128(K8));
+ _mm_storeu_si128(DK_mm + 7, _mm_aesimc_si128(K7));
+ _mm_storeu_si128(DK_mm + 8, _mm_aesimc_si128(K6));
+ _mm_storeu_si128(DK_mm + 9, _mm_aesimc_si128(K5));
+ _mm_storeu_si128(DK_mm + 10, _mm_aesimc_si128(K4));
+ _mm_storeu_si128(DK_mm + 11, _mm_aesimc_si128(K3));
+ _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);
+ }
+
+#undef AES_ENC_4_ROUNDS
+#undef AES_ENC_4_LAST_ROUNDS
+#undef AES_DEC_4_ROUNDS
+#undef AES_DEC_4_LAST_ROUNDS
+
+}
diff --git a/comm/third_party/botan/src/lib/block/aes/aes_ni/info.txt b/comm/third_party/botan/src/lib/block/aes/aes_ni/info.txt
new file mode 100644
index 0000000000..2e9749fb8e
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/aes/aes_ni/info.txt
@@ -0,0 +1,9 @@
+<defines>
+AES_NI -> 20131128
+</defines>
+
+<isa>
+sse2
+ssse3
+aesni
+</isa>
diff --git a/comm/third_party/botan/src/lib/block/aes/aes_power8/aes_power8.cpp b/comm/third_party/botan/src/lib/block/aes/aes_power8/aes_power8.cpp
new file mode 100644
index 0000000000..18bc85933b
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/aes/aes_power8/aes_power8.cpp
@@ -0,0 +1,529 @@
+/*
+* AES using POWER8/POWER9 crypto extensions
+*
+* Contributed by Jeffrey Walton
+*
+* Further changes
+* (C) 2018,2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/aes.h>
+#include <botan/cpuid.h>
+
+#include <altivec.h>
+#undef vector
+#undef bool
+
+namespace Botan {
+
+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())
+ {
+ 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);
+ }
+ else
+ {
+ return src;
+ }
+ }
+
+inline Altivec64x2 load_key(const uint32_t key[])
+ {
+ return (Altivec64x2)reverse_vec((Altivec8x16)vec_vsx_ld(0, key));;
+ }
+
+inline Altivec64x2 load_block(const uint8_t src[])
+ {
+ return (Altivec64x2)reverse_vec(vec_vsx_ld(0, src));
+ }
+
+inline void store_block(Altivec64x2 src, uint8_t dest[])
+ {
+ vec_vsx_st(reverse_vec((Altivec8x16)src), 0, dest);
+ }
+
+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_FUNC_ISA("crypto")
+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]);
+ const Altivec64x2 K3 = load_key(&m_EK[12]);
+ const Altivec64x2 K4 = load_key(&m_EK[16]);
+ const Altivec64x2 K5 = load_key(&m_EK[20]);
+ const Altivec64x2 K6 = load_key(&m_EK[24]);
+ const Altivec64x2 K7 = load_key(&m_EK[28]);
+ const Altivec64x2 K8 = load_key(&m_EK[32]);
+ const Altivec64x2 K9 = load_key(&m_EK[36]);
+ const Altivec64x2 K10 = load_key(&m_EK[40]);
+
+ while(blocks >= 4)
+ {
+ Altivec64x2 B0 = load_block(in);
+ Altivec64x2 B1 = load_block(in+16);
+ 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);
+
+ store_blocks(B0, B1, B2, B3, out);
+
+ out += 4*16;
+ in += 4*16;
+ blocks -= 4;
+ }
+
+ for(size_t i = 0; i != blocks; ++i)
+ {
+ 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);
+
+ store_block(B, out);
+
+ out += 16;
+ in += 16;
+ }
+ }
+
+BOTAN_FUNC_ISA("crypto")
+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]);
+ const Altivec64x2 K3 = load_key(&m_EK[28]);
+ const Altivec64x2 K4 = load_key(&m_EK[24]);
+ const Altivec64x2 K5 = load_key(&m_EK[20]);
+ const Altivec64x2 K6 = load_key(&m_EK[16]);
+ const Altivec64x2 K7 = load_key(&m_EK[12]);
+ const Altivec64x2 K8 = load_key(&m_EK[8]);
+ const Altivec64x2 K9 = load_key(&m_EK[4]);
+ const Altivec64x2 K10 = load_key(&m_EK[0]);
+
+ while(blocks >= 4)
+ {
+ Altivec64x2 B0 = load_block(in);
+ Altivec64x2 B1 = load_block(in+16);
+ 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);
+
+ store_blocks(B0, B1, B2, B3, out);
+
+ out += 4*16;
+ in += 4*16;
+ blocks -= 4;
+ }
+
+ for(size_t i = 0; i != blocks; ++i)
+ {
+ 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);
+
+ store_block(B, out);
+
+ out += 16;
+ in += 16;
+ }
+ }
+
+BOTAN_FUNC_ISA("crypto")
+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]);
+ const Altivec64x2 K3 = load_key(&m_EK[12]);
+ const Altivec64x2 K4 = load_key(&m_EK[16]);
+ const Altivec64x2 K5 = load_key(&m_EK[20]);
+ const Altivec64x2 K6 = load_key(&m_EK[24]);
+ const Altivec64x2 K7 = load_key(&m_EK[28]);
+ const Altivec64x2 K8 = load_key(&m_EK[32]);
+ const Altivec64x2 K9 = load_key(&m_EK[36]);
+ const Altivec64x2 K10 = load_key(&m_EK[40]);
+ const Altivec64x2 K11 = load_key(&m_EK[44]);
+ const Altivec64x2 K12 = load_key(&m_EK[48]);
+
+ while(blocks >= 4)
+ {
+ Altivec64x2 B0 = load_block(in);
+ Altivec64x2 B1 = load_block(in+16);
+ 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);
+
+ store_blocks(B0, B1, B2, B3, out);
+
+ out += 4*16;
+ in += 4*16;
+ blocks -= 4;
+ }
+
+ for(size_t i = 0; i != blocks; ++i)
+ {
+ 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);
+
+ store_block(B, out);
+
+ out += 16;
+ in += 16;
+ }
+ }
+
+BOTAN_FUNC_ISA("crypto")
+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]);
+ const Altivec64x2 K3 = load_key(&m_EK[36]);
+ const Altivec64x2 K4 = load_key(&m_EK[32]);
+ const Altivec64x2 K5 = load_key(&m_EK[28]);
+ const Altivec64x2 K6 = load_key(&m_EK[24]);
+ const Altivec64x2 K7 = load_key(&m_EK[20]);
+ const Altivec64x2 K8 = load_key(&m_EK[16]);
+ const Altivec64x2 K9 = load_key(&m_EK[12]);
+ const Altivec64x2 K10 = load_key(&m_EK[8]);
+ const Altivec64x2 K11 = load_key(&m_EK[4]);
+ const Altivec64x2 K12 = load_key(&m_EK[0]);
+
+ while(blocks >= 4)
+ {
+ Altivec64x2 B0 = load_block(in);
+ Altivec64x2 B1 = load_block(in+16);
+ 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);
+
+ store_blocks(B0, B1, B2, B3, out);
+
+ out += 4*16;
+ in += 4*16;
+ blocks -= 4;
+ }
+
+ for(size_t i = 0; i != blocks; ++i)
+ {
+ 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);
+
+ store_block(B, out);
+
+ out += 16;
+ in += 16;
+ }
+ }
+
+BOTAN_FUNC_ISA("crypto")
+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]);
+ const Altivec64x2 K3 = load_key(&m_EK[12]);
+ const Altivec64x2 K4 = load_key(&m_EK[16]);
+ const Altivec64x2 K5 = load_key(&m_EK[20]);
+ const Altivec64x2 K6 = load_key(&m_EK[24]);
+ const Altivec64x2 K7 = load_key(&m_EK[28]);
+ const Altivec64x2 K8 = load_key(&m_EK[32]);
+ const Altivec64x2 K9 = load_key(&m_EK[36]);
+ const Altivec64x2 K10 = load_key(&m_EK[40]);
+ const Altivec64x2 K11 = load_key(&m_EK[44]);
+ const Altivec64x2 K12 = load_key(&m_EK[48]);
+ const Altivec64x2 K13 = load_key(&m_EK[52]);
+ const Altivec64x2 K14 = load_key(&m_EK[56]);
+
+ while(blocks >= 4)
+ {
+ Altivec64x2 B0 = load_block(in);
+ Altivec64x2 B1 = load_block(in+16);
+ 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);
+
+ store_blocks(B0, B1, B2, B3, out);
+
+ out += 4*16;
+ in += 4*16;
+ blocks -= 4;
+ }
+
+ for(size_t i = 0; i != blocks; ++i)
+ {
+ 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);
+
+ store_block(B, out);
+
+ out += 16;
+ in += 16;
+ }
+ }
+
+BOTAN_FUNC_ISA("crypto")
+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]);
+ const Altivec64x2 K3 = load_key(&m_EK[44]);
+ const Altivec64x2 K4 = load_key(&m_EK[40]);
+ const Altivec64x2 K5 = load_key(&m_EK[36]);
+ const Altivec64x2 K6 = load_key(&m_EK[32]);
+ const Altivec64x2 K7 = load_key(&m_EK[28]);
+ const Altivec64x2 K8 = load_key(&m_EK[24]);
+ const Altivec64x2 K9 = load_key(&m_EK[20]);
+ const Altivec64x2 K10 = load_key(&m_EK[16]);
+ const Altivec64x2 K11 = load_key(&m_EK[12]);
+ const Altivec64x2 K12 = load_key(&m_EK[8]);
+ const Altivec64x2 K13 = load_key(&m_EK[4]);
+ const Altivec64x2 K14 = load_key(&m_EK[0]);
+
+ while(blocks >= 4)
+ {
+ Altivec64x2 B0 = load_block(in);
+ Altivec64x2 B1 = load_block(in+16);
+ 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);
+
+ store_blocks(B0, B1, B2, B3, out);
+
+ out += 4*16;
+ in += 4*16;
+ blocks -= 4;
+ }
+
+ for(size_t i = 0; i != blocks; ++i)
+ {
+ 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);
+
+ store_block(B, out);
+
+ out += 16;
+ in += 16;
+ }
+ }
+
+#undef AES_XOR_4
+#undef AES_ENCRYPT_4
+#undef AES_ENCRYPT_4_LAST
+#undef AES_DECRYPT_4
+#undef AES_DECRYPT_4_LAST
+
+}
diff --git a/comm/third_party/botan/src/lib/block/aes/aes_power8/info.txt b/comm/third_party/botan/src/lib/block/aes/aes_power8/info.txt
new file mode 100644
index 0000000000..df569edd50
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/aes/aes_power8/info.txt
@@ -0,0 +1,11 @@
+<defines>
+AES_POWER8 -> 20180223
+</defines>
+
+<arch>
+ppc64
+</arch>
+
+<isa>
+powercrypto
+</isa>
diff --git a/comm/third_party/botan/src/lib/block/aes/aes_vperm/aes_vperm.cpp b/comm/third_party/botan/src/lib/block/aes/aes_vperm/aes_vperm.cpp
new file mode 100644
index 0000000000..4ae6bb2236
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/aes/aes_vperm/aes_vperm.cpp
@@ -0,0 +1,627 @@
+/*
+* AES using vector permutes (SSSE3, NEON)
+* (C) 2010,2016,2019 Jack Lloyd
+*
+* Based on public domain x86-64 assembly written by Mike Hamburg,
+* described in "Accelerating AES with Vector Permute Instructions"
+* (CHES 2009). His original code is available at
+* https://crypto.stanford.edu/vpaes/
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/aes.h>
+#include <botan/internal/ct_utils.h>
+#include <botan/internal/simd_32.h>
+
+#if defined(BOTAN_SIMD_USE_SSE2)
+ #include <tmmintrin.h>
+#endif
+
+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((__vector signed char)b.raw(), zero);
+ const auto r = vec_perm((__vector signed char)a.raw(), (__vector signed char)a.raw(), (__vector unsigned char)b.raw());
+ return SIMD_4x32((__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),
+};
+
+const SIMD_4x32 lo_nibs_mask = SIMD_4x32::splat_u8(0x0F);
+
+inline SIMD_4x32 low_nibs(SIMD_4x32 x)
+ {
+ return lo_nibs_mask & x;
+ }
+
+inline SIMD_4x32 high_nibs(SIMD_4x32 x)
+ {
+ 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)
+ {
+ 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)
+ {
+ 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 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 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;
+
+ 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));
+
+ return shuffle(shuffle(sbou, t5) ^ shuffle(sbot, t6) ^ K, vperm_sr[r % 4]);
+ }
+
+inline SIMD_4x32 BOTAN_FUNC_ISA(BOTAN_VPERM_ISA) aes_dec_first_round(SIMD_4x32 B, SIMD_4x32 K)
+ {
+ 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);
+
+ B ^= Bh;
+
+ 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 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);
+ }
+
+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);
+
+ B ^= Bh;
+
+ 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 SIMD_4x32 x = shuffle(sboud, t5) ^ shuffle(sbotd, t6) ^ K;
+ return shuffle(x, vperm_sr[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)
+ {
+ CT::poison(in, blocks * 16);
+
+ const size_t blocks2 = blocks - (blocks % 2);
+
+ for(size_t i = 0; i != blocks2; i += 2)
+ {
+ SIMD_4x32 B0 = SIMD_4x32::load_le(in + i*16);
+ SIMD_4x32 B1 = SIMD_4x32::load_le(in + (i+1)*16);
+
+ B0 = aes_enc_first_round(B0, K[0]);
+ B1 = aes_enc_first_round(B1, K[0]);
+
+ for(size_t r = 1; r != rounds; ++r)
+ {
+ B0 = aes_enc_round(B0, K[r], r);
+ B1 = aes_enc_round(B1, K[r], r);
+ }
+
+ B0 = aes_enc_last_round(B0, K[rounds], rounds);
+ B1 = aes_enc_last_round(B1, K[rounds], rounds);
+
+ B0.store_le(out + i*16);
+ B1.store_le(out + (i+1)*16);
+ }
+
+ for(size_t i = blocks2; i < blocks; ++i)
+ {
+ SIMD_4x32 B = SIMD_4x32::load_le(in + i*16); // ???
+
+ B = aes_enc_first_round(B, K[0]);
+
+ for(size_t r = 1; r != rounds; ++r)
+ {
+ B = aes_enc_round(B, K[r], r);
+ }
+
+ B = aes_enc_last_round(B, K[rounds], rounds);
+ B.store_le(out + i*16);
+ }
+
+ CT::unpoison(in, blocks * 16);
+ 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)
+ {
+ CT::poison(in, blocks * 16);
+
+ const size_t blocks2 = blocks - (blocks % 2);
+
+ for(size_t i = 0; i != blocks2; i += 2)
+ {
+ SIMD_4x32 B0 = SIMD_4x32::load_le(in + i*16);
+ SIMD_4x32 B1 = SIMD_4x32::load_le(in + (i+1)*16);
+
+ B0 = aes_dec_first_round(B0, K[0]);
+ B1 = aes_dec_first_round(B1, K[0]);
+
+ for(size_t r = 1; r != rounds; ++r)
+ {
+ B0 = aes_dec_round(B0, K[r], r);
+ B1 = aes_dec_round(B1, K[r], r);
+ }
+
+ B0 = aes_dec_last_round(B0, K[rounds], rounds);
+ B1 = aes_dec_last_round(B1, K[rounds], rounds);
+
+ B0.store_le(out + i*16);
+ B1.store_le(out + (i+1)*16);
+ }
+
+ for(size_t i = blocks2; i < blocks; ++i)
+ {
+ SIMD_4x32 B = SIMD_4x32::load_le(in + i*16); // ???
+
+ B = aes_dec_first_round(B, K[0]);
+
+ for(size_t r = 1; r != rounds; ++r)
+ {
+ B = aes_dec_round(B, K[r], r);
+ }
+
+ B = aes_dec_last_round(B, K[rounds], rounds);
+ B.store_le(out + i*16);
+ }
+
+ CT::unpoison(in, blocks * 16);
+ CT::unpoison(out, blocks * 16);
+ }
+
+}
+
+void 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]),
+ };
+
+ 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
+ {
+ 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]),
+ };
+
+ 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
+ {
+ 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]),
+ };
+
+ 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
+ {
+ 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]),
+ };
+
+ 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
+ {
+ 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]),
+ };
+
+ 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
+ {
+ 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]),
+ };
+
+ return vperm_decrypt_blocks(in, out, blocks, K, 14);
+ }
+
+namespace {
+
+inline SIMD_4x32 BOTAN_FUNC_ISA(BOTAN_VPERM_ISA)
+ 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)
+ {
+ 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]);
+ }
+
+SIMD_4x32 BOTAN_FUNC_ISA(BOTAN_VPERM_ISA) 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] = {
+ SIMD_4x32(0x7ED9A700, 0xB6116FC8, 0x82255BFC, 0x4AED9334),
+ SIMD_4x32(0x27143300, 0x45765162, 0xE9DAFDCE, 0x8BB89FAC),
+ SIMD_4x32(0xCCA86400, 0x27438FEB, 0xADC90561, 0x4622EE8A),
+ SIMD_4x32(0x4F92DD00, 0x815C13CE, 0xBD602FF2, 0x73AEE13C),
+ SIMD_4x32(0x01C6C700, 0x03C4C502, 0xFA3D3CFB, 0xF83F3EF9),
+ SIMD_4x32(0x38CFF700, 0xEE1921D6, 0x7384BC4B, 0xA5526A9D),
+ SIMD_4x32(0x53732000, 0xE3C390B0, 0x10306343, 0xA080D3F3),
+ SIMD_4x32(0x036982E8, 0xA0CA214B, 0x8CE60D67, 0x2F45AEC4),
+ };
+
+ SIMD_4x32 t = aes_schedule_transform(k, dsk[0], dsk[1]);
+ SIMD_4x32 output = shuffle(t, mc_forward0);
+
+ t = aes_schedule_transform(t, dsk[2], dsk[3]);
+ output = shuffle(t ^ output, mc_forward0);
+
+ t = aes_schedule_transform(t, dsk[4], dsk[5]);
+ output = shuffle(t ^ output, mc_forward0);
+
+ t = aes_schedule_transform(t, dsk[6], dsk[7]);
+ output = shuffle(t ^ output, mc_forward0);
+
+ return shuffle(output, vperm_sr[round_no % 4]);
+ }
+
+SIMD_4x32 BOTAN_FUNC_ISA(BOTAN_VPERM_ISA) 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);
+ 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)
+ {
+ const SIMD_4x32 deskew1(0x47A4E300, 0x07E4A340, 0x5DBEF91A, 0x1DFEB95A);
+ const SIMD_4x32 deskew2(0x83EA6900, 0x5F36B5DC, 0xF49D1E77, 0x2841C2AB);
+
+ k ^= SIMD_4x32::splat_u8(0x5B);
+ 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 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 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);
+ }
+
+SIMD_4x32 BOTAN_FUNC_ISA(BOTAN_VPERM_ISA) aes_schedule_round(SIMD_4x32 rc, SIMD_4x32 input1, SIMD_4x32 input2)
+ {
+ // 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);
+ }
+
+SIMD_4x32 BOTAN_FUNC_ISA(BOTAN_VPERM_ISA) 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);
+
+ const SIMD_4x32 zero_top_half(0, 0, 0xFFFFFFFF, 0xFFFFFFFF);
+ y &= zero_top_half;
+ return y ^ shuffle(x, shuffle3332) ^ shuffle(y, shuffle2000);
+ }
+
+}
+
+void AES_128::vperm_key_schedule(const uint8_t keyb[], size_t)
+ {
+ 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]);
+
+ key = aes_schedule_transform(key, k_ipt1, k_ipt2);
+ key.store_le(&m_EK[0]);
+
+ for(size_t i = 1; i != 10; ++i)
+ {
+ 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);
+ 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)
+ {
+ 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]);
+
+ key1 = aes_schedule_transform(key1, k_ipt1, k_ipt2);
+ key2 = aes_schedule_transform(key2, k_ipt1, k_ipt2);
+
+ 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);
+ 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)]);
+
+ t = aes_schedule_192_smear(key2, t);
+
+ 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);
+
+ if(i == 3)
+ {
+ aes_schedule_mangle_last(key2, (i+1)%4).store_le(&m_EK[4*(3*i+3)]);
+ aes_schedule_mangle_last_dec(key2).store_le(&m_DK[4*(9-3*i)]);
+ }
+ else
+ {
+ aes_schedule_mangle(key2, (i+1)%4).store_le(&m_EK[4*(3*i+3)]);
+ aes_schedule_mangle_dec(key2, (i+1)%4).store_le(&m_DK[4*(9-3*i)]);
+ }
+
+ key1 = key2;
+ key2 = aes_schedule_192_smear(key2, t);
+ }
+ }
+
+void AES_256::vperm_key_schedule(const uint8_t keyb[], size_t)
+ {
+ 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]);
+
+ key1 = aes_schedule_transform(key1, k_ipt1, k_ipt2);
+ key2 = aes_schedule_transform(key2, k_ipt1, k_ipt2);
+
+ key1.store_le(&m_EK[0]);
+ aes_schedule_mangle(key2, 3).store_le(&m_EK[4]);
+
+ aes_schedule_mangle_dec(key2, 1).store_le(&m_DK[4*13]);
+
+ const SIMD_4x32 shuffle3333 = SIMD_4x32::splat(0x0F0E0D0C);
+
+ 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);
+
+ 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)]);
+
+ key2 = aes_schedule_round(shuffle(key2, shuffle3333), k_t);
+
+ aes_schedule_mangle(key2, (i-1)%4).store_le(&m_EK[4*(i+1)]);
+ aes_schedule_mangle_dec(key2, (i+1)%4).store_le(&m_DK[4*(13-i)]);
+ }
+
+ 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]);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/block/aes/aes_vperm/info.txt b/comm/third_party/botan/src/lib/block/aes/aes_vperm/info.txt
new file mode 100644
index 0000000000..0b7eabaace
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/aes/aes_vperm/info.txt
@@ -0,0 +1,36 @@
+<defines>
+AES_VPERM -> 20190901
+</defines>
+
+endian little
+
+<isa>
+x86_32:sse2
+x86_64:sse2
+x86_32:ssse3
+x86_64:ssse3
+arm32:neon
+arm64:neon
+ppc32:altivec
+ppc64:altivec
+</isa>
+
+<arch>
+x86_32
+x86_64
+arm32
+arm64
+ppc32
+ppc64
+</arch>
+
+<requires>
+simd
+</requires>
+
+<cc>
+gcc
+clang
+msvc:19.10 # VC 2017
+sunstudio
+</cc>
diff --git a/comm/third_party/botan/src/lib/block/aes/info.txt b/comm/third_party/botan/src/lib/block/aes/info.txt
new file mode 100644
index 0000000000..62455cf2c3
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/aes/info.txt
@@ -0,0 +1,3 @@
+<defines>
+AES -> 20131128
+</defines>
diff --git a/comm/third_party/botan/src/lib/block/aria/aria.cpp b/comm/third_party/botan/src/lib/block/aria/aria.cpp
new file mode 100644
index 0000000000..79105a88c4
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/aria/aria.cpp
@@ -0,0 +1,506 @@
+/*
+* ARIA
+* Adapted for Botan by Jeffrey Walton, public domain
+*
+* Further changes
+* (C) 2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*
+* This ARIA implementation is based on the 32-bit implementation by Aaram Yun from the
+* National Security Research Institute, KOREA. Aaram Yun's implementation is based on
+* the 8-bit implementation by Jin Hong. The source files are available in ARIA.zip from
+* the Korea Internet & Security Agency website.
+* <A HREF="https://tools.ietf.org/html/rfc5794">RFC 5794, A Description of the ARIA Encryption Algorithm</A>,
+* <A HREF="http://seed.kisa.or.kr/iwt/ko/bbs/EgovReferenceList.do?bbsId=BBSMSTR_000000000002">Korea
+* Internet & Security Agency homepage</A>
+*/
+
+#include <botan/aria.h>
+#include <botan/loadstor.h>
+#include <botan/rotate.h>
+#include <botan/cpuid.h>
+
+namespace Botan {
+
+namespace {
+
+namespace ARIA_F {
+
+alignas(64)
+const uint32_t S1[256]={
+ 0x00636363,0x007c7c7c,0x00777777,0x007b7b7b,0x00f2f2f2,0x006b6b6b,0x006f6f6f,0x00c5c5c5,
+ 0x00303030,0x00010101,0x00676767,0x002b2b2b,0x00fefefe,0x00d7d7d7,0x00ababab,0x00767676,
+ 0x00cacaca,0x00828282,0x00c9c9c9,0x007d7d7d,0x00fafafa,0x00595959,0x00474747,0x00f0f0f0,
+ 0x00adadad,0x00d4d4d4,0x00a2a2a2,0x00afafaf,0x009c9c9c,0x00a4a4a4,0x00727272,0x00c0c0c0,
+ 0x00b7b7b7,0x00fdfdfd,0x00939393,0x00262626,0x00363636,0x003f3f3f,0x00f7f7f7,0x00cccccc,
+ 0x00343434,0x00a5a5a5,0x00e5e5e5,0x00f1f1f1,0x00717171,0x00d8d8d8,0x00313131,0x00151515,
+ 0x00040404,0x00c7c7c7,0x00232323,0x00c3c3c3,0x00181818,0x00969696,0x00050505,0x009a9a9a,
+ 0x00070707,0x00121212,0x00808080,0x00e2e2e2,0x00ebebeb,0x00272727,0x00b2b2b2,0x00757575,
+ 0x00090909,0x00838383,0x002c2c2c,0x001a1a1a,0x001b1b1b,0x006e6e6e,0x005a5a5a,0x00a0a0a0,
+ 0x00525252,0x003b3b3b,0x00d6d6d6,0x00b3b3b3,0x00292929,0x00e3e3e3,0x002f2f2f,0x00848484,
+ 0x00535353,0x00d1d1d1,0x00000000,0x00ededed,0x00202020,0x00fcfcfc,0x00b1b1b1,0x005b5b5b,
+ 0x006a6a6a,0x00cbcbcb,0x00bebebe,0x00393939,0x004a4a4a,0x004c4c4c,0x00585858,0x00cfcfcf,
+ 0x00d0d0d0,0x00efefef,0x00aaaaaa,0x00fbfbfb,0x00434343,0x004d4d4d,0x00333333,0x00858585,
+ 0x00454545,0x00f9f9f9,0x00020202,0x007f7f7f,0x00505050,0x003c3c3c,0x009f9f9f,0x00a8a8a8,
+ 0x00515151,0x00a3a3a3,0x00404040,0x008f8f8f,0x00929292,0x009d9d9d,0x00383838,0x00f5f5f5,
+ 0x00bcbcbc,0x00b6b6b6,0x00dadada,0x00212121,0x00101010,0x00ffffff,0x00f3f3f3,0x00d2d2d2,
+ 0x00cdcdcd,0x000c0c0c,0x00131313,0x00ececec,0x005f5f5f,0x00979797,0x00444444,0x00171717,
+ 0x00c4c4c4,0x00a7a7a7,0x007e7e7e,0x003d3d3d,0x00646464,0x005d5d5d,0x00191919,0x00737373,
+ 0x00606060,0x00818181,0x004f4f4f,0x00dcdcdc,0x00222222,0x002a2a2a,0x00909090,0x00888888,
+ 0x00464646,0x00eeeeee,0x00b8b8b8,0x00141414,0x00dedede,0x005e5e5e,0x000b0b0b,0x00dbdbdb,
+ 0x00e0e0e0,0x00323232,0x003a3a3a,0x000a0a0a,0x00494949,0x00060606,0x00242424,0x005c5c5c,
+ 0x00c2c2c2,0x00d3d3d3,0x00acacac,0x00626262,0x00919191,0x00959595,0x00e4e4e4,0x00797979,
+ 0x00e7e7e7,0x00c8c8c8,0x00373737,0x006d6d6d,0x008d8d8d,0x00d5d5d5,0x004e4e4e,0x00a9a9a9,
+ 0x006c6c6c,0x00565656,0x00f4f4f4,0x00eaeaea,0x00656565,0x007a7a7a,0x00aeaeae,0x00080808,
+ 0x00bababa,0x00787878,0x00252525,0x002e2e2e,0x001c1c1c,0x00a6a6a6,0x00b4b4b4,0x00c6c6c6,
+ 0x00e8e8e8,0x00dddddd,0x00747474,0x001f1f1f,0x004b4b4b,0x00bdbdbd,0x008b8b8b,0x008a8a8a,
+ 0x00707070,0x003e3e3e,0x00b5b5b5,0x00666666,0x00484848,0x00030303,0x00f6f6f6,0x000e0e0e,
+ 0x00616161,0x00353535,0x00575757,0x00b9b9b9,0x00868686,0x00c1c1c1,0x001d1d1d,0x009e9e9e,
+ 0x00e1e1e1,0x00f8f8f8,0x00989898,0x00111111,0x00696969,0x00d9d9d9,0x008e8e8e,0x00949494,
+ 0x009b9b9b,0x001e1e1e,0x00878787,0x00e9e9e9,0x00cecece,0x00555555,0x00282828,0x00dfdfdf,
+ 0x008c8c8c,0x00a1a1a1,0x00898989,0x000d0d0d,0x00bfbfbf,0x00e6e6e6,0x00424242,0x00686868,
+ 0x00414141,0x00999999,0x002d2d2d,0x000f0f0f,0x00b0b0b0,0x00545454,0x00bbbbbb,0x00161616
+};
+
+alignas(64)
+const uint32_t S2[256]={
+ 0xe200e2e2,0x4e004e4e,0x54005454,0xfc00fcfc,0x94009494,0xc200c2c2,0x4a004a4a,0xcc00cccc,
+ 0x62006262,0x0d000d0d,0x6a006a6a,0x46004646,0x3c003c3c,0x4d004d4d,0x8b008b8b,0xd100d1d1,
+ 0x5e005e5e,0xfa00fafa,0x64006464,0xcb00cbcb,0xb400b4b4,0x97009797,0xbe00bebe,0x2b002b2b,
+ 0xbc00bcbc,0x77007777,0x2e002e2e,0x03000303,0xd300d3d3,0x19001919,0x59005959,0xc100c1c1,
+ 0x1d001d1d,0x06000606,0x41004141,0x6b006b6b,0x55005555,0xf000f0f0,0x99009999,0x69006969,
+ 0xea00eaea,0x9c009c9c,0x18001818,0xae00aeae,0x63006363,0xdf00dfdf,0xe700e7e7,0xbb00bbbb,
+ 0x00000000,0x73007373,0x66006666,0xfb00fbfb,0x96009696,0x4c004c4c,0x85008585,0xe400e4e4,
+ 0x3a003a3a,0x09000909,0x45004545,0xaa00aaaa,0x0f000f0f,0xee00eeee,0x10001010,0xeb00ebeb,
+ 0x2d002d2d,0x7f007f7f,0xf400f4f4,0x29002929,0xac00acac,0xcf00cfcf,0xad00adad,0x91009191,
+ 0x8d008d8d,0x78007878,0xc800c8c8,0x95009595,0xf900f9f9,0x2f002f2f,0xce00cece,0xcd00cdcd,
+ 0x08000808,0x7a007a7a,0x88008888,0x38003838,0x5c005c5c,0x83008383,0x2a002a2a,0x28002828,
+ 0x47004747,0xdb00dbdb,0xb800b8b8,0xc700c7c7,0x93009393,0xa400a4a4,0x12001212,0x53005353,
+ 0xff00ffff,0x87008787,0x0e000e0e,0x31003131,0x36003636,0x21002121,0x58005858,0x48004848,
+ 0x01000101,0x8e008e8e,0x37003737,0x74007474,0x32003232,0xca00caca,0xe900e9e9,0xb100b1b1,
+ 0xb700b7b7,0xab00abab,0x0c000c0c,0xd700d7d7,0xc400c4c4,0x56005656,0x42004242,0x26002626,
+ 0x07000707,0x98009898,0x60006060,0xd900d9d9,0xb600b6b6,0xb900b9b9,0x11001111,0x40004040,
+ 0xec00ecec,0x20002020,0x8c008c8c,0xbd00bdbd,0xa000a0a0,0xc900c9c9,0x84008484,0x04000404,
+ 0x49004949,0x23002323,0xf100f1f1,0x4f004f4f,0x50005050,0x1f001f1f,0x13001313,0xdc00dcdc,
+ 0xd800d8d8,0xc000c0c0,0x9e009e9e,0x57005757,0xe300e3e3,0xc300c3c3,0x7b007b7b,0x65006565,
+ 0x3b003b3b,0x02000202,0x8f008f8f,0x3e003e3e,0xe800e8e8,0x25002525,0x92009292,0xe500e5e5,
+ 0x15001515,0xdd00dddd,0xfd00fdfd,0x17001717,0xa900a9a9,0xbf00bfbf,0xd400d4d4,0x9a009a9a,
+ 0x7e007e7e,0xc500c5c5,0x39003939,0x67006767,0xfe00fefe,0x76007676,0x9d009d9d,0x43004343,
+ 0xa700a7a7,0xe100e1e1,0xd000d0d0,0xf500f5f5,0x68006868,0xf200f2f2,0x1b001b1b,0x34003434,
+ 0x70007070,0x05000505,0xa300a3a3,0x8a008a8a,0xd500d5d5,0x79007979,0x86008686,0xa800a8a8,
+ 0x30003030,0xc600c6c6,0x51005151,0x4b004b4b,0x1e001e1e,0xa600a6a6,0x27002727,0xf600f6f6,
+ 0x35003535,0xd200d2d2,0x6e006e6e,0x24002424,0x16001616,0x82008282,0x5f005f5f,0xda00dada,
+ 0xe600e6e6,0x75007575,0xa200a2a2,0xef00efef,0x2c002c2c,0xb200b2b2,0x1c001c1c,0x9f009f9f,
+ 0x5d005d5d,0x6f006f6f,0x80008080,0x0a000a0a,0x72007272,0x44004444,0x9b009b9b,0x6c006c6c,
+ 0x90009090,0x0b000b0b,0x5b005b5b,0x33003333,0x7d007d7d,0x5a005a5a,0x52005252,0xf300f3f3,
+ 0x61006161,0xa100a1a1,0xf700f7f7,0xb000b0b0,0xd600d6d6,0x3f003f3f,0x7c007c7c,0x6d006d6d,
+ 0xed00eded,0x14001414,0xe000e0e0,0xa500a5a5,0x3d003d3d,0x22002222,0xb300b3b3,0xf800f8f8,
+ 0x89008989,0xde00dede,0x71007171,0x1a001a1a,0xaf00afaf,0xba00baba,0xb500b5b5,0x81008181
+};
+
+alignas(64)
+const uint32_t X1[256]={
+ 0x52520052,0x09090009,0x6a6a006a,0xd5d500d5,0x30300030,0x36360036,0xa5a500a5,0x38380038,
+ 0xbfbf00bf,0x40400040,0xa3a300a3,0x9e9e009e,0x81810081,0xf3f300f3,0xd7d700d7,0xfbfb00fb,
+ 0x7c7c007c,0xe3e300e3,0x39390039,0x82820082,0x9b9b009b,0x2f2f002f,0xffff00ff,0x87870087,
+ 0x34340034,0x8e8e008e,0x43430043,0x44440044,0xc4c400c4,0xdede00de,0xe9e900e9,0xcbcb00cb,
+ 0x54540054,0x7b7b007b,0x94940094,0x32320032,0xa6a600a6,0xc2c200c2,0x23230023,0x3d3d003d,
+ 0xeeee00ee,0x4c4c004c,0x95950095,0x0b0b000b,0x42420042,0xfafa00fa,0xc3c300c3,0x4e4e004e,
+ 0x08080008,0x2e2e002e,0xa1a100a1,0x66660066,0x28280028,0xd9d900d9,0x24240024,0xb2b200b2,
+ 0x76760076,0x5b5b005b,0xa2a200a2,0x49490049,0x6d6d006d,0x8b8b008b,0xd1d100d1,0x25250025,
+ 0x72720072,0xf8f800f8,0xf6f600f6,0x64640064,0x86860086,0x68680068,0x98980098,0x16160016,
+ 0xd4d400d4,0xa4a400a4,0x5c5c005c,0xcccc00cc,0x5d5d005d,0x65650065,0xb6b600b6,0x92920092,
+ 0x6c6c006c,0x70700070,0x48480048,0x50500050,0xfdfd00fd,0xeded00ed,0xb9b900b9,0xdada00da,
+ 0x5e5e005e,0x15150015,0x46460046,0x57570057,0xa7a700a7,0x8d8d008d,0x9d9d009d,0x84840084,
+ 0x90900090,0xd8d800d8,0xabab00ab,0x00000000,0x8c8c008c,0xbcbc00bc,0xd3d300d3,0x0a0a000a,
+ 0xf7f700f7,0xe4e400e4,0x58580058,0x05050005,0xb8b800b8,0xb3b300b3,0x45450045,0x06060006,
+ 0xd0d000d0,0x2c2c002c,0x1e1e001e,0x8f8f008f,0xcaca00ca,0x3f3f003f,0x0f0f000f,0x02020002,
+ 0xc1c100c1,0xafaf00af,0xbdbd00bd,0x03030003,0x01010001,0x13130013,0x8a8a008a,0x6b6b006b,
+ 0x3a3a003a,0x91910091,0x11110011,0x41410041,0x4f4f004f,0x67670067,0xdcdc00dc,0xeaea00ea,
+ 0x97970097,0xf2f200f2,0xcfcf00cf,0xcece00ce,0xf0f000f0,0xb4b400b4,0xe6e600e6,0x73730073,
+ 0x96960096,0xacac00ac,0x74740074,0x22220022,0xe7e700e7,0xadad00ad,0x35350035,0x85850085,
+ 0xe2e200e2,0xf9f900f9,0x37370037,0xe8e800e8,0x1c1c001c,0x75750075,0xdfdf00df,0x6e6e006e,
+ 0x47470047,0xf1f100f1,0x1a1a001a,0x71710071,0x1d1d001d,0x29290029,0xc5c500c5,0x89890089,
+ 0x6f6f006f,0xb7b700b7,0x62620062,0x0e0e000e,0xaaaa00aa,0x18180018,0xbebe00be,0x1b1b001b,
+ 0xfcfc00fc,0x56560056,0x3e3e003e,0x4b4b004b,0xc6c600c6,0xd2d200d2,0x79790079,0x20200020,
+ 0x9a9a009a,0xdbdb00db,0xc0c000c0,0xfefe00fe,0x78780078,0xcdcd00cd,0x5a5a005a,0xf4f400f4,
+ 0x1f1f001f,0xdddd00dd,0xa8a800a8,0x33330033,0x88880088,0x07070007,0xc7c700c7,0x31310031,
+ 0xb1b100b1,0x12120012,0x10100010,0x59590059,0x27270027,0x80800080,0xecec00ec,0x5f5f005f,
+ 0x60600060,0x51510051,0x7f7f007f,0xa9a900a9,0x19190019,0xb5b500b5,0x4a4a004a,0x0d0d000d,
+ 0x2d2d002d,0xe5e500e5,0x7a7a007a,0x9f9f009f,0x93930093,0xc9c900c9,0x9c9c009c,0xefef00ef,
+ 0xa0a000a0,0xe0e000e0,0x3b3b003b,0x4d4d004d,0xaeae00ae,0x2a2a002a,0xf5f500f5,0xb0b000b0,
+ 0xc8c800c8,0xebeb00eb,0xbbbb00bb,0x3c3c003c,0x83830083,0x53530053,0x99990099,0x61610061,
+ 0x17170017,0x2b2b002b,0x04040004,0x7e7e007e,0xbaba00ba,0x77770077,0xd6d600d6,0x26260026,
+ 0xe1e100e1,0x69690069,0x14140014,0x63630063,0x55550055,0x21210021,0x0c0c000c,0x7d7d007d
+};
+
+alignas(64)
+const uint32_t X2[256]={
+ 0x30303000,0x68686800,0x99999900,0x1b1b1b00,0x87878700,0xb9b9b900,0x21212100,0x78787800,
+ 0x50505000,0x39393900,0xdbdbdb00,0xe1e1e100,0x72727200,0x09090900,0x62626200,0x3c3c3c00,
+ 0x3e3e3e00,0x7e7e7e00,0x5e5e5e00,0x8e8e8e00,0xf1f1f100,0xa0a0a000,0xcccccc00,0xa3a3a300,
+ 0x2a2a2a00,0x1d1d1d00,0xfbfbfb00,0xb6b6b600,0xd6d6d600,0x20202000,0xc4c4c400,0x8d8d8d00,
+ 0x81818100,0x65656500,0xf5f5f500,0x89898900,0xcbcbcb00,0x9d9d9d00,0x77777700,0xc6c6c600,
+ 0x57575700,0x43434300,0x56565600,0x17171700,0xd4d4d400,0x40404000,0x1a1a1a00,0x4d4d4d00,
+ 0xc0c0c000,0x63636300,0x6c6c6c00,0xe3e3e300,0xb7b7b700,0xc8c8c800,0x64646400,0x6a6a6a00,
+ 0x53535300,0xaaaaaa00,0x38383800,0x98989800,0x0c0c0c00,0xf4f4f400,0x9b9b9b00,0xededed00,
+ 0x7f7f7f00,0x22222200,0x76767600,0xafafaf00,0xdddddd00,0x3a3a3a00,0x0b0b0b00,0x58585800,
+ 0x67676700,0x88888800,0x06060600,0xc3c3c300,0x35353500,0x0d0d0d00,0x01010100,0x8b8b8b00,
+ 0x8c8c8c00,0xc2c2c200,0xe6e6e600,0x5f5f5f00,0x02020200,0x24242400,0x75757500,0x93939300,
+ 0x66666600,0x1e1e1e00,0xe5e5e500,0xe2e2e200,0x54545400,0xd8d8d800,0x10101000,0xcecece00,
+ 0x7a7a7a00,0xe8e8e800,0x08080800,0x2c2c2c00,0x12121200,0x97979700,0x32323200,0xababab00,
+ 0xb4b4b400,0x27272700,0x0a0a0a00,0x23232300,0xdfdfdf00,0xefefef00,0xcacaca00,0xd9d9d900,
+ 0xb8b8b800,0xfafafa00,0xdcdcdc00,0x31313100,0x6b6b6b00,0xd1d1d100,0xadadad00,0x19191900,
+ 0x49494900,0xbdbdbd00,0x51515100,0x96969600,0xeeeeee00,0xe4e4e400,0xa8a8a800,0x41414100,
+ 0xdadada00,0xffffff00,0xcdcdcd00,0x55555500,0x86868600,0x36363600,0xbebebe00,0x61616100,
+ 0x52525200,0xf8f8f800,0xbbbbbb00,0x0e0e0e00,0x82828200,0x48484800,0x69696900,0x9a9a9a00,
+ 0xe0e0e000,0x47474700,0x9e9e9e00,0x5c5c5c00,0x04040400,0x4b4b4b00,0x34343400,0x15151500,
+ 0x79797900,0x26262600,0xa7a7a700,0xdedede00,0x29292900,0xaeaeae00,0x92929200,0xd7d7d700,
+ 0x84848400,0xe9e9e900,0xd2d2d200,0xbababa00,0x5d5d5d00,0xf3f3f300,0xc5c5c500,0xb0b0b000,
+ 0xbfbfbf00,0xa4a4a400,0x3b3b3b00,0x71717100,0x44444400,0x46464600,0x2b2b2b00,0xfcfcfc00,
+ 0xebebeb00,0x6f6f6f00,0xd5d5d500,0xf6f6f600,0x14141400,0xfefefe00,0x7c7c7c00,0x70707000,
+ 0x5a5a5a00,0x7d7d7d00,0xfdfdfd00,0x2f2f2f00,0x18181800,0x83838300,0x16161600,0xa5a5a500,
+ 0x91919100,0x1f1f1f00,0x05050500,0x95959500,0x74747400,0xa9a9a900,0xc1c1c100,0x5b5b5b00,
+ 0x4a4a4a00,0x85858500,0x6d6d6d00,0x13131300,0x07070700,0x4f4f4f00,0x4e4e4e00,0x45454500,
+ 0xb2b2b200,0x0f0f0f00,0xc9c9c900,0x1c1c1c00,0xa6a6a600,0xbcbcbc00,0xececec00,0x73737300,
+ 0x90909000,0x7b7b7b00,0xcfcfcf00,0x59595900,0x8f8f8f00,0xa1a1a100,0xf9f9f900,0x2d2d2d00,
+ 0xf2f2f200,0xb1b1b100,0x00000000,0x94949400,0x37373700,0x9f9f9f00,0xd0d0d000,0x2e2e2e00,
+ 0x9c9c9c00,0x6e6e6e00,0x28282800,0x3f3f3f00,0x80808000,0xf0f0f000,0x3d3d3d00,0xd3d3d300,
+ 0x25252500,0x8a8a8a00,0xb5b5b500,0xe7e7e700,0x42424200,0xb3b3b300,0xc7c7c700,0xeaeaea00,
+ 0xf7f7f700,0x4c4c4c00,0x11111100,0x33333300,0x03030300,0xa2a2a200,0xacacac00,0x60606000
+};
+
+inline void ARIA_FO(uint32_t& T0, uint32_t& T1, uint32_t& T2, uint32_t& T3)
+ {
+ T0 = S1[get_byte(0,T0)] ^ S2[get_byte(1,T0)] ^ X1[get_byte(2,T0)] ^ X2[get_byte(3,T0)];
+ T1 = S1[get_byte(0,T1)] ^ S2[get_byte(1,T1)] ^ X1[get_byte(2,T1)] ^ X2[get_byte(3,T1)];
+ T2 = S1[get_byte(0,T2)] ^ S2[get_byte(1,T2)] ^ X1[get_byte(2,T2)] ^ X2[get_byte(3,T2)];
+ T3 = S1[get_byte(0,T3)] ^ S2[get_byte(1,T3)] ^ X1[get_byte(2,T3)] ^ X2[get_byte(3,T3)];
+
+ T1 ^= T2;
+ T2 ^= T3; T0 ^= T1;
+ T3 ^= T1; T2 ^= T0;
+ T1 ^= T2;
+
+ T1 = ((T1 << 8) & 0xFF00FF00) | ((T1 >> 8) & 0x00FF00FF);
+ T2 = rotr<16>(T2);
+ T3 = reverse_bytes(T3);
+
+ T1 ^= T2;
+ T2 ^= T3; T0 ^= T1;
+ T3 ^= T1; T2 ^= T0;
+ T1 ^= T2;
+ }
+
+inline void ARIA_FE(uint32_t& T0, uint32_t& T1, uint32_t& T2, uint32_t& T3)
+ {
+ T0 = X1[get_byte(0,T0)] ^ X2[get_byte(1,T0)] ^ S1[get_byte(2,T0)] ^ S2[get_byte(3,T0)];
+ T1 = X1[get_byte(0,T1)] ^ X2[get_byte(1,T1)] ^ S1[get_byte(2,T1)] ^ S2[get_byte(3,T1)];
+ T2 = X1[get_byte(0,T2)] ^ X2[get_byte(1,T2)] ^ S1[get_byte(2,T2)] ^ S2[get_byte(3,T2)];
+ T3 = X1[get_byte(0,T3)] ^ X2[get_byte(1,T3)] ^ S1[get_byte(2,T3)] ^ S2[get_byte(3,T3)];
+
+ T1 ^= T2;
+ T2 ^= T3; T0 ^= T1;
+ T3 ^= T1; T2 ^= T0;
+ T1 ^= T2;
+
+ T3 = ((T3 << 8) & 0xFF00FF00) | ((T3 >> 8) & 0x00FF00FF);
+ T0 = rotr<16>(T0);
+ T1 = reverse_bytes(T1);
+
+ T1 ^= T2;
+ T2 ^= T3; T0 ^= T1;
+ T3 ^= T1; T2 ^= T0;
+ T1 ^= T2;
+ }
+
+/*
+* ARIA encryption and decryption
+*/
+void transform(const uint8_t in[], uint8_t out[], size_t blocks,
+ const secure_vector<uint32_t>& KS)
+ {
+ /*
+ * Hit every cache line of S1, S2, X1, X2
+ *
+ * The initializer of Z ensures Z == 0xFFFFFFFF for any cache line
+ * size that is a power of 2 and <= 512
+ */
+ const size_t cache_line_size = CPUID::cache_line_size();
+
+ volatile uint32_t Z = 0x11101010;
+ for(size_t i = 0; i < 256; i += cache_line_size / sizeof(uint32_t))
+ {
+ Z |= S1[i] | S2[i] | X1[i] | X2[i];
+ }
+
+ const size_t ROUNDS = (KS.size() / 4) - 1;
+
+ for(size_t i = 0; i != blocks; ++i)
+ {
+ uint32_t t0, t1, t2, t3;
+ load_be(in + 16*i, t0, t1, t2, t3);
+
+ t0 &= Z;
+
+ for(size_t r = 0; r < ROUNDS; r += 2)
+ {
+ t0 ^= KS[4*r];
+ t1 ^= KS[4*r+1];
+ t2 ^= KS[4*r+2];
+ t3 ^= KS[4*r+3];
+ ARIA_FO(t0,t1,t2,t3);
+
+ t0 ^= KS[4*r+4];
+ t1 ^= KS[4*r+5];
+ t2 ^= KS[4*r+6];
+ t3 ^= KS[4*r+7];
+
+ if(r != ROUNDS-2)
+ ARIA_FE(t0,t1,t2,t3);
+ }
+
+ out[16*i+ 0] = static_cast<uint8_t>(X1[get_byte(0,t0)] ) ^ get_byte(0, KS[4*ROUNDS]);
+ out[16*i+ 1] = static_cast<uint8_t>(X2[get_byte(1,t0)]>>8) ^ get_byte(1, KS[4*ROUNDS]);
+ out[16*i+ 2] = static_cast<uint8_t>(S1[get_byte(2,t0)] ) ^ get_byte(2, KS[4*ROUNDS]);
+ out[16*i+ 3] = static_cast<uint8_t>(S2[get_byte(3,t0)] ) ^ get_byte(3, KS[4*ROUNDS]);
+ out[16*i+ 4] = static_cast<uint8_t>(X1[get_byte(0,t1)] ) ^ get_byte(0, KS[4*ROUNDS+1]);
+ out[16*i+ 5] = static_cast<uint8_t>(X2[get_byte(1,t1)]>>8) ^ get_byte(1, KS[4*ROUNDS+1]);
+ out[16*i+ 6] = static_cast<uint8_t>(S1[get_byte(2,t1)] ) ^ get_byte(2, KS[4*ROUNDS+1]);
+ out[16*i+ 7] = static_cast<uint8_t>(S2[get_byte(3,t1)] ) ^ get_byte(3, KS[4*ROUNDS+1]);
+ out[16*i+ 8] = static_cast<uint8_t>(X1[get_byte(0,t2)] ) ^ get_byte(0, KS[4*ROUNDS+2]);
+ out[16*i+ 9] = static_cast<uint8_t>(X2[get_byte(1,t2)]>>8) ^ get_byte(1, KS[4*ROUNDS+2]);
+ out[16*i+10] = static_cast<uint8_t>(S1[get_byte(2,t2)] ) ^ get_byte(2, KS[4*ROUNDS+2]);
+ out[16*i+11] = static_cast<uint8_t>(S2[get_byte(3,t2)] ) ^ get_byte(3, KS[4*ROUNDS+2]);
+ out[16*i+12] = static_cast<uint8_t>(X1[get_byte(0,t3)] ) ^ get_byte(0, KS[4*ROUNDS+3]);
+ out[16*i+13] = static_cast<uint8_t>(X2[get_byte(1,t3)]>>8) ^ get_byte(1, KS[4*ROUNDS+3]);
+ out[16*i+14] = static_cast<uint8_t>(S1[get_byte(2,t3)] ) ^ get_byte(2, KS[4*ROUNDS+3]);
+ out[16*i+15] = static_cast<uint8_t>(S2[get_byte(3,t3)] ) ^ get_byte(3, KS[4*ROUNDS+3]);
+ }
+ }
+
+// n-bit right shift of Y XORed to X
+template<size_t N>
+inline void ARIA_ROL128(const uint32_t X[4], const uint32_t Y[4], uint32_t KS[4])
+ {
+ // MSVC is not generating a "rotate immediate". Constify to help it along.
+ static const size_t Q = 4 - (N / 32);
+ static const size_t R = N % 32;
+ static_assert(R > 0 && R < 32, "Rotation in range for type");
+ KS[0] = (X[0]) ^ ((Y[(Q )%4])>>R) ^ ((Y[(Q+3)%4])<<(32-R));
+ KS[1] = (X[1]) ^ ((Y[(Q+1)%4])>>R) ^ ((Y[(Q )%4])<<(32-R));
+ KS[2] = (X[2]) ^ ((Y[(Q+2)%4])>>R) ^ ((Y[(Q+1)%4])<<(32-R));
+ KS[3] = (X[3]) ^ ((Y[(Q+3)%4])>>R) ^ ((Y[(Q+2)%4])<<(32-R));
+ }
+
+/*
+* ARIA Key Schedule
+*/
+void key_schedule(secure_vector<uint32_t>& ERK,
+ secure_vector<uint32_t>& DRK,
+ const uint8_t key[], size_t length)
+ {
+ const uint32_t KRK[3][4] = {
+ {0x517cc1b7, 0x27220a94, 0xfe13abe8, 0xfa9a6ee0},
+ {0x6db14acc, 0x9e21c820, 0xff28b1d5, 0xef5de2b0},
+ {0xdb92371d, 0x2126e970, 0x03249775, 0x04e8c90e}
+ };
+
+ const size_t CK0 = (length / 8) - 2;
+ const size_t CK1 = (CK0 + 1) % 3;
+ const size_t CK2 = (CK1 + 1) % 3;
+
+ uint32_t w0[4];
+ uint32_t w1[4];
+ uint32_t w2[4];
+ uint32_t w3[4];
+
+ w0[0] = load_be<uint32_t>(key,0);
+ w0[1] = load_be<uint32_t>(key,1);
+ w0[2] = load_be<uint32_t>(key,2);
+ w0[3] = load_be<uint32_t>(key,3);
+
+ w1[0] = w0[0] ^ KRK[CK0][0];
+ w1[1] = w0[1] ^ KRK[CK0][1];
+ w1[2] = w0[2] ^ KRK[CK0][2];
+ w1[3] = w0[3] ^ KRK[CK0][3];
+
+ ARIA_FO(w1[0], w1[1], w1[2], w1[3]);
+
+ if(length == 24 || length == 32)
+ {
+ w1[0] ^= load_be<uint32_t>(key,4);
+ w1[1] ^= load_be<uint32_t>(key,5);
+ }
+ if(length == 32)
+ {
+ w1[2] ^= load_be<uint32_t>(key,6);
+ w1[3] ^= load_be<uint32_t>(key,7);
+ }
+
+ w2[0] = w1[0] ^ KRK[CK1][0];
+ w2[1] = w1[1] ^ KRK[CK1][1];
+ w2[2] = w1[2] ^ KRK[CK1][2];
+ w2[3] = w1[3] ^ KRK[CK1][3];
+
+ ARIA_FE(w2[0], w2[1], w2[2], w2[3]);
+
+ w2[0] ^= w0[0];
+ w2[1] ^= w0[1];
+ w2[2] ^= w0[2];
+ w2[3] ^= w0[3];
+
+ w3[0] = w2[0] ^ KRK[CK2][0];
+ w3[1] = w2[1] ^ KRK[CK2][1];
+ w3[2] = w2[2] ^ KRK[CK2][2];
+ w3[3] = w2[3] ^ KRK[CK2][3];
+
+ ARIA_FO(w3[0], w3[1], w3[2], w3[3]);
+
+ w3[0] ^= w1[0];
+ w3[1] ^= w1[1];
+ w3[2] ^= w1[2];
+ w3[3] ^= w1[3];
+
+ if(length == 16)
+ ERK.resize(4*13);
+ else if(length == 24)
+ ERK.resize(4*15);
+ else if(length == 32)
+ ERK.resize(4*17);
+
+ ARIA_ROL128<19>(w0, w1, &ERK[ 0]);
+ ARIA_ROL128<19>(w1, w2, &ERK[ 4]);
+ ARIA_ROL128<19>(w2, w3, &ERK[ 8]);
+ ARIA_ROL128<19>(w3, w0, &ERK[12]);
+ ARIA_ROL128<31>(w0, w1, &ERK[16]);
+ ARIA_ROL128<31>(w1, w2, &ERK[20]);
+ ARIA_ROL128<31>(w2, w3, &ERK[24]);
+ ARIA_ROL128<31>(w3, w0, &ERK[28]);
+ ARIA_ROL128<67>(w0, w1, &ERK[32]);
+ ARIA_ROL128<67>(w1, w2, &ERK[36]);
+ ARIA_ROL128<67>(w2, w3, &ERK[40]);
+ ARIA_ROL128<67>(w3, w0, &ERK[44]);
+ ARIA_ROL128<97>(w0, w1, &ERK[48]);
+
+ if(length == 24 || length == 32)
+ {
+ ARIA_ROL128<97>(w1, w2, &ERK[52]);
+ ARIA_ROL128<97>(w2, w3, &ERK[56]);
+
+ if(length == 32)
+ {
+ ARIA_ROL128< 97>(w3, w0, &ERK[60]);
+ ARIA_ROL128<109>(w0, w1, &ERK[64]);
+ }
+ }
+
+ // Now create the decryption key schedule
+ DRK.resize(ERK.size());
+
+ for(size_t i = 0; i != DRK.size(); i += 4)
+ {
+ DRK[i ] = ERK[ERK.size()-4-i];
+ DRK[i+1] = ERK[ERK.size()-3-i];
+ DRK[i+2] = ERK[ERK.size()-2-i];
+ DRK[i+3] = ERK[ERK.size()-1-i];
+ }
+
+ for(size_t i = 4; i != DRK.size() - 4; i += 4)
+ {
+ for(size_t j = 0; j != 4; ++j)
+ {
+ DRK[i+j] = rotr<8>(DRK[i+j]) ^
+ rotr<16>(DRK[i+j]) ^
+ rotr<24>(DRK[i+j]);
+ }
+
+ DRK[i+1] ^= DRK[i+2]; DRK[i+2] ^= DRK[i+3];
+ DRK[i+0] ^= DRK[i+1]; DRK[i+3] ^= DRK[i+1];
+ DRK[i+2] ^= DRK[i+0]; DRK[i+1] ^= DRK[i+2];
+
+ DRK[i+1] = ((DRK[i+1] << 8) & 0xFF00FF00) | ((DRK[i+1] >> 8) & 0x00FF00FF);
+ DRK[i+2] = rotr<16>(DRK[i+2]);
+ DRK[i+3] = reverse_bytes(DRK[i+3]);
+
+ DRK[i+1] ^= DRK[i+2]; DRK[i+2] ^= DRK[i+3];
+ DRK[i+0] ^= DRK[i+1]; DRK[i+3] ^= DRK[i+1];
+ DRK[i+2] ^= DRK[i+0]; DRK[i+1] ^= DRK[i+2];
+ }
+ }
+
+}
+
+}
+
+void ARIA_128::encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+ {
+ verify_key_set(m_ERK.size() > 0);
+ ARIA_F::transform(in, out, blocks, m_ERK);
+ }
+
+void ARIA_192::encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+ {
+ verify_key_set(m_ERK.size() > 0);
+ ARIA_F::transform(in, out, blocks, m_ERK);
+ }
+
+void ARIA_256::encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+ {
+ verify_key_set(m_ERK.size() > 0);
+ ARIA_F::transform(in, out, blocks, m_ERK);
+ }
+
+void ARIA_128::decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+ {
+ verify_key_set(m_DRK.size() > 0);
+ ARIA_F::transform(in, out, blocks, m_DRK);
+ }
+
+void ARIA_192::decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+ {
+ verify_key_set(m_DRK.size() > 0);
+ ARIA_F::transform(in, out, blocks, m_DRK);
+ }
+
+void ARIA_256::decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+ {
+ verify_key_set(m_DRK.size() > 0);
+ ARIA_F::transform(in, out, blocks, m_DRK);
+ }
+
+void ARIA_128::key_schedule(const uint8_t key[], size_t length)
+ {
+ ARIA_F::key_schedule(m_ERK, m_DRK, key, length);
+ }
+
+void ARIA_192::key_schedule(const uint8_t key[], size_t length)
+ {
+ ARIA_F::key_schedule(m_ERK, m_DRK, key, length);
+ }
+
+void ARIA_256::key_schedule(const uint8_t key[], size_t length)
+ {
+ ARIA_F::key_schedule(m_ERK, m_DRK, key, length);
+ }
+
+void ARIA_128::clear()
+ {
+ zap(m_ERK);
+ zap(m_DRK);
+ }
+
+void ARIA_192::clear()
+ {
+ zap(m_ERK);
+ zap(m_DRK);
+ }
+
+void ARIA_256::clear()
+ {
+ zap(m_ERK);
+ zap(m_DRK);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/block/aria/aria.h b/comm/third_party/botan/src/lib/block/aria/aria.h
new file mode 100644
index 0000000000..507226b7d3
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/aria/aria.h
@@ -0,0 +1,84 @@
+/*
+* ARIA
+* (C) 2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*
+* This ARIA implementation is based on the 32-bit implementation by Aaram Yun from the
+* National Security Research Institute, KOREA. Aaram Yun's implementation is based on
+* the 8-bit implementation by Jin Hong. The source files are available in ARIA.zip from
+* the Korea Internet & Security Agency website.
+* <A HREF="https://tools.ietf.org/html/rfc5794">RFC 5794, A Description of the ARIA Encryption Algorithm</A>,
+* <A HREF="http://seed.kisa.or.kr/iwt/ko/bbs/EgovReferenceList.do?bbsId=BBSMSTR_000000000002">Korea
+* Internet & Security Agency homepage</A>
+*/
+
+#ifndef BOTAN_ARIA_H_
+#define BOTAN_ARIA_H_
+
+#include <botan/block_cipher.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(aria.h)
+
+namespace Botan {
+
+/**
+* ARIA-128
+*/
+class BOTAN_PUBLIC_API(2,3) ARIA_128 final : public Block_Cipher_Fixed_Params<16, 16>
+ {
+ public:
+ void encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+ void decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+
+ void clear() override;
+ std::string name() const override { return "ARIA-128"; }
+ BlockCipher* clone() const override { return new ARIA_128; }
+ private:
+ void key_schedule(const uint8_t key[], size_t length) override;
+
+ // Encryption and Decryption round keys.
+ secure_vector<uint32_t> m_ERK, m_DRK;
+ };
+
+/**
+* ARIA-192
+*/
+class BOTAN_PUBLIC_API(2,3) ARIA_192 final : public Block_Cipher_Fixed_Params<16, 24>
+ {
+ public:
+ void encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+ void decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+
+ void clear() override;
+ std::string name() const override { return "ARIA-192"; }
+ BlockCipher* clone() const override { return new ARIA_192; }
+ private:
+ void key_schedule(const uint8_t key[], size_t length) override;
+
+ // Encryption and Decryption round keys.
+ secure_vector<uint32_t> m_ERK, m_DRK;
+ };
+
+/**
+* ARIA-256
+*/
+class BOTAN_PUBLIC_API(2,3) ARIA_256 final : public Block_Cipher_Fixed_Params<16, 32>
+ {
+ public:
+ void encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+ void decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+
+ void clear() override;
+ std::string name() const override { return "ARIA-256"; }
+ BlockCipher* clone() const override { return new ARIA_256; }
+ private:
+ void key_schedule(const uint8_t key[], size_t length) override;
+
+ // Encryption and Decryption round keys.
+ secure_vector<uint32_t> m_ERK, m_DRK;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/block/aria/info.txt b/comm/third_party/botan/src/lib/block/aria/info.txt
new file mode 100644
index 0000000000..78c16726c8
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/aria/info.txt
@@ -0,0 +1,7 @@
+<defines>
+ARIA -> 20170415
+</defines>
+
+<header:public>
+aria.h
+</header:public>
diff --git a/comm/third_party/botan/src/lib/block/block_cipher.cpp b/comm/third_party/botan/src/lib/block/block_cipher.cpp
new file mode 100644
index 0000000000..fb0564646e
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/block_cipher.cpp
@@ -0,0 +1,363 @@
+/*
+* Block Ciphers
+* (C) 2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/block_cipher.h>
+#include <botan/scan_name.h>
+#include <botan/exceptn.h>
+
+#if defined(BOTAN_HAS_AES)
+ #include <botan/aes.h>
+#endif
+
+#if defined(BOTAN_HAS_ARIA)
+ #include <botan/aria.h>
+#endif
+
+#if defined(BOTAN_HAS_BLOWFISH)
+ #include <botan/blowfish.h>
+#endif
+
+#if defined(BOTAN_HAS_CAMELLIA)
+ #include <botan/camellia.h>
+#endif
+
+#if defined(BOTAN_HAS_CAST_128)
+ #include <botan/cast128.h>
+#endif
+
+#if defined(BOTAN_HAS_CAST_256)
+ #include <botan/cast256.h>
+#endif
+
+#if defined(BOTAN_HAS_CASCADE)
+ #include <botan/cascade.h>
+#endif
+
+#if defined(BOTAN_HAS_DES)
+ #include <botan/des.h>
+ #include <botan/desx.h>
+#endif
+
+#if defined(BOTAN_HAS_GOST_28147_89)
+ #include <botan/gost_28147.h>
+#endif
+
+#if defined(BOTAN_HAS_IDEA)
+ #include <botan/idea.h>
+#endif
+
+#if defined(BOTAN_HAS_KASUMI)
+ #include <botan/kasumi.h>
+#endif
+
+#if defined(BOTAN_HAS_LION)
+ #include <botan/lion.h>
+#endif
+
+#if defined(BOTAN_HAS_MISTY1)
+ #include <botan/misty1.h>
+#endif
+
+#if defined(BOTAN_HAS_NOEKEON)
+ #include <botan/noekeon.h>
+#endif
+
+#if defined(BOTAN_HAS_SEED)
+ #include <botan/seed.h>
+#endif
+
+#if defined(BOTAN_HAS_SERPENT)
+ #include <botan/serpent.h>
+#endif
+
+#if defined(BOTAN_HAS_SHACAL2)
+ #include <botan/shacal2.h>
+#endif
+
+#if defined(BOTAN_HAS_SM4)
+ #include <botan/sm4.h>
+#endif
+
+#if defined(BOTAN_HAS_TWOFISH)
+ #include <botan/twofish.h>
+#endif
+
+#if defined(BOTAN_HAS_THREEFISH_512)
+ #include <botan/threefish_512.h>
+#endif
+
+#if defined(BOTAN_HAS_XTEA)
+ #include <botan/xtea.h>
+#endif
+
+#if defined(BOTAN_HAS_OPENSSL)
+ #include <botan/internal/openssl.h>
+#endif
+
+#if defined(BOTAN_HAS_COMMONCRYPTO)
+ #include <botan/internal/commoncrypto.h>
+#endif
+
+namespace Botan {
+
+std::unique_ptr<BlockCipher>
+BlockCipher::create(const std::string& algo,
+ const std::string& provider)
+ {
+#if defined(BOTAN_HAS_COMMONCRYPTO)
+ if(provider.empty() || provider == "commoncrypto")
+ {
+ if(auto bc = make_commoncrypto_block_cipher(algo))
+ return bc;
+
+ if(!provider.empty())
+ return nullptr;
+ }
+#endif
+
+#if defined(BOTAN_HAS_OPENSSL)
+ if(provider.empty() || provider == "openssl")
+ {
+ if(auto bc = make_openssl_block_cipher(algo))
+ return bc;
+
+ if(!provider.empty())
+ return nullptr;
+ }
+#endif
+
+ // TODO: CryptoAPI
+ // TODO: /dev/crypto
+
+ // Only base providers from here on out
+ if(provider.empty() == false && provider != "base")
+ return nullptr;
+
+#if defined(BOTAN_HAS_AES)
+ if(algo == "AES-128")
+ {
+ return std::unique_ptr<BlockCipher>(new AES_128);
+ }
+
+ if(algo == "AES-192")
+ {
+ return std::unique_ptr<BlockCipher>(new AES_192);
+ }
+
+ if(algo == "AES-256")
+ {
+ return std::unique_ptr<BlockCipher>(new AES_256);
+ }
+#endif
+
+#if defined(BOTAN_HAS_ARIA)
+ if(algo == "ARIA-128")
+ {
+ return std::unique_ptr<BlockCipher>(new ARIA_128);
+ }
+
+ if(algo == "ARIA-192")
+ {
+ return std::unique_ptr<BlockCipher>(new ARIA_192);
+ }
+
+ if(algo == "ARIA-256")
+ {
+ return std::unique_ptr<BlockCipher>(new ARIA_256);
+ }
+#endif
+
+#if defined(BOTAN_HAS_SERPENT)
+ if(algo == "Serpent")
+ {
+ return std::unique_ptr<BlockCipher>(new Serpent);
+ }
+#endif
+
+#if defined(BOTAN_HAS_SHACAL2)
+ if(algo == "SHACAL2")
+ {
+ return std::unique_ptr<BlockCipher>(new SHACAL2);
+ }
+#endif
+
+#if defined(BOTAN_HAS_TWOFISH)
+ if(algo == "Twofish")
+ {
+ return std::unique_ptr<BlockCipher>(new Twofish);
+ }
+#endif
+
+#if defined(BOTAN_HAS_THREEFISH_512)
+ if(algo == "Threefish-512")
+ {
+ return std::unique_ptr<BlockCipher>(new Threefish_512);
+ }
+#endif
+
+#if defined(BOTAN_HAS_BLOWFISH)
+ if(algo == "Blowfish")
+ {
+ return std::unique_ptr<BlockCipher>(new Blowfish);
+ }
+#endif
+
+#if defined(BOTAN_HAS_CAMELLIA)
+ if(algo == "Camellia-128")
+ {
+ return std::unique_ptr<BlockCipher>(new Camellia_128);
+ }
+
+ if(algo == "Camellia-192")
+ {
+ return std::unique_ptr<BlockCipher>(new Camellia_192);
+ }
+
+ if(algo == "Camellia-256")
+ {
+ return std::unique_ptr<BlockCipher>(new Camellia_256);
+ }
+#endif
+
+#if defined(BOTAN_HAS_DES)
+ if(algo == "DES")
+ {
+ return std::unique_ptr<BlockCipher>(new DES);
+ }
+
+ if(algo == "DESX")
+ {
+ return std::unique_ptr<BlockCipher>(new DESX);
+ }
+
+ if(algo == "TripleDES" || algo == "3DES" || algo == "DES-EDE")
+ {
+ return std::unique_ptr<BlockCipher>(new TripleDES);
+ }
+#endif
+
+#if defined(BOTAN_HAS_NOEKEON)
+ if(algo == "Noekeon")
+ {
+ return std::unique_ptr<BlockCipher>(new Noekeon);
+ }
+#endif
+
+#if defined(BOTAN_HAS_CAST_128)
+ if(algo == "CAST-128" || algo == "CAST5")
+ {
+ return std::unique_ptr<BlockCipher>(new CAST_128);
+ }
+#endif
+
+#if defined(BOTAN_HAS_CAST_256)
+ if(algo == "CAST-256")
+ {
+ return std::unique_ptr<BlockCipher>(new CAST_256);
+ }
+#endif
+
+#if defined(BOTAN_HAS_IDEA)
+ if(algo == "IDEA")
+ {
+ return std::unique_ptr<BlockCipher>(new IDEA);
+ }
+#endif
+
+#if defined(BOTAN_HAS_KASUMI)
+ if(algo == "KASUMI")
+ {
+ return std::unique_ptr<BlockCipher>(new KASUMI);
+ }
+#endif
+
+#if defined(BOTAN_HAS_MISTY1)
+ if(algo == "MISTY1")
+ {
+ return std::unique_ptr<BlockCipher>(new MISTY1);
+ }
+#endif
+
+#if defined(BOTAN_HAS_SEED)
+ if(algo == "SEED")
+ {
+ return std::unique_ptr<BlockCipher>(new SEED);
+ }
+#endif
+
+#if defined(BOTAN_HAS_SM4)
+ if(algo == "SM4")
+ {
+ return std::unique_ptr<BlockCipher>(new SM4);
+ }
+#endif
+
+#if defined(BOTAN_HAS_XTEA)
+ if(algo == "XTEA")
+ {
+ return std::unique_ptr<BlockCipher>(new XTEA);
+ }
+#endif
+
+ const SCAN_Name req(algo);
+
+#if defined(BOTAN_HAS_GOST_28147_89)
+ if(req.algo_name() == "GOST-28147-89")
+ {
+ return std::unique_ptr<BlockCipher>(new GOST_28147_89(req.arg(0, "R3411_94_TestParam")));
+ }
+#endif
+
+#if defined(BOTAN_HAS_CASCADE)
+ if(req.algo_name() == "Cascade" && req.arg_count() == 2)
+ {
+ std::unique_ptr<BlockCipher> c1(BlockCipher::create(req.arg(0)));
+ std::unique_ptr<BlockCipher> c2(BlockCipher::create(req.arg(1)));
+
+ if(c1 && c2)
+ return std::unique_ptr<BlockCipher>(new Cascade_Cipher(c1.release(), c2.release()));
+ }
+#endif
+
+#if defined(BOTAN_HAS_LION)
+ if(req.algo_name() == "Lion" && req.arg_count_between(2, 3))
+ {
+ std::unique_ptr<HashFunction> hash(HashFunction::create(req.arg(0)));
+ std::unique_ptr<StreamCipher> stream(StreamCipher::create(req.arg(1)));
+
+ if(hash && stream)
+ {
+ const size_t block_size = req.arg_as_integer(2, 1024);
+ return std::unique_ptr<BlockCipher>(new Lion(hash.release(), stream.release(), block_size));
+ }
+ }
+#endif
+
+ BOTAN_UNUSED(req);
+ BOTAN_UNUSED(provider);
+
+ return nullptr;
+ }
+
+//static
+std::unique_ptr<BlockCipher>
+BlockCipher::create_or_throw(const std::string& algo,
+ const std::string& provider)
+ {
+ if(auto bc = BlockCipher::create(algo, provider))
+ {
+ return bc;
+ }
+ throw Lookup_Error("Block cipher", algo, provider);
+ }
+
+std::vector<std::string> BlockCipher::providers(const std::string& algo)
+ {
+ return probe_providers_of<BlockCipher>(algo, { "base", "openssl", "commoncrypto" });
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/block/block_cipher.h b/comm/third_party/botan/src/lib/block/block_cipher.h
new file mode 100644
index 0000000000..68cdd1afe0
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/block_cipher.h
@@ -0,0 +1,254 @@
+/*
+* Block Cipher Base Class
+* (C) 1999-2009 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_BLOCK_CIPHER_H_
+#define BOTAN_BLOCK_CIPHER_H_
+
+#include <botan/sym_algo.h>
+#include <string>
+#include <memory>
+#include <vector>
+
+namespace Botan {
+
+/**
+* This class represents a block cipher object.
+*/
+class BOTAN_PUBLIC_API(2,0) BlockCipher : public SymmetricAlgorithm
+ {
+ public:
+
+ /**
+ * Create an instance based on a name
+ * If provider is empty then best available is chosen.
+ * @param algo_spec algorithm name
+ * @param provider provider implementation to choose
+ * @return a null pointer if the algo/provider combination cannot be found
+ */
+ static std::unique_ptr<BlockCipher>
+ create(const std::string& algo_spec,
+ const std::string& provider = "");
+
+ /**
+ * Create an instance based on a name, or throw if the
+ * algo/provider combination cannot be found. If provider is
+ * empty then best available is chosen.
+ */
+ static std::unique_ptr<BlockCipher>
+ create_or_throw(const std::string& algo_spec,
+ const std::string& provider = "");
+
+ /**
+ * @return list of available providers for this algorithm, empty if not available
+ * @param algo_spec algorithm name
+ */
+ static std::vector<std::string> providers(const std::string& algo_spec);
+
+ /**
+ * @return block size of this algorithm
+ */
+ virtual size_t block_size() const = 0;
+
+ /**
+ * @return native parallelism of this cipher in blocks
+ */
+ virtual size_t parallelism() const { return 1; }
+
+ /**
+ * @return prefererred parallelism of this cipher in bytes
+ */
+ size_t parallel_bytes() const
+ {
+ return parallelism() * block_size() * BOTAN_BLOCK_CIPHER_PAR_MULT;
+ }
+
+ /**
+ * @return provider information about this implementation. Default is "base",
+ * might also return "sse2", "avx2", "openssl", or some other arbitrary string.
+ */
+ virtual std::string provider() const { return "base"; }
+
+ /**
+ * Encrypt a block.
+ * @param in The plaintext block to be encrypted as a byte array.
+ * Must be of length block_size().
+ * @param out The byte array designated to hold the encrypted block.
+ * Must be of length block_size().
+ */
+ void encrypt(const uint8_t in[], uint8_t out[]) const
+ { encrypt_n(in, out, 1); }
+
+ /**
+ * Decrypt a block.
+ * @param in The ciphertext block to be decypted 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().
+ */
+ void decrypt(const uint8_t in[], uint8_t out[]) const
+ { decrypt_n(in, out, 1); }
+
+ /**
+ * Encrypt a block.
+ * @param block the plaintext block to be encrypted
+ * Must be of length block_size(). Will hold the result when the function
+ * has finished.
+ */
+ void encrypt(uint8_t block[]) const { encrypt_n(block, block, 1); }
+
+ /**
+ * Decrypt a block.
+ * @param block the ciphertext block to be decrypted
+ * Must be of length block_size(). Will hold the result when the function
+ * has finished.
+ */
+ void decrypt(uint8_t block[]) const { decrypt_n(block, block, 1); }
+
+ /**
+ * Encrypt one or more blocks
+ * @param block the input/output buffer (multiple of block_size())
+ */
+ template<typename Alloc>
+ void encrypt(std::vector<uint8_t, Alloc>& block) const
+ {
+ return encrypt_n(block.data(), block.data(), block.size() / block_size());
+ }
+
+ /**
+ * Decrypt one or more blocks
+ * @param block the input/output buffer (multiple of block_size())
+ */
+ template<typename Alloc>
+ void decrypt(std::vector<uint8_t, Alloc>& block) const
+ {
+ return decrypt_n(block.data(), block.data(), block.size() / block_size());
+ }
+
+ /**
+ * Encrypt one or more blocks
+ * @param in the input buffer (multiple of block_size())
+ * @param out the output buffer (same size as in)
+ */
+ template<typename Alloc, typename Alloc2>
+ void encrypt(const std::vector<uint8_t, Alloc>& in,
+ std::vector<uint8_t, Alloc2>& out) const
+ {
+ return encrypt_n(in.data(), out.data(), in.size() / block_size());
+ }
+
+ /**
+ * Decrypt one or more blocks
+ * @param in the input buffer (multiple of block_size())
+ * @param out the output buffer (same size as in)
+ */
+ template<typename Alloc, typename Alloc2>
+ void decrypt(const std::vector<uint8_t, Alloc>& in,
+ std::vector<uint8_t, Alloc2>& out) const
+ {
+ return decrypt_n(in.data(), out.data(), in.size() / block_size());
+ }
+
+ /**
+ * Encrypt one or more blocks
+ * @param in the input buffer (multiple of block_size())
+ * @param out the output buffer (same size as in)
+ * @param blocks the number of blocks to process
+ */
+ virtual void encrypt_n(const uint8_t in[], uint8_t out[],
+ size_t blocks) const = 0;
+
+ /**
+ * Decrypt one or more blocks
+ * @param in the input buffer (multiple of block_size())
+ * @param out the output buffer (same size as in)
+ * @param blocks the number of blocks to process
+ */
+ 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
+ {
+ const size_t BS = block_size();
+ xor_buf(data, mask, blocks * BS);
+ encrypt_n(data, data, blocks);
+ xor_buf(data, mask, blocks * BS);
+ }
+
+ virtual 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);
+ decrypt_n(data, data, blocks);
+ xor_buf(data, mask, blocks * BS);
+ }
+
+ /**
+ * @return new object representing the same algorithm as *this
+ */
+ virtual BlockCipher* clone() const = 0;
+
+ virtual ~BlockCipher() = default;
+ };
+
+/**
+* Tweakable block ciphers allow setting a tweak which is a non-keyed
+* value which affects the encryption/decryption operation.
+*/
+class BOTAN_PUBLIC_API(2,8) Tweakable_Block_Cipher : public BlockCipher
+ {
+ public:
+ /**
+ * Set the tweak value. This must be called after setting a key. The value
+ * persists until either set_tweak, set_key, or clear is called.
+ * Different algorithms support different tweak length(s). If called with
+ * an unsupported length, Invalid_Argument will be thrown.
+ */
+ virtual void set_tweak(const uint8_t tweak[], size_t len) = 0;
+ };
+
+/**
+* Represents a block cipher with a single fixed block size
+*/
+template<size_t BS, size_t KMIN, size_t KMAX = 0, size_t KMOD = 1, typename BaseClass = BlockCipher>
+class Block_Cipher_Fixed_Params : public BaseClass
+ {
+ public:
+ enum { BLOCK_SIZE = BS };
+ size_t block_size() const final override { return BS; }
+
+ // override to take advantage of compile time constant block size
+ void encrypt_n_xex(uint8_t data[],
+ const uint8_t mask[],
+ size_t blocks) const final override
+ {
+ xor_buf(data, mask, blocks * BS);
+ this->encrypt_n(data, data, blocks);
+ xor_buf(data, mask, blocks * BS);
+ }
+
+ void decrypt_n_xex(uint8_t data[],
+ const uint8_t mask[],
+ size_t blocks) const final override
+ {
+ xor_buf(data, mask, blocks * BS);
+ this->decrypt_n(data, data, blocks);
+ xor_buf(data, mask, blocks * BS);
+ }
+
+ Key_Length_Specification key_spec() const final override
+ {
+ return Key_Length_Specification(KMIN, KMAX, KMOD);
+ }
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/block/blowfish/blowfish.cpp b/comm/third_party/botan/src/lib/block/blowfish/blowfish.cpp
new file mode 100644
index 0000000000..ecb9f82e36
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/blowfish/blowfish.cpp
@@ -0,0 +1,456 @@
+/*
+* Blowfish
+* (C) 1999-2011,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/blowfish.h>
+#include <botan/loadstor.h>
+
+namespace Botan {
+
+namespace {
+
+const uint32_t P_INIT[18] = {
+ 0x243F6A88, 0x85A308D3, 0x13198A2E, 0x03707344, 0xA4093822, 0x299F31D0,
+ 0x082EFA98, 0xEC4E6C89, 0x452821E6, 0x38D01377, 0xBE5466CF, 0x34E90C6C,
+ 0xC0AC29B7, 0xC97C50DD, 0x3F84D5B5, 0xB5470917, 0x9216D5D9, 0x8979FB1B };
+
+const uint32_t S_INIT[1024] = {
+ 0xD1310BA6, 0x98DFB5AC, 0x2FFD72DB, 0xD01ADFB7, 0xB8E1AFED, 0x6A267E96,
+ 0xBA7C9045, 0xF12C7F99, 0x24A19947, 0xB3916CF7, 0x0801F2E2, 0x858EFC16,
+ 0x636920D8, 0x71574E69, 0xA458FEA3, 0xF4933D7E, 0x0D95748F, 0x728EB658,
+ 0x718BCD58, 0x82154AEE, 0x7B54A41D, 0xC25A59B5, 0x9C30D539, 0x2AF26013,
+ 0xC5D1B023, 0x286085F0, 0xCA417918, 0xB8DB38EF, 0x8E79DCB0, 0x603A180E,
+ 0x6C9E0E8B, 0xB01E8A3E, 0xD71577C1, 0xBD314B27, 0x78AF2FDA, 0x55605C60,
+ 0xE65525F3, 0xAA55AB94, 0x57489862, 0x63E81440, 0x55CA396A, 0x2AAB10B6,
+ 0xB4CC5C34, 0x1141E8CE, 0xA15486AF, 0x7C72E993, 0xB3EE1411, 0x636FBC2A,
+ 0x2BA9C55D, 0x741831F6, 0xCE5C3E16, 0x9B87931E, 0xAFD6BA33, 0x6C24CF5C,
+ 0x7A325381, 0x28958677, 0x3B8F4898, 0x6B4BB9AF, 0xC4BFE81B, 0x66282193,
+ 0x61D809CC, 0xFB21A991, 0x487CAC60, 0x5DEC8032, 0xEF845D5D, 0xE98575B1,
+ 0xDC262302, 0xEB651B88, 0x23893E81, 0xD396ACC5, 0x0F6D6FF3, 0x83F44239,
+ 0x2E0B4482, 0xA4842004, 0x69C8F04A, 0x9E1F9B5E, 0x21C66842, 0xF6E96C9A,
+ 0x670C9C61, 0xABD388F0, 0x6A51A0D2, 0xD8542F68, 0x960FA728, 0xAB5133A3,
+ 0x6EEF0B6C, 0x137A3BE4, 0xBA3BF050, 0x7EFB2A98, 0xA1F1651D, 0x39AF0176,
+ 0x66CA593E, 0x82430E88, 0x8CEE8619, 0x456F9FB4, 0x7D84A5C3, 0x3B8B5EBE,
+ 0xE06F75D8, 0x85C12073, 0x401A449F, 0x56C16AA6, 0x4ED3AA62, 0x363F7706,
+ 0x1BFEDF72, 0x429B023D, 0x37D0D724, 0xD00A1248, 0xDB0FEAD3, 0x49F1C09B,
+ 0x075372C9, 0x80991B7B, 0x25D479D8, 0xF6E8DEF7, 0xE3FE501A, 0xB6794C3B,
+ 0x976CE0BD, 0x04C006BA, 0xC1A94FB6, 0x409F60C4, 0x5E5C9EC2, 0x196A2463,
+ 0x68FB6FAF, 0x3E6C53B5, 0x1339B2EB, 0x3B52EC6F, 0x6DFC511F, 0x9B30952C,
+ 0xCC814544, 0xAF5EBD09, 0xBEE3D004, 0xDE334AFD, 0x660F2807, 0x192E4BB3,
+ 0xC0CBA857, 0x45C8740F, 0xD20B5F39, 0xB9D3FBDB, 0x5579C0BD, 0x1A60320A,
+ 0xD6A100C6, 0x402C7279, 0x679F25FE, 0xFB1FA3CC, 0x8EA5E9F8, 0xDB3222F8,
+ 0x3C7516DF, 0xFD616B15, 0x2F501EC8, 0xAD0552AB, 0x323DB5FA, 0xFD238760,
+ 0x53317B48, 0x3E00DF82, 0x9E5C57BB, 0xCA6F8CA0, 0x1A87562E, 0xDF1769DB,
+ 0xD542A8F6, 0x287EFFC3, 0xAC6732C6, 0x8C4F5573, 0x695B27B0, 0xBBCA58C8,
+ 0xE1FFA35D, 0xB8F011A0, 0x10FA3D98, 0xFD2183B8, 0x4AFCB56C, 0x2DD1D35B,
+ 0x9A53E479, 0xB6F84565, 0xD28E49BC, 0x4BFB9790, 0xE1DDF2DA, 0xA4CB7E33,
+ 0x62FB1341, 0xCEE4C6E8, 0xEF20CADA, 0x36774C01, 0xD07E9EFE, 0x2BF11FB4,
+ 0x95DBDA4D, 0xAE909198, 0xEAAD8E71, 0x6B93D5A0, 0xD08ED1D0, 0xAFC725E0,
+ 0x8E3C5B2F, 0x8E7594B7, 0x8FF6E2FB, 0xF2122B64, 0x8888B812, 0x900DF01C,
+ 0x4FAD5EA0, 0x688FC31C, 0xD1CFF191, 0xB3A8C1AD, 0x2F2F2218, 0xBE0E1777,
+ 0xEA752DFE, 0x8B021FA1, 0xE5A0CC0F, 0xB56F74E8, 0x18ACF3D6, 0xCE89E299,
+ 0xB4A84FE0, 0xFD13E0B7, 0x7CC43B81, 0xD2ADA8D9, 0x165FA266, 0x80957705,
+ 0x93CC7314, 0x211A1477, 0xE6AD2065, 0x77B5FA86, 0xC75442F5, 0xFB9D35CF,
+ 0xEBCDAF0C, 0x7B3E89A0, 0xD6411BD3, 0xAE1E7E49, 0x00250E2D, 0x2071B35E,
+ 0x226800BB, 0x57B8E0AF, 0x2464369B, 0xF009B91E, 0x5563911D, 0x59DFA6AA,
+ 0x78C14389, 0xD95A537F, 0x207D5BA2, 0x02E5B9C5, 0x83260376, 0x6295CFA9,
+ 0x11C81968, 0x4E734A41, 0xB3472DCA, 0x7B14A94A, 0x1B510052, 0x9A532915,
+ 0xD60F573F, 0xBC9BC6E4, 0x2B60A476, 0x81E67400, 0x08BA6FB5, 0x571BE91F,
+ 0xF296EC6B, 0x2A0DD915, 0xB6636521, 0xE7B9F9B6, 0xFF34052E, 0xC5855664,
+ 0x53B02D5D, 0xA99F8FA1, 0x08BA4799, 0x6E85076A, 0x4B7A70E9, 0xB5B32944,
+ 0xDB75092E, 0xC4192623, 0xAD6EA6B0, 0x49A7DF7D, 0x9CEE60B8, 0x8FEDB266,
+ 0xECAA8C71, 0x699A17FF, 0x5664526C, 0xC2B19EE1, 0x193602A5, 0x75094C29,
+ 0xA0591340, 0xE4183A3E, 0x3F54989A, 0x5B429D65, 0x6B8FE4D6, 0x99F73FD6,
+ 0xA1D29C07, 0xEFE830F5, 0x4D2D38E6, 0xF0255DC1, 0x4CDD2086, 0x8470EB26,
+ 0x6382E9C6, 0x021ECC5E, 0x09686B3F, 0x3EBAEFC9, 0x3C971814, 0x6B6A70A1,
+ 0x687F3584, 0x52A0E286, 0xB79C5305, 0xAA500737, 0x3E07841C, 0x7FDEAE5C,
+ 0x8E7D44EC, 0x5716F2B8, 0xB03ADA37, 0xF0500C0D, 0xF01C1F04, 0x0200B3FF,
+ 0xAE0CF51A, 0x3CB574B2, 0x25837A58, 0xDC0921BD, 0xD19113F9, 0x7CA92FF6,
+ 0x94324773, 0x22F54701, 0x3AE5E581, 0x37C2DADC, 0xC8B57634, 0x9AF3DDA7,
+ 0xA9446146, 0x0FD0030E, 0xECC8C73E, 0xA4751E41, 0xE238CD99, 0x3BEA0E2F,
+ 0x3280BBA1, 0x183EB331, 0x4E548B38, 0x4F6DB908, 0x6F420D03, 0xF60A04BF,
+ 0x2CB81290, 0x24977C79, 0x5679B072, 0xBCAF89AF, 0xDE9A771F, 0xD9930810,
+ 0xB38BAE12, 0xDCCF3F2E, 0x5512721F, 0x2E6B7124, 0x501ADDE6, 0x9F84CD87,
+ 0x7A584718, 0x7408DA17, 0xBC9F9ABC, 0xE94B7D8C, 0xEC7AEC3A, 0xDB851DFA,
+ 0x63094366, 0xC464C3D2, 0xEF1C1847, 0x3215D908, 0xDD433B37, 0x24C2BA16,
+ 0x12A14D43, 0x2A65C451, 0x50940002, 0x133AE4DD, 0x71DFF89E, 0x10314E55,
+ 0x81AC77D6, 0x5F11199B, 0x043556F1, 0xD7A3C76B, 0x3C11183B, 0x5924A509,
+ 0xF28FE6ED, 0x97F1FBFA, 0x9EBABF2C, 0x1E153C6E, 0x86E34570, 0xEAE96FB1,
+ 0x860E5E0A, 0x5A3E2AB3, 0x771FE71C, 0x4E3D06FA, 0x2965DCB9, 0x99E71D0F,
+ 0x803E89D6, 0x5266C825, 0x2E4CC978, 0x9C10B36A, 0xC6150EBA, 0x94E2EA78,
+ 0xA5FC3C53, 0x1E0A2DF4, 0xF2F74EA7, 0x361D2B3D, 0x1939260F, 0x19C27960,
+ 0x5223A708, 0xF71312B6, 0xEBADFE6E, 0xEAC31F66, 0xE3BC4595, 0xA67BC883,
+ 0xB17F37D1, 0x018CFF28, 0xC332DDEF, 0xBE6C5AA5, 0x65582185, 0x68AB9802,
+ 0xEECEA50F, 0xDB2F953B, 0x2AEF7DAD, 0x5B6E2F84, 0x1521B628, 0x29076170,
+ 0xECDD4775, 0x619F1510, 0x13CCA830, 0xEB61BD96, 0x0334FE1E, 0xAA0363CF,
+ 0xB5735C90, 0x4C70A239, 0xD59E9E0B, 0xCBAADE14, 0xEECC86BC, 0x60622CA7,
+ 0x9CAB5CAB, 0xB2F3846E, 0x648B1EAF, 0x19BDF0CA, 0xA02369B9, 0x655ABB50,
+ 0x40685A32, 0x3C2AB4B3, 0x319EE9D5, 0xC021B8F7, 0x9B540B19, 0x875FA099,
+ 0x95F7997E, 0x623D7DA8, 0xF837889A, 0x97E32D77, 0x11ED935F, 0x16681281,
+ 0x0E358829, 0xC7E61FD6, 0x96DEDFA1, 0x7858BA99, 0x57F584A5, 0x1B227263,
+ 0x9B83C3FF, 0x1AC24696, 0xCDB30AEB, 0x532E3054, 0x8FD948E4, 0x6DBC3128,
+ 0x58EBF2EF, 0x34C6FFEA, 0xFE28ED61, 0xEE7C3C73, 0x5D4A14D9, 0xE864B7E3,
+ 0x42105D14, 0x203E13E0, 0x45EEE2B6, 0xA3AAABEA, 0xDB6C4F15, 0xFACB4FD0,
+ 0xC742F442, 0xEF6ABBB5, 0x654F3B1D, 0x41CD2105, 0xD81E799E, 0x86854DC7,
+ 0xE44B476A, 0x3D816250, 0xCF62A1F2, 0x5B8D2646, 0xFC8883A0, 0xC1C7B6A3,
+ 0x7F1524C3, 0x69CB7492, 0x47848A0B, 0x5692B285, 0x095BBF00, 0xAD19489D,
+ 0x1462B174, 0x23820E00, 0x58428D2A, 0x0C55F5EA, 0x1DADF43E, 0x233F7061,
+ 0x3372F092, 0x8D937E41, 0xD65FECF1, 0x6C223BDB, 0x7CDE3759, 0xCBEE7460,
+ 0x4085F2A7, 0xCE77326E, 0xA6078084, 0x19F8509E, 0xE8EFD855, 0x61D99735,
+ 0xA969A7AA, 0xC50C06C2, 0x5A04ABFC, 0x800BCADC, 0x9E447A2E, 0xC3453484,
+ 0xFDD56705, 0x0E1E9EC9, 0xDB73DBD3, 0x105588CD, 0x675FDA79, 0xE3674340,
+ 0xC5C43465, 0x713E38D8, 0x3D28F89E, 0xF16DFF20, 0x153E21E7, 0x8FB03D4A,
+ 0xE6E39F2B, 0xDB83ADF7, 0xE93D5A68, 0x948140F7, 0xF64C261C, 0x94692934,
+ 0x411520F7, 0x7602D4F7, 0xBCF46B2E, 0xD4A20068, 0xD4082471, 0x3320F46A,
+ 0x43B7D4B7, 0x500061AF, 0x1E39F62E, 0x97244546, 0x14214F74, 0xBF8B8840,
+ 0x4D95FC1D, 0x96B591AF, 0x70F4DDD3, 0x66A02F45, 0xBFBC09EC, 0x03BD9785,
+ 0x7FAC6DD0, 0x31CB8504, 0x96EB27B3, 0x55FD3941, 0xDA2547E6, 0xABCA0A9A,
+ 0x28507825, 0x530429F4, 0x0A2C86DA, 0xE9B66DFB, 0x68DC1462, 0xD7486900,
+ 0x680EC0A4, 0x27A18DEE, 0x4F3FFEA2, 0xE887AD8C, 0xB58CE006, 0x7AF4D6B6,
+ 0xAACE1E7C, 0xD3375FEC, 0xCE78A399, 0x406B2A42, 0x20FE9E35, 0xD9F385B9,
+ 0xEE39D7AB, 0x3B124E8B, 0x1DC9FAF7, 0x4B6D1856, 0x26A36631, 0xEAE397B2,
+ 0x3A6EFA74, 0xDD5B4332, 0x6841E7F7, 0xCA7820FB, 0xFB0AF54E, 0xD8FEB397,
+ 0x454056AC, 0xBA489527, 0x55533A3A, 0x20838D87, 0xFE6BA9B7, 0xD096954B,
+ 0x55A867BC, 0xA1159A58, 0xCCA92963, 0x99E1DB33, 0xA62A4A56, 0x3F3125F9,
+ 0x5EF47E1C, 0x9029317C, 0xFDF8E802, 0x04272F70, 0x80BB155C, 0x05282CE3,
+ 0x95C11548, 0xE4C66D22, 0x48C1133F, 0xC70F86DC, 0x07F9C9EE, 0x41041F0F,
+ 0x404779A4, 0x5D886E17, 0x325F51EB, 0xD59BC0D1, 0xF2BCC18F, 0x41113564,
+ 0x257B7834, 0x602A9C60, 0xDFF8E8A3, 0x1F636C1B, 0x0E12B4C2, 0x02E1329E,
+ 0xAF664FD1, 0xCAD18115, 0x6B2395E0, 0x333E92E1, 0x3B240B62, 0xEEBEB922,
+ 0x85B2A20E, 0xE6BA0D99, 0xDE720C8C, 0x2DA2F728, 0xD0127845, 0x95B794FD,
+ 0x647D0862, 0xE7CCF5F0, 0x5449A36F, 0x877D48FA, 0xC39DFD27, 0xF33E8D1E,
+ 0x0A476341, 0x992EFF74, 0x3A6F6EAB, 0xF4F8FD37, 0xA812DC60, 0xA1EBDDF8,
+ 0x991BE14C, 0xDB6E6B0D, 0xC67B5510, 0x6D672C37, 0x2765D43B, 0xDCD0E804,
+ 0xF1290DC7, 0xCC00FFA3, 0xB5390F92, 0x690FED0B, 0x667B9FFB, 0xCEDB7D9C,
+ 0xA091CF0B, 0xD9155EA3, 0xBB132F88, 0x515BAD24, 0x7B9479BF, 0x763BD6EB,
+ 0x37392EB3, 0xCC115979, 0x8026E297, 0xF42E312D, 0x6842ADA7, 0xC66A2B3B,
+ 0x12754CCC, 0x782EF11C, 0x6A124237, 0xB79251E7, 0x06A1BBE6, 0x4BFB6350,
+ 0x1A6B1018, 0x11CAEDFA, 0x3D25BDD8, 0xE2E1C3C9, 0x44421659, 0x0A121386,
+ 0xD90CEC6E, 0xD5ABEA2A, 0x64AF674E, 0xDA86A85F, 0xBEBFE988, 0x64E4C3FE,
+ 0x9DBC8057, 0xF0F7C086, 0x60787BF8, 0x6003604D, 0xD1FD8346, 0xF6381FB0,
+ 0x7745AE04, 0xD736FCCC, 0x83426B33, 0xF01EAB71, 0xB0804187, 0x3C005E5F,
+ 0x77A057BE, 0xBDE8AE24, 0x55464299, 0xBF582E61, 0x4E58F48F, 0xF2DDFDA2,
+ 0xF474EF38, 0x8789BDC2, 0x5366F9C3, 0xC8B38E74, 0xB475F255, 0x46FCD9B9,
+ 0x7AEB2661, 0x8B1DDF84, 0x846A0E79, 0x915F95E2, 0x466E598E, 0x20B45770,
+ 0x8CD55591, 0xC902DE4C, 0xB90BACE1, 0xBB8205D0, 0x11A86248, 0x7574A99E,
+ 0xB77F19B6, 0xE0A9DC09, 0x662D09A1, 0xC4324633, 0xE85A1F02, 0x09F0BE8C,
+ 0x4A99A025, 0x1D6EFE10, 0x1AB93D1D, 0x0BA5A4DF, 0xA186F20F, 0x2868F169,
+ 0xDCB7DA83, 0x573906FE, 0xA1E2CE9B, 0x4FCD7F52, 0x50115E01, 0xA70683FA,
+ 0xA002B5C4, 0x0DE6D027, 0x9AF88C27, 0x773F8641, 0xC3604C06, 0x61A806B5,
+ 0xF0177A28, 0xC0F586E0, 0x006058AA, 0x30DC7D62, 0x11E69ED7, 0x2338EA63,
+ 0x53C2DD94, 0xC2C21634, 0xBBCBEE56, 0x90BCB6DE, 0xEBFC7DA1, 0xCE591D76,
+ 0x6F05E409, 0x4B7C0188, 0x39720A3D, 0x7C927C24, 0x86E3725F, 0x724D9DB9,
+ 0x1AC15BB4, 0xD39EB8FC, 0xED545578, 0x08FCA5B5, 0xD83D7CD3, 0x4DAD0FC4,
+ 0x1E50EF5E, 0xB161E6F8, 0xA28514D9, 0x6C51133C, 0x6FD5C7E7, 0x56E14EC4,
+ 0x362ABFCE, 0xDDC6C837, 0xD79A3234, 0x92638212, 0x670EFA8E, 0x406000E0,
+ 0x3A39CE37, 0xD3FAF5CF, 0xABC27737, 0x5AC52D1B, 0x5CB0679E, 0x4FA33742,
+ 0xD3822740, 0x99BC9BBE, 0xD5118E9D, 0xBF0F7315, 0xD62D1C7E, 0xC700C47B,
+ 0xB78C1B6B, 0x21A19045, 0xB26EB1BE, 0x6A366EB4, 0x5748AB2F, 0xBC946E79,
+ 0xC6A376D2, 0x6549C2C8, 0x530FF8EE, 0x468DDE7D, 0xD5730A1D, 0x4CD04DC6,
+ 0x2939BBDB, 0xA9BA4650, 0xAC9526E8, 0xBE5EE304, 0xA1FAD5F0, 0x6A2D519A,
+ 0x63EF8CE2, 0x9A86EE22, 0xC089C2B8, 0x43242EF6, 0xA51E03AA, 0x9CF2D0A4,
+ 0x83C061BA, 0x9BE96A4D, 0x8FE51550, 0xBA645BD6, 0x2826A2F9, 0xA73A3AE1,
+ 0x4BA99586, 0xEF5562E9, 0xC72FEFD3, 0xF752F7DA, 0x3F046F69, 0x77FA0A59,
+ 0x80E4A915, 0x87B08601, 0x9B09E6AD, 0x3B3EE593, 0xE990FD5A, 0x9E34D797,
+ 0x2CF0B7D9, 0x022B8B51, 0x96D5AC3A, 0x017DA67D, 0xD1CF3ED6, 0x7C7D2D28,
+ 0x1F9F25CF, 0xADF2B89B, 0x5AD6B472, 0x5A88F54C, 0xE029AC71, 0xE019A5E6,
+ 0x47B0ACFD, 0xED93FA9B, 0xE8D3C48D, 0x283B57CC, 0xF8D56629, 0x79132E28,
+ 0x785F0191, 0xED756055, 0xF7960E44, 0xE3D35E8C, 0x15056DD4, 0x88F46DBA,
+ 0x03A16125, 0x0564F0BD, 0xC3EB9E15, 0x3C9057A2, 0x97271AEC, 0xA93A072A,
+ 0x1B3F6D9B, 0x1E6321F5, 0xF59C66FB, 0x26DCF319, 0x7533D928, 0xB155FDF5,
+ 0x03563482, 0x8ABA3CBB, 0x28517711, 0xC20AD9F8, 0xABCC5167, 0xCCAD925F,
+ 0x4DE81751, 0x3830DC8E, 0x379D5862, 0x9320F991, 0xEA7A90C2, 0xFB3E7BCE,
+ 0x5121CE64, 0x774FBE32, 0xA8B6E37E, 0xC3293D46, 0x48DE5369, 0x6413E680,
+ 0xA2AE0810, 0xDD6DB224, 0x69852DFD, 0x09072166, 0xB39A460A, 0x6445C0DD,
+ 0x586CDECF, 0x1C20C8AE, 0x5BBEF7DD, 0x1B588D40, 0xCCD2017F, 0x6BB4E3BB,
+ 0xDDA26A7E, 0x3A59FF45, 0x3E350A44, 0xBCB4CDD5, 0x72EACEA8, 0xFA6484BB,
+ 0x8D6612AE, 0xBF3C6F47, 0xD29BE463, 0x542F5D9E, 0xAEC2771B, 0xF64E6370,
+ 0x740E0D8D, 0xE75B1357, 0xF8721671, 0xAF537D5D, 0x4040CB08, 0x4EB4E2CC,
+ 0x34D2466A, 0x0115AF84, 0xE1B00428, 0x95983A1D, 0x06B89FB4, 0xCE6EA048,
+ 0x6F3F3B82, 0x3520AB82, 0x011A1D4B, 0x277227F8, 0x611560B1, 0xE7933FDC,
+ 0xBB3A792B, 0x344525BD, 0xA08839E1, 0x51CE794B, 0x2F32C9B7, 0xA01FBAC9,
+ 0xE01CC87E, 0xBCC7D1F6, 0xCF0111C3, 0xA1E8AAC7, 0x1A908749, 0xD44FBD9A,
+ 0xD0DADECB, 0xD50ADA38, 0x0339C32A, 0xC6913667, 0x8DF9317C, 0xE0B12B4F,
+ 0xF79E59B7, 0x43F5BB3A, 0xF2D519FF, 0x27D9459C, 0xBF97222C, 0x15E6FC2A,
+ 0x0F91FC71, 0x9B941525, 0xFAE59361, 0xCEB69CEB, 0xC2A86459, 0x12BAA8D1,
+ 0xB6C1075E, 0xE3056A0C, 0x10D25065, 0xCB03A442, 0xE0EC6E0E, 0x1698DB3B,
+ 0x4C98A0BE, 0x3278E964, 0x9F1F9532, 0xE0D392DF, 0xD3A0342B, 0x8971F21E,
+ 0x1B0A7441, 0x4BA3348C, 0xC5BE7120, 0xC37632D8, 0xDF359F8D, 0x9B992F2E,
+ 0xE60B6F47, 0x0FE3F11D, 0xE54CDA54, 0x1EDAD891, 0xCE6279CF, 0xCD3E7E6F,
+ 0x1618B166, 0xFD2C1D05, 0x848FD2C5, 0xF6FB2299, 0xF523F357, 0xA6327623,
+ 0x93A83531, 0x56CCCD02, 0xACF08162, 0x5A75EBB5, 0x6E163697, 0x88D273CC,
+ 0xDE966292, 0x81B949D0, 0x4C50901B, 0x71C65614, 0xE6C6C7BD, 0x327A140A,
+ 0x45E1D006, 0xC3F27B9A, 0xC9AA53FD, 0x62A80F00, 0xBB25BFE2, 0x35BDD2F6,
+ 0x71126905, 0xB2040222, 0xB6CBCF7C, 0xCD769C2B, 0x53113EC0, 0x1640E3D3,
+ 0x38ABBD60, 0x2547ADF0, 0xBA38209C, 0xF746CE76, 0x77AFA1C5, 0x20756060,
+ 0x85CBFE4E, 0x8AE88DD8, 0x7AAAF9B0, 0x4CF9AA7E, 0x1948C25C, 0x02FB8A8C,
+ 0x01C36AE4, 0xD6EBE1F9, 0x90D4F869, 0xA65CDEA0, 0x3F09252D, 0xC208E69F,
+ 0xB74E6132, 0xCE77E25B, 0x578FDFE3, 0x3AC372E6 };
+
+inline uint32_t BFF(uint32_t X, const secure_vector<uint32_t>& S)
+ {
+ return ((S[ get_byte(0, X)] + S[256+get_byte(1, X)]) ^
+ S[512+get_byte(2, X)]) + S[768+get_byte(3, X)];
+ }
+
+}
+
+/*
+* Blowfish Encryption
+*/
+void Blowfish::encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+ {
+ verify_key_set(m_S.empty() == false);
+
+ while(blocks >= 4)
+ {
+ uint32_t L0, R0, L1, R1, L2, R2, L3, R3;
+ load_be(in, L0, R0, L1, R1, L2, R2, L3, R3);
+
+ for(size_t r = 0; r != 16; r += 2)
+ {
+ L0 ^= m_P[r];
+ L1 ^= m_P[r];
+ L2 ^= m_P[r];
+ L3 ^= m_P[r];
+ R0 ^= BFF(L0, m_S);
+ R1 ^= BFF(L1, m_S);
+ R2 ^= BFF(L2, m_S);
+ R3 ^= BFF(L3, m_S);
+
+ R0 ^= m_P[r+1];
+ R1 ^= m_P[r+1];
+ R2 ^= m_P[r+1];
+ R3 ^= m_P[r+1];
+ L0 ^= BFF(R0, m_S);
+ L1 ^= BFF(R1, m_S);
+ L2 ^= BFF(R2, m_S);
+ L3 ^= BFF(R3, m_S);
+ }
+
+ L0 ^= m_P[16]; R0 ^= m_P[17];
+ L1 ^= m_P[16]; R1 ^= m_P[17];
+ L2 ^= m_P[16]; R2 ^= m_P[17];
+ L3 ^= m_P[16]; R3 ^= m_P[17];
+
+ store_be(out, R0, L0, R1, L1, R2, L2, R3, L3);
+
+ in += 4*BLOCK_SIZE;
+ out += 4*BLOCK_SIZE;
+ blocks -= 4;
+ }
+
+ while(blocks)
+ {
+ uint32_t L, R;
+ load_be(in, L, R);
+
+ for(size_t r = 0; r != 16; r += 2)
+ {
+ L ^= m_P[r];
+ R ^= BFF(L, m_S);
+
+ R ^= m_P[r+1];
+ L ^= BFF(R, m_S);
+ }
+
+ L ^= m_P[16]; R ^= m_P[17];
+
+ store_be(out, R, L);
+
+ in += BLOCK_SIZE;
+ out += BLOCK_SIZE;
+ blocks--;
+ }
+ }
+
+/*
+* Blowfish Decryption
+*/
+void Blowfish::decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+ {
+ verify_key_set(m_S.empty() == false);
+
+ while(blocks >= 4)
+ {
+ uint32_t L0, R0, L1, R1, L2, R2, L3, R3;
+ load_be(in, L0, R0, L1, R1, L2, R2, L3, R3);
+
+ for(size_t r = 17; r != 1; r -= 2)
+ {
+ L0 ^= m_P[r];
+ L1 ^= m_P[r];
+ L2 ^= m_P[r];
+ L3 ^= m_P[r];
+ R0 ^= BFF(L0, m_S);
+ R1 ^= BFF(L1, m_S);
+ R2 ^= BFF(L2, m_S);
+ R3 ^= BFF(L3, m_S);
+
+ R0 ^= m_P[r-1];
+ R1 ^= m_P[r-1];
+ R2 ^= m_P[r-1];
+ R3 ^= m_P[r-1];
+
+ L0 ^= BFF(R0, m_S);
+ L1 ^= BFF(R1, m_S);
+ L2 ^= BFF(R2, m_S);
+ L3 ^= BFF(R3, m_S);
+ }
+
+ L0 ^= m_P[1]; R0 ^= m_P[0];
+ L1 ^= m_P[1]; R1 ^= m_P[0];
+ L2 ^= m_P[1]; R2 ^= m_P[0];
+ L3 ^= m_P[1]; R3 ^= m_P[0];
+
+ store_be(out, R0, L0, R1, L1, R2, L2, R3, L3);
+
+ in += 4*BLOCK_SIZE;
+ out += 4*BLOCK_SIZE;
+ blocks -= 4;
+ }
+
+ while(blocks)
+ {
+ uint32_t L, R;
+ load_be(in, L, R);
+
+ for(size_t r = 17; r != 1; r -= 2)
+ {
+ L ^= m_P[r];
+ R ^= BFF(L, m_S);
+
+ R ^= m_P[r-1];
+ L ^= BFF(R, m_S);
+ }
+
+ L ^= m_P[1]; R ^= m_P[0];
+
+ store_be(out, R, L);
+
+ in += BLOCK_SIZE;
+ out += BLOCK_SIZE;
+ blocks--;
+ }
+ }
+
+/*
+* Blowfish Key Schedule
+*/
+void Blowfish::key_schedule(const uint8_t key[], size_t length)
+ {
+ m_P.resize(18);
+ copy_mem(m_P.data(), P_INIT, 18);
+
+ m_S.resize(1024);
+ copy_mem(m_S.data(), S_INIT, 1024);
+
+ key_expansion(key, length, nullptr, 0);
+ }
+
+void Blowfish::key_expansion(const uint8_t key[],
+ size_t length,
+ const uint8_t salt[],
+ size_t salt_length)
+ {
+ BOTAN_ASSERT_NOMSG(salt_length % 4 == 0);
+
+ for(size_t i = 0, j = 0; i != 18; ++i, j += 4)
+ m_P[i] ^= make_uint32(key[(j ) % length], key[(j+1) % length],
+ key[(j+2) % length], key[(j+3) % length]);
+
+ const size_t P_salt_offset = (salt_length > 0) ? 18 % (salt_length / 4) : 0;
+
+ uint32_t L = 0, R = 0;
+ generate_sbox(m_P, L, R, salt, salt_length, 0);
+ generate_sbox(m_S, L, R, salt, salt_length, P_salt_offset);
+ }
+
+/*
+* Modified key schedule used for bcrypt password hashing
+*/
+void Blowfish::salted_set_key(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;
+ }
+
+ m_P.resize(18);
+ copy_mem(m_P.data(), P_INIT, 18);
+
+ m_S.resize(1024);
+ copy_mem(m_S.data(), S_INIT, 1024);
+ key_expansion(key, length, salt, salt_length);
+
+ if(workfactor > 0)
+ {
+ const size_t rounds = static_cast<size_t>(1) << workfactor;
+
+ for(size_t r = 0; r != rounds; ++r)
+ {
+ if(salt_first)
+ {
+ key_expansion(salt, salt_length, nullptr, 0);
+ key_expansion(key, length, nullptr, 0);
+ }
+ else
+ {
+ key_expansion(key, length, nullptr, 0);
+ key_expansion(salt, salt_length, nullptr, 0);
+ }
+ }
+ }
+ }
+
+/*
+* Generate one of the Sboxes
+*/
+void Blowfish::generate_sbox(secure_vector<uint32_t>& box,
+ uint32_t& L, uint32_t& R,
+ const uint8_t salt[],
+ size_t salt_length,
+ size_t salt_off) const
+ {
+ for(size_t i = 0; i != box.size(); i += 2)
+ {
+ if(salt_length > 0)
+ {
+ L ^= load_be<uint32_t>(salt, (i + salt_off) % (salt_length / 4));
+ R ^= load_be<uint32_t>(salt, (i + salt_off + 1) % (salt_length / 4));
+ }
+
+ for(size_t r = 0; r != 16; r += 2)
+ {
+ L ^= m_P[r];
+ R ^= BFF(L, m_S);
+
+ R ^= m_P[r+1];
+ L ^= BFF(R, m_S);
+ }
+
+ uint32_t T = R; R = L ^ m_P[16]; L = T ^ m_P[17];
+ box[i] = L;
+ box[i+1] = R;
+ }
+ }
+
+/*
+* Clear memory of sensitive data
+*/
+void Blowfish::clear()
+ {
+ zap(m_P);
+ zap(m_S);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/block/blowfish/blowfish.h b/comm/third_party/botan/src/lib/block/blowfish/blowfish.h
new file mode 100644
index 0000000000..3ba39cbdbb
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/blowfish/blowfish.h
@@ -0,0 +1,62 @@
+/*
+* Blowfish
+* (C) 1999-2011 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_BLOWFISH_H_
+#define BOTAN_BLOWFISH_H_
+
+#include <botan/block_cipher.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(blowfish.h)
+
+namespace Botan {
+
+/**
+* Blowfish
+*/
+class BOTAN_PUBLIC_API(2,0) Blowfish final : public Block_Cipher_Fixed_Params<8, 1, 56>
+ {
+ public:
+ void encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+ void decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+
+ /**
+ * Modified EKSBlowfish key schedule, used for bcrypt password hashing
+ */
+ void salted_set_key(const uint8_t key[], size_t key_length,
+ const uint8_t salt[], size_t salt_length,
+ const size_t workfactor, bool salt_first = false);
+
+ BOTAN_DEPRECATED("Use Blowfish::salted_set_key taking salt length")
+ void eks_key_schedule(const uint8_t key[], size_t key_length,
+ const uint8_t salt[16], size_t workfactor)
+ {
+ salted_set_key(key, key_length, salt, 16, workfactor);
+ }
+
+ void clear() override;
+ std::string name() const override { return "Blowfish"; }
+ BlockCipher* clone() const override { return new Blowfish; }
+ private:
+ void key_schedule(const uint8_t key[], size_t length) override;
+
+ void key_expansion(const uint8_t key[],
+ size_t key_length,
+ const uint8_t salt[],
+ size_t salt_length);
+
+ void generate_sbox(secure_vector<uint32_t>& box,
+ uint32_t& L, uint32_t& R,
+ const uint8_t salt[],
+ size_t salt_length,
+ size_t salt_off) const;
+
+ secure_vector<uint32_t> m_S, m_P;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/block/blowfish/info.txt b/comm/third_party/botan/src/lib/block/blowfish/info.txt
new file mode 100644
index 0000000000..cc72634dfc
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/blowfish/info.txt
@@ -0,0 +1,3 @@
+<defines>
+BLOWFISH -> 20180718
+</defines>
diff --git a/comm/third_party/botan/src/lib/block/camellia/camellia.cpp b/comm/third_party/botan/src/lib/block/camellia/camellia.cpp
new file mode 100644
index 0000000000..557b3012db
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/camellia/camellia.cpp
@@ -0,0 +1,924 @@
+/*
+* Camellia
+* (C) 2012 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/camellia.h>
+#include <botan/loadstor.h>
+#include <botan/rotate.h>
+
+namespace Botan {
+
+namespace {
+
+alignas(64) const uint64_t Camellia_SBOX1[256] = {
+0x7070700070000070, 0x8282820082000082, 0x2C2C2C002C00002C, 0xECECEC00EC0000EC,
+0xB3B3B300B30000B3, 0x2727270027000027, 0xC0C0C000C00000C0, 0xE5E5E500E50000E5,
+0xE4E4E400E40000E4, 0x8585850085000085, 0x5757570057000057, 0x3535350035000035,
+0xEAEAEA00EA0000EA, 0x0C0C0C000C00000C, 0xAEAEAE00AE0000AE, 0x4141410041000041,
+0x2323230023000023, 0xEFEFEF00EF0000EF, 0x6B6B6B006B00006B, 0x9393930093000093,
+0x4545450045000045, 0x1919190019000019, 0xA5A5A500A50000A5, 0x2121210021000021,
+0xEDEDED00ED0000ED, 0x0E0E0E000E00000E, 0x4F4F4F004F00004F, 0x4E4E4E004E00004E,
+0x1D1D1D001D00001D, 0x6565650065000065, 0x9292920092000092, 0xBDBDBD00BD0000BD,
+0x8686860086000086, 0xB8B8B800B80000B8, 0xAFAFAF00AF0000AF, 0x8F8F8F008F00008F,
+0x7C7C7C007C00007C, 0xEBEBEB00EB0000EB, 0x1F1F1F001F00001F, 0xCECECE00CE0000CE,
+0x3E3E3E003E00003E, 0x3030300030000030, 0xDCDCDC00DC0000DC, 0x5F5F5F005F00005F,
+0x5E5E5E005E00005E, 0xC5C5C500C50000C5, 0x0B0B0B000B00000B, 0x1A1A1A001A00001A,
+0xA6A6A600A60000A6, 0xE1E1E100E10000E1, 0x3939390039000039, 0xCACACA00CA0000CA,
+0xD5D5D500D50000D5, 0x4747470047000047, 0x5D5D5D005D00005D, 0x3D3D3D003D00003D,
+0xD9D9D900D90000D9, 0x0101010001000001, 0x5A5A5A005A00005A, 0xD6D6D600D60000D6,
+0x5151510051000051, 0x5656560056000056, 0x6C6C6C006C00006C, 0x4D4D4D004D00004D,
+0x8B8B8B008B00008B, 0x0D0D0D000D00000D, 0x9A9A9A009A00009A, 0x6666660066000066,
+0xFBFBFB00FB0000FB, 0xCCCCCC00CC0000CC, 0xB0B0B000B00000B0, 0x2D2D2D002D00002D,
+0x7474740074000074, 0x1212120012000012, 0x2B2B2B002B00002B, 0x2020200020000020,
+0xF0F0F000F00000F0, 0xB1B1B100B10000B1, 0x8484840084000084, 0x9999990099000099,
+0xDFDFDF00DF0000DF, 0x4C4C4C004C00004C, 0xCBCBCB00CB0000CB, 0xC2C2C200C20000C2,
+0x3434340034000034, 0x7E7E7E007E00007E, 0x7676760076000076, 0x0505050005000005,
+0x6D6D6D006D00006D, 0xB7B7B700B70000B7, 0xA9A9A900A90000A9, 0x3131310031000031,
+0xD1D1D100D10000D1, 0x1717170017000017, 0x0404040004000004, 0xD7D7D700D70000D7,
+0x1414140014000014, 0x5858580058000058, 0x3A3A3A003A00003A, 0x6161610061000061,
+0xDEDEDE00DE0000DE, 0x1B1B1B001B00001B, 0x1111110011000011, 0x1C1C1C001C00001C,
+0x3232320032000032, 0x0F0F0F000F00000F, 0x9C9C9C009C00009C, 0x1616160016000016,
+0x5353530053000053, 0x1818180018000018, 0xF2F2F200F20000F2, 0x2222220022000022,
+0xFEFEFE00FE0000FE, 0x4444440044000044, 0xCFCFCF00CF0000CF, 0xB2B2B200B20000B2,
+0xC3C3C300C30000C3, 0xB5B5B500B50000B5, 0x7A7A7A007A00007A, 0x9191910091000091,
+0x2424240024000024, 0x0808080008000008, 0xE8E8E800E80000E8, 0xA8A8A800A80000A8,
+0x6060600060000060, 0xFCFCFC00FC0000FC, 0x6969690069000069, 0x5050500050000050,
+0xAAAAAA00AA0000AA, 0xD0D0D000D00000D0, 0xA0A0A000A00000A0, 0x7D7D7D007D00007D,
+0xA1A1A100A10000A1, 0x8989890089000089, 0x6262620062000062, 0x9797970097000097,
+0x5454540054000054, 0x5B5B5B005B00005B, 0x1E1E1E001E00001E, 0x9595950095000095,
+0xE0E0E000E00000E0, 0xFFFFFF00FF0000FF, 0x6464640064000064, 0xD2D2D200D20000D2,
+0x1010100010000010, 0xC4C4C400C40000C4, 0x0000000000000000, 0x4848480048000048,
+0xA3A3A300A30000A3, 0xF7F7F700F70000F7, 0x7575750075000075, 0xDBDBDB00DB0000DB,
+0x8A8A8A008A00008A, 0x0303030003000003, 0xE6E6E600E60000E6, 0xDADADA00DA0000DA,
+0x0909090009000009, 0x3F3F3F003F00003F, 0xDDDDDD00DD0000DD, 0x9494940094000094,
+0x8787870087000087, 0x5C5C5C005C00005C, 0x8383830083000083, 0x0202020002000002,
+0xCDCDCD00CD0000CD, 0x4A4A4A004A00004A, 0x9090900090000090, 0x3333330033000033,
+0x7373730073000073, 0x6767670067000067, 0xF6F6F600F60000F6, 0xF3F3F300F30000F3,
+0x9D9D9D009D00009D, 0x7F7F7F007F00007F, 0xBFBFBF00BF0000BF, 0xE2E2E200E20000E2,
+0x5252520052000052, 0x9B9B9B009B00009B, 0xD8D8D800D80000D8, 0x2626260026000026,
+0xC8C8C800C80000C8, 0x3737370037000037, 0xC6C6C600C60000C6, 0x3B3B3B003B00003B,
+0x8181810081000081, 0x9696960096000096, 0x6F6F6F006F00006F, 0x4B4B4B004B00004B,
+0x1313130013000013, 0xBEBEBE00BE0000BE, 0x6363630063000063, 0x2E2E2E002E00002E,
+0xE9E9E900E90000E9, 0x7979790079000079, 0xA7A7A700A70000A7, 0x8C8C8C008C00008C,
+0x9F9F9F009F00009F, 0x6E6E6E006E00006E, 0xBCBCBC00BC0000BC, 0x8E8E8E008E00008E,
+0x2929290029000029, 0xF5F5F500F50000F5, 0xF9F9F900F90000F9, 0xB6B6B600B60000B6,
+0x2F2F2F002F00002F, 0xFDFDFD00FD0000FD, 0xB4B4B400B40000B4, 0x5959590059000059,
+0x7878780078000078, 0x9898980098000098, 0x0606060006000006, 0x6A6A6A006A00006A,
+0xE7E7E700E70000E7, 0x4646460046000046, 0x7171710071000071, 0xBABABA00BA0000BA,
+0xD4D4D400D40000D4, 0x2525250025000025, 0xABABAB00AB0000AB, 0x4242420042000042,
+0x8888880088000088, 0xA2A2A200A20000A2, 0x8D8D8D008D00008D, 0xFAFAFA00FA0000FA,
+0x7272720072000072, 0x0707070007000007, 0xB9B9B900B90000B9, 0x5555550055000055,
+0xF8F8F800F80000F8, 0xEEEEEE00EE0000EE, 0xACACAC00AC0000AC, 0x0A0A0A000A00000A,
+0x3636360036000036, 0x4949490049000049, 0x2A2A2A002A00002A, 0x6868680068000068,
+0x3C3C3C003C00003C, 0x3838380038000038, 0xF1F1F100F10000F1, 0xA4A4A400A40000A4,
+0x4040400040000040, 0x2828280028000028, 0xD3D3D300D30000D3, 0x7B7B7B007B00007B,
+0xBBBBBB00BB0000BB, 0xC9C9C900C90000C9, 0x4343430043000043, 0xC1C1C100C10000C1,
+0x1515150015000015, 0xE3E3E300E30000E3, 0xADADAD00AD0000AD, 0xF4F4F400F40000F4,
+0x7777770077000077, 0xC7C7C700C70000C7, 0x8080800080000080, 0x9E9E9E009E00009E };
+
+alignas(64) const uint64_t Camellia_SBOX2[256] = {
+0x00E0E0E0E0E00000, 0x0005050505050000, 0x0058585858580000, 0x00D9D9D9D9D90000,
+0x0067676767670000, 0x004E4E4E4E4E0000, 0x0081818181810000, 0x00CBCBCBCBCB0000,
+0x00C9C9C9C9C90000, 0x000B0B0B0B0B0000, 0x00AEAEAEAEAE0000, 0x006A6A6A6A6A0000,
+0x00D5D5D5D5D50000, 0x0018181818180000, 0x005D5D5D5D5D0000, 0x0082828282820000,
+0x0046464646460000, 0x00DFDFDFDFDF0000, 0x00D6D6D6D6D60000, 0x0027272727270000,
+0x008A8A8A8A8A0000, 0x0032323232320000, 0x004B4B4B4B4B0000, 0x0042424242420000,
+0x00DBDBDBDBDB0000, 0x001C1C1C1C1C0000, 0x009E9E9E9E9E0000, 0x009C9C9C9C9C0000,
+0x003A3A3A3A3A0000, 0x00CACACACACA0000, 0x0025252525250000, 0x007B7B7B7B7B0000,
+0x000D0D0D0D0D0000, 0x0071717171710000, 0x005F5F5F5F5F0000, 0x001F1F1F1F1F0000,
+0x00F8F8F8F8F80000, 0x00D7D7D7D7D70000, 0x003E3E3E3E3E0000, 0x009D9D9D9D9D0000,
+0x007C7C7C7C7C0000, 0x0060606060600000, 0x00B9B9B9B9B90000, 0x00BEBEBEBEBE0000,
+0x00BCBCBCBCBC0000, 0x008B8B8B8B8B0000, 0x0016161616160000, 0x0034343434340000,
+0x004D4D4D4D4D0000, 0x00C3C3C3C3C30000, 0x0072727272720000, 0x0095959595950000,
+0x00ABABABABAB0000, 0x008E8E8E8E8E0000, 0x00BABABABABA0000, 0x007A7A7A7A7A0000,
+0x00B3B3B3B3B30000, 0x0002020202020000, 0x00B4B4B4B4B40000, 0x00ADADADADAD0000,
+0x00A2A2A2A2A20000, 0x00ACACACACAC0000, 0x00D8D8D8D8D80000, 0x009A9A9A9A9A0000,
+0x0017171717170000, 0x001A1A1A1A1A0000, 0x0035353535350000, 0x00CCCCCCCCCC0000,
+0x00F7F7F7F7F70000, 0x0099999999990000, 0x0061616161610000, 0x005A5A5A5A5A0000,
+0x00E8E8E8E8E80000, 0x0024242424240000, 0x0056565656560000, 0x0040404040400000,
+0x00E1E1E1E1E10000, 0x0063636363630000, 0x0009090909090000, 0x0033333333330000,
+0x00BFBFBFBFBF0000, 0x0098989898980000, 0x0097979797970000, 0x0085858585850000,
+0x0068686868680000, 0x00FCFCFCFCFC0000, 0x00ECECECECEC0000, 0x000A0A0A0A0A0000,
+0x00DADADADADA0000, 0x006F6F6F6F6F0000, 0x0053535353530000, 0x0062626262620000,
+0x00A3A3A3A3A30000, 0x002E2E2E2E2E0000, 0x0008080808080000, 0x00AFAFAFAFAF0000,
+0x0028282828280000, 0x00B0B0B0B0B00000, 0x0074747474740000, 0x00C2C2C2C2C20000,
+0x00BDBDBDBDBD0000, 0x0036363636360000, 0x0022222222220000, 0x0038383838380000,
+0x0064646464640000, 0x001E1E1E1E1E0000, 0x0039393939390000, 0x002C2C2C2C2C0000,
+0x00A6A6A6A6A60000, 0x0030303030300000, 0x00E5E5E5E5E50000, 0x0044444444440000,
+0x00FDFDFDFDFD0000, 0x0088888888880000, 0x009F9F9F9F9F0000, 0x0065656565650000,
+0x0087878787870000, 0x006B6B6B6B6B0000, 0x00F4F4F4F4F40000, 0x0023232323230000,
+0x0048484848480000, 0x0010101010100000, 0x00D1D1D1D1D10000, 0x0051515151510000,
+0x00C0C0C0C0C00000, 0x00F9F9F9F9F90000, 0x00D2D2D2D2D20000, 0x00A0A0A0A0A00000,
+0x0055555555550000, 0x00A1A1A1A1A10000, 0x0041414141410000, 0x00FAFAFAFAFA0000,
+0x0043434343430000, 0x0013131313130000, 0x00C4C4C4C4C40000, 0x002F2F2F2F2F0000,
+0x00A8A8A8A8A80000, 0x00B6B6B6B6B60000, 0x003C3C3C3C3C0000, 0x002B2B2B2B2B0000,
+0x00C1C1C1C1C10000, 0x00FFFFFFFFFF0000, 0x00C8C8C8C8C80000, 0x00A5A5A5A5A50000,
+0x0020202020200000, 0x0089898989890000, 0x0000000000000000, 0x0090909090900000,
+0x0047474747470000, 0x00EFEFEFEFEF0000, 0x00EAEAEAEAEA0000, 0x00B7B7B7B7B70000,
+0x0015151515150000, 0x0006060606060000, 0x00CDCDCDCDCD0000, 0x00B5B5B5B5B50000,
+0x0012121212120000, 0x007E7E7E7E7E0000, 0x00BBBBBBBBBB0000, 0x0029292929290000,
+0x000F0F0F0F0F0000, 0x00B8B8B8B8B80000, 0x0007070707070000, 0x0004040404040000,
+0x009B9B9B9B9B0000, 0x0094949494940000, 0x0021212121210000, 0x0066666666660000,
+0x00E6E6E6E6E60000, 0x00CECECECECE0000, 0x00EDEDEDEDED0000, 0x00E7E7E7E7E70000,
+0x003B3B3B3B3B0000, 0x00FEFEFEFEFE0000, 0x007F7F7F7F7F0000, 0x00C5C5C5C5C50000,
+0x00A4A4A4A4A40000, 0x0037373737370000, 0x00B1B1B1B1B10000, 0x004C4C4C4C4C0000,
+0x0091919191910000, 0x006E6E6E6E6E0000, 0x008D8D8D8D8D0000, 0x0076767676760000,
+0x0003030303030000, 0x002D2D2D2D2D0000, 0x00DEDEDEDEDE0000, 0x0096969696960000,
+0x0026262626260000, 0x007D7D7D7D7D0000, 0x00C6C6C6C6C60000, 0x005C5C5C5C5C0000,
+0x00D3D3D3D3D30000, 0x00F2F2F2F2F20000, 0x004F4F4F4F4F0000, 0x0019191919190000,
+0x003F3F3F3F3F0000, 0x00DCDCDCDCDC0000, 0x0079797979790000, 0x001D1D1D1D1D0000,
+0x0052525252520000, 0x00EBEBEBEBEB0000, 0x00F3F3F3F3F30000, 0x006D6D6D6D6D0000,
+0x005E5E5E5E5E0000, 0x00FBFBFBFBFB0000, 0x0069696969690000, 0x00B2B2B2B2B20000,
+0x00F0F0F0F0F00000, 0x0031313131310000, 0x000C0C0C0C0C0000, 0x00D4D4D4D4D40000,
+0x00CFCFCFCFCF0000, 0x008C8C8C8C8C0000, 0x00E2E2E2E2E20000, 0x0075757575750000,
+0x00A9A9A9A9A90000, 0x004A4A4A4A4A0000, 0x0057575757570000, 0x0084848484840000,
+0x0011111111110000, 0x0045454545450000, 0x001B1B1B1B1B0000, 0x00F5F5F5F5F50000,
+0x00E4E4E4E4E40000, 0x000E0E0E0E0E0000, 0x0073737373730000, 0x00AAAAAAAAAA0000,
+0x00F1F1F1F1F10000, 0x00DDDDDDDDDD0000, 0x0059595959590000, 0x0014141414140000,
+0x006C6C6C6C6C0000, 0x0092929292920000, 0x0054545454540000, 0x00D0D0D0D0D00000,
+0x0078787878780000, 0x0070707070700000, 0x00E3E3E3E3E30000, 0x0049494949490000,
+0x0080808080800000, 0x0050505050500000, 0x00A7A7A7A7A70000, 0x00F6F6F6F6F60000,
+0x0077777777770000, 0x0093939393930000, 0x0086868686860000, 0x0083838383830000,
+0x002A2A2A2A2A0000, 0x00C7C7C7C7C70000, 0x005B5B5B5B5B0000, 0x00E9E9E9E9E90000,
+0x00EEEEEEEEEE0000, 0x008F8F8F8F8F0000, 0x0001010101010000, 0x003D3D3D3D3D0000 };
+
+alignas(64) const uint64_t Camellia_SBOX3[256] = {
+0x3800383800383800, 0x4100414100414100, 0x1600161600161600, 0x7600767600767600,
+0xD900D9D900D9D900, 0x9300939300939300, 0x6000606000606000, 0xF200F2F200F2F200,
+0x7200727200727200, 0xC200C2C200C2C200, 0xAB00ABAB00ABAB00, 0x9A009A9A009A9A00,
+0x7500757500757500, 0x0600060600060600, 0x5700575700575700, 0xA000A0A000A0A000,
+0x9100919100919100, 0xF700F7F700F7F700, 0xB500B5B500B5B500, 0xC900C9C900C9C900,
+0xA200A2A200A2A200, 0x8C008C8C008C8C00, 0xD200D2D200D2D200, 0x9000909000909000,
+0xF600F6F600F6F600, 0x0700070700070700, 0xA700A7A700A7A700, 0x2700272700272700,
+0x8E008E8E008E8E00, 0xB200B2B200B2B200, 0x4900494900494900, 0xDE00DEDE00DEDE00,
+0x4300434300434300, 0x5C005C5C005C5C00, 0xD700D7D700D7D700, 0xC700C7C700C7C700,
+0x3E003E3E003E3E00, 0xF500F5F500F5F500, 0x8F008F8F008F8F00, 0x6700676700676700,
+0x1F001F1F001F1F00, 0x1800181800181800, 0x6E006E6E006E6E00, 0xAF00AFAF00AFAF00,
+0x2F002F2F002F2F00, 0xE200E2E200E2E200, 0x8500858500858500, 0x0D000D0D000D0D00,
+0x5300535300535300, 0xF000F0F000F0F000, 0x9C009C9C009C9C00, 0x6500656500656500,
+0xEA00EAEA00EAEA00, 0xA300A3A300A3A300, 0xAE00AEAE00AEAE00, 0x9E009E9E009E9E00,
+0xEC00ECEC00ECEC00, 0x8000808000808000, 0x2D002D2D002D2D00, 0x6B006B6B006B6B00,
+0xA800A8A800A8A800, 0x2B002B2B002B2B00, 0x3600363600363600, 0xA600A6A600A6A600,
+0xC500C5C500C5C500, 0x8600868600868600, 0x4D004D4D004D4D00, 0x3300333300333300,
+0xFD00FDFD00FDFD00, 0x6600666600666600, 0x5800585800585800, 0x9600969600969600,
+0x3A003A3A003A3A00, 0x0900090900090900, 0x9500959500959500, 0x1000101000101000,
+0x7800787800787800, 0xD800D8D800D8D800, 0x4200424200424200, 0xCC00CCCC00CCCC00,
+0xEF00EFEF00EFEF00, 0x2600262600262600, 0xE500E5E500E5E500, 0x6100616100616100,
+0x1A001A1A001A1A00, 0x3F003F3F003F3F00, 0x3B003B3B003B3B00, 0x8200828200828200,
+0xB600B6B600B6B600, 0xDB00DBDB00DBDB00, 0xD400D4D400D4D400, 0x9800989800989800,
+0xE800E8E800E8E800, 0x8B008B8B008B8B00, 0x0200020200020200, 0xEB00EBEB00EBEB00,
+0x0A000A0A000A0A00, 0x2C002C2C002C2C00, 0x1D001D1D001D1D00, 0xB000B0B000B0B000,
+0x6F006F6F006F6F00, 0x8D008D8D008D8D00, 0x8800888800888800, 0x0E000E0E000E0E00,
+0x1900191900191900, 0x8700878700878700, 0x4E004E4E004E4E00, 0x0B000B0B000B0B00,
+0xA900A9A900A9A900, 0x0C000C0C000C0C00, 0x7900797900797900, 0x1100111100111100,
+0x7F007F7F007F7F00, 0x2200222200222200, 0xE700E7E700E7E700, 0x5900595900595900,
+0xE100E1E100E1E100, 0xDA00DADA00DADA00, 0x3D003D3D003D3D00, 0xC800C8C800C8C800,
+0x1200121200121200, 0x0400040400040400, 0x7400747400747400, 0x5400545400545400,
+0x3000303000303000, 0x7E007E7E007E7E00, 0xB400B4B400B4B400, 0x2800282800282800,
+0x5500555500555500, 0x6800686800686800, 0x5000505000505000, 0xBE00BEBE00BEBE00,
+0xD000D0D000D0D000, 0xC400C4C400C4C400, 0x3100313100313100, 0xCB00CBCB00CBCB00,
+0x2A002A2A002A2A00, 0xAD00ADAD00ADAD00, 0x0F000F0F000F0F00, 0xCA00CACA00CACA00,
+0x7000707000707000, 0xFF00FFFF00FFFF00, 0x3200323200323200, 0x6900696900696900,
+0x0800080800080800, 0x6200626200626200, 0x0000000000000000, 0x2400242400242400,
+0xD100D1D100D1D100, 0xFB00FBFB00FBFB00, 0xBA00BABA00BABA00, 0xED00EDED00EDED00,
+0x4500454500454500, 0x8100818100818100, 0x7300737300737300, 0x6D006D6D006D6D00,
+0x8400848400848400, 0x9F009F9F009F9F00, 0xEE00EEEE00EEEE00, 0x4A004A4A004A4A00,
+0xC300C3C300C3C300, 0x2E002E2E002E2E00, 0xC100C1C100C1C100, 0x0100010100010100,
+0xE600E6E600E6E600, 0x2500252500252500, 0x4800484800484800, 0x9900999900999900,
+0xB900B9B900B9B900, 0xB300B3B300B3B300, 0x7B007B7B007B7B00, 0xF900F9F900F9F900,
+0xCE00CECE00CECE00, 0xBF00BFBF00BFBF00, 0xDF00DFDF00DFDF00, 0x7100717100717100,
+0x2900292900292900, 0xCD00CDCD00CDCD00, 0x6C006C6C006C6C00, 0x1300131300131300,
+0x6400646400646400, 0x9B009B9B009B9B00, 0x6300636300636300, 0x9D009D9D009D9D00,
+0xC000C0C000C0C000, 0x4B004B4B004B4B00, 0xB700B7B700B7B700, 0xA500A5A500A5A500,
+0x8900898900898900, 0x5F005F5F005F5F00, 0xB100B1B100B1B100, 0x1700171700171700,
+0xF400F4F400F4F400, 0xBC00BCBC00BCBC00, 0xD300D3D300D3D300, 0x4600464600464600,
+0xCF00CFCF00CFCF00, 0x3700373700373700, 0x5E005E5E005E5E00, 0x4700474700474700,
+0x9400949400949400, 0xFA00FAFA00FAFA00, 0xFC00FCFC00FCFC00, 0x5B005B5B005B5B00,
+0x9700979700979700, 0xFE00FEFE00FEFE00, 0x5A005A5A005A5A00, 0xAC00ACAC00ACAC00,
+0x3C003C3C003C3C00, 0x4C004C4C004C4C00, 0x0300030300030300, 0x3500353500353500,
+0xF300F3F300F3F300, 0x2300232300232300, 0xB800B8B800B8B800, 0x5D005D5D005D5D00,
+0x6A006A6A006A6A00, 0x9200929200929200, 0xD500D5D500D5D500, 0x2100212100212100,
+0x4400444400444400, 0x5100515100515100, 0xC600C6C600C6C600, 0x7D007D7D007D7D00,
+0x3900393900393900, 0x8300838300838300, 0xDC00DCDC00DCDC00, 0xAA00AAAA00AAAA00,
+0x7C007C7C007C7C00, 0x7700777700777700, 0x5600565600565600, 0x0500050500050500,
+0x1B001B1B001B1B00, 0xA400A4A400A4A400, 0x1500151500151500, 0x3400343400343400,
+0x1E001E1E001E1E00, 0x1C001C1C001C1C00, 0xF800F8F800F8F800, 0x5200525200525200,
+0x2000202000202000, 0x1400141400141400, 0xE900E9E900E9E900, 0xBD00BDBD00BDBD00,
+0xDD00DDDD00DDDD00, 0xE400E4E400E4E400, 0xA100A1A100A1A100, 0xE000E0E000E0E000,
+0x8A008A8A008A8A00, 0xF100F1F100F1F100, 0xD600D6D600D6D600, 0x7A007A7A007A7A00,
+0xBB00BBBB00BBBB00, 0xE300E3E300E3E300, 0x4000404000404000, 0x4F004F4F004F4F00 };
+
+alignas(64) const uint64_t Camellia_SBOX4[256] = {
+0x7070007000007070, 0x2C2C002C00002C2C, 0xB3B300B30000B3B3, 0xC0C000C00000C0C0,
+0xE4E400E40000E4E4, 0x5757005700005757, 0xEAEA00EA0000EAEA, 0xAEAE00AE0000AEAE,
+0x2323002300002323, 0x6B6B006B00006B6B, 0x4545004500004545, 0xA5A500A50000A5A5,
+0xEDED00ED0000EDED, 0x4F4F004F00004F4F, 0x1D1D001D00001D1D, 0x9292009200009292,
+0x8686008600008686, 0xAFAF00AF0000AFAF, 0x7C7C007C00007C7C, 0x1F1F001F00001F1F,
+0x3E3E003E00003E3E, 0xDCDC00DC0000DCDC, 0x5E5E005E00005E5E, 0x0B0B000B00000B0B,
+0xA6A600A60000A6A6, 0x3939003900003939, 0xD5D500D50000D5D5, 0x5D5D005D00005D5D,
+0xD9D900D90000D9D9, 0x5A5A005A00005A5A, 0x5151005100005151, 0x6C6C006C00006C6C,
+0x8B8B008B00008B8B, 0x9A9A009A00009A9A, 0xFBFB00FB0000FBFB, 0xB0B000B00000B0B0,
+0x7474007400007474, 0x2B2B002B00002B2B, 0xF0F000F00000F0F0, 0x8484008400008484,
+0xDFDF00DF0000DFDF, 0xCBCB00CB0000CBCB, 0x3434003400003434, 0x7676007600007676,
+0x6D6D006D00006D6D, 0xA9A900A90000A9A9, 0xD1D100D10000D1D1, 0x0404000400000404,
+0x1414001400001414, 0x3A3A003A00003A3A, 0xDEDE00DE0000DEDE, 0x1111001100001111,
+0x3232003200003232, 0x9C9C009C00009C9C, 0x5353005300005353, 0xF2F200F20000F2F2,
+0xFEFE00FE0000FEFE, 0xCFCF00CF0000CFCF, 0xC3C300C30000C3C3, 0x7A7A007A00007A7A,
+0x2424002400002424, 0xE8E800E80000E8E8, 0x6060006000006060, 0x6969006900006969,
+0xAAAA00AA0000AAAA, 0xA0A000A00000A0A0, 0xA1A100A10000A1A1, 0x6262006200006262,
+0x5454005400005454, 0x1E1E001E00001E1E, 0xE0E000E00000E0E0, 0x6464006400006464,
+0x1010001000001010, 0x0000000000000000, 0xA3A300A30000A3A3, 0x7575007500007575,
+0x8A8A008A00008A8A, 0xE6E600E60000E6E6, 0x0909000900000909, 0xDDDD00DD0000DDDD,
+0x8787008700008787, 0x8383008300008383, 0xCDCD00CD0000CDCD, 0x9090009000009090,
+0x7373007300007373, 0xF6F600F60000F6F6, 0x9D9D009D00009D9D, 0xBFBF00BF0000BFBF,
+0x5252005200005252, 0xD8D800D80000D8D8, 0xC8C800C80000C8C8, 0xC6C600C60000C6C6,
+0x8181008100008181, 0x6F6F006F00006F6F, 0x1313001300001313, 0x6363006300006363,
+0xE9E900E90000E9E9, 0xA7A700A70000A7A7, 0x9F9F009F00009F9F, 0xBCBC00BC0000BCBC,
+0x2929002900002929, 0xF9F900F90000F9F9, 0x2F2F002F00002F2F, 0xB4B400B40000B4B4,
+0x7878007800007878, 0x0606000600000606, 0xE7E700E70000E7E7, 0x7171007100007171,
+0xD4D400D40000D4D4, 0xABAB00AB0000ABAB, 0x8888008800008888, 0x8D8D008D00008D8D,
+0x7272007200007272, 0xB9B900B90000B9B9, 0xF8F800F80000F8F8, 0xACAC00AC0000ACAC,
+0x3636003600003636, 0x2A2A002A00002A2A, 0x3C3C003C00003C3C, 0xF1F100F10000F1F1,
+0x4040004000004040, 0xD3D300D30000D3D3, 0xBBBB00BB0000BBBB, 0x4343004300004343,
+0x1515001500001515, 0xADAD00AD0000ADAD, 0x7777007700007777, 0x8080008000008080,
+0x8282008200008282, 0xECEC00EC0000ECEC, 0x2727002700002727, 0xE5E500E50000E5E5,
+0x8585008500008585, 0x3535003500003535, 0x0C0C000C00000C0C, 0x4141004100004141,
+0xEFEF00EF0000EFEF, 0x9393009300009393, 0x1919001900001919, 0x2121002100002121,
+0x0E0E000E00000E0E, 0x4E4E004E00004E4E, 0x6565006500006565, 0xBDBD00BD0000BDBD,
+0xB8B800B80000B8B8, 0x8F8F008F00008F8F, 0xEBEB00EB0000EBEB, 0xCECE00CE0000CECE,
+0x3030003000003030, 0x5F5F005F00005F5F, 0xC5C500C50000C5C5, 0x1A1A001A00001A1A,
+0xE1E100E10000E1E1, 0xCACA00CA0000CACA, 0x4747004700004747, 0x3D3D003D00003D3D,
+0x0101000100000101, 0xD6D600D60000D6D6, 0x5656005600005656, 0x4D4D004D00004D4D,
+0x0D0D000D00000D0D, 0x6666006600006666, 0xCCCC00CC0000CCCC, 0x2D2D002D00002D2D,
+0x1212001200001212, 0x2020002000002020, 0xB1B100B10000B1B1, 0x9999009900009999,
+0x4C4C004C00004C4C, 0xC2C200C20000C2C2, 0x7E7E007E00007E7E, 0x0505000500000505,
+0xB7B700B70000B7B7, 0x3131003100003131, 0x1717001700001717, 0xD7D700D70000D7D7,
+0x5858005800005858, 0x6161006100006161, 0x1B1B001B00001B1B, 0x1C1C001C00001C1C,
+0x0F0F000F00000F0F, 0x1616001600001616, 0x1818001800001818, 0x2222002200002222,
+0x4444004400004444, 0xB2B200B20000B2B2, 0xB5B500B50000B5B5, 0x9191009100009191,
+0x0808000800000808, 0xA8A800A80000A8A8, 0xFCFC00FC0000FCFC, 0x5050005000005050,
+0xD0D000D00000D0D0, 0x7D7D007D00007D7D, 0x8989008900008989, 0x9797009700009797,
+0x5B5B005B00005B5B, 0x9595009500009595, 0xFFFF00FF0000FFFF, 0xD2D200D20000D2D2,
+0xC4C400C40000C4C4, 0x4848004800004848, 0xF7F700F70000F7F7, 0xDBDB00DB0000DBDB,
+0x0303000300000303, 0xDADA00DA0000DADA, 0x3F3F003F00003F3F, 0x9494009400009494,
+0x5C5C005C00005C5C, 0x0202000200000202, 0x4A4A004A00004A4A, 0x3333003300003333,
+0x6767006700006767, 0xF3F300F30000F3F3, 0x7F7F007F00007F7F, 0xE2E200E20000E2E2,
+0x9B9B009B00009B9B, 0x2626002600002626, 0x3737003700003737, 0x3B3B003B00003B3B,
+0x9696009600009696, 0x4B4B004B00004B4B, 0xBEBE00BE0000BEBE, 0x2E2E002E00002E2E,
+0x7979007900007979, 0x8C8C008C00008C8C, 0x6E6E006E00006E6E, 0x8E8E008E00008E8E,
+0xF5F500F50000F5F5, 0xB6B600B60000B6B6, 0xFDFD00FD0000FDFD, 0x5959005900005959,
+0x9898009800009898, 0x6A6A006A00006A6A, 0x4646004600004646, 0xBABA00BA0000BABA,
+0x2525002500002525, 0x4242004200004242, 0xA2A200A20000A2A2, 0xFAFA00FA0000FAFA,
+0x0707000700000707, 0x5555005500005555, 0xEEEE00EE0000EEEE, 0x0A0A000A00000A0A,
+0x4949004900004949, 0x6868006800006868, 0x3838003800003838, 0xA4A400A40000A4A4,
+0x2828002800002828, 0x7B7B007B00007B7B, 0xC9C900C90000C9C9, 0xC1C100C10000C1C1,
+0xE3E300E30000E3E3, 0xF4F400F40000F4F4, 0xC7C700C70000C7C7, 0x9E9E009E00009E9E };
+
+alignas(64) const uint64_t Camellia_SBOX5[256] = {
+0x00E0E0E000E0E0E0, 0x0005050500050505, 0x0058585800585858, 0x00D9D9D900D9D9D9,
+0x0067676700676767, 0x004E4E4E004E4E4E, 0x0081818100818181, 0x00CBCBCB00CBCBCB,
+0x00C9C9C900C9C9C9, 0x000B0B0B000B0B0B, 0x00AEAEAE00AEAEAE, 0x006A6A6A006A6A6A,
+0x00D5D5D500D5D5D5, 0x0018181800181818, 0x005D5D5D005D5D5D, 0x0082828200828282,
+0x0046464600464646, 0x00DFDFDF00DFDFDF, 0x00D6D6D600D6D6D6, 0x0027272700272727,
+0x008A8A8A008A8A8A, 0x0032323200323232, 0x004B4B4B004B4B4B, 0x0042424200424242,
+0x00DBDBDB00DBDBDB, 0x001C1C1C001C1C1C, 0x009E9E9E009E9E9E, 0x009C9C9C009C9C9C,
+0x003A3A3A003A3A3A, 0x00CACACA00CACACA, 0x0025252500252525, 0x007B7B7B007B7B7B,
+0x000D0D0D000D0D0D, 0x0071717100717171, 0x005F5F5F005F5F5F, 0x001F1F1F001F1F1F,
+0x00F8F8F800F8F8F8, 0x00D7D7D700D7D7D7, 0x003E3E3E003E3E3E, 0x009D9D9D009D9D9D,
+0x007C7C7C007C7C7C, 0x0060606000606060, 0x00B9B9B900B9B9B9, 0x00BEBEBE00BEBEBE,
+0x00BCBCBC00BCBCBC, 0x008B8B8B008B8B8B, 0x0016161600161616, 0x0034343400343434,
+0x004D4D4D004D4D4D, 0x00C3C3C300C3C3C3, 0x0072727200727272, 0x0095959500959595,
+0x00ABABAB00ABABAB, 0x008E8E8E008E8E8E, 0x00BABABA00BABABA, 0x007A7A7A007A7A7A,
+0x00B3B3B300B3B3B3, 0x0002020200020202, 0x00B4B4B400B4B4B4, 0x00ADADAD00ADADAD,
+0x00A2A2A200A2A2A2, 0x00ACACAC00ACACAC, 0x00D8D8D800D8D8D8, 0x009A9A9A009A9A9A,
+0x0017171700171717, 0x001A1A1A001A1A1A, 0x0035353500353535, 0x00CCCCCC00CCCCCC,
+0x00F7F7F700F7F7F7, 0x0099999900999999, 0x0061616100616161, 0x005A5A5A005A5A5A,
+0x00E8E8E800E8E8E8, 0x0024242400242424, 0x0056565600565656, 0x0040404000404040,
+0x00E1E1E100E1E1E1, 0x0063636300636363, 0x0009090900090909, 0x0033333300333333,
+0x00BFBFBF00BFBFBF, 0x0098989800989898, 0x0097979700979797, 0x0085858500858585,
+0x0068686800686868, 0x00FCFCFC00FCFCFC, 0x00ECECEC00ECECEC, 0x000A0A0A000A0A0A,
+0x00DADADA00DADADA, 0x006F6F6F006F6F6F, 0x0053535300535353, 0x0062626200626262,
+0x00A3A3A300A3A3A3, 0x002E2E2E002E2E2E, 0x0008080800080808, 0x00AFAFAF00AFAFAF,
+0x0028282800282828, 0x00B0B0B000B0B0B0, 0x0074747400747474, 0x00C2C2C200C2C2C2,
+0x00BDBDBD00BDBDBD, 0x0036363600363636, 0x0022222200222222, 0x0038383800383838,
+0x0064646400646464, 0x001E1E1E001E1E1E, 0x0039393900393939, 0x002C2C2C002C2C2C,
+0x00A6A6A600A6A6A6, 0x0030303000303030, 0x00E5E5E500E5E5E5, 0x0044444400444444,
+0x00FDFDFD00FDFDFD, 0x0088888800888888, 0x009F9F9F009F9F9F, 0x0065656500656565,
+0x0087878700878787, 0x006B6B6B006B6B6B, 0x00F4F4F400F4F4F4, 0x0023232300232323,
+0x0048484800484848, 0x0010101000101010, 0x00D1D1D100D1D1D1, 0x0051515100515151,
+0x00C0C0C000C0C0C0, 0x00F9F9F900F9F9F9, 0x00D2D2D200D2D2D2, 0x00A0A0A000A0A0A0,
+0x0055555500555555, 0x00A1A1A100A1A1A1, 0x0041414100414141, 0x00FAFAFA00FAFAFA,
+0x0043434300434343, 0x0013131300131313, 0x00C4C4C400C4C4C4, 0x002F2F2F002F2F2F,
+0x00A8A8A800A8A8A8, 0x00B6B6B600B6B6B6, 0x003C3C3C003C3C3C, 0x002B2B2B002B2B2B,
+0x00C1C1C100C1C1C1, 0x00FFFFFF00FFFFFF, 0x00C8C8C800C8C8C8, 0x00A5A5A500A5A5A5,
+0x0020202000202020, 0x0089898900898989, 0x0000000000000000, 0x0090909000909090,
+0x0047474700474747, 0x00EFEFEF00EFEFEF, 0x00EAEAEA00EAEAEA, 0x00B7B7B700B7B7B7,
+0x0015151500151515, 0x0006060600060606, 0x00CDCDCD00CDCDCD, 0x00B5B5B500B5B5B5,
+0x0012121200121212, 0x007E7E7E007E7E7E, 0x00BBBBBB00BBBBBB, 0x0029292900292929,
+0x000F0F0F000F0F0F, 0x00B8B8B800B8B8B8, 0x0007070700070707, 0x0004040400040404,
+0x009B9B9B009B9B9B, 0x0094949400949494, 0x0021212100212121, 0x0066666600666666,
+0x00E6E6E600E6E6E6, 0x00CECECE00CECECE, 0x00EDEDED00EDEDED, 0x00E7E7E700E7E7E7,
+0x003B3B3B003B3B3B, 0x00FEFEFE00FEFEFE, 0x007F7F7F007F7F7F, 0x00C5C5C500C5C5C5,
+0x00A4A4A400A4A4A4, 0x0037373700373737, 0x00B1B1B100B1B1B1, 0x004C4C4C004C4C4C,
+0x0091919100919191, 0x006E6E6E006E6E6E, 0x008D8D8D008D8D8D, 0x0076767600767676,
+0x0003030300030303, 0x002D2D2D002D2D2D, 0x00DEDEDE00DEDEDE, 0x0096969600969696,
+0x0026262600262626, 0x007D7D7D007D7D7D, 0x00C6C6C600C6C6C6, 0x005C5C5C005C5C5C,
+0x00D3D3D300D3D3D3, 0x00F2F2F200F2F2F2, 0x004F4F4F004F4F4F, 0x0019191900191919,
+0x003F3F3F003F3F3F, 0x00DCDCDC00DCDCDC, 0x0079797900797979, 0x001D1D1D001D1D1D,
+0x0052525200525252, 0x00EBEBEB00EBEBEB, 0x00F3F3F300F3F3F3, 0x006D6D6D006D6D6D,
+0x005E5E5E005E5E5E, 0x00FBFBFB00FBFBFB, 0x0069696900696969, 0x00B2B2B200B2B2B2,
+0x00F0F0F000F0F0F0, 0x0031313100313131, 0x000C0C0C000C0C0C, 0x00D4D4D400D4D4D4,
+0x00CFCFCF00CFCFCF, 0x008C8C8C008C8C8C, 0x00E2E2E200E2E2E2, 0x0075757500757575,
+0x00A9A9A900A9A9A9, 0x004A4A4A004A4A4A, 0x0057575700575757, 0x0084848400848484,
+0x0011111100111111, 0x0045454500454545, 0x001B1B1B001B1B1B, 0x00F5F5F500F5F5F5,
+0x00E4E4E400E4E4E4, 0x000E0E0E000E0E0E, 0x0073737300737373, 0x00AAAAAA00AAAAAA,
+0x00F1F1F100F1F1F1, 0x00DDDDDD00DDDDDD, 0x0059595900595959, 0x0014141400141414,
+0x006C6C6C006C6C6C, 0x0092929200929292, 0x0054545400545454, 0x00D0D0D000D0D0D0,
+0x0078787800787878, 0x0070707000707070, 0x00E3E3E300E3E3E3, 0x0049494900494949,
+0x0080808000808080, 0x0050505000505050, 0x00A7A7A700A7A7A7, 0x00F6F6F600F6F6F6,
+0x0077777700777777, 0x0093939300939393, 0x0086868600868686, 0x0083838300838383,
+0x002A2A2A002A2A2A, 0x00C7C7C700C7C7C7, 0x005B5B5B005B5B5B, 0x00E9E9E900E9E9E9,
+0x00EEEEEE00EEEEEE, 0x008F8F8F008F8F8F, 0x0001010100010101, 0x003D3D3D003D3D3D };
+
+alignas(64) const uint64_t Camellia_SBOX6[256] = {
+0x3800383838003838, 0x4100414141004141, 0x1600161616001616, 0x7600767676007676,
+0xD900D9D9D900D9D9, 0x9300939393009393, 0x6000606060006060, 0xF200F2F2F200F2F2,
+0x7200727272007272, 0xC200C2C2C200C2C2, 0xAB00ABABAB00ABAB, 0x9A009A9A9A009A9A,
+0x7500757575007575, 0x0600060606000606, 0x5700575757005757, 0xA000A0A0A000A0A0,
+0x9100919191009191, 0xF700F7F7F700F7F7, 0xB500B5B5B500B5B5, 0xC900C9C9C900C9C9,
+0xA200A2A2A200A2A2, 0x8C008C8C8C008C8C, 0xD200D2D2D200D2D2, 0x9000909090009090,
+0xF600F6F6F600F6F6, 0x0700070707000707, 0xA700A7A7A700A7A7, 0x2700272727002727,
+0x8E008E8E8E008E8E, 0xB200B2B2B200B2B2, 0x4900494949004949, 0xDE00DEDEDE00DEDE,
+0x4300434343004343, 0x5C005C5C5C005C5C, 0xD700D7D7D700D7D7, 0xC700C7C7C700C7C7,
+0x3E003E3E3E003E3E, 0xF500F5F5F500F5F5, 0x8F008F8F8F008F8F, 0x6700676767006767,
+0x1F001F1F1F001F1F, 0x1800181818001818, 0x6E006E6E6E006E6E, 0xAF00AFAFAF00AFAF,
+0x2F002F2F2F002F2F, 0xE200E2E2E200E2E2, 0x8500858585008585, 0x0D000D0D0D000D0D,
+0x5300535353005353, 0xF000F0F0F000F0F0, 0x9C009C9C9C009C9C, 0x6500656565006565,
+0xEA00EAEAEA00EAEA, 0xA300A3A3A300A3A3, 0xAE00AEAEAE00AEAE, 0x9E009E9E9E009E9E,
+0xEC00ECECEC00ECEC, 0x8000808080008080, 0x2D002D2D2D002D2D, 0x6B006B6B6B006B6B,
+0xA800A8A8A800A8A8, 0x2B002B2B2B002B2B, 0x3600363636003636, 0xA600A6A6A600A6A6,
+0xC500C5C5C500C5C5, 0x8600868686008686, 0x4D004D4D4D004D4D, 0x3300333333003333,
+0xFD00FDFDFD00FDFD, 0x6600666666006666, 0x5800585858005858, 0x9600969696009696,
+0x3A003A3A3A003A3A, 0x0900090909000909, 0x9500959595009595, 0x1000101010001010,
+0x7800787878007878, 0xD800D8D8D800D8D8, 0x4200424242004242, 0xCC00CCCCCC00CCCC,
+0xEF00EFEFEF00EFEF, 0x2600262626002626, 0xE500E5E5E500E5E5, 0x6100616161006161,
+0x1A001A1A1A001A1A, 0x3F003F3F3F003F3F, 0x3B003B3B3B003B3B, 0x8200828282008282,
+0xB600B6B6B600B6B6, 0xDB00DBDBDB00DBDB, 0xD400D4D4D400D4D4, 0x9800989898009898,
+0xE800E8E8E800E8E8, 0x8B008B8B8B008B8B, 0x0200020202000202, 0xEB00EBEBEB00EBEB,
+0x0A000A0A0A000A0A, 0x2C002C2C2C002C2C, 0x1D001D1D1D001D1D, 0xB000B0B0B000B0B0,
+0x6F006F6F6F006F6F, 0x8D008D8D8D008D8D, 0x8800888888008888, 0x0E000E0E0E000E0E,
+0x1900191919001919, 0x8700878787008787, 0x4E004E4E4E004E4E, 0x0B000B0B0B000B0B,
+0xA900A9A9A900A9A9, 0x0C000C0C0C000C0C, 0x7900797979007979, 0x1100111111001111,
+0x7F007F7F7F007F7F, 0x2200222222002222, 0xE700E7E7E700E7E7, 0x5900595959005959,
+0xE100E1E1E100E1E1, 0xDA00DADADA00DADA, 0x3D003D3D3D003D3D, 0xC800C8C8C800C8C8,
+0x1200121212001212, 0x0400040404000404, 0x7400747474007474, 0x5400545454005454,
+0x3000303030003030, 0x7E007E7E7E007E7E, 0xB400B4B4B400B4B4, 0x2800282828002828,
+0x5500555555005555, 0x6800686868006868, 0x5000505050005050, 0xBE00BEBEBE00BEBE,
+0xD000D0D0D000D0D0, 0xC400C4C4C400C4C4, 0x3100313131003131, 0xCB00CBCBCB00CBCB,
+0x2A002A2A2A002A2A, 0xAD00ADADAD00ADAD, 0x0F000F0F0F000F0F, 0xCA00CACACA00CACA,
+0x7000707070007070, 0xFF00FFFFFF00FFFF, 0x3200323232003232, 0x6900696969006969,
+0x0800080808000808, 0x6200626262006262, 0x0000000000000000, 0x2400242424002424,
+0xD100D1D1D100D1D1, 0xFB00FBFBFB00FBFB, 0xBA00BABABA00BABA, 0xED00EDEDED00EDED,
+0x4500454545004545, 0x8100818181008181, 0x7300737373007373, 0x6D006D6D6D006D6D,
+0x8400848484008484, 0x9F009F9F9F009F9F, 0xEE00EEEEEE00EEEE, 0x4A004A4A4A004A4A,
+0xC300C3C3C300C3C3, 0x2E002E2E2E002E2E, 0xC100C1C1C100C1C1, 0x0100010101000101,
+0xE600E6E6E600E6E6, 0x2500252525002525, 0x4800484848004848, 0x9900999999009999,
+0xB900B9B9B900B9B9, 0xB300B3B3B300B3B3, 0x7B007B7B7B007B7B, 0xF900F9F9F900F9F9,
+0xCE00CECECE00CECE, 0xBF00BFBFBF00BFBF, 0xDF00DFDFDF00DFDF, 0x7100717171007171,
+0x2900292929002929, 0xCD00CDCDCD00CDCD, 0x6C006C6C6C006C6C, 0x1300131313001313,
+0x6400646464006464, 0x9B009B9B9B009B9B, 0x6300636363006363, 0x9D009D9D9D009D9D,
+0xC000C0C0C000C0C0, 0x4B004B4B4B004B4B, 0xB700B7B7B700B7B7, 0xA500A5A5A500A5A5,
+0x8900898989008989, 0x5F005F5F5F005F5F, 0xB100B1B1B100B1B1, 0x1700171717001717,
+0xF400F4F4F400F4F4, 0xBC00BCBCBC00BCBC, 0xD300D3D3D300D3D3, 0x4600464646004646,
+0xCF00CFCFCF00CFCF, 0x3700373737003737, 0x5E005E5E5E005E5E, 0x4700474747004747,
+0x9400949494009494, 0xFA00FAFAFA00FAFA, 0xFC00FCFCFC00FCFC, 0x5B005B5B5B005B5B,
+0x9700979797009797, 0xFE00FEFEFE00FEFE, 0x5A005A5A5A005A5A, 0xAC00ACACAC00ACAC,
+0x3C003C3C3C003C3C, 0x4C004C4C4C004C4C, 0x0300030303000303, 0x3500353535003535,
+0xF300F3F3F300F3F3, 0x2300232323002323, 0xB800B8B8B800B8B8, 0x5D005D5D5D005D5D,
+0x6A006A6A6A006A6A, 0x9200929292009292, 0xD500D5D5D500D5D5, 0x2100212121002121,
+0x4400444444004444, 0x5100515151005151, 0xC600C6C6C600C6C6, 0x7D007D7D7D007D7D,
+0x3900393939003939, 0x8300838383008383, 0xDC00DCDCDC00DCDC, 0xAA00AAAAAA00AAAA,
+0x7C007C7C7C007C7C, 0x7700777777007777, 0x5600565656005656, 0x0500050505000505,
+0x1B001B1B1B001B1B, 0xA400A4A4A400A4A4, 0x1500151515001515, 0x3400343434003434,
+0x1E001E1E1E001E1E, 0x1C001C1C1C001C1C, 0xF800F8F8F800F8F8, 0x5200525252005252,
+0x2000202020002020, 0x1400141414001414, 0xE900E9E9E900E9E9, 0xBD00BDBDBD00BDBD,
+0xDD00DDDDDD00DDDD, 0xE400E4E4E400E4E4, 0xA100A1A1A100A1A1, 0xE000E0E0E000E0E0,
+0x8A008A8A8A008A8A, 0xF100F1F1F100F1F1, 0xD600D6D6D600D6D6, 0x7A007A7A7A007A7A,
+0xBB00BBBBBB00BBBB, 0xE300E3E3E300E3E3, 0x4000404040004040, 0x4F004F4F4F004F4F };
+
+alignas(64) const uint64_t Camellia_SBOX7[256] = {
+0x7070007070700070, 0x2C2C002C2C2C002C, 0xB3B300B3B3B300B3, 0xC0C000C0C0C000C0,
+0xE4E400E4E4E400E4, 0x5757005757570057, 0xEAEA00EAEAEA00EA, 0xAEAE00AEAEAE00AE,
+0x2323002323230023, 0x6B6B006B6B6B006B, 0x4545004545450045, 0xA5A500A5A5A500A5,
+0xEDED00EDEDED00ED, 0x4F4F004F4F4F004F, 0x1D1D001D1D1D001D, 0x9292009292920092,
+0x8686008686860086, 0xAFAF00AFAFAF00AF, 0x7C7C007C7C7C007C, 0x1F1F001F1F1F001F,
+0x3E3E003E3E3E003E, 0xDCDC00DCDCDC00DC, 0x5E5E005E5E5E005E, 0x0B0B000B0B0B000B,
+0xA6A600A6A6A600A6, 0x3939003939390039, 0xD5D500D5D5D500D5, 0x5D5D005D5D5D005D,
+0xD9D900D9D9D900D9, 0x5A5A005A5A5A005A, 0x5151005151510051, 0x6C6C006C6C6C006C,
+0x8B8B008B8B8B008B, 0x9A9A009A9A9A009A, 0xFBFB00FBFBFB00FB, 0xB0B000B0B0B000B0,
+0x7474007474740074, 0x2B2B002B2B2B002B, 0xF0F000F0F0F000F0, 0x8484008484840084,
+0xDFDF00DFDFDF00DF, 0xCBCB00CBCBCB00CB, 0x3434003434340034, 0x7676007676760076,
+0x6D6D006D6D6D006D, 0xA9A900A9A9A900A9, 0xD1D100D1D1D100D1, 0x0404000404040004,
+0x1414001414140014, 0x3A3A003A3A3A003A, 0xDEDE00DEDEDE00DE, 0x1111001111110011,
+0x3232003232320032, 0x9C9C009C9C9C009C, 0x5353005353530053, 0xF2F200F2F2F200F2,
+0xFEFE00FEFEFE00FE, 0xCFCF00CFCFCF00CF, 0xC3C300C3C3C300C3, 0x7A7A007A7A7A007A,
+0x2424002424240024, 0xE8E800E8E8E800E8, 0x6060006060600060, 0x6969006969690069,
+0xAAAA00AAAAAA00AA, 0xA0A000A0A0A000A0, 0xA1A100A1A1A100A1, 0x6262006262620062,
+0x5454005454540054, 0x1E1E001E1E1E001E, 0xE0E000E0E0E000E0, 0x6464006464640064,
+0x1010001010100010, 0x0000000000000000, 0xA3A300A3A3A300A3, 0x7575007575750075,
+0x8A8A008A8A8A008A, 0xE6E600E6E6E600E6, 0x0909000909090009, 0xDDDD00DDDDDD00DD,
+0x8787008787870087, 0x8383008383830083, 0xCDCD00CDCDCD00CD, 0x9090009090900090,
+0x7373007373730073, 0xF6F600F6F6F600F6, 0x9D9D009D9D9D009D, 0xBFBF00BFBFBF00BF,
+0x5252005252520052, 0xD8D800D8D8D800D8, 0xC8C800C8C8C800C8, 0xC6C600C6C6C600C6,
+0x8181008181810081, 0x6F6F006F6F6F006F, 0x1313001313130013, 0x6363006363630063,
+0xE9E900E9E9E900E9, 0xA7A700A7A7A700A7, 0x9F9F009F9F9F009F, 0xBCBC00BCBCBC00BC,
+0x2929002929290029, 0xF9F900F9F9F900F9, 0x2F2F002F2F2F002F, 0xB4B400B4B4B400B4,
+0x7878007878780078, 0x0606000606060006, 0xE7E700E7E7E700E7, 0x7171007171710071,
+0xD4D400D4D4D400D4, 0xABAB00ABABAB00AB, 0x8888008888880088, 0x8D8D008D8D8D008D,
+0x7272007272720072, 0xB9B900B9B9B900B9, 0xF8F800F8F8F800F8, 0xACAC00ACACAC00AC,
+0x3636003636360036, 0x2A2A002A2A2A002A, 0x3C3C003C3C3C003C, 0xF1F100F1F1F100F1,
+0x4040004040400040, 0xD3D300D3D3D300D3, 0xBBBB00BBBBBB00BB, 0x4343004343430043,
+0x1515001515150015, 0xADAD00ADADAD00AD, 0x7777007777770077, 0x8080008080800080,
+0x8282008282820082, 0xECEC00ECECEC00EC, 0x2727002727270027, 0xE5E500E5E5E500E5,
+0x8585008585850085, 0x3535003535350035, 0x0C0C000C0C0C000C, 0x4141004141410041,
+0xEFEF00EFEFEF00EF, 0x9393009393930093, 0x1919001919190019, 0x2121002121210021,
+0x0E0E000E0E0E000E, 0x4E4E004E4E4E004E, 0x6565006565650065, 0xBDBD00BDBDBD00BD,
+0xB8B800B8B8B800B8, 0x8F8F008F8F8F008F, 0xEBEB00EBEBEB00EB, 0xCECE00CECECE00CE,
+0x3030003030300030, 0x5F5F005F5F5F005F, 0xC5C500C5C5C500C5, 0x1A1A001A1A1A001A,
+0xE1E100E1E1E100E1, 0xCACA00CACACA00CA, 0x4747004747470047, 0x3D3D003D3D3D003D,
+0x0101000101010001, 0xD6D600D6D6D600D6, 0x5656005656560056, 0x4D4D004D4D4D004D,
+0x0D0D000D0D0D000D, 0x6666006666660066, 0xCCCC00CCCCCC00CC, 0x2D2D002D2D2D002D,
+0x1212001212120012, 0x2020002020200020, 0xB1B100B1B1B100B1, 0x9999009999990099,
+0x4C4C004C4C4C004C, 0xC2C200C2C2C200C2, 0x7E7E007E7E7E007E, 0x0505000505050005,
+0xB7B700B7B7B700B7, 0x3131003131310031, 0x1717001717170017, 0xD7D700D7D7D700D7,
+0x5858005858580058, 0x6161006161610061, 0x1B1B001B1B1B001B, 0x1C1C001C1C1C001C,
+0x0F0F000F0F0F000F, 0x1616001616160016, 0x1818001818180018, 0x2222002222220022,
+0x4444004444440044, 0xB2B200B2B2B200B2, 0xB5B500B5B5B500B5, 0x9191009191910091,
+0x0808000808080008, 0xA8A800A8A8A800A8, 0xFCFC00FCFCFC00FC, 0x5050005050500050,
+0xD0D000D0D0D000D0, 0x7D7D007D7D7D007D, 0x8989008989890089, 0x9797009797970097,
+0x5B5B005B5B5B005B, 0x9595009595950095, 0xFFFF00FFFFFF00FF, 0xD2D200D2D2D200D2,
+0xC4C400C4C4C400C4, 0x4848004848480048, 0xF7F700F7F7F700F7, 0xDBDB00DBDBDB00DB,
+0x0303000303030003, 0xDADA00DADADA00DA, 0x3F3F003F3F3F003F, 0x9494009494940094,
+0x5C5C005C5C5C005C, 0x0202000202020002, 0x4A4A004A4A4A004A, 0x3333003333330033,
+0x6767006767670067, 0xF3F300F3F3F300F3, 0x7F7F007F7F7F007F, 0xE2E200E2E2E200E2,
+0x9B9B009B9B9B009B, 0x2626002626260026, 0x3737003737370037, 0x3B3B003B3B3B003B,
+0x9696009696960096, 0x4B4B004B4B4B004B, 0xBEBE00BEBEBE00BE, 0x2E2E002E2E2E002E,
+0x7979007979790079, 0x8C8C008C8C8C008C, 0x6E6E006E6E6E006E, 0x8E8E008E8E8E008E,
+0xF5F500F5F5F500F5, 0xB6B600B6B6B600B6, 0xFDFD00FDFDFD00FD, 0x5959005959590059,
+0x9898009898980098, 0x6A6A006A6A6A006A, 0x4646004646460046, 0xBABA00BABABA00BA,
+0x2525002525250025, 0x4242004242420042, 0xA2A200A2A2A200A2, 0xFAFA00FAFAFA00FA,
+0x0707000707070007, 0x5555005555550055, 0xEEEE00EEEEEE00EE, 0x0A0A000A0A0A000A,
+0x4949004949490049, 0x6868006868680068, 0x3838003838380038, 0xA4A400A4A4A400A4,
+0x2828002828280028, 0x7B7B007B7B7B007B, 0xC9C900C9C9C900C9, 0xC1C100C1C1C100C1,
+0xE3E300E3E3E300E3, 0xF4F400F4F4F400F4, 0xC7C700C7C7C700C7, 0x9E9E009E9E9E009E };
+
+alignas(64) const uint64_t Camellia_SBOX8[256] = {
+0x7070700070707000, 0x8282820082828200, 0x2C2C2C002C2C2C00, 0xECECEC00ECECEC00,
+0xB3B3B300B3B3B300, 0x2727270027272700, 0xC0C0C000C0C0C000, 0xE5E5E500E5E5E500,
+0xE4E4E400E4E4E400, 0x8585850085858500, 0x5757570057575700, 0x3535350035353500,
+0xEAEAEA00EAEAEA00, 0x0C0C0C000C0C0C00, 0xAEAEAE00AEAEAE00, 0x4141410041414100,
+0x2323230023232300, 0xEFEFEF00EFEFEF00, 0x6B6B6B006B6B6B00, 0x9393930093939300,
+0x4545450045454500, 0x1919190019191900, 0xA5A5A500A5A5A500, 0x2121210021212100,
+0xEDEDED00EDEDED00, 0x0E0E0E000E0E0E00, 0x4F4F4F004F4F4F00, 0x4E4E4E004E4E4E00,
+0x1D1D1D001D1D1D00, 0x6565650065656500, 0x9292920092929200, 0xBDBDBD00BDBDBD00,
+0x8686860086868600, 0xB8B8B800B8B8B800, 0xAFAFAF00AFAFAF00, 0x8F8F8F008F8F8F00,
+0x7C7C7C007C7C7C00, 0xEBEBEB00EBEBEB00, 0x1F1F1F001F1F1F00, 0xCECECE00CECECE00,
+0x3E3E3E003E3E3E00, 0x3030300030303000, 0xDCDCDC00DCDCDC00, 0x5F5F5F005F5F5F00,
+0x5E5E5E005E5E5E00, 0xC5C5C500C5C5C500, 0x0B0B0B000B0B0B00, 0x1A1A1A001A1A1A00,
+0xA6A6A600A6A6A600, 0xE1E1E100E1E1E100, 0x3939390039393900, 0xCACACA00CACACA00,
+0xD5D5D500D5D5D500, 0x4747470047474700, 0x5D5D5D005D5D5D00, 0x3D3D3D003D3D3D00,
+0xD9D9D900D9D9D900, 0x0101010001010100, 0x5A5A5A005A5A5A00, 0xD6D6D600D6D6D600,
+0x5151510051515100, 0x5656560056565600, 0x6C6C6C006C6C6C00, 0x4D4D4D004D4D4D00,
+0x8B8B8B008B8B8B00, 0x0D0D0D000D0D0D00, 0x9A9A9A009A9A9A00, 0x6666660066666600,
+0xFBFBFB00FBFBFB00, 0xCCCCCC00CCCCCC00, 0xB0B0B000B0B0B000, 0x2D2D2D002D2D2D00,
+0x7474740074747400, 0x1212120012121200, 0x2B2B2B002B2B2B00, 0x2020200020202000,
+0xF0F0F000F0F0F000, 0xB1B1B100B1B1B100, 0x8484840084848400, 0x9999990099999900,
+0xDFDFDF00DFDFDF00, 0x4C4C4C004C4C4C00, 0xCBCBCB00CBCBCB00, 0xC2C2C200C2C2C200,
+0x3434340034343400, 0x7E7E7E007E7E7E00, 0x7676760076767600, 0x0505050005050500,
+0x6D6D6D006D6D6D00, 0xB7B7B700B7B7B700, 0xA9A9A900A9A9A900, 0x3131310031313100,
+0xD1D1D100D1D1D100, 0x1717170017171700, 0x0404040004040400, 0xD7D7D700D7D7D700,
+0x1414140014141400, 0x5858580058585800, 0x3A3A3A003A3A3A00, 0x6161610061616100,
+0xDEDEDE00DEDEDE00, 0x1B1B1B001B1B1B00, 0x1111110011111100, 0x1C1C1C001C1C1C00,
+0x3232320032323200, 0x0F0F0F000F0F0F00, 0x9C9C9C009C9C9C00, 0x1616160016161600,
+0x5353530053535300, 0x1818180018181800, 0xF2F2F200F2F2F200, 0x2222220022222200,
+0xFEFEFE00FEFEFE00, 0x4444440044444400, 0xCFCFCF00CFCFCF00, 0xB2B2B200B2B2B200,
+0xC3C3C300C3C3C300, 0xB5B5B500B5B5B500, 0x7A7A7A007A7A7A00, 0x9191910091919100,
+0x2424240024242400, 0x0808080008080800, 0xE8E8E800E8E8E800, 0xA8A8A800A8A8A800,
+0x6060600060606000, 0xFCFCFC00FCFCFC00, 0x6969690069696900, 0x5050500050505000,
+0xAAAAAA00AAAAAA00, 0xD0D0D000D0D0D000, 0xA0A0A000A0A0A000, 0x7D7D7D007D7D7D00,
+0xA1A1A100A1A1A100, 0x8989890089898900, 0x6262620062626200, 0x9797970097979700,
+0x5454540054545400, 0x5B5B5B005B5B5B00, 0x1E1E1E001E1E1E00, 0x9595950095959500,
+0xE0E0E000E0E0E000, 0xFFFFFF00FFFFFF00, 0x6464640064646400, 0xD2D2D200D2D2D200,
+0x1010100010101000, 0xC4C4C400C4C4C400, 0x0000000000000000, 0x4848480048484800,
+0xA3A3A300A3A3A300, 0xF7F7F700F7F7F700, 0x7575750075757500, 0xDBDBDB00DBDBDB00,
+0x8A8A8A008A8A8A00, 0x0303030003030300, 0xE6E6E600E6E6E600, 0xDADADA00DADADA00,
+0x0909090009090900, 0x3F3F3F003F3F3F00, 0xDDDDDD00DDDDDD00, 0x9494940094949400,
+0x8787870087878700, 0x5C5C5C005C5C5C00, 0x8383830083838300, 0x0202020002020200,
+0xCDCDCD00CDCDCD00, 0x4A4A4A004A4A4A00, 0x9090900090909000, 0x3333330033333300,
+0x7373730073737300, 0x6767670067676700, 0xF6F6F600F6F6F600, 0xF3F3F300F3F3F300,
+0x9D9D9D009D9D9D00, 0x7F7F7F007F7F7F00, 0xBFBFBF00BFBFBF00, 0xE2E2E200E2E2E200,
+0x5252520052525200, 0x9B9B9B009B9B9B00, 0xD8D8D800D8D8D800, 0x2626260026262600,
+0xC8C8C800C8C8C800, 0x3737370037373700, 0xC6C6C600C6C6C600, 0x3B3B3B003B3B3B00,
+0x8181810081818100, 0x9696960096969600, 0x6F6F6F006F6F6F00, 0x4B4B4B004B4B4B00,
+0x1313130013131300, 0xBEBEBE00BEBEBE00, 0x6363630063636300, 0x2E2E2E002E2E2E00,
+0xE9E9E900E9E9E900, 0x7979790079797900, 0xA7A7A700A7A7A700, 0x8C8C8C008C8C8C00,
+0x9F9F9F009F9F9F00, 0x6E6E6E006E6E6E00, 0xBCBCBC00BCBCBC00, 0x8E8E8E008E8E8E00,
+0x2929290029292900, 0xF5F5F500F5F5F500, 0xF9F9F900F9F9F900, 0xB6B6B600B6B6B600,
+0x2F2F2F002F2F2F00, 0xFDFDFD00FDFDFD00, 0xB4B4B400B4B4B400, 0x5959590059595900,
+0x7878780078787800, 0x9898980098989800, 0x0606060006060600, 0x6A6A6A006A6A6A00,
+0xE7E7E700E7E7E700, 0x4646460046464600, 0x7171710071717100, 0xBABABA00BABABA00,
+0xD4D4D400D4D4D400, 0x2525250025252500, 0xABABAB00ABABAB00, 0x4242420042424200,
+0x8888880088888800, 0xA2A2A200A2A2A200, 0x8D8D8D008D8D8D00, 0xFAFAFA00FAFAFA00,
+0x7272720072727200, 0x0707070007070700, 0xB9B9B900B9B9B900, 0x5555550055555500,
+0xF8F8F800F8F8F800, 0xEEEEEE00EEEEEE00, 0xACACAC00ACACAC00, 0x0A0A0A000A0A0A00,
+0x3636360036363600, 0x4949490049494900, 0x2A2A2A002A2A2A00, 0x6868680068686800,
+0x3C3C3C003C3C3C00, 0x3838380038383800, 0xF1F1F100F1F1F100, 0xA4A4A400A4A4A400,
+0x4040400040404000, 0x2828280028282800, 0xD3D3D300D3D3D300, 0x7B7B7B007B7B7B00,
+0xBBBBBB00BBBBBB00, 0xC9C9C900C9C9C900, 0x4343430043434300, 0xC1C1C100C1C1C100,
+0x1515150015151500, 0xE3E3E300E3E3E300, 0xADADAD00ADADAD00, 0xF4F4F400F4F4F400,
+0x7777770077777700, 0xC7C7C700C7C7C700, 0x8080800080808000, 0x9E9E9E009E9E9E00 };
+
+namespace Camellia_F {
+
+/*
+* We use the slow byte-wise version of F in the first and last rounds
+* to help protect against side channels analyzing cache hits on the
+* larger sbox tables.
+*/
+uint64_t F_SLOW(uint64_t v, uint64_t K)
+ {
+ alignas(64)
+ static const uint8_t SBOX[256] = {
+ 0x70, 0x82, 0x2C, 0xEC, 0xB3, 0x27, 0xC0, 0xE5, 0xE4, 0x85, 0x57,
+ 0x35, 0xEA, 0x0C, 0xAE, 0x41, 0x23, 0xEF, 0x6B, 0x93, 0x45, 0x19,
+ 0xA5, 0x21, 0xED, 0x0E, 0x4F, 0x4E, 0x1D, 0x65, 0x92, 0xBD, 0x86,
+ 0xB8, 0xAF, 0x8F, 0x7C, 0xEB, 0x1F, 0xCE, 0x3E, 0x30, 0xDC, 0x5F,
+ 0x5E, 0xC5, 0x0B, 0x1A, 0xA6, 0xE1, 0x39, 0xCA, 0xD5, 0x47, 0x5D,
+ 0x3D, 0xD9, 0x01, 0x5A, 0xD6, 0x51, 0x56, 0x6C, 0x4D, 0x8B, 0x0D,
+ 0x9A, 0x66, 0xFB, 0xCC, 0xB0, 0x2D, 0x74, 0x12, 0x2B, 0x20, 0xF0,
+ 0xB1, 0x84, 0x99, 0xDF, 0x4C, 0xCB, 0xC2, 0x34, 0x7E, 0x76, 0x05,
+ 0x6D, 0xB7, 0xA9, 0x31, 0xD1, 0x17, 0x04, 0xD7, 0x14, 0x58, 0x3A,
+ 0x61, 0xDE, 0x1B, 0x11, 0x1C, 0x32, 0x0F, 0x9C, 0x16, 0x53, 0x18,
+ 0xF2, 0x22, 0xFE, 0x44, 0xCF, 0xB2, 0xC3, 0xB5, 0x7A, 0x91, 0x24,
+ 0x08, 0xE8, 0xA8, 0x60, 0xFC, 0x69, 0x50, 0xAA, 0xD0, 0xA0, 0x7D,
+ 0xA1, 0x89, 0x62, 0x97, 0x54, 0x5B, 0x1E, 0x95, 0xE0, 0xFF, 0x64,
+ 0xD2, 0x10, 0xC4, 0x00, 0x48, 0xA3, 0xF7, 0x75, 0xDB, 0x8A, 0x03,
+ 0xE6, 0xDA, 0x09, 0x3F, 0xDD, 0x94, 0x87, 0x5C, 0x83, 0x02, 0xCD,
+ 0x4A, 0x90, 0x33, 0x73, 0x67, 0xF6, 0xF3, 0x9D, 0x7F, 0xBF, 0xE2,
+ 0x52, 0x9B, 0xD8, 0x26, 0xC8, 0x37, 0xC6, 0x3B, 0x81, 0x96, 0x6F,
+ 0x4B, 0x13, 0xBE, 0x63, 0x2E, 0xE9, 0x79, 0xA7, 0x8C, 0x9F, 0x6E,
+ 0xBC, 0x8E, 0x29, 0xF5, 0xF9, 0xB6, 0x2F, 0xFD, 0xB4, 0x59, 0x78,
+ 0x98, 0x06, 0x6A, 0xE7, 0x46, 0x71, 0xBA, 0xD4, 0x25, 0xAB, 0x42,
+ 0x88, 0xA2, 0x8D, 0xFA, 0x72, 0x07, 0xB9, 0x55, 0xF8, 0xEE, 0xAC,
+ 0x0A, 0x36, 0x49, 0x2A, 0x68, 0x3C, 0x38, 0xF1, 0xA4, 0x40, 0x28,
+ 0xD3, 0x7B, 0xBB, 0xC9, 0x43, 0xC1, 0x15, 0xE3, 0xAD, 0xF4, 0x77,
+ 0xC7, 0x80, 0x9E };
+
+ const uint64_t x = v ^ K;
+
+ const uint8_t t1 = SBOX[get_byte(0, x)];
+ const uint8_t t2 = rotl<1>(SBOX[get_byte(1, x)]);
+ const uint8_t t3 = rotl<7>(SBOX[get_byte(2, x)]);
+ const uint8_t t4 = SBOX[rotl<1>(get_byte(3, x))];
+ const uint8_t t5 = rotl<1>(SBOX[get_byte(4, x)]);
+ const uint8_t t6 = rotl<7>(SBOX[get_byte(5, x)]);
+ const uint8_t t7 = SBOX[rotl<1>(get_byte(6, x))];
+ const uint8_t t8 = SBOX[get_byte(7, x)];
+
+ const uint8_t y1 = t1 ^ t3 ^ t4 ^ t6 ^ t7 ^ t8;
+ const uint8_t y2 = t1 ^ t2 ^ t4 ^ t5 ^ t7 ^ t8;
+ const uint8_t y3 = t1 ^ t2 ^ t3 ^ t5 ^ t6 ^ t8;
+ const uint8_t y4 = t2 ^ t3 ^ t4 ^ t5 ^ t6 ^ t7;
+ const uint8_t y5 = t1 ^ t2 ^ t6 ^ t7 ^ t8;
+ const uint8_t y6 = t2 ^ t3 ^ t5 ^ t7 ^ t8;
+ const uint8_t y7 = t3 ^ t4 ^ t5 ^ t6 ^ t8;
+ const uint8_t y8 = t1 ^ t4 ^ t5 ^ t6 ^ t7;
+
+ return make_uint64(y1, y2, y3, y4, y5, y6, y7, y8);
+ }
+
+inline uint64_t F(uint64_t v, uint64_t K)
+ {
+ const uint64_t x = v ^ K;
+
+ return Camellia_SBOX1[get_byte(0, x)] ^
+ Camellia_SBOX2[get_byte(1, x)] ^
+ Camellia_SBOX3[get_byte(2, x)] ^
+ Camellia_SBOX4[get_byte(3, x)] ^
+ Camellia_SBOX5[get_byte(4, x)] ^
+ Camellia_SBOX6[get_byte(5, x)] ^
+ Camellia_SBOX7[get_byte(6, x)] ^
+ Camellia_SBOX8[get_byte(7, x)];
+ }
+
+inline uint64_t FL(uint64_t v, uint64_t K)
+ {
+ uint32_t x1 = static_cast<uint32_t>(v >> 32);
+ uint32_t x2 = static_cast<uint32_t>(v & 0xFFFFFFFF);
+
+ const uint32_t k1 = static_cast<uint32_t>(K >> 32);
+ const uint32_t k2 = static_cast<uint32_t>(K & 0xFFFFFFFF);
+
+ x2 ^= rotl<1>(x1 & k1);
+ x1 ^= (x2 | k2);
+
+ return ((static_cast<uint64_t>(x1) << 32) | x2);
+ }
+
+inline uint64_t FLINV(uint64_t v, uint64_t K)
+ {
+ uint32_t x1 = static_cast<uint32_t>(v >> 32);
+ uint32_t x2 = static_cast<uint32_t>(v & 0xFFFFFFFF);
+
+ const uint32_t k1 = static_cast<uint32_t>(K >> 32);
+ const uint32_t k2 = static_cast<uint32_t>(K & 0xFFFFFFFF);
+
+ x1 ^= (x2 | k2);
+ x2 ^= rotl<1>(x1 & k1);
+
+ return ((static_cast<uint64_t>(x1) << 32) | x2);
+ }
+
+/*
+* Camellia Encryption
+*/
+void encrypt(const uint8_t in[], uint8_t out[], size_t blocks,
+ const secure_vector<uint64_t>& SK, const size_t rounds)
+ {
+ BOTAN_PARALLEL_FOR(size_t i = 0; i < blocks; ++i)
+ {
+ uint64_t D1, D2;
+ load_be(in + 16*i, D1, D2);
+
+ const uint64_t* K = SK.data();
+
+ D1 ^= *K++;
+ D2 ^= *K++;
+
+ D2 ^= F_SLOW(D1, *K++);
+ D1 ^= F_SLOW(D2, *K++);
+
+ for(size_t r = 1; r != rounds - 1; ++r)
+ {
+ if(r % 3 == 0)
+ {
+ D1 = FL (D1, *K++);
+ D2 = FLINV(D2, *K++);
+ }
+
+ D2 ^= F(D1, *K++);
+ D1 ^= F(D2, *K++);
+ }
+
+ D2 ^= F_SLOW(D1, *K++);
+ D1 ^= F_SLOW(D2, *K++);
+
+ D2 ^= *K++;
+ D1 ^= *K++;
+
+ store_be(out + 16*i, D2, D1);
+ }
+ }
+
+/*
+* Camellia Decryption
+*/
+void decrypt(const uint8_t in[], uint8_t out[], size_t blocks,
+ const secure_vector<uint64_t>& SK, const size_t rounds)
+ {
+ BOTAN_PARALLEL_FOR(size_t i = 0; i < blocks; ++i)
+ {
+ uint64_t D1, D2;
+ load_be(in + 16*i, D1, D2);
+
+ const uint64_t* K = &SK[SK.size()-1];
+
+ D2 ^= *K--;
+ D1 ^= *K--;
+
+ D2 ^= F_SLOW(D1, *K--);
+ D1 ^= F_SLOW(D2, *K--);
+
+ for(size_t r = 1; r != rounds - 1; ++r)
+ {
+ if(r % 3 == 0)
+ {
+ D1 = FL (D1, *K--);
+ D2 = FLINV(D2, *K--);
+ }
+
+ D2 ^= F(D1, *K--);
+ D1 ^= F(D2, *K--);
+ }
+
+ D2 ^= F_SLOW(D1, *K--);
+ D1 ^= F_SLOW(D2, *K--);
+
+ D1 ^= *K--;
+ D2 ^= *K;
+
+ store_be(out + 16*i, D2, D1);
+ }
+ }
+
+uint64_t left_rot_hi(uint64_t h, uint64_t l, size_t shift)
+ {
+ return (h << shift) | (l >> (64-shift));
+ }
+
+uint64_t left_rot_lo(uint64_t h, uint64_t l, size_t shift)
+ {
+ return (h >> (64-shift)) | (l << shift);
+ }
+
+/*
+* Camellia Key Schedule
+*/
+void key_schedule(secure_vector<uint64_t>& SK, const uint8_t key[], size_t length)
+ {
+ const uint64_t Sigma1 = 0xA09E667F3BCC908B;
+ const uint64_t Sigma2 = 0xB67AE8584CAA73B2;
+ const uint64_t Sigma3 = 0xC6EF372FE94F82BE;
+ const uint64_t Sigma4 = 0x54FF53A5F1D36F1C;
+ const uint64_t Sigma5 = 0x10E527FADE682D1D;
+ const uint64_t Sigma6 = 0xB05688C2B3E6C1FD;
+
+ const uint64_t KL_H = load_be<uint64_t>(key, 0);
+ const uint64_t KL_L = load_be<uint64_t>(key, 1);
+
+ const uint64_t KR_H = (length >= 24) ? load_be<uint64_t>(key, 2) : 0;
+ const uint64_t KR_L =
+ (length == 32) ? load_be<uint64_t>(key, 3) : ((length == 24) ? ~KR_H : 0);
+
+ uint64_t D1 = KL_H ^ KR_H;
+ uint64_t D2 = KL_L ^ KR_L;
+ D2 ^= F(D1, Sigma1);
+ D1 ^= F(D2, Sigma2);
+ D1 ^= KL_H;
+ D2 ^= KL_L;
+ D2 ^= F(D1, Sigma3);
+ D1 ^= F(D2, Sigma4);
+
+ const uint64_t KA_H = D1;
+ const uint64_t KA_L = D2;
+
+ D1 = KA_H ^ KR_H;
+ D2 = KA_L ^ KR_L;
+ D2 ^= F(D1, Sigma5);
+ D1 ^= F(D2, Sigma6);
+
+ const uint64_t KB_H = D1;
+ const uint64_t KB_L = D2;
+
+ if(length == 16)
+ {
+ SK.resize(26);
+
+ SK[ 0] = KL_H;
+ SK[ 1] = KL_L;
+ SK[ 2] = KA_H;
+ SK[ 3] = KA_L;
+ SK[ 4] = left_rot_hi(KL_H, KL_L, 15);
+ SK[ 5] = left_rot_lo(KL_H, KL_L, 15);
+ SK[ 6] = left_rot_hi(KA_H, KA_L, 15);
+ SK[ 7] = left_rot_lo(KA_H, KA_L, 15);
+ SK[ 8] = left_rot_hi(KA_H, KA_L, 30);
+ SK[ 9] = left_rot_lo(KA_H, KA_L, 30);
+ SK[10] = left_rot_hi(KL_H, KL_L, 45);
+ SK[11] = left_rot_lo(KL_H, KL_L, 45);
+ SK[12] = left_rot_hi(KA_H, KA_L, 45);
+ SK[13] = left_rot_lo(KL_H, KL_L, 60);
+ SK[14] = left_rot_hi(KA_H, KA_L, 60);
+ SK[15] = left_rot_lo(KA_H, KA_L, 60);
+ SK[16] = left_rot_lo(KL_H, KL_L, 77-64);
+ SK[17] = left_rot_hi(KL_H, KL_L, 77-64);
+ SK[18] = left_rot_lo(KL_H, KL_L, 94-64);
+ SK[19] = left_rot_hi(KL_H, KL_L, 94-64);
+ SK[20] = left_rot_lo(KA_H, KA_L, 94-64);
+ SK[21] = left_rot_hi(KA_H, KA_L, 94-64);
+ SK[22] = left_rot_lo(KL_H, KL_L, 111-64);
+ SK[23] = left_rot_hi(KL_H, KL_L, 111-64);
+ SK[24] = left_rot_lo(KA_H, KA_L, 111-64);
+ SK[25] = left_rot_hi(KA_H, KA_L, 111-64);
+ }
+ else
+ {
+ SK.resize(34);
+
+ SK[ 0] = KL_H;
+ SK[ 1] = KL_L;
+ SK[ 2] = KB_H;
+ SK[ 3] = KB_L;
+
+ SK[ 4] = left_rot_hi(KR_H, KR_L, 15);
+ SK[ 5] = left_rot_lo(KR_H, KR_L, 15);
+ SK[ 6] = left_rot_hi(KA_H, KA_L, 15);
+ SK[ 7] = left_rot_lo(KA_H, KA_L, 15);
+
+ SK[ 8] = left_rot_hi(KR_H, KR_L, 30);
+ SK[ 9] = left_rot_lo(KR_H, KR_L, 30);
+ SK[10] = left_rot_hi(KB_H, KB_L, 30);
+ SK[11] = left_rot_lo(KB_H, KB_L, 30);
+
+ SK[12] = left_rot_hi(KL_H, KL_L, 45);
+ SK[13] = left_rot_lo(KL_H, KL_L, 45);
+ SK[14] = left_rot_hi(KA_H, KA_L, 45);
+ SK[15] = left_rot_lo(KA_H, KA_L, 45);
+
+ SK[16] = left_rot_hi(KL_H, KL_L, 60);
+ SK[17] = left_rot_lo(KL_H, KL_L, 60);
+ SK[18] = left_rot_hi(KR_H, KR_L, 60);
+ SK[19] = left_rot_lo(KR_H, KR_L, 60);
+ SK[20] = left_rot_hi(KB_H, KB_L, 60);
+ SK[21] = left_rot_lo(KB_H, KB_L, 60);
+
+ SK[22] = left_rot_lo(KL_H, KL_L, 77-64);
+ SK[23] = left_rot_hi(KL_H, KL_L, 77-64);
+ SK[24] = left_rot_lo(KA_H, KA_L, 77-64);
+ SK[25] = left_rot_hi(KA_H, KA_L, 77-64);
+
+ SK[26] = left_rot_lo(KR_H, KR_L, 94-64);
+ SK[27] = left_rot_hi(KR_H, KR_L, 94-64);
+ SK[28] = left_rot_lo(KA_H, KA_L, 94-64);
+ SK[29] = left_rot_hi(KA_H, KA_L, 94-64);
+ SK[30] = left_rot_lo(KL_H, KL_L, 111-64);
+ SK[31] = left_rot_hi(KL_H, KL_L, 111-64);
+ SK[32] = left_rot_lo(KB_H, KB_L, 111-64);
+ SK[33] = left_rot_hi(KB_H, KB_L, 111-64);
+ }
+ }
+
+}
+
+}
+
+void Camellia_128::encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+ {
+ verify_key_set(m_SK.empty() == false);
+ 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
+ {
+ verify_key_set(m_SK.empty() == false);
+ 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
+ {
+ verify_key_set(m_SK.empty() == false);
+ 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
+ {
+ verify_key_set(m_SK.empty() == false);
+ 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
+ {
+ verify_key_set(m_SK.empty() == false);
+ 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
+ {
+ verify_key_set(m_SK.empty() == false);
+ Camellia_F::decrypt(in, out, blocks, m_SK, 12);
+ }
+
+void Camellia_128::key_schedule(const uint8_t key[], size_t length)
+ {
+ Camellia_F::key_schedule(m_SK, key, length);
+ }
+
+void Camellia_192::key_schedule(const uint8_t key[], size_t length)
+ {
+ Camellia_F::key_schedule(m_SK, key, length);
+ }
+
+void Camellia_256::key_schedule(const uint8_t key[], size_t length)
+ {
+ Camellia_F::key_schedule(m_SK, key, length);
+ }
+
+void Camellia_128::clear()
+ {
+ zap(m_SK);
+ }
+
+void Camellia_192::clear()
+ {
+ zap(m_SK);
+ }
+
+void Camellia_256::clear()
+ {
+ zap(m_SK);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/block/camellia/camellia.h b/comm/third_party/botan/src/lib/block/camellia/camellia.h
new file mode 100644
index 0000000000..4995eb0c93
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/camellia/camellia.h
@@ -0,0 +1,73 @@
+/*
+* Camellia
+* (C) 2012 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_CAMELLIA_H_
+#define BOTAN_CAMELLIA_H_
+
+#include <botan/block_cipher.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(camellia.h)
+
+namespace Botan {
+
+/**
+* Camellia-128
+*/
+class BOTAN_PUBLIC_API(2,0) Camellia_128 final : public Block_Cipher_Fixed_Params<16, 16>
+ {
+ public:
+ void encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+ void decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+
+ void clear() override;
+ std::string name() const override { return "Camellia-128"; }
+ BlockCipher* clone() const override { return new Camellia_128; }
+ private:
+ void key_schedule(const uint8_t key[], size_t length) override;
+
+ secure_vector<uint64_t> m_SK;
+ };
+
+/**
+* Camellia-192
+*/
+class BOTAN_PUBLIC_API(2,0) Camellia_192 final : public Block_Cipher_Fixed_Params<16, 24>
+ {
+ public:
+ void encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+ void decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+
+ void clear() override;
+ std::string name() const override { return "Camellia-192"; }
+ BlockCipher* clone() const override { return new Camellia_192; }
+ private:
+ void key_schedule(const uint8_t key[], size_t length) override;
+
+ secure_vector<uint64_t> m_SK;
+ };
+
+/**
+* Camellia-256
+*/
+class BOTAN_PUBLIC_API(2,0) Camellia_256 final : public Block_Cipher_Fixed_Params<16, 32>
+ {
+ public:
+ void encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+ void decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+
+ void clear() override;
+ std::string name() const override { return "Camellia-256"; }
+ BlockCipher* clone() const override { return new Camellia_256; }
+ private:
+ void key_schedule(const uint8_t key[], size_t length) override;
+
+ secure_vector<uint64_t> m_SK;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/block/camellia/info.txt b/comm/third_party/botan/src/lib/block/camellia/info.txt
new file mode 100644
index 0000000000..c70a7f3451
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/camellia/info.txt
@@ -0,0 +1,7 @@
+<defines>
+CAMELLIA -> 20150922
+</defines>
+
+<header:public>
+camellia.h
+</header:public>
diff --git a/comm/third_party/botan/src/lib/block/cascade/cascade.cpp b/comm/third_party/botan/src/lib/block/cascade/cascade.cpp
new file mode 100644
index 0000000000..6607fd5b27
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/cascade/cascade.cpp
@@ -0,0 +1,93 @@
+/*
+* Block Cipher Cascade
+* (C) 2010 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/cascade.h>
+
+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());
+
+ 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());
+
+ m_cipher2->decrypt_n(in, out, c2_blocks);
+ m_cipher1->decrypt_n(out, out, c1_blocks);
+ }
+
+void Cascade_Cipher::key_schedule(const uint8_t key[], size_t)
+ {
+ const uint8_t* key2 = key + m_cipher1->maximum_keylength();
+
+ m_cipher1->set_key(key , m_cipher1->maximum_keylength());
+ m_cipher2->set_key(key2, m_cipher2->maximum_keylength());
+ }
+
+void Cascade_Cipher::clear()
+ {
+ m_cipher1->clear();
+ m_cipher2->clear();
+ }
+
+std::string Cascade_Cipher::name() const
+ {
+ return "Cascade(" + m_cipher1->name() + "," + m_cipher2->name() + ")";
+ }
+
+BlockCipher* Cascade_Cipher::clone() const
+ {
+ return new Cascade_Cipher(m_cipher1->clone(),
+ m_cipher2->clone());
+ }
+
+namespace {
+
+size_t euclids_algorithm(size_t a, size_t b)
+ {
+ while(b != 0)
+ {
+ size_t t = b;
+ b = a % b;
+ a = t;
+ }
+
+ return a;
+ }
+
+size_t block_size_for_cascade(size_t bs, size_t bs2)
+ {
+ if(bs == bs2)
+ return bs;
+
+ const size_t gcd = euclids_algorithm(bs, bs2);
+
+ return (bs * bs2) / gcd;
+ }
+
+}
+
+Cascade_Cipher::Cascade_Cipher(BlockCipher* c1, BlockCipher* c2) :
+ m_cipher1(c1), m_cipher2(c2)
+ {
+ m_block = block_size_for_cascade(c1->block_size(), c2->block_size());
+
+ BOTAN_ASSERT(m_block % c1->block_size() == 0 &&
+ m_block % c2->block_size() == 0,
+ "Combined block size is a multiple of each ciphers block");
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/block/cascade/cascade.h b/comm/third_party/botan/src/lib/block/cascade/cascade.h
new file mode 100644
index 0000000000..26f5133811
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/cascade/cascade.h
@@ -0,0 +1,57 @@
+/*
+* Block Cipher Cascade
+* (C) 2010 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_CASCADE_H_
+#define BOTAN_CASCADE_H_
+
+#include <botan/block_cipher.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(cascade.h)
+
+namespace Botan {
+
+/**
+* Block Cipher Cascade
+*/
+class BOTAN_PUBLIC_API(2,0) Cascade_Cipher final : public BlockCipher
+ {
+ public:
+ void encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+ void decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+
+ size_t block_size() const override { return m_block; }
+
+ Key_Length_Specification key_spec() const override
+ {
+ return Key_Length_Specification(m_cipher1->maximum_keylength() +
+ m_cipher2->maximum_keylength());
+ }
+
+ void clear() override;
+ std::string name() const override;
+ BlockCipher* clone() const override;
+
+ /**
+ * Create a cascade of two block ciphers
+ * @param cipher1 the first cipher
+ * @param cipher2 the second cipher
+ */
+ Cascade_Cipher(BlockCipher* cipher1, BlockCipher* cipher2);
+
+ Cascade_Cipher(const Cascade_Cipher&) = delete;
+ Cascade_Cipher& operator=(const Cascade_Cipher&) = delete;
+ private:
+ void key_schedule(const uint8_t[], size_t) override;
+
+ size_t m_block;
+ std::unique_ptr<BlockCipher> m_cipher1, m_cipher2;
+ };
+
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/block/cascade/info.txt b/comm/third_party/botan/src/lib/block/cascade/info.txt
new file mode 100644
index 0000000000..15f5b22627
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/cascade/info.txt
@@ -0,0 +1,7 @@
+<defines>
+CASCADE -> 20131128
+</defines>
+
+<header:public>
+cascade.h
+</header:public>
diff --git a/comm/third_party/botan/src/lib/block/cast128/cast128.cpp b/comm/third_party/botan/src/lib/block/cast128/cast128.cpp
new file mode 100644
index 0000000000..bcb273be7a
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/cast128/cast128.cpp
@@ -0,0 +1,471 @@
+/*
+* CAST-128
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/cast128.h>
+#include <botan/internal/cast_sboxes.h>
+#include <botan/loadstor.h>
+#include <botan/rotate.h>
+
+namespace Botan {
+
+namespace {
+
+/*
+* CAST-128 Round Type 1
+*/
+inline uint32_t F1(uint32_t R, uint32_t MK, uint8_t RK)
+ {
+ const uint32_t T = rotl_var(MK + R, RK);
+ return (CAST_SBOX1[get_byte(0, T)] ^ CAST_SBOX2[get_byte(1, T)]) -
+ CAST_SBOX3[get_byte(2, T)] + CAST_SBOX4[get_byte(3, T)];
+ }
+
+/*
+* CAST-128 Round Type 2
+*/
+inline uint32_t F2(uint32_t R, uint32_t MK, uint8_t RK)
+ {
+ const uint32_t T = rotl_var(MK ^ R, RK);
+ return (CAST_SBOX1[get_byte(0, T)] - CAST_SBOX2[get_byte(1, T)] +
+ CAST_SBOX3[get_byte(2, T)]) ^ CAST_SBOX4[get_byte(3, T)];
+ }
+
+/*
+* CAST-128 Round Type 3
+*/
+inline uint32_t F3(uint32_t R, uint32_t MK, uint8_t RK)
+ {
+ const uint32_t T = rotl_var(MK - R, RK);
+ return ((CAST_SBOX1[get_byte(0, T)] + CAST_SBOX2[get_byte(1, T)]) ^
+ CAST_SBOX3[get_byte(2, T)]) - CAST_SBOX4[get_byte(3, T)];
+ }
+
+}
+
+/*
+* CAST-128 Encryption
+*/
+void CAST_128::encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+ {
+ verify_key_set(m_RK.empty() == false);
+
+ while(blocks >= 2)
+ {
+ uint32_t L0, R0, L1, R1;
+ load_be(in, L0, R0, L1, R1);
+
+ L0 ^= F1(R0, m_MK[ 0], m_RK[ 0]);
+ L1 ^= F1(R1, m_MK[ 0], m_RK[ 0]);
+ R0 ^= F2(L0, m_MK[ 1], m_RK[ 1]);
+ R1 ^= F2(L1, m_MK[ 1], m_RK[ 1]);
+ L0 ^= F3(R0, m_MK[ 2], m_RK[ 2]);
+ L1 ^= F3(R1, m_MK[ 2], m_RK[ 2]);
+ R0 ^= F1(L0, m_MK[ 3], m_RK[ 3]);
+ R1 ^= F1(L1, m_MK[ 3], m_RK[ 3]);
+ L0 ^= F2(R0, m_MK[ 4], m_RK[ 4]);
+ L1 ^= F2(R1, m_MK[ 4], m_RK[ 4]);
+ R0 ^= F3(L0, m_MK[ 5], m_RK[ 5]);
+ R1 ^= F3(L1, m_MK[ 5], m_RK[ 5]);
+ L0 ^= F1(R0, m_MK[ 6], m_RK[ 6]);
+ L1 ^= F1(R1, m_MK[ 6], m_RK[ 6]);
+ R0 ^= F2(L0, m_MK[ 7], m_RK[ 7]);
+ R1 ^= F2(L1, m_MK[ 7], m_RK[ 7]);
+ L0 ^= F3(R0, m_MK[ 8], m_RK[ 8]);
+ L1 ^= F3(R1, m_MK[ 8], m_RK[ 8]);
+ R0 ^= F1(L0, m_MK[ 9], m_RK[ 9]);
+ R1 ^= F1(L1, m_MK[ 9], m_RK[ 9]);
+ L0 ^= F2(R0, m_MK[10], m_RK[10]);
+ L1 ^= F2(R1, m_MK[10], m_RK[10]);
+ R0 ^= F3(L0, m_MK[11], m_RK[11]);
+ R1 ^= F3(L1, m_MK[11], m_RK[11]);
+ L0 ^= F1(R0, m_MK[12], m_RK[12]);
+ L1 ^= F1(R1, m_MK[12], m_RK[12]);
+ R0 ^= F2(L0, m_MK[13], m_RK[13]);
+ R1 ^= F2(L1, m_MK[13], m_RK[13]);
+ L0 ^= F3(R0, m_MK[14], m_RK[14]);
+ L1 ^= F3(R1, m_MK[14], m_RK[14]);
+ R0 ^= F1(L0, m_MK[15], m_RK[15]);
+ R1 ^= F1(L1, m_MK[15], m_RK[15]);
+
+ store_be(out, R0, L0, R1, L1);
+
+ blocks -= 2;
+ out += 2 * BLOCK_SIZE;
+ in += 2 * BLOCK_SIZE;
+ }
+
+ if(blocks)
+ {
+ uint32_t L, R;
+ load_be(in, L, R);
+
+ L ^= F1(R, m_MK[ 0], m_RK[ 0]);
+ R ^= F2(L, m_MK[ 1], m_RK[ 1]);
+ L ^= F3(R, m_MK[ 2], m_RK[ 2]);
+ R ^= F1(L, m_MK[ 3], m_RK[ 3]);
+ L ^= F2(R, m_MK[ 4], m_RK[ 4]);
+ R ^= F3(L, m_MK[ 5], m_RK[ 5]);
+ L ^= F1(R, m_MK[ 6], m_RK[ 6]);
+ R ^= F2(L, m_MK[ 7], m_RK[ 7]);
+ L ^= F3(R, m_MK[ 8], m_RK[ 8]);
+ R ^= F1(L, m_MK[ 9], m_RK[ 9]);
+ L ^= F2(R, m_MK[10], m_RK[10]);
+ R ^= F3(L, m_MK[11], m_RK[11]);
+ L ^= F1(R, m_MK[12], m_RK[12]);
+ R ^= F2(L, m_MK[13], m_RK[13]);
+ L ^= F3(R, m_MK[14], m_RK[14]);
+ R ^= F1(L, m_MK[15], m_RK[15]);
+
+ store_be(out, R, L);
+ }
+ }
+
+/*
+* CAST-128 Decryption
+*/
+void CAST_128::decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+ {
+ verify_key_set(m_RK.empty() == false);
+
+ while(blocks >= 2)
+ {
+ uint32_t L0, R0, L1, R1;
+ load_be(in, L0, R0, L1, R1);
+
+ L0 ^= F1(R0, m_MK[15], m_RK[15]);
+ L1 ^= F1(R1, m_MK[15], m_RK[15]);
+ R0 ^= F3(L0, m_MK[14], m_RK[14]);
+ R1 ^= F3(L1, m_MK[14], m_RK[14]);
+ L0 ^= F2(R0, m_MK[13], m_RK[13]);
+ L1 ^= F2(R1, m_MK[13], m_RK[13]);
+ R0 ^= F1(L0, m_MK[12], m_RK[12]);
+ R1 ^= F1(L1, m_MK[12], m_RK[12]);
+ L0 ^= F3(R0, m_MK[11], m_RK[11]);
+ L1 ^= F3(R1, m_MK[11], m_RK[11]);
+ R0 ^= F2(L0, m_MK[10], m_RK[10]);
+ R1 ^= F2(L1, m_MK[10], m_RK[10]);
+ L0 ^= F1(R0, m_MK[ 9], m_RK[ 9]);
+ L1 ^= F1(R1, m_MK[ 9], m_RK[ 9]);
+ R0 ^= F3(L0, m_MK[ 8], m_RK[ 8]);
+ R1 ^= F3(L1, m_MK[ 8], m_RK[ 8]);
+ L0 ^= F2(R0, m_MK[ 7], m_RK[ 7]);
+ L1 ^= F2(R1, m_MK[ 7], m_RK[ 7]);
+ R0 ^= F1(L0, m_MK[ 6], m_RK[ 6]);
+ R1 ^= F1(L1, m_MK[ 6], m_RK[ 6]);
+ L0 ^= F3(R0, m_MK[ 5], m_RK[ 5]);
+ L1 ^= F3(R1, m_MK[ 5], m_RK[ 5]);
+ R0 ^= F2(L0, m_MK[ 4], m_RK[ 4]);
+ R1 ^= F2(L1, m_MK[ 4], m_RK[ 4]);
+ L0 ^= F1(R0, m_MK[ 3], m_RK[ 3]);
+ L1 ^= F1(R1, m_MK[ 3], m_RK[ 3]);
+ R0 ^= F3(L0, m_MK[ 2], m_RK[ 2]);
+ R1 ^= F3(L1, m_MK[ 2], m_RK[ 2]);
+ L0 ^= F2(R0, m_MK[ 1], m_RK[ 1]);
+ L1 ^= F2(R1, m_MK[ 1], m_RK[ 1]);
+ R0 ^= F1(L0, m_MK[ 0], m_RK[ 0]);
+ R1 ^= F1(L1, m_MK[ 0], m_RK[ 0]);
+
+ store_be(out, R0, L0, R1, L1);
+
+ blocks -= 2;
+ out += 2 * BLOCK_SIZE;
+ in += 2 * BLOCK_SIZE;
+ }
+
+ if(blocks)
+ {
+ uint32_t L, R;
+ load_be(in, L, R);
+
+ L ^= F1(R, m_MK[15], m_RK[15]);
+ R ^= F3(L, m_MK[14], m_RK[14]);
+ L ^= F2(R, m_MK[13], m_RK[13]);
+ R ^= F1(L, m_MK[12], m_RK[12]);
+ L ^= F3(R, m_MK[11], m_RK[11]);
+ R ^= F2(L, m_MK[10], m_RK[10]);
+ L ^= F1(R, m_MK[ 9], m_RK[ 9]);
+ R ^= F3(L, m_MK[ 8], m_RK[ 8]);
+ L ^= F2(R, m_MK[ 7], m_RK[ 7]);
+ R ^= F1(L, m_MK[ 6], m_RK[ 6]);
+ L ^= F3(R, m_MK[ 5], m_RK[ 5]);
+ R ^= F2(L, m_MK[ 4], m_RK[ 4]);
+ L ^= F1(R, m_MK[ 3], m_RK[ 3]);
+ R ^= F3(L, m_MK[ 2], m_RK[ 2]);
+ L ^= F2(R, m_MK[ 1], m_RK[ 1]);
+ R ^= F1(L, m_MK[ 0], m_RK[ 0]);
+
+ store_be(out, R, L);
+ }
+ }
+
+/*
+* CAST-128 Key Schedule
+*/
+void CAST_128::key_schedule(const uint8_t key[], size_t length)
+ {
+ m_MK.resize(48);
+ m_RK.resize(48);
+
+ secure_vector<uint8_t> key16(16);
+ copy_mem(key16.data(), key, length);
+
+ secure_vector<uint32_t> X(4);
+ for(size_t i = 0; i != 4; ++i)
+ X[i] = load_be<uint32_t>(key16.data(), i);
+
+ cast_ks(m_MK, X);
+
+ secure_vector<uint32_t> RK32(48);
+ cast_ks(RK32, X);
+
+ for(size_t i = 0; i != 16; ++i)
+ m_RK[i] = RK32[i] % 32;
+ }
+
+void CAST_128::clear()
+ {
+ zap(m_MK);
+ zap(m_RK);
+ }
+
+/*
+* S-Box Based Key Expansion
+*/
+void CAST_128::cast_ks(secure_vector<uint32_t>& K,
+ secure_vector<uint32_t>& X)
+ {
+ alignas(64) static const uint32_t S5[256] = {
+ 0x7EC90C04, 0x2C6E74B9, 0x9B0E66DF, 0xA6337911, 0xB86A7FFF, 0x1DD358F5,
+ 0x44DD9D44, 0x1731167F, 0x08FBF1FA, 0xE7F511CC, 0xD2051B00, 0x735ABA00,
+ 0x2AB722D8, 0x386381CB, 0xACF6243A, 0x69BEFD7A, 0xE6A2E77F, 0xF0C720CD,
+ 0xC4494816, 0xCCF5C180, 0x38851640, 0x15B0A848, 0xE68B18CB, 0x4CAADEFF,
+ 0x5F480A01, 0x0412B2AA, 0x259814FC, 0x41D0EFE2, 0x4E40B48D, 0x248EB6FB,
+ 0x8DBA1CFE, 0x41A99B02, 0x1A550A04, 0xBA8F65CB, 0x7251F4E7, 0x95A51725,
+ 0xC106ECD7, 0x97A5980A, 0xC539B9AA, 0x4D79FE6A, 0xF2F3F763, 0x68AF8040,
+ 0xED0C9E56, 0x11B4958B, 0xE1EB5A88, 0x8709E6B0, 0xD7E07156, 0x4E29FEA7,
+ 0x6366E52D, 0x02D1C000, 0xC4AC8E05, 0x9377F571, 0x0C05372A, 0x578535F2,
+ 0x2261BE02, 0xD642A0C9, 0xDF13A280, 0x74B55BD2, 0x682199C0, 0xD421E5EC,
+ 0x53FB3CE8, 0xC8ADEDB3, 0x28A87FC9, 0x3D959981, 0x5C1FF900, 0xFE38D399,
+ 0x0C4EFF0B, 0x062407EA, 0xAA2F4FB1, 0x4FB96976, 0x90C79505, 0xB0A8A774,
+ 0xEF55A1FF, 0xE59CA2C2, 0xA6B62D27, 0xE66A4263, 0xDF65001F, 0x0EC50966,
+ 0xDFDD55BC, 0x29DE0655, 0x911E739A, 0x17AF8975, 0x32C7911C, 0x89F89468,
+ 0x0D01E980, 0x524755F4, 0x03B63CC9, 0x0CC844B2, 0xBCF3F0AA, 0x87AC36E9,
+ 0xE53A7426, 0x01B3D82B, 0x1A9E7449, 0x64EE2D7E, 0xCDDBB1DA, 0x01C94910,
+ 0xB868BF80, 0x0D26F3FD, 0x9342EDE7, 0x04A5C284, 0x636737B6, 0x50F5B616,
+ 0xF24766E3, 0x8ECA36C1, 0x136E05DB, 0xFEF18391, 0xFB887A37, 0xD6E7F7D4,
+ 0xC7FB7DC9, 0x3063FCDF, 0xB6F589DE, 0xEC2941DA, 0x26E46695, 0xB7566419,
+ 0xF654EFC5, 0xD08D58B7, 0x48925401, 0xC1BACB7F, 0xE5FF550F, 0xB6083049,
+ 0x5BB5D0E8, 0x87D72E5A, 0xAB6A6EE1, 0x223A66CE, 0xC62BF3CD, 0x9E0885F9,
+ 0x68CB3E47, 0x086C010F, 0xA21DE820, 0xD18B69DE, 0xF3F65777, 0xFA02C3F6,
+ 0x407EDAC3, 0xCBB3D550, 0x1793084D, 0xB0D70EBA, 0x0AB378D5, 0xD951FB0C,
+ 0xDED7DA56, 0x4124BBE4, 0x94CA0B56, 0x0F5755D1, 0xE0E1E56E, 0x6184B5BE,
+ 0x580A249F, 0x94F74BC0, 0xE327888E, 0x9F7B5561, 0xC3DC0280, 0x05687715,
+ 0x646C6BD7, 0x44904DB3, 0x66B4F0A3, 0xC0F1648A, 0x697ED5AF, 0x49E92FF6,
+ 0x309E374F, 0x2CB6356A, 0x85808573, 0x4991F840, 0x76F0AE02, 0x083BE84D,
+ 0x28421C9A, 0x44489406, 0x736E4CB8, 0xC1092910, 0x8BC95FC6, 0x7D869CF4,
+ 0x134F616F, 0x2E77118D, 0xB31B2BE1, 0xAA90B472, 0x3CA5D717, 0x7D161BBA,
+ 0x9CAD9010, 0xAF462BA2, 0x9FE459D2, 0x45D34559, 0xD9F2DA13, 0xDBC65487,
+ 0xF3E4F94E, 0x176D486F, 0x097C13EA, 0x631DA5C7, 0x445F7382, 0x175683F4,
+ 0xCDC66A97, 0x70BE0288, 0xB3CDCF72, 0x6E5DD2F3, 0x20936079, 0x459B80A5,
+ 0xBE60E2DB, 0xA9C23101, 0xEBA5315C, 0x224E42F2, 0x1C5C1572, 0xF6721B2C,
+ 0x1AD2FFF3, 0x8C25404E, 0x324ED72F, 0x4067B7FD, 0x0523138E, 0x5CA3BC78,
+ 0xDC0FD66E, 0x75922283, 0x784D6B17, 0x58EBB16E, 0x44094F85, 0x3F481D87,
+ 0xFCFEAE7B, 0x77B5FF76, 0x8C2302BF, 0xAAF47556, 0x5F46B02A, 0x2B092801,
+ 0x3D38F5F7, 0x0CA81F36, 0x52AF4A8A, 0x66D5E7C0, 0xDF3B0874, 0x95055110,
+ 0x1B5AD7A8, 0xF61ED5AD, 0x6CF6E479, 0x20758184, 0xD0CEFA65, 0x88F7BE58,
+ 0x4A046826, 0x0FF6F8F3, 0xA09C7F70, 0x5346ABA0, 0x5CE96C28, 0xE176EDA3,
+ 0x6BAC307F, 0x376829D2, 0x85360FA9, 0x17E3FE2A, 0x24B79767, 0xF5A96B20,
+ 0xD6CD2595, 0x68FF1EBF, 0x7555442C, 0xF19F06BE, 0xF9E0659A, 0xEEB9491D,
+ 0x34010718, 0xBB30CAB8, 0xE822FE15, 0x88570983, 0x750E6249, 0xDA627E55,
+ 0x5E76FFA8, 0xB1534546, 0x6D47DE08, 0xEFE9E7D4 };
+
+ alignas(64) static const uint32_t S6[256] = {
+ 0xF6FA8F9D, 0x2CAC6CE1, 0x4CA34867, 0xE2337F7C, 0x95DB08E7, 0x016843B4,
+ 0xECED5CBC, 0x325553AC, 0xBF9F0960, 0xDFA1E2ED, 0x83F0579D, 0x63ED86B9,
+ 0x1AB6A6B8, 0xDE5EBE39, 0xF38FF732, 0x8989B138, 0x33F14961, 0xC01937BD,
+ 0xF506C6DA, 0xE4625E7E, 0xA308EA99, 0x4E23E33C, 0x79CBD7CC, 0x48A14367,
+ 0xA3149619, 0xFEC94BD5, 0xA114174A, 0xEAA01866, 0xA084DB2D, 0x09A8486F,
+ 0xA888614A, 0x2900AF98, 0x01665991, 0xE1992863, 0xC8F30C60, 0x2E78EF3C,
+ 0xD0D51932, 0xCF0FEC14, 0xF7CA07D2, 0xD0A82072, 0xFD41197E, 0x9305A6B0,
+ 0xE86BE3DA, 0x74BED3CD, 0x372DA53C, 0x4C7F4448, 0xDAB5D440, 0x6DBA0EC3,
+ 0x083919A7, 0x9FBAEED9, 0x49DBCFB0, 0x4E670C53, 0x5C3D9C01, 0x64BDB941,
+ 0x2C0E636A, 0xBA7DD9CD, 0xEA6F7388, 0xE70BC762, 0x35F29ADB, 0x5C4CDD8D,
+ 0xF0D48D8C, 0xB88153E2, 0x08A19866, 0x1AE2EAC8, 0x284CAF89, 0xAA928223,
+ 0x9334BE53, 0x3B3A21BF, 0x16434BE3, 0x9AEA3906, 0xEFE8C36E, 0xF890CDD9,
+ 0x80226DAE, 0xC340A4A3, 0xDF7E9C09, 0xA694A807, 0x5B7C5ECC, 0x221DB3A6,
+ 0x9A69A02F, 0x68818A54, 0xCEB2296F, 0x53C0843A, 0xFE893655, 0x25BFE68A,
+ 0xB4628ABC, 0xCF222EBF, 0x25AC6F48, 0xA9A99387, 0x53BDDB65, 0xE76FFBE7,
+ 0xE967FD78, 0x0BA93563, 0x8E342BC1, 0xE8A11BE9, 0x4980740D, 0xC8087DFC,
+ 0x8DE4BF99, 0xA11101A0, 0x7FD37975, 0xDA5A26C0, 0xE81F994F, 0x9528CD89,
+ 0xFD339FED, 0xB87834BF, 0x5F04456D, 0x22258698, 0xC9C4C83B, 0x2DC156BE,
+ 0x4F628DAA, 0x57F55EC5, 0xE2220ABE, 0xD2916EBF, 0x4EC75B95, 0x24F2C3C0,
+ 0x42D15D99, 0xCD0D7FA0, 0x7B6E27FF, 0xA8DC8AF0, 0x7345C106, 0xF41E232F,
+ 0x35162386, 0xE6EA8926, 0x3333B094, 0x157EC6F2, 0x372B74AF, 0x692573E4,
+ 0xE9A9D848, 0xF3160289, 0x3A62EF1D, 0xA787E238, 0xF3A5F676, 0x74364853,
+ 0x20951063, 0x4576698D, 0xB6FAD407, 0x592AF950, 0x36F73523, 0x4CFB6E87,
+ 0x7DA4CEC0, 0x6C152DAA, 0xCB0396A8, 0xC50DFE5D, 0xFCD707AB, 0x0921C42F,
+ 0x89DFF0BB, 0x5FE2BE78, 0x448F4F33, 0x754613C9, 0x2B05D08D, 0x48B9D585,
+ 0xDC049441, 0xC8098F9B, 0x7DEDE786, 0xC39A3373, 0x42410005, 0x6A091751,
+ 0x0EF3C8A6, 0x890072D6, 0x28207682, 0xA9A9F7BE, 0xBF32679D, 0xD45B5B75,
+ 0xB353FD00, 0xCBB0E358, 0x830F220A, 0x1F8FB214, 0xD372CF08, 0xCC3C4A13,
+ 0x8CF63166, 0x061C87BE, 0x88C98F88, 0x6062E397, 0x47CF8E7A, 0xB6C85283,
+ 0x3CC2ACFB, 0x3FC06976, 0x4E8F0252, 0x64D8314D, 0xDA3870E3, 0x1E665459,
+ 0xC10908F0, 0x513021A5, 0x6C5B68B7, 0x822F8AA0, 0x3007CD3E, 0x74719EEF,
+ 0xDC872681, 0x073340D4, 0x7E432FD9, 0x0C5EC241, 0x8809286C, 0xF592D891,
+ 0x08A930F6, 0x957EF305, 0xB7FBFFBD, 0xC266E96F, 0x6FE4AC98, 0xB173ECC0,
+ 0xBC60B42A, 0x953498DA, 0xFBA1AE12, 0x2D4BD736, 0x0F25FAAB, 0xA4F3FCEB,
+ 0xE2969123, 0x257F0C3D, 0x9348AF49, 0x361400BC, 0xE8816F4A, 0x3814F200,
+ 0xA3F94043, 0x9C7A54C2, 0xBC704F57, 0xDA41E7F9, 0xC25AD33A, 0x54F4A084,
+ 0xB17F5505, 0x59357CBE, 0xEDBD15C8, 0x7F97C5AB, 0xBA5AC7B5, 0xB6F6DEAF,
+ 0x3A479C3A, 0x5302DA25, 0x653D7E6A, 0x54268D49, 0x51A477EA, 0x5017D55B,
+ 0xD7D25D88, 0x44136C76, 0x0404A8C8, 0xB8E5A121, 0xB81A928A, 0x60ED5869,
+ 0x97C55B96, 0xEAEC991B, 0x29935913, 0x01FDB7F1, 0x088E8DFA, 0x9AB6F6F5,
+ 0x3B4CBF9F, 0x4A5DE3AB, 0xE6051D35, 0xA0E1D855, 0xD36B4CF1, 0xF544EDEB,
+ 0xB0E93524, 0xBEBB8FBD, 0xA2D762CF, 0x49C92F54, 0x38B5F331, 0x7128A454,
+ 0x48392905, 0xA65B1DB8, 0x851C97BD, 0xD675CF2F };
+
+ alignas(64) static const uint32_t S7[256] = {
+ 0x85E04019, 0x332BF567, 0x662DBFFF, 0xCFC65693, 0x2A8D7F6F, 0xAB9BC912,
+ 0xDE6008A1, 0x2028DA1F, 0x0227BCE7, 0x4D642916, 0x18FAC300, 0x50F18B82,
+ 0x2CB2CB11, 0xB232E75C, 0x4B3695F2, 0xB28707DE, 0xA05FBCF6, 0xCD4181E9,
+ 0xE150210C, 0xE24EF1BD, 0xB168C381, 0xFDE4E789, 0x5C79B0D8, 0x1E8BFD43,
+ 0x4D495001, 0x38BE4341, 0x913CEE1D, 0x92A79C3F, 0x089766BE, 0xBAEEADF4,
+ 0x1286BECF, 0xB6EACB19, 0x2660C200, 0x7565BDE4, 0x64241F7A, 0x8248DCA9,
+ 0xC3B3AD66, 0x28136086, 0x0BD8DFA8, 0x356D1CF2, 0x107789BE, 0xB3B2E9CE,
+ 0x0502AA8F, 0x0BC0351E, 0x166BF52A, 0xEB12FF82, 0xE3486911, 0xD34D7516,
+ 0x4E7B3AFF, 0x5F43671B, 0x9CF6E037, 0x4981AC83, 0x334266CE, 0x8C9341B7,
+ 0xD0D854C0, 0xCB3A6C88, 0x47BC2829, 0x4725BA37, 0xA66AD22B, 0x7AD61F1E,
+ 0x0C5CBAFA, 0x4437F107, 0xB6E79962, 0x42D2D816, 0x0A961288, 0xE1A5C06E,
+ 0x13749E67, 0x72FC081A, 0xB1D139F7, 0xF9583745, 0xCF19DF58, 0xBEC3F756,
+ 0xC06EBA30, 0x07211B24, 0x45C28829, 0xC95E317F, 0xBC8EC511, 0x38BC46E9,
+ 0xC6E6FA14, 0xBAE8584A, 0xAD4EBC46, 0x468F508B, 0x7829435F, 0xF124183B,
+ 0x821DBA9F, 0xAFF60FF4, 0xEA2C4E6D, 0x16E39264, 0x92544A8B, 0x009B4FC3,
+ 0xABA68CED, 0x9AC96F78, 0x06A5B79A, 0xB2856E6E, 0x1AEC3CA9, 0xBE838688,
+ 0x0E0804E9, 0x55F1BE56, 0xE7E5363B, 0xB3A1F25D, 0xF7DEBB85, 0x61FE033C,
+ 0x16746233, 0x3C034C28, 0xDA6D0C74, 0x79AAC56C, 0x3CE4E1AD, 0x51F0C802,
+ 0x98F8F35A, 0x1626A49F, 0xEED82B29, 0x1D382FE3, 0x0C4FB99A, 0xBB325778,
+ 0x3EC6D97B, 0x6E77A6A9, 0xCB658B5C, 0xD45230C7, 0x2BD1408B, 0x60C03EB7,
+ 0xB9068D78, 0xA33754F4, 0xF430C87D, 0xC8A71302, 0xB96D8C32, 0xEBD4E7BE,
+ 0xBE8B9D2D, 0x7979FB06, 0xE7225308, 0x8B75CF77, 0x11EF8DA4, 0xE083C858,
+ 0x8D6B786F, 0x5A6317A6, 0xFA5CF7A0, 0x5DDA0033, 0xF28EBFB0, 0xF5B9C310,
+ 0xA0EAC280, 0x08B9767A, 0xA3D9D2B0, 0x79D34217, 0x021A718D, 0x9AC6336A,
+ 0x2711FD60, 0x438050E3, 0x069908A8, 0x3D7FEDC4, 0x826D2BEF, 0x4EEB8476,
+ 0x488DCF25, 0x36C9D566, 0x28E74E41, 0xC2610ACA, 0x3D49A9CF, 0xBAE3B9DF,
+ 0xB65F8DE6, 0x92AEAF64, 0x3AC7D5E6, 0x9EA80509, 0xF22B017D, 0xA4173F70,
+ 0xDD1E16C3, 0x15E0D7F9, 0x50B1B887, 0x2B9F4FD5, 0x625ABA82, 0x6A017962,
+ 0x2EC01B9C, 0x15488AA9, 0xD716E740, 0x40055A2C, 0x93D29A22, 0xE32DBF9A,
+ 0x058745B9, 0x3453DC1E, 0xD699296E, 0x496CFF6F, 0x1C9F4986, 0xDFE2ED07,
+ 0xB87242D1, 0x19DE7EAE, 0x053E561A, 0x15AD6F8C, 0x66626C1C, 0x7154C24C,
+ 0xEA082B2A, 0x93EB2939, 0x17DCB0F0, 0x58D4F2AE, 0x9EA294FB, 0x52CF564C,
+ 0x9883FE66, 0x2EC40581, 0x763953C3, 0x01D6692E, 0xD3A0C108, 0xA1E7160E,
+ 0xE4F2DFA6, 0x693ED285, 0x74904698, 0x4C2B0EDD, 0x4F757656, 0x5D393378,
+ 0xA132234F, 0x3D321C5D, 0xC3F5E194, 0x4B269301, 0xC79F022F, 0x3C997E7E,
+ 0x5E4F9504, 0x3FFAFBBD, 0x76F7AD0E, 0x296693F4, 0x3D1FCE6F, 0xC61E45BE,
+ 0xD3B5AB34, 0xF72BF9B7, 0x1B0434C0, 0x4E72B567, 0x5592A33D, 0xB5229301,
+ 0xCFD2A87F, 0x60AEB767, 0x1814386B, 0x30BCC33D, 0x38A0C07D, 0xFD1606F2,
+ 0xC363519B, 0x589DD390, 0x5479F8E6, 0x1CB8D647, 0x97FD61A9, 0xEA7759F4,
+ 0x2D57539D, 0x569A58CF, 0xE84E63AD, 0x462E1B78, 0x6580F87E, 0xF3817914,
+ 0x91DA55F4, 0x40A230F3, 0xD1988F35, 0xB6E318D2, 0x3FFA50BC, 0x3D40F021,
+ 0xC3C0BDAE, 0x4958C24C, 0x518F36B2, 0x84B1D370, 0x0FEDCE83, 0x878DDADA,
+ 0xF2A279C7, 0x94E01BE8, 0x90716F4B, 0x954B8AA3 };
+
+ alignas(64) static const uint32_t S8[256] = {
+ 0xE216300D, 0xBBDDFFFC, 0xA7EBDABD, 0x35648095, 0x7789F8B7, 0xE6C1121B,
+ 0x0E241600, 0x052CE8B5, 0x11A9CFB0, 0xE5952F11, 0xECE7990A, 0x9386D174,
+ 0x2A42931C, 0x76E38111, 0xB12DEF3A, 0x37DDDDFC, 0xDE9ADEB1, 0x0A0CC32C,
+ 0xBE197029, 0x84A00940, 0xBB243A0F, 0xB4D137CF, 0xB44E79F0, 0x049EEDFD,
+ 0x0B15A15D, 0x480D3168, 0x8BBBDE5A, 0x669DED42, 0xC7ECE831, 0x3F8F95E7,
+ 0x72DF191B, 0x7580330D, 0x94074251, 0x5C7DCDFA, 0xABBE6D63, 0xAA402164,
+ 0xB301D40A, 0x02E7D1CA, 0x53571DAE, 0x7A3182A2, 0x12A8DDEC, 0xFDAA335D,
+ 0x176F43E8, 0x71FB46D4, 0x38129022, 0xCE949AD4, 0xB84769AD, 0x965BD862,
+ 0x82F3D055, 0x66FB9767, 0x15B80B4E, 0x1D5B47A0, 0x4CFDE06F, 0xC28EC4B8,
+ 0x57E8726E, 0x647A78FC, 0x99865D44, 0x608BD593, 0x6C200E03, 0x39DC5FF6,
+ 0x5D0B00A3, 0xAE63AFF2, 0x7E8BD632, 0x70108C0C, 0xBBD35049, 0x2998DF04,
+ 0x980CF42A, 0x9B6DF491, 0x9E7EDD53, 0x06918548, 0x58CB7E07, 0x3B74EF2E,
+ 0x522FFFB1, 0xD24708CC, 0x1C7E27CD, 0xA4EB215B, 0x3CF1D2E2, 0x19B47A38,
+ 0x424F7618, 0x35856039, 0x9D17DEE7, 0x27EB35E6, 0xC9AFF67B, 0x36BAF5B8,
+ 0x09C467CD, 0xC18910B1, 0xE11DBF7B, 0x06CD1AF8, 0x7170C608, 0x2D5E3354,
+ 0xD4DE495A, 0x64C6D006, 0xBCC0C62C, 0x3DD00DB3, 0x708F8F34, 0x77D51B42,
+ 0x264F620F, 0x24B8D2BF, 0x15C1B79E, 0x46A52564, 0xF8D7E54E, 0x3E378160,
+ 0x7895CDA5, 0x859C15A5, 0xE6459788, 0xC37BC75F, 0xDB07BA0C, 0x0676A3AB,
+ 0x7F229B1E, 0x31842E7B, 0x24259FD7, 0xF8BEF472, 0x835FFCB8, 0x6DF4C1F2,
+ 0x96F5B195, 0xFD0AF0FC, 0xB0FE134C, 0xE2506D3D, 0x4F9B12EA, 0xF215F225,
+ 0xA223736F, 0x9FB4C428, 0x25D04979, 0x34C713F8, 0xC4618187, 0xEA7A6E98,
+ 0x7CD16EFC, 0x1436876C, 0xF1544107, 0xBEDEEE14, 0x56E9AF27, 0xA04AA441,
+ 0x3CF7C899, 0x92ECBAE6, 0xDD67016D, 0x151682EB, 0xA842EEDF, 0xFDBA60B4,
+ 0xF1907B75, 0x20E3030F, 0x24D8C29E, 0xE139673B, 0xEFA63FB8, 0x71873054,
+ 0xB6F2CF3B, 0x9F326442, 0xCB15A4CC, 0xB01A4504, 0xF1E47D8D, 0x844A1BE5,
+ 0xBAE7DFDC, 0x42CBDA70, 0xCD7DAE0A, 0x57E85B7A, 0xD53F5AF6, 0x20CF4D8C,
+ 0xCEA4D428, 0x79D130A4, 0x3486EBFB, 0x33D3CDDC, 0x77853B53, 0x37EFFCB5,
+ 0xC5068778, 0xE580B3E6, 0x4E68B8F4, 0xC5C8B37E, 0x0D809EA2, 0x398FEB7C,
+ 0x132A4F94, 0x43B7950E, 0x2FEE7D1C, 0x223613BD, 0xDD06CAA2, 0x37DF932B,
+ 0xC4248289, 0xACF3EBC3, 0x5715F6B7, 0xEF3478DD, 0xF267616F, 0xC148CBE4,
+ 0x9052815E, 0x5E410FAB, 0xB48A2465, 0x2EDA7FA4, 0xE87B40E4, 0xE98EA084,
+ 0x5889E9E1, 0xEFD390FC, 0xDD07D35B, 0xDB485694, 0x38D7E5B2, 0x57720101,
+ 0x730EDEBC, 0x5B643113, 0x94917E4F, 0x503C2FBA, 0x646F1282, 0x7523D24A,
+ 0xE0779695, 0xF9C17A8F, 0x7A5B2121, 0xD187B896, 0x29263A4D, 0xBA510CDF,
+ 0x81F47C9F, 0xAD1163ED, 0xEA7B5965, 0x1A00726E, 0x11403092, 0x00DA6D77,
+ 0x4A0CDD61, 0xAD1F4603, 0x605BDFB0, 0x9EEDC364, 0x22EBE6A8, 0xCEE7D28A,
+ 0xA0E736A0, 0x5564A6B9, 0x10853209, 0xC7EB8F37, 0x2DE705CA, 0x8951570F,
+ 0xDF09822B, 0xBD691A6C, 0xAA12E4F2, 0x87451C0F, 0xE0F6A27A, 0x3ADA4819,
+ 0x4CF1764F, 0x0D771C2B, 0x67CDB156, 0x350D8384, 0x5938FA0F, 0x42399EF3,
+ 0x36997B07, 0x0E84093D, 0x4AA93E61, 0x8360D87B, 0x1FA98B0C, 0x1149382C,
+ 0xE97625A5, 0x0614D1B7, 0x0E25244B, 0x0C768347, 0x589E8D82, 0x0D2059D1,
+ 0xA466BB1E, 0xF8DA0A82, 0x04F19130, 0xBA6E4EC0, 0x99265164, 0x1EE7230D,
+ 0x50B2AD80, 0xEAEE6801, 0x8DB2A283, 0xEA8BF59E };
+
+ class ByteReader final
+ {
+ public:
+ uint8_t operator()(size_t i) const
+ {
+ return static_cast<uint8_t>(m_X[i/4] >> (8*(3 - (i%4))));
+ }
+
+ explicit ByteReader(const uint32_t* x) : m_X(x) {}
+ private:
+ const uint32_t* m_X;
+ };
+
+ secure_vector<uint32_t> Z(4);
+ ByteReader x(X.data()), 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)];
+ Z[2] = X[3] ^ S5[z( 7)] ^ S6[z( 6)] ^ S7[z( 5)] ^ S8[z( 4)] ^ S5[x( 9)];
+ Z[3] = X[1] ^ S5[z(10)] ^ S6[z( 9)] ^ S7[z(11)] ^ S8[z( 8)] ^ S6[x(11)];
+ K[ 0] = S5[z( 8)] ^ S6[z( 9)] ^ S7[z( 7)] ^ S8[z( 6)] ^ S5[z( 2)];
+ K[ 1] = S5[z(10)] ^ S6[z(11)] ^ S7[z( 5)] ^ S8[z( 4)] ^ S6[z( 6)];
+ K[ 2] = S5[z(12)] ^ S6[z(13)] ^ S7[z( 3)] ^ S8[z( 2)] ^ S7[z( 9)];
+ K[ 3] = S5[z(14)] ^ S6[z(15)] ^ S7[z( 1)] ^ S8[z( 0)] ^ S8[z(12)];
+ X[0] = Z[2] ^ S5[z( 5)] ^ S6[z( 7)] ^ S7[z( 4)] ^ S8[z( 6)] ^ S7[z( 0)];
+ X[1] = Z[0] ^ S5[x( 0)] ^ S6[x( 2)] ^ S7[x( 1)] ^ S8[x( 3)] ^ S8[z( 2)];
+ X[2] = Z[1] ^ S5[x( 7)] ^ S6[x( 6)] ^ S7[x( 5)] ^ S8[x( 4)] ^ S5[z( 1)];
+ X[3] = Z[3] ^ S5[x(10)] ^ S6[x( 9)] ^ S7[x(11)] ^ S8[x( 8)] ^ S6[z( 3)];
+ K[ 4] = S5[x( 3)] ^ S6[x( 2)] ^ S7[x(12)] ^ S8[x(13)] ^ S5[x( 8)];
+ K[ 5] = S5[x( 1)] ^ S6[x( 0)] ^ S7[x(14)] ^ S8[x(15)] ^ S6[x(13)];
+ K[ 6] = S5[x( 7)] ^ S6[x( 6)] ^ S7[x( 8)] ^ S8[x( 9)] ^ S7[x( 3)];
+ K[ 7] = S5[x( 5)] ^ S6[x( 4)] ^ S7[x(10)] ^ S8[x(11)] ^ S8[x( 7)];
+ 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)];
+ Z[2] = X[3] ^ S5[z( 7)] ^ S6[z( 6)] ^ S7[z( 5)] ^ S8[z( 4)] ^ S5[x( 9)];
+ Z[3] = X[1] ^ S5[z(10)] ^ S6[z( 9)] ^ S7[z(11)] ^ S8[z( 8)] ^ S6[x(11)];
+ K[ 8] = S5[z( 3)] ^ S6[z( 2)] ^ S7[z(12)] ^ S8[z(13)] ^ S5[z( 9)];
+ K[ 9] = S5[z( 1)] ^ S6[z( 0)] ^ S7[z(14)] ^ S8[z(15)] ^ S6[z(12)];
+ K[10] = S5[z( 7)] ^ S6[z( 6)] ^ S7[z( 8)] ^ S8[z( 9)] ^ S7[z( 2)];
+ K[11] = S5[z( 5)] ^ S6[z( 4)] ^ S7[z(10)] ^ S8[z(11)] ^ S8[z( 6)];
+ X[0] = Z[2] ^ S5[z( 5)] ^ S6[z( 7)] ^ S7[z( 4)] ^ S8[z( 6)] ^ S7[z( 0)];
+ X[1] = Z[0] ^ S5[x( 0)] ^ S6[x( 2)] ^ S7[x( 1)] ^ S8[x( 3)] ^ S8[z( 2)];
+ X[2] = Z[1] ^ S5[x( 7)] ^ S6[x( 6)] ^ S7[x( 5)] ^ S8[x( 4)] ^ S5[z( 1)];
+ X[3] = Z[3] ^ S5[x(10)] ^ S6[x( 9)] ^ S7[x(11)] ^ S8[x( 8)] ^ S6[z( 3)];
+ K[12] = S5[x( 8)] ^ S6[x( 9)] ^ S7[x( 7)] ^ S8[x( 6)] ^ S5[x( 3)];
+ K[13] = S5[x(10)] ^ S6[x(11)] ^ S7[x( 5)] ^ S8[x( 4)] ^ S6[x( 7)];
+ K[14] = S5[x(12)] ^ S6[x(13)] ^ S7[x( 3)] ^ S8[x( 2)] ^ S7[x( 8)];
+ K[15] = S5[x(14)] ^ S6[x(15)] ^ S7[x( 1)] ^ S8[x( 0)] ^ S8[x(13)];
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/block/cast128/cast128.h b/comm/third_party/botan/src/lib/block/cast128/cast128.h
new file mode 100644
index 0000000000..a5f2a64019
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/cast128/cast128.h
@@ -0,0 +1,42 @@
+/*
+* CAST-128
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_CAST128_H_
+#define BOTAN_CAST128_H_
+
+#include <botan/block_cipher.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(cast128.h)
+
+namespace Botan {
+
+/**
+* CAST-128
+*/
+class BOTAN_PUBLIC_API(2,0) CAST_128 final : public Block_Cipher_Fixed_Params<8, 11, 16>
+ {
+ public:
+ void encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+ void decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+
+ void clear() override;
+ std::string name() const override { return "CAST-128"; }
+ BlockCipher* clone() const override { return new CAST_128; }
+
+ private:
+ void key_schedule(const uint8_t[], size_t) override;
+
+ static void cast_ks(secure_vector<uint32_t>& ks,
+ secure_vector<uint32_t>& user_key);
+
+ secure_vector<uint32_t> m_MK;
+ secure_vector<uint8_t> m_RK;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/block/cast128/cast_sboxes.h b/comm/third_party/botan/src/lib/block/cast128/cast_sboxes.h
new file mode 100644
index 0000000000..f4d005cc94
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/cast128/cast_sboxes.h
@@ -0,0 +1,197 @@
+/*
+* S-Box Tables for CAST-128 and CAST-256
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_CAST_SBOX_TABLES_H_
+#define BOTAN_CAST_SBOX_TABLES_H_
+
+#include <botan/types.h>
+
+namespace Botan {
+
+alignas(64) const uint32_t CAST_SBOX1[256] = {
+ 0x30FB40D4, 0x9FA0FF0B, 0x6BECCD2F, 0x3F258C7A, 0x1E213F2F, 0x9C004DD3,
+ 0x6003E540, 0xCF9FC949, 0xBFD4AF27, 0x88BBBDB5, 0xE2034090, 0x98D09675,
+ 0x6E63A0E0, 0x15C361D2, 0xC2E7661D, 0x22D4FF8E, 0x28683B6F, 0xC07FD059,
+ 0xFF2379C8, 0x775F50E2, 0x43C340D3, 0xDF2F8656, 0x887CA41A, 0xA2D2BD2D,
+ 0xA1C9E0D6, 0x346C4819, 0x61B76D87, 0x22540F2F, 0x2ABE32E1, 0xAA54166B,
+ 0x22568E3A, 0xA2D341D0, 0x66DB40C8, 0xA784392F, 0x004DFF2F, 0x2DB9D2DE,
+ 0x97943FAC, 0x4A97C1D8, 0x527644B7, 0xB5F437A7, 0xB82CBAEF, 0xD751D159,
+ 0x6FF7F0ED, 0x5A097A1F, 0x827B68D0, 0x90ECF52E, 0x22B0C054, 0xBC8E5935,
+ 0x4B6D2F7F, 0x50BB64A2, 0xD2664910, 0xBEE5812D, 0xB7332290, 0xE93B159F,
+ 0xB48EE411, 0x4BFF345D, 0xFD45C240, 0xAD31973F, 0xC4F6D02E, 0x55FC8165,
+ 0xD5B1CAAD, 0xA1AC2DAE, 0xA2D4B76D, 0xC19B0C50, 0x882240F2, 0x0C6E4F38,
+ 0xA4E4BFD7, 0x4F5BA272, 0x564C1D2F, 0xC59C5319, 0xB949E354, 0xB04669FE,
+ 0xB1B6AB8A, 0xC71358DD, 0x6385C545, 0x110F935D, 0x57538AD5, 0x6A390493,
+ 0xE63D37E0, 0x2A54F6B3, 0x3A787D5F, 0x6276A0B5, 0x19A6FCDF, 0x7A42206A,
+ 0x29F9D4D5, 0xF61B1891, 0xBB72275E, 0xAA508167, 0x38901091, 0xC6B505EB,
+ 0x84C7CB8C, 0x2AD75A0F, 0x874A1427, 0xA2D1936B, 0x2AD286AF, 0xAA56D291,
+ 0xD7894360, 0x425C750D, 0x93B39E26, 0x187184C9, 0x6C00B32D, 0x73E2BB14,
+ 0xA0BEBC3C, 0x54623779, 0x64459EAB, 0x3F328B82, 0x7718CF82, 0x59A2CEA6,
+ 0x04EE002E, 0x89FE78E6, 0x3FAB0950, 0x325FF6C2, 0x81383F05, 0x6963C5C8,
+ 0x76CB5AD6, 0xD49974C9, 0xCA180DCF, 0x380782D5, 0xC7FA5CF6, 0x8AC31511,
+ 0x35E79E13, 0x47DA91D0, 0xF40F9086, 0xA7E2419E, 0x31366241, 0x051EF495,
+ 0xAA573B04, 0x4A805D8D, 0x548300D0, 0x00322A3C, 0xBF64CDDF, 0xBA57A68E,
+ 0x75C6372B, 0x50AFD341, 0xA7C13275, 0x915A0BF5, 0x6B54BFAB, 0x2B0B1426,
+ 0xAB4CC9D7, 0x449CCD82, 0xF7FBF265, 0xAB85C5F3, 0x1B55DB94, 0xAAD4E324,
+ 0xCFA4BD3F, 0x2DEAA3E2, 0x9E204D02, 0xC8BD25AC, 0xEADF55B3, 0xD5BD9E98,
+ 0xE31231B2, 0x2AD5AD6C, 0x954329DE, 0xADBE4528, 0xD8710F69, 0xAA51C90F,
+ 0xAA786BF6, 0x22513F1E, 0xAA51A79B, 0x2AD344CC, 0x7B5A41F0, 0xD37CFBAD,
+ 0x1B069505, 0x41ECE491, 0xB4C332E6, 0x032268D4, 0xC9600ACC, 0xCE387E6D,
+ 0xBF6BB16C, 0x6A70FB78, 0x0D03D9C9, 0xD4DF39DE, 0xE01063DA, 0x4736F464,
+ 0x5AD328D8, 0xB347CC96, 0x75BB0FC3, 0x98511BFB, 0x4FFBCC35, 0xB58BCF6A,
+ 0xE11F0ABC, 0xBFC5FE4A, 0xA70AEC10, 0xAC39570A, 0x3F04442F, 0x6188B153,
+ 0xE0397A2E, 0x5727CB79, 0x9CEB418F, 0x1CACD68D, 0x2AD37C96, 0x0175CB9D,
+ 0xC69DFF09, 0xC75B65F0, 0xD9DB40D8, 0xEC0E7779, 0x4744EAD4, 0xB11C3274,
+ 0xDD24CB9E, 0x7E1C54BD, 0xF01144F9, 0xD2240EB1, 0x9675B3FD, 0xA3AC3755,
+ 0xD47C27AF, 0x51C85F4D, 0x56907596, 0xA5BB15E6, 0x580304F0, 0xCA042CF1,
+ 0x011A37EA, 0x8DBFAADB, 0x35BA3E4A, 0x3526FFA0, 0xC37B4D09, 0xBC306ED9,
+ 0x98A52666, 0x5648F725, 0xFF5E569D, 0x0CED63D0, 0x7C63B2CF, 0x700B45E1,
+ 0xD5EA50F1, 0x85A92872, 0xAF1FBDA7, 0xD4234870, 0xA7870BF3, 0x2D3B4D79,
+ 0x42E04198, 0x0CD0EDE7, 0x26470DB8, 0xF881814C, 0x474D6AD7, 0x7C0C5E5C,
+ 0xD1231959, 0x381B7298, 0xF5D2F4DB, 0xAB838653, 0x6E2F1E23, 0x83719C9E,
+ 0xBD91E046, 0x9A56456E, 0xDC39200C, 0x20C8C571, 0x962BDA1C, 0xE1E696FF,
+ 0xB141AB08, 0x7CCA89B9, 0x1A69E783, 0x02CC4843, 0xA2F7C579, 0x429EF47D,
+ 0x427B169C, 0x5AC9F049, 0xDD8F0F00, 0x5C8165BF };
+
+alignas(64) const uint32_t CAST_SBOX2[256] = {
+ 0x1F201094, 0xEF0BA75B, 0x69E3CF7E, 0x393F4380, 0xFE61CF7A, 0xEEC5207A,
+ 0x55889C94, 0x72FC0651, 0xADA7EF79, 0x4E1D7235, 0xD55A63CE, 0xDE0436BA,
+ 0x99C430EF, 0x5F0C0794, 0x18DCDB7D, 0xA1D6EFF3, 0xA0B52F7B, 0x59E83605,
+ 0xEE15B094, 0xE9FFD909, 0xDC440086, 0xEF944459, 0xBA83CCB3, 0xE0C3CDFB,
+ 0xD1DA4181, 0x3B092AB1, 0xF997F1C1, 0xA5E6CF7B, 0x01420DDB, 0xE4E7EF5B,
+ 0x25A1FF41, 0xE180F806, 0x1FC41080, 0x179BEE7A, 0xD37AC6A9, 0xFE5830A4,
+ 0x98DE8B7F, 0x77E83F4E, 0x79929269, 0x24FA9F7B, 0xE113C85B, 0xACC40083,
+ 0xD7503525, 0xF7EA615F, 0x62143154, 0x0D554B63, 0x5D681121, 0xC866C359,
+ 0x3D63CF73, 0xCEE234C0, 0xD4D87E87, 0x5C672B21, 0x071F6181, 0x39F7627F,
+ 0x361E3084, 0xE4EB573B, 0x602F64A4, 0xD63ACD9C, 0x1BBC4635, 0x9E81032D,
+ 0x2701F50C, 0x99847AB4, 0xA0E3DF79, 0xBA6CF38C, 0x10843094, 0x2537A95E,
+ 0xF46F6FFE, 0xA1FF3B1F, 0x208CFB6A, 0x8F458C74, 0xD9E0A227, 0x4EC73A34,
+ 0xFC884F69, 0x3E4DE8DF, 0xEF0E0088, 0x3559648D, 0x8A45388C, 0x1D804366,
+ 0x721D9BFD, 0xA58684BB, 0xE8256333, 0x844E8212, 0x128D8098, 0xFED33FB4,
+ 0xCE280AE1, 0x27E19BA5, 0xD5A6C252, 0xE49754BD, 0xC5D655DD, 0xEB667064,
+ 0x77840B4D, 0xA1B6A801, 0x84DB26A9, 0xE0B56714, 0x21F043B7, 0xE5D05860,
+ 0x54F03084, 0x066FF472, 0xA31AA153, 0xDADC4755, 0xB5625DBF, 0x68561BE6,
+ 0x83CA6B94, 0x2D6ED23B, 0xECCF01DB, 0xA6D3D0BA, 0xB6803D5C, 0xAF77A709,
+ 0x33B4A34C, 0x397BC8D6, 0x5EE22B95, 0x5F0E5304, 0x81ED6F61, 0x20E74364,
+ 0xB45E1378, 0xDE18639B, 0x881CA122, 0xB96726D1, 0x8049A7E8, 0x22B7DA7B,
+ 0x5E552D25, 0x5272D237, 0x79D2951C, 0xC60D894C, 0x488CB402, 0x1BA4FE5B,
+ 0xA4B09F6B, 0x1CA815CF, 0xA20C3005, 0x8871DF63, 0xB9DE2FCB, 0x0CC6C9E9,
+ 0x0BEEFF53, 0xE3214517, 0xB4542835, 0x9F63293C, 0xEE41E729, 0x6E1D2D7C,
+ 0x50045286, 0x1E6685F3, 0xF33401C6, 0x30A22C95, 0x31A70850, 0x60930F13,
+ 0x73F98417, 0xA1269859, 0xEC645C44, 0x52C877A9, 0xCDFF33A6, 0xA02B1741,
+ 0x7CBAD9A2, 0x2180036F, 0x50D99C08, 0xCB3F4861, 0xC26BD765, 0x64A3F6AB,
+ 0x80342676, 0x25A75E7B, 0xE4E6D1FC, 0x20C710E6, 0xCDF0B680, 0x17844D3B,
+ 0x31EEF84D, 0x7E0824E4, 0x2CCB49EB, 0x846A3BAE, 0x8FF77888, 0xEE5D60F6,
+ 0x7AF75673, 0x2FDD5CDB, 0xA11631C1, 0x30F66F43, 0xB3FAEC54, 0x157FD7FA,
+ 0xEF8579CC, 0xD152DE58, 0xDB2FFD5E, 0x8F32CE19, 0x306AF97A, 0x02F03EF8,
+ 0x99319AD5, 0xC242FA0F, 0xA7E3EBB0, 0xC68E4906, 0xB8DA230C, 0x80823028,
+ 0xDCDEF3C8, 0xD35FB171, 0x088A1BC8, 0xBEC0C560, 0x61A3C9E8, 0xBCA8F54D,
+ 0xC72FEFFA, 0x22822E99, 0x82C570B4, 0xD8D94E89, 0x8B1C34BC, 0x301E16E6,
+ 0x273BE979, 0xB0FFEAA6, 0x61D9B8C6, 0x00B24869, 0xB7FFCE3F, 0x08DC283B,
+ 0x43DAF65A, 0xF7E19798, 0x7619B72F, 0x8F1C9BA4, 0xDC8637A0, 0x16A7D3B1,
+ 0x9FC393B7, 0xA7136EEB, 0xC6BCC63E, 0x1A513742, 0xEF6828BC, 0x520365D6,
+ 0x2D6A77AB, 0x3527ED4B, 0x821FD216, 0x095C6E2E, 0xDB92F2FB, 0x5EEA29CB,
+ 0x145892F5, 0x91584F7F, 0x5483697B, 0x2667A8CC, 0x85196048, 0x8C4BACEA,
+ 0x833860D4, 0x0D23E0F9, 0x6C387E8A, 0x0AE6D249, 0xB284600C, 0xD835731D,
+ 0xDCB1C647, 0xAC4C56EA, 0x3EBD81B3, 0x230EABB0, 0x6438BC87, 0xF0B5B1FA,
+ 0x8F5EA2B3, 0xFC184642, 0x0A036B7A, 0x4FB089BD, 0x649DA589, 0xA345415E,
+ 0x5C038323, 0x3E5D3BB9, 0x43D79572, 0x7E6DD07C, 0x06DFDF1E, 0x6C6CC4EF,
+ 0x7160A539, 0x73BFBE70, 0x83877605, 0x4523ECF1 };
+
+alignas(64) const uint32_t CAST_SBOX3[256] = {
+ 0x8DEFC240, 0x25FA5D9F, 0xEB903DBF, 0xE810C907, 0x47607FFF, 0x369FE44B,
+ 0x8C1FC644, 0xAECECA90, 0xBEB1F9BF, 0xEEFBCAEA, 0xE8CF1950, 0x51DF07AE,
+ 0x920E8806, 0xF0AD0548, 0xE13C8D83, 0x927010D5, 0x11107D9F, 0x07647DB9,
+ 0xB2E3E4D4, 0x3D4F285E, 0xB9AFA820, 0xFADE82E0, 0xA067268B, 0x8272792E,
+ 0x553FB2C0, 0x489AE22B, 0xD4EF9794, 0x125E3FBC, 0x21FFFCEE, 0x825B1BFD,
+ 0x9255C5ED, 0x1257A240, 0x4E1A8302, 0xBAE07FFF, 0x528246E7, 0x8E57140E,
+ 0x3373F7BF, 0x8C9F8188, 0xA6FC4EE8, 0xC982B5A5, 0xA8C01DB7, 0x579FC264,
+ 0x67094F31, 0xF2BD3F5F, 0x40FFF7C1, 0x1FB78DFC, 0x8E6BD2C1, 0x437BE59B,
+ 0x99B03DBF, 0xB5DBC64B, 0x638DC0E6, 0x55819D99, 0xA197C81C, 0x4A012D6E,
+ 0xC5884A28, 0xCCC36F71, 0xB843C213, 0x6C0743F1, 0x8309893C, 0x0FEDDD5F,
+ 0x2F7FE850, 0xD7C07F7E, 0x02507FBF, 0x5AFB9A04, 0xA747D2D0, 0x1651192E,
+ 0xAF70BF3E, 0x58C31380, 0x5F98302E, 0x727CC3C4, 0x0A0FB402, 0x0F7FEF82,
+ 0x8C96FDAD, 0x5D2C2AAE, 0x8EE99A49, 0x50DA88B8, 0x8427F4A0, 0x1EAC5790,
+ 0x796FB449, 0x8252DC15, 0xEFBD7D9B, 0xA672597D, 0xADA840D8, 0x45F54504,
+ 0xFA5D7403, 0xE83EC305, 0x4F91751A, 0x925669C2, 0x23EFE941, 0xA903F12E,
+ 0x60270DF2, 0x0276E4B6, 0x94FD6574, 0x927985B2, 0x8276DBCB, 0x02778176,
+ 0xF8AF918D, 0x4E48F79E, 0x8F616DDF, 0xE29D840E, 0x842F7D83, 0x340CE5C8,
+ 0x96BBB682, 0x93B4B148, 0xEF303CAB, 0x984FAF28, 0x779FAF9B, 0x92DC560D,
+ 0x224D1E20, 0x8437AA88, 0x7D29DC96, 0x2756D3DC, 0x8B907CEE, 0xB51FD240,
+ 0xE7C07CE3, 0xE566B4A1, 0xC3E9615E, 0x3CF8209D, 0x6094D1E3, 0xCD9CA341,
+ 0x5C76460E, 0x00EA983B, 0xD4D67881, 0xFD47572C, 0xF76CEDD9, 0xBDA8229C,
+ 0x127DADAA, 0x438A074E, 0x1F97C090, 0x081BDB8A, 0x93A07EBE, 0xB938CA15,
+ 0x97B03CFF, 0x3DC2C0F8, 0x8D1AB2EC, 0x64380E51, 0x68CC7BFB, 0xD90F2788,
+ 0x12490181, 0x5DE5FFD4, 0xDD7EF86A, 0x76A2E214, 0xB9A40368, 0x925D958F,
+ 0x4B39FFFA, 0xBA39AEE9, 0xA4FFD30B, 0xFAF7933B, 0x6D498623, 0x193CBCFA,
+ 0x27627545, 0x825CF47A, 0x61BD8BA0, 0xD11E42D1, 0xCEAD04F4, 0x127EA392,
+ 0x10428DB7, 0x8272A972, 0x9270C4A8, 0x127DE50B, 0x285BA1C8, 0x3C62F44F,
+ 0x35C0EAA5, 0xE805D231, 0x428929FB, 0xB4FCDF82, 0x4FB66A53, 0x0E7DC15B,
+ 0x1F081FAB, 0x108618AE, 0xFCFD086D, 0xF9FF2889, 0x694BCC11, 0x236A5CAE,
+ 0x12DECA4D, 0x2C3F8CC5, 0xD2D02DFE, 0xF8EF5896, 0xE4CF52DA, 0x95155B67,
+ 0x494A488C, 0xB9B6A80C, 0x5C8F82BC, 0x89D36B45, 0x3A609437, 0xEC00C9A9,
+ 0x44715253, 0x0A874B49, 0xD773BC40, 0x7C34671C, 0x02717EF6, 0x4FEB5536,
+ 0xA2D02FFF, 0xD2BF60C4, 0xD43F03C0, 0x50B4EF6D, 0x07478CD1, 0x006E1888,
+ 0xA2E53F55, 0xB9E6D4BC, 0xA2048016, 0x97573833, 0xD7207D67, 0xDE0F8F3D,
+ 0x72F87B33, 0xABCC4F33, 0x7688C55D, 0x7B00A6B0, 0x947B0001, 0x570075D2,
+ 0xF9BB88F8, 0x8942019E, 0x4264A5FF, 0x856302E0, 0x72DBD92B, 0xEE971B69,
+ 0x6EA22FDE, 0x5F08AE2B, 0xAF7A616D, 0xE5C98767, 0xCF1FEBD2, 0x61EFC8C2,
+ 0xF1AC2571, 0xCC8239C2, 0x67214CB8, 0xB1E583D1, 0xB7DC3E62, 0x7F10BDCE,
+ 0xF90A5C38, 0x0FF0443D, 0x606E6DC6, 0x60543A49, 0x5727C148, 0x2BE98A1D,
+ 0x8AB41738, 0x20E1BE24, 0xAF96DA0F, 0x68458425, 0x99833BE5, 0x600D457D,
+ 0x282F9350, 0x8334B362, 0xD91D1120, 0x2B6D8DA0, 0x642B1E31, 0x9C305A00,
+ 0x52BCE688, 0x1B03588A, 0xF7BAEFD5, 0x4142ED9C, 0xA4315C11, 0x83323EC5,
+ 0xDFEF4636, 0xA133C501, 0xE9D3531C, 0xEE353783 };
+
+alignas(64) const uint32_t CAST_SBOX4[256] = {
+ 0x9DB30420, 0x1FB6E9DE, 0xA7BE7BEF, 0xD273A298, 0x4A4F7BDB, 0x64AD8C57,
+ 0x85510443, 0xFA020ED1, 0x7E287AFF, 0xE60FB663, 0x095F35A1, 0x79EBF120,
+ 0xFD059D43, 0x6497B7B1, 0xF3641F63, 0x241E4ADF, 0x28147F5F, 0x4FA2B8CD,
+ 0xC9430040, 0x0CC32220, 0xFDD30B30, 0xC0A5374F, 0x1D2D00D9, 0x24147B15,
+ 0xEE4D111A, 0x0FCA5167, 0x71FF904C, 0x2D195FFE, 0x1A05645F, 0x0C13FEFE,
+ 0x081B08CA, 0x05170121, 0x80530100, 0xE83E5EFE, 0xAC9AF4F8, 0x7FE72701,
+ 0xD2B8EE5F, 0x06DF4261, 0xBB9E9B8A, 0x7293EA25, 0xCE84FFDF, 0xF5718801,
+ 0x3DD64B04, 0xA26F263B, 0x7ED48400, 0x547EEBE6, 0x446D4CA0, 0x6CF3D6F5,
+ 0x2649ABDF, 0xAEA0C7F5, 0x36338CC1, 0x503F7E93, 0xD3772061, 0x11B638E1,
+ 0x72500E03, 0xF80EB2BB, 0xABE0502E, 0xEC8D77DE, 0x57971E81, 0xE14F6746,
+ 0xC9335400, 0x6920318F, 0x081DBB99, 0xFFC304A5, 0x4D351805, 0x7F3D5CE3,
+ 0xA6C866C6, 0x5D5BCCA9, 0xDAEC6FEA, 0x9F926F91, 0x9F46222F, 0x3991467D,
+ 0xA5BF6D8E, 0x1143C44F, 0x43958302, 0xD0214EEB, 0x022083B8, 0x3FB6180C,
+ 0x18F8931E, 0x281658E6, 0x26486E3E, 0x8BD78A70, 0x7477E4C1, 0xB506E07C,
+ 0xF32D0A25, 0x79098B02, 0xE4EABB81, 0x28123B23, 0x69DEAD38, 0x1574CA16,
+ 0xDF871B62, 0x211C40B7, 0xA51A9EF9, 0x0014377B, 0x041E8AC8, 0x09114003,
+ 0xBD59E4D2, 0xE3D156D5, 0x4FE876D5, 0x2F91A340, 0x557BE8DE, 0x00EAE4A7,
+ 0x0CE5C2EC, 0x4DB4BBA6, 0xE756BDFF, 0xDD3369AC, 0xEC17B035, 0x06572327,
+ 0x99AFC8B0, 0x56C8C391, 0x6B65811C, 0x5E146119, 0x6E85CB75, 0xBE07C002,
+ 0xC2325577, 0x893FF4EC, 0x5BBFC92D, 0xD0EC3B25, 0xB7801AB7, 0x8D6D3B24,
+ 0x20C763EF, 0xC366A5FC, 0x9C382880, 0x0ACE3205, 0xAAC9548A, 0xECA1D7C7,
+ 0x041AFA32, 0x1D16625A, 0x6701902C, 0x9B757A54, 0x31D477F7, 0x9126B031,
+ 0x36CC6FDB, 0xC70B8B46, 0xD9E66A48, 0x56E55A79, 0x026A4CEB, 0x52437EFF,
+ 0x2F8F76B4, 0x0DF980A5, 0x8674CDE3, 0xEDDA04EB, 0x17A9BE04, 0x2C18F4DF,
+ 0xB7747F9D, 0xAB2AF7B4, 0xEFC34D20, 0x2E096B7C, 0x1741A254, 0xE5B6A035,
+ 0x213D42F6, 0x2C1C7C26, 0x61C2F50F, 0x6552DAF9, 0xD2C231F8, 0x25130F69,
+ 0xD8167FA2, 0x0418F2C8, 0x001A96A6, 0x0D1526AB, 0x63315C21, 0x5E0A72EC,
+ 0x49BAFEFD, 0x187908D9, 0x8D0DBD86, 0x311170A7, 0x3E9B640C, 0xCC3E10D7,
+ 0xD5CAD3B6, 0x0CAEC388, 0xF73001E1, 0x6C728AFF, 0x71EAE2A1, 0x1F9AF36E,
+ 0xCFCBD12F, 0xC1DE8417, 0xAC07BE6B, 0xCB44A1D8, 0x8B9B0F56, 0x013988C3,
+ 0xB1C52FCA, 0xB4BE31CD, 0xD8782806, 0x12A3A4E2, 0x6F7DE532, 0x58FD7EB6,
+ 0xD01EE900, 0x24ADFFC2, 0xF4990FC5, 0x9711AAC5, 0x001D7B95, 0x82E5E7D2,
+ 0x109873F6, 0x00613096, 0xC32D9521, 0xADA121FF, 0x29908415, 0x7FBB977F,
+ 0xAF9EB3DB, 0x29C9ED2A, 0x5CE2A465, 0xA730F32C, 0xD0AA3FE8, 0x8A5CC091,
+ 0xD49E2CE7, 0x0CE454A9, 0xD60ACD86, 0x015F1919, 0x77079103, 0xDEA03AF6,
+ 0x78A8565E, 0xDEE356DF, 0x21F05CBE, 0x8B75E387, 0xB3C50651, 0xB8A5C3EF,
+ 0xD8EEB6D2, 0xE523BE77, 0xC2154529, 0x2F69EFDF, 0xAFE67AFB, 0xF470C4B2,
+ 0xF3E0EB5B, 0xD6CC9876, 0x39E4460C, 0x1FDA8538, 0x1987832F, 0xCA007367,
+ 0xA99144F8, 0x296B299E, 0x492FC295, 0x9266BEAB, 0xB5676E69, 0x9BD3DDDA,
+ 0xDF7E052F, 0xDB25701C, 0x1B5E51EE, 0xF65324E6, 0x6AFCE36C, 0x0316CC04,
+ 0x8644213E, 0xB7DC59D0, 0x7965291F, 0xCCD6FD43, 0x41823979, 0x932BCDF6,
+ 0xB657C34D, 0x4EDFD282, 0x7AE5290C, 0x3CB9536B, 0x851E20FE, 0x9833557E,
+ 0x13ECF0B0, 0xD3FFB372, 0x3F85C5C1, 0x0AEF7ED2 };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/block/cast128/info.txt b/comm/third_party/botan/src/lib/block/cast128/info.txt
new file mode 100644
index 0000000000..6e6cf30abe
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/cast128/info.txt
@@ -0,0 +1,12 @@
+<defines>
+CAST -> 20131128
+CAST_128 -> 20171203
+</defines>
+
+<header:internal>
+cast_sboxes.h
+</header:internal>
+
+<header:public>
+cast128.h
+</header:public>
diff --git a/comm/third_party/botan/src/lib/block/cast256/cast256.cpp b/comm/third_party/botan/src/lib/block/cast256/cast256.cpp
new file mode 100644
index 0000000000..226955f7cb
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/cast256/cast256.cpp
@@ -0,0 +1,232 @@
+/*
+* CAST-256
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/cast256.h>
+#include <botan/internal/cast_sboxes.h>
+#include <botan/loadstor.h>
+#include <botan/rotate.h>
+
+namespace Botan {
+
+namespace {
+
+/*
+* CAST-256 Round Type 1
+*/
+void round1(uint32_t& out, uint32_t in, uint32_t MK, uint32_t RK)
+ {
+ const uint32_t T = rotl_var(MK + in, RK);
+ out ^= (CAST_SBOX1[get_byte(0, T)] ^ CAST_SBOX2[get_byte(1, T)]) -
+ CAST_SBOX3[get_byte(2, T)] + CAST_SBOX4[get_byte(3, T)];
+ }
+
+/*
+* CAST-256 Round Type 2
+*/
+void round2(uint32_t& out, uint32_t in, uint32_t MK, uint32_t RK)
+ {
+ const uint32_t T = rotl_var(MK ^ in, RK);
+ out ^= (CAST_SBOX1[get_byte(0, T)] - CAST_SBOX2[get_byte(1, T)] +
+ CAST_SBOX3[get_byte(2, T)]) ^ CAST_SBOX4[get_byte(3, T)];
+ }
+
+/*
+* CAST-256 Round Type 3
+*/
+void round3(uint32_t& out, uint32_t in, uint32_t MK, uint32_t RK)
+ {
+ const uint32_t T = rotl_var(MK - in, RK);
+ out ^= ((CAST_SBOX1[get_byte(0, T)] + CAST_SBOX2[get_byte(1, T)]) ^
+ CAST_SBOX3[get_byte(2, T)]) - CAST_SBOX4[get_byte(3, T)];
+ }
+
+}
+
+/*
+* CAST-256 Encryption
+*/
+void CAST_256::encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+ {
+ verify_key_set(m_RK.empty() == false);
+
+ for(size_t i = 0; i != blocks; ++i)
+ {
+ uint32_t A = load_be<uint32_t>(in, 0);
+ uint32_t B = load_be<uint32_t>(in, 1);
+ uint32_t C = load_be<uint32_t>(in, 2);
+ uint32_t D = load_be<uint32_t>(in, 3);
+
+ round1(C, D, m_MK[ 0], m_RK[ 0]); round2(B, C, m_MK[ 1], m_RK[ 1]);
+ round3(A, B, m_MK[ 2], m_RK[ 2]); round1(D, A, m_MK[ 3], m_RK[ 3]);
+ round1(C, D, m_MK[ 4], m_RK[ 4]); round2(B, C, m_MK[ 5], m_RK[ 5]);
+ round3(A, B, m_MK[ 6], m_RK[ 6]); round1(D, A, m_MK[ 7], m_RK[ 7]);
+ round1(C, D, m_MK[ 8], m_RK[ 8]); round2(B, C, m_MK[ 9], m_RK[ 9]);
+ round3(A, B, m_MK[10], m_RK[10]); round1(D, A, m_MK[11], m_RK[11]);
+ round1(C, D, m_MK[12], m_RK[12]); round2(B, C, m_MK[13], m_RK[13]);
+ round3(A, B, m_MK[14], m_RK[14]); round1(D, A, m_MK[15], m_RK[15]);
+ round1(C, D, m_MK[16], m_RK[16]); round2(B, C, m_MK[17], m_RK[17]);
+ round3(A, B, m_MK[18], m_RK[18]); round1(D, A, m_MK[19], m_RK[19]);
+ round1(C, D, m_MK[20], m_RK[20]); round2(B, C, m_MK[21], m_RK[21]);
+ round3(A, B, m_MK[22], m_RK[22]); round1(D, A, m_MK[23], m_RK[23]);
+ round1(D, A, m_MK[27], m_RK[27]); round3(A, B, m_MK[26], m_RK[26]);
+ round2(B, C, m_MK[25], m_RK[25]); round1(C, D, m_MK[24], m_RK[24]);
+ round1(D, A, m_MK[31], m_RK[31]); round3(A, B, m_MK[30], m_RK[30]);
+ round2(B, C, m_MK[29], m_RK[29]); round1(C, D, m_MK[28], m_RK[28]);
+ round1(D, A, m_MK[35], m_RK[35]); round3(A, B, m_MK[34], m_RK[34]);
+ round2(B, C, m_MK[33], m_RK[33]); round1(C, D, m_MK[32], m_RK[32]);
+ round1(D, A, m_MK[39], m_RK[39]); round3(A, B, m_MK[38], m_RK[38]);
+ round2(B, C, m_MK[37], m_RK[37]); round1(C, D, m_MK[36], m_RK[36]);
+ round1(D, A, m_MK[43], m_RK[43]); round3(A, B, m_MK[42], m_RK[42]);
+ round2(B, C, m_MK[41], m_RK[41]); round1(C, D, m_MK[40], m_RK[40]);
+ round1(D, A, m_MK[47], m_RK[47]); round3(A, B, m_MK[46], m_RK[46]);
+ round2(B, C, m_MK[45], m_RK[45]); round1(C, D, m_MK[44], m_RK[44]);
+
+ store_be(out, A, B, C, D);
+
+ in += BLOCK_SIZE;
+ out += BLOCK_SIZE;
+ }
+ }
+
+/*
+* CAST-256 Decryption
+*/
+void CAST_256::decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+ {
+ verify_key_set(m_RK.empty() == false);
+
+ for(size_t i = 0; i != blocks; ++i)
+ {
+ uint32_t A = load_be<uint32_t>(in, 0);
+ uint32_t B = load_be<uint32_t>(in, 1);
+ uint32_t C = load_be<uint32_t>(in, 2);
+ uint32_t D = load_be<uint32_t>(in, 3);
+
+ round1(C, D, m_MK[44], m_RK[44]); round2(B, C, m_MK[45], m_RK[45]);
+ round3(A, B, m_MK[46], m_RK[46]); round1(D, A, m_MK[47], m_RK[47]);
+ round1(C, D, m_MK[40], m_RK[40]); round2(B, C, m_MK[41], m_RK[41]);
+ round3(A, B, m_MK[42], m_RK[42]); round1(D, A, m_MK[43], m_RK[43]);
+ round1(C, D, m_MK[36], m_RK[36]); round2(B, C, m_MK[37], m_RK[37]);
+ round3(A, B, m_MK[38], m_RK[38]); round1(D, A, m_MK[39], m_RK[39]);
+ round1(C, D, m_MK[32], m_RK[32]); round2(B, C, m_MK[33], m_RK[33]);
+ round3(A, B, m_MK[34], m_RK[34]); round1(D, A, m_MK[35], m_RK[35]);
+ round1(C, D, m_MK[28], m_RK[28]); round2(B, C, m_MK[29], m_RK[29]);
+ round3(A, B, m_MK[30], m_RK[30]); round1(D, A, m_MK[31], m_RK[31]);
+ round1(C, D, m_MK[24], m_RK[24]); round2(B, C, m_MK[25], m_RK[25]);
+ round3(A, B, m_MK[26], m_RK[26]); round1(D, A, m_MK[27], m_RK[27]);
+ round1(D, A, m_MK[23], m_RK[23]); round3(A, B, m_MK[22], m_RK[22]);
+ round2(B, C, m_MK[21], m_RK[21]); round1(C, D, m_MK[20], m_RK[20]);
+ round1(D, A, m_MK[19], m_RK[19]); round3(A, B, m_MK[18], m_RK[18]);
+ round2(B, C, m_MK[17], m_RK[17]); round1(C, D, m_MK[16], m_RK[16]);
+ round1(D, A, m_MK[15], m_RK[15]); round3(A, B, m_MK[14], m_RK[14]);
+ round2(B, C, m_MK[13], m_RK[13]); round1(C, D, m_MK[12], m_RK[12]);
+ round1(D, A, m_MK[11], m_RK[11]); round3(A, B, m_MK[10], m_RK[10]);
+ round2(B, C, m_MK[ 9], m_RK[ 9]); round1(C, D, m_MK[ 8], m_RK[ 8]);
+ round1(D, A, m_MK[ 7], m_RK[ 7]); round3(A, B, m_MK[ 6], m_RK[ 6]);
+ round2(B, C, m_MK[ 5], m_RK[ 5]); round1(C, D, m_MK[ 4], m_RK[ 4]);
+ round1(D, A, m_MK[ 3], m_RK[ 3]); round3(A, B, m_MK[ 2], m_RK[ 2]);
+ round2(B, C, m_MK[ 1], m_RK[ 1]); round1(C, D, m_MK[ 0], m_RK[ 0]);
+
+ store_be(out, A, B, C, D);
+
+ in += BLOCK_SIZE;
+ out += BLOCK_SIZE;
+ }
+ }
+
+/*
+* CAST-256 Key Schedule
+*/
+void CAST_256::key_schedule(const uint8_t key[], size_t length)
+ {
+ static const uint32_t KEY_MASK[192] = {
+ 0x5A827999, 0xC95C653A, 0x383650DB, 0xA7103C7C, 0x15EA281D, 0x84C413BE,
+ 0xF39DFF5F, 0x6277EB00, 0xD151D6A1, 0x402BC242, 0xAF05ADE3, 0x1DDF9984,
+ 0x8CB98525, 0xFB9370C6, 0x6A6D5C67, 0xD9474808, 0x482133A9, 0xB6FB1F4A,
+ 0x25D50AEB, 0x94AEF68C, 0x0388E22D, 0x7262CDCE, 0xE13CB96F, 0x5016A510,
+ 0xBEF090B1, 0x2DCA7C52, 0x9CA467F3, 0x0B7E5394, 0x7A583F35, 0xE9322AD6,
+ 0x580C1677, 0xC6E60218, 0x35BFEDB9, 0xA499D95A, 0x1373C4FB, 0x824DB09C,
+ 0xF1279C3D, 0x600187DE, 0xCEDB737F, 0x3DB55F20, 0xAC8F4AC1, 0x1B693662,
+ 0x8A432203, 0xF91D0DA4, 0x67F6F945, 0xD6D0E4E6, 0x45AAD087, 0xB484BC28,
+ 0x235EA7C9, 0x9238936A, 0x01127F0B, 0x6FEC6AAC, 0xDEC6564D, 0x4DA041EE,
+ 0xBC7A2D8F, 0x2B541930, 0x9A2E04D1, 0x0907F072, 0x77E1DC13, 0xE6BBC7B4,
+ 0x5595B355, 0xC46F9EF6, 0x33498A97, 0xA2237638, 0x10FD61D9, 0x7FD74D7A,
+ 0xEEB1391B, 0x5D8B24BC, 0xCC65105D, 0x3B3EFBFE, 0xAA18E79F, 0x18F2D340,
+ 0x87CCBEE1, 0xF6A6AA82, 0x65809623, 0xD45A81C4, 0x43346D65, 0xB20E5906,
+ 0x20E844A7, 0x8FC23048, 0xFE9C1BE9, 0x6D76078A, 0xDC4FF32B, 0x4B29DECC,
+ 0xBA03CA6D, 0x28DDB60E, 0x97B7A1AF, 0x06918D50, 0x756B78F1, 0xE4456492,
+ 0x531F5033, 0xC1F93BD4, 0x30D32775, 0x9FAD1316, 0x0E86FEB7, 0x7D60EA58,
+ 0xEC3AD5F9, 0x5B14C19A, 0xC9EEAD3B, 0x38C898DC, 0xA7A2847D, 0x167C701E,
+ 0x85565BBF, 0xF4304760, 0x630A3301, 0xD1E41EA2, 0x40BE0A43, 0xAF97F5E4,
+ 0x1E71E185, 0x8D4BCD26, 0xFC25B8C7, 0x6AFFA468, 0xD9D99009, 0x48B37BAA,
+ 0xB78D674B, 0x266752EC, 0x95413E8D, 0x041B2A2E, 0x72F515CF, 0xE1CF0170,
+ 0x50A8ED11, 0xBF82D8B2, 0x2E5CC453, 0x9D36AFF4, 0x0C109B95, 0x7AEA8736,
+ 0xE9C472D7, 0x589E5E78, 0xC7784A19, 0x365235BA, 0xA52C215B, 0x14060CFC,
+ 0x82DFF89D, 0xF1B9E43E, 0x6093CFDF, 0xCF6DBB80, 0x3E47A721, 0xAD2192C2,
+ 0x1BFB7E63, 0x8AD56A04, 0xF9AF55A5, 0x68894146, 0xD7632CE7, 0x463D1888,
+ 0xB5170429, 0x23F0EFCA, 0x92CADB6B, 0x01A4C70C, 0x707EB2AD, 0xDF589E4E,
+ 0x4E3289EF, 0xBD0C7590, 0x2BE66131, 0x9AC04CD2, 0x099A3873, 0x78742414,
+ 0xE74E0FB5, 0x5627FB56, 0xC501E6F7, 0x33DBD298, 0xA2B5BE39, 0x118FA9DA,
+ 0x8069957B, 0xEF43811C, 0x5E1D6CBD, 0xCCF7585E, 0x3BD143FF, 0xAAAB2FA0,
+ 0x19851B41, 0x885F06E2, 0xF738F283, 0x6612DE24, 0xD4ECC9C5, 0x43C6B566,
+ 0xB2A0A107, 0x217A8CA8, 0x90547849, 0xFF2E63EA, 0x6E084F8B, 0xDCE23B2C,
+ 0x4BBC26CD, 0xBA96126E, 0x296FFE0F, 0x9849E9B0, 0x0723D551, 0x75FDC0F2,
+ 0xE4D7AC93, 0x53B19834, 0xC28B83D5, 0x31656F76, 0xA03F5B17, 0x0F1946B8 };
+
+ static const uint8_t KEY_ROT[32] = {
+ 0x13, 0x04, 0x15, 0x06, 0x17, 0x08, 0x19, 0x0A, 0x1B, 0x0C,
+ 0x1D, 0x0E, 0x1F, 0x10, 0x01, 0x12, 0x03, 0x14, 0x05, 0x16,
+ 0x07, 0x18, 0x09, 0x1A, 0x0B, 0x1C, 0x0D, 0x1E, 0x0F, 0x00,
+ 0x11, 0x02 };
+
+ m_MK.resize(48);
+ m_RK.resize(48);
+
+ secure_vector<uint32_t> K(8);
+ for(size_t i = 0; i != length; ++i)
+ K[i/4] = (K[i/4] << 8) + key[i];
+
+ uint32_t A = K[0], B = K[1], C = K[2], D = K[3],
+ E = K[4], F = K[5], G = K[6], H = K[7];
+
+ for(size_t i = 0; i != 48; i += 4)
+ {
+ round1(G, H, KEY_MASK[4*i+ 0], KEY_ROT[(4*i+ 0) % 32]);
+ round2(F, G, KEY_MASK[4*i+ 1], KEY_ROT[(4*i+ 1) % 32]);
+ round3(E, F, KEY_MASK[4*i+ 2], KEY_ROT[(4*i+ 2) % 32]);
+ round1(D, E, KEY_MASK[4*i+ 3], KEY_ROT[(4*i+ 3) % 32]);
+ round2(C, D, KEY_MASK[4*i+ 4], KEY_ROT[(4*i+ 4) % 32]);
+ round3(B, C, KEY_MASK[4*i+ 5], KEY_ROT[(4*i+ 5) % 32]);
+ round1(A, B, KEY_MASK[4*i+ 6], KEY_ROT[(4*i+ 6) % 32]);
+ round2(H, A, KEY_MASK[4*i+ 7], KEY_ROT[(4*i+ 7) % 32]);
+ round1(G, H, KEY_MASK[4*i+ 8], KEY_ROT[(4*i+ 8) % 32]);
+ round2(F, G, KEY_MASK[4*i+ 9], KEY_ROT[(4*i+ 9) % 32]);
+ round3(E, F, KEY_MASK[4*i+10], KEY_ROT[(4*i+10) % 32]);
+ round1(D, E, KEY_MASK[4*i+11], KEY_ROT[(4*i+11) % 32]);
+ round2(C, D, KEY_MASK[4*i+12], KEY_ROT[(4*i+12) % 32]);
+ round3(B, C, KEY_MASK[4*i+13], KEY_ROT[(4*i+13) % 32]);
+ round1(A, B, KEY_MASK[4*i+14], KEY_ROT[(4*i+14) % 32]);
+ round2(H, A, KEY_MASK[4*i+15], KEY_ROT[(4*i+15) % 32]);
+
+ m_RK[i ] = (A % 32);
+ m_RK[i+1] = (C % 32);
+ m_RK[i+2] = (E % 32);
+ m_RK[i+3] = (G % 32);
+ m_MK[i ] = H;
+ m_MK[i+1] = F;
+ m_MK[i+2] = D;
+ m_MK[i+3] = B;
+ }
+ }
+
+void CAST_256::clear()
+ {
+ zap(m_MK);
+ zap(m_RK);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/block/cast256/cast256.h b/comm/third_party/botan/src/lib/block/cast256/cast256.h
new file mode 100644
index 0000000000..3c30169372
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/cast256/cast256.h
@@ -0,0 +1,38 @@
+/*
+* CAST-256
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_CAST256_H_
+#define BOTAN_CAST256_H_
+
+#include <botan/block_cipher.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(cast256.h)
+
+namespace Botan {
+
+/**
+* CAST-256
+*/
+class BOTAN_PUBLIC_API(2,0) CAST_256 final : public Block_Cipher_Fixed_Params<16, 4, 32, 4>
+ {
+ public:
+ void encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+ void decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+
+ void clear() override;
+ std::string name() const override { return "CAST-256"; }
+ BlockCipher* clone() const override { return new CAST_256; }
+ private:
+ void key_schedule(const uint8_t[], size_t) override;
+
+ secure_vector<uint32_t> m_MK;
+ secure_vector<uint8_t> m_RK;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/block/cast256/info.txt b/comm/third_party/botan/src/lib/block/cast256/info.txt
new file mode 100644
index 0000000000..b109fe0533
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/cast256/info.txt
@@ -0,0 +1,7 @@
+<defines>
+CAST_256 -> 20171203
+</defines>
+
+<requires>
+cast128
+</requires>
diff --git a/comm/third_party/botan/src/lib/block/des/des.cpp b/comm/third_party/botan/src/lib/block/des/des.cpp
new file mode 100644
index 0000000000..0aa9e6a791
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/des/des.cpp
@@ -0,0 +1,410 @@
+/*
+* DES
+* (C) 1999-2008,2018 Jack Lloyd
+*
+* Based on a public domain implemenation by Phil Karn (who in turn
+* credited Richard Outerbridge and Jim Gillogly)
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/des.h>
+#include <botan/loadstor.h>
+#include <botan/rotate.h>
+
+namespace Botan {
+
+namespace {
+
+/*
+* DES Key Schedule
+*/
+void des_key_schedule(uint32_t round_key[32], 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) |
+ ((key[3] & 0x80) << 16) | ((key[2] & 0x80) << 15) |
+ ((key[1] & 0x80) << 14) | ((key[0] & 0x80) << 13) |
+ ((key[7] & 0x40) << 13) | ((key[6] & 0x40) << 12) |
+ ((key[5] & 0x40) << 11) | ((key[4] & 0x40) << 10) |
+ ((key[3] & 0x40) << 9) | ((key[2] & 0x40) << 8) |
+ ((key[1] & 0x40) << 7) | ((key[0] & 0x40) << 6) |
+ ((key[7] & 0x20) << 6) | ((key[6] & 0x20) << 5) |
+ ((key[5] & 0x20) << 4) | ((key[4] & 0x20) << 3) |
+ ((key[3] & 0x20) << 2) | ((key[2] & 0x20) << 1) |
+ ((key[1] & 0x20) ) | ((key[0] & 0x20) >> 1) |
+ ((key[7] & 0x10) >> 1) | ((key[6] & 0x10) >> 2) |
+ ((key[5] & 0x10) >> 3) | ((key[4] & 0x10) >> 4);
+ uint32_t D = ((key[7] & 0x02) << 26) | ((key[6] & 0x02) << 25) |
+ ((key[5] & 0x02) << 24) | ((key[4] & 0x02) << 23) |
+ ((key[3] & 0x02) << 22) | ((key[2] & 0x02) << 21) |
+ ((key[1] & 0x02) << 20) | ((key[0] & 0x02) << 19) |
+ ((key[7] & 0x04) << 17) | ((key[6] & 0x04) << 16) |
+ ((key[5] & 0x04) << 15) | ((key[4] & 0x04) << 14) |
+ ((key[3] & 0x04) << 13) | ((key[2] & 0x04) << 12) |
+ ((key[1] & 0x04) << 11) | ((key[0] & 0x04) << 10) |
+ ((key[7] & 0x08) << 8) | ((key[6] & 0x08) << 7) |
+ ((key[5] & 0x08) << 6) | ((key[4] & 0x08) << 5) |
+ ((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);
+
+ 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 DES_SPBOX1[get_byte(0, T0)] ^ DES_SPBOX2[get_byte(0, T1)] ^
+ DES_SPBOX3[get_byte(1, T0)] ^ DES_SPBOX4[get_byte(1, T1)] ^
+ DES_SPBOX5[get_byte(2, T0)] ^ DES_SPBOX6[get_byte(2, T1)] ^
+ DES_SPBOX7[get_byte(3, T0)] ^ DES_SPBOX8[get_byte(3, T1)];
+ }
+
+/*
+* 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]);
+ }
+
+ 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, const uint8_t block[])
+ {
+ // IP sequence by Wei Dai, taken from public domain Crypto++
+ L = load_be<uint32_t>(block, 0);
+ R = load_be<uint32_t>(block, 1);
+
+ 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, uint8_t out[])
+ {
+ // 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);
+
+ store_be(out, R, L);
+ }
+
+}
+
+/*
+* DES Encryption
+*/
+void DES::encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+ {
+ verify_key_set(m_round_key.empty() == false);
+
+ while(blocks >= 2)
+ {
+ uint32_t L0, R0;
+ uint32_t L1, R1;
+
+ des_IP(L0, R0, in);
+ des_IP(L1, R1, in + BLOCK_SIZE);
+
+ des_encrypt_x2(L0, R0, L1, R1, m_round_key.data());
+
+ des_FP(L0, R0, out);
+ des_FP(L1, R1, out + BLOCK_SIZE);
+
+ in += 2*BLOCK_SIZE;
+ out += 2*BLOCK_SIZE;
+ blocks -= 2;
+ }
+
+ for(size_t i = 0; i < blocks; ++i)
+ {
+ uint32_t L, R;
+ des_IP(L, R, in + BLOCK_SIZE*i);
+ des_encrypt(L, R, m_round_key.data());
+ des_FP(L, R, out + BLOCK_SIZE*i);
+ }
+ }
+
+/*
+* DES Decryption
+*/
+void DES::decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+ {
+ verify_key_set(m_round_key.empty() == false);
+
+ while(blocks >= 2)
+ {
+ uint32_t L0, R0;
+ uint32_t L1, R1;
+
+ des_IP(L0, R0, in);
+ des_IP(L1, R1, in + BLOCK_SIZE);
+
+ des_decrypt_x2(L0, R0, L1, R1, m_round_key.data());
+
+ des_FP(L0, R0, out);
+ des_FP(L1, R1, out + BLOCK_SIZE);
+
+ in += 2*BLOCK_SIZE;
+ out += 2*BLOCK_SIZE;
+ blocks -= 2;
+ }
+
+ for(size_t i = 0; i < blocks; ++i)
+ {
+ uint32_t L, R;
+ des_IP(L, R, in + BLOCK_SIZE*i);
+ des_decrypt(L, R, m_round_key.data());
+ des_FP(L, R, out + BLOCK_SIZE*i);
+ }
+ }
+
+/*
+* DES Key Schedule
+*/
+void DES::key_schedule(const uint8_t key[], size_t)
+ {
+ m_round_key.resize(32);
+ des_key_schedule(m_round_key.data(), key);
+ }
+
+void DES::clear()
+ {
+ zap(m_round_key);
+ }
+
+/*
+* TripleDES Encryption
+*/
+void TripleDES::encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+ {
+ verify_key_set(m_round_key.empty() == false);
+
+ while(blocks >= 2)
+ {
+ uint32_t L0, R0;
+ uint32_t L1, R1;
+
+ des_IP(L0, R0, in);
+ des_IP(L1, R1, in + BLOCK_SIZE);
+
+ 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, out);
+ des_FP(L1, R1, out + BLOCK_SIZE);
+
+ in += 2*BLOCK_SIZE;
+ out += 2*BLOCK_SIZE;
+ blocks -= 2;
+ }
+
+ for(size_t i = 0; i != blocks; ++i)
+ {
+ uint32_t L, R;
+ des_IP(L, R, in + BLOCK_SIZE*i);
+
+ des_encrypt(L, R, &m_round_key[0]);
+ des_decrypt(R, L, &m_round_key[32]);
+ des_encrypt(L, R, &m_round_key[64]);
+
+ des_FP(L, R, out + BLOCK_SIZE*i);
+ }
+ }
+
+/*
+* TripleDES Decryption
+*/
+void TripleDES::decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+ {
+ verify_key_set(m_round_key.empty() == false);
+
+ while(blocks >= 2)
+ {
+ uint32_t L0, R0;
+ uint32_t L1, R1;
+
+ des_IP(L0, R0, in);
+ des_IP(L1, R1, in + BLOCK_SIZE);
+
+ 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, out);
+ des_FP(L1, R1, out + BLOCK_SIZE);
+
+ in += 2*BLOCK_SIZE;
+ out += 2*BLOCK_SIZE;
+ blocks -= 2;
+ }
+
+ for(size_t i = 0; i != blocks; ++i)
+ {
+ uint32_t L, R;
+ des_IP(L, R, in + BLOCK_SIZE*i);
+
+ des_decrypt(L, R, &m_round_key[64]);
+ des_encrypt(R, L, &m_round_key[32]);
+ des_decrypt(L, R, &m_round_key[0]);
+
+ des_FP(L, R, out + BLOCK_SIZE*i);
+ }
+ }
+
+/*
+* TripleDES Key Schedule
+*/
+void TripleDES::key_schedule(const uint8_t key[], size_t length)
+ {
+ m_round_key.resize(3*32);
+ des_key_schedule(&m_round_key[0], key);
+ des_key_schedule(&m_round_key[32], key + 8);
+
+ if(length == 24)
+ des_key_schedule(&m_round_key[64], key + 16);
+ else
+ copy_mem(&m_round_key[64], &m_round_key[0], 32);
+ }
+
+void TripleDES::clear()
+ {
+ zap(m_round_key);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/block/des/des.h b/comm/third_party/botan/src/lib/block/des/des.h
new file mode 100644
index 0000000000..d8bbcfdd10
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/des/des.h
@@ -0,0 +1,67 @@
+/*
+* DES
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_DES_H_
+#define BOTAN_DES_H_
+
+#include <botan/block_cipher.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(des.h)
+
+namespace Botan {
+
+/**
+* DES
+*/
+class BOTAN_PUBLIC_API(2,0) DES final : public Block_Cipher_Fixed_Params<8, 8>
+ {
+ public:
+ void encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+ void decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+
+ void clear() override;
+ std::string name() const override { return "DES"; }
+ BlockCipher* clone() const override { return new DES; }
+ private:
+ void key_schedule(const uint8_t[], size_t) override;
+
+ secure_vector<uint32_t> m_round_key;
+ };
+
+/**
+* Triple DES
+*/
+class BOTAN_PUBLIC_API(2,0) TripleDES final : public Block_Cipher_Fixed_Params<8, 16, 24, 8>
+ {
+ public:
+ void encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+ void decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+
+ void clear() override;
+ std::string name() const override { return "TripleDES"; }
+ BlockCipher* clone() const override { return new TripleDES; }
+ private:
+ void key_schedule(const uint8_t[], size_t) override;
+
+ secure_vector<uint32_t> m_round_key;
+ };
+
+/*
+* DES Tables
+*/
+extern const uint32_t DES_SPBOX1[256];
+extern const uint32_t DES_SPBOX2[256];
+extern const uint32_t DES_SPBOX3[256];
+extern const uint32_t DES_SPBOX4[256];
+extern const uint32_t DES_SPBOX5[256];
+extern const uint32_t DES_SPBOX6[256];
+extern const uint32_t DES_SPBOX7[256];
+extern const uint32_t DES_SPBOX8[256];
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/block/des/des_tab.cpp b/comm/third_party/botan/src/lib/block/des/des_tab.cpp
new file mode 100644
index 0000000000..cb6ab4e7e9
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/des/des_tab.cpp
@@ -0,0 +1,372 @@
+/*
+* Substitution/Permutation Tables for DES
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/des.h>
+
+namespace Botan {
+
+alignas(64) const uint32_t DES_SPBOX1[256] = {
+ 0x01010400, 0x00000000, 0x00010000, 0x01010404, 0x01010004, 0x00010404,
+ 0x00000004, 0x00010000, 0x00000400, 0x01010400, 0x01010404, 0x00000400,
+ 0x01000404, 0x01010004, 0x01000000, 0x00000004, 0x00000404, 0x01000400,
+ 0x01000400, 0x00010400, 0x00010400, 0x01010000, 0x01010000, 0x01000404,
+ 0x00010004, 0x01000004, 0x01000004, 0x00010004, 0x00000000, 0x00000404,
+ 0x00010404, 0x01000000, 0x00010000, 0x01010404, 0x00000004, 0x01010000,
+ 0x01010400, 0x01000000, 0x01000000, 0x00000400, 0x01010004, 0x00010000,
+ 0x00010400, 0x01000004, 0x00000400, 0x00000004, 0x01000404, 0x00010404,
+ 0x01010404, 0x00010004, 0x01010000, 0x01000404, 0x01000004, 0x00000404,
+ 0x00010404, 0x01010400, 0x00000404, 0x01000400, 0x01000400, 0x00000000,
+ 0x00010004, 0x00010400, 0x00000000, 0x01010004, 0x01010400, 0x00000000,
+ 0x00010000, 0x01010404, 0x01010004, 0x00010404, 0x00000004, 0x00010000,
+ 0x00000400, 0x01010400, 0x01010404, 0x00000400, 0x01000404, 0x01010004,
+ 0x01000000, 0x00000004, 0x00000404, 0x01000400, 0x01000400, 0x00010400,
+ 0x00010400, 0x01010000, 0x01010000, 0x01000404, 0x00010004, 0x01000004,
+ 0x01000004, 0x00010004, 0x00000000, 0x00000404, 0x00010404, 0x01000000,
+ 0x00010000, 0x01010404, 0x00000004, 0x01010000, 0x01010400, 0x01000000,
+ 0x01000000, 0x00000400, 0x01010004, 0x00010000, 0x00010400, 0x01000004,
+ 0x00000400, 0x00000004, 0x01000404, 0x00010404, 0x01010404, 0x00010004,
+ 0x01010000, 0x01000404, 0x01000004, 0x00000404, 0x00010404, 0x01010400,
+ 0x00000404, 0x01000400, 0x01000400, 0x00000000, 0x00010004, 0x00010400,
+ 0x00000000, 0x01010004, 0x01010400, 0x00000000, 0x00010000, 0x01010404,
+ 0x01010004, 0x00010404, 0x00000004, 0x00010000, 0x00000400, 0x01010400,
+ 0x01010404, 0x00000400, 0x01000404, 0x01010004, 0x01000000, 0x00000004,
+ 0x00000404, 0x01000400, 0x01000400, 0x00010400, 0x00010400, 0x01010000,
+ 0x01010000, 0x01000404, 0x00010004, 0x01000004, 0x01000004, 0x00010004,
+ 0x00000000, 0x00000404, 0x00010404, 0x01000000, 0x00010000, 0x01010404,
+ 0x00000004, 0x01010000, 0x01010400, 0x01000000, 0x01000000, 0x00000400,
+ 0x01010004, 0x00010000, 0x00010400, 0x01000004, 0x00000400, 0x00000004,
+ 0x01000404, 0x00010404, 0x01010404, 0x00010004, 0x01010000, 0x01000404,
+ 0x01000004, 0x00000404, 0x00010404, 0x01010400, 0x00000404, 0x01000400,
+ 0x01000400, 0x00000000, 0x00010004, 0x00010400, 0x00000000, 0x01010004,
+ 0x01010400, 0x00000000, 0x00010000, 0x01010404, 0x01010004, 0x00010404,
+ 0x00000004, 0x00010000, 0x00000400, 0x01010400, 0x01010404, 0x00000400,
+ 0x01000404, 0x01010004, 0x01000000, 0x00000004, 0x00000404, 0x01000400,
+ 0x01000400, 0x00010400, 0x00010400, 0x01010000, 0x01010000, 0x01000404,
+ 0x00010004, 0x01000004, 0x01000004, 0x00010004, 0x00000000, 0x00000404,
+ 0x00010404, 0x01000000, 0x00010000, 0x01010404, 0x00000004, 0x01010000,
+ 0x01010400, 0x01000000, 0x01000000, 0x00000400, 0x01010004, 0x00010000,
+ 0x00010400, 0x01000004, 0x00000400, 0x00000004, 0x01000404, 0x00010404,
+ 0x01010404, 0x00010004, 0x01010000, 0x01000404, 0x01000004, 0x00000404,
+ 0x00010404, 0x01010400, 0x00000404, 0x01000400, 0x01000400, 0x00000000,
+ 0x00010004, 0x00010400, 0x00000000, 0x01010004 };
+
+alignas(64) const uint32_t DES_SPBOX2[256] = {
+ 0x80108020, 0x80008000, 0x00008000, 0x00108020, 0x00100000, 0x00000020,
+ 0x80100020, 0x80008020, 0x80000020, 0x80108020, 0x80108000, 0x80000000,
+ 0x80008000, 0x00100000, 0x00000020, 0x80100020, 0x00108000, 0x00100020,
+ 0x80008020, 0x00000000, 0x80000000, 0x00008000, 0x00108020, 0x80100000,
+ 0x00100020, 0x80000020, 0x00000000, 0x00108000, 0x00008020, 0x80108000,
+ 0x80100000, 0x00008020, 0x00000000, 0x00108020, 0x80100020, 0x00100000,
+ 0x80008020, 0x80100000, 0x80108000, 0x00008000, 0x80100000, 0x80008000,
+ 0x00000020, 0x80108020, 0x00108020, 0x00000020, 0x00008000, 0x80000000,
+ 0x00008020, 0x80108000, 0x00100000, 0x80000020, 0x00100020, 0x80008020,
+ 0x80000020, 0x00100020, 0x00108000, 0x00000000, 0x80008000, 0x00008020,
+ 0x80000000, 0x80100020, 0x80108020, 0x00108000, 0x80108020, 0x80008000,
+ 0x00008000, 0x00108020, 0x00100000, 0x00000020, 0x80100020, 0x80008020,
+ 0x80000020, 0x80108020, 0x80108000, 0x80000000, 0x80008000, 0x00100000,
+ 0x00000020, 0x80100020, 0x00108000, 0x00100020, 0x80008020, 0x00000000,
+ 0x80000000, 0x00008000, 0x00108020, 0x80100000, 0x00100020, 0x80000020,
+ 0x00000000, 0x00108000, 0x00008020, 0x80108000, 0x80100000, 0x00008020,
+ 0x00000000, 0x00108020, 0x80100020, 0x00100000, 0x80008020, 0x80100000,
+ 0x80108000, 0x00008000, 0x80100000, 0x80008000, 0x00000020, 0x80108020,
+ 0x00108020, 0x00000020, 0x00008000, 0x80000000, 0x00008020, 0x80108000,
+ 0x00100000, 0x80000020, 0x00100020, 0x80008020, 0x80000020, 0x00100020,
+ 0x00108000, 0x00000000, 0x80008000, 0x00008020, 0x80000000, 0x80100020,
+ 0x80108020, 0x00108000, 0x80108020, 0x80008000, 0x00008000, 0x00108020,
+ 0x00100000, 0x00000020, 0x80100020, 0x80008020, 0x80000020, 0x80108020,
+ 0x80108000, 0x80000000, 0x80008000, 0x00100000, 0x00000020, 0x80100020,
+ 0x00108000, 0x00100020, 0x80008020, 0x00000000, 0x80000000, 0x00008000,
+ 0x00108020, 0x80100000, 0x00100020, 0x80000020, 0x00000000, 0x00108000,
+ 0x00008020, 0x80108000, 0x80100000, 0x00008020, 0x00000000, 0x00108020,
+ 0x80100020, 0x00100000, 0x80008020, 0x80100000, 0x80108000, 0x00008000,
+ 0x80100000, 0x80008000, 0x00000020, 0x80108020, 0x00108020, 0x00000020,
+ 0x00008000, 0x80000000, 0x00008020, 0x80108000, 0x00100000, 0x80000020,
+ 0x00100020, 0x80008020, 0x80000020, 0x00100020, 0x00108000, 0x00000000,
+ 0x80008000, 0x00008020, 0x80000000, 0x80100020, 0x80108020, 0x00108000,
+ 0x80108020, 0x80008000, 0x00008000, 0x00108020, 0x00100000, 0x00000020,
+ 0x80100020, 0x80008020, 0x80000020, 0x80108020, 0x80108000, 0x80000000,
+ 0x80008000, 0x00100000, 0x00000020, 0x80100020, 0x00108000, 0x00100020,
+ 0x80008020, 0x00000000, 0x80000000, 0x00008000, 0x00108020, 0x80100000,
+ 0x00100020, 0x80000020, 0x00000000, 0x00108000, 0x00008020, 0x80108000,
+ 0x80100000, 0x00008020, 0x00000000, 0x00108020, 0x80100020, 0x00100000,
+ 0x80008020, 0x80100000, 0x80108000, 0x00008000, 0x80100000, 0x80008000,
+ 0x00000020, 0x80108020, 0x00108020, 0x00000020, 0x00008000, 0x80000000,
+ 0x00008020, 0x80108000, 0x00100000, 0x80000020, 0x00100020, 0x80008020,
+ 0x80000020, 0x00100020, 0x00108000, 0x00000000, 0x80008000, 0x00008020,
+ 0x80000000, 0x80100020, 0x80108020, 0x00108000 };
+
+alignas(64) const uint32_t DES_SPBOX3[256] = {
+ 0x00000208, 0x08020200, 0x00000000, 0x08020008, 0x08000200, 0x00000000,
+ 0x00020208, 0x08000200, 0x00020008, 0x08000008, 0x08000008, 0x00020000,
+ 0x08020208, 0x00020008, 0x08020000, 0x00000208, 0x08000000, 0x00000008,
+ 0x08020200, 0x00000200, 0x00020200, 0x08020000, 0x08020008, 0x00020208,
+ 0x08000208, 0x00020200, 0x00020000, 0x08000208, 0x00000008, 0x08020208,
+ 0x00000200, 0x08000000, 0x08020200, 0x08000000, 0x00020008, 0x00000208,
+ 0x00020000, 0x08020200, 0x08000200, 0x00000000, 0x00000200, 0x00020008,
+ 0x08020208, 0x08000200, 0x08000008, 0x00000200, 0x00000000, 0x08020008,
+ 0x08000208, 0x00020000, 0x08000000, 0x08020208, 0x00000008, 0x00020208,
+ 0x00020200, 0x08000008, 0x08020000, 0x08000208, 0x00000208, 0x08020000,
+ 0x00020208, 0x00000008, 0x08020008, 0x00020200, 0x00000208, 0x08020200,
+ 0x00000000, 0x08020008, 0x08000200, 0x00000000, 0x00020208, 0x08000200,
+ 0x00020008, 0x08000008, 0x08000008, 0x00020000, 0x08020208, 0x00020008,
+ 0x08020000, 0x00000208, 0x08000000, 0x00000008, 0x08020200, 0x00000200,
+ 0x00020200, 0x08020000, 0x08020008, 0x00020208, 0x08000208, 0x00020200,
+ 0x00020000, 0x08000208, 0x00000008, 0x08020208, 0x00000200, 0x08000000,
+ 0x08020200, 0x08000000, 0x00020008, 0x00000208, 0x00020000, 0x08020200,
+ 0x08000200, 0x00000000, 0x00000200, 0x00020008, 0x08020208, 0x08000200,
+ 0x08000008, 0x00000200, 0x00000000, 0x08020008, 0x08000208, 0x00020000,
+ 0x08000000, 0x08020208, 0x00000008, 0x00020208, 0x00020200, 0x08000008,
+ 0x08020000, 0x08000208, 0x00000208, 0x08020000, 0x00020208, 0x00000008,
+ 0x08020008, 0x00020200, 0x00000208, 0x08020200, 0x00000000, 0x08020008,
+ 0x08000200, 0x00000000, 0x00020208, 0x08000200, 0x00020008, 0x08000008,
+ 0x08000008, 0x00020000, 0x08020208, 0x00020008, 0x08020000, 0x00000208,
+ 0x08000000, 0x00000008, 0x08020200, 0x00000200, 0x00020200, 0x08020000,
+ 0x08020008, 0x00020208, 0x08000208, 0x00020200, 0x00020000, 0x08000208,
+ 0x00000008, 0x08020208, 0x00000200, 0x08000000, 0x08020200, 0x08000000,
+ 0x00020008, 0x00000208, 0x00020000, 0x08020200, 0x08000200, 0x00000000,
+ 0x00000200, 0x00020008, 0x08020208, 0x08000200, 0x08000008, 0x00000200,
+ 0x00000000, 0x08020008, 0x08000208, 0x00020000, 0x08000000, 0x08020208,
+ 0x00000008, 0x00020208, 0x00020200, 0x08000008, 0x08020000, 0x08000208,
+ 0x00000208, 0x08020000, 0x00020208, 0x00000008, 0x08020008, 0x00020200,
+ 0x00000208, 0x08020200, 0x00000000, 0x08020008, 0x08000200, 0x00000000,
+ 0x00020208, 0x08000200, 0x00020008, 0x08000008, 0x08000008, 0x00020000,
+ 0x08020208, 0x00020008, 0x08020000, 0x00000208, 0x08000000, 0x00000008,
+ 0x08020200, 0x00000200, 0x00020200, 0x08020000, 0x08020008, 0x00020208,
+ 0x08000208, 0x00020200, 0x00020000, 0x08000208, 0x00000008, 0x08020208,
+ 0x00000200, 0x08000000, 0x08020200, 0x08000000, 0x00020008, 0x00000208,
+ 0x00020000, 0x08020200, 0x08000200, 0x00000000, 0x00000200, 0x00020008,
+ 0x08020208, 0x08000200, 0x08000008, 0x00000200, 0x00000000, 0x08020008,
+ 0x08000208, 0x00020000, 0x08000000, 0x08020208, 0x00000008, 0x00020208,
+ 0x00020200, 0x08000008, 0x08020000, 0x08000208, 0x00000208, 0x08020000,
+ 0x00020208, 0x00000008, 0x08020008, 0x00020200 };
+
+alignas(64) const uint32_t DES_SPBOX4[256] = {
+ 0x00802001, 0x00002081, 0x00002081, 0x00000080, 0x00802080, 0x00800081,
+ 0x00800001, 0x00002001, 0x00000000, 0x00802000, 0x00802000, 0x00802081,
+ 0x00000081, 0x00000000, 0x00800080, 0x00800001, 0x00000001, 0x00002000,
+ 0x00800000, 0x00802001, 0x00000080, 0x00800000, 0x00002001, 0x00002080,
+ 0x00800081, 0x00000001, 0x00002080, 0x00800080, 0x00002000, 0x00802080,
+ 0x00802081, 0x00000081, 0x00800080, 0x00800001, 0x00802000, 0x00802081,
+ 0x00000081, 0x00000000, 0x00000000, 0x00802000, 0x00002080, 0x00800080,
+ 0x00800081, 0x00000001, 0x00802001, 0x00002081, 0x00002081, 0x00000080,
+ 0x00802081, 0x00000081, 0x00000001, 0x00002000, 0x00800001, 0x00002001,
+ 0x00802080, 0x00800081, 0x00002001, 0x00002080, 0x00800000, 0x00802001,
+ 0x00000080, 0x00800000, 0x00002000, 0x00802080, 0x00802001, 0x00002081,
+ 0x00002081, 0x00000080, 0x00802080, 0x00800081, 0x00800001, 0x00002001,
+ 0x00000000, 0x00802000, 0x00802000, 0x00802081, 0x00000081, 0x00000000,
+ 0x00800080, 0x00800001, 0x00000001, 0x00002000, 0x00800000, 0x00802001,
+ 0x00000080, 0x00800000, 0x00002001, 0x00002080, 0x00800081, 0x00000001,
+ 0x00002080, 0x00800080, 0x00002000, 0x00802080, 0x00802081, 0x00000081,
+ 0x00800080, 0x00800001, 0x00802000, 0x00802081, 0x00000081, 0x00000000,
+ 0x00000000, 0x00802000, 0x00002080, 0x00800080, 0x00800081, 0x00000001,
+ 0x00802001, 0x00002081, 0x00002081, 0x00000080, 0x00802081, 0x00000081,
+ 0x00000001, 0x00002000, 0x00800001, 0x00002001, 0x00802080, 0x00800081,
+ 0x00002001, 0x00002080, 0x00800000, 0x00802001, 0x00000080, 0x00800000,
+ 0x00002000, 0x00802080, 0x00802001, 0x00002081, 0x00002081, 0x00000080,
+ 0x00802080, 0x00800081, 0x00800001, 0x00002001, 0x00000000, 0x00802000,
+ 0x00802000, 0x00802081, 0x00000081, 0x00000000, 0x00800080, 0x00800001,
+ 0x00000001, 0x00002000, 0x00800000, 0x00802001, 0x00000080, 0x00800000,
+ 0x00002001, 0x00002080, 0x00800081, 0x00000001, 0x00002080, 0x00800080,
+ 0x00002000, 0x00802080, 0x00802081, 0x00000081, 0x00800080, 0x00800001,
+ 0x00802000, 0x00802081, 0x00000081, 0x00000000, 0x00000000, 0x00802000,
+ 0x00002080, 0x00800080, 0x00800081, 0x00000001, 0x00802001, 0x00002081,
+ 0x00002081, 0x00000080, 0x00802081, 0x00000081, 0x00000001, 0x00002000,
+ 0x00800001, 0x00002001, 0x00802080, 0x00800081, 0x00002001, 0x00002080,
+ 0x00800000, 0x00802001, 0x00000080, 0x00800000, 0x00002000, 0x00802080,
+ 0x00802001, 0x00002081, 0x00002081, 0x00000080, 0x00802080, 0x00800081,
+ 0x00800001, 0x00002001, 0x00000000, 0x00802000, 0x00802000, 0x00802081,
+ 0x00000081, 0x00000000, 0x00800080, 0x00800001, 0x00000001, 0x00002000,
+ 0x00800000, 0x00802001, 0x00000080, 0x00800000, 0x00002001, 0x00002080,
+ 0x00800081, 0x00000001, 0x00002080, 0x00800080, 0x00002000, 0x00802080,
+ 0x00802081, 0x00000081, 0x00800080, 0x00800001, 0x00802000, 0x00802081,
+ 0x00000081, 0x00000000, 0x00000000, 0x00802000, 0x00002080, 0x00800080,
+ 0x00800081, 0x00000001, 0x00802001, 0x00002081, 0x00002081, 0x00000080,
+ 0x00802081, 0x00000081, 0x00000001, 0x00002000, 0x00800001, 0x00002001,
+ 0x00802080, 0x00800081, 0x00002001, 0x00002080, 0x00800000, 0x00802001,
+ 0x00000080, 0x00800000, 0x00002000, 0x00802080 };
+
+alignas(64) const uint32_t DES_SPBOX5[256] = {
+ 0x00000100, 0x02080100, 0x02080000, 0x42000100, 0x00080000, 0x00000100,
+ 0x40000000, 0x02080000, 0x40080100, 0x00080000, 0x02000100, 0x40080100,
+ 0x42000100, 0x42080000, 0x00080100, 0x40000000, 0x02000000, 0x40080000,
+ 0x40080000, 0x00000000, 0x40000100, 0x42080100, 0x42080100, 0x02000100,
+ 0x42080000, 0x40000100, 0x00000000, 0x42000000, 0x02080100, 0x02000000,
+ 0x42000000, 0x00080100, 0x00080000, 0x42000100, 0x00000100, 0x02000000,
+ 0x40000000, 0x02080000, 0x42000100, 0x40080100, 0x02000100, 0x40000000,
+ 0x42080000, 0x02080100, 0x40080100, 0x00000100, 0x02000000, 0x42080000,
+ 0x42080100, 0x00080100, 0x42000000, 0x42080100, 0x02080000, 0x00000000,
+ 0x40080000, 0x42000000, 0x00080100, 0x02000100, 0x40000100, 0x00080000,
+ 0x00000000, 0x40080000, 0x02080100, 0x40000100, 0x00000100, 0x02080100,
+ 0x02080000, 0x42000100, 0x00080000, 0x00000100, 0x40000000, 0x02080000,
+ 0x40080100, 0x00080000, 0x02000100, 0x40080100, 0x42000100, 0x42080000,
+ 0x00080100, 0x40000000, 0x02000000, 0x40080000, 0x40080000, 0x00000000,
+ 0x40000100, 0x42080100, 0x42080100, 0x02000100, 0x42080000, 0x40000100,
+ 0x00000000, 0x42000000, 0x02080100, 0x02000000, 0x42000000, 0x00080100,
+ 0x00080000, 0x42000100, 0x00000100, 0x02000000, 0x40000000, 0x02080000,
+ 0x42000100, 0x40080100, 0x02000100, 0x40000000, 0x42080000, 0x02080100,
+ 0x40080100, 0x00000100, 0x02000000, 0x42080000, 0x42080100, 0x00080100,
+ 0x42000000, 0x42080100, 0x02080000, 0x00000000, 0x40080000, 0x42000000,
+ 0x00080100, 0x02000100, 0x40000100, 0x00080000, 0x00000000, 0x40080000,
+ 0x02080100, 0x40000100, 0x00000100, 0x02080100, 0x02080000, 0x42000100,
+ 0x00080000, 0x00000100, 0x40000000, 0x02080000, 0x40080100, 0x00080000,
+ 0x02000100, 0x40080100, 0x42000100, 0x42080000, 0x00080100, 0x40000000,
+ 0x02000000, 0x40080000, 0x40080000, 0x00000000, 0x40000100, 0x42080100,
+ 0x42080100, 0x02000100, 0x42080000, 0x40000100, 0x00000000, 0x42000000,
+ 0x02080100, 0x02000000, 0x42000000, 0x00080100, 0x00080000, 0x42000100,
+ 0x00000100, 0x02000000, 0x40000000, 0x02080000, 0x42000100, 0x40080100,
+ 0x02000100, 0x40000000, 0x42080000, 0x02080100, 0x40080100, 0x00000100,
+ 0x02000000, 0x42080000, 0x42080100, 0x00080100, 0x42000000, 0x42080100,
+ 0x02080000, 0x00000000, 0x40080000, 0x42000000, 0x00080100, 0x02000100,
+ 0x40000100, 0x00080000, 0x00000000, 0x40080000, 0x02080100, 0x40000100,
+ 0x00000100, 0x02080100, 0x02080000, 0x42000100, 0x00080000, 0x00000100,
+ 0x40000000, 0x02080000, 0x40080100, 0x00080000, 0x02000100, 0x40080100,
+ 0x42000100, 0x42080000, 0x00080100, 0x40000000, 0x02000000, 0x40080000,
+ 0x40080000, 0x00000000, 0x40000100, 0x42080100, 0x42080100, 0x02000100,
+ 0x42080000, 0x40000100, 0x00000000, 0x42000000, 0x02080100, 0x02000000,
+ 0x42000000, 0x00080100, 0x00080000, 0x42000100, 0x00000100, 0x02000000,
+ 0x40000000, 0x02080000, 0x42000100, 0x40080100, 0x02000100, 0x40000000,
+ 0x42080000, 0x02080100, 0x40080100, 0x00000100, 0x02000000, 0x42080000,
+ 0x42080100, 0x00080100, 0x42000000, 0x42080100, 0x02080000, 0x00000000,
+ 0x40080000, 0x42000000, 0x00080100, 0x02000100, 0x40000100, 0x00080000,
+ 0x00000000, 0x40080000, 0x02080100, 0x40000100 };
+
+alignas(64) const uint32_t DES_SPBOX6[256] = {
+ 0x20000010, 0x20400000, 0x00004000, 0x20404010, 0x20400000, 0x00000010,
+ 0x20404010, 0x00400000, 0x20004000, 0x00404010, 0x00400000, 0x20000010,
+ 0x00400010, 0x20004000, 0x20000000, 0x00004010, 0x00000000, 0x00400010,
+ 0x20004010, 0x00004000, 0x00404000, 0x20004010, 0x00000010, 0x20400010,
+ 0x20400010, 0x00000000, 0x00404010, 0x20404000, 0x00004010, 0x00404000,
+ 0x20404000, 0x20000000, 0x20004000, 0x00000010, 0x20400010, 0x00404000,
+ 0x20404010, 0x00400000, 0x00004010, 0x20000010, 0x00400000, 0x20004000,
+ 0x20000000, 0x00004010, 0x20000010, 0x20404010, 0x00404000, 0x20400000,
+ 0x00404010, 0x20404000, 0x00000000, 0x20400010, 0x00000010, 0x00004000,
+ 0x20400000, 0x00404010, 0x00004000, 0x00400010, 0x20004010, 0x00000000,
+ 0x20404000, 0x20000000, 0x00400010, 0x20004010, 0x20000010, 0x20400000,
+ 0x00004000, 0x20404010, 0x20400000, 0x00000010, 0x20404010, 0x00400000,
+ 0x20004000, 0x00404010, 0x00400000, 0x20000010, 0x00400010, 0x20004000,
+ 0x20000000, 0x00004010, 0x00000000, 0x00400010, 0x20004010, 0x00004000,
+ 0x00404000, 0x20004010, 0x00000010, 0x20400010, 0x20400010, 0x00000000,
+ 0x00404010, 0x20404000, 0x00004010, 0x00404000, 0x20404000, 0x20000000,
+ 0x20004000, 0x00000010, 0x20400010, 0x00404000, 0x20404010, 0x00400000,
+ 0x00004010, 0x20000010, 0x00400000, 0x20004000, 0x20000000, 0x00004010,
+ 0x20000010, 0x20404010, 0x00404000, 0x20400000, 0x00404010, 0x20404000,
+ 0x00000000, 0x20400010, 0x00000010, 0x00004000, 0x20400000, 0x00404010,
+ 0x00004000, 0x00400010, 0x20004010, 0x00000000, 0x20404000, 0x20000000,
+ 0x00400010, 0x20004010, 0x20000010, 0x20400000, 0x00004000, 0x20404010,
+ 0x20400000, 0x00000010, 0x20404010, 0x00400000, 0x20004000, 0x00404010,
+ 0x00400000, 0x20000010, 0x00400010, 0x20004000, 0x20000000, 0x00004010,
+ 0x00000000, 0x00400010, 0x20004010, 0x00004000, 0x00404000, 0x20004010,
+ 0x00000010, 0x20400010, 0x20400010, 0x00000000, 0x00404010, 0x20404000,
+ 0x00004010, 0x00404000, 0x20404000, 0x20000000, 0x20004000, 0x00000010,
+ 0x20400010, 0x00404000, 0x20404010, 0x00400000, 0x00004010, 0x20000010,
+ 0x00400000, 0x20004000, 0x20000000, 0x00004010, 0x20000010, 0x20404010,
+ 0x00404000, 0x20400000, 0x00404010, 0x20404000, 0x00000000, 0x20400010,
+ 0x00000010, 0x00004000, 0x20400000, 0x00404010, 0x00004000, 0x00400010,
+ 0x20004010, 0x00000000, 0x20404000, 0x20000000, 0x00400010, 0x20004010,
+ 0x20000010, 0x20400000, 0x00004000, 0x20404010, 0x20400000, 0x00000010,
+ 0x20404010, 0x00400000, 0x20004000, 0x00404010, 0x00400000, 0x20000010,
+ 0x00400010, 0x20004000, 0x20000000, 0x00004010, 0x00000000, 0x00400010,
+ 0x20004010, 0x00004000, 0x00404000, 0x20004010, 0x00000010, 0x20400010,
+ 0x20400010, 0x00000000, 0x00404010, 0x20404000, 0x00004010, 0x00404000,
+ 0x20404000, 0x20000000, 0x20004000, 0x00000010, 0x20400010, 0x00404000,
+ 0x20404010, 0x00400000, 0x00004010, 0x20000010, 0x00400000, 0x20004000,
+ 0x20000000, 0x00004010, 0x20000010, 0x20404010, 0x00404000, 0x20400000,
+ 0x00404010, 0x20404000, 0x00000000, 0x20400010, 0x00000010, 0x00004000,
+ 0x20400000, 0x00404010, 0x00004000, 0x00400010, 0x20004010, 0x00000000,
+ 0x20404000, 0x20000000, 0x00400010, 0x20004010 };
+
+alignas(64) const uint32_t DES_SPBOX7[256] = {
+ 0x00200000, 0x04200002, 0x04000802, 0x00000000, 0x00000800, 0x04000802,
+ 0x00200802, 0x04200800, 0x04200802, 0x00200000, 0x00000000, 0x04000002,
+ 0x00000002, 0x04000000, 0x04200002, 0x00000802, 0x04000800, 0x00200802,
+ 0x00200002, 0x04000800, 0x04000002, 0x04200000, 0x04200800, 0x00200002,
+ 0x04200000, 0x00000800, 0x00000802, 0x04200802, 0x00200800, 0x00000002,
+ 0x04000000, 0x00200800, 0x04000000, 0x00200800, 0x00200000, 0x04000802,
+ 0x04000802, 0x04200002, 0x04200002, 0x00000002, 0x00200002, 0x04000000,
+ 0x04000800, 0x00200000, 0x04200800, 0x00000802, 0x00200802, 0x04200800,
+ 0x00000802, 0x04000002, 0x04200802, 0x04200000, 0x00200800, 0x00000000,
+ 0x00000002, 0x04200802, 0x00000000, 0x00200802, 0x04200000, 0x00000800,
+ 0x04000002, 0x04000800, 0x00000800, 0x00200002, 0x00200000, 0x04200002,
+ 0x04000802, 0x00000000, 0x00000800, 0x04000802, 0x00200802, 0x04200800,
+ 0x04200802, 0x00200000, 0x00000000, 0x04000002, 0x00000002, 0x04000000,
+ 0x04200002, 0x00000802, 0x04000800, 0x00200802, 0x00200002, 0x04000800,
+ 0x04000002, 0x04200000, 0x04200800, 0x00200002, 0x04200000, 0x00000800,
+ 0x00000802, 0x04200802, 0x00200800, 0x00000002, 0x04000000, 0x00200800,
+ 0x04000000, 0x00200800, 0x00200000, 0x04000802, 0x04000802, 0x04200002,
+ 0x04200002, 0x00000002, 0x00200002, 0x04000000, 0x04000800, 0x00200000,
+ 0x04200800, 0x00000802, 0x00200802, 0x04200800, 0x00000802, 0x04000002,
+ 0x04200802, 0x04200000, 0x00200800, 0x00000000, 0x00000002, 0x04200802,
+ 0x00000000, 0x00200802, 0x04200000, 0x00000800, 0x04000002, 0x04000800,
+ 0x00000800, 0x00200002, 0x00200000, 0x04200002, 0x04000802, 0x00000000,
+ 0x00000800, 0x04000802, 0x00200802, 0x04200800, 0x04200802, 0x00200000,
+ 0x00000000, 0x04000002, 0x00000002, 0x04000000, 0x04200002, 0x00000802,
+ 0x04000800, 0x00200802, 0x00200002, 0x04000800, 0x04000002, 0x04200000,
+ 0x04200800, 0x00200002, 0x04200000, 0x00000800, 0x00000802, 0x04200802,
+ 0x00200800, 0x00000002, 0x04000000, 0x00200800, 0x04000000, 0x00200800,
+ 0x00200000, 0x04000802, 0x04000802, 0x04200002, 0x04200002, 0x00000002,
+ 0x00200002, 0x04000000, 0x04000800, 0x00200000, 0x04200800, 0x00000802,
+ 0x00200802, 0x04200800, 0x00000802, 0x04000002, 0x04200802, 0x04200000,
+ 0x00200800, 0x00000000, 0x00000002, 0x04200802, 0x00000000, 0x00200802,
+ 0x04200000, 0x00000800, 0x04000002, 0x04000800, 0x00000800, 0x00200002,
+ 0x00200000, 0x04200002, 0x04000802, 0x00000000, 0x00000800, 0x04000802,
+ 0x00200802, 0x04200800, 0x04200802, 0x00200000, 0x00000000, 0x04000002,
+ 0x00000002, 0x04000000, 0x04200002, 0x00000802, 0x04000800, 0x00200802,
+ 0x00200002, 0x04000800, 0x04000002, 0x04200000, 0x04200800, 0x00200002,
+ 0x04200000, 0x00000800, 0x00000802, 0x04200802, 0x00200800, 0x00000002,
+ 0x04000000, 0x00200800, 0x04000000, 0x00200800, 0x00200000, 0x04000802,
+ 0x04000802, 0x04200002, 0x04200002, 0x00000002, 0x00200002, 0x04000000,
+ 0x04000800, 0x00200000, 0x04200800, 0x00000802, 0x00200802, 0x04200800,
+ 0x00000802, 0x04000002, 0x04200802, 0x04200000, 0x00200800, 0x00000000,
+ 0x00000002, 0x04200802, 0x00000000, 0x00200802, 0x04200000, 0x00000800,
+ 0x04000002, 0x04000800, 0x00000800, 0x00200002 };
+
+alignas(64) const uint32_t DES_SPBOX8[256] = {
+ 0x10001040, 0x00001000, 0x00040000, 0x10041040, 0x10000000, 0x10001040,
+ 0x00000040, 0x10000000, 0x00040040, 0x10040000, 0x10041040, 0x00041000,
+ 0x10041000, 0x00041040, 0x00001000, 0x00000040, 0x10040000, 0x10000040,
+ 0x10001000, 0x00001040, 0x00041000, 0x00040040, 0x10040040, 0x10041000,
+ 0x00001040, 0x00000000, 0x00000000, 0x10040040, 0x10000040, 0x10001000,
+ 0x00041040, 0x00040000, 0x00041040, 0x00040000, 0x10041000, 0x00001000,
+ 0x00000040, 0x10040040, 0x00001000, 0x00041040, 0x10001000, 0x00000040,
+ 0x10000040, 0x10040000, 0x10040040, 0x10000000, 0x00040000, 0x10001040,
+ 0x00000000, 0x10041040, 0x00040040, 0x10000040, 0x10040000, 0x10001000,
+ 0x10001040, 0x00000000, 0x10041040, 0x00041000, 0x00041000, 0x00001040,
+ 0x00001040, 0x00040040, 0x10000000, 0x10041000, 0x10001040, 0x00001000,
+ 0x00040000, 0x10041040, 0x10000000, 0x10001040, 0x00000040, 0x10000000,
+ 0x00040040, 0x10040000, 0x10041040, 0x00041000, 0x10041000, 0x00041040,
+ 0x00001000, 0x00000040, 0x10040000, 0x10000040, 0x10001000, 0x00001040,
+ 0x00041000, 0x00040040, 0x10040040, 0x10041000, 0x00001040, 0x00000000,
+ 0x00000000, 0x10040040, 0x10000040, 0x10001000, 0x00041040, 0x00040000,
+ 0x00041040, 0x00040000, 0x10041000, 0x00001000, 0x00000040, 0x10040040,
+ 0x00001000, 0x00041040, 0x10001000, 0x00000040, 0x10000040, 0x10040000,
+ 0x10040040, 0x10000000, 0x00040000, 0x10001040, 0x00000000, 0x10041040,
+ 0x00040040, 0x10000040, 0x10040000, 0x10001000, 0x10001040, 0x00000000,
+ 0x10041040, 0x00041000, 0x00041000, 0x00001040, 0x00001040, 0x00040040,
+ 0x10000000, 0x10041000, 0x10001040, 0x00001000, 0x00040000, 0x10041040,
+ 0x10000000, 0x10001040, 0x00000040, 0x10000000, 0x00040040, 0x10040000,
+ 0x10041040, 0x00041000, 0x10041000, 0x00041040, 0x00001000, 0x00000040,
+ 0x10040000, 0x10000040, 0x10001000, 0x00001040, 0x00041000, 0x00040040,
+ 0x10040040, 0x10041000, 0x00001040, 0x00000000, 0x00000000, 0x10040040,
+ 0x10000040, 0x10001000, 0x00041040, 0x00040000, 0x00041040, 0x00040000,
+ 0x10041000, 0x00001000, 0x00000040, 0x10040040, 0x00001000, 0x00041040,
+ 0x10001000, 0x00000040, 0x10000040, 0x10040000, 0x10040040, 0x10000000,
+ 0x00040000, 0x10001040, 0x00000000, 0x10041040, 0x00040040, 0x10000040,
+ 0x10040000, 0x10001000, 0x10001040, 0x00000000, 0x10041040, 0x00041000,
+ 0x00041000, 0x00001040, 0x00001040, 0x00040040, 0x10000000, 0x10041000,
+ 0x10001040, 0x00001000, 0x00040000, 0x10041040, 0x10000000, 0x10001040,
+ 0x00000040, 0x10000000, 0x00040040, 0x10040000, 0x10041040, 0x00041000,
+ 0x10041000, 0x00041040, 0x00001000, 0x00000040, 0x10040000, 0x10000040,
+ 0x10001000, 0x00001040, 0x00041000, 0x00040040, 0x10040040, 0x10041000,
+ 0x00001040, 0x00000000, 0x00000000, 0x10040040, 0x10000040, 0x10001000,
+ 0x00041040, 0x00040000, 0x00041040, 0x00040000, 0x10041000, 0x00001000,
+ 0x00000040, 0x10040040, 0x00001000, 0x00041040, 0x10001000, 0x00000040,
+ 0x10000040, 0x10040000, 0x10040040, 0x10000000, 0x00040000, 0x10001040,
+ 0x00000000, 0x10041040, 0x00040040, 0x10000040, 0x10040000, 0x10001000,
+ 0x10001040, 0x00000000, 0x10041040, 0x00041000, 0x00041000, 0x00001040,
+ 0x00001040, 0x00040040, 0x10000000, 0x10041000 };
+
+}
diff --git a/comm/third_party/botan/src/lib/block/des/desx.cpp b/comm/third_party/botan/src/lib/block/des/desx.cpp
new file mode 100644
index 0000000000..e869b3ebf8
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/des/desx.cpp
@@ -0,0 +1,65 @@
+/*
+* DES
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/desx.h>
+
+namespace Botan {
+
+/*
+* DESX Encryption
+*/
+void DESX::encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+ {
+ verify_key_set(m_K1.empty() == false);
+
+ for(size_t i = 0; i != blocks; ++i)
+ {
+ xor_buf(out, in, m_K1.data(), BLOCK_SIZE);
+ m_des.encrypt(out);
+ xor_buf(out, m_K2.data(), BLOCK_SIZE);
+
+ in += BLOCK_SIZE;
+ out += BLOCK_SIZE;
+ }
+ }
+
+/*
+* DESX Decryption
+*/
+void DESX::decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+ {
+ verify_key_set(m_K1.empty() == false);
+
+ for(size_t i = 0; i != blocks; ++i)
+ {
+ xor_buf(out, in, m_K2.data(), BLOCK_SIZE);
+ m_des.decrypt(out);
+ xor_buf(out, m_K1.data(), BLOCK_SIZE);
+
+ in += BLOCK_SIZE;
+ out += BLOCK_SIZE;
+ }
+ }
+
+/*
+* DESX Key Schedule
+*/
+void DESX::key_schedule(const uint8_t key[], size_t)
+ {
+ m_K1.assign(key, key + 8);
+ m_des.set_key(key + 8, 8);
+ m_K2.assign(key + 16, key + 24);
+ }
+
+void DESX::clear()
+ {
+ m_des.clear();
+ zap(m_K1);
+ zap(m_K2);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/block/des/desx.h b/comm/third_party/botan/src/lib/block/des/desx.h
new file mode 100644
index 0000000000..0189a99826
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/des/desx.h
@@ -0,0 +1,37 @@
+/*
+* DESX
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_DESX_H_
+#define BOTAN_DESX_H_
+
+#include <botan/des.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(desx.h)
+
+namespace Botan {
+
+/**
+* DESX
+*/
+class BOTAN_PUBLIC_API(2,0) DESX final : public Block_Cipher_Fixed_Params<8, 24>
+ {
+ public:
+ void encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+ void decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+
+ void clear() override;
+ std::string name() const override { return "DESX"; }
+ BlockCipher* clone() const override { return new DESX; }
+ private:
+ void key_schedule(const uint8_t[], size_t) override;
+ secure_vector<uint8_t> m_K1, m_K2;
+ DES m_des;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/block/des/info.txt b/comm/third_party/botan/src/lib/block/des/info.txt
new file mode 100644
index 0000000000..05f85b523c
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/des/info.txt
@@ -0,0 +1,3 @@
+<defines>
+DES -> 20131128
+</defines>
diff --git a/comm/third_party/botan/src/lib/block/gost_28147/gost_28147.cpp b/comm/third_party/botan/src/lib/block/gost_28147/gost_28147.cpp
new file mode 100644
index 0000000000..2b8aa031e8
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/gost_28147/gost_28147.cpp
@@ -0,0 +1,189 @@
+/*
+* GOST 28147-89
+* (C) 1999-2009,2011 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/gost_28147.h>
+#include <botan/exceptn.h>
+#include <botan/loadstor.h>
+#include <botan/rotate.h>
+
+namespace Botan {
+
+uint8_t GOST_28147_89_Params::sbox_entry(size_t row, size_t col) const
+ {
+ const uint8_t x = m_sboxes[4 * col + (row / 2)];
+ return (row % 2 == 0) ? (x >> 4) : (x & 0x0F);
+ }
+
+uint8_t GOST_28147_89_Params::sbox_pair(size_t row, size_t col) const
+ {
+ const uint8_t x = m_sboxes[4 * (col % 16) + row];
+ const uint8_t y = m_sboxes[4 * (col / 16) + row];
+ return (x >> 4) | (y << 4);
+ }
+
+GOST_28147_89_Params::GOST_28147_89_Params(const std::string& n) : m_name(n)
+ {
+ // Encoded in the packed fromat from RFC 4357
+
+ // GostR3411_94_TestParamSet (OID 1.2.643.2.2.31.0)
+ static const uint8_t GOST_R_3411_TEST_PARAMS[64] = {
+ 0x4E, 0x57, 0x64, 0xD1, 0xAB, 0x8D, 0xCB, 0xBF, 0x94, 0x1A, 0x7A,
+ 0x4D, 0x2C, 0xD1, 0x10, 0x10, 0xD6, 0xA0, 0x57, 0x35, 0x8D, 0x38,
+ 0xF2, 0xF7, 0x0F, 0x49, 0xD1, 0x5A, 0xEA, 0x2F, 0x8D, 0x94, 0x62,
+ 0xEE, 0x43, 0x09, 0xB3, 0xF4, 0xA6, 0xA2, 0x18, 0xC6, 0x98, 0xE3,
+ 0xC1, 0x7C, 0xE5, 0x7E, 0x70, 0x6B, 0x09, 0x66, 0xF7, 0x02, 0x3C,
+ 0x8B, 0x55, 0x95, 0xBF, 0x28, 0x39, 0xB3, 0x2E, 0xCC };
+
+ // GostR3411-94-CryptoProParamSet (OID 1.2.643.2.2.31.1)
+ static const uint8_t GOST_R_3411_CRYPTOPRO_PARAMS[64] = {
+ 0xA5, 0x74, 0x77, 0xD1, 0x4F, 0xFA, 0x66, 0xE3, 0x54, 0xC7, 0x42,
+ 0x4A, 0x60, 0xEC, 0xB4, 0x19, 0x82, 0x90, 0x9D, 0x75, 0x1D, 0x4F,
+ 0xC9, 0x0B, 0x3B, 0x12, 0x2F, 0x54, 0x79, 0x08, 0xA0, 0xAF, 0xD1,
+ 0x3E, 0x1A, 0x38, 0xC7, 0xB1, 0x81, 0xC6, 0xE6, 0x56, 0x05, 0x87,
+ 0x03, 0x25, 0xEB, 0xFE, 0x9C, 0x6D, 0xF8, 0x6D, 0x2E, 0xAB, 0xDE,
+ 0x20, 0xBA, 0x89, 0x3C, 0x92, 0xF8, 0xD3, 0x53, 0xBC };
+
+ if(m_name == "R3411_94_TestParam")
+ m_sboxes = GOST_R_3411_TEST_PARAMS;
+ else if(m_name == "R3411_CryptoPro")
+ m_sboxes = GOST_R_3411_CRYPTOPRO_PARAMS;
+ else
+ throw Invalid_Argument("GOST_28147_89_Params: Unknown " + m_name);
+ }
+
+/*
+* GOST Constructor
+*/
+GOST_28147_89::GOST_28147_89(const GOST_28147_89_Params& param) : m_SBOX(1024)
+ {
+ // Convert the parallel 4x4 sboxes into larger word-based sboxes
+
+ for(size_t i = 0; i != 256; ++i)
+ {
+ m_SBOX[i ] = rotl<11, uint32_t>(param.sbox_pair(0, i));
+ m_SBOX[i+256] = rotl<19, uint32_t>(param.sbox_pair(1, i));
+ m_SBOX[i+512] = rotl<27, uint32_t>(param.sbox_pair(2, i));
+ m_SBOX[i+768] = rotl< 3, uint32_t>(param.sbox_pair(3, i));
+ }
+ }
+
+std::string GOST_28147_89::name() const
+ {
+ /*
+ 'Guess' the right name for the sbox on the basis of the values.
+ This would need to be updated if support for other sbox parameters
+ is added. Preferably, we would just store the string value in the
+ constructor, but can't break binary compat.
+ */
+ std::string sbox_name = "";
+ if(m_SBOX[0] == 0x00072000)
+ sbox_name = "R3411_94_TestParam";
+ else if(m_SBOX[0] == 0x0002D000)
+ sbox_name = "R3411_CryptoPro";
+ else
+ throw Internal_Error("GOST-28147 unrecognized sbox value");
+
+ return "GOST-28147-89(" + sbox_name + ")";
+ }
+
+/*
+* Two rounds of GOST
+*/
+#define GOST_2ROUND(N1, N2, R1, R2) \
+ do { \
+ uint32_t T0 = N1 + m_EK[R1]; \
+ N2 ^= m_SBOX[get_byte(3, T0)] | \
+ m_SBOX[get_byte(2, T0)+256] | \
+ m_SBOX[get_byte(1, T0)+512] | \
+ m_SBOX[get_byte(0, T0)+768]; \
+ \
+ uint32_t T1 = N2 + m_EK[R2]; \
+ N1 ^= m_SBOX[get_byte(3, T1)] | \
+ m_SBOX[get_byte(2, T1)+256] | \
+ m_SBOX[get_byte(1, T1)+512] | \
+ m_SBOX[get_byte(0, T1)+768]; \
+ } while(0)
+
+/*
+* GOST Encryption
+*/
+void GOST_28147_89::encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+ {
+ verify_key_set(m_EK.empty() == false);
+
+ for(size_t i = 0; i != blocks; ++i)
+ {
+ uint32_t N1 = load_le<uint32_t>(in, 0);
+ uint32_t N2 = load_le<uint32_t>(in, 1);
+
+ for(size_t j = 0; j != 3; ++j)
+ {
+ GOST_2ROUND(N1, N2, 0, 1);
+ GOST_2ROUND(N1, N2, 2, 3);
+ GOST_2ROUND(N1, N2, 4, 5);
+ GOST_2ROUND(N1, N2, 6, 7);
+ }
+
+ GOST_2ROUND(N1, N2, 7, 6);
+ GOST_2ROUND(N1, N2, 5, 4);
+ GOST_2ROUND(N1, N2, 3, 2);
+ GOST_2ROUND(N1, N2, 1, 0);
+
+ store_le(out, N2, N1);
+
+ in += BLOCK_SIZE;
+ out += BLOCK_SIZE;
+ }
+ }
+
+/*
+* GOST Decryption
+*/
+void GOST_28147_89::decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+ {
+ verify_key_set(m_EK.empty() == false);
+
+ for(size_t i = 0; i != blocks; ++i)
+ {
+ uint32_t N1 = load_le<uint32_t>(in, 0);
+ uint32_t N2 = load_le<uint32_t>(in, 1);
+
+ GOST_2ROUND(N1, N2, 0, 1);
+ GOST_2ROUND(N1, N2, 2, 3);
+ GOST_2ROUND(N1, N2, 4, 5);
+ GOST_2ROUND(N1, N2, 6, 7);
+
+ for(size_t j = 0; j != 3; ++j)
+ {
+ GOST_2ROUND(N1, N2, 7, 6);
+ GOST_2ROUND(N1, N2, 5, 4);
+ GOST_2ROUND(N1, N2, 3, 2);
+ GOST_2ROUND(N1, N2, 1, 0);
+ }
+
+ store_le(out, N2, N1);
+ in += BLOCK_SIZE;
+ out += BLOCK_SIZE;
+ }
+ }
+
+/*
+* GOST Key Schedule
+*/
+void GOST_28147_89::key_schedule(const uint8_t key[], size_t)
+ {
+ m_EK.resize(8);
+ for(size_t i = 0; i != 8; ++i)
+ m_EK[i] = load_le<uint32_t>(key, i);
+ }
+
+void GOST_28147_89::clear()
+ {
+ zap(m_EK);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/block/gost_28147/gost_28147.h b/comm/third_party/botan/src/lib/block/gost_28147/gost_28147.h
new file mode 100644
index 0000000000..f71bb28bd9
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/gost_28147/gost_28147.h
@@ -0,0 +1,95 @@
+/*
+* GOST 28147-89
+* (C) 1999-2009 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_GOST_28147_89_H_
+#define BOTAN_GOST_28147_89_H_
+
+#include <botan/block_cipher.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(gost_28147.h)
+
+namespace Botan {
+
+/**
+* The GOST 28147-89 block cipher uses a set of 4 bit Sboxes, however
+* the standard does not actually define these Sboxes; they are
+* considered a local configuration issue. Several different sets are
+* used.
+*/
+class BOTAN_PUBLIC_API(2,0) GOST_28147_89_Params final
+ {
+ public:
+ /**
+ * @param row the row
+ * @param col the column
+ * @return sbox entry at this row/column
+ */
+ uint8_t sbox_entry(size_t row, size_t col) const;
+
+ /**
+ * @return name of this parameter set
+ */
+ std::string param_name() const { return m_name; }
+
+ /**
+ * Return a representation used for building larger tables
+ * For internal use
+ */
+ uint8_t sbox_pair(size_t row, size_t col) const;
+
+ /**
+ * Default GOST parameters are the ones given in GOST R 34.11 for
+ * testing purposes; these sboxes are also used by Crypto++, and,
+ * at least according to Wikipedia, the Central Bank of Russian
+ * Federation
+ * @param name of the parameter set
+ */
+ explicit GOST_28147_89_Params(const std::string& name = "R3411_94_TestParam");
+ private:
+ const uint8_t* m_sboxes;
+ std::string m_name;
+ };
+
+/**
+* GOST 28147-89
+*/
+class BOTAN_PUBLIC_API(2,0) GOST_28147_89 final : public Block_Cipher_Fixed_Params<8, 32>
+ {
+ public:
+ void encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+ void decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+
+ void clear() override;
+
+ std::string name() const override;
+ BlockCipher* clone() const override { return new GOST_28147_89(m_SBOX); }
+
+ /**
+ * @param params the sbox parameters to use
+ */
+ explicit GOST_28147_89(const GOST_28147_89_Params& params);
+
+ explicit GOST_28147_89(const std::string& param_name) :
+ GOST_28147_89(GOST_28147_89_Params(param_name)) {}
+ private:
+ explicit GOST_28147_89(const std::vector<uint32_t>& other_SBOX) :
+ m_SBOX(other_SBOX), m_EK(8) {}
+
+ void key_schedule(const uint8_t[], size_t) override;
+
+ /*
+ * The sbox is not secret, this is just a larger expansion of it
+ * which we generate at runtime for faster execution
+ */
+ std::vector<uint32_t> m_SBOX;
+
+ secure_vector<uint32_t> m_EK;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/block/gost_28147/info.txt b/comm/third_party/botan/src/lib/block/gost_28147/info.txt
new file mode 100644
index 0000000000..17fc971591
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/gost_28147/info.txt
@@ -0,0 +1,3 @@
+<defines>
+GOST_28147_89 -> 20131128
+</defines>
diff --git a/comm/third_party/botan/src/lib/block/idea/idea.cpp b/comm/third_party/botan/src/lib/block/idea/idea.cpp
new file mode 100644
index 0000000000..f8f5ceb348
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/idea/idea.cpp
@@ -0,0 +1,240 @@
+/*
+* IDEA
+* (C) 1999-2010,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/idea.h>
+#include <botan/loadstor.h>
+#include <botan/cpuid.h>
+#include <botan/internal/ct_utils.h>
+
+namespace Botan {
+
+namespace {
+
+/*
+* Multiplication modulo 65537
+*/
+inline uint16_t mul(uint16_t x, uint16_t y)
+ {
+ const uint32_t P = static_cast<uint32_t>(x) * y;
+ const auto P_mask = CT::Mask<uint16_t>(CT::Mask<uint32_t>::is_zero(P));
+
+ const uint32_t P_hi = P >> 16;
+ const uint32_t P_lo = P & 0xFFFF;
+
+ const uint16_t carry = (P_lo < P_hi);
+ const uint16_t r_1 = static_cast<uint16_t>((P_lo - P_hi) + carry);
+ const uint16_t r_2 = 1 - x - y;
+
+ return P_mask.select(r_2, r_1);
+ }
+
+/*
+* Find multiplicative inverses modulo 65537
+*
+* 65537 is prime; thus Fermat's little theorem tells us that
+* x^65537 == x modulo 65537, which means
+* x^(65537-2) == x^-1 modulo 65537 since
+* x^(65537-2) * x == 1 mod 65537
+*
+* Do the exponentiation with a basic square and multiply: all bits are
+* of exponent are 1 so we always multiply
+*/
+uint16_t mul_inv(uint16_t x)
+ {
+ uint16_t y = x;
+
+ for(size_t i = 0; i != 15; ++i)
+ {
+ y = mul(y, y); // square
+ y = mul(y, x);
+ }
+
+ return y;
+ }
+
+/**
+* IDEA is involutional, depending only on the key schedule
+*/
+void idea_op(const uint8_t in[], uint8_t out[], size_t blocks, const uint16_t K[52])
+ {
+ const size_t BLOCK_SIZE = 8;
+
+ CT::poison(in, blocks * 8);
+ CT::poison(out, blocks * 8);
+ CT::poison(K, 52);
+
+ BOTAN_PARALLEL_FOR(size_t i = 0; i < blocks; ++i)
+ {
+ uint16_t X1, X2, X3, X4;
+ load_be(in + BLOCK_SIZE*i, X1, X2, X3, X4);
+
+ for(size_t j = 0; j != 8; ++j)
+ {
+ X1 = mul(X1, K[6*j+0]);
+ X2 += K[6*j+1];
+ X3 += K[6*j+2];
+ X4 = mul(X4, K[6*j+3]);
+
+ const uint16_t T0 = X3;
+ X3 = mul(X3 ^ X1, K[6*j+4]);
+
+ const uint16_t T1 = X2;
+ X2 = mul((X2 ^ X4) + X3, K[6*j+5]);
+ X3 += X2;
+
+ X1 ^= X2;
+ X4 ^= X3;
+ X2 ^= T0;
+ X3 ^= T1;
+ }
+
+ X1 = mul(X1, K[48]);
+ X2 += K[50];
+ X3 += K[49];
+ X4 = mul(X4, K[51]);
+
+ store_be(out + BLOCK_SIZE*i, X1, X3, X2, X4);
+ }
+
+ CT::unpoison(in, blocks * 8);
+ CT::unpoison(out, blocks * 8);
+ CT::unpoison(K, 52);
+ }
+
+}
+
+size_t IDEA::parallelism() const
+ {
+#if defined(BOTAN_HAS_IDEA_SSE2)
+ if(CPUID::has_sse2())
+ {
+ return 8;
+ }
+#endif
+
+ return 1;
+ }
+
+std::string IDEA::provider() const
+ {
+#if defined(BOTAN_HAS_IDEA_SSE2)
+ if(CPUID::has_sse2())
+ {
+ return "sse2";
+ }
+#endif
+
+ return "base";
+ }
+
+/*
+* IDEA Encryption
+*/
+void IDEA::encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+ {
+ verify_key_set(m_EK.empty() == false);
+
+#if defined(BOTAN_HAS_IDEA_SSE2)
+ if(CPUID::has_sse2())
+ {
+ while(blocks >= 8)
+ {
+ sse2_idea_op_8(in, out, m_EK.data());
+ in += 8 * BLOCK_SIZE;
+ out += 8 * BLOCK_SIZE;
+ blocks -= 8;
+ }
+ }
+#endif
+
+ idea_op(in, out, blocks, m_EK.data());
+ }
+
+/*
+* IDEA Decryption
+*/
+void IDEA::decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+ {
+ verify_key_set(m_DK.empty() == false);
+
+#if defined(BOTAN_HAS_IDEA_SSE2)
+ if(CPUID::has_sse2())
+ {
+ while(blocks >= 8)
+ {
+ sse2_idea_op_8(in, out, m_DK.data());
+ in += 8 * BLOCK_SIZE;
+ out += 8 * BLOCK_SIZE;
+ blocks -= 8;
+ }
+ }
+#endif
+
+ idea_op(in, out, blocks, m_DK.data());
+ }
+
+/*
+* IDEA Key Schedule
+*/
+void IDEA::key_schedule(const uint8_t key[], size_t)
+ {
+ m_EK.resize(52);
+ m_DK.resize(52);
+
+ CT::poison(key, 16);
+ CT::poison(m_EK.data(), 52);
+ CT::poison(m_DK.data(), 52);
+
+ secure_vector<uint64_t> K(2);
+
+ K[0] = load_be<uint64_t>(key, 0);
+ K[1] = load_be<uint64_t>(key, 1);
+
+ for(size_t off = 0; off != 48; off += 8)
+ {
+ for(size_t i = 0; i != 8; ++i)
+ m_EK[off+i] = static_cast<uint16_t>(K[i/4] >> (48-16*(i % 4)));
+
+ const uint64_t Kx = (K[0] >> 39);
+ const uint64_t Ky = (K[1] >> 39);
+
+ K[0] = (K[0] << 25) | Ky;
+ K[1] = (K[1] << 25) | Kx;
+ }
+
+ for(size_t i = 0; i != 4; ++i)
+ m_EK[48+i] = static_cast<uint16_t>(K[i/4] >> (48-16*(i % 4)));
+
+ m_DK[0] = mul_inv(m_EK[48]);
+ m_DK[1] = -m_EK[49];
+ m_DK[2] = -m_EK[50];
+ m_DK[3] = mul_inv(m_EK[51]);
+
+ for(size_t i = 0; i != 8*6; i += 6)
+ {
+ m_DK[i+4] = m_EK[46-i];
+ m_DK[i+5] = m_EK[47-i];
+ m_DK[i+6] = mul_inv(m_EK[42-i]);
+ m_DK[i+7] = -m_EK[44-i];
+ m_DK[i+8] = -m_EK[43-i];
+ m_DK[i+9] = mul_inv(m_EK[45-i]);
+ }
+
+ std::swap(m_DK[49], m_DK[50]);
+
+ CT::unpoison(key, 16);
+ CT::unpoison(m_EK.data(), 52);
+ CT::unpoison(m_DK.data(), 52);
+ }
+
+void IDEA::clear()
+ {
+ zap(m_EK);
+ zap(m_DK);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/block/idea/idea.h b/comm/third_party/botan/src/lib/block/idea/idea.h
new file mode 100644
index 0000000000..e5e51606b9
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/idea/idea.h
@@ -0,0 +1,45 @@
+/*
+* IDEA
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_IDEA_H_
+#define BOTAN_IDEA_H_
+
+#include <botan/block_cipher.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(idea.h)
+
+namespace Botan {
+
+/**
+* IDEA
+*/
+class BOTAN_PUBLIC_API(2,0) IDEA final : public Block_Cipher_Fixed_Params<8, 16>
+ {
+ public:
+ void encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+ 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 "IDEA"; }
+ BlockCipher* clone() const override { return new IDEA; }
+ size_t parallelism() const override;
+
+ private:
+#if defined(BOTAN_HAS_IDEA_SSE2)
+ void sse2_idea_op_8(const uint8_t in[64], uint8_t out[64], const uint16_t EK[52]) const;
+#endif
+
+ void key_schedule(const uint8_t[], size_t) override;
+
+ secure_vector<uint16_t> m_EK, m_DK;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/block/idea/idea_sse2/idea_sse2.cpp b/comm/third_party/botan/src/lib/block/idea/idea_sse2/idea_sse2.cpp
new file mode 100644
index 0000000000..93648cfc7a
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/idea/idea_sse2/idea_sse2.cpp
@@ -0,0 +1,208 @@
+/*
+* IDEA in SSE2
+* (C) 2009 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/idea.h>
+#include <botan/internal/ct_utils.h>
+#include <emmintrin.h>
+
+namespace Botan {
+
+namespace {
+
+BOTAN_FUNC_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);
+
+ 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.
+ */
+
+ T = _mm_or_si128(
+ _mm_andnot_si128(X_is_zero, T),
+ _mm_and_si128(_mm_sub_epi16(ones, K), X_is_zero));
+
+ T = _mm_or_si128(
+ _mm_andnot_si128(K_is_zero, T),
+ _mm_and_si128(_mm_sub_epi16(ones, X), K_is_zero));
+
+ return T;
+ }
+
+/*
+* 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));
+
+ B0 = _mm_unpacklo_epi64(T0, T2);
+ B1 = _mm_unpackhi_epi64(T0, T2);
+ B2 = _mm_unpacklo_epi64(T1, T3);
+ B3 = _mm_unpackhi_epi64(T1, T3);
+ }
+
+/*
+* 4x8 matrix transpose (reverse)
+*/
+BOTAN_FUNC_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);
+ __m128i T3 = _mm_unpackhi_epi64(B2, B3);
+
+ 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));
+
+ T0 = _mm_shufflehi_epi16(T0, _MM_SHUFFLE(3, 1, 2, 0));
+ T1 = _mm_shufflehi_epi16(T1, _MM_SHUFFLE(3, 1, 2, 0));
+ T2 = _mm_shufflehi_epi16(T2, _MM_SHUFFLE(3, 1, 2, 0));
+ T3 = _mm_shufflehi_epi16(T3, _MM_SHUFFLE(3, 1, 2, 0));
+
+ T0 = _mm_shufflelo_epi16(T0, _MM_SHUFFLE(3, 1, 2, 0));
+ T1 = _mm_shufflelo_epi16(T1, _MM_SHUFFLE(3, 1, 2, 0));
+ T2 = _mm_shufflelo_epi16(T2, _MM_SHUFFLE(3, 1, 2, 0));
+ T3 = _mm_shufflelo_epi16(T3, _MM_SHUFFLE(3, 1, 2, 0));
+
+ B0 = _mm_unpacklo_epi32(T0, T1);
+ B1 = _mm_unpackhi_epi32(T0, T1);
+ B2 = _mm_unpacklo_epi32(T2, T3);
+ B3 = _mm_unpackhi_epi32(T2, T3);
+ }
+
+}
+
+/*
+* 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]) const
+ {
+ CT::poison(in, 64);
+ CT::poison(out, 64);
+ CT::poison(EK, 52);
+
+ const __m128i* in_mm = reinterpret_cast<const __m128i*>(in);
+
+ __m128i B0 = _mm_loadu_si128(in_mm + 0);
+ __m128i B1 = _mm_loadu_si128(in_mm + 1);
+ __m128i B2 = _mm_loadu_si128(in_mm + 2);
+ __m128i B3 = _mm_loadu_si128(in_mm + 3);
+
+ transpose_in(B0, B1, B2, B3);
+
+ // byte swap
+ B0 = _mm_or_si128(_mm_slli_epi16(B0, 8), _mm_srli_epi16(B0, 8));
+ B1 = _mm_or_si128(_mm_slli_epi16(B1, 8), _mm_srli_epi16(B1, 8));
+ B2 = _mm_or_si128(_mm_slli_epi16(B2, 8), _mm_srli_epi16(B2, 8));
+ B3 = _mm_or_si128(_mm_slli_epi16(B3, 8), _mm_srli_epi16(B3, 8));
+
+ for(size_t i = 0; i != 8; ++i)
+ {
+ B0 = mul(B0, EK[6*i+0]);
+ B1 = _mm_add_epi16(B1, _mm_set1_epi16(EK[6*i+1]));
+ B2 = _mm_add_epi16(B2, _mm_set1_epi16(EK[6*i+2]));
+ B3 = mul(B3, EK[6*i+3]);
+
+ __m128i T0 = B2;
+ B2 = _mm_xor_si128(B2, B0);
+ B2 = mul(B2, EK[6*i+4]);
+
+ __m128i T1 = B1;
+
+ B1 = _mm_xor_si128(B1, B3);
+ B1 = _mm_add_epi16(B1, B2);
+ B1 = mul(B1, EK[6*i+5]);
+
+ B2 = _mm_add_epi16(B2, B1);
+
+ B0 = _mm_xor_si128(B0, B1);
+ B1 = _mm_xor_si128(B1, T0);
+ B3 = _mm_xor_si128(B3, B2);
+ B2 = _mm_xor_si128(B2, T1);
+ }
+
+ B0 = mul(B0, EK[48]);
+ B1 = _mm_add_epi16(B1, _mm_set1_epi16(EK[50]));
+ B2 = _mm_add_epi16(B2, _mm_set1_epi16(EK[49]));
+ B3 = mul(B3, EK[51]);
+
+ // byte swap
+ B0 = _mm_or_si128(_mm_slli_epi16(B0, 8), _mm_srli_epi16(B0, 8));
+ B1 = _mm_or_si128(_mm_slli_epi16(B1, 8), _mm_srli_epi16(B1, 8));
+ B2 = _mm_or_si128(_mm_slli_epi16(B2, 8), _mm_srli_epi16(B2, 8));
+ B3 = _mm_or_si128(_mm_slli_epi16(B3, 8), _mm_srli_epi16(B3, 8));
+
+ transpose_out(B0, B2, B1, B3);
+
+ __m128i* out_mm = reinterpret_cast<__m128i*>(out);
+
+ _mm_storeu_si128(out_mm + 0, B0);
+ _mm_storeu_si128(out_mm + 1, B2);
+ _mm_storeu_si128(out_mm + 2, B1);
+ _mm_storeu_si128(out_mm + 3, B3);
+
+ CT::unpoison(in, 64);
+ CT::unpoison(out, 64);
+ CT::unpoison(EK, 52);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/block/idea/idea_sse2/info.txt b/comm/third_party/botan/src/lib/block/idea/idea_sse2/info.txt
new file mode 100644
index 0000000000..b0ca2d02fa
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/idea/idea_sse2/info.txt
@@ -0,0 +1,7 @@
+<defines>
+IDEA_SSE2 -> 20131128
+</defines>
+
+<isa>
+sse2
+</isa>
diff --git a/comm/third_party/botan/src/lib/block/idea/info.txt b/comm/third_party/botan/src/lib/block/idea/info.txt
new file mode 100644
index 0000000000..bcbdce03f1
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/idea/info.txt
@@ -0,0 +1,3 @@
+<defines>
+IDEA -> 20131128
+</defines>
diff --git a/comm/third_party/botan/src/lib/block/info.txt b/comm/third_party/botan/src/lib/block/info.txt
new file mode 100644
index 0000000000..b03a8c8f59
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/info.txt
@@ -0,0 +1,7 @@
+<defines>
+BLOCK_CIPHER -> 20131128
+</defines>
+
+<header:public>
+block_cipher.h
+</header:public>
diff --git a/comm/third_party/botan/src/lib/block/kasumi/info.txt b/comm/third_party/botan/src/lib/block/kasumi/info.txt
new file mode 100644
index 0000000000..b48183ca90
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/kasumi/info.txt
@@ -0,0 +1,3 @@
+<defines>
+KASUMI -> 20131128
+</defines>
diff --git a/comm/third_party/botan/src/lib/block/kasumi/kasumi.cpp b/comm/third_party/botan/src/lib/block/kasumi/kasumi.cpp
new file mode 100644
index 0000000000..819567b1c1
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/kasumi/kasumi.cpp
@@ -0,0 +1,238 @@
+/*
+* KASUMI
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/kasumi.h>
+#include <botan/loadstor.h>
+#include <botan/rotate.h>
+
+namespace Botan {
+
+namespace {
+
+/*
+* KASUMI S-Boxes
+*/
+alignas(64) const uint8_t KASUMI_SBOX_S7[128] = {
+ 0x36, 0x32, 0x3E, 0x38, 0x16, 0x22, 0x5E, 0x60, 0x26, 0x06, 0x3F, 0x5D,
+ 0x02, 0x12, 0x7B, 0x21, 0x37, 0x71, 0x27, 0x72, 0x15, 0x43, 0x41, 0x0C,
+ 0x2F, 0x49, 0x2E, 0x1B, 0x19, 0x6F, 0x7C, 0x51, 0x35, 0x09, 0x79, 0x4F,
+ 0x34, 0x3C, 0x3A, 0x30, 0x65, 0x7F, 0x28, 0x78, 0x68, 0x46, 0x47, 0x2B,
+ 0x14, 0x7A, 0x48, 0x3D, 0x17, 0x6D, 0x0D, 0x64, 0x4D, 0x01, 0x10, 0x07,
+ 0x52, 0x0A, 0x69, 0x62, 0x75, 0x74, 0x4C, 0x0B, 0x59, 0x6A, 0x00, 0x7D,
+ 0x76, 0x63, 0x56, 0x45, 0x1E, 0x39, 0x7E, 0x57, 0x70, 0x33, 0x11, 0x05,
+ 0x5F, 0x0E, 0x5A, 0x54, 0x5B, 0x08, 0x23, 0x67, 0x20, 0x61, 0x1C, 0x42,
+ 0x66, 0x1F, 0x1A, 0x2D, 0x4B, 0x04, 0x55, 0x5C, 0x25, 0x4A, 0x50, 0x31,
+ 0x44, 0x1D, 0x73, 0x2C, 0x40, 0x6B, 0x6C, 0x18, 0x6E, 0x53, 0x24, 0x4E,
+ 0x2A, 0x13, 0x0F, 0x29, 0x58, 0x77, 0x3B, 0x03 };
+
+alignas(64) const uint16_t KASUMI_SBOX_S9[512] = {
+ 0x00A7, 0x00EF, 0x00A1, 0x017B, 0x0187, 0x014E, 0x0009, 0x0152, 0x0026,
+ 0x00E2, 0x0030, 0x0166, 0x01C4, 0x0181, 0x005A, 0x018D, 0x00B7, 0x00FD,
+ 0x0093, 0x014B, 0x019F, 0x0154, 0x0033, 0x016A, 0x0132, 0x01F4, 0x0106,
+ 0x0052, 0x00D8, 0x009F, 0x0164, 0x00B1, 0x00AF, 0x00F1, 0x01E9, 0x0025,
+ 0x00CE, 0x0011, 0x0000, 0x014D, 0x002C, 0x00FE, 0x017A, 0x003A, 0x008F,
+ 0x00DC, 0x0051, 0x0190, 0x005F, 0x0003, 0x013B, 0x00F5, 0x0036, 0x00EB,
+ 0x00DA, 0x0195, 0x01D8, 0x0108, 0x00AC, 0x01EE, 0x0173, 0x0122, 0x018F,
+ 0x004C, 0x00A5, 0x00C5, 0x018B, 0x0079, 0x0101, 0x01E0, 0x01A7, 0x00D4,
+ 0x00F0, 0x001C, 0x01CE, 0x00B0, 0x0196, 0x01FB, 0x0120, 0x00DF, 0x01F5,
+ 0x0197, 0x00F9, 0x0109, 0x0059, 0x00BA, 0x00DD, 0x01AC, 0x00A4, 0x004A,
+ 0x01B8, 0x00C4, 0x01CA, 0x01A5, 0x015E, 0x00A3, 0x00E8, 0x009E, 0x0086,
+ 0x0162, 0x000D, 0x00FA, 0x01EB, 0x008E, 0x00BF, 0x0045, 0x00C1, 0x01A9,
+ 0x0098, 0x00E3, 0x016E, 0x0087, 0x0158, 0x012C, 0x0114, 0x00F2, 0x01B5,
+ 0x0140, 0x0071, 0x0116, 0x000B, 0x00F3, 0x0057, 0x013D, 0x0024, 0x005D,
+ 0x01F0, 0x001B, 0x01E7, 0x01BE, 0x01E2, 0x0029, 0x0044, 0x009C, 0x01C9,
+ 0x0083, 0x0146, 0x0193, 0x0153, 0x0014, 0x0027, 0x0073, 0x01BA, 0x007C,
+ 0x01DB, 0x0180, 0x01FC, 0x0035, 0x0070, 0x00AA, 0x01DF, 0x0097, 0x007E,
+ 0x00A9, 0x0049, 0x010C, 0x0117, 0x0141, 0x00A8, 0x016C, 0x016B, 0x0124,
+ 0x002E, 0x01F3, 0x0189, 0x0147, 0x0144, 0x0018, 0x01C8, 0x010B, 0x009D,
+ 0x01CC, 0x01E8, 0x01AA, 0x0135, 0x00E5, 0x01B7, 0x01FA, 0x00D0, 0x010F,
+ 0x015D, 0x0191, 0x01B2, 0x00EC, 0x0010, 0x00D1, 0x0167, 0x0034, 0x0038,
+ 0x0078, 0x00C7, 0x0115, 0x01D1, 0x01A0, 0x00FC, 0x011F, 0x00F6, 0x0006,
+ 0x0053, 0x0131, 0x01A4, 0x0159, 0x0099, 0x01F6, 0x0041, 0x003D, 0x00F4,
+ 0x011A, 0x00AD, 0x00DE, 0x01A2, 0x0043, 0x0182, 0x0170, 0x0105, 0x0065,
+ 0x01DC, 0x0123, 0x00C3, 0x01AE, 0x0031, 0x004F, 0x00A6, 0x014A, 0x0118,
+ 0x017F, 0x0175, 0x0080, 0x017E, 0x0198, 0x009B, 0x01EF, 0x016F, 0x0184,
+ 0x0112, 0x006B, 0x01CB, 0x01A1, 0x003E, 0x01C6, 0x0084, 0x00E1, 0x00CB,
+ 0x013C, 0x00EA, 0x000E, 0x012D, 0x005B, 0x01F7, 0x011E, 0x01A8, 0x00D3,
+ 0x015B, 0x0133, 0x008C, 0x0176, 0x0023, 0x0067, 0x007D, 0x01AB, 0x0013,
+ 0x00D6, 0x01C5, 0x0092, 0x01F2, 0x013A, 0x01BC, 0x00E6, 0x0100, 0x0149,
+ 0x00C6, 0x011D, 0x0032, 0x0074, 0x004E, 0x019A, 0x000A, 0x00CD, 0x01FE,
+ 0x00AB, 0x00E7, 0x002D, 0x008B, 0x01D3, 0x001D, 0x0056, 0x01F9, 0x0020,
+ 0x0048, 0x001A, 0x0156, 0x0096, 0x0139, 0x01EA, 0x01AF, 0x00EE, 0x019B,
+ 0x0145, 0x0095, 0x01D9, 0x0028, 0x0077, 0x00AE, 0x0163, 0x00B9, 0x00E9,
+ 0x0185, 0x0047, 0x01C0, 0x0111, 0x0174, 0x0037, 0x006E, 0x00B2, 0x0142,
+ 0x000C, 0x01D5, 0x0188, 0x0171, 0x00BE, 0x0001, 0x006D, 0x0177, 0x0089,
+ 0x00B5, 0x0058, 0x004B, 0x0134, 0x0104, 0x01E4, 0x0062, 0x0110, 0x0172,
+ 0x0113, 0x019C, 0x006F, 0x0150, 0x013E, 0x0004, 0x01F8, 0x01EC, 0x0103,
+ 0x0130, 0x004D, 0x0151, 0x01B3, 0x0015, 0x0165, 0x012F, 0x014C, 0x01E3,
+ 0x0012, 0x002F, 0x0055, 0x0019, 0x01F1, 0x01DA, 0x0121, 0x0064, 0x010D,
+ 0x0128, 0x01DE, 0x010E, 0x006A, 0x001F, 0x0068, 0x01B1, 0x0054, 0x019E,
+ 0x01E6, 0x018A, 0x0060, 0x0063, 0x009A, 0x01FF, 0x0094, 0x019D, 0x0169,
+ 0x0199, 0x00FF, 0x00A2, 0x00D7, 0x012E, 0x00C9, 0x010A, 0x015F, 0x0157,
+ 0x0090, 0x01B9, 0x016D, 0x006C, 0x012A, 0x00FB, 0x0022, 0x00B6, 0x01FD,
+ 0x008A, 0x00D2, 0x014F, 0x0085, 0x0137, 0x0160, 0x0148, 0x008D, 0x018C,
+ 0x015A, 0x007B, 0x013F, 0x01C2, 0x0119, 0x01AD, 0x00E4, 0x01BB, 0x01E1,
+ 0x005C, 0x0194, 0x01E5, 0x01A6, 0x00F8, 0x0129, 0x0017, 0x00D5, 0x0082,
+ 0x01D2, 0x0016, 0x00D9, 0x011B, 0x0046, 0x0126, 0x0168, 0x01A3, 0x007F,
+ 0x0138, 0x0179, 0x0007, 0x01D4, 0x00C2, 0x0002, 0x0075, 0x0127, 0x01CF,
+ 0x0102, 0x00E0, 0x01BF, 0x00F7, 0x00BB, 0x0050, 0x018E, 0x011C, 0x0161,
+ 0x0069, 0x0186, 0x012B, 0x01D7, 0x01D6, 0x00B8, 0x0039, 0x00C8, 0x015C,
+ 0x003F, 0x00CC, 0x00BC, 0x0021, 0x01C3, 0x0061, 0x001E, 0x0136, 0x00DB,
+ 0x005E, 0x00A0, 0x0081, 0x01ED, 0x0040, 0x00B3, 0x0107, 0x0066, 0x00BD,
+ 0x00CF, 0x0072, 0x0192, 0x01B6, 0x01DD, 0x0183, 0x007A, 0x00C0, 0x002A,
+ 0x017D, 0x0005, 0x0091, 0x0076, 0x00B4, 0x01C1, 0x0125, 0x0143, 0x0088,
+ 0x017C, 0x002B, 0x0042, 0x003C, 0x01C7, 0x0155, 0x01BD, 0x00CA, 0x01B0,
+ 0x0008, 0x00ED, 0x000F, 0x0178, 0x01B4, 0x01D0, 0x003B, 0x01CD };
+
+/*
+* KASUMI FI Function
+*/
+uint16_t FI(uint16_t I, uint16_t K)
+ {
+ uint16_t D9 = (I >> 7);
+ uint8_t D7 = (I & 0x7F);
+ D9 = KASUMI_SBOX_S9[D9] ^ D7;
+ D7 = KASUMI_SBOX_S7[D7] ^ (D9 & 0x7F);
+
+ D7 ^= (K >> 9);
+ D9 = KASUMI_SBOX_S9[D9 ^ (K & 0x1FF)] ^ D7;
+ D7 = KASUMI_SBOX_S7[D7] ^ (D9 & 0x7F);
+ return static_cast<uint16_t>(D7 << 9) | D9;
+ }
+
+}
+
+/*
+* KASUMI Encryption
+*/
+void KASUMI::encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+ {
+ verify_key_set(m_EK.empty() == false);
+
+ for(size_t i = 0; i != blocks; ++i)
+ {
+ uint16_t B0 = load_be<uint16_t>(in, 0);
+ uint16_t B1 = load_be<uint16_t>(in, 1);
+ uint16_t B2 = load_be<uint16_t>(in, 2);
+ uint16_t B3 = load_be<uint16_t>(in, 3);
+
+ for(size_t j = 0; j != 8; j += 2)
+ {
+ const uint16_t* K = &m_EK[8*j];
+
+ uint16_t R = B1 ^ (rotl<1>(B0) & K[0]);
+ uint16_t L = B0 ^ (rotl<1>(R) | K[1]);
+
+ L = FI(L ^ K[ 2], K[ 3]) ^ R;
+ R = FI(R ^ K[ 4], K[ 5]) ^ L;
+ L = FI(L ^ K[ 6], K[ 7]) ^ R;
+
+ R = B2 ^= R;
+ L = B3 ^= L;
+
+ R = FI(R ^ K[10], K[11]) ^ L;
+ L = FI(L ^ K[12], K[13]) ^ R;
+ R = FI(R ^ K[14], K[15]) ^ L;
+
+ R ^= (rotl<1>(L) & K[8]);
+ L ^= (rotl<1>(R) | K[9]);
+
+ B0 ^= L;
+ B1 ^= R;
+ }
+
+ store_be(out, B0, B1, B2, B3);
+
+ in += BLOCK_SIZE;
+ out += BLOCK_SIZE;
+ }
+ }
+
+/*
+* KASUMI Decryption
+*/
+void KASUMI::decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+ {
+ verify_key_set(m_EK.empty() == false);
+
+ for(size_t i = 0; i != blocks; ++i)
+ {
+ uint16_t B0 = load_be<uint16_t>(in, 0);
+ uint16_t B1 = load_be<uint16_t>(in, 1);
+ uint16_t B2 = load_be<uint16_t>(in, 2);
+ uint16_t B3 = load_be<uint16_t>(in, 3);
+
+ for(size_t j = 0; j != 8; j += 2)
+ {
+ const uint16_t* K = &m_EK[8*(6-j)];
+
+ uint16_t L = B2, R = B3;
+
+ L = FI(L ^ K[10], K[11]) ^ R;
+ R = FI(R ^ K[12], K[13]) ^ L;
+ L = FI(L ^ K[14], K[15]) ^ R;
+
+ L ^= (rotl<1>(R) & K[8]);
+ R ^= (rotl<1>(L) | K[9]);
+
+ R = B0 ^= R;
+ L = B1 ^= L;
+
+ L ^= (rotl<1>(R) & K[0]);
+ R ^= (rotl<1>(L) | K[1]);
+
+ R = FI(R ^ K[2], K[3]) ^ L;
+ L = FI(L ^ K[4], K[5]) ^ R;
+ R = FI(R ^ K[6], K[7]) ^ L;
+
+ B2 ^= L;
+ B3 ^= R;
+ }
+
+ store_be(out, B0, B1, B2, B3);
+
+ in += BLOCK_SIZE;
+ out += BLOCK_SIZE;
+ }
+ }
+
+/*
+* KASUMI Key Schedule
+*/
+void KASUMI::key_schedule(const uint8_t key[], size_t)
+ {
+ static const uint16_t RC[] = { 0x0123, 0x4567, 0x89AB, 0xCDEF,
+ 0xFEDC, 0xBA98, 0x7654, 0x3210 };
+
+ secure_vector<uint16_t> K(16);
+ for(size_t i = 0; i != 8; ++i)
+ {
+ K[i] = load_be<uint16_t>(key, i);
+ K[i+8] = K[i] ^ RC[i];
+ }
+
+ m_EK.resize(64);
+
+ for(size_t i = 0; i != 8; ++i)
+ {
+ m_EK[8*i ] = rotl<2>(K[(i+0) % 8]);
+ m_EK[8*i+1] = rotl<1>(K[(i+2) % 8 + 8]);
+ m_EK[8*i+2] = rotl<5>(K[(i+1) % 8]);
+ m_EK[8*i+3] = K[(i+4) % 8 + 8];
+ m_EK[8*i+4] = rotl<8>(K[(i+5) % 8]);
+ m_EK[8*i+5] = K[(i+3) % 8 + 8];
+ m_EK[8*i+6] = rotl<13>(K[(i+6) % 8]);
+ m_EK[8*i+7] = K[(i+7) % 8 + 8];
+ }
+ }
+
+void KASUMI::clear()
+ {
+ zap(m_EK);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/block/kasumi/kasumi.h b/comm/third_party/botan/src/lib/block/kasumi/kasumi.h
new file mode 100644
index 0000000000..9ea09a1036
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/kasumi/kasumi.h
@@ -0,0 +1,37 @@
+/*
+* KASUMI
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_KASUMI_H_
+#define BOTAN_KASUMI_H_
+
+#include <botan/block_cipher.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(kasumi.h)
+
+namespace Botan {
+
+/**
+* KASUMI, the block cipher used in 3G telephony
+*/
+class BOTAN_PUBLIC_API(2,0) KASUMI final : public Block_Cipher_Fixed_Params<8, 16>
+ {
+ public:
+ void encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+ void decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+
+ void clear() override;
+ std::string name() const override { return "KASUMI"; }
+ BlockCipher* clone() const override { return new KASUMI; }
+ private:
+ void key_schedule(const uint8_t[], size_t) override;
+
+ secure_vector<uint16_t> m_EK;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/block/lion/info.txt b/comm/third_party/botan/src/lib/block/lion/info.txt
new file mode 100644
index 0000000000..a7b93e92ee
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/lion/info.txt
@@ -0,0 +1,8 @@
+<defines>
+LION -> 20131128
+</defines>
+
+<requires>
+stream
+hash
+</requires>
diff --git a/comm/third_party/botan/src/lib/block/lion/lion.cpp b/comm/third_party/botan/src/lib/block/lion/lion.cpp
new file mode 100644
index 0000000000..c9589a46ac
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/lion/lion.cpp
@@ -0,0 +1,138 @@
+/*
+* Lion
+* (C) 1999-2007,2014 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/lion.h>
+#include <botan/exceptn.h>
+
+namespace Botan {
+
+/*
+* Lion Encryption
+*/
+void Lion::encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+ {
+ verify_key_set(m_key1.empty() == false);
+
+ const size_t LEFT_SIZE = left_size();
+ const size_t RIGHT_SIZE = right_size();
+
+ secure_vector<uint8_t> buffer_vec(LEFT_SIZE);
+ uint8_t* buffer = buffer_vec.data();
+
+ for(size_t i = 0; i != blocks; ++i)
+ {
+ xor_buf(buffer, in, m_key1.data(), LEFT_SIZE);
+ m_cipher->set_key(buffer, LEFT_SIZE);
+ m_cipher->cipher(in + LEFT_SIZE, out + LEFT_SIZE, RIGHT_SIZE);
+
+ m_hash->update(out + LEFT_SIZE, RIGHT_SIZE);
+ m_hash->final(buffer);
+ xor_buf(out, in, buffer, LEFT_SIZE);
+
+ xor_buf(buffer, out, m_key2.data(), LEFT_SIZE);
+ m_cipher->set_key(buffer, LEFT_SIZE);
+ m_cipher->cipher1(out + LEFT_SIZE, RIGHT_SIZE);
+
+ in += m_block_size;
+ out += m_block_size;
+ }
+ }
+
+/*
+* Lion Decryption
+*/
+void Lion::decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+ {
+ verify_key_set(m_key1.empty() == false);
+
+ const size_t LEFT_SIZE = left_size();
+ const size_t RIGHT_SIZE = right_size();
+
+ secure_vector<uint8_t> buffer_vec(LEFT_SIZE);
+ uint8_t* buffer = buffer_vec.data();
+
+ for(size_t i = 0; i != blocks; ++i)
+ {
+ xor_buf(buffer, in, m_key2.data(), LEFT_SIZE);
+ m_cipher->set_key(buffer, LEFT_SIZE);
+ m_cipher->cipher(in + LEFT_SIZE, out + LEFT_SIZE, RIGHT_SIZE);
+
+ m_hash->update(out + LEFT_SIZE, RIGHT_SIZE);
+ m_hash->final(buffer);
+ xor_buf(out, in, buffer, LEFT_SIZE);
+
+ xor_buf(buffer, out, m_key1.data(), LEFT_SIZE);
+ m_cipher->set_key(buffer, LEFT_SIZE);
+ m_cipher->cipher1(out + LEFT_SIZE, RIGHT_SIZE);
+
+ in += m_block_size;
+ out += m_block_size;
+ }
+ }
+
+/*
+* Lion Key Schedule
+*/
+void Lion::key_schedule(const uint8_t key[], size_t length)
+ {
+ clear();
+
+ const size_t half = length / 2;
+
+ m_key1.resize(left_size());
+ m_key2.resize(left_size());
+ clear_mem(m_key1.data(), m_key1.size());
+ clear_mem(m_key2.data(), m_key2.size());
+ copy_mem(m_key1.data(), key, half);
+ copy_mem(m_key2.data(), key + half, half);
+ }
+
+/*
+* Return the name of this type
+*/
+std::string Lion::name() const
+ {
+ return "Lion(" + m_hash->name() + "," +
+ m_cipher->name() + "," +
+ std::to_string(block_size()) + ")";
+ }
+
+/*
+* Return a clone of this object
+*/
+BlockCipher* Lion::clone() const
+ {
+ return new Lion(m_hash->clone(), m_cipher->clone(), block_size());
+ }
+
+/*
+* Clear memory of sensitive data
+*/
+void Lion::clear()
+ {
+ zap(m_key1);
+ zap(m_key2);
+ m_hash->clear();
+ m_cipher->clear();
+ }
+
+/*
+* Lion Constructor
+*/
+Lion::Lion(HashFunction* hash, StreamCipher* cipher, size_t bs) :
+ m_block_size(std::max<size_t>(2*hash->output_length() + 1, bs)),
+ m_hash(hash),
+ m_cipher(cipher)
+ {
+ if(2*left_size() + 1 > m_block_size)
+ throw Invalid_Argument(name() + ": Chosen block size is too small");
+
+ if(!m_cipher->valid_keylength(left_size()))
+ throw Invalid_Argument(name() + ": This stream/hash combo is invalid");
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/block/lion/lion.h b/comm/third_party/botan/src/lib/block/lion/lion.h
new file mode 100644
index 0000000000..fa8e9f4145
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/lion/lion.h
@@ -0,0 +1,66 @@
+/*
+* Lion
+* (C) 1999-2007,2014 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_LION_H_
+#define BOTAN_LION_H_
+
+#include <botan/block_cipher.h>
+#include <botan/stream_cipher.h>
+#include <botan/hash.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(lion.h)
+
+namespace Botan {
+
+/**
+* Lion is a block cipher construction designed by Ross Anderson and
+* Eli Biham, described in "Two Practical and Provably Secure Block
+* Ciphers: BEAR and LION". It has a variable block size and is
+* designed to encrypt very large blocks (up to a megabyte)
+
+* https://www.cl.cam.ac.uk/~rja14/Papers/bear-lion.pdf
+*/
+class BOTAN_PUBLIC_API(2,0) Lion final : public BlockCipher
+ {
+ public:
+ void encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+ void decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+
+ size_t block_size() const override { return m_block_size; }
+
+ Key_Length_Specification key_spec() const override
+ {
+ return Key_Length_Specification(2, 2*m_hash->output_length(), 2);
+ }
+
+ void clear() override;
+ std::string name() const override;
+ BlockCipher* clone() const override;
+
+ /**
+ * @param hash the hash to use internally
+ * @param cipher the stream cipher to use internally
+ * @param block_size the size of the block to use
+ */
+ Lion(HashFunction* hash,
+ StreamCipher* cipher,
+ size_t block_size);
+ private:
+ void key_schedule(const uint8_t[], size_t) override;
+
+ size_t left_size() const { return m_hash->output_length(); }
+ size_t right_size() const { return m_block_size - left_size(); }
+
+ const size_t m_block_size;
+ std::unique_ptr<HashFunction> m_hash;
+ std::unique_ptr<StreamCipher> m_cipher;
+ secure_vector<uint8_t> m_key1, m_key2;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/block/misty1/info.txt b/comm/third_party/botan/src/lib/block/misty1/info.txt
new file mode 100644
index 0000000000..bf21dd4390
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/misty1/info.txt
@@ -0,0 +1,3 @@
+<defines>
+MISTY1 -> 20131128
+</defines>
diff --git a/comm/third_party/botan/src/lib/block/misty1/misty1.cpp b/comm/third_party/botan/src/lib/block/misty1/misty1.cpp
new file mode 100644
index 0000000000..ba5b06abec
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/misty1/misty1.cpp
@@ -0,0 +1,263 @@
+/*
+* MISTY1
+* (C) 1999-2009 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/misty1.h>
+#include <botan/loadstor.h>
+
+namespace Botan {
+
+namespace {
+
+alignas(64) static const uint8_t MISTY1_SBOX_S7[128] = {
+ 0x1B, 0x32, 0x33, 0x5A, 0x3B, 0x10, 0x17, 0x54, 0x5B, 0x1A, 0x72, 0x73,
+ 0x6B, 0x2C, 0x66, 0x49, 0x1F, 0x24, 0x13, 0x6C, 0x37, 0x2E, 0x3F, 0x4A,
+ 0x5D, 0x0F, 0x40, 0x56, 0x25, 0x51, 0x1C, 0x04, 0x0B, 0x46, 0x20, 0x0D,
+ 0x7B, 0x35, 0x44, 0x42, 0x2B, 0x1E, 0x41, 0x14, 0x4B, 0x79, 0x15, 0x6F,
+ 0x0E, 0x55, 0x09, 0x36, 0x74, 0x0C, 0x67, 0x53, 0x28, 0x0A, 0x7E, 0x38,
+ 0x02, 0x07, 0x60, 0x29, 0x19, 0x12, 0x65, 0x2F, 0x30, 0x39, 0x08, 0x68,
+ 0x5F, 0x78, 0x2A, 0x4C, 0x64, 0x45, 0x75, 0x3D, 0x59, 0x48, 0x03, 0x57,
+ 0x7C, 0x4F, 0x62, 0x3C, 0x1D, 0x21, 0x5E, 0x27, 0x6A, 0x70, 0x4D, 0x3A,
+ 0x01, 0x6D, 0x6E, 0x63, 0x18, 0x77, 0x23, 0x05, 0x26, 0x76, 0x00, 0x31,
+ 0x2D, 0x7A, 0x7F, 0x61, 0x50, 0x22, 0x11, 0x06, 0x47, 0x16, 0x52, 0x4E,
+ 0x71, 0x3E, 0x69, 0x43, 0x34, 0x5C, 0x58, 0x7D };
+
+alignas(64) static const uint16_t MISTY1_SBOX_S9[512] = {
+ 0x01C3, 0x00CB, 0x0153, 0x019F, 0x01E3, 0x00E9, 0x00FB, 0x0035, 0x0181,
+ 0x00B9, 0x0117, 0x01EB, 0x0133, 0x0009, 0x002D, 0x00D3, 0x00C7, 0x014A,
+ 0x0037, 0x007E, 0x00EB, 0x0164, 0x0193, 0x01D8, 0x00A3, 0x011E, 0x0055,
+ 0x002C, 0x001D, 0x01A2, 0x0163, 0x0118, 0x014B, 0x0152, 0x01D2, 0x000F,
+ 0x002B, 0x0030, 0x013A, 0x00E5, 0x0111, 0x0138, 0x018E, 0x0063, 0x00E3,
+ 0x00C8, 0x01F4, 0x001B, 0x0001, 0x009D, 0x00F8, 0x01A0, 0x016D, 0x01F3,
+ 0x001C, 0x0146, 0x007D, 0x00D1, 0x0082, 0x01EA, 0x0183, 0x012D, 0x00F4,
+ 0x019E, 0x01D3, 0x00DD, 0x01E2, 0x0128, 0x01E0, 0x00EC, 0x0059, 0x0091,
+ 0x0011, 0x012F, 0x0026, 0x00DC, 0x00B0, 0x018C, 0x010F, 0x01F7, 0x00E7,
+ 0x016C, 0x00B6, 0x00F9, 0x00D8, 0x0151, 0x0101, 0x014C, 0x0103, 0x00B8,
+ 0x0154, 0x012B, 0x01AE, 0x0017, 0x0071, 0x000C, 0x0047, 0x0058, 0x007F,
+ 0x01A4, 0x0134, 0x0129, 0x0084, 0x015D, 0x019D, 0x01B2, 0x01A3, 0x0048,
+ 0x007C, 0x0051, 0x01CA, 0x0023, 0x013D, 0x01A7, 0x0165, 0x003B, 0x0042,
+ 0x00DA, 0x0192, 0x00CE, 0x00C1, 0x006B, 0x009F, 0x01F1, 0x012C, 0x0184,
+ 0x00FA, 0x0196, 0x01E1, 0x0169, 0x017D, 0x0031, 0x0180, 0x010A, 0x0094,
+ 0x01DA, 0x0186, 0x013E, 0x011C, 0x0060, 0x0175, 0x01CF, 0x0067, 0x0119,
+ 0x0065, 0x0068, 0x0099, 0x0150, 0x0008, 0x0007, 0x017C, 0x00B7, 0x0024,
+ 0x0019, 0x00DE, 0x0127, 0x00DB, 0x00E4, 0x01A9, 0x0052, 0x0109, 0x0090,
+ 0x019C, 0x01C1, 0x0028, 0x01B3, 0x0135, 0x016A, 0x0176, 0x00DF, 0x01E5,
+ 0x0188, 0x00C5, 0x016E, 0x01DE, 0x01B1, 0x00C3, 0x01DF, 0x0036, 0x00EE,
+ 0x01EE, 0x00F0, 0x0093, 0x0049, 0x009A, 0x01B6, 0x0069, 0x0081, 0x0125,
+ 0x000B, 0x005E, 0x00B4, 0x0149, 0x01C7, 0x0174, 0x003E, 0x013B, 0x01B7,
+ 0x008E, 0x01C6, 0x00AE, 0x0010, 0x0095, 0x01EF, 0x004E, 0x00F2, 0x01FD,
+ 0x0085, 0x00FD, 0x00F6, 0x00A0, 0x016F, 0x0083, 0x008A, 0x0156, 0x009B,
+ 0x013C, 0x0107, 0x0167, 0x0098, 0x01D0, 0x01E9, 0x0003, 0x01FE, 0x00BD,
+ 0x0122, 0x0089, 0x00D2, 0x018F, 0x0012, 0x0033, 0x006A, 0x0142, 0x00ED,
+ 0x0170, 0x011B, 0x00E2, 0x014F, 0x0158, 0x0131, 0x0147, 0x005D, 0x0113,
+ 0x01CD, 0x0079, 0x0161, 0x01A5, 0x0179, 0x009E, 0x01B4, 0x00CC, 0x0022,
+ 0x0132, 0x001A, 0x00E8, 0x0004, 0x0187, 0x01ED, 0x0197, 0x0039, 0x01BF,
+ 0x01D7, 0x0027, 0x018B, 0x00C6, 0x009C, 0x00D0, 0x014E, 0x006C, 0x0034,
+ 0x01F2, 0x006E, 0x00CA, 0x0025, 0x00BA, 0x0191, 0x00FE, 0x0013, 0x0106,
+ 0x002F, 0x01AD, 0x0172, 0x01DB, 0x00C0, 0x010B, 0x01D6, 0x00F5, 0x01EC,
+ 0x010D, 0x0076, 0x0114, 0x01AB, 0x0075, 0x010C, 0x01E4, 0x0159, 0x0054,
+ 0x011F, 0x004B, 0x00C4, 0x01BE, 0x00F7, 0x0029, 0x00A4, 0x000E, 0x01F0,
+ 0x0077, 0x004D, 0x017A, 0x0086, 0x008B, 0x00B3, 0x0171, 0x00BF, 0x010E,
+ 0x0104, 0x0097, 0x015B, 0x0160, 0x0168, 0x00D7, 0x00BB, 0x0066, 0x01CE,
+ 0x00FC, 0x0092, 0x01C5, 0x006F, 0x0016, 0x004A, 0x00A1, 0x0139, 0x00AF,
+ 0x00F1, 0x0190, 0x000A, 0x01AA, 0x0143, 0x017B, 0x0056, 0x018D, 0x0166,
+ 0x00D4, 0x01FB, 0x014D, 0x0194, 0x019A, 0x0087, 0x01F8, 0x0123, 0x00A7,
+ 0x01B8, 0x0141, 0x003C, 0x01F9, 0x0140, 0x002A, 0x0155, 0x011A, 0x01A1,
+ 0x0198, 0x00D5, 0x0126, 0x01AF, 0x0061, 0x012E, 0x0157, 0x01DC, 0x0072,
+ 0x018A, 0x00AA, 0x0096, 0x0115, 0x00EF, 0x0045, 0x007B, 0x008D, 0x0145,
+ 0x0053, 0x005F, 0x0178, 0x00B2, 0x002E, 0x0020, 0x01D5, 0x003F, 0x01C9,
+ 0x01E7, 0x01AC, 0x0044, 0x0038, 0x0014, 0x00B1, 0x016B, 0x00AB, 0x00B5,
+ 0x005A, 0x0182, 0x01C8, 0x01D4, 0x0018, 0x0177, 0x0064, 0x00CF, 0x006D,
+ 0x0100, 0x0199, 0x0130, 0x015A, 0x0005, 0x0120, 0x01BB, 0x01BD, 0x00E0,
+ 0x004F, 0x00D6, 0x013F, 0x01C4, 0x012A, 0x0015, 0x0006, 0x00FF, 0x019B,
+ 0x00A6, 0x0043, 0x0088, 0x0050, 0x015F, 0x01E8, 0x0121, 0x0073, 0x017E,
+ 0x00BC, 0x00C2, 0x00C9, 0x0173, 0x0189, 0x01F5, 0x0074, 0x01CC, 0x01E6,
+ 0x01A8, 0x0195, 0x001F, 0x0041, 0x000D, 0x01BA, 0x0032, 0x003D, 0x01D1,
+ 0x0080, 0x00A8, 0x0057, 0x01B9, 0x0162, 0x0148, 0x00D9, 0x0105, 0x0062,
+ 0x007A, 0x0021, 0x01FF, 0x0112, 0x0108, 0x01C0, 0x00A9, 0x011D, 0x01B0,
+ 0x01A6, 0x00CD, 0x00F3, 0x005C, 0x0102, 0x005B, 0x01D9, 0x0144, 0x01F6,
+ 0x00AD, 0x00A5, 0x003A, 0x01CB, 0x0136, 0x017F, 0x0046, 0x00E1, 0x001E,
+ 0x01DD, 0x00E6, 0x0137, 0x01FA, 0x0185, 0x008C, 0x008F, 0x0040, 0x01B5,
+ 0x00BE, 0x0078, 0x0000, 0x00AC, 0x0110, 0x015E, 0x0124, 0x0002, 0x01BC,
+ 0x00A2, 0x00EA, 0x0070, 0x01FC, 0x0116, 0x015C, 0x004C, 0x01C2 };
+
+/*
+* MISTY1 FI Function
+*/
+uint16_t FI(uint16_t input, uint16_t key7, uint16_t key9)
+ {
+ uint16_t D9 = input >> 7, D7 = input & 0x7F;
+ D9 = MISTY1_SBOX_S9[D9] ^ D7;
+ D7 = (MISTY1_SBOX_S7[D7] ^ key7 ^ D9) & 0x7F;
+ D9 = MISTY1_SBOX_S9[D9 ^ key9] ^ D7;
+ return static_cast<uint16_t>(D7 << 9) | D9;
+ }
+
+}
+
+/*
+* MISTY1 Encryption
+*/
+void MISTY1::encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+ {
+ verify_key_set(m_EK.empty() == false);
+
+ for(size_t i = 0; i != blocks; ++i)
+ {
+ uint16_t B0 = load_be<uint16_t>(in, 0);
+ uint16_t B1 = load_be<uint16_t>(in, 1);
+ uint16_t B2 = load_be<uint16_t>(in, 2);
+ uint16_t B3 = load_be<uint16_t>(in, 3);
+
+ for(size_t j = 0; j != 12; j += 3)
+ {
+ const uint16_t* RK = &m_EK[8 * j];
+
+ B1 ^= B0 & RK[0];
+ B0 ^= B1 | RK[1];
+ B3 ^= B2 & RK[2];
+ B2 ^= B3 | RK[3];
+
+ uint16_t T0, T1;
+
+ T0 = FI(B0 ^ RK[ 4], RK[ 5], RK[ 6]) ^ B1;
+ T1 = FI(B1 ^ RK[ 7], RK[ 8], RK[ 9]) ^ T0;
+ T0 = FI(T0 ^ RK[10], RK[11], RK[12]) ^ T1;
+
+ B2 ^= T1 ^ RK[13];
+ B3 ^= T0;
+
+ T0 = FI(B2 ^ RK[14], RK[15], RK[16]) ^ B3;
+ T1 = FI(B3 ^ RK[17], RK[18], RK[19]) ^ T0;
+ T0 = FI(T0 ^ RK[20], RK[21], RK[22]) ^ T1;
+
+ B0 ^= T1 ^ RK[23];
+ B1 ^= T0;
+ }
+
+ B1 ^= B0 & m_EK[96];
+ B0 ^= B1 | m_EK[97];
+ B3 ^= B2 & m_EK[98];
+ B2 ^= B3 | m_EK[99];
+
+ store_be(out, B2, B3, B0, B1);
+
+ in += BLOCK_SIZE;
+ out += BLOCK_SIZE;
+ }
+ }
+
+/*
+* MISTY1 Decryption
+*/
+void MISTY1::decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+ {
+ verify_key_set(m_DK.empty() == false);
+
+ for(size_t i = 0; i != blocks; ++i)
+ {
+ uint16_t B0 = load_be<uint16_t>(in, 2);
+ uint16_t B1 = load_be<uint16_t>(in, 3);
+ uint16_t B2 = load_be<uint16_t>(in, 0);
+ uint16_t B3 = load_be<uint16_t>(in, 1);
+
+ for(size_t j = 0; j != 12; j += 3)
+ {
+ const uint16_t* RK = &m_DK[8 * j];
+
+ B2 ^= B3 | RK[0];
+ B3 ^= B2 & RK[1];
+ B0 ^= B1 | RK[2];
+ B1 ^= B0 & RK[3];
+
+ uint16_t T0, T1;
+
+ T0 = FI(B2 ^ RK[ 4], RK[ 5], RK[ 6]) ^ B3;
+ T1 = FI(B3 ^ RK[ 7], RK[ 8], RK[ 9]) ^ T0;
+ T0 = FI(T0 ^ RK[10], RK[11], RK[12]) ^ T1;
+
+ B0 ^= T1 ^ RK[13];
+ B1 ^= T0;
+
+ T0 = FI(B0 ^ RK[14], RK[15], RK[16]) ^ B1;
+ T1 = FI(B1 ^ RK[17], RK[18], RK[19]) ^ T0;
+ T0 = FI(T0 ^ RK[20], RK[21], RK[22]) ^ T1;
+
+ B2 ^= T1 ^ RK[23];
+ B3 ^= T0;
+ }
+
+ B2 ^= B3 | m_DK[96];
+ B3 ^= B2 & m_DK[97];
+ B0 ^= B1 | m_DK[98];
+ B1 ^= B0 & m_DK[99];
+
+ store_be(out, B0, B1, B2, B3);
+
+ in += BLOCK_SIZE;
+ out += BLOCK_SIZE;
+ }
+ }
+
+/*
+* MISTY1 Key Schedule
+*/
+void MISTY1::key_schedule(const uint8_t key[], size_t length)
+ {
+ secure_vector<uint16_t> KS(32);
+ for(size_t i = 0; i != length / 2; ++i)
+ KS[i] = load_be<uint16_t>(key, i);
+
+ for(size_t i = 0; i != 8; ++i)
+ {
+ KS[i+ 8] = FI(KS[i], KS[(i+1) % 8] >> 9, KS[(i+1) % 8] & 0x1FF);
+ KS[i+16] = KS[i+8] >> 9;
+ KS[i+24] = KS[i+8] & 0x1FF;
+ }
+
+ /*
+ * Precomputed indexes for the orderings of the subkeys (MISTY1 reuses
+ * values)
+ */
+ static const uint8_t EK_ORDER[100] = {
+ 0x00, 0x0E, 0x0A, 0x04, 0x00, 0x15, 0x1D, 0x02, 0x11, 0x19, 0x07, 0x13,
+ 0x1B, 0x04, 0x01, 0x16, 0x1E, 0x03, 0x12, 0x1A, 0x00, 0x14, 0x1C, 0x05,
+ 0x01, 0x0F, 0x0B, 0x05, 0x02, 0x17, 0x1F, 0x04, 0x13, 0x1B, 0x01, 0x15,
+ 0x1D, 0x06, 0x03, 0x10, 0x18, 0x05, 0x14, 0x1C, 0x02, 0x16, 0x1E, 0x07,
+ 0x02, 0x08, 0x0C, 0x06, 0x04, 0x11, 0x19, 0x06, 0x15, 0x1D, 0x03, 0x17,
+ 0x1F, 0x00, 0x05, 0x12, 0x1A, 0x07, 0x16, 0x1E, 0x04, 0x10, 0x18, 0x01,
+ 0x03, 0x09, 0x0D, 0x07, 0x06, 0x13, 0x1B, 0x00, 0x17, 0x1F, 0x05, 0x11,
+ 0x19, 0x02, 0x07, 0x14, 0x1C, 0x01, 0x10, 0x18, 0x06, 0x12, 0x1A, 0x03,
+ 0x04, 0x0A, 0x0E, 0x00 };
+
+ static const uint8_t DK_ORDER[100] = {
+ 0x00, 0x0E, 0x0A, 0x04, 0x07, 0x14, 0x1C, 0x01, 0x10, 0x18, 0x06, 0x12,
+ 0x1A, 0x03, 0x06, 0x13, 0x1B, 0x00, 0x17, 0x1F, 0x05, 0x11, 0x19, 0x02,
+ 0x07, 0x0D, 0x09, 0x03, 0x05, 0x12, 0x1A, 0x07, 0x16, 0x1E, 0x04, 0x10,
+ 0x18, 0x01, 0x04, 0x11, 0x19, 0x06, 0x15, 0x1D, 0x03, 0x17, 0x1F, 0x00,
+ 0x06, 0x0C, 0x08, 0x02, 0x03, 0x10, 0x18, 0x05, 0x14, 0x1C, 0x02, 0x16,
+ 0x1E, 0x07, 0x02, 0x17, 0x1F, 0x04, 0x13, 0x1B, 0x01, 0x15, 0x1D, 0x06,
+ 0x05, 0x0B, 0x0F, 0x01, 0x01, 0x16, 0x1E, 0x03, 0x12, 0x1A, 0x00, 0x14,
+ 0x1C, 0x05, 0x00, 0x15, 0x1D, 0x02, 0x11, 0x19, 0x07, 0x13, 0x1B, 0x04,
+ 0x04, 0x0A, 0x0E, 0x00 };
+
+ m_EK.resize(100);
+ m_DK.resize(100);
+
+ for(size_t i = 0; i != 100; ++i)
+ {
+ m_EK[i] = KS[EK_ORDER[i]];
+ m_DK[i] = KS[DK_ORDER[i]];
+ }
+ }
+
+void MISTY1::clear()
+ {
+ zap(m_EK);
+ zap(m_DK);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/block/misty1/misty1.h b/comm/third_party/botan/src/lib/block/misty1/misty1.h
new file mode 100644
index 0000000000..b6c4abbd53
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/misty1/misty1.h
@@ -0,0 +1,37 @@
+/*
+* MISTY1
+* (C) 1999-2008 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_MISTY1_H_
+#define BOTAN_MISTY1_H_
+
+#include <botan/block_cipher.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(misty1.h)
+
+namespace Botan {
+
+/**
+* MISTY1 with 8 rounds
+*/
+class BOTAN_PUBLIC_API(2,0) MISTY1 final : public Block_Cipher_Fixed_Params<8, 16>
+ {
+ public:
+ void encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+ void decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+
+ void clear() override;
+ std::string name() const override { return "MISTY1"; }
+ BlockCipher* clone() const override { return new MISTY1; }
+ private:
+ void key_schedule(const uint8_t[], size_t) override;
+
+ secure_vector<uint16_t> m_EK, m_DK;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/block/noekeon/info.txt b/comm/third_party/botan/src/lib/block/noekeon/info.txt
new file mode 100644
index 0000000000..f3445eb7e6
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/noekeon/info.txt
@@ -0,0 +1,3 @@
+<defines>
+NOEKEON -> 20131128
+</defines>
diff --git a/comm/third_party/botan/src/lib/block/noekeon/noekeon.cpp b/comm/third_party/botan/src/lib/block/noekeon/noekeon.cpp
new file mode 100644
index 0000000000..0a820c7014
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/noekeon/noekeon.cpp
@@ -0,0 +1,267 @@
+/*
+* Noekeon
+* (C) 1999-2008 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/noekeon.h>
+#include <botan/loadstor.h>
+#include <botan/rotate.h>
+#include <botan/cpuid.h>
+
+namespace Botan {
+
+namespace {
+
+/*
+* Noekeon's Theta Operation
+*/
+inline void theta(uint32_t& A0, uint32_t& A1,
+ uint32_t& A2, uint32_t& A3,
+ const uint32_t EK[4])
+ {
+ uint32_t T = A0 ^ A2;
+ T ^= rotl<8>(T) ^ rotr<8>(T);
+ A1 ^= T;
+ A3 ^= T;
+
+ A0 ^= EK[0];
+ A1 ^= EK[1];
+ A2 ^= EK[2];
+ A3 ^= EK[3];
+
+ T = A1 ^ A3;
+ T ^= rotl<8>(T) ^ rotr<8>(T);
+ A0 ^= T;
+ A2 ^= T;
+ }
+
+/*
+* Theta With Null Key
+*/
+inline void theta(uint32_t& A0, uint32_t& A1,
+ uint32_t& A2, uint32_t& A3)
+ {
+ uint32_t T = A0 ^ A2;
+ T ^= rotl<8>(T) ^ rotr<8>(T);
+ A1 ^= T;
+ A3 ^= T;
+
+ T = A1 ^ A3;
+ T ^= rotl<8>(T) ^ rotr<8>(T);
+ A0 ^= T;
+ A2 ^= T;
+ }
+
+/*
+* Noekeon's Gamma S-Box Layer
+*/
+inline void gamma(uint32_t& A0, uint32_t& A1, uint32_t& A2, uint32_t& A3)
+ {
+ A1 ^= ~A3 & ~A2;
+ A0 ^= A2 & A1;
+
+ uint32_t T = A3;
+ A3 = A0;
+ A0 = T;
+
+ A2 ^= A0 ^ A1 ^ A3;
+
+ A1 ^= ~A3 & ~A2;
+ A0 ^= A2 & A1;
+ }
+
+}
+
+size_t Noekeon::parallelism() const
+ {
+#if defined(BOTAN_HAS_NOEKEON_SIMD)
+ if(CPUID::has_simd_32())
+ {
+ return 4;
+ }
+#endif
+
+ return 1;
+ }
+
+std::string Noekeon::provider() const
+ {
+#if defined(BOTAN_HAS_NOEKEON_SIMD)
+ if(CPUID::has_simd_32())
+ {
+ return "simd";
+ }
+#endif
+
+ return "base";
+ }
+
+/*
+* Noekeon Round Constants
+*/
+const uint8_t Noekeon::RC[] = {
+ 0x80, 0x1B, 0x36, 0x6C, 0xD8, 0xAB, 0x4D, 0x9A,
+ 0x2F, 0x5E, 0xBC, 0x63, 0xC6, 0x97, 0x35, 0x6A,
+ 0xD4 };
+
+/*
+* Noekeon Encryption
+*/
+void Noekeon::encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+ {
+ verify_key_set(m_EK.empty() == false);
+
+#if defined(BOTAN_HAS_NOEKEON_SIMD)
+ if(CPUID::has_simd_32())
+ {
+ while(blocks >= 4)
+ {
+ simd_encrypt_4(in, out);
+ in += 4 * BLOCK_SIZE;
+ out += 4 * BLOCK_SIZE;
+ blocks -= 4;
+ }
+ }
+#endif
+
+ for(size_t i = 0; i != blocks; ++i)
+ {
+ uint32_t A0 = load_be<uint32_t>(in, 0);
+ uint32_t A1 = load_be<uint32_t>(in, 1);
+ uint32_t A2 = load_be<uint32_t>(in, 2);
+ uint32_t A3 = load_be<uint32_t>(in, 3);
+
+ for(size_t j = 0; j != 16; ++j)
+ {
+ A0 ^= RC[j];
+ theta(A0, A1, A2, A3, m_EK.data());
+
+ A1 = rotl<1>(A1);
+ A2 = rotl<5>(A2);
+ A3 = rotl<2>(A3);
+
+ gamma(A0, A1, A2, A3);
+
+ A1 = rotr<1>(A1);
+ A2 = rotr<5>(A2);
+ A3 = rotr<2>(A3);
+ }
+
+ A0 ^= RC[16];
+ theta(A0, A1, A2, A3, m_EK.data());
+
+ store_be(out, A0, A1, A2, A3);
+
+ in += BLOCK_SIZE;
+ out += BLOCK_SIZE;
+ }
+ }
+
+/*
+* Noekeon Encryption
+*/
+void Noekeon::decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+ {
+ verify_key_set(m_DK.empty() == false);
+
+#if defined(BOTAN_HAS_NOEKEON_SIMD)
+ if(CPUID::has_simd_32())
+ {
+ while(blocks >= 4)
+ {
+ simd_decrypt_4(in, out);
+ in += 4 * BLOCK_SIZE;
+ out += 4 * BLOCK_SIZE;
+ blocks -= 4;
+ }
+ }
+#endif
+
+ for(size_t i = 0; i != blocks; ++i)
+ {
+ uint32_t A0 = load_be<uint32_t>(in, 0);
+ uint32_t A1 = load_be<uint32_t>(in, 1);
+ uint32_t A2 = load_be<uint32_t>(in, 2);
+ uint32_t A3 = load_be<uint32_t>(in, 3);
+
+ for(size_t j = 16; j != 0; --j)
+ {
+ theta(A0, A1, A2, A3, m_DK.data());
+ A0 ^= RC[j];
+
+ A1 = rotl<1>(A1);
+ A2 = rotl<5>(A2);
+ A3 = rotl<2>(A3);
+
+ gamma(A0, A1, A2, A3);
+
+ A1 = rotr<1>(A1);
+ A2 = rotr<5>(A2);
+ A3 = rotr<2>(A3);
+ }
+
+ theta(A0, A1, A2, A3, m_DK.data());
+ A0 ^= RC[0];
+
+ store_be(out, A0, A1, A2, A3);
+
+ in += BLOCK_SIZE;
+ out += BLOCK_SIZE;
+ }
+ }
+
+/*
+* Noekeon Key Schedule
+*/
+void Noekeon::key_schedule(const uint8_t key[], size_t)
+ {
+ uint32_t A0 = load_be<uint32_t>(key, 0);
+ uint32_t A1 = load_be<uint32_t>(key, 1);
+ uint32_t A2 = load_be<uint32_t>(key, 2);
+ uint32_t A3 = load_be<uint32_t>(key, 3);
+
+ for(size_t i = 0; i != 16; ++i)
+ {
+ A0 ^= RC[i];
+ theta(A0, A1, A2, A3);
+
+ A1 = rotl<1>(A1);
+ A2 = rotl<5>(A2);
+ A3 = rotl<2>(A3);
+
+ gamma(A0, A1, A2, A3);
+
+ A1 = rotr<1>(A1);
+ A2 = rotr<5>(A2);
+ A3 = rotr<2>(A3);
+ }
+
+ A0 ^= RC[16];
+
+ m_DK.resize(4);
+ m_DK[0] = A0;
+ m_DK[1] = A1;
+ m_DK[2] = A2;
+ m_DK[3] = A3;
+
+ theta(A0, A1, A2, A3);
+
+ m_EK.resize(4);
+ m_EK[0] = A0;
+ m_EK[1] = A1;
+ m_EK[2] = A2;
+ m_EK[3] = A3;
+ }
+
+/*
+* Clear memory of sensitive data
+*/
+void Noekeon::clear()
+ {
+ zap(m_EK);
+ zap(m_DK);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/block/noekeon/noekeon.h b/comm/third_party/botan/src/lib/block/noekeon/noekeon.h
new file mode 100644
index 0000000000..9e7e4a5ff6
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/noekeon/noekeon.h
@@ -0,0 +1,49 @@
+/*
+* Noekeon
+* (C) 1999-2008 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_NOEKEON_H_
+#define BOTAN_NOEKEON_H_
+
+#include <botan/block_cipher.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(noekeon.h)
+
+namespace Botan {
+
+/**
+* Noekeon
+*/
+class BOTAN_PUBLIC_API(2,0) Noekeon final : public Block_Cipher_Fixed_Params<16, 16>
+ {
+ public:
+ void encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+ void decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+
+ std::string provider() const override;
+ void clear() override;
+ std::string name() const override { return "Noekeon"; }
+ BlockCipher* clone() const override { return new Noekeon; }
+ size_t parallelism() const override;
+
+ private:
+#if defined(BOTAN_HAS_NOEKEON_SIMD)
+ void simd_encrypt_4(const uint8_t in[], uint8_t out[]) const;
+ void simd_decrypt_4(const uint8_t in[], uint8_t out[]) const;
+#endif
+
+ /**
+ * The Noekeon round constants
+ */
+ static const uint8_t RC[17];
+
+ void key_schedule(const uint8_t[], size_t) override;
+ secure_vector<uint32_t> m_EK, m_DK;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/block/noekeon/noekeon_simd/info.txt b/comm/third_party/botan/src/lib/block/noekeon/noekeon_simd/info.txt
new file mode 100644
index 0000000000..a09e491b5c
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/noekeon/noekeon_simd/info.txt
@@ -0,0 +1,8 @@
+<defines>
+NOEKEON_SIMD -> 20160903
+</defines>
+
+<requires>
+noekeon
+simd
+</requires>
diff --git a/comm/third_party/botan/src/lib/block/noekeon/noekeon_simd/noekeon_simd.cpp b/comm/third_party/botan/src/lib/block/noekeon/noekeon_simd/noekeon_simd.cpp
new file mode 100644
index 0000000000..83467a054c
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/noekeon/noekeon_simd/noekeon_simd.cpp
@@ -0,0 +1,143 @@
+/*
+* Noekeon in SIMD
+* (C) 2010 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/noekeon.h>
+#include <botan/internal/simd_32.h>
+
+namespace Botan {
+
+/*
+* Noekeon's Theta Operation
+*/
+#define NOK_SIMD_THETA(A0, A1, A2, A3, K0, K1, K2, K3) \
+ do { \
+ SIMD_4x32 T = A0 ^ A2; \
+ T ^= T.rotl<8>() ^ T.rotr<8>(); \
+ A1 ^= T; \
+ A3 ^= T; \
+ \
+ A0 ^= K0; \
+ A1 ^= K1; \
+ A2 ^= K2; \
+ A3 ^= K3; \
+ \
+ T = A1 ^ A3; \
+ T ^= T.rotl<8>() ^ T.rotr<8>(); \
+ A0 ^= T; \
+ A2 ^= T; \
+ } while(0)
+
+/*
+* Noekeon's Gamma S-Box Layer
+*/
+#define NOK_SIMD_GAMMA(A0, A1, A2, A3) \
+ do \
+ { \
+ A1 ^= A3.andc(~A2); \
+ A0 ^= A2 & A1; \
+ \
+ SIMD_4x32 T = A3; \
+ A3 = A0; \
+ A0 = T; \
+ \
+ A2 ^= A0 ^ A1 ^ A3; \
+ \
+ A1 ^= A3.andc(~A2); \
+ A0 ^= A2 & A1; \
+ } while(0)
+
+/*
+* Noekeon Encryption
+*/
+void 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]);
+ const SIMD_4x32 K3 = SIMD_4x32::splat(m_EK[3]);
+
+ SIMD_4x32 A0 = SIMD_4x32::load_be(in );
+ SIMD_4x32 A1 = SIMD_4x32::load_be(in + 16);
+ SIMD_4x32 A2 = SIMD_4x32::load_be(in + 32);
+ SIMD_4x32 A3 = SIMD_4x32::load_be(in + 48);
+
+ SIMD_4x32::transpose(A0, A1, A2, A3);
+
+ for(size_t i = 0; i != 16; ++i)
+ {
+ A0 ^= SIMD_4x32::splat(RC[i]);
+
+ NOK_SIMD_THETA(A0, A1, A2, A3, K0, K1, K2, K3);
+
+ A1 = A1.rotl<1>();
+ A2 = A2.rotl<5>();
+ A3 = A3.rotl<2>();
+
+ NOK_SIMD_GAMMA(A0, A1, A2, A3);
+
+ A1 = A1.rotr<1>();
+ A2 = A2.rotr<5>();
+ A3 = A3.rotr<2>();
+ }
+
+ A0 ^= SIMD_4x32::splat(RC[16]);
+ NOK_SIMD_THETA(A0, A1, A2, A3, K0, K1, K2, K3);
+
+ SIMD_4x32::transpose(A0, A1, A2, A3);
+
+ A0.store_be(out);
+ A1.store_be(out + 16);
+ A2.store_be(out + 32);
+ A3.store_be(out + 48);
+ }
+
+/*
+* Noekeon Encryption
+*/
+void 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]);
+ const SIMD_4x32 K3 = SIMD_4x32::splat(m_DK[3]);
+
+ SIMD_4x32 A0 = SIMD_4x32::load_be(in );
+ SIMD_4x32 A1 = SIMD_4x32::load_be(in + 16);
+ SIMD_4x32 A2 = SIMD_4x32::load_be(in + 32);
+ SIMD_4x32 A3 = SIMD_4x32::load_be(in + 48);
+
+ SIMD_4x32::transpose(A0, A1, A2, A3);
+
+ for(size_t i = 0; i != 16; ++i)
+ {
+ NOK_SIMD_THETA(A0, A1, A2, A3, K0, K1, K2, K3);
+
+ A0 ^= SIMD_4x32::splat(RC[16-i]);
+
+ A1 = A1.rotl<1>();
+ A2 = A2.rotl<5>();
+ A3 = A3.rotl<2>();
+
+ NOK_SIMD_GAMMA(A0, A1, A2, A3);
+
+ A1 = A1.rotr<1>();
+ A2 = A2.rotr<5>();
+ A3 = A3.rotr<2>();
+ }
+
+ NOK_SIMD_THETA(A0, A1, A2, A3, K0, K1, K2, K3);
+ A0 ^= SIMD_4x32::splat(RC[0]);
+
+ SIMD_4x32::transpose(A0, A1, A2, A3);
+
+ A0.store_be(out);
+ A1.store_be(out + 16);
+ A2.store_be(out + 32);
+ A3.store_be(out + 48);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/block/seed/info.txt b/comm/third_party/botan/src/lib/block/seed/info.txt
new file mode 100644
index 0000000000..dd395eda1a
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/seed/info.txt
@@ -0,0 +1,3 @@
+<defines>
+SEED -> 20131128
+</defines>
diff --git a/comm/third_party/botan/src/lib/block/seed/seed.cpp b/comm/third_party/botan/src/lib/block/seed/seed.cpp
new file mode 100644
index 0000000000..e596c70acb
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/seed/seed.cpp
@@ -0,0 +1,328 @@
+/*
+* SEED
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/seed.h>
+#include <botan/loadstor.h>
+
+namespace Botan {
+
+namespace {
+
+alignas(64) const uint32_t SEED_S0[256] = {
+ 0x2989A1A8, 0x05858184, 0x16C6D2D4, 0x13C3D3D0, 0x14445054, 0x1D0D111C,
+ 0x2C8CA0AC, 0x25052124, 0x1D4D515C, 0x03434340, 0x18081018, 0x1E0E121C,
+ 0x11415150, 0x3CCCF0FC, 0x0ACAC2C8, 0x23436360, 0x28082028, 0x04444044,
+ 0x20002020, 0x1D8D919C, 0x20C0E0E0, 0x22C2E2E0, 0x08C8C0C8, 0x17071314,
+ 0x2585A1A4, 0x0F8F838C, 0x03030300, 0x3B4B7378, 0x3B8BB3B8, 0x13031310,
+ 0x12C2D2D0, 0x2ECEE2EC, 0x30407070, 0x0C8C808C, 0x3F0F333C, 0x2888A0A8,
+ 0x32023230, 0x1DCDD1DC, 0x36C6F2F4, 0x34447074, 0x2CCCE0EC, 0x15859194,
+ 0x0B0B0308, 0x17475354, 0x1C4C505C, 0x1B4B5358, 0x3D8DB1BC, 0x01010100,
+ 0x24042024, 0x1C0C101C, 0x33437370, 0x18889098, 0x10001010, 0x0CCCC0CC,
+ 0x32C2F2F0, 0x19C9D1D8, 0x2C0C202C, 0x27C7E3E4, 0x32427270, 0x03838380,
+ 0x1B8B9398, 0x11C1D1D0, 0x06868284, 0x09C9C1C8, 0x20406060, 0x10405050,
+ 0x2383A3A0, 0x2BCBE3E8, 0x0D0D010C, 0x3686B2B4, 0x1E8E929C, 0x0F4F434C,
+ 0x3787B3B4, 0x1A4A5258, 0x06C6C2C4, 0x38487078, 0x2686A2A4, 0x12021210,
+ 0x2F8FA3AC, 0x15C5D1D4, 0x21416160, 0x03C3C3C0, 0x3484B0B4, 0x01414140,
+ 0x12425250, 0x3D4D717C, 0x0D8D818C, 0x08080008, 0x1F0F131C, 0x19899198,
+ 0x00000000, 0x19091118, 0x04040004, 0x13435350, 0x37C7F3F4, 0x21C1E1E0,
+ 0x3DCDF1FC, 0x36467274, 0x2F0F232C, 0x27072324, 0x3080B0B0, 0x0B8B8388,
+ 0x0E0E020C, 0x2B8BA3A8, 0x2282A2A0, 0x2E4E626C, 0x13839390, 0x0D4D414C,
+ 0x29496168, 0x3C4C707C, 0x09090108, 0x0A0A0208, 0x3F8FB3BC, 0x2FCFE3EC,
+ 0x33C3F3F0, 0x05C5C1C4, 0x07878384, 0x14041014, 0x3ECEF2FC, 0x24446064,
+ 0x1ECED2DC, 0x2E0E222C, 0x0B4B4348, 0x1A0A1218, 0x06060204, 0x21012120,
+ 0x2B4B6368, 0x26466264, 0x02020200, 0x35C5F1F4, 0x12829290, 0x0A8A8288,
+ 0x0C0C000C, 0x3383B3B0, 0x3E4E727C, 0x10C0D0D0, 0x3A4A7278, 0x07474344,
+ 0x16869294, 0x25C5E1E4, 0x26062224, 0x00808080, 0x2D8DA1AC, 0x1FCFD3DC,
+ 0x2181A1A0, 0x30003030, 0x37073334, 0x2E8EA2AC, 0x36063234, 0x15051114,
+ 0x22022220, 0x38083038, 0x34C4F0F4, 0x2787A3A4, 0x05454144, 0x0C4C404C,
+ 0x01818180, 0x29C9E1E8, 0x04848084, 0x17879394, 0x35053134, 0x0BCBC3C8,
+ 0x0ECEC2CC, 0x3C0C303C, 0x31417170, 0x11011110, 0x07C7C3C4, 0x09898188,
+ 0x35457174, 0x3BCBF3F8, 0x1ACAD2D8, 0x38C8F0F8, 0x14849094, 0x19495158,
+ 0x02828280, 0x04C4C0C4, 0x3FCFF3FC, 0x09494148, 0x39093138, 0x27476364,
+ 0x00C0C0C0, 0x0FCFC3CC, 0x17C7D3D4, 0x3888B0B8, 0x0F0F030C, 0x0E8E828C,
+ 0x02424240, 0x23032320, 0x11819190, 0x2C4C606C, 0x1BCBD3D8, 0x2484A0A4,
+ 0x34043034, 0x31C1F1F0, 0x08484048, 0x02C2C2C0, 0x2F4F636C, 0x3D0D313C,
+ 0x2D0D212C, 0x00404040, 0x3E8EB2BC, 0x3E0E323C, 0x3C8CB0BC, 0x01C1C1C0,
+ 0x2A8AA2A8, 0x3A8AB2B8, 0x0E4E424C, 0x15455154, 0x3B0B3338, 0x1CCCD0DC,
+ 0x28486068, 0x3F4F737C, 0x1C8C909C, 0x18C8D0D8, 0x0A4A4248, 0x16465254,
+ 0x37477374, 0x2080A0A0, 0x2DCDE1EC, 0x06464244, 0x3585B1B4, 0x2B0B2328,
+ 0x25456164, 0x3ACAF2F8, 0x23C3E3E0, 0x3989B1B8, 0x3181B1B0, 0x1F8F939C,
+ 0x1E4E525C, 0x39C9F1F8, 0x26C6E2E4, 0x3282B2B0, 0x31013130, 0x2ACAE2E8,
+ 0x2D4D616C, 0x1F4F535C, 0x24C4E0E4, 0x30C0F0F0, 0x0DCDC1CC, 0x08888088,
+ 0x16061214, 0x3A0A3238, 0x18485058, 0x14C4D0D4, 0x22426260, 0x29092128,
+ 0x07070304, 0x33033330, 0x28C8E0E8, 0x1B0B1318, 0x05050104, 0x39497178,
+ 0x10809090, 0x2A4A6268, 0x2A0A2228, 0x1A8A9298 };
+
+alignas(64) const uint32_t SEED_S1[256] = {
+ 0x38380830, 0xE828C8E0, 0x2C2D0D21, 0xA42686A2, 0xCC0FCFC3, 0xDC1ECED2,
+ 0xB03383B3, 0xB83888B0, 0xAC2F8FA3, 0x60204060, 0x54154551, 0xC407C7C3,
+ 0x44044440, 0x6C2F4F63, 0x682B4B63, 0x581B4B53, 0xC003C3C3, 0x60224262,
+ 0x30330333, 0xB43585B1, 0x28290921, 0xA02080A0, 0xE022C2E2, 0xA42787A3,
+ 0xD013C3D3, 0x90118191, 0x10110111, 0x04060602, 0x1C1C0C10, 0xBC3C8CB0,
+ 0x34360632, 0x480B4B43, 0xEC2FCFE3, 0x88088880, 0x6C2C4C60, 0xA82888A0,
+ 0x14170713, 0xC404C4C0, 0x14160612, 0xF434C4F0, 0xC002C2C2, 0x44054541,
+ 0xE021C1E1, 0xD416C6D2, 0x3C3F0F33, 0x3C3D0D31, 0x8C0E8E82, 0x98188890,
+ 0x28280820, 0x4C0E4E42, 0xF436C6F2, 0x3C3E0E32, 0xA42585A1, 0xF839C9F1,
+ 0x0C0D0D01, 0xDC1FCFD3, 0xD818C8D0, 0x282B0B23, 0x64264662, 0x783A4A72,
+ 0x24270723, 0x2C2F0F23, 0xF031C1F1, 0x70324272, 0x40024242, 0xD414C4D0,
+ 0x40014141, 0xC000C0C0, 0x70334373, 0x64274763, 0xAC2C8CA0, 0x880B8B83,
+ 0xF437C7F3, 0xAC2D8DA1, 0x80008080, 0x1C1F0F13, 0xC80ACAC2, 0x2C2C0C20,
+ 0xA82A8AA2, 0x34340430, 0xD012C2D2, 0x080B0B03, 0xEC2ECEE2, 0xE829C9E1,
+ 0x5C1D4D51, 0x94148490, 0x18180810, 0xF838C8F0, 0x54174753, 0xAC2E8EA2,
+ 0x08080800, 0xC405C5C1, 0x10130313, 0xCC0DCDC1, 0x84068682, 0xB83989B1,
+ 0xFC3FCFF3, 0x7C3D4D71, 0xC001C1C1, 0x30310131, 0xF435C5F1, 0x880A8A82,
+ 0x682A4A62, 0xB03181B1, 0xD011C1D1, 0x20200020, 0xD417C7D3, 0x00020202,
+ 0x20220222, 0x04040400, 0x68284860, 0x70314171, 0x04070703, 0xD81BCBD3,
+ 0x9C1D8D91, 0x98198991, 0x60214161, 0xBC3E8EB2, 0xE426C6E2, 0x58194951,
+ 0xDC1DCDD1, 0x50114151, 0x90108090, 0xDC1CCCD0, 0x981A8A92, 0xA02383A3,
+ 0xA82B8BA3, 0xD010C0D0, 0x80018181, 0x0C0F0F03, 0x44074743, 0x181A0A12,
+ 0xE023C3E3, 0xEC2CCCE0, 0x8C0D8D81, 0xBC3F8FB3, 0x94168692, 0x783B4B73,
+ 0x5C1C4C50, 0xA02282A2, 0xA02181A1, 0x60234363, 0x20230323, 0x4C0D4D41,
+ 0xC808C8C0, 0x9C1E8E92, 0x9C1C8C90, 0x383A0A32, 0x0C0C0C00, 0x2C2E0E22,
+ 0xB83A8AB2, 0x6C2E4E62, 0x9C1F8F93, 0x581A4A52, 0xF032C2F2, 0x90128292,
+ 0xF033C3F3, 0x48094941, 0x78384870, 0xCC0CCCC0, 0x14150511, 0xF83BCBF3,
+ 0x70304070, 0x74354571, 0x7C3F4F73, 0x34350531, 0x10100010, 0x00030303,
+ 0x64244460, 0x6C2D4D61, 0xC406C6C2, 0x74344470, 0xD415C5D1, 0xB43484B0,
+ 0xE82ACAE2, 0x08090901, 0x74364672, 0x18190911, 0xFC3ECEF2, 0x40004040,
+ 0x10120212, 0xE020C0E0, 0xBC3D8DB1, 0x04050501, 0xF83ACAF2, 0x00010101,
+ 0xF030C0F0, 0x282A0A22, 0x5C1E4E52, 0xA82989A1, 0x54164652, 0x40034343,
+ 0x84058581, 0x14140410, 0x88098981, 0x981B8B93, 0xB03080B0, 0xE425C5E1,
+ 0x48084840, 0x78394971, 0x94178793, 0xFC3CCCF0, 0x1C1E0E12, 0x80028282,
+ 0x20210121, 0x8C0C8C80, 0x181B0B13, 0x5C1F4F53, 0x74374773, 0x54144450,
+ 0xB03282B2, 0x1C1D0D11, 0x24250521, 0x4C0F4F43, 0x00000000, 0x44064642,
+ 0xEC2DCDE1, 0x58184850, 0x50124252, 0xE82BCBE3, 0x7C3E4E72, 0xD81ACAD2,
+ 0xC809C9C1, 0xFC3DCDF1, 0x30300030, 0x94158591, 0x64254561, 0x3C3C0C30,
+ 0xB43686B2, 0xE424C4E0, 0xB83B8BB3, 0x7C3C4C70, 0x0C0E0E02, 0x50104050,
+ 0x38390931, 0x24260622, 0x30320232, 0x84048480, 0x68294961, 0x90138393,
+ 0x34370733, 0xE427C7E3, 0x24240420, 0xA42484A0, 0xC80BCBC3, 0x50134353,
+ 0x080A0A02, 0x84078783, 0xD819C9D1, 0x4C0C4C40, 0x80038383, 0x8C0F8F83,
+ 0xCC0ECEC2, 0x383B0B33, 0x480A4A42, 0xB43787B3 };
+
+alignas(64) const uint32_t SEED_S2[256] = {
+ 0xA1A82989, 0x81840585, 0xD2D416C6, 0xD3D013C3, 0x50541444, 0x111C1D0D,
+ 0xA0AC2C8C, 0x21242505, 0x515C1D4D, 0x43400343, 0x10181808, 0x121C1E0E,
+ 0x51501141, 0xF0FC3CCC, 0xC2C80ACA, 0x63602343, 0x20282808, 0x40440444,
+ 0x20202000, 0x919C1D8D, 0xE0E020C0, 0xE2E022C2, 0xC0C808C8, 0x13141707,
+ 0xA1A42585, 0x838C0F8F, 0x03000303, 0x73783B4B, 0xB3B83B8B, 0x13101303,
+ 0xD2D012C2, 0xE2EC2ECE, 0x70703040, 0x808C0C8C, 0x333C3F0F, 0xA0A82888,
+ 0x32303202, 0xD1DC1DCD, 0xF2F436C6, 0x70743444, 0xE0EC2CCC, 0x91941585,
+ 0x03080B0B, 0x53541747, 0x505C1C4C, 0x53581B4B, 0xB1BC3D8D, 0x01000101,
+ 0x20242404, 0x101C1C0C, 0x73703343, 0x90981888, 0x10101000, 0xC0CC0CCC,
+ 0xF2F032C2, 0xD1D819C9, 0x202C2C0C, 0xE3E427C7, 0x72703242, 0x83800383,
+ 0x93981B8B, 0xD1D011C1, 0x82840686, 0xC1C809C9, 0x60602040, 0x50501040,
+ 0xA3A02383, 0xE3E82BCB, 0x010C0D0D, 0xB2B43686, 0x929C1E8E, 0x434C0F4F,
+ 0xB3B43787, 0x52581A4A, 0xC2C406C6, 0x70783848, 0xA2A42686, 0x12101202,
+ 0xA3AC2F8F, 0xD1D415C5, 0x61602141, 0xC3C003C3, 0xB0B43484, 0x41400141,
+ 0x52501242, 0x717C3D4D, 0x818C0D8D, 0x00080808, 0x131C1F0F, 0x91981989,
+ 0x00000000, 0x11181909, 0x00040404, 0x53501343, 0xF3F437C7, 0xE1E021C1,
+ 0xF1FC3DCD, 0x72743646, 0x232C2F0F, 0x23242707, 0xB0B03080, 0x83880B8B,
+ 0x020C0E0E, 0xA3A82B8B, 0xA2A02282, 0x626C2E4E, 0x93901383, 0x414C0D4D,
+ 0x61682949, 0x707C3C4C, 0x01080909, 0x02080A0A, 0xB3BC3F8F, 0xE3EC2FCF,
+ 0xF3F033C3, 0xC1C405C5, 0x83840787, 0x10141404, 0xF2FC3ECE, 0x60642444,
+ 0xD2DC1ECE, 0x222C2E0E, 0x43480B4B, 0x12181A0A, 0x02040606, 0x21202101,
+ 0x63682B4B, 0x62642646, 0x02000202, 0xF1F435C5, 0x92901282, 0x82880A8A,
+ 0x000C0C0C, 0xB3B03383, 0x727C3E4E, 0xD0D010C0, 0x72783A4A, 0x43440747,
+ 0x92941686, 0xE1E425C5, 0x22242606, 0x80800080, 0xA1AC2D8D, 0xD3DC1FCF,
+ 0xA1A02181, 0x30303000, 0x33343707, 0xA2AC2E8E, 0x32343606, 0x11141505,
+ 0x22202202, 0x30383808, 0xF0F434C4, 0xA3A42787, 0x41440545, 0x404C0C4C,
+ 0x81800181, 0xE1E829C9, 0x80840484, 0x93941787, 0x31343505, 0xC3C80BCB,
+ 0xC2CC0ECE, 0x303C3C0C, 0x71703141, 0x11101101, 0xC3C407C7, 0x81880989,
+ 0x71743545, 0xF3F83BCB, 0xD2D81ACA, 0xF0F838C8, 0x90941484, 0x51581949,
+ 0x82800282, 0xC0C404C4, 0xF3FC3FCF, 0x41480949, 0x31383909, 0x63642747,
+ 0xC0C000C0, 0xC3CC0FCF, 0xD3D417C7, 0xB0B83888, 0x030C0F0F, 0x828C0E8E,
+ 0x42400242, 0x23202303, 0x91901181, 0x606C2C4C, 0xD3D81BCB, 0xA0A42484,
+ 0x30343404, 0xF1F031C1, 0x40480848, 0xC2C002C2, 0x636C2F4F, 0x313C3D0D,
+ 0x212C2D0D, 0x40400040, 0xB2BC3E8E, 0x323C3E0E, 0xB0BC3C8C, 0xC1C001C1,
+ 0xA2A82A8A, 0xB2B83A8A, 0x424C0E4E, 0x51541545, 0x33383B0B, 0xD0DC1CCC,
+ 0x60682848, 0x737C3F4F, 0x909C1C8C, 0xD0D818C8, 0x42480A4A, 0x52541646,
+ 0x73743747, 0xA0A02080, 0xE1EC2DCD, 0x42440646, 0xB1B43585, 0x23282B0B,
+ 0x61642545, 0xF2F83ACA, 0xE3E023C3, 0xB1B83989, 0xB1B03181, 0x939C1F8F,
+ 0x525C1E4E, 0xF1F839C9, 0xE2E426C6, 0xB2B03282, 0x31303101, 0xE2E82ACA,
+ 0x616C2D4D, 0x535C1F4F, 0xE0E424C4, 0xF0F030C0, 0xC1CC0DCD, 0x80880888,
+ 0x12141606, 0x32383A0A, 0x50581848, 0xD0D414C4, 0x62602242, 0x21282909,
+ 0x03040707, 0x33303303, 0xE0E828C8, 0x13181B0B, 0x01040505, 0x71783949,
+ 0x90901080, 0x62682A4A, 0x22282A0A, 0x92981A8A };
+
+alignas(64) const uint32_t SEED_S3[256] = {
+ 0x08303838, 0xC8E0E828, 0x0D212C2D, 0x86A2A426, 0xCFC3CC0F, 0xCED2DC1E,
+ 0x83B3B033, 0x88B0B838, 0x8FA3AC2F, 0x40606020, 0x45515415, 0xC7C3C407,
+ 0x44404404, 0x4F636C2F, 0x4B63682B, 0x4B53581B, 0xC3C3C003, 0x42626022,
+ 0x03333033, 0x85B1B435, 0x09212829, 0x80A0A020, 0xC2E2E022, 0x87A3A427,
+ 0xC3D3D013, 0x81919011, 0x01111011, 0x06020406, 0x0C101C1C, 0x8CB0BC3C,
+ 0x06323436, 0x4B43480B, 0xCFE3EC2F, 0x88808808, 0x4C606C2C, 0x88A0A828,
+ 0x07131417, 0xC4C0C404, 0x06121416, 0xC4F0F434, 0xC2C2C002, 0x45414405,
+ 0xC1E1E021, 0xC6D2D416, 0x0F333C3F, 0x0D313C3D, 0x8E828C0E, 0x88909818,
+ 0x08202828, 0x4E424C0E, 0xC6F2F436, 0x0E323C3E, 0x85A1A425, 0xC9F1F839,
+ 0x0D010C0D, 0xCFD3DC1F, 0xC8D0D818, 0x0B23282B, 0x46626426, 0x4A72783A,
+ 0x07232427, 0x0F232C2F, 0xC1F1F031, 0x42727032, 0x42424002, 0xC4D0D414,
+ 0x41414001, 0xC0C0C000, 0x43737033, 0x47636427, 0x8CA0AC2C, 0x8B83880B,
+ 0xC7F3F437, 0x8DA1AC2D, 0x80808000, 0x0F131C1F, 0xCAC2C80A, 0x0C202C2C,
+ 0x8AA2A82A, 0x04303434, 0xC2D2D012, 0x0B03080B, 0xCEE2EC2E, 0xC9E1E829,
+ 0x4D515C1D, 0x84909414, 0x08101818, 0xC8F0F838, 0x47535417, 0x8EA2AC2E,
+ 0x08000808, 0xC5C1C405, 0x03131013, 0xCDC1CC0D, 0x86828406, 0x89B1B839,
+ 0xCFF3FC3F, 0x4D717C3D, 0xC1C1C001, 0x01313031, 0xC5F1F435, 0x8A82880A,
+ 0x4A62682A, 0x81B1B031, 0xC1D1D011, 0x00202020, 0xC7D3D417, 0x02020002,
+ 0x02222022, 0x04000404, 0x48606828, 0x41717031, 0x07030407, 0xCBD3D81B,
+ 0x8D919C1D, 0x89919819, 0x41616021, 0x8EB2BC3E, 0xC6E2E426, 0x49515819,
+ 0xCDD1DC1D, 0x41515011, 0x80909010, 0xCCD0DC1C, 0x8A92981A, 0x83A3A023,
+ 0x8BA3A82B, 0xC0D0D010, 0x81818001, 0x0F030C0F, 0x47434407, 0x0A12181A,
+ 0xC3E3E023, 0xCCE0EC2C, 0x8D818C0D, 0x8FB3BC3F, 0x86929416, 0x4B73783B,
+ 0x4C505C1C, 0x82A2A022, 0x81A1A021, 0x43636023, 0x03232023, 0x4D414C0D,
+ 0xC8C0C808, 0x8E929C1E, 0x8C909C1C, 0x0A32383A, 0x0C000C0C, 0x0E222C2E,
+ 0x8AB2B83A, 0x4E626C2E, 0x8F939C1F, 0x4A52581A, 0xC2F2F032, 0x82929012,
+ 0xC3F3F033, 0x49414809, 0x48707838, 0xCCC0CC0C, 0x05111415, 0xCBF3F83B,
+ 0x40707030, 0x45717435, 0x4F737C3F, 0x05313435, 0x00101010, 0x03030003,
+ 0x44606424, 0x4D616C2D, 0xC6C2C406, 0x44707434, 0xC5D1D415, 0x84B0B434,
+ 0xCAE2E82A, 0x09010809, 0x46727436, 0x09111819, 0xCEF2FC3E, 0x40404000,
+ 0x02121012, 0xC0E0E020, 0x8DB1BC3D, 0x05010405, 0xCAF2F83A, 0x01010001,
+ 0xC0F0F030, 0x0A22282A, 0x4E525C1E, 0x89A1A829, 0x46525416, 0x43434003,
+ 0x85818405, 0x04101414, 0x89818809, 0x8B93981B, 0x80B0B030, 0xC5E1E425,
+ 0x48404808, 0x49717839, 0x87939417, 0xCCF0FC3C, 0x0E121C1E, 0x82828002,
+ 0x01212021, 0x8C808C0C, 0x0B13181B, 0x4F535C1F, 0x47737437, 0x44505414,
+ 0x82B2B032, 0x0D111C1D, 0x05212425, 0x4F434C0F, 0x00000000, 0x46424406,
+ 0xCDE1EC2D, 0x48505818, 0x42525012, 0xCBE3E82B, 0x4E727C3E, 0xCAD2D81A,
+ 0xC9C1C809, 0xCDF1FC3D, 0x00303030, 0x85919415, 0x45616425, 0x0C303C3C,
+ 0x86B2B436, 0xC4E0E424, 0x8BB3B83B, 0x4C707C3C, 0x0E020C0E, 0x40505010,
+ 0x09313839, 0x06222426, 0x02323032, 0x84808404, 0x49616829, 0x83939013,
+ 0x07333437, 0xC7E3E427, 0x04202424, 0x84A0A424, 0xCBC3C80B, 0x43535013,
+ 0x0A02080A, 0x87838407, 0xC9D1D819, 0x4C404C0C, 0x83838003, 0x8F838C0F,
+ 0xCEC2CC0E, 0x0B33383B, 0x4A42480A, 0x87B3B437 };
+
+/*
+* SEED G Function
+*/
+inline uint32_t SEED_G(uint32_t X)
+ {
+ return (SEED_S0[get_byte(3, X)] ^ SEED_S1[get_byte(2, X)] ^
+ SEED_S2[get_byte(1, X)] ^ SEED_S3[get_byte(0, X)]);
+ }
+
+}
+
+/*
+* SEED Encryption
+*/
+void SEED::encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+ {
+ verify_key_set(m_K.empty() == false);
+
+ for(size_t i = 0; i != blocks; ++i)
+ {
+ uint32_t B0 = load_be<uint32_t>(in, 0);
+ uint32_t B1 = load_be<uint32_t>(in, 1);
+ uint32_t B2 = load_be<uint32_t>(in, 2);
+ uint32_t B3 = load_be<uint32_t>(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]);
+ T0 = SEED_G(T1 + T0);
+ T1 = SEED_G(T1 + T0);
+ B1 ^= T1;
+ B0 ^= T0 + T1;
+
+ T0 = B0 ^ m_K[2*j+2];
+ T1 = SEED_G(B0 ^ B1 ^ m_K[2*j+3]);
+ T0 = SEED_G(T1 + T0);
+ T1 = SEED_G(T1 + T0);
+ B3 ^= T1;
+ B2 ^= T0 + T1;
+ }
+
+ store_be(out, B2, B3, B0, B1);
+
+ in += BLOCK_SIZE;
+ out += BLOCK_SIZE;
+ }
+ }
+
+/*
+* SEED Decryption
+*/
+void SEED::decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+ {
+ verify_key_set(m_K.empty() == false);
+
+ for(size_t i = 0; i != blocks; ++i)
+ {
+ uint32_t B0 = load_be<uint32_t>(in, 0);
+ uint32_t B1 = load_be<uint32_t>(in, 1);
+ uint32_t B2 = load_be<uint32_t>(in, 2);
+ uint32_t B3 = load_be<uint32_t>(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]);
+ T0 = SEED_G(T1 + T0);
+ T1 = SEED_G(T1 + T0);
+ B1 ^= T1;
+ B0 ^= T0 + T1;
+
+ T0 = B0 ^ m_K[28-2*j];
+ T1 = SEED_G(B0 ^ B1 ^ m_K[29-2*j]);
+ T0 = SEED_G(T1 + T0);
+ T1 = SEED_G(T1 + T0);
+ B3 ^= T1;
+ B2 ^= T0 + T1;
+ }
+
+ store_be(out, B2, B3, B0, B1);
+
+ in += BLOCK_SIZE;
+ out += BLOCK_SIZE;
+ }
+ }
+
+/*
+* SEED Key Schedule
+*/
+void SEED::key_schedule(const uint8_t key[], size_t)
+ {
+ const uint32_t RC[16] = {
+ 0x9E3779B9, 0x3C6EF373, 0x78DDE6E6, 0xF1BBCDCC,
+ 0xE3779B99, 0xC6EF3733, 0x8DDE6E67, 0x1BBCDCCF,
+ 0x3779B99E, 0x6EF3733C, 0xDDE6E678, 0xBBCDCCF1,
+ 0x779B99E3, 0xEF3733C6, 0xDE6E678D, 0xBCDCCF1B
+ };
+
+ secure_vector<uint32_t> WK(4);
+
+ for(size_t i = 0; i != 4; ++i)
+ WK[i] = load_be<uint32_t>(key, i);
+
+ m_K.resize(32);
+
+ for(size_t i = 0; i != 16; i += 2)
+ {
+ m_K[2*i ] = SEED_G(WK[0] + WK[2] - RC[i]);
+ m_K[2*i+1] = SEED_G(WK[1] - WK[3] + RC[i]) ^ m_K[2*i];
+
+ uint32_t T = (WK[0] & 0xFF) << 24;
+ WK[0] = (WK[0] >> 8) | (get_byte(3, WK[1]) << 24);
+ WK[1] = (WK[1] >> 8) | T;
+
+ m_K[2*i+2] = SEED_G(WK[0] + WK[2] - RC[i+1]);
+ m_K[2*i+3] = SEED_G(WK[1] - WK[3] + RC[i+1]) ^ m_K[2*i+2];
+
+ T = get_byte(0, WK[3]);
+ WK[3] = (WK[3] << 8) | get_byte(0, WK[2]);
+ WK[2] = (WK[2] << 8) | T;
+ }
+ }
+
+void SEED::clear()
+ {
+ zap(m_K);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/block/seed/seed.h b/comm/third_party/botan/src/lib/block/seed/seed.h
new file mode 100644
index 0000000000..a616243135
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/seed/seed.h
@@ -0,0 +1,37 @@
+/*
+* SEED
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_SEED_H_
+#define BOTAN_SEED_H_
+
+#include <botan/block_cipher.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(seed.h)
+
+namespace Botan {
+
+/**
+* SEED, a Korean block cipher
+*/
+class BOTAN_PUBLIC_API(2,0) SEED final : public Block_Cipher_Fixed_Params<16, 16>
+ {
+ public:
+ void encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+ void decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+
+ void clear() override;
+ std::string name() const override { return "SEED"; }
+ BlockCipher* clone() const override { return new SEED; }
+ private:
+ void key_schedule(const uint8_t[], size_t) override;
+
+ secure_vector<uint32_t> m_K;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/block/serpent/info.txt b/comm/third_party/botan/src/lib/block/serpent/info.txt
new file mode 100644
index 0000000000..89b860ce4f
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/serpent/info.txt
@@ -0,0 +1,11 @@
+<defines>
+SERPENT -> 20131128
+</defines>
+
+<header:public>
+serpent.h
+</header:public>
+
+<header:internal>
+serpent_sbox.h
+</header:internal>
diff --git a/comm/third_party/botan/src/lib/block/serpent/serpent.cpp b/comm/third_party/botan/src/lib/block/serpent/serpent.cpp
new file mode 100644
index 0000000000..ff37a177c7
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/serpent/serpent.cpp
@@ -0,0 +1,299 @@
+/*
+* Serpent
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/serpent.h>
+#include <botan/loadstor.h>
+#include <botan/rotate.h>
+#include <botan/internal/serpent_sbox.h>
+
+#if defined(BOTAN_HAS_SERPENT_SIMD) || defined(BOTAN_HAS_SERPENT_AVX2)
+ #include <botan/cpuid.h>
+#endif
+
+namespace Botan {
+
+namespace {
+
+/*
+* Serpent's Linear Transform
+*/
+inline void transform(uint32_t& B0, uint32_t& B1, uint32_t& B2, uint32_t& B3)
+ {
+ B0 = rotl<13>(B0); B2 = rotl<3>(B2);
+ B1 ^= B0 ^ B2; B3 ^= B2 ^ (B0 << 3);
+ B1 = rotl<1>(B1); B3 = rotl<7>(B3);
+ B0 ^= B1 ^ B3; B2 ^= B3 ^ (B1 << 7);
+ B0 = rotl<5>(B0); B2 = rotl<22>(B2);
+ }
+
+/*
+* Serpent's Inverse Linear Transform
+*/
+inline void i_transform(uint32_t& B0, uint32_t& B1, uint32_t& B2, uint32_t& B3)
+ {
+ B2 = rotr<22>(B2); B0 = rotr<5>(B0);
+ B2 ^= B3 ^ (B1 << 7); B0 ^= B1 ^ B3;
+ B3 = rotr<7>(B3); B1 = rotr<1>(B1);
+ B3 ^= B2 ^ (B0 << 3); B1 ^= B0 ^ B2;
+ B2 = rotr<3>(B2); B0 = rotr<13>(B0);
+ }
+
+}
+
+/*
+* XOR a key block with a data block
+*/
+#define key_xor(round, B0, B1, B2, B3) \
+ B0 ^= m_round_key[4*round ]; \
+ B1 ^= m_round_key[4*round+1]; \
+ B2 ^= m_round_key[4*round+2]; \
+ B3 ^= m_round_key[4*round+3];
+
+/*
+* Serpent Encryption
+*/
+void Serpent::encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+ {
+ verify_key_set(m_round_key.empty() == false);
+
+#if defined(BOTAN_HAS_SERPENT_AVX2)
+ if(CPUID::has_avx2())
+ {
+ while(blocks >= 8)
+ {
+ avx2_encrypt_8(in, out);
+ in += 8 * BLOCK_SIZE;
+ out += 8 * BLOCK_SIZE;
+ blocks -= 8;
+ }
+ }
+#endif
+
+#if defined(BOTAN_HAS_SERPENT_SIMD)
+ if(CPUID::has_simd_32())
+ {
+ while(blocks >= 4)
+ {
+ simd_encrypt_4(in, out);
+ in += 4 * BLOCK_SIZE;
+ out += 4 * BLOCK_SIZE;
+ blocks -= 4;
+ }
+ }
+#endif
+
+ BOTAN_PARALLEL_SIMD_FOR(size_t i = 0; i < blocks; ++i)
+ {
+ uint32_t B0, B1, B2, B3;
+ load_le(in + 16*i, B0, B1, B2, B3);
+
+ key_xor( 0,B0,B1,B2,B3); SBoxE0(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+ key_xor( 1,B0,B1,B2,B3); SBoxE1(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+ key_xor( 2,B0,B1,B2,B3); SBoxE2(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+ key_xor( 3,B0,B1,B2,B3); SBoxE3(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+ key_xor( 4,B0,B1,B2,B3); SBoxE4(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+ key_xor( 5,B0,B1,B2,B3); SBoxE5(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+ key_xor( 6,B0,B1,B2,B3); SBoxE6(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+ key_xor( 7,B0,B1,B2,B3); SBoxE7(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+ key_xor( 8,B0,B1,B2,B3); SBoxE0(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+ key_xor( 9,B0,B1,B2,B3); SBoxE1(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+ key_xor(10,B0,B1,B2,B3); SBoxE2(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+ key_xor(11,B0,B1,B2,B3); SBoxE3(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+ key_xor(12,B0,B1,B2,B3); SBoxE4(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+ key_xor(13,B0,B1,B2,B3); SBoxE5(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+ key_xor(14,B0,B1,B2,B3); SBoxE6(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+ key_xor(15,B0,B1,B2,B3); SBoxE7(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+ key_xor(16,B0,B1,B2,B3); SBoxE0(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+ key_xor(17,B0,B1,B2,B3); SBoxE1(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+ key_xor(18,B0,B1,B2,B3); SBoxE2(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+ key_xor(19,B0,B1,B2,B3); SBoxE3(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+ key_xor(20,B0,B1,B2,B3); SBoxE4(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+ key_xor(21,B0,B1,B2,B3); SBoxE5(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+ key_xor(22,B0,B1,B2,B3); SBoxE6(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+ key_xor(23,B0,B1,B2,B3); SBoxE7(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+ key_xor(24,B0,B1,B2,B3); SBoxE0(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+ key_xor(25,B0,B1,B2,B3); SBoxE1(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+ key_xor(26,B0,B1,B2,B3); SBoxE2(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+ key_xor(27,B0,B1,B2,B3); SBoxE3(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+ key_xor(28,B0,B1,B2,B3); SBoxE4(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+ key_xor(29,B0,B1,B2,B3); SBoxE5(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+ key_xor(30,B0,B1,B2,B3); SBoxE6(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+ key_xor(31,B0,B1,B2,B3); SBoxE7(B0,B1,B2,B3); key_xor(32,B0,B1,B2,B3);
+
+ store_le(out + 16*i, B0, B1, B2, B3);
+ }
+ }
+
+/*
+* Serpent Decryption
+*/
+void Serpent::decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+ {
+ verify_key_set(m_round_key.empty() == false);
+
+#if defined(BOTAN_HAS_SERPENT_AVX2)
+ if(CPUID::has_avx2())
+ {
+ while(blocks >= 8)
+ {
+ avx2_decrypt_8(in, out);
+ in += 8 * BLOCK_SIZE;
+ out += 8 * BLOCK_SIZE;
+ blocks -= 8;
+ }
+ }
+#endif
+
+#if defined(BOTAN_HAS_SERPENT_SIMD)
+ if(CPUID::has_simd_32())
+ {
+ while(blocks >= 4)
+ {
+ simd_decrypt_4(in, out);
+ in += 4 * BLOCK_SIZE;
+ out += 4 * BLOCK_SIZE;
+ blocks -= 4;
+ }
+ }
+#endif
+
+ BOTAN_PARALLEL_SIMD_FOR(size_t i = 0; i < blocks; ++i)
+ {
+ uint32_t B0, B1, B2, B3;
+ load_le(in + 16*i, B0, B1, B2, B3);
+
+ key_xor(32,B0,B1,B2,B3); SBoxD7(B0,B1,B2,B3); key_xor(31,B0,B1,B2,B3);
+ i_transform(B0,B1,B2,B3); SBoxD6(B0,B1,B2,B3); key_xor(30,B0,B1,B2,B3);
+ i_transform(B0,B1,B2,B3); SBoxD5(B0,B1,B2,B3); key_xor(29,B0,B1,B2,B3);
+ i_transform(B0,B1,B2,B3); SBoxD4(B0,B1,B2,B3); key_xor(28,B0,B1,B2,B3);
+ i_transform(B0,B1,B2,B3); SBoxD3(B0,B1,B2,B3); key_xor(27,B0,B1,B2,B3);
+ i_transform(B0,B1,B2,B3); SBoxD2(B0,B1,B2,B3); key_xor(26,B0,B1,B2,B3);
+ i_transform(B0,B1,B2,B3); SBoxD1(B0,B1,B2,B3); key_xor(25,B0,B1,B2,B3);
+ i_transform(B0,B1,B2,B3); SBoxD0(B0,B1,B2,B3); key_xor(24,B0,B1,B2,B3);
+ i_transform(B0,B1,B2,B3); SBoxD7(B0,B1,B2,B3); key_xor(23,B0,B1,B2,B3);
+ i_transform(B0,B1,B2,B3); SBoxD6(B0,B1,B2,B3); key_xor(22,B0,B1,B2,B3);
+ i_transform(B0,B1,B2,B3); SBoxD5(B0,B1,B2,B3); key_xor(21,B0,B1,B2,B3);
+ i_transform(B0,B1,B2,B3); SBoxD4(B0,B1,B2,B3); key_xor(20,B0,B1,B2,B3);
+ i_transform(B0,B1,B2,B3); SBoxD3(B0,B1,B2,B3); key_xor(19,B0,B1,B2,B3);
+ i_transform(B0,B1,B2,B3); SBoxD2(B0,B1,B2,B3); key_xor(18,B0,B1,B2,B3);
+ i_transform(B0,B1,B2,B3); SBoxD1(B0,B1,B2,B3); key_xor(17,B0,B1,B2,B3);
+ i_transform(B0,B1,B2,B3); SBoxD0(B0,B1,B2,B3); key_xor(16,B0,B1,B2,B3);
+ i_transform(B0,B1,B2,B3); SBoxD7(B0,B1,B2,B3); key_xor(15,B0,B1,B2,B3);
+ i_transform(B0,B1,B2,B3); SBoxD6(B0,B1,B2,B3); key_xor(14,B0,B1,B2,B3);
+ i_transform(B0,B1,B2,B3); SBoxD5(B0,B1,B2,B3); key_xor(13,B0,B1,B2,B3);
+ i_transform(B0,B1,B2,B3); SBoxD4(B0,B1,B2,B3); key_xor(12,B0,B1,B2,B3);
+ i_transform(B0,B1,B2,B3); SBoxD3(B0,B1,B2,B3); key_xor(11,B0,B1,B2,B3);
+ i_transform(B0,B1,B2,B3); SBoxD2(B0,B1,B2,B3); key_xor(10,B0,B1,B2,B3);
+ i_transform(B0,B1,B2,B3); SBoxD1(B0,B1,B2,B3); key_xor( 9,B0,B1,B2,B3);
+ i_transform(B0,B1,B2,B3); SBoxD0(B0,B1,B2,B3); key_xor( 8,B0,B1,B2,B3);
+ i_transform(B0,B1,B2,B3); SBoxD7(B0,B1,B2,B3); key_xor( 7,B0,B1,B2,B3);
+ i_transform(B0,B1,B2,B3); SBoxD6(B0,B1,B2,B3); key_xor( 6,B0,B1,B2,B3);
+ i_transform(B0,B1,B2,B3); SBoxD5(B0,B1,B2,B3); key_xor( 5,B0,B1,B2,B3);
+ i_transform(B0,B1,B2,B3); SBoxD4(B0,B1,B2,B3); key_xor( 4,B0,B1,B2,B3);
+ i_transform(B0,B1,B2,B3); SBoxD3(B0,B1,B2,B3); key_xor( 3,B0,B1,B2,B3);
+ i_transform(B0,B1,B2,B3); SBoxD2(B0,B1,B2,B3); key_xor( 2,B0,B1,B2,B3);
+ i_transform(B0,B1,B2,B3); SBoxD1(B0,B1,B2,B3); key_xor( 1,B0,B1,B2,B3);
+ i_transform(B0,B1,B2,B3); SBoxD0(B0,B1,B2,B3); key_xor( 0,B0,B1,B2,B3);
+
+ store_le(out + 16*i, B0, B1, B2, B3);
+ }
+ }
+
+#undef key_xor
+#undef transform
+#undef i_transform
+
+/*
+* Serpent Key Schedule
+*/
+void Serpent::key_schedule(const uint8_t key[], size_t length)
+ {
+ const uint32_t PHI = 0x9E3779B9;
+
+ secure_vector<uint32_t> W(140);
+ for(size_t i = 0; i != length / 4; ++i)
+ W[i] = load_le<uint32_t>(key, i);
+
+ W[length / 4] |= uint32_t(1) << ((length%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);
+ W[i] = rotl<11>(wi);
+ }
+
+ SBoxE0(W[ 20],W[ 21],W[ 22],W[ 23]);
+ SBoxE0(W[ 52],W[ 53],W[ 54],W[ 55]);
+ SBoxE0(W[ 84],W[ 85],W[ 86],W[ 87]);
+ SBoxE0(W[116],W[117],W[118],W[119]);
+
+ SBoxE1(W[ 16],W[ 17],W[ 18],W[ 19]);
+ SBoxE1(W[ 48],W[ 49],W[ 50],W[ 51]);
+ SBoxE1(W[ 80],W[ 81],W[ 82],W[ 83]);
+ SBoxE1(W[112],W[113],W[114],W[115]);
+
+ SBoxE2(W[ 12],W[ 13],W[ 14],W[ 15]);
+ SBoxE2(W[ 44],W[ 45],W[ 46],W[ 47]);
+ SBoxE2(W[ 76],W[ 77],W[ 78],W[ 79]);
+ SBoxE2(W[108],W[109],W[110],W[111]);
+
+ SBoxE3(W[ 8],W[ 9],W[ 10],W[ 11]);
+ SBoxE3(W[ 40],W[ 41],W[ 42],W[ 43]);
+ SBoxE3(W[ 72],W[ 73],W[ 74],W[ 75]);
+ SBoxE3(W[104],W[105],W[106],W[107]);
+ SBoxE3(W[136],W[137],W[138],W[139]);
+
+ SBoxE4(W[ 36],W[ 37],W[ 38],W[ 39]);
+ SBoxE4(W[ 68],W[ 69],W[ 70],W[ 71]);
+ SBoxE4(W[100],W[101],W[102],W[103]);
+ SBoxE4(W[132],W[133],W[134],W[135]);
+
+ SBoxE5(W[ 32],W[ 33],W[ 34],W[ 35]);
+ SBoxE5(W[ 64],W[ 65],W[ 66],W[ 67]);
+ SBoxE5(W[ 96],W[ 97],W[ 98],W[ 99]);
+ SBoxE5(W[128],W[129],W[130],W[131]);
+
+ SBoxE6(W[ 28],W[ 29],W[ 30],W[ 31]);
+ SBoxE6(W[ 60],W[ 61],W[ 62],W[ 63]);
+ SBoxE6(W[ 92],W[ 93],W[ 94],W[ 95]);
+ SBoxE6(W[124],W[125],W[126],W[127]);
+
+ SBoxE7(W[ 24],W[ 25],W[ 26],W[ 27]);
+ SBoxE7(W[ 56],W[ 57],W[ 58],W[ 59]);
+ SBoxE7(W[ 88],W[ 89],W[ 90],W[ 91]);
+ SBoxE7(W[120],W[121],W[122],W[123]);
+
+ m_round_key.assign(W.begin() + 8, W.end());
+ }
+
+void Serpent::clear()
+ {
+ zap(m_round_key);
+ }
+
+std::string Serpent::provider() const
+ {
+#if defined(BOTAN_HAS_SERPENT_AVX2)
+ if(CPUID::has_avx2())
+ {
+ return "avx2";
+ }
+#endif
+
+#if defined(BOTAN_HAS_SERPENT_SIMD)
+ if(CPUID::has_simd_32())
+ {
+ return "simd";
+ }
+#endif
+
+ return "base";
+ }
+
+#undef key_xor
+
+}
diff --git a/comm/third_party/botan/src/lib/block/serpent/serpent.h b/comm/third_party/botan/src/lib/block/serpent/serpent.h
new file mode 100644
index 0000000000..64eb8a8b04
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/serpent/serpent.h
@@ -0,0 +1,53 @@
+/*
+* Serpent
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_SERPENT_H_
+#define BOTAN_SERPENT_H_
+
+#include <botan/block_cipher.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(serpent.h)
+
+namespace Botan {
+
+/**
+* Serpent is the most conservative of the AES finalists
+* https://www.cl.cam.ac.uk/~rja14/serpent.html
+*/
+class BOTAN_PUBLIC_API(2,0) Serpent final : public Block_Cipher_Fixed_Params<16, 16, 32, 8>
+ {
+ public:
+ void encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+ 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 "Serpent"; }
+ BlockCipher* clone() const override { return new Serpent; }
+
+ size_t parallelism() const override { return 4; }
+
+ private:
+
+#if defined(BOTAN_HAS_SERPENT_SIMD)
+ void simd_encrypt_4(const uint8_t in[64], uint8_t out[64]) const;
+ void simd_decrypt_4(const uint8_t in[64], uint8_t out[64]) const;
+#endif
+
+#if defined(BOTAN_HAS_SERPENT_AVX2)
+ void avx2_encrypt_8(const uint8_t in[64], uint8_t out[64]) const;
+ void avx2_decrypt_8(const uint8_t in[64], uint8_t out[64]) const;
+#endif
+
+ void key_schedule(const uint8_t key[], size_t length) override;
+
+ secure_vector<uint32_t> m_round_key;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/block/serpent/serpent_avx2/info.txt b/comm/third_party/botan/src/lib/block/serpent/serpent_avx2/info.txt
new file mode 100644
index 0000000000..b0fbfb334e
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/serpent/serpent_avx2/info.txt
@@ -0,0 +1,17 @@
+<defines>
+SERPENT_AVX2 -> 20180824
+</defines>
+
+<isa>
+avx2
+</isa>
+
+<requires>
+simd_avx2
+</requires>
+
+# We must exclude MSVC due to #2120
+<cc>
+gcc
+clang
+</cc>
diff --git a/comm/third_party/botan/src/lib/block/serpent/serpent_avx2/serpent_avx2.cpp b/comm/third_party/botan/src/lib/block/serpent/serpent_avx2/serpent_avx2.cpp
new file mode 100644
index 0000000000..0db332035d
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/serpent/serpent_avx2/serpent_avx2.cpp
@@ -0,0 +1,169 @@
+/*
+* (C) 2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/serpent.h>
+#include <botan/internal/serpent_sbox.h>
+#include <botan/internal/simd_avx2.h>
+
+namespace Botan {
+
+
+#define key_xor(round, B0, B1, B2, B3) \
+ do { \
+ B0 ^= SIMD_8x32::splat(m_round_key[4*round ]); \
+ B1 ^= SIMD_8x32::splat(m_round_key[4*round+1]); \
+ B2 ^= SIMD_8x32::splat(m_round_key[4*round+2]); \
+ B3 ^= SIMD_8x32::splat(m_round_key[4*round+3]); \
+ } while(0)
+
+/*
+* Serpent's linear transformations
+*/
+#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)
+
+BOTAN_FUNC_ISA("avx2")
+void Serpent::avx2_encrypt_8(const uint8_t in[128], uint8_t out[128]) const
+ {
+ SIMD_8x32::reset_registers();
+
+ SIMD_8x32 B0 = SIMD_8x32::load_le(in);
+ SIMD_8x32 B1 = SIMD_8x32::load_le(in + 32);
+ SIMD_8x32 B2 = SIMD_8x32::load_le(in + 64);
+ SIMD_8x32 B3 = SIMD_8x32::load_le(in + 96);
+
+ SIMD_8x32::transpose(B0, B1, B2, B3);
+
+ key_xor( 0,B0,B1,B2,B3); SBoxE0(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+ key_xor( 1,B0,B1,B2,B3); SBoxE1(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+ key_xor( 2,B0,B1,B2,B3); SBoxE2(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+ key_xor( 3,B0,B1,B2,B3); SBoxE3(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+ key_xor( 4,B0,B1,B2,B3); SBoxE4(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+ key_xor( 5,B0,B1,B2,B3); SBoxE5(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+ key_xor( 6,B0,B1,B2,B3); SBoxE6(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+ key_xor( 7,B0,B1,B2,B3); SBoxE7(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+ key_xor( 8,B0,B1,B2,B3); SBoxE0(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+ key_xor( 9,B0,B1,B2,B3); SBoxE1(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+ key_xor(10,B0,B1,B2,B3); SBoxE2(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+ key_xor(11,B0,B1,B2,B3); SBoxE3(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+ key_xor(12,B0,B1,B2,B3); SBoxE4(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+ key_xor(13,B0,B1,B2,B3); SBoxE5(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+ key_xor(14,B0,B1,B2,B3); SBoxE6(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+ key_xor(15,B0,B1,B2,B3); SBoxE7(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+ key_xor(16,B0,B1,B2,B3); SBoxE0(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+ key_xor(17,B0,B1,B2,B3); SBoxE1(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+ key_xor(18,B0,B1,B2,B3); SBoxE2(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+ key_xor(19,B0,B1,B2,B3); SBoxE3(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+ key_xor(20,B0,B1,B2,B3); SBoxE4(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+ key_xor(21,B0,B1,B2,B3); SBoxE5(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+ key_xor(22,B0,B1,B2,B3); SBoxE6(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+ key_xor(23,B0,B1,B2,B3); SBoxE7(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+ key_xor(24,B0,B1,B2,B3); SBoxE0(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+ key_xor(25,B0,B1,B2,B3); SBoxE1(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+ key_xor(26,B0,B1,B2,B3); SBoxE2(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+ key_xor(27,B0,B1,B2,B3); SBoxE3(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+ key_xor(28,B0,B1,B2,B3); SBoxE4(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+ key_xor(29,B0,B1,B2,B3); SBoxE5(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+ key_xor(30,B0,B1,B2,B3); SBoxE6(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+ key_xor(31,B0,B1,B2,B3); SBoxE7(B0,B1,B2,B3); key_xor(32,B0,B1,B2,B3);
+
+ SIMD_8x32::transpose(B0, B1, B2, B3);
+ B0.store_le(out);
+ B1.store_le(out + 32);
+ B2.store_le(out + 64);
+ B3.store_le(out + 96);
+
+ SIMD_8x32::zero_registers();
+ }
+
+BOTAN_FUNC_ISA("avx2")
+void Serpent::avx2_decrypt_8(const uint8_t in[128], uint8_t out[128]) const
+ {
+ SIMD_8x32::reset_registers();
+
+ SIMD_8x32 B0 = SIMD_8x32::load_le(in);
+ SIMD_8x32 B1 = SIMD_8x32::load_le(in + 32);
+ SIMD_8x32 B2 = SIMD_8x32::load_le(in + 64);
+ SIMD_8x32 B3 = SIMD_8x32::load_le(in + 96);
+
+ SIMD_8x32::transpose(B0, B1, B2, B3);
+
+ key_xor(32,B0,B1,B2,B3); SBoxD7(B0,B1,B2,B3); key_xor(31,B0,B1,B2,B3);
+ i_transform(B0,B1,B2,B3); SBoxD6(B0,B1,B2,B3); key_xor(30,B0,B1,B2,B3);
+ i_transform(B0,B1,B2,B3); SBoxD5(B0,B1,B2,B3); key_xor(29,B0,B1,B2,B3);
+ i_transform(B0,B1,B2,B3); SBoxD4(B0,B1,B2,B3); key_xor(28,B0,B1,B2,B3);
+ i_transform(B0,B1,B2,B3); SBoxD3(B0,B1,B2,B3); key_xor(27,B0,B1,B2,B3);
+ i_transform(B0,B1,B2,B3); SBoxD2(B0,B1,B2,B3); key_xor(26,B0,B1,B2,B3);
+ i_transform(B0,B1,B2,B3); SBoxD1(B0,B1,B2,B3); key_xor(25,B0,B1,B2,B3);
+ i_transform(B0,B1,B2,B3); SBoxD0(B0,B1,B2,B3); key_xor(24,B0,B1,B2,B3);
+
+ i_transform(B0,B1,B2,B3); SBoxD7(B0,B1,B2,B3); key_xor(23,B0,B1,B2,B3);
+ i_transform(B0,B1,B2,B3); SBoxD6(B0,B1,B2,B3); key_xor(22,B0,B1,B2,B3);
+ i_transform(B0,B1,B2,B3); SBoxD5(B0,B1,B2,B3); key_xor(21,B0,B1,B2,B3);
+ i_transform(B0,B1,B2,B3); SBoxD4(B0,B1,B2,B3); key_xor(20,B0,B1,B2,B3);
+ i_transform(B0,B1,B2,B3); SBoxD3(B0,B1,B2,B3); key_xor(19,B0,B1,B2,B3);
+ i_transform(B0,B1,B2,B3); SBoxD2(B0,B1,B2,B3); key_xor(18,B0,B1,B2,B3);
+ i_transform(B0,B1,B2,B3); SBoxD1(B0,B1,B2,B3); key_xor(17,B0,B1,B2,B3);
+ i_transform(B0,B1,B2,B3); SBoxD0(B0,B1,B2,B3); key_xor(16,B0,B1,B2,B3);
+
+ i_transform(B0,B1,B2,B3); SBoxD7(B0,B1,B2,B3); key_xor(15,B0,B1,B2,B3);
+ i_transform(B0,B1,B2,B3); SBoxD6(B0,B1,B2,B3); key_xor(14,B0,B1,B2,B3);
+ i_transform(B0,B1,B2,B3); SBoxD5(B0,B1,B2,B3); key_xor(13,B0,B1,B2,B3);
+ i_transform(B0,B1,B2,B3); SBoxD4(B0,B1,B2,B3); key_xor(12,B0,B1,B2,B3);
+ i_transform(B0,B1,B2,B3); SBoxD3(B0,B1,B2,B3); key_xor(11,B0,B1,B2,B3);
+ i_transform(B0,B1,B2,B3); SBoxD2(B0,B1,B2,B3); key_xor(10,B0,B1,B2,B3);
+ i_transform(B0,B1,B2,B3); SBoxD1(B0,B1,B2,B3); key_xor( 9,B0,B1,B2,B3);
+ i_transform(B0,B1,B2,B3); SBoxD0(B0,B1,B2,B3); key_xor( 8,B0,B1,B2,B3);
+
+ i_transform(B0,B1,B2,B3); SBoxD7(B0,B1,B2,B3); key_xor( 7,B0,B1,B2,B3);
+ i_transform(B0,B1,B2,B3); SBoxD6(B0,B1,B2,B3); key_xor( 6,B0,B1,B2,B3);
+ i_transform(B0,B1,B2,B3); SBoxD5(B0,B1,B2,B3); key_xor( 5,B0,B1,B2,B3);
+ i_transform(B0,B1,B2,B3); SBoxD4(B0,B1,B2,B3); key_xor( 4,B0,B1,B2,B3);
+ i_transform(B0,B1,B2,B3); SBoxD3(B0,B1,B2,B3); key_xor( 3,B0,B1,B2,B3);
+ i_transform(B0,B1,B2,B3); SBoxD2(B0,B1,B2,B3); key_xor( 2,B0,B1,B2,B3);
+ i_transform(B0,B1,B2,B3); SBoxD1(B0,B1,B2,B3); key_xor( 1,B0,B1,B2,B3);
+ i_transform(B0,B1,B2,B3); SBoxD0(B0,B1,B2,B3); key_xor( 0,B0,B1,B2,B3);
+
+ SIMD_8x32::transpose(B0, B1, B2, B3);
+
+ B0.store_le(out);
+ B1.store_le(out + 32);
+ B2.store_le(out + 64);
+ B3.store_le(out + 96);
+
+ SIMD_8x32::zero_registers();
+ }
+
+#undef key_xor
+#undef transform
+#undef i_transform
+
+}
diff --git a/comm/third_party/botan/src/lib/block/serpent/serpent_sbox.h b/comm/third_party/botan/src/lib/block/serpent/serpent_sbox.h
new file mode 100644
index 0000000000..31471e7247
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/serpent/serpent_sbox.h
@@ -0,0 +1,446 @@
+/*
+* Serpent SBox Expressions
+* (C) 1999-2007,2013 Jack Lloyd
+*
+* The sbox expressions used here were discovered by Dag Arne Osvik and
+* are described in his paper "Speeding Up Serpent".
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_SERPENT_SBOX_H_
+#define BOTAN_SERPENT_SBOX_H_
+
+#include <botan/build.h>
+
+template<typename T>
+BOTAN_FORCE_INLINE void SBoxE0(T& a, T& b, T& c, T& d)
+ {
+ d ^= a;
+ T t0 = b;
+ b &= d;
+ t0 ^= c;
+ b ^= a;
+ a |= d;
+ a ^= t0;
+ t0 ^= d;
+ d ^= c;
+ c |= b;
+ c ^= t0;
+ t0 = ~t0;
+ t0 |= b;
+ b ^= d;
+ b ^= t0;
+ d |= a;
+ b ^= d;
+ t0 ^= d;
+ d = a;
+ a = b;
+ b = t0;
+ }
+
+template<typename T>
+BOTAN_FORCE_INLINE void SBoxE1(T& a, T& b, T& c, T& d)
+ {
+ a = ~a;
+ c = ~c;
+ T t0 = a;
+ a &= b;
+ c ^= a;
+ a |= d;
+ d ^= c;
+ b ^= a;
+ a ^= t0;
+ t0 |= b;
+ b ^= d;
+ c |= a;
+ c &= t0;
+ a ^= b;
+ b &= c;
+ b ^= a;
+ a &= c;
+ t0 ^= a;
+ a = c;
+ c = d;
+ d = b;
+ b = t0;
+ }
+
+template<typename T>
+BOTAN_FORCE_INLINE void SBoxE2(T& a, T& b, T& c, T& d)
+ {
+ T t0 = a;
+ a &= c;
+ a ^= d;
+ c ^= b;
+ c ^= a;
+ d |= t0;
+ d ^= b;
+ t0 ^= c;
+ b = d;
+ d |= t0;
+ d ^= a;
+ a &= b;
+ t0 ^= a;
+ b ^= d;
+ b ^= t0;
+ a = c;
+ c = b;
+ b = d;
+ d = ~t0;
+ }
+
+template<typename T>
+BOTAN_FORCE_INLINE void SBoxE3(T& a, T& b, T& c, T& d)
+ {
+ T t0 = a;
+ a |= d;
+ d ^= b;
+ b &= t0;
+ t0 ^= c;
+ c ^= d;
+ d &= a;
+ t0 |= b;
+ d ^= t0;
+ a ^= b;
+ t0 &= a;
+ b ^= d;
+ t0 ^= c;
+ b |= a;
+ b ^= c;
+ a ^= d;
+ c = b;
+ b |= d;
+ a ^= b;
+ b = c;
+ c = d;
+ d = t0;
+ }
+
+template<typename T>
+BOTAN_FORCE_INLINE void SBoxE4(T& a, T& b, T& c, T& d)
+ {
+ b ^= d;
+ d = ~d;
+ c ^= d;
+ d ^= a;
+ T t0 = b;
+ b &= d;
+ b ^= c;
+ t0 ^= d;
+ a ^= t0;
+ c &= t0;
+ c ^= a;
+ a &= b;
+ d ^= a;
+ t0 |= b;
+ t0 ^= a;
+ a |= d;
+ a ^= c;
+ c &= d;
+ a = ~a;
+ t0 ^= c;
+ c = a;
+ a = b;
+ b = t0;
+ }
+
+template<typename T>
+BOTAN_FORCE_INLINE void SBoxE5(T& a, T& b, T& c, T& d)
+ {
+ a ^= b;
+ b ^= d;
+ d = ~d;
+ T t0 = b;
+ b &= a;
+ c ^= d;
+ b ^= c;
+ c |= t0;
+ t0 ^= d;
+ d &= b;
+ d ^= a;
+ t0 ^= b;
+ t0 ^= c;
+ c ^= a;
+ a &= d;
+ c = ~c;
+ a ^= t0;
+ t0 |= d;
+ t0 ^= c;
+ c = a;
+ a = b;
+ b = d;
+ d = t0;
+ }
+
+template<typename T>
+BOTAN_FORCE_INLINE void SBoxE6(T& a, T& b, T& c, T& d)
+ {
+ c = ~c;
+ T t0 = d;
+ d &= a;
+ a ^= t0;
+ d ^= c;
+ c |= t0;
+ b ^= d;
+ c ^= a;
+ a |= b;
+ c ^= b;
+ t0 ^= a;
+ a |= d;
+ a ^= c;
+ t0 ^= d;
+ t0 ^= a;
+ d = ~d;
+ c &= t0;
+ d ^= c;
+ c = t0;
+ }
+
+template<typename T>
+BOTAN_FORCE_INLINE void SBoxE7(T& a, T& b, T& c, T& d)
+ {
+ T t0 = b;
+ b |= c;
+ b ^= d;
+ t0 ^= c;
+ c ^= b;
+ d |= t0;
+ d &= a;
+ t0 ^= c;
+ d ^= b;
+ b |= t0;
+ b ^= a;
+ a |= t0;
+ a ^= c;
+ b ^= t0;
+ c ^= b;
+ b &= a;
+ b ^= t0;
+ c = ~c;
+ c |= a;
+ t0 ^= c;
+ c = b;
+ b = d;
+ d = a;
+ a = t0;
+ }
+
+template<typename T>
+BOTAN_FORCE_INLINE void SBoxD0(T& a, T& b, T& c, T& d)
+ {
+ c = ~c;
+ T t0 = b;
+ b |= a;
+ t0 = ~t0;
+ b ^= c;
+ c |= t0;
+ b ^= d;
+ a ^= t0;
+ c ^= a;
+ a &= d;
+ t0 ^= a;
+ a |= b;
+ a ^= c;
+ d ^= t0;
+ c ^= b;
+ d ^= a;
+ d ^= b;
+ c &= d;
+ t0 ^= c;
+ c = b;
+ b = t0;
+ }
+
+template<typename T>
+BOTAN_FORCE_INLINE void SBoxD1(T& a, T& b, T& c, T& d)
+ {
+ T t0 = b;
+ b ^= d;
+ d &= b;
+ t0 ^= c;
+ d ^= a;
+ a |= b;
+ c ^= d;
+ a ^= t0;
+ a |= c;
+ b ^= d;
+ a ^= b;
+ b |= d;
+ b ^= a;
+ t0 = ~t0;
+ t0 ^= b;
+ b |= a;
+ b ^= a;
+ b |= t0;
+ d ^= b;
+ b = a;
+ a = t0;
+ t0 = c;
+ c = d;
+ d = t0;
+ }
+
+template<typename T>
+BOTAN_FORCE_INLINE void SBoxD2(T& a, T& b, T& c, T& d)
+ {
+ c ^= d;
+ d ^= a;
+ T t0 = d;
+ d &= c;
+ d ^= b;
+ b |= c;
+ b ^= t0;
+ t0 &= d;
+ c ^= d;
+ t0 &= a;
+ t0 ^= c;
+ c &= b;
+ c |= a;
+ d = ~d;
+ c ^= d;
+ a ^= d;
+ a &= b;
+ d ^= t0;
+ d ^= a;
+ a = b;
+ b = t0;
+ }
+
+template<typename T>
+BOTAN_FORCE_INLINE void SBoxD3(T& a, T& b, T& c, T& d)
+ {
+ T t0 = c;
+ c ^= b;
+ a ^= c;
+ t0 &= c;
+ t0 ^= a;
+ a &= b;
+ b ^= d;
+ d |= t0;
+ c ^= d;
+ a ^= d;
+ b ^= t0;
+ d &= c;
+ d ^= b;
+ b ^= a;
+ b |= c;
+ a ^= d;
+ b ^= t0;
+ a ^= b;
+ t0 = a;
+ a = c;
+ c = d;
+ d = t0;
+ }
+
+template<typename T>
+BOTAN_FORCE_INLINE void SBoxD4(T& a, T& b, T& c, T& d)
+ {
+ T t0 = c;
+ c &= d;
+ c ^= b;
+ b |= d;
+ b &= a;
+ t0 ^= c;
+ t0 ^= b;
+ b &= c;
+ a = ~a;
+ d ^= t0;
+ b ^= d;
+ d &= a;
+ d ^= c;
+ a ^= b;
+ c &= a;
+ d ^= a;
+ c ^= t0;
+ c |= d;
+ d ^= a;
+ c ^= b;
+ b = d;
+ d = t0;
+ }
+
+template<typename T>
+BOTAN_FORCE_INLINE void SBoxD5(T& a, T& b, T& c, T& d)
+ {
+ b = ~b;
+ T t0 = d;
+ c ^= b;
+ d |= a;
+ d ^= c;
+ c |= b;
+ c &= a;
+ t0 ^= d;
+ c ^= t0;
+ t0 |= a;
+ t0 ^= b;
+ b &= c;
+ b ^= d;
+ t0 ^= c;
+ d &= t0;
+ t0 ^= b;
+ d ^= t0;
+ t0 = ~t0;
+ d ^= a;
+ a = b;
+ b = t0;
+ t0 = d;
+ d = c;
+ c = t0;
+ }
+
+template<typename T>
+BOTAN_FORCE_INLINE void SBoxD6(T& a, T& b, T& c, T& d)
+ {
+ a ^= c;
+ T t0 = c;
+ c &= a;
+ t0 ^= d;
+ c = ~c;
+ d ^= b;
+ c ^= d;
+ t0 |= a;
+ a ^= c;
+ d ^= t0;
+ t0 ^= b;
+ b &= d;
+ b ^= a;
+ a ^= d;
+ a |= c;
+ d ^= b;
+ t0 ^= a;
+ a = b;
+ b = c;
+ c = t0;
+ }
+
+template<typename T>
+BOTAN_FORCE_INLINE void SBoxD7(T& a, T& b, T& c, T& d)
+ {
+ T t0 = c;
+ c ^= a;
+ a &= d;
+ t0 |= d;
+ c = ~c;
+ d ^= b;
+ b |= a;
+ a ^= c;
+ c &= t0;
+ d &= t0;
+ b ^= c;
+ c ^= a;
+ a |= c;
+ t0 ^= b;
+ a ^= d;
+ d ^= t0;
+ t0 |= a;
+ d ^= c;
+ t0 ^= c;
+ c = b;
+ b = a;
+ a = d;
+ d = t0;
+ }
+
+#endif
diff --git a/comm/third_party/botan/src/lib/block/serpent/serpent_simd/info.txt b/comm/third_party/botan/src/lib/block/serpent/serpent_simd/info.txt
new file mode 100644
index 0000000000..f7dadf33fc
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/serpent/serpent_simd/info.txt
@@ -0,0 +1,7 @@
+<defines>
+SERPENT_SIMD -> 20160903
+</defines>
+
+<requires>
+simd
+</requires>
diff --git a/comm/third_party/botan/src/lib/block/serpent/serpent_simd/serpent_simd.cpp b/comm/third_party/botan/src/lib/block/serpent/serpent_simd/serpent_simd.cpp
new file mode 100644
index 0000000000..8ac783ba5c
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/serpent/serpent_simd/serpent_simd.cpp
@@ -0,0 +1,169 @@
+/*
+* Serpent (SIMD)
+* (C) 2009,2013 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/serpent.h>
+#include <botan/internal/serpent_sbox.h>
+#include <botan/internal/simd_32.h>
+
+namespace Botan {
+
+#define key_xor(round, B0, B1, B2, B3) \
+ do { \
+ B0 ^= SIMD_4x32::splat(m_round_key[4*round ]); \
+ B1 ^= SIMD_4x32::splat(m_round_key[4*round+1]); \
+ B2 ^= SIMD_4x32::splat(m_round_key[4*round+2]); \
+ B3 ^= SIMD_4x32::splat(m_round_key[4*round+3]); \
+ } while(0)
+
+/*
+* Serpent's linear transformations
+*/
+#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)
+
+/*
+* SIMD Serpent Encryption of 4 blocks in parallel
+*/
+void Serpent::simd_encrypt_4(const uint8_t in[64], uint8_t out[64]) const
+ {
+ SIMD_4x32 B0 = SIMD_4x32::load_le(in);
+ SIMD_4x32 B1 = SIMD_4x32::load_le(in + 16);
+ SIMD_4x32 B2 = SIMD_4x32::load_le(in + 32);
+ SIMD_4x32 B3 = SIMD_4x32::load_le(in + 48);
+
+ SIMD_4x32::transpose(B0, B1, B2, B3);
+
+ key_xor( 0,B0,B1,B2,B3); SBoxE0(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+ key_xor( 1,B0,B1,B2,B3); SBoxE1(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+ key_xor( 2,B0,B1,B2,B3); SBoxE2(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+ key_xor( 3,B0,B1,B2,B3); SBoxE3(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+ key_xor( 4,B0,B1,B2,B3); SBoxE4(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+ key_xor( 5,B0,B1,B2,B3); SBoxE5(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+ key_xor( 6,B0,B1,B2,B3); SBoxE6(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+ key_xor( 7,B0,B1,B2,B3); SBoxE7(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+
+ key_xor( 8,B0,B1,B2,B3); SBoxE0(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+ key_xor( 9,B0,B1,B2,B3); SBoxE1(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+ key_xor(10,B0,B1,B2,B3); SBoxE2(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+ key_xor(11,B0,B1,B2,B3); SBoxE3(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+ key_xor(12,B0,B1,B2,B3); SBoxE4(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+ key_xor(13,B0,B1,B2,B3); SBoxE5(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+ key_xor(14,B0,B1,B2,B3); SBoxE6(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+ key_xor(15,B0,B1,B2,B3); SBoxE7(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+
+ key_xor(16,B0,B1,B2,B3); SBoxE0(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+ key_xor(17,B0,B1,B2,B3); SBoxE1(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+ key_xor(18,B0,B1,B2,B3); SBoxE2(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+ key_xor(19,B0,B1,B2,B3); SBoxE3(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+ key_xor(20,B0,B1,B2,B3); SBoxE4(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+ key_xor(21,B0,B1,B2,B3); SBoxE5(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+ key_xor(22,B0,B1,B2,B3); SBoxE6(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+ key_xor(23,B0,B1,B2,B3); SBoxE7(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+
+ key_xor(24,B0,B1,B2,B3); SBoxE0(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+ key_xor(25,B0,B1,B2,B3); SBoxE1(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+ key_xor(26,B0,B1,B2,B3); SBoxE2(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+ key_xor(27,B0,B1,B2,B3); SBoxE3(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+ key_xor(28,B0,B1,B2,B3); SBoxE4(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+ key_xor(29,B0,B1,B2,B3); SBoxE5(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+ key_xor(30,B0,B1,B2,B3); SBoxE6(B0,B1,B2,B3); transform(B0,B1,B2,B3);
+ key_xor(31,B0,B1,B2,B3); SBoxE7(B0,B1,B2,B3); key_xor(32,B0,B1,B2,B3);
+
+ SIMD_4x32::transpose(B0, B1, B2, B3);
+
+ B0.store_le(out);
+ B1.store_le(out + 16);
+ B2.store_le(out + 32);
+ B3.store_le(out + 48);
+ }
+
+/*
+* SIMD Serpent Decryption of 4 blocks in parallel
+*/
+void Serpent::simd_decrypt_4(const uint8_t in[64], uint8_t out[64]) const
+ {
+ SIMD_4x32 B0 = SIMD_4x32::load_le(in);
+ SIMD_4x32 B1 = SIMD_4x32::load_le(in + 16);
+ SIMD_4x32 B2 = SIMD_4x32::load_le(in + 32);
+ SIMD_4x32 B3 = SIMD_4x32::load_le(in + 48);
+
+ SIMD_4x32::transpose(B0, B1, B2, B3);
+
+ key_xor(32,B0,B1,B2,B3); SBoxD7(B0,B1,B2,B3); key_xor(31,B0,B1,B2,B3);
+ i_transform(B0,B1,B2,B3); SBoxD6(B0,B1,B2,B3); key_xor(30,B0,B1,B2,B3);
+ i_transform(B0,B1,B2,B3); SBoxD5(B0,B1,B2,B3); key_xor(29,B0,B1,B2,B3);
+ i_transform(B0,B1,B2,B3); SBoxD4(B0,B1,B2,B3); key_xor(28,B0,B1,B2,B3);
+ i_transform(B0,B1,B2,B3); SBoxD3(B0,B1,B2,B3); key_xor(27,B0,B1,B2,B3);
+ i_transform(B0,B1,B2,B3); SBoxD2(B0,B1,B2,B3); key_xor(26,B0,B1,B2,B3);
+ i_transform(B0,B1,B2,B3); SBoxD1(B0,B1,B2,B3); key_xor(25,B0,B1,B2,B3);
+ i_transform(B0,B1,B2,B3); SBoxD0(B0,B1,B2,B3); key_xor(24,B0,B1,B2,B3);
+
+ i_transform(B0,B1,B2,B3); SBoxD7(B0,B1,B2,B3); key_xor(23,B0,B1,B2,B3);
+ i_transform(B0,B1,B2,B3); SBoxD6(B0,B1,B2,B3); key_xor(22,B0,B1,B2,B3);
+ i_transform(B0,B1,B2,B3); SBoxD5(B0,B1,B2,B3); key_xor(21,B0,B1,B2,B3);
+ i_transform(B0,B1,B2,B3); SBoxD4(B0,B1,B2,B3); key_xor(20,B0,B1,B2,B3);
+ i_transform(B0,B1,B2,B3); SBoxD3(B0,B1,B2,B3); key_xor(19,B0,B1,B2,B3);
+ i_transform(B0,B1,B2,B3); SBoxD2(B0,B1,B2,B3); key_xor(18,B0,B1,B2,B3);
+ i_transform(B0,B1,B2,B3); SBoxD1(B0,B1,B2,B3); key_xor(17,B0,B1,B2,B3);
+ i_transform(B0,B1,B2,B3); SBoxD0(B0,B1,B2,B3); key_xor(16,B0,B1,B2,B3);
+
+ i_transform(B0,B1,B2,B3); SBoxD7(B0,B1,B2,B3); key_xor(15,B0,B1,B2,B3);
+ i_transform(B0,B1,B2,B3); SBoxD6(B0,B1,B2,B3); key_xor(14,B0,B1,B2,B3);
+ i_transform(B0,B1,B2,B3); SBoxD5(B0,B1,B2,B3); key_xor(13,B0,B1,B2,B3);
+ i_transform(B0,B1,B2,B3); SBoxD4(B0,B1,B2,B3); key_xor(12,B0,B1,B2,B3);
+ i_transform(B0,B1,B2,B3); SBoxD3(B0,B1,B2,B3); key_xor(11,B0,B1,B2,B3);
+ i_transform(B0,B1,B2,B3); SBoxD2(B0,B1,B2,B3); key_xor(10,B0,B1,B2,B3);
+ i_transform(B0,B1,B2,B3); SBoxD1(B0,B1,B2,B3); key_xor( 9,B0,B1,B2,B3);
+ i_transform(B0,B1,B2,B3); SBoxD0(B0,B1,B2,B3); key_xor( 8,B0,B1,B2,B3);
+
+ i_transform(B0,B1,B2,B3); SBoxD7(B0,B1,B2,B3); key_xor( 7,B0,B1,B2,B3);
+ i_transform(B0,B1,B2,B3); SBoxD6(B0,B1,B2,B3); key_xor( 6,B0,B1,B2,B3);
+ i_transform(B0,B1,B2,B3); SBoxD5(B0,B1,B2,B3); key_xor( 5,B0,B1,B2,B3);
+ i_transform(B0,B1,B2,B3); SBoxD4(B0,B1,B2,B3); key_xor( 4,B0,B1,B2,B3);
+ i_transform(B0,B1,B2,B3); SBoxD3(B0,B1,B2,B3); key_xor( 3,B0,B1,B2,B3);
+ i_transform(B0,B1,B2,B3); SBoxD2(B0,B1,B2,B3); key_xor( 2,B0,B1,B2,B3);
+ i_transform(B0,B1,B2,B3); SBoxD1(B0,B1,B2,B3); key_xor( 1,B0,B1,B2,B3);
+ i_transform(B0,B1,B2,B3); SBoxD0(B0,B1,B2,B3); key_xor( 0,B0,B1,B2,B3);
+
+ SIMD_4x32::transpose(B0, B1, B2, B3);
+
+ B0.store_le(out);
+ B1.store_le(out + 16);
+ B2.store_le(out + 32);
+ B3.store_le(out + 48);
+ }
+
+#undef key_xor
+#undef transform
+#undef i_transform
+
+}
diff --git a/comm/third_party/botan/src/lib/block/shacal2/info.txt b/comm/third_party/botan/src/lib/block/shacal2/info.txt
new file mode 100644
index 0000000000..62e00503f9
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/shacal2/info.txt
@@ -0,0 +1,5 @@
+<defines>
+SHACAL2 -> 20170813
+</defines>
+
+
diff --git a/comm/third_party/botan/src/lib/block/shacal2/shacal2.cpp b/comm/third_party/botan/src/lib/block/shacal2/shacal2.cpp
new file mode 100644
index 0000000000..b0c57f2359
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/shacal2/shacal2.cpp
@@ -0,0 +1,280 @@
+/*
+* SHACAL-2
+* (C) 2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/shacal2.h>
+#include <botan/loadstor.h>
+#include <botan/rotate.h>
+#include <botan/cpuid.h>
+
+namespace Botan {
+
+namespace {
+
+inline void SHACAL2_Fwd(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 RK)
+ {
+ const uint32_t A_rho = rotr<2>(A) ^ rotr<13>(A) ^ rotr<22>(A);
+ const uint32_t E_rho = rotr<6>(E) ^ rotr<11>(E) ^ rotr<25>(E);
+
+ H += E_rho + ((E & F) ^ (~E & G)) + RK;
+ D += H;
+ H += A_rho + ((A & B) | ((A | B) & C));
+ }
+
+inline void SHACAL2_Rev(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 RK)
+ {
+ const uint32_t A_rho = rotr<2>(A) ^ rotr<13>(A) ^ rotr<22>(A);
+ const uint32_t E_rho = rotr<6>(E) ^ rotr<11>(E) ^ rotr<25>(E);
+
+ H -= A_rho + ((A & B) | ((A | B) & C));
+ D -= H;
+ H -= E_rho + ((E & F) ^ (~E & G)) + RK;
+ }
+
+}
+
+/*
+* SHACAL2 Encryption
+*/
+void SHACAL2::encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+ {
+ verify_key_set(m_RK.empty() == false);
+
+#if defined(BOTAN_HAS_SHACAL2_X86)
+ if(CPUID::has_intel_sha())
+ {
+ return x86_encrypt_blocks(in, out, blocks);
+ }
+#endif
+
+#if defined(BOTAN_HAS_SHACAL2_AVX2)
+ if(CPUID::has_avx2())
+ {
+ while(blocks >= 8)
+ {
+ avx2_encrypt_8(in, out);
+ in += 8*BLOCK_SIZE;
+ out += 8*BLOCK_SIZE;
+ blocks -= 8;
+ }
+ }
+#endif
+
+#if defined(BOTAN_HAS_SHACAL2_SIMD)
+ if(CPUID::has_simd_32())
+ {
+ while(blocks >= 4)
+ {
+ simd_encrypt_4(in, out);
+ in += 4*BLOCK_SIZE;
+ out += 4*BLOCK_SIZE;
+ blocks -= 4;
+ }
+ }
+#endif
+
+ for(size_t i = 0; i != blocks; ++i)
+ {
+ uint32_t A = load_be<uint32_t>(in, 0);
+ uint32_t B = load_be<uint32_t>(in, 1);
+ uint32_t C = load_be<uint32_t>(in, 2);
+ uint32_t D = load_be<uint32_t>(in, 3);
+ uint32_t E = load_be<uint32_t>(in, 4);
+ uint32_t F = load_be<uint32_t>(in, 5);
+ uint32_t G = load_be<uint32_t>(in, 6);
+ uint32_t H = load_be<uint32_t>(in, 7);
+
+ 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]);
+ }
+
+ store_be(out, A, B, C, D, E, F, G, H);
+
+ in += BLOCK_SIZE;
+ out += BLOCK_SIZE;
+ }
+ }
+
+/*
+* SHACAL2 Encryption
+*/
+void SHACAL2::decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+ {
+ verify_key_set(m_RK.empty() == false);
+
+#if defined(BOTAN_HAS_SHACAL2_AVX2)
+ if(CPUID::has_avx2())
+ {
+ while(blocks >= 8)
+ {
+ avx2_decrypt_8(in, out);
+ in += 8*BLOCK_SIZE;
+ out += 8*BLOCK_SIZE;
+ blocks -= 8;
+ }
+ }
+#endif
+
+#if defined(BOTAN_HAS_SHACAL2_SIMD)
+ if(CPUID::has_simd_32())
+ {
+ while(blocks >= 4)
+ {
+ simd_decrypt_4(in, out);
+ in += 4*BLOCK_SIZE;
+ out += 4*BLOCK_SIZE;
+ blocks -= 4;
+ }
+ }
+#endif
+
+ for(size_t i = 0; i != blocks; ++i)
+ {
+ uint32_t A = load_be<uint32_t>(in, 0);
+ uint32_t B = load_be<uint32_t>(in, 1);
+ uint32_t C = load_be<uint32_t>(in, 2);
+ uint32_t D = load_be<uint32_t>(in, 3);
+ uint32_t E = load_be<uint32_t>(in, 4);
+ uint32_t F = load_be<uint32_t>(in, 5);
+ uint32_t G = load_be<uint32_t>(in, 6);
+ uint32_t H = load_be<uint32_t>(in, 7);
+
+ 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]);
+ }
+
+ store_be(out, A, B, C, D, E, F, G, H);
+
+ in += BLOCK_SIZE;
+ out += BLOCK_SIZE;
+ }
+ }
+
+/*
+* SHACAL2 Key Schedule
+*/
+void SHACAL2::key_schedule(const uint8_t key[], size_t len)
+ {
+ const uint32_t RC[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
+ };
+
+ if(m_RK.empty())
+ m_RK.resize(64);
+ else
+ clear_mem(m_RK.data(), m_RK.size());
+
+ load_be(m_RK.data(), key, len/4);
+
+ for(size_t i = 16; i != 64; ++i)
+ {
+ const uint32_t sigma0_15 = rotr< 7>(m_RK[i-15]) ^ rotr<18>(m_RK[i-15]) ^ (m_RK[i-15] >> 3);
+ const uint32_t sigma1_2 = rotr<17>(m_RK[i- 2]) ^ rotr<19>(m_RK[i- 2]) ^ (m_RK[i- 2] >> 10);
+ m_RK[i] = m_RK[i-16] + sigma0_15 + m_RK[i-7] + sigma1_2;
+ }
+
+ for(size_t i = 0; i != 64; ++i)
+ {
+ m_RK[i] += RC[i];
+ }
+ }
+
+size_t SHACAL2::parallelism() const
+ {
+#if defined(BOTAN_HAS_SHACAL2_X86)
+ if(CPUID::has_intel_sha())
+ {
+ return 4;
+ }
+#endif
+
+#if defined(BOTAN_HAS_SHACAL2_AVX2)
+ if(CPUID::has_avx2())
+ {
+ return 8;
+ }
+#endif
+
+#if defined(BOTAN_HAS_SHACAL2_SIMD)
+ if(CPUID::has_simd_32())
+ {
+ return 4;
+ }
+#endif
+
+ return 1;
+ }
+
+std::string SHACAL2::provider() const
+ {
+#if defined(BOTAN_HAS_SHACAL2_X86)
+ if(CPUID::has_intel_sha())
+ {
+ return "intel_sha";
+ }
+#endif
+
+#if defined(BOTAN_HAS_SHACAL2_AVX2)
+ if(CPUID::has_avx2())
+ {
+ return "avx2";
+ }
+#endif
+
+#if defined(BOTAN_HAS_SHACAL2_SIMD)
+ if(CPUID::has_simd_32())
+ {
+ return "simd";
+ }
+#endif
+
+ return "base";
+ }
+
+/*
+* Clear memory of sensitive data
+*/
+void SHACAL2::clear()
+ {
+ zap(m_RK);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/block/shacal2/shacal2.h b/comm/third_party/botan/src/lib/block/shacal2/shacal2.h
new file mode 100644
index 0000000000..b752a03390
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/shacal2/shacal2.h
@@ -0,0 +1,54 @@
+/*
+* SHACAL-2
+* (C) 2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_SHACAL2_H_
+#define BOTAN_SHACAL2_H_
+
+#include <botan/block_cipher.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(shacal2.h)
+
+namespace Botan {
+
+/**
+* SHACAL2
+*/
+class BOTAN_PUBLIC_API(2,3) SHACAL2 final : public Block_Cipher_Fixed_Params<32, 16, 64, 4>
+ {
+ public:
+ void encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+ void decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+
+ std::string provider() const override;
+ void clear() override;
+ std::string name() const override { return "SHACAL2"; }
+ BlockCipher* clone() const override { return new SHACAL2; }
+ size_t parallelism() const override;
+
+ private:
+ void key_schedule(const uint8_t[], size_t) override;
+
+#if defined(BOTAN_HAS_SHACAL2_SIMD)
+ void simd_encrypt_4(const uint8_t in[], uint8_t out[]) const;
+ void simd_decrypt_4(const uint8_t in[], uint8_t out[]) const;
+#endif
+
+#if defined(BOTAN_HAS_SHACAL2_AVX2)
+ void avx2_encrypt_8(const uint8_t in[], uint8_t out[]) const;
+ void avx2_decrypt_8(const uint8_t in[], uint8_t out[]) const;
+#endif
+
+#if defined(BOTAN_HAS_SHACAL2_X86)
+ void x86_encrypt_blocks(const uint8_t in[], uint8_t out[], size_t blocks) const;
+#endif
+
+ secure_vector<uint32_t> m_RK;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/block/shacal2/shacal2_avx2/info.txt b/comm/third_party/botan/src/lib/block/shacal2/shacal2_avx2/info.txt
new file mode 100644
index 0000000000..a0b5ce1a97
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/shacal2/shacal2_avx2/info.txt
@@ -0,0 +1,11 @@
+<defines>
+SHACAL2_AVX2 -> 20180826
+</defines>
+
+<isa>
+avx2
+</isa>
+
+<requires>
+simd_avx2
+</requires>
diff --git a/comm/third_party/botan/src/lib/block/shacal2/shacal2_avx2/shacal2_avx2.cpp b/comm/third_party/botan/src/lib/block/shacal2/shacal2_avx2/shacal2_avx2.cpp
new file mode 100644
index 0000000000..a465a38286
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/shacal2/shacal2_avx2/shacal2_avx2.cpp
@@ -0,0 +1,122 @@
+/*
+* (C) 2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/shacal2.h>
+#include <botan/internal/simd_avx2.h>
+
+namespace Botan {
+
+namespace {
+
+void BOTAN_FORCE_INLINE BOTAN_FUNC_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.rho<6,11,25>() + ((E & F) ^ (~E & G)) + SIMD_8x32::splat(RK);
+ D += H;
+ H += A.rho<2,13,22>() + ((A & B) | ((A | B) & C));
+ }
+
+void BOTAN_FORCE_INLINE BOTAN_FUNC_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.rho<2,13,22>() + ((A & B) | ((A | B) & C));
+ D -= H;
+ H -= E.rho<6,11,25>() + ((E & F) ^ (~E & G)) + SIMD_8x32::splat(RK);
+ }
+
+}
+
+void BOTAN_FUNC_ISA("avx2") SHACAL2::avx2_encrypt_8(const uint8_t in[], uint8_t out[]) const
+ {
+ SIMD_8x32::reset_registers();
+
+ SIMD_8x32 A = SIMD_8x32::load_be(in);
+ SIMD_8x32 B = SIMD_8x32::load_be(in+32);
+ SIMD_8x32 C = SIMD_8x32::load_be(in+64);
+ SIMD_8x32 D = SIMD_8x32::load_be(in+96);
+
+ SIMD_8x32 E = SIMD_8x32::load_be(in+128);
+ SIMD_8x32 F = SIMD_8x32::load_be(in+160);
+ SIMD_8x32 G = SIMD_8x32::load_be(in+192);
+ SIMD_8x32 H = SIMD_8x32::load_be(in+224);
+
+ 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);
+ B.store_be(out+32);
+ C.store_be(out+64);
+ D.store_be(out+96);
+
+ E.store_be(out+128);
+ F.store_be(out+160);
+ G.store_be(out+192);
+ H.store_be(out+224);
+
+ SIMD_8x32::zero_registers();
+ }
+
+BOTAN_FUNC_ISA("avx2") void SHACAL2::avx2_decrypt_8(const uint8_t in[], uint8_t out[]) const
+ {
+ SIMD_8x32::reset_registers();
+
+ SIMD_8x32 A = SIMD_8x32::load_be(in);
+ SIMD_8x32 B = SIMD_8x32::load_be(in+32);
+ SIMD_8x32 C = SIMD_8x32::load_be(in+64);
+ SIMD_8x32 D = SIMD_8x32::load_be(in+96);
+
+ SIMD_8x32 E = SIMD_8x32::load_be(in+128);
+ SIMD_8x32 F = SIMD_8x32::load_be(in+160);
+ SIMD_8x32 G = SIMD_8x32::load_be(in+192);
+ SIMD_8x32 H = SIMD_8x32::load_be(in+224);
+
+ 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);
+ B.store_be(out+32);
+ C.store_be(out+64);
+ D.store_be(out+96);
+
+ E.store_be(out+128);
+ F.store_be(out+160);
+ G.store_be(out+192);
+ H.store_be(out+224);
+
+ SIMD_8x32::zero_registers();
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/block/shacal2/shacal2_simd/info.txt b/comm/third_party/botan/src/lib/block/shacal2/shacal2_simd/info.txt
new file mode 100644
index 0000000000..8d715c668c
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/shacal2/shacal2_simd/info.txt
@@ -0,0 +1,8 @@
+<defines>
+SHACAL2_SIMD -> 20170813
+</defines>
+
+<requires>
+shacal2
+simd
+</requires>
diff --git a/comm/third_party/botan/src/lib/block/shacal2/shacal2_simd/shacal2_simd.cpp b/comm/third_party/botan/src/lib/block/shacal2/shacal2_simd/shacal2_simd.cpp
new file mode 100644
index 0000000000..6d15faf1a6
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/shacal2/shacal2_simd/shacal2_simd.cpp
@@ -0,0 +1,119 @@
+/*
+* SHACAL-2 using SIMD
+* (C) 2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/shacal2.h>
+#include <botan/internal/simd_32.h>
+
+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)
+ {
+ H += E.rho<6,11,25>() + ((E & F) ^ (~E & G)) + SIMD_4x32::splat(RK);
+ D += H;
+ H += A.rho<2,13,22>() + ((A & B) | ((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)
+ {
+ H -= A.rho<2,13,22>() + ((A & B) | ((A | B) & C));
+ D -= H;
+ H -= E.rho<6,11,25>() + ((E & F) ^ (~E & G)) + SIMD_4x32::splat(RK);
+ }
+
+}
+
+void 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);
+ SIMD_4x32 F = SIMD_4x32::load_be(in+48);
+
+ SIMD_4x32 C = SIMD_4x32::load_be(in+64);
+ SIMD_4x32 G = SIMD_4x32::load_be(in+80);
+ SIMD_4x32 D = SIMD_4x32::load_be(in+96);
+ SIMD_4x32 H = SIMD_4x32::load_be(in+112);
+
+ SIMD_4x32::transpose(A, B, C, D);
+ SIMD_4x32::transpose(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_4x32::transpose(A, B, C, D);
+ SIMD_4x32::transpose(E, F, G, H);
+
+ A.store_be(out);
+ E.store_be(out+16);
+ B.store_be(out+32);
+ F.store_be(out+48);
+
+ C.store_be(out+64);
+ G.store_be(out+80);
+ D.store_be(out+96);
+ H.store_be(out+112);
+ }
+
+void 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);
+ SIMD_4x32 F = SIMD_4x32::load_be(in+48);
+
+ SIMD_4x32 C = SIMD_4x32::load_be(in+64);
+ SIMD_4x32 G = SIMD_4x32::load_be(in+80);
+ SIMD_4x32 D = SIMD_4x32::load_be(in+96);
+ SIMD_4x32 H = SIMD_4x32::load_be(in+112);
+
+ SIMD_4x32::transpose(A, B, C, D);
+ SIMD_4x32::transpose(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_4x32::transpose(A, B, C, D);
+ SIMD_4x32::transpose(E, F, G, H);
+
+ A.store_be(out);
+ E.store_be(out+16);
+ B.store_be(out+32);
+ F.store_be(out+48);
+
+ C.store_be(out+64);
+ G.store_be(out+80);
+ D.store_be(out+96);
+ H.store_be(out+112);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/block/shacal2/shacal2_x86/info.txt b/comm/third_party/botan/src/lib/block/shacal2/shacal2_x86/info.txt
new file mode 100644
index 0000000000..2988330482
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/shacal2/shacal2_x86/info.txt
@@ -0,0 +1,20 @@
+<defines>
+SHACAL2_X86 -> 20170814
+</defines>
+
+<requires>
+shacal2
+</requires>
+
+<isa>
+sha
+sse2
+ssse3
+</isa>
+
+<cc>
+gcc:5.0
+clang:3.9
+msvc:19.0 # MSVS 2015
+</cc>
+
diff --git a/comm/third_party/botan/src/lib/block/shacal2/shacal2_x86/shacal2_x86.cpp b/comm/third_party/botan/src/lib/block/shacal2/shacal2_x86/shacal2_x86.cpp
new file mode 100644
index 0000000000..1611d6c9b6
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/shacal2/shacal2_x86/shacal2_x86.cpp
@@ -0,0 +1,118 @@
+/*
+* SHACAL-2 using x86 SHA extensions
+* (C) 2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/shacal2.h>
+#include <immintrin.h>
+
+namespace Botan {
+
+/*
+Only encryption is supported since the inverse round function would
+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
+ {
+ 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);
+
+ const __m128i* RK_mm = reinterpret_cast<const __m128i*>(m_RK.data());
+ const __m128i* in_mm = reinterpret_cast<const __m128i*>(in);
+ __m128i* out_mm = reinterpret_cast<__m128i*>(out);
+
+ while(blocks >= 2)
+ {
+ __m128i B0_0 = _mm_loadu_si128(in_mm);
+ __m128i B0_1 = _mm_loadu_si128(in_mm+1);
+ __m128i B1_0 = _mm_loadu_si128(in_mm+2);
+ __m128i B1_1 = _mm_loadu_si128(in_mm+3);
+
+ __m128i TMP = _mm_shuffle_epi8(_mm_unpacklo_epi64(B0_0, B0_1), MASK2);
+ B0_1 = _mm_shuffle_epi8(_mm_unpackhi_epi64(B0_0, B0_1), MASK2);
+ B0_0 = TMP;
+
+ TMP = _mm_shuffle_epi8(_mm_unpacklo_epi64(B1_0, B1_1), MASK2);
+ B1_1 = _mm_shuffle_epi8(_mm_unpackhi_epi64(B1_0, B1_1), MASK2);
+ B1_0 = TMP;
+
+ for(size_t i = 0; i != 8; ++i)
+ {
+ const __m128i RK0 = _mm_loadu_si128(RK_mm + 2*i);
+ const __m128i RK2 = _mm_loadu_si128(RK_mm + 2*i+1);
+ const __m128i RK1 = _mm_srli_si128(RK0, 8);
+ const __m128i RK3 = _mm_srli_si128(RK2, 8);
+
+ B0_1 = _mm_sha256rnds2_epu32(B0_1, B0_0, RK0);
+ B1_1 = _mm_sha256rnds2_epu32(B1_1, B1_0, RK0);
+
+ B0_0 = _mm_sha256rnds2_epu32(B0_0, B0_1, RK1);
+ B1_0 = _mm_sha256rnds2_epu32(B1_0, B1_1, RK1);
+
+ B0_1 = _mm_sha256rnds2_epu32(B0_1, B0_0, RK2);
+ B1_1 = _mm_sha256rnds2_epu32(B1_1, B1_0, RK2);
+
+ B0_0 = _mm_sha256rnds2_epu32(B0_0, B0_1, RK3);
+ B1_0 = _mm_sha256rnds2_epu32(B1_0, B1_1, RK3);
+ }
+
+ TMP = _mm_shuffle_epi8(_mm_unpackhi_epi64(B0_0, B0_1), MASK1);
+ B0_1 = _mm_shuffle_epi8(_mm_unpacklo_epi64(B0_0, B0_1), MASK1);
+ B0_0 = TMP;
+
+ TMP = _mm_shuffle_epi8(_mm_unpackhi_epi64(B1_0, B1_1), MASK1);
+ B1_1 = _mm_shuffle_epi8(_mm_unpacklo_epi64(B1_0, B1_1), MASK1);
+ B1_0 = TMP;
+
+ // Save state
+ _mm_storeu_si128(out_mm + 0, B0_0);
+ _mm_storeu_si128(out_mm + 1, B0_1);
+ _mm_storeu_si128(out_mm + 2, B1_0);
+ _mm_storeu_si128(out_mm + 3, B1_1);
+
+ blocks -= 2;
+ in_mm += 4;
+ out_mm += 4;
+ }
+
+ while(blocks)
+ {
+ __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);
+ B1 = _mm_shuffle_epi8(_mm_unpackhi_epi64(B0, B1), MASK2);
+ B0 = TMP;
+
+ for(size_t i = 0; i != 8; ++i)
+ {
+ const __m128i RK0 = _mm_loadu_si128(RK_mm + 2*i);
+ const __m128i RK2 = _mm_loadu_si128(RK_mm + 2*i+1);
+ const __m128i RK1 = _mm_srli_si128(RK0, 8);
+ const __m128i RK3 = _mm_srli_si128(RK2, 8);
+
+ B1 = _mm_sha256rnds2_epu32(B1, B0, RK0);
+ B0 = _mm_sha256rnds2_epu32(B0, B1, RK1);
+ B1 = _mm_sha256rnds2_epu32(B1, B0, RK2);
+ B0 = _mm_sha256rnds2_epu32(B0, B1, RK3);
+ }
+
+ TMP = _mm_shuffle_epi8(_mm_unpackhi_epi64(B0, B1), MASK1);
+ B1 = _mm_shuffle_epi8(_mm_unpacklo_epi64(B0, B1), MASK1);
+ B0 = TMP;
+
+ // Save state
+ _mm_storeu_si128(out_mm, B0);
+ _mm_storeu_si128(out_mm + 1, B1);
+
+ blocks--;
+ in_mm += 2;
+ out_mm += 2;
+ }
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/block/sm4/info.txt b/comm/third_party/botan/src/lib/block/sm4/info.txt
new file mode 100644
index 0000000000..32561f6d6f
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/sm4/info.txt
@@ -0,0 +1,3 @@
+<defines>
+SM4 -> 20170716
+</defines>
diff --git a/comm/third_party/botan/src/lib/block/sm4/sm4.cpp b/comm/third_party/botan/src/lib/block/sm4/sm4.cpp
new file mode 100644
index 0000000000..8198330e6e
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/sm4/sm4.cpp
@@ -0,0 +1,341 @@
+/*
+* SM4
+* (C) 2017 Ribose Inc
+* (C) 2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/sm4.h>
+#include <botan/loadstor.h>
+#include <botan/rotate.h>
+#include <botan/cpuid.h>
+
+namespace Botan {
+
+namespace {
+
+alignas(64)
+const uint8_t SM4_SBOX[256] = {
+0xD6, 0x90, 0xE9, 0xFE, 0xCC, 0xE1, 0x3D, 0xB7, 0x16, 0xB6, 0x14, 0xC2, 0x28, 0xFB, 0x2C, 0x05,
+0x2B, 0x67, 0x9A, 0x76, 0x2A, 0xBE, 0x04, 0xC3, 0xAA, 0x44, 0x13, 0x26, 0x49, 0x86, 0x06, 0x99,
+0x9C, 0x42, 0x50, 0xF4, 0x91, 0xEF, 0x98, 0x7A, 0x33, 0x54, 0x0B, 0x43, 0xED, 0xCF, 0xAC, 0x62,
+0xE4, 0xB3, 0x1C, 0xA9, 0xC9, 0x08, 0xE8, 0x95, 0x80, 0xDF, 0x94, 0xFA, 0x75, 0x8F, 0x3F, 0xA6,
+0x47, 0x07, 0xA7, 0xFC, 0xF3, 0x73, 0x17, 0xBA, 0x83, 0x59, 0x3C, 0x19, 0xE6, 0x85, 0x4F, 0xA8,
+0x68, 0x6B, 0x81, 0xB2, 0x71, 0x64, 0xDA, 0x8B, 0xF8, 0xEB, 0x0F, 0x4B, 0x70, 0x56, 0x9D, 0x35,
+0x1E, 0x24, 0x0E, 0x5E, 0x63, 0x58, 0xD1, 0xA2, 0x25, 0x22, 0x7C, 0x3B, 0x01, 0x21, 0x78, 0x87,
+0xD4, 0x00, 0x46, 0x57, 0x9F, 0xD3, 0x27, 0x52, 0x4C, 0x36, 0x02, 0xE7, 0xA0, 0xC4, 0xC8, 0x9E,
+0xEA, 0xBF, 0x8A, 0xD2, 0x40, 0xC7, 0x38, 0xB5, 0xA3, 0xF7, 0xF2, 0xCE, 0xF9, 0x61, 0x15, 0xA1,
+0xE0, 0xAE, 0x5D, 0xA4, 0x9B, 0x34, 0x1A, 0x55, 0xAD, 0x93, 0x32, 0x30, 0xF5, 0x8C, 0xB1, 0xE3,
+0x1D, 0xF6, 0xE2, 0x2E, 0x82, 0x66, 0xCA, 0x60, 0xC0, 0x29, 0x23, 0xAB, 0x0D, 0x53, 0x4E, 0x6F,
+0xD5, 0xDB, 0x37, 0x45, 0xDE, 0xFD, 0x8E, 0x2F, 0x03, 0xFF, 0x6A, 0x72, 0x6D, 0x6C, 0x5B, 0x51,
+0x8D, 0x1B, 0xAF, 0x92, 0xBB, 0xDD, 0xBC, 0x7F, 0x11, 0xD9, 0x5C, 0x41, 0x1F, 0x10, 0x5A, 0xD8,
+0x0A, 0xC1, 0x31, 0x88, 0xA5, 0xCD, 0x7B, 0xBD, 0x2D, 0x74, 0xD0, 0x12, 0xB8, 0xE5, 0xB4, 0xB0,
+0x89, 0x69, 0x97, 0x4A, 0x0C, 0x96, 0x77, 0x7E, 0x65, 0xB9, 0xF1, 0x09, 0xC5, 0x6E, 0xC6, 0x84,
+0x18, 0xF0, 0x7D, 0xEC, 0x3A, 0xDC, 0x4D, 0x20, 0x79, 0xEE, 0x5F, 0x3E, 0xD7, 0xCB, 0x39, 0x48
+};
+
+/*
+* SM4_SBOX_T[j] == L(SM4_SBOX[j]).
+*/
+alignas(64)
+const uint32_t SM4_SBOX_T[256] = {
+ 0x8ED55B5B, 0xD0924242, 0x4DEAA7A7, 0x06FDFBFB, 0xFCCF3333, 0x65E28787,
+ 0xC93DF4F4, 0x6BB5DEDE, 0x4E165858, 0x6EB4DADA, 0x44145050, 0xCAC10B0B,
+ 0x8828A0A0, 0x17F8EFEF, 0x9C2CB0B0, 0x11051414, 0x872BACAC, 0xFB669D9D,
+ 0xF2986A6A, 0xAE77D9D9, 0x822AA8A8, 0x46BCFAFA, 0x14041010, 0xCFC00F0F,
+ 0x02A8AAAA, 0x54451111, 0x5F134C4C, 0xBE269898, 0x6D482525, 0x9E841A1A,
+ 0x1E061818, 0xFD9B6666, 0xEC9E7272, 0x4A430909, 0x10514141, 0x24F7D3D3,
+ 0xD5934646, 0x53ECBFBF, 0xF89A6262, 0x927BE9E9, 0xFF33CCCC, 0x04555151,
+ 0x270B2C2C, 0x4F420D0D, 0x59EEB7B7, 0xF3CC3F3F, 0x1CAEB2B2, 0xEA638989,
+ 0x74E79393, 0x7FB1CECE, 0x6C1C7070, 0x0DABA6A6, 0xEDCA2727, 0x28082020,
+ 0x48EBA3A3, 0xC1975656, 0x80820202, 0xA3DC7F7F, 0xC4965252, 0x12F9EBEB,
+ 0xA174D5D5, 0xB38D3E3E, 0xC33FFCFC, 0x3EA49A9A, 0x5B461D1D, 0x1B071C1C,
+ 0x3BA59E9E, 0x0CFFF3F3, 0x3FF0CFCF, 0xBF72CDCD, 0x4B175C5C, 0x52B8EAEA,
+ 0x8F810E0E, 0x3D586565, 0xCC3CF0F0, 0x7D196464, 0x7EE59B9B, 0x91871616,
+ 0x734E3D3D, 0x08AAA2A2, 0xC869A1A1, 0xC76AADAD, 0x85830606, 0x7AB0CACA,
+ 0xB570C5C5, 0xF4659191, 0xB2D96B6B, 0xA7892E2E, 0x18FBE3E3, 0x47E8AFAF,
+ 0x330F3C3C, 0x674A2D2D, 0xB071C1C1, 0x0E575959, 0xE99F7676, 0xE135D4D4,
+ 0x661E7878, 0xB4249090, 0x360E3838, 0x265F7979, 0xEF628D8D, 0x38596161,
+ 0x95D24747, 0x2AA08A8A, 0xB1259494, 0xAA228888, 0x8C7DF1F1, 0xD73BECEC,
+ 0x05010404, 0xA5218484, 0x9879E1E1, 0x9B851E1E, 0x84D75353, 0x00000000,
+ 0x5E471919, 0x0B565D5D, 0xE39D7E7E, 0x9FD04F4F, 0xBB279C9C, 0x1A534949,
+ 0x7C4D3131, 0xEE36D8D8, 0x0A020808, 0x7BE49F9F, 0x20A28282, 0xD4C71313,
+ 0xE8CB2323, 0xE69C7A7A, 0x42E9ABAB, 0x43BDFEFE, 0xA2882A2A, 0x9AD14B4B,
+ 0x40410101, 0xDBC41F1F, 0xD838E0E0, 0x61B7D6D6, 0x2FA18E8E, 0x2BF4DFDF,
+ 0x3AF1CBCB, 0xF6CD3B3B, 0x1DFAE7E7, 0xE5608585, 0x41155454, 0x25A38686,
+ 0x60E38383, 0x16ACBABA, 0x295C7575, 0x34A69292, 0xF7996E6E, 0xE434D0D0,
+ 0x721A6868, 0x01545555, 0x19AFB6B6, 0xDF914E4E, 0xFA32C8C8, 0xF030C0C0,
+ 0x21F6D7D7, 0xBC8E3232, 0x75B3C6C6, 0x6FE08F8F, 0x691D7474, 0x2EF5DBDB,
+ 0x6AE18B8B, 0x962EB8B8, 0x8A800A0A, 0xFE679999, 0xE2C92B2B, 0xE0618181,
+ 0xC0C30303, 0x8D29A4A4, 0xAF238C8C, 0x07A9AEAE, 0x390D3434, 0x1F524D4D,
+ 0x764F3939, 0xD36EBDBD, 0x81D65757, 0xB7D86F6F, 0xEB37DCDC, 0x51441515,
+ 0xA6DD7B7B, 0x09FEF7F7, 0xB68C3A3A, 0x932FBCBC, 0x0F030C0C, 0x03FCFFFF,
+ 0xC26BA9A9, 0xBA73C9C9, 0xD96CB5B5, 0xDC6DB1B1, 0x375A6D6D, 0x15504545,
+ 0xB98F3636, 0x771B6C6C, 0x13ADBEBE, 0xDA904A4A, 0x57B9EEEE, 0xA9DE7777,
+ 0x4CBEF2F2, 0x837EFDFD, 0x55114444, 0xBDDA6767, 0x2C5D7171, 0x45400505,
+ 0x631F7C7C, 0x50104040, 0x325B6969, 0xB8DB6363, 0x220A2828, 0xC5C20707,
+ 0xF531C4C4, 0xA88A2222, 0x31A79696, 0xF9CE3737, 0x977AEDED, 0x49BFF6F6,
+ 0x992DB4B4, 0xA475D1D1, 0x90D34343, 0x5A124848, 0x58BAE2E2, 0x71E69797,
+ 0x64B6D2D2, 0x70B2C2C2, 0xAD8B2626, 0xCD68A5A5, 0xCB955E5E, 0x624B2929,
+ 0x3C0C3030, 0xCE945A5A, 0xAB76DDDD, 0x867FF9F9, 0xF1649595, 0x5DBBE6E6,
+ 0x35F2C7C7, 0x2D092424, 0xD1C61717, 0xD66FB9B9, 0xDEC51B1B, 0x94861212,
+ 0x78186060, 0x30F3C3C3, 0x897CF5F5, 0x5CEFB3B3, 0xD23AE8E8, 0xACDF7373,
+ 0x794C3535, 0xA0208080, 0x9D78E5E5, 0x56EDBBBB, 0x235E7D7D, 0xC63EF8F8,
+ 0x8BD45F5F, 0xE7C82F2F, 0xDD39E4E4, 0x68492121 };
+
+inline uint32_t SM4_T_slow(uint32_t b)
+ {
+ const uint32_t t = make_uint32(SM4_SBOX[get_byte(0,b)],
+ SM4_SBOX[get_byte(1,b)],
+ SM4_SBOX[get_byte(2,b)],
+ SM4_SBOX[get_byte(3,b)]);
+
+ // L linear transform
+ return t ^ rotl<2>(t) ^ rotl<10>(t) ^ rotl<18>(t) ^ rotl<24>(t);
+ }
+
+inline uint32_t SM4_T(uint32_t b)
+ {
+ return SM4_SBOX_T[get_byte(0,b)] ^
+ rotr< 8>(SM4_SBOX_T[get_byte(1,b)]) ^
+ rotr<16>(SM4_SBOX_T[get_byte(2,b)]) ^
+ rotr<24>(SM4_SBOX_T[get_byte(3,b)]);
+ }
+
+// Variant of T for key schedule
+inline uint32_t SM4_Tp(uint32_t b)
+ {
+ const uint32_t t = make_uint32(SM4_SBOX[get_byte(0,b)],
+ SM4_SBOX[get_byte(1,b)],
+ SM4_SBOX[get_byte(2,b)],
+ SM4_SBOX[get_byte(3,b)]);
+
+ // L' linear transform
+ return t ^ rotl<13>(t) ^ rotl<23>(t);
+ }
+
+#define SM4_E_RNDS(B, R, F) do { \
+ B##0 ^= F(B##1 ^ B##2 ^ B##3 ^ m_RK[4*R+0]); \
+ B##1 ^= F(B##2 ^ B##3 ^ B##0 ^ m_RK[4*R+1]); \
+ B##2 ^= F(B##3 ^ B##0 ^ B##1 ^ m_RK[4*R+2]); \
+ B##3 ^= F(B##0 ^ B##1 ^ B##2 ^ m_RK[4*R+3]); \
+ } while(0)
+
+#define SM4_D_RNDS(B, R, F) do { \
+ B##0 ^= F(B##1 ^ B##2 ^ B##3 ^ m_RK[4*R+3]); \
+ B##1 ^= F(B##2 ^ B##3 ^ B##0 ^ m_RK[4*R+2]); \
+ B##2 ^= F(B##3 ^ B##0 ^ B##1 ^ m_RK[4*R+1]); \
+ B##3 ^= F(B##0 ^ B##1 ^ B##2 ^ m_RK[4*R+0]); \
+ } while(0)
+
+}
+
+/*
+* SM4 Encryption
+*/
+void SM4::encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+ {
+ verify_key_set(m_RK.empty() == false);
+
+#if defined(BOTAN_HAS_SM4_ARMV8)
+ if(CPUID::has_arm_sm4())
+ return sm4_armv8_encrypt(in, out, blocks);
+#endif
+
+ while(blocks >= 2)
+ {
+ uint32_t B0 = load_be<uint32_t>(in, 0);
+ uint32_t B1 = load_be<uint32_t>(in, 1);
+ uint32_t B2 = load_be<uint32_t>(in, 2);
+ uint32_t B3 = load_be<uint32_t>(in, 3);
+
+ uint32_t C0 = load_be<uint32_t>(in, 4);
+ uint32_t C1 = load_be<uint32_t>(in, 5);
+ uint32_t C2 = load_be<uint32_t>(in, 6);
+ uint32_t C3 = load_be<uint32_t>(in, 7);
+
+ SM4_E_RNDS(B, 0, SM4_T_slow);
+ SM4_E_RNDS(C, 0, SM4_T_slow);
+ SM4_E_RNDS(B, 1, SM4_T);
+ SM4_E_RNDS(C, 1, SM4_T);
+ SM4_E_RNDS(B, 2, SM4_T);
+ SM4_E_RNDS(C, 2, SM4_T);
+ SM4_E_RNDS(B, 3, SM4_T);
+ SM4_E_RNDS(C, 3, SM4_T);
+ SM4_E_RNDS(B, 4, SM4_T);
+ SM4_E_RNDS(C, 4, SM4_T);
+ SM4_E_RNDS(B, 5, SM4_T);
+ SM4_E_RNDS(C, 5, SM4_T);
+ SM4_E_RNDS(B, 6, SM4_T);
+ SM4_E_RNDS(C, 6, SM4_T);
+ SM4_E_RNDS(B, 7, SM4_T_slow);
+ SM4_E_RNDS(C, 7, SM4_T_slow);
+
+ store_be(out, B3, B2, B1, B0, C3, C2, C1, C0);
+
+ in += 2*BLOCK_SIZE;
+ out += 2*BLOCK_SIZE;
+ blocks -= 2;
+ }
+
+ for(size_t i = 0; i != blocks; ++i)
+ {
+ uint32_t B0 = load_be<uint32_t>(in, 0);
+ uint32_t B1 = load_be<uint32_t>(in, 1);
+ uint32_t B2 = load_be<uint32_t>(in, 2);
+ uint32_t B3 = load_be<uint32_t>(in, 3);
+
+ SM4_E_RNDS(B, 0, SM4_T_slow);
+ SM4_E_RNDS(B, 1, SM4_T);
+ SM4_E_RNDS(B, 2, SM4_T);
+ SM4_E_RNDS(B, 3, SM4_T);
+ SM4_E_RNDS(B, 4, SM4_T);
+ SM4_E_RNDS(B, 5, SM4_T);
+ SM4_E_RNDS(B, 6, SM4_T);
+ SM4_E_RNDS(B, 7, SM4_T_slow);
+
+ store_be(out, B3, B2, B1, B0);
+
+ in += BLOCK_SIZE;
+ out += BLOCK_SIZE;
+ }
+ }
+
+/*
+* SM4 Decryption
+*/
+void SM4::decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+ {
+ verify_key_set(m_RK.empty() == false);
+
+#if defined(BOTAN_HAS_SM4_ARMV8)
+ if(CPUID::has_arm_sm4())
+ return sm4_armv8_decrypt(in, out, blocks);
+#endif
+
+ while(blocks >= 2)
+ {
+ uint32_t B0 = load_be<uint32_t>(in, 0);
+ uint32_t B1 = load_be<uint32_t>(in, 1);
+ uint32_t B2 = load_be<uint32_t>(in, 2);
+ uint32_t B3 = load_be<uint32_t>(in, 3);
+
+ uint32_t C0 = load_be<uint32_t>(in, 4);
+ uint32_t C1 = load_be<uint32_t>(in, 5);
+ uint32_t C2 = load_be<uint32_t>(in, 6);
+ uint32_t C3 = load_be<uint32_t>(in, 7);
+
+ SM4_D_RNDS(B, 7, SM4_T_slow);
+ SM4_D_RNDS(C, 7, SM4_T_slow);
+ SM4_D_RNDS(B, 6, SM4_T);
+ SM4_D_RNDS(C, 6, SM4_T);
+ SM4_D_RNDS(B, 5, SM4_T);
+ SM4_D_RNDS(C, 5, SM4_T);
+ SM4_D_RNDS(B, 4, SM4_T);
+ SM4_D_RNDS(C, 4, SM4_T);
+ SM4_D_RNDS(B, 3, SM4_T);
+ SM4_D_RNDS(C, 3, SM4_T);
+ SM4_D_RNDS(B, 2, SM4_T);
+ SM4_D_RNDS(C, 2, SM4_T);
+ SM4_D_RNDS(B, 1, SM4_T);
+ SM4_D_RNDS(C, 1, SM4_T);
+ SM4_D_RNDS(B, 0, SM4_T_slow);
+ SM4_D_RNDS(C, 0, SM4_T_slow);
+
+ store_be(out, B3, B2, B1, B0, C3, C2, C1, C0);
+
+ in += 2*BLOCK_SIZE;
+ out += 2*BLOCK_SIZE;
+ blocks -= 2;
+ }
+
+ for(size_t i = 0; i != blocks; ++i)
+ {
+ uint32_t B0 = load_be<uint32_t>(in, 0);
+ uint32_t B1 = load_be<uint32_t>(in, 1);
+ uint32_t B2 = load_be<uint32_t>(in, 2);
+ uint32_t B3 = load_be<uint32_t>(in, 3);
+
+ SM4_D_RNDS(B, 7, SM4_T_slow);
+ SM4_D_RNDS(B, 6, SM4_T);
+ SM4_D_RNDS(B, 5, SM4_T);
+ SM4_D_RNDS(B, 4, SM4_T);
+ SM4_D_RNDS(B, 3, SM4_T);
+ SM4_D_RNDS(B, 2, SM4_T);
+ SM4_D_RNDS(B, 1, SM4_T);
+ SM4_D_RNDS(B, 0, SM4_T_slow);
+
+ store_be(out, B3, B2, B1, B0);
+
+ in += BLOCK_SIZE;
+ out += BLOCK_SIZE;
+ }
+ }
+
+#undef SM4_E_RNDS
+#undef SM4_D_RNDS
+
+/*
+* SM4 Key Schedule
+*/
+void SM4::key_schedule(const uint8_t key[], size_t)
+ {
+ // System parameter or family key
+ const uint32_t FK[4] = { 0xa3b1bac6, 0x56aa3350, 0x677d9197, 0xb27022dc };
+
+ const uint32_t CK[32] = {
+ 0x00070E15, 0x1C232A31, 0x383F464D, 0x545B6269,
+ 0x70777E85, 0x8C939AA1, 0xA8AFB6BD, 0xC4CBD2D9,
+ 0xE0E7EEF5, 0xFC030A11, 0x181F262D, 0x343B4249,
+ 0x50575E65, 0x6C737A81, 0x888F969D, 0xA4ABB2B9,
+ 0xC0C7CED5, 0xDCE3EAF1, 0xF8FF060D, 0x141B2229,
+ 0x30373E45, 0x4C535A61, 0x686F767D, 0x848B9299,
+ 0xA0A7AEB5, 0xBCC3CAD1, 0xD8DFE6ED, 0xF4FB0209,
+ 0x10171E25, 0x2C333A41, 0x484F565D, 0x646B7279
+ };
+
+ secure_vector<uint32_t> K(4);
+ K[0] = load_be<uint32_t>(key, 0) ^ FK[0];
+ K[1] = load_be<uint32_t>(key, 1) ^ FK[1];
+ K[2] = load_be<uint32_t>(key, 2) ^ FK[2];
+ K[3] = load_be<uint32_t>(key, 3) ^ FK[3];
+
+ m_RK.resize(32);
+ for(size_t i = 0; i != 32; ++i)
+ {
+ K[i % 4] ^= SM4_Tp(K[(i+1)%4] ^ K[(i+2)%4] ^ K[(i+3)%4] ^ CK[i]);
+ m_RK[i] = K[i % 4];
+ }
+ }
+
+void SM4::clear()
+ {
+ zap(m_RK);
+ }
+
+size_t SM4::parallelism() const
+ {
+#if defined(BOTAN_HAS_SM4_ARMV8)
+ if(CPUID::has_arm_sm4())
+ {
+ return 4;
+ }
+#endif
+
+ return 1;
+ }
+
+std::string SM4::provider() const
+ {
+#if defined(BOTAN_HAS_SM4_ARMV8)
+ if(CPUID::has_arm_sm4())
+ {
+ return "armv8";
+ }
+#endif
+
+ return "base";
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/block/sm4/sm4.h b/comm/third_party/botan/src/lib/block/sm4/sm4.h
new file mode 100644
index 0000000000..637ffd4bee
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/sm4/sm4.h
@@ -0,0 +1,45 @@
+/*
+* SM4
+* (C) 2017 Ribose Inc
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_SM4_H_
+#define BOTAN_SM4_H_
+
+#include <botan/block_cipher.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(sm4.h)
+
+namespace Botan {
+
+/**
+* SM4
+*/
+class BOTAN_PUBLIC_API(2,2) SM4 final : public Block_Cipher_Fixed_Params<16, 16>
+ {
+ public:
+ void encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+ void decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+
+ void clear() override;
+ std::string name() const override { return "SM4"; }
+ BlockCipher* clone() const override { return new SM4; }
+
+ std::string provider() const override;
+ size_t parallelism() const override;
+ private:
+ void key_schedule(const uint8_t[], size_t) override;
+
+#if defined(BOTAN_HAS_SM4_ARMV8)
+ void sm4_armv8_encrypt(const uint8_t in[], uint8_t out[], size_t blocks) const;
+ void sm4_armv8_decrypt(const uint8_t in[], uint8_t out[], size_t blocks) const;
+#endif
+
+ secure_vector<uint32_t> m_RK;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/block/sm4/sm4_armv8/info.txt b/comm/third_party/botan/src/lib/block/sm4/sm4_armv8/info.txt
new file mode 100644
index 0000000000..03ff034395
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/sm4/sm4_armv8/info.txt
@@ -0,0 +1,11 @@
+<defines>
+SM4_ARMV8 -> 20180709
+</defines>
+
+<isa>
+armv8sm4
+</isa>
+
+<cc>
+gcc:8
+</cc>
diff --git a/comm/third_party/botan/src/lib/block/sm4/sm4_armv8/sm4_armv8.cpp b/comm/third_party/botan/src/lib/block/sm4/sm4_armv8/sm4_armv8.cpp
new file mode 100644
index 0000000000..9e7a71a102
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/sm4/sm4_armv8/sm4_armv8.cpp
@@ -0,0 +1,174 @@
+/*
+* (C) 2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/sm4.h>
+#include <arm_neon.h>
+
+namespace Botan {
+
+namespace {
+
+static const uint8_t qswap_tbl[16] = {
+ 12, 13, 14, 15, 8, 9, 10, 11, 4, 5, 6, 7, 0, 1, 2, 3
+};
+
+static 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)));
+ }
+
+inline uint32x4_t bswap_32(uint32x4_t B)
+ {
+ return vreinterpretq_u32_u8(vrev32q_u8(vreinterpretq_u8_u32(B)));
+ }
+
+/*
+ Swap both the quad-words and bytes within each word
+ equivalent to return bswap_32(qswap_32(B))
+*/
+inline uint32x4_t bqswap_32(uint32x4_t B)
+ {
+ return vreinterpretq_u32_u8(vqtbl1q_u8(vreinterpretq_u8_u32(B), vld1q_u8(bswap_tbl)));
+ }
+
+#define SM4_E(B0, B1, B2, B3, K) do { \
+ B0 = vsm4eq_u32(B0, K); \
+ B1 = vsm4eq_u32(B1, K); \
+ B2 = vsm4eq_u32(B2, K); \
+ B3 = vsm4eq_u32(B3, K); \
+ } while(0)
+
+}
+
+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]);
+ 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]);
+ const uint32x4_t K4 = vld1q_u32(&m_RK[16]);
+ const uint32x4_t K5 = vld1q_u32(&m_RK[20]);
+ const uint32x4_t K6 = vld1q_u32(&m_RK[24]);
+ const uint32x4_t K7 = vld1q_u32(&m_RK[28]);
+
+ const uint32_t* input32 = reinterpret_cast<const uint32_t*>(reinterpret_cast<const void*>(input8));
+ uint32_t* output32 = reinterpret_cast<uint32_t*>(reinterpret_cast<void*>(output8));
+
+ while(blocks >= 4)
+ {
+ uint32x4_t B0 = bswap_32(vld1q_u32(input32));
+ uint32x4_t B1 = bswap_32(vld1q_u32(input32+4));
+ uint32x4_t B2 = bswap_32(vld1q_u32(input32+8));
+ uint32x4_t B3 = bswap_32(vld1q_u32(input32+12));
+
+ SM4_E(B0, B1, B2, B3, K0);
+ SM4_E(B0, B1, B2, B3, K1);
+ SM4_E(B0, B1, B2, B3, K2);
+ SM4_E(B0, B1, B2, B3, K3);
+ SM4_E(B0, B1, B2, B3, K4);
+ SM4_E(B0, B1, B2, B3, K5);
+ SM4_E(B0, B1, B2, B3, K6);
+ SM4_E(B0, B1, B2, B3, K7);
+
+ vst1q_u32(output32 , bqswap_32(B0));
+ vst1q_u32(output32+ 4, bqswap_32(B1));
+ vst1q_u32(output32+ 8, bqswap_32(B2));
+ vst1q_u32(output32+12, bqswap_32(B3));
+
+ input32 += 4*4;
+ output32 += 4*4;
+ blocks -= 4;
+ }
+
+ for(size_t i = 0; i != blocks; ++i)
+ {
+ uint32x4_t B = bswap_32(vld1q_u32(input32));
+
+ B = vsm4eq_u32(B, K0);
+ B = vsm4eq_u32(B, K1);
+ B = vsm4eq_u32(B, K2);
+ B = vsm4eq_u32(B, K3);
+ B = vsm4eq_u32(B, K4);
+ B = vsm4eq_u32(B, K5);
+ B = vsm4eq_u32(B, K6);
+ B = vsm4eq_u32(B, K7);
+
+ vst1q_u32(output32, bqswap_32(B));
+
+ input32 += 4;
+ output32 += 4;
+ }
+ }
+
+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]));
+ 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]));
+ const uint32x4_t K4 = qswap_32(vld1q_u32(&m_RK[16]));
+ const uint32x4_t K5 = qswap_32(vld1q_u32(&m_RK[20]));
+ 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<const uint32_t*>(reinterpret_cast<const void*>(input8));
+ uint32_t* output32 = reinterpret_cast<uint32_t*>(reinterpret_cast<void*>(output8));
+
+ while(blocks >= 4)
+ {
+ uint32x4_t B0 = bswap_32(vld1q_u32(input32));
+ uint32x4_t B1 = bswap_32(vld1q_u32(input32+4));
+ uint32x4_t B2 = bswap_32(vld1q_u32(input32+8));
+ uint32x4_t B3 = bswap_32(vld1q_u32(input32+12));
+
+ SM4_E(B0, B1, B2, B3, K7);
+ SM4_E(B0, B1, B2, B3, K6);
+ SM4_E(B0, B1, B2, B3, K5);
+ SM4_E(B0, B1, B2, B3, K4);
+ SM4_E(B0, B1, B2, B3, K3);
+ SM4_E(B0, B1, B2, B3, K2);
+ SM4_E(B0, B1, B2, B3, K1);
+ SM4_E(B0, B1, B2, B3, K0);
+
+ vst1q_u32(output32 , bqswap_32(B0));
+ vst1q_u32(output32+ 4, bqswap_32(B1));
+ vst1q_u32(output32+ 8, bqswap_32(B2));
+ vst1q_u32(output32+12, bqswap_32(B3));
+
+ input32 += 4*4;
+ output32 += 4*4;
+ blocks -= 4;
+ }
+
+ for(size_t i = 0; i != blocks; ++i)
+ {
+ uint32x4_t B = bswap_32(vld1q_u32(input32));
+
+ B = vsm4eq_u32(B, K7);
+ B = vsm4eq_u32(B, K6);
+ B = vsm4eq_u32(B, K5);
+ B = vsm4eq_u32(B, K4);
+ B = vsm4eq_u32(B, K3);
+ B = vsm4eq_u32(B, K2);
+ B = vsm4eq_u32(B, K1);
+ B = vsm4eq_u32(B, K0);
+
+ vst1q_u32(output32, bqswap_32(B));
+
+ input32 += 4;
+ output32 += 4;
+ }
+ }
+
+#undef SM4_E
+
+}
diff --git a/comm/third_party/botan/src/lib/block/threefish_512/info.txt b/comm/third_party/botan/src/lib/block/threefish_512/info.txt
new file mode 100644
index 0000000000..e122236794
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/threefish_512/info.txt
@@ -0,0 +1,3 @@
+<defines>
+THREEFISH_512 -> 20131224
+</defines>
diff --git a/comm/third_party/botan/src/lib/block/threefish_512/threefish.h b/comm/third_party/botan/src/lib/block/threefish_512/threefish.h
new file mode 100644
index 0000000000..f866a717ff
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/threefish_512/threefish.h
@@ -0,0 +1,17 @@
+/*
+* Threefish
+* (C) 2013,2014 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_THREEFISH_H_
+#define BOTAN_THREEFISH_H_
+
+// This header is deprecated and will be removed in a future major release
+
+#include <botan/threefish_512.h>
+
+BOTAN_DEPRECATED_HEADER(threefish.h)
+
+#endif
diff --git a/comm/third_party/botan/src/lib/block/threefish_512/threefish_512.cpp b/comm/third_party/botan/src/lib/block/threefish_512/threefish_512.cpp
new file mode 100644
index 0000000000..e34a9e0dca
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/threefish_512/threefish_512.cpp
@@ -0,0 +1,273 @@
+/*
+* Threefish-512
+* (C) 2013,2014,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/threefish_512.h>
+#include <botan/loadstor.h>
+#include <botan/rotate.h>
+#include <botan/cpuid.h>
+
+namespace Botan {
+
+#define THREEFISH_ROUND(X0,X1,X2,X3,X4,X5,X6,X7,ROT1,ROT2,ROT3,ROT4) \
+ do { \
+ X0 += X4; \
+ X1 += X5; \
+ X2 += X6; \
+ X3 += X7; \
+ X4 = rotl<ROT1>(X4); \
+ X5 = rotl<ROT2>(X5); \
+ X6 = rotl<ROT3>(X6); \
+ X7 = rotl<ROT4>(X7); \
+ X4 ^= X0; \
+ X5 ^= X1; \
+ X6 ^= X2; \
+ X7 ^= X3; \
+ } while(0)
+
+#define THREEFISH_INJECT_KEY(r) \
+ do { \
+ X0 += m_K[(r ) % 9]; \
+ X1 += m_K[(r+1) % 9]; \
+ X2 += m_K[(r+2) % 9]; \
+ X3 += m_K[(r+3) % 9]; \
+ X4 += m_K[(r+4) % 9]; \
+ X5 += m_K[(r+5) % 9] + m_T[(r ) % 3]; \
+ X6 += m_K[(r+6) % 9] + m_T[(r+1) % 3]; \
+ X7 += m_K[(r+7) % 9] + (r); \
+ } while(0)
+
+#define THREEFISH_ENC_8_ROUNDS(R1,R2) \
+ do { \
+ THREEFISH_ROUND(X0,X2,X4,X6, X1,X3,X5,X7, 46,36,19,37); \
+ THREEFISH_ROUND(X2,X4,X6,X0, X1,X7,X5,X3, 33,27,14,42); \
+ THREEFISH_ROUND(X4,X6,X0,X2, X1,X3,X5,X7, 17,49,36,39); \
+ THREEFISH_ROUND(X6,X0,X2,X4, X1,X7,X5,X3, 44, 9,54,56); \
+ THREEFISH_INJECT_KEY(R1); \
+ \
+ THREEFISH_ROUND(X0,X2,X4,X6, X1,X3,X5,X7, 39,30,34,24); \
+ THREEFISH_ROUND(X2,X4,X6,X0, X1,X7,X5,X3, 13,50,10,17); \
+ THREEFISH_ROUND(X4,X6,X0,X2, X1,X3,X5,X7, 25,29,39,43); \
+ THREEFISH_ROUND(X6,X0,X2,X4, X1,X7,X5,X3, 8,35,56,22); \
+ THREEFISH_INJECT_KEY(R2); \
+ } while(0)
+
+void Threefish_512::skein_feedfwd(const secure_vector<uint64_t>& M,
+ const secure_vector<uint64_t>& T)
+ {
+ BOTAN_ASSERT(m_K.size() == 9, "Key was set");
+ BOTAN_ASSERT(M.size() == 8, "Single block");
+
+ m_T[0] = T[0];
+ m_T[1] = T[1];
+ m_T[2] = T[0] ^ T[1];
+
+ uint64_t X0 = M[0];
+ uint64_t X1 = M[1];
+ uint64_t X2 = M[2];
+ uint64_t X3 = M[3];
+ uint64_t X4 = M[4];
+ uint64_t X5 = M[5];
+ uint64_t X6 = M[6];
+ uint64_t X7 = M[7];
+
+ THREEFISH_INJECT_KEY(0);
+
+ THREEFISH_ENC_8_ROUNDS(1,2);
+ THREEFISH_ENC_8_ROUNDS(3,4);
+ THREEFISH_ENC_8_ROUNDS(5,6);
+ THREEFISH_ENC_8_ROUNDS(7,8);
+ THREEFISH_ENC_8_ROUNDS(9,10);
+ THREEFISH_ENC_8_ROUNDS(11,12);
+ THREEFISH_ENC_8_ROUNDS(13,14);
+ THREEFISH_ENC_8_ROUNDS(15,16);
+ THREEFISH_ENC_8_ROUNDS(17,18);
+
+ m_K[0] = M[0] ^ X0;
+ m_K[1] = M[1] ^ X1;
+ m_K[2] = M[2] ^ X2;
+ m_K[3] = M[3] ^ X3;
+ m_K[4] = M[4] ^ X4;
+ m_K[5] = M[5] ^ X5;
+ m_K[6] = M[6] ^ X6;
+ m_K[7] = M[7] ^ X7;
+
+ m_K[8] = m_K[0] ^ m_K[1] ^ m_K[2] ^ m_K[3] ^
+ m_K[4] ^ m_K[5] ^ m_K[6] ^ m_K[7] ^ 0x1BD11BDAA9FC1A22;
+ }
+
+size_t Threefish_512::parallelism() const
+ {
+#if defined(BOTAN_HAS_THREEFISH_512_AVX2)
+ if(CPUID::has_avx2())
+ {
+ return 2;
+ }
+#endif
+
+ return 1;
+ }
+
+std::string Threefish_512::provider() const
+ {
+#if defined(BOTAN_HAS_THREEFISH_512_AVX2)
+ if(CPUID::has_avx2())
+ {
+ return "avx2";
+ }
+#endif
+
+ return "base";
+ }
+
+void Threefish_512::encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+ {
+ verify_key_set(m_K.empty() == false);
+
+#if defined(BOTAN_HAS_THREEFISH_512_AVX2)
+ if(CPUID::has_avx2())
+ {
+ return avx2_encrypt_n(in, out, blocks);
+ }
+#endif
+
+ BOTAN_PARALLEL_SIMD_FOR(size_t i = 0; i < blocks; ++i)
+ {
+ uint64_t X0, X1, X2, X3, X4, X5, X6, X7;
+ load_le(in + BLOCK_SIZE*i, X0, X1, X2, X3, X4, X5, X6, X7);
+
+ THREEFISH_INJECT_KEY(0);
+
+ THREEFISH_ENC_8_ROUNDS(1,2);
+ THREEFISH_ENC_8_ROUNDS(3,4);
+ THREEFISH_ENC_8_ROUNDS(5,6);
+ THREEFISH_ENC_8_ROUNDS(7,8);
+ THREEFISH_ENC_8_ROUNDS(9,10);
+ THREEFISH_ENC_8_ROUNDS(11,12);
+ THREEFISH_ENC_8_ROUNDS(13,14);
+ THREEFISH_ENC_8_ROUNDS(15,16);
+ THREEFISH_ENC_8_ROUNDS(17,18);
+
+ store_le(out + BLOCK_SIZE*i, X0, X1, X2, X3, X4, X5, X6, X7);
+ }
+ }
+
+#undef THREEFISH_ENC_8_ROUNDS
+#undef THREEFISH_INJECT_KEY
+#undef THREEFISH_ROUND
+
+void Threefish_512::decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+ {
+ verify_key_set(m_K.empty() == false);
+
+#if defined(BOTAN_HAS_THREEFISH_512_AVX2)
+ if(CPUID::has_avx2())
+ {
+ return avx2_decrypt_n(in, out, blocks);
+ }
+#endif
+
+#define THREEFISH_ROUND(X0,X1,X2,X3,X4,X5,X6,X7,ROT1,ROT2,ROT3,ROT4) \
+ do { \
+ X4 ^= X0; \
+ X5 ^= X1; \
+ X6 ^= X2; \
+ X7 ^= X3; \
+ X4 = rotr<ROT1>(X4); \
+ X5 = rotr<ROT2>(X5); \
+ X6 = rotr<ROT3>(X6); \
+ X7 = rotr<ROT4>(X7); \
+ X0 -= X4; \
+ X1 -= X5; \
+ X2 -= X6; \
+ X3 -= X7; \
+ } while(0)
+
+#define THREEFISH_INJECT_KEY(r) \
+ do { \
+ X0 -= m_K[(r ) % 9]; \
+ X1 -= m_K[(r+1) % 9]; \
+ X2 -= m_K[(r+2) % 9]; \
+ X3 -= m_K[(r+3) % 9]; \
+ X4 -= m_K[(r+4) % 9]; \
+ X5 -= m_K[(r+5) % 9] + m_T[(r ) % 3]; \
+ X6 -= m_K[(r+6) % 9] + m_T[(r+1) % 3]; \
+ X7 -= m_K[(r+7) % 9] + (r); \
+ } while(0)
+
+#define THREEFISH_DEC_8_ROUNDS(R1,R2) \
+ do { \
+ THREEFISH_ROUND(X6,X0,X2,X4, X1,X7,X5,X3, 8,35,56,22); \
+ THREEFISH_ROUND(X4,X6,X0,X2, X1,X3,X5,X7, 25,29,39,43); \
+ THREEFISH_ROUND(X2,X4,X6,X0, X1,X7,X5,X3, 13,50,10,17); \
+ THREEFISH_ROUND(X0,X2,X4,X6, X1,X3,X5,X7, 39,30,34,24); \
+ THREEFISH_INJECT_KEY(R1); \
+ \
+ THREEFISH_ROUND(X6,X0,X2,X4, X1,X7,X5,X3, 44, 9,54,56); \
+ THREEFISH_ROUND(X4,X6,X0,X2, X1,X3,X5,X7, 17,49,36,39); \
+ THREEFISH_ROUND(X2,X4,X6,X0, X1,X7,X5,X3, 33,27,14,42); \
+ THREEFISH_ROUND(X0,X2,X4,X6, X1,X3,X5,X7, 46,36,19,37); \
+ THREEFISH_INJECT_KEY(R2); \
+ } while(0)
+
+ BOTAN_PARALLEL_SIMD_FOR(size_t i = 0; i < blocks; ++i)
+ {
+ uint64_t X0, X1, X2, X3, X4, X5, X6, X7;
+ load_le(in + BLOCK_SIZE*i, X0, X1, X2, X3, X4, X5, X6, X7);
+
+ THREEFISH_INJECT_KEY(18);
+
+ THREEFISH_DEC_8_ROUNDS(17,16);
+ THREEFISH_DEC_8_ROUNDS(15,14);
+ THREEFISH_DEC_8_ROUNDS(13,12);
+ THREEFISH_DEC_8_ROUNDS(11,10);
+ THREEFISH_DEC_8_ROUNDS(9,8);
+ THREEFISH_DEC_8_ROUNDS(7,6);
+ THREEFISH_DEC_8_ROUNDS(5,4);
+ THREEFISH_DEC_8_ROUNDS(3,2);
+ THREEFISH_DEC_8_ROUNDS(1,0);
+
+ store_le(out + BLOCK_SIZE*i, X0, X1, X2, X3, X4, X5, X6, X7);
+ }
+
+#undef THREEFISH_DEC_8_ROUNDS
+#undef THREEFISH_INJECT_KEY
+#undef THREEFISH_ROUND
+ }
+
+void Threefish_512::set_tweak(const uint8_t tweak[], size_t len)
+ {
+ BOTAN_ARG_CHECK(len == 16, "Threefish-512 requires 128 bit tweak");
+
+ m_T.resize(3);
+ m_T[0] = load_le<uint64_t>(tweak, 0);
+ m_T[1] = load_le<uint64_t>(tweak, 1);
+ m_T[2] = m_T[0] ^ m_T[1];
+ }
+
+void Threefish_512::key_schedule(const uint8_t key[], size_t)
+ {
+ // todo: define key schedule for smaller keys
+ m_K.resize(9);
+
+ for(size_t i = 0; i != 8; ++i)
+ m_K[i] = load_le<uint64_t>(key, i);
+
+ m_K[8] = m_K[0] ^ m_K[1] ^ m_K[2] ^ m_K[3] ^
+ m_K[4] ^ m_K[5] ^ m_K[6] ^ m_K[7] ^ 0x1BD11BDAA9FC1A22;
+
+ // Reset tweak to all zeros on key reset
+ m_T.resize(3);
+ zeroise(m_T);
+ }
+
+void Threefish_512::clear()
+ {
+ zap(m_K);
+ zap(m_T);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/block/threefish_512/threefish_512.h b/comm/third_party/botan/src/lib/block/threefish_512/threefish_512.h
new file mode 100644
index 0000000000..f3adf71a92
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/threefish_512/threefish_512.h
@@ -0,0 +1,57 @@
+/*
+* Threefish-512
+* (C) 2013,2014 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_THREEFISH_512_H_
+#define BOTAN_THREEFISH_512_H_
+
+#include <botan/block_cipher.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(threefish_512.h)
+
+namespace Botan {
+
+/**
+* Threefish-512
+*/
+class BOTAN_PUBLIC_API(2,0) Threefish_512 final :
+ public Block_Cipher_Fixed_Params<64, 64, 0, 1, Tweakable_Block_Cipher>
+ {
+ public:
+ void encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+ void decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+
+ void set_tweak(const uint8_t tweak[], size_t len) override;
+
+ void clear() override;
+ std::string provider() const override;
+ std::string name() const override { return "Threefish-512"; }
+ BlockCipher* clone() const override { return new Threefish_512; }
+ size_t parallelism() const override;
+
+ private:
+
+#if defined(BOTAN_HAS_THREEFISH_512_AVX2)
+ void avx2_encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const;
+ void avx2_decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const;
+#endif
+
+ void key_schedule(const uint8_t key[], size_t key_len) override;
+
+ // Interface for Skein
+ friend class Skein_512;
+
+ void skein_feedfwd(const secure_vector<uint64_t>& M,
+ const secure_vector<uint64_t>& T);
+
+ // Private data
+ secure_vector<uint64_t> m_T;
+ secure_vector<uint64_t> m_K;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/block/threefish_512/threefish_512_avx2/info.txt b/comm/third_party/botan/src/lib/block/threefish_512/threefish_512_avx2/info.txt
new file mode 100644
index 0000000000..b5374c4406
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/threefish_512/threefish_512_avx2/info.txt
@@ -0,0 +1,14 @@
+<defines>
+THREEFISH_512_AVX2 -> 20160903
+</defines>
+
+<isa>
+avx2
+</isa>
+
+<cc>
+gcc
+clang
+msvc
+icc
+</cc>
diff --git a/comm/third_party/botan/src/lib/block/threefish_512/threefish_512_avx2/threefish_512_avx2.cpp b/comm/third_party/botan/src/lib/block/threefish_512/threefish_512_avx2/threefish_512_avx2.cpp
new file mode 100644
index 0000000000..0ceea2d7f0
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/threefish_512/threefish_512_avx2/threefish_512_avx2.cpp
@@ -0,0 +1,444 @@
+/*
+* Threefish-512 using AVX2
+* (C) 2013,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/threefish_512.h>
+#include <immintrin.h>
+
+namespace Botan {
+
+namespace {
+
+BOTAN_FUNC_ISA("avx2")
+inline void interleave_epi64(__m256i& X0, __m256i& X1)
+ {
+ // interleave X0 and X1 qwords
+ // (X0,X1,X2,X3),(X4,X5,X6,X7) -> (X0,X2,X4,X6),(X1,X3,X5,X7)
+
+ const __m256i T0 = _mm256_unpacklo_epi64(X0, X1);
+ const __m256i T1 = _mm256_unpackhi_epi64(X0, X1);
+
+ X0 = _mm256_permute4x64_epi64(T0, _MM_SHUFFLE(3,1,2,0));
+ X1 = _mm256_permute4x64_epi64(T1, _MM_SHUFFLE(3,1,2,0));
+ }
+
+BOTAN_FUNC_ISA("avx2")
+inline void deinterleave_epi64(__m256i& X0, __m256i& X1)
+ {
+ const __m256i T0 = _mm256_permute4x64_epi64(X0, _MM_SHUFFLE(3,1,2,0));
+ const __m256i T1 = _mm256_permute4x64_epi64(X1, _MM_SHUFFLE(3,1,2,0));
+
+ X0 = _mm256_unpacklo_epi64(T0, T1);
+ X1 = _mm256_unpackhi_epi64(T0, T1);
+ }
+
+BOTAN_FUNC_ISA("avx2")
+inline void rotate_keys(__m256i& R0, __m256i& R1, __m256i R2)
+ {
+ /*
+ Behold. The key schedule progresses like so. The values
+ loop back to the originals after the rounds are complete
+ so we don't need to reload for starting the next block.
+
+ R0 R1 R2
+ K1,K2,K3 (7,5,3,1),(8,6,4,2),(0,7,5,3)
+ K3,K4,K5 (0,7,5,3),(1,8,6,4),(2,0,7,5)
+ K5,K6,K7 (2,0,7,5),(3,1,8,6),(4,2,0,7)
+
+ K7,K8,K0 (4,2,0,7),(5,3,1,8),(6,4,2,0)
+ K0,K1,K2 (6,4,2,0),(7,5,3,1),(8,6,4,2)
+ K2,K3,K4 (8,6,4,2),(0,7,5,3),(1,8,6,4)
+
+ K4,K5,K6 (1,8,6,4),(2,0,7,5),(3,1,8,6)
+ K6,K7,K8 (3,1,8,6),(4,2,0,7),(5,3,1,8)
+ K8,K0,K1 (5,3,1,8),(6,4,2,0),(7,5,3,1)
+
+ To compute the values for the next round:
+ X0 is X2 from the last round
+ X1 becomes (X0[4],X1[1:3])
+ X2 becomes (X1[4],X2[1:3])
+
+ Uses 3 permutes and 2 blends, is there a faster way?
+ */
+ __m256i T0 = _mm256_permute4x64_epi64(R0, _MM_SHUFFLE(0,0,0,0));
+ __m256i T1 = _mm256_permute4x64_epi64(R1, _MM_SHUFFLE(0,3,2,1));
+ __m256i T2 = _mm256_permute4x64_epi64(R2, _MM_SHUFFLE(0,3,2,1));
+
+ R0 = _mm256_blend_epi32(T1, T0, 0xC0);
+ R1 = _mm256_blend_epi32(T2, T1, 0xC0);
+ }
+
+
+}
+
+BOTAN_FUNC_ISA("avx2")
+void Threefish_512::avx2_encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+ {
+ _mm256_zeroupper();
+
+ const uint64_t* K = m_K.data();
+ const uint64_t* T_64 = m_T.data();
+
+ const __m256i ROTATE_1 = _mm256_set_epi64x(37,19,36,46);
+ const __m256i ROTATE_2 = _mm256_set_epi64x(42,14,27,33);
+ const __m256i ROTATE_3 = _mm256_set_epi64x(39,36,49,17);
+ const __m256i ROTATE_4 = _mm256_set_epi64x(56,54, 9,44);
+ const __m256i ROTATE_5 = _mm256_set_epi64x(24,34,30,39);
+ const __m256i ROTATE_6 = _mm256_set_epi64x(17,10,50,13);
+ const __m256i ROTATE_7 = _mm256_set_epi64x(43,39,29,25);
+ const __m256i ROTATE_8 = _mm256_set_epi64x(22,56,35, 8);
+
+#define THREEFISH_ROUND(X0, X1, SHL) \
+ do { \
+ const __m256i SHR = _mm256_sub_epi64(_mm256_set1_epi64x(64), SHL); \
+ X0 = _mm256_add_epi64(X0, X1); \
+ X1 = _mm256_or_si256(_mm256_sllv_epi64(X1, SHL), _mm256_srlv_epi64(X1, SHR)); \
+ X1 = _mm256_xor_si256(X1, X0); \
+ X0 = _mm256_permute4x64_epi64(X0, _MM_SHUFFLE(0, 3, 2, 1)); \
+ X1 = _mm256_permute4x64_epi64(X1, _MM_SHUFFLE(1, 2, 3, 0)); \
+ } while(0)
+
+#define THREEFISH_ROUND_2(X0, X1, X2, X3, SHL) \
+ do { \
+ const __m256i SHR = _mm256_sub_epi64(_mm256_set1_epi64x(64), SHL); \
+ X0 = _mm256_add_epi64(X0, X1); \
+ X2 = _mm256_add_epi64(X2, X3); \
+ X1 = _mm256_or_si256(_mm256_sllv_epi64(X1, SHL), _mm256_srlv_epi64(X1, SHR)); \
+ X3 = _mm256_or_si256(_mm256_sllv_epi64(X3, SHL), _mm256_srlv_epi64(X3, SHR)); \
+ X1 = _mm256_xor_si256(X1, X0); \
+ X3 = _mm256_xor_si256(X3, X2); \
+ X0 = _mm256_permute4x64_epi64(X0, _MM_SHUFFLE(0, 3, 2, 1)); \
+ X2 = _mm256_permute4x64_epi64(X2, _MM_SHUFFLE(0, 3, 2, 1)); \
+ X1 = _mm256_permute4x64_epi64(X1, _MM_SHUFFLE(1, 2, 3, 0)); \
+ X3 = _mm256_permute4x64_epi64(X3, _MM_SHUFFLE(1, 2, 3, 0)); \
+ } while(0)
+
+#define THREEFISH_INJECT_KEY(X0, X1, R, K0, K1, T0I, T1I) \
+ do { \
+ const __m256i T0 = _mm256_permute4x64_epi64(T, _MM_SHUFFLE(T0I, 0, 0, 0)); \
+ const __m256i T1 = _mm256_permute4x64_epi64(T, _MM_SHUFFLE(0, T1I, 0, 0)); \
+ X0 = _mm256_add_epi64(X0, K0); \
+ X1 = _mm256_add_epi64(X1, K1); \
+ X1 = _mm256_add_epi64(X1, _mm256_set_epi64x(R,0,0,0)); \
+ X0 = _mm256_add_epi64(X0, T0); \
+ X1 = _mm256_add_epi64(X1, T1); \
+ } while(0)
+
+#define THREEFISH_INJECT_KEY_2(X0, X1, X2, X3, R, K0, K1, T0I, T1I) \
+ do { \
+ const __m256i T0 = _mm256_permute4x64_epi64(T, _MM_SHUFFLE(T0I, 0, 0, 0)); \
+ __m256i T1 = _mm256_permute4x64_epi64(T, _MM_SHUFFLE(0, T1I, 0, 0)); \
+ X0 = _mm256_add_epi64(X0, K0); \
+ X2 = _mm256_add_epi64(X2, K0); \
+ X1 = _mm256_add_epi64(X1, K1); \
+ X3 = _mm256_add_epi64(X3, K1); \
+ T1 = _mm256_add_epi64(T1, _mm256_set_epi64x(R,0,0,0)); \
+ X0 = _mm256_add_epi64(X0, T0); \
+ X2 = _mm256_add_epi64(X2, T0); \
+ X1 = _mm256_add_epi64(X1, T1); \
+ X3 = _mm256_add_epi64(X3, T1); \
+ } while(0)
+
+#define THREEFISH_ENC_8_ROUNDS(X0, X1, R, K0, K1, K2, T0, T1, T2) \
+ do { \
+ rotate_keys(K1, K2, K0); \
+ THREEFISH_ROUND(X0, X1, ROTATE_1); \
+ THREEFISH_ROUND(X0, X1, ROTATE_2); \
+ THREEFISH_ROUND(X0, X1, ROTATE_3); \
+ THREEFISH_ROUND(X0, X1, ROTATE_4); \
+ THREEFISH_INJECT_KEY(X0, X1, R, K0, K1, T0, T1); \
+ \
+ THREEFISH_ROUND(X0, X1, ROTATE_5); \
+ THREEFISH_ROUND(X0, X1, ROTATE_6); \
+ THREEFISH_ROUND(X0, X1, ROTATE_7); \
+ THREEFISH_ROUND(X0, X1, ROTATE_8); \
+ THREEFISH_INJECT_KEY(X0, X1, R+1, K1, K2, T2, T0); \
+ } while(0)
+
+#define THREEFISH_ENC_2_8_ROUNDS(X0, X1, X2, X3, R, K0, K1, K2, T0, T1, T2) \
+ do { \
+ rotate_keys(K1, K2, K0); \
+ THREEFISH_ROUND_2(X0, X1, X2, X3, ROTATE_1); \
+ THREEFISH_ROUND_2(X0, X1, X2, X3, ROTATE_2); \
+ THREEFISH_ROUND_2(X0, X1, X2, X3, ROTATE_3); \
+ THREEFISH_ROUND_2(X0, X1, X2, X3, ROTATE_4); \
+ THREEFISH_INJECT_KEY_2(X0, X1, X2, X3, R, K0, K1, T0, T1); \
+ \
+ THREEFISH_ROUND_2(X0, X1, X2, X3, ROTATE_5); \
+ THREEFISH_ROUND_2(X0, X1, X2, X3, ROTATE_6); \
+ THREEFISH_ROUND_2(X0, X1, X2, X3, ROTATE_7); \
+ THREEFISH_ROUND_2(X0, X1, X2, X3, ROTATE_8); \
+ THREEFISH_INJECT_KEY_2(X0, X1, X2, X3, R+1, K1, K2, T2, T0); \
+ } while(0)
+
+ __m256i K0 = _mm256_set_epi64x(K[5], K[3], K[1], K[8]);
+ __m256i K1 = _mm256_set_epi64x(K[6], K[4], K[2], K[0]);
+ __m256i K2 = _mm256_set_epi64x(K[7], K[5], K[3], K[1]);
+
+ const __m256i* in_mm = reinterpret_cast<const __m256i*>(in);
+ __m256i* out_mm = reinterpret_cast<__m256i*>(out);
+
+ while(blocks >= 2)
+ {
+ __m256i X0 = _mm256_loadu_si256(in_mm++);
+ __m256i X1 = _mm256_loadu_si256(in_mm++);
+ __m256i X2 = _mm256_loadu_si256(in_mm++);
+ __m256i X3 = _mm256_loadu_si256(in_mm++);
+
+ const __m256i T = _mm256_set_epi64x(T_64[0], T_64[1], T_64[2], 0);
+
+ interleave_epi64(X0, X1);
+ interleave_epi64(X2, X3);
+
+ THREEFISH_INJECT_KEY_2(X0, X1, X2, X3, 0, K1, K2, 2, 3);
+
+ THREEFISH_ENC_2_8_ROUNDS(X0, X1, X2, X3, 1, K2,K0,K1, 1, 2, 3);
+ THREEFISH_ENC_2_8_ROUNDS(X0, X1, X2, X3, 3, K1,K2,K0, 2, 3, 1);
+ THREEFISH_ENC_2_8_ROUNDS(X0, X1, X2, X3, 5, K0,K1,K2, 3, 1, 2);
+ THREEFISH_ENC_2_8_ROUNDS(X0, X1, X2, X3, 7, K2,K0,K1, 1, 2, 3);
+ THREEFISH_ENC_2_8_ROUNDS(X0, X1, X2, X3, 9, K1,K2,K0, 2, 3, 1);
+ THREEFISH_ENC_2_8_ROUNDS(X0, X1, X2, X3, 11, K0,K1,K2, 3, 1, 2);
+ THREEFISH_ENC_2_8_ROUNDS(X0, X1, X2, X3, 13, K2,K0,K1, 1, 2, 3);
+ THREEFISH_ENC_2_8_ROUNDS(X0, X1, X2, X3, 15, K1,K2,K0, 2, 3, 1);
+ THREEFISH_ENC_2_8_ROUNDS(X0, X1, X2, X3, 17, K0,K1,K2, 3, 1, 2);
+
+ deinterleave_epi64(X0, X1);
+ deinterleave_epi64(X2, X3);
+
+ _mm256_storeu_si256(out_mm++, X0);
+ _mm256_storeu_si256(out_mm++, X1);
+ _mm256_storeu_si256(out_mm++, X2);
+ _mm256_storeu_si256(out_mm++, X3);
+
+ blocks -= 2;
+ }
+
+ for(size_t i = 0; i != blocks; ++i)
+ {
+ __m256i X0 = _mm256_loadu_si256(in_mm++);
+ __m256i X1 = _mm256_loadu_si256(in_mm++);
+
+ const __m256i T = _mm256_set_epi64x(T_64[0], T_64[1], T_64[2], 0);
+
+ interleave_epi64(X0, X1);
+
+ THREEFISH_INJECT_KEY(X0, X1, 0, K1, K2, 2, 3);
+
+ THREEFISH_ENC_8_ROUNDS(X0, X1, 1, K2,K0,K1, 1, 2, 3);
+ THREEFISH_ENC_8_ROUNDS(X0, X1, 3, K1,K2,K0, 2, 3, 1);
+ THREEFISH_ENC_8_ROUNDS(X0, X1, 5, K0,K1,K2, 3, 1, 2);
+ THREEFISH_ENC_8_ROUNDS(X0, X1, 7, K2,K0,K1, 1, 2, 3);
+ THREEFISH_ENC_8_ROUNDS(X0, X1, 9, K1,K2,K0, 2, 3, 1);
+ THREEFISH_ENC_8_ROUNDS(X0, X1, 11, K0,K1,K2, 3, 1, 2);
+ THREEFISH_ENC_8_ROUNDS(X0, X1, 13, K2,K0,K1, 1, 2, 3);
+ THREEFISH_ENC_8_ROUNDS(X0, X1, 15, K1,K2,K0, 2, 3, 1);
+ THREEFISH_ENC_8_ROUNDS(X0, X1, 17, K0,K1,K2, 3, 1, 2);
+
+ deinterleave_epi64(X0, X1);
+
+ _mm256_storeu_si256(out_mm++, X0);
+ _mm256_storeu_si256(out_mm++, X1);
+ }
+
+ _mm256_zeroall();
+
+#undef THREEFISH_ENC_8_ROUNDS
+#undef THREEFISH_ROUND
+#undef THREEFISH_INJECT_KEY
+#undef THREEFISH_DEC_2_8_ROUNDS
+#undef THREEFISH_ROUND_2
+#undef THREEFISH_INJECT_KEY_2
+ }
+
+BOTAN_FUNC_ISA("avx2")
+void Threefish_512::avx2_decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+ {
+ _mm256_zeroupper();
+
+ const uint64_t* K = m_K.data();
+ const uint64_t* T_64 = m_T.data();
+
+ const __m256i ROTATE_1 = _mm256_set_epi64x(37,19,36,46);
+ const __m256i ROTATE_2 = _mm256_set_epi64x(42,14,27,33);
+ const __m256i ROTATE_3 = _mm256_set_epi64x(39,36,49,17);
+ const __m256i ROTATE_4 = _mm256_set_epi64x(56,54, 9,44);
+ const __m256i ROTATE_5 = _mm256_set_epi64x(24,34,30,39);
+ const __m256i ROTATE_6 = _mm256_set_epi64x(17,10,50,13);
+ const __m256i ROTATE_7 = _mm256_set_epi64x(43,39,29,25);
+ const __m256i ROTATE_8 = _mm256_set_epi64x(22,56,35, 8);
+
+#define THREEFISH_ROUND(X0, X1, SHR) \
+ do { \
+ const __m256i SHL = _mm256_sub_epi64(_mm256_set1_epi64x(64), SHR); \
+ X0 = _mm256_permute4x64_epi64(X0, _MM_SHUFFLE(2, 1, 0, 3)); \
+ X1 = _mm256_permute4x64_epi64(X1, _MM_SHUFFLE(1, 2, 3, 0)); \
+ X1 = _mm256_xor_si256(X1, X0); \
+ X1 = _mm256_or_si256(_mm256_sllv_epi64(X1, SHL), _mm256_srlv_epi64(X1, SHR)); \
+ X0 = _mm256_sub_epi64(X0, X1); \
+ } while(0)
+
+#define THREEFISH_ROUND_2(X0, X1, X2, X3, SHR) \
+ do { \
+ const __m256i SHL = _mm256_sub_epi64(_mm256_set1_epi64x(64), SHR); \
+ X0 = _mm256_permute4x64_epi64(X0, _MM_SHUFFLE(2, 1, 0, 3)); \
+ X2 = _mm256_permute4x64_epi64(X2, _MM_SHUFFLE(2, 1, 0, 3)); \
+ X1 = _mm256_permute4x64_epi64(X1, _MM_SHUFFLE(1, 2, 3, 0)); \
+ X3 = _mm256_permute4x64_epi64(X3, _MM_SHUFFLE(1, 2, 3, 0)); \
+ X1 = _mm256_xor_si256(X1, X0); \
+ X3 = _mm256_xor_si256(X3, X2); \
+ X1 = _mm256_or_si256(_mm256_sllv_epi64(X1, SHL), _mm256_srlv_epi64(X1, SHR)); \
+ X3 = _mm256_or_si256(_mm256_sllv_epi64(X3, SHL), _mm256_srlv_epi64(X3, SHR)); \
+ X0 = _mm256_sub_epi64(X0, X1); \
+ X2 = _mm256_sub_epi64(X2, X3); \
+ } while(0)
+
+#define THREEFISH_INJECT_KEY(X0, X1, R, K0, K1, T0I, T1I) \
+ do { \
+ const __m256i T0 = _mm256_permute4x64_epi64(T, _MM_SHUFFLE(T0I, 0, 0, 0)); \
+ const __m256i T1 = _mm256_permute4x64_epi64(T, _MM_SHUFFLE(0, T1I, 0, 0)); \
+ X0 = _mm256_sub_epi64(X0, K0); \
+ X1 = _mm256_sub_epi64(X1, K1); \
+ X1 = _mm256_sub_epi64(X1, _mm256_set_epi64x(R, 0, 0, 0)); \
+ X0 = _mm256_sub_epi64(X0, T0); \
+ X1 = _mm256_sub_epi64(X1, T1); \
+ } while(0)
+
+#define THREEFISH_DEC_8_ROUNDS(X0, X1, R, K1, K2, K3, T0, T1, T2) \
+ do { \
+ THREEFISH_INJECT_KEY(X0, X1, R+1, K2, K3, T2, T0); \
+ THREEFISH_ROUND(X0, X1, ROTATE_8); \
+ THREEFISH_ROUND(X0, X1, ROTATE_7); \
+ THREEFISH_ROUND(X0, X1, ROTATE_6); \
+ THREEFISH_ROUND(X0, X1, ROTATE_5); \
+ \
+ THREEFISH_INJECT_KEY(X0, X1, R, K1, K2, T0, T1); \
+ THREEFISH_ROUND(X0, X1, ROTATE_4); \
+ THREEFISH_ROUND(X0, X1, ROTATE_3); \
+ THREEFISH_ROUND(X0, X1, ROTATE_2); \
+ THREEFISH_ROUND(X0, X1, ROTATE_1); \
+ } while(0)
+
+#define THREEFISH_INJECT_KEY_2(X0, X1, X2, X3, R, K0, K1, T0I, T1I) \
+ do { \
+ const __m256i T0 = _mm256_permute4x64_epi64(T, _MM_SHUFFLE(T0I, 0, 0, 0)); \
+ __m256i T1 = _mm256_permute4x64_epi64(T, _MM_SHUFFLE(0, T1I, 0, 0)); \
+ X0 = _mm256_sub_epi64(X0, K0); \
+ X2 = _mm256_sub_epi64(X2, K0); \
+ X1 = _mm256_sub_epi64(X1, K1); \
+ X3 = _mm256_sub_epi64(X3, K1); \
+ T1 = _mm256_add_epi64(T1, _mm256_set_epi64x(R,0,0,0)); \
+ X0 = _mm256_sub_epi64(X0, T0); \
+ X2 = _mm256_sub_epi64(X2, T0); \
+ X1 = _mm256_sub_epi64(X1, T1); \
+ X3 = _mm256_sub_epi64(X3, T1); \
+ } while(0)
+
+#define THREEFISH_DEC_2_8_ROUNDS(X0, X1, X2, X3, R, K1, K2, K3, T0, T1, T2) \
+ do { \
+ THREEFISH_INJECT_KEY_2(X0, X1, X2, X3, R+1, K2, K3, T2, T0); \
+ THREEFISH_ROUND_2(X0, X1, X2, X3, ROTATE_8); \
+ THREEFISH_ROUND_2(X0, X1, X2, X3, ROTATE_7); \
+ THREEFISH_ROUND_2(X0, X1, X2, X3, ROTATE_6); \
+ THREEFISH_ROUND_2(X0, X1, X2, X3, ROTATE_5); \
+ \
+ THREEFISH_INJECT_KEY_2(X0, X1, X2, X3, R, K1, K2, T0, T1); \
+ THREEFISH_ROUND_2(X0, X1, X2, X3, ROTATE_4); \
+ THREEFISH_ROUND_2(X0, X1, X2, X3, ROTATE_3); \
+ THREEFISH_ROUND_2(X0, X1, X2, X3, ROTATE_2); \
+ THREEFISH_ROUND_2(X0, X1, X2, X3, ROTATE_1); \
+ } while(0)
+
+ /*
+ v1.0 key schedule: 9 ymm registers (only need 2 or 3)
+ (0,1,2,3),(4,5,6,7) [8]
+ then mutating with vpermq
+ */
+ const __m256i K0 = _mm256_set_epi64x(K[6], K[4], K[2], K[0]);
+ const __m256i K1 = _mm256_set_epi64x(K[7], K[5], K[3], K[1]);
+ const __m256i K2 = _mm256_set_epi64x(K[8], K[6], K[4], K[2]);
+ const __m256i K3 = _mm256_set_epi64x(K[0], K[7], K[5], K[3]);
+ const __m256i K4 = _mm256_set_epi64x(K[1], K[8], K[6], K[4]);
+ const __m256i K5 = _mm256_set_epi64x(K[2], K[0], K[7], K[5]);
+ const __m256i K6 = _mm256_set_epi64x(K[3], K[1], K[8], K[6]);
+ const __m256i K7 = _mm256_set_epi64x(K[4], K[2], K[0], K[7]);
+ const __m256i K8 = _mm256_set_epi64x(K[5], K[3], K[1], K[8]);
+
+ const __m256i* in_mm = reinterpret_cast<const __m256i*>(in);
+ __m256i* out_mm = reinterpret_cast<__m256i*>(out);
+
+ while(blocks >= 2)
+ {
+ __m256i X0 = _mm256_loadu_si256(in_mm++);
+ __m256i X1 = _mm256_loadu_si256(in_mm++);
+ __m256i X2 = _mm256_loadu_si256(in_mm++);
+ __m256i X3 = _mm256_loadu_si256(in_mm++);
+
+ const __m256i T = _mm256_set_epi64x(T_64[0], T_64[1], T_64[2], 0);
+
+ interleave_epi64(X0, X1);
+ interleave_epi64(X2, X3);
+
+ THREEFISH_DEC_2_8_ROUNDS(X0, X1, X2, X3, 17, K8,K0,K1, 3, 1, 2);
+ THREEFISH_DEC_2_8_ROUNDS(X0, X1, X2, X3, 15, K6,K7,K8, 2, 3, 1);
+ THREEFISH_DEC_2_8_ROUNDS(X0, X1, X2, X3, 13, K4,K5,K6, 1, 2, 3);
+ THREEFISH_DEC_2_8_ROUNDS(X0, X1, X2, X3, 11, K2,K3,K4, 3, 1, 2);
+ THREEFISH_DEC_2_8_ROUNDS(X0, X1, X2, X3, 9, K0,K1,K2, 2, 3, 1);
+ THREEFISH_DEC_2_8_ROUNDS(X0, X1, X2, X3, 7, K7,K8,K0, 1, 2, 3);
+ THREEFISH_DEC_2_8_ROUNDS(X0, X1, X2, X3, 5, K5,K6,K7, 3, 1, 2);
+ THREEFISH_DEC_2_8_ROUNDS(X0, X1, X2, X3, 3, K3,K4,K5, 2, 3, 1);
+ THREEFISH_DEC_2_8_ROUNDS(X0, X1, X2, X3, 1, K1,K2,K3, 1, 2, 3);
+
+ THREEFISH_INJECT_KEY_2(X0, X1, X2, X3, 0, K0, K1, 2, 3);
+
+ deinterleave_epi64(X0, X1);
+ deinterleave_epi64(X2, X3);
+
+ _mm256_storeu_si256(out_mm++, X0);
+ _mm256_storeu_si256(out_mm++, X1);
+ _mm256_storeu_si256(out_mm++, X2);
+ _mm256_storeu_si256(out_mm++, X3);
+
+ blocks -= 2;
+ }
+
+ for(size_t i = 0; i != blocks; ++i)
+ {
+ __m256i X0 = _mm256_loadu_si256(in_mm++);
+ __m256i X1 = _mm256_loadu_si256(in_mm++);
+
+ const __m256i T = _mm256_set_epi64x(T_64[0], T_64[1], T_64[2], 0);
+
+ interleave_epi64(X0, X1);
+
+ THREEFISH_DEC_8_ROUNDS(X0, X1, 17, K8,K0,K1, 3, 1, 2);
+ THREEFISH_DEC_8_ROUNDS(X0, X1, 15, K6,K7,K8, 2, 3, 1);
+ THREEFISH_DEC_8_ROUNDS(X0, X1, 13, K4,K5,K6, 1, 2, 3);
+ THREEFISH_DEC_8_ROUNDS(X0, X1, 11, K2,K3,K4, 3, 1, 2);
+ THREEFISH_DEC_8_ROUNDS(X0, X1, 9, K0,K1,K2, 2, 3, 1);
+ THREEFISH_DEC_8_ROUNDS(X0, X1, 7, K7,K8,K0, 1, 2, 3);
+ THREEFISH_DEC_8_ROUNDS(X0, X1, 5, K5,K6,K7, 3, 1, 2);
+ THREEFISH_DEC_8_ROUNDS(X0, X1, 3, K3,K4,K5, 2, 3, 1);
+ THREEFISH_DEC_8_ROUNDS(X0, X1, 1, K1,K2,K3, 1, 2, 3);
+
+ THREEFISH_INJECT_KEY(X0, X1, 0, K0, K1, 2, 3);
+
+ deinterleave_epi64(X0, X1);
+
+ _mm256_storeu_si256(out_mm++, X0);
+ _mm256_storeu_si256(out_mm++, X1);
+ }
+
+#undef THREEFISH_DEC_8_ROUNDS
+#undef THREEFISH_ROUND
+#undef THREEFISH_INJECT_KEY
+#undef THREEFISH_DEC_2_8_ROUNDS
+#undef THREEFISH_ROUND_2
+#undef THREEFISH_INJECT_KEY_2
+
+ _mm256_zeroall();
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/block/twofish/info.txt b/comm/third_party/botan/src/lib/block/twofish/info.txt
new file mode 100644
index 0000000000..9febbc8dd2
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/twofish/info.txt
@@ -0,0 +1,3 @@
+<defines>
+TWOFISH -> 20131128
+</defines>
diff --git a/comm/third_party/botan/src/lib/block/twofish/twofish.cpp b/comm/third_party/botan/src/lib/block/twofish/twofish.cpp
new file mode 100644
index 0000000000..3a508dc9d5
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/twofish/twofish.cpp
@@ -0,0 +1,326 @@
+/*
+* Twofish
+* (C) 1999-2007,2017 Jack Lloyd
+*
+* The key schedule implemenation is based on a public domain
+* implementation by Matthew Skala
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/twofish.h>
+#include <botan/loadstor.h>
+#include <botan/rotate.h>
+
+namespace Botan {
+
+namespace {
+
+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<uint32_t>& 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)];
+ uint32_t Y = SB[ get_byte(0, B)] ^ SB[256+get_byte(3, B)] ^
+ SB[512+get_byte(2, B)] ^ SB[768+get_byte(1, B)];
+
+ X += Y;
+ Y += X;
+
+ X += RK1;
+ Y += RK2;
+
+ C = rotr<1>(C ^ X);
+ D = rotl<1>(D) ^ Y;
+ }
+
+inline void TF_D(uint32_t A, uint32_t B, uint32_t& C, uint32_t& D,
+ uint32_t RK1, uint32_t RK2,
+ const secure_vector<uint32_t>& 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)];
+ uint32_t Y = SB[ get_byte(0, B)] ^ SB[256+get_byte(3, B)] ^
+ SB[512+get_byte(2, B)] ^ SB[768+get_byte(1, B)];
+
+ X += Y;
+ Y += X;
+
+ X += RK1;
+ Y += RK2;
+
+ C = rotl<1>(C) ^ X;
+ D = rotr<1>(D ^ Y);
+ }
+
+}
+
+/*
+* Twofish Encryption
+*/
+void Twofish::encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+ {
+ verify_key_set(m_SB.empty() == false);
+
+ while(blocks >= 2)
+ {
+ uint32_t A0, B0, C0, D0;
+ uint32_t A1, B1, C1, D1;
+ load_le(in, A0, B0, C0, D0, A1, B1, C1, D1);
+
+ A0 ^= m_RK[0];
+ A1 ^= m_RK[0];
+ B0 ^= m_RK[1];
+ B1 ^= m_RK[1];
+ C0 ^= m_RK[2];
+ C1 ^= m_RK[2];
+ D0 ^= m_RK[3];
+ D1 ^= m_RK[3];
+
+ for(size_t k = 8; k != 40; k += 4)
+ {
+ TF_E(A0, B0, C0, D0, m_RK[k+0], m_RK[k+1], m_SB);
+ TF_E(A1, B1, C1, D1, m_RK[k+0], m_RK[k+1], m_SB);
+
+ TF_E(C0, D0, A0, B0, m_RK[k+2], m_RK[k+3], m_SB);
+ TF_E(C1, D1, A1, B1, m_RK[k+2], m_RK[k+3], m_SB);
+ }
+
+ C0 ^= m_RK[4];
+ C1 ^= m_RK[4];
+ D0 ^= m_RK[5];
+ D1 ^= m_RK[5];
+ A0 ^= m_RK[6];
+ A1 ^= m_RK[6];
+ B0 ^= m_RK[7];
+ B1 ^= m_RK[7];
+
+ store_le(out, C0, D0, A0, B0, C1, D1, A1, B1);
+
+ blocks -= 2;
+ out += 2*BLOCK_SIZE;
+ in += 2*BLOCK_SIZE;
+ }
+
+ if(blocks)
+ {
+ uint32_t A, B, C, D;
+ load_le(in, A, B, C, D);
+
+ A ^= m_RK[0];
+ B ^= m_RK[1];
+ C ^= m_RK[2];
+ D ^= m_RK[3];
+
+ for(size_t k = 8; k != 40; k += 4)
+ {
+ TF_E(A, B, C, D, m_RK[k ], m_RK[k+1], m_SB);
+ TF_E(C, D, A, B, m_RK[k+2], m_RK[k+3], m_SB);
+ }
+
+ C ^= m_RK[4];
+ D ^= m_RK[5];
+ A ^= m_RK[6];
+ B ^= m_RK[7];
+
+ store_le(out, C, D, A, B);
+ }
+ }
+
+/*
+* Twofish Decryption
+*/
+void Twofish::decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+ {
+ verify_key_set(m_SB.empty() == false);
+
+ while(blocks >= 2)
+ {
+ uint32_t A0, B0, C0, D0;
+ uint32_t A1, B1, C1, D1;
+ load_le(in, A0, B0, C0, D0, A1, B1, C1, D1);
+
+ A0 ^= m_RK[4];
+ A1 ^= m_RK[4];
+ B0 ^= m_RK[5];
+ B1 ^= m_RK[5];
+ C0 ^= m_RK[6];
+ C1 ^= m_RK[6];
+ D0 ^= m_RK[7];
+ D1 ^= m_RK[7];
+
+ for(size_t k = 40; k != 8; k -= 4)
+ {
+ TF_D(A0, B0, C0, D0, m_RK[k-2], m_RK[k-1], m_SB);
+ TF_D(A1, B1, C1, D1, m_RK[k-2], m_RK[k-1], m_SB);
+
+ TF_D(C0, D0, A0, B0, m_RK[k-4], m_RK[k-3], m_SB);
+ TF_D(C1, D1, A1, B1, m_RK[k-4], m_RK[k-3], m_SB);
+ }
+
+ C0 ^= m_RK[0];
+ C1 ^= m_RK[0];
+ D0 ^= m_RK[1];
+ D1 ^= m_RK[1];
+ A0 ^= m_RK[2];
+ A1 ^= m_RK[2];
+ B0 ^= m_RK[3];
+ B1 ^= m_RK[3];
+
+ store_le(out, C0, D0, A0, B0, C1, D1, A1, B1);
+
+ blocks -= 2;
+ out += 2*BLOCK_SIZE;
+ in += 2*BLOCK_SIZE;
+ }
+
+ if(blocks)
+ {
+ uint32_t A, B, C, D;
+ load_le(in, A, B, C, D);
+
+ A ^= m_RK[4];
+ B ^= m_RK[5];
+ C ^= m_RK[6];
+ D ^= m_RK[7];
+
+ for(size_t k = 40; k != 8; k -= 4)
+ {
+ TF_D(A, B, C, D, m_RK[k-2], m_RK[k-1], m_SB);
+ TF_D(C, D, A, B, m_RK[k-4], m_RK[k-3], m_SB);
+ }
+
+ C ^= m_RK[0];
+ D ^= m_RK[1];
+ A ^= m_RK[2];
+ B ^= m_RK[3];
+
+ store_le(out, C, D, A, B);
+ }
+ }
+
+/*
+* Twofish Key Schedule
+*/
+void Twofish::key_schedule(const uint8_t key[], size_t length)
+ {
+ m_SB.resize(1024);
+ m_RK.resize(40);
+
+ secure_vector<uint8_t> S(16);
+
+ for(size_t i = 0; i != length; ++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];
+ }
+ }
+
+ if(length == 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]];
+ }
+
+ 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]];
+ Y = rotl<8>(Y);
+ X += Y; Y += X;
+
+ m_RK[i] = X;
+ m_RK[i+1] = rotl<9>(Y);
+ }
+ }
+ else if(length == 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]];
+ }
+
+ 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]];
+ Y = rotl<8>(Y);
+ X += Y; Y += X;
+
+ m_RK[i] = X;
+ m_RK[i+1] = rotl<9>(Y);
+ }
+ }
+ else if(length == 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]];
+ }
+
+ 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]];
+ Y = rotl<8>(Y);
+ X += Y; Y += X;
+
+ m_RK[i] = X;
+ m_RK[i+1] = rotl<9>(Y);
+ }
+ }
+ }
+
+/*
+* Clear memory of sensitive data
+*/
+void Twofish::clear()
+ {
+ zap(m_SB);
+ zap(m_RK);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/block/twofish/twofish.h b/comm/third_party/botan/src/lib/block/twofish/twofish.h
new file mode 100644
index 0000000000..027e2c7011
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/twofish/twofish.h
@@ -0,0 +1,47 @@
+/*
+* Twofish
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_TWOFISH_H_
+#define BOTAN_TWOFISH_H_
+
+#include <botan/block_cipher.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(twofish.h)
+
+namespace Botan {
+
+/**
+* Twofish, an AES finalist
+*/
+class BOTAN_PUBLIC_API(2,0) Twofish final : public Block_Cipher_Fixed_Params<16, 16, 32, 8>
+ {
+ public:
+ void encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+ void decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+
+ void clear() override;
+ std::string name() const override { return "Twofish"; }
+ BlockCipher* clone() const override { return new Twofish; }
+ private:
+ void key_schedule(const uint8_t[], size_t) 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];
+
+ secure_vector<uint32_t> m_SB, m_RK;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/block/twofish/twofish_tab.cpp b/comm/third_party/botan/src/lib/block/twofish/twofish_tab.cpp
new file mode 100644
index 0000000000..acdb35560a
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/twofish/twofish_tab.cpp
@@ -0,0 +1,293 @@
+/*
+* S-Box and MDS Tables for Twofish
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/twofish.h>
+
+namespace Botan {
+
+alignas(64) 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(64) 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(64) 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(64) 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(64) 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(64) 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(64) 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(64) 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 };
+
+}
diff --git a/comm/third_party/botan/src/lib/block/xtea/info.txt b/comm/third_party/botan/src/lib/block/xtea/info.txt
new file mode 100644
index 0000000000..b9b9ad3652
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/xtea/info.txt
@@ -0,0 +1,3 @@
+<defines>
+XTEA -> 20131128
+</defines>
diff --git a/comm/third_party/botan/src/lib/block/xtea/xtea.cpp b/comm/third_party/botan/src/lib/block/xtea/xtea.cpp
new file mode 100644
index 0000000000..7d815529ff
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/xtea/xtea.cpp
@@ -0,0 +1,134 @@
+/*
+* XTEA
+* (C) 1999-2009,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/xtea.h>
+#include <botan/loadstor.h>
+
+namespace Botan {
+
+/*
+* XTEA Encryption
+*/
+void XTEA::encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+ {
+ verify_key_set(m_EK.empty() == false);
+
+ const uint32_t* EK = &m_EK[0];
+
+ const size_t blocks4 = blocks / 4;
+ const size_t blocks_left = blocks % 4;
+
+ BOTAN_PARALLEL_FOR(size_t i = 0; i < blocks4; i++)
+ {
+ uint32_t L0, R0, L1, R1, L2, R2, L3, R3;
+ load_be(in + 4*BLOCK_SIZE*i, L0, R0, L1, R1, L2, R2, L3, R3);
+
+ for(size_t r = 0; r != 32; ++r)
+ {
+ L0 += (((R0 << 4) ^ (R0 >> 5)) + R0) ^ EK[2*r];
+ L1 += (((R1 << 4) ^ (R1 >> 5)) + R1) ^ EK[2*r];
+ L2 += (((R2 << 4) ^ (R2 >> 5)) + R2) ^ EK[2*r];
+ L3 += (((R3 << 4) ^ (R3 >> 5)) + R3) ^ EK[2*r];
+
+ R0 += (((L0 << 4) ^ (L0 >> 5)) + L0) ^ EK[2*r+1];
+ R1 += (((L1 << 4) ^ (L1 >> 5)) + L1) ^ EK[2*r+1];
+ R2 += (((L2 << 4) ^ (L2 >> 5)) + L2) ^ EK[2*r+1];
+ R3 += (((L3 << 4) ^ (L3 >> 5)) + L3) ^ EK[2*r+1];
+ }
+
+ store_be(out + 4*BLOCK_SIZE*i, L0, R0, L1, R1, L2, R2, L3, R3);
+ }
+
+ BOTAN_PARALLEL_FOR(size_t i = 0; i < blocks_left; ++i)
+ {
+ uint32_t L, R;
+ load_be(in + BLOCK_SIZE*(4*blocks4+i), L, R);
+
+ for(size_t r = 0; r != 32; ++r)
+ {
+ L += (((R << 4) ^ (R >> 5)) + R) ^ EK[2*r];
+ R += (((L << 4) ^ (L >> 5)) + L) ^ EK[2*r+1];
+ }
+
+ store_be(out + BLOCK_SIZE*(4*blocks4+i), L, R);
+ }
+ }
+
+/*
+* XTEA Decryption
+*/
+void XTEA::decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
+ {
+ verify_key_set(m_EK.empty() == false);
+
+ const uint32_t* EK = &m_EK[0];
+
+ const size_t blocks4 = blocks / 4;
+ const size_t blocks_left = blocks % 4;
+
+ BOTAN_PARALLEL_FOR(size_t i = 0; i < blocks4; i++)
+ {
+ uint32_t L0, R0, L1, R1, L2, R2, L3, R3;
+ load_be(in + 4*BLOCK_SIZE*i, L0, R0, L1, R1, L2, R2, L3, R3);
+
+ for(size_t r = 0; r != 32; ++r)
+ {
+ R0 -= (((L0 << 4) ^ (L0 >> 5)) + L0) ^ EK[63 - 2*r];
+ R1 -= (((L1 << 4) ^ (L1 >> 5)) + L1) ^ EK[63 - 2*r];
+ R2 -= (((L2 << 4) ^ (L2 >> 5)) + L2) ^ EK[63 - 2*r];
+ R3 -= (((L3 << 4) ^ (L3 >> 5)) + L3) ^ EK[63 - 2*r];
+
+ L0 -= (((R0 << 4) ^ (R0 >> 5)) + R0) ^ EK[62 - 2*r];
+ L1 -= (((R1 << 4) ^ (R1 >> 5)) + R1) ^ EK[62 - 2*r];
+ L2 -= (((R2 << 4) ^ (R2 >> 5)) + R2) ^ EK[62 - 2*r];
+ L3 -= (((R3 << 4) ^ (R3 >> 5)) + R3) ^ EK[62 - 2*r];
+ }
+
+ store_be(out + 4*BLOCK_SIZE*i, L0, R0, L1, R1, L2, R2, L3, R3);
+ }
+
+ BOTAN_PARALLEL_FOR(size_t i = 0; i < blocks_left; ++i)
+ {
+ uint32_t L, R;
+ load_be(in + BLOCK_SIZE*(4*blocks4+i), L, R);
+
+ for(size_t r = 0; r != 32; ++r)
+ {
+ R -= (((L << 4) ^ (L >> 5)) + L) ^ m_EK[63 - 2*r];
+ L -= (((R << 4) ^ (R >> 5)) + R) ^ m_EK[62 - 2*r];
+ }
+
+ store_be(out + BLOCK_SIZE*(4*blocks4+i), L, R);
+ }
+ }
+
+/*
+* XTEA Key Schedule
+*/
+void XTEA::key_schedule(const uint8_t key[], size_t)
+ {
+ m_EK.resize(64);
+
+ secure_vector<uint32_t> UK(4);
+ for(size_t i = 0; i != 4; ++i)
+ UK[i] = load_be<uint32_t>(key, i);
+
+ uint32_t D = 0;
+ for(size_t i = 0; i != 64; i += 2)
+ {
+ m_EK[i ] = D + UK[D % 4];
+ D += 0x9E3779B9;
+ m_EK[i+1] = D + UK[(D >> 11) % 4];
+ }
+ }
+
+void XTEA::clear()
+ {
+ zap(m_EK);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/block/xtea/xtea.h b/comm/third_party/botan/src/lib/block/xtea/xtea.h
new file mode 100644
index 0000000000..bae0bc7e63
--- /dev/null
+++ b/comm/third_party/botan/src/lib/block/xtea/xtea.h
@@ -0,0 +1,37 @@
+/*
+* XTEA
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_XTEA_H_
+#define BOTAN_XTEA_H_
+
+#include <botan/block_cipher.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(xtea.h)
+
+namespace Botan {
+
+/**
+* XTEA
+*/
+class BOTAN_PUBLIC_API(2,0) XTEA final : public Block_Cipher_Fixed_Params<8, 16>
+ {
+ public:
+ void encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+ void decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override;
+
+ void clear() override;
+ std::string name() const override { return "XTEA"; }
+ BlockCipher* clone() const override { return new XTEA; }
+
+ private:
+ void key_schedule(const uint8_t[], size_t) override;
+ secure_vector<uint32_t> m_EK;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/codec/base32/base32.cpp b/comm/third_party/botan/src/lib/codec/base32/base32.cpp
new file mode 100644
index 0000000000..224dae9916
--- /dev/null
+++ b/comm/third_party/botan/src/lib/codec/base32/base32.cpp
@@ -0,0 +1,233 @@
+/*
+* Base32 Encoding and Decoding
+* (C) 2018 Erwan Chaussy
+* (C) 2018,2020 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/base32.h>
+#include <botan/internal/codec_base.h>
+#include <botan/internal/rounding.h>
+#include <botan/internal/ct_utils.h>
+
+namespace Botan {
+
+namespace {
+
+class Base32 final
+ {
+ public:
+ static inline std::string name() noexcept
+ {
+ return "base32";
+ }
+
+ static inline size_t encoding_bytes_in() noexcept
+ {
+ return m_encoding_bytes_in;
+ }
+ static inline size_t encoding_bytes_out() noexcept
+ {
+ return m_encoding_bytes_out;
+ }
+
+ static inline size_t decoding_bytes_in() noexcept
+ {
+ return m_encoding_bytes_out;
+ }
+ static inline size_t decoding_bytes_out() noexcept
+ {
+ return m_encoding_bytes_in;
+ }
+
+ static inline size_t bits_consumed() noexcept
+ {
+ return m_encoding_bits;
+ }
+ static inline size_t remaining_bits_before_padding() noexcept
+ {
+ return m_remaining_bits_before_padding;
+ }
+
+ static inline 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 inline 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;
+ }
+
+ static void encode(char out[8], const uint8_t in[5]) noexcept;
+
+ static uint8_t lookup_binary_value(char input) noexcept;
+
+ static bool check_bad_char(uint8_t bin, char input, bool ignore_ws);
+
+ static void decode(uint8_t* out_ptr, const uint8_t decode_buf[8])
+ {
+ out_ptr[0] = (decode_buf[0] << 3) | (decode_buf[1] >> 2);
+ out_ptr[1] = (decode_buf[1] << 6) | (decode_buf[2] << 1) | (decode_buf[3] >> 4);
+ out_ptr[2] = (decode_buf[3] << 4) | (decode_buf[4] >> 1);
+ out_ptr[3] = (decode_buf[4] << 7) | (decode_buf[5] << 2) | (decode_buf[6] >> 3);
+ out_ptr[4] = (decode_buf[6] << 5) | decode_buf[7];
+ }
+
+ static inline size_t bytes_to_remove(size_t final_truncate)
+ {
+ return final_truncate ? (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 const size_t m_encoding_bytes_in = 5;
+ static const 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<uint8_t>::is_lt(x, 26);
+
+ const char c_AZ = 'A' + x;
+ const char c_27 = '2' + (x - 26);
+
+ return in_AZ.select(c_AZ, c_27);
+ }
+
+}
+
+//static
+void Base32::encode(char out[8], const uint8_t in[5]) noexcept
+ {
+ const uint8_t b0 = (in[0] & 0xF8) >> 3;
+ const uint8_t b1 = ((in[0] & 0x07) << 2) | (in[1] >> 6);
+ const uint8_t b2 = ((in[1] & 0x3E) >> 1);
+ const uint8_t b3 = ((in[1] & 0x01) << 4) | (in[2] >> 4);
+ const uint8_t b4 = ((in[2] & 0x0F) << 1) | (in[3] >> 7);
+ const uint8_t b5 = ((in[3] & 0x7C) >> 2);
+ 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);
+ }
+
+//static
+uint8_t Base32::lookup_binary_value(char input) noexcept
+ {
+ const uint8_t c = static_cast<uint8_t>(input);
+
+ const auto is_alpha_upper = CT::Mask<uint8_t>::is_within_range(c, uint8_t('A'), uint8_t('Z'));
+ const auto is_decimal = CT::Mask<uint8_t>::is_within_range(c, uint8_t('2'), uint8_t('7'));
+
+ const auto is_equal = CT::Mask<uint8_t>::is_equal(c, uint8_t('='));
+ const auto is_whitespace = CT::Mask<uint8_t>::is_any_of(c, {
+ uint8_t(' '), uint8_t('\t'), uint8_t('\n'), uint8_t('\r')
+ });
+
+ const uint8_t c_upper = c - uint8_t('A');
+ const uint8_t c_decim = c - uint8_t('2') + 26;
+
+ uint8_t ret = 0xFF; // default value
+
+ ret = is_alpha_upper.select(c_upper, ret);
+ ret = is_decimal.select(c_decim, ret);
+ ret = is_equal.select(0x81, ret);
+ ret = is_whitespace.select(0x80, ret);
+
+ return ret;
+ }
+
+//static
+bool Base32::check_bad_char(uint8_t bin, char input, bool ignore_ws)
+ {
+ if(bin <= 0x1F)
+ {
+ return true;
+ }
+ else if(!(bin == 0x81 || (bin == 0x80 && ignore_ws)))
+ {
+ std::string bad_char(1, input);
+ if(bad_char == "\t")
+ { bad_char = "\\t"; }
+ else if(bad_char == "\n")
+ { bad_char = "\\n"; }
+ else if(bad_char == "\r")
+ { bad_char = "\\r"; }
+
+ throw Invalid_Argument(
+ std::string("base32_decode: invalid base32 character '") +
+ bad_char + "'");
+ }
+ return false;
+ }
+
+}
+
+size_t base32_encode(char out[],
+ const uint8_t in[],
+ size_t input_length,
+ size_t& input_consumed,
+ bool final_inputs)
+ {
+ return base_encode(Base32(), out, in, input_length, input_consumed, final_inputs);
+ }
+
+std::string base32_encode(const uint8_t input[],
+ size_t input_length)
+ {
+ return base_encode_to_string(Base32(), input, input_length);
+ }
+
+size_t base32_decode(uint8_t out[],
+ const char in[],
+ size_t input_length,
+ size_t& input_consumed,
+ bool final_inputs,
+ bool ignore_ws)
+ {
+ return base_decode(Base32(), out, in, input_length, input_consumed, final_inputs, ignore_ws);
+ }
+
+size_t base32_decode(uint8_t output[],
+ const char input[],
+ size_t input_length,
+ bool ignore_ws)
+ {
+ return base_decode_full(Base32(), output, input, input_length, ignore_ws);
+ }
+
+size_t base32_decode(uint8_t output[],
+ const std::string& input,
+ bool ignore_ws)
+ {
+ return base32_decode(output, input.data(), input.length(), ignore_ws);
+ }
+
+secure_vector<uint8_t> base32_decode(const char input[],
+ size_t input_length,
+ bool ignore_ws)
+ {
+ return base_decode_to_vec<secure_vector<uint8_t>>(Base32(), input, input_length, ignore_ws);
+ }
+
+secure_vector<uint8_t> base32_decode(const std::string& input,
+ bool ignore_ws)
+ {
+ return base32_decode(input.data(), input.size(), ignore_ws);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/codec/base32/base32.h b/comm/third_party/botan/src/lib/codec/base32/base32.h
new file mode 100644
index 0000000000..d2bcb3e6ab
--- /dev/null
+++ b/comm/third_party/botan/src/lib/codec/base32/base32.h
@@ -0,0 +1,127 @@
+/*
+* Base32 Encoding and Decoding
+* (C) 2018 Erwan Chaussy
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_BASE32_CODEC_H_
+#define BOTAN_BASE32_CODEC_H_
+
+#include <botan/secmem.h>
+#include <string>
+
+namespace Botan {
+
+/**
+* Perform base32 encoding
+* @param output an array of at least base32_encode_max_output bytes
+* @param input is some binary data
+* @param input_length length of input in bytes
+* @param input_consumed is an output parameter which says how many
+* bytes of input were actually consumed. If less than
+* input_length, then the range input[consumed:length]
+* should be passed in later along with more input.
+* @param final_inputs true iff this is the last input, in which case
+ padding chars will be applied if needed
+* @return number of bytes written to output
+*/
+size_t BOTAN_PUBLIC_API(2, 7) base32_encode(char output[],
+ const uint8_t input[],
+ size_t input_length,
+ size_t& input_consumed,
+ bool final_inputs);
+
+/**
+* Perform base32 encoding
+* @param input some input
+* @param input_length length of input in bytes
+* @return base32 representation of input
+*/
+std::string BOTAN_PUBLIC_API(2, 7) base32_encode(const uint8_t input[],
+ size_t input_length);
+
+/**
+* Perform base32 encoding
+* @param input some input
+* @return base32 representation of input
+*/
+template <typename Alloc>
+std::string base32_encode(const std::vector<uint8_t, Alloc>& input)
+ {
+ return base32_encode(input.data(), input.size());
+ }
+
+/**
+* Perform base32 decoding
+* @param output an array of at least base32_decode_max_output bytes
+* @param input some base32 input
+* @param input_length length of input in bytes
+* @param input_consumed is an output parameter which says how many
+* bytes of input were actually consumed. If less than
+* input_length, then the range input[consumed:length]
+* should be passed in later along with more input.
+* @param final_inputs true iff this is the last input, in which case
+ padding is allowed
+* @param ignore_ws ignore whitespace on input; if false, throw an
+ exception if whitespace is encountered
+* @return number of bytes written to output
+*/
+size_t BOTAN_PUBLIC_API(2, 7) base32_decode(uint8_t output[],
+ const char input[],
+ size_t input_length,
+ size_t& input_consumed,
+ bool final_inputs,
+ bool ignore_ws = true);
+
+/**
+* Perform base32 decoding
+* @param output an array of at least base32_decode_max_output bytes
+* @param input some base32 input
+* @param input_length length of input in bytes
+* @param ignore_ws ignore whitespace on input; if false, throw an
+ exception if whitespace is encountered
+* @return number of bytes written to output
+*/
+size_t BOTAN_PUBLIC_API(2, 7) base32_decode(uint8_t output[],
+ const char input[],
+ size_t input_length,
+ bool ignore_ws = true);
+
+/**
+* Perform base32 decoding
+* @param output an array of at least base32_decode_max_output bytes
+* @param input some base32 input
+* @param ignore_ws ignore whitespace on input; if false, throw an
+ exception if whitespace is encountered
+* @return number of bytes written to output
+*/
+size_t BOTAN_PUBLIC_API(2, 7) base32_decode(uint8_t output[],
+ const std::string& input,
+ bool ignore_ws = true);
+
+/**
+* Perform base32 decoding
+* @param input some base32 input
+* @param input_length the length of input in bytes
+* @param ignore_ws ignore whitespace on input; if false, throw an
+ exception if whitespace is encountered
+* @return decoded base32 output
+*/
+secure_vector<uint8_t> BOTAN_PUBLIC_API(2, 7) base32_decode(const char input[],
+ size_t input_length,
+ bool ignore_ws = true);
+
+/**
+* Perform base32 decoding
+* @param input some base32 input
+* @param ignore_ws ignore whitespace on input; if false, throw an
+ exception if whitespace is encountered
+* @return decoded base32 output
+*/
+secure_vector<uint8_t> BOTAN_PUBLIC_API(2, 7) base32_decode(const std::string& input,
+ bool ignore_ws = true);
+
+} // namespace Botan
+
+#endif
diff --git a/comm/third_party/botan/src/lib/codec/base32/info.txt b/comm/third_party/botan/src/lib/codec/base32/info.txt
new file mode 100644
index 0000000000..8e414c5a33
--- /dev/null
+++ b/comm/third_party/botan/src/lib/codec/base32/info.txt
@@ -0,0 +1,3 @@
+<defines>
+BASE32_CODEC -> 20180418
+</defines>
diff --git a/comm/third_party/botan/src/lib/codec/base58/base58.cpp b/comm/third_party/botan/src/lib/codec/base58/base58.cpp
new file mode 100644
index 0000000000..a6d509012d
--- /dev/null
+++ b/comm/third_party/botan/src/lib/codec/base58/base58.cpp
@@ -0,0 +1,189 @@
+/*
+* (C) 2018,2020 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/base58.h>
+#include <botan/exceptn.h>
+#include <botan/bigint.h>
+#include <botan/divide.h>
+#include <botan/loadstor.h>
+#include <botan/internal/ct_utils.h>
+#include <botan/hash.h>
+
+namespace Botan {
+
+namespace {
+
+uint32_t sha256_d_checksum(const uint8_t input[], size_t input_length)
+ {
+ std::unique_ptr<HashFunction> sha256 = HashFunction::create_or_throw("SHA-256");
+
+ std::vector<uint8_t> checksum(32);
+
+ sha256->update(input, input_length);
+ sha256->final(checksum);
+
+ sha256->update(checksum);
+ sha256->final(checksum);
+
+ return load_be<uint32_t>(checksum.data(), 0);
+ }
+
+char lookup_base58_char(uint8_t x)
+ {
+ // "123456789 ABCDEFGH JKLMN PQRSTUVWXYZ abcdefghijk mnopqrstuvwxyz"
+ BOTAN_DEBUG_ASSERT(x < 58);
+
+ const auto is_dec_19 = CT::Mask<uint8_t>::is_lte(x, 8);
+ const auto is_alpha_AH = CT::Mask<uint8_t>::is_within_range(x, 9, 16);
+ const auto is_alpha_JN = CT::Mask<uint8_t>::is_within_range(x, 17, 21);
+ const auto is_alpha_PZ = CT::Mask<uint8_t>::is_within_range(x, 22, 32);
+ const auto is_alpha_ak = CT::Mask<uint8_t>::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);
+
+ return ret;
+ }
+
+std::string base58_encode(BigInt v, size_t leading_zeros)
+ {
+ const uint8_t radix = 58;
+
+ std::string result;
+ BigInt q;
+
+ while(v.is_nonzero())
+ {
+ uint8_t r;
+ ct_divide_u8(v, radix, q, r);
+ result.push_back(lookup_base58_char(r));
+ v.swap(q);
+ }
+
+ for(size_t i = 0; i != leading_zeros; ++i)
+ result.push_back('1'); // 'zero' byte
+
+ return std::string(result.rbegin(), result.rend());
+ }
+
+template<typename T, typename Z>
+size_t count_leading_zeros(const T input[], size_t input_length, Z zero)
+ {
+ size_t leading_zeros = 0;
+
+ while(leading_zeros < input_length && input[leading_zeros] == zero)
+ leading_zeros += 1;
+
+ return leading_zeros;
+ }
+
+uint8_t base58_value_of(char input)
+ {
+ // "123456789 ABCDEFGH JKLMN PQRSTUVWXYZ abcdefghijk mnopqrstuvwxyz"
+
+ const uint8_t c = static_cast<uint8_t>(input);
+
+ const auto is_dec_19 = CT::Mask<uint8_t>::is_within_range(c, uint8_t('1'), uint8_t('9'));
+ const auto is_alpha_AH = CT::Mask<uint8_t>::is_within_range(c, uint8_t('A'), uint8_t('H'));
+ const auto is_alpha_JN = CT::Mask<uint8_t>::is_within_range(c, uint8_t('J'), uint8_t('N'));
+ const auto is_alpha_PZ = CT::Mask<uint8_t>::is_within_range(c, uint8_t('P'), uint8_t('Z'));
+
+ const auto is_alpha_ak = CT::Mask<uint8_t>::is_within_range(c, uint8_t('a'), uint8_t('k'));
+ const auto is_alpha_mz = CT::Mask<uint8_t>::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;
+ }
+
+}
+
+std::string base58_encode(const uint8_t input[], size_t input_length)
+ {
+ BigInt v(input, input_length);
+ return base58_encode(v, count_leading_zeros(input, input_length, 0));
+ }
+
+std::string base58_check_encode(const uint8_t input[], size_t input_length)
+ {
+ BigInt v(input, input_length);
+ v <<= 32;
+ v += sha256_d_checksum(input, input_length);
+ return base58_encode(v, count_leading_zeros(input, input_length, 0));
+ }
+
+std::vector<uint8_t> base58_decode(const char input[], size_t input_length)
+ {
+ const size_t leading_zeros = count_leading_zeros(input, input_length, '1');
+
+ BigInt v;
+
+ for(size_t i = leading_zeros; i != input_length; ++i)
+ {
+ const char c = input[i];
+
+ if(c == ' ' || c == '\n')
+ continue;
+
+ const uint8_t idx = base58_value_of(c);
+
+ if(idx == 0xFF)
+ throw Decoding_Error("Invalid base58");
+
+ v *= 58;
+ v += idx;
+ }
+
+ std::vector<uint8_t> output(v.bytes() + leading_zeros);
+ v.binary_encode(output.data() + leading_zeros);
+ return output;
+ }
+
+std::vector<uint8_t> base58_check_decode(const char input[], size_t input_length)
+ {
+ std::vector<uint8_t> dec = base58_decode(input, input_length);
+
+ if(dec.size() < 4)
+ throw Decoding_Error("Invalid base58 too short for checksum");
+
+ const uint32_t computed_checksum = sha256_d_checksum(dec.data(), dec.size() - 4);
+ const uint32_t checksum = load_be<uint32_t>(&dec[dec.size()-4], 0);
+
+ if(checksum != computed_checksum)
+ throw Decoding_Error("Invalid base58 checksum");
+
+ dec.resize(dec.size() - 4);
+
+ return dec;
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/codec/base58/base58.h b/comm/third_party/botan/src/lib/codec/base58/base58.h
new file mode 100644
index 0000000000..4654a0557c
--- /dev/null
+++ b/comm/third_party/botan/src/lib/codec/base58/base58.h
@@ -0,0 +1,76 @@
+/*
+* (C) 2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_BASE58_CODEC_H_
+#define BOTAN_BASE58_CODEC_H_
+
+#include <botan/secmem.h>
+#include <vector>
+#include <string>
+
+namespace Botan {
+
+/**
+* Perform base58 encoding
+*
+* This is raw base58 encoding, without the checksum
+*/
+std::string
+BOTAN_PUBLIC_API(2,9) base58_encode(const uint8_t input[],
+ size_t input_length);
+
+/**
+* Perform base58 encoding with checksum
+*/
+std::string
+BOTAN_PUBLIC_API(2,9) base58_check_encode(const uint8_t input[],
+ size_t input_length);
+
+
+/**
+* Perform base58 decoding
+*
+* This is raw base58 encoding, without the checksum
+*/
+std::vector<uint8_t>
+BOTAN_PUBLIC_API(2,9) base58_decode(const char input[],
+ size_t input_length);
+
+/**
+* Perform base58 decoding with checksum
+*/
+std::vector<uint8_t>
+BOTAN_PUBLIC_API(2,9) base58_check_decode(const char input[],
+ size_t input_length);
+
+
+// Some convenience wrappers:
+
+template<typename Alloc>
+inline std::string base58_encode(const std::vector<uint8_t, Alloc>& vec)
+ {
+ return base58_encode(vec.data(), vec.size());
+ }
+
+template<typename Alloc>
+inline std::string base58_check_encode(const std::vector<uint8_t, Alloc>& vec)
+ {
+ return base58_check_encode(vec.data(), vec.size());
+ }
+
+inline std::vector<uint8_t> base58_decode(const std::string& s)
+ {
+ return base58_decode(s.data(), s.size());
+ }
+
+inline std::vector<uint8_t> base58_check_decode(const std::string& s)
+ {
+ return base58_check_decode(s.data(), s.size());
+ }
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/codec/base58/info.txt b/comm/third_party/botan/src/lib/codec/base58/info.txt
new file mode 100644
index 0000000000..0b09c016d0
--- /dev/null
+++ b/comm/third_party/botan/src/lib/codec/base58/info.txt
@@ -0,0 +1,8 @@
+<defines>
+BASE58_CODEC -> 20181209
+</defines>
+
+<requires>
+sha2_32
+bigint
+</requires>
diff --git a/comm/third_party/botan/src/lib/codec/base64/base64.cpp b/comm/third_party/botan/src/lib/codec/base64/base64.cpp
new file mode 100644
index 0000000000..93675f0e34
--- /dev/null
+++ b/comm/third_party/botan/src/lib/codec/base64/base64.cpp
@@ -0,0 +1,248 @@
+/*
+* Base64 Encoding and Decoding
+* (C) 2010,2015,2020 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/base64.h>
+#include <botan/internal/codec_base.h>
+#include <botan/exceptn.h>
+#include <botan/internal/rounding.h>
+#include <botan/internal/ct_utils.h>
+
+namespace Botan {
+
+namespace {
+
+class Base64 final
+ {
+ public:
+ static inline std::string name() noexcept
+ {
+ return "base64";
+ }
+
+ static inline size_t encoding_bytes_in() noexcept
+ {
+ return m_encoding_bytes_in;
+ }
+ static inline size_t encoding_bytes_out() noexcept
+ {
+ return m_encoding_bytes_out;
+ }
+
+ static inline size_t decoding_bytes_in() noexcept
+ {
+ return m_encoding_bytes_out;
+ }
+ static inline size_t decoding_bytes_out() noexcept
+ {
+ return m_encoding_bytes_in;
+ }
+
+ static inline size_t bits_consumed() noexcept
+ {
+ return m_encoding_bits;
+ }
+ static inline size_t remaining_bits_before_padding() noexcept
+ {
+ return m_remaining_bits_before_padding;
+ }
+
+ static inline 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 inline 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;
+ }
+
+ static void encode(char out[8], const uint8_t in[5]) noexcept;
+
+ static uint8_t lookup_binary_value(char input) noexcept;
+
+ static bool check_bad_char(uint8_t bin, char input, bool ignore_ws);
+
+ static void decode(uint8_t* out_ptr, const uint8_t decode_buf[4])
+ {
+ out_ptr[0] = (decode_buf[0] << 2) | (decode_buf[1] >> 4);
+ out_ptr[1] = (decode_buf[1] << 4) | (decode_buf[2] >> 2);
+ out_ptr[2] = (decode_buf[2] << 6) | decode_buf[3];
+ }
+
+ static inline 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 const size_t m_encoding_bytes_in = 3;
+ static const size_t m_encoding_bytes_out = 4;
+ };
+
+char lookup_base64_char(uint8_t x)
+ {
+ BOTAN_DEBUG_ASSERT(x < 64);
+
+ const auto in_az = CT::Mask<uint8_t>::is_within_range(x, 26, 51);
+ const auto in_09 = CT::Mask<uint8_t>::is_within_range(x, 52, 61);
+ const auto eq_plus = CT::Mask<uint8_t>::is_equal(x, 62);
+ const auto eq_slash = CT::Mask<uint8_t>::is_equal(x, 63);
+
+ const char c_AZ = 'A' + x;
+ const char c_az = 'a' + (x - 26);
+ const char c_09 = '0' + (x - 2*26);
+ const char c_plus = '+';
+ const char c_slash = '/';
+
+ char ret = c_AZ;
+ ret = in_az.select(c_az, ret);
+ ret = in_09.select(c_09, ret);
+ ret = eq_plus.select(c_plus, ret);
+ ret = eq_slash.select(c_slash, ret);
+
+ return ret;
+ }
+
+//static
+void Base64::encode(char out[8], const uint8_t in[5]) noexcept
+ {
+ const uint8_t b0 = (in[0] & 0xFC) >> 2;
+ const uint8_t b1 = ((in[0] & 0x03) << 4) | (in[1] >> 4);
+ const uint8_t b2 = ((in[1] & 0x0F) << 2) | (in[2] >> 6);
+ const uint8_t b3 = in[2] & 0x3F;
+ out[0] = lookup_base64_char(b0);
+ out[1] = lookup_base64_char(b1);
+ out[2] = lookup_base64_char(b2);
+ out[3] = lookup_base64_char(b3);
+ }
+
+//static
+uint8_t Base64::lookup_binary_value(char input) noexcept
+ {
+ const uint8_t c = static_cast<uint8_t>(input);
+
+ const auto is_alpha_upper = CT::Mask<uint8_t>::is_within_range(c, uint8_t('A'), uint8_t('Z'));
+ const auto is_alpha_lower = CT::Mask<uint8_t>::is_within_range(c, uint8_t('a'), uint8_t('z'));
+ const auto is_decimal = CT::Mask<uint8_t>::is_within_range(c, uint8_t('0'), uint8_t('9'));
+
+ const auto is_plus = CT::Mask<uint8_t>::is_equal(c, uint8_t('+'));
+ const auto is_slash = CT::Mask<uint8_t>::is_equal(c, uint8_t('/'));
+ const auto is_equal = CT::Mask<uint8_t>::is_equal(c, uint8_t('='));
+
+ const auto is_whitespace = CT::Mask<uint8_t>::is_any_of(c, {
+ uint8_t(' '), uint8_t('\t'), uint8_t('\n'), uint8_t('\r')
+ });
+
+ const uint8_t c_upper = c - uint8_t('A');
+ const uint8_t c_lower = c - uint8_t('a') + 26;
+ const uint8_t c_decim = c - uint8_t('0') + 2*26;
+
+ uint8_t ret = 0xFF; // default value
+
+ ret = is_alpha_upper.select(c_upper, ret);
+ ret = is_alpha_lower.select(c_lower, ret);
+ ret = is_decimal.select(c_decim, ret);
+ ret = is_plus.select(62, ret);
+ ret = is_slash.select(63, ret);
+ ret = is_equal.select(0x81, ret);
+ ret = is_whitespace.select(0x80, ret);
+
+ return ret;
+ }
+
+//static
+bool Base64::check_bad_char(uint8_t bin, char input, bool ignore_ws)
+ {
+ if(bin <= 0x3F)
+ {
+ return true;
+ }
+ else if(!(bin == 0x81 || (bin == 0x80 && ignore_ws)))
+ {
+ std::string bad_char(1, input);
+ if(bad_char == "\t")
+ { bad_char = "\\t"; }
+ else if(bad_char == "\n")
+ { bad_char = "\\n"; }
+ else if(bad_char == "\r")
+ { bad_char = "\\r"; }
+
+ throw Invalid_Argument(
+ std::string("base64_decode: invalid base64 character '") +
+ bad_char + "'");
+ }
+ return false;
+ }
+
+}
+
+size_t base64_encode(char out[],
+ const uint8_t in[],
+ size_t input_length,
+ size_t& input_consumed,
+ bool final_inputs)
+ {
+ return base_encode(Base64(), out, in, input_length, input_consumed, final_inputs);
+ }
+
+std::string base64_encode(const uint8_t input[],
+ size_t input_length)
+ {
+ return base_encode_to_string(Base64(), input, input_length);
+ }
+
+size_t base64_decode(uint8_t out[],
+ const char in[],
+ size_t input_length,
+ size_t& input_consumed,
+ bool final_inputs,
+ bool ignore_ws)
+ {
+ return base_decode(Base64(), out, in, input_length, input_consumed, final_inputs, ignore_ws);
+ }
+
+size_t base64_decode(uint8_t output[],
+ const char input[],
+ size_t input_length,
+ bool ignore_ws)
+ {
+ return base_decode_full(Base64(), output, input, input_length, ignore_ws);
+ }
+
+size_t base64_decode(uint8_t output[],
+ const std::string& input,
+ bool ignore_ws)
+ {
+ return base64_decode(output, input.data(), input.length(), ignore_ws);
+ }
+
+secure_vector<uint8_t> base64_decode(const char input[],
+ size_t input_length,
+ bool ignore_ws)
+ {
+ return base_decode_to_vec<secure_vector<uint8_t>>(Base64(), input, input_length, ignore_ws);
+ }
+
+secure_vector<uint8_t> base64_decode(const std::string& input,
+ bool ignore_ws)
+ {
+ return base64_decode(input.data(), input.size(), ignore_ws);
+ }
+
+size_t base64_encode_max_output(size_t input_length)
+ {
+ return Base64::encode_max_output(input_length);
+ }
+
+size_t base64_decode_max_output(size_t input_length)
+ {
+ return Base64::decode_max_output(input_length);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/codec/base64/base64.h b/comm/third_party/botan/src/lib/codec/base64/base64.h
new file mode 100644
index 0000000000..a20d03b0f7
--- /dev/null
+++ b/comm/third_party/botan/src/lib/codec/base64/base64.h
@@ -0,0 +1,141 @@
+/*
+* Base64 Encoding and Decoding
+* (C) 2010 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_BASE64_CODEC_H_
+#define BOTAN_BASE64_CODEC_H_
+
+#include <botan/secmem.h>
+#include <string>
+
+namespace Botan {
+
+/**
+* Perform base64 encoding
+* @param output an array of at least base64_encode_max_output bytes
+* @param input is some binary data
+* @param input_length length of input in bytes
+* @param input_consumed is an output parameter which says how many
+* bytes of input were actually consumed. If less than
+* input_length, then the range input[consumed:length]
+* should be passed in later along with more input.
+* @param final_inputs true iff this is the last input, in which case
+ padding chars will be applied if needed
+* @return number of bytes written to output
+*/
+size_t BOTAN_PUBLIC_API(2,0) base64_encode(char output[],
+ const uint8_t input[],
+ size_t input_length,
+ size_t& input_consumed,
+ bool final_inputs);
+
+/**
+* Perform base64 encoding
+* @param input some input
+* @param input_length length of input in bytes
+* @return base64adecimal representation of input
+*/
+std::string BOTAN_PUBLIC_API(2,0) base64_encode(const uint8_t input[],
+ size_t input_length);
+
+/**
+* Perform base64 encoding
+* @param input some input
+* @return base64adecimal representation of input
+*/
+template<typename Alloc>
+std::string base64_encode(const std::vector<uint8_t, Alloc>& input)
+ {
+ return base64_encode(input.data(), input.size());
+ }
+
+/**
+* Perform base64 decoding
+* @param output an array of at least base64_decode_max_output bytes
+* @param input some base64 input
+* @param input_length length of input in bytes
+* @param input_consumed is an output parameter which says how many
+* bytes of input were actually consumed. If less than
+* input_length, then the range input[consumed:length]
+* should be passed in later along with more input.
+* @param final_inputs true iff this is the last input, in which case
+ padding is allowed
+* @param ignore_ws ignore whitespace on input; if false, throw an
+ exception if whitespace is encountered
+* @return number of bytes written to output
+*/
+size_t BOTAN_PUBLIC_API(2,0) base64_decode(uint8_t output[],
+ const char input[],
+ size_t input_length,
+ size_t& input_consumed,
+ bool final_inputs,
+ bool ignore_ws = true);
+
+/**
+* Perform base64 decoding
+* @param output an array of at least base64_decode_max_output bytes
+* @param input some base64 input
+* @param input_length length of input in bytes
+* @param ignore_ws ignore whitespace on input; if false, throw an
+ exception if whitespace is encountered
+* @return number of bytes written to output
+*/
+size_t BOTAN_PUBLIC_API(2,0) base64_decode(uint8_t output[],
+ const char input[],
+ size_t input_length,
+ bool ignore_ws = true);
+
+/**
+* Perform base64 decoding
+* @param output an array of at least base64_decode_max_output bytes
+* @param input some base64 input
+* @param ignore_ws ignore whitespace on input; if false, throw an
+ exception if whitespace is encountered
+* @return number of bytes written to output
+*/
+size_t BOTAN_PUBLIC_API(2,0) base64_decode(uint8_t output[],
+ const std::string& input,
+ bool ignore_ws = true);
+
+/**
+* Perform base64 decoding
+* @param input some base64 input
+* @param input_length the length of input in bytes
+* @param ignore_ws ignore whitespace on input; if false, throw an
+ exception if whitespace is encountered
+* @return decoded base64 output
+*/
+secure_vector<uint8_t> BOTAN_PUBLIC_API(2,0) base64_decode(const char input[],
+ size_t input_length,
+ bool ignore_ws = true);
+
+/**
+* Perform base64 decoding
+* @param input some base64 input
+* @param ignore_ws ignore whitespace on input; if false, throw an
+ exception if whitespace is encountered
+* @return decoded base64 output
+*/
+secure_vector<uint8_t> BOTAN_PUBLIC_API(2,0) base64_decode(const std::string& input,
+ bool ignore_ws = true);
+
+/**
+* Calculate the size of output buffer for base64_encode
+* @param input_length the length of input in bytes
+* @return the size of output buffer in bytes
+*/
+size_t BOTAN_PUBLIC_API(2,1) base64_encode_max_output(size_t input_length);
+
+/**
+* Calculate the size of output buffer for base64_decode
+* @param input_length the length of input in bytes
+* @return the size of output buffer in bytes
+*/
+size_t BOTAN_PUBLIC_API(2,1) base64_decode_max_output(size_t input_length);
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/codec/base64/info.txt b/comm/third_party/botan/src/lib/codec/base64/info.txt
new file mode 100644
index 0000000000..ceed636053
--- /dev/null
+++ b/comm/third_party/botan/src/lib/codec/base64/info.txt
@@ -0,0 +1,3 @@
+<defines>
+BASE64_CODEC -> 20131128
+</defines>
diff --git a/comm/third_party/botan/src/lib/codec/hex/hex.cpp b/comm/third_party/botan/src/lib/codec/hex/hex.cpp
new file mode 100644
index 0000000000..1ae21f398b
--- /dev/null
+++ b/comm/third_party/botan/src/lib/codec/hex/hex.cpp
@@ -0,0 +1,210 @@
+/*
+* Hex Encoding and Decoding
+* (C) 2010,2020 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/hex.h>
+#include <botan/mem_ops.h>
+#include <botan/exceptn.h>
+#include <botan/internal/ct_utils.h>
+
+namespace Botan {
+
+namespace {
+
+char hex_encode_nibble(uint8_t n, bool uppercase)
+ {
+ BOTAN_DEBUG_ASSERT(n <= 15);
+
+ const auto in_09 = CT::Mask<uint8_t>::is_lt(n, 10);
+
+ const char c_09 = n + '0';
+ const char c_af = n + (uppercase ? 'A' : 'a') - 10;
+
+ return in_09.select(c_09, c_af);
+ }
+
+}
+
+void hex_encode(char output[],
+ const uint8_t input[],
+ size_t input_length,
+ bool uppercase)
+ {
+ for(size_t i = 0; i != input_length; ++i)
+ {
+ const uint8_t n0 = (input[i] >> 4) & 0xF;
+ const uint8_t n1 = (input[i] ) & 0xF;
+
+ output[2*i ] = hex_encode_nibble(n0, uppercase);
+ output[2*i+1] = hex_encode_nibble(n1, uppercase);
+ }
+ }
+
+std::string hex_encode(const uint8_t input[],
+ size_t input_length,
+ bool uppercase)
+ {
+ std::string output(2 * input_length, 0);
+
+ if(input_length)
+ hex_encode(&output.front(), input, input_length, uppercase);
+
+ return output;
+ }
+
+namespace {
+
+uint8_t hex_char_to_bin(char input)
+ {
+ const uint8_t c = static_cast<uint8_t>(input);
+
+ const auto is_alpha_upper = CT::Mask<uint8_t>::is_within_range(c, uint8_t('A'), uint8_t('F'));
+ const auto is_alpha_lower = CT::Mask<uint8_t>::is_within_range(c, uint8_t('a'), uint8_t('f'));
+ const auto is_decimal = CT::Mask<uint8_t>::is_within_range(c, uint8_t('0'), uint8_t('9'));
+
+ const auto is_whitespace = CT::Mask<uint8_t>::is_any_of(c, {
+ uint8_t(' '), uint8_t('\t'), uint8_t('\n'), uint8_t('\r')
+ });
+
+ const uint8_t c_upper = c - uint8_t('A') + 10;
+ const uint8_t c_lower = c - uint8_t('a') + 10;
+ const uint8_t c_decim = c - uint8_t('0');
+
+ uint8_t ret = 0xFF; // default value
+
+ ret = is_alpha_upper.select(c_upper, ret);
+ ret = is_alpha_lower.select(c_lower, ret);
+ ret = is_decimal.select(c_decim, ret);
+ ret = is_whitespace.select(0x80, ret);
+
+ return ret;
+ }
+
+}
+
+
+size_t hex_decode(uint8_t output[],
+ const char input[],
+ size_t input_length,
+ size_t& input_consumed,
+ bool ignore_ws)
+ {
+ uint8_t* out_ptr = output;
+ bool top_nibble = true;
+
+ clear_mem(output, input_length / 2);
+
+ for(size_t i = 0; i != input_length; ++i)
+ {
+ const uint8_t bin = hex_char_to_bin(input[i]);
+
+ if(bin >= 0x10)
+ {
+ if(bin == 0x80 && ignore_ws)
+ continue;
+
+ std::string bad_char(1, input[i]);
+ if(bad_char == "\t")
+ bad_char = "\\t";
+ else if(bad_char == "\n")
+ bad_char = "\\n";
+
+ throw Invalid_Argument(
+ std::string("hex_decode: invalid hex character '") +
+ bad_char + "'");
+ }
+
+ if(top_nibble)
+ *out_ptr |= bin << 4;
+ else
+ *out_ptr |= bin;
+
+ top_nibble = !top_nibble;
+ if(top_nibble)
+ ++out_ptr;
+ }
+
+ input_consumed = input_length;
+ size_t written = (out_ptr - output);
+
+ /*
+ * We only got half of a uint8_t at the end; zap the half-written
+ * output and mark it as unread
+ */
+ if(!top_nibble)
+ {
+ *out_ptr = 0;
+ input_consumed -= 1;
+ }
+
+ return written;
+ }
+
+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);
+
+ if(consumed != input_length)
+ throw Invalid_Argument("hex_decode: input did not have full bytes");
+
+ return written;
+ }
+
+size_t hex_decode(uint8_t output[],
+ const std::string& input,
+ bool ignore_ws)
+ {
+ return hex_decode(output, input.data(), input.length(), ignore_ws);
+ }
+
+secure_vector<uint8_t> hex_decode_locked(const char input[],
+ size_t input_length,
+ bool ignore_ws)
+ {
+ secure_vector<uint8_t> bin(1 + input_length / 2);
+
+ size_t written = hex_decode(bin.data(),
+ input,
+ input_length,
+ ignore_ws);
+
+ bin.resize(written);
+ return bin;
+ }
+
+secure_vector<uint8_t> hex_decode_locked(const std::string& input,
+ bool ignore_ws)
+ {
+ return hex_decode_locked(input.data(), input.size(), ignore_ws);
+ }
+
+std::vector<uint8_t> hex_decode(const char input[],
+ size_t input_length,
+ bool ignore_ws)
+ {
+ std::vector<uint8_t> bin(1 + input_length / 2);
+
+ size_t written = hex_decode(bin.data(),
+ input,
+ input_length,
+ ignore_ws);
+
+ bin.resize(written);
+ return bin;
+ }
+
+std::vector<uint8_t> hex_decode(const std::string& input,
+ bool ignore_ws)
+ {
+ return hex_decode(input.data(), input.size(), ignore_ws);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/codec/hex/hex.h b/comm/third_party/botan/src/lib/codec/hex/hex.h
new file mode 100644
index 0000000000..330d8a69a5
--- /dev/null
+++ b/comm/third_party/botan/src/lib/codec/hex/hex.h
@@ -0,0 +1,148 @@
+/*
+* Hex Encoding and Decoding
+* (C) 2010 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_HEX_CODEC_H_
+#define BOTAN_HEX_CODEC_H_
+
+#include <botan/secmem.h>
+#include <string>
+
+namespace Botan {
+
+/**
+* Perform hex encoding
+* @param output an array of at least input_length*2 bytes
+* @param input is some binary data
+* @param input_length length of input in bytes
+* @param uppercase should output be upper or lower case?
+*/
+void BOTAN_PUBLIC_API(2,0) hex_encode(char output[],
+ const uint8_t input[],
+ size_t input_length,
+ bool uppercase = true);
+
+/**
+* Perform hex encoding
+* @param input some input
+* @param input_length length of input in bytes
+* @param uppercase should output be upper or lower case?
+* @return hexadecimal representation of input
+*/
+std::string BOTAN_PUBLIC_API(2,0) hex_encode(const uint8_t input[],
+ size_t input_length,
+ bool uppercase = true);
+
+/**
+* Perform hex encoding
+* @param input some input
+* @param uppercase should output be upper or lower case?
+* @return hexadecimal representation of input
+*/
+template<typename Alloc>
+std::string hex_encode(const std::vector<uint8_t, Alloc>& input,
+ bool uppercase = true)
+ {
+ return hex_encode(input.data(), input.size(), uppercase);
+ }
+
+/**
+* Perform hex decoding
+* @param output an array of at least input_length/2 bytes
+* @param input some hex input
+* @param input_length length of input in bytes
+* @param input_consumed is an output parameter which says how many
+* bytes of input were actually consumed. If less than
+* input_length, then the range input[consumed:length]
+* should be passed in later along with more input.
+* @param ignore_ws ignore whitespace on input; if false, throw an
+ exception if whitespace is encountered
+* @return number of bytes written to output
+*/
+size_t BOTAN_PUBLIC_API(2,0) hex_decode(uint8_t output[],
+ const char input[],
+ size_t input_length,
+ size_t& input_consumed,
+ bool ignore_ws = true);
+
+/**
+* Perform hex decoding
+* @param output an array of at least input_length/2 bytes
+* @param input some hex input
+* @param input_length length of input in bytes
+* @param ignore_ws ignore whitespace on input; if false, throw an
+ exception if whitespace is encountered
+* @return number of bytes written to output
+*/
+size_t BOTAN_PUBLIC_API(2,0) hex_decode(uint8_t output[],
+ const char input[],
+ size_t input_length,
+ bool ignore_ws = true);
+
+/**
+* Perform hex decoding
+* @param output an array of at least input_length/2 bytes
+* @param input some hex input
+* @param ignore_ws ignore whitespace on input; if false, throw an
+ exception if whitespace is encountered
+* @return number of bytes written to output
+*/
+size_t BOTAN_PUBLIC_API(2,0) hex_decode(uint8_t output[],
+ const std::string& input,
+ bool ignore_ws = true);
+
+/**
+* Perform hex decoding
+* @param input some hex input
+* @param input_length the length of input in bytes
+* @param ignore_ws ignore whitespace on input; if false, throw an
+ exception if whitespace is encountered
+* @return decoded hex output
+*/
+std::vector<uint8_t> BOTAN_PUBLIC_API(2,0)
+hex_decode(const char input[],
+ size_t input_length,
+ bool ignore_ws = true);
+
+/**
+* Perform hex decoding
+* @param input some hex input
+* @param ignore_ws ignore whitespace on input; if false, throw an
+ exception if whitespace is encountered
+* @return decoded hex output
+*/
+std::vector<uint8_t> BOTAN_PUBLIC_API(2,0)
+hex_decode(const std::string& input,
+ bool ignore_ws = true);
+
+
+/**
+* Perform hex decoding
+* @param input some hex input
+* @param input_length the length of input in bytes
+* @param ignore_ws ignore whitespace on input; if false, throw an
+ exception if whitespace is encountered
+* @return decoded hex output
+*/
+secure_vector<uint8_t> BOTAN_PUBLIC_API(2,0)
+hex_decode_locked(const char input[],
+ size_t input_length,
+ bool ignore_ws = true);
+
+/**
+* Perform hex decoding
+* @param input some hex input
+* @param ignore_ws ignore whitespace on input; if false, throw an
+ exception if whitespace is encountered
+* @return decoded hex output
+*/
+secure_vector<uint8_t> BOTAN_PUBLIC_API(2,0)
+hex_decode_locked(const std::string& input,
+ bool ignore_ws = true);
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/codec/hex/info.txt b/comm/third_party/botan/src/lib/codec/hex/info.txt
new file mode 100644
index 0000000000..6ee27e57c2
--- /dev/null
+++ b/comm/third_party/botan/src/lib/codec/hex/info.txt
@@ -0,0 +1,3 @@
+<defines>
+HEX_CODEC -> 20131128
+</defines>
diff --git a/comm/third_party/botan/src/lib/compat/sodium/info.txt b/comm/third_party/botan/src/lib/compat/sodium/info.txt
new file mode 100644
index 0000000000..4adc853d9a
--- /dev/null
+++ b/comm/third_party/botan/src/lib/compat/sodium/info.txt
@@ -0,0 +1,17 @@
+<defines>
+SODIUM_API -> 20190615
+</defines>
+
+<requires>
+chacha
+salsa20
+poly1305
+chacha20poly1305
+curve25519
+ed25519
+sha2_32
+sha2_64
+hmac
+siphash
+system_rng
+</requires>
diff --git a/comm/third_party/botan/src/lib/compat/sodium/sodium.h b/comm/third_party/botan/src/lib/compat/sodium/sodium.h
new file mode 100644
index 0000000000..821fd4f5d0
--- /dev/null
+++ b/comm/third_party/botan/src/lib/compat/sodium/sodium.h
@@ -0,0 +1,1453 @@
+/*
+* (C) 2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_SODIUM_COMPAT_H_
+#define BOTAN_SODIUM_COMPAT_H_
+
+#include <botan/types.h>
+
+namespace Botan {
+
+/**
+* The Sodium namespace contains a partial implementation of the
+* libsodium API.
+*/
+namespace Sodium {
+
+// sodium/randombytes.h
+enum Sodium_Constants : size_t {
+ SODIUM_SIZE_MAX = 0xFFFFFFFF,
+
+ crypto_aead_chacha20poly1305_ABYTES = 16,
+ crypto_aead_chacha20poly1305_KEYBYTES = 32,
+ crypto_aead_chacha20poly1305_MESSAGEBYTES_MAX = SODIUM_SIZE_MAX,
+ crypto_aead_chacha20poly1305_NPUBBYTES = 8,
+ crypto_aead_chacha20poly1305_NSECBYTES = 0,
+
+ crypto_aead_chacha20poly1305_ietf_ABYTES = 16,
+ crypto_aead_chacha20poly1305_ietf_KEYBYTES = 32,
+ crypto_aead_chacha20poly1305_ietf_MESSAGEBYTES_MAX = SODIUM_SIZE_MAX,
+ crypto_aead_chacha20poly1305_ietf_NPUBBYTES = 12,
+ crypto_aead_chacha20poly1305_ietf_NSECBYTES = 0,
+
+ crypto_aead_xchacha20poly1305_ietf_ABYTES = 16,
+ crypto_aead_xchacha20poly1305_ietf_KEYBYTES = 32,
+ crypto_aead_xchacha20poly1305_ietf_MESSAGEBYTES_MAX = SODIUM_SIZE_MAX,
+ crypto_aead_xchacha20poly1305_ietf_NPUBBYTES = 24,
+ crypto_aead_xchacha20poly1305_ietf_NSECBYTES = 0,
+
+ crypto_auth_hmacsha256_BYTES = 32,
+ crypto_auth_hmacsha256_KEYBYTES = 32,
+ crypto_auth_hmacsha512256_BYTES = 32,
+ crypto_auth_hmacsha512256_KEYBYTES = 32,
+ crypto_auth_hmacsha512_BYTES = 64,
+ crypto_auth_hmacsha512_KEYBYTES = 32,
+
+ crypto_auth_BYTES = crypto_auth_hmacsha512256_BYTES,
+ crypto_auth_KEYBYTES = crypto_auth_hmacsha512256_KEYBYTES,
+
+ crypto_box_curve25519xsalsa20poly1305_BEFORENMBYTES = 32,
+ crypto_box_curve25519xsalsa20poly1305_BOXZEROBYTES = 16,
+ crypto_box_curve25519xsalsa20poly1305_MACBYTES = 16,
+ crypto_box_curve25519xsalsa20poly1305_MESSAGEBYTES_MAX = SODIUM_SIZE_MAX,
+ crypto_box_curve25519xsalsa20poly1305_NONCEBYTES = 24,
+ crypto_box_curve25519xsalsa20poly1305_PUBLICKEYBYTES = 32,
+ crypto_box_curve25519xsalsa20poly1305_SECRETKEYBYTES = 32,
+ crypto_box_curve25519xsalsa20poly1305_SEEDBYTES = 32,
+ crypto_box_curve25519xsalsa20poly1305_ZEROBYTES = crypto_box_curve25519xsalsa20poly1305_BOXZEROBYTES + crypto_box_curve25519xsalsa20poly1305_MACBYTES,
+
+ crypto_box_BEFORENMBYTES = crypto_box_curve25519xsalsa20poly1305_BEFORENMBYTES,
+ crypto_box_BOXZEROBYTES = crypto_box_curve25519xsalsa20poly1305_BOXZEROBYTES,
+ crypto_box_MACBYTES = crypto_box_curve25519xsalsa20poly1305_MACBYTES,
+ crypto_box_MESSAGEBYTES_MAX = crypto_box_curve25519xsalsa20poly1305_MESSAGEBYTES_MAX,
+ crypto_box_NONCEBYTES = crypto_box_curve25519xsalsa20poly1305_NONCEBYTES,
+ crypto_box_PUBLICKEYBYTES = crypto_box_curve25519xsalsa20poly1305_PUBLICKEYBYTES,
+ crypto_box_SECRETKEYBYTES = crypto_box_curve25519xsalsa20poly1305_SECRETKEYBYTES,
+ crypto_box_SEEDBYTES = crypto_box_curve25519xsalsa20poly1305_SEEDBYTES,
+ crypto_box_ZEROBYTES = crypto_box_curve25519xsalsa20poly1305_ZEROBYTES,
+
+ crypto_core_hchacha20_CONSTBYTES = 16,
+ crypto_core_hchacha20_INPUTBYTES = 16,
+ crypto_core_hchacha20_KEYBYTES = 32,
+ crypto_core_hchacha20_OUTPUTBYTES = 32,
+
+ crypto_core_hsalsa20_CONSTBYTES = 16,
+ crypto_core_hsalsa20_INPUTBYTES = 16,
+ crypto_core_hsalsa20_KEYBYTES = 32,
+ crypto_core_hsalsa20_OUTPUTBYTES = 32,
+
+ crypto_hash_sha256_BYTES = 32,
+ crypto_hash_sha512_BYTES = 64,
+ crypto_hash_BYTES = crypto_hash_sha512_BYTES,
+
+ crypto_onetimeauth_poly1305_BYTES = 16,
+ crypto_onetimeauth_poly1305_KEYBYTES = 32,
+ crypto_onetimeauth_BYTES = crypto_onetimeauth_poly1305_BYTES,
+ crypto_onetimeauth_KEYBYTES = crypto_onetimeauth_poly1305_KEYBYTES,
+
+ crypto_scalarmult_curve25519_BYTES = 32,
+ crypto_scalarmult_curve25519_SCALARBYTES = 32,
+ crypto_scalarmult_BYTES = crypto_scalarmult_curve25519_BYTES,
+ crypto_scalarmult_SCALARBYTES = crypto_scalarmult_curve25519_SCALARBYTES,
+
+ crypto_secretbox_xsalsa20poly1305_BOXZEROBYTES = 16,
+ crypto_secretbox_xsalsa20poly1305_KEYBYTES = 32,
+ crypto_secretbox_xsalsa20poly1305_MACBYTES = 16,
+ crypto_secretbox_xsalsa20poly1305_MESSAGEBYTES_MAX = SODIUM_SIZE_MAX,
+ crypto_secretbox_xsalsa20poly1305_NONCEBYTES = 24,
+ crypto_secretbox_xsalsa20poly1305_ZEROBYTES = crypto_secretbox_xsalsa20poly1305_BOXZEROBYTES + crypto_secretbox_xsalsa20poly1305_MACBYTES,
+
+ crypto_secretbox_BOXZEROBYTES = crypto_secretbox_xsalsa20poly1305_BOXZEROBYTES,
+ crypto_secretbox_KEYBYTES = crypto_secretbox_xsalsa20poly1305_KEYBYTES,
+ crypto_secretbox_MACBYTES = crypto_secretbox_xsalsa20poly1305_MACBYTES,
+ crypto_secretbox_MESSAGEBYTES_MAX = crypto_secretbox_xsalsa20poly1305_MESSAGEBYTES_MAX,
+ crypto_secretbox_NONCEBYTES = crypto_secretbox_xsalsa20poly1305_NONCEBYTES,
+ crypto_secretbox_ZEROBYTES = crypto_secretbox_xsalsa20poly1305_ZEROBYTES,
+
+ crypto_shorthash_siphash24_BYTES = 8,
+ crypto_shorthash_siphash24_KEYBYTES = 16,
+ crypto_shorthash_BYTES = crypto_shorthash_siphash24_BYTES,
+ crypto_shorthash_KEYBYTES = crypto_shorthash_siphash24_KEYBYTES,
+
+ crypto_sign_ed25519_BYTES = 64,
+ crypto_sign_ed25519_MESSAGEBYTES_MAX = (SODIUM_SIZE_MAX - crypto_sign_ed25519_BYTES),
+ crypto_sign_ed25519_PUBLICKEYBYTES = 32,
+ crypto_sign_ed25519_SECRETKEYBYTES = (32 + 32),
+ crypto_sign_ed25519_SEEDBYTES = 32,
+ crypto_sign_BYTES = crypto_sign_ed25519_BYTES,
+ crypto_sign_MESSAGEBYTES_MAX = crypto_sign_ed25519_MESSAGEBYTES_MAX,
+ crypto_sign_PUBLICKEYBYTES = crypto_sign_ed25519_PUBLICKEYBYTES,
+ crypto_sign_SECRETKEYBYTES = crypto_sign_ed25519_SECRETKEYBYTES,
+ crypto_sign_SEEDBYTES = crypto_sign_ed25519_SEEDBYTES,
+
+ crypto_stream_chacha20_KEYBYTES = 32,
+ crypto_stream_chacha20_MESSAGEBYTES_MAX = SODIUM_SIZE_MAX,
+ crypto_stream_chacha20_NONCEBYTES = 8,
+ crypto_stream_chacha20_ietf_KEYBYTES = 32,
+ crypto_stream_chacha20_ietf_MESSAGEBYTES_MAX = SODIUM_SIZE_MAX,
+ crypto_stream_chacha20_ietf_NONCEBYTES = 12,
+ crypto_stream_salsa20_KEYBYTES = 32,
+ crypto_stream_salsa20_MESSAGEBYTES_MAX = SODIUM_SIZE_MAX,
+ crypto_stream_salsa20_NONCEBYTES = 8,
+ crypto_stream_xchacha20_KEYBYTES = 32,
+ crypto_stream_xchacha20_MESSAGEBYTES_MAX = SODIUM_SIZE_MAX,
+ crypto_stream_xchacha20_NONCEBYTES = 24,
+ crypto_stream_xsalsa20_KEYBYTES = 32,
+ crypto_stream_xsalsa20_MESSAGEBYTES_MAX = SODIUM_SIZE_MAX,
+ crypto_stream_xsalsa20_NONCEBYTES = 24,
+ crypto_stream_KEYBYTES = crypto_stream_xsalsa20_KEYBYTES,
+ crypto_stream_MESSAGEBYTES_MAX = crypto_stream_xsalsa20_MESSAGEBYTES_MAX,
+ crypto_stream_NONCEBYTES = crypto_stream_xsalsa20_NONCEBYTES,
+
+ crypto_verify_16_BYTES = 16,
+ crypto_verify_32_BYTES = 32,
+ crypto_verify_64_BYTES = 64,
+
+ randombytes_SEEDBYTES = 32,
+};
+
+inline const char* sodium_version_string() { return "Botan Sodium Compat"; }
+
+inline int sodium_library_version_major() { return 0; }
+
+inline int sodium_library_version_minor() { return 0; }
+
+inline int sodium_library_minimal() { return 0; }
+
+inline int sodium_init() { return 0; }
+
+// sodium/crypto_verify_{16,32,64}.h
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_verify_16(const uint8_t x[16], const uint8_t y[16]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_verify_32(const uint8_t x[32], const uint8_t y[32]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_verify_64(const uint8_t x[64], const uint8_t y[64]);
+
+// sodium/utils.h
+BOTAN_PUBLIC_API(2,11)
+void sodium_memzero(void* ptr, size_t len);
+
+BOTAN_PUBLIC_API(2,11)
+int sodium_memcmp(const void* x, const void* y, size_t len);
+
+BOTAN_PUBLIC_API(2,11)
+int sodium_compare(const uint8_t x[], const uint8_t y[], size_t len);
+
+BOTAN_PUBLIC_API(2,11)
+int sodium_is_zero(const uint8_t nonce[], size_t nlen);
+
+BOTAN_PUBLIC_API(2,11)
+void sodium_increment(uint8_t n[], size_t nlen);
+
+BOTAN_PUBLIC_API(2,11)
+void sodium_add(uint8_t a[], const uint8_t b[], size_t len);
+
+BOTAN_PUBLIC_API(2,11)
+void* sodium_malloc(size_t size);
+
+BOTAN_PUBLIC_API(2,11)
+void* sodium_allocarray(size_t count, size_t size);
+
+BOTAN_PUBLIC_API(2,11)
+void sodium_free(void* ptr);
+
+BOTAN_PUBLIC_API(2,11)
+int sodium_mprotect_noaccess(void* ptr);
+
+BOTAN_PUBLIC_API(2,11)
+int sodium_mprotect_readwrite(void* ptr);
+
+// sodium/randombytes.h
+
+inline size_t randombytes_seedbytes() { return randombytes_SEEDBYTES; }
+
+BOTAN_PUBLIC_API(2,11)
+void randombytes_buf(void* buf, size_t size);
+
+BOTAN_PUBLIC_API(2,11)
+void randombytes_buf_deterministic(void* buf, size_t size,
+ const uint8_t seed[randombytes_SEEDBYTES]);
+
+BOTAN_PUBLIC_API(2,11)
+uint32_t randombytes_uniform(uint32_t upper_bound);
+
+inline uint32_t randombytes_random()
+ {
+ uint32_t x = 0;
+ randombytes_buf(&x, 4);
+ return x;
+ }
+
+inline void randombytes_stir() {}
+
+inline int randombytes_close() { return 0; }
+
+inline const char* randombytes_implementation_name()
+ {
+ return "botan";
+ }
+
+inline void randombytes(uint8_t buf[], size_t buf_len)
+ {
+ return randombytes_buf(buf, buf_len);
+ }
+
+// sodium/crypto_secretbox_xsalsa20poly1305.h
+
+inline size_t crypto_secretbox_xsalsa20poly1305_keybytes()
+ {
+ return crypto_secretbox_xsalsa20poly1305_KEYBYTES;
+ }
+
+inline size_t crypto_secretbox_xsalsa20poly1305_noncebytes()
+ {
+ return crypto_secretbox_xsalsa20poly1305_NONCEBYTES;
+ }
+
+inline size_t crypto_secretbox_xsalsa20poly1305_macbytes()
+ {
+ return crypto_secretbox_xsalsa20poly1305_MACBYTES;
+ }
+
+inline size_t crypto_secretbox_xsalsa20poly1305_messagebytes_max()
+ {
+ return crypto_secretbox_xsalsa20poly1305_MESSAGEBYTES_MAX;
+ }
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_secretbox_xsalsa20poly1305(uint8_t ctext[],
+ const uint8_t ptext[],
+ size_t ptext_len,
+ const uint8_t nonce[],
+ const uint8_t key[]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_secretbox_xsalsa20poly1305_open(uint8_t ptext[],
+ const uint8_t ctext[],
+ size_t ctext_len,
+ const uint8_t nonce[],
+ const uint8_t key[]);
+
+inline void crypto_secretbox_xsalsa20poly1305_keygen(uint8_t k[32])
+ {
+ return randombytes_buf(k, 32);
+ }
+
+inline size_t crypto_secretbox_xsalsa20poly1305_boxzerobytes()
+ {
+ return crypto_secretbox_xsalsa20poly1305_BOXZEROBYTES;
+ }
+
+inline size_t crypto_secretbox_xsalsa20poly1305_zerobytes()
+ {
+ return crypto_secretbox_xsalsa20poly1305_ZEROBYTES;
+ }
+
+// sodium/crypto_secretbox.h
+
+inline size_t crypto_secretbox_keybytes() { return crypto_secretbox_KEYBYTES; }
+
+inline size_t crypto_secretbox_noncebytes() { return crypto_secretbox_NONCEBYTES; }
+
+inline size_t crypto_secretbox_macbytes() { return crypto_secretbox_MACBYTES; }
+
+inline size_t crypto_secretbox_messagebytes_max() { return crypto_secretbox_xsalsa20poly1305_MESSAGEBYTES_MAX; }
+
+inline const char* crypto_secretbox_primitive() { return "xsalsa20poly1305"; }
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_secretbox_detached(uint8_t ctext[], uint8_t mac[],
+ const uint8_t ptext[],
+ size_t ptext_len,
+ const uint8_t nonce[],
+ const uint8_t key[]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_secretbox_open_detached(uint8_t ptext[],
+ const uint8_t ctext[],
+ const uint8_t mac[],
+ size_t ctext_len,
+ const uint8_t nonce[],
+ const uint8_t key[]);
+
+inline int crypto_secretbox_easy(uint8_t ctext[], const uint8_t ptext[],
+ size_t ptext_len, const uint8_t nonce[],
+ const uint8_t key[])
+ {
+ return crypto_secretbox_detached(ctext + crypto_secretbox_MACBYTES, ctext,
+ ptext, ptext_len, nonce, key);
+ }
+
+inline int crypto_secretbox_open_easy(uint8_t out[], const uint8_t ctext[], size_t ctext_len,
+ const uint8_t nonce[], const uint8_t key[])
+ {
+ if(ctext_len < crypto_secretbox_MACBYTES)
+ {
+ return -1;
+ }
+
+ return crypto_secretbox_open_detached(out, ctext + crypto_secretbox_MACBYTES,
+ ctext, ctext_len - crypto_secretbox_MACBYTES,
+ nonce, key);
+ }
+
+inline void crypto_secretbox_keygen(uint8_t k[32])
+ {
+ return randombytes_buf(k, 32);
+ }
+
+inline size_t crypto_secretbox_zerobytes()
+ {
+ return crypto_secretbox_ZEROBYTES;
+ }
+
+inline size_t crypto_secretbox_boxzerobytes()
+ {
+ return crypto_secretbox_BOXZEROBYTES;
+ }
+
+inline int crypto_secretbox(uint8_t ctext[], const uint8_t ptext[],
+ size_t ptext_len, const uint8_t nonce[],
+ const uint8_t key[])
+ {
+ return crypto_secretbox_xsalsa20poly1305(ctext, ptext, ptext_len, nonce, key);
+ }
+
+inline int crypto_secretbox_open(uint8_t ptext[], const uint8_t ctext[],
+ size_t ctext_len, const uint8_t nonce[],
+ const uint8_t key[])
+ {
+ return crypto_secretbox_xsalsa20poly1305_open(ptext, ctext, ctext_len, nonce, key);
+ }
+
+// sodium/crypto_aead_xchacha20poly1305.h
+
+inline size_t crypto_aead_chacha20poly1305_ietf_keybytes()
+ {
+ return crypto_aead_chacha20poly1305_ietf_KEYBYTES;
+ }
+
+inline size_t crypto_aead_chacha20poly1305_ietf_nsecbytes()
+ {
+ return crypto_aead_chacha20poly1305_ietf_NSECBYTES;
+ }
+
+inline size_t crypto_aead_chacha20poly1305_ietf_npubbytes()
+ {
+ return crypto_aead_chacha20poly1305_ietf_NPUBBYTES;
+ }
+
+inline size_t crypto_aead_chacha20poly1305_ietf_abytes()
+ {
+ return crypto_aead_chacha20poly1305_ietf_ABYTES;
+ }
+
+inline size_t crypto_aead_chacha20poly1305_ietf_messagebytes_max()
+ {
+ return crypto_aead_chacha20poly1305_ietf_MESSAGEBYTES_MAX;
+ }
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_aead_chacha20poly1305_ietf_encrypt(uint8_t ctext[],
+ unsigned long long* ctext_len,
+ const uint8_t ptext[],
+ size_t ptext_len,
+ const uint8_t ad[],
+ size_t ad_len,
+ const uint8_t unused_secret_nonce[],
+ const uint8_t nonce[],
+ const uint8_t key[]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_aead_chacha20poly1305_ietf_decrypt(uint8_t ptext[],
+ unsigned long long* ptext_len,
+ uint8_t unused_secret_nonce[],
+ const uint8_t ctext[],
+ size_t ctext_len,
+ const uint8_t ad[],
+ size_t ad_len,
+ const uint8_t nonce[],
+ const uint8_t key[]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_aead_chacha20poly1305_ietf_encrypt_detached(uint8_t ctext[],
+ uint8_t mac[],
+ unsigned long long* mac_len,
+ const uint8_t ptext[],
+ size_t ptext_len,
+ const uint8_t ad[],
+ size_t ad_len,
+ const uint8_t unused_secret_nonce[],
+ const uint8_t nonce[],
+ const uint8_t key[]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_aead_chacha20poly1305_ietf_decrypt_detached(uint8_t m[],
+ uint8_t unused_secret_nonce[],
+ const uint8_t ctext[],
+ size_t ctext_len,
+ const uint8_t mac[],
+ const uint8_t ad[],
+ size_t ad_len,
+ const uint8_t nonce[],
+ const uint8_t key[]);
+
+inline void crypto_aead_chacha20poly1305_ietf_keygen(uint8_t k[32])
+ {
+ return randombytes_buf(k, crypto_aead_chacha20poly1305_ietf_KEYBYTES);
+ }
+
+inline size_t crypto_aead_chacha20poly1305_keybytes()
+ {
+ return crypto_aead_chacha20poly1305_KEYBYTES;
+ }
+
+inline size_t crypto_aead_chacha20poly1305_nsecbytes()
+ {
+ return crypto_aead_chacha20poly1305_NSECBYTES;
+ }
+
+inline size_t crypto_aead_chacha20poly1305_npubbytes()
+ {
+ return crypto_aead_chacha20poly1305_NPUBBYTES;
+ }
+
+inline size_t crypto_aead_chacha20poly1305_abytes()
+ {
+ return crypto_aead_chacha20poly1305_ABYTES;
+ }
+
+inline size_t crypto_aead_chacha20poly1305_messagebytes_max()
+ {
+ return crypto_aead_chacha20poly1305_MESSAGEBYTES_MAX;
+ }
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_aead_chacha20poly1305_encrypt(uint8_t ctext[],
+ unsigned long long* ctext_len,
+ const uint8_t ptext[],
+ size_t ptext_len,
+ const uint8_t ad[],
+ size_t ad_len,
+ const uint8_t unused_secret_nonce[],
+ const uint8_t nonce[],
+ const uint8_t key[]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_aead_chacha20poly1305_decrypt(uint8_t m[],
+ unsigned long long* ptext_len,
+ uint8_t unused_secret_nonce[],
+ const uint8_t ctext[],
+ size_t ctext_len,
+ const uint8_t ad[],
+ size_t ad_len,
+ const uint8_t nonce[],
+ const uint8_t key[]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_aead_chacha20poly1305_encrypt_detached(uint8_t ctext[],
+ uint8_t mac[],
+ unsigned long long* mac_len,
+ const uint8_t ptext[],
+ size_t ptext_len,
+ const uint8_t ad[],
+ size_t ad_len,
+ const uint8_t unused_secret_nonce[],
+ const uint8_t nonce[],
+ const uint8_t key[]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_aead_chacha20poly1305_decrypt_detached(uint8_t m[],
+ uint8_t unused_secret_nonce[],
+ const uint8_t ctext[],
+ size_t ctext_len,
+ const uint8_t mac[],
+ const uint8_t ad[],
+ size_t ad_len,
+ const uint8_t nonce[],
+ const uint8_t key[]);
+
+inline void crypto_aead_chacha20poly1305_keygen(uint8_t k[32])
+ {
+ randombytes_buf(k, 32);
+ }
+
+// sodium/crypto_aead_xchacha20poly1305.h
+
+inline size_t crypto_aead_xchacha20poly1305_ietf_keybytes()
+ {
+ return crypto_aead_xchacha20poly1305_ietf_KEYBYTES;
+ }
+
+inline size_t crypto_aead_xchacha20poly1305_ietf_nsecbytes()
+ {
+ return crypto_aead_xchacha20poly1305_ietf_NSECBYTES;
+ }
+
+inline size_t crypto_aead_xchacha20poly1305_ietf_npubbytes()
+ {
+ return crypto_aead_xchacha20poly1305_ietf_NPUBBYTES;
+ }
+
+inline size_t crypto_aead_xchacha20poly1305_ietf_abytes()
+ {
+ return crypto_aead_xchacha20poly1305_ietf_ABYTES;
+ }
+
+inline size_t crypto_aead_xchacha20poly1305_ietf_messagebytes_max()
+ {
+ return crypto_aead_xchacha20poly1305_ietf_MESSAGEBYTES_MAX;
+ }
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_aead_xchacha20poly1305_ietf_encrypt(uint8_t ctext[],
+ unsigned long long* ctext_len,
+ const uint8_t ptext[],
+ size_t ptext_len,
+ const uint8_t ad[],
+ size_t ad_len,
+ const uint8_t unused_secret_nonce[],
+ const uint8_t nonce[],
+ const uint8_t key[]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_aead_xchacha20poly1305_ietf_decrypt(uint8_t ptext[],
+ unsigned long long* ptext_len,
+ uint8_t unused_secret_nonce[],
+ const uint8_t ctext[],
+ size_t ctext_len,
+ const uint8_t ad[],
+ size_t ad_len,
+ const uint8_t nonce[],
+ const uint8_t key[]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_aead_xchacha20poly1305_ietf_encrypt_detached(uint8_t ctext[],
+ uint8_t mac[],
+ unsigned long long* mac_len,
+ const uint8_t ptext[],
+ size_t ptext_len,
+ const uint8_t ad[],
+ size_t ad_len,
+ const uint8_t unused_secret_nonce[],
+ const uint8_t nonce[],
+ const uint8_t key[]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_aead_xchacha20poly1305_ietf_decrypt_detached(uint8_t ptext[],
+ uint8_t unused_secret_nonce[],
+ const uint8_t ctext[],
+ size_t ctext_len,
+ const uint8_t mac[],
+ const uint8_t ad[],
+ size_t ad_len,
+ const uint8_t nonce[],
+ const uint8_t key[]);
+
+inline void crypto_aead_xchacha20poly1305_ietf_keygen(uint8_t k[32])
+ {
+ return randombytes_buf(k, 32);
+ }
+
+// sodium/crypto_box_curve25519xsalsa20poly1305.h
+
+inline size_t crypto_box_curve25519xsalsa20poly1305_seedbytes()
+ {
+ return crypto_box_curve25519xsalsa20poly1305_SEEDBYTES;
+ }
+
+inline size_t crypto_box_curve25519xsalsa20poly1305_publickeybytes()
+ {
+ return crypto_box_curve25519xsalsa20poly1305_PUBLICKEYBYTES;
+ }
+
+inline size_t crypto_box_curve25519xsalsa20poly1305_secretkeybytes()
+ {
+ return crypto_box_curve25519xsalsa20poly1305_SECRETKEYBYTES;
+ }
+
+inline size_t crypto_box_curve25519xsalsa20poly1305_beforenmbytes()
+ {
+ return crypto_box_curve25519xsalsa20poly1305_BEFORENMBYTES;
+ }
+
+inline size_t crypto_box_curve25519xsalsa20poly1305_noncebytes()
+ {
+ return crypto_box_curve25519xsalsa20poly1305_NONCEBYTES;
+ }
+
+inline size_t crypto_box_curve25519xsalsa20poly1305_macbytes()
+ {
+ return crypto_box_curve25519xsalsa20poly1305_MACBYTES;
+ }
+
+inline size_t crypto_box_curve25519xsalsa20poly1305_messagebytes_max()
+ {
+ return crypto_box_curve25519xsalsa20poly1305_MESSAGEBYTES_MAX;
+ }
+
+inline size_t crypto_box_curve25519xsalsa20poly1305_boxzerobytes()
+ {
+ return crypto_box_curve25519xsalsa20poly1305_BOXZEROBYTES;
+ }
+
+inline size_t crypto_box_curve25519xsalsa20poly1305_zerobytes()
+ {
+ return crypto_box_curve25519xsalsa20poly1305_ZEROBYTES;
+ }
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_box_curve25519xsalsa20poly1305_seed_keypair(uint8_t pk[32],
+ uint8_t sk[32],
+ const uint8_t seed[32]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_box_curve25519xsalsa20poly1305_keypair(uint8_t pk[32],
+ uint8_t sk[32]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_box_curve25519xsalsa20poly1305_beforenm(uint8_t key[],
+ const uint8_t pk[32],
+ const uint8_t sk[32]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_box_curve25519xsalsa20poly1305(uint8_t ctext[],
+ const uint8_t ptext[],
+ size_t ptext_len,
+ const uint8_t nonce[],
+ const uint8_t pk[32],
+ const uint8_t sk[32]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_box_curve25519xsalsa20poly1305_open(uint8_t ptext[],
+ const uint8_t ctext[],
+ size_t ctext_len,
+ const uint8_t nonce[],
+ const uint8_t pk[32],
+ const uint8_t sk[32]);
+
+inline int crypto_box_curve25519xsalsa20poly1305_afternm(uint8_t ctext[],
+ const uint8_t ptext[],
+ size_t ptext_len,
+ const uint8_t nonce[],
+ const uint8_t key[])
+ {
+ return crypto_secretbox_xsalsa20poly1305(ctext, ptext, ptext_len, nonce, key);
+ }
+
+inline int crypto_box_curve25519xsalsa20poly1305_open_afternm(uint8_t ptext[],
+ const uint8_t ctext[],
+ size_t ctext_len,
+ const uint8_t nonce[],
+ const uint8_t key[])
+ {
+ return crypto_secretbox_xsalsa20poly1305_open(ptext, ctext, ctext_len, nonce, key);
+ }
+
+// sodium/crypto_box.h
+
+inline size_t crypto_box_seedbytes()
+ {
+ return crypto_box_SEEDBYTES;
+ }
+
+inline size_t crypto_box_publickeybytes()
+ {
+ return crypto_box_PUBLICKEYBYTES;
+ }
+
+inline size_t crypto_box_secretkeybytes()
+ {
+ return crypto_box_SECRETKEYBYTES;
+ }
+
+inline size_t crypto_box_noncebytes()
+ {
+ return crypto_box_NONCEBYTES;
+ }
+
+inline size_t crypto_box_macbytes()
+ {
+ return crypto_box_MACBYTES;
+ }
+
+inline size_t crypto_box_messagebytes_max()
+ {
+ return crypto_box_MESSAGEBYTES_MAX;
+ }
+
+inline size_t crypto_box_beforenmbytes()
+ {
+ return crypto_box_BEFORENMBYTES;
+ }
+
+inline const char* crypto_box_primitive() { return "curve25519xsalsa20poly1305"; }
+
+inline int crypto_box_seed_keypair(uint8_t pk[32], uint8_t sk[32],
+ const uint8_t seed[])
+ {
+ return crypto_box_curve25519xsalsa20poly1305_seed_keypair(pk, sk, seed);
+ }
+
+inline int crypto_box_keypair(uint8_t pk[32], uint8_t sk[32])
+ {
+ return crypto_box_curve25519xsalsa20poly1305_keypair(pk, sk);
+ }
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_box_detached(uint8_t ctext[], uint8_t mac[],
+ const uint8_t ptext[], size_t ptext_len,
+ const uint8_t nonce[], const uint8_t pk[32],
+ const uint8_t sk[32]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_box_open_detached(uint8_t ptext[], const uint8_t ctext[],
+ const uint8_t mac[],
+ size_t ctext_len,
+ const uint8_t nonce[],
+ const uint8_t pk[32],
+ const uint8_t sk[32]);
+
+inline int crypto_box_easy(uint8_t ctext[], const uint8_t ptext[],
+ size_t ptext_len, const uint8_t nonce[],
+ const uint8_t pk[32], const uint8_t sk[32])
+ {
+ return crypto_box_detached(ctext + crypto_box_MACBYTES, ctext, ptext, ptext_len, nonce, pk, sk);
+ }
+
+inline int crypto_box_open_easy(uint8_t ptext[], const uint8_t ctext[],
+ size_t ctext_len, const uint8_t nonce[],
+ const uint8_t pk[32], const uint8_t sk[32])
+ {
+ if(ctext_len < crypto_box_MACBYTES)
+ {
+ return -1;
+ }
+
+ return crypto_box_open_detached(ptext, ctext + crypto_box_MACBYTES,
+ ctext, ctext_len - crypto_box_MACBYTES,
+ nonce, pk, sk);
+ }
+
+inline int crypto_box_beforenm(uint8_t key[], const uint8_t pk[32],
+ const uint8_t sk[32])
+ {
+ return crypto_box_curve25519xsalsa20poly1305_beforenm(key, pk, sk);
+ }
+
+inline int crypto_box_afternm(uint8_t ctext[], const uint8_t ptext[],
+ size_t ptext_len, const uint8_t nonce[],
+ const uint8_t key[])
+ {
+ return crypto_box_curve25519xsalsa20poly1305_afternm(ctext, ptext, ptext_len, nonce, key);
+ }
+
+inline int crypto_box_open_afternm(uint8_t ptext[], const uint8_t ctext[],
+ size_t ctext_len, const uint8_t nonce[],
+ const uint8_t key[])
+ {
+ return crypto_box_curve25519xsalsa20poly1305_open_afternm(ptext, ctext, ctext_len, nonce, key);
+ }
+
+inline int crypto_box_open_detached_afternm(uint8_t ptext[], const uint8_t ctext[],
+ const uint8_t mac[],
+ size_t ctext_len, const uint8_t nonce[],
+ const uint8_t key[])
+ {
+ return crypto_secretbox_open_detached(ptext, ctext, mac, ctext_len, nonce, key);
+ }
+
+inline int crypto_box_open_easy_afternm(uint8_t ptext[], const uint8_t ctext[],
+ size_t ctext_len, const uint8_t nonce[],
+ const uint8_t key[])
+ {
+ if(ctext_len < crypto_box_MACBYTES)
+ {
+ return -1;
+ }
+
+ return crypto_box_open_detached_afternm(ptext, ctext + crypto_box_MACBYTES,
+ ctext, ctext_len - crypto_box_MACBYTES,
+ nonce, key);
+ }
+
+inline int crypto_box_detached_afternm(uint8_t ctext[], uint8_t mac[],
+ const uint8_t ptext[], size_t ptext_len,
+ const uint8_t nonce[], const uint8_t key[])
+ {
+ return crypto_secretbox_detached(ctext, mac, ptext, ptext_len, nonce, key);
+ }
+
+inline int crypto_box_easy_afternm(uint8_t ctext[], const uint8_t ptext[],
+ size_t ptext_len, const uint8_t nonce[],
+ const uint8_t key[])
+ {
+ return crypto_box_detached_afternm(ctext + crypto_box_MACBYTES, ctext, ptext, ptext_len, nonce, key);
+ }
+
+inline size_t crypto_box_zerobytes() { return crypto_box_ZEROBYTES; }
+
+inline size_t crypto_box_boxzerobytes() { return crypto_box_BOXZEROBYTES; }
+
+inline int crypto_box(uint8_t ctext[], const uint8_t ptext[],
+ size_t ptext_len, const uint8_t nonce[],
+ const uint8_t pk[32], const uint8_t sk[32])
+ {
+ return crypto_box_curve25519xsalsa20poly1305(ctext, ptext, ptext_len, nonce, pk, sk);
+ }
+
+inline int crypto_box_open(uint8_t ptext[], const uint8_t ctext[],
+ size_t ctext_len, const uint8_t nonce[],
+ const uint8_t pk[32], const uint8_t sk[32])
+ {
+ return crypto_box_curve25519xsalsa20poly1305_open(ptext, ctext, ctext_len, nonce, pk, sk);
+ }
+
+// sodium/crypto_hash_sha512.h
+
+inline size_t crypto_hash_sha512_bytes() { return crypto_hash_sha512_BYTES; }
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_hash_sha512(uint8_t out[64], const uint8_t in[], size_t in_len);
+
+// sodium/crypto_auth_hmacsha512.h
+
+inline size_t crypto_auth_hmacsha512_bytes() { return crypto_auth_hmacsha512_BYTES; }
+
+inline size_t crypto_auth_hmacsha512_keybytes() { return crypto_auth_hmacsha512_KEYBYTES; }
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_auth_hmacsha512(uint8_t out[],
+ const uint8_t in[],
+ size_t in_len,
+ const uint8_t key[]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_auth_hmacsha512_verify(const uint8_t h[],
+ const uint8_t in[],
+ size_t in_len,
+ const uint8_t key[]);
+
+inline void crypto_auth_hmacsha512_keygen(uint8_t k[32])
+ {
+ return randombytes_buf(k, 32);
+ }
+
+// sodium/crypto_auth_hmacsha512256.h
+
+inline size_t crypto_auth_hmacsha512256_bytes()
+ {
+ return crypto_auth_hmacsha512256_BYTES;
+ }
+
+inline size_t crypto_auth_hmacsha512256_keybytes()
+ {
+ return crypto_auth_hmacsha512256_KEYBYTES;
+ }
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_auth_hmacsha512256(uint8_t out[],
+ const uint8_t in[],
+ size_t in_len,
+ const uint8_t key[]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_auth_hmacsha512256_verify(const uint8_t h[],
+ const uint8_t in[],
+ size_t in_len,
+ const uint8_t key[]);
+
+inline void crypto_auth_hmacsha512256_keygen(uint8_t k[32])
+ {
+ return randombytes_buf(k, 32);
+ }
+
+// sodium/crypto_auth.h
+
+inline size_t crypto_auth_bytes() { return crypto_auth_BYTES; }
+
+inline size_t crypto_auth_keybytes() { return crypto_auth_KEYBYTES; }
+
+inline const char* crypto_auth_primitive() { return "hmacsha512256"; }
+
+inline int crypto_auth(uint8_t out[], const uint8_t in[],
+ size_t in_len, const uint8_t key[])
+ {
+ return crypto_auth_hmacsha512256(out, in, in_len, key);
+ }
+
+inline int crypto_auth_verify(const uint8_t mac[], const uint8_t in[],
+ size_t in_len, const uint8_t key[])
+ {
+ return crypto_auth_hmacsha512256_verify(mac, in, in_len, key);
+ }
+
+inline void crypto_auth_keygen(uint8_t k[])
+ {
+ return randombytes_buf(k, crypto_auth_KEYBYTES);
+ }
+
+// sodium/crypto_hash_sha256.h
+
+inline size_t crypto_hash_sha256_bytes()
+ {
+ return crypto_hash_sha256_BYTES;
+ }
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_hash_sha256(uint8_t out[], const uint8_t in[], size_t in_len);
+
+// sodium/crypto_auth_hmacsha256.h
+
+inline size_t crypto_auth_hmacsha256_bytes()
+ {
+ return crypto_auth_hmacsha256_BYTES;
+ }
+
+inline size_t crypto_auth_hmacsha256_keybytes()
+ {
+ return crypto_auth_hmacsha256_KEYBYTES;
+ }
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_auth_hmacsha256(uint8_t out[],
+ const uint8_t in[],
+ size_t in_len,
+ const uint8_t key[]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_auth_hmacsha256_verify(const uint8_t h[],
+ const uint8_t in[],
+ size_t in_len,
+ const uint8_t key[]);
+
+inline void crypto_auth_hmacsha256_keygen(uint8_t k[32])
+ {
+ return randombytes_buf(k, 32);
+ }
+
+// sodium/crypto_stream_xsalsa20.h
+
+inline size_t crypto_stream_xsalsa20_keybytes()
+ {
+ return crypto_stream_xsalsa20_KEYBYTES;
+ }
+
+inline size_t crypto_stream_xsalsa20_noncebytes()
+ {
+ return crypto_stream_xsalsa20_NONCEBYTES;
+ }
+
+inline size_t crypto_stream_xsalsa20_messagebytes_max()
+ {
+ return crypto_stream_xsalsa20_MESSAGEBYTES_MAX;
+ }
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_stream_xsalsa20(uint8_t out[], size_t ctext_len,
+ const uint8_t nonce[], const uint8_t key[]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_stream_xsalsa20_xor(uint8_t out[], const uint8_t ptext[],
+ size_t ptext_len, const uint8_t nonce[],
+ const uint8_t key[]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_stream_xsalsa20_xor_ic(uint8_t out[], const uint8_t ptext[],
+ size_t ptext_len,
+ const uint8_t nonce[], uint64_t ic,
+ const uint8_t key[]);
+
+inline void crypto_stream_xsalsa20_keygen(uint8_t k[32])
+ {
+ return randombytes_buf(k, 32);
+ }
+
+// sodium/crypto_core_hsalsa20.h
+
+inline size_t crypto_core_hsalsa20_outputbytes()
+ {
+ return crypto_core_hsalsa20_OUTPUTBYTES;
+ }
+
+inline size_t crypto_core_hsalsa20_inputbytes()
+ {
+ return crypto_core_hsalsa20_INPUTBYTES;
+ }
+
+inline size_t crypto_core_hsalsa20_keybytes()
+ {
+ return crypto_core_hsalsa20_KEYBYTES;
+ }
+
+inline size_t crypto_core_hsalsa20_constbytes()
+ {
+ return crypto_core_hsalsa20_CONSTBYTES;
+ }
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_core_hsalsa20(uint8_t out[], const uint8_t in[],
+ const uint8_t key[], const uint8_t c[]);
+
+// sodium/crypto_hash.h
+
+inline size_t crypto_hash_bytes()
+ {
+ return crypto_hash_BYTES;
+ }
+
+inline int crypto_hash(uint8_t out[], const uint8_t in[], size_t in_len)
+ {
+ return crypto_hash_sha512(out, in, in_len);
+ }
+
+inline const char* crypto_hash_primitive() { return "sha512"; }
+
+// sodium/crypto_onetimeauth_poly1305.h
+
+inline size_t crypto_onetimeauth_poly1305_bytes()
+ {
+ return crypto_onetimeauth_poly1305_BYTES;
+ }
+
+inline size_t crypto_onetimeauth_poly1305_keybytes()
+ {
+ return crypto_onetimeauth_poly1305_KEYBYTES;
+ }
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_onetimeauth_poly1305(uint8_t out[],
+ const uint8_t in[],
+ size_t in_len,
+ const uint8_t key[]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_onetimeauth_poly1305_verify(const uint8_t h[],
+ const uint8_t in[],
+ size_t in_len,
+ const uint8_t key[]);
+
+inline void crypto_onetimeauth_poly1305_keygen(uint8_t k[32])
+ {
+ return randombytes_buf(k, 32);
+ }
+
+// sodium/crypto_onetimeauth.h
+
+inline size_t crypto_onetimeauth_bytes() { return crypto_onetimeauth_BYTES; }
+
+inline size_t crypto_onetimeauth_keybytes() { return crypto_onetimeauth_KEYBYTES; }
+
+inline const char* crypto_onetimeauth_primitive() { return "poly1305"; }
+
+inline int crypto_onetimeauth(uint8_t out[], const uint8_t in[],
+ size_t in_len, const uint8_t key[])
+ {
+ return crypto_onetimeauth_poly1305(out, in, in_len, key);
+ }
+
+inline int crypto_onetimeauth_verify(const uint8_t h[], const uint8_t in[],
+ size_t in_len, const uint8_t key[])
+ {
+ return crypto_onetimeauth_poly1305_verify(h, in, in_len, key);
+ }
+
+inline void crypto_onetimeauth_keygen(uint8_t k[32])
+ {
+ return crypto_onetimeauth_poly1305_keygen(k);
+ }
+
+// sodium/crypto_scalarmult_curve25519.h
+
+inline size_t crypto_scalarmult_curve25519_bytes()
+ {
+ return crypto_scalarmult_curve25519_BYTES;
+ }
+
+inline size_t crypto_scalarmult_curve25519_scalarbytes()
+ {
+ return crypto_scalarmult_curve25519_SCALARBYTES;
+ }
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_scalarmult_curve25519(uint8_t out[32], const uint8_t scalar[32], const uint8_t basepoint[32]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_scalarmult_curve25519_base(uint8_t out[32], const uint8_t scalar[32]);
+
+// sodium/crypto_scalarmult.h
+
+inline size_t crypto_scalarmult_bytes() { return crypto_scalarmult_curve25519_bytes(); }
+
+inline size_t crypto_scalarmult_scalarbytes() { return crypto_scalarmult_curve25519_scalarbytes(); }
+
+inline const char* crypto_scalarmult_primitive() { return "curve25519"; }
+
+inline int crypto_scalarmult_base(uint8_t out[], const uint8_t scalar[])
+ {
+ return crypto_scalarmult_curve25519_base(out, scalar);
+ }
+
+inline int crypto_scalarmult(uint8_t out[], const uint8_t scalar[], const uint8_t base[])
+ {
+ return crypto_scalarmult_curve25519(out, scalar, base);
+ }
+
+// sodium/crypto_stream_chacha20.h
+
+inline size_t crypto_stream_chacha20_keybytes()
+ {
+ return crypto_stream_chacha20_KEYBYTES;
+ }
+
+inline size_t crypto_stream_chacha20_noncebytes()
+ {
+ return crypto_stream_chacha20_NONCEBYTES;
+ }
+
+inline size_t crypto_stream_chacha20_messagebytes_max()
+ {
+ return crypto_stream_chacha20_MESSAGEBYTES_MAX;
+ }
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_stream_chacha20(uint8_t out[], size_t ctext_len,
+ const uint8_t nonce[], const uint8_t key[]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_stream_chacha20_xor(uint8_t out[], const uint8_t ptext[],
+ size_t ptext_len, const uint8_t nonce[],
+ const uint8_t key[]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_stream_chacha20_xor_ic(uint8_t out[], const uint8_t ptext[],
+ size_t ptext_len,
+ const uint8_t nonce[], uint64_t ic,
+ const uint8_t key[]);
+
+inline void crypto_stream_chacha20_keygen(uint8_t k[32])
+ {
+ return randombytes_buf(k, 32);
+ }
+
+inline size_t crypto_stream_chacha20_ietf_keybytes()
+ {
+ return crypto_stream_chacha20_ietf_KEYBYTES;
+ }
+
+inline size_t crypto_stream_chacha20_ietf_noncebytes()
+ {
+ return crypto_stream_chacha20_ietf_NONCEBYTES;
+ }
+
+inline size_t crypto_stream_chacha20_ietf_messagebytes_max()
+ {
+ return crypto_stream_chacha20_ietf_MESSAGEBYTES_MAX;
+ }
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_stream_chacha20_ietf(uint8_t out[], size_t ctext_len,
+ const uint8_t nonce[], const uint8_t key[]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_stream_chacha20_ietf_xor(uint8_t out[], const uint8_t ptext[],
+ size_t ptext_len, const uint8_t nonce[],
+ const uint8_t key[]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_stream_chacha20_ietf_xor_ic(uint8_t out[], const uint8_t ptext[],
+ size_t ptext_len,
+ const uint8_t nonce[], uint32_t ic,
+ const uint8_t key[]);
+
+inline void crypto_stream_chacha20_ietf_keygen(uint8_t k[32])
+ {
+ return randombytes_buf(k, 32);
+ }
+
+// sodium/crypto_stream_xchacha20.h
+
+inline size_t crypto_stream_xchacha20_keybytes()
+ {
+ return crypto_stream_xchacha20_KEYBYTES;
+ }
+
+inline size_t crypto_stream_xchacha20_noncebytes()
+ {
+ return crypto_stream_xchacha20_NONCEBYTES;
+ }
+
+inline size_t crypto_stream_xchacha20_messagebytes_max()
+ {
+ return crypto_stream_xchacha20_MESSAGEBYTES_MAX;
+ }
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_stream_xchacha20(uint8_t out[], size_t ctext_len,
+ const uint8_t nonce[], const uint8_t key[]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_stream_xchacha20_xor(uint8_t out[], const uint8_t ptext[],
+ size_t ptext_len, const uint8_t nonce[],
+ const uint8_t key[]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_stream_xchacha20_xor_ic(uint8_t out[], const uint8_t ptext[],
+ size_t ptext_len,
+ const uint8_t nonce[], uint64_t ic,
+ const uint8_t key[]);
+
+inline void crypto_stream_xchacha20_keygen(uint8_t k[32])
+ {
+ return randombytes_buf(k, crypto_stream_xchacha20_KEYBYTES);
+ }
+
+// sodium/crypto_stream_salsa20.h
+
+inline size_t crypto_stream_salsa20_keybytes()
+ {
+ return crypto_stream_xsalsa20_KEYBYTES;
+ }
+
+inline size_t crypto_stream_salsa20_noncebytes()
+ {
+ return crypto_stream_salsa20_NONCEBYTES;
+ }
+
+inline size_t crypto_stream_salsa20_messagebytes_max()
+ {
+ return crypto_stream_salsa20_MESSAGEBYTES_MAX;
+ }
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_stream_salsa20(uint8_t out[], size_t ctext_len,
+ const uint8_t nonce[], const uint8_t key[]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_stream_salsa20_xor(uint8_t out[], const uint8_t ptext[],
+ size_t ptext_len, const uint8_t nonce[],
+ const uint8_t key[]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_stream_salsa20_xor_ic(uint8_t out[], const uint8_t ptext[],
+ size_t ptext_len,
+ const uint8_t nonce[], uint64_t ic,
+ const uint8_t key[]);
+
+inline void crypto_stream_salsa20_keygen(uint8_t k[32])
+ {
+ return randombytes_buf(k, 32);
+ }
+
+// sodium/crypto_stream.h
+
+inline size_t crypto_stream_keybytes() { return crypto_stream_xsalsa20_keybytes(); }
+
+inline size_t crypto_stream_noncebytes() { return crypto_stream_xsalsa20_noncebytes(); }
+
+inline size_t crypto_stream_messagebytes_max() { return crypto_stream_MESSAGEBYTES_MAX; }
+
+inline const char* crypto_stream_primitive() { return "xsalsa20"; }
+
+inline int crypto_stream(uint8_t out[], size_t out_len,
+ const uint8_t nonce[24], const uint8_t key[32])
+ {
+ return crypto_stream_xsalsa20(out, out_len, nonce, key);
+ }
+
+inline int crypto_stream_xor(uint8_t out[], const uint8_t in[], size_t in_len,
+ const uint8_t nonce[24], const uint8_t key[32])
+ {
+ return crypto_stream_xsalsa20_xor(out, in, in_len, nonce, key);
+ }
+
+inline void crypto_stream_keygen(uint8_t key[32])
+ {
+ return randombytes_buf(key, 32);
+ }
+
+// sodium/crypto_shorthash_siphash24.h
+
+inline size_t crypto_shorthash_siphash24_bytes() { return crypto_shorthash_siphash24_BYTES; }
+
+inline size_t crypto_shorthash_siphash24_keybytes() { return crypto_shorthash_siphash24_KEYBYTES; }
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_shorthash_siphash24(uint8_t out[8], const uint8_t in[], size_t in_len, const uint8_t key[16]);
+
+// sodium/crypto_shorthash.h
+
+inline size_t crypto_shorthash_bytes() { return crypto_shorthash_siphash24_bytes(); }
+
+inline size_t crypto_shorthash_keybytes() { return crypto_shorthash_siphash24_keybytes(); }
+
+inline const char* crypto_shorthash_primitive() { return "siphash24"; }
+
+inline int crypto_shorthash(uint8_t out[], const uint8_t in[],
+ size_t in_len, const uint8_t k[16])
+ {
+ return crypto_shorthash_siphash24(out, in, in_len, k);
+ }
+
+inline void crypto_shorthash_keygen(uint8_t k[16])
+ {
+ randombytes_buf(k, crypto_shorthash_siphash24_KEYBYTES);
+ }
+
+// sodium/crypto_sign_ed25519.h
+
+inline size_t crypto_sign_ed25519_bytes()
+ {
+ return crypto_sign_ed25519_BYTES;
+ }
+
+inline size_t crypto_sign_ed25519_seedbytes()
+ {
+ return crypto_sign_ed25519_SEEDBYTES;
+ }
+
+inline size_t crypto_sign_ed25519_publickeybytes()
+ {
+ return crypto_sign_ed25519_PUBLICKEYBYTES;
+ }
+
+inline size_t crypto_sign_ed25519_secretkeybytes()
+ {
+ return crypto_sign_ed25519_SECRETKEYBYTES;
+ }
+
+inline size_t crypto_sign_ed25519_messagebytes_max()
+ {
+ return crypto_sign_ed25519_MESSAGEBYTES_MAX;
+ }
+
+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]);
+
+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]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_sign_ed25519_keypair(uint8_t pk[32], uint8_t sk[64]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_sign_ed25519_seed_keypair(uint8_t pk[], uint8_t sk[],
+ const uint8_t seed[]);
+
+// sodium/crypto_sign.h
+
+inline size_t crypto_sign_bytes()
+ {
+ return crypto_sign_BYTES;
+ }
+
+inline size_t crypto_sign_seedbytes()
+ {
+ return crypto_sign_SEEDBYTES;
+ }
+
+inline size_t crypto_sign_publickeybytes()
+ {
+ return crypto_sign_PUBLICKEYBYTES;
+ }
+
+inline size_t crypto_sign_secretkeybytes()
+ {
+ return crypto_sign_SECRETKEYBYTES;
+ }
+
+inline size_t crypto_sign_messagebytes_max()
+ {
+ return crypto_sign_MESSAGEBYTES_MAX;
+ }
+
+inline const char* crypto_sign_primitive()
+ {
+ return "ed25519";
+ }
+
+inline int crypto_sign_seed_keypair(uint8_t pk[32], uint8_t sk[32],
+ 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])
+ {
+ 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])
+ {
+ return crypto_sign_ed25519_detached(sig, sig_len, msg, msg_len, sk);
+ }
+
+inline int crypto_sign_verify_detached(const uint8_t sig[],
+ const uint8_t in[],
+ size_t in_len,
+ const uint8_t pk[32])
+ {
+ return crypto_sign_ed25519_verify_detached(sig, in, in_len, pk);
+ }
+
+}
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/compat/sodium/sodium_25519.cpp b/comm/third_party/botan/src/lib/compat/sodium/sodium_25519.cpp
new file mode 100644
index 0000000000..e1e44b9ad4
--- /dev/null
+++ b/comm/third_party/botan/src/lib/compat/sodium/sodium_25519.cpp
@@ -0,0 +1,60 @@
+/*
+* (C) 2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/sodium.h>
+#include <botan/ed25519.h>
+#include <botan/curve25519.h>
+
+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;
+ }
+
+int Sodium::crypto_scalarmult_curve25519_base(uint8_t out[32], const uint8_t scalar[32])
+ {
+ curve25519_basepoint(out, scalar);
+ return 0;
+ }
+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])
+ {
+ ed25519_sign(sig, msg, msg_len, sk, nullptr, 0);
+
+ if(sig_len)
+ *sig_len = 64;
+ return 0;
+ }
+
+int Sodium::crypto_sign_ed25519_verify_detached(const uint8_t sig[],
+ const uint8_t msg[],
+ size_t msg_len,
+ const uint8_t pk[32])
+ {
+ const bool ok = ed25519_verify(msg, msg_len, sig, pk, nullptr, 0);
+ return ok ? 0 : -1;
+ }
+
+int Sodium::crypto_sign_ed25519_keypair(uint8_t pk[32], uint8_t sk[64])
+ {
+ secure_vector<uint8_t> seed(32);
+ randombytes_buf(seed.data(), seed.size());
+ return crypto_sign_ed25519_seed_keypair(pk, sk, seed.data());
+ }
+
+int Sodium::crypto_sign_ed25519_seed_keypair(uint8_t pk[], uint8_t sk[],
+ const uint8_t seed[])
+ {
+ ed25519_gen_keypair(pk, sk, seed);
+ return 0;
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/compat/sodium/sodium_aead.cpp b/comm/third_party/botan/src/lib/compat/sodium/sodium_aead.cpp
new file mode 100644
index 0000000000..0d6f35eaf7
--- /dev/null
+++ b/comm/third_party/botan/src/lib/compat/sodium/sodium_aead.cpp
@@ -0,0 +1,359 @@
+/*
+* (C) 2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/sodium.h>
+#include <botan/aead.h>
+
+namespace Botan {
+
+namespace {
+
+int sodium_aead_chacha20poly1305_encrypt(uint8_t ctext[],
+ unsigned long long* ctext_len,
+ const uint8_t ptext[],
+ size_t ptext_len,
+ const uint8_t ad[],
+ size_t ad_len,
+ const uint8_t nonce[],
+ size_t nonce_len,
+ const uint8_t key[])
+ {
+ auto chacha20poly1305 = AEAD_Mode::create_or_throw("ChaCha20Poly1305", ENCRYPTION);
+
+ chacha20poly1305->set_key(key, 32);
+ chacha20poly1305->set_associated_data(ad, ad_len);
+ chacha20poly1305->start(nonce, nonce_len);
+
+ // FIXME do this in-place
+ secure_vector<uint8_t> buf;
+ buf.reserve(ptext_len + 16);
+ buf.assign(ptext, ptext + ptext_len);
+
+ chacha20poly1305->finish(buf);
+
+ copy_mem(ctext, buf.data(), buf.size());
+ if(ctext_len)
+ *ctext_len = buf.size();
+ return 0;
+ }
+
+int sodium_aead_chacha20poly1305_decrypt(uint8_t ptext[],
+ unsigned long long* ptext_len,
+ const uint8_t ctext[],
+ size_t ctext_len,
+ const uint8_t ad[],
+ size_t ad_len,
+ const uint8_t nonce[],
+ size_t nonce_len,
+ const uint8_t key[])
+ {
+ if(ctext_len < 16)
+ return -1;
+
+ *ptext_len = 0;
+
+ auto chacha20poly1305 = AEAD_Mode::create_or_throw("ChaCha20Poly1305", DECRYPTION);
+
+ chacha20poly1305->set_key(key, 32);
+ chacha20poly1305->set_associated_data(ad, ad_len);
+ chacha20poly1305->start(nonce, nonce_len);
+
+ // FIXME do this in-place
+ secure_vector<uint8_t> buf;
+ buf.assign(ctext, ctext + ctext_len);
+
+ try
+ {
+ chacha20poly1305->finish(buf);
+ }
+ catch(Invalid_Authentication_Tag&)
+ {
+ return -1;
+ }
+
+ *ptext_len = ctext_len - 16;
+
+ copy_mem(ptext, buf.data(), buf.size());
+ return 0;
+ }
+
+int sodium_aead_chacha20poly1305_encrypt_detached(uint8_t ctext[],
+ uint8_t mac[],
+ const uint8_t ptext[],
+ size_t ptext_len,
+ const uint8_t ad[],
+ size_t ad_len,
+ const uint8_t nonce[],
+ size_t nonce_len,
+ const uint8_t key[])
+ {
+ auto chacha20poly1305 = AEAD_Mode::create_or_throw("ChaCha20Poly1305", ENCRYPTION);
+
+ chacha20poly1305->set_key(key, 32);
+ chacha20poly1305->set_associated_data(ad, ad_len);
+ chacha20poly1305->start(nonce, nonce_len);
+
+ // FIXME do this in-place
+ secure_vector<uint8_t> buf;
+ buf.reserve(ptext_len + 16);
+ buf.assign(ptext, ptext + ptext_len);
+
+ chacha20poly1305->finish(buf);
+
+ copy_mem(ctext, buf.data(), ptext_len);
+ copy_mem(mac, buf.data() + ptext_len, 16);
+ return 0;
+ }
+
+int sodium_aead_chacha20poly1305_decrypt_detached(uint8_t ptext[],
+ const uint8_t ctext[],
+ size_t ctext_len,
+ const uint8_t mac[],
+ const uint8_t ad[],
+ size_t ad_len,
+ const uint8_t nonce[],
+ size_t nonce_len,
+ const uint8_t key[])
+ {
+ auto chacha20poly1305 = AEAD_Mode::create_or_throw("ChaCha20Poly1305", DECRYPTION);
+
+ chacha20poly1305->set_key(key, 32);
+ chacha20poly1305->set_associated_data(ad, ad_len);
+ chacha20poly1305->start(nonce, nonce_len);
+
+ // FIXME do this in-place
+ secure_vector<uint8_t> buf;
+ buf.reserve(ctext_len + 16);
+ buf.assign(ctext, ctext + ctext_len);
+ buf.insert(buf.end(), mac, mac + 16);
+
+ try
+ {
+ chacha20poly1305->finish(buf);
+ }
+ catch(Invalid_Authentication_Tag&)
+ {
+ return -1;
+ }
+
+ copy_mem(ptext, buf.data(), buf.size());
+ return 0;
+ }
+
+}
+
+int Sodium::crypto_aead_chacha20poly1305_ietf_encrypt(uint8_t ctext[],
+ unsigned long long* ctext_len,
+ const uint8_t ptext[],
+ size_t ptext_len,
+ const uint8_t ad[],
+ size_t ad_len,
+ const uint8_t unused_secret_nonce[],
+ const uint8_t nonce[],
+ const uint8_t key[])
+ {
+ BOTAN_UNUSED(unused_secret_nonce);
+
+ return sodium_aead_chacha20poly1305_encrypt(
+ ctext, ctext_len, ptext, ptext_len,
+ ad, ad_len, nonce, crypto_aead_chacha20poly1305_ietf_npubbytes(), key);
+ }
+
+int Sodium::crypto_aead_chacha20poly1305_ietf_decrypt(uint8_t ptext[],
+ unsigned long long* ptext_len,
+ uint8_t unused_secret_nonce[],
+ const uint8_t ctext[],
+ size_t ctext_len,
+ const uint8_t ad[],
+ size_t ad_len,
+ const uint8_t nonce[],
+ const uint8_t key[])
+ {
+ BOTAN_UNUSED(unused_secret_nonce);
+
+ return sodium_aead_chacha20poly1305_decrypt(
+ ptext, ptext_len, ctext, ctext_len,
+ ad, ad_len, nonce, crypto_aead_chacha20poly1305_ietf_npubbytes(), key);
+ }
+
+int Sodium::crypto_aead_chacha20poly1305_ietf_encrypt_detached(uint8_t ctext[],
+ uint8_t mac[],
+ unsigned long long* mac_len,
+ const uint8_t ptext[],
+ size_t ptext_len,
+ const uint8_t ad[],
+ size_t ad_len,
+ const uint8_t unused_secret_nonce[],
+ const uint8_t nonce[],
+ const uint8_t key[])
+ {
+ BOTAN_UNUSED(unused_secret_nonce);
+
+ if(mac_len)
+ *mac_len = 16;
+
+ return sodium_aead_chacha20poly1305_encrypt_detached(
+ ctext, mac, ptext, ptext_len,
+ ad, ad_len, nonce, crypto_aead_chacha20poly1305_ietf_npubbytes(), key);
+ }
+
+int Sodium::crypto_aead_chacha20poly1305_ietf_decrypt_detached(uint8_t ptext[],
+ uint8_t unused_secret_nonce[],
+ const uint8_t ctext[],
+ size_t ctext_len,
+ const uint8_t mac[],
+ const uint8_t ad[],
+ size_t ad_len,
+ const uint8_t nonce[],
+ const uint8_t key[])
+ {
+ BOTAN_UNUSED(unused_secret_nonce);
+
+ return sodium_aead_chacha20poly1305_decrypt_detached(
+ ptext, ctext, ctext_len, mac,
+ ad, ad_len, nonce, crypto_aead_chacha20poly1305_ietf_npubbytes(), key);
+ }
+
+int Sodium::crypto_aead_chacha20poly1305_encrypt(uint8_t ctext[],
+ unsigned long long* ctext_len,
+ const uint8_t ptext[],
+ size_t ptext_len,
+ const uint8_t ad[],
+ size_t ad_len,
+ const uint8_t unused_secret_nonce[],
+ const uint8_t nonce[],
+ const uint8_t key[])
+ {
+ BOTAN_UNUSED(unused_secret_nonce);
+ return sodium_aead_chacha20poly1305_encrypt(
+ ctext, ctext_len, ptext, ptext_len,
+ ad, ad_len, nonce, crypto_aead_chacha20poly1305_npubbytes(), key);
+ }
+
+int Sodium::crypto_aead_chacha20poly1305_decrypt(uint8_t ptext[],
+ unsigned long long* ptext_len,
+ uint8_t unused_secret_nonce[],
+ const uint8_t ctext[],
+ size_t ctext_len,
+ const uint8_t ad[],
+ size_t ad_len,
+ const uint8_t nonce[],
+ const uint8_t key[])
+ {
+ BOTAN_UNUSED(unused_secret_nonce);
+ return sodium_aead_chacha20poly1305_decrypt(
+ ptext, ptext_len, ctext, ctext_len,
+ ad, ad_len, nonce, crypto_aead_chacha20poly1305_npubbytes(), key);
+ }
+
+int Sodium::crypto_aead_chacha20poly1305_encrypt_detached(uint8_t ctext[],
+ uint8_t mac[],
+ unsigned long long* mac_len,
+ const uint8_t ptext[],
+ size_t ptext_len,
+ const uint8_t ad[],
+ size_t ad_len,
+ const uint8_t unused_secret_nonce[],
+ const uint8_t nonce[],
+ const uint8_t key[])
+ {
+ BOTAN_UNUSED(unused_secret_nonce);
+ if(mac_len)
+ *mac_len = 16;
+
+ return sodium_aead_chacha20poly1305_encrypt_detached(
+ ctext, mac, ptext, ptext_len,
+ ad, ad_len, nonce, crypto_aead_chacha20poly1305_npubbytes(), key);
+ }
+
+int Sodium::crypto_aead_chacha20poly1305_decrypt_detached(uint8_t ptext[],
+ uint8_t unused_secret_nonce[],
+ const uint8_t ctext[],
+ size_t ctext_len,
+ const uint8_t mac[],
+ const uint8_t ad[],
+ size_t ad_len,
+ const uint8_t nonce[],
+ const uint8_t key[])
+ {
+ BOTAN_UNUSED(unused_secret_nonce);
+
+ return sodium_aead_chacha20poly1305_decrypt_detached(
+ ptext, ctext, ctext_len, mac,
+ ad, ad_len, nonce, crypto_aead_chacha20poly1305_npubbytes(), key);
+ }
+
+int Sodium::crypto_aead_xchacha20poly1305_ietf_encrypt(uint8_t ctext[],
+ unsigned long long* ctext_len,
+ const uint8_t ptext[],
+ size_t ptext_len,
+ const uint8_t ad[],
+ size_t ad_len,
+ const uint8_t unused_secret_nonce[],
+ const uint8_t nonce[],
+ const uint8_t key[])
+ {
+ BOTAN_UNUSED(unused_secret_nonce);
+
+ return sodium_aead_chacha20poly1305_encrypt(
+ ctext, ctext_len, ptext, ptext_len,
+ ad, ad_len, nonce, crypto_aead_xchacha20poly1305_ietf_npubbytes(), key);
+ }
+
+int Sodium::crypto_aead_xchacha20poly1305_ietf_decrypt(uint8_t ptext[],
+ unsigned long long* ptext_len,
+ uint8_t unused_secret_nonce[],
+ const uint8_t ctext[],
+ size_t ctext_len,
+ const uint8_t ad[],
+ size_t ad_len,
+ const uint8_t nonce[],
+ const uint8_t key[])
+ {
+ BOTAN_UNUSED(unused_secret_nonce);
+
+ return sodium_aead_chacha20poly1305_decrypt(
+ ptext, ptext_len, ctext, ctext_len,
+ ad, ad_len, nonce, crypto_aead_xchacha20poly1305_ietf_npubbytes(), key);
+ }
+
+int Sodium::crypto_aead_xchacha20poly1305_ietf_encrypt_detached(uint8_t ctext[],
+ uint8_t mac[],
+ unsigned long long* mac_len,
+ const uint8_t ptext[],
+ size_t ptext_len,
+ const uint8_t ad[],
+ size_t ad_len,
+ const uint8_t unused_secret_nonce[],
+ const uint8_t nonce[],
+ const uint8_t key[])
+ {
+ BOTAN_UNUSED(unused_secret_nonce);
+ if(mac_len)
+ *mac_len = 16;
+
+ return sodium_aead_chacha20poly1305_encrypt_detached(
+ ctext, mac, ptext, ptext_len,
+ ad, ad_len, nonce, crypto_aead_xchacha20poly1305_ietf_npubbytes(), key);
+ }
+
+int Sodium::crypto_aead_xchacha20poly1305_ietf_decrypt_detached(uint8_t ptext[],
+ uint8_t unused_secret_nonce[],
+ const uint8_t ctext[],
+ size_t ctext_len,
+ const uint8_t mac[],
+ const uint8_t ad[],
+ size_t ad_len,
+ const uint8_t nonce[],
+ const uint8_t key[])
+ {
+ BOTAN_UNUSED(unused_secret_nonce);
+ return sodium_aead_chacha20poly1305_decrypt_detached(
+ ptext, ctext, ctext_len, mac,
+ ad, ad_len, nonce, crypto_aead_xchacha20poly1305_ietf_npubbytes(), key);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/compat/sodium/sodium_auth.cpp b/comm/third_party/botan/src/lib/compat/sodium/sodium_auth.cpp
new file mode 100644
index 0000000000..747b8af33e
--- /dev/null
+++ b/comm/third_party/botan/src/lib/compat/sodium/sodium_auth.cpp
@@ -0,0 +1,131 @@
+/*
+* (C) 2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/sodium.h>
+#include <botan/mac.h>
+#include <botan/hash.h>
+
+namespace Botan {
+
+int Sodium::crypto_hash_sha512(uint8_t out[64], const uint8_t in[], size_t in_len)
+ {
+ auto sha512 = HashFunction::create_or_throw("SHA-512");
+ sha512->update(in, in_len);
+ sha512->final(out);
+ return 0;
+ }
+
+int Sodium::crypto_hash_sha256(uint8_t out[], const uint8_t in[], size_t in_len)
+ {
+ auto sha256 = HashFunction::create_or_throw("SHA-256");
+ sha256->update(in, in_len);
+ sha256->final(out);
+ return 0;
+ }
+
+int Sodium::crypto_shorthash_siphash24(uint8_t out[8], const uint8_t in[],
+ size_t in_len, const uint8_t key[16])
+ {
+ auto mac = MessageAuthenticationCode::create_or_throw("SipHash(2,4)");
+ mac->set_key(key, crypto_shorthash_siphash24_KEYBYTES);
+ mac->update(in, in_len);
+ mac->final(out);
+ return 0;
+ }
+
+int Sodium::crypto_onetimeauth_poly1305(uint8_t out[],
+ const uint8_t in[],
+ size_t in_len,
+ const uint8_t key[])
+ {
+ auto mac = MessageAuthenticationCode::create_or_throw("Poly1305");
+ mac->set_key(key, crypto_onetimeauth_poly1305_KEYBYTES);
+ mac->update(in, in_len);
+ mac->final(out);
+ return 0;
+ }
+
+int Sodium::crypto_onetimeauth_poly1305_verify(const uint8_t mac[],
+ const uint8_t in[],
+ size_t in_len,
+ const uint8_t key[])
+ {
+ secure_vector<uint8_t> computed(crypto_onetimeauth_poly1305_BYTES);
+ crypto_onetimeauth_poly1305(computed.data(), in, in_len, key);
+ return crypto_verify_16(computed.data(), mac) ? 0 : -1;
+ }
+
+int Sodium::crypto_auth_hmacsha512(uint8_t out[],
+ const uint8_t in[],
+ size_t in_len,
+ const uint8_t key[])
+ {
+ auto mac = MessageAuthenticationCode::create_or_throw("HMAC(SHA-512)");
+ mac->set_key(key, crypto_auth_hmacsha512_KEYBYTES);
+ mac->update(in, in_len);
+ mac->final(out);
+ return 0;
+ }
+
+int Sodium::crypto_auth_hmacsha512_verify(const uint8_t mac[],
+ const uint8_t in[],
+ size_t in_len,
+ const uint8_t key[])
+ {
+ secure_vector<uint8_t> computed(crypto_auth_hmacsha512_BYTES);
+ crypto_auth_hmacsha512(computed.data(), in, in_len, key);
+ return crypto_verify_64(computed.data(), mac) ? 0 : -1;
+ }
+
+int Sodium::crypto_auth_hmacsha512256(uint8_t out[],
+ const uint8_t in[],
+ size_t in_len,
+ const uint8_t key[])
+ {
+ auto mac = MessageAuthenticationCode::create_or_throw("HMAC(SHA-512)");
+ mac->set_key(key, crypto_auth_hmacsha512256_KEYBYTES);
+ mac->update(in, in_len);
+
+ secure_vector<uint8_t> buf(64);
+ mac->final(buf);
+
+ copy_mem(out, buf.data(), crypto_auth_hmacsha512256_BYTES);
+ return 0;
+ }
+
+int Sodium::crypto_auth_hmacsha512256_verify(const uint8_t mac[],
+ const uint8_t in[],
+ size_t in_len,
+ const uint8_t key[])
+ {
+ secure_vector<uint8_t> computed(crypto_auth_hmacsha512256_BYTES);
+ crypto_auth_hmacsha512256(computed.data(), in, in_len, key);
+ return crypto_verify_32(computed.data(), mac) ? 0 : -1;
+ }
+
+int Sodium::crypto_auth_hmacsha256(uint8_t out[],
+ const uint8_t in[],
+ size_t in_len,
+ const uint8_t key[])
+ {
+ auto mac = MessageAuthenticationCode::create_or_throw("HMAC(SHA-256)");
+ mac->set_key(key, crypto_auth_hmacsha256_KEYBYTES);
+ mac->update(in, in_len);
+ mac->final(out);
+ return 0;
+ }
+
+int Sodium::crypto_auth_hmacsha256_verify(const uint8_t mac[],
+ const uint8_t in[],
+ size_t in_len,
+ const uint8_t key[])
+ {
+ secure_vector<uint8_t> computed(crypto_auth_hmacsha256_BYTES);
+ crypto_auth_hmacsha256(computed.data(), in, in_len, key);
+ return crypto_verify_32(computed.data(), mac) ? 0 : -1;
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/compat/sodium/sodium_box.cpp b/comm/third_party/botan/src/lib/compat/sodium/sodium_box.cpp
new file mode 100644
index 0000000000..52b9a03036
--- /dev/null
+++ b/comm/third_party/botan/src/lib/compat/sodium/sodium_box.cpp
@@ -0,0 +1,100 @@
+/*
+* (C) 2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/sodium.h>
+#include <botan/secmem.h>
+
+namespace Botan {
+
+int Sodium::crypto_box_curve25519xsalsa20poly1305_seed_keypair(uint8_t pk[32],
+ uint8_t sk[32],
+ const uint8_t seed[32])
+ {
+ secure_vector<uint8_t> digest(64);
+ crypto_hash_sha512(digest.data(), seed, 32);
+ copy_mem(sk, digest.data(), 32);
+ return crypto_scalarmult_curve25519_base(pk, sk);
+ }
+
+int Sodium::crypto_box_curve25519xsalsa20poly1305_keypair(uint8_t pk[32],
+ uint8_t sk[32])
+ {
+ randombytes_buf(sk, 32);
+ return crypto_scalarmult_curve25519_base(pk, sk);
+ }
+
+int Sodium::crypto_box_curve25519xsalsa20poly1305_beforenm(uint8_t key[],
+ const uint8_t pk[32],
+ const uint8_t sk[32])
+ {
+ const uint8_t zero[16] = { 0 };
+ secure_vector<uint8_t> shared(32);
+
+ if(crypto_scalarmult_curve25519(shared.data(), sk, pk) != 0)
+ return -1;
+
+ return crypto_core_hsalsa20(key, zero, shared.data(), nullptr);
+ }
+
+int Sodium::crypto_box_curve25519xsalsa20poly1305(uint8_t ctext[],
+ const uint8_t ptext[],
+ size_t ptext_len,
+ const uint8_t nonce[],
+ const uint8_t pk[32],
+ const uint8_t sk[32])
+ {
+ secure_vector<uint8_t> shared(32);
+
+ if(crypto_box_curve25519xsalsa20poly1305_beforenm(shared.data(), pk, sk) != 0)
+ return -1;
+
+ return crypto_box_curve25519xsalsa20poly1305_afternm(ctext, ptext, ptext_len, nonce, shared.data());
+ }
+
+int Sodium::crypto_box_curve25519xsalsa20poly1305_open(uint8_t ptext[],
+ const uint8_t ctext[],
+ size_t ctext_len,
+ const uint8_t nonce[],
+ const uint8_t pk[32],
+ const uint8_t sk[32])
+ {
+ secure_vector<uint8_t> shared(32);
+
+ if(crypto_box_curve25519xsalsa20poly1305_beforenm(shared.data(), pk, sk) != 0)
+ return -1;
+
+ return crypto_box_curve25519xsalsa20poly1305_open_afternm(ptext, ctext, ctext_len, nonce, shared.data());
+ }
+
+int Sodium::crypto_box_detached(uint8_t ctext[], uint8_t mac[],
+ const uint8_t ptext[], size_t ptext_len,
+ const uint8_t nonce[], const uint8_t pk[32],
+ const uint8_t sk[32])
+ {
+ secure_vector<uint8_t> shared(32);
+
+ if(crypto_box_beforenm(shared.data(), pk, sk) != 0)
+ return -1;
+
+ return crypto_box_detached_afternm(ctext, mac, ptext, ptext_len, nonce, shared.data());
+ }
+
+int Sodium::crypto_box_open_detached(uint8_t ptext[], const uint8_t ctext[],
+ const uint8_t mac[],
+ size_t ctext_len,
+ const uint8_t nonce[],
+ const uint8_t pk[32],
+ const uint8_t sk[32])
+ {
+ secure_vector<uint8_t> shared(32);
+
+ if(crypto_box_beforenm(shared.data(), pk, sk) != 0)
+ return -1;
+
+ return crypto_box_open_detached_afternm(ptext, ctext, mac, ctext_len, nonce, shared.data());
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/compat/sodium/sodium_chacha.cpp b/comm/third_party/botan/src/lib/compat/sodium/sodium_chacha.cpp
new file mode 100644
index 0000000000..fed7a52f6f
--- /dev/null
+++ b/comm/third_party/botan/src/lib/compat/sodium/sodium_chacha.cpp
@@ -0,0 +1,109 @@
+/*
+* (C) 2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/sodium.h>
+#include <botan/stream_cipher.h>
+
+namespace Botan {
+
+int Sodium::crypto_stream_chacha20(uint8_t out[], size_t out_len,
+ const uint8_t nonce[], const uint8_t key[])
+ {
+ auto chacha = StreamCipher::create_or_throw("ChaCha(20)");
+ chacha->set_key(key, crypto_stream_chacha20_KEYBYTES);
+ chacha->set_iv(nonce, crypto_stream_chacha20_NONCEBYTES);
+ chacha->write_keystream(out, out_len);
+ return 0;
+ }
+
+int Sodium::crypto_stream_chacha20_xor(uint8_t out[], const uint8_t in[],
+ size_t in_len, const uint8_t nonce[],
+ const uint8_t key[])
+ {
+ return crypto_stream_chacha20_xor_ic(out, in, in_len, nonce, 0, key);
+ }
+
+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
+ return -1;
+
+ auto chacha = StreamCipher::create_or_throw("ChaCha(20)");
+ chacha->set_key(key, crypto_stream_chacha20_KEYBYTES);
+ chacha->set_iv(nonce, crypto_stream_chacha20_NONCEBYTES);
+ chacha->seek(ic * 64);
+ chacha->cipher(in, out, in_len);
+ return 0;
+ }
+
+int Sodium::crypto_stream_chacha20_ietf(uint8_t out[], size_t out_len,
+ const uint8_t nonce[], const uint8_t key[])
+ {
+ auto chacha = StreamCipher::create_or_throw("ChaCha(20)");
+ chacha->set_key(key, crypto_stream_chacha20_ietf_KEYBYTES);
+ chacha->set_iv(nonce, crypto_stream_chacha20_ietf_NONCEBYTES);
+ chacha->write_keystream(out, out_len);
+ return 0;
+ }
+
+int Sodium::crypto_stream_chacha20_ietf_xor(uint8_t out[],
+ const uint8_t in[], size_t in_len,
+ const uint8_t nonce[],
+ const uint8_t key[])
+ {
+ return crypto_stream_chacha20_ietf_xor_ic(out, in, in_len, nonce, 0, key);
+ }
+
+int Sodium::crypto_stream_chacha20_ietf_xor_ic(uint8_t out[],
+ const uint8_t in[], size_t in_len,
+ const uint8_t nonce[], uint32_t ic,
+ const uint8_t key[])
+ {
+ auto chacha = StreamCipher::create_or_throw("ChaCha(20)");
+ chacha->set_key(key, crypto_stream_chacha20_ietf_KEYBYTES);
+ chacha->set_iv(nonce, crypto_stream_chacha20_ietf_NONCEBYTES);
+ chacha->seek(static_cast<uint64_t>(ic) * 64);
+ chacha->cipher(in, out, in_len);
+ return 0;
+ }
+
+int Sodium::crypto_stream_xchacha20(uint8_t out[], size_t out_len,
+ const uint8_t nonce[], const uint8_t key[])
+ {
+ auto chacha = StreamCipher::create_or_throw("ChaCha(20)");
+ chacha->set_key(key, crypto_stream_xchacha20_KEYBYTES);
+ chacha->set_iv(nonce, crypto_stream_xchacha20_NONCEBYTES);
+ chacha->write_keystream(out, out_len);
+ return 0;
+ }
+
+int Sodium::crypto_stream_xchacha20_xor(uint8_t out[], const uint8_t in[],
+ size_t in_len, const uint8_t nonce[],
+ const uint8_t key[])
+ {
+ return crypto_stream_xchacha20_xor_ic(out, in, in_len, nonce, 0, key);
+ }
+
+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
+ return -1;
+
+ auto chacha = StreamCipher::create_or_throw("ChaCha(20)");
+ chacha->set_key(key, crypto_stream_xchacha20_KEYBYTES);
+ chacha->set_iv(nonce, crypto_stream_xchacha20_NONCEBYTES);
+ chacha->seek(ic * 64);
+ chacha->cipher(in, out, in_len);
+ return 0;
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/compat/sodium/sodium_salsa.cpp b/comm/third_party/botan/src/lib/compat/sodium/sodium_salsa.cpp
new file mode 100644
index 0000000000..c1465a9bb5
--- /dev/null
+++ b/comm/third_party/botan/src/lib/compat/sodium/sodium_salsa.cpp
@@ -0,0 +1,124 @@
+/*
+* (C) 2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/sodium.h>
+#include <botan/salsa20.h>
+#include <botan/loadstor.h>
+
+namespace Botan {
+
+int Sodium::crypto_core_hsalsa20(uint8_t out[], const uint8_t in[],
+ const uint8_t key[], const uint8_t c[])
+ {
+ uint32_t in32[16] = { 0 };
+
+ static const uint32_t SIGMA[] =
+ { 0x61707865, 0x3320646e, 0x79622d32, 0x6b206574 };
+
+ if(c == nullptr)
+ {
+ in32[0] = SIGMA[0];
+ in32[5] = SIGMA[1];
+ in32[10] = SIGMA[2];
+ in32[15] = SIGMA[3];
+ }
+ else
+ {
+ in32[0] = load_le<uint32_t>(c, 0);
+ in32[5] = load_le<uint32_t>(c, 1);
+ in32[10] = load_le<uint32_t>(c, 2);
+ in32[15] = load_le<uint32_t>(c, 3);
+ }
+
+ in32[1] = load_le<uint32_t>(key, 0);
+ in32[2] = load_le<uint32_t>(key, 1);
+ in32[3] = load_le<uint32_t>(key, 2);
+ in32[4] = load_le<uint32_t>(key, 3);
+
+ in32[6] = load_le<uint32_t>(in, 0);
+ in32[7] = load_le<uint32_t>(in, 1);
+ in32[8] = load_le<uint32_t>(in, 2);
+ in32[9] = load_le<uint32_t>(in, 3);
+
+ in32[11] = load_le<uint32_t>(key, 4);
+ in32[12] = load_le<uint32_t>(key, 5);
+ in32[13] = load_le<uint32_t>(key, 6);
+ in32[14] = load_le<uint32_t>(key, 7);
+
+ uint32_t out32[8] = { 0 };
+ Salsa20::hsalsa20(out32, in32);
+
+ copy_out_le(out, 32, out32);
+ return 0;
+ }
+
+int Sodium::crypto_stream_salsa20(uint8_t out[], size_t out_len,
+ const uint8_t nonce[], const uint8_t key[])
+ {
+ Salsa20 salsa;
+ salsa.set_key(key, crypto_stream_salsa20_KEYBYTES);
+ salsa.set_iv(nonce, crypto_stream_salsa20_NONCEBYTES);
+ salsa.write_keystream(out, out_len);
+ return 0;
+ }
+
+int Sodium::crypto_stream_salsa20_xor(uint8_t out[], const uint8_t in[],
+ size_t in_len, const uint8_t nonce[],
+ const uint8_t key[])
+ {
+ return crypto_stream_salsa20_xor_ic(out, in, in_len, nonce, 0, key);
+ }
+
+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
+ return -1;
+
+ Salsa20 salsa;
+ salsa.set_key(key, crypto_stream_salsa20_KEYBYTES);
+ salsa.set_iv(nonce, crypto_stream_salsa20_NONCEBYTES);
+ salsa.seek(ic * 64);
+ salsa.cipher(in, out, in_len);
+ return 0;
+ }
+
+int Sodium::crypto_stream_xsalsa20(uint8_t out[], size_t out_len,
+ const uint8_t nonce[], const uint8_t key[])
+ {
+ Salsa20 salsa;
+ salsa.set_key(key, crypto_stream_xsalsa20_KEYBYTES);
+ salsa.set_iv(nonce, crypto_stream_xsalsa20_NONCEBYTES);
+ salsa.write_keystream(out, out_len);
+ return 0;
+ }
+
+int Sodium::crypto_stream_xsalsa20_xor(uint8_t out[], const uint8_t in[],
+ size_t in_len, const uint8_t nonce[],
+ const uint8_t key[])
+ {
+ return crypto_stream_xsalsa20_xor_ic(out, in, in_len, nonce, 0, key);
+ }
+
+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
+ return -1;
+
+ Salsa20 salsa;
+ salsa.set_key(key, crypto_stream_xsalsa20_KEYBYTES);
+ salsa.set_iv(nonce, crypto_stream_xsalsa20_NONCEBYTES);
+ salsa.seek(ic * 64);
+ salsa.cipher(in, out, in_len);
+ return 0;
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/compat/sodium/sodium_secretbox.cpp b/comm/third_party/botan/src/lib/compat/sodium/sodium_secretbox.cpp
new file mode 100644
index 0000000000..255e0631b1
--- /dev/null
+++ b/comm/third_party/botan/src/lib/compat/sodium/sodium_secretbox.cpp
@@ -0,0 +1,123 @@
+/*
+* (C) 2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/sodium.h>
+#include <botan/secmem.h>
+#include <botan/stream_cipher.h>
+#include <botan/mac.h>
+
+namespace Botan {
+
+int Sodium::crypto_secretbox_xsalsa20poly1305(uint8_t ctext[],
+ const uint8_t ptext[],
+ size_t ptext_len,
+ const uint8_t nonce[],
+ const uint8_t key[])
+ {
+ if(ptext_len < 32)
+ return -1;
+
+ auto salsa = StreamCipher::create_or_throw("Salsa20");
+ salsa->set_key(key, crypto_secretbox_KEYBYTES);
+ salsa->set_iv(nonce, crypto_secretbox_NONCEBYTES);
+
+ secure_vector<uint8_t> auth_key(32);
+ salsa->write_keystream(auth_key.data(), auth_key.size());
+
+ salsa->cipher(ptext + 32, ctext + 32, ptext_len - 32);
+
+ auto poly1305 = MessageAuthenticationCode::create_or_throw("Poly1305");
+ poly1305->set_key(auth_key);
+ poly1305->update(ctext + 32, ptext_len - 32);
+ poly1305->final(ctext + 16);
+
+ clear_mem(ctext, 16);
+ return 0;
+ }
+
+int Sodium::crypto_secretbox_xsalsa20poly1305_open(uint8_t ptext[],
+ const uint8_t ctext[],
+ size_t ctext_len,
+ const uint8_t nonce[],
+ const uint8_t key[])
+ {
+ if(ctext_len < crypto_box_curve25519xsalsa20poly1305_ZEROBYTES)
+ {
+ return -1;
+ }
+
+ auto salsa = StreamCipher::create_or_throw("Salsa20");
+ salsa->set_key(key, crypto_secretbox_KEYBYTES);
+ salsa->set_iv(nonce, crypto_secretbox_NONCEBYTES);
+
+ secure_vector<uint8_t> auth_key(32);
+ salsa->write_keystream(auth_key.data(), auth_key.size());
+
+ auto poly1305 = MessageAuthenticationCode::create_or_throw("Poly1305");
+ poly1305->set_key(auth_key);
+ poly1305->update(ctext + 32, ctext_len - 32);
+ secure_vector<uint8_t> computed = poly1305->final();
+
+ if(!constant_time_compare(computed.data(), ctext + 16, 16))
+ return -1;
+
+ salsa->cipher(ctext + 32, ptext + 32, ctext_len - 32);
+
+ clear_mem(ptext, 32);
+ return 0;
+ }
+
+int Sodium::crypto_secretbox_detached(uint8_t ctext[], uint8_t mac[],
+ const uint8_t ptext[],
+ size_t ptext_len,
+ const uint8_t nonce[],
+ const uint8_t key[])
+ {
+ auto salsa = StreamCipher::create_or_throw("Salsa20");
+ salsa->set_key(key, crypto_secretbox_KEYBYTES);
+ salsa->set_iv(nonce, crypto_secretbox_NONCEBYTES);
+
+ secure_vector<uint8_t> auth_key(32);
+ salsa->write_keystream(auth_key.data(), auth_key.size());
+
+ salsa->cipher(ptext, ctext, ptext_len);
+
+ auto poly1305 = MessageAuthenticationCode::create_or_throw("Poly1305");
+ poly1305->set_key(auth_key);
+ poly1305->update(ctext, ptext_len);
+ poly1305->final(mac);
+
+ return 0;
+ }
+
+int Sodium::crypto_secretbox_open_detached(uint8_t ptext[],
+ const uint8_t ctext[],
+ const uint8_t mac[],
+ size_t ctext_len,
+ const uint8_t nonce[],
+ const uint8_t key[])
+ {
+ auto salsa = StreamCipher::create_or_throw("Salsa20");
+ salsa->set_key(key, crypto_secretbox_KEYBYTES);
+ salsa->set_iv(nonce, crypto_secretbox_NONCEBYTES);
+
+ secure_vector<uint8_t> auth_key(32);
+ salsa->write_keystream(auth_key.data(), auth_key.size());
+
+ auto poly1305 = MessageAuthenticationCode::create_or_throw("Poly1305");
+ poly1305->set_key(auth_key);
+ poly1305->update(ctext, ctext_len);
+ secure_vector<uint8_t> computed_mac = poly1305->final();
+
+ if(!constant_time_compare(mac, computed_mac.data(), computed_mac.size()))
+ return -1;
+
+ salsa->cipher(ctext, ptext, ctext_len);
+
+ return 0;
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/compat/sodium/sodium_utils.cpp b/comm/third_party/botan/src/lib/compat/sodium/sodium_utils.cpp
new file mode 100644
index 0000000000..3f0a6c84e7
--- /dev/null
+++ b/comm/third_party/botan/src/lib/compat/sodium/sodium_utils.cpp
@@ -0,0 +1,160 @@
+/*
+* (C) 2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/sodium.h>
+#include <botan/chacha.h>
+#include <botan/mem_ops.h>
+#include <botan/system_rng.h>
+#include <botan/internal/os_utils.h>
+#include <botan/internal/ct_utils.h>
+#include <botan/loadstor.h>
+
+namespace Botan {
+
+void Sodium::randombytes_buf(void* buf, size_t len)
+ {
+ system_rng().randomize(static_cast<uint8_t*>(buf), len);
+ }
+
+uint32_t Sodium::randombytes_uniform(uint32_t upper_bound)
+ {
+ if(upper_bound <= 1)
+ return 0;
+
+ // Not completely uniform
+ uint64_t x;
+ randombytes_buf(&x, sizeof(x));
+ return x % upper_bound;
+ }
+
+void Sodium::randombytes_buf_deterministic(void* buf, size_t size, const uint8_t seed[randombytes_SEEDBYTES])
+ {
+ const unsigned char nonce[12] = {
+ 'L', 'i', 'b', 's', 'o', 'd', 'i', 'u', 'm', 'D', 'R', 'G'
+ };
+
+ ChaCha chacha(20);
+ chacha.set_key(seed, randombytes_SEEDBYTES);
+ chacha.set_iv(nonce, sizeof(nonce));
+ chacha.write_keystream(static_cast<uint8_t*>(buf), size);
+ }
+
+int Sodium::crypto_verify_16(const uint8_t x[16], const uint8_t y[16])
+ {
+ return same_mem(x, y, 16);
+ }
+
+int Sodium::crypto_verify_32(const uint8_t x[32], const uint8_t y[32])
+ {
+ return same_mem(x, y, 32);
+ }
+
+int Sodium::crypto_verify_64(const uint8_t x[64], const uint8_t y[64])
+ {
+ return same_mem(x, y, 64);
+ }
+
+void Sodium::sodium_memzero(void* ptr, size_t len)
+ {
+ secure_scrub_memory(ptr, len);
+ }
+
+int Sodium::sodium_memcmp(const void* x, const void* y, size_t len)
+ {
+ const bool same = constant_time_compare(static_cast<const uint8_t*>(x), static_cast<const uint8_t*>(y), len);
+ return same ? 0 : -1;
+ }
+
+int Sodium::sodium_compare(const uint8_t x[], const uint8_t y[], size_t len)
+ {
+ const uint8_t LT = static_cast<uint8_t>(-1);
+ const uint8_t EQ = 0;
+ const uint8_t GT = 1;
+
+ uint8_t result = EQ; // until found otherwise
+
+ for(size_t i = 0; i != len; ++i)
+ {
+ const auto is_eq = CT::Mask<uint8_t>::is_equal(x[i], y[i]);
+ const auto is_lt = CT::Mask<uint8_t>::is_lt(x[i], y[i]);
+ result = is_eq.select(result, is_lt.select(LT, GT));
+ }
+
+ return static_cast<int8_t>(result);
+ }
+
+int Sodium::sodium_is_zero(const uint8_t b[], size_t len)
+ {
+ uint8_t sum = 0;
+ for(size_t i = 0; i != len; ++i)
+ sum |= b[i];
+ return static_cast<int>(CT::Mask<uint8_t>::expand(sum).if_not_set_return(1));
+ }
+
+void Sodium::sodium_increment(uint8_t b[], size_t len)
+ {
+ uint8_t carry = 1;
+ for(size_t i = 0; i != len; ++i)
+ {
+ b[i] += carry;
+ carry &= (b[i] == 0);
+ }
+ }
+
+void Sodium::sodium_add(uint8_t a[], const uint8_t b[], size_t len)
+ {
+ uint8_t carry = 0;
+ for(size_t i = 0; i != len; ++i)
+ {
+ a[i] += b[i] + carry;
+ carry = (a[i] < b[i]);
+ }
+ }
+
+void* Sodium::sodium_malloc(size_t size)
+ {
+ const uint64_t len = size;
+
+ if(size + sizeof(len) < size)
+ return nullptr;
+
+ uint8_t* p = static_cast<uint8_t*>(std::calloc(size + sizeof(len), 1));
+ store_le(len, p);
+ return p + 8;
+ }
+
+void Sodium::sodium_free(void* ptr)
+ {
+ if(ptr == nullptr)
+ return;
+
+ uint8_t* p = static_cast<uint8_t*>(ptr) - 8;
+ const uint64_t len = load_le<uint64_t>(p, 0);
+ secure_scrub_memory(ptr, static_cast<size_t>(len));
+ std::free(p);
+ }
+
+void* Sodium::sodium_allocarray(size_t count, size_t size)
+ {
+ const size_t bytes = count * size;
+ if(bytes < count || bytes < size)
+ return nullptr;
+ return sodium_malloc(bytes);
+ }
+
+int Sodium::sodium_mprotect_noaccess(void* ptr)
+ {
+ OS::page_prohibit_access(ptr);
+ return 0;
+ }
+
+int Sodium::sodium_mprotect_readwrite(void* ptr)
+ {
+ OS::page_allow_access(ptr);
+ return 0;
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/compression/bzip2/bzip2.cpp b/comm/third_party/botan/src/lib/compression/bzip2/bzip2.cpp
new file mode 100644
index 0000000000..c9dfc2ce82
--- /dev/null
+++ b/comm/third_party/botan/src/lib/compression/bzip2/bzip2.cpp
@@ -0,0 +1,110 @@
+/*
+* Bzip2 Compressor
+* (C) 2001 Peter J Jones
+* 2001-2007,2014 Jack Lloyd
+* 2006 Matt Johnston
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/bzip2.h>
+#include <botan/exceptn.h>
+#include <botan/internal/compress_utils.h>
+
+#define BZ_NO_STDIO
+#include <bzlib.h>
+
+namespace Botan {
+
+namespace {
+
+class Bzip2_Stream : public Zlib_Style_Stream<bz_stream, char>
+ {
+ public:
+ Bzip2_Stream()
+ {
+ streamp()->opaque = alloc();
+ streamp()->bzalloc = Compression_Alloc_Info::malloc<int>;
+ streamp()->bzfree = Compression_Alloc_Info::free;
+ }
+
+ uint32_t run_flag() const override { return BZ_RUN; }
+ uint32_t flush_flag() const override { return BZ_FLUSH; }
+ uint32_t finish_flag() const override { return BZ_FINISH; }
+ };
+
+class Bzip2_Compression_Stream final : public Bzip2_Stream
+ {
+ public:
+ explicit Bzip2_Compression_Stream(size_t block_size)
+ {
+ /*
+ * Defaults to 900k blocks as the computation cost of
+ * compression is not overly affected by the size, though
+ * more memory is required.
+ */
+ if(block_size == 0 || block_size >= 9)
+ block_size = 9;
+
+ int rc = BZ2_bzCompressInit(streamp(), block_size, 0, 0);
+
+ if(rc != BZ_OK)
+ throw Compression_Error("BZ2_bzCompressInit", ErrorType::Bzip2Error, rc);
+ }
+
+ ~Bzip2_Compression_Stream()
+ {
+ BZ2_bzCompressEnd(streamp());
+ }
+
+ bool run(uint32_t flags) override
+ {
+ int rc = BZ2_bzCompress(streamp(), flags);
+
+ if(rc < 0)
+ throw Compression_Error("BZ2_bzCompress", ErrorType::Bzip2Error, rc);
+
+ return (rc == BZ_STREAM_END);
+ }
+ };
+
+class Bzip2_Decompression_Stream final : public Bzip2_Stream
+ {
+ public:
+ Bzip2_Decompression_Stream()
+ {
+ int rc = BZ2_bzDecompressInit(streamp(), 0, 0);
+
+ if(rc != BZ_OK)
+ throw Compression_Error("BZ2_bzDecompressInit", ErrorType::Bzip2Error, rc);
+ }
+
+ ~Bzip2_Decompression_Stream()
+ {
+ BZ2_bzDecompressEnd(streamp());
+ }
+
+ bool run(uint32_t) override
+ {
+ int rc = BZ2_bzDecompress(streamp());
+
+ if(rc != BZ_OK && rc != BZ_STREAM_END)
+ throw Compression_Error("BZ2_bzDecompress", ErrorType::Bzip2Error, rc);
+
+ return (rc == BZ_STREAM_END);
+ }
+ };
+
+}
+
+Compression_Stream* Bzip2_Compression::make_stream(size_t comp_level) const
+ {
+ return new Bzip2_Compression_Stream(comp_level);
+ }
+
+Compression_Stream* Bzip2_Decompression::make_stream() const
+ {
+ return new Bzip2_Decompression_Stream;
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/compression/bzip2/bzip2.h b/comm/third_party/botan/src/lib/compression/bzip2/bzip2.h
new file mode 100644
index 0000000000..e056d55021
--- /dev/null
+++ b/comm/third_party/botan/src/lib/compression/bzip2/bzip2.h
@@ -0,0 +1,40 @@
+/*
+* Bzip2 Compressor
+* (C) 2001 Peter J Jones
+* 2001-2007,2014 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_BZIP2_H_
+#define BOTAN_BZIP2_H_
+
+#include <botan/compression.h>
+
+namespace Botan {
+
+/**
+* Bzip2 Compression
+*/
+class BOTAN_PUBLIC_API(2,0) Bzip2_Compression final : public Stream_Compression
+ {
+ public:
+ std::string name() const override { return "Bzip2_Compression"; }
+ private:
+ Compression_Stream* make_stream(size_t comp_level) const override;
+ };
+
+/**
+* Bzip2 Deccompression
+*/
+class BOTAN_PUBLIC_API(2,0) Bzip2_Decompression final : public Stream_Decompression
+ {
+ public:
+ std::string name() const override { return "Bzip2_Decompression"; }
+ private:
+ Compression_Stream* make_stream() const override;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/compression/bzip2/info.txt b/comm/third_party/botan/src/lib/compression/bzip2/info.txt
new file mode 100644
index 0000000000..8826df2f96
--- /dev/null
+++ b/comm/third_party/botan/src/lib/compression/bzip2/info.txt
@@ -0,0 +1,9 @@
+<defines>
+BZIP2 -> 20160412
+</defines>
+
+load_on vendor
+
+<libs>
+all -> bz2
+</libs>
diff --git a/comm/third_party/botan/src/lib/compression/compress_utils.cpp b/comm/third_party/botan/src/lib/compression/compress_utils.cpp
new file mode 100644
index 0000000000..f49a0ede12
--- /dev/null
+++ b/comm/third_party/botan/src/lib/compression/compress_utils.cpp
@@ -0,0 +1,196 @@
+/*
+* Compression Utils
+* (C) 2014,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/compress_utils.h>
+#include <botan/exceptn.h>
+#include <cstdlib>
+
+namespace Botan {
+
+void* Compression_Alloc_Info::do_malloc(size_t n, size_t size)
+ {
+ // TODO maximum length check here?
+ void* ptr = std::calloc(n, size);
+
+ /*
+ * Return null rather than throwing here as we are being called by a
+ * C library and it may not be possible for an exception to unwind
+ * the call stack from here. The compression library is expecting a
+ * function written in C and a null return on error, which it will
+ * send upwards to the compression wrappers.
+ */
+
+ if(ptr)
+ {
+ m_current_allocs[ptr] = n * size;
+ }
+
+ return ptr;
+ }
+
+void Compression_Alloc_Info::do_free(void* ptr)
+ {
+ if(ptr)
+ {
+ auto i = m_current_allocs.find(ptr);
+
+ if(i == m_current_allocs.end())
+ throw Internal_Error("Compression_Alloc_Info::free got pointer not allocated by us");
+
+ secure_scrub_memory(ptr, i->second);
+ std::free(ptr);
+ m_current_allocs.erase(i);
+ }
+ }
+
+void Stream_Compression::clear()
+ {
+ m_stream.reset();
+ }
+
+void Stream_Compression::start(size_t level)
+ {
+ m_stream.reset(make_stream(level));
+ }
+
+void Stream_Compression::process(secure_vector<uint8_t>& buf, size_t offset, uint32_t flags)
+ {
+ BOTAN_ASSERT(m_stream, "Initialized");
+ BOTAN_ASSERT(buf.size() >= offset, "Offset is sane");
+
+ // bzip doesn't like being called with no input and BZ_RUN
+ if(buf.size() == offset && flags == m_stream->run_flag())
+ {
+ return;
+ }
+
+ if(m_buffer.size() < buf.size() + offset)
+ m_buffer.resize(buf.size() + offset);
+
+ // If the output buffer has zero length, .data() might return nullptr. This would
+ // make some compression algorithms (notably those provided by zlib) fail.
+ // Any small positive value works fine, but we choose 32 as it is the smallest power
+ // of two that is large enough to hold all the headers and trailers of the common
+ // formats, preventing further resizings to make room for output data.
+ if(m_buffer.size() == 0)
+ m_buffer.resize(32);
+
+ m_stream->next_in(buf.data() + offset, buf.size() - offset);
+ m_stream->next_out(m_buffer.data() + offset, m_buffer.size() - offset);
+
+ while(true)
+ {
+ const bool stream_end = m_stream->run(flags);
+
+ if(stream_end)
+ {
+ BOTAN_ASSERT(m_stream->avail_in() == 0, "After stream is done, no input remains to be processed");
+ m_buffer.resize(m_buffer.size() - m_stream->avail_out());
+ break;
+ }
+ else if(m_stream->avail_out() == 0)
+ {
+ const size_t added = 8 + m_buffer.size();
+ m_buffer.resize(m_buffer.size() + added);
+ m_stream->next_out(m_buffer.data() + m_buffer.size() - added, added);
+ }
+ else if(m_stream->avail_in() == 0)
+ {
+ m_buffer.resize(m_buffer.size() - m_stream->avail_out());
+ break;
+ }
+ }
+
+ copy_mem(m_buffer.data(), buf.data(), offset);
+ buf.swap(m_buffer);
+ }
+
+void Stream_Compression::update(secure_vector<uint8_t>& buf, size_t offset, bool flush)
+ {
+ BOTAN_ASSERT(m_stream, "Initialized");
+ process(buf, offset, flush ? m_stream->flush_flag() : m_stream->run_flag());
+ }
+
+void Stream_Compression::finish(secure_vector<uint8_t>& buf, size_t offset)
+ {
+ BOTAN_ASSERT(m_stream, "Initialized");
+ process(buf, offset, m_stream->finish_flag());
+ clear();
+ }
+
+void Stream_Decompression::clear()
+ {
+ m_stream.reset();
+ }
+
+void Stream_Decompression::start()
+ {
+ m_stream.reset(make_stream());
+ }
+
+void Stream_Decompression::process(secure_vector<uint8_t>& buf, size_t offset, uint32_t flags)
+ {
+ BOTAN_ASSERT(m_stream, "Initialized");
+ BOTAN_ASSERT(buf.size() >= offset, "Offset is sane");
+
+ if(m_buffer.size() < buf.size() + offset)
+ m_buffer.resize(buf.size() + offset);
+
+ m_stream->next_in(buf.data() + offset, buf.size() - offset);
+ m_stream->next_out(m_buffer.data() + offset, m_buffer.size() - offset);
+
+ while(true)
+ {
+ const bool stream_end = m_stream->run(flags);
+
+ if(stream_end)
+ {
+ if(m_stream->avail_in() == 0) // all data consumed?
+ {
+ m_buffer.resize(m_buffer.size() - m_stream->avail_out());
+ clear();
+ break;
+ }
+
+ // More data follows: try to process as a following stream
+ const size_t read = (buf.size() - offset) - m_stream->avail_in();
+ start();
+ m_stream->next_in(buf.data() + offset + read, buf.size() - offset - read);
+ }
+
+ if(m_stream->avail_out() == 0)
+ {
+ const size_t added = 8 + m_buffer.size();
+ m_buffer.resize(m_buffer.size() + added);
+ m_stream->next_out(m_buffer.data() + m_buffer.size() - added, added);
+ }
+ else if(m_stream->avail_in() == 0)
+ {
+ m_buffer.resize(m_buffer.size() - m_stream->avail_out());
+ break;
+ }
+ }
+
+ copy_mem(m_buffer.data(), buf.data(), offset);
+ buf.swap(m_buffer);
+ }
+
+void Stream_Decompression::update(secure_vector<uint8_t>& buf, size_t offset)
+ {
+ process(buf, offset, m_stream->run_flag());
+ }
+
+void Stream_Decompression::finish(secure_vector<uint8_t>& buf, size_t offset)
+ {
+ if(buf.size() != offset || m_stream.get())
+ process(buf, offset, m_stream->finish_flag());
+
+ if(m_stream.get())
+ throw Invalid_State(name() + " finished but not at stream end");
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/compression/compress_utils.h b/comm/third_party/botan/src/lib/compression/compress_utils.h
new file mode 100644
index 0000000000..396fc47bed
--- /dev/null
+++ b/comm/third_party/botan/src/lib/compression/compress_utils.h
@@ -0,0 +1,89 @@
+/*
+* Compression utility header
+* (C) 2014 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_COMPRESSION_UTILS_H_
+#define BOTAN_COMPRESSION_UTILS_H_
+
+#include <botan/compression.h>
+#include <memory>
+#include <unordered_map>
+
+namespace Botan {
+
+/*
+* Allocation Size Tracking Helper for Zlib/Bzlib/LZMA
+*/
+class Compression_Alloc_Info final
+ {
+ public:
+ template<typename T>
+ static void* malloc(void* self, T n, T size)
+ {
+ return static_cast<Compression_Alloc_Info*>(self)->do_malloc(n, size);
+ }
+
+ static void free(void* self, void* ptr)
+ {
+ static_cast<Compression_Alloc_Info*>(self)->do_free(ptr);
+ }
+
+ private:
+ void* do_malloc(size_t n, size_t size);
+ void do_free(void* ptr);
+
+ std::unordered_map<void*, size_t> m_current_allocs;
+ };
+
+/**
+* Wrapper for Zlib/Bzlib/LZMA stream types
+*/
+template<typename Stream, typename ByteType>
+class Zlib_Style_Stream : public Compression_Stream
+ {
+ public:
+ void next_in(uint8_t* b, size_t len) override
+ {
+ m_stream.next_in = reinterpret_cast<ByteType*>(b);
+ m_stream.avail_in = len;
+ }
+
+ void next_out(uint8_t* b, size_t len) override
+ {
+ m_stream.next_out = reinterpret_cast<ByteType*>(b);
+ m_stream.avail_out = len;
+ }
+
+ size_t avail_in() const override { return m_stream.avail_in; }
+
+ size_t avail_out() const override { return m_stream.avail_out; }
+
+ Zlib_Style_Stream()
+ {
+ clear_mem(&m_stream, 1);
+ m_allocs.reset(new Compression_Alloc_Info);
+ }
+
+ ~Zlib_Style_Stream()
+ {
+ clear_mem(&m_stream, 1);
+ m_allocs.reset();
+ }
+
+ protected:
+ typedef Stream stream_t;
+
+ stream_t* streamp() { return &m_stream; }
+
+ Compression_Alloc_Info* alloc() { return m_allocs.get(); }
+ private:
+ stream_t m_stream;
+ std::unique_ptr<Compression_Alloc_Info> m_allocs;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/compression/compression.cpp b/comm/third_party/botan/src/lib/compression/compression.cpp
new file mode 100644
index 0000000000..361bf7dd3c
--- /dev/null
+++ b/comm/third_party/botan/src/lib/compression/compression.cpp
@@ -0,0 +1,116 @@
+/*
+* Compression Factory
+* (C) 2014,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/compression.h>
+#include <botan/mem_ops.h>
+#include <botan/exceptn.h>
+#include <cstdlib>
+
+#if defined(BOTAN_HAS_ZLIB)
+ #include <botan/zlib.h>
+#endif
+
+#if defined(BOTAN_HAS_BZIP2)
+ #include <botan/bzip2.h>
+#endif
+
+#if defined(BOTAN_HAS_LZMA)
+ #include <botan/lzma.h>
+#endif
+
+namespace Botan {
+
+Compression_Algorithm* make_compressor(const std::string& name)
+ {
+#if defined(BOTAN_HAS_ZLIB)
+ if(name == "Zlib" || name == "zlib")
+ return new Zlib_Compression;
+ if(name == "Gzip" || name == "gzip" || name == "gz")
+ return new Gzip_Compression;
+ if(name == "Deflate" || name == "deflate")
+ return new Deflate_Compression;
+#endif
+
+#if defined(BOTAN_HAS_BZIP2)
+ if(name == "bzip2" || name == "bz2" || name == "Bzip2")
+ return new Bzip2_Compression;
+#endif
+
+#if defined(BOTAN_HAS_LZMA)
+ if(name == "lzma" || name == "xz" || name == "LZMA")
+ return new LZMA_Compression;
+#endif
+
+ BOTAN_UNUSED(name);
+ return nullptr;
+ }
+
+//static
+std::unique_ptr<Compression_Algorithm>
+Compression_Algorithm::create(const std::string& algo)
+ {
+ std::unique_ptr<Compression_Algorithm> compressor(make_compressor(algo));
+ return compressor;
+ }
+
+//static
+std::unique_ptr<Compression_Algorithm>
+Compression_Algorithm::create_or_throw(const std::string& algo)
+ {
+ if(auto compressor = Compression_Algorithm::create(algo))
+ {
+ return compressor;
+ }
+ throw Lookup_Error("Compression", algo, "");
+ }
+
+Decompression_Algorithm* make_decompressor(const std::string& name)
+ {
+#if defined(BOTAN_HAS_ZLIB)
+ if(name == "Zlib" || name == "zlib")
+ return new Zlib_Decompression;
+ if(name == "Gzip" || name == "gzip" || name == "gz")
+ return new Gzip_Decompression;
+ if(name == "Deflate" || name == "deflate")
+ return new Deflate_Decompression;
+#endif
+
+#if defined(BOTAN_HAS_BZIP2)
+ if(name == "bzip2" || name == "bz2" || name == "Bzip2")
+ return new Bzip2_Decompression;
+#endif
+
+#if defined(BOTAN_HAS_LZMA)
+ if(name == "lzma" || name == "xz" || name == "LZMA")
+ return new LZMA_Decompression;
+#endif
+
+ BOTAN_UNUSED(name);
+ return nullptr;
+ }
+
+//static
+std::unique_ptr<Decompression_Algorithm>
+Decompression_Algorithm::create(const std::string& algo)
+ {
+ std::unique_ptr<Decompression_Algorithm> decompressor(make_decompressor(algo));
+ return decompressor;
+ }
+
+//static
+std::unique_ptr<Decompression_Algorithm>
+Decompression_Algorithm::create_or_throw(const std::string& algo)
+ {
+ if(auto decompressor = Decompression_Algorithm::create(algo))
+ {
+ return decompressor;
+ }
+ throw Lookup_Error("Decompression", algo, "");
+ }
+
+}
+
diff --git a/comm/third_party/botan/src/lib/compression/compression.h b/comm/third_party/botan/src/lib/compression/compression.h
new file mode 100644
index 0000000000..217fae623a
--- /dev/null
+++ b/comm/third_party/botan/src/lib/compression/compression.h
@@ -0,0 +1,238 @@
+/*
+* Compression Transform
+* (C) 2014 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_COMPRESSION_TRANSFORM_H_
+#define BOTAN_COMPRESSION_TRANSFORM_H_
+
+#include <botan/secmem.h>
+#include <botan/exceptn.h>
+#include <string>
+
+namespace Botan {
+
+/**
+* Interface for a compression algorithm.
+*/
+class BOTAN_PUBLIC_API(2,0) Compression_Algorithm
+ {
+ public:
+ /**
+ * Create an instance based on a name, or return null if the
+ * algo combination cannot be found.
+ */
+ static std::unique_ptr<Compression_Algorithm>
+ create(const std::string& algo_spec);
+
+ /**
+ * Create an instance based on a name
+ * @param algo_spec algorithm name
+ * Throws Lookup_Error if not found.
+ */
+ static std::unique_ptr<Compression_Algorithm>
+ create_or_throw(const std::string& algo_spec);
+
+ /**
+ * Begin compressing. Most compression algorithms offer a tunable
+ * time/compression tradeoff parameter generally represented by
+ * an integer in the range of 1 to 9.
+ *
+ * If 0 or a value out of range is provided, a compression algorithm
+ * specific default is used.
+ */
+ virtual void start(size_t comp_level = 0) = 0;
+
+ /**
+ * Process some data.
+ * @param buf in/out parameter which will possibly be resized or swapped
+ * @param offset an offset into blocks to begin processing
+ * @param flush if true the compressor will be told to flush state
+ */
+ virtual void update(secure_vector<uint8_t>& buf, size_t offset = 0, bool flush = false) = 0;
+
+ /**
+ * Finish compressing
+ *
+ * @param final_block in/out parameter
+ * @param offset an offset into final_block to begin processing
+ */
+ virtual void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) = 0;
+
+ /**
+ * @return name of the compression algorithm
+ */
+ virtual std::string name() const = 0;
+
+ /**
+ * Reset the state and abort the current message; start can be
+ * called again to process a new message.
+ */
+ virtual void clear() = 0;
+
+ virtual ~Compression_Algorithm() = default;
+ };
+
+/*
+* Interface for a decompression algorithm.
+*/
+class BOTAN_PUBLIC_API(2,0) Decompression_Algorithm
+ {
+ public:
+ /**
+ * Create an instance based on a name, or return null if the
+ * algo combination cannot be found.
+ */
+ static std::unique_ptr<Decompression_Algorithm>
+ create(const std::string& algo_spec);
+
+ /**
+ * Create an instance based on a name
+ * @param algo_spec algorithm name
+ * Throws Lookup_Error if not found.
+ */
+ static std::unique_ptr<Decompression_Algorithm>
+ create_or_throw(const std::string& algo_spec);
+
+ /**
+ * Begin decompressing.
+ * Decompression does not support levels, as compression does.
+ */
+ virtual void start() = 0;
+
+ /**
+ * Process some data.
+ * @param buf in/out parameter which will possibly be resized or swapped
+ * @param offset an offset into blocks to begin processing
+ */
+ virtual void update(secure_vector<uint8_t>& buf, size_t offset = 0) = 0;
+
+ /**
+ * Finish decompressing
+ *
+ * @param final_block in/out parameter
+ * @param offset an offset into final_block to begin processing
+ */
+ virtual void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) = 0;
+
+ /**
+ * @return name of the decompression algorithm
+ */
+ virtual std::string name() const = 0;
+
+ /**
+ * Reset the state and abort the current message; start can be
+ * called again to process a new message.
+ */
+ virtual void clear() = 0;
+
+ virtual ~Decompression_Algorithm() = default;
+ };
+
+BOTAN_PUBLIC_API(2,0) Compression_Algorithm* make_compressor(const std::string& type);
+BOTAN_PUBLIC_API(2,0) Decompression_Algorithm* make_decompressor(const std::string& type);
+
+/**
+* An error that occurred during compression (or decompression)
+*/
+class BOTAN_PUBLIC_API(2,9) Compression_Error : public Exception
+ {
+ public:
+
+ /**
+ * @param func_name the name of the compression API that was called
+ * (eg "BZ2_bzCompressInit" or "lzma_code")
+ * @param type what library this came from
+ * @param rc the error return code from the compression API. The
+ * interpretation of this value will depend on the library.
+ */
+ Compression_Error(const char* func_name, ErrorType type, int rc) :
+ Exception("Compression API " + std::string(func_name) +
+ " failed with return code " + std::to_string(rc)),
+ m_type(type),
+ m_rc(rc)
+ {}
+
+ ErrorType error_type() const noexcept override { return m_type; }
+
+ int error_code() const noexcept override { return m_rc; }
+
+ private:
+ ErrorType m_type;
+ int m_rc;
+ };
+
+/**
+* Adapts a zlib style API
+*/
+class Compression_Stream
+ {
+ public:
+ virtual ~Compression_Stream() = default;
+
+ virtual void next_in(uint8_t* b, size_t len) = 0;
+
+ virtual void next_out(uint8_t* b, size_t len) = 0;
+
+ virtual size_t avail_in() const = 0;
+
+ virtual size_t avail_out() const = 0;
+
+ virtual uint32_t run_flag() const = 0;
+ virtual uint32_t flush_flag() const = 0;
+ virtual uint32_t finish_flag() const = 0;
+
+ virtual bool run(uint32_t flags) = 0;
+ };
+
+/**
+* Used to implement compression using Compression_Stream
+*/
+class Stream_Compression : public Compression_Algorithm
+ {
+ public:
+ void update(secure_vector<uint8_t>& buf, size_t offset, bool flush) final override;
+
+ void finish(secure_vector<uint8_t>& buf, size_t offset) final override;
+
+ void clear() final override;
+
+ private:
+ void start(size_t level) final override;
+
+ void process(secure_vector<uint8_t>& buf, size_t offset, uint32_t flags);
+
+ virtual Compression_Stream* make_stream(size_t level) const = 0;
+
+ secure_vector<uint8_t> m_buffer;
+ std::unique_ptr<Compression_Stream> m_stream;
+ };
+
+/**
+* FIXME add doc
+*/
+class Stream_Decompression : public Decompression_Algorithm
+ {
+ public:
+ void update(secure_vector<uint8_t>& buf, size_t offset) final override;
+
+ void finish(secure_vector<uint8_t>& buf, size_t offset) final override;
+
+ void clear() final override;
+
+ private:
+ void start() final override;
+
+ void process(secure_vector<uint8_t>& buf, size_t offset, uint32_t flags);
+
+ virtual Compression_Stream* make_stream() const = 0;
+
+ secure_vector<uint8_t> m_buffer;
+ std::unique_ptr<Compression_Stream> m_stream;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/compression/info.txt b/comm/third_party/botan/src/lib/compression/info.txt
new file mode 100644
index 0000000000..fade2e51aa
--- /dev/null
+++ b/comm/third_party/botan/src/lib/compression/info.txt
@@ -0,0 +1,11 @@
+<defines>
+COMPRESSION -> 20141117
+</defines>
+
+<header:internal>
+compress_utils.h
+</header:internal>
+
+<header:public>
+compression.h
+</header:public>
diff --git a/comm/third_party/botan/src/lib/compression/lzma/info.txt b/comm/third_party/botan/src/lib/compression/lzma/info.txt
new file mode 100644
index 0000000000..477a7b7951
--- /dev/null
+++ b/comm/third_party/botan/src/lib/compression/lzma/info.txt
@@ -0,0 +1,9 @@
+<defines>
+LZMA -> 20160412
+</defines>
+
+load_on vendor
+
+<libs>
+all -> lzma
+</libs>
diff --git a/comm/third_party/botan/src/lib/compression/lzma/lzma.cpp b/comm/third_party/botan/src/lib/compression/lzma/lzma.cpp
new file mode 100644
index 0000000000..73bb9eb89f
--- /dev/null
+++ b/comm/third_party/botan/src/lib/compression/lzma/lzma.cpp
@@ -0,0 +1,95 @@
+/*
+* Lzma Compressor
+* (C) 2001 Peter J Jones
+* 2001-2007,2014 Jack Lloyd
+* 2006 Matt Johnston
+* 2012 Vojtech Kral
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/lzma.h>
+#include <botan/internal/compress_utils.h>
+#include <botan/exceptn.h>
+#include <lzma.h>
+
+namespace Botan {
+
+namespace {
+
+class LZMA_Stream : public Zlib_Style_Stream<lzma_stream, uint8_t>
+ {
+ public:
+ LZMA_Stream()
+ {
+ m_allocator.opaque = alloc();
+ m_allocator.alloc = Compression_Alloc_Info::malloc<size_t>;
+ m_allocator.free = Compression_Alloc_Info::free;
+ streamp()->allocator = &m_allocator;
+ }
+
+ ~LZMA_Stream()
+ {
+ ::lzma_end(streamp());
+ }
+
+ bool run(uint32_t flags) override
+ {
+ lzma_ret rc = ::lzma_code(streamp(), static_cast<lzma_action>(flags));
+
+ if(rc != LZMA_OK && rc != LZMA_STREAM_END)
+ throw Compression_Error("lzma_code", ErrorType::LzmaError, rc);
+
+ return (rc == LZMA_STREAM_END);
+ }
+
+ uint32_t run_flag() const override { return LZMA_RUN; }
+ uint32_t flush_flag() const override { return LZMA_FULL_FLUSH; }
+ uint32_t finish_flag() const override { return LZMA_FINISH; }
+ private:
+ ::lzma_allocator m_allocator;
+ };
+
+class LZMA_Compression_Stream final : public LZMA_Stream
+ {
+ public:
+ explicit LZMA_Compression_Stream(size_t level)
+ {
+ if(level == 0)
+ level = 6; // default
+ else if(level > 9)
+ level = 9; // clamp to maximum allowed value
+
+ lzma_ret rc = ::lzma_easy_encoder(streamp(), level, LZMA_CHECK_CRC64);
+
+ if(rc != LZMA_OK)
+ throw Compression_Error("lzam_easy_encoder", ErrorType::LzmaError, rc);
+ }
+ };
+
+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);
+
+ if(rc != LZMA_OK)
+ throw Compression_Error("lzma_stream_decoder", ErrorType::LzmaError, rc);
+ }
+ };
+
+}
+
+Compression_Stream* LZMA_Compression::make_stream(size_t level) const
+ {
+ return new LZMA_Compression_Stream(level);
+ }
+
+Compression_Stream* LZMA_Decompression::make_stream() const
+ {
+ return new LZMA_Decompression_Stream;
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/compression/lzma/lzma.h b/comm/third_party/botan/src/lib/compression/lzma/lzma.h
new file mode 100644
index 0000000000..02a1f8c8da
--- /dev/null
+++ b/comm/third_party/botan/src/lib/compression/lzma/lzma.h
@@ -0,0 +1,42 @@
+/*
+* Lzma Compressor
+* (C) 2001 Peter J Jones
+* 2001-2007 Jack Lloyd
+* 2012 Vojtech Kral
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_LZMA_H_
+#define BOTAN_LZMA_H_
+
+#include <botan/compression.h>
+
+namespace Botan {
+
+/**
+* LZMA Compression
+*/
+class BOTAN_PUBLIC_API(2,0) LZMA_Compression final : public Stream_Compression
+ {
+ public:
+ std::string name() const override { return "LZMA_Compression"; }
+
+ private:
+ Compression_Stream* make_stream(size_t level) const override;
+ };
+
+/**
+* LZMA Deccompression
+*/
+class BOTAN_PUBLIC_API(2,0) LZMA_Decompression final : public Stream_Decompression
+ {
+ public:
+ std::string name() const override { return "LZMA_Decompression"; }
+ private:
+ Compression_Stream* make_stream() const override;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/compression/zlib/info.txt b/comm/third_party/botan/src/lib/compression/zlib/info.txt
new file mode 100644
index 0000000000..1102bc5e1e
--- /dev/null
+++ b/comm/third_party/botan/src/lib/compression/zlib/info.txt
@@ -0,0 +1,10 @@
+<defines>
+ZLIB -> 20160412
+</defines>
+
+load_on vendor
+
+<libs>
+all!windows -> z
+windows -> zlib
+</libs>
diff --git a/comm/third_party/botan/src/lib/compression/zlib/zlib.cpp b/comm/third_party/botan/src/lib/compression/zlib/zlib.cpp
new file mode 100644
index 0000000000..285bc4e916
--- /dev/null
+++ b/comm/third_party/botan/src/lib/compression/zlib/zlib.cpp
@@ -0,0 +1,173 @@
+/*
+* Zlib Compressor
+* (C) 2001 Peter J Jones
+* 2001-2007,2014 Jack Lloyd
+* 2006 Matt Johnston
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/zlib.h>
+#include <botan/internal/compress_utils.h>
+#include <botan/exceptn.h>
+#include <zlib.h>
+
+namespace Botan {
+
+namespace {
+
+class Zlib_Stream : public Zlib_Style_Stream<z_stream, Bytef>
+ {
+ public:
+ Zlib_Stream()
+ {
+ streamp()->opaque = alloc();
+ streamp()->zalloc = Compression_Alloc_Info::malloc<unsigned int>;
+ streamp()->zfree = Compression_Alloc_Info::free;
+ }
+
+ uint32_t run_flag() const override { return Z_NO_FLUSH; }
+ uint32_t flush_flag() const override { return Z_SYNC_FLUSH; }
+ uint32_t finish_flag() const override { return Z_FINISH; }
+
+ int compute_window_bits(int wbits, int wbits_offset) const
+ {
+ if(wbits_offset == -1)
+ return -wbits;
+ else
+ return wbits + wbits_offset;
+ }
+ };
+
+class Zlib_Compression_Stream : public Zlib_Stream
+ {
+ public:
+ Zlib_Compression_Stream(size_t level, int wbits, int wbits_offset = 0)
+ {
+ wbits = compute_window_bits(wbits, wbits_offset);
+
+ if(level >= 9)
+ level = 9;
+ else if(level == 0)
+ level = 6;
+
+ int rc = ::deflateInit2(streamp(), level, Z_DEFLATED, wbits, 8, Z_DEFAULT_STRATEGY);
+
+ if(rc != Z_OK)
+ throw Compression_Error("deflateInit2", ErrorType::ZlibError, rc);
+ }
+
+ ~Zlib_Compression_Stream()
+ {
+ ::deflateEnd(streamp());
+ }
+
+ bool run(uint32_t flags) override
+ {
+ 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);
+
+ return (rc == Z_STREAM_END);
+ }
+ };
+
+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));
+
+ if(rc != Z_OK)
+ throw Compression_Error("inflateInit2", ErrorType::ZlibError, rc);
+ }
+
+ ~Zlib_Decompression_Stream()
+ {
+ ::inflateEnd(streamp());
+ }
+
+ bool run(uint32_t flags) override
+ {
+ 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);
+
+ return (rc == Z_STREAM_END);
+ }
+ };
+
+class Deflate_Compression_Stream final : public Zlib_Compression_Stream
+ {
+ public:
+ Deflate_Compression_Stream(size_t level, int wbits) :
+ Zlib_Compression_Stream(level, wbits, -1) {}
+ };
+
+class Deflate_Decompression_Stream final : public Zlib_Decompression_Stream
+ {
+ public:
+ explicit Deflate_Decompression_Stream(int wbits) : Zlib_Decompression_Stream(wbits, -1) {}
+ };
+
+class Gzip_Compression_Stream final : public Zlib_Compression_Stream
+ {
+ public:
+ Gzip_Compression_Stream(size_t level, int wbits, uint8_t os_code, uint64_t hdr_time) :
+ Zlib_Compression_Stream(level, wbits, 16)
+ {
+ clear_mem(&m_header, 1);
+ m_header.os = os_code;
+ m_header.time = static_cast<uLong>(hdr_time);
+
+ int rc = deflateSetHeader(streamp(), &m_header);
+ if(rc != Z_OK)
+ throw Compression_Error("deflateSetHeader", ErrorType::ZlibError, rc);
+ }
+
+ private:
+ ::gz_header m_header;
+ };
+
+class Gzip_Decompression_Stream final : public Zlib_Decompression_Stream
+ {
+ public:
+ explicit Gzip_Decompression_Stream(int wbits) : Zlib_Decompression_Stream(wbits, 16) {}
+ };
+
+}
+
+Compression_Stream* Zlib_Compression::make_stream(size_t level) const
+ {
+ return new Zlib_Compression_Stream(level, 15);
+ }
+
+Compression_Stream* Zlib_Decompression::make_stream() const
+ {
+ return new Zlib_Decompression_Stream(15);
+ }
+
+Compression_Stream* Deflate_Compression::make_stream(size_t level) const
+ {
+ return new Deflate_Compression_Stream(level, 15);
+ }
+
+Compression_Stream* Deflate_Decompression::make_stream() const
+ {
+ return new Deflate_Decompression_Stream(15);
+ }
+
+Compression_Stream* Gzip_Compression::make_stream(size_t level) const
+ {
+ return new Gzip_Compression_Stream(level, 15, m_os_code, m_hdr_time);
+ }
+
+Compression_Stream* Gzip_Decompression::make_stream() const
+ {
+ return new Gzip_Decompression_Stream(15);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/compression/zlib/zlib.h b/comm/third_party/botan/src/lib/compression/zlib/zlib.h
new file mode 100644
index 0000000000..cc00603446
--- /dev/null
+++ b/comm/third_party/botan/src/lib/compression/zlib/zlib.h
@@ -0,0 +1,89 @@
+/*
+* Zlib Compressor
+* (C) 2001 Peter J Jones
+* 2001-2007,2014 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_ZLIB_H_
+#define BOTAN_ZLIB_H_
+
+#include <botan/compression.h>
+
+namespace Botan {
+
+/**
+* Zlib Compression
+*/
+class BOTAN_PUBLIC_API(2,0) Zlib_Compression final : public Stream_Compression
+ {
+ public:
+ std::string name() const override { return "Zlib_Compression"; }
+ private:
+ Compression_Stream* make_stream(size_t level) const override;
+ };
+
+/**
+* Zlib Decompression
+*/
+class BOTAN_PUBLIC_API(2,0) Zlib_Decompression final : public Stream_Decompression
+ {
+ public:
+ std::string name() const override { return "Zlib_Decompression"; }
+ private:
+ Compression_Stream* make_stream() const override;
+ };
+
+/**
+* Deflate Compression
+*/
+class BOTAN_PUBLIC_API(2,0) Deflate_Compression final : public Stream_Compression
+ {
+ public:
+ std::string name() const override { return "Deflate_Compression"; }
+ private:
+ Compression_Stream* make_stream(size_t level) const override;
+ };
+
+/**
+* Deflate Decompression
+*/
+class BOTAN_PUBLIC_API(2,0) Deflate_Decompression final : public Stream_Decompression
+ {
+ public:
+ std::string name() const override { return "Deflate_Decompression"; }
+ private:
+ Compression_Stream* make_stream() const override;
+ };
+
+/**
+* Gzip Compression
+*/
+class BOTAN_PUBLIC_API(2,0) Gzip_Compression final : public Stream_Compression
+ {
+ public:
+ explicit Gzip_Compression(uint8_t os_code = 255, uint64_t hdr_time = 0) :
+ m_hdr_time(hdr_time), m_os_code(os_code) {}
+
+ std::string name() const override { return "Gzip_Compression"; }
+ private:
+ Compression_Stream* make_stream(size_t level) const override;
+ const uint64_t m_hdr_time;
+ const uint8_t m_os_code;
+ };
+
+/**
+* Gzip Decompression
+*/
+class BOTAN_PUBLIC_API(2,0) Gzip_Decompression final : public Stream_Decompression
+ {
+ public:
+ std::string name() const override { return "Gzip_Decompression"; }
+ private:
+ Compression_Stream* make_stream() const override;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/entropy/dev_random/dev_random.cpp b/comm/third_party/botan/src/lib/entropy/dev_random/dev_random.cpp
new file mode 100644
index 0000000000..44fcbace3f
--- /dev/null
+++ b/comm/third_party/botan/src/lib/entropy/dev_random/dev_random.cpp
@@ -0,0 +1,122 @@
+/*
+* Reader of /dev/random and company
+* (C) 1999-2009,2013 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/dev_random.h>
+#include <botan/exceptn.h>
+
+#include <sys/types.h>
+#include <sys/select.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+
+namespace Botan {
+
+/**
+Device_EntropySource constructor
+Open a file descriptor to each (available) device in fsnames
+*/
+Device_EntropySource::Device_EntropySource(const std::vector<std::string>& fsnames)
+ {
+#ifndef O_NONBLOCK
+ #define O_NONBLOCK 0
+#endif
+
+#ifndef O_NOCTTY
+ #define O_NOCTTY 0
+#endif
+
+ const int flags = O_RDONLY | O_NONBLOCK | O_NOCTTY;
+
+ m_max_fd = 0;
+
+ for(auto fsname : fsnames)
+ {
+ int fd = ::open(fsname.c_str(), flags);
+
+ if(fd < 0)
+ {
+ /*
+ ENOENT or EACCES is normal as some of the named devices may not exist
+ on this system. But any other errno value probably indicates
+ either a bug in the application or file descriptor exhaustion.
+ */
+ if(errno != ENOENT && errno != EACCES)
+ throw System_Error("Opening OS RNG device failed", errno);
+ }
+ else
+ {
+ if(fd > FD_SETSIZE)
+ {
+ ::close(fd);
+ throw Invalid_State("Open of OS RNG succeeded but returned fd is too large for fd_set");
+ }
+
+ m_dev_fds.push_back(fd);
+ m_max_fd = std::max(m_max_fd, fd);
+ }
+ }
+ }
+
+/**
+Device_EntropySource destructor: close all open devices
+*/
+Device_EntropySource::~Device_EntropySource()
+ {
+ for(int fd : m_dev_fds)
+ {
+ // ignoring return value here, can't throw in destructor anyway
+ ::close(fd);
+ }
+ }
+
+/**
+* Gather entropy from a RNG device
+*/
+size_t Device_EntropySource::poll(RandomNumberGenerator& rng)
+ {
+ size_t bits = 0;
+
+ if(m_dev_fds.size() > 0)
+ {
+ fd_set read_set;
+ FD_ZERO(&read_set);
+
+ for(int dev_fd : m_dev_fds)
+ {
+ FD_SET(dev_fd, &read_set);
+ }
+
+ secure_vector<uint8_t> io_buf(BOTAN_SYSTEM_RNG_POLL_REQUEST);
+
+ struct ::timeval timeout;
+ timeout.tv_sec = (BOTAN_SYSTEM_RNG_POLL_TIMEOUT_MS / 1000);
+ timeout.tv_usec = (BOTAN_SYSTEM_RNG_POLL_TIMEOUT_MS % 1000) * 1000;
+
+ if(::select(m_max_fd + 1, &read_set, nullptr, nullptr, &timeout) > 0)
+ {
+ for(int dev_fd : m_dev_fds)
+ {
+ if(FD_ISSET(dev_fd, &read_set))
+ {
+ const ssize_t got = ::read(dev_fd, io_buf.data(), io_buf.size());
+
+ if(got > 0)
+ {
+ rng.add_entropy(io_buf.data(), static_cast<size_t>(got));
+ bits += got * 8;
+ }
+ }
+ }
+ }
+ }
+
+ return bits;
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/entropy/dev_random/dev_random.h b/comm/third_party/botan/src/lib/entropy/dev_random/dev_random.h
new file mode 100644
index 0000000000..6195f85648
--- /dev/null
+++ b/comm/third_party/botan/src/lib/entropy/dev_random/dev_random.h
@@ -0,0 +1,37 @@
+/*
+* /dev/random EntropySource
+* (C) 1999-2009 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_ENTROPY_SRC_DEVICE_H_
+#define BOTAN_ENTROPY_SRC_DEVICE_H_
+
+#include <botan/entropy_src.h>
+#include <vector>
+#include <string>
+
+namespace Botan {
+
+/**
+* Entropy source reading from kernel devices like /dev/random
+*/
+class Device_EntropySource final : public Entropy_Source
+ {
+ public:
+ std::string name() const override { return "dev_random"; }
+
+ size_t poll(RandomNumberGenerator& rng) override;
+
+ explicit Device_EntropySource(const std::vector<std::string>& fsnames);
+
+ ~Device_EntropySource();
+ private:
+ std::vector<int> m_dev_fds;
+ int m_max_fd;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/entropy/dev_random/info.txt b/comm/third_party/botan/src/lib/entropy/dev_random/info.txt
new file mode 100644
index 0000000000..3872411f30
--- /dev/null
+++ b/comm/third_party/botan/src/lib/entropy/dev_random/info.txt
@@ -0,0 +1,11 @@
+<defines>
+ENTROPY_SRC_DEV_RANDOM -> 20131128
+</defines>
+
+<header:internal>
+dev_random.h
+</header:internal>
+
+<os_features>
+dev_random,posix1
+</os_features>
diff --git a/comm/third_party/botan/src/lib/entropy/entropy_src.h b/comm/third_party/botan/src/lib/entropy/entropy_src.h
new file mode 100644
index 0000000000..56e5bd53e5
--- /dev/null
+++ b/comm/third_party/botan/src/lib/entropy/entropy_src.h
@@ -0,0 +1,87 @@
+/*
+* EntropySource
+* (C) 2008,2009,2014,2015,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_ENTROPY_H_
+#define BOTAN_ENTROPY_H_
+
+#include <botan/secmem.h>
+#include <botan/rng.h>
+#include <string>
+#include <chrono>
+#include <memory>
+#include <vector>
+
+namespace Botan {
+
+class RandomNumberGenerator;
+
+/**
+* Abstract interface to a source of entropy
+*/
+class BOTAN_PUBLIC_API(2,0) Entropy_Source
+ {
+ public:
+ /**
+ * Return a new entropy source of a particular type, or null
+ * Each entropy source may require substantial resources (eg, a file handle
+ * or socket instance), so try to share them among multiple RNGs, or just
+ * use the preconfigured global list accessed by Entropy_Sources::global_sources()
+ */
+ static std::unique_ptr<Entropy_Source> create(const std::string& type);
+
+ /**
+ * @return name identifying this entropy source
+ */
+ virtual std::string name() const = 0;
+
+ /**
+ * Perform an entropy gathering poll
+ * @param rng will be provided with entropy via calls to add_entropy
+ * @return conservative estimate of actual entropy added to rng during poll
+ */
+ virtual size_t poll(RandomNumberGenerator& rng) = 0;
+
+ Entropy_Source() = default;
+ Entropy_Source(const Entropy_Source& other) = delete;
+ Entropy_Source(Entropy_Source&& other) = delete;
+ Entropy_Source& operator=(const Entropy_Source& other) = delete;
+
+ virtual ~Entropy_Source() = default;
+ };
+
+class BOTAN_PUBLIC_API(2,0) Entropy_Sources final
+ {
+ public:
+ static Entropy_Sources& global_sources();
+
+ void add_source(std::unique_ptr<Entropy_Source> src);
+
+ std::vector<std::string> enabled_sources() const;
+
+ size_t poll(RandomNumberGenerator& rng,
+ size_t bits,
+ std::chrono::milliseconds timeout);
+
+ /**
+ * Poll just a single named source. Ordinally only used for testing
+ */
+ size_t poll_just(RandomNumberGenerator& rng, const std::string& src);
+
+ Entropy_Sources() = default;
+ explicit Entropy_Sources(const std::vector<std::string>& sources);
+
+ Entropy_Sources(const Entropy_Sources& other) = delete;
+ Entropy_Sources(Entropy_Sources&& other) = delete;
+ Entropy_Sources& operator=(const Entropy_Sources& other) = delete;
+
+ private:
+ std::vector<std::unique_ptr<Entropy_Source>> m_srcs;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/entropy/entropy_srcs.cpp b/comm/third_party/botan/src/lib/entropy/entropy_srcs.cpp
new file mode 100644
index 0000000000..e6573e2963
--- /dev/null
+++ b/comm/third_party/botan/src/lib/entropy/entropy_srcs.cpp
@@ -0,0 +1,235 @@
+/*
+* Entropy Source Polling
+* (C) 2008-2010,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/entropy_src.h>
+#include <botan/rng.h>
+
+#if defined(BOTAN_HAS_SYSTEM_RNG)
+ #include <botan/system_rng.h>
+#endif
+
+#if defined(BOTAN_HAS_PROCESSOR_RNG)
+ #include <botan/processor_rng.h>
+#endif
+
+#if defined(BOTAN_HAS_ENTROPY_SRC_RDRAND)
+ #include <botan/internal/rdrand.h>
+#endif
+
+#if defined(BOTAN_HAS_ENTROPY_SRC_RDSEED)
+ #include <botan/internal/rdseed.h>
+#endif
+
+#if defined(BOTAN_HAS_ENTROPY_SRC_DARN)
+ #include <botan/internal/p9_darn.h>
+#endif
+
+#if defined(BOTAN_HAS_ENTROPY_SRC_DEV_RANDOM)
+ #include <botan/internal/dev_random.h>
+#endif
+
+#if defined(BOTAN_HAS_ENTROPY_SRC_WIN32)
+ #include <botan/internal/es_win32.h>
+#endif
+
+#if defined(BOTAN_HAS_ENTROPY_SRC_PROC_WALKER)
+ #include <botan/internal/proc_walk.h>
+ #include <botan/internal/os_utils.h>
+#endif
+
+#if defined(BOTAN_HAS_ENTROPY_SRC_GETENTROPY)
+ #include <botan/internal/getentropy.h>
+#endif
+
+namespace Botan {
+
+namespace {
+
+#if defined(BOTAN_HAS_SYSTEM_RNG)
+
+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;
+ rng.reseed_from_rng(system_rng(), poll_bits);
+ return poll_bits;
+ }
+
+ std::string name() const override { return "system_rng"; }
+ };
+
+#endif
+
+#if defined(BOTAN_HAS_PROCESSOR_RNG)
+
+class Processor_RNG_EntropySource final : public Entropy_Source
+ {
+ public:
+ size_t poll(RandomNumberGenerator& rng) override
+ {
+ /*
+ * Intel's documentation for RDRAND at
+ * https://software.intel.com/en-us/articles/intel-digital-random-number-generator-drng-software-implementation-guide
+ * claims that software can guarantee a reseed event by polling enough data:
+ * "There is an upper bound of 511 samples per seed in the implementation
+ * where samples are 128 bits in size and can provide two 64-bit random
+ * numbers each."
+ *
+ * By requesting 65536 bits we are asking for 512 samples and thus are assured
+ * that at some point in producing the output, at least one reseed of the
+ * internal state will occur.
+ *
+ * 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.
+ */
+ const size_t poll_bits = 65536;
+ rng.reseed_from_rng(m_hwrng, poll_bits);
+ // Avoid trusting a black box, don't count this as contributing entropy:
+ return 0;
+ }
+
+ std::string name() const override { return m_hwrng.name(); }
+ private:
+ Processor_RNG m_hwrng;
+ };
+
+#endif
+
+}
+
+std::unique_ptr<Entropy_Source> Entropy_Source::create(const std::string& name)
+ {
+#if defined(BOTAN_HAS_SYSTEM_RNG)
+ if(name == "system_rng" || name == "win32_cryptoapi")
+ {
+ return std::unique_ptr<Entropy_Source>(new System_RNG_EntropySource);
+ }
+#endif
+
+#if defined(BOTAN_HAS_PROCESSOR_RNG)
+ if(name == "hwrng" || name == "rdrand" || name == "p9_darn")
+ {
+ if(Processor_RNG::available())
+ {
+ return std::unique_ptr<Entropy_Source>(new Processor_RNG_EntropySource);
+ }
+ }
+#endif
+
+#if defined(BOTAN_HAS_ENTROPY_SRC_RDSEED)
+ if(name == "rdseed")
+ {
+ return std::unique_ptr<Entropy_Source>(new Intel_Rdseed);
+ }
+#endif
+
+#if defined(BOTAN_HAS_ENTROPY_SRC_GETENTROPY)
+ if(name == "getentropy")
+ {
+ return std::unique_ptr<Entropy_Source>(new Getentropy);
+ }
+#endif
+
+#if defined(BOTAN_HAS_ENTROPY_SRC_DEV_RANDOM)
+ if(name == "dev_random")
+ {
+ return std::unique_ptr<Entropy_Source>(new Device_EntropySource(BOTAN_SYSTEM_RNG_POLL_DEVICES));
+ }
+#endif
+
+#if defined(BOTAN_HAS_ENTROPY_SRC_PROC_WALKER)
+ if(name == "proc_walk" && OS::running_in_privileged_state() == false)
+ {
+ const std::string root_dir = BOTAN_ENTROPY_PROC_FS_PATH;
+ if(!root_dir.empty())
+ return std::unique_ptr<Entropy_Source>(new ProcWalking_EntropySource(root_dir));
+ }
+#endif
+
+#if defined(BOTAN_HAS_ENTROPY_SRC_WIN32)
+ if(name == "system_stats")
+ {
+ return std::unique_ptr<Entropy_Source>(new Win32_EntropySource);
+ }
+#endif
+
+ BOTAN_UNUSED(name);
+ return std::unique_ptr<Entropy_Source>();
+ }
+
+void Entropy_Sources::add_source(std::unique_ptr<Entropy_Source> src)
+ {
+ if(src.get())
+ {
+ m_srcs.push_back(std::move(src));
+ }
+ }
+
+std::vector<std::string> Entropy_Sources::enabled_sources() const
+ {
+ std::vector<std::string> sources;
+ for(size_t i = 0; i != m_srcs.size(); ++i)
+ {
+ sources.push_back(m_srcs[i]->name());
+ }
+ return sources;
+ }
+
+size_t Entropy_Sources::poll(RandomNumberGenerator& rng,
+ size_t poll_bits,
+ std::chrono::milliseconds timeout)
+ {
+ typedef std::chrono::system_clock clock;
+
+ auto deadline = clock::now() + timeout;
+
+ size_t bits_collected = 0;
+
+ for(size_t i = 0; i != m_srcs.size(); ++i)
+ {
+ bits_collected += m_srcs[i]->poll(rng);
+
+ if (bits_collected >= poll_bits || clock::now() > deadline)
+ break;
+ }
+
+ return bits_collected;
+ }
+
+size_t Entropy_Sources::poll_just(RandomNumberGenerator& rng, const std::string& the_src)
+ {
+ for(size_t i = 0; i != m_srcs.size(); ++i)
+ {
+ if(m_srcs[i]->name() == the_src)
+ {
+ return m_srcs[i]->poll(rng);
+ }
+ }
+
+ return 0;
+ }
+
+Entropy_Sources::Entropy_Sources(const std::vector<std::string>& sources)
+ {
+ for(auto&& src_name : sources)
+ {
+ add_source(Entropy_Source::create(src_name));
+ }
+ }
+
+Entropy_Sources& Entropy_Sources::global_sources()
+ {
+ static Entropy_Sources global_entropy_sources(BOTAN_ENTROPY_DEFAULT_SOURCES);
+
+ return global_entropy_sources;
+ }
+
+}
+
diff --git a/comm/third_party/botan/src/lib/entropy/getentropy/getentropy.cpp b/comm/third_party/botan/src/lib/entropy/getentropy/getentropy.cpp
new file mode 100644
index 0000000000..7e87367a20
--- /dev/null
+++ b/comm/third_party/botan/src/lib/entropy/getentropy/getentropy.cpp
@@ -0,0 +1,35 @@
+/*
+* System Call getentropy(2)
+* (C) 2017 Alexander Bluhm (genua GmbH)
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/getentropy.h>
+
+#if defined(BOTAN_TARGET_OS_IS_OPENBSD) || defined(BOTAN_TARGET_OS_IS_FREEBSD)
+ #include <unistd.h>
+#else
+ #include <sys/random.h>
+#endif
+
+namespace Botan {
+
+/**
+* Gather 256 bytes entropy from getentropy(2). Note that maximum
+* buffer size is limited to 256 bytes. On OpenBSD this does neither
+* block nor fail.
+*/
+size_t Getentropy::poll(RandomNumberGenerator& rng)
+ {
+ secure_vector<uint8_t> buf(256);
+
+ if(::getentropy(buf.data(), buf.size()) == 0)
+ {
+ rng.add_entropy(buf.data(), buf.size());
+ return buf.size() * 8;
+ }
+
+ return 0;
+ }
+}
diff --git a/comm/third_party/botan/src/lib/entropy/getentropy/getentropy.h b/comm/third_party/botan/src/lib/entropy/getentropy/getentropy.h
new file mode 100644
index 0000000000..26783cf78a
--- /dev/null
+++ b/comm/third_party/botan/src/lib/entropy/getentropy/getentropy.h
@@ -0,0 +1,28 @@
+/*
+* Entropy Source Using OpenBSD getentropy(2) system call
+* (C) 2017 Alexander Bluhm (genua GmbH)
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_ENTROPY_SRC_GETENTROPY_H_
+#define BOTAN_ENTROPY_SRC_GETENTROPY_H_
+
+#include <botan/entropy_src.h>
+
+namespace Botan {
+
+/**
+* Entropy source using the getentropy(2) system call first introduced in
+* OpenBSD 5.6 and added to Solaris 11.3.
+*/
+class Getentropy final : public Entropy_Source
+ {
+ public:
+ std::string name() const override { return "getentropy"; }
+ size_t poll(RandomNumberGenerator& rng) override;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/entropy/getentropy/info.txt b/comm/third_party/botan/src/lib/entropy/getentropy/info.txt
new file mode 100644
index 0000000000..886e57151f
--- /dev/null
+++ b/comm/third_party/botan/src/lib/entropy/getentropy/info.txt
@@ -0,0 +1,11 @@
+<defines>
+ENTROPY_SRC_GETENTROPY -> 20170327
+</defines>
+
+<header:internal>
+getentropy.h
+</header:internal>
+
+<os_features>
+getentropy
+</os_features>
diff --git a/comm/third_party/botan/src/lib/entropy/info.txt b/comm/third_party/botan/src/lib/entropy/info.txt
new file mode 100644
index 0000000000..57f1930b99
--- /dev/null
+++ b/comm/third_party/botan/src/lib/entropy/info.txt
@@ -0,0 +1,7 @@
+<defines>
+ENTROPY_SOURCE -> 20151120
+</defines>
+
+<requires>
+rng
+</requires>
diff --git a/comm/third_party/botan/src/lib/entropy/proc_walk/info.txt b/comm/third_party/botan/src/lib/entropy/proc_walk/info.txt
new file mode 100644
index 0000000000..2bba7e276b
--- /dev/null
+++ b/comm/third_party/botan/src/lib/entropy/proc_walk/info.txt
@@ -0,0 +1,11 @@
+<defines>
+ENTROPY_SRC_PROC_WALKER -> 20131128
+</defines>
+
+<header:internal>
+proc_walk.h
+</header:internal>
+
+<os_features>
+posix1,proc_fs
+</os_features>
diff --git a/comm/third_party/botan/src/lib/entropy/proc_walk/proc_walk.cpp b/comm/third_party/botan/src/lib/entropy/proc_walk/proc_walk.cpp
new file mode 100644
index 0000000000..d780cbf739
--- /dev/null
+++ b/comm/third_party/botan/src/lib/entropy/proc_walk/proc_walk.cpp
@@ -0,0 +1,154 @@
+/*
+* Entropy source based on reading files in /proc on the assumption
+* that a remote attacker will have difficulty guessing some of them.
+*
+* (C) 1999-2008,2012 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/proc_walk.h>
+#include <deque>
+
+#ifndef _POSIX_C_SOURCE
+ #define _POSIX_C_SOURCE 199309
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <fcntl.h>
+
+namespace Botan {
+
+namespace {
+
+class Directory_Walker final : public File_Descriptor_Source
+ {
+ public:
+ explicit Directory_Walker(const std::string& root) :
+ m_cur_dir(std::make_pair<DIR*, std::string>(nullptr, ""))
+ {
+ if(DIR* root_dir = ::opendir(root.c_str()))
+ m_cur_dir = std::make_pair(root_dir, root);
+ }
+
+ ~Directory_Walker()
+ {
+ if(m_cur_dir.first)
+ ::closedir(m_cur_dir.first);
+ }
+
+ int next_fd() override;
+ private:
+ std::pair<struct dirent*, std::string> get_next_dirent();
+
+ std::pair<DIR*, std::string> m_cur_dir;
+ std::deque<std::string> m_dirlist;
+ };
+
+std::pair<struct dirent*, std::string> Directory_Walker::get_next_dirent()
+ {
+ while(m_cur_dir.first)
+ {
+ if(struct dirent* dir = ::readdir(m_cur_dir.first))
+ return std::make_pair(dir, m_cur_dir.second);
+
+ ::closedir(m_cur_dir.first);
+ m_cur_dir = std::make_pair<DIR*, std::string>(nullptr, "");
+
+ while(!m_dirlist.empty() && !m_cur_dir.first)
+ {
+ const std::string next_dir_name = m_dirlist[0];
+ m_dirlist.pop_front();
+
+ if(DIR* next_dir = ::opendir(next_dir_name.c_str()))
+ m_cur_dir = std::make_pair(next_dir, next_dir_name);
+ }
+ }
+
+ return std::make_pair<struct dirent*, std::string>(nullptr, ""); // nothing left
+ }
+
+int Directory_Walker::next_fd()
+ {
+ while(true)
+ {
+ std::pair<struct dirent*, std::string> entry = get_next_dirent();
+
+ if(!entry.first)
+ break; // no more dirs
+
+ const std::string filename = entry.first->d_name;
+
+ if(filename == "." || filename == "..")
+ continue;
+
+ const std::string full_path = entry.second + "/" + filename;
+
+ struct stat stat_buf;
+ if(::lstat(full_path.c_str(), &stat_buf) == -1)
+ continue;
+
+ if(S_ISDIR(stat_buf.st_mode))
+ {
+ m_dirlist.push_back(full_path);
+ }
+ else if(S_ISREG(stat_buf.st_mode) && (stat_buf.st_mode & S_IROTH))
+ {
+ int fd = ::open(full_path.c_str(), O_RDONLY | O_NOCTTY);
+
+ if(fd >= 0)
+ return fd;
+ }
+ }
+
+ return -1;
+ }
+
+}
+
+size_t ProcWalking_EntropySource::poll(RandomNumberGenerator& rng)
+ {
+ const size_t MAX_FILES_READ_PER_POLL = 2048;
+
+ lock_guard_type<mutex_type> lock(m_mutex);
+
+ if(!m_dir)
+ m_dir.reset(new Directory_Walker(m_path));
+
+ m_buf.resize(4096);
+
+ size_t bits = 0;
+
+ for(size_t i = 0; i != MAX_FILES_READ_PER_POLL; ++i)
+ {
+ int fd = m_dir->next_fd();
+
+ // If we've exhaused this walk of the directory, halt the poll
+ if(fd == -1)
+ {
+ m_dir.reset();
+ break;
+ }
+
+ ssize_t got = ::read(fd, m_buf.data(), m_buf.size());
+ ::close(fd);
+
+ if(got > 0)
+ {
+ rng.add_entropy(m_buf.data(), static_cast<size_t>(got));
+
+ // Conservative estimate of 4 bits per file
+ bits += 4;
+ }
+
+ if(bits > 128)
+ break;
+ }
+
+ return bits;
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/entropy/proc_walk/proc_walk.h b/comm/third_party/botan/src/lib/entropy/proc_walk/proc_walk.h
new file mode 100644
index 0000000000..4c5013d29c
--- /dev/null
+++ b/comm/third_party/botan/src/lib/entropy/proc_walk/proc_walk.h
@@ -0,0 +1,45 @@
+/*
+* File Tree Walking EntropySource
+* (C) 1999-2008 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_ENTROPY_SRC_PROC_WALK_H_
+#define BOTAN_ENTROPY_SRC_PROC_WALK_H_
+
+#include <botan/entropy_src.h>
+#include <botan/mutex.h>
+
+namespace Botan {
+
+class File_Descriptor_Source
+ {
+ public:
+ virtual int next_fd() = 0;
+ virtual ~File_Descriptor_Source() = default;
+ };
+
+/**
+* File Tree Walking Entropy Source
+*/
+class ProcWalking_EntropySource final : public Entropy_Source
+ {
+ public:
+ std::string name() const override { return "proc_walk"; }
+
+ size_t poll(RandomNumberGenerator& rng) override;
+
+ explicit ProcWalking_EntropySource(const std::string& root_dir) :
+ m_path(root_dir), m_dir(nullptr) {}
+
+ private:
+ const std::string m_path;
+ mutex_type m_mutex;
+ std::unique_ptr<File_Descriptor_Source> m_dir;
+ secure_vector<uint8_t> m_buf;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/entropy/rdseed/info.txt b/comm/third_party/botan/src/lib/entropy/rdseed/info.txt
new file mode 100644
index 0000000000..ee822ad7c0
--- /dev/null
+++ b/comm/third_party/botan/src/lib/entropy/rdseed/info.txt
@@ -0,0 +1,19 @@
+<defines>
+ENTROPY_SRC_RDSEED -> 20151218
+</defines>
+
+<isa>
+rdseed
+sse2 # for mm_pause see #2139
+</isa>
+
+<header:internal>
+rdseed.h
+</header:internal>
+
+<cc>
+gcc
+clang
+icc
+msvc
+</cc>
diff --git a/comm/third_party/botan/src/lib/entropy/rdseed/rdseed.cpp b/comm/third_party/botan/src/lib/entropy/rdseed/rdseed.cpp
new file mode 100644
index 0000000000..1830edf9de
--- /dev/null
+++ b/comm/third_party/botan/src/lib/entropy/rdseed/rdseed.cpp
@@ -0,0 +1,97 @@
+/*
+* Entropy Source Using Intel's rdseed instruction
+* (C) 2015 Daniel Neus
+* (C) 2015,2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/rdseed.h>
+#include <botan/cpuid.h>
+
+#include <immintrin.h>
+
+namespace Botan {
+
+namespace {
+
+BOTAN_FUNC_ISA("rdseed")
+bool read_rdseed(secure_vector<uint32_t>& 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
+ * occupied in tight RDSEED loops, RDSEED will still usually succeed in under
+ * 150 attempts. The maximum ever seen was 230 attempts until success. When
+ * idle, RDSEED usually succeeds in 1 or 2 attempts.
+ *
+ * We set an upper bound of 512 attempts, because it is possible that due
+ * to firmware issue RDSEED is simply broken and never succeeds. We do not
+ * want to loop forever in that case. If we exceed that limit, then we assume
+ * the hardware is actually just broken, and stop the poll.
+ */
+ const size_t RDSEED_RETRIES = 512;
+
+ for(size_t i = 0; i != RDSEED_RETRIES; ++i)
+ {
+ uint32_t r = 0;
+ int cf = 0;
+
+#if defined(BOTAN_USE_GCC_INLINE_ASM)
+ asm("rdseed %0; adcl $0,%1" :
+ "=r" (r), "=r" (cf) : "0" (r), "1" (cf) : "cc");
+#else
+ cf = _rdseed32_step(&r);
+#endif
+
+ if(1 == cf)
+ {
+ seed.push_back(r);
+ return true;
+ }
+
+ // Intel suggests pausing if RDSEED fails.
+ _mm_pause();
+ }
+
+ return false; // failed to produce an output after many attempts
+ }
+
+}
+
+size_t Intel_Rdseed::poll(RandomNumberGenerator& rng)
+ {
+ const size_t RDSEED_BYTES = 1024;
+ static_assert(RDSEED_BYTES % 4 == 0, "Bad RDSEED configuration");
+
+ if(CPUID::has_rdseed())
+ {
+ secure_vector<uint32_t> seed;
+ seed.reserve(RDSEED_BYTES / 4);
+
+ for(size_t p = 0; p != RDSEED_BYTES / 4; ++p)
+ {
+ /*
+ If at any point we exceed our retry count, we stop the entire seed
+ gathering process. This situation will only occur in situations of
+ extremely high RDSEED utilization. If RDSEED is currently so highly
+ contended, then the rest of the poll is likely to also face contention and
+ it is better to quit now rather than (presumably) face very high retry
+ times for the rest of the poll.
+ */
+ if(!read_rdseed(seed))
+ break;
+ }
+
+ if(seed.size() > 0)
+ {
+ rng.add_entropy(reinterpret_cast<const uint8_t*>(seed.data()),
+ seed.size() * sizeof(uint32_t));
+ }
+ }
+
+ // RDSEED is used but not trusted
+ return 0;
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/entropy/rdseed/rdseed.h b/comm/third_party/botan/src/lib/entropy/rdseed/rdseed.h
new file mode 100644
index 0000000000..da94bc0a10
--- /dev/null
+++ b/comm/third_party/botan/src/lib/entropy/rdseed/rdseed.h
@@ -0,0 +1,28 @@
+/*
+* Entropy Source Using Intel's rdseed instruction
+* (C) 2015 Jack Lloyd, Daniel Neus
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_ENTROPY_SRC_RDSEED_H_
+#define BOTAN_ENTROPY_SRC_RDSEED_H_
+
+#include <botan/entropy_src.h>
+
+namespace Botan {
+
+/**
+* Entropy source using the rdseed instruction first introduced on
+* Intel's Broadwell architecture.
+*/
+class Intel_Rdseed final : public Entropy_Source
+ {
+ public:
+ std::string name() const override { return "rdseed"; }
+ size_t poll(RandomNumberGenerator& rng) override;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/entropy/win32_stats/es_win32.cpp b/comm/third_party/botan/src/lib/entropy/win32_stats/es_win32.cpp
new file mode 100644
index 0000000000..3a175bf19d
--- /dev/null
+++ b/comm/third_party/botan/src/lib/entropy/win32_stats/es_win32.cpp
@@ -0,0 +1,59 @@
+/*
+* (C) 1999-2009,2016,2020 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/es_win32.h>
+
+#define NOMINMAX 1
+#define _WINSOCKAPI_ // stop windows.h including winsock.h
+#include <windows.h>
+
+namespace Botan {
+
+size_t Win32_EntropySource::poll(RandomNumberGenerator& rng)
+ {
+ rng.add_entropy_T(::GetTickCount());
+ rng.add_entropy_T(::GetMessagePos());
+ rng.add_entropy_T(::GetMessageTime());
+ rng.add_entropy_T(::GetInputState());
+
+ rng.add_entropy_T(::GetCurrentProcessId());
+ rng.add_entropy_T(::GetCurrentThreadId());
+
+ SYSTEM_INFO sys_info;
+ ::GetSystemInfo(&sys_info);
+ rng.add_entropy_T(sys_info);
+
+ MEMORYSTATUSEX mem_info;
+ ::GlobalMemoryStatusEx(&mem_info);
+ rng.add_entropy_T(mem_info);
+
+ POINT point;
+ ::GetCursorPos(&point);
+ rng.add_entropy_T(point);
+
+ ::GetCaretPos(&point);
+ rng.add_entropy_T(point);
+
+ /*
+ Potential other sources to investigate
+
+ GetProductInfo
+ GetComputerNameExA
+ GetSystemFirmwareTable
+ GetVersionExA
+ GetProcessorSystemCycleTime
+ GetProcessHandleCount(GetCurrentProcess())
+ GetThreadTimes(GetCurrentThread())
+ QueryThreadCycleTime
+ QueryIdleProcessorCycleTime
+ QueryUnbiasedInterruptTime
+ */
+
+ // We assume all of the above is basically junk
+ return 0;
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/entropy/win32_stats/es_win32.h b/comm/third_party/botan/src/lib/entropy/win32_stats/es_win32.h
new file mode 100644
index 0000000000..2b11ee0801
--- /dev/null
+++ b/comm/third_party/botan/src/lib/entropy/win32_stats/es_win32.h
@@ -0,0 +1,27 @@
+/*
+* Win32 EntropySource
+* (C) 1999-2009 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_ENTROPY_SRC_WIN32_H_
+#define BOTAN_ENTROPY_SRC_WIN32_H_
+
+#include <botan/entropy_src.h>
+
+namespace Botan {
+
+/**
+* Win32 Entropy Source
+*/
+class Win32_EntropySource final : public Entropy_Source
+ {
+ public:
+ std::string name() const override { return "system_stats"; }
+ size_t poll(RandomNumberGenerator& rng) override;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/entropy/win32_stats/info.txt b/comm/third_party/botan/src/lib/entropy/win32_stats/info.txt
new file mode 100644
index 0000000000..1123403b5d
--- /dev/null
+++ b/comm/third_party/botan/src/lib/entropy/win32_stats/info.txt
@@ -0,0 +1,15 @@
+<defines>
+ENTROPY_SRC_WIN32 -> 20200209
+</defines>
+
+<header:internal>
+es_win32.h
+</header:internal>
+
+<os_features>
+win32
+</os_features>
+
+<libs>
+windows -> user32
+</libs>
diff --git a/comm/third_party/botan/src/lib/ffi/ffi.cpp b/comm/third_party/botan/src/lib/ffi/ffi.cpp
new file mode 100644
index 0000000000..9126394ed3
--- /dev/null
+++ b/comm/third_party/botan/src/lib/ffi/ffi.cpp
@@ -0,0 +1,297 @@
+/*
+* (C) 2015,2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/ffi.h>
+#include <botan/internal/ffi_util.h>
+#include <botan/internal/os_utils.h>
+#include <botan/version.h>
+#include <botan/mem_ops.h>
+#include <botan/hex.h>
+#include <botan/base64.h>
+#include <cstdio>
+#include <cstdlib>
+
+namespace Botan_FFI {
+
+int ffi_error_exception_thrown(const char* func_name, const char* exn, int rc)
+ {
+ std::string val;
+ if(Botan::OS::read_env_variable(val, "BOTAN_FFI_PRINT_EXCEPTIONS") == true && val != "")
+ {
+ std::fprintf(stderr, "in %s exception '%s' returning %d\n", func_name, exn, rc);
+ }
+ return rc;
+ }
+
+namespace {
+
+int ffi_map_error_type(Botan::ErrorType err)
+ {
+ switch(err)
+ {
+ case Botan::ErrorType::Unknown:
+ return BOTAN_FFI_ERROR_UNKNOWN_ERROR;
+
+ case Botan::ErrorType::SystemError:
+ case Botan::ErrorType::IoError:
+ case Botan::ErrorType::OpenSSLError:
+ case Botan::ErrorType::Pkcs11Error:
+ case Botan::ErrorType::CommonCryptoError:
+ case Botan::ErrorType::TPMError:
+ case Botan::ErrorType::ZlibError:
+ case Botan::ErrorType::Bzip2Error:
+ case Botan::ErrorType::LzmaError:
+ case Botan::ErrorType::DatabaseError:
+ return BOTAN_FFI_ERROR_SYSTEM_ERROR;
+
+ case Botan::ErrorType::NotImplemented:
+ return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+ case Botan::ErrorType::OutOfMemory:
+ return BOTAN_FFI_ERROR_OUT_OF_MEMORY;
+ case Botan::ErrorType::InternalError:
+ return BOTAN_FFI_ERROR_INTERNAL_ERROR;
+ case Botan::ErrorType::InvalidObjectState:
+ return BOTAN_FFI_ERROR_INVALID_OBJECT_STATE;
+ case Botan::ErrorType::KeyNotSet:
+ return BOTAN_FFI_ERROR_KEY_NOT_SET;
+ case Botan::ErrorType::InvalidArgument:
+ case Botan::ErrorType::InvalidNonceLength:
+ return BOTAN_FFI_ERROR_BAD_PARAMETER;
+
+ case Botan::ErrorType::EncodingFailure:
+ case Botan::ErrorType::DecodingFailure:
+ return BOTAN_FFI_ERROR_INVALID_INPUT;
+
+ case Botan::ErrorType::InvalidTag:
+ return BOTAN_FFI_ERROR_BAD_MAC;
+
+ case Botan::ErrorType::InvalidKeyLength:
+ return BOTAN_FFI_ERROR_INVALID_KEY_LENGTH;
+ case Botan::ErrorType::LookupError:
+ return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+
+ case Botan::ErrorType::HttpError:
+ return BOTAN_FFI_ERROR_HTTP_ERROR;
+ case Botan::ErrorType::TLSError:
+ return BOTAN_FFI_ERROR_TLS_ERROR;
+ case Botan::ErrorType::RoughtimeError:
+ return BOTAN_FFI_ERROR_ROUGHTIME_ERROR;
+ }
+
+ return BOTAN_FFI_ERROR_UNKNOWN_ERROR;
+ }
+
+}
+
+int ffi_guard_thunk(const char* func_name, std::function<int ()> thunk)
+ {
+ 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");
+ }
+
+ return BOTAN_FFI_ERROR_UNKNOWN_ERROR;
+ }
+
+}
+
+extern "C" {
+
+using namespace Botan_FFI;
+
+const char* botan_error_description(int err)
+ {
+ switch(err)
+ {
+ case BOTAN_FFI_SUCCESS:
+ return "OK";
+
+ case BOTAN_FFI_INVALID_VERIFIER:
+ return "Invalid verifier";
+
+ case BOTAN_FFI_ERROR_INVALID_INPUT:
+ return "Invalid input";
+
+ case BOTAN_FFI_ERROR_BAD_MAC:
+ return "Invalid authentication code";
+
+ case BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE:
+ return "Insufficient buffer space";
+
+ case BOTAN_FFI_ERROR_EXCEPTION_THROWN:
+ return "Exception thrown";
+
+ case BOTAN_FFI_ERROR_OUT_OF_MEMORY:
+ return "Out of memory";
+
+ case BOTAN_FFI_ERROR_SYSTEM_ERROR:
+ return "Error while calling system API";
+
+ case BOTAN_FFI_ERROR_INTERNAL_ERROR:
+ return "Internal error";
+
+ case BOTAN_FFI_ERROR_BAD_FLAG:
+ return "Bad flag";
+
+ case BOTAN_FFI_ERROR_NULL_POINTER:
+ return "Null pointer argument";
+
+ case BOTAN_FFI_ERROR_BAD_PARAMETER:
+ return "Bad parameter";
+
+ case BOTAN_FFI_ERROR_KEY_NOT_SET:
+ return "Key not set on object";
+
+ case BOTAN_FFI_ERROR_INVALID_KEY_LENGTH:
+ return "Invalid key length";
+
+ case BOTAN_FFI_ERROR_INVALID_OBJECT_STATE:
+ return "Invalid object state";
+
+ case BOTAN_FFI_ERROR_NOT_IMPLEMENTED:
+ return "Not implemented";
+
+ case BOTAN_FFI_ERROR_INVALID_OBJECT:
+ return "Invalid object handle";
+
+ case BOTAN_FFI_ERROR_TLS_ERROR:
+ return "TLS error";
+
+ case BOTAN_FFI_ERROR_HTTP_ERROR:
+ return "HTTP error";
+
+ case BOTAN_FFI_ERROR_UNKNOWN_ERROR:
+ return "Unknown error";
+ }
+
+ return "Unknown error";
+ }
+
+/*
+* Versioning
+*/
+uint32_t botan_ffi_api_version()
+ {
+ return BOTAN_HAS_FFI;
+ }
+
+int botan_ffi_supports_api(uint32_t api_version)
+ {
+ // This is the API introduced in 2.18
+ if(api_version == 20210220)
+ return BOTAN_FFI_SUCCESS;
+
+ // This is the API introduced in 2.13
+ if(api_version == 20191214)
+ return BOTAN_FFI_SUCCESS;
+
+ // This is the API introduced in 2.8
+ if(api_version == 20180713)
+ return BOTAN_FFI_SUCCESS;
+
+ // This is the API introduced in 2.3
+ if(api_version == 20170815)
+ return BOTAN_FFI_SUCCESS;
+
+ // This is the API introduced in 2.1
+ if(api_version == 20170327)
+ return BOTAN_FFI_SUCCESS;
+
+ // This is the API introduced in 2.0
+ if(api_version == 20150515)
+ return BOTAN_FFI_SUCCESS;
+
+ // Something else:
+ return -1;
+ }
+
+const char* botan_version_string()
+ {
+ return Botan::version_cstr();
+ }
+
+uint32_t botan_version_major() { return Botan::version_major(); }
+uint32_t botan_version_minor() { return Botan::version_minor(); }
+uint32_t botan_version_patch() { return Botan::version_patch(); }
+uint32_t botan_version_datestamp() { return Botan::version_datestamp(); }
+
+int botan_constant_time_compare(const uint8_t* x, const uint8_t* y, size_t len)
+ {
+ return Botan::constant_time_compare(x, y, len) ? 0 : -1;
+ }
+
+int botan_same_mem(const uint8_t* x, const uint8_t* y, size_t len)
+ {
+ return botan_constant_time_compare(x, y, len);
+ }
+
+int botan_scrub_mem(void* mem, size_t bytes)
+ {
+ 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)
+ {
+ return ffi_guard_thunk(__func__, [=]() -> int {
+ const bool uppercase = (flags & BOTAN_FFI_HEX_LOWER_CASE) == 0;
+ Botan::hex_encode(out, in, len, uppercase);
+ return BOTAN_FFI_SUCCESS;
+ });
+ }
+
+int botan_hex_decode(const char* hex_str, size_t in_len, uint8_t* out, size_t* out_len)
+ {
+ return ffi_guard_thunk(__func__, [=]() -> int {
+ const std::vector<uint8_t> bin = Botan::hex_decode(hex_str, in_len);
+ return Botan_FFI::write_vec_output(out, out_len, bin);
+ });
+ }
+
+int botan_base64_encode(const uint8_t* in, size_t len, char* out, size_t* out_len)
+ {
+ 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);
+ });
+ }
+
+int botan_base64_decode(const char* base64_str, size_t in_len,
+ uint8_t* out, size_t* out_len)
+ {
+ 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);
+ return BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE;
+ }
+
+ *out_len = Botan::base64_decode(out, std::string(base64_str, in_len));
+ return BOTAN_FFI_SUCCESS;
+ });
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/ffi/ffi.h b/comm/third_party/botan/src/lib/ffi/ffi.h
new file mode 100644
index 0000000000..ff7fc4b95f
--- /dev/null
+++ b/comm/third_party/botan/src/lib/ffi/ffi.h
@@ -0,0 +1,1778 @@
+/*
+* FFI (C89 API)
+* (C) 2015,2017 Jack Lloyd
+* (C) 2021 René Fischer
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_FFI_H_
+#define BOTAN_FFI_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+This header exports some of botan's functionality via a C89 interface. This API
+is uesd by the Python, OCaml, Rust and Ruby bindings via those languages
+respective ctypes/FFI libraries.
+
+The API is intended to be as easy as possible to call from other
+languages, which often have easy ways to call C, because C. But some C
+code is easier to deal with than others, so to make things easy this
+API follows a few simple rules:
+
+- All interactions are via pointers to opaque structs. No need to worry about
+ structure padding issues and the like.
+
+- All functions return an int error code (except the version calls, which are
+ assumed to always have something to say).
+
+- 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.
+
+ Note this does not apply to all functions, like `botan_hash_final`
+ which is not idempotent and are documented specially. But it's a
+ general theory of operation.
+
+ TODO:
+ - Doxygen comments for all functions/params
+ - TLS
+*/
+
+#include <botan/build.h>
+#include <stdint.h>
+#include <stddef.h>
+
+/**
+* Error codes
+*
+* If you add a new value here be sure to also add it in
+* botan_error_description
+*/
+enum BOTAN_FFI_ERROR {
+ BOTAN_FFI_SUCCESS = 0,
+ BOTAN_FFI_INVALID_VERIFIER = 1,
+
+ BOTAN_FFI_ERROR_INVALID_INPUT = -1,
+ BOTAN_FFI_ERROR_BAD_MAC = -2,
+
+ BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE = -10,
+
+ BOTAN_FFI_ERROR_EXCEPTION_THROWN = -20,
+ BOTAN_FFI_ERROR_OUT_OF_MEMORY = -21,
+ BOTAN_FFI_ERROR_SYSTEM_ERROR = -22,
+ BOTAN_FFI_ERROR_INTERNAL_ERROR = -23,
+
+ BOTAN_FFI_ERROR_BAD_FLAG = -30,
+ BOTAN_FFI_ERROR_NULL_POINTER = -31,
+ BOTAN_FFI_ERROR_BAD_PARAMETER = -32,
+ BOTAN_FFI_ERROR_KEY_NOT_SET = -33,
+ BOTAN_FFI_ERROR_INVALID_KEY_LENGTH = -34,
+ BOTAN_FFI_ERROR_INVALID_OBJECT_STATE = -35,
+
+ BOTAN_FFI_ERROR_NOT_IMPLEMENTED = -40,
+ BOTAN_FFI_ERROR_INVALID_OBJECT = -50,
+
+ BOTAN_FFI_ERROR_TLS_ERROR = -75,
+ BOTAN_FFI_ERROR_HTTP_ERROR = -76,
+ BOTAN_FFI_ERROR_ROUGHTIME_ERROR = -77,
+
+ BOTAN_FFI_ERROR_UNKNOWN_ERROR = -100,
+};
+
+/**
+* Convert an error code into a string. Returns "Unknown error"
+* if the error code is not a known one.
+*/
+BOTAN_PUBLIC_API(2,8) const char* botan_error_description(int err);
+
+/**
+* Return the version of the currently supported FFI API. This is
+* expressed in the form YYYYMMDD of the release date of this version
+* of the API.
+*/
+BOTAN_PUBLIC_API(2,0) uint32_t botan_ffi_api_version(void);
+
+/**
+* Return 0 (ok) if the version given is one this library supports.
+* botan_ffi_supports_api(botan_ffi_api_version()) will always return 0.
+*/
+BOTAN_PUBLIC_API(2,0) int botan_ffi_supports_api(uint32_t api_version);
+
+/**
+* Return a free-form version string, e.g., 2.0.0
+*/
+BOTAN_PUBLIC_API(2,0) const char* botan_version_string(void);
+
+/**
+* Return the major version of the library
+*/
+BOTAN_PUBLIC_API(2,0) uint32_t botan_version_major(void);
+
+/**
+* Return the minor version of the library
+*/
+BOTAN_PUBLIC_API(2,0) uint32_t botan_version_minor(void);
+
+/**
+* Return the patch version of the library
+*/
+BOTAN_PUBLIC_API(2,0) uint32_t botan_version_patch(void);
+
+/**
+* Return the date this version was released as
+* an integer, or 0 if an unreleased version
+*/
+BOTAN_PUBLIC_API(2,0) uint32_t botan_version_datestamp(void);
+
+/**
+* Returns 0 if x[0..len] == y[0..len], or otherwise -1
+*/
+BOTAN_PUBLIC_API(2,3) int botan_constant_time_compare(const uint8_t* x, const uint8_t* y, size_t len);
+
+/**
+* Deprecated equivalent to botan_constant_time_compare
+*/
+BOTAN_PUBLIC_API(2,0) int botan_same_mem(const uint8_t* x, const uint8_t* y, size_t len);
+
+/**
+* Clear out memory using a system specific approach to bypass elision by the
+* compiler (currently using RtlSecureZeroMemory or tricks with volatile pointers).
+*/
+BOTAN_PUBLIC_API(2,2) int botan_scrub_mem(void* mem, size_t bytes);
+
+#define BOTAN_FFI_HEX_LOWER_CASE 1
+
+/**
+* Perform hex encoding
+* @param x is some binary data
+* @param len length of x in bytes
+* @param out an array of at least x*2 bytes
+* @param flags flags out be upper or lower case?
+* @return 0 on success, 1 on failure
+*/
+BOTAN_PUBLIC_API(2,0) int botan_hex_encode(const uint8_t* x, size_t len, char* out, uint32_t flags);
+
+/**
+* Perform hex decoding
+* @param hex_str a string of hex chars (whitespace is ignored)
+* @param in_len the length of hex_str
+* @param out the output buffer should be at least strlen(hex_str)/2 bytes
+* @param out_len the size of out
+*/
+BOTAN_PUBLIC_API(2,3) int botan_hex_decode(const char* hex_str, size_t in_len, uint8_t* out, size_t* out_len);
+
+/**
+* Perform base64 encoding
+*/
+BOTAN_PUBLIC_API(2,3) int botan_base64_encode(const uint8_t* x, size_t len, char* out, size_t* out_len);
+
+
+/**
+* Perform base64 decoding
+*/
+BOTAN_PUBLIC_API(2,3) int botan_base64_decode(const char* base64_str, size_t in_len,
+ uint8_t* out, size_t* out_len);
+
+/**
+* RNG type
+*/
+typedef struct botan_rng_struct* botan_rng_t;
+
+/**
+* Initialize a random number generator object
+* @param rng rng object
+* @param rng_type type of the rng, possible values:
+* "system": system RNG
+* "user": userspace RNG
+* "user-threadsafe": userspace RNG, with internal locking
+* "rdrand": directly read RDRAND
+* Set rng_type to null to let the library choose some default.
+*/
+BOTAN_PUBLIC_API(2,0) int botan_rng_init(botan_rng_t* rng, const char* rng_type);
+
+/**
+* Initialize a custom random number generator from a set of callback functions
+* @param rng rng object
+* @param rng_name name of the rng
+* @param context An application-specific context passed to the callback functions
+* @param get_cb Callback for getting random bytes from the rng, return 0 for success
+* @param add_entry_cb Callback for adding entropy to the rng, return 0 for success, may be NULL
+* @param destroy_cb Callback called when rng is destroyed, may be NULL
+*/
+BOTAN_PUBLIC_API(2,18) int botan_rng_init_custom(botan_rng_t* rng_out, const char* rng_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));
+
+/**
+* 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
+* @return 0 on success, negative on failure
+*/
+BOTAN_PUBLIC_API(2,0) int botan_rng_get(botan_rng_t rng, uint8_t* out, size_t out_len);
+
+/**
+* Reseed a random number generator
+* Uses the System_RNG as a seed generator.
+*
+* @param rng rng object
+* @param bits number of bits to to reseed with
+* @return 0 on success, a negative value on failure
+*/
+BOTAN_PUBLIC_API(2,0) int botan_rng_reseed(botan_rng_t rng, size_t bits);
+
+/**
+* Reseed a random number generator
+*
+* @param rng rng object
+* @param source_rng the rng that will be read from
+* @param bits number of bits to to reseed with
+* @return 0 on success, a negative value on failure
+*/
+BOTAN_PUBLIC_API(2,8) int botan_rng_reseed_from_rng(botan_rng_t rng,
+ botan_rng_t source_rng,
+ size_t bits);
+
+/**
+* Add some seed material to a random number generator
+*
+* @param rng rng object
+* @param entropy the data to add
+* @param entropy_len length of entropy buffer
+* @return 0 on success, a negative value on failure
+*/
+BOTAN_PUBLIC_API(2,8) int botan_rng_add_entropy(botan_rng_t rng,
+ const uint8_t* entropy,
+ size_t entropy_len);
+
+/**
+* Frees all resources of the random number generator object
+* @param rng rng object
+* @return 0 if success, error if invalid object handle
+*/
+BOTAN_PUBLIC_API(2,0) int botan_rng_destroy(botan_rng_t rng);
+
+/*
+* Hash type
+*/
+typedef struct botan_hash_struct* botan_hash_t;
+
+/**
+* Initialize a hash function object
+* @param hash hash object
+* @param hash_name name of the hash function, e.g., "SHA-384"
+* @param flags should be 0 in current API revision, all other uses are reserved
+* and return BOTAN_FFI_ERROR_BAD_FLAG
+*/
+BOTAN_PUBLIC_API(2,0) int botan_hash_init(botan_hash_t* hash, const char* hash_name, uint32_t flags);
+
+/**
+* Copy the state of a hash function object
+* @param dest destination hash object
+* @param source source hash object
+* @return 0 on success, a negative value on failure
+*/
+BOTAN_PUBLIC_API(2,2) int botan_hash_copy_state(botan_hash_t *dest, const botan_hash_t source);
+
+/**
+* Writes the output length of the hash function to *output_length
+* @param hash hash object
+* @param output_length output buffer to hold the hash function output length
+* @return 0 on success, a negative value on failure
+*/
+BOTAN_PUBLIC_API(2,0) int botan_hash_output_length(botan_hash_t hash, size_t* output_length);
+
+/**
+* Writes the block size of the hash function to *block_size
+* @param hash hash object
+* @param block_size output buffer to hold the hash function output length
+* @return 0 on success, a negative value on failure
+*/
+BOTAN_PUBLIC_API(2,2) int botan_hash_block_size(botan_hash_t hash, size_t* block_size);
+
+/**
+* Send more input to the hash function
+* @param hash hash 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_PUBLIC_API(2,0) int botan_hash_update(botan_hash_t hash, const uint8_t* in, size_t in_len);
+
+/**
+* Finalizes the hash computation and writes the output to
+* out[0:botan_hash_output_length()] then reinitializes for computing
+* another digest as if botan_hash_clear had been called.
+* @param hash hash object
+* @param out output buffer
+* @return 0 on success, a negative value on failure
+*/
+BOTAN_PUBLIC_API(2,0) int botan_hash_final(botan_hash_t hash, uint8_t out[]);
+
+/**
+* Reinitializes the state of the hash computation. A hash can
+* be computed (with update/final) immediately.
+* @param hash hash object
+* @return 0 on success, a negative value on failure
+*/
+BOTAN_PUBLIC_API(2,0) int botan_hash_clear(botan_hash_t hash);
+
+/**
+* Frees all resources of the hash object
+* @param hash hash object
+* @return 0 if success, error if invalid object handle
+*/
+BOTAN_PUBLIC_API(2,0) int botan_hash_destroy(botan_hash_t hash);
+
+/**
+* Get the name of this hash function
+* @param hash 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_PUBLIC_API(2,8) int botan_hash_name(botan_hash_t hash, char* name, size_t* name_len);
+
+/*
+* Message Authentication type
+*/
+typedef struct botan_mac_struct* botan_mac_t;
+
+/**
+* Initialize a message authentication code object
+* @param mac mac object
+* @param mac_name name of the hash function, e.g., "HMAC(SHA-384)"
+* @param flags should be 0 in current API revision, all other uses are reserved
+* and return a negative value (error code)
+* @return 0 on success, a negative value on failure
+*/
+BOTAN_PUBLIC_API(2,0) int botan_mac_init(botan_mac_t* mac, const char* mac_name, uint32_t flags);
+
+/**
+* Writes the output length of the message authentication code to *output_length
+* @param mac mac object
+* @param output_length output buffer to hold the MAC output length
+* @return 0 on success, a negative value on failure
+*/
+BOTAN_PUBLIC_API(2,0) int botan_mac_output_length(botan_mac_t mac, size_t* output_length);
+
+/**
+* Sets the key on the MAC
+* @param mac mac object
+* @param key buffer holding the key
+* @param key_len size of the key buffer in bytes
+* @return 0 on success, a negative value on failure
+*/
+BOTAN_PUBLIC_API(2,0) int botan_mac_set_key(botan_mac_t mac, const uint8_t* key, size_t key_len);
+
+/**
+* Send more input to the message authentication code
+* @param mac mac object
+* @param buf input buffer
+* @param len number of bytes to read from the input buffer
+* @return 0 on success, a negative value on failure
+*/
+BOTAN_PUBLIC_API(2,0) int botan_mac_update(botan_mac_t mac, const uint8_t* buf, size_t len);
+
+/**
+* Finalizes the MAC computation and writes the output to
+* out[0:botan_mac_output_length()] then reinitializes for computing
+* another MAC as if botan_mac_clear had been called.
+* @param mac mac object
+* @param out output buffer
+* @return 0 on success, a negative value on failure
+*/
+BOTAN_PUBLIC_API(2,0) int botan_mac_final(botan_mac_t mac, uint8_t out[]);
+
+/**
+* Reinitializes the state of the MAC computation. A MAC can
+* be computed (with update/final) immediately.
+* @param mac mac object
+* @return 0 on success, a negative value on failure
+*/
+BOTAN_PUBLIC_API(2,0) int botan_mac_clear(botan_mac_t mac);
+
+/**
+* Get the name of this MAC
+* @param mac 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_PUBLIC_API(2,8) int botan_mac_name(botan_mac_t mac, char* name, size_t* name_len);
+
+/**
+* Get the key length limits of this auth code
+* @param mac the object to read
+* @param out_minimum_keylength if non-NULL, will be set to minimum keylength of MAC
+* @param out_maximum_keylength if non-NULL, will be set to maximum keylength of MAC
+* @param out_keylength_modulo if non-NULL will be set to byte multiple of valid keys
+*/
+BOTAN_PUBLIC_API(2,8) int botan_mac_get_keyspec(botan_mac_t mac,
+ size_t* out_minimum_keylength,
+ size_t* out_maximum_keylength,
+ size_t* out_keylength_modulo);
+
+/**
+* Frees all resources of the MAC object
+* @param mac mac object
+* @return 0 if success, error if invalid object handle
+*/
+BOTAN_PUBLIC_API(2,0) int botan_mac_destroy(botan_mac_t mac);
+
+/*
+* Cipher modes
+*/
+typedef struct botan_cipher_struct* botan_cipher_t;
+
+#define BOTAN_CIPHER_INIT_FLAG_MASK_DIRECTION 1
+#define BOTAN_CIPHER_INIT_FLAG_ENCRYPT 0
+#define BOTAN_CIPHER_INIT_FLAG_DECRYPT 1
+
+/**
+* Initialize a cipher object
+*/
+BOTAN_PUBLIC_API(2,0) int botan_cipher_init(botan_cipher_t* cipher, const char* name, uint32_t flags);
+
+/**
+* Return the name of the cipher object
+*/
+BOTAN_PUBLIC_API(2,8) int botan_cipher_name(botan_cipher_t cipher, char* name, size_t* name_len);
+
+/**
+* Return the output length of this cipher, for a particular input length.
+*/
+BOTAN_PUBLIC_API(2,8) int botan_cipher_output_length(botan_cipher_t cipher, size_t in_len, size_t* out_len);
+
+/**
+* Return if the specified nonce length is valid for this cipher
+*/
+BOTAN_PUBLIC_API(2,0) int botan_cipher_valid_nonce_length(botan_cipher_t cipher, size_t nl);
+
+/**
+* Get the tag length of the cipher (0 for non-AEAD modes)
+*/
+BOTAN_PUBLIC_API(2,0) int botan_cipher_get_tag_length(botan_cipher_t cipher, size_t* tag_size);
+
+/**
+* Get the default nonce length of this cipher
+*/
+BOTAN_PUBLIC_API(2,0) int botan_cipher_get_default_nonce_length(botan_cipher_t cipher, size_t* nl);
+
+/**
+* Return the update granularity of the cipher; botan_cipher_update must be
+* called with blocks of this size, except for the final.
+*/
+BOTAN_PUBLIC_API(2,0) int botan_cipher_get_update_granularity(botan_cipher_t cipher, size_t* ug);
+
+/**
+* Get information about the key lengths. Prefer botan_cipher_get_keyspec
+*/
+BOTAN_PUBLIC_API(2,0) int botan_cipher_query_keylen(botan_cipher_t,
+ size_t* out_minimum_keylength,
+ size_t* out_maximum_keylength);
+
+/**
+* Get information about the supported key lengths.
+*/
+BOTAN_PUBLIC_API(2,8) int botan_cipher_get_keyspec(botan_cipher_t,
+ size_t* min_keylen,
+ size_t* max_keylen,
+ size_t* mod_keylen);
+
+/**
+* Set the key for this cipher object
+*/
+BOTAN_PUBLIC_API(2,0) int botan_cipher_set_key(botan_cipher_t cipher,
+ const uint8_t* key, size_t key_len);
+
+/**
+* Reset the message specific state for this cipher.
+* Without resetting the keys, this resets the nonce, and any state
+* associated with any message bits that have been processed so far.
+*
+* It is conceptually equivalent to calling botan_cipher_clear followed
+* by botan_cipher_set_key with the original key.
+*/
+BOTAN_PUBLIC_API(2,8) int botan_cipher_reset(botan_cipher_t cipher);
+
+/**
+* Set the associated data. Will fail if cipher is not an AEAD
+*/
+BOTAN_PUBLIC_API(2,0) int botan_cipher_set_associated_data(botan_cipher_t cipher,
+ const uint8_t* ad, size_t ad_len);
+
+/**
+* Begin processing a new message using the provided nonce
+*/
+BOTAN_PUBLIC_API(2,0) int botan_cipher_start(botan_cipher_t cipher,
+ const uint8_t* nonce, size_t nonce_len);
+
+#define BOTAN_CIPHER_UPDATE_FLAG_FINAL (1U << 0)
+
+/**
+* Encrypt some data
+*/
+BOTAN_PUBLIC_API(2,0) int botan_cipher_update(botan_cipher_t cipher,
+ uint32_t flags,
+ uint8_t output[],
+ size_t output_size,
+ size_t* output_written,
+ const uint8_t input_bytes[],
+ size_t input_size,
+ size_t* input_consumed);
+
+/**
+* Reset the key, nonce, AD and all other state on this cipher object
+*/
+BOTAN_PUBLIC_API(2,0) int botan_cipher_clear(botan_cipher_t hash);
+
+/**
+* Destroy the cipher object
+* @return 0 if success, error if invalid object handle
+*/
+BOTAN_PUBLIC_API(2,0) int botan_cipher_destroy(botan_cipher_t cipher);
+
+/*
+* Derive a key from a passphrase for a number of iterations
+* @param pbkdf_algo PBKDF algorithm, e.g., "PBKDF2(SHA-256)"
+* @param out buffer to store the derived key, must be of out_len bytes
+* @param out_len the desired length of the key to produce
+* @param passphrase the password to derive the key from
+* @param salt a randomly chosen salt
+* @param salt_len length of salt in bytes
+* @param iterations the number of iterations to use (use 10K or more)
+* @return 0 on success, a negative value on failure
+*
+* Deprecated: use
+* botan_pwdhash(pbkdf_algo, iterations, 0, 0, out, out_len,
+* passphrase, 0, salt, salt_len);
+*/
+BOTAN_PUBLIC_API(2,0) int
+BOTAN_DEPRECATED("Use botan_pwdhash")
+botan_pbkdf(const char* pbkdf_algo,
+ uint8_t out[], size_t out_len,
+ const char* passphrase,
+ const uint8_t salt[], size_t salt_len,
+ size_t iterations);
+
+/**
+* Derive a key from a passphrase, running until msec time has elapsed.
+* @param pbkdf_algo PBKDF algorithm, e.g., "PBKDF2(SHA-256)"
+* @param out buffer to store the derived key, must be of out_len bytes
+* @param out_len the desired length of the key to produce
+* @param passphrase the password to derive the key from
+* @param salt a randomly chosen salt
+* @param salt_len length of salt in bytes
+* @param milliseconds_to_run if iterations is zero, then instead the PBKDF is
+* run until milliseconds_to_run milliseconds has passed
+* @param out_iterations_used set to the number iterations executed
+* @return 0 on success, a negative value on failure
+*
+* Deprecated: use
+*
+* botan_pwdhash_timed(pbkdf_algo,
+* static_cast<uint32_t>(ms_to_run),
+* iterations_used,
+* nullptr,
+* nullptr,
+* out, out_len,
+* password, 0,
+* salt, salt_len);
+*/
+BOTAN_PUBLIC_API(2,0) int botan_pbkdf_timed(const char* pbkdf_algo,
+ uint8_t out[], size_t out_len,
+ const char* passphrase,
+ const uint8_t salt[], size_t salt_len,
+ size_t milliseconds_to_run,
+ size_t* out_iterations_used);
+
+
+/*
+* Derive a key from a passphrase
+* @param algo PBKDF algorithm, e.g., "PBKDF2(SHA-256)" or "Scrypt"
+* @param param1 the first PBKDF algorithm parameter
+* @param param2 the second PBKDF algorithm parameter (may be zero if unneeded)
+* @param param3 the third PBKDF algorithm parameter (may be zero if unneeded)
+* @param out buffer to store the derived key, must be of out_len bytes
+* @param out_len the desired length of the key to produce
+* @param passphrase the password to derive the key from
+* @param passphrase_len if > 0, specifies length of password. If len == 0, then
+* strlen will be called on passphrase to compute the length.
+* @param salt a randomly chosen salt
+* @param salt_len length of salt in bytes
+* @return 0 on success, a negative value on failure
+*/
+int BOTAN_PUBLIC_API(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);
+
+/*
+* Derive a key from a passphrase
+* @param pbkdf_algo PBKDF algorithm, e.g., "Scrypt" or "PBKDF2(SHA-256)"
+* @param msec the desired runtime in milliseconds
+* @param param1 will be set to the first password hash parameter
+* @param param2 will be set to the second password hash parameter
+* @param param3 will be set to the third password hash parameter
+* @param out buffer to store the derived key, must be of out_len bytes
+* @param out_len the desired length of the key to produce
+* @param passphrase the password to derive the key from
+* @param passphrase_len if > 0, specifies length of password. If len == 0, then
+* strlen will be called on passphrase to compute the length.
+* @param salt a randomly chosen salt
+* @param salt_len length of salt in bytes
+* @return 0 on success, a negative value on failure
+*/
+int BOTAN_PUBLIC_API(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);
+
+/**
+* Derive a key using scrypt
+* Deprecated; use
+* botan_pwdhash("Scrypt", N, r, p, out, out_len, password, 0, salt, salt_len);
+*/
+BOTAN_PUBLIC_API(2,8) int
+BOTAN_DEPRECATED("Use botan_pwdhash")
+botan_scrypt(uint8_t out[], size_t out_len,
+ const char* passphrase,
+ const uint8_t salt[], size_t salt_len,
+ size_t N, size_t r, size_t p);
+
+/**
+* Derive a key
+* @param kdf_algo KDF algorithm, e.g., "SP800-56C"
+* @param out buffer holding the derived key, must be of length out_len
+* @param out_len the desired output length in bytes
+* @param secret the secret input
+* @param secret_len size of secret in bytes
+* @param salt a diversifier
+* @param salt_len size of salt in bytes
+* @param label purpose for the derived keying material
+* @param label_len size of label in bytes
+* @return 0 on success, a negative value on failure
+*/
+BOTAN_PUBLIC_API(2,0) int botan_kdf(const char* kdf_algo,
+ uint8_t out[], size_t out_len,
+ const uint8_t secret[], size_t secret_len,
+ const uint8_t salt[], size_t salt_len,
+ const uint8_t label[], size_t label_len);
+
+/*
+* Raw Block Cipher (PRP) interface
+*/
+typedef struct botan_block_cipher_struct* botan_block_cipher_t;
+
+/**
+* Initialize a block cipher object
+*/
+BOTAN_PUBLIC_API(2,1) int botan_block_cipher_init(botan_block_cipher_t* bc,
+ const char* cipher_name);
+
+/**
+* Destroy a block cipher object
+* @return 0 if success, error if invalid object handle
+*/
+BOTAN_PUBLIC_API(2,1) int botan_block_cipher_destroy(botan_block_cipher_t bc);
+
+/**
+* Reinitializes the block cipher
+* @return 0 on success, a negative value on failure
+*/
+BOTAN_PUBLIC_API(2,1) int botan_block_cipher_clear(botan_block_cipher_t bc);
+
+/**
+* Set the key for a block cipher instance
+*/
+BOTAN_PUBLIC_API(2,1) int botan_block_cipher_set_key(botan_block_cipher_t bc,
+ const uint8_t key[], size_t len);
+
+/**
+* Return the positive block size of this block cipher, or negative to
+* indicate an error
+*/
+BOTAN_PUBLIC_API(2,1) int botan_block_cipher_block_size(botan_block_cipher_t bc);
+
+/**
+* Encrypt one or more blocks with the cipher
+*/
+BOTAN_PUBLIC_API(2,1) int botan_block_cipher_encrypt_blocks(botan_block_cipher_t bc,
+ const uint8_t in[],
+ uint8_t out[],
+ size_t blocks);
+
+/**
+* Decrypt one or more blocks with the cipher
+*/
+BOTAN_PUBLIC_API(2,1) int botan_block_cipher_decrypt_blocks(botan_block_cipher_t bc,
+ const uint8_t in[],
+ uint8_t out[],
+ size_t blocks);
+
+/**
+* Get the name of this block cipher
+* @param cipher 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_PUBLIC_API(2,8) int botan_block_cipher_name(botan_block_cipher_t cipher,
+ char* name, size_t* name_len);
+
+
+/**
+* Get the key length limits of this block cipher
+* @param cipher the object to read
+* @param out_minimum_keylength if non-NULL, will be set to minimum keylength of cipher
+* @param out_maximum_keylength if non-NULL, will be set to maximum keylength of cipher
+* @param out_keylength_modulo if non-NULL will be set to byte multiple of valid keys
+*/
+BOTAN_PUBLIC_API(2,8) int botan_block_cipher_get_keyspec(botan_block_cipher_t cipher,
+ size_t* out_minimum_keylength,
+ size_t* out_maximum_keylength,
+ size_t* out_keylength_modulo);
+
+/*
+* Multiple precision integers (MPI)
+*/
+typedef struct botan_mp_struct* botan_mp_t;
+
+/**
+* Initialize an MPI
+*/
+BOTAN_PUBLIC_API(2,1) int botan_mp_init(botan_mp_t* mp);
+
+/**
+* Destroy (deallocate) an MPI
+* @return 0 if success, error if invalid object handle
+*/
+BOTAN_PUBLIC_API(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
+*/
+BOTAN_PUBLIC_API(2,1) int botan_mp_to_hex(const botan_mp_t mp, char* out);
+
+/**
+* Convert the MPI to a string. Currently base == 10 and base == 16 are supported.
+*/
+BOTAN_PUBLIC_API(2,1) int botan_mp_to_str(const botan_mp_t mp, uint8_t base, char* out, size_t* out_len);
+
+/**
+* Set the MPI to zero
+*/
+BOTAN_PUBLIC_API(2,1) int botan_mp_clear(botan_mp_t mp);
+
+/**
+* Set the MPI value from an int
+*/
+BOTAN_PUBLIC_API(2,1) int botan_mp_set_from_int(botan_mp_t mp, int initial_value);
+
+/**
+* Set the MPI value from another MP object
+*/
+BOTAN_PUBLIC_API(2,1) int botan_mp_set_from_mp(botan_mp_t dest, const botan_mp_t source);
+
+/**
+* Set the MPI value from a string
+*/
+BOTAN_PUBLIC_API(2,1) int botan_mp_set_from_str(botan_mp_t dest, const char* str);
+
+/**
+* Set the MPI value from a string with arbitrary radix.
+* For arbitrary being 10 or 16.
+*/
+BOTAN_PUBLIC_API(2,1) int botan_mp_set_from_radix_str(botan_mp_t dest, const char* str, size_t radix);
+
+/**
+* Return the number of significant bits in the MPI
+*/
+BOTAN_PUBLIC_API(2,1) int botan_mp_num_bits(const botan_mp_t n, size_t* bits);
+
+/**
+* Return the number of significant bytes in the MPI
+*/
+BOTAN_PUBLIC_API(2,1) int botan_mp_num_bytes(const botan_mp_t n, size_t* bytes);
+
+/*
+* Convert the MPI to a big-endian binary string. Writes botan_mp_num_bytes to vec
+*/
+BOTAN_PUBLIC_API(2,1) int botan_mp_to_bin(const botan_mp_t mp, uint8_t vec[]);
+
+/*
+* Set an MP to the big-endian binary value
+*/
+BOTAN_PUBLIC_API(2,1) int botan_mp_from_bin(const botan_mp_t mp, const uint8_t vec[], size_t vec_len);
+
+/*
+* Convert the MPI to a uint32_t, if possible. Fails if MPI is negative or too large.
+*/
+BOTAN_PUBLIC_API(2,1) int botan_mp_to_uint32(const botan_mp_t mp, uint32_t* val);
+
+/**
+* This function should have been named mp_is_non_negative. Returns 1
+* iff mp is greater than *or equal to* zero. Use botan_mp_is_negative
+* to detect negative numbers, botan_mp_is_zero to check for zero.
+*/
+BOTAN_PUBLIC_API(2,1) int botan_mp_is_positive(const botan_mp_t mp);
+
+/**
+* Return 1 iff mp is less than 0
+*/
+BOTAN_PUBLIC_API(2,1) int botan_mp_is_negative(const botan_mp_t mp);
+
+BOTAN_PUBLIC_API(2,1) int botan_mp_flip_sign(botan_mp_t mp);
+
+BOTAN_PUBLIC_API(2,1) int botan_mp_is_zero(const botan_mp_t mp);
+
+BOTAN_PUBLIC_API(2,1) BOTAN_DEPRECATED("Use botan_mp_get_bit(0)")
+int botan_mp_is_odd(const botan_mp_t mp);
+BOTAN_PUBLIC_API(2,1) BOTAN_DEPRECATED("Use botan_mp_get_bit(0)")
+int botan_mp_is_even(const botan_mp_t mp);
+
+BOTAN_PUBLIC_API(2,8) int botan_mp_add_u32(botan_mp_t result, const botan_mp_t x, uint32_t y);
+BOTAN_PUBLIC_API(2,8) int botan_mp_sub_u32(botan_mp_t result, const botan_mp_t x, uint32_t y);
+
+BOTAN_PUBLIC_API(2,1) int botan_mp_add(botan_mp_t result, const botan_mp_t x, const botan_mp_t y);
+BOTAN_PUBLIC_API(2,1) int botan_mp_sub(botan_mp_t result, const botan_mp_t x, const botan_mp_t y);
+BOTAN_PUBLIC_API(2,1) int botan_mp_mul(botan_mp_t result, const botan_mp_t x, const botan_mp_t y);
+
+BOTAN_PUBLIC_API(2,1) int botan_mp_div(botan_mp_t quotient,
+ botan_mp_t remainder,
+ const botan_mp_t x, const botan_mp_t y);
+
+BOTAN_PUBLIC_API(2,1) int botan_mp_mod_mul(botan_mp_t result, const botan_mp_t x,
+ const botan_mp_t y, const botan_mp_t mod);
+
+/*
+* Returns 0 if x != y
+* Returns 1 if x == y
+* Returns negative number on error
+*/
+BOTAN_PUBLIC_API(2,1) int botan_mp_equal(const botan_mp_t x, const botan_mp_t y);
+
+/*
+* Sets *result to comparison result:
+* -1 if x < y, 0 if x == y, 1 if x > y
+* Returns negative number on error or zero on success
+*/
+BOTAN_PUBLIC_API(2,1) int botan_mp_cmp(int* result, const botan_mp_t x, const botan_mp_t y);
+
+/*
+* Swap two botan_mp_t
+*/
+BOTAN_PUBLIC_API(2,1) int botan_mp_swap(botan_mp_t x, botan_mp_t y);
+
+/* Return (base^exponent) % modulus */
+BOTAN_PUBLIC_API(2,1) int botan_mp_powmod(botan_mp_t out, const botan_mp_t base, const botan_mp_t exponent, const botan_mp_t modulus);
+
+BOTAN_PUBLIC_API(2,1) int botan_mp_lshift(botan_mp_t out, const botan_mp_t in, size_t shift);
+BOTAN_PUBLIC_API(2,1) int botan_mp_rshift(botan_mp_t out, const botan_mp_t in, size_t shift);
+
+BOTAN_PUBLIC_API(2,1) int botan_mp_mod_inverse(botan_mp_t out, const botan_mp_t in, const botan_mp_t modulus);
+
+BOTAN_PUBLIC_API(2,1) int botan_mp_rand_bits(botan_mp_t rand_out, botan_rng_t rng, size_t bits);
+
+BOTAN_PUBLIC_API(2,1) int botan_mp_rand_range(botan_mp_t rand_out, botan_rng_t rng,
+ const botan_mp_t lower_bound, const botan_mp_t upper_bound);
+
+BOTAN_PUBLIC_API(2,1) int botan_mp_gcd(botan_mp_t out, const botan_mp_t x, const botan_mp_t y);
+
+/**
+* Returns 0 if n is not prime
+* Returns 1 if n is prime
+* Returns negative number on error
+*/
+BOTAN_PUBLIC_API(2,1) int botan_mp_is_prime(const botan_mp_t n, botan_rng_t rng, size_t test_prob);
+
+/**
+* Returns 0 if specified bit of n is not set
+* Returns 1 if specified bit of n is set
+* Returns negative number on error
+*/
+BOTAN_PUBLIC_API(2,1) int botan_mp_get_bit(const botan_mp_t n, size_t bit);
+
+/**
+* Set the specified bit
+*/
+BOTAN_PUBLIC_API(2,1) int botan_mp_set_bit(botan_mp_t n, size_t bit);
+
+/**
+* Clear the specified bit
+*/
+BOTAN_PUBLIC_API(2,1) int botan_mp_clear_bit(botan_mp_t n, size_t bit);
+
+/* Bcrypt password hashing */
+
+/**
+* Create a password hash using Bcrypt
+* @param out buffer holding the password hash, should be of length 64 bytes
+* @param out_len the desired output length in bytes
+* @param password the password
+* @param rng a random number generator
+* @param work_factor how much work to do to slow down guessing attacks
+* @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$...
+*/
+BOTAN_PUBLIC_API(2,0) int botan_bcrypt_generate(uint8_t* out, size_t* out_len,
+ const char* password,
+ botan_rng_t rng,
+ size_t work_factor,
+ uint32_t flags);
+
+/**
+* Check a previously created password hash
+* @param pass the password to check against
+* @param hash the stored hash to check against
+* @return 0 if if this password/hash combination is valid,
+* 1 if the combination is not valid (but otherwise well formed),
+* negative on error
+*/
+BOTAN_PUBLIC_API(2,0) int botan_bcrypt_is_valid(const char* pass, const char* hash);
+
+/*
+* Public/private key creation, import, ...
+*/
+typedef struct botan_privkey_struct* botan_privkey_t;
+
+/**
+* Create a new private key
+* @param key the new object will be placed here
+* @param algo_name something like "RSA" or "ECDSA"
+* @param algo_params is specific to the algorithm. For RSA, specifies
+* the modulus bit length. For ECC is the name of the curve.
+* @param rng a random number generator
+*/
+BOTAN_PUBLIC_API(2,0) int botan_privkey_create(botan_privkey_t* key,
+ const char* algo_name,
+ const char* algo_params,
+ botan_rng_t rng);
+
+#define BOTAN_CHECK_KEY_EXPENSIVE_TESTS 1
+
+BOTAN_PUBLIC_API(2,0) int botan_privkey_check_key(botan_privkey_t key, botan_rng_t rng, uint32_t flags);
+
+BOTAN_PUBLIC_API(2,0) BOTAN_DEPRECATED("Use botan_privkey_create")
+int botan_privkey_create_rsa(botan_privkey_t* key, botan_rng_t rng, size_t n_bits);
+BOTAN_PUBLIC_API(2,0) BOTAN_DEPRECATED("Use botan_privkey_create")
+int botan_privkey_create_ecdsa(botan_privkey_t* key, botan_rng_t rng, const char* params);
+BOTAN_PUBLIC_API(2,0) BOTAN_DEPRECATED("Use botan_privkey_create")
+int botan_privkey_create_ecdh(botan_privkey_t* key, botan_rng_t rng, const char* params);
+BOTAN_PUBLIC_API(2,0) BOTAN_DEPRECATED("Use botan_privkey_create")
+int botan_privkey_create_mceliece(botan_privkey_t* key, botan_rng_t rng, size_t n, size_t t);
+BOTAN_PUBLIC_API(2,0) BOTAN_DEPRECATED("Use botan_privkey_create")
+int botan_privkey_create_dh(botan_privkey_t* key, botan_rng_t rng, const char* param);
+
+/**
+ * Generates DSA key pair. Gives to a caller control over key length
+ * and order of a subgroup 'q'.
+ *
+ * @param key handler to the resulting key
+ * @param rng initialized PRNG
+ * @param pbits length of the key in bits. Must be between in range (1024, 3072)
+ * and multiple of 64. Bit size of the prime 'p'
+ * @param qbits order of the subgroup. Must be in range (160, 256) and multiple
+ * of 8
+ *
+ * @returns BOTAN_FFI_SUCCESS Success, `key' initialized with DSA key
+ * @returns BOTAN_FFI_ERROR_NULL_POINTER either `key' or `rng' is NULL
+ * @returns BOTAN_FFI_ERROR_BAD_PARAMETER unexpected value for either `pbits' or
+ * `qbits'
+ * @returns BOTAN_FFI_ERROR_NOT_IMPLEMENTED functionality not implemented
+ *
+*/
+BOTAN_PUBLIC_API(2,5) int botan_privkey_create_dsa(
+ botan_privkey_t* key,
+ botan_rng_t rng,
+ size_t pbits,
+ size_t qbits);
+
+/**
+ * Generates ElGamal key pair. Caller has a control over key length
+ * and order of a subgroup 'q'. Function is able to use two types of
+ * primes:
+ * * if pbits-1 == qbits then safe primes are used for key generation
+ * * otherwise generation uses group of prime order
+ *
+ * @param key handler to the resulting key
+ * @param rng initialized PRNG
+ * @param pbits length of the key in bits. Must be at least 1024
+ * @param qbits order of the subgroup. Must be at least 160
+ *
+ * @returns BOTAN_FFI_SUCCESS Success, `key' initialized with DSA key
+ * @returns BOTAN_FFI_ERROR_NULL_POINTER either `key' or `rng' is NULL
+ * @returns BOTAN_FFI_ERROR_BAD_PARAMETER unexpected value for either `pbits' or
+ * `qbits'
+ * @returns BOTAN_FFI_ERROR_NOT_IMPLEMENTED functionality not implemented
+ *
+*/
+BOTAN_PUBLIC_API(2,5) int botan_privkey_create_elgamal(
+ botan_privkey_t* key,
+ botan_rng_t rng,
+ size_t pbits,
+ size_t qbits);
+
+/**
+* Input currently assumed to be PKCS #8 structure;
+* Set password to NULL to indicate no encryption expected
+* Starting in 2.8.0, the rng parameter is unused and may be set to null
+*/
+BOTAN_PUBLIC_API(2,0) int botan_privkey_load(botan_privkey_t* key,
+ botan_rng_t rng,
+ const uint8_t bits[], size_t len,
+ const char* password);
+
+/**
+* @return 0 if success, error if invalid object handle
+*/
+BOTAN_PUBLIC_API(2,0) int botan_privkey_destroy(botan_privkey_t key);
+
+#define BOTAN_PRIVKEY_EXPORT_FLAG_DER 0
+#define BOTAN_PRIVKEY_EXPORT_FLAG_PEM 1
+
+/**
+* On input *out_len is number of bytes in out[]
+* On output *out_len is number of bytes written (or required)
+* If out is not big enough no output is written, *out_len is set and 1 is returned
+* Returns 0 on success and sets
+* If some other error occurs a negative integer is returned.
+*/
+BOTAN_PUBLIC_API(2,0) int botan_privkey_export(botan_privkey_t key,
+ uint8_t out[], size_t* out_len,
+ uint32_t flags);
+
+BOTAN_PUBLIC_API(2,8) int botan_privkey_algo_name(botan_privkey_t key, char out[], size_t* out_len);
+
+/**
+* Set encryption_algo to NULL or "" to have the library choose a default (recommended)
+*/
+BOTAN_DEPRECATED("Use botan_privkey_export_encrypted_pbkdf_{msec,iter}")
+BOTAN_PUBLIC_API(2,0) int botan_privkey_export_encrypted(botan_privkey_t key,
+ uint8_t out[], size_t* out_len,
+ botan_rng_t rng,
+ const char* passphrase,
+ const char* encryption_algo,
+ uint32_t flags);
+
+/*
+* Export a private key, running PBKDF for specified amount of time
+* @param key the private key to export
+*/
+BOTAN_PUBLIC_API(2,0) 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,
+ uint32_t pbkdf_msec_runtime,
+ size_t* pbkdf_iterations_out,
+ const char* cipher_algo,
+ const char* pbkdf_algo,
+ uint32_t flags);
+
+/**
+* Export a private key using the specified number of iterations.
+*/
+BOTAN_PUBLIC_API(2,0) 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,
+ size_t pbkdf_iterations,
+ const char* cipher_algo,
+ const char* pbkdf_algo,
+ uint32_t flags);
+
+typedef struct botan_pubkey_struct* botan_pubkey_t;
+
+BOTAN_PUBLIC_API(2,0) int botan_pubkey_load(botan_pubkey_t* key, const uint8_t bits[], size_t len);
+
+BOTAN_PUBLIC_API(2,0) int botan_privkey_export_pubkey(botan_pubkey_t* out, botan_privkey_t in);
+
+BOTAN_PUBLIC_API(2,0) int botan_pubkey_export(botan_pubkey_t key, uint8_t out[], size_t* out_len, uint32_t flags);
+
+BOTAN_PUBLIC_API(2,0) int botan_pubkey_algo_name(botan_pubkey_t key, char out[], size_t* out_len);
+
+/**
+* Returns 0 if key is valid, negative if invalid key or some other error
+*/
+BOTAN_PUBLIC_API(2,0) int botan_pubkey_check_key(botan_pubkey_t key, botan_rng_t rng, uint32_t flags);
+
+BOTAN_PUBLIC_API(2,0) int botan_pubkey_estimated_strength(botan_pubkey_t key, size_t* estimate);
+
+BOTAN_PUBLIC_API(2,0) int botan_pubkey_fingerprint(botan_pubkey_t key, const char* hash,
+ uint8_t out[], size_t* out_len);
+
+/**
+* @return 0 if success, error if invalid object handle
+*/
+BOTAN_PUBLIC_API(2,0) int botan_pubkey_destroy(botan_pubkey_t key);
+
+/*
+* Get arbitrary named fields from public or privat keys
+*/
+BOTAN_PUBLIC_API(2,0) int botan_pubkey_get_field(botan_mp_t output,
+ botan_pubkey_t key,
+ const char* field_name);
+
+BOTAN_PUBLIC_API(2,0) int botan_privkey_get_field(botan_mp_t output,
+ botan_privkey_t key,
+ const char* field_name);
+
+/*
+* Algorithm specific key operations: RSA
+*/
+BOTAN_PUBLIC_API(2,0) int botan_privkey_load_rsa(botan_privkey_t* key,
+ botan_mp_t p,
+ botan_mp_t q,
+ botan_mp_t e);
+
+BOTAN_PUBLIC_API(2,8) int botan_privkey_load_rsa_pkcs1(botan_privkey_t* key,
+ const uint8_t bits[],
+ size_t len);
+
+BOTAN_PUBLIC_API(2,0) BOTAN_DEPRECATED("Use botan_privkey_get_field")
+int botan_privkey_rsa_get_p(botan_mp_t p, botan_privkey_t rsa_key);
+BOTAN_PUBLIC_API(2,0) BOTAN_DEPRECATED("Use botan_privkey_get_field")
+int botan_privkey_rsa_get_q(botan_mp_t q, botan_privkey_t rsa_key);
+BOTAN_PUBLIC_API(2,0) BOTAN_DEPRECATED("Use botan_privkey_get_field")
+int botan_privkey_rsa_get_d(botan_mp_t d, botan_privkey_t rsa_key);
+BOTAN_PUBLIC_API(2,0) BOTAN_DEPRECATED("Use botan_privkey_get_field")
+int botan_privkey_rsa_get_n(botan_mp_t n, botan_privkey_t rsa_key);
+BOTAN_PUBLIC_API(2,0) BOTAN_DEPRECATED("Use botan_privkey_get_field")
+int botan_privkey_rsa_get_e(botan_mp_t e, botan_privkey_t rsa_key);
+
+BOTAN_PUBLIC_API(2,8) int botan_privkey_rsa_get_privkey(botan_privkey_t rsa_key,
+ uint8_t out[], size_t* out_len,
+ uint32_t flags);
+
+BOTAN_PUBLIC_API(2,0) int botan_pubkey_load_rsa(botan_pubkey_t* key,
+ botan_mp_t n,
+ botan_mp_t e);
+
+BOTAN_PUBLIC_API(2,0) BOTAN_DEPRECATED("Use botan_pubkey_get_field")
+int botan_pubkey_rsa_get_e(botan_mp_t e, botan_pubkey_t rsa_key);
+BOTAN_PUBLIC_API(2,0) BOTAN_DEPRECATED("Use botan_pubkey_get_field")
+int botan_pubkey_rsa_get_n(botan_mp_t n, botan_pubkey_t rsa_key);
+
+/*
+* Algorithm specific key operations: DSA
+*/
+BOTAN_PUBLIC_API(2,0) 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);
+
+BOTAN_PUBLIC_API(2,0) 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);
+
+BOTAN_PUBLIC_API(2,0) BOTAN_DEPRECATED("Use botan_privkey_get_field")
+int botan_privkey_dsa_get_x(botan_mp_t n, botan_privkey_t key);
+
+BOTAN_PUBLIC_API(2,0) BOTAN_DEPRECATED("Use botan_pubkey_get_field")
+int botan_pubkey_dsa_get_p(botan_mp_t p, botan_pubkey_t key);
+BOTAN_PUBLIC_API(2,0) BOTAN_DEPRECATED("Use botan_pubkey_get_field")
+int botan_pubkey_dsa_get_q(botan_mp_t q, botan_pubkey_t key);
+BOTAN_PUBLIC_API(2,0) BOTAN_DEPRECATED("Use botan_pubkey_get_field")
+int botan_pubkey_dsa_get_g(botan_mp_t d, botan_pubkey_t key);
+BOTAN_PUBLIC_API(2,0) BOTAN_DEPRECATED("Use botan_pubkey_get_field")
+int botan_pubkey_dsa_get_y(botan_mp_t y, botan_pubkey_t key);
+
+/*
+* Loads Diffie Hellman private key
+*
+* @param key variable populated with key material
+* @param p prime order of a Z_p group
+* @param g group generator
+* @param x private key
+*
+* @pre key is NULL on input
+* @post function allocates memory and assigns to `key'
+*
+* @return 0 on success, a negative value on failure
+*/
+BOTAN_PUBLIC_API(2,0) int botan_privkey_load_dh(botan_privkey_t* key,
+ botan_mp_t p,
+ botan_mp_t g,
+ botan_mp_t x);
+/**
+* Loads Diffie Hellman public key
+*
+* @param key variable populated with key material
+* @param p prime order of a Z_p group
+* @param g group generator
+* @param y public key
+*
+* @pre key is NULL on input
+* @post function allocates memory and assigns to `key'
+*
+* @return 0 on success, a negative value on failure
+*/
+BOTAN_PUBLIC_API(2,0) int botan_pubkey_load_dh(botan_pubkey_t* key,
+ botan_mp_t p,
+ botan_mp_t g,
+ botan_mp_t y);
+
+/*
+* Algorithm specific key operations: ElGamal
+*/
+
+/**
+* Loads ElGamal public key
+* @param key variable populated with key material
+* @param p prime order of a Z_p group
+* @param g group generator
+* @param y public key
+*
+* @pre key is NULL on input
+* @post function allocates memory and assigns to `key'
+*
+* @return 0 on success, a negative value on failure
+*/
+BOTAN_PUBLIC_API(2,0) int botan_pubkey_load_elgamal(botan_pubkey_t* key,
+ botan_mp_t p,
+ botan_mp_t g,
+ botan_mp_t y);
+
+/**
+* Loads ElGamal private key
+*
+* @param key variable populated with key material
+* @param p prime order of a Z_p group
+* @param g group generator
+* @param x private key
+*
+* @pre key is NULL on input
+* @post function allocates memory and assigns to `key'
+*
+* @return 0 on success, a negative value on failure
+*/
+BOTAN_PUBLIC_API(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: Ed25519
+*/
+
+BOTAN_PUBLIC_API(2,2) int botan_privkey_load_ed25519(botan_privkey_t* key,
+ const uint8_t privkey[32]);
+
+BOTAN_PUBLIC_API(2,2) int botan_pubkey_load_ed25519(botan_pubkey_t* key,
+ const uint8_t pubkey[32]);
+
+BOTAN_PUBLIC_API(2,2) int botan_privkey_ed25519_get_privkey(botan_privkey_t key,
+ uint8_t output[64]);
+
+BOTAN_PUBLIC_API(2,2) int botan_pubkey_ed25519_get_pubkey(botan_pubkey_t key,
+ uint8_t pubkey[32]);
+
+/*
+* Algorithm specific key operations: X25519
+*/
+
+BOTAN_PUBLIC_API(2,8) int botan_privkey_load_x25519(botan_privkey_t* key,
+ const uint8_t privkey[32]);
+
+BOTAN_PUBLIC_API(2,8) int botan_pubkey_load_x25519(botan_pubkey_t* key,
+ const uint8_t pubkey[32]);
+
+BOTAN_PUBLIC_API(2,8) int botan_privkey_x25519_get_privkey(botan_privkey_t key,
+ uint8_t output[32]);
+
+BOTAN_PUBLIC_API(2,8) int botan_pubkey_x25519_get_pubkey(botan_pubkey_t key,
+ uint8_t pubkey[32]);
+
+/*
+* Algorithm specific key operations: ECDSA and ECDH
+*/
+BOTAN_PUBLIC_API(2,2)
+int botan_privkey_load_ecdsa(botan_privkey_t* key,
+ const botan_mp_t scalar,
+ const char* curve_name);
+
+BOTAN_PUBLIC_API(2,2)
+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);
+
+BOTAN_PUBLIC_API(2,2)
+int botan_pubkey_load_ecdh(botan_pubkey_t* key,
+ const botan_mp_t public_x,
+ const botan_mp_t public_y,
+ const char* curve_name);
+
+BOTAN_PUBLIC_API(2,2)
+int botan_privkey_load_ecdh(botan_privkey_t* key,
+ const botan_mp_t scalar,
+ const char* curve_name);
+
+BOTAN_PUBLIC_API(2,2)
+int botan_pubkey_load_sm2(botan_pubkey_t* key,
+ const botan_mp_t public_x,
+ const botan_mp_t public_y,
+ const char* curve_name);
+
+BOTAN_PUBLIC_API(2,2)
+int botan_privkey_load_sm2(botan_privkey_t* key,
+ const botan_mp_t scalar,
+ const char* curve_name);
+
+BOTAN_PUBLIC_API(2,2) BOTAN_DEPRECATED("Use botan_pubkey_load_sm2")
+int botan_pubkey_load_sm2_enc(botan_pubkey_t* key,
+ const botan_mp_t public_x,
+ const botan_mp_t public_y,
+ const char* curve_name);
+
+BOTAN_PUBLIC_API(2,2) BOTAN_DEPRECATED("Use botan_privkey_load_sm2")
+int botan_privkey_load_sm2_enc(botan_privkey_t* key,
+ const botan_mp_t scalar,
+ const char* curve_name);
+
+BOTAN_PUBLIC_API(2,3)
+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);
+
+/*
+* Public Key Encryption
+*/
+typedef struct botan_pk_op_encrypt_struct* botan_pk_op_encrypt_t;
+
+BOTAN_PUBLIC_API(2,0) int botan_pk_op_encrypt_create(botan_pk_op_encrypt_t* op,
+ botan_pubkey_t key,
+ const char* padding,
+ uint32_t flags);
+
+/**
+* @return 0 if success, error if invalid object handle
+*/
+BOTAN_PUBLIC_API(2,0) int botan_pk_op_encrypt_destroy(botan_pk_op_encrypt_t op);
+
+BOTAN_PUBLIC_API(2,8) int botan_pk_op_encrypt_output_length(botan_pk_op_encrypt_t op,
+ size_t ptext_len,
+ size_t* ctext_len);
+
+BOTAN_PUBLIC_API(2,0) int botan_pk_op_encrypt(botan_pk_op_encrypt_t op,
+ botan_rng_t rng,
+ uint8_t out[],
+ size_t* out_len,
+ const uint8_t plaintext[],
+ size_t plaintext_len);
+
+/*
+* Public Key Decryption
+*/
+typedef struct botan_pk_op_decrypt_struct* botan_pk_op_decrypt_t;
+
+BOTAN_PUBLIC_API(2,0) int botan_pk_op_decrypt_create(botan_pk_op_decrypt_t* op,
+ botan_privkey_t key,
+ const char* padding,
+ uint32_t flags);
+
+/**
+* @return 0 if success, error if invalid object handle
+*/
+BOTAN_PUBLIC_API(2,0) int botan_pk_op_decrypt_destroy(botan_pk_op_decrypt_t op);
+
+BOTAN_PUBLIC_API(2,8) int botan_pk_op_decrypt_output_length(botan_pk_op_decrypt_t op,
+ size_t ctext_len,
+ size_t* ptext_len);
+
+BOTAN_PUBLIC_API(2,0) int botan_pk_op_decrypt(botan_pk_op_decrypt_t op,
+ uint8_t out[], size_t* out_len,
+ const uint8_t ciphertext[], size_t ciphertext_len);
+
+/*
+* Signature Generation
+*/
+
+#define BOTAN_PUBKEY_DER_FORMAT_SIGNATURE 1
+
+typedef struct botan_pk_op_sign_struct* botan_pk_op_sign_t;
+
+BOTAN_PUBLIC_API(2,0)
+int botan_pk_op_sign_create(botan_pk_op_sign_t* op,
+ botan_privkey_t key,
+ const char* hash_and_padding,
+ uint32_t flags);
+
+/**
+* @return 0 if success, error if invalid object handle
+*/
+BOTAN_PUBLIC_API(2,0) int botan_pk_op_sign_destroy(botan_pk_op_sign_t op);
+
+BOTAN_PUBLIC_API(2,8) int botan_pk_op_sign_output_length(botan_pk_op_sign_t op, size_t* olen);
+
+BOTAN_PUBLIC_API(2,0) int botan_pk_op_sign_update(botan_pk_op_sign_t op, const uint8_t in[], size_t in_len);
+
+BOTAN_PUBLIC_API(2,0)
+int botan_pk_op_sign_finish(botan_pk_op_sign_t op, botan_rng_t rng,
+ uint8_t sig[], size_t* sig_len);
+
+/*
+* Signature Verification
+*/
+typedef struct botan_pk_op_verify_struct* botan_pk_op_verify_t;
+
+BOTAN_PUBLIC_API(2,0)
+int botan_pk_op_verify_create(botan_pk_op_verify_t* op,
+ botan_pubkey_t key,
+ const char* hash_and_padding,
+ uint32_t flags);
+
+/**
+* @return 0 if success, error if invalid object handle
+*/
+BOTAN_PUBLIC_API(2,0) int botan_pk_op_verify_destroy(botan_pk_op_verify_t op);
+
+BOTAN_PUBLIC_API(2,0) int botan_pk_op_verify_update(botan_pk_op_verify_t op, const uint8_t in[], size_t in_len);
+BOTAN_PUBLIC_API(2,0) int botan_pk_op_verify_finish(botan_pk_op_verify_t op, const uint8_t sig[], size_t sig_len);
+
+/*
+* Key Agreement
+*/
+typedef struct botan_pk_op_ka_struct* botan_pk_op_ka_t;
+
+BOTAN_PUBLIC_API(2,0)
+int botan_pk_op_key_agreement_create(botan_pk_op_ka_t* op,
+ botan_privkey_t key,
+ const char* kdf,
+ uint32_t flags);
+
+/**
+* @return 0 if success, error if invalid object handle
+*/
+BOTAN_PUBLIC_API(2,0) int botan_pk_op_key_agreement_destroy(botan_pk_op_ka_t op);
+
+BOTAN_PUBLIC_API(2,0) int botan_pk_op_key_agreement_export_public(botan_privkey_t key,
+ uint8_t out[], size_t* out_len);
+
+BOTAN_PUBLIC_API(2,8) int botan_pk_op_key_agreement_size(botan_pk_op_ka_t op, size_t* out_len);
+
+BOTAN_PUBLIC_API(2,0)
+int botan_pk_op_key_agreement(botan_pk_op_ka_t op,
+ uint8_t out[], size_t* out_len,
+ const uint8_t other_key[], size_t other_key_len,
+ const uint8_t salt[], size_t salt_len);
+
+BOTAN_PUBLIC_API(2,0) int botan_pkcs_hash_id(const char* hash_name, uint8_t pkcs_id[], size_t* pkcs_id_len);
+
+
+/*
+*
+* @param mce_key must be a McEliece key
+* ct_len should be pt_len + n/8 + a few?
+*/
+BOTAN_PUBLIC_API(2,0) BOTAN_DEPRECATED("Poorly specified, avoid in new code")
+int botan_mceies_encrypt(botan_pubkey_t mce_key,
+ botan_rng_t rng,
+ const char* aead,
+ const uint8_t pt[], size_t pt_len,
+ const uint8_t ad[], size_t ad_len,
+ uint8_t ct[], size_t* ct_len);
+
+BOTAN_PUBLIC_API(2,0) BOTAN_DEPRECATED("Poorly specified, avoid in new code")
+int botan_mceies_decrypt(botan_privkey_t mce_key,
+ const char* aead,
+ const uint8_t ct[], size_t ct_len,
+ const uint8_t ad[], size_t ad_len,
+ uint8_t pt[], size_t* pt_len);
+
+/*
+* X.509 certificates
+**************************/
+
+typedef struct botan_x509_cert_struct* botan_x509_cert_t;
+
+BOTAN_PUBLIC_API(2,0) int botan_x509_cert_load(botan_x509_cert_t* cert_obj, const uint8_t cert[], size_t cert_len);
+BOTAN_PUBLIC_API(2,0) int botan_x509_cert_load_file(botan_x509_cert_t* cert_obj, const char* filename);
+
+/**
+* @return 0 if success, error if invalid object handle
+*/
+BOTAN_PUBLIC_API(2,0) int botan_x509_cert_destroy(botan_x509_cert_t cert);
+
+BOTAN_PUBLIC_API(2,8) int botan_x509_cert_dup(botan_x509_cert_t* new_cert, botan_x509_cert_t cert);
+
+/* Prefer botan_x509_cert_not_before and botan_x509_cert_not_after */
+BOTAN_PUBLIC_API(2,0) int botan_x509_cert_get_time_starts(botan_x509_cert_t cert, char out[], size_t* out_len);
+BOTAN_PUBLIC_API(2,0) int botan_x509_cert_get_time_expires(botan_x509_cert_t cert, char out[], size_t* out_len);
+
+BOTAN_PUBLIC_API(2,8) int botan_x509_cert_not_before(botan_x509_cert_t cert, uint64_t* time_since_epoch);
+BOTAN_PUBLIC_API(2,8) int botan_x509_cert_not_after(botan_x509_cert_t cert, uint64_t* time_since_epoch);
+
+BOTAN_PUBLIC_API(2,0) int botan_x509_cert_get_fingerprint(botan_x509_cert_t cert, const char* hash, uint8_t out[], size_t* out_len);
+
+BOTAN_PUBLIC_API(2,0) int botan_x509_cert_get_serial_number(botan_x509_cert_t cert, uint8_t out[], size_t* out_len);
+BOTAN_PUBLIC_API(2,0) int botan_x509_cert_get_authority_key_id(botan_x509_cert_t cert, uint8_t out[], size_t* out_len);
+BOTAN_PUBLIC_API(2,0) int botan_x509_cert_get_subject_key_id(botan_x509_cert_t cert, uint8_t out[], size_t* out_len);
+
+BOTAN_PUBLIC_API(2,0) int botan_x509_cert_get_public_key_bits(botan_x509_cert_t cert,
+ uint8_t out[], size_t* out_len);
+
+BOTAN_PUBLIC_API(2,0) int botan_x509_cert_get_public_key(botan_x509_cert_t cert, botan_pubkey_t* key);
+
+BOTAN_PUBLIC_API(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_PUBLIC_API(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_PUBLIC_API(2,0) int botan_x509_cert_to_string(botan_x509_cert_t cert, char out[], size_t* out_len);
+
+/* Must match values of Key_Constraints in key_constraints.h */
+enum botan_x509_cert_key_constraints {
+ NO_CONSTRAINTS = 0,
+ DIGITAL_SIGNATURE = 32768,
+ NON_REPUDIATION = 16384,
+ KEY_ENCIPHERMENT = 8192,
+ DATA_ENCIPHERMENT = 4096,
+ KEY_AGREEMENT = 2048,
+ KEY_CERT_SIGN = 1024,
+ CRL_SIGN = 512,
+ ENCIPHER_ONLY = 256,
+ DECIPHER_ONLY = 128
+};
+
+BOTAN_PUBLIC_API(2,0) int botan_x509_cert_allowed_usage(botan_x509_cert_t cert, unsigned int key_usage);
+
+/**
+* Check if the certificate matches the specified hostname via alternative name or CN match.
+* RFC 5280 wildcards also supported.
+*/
+BOTAN_PUBLIC_API(2,5) int botan_x509_cert_hostname_match(botan_x509_cert_t cert, const char* hostname);
+
+/**
+* Returns 0 if the validation was successful, 1 if validation failed,
+* and negative on error. A status code with details is written to
+* *validation_result
+*
+* Intermediates or trusted lists can be null
+* Trusted path can be null
+*/
+BOTAN_PUBLIC_API(2,8) int botan_x509_cert_verify(
+ int* validation_result,
+ botan_x509_cert_t cert,
+ const botan_x509_cert_t* intermediates,
+ size_t intermediates_len,
+ const botan_x509_cert_t* trusted,
+ size_t trusted_len,
+ const char* trusted_path,
+ size_t required_strength,
+ const char* hostname,
+ uint64_t reference_time);
+
+/**
+* Returns a pointer to a static character string explaining the status code,
+* or else NULL if unknown.
+*/
+BOTAN_PUBLIC_API(2,8) const char* botan_x509_cert_validation_status(int code);
+
+/*
+* X.509 CRL
+**************************/
+
+typedef struct botan_x509_crl_struct* botan_x509_crl_t;
+
+BOTAN_PUBLIC_API(2,13) int botan_x509_crl_load_file(botan_x509_crl_t* crl_obj, const char* crl_path);
+BOTAN_PUBLIC_API(2,13) int botan_x509_crl_load(botan_x509_crl_t* crl_obj, const uint8_t crl_bits[], size_t crl_bits_len);
+
+BOTAN_PUBLIC_API(2,13) int botan_x509_crl_destroy(botan_x509_crl_t crl);
+
+/**
+ * Given a CRL and a certificate,
+ * check if the certificate is revoked on that particular CRL
+ */
+BOTAN_PUBLIC_API(2,13) int botan_x509_is_revoked(botan_x509_crl_t crl, botan_x509_cert_t cert);
+
+/**
+ * Different flavor of `botan_x509_cert_verify`, supports revocation lists.
+ * CRLs are passed as an array, same as intermediates and trusted CAs
+ */
+BOTAN_PUBLIC_API(2,13) int botan_x509_cert_verify_with_crl(
+ int* validation_result,
+ botan_x509_cert_t cert,
+ const botan_x509_cert_t* intermediates,
+ size_t intermediates_len,
+ const botan_x509_cert_t* trusted,
+ size_t trusted_len,
+ const botan_x509_crl_t* crls,
+ size_t crls_len,
+ const char* trusted_path,
+ size_t required_strength,
+ const char* hostname,
+ uint64_t reference_time);
+
+/**
+ * Key wrapping as per RFC 3394
+ */
+BOTAN_PUBLIC_API(2,2)
+int botan_key_wrap3394(const uint8_t key[], size_t key_len,
+ const uint8_t kek[], size_t kek_len,
+ uint8_t wrapped_key[], size_t *wrapped_key_len);
+
+BOTAN_PUBLIC_API(2,2)
+int botan_key_unwrap3394(const uint8_t wrapped_key[], size_t wrapped_key_len,
+ const uint8_t kek[], size_t kek_len,
+ uint8_t key[], size_t *key_len);
+
+/**
+* HOTP
+*/
+
+typedef struct botan_hotp_struct* botan_hotp_t;
+
+/**
+* Initialize a HOTP instance
+*/
+BOTAN_PUBLIC_API(2,8)
+int botan_hotp_init(botan_hotp_t* hotp,
+ const uint8_t key[], size_t key_len,
+ const char* hash_algo,
+ size_t digits);
+
+/**
+* Destroy a HOTP instance
+* @return 0 if success, error if invalid object handle
+*/
+BOTAN_PUBLIC_API(2,8)
+int botan_hotp_destroy(botan_hotp_t hotp);
+
+/**
+* Generate a HOTP code for the provided counter
+*/
+BOTAN_PUBLIC_API(2,8)
+int botan_hotp_generate(botan_hotp_t hotp,
+ uint32_t* hotp_code,
+ uint64_t hotp_counter);
+
+/**
+* Verify a HOTP code
+*/
+BOTAN_PUBLIC_API(2,8)
+int botan_hotp_check(botan_hotp_t hotp,
+ uint64_t* next_hotp_counter,
+ uint32_t hotp_code,
+ uint64_t hotp_counter,
+ size_t resync_range);
+
+
+/**
+* TOTP
+*/
+
+typedef struct botan_totp_struct* botan_totp_t;
+
+/**
+* Initialize a TOTP instance
+*/
+BOTAN_PUBLIC_API(2,8)
+int botan_totp_init(botan_totp_t* totp,
+ const uint8_t key[], size_t key_len,
+ const char* hash_algo,
+ size_t digits,
+ size_t time_step);
+
+/**
+* Destroy a TOTP instance
+* @return 0 if success, error if invalid object handle
+*/
+BOTAN_PUBLIC_API(2,8)
+int botan_totp_destroy(botan_totp_t totp);
+
+/**
+* Generate a TOTP code for the provided timestamp
+* @param totp the TOTP object
+* @param totp_code the OTP code will be written here
+* @param timestamp the current local timestamp
+*/
+BOTAN_PUBLIC_API(2,8)
+int botan_totp_generate(botan_totp_t totp,
+ uint32_t* totp_code,
+ uint64_t timestamp);
+
+/**
+* Verify a TOTP code
+* @param totp the TOTP object
+* @param totp_code the presented OTP
+* @param timestamp the current local timestamp
+* @param acceptable_clock_drift specifies the acceptable amount
+* of clock drift (in terms of time steps) between the two hosts.
+*/
+BOTAN_PUBLIC_API(2,8)
+int botan_totp_check(botan_totp_t totp,
+ uint32_t totp_code,
+ uint64_t timestamp,
+ size_t acceptable_clock_drift);
+
+
+/**
+* Format Preserving Encryption
+*/
+
+typedef struct botan_fpe_struct* botan_fpe_t;
+
+#define BOTAN_FPE_FLAG_FE1_COMPAT_MODE 1
+
+BOTAN_PUBLIC_API(2,8)
+int botan_fpe_fe1_init(botan_fpe_t* fpe, botan_mp_t n,
+ const uint8_t key[], size_t key_len,
+ size_t rounds, uint32_t flags);
+
+/**
+* @return 0 if success, error if invalid object handle
+*/
+BOTAN_PUBLIC_API(2,8)
+int botan_fpe_destroy(botan_fpe_t fpe);
+
+BOTAN_PUBLIC_API(2,8)
+int botan_fpe_encrypt(botan_fpe_t fpe, botan_mp_t x, const uint8_t tweak[], size_t tweak_len);
+
+BOTAN_PUBLIC_API(2,8)
+int botan_fpe_decrypt(botan_fpe_t fpe, botan_mp_t x, const uint8_t tweak[], size_t tweak_len);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/comm/third_party/botan/src/lib/ffi/ffi_block.cpp b/comm/third_party/botan/src/lib/ffi/ffi_block.cpp
new file mode 100644
index 0000000000..fa5c25c57e
--- /dev/null
+++ b/comm/third_party/botan/src/lib/ffi/ffi_block.cpp
@@ -0,0 +1,112 @@
+/*
+* (C) 2015,2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/ffi.h>
+#include <botan/internal/ffi_util.h>
+#include <botan/block_cipher.h>
+
+extern "C" {
+
+using namespace Botan_FFI;
+
+BOTAN_FFI_DECLARE_STRUCT(botan_block_cipher_struct, Botan::BlockCipher, 0x64C29716);
+
+int botan_block_cipher_init(botan_block_cipher_t* bc, const char* bc_name)
+ {
+ return ffi_guard_thunk(__func__, [=]() -> int {
+ if(bc == nullptr || bc_name == nullptr || *bc_name == 0)
+ return BOTAN_FFI_ERROR_NULL_POINTER;
+
+ *bc = nullptr;
+
+ std::unique_ptr<Botan::BlockCipher> cipher(Botan::BlockCipher::create(bc_name));
+ if(cipher == nullptr)
+ return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+
+ *bc = new botan_block_cipher_struct(cipher.release());
+ return BOTAN_FFI_SUCCESS;
+ });
+ }
+
+/**
+* Destroy a block cipher object
+*/
+int botan_block_cipher_destroy(botan_block_cipher_t bc)
+ {
+ return BOTAN_FFI_CHECKED_DELETE(bc);
+ }
+
+int botan_block_cipher_clear(botan_block_cipher_t bc)
+ {
+ return BOTAN_FFI_DO(Botan::BlockCipher, bc, b, { b.clear(); });
+ }
+
+/**
+* Set the key for a block cipher instance
+*/
+int botan_block_cipher_set_key(botan_block_cipher_t bc,
+ const uint8_t key[], size_t len)
+ {
+ if(key == nullptr)
+ return BOTAN_FFI_ERROR_NULL_POINTER;
+ return BOTAN_FFI_DO(Botan::BlockCipher, bc, b, { b.set_key(key, len); });
+ }
+
+/**
+* Return the positive block size of this block cipher, or negative to
+* indicate an error
+*/
+int botan_block_cipher_block_size(botan_block_cipher_t bc)
+ {
+ return BOTAN_FFI_RETURNING(Botan::BlockCipher, bc, b,
+ { return static_cast<int>(b.block_size()); });
+ }
+
+int botan_block_cipher_encrypt_blocks(botan_block_cipher_t bc,
+ const uint8_t in[],
+ uint8_t out[],
+ size_t blocks)
+ {
+ if(in == nullptr || out == nullptr)
+ return BOTAN_FFI_ERROR_NULL_POINTER;
+ return BOTAN_FFI_DO(Botan::BlockCipher, bc, b, { b.encrypt_n(in, out, blocks); });
+ }
+
+int botan_block_cipher_decrypt_blocks(botan_block_cipher_t bc,
+ const uint8_t in[],
+ uint8_t out[],
+ size_t blocks)
+ {
+ if(in == nullptr || out == nullptr)
+ return BOTAN_FFI_ERROR_NULL_POINTER;
+ return BOTAN_FFI_DO(Botan::BlockCipher, bc, b, { b.decrypt_n(in, out, blocks); });
+ }
+
+int botan_block_cipher_name(botan_block_cipher_t cipher, char* name, size_t* name_len)
+ {
+ if(name_len == nullptr)
+ return BOTAN_FFI_ERROR_NULL_POINTER;
+
+ return BOTAN_FFI_DO(Botan::BlockCipher, cipher, bc, {
+ return write_str_output(name, name_len, bc.name()); });
+ }
+
+int botan_block_cipher_get_keyspec(botan_block_cipher_t cipher,
+ size_t* out_minimum_keylength,
+ size_t* out_maximum_keylength,
+ size_t* out_keylength_modulo)
+ {
+ return BOTAN_FFI_DO(Botan::BlockCipher, cipher, bc, {
+ if(out_minimum_keylength)
+ *out_minimum_keylength = bc.minimum_keylength();
+ if(out_maximum_keylength)
+ *out_maximum_keylength = bc.maximum_keylength();
+ if(out_keylength_modulo)
+ *out_keylength_modulo = bc.key_spec().keylength_multiple();
+ });
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/ffi/ffi_cert.cpp b/comm/third_party/botan/src/lib/ffi/ffi_cert.cpp
new file mode 100644
index 0000000000..1baaa18d6b
--- /dev/null
+++ b/comm/third_party/botan/src/lib/ffi/ffi_cert.cpp
@@ -0,0 +1,503 @@
+/*
+* (C) 2015,2017,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/ffi.h>
+#include <botan/internal/ffi_util.h>
+#include <botan/internal/ffi_pkey.h>
+
+#if defined(BOTAN_HAS_X509_CERTIFICATES)
+ #include <botan/x509cert.h>
+ #include <botan/x509path.h>
+ #include <botan/x509_crl.h>
+ #include <botan/data_src.h>
+#endif
+
+extern "C" {
+
+using namespace Botan_FFI;
+
+#if defined(BOTAN_HAS_X509_CERTIFICATES)
+
+BOTAN_FFI_DECLARE_STRUCT(botan_x509_cert_struct, Botan::X509_Certificate, 0x8F628937);
+
+#endif
+
+int botan_x509_cert_load_file(botan_x509_cert_t* cert_obj, const char* cert_path)
+ {
+ if(!cert_obj || !cert_path)
+ return BOTAN_FFI_ERROR_NULL_POINTER;
+
+#if defined(BOTAN_HAS_X509_CERTIFICATES) && defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
+
+ return ffi_guard_thunk(__func__, [=]() -> int {
+ std::unique_ptr<Botan::X509_Certificate> c(new Botan::X509_Certificate(cert_path));
+ *cert_obj = new botan_x509_cert_struct(c.release());
+ return BOTAN_FFI_SUCCESS;
+ });
+
+#else
+ return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+ }
+
+int botan_x509_cert_dup(botan_x509_cert_t* cert_obj, botan_x509_cert_t cert)
+ {
+ if(!cert_obj)
+ return BOTAN_FFI_ERROR_NULL_POINTER;
+
+#if defined(BOTAN_HAS_X509_CERTIFICATES) && defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
+
+ return ffi_guard_thunk(__func__, [=]() -> int {
+ std::unique_ptr<Botan::X509_Certificate> c(new Botan::X509_Certificate(safe_get(cert)));
+ *cert_obj = new botan_x509_cert_struct(c.release());
+ return BOTAN_FFI_SUCCESS;
+ });
+
+#else
+ BOTAN_UNUSED(cert);
+ return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+ }
+
+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)
+ return BOTAN_FFI_ERROR_NULL_POINTER;
+
+#if defined(BOTAN_HAS_X509_CERTIFICATES)
+ return ffi_guard_thunk(__func__, [=]() -> int {
+ Botan::DataSource_Memory bits(cert_bits, cert_bits_len);
+ std::unique_ptr<Botan::X509_Certificate> c(new Botan::X509_Certificate(bits));
+ *cert_obj = new botan_x509_cert_struct(c.release());
+ return BOTAN_FFI_SUCCESS;
+ });
+#else
+ BOTAN_UNUSED(cert_bits_len);
+ 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)
+ return BOTAN_FFI_ERROR_NULL_POINTER;
+
+ *key = nullptr;
+
+#if defined(BOTAN_HAS_X509_CERTIFICATES)
+ return ffi_guard_thunk(__func__, [=]() -> int {
+ std::unique_ptr<Botan::Public_Key> publicKey = safe_get(cert).load_subject_public_key();
+ *key = new botan_pubkey_struct(publicKey.release());
+ return BOTAN_FFI_SUCCESS;
+ });
+#else
+ BOTAN_UNUSED(cert);
+ return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+ }
+
+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 defined(BOTAN_HAS_X509_CERTIFICATES)
+ return BOTAN_FFI_DO(Botan::X509_Certificate, cert, c, { return write_str_output(out, out_len, c.issuer_info(key).at(index)); });
+#else
+ BOTAN_UNUSED(cert, key, index, out, out_len);
+ 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 defined(BOTAN_HAS_X509_CERTIFICATES)
+ return BOTAN_FFI_DO(Botan::X509_Certificate, cert, c, { return write_str_output(out, out_len, c.subject_info(key).at(index)); });
+#else
+ BOTAN_UNUSED(cert, key, index, out, out_len);
+ return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+ }
+
+int botan_x509_cert_to_string(botan_x509_cert_t cert, char out[], size_t* out_len)
+ {
+#if defined(BOTAN_HAS_X509_CERTIFICATES)
+ return BOTAN_FFI_DO(Botan::X509_Certificate, cert, c, { return write_str_output(out, out_len, c.to_string()); });
+#else
+ BOTAN_UNUSED(cert, out, out_len);
+ return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+ }
+
+int botan_x509_cert_allowed_usage(botan_x509_cert_t cert, unsigned int key_usage)
+ {
+#if defined(BOTAN_HAS_X509_CERTIFICATES)
+ return BOTAN_FFI_RETURNING(Botan::X509_Certificate, cert, c, {
+ const Botan::Key_Constraints k = static_cast<Botan::Key_Constraints>(key_usage);
+ if(c.allowed_usage(k))
+ return BOTAN_FFI_SUCCESS;
+ return 1;
+ });
+#else
+ BOTAN_UNUSED(cert, key_usage);
+ 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);
+#else
+ BOTAN_UNUSED(cert);
+ return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+ }
+
+int botan_x509_cert_get_time_starts(botan_x509_cert_t cert, char out[], size_t* out_len)
+ {
+#if defined(BOTAN_HAS_X509_CERTIFICATES)
+ return BOTAN_FFI_DO(Botan::X509_Certificate, cert, c, { return write_str_output(out, out_len, c.not_before().to_string()); });
+#else
+ BOTAN_UNUSED(cert, out, out_len);
+ return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+ }
+
+int botan_x509_cert_get_time_expires(botan_x509_cert_t cert, char out[], size_t* out_len)
+ {
+#if defined(BOTAN_HAS_X509_CERTIFICATES)
+ return BOTAN_FFI_DO(Botan::X509_Certificate, cert, c, { return write_str_output(out, out_len, c.not_after().to_string()); });
+#else
+ BOTAN_UNUSED(cert, out, out_len);
+ return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+ }
+
+int botan_x509_cert_not_before(botan_x509_cert_t cert, uint64_t* time_since_epoch)
+ {
+#if defined(BOTAN_HAS_X509_CERTIFICATES)
+ return BOTAN_FFI_DO(Botan::X509_Certificate, cert, c, {
+ *time_since_epoch = c.not_before().time_since_epoch();
+ });
+#else
+ BOTAN_UNUSED(cert, time_since_epoch);
+ return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+ }
+
+int botan_x509_cert_not_after(botan_x509_cert_t cert, uint64_t* time_since_epoch)
+ {
+#if defined(BOTAN_HAS_X509_CERTIFICATES)
+ return BOTAN_FFI_DO(Botan::X509_Certificate, cert, c, {
+ *time_since_epoch = c.not_after().time_since_epoch();
+ });
+#else
+ BOTAN_UNUSED(cert, time_since_epoch);
+ return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+ }
+
+int botan_x509_cert_get_serial_number(botan_x509_cert_t cert, uint8_t out[], size_t* out_len)
+ {
+#if defined(BOTAN_HAS_X509_CERTIFICATES)
+ return BOTAN_FFI_DO(Botan::X509_Certificate, cert, c, { return write_vec_output(out, out_len, c.serial_number()); });
+#else
+ BOTAN_UNUSED(cert, out, out_len);
+ 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 defined(BOTAN_HAS_X509_CERTIFICATES)
+ return BOTAN_FFI_DO(Botan::X509_Certificate, cert, c, { return write_str_output(out, out_len, c.fingerprint(hash)); });
+#else
+ BOTAN_UNUSED(cert, hash, out, out_len);
+ return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+ }
+
+int botan_x509_cert_get_authority_key_id(botan_x509_cert_t cert, uint8_t out[], size_t* out_len)
+ {
+#if defined(BOTAN_HAS_X509_CERTIFICATES)
+ return BOTAN_FFI_DO(Botan::X509_Certificate, cert, c, { return write_vec_output(out, out_len, c.authority_key_id()); });
+#else
+ BOTAN_UNUSED(cert, out, out_len);
+ return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+ }
+
+int botan_x509_cert_get_subject_key_id(botan_x509_cert_t cert, uint8_t out[], size_t* out_len)
+ {
+#if defined(BOTAN_HAS_X509_CERTIFICATES)
+ return BOTAN_FFI_DO(Botan::X509_Certificate, cert, c, { return write_vec_output(out, out_len, c.subject_key_id()); });
+#else
+ BOTAN_UNUSED(cert, out, out_len);
+ return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+ }
+
+int botan_x509_cert_get_public_key_bits(botan_x509_cert_t cert, uint8_t out[], size_t* out_len)
+ {
+#if defined(BOTAN_HAS_X509_CERTIFICATES)
+ return BOTAN_FFI_DO(Botan::X509_Certificate, cert, c, { return write_vec_output(out, out_len, c.subject_public_key_bits()); });
+#else
+ BOTAN_UNUSED(cert, out, out_len);
+ 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;
+
+#if defined(BOTAN_HAS_X509_CERTIFICATES)
+ return BOTAN_FFI_DO(Botan::X509_Certificate, cert, c,
+ { return c.matches_dns_name(hostname) ? 0 : -1; });
+#else
+ BOTAN_UNUSED(cert);
+ return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+ }
+
+int botan_x509_cert_verify(int* result_code,
+ botan_x509_cert_t cert,
+ const botan_x509_cert_t* intermediates,
+ size_t intermediates_len,
+ const botan_x509_cert_t* trusted,
+ size_t trusted_len,
+ const char* trusted_path,
+ size_t required_strength,
+ const char* hostname_cstr,
+ uint64_t reference_time)
+ {
+ if(required_strength == 0)
+ required_strength = 110;
+
+#if defined(BOTAN_HAS_X509_CERTIFICATES)
+ return ffi_guard_thunk(__func__, [=]() -> int {
+ const std::string hostname((hostname_cstr == nullptr) ? "" : hostname_cstr);
+ const Botan::Usage_Type usage = Botan::Usage_Type::UNSPECIFIED;
+ const auto validation_time = reference_time == 0 ?
+ std::chrono::system_clock::now() :
+ std::chrono::system_clock::from_time_t(static_cast<time_t>(reference_time));
+
+ std::vector<Botan::X509_Certificate> end_certs;
+ end_certs.push_back(safe_get(cert));
+ for(size_t i = 0; i != intermediates_len; ++i)
+ end_certs.push_back(safe_get(intermediates[i]));
+
+ std::unique_ptr<Botan::Certificate_Store> trusted_from_path;
+ std::unique_ptr<Botan::Certificate_Store_In_Memory> trusted_extra;
+ std::vector<Botan::Certificate_Store*> trusted_roots;
+
+ if(trusted_path && *trusted_path)
+ {
+ trusted_from_path.reset(new Botan::Certificate_Store_In_Memory(trusted_path));
+ trusted_roots.push_back(trusted_from_path.get());
+ }
+
+ if(trusted_len > 0)
+ {
+ trusted_extra.reset(new Botan::Certificate_Store_In_Memory);
+ for(size_t i = 0; i != trusted_len; ++i)
+ {
+ trusted_extra->add_certificate(safe_get(trusted[i]));
+ }
+ trusted_roots.push_back(trusted_extra.get());
+ }
+
+ 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)
+ *result_code = static_cast<int>(validation_result.result());
+
+ if(validation_result.successful_validation())
+ return 0;
+ else
+ return 1;
+ });
+#else
+ BOTAN_UNUSED(result_code, cert, intermediates, intermediates_len, trusted);
+ BOTAN_UNUSED(trusted_len, trusted_path, hostname_cstr, reference_time);
+ return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+ }
+
+const char* botan_x509_cert_validation_status(int code)
+ {
+ if(code < 0)
+ return nullptr;
+
+#if defined(BOTAN_HAS_X509_CERTIFICATES)
+ Botan::Certificate_Status_Code sc = static_cast<Botan::Certificate_Status_Code>(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)
+ return BOTAN_FFI_ERROR_NULL_POINTER;
+
+#if defined(BOTAN_HAS_X509_CERTIFICATES) && defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
+
+ return ffi_guard_thunk(__func__, [=]() -> int {
+ std::unique_ptr<Botan::X509_CRL> c(new Botan::X509_CRL(crl_path));
+ *crl_obj = new botan_x509_crl_struct(c.release());
+ return BOTAN_FFI_SUCCESS;
+ });
+
+#else
+ return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+ }
+
+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)
+ return BOTAN_FFI_ERROR_NULL_POINTER;
+
+#if defined(BOTAN_HAS_X509_CERTIFICATES)
+ return ffi_guard_thunk(__func__, [=]() -> int {
+ Botan::DataSource_Memory bits(crl_bits, crl_bits_len);
+ std::unique_ptr<Botan::X509_CRL> c(new Botan::X509_CRL(bits));
+ *crl_obj = new botan_x509_crl_struct(c.release());
+ return BOTAN_FFI_SUCCESS;
+ });
+#else
+ BOTAN_UNUSED(crl_bits_len);
+ 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);
+#else
+ BOTAN_UNUSED(crl);
+ 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_RETURNING(Botan::X509_CRL, crl, c, {
+ return c.is_revoked(safe_get(cert)) ? 0 : -1;
+ });
+#else
+ BOTAN_UNUSED(cert);
+ BOTAN_UNUSED(crl);
+ 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,
+ size_t intermediates_len,
+ const botan_x509_cert_t* trusted,
+ size_t trusted_len,
+ const botan_x509_crl_t* crls,
+ size_t crls_len,
+ const char* trusted_path,
+ size_t required_strength,
+ const char* hostname_cstr,
+ uint64_t reference_time)
+ {
+ if(required_strength == 0)
+ required_strength = 110;
+
+#if defined(BOTAN_HAS_X509_CERTIFICATES)
+ return ffi_guard_thunk(__func__, [=]() -> int {
+ const std::string hostname((hostname_cstr == nullptr) ? "" : hostname_cstr);
+ const Botan::Usage_Type usage = Botan::Usage_Type::UNSPECIFIED;
+ const auto validation_time = reference_time == 0 ?
+ std::chrono::system_clock::now() :
+ std::chrono::system_clock::from_time_t(static_cast<time_t>(reference_time));
+
+ std::vector<Botan::X509_Certificate> end_certs;
+ end_certs.push_back(safe_get(cert));
+ for(size_t i = 0; i != intermediates_len; ++i)
+ end_certs.push_back(safe_get(intermediates[i]));
+
+ std::unique_ptr<Botan::Certificate_Store> trusted_from_path;
+ std::unique_ptr<Botan::Certificate_Store_In_Memory> trusted_extra;
+ std::unique_ptr<Botan::Certificate_Store_In_Memory> trusted_crls;
+ std::vector<Botan::Certificate_Store*> trusted_roots;
+
+ if(trusted_path && *trusted_path)
+ {
+ trusted_from_path.reset(new Botan::Certificate_Store_In_Memory(trusted_path));
+ trusted_roots.push_back(trusted_from_path.get());
+ }
+
+ if(trusted_len > 0)
+ {
+ trusted_extra.reset(new Botan::Certificate_Store_In_Memory);
+ for(size_t i = 0; i != trusted_len; ++i)
+ {
+ trusted_extra->add_certificate(safe_get(trusted[i]));
+ }
+ trusted_roots.push_back(trusted_extra.get());
+ }
+
+ if(crls_len > 0)
+ {
+ trusted_crls.reset(new Botan::Certificate_Store_In_Memory);
+ for(size_t i = 0; i != crls_len; ++i)
+ {
+ trusted_crls->add_crl(safe_get(crls[i]));
+ }
+ trusted_roots.push_back(trusted_crls.get());
+ }
+
+ 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)
+ *result_code = static_cast<int>(validation_result.result());
+
+ if(validation_result.successful_validation())
+ return 0;
+ else
+ return 1;
+ });
+#else
+ BOTAN_UNUSED(result_code, cert, intermediates, intermediates_len, trusted);
+ BOTAN_UNUSED(trusted_len, trusted_path, hostname_cstr, reference_time, crls, crls_len);
+ return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/ffi/ffi_cipher.cpp b/comm/third_party/botan/src/lib/ffi/ffi_cipher.cpp
new file mode 100644
index 0000000000..17f443c895
--- /dev/null
+++ b/comm/third_party/botan/src/lib/ffi/ffi_cipher.cpp
@@ -0,0 +1,233 @@
+/*
+* (C) 2015,2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/ffi.h>
+#include <botan/internal/ffi_util.h>
+#include <botan/aead.h>
+
+extern "C" {
+
+using namespace Botan_FFI;
+
+struct botan_cipher_struct final : public botan_struct<Botan::Cipher_Mode, 0xB4A2BF9C>
+ {
+ explicit botan_cipher_struct(Botan::Cipher_Mode* x) : botan_struct(x) {}
+ Botan::secure_vector<uint8_t> m_buf;
+ };
+
+int botan_cipher_init(botan_cipher_t* cipher, const char* cipher_name, uint32_t flags)
+ {
+ return ffi_guard_thunk(__func__, [=]() -> int {
+ const bool encrypt_p = ((flags & BOTAN_CIPHER_INIT_FLAG_MASK_DIRECTION) == BOTAN_CIPHER_INIT_FLAG_ENCRYPT);
+ const Botan::Cipher_Dir dir = encrypt_p ? Botan::ENCRYPTION : Botan::DECRYPTION;
+ std::unique_ptr<Botan::Cipher_Mode> mode(Botan::Cipher_Mode::create(cipher_name, dir));
+ if(!mode)
+ return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+ *cipher = new botan_cipher_struct(mode.release());
+ return BOTAN_FFI_SUCCESS;
+ });
+ }
+
+int botan_cipher_destroy(botan_cipher_t cipher)
+ {
+ return BOTAN_FFI_CHECKED_DELETE(cipher);
+ }
+
+int botan_cipher_clear(botan_cipher_t cipher)
+ {
+ return BOTAN_FFI_DO(Botan::Cipher_Mode, cipher, c, { c.clear(); });
+ }
+
+int botan_cipher_reset(botan_cipher_t cipher)
+ {
+ return BOTAN_FFI_DO(Botan::Cipher_Mode, cipher, c, { c.reset(); });
+ }
+
+int botan_cipher_output_length(botan_cipher_t cipher, size_t in_len, size_t* out_len)
+ {
+ if(out_len == nullptr)
+ return BOTAN_FFI_ERROR_NULL_POINTER;
+
+ return BOTAN_FFI_DO(Botan::Cipher_Mode, cipher, c, { *out_len = c.output_length(in_len); });
+ }
+
+int botan_cipher_query_keylen(botan_cipher_t cipher,
+ size_t* out_minimum_keylength,
+ size_t* out_maximum_keylength)
+ {
+ return BOTAN_FFI_DO(Botan::Cipher_Mode, cipher, c, {
+ *out_minimum_keylength = c.key_spec().minimum_keylength();
+ *out_maximum_keylength = c.key_spec().maximum_keylength();
+ });
+ }
+
+int botan_cipher_get_keyspec(botan_cipher_t cipher,
+ size_t* out_minimum_keylength,
+ size_t* out_maximum_keylength,
+ size_t* out_keylength_modulo)
+ {
+ return BOTAN_FFI_DO(Botan::Cipher_Mode, cipher, c, {
+ if(out_minimum_keylength)
+ *out_minimum_keylength = c.key_spec().minimum_keylength();
+ if(out_maximum_keylength)
+ *out_maximum_keylength = c.key_spec().maximum_keylength();
+ if(out_keylength_modulo)
+ *out_keylength_modulo = c.key_spec().keylength_multiple();
+ });
+ }
+
+int botan_cipher_set_key(botan_cipher_t cipher,
+ const uint8_t* key, size_t key_len)
+ {
+ return BOTAN_FFI_DO(Botan::Cipher_Mode, cipher, c, { c.set_key(key, key_len); });
+ }
+
+int botan_cipher_start(botan_cipher_t cipher_obj,
+ const uint8_t* nonce, size_t nonce_len)
+ {
+ return ffi_guard_thunk(__func__, [=]() -> int {
+ Botan::Cipher_Mode& cipher = safe_get(cipher_obj);
+ cipher.start(nonce, nonce_len);
+ cipher_obj->m_buf.reserve(cipher.update_granularity());
+ return BOTAN_FFI_SUCCESS;
+ });
+ }
+
+int botan_cipher_update(botan_cipher_t cipher_obj,
+ uint32_t flags,
+ uint8_t output_ptr[],
+ size_t orig_output_size,
+ size_t* output_written,
+ const uint8_t input_ptr[],
+ size_t orig_input_size,
+ size_t* input_consumed)
+ {
+ return ffi_guard_thunk(__func__, [=]() -> int {
+
+ size_t input_size = orig_input_size;
+ size_t output_size = orig_output_size;
+ const uint8_t* input = input_ptr;
+ uint8_t* output = output_ptr;
+
+ using namespace Botan;
+ Cipher_Mode& cipher = safe_get(cipher_obj);
+ secure_vector<uint8_t>& mbuf = cipher_obj->m_buf;
+
+ const bool final_input = (flags & BOTAN_CIPHER_UPDATE_FLAG_FINAL);
+
+ if(final_input)
+ {
+ mbuf.assign(input, input + input_size);
+ *input_consumed = input_size;
+ *output_written = 0;
+
+ try
+ {
+ cipher.finish(mbuf);
+ }
+ catch(Invalid_Authentication_Tag&)
+ {
+ return BOTAN_FFI_ERROR_BAD_MAC;
+ }
+
+ *output_written = mbuf.size();
+
+ if(mbuf.size() <= output_size)
+ {
+ copy_mem(output, mbuf.data(), mbuf.size());
+ mbuf.clear();
+ return BOTAN_FFI_SUCCESS;
+ }
+
+ return -1;
+ }
+
+ if(input_size == 0)
+ {
+ // Currently must take entire buffer in this case
+ *output_written = mbuf.size();
+ if(output_size >= mbuf.size())
+ {
+ copy_mem(output, mbuf.data(), mbuf.size());
+ mbuf.clear();
+ return BOTAN_FFI_SUCCESS;
+ }
+
+ return -1;
+ }
+
+ const size_t ud = cipher.update_granularity();
+ BOTAN_ASSERT(cipher.update_granularity() > cipher.minimum_final_size(), "logic error");
+
+ mbuf.resize(ud);
+ size_t taken = 0, written = 0;
+
+ while(input_size >= ud && output_size >= ud)
+ {
+ // FIXME we can use process here and avoid the copy
+ copy_mem(mbuf.data(), input, ud);
+ cipher.update(mbuf);
+
+ input_size -= ud;
+ copy_mem(output, mbuf.data(), ud);
+ input += ud;
+ taken += ud;
+
+ output_size -= ud;
+ output += ud;
+ written += ud;
+ }
+
+ *output_written = written;
+ *input_consumed = taken;
+
+ return BOTAN_FFI_SUCCESS;
+ });
+ }
+
+int botan_cipher_set_associated_data(botan_cipher_t cipher,
+ const uint8_t* ad,
+ size_t ad_len)
+ {
+ return BOTAN_FFI_RETURNING(Botan::Cipher_Mode, cipher, c, {
+ if(Botan::AEAD_Mode* aead = dynamic_cast<Botan::AEAD_Mode*>(&c))
+ {
+ aead->set_associated_data(ad, ad_len);
+ return BOTAN_FFI_SUCCESS;
+ }
+ return BOTAN_FFI_ERROR_BAD_PARAMETER;
+ });
+ }
+
+int botan_cipher_valid_nonce_length(botan_cipher_t cipher, size_t nl)
+ {
+ return BOTAN_FFI_RETURNING(Botan::Cipher_Mode, cipher, c, {
+ return c.valid_nonce_length(nl) ? 1 : 0;
+ });
+ }
+
+int botan_cipher_get_default_nonce_length(botan_cipher_t cipher, size_t* nl)
+ {
+ return BOTAN_FFI_DO(Botan::Cipher_Mode, cipher, c, { *nl = c.default_nonce_length(); });
+ }
+
+int botan_cipher_get_update_granularity(botan_cipher_t cipher, size_t* ug)
+ {
+ return BOTAN_FFI_DO(Botan::Cipher_Mode, cipher, c, { *ug = c.update_granularity(); });
+ }
+
+int botan_cipher_get_tag_length(botan_cipher_t cipher, size_t* tl)
+ {
+ return BOTAN_FFI_DO(Botan::Cipher_Mode, cipher, c, { *tl = c.tag_size(); });
+ }
+
+int botan_cipher_name(botan_cipher_t cipher, char* name, size_t* name_len)
+ {
+ return BOTAN_FFI_DO(Botan::Cipher_Mode, cipher, c, {
+ return write_str_output(name, name_len, c.name()); });
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/ffi/ffi_fpe.cpp b/comm/third_party/botan/src/lib/ffi/ffi_fpe.cpp
new file mode 100644
index 0000000000..01706ea201
--- /dev/null
+++ b/comm/third_party/botan/src/lib/ffi/ffi_fpe.cpp
@@ -0,0 +1,94 @@
+/*
+* (C) 2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/ffi.h>
+#include <botan/internal/ffi_util.h>
+#include <botan/internal/ffi_mp.h>
+#include <memory>
+
+#if defined(BOTAN_HAS_FPE_FE1)
+ #include <botan/fpe_fe1.h>
+#endif
+
+extern "C" {
+
+using namespace Botan_FFI;
+
+#if defined(BOTAN_HAS_FPE_FE1)
+
+BOTAN_FFI_DECLARE_STRUCT(botan_fpe_struct, Botan::FPE_FE1, 0xD49FB820);
+
+#endif
+
+int botan_fpe_fe1_init(botan_fpe_t* fpe, botan_mp_t n,
+ const uint8_t key[], size_t key_len,
+ size_t rounds, uint32_t flags)
+ {
+#if defined(BOTAN_HAS_FPE_FE1)
+
+ return ffi_guard_thunk(__func__, [=]() {
+
+ if(fpe == nullptr || key == nullptr)
+ return BOTAN_FFI_ERROR_NULL_POINTER;
+
+ *fpe = nullptr;
+
+ if(flags != 0 && flags != BOTAN_FPE_FLAG_FE1_COMPAT_MODE)
+ return BOTAN_FFI_ERROR_BAD_FLAG;
+
+ const bool compat_mode = (flags & BOTAN_FPE_FLAG_FE1_COMPAT_MODE);
+
+ std::unique_ptr<Botan::FPE_FE1> fpe_obj(
+ new Botan::FPE_FE1(safe_get(n), rounds, compat_mode));
+
+ fpe_obj->set_key(key, key_len);
+
+ *fpe = new botan_fpe_struct(fpe_obj.release());
+ return BOTAN_FFI_SUCCESS;
+ });
+#else
+ *fpe = nullptr;
+ return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+ }
+
+int botan_fpe_destroy(botan_fpe_t fpe)
+ {
+#if defined(BOTAN_HAS_FPE_FE1)
+ return BOTAN_FFI_CHECKED_DELETE(fpe);
+#else
+ return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+ }
+
+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);
+ safe_get(x) = r;
+ return BOTAN_FFI_SUCCESS;
+ });
+#else
+ return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+ }
+
+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);
+ safe_get(x) = r;
+ return BOTAN_FFI_SUCCESS;
+ });
+
+#else
+ return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/ffi/ffi_hash.cpp b/comm/third_party/botan/src/lib/ffi/ffi_hash.cpp
new file mode 100644
index 0000000000..12eb92301e
--- /dev/null
+++ b/comm/third_party/botan/src/lib/ffi/ffi_hash.cpp
@@ -0,0 +1,91 @@
+/*
+* (C) 2015,2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/ffi.h>
+#include <botan/internal/ffi_util.h>
+#include <botan/hash.h>
+
+extern "C" {
+
+using namespace Botan_FFI;
+
+BOTAN_FFI_DECLARE_STRUCT(botan_hash_struct, Botan::HashFunction, 0x1F0A4F84);
+
+int botan_hash_init(botan_hash_t* hash, const char* hash_name, uint32_t flags)
+ {
+ return ffi_guard_thunk(__func__, [=]() -> int {
+ if(hash == nullptr || hash_name == nullptr || *hash_name == 0)
+ return BOTAN_FFI_ERROR_NULL_POINTER;
+ if(flags != 0)
+ return BOTAN_FFI_ERROR_BAD_FLAG;
+
+ std::unique_ptr<Botan::HashFunction> h = Botan::HashFunction::create(hash_name);
+ if(h == nullptr)
+ return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+
+ *hash = new botan_hash_struct(h.release());
+ return BOTAN_FFI_SUCCESS;
+ });
+ }
+
+int botan_hash_destroy(botan_hash_t hash)
+ {
+ return BOTAN_FFI_CHECKED_DELETE(hash);
+ }
+
+int botan_hash_output_length(botan_hash_t hash, size_t* out)
+ {
+ if(out == nullptr)
+ return BOTAN_FFI_ERROR_NULL_POINTER;
+ return BOTAN_FFI_DO(Botan::HashFunction, hash, h, { *out = h.output_length(); });
+ }
+
+int botan_hash_block_size(botan_hash_t hash, size_t* out)
+ {
+ if(out == nullptr)
+ return BOTAN_FFI_ERROR_NULL_POINTER;
+ return BOTAN_FFI_DO(Botan::HashFunction, hash, h, { *out = h.hash_block_size(); });
+ }
+
+int botan_hash_clear(botan_hash_t hash)
+ {
+ return BOTAN_FFI_DO(Botan::HashFunction, hash, h, { h.clear(); });
+ }
+
+int botan_hash_update(botan_hash_t hash, const uint8_t* buf, size_t len)
+ {
+ if(len == 0)
+ return 0;
+
+ if(buf == nullptr)
+ return BOTAN_FFI_ERROR_NULL_POINTER;
+
+ return BOTAN_FFI_DO(Botan::HashFunction, hash, h, { h.update(buf, len); });
+ }
+
+int botan_hash_final(botan_hash_t hash, uint8_t out[])
+ {
+ if(out == nullptr)
+ return BOTAN_FFI_ERROR_NULL_POINTER;
+ return BOTAN_FFI_DO(Botan::HashFunction, hash, h, { h.final(out); });
+ }
+
+int botan_hash_copy_state(botan_hash_t* dest, const botan_hash_t source)
+ {
+ return BOTAN_FFI_DO(Botan::HashFunction, source, src, {
+ *dest = new botan_hash_struct(src.copy_state().release()); });
+ }
+
+int botan_hash_name(botan_hash_t hash, char* name, size_t* name_len)
+ {
+ if(name_len == nullptr)
+ return BOTAN_FFI_ERROR_NULL_POINTER;
+
+ return BOTAN_FFI_DO(Botan::HashFunction, hash, h, {
+ return write_str_output(name, name_len, h.name()); });
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/ffi/ffi_hotp.cpp b/comm/third_party/botan/src/lib/ffi/ffi_hotp.cpp
new file mode 100644
index 0000000000..74f059d502
--- /dev/null
+++ b/comm/third_party/botan/src/lib/ffi/ffi_hotp.cpp
@@ -0,0 +1,99 @@
+/*
+* (C) 2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/ffi.h>
+#include <botan/internal/ffi_util.h>
+
+#if defined(BOTAN_HAS_HOTP)
+ #include <botan/otp.h>
+#endif
+
+extern "C" {
+
+using namespace Botan_FFI;
+
+#if defined(BOTAN_HAS_HOTP)
+
+BOTAN_FFI_DECLARE_STRUCT(botan_hotp_struct, Botan::HOTP, 0x89CBF191);
+
+#endif
+
+int botan_hotp_init(botan_hotp_t* hotp,
+ const uint8_t key[], size_t key_len,
+ const char* hash_algo,
+ size_t digits)
+ {
+ if(hotp == nullptr || key == nullptr || hash_algo == nullptr)
+ return BOTAN_FFI_ERROR_NULL_POINTER;
+
+ *hotp = nullptr;
+
+#if defined(BOTAN_HAS_HOTP)
+ return ffi_guard_thunk(__func__, [=]() -> int {
+
+ *hotp = new botan_hotp_struct(
+ new Botan::HOTP(key, key_len, hash_algo, digits));
+
+ return BOTAN_FFI_SUCCESS;
+ });
+#else
+ BOTAN_UNUSED(hotp, key, key_len, hash_algo, digits);
+ return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+ }
+
+int botan_hotp_destroy(botan_hotp_t hotp)
+ {
+#if defined(BOTAN_HAS_HOTP)
+ return BOTAN_FFI_CHECKED_DELETE(hotp);
+#else
+ BOTAN_UNUSED(hotp);
+ return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+ }
+
+int botan_hotp_generate(botan_hotp_t hotp,
+ uint32_t* hotp_code,
+ uint64_t hotp_counter)
+ {
+#if defined(BOTAN_HAS_HOTP)
+ if(hotp == nullptr || hotp_code == nullptr)
+ return BOTAN_FFI_ERROR_NULL_POINTER;
+
+ return BOTAN_FFI_DO(Botan::HOTP, hotp, h, {
+ *hotp_code = h.generate_hotp(hotp_counter);
+ });
+
+#else
+ BOTAN_UNUSED(hotp, hotp_code, hotp_counter);
+ return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+ }
+
+int botan_hotp_check(botan_hotp_t hotp,
+ uint64_t* next_hotp_counter,
+ uint32_t hotp_code,
+ uint64_t hotp_counter,
+ size_t resync_range)
+ {
+#if defined(BOTAN_HAS_HOTP)
+ return BOTAN_FFI_RETURNING(Botan::HOTP, hotp, h, {
+
+ auto resp = h.verify_hotp(hotp_code, hotp_counter, resync_range);
+
+ if(next_hotp_counter)
+ *next_hotp_counter = resp.second;
+
+ return (resp.first == true) ? BOTAN_FFI_SUCCESS : BOTAN_FFI_INVALID_VERIFIER;
+ });
+
+#else
+ BOTAN_UNUSED(hotp, next_hotp_counter, hotp_code, hotp_counter, resync_range);
+ return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/ffi/ffi_kdf.cpp b/comm/third_party/botan/src/lib/ffi/ffi_kdf.cpp
new file mode 100644
index 0000000000..d38dd594bd
--- /dev/null
+++ b/comm/third_party/botan/src/lib/ffi/ffi_kdf.cpp
@@ -0,0 +1,189 @@
+/*
+* (C) 2015,2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/ffi.h>
+#include <botan/internal/ffi_util.h>
+#include <botan/internal/ffi_rng.h>
+#include <botan/pbkdf.h>
+#include <botan/pwdhash.h>
+#include <botan/kdf.h>
+
+#if defined(BOTAN_HAS_BCRYPT)
+ #include <botan/bcrypt.h>
+#endif
+
+extern "C" {
+
+using namespace Botan_FFI;
+
+int botan_pbkdf(const char* algo, uint8_t out[], size_t out_len,
+ const char* pass, const uint8_t salt[], size_t salt_len,
+ size_t iterations)
+ {
+ return botan_pwdhash(algo,
+ iterations,
+ 0,
+ 0,
+ out, out_len,
+ pass, 0,
+ salt, salt_len);
+ }
+
+int botan_pbkdf_timed(const char* algo,
+ uint8_t out[], size_t out_len,
+ const char* password,
+ const uint8_t salt[], size_t salt_len,
+ size_t ms_to_run,
+ size_t* iterations_used)
+ {
+ return botan_pwdhash_timed(algo,
+ static_cast<uint32_t>(ms_to_run),
+ iterations_used,
+ nullptr,
+ nullptr,
+ out, out_len,
+ password, 0,
+ salt, salt_len);
+ }
+
+int botan_pwdhash(
+ const char* algo,
+ size_t param1,
+ size_t param2,
+ size_t param3,
+ uint8_t out[],
+ size_t out_len,
+ const char* password,
+ size_t password_len,
+ const uint8_t salt[],
+ size_t salt_len)
+ {
+ if(algo == nullptr || password == nullptr)
+ return BOTAN_FFI_ERROR_NULL_POINTER;
+
+ if(password_len == 0)
+ password_len = std::strlen(password);
+
+ return ffi_guard_thunk(__func__, [=]() -> int {
+ auto pwdhash_fam = Botan::PasswordHashFamily::create(algo);
+
+ if(!pwdhash_fam)
+ return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+
+ auto pwdhash = pwdhash_fam->from_params(param1, param2, param3);
+
+ pwdhash->derive_key(out, out_len,
+ password, password_len,
+ salt, salt_len);
+
+ return BOTAN_FFI_SUCCESS;
+ });
+ }
+
+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* password,
+ size_t password_len,
+ const uint8_t salt[],
+ size_t salt_len)
+ {
+ if(algo == nullptr || password == nullptr)
+ return BOTAN_FFI_ERROR_NULL_POINTER;
+
+ if(password_len == 0)
+ password_len = std::strlen(password);
+
+ return ffi_guard_thunk(__func__, [=]() -> int {
+
+ auto pwdhash_fam = Botan::PasswordHashFamily::create(algo);
+
+ if(!pwdhash_fam)
+ return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+
+ auto pwdhash = pwdhash_fam->tune(out_len, std::chrono::milliseconds(msec));
+
+ if(param1)
+ *param1 = pwdhash->iterations();
+ if(param2)
+ *param2 = pwdhash->parallelism();
+ if(param3)
+ *param3 = pwdhash->memory_param();
+
+ pwdhash->derive_key(out, out_len,
+ password, password_len,
+ salt, salt_len);
+
+ return BOTAN_FFI_SUCCESS;
+ });
+ }
+
+int botan_kdf(const char* kdf_algo,
+ uint8_t out[], size_t out_len,
+ const uint8_t secret[], size_t secret_len,
+ const uint8_t salt[], size_t salt_len,
+ const uint8_t label[], size_t label_len)
+ {
+ return ffi_guard_thunk(__func__, [=]() -> int {
+ std::unique_ptr<Botan::KDF> kdf(Botan::get_kdf(kdf_algo));
+ kdf->kdf(out, out_len, secret, secret_len, salt, salt_len, label, label_len);
+ return BOTAN_FFI_SUCCESS;
+ });
+ }
+
+int botan_scrypt(uint8_t out[], size_t out_len,
+ const char* password,
+ const uint8_t salt[], size_t salt_len,
+ size_t N, size_t r, size_t p)
+ {
+ return botan_pwdhash("Scrypt", N, r, p,
+ out, out_len,
+ password, 0,
+ salt, salt_len);
+ }
+
+int botan_bcrypt_generate(uint8_t* out, size_t* out_len,
+ const char* pass,
+ botan_rng_t rng_obj, size_t wf,
+ uint32_t flags)
+ {
+#if defined(BOTAN_HAS_BCRYPT)
+ return ffi_guard_thunk(__func__, [=]() -> int {
+ if(out == nullptr || out_len == nullptr || pass == nullptr)
+ return BOTAN_FFI_ERROR_NULL_POINTER;
+
+ if(flags != 0)
+ return BOTAN_FFI_ERROR_BAD_FLAG;
+
+ if(wf < 4 || wf > 18)
+ return BOTAN_FFI_ERROR_BAD_PARAMETER;
+
+ Botan::RandomNumberGenerator& rng = safe_get(rng_obj);
+ const std::string bcrypt = Botan::generate_bcrypt(pass, rng, static_cast<uint16_t>(wf));
+ return write_str_output(out, out_len, bcrypt);
+ });
+#else
+ return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+ }
+
+int botan_bcrypt_is_valid(const char* pass, const char* hash)
+ {
+#if defined(BOTAN_HAS_BCRYPT)
+ return ffi_guard_thunk(__func__, [=]() -> int {
+ return Botan::check_bcrypt(pass, hash) ? BOTAN_FFI_SUCCESS : BOTAN_FFI_INVALID_VERIFIER;
+ });
+#else
+ return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/ffi/ffi_keywrap.cpp b/comm/third_party/botan/src/lib/ffi/ffi_keywrap.cpp
new file mode 100644
index 0000000000..f74904cb7f
--- /dev/null
+++ b/comm/third_party/botan/src/lib/ffi/ffi_keywrap.cpp
@@ -0,0 +1,50 @@
+/*
+* (C) 2017 Ribose Inc
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/ffi.h>
+#include <botan/internal/ffi_util.h>
+
+#if defined(BOTAN_HAS_RFC3394_KEYWRAP)
+ #include <botan/rfc3394.h>
+#endif
+
+extern "C" {
+
+using namespace Botan_FFI;
+
+int botan_key_wrap3394(const uint8_t key[], size_t key_len,
+ const uint8_t kek[], size_t kek_len,
+ uint8_t wrapped_key[], size_t* wrapped_key_len)
+ {
+#if defined(BOTAN_HAS_RFC3394_KEYWRAP)
+ return ffi_guard_thunk(__func__, [=]() -> int {
+ const Botan::SymmetricKey kek_sym(kek, kek_len);
+ const Botan::secure_vector<uint8_t> key_pt(key, key + key_len);
+ const Botan::secure_vector<uint8_t> key_ct = Botan::rfc3394_keywrap(key_pt, kek_sym);
+ return write_vec_output(wrapped_key, wrapped_key_len, key_ct);
+ });
+#else
+ return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+ }
+
+int botan_key_unwrap3394(const uint8_t wrapped_key[], size_t wrapped_key_len,
+ const uint8_t kek[], size_t kek_len,
+ uint8_t key[], size_t* key_len)
+ {
+#if defined(BOTAN_HAS_RFC3394_KEYWRAP)
+ return ffi_guard_thunk(__func__, [=]() -> int {
+ const Botan::SymmetricKey kek_sym(kek, kek_len);
+ const Botan::secure_vector<uint8_t> key_ct(wrapped_key, wrapped_key + wrapped_key_len);
+ const Botan::secure_vector<uint8_t> key_pt = Botan::rfc3394_keyunwrap(key_ct, kek_sym);
+ return write_vec_output(key, key_len, key_pt);
+ });
+#else
+ return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/ffi/ffi_mac.cpp b/comm/third_party/botan/src/lib/ffi/ffi_mac.cpp
new file mode 100644
index 0000000000..3b6cc3bef4
--- /dev/null
+++ b/comm/third_party/botan/src/lib/ffi/ffi_mac.cpp
@@ -0,0 +1,85 @@
+/*
+* (C) 2015,2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/ffi.h>
+#include <botan/internal/ffi_util.h>
+#include <botan/mac.h>
+
+extern "C" {
+
+using namespace Botan_FFI;
+
+BOTAN_FFI_DECLARE_STRUCT(botan_mac_struct, Botan::MessageAuthenticationCode, 0xA06E8FC1);
+
+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)
+ return BOTAN_FFI_ERROR_NULL_POINTER;
+
+ std::unique_ptr<Botan::MessageAuthenticationCode> m =
+ Botan::MessageAuthenticationCode::create(mac_name);
+
+ if(m == nullptr)
+ return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+
+ *mac = new botan_mac_struct(m.release());
+ return BOTAN_FFI_SUCCESS;
+ });
+ }
+
+int botan_mac_destroy(botan_mac_t mac)
+ {
+ return BOTAN_FFI_CHECKED_DELETE(mac);
+ }
+
+int botan_mac_set_key(botan_mac_t mac, const uint8_t* key, size_t key_len)
+ {
+ return BOTAN_FFI_DO(Botan::MessageAuthenticationCode, mac, m, { m.set_key(key, key_len); });
+ }
+
+int botan_mac_output_length(botan_mac_t mac, size_t* out)
+ {
+ return BOTAN_FFI_DO(Botan::MessageAuthenticationCode, mac, m, { *out = m.output_length(); });
+ }
+
+int botan_mac_clear(botan_mac_t mac)
+ {
+ return BOTAN_FFI_DO(Botan::MessageAuthenticationCode, mac, m, { m.clear(); });
+ }
+
+int botan_mac_update(botan_mac_t mac, const uint8_t* buf, size_t len)
+ {
+ return BOTAN_FFI_DO(Botan::MessageAuthenticationCode, mac, m, { m.update(buf, len); });
+ }
+
+int botan_mac_final(botan_mac_t mac, uint8_t out[])
+ {
+ return BOTAN_FFI_DO(Botan::MessageAuthenticationCode, mac, m, { m.final(out); });
+ }
+
+int botan_mac_name(botan_mac_t mac, char* name, size_t* name_len)
+ {
+ return BOTAN_FFI_DO(Botan::MessageAuthenticationCode, mac, m, {
+ return write_str_output(name, name_len, m.name()); });
+ }
+
+int botan_mac_get_keyspec(botan_mac_t mac,
+ size_t* out_minimum_keylength,
+ size_t* out_maximum_keylength,
+ size_t* out_keylength_modulo)
+ {
+ return BOTAN_FFI_DO(Botan::MessageAuthenticationCode, mac, m, {
+ if(out_minimum_keylength)
+ *out_minimum_keylength = m.minimum_keylength();
+ if(out_maximum_keylength)
+ *out_maximum_keylength = m.maximum_keylength();
+ if(out_keylength_modulo)
+ *out_keylength_modulo = m.key_spec().keylength_multiple();
+ });
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/ffi/ffi_mp.cpp b/comm/third_party/botan/src/lib/ffi/ffi_mp.cpp
new file mode 100644
index 0000000000..68869e6ecf
--- /dev/null
+++ b/comm/third_party/botan/src/lib/ffi/ffi_mp.cpp
@@ -0,0 +1,312 @@
+/*
+* (C) 2015,2017 Jack Lloyd
+* (C) 2017 Ribose Inc
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/ffi.h>
+#include <botan/internal/ffi_util.h>
+#include <botan/internal/ffi_rng.h>
+#include <botan/internal/ffi_mp.h>
+#include <botan/reducer.h>
+#include <botan/numthry.h>
+#include <botan/divide.h>
+
+extern "C" {
+
+using namespace Botan_FFI;
+
+int botan_mp_init(botan_mp_t* mp_out)
+ {
+ return ffi_guard_thunk(__func__, [=]() -> int {
+ if(mp_out == nullptr)
+ return BOTAN_FFI_ERROR_NULL_POINTER;
+
+ *mp_out = new botan_mp_struct(new Botan::BigInt);
+ return BOTAN_FFI_SUCCESS;
+ });
+ }
+
+int botan_mp_clear(botan_mp_t mp)
+ {
+ return BOTAN_FFI_DO(Botan::BigInt, mp, bn, { bn.clear(); });
+ }
+
+int botan_mp_set_from_int(botan_mp_t mp, int initial_value)
+ {
+ return BOTAN_FFI_DO(Botan::BigInt, mp, bn, {
+ if(initial_value >= 0)
+ {
+ bn = Botan::BigInt(static_cast<uint64_t>(initial_value));
+ }
+ else
+ {
+ bn = Botan::BigInt(static_cast<uint64_t>(-initial_value));
+ bn.flip_sign();
+ }
+ });
+ }
+
+int botan_mp_set_from_str(botan_mp_t mp, const char* str)
+ {
+ return BOTAN_FFI_DO(Botan::BigInt, mp, bn, { bn = Botan::BigInt(str); });
+ }
+
+int botan_mp_set_from_radix_str(botan_mp_t mp, const char* str, size_t radix)
+ {
+ return BOTAN_FFI_DO(Botan::BigInt, mp, bn, {
+ Botan::BigInt::Base base;
+ if(radix == 10)
+ base = Botan::BigInt::Decimal;
+ else if(radix == 16)
+ base = Botan::BigInt::Hexadecimal;
+ else
+ 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);
+ });
+ }
+
+int botan_mp_set_from_mp(botan_mp_t dest, const botan_mp_t source)
+ {
+ return BOTAN_FFI_DO(Botan::BigInt, dest, bn, { bn = safe_get(source); });
+ }
+
+int botan_mp_is_negative(const botan_mp_t mp)
+ {
+ return BOTAN_FFI_DO(Botan::BigInt, mp, bn, { return bn.is_negative() ? 1 : 0; });
+ }
+
+int botan_mp_is_positive(const botan_mp_t mp)
+ {
+ return BOTAN_FFI_DO(Botan::BigInt, mp, bn, { return bn.is_positive() ? 1 : 0; });
+ }
+
+int botan_mp_flip_sign(botan_mp_t mp)
+ {
+ return BOTAN_FFI_DO(Botan::BigInt, mp, bn, { bn.flip_sign(); });
+ }
+
+int botan_mp_from_bin(botan_mp_t mp, const uint8_t bin[], size_t bin_len)
+ {
+ return BOTAN_FFI_DO(Botan::BigInt, mp, bn, { bn.binary_decode(bin, bin_len); });
+ }
+
+int botan_mp_to_hex(const botan_mp_t mp, char* out)
+ {
+ return BOTAN_FFI_DO(Botan::BigInt, mp, bn, {
+ const std::string hex = bn.to_hex_string();
+ 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)
+ {
+ return BOTAN_FFI_RETURNING(Botan::BigInt, mp, bn, {
+
+ if(digit_base == 0 || digit_base == 10)
+ return write_str_output(out, out_len, bn.to_dec_string());
+ else if(digit_base == 16)
+ return write_str_output(out, out_len, bn.to_hex_string());
+ else
+ return BOTAN_FFI_ERROR_BAD_PARAMETER;
+ });
+ }
+
+int botan_mp_to_bin(const botan_mp_t mp, uint8_t vec[])
+ {
+ return BOTAN_FFI_DO(Botan::BigInt, mp, bn, { bn.binary_encode(vec); });
+ }
+
+int botan_mp_to_uint32(const botan_mp_t mp, uint32_t* val)
+ {
+ if(val == nullptr)
+ {
+ return BOTAN_FFI_ERROR_NULL_POINTER;
+ }
+ return BOTAN_FFI_DO(Botan::BigInt, mp, bn, { *val = bn.to_u32bit(); });
+ }
+
+int botan_mp_destroy(botan_mp_t mp)
+ {
+ return BOTAN_FFI_CHECKED_DELETE(mp);
+ }
+
+int botan_mp_add(botan_mp_t result, const botan_mp_t x, const botan_mp_t y)
+ {
+ return BOTAN_FFI_DO(Botan::BigInt, result, res, {
+ if(result == x)
+ res += safe_get(y);
+ else
+ res = safe_get(x) + safe_get(y);
+ });
+ }
+
+int botan_mp_sub(botan_mp_t result, const botan_mp_t x, const botan_mp_t y)
+ {
+ return BOTAN_FFI_DO(Botan::BigInt, result, res, {
+ if(result == x)
+ res -= safe_get(y);
+ else
+ res = safe_get(x) - safe_get(y);
+ });
+ }
+
+int botan_mp_add_u32(botan_mp_t result, const botan_mp_t x, uint32_t y)
+ {
+ return BOTAN_FFI_DO(Botan::BigInt, result, res, {
+ if(result == x)
+ res += static_cast<Botan::word>(y);
+ else
+ res = safe_get(x) + static_cast<Botan::word>(y);
+ });
+ }
+
+int botan_mp_sub_u32(botan_mp_t result, const botan_mp_t x, uint32_t y)
+ {
+ return BOTAN_FFI_DO(Botan::BigInt, result, res, {
+ if(result == x)
+ res -= static_cast<Botan::word>(y);
+ else
+ res = safe_get(x) - static_cast<Botan::word>(y);
+ });
+ }
+
+int botan_mp_mul(botan_mp_t result, const botan_mp_t x, const botan_mp_t y)
+ {
+ return BOTAN_FFI_DO(Botan::BigInt, result, res, {
+ if(result == x)
+ res *= safe_get(y);
+ else
+ res = safe_get(x) * safe_get(y);
+ });
+ }
+
+int botan_mp_div(botan_mp_t quotient,
+ botan_mp_t remainder,
+ const botan_mp_t x, const botan_mp_t y)
+ {
+ return BOTAN_FFI_DO(Botan::BigInt, quotient, q, {
+ Botan::BigInt r;
+ Botan::vartime_divide(safe_get(x), safe_get(y), q, r);
+ safe_get(remainder) = r;
+ });
+ }
+
+int botan_mp_equal(const botan_mp_t x_w, const botan_mp_t y_w)
+ {
+ return BOTAN_FFI_RETURNING(Botan::BigInt, x_w, x, { return x == safe_get(y_w); });
+ }
+
+int botan_mp_is_zero(const botan_mp_t mp)
+ {
+ return BOTAN_FFI_RETURNING(Botan::BigInt, mp, bn, { return bn.is_zero(); });
+ }
+
+int botan_mp_is_odd(const botan_mp_t mp)
+ {
+ return BOTAN_FFI_RETURNING(Botan::BigInt, mp, bn, { return bn.is_odd(); });
+ }
+
+int botan_mp_is_even(const botan_mp_t mp)
+ {
+ return BOTAN_FFI_RETURNING(Botan::BigInt, mp, bn, { return bn.is_even(); });
+ }
+
+int botan_mp_cmp(int* result, const botan_mp_t x_w, const botan_mp_t y_w)
+ {
+ return BOTAN_FFI_DO(Botan::BigInt, x_w, x, { *result = x.cmp(safe_get(y_w)); });
+ }
+
+int botan_mp_swap(botan_mp_t x_w, botan_mp_t y_w)
+ {
+ return BOTAN_FFI_DO(Botan::BigInt, x_w, x, { x.swap(safe_get(y_w)); });
+ }
+
+// Return (base^exponent) % modulus
+int botan_mp_powmod(botan_mp_t out, const botan_mp_t base, const botan_mp_t exponent, const botan_mp_t modulus)
+ {
+ return BOTAN_FFI_DO(Botan::BigInt, out, o,
+ { o = Botan::power_mod(safe_get(base), safe_get(exponent), safe_get(modulus)); });
+ }
+
+int botan_mp_lshift(botan_mp_t out, const botan_mp_t in, size_t shift)
+ {
+ return BOTAN_FFI_DO(Botan::BigInt, out, o, { o = safe_get(in) << shift; });
+ }
+
+int botan_mp_rshift(botan_mp_t out, const botan_mp_t in, size_t shift)
+ {
+ return BOTAN_FFI_DO(Botan::BigInt, out, o, { o = safe_get(in) >> shift; });
+ }
+
+int botan_mp_mod_inverse(botan_mp_t out, const botan_mp_t in, const botan_mp_t modulus)
+ {
+ return BOTAN_FFI_DO(Botan::BigInt, out, o, { o = Botan::inverse_mod(safe_get(in), safe_get(modulus)); });
+ }
+
+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_DO(Botan::BigInt, out, o, {
+ Botan::Modular_Reducer reducer(safe_get(modulus));
+ o = reducer.multiply(safe_get(x), safe_get(y));
+ });
+ }
+
+int botan_mp_rand_bits(botan_mp_t rand_out, botan_rng_t rng, size_t bits)
+ {
+ return BOTAN_FFI_DO(Botan::RandomNumberGenerator, rng, r, {
+ safe_get(rand_out).randomize(r, bits); });
+ }
+
+int botan_mp_rand_range(botan_mp_t rand_out,
+ botan_rng_t rng,
+ const botan_mp_t lower,
+ const botan_mp_t upper)
+ {
+ return BOTAN_FFI_DO(Botan::RandomNumberGenerator, rng, r, {
+ safe_get(rand_out) = Botan::BigInt::random_integer(r, safe_get(lower), safe_get(upper)); });
+ }
+
+int botan_mp_gcd(botan_mp_t out, const botan_mp_t x, const botan_mp_t y)
+ {
+ return BOTAN_FFI_DO(Botan::BigInt, out, o, {
+ o = Botan::gcd(safe_get(x), safe_get(y)); });
+ }
+
+int botan_mp_is_prime(const botan_mp_t mp, botan_rng_t rng, size_t test_prob)
+ {
+ return BOTAN_FFI_RETURNING(Botan::BigInt, mp, n,
+ { return (Botan::is_prime(n, safe_get(rng), test_prob)) ? 1 : 0; });
+ }
+
+int botan_mp_get_bit(const botan_mp_t mp, size_t bit)
+ {
+ return BOTAN_FFI_RETURNING(Botan::BigInt, mp, n, { return (n.get_bit(bit)); });
+ }
+
+int botan_mp_set_bit(botan_mp_t mp, size_t bit)
+ {
+ return BOTAN_FFI_DO(Botan::BigInt, mp, n, { n.set_bit(bit); });
+ }
+
+int botan_mp_clear_bit(botan_mp_t mp, size_t bit)
+ {
+ return BOTAN_FFI_DO(Botan::BigInt, mp, n, { n.clear_bit(bit); });
+ }
+
+int botan_mp_num_bits(const botan_mp_t mp, size_t* bits)
+ {
+ return BOTAN_FFI_DO(Botan::BigInt, mp, n, { *bits = n.bits(); });
+ }
+
+int botan_mp_num_bytes(const botan_mp_t mp, size_t* bytes)
+ {
+ return BOTAN_FFI_DO(Botan::BigInt, mp, n, { *bytes = n.bytes(); });
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/ffi/ffi_mp.h b/comm/third_party/botan/src/lib/ffi/ffi_mp.h
new file mode 100644
index 0000000000..3cc85b66a3
--- /dev/null
+++ b/comm/third_party/botan/src/lib/ffi/ffi_mp.h
@@ -0,0 +1,19 @@
+/*
+* (C) 2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_FFI_MP_H_
+#define BOTAN_FFI_MP_H_
+
+#include <botan/bigint.h>
+#include <botan/internal/ffi_util.h>
+
+extern "C" {
+
+BOTAN_FFI_DECLARE_STRUCT(botan_mp_struct, Botan::BigInt, 0xC828B9D2);
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/ffi/ffi_pk_op.cpp b/comm/third_party/botan/src/lib/ffi/ffi_pk_op.cpp
new file mode 100644
index 0000000000..2efb297aca
--- /dev/null
+++ b/comm/third_party/botan/src/lib/ffi/ffi_pk_op.cpp
@@ -0,0 +1,255 @@
+/*
+* (C) 2015,2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/ffi.h>
+#include <botan/internal/ffi_util.h>
+#include <botan/internal/ffi_pkey.h>
+#include <botan/internal/ffi_rng.h>
+#include <botan/pubkey.h>
+
+extern "C" {
+
+using namespace Botan_FFI;
+
+BOTAN_FFI_DECLARE_STRUCT(botan_pk_op_encrypt_struct, Botan::PK_Encryptor, 0x891F3FC3);
+BOTAN_FFI_DECLARE_STRUCT(botan_pk_op_decrypt_struct, Botan::PK_Decryptor, 0x912F3C37);
+BOTAN_FFI_DECLARE_STRUCT(botan_pk_op_sign_struct, Botan::PK_Signer, 0x1AF0C39F);
+BOTAN_FFI_DECLARE_STRUCT(botan_pk_op_verify_struct, Botan::PK_Verifier, 0x2B91F936);
+BOTAN_FFI_DECLARE_STRUCT(botan_pk_op_ka_struct, Botan::PK_Key_Agreement, 0x2939CAB1);
+
+int botan_pk_op_encrypt_create(botan_pk_op_encrypt_t* op,
+ botan_pubkey_t key_obj,
+ const char* padding,
+ uint32_t flags)
+ {
+ if(op == nullptr)
+ return BOTAN_FFI_ERROR_NULL_POINTER;
+
+ if(flags != 0 && flags != BOTAN_PUBKEY_DER_FORMAT_SIGNATURE)
+ return BOTAN_FFI_ERROR_BAD_FLAG;
+
+ return ffi_guard_thunk(__func__, [=]() -> int {
+ *op = nullptr;
+
+ std::unique_ptr<Botan::PK_Encryptor> pk(new Botan::PK_Encryptor_EME(safe_get(key_obj), Botan::system_rng(), padding));
+ *op = new botan_pk_op_encrypt_struct(pk.release());
+ return BOTAN_FFI_SUCCESS;
+ });
+ }
+
+int botan_pk_op_encrypt_destroy(botan_pk_op_encrypt_t op)
+ {
+ return BOTAN_FFI_CHECKED_DELETE(op);
+ }
+
+int botan_pk_op_encrypt_output_length(botan_pk_op_encrypt_t op, size_t ptext_len, size_t* ctext_len)
+ {
+ if(ctext_len == nullptr)
+ return BOTAN_FFI_ERROR_NULL_POINTER;
+ return BOTAN_FFI_DO(Botan::PK_Encryptor, op, o, { *ctext_len = o.ciphertext_length(ptext_len); });
+ }
+
+int botan_pk_op_encrypt(botan_pk_op_encrypt_t op,
+ botan_rng_t rng_obj,
+ uint8_t out[], size_t* out_len,
+ const uint8_t plaintext[], size_t plaintext_len)
+ {
+ return BOTAN_FFI_DO(Botan::PK_Encryptor, op, o, {
+ return write_vec_output(out, out_len, o.encrypt(plaintext, plaintext_len, safe_get(rng_obj)));
+ });
+ }
+
+/*
+* Public Key Decryption
+*/
+int botan_pk_op_decrypt_create(botan_pk_op_decrypt_t* op,
+ botan_privkey_t key_obj,
+ const char* padding,
+ uint32_t flags)
+ {
+ if(op == nullptr)
+ return BOTAN_FFI_ERROR_NULL_POINTER;
+
+ if(flags != 0)
+ return BOTAN_FFI_ERROR_BAD_FLAG;
+
+ return ffi_guard_thunk(__func__, [=]() -> int {
+ *op = nullptr;
+
+ std::unique_ptr<Botan::PK_Decryptor> pk(new Botan::PK_Decryptor_EME(safe_get(key_obj), Botan::system_rng(), padding));
+ *op = new botan_pk_op_decrypt_struct(pk.release());
+ return BOTAN_FFI_SUCCESS;
+ });
+ }
+
+int botan_pk_op_decrypt_destroy(botan_pk_op_decrypt_t op)
+ {
+ return BOTAN_FFI_CHECKED_DELETE(op);
+ }
+
+int botan_pk_op_decrypt_output_length(botan_pk_op_decrypt_t op, size_t ctext_len, size_t* ptext_len)
+ {
+ if(ptext_len == nullptr)
+ return BOTAN_FFI_ERROR_NULL_POINTER;
+ return BOTAN_FFI_DO(Botan::PK_Decryptor, op, o, { *ptext_len = o.plaintext_length(ctext_len); });
+ }
+
+int botan_pk_op_decrypt(botan_pk_op_decrypt_t op,
+ uint8_t out[], size_t* out_len,
+ const uint8_t ciphertext[], size_t ciphertext_len)
+ {
+ return BOTAN_FFI_DO(Botan::PK_Decryptor, op, o, {
+ return write_vec_output(out, out_len, o.decrypt(ciphertext, ciphertext_len));
+ });
+ }
+
+/*
+* Signature Generation
+*/
+int botan_pk_op_sign_create(botan_pk_op_sign_t* op,
+ botan_privkey_t key_obj,
+ const char* hash,
+ uint32_t flags)
+ {
+ if(op == nullptr)
+ return BOTAN_FFI_ERROR_NULL_POINTER;
+
+ if(flags != 0 && flags != BOTAN_PUBKEY_DER_FORMAT_SIGNATURE)
+ return BOTAN_FFI_ERROR_BAD_FLAG;
+
+ return ffi_guard_thunk(__func__, [=]() -> int {
+ *op = nullptr;
+
+ auto format = (flags & BOTAN_PUBKEY_DER_FORMAT_SIGNATURE) ? Botan::DER_SEQUENCE : Botan::IEEE_1363;
+
+ std::unique_ptr<Botan::PK_Signer> pk(new Botan::PK_Signer(safe_get(key_obj), Botan::system_rng(), hash, format));
+ *op = new botan_pk_op_sign_struct(pk.release());
+ return BOTAN_FFI_SUCCESS;
+ });
+ }
+
+int botan_pk_op_sign_destroy(botan_pk_op_sign_t op)
+ {
+ return BOTAN_FFI_CHECKED_DELETE(op);
+ }
+
+int botan_pk_op_sign_output_length(botan_pk_op_sign_t op, size_t* sig_len)
+ {
+ if(sig_len == nullptr)
+ return BOTAN_FFI_ERROR_NULL_POINTER;
+
+ return BOTAN_FFI_DO(Botan::PK_Signer, op, o, { *sig_len = o.signature_length(); });
+ }
+
+int botan_pk_op_sign_update(botan_pk_op_sign_t op, const uint8_t in[], size_t in_len)
+ {
+ return BOTAN_FFI_DO(Botan::PK_Signer, op, o, { o.update(in, in_len); });
+ }
+
+int botan_pk_op_sign_finish(botan_pk_op_sign_t op, botan_rng_t rng_obj, uint8_t out[], size_t* out_len)
+ {
+ return BOTAN_FFI_DO(Botan::PK_Signer, op, o, {
+ return write_vec_output(out, out_len, o.signature(safe_get(rng_obj)));
+ });
+ }
+
+int botan_pk_op_verify_create(botan_pk_op_verify_t* op,
+ botan_pubkey_t key_obj,
+ const char* hash,
+ uint32_t flags)
+ {
+ if(op == nullptr)
+ return BOTAN_FFI_ERROR_NULL_POINTER;
+
+ if(flags != 0 && flags != BOTAN_PUBKEY_DER_FORMAT_SIGNATURE)
+ return BOTAN_FFI_ERROR_BAD_FLAG;
+
+ return ffi_guard_thunk(__func__, [=]() -> int {
+ *op = nullptr;
+ auto format = (flags & BOTAN_PUBKEY_DER_FORMAT_SIGNATURE) ? Botan::DER_SEQUENCE : Botan::IEEE_1363;
+ std::unique_ptr<Botan::PK_Verifier> pk(new Botan::PK_Verifier(safe_get(key_obj), hash, format));
+ *op = new botan_pk_op_verify_struct(pk.release());
+ return BOTAN_FFI_SUCCESS;
+ });
+ }
+
+int botan_pk_op_verify_destroy(botan_pk_op_verify_t op)
+ {
+ return BOTAN_FFI_CHECKED_DELETE(op);
+ }
+
+int botan_pk_op_verify_update(botan_pk_op_verify_t op, const uint8_t in[], size_t in_len)
+ {
+ return BOTAN_FFI_DO(Botan::PK_Verifier, op, o, { o.update(in, in_len); });
+ }
+
+int botan_pk_op_verify_finish(botan_pk_op_verify_t op, const uint8_t sig[], size_t sig_len)
+ {
+ return BOTAN_FFI_RETURNING(Botan::PK_Verifier, op, o, {
+ const bool legit = o.check_signature(sig, sig_len);
+
+ if(legit)
+ return BOTAN_FFI_SUCCESS;
+ else
+ return BOTAN_FFI_INVALID_VERIFIER;
+ });
+ }
+
+int botan_pk_op_key_agreement_create(botan_pk_op_ka_t* op,
+ botan_privkey_t key_obj,
+ const char* kdf,
+ uint32_t flags)
+ {
+ if(op == nullptr)
+ return BOTAN_FFI_ERROR_NULL_POINTER;
+
+ if(flags != 0)
+ return BOTAN_FFI_ERROR_BAD_FLAG;
+
+ return ffi_guard_thunk(__func__, [=]() -> int {
+ *op = nullptr;
+ std::unique_ptr<Botan::PK_Key_Agreement> pk(new Botan::PK_Key_Agreement(safe_get(key_obj), Botan::system_rng(), kdf));
+ *op = new botan_pk_op_ka_struct(pk.release());
+ return BOTAN_FFI_SUCCESS;
+ });
+ }
+
+int botan_pk_op_key_agreement_destroy(botan_pk_op_ka_t op)
+ {
+ return BOTAN_FFI_CHECKED_DELETE(op);
+ }
+
+int botan_pk_op_key_agreement_export_public(botan_privkey_t key,
+ uint8_t out[], size_t* out_len)
+ {
+ return BOTAN_FFI_DO(Botan::Private_Key, key, k, {
+ if(auto kak = dynamic_cast<const Botan::PK_Key_Agreement_Key*>(&k))
+ return write_vec_output(out, out_len, kak->public_value());
+ return BOTAN_FFI_ERROR_BAD_FLAG;
+ });
+ }
+
+int botan_pk_op_key_agreement_size(botan_pk_op_ka_t op, size_t* out_len)
+ {
+ return BOTAN_FFI_DO(Botan::PK_Key_Agreement, op, o, {
+ if(out_len == nullptr)
+ return BOTAN_FFI_ERROR_NULL_POINTER;
+ *out_len = o.agreed_value_size();
+ });
+ }
+
+int botan_pk_op_key_agreement(botan_pk_op_ka_t op,
+ uint8_t out[], size_t* out_len,
+ const uint8_t other_key[], size_t other_key_len,
+ const uint8_t salt[], size_t salt_len)
+ {
+ return BOTAN_FFI_DO(Botan::PK_Key_Agreement, op, o, {
+ auto k = o.derive_key(*out_len, other_key, other_key_len, salt, salt_len).bits_of();
+ return write_vec_output(out, out_len, k);
+ });
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/ffi/ffi_pkey.cpp b/comm/third_party/botan/src/lib/ffi/ffi_pkey.cpp
new file mode 100644
index 0000000000..2fb3d338e2
--- /dev/null
+++ b/comm/third_party/botan/src/lib/ffi/ffi_pkey.cpp
@@ -0,0 +1,279 @@
+/*
+* (C) 2015,2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/ffi.h>
+#include <botan/internal/ffi_util.h>
+#include <botan/internal/ffi_pkey.h>
+#include <botan/internal/ffi_rng.h>
+#include <botan/data_src.h>
+#include <botan/hash.h>
+#include <botan/pkcs8.h>
+#include <botan/pk_keys.h>
+#include <botan/x509_key.h>
+#include <botan/pk_algs.h>
+
+#if defined(BOTAN_HAS_HASH_ID)
+ #include <botan/hash_id.h>
+#endif
+
+extern "C" {
+
+using namespace Botan_FFI;
+
+int botan_privkey_create(botan_privkey_t* key_obj,
+ const char* algo_name,
+ const char* algo_params,
+ botan_rng_t rng_obj)
+ {
+ return ffi_guard_thunk(__func__, [=]() -> int {
+ if(key_obj == nullptr)
+ return BOTAN_FFI_ERROR_NULL_POINTER;
+
+ *key_obj = nullptr;
+ if(rng_obj == nullptr)
+ return BOTAN_FFI_ERROR_NULL_POINTER;
+
+ Botan::RandomNumberGenerator& rng = safe_get(rng_obj);
+ std::unique_ptr<Botan::Private_Key> key(
+ Botan::create_private_key(algo_name ? algo_name : "RSA",
+ rng,
+ algo_params ? algo_params : ""));
+
+ if(key)
+ {
+ *key_obj = new botan_privkey_struct(key.release());
+ return BOTAN_FFI_SUCCESS;
+ }
+ else
+ {
+ return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+ }
+ });
+ }
+
+int botan_privkey_load(botan_privkey_t* key, botan_rng_t rng_obj,
+ const uint8_t bits[], size_t len,
+ const char* password)
+ {
+ BOTAN_UNUSED(rng_obj);
+
+ *key = nullptr;
+
+ return ffi_guard_thunk(__func__, [=]() -> int {
+ Botan::DataSource_Memory src(bits, len);
+
+ std::unique_ptr<Botan::Private_Key> pkcs8;
+
+ if(password == nullptr)
+ {
+ pkcs8 = Botan::PKCS8::load_key(src);
+ }
+ else
+ {
+ pkcs8 = Botan::PKCS8::load_key(src, std::string(password));
+ }
+
+ if(pkcs8)
+ {
+ *key = new botan_privkey_struct(pkcs8.release());
+ return BOTAN_FFI_SUCCESS;
+ }
+ return BOTAN_FFI_ERROR_UNKNOWN_ERROR;
+ });
+ }
+
+int botan_privkey_destroy(botan_privkey_t key)
+ {
+ return BOTAN_FFI_CHECKED_DELETE(key);
+ }
+
+int botan_pubkey_load(botan_pubkey_t* key,
+ const uint8_t bits[], size_t bits_len)
+ {
+ *key = nullptr;
+
+ return ffi_guard_thunk(__func__, [=]() -> int {
+ Botan::DataSource_Memory src(bits, bits_len);
+ std::unique_ptr<Botan::Public_Key> pubkey(Botan::X509::load_key(src));
+
+ if(pubkey == nullptr)
+ return BOTAN_FFI_ERROR_UNKNOWN_ERROR;
+
+ *key = new botan_pubkey_struct(pubkey.release());
+ return BOTAN_FFI_SUCCESS;
+ });
+ }
+
+int botan_pubkey_destroy(botan_pubkey_t key)
+ {
+ return BOTAN_FFI_CHECKED_DELETE(key);
+ }
+
+int botan_privkey_export_pubkey(botan_pubkey_t* pubout, botan_privkey_t key_obj)
+ {
+ return ffi_guard_thunk(__func__, [=]() -> int {
+ std::unique_ptr<Botan::Public_Key>
+ pubkey(Botan::X509::load_key(Botan::X509::BER_encode(safe_get(key_obj))));
+
+ *pubout = new botan_pubkey_struct(pubkey.release());
+ return BOTAN_FFI_SUCCESS;
+ });
+ }
+
+int botan_privkey_algo_name(botan_privkey_t key, char out[], size_t* out_len)
+ {
+ return BOTAN_FFI_DO(Botan::Private_Key, key, k, { return write_str_output(out, out_len, k.algo_name()); });
+ }
+
+int botan_pubkey_algo_name(botan_pubkey_t key, char out[], size_t* out_len)
+ {
+ return BOTAN_FFI_DO(Botan::Public_Key, key, k, { return write_str_output(out, out_len, k.algo_name()); });
+ }
+
+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);
+
+ return BOTAN_FFI_RETURNING(Botan::Public_Key, key, k, {
+ return (k.check_key(safe_get(rng), strong) == true) ? 0 : BOTAN_FFI_ERROR_INVALID_INPUT;
+ });
+ }
+
+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);
+ return BOTAN_FFI_RETURNING(Botan::Private_Key, key, k, {
+ return (k.check_key(safe_get(rng), strong) == true) ? 0 : BOTAN_FFI_ERROR_INVALID_INPUT;
+ });
+ }
+
+int botan_pubkey_export(botan_pubkey_t key, uint8_t out[], size_t* out_len, uint32_t flags)
+ {
+ return BOTAN_FFI_DO(Botan::Public_Key, key, k, {
+ if(flags == BOTAN_PRIVKEY_EXPORT_FLAG_DER)
+ return write_vec_output(out, out_len, Botan::X509::BER_encode(k));
+ else if(flags == BOTAN_PRIVKEY_EXPORT_FLAG_PEM)
+ return write_str_output(out, out_len, Botan::X509::PEM_encode(k));
+ else
+ return BOTAN_FFI_ERROR_BAD_FLAG;
+ });
+ }
+
+int botan_privkey_export(botan_privkey_t key, uint8_t out[], size_t* out_len, uint32_t flags)
+ {
+ return BOTAN_FFI_DO(Botan::Private_Key, key, k, {
+ if(flags == BOTAN_PRIVKEY_EXPORT_FLAG_DER)
+ return write_vec_output(out, out_len, Botan::PKCS8::BER_encode(k));
+ else if(flags == BOTAN_PRIVKEY_EXPORT_FLAG_PEM)
+ return write_str_output(out, out_len, Botan::PKCS8::PEM_encode(k));
+ else
+ return BOTAN_FFI_ERROR_BAD_FLAG;
+ });
+ }
+
+int botan_privkey_export_encrypted(botan_privkey_t key,
+ uint8_t out[], size_t* out_len,
+ botan_rng_t rng_obj,
+ const char* pass,
+ const char* /*ignored - pbe*/,
+ uint32_t flags)
+ {
+ return botan_privkey_export_encrypted_pbkdf_iter(key, out, out_len, rng_obj, pass, 100000, nullptr, nullptr, flags);
+ }
+
+int botan_privkey_export_encrypted_pbkdf_msec(botan_privkey_t key,
+ uint8_t out[], size_t* out_len,
+ botan_rng_t rng_obj,
+ const char* pass,
+ uint32_t pbkdf_msec,
+ size_t* pbkdf_iters_out,
+ const char* maybe_cipher,
+ const char* maybe_pbkdf_hash,
+ uint32_t flags)
+ {
+ return BOTAN_FFI_DO(Botan::Private_Key, key, k, {
+ const std::chrono::milliseconds pbkdf_time(pbkdf_msec);
+ Botan::RandomNumberGenerator& rng = safe_get(rng_obj);
+
+ const std::string cipher = (maybe_cipher ? maybe_cipher : "");
+ const std::string pbkdf_hash = (maybe_pbkdf_hash ? maybe_pbkdf_hash : "");
+
+ if(flags == BOTAN_PRIVKEY_EXPORT_FLAG_DER)
+ {
+ return write_vec_output(out, out_len,
+ Botan::PKCS8::BER_encode_encrypted_pbkdf_msec(k, rng, pass, pbkdf_time, pbkdf_iters_out, cipher, pbkdf_hash));
+ }
+ else if(flags == BOTAN_PRIVKEY_EXPORT_FLAG_PEM)
+ {
+ return write_str_output(out, out_len,
+ Botan::PKCS8::PEM_encode_encrypted_pbkdf_msec(k, rng, pass, pbkdf_time, pbkdf_iters_out, cipher, pbkdf_hash));
+ }
+ else
+ {
+ return -2;
+ }
+ });
+ }
+
+int botan_privkey_export_encrypted_pbkdf_iter(botan_privkey_t key,
+ uint8_t out[], size_t* out_len,
+ botan_rng_t rng_obj,
+ const char* pass,
+ size_t pbkdf_iter,
+ const char* maybe_cipher,
+ const char* maybe_pbkdf_hash,
+ uint32_t flags)
+ {
+ return BOTAN_FFI_DO(Botan::Private_Key, key, k, {
+ Botan::RandomNumberGenerator& rng = safe_get(rng_obj);
+
+ const std::string cipher = (maybe_cipher ? maybe_cipher : "");
+ const std::string pbkdf_hash = (maybe_pbkdf_hash ? maybe_pbkdf_hash : "");
+
+ if(flags == BOTAN_PRIVKEY_EXPORT_FLAG_DER)
+ {
+ return write_vec_output(out, out_len,
+ Botan::PKCS8::BER_encode_encrypted_pbkdf_iter(k, rng, pass, pbkdf_iter, cipher, pbkdf_hash));
+ }
+ else if(flags == BOTAN_PRIVKEY_EXPORT_FLAG_PEM)
+ {
+ return write_str_output(out, out_len,
+ Botan::PKCS8::PEM_encode_encrypted_pbkdf_iter(k, rng, pass, pbkdf_iter, cipher, pbkdf_hash));
+ }
+ else
+ {
+ return -2;
+ }
+ });
+ }
+
+int botan_pubkey_estimated_strength(botan_pubkey_t key, size_t* estimate)
+ {
+ return BOTAN_FFI_DO(Botan::Public_Key, key, k, { *estimate = k.estimated_strength(); });
+ }
+
+int botan_pubkey_fingerprint(botan_pubkey_t key, const char* hash_fn,
+ uint8_t out[], size_t* out_len)
+ {
+ return BOTAN_FFI_DO(Botan::Public_Key, key, k, {
+ std::unique_ptr<Botan::HashFunction> h(Botan::HashFunction::create(hash_fn));
+ return write_vec_output(out, out_len, h->process(k.public_key_bits()));
+ });
+ }
+
+int botan_pkcs_hash_id(const char* hash_name, uint8_t pkcs_id[], size_t* pkcs_id_len)
+ {
+#if defined(BOTAN_HAS_HASH_ID)
+ return ffi_guard_thunk(__func__, [=]() -> int {
+ const std::vector<uint8_t> hash_id = Botan::pkcs_hash_id(hash_name);
+ return write_output(pkcs_id, pkcs_id_len, hash_id.data(), hash_id.size());
+ });
+#else
+ return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/ffi/ffi_pkey.h b/comm/third_party/botan/src/lib/ffi/ffi_pkey.h
new file mode 100644
index 0000000000..de5e19dd08
--- /dev/null
+++ b/comm/third_party/botan/src/lib/ffi/ffi_pkey.h
@@ -0,0 +1,20 @@
+/*
+* (C) 2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_FFI_PKEY_H_
+#define BOTAN_FFI_PKEY_H_
+
+#include <botan/pk_keys.h>
+#include <botan/internal/ffi_util.h>
+
+extern "C" {
+
+BOTAN_FFI_DECLARE_STRUCT(botan_pubkey_struct, Botan::Public_Key, 0x2C286519);
+BOTAN_FFI_DECLARE_STRUCT(botan_privkey_struct, Botan::Private_Key, 0x7F96385E);
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/ffi/ffi_pkey_algs.cpp b/comm/third_party/botan/src/lib/ffi/ffi_pkey_algs.cpp
new file mode 100644
index 0000000000..9f5d543100
--- /dev/null
+++ b/comm/third_party/botan/src/lib/ffi/ffi_pkey_algs.cpp
@@ -0,0 +1,980 @@
+/*
+* (C) 2015,2017 Jack Lloyd
+* (C) 2017 Ribose Inc
+* (C) 2018 René Korthaus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/ffi.h>
+#include <botan/hash.h>
+#include <botan/pem.h>
+#include <botan/internal/ffi_util.h>
+#include <botan/internal/ffi_pkey.h>
+#include <botan/internal/ffi_rng.h>
+#include <botan/internal/ffi_mp.h>
+
+#if defined(BOTAN_HAS_ECC_PUBLIC_KEY_CRYPTO)
+ #include <botan/ecc_key.h>
+#endif
+
+#if defined(BOTAN_HAS_DL_PUBLIC_KEY_FAMILY)
+ #include <botan/dl_algo.h>
+#endif
+
+#if defined(BOTAN_HAS_RSA)
+ #include <botan/rsa.h>
+#endif
+
+#if defined(BOTAN_HAS_ELGAMAL)
+ #include <botan/elgamal.h>
+#endif
+
+#if defined(BOTAN_HAS_DSA)
+ #include <botan/dsa.h>
+#endif
+
+#if defined(BOTAN_HAS_ECDSA)
+ #include <botan/ecdsa.h>
+#endif
+
+#if defined(BOTAN_HAS_SM2)
+ #include <botan/sm2.h>
+#endif
+
+#if defined(BOTAN_HAS_ECDH)
+ #include <botan/ecdh.h>
+#endif
+
+#if defined(BOTAN_HAS_CURVE_25519)
+ #include <botan/curve25519.h>
+#endif
+
+#if defined(BOTAN_HAS_ED25519)
+ #include <botan/ed25519.h>
+#endif
+
+#if defined(BOTAN_HAS_MCELIECE)
+ #include <botan/mceliece.h>
+#endif
+
+#if defined(BOTAN_HAS_MCEIES)
+ #include <botan/mceies.h>
+#endif
+
+#if defined(BOTAN_HAS_DIFFIE_HELLMAN)
+ #include <botan/dh.h>
+#endif
+
+
+namespace {
+
+#if defined(BOTAN_HAS_ECC_PUBLIC_KEY_CRYPTO)
+
+// These are always called within an existing try/catch block
+
+template<class ECPrivateKey_t>
+int privkey_load_ec(std::unique_ptr<ECPrivateKey_t>& key,
+ const Botan::BigInt& scalar,
+ const char* curve_name)
+ {
+ if(curve_name == nullptr)
+ return BOTAN_FFI_ERROR_NULL_POINTER;
+
+ Botan::Null_RNG null_rng;
+ Botan::EC_Group grp(curve_name);
+ key.reset(new ECPrivateKey_t(null_rng, grp, scalar));
+ return BOTAN_FFI_SUCCESS;
+ }
+
+template<class ECPublicKey_t>
+int pubkey_load_ec(std::unique_ptr<ECPublicKey_t>& key,
+ const Botan::BigInt& public_x,
+ const Botan::BigInt& public_y,
+ const char* curve_name)
+ {
+ if(curve_name == nullptr)
+ return BOTAN_FFI_ERROR_NULL_POINTER;
+
+ Botan::EC_Group grp(curve_name);
+ Botan::PointGFp uncompressed_point = grp.point(public_x, public_y);
+ key.reset(new ECPublicKey_t(grp, uncompressed_point));
+ return BOTAN_FFI_SUCCESS;
+ }
+
+#endif
+
+Botan::BigInt pubkey_get_field(const Botan::Public_Key& key,
+ const std::string& field)
+ {
+ // Maybe this should be `return key.get_integer_field(field_name)`?
+
+#if defined(BOTAN_HAS_RSA)
+ if(const Botan::RSA_PublicKey* rsa = dynamic_cast<const Botan::RSA_PublicKey*>(&key))
+ {
+ if(field == "n")
+ return rsa->get_n();
+ else if(field == "e")
+ return rsa->get_e();
+ else
+ throw Botan_FFI::FFI_Error("Bad field", BOTAN_FFI_ERROR_BAD_PARAMETER);
+ }
+#endif
+
+#if defined(BOTAN_HAS_DL_PUBLIC_KEY_FAMILY)
+ // Handles DSA, ElGamal, etc
+ if(const Botan::DL_Scheme_PublicKey* dl = dynamic_cast<const Botan::DL_Scheme_PublicKey*>(&key))
+ {
+ if(field == "p")
+ return dl->group_p();
+ else if(field == "q")
+ return dl->group_q();
+ else if(field == "g")
+ return dl->group_g();
+ else if(field == "y")
+ return dl->get_y();
+ else
+ throw Botan_FFI::FFI_Error("Bad field", BOTAN_FFI_ERROR_BAD_PARAMETER);
+ }
+#endif
+
+#if defined(BOTAN_HAS_ECC_PUBLIC_KEY_CRYPTO)
+ if(const Botan::EC_PublicKey* ecc = dynamic_cast<const Botan::EC_PublicKey*>(&key))
+ {
+ if(field == "public_x")
+ return ecc->public_point().get_affine_x();
+ else if(field == "public_y")
+ return ecc->public_point().get_affine_y();
+ else if(field == "base_x")
+ return ecc->domain().get_g_x();
+ else if(field == "base_y")
+ return ecc->domain().get_g_y();
+ else if(field == "p")
+ return ecc->domain().get_p();
+ else if(field == "a")
+ return ecc->domain().get_a();
+ else if(field == "b")
+ return ecc->domain().get_b();
+ else if(field == "cofactor")
+ return ecc->domain().get_cofactor();
+ else if(field == "order")
+ return ecc->domain().get_order();
+ else
+ throw Botan_FFI::FFI_Error("Bad field", BOTAN_FFI_ERROR_BAD_PARAMETER);
+ }
+#endif
+
+ // Some other algorithm type not supported by this function
+ throw Botan_FFI::FFI_Error("Field getter not implemented for this algorithm type",
+ BOTAN_FFI_ERROR_NOT_IMPLEMENTED);
+ }
+
+Botan::BigInt privkey_get_field(const Botan::Private_Key& key,
+ const std::string& field)
+ {
+ //return key.get_integer_field(field);
+
+#if defined(BOTAN_HAS_RSA)
+
+ if(const Botan::RSA_PrivateKey* rsa = dynamic_cast<const Botan::RSA_PrivateKey*>(&key))
+ {
+ if(field == "p")
+ return rsa->get_p();
+ else if(field == "q")
+ return rsa->get_q();
+ else if(field == "d")
+ return rsa->get_d();
+ else if(field == "c")
+ return rsa->get_c();
+ else if(field == "d1")
+ return rsa->get_d1();
+ else if(field == "d2")
+ return rsa->get_d2();
+ else
+ return pubkey_get_field(key, field);
+ }
+#endif
+
+#if defined(BOTAN_HAS_DL_PUBLIC_KEY_FAMILY)
+ // Handles DSA, ElGamal, etc
+ if(const Botan::DL_Scheme_PrivateKey* dl = dynamic_cast<const Botan::DL_Scheme_PrivateKey*>(&key))
+ {
+ if(field == "x")
+ return dl->get_x();
+ else
+ return pubkey_get_field(key, field);
+ }
+#endif
+
+#if defined(BOTAN_HAS_ECC_PUBLIC_KEY_CRYPTO)
+ if(const Botan::EC_PrivateKey* ecc = dynamic_cast<const Botan::EC_PrivateKey*>(&key))
+ {
+ if(field == "x")
+ return ecc->private_value();
+ else
+ return pubkey_get_field(key, field);
+ }
+#endif
+
+ return pubkey_get_field(key, field);
+ }
+
+}
+
+extern "C" {
+
+using namespace Botan_FFI;
+
+int botan_pubkey_get_field(botan_mp_t output,
+ botan_pubkey_t key,
+ const char* field_name_cstr)
+ {
+ if(field_name_cstr == nullptr)
+ return BOTAN_FFI_ERROR_NULL_POINTER;
+
+ const std::string field_name(field_name_cstr);
+
+ return BOTAN_FFI_DO(Botan::Public_Key, key, k, {
+ safe_get(output) = pubkey_get_field(k, field_name);
+ });
+ }
+
+int botan_privkey_get_field(botan_mp_t output,
+ botan_privkey_t key,
+ const char* field_name_cstr)
+ {
+ if(field_name_cstr == nullptr)
+ return BOTAN_FFI_ERROR_NULL_POINTER;
+
+ const std::string field_name(field_name_cstr);
+
+ return BOTAN_FFI_DO(Botan::Private_Key, key, k, {
+ safe_get(output) = privkey_get_field(k, field_name);
+ });
+ }
+
+/* RSA specific operations */
+
+int botan_privkey_create_rsa(botan_privkey_t* key_obj, botan_rng_t rng_obj, size_t n_bits)
+ {
+ if(n_bits < 1024 || n_bits > 16*1024)
+ return BOTAN_FFI_ERROR_BAD_PARAMETER;
+
+ 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)
+ *key = nullptr;
+
+ return ffi_guard_thunk(__func__, [=]() -> int {
+ *key = new botan_privkey_struct(new Botan::RSA_PrivateKey(safe_get(rsa_p),
+ safe_get(rsa_q),
+ safe_get(rsa_e)));
+ return BOTAN_FFI_SUCCESS;
+ });
+#else
+ BOTAN_UNUSED(key, rsa_p, rsa_q, rsa_e);
+ return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+ }
+
+int botan_privkey_load_rsa_pkcs1(botan_privkey_t* key,
+ const uint8_t bits[],
+ size_t len)
+ {
+#if defined(BOTAN_HAS_RSA)
+ *key = nullptr;
+
+ Botan::secure_vector<uint8_t> src(bits, bits + len);
+ return ffi_guard_thunk(__func__, [=]() -> int {
+ Botan::AlgorithmIdentifier alg_id("RSA", Botan::AlgorithmIdentifier::USE_NULL_PARAM);
+ *key = new botan_privkey_struct(new Botan::RSA_PrivateKey(alg_id, src));
+ return BOTAN_FFI_SUCCESS;
+ });
+#else
+ BOTAN_UNUSED(key, bits, len);
+ return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+ }
+
+int botan_pubkey_load_rsa(botan_pubkey_t* key,
+ botan_mp_t n, botan_mp_t e)
+ {
+#if defined(BOTAN_HAS_RSA)
+ *key = nullptr;
+ return ffi_guard_thunk(__func__, [=]() -> int {
+ *key = new botan_pubkey_struct(new Botan::RSA_PublicKey(safe_get(n), safe_get(e)));
+ return BOTAN_FFI_SUCCESS;
+ });
+#else
+ BOTAN_UNUSED(key, n, e);
+ 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");
+ }
+
+int botan_privkey_rsa_get_q(botan_mp_t q, botan_privkey_t key)
+ {
+ return botan_privkey_get_field(q, key, "q");
+ }
+
+int botan_privkey_rsa_get_n(botan_mp_t n, botan_privkey_t key)
+ {
+ return botan_privkey_get_field(n, key, "n");
+ }
+
+int botan_privkey_rsa_get_e(botan_mp_t e, botan_privkey_t key)
+ {
+ return botan_privkey_get_field(e, key, "e");
+ }
+
+int botan_privkey_rsa_get_d(botan_mp_t d, botan_privkey_t key)
+ {
+ return botan_privkey_get_field(d, key, "d");
+ }
+
+int botan_pubkey_rsa_get_e(botan_mp_t e, botan_pubkey_t key)
+ {
+ return botan_pubkey_get_field(e, key, "e");
+ }
+
+int botan_pubkey_rsa_get_n(botan_mp_t n, botan_pubkey_t key)
+ {
+ return botan_pubkey_get_field(n, key, "n");
+ }
+
+int botan_privkey_rsa_get_privkey(botan_privkey_t rsa_key,
+ uint8_t out[], size_t* out_len,
+ uint32_t flags)
+ {
+#if defined(BOTAN_HAS_RSA)
+ return BOTAN_FFI_DO(Botan::Private_Key, rsa_key, k, {
+ if(const Botan::RSA_PrivateKey* rsa = dynamic_cast<const Botan::RSA_PrivateKey*>(&k))
+ {
+ 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"));
+ else
+ return BOTAN_FFI_ERROR_BAD_FLAG;
+ }
+ else
+ {
+ return BOTAN_FFI_ERROR_BAD_PARAMETER;
+ }
+ });
+#else
+ BOTAN_UNUSED(rsa_key, out, out_len);
+ return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+ }
+
+/* DSA specific operations */
+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))
+ return BOTAN_FFI_ERROR_NULL_POINTER;
+
+ if ((pbits % 64) || (qbits % 8) ||
+ (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);
+ *key = new botan_privkey_struct(new Botan::DSA_PrivateKey(rng, group));
+ return BOTAN_FFI_SUCCESS;
+ });
+#else
+ BOTAN_UNUSED(key, rng_obj, pbits, qbits);
+ return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+ }
+
+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)
+ *key = nullptr;
+
+ return ffi_guard_thunk(__func__, [=]() -> int {
+ Botan::Null_RNG null_rng;
+ Botan::DL_Group group(safe_get(p), safe_get(q), safe_get(g));
+ *key = new botan_privkey_struct(new Botan::DSA_PrivateKey(null_rng, group, safe_get(x)));
+ return BOTAN_FFI_SUCCESS;
+ });
+#else
+ BOTAN_UNUSED(key, p, q, g, x);
+ return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+ }
+
+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)
+ *key = nullptr;
+
+ return ffi_guard_thunk(__func__, [=]() -> int {
+ Botan::DL_Group group(safe_get(p), safe_get(q), safe_get(g));
+ *key = new botan_pubkey_struct(new Botan::DSA_PublicKey(group, safe_get(y)));
+ return BOTAN_FFI_SUCCESS;
+ });
+#else
+ BOTAN_UNUSED(key, p, q, g, y);
+ return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+ }
+
+int botan_privkey_dsa_get_x(botan_mp_t x, botan_privkey_t key)
+ {
+ return botan_privkey_get_field(x, key, "x");
+ }
+
+int botan_pubkey_dsa_get_p(botan_mp_t p, botan_pubkey_t key)
+ {
+ return botan_pubkey_get_field(p, key, "p");
+ }
+
+int botan_pubkey_dsa_get_q(botan_mp_t q, botan_pubkey_t key)
+ {
+ return botan_pubkey_get_field(q, key, "q");
+ }
+
+int botan_pubkey_dsa_get_g(botan_mp_t g, botan_pubkey_t key)
+ {
+ return botan_pubkey_get_field(g, key, "g");
+ }
+
+int botan_pubkey_dsa_get_y(botan_mp_t y, botan_pubkey_t key)
+ {
+ return botan_pubkey_get_field(y, key, "y");
+ }
+
+int botan_privkey_create_ecdsa(botan_privkey_t* key_obj, botan_rng_t rng_obj, const char* param_str)
+ {
+ return botan_privkey_create(key_obj, "ECDSA", param_str, rng_obj);
+ }
+
+/* ECDSA specific operations */
+
+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)
+ return ffi_guard_thunk(__func__, [=]() -> int {
+ std::unique_ptr<Botan::ECDSA_PublicKey> p_key;
+
+ 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(p_key.release());
+
+ return rc;
+ });
+#else
+ BOTAN_UNUSED(key, public_x, public_y, 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)
+ return ffi_guard_thunk(__func__, [=]() -> int {
+ std::unique_ptr<Botan::ECDSA_PrivateKey> p_key;
+ int rc = privkey_load_ec(p_key, safe_get(scalar), curve_name);
+ if(rc == BOTAN_FFI_SUCCESS)
+ *key = new botan_privkey_struct(p_key.release());
+ return rc;
+ });
+#else
+ BOTAN_UNUSED(key, scalar, curve_name);
+ return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+ }
+
+/* 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))
+ return BOTAN_FFI_ERROR_NULL_POINTER;
+
+ if ((pbits < 1024) || (qbits<160)) {
+ return BOTAN_FFI_ERROR_BAD_PARAMETER;
+ }
+
+ 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);
+ *key = new botan_privkey_struct(new Botan::ElGamal_PrivateKey(rng, group));
+ return BOTAN_FFI_SUCCESS;
+ });
+#else
+ BOTAN_UNUSED(key, rng_obj, pbits);
+ return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+ }
+
+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)
+ *key = nullptr;
+ return ffi_guard_thunk(__func__, [=]() -> int {
+ Botan::DL_Group group(safe_get(p), safe_get(g));
+ *key = new botan_pubkey_struct(new Botan::ElGamal_PublicKey(group, safe_get(y)));
+ return BOTAN_FFI_SUCCESS;
+ });
+#else
+ BOTAN_UNUSED(key, p, g, y);
+ return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+ }
+
+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)
+ *key = nullptr;
+ return ffi_guard_thunk(__func__, [=]() -> int {
+ Botan::Null_RNG null_rng;
+ Botan::DL_Group group(safe_get(p), safe_get(g));
+ *key = new botan_privkey_struct(new Botan::ElGamal_PrivateKey(null_rng, group, safe_get(x)));
+ return BOTAN_FFI_SUCCESS;
+ });
+#else
+ BOTAN_UNUSED(key, p, g, x);
+ return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+ }
+
+/* Diffie Hellman specific operations */
+
+int botan_privkey_create_dh(botan_privkey_t* key_obj, botan_rng_t rng_obj, const char* param_str)
+ {
+ return botan_privkey_create(key_obj, "DH", param_str, rng_obj);
+ }
+
+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)
+ *key = nullptr;
+ return ffi_guard_thunk(__func__, [=]() -> int {
+ Botan::Null_RNG null_rng;
+ Botan::DL_Group group(safe_get(p), safe_get(g));
+ *key = new botan_privkey_struct(new Botan::DH_PrivateKey(null_rng, group, safe_get(x)));
+ return BOTAN_FFI_SUCCESS;
+ });
+#else
+ BOTAN_UNUSED(key, p, g, x);
+ return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+ }
+
+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)
+ *key = nullptr;
+ return ffi_guard_thunk(__func__, [=]() -> int {
+ Botan::DL_Group group(safe_get(p), safe_get(g));
+ *key = new botan_pubkey_struct(new Botan::DH_PublicKey(group, safe_get(y)));
+ return BOTAN_FFI_SUCCESS;
+ });
+#else
+ BOTAN_UNUSED(key, p, g, y);
+ return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+ }
+
+/* ECDH + x25519 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)
+ return BOTAN_FFI_ERROR_NULL_POINTER;
+
+ const std::string params(param_str);
+
+ if(params == "curve25519")
+ return botan_privkey_create(key_obj, "Curve25519", "", rng_obj);
+
+ return botan_privkey_create(key_obj, "ECDH", param_str, rng_obj);
+ }
+
+int botan_pubkey_load_ecdh(botan_pubkey_t* key,
+ const botan_mp_t public_x,
+ const botan_mp_t public_y,
+ const char* curve_name)
+ {
+#if defined(BOTAN_HAS_ECDH)
+ return ffi_guard_thunk(__func__, [=]() -> int {
+ std::unique_ptr<Botan::ECDH_PublicKey> p_key;
+ 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(p_key.release());
+ return rc;
+ });
+#else
+ BOTAN_UNUSED(key, public_x, public_y, 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)
+ return ffi_guard_thunk(__func__, [=]() -> int {
+ std::unique_ptr<Botan::ECDH_PrivateKey> p_key;
+ int rc = privkey_load_ec(p_key, safe_get(scalar), curve_name);
+ if(rc == BOTAN_FFI_SUCCESS)
+ *key = new botan_privkey_struct(p_key.release());
+ return rc;
+ });
+#else
+ BOTAN_UNUSED(key, scalar, curve_name);
+ return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+ }
+
+/* SM2 specific operations */
+
+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)
+ return BOTAN_FFI_ERROR_NULL_POINTER;
+
+#if defined(BOTAN_HAS_SM2)
+ return ffi_guard_thunk(__func__, [=]() -> int {
+ const Botan::Public_Key& pub_key = safe_get(key);
+ const Botan::EC_PublicKey* ec_key = dynamic_cast<const Botan::EC_PublicKey*>(&pub_key);
+
+ if(ec_key == nullptr)
+ return BOTAN_FFI_ERROR_BAD_PARAMETER;
+
+ if(ec_key->algo_name() != "SM2")
+ return BOTAN_FFI_ERROR_BAD_PARAMETER;
+
+ const std::string ident_str(ident);
+ std::unique_ptr<Botan::HashFunction> hash =
+ Botan::HashFunction::create_or_throw(hash_algo);
+
+ const std::vector<uint8_t> za =
+ Botan::sm2_compute_za(*hash, ident_str, ec_key->domain(), ec_key->public_point());
+
+ return write_vec_output(out, out_len, za);
+ });
+#else
+ return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+ }
+
+int botan_pubkey_load_sm2(botan_pubkey_t* key,
+ const botan_mp_t public_x,
+ const botan_mp_t public_y,
+ const char* curve_name)
+ {
+#if defined(BOTAN_HAS_SM2)
+ return ffi_guard_thunk(__func__, [=]() -> int {
+ std::unique_ptr<Botan::SM2_PublicKey> p_key;
+ if(!pubkey_load_ec(p_key, safe_get(public_x), safe_get(public_y), curve_name))
+ {
+ *key = new botan_pubkey_struct(p_key.release());
+ return BOTAN_FFI_SUCCESS;
+ }
+ return BOTAN_FFI_ERROR_UNKNOWN_ERROR;
+ });
+#else
+ BOTAN_UNUSED(key, public_x, public_y, 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)
+ return ffi_guard_thunk(__func__, [=]() -> int {
+ std::unique_ptr<Botan::SM2_PrivateKey> p_key;
+ int rc = privkey_load_ec(p_key, safe_get(scalar), curve_name);
+
+ if(rc == BOTAN_FFI_SUCCESS)
+ *key = new botan_privkey_struct(p_key.release());
+ return rc;
+ });
+#else
+ BOTAN_UNUSED(key, scalar, curve_name);
+ return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+ }
+
+int botan_pubkey_load_sm2_enc(botan_pubkey_t* key,
+ const botan_mp_t public_x,
+ const botan_mp_t public_y,
+ const char* curve_name)
+ {
+ return botan_pubkey_load_sm2(key, public_x, public_y, curve_name);
+ }
+
+int botan_privkey_load_sm2_enc(botan_privkey_t* key,
+ const botan_mp_t scalar,
+ const char* curve_name)
+ {
+ return botan_privkey_load_sm2(key, scalar, curve_name);
+ }
+
+/* Ed25519 specific operations */
+
+int botan_privkey_load_ed25519(botan_privkey_t* key,
+ const uint8_t privkey[32])
+ {
+#if defined(BOTAN_HAS_ED25519)
+ *key = nullptr;
+ return ffi_guard_thunk(__func__, [=]() -> int {
+ const Botan::secure_vector<uint8_t> privkey_vec(privkey, privkey + 32);
+ *key = new botan_privkey_struct(new Botan::Ed25519_PrivateKey(privkey_vec));
+ return BOTAN_FFI_SUCCESS;
+ });
+#else
+ BOTAN_UNUSED(key, privkey);
+ return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+ }
+
+int botan_pubkey_load_ed25519(botan_pubkey_t* key,
+ const uint8_t pubkey[32])
+ {
+#if defined(BOTAN_HAS_ED25519)
+ *key = nullptr;
+ return ffi_guard_thunk(__func__, [=]() -> int {
+ const std::vector<uint8_t> pubkey_vec(pubkey, pubkey + 32);
+ *key = new botan_pubkey_struct(new Botan::Ed25519_PublicKey(pubkey_vec));
+ return BOTAN_FFI_SUCCESS;
+ });
+#else
+ BOTAN_UNUSED(key, pubkey);
+ return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+ }
+
+int botan_privkey_ed25519_get_privkey(botan_privkey_t key,
+ uint8_t output[64])
+ {
+#if defined(BOTAN_HAS_ED25519)
+ return BOTAN_FFI_DO(Botan::Private_Key, key, k, {
+ if(Botan::Ed25519_PrivateKey* ed = dynamic_cast<Botan::Ed25519_PrivateKey*>(&k))
+ {
+ const Botan::secure_vector<uint8_t>& ed_key = ed->get_private_key();
+ if(ed_key.size() != 64)
+ return BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE;
+ Botan::copy_mem(output, ed_key.data(), ed_key.size());
+ return BOTAN_FFI_SUCCESS;
+ }
+ else
+ {
+ return BOTAN_FFI_ERROR_BAD_PARAMETER;
+ }
+ });
+#else
+ BOTAN_UNUSED(key, output);
+ return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+ }
+
+int botan_pubkey_ed25519_get_pubkey(botan_pubkey_t key,
+ uint8_t output[32])
+ {
+#if defined(BOTAN_HAS_ED25519)
+ return BOTAN_FFI_DO(Botan::Public_Key, key, k, {
+ if(Botan::Ed25519_PublicKey* ed = dynamic_cast<Botan::Ed25519_PublicKey*>(&k))
+ {
+ const std::vector<uint8_t>& ed_key = ed->get_public_key();
+ if(ed_key.size() != 32)
+ return BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE;
+ Botan::copy_mem(output, ed_key.data(), ed_key.size());
+ return BOTAN_FFI_SUCCESS;
+ }
+ else
+ {
+ return BOTAN_FFI_ERROR_BAD_PARAMETER;
+ }
+ });
+#else
+ BOTAN_UNUSED(key, output);
+ return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+ }
+
+/* X25519 specific operations */
+
+int botan_privkey_load_x25519(botan_privkey_t* key,
+ const uint8_t privkey[32])
+ {
+#if defined(BOTAN_HAS_X25519)
+ *key = nullptr;
+ return ffi_guard_thunk(__func__, [=]() -> int {
+ const Botan::secure_vector<uint8_t> privkey_vec(privkey, privkey + 32);
+ *key = new botan_privkey_struct(new Botan::X25519_PrivateKey(privkey_vec));
+ return BOTAN_FFI_SUCCESS;
+ });
+#else
+ BOTAN_UNUSED(key, privkey);
+ return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+ }
+
+int botan_pubkey_load_x25519(botan_pubkey_t* key,
+ const uint8_t pubkey[32])
+ {
+#if defined(BOTAN_HAS_X25519)
+ *key = nullptr;
+ return ffi_guard_thunk(__func__, [=]() -> int {
+ const std::vector<uint8_t> pubkey_vec(pubkey, pubkey + 32);
+ *key = new botan_pubkey_struct(new Botan::X25519_PublicKey(pubkey_vec));
+ return BOTAN_FFI_SUCCESS;
+ });
+#else
+ BOTAN_UNUSED(key, pubkey);
+ return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+ }
+
+int botan_privkey_x25519_get_privkey(botan_privkey_t key,
+ uint8_t output[32])
+ {
+#if defined(BOTAN_HAS_X25519)
+ return BOTAN_FFI_DO(Botan::Private_Key, key, k, {
+ if(Botan::X25519_PrivateKey* x25519 = dynamic_cast<Botan::X25519_PrivateKey*>(&k))
+ {
+ const Botan::secure_vector<uint8_t>& x25519_key = x25519->get_x();
+ if(x25519_key.size() != 32)
+ return BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE;
+ Botan::copy_mem(output, x25519_key.data(), x25519_key.size());
+ return BOTAN_FFI_SUCCESS;
+ }
+ else
+ {
+ return BOTAN_FFI_ERROR_BAD_PARAMETER;
+ }
+ });
+#else
+ BOTAN_UNUSED(key, output);
+ return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+ }
+
+int botan_pubkey_x25519_get_pubkey(botan_pubkey_t key,
+ uint8_t output[32])
+ {
+#if defined(BOTAN_HAS_X25519)
+ return BOTAN_FFI_DO(Botan::Public_Key, key, k, {
+ if(Botan::X25519_PublicKey* x25519 = dynamic_cast<Botan::X25519_PublicKey*>(&k))
+ {
+ const std::vector<uint8_t>& 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());
+ return BOTAN_FFI_SUCCESS;
+ }
+ else
+ {
+ return BOTAN_FFI_ERROR_BAD_PARAMETER;
+ }
+ });
+#else
+ BOTAN_UNUSED(key, output);
+ return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+ }
+
+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);
+ }
+
+int botan_mceies_decrypt(botan_privkey_t mce_key_obj,
+ const char* aead,
+ const uint8_t ct[], size_t ct_len,
+ const uint8_t ad[], size_t ad_len,
+ uint8_t out[], size_t* out_len)
+ {
+ return ffi_guard_thunk(__func__, [=]() -> int {
+ Botan::Private_Key& key = safe_get(mce_key_obj);
+
+#if defined(BOTAN_HAS_MCELIECE) && defined(BOTAN_HAS_MCEIES)
+ Botan::McEliece_PrivateKey* mce = dynamic_cast<Botan::McEliece_PrivateKey*>(&key);
+ if(!mce)
+ return BOTAN_FFI_ERROR_BAD_PARAMETER;
+
+ const Botan::secure_vector<uint8_t> pt = mceies_decrypt(*mce, ct, ct_len, ad, ad_len, aead);
+ return write_vec_output(out, out_len, pt);
+#else
+ return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+ });
+ }
+
+int botan_mceies_encrypt(botan_pubkey_t mce_key_obj,
+ botan_rng_t rng_obj,
+ const char* aead,
+ const uint8_t pt[], size_t pt_len,
+ const uint8_t ad[], size_t ad_len,
+ uint8_t out[], size_t* out_len)
+ {
+ return ffi_guard_thunk(__func__, [=]() -> int {
+ Botan::Public_Key& key = safe_get(mce_key_obj);
+ Botan::RandomNumberGenerator& rng = safe_get(rng_obj);
+
+#if defined(BOTAN_HAS_MCELIECE) && defined(BOTAN_HAS_MCEIES)
+ Botan::McEliece_PublicKey* mce = dynamic_cast<Botan::McEliece_PublicKey*>(&key);
+ if(!mce)
+ return BOTAN_FFI_ERROR_BAD_PARAMETER;
+
+ Botan::secure_vector<uint8_t> ct = mceies_encrypt(*mce, pt, pt_len, ad, ad_len, rng, aead);
+ return write_vec_output(out, out_len, ct);
+#else
+ return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+ });
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/ffi/ffi_rng.cpp b/comm/third_party/botan/src/lib/ffi/ffi_rng.cpp
new file mode 100644
index 0000000000..7bb365a248
--- /dev/null
+++ b/comm/third_party/botan/src/lib/ffi/ffi_rng.cpp
@@ -0,0 +1,183 @@
+/*
+* (C) 2015,2017 Jack Lloyd
+* (C) 2021 René Fischer
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/ffi.h>
+#include <botan/internal/ffi_util.h>
+#include <botan/internal/ffi_rng.h>
+#include <botan/system_rng.h>
+#include <botan/auto_rng.h>
+
+#include <functional>
+
+#if defined(BOTAN_HAS_PROCESSOR_RNG)
+ #include <botan/processor_rng.h>
+#endif
+
+extern "C" {
+
+using namespace Botan_FFI;
+
+int botan_rng_init(botan_rng_t* rng_out, const char* rng_type)
+ {
+ return ffi_guard_thunk(__func__, [=]() -> int {
+ if(rng_out == nullptr)
+ return BOTAN_FFI_ERROR_NULL_POINTER;
+
+ const std::string rng_type_s(rng_type ? rng_type : "system");
+
+ std::unique_ptr<Botan::RandomNumberGenerator> rng;
+
+ if(rng_type_s == "system")
+ {
+ rng.reset(new Botan::System_RNG);
+ }
+ else if(rng_type_s == "user" || rng_type_s == "user-threadsafe")
+ {
+ rng.reset(new Botan::AutoSeeded_RNG);
+ }
+ else if(rng_type_s == "null")
+ {
+ rng.reset(new Botan::Null_RNG);
+ }
+#if defined(BOTAN_HAS_PROCESSOR_RNG)
+ else if((rng_type_s == "rdrand" || rng_type_s == "hwrng") && Botan::Processor_RNG::available())
+ {
+ rng.reset(new Botan::Processor_RNG);
+ }
+#endif
+
+ if(!rng)
+ {
+ return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+ }
+
+ *rng_out = new botan_rng_struct(rng.release());
+ return BOTAN_FFI_SUCCESS;
+ });
+ }
+
+int botan_rng_init_custom(botan_rng_t* rng_out, const char* rng_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))
+{
+return ffi_guard_thunk(__func__,[=]() -> int {
+ if(rng_out == nullptr)
+ return BOTAN_FFI_ERROR_NULL_POINTER;
+
+ if(rng_name == nullptr)
+ return BOTAN_FFI_ERROR_NULL_POINTER;
+
+ if(get_cb == nullptr)
+ return BOTAN_FFI_ERROR_NULL_POINTER;
+
+ class Custom_RNG : public Botan::RandomNumberGenerator
+ {
+ public:
+ Custom_RNG(const std::string& 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;
+ }
+
+ ~Custom_RNG()
+ {
+ if(m_destroy_cb)
+ {
+ m_destroy_cb(m_context);
+ }
+ }
+
+ void randomize(uint8_t output[], size_t length) override
+ {
+ int rc = m_get_cb(m_context, output, length);
+ if(rc)
+ {
+ throw Botan::Invalid_State("Failed to get random from C callback, rc=" + std::to_string(rc));
+ }
+ }
+
+ bool accepts_input() const override
+ {
+ return m_add_entropy_cb != nullptr;
+ }
+
+ void add_entropy(const uint8_t input[], size_t length) override
+ {
+ if(m_add_entropy_cb == nullptr)
+ {
+ return;
+ }
+
+ int rc = m_add_entropy_cb(m_context, input, length);
+ if(rc)
+ {
+ throw Botan::Invalid_State("Failed to add entropy via C callback, rc=" + std::to_string(rc));
+ }
+ }
+
+ std::string name() const override
+ {
+ return m_name;
+ }
+
+ void clear() override
+ {
+ }
+
+ bool is_seeded() const override
+ {
+ return true;
+ }
+
+ private:
+ std::string m_name;
+ void* m_context;
+ std::function<int(void* context, uint8_t* out, size_t out_len)> m_get_cb;
+ std::function<int(void* context, const uint8_t input[], size_t length)> m_add_entropy_cb;
+ std::function<void(void* context)> m_destroy_cb;
+ };
+
+ std::unique_ptr<Botan::RandomNumberGenerator> rng(new Custom_RNG(rng_name, context, get_cb, add_entropy_cb, destroy_cb));
+
+ *rng_out = new botan_rng_struct(rng.release());
+ return BOTAN_FFI_SUCCESS;
+ });
+}
+
+int botan_rng_destroy(botan_rng_t rng)
+ {
+ return BOTAN_FFI_CHECKED_DELETE(rng);
+ }
+
+int botan_rng_get(botan_rng_t rng, uint8_t* out, size_t out_len)
+ {
+ return BOTAN_FFI_DO(Botan::RandomNumberGenerator, rng, r, { r.randomize(out, out_len); });
+ }
+
+int botan_rng_reseed(botan_rng_t rng, size_t bits)
+ {
+ return BOTAN_FFI_DO(Botan::RandomNumberGenerator, rng, r, { r.reseed_from_rng(Botan::system_rng(), bits); });
+ }
+
+int botan_rng_add_entropy(botan_rng_t rng, const uint8_t* input, size_t len)
+ {
+ return BOTAN_FFI_DO(Botan::RandomNumberGenerator, rng, 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_DO(Botan::RandomNumberGenerator, rng, r, { r.reseed_from_rng(safe_get(source_rng), bits); });
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/ffi/ffi_rng.h b/comm/third_party/botan/src/lib/ffi/ffi_rng.h
new file mode 100644
index 0000000000..2117853c47
--- /dev/null
+++ b/comm/third_party/botan/src/lib/ffi/ffi_rng.h
@@ -0,0 +1,19 @@
+/*
+* (C) 2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_FFI_RNG_H_
+#define BOTAN_FFI_RNG_H_
+
+#include <botan/rng.h>
+#include <botan/internal/ffi_util.h>
+
+extern "C" {
+
+BOTAN_FFI_DECLARE_STRUCT(botan_rng_struct, Botan::RandomNumberGenerator, 0x4901F9C1);
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/ffi/ffi_totp.cpp b/comm/third_party/botan/src/lib/ffi/ffi_totp.cpp
new file mode 100644
index 0000000000..9ca8e8e6ae
--- /dev/null
+++ b/comm/third_party/botan/src/lib/ffi/ffi_totp.cpp
@@ -0,0 +1,94 @@
+/*
+* (C) 2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/ffi.h>
+#include <botan/internal/ffi_util.h>
+
+#if defined(BOTAN_HAS_TOTP)
+ #include <botan/otp.h>
+#endif
+
+extern "C" {
+
+using namespace Botan_FFI;
+
+#if defined(BOTAN_HAS_TOTP)
+
+BOTAN_FFI_DECLARE_STRUCT(botan_totp_struct, Botan::TOTP, 0x3D9D2CD1);
+
+#endif
+
+int botan_totp_init(botan_totp_t* totp,
+ const uint8_t key[], size_t key_len,
+ const char* hash_algo,
+ size_t digits,
+ size_t time_step)
+ {
+ if(totp == nullptr || key == nullptr || hash_algo == nullptr)
+ return BOTAN_FFI_ERROR_NULL_POINTER;
+
+ *totp = nullptr;
+
+#if defined(BOTAN_HAS_TOTP)
+ return ffi_guard_thunk(__func__, [=]() -> int {
+
+ *totp = new botan_totp_struct(
+ new Botan::TOTP(key, key_len, hash_algo, digits, time_step));
+
+ return BOTAN_FFI_SUCCESS;
+ });
+#else
+ BOTAN_UNUSED(totp, key, key_len, hash_algo, digits, time_step);
+ return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+ }
+
+int botan_totp_destroy(botan_totp_t totp)
+ {
+#if defined(BOTAN_HAS_TOTP)
+ return BOTAN_FFI_CHECKED_DELETE(totp);
+#else
+ BOTAN_UNUSED(totp);
+ return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+ }
+
+int botan_totp_generate(botan_totp_t totp,
+ uint32_t* totp_code,
+ uint64_t timestamp)
+ {
+#if defined(BOTAN_HAS_TOTP)
+ if(totp == nullptr || totp_code == nullptr)
+ return BOTAN_FFI_ERROR_NULL_POINTER;
+
+ return BOTAN_FFI_DO(Botan::TOTP, totp, t, {
+ *totp_code = t.generate_totp(timestamp);
+ });
+
+#else
+ BOTAN_UNUSED(totp, totp_code, timestamp);
+ return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+ }
+
+int botan_totp_check(botan_totp_t totp,
+ uint32_t totp_code,
+ uint64_t timestamp,
+ size_t acceptable_clock_drift)
+ {
+#if defined(BOTAN_HAS_TOTP)
+ return BOTAN_FFI_RETURNING(Botan::TOTP, totp, t, {
+ const bool ok = t.verify_totp(totp_code, timestamp, acceptable_clock_drift);
+ return (ok ? BOTAN_FFI_SUCCESS : BOTAN_FFI_INVALID_VERIFIER);
+ });
+
+#else
+ BOTAN_UNUSED(totp, totp_code, timestamp, acceptable_clock_drift);
+ return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/ffi/ffi_util.h b/comm/third_party/botan/src/lib/ffi/ffi_util.h
new file mode 100644
index 0000000000..4269aa3e8c
--- /dev/null
+++ b/comm/third_party/botan/src/lib/ffi/ffi_util.h
@@ -0,0 +1,182 @@
+/*
+* (C) 2015,2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_FFI_UTILS_H_
+#define BOTAN_FFI_UTILS_H_
+
+#include <cstdint>
+#include <memory>
+#include <stdexcept>
+#include <functional>
+#include <botan/exceptn.h>
+#include <botan/mem_ops.h>
+
+namespace Botan_FFI {
+
+class BOTAN_UNSTABLE_API FFI_Error final : public Botan::Exception
+ {
+ public:
+ FFI_Error(const std::string& what, int err_code) :
+ Exception("FFI error", what),
+ m_err_code(err_code)
+ {}
+
+ int error_code() const noexcept override { return m_err_code; }
+
+ Botan::ErrorType error_type() const noexcept override { return Botan::ErrorType::InvalidArgument; }
+
+ private:
+ int m_err_code;
+ };
+
+template<typename T, uint32_t MAGIC>
+struct botan_struct
+ {
+ public:
+ botan_struct(T* obj) : m_magic(MAGIC), m_obj(obj) {}
+ virtual ~botan_struct() { m_magic = 0; m_obj.reset(); }
+
+ bool magic_ok() const { return (m_magic == MAGIC); }
+
+ T* unsafe_get() const
+ {
+ return m_obj.get();
+ }
+ private:
+ uint32_t m_magic = 0;
+ std::unique_ptr<T> m_obj;
+ };
+
+#define BOTAN_FFI_DECLARE_STRUCT(NAME, TYPE, MAGIC) \
+ struct NAME final : public Botan_FFI::botan_struct<TYPE, MAGIC> { explicit NAME(TYPE* x) : botan_struct(x) {} }
+
+// Declared in ffi.cpp
+int ffi_error_exception_thrown(const char* func_name, const char* exn,
+ int rc = BOTAN_FFI_ERROR_EXCEPTION_THROWN);
+
+template<typename T, uint32_t M>
+T& safe_get(botan_struct<T,M>* p)
+ {
+ if(!p)
+ throw FFI_Error("Null pointer argument", BOTAN_FFI_ERROR_NULL_POINTER);
+ if(p->magic_ok() == false)
+ throw FFI_Error("Bad magic in ffi object", BOTAN_FFI_ERROR_INVALID_OBJECT);
+
+ if(T* t = p->unsafe_get())
+ return *t;
+
+ throw FFI_Error("Invalid object pointer", BOTAN_FFI_ERROR_INVALID_OBJECT);
+ }
+
+int ffi_guard_thunk(const char* func_name, std::function<int ()>);
+
+template<typename T, uint32_t M, typename F>
+int apply_fn(botan_struct<T, M>* o, const char* func_name, F func)
+ {
+ if(!o)
+ return BOTAN_FFI_ERROR_NULL_POINTER;
+
+ if(o->magic_ok() == false)
+ return BOTAN_FFI_ERROR_INVALID_OBJECT;
+
+ T* p = o->unsafe_get();
+ if(p == nullptr)
+ return BOTAN_FFI_ERROR_INVALID_OBJECT;
+
+ return ffi_guard_thunk(func_name, [&]() { return func(*p); });
+ }
+
+#define BOTAN_FFI_DO(T, obj, param, block) \
+ apply_fn(obj, __func__, \
+ [=](T& param) -> int { do { block } while(0); return BOTAN_FFI_SUCCESS; })
+
+/*
+* Like BOTAN_FFI_DO but with no trailing return with the expectation
+* that the block always returns a value. This exists because otherwise
+* MSVC warns about the dead return after the block in FFI_DO.
+*/
+#define BOTAN_FFI_RETURNING(T, obj, param, block) \
+ apply_fn(obj, __func__, \
+ [=](T& param) -> int { do { block } while(0); })
+
+template<typename T, uint32_t M>
+int ffi_delete_object(botan_struct<T, M>* obj, const char* func_name)
+ {
+ try
+ {
+ if(obj == nullptr)
+ return BOTAN_FFI_SUCCESS; // ignore delete of null objects
+
+ if(obj->magic_ok() == false)
+ return BOTAN_FFI_ERROR_INVALID_OBJECT;
+
+ delete obj;
+ return BOTAN_FFI_SUCCESS;
+ }
+ catch(std::exception& e)
+ {
+ return ffi_error_exception_thrown(func_name, e.what());
+ }
+ catch(...)
+ {
+ return ffi_error_exception_thrown(func_name, "unknown exception");
+ }
+ }
+
+#define BOTAN_FFI_CHECKED_DELETE(o) ffi_delete_object(o, __func__)
+
+inline int write_output(uint8_t out[], size_t* out_len, const uint8_t buf[], size_t buf_len)
+ {
+ if(out_len == nullptr)
+ return BOTAN_FFI_ERROR_NULL_POINTER;
+
+ const size_t avail = *out_len;
+ *out_len = buf_len;
+
+ if((avail >= buf_len) && (out != nullptr))
+ {
+ Botan::copy_mem(out, buf, buf_len);
+ return BOTAN_FFI_SUCCESS;
+ }
+ else
+ {
+ if(out != nullptr)
+ {
+ Botan::clear_mem(out, avail);
+ }
+ return BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE;
+ }
+ }
+
+template<typename Alloc>
+int write_vec_output(uint8_t out[], size_t* out_len, const std::vector<uint8_t, Alloc>& buf)
+ {
+ return write_output(out, out_len, buf.data(), buf.size());
+ }
+
+inline int write_str_output(uint8_t out[], size_t* out_len, const std::string& str)
+ {
+ return write_output(out, out_len,
+ Botan::cast_char_ptr_to_uint8(str.data()),
+ str.size() + 1);
+ }
+
+inline int write_str_output(char out[], size_t* out_len, const std::string& str)
+ {
+ return write_str_output(Botan::cast_char_ptr_to_uint8(out), out_len, str);
+ }
+
+inline int write_str_output(char out[], size_t* out_len, const std::vector<uint8_t>& str_vec)
+ {
+ return write_output(Botan::cast_char_ptr_to_uint8(out),
+ out_len,
+ str_vec.data(),
+ str_vec.size());
+ }
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/ffi/info.txt b/comm/third_party/botan/src/lib/ffi/info.txt
new file mode 100644
index 0000000000..865beb61ed
--- /dev/null
+++ b/comm/third_party/botan/src/lib/ffi/info.txt
@@ -0,0 +1,31 @@
+<defines>
+FFI -> 20210220
+</defines>
+
+<header:internal>
+ffi_mp.h
+ffi_pkey.h
+ffi_rng.h
+ffi_util.h
+</header:internal>
+
+<header:public>
+ffi.h
+</header:public>
+
+<requires>
+block
+stream
+hash
+aead
+kdf
+pbkdf
+pubkey
+pem
+bigint
+sha2_32
+#x509
+#tls
+system_rng
+auto_rng
+</requires>
diff --git a/comm/third_party/botan/src/lib/filters/algo_filt.cpp b/comm/third_party/botan/src/lib/filters/algo_filt.cpp
new file mode 100644
index 0000000000..c944b72e5b
--- /dev/null
+++ b/comm/third_party/botan/src/lib/filters/algo_filt.cpp
@@ -0,0 +1,96 @@
+/*
+* Filters
+* (C) 1999-2007,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/filters.h>
+#include <algorithm>
+
+namespace Botan {
+
+#if defined(BOTAN_HAS_STREAM_CIPHER)
+
+StreamCipher_Filter::StreamCipher_Filter(StreamCipher* cipher) :
+ m_buffer(BOTAN_DEFAULT_BUFFER_SIZE),
+ m_cipher(cipher)
+ {
+ }
+
+StreamCipher_Filter::StreamCipher_Filter(StreamCipher* cipher, const SymmetricKey& key) :
+ StreamCipher_Filter(cipher)
+ {
+ m_cipher->set_key(key);
+ }
+
+StreamCipher_Filter::StreamCipher_Filter(const std::string& sc_name) :
+ m_buffer(BOTAN_DEFAULT_BUFFER_SIZE),
+ m_cipher(StreamCipher::create_or_throw(sc_name))
+ {
+ }
+
+StreamCipher_Filter::StreamCipher_Filter(const std::string& sc_name, const SymmetricKey& key) :
+ StreamCipher_Filter(sc_name)
+ {
+ m_cipher->set_key(key);
+ }
+
+void StreamCipher_Filter::write(const uint8_t input[], size_t length)
+ {
+ while(length)
+ {
+ size_t copied = std::min<size_t>(length, m_buffer.size());
+ m_cipher->cipher(input, m_buffer.data(), copied);
+ send(m_buffer, copied);
+ input += copied;
+ length -= copied;
+ }
+ }
+
+#endif
+
+#if defined(BOTAN_HAS_HASH)
+
+Hash_Filter::Hash_Filter(const std::string& hash_name, size_t len) :
+ m_hash(HashFunction::create_or_throw(hash_name)),
+ m_out_len(len)
+ {
+ }
+
+void Hash_Filter::end_msg()
+ {
+ secure_vector<uint8_t> output = m_hash->final();
+ if(m_out_len)
+ send(output, std::min<size_t>(m_out_len, output.size()));
+ else
+ send(output);
+ }
+#endif
+
+#if defined(BOTAN_HAS_MAC)
+
+MAC_Filter::MAC_Filter(const std::string& mac_name, size_t len) :
+ m_mac(MessageAuthenticationCode::create_or_throw(mac_name)),
+ m_out_len(len)
+ {
+ }
+
+MAC_Filter::MAC_Filter(const std::string& mac_name, const SymmetricKey& key, size_t len) :
+ MAC_Filter(mac_name, len)
+ {
+ m_mac->set_key(key);
+ }
+
+void MAC_Filter::end_msg()
+ {
+ secure_vector<uint8_t> output = m_mac->final();
+ if(m_out_len)
+ send(output, std::min<size_t>(m_out_len, output.size()));
+ else
+ send(output);
+ }
+
+#endif
+
+}
diff --git a/comm/third_party/botan/src/lib/filters/b64_filt.cpp b/comm/third_party/botan/src/lib/filters/b64_filt.cpp
new file mode 100644
index 0000000000..8cbba1a6e5
--- /dev/null
+++ b/comm/third_party/botan/src/lib/filters/b64_filt.cpp
@@ -0,0 +1,182 @@
+/*
+* Base64 Encoder/Decoder
+* (C) 1999-2010 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/filters.h>
+#include <botan/base64.h>
+#include <botan/exceptn.h>
+#include <algorithm>
+
+namespace Botan {
+
+/*
+* Base64_Encoder Constructor
+*/
+Base64_Encoder::Base64_Encoder(bool breaks, size_t length, bool t_n) :
+ m_line_length(breaks ? length : 0),
+ m_trailing_newline(t_n && breaks),
+ m_in(48),
+ m_out(64),
+ m_position(0),
+ m_out_position(0)
+ {
+ }
+
+/*
+* Encode and send a block
+*/
+void Base64_Encoder::encode_and_send(const uint8_t input[], size_t length,
+ bool final_inputs)
+ {
+ while(length)
+ {
+ 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);
+
+ do_output(m_out.data(), produced);
+
+ // FIXME: s/proc/consumed/?
+ input += proc;
+ length -= proc;
+ }
+ }
+
+/*
+* Handle the output
+*/
+void Base64_Encoder::do_output(const uint8_t input[], size_t length)
+ {
+ 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);
+ send(input + offset, sent);
+ m_out_position += sent;
+ remaining -= sent;
+ offset += sent;
+ if(m_out_position == m_line_length)
+ {
+ send('\n');
+ m_out_position = 0;
+ }
+ }
+ }
+ }
+
+/*
+* Convert some data into Base64
+*/
+void Base64_Encoder::write(const uint8_t input[], size_t length)
+ {
+ buffer_insert(m_in, m_position, input, length);
+ if(m_position + length >= m_in.size())
+ {
+ encode_and_send(m_in.data(), m_in.size());
+ input += (m_in.size() - m_position);
+ length -= (m_in.size() - m_position);
+ while(length >= m_in.size())
+ {
+ encode_and_send(input, m_in.size());
+ input += m_in.size();
+ length -= m_in.size();
+ }
+ copy_mem(m_in.data(), input, length);
+ m_position = 0;
+ }
+ m_position += length;
+ }
+
+/*
+* Flush buffers
+*/
+void Base64_Encoder::end_msg()
+ {
+ encode_and_send(m_in.data(), m_position, true);
+
+ if(m_trailing_newline || (m_out_position && m_line_length))
+ send('\n');
+
+ m_out_position = m_position = 0;
+ }
+
+/*
+* Base64_Decoder Constructor
+*/
+Base64_Decoder::Base64_Decoder(Decoder_Checking c) :
+ m_checking(c), m_in(64), m_out(48), m_position(0)
+ {
+ }
+
+/*
+* Convert some data from Base64
+*/
+void Base64_Decoder::write(const uint8_t input[], size_t length)
+ {
+ while(length)
+ {
+ size_t to_copy = std::min<size_t>(length, m_in.size() - m_position);
+ if(to_copy == 0)
+ {
+ m_in.resize(m_in.size()*2);
+ m_out.resize(m_out.size()*2);
+ }
+ copy_mem(&m_in[m_position], input, to_copy);
+ m_position += to_copy;
+
+ size_t consumed = 0;
+ 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);
+
+ if(consumed != m_position)
+ {
+ copy_mem(m_in.data(), m_in.data() + consumed, m_position - consumed);
+ m_position = m_position - consumed;
+ }
+ else
+ m_position = 0;
+
+ length -= to_copy;
+ input += to_copy;
+ }
+ }
+
+/*
+* Flush buffers
+*/
+void Base64_Decoder::end_msg()
+ {
+ size_t consumed = 0;
+ 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);
+
+ const bool not_full_bytes = consumed != m_position;
+
+ m_position = 0;
+
+ if(not_full_bytes)
+ throw Invalid_Argument("Base64_Decoder: Input not full bytes");
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/filters/b64_filt.h b/comm/third_party/botan/src/lib/filters/b64_filt.h
new file mode 100644
index 0000000000..af7bb7cf2f
--- /dev/null
+++ b/comm/third_party/botan/src/lib/filters/b64_filt.h
@@ -0,0 +1,14 @@
+/*
+* Base64 Encoder/Decoder
+* (C) 1999-2010 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_BASE64_FILTER_H_
+#define BOTAN_BASE64_FILTER_H_
+
+#include <botan/filters.h>
+BOTAN_DEPRECATED_HEADER(b64_filt.h)
+
+#endif
diff --git a/comm/third_party/botan/src/lib/filters/basefilt.cpp b/comm/third_party/botan/src/lib/filters/basefilt.cpp
new file mode 100644
index 0000000000..b34b70bd65
--- /dev/null
+++ b/comm/third_party/botan/src/lib/filters/basefilt.cpp
@@ -0,0 +1,52 @@
+/*
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/filters.h>
+
+namespace Botan {
+
+/*
+* Chain Constructor
+*/
+Chain::Chain(Filter* f1, Filter* f2, Filter* f3, Filter* f4)
+ {
+ if(f1) { attach(f1); incr_owns(); }
+ if(f2) { attach(f2); incr_owns(); }
+ if(f3) { attach(f3); incr_owns(); }
+ if(f4) { attach(f4); incr_owns(); }
+ }
+
+/*
+* Chain Constructor
+*/
+Chain::Chain(Filter* filters[], size_t count)
+ {
+ for(size_t j = 0; j != count; ++j)
+ if(filters[j])
+ {
+ attach(filters[j]);
+ incr_owns();
+ }
+ }
+
+/*
+* Fork Constructor
+*/
+Fork::Fork(Filter* f1, Filter* f2, Filter* f3, Filter* f4)
+ {
+ Filter* filters[4] = { f1, f2, f3, f4 };
+ set_next(filters, 4);
+ }
+
+/*
+* Fork Constructor
+*/
+Fork::Fork(Filter* filters[], size_t count)
+ {
+ set_next(filters, count);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/filters/basefilt.h b/comm/third_party/botan/src/lib/filters/basefilt.h
new file mode 100644
index 0000000000..422989b759
--- /dev/null
+++ b/comm/third_party/botan/src/lib/filters/basefilt.h
@@ -0,0 +1,18 @@
+/*
+* Basic Filters
+* (C) 1999-2007 Jack Lloyd
+* (C) 2013 Joel Low
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_BASEFILT_H_
+#define BOTAN_BASEFILT_H_
+
+// This header is deprecated and will be removed in a future major release
+
+#include <botan/filters.h>
+
+BOTAN_DEPRECATED_HEADER(basefilt.h)
+
+#endif
diff --git a/comm/third_party/botan/src/lib/filters/buf_filt.cpp b/comm/third_party/botan/src/lib/filters/buf_filt.cpp
new file mode 100644
index 0000000000..0e34a56dd6
--- /dev/null
+++ b/comm/third_party/botan/src/lib/filters/buf_filt.cpp
@@ -0,0 +1,103 @@
+/*
+* Buffered Filter
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/filters.h>
+#include <botan/mem_ops.h>
+#include <botan/internal/rounding.h>
+#include <botan/exceptn.h>
+
+namespace Botan {
+
+/*
+* Buffered_Filter Constructor
+*/
+Buffered_Filter::Buffered_Filter(size_t b, size_t f) :
+ m_main_block_mod(b), m_final_minimum(f)
+ {
+ if(m_main_block_mod == 0)
+ throw Invalid_Argument("m_main_block_mod == 0");
+
+ if(m_final_minimum > m_main_block_mod)
+ throw Invalid_Argument("m_final_minimum > m_main_block_mod");
+
+ m_buffer.resize(2 * m_main_block_mod);
+ m_buffer_pos = 0;
+ }
+
+/*
+* Buffer input into blocks, trying to minimize copying
+*/
+void Buffered_Filter::write(const uint8_t input[], size_t input_size)
+ {
+ if(!input_size)
+ return;
+
+ if(m_buffer_pos + input_size >= m_main_block_mod + m_final_minimum)
+ {
+ size_t to_copy = std::min<size_t>(m_buffer.size() - m_buffer_pos, input_size);
+
+ copy_mem(&m_buffer[m_buffer_pos], input, to_copy);
+ m_buffer_pos += to_copy;
+
+ input += to_copy;
+ input_size -= to_copy;
+
+ size_t total_to_consume =
+ round_down(std::min(m_buffer_pos,
+ m_buffer_pos + input_size - m_final_minimum),
+ m_main_block_mod);
+
+ buffered_block(m_buffer.data(), total_to_consume);
+
+ m_buffer_pos -= total_to_consume;
+
+ copy_mem(m_buffer.data(), m_buffer.data() + total_to_consume, m_buffer_pos);
+ }
+
+ 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;
+
+ if(to_copy)
+ {
+ buffered_block(input, to_copy);
+
+ input += to_copy;
+ input_size -= to_copy;
+ }
+ }
+
+ copy_mem(&m_buffer[m_buffer_pos], input, input_size);
+ m_buffer_pos += input_size;
+ }
+
+/*
+* Finish/flush operation
+*/
+void Buffered_Filter::end_msg()
+ {
+ if(m_buffer_pos < m_final_minimum)
+ throw Invalid_State("Buffered filter end_msg without enough input");
+
+ 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;
+ buffered_block(m_buffer.data(), spare_bytes);
+ buffered_final(&m_buffer[spare_bytes], m_buffer_pos - spare_bytes);
+ }
+ else
+ {
+ buffered_final(m_buffer.data(), m_buffer_pos);
+ }
+
+ m_buffer_pos = 0;
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/filters/buf_filt.h b/comm/third_party/botan/src/lib/filters/buf_filt.h
new file mode 100644
index 0000000000..1f9ed3eff0
--- /dev/null
+++ b/comm/third_party/botan/src/lib/filters/buf_filt.h
@@ -0,0 +1,14 @@
+/*
+* Buffered Filter
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_BUFFERED_FILTER_H_
+#define BOTAN_BUFFERED_FILTER_H_
+
+#include <botan/filters.h>
+BOTAN_DEPRECATED_HEADER(buf_filt.h)
+
+#endif
diff --git a/comm/third_party/botan/src/lib/filters/cipher_filter.cpp b/comm/third_party/botan/src/lib/filters/cipher_filter.cpp
new file mode 100644
index 0000000000..720aeac6fd
--- /dev/null
+++ b/comm/third_party/botan/src/lib/filters/cipher_filter.cpp
@@ -0,0 +1,103 @@
+/*
+* Filter interface for Cipher_Modes
+* (C) 2013,2014,2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/filters.h>
+#include <botan/internal/rounding.h>
+
+namespace Botan {
+
+namespace {
+
+size_t choose_update_size(size_t update_granularity)
+ {
+ const size_t target_size = 1024;
+
+ if(update_granularity >= target_size)
+ return update_granularity;
+
+ return round_up(target_size, update_granularity);
+ }
+
+}
+
+Cipher_Mode_Filter::Cipher_Mode_Filter(Cipher_Mode* mode) :
+ Buffered_Filter(choose_update_size(mode->update_granularity()),
+ mode->minimum_final_size()),
+ m_mode(mode),
+ m_nonce(mode->default_nonce_length()),
+ m_buffer(m_mode->update_granularity())
+ {
+ }
+
+std::string Cipher_Mode_Filter::name() const
+ {
+ return m_mode->name();
+ }
+
+void Cipher_Mode_Filter::set_iv(const InitializationVector& iv)
+ {
+ m_nonce = unlock(iv.bits_of());
+ }
+
+void Cipher_Mode_Filter::set_key(const SymmetricKey& key)
+ {
+ m_mode->set_key(key);
+ }
+
+Key_Length_Specification Cipher_Mode_Filter::key_spec() const
+ {
+ return m_mode->key_spec();
+ }
+
+bool Cipher_Mode_Filter::valid_iv_length(size_t length) const
+ {
+ return m_mode->valid_nonce_length(length);
+ }
+
+void Cipher_Mode_Filter::write(const uint8_t input[], size_t input_length)
+ {
+ Buffered_Filter::write(input, input_length);
+ }
+
+void Cipher_Mode_Filter::end_msg()
+ {
+ Buffered_Filter::end_msg();
+ }
+
+void Cipher_Mode_Filter::start_msg()
+ {
+ if(m_nonce.empty() && !m_mode->valid_nonce_length(0))
+ throw Invalid_State("Cipher " + m_mode->name() + " requires a fresh nonce for each message");
+
+ m_mode->start(m_nonce);
+ m_nonce.clear();
+ }
+
+void Cipher_Mode_Filter::buffered_block(const uint8_t input[], size_t input_length)
+ {
+ while(input_length)
+ {
+ const size_t take = std::min(m_mode->update_granularity(), input_length);
+
+ m_buffer.assign(input, input + take);
+ m_mode->update(m_buffer);
+
+ send(m_buffer);
+
+ input += take;
+ input_length -= take;
+ }
+ }
+
+void Cipher_Mode_Filter::buffered_final(const uint8_t input[], size_t input_length)
+ {
+ secure_vector<uint8_t> buf(input, input + input_length);
+ m_mode->finish(buf);
+ send(buf);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/filters/cipher_filter.h b/comm/third_party/botan/src/lib/filters/cipher_filter.h
new file mode 100644
index 0000000000..84fc58b6ee
--- /dev/null
+++ b/comm/third_party/botan/src/lib/filters/cipher_filter.h
@@ -0,0 +1,14 @@
+/*
+* Filter interface for ciphers
+* (C) 2013,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_CIPHER_FILTER_H_
+#define BOTAN_CIPHER_FILTER_H_
+
+#include <botan/filters.h>
+BOTAN_DEPRECATED_HEADER(cipher_filter.h)
+
+#endif
diff --git a/comm/third_party/botan/src/lib/filters/comp_filter.cpp b/comm/third_party/botan/src/lib/filters/comp_filter.cpp
new file mode 100644
index 0000000000..c2814460ae
--- /dev/null
+++ b/comm/third_party/botan/src/lib/filters/comp_filter.cpp
@@ -0,0 +1,122 @@
+/*
+* Filter interface for compression
+* (C) 2014,2015,2016 Jack Lloyd
+* (C) 2015 Matej Kenda
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/filters.h>
+#include <botan/exceptn.h>
+
+#if defined(BOTAN_HAS_COMPRESSION)
+ #include <botan/compression.h>
+#endif
+
+namespace Botan {
+
+#if defined(BOTAN_HAS_COMPRESSION)
+
+Compression_Filter::Compression_Filter(const std::string& type, size_t level, size_t bs) :
+ m_comp(make_compressor(type)),
+ m_buffersize(std::max<size_t>(bs, 256)),
+ m_level(level)
+ {
+ if(!m_comp)
+ {
+ throw Invalid_Argument("Compression type '" + type + "' not found");
+ }
+ }
+
+Compression_Filter::~Compression_Filter() { /* for unique_ptr */ }
+
+std::string Compression_Filter::name() const
+ {
+ return m_comp->name();
+ }
+
+void Compression_Filter::start_msg()
+ {
+ m_comp->start(m_level);
+ }
+
+void Compression_Filter::write(const uint8_t input[], size_t input_length)
+ {
+ while(input_length)
+ {
+ const size_t take = std::min(m_buffersize, input_length);
+ BOTAN_ASSERT(take > 0, "Consumed something");
+
+ m_buffer.assign(input, input + take);
+ m_comp->update(m_buffer);
+
+ send(m_buffer);
+
+ input += take;
+ input_length -= take;
+ }
+ }
+
+void Compression_Filter::flush()
+ {
+ m_buffer.clear();
+ m_comp->update(m_buffer, 0, true);
+ send(m_buffer);
+ }
+
+void Compression_Filter::end_msg()
+ {
+ m_buffer.clear();
+ m_comp->finish(m_buffer);
+ send(m_buffer);
+ }
+
+Decompression_Filter::Decompression_Filter(const std::string& type, size_t bs) :
+ m_comp(make_decompressor(type)),
+ m_buffersize(std::max<size_t>(bs, 256))
+ {
+ if(!m_comp)
+ {
+ throw Invalid_Argument("Compression type '" + type + "' not found");
+ }
+ }
+
+Decompression_Filter::~Decompression_Filter() { /* for unique_ptr */ }
+
+std::string Decompression_Filter::name() const
+ {
+ return m_comp->name();
+ }
+
+void Decompression_Filter::start_msg()
+ {
+ m_comp->start();
+ }
+
+void Decompression_Filter::write(const uint8_t input[], size_t input_length)
+ {
+ while(input_length)
+ {
+ const size_t take = std::min(m_buffersize, input_length);
+ BOTAN_ASSERT(take > 0, "Consumed something");
+
+ m_buffer.assign(input, input + take);
+ m_comp->update(m_buffer);
+
+ send(m_buffer);
+
+ input += take;
+ input_length -= take;
+ }
+ }
+
+void Decompression_Filter::end_msg()
+ {
+ m_buffer.clear();
+ m_comp->finish(m_buffer);
+ send(m_buffer);
+ }
+
+#endif
+
+}
diff --git a/comm/third_party/botan/src/lib/filters/comp_filter.h b/comm/third_party/botan/src/lib/filters/comp_filter.h
new file mode 100644
index 0000000000..86b587ac30
--- /dev/null
+++ b/comm/third_party/botan/src/lib/filters/comp_filter.h
@@ -0,0 +1,15 @@
+/*
+* Filter interface for compression
+* (C) 2014,2015,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_COMPRESSION_FILTER_H_
+#define BOTAN_COMPRESSION_FILTER_H_
+
+BOTAN_DEPRECATED_HEADER(comp_filter.h)
+
+#include <botan/filters.h>
+
+#endif
diff --git a/comm/third_party/botan/src/lib/filters/data_snk.cpp b/comm/third_party/botan/src/lib/filters/data_snk.cpp
new file mode 100644
index 0000000000..9f0ddff96d
--- /dev/null
+++ b/comm/third_party/botan/src/lib/filters/data_snk.cpp
@@ -0,0 +1,75 @@
+/*
+* DataSink
+* (C) 1999-2007 Jack Lloyd
+* 2005 Matthew Gregan
+* 2017 Philippe Lieser
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/data_snk.h>
+#include <botan/exceptn.h>
+#include <ostream>
+
+#if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
+ #include <fstream>
+#endif
+
+namespace Botan {
+
+/*
+* 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);
+ if(!m_sink.good())
+ throw Stream_IO_Error("DataSink_Stream: Failure writing to " +
+ m_identifier);
+ }
+
+/*
+* Flush the stream
+*/
+void DataSink_Stream::end_msg()
+ {
+ m_sink.flush();
+ }
+
+/*
+* DataSink_Stream Constructor
+*/
+DataSink_Stream::DataSink_Stream(std::ostream& out,
+ const std::string& name) :
+ m_identifier(name),
+ m_sink(out)
+ {
+ }
+
+#if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
+
+/*
+* DataSink_Stream Constructor
+*/
+DataSink_Stream::DataSink_Stream(const std::string& path,
+ bool use_binary) :
+ m_identifier(path),
+ m_sink_memory(new std::ofstream(path, use_binary ? std::ios::binary : std::ios::out)),
+ m_sink(*m_sink_memory)
+ {
+ if(!m_sink.good())
+ {
+ throw Stream_IO_Error("DataSink_Stream: Failure opening " + path);
+ }
+ }
+#endif
+
+/*
+* DataSink_Stream Destructor
+*/
+DataSink_Stream::~DataSink_Stream()
+ {
+ // for ~unique_ptr
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/filters/data_snk.h b/comm/third_party/botan/src/lib/filters/data_snk.h
new file mode 100644
index 0000000000..49484b1c1a
--- /dev/null
+++ b/comm/third_party/botan/src/lib/filters/data_snk.h
@@ -0,0 +1,76 @@
+/*
+* DataSink
+* (C) 1999-2007 Jack Lloyd
+* 2017 Philippe Lieser
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_DATA_SINK_H_
+#define BOTAN_DATA_SINK_H_
+
+#include <botan/filter.h>
+#include <memory>
+#include <iosfwd>
+
+namespace Botan {
+
+/**
+* This class represents abstract data sink objects.
+*/
+class BOTAN_PUBLIC_API(2,0) DataSink : public Filter
+ {
+ public:
+ bool attachable() override { return false; }
+ DataSink() = default;
+ virtual ~DataSink() = default;
+
+ DataSink& operator=(const DataSink&) = delete;
+ DataSink(const DataSink&) = delete;
+ };
+
+/**
+* This class represents a data sink which writes its output to a stream.
+*/
+class BOTAN_PUBLIC_API(2,0) DataSink_Stream final : public DataSink
+ {
+ public:
+ /**
+ * Construct a DataSink_Stream from a stream.
+ * @param stream the stream to write to
+ * @param name identifier
+ */
+ DataSink_Stream(std::ostream& stream,
+ const std::string& name = "<std::ostream>");
+
+#if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
+
+ /**
+ * Construct a DataSink_Stream from a filesystem path name.
+ * @param pathname the name of the file to open a stream to
+ * @param use_binary indicates whether to treat the file
+ * as a binary file or not
+ */
+ DataSink_Stream(const std::string& pathname,
+ bool use_binary = false);
+#endif
+
+ std::string name() const override { return m_identifier; }
+
+ void write(const uint8_t[], size_t) override;
+
+ void end_msg() override;
+
+ ~DataSink_Stream();
+
+ private:
+ const std::string m_identifier;
+
+ // May be null, if m_sink was an external reference
+ std::unique_ptr<std::ostream> m_sink_memory;
+ std::ostream& m_sink;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/filters/fd_unix/fd_unix.cpp b/comm/third_party/botan/src/lib/filters/fd_unix/fd_unix.cpp
new file mode 100644
index 0000000000..657dde3b40
--- /dev/null
+++ b/comm/third_party/botan/src/lib/filters/fd_unix/fd_unix.cpp
@@ -0,0 +1,55 @@
+/*
+* Pipe I/O for Unix
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/pipe.h>
+#include <botan/exceptn.h>
+#include <unistd.h>
+
+namespace Botan {
+
+/*
+* Write data from a pipe into a Unix fd
+*/
+int operator<<(int fd, Pipe& pipe)
+ {
+ secure_vector<uint8_t> buffer(BOTAN_DEFAULT_BUFFER_SIZE);
+ while(pipe.remaining())
+ {
+ size_t got = pipe.read(buffer.data(), buffer.size());
+ size_t position = 0;
+ while(got)
+ {
+ ssize_t ret = ::write(fd, &buffer[position], got);
+ if(ret < 0)
+ throw Stream_IO_Error("Pipe output operator (unixfd) has failed");
+
+ position += static_cast<size_t>(ret);
+ got -= static_cast<size_t>(ret);
+ }
+ }
+ return fd;
+ }
+
+/*
+* Read data from a Unix fd into a pipe
+*/
+int operator>>(int fd, Pipe& pipe)
+ {
+ secure_vector<uint8_t> buffer(BOTAN_DEFAULT_BUFFER_SIZE);
+ while(true)
+ {
+ 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)
+ break;
+ pipe.write(buffer.data(), static_cast<size_t>(ret));
+ }
+ return fd;
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/filters/fd_unix/fd_unix.h b/comm/third_party/botan/src/lib/filters/fd_unix/fd_unix.h
new file mode 100644
index 0000000000..b5a3a4cd8b
--- /dev/null
+++ b/comm/third_party/botan/src/lib/filters/fd_unix/fd_unix.h
@@ -0,0 +1,35 @@
+/*
+* Pipe I/O for Unix
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_PIPE_UNIXFD_H_
+#define BOTAN_PIPE_UNIXFD_H_
+
+#include <botan/types.h>
+
+namespace Botan {
+
+class Pipe;
+
+/**
+* Stream output operator; dumps the results from pipe's default
+* message to the output stream.
+* @param out file descriptor for an open output stream
+* @param pipe the pipe
+*/
+int BOTAN_PUBLIC_API(2,0) operator<<(int out, Pipe& pipe);
+
+/**
+* File descriptor input operator; dumps the remaining bytes of input
+* to the (assumed open) pipe message.
+* @param in file descriptor for an open input stream
+* @param pipe the pipe
+*/
+int BOTAN_PUBLIC_API(2,0) operator>>(int in, Pipe& pipe);
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/filters/fd_unix/info.txt b/comm/third_party/botan/src/lib/filters/fd_unix/info.txt
new file mode 100644
index 0000000000..109c9c9abe
--- /dev/null
+++ b/comm/third_party/botan/src/lib/filters/fd_unix/info.txt
@@ -0,0 +1,7 @@
+<defines>
+PIPE_UNIXFD_IO -> 20131128
+</defines>
+
+<os_features>
+posix1
+</os_features>
diff --git a/comm/third_party/botan/src/lib/filters/filter.cpp b/comm/third_party/botan/src/lib/filters/filter.cpp
new file mode 100644
index 0000000000..6653fc7815
--- /dev/null
+++ b/comm/third_party/botan/src/lib/filters/filter.cpp
@@ -0,0 +1,129 @@
+/*
+* Filter
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/filter.h>
+#include <botan/exceptn.h>
+
+namespace Botan {
+
+/*
+* Filter Constructor
+*/
+Filter::Filter()
+ {
+ m_next.resize(1);
+ m_port_num = 0;
+ m_filter_owns = 0;
+ m_owned = false;
+ }
+
+/*
+* Send data to all ports
+*/
+void Filter::send(const uint8_t input[], size_t length)
+ {
+ if(!length)
+ return;
+
+ bool nothing_attached = true;
+ for(size_t j = 0; j != total_ports(); ++j)
+ if(m_next[j])
+ {
+ if(m_write_queue.size())
+ m_next[j]->write(m_write_queue.data(), m_write_queue.size());
+ m_next[j]->write(input, length);
+ nothing_attached = false;
+ }
+
+ if(nothing_attached)
+ m_write_queue += std::make_pair(input, length);
+ else
+ m_write_queue.clear();
+ }
+
+/*
+* Start a new message
+*/
+void Filter::new_msg()
+ {
+ start_msg();
+ for(size_t j = 0; j != total_ports(); ++j)
+ if(m_next[j])
+ m_next[j]->new_msg();
+ }
+
+/*
+* End the current message
+*/
+void Filter::finish_msg()
+ {
+ end_msg();
+ for(size_t j = 0; j != total_ports(); ++j)
+ if(m_next[j])
+ m_next[j]->finish_msg();
+ }
+
+/*
+* Attach a filter to the current port
+*/
+void Filter::attach(Filter* new_filter)
+ {
+ if(new_filter)
+ {
+ Filter* last = this;
+ while(last->get_next())
+ last = last->get_next();
+ last->m_next[last->current_port()] = new_filter;
+ }
+ }
+
+/*
+* Set the active port on a filter
+*/
+void Filter::set_port(size_t new_port)
+ {
+ if(new_port >= total_ports())
+ throw Invalid_Argument("Filter: Invalid port number");
+ m_port_num = new_port;
+ }
+
+/*
+* Return the next Filter in the logical chain
+*/
+Filter* Filter::get_next() const
+ {
+ if(m_port_num < m_next.size())
+ return m_next[m_port_num];
+ return nullptr;
+ }
+
+/*
+* Set the next Filters
+*/
+void Filter::set_next(Filter* filters[], size_t size)
+ {
+ m_next.clear();
+
+ m_port_num = 0;
+ m_filter_owns = 0;
+
+ while(size && filters && (filters[size-1] == nullptr))
+ --size;
+
+ if(filters && size)
+ m_next.assign(filters, filters + size);
+ }
+
+/*
+* Return the total number of ports
+*/
+size_t Filter::total_ports() const
+ {
+ return m_next.size();
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/filters/filter.h b/comm/third_party/botan/src/lib/filters/filter.h
new file mode 100644
index 0000000000..94b9c6ccd0
--- /dev/null
+++ b/comm/third_party/botan/src/lib/filters/filter.h
@@ -0,0 +1,175 @@
+/*
+* Filter
+* (C) 1999-2007 Jack Lloyd
+* (C) 2013 Joel Low
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_FILTER_H_
+#define BOTAN_FILTER_H_
+
+#include <botan/secmem.h>
+#include <vector>
+#include <string>
+
+namespace Botan {
+
+/**
+* This class represents general abstract filter objects.
+*/
+class BOTAN_PUBLIC_API(2,0) Filter
+ {
+ public:
+ /**
+ * @return descriptive name for this filter
+ */
+ virtual std::string name() const = 0;
+
+ /**
+ * Write a portion of a message to this filter.
+ * @param input the input as a byte array
+ * @param length the length of the byte array input
+ */
+ virtual void write(const uint8_t input[], size_t length) = 0;
+
+ /**
+ * Start a new message. Must be closed by end_msg() before another
+ * message can be started.
+ */
+ virtual void start_msg() { /* default empty */ }
+
+ /**
+ * Notify that the current message is finished; flush buffers and
+ * do end-of-message processing (if any).
+ */
+ virtual void end_msg() { /* default empty */ }
+
+ /**
+ * Check whether this filter is an attachable filter.
+ * @return true if this filter is attachable, false otherwise
+ */
+ virtual bool attachable() { return true; }
+
+ virtual ~Filter() = default;
+ protected:
+ /**
+ * @param in some input for the filter
+ * @param length the length of in
+ */
+ virtual void send(const uint8_t in[], size_t length);
+
+ /**
+ * @param in some input for the filter
+ */
+ void send(uint8_t in) { send(&in, 1); }
+
+ /**
+ * @param in some input for the filter
+ */
+ template<typename Alloc>
+ void send(const std::vector<uint8_t, Alloc>& in)
+ {
+ send(in.data(), in.size());
+ }
+
+ /**
+ * @param in some input for the filter
+ * @param length the number of bytes of in to send
+ */
+ template<typename Alloc>
+ void send(const std::vector<uint8_t, Alloc>& in, size_t length)
+ {
+ BOTAN_ASSERT_NOMSG(length <= in.size());
+ send(in.data(), length);
+ }
+
+ Filter();
+
+ Filter(const Filter&) = delete;
+
+ Filter& operator=(const Filter&) = delete;
+
+ private:
+ /**
+ * Start a new message in *this and all following filters. Only for
+ * internal use, not intended for use in client applications.
+ */
+ void new_msg();
+
+ /**
+ * End a new message in *this and all following filters. Only for
+ * internal use, not intended for use in client applications.
+ */
+ void finish_msg();
+
+ friend class Pipe;
+ friend class Fanout_Filter;
+
+ size_t total_ports() const;
+ size_t current_port() const { return m_port_num; }
+
+ /**
+ * Set the active port
+ * @param new_port the new value
+ */
+ void set_port(size_t new_port);
+
+ size_t owns() const { return m_filter_owns; }
+
+ /**
+ * Attach another filter to this one
+ * @param f filter to attach
+ */
+ void attach(Filter* f);
+
+ /**
+ * @param filters the filters to set
+ * @param count number of items in filters
+ */
+ void set_next(Filter* filters[], size_t count);
+ Filter* get_next() const;
+
+ secure_vector<uint8_t> m_write_queue;
+ std::vector<Filter*> m_next; // not owned
+ size_t m_port_num, m_filter_owns;
+
+ // true if filter belongs to a pipe --> prohibit filter sharing!
+ bool m_owned;
+ };
+
+/**
+* This is the abstract Fanout_Filter base class.
+**/
+class BOTAN_PUBLIC_API(2,0) Fanout_Filter : public Filter
+ {
+ protected:
+ /**
+ * Increment the number of filters past us that we own
+ */
+ void incr_owns() { ++m_filter_owns; }
+
+ void set_port(size_t n) { Filter::set_port(n); }
+
+ void set_next(Filter* f[], size_t n) { Filter::set_next(f, n); }
+
+ void attach(Filter* f) { Filter::attach(f); }
+
+ private:
+ friend class Threaded_Fork;
+ using Filter::m_write_queue;
+ using Filter::total_ports;
+ using Filter::m_next;
+ };
+
+/**
+* The type of checking to be performed by decoders:
+* NONE - no checks, IGNORE_WS - perform checks, but ignore
+* whitespaces, FULL_CHECK - perform checks, also complain
+* about white spaces.
+*/
+enum Decoder_Checking { NONE, IGNORE_WS, FULL_CHECK };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/filters/filters.h b/comm/third_party/botan/src/lib/filters/filters.h
new file mode 100644
index 0000000000..c19e1a749c
--- /dev/null
+++ b/comm/third_party/botan/src/lib/filters/filters.h
@@ -0,0 +1,741 @@
+/*
+* Common Filters
+* (C) 1999-2007,2015 Jack Lloyd
+* (C) 2013 Joel Low
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_FILTERS_H_
+#define BOTAN_FILTERS_H_
+
+#include <botan/secmem.h>
+#include <botan/data_snk.h>
+#include <botan/pipe.h>
+#include <botan/symkey.h>
+#include <botan/cipher_mode.h>
+
+#if defined(BOTAN_TARGET_OS_HAS_THREADS)
+ #include <thread>
+#endif
+
+#if defined(BOTAN_HAS_STREAM_CIPHER)
+ #include <botan/stream_cipher.h>
+#endif
+
+#if defined(BOTAN_HAS_HASH)
+ #include <botan/hash.h>
+#endif
+
+#if defined(BOTAN_HAS_MAC)
+ #include <botan/mac.h>
+#endif
+
+namespace Botan {
+
+/**
+* Filter mixin that breaks input into blocks, useful for
+* cipher modes
+*/
+class BOTAN_PUBLIC_API(2,0) Buffered_Filter
+ {
+ public:
+ /**
+ * Write bytes into the buffered filter, which will them emit them
+ * in calls to buffered_block in the subclass
+ * @param in the input bytes
+ * @param length of in in bytes
+ */
+ void write(const uint8_t in[], size_t length);
+
+ template<typename Alloc>
+ void write(const std::vector<uint8_t, Alloc>& in, size_t length)
+ {
+ write(in.data(), length);
+ }
+
+ /**
+ * Finish a message, emitting to buffered_block and buffered_final
+ * Will throw an exception if less than final_minimum bytes were
+ * written into the filter.
+ */
+ void end_msg();
+
+ /**
+ * Initialize a Buffered_Filter
+ * @param block_size the function buffered_block will be called
+ * with inputs which are a multiple of this size
+ * @param final_minimum the function buffered_final will be called
+ * with at least this many bytes.
+ */
+ Buffered_Filter(size_t block_size, size_t final_minimum);
+
+ virtual ~Buffered_Filter() = default;
+ protected:
+ /**
+ * The block processor, implemented by subclasses
+ * @param input some input bytes
+ * @param length the size of input, guaranteed to be a multiple
+ * of block_size
+ */
+ virtual void buffered_block(const uint8_t input[], size_t length) = 0;
+
+ /**
+ * The final block, implemented by subclasses
+ * @param input some input bytes
+ * @param length the size of input, guaranteed to be at least
+ * final_minimum bytes
+ */
+ virtual void buffered_final(const uint8_t input[], size_t length) = 0;
+
+ /**
+ * @return block size of inputs
+ */
+ size_t buffered_block_size() const { return m_main_block_mod; }
+
+ /**
+ * @return current position in the buffer
+ */
+ size_t current_position() const { return m_buffer_pos; }
+
+ /**
+ * Reset the buffer position
+ */
+ void buffer_reset() { m_buffer_pos = 0; }
+ private:
+ size_t m_main_block_mod, m_final_minimum;
+
+ secure_vector<uint8_t> m_buffer;
+ size_t m_buffer_pos;
+ };
+
+/**
+* This class represents keyed filters, i.e. filters that have to be
+* fed with a key in order to function.
+*/
+class BOTAN_PUBLIC_API(2,0) Keyed_Filter : public Filter
+ {
+ public:
+ /**
+ * Set the key of this filter
+ * @param key the key to use
+ */
+ virtual void set_key(const SymmetricKey& key) = 0;
+
+ /**
+ * Set the initialization vector of this filter. Note: you should
+ * call set_iv() only after you have called set_key()
+ * @param iv the initialization vector to use
+ */
+ virtual void set_iv(const InitializationVector& iv)
+ {
+ if(iv.length() != 0)
+ throw Invalid_IV_Length(name(), iv.length());
+ }
+
+ /**
+ * Check whether a key length is valid for this filter
+ * @param length the key length to be checked for validity
+ * @return true if the key length is valid, false otherwise
+ */
+ bool valid_keylength(size_t length) const
+ {
+ return key_spec().valid_keylength(length);
+ }
+
+ /**
+ * @return object describing limits on key size
+ */
+ virtual Key_Length_Specification key_spec() const = 0;
+
+ /**
+ * Check whether an IV length is valid for this filter
+ * @param length the IV length to be checked for validity
+ * @return true if the IV length is valid, false otherwise
+ */
+ virtual bool valid_iv_length(size_t length) const
+ { return (length == 0); }
+ };
+
+/**
+* Filter interface for cipher modes
+*/
+class BOTAN_PUBLIC_API(2,0) Cipher_Mode_Filter final : public Keyed_Filter,
+ private Buffered_Filter
+ {
+ public:
+ explicit Cipher_Mode_Filter(Cipher_Mode* t);
+
+ explicit Cipher_Mode_Filter(std::unique_ptr<Cipher_Mode> t) :
+ Cipher_Mode_Filter(t.release()) {}
+
+ void set_iv(const InitializationVector& iv) override;
+
+ void set_key(const SymmetricKey& key) override;
+
+ Key_Length_Specification key_spec() const override;
+
+ bool valid_iv_length(size_t length) const override;
+
+ std::string name() const override;
+
+ private:
+ void write(const uint8_t input[], size_t input_length) override;
+ void start_msg() override;
+ void end_msg() override;
+
+ void buffered_block(const uint8_t input[], size_t input_length) override;
+ void buffered_final(const uint8_t input[], size_t input_length) override;
+
+ std::unique_ptr<Cipher_Mode> m_mode;
+ std::vector<uint8_t> m_nonce;
+ secure_vector<uint8_t> m_buffer;
+ };
+
+// deprecated aliases, will be removed in a future major release
+typedef Cipher_Mode_Filter Transform_Filter;
+typedef Transform_Filter Transformation_Filter;
+
+/*
+* Get a cipher object
+*/
+
+/**
+* Factory method for general symmetric cipher filters. No key will be
+* set in the filter.
+*
+* @param algo_spec the name of the desired cipher
+* @param direction determines whether the filter will be an encrypting or
+* decrypting filter
+* @return pointer to the encryption or decryption filter
+*/
+inline Keyed_Filter* get_cipher(const std::string& algo_spec,
+ Cipher_Dir direction)
+ {
+ std::unique_ptr<Cipher_Mode> c(Cipher_Mode::create_or_throw(algo_spec, direction));
+ return new Cipher_Mode_Filter(c.release());
+ }
+
+/**
+* Factory method for general symmetric cipher filters.
+* @param algo_spec the name of the desired cipher
+* @param key the key to be used for encryption/decryption performed by
+* the filter
+* @param direction determines whether the filter will be an encrypting
+* or decrypting filter
+* @return pointer to the encryption or decryption filter
+*/
+inline Keyed_Filter* get_cipher(const std::string& algo_spec,
+ const SymmetricKey& key,
+ Cipher_Dir direction)
+ {
+ Keyed_Filter* cipher = get_cipher(algo_spec, direction);
+ cipher->set_key(key);
+ return cipher;
+ }
+
+/**
+* Factory method for general symmetric cipher filters.
+* @param algo_spec the name of the desired cipher
+* @param key the key to be used for encryption/decryption performed by
+* the filter
+* @param iv the initialization vector to be used
+* @param direction determines whether the filter will be an encrypting
+* or decrypting filter
+* @return pointer to newly allocated encryption or decryption filter
+*/
+inline Keyed_Filter* get_cipher(const std::string& algo_spec,
+ const SymmetricKey& key,
+ const InitializationVector& iv,
+ Cipher_Dir direction)
+ {
+ Keyed_Filter* cipher = get_cipher(algo_spec, key, direction);
+ if(iv.length())
+ cipher->set_iv(iv);
+ return cipher;
+ }
+
+#if defined(BOTAN_HAS_STREAM_CIPHER)
+
+/**
+* Stream Cipher Filter
+*/
+class BOTAN_PUBLIC_API(2,0) StreamCipher_Filter final : public Keyed_Filter
+ {
+ public:
+
+ std::string name() const override { return m_cipher->name(); }
+
+ /**
+ * Write input data
+ * @param input data
+ * @param input_len length of input in bytes
+ */
+ void write(const uint8_t input[], size_t input_len) override;
+
+ bool valid_iv_length(size_t iv_len) const override
+ { return m_cipher->valid_iv_length(iv_len); }
+
+ /**
+ * Set the initialization vector for this filter.
+ * @param iv the initialization vector to set
+ */
+ void set_iv(const InitializationVector& iv) override
+ {
+ m_cipher->set_iv(iv.begin(), iv.length());
+ }
+
+ /**
+ * Set the key of this filter.
+ * @param key the key to set
+ */
+ void set_key(const SymmetricKey& key) override { m_cipher->set_key(key); }
+
+ Key_Length_Specification key_spec() const override { return m_cipher->key_spec(); }
+
+ /**
+ * Construct a stream cipher filter.
+ * @param cipher a cipher object to use
+ */
+ explicit StreamCipher_Filter(StreamCipher* cipher);
+
+ /**
+ * Construct a stream cipher filter.
+ * @param cipher a cipher object to use
+ * @param key the key to use inside this filter
+ */
+ StreamCipher_Filter(StreamCipher* cipher, const SymmetricKey& key);
+
+ /**
+ * Construct a stream cipher filter.
+ * @param cipher the name of the desired cipher
+ */
+ explicit StreamCipher_Filter(const std::string& cipher);
+
+ /**
+ * Construct a stream cipher filter.
+ * @param cipher the name of the desired cipher
+ * @param key the key to use inside this filter
+ */
+ StreamCipher_Filter(const std::string& cipher, const SymmetricKey& key);
+ private:
+ secure_vector<uint8_t> m_buffer;
+ std::unique_ptr<StreamCipher> m_cipher;
+ };
+#endif
+
+#if defined(BOTAN_HAS_HASH)
+
+/**
+* Hash Filter.
+*/
+class BOTAN_PUBLIC_API(2,0) Hash_Filter final : public Filter
+ {
+ public:
+ void write(const uint8_t input[], size_t len) override { m_hash->update(input, len); }
+ void end_msg() override;
+
+ std::string name() const override { return m_hash->name(); }
+
+ /**
+ * Construct a hash filter.
+ * @param hash the hash function to use
+ * @param len the output length of this filter. Leave the default
+ * value 0 if you want to use the full output of the hashfunction
+ * 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) {}
+
+ /**
+ * Construct a hash filter.
+ * @param request the name of the hash algorithm to use
+ * @param len the output length of this filter. Leave the default
+ * value 0 if you want to use the full output of the hashfunction
+ * hash. Otherwise, specify a smaller value here so that the
+ * output of the hash algorithm will be cut off.
+ */
+ Hash_Filter(const std::string& request, size_t len = 0);
+
+ private:
+ std::unique_ptr<HashFunction> m_hash;
+ const size_t m_out_len;
+ };
+#endif
+
+#if defined(BOTAN_HAS_MAC)
+
+/**
+* MessageAuthenticationCode Filter.
+*/
+class BOTAN_PUBLIC_API(2,0) MAC_Filter final : public Keyed_Filter
+ {
+ public:
+ void write(const uint8_t input[], size_t len) override { m_mac->update(input, len); }
+ void end_msg() override;
+
+ std::string name() const override { return m_mac->name(); }
+
+ /**
+ * Set the key of this filter.
+ * @param key the key to set
+ */
+ void set_key(const SymmetricKey& key) override { m_mac->set_key(key); }
+
+ Key_Length_Specification key_spec() const override { return m_mac->key_spec(); }
+
+ /**
+ * Construct a MAC filter. The MAC key will be left empty.
+ * @param mac the MAC to use
+ * @param out_len the output length of this filter. Leave the default
+ * value 0 if you want to use the full output of the
+ * 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)
+ {
+ }
+
+ /**
+ * Construct a MAC filter.
+ * @param mac the MAC to use
+ * @param key the MAC key to use
+ * @param out_len the output length of this filter. Leave the default
+ * value 0 if you want to use the full output of the
+ * MAC. Otherwise, specify a smaller value here so that the
+ * output of the MAC will be cut off.
+ */
+ MAC_Filter(MessageAuthenticationCode* mac,
+ const SymmetricKey& key,
+ size_t out_len = 0) :
+ m_mac(mac),
+ m_out_len(out_len)
+ {
+ m_mac->set_key(key);
+ }
+
+ /**
+ * Construct a MAC filter. The MAC key will be left empty.
+ * @param mac the name of the MAC to use
+ * @param len the output length of this filter. Leave the default
+ * value 0 if you want to use the full output of the
+ * MAC. Otherwise, specify a smaller value here so that the
+ * output of the MAC will be cut off.
+ */
+ MAC_Filter(const std::string& mac, size_t len = 0);
+
+ /**
+ * Construct a MAC filter.
+ * @param mac the name of the MAC to use
+ * @param key the MAC key to use
+ * @param len the output length of this filter. Leave the default
+ * value 0 if you want to use the full output of the
+ * MAC. Otherwise, specify a smaller value here so that the
+ * output of the MAC will be cut off.
+ */
+ MAC_Filter(const std::string& mac, const SymmetricKey& key,
+ size_t len = 0);
+ private:
+ std::unique_ptr<MessageAuthenticationCode> m_mac;
+ const size_t m_out_len;
+ };
+#endif
+
+#if defined(BOTAN_HAS_COMPRESSION)
+
+class Compression_Algorithm;
+class Decompression_Algorithm;
+
+/**
+* Filter interface for compression
+*/
+class BOTAN_PUBLIC_API(2,0) Compression_Filter final : public Filter
+ {
+ public:
+ void start_msg() override;
+ void write(const uint8_t input[], size_t input_length) override;
+ void end_msg() override;
+
+ void flush();
+
+ std::string name() const override;
+
+ Compression_Filter(const std::string& type,
+ size_t compression_level,
+ size_t buffer_size = 4096);
+
+ ~Compression_Filter();
+ private:
+ std::unique_ptr<Compression_Algorithm> m_comp;
+ size_t m_buffersize, m_level;
+ secure_vector<uint8_t> m_buffer;
+ };
+
+/**
+* Filter interface for decompression
+*/
+class BOTAN_PUBLIC_API(2,0) Decompression_Filter final : public Filter
+ {
+ public:
+ void start_msg() override;
+ void write(const uint8_t input[], size_t input_length) override;
+ void end_msg() override;
+
+ std::string name() const override;
+
+ Decompression_Filter(const std::string& type,
+ size_t buffer_size = 4096);
+
+ ~Decompression_Filter();
+ private:
+ std::unique_ptr<Decompression_Algorithm> m_comp;
+ std::size_t m_buffersize;
+ secure_vector<uint8_t> m_buffer;
+ };
+
+#endif
+
+/**
+* This class represents a Base64 encoder.
+*/
+class BOTAN_PUBLIC_API(2,0) Base64_Encoder final : public Filter
+ {
+ public:
+ std::string name() const override { return "Base64_Encoder"; }
+
+ /**
+ * Input a part of a message to the encoder.
+ * @param input the message to input as a byte array
+ * @param length the length of the byte array input
+ */
+ void write(const uint8_t input[], size_t length) override;
+
+ /**
+ * Inform the Encoder that the current message shall be closed.
+ */
+ void end_msg() override;
+
+ /**
+ * Create a base64 encoder.
+ * @param breaks whether to use line breaks in the output
+ * @param length the length of the lines of the output
+ * @param t_n whether to use a trailing newline
+ */
+ Base64_Encoder(bool breaks = false, size_t length = 72,
+ bool t_n = false);
+ private:
+ void encode_and_send(const uint8_t input[], size_t length,
+ bool final_inputs = false);
+ void do_output(const uint8_t output[], size_t length);
+
+ const size_t m_line_length;
+ const bool m_trailing_newline;
+ std::vector<uint8_t> m_in, m_out;
+ size_t m_position, m_out_position;
+ };
+
+/**
+* This object represents a Base64 decoder.
+*/
+class BOTAN_PUBLIC_API(2,0) Base64_Decoder final : public Filter
+ {
+ public:
+ std::string name() const override { return "Base64_Decoder"; }
+
+ /**
+ * Input a part of a message to the decoder.
+ * @param input the message to input as a byte array
+ * @param length the length of the byte array input
+ */
+ void write(const uint8_t input[], size_t length) override;
+
+ /**
+ * Finish up the current message
+ */
+ void end_msg() override;
+
+ /**
+ * Create a base64 decoder.
+ * @param checking the type of checking that shall be performed by
+ * the decoder
+ */
+ explicit Base64_Decoder(Decoder_Checking checking = NONE);
+ private:
+ const Decoder_Checking m_checking;
+ std::vector<uint8_t> m_in, m_out;
+ size_t m_position;
+ };
+
+/**
+* Converts arbitrary binary data to hex strings, optionally with
+* newlines inserted
+*/
+class BOTAN_PUBLIC_API(2,0) Hex_Encoder final : public Filter
+ {
+ public:
+ /**
+ * Whether to use uppercase or lowercase letters for the encoded string.
+ */
+ enum Case { Uppercase, Lowercase };
+
+ std::string name() const override { return "Hex_Encoder"; }
+
+ void write(const uint8_t in[], size_t length) override;
+ void end_msg() override;
+
+ /**
+ * Create a hex encoder.
+ * @param the_case the case to use in the encoded strings.
+ */
+ explicit Hex_Encoder(Case the_case);
+
+ /**
+ * Create a hex encoder.
+ * @param newlines should newlines be used
+ * @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);
+ private:
+ void encode_and_send(const uint8_t[], size_t);
+
+ const Case m_casing;
+ const size_t m_line_length;
+ std::vector<uint8_t> m_in, m_out;
+ size_t m_position, m_counter;
+ };
+
+/**
+* Converts hex strings to bytes
+*/
+class BOTAN_PUBLIC_API(2,0) Hex_Decoder final : public Filter
+ {
+ public:
+ std::string name() const override { return "Hex_Decoder"; }
+
+ void write(const uint8_t[], size_t) override;
+ void end_msg() override;
+
+ /**
+ * Construct a Hex Decoder using the specified
+ * character checking.
+ * @param checking the checking to use during decoding.
+ */
+ explicit Hex_Decoder(Decoder_Checking checking = NONE);
+ private:
+ const Decoder_Checking m_checking;
+ std::vector<uint8_t> m_in, m_out;
+ size_t m_position;
+ };
+
+/**
+* BitBucket is a filter which simply discards all inputs
+*/
+class BOTAN_PUBLIC_API(2,0) BitBucket final : public Filter
+ {
+ public:
+ void write(const uint8_t[], size_t) override { /* discard */ }
+
+ std::string name() const override { return "BitBucket"; }
+ };
+
+/**
+* This class represents Filter chains. A Filter chain is an ordered
+* concatenation of Filters, the input to a Chain sequentially passes
+* through all the Filters contained in the Chain.
+*/
+
+class BOTAN_PUBLIC_API(2,0) Chain final : public Fanout_Filter
+ {
+ public:
+ void write(const uint8_t input[], size_t length) override { send(input, length); }
+
+ std::string name() const override { return "Chain"; }
+
+ /**
+ * 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);
+
+ /**
+ * Construct a chain from range of filters
+ * @param filter_arr the list of filters
+ * @param length how many filters
+ */
+ Chain(Filter* filter_arr[], size_t length);
+ };
+
+/**
+* This class represents a fork filter, whose purpose is to fork the
+* flow of data. It causes an input message to result in n messages at
+* the end of the filter, where n is the number of forks.
+*/
+class BOTAN_PUBLIC_API(2,0) Fork : public Fanout_Filter
+ {
+ public:
+ void write(const uint8_t input[], size_t length) override { send(input, length); }
+ void set_port(size_t n) { Fanout_Filter::set_port(n); }
+
+ std::string name() const override { return "Fork"; }
+
+ /**
+ * Construct a Fork filter with up to four forks.
+ */
+ Fork(Filter*, Filter*, Filter* = nullptr, Filter* = nullptr);
+
+ /**
+ * Construct a Fork from range of filters
+ * @param filter_arr the list of filters
+ * @param length how many filters
+ */
+ Fork(Filter* filter_arr[], size_t length);
+ };
+
+#if defined(BOTAN_HAS_THREAD_UTILS)
+
+/**
+* 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.
+*/
+class BOTAN_PUBLIC_API(2,0) Threaded_Fork final : public Fork
+ {
+ public:
+ std::string name() const override;
+
+ /**
+ * Construct a Threaded_Fork filter with up to four forks.
+ */
+ Threaded_Fork(Filter*, Filter*, Filter* = nullptr, Filter* = 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);
+
+ ~Threaded_Fork();
+
+ private:
+ void set_next(Filter* f[], size_t n);
+ void send(const uint8_t in[], size_t length) override;
+ void thread_delegate_work(const uint8_t input[], size_t length);
+ void thread_entry(Filter* filter);
+
+ std::vector<std::shared_ptr<std::thread>> m_threads;
+ std::unique_ptr<struct Threaded_Fork_Data> m_thread_data;
+ };
+#endif
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/filters/hex_filt.cpp b/comm/third_party/botan/src/lib/filters/hex_filt.cpp
new file mode 100644
index 0000000000..761c73ade1
--- /dev/null
+++ b/comm/third_party/botan/src/lib/filters/hex_filt.cpp
@@ -0,0 +1,170 @@
+/*
+* Hex Encoder/Decoder
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/filters.h>
+#include <botan/hex.h>
+#include <botan/exceptn.h>
+#include <algorithm>
+
+namespace Botan {
+
+/**
+* Size used for internal buffer in hex encoder/decoder
+*/
+const size_t HEX_CODEC_BUFFER_SIZE = 256;
+
+/*
+* Hex_Encoder Constructor
+*/
+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;
+ }
+
+/*
+* Hex_Encoder Constructor
+*/
+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;
+ }
+
+/*
+* Encode and send a block
+*/
+void Hex_Encoder::encode_and_send(const uint8_t block[], size_t length)
+ {
+ hex_encode(cast_uint8_ptr_to_char(m_out.data()),
+ block, length,
+ m_casing == Uppercase);
+
+ 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);
+ send(&m_out[offset], sent);
+ m_counter += sent;
+ remaining -= sent;
+ offset += sent;
+ if(m_counter == m_line_length)
+ {
+ send('\n');
+ m_counter = 0;
+ }
+ }
+ }
+ }
+
+/*
+* Convert some data into hex format
+*/
+void Hex_Encoder::write(const uint8_t input[], size_t length)
+ {
+ buffer_insert(m_in, m_position, input, length);
+ if(m_position + length >= m_in.size())
+ {
+ encode_and_send(m_in.data(), m_in.size());
+ input += (m_in.size() - m_position);
+ length -= (m_in.size() - m_position);
+ while(length >= m_in.size())
+ {
+ encode_and_send(input, m_in.size());
+ input += m_in.size();
+ length -= m_in.size();
+ }
+ copy_mem(m_in.data(), input, length);
+ m_position = 0;
+ }
+ m_position += length;
+ }
+
+/*
+* Flush buffers
+*/
+void Hex_Encoder::end_msg()
+ {
+ encode_and_send(m_in.data(), m_position);
+ if(m_counter && m_line_length)
+ send('\n');
+ m_counter = m_position = 0;
+ }
+
+/*
+* Hex_Decoder Constructor
+*/
+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<size_t>(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 = hex_decode(m_out.data(),
+ cast_uint8_ptr_to_char(m_in.data()),
+ m_position,
+ consumed,
+ m_checking != FULL_CHECK);
+
+ send(m_out, written);
+
+ if(consumed != m_position)
+ {
+ copy_mem(m_in.data(), m_in.data() + consumed, m_position - consumed);
+ m_position = m_position - consumed;
+ }
+ else
+ m_position = 0;
+
+ length -= to_copy;
+ input += to_copy;
+ }
+ }
+
+/*
+* Flush buffers
+*/
+void Hex_Decoder::end_msg()
+ {
+ size_t consumed = 0;
+ 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);
+
+ const bool not_full_bytes = consumed != m_position;
+
+ m_position = 0;
+
+ if(not_full_bytes)
+ throw Invalid_Argument("Hex_Decoder: Input not full bytes");
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/filters/hex_filt.h b/comm/third_party/botan/src/lib/filters/hex_filt.h
new file mode 100644
index 0000000000..b529b52990
--- /dev/null
+++ b/comm/third_party/botan/src/lib/filters/hex_filt.h
@@ -0,0 +1,14 @@
+/*
+* Hex Encoder/Decoder
+* (C) 1999-2010 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_HEX_FILTER_H_
+#define BOTAN_HEX_FILTER_H_
+
+#include <botan/filters.h>
+BOTAN_DEPRECATED_HEADER(hex_filt.h)
+
+#endif
diff --git a/comm/third_party/botan/src/lib/filters/info.txt b/comm/third_party/botan/src/lib/filters/info.txt
new file mode 100644
index 0000000000..2fe9335795
--- /dev/null
+++ b/comm/third_party/botan/src/lib/filters/info.txt
@@ -0,0 +1,31 @@
+<defines>
+FILTERS -> 20160415
+CODEC_FILTERS -> 20131128
+</defines>
+
+<header:public>
+basefilt.h
+comp_filter.h
+cipher_filter.h
+buf_filt.h
+key_filt.h
+
+b64_filt.h
+hex_filt.h
+
+secqueue.h
+
+data_snk.h
+filter.h
+filters.h
+pipe.h
+</header:public>
+
+<header:internal>
+out_buf.h
+</header:internal>
+
+<requires>
+modes
+base64
+</requires>
diff --git a/comm/third_party/botan/src/lib/filters/key_filt.h b/comm/third_party/botan/src/lib/filters/key_filt.h
new file mode 100644
index 0000000000..3d85d886ab
--- /dev/null
+++ b/comm/third_party/botan/src/lib/filters/key_filt.h
@@ -0,0 +1,14 @@
+/*
+* Keyed_Filter
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_KEYED_FILTER_H_
+#define BOTAN_KEYED_FILTER_H_
+
+#include <botan/filter.h>
+BOTAN_DEPRECATED_HEADER(key_filt.h)
+
+#endif
diff --git a/comm/third_party/botan/src/lib/filters/out_buf.cpp b/comm/third_party/botan/src/lib/filters/out_buf.cpp
new file mode 100644
index 0000000000..645cc08236
--- /dev/null
+++ b/comm/third_party/botan/src/lib/filters/out_buf.cpp
@@ -0,0 +1,121 @@
+/*
+* Pipe Output Buffer
+* (C) 1999-2007,2011 Jack Lloyd
+* 2012 Markus Wanner
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/out_buf.h>
+#include <botan/secqueue.h>
+
+namespace Botan {
+
+/*
+* Read data from a message
+*/
+size_t Output_Buffers::read(uint8_t output[], size_t length,
+ Pipe::message_id msg)
+ {
+ SecureQueue* q = get(msg);
+ if(q)
+ return q->read(output, length);
+ return 0;
+ }
+
+/*
+* 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)
+ return q->peek(output, length, stream_offset);
+ return 0;
+ }
+
+/*
+* Check available bytes in a message
+*/
+size_t Output_Buffers::remaining(Pipe::message_id msg) const
+ {
+ SecureQueue* q = get(msg);
+ if(q)
+ return q->size();
+ return 0;
+ }
+
+/*
+* 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)
+ return q->get_bytes_read();
+ return 0;
+ }
+
+/*
+* Add a new output queue
+*/
+void Output_Buffers::add(SecureQueue* queue)
+ {
+ BOTAN_ASSERT(queue, "queue was provided");
+
+ BOTAN_ASSERT(m_buffers.size() < m_buffers.max_size(),
+ "Room was available in container");
+
+ m_buffers.push_back(std::unique_ptr<SecureQueue>(queue));
+ }
+
+/*
+* Retire old output queues
+*/
+void Output_Buffers::retire()
+ {
+ for(size_t i = 0; i != m_buffers.size(); ++i)
+ if(m_buffers[i] && m_buffers[i]->size() == 0)
+ {
+ m_buffers[i].reset();
+ }
+
+ while(m_buffers.size() && !m_buffers[0])
+ {
+ m_buffers.pop_front();
+ m_offset = m_offset + Pipe::message_id(1);
+ }
+ }
+
+/*
+* Get a particular output queue
+*/
+SecureQueue* Output_Buffers::get(Pipe::message_id msg) const
+ {
+ if(msg < m_offset)
+ return nullptr;
+
+ BOTAN_ASSERT(msg < message_count(), "Message number is in range");
+
+ return m_buffers[msg-m_offset].get();
+ }
+
+/*
+* Return the total number of messages
+*/
+Pipe::message_id Output_Buffers::message_count() const
+ {
+ return (m_offset + m_buffers.size());
+ }
+
+/*
+* Output_Buffers Constructor
+*/
+Output_Buffers::Output_Buffers()
+ {
+ m_offset = 0;
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/filters/out_buf.h b/comm/third_party/botan/src/lib/filters/out_buf.h
new file mode 100644
index 0000000000..d6efbdaf27
--- /dev/null
+++ b/comm/third_party/botan/src/lib/filters/out_buf.h
@@ -0,0 +1,44 @@
+/*
+* Output Buffer
+* (C) 1999-2007 Jack Lloyd
+* 2012 Markus Wanner
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_OUTPUT_BUFFER_H_
+#define BOTAN_OUTPUT_BUFFER_H_
+
+#include <botan/types.h>
+#include <botan/pipe.h>
+#include <deque>
+
+namespace Botan {
+
+/**
+* Container of output buffers for Pipe
+*/
+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;
+
+ void add(class SecureQueue*);
+ void retire();
+
+ Pipe::message_id message_count() const;
+
+ Output_Buffers();
+ private:
+ class SecureQueue* get(Pipe::message_id) const;
+
+ std::deque<std::unique_ptr<SecureQueue>> m_buffers;
+ Pipe::message_id m_offset;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/filters/pipe.cpp b/comm/third_party/botan/src/lib/filters/pipe.cpp
new file mode 100644
index 0000000000..0bba81bf2d
--- /dev/null
+++ b/comm/third_party/botan/src/lib/filters/pipe.cpp
@@ -0,0 +1,311 @@
+/*
+* Pipe
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/pipe.h>
+#include <botan/internal/out_buf.h>
+#include <botan/secqueue.h>
+
+namespace Botan {
+
+namespace {
+
+/*
+* A Filter that does nothing
+*/
+class Null_Filter final : public Filter
+ {
+ public:
+ void write(const uint8_t input[], size_t length) override
+ { send(input, length); }
+
+ std::string name() const override { return "Null"; }
+ };
+
+}
+
+/*
+* Pipe Constructor
+*/
+Pipe::Pipe(Filter* f1, Filter* f2, Filter* f3, Filter* f4) :
+ Pipe({f1,f2,f3,f4})
+ {
+ }
+
+/*
+* Pipe Constructor
+*/
+Pipe::Pipe(std::initializer_list<Filter*> args)
+ {
+ m_outputs.reset(new Output_Buffers);
+ m_pipe = nullptr;
+ m_default_read = 0;
+ m_inside_msg = false;
+
+ for(auto i = args.begin(); i != args.end(); ++i)
+ do_append(*i);
+ }
+
+/*
+* Pipe Destructor
+*/
+Pipe::~Pipe()
+ {
+ destruct(m_pipe);
+ }
+
+/*
+* Reset the Pipe
+*/
+void Pipe::reset()
+ {
+ destruct(m_pipe);
+ m_pipe = nullptr;
+ m_inside_msg = false;
+ }
+
+/*
+* Destroy the Pipe
+*/
+void Pipe::destruct(Filter* to_kill)
+ {
+ if(!to_kill || dynamic_cast<SecureQueue*>(to_kill))
+ return;
+ for(size_t j = 0; j != to_kill->total_ports(); ++j)
+ destruct(to_kill->m_next[j]);
+ delete to_kill;
+ }
+
+/*
+* Test if the Pipe has any data in it
+*/
+bool Pipe::end_of_data() const
+ {
+ return (remaining() == 0);
+ }
+
+/*
+* Set the default read message
+*/
+void Pipe::set_default_msg(message_id msg)
+ {
+ if(msg >= message_count())
+ throw Invalid_Argument("Pipe::set_default_msg: msg number is too high");
+ m_default_read = msg;
+ }
+
+/*
+* Process a full message at once
+*/
+void Pipe::process_msg(const uint8_t input[], size_t length)
+ {
+ start_msg();
+ write(input, length);
+ end_msg();
+ }
+
+/*
+* Process a full message at once
+*/
+void Pipe::process_msg(const secure_vector<uint8_t>& input)
+ {
+ process_msg(input.data(), input.size());
+ }
+
+void Pipe::process_msg(const std::vector<uint8_t>& input)
+ {
+ process_msg(input.data(), input.size());
+ }
+
+/*
+* Process a full message at once
+*/
+void Pipe::process_msg(const std::string& input)
+ {
+ process_msg(cast_char_ptr_to_uint8(input.data()), input.length());
+ }
+
+/*
+* Process a full message at once
+*/
+void Pipe::process_msg(DataSource& input)
+ {
+ start_msg();
+ write(input);
+ end_msg();
+ }
+
+/*
+* Start a new message
+*/
+void Pipe::start_msg()
+ {
+ if(m_inside_msg)
+ throw Invalid_State("Pipe::start_msg: Message was already started");
+ if(m_pipe == nullptr)
+ m_pipe = new Null_Filter;
+ find_endpoints(m_pipe);
+ m_pipe->new_msg();
+ m_inside_msg = true;
+ }
+
+/*
+* End the current message
+*/
+void Pipe::end_msg()
+ {
+ if(!m_inside_msg)
+ throw Invalid_State("Pipe::end_msg: Message was already ended");
+ m_pipe->finish_msg();
+ clear_endpoints(m_pipe);
+ if(dynamic_cast<Null_Filter*>(m_pipe))
+ {
+ delete m_pipe;
+ m_pipe = nullptr;
+ }
+ m_inside_msg = false;
+
+ m_outputs->retire();
+ }
+
+/*
+* Find the endpoints of the Pipe
+*/
+void Pipe::find_endpoints(Filter* f)
+ {
+ for(size_t j = 0; j != f->total_ports(); ++j)
+ if(f->m_next[j] && !dynamic_cast<SecureQueue*>(f->m_next[j]))
+ find_endpoints(f->m_next[j]);
+ else
+ {
+ SecureQueue* q = new SecureQueue;
+ f->m_next[j] = q;
+ m_outputs->add(q);
+ }
+ }
+
+/*
+* Remove the SecureQueues attached to the Filter
+*/
+void Pipe::clear_endpoints(Filter* f)
+ {
+ if(!f) return;
+ for(size_t j = 0; j != f->total_ports(); ++j)
+ {
+ if(f->m_next[j] && dynamic_cast<SecureQueue*>(f->m_next[j]))
+ f->m_next[j] = nullptr;
+ clear_endpoints(f->m_next[j]);
+ }
+ }
+
+void Pipe::append(Filter* filter)
+ {
+ do_append(filter);
+ }
+
+void Pipe::append_filter(Filter* filter)
+ {
+ if(m_outputs->message_count() != 0)
+ throw Invalid_State("Cannot call Pipe::append_filter after start_msg");
+
+ do_append(filter);
+ }
+
+void Pipe::prepend(Filter* filter)
+ {
+ do_prepend(filter);
+ }
+
+void Pipe::prepend_filter(Filter* filter)
+ {
+ if(m_outputs->message_count() != 0)
+ throw Invalid_State("Cannot call Pipe::prepend_filter after start_msg");
+
+ do_prepend(filter);
+ }
+
+/*
+* Append a Filter to the Pipe
+*/
+void Pipe::do_append(Filter* filter)
+ {
+ if(!filter)
+ return;
+ if(dynamic_cast<SecureQueue*>(filter))
+ throw Invalid_Argument("Pipe::append: SecureQueue cannot be used");
+ if(filter->m_owned)
+ throw Invalid_Argument("Filters cannot be shared among multiple Pipes");
+
+ if(m_inside_msg)
+ throw Invalid_State("Cannot append to a Pipe while it is processing");
+
+ filter->m_owned = true;
+
+ if(!m_pipe) m_pipe = filter;
+ else m_pipe->attach(filter);
+ }
+
+/*
+* Prepend a Filter to the Pipe
+*/
+void Pipe::do_prepend(Filter* filter)
+ {
+ if(m_inside_msg)
+ throw Invalid_State("Cannot prepend to a Pipe while it is processing");
+ if(!filter)
+ return;
+ if(dynamic_cast<SecureQueue*>(filter))
+ throw Invalid_Argument("Pipe::prepend: SecureQueue cannot be used");
+ if(filter->m_owned)
+ throw Invalid_Argument("Filters cannot be shared among multiple Pipes");
+
+ filter->m_owned = true;
+
+ if(m_pipe) filter->attach(m_pipe);
+ m_pipe = filter;
+ }
+
+/*
+* Pop a Filter off the Pipe
+*/
+void Pipe::pop()
+ {
+ if(m_inside_msg)
+ throw Invalid_State("Cannot pop off a Pipe while it is processing");
+
+ if(!m_pipe)
+ return;
+
+ if(m_pipe->total_ports() > 1)
+ throw Invalid_State("Cannot pop off a Filter with multiple ports");
+
+ size_t to_remove = m_pipe->owns() + 1;
+
+ while(to_remove--)
+ {
+ std::unique_ptr<Filter> to_destroy(m_pipe);
+ m_pipe = m_pipe->m_next[0];
+ }
+ }
+
+/*
+* Return the number of messages in this Pipe
+*/
+Pipe::message_id Pipe::message_count() const
+ {
+ return m_outputs->message_count();
+ }
+
+/*
+* Static Member Variables
+*/
+const Pipe::message_id Pipe::LAST_MESSAGE =
+ static_cast<Pipe::message_id>(-2);
+
+const Pipe::message_id Pipe::DEFAULT_MESSAGE =
+ static_cast<Pipe::message_id>(-1);
+
+}
diff --git a/comm/third_party/botan/src/lib/filters/pipe.h b/comm/third_party/botan/src/lib/filters/pipe.h
new file mode 100644
index 0000000000..03b5160835
--- /dev/null
+++ b/comm/third_party/botan/src/lib/filters/pipe.h
@@ -0,0 +1,379 @@
+/*
+* Pipe
+* (C) 1999-2007 Jack Lloyd
+* 2012 Markus Wanner
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_PIPE_H_
+#define BOTAN_PIPE_H_
+
+#include <botan/data_src.h>
+#include <botan/exceptn.h>
+#include <initializer_list>
+#include <iosfwd>
+
+namespace Botan {
+
+class Filter;
+class Output_Buffers;
+
+/**
+* This class represents pipe objects.
+* A set of filters can be placed into a pipe, and information flows
+* 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.
+*/
+class BOTAN_PUBLIC_API(2,0) Pipe final : public DataSource
+ {
+ public:
+ /**
+ * An opaque type that identifies a message in this Pipe
+ */
+ typedef size_t message_id;
+
+ /**
+ * Exception if you use an invalid message as an argument to
+ * read, remaining, etc
+ */
+ class BOTAN_PUBLIC_API(2,0) Invalid_Message_Number final : public Invalid_Argument
+ {
+ public:
+ /**
+ * @param where the error occurred
+ * @param msg the invalid message id that was used
+ */
+ Invalid_Message_Number(const std::string& where, message_id msg) :
+ Invalid_Argument("Pipe::" + where + ": Invalid message number " +
+ std::to_string(msg))
+ {}
+ };
+
+ /**
+ * A meta-id for whatever the last message is
+ */
+ static const message_id LAST_MESSAGE;
+
+ /**
+ * A meta-id for the default message (set with set_default_msg)
+ */
+ static const message_id DEFAULT_MESSAGE;
+
+ /**
+ * Write input to the pipe, i.e. to its first filter.
+ * @param in the byte array to write
+ * @param length the length of the byte array in
+ */
+ void write(const uint8_t in[], size_t length);
+
+ /**
+ * 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<uint8_t>& in)
+ { write(in.data(), in.size()); }
+
+ /**
+ * Write input to the pipe, i.e. to its first filter.
+ * @param in the std::vector containing the data to write
+ */
+ void write(const std::vector<uint8_t>& in)
+ { write(in.data(), in.size()); }
+
+ /**
+ * Write input to the pipe, i.e. to its first filter.
+ * @param in the string containing the data to write
+ */
+ void write(const std::string& in);
+
+ /**
+ * Write input to the pipe, i.e. to its first filter.
+ * @param in the DataSource to read the data from
+ */
+ void write(DataSource& in);
+
+ /**
+ * Write input to the pipe, i.e. to its first filter.
+ * @param in a single byte to be written
+ */
+ void write(uint8_t in);
+
+ /**
+ * Perform start_msg(), write() and end_msg() sequentially.
+ * @param in the byte array containing the data to write
+ * @param length the length of the byte array to write
+ */
+ void process_msg(const uint8_t in[], size_t length);
+
+ /**
+ * Perform start_msg(), write() and end_msg() sequentially.
+ * @param in the secure_vector containing the data to write
+ */
+ void process_msg(const secure_vector<uint8_t>& in);
+
+ /**
+ * Perform start_msg(), write() and end_msg() sequentially.
+ * @param in the secure_vector containing the data to write
+ */
+ void process_msg(const std::vector<uint8_t>& in);
+
+ /**
+ * Perform start_msg(), write() and end_msg() sequentially.
+ * @param in the string containing the data to write
+ */
+ void process_msg(const std::string& in);
+
+ /**
+ * Perform start_msg(), write() and end_msg() sequentially.
+ * @param in the DataSource providing the data to write
+ */
+ void process_msg(DataSource& in);
+
+ /**
+ * Find out how many bytes are ready to read.
+ * @param msg the number identifying the message
+ * for which the information is desired
+ * @return number of bytes that can still be read
+ */
+ size_t remaining(message_id msg = DEFAULT_MESSAGE) const BOTAN_WARN_UNUSED_RESULT;
+
+ /**
+ * Read the default message from the pipe. Moves the internal
+ * offset so that every call to read will return a new portion of
+ * the message.
+ *
+ * @param output the byte array to write the read bytes to
+ * @param length the length of the byte array output
+ * @return number of bytes actually read into output
+ */
+ size_t read(uint8_t output[], size_t length) override BOTAN_WARN_UNUSED_RESULT;
+
+ /**
+ * Read a specified message from the pipe. Moves the internal
+ * offset so that every call to read will return a new portion of
+ * the message.
+ * @param output the byte array to write the read bytes to
+ * @param length the length of the byte array output
+ * @param msg the number identifying the message to read from
+ * @return number of bytes actually read into output
+ */
+ size_t read(uint8_t output[], size_t length, message_id msg) BOTAN_WARN_UNUSED_RESULT;
+
+ /**
+ * Read a single byte from the pipe. Moves the internal offset so
+ * that every call to read will return a new portion of the
+ * message.
+ *
+ * @param output the byte to write the result to
+ * @param msg the message to read from
+ * @return number of bytes actually read into output
+ */
+ size_t read(uint8_t& output, message_id msg = DEFAULT_MESSAGE) BOTAN_WARN_UNUSED_RESULT;
+
+ /**
+ * Read the full contents of the pipe.
+ * @param msg the number identifying the message to read from
+ * @return secure_vector holding the contents of the pipe
+ */
+ secure_vector<uint8_t> read_all(message_id msg = DEFAULT_MESSAGE) BOTAN_WARN_UNUSED_RESULT;
+
+ /**
+ * Read the full contents of the pipe.
+ * @param msg the number identifying the message to read from
+ * @return string holding the contents of the pipe
+ */
+ std::string read_all_as_string(message_id msg = DEFAULT_MESSAGE) BOTAN_WARN_UNUSED_RESULT;
+
+ /**
+ * Read from the default message but do not modify the internal
+ * offset. Consecutive calls to peek() will return portions of
+ * the message starting at the same position.
+ * @param output the byte array to write the peeked message part to
+ * @param length the length of the byte array output
+ * @param offset the offset from the current position in message
+ * @return number of bytes actually peeked and written into output
+ */
+ size_t peek(uint8_t output[], size_t length, size_t offset) const override BOTAN_WARN_UNUSED_RESULT;
+
+ /** Read from the specified message but do not modify the
+ * internal offset. Consecutive calls to peek() will return
+ * portions of the message starting at the same position.
+ * @param output the byte array to write the peeked message part to
+ * @param length the length of the byte array output
+ * @param offset the offset from the current position in message
+ * @param msg the number identifying the message to peek from
+ * @return number of bytes actually peeked and written into output
+ */
+ size_t peek(uint8_t output[], size_t length,
+ size_t offset, message_id msg) const BOTAN_WARN_UNUSED_RESULT;
+
+ /** Read a single byte from the specified message but do not
+ * modify the internal offset. Consecutive calls to peek() will
+ * return portions of the message starting at the same position.
+ * @param output the byte to write the peeked message byte to
+ * @param offset the offset from the current position in message
+ * @param msg the number identifying the message to peek from
+ * @return number of bytes actually peeked and written into output
+ */
+ size_t peek(uint8_t& output, size_t offset,
+ message_id msg = DEFAULT_MESSAGE) const BOTAN_WARN_UNUSED_RESULT;
+
+ /**
+ * @return the number of bytes read from the default message.
+ */
+ size_t get_bytes_read() const override;
+
+ /**
+ * @return the number of bytes read from the specified message.
+ */
+ size_t get_bytes_read(message_id msg) const;
+
+ bool check_available(size_t n) override;
+ bool check_available_msg(size_t n, message_id msg);
+
+ /**
+ * @return currently set default message
+ */
+ size_t default_msg() const { return m_default_read; }
+
+ /**
+ * Set the default message
+ * @param msg the number identifying the message which is going to
+ * be the new default message
+ */
+ void set_default_msg(message_id msg);
+
+ /**
+ * Get the number of messages the are in this pipe.
+ * @return number of messages the are in this pipe
+ */
+ message_id message_count() const;
+
+ /**
+ * Test whether this pipe has any data that can be read from.
+ * @return true if there is more data to read, false otherwise
+ */
+ bool end_of_data() const override;
+
+ /**
+ * Start a new message in the pipe. A potential other message in this pipe
+ * must be closed with end_msg() before this function may be called.
+ */
+ void start_msg();
+
+ /**
+ * End the current message.
+ */
+ void end_msg();
+
+ /**
+ * Insert a new filter at the front of the pipe
+ * Deprecated because runtime modification of Pipes is deprecated.
+ * You can instead use prepend_filter which only works before the first
+ * message is processed.
+ * @param filt the new filter to insert
+ */
+ BOTAN_DEPRECATED("Runtime modification of Pipe deprecated")
+ void prepend(Filter* filt);
+
+ /**
+ * Insert a new filter at the back of the pipe
+ * Deprecated because runtime modification of Pipes is deprecated.
+ * You can instead use append_filter which only works before the first
+ * message is processed.
+ * @param filt the new filter to insert
+ */
+ BOTAN_DEPRECATED("Runtime modification of Pipe deprecated")
+ void append(Filter* filt);
+
+ /**
+ * Remove the first filter at the front of the pipe.
+ */
+ BOTAN_DEPRECATED("Runtime modification of Pipe deprecated")
+ void pop();
+
+ /**
+ * Reset this pipe to an empty pipe.
+ */
+ BOTAN_DEPRECATED("Runtime modification of Pipe deprecated")
+ void reset();
+
+ /**
+ * Append a new filter onto the filter sequence. This may only be
+ * called immediately after initial construction, before _any_
+ * calls to start_msg have been made.
+ *
+ * This function (unlike append) is not deprecated, as it allows
+ * only modification of the pipe at initialization (before use)
+ * rather than after messages have been processed.
+ */
+ void append_filter(Filter* filt);
+
+ /**
+ * Prepend a new filter onto the filter sequence. This may only be
+ * called immediately after initial construction, before _any_
+ * calls to start_msg have been made.
+ *
+ * This function (unlike prepend) is not deprecated, as it allows
+ * only modification of the pipe at initialization (before use)
+ * rather than after messages have been processed.
+ */
+ void prepend_filter(Filter* filt);
+
+ /**
+ * 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);
+
+ /**
+ * Construct a Pipe from a list of filters
+ * @param filters the set of filters to use
+ */
+ explicit Pipe(std::initializer_list<Filter*> filters);
+
+ Pipe(const Pipe&) = delete;
+ Pipe& operator=(const Pipe&) = delete;
+
+ ~Pipe();
+ private:
+ void destruct(Filter*);
+ void do_append(Filter* filt);
+ void do_prepend(Filter* filt);
+ void find_endpoints(Filter*);
+ void clear_endpoints(Filter*);
+
+ message_id get_message_no(const std::string&, message_id) const;
+
+ Filter* m_pipe;
+ std::unique_ptr<Output_Buffers> m_outputs;
+ message_id m_default_read;
+ bool m_inside_msg;
+ };
+
+/**
+* Stream output operator; dumps the results from pipe's default
+* message to the output stream.
+* @param out an output stream
+* @param pipe the pipe
+*/
+BOTAN_PUBLIC_API(2,0) std::ostream& operator<<(std::ostream& out, Pipe& pipe);
+
+/**
+* Stream input operator; dumps the remaining bytes of input
+* to the (assumed open) pipe message.
+* @param in the input stream
+* @param pipe the pipe
+*/
+BOTAN_PUBLIC_API(2,0) std::istream& operator>>(std::istream& in, Pipe& pipe);
+
+}
+
+#if defined(BOTAN_HAS_PIPE_UNIXFD_IO)
+ #include <botan/fd_unix.h>
+#endif
+
+#endif
diff --git a/comm/third_party/botan/src/lib/filters/pipe_io.cpp b/comm/third_party/botan/src/lib/filters/pipe_io.cpp
new file mode 100644
index 0000000000..a909cba725
--- /dev/null
+++ b/comm/third_party/botan/src/lib/filters/pipe_io.cpp
@@ -0,0 +1,47 @@
+/*
+* Pipe I/O
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/pipe.h>
+#include <istream>
+#include <ostream>
+
+namespace Botan {
+
+/*
+* Write data from a pipe into an ostream
+*/
+std::ostream& operator<<(std::ostream& stream, Pipe& pipe)
+ {
+ secure_vector<uint8_t> buffer(BOTAN_DEFAULT_BUFFER_SIZE);
+ while(stream.good() && pipe.remaining())
+ {
+ const size_t got = pipe.read(buffer.data(), buffer.size());
+ stream.write(cast_uint8_ptr_to_char(buffer.data()), got);
+ }
+ if(!stream.good())
+ throw Stream_IO_Error("Pipe output operator (iostream) has failed");
+ return stream;
+ }
+
+/*
+* Read data from an istream into a pipe
+*/
+std::istream& operator>>(std::istream& stream, Pipe& pipe)
+ {
+ secure_vector<uint8_t> buffer(BOTAN_DEFAULT_BUFFER_SIZE);
+ while(stream.good())
+ {
+ stream.read(cast_uint8_ptr_to_char(buffer.data()), buffer.size());
+ const size_t got = static_cast<size_t>(stream.gcount());
+ pipe.write(buffer.data(), got);
+ }
+ if(stream.bad() || (stream.fail() && !stream.eof()))
+ throw Stream_IO_Error("Pipe input operator (iostream) has failed");
+ return stream;
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/filters/pipe_rw.cpp b/comm/third_party/botan/src/lib/filters/pipe_rw.cpp
new file mode 100644
index 0000000000..dc7b973727
--- /dev/null
+++ b/comm/third_party/botan/src/lib/filters/pipe_rw.cpp
@@ -0,0 +1,181 @@
+/*
+* Pipe Reading/Writing
+* (C) 1999-2007 Jack Lloyd
+* 2012 Markus Wanner
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/pipe.h>
+#include <botan/filter.h>
+#include <botan/internal/out_buf.h>
+
+namespace Botan {
+
+/*
+* Look up the canonical ID for a queue
+*/
+Pipe::message_id Pipe::get_message_no(const std::string& func_name,
+ message_id msg) const
+ {
+ if(msg == DEFAULT_MESSAGE)
+ msg = default_msg();
+ else if(msg == LAST_MESSAGE)
+ msg = message_count() - 1;
+
+ if(msg >= message_count())
+ throw Invalid_Message_Number(func_name, msg);
+
+ return msg;
+ }
+
+/*
+* Write into a Pipe
+*/
+void Pipe::write(const uint8_t input[], size_t length)
+ {
+ if(!m_inside_msg)
+ throw Invalid_State("Cannot write to a Pipe while it is not processing");
+ m_pipe->write(input, length);
+ }
+
+/*
+* Write a string into a Pipe
+*/
+void Pipe::write(const std::string& str)
+ {
+ write(cast_char_ptr_to_uint8(str.data()), str.size());
+ }
+
+/*
+* Write a single byte into a Pipe
+*/
+void Pipe::write(uint8_t input)
+ {
+ write(&input, 1);
+ }
+
+/*
+* Write the contents of a DataSource into a Pipe
+*/
+void Pipe::write(DataSource& source)
+ {
+ secure_vector<uint8_t> buffer(BOTAN_DEFAULT_BUFFER_SIZE);
+ while(!source.end_of_data())
+ {
+ size_t got = source.read(buffer.data(), buffer.size());
+ write(buffer.data(), got);
+ }
+ }
+
+/*
+* Read some data from the pipe
+*/
+size_t Pipe::read(uint8_t output[], size_t length, message_id msg)
+ {
+ return m_outputs->read(output, length, get_message_no("read", msg));
+ }
+
+/*
+* Read some data from the pipe
+*/
+size_t Pipe::read(uint8_t output[], size_t length)
+ {
+ return read(output, length, DEFAULT_MESSAGE);
+ }
+
+/*
+* Read a single byte from the pipe
+*/
+size_t Pipe::read(uint8_t& out, message_id msg)
+ {
+ return read(&out, 1, msg);
+ }
+
+/*
+* Return all data in the pipe
+*/
+secure_vector<uint8_t> Pipe::read_all(message_id msg)
+ {
+ msg = ((msg != DEFAULT_MESSAGE) ? msg : default_msg());
+ secure_vector<uint8_t> buffer(remaining(msg));
+ size_t got = read(buffer.data(), buffer.size(), msg);
+ buffer.resize(got);
+ return buffer;
+ }
+
+/*
+* Return all data in the pipe as a string
+*/
+std::string Pipe::read_all_as_string(message_id msg)
+ {
+ msg = ((msg != DEFAULT_MESSAGE) ? msg : default_msg());
+ secure_vector<uint8_t> buffer(BOTAN_DEFAULT_BUFFER_SIZE);
+ std::string str;
+ str.reserve(remaining(msg));
+
+ while(true)
+ {
+ size_t got = read(buffer.data(), buffer.size(), msg);
+ if(got == 0)
+ break;
+ str.append(cast_uint8_ptr_to_char(buffer.data()), got);
+ }
+
+ return str;
+ }
+
+/*
+* Find out how many bytes are ready to read
+*/
+size_t Pipe::remaining(message_id msg) const
+ {
+ return m_outputs->remaining(get_message_no("remaining", msg));
+ }
+
+/*
+* Peek at some data in the pipe
+*/
+size_t Pipe::peek(uint8_t output[], size_t length,
+ size_t offset, message_id msg) const
+ {
+ return m_outputs->peek(output, length, offset, get_message_no("peek", msg));
+ }
+
+/*
+* Peek at some data in the pipe
+*/
+size_t Pipe::peek(uint8_t output[], size_t length, size_t offset) const
+ {
+ return peek(output, length, offset, DEFAULT_MESSAGE);
+ }
+
+/*
+* Peek at a byte in the pipe
+*/
+size_t Pipe::peek(uint8_t& out, size_t offset, message_id msg) const
+ {
+ return peek(&out, 1, offset, msg);
+ }
+
+size_t Pipe::get_bytes_read() const
+ {
+ return m_outputs->get_bytes_read(default_msg());
+ }
+
+size_t Pipe::get_bytes_read(message_id msg) const
+ {
+ return m_outputs->get_bytes_read(msg);
+ }
+
+bool Pipe::check_available(size_t n)
+ {
+ return (n <= remaining(default_msg()));
+ }
+
+bool Pipe::check_available_msg(size_t n, message_id msg)
+ {
+ return (n <= remaining(msg));
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/filters/secqueue.cpp b/comm/third_party/botan/src/lib/filters/secqueue.cpp
new file mode 100644
index 0000000000..1c8d281493
--- /dev/null
+++ b/comm/third_party/botan/src/lib/filters/secqueue.cpp
@@ -0,0 +1,232 @@
+/*
+* SecureQueue
+* (C) 1999-2007 Jack Lloyd
+* 2012 Markus Wanner
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/secqueue.h>
+#include <algorithm>
+
+namespace Botan {
+
+/**
+* A node in a SecureQueue
+*/
+class SecureQueueNode final
+ {
+ public:
+ SecureQueueNode() : m_buffer(BOTAN_DEFAULT_BUFFER_SIZE)
+ { m_next = nullptr; m_start = m_end = 0; }
+
+ ~SecureQueueNode() { m_next = nullptr; m_start = m_end = 0; }
+
+ size_t write(const uint8_t input[], size_t length)
+ {
+ size_t copied = std::min<size_t>(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);
+ copy_mem(output, m_buffer.data() + m_start, copied);
+ m_start += copied;
+ return copied;
+ }
+
+ size_t peek(uint8_t output[], size_t length, size_t offset = 0)
+ {
+ const size_t left = m_end - m_start;
+ if(offset >= left) return 0;
+ size_t copied = std::min(length, left - offset);
+ copy_mem(output, m_buffer.data() + m_start + offset, copied);
+ return copied;
+ }
+
+ size_t size() const { return (m_end - m_start); }
+ private:
+ friend class SecureQueue;
+ SecureQueueNode* m_next;
+ secure_vector<uint8_t> m_buffer;
+ size_t m_start, m_end;
+ };
+
+/*
+* Create a SecureQueue
+*/
+SecureQueue::SecureQueue()
+ {
+ m_bytes_read = 0;
+ set_next(nullptr, 0);
+ m_head = m_tail = new SecureQueueNode;
+ }
+
+/*
+* Copy a SecureQueue
+*/
+SecureQueue::SecureQueue(const SecureQueue& input) :
+ Fanout_Filter(), DataSource()
+ {
+ m_bytes_read = 0;
+ set_next(nullptr, 0);
+
+ m_head = m_tail = new SecureQueueNode;
+ SecureQueueNode* temp = input.m_head;
+ while(temp)
+ {
+ write(&temp->m_buffer[temp->m_start], temp->m_end - temp->m_start);
+ temp = temp->m_next;
+ }
+ }
+
+/*
+* Destroy this SecureQueue
+*/
+void SecureQueue::destroy()
+ {
+ SecureQueueNode* temp = m_head;
+ while(temp)
+ {
+ SecureQueueNode* holder = temp->m_next;
+ delete temp;
+ temp = holder;
+ }
+ m_head = m_tail = nullptr;
+ }
+
+/*
+* Copy a SecureQueue
+*/
+SecureQueue& SecureQueue::operator=(const SecureQueue& input)
+ {
+ if(this == &input)
+ return *this;
+
+ destroy();
+ m_bytes_read = input.get_bytes_read();
+ m_head = m_tail = new SecureQueueNode;
+ SecureQueueNode* temp = input.m_head;
+ while(temp)
+ {
+ write(&temp->m_buffer[temp->m_start], temp->m_end - temp->m_start);
+ temp = temp->m_next;
+ }
+ return (*this);
+ }
+
+/*
+* 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;
+ while(length)
+ {
+ const size_t n = m_tail->write(input, length);
+ input += n;
+ length -= n;
+ if(length)
+ {
+ m_tail->m_next = new SecureQueueNode;
+ m_tail = m_tail->m_next;
+ }
+ }
+ }
+
+/*
+* Read some bytes from the queue
+*/
+size_t SecureQueue::read(uint8_t output[], size_t length)
+ {
+ size_t got = 0;
+ while(length && m_head)
+ {
+ 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;
+ m_head = holder;
+ }
+ }
+ m_bytes_read += got;
+ return got;
+ }
+
+/*
+* Read data, but do not remove it from queue
+*/
+size_t SecureQueue::peek(uint8_t output[], size_t length, size_t offset) const
+ {
+ SecureQueueNode* current = m_head;
+
+ while(offset && current)
+ {
+ if(offset >= current->size())
+ {
+ offset -= current->size();
+ current = current->m_next;
+ }
+ else
+ break;
+ }
+
+ size_t got = 0;
+ while(length && current)
+ {
+ const size_t n = current->peek(output, length, offset);
+ offset = 0;
+ output += n;
+ got += n;
+ length -= n;
+ current = current->m_next;
+ }
+ return got;
+ }
+
+/**
+* Return how many bytes have been read so far.
+*/
+size_t SecureQueue::get_bytes_read() const
+ {
+ return m_bytes_read;
+ }
+
+/*
+* Return how many bytes the queue holds
+*/
+size_t SecureQueue::size() const
+ {
+ SecureQueueNode* current = m_head;
+ size_t count = 0;
+
+ while(current)
+ {
+ count += current->size();
+ current = current->m_next;
+ }
+ return count;
+ }
+
+/*
+* Test if the queue has any data in it
+*/
+bool SecureQueue::end_of_data() const
+ {
+ return (size() == 0);
+ }
+
+bool SecureQueue::empty() const
+ {
+ return (size() == 0);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/filters/secqueue.h b/comm/third_party/botan/src/lib/filters/secqueue.h
new file mode 100644
index 0000000000..42d2f09e2d
--- /dev/null
+++ b/comm/third_party/botan/src/lib/filters/secqueue.h
@@ -0,0 +1,74 @@
+/*
+* SecureQueue
+* (C) 1999-2007 Jack Lloyd
+* 2012 Markus Wanner
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_SECURE_QUEUE_H_
+#define BOTAN_SECURE_QUEUE_H_
+
+#include <botan/data_src.h>
+#include <botan/filter.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(secqueue.h)
+
+namespace Botan {
+
+/**
+* A queue that knows how to zeroize itself
+*/
+class BOTAN_PUBLIC_API(2,0) SecureQueue final : public Fanout_Filter, public DataSource
+ {
+ public:
+ std::string name() const override { return "Queue"; }
+
+ void write(const uint8_t[], size_t) override;
+
+ size_t read(uint8_t[], size_t) override;
+ size_t peek(uint8_t[], size_t, size_t = 0) const override;
+ size_t get_bytes_read() const override;
+
+ bool end_of_data() const override;
+
+ bool empty() const;
+
+ bool check_available(size_t n) override { return n <= size(); }
+
+ /**
+ * @return number of bytes available in the queue
+ */
+ size_t size() const;
+
+ bool attachable() override { return false; }
+
+ /**
+ * SecureQueue assignment
+ * @param other the queue to copy
+ */
+ SecureQueue& operator=(const SecureQueue& other);
+
+ /**
+ * SecureQueue default constructor (creates empty queue)
+ */
+ SecureQueue();
+
+ /**
+ * SecureQueue copy constructor
+ * @param other the queue to copy
+ */
+ SecureQueue(const SecureQueue& other);
+
+ ~SecureQueue() { destroy(); }
+
+ private:
+ void destroy();
+ size_t m_bytes_read;
+ class SecureQueueNode* m_head;
+ class SecureQueueNode* m_tail;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/filters/threaded_fork.cpp b/comm/third_party/botan/src/lib/filters/threaded_fork.cpp
new file mode 100644
index 0000000000..2d77f9fd1c
--- /dev/null
+++ b/comm/third_party/botan/src/lib/filters/threaded_fork.cpp
@@ -0,0 +1,153 @@
+/*
+* Threaded Fork
+* (C) 2013 Joel Low
+* 2013 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/filters.h>
+
+#if defined(BOTAN_HAS_THREAD_UTILS)
+
+#include <botan/internal/semaphore.h>
+#include <botan/internal/barrier.h>
+#include <functional>
+
+namespace Botan {
+
+struct Threaded_Fork_Data
+ {
+ /*
+ * Semaphore for indicating that there is work to be done (or to
+ * quit)
+ */
+ Semaphore m_input_ready_semaphore;
+
+ /*
+ * Synchronises all threads to complete processing data in lock-step.
+ */
+ Barrier m_input_complete_barrier;
+
+ /*
+ * The work that needs to be done. This should be only when the threads
+ * are NOT running (i.e. before notifying the work condition, after
+ * the input_complete_barrier has reset.)
+ */
+ const uint8_t* m_input = nullptr;
+
+ /*
+ * The length of the work that needs to be done.
+ */
+ size_t m_input_length = 0;
+ };
+
+/*
+* Threaded_Fork constructor
+*/
+Threaded_Fork::Threaded_Fork(Filter* f1, Filter* f2, Filter* f3, Filter* f4) :
+ Fork(nullptr, static_cast<size_t>(0)),
+ m_thread_data(new Threaded_Fork_Data)
+ {
+ Filter* filters[4] = { f1, f2, f3, f4 };
+ set_next(filters, 4);
+ }
+
+/*
+* Threaded_Fork constructor
+*/
+Threaded_Fork::Threaded_Fork(Filter* filters[], size_t count) :
+ Fork(nullptr, static_cast<size_t>(0)),
+ m_thread_data(new Threaded_Fork_Data)
+ {
+ set_next(filters, count);
+ }
+
+Threaded_Fork::~Threaded_Fork()
+ {
+ m_thread_data->m_input = nullptr;
+ m_thread_data->m_input_length = 0;
+
+ m_thread_data->m_input_ready_semaphore.release(m_threads.size());
+
+ for(auto& thread : m_threads)
+ thread->join();
+ }
+
+std::string Threaded_Fork::name() const
+ {
+ return "Threaded Fork";
+ }
+
+void Threaded_Fork::set_next(Filter* f[], size_t n)
+ {
+ Fork::set_next(f, n);
+ n = m_next.size();
+
+ if(n < m_threads.size())
+ m_threads.resize(n);
+ else
+ {
+ m_threads.reserve(n);
+ for(size_t i = m_threads.size(); i != n; ++i)
+ {
+ m_threads.push_back(
+ std::shared_ptr<std::thread>(
+ new std::thread(
+ std::bind(&Threaded_Fork::thread_entry, this, m_next[i]))));
+ }
+ }
+ }
+
+void Threaded_Fork::send(const uint8_t input[], size_t length)
+ {
+ if(m_write_queue.size())
+ thread_delegate_work(m_write_queue.data(), m_write_queue.size());
+ thread_delegate_work(input, length);
+
+ bool nothing_attached = true;
+ for(size_t j = 0; j != total_ports(); ++j)
+ if(m_next[j])
+ nothing_attached = false;
+
+ if(nothing_attached)
+ m_write_queue += std::make_pair(input, length);
+ else
+ m_write_queue.clear();
+ }
+
+void Threaded_Fork::thread_delegate_work(const uint8_t input[], size_t length)
+ {
+ //Set the data to do.
+ m_thread_data->m_input = input;
+ m_thread_data->m_input_length = length;
+
+ //Let the workers start processing.
+ m_thread_data->m_input_complete_barrier.wait(total_ports() + 1);
+ m_thread_data->m_input_ready_semaphore.release(total_ports());
+
+ //Wait for all the filters to finish processing.
+ m_thread_data->m_input_complete_barrier.sync();
+
+ //Reset the thread data
+ m_thread_data->m_input = nullptr;
+ m_thread_data->m_input_length = 0;
+ }
+
+void Threaded_Fork::thread_entry(Filter* filter)
+ {
+ while(true)
+ {
+ m_thread_data->m_input_ready_semaphore.acquire();
+
+ if(!m_thread_data->m_input)
+ break;
+
+ filter->write(m_thread_data->m_input, m_thread_data->m_input_length);
+ m_thread_data->m_input_complete_barrier.sync();
+ }
+ }
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/hash/blake2/blake2b.cpp b/comm/third_party/botan/src/lib/hash/blake2/blake2b.cpp
new file mode 100644
index 0000000000..0280d0c8b3
--- /dev/null
+++ b/comm/third_party/botan/src/lib/hash/blake2/blake2b.cpp
@@ -0,0 +1,207 @@
+/*
+* BLAKE2b
+* (C) 2016 cynecx
+* (C) 2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/blake2b.h>
+#include <botan/exceptn.h>
+#include <botan/mem_ops.h>
+#include <botan/loadstor.h>
+#include <botan/rotate.h>
+#include <algorithm>
+
+namespace Botan {
+
+namespace {
+
+enum blake2b_constant {
+ BLAKE2B_BLOCKBYTES = 128,
+ BLAKE2B_IVU64COUNT = 8
+};
+
+const uint64_t blake2b_IV[BLAKE2B_IVU64COUNT] = {
+ 0x6a09e667f3bcc908, 0xbb67ae8584caa73b,
+ 0x3c6ef372fe94f82b, 0xa54ff53a5f1d36f1,
+ 0x510e527fade682d1, 0x9b05688c2b3e6c1f,
+ 0x1f83d9abfb41bd6b, 0x5be0cd19137e2179
+};
+
+}
+
+BLAKE2b::BLAKE2b(size_t output_bits) :
+ m_output_bits(output_bits),
+ m_buffer(BLAKE2B_BLOCKBYTES),
+ m_bufpos(0),
+ m_H(BLAKE2B_IVU64COUNT)
+ {
+ if(output_bits == 0 || output_bits > 512 || output_bits % 8 != 0)
+ {
+ throw Invalid_Argument("Bad output bits size for BLAKE2b");
+ }
+
+ state_init();
+ }
+
+void BLAKE2b::state_init()
+ {
+ copy_mem(m_H.data(), blake2b_IV, BLAKE2B_IVU64COUNT);
+ m_H[0] ^= 0x01010000 ^ static_cast<uint8_t>(output_length());
+ m_T[0] = m_T[1] = 0;
+ m_F[0] = m_F[1] = 0;
+ m_bufpos = 0;
+ }
+
+namespace {
+
+BOTAN_FORCE_INLINE void G(uint64_t& a, uint64_t& b, uint64_t& c, uint64_t& d,
+ uint64_t M0, uint64_t M1)
+ {
+ a = a + b + M0;
+ d = rotr<32>(d ^ a);
+ c = c + d;
+ b = rotr<24>(b ^ c);
+ a = a + b + M1;
+ d = rotr<16>(d ^ a);
+ c = c + d;
+ b = rotr<63>(b ^ c);
+ }
+
+template<size_t i0, size_t i1, size_t i2, size_t i3, size_t i4, size_t i5, size_t i6, size_t i7,
+ size_t i8, size_t i9, size_t iA, size_t iB, size_t iC, size_t iD, size_t iE, size_t iF>
+BOTAN_FORCE_INLINE void ROUND(uint64_t* v, const uint64_t* M)
+ {
+ G(v[ 0], v[ 4], v[ 8], v[12], M[i0], M[i1]);
+ G(v[ 1], v[ 5], v[ 9], v[13], M[i2], M[i3]);
+ G(v[ 2], v[ 6], v[10], v[14], M[i4], M[i5]);
+ G(v[ 3], v[ 7], v[11], v[15], M[i6], M[i7]);
+ G(v[ 0], v[ 5], v[10], v[15], M[i8], M[i9]);
+ G(v[ 1], v[ 6], v[11], v[12], M[iA], M[iB]);
+ G(v[ 2], v[ 7], v[ 8], v[13], M[iC], M[iD]);
+ G(v[ 3], v[ 4], v[ 9], v[14], M[iE], M[iF]);
+ }
+
+
+}
+
+void BLAKE2b::compress(const uint8_t* input, size_t blocks, uint64_t increment)
+ {
+ for(size_t b = 0; b != blocks; ++b)
+ {
+ m_T[0] += increment;
+ if(m_T[0] < increment)
+ {
+ m_T[1]++;
+ }
+
+ uint64_t M[16];
+ uint64_t v[16];
+ load_le(M, input, 16);
+
+ input += BLAKE2B_BLOCKBYTES;
+
+ for(size_t i = 0; i < 8; i++)
+ v[i] = m_H[i];
+ for(size_t i = 0; i != 8; ++i)
+ v[i + 8] = blake2b_IV[i];
+
+ v[12] ^= m_T[0];
+ v[13] ^= m_T[1];
+ v[14] ^= m_F[0];
+ v[15] ^= m_F[1];
+
+ ROUND< 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15>(v, M);
+ ROUND<14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3>(v, M);
+ ROUND<11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4>(v, M);
+ ROUND< 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8>(v, M);
+ ROUND< 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13>(v, M);
+ ROUND< 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9>(v, M);
+ ROUND<12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11>(v, M);
+ ROUND<13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10>(v, M);
+ ROUND< 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5>(v, M);
+ ROUND<10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0>(v, M);
+ ROUND< 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15>(v, M);
+ ROUND<14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3>(v, M);
+
+ for(size_t i = 0; i < 8; i++)
+ {
+ m_H[i] ^= v[i] ^ v[i + 8];
+ }
+ }
+ }
+
+void BLAKE2b::add_data(const uint8_t input[], size_t length)
+ {
+ if(length == 0)
+ return;
+
+ if(m_bufpos > 0)
+ {
+ if(m_bufpos < BLAKE2B_BLOCKBYTES)
+ {
+ const size_t take = std::min(BLAKE2B_BLOCKBYTES - m_bufpos, length);
+ copy_mem(&m_buffer[m_bufpos], input, take);
+ m_bufpos += take;
+ length -= take;
+ input += take;
+ }
+
+ if(m_bufpos == m_buffer.size() && length > 0)
+ {
+ compress(m_buffer.data(), 1, BLAKE2B_BLOCKBYTES);
+ m_bufpos = 0;
+ }
+ }
+
+ if(length > BLAKE2B_BLOCKBYTES)
+ {
+ const size_t full_blocks = ((length-1) / BLAKE2B_BLOCKBYTES);
+ compress(input, full_blocks, BLAKE2B_BLOCKBYTES);
+
+ input += full_blocks * BLAKE2B_BLOCKBYTES;
+ length -= full_blocks * BLAKE2B_BLOCKBYTES;
+ }
+
+ if(length > 0)
+ {
+ copy_mem(&m_buffer[m_bufpos], input, length);
+ m_bufpos += length;
+ }
+ }
+
+void BLAKE2b::final_result(uint8_t output[])
+ {
+ if(m_bufpos != BLAKE2B_BLOCKBYTES)
+ clear_mem(&m_buffer[m_bufpos], BLAKE2B_BLOCKBYTES - m_bufpos);
+ m_F[0] = 0xFFFFFFFFFFFFFFFF;
+ compress(m_buffer.data(), 1, m_bufpos);
+ copy_out_vec_le(output, output_length(), m_H);
+ state_init();
+ }
+
+std::string BLAKE2b::name() const
+ {
+ return "BLAKE2b(" + std::to_string(m_output_bits) + ")";
+ }
+
+HashFunction* BLAKE2b::clone() const
+ {
+ return new BLAKE2b(m_output_bits);
+ }
+
+std::unique_ptr<HashFunction> BLAKE2b::copy_state() const
+ {
+ return std::unique_ptr<HashFunction>(new BLAKE2b(*this));
+ }
+
+void BLAKE2b::clear()
+ {
+ zeroise(m_H);
+ zeroise(m_buffer);
+ m_bufpos = 0;
+ state_init();
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/hash/blake2/blake2b.h b/comm/third_party/botan/src/lib/hash/blake2/blake2b.h
new file mode 100644
index 0000000000..9b0b655f25
--- /dev/null
+++ b/comm/third_party/botan/src/lib/hash/blake2/blake2b.h
@@ -0,0 +1,60 @@
+/*
+* BLAKE2b
+* (C) 2016 cynecx
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_BLAKE2B_H_
+#define BOTAN_BLAKE2B_H_
+
+#include <botan/hash.h>
+#include <string>
+#include <memory>
+
+BOTAN_FUTURE_INTERNAL_HEADER(blake2b.h)
+
+namespace Botan {
+
+/**
+* BLAKE2B
+*/
+class BOTAN_PUBLIC_API(2,0) BLAKE2b final : public HashFunction
+ {
+ public:
+ /**
+ * @param output_bits the output size of BLAKE2b in bits
+ */
+ explicit BLAKE2b(size_t output_bits = 512);
+
+ size_t hash_block_size() const override { return 128; }
+ size_t output_length() const override { return m_output_bits / 8; }
+
+ HashFunction* clone() const override;
+ std::string name() const override;
+ void clear() override;
+
+ std::unique_ptr<HashFunction> copy_state() const override;
+
+ private:
+ void add_data(const uint8_t input[], size_t length) override;
+ void final_result(uint8_t out[]) override;
+
+ void state_init();
+ void compress(const uint8_t* data, size_t blocks, uint64_t increment);
+
+ const size_t m_output_bits;
+
+ secure_vector<uint8_t> m_buffer;
+ size_t m_bufpos;
+
+ secure_vector<uint64_t> m_H;
+ uint64_t m_T[2];
+ uint64_t m_F[2];
+ };
+
+typedef BLAKE2b Blake2b;
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/hash/blake2/info.txt b/comm/third_party/botan/src/lib/hash/blake2/info.txt
new file mode 100644
index 0000000000..b99be46015
--- /dev/null
+++ b/comm/third_party/botan/src/lib/hash/blake2/info.txt
@@ -0,0 +1,3 @@
+<defines>
+BLAKE2B -> 20130131
+</defines>
diff --git a/comm/third_party/botan/src/lib/hash/checksum/adler32/adler32.cpp b/comm/third_party/botan/src/lib/hash/checksum/adler32/adler32.cpp
new file mode 100644
index 0000000000..329ba99a88
--- /dev/null
+++ b/comm/third_party/botan/src/lib/hash/checksum/adler32/adler32.cpp
@@ -0,0 +1,86 @@
+/*
+* Adler32
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/adler32.h>
+#include <botan/loadstor.h>
+
+namespace Botan {
+
+namespace {
+
+void adler32_update(const uint8_t input[], size_t length,
+ uint16_t& S1, uint16_t& S2)
+ {
+ uint32_t S1x = S1;
+ uint32_t S2x = S2;
+
+ while(length >= 16)
+ {
+ S1x += input[ 0]; S2x += S1x;
+ S1x += input[ 1]; S2x += S1x;
+ S1x += input[ 2]; S2x += S1x;
+ S1x += input[ 3]; S2x += S1x;
+ S1x += input[ 4]; S2x += S1x;
+ S1x += input[ 5]; S2x += S1x;
+ S1x += input[ 6]; S2x += S1x;
+ S1x += input[ 7]; S2x += S1x;
+ S1x += input[ 8]; S2x += S1x;
+ S1x += input[ 9]; S2x += S1x;
+ S1x += input[10]; S2x += S1x;
+ S1x += input[11]; S2x += S1x;
+ S1x += input[12]; S2x += S1x;
+ S1x += input[13]; S2x += S1x;
+ S1x += input[14]; S2x += S1x;
+ S1x += input[15]; S2x += S1x;
+ input += 16;
+ length -= 16;
+ }
+
+ for(size_t j = 0; j != length; ++j)
+ {
+ S1x += input[j];
+ S2x += S1x;
+ }
+
+ S1 = S1x % 65521;
+ S2 = S2x % 65521;
+ }
+
+}
+
+/*
+* Update an Adler32 Checksum
+*/
+void Adler32::add_data(const uint8_t input[], size_t length)
+ {
+ const size_t PROCESS_AMOUNT = 5552;
+
+ while(length >= PROCESS_AMOUNT)
+ {
+ adler32_update(input, PROCESS_AMOUNT, m_S1, m_S2);
+ input += PROCESS_AMOUNT;
+ length -= PROCESS_AMOUNT;
+ }
+
+ adler32_update(input, length, m_S1, m_S2);
+ }
+
+/*
+* Finalize an Adler32 Checksum
+*/
+void Adler32::final_result(uint8_t output[])
+ {
+ store_be(output, m_S2, m_S1);
+ clear();
+ }
+
+std::unique_ptr<HashFunction> Adler32::copy_state() const
+ {
+ return std::unique_ptr<HashFunction>(new Adler32(*this));
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/hash/checksum/adler32/adler32.h b/comm/third_party/botan/src/lib/hash/checksum/adler32/adler32.h
new file mode 100644
index 0000000000..cd84a7597e
--- /dev/null
+++ b/comm/third_party/botan/src/lib/hash/checksum/adler32/adler32.h
@@ -0,0 +1,40 @@
+/*
+* Adler32
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_ADLER32_H_
+#define BOTAN_ADLER32_H_
+
+#include <botan/hash.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(adler32.h)
+
+namespace Botan {
+
+/**
+* The Adler32 checksum, used in zlib
+*/
+class BOTAN_PUBLIC_API(2,0) Adler32 final : public HashFunction
+ {
+ public:
+ std::string name() const override { return "Adler32"; }
+ size_t output_length() const override { return 4; }
+ HashFunction* clone() const override { return new Adler32; }
+ std::unique_ptr<HashFunction> copy_state() const override;
+
+ void clear() override { m_S1 = 1; m_S2 = 0; }
+
+ Adler32() { clear(); }
+ ~Adler32() { clear(); }
+ private:
+ void add_data(const uint8_t[], size_t) override;
+ void final_result(uint8_t[]) override;
+ uint16_t m_S1, m_S2;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/hash/checksum/adler32/info.txt b/comm/third_party/botan/src/lib/hash/checksum/adler32/info.txt
new file mode 100644
index 0000000000..4ddcb2e08b
--- /dev/null
+++ b/comm/third_party/botan/src/lib/hash/checksum/adler32/info.txt
@@ -0,0 +1,3 @@
+<defines>
+ADLER32 -> 20131128
+</defines>
diff --git a/comm/third_party/botan/src/lib/hash/checksum/crc24/crc24.cpp b/comm/third_party/botan/src/lib/hash/checksum/crc24/crc24.cpp
new file mode 100644
index 0000000000..cfec0471c2
--- /dev/null
+++ b/comm/third_party/botan/src/lib/hash/checksum/crc24/crc24.cpp
@@ -0,0 +1,252 @@
+/*
+* CRC24
+* (C) 1999-2007 Jack Lloyd
+* (C) 2017 [Ribose Inc](https://www.ribose.com). Performed by Krzysztof Kwiatkowski.
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/crc24.h>
+#include <botan/loadstor.h>
+#include <botan/bswap.h>
+
+namespace Botan {
+
+namespace {
+
+alignas(64) const uint32_t CRC24_T0[256] = {
+ 0x00000000, 0x00FB4C86, 0x000DD58A, 0x00F6990C, 0x00E1E693, 0x001AAA15, 0x00EC3319,
+ 0x00177F9F, 0x003981A1, 0x00C2CD27, 0x0034542B, 0x00CF18AD, 0x00D86732, 0x00232BB4,
+ 0x00D5B2B8, 0x002EFE3E, 0x00894EC5, 0x00720243, 0x00849B4F, 0x007FD7C9, 0x0068A856,
+ 0x0093E4D0, 0x00657DDC, 0x009E315A, 0x00B0CF64, 0x004B83E2, 0x00BD1AEE, 0x00465668,
+ 0x005129F7, 0x00AA6571, 0x005CFC7D, 0x00A7B0FB, 0x00E9D10C, 0x00129D8A, 0x00E40486,
+ 0x001F4800, 0x0008379F, 0x00F37B19, 0x0005E215, 0x00FEAE93, 0x00D050AD, 0x002B1C2B,
+ 0x00DD8527, 0x0026C9A1, 0x0031B63E, 0x00CAFAB8, 0x003C63B4, 0x00C72F32, 0x00609FC9,
+ 0x009BD34F, 0x006D4A43, 0x009606C5, 0x0081795A, 0x007A35DC, 0x008CACD0, 0x0077E056,
+ 0x00591E68, 0x00A252EE, 0x0054CBE2, 0x00AF8764, 0x00B8F8FB, 0x0043B47D, 0x00B52D71,
+ 0x004E61F7, 0x00D2A319, 0x0029EF9F, 0x00DF7693, 0x00243A15, 0x0033458A, 0x00C8090C,
+ 0x003E9000, 0x00C5DC86, 0x00EB22B8, 0x00106E3E, 0x00E6F732, 0x001DBBB4, 0x000AC42B,
+ 0x00F188AD, 0x000711A1, 0x00FC5D27, 0x005BEDDC, 0x00A0A15A, 0x00563856, 0x00AD74D0,
+ 0x00BA0B4F, 0x004147C9, 0x00B7DEC5, 0x004C9243, 0x00626C7D, 0x009920FB, 0x006FB9F7,
+ 0x0094F571, 0x00838AEE, 0x0078C668, 0x008E5F64, 0x007513E2, 0x003B7215, 0x00C03E93,
+ 0x0036A79F, 0x00CDEB19, 0x00DA9486, 0x0021D800, 0x00D7410C, 0x002C0D8A, 0x0002F3B4,
+ 0x00F9BF32, 0x000F263E, 0x00F46AB8, 0x00E31527, 0x001859A1, 0x00EEC0AD, 0x00158C2B,
+ 0x00B23CD0, 0x00497056, 0x00BFE95A, 0x0044A5DC, 0x0053DA43, 0x00A896C5, 0x005E0FC9,
+ 0x00A5434F, 0x008BBD71, 0x0070F1F7, 0x008668FB, 0x007D247D, 0x006A5BE2, 0x00911764,
+ 0x00678E68, 0x009CC2EE, 0x00A44733, 0x005F0BB5, 0x00A992B9, 0x0052DE3F, 0x0045A1A0,
+ 0x00BEED26, 0x0048742A, 0x00B338AC, 0x009DC692, 0x00668A14, 0x00901318, 0x006B5F9E,
+ 0x007C2001, 0x00876C87, 0x0071F58B, 0x008AB90D, 0x002D09F6, 0x00D64570, 0x0020DC7C,
+ 0x00DB90FA, 0x00CCEF65, 0x0037A3E3, 0x00C13AEF, 0x003A7669, 0x00148857, 0x00EFC4D1,
+ 0x00195DDD, 0x00E2115B, 0x00F56EC4, 0x000E2242, 0x00F8BB4E, 0x0003F7C8, 0x004D963F,
+ 0x00B6DAB9, 0x004043B5, 0x00BB0F33, 0x00AC70AC, 0x00573C2A, 0x00A1A526, 0x005AE9A0,
+ 0x0074179E, 0x008F5B18, 0x0079C214, 0x00828E92, 0x0095F10D, 0x006EBD8B, 0x00982487,
+ 0x00636801, 0x00C4D8FA, 0x003F947C, 0x00C90D70, 0x003241F6, 0x00253E69, 0x00DE72EF,
+ 0x0028EBE3, 0x00D3A765, 0x00FD595B, 0x000615DD, 0x00F08CD1, 0x000BC057, 0x001CBFC8,
+ 0x00E7F34E, 0x00116A42, 0x00EA26C4, 0x0076E42A, 0x008DA8AC, 0x007B31A0, 0x00807D26,
+ 0x009702B9, 0x006C4E3F, 0x009AD733, 0x00619BB5, 0x004F658B, 0x00B4290D, 0x0042B001,
+ 0x00B9FC87, 0x00AE8318, 0x0055CF9E, 0x00A35692, 0x00581A14, 0x00FFAAEF, 0x0004E669,
+ 0x00F27F65, 0x000933E3, 0x001E4C7C, 0x00E500FA, 0x001399F6, 0x00E8D570, 0x00C62B4E,
+ 0x003D67C8, 0x00CBFEC4, 0x0030B242, 0x0027CDDD, 0x00DC815B, 0x002A1857, 0x00D154D1,
+ 0x009F3526, 0x006479A0, 0x0092E0AC, 0x0069AC2A, 0x007ED3B5, 0x00859F33, 0x0073063F,
+ 0x00884AB9, 0x00A6B487, 0x005DF801, 0x00AB610D, 0x00502D8B, 0x00475214, 0x00BC1E92,
+ 0x004A879E, 0x00B1CB18, 0x00167BE3, 0x00ED3765, 0x001BAE69, 0x00E0E2EF, 0x00F79D70,
+ 0x000CD1F6, 0x00FA48FA, 0x0001047C, 0x002FFA42, 0x00D4B6C4, 0x00222FC8, 0x00D9634E,
+ 0x00CE1CD1, 0x00355057, 0x00C3C95B, 0x003885DD };
+
+alignas(64) const uint32_t CRC24_T1[256] = {
+ 0x00000000, 0x00488F66, 0x00901ECD, 0x00D891AB, 0x00DB711C, 0x0093FE7A, 0x004B6FD1,
+ 0x0003E0B7, 0x00B6E338, 0x00FE6C5E, 0x0026FDF5, 0x006E7293, 0x006D9224, 0x00251D42,
+ 0x00FD8CE9, 0x00B5038F, 0x006CC771, 0x00244817, 0x00FCD9BC, 0x00B456DA, 0x00B7B66D,
+ 0x00FF390B, 0x0027A8A0, 0x006F27C6, 0x00DA2449, 0x0092AB2F, 0x004A3A84, 0x0002B5E2,
+ 0x00015555, 0x0049DA33, 0x00914B98, 0x00D9C4FE, 0x00D88EE3, 0x00900185, 0x0048902E,
+ 0x00001F48, 0x0003FFFF, 0x004B7099, 0x0093E132, 0x00DB6E54, 0x006E6DDB, 0x0026E2BD,
+ 0x00FE7316, 0x00B6FC70, 0x00B51CC7, 0x00FD93A1, 0x0025020A, 0x006D8D6C, 0x00B44992,
+ 0x00FCC6F4, 0x0024575F, 0x006CD839, 0x006F388E, 0x0027B7E8, 0x00FF2643, 0x00B7A925,
+ 0x0002AAAA, 0x004A25CC, 0x0092B467, 0x00DA3B01, 0x00D9DBB6, 0x009154D0, 0x0049C57B,
+ 0x00014A1D, 0x004B5141, 0x0003DE27, 0x00DB4F8C, 0x0093C0EA, 0x0090205D, 0x00D8AF3B,
+ 0x00003E90, 0x0048B1F6, 0x00FDB279, 0x00B53D1F, 0x006DACB4, 0x002523D2, 0x0026C365,
+ 0x006E4C03, 0x00B6DDA8, 0x00FE52CE, 0x00279630, 0x006F1956, 0x00B788FD, 0x00FF079B,
+ 0x00FCE72C, 0x00B4684A, 0x006CF9E1, 0x00247687, 0x00917508, 0x00D9FA6E, 0x00016BC5,
+ 0x0049E4A3, 0x004A0414, 0x00028B72, 0x00DA1AD9, 0x009295BF, 0x0093DFA2, 0x00DB50C4,
+ 0x0003C16F, 0x004B4E09, 0x0048AEBE, 0x000021D8, 0x00D8B073, 0x00903F15, 0x00253C9A,
+ 0x006DB3FC, 0x00B52257, 0x00FDAD31, 0x00FE4D86, 0x00B6C2E0, 0x006E534B, 0x0026DC2D,
+ 0x00FF18D3, 0x00B797B5, 0x006F061E, 0x00278978, 0x002469CF, 0x006CE6A9, 0x00B47702,
+ 0x00FCF864, 0x0049FBEB, 0x0001748D, 0x00D9E526, 0x00916A40, 0x00928AF7, 0x00DA0591,
+ 0x0002943A, 0x004A1B5C, 0x0096A282, 0x00DE2DE4, 0x0006BC4F, 0x004E3329, 0x004DD39E,
+ 0x00055CF8, 0x00DDCD53, 0x00954235, 0x002041BA, 0x0068CEDC, 0x00B05F77, 0x00F8D011,
+ 0x00FB30A6, 0x00B3BFC0, 0x006B2E6B, 0x0023A10D, 0x00FA65F3, 0x00B2EA95, 0x006A7B3E,
+ 0x0022F458, 0x002114EF, 0x00699B89, 0x00B10A22, 0x00F98544, 0x004C86CB, 0x000409AD,
+ 0x00DC9806, 0x00941760, 0x0097F7D7, 0x00DF78B1, 0x0007E91A, 0x004F667C, 0x004E2C61,
+ 0x0006A307, 0x00DE32AC, 0x0096BDCA, 0x00955D7D, 0x00DDD21B, 0x000543B0, 0x004DCCD6,
+ 0x00F8CF59, 0x00B0403F, 0x0068D194, 0x00205EF2, 0x0023BE45, 0x006B3123, 0x00B3A088,
+ 0x00FB2FEE, 0x0022EB10, 0x006A6476, 0x00B2F5DD, 0x00FA7ABB, 0x00F99A0C, 0x00B1156A,
+ 0x006984C1, 0x00210BA7, 0x00940828, 0x00DC874E, 0x000416E5, 0x004C9983, 0x004F7934,
+ 0x0007F652, 0x00DF67F9, 0x0097E89F, 0x00DDF3C3, 0x00957CA5, 0x004DED0E, 0x00056268,
+ 0x000682DF, 0x004E0DB9, 0x00969C12, 0x00DE1374, 0x006B10FB, 0x00239F9D, 0x00FB0E36,
+ 0x00B38150, 0x00B061E7, 0x00F8EE81, 0x00207F2A, 0x0068F04C, 0x00B134B2, 0x00F9BBD4,
+ 0x00212A7F, 0x0069A519, 0x006A45AE, 0x0022CAC8, 0x00FA5B63, 0x00B2D405, 0x0007D78A,
+ 0x004F58EC, 0x0097C947, 0x00DF4621, 0x00DCA696, 0x009429F0, 0x004CB85B, 0x0004373D,
+ 0x00057D20, 0x004DF246, 0x009563ED, 0x00DDEC8B, 0x00DE0C3C, 0x0096835A, 0x004E12F1,
+ 0x00069D97, 0x00B39E18, 0x00FB117E, 0x002380D5, 0x006B0FB3, 0x0068EF04, 0x00206062,
+ 0x00F8F1C9, 0x00B07EAF, 0x0069BA51, 0x00213537, 0x00F9A49C, 0x00B12BFA, 0x00B2CB4D,
+ 0x00FA442B, 0x0022D580, 0x006A5AE6, 0x00DF5969, 0x0097D60F, 0x004F47A4, 0x0007C8C2,
+ 0x00042875, 0x004CA713, 0x009436B8, 0x00DCB9DE };
+
+alignas(64) const uint32_t CRC24_T2[256] = {
+ 0x00000000, 0x00D70983, 0x00555F80, 0x00825603, 0x0051F286, 0x0086FB05, 0x0004AD06,
+ 0x00D3A485, 0x0059A88B, 0x008EA108, 0x000CF70B, 0x00DBFE88, 0x00085A0D, 0x00DF538E,
+ 0x005D058D, 0x008A0C0E, 0x00491C91, 0x009E1512, 0x001C4311, 0x00CB4A92, 0x0018EE17,
+ 0x00CFE794, 0x004DB197, 0x009AB814, 0x0010B41A, 0x00C7BD99, 0x0045EB9A, 0x0092E219,
+ 0x0041469C, 0x00964F1F, 0x0014191C, 0x00C3109F, 0x006974A4, 0x00BE7D27, 0x003C2B24,
+ 0x00EB22A7, 0x00388622, 0x00EF8FA1, 0x006DD9A2, 0x00BAD021, 0x0030DC2F, 0x00E7D5AC,
+ 0x006583AF, 0x00B28A2C, 0x00612EA9, 0x00B6272A, 0x00347129, 0x00E378AA, 0x00206835,
+ 0x00F761B6, 0x007537B5, 0x00A23E36, 0x00719AB3, 0x00A69330, 0x0024C533, 0x00F3CCB0,
+ 0x0079C0BE, 0x00AEC93D, 0x002C9F3E, 0x00FB96BD, 0x00283238, 0x00FF3BBB, 0x007D6DB8,
+ 0x00AA643B, 0x0029A4CE, 0x00FEAD4D, 0x007CFB4E, 0x00ABF2CD, 0x00785648, 0x00AF5FCB,
+ 0x002D09C8, 0x00FA004B, 0x00700C45, 0x00A705C6, 0x002553C5, 0x00F25A46, 0x0021FEC3,
+ 0x00F6F740, 0x0074A143, 0x00A3A8C0, 0x0060B85F, 0x00B7B1DC, 0x0035E7DF, 0x00E2EE5C,
+ 0x00314AD9, 0x00E6435A, 0x00641559, 0x00B31CDA, 0x003910D4, 0x00EE1957, 0x006C4F54,
+ 0x00BB46D7, 0x0068E252, 0x00BFEBD1, 0x003DBDD2, 0x00EAB451, 0x0040D06A, 0x0097D9E9,
+ 0x00158FEA, 0x00C28669, 0x001122EC, 0x00C62B6F, 0x00447D6C, 0x009374EF, 0x001978E1,
+ 0x00CE7162, 0x004C2761, 0x009B2EE2, 0x00488A67, 0x009F83E4, 0x001DD5E7, 0x00CADC64,
+ 0x0009CCFB, 0x00DEC578, 0x005C937B, 0x008B9AF8, 0x00583E7D, 0x008F37FE, 0x000D61FD,
+ 0x00DA687E, 0x00506470, 0x00876DF3, 0x00053BF0, 0x00D23273, 0x000196F6, 0x00D69F75,
+ 0x0054C976, 0x0083C0F5, 0x00A9041B, 0x007E0D98, 0x00FC5B9B, 0x002B5218, 0x00F8F69D,
+ 0x002FFF1E, 0x00ADA91D, 0x007AA09E, 0x00F0AC90, 0x0027A513, 0x00A5F310, 0x0072FA93,
+ 0x00A15E16, 0x00765795, 0x00F40196, 0x00230815, 0x00E0188A, 0x00371109, 0x00B5470A,
+ 0x00624E89, 0x00B1EA0C, 0x0066E38F, 0x00E4B58C, 0x0033BC0F, 0x00B9B001, 0x006EB982,
+ 0x00ECEF81, 0x003BE602, 0x00E84287, 0x003F4B04, 0x00BD1D07, 0x006A1484, 0x00C070BF,
+ 0x0017793C, 0x00952F3F, 0x004226BC, 0x00918239, 0x00468BBA, 0x00C4DDB9, 0x0013D43A,
+ 0x0099D834, 0x004ED1B7, 0x00CC87B4, 0x001B8E37, 0x00C82AB2, 0x001F2331, 0x009D7532,
+ 0x004A7CB1, 0x00896C2E, 0x005E65AD, 0x00DC33AE, 0x000B3A2D, 0x00D89EA8, 0x000F972B,
+ 0x008DC128, 0x005AC8AB, 0x00D0C4A5, 0x0007CD26, 0x00859B25, 0x005292A6, 0x00813623,
+ 0x00563FA0, 0x00D469A3, 0x00036020, 0x0080A0D5, 0x0057A956, 0x00D5FF55, 0x0002F6D6,
+ 0x00D15253, 0x00065BD0, 0x00840DD3, 0x00530450, 0x00D9085E, 0x000E01DD, 0x008C57DE,
+ 0x005B5E5D, 0x0088FAD8, 0x005FF35B, 0x00DDA558, 0x000AACDB, 0x00C9BC44, 0x001EB5C7,
+ 0x009CE3C4, 0x004BEA47, 0x00984EC2, 0x004F4741, 0x00CD1142, 0x001A18C1, 0x009014CF,
+ 0x00471D4C, 0x00C54B4F, 0x001242CC, 0x00C1E649, 0x0016EFCA, 0x0094B9C9, 0x0043B04A,
+ 0x00E9D471, 0x003EDDF2, 0x00BC8BF1, 0x006B8272, 0x00B826F7, 0x006F2F74, 0x00ED7977,
+ 0x003A70F4, 0x00B07CFA, 0x00677579, 0x00E5237A, 0x00322AF9, 0x00E18E7C, 0x003687FF,
+ 0x00B4D1FC, 0x0063D87F, 0x00A0C8E0, 0x0077C163, 0x00F59760, 0x00229EE3, 0x00F13A66,
+ 0x002633E5, 0x00A465E6, 0x00736C65, 0x00F9606B, 0x002E69E8, 0x00AC3FEB, 0x007B3668,
+ 0x00A892ED, 0x007F9B6E, 0x00FDCD6D, 0x002AC4EE };
+
+alignas(64) const uint32_t CRC24_T3[256] = {
+ 0x00000000, 0x00520936, 0x00A4126C, 0x00F61B5A, 0x004825D8, 0x001A2CEE, 0x00EC37B4,
+ 0x00BE3E82, 0x006B0636, 0x00390F00, 0x00CF145A, 0x009D1D6C, 0x002323EE, 0x00712AD8,
+ 0x00873182, 0x00D538B4, 0x00D60C6C, 0x0084055A, 0x00721E00, 0x00201736, 0x009E29B4,
+ 0x00CC2082, 0x003A3BD8, 0x006832EE, 0x00BD0A5A, 0x00EF036C, 0x00191836, 0x004B1100,
+ 0x00F52F82, 0x00A726B4, 0x00513DEE, 0x000334D8, 0x00AC19D8, 0x00FE10EE, 0x00080BB4,
+ 0x005A0282, 0x00E43C00, 0x00B63536, 0x00402E6C, 0x0012275A, 0x00C71FEE, 0x009516D8,
+ 0x00630D82, 0x003104B4, 0x008F3A36, 0x00DD3300, 0x002B285A, 0x0079216C, 0x007A15B4,
+ 0x00281C82, 0x00DE07D8, 0x008C0EEE, 0x0032306C, 0x0060395A, 0x00962200, 0x00C42B36,
+ 0x00111382, 0x00431AB4, 0x00B501EE, 0x00E708D8, 0x0059365A, 0x000B3F6C, 0x00FD2436,
+ 0x00AF2D00, 0x00A37F36, 0x00F17600, 0x00076D5A, 0x0055646C, 0x00EB5AEE, 0x00B953D8,
+ 0x004F4882, 0x001D41B4, 0x00C87900, 0x009A7036, 0x006C6B6C, 0x003E625A, 0x00805CD8,
+ 0x00D255EE, 0x00244EB4, 0x00764782, 0x0075735A, 0x00277A6C, 0x00D16136, 0x00836800,
+ 0x003D5682, 0x006F5FB4, 0x009944EE, 0x00CB4DD8, 0x001E756C, 0x004C7C5A, 0x00BA6700,
+ 0x00E86E36, 0x005650B4, 0x00045982, 0x00F242D8, 0x00A04BEE, 0x000F66EE, 0x005D6FD8,
+ 0x00AB7482, 0x00F97DB4, 0x00474336, 0x00154A00, 0x00E3515A, 0x00B1586C, 0x006460D8,
+ 0x003669EE, 0x00C072B4, 0x00927B82, 0x002C4500, 0x007E4C36, 0x0088576C, 0x00DA5E5A,
+ 0x00D96A82, 0x008B63B4, 0x007D78EE, 0x002F71D8, 0x00914F5A, 0x00C3466C, 0x00355D36,
+ 0x00675400, 0x00B26CB4, 0x00E06582, 0x00167ED8, 0x004477EE, 0x00FA496C, 0x00A8405A,
+ 0x005E5B00, 0x000C5236, 0x0046FF6C, 0x0014F65A, 0x00E2ED00, 0x00B0E436, 0x000EDAB4,
+ 0x005CD382, 0x00AAC8D8, 0x00F8C1EE, 0x002DF95A, 0x007FF06C, 0x0089EB36, 0x00DBE200,
+ 0x0065DC82, 0x0037D5B4, 0x00C1CEEE, 0x0093C7D8, 0x0090F300, 0x00C2FA36, 0x0034E16C,
+ 0x0066E85A, 0x00D8D6D8, 0x008ADFEE, 0x007CC4B4, 0x002ECD82, 0x00FBF536, 0x00A9FC00,
+ 0x005FE75A, 0x000DEE6C, 0x00B3D0EE, 0x00E1D9D8, 0x0017C282, 0x0045CBB4, 0x00EAE6B4,
+ 0x00B8EF82, 0x004EF4D8, 0x001CFDEE, 0x00A2C36C, 0x00F0CA5A, 0x0006D100, 0x0054D836,
+ 0x0081E082, 0x00D3E9B4, 0x0025F2EE, 0x0077FBD8, 0x00C9C55A, 0x009BCC6C, 0x006DD736,
+ 0x003FDE00, 0x003CEAD8, 0x006EE3EE, 0x0098F8B4, 0x00CAF182, 0x0074CF00, 0x0026C636,
+ 0x00D0DD6C, 0x0082D45A, 0x0057ECEE, 0x0005E5D8, 0x00F3FE82, 0x00A1F7B4, 0x001FC936,
+ 0x004DC000, 0x00BBDB5A, 0x00E9D26C, 0x00E5805A, 0x00B7896C, 0x00419236, 0x00139B00,
+ 0x00ADA582, 0x00FFACB4, 0x0009B7EE, 0x005BBED8, 0x008E866C, 0x00DC8F5A, 0x002A9400,
+ 0x00789D36, 0x00C6A3B4, 0x0094AA82, 0x0062B1D8, 0x0030B8EE, 0x00338C36, 0x00618500,
+ 0x00979E5A, 0x00C5976C, 0x007BA9EE, 0x0029A0D8, 0x00DFBB82, 0x008DB2B4, 0x00588A00,
+ 0x000A8336, 0x00FC986C, 0x00AE915A, 0x0010AFD8, 0x0042A6EE, 0x00B4BDB4, 0x00E6B482,
+ 0x00499982, 0x001B90B4, 0x00ED8BEE, 0x00BF82D8, 0x0001BC5A, 0x0053B56C, 0x00A5AE36,
+ 0x00F7A700, 0x00229FB4, 0x00709682, 0x00868DD8, 0x00D484EE, 0x006ABA6C, 0x0038B35A,
+ 0x00CEA800, 0x009CA136, 0x009F95EE, 0x00CD9CD8, 0x003B8782, 0x00698EB4, 0x00D7B036,
+ 0x0085B900, 0x0073A25A, 0x0021AB6C, 0x00F493D8, 0x00A69AEE, 0x005081B4, 0x00028882,
+ 0x00BCB600, 0x00EEBF36, 0x0018A46C, 0x004AAD5A };
+
+inline uint32_t process8(uint32_t crc, uint8_t data)
+ {
+ return (crc >> 8) ^ CRC24_T0[(crc & 0xff) ^ data];
+ }
+
+inline uint32_t process32(uint32_t crc, uint32_t word)
+ {
+ crc ^= word;
+ crc = CRC24_T3[(crc >> 0) & 0xff]
+ ^ CRC24_T2[(crc >> 8) & 0xff]
+ ^ CRC24_T1[(crc >> 16) & 0xff]
+ ^ CRC24_T0[(crc >> 24) & 0xff];
+ return crc;
+ }
+}
+
+std::unique_ptr<HashFunction> CRC24::copy_state() const
+ {
+ return std::unique_ptr<HashFunction>(new CRC24(*this));
+ }
+
+/*
+* Update a CRC24 Checksum
+*
+* Implementation uses Slicing-by-N algorithm described in
+* "Novel Table Lookup-Based Algorithms for High-Performance
+* CRC Generation", by M.Kounavis.
+*
+* This algorithm uses 4 precomputed look-up tables. First
+* table T0 is computed same way as in a method proposed
+* by D. Sarwate (1988). Then T_1, T2 and T3 are computed
+* in following way:
+*
+* T1[j] = (T0[j] >> 8) ^ T0[ T0[j] & 0xFF ]
+* T2[j] = (T1[j] >> 8) ^ T0[ T1[j] & 0xFF ]
+* T3[j] = (T2[j] >> 8) ^ T0[ T2[j] & 0xFF ]
+*
+*/
+void CRC24::add_data(const uint8_t input[], size_t length)
+ {
+ uint32_t d[4];
+ uint32_t tmp = m_crc;
+
+ // Input is word aligned if WA & input == 0
+ static const uint8_t WA = (BOTAN_MP_WORD_BITS/8) - 1;
+
+ // Ensure input is word aligned before processing in parallel
+ for(;length && (reinterpret_cast<uintptr_t>(input) & WA); length--)
+ tmp = process8(tmp, *input++);
+
+ while(length >= 16)
+ {
+ load_le(d, input, 4);
+ tmp = process32(tmp, d[0]);
+ tmp = process32(tmp, d[1]);
+ tmp = process32(tmp, d[2]);
+ tmp = process32(tmp, d[3]);
+
+ input += 16;
+ length -= 16;
+ }
+
+ while(length--)
+ tmp = process8(tmp, *input++);
+
+ m_crc = tmp & 0xffffff;
+ }
+
+/*
+* Finalize a CRC24 Checksum
+*/
+void CRC24::final_result(uint8_t output[])
+ {
+ output[0] = get_byte(3, m_crc);
+ output[1] = get_byte(2, m_crc);
+ output[2] = get_byte(1, m_crc);
+ clear();
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/hash/checksum/crc24/crc24.h b/comm/third_party/botan/src/lib/hash/checksum/crc24/crc24.h
new file mode 100644
index 0000000000..1809740a40
--- /dev/null
+++ b/comm/third_party/botan/src/lib/hash/checksum/crc24/crc24.h
@@ -0,0 +1,41 @@
+/*
+* CRC24
+* (C) 1999-2007 Jack Lloyd
+* (C) 2017 [Ribose Inc](https://www.ribose.com). Performed by Krzysztof Kwiatkowski.
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_CRC24_H_
+#define BOTAN_CRC24_H_
+
+#include <botan/hash.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(crc24.h)
+
+namespace Botan {
+
+/**
+* 24-bit cyclic redundancy check
+*/
+class BOTAN_PUBLIC_API(2,0) CRC24 final : public HashFunction
+ {
+ public:
+ std::string name() const override { return "CRC24"; }
+ size_t output_length() const override { return 3; }
+ HashFunction* clone() const override { return new CRC24; }
+ std::unique_ptr<HashFunction> copy_state() const override;
+
+ void clear() override { m_crc = 0XCE04B7L; }
+
+ CRC24() { clear(); }
+ ~CRC24() { clear(); }
+ private:
+ void add_data(const uint8_t[], size_t) override;
+ void final_result(uint8_t[]) override;
+ uint32_t m_crc;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/hash/checksum/crc24/info.txt b/comm/third_party/botan/src/lib/hash/checksum/crc24/info.txt
new file mode 100644
index 0000000000..3c5070cc4a
--- /dev/null
+++ b/comm/third_party/botan/src/lib/hash/checksum/crc24/info.txt
@@ -0,0 +1,3 @@
+<defines>
+CRC24 -> 20131128
+</defines>
diff --git a/comm/third_party/botan/src/lib/hash/checksum/crc32/crc32.cpp b/comm/third_party/botan/src/lib/hash/checksum/crc32/crc32.cpp
new file mode 100644
index 0000000000..1a76764a80
--- /dev/null
+++ b/comm/third_party/botan/src/lib/hash/checksum/crc32/crc32.cpp
@@ -0,0 +1,111 @@
+/*
+* CRC32
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/crc32.h>
+#include <botan/loadstor.h>
+
+namespace Botan {
+
+std::unique_ptr<HashFunction> CRC32::copy_state() const
+ {
+ return std::unique_ptr<HashFunction>(new CRC32(*this));
+ }
+
+namespace {
+
+const uint32_t CRC32_T0[256] = {
+ 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F,
+ 0xE963A535, 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,
+ 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2,
+ 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
+ 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9,
+ 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,
+ 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C,
+ 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
+ 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423,
+ 0xCFBA9599, 0xB8BDA50F, 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
+ 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 0x76DC4190, 0x01DB7106,
+ 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
+ 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D,
+ 0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,
+ 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950,
+ 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
+ 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7,
+ 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,
+ 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C, 0x270241AA,
+ 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
+ 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81,
+ 0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A,
+ 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84,
+ 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
+ 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB,
+ 0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
+ 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, 0xA1D1937E,
+ 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
+ 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55,
+ 0x316E8EEF, 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
+ 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28,
+ 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
+ 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F,
+ 0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38,
+ 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242,
+ 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
+ 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69,
+ 0x616BFFD3, 0x166CCF45, 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2,
+ 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC,
+ 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
+ 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693,
+ 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
+ 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D };
+
+}
+
+/*
+* Update a CRC32 Checksum
+*/
+void CRC32::add_data(const uint8_t input[], size_t length)
+ {
+ uint32_t tmp = m_crc;
+ while(length >= 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);
+ input += 16;
+ length -= 16;
+ }
+
+ for(size_t i = 0; i != length; ++i)
+ tmp = CRC32_T0[(tmp ^ input[i]) & 0xFF] ^ (tmp >> 8);
+
+ m_crc = tmp;
+ }
+
+/*
+* Finalize a CRC32 Checksum
+*/
+void CRC32::final_result(uint8_t output[])
+ {
+ m_crc ^= 0xFFFFFFFF;
+ store_be(m_crc, output);
+ clear();
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/hash/checksum/crc32/crc32.h b/comm/third_party/botan/src/lib/hash/checksum/crc32/crc32.h
new file mode 100644
index 0000000000..f712b42cac
--- /dev/null
+++ b/comm/third_party/botan/src/lib/hash/checksum/crc32/crc32.h
@@ -0,0 +1,40 @@
+/*
+* CRC32
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_CRC32_H_
+#define BOTAN_CRC32_H_
+
+#include <botan/hash.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(crc32.h)
+
+namespace Botan {
+
+/**
+* 32-bit cyclic redundancy check
+*/
+class BOTAN_PUBLIC_API(2,0) CRC32 final : public HashFunction
+ {
+ public:
+ std::string name() const override { return "CRC32"; }
+ size_t output_length() const override { return 4; }
+ HashFunction* clone() const override { return new CRC32; }
+ std::unique_ptr<HashFunction> copy_state() const override;
+
+ void clear() override { m_crc = 0xFFFFFFFF; }
+
+ CRC32() { clear(); }
+ ~CRC32() { clear(); }
+ private:
+ void add_data(const uint8_t[], size_t) override;
+ void final_result(uint8_t[]) override;
+ uint32_t m_crc;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/hash/checksum/crc32/info.txt b/comm/third_party/botan/src/lib/hash/checksum/crc32/info.txt
new file mode 100644
index 0000000000..7d3e452398
--- /dev/null
+++ b/comm/third_party/botan/src/lib/hash/checksum/crc32/info.txt
@@ -0,0 +1,3 @@
+<defines>
+CRC32 -> 20131128
+</defines>
diff --git a/comm/third_party/botan/src/lib/hash/checksum/info.txt b/comm/third_party/botan/src/lib/hash/checksum/info.txt
new file mode 100644
index 0000000000..5aab13b59f
--- /dev/null
+++ b/comm/third_party/botan/src/lib/hash/checksum/info.txt
@@ -0,0 +1,4 @@
+
+<requires>
+hash
+</requires>
diff --git a/comm/third_party/botan/src/lib/hash/comb4p/comb4p.cpp b/comm/third_party/botan/src/lib/hash/comb4p/comb4p.cpp
new file mode 100644
index 0000000000..419e00df54
--- /dev/null
+++ b/comm/third_party/botan/src/lib/hash/comb4p/comb4p.cpp
@@ -0,0 +1,110 @@
+/*
+* Comb4P hash combiner
+* (C) 2010 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/comb4p.h>
+#include <botan/exceptn.h>
+
+namespace Botan {
+
+namespace {
+
+void comb4p_round(secure_vector<uint8_t>& out,
+ const secure_vector<uint8_t>& in,
+ uint8_t round_no,
+ HashFunction& h1,
+ HashFunction& h2)
+ {
+ h1.update(round_no);
+ h2.update(round_no);
+
+ h1.update(in.data(), in.size());
+ h2.update(in.data(), in.size());
+
+ secure_vector<uint8_t> h_buf = h1.final();
+ xor_buf(out.data(), h_buf.data(), std::min(out.size(), h_buf.size()));
+
+ h_buf = h2.final();
+ xor_buf(out.data(), h_buf.data(), std::min(out.size(), h_buf.size()));
+ }
+
+}
+
+Comb4P::Comb4P(HashFunction* h1, HashFunction* h2) :
+ m_hash1(h1), m_hash2(h2)
+ {
+ if(m_hash1->name() == m_hash2->name())
+ throw Invalid_Argument("Comb4P: Must use two distinct hashes");
+
+ if(m_hash1->output_length() != m_hash2->output_length())
+ throw Invalid_Argument("Comb4P: Incompatible hashes " +
+ m_hash1->name() + " and " +
+ m_hash2->name());
+
+ clear();
+ }
+
+size_t Comb4P::hash_block_size() const
+ {
+ if(m_hash1->hash_block_size() == m_hash2->hash_block_size())
+ return m_hash1->hash_block_size();
+
+ /*
+ * Return LCM of the block sizes? This would probably be OK for
+ * HMAC, which is the main thing relying on knowing the block size.
+ */
+ return 0;
+ }
+
+void Comb4P::clear()
+ {
+ m_hash1->clear();
+ m_hash2->clear();
+
+ // Prep for processing next message, if any
+ m_hash1->update(0);
+ m_hash2->update(0);
+ }
+
+std::unique_ptr<HashFunction> Comb4P::copy_state() const
+ {
+ std::unique_ptr<Comb4P> copy(new Comb4P);
+ copy->m_hash1 = m_hash1->copy_state();
+ copy->m_hash2 = m_hash2->copy_state();
+ // work around GCC 4.8 bug
+ return std::unique_ptr<HashFunction>(copy.release());
+ }
+
+void Comb4P::add_data(const uint8_t input[], size_t length)
+ {
+ m_hash1->update(input, length);
+ m_hash2->update(input, length);
+ }
+
+void Comb4P::final_result(uint8_t out[])
+ {
+ secure_vector<uint8_t> h1 = m_hash1->final();
+ secure_vector<uint8_t> h2 = m_hash2->final();
+
+ // First round
+ xor_buf(h1.data(), h2.data(), std::min(h1.size(), h2.size()));
+
+ // Second round
+ comb4p_round(h2, h1, 1, *m_hash1, *m_hash2);
+
+ // Third round
+ comb4p_round(h1, h2, 2, *m_hash1, *m_hash2);
+
+ copy_mem(out , h1.data(), h1.size());
+ copy_mem(out + h1.size(), h2.data(), h2.size());
+
+ // Prep for processing next message, if any
+ m_hash1->update(0);
+ m_hash2->update(0);
+ }
+
+}
+
diff --git a/comm/third_party/botan/src/lib/hash/comb4p/comb4p.h b/comm/third_party/botan/src/lib/hash/comb4p/comb4p.h
new file mode 100644
index 0000000000..518314c17d
--- /dev/null
+++ b/comm/third_party/botan/src/lib/hash/comb4p/comb4p.h
@@ -0,0 +1,61 @@
+/*
+* Comb4P hash combiner
+* (C) 2010 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_COMB4P_H_
+#define BOTAN_COMB4P_H_
+
+#include <botan/hash.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(comb4p.h)
+
+namespace Botan {
+
+/**
+* Combines two hash functions using a Feistel scheme. Described in
+* "On the Security of Hash Function Combiners", Anja Lehmann
+*/
+class BOTAN_PUBLIC_API(2,0) Comb4P final : public HashFunction
+ {
+ public:
+ /**
+ * @param h1 the first hash
+ * @param h2 the second hash
+ */
+ Comb4P(HashFunction* h1, HashFunction* h2);
+
+ size_t hash_block_size() const override;
+
+ size_t output_length() const override
+ {
+ return m_hash1->output_length() + m_hash2->output_length();
+ }
+
+ HashFunction* clone() const override
+ {
+ return new Comb4P(m_hash1->clone(), m_hash2->clone());
+ }
+
+ std::unique_ptr<HashFunction> copy_state() const override;
+
+ std::string name() const override
+ {
+ return "Comb4P(" + m_hash1->name() + "," + m_hash2->name() + ")";
+ }
+
+ void clear() override;
+ private:
+ Comb4P() = default;
+
+ void add_data(const uint8_t input[], size_t length) override;
+ void final_result(uint8_t out[]) override;
+
+ std::unique_ptr<HashFunction> m_hash1, m_hash2;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/hash/comb4p/info.txt b/comm/third_party/botan/src/lib/hash/comb4p/info.txt
new file mode 100644
index 0000000000..1bfac599ed
--- /dev/null
+++ b/comm/third_party/botan/src/lib/hash/comb4p/info.txt
@@ -0,0 +1,3 @@
+<defines>
+COMB4P -> 20131128
+</defines>
diff --git a/comm/third_party/botan/src/lib/hash/gost_3411/gost_3411.cpp b/comm/third_party/botan/src/lib/hash/gost_3411/gost_3411.cpp
new file mode 100644
index 0000000000..e3bcd21102
--- /dev/null
+++ b/comm/third_party/botan/src/lib/hash/gost_3411/gost_3411.cpp
@@ -0,0 +1,248 @@
+/*
+* GOST 34.11
+* (C) 2009 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/gost_3411.h>
+#include <botan/loadstor.h>
+
+namespace Botan {
+
+/**
+* GOST 34.11 Constructor
+*/
+GOST_34_11::GOST_34_11() :
+ m_cipher(GOST_28147_89_Params("R3411_CryptoPro")),
+ m_buffer(32),
+ m_sum(32),
+ m_hash(32)
+ {
+ m_count = 0;
+ m_position = 0;
+ }
+
+void GOST_34_11::clear()
+ {
+ m_cipher.clear();
+ zeroise(m_sum);
+ zeroise(m_hash);
+ m_count = 0;
+ m_position = 0;
+ }
+
+std::unique_ptr<HashFunction> GOST_34_11::copy_state() const
+ {
+ return std::unique_ptr<HashFunction>(new GOST_34_11(*this));
+ }
+
+/**
+* Hash additional inputs
+*/
+void GOST_34_11::add_data(const uint8_t input[], size_t length)
+ {
+ m_count += length;
+
+ if(m_position)
+ {
+ buffer_insert(m_buffer, m_position, input, length);
+
+ if(m_position + length >= hash_block_size())
+ {
+ compress_n(m_buffer.data(), 1);
+ input += (hash_block_size() - m_position);
+ length -= (hash_block_size() - m_position);
+ m_position = 0;
+ }
+ }
+
+ const size_t full_blocks = length / hash_block_size();
+ const size_t remaining = length % hash_block_size();
+
+ if(full_blocks)
+ compress_n(input, full_blocks);
+
+ buffer_insert(m_buffer, m_position, input + full_blocks * hash_block_size(), remaining);
+ m_position += remaining;
+ }
+
+/**
+* The GOST 34.11 compression function
+*/
+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;
+ carry = get_byte(0, s);
+ m_sum[j] = get_byte(1, s);
+ }
+
+ uint8_t S[32] = { 0 };
+
+ uint64_t U[4], V[4];
+ load_be(U, m_hash.data(), 4);
+ load_be(V, input + 32*i, 4);
+
+ for(size_t j = 0; j != 4; ++j)
+ {
+ uint8_t key[32] = { 0 };
+
+ // P transformation
+ for(size_t k = 0; k != 4; ++k)
+ {
+ const uint64_t UVk = U[k] ^ V[k];
+ for(size_t l = 0; l != 8; ++l)
+ key[4*l+k] = get_byte(l, UVk);
+ }
+
+ m_cipher.set_key(key, 32);
+ m_cipher.encrypt(&m_hash[8*j], S + 8*j);
+
+ if(j == 3)
+ break;
+
+ // A(x)
+ uint64_t A_U = U[0];
+ U[0] = U[1];
+ U[1] = U[2];
+ U[2] = U[3];
+ U[3] = U[0] ^ A_U;
+
+ if(j == 1) // C_3
+ {
+ U[0] ^= 0x00FF00FF00FF00FF;
+ U[1] ^= 0xFF00FF00FF00FF00;
+ U[2] ^= 0x00FFFF00FF0000FF;
+ U[3] ^= 0xFF000000FFFF00FF;
+ }
+
+ // A(A(x))
+ uint64_t AA_V_1 = V[0] ^ V[1];
+ uint64_t AA_V_2 = V[1] ^ V[2];
+ V[0] = V[2];
+ V[1] = V[3];
+ V[2] = AA_V_1;
+ V[3] = AA_V_2;
+ }
+
+ uint8_t S2[32] = { 0 };
+
+ // 12 rounds of psi
+ S2[ 0] = S[24];
+ S2[ 1] = S[25];
+ S2[ 2] = S[26];
+ S2[ 3] = S[27];
+ S2[ 4] = S[28];
+ S2[ 5] = S[29];
+ S2[ 6] = S[30];
+ S2[ 7] = S[31];
+ S2[ 8] = S[ 0] ^ S[ 2] ^ S[ 4] ^ S[ 6] ^ S[24] ^ S[30];
+ S2[ 9] = S[ 1] ^ S[ 3] ^ S[ 5] ^ S[ 7] ^ S[25] ^ S[31];
+ S2[10] = S[ 0] ^ S[ 8] ^ S[24] ^ S[26] ^ S[30];
+ S2[11] = S[ 1] ^ S[ 9] ^ S[25] ^ S[27] ^ S[31];
+ S2[12] = S[ 0] ^ S[ 4] ^ S[ 6] ^ S[10] ^ S[24] ^ S[26] ^ S[28] ^ S[30];
+ S2[13] = S[ 1] ^ S[ 5] ^ S[ 7] ^ S[11] ^ S[25] ^ S[27] ^ S[29] ^ S[31];
+ S2[14] = S[ 0] ^ S[ 4] ^ S[ 8] ^ S[12] ^ S[24] ^ S[26] ^ S[28];
+ S2[15] = S[ 1] ^ S[ 5] ^ S[ 9] ^ S[13] ^ S[25] ^ S[27] ^ S[29];
+ S2[16] = S[ 2] ^ S[ 6] ^ S[10] ^ S[14] ^ S[26] ^ S[28] ^ S[30];
+ S2[17] = S[ 3] ^ S[ 7] ^ S[11] ^ S[15] ^ S[27] ^ S[29] ^ S[31];
+ S2[18] = S[ 0] ^ S[ 2] ^ S[ 6] ^ S[ 8] ^ S[12] ^ S[16] ^ S[24] ^ S[28];
+ S2[19] = S[ 1] ^ S[ 3] ^ S[ 7] ^ S[ 9] ^ S[13] ^ S[17] ^ S[25] ^ S[29];
+ S2[20] = S[ 2] ^ S[ 4] ^ S[ 8] ^ S[10] ^ S[14] ^ S[18] ^ S[26] ^ S[30];
+ S2[21] = S[ 3] ^ S[ 5] ^ S[ 9] ^ S[11] ^ S[15] ^ S[19] ^ S[27] ^ S[31];
+ S2[22] = S[ 0] ^ S[ 2] ^ S[10] ^ S[12] ^ S[16] ^ S[20] ^ S[24] ^ S[28] ^ S[30];
+ S2[23] = S[ 1] ^ S[ 3] ^ S[11] ^ S[13] ^ S[17] ^ S[21] ^ S[25] ^ S[29] ^ S[31];
+ S2[24] = S[ 0] ^ S[ 6] ^ S[12] ^ S[14] ^ S[18] ^ S[22] ^ S[24] ^ S[26];
+ S2[25] = S[ 1] ^ S[ 7] ^ S[13] ^ S[15] ^ S[19] ^ S[23] ^ S[25] ^ S[27];
+ S2[26] = S[ 2] ^ S[ 8] ^ S[14] ^ S[16] ^ S[20] ^ S[24] ^ S[26] ^ S[28];
+ S2[27] = S[ 3] ^ S[ 9] ^ S[15] ^ S[17] ^ S[21] ^ S[25] ^ S[27] ^ S[29];
+ S2[28] = S[ 4] ^ S[10] ^ S[16] ^ S[18] ^ S[22] ^ S[26] ^ S[28] ^ S[30];
+ S2[29] = S[ 5] ^ S[11] ^ S[17] ^ S[19] ^ S[23] ^ S[27] ^ S[29] ^ S[31];
+ S2[30] = S[ 0] ^ S[ 2] ^ S[ 4] ^ S[12] ^ S[18] ^ S[20] ^ S[28];
+ S2[31] = S[ 1] ^ S[ 3] ^ S[ 5] ^ S[13] ^ S[19] ^ S[21] ^ S[29];
+
+ xor_buf(S, S2, input + 32*i, 32);
+
+ S2[0] = S[0] ^ S[2] ^ S[4] ^ S[6] ^ S[24] ^ S[30];
+ S2[1] = S[1] ^ S[3] ^ S[5] ^ S[7] ^ S[25] ^ S[31];
+
+ copy_mem(S, S+2, 30);
+ S[30] = S2[0];
+ S[31] = S2[1];
+
+ xor_buf(S, m_hash.data(), 32);
+
+ // 61 rounds of psi
+ S2[ 0] = S[ 2] ^ S[ 6] ^ S[14] ^ S[20] ^ S[22] ^ S[26] ^ S[28] ^ S[30];
+ S2[ 1] = S[ 3] ^ S[ 7] ^ S[15] ^ S[21] ^ S[23] ^ S[27] ^ S[29] ^ S[31];
+ S2[ 2] = S[ 0] ^ S[ 2] ^ S[ 6] ^ S[ 8] ^ S[16] ^ S[22] ^ S[28];
+ S2[ 3] = S[ 1] ^ S[ 3] ^ S[ 7] ^ S[ 9] ^ S[17] ^ S[23] ^ S[29];
+ S2[ 4] = S[ 2] ^ S[ 4] ^ S[ 8] ^ S[10] ^ S[18] ^ S[24] ^ S[30];
+ S2[ 5] = S[ 3] ^ S[ 5] ^ S[ 9] ^ S[11] ^ S[19] ^ S[25] ^ S[31];
+ S2[ 6] = S[ 0] ^ S[ 2] ^ S[10] ^ S[12] ^ S[20] ^ S[24] ^ S[26] ^ S[30];
+ S2[ 7] = S[ 1] ^ S[ 3] ^ S[11] ^ S[13] ^ S[21] ^ S[25] ^ S[27] ^ S[31];
+ S2[ 8] = S[ 0] ^ S[ 6] ^ S[12] ^ S[14] ^ S[22] ^ S[24] ^ S[26] ^ S[28] ^ S[30];
+ S2[ 9] = S[ 1] ^ S[ 7] ^ S[13] ^ S[15] ^ S[23] ^ S[25] ^ S[27] ^ S[29] ^ S[31];
+ S2[10] = S[ 0] ^ S[ 4] ^ S[ 6] ^ S[ 8] ^ S[14] ^ S[16] ^ S[26] ^ S[28];
+ S2[11] = S[ 1] ^ S[ 5] ^ S[ 7] ^ S[ 9] ^ S[15] ^ S[17] ^ S[27] ^ S[29];
+ S2[12] = S[ 2] ^ S[ 6] ^ S[ 8] ^ S[10] ^ S[16] ^ S[18] ^ S[28] ^ S[30];
+ S2[13] = S[ 3] ^ S[ 7] ^ S[ 9] ^ S[11] ^ S[17] ^ S[19] ^ S[29] ^ S[31];
+ S2[14] = S[ 0] ^ S[ 2] ^ S[ 6] ^ S[ 8] ^ S[10] ^ S[12] ^ S[18] ^ S[20] ^ S[24];
+ S2[15] = S[ 1] ^ S[ 3] ^ S[ 7] ^ S[ 9] ^ S[11] ^ S[13] ^ S[19] ^ S[21] ^ S[25];
+ S2[16] = S[ 2] ^ S[ 4] ^ S[ 8] ^ S[10] ^ S[12] ^ S[14] ^ S[20] ^ S[22] ^ S[26];
+ S2[17] = S[ 3] ^ S[ 5] ^ S[ 9] ^ S[11] ^ S[13] ^ S[15] ^ S[21] ^ S[23] ^ S[27];
+ S2[18] = S[ 4] ^ S[ 6] ^ S[10] ^ S[12] ^ S[14] ^ S[16] ^ S[22] ^ S[24] ^ S[28];
+ S2[19] = S[ 5] ^ S[ 7] ^ S[11] ^ S[13] ^ S[15] ^ S[17] ^ S[23] ^ S[25] ^ S[29];
+ S2[20] = S[ 6] ^ S[ 8] ^ S[12] ^ S[14] ^ S[16] ^ S[18] ^ S[24] ^ S[26] ^ S[30];
+ S2[21] = S[ 7] ^ S[ 9] ^ S[13] ^ S[15] ^ S[17] ^ S[19] ^ S[25] ^ S[27] ^ S[31];
+ S2[22] = S[ 0] ^ S[ 2] ^ S[ 4] ^ S[ 6] ^ S[ 8] ^ S[10] ^ S[14] ^ S[16] ^
+ S[18] ^ S[20] ^ S[24] ^ S[26] ^ S[28] ^ S[30];
+ S2[23] = S[ 1] ^ S[ 3] ^ S[ 5] ^ S[ 7] ^ S[ 9] ^ S[11] ^ S[15] ^ S[17] ^
+ S[19] ^ S[21] ^ S[25] ^ S[27] ^ S[29] ^ S[31];
+ S2[24] = S[ 0] ^ S[ 8] ^ S[10] ^ S[12] ^ S[16] ^ S[18] ^ S[20] ^ S[22] ^
+ S[24] ^ S[26] ^ S[28];
+ S2[25] = S[ 1] ^ S[ 9] ^ S[11] ^ S[13] ^ S[17] ^ S[19] ^ S[21] ^ S[23] ^
+ S[25] ^ S[27] ^ S[29];
+ S2[26] = S[ 2] ^ S[10] ^ S[12] ^ S[14] ^ S[18] ^ S[20] ^ S[22] ^ S[24] ^
+ S[26] ^ S[28] ^ S[30];
+ S2[27] = S[ 3] ^ S[11] ^ S[13] ^ S[15] ^ S[19] ^ S[21] ^ S[23] ^ S[25] ^
+ S[27] ^ S[29] ^ S[31];
+ S2[28] = S[ 0] ^ S[ 2] ^ S[ 6] ^ S[12] ^ S[14] ^ S[16] ^ S[20] ^ S[22] ^ S[26] ^ S[28];
+ S2[29] = S[ 1] ^ S[ 3] ^ S[ 7] ^ S[13] ^ S[15] ^ S[17] ^ S[21] ^ S[23] ^ S[27] ^ S[29];
+ S2[30] = S[ 2] ^ S[ 4] ^ S[ 8] ^ S[14] ^ S[16] ^ S[18] ^ S[22] ^ S[24] ^ S[28] ^ S[30];
+ S2[31] = S[ 3] ^ S[ 5] ^ S[ 9] ^ S[15] ^ S[17] ^ S[19] ^ S[23] ^ S[25] ^ S[29] ^ S[31];
+
+ copy_mem(m_hash.data(), S2, 32);
+ }
+ }
+
+/**
+* Produce the final GOST 34.11 output
+*/
+void GOST_34_11::final_result(uint8_t out[])
+ {
+ if(m_position)
+ {
+ clear_mem(m_buffer.data() + m_position, m_buffer.size() - m_position);
+ compress_n(m_buffer.data(), 1);
+ }
+
+ secure_vector<uint8_t> length_buf(32);
+ const uint64_t bit_count = m_count * 8;
+ store_le(bit_count, length_buf.data());
+
+ secure_vector<uint8_t> sum_buf = m_sum;
+
+ compress_n(length_buf.data(), 1);
+ compress_n(sum_buf.data(), 1);
+
+ copy_mem(out, m_hash.data(), 32);
+
+ clear();
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/hash/gost_3411/gost_3411.h b/comm/third_party/botan/src/lib/hash/gost_3411/gost_3411.h
new file mode 100644
index 0000000000..51d7aa4777
--- /dev/null
+++ b/comm/third_party/botan/src/lib/hash/gost_3411/gost_3411.h
@@ -0,0 +1,47 @@
+/*
+* GOST 34.11
+* (C) 2009 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_GOST_3411_H_
+#define BOTAN_GOST_3411_H_
+
+#include <botan/hash.h>
+#include <botan/gost_28147.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(gost_3411.h)
+
+namespace Botan {
+
+/**
+* GOST 34.11
+*/
+class BOTAN_PUBLIC_API(2,0) GOST_34_11 final : public HashFunction
+ {
+ public:
+ std::string name() const override { return "GOST-R-34.11-94" ; }
+ size_t output_length() const override { return 32; }
+ size_t hash_block_size() const override { return 32; }
+ HashFunction* clone() const override { return new GOST_34_11; }
+ std::unique_ptr<HashFunction> copy_state() const override;
+
+ void clear() override;
+
+ GOST_34_11();
+ private:
+ void compress_n(const uint8_t input[], size_t blocks);
+
+ void add_data(const uint8_t[], size_t) override;
+ void final_result(uint8_t[]) override;
+
+ GOST_28147_89 m_cipher;
+ secure_vector<uint8_t> m_buffer, m_sum, m_hash;
+ size_t m_position;
+ uint64_t m_count;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/hash/gost_3411/info.txt b/comm/third_party/botan/src/lib/hash/gost_3411/info.txt
new file mode 100644
index 0000000000..31749c3022
--- /dev/null
+++ b/comm/third_party/botan/src/lib/hash/gost_3411/info.txt
@@ -0,0 +1,7 @@
+<defines>
+GOST_34_11 -> 20131128
+</defines>
+
+<requires>
+gost_28147
+</requires>
diff --git a/comm/third_party/botan/src/lib/hash/hash.cpp b/comm/third_party/botan/src/lib/hash/hash.cpp
new file mode 100644
index 0000000000..63218006c9
--- /dev/null
+++ b/comm/third_party/botan/src/lib/hash/hash.cpp
@@ -0,0 +1,360 @@
+/*
+* Hash Functions
+* (C) 2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/hash.h>
+#include <botan/scan_name.h>
+#include <botan/exceptn.h>
+
+#if defined(BOTAN_HAS_ADLER32)
+ #include <botan/adler32.h>
+#endif
+
+#if defined(BOTAN_HAS_CRC24)
+ #include <botan/crc24.h>
+#endif
+
+#if defined(BOTAN_HAS_CRC32)
+ #include <botan/crc32.h>
+#endif
+
+#if defined(BOTAN_HAS_GOST_34_11)
+ #include <botan/gost_3411.h>
+#endif
+
+#if defined(BOTAN_HAS_KECCAK)
+ #include <botan/keccak.h>
+#endif
+
+#if defined(BOTAN_HAS_MD4)
+ #include <botan/md4.h>
+#endif
+
+#if defined(BOTAN_HAS_MD5)
+ #include <botan/md5.h>
+#endif
+
+#if defined(BOTAN_HAS_RIPEMD_160)
+ #include <botan/rmd160.h>
+#endif
+
+#if defined(BOTAN_HAS_SHA1)
+ #include <botan/sha160.h>
+#endif
+
+#if defined(BOTAN_HAS_SHA2_32)
+ #include <botan/sha2_32.h>
+#endif
+
+#if defined(BOTAN_HAS_SHA2_64)
+ #include <botan/sha2_64.h>
+#endif
+
+#if defined(BOTAN_HAS_SHA3)
+ #include <botan/sha3.h>
+#endif
+
+#if defined(BOTAN_HAS_SHAKE)
+ #include <botan/shake.h>
+#endif
+
+#if defined(BOTAN_HAS_SKEIN_512)
+ #include <botan/skein_512.h>
+#endif
+
+#if defined(BOTAN_HAS_STREEBOG)
+ #include <botan/streebog.h>
+#endif
+
+#if defined(BOTAN_HAS_SM3)
+ #include <botan/sm3.h>
+#endif
+
+#if defined(BOTAN_HAS_TIGER)
+ #include <botan/tiger.h>
+#endif
+
+#if defined(BOTAN_HAS_WHIRLPOOL)
+ #include <botan/whrlpool.h>
+#endif
+
+#if defined(BOTAN_HAS_PARALLEL_HASH)
+ #include <botan/par_hash.h>
+#endif
+
+#if defined(BOTAN_HAS_COMB4P)
+ #include <botan/comb4p.h>
+#endif
+
+#if defined(BOTAN_HAS_BLAKE2B)
+ #include <botan/blake2b.h>
+#endif
+
+#if defined(BOTAN_HAS_OPENSSL)
+ #include <botan/internal/openssl.h>
+#endif
+
+#if defined(BOTAN_HAS_COMMONCRYPTO)
+ #include <botan/internal/commoncrypto.h>
+#endif
+
+namespace Botan {
+
+std::unique_ptr<HashFunction> HashFunction::create(const std::string& algo_spec,
+ const std::string& provider)
+ {
+
+#if defined(BOTAN_HAS_COMMONCRYPTO)
+ if(provider.empty() || provider == "commoncrypto")
+ {
+ if(auto hash = make_commoncrypto_hash(algo_spec))
+ return hash;
+
+ if(!provider.empty())
+ return nullptr;
+ }
+#endif
+
+#if defined(BOTAN_HAS_OPENSSL)
+ if(provider.empty() || provider == "openssl")
+ {
+ if(auto hash = make_openssl_hash(algo_spec))
+ return hash;
+
+ if(!provider.empty())
+ return nullptr;
+ }
+#endif
+
+ if(provider.empty() == false && provider != "base")
+ return nullptr; // unknown provider
+
+#if defined(BOTAN_HAS_SHA1)
+ if(algo_spec == "SHA-160" ||
+ algo_spec == "SHA-1" ||
+ algo_spec == "SHA1")
+ {
+ return std::unique_ptr<HashFunction>(new SHA_160);
+ }
+#endif
+
+#if defined(BOTAN_HAS_SHA2_32)
+ if(algo_spec == "SHA-224")
+ {
+ return std::unique_ptr<HashFunction>(new SHA_224);
+ }
+
+ if(algo_spec == "SHA-256")
+ {
+ return std::unique_ptr<HashFunction>(new SHA_256);
+ }
+#endif
+
+#if defined(BOTAN_HAS_SHA2_64)
+ if(algo_spec == "SHA-384")
+ {
+ return std::unique_ptr<HashFunction>(new SHA_384);
+ }
+
+ if(algo_spec == "SHA-512")
+ {
+ return std::unique_ptr<HashFunction>(new SHA_512);
+ }
+
+ if(algo_spec == "SHA-512-256")
+ {
+ return std::unique_ptr<HashFunction>(new SHA_512_256);
+ }
+#endif
+
+#if defined(BOTAN_HAS_RIPEMD_160)
+ if(algo_spec == "RIPEMD-160")
+ {
+ return std::unique_ptr<HashFunction>(new RIPEMD_160);
+ }
+#endif
+
+#if defined(BOTAN_HAS_WHIRLPOOL)
+ if(algo_spec == "Whirlpool")
+ {
+ return std::unique_ptr<HashFunction>(new Whirlpool);
+ }
+#endif
+
+#if defined(BOTAN_HAS_MD5)
+ if(algo_spec == "MD5")
+ {
+ return std::unique_ptr<HashFunction>(new MD5);
+ }
+#endif
+
+#if defined(BOTAN_HAS_MD4)
+ if(algo_spec == "MD4")
+ {
+ return std::unique_ptr<HashFunction>(new MD4);
+ }
+#endif
+
+#if defined(BOTAN_HAS_GOST_34_11)
+ if(algo_spec == "GOST-R-34.11-94" || algo_spec == "GOST-34.11")
+ {
+ return std::unique_ptr<HashFunction>(new GOST_34_11);
+ }
+#endif
+
+#if defined(BOTAN_HAS_ADLER32)
+ if(algo_spec == "Adler32")
+ {
+ return std::unique_ptr<HashFunction>(new Adler32);
+ }
+#endif
+
+#if defined(BOTAN_HAS_CRC24)
+ if(algo_spec == "CRC24")
+ {
+ return std::unique_ptr<HashFunction>(new CRC24);
+ }
+#endif
+
+#if defined(BOTAN_HAS_CRC32)
+ if(algo_spec == "CRC32")
+ {
+ return std::unique_ptr<HashFunction>(new CRC32);
+ }
+#endif
+
+ const SCAN_Name req(algo_spec);
+
+#if defined(BOTAN_HAS_TIGER)
+ if(req.algo_name() == "Tiger")
+ {
+ return std::unique_ptr<HashFunction>(
+ new Tiger(req.arg_as_integer(0, 24),
+ req.arg_as_integer(1, 3)));
+ }
+#endif
+
+#if defined(BOTAN_HAS_SKEIN_512)
+ if(req.algo_name() == "Skein-512")
+ {
+ return std::unique_ptr<HashFunction>(
+ new Skein_512(req.arg_as_integer(0, 512), req.arg(1, "")));
+ }
+#endif
+
+#if defined(BOTAN_HAS_BLAKE2B)
+ if(req.algo_name() == "Blake2b" || req.algo_name() == "BLAKE2b")
+ {
+ return std::unique_ptr<HashFunction>(
+ new Blake2b(req.arg_as_integer(0, 512)));
+ }
+#endif
+
+#if defined(BOTAN_HAS_KECCAK)
+ if(req.algo_name() == "Keccak-1600")
+ {
+ return std::unique_ptr<HashFunction>(
+ new Keccak_1600(req.arg_as_integer(0, 512)));
+ }
+#endif
+
+#if defined(BOTAN_HAS_SHA3)
+ if(req.algo_name() == "SHA-3")
+ {
+ return std::unique_ptr<HashFunction>(
+ new SHA_3(req.arg_as_integer(0, 512)));
+ }
+#endif
+
+#if defined(BOTAN_HAS_SHAKE)
+ if(req.algo_name() == "SHAKE-128")
+ {
+ return std::unique_ptr<HashFunction>(new SHAKE_128(req.arg_as_integer(0, 128)));
+ }
+ if(req.algo_name() == "SHAKE-256")
+ {
+ return std::unique_ptr<HashFunction>(new SHAKE_256(req.arg_as_integer(0, 256)));
+ }
+#endif
+
+#if defined(BOTAN_HAS_STREEBOG)
+ if(algo_spec == "Streebog-256")
+ {
+ return std::unique_ptr<HashFunction>(new Streebog_256);
+ }
+ if(algo_spec == "Streebog-512")
+ {
+ return std::unique_ptr<HashFunction>(new Streebog_512);
+ }
+#endif
+
+#if defined(BOTAN_HAS_SM3)
+ if(algo_spec == "SM3")
+ {
+ return std::unique_ptr<HashFunction>(new SM3);
+ }
+#endif
+
+#if defined(BOTAN_HAS_WHIRLPOOL)
+ if(req.algo_name() == "Whirlpool")
+ {
+ return std::unique_ptr<HashFunction>(new Whirlpool);
+ }
+#endif
+
+#if defined(BOTAN_HAS_PARALLEL_HASH)
+ if(req.algo_name() == "Parallel")
+ {
+ std::vector<std::unique_ptr<HashFunction>> hashes;
+
+ for(size_t i = 0; i != req.arg_count(); ++i)
+ {
+ auto h = HashFunction::create(req.arg(i));
+ if(!h)
+ {
+ return nullptr;
+ }
+ hashes.push_back(std::move(h));
+ }
+
+ return std::unique_ptr<HashFunction>(new Parallel(hashes));
+ }
+#endif
+
+#if defined(BOTAN_HAS_COMB4P)
+ if(req.algo_name() == "Comb4P" && req.arg_count() == 2)
+ {
+ std::unique_ptr<HashFunction> h1(HashFunction::create(req.arg(0)));
+ std::unique_ptr<HashFunction> h2(HashFunction::create(req.arg(1)));
+
+ if(h1 && h2)
+ return std::unique_ptr<HashFunction>(new Comb4P(h1.release(), h2.release()));
+ }
+#endif
+
+
+ return nullptr;
+ }
+
+//static
+std::unique_ptr<HashFunction>
+HashFunction::create_or_throw(const std::string& algo,
+ const std::string& provider)
+ {
+ if(auto hash = HashFunction::create(algo, provider))
+ {
+ return hash;
+ }
+ throw Lookup_Error("Hash", algo, provider);
+ }
+
+std::vector<std::string> HashFunction::providers(const std::string& algo_spec)
+ {
+ return probe_providers_of<HashFunction>(algo_spec, {"base", "openssl", "commoncrypto"});
+ }
+
+}
+
diff --git a/comm/third_party/botan/src/lib/hash/hash.h b/comm/third_party/botan/src/lib/hash/hash.h
new file mode 100644
index 0000000000..8c6440e650
--- /dev/null
+++ b/comm/third_party/botan/src/lib/hash/hash.h
@@ -0,0 +1,91 @@
+/*
+* Hash Function Base Class
+* (C) 1999-2008 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_HASH_FUNCTION_BASE_CLASS_H_
+#define BOTAN_HASH_FUNCTION_BASE_CLASS_H_
+
+#include <botan/buf_comp.h>
+#include <string>
+#include <memory>
+
+namespace Botan {
+
+/**
+* This class represents hash function (message digest) objects
+*/
+class BOTAN_PUBLIC_API(2,0) HashFunction : public Buffered_Computation
+ {
+ public:
+ /**
+ * Create an instance based on a name, or return null if the
+ * algo/provider combination cannot be found. If provider is
+ * empty then best available is chosen.
+ */
+ static std::unique_ptr<HashFunction>
+ create(const std::string& algo_spec,
+ const std::string& provider = "");
+
+ /**
+ * Create an instance based on a name
+ * If provider is empty then best available is chosen.
+ * @param algo_spec algorithm name
+ * @param provider provider implementation to use
+ * Throws Lookup_Error if not found.
+ */
+ static std::unique_ptr<HashFunction>
+ create_or_throw(const std::string& algo_spec,
+ const std::string& provider = "");
+
+ /**
+ * @return list of available providers for this algorithm, empty if not available
+ * @param algo_spec algorithm name
+ */
+ static std::vector<std::string> providers(const std::string& algo_spec);
+
+ /**
+ * @return new object representing the same algorithm as *this
+ */
+ virtual HashFunction* clone() const = 0;
+
+ /**
+ * @return provider information about this implementation. Default is "base",
+ * might also return "sse2", "avx2", "openssl", or some other arbitrary string.
+ */
+ virtual std::string provider() const { return "base"; }
+
+ virtual ~HashFunction() = default;
+
+ /**
+ * Reset the state.
+ */
+ virtual void clear() = 0;
+
+ /**
+ * @return the hash function name
+ */
+ virtual std::string name() const = 0;
+
+ /**
+ * @return hash block size as defined for this algorithm
+ */
+ virtual size_t hash_block_size() const { return 0; }
+
+ /**
+ * Return a new hash object with the same state as *this. This
+ * allows computing the hash of several messages with a common
+ * prefix more efficiently than would otherwise be possible.
+ *
+ * This function should be called `clone` but that was already
+ * used for the case of returning an uninitialized object.
+ * @return new hash object
+ */
+ virtual std::unique_ptr<HashFunction> copy_state() const = 0;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/hash/info.txt b/comm/third_party/botan/src/lib/hash/info.txt
new file mode 100644
index 0000000000..8d38170589
--- /dev/null
+++ b/comm/third_party/botan/src/lib/hash/info.txt
@@ -0,0 +1,7 @@
+<defines>
+HASH -> 20180112
+</defines>
+
+<header:public>
+hash.h
+</header:public>
diff --git a/comm/third_party/botan/src/lib/hash/keccak/info.txt b/comm/third_party/botan/src/lib/hash/keccak/info.txt
new file mode 100644
index 0000000000..6f7345af87
--- /dev/null
+++ b/comm/third_party/botan/src/lib/hash/keccak/info.txt
@@ -0,0 +1,7 @@
+<defines>
+KECCAK -> 20131128
+</defines>
+
+<requires>
+sha3
+</requires>
diff --git a/comm/third_party/botan/src/lib/hash/keccak/keccak.cpp b/comm/third_party/botan/src/lib/hash/keccak/keccak.cpp
new file mode 100644
index 0000000000..b8196495c6
--- /dev/null
+++ b/comm/third_party/botan/src/lib/hash/keccak/keccak.cpp
@@ -0,0 +1,68 @@
+/*
+* Keccak
+* (C) 2010,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/keccak.h>
+#include <botan/sha3.h>
+#include <botan/exceptn.h>
+#include <botan/loadstor.h>
+
+namespace Botan {
+
+std::unique_ptr<HashFunction> Keccak_1600::copy_state() const
+ {
+ return std::unique_ptr<HashFunction>(new Keccak_1600(*this));
+ }
+
+Keccak_1600::Keccak_1600(size_t output_bits) :
+ m_output_bits(output_bits),
+ m_bitrate(1600 - 2*output_bits),
+ m_S(25),
+ m_S_pos(0)
+ {
+ // We only support the parameters for the SHA-3 proposal
+
+ if(output_bits != 224 && output_bits != 256 &&
+ output_bits != 384 && output_bits != 512)
+ throw Invalid_Argument("Keccak_1600: Invalid output length " +
+ std::to_string(output_bits));
+ }
+
+std::string Keccak_1600::name() const
+ {
+ return "Keccak-1600(" + std::to_string(m_output_bits) + ")";
+ }
+
+HashFunction* Keccak_1600::clone() const
+ {
+ return new Keccak_1600(m_output_bits);
+ }
+
+void Keccak_1600::clear()
+ {
+ zeroise(m_S);
+ m_S_pos = 0;
+ }
+
+void Keccak_1600::add_data(const uint8_t input[], size_t length)
+ {
+ m_S_pos = SHA_3::absorb(m_bitrate, m_S, m_S_pos, input, length);
+ }
+
+void Keccak_1600::final_result(uint8_t output[])
+ {
+ SHA_3::finish(m_bitrate, m_S, m_S_pos, 0x01, 0x80);
+
+ /*
+ * We never have to run the permutation again because we only support
+ * limited output lengths
+ */
+ copy_out_vec_le(output, m_output_bits/8, m_S);
+
+ clear();
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/hash/keccak/keccak.h b/comm/third_party/botan/src/lib/hash/keccak/keccak.h
new file mode 100644
index 0000000000..083d7fc5a6
--- /dev/null
+++ b/comm/third_party/botan/src/lib/hash/keccak/keccak.h
@@ -0,0 +1,51 @@
+/*
+* Keccak
+* (C) 2010 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_KECCAK_H_
+#define BOTAN_KECCAK_H_
+
+#include <botan/hash.h>
+#include <botan/secmem.h>
+#include <string>
+
+namespace Botan {
+
+BOTAN_FUTURE_INTERNAL_HEADER(keccak.h)
+
+/**
+* Keccak[1600], a SHA-3 candidate
+*/
+class BOTAN_PUBLIC_API(2,0) Keccak_1600 final : public HashFunction
+ {
+ public:
+
+ /**
+ * @param output_bits the size of the hash output; must be one of
+ * 224, 256, 384, or 512
+ */
+ explicit Keccak_1600(size_t output_bits = 512);
+
+ size_t hash_block_size() const override { return m_bitrate / 8; }
+ size_t output_length() const override { return m_output_bits / 8; }
+
+ HashFunction* clone() const override;
+ std::unique_ptr<HashFunction> copy_state() const override;
+ std::string name() const override;
+ void clear() override;
+
+ private:
+ void add_data(const uint8_t input[], size_t length) override;
+ void final_result(uint8_t out[]) override;
+
+ size_t m_output_bits, m_bitrate;
+ secure_vector<uint64_t> m_S;
+ size_t m_S_pos;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/hash/md4/info.txt b/comm/third_party/botan/src/lib/hash/md4/info.txt
new file mode 100644
index 0000000000..fde5168adf
--- /dev/null
+++ b/comm/third_party/botan/src/lib/hash/md4/info.txt
@@ -0,0 +1,7 @@
+<defines>
+MD4 -> 20131128
+</defines>
+
+<requires>
+mdx_hash
+</requires>
diff --git a/comm/third_party/botan/src/lib/hash/md4/md4.cpp b/comm/third_party/botan/src/lib/hash/md4/md4.cpp
new file mode 100644
index 0000000000..9cc92e1e38
--- /dev/null
+++ b/comm/third_party/botan/src/lib/hash/md4/md4.cpp
@@ -0,0 +1,144 @@
+/*
+* MD4
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/md4.h>
+#include <botan/loadstor.h>
+#include <botan/rotate.h>
+
+namespace Botan {
+
+std::unique_ptr<HashFunction> MD4::copy_state() const
+ {
+ return std::unique_ptr<HashFunction>(new MD4(*this));
+ }
+
+namespace {
+
+inline void FF4(uint32_t& A, uint32_t& B, uint32_t& C, uint32_t& D,
+ uint32_t M0, uint32_t M1, uint32_t M2, uint32_t M3)
+
+ {
+ A += (D ^ (B & (C ^ D))) + M0;
+ A = rotl<3>(A);
+
+ D += (C ^ (A & (B ^ C))) + M1;
+ D = rotl<7>(D);
+
+ C += (B ^ (D & (A ^ B))) + M2;
+ C = rotl<11>(C);
+
+ B += (A ^ (C & (D ^ A))) + M3;
+ B = rotl<19>(B);
+ }
+
+inline void GG4(uint32_t& A, uint32_t& B, uint32_t& C, uint32_t& D,
+ uint32_t M0, uint32_t M1, uint32_t M2, uint32_t M3)
+
+ {
+ A += ((B & C) | (D & (B | C))) + M0 + 0x5A827999;
+ A = rotl<3>(A);
+
+ D += ((A & B) | (C & (A | B))) + M1 + 0x5A827999;
+ D = rotl<5>(D);
+
+ C += ((D & A) | (B & (D | A))) + M2 + 0x5A827999;
+ C = rotl<9>(C);
+
+ B += ((C & D) | (A & (C | D))) + M3 + 0x5A827999;
+ B = rotl<13>(B);
+ }
+
+inline void HH4(uint32_t& A, uint32_t& B, uint32_t& C, uint32_t& D,
+ uint32_t M0, uint32_t M1, uint32_t M2, uint32_t M3)
+
+ {
+ A += (B ^ C ^ D) + M0 + 0x6ED9EBA1;
+ A = rotl<3>(A);
+
+ D += (A ^ B ^ C) + M1 + 0x6ED9EBA1;
+ D = rotl<9>(D);
+
+ C += (A ^ B ^ D) + M2 + 0x6ED9EBA1;
+ C = rotl<11>(C);
+
+ B += (A ^ C ^ D) + M3 + 0x6ED9EBA1;
+ B = rotl<15>(B);
+ }
+
+}
+
+/*
+* MD4 Compression Function
+*/
+void MD4::compress_n(const uint8_t input[], size_t blocks)
+ {
+ uint32_t A = m_digest[0], B = m_digest[1], C = m_digest[2], D = m_digest[3];
+
+ for(size_t i = 0; i != blocks; ++i)
+ {
+ uint32_t M00 = load_le<uint32_t>(input, 0);
+ uint32_t M01 = load_le<uint32_t>(input, 1);
+ uint32_t M02 = load_le<uint32_t>(input, 2);
+ uint32_t M03 = load_le<uint32_t>(input, 3);
+ uint32_t M04 = load_le<uint32_t>(input, 4);
+ uint32_t M05 = load_le<uint32_t>(input, 5);
+ uint32_t M06 = load_le<uint32_t>(input, 6);
+ uint32_t M07 = load_le<uint32_t>(input, 7);
+ uint32_t M08 = load_le<uint32_t>(input, 8);
+ uint32_t M09 = load_le<uint32_t>(input, 9);
+ uint32_t M10 = load_le<uint32_t>(input, 10);
+ uint32_t M11 = load_le<uint32_t>(input, 11);
+ uint32_t M12 = load_le<uint32_t>(input, 12);
+ uint32_t M13 = load_le<uint32_t>(input, 13);
+ uint32_t M14 = load_le<uint32_t>(input, 14);
+ uint32_t M15 = load_le<uint32_t>(input, 15);
+
+ FF4(A, B, C, D, M00, M01, M02, M03);
+ FF4(A, B, C, D, M04, M05, M06, M07);
+ FF4(A, B, C, D, M08, M09, M10, M11);
+ FF4(A, B, C, D, M12, M13, M14, M15);
+
+ GG4(A, B, C, D, M00, M04, M08, M12);
+ GG4(A, B, C, D, M01, M05, M09, M13);
+ GG4(A, B, C, D, M02, M06, M10, M14);
+ GG4(A, B, C, D, M03, M07, M11, M15);
+
+ HH4(A, B, C, D, M00, M08, M04, M12);
+ HH4(A, B, C, D, M02, M10, M06, M14);
+ HH4(A, B, C, D, M01, M09, M05, M13);
+ HH4(A, B, C, D, M03, M11, M07, M15);
+
+ A = (m_digest[0] += A);
+ B = (m_digest[1] += B);
+ C = (m_digest[2] += C);
+ D = (m_digest[3] += D);
+
+ input += hash_block_size();
+ }
+ }
+
+/*
+* Copy out the digest
+*/
+void MD4::copy_out(uint8_t output[])
+ {
+ copy_out_vec_le(output, output_length(), m_digest);
+ }
+
+/*
+* Clear memory of sensitive data
+*/
+void MD4::clear()
+ {
+ MDx_HashFunction::clear();
+ m_digest[0] = 0x67452301;
+ m_digest[1] = 0xEFCDAB89;
+ m_digest[2] = 0x98BADCFE;
+ m_digest[3] = 0x10325476;
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/hash/md4/md4.h b/comm/third_party/botan/src/lib/hash/md4/md4.h
new file mode 100644
index 0000000000..0e6f5dc080
--- /dev/null
+++ b/comm/third_party/botan/src/lib/hash/md4/md4.h
@@ -0,0 +1,45 @@
+/*
+* MD4
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_MD4_H_
+#define BOTAN_MD4_H_
+
+#include <botan/mdx_hash.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(md4.h)
+
+namespace Botan {
+
+/**
+* MD4
+*/
+class BOTAN_PUBLIC_API(2,0) MD4 final : public MDx_HashFunction
+ {
+ public:
+ std::string name() const override { return "MD4"; }
+ size_t output_length() const override { return 16; }
+ HashFunction* clone() const override { return new MD4; }
+ std::unique_ptr<HashFunction> copy_state() const override;
+
+ void clear() override;
+
+ MD4() : MDx_HashFunction(64, false, true), m_digest(4)
+ { clear(); }
+
+ private:
+ void compress_n(const uint8_t input[], size_t blocks) override;
+ void copy_out(uint8_t[]) override;
+
+ /**
+ * The digest value
+ */
+ secure_vector<uint32_t> m_digest;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/hash/md5/info.txt b/comm/third_party/botan/src/lib/hash/md5/info.txt
new file mode 100644
index 0000000000..5e0aadec88
--- /dev/null
+++ b/comm/third_party/botan/src/lib/hash/md5/info.txt
@@ -0,0 +1,7 @@
+<defines>
+MD5 -> 20131128
+</defines>
+
+<requires>
+mdx_hash
+</requires>
diff --git a/comm/third_party/botan/src/lib/hash/md5/md5.cpp b/comm/third_party/botan/src/lib/hash/md5/md5.cpp
new file mode 100644
index 0000000000..590af4641f
--- /dev/null
+++ b/comm/third_party/botan/src/lib/hash/md5/md5.cpp
@@ -0,0 +1,140 @@
+/*
+* MD5
+* (C) 1999-2008 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/md5.h>
+#include <botan/loadstor.h>
+#include <botan/rotate.h>
+
+namespace Botan {
+
+std::unique_ptr<HashFunction> MD5::copy_state() const
+ {
+ return std::unique_ptr<HashFunction>(new MD5(*this));
+ }
+
+namespace {
+
+/*
+* MD5 FF Function
+*/
+template<size_t S>
+inline void FF(uint32_t& A, uint32_t B, uint32_t C, uint32_t D, uint32_t M)
+ {
+ A += (D ^ (B & (C ^ D))) + M;
+ A = rotl<S>(A) + B;
+ }
+
+/*
+* MD5 GG Function
+*/
+template<size_t S>
+inline void GG(uint32_t& A, uint32_t B, uint32_t C, uint32_t D, uint32_t M)
+ {
+ A += (C ^ (D & (B ^ C))) + M;
+ A = rotl<S>(A) + B;
+ }
+
+/*
+* MD5 HH Function
+*/
+template<size_t S>
+inline void HH(uint32_t& A, uint32_t B, uint32_t C, uint32_t D, uint32_t M)
+ {
+ A += (B ^ C ^ D) + M;
+ A = rotl<S>(A) + B;
+ }
+
+/*
+* MD5 II Function
+*/
+template<size_t S>
+inline void II(uint32_t& A, uint32_t B, uint32_t C, uint32_t D, uint32_t M)
+ {
+ A += (C ^ (B | ~D)) + M;
+ A = rotl<S>(A) + B;
+ }
+
+}
+
+/*
+* MD5 Compression Function
+*/
+void MD5::compress_n(const uint8_t input[], size_t blocks)
+ {
+ uint32_t A = m_digest[0], B = m_digest[1], C = m_digest[2], D = m_digest[3];
+
+ for(size_t i = 0; i != blocks; ++i)
+ {
+ load_le(m_M.data(), input, m_M.size());
+
+ FF< 7>(A,B,C,D,m_M[ 0]+0xD76AA478); FF<12>(D,A,B,C,m_M[ 1]+0xE8C7B756);
+ FF<17>(C,D,A,B,m_M[ 2]+0x242070DB); FF<22>(B,C,D,A,m_M[ 3]+0xC1BDCEEE);
+ FF< 7>(A,B,C,D,m_M[ 4]+0xF57C0FAF); FF<12>(D,A,B,C,m_M[ 5]+0x4787C62A);
+ FF<17>(C,D,A,B,m_M[ 6]+0xA8304613); FF<22>(B,C,D,A,m_M[ 7]+0xFD469501);
+ FF< 7>(A,B,C,D,m_M[ 8]+0x698098D8); FF<12>(D,A,B,C,m_M[ 9]+0x8B44F7AF);
+ FF<17>(C,D,A,B,m_M[10]+0xFFFF5BB1); FF<22>(B,C,D,A,m_M[11]+0x895CD7BE);
+ FF< 7>(A,B,C,D,m_M[12]+0x6B901122); FF<12>(D,A,B,C,m_M[13]+0xFD987193);
+ FF<17>(C,D,A,B,m_M[14]+0xA679438E); FF<22>(B,C,D,A,m_M[15]+0x49B40821);
+
+ GG< 5>(A,B,C,D,m_M[ 1]+0xF61E2562); GG< 9>(D,A,B,C,m_M[ 6]+0xC040B340);
+ GG<14>(C,D,A,B,m_M[11]+0x265E5A51); GG<20>(B,C,D,A,m_M[ 0]+0xE9B6C7AA);
+ GG< 5>(A,B,C,D,m_M[ 5]+0xD62F105D); GG< 9>(D,A,B,C,m_M[10]+0x02441453);
+ GG<14>(C,D,A,B,m_M[15]+0xD8A1E681); GG<20>(B,C,D,A,m_M[ 4]+0xE7D3FBC8);
+ GG< 5>(A,B,C,D,m_M[ 9]+0x21E1CDE6); GG< 9>(D,A,B,C,m_M[14]+0xC33707D6);
+ GG<14>(C,D,A,B,m_M[ 3]+0xF4D50D87); GG<20>(B,C,D,A,m_M[ 8]+0x455A14ED);
+ GG< 5>(A,B,C,D,m_M[13]+0xA9E3E905); GG< 9>(D,A,B,C,m_M[ 2]+0xFCEFA3F8);
+ GG<14>(C,D,A,B,m_M[ 7]+0x676F02D9); GG<20>(B,C,D,A,m_M[12]+0x8D2A4C8A);
+
+ HH< 4>(A,B,C,D,m_M[ 5]+0xFFFA3942); HH<11>(D,A,B,C,m_M[ 8]+0x8771F681);
+ HH<16>(C,D,A,B,m_M[11]+0x6D9D6122); HH<23>(B,C,D,A,m_M[14]+0xFDE5380C);
+ HH< 4>(A,B,C,D,m_M[ 1]+0xA4BEEA44); HH<11>(D,A,B,C,m_M[ 4]+0x4BDECFA9);
+ HH<16>(C,D,A,B,m_M[ 7]+0xF6BB4B60); HH<23>(B,C,D,A,m_M[10]+0xBEBFBC70);
+ HH< 4>(A,B,C,D,m_M[13]+0x289B7EC6); HH<11>(D,A,B,C,m_M[ 0]+0xEAA127FA);
+ HH<16>(C,D,A,B,m_M[ 3]+0xD4EF3085); HH<23>(B,C,D,A,m_M[ 6]+0x04881D05);
+ HH< 4>(A,B,C,D,m_M[ 9]+0xD9D4D039); HH<11>(D,A,B,C,m_M[12]+0xE6DB99E5);
+ HH<16>(C,D,A,B,m_M[15]+0x1FA27CF8); HH<23>(B,C,D,A,m_M[ 2]+0xC4AC5665);
+
+ II< 6>(A,B,C,D,m_M[ 0]+0xF4292244); II<10>(D,A,B,C,m_M[ 7]+0x432AFF97);
+ II<15>(C,D,A,B,m_M[14]+0xAB9423A7); II<21>(B,C,D,A,m_M[ 5]+0xFC93A039);
+ II< 6>(A,B,C,D,m_M[12]+0x655B59C3); II<10>(D,A,B,C,m_M[ 3]+0x8F0CCC92);
+ II<15>(C,D,A,B,m_M[10]+0xFFEFF47D); II<21>(B,C,D,A,m_M[ 1]+0x85845DD1);
+ II< 6>(A,B,C,D,m_M[ 8]+0x6FA87E4F); II<10>(D,A,B,C,m_M[15]+0xFE2CE6E0);
+ II<15>(C,D,A,B,m_M[ 6]+0xA3014314); II<21>(B,C,D,A,m_M[13]+0x4E0811A1);
+ II< 6>(A,B,C,D,m_M[ 4]+0xF7537E82); II<10>(D,A,B,C,m_M[11]+0xBD3AF235);
+ II<15>(C,D,A,B,m_M[ 2]+0x2AD7D2BB); II<21>(B,C,D,A,m_M[ 9]+0xEB86D391);
+
+ A = (m_digest[0] += A);
+ B = (m_digest[1] += B);
+ C = (m_digest[2] += C);
+ D = (m_digest[3] += D);
+
+ input += hash_block_size();
+ }
+ }
+
+/*
+* Copy out the digest
+*/
+void MD5::copy_out(uint8_t output[])
+ {
+ copy_out_vec_le(output, output_length(), m_digest);
+ }
+
+/*
+* Clear memory of sensitive data
+*/
+void MD5::clear()
+ {
+ MDx_HashFunction::clear();
+ zeroise(m_M);
+ m_digest[0] = 0x67452301;
+ m_digest[1] = 0xEFCDAB89;
+ m_digest[2] = 0x98BADCFE;
+ m_digest[3] = 0x10325476;
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/hash/md5/md5.h b/comm/third_party/botan/src/lib/hash/md5/md5.h
new file mode 100644
index 0000000000..182145523d
--- /dev/null
+++ b/comm/third_party/botan/src/lib/hash/md5/md5.h
@@ -0,0 +1,50 @@
+/*
+* MD5
+* (C) 1999-2008 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_MD5_H_
+#define BOTAN_MD5_H_
+
+#include <botan/mdx_hash.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(md5.h)
+
+namespace Botan {
+
+/**
+* MD5
+*/
+class BOTAN_PUBLIC_API(2,0) MD5 final : public MDx_HashFunction
+ {
+ public:
+ std::string name() const override { return "MD5"; }
+ size_t output_length() const override { return 16; }
+ HashFunction* clone() const override { return new MD5; }
+ std::unique_ptr<HashFunction> copy_state() const override;
+
+ void clear() override;
+
+ MD5() : MDx_HashFunction(64, false, true), m_M(16), m_digest(4)
+ { clear(); }
+
+ private:
+ void compress_n(const uint8_t[], size_t blocks) override;
+ void copy_out(uint8_t[]) override;
+
+ /**
+ * The message buffer
+ */
+ secure_vector<uint32_t> m_M;
+
+ /**
+ * The digest value
+ */
+ secure_vector<uint32_t> m_digest;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/hash/mdx_hash/info.txt b/comm/third_party/botan/src/lib/hash/mdx_hash/info.txt
new file mode 100644
index 0000000000..6a509f1be9
--- /dev/null
+++ b/comm/third_party/botan/src/lib/hash/mdx_hash/info.txt
@@ -0,0 +1,5 @@
+<defines>
+MDX_HASH_FUNCTION -> 20131128
+</defines>
+
+load_on dep
diff --git a/comm/third_party/botan/src/lib/hash/mdx_hash/mdx_hash.cpp b/comm/third_party/botan/src/lib/hash/mdx_hash/mdx_hash.cpp
new file mode 100644
index 0000000000..64ae516a82
--- /dev/null
+++ b/comm/third_party/botan/src/lib/hash/mdx_hash/mdx_hash.cpp
@@ -0,0 +1,121 @@
+/*
+* Merkle-Damgard Hash Function
+* (C) 1999-2008,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/mdx_hash.h>
+#include <botan/exceptn.h>
+#include <botan/loadstor.h>
+#include <botan/internal/bit_ops.h>
+
+namespace Botan {
+
+/*
+* MDx_HashFunction Constructor
+*/
+MDx_HashFunction::MDx_HashFunction(size_t block_len,
+ bool byte_big_endian,
+ bool bit_big_endian,
+ uint8_t cnt_size) :
+ m_pad_char(bit_big_endian == true ? 0x80 : 0x01),
+ m_counter_size(cnt_size),
+ m_block_bits(ceil_log2(block_len)),
+ m_count_big_endian(byte_big_endian),
+ m_count(0),
+ m_buffer(block_len),
+ m_position(0)
+ {
+ if(!is_power_of_2(block_len))
+ throw Invalid_Argument("MDx_HashFunction block length must be a power of 2");
+ if(m_block_bits < 3 || m_block_bits > 16)
+ throw Invalid_Argument("MDx_HashFunction block size too large or too small");
+ if(m_counter_size < 8 || m_counter_size > block_len)
+ throw Invalid_State("MDx_HashFunction invalid counter length");
+ }
+
+/*
+* Clear memory of sensitive data
+*/
+void MDx_HashFunction::clear()
+ {
+ zeroise(m_buffer);
+ m_count = m_position = 0;
+ }
+
+/*
+* Update the hash
+*/
+void MDx_HashFunction::add_data(const uint8_t input[], size_t length)
+ {
+ const size_t block_len = static_cast<size_t>(1) << m_block_bits;
+
+ m_count += length;
+
+ if(m_position)
+ {
+ buffer_insert(m_buffer, m_position, input, length);
+
+ if(m_position + length >= block_len)
+ {
+ compress_n(m_buffer.data(), 1);
+ input += (block_len - m_position);
+ length -= (block_len - m_position);
+ m_position = 0;
+ }
+ }
+
+ // Just in case the compiler can't figure out block_len is a power of 2
+ const size_t full_blocks = length >> m_block_bits;
+ const size_t remaining = length & (block_len - 1);
+
+ if(full_blocks > 0)
+ {
+ compress_n(input, full_blocks);
+ }
+
+ buffer_insert(m_buffer, m_position, input + full_blocks * block_len, remaining);
+ m_position += remaining;
+ }
+
+/*
+* Finalize a hash
+*/
+void MDx_HashFunction::final_result(uint8_t output[])
+ {
+ const size_t block_len = static_cast<size_t>(1) << m_block_bits;
+
+ clear_mem(&m_buffer[m_position], block_len - m_position);
+ m_buffer[m_position] = m_pad_char;
+
+ if(m_position >= block_len - m_counter_size)
+ {
+ compress_n(m_buffer.data(), 1);
+ zeroise(m_buffer);
+ }
+
+ write_count(&m_buffer[block_len - m_counter_size]);
+
+ compress_n(m_buffer.data(), 1);
+ copy_out(output);
+ clear();
+ }
+
+/*
+* Write the count bits to the buffer
+*/
+void MDx_HashFunction::write_count(uint8_t out[])
+ {
+ BOTAN_ASSERT_NOMSG(m_counter_size <= output_length());
+ BOTAN_ASSERT_NOMSG(m_counter_size >= 8);
+
+ const uint64_t bit_count = m_count * 8;
+
+ if(m_count_big_endian)
+ store_be(bit_count, out + m_counter_size - 8);
+ else
+ store_le(bit_count, out + m_counter_size - 8);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/hash/mdx_hash/mdx_hash.h b/comm/third_party/botan/src/lib/hash/mdx_hash/mdx_hash.h
new file mode 100644
index 0000000000..a061e9c8a3
--- /dev/null
+++ b/comm/third_party/botan/src/lib/hash/mdx_hash/mdx_hash.h
@@ -0,0 +1,73 @@
+/*
+* MDx Hash Function
+* (C) 1999-2008 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_MDX_BASE_H_
+#define BOTAN_MDX_BASE_H_
+
+#include <botan/hash.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(mdx_hash.h)
+
+namespace Botan {
+
+/**
+* MDx Hash Function Base Class
+*/
+class BOTAN_PUBLIC_API(2,0) MDx_HashFunction : public HashFunction
+ {
+ public:
+ /**
+ * @param block_length is the number of bytes per block, which must
+ * be a power of 2 and at least 8.
+ * @param big_byte_endian specifies if the hash uses big-endian bytes
+ * @param big_bit_endian specifies if the hash uses big-endian bits
+ * @param counter_size specifies the size of the counter var in bytes
+ */
+ MDx_HashFunction(size_t block_length,
+ bool big_byte_endian,
+ bool big_bit_endian,
+ uint8_t counter_size = 8);
+
+ size_t hash_block_size() const override final { return m_buffer.size(); }
+ protected:
+ void add_data(const uint8_t input[], size_t length) override final;
+ void final_result(uint8_t output[]) override final;
+
+ /**
+ * Run the hash's compression function over a set of blocks
+ * @param blocks the input
+ * @param block_n the number of blocks
+ */
+ virtual void compress_n(const uint8_t blocks[], size_t block_n) = 0;
+
+ void clear() override;
+
+ /**
+ * Copy the output to the buffer
+ * @param buffer to put the output into
+ */
+ virtual void copy_out(uint8_t buffer[]) = 0;
+
+ /**
+ * Write the count, if used, to this spot
+ * @param out where to write the counter to
+ */
+ virtual void write_count(uint8_t out[]);
+ private:
+ const uint8_t m_pad_char;
+ const uint8_t m_counter_size;
+ const uint8_t m_block_bits;
+ const bool m_count_big_endian;
+
+ uint64_t m_count;
+ secure_vector<uint8_t> m_buffer;
+ size_t m_position;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/hash/par_hash/info.txt b/comm/third_party/botan/src/lib/hash/par_hash/info.txt
new file mode 100644
index 0000000000..b04e0f4df1
--- /dev/null
+++ b/comm/third_party/botan/src/lib/hash/par_hash/info.txt
@@ -0,0 +1,3 @@
+<defines>
+PARALLEL_HASH -> 20131128
+</defines>
diff --git a/comm/third_party/botan/src/lib/hash/par_hash/par_hash.cpp b/comm/third_party/botan/src/lib/hash/par_hash/par_hash.cpp
new file mode 100644
index 0000000000..a0297dfe85
--- /dev/null
+++ b/comm/third_party/botan/src/lib/hash/par_hash/par_hash.cpp
@@ -0,0 +1,86 @@
+/*
+* Parallel Hash
+* (C) 1999-2009 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/par_hash.h>
+#include <botan/parsing.h>
+
+namespace Botan {
+
+void Parallel::add_data(const uint8_t input[], size_t length)
+ {
+ for(auto&& hash : m_hashes)
+ hash->update(input, length);
+ }
+
+void Parallel::final_result(uint8_t out[])
+ {
+ size_t offset = 0;
+
+ for(auto&& hash : m_hashes)
+ {
+ hash->final(out + offset);
+ offset += hash->output_length();
+ }
+ }
+
+size_t Parallel::output_length() const
+ {
+ size_t sum = 0;
+
+ for(auto&& hash : m_hashes)
+ sum += hash->output_length();
+ return sum;
+ }
+
+std::string Parallel::name() const
+ {
+ std::vector<std::string> names;
+
+ for(auto&& hash : m_hashes)
+ names.push_back(hash->name());
+
+ return "Parallel(" + string_join(names, ',') + ")";
+ }
+
+HashFunction* Parallel::clone() const
+ {
+ std::vector<std::unique_ptr<HashFunction>> hash_copies;
+
+ for(auto&& hash : m_hashes)
+ hash_copies.push_back(std::unique_ptr<HashFunction>(hash->clone()));
+
+ return new Parallel(hash_copies);
+ }
+
+std::unique_ptr<HashFunction> Parallel::copy_state() const
+ {
+ std::vector<std::unique_ptr<HashFunction>> hash_clones;
+
+ for(const std::unique_ptr<HashFunction>& hash : m_hashes)
+ {
+ hash_clones.push_back(hash->copy_state());
+ }
+
+ return std::unique_ptr<HashFunction>(new Parallel(hash_clones));
+ }
+
+void Parallel::clear()
+ {
+ for(auto&& hash : m_hashes)
+ hash->clear();
+ }
+
+Parallel::Parallel(std::vector<std::unique_ptr<HashFunction>>& h)
+ {
+ for(size_t i = 0; i != h.size(); ++i)
+ {
+ m_hashes.push_back(std::unique_ptr<HashFunction>(h[i].release()));
+ }
+ }
+
+
+}
diff --git a/comm/third_party/botan/src/lib/hash/par_hash/par_hash.h b/comm/third_party/botan/src/lib/hash/par_hash/par_hash.h
new file mode 100644
index 0000000000..8942cfe4db
--- /dev/null
+++ b/comm/third_party/botan/src/lib/hash/par_hash/par_hash.h
@@ -0,0 +1,50 @@
+/*
+* Parallel Hash
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_PARALLEL_HASH_H_
+#define BOTAN_PARALLEL_HASH_H_
+
+#include <botan/hash.h>
+#include <vector>
+
+BOTAN_FUTURE_INTERNAL_HEADER(par_hash.h)
+
+namespace Botan {
+
+/**
+* Parallel Hashes
+*/
+class BOTAN_PUBLIC_API(2,0) Parallel final : public HashFunction
+ {
+ public:
+ void clear() override;
+ std::string name() const override;
+ HashFunction* clone() const override;
+ std::unique_ptr<HashFunction> copy_state() const override;
+
+ size_t output_length() const override;
+
+ /**
+ * @param hashes a set of hashes to compute in parallel
+ * Takes ownership of all pointers
+ */
+ explicit Parallel(std::vector<std::unique_ptr<HashFunction>>& hashes);
+
+ Parallel(const Parallel&) = delete;
+ Parallel& operator=(const Parallel&) = delete;
+ private:
+ Parallel() = delete;
+
+ void add_data(const uint8_t[], size_t) override;
+ void final_result(uint8_t[]) override;
+
+ std::vector<std::unique_ptr<HashFunction>> m_hashes;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/hash/rmd160/info.txt b/comm/third_party/botan/src/lib/hash/rmd160/info.txt
new file mode 100644
index 0000000000..53e2f993d9
--- /dev/null
+++ b/comm/third_party/botan/src/lib/hash/rmd160/info.txt
@@ -0,0 +1,7 @@
+<defines>
+RIPEMD_160 -> 20131128
+</defines>
+
+<requires>
+mdx_hash
+</requires>
diff --git a/comm/third_party/botan/src/lib/hash/rmd160/rmd160.cpp b/comm/third_party/botan/src/lib/hash/rmd160/rmd160.cpp
new file mode 100644
index 0000000000..2fa8f84d0d
--- /dev/null
+++ b/comm/third_party/botan/src/lib/hash/rmd160/rmd160.cpp
@@ -0,0 +1,221 @@
+/*
+* RIPEMD-160
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/rmd160.h>
+#include <botan/loadstor.h>
+#include <botan/rotate.h>
+
+namespace Botan {
+
+std::unique_ptr<HashFunction> RIPEMD_160::copy_state() const
+ {
+ return std::unique_ptr<HashFunction>(new RIPEMD_160(*this));
+ }
+
+namespace {
+
+/*
+* RIPEMD-160 F1 Function
+*/
+template<size_t S>
+inline void F1(uint32_t& A, uint32_t B, uint32_t& C, uint32_t D, uint32_t E,
+ uint32_t M)
+ {
+ A += (B ^ C ^ D) + M;
+ A = rotl<S>(A) + E;
+ C = rotl<10>(C);
+ }
+
+/*
+* RIPEMD-160 F2 Function
+*/
+template<size_t S>
+inline void F2(uint32_t& A, uint32_t B, uint32_t& C, uint32_t D, uint32_t E,
+ uint32_t M)
+ {
+ A += (D ^ (B & (C ^ D))) + M;
+ A = rotl<S>(A) + E;
+ C = rotl<10>(C);
+ }
+
+/*
+* RIPEMD-160 F3 Function
+*/
+template<size_t S>
+inline void F3(uint32_t& A, uint32_t B, uint32_t& C, uint32_t D, uint32_t E,
+ uint32_t M)
+ {
+ A += (D ^ (B | ~C)) + M;
+ A = rotl<S>(A) + E;
+ C = rotl<10>(C);
+ }
+
+/*
+* RIPEMD-160 F4 Function
+*/
+template<size_t S>
+inline void F4(uint32_t& A, uint32_t B, uint32_t& C, uint32_t D, uint32_t E,
+ uint32_t M)
+ {
+ A += (C ^ (D & (B ^ C))) + M;
+ A = rotl<S>(A) + E;
+ C = rotl<10>(C);
+ }
+
+/*
+* RIPEMD-160 F5 Function
+*/
+template<size_t S>
+inline void F5(uint32_t& A, uint32_t B, uint32_t& C, uint32_t D, uint32_t E,
+ uint32_t M)
+ {
+ A += (B ^ (C | ~D)) + M;
+ A = rotl<S>(A) + E;
+ C = rotl<10>(C);
+ }
+
+}
+
+/*
+* RIPEMD-160 Compression Function
+*/
+void RIPEMD_160::compress_n(const uint8_t input[], size_t blocks)
+ {
+ const uint32_t MAGIC2 = 0x5A827999, MAGIC3 = 0x6ED9EBA1,
+ MAGIC4 = 0x8F1BBCDC, MAGIC5 = 0xA953FD4E,
+ MAGIC6 = 0x50A28BE6, MAGIC7 = 0x5C4DD124,
+ MAGIC8 = 0x6D703EF3, MAGIC9 = 0x7A6D76E9;
+
+ for(size_t i = 0; i != blocks; ++i)
+ {
+ load_le(m_M.data(), input, m_M.size());
+
+ uint32_t A1 = m_digest[0], A2 = A1,
+ B1 = m_digest[1], B2 = B1,
+ C1 = m_digest[2], C2 = C1,
+ D1 = m_digest[3], D2 = D1,
+ E1 = m_digest[4], E2 = E1;
+
+ F1<11>(A1,B1,C1,D1,E1,m_M[ 0] ); F5< 8>(A2,B2,C2,D2,E2,m_M[ 5]+MAGIC6);
+ F1<14>(E1,A1,B1,C1,D1,m_M[ 1] ); F5< 9>(E2,A2,B2,C2,D2,m_M[14]+MAGIC6);
+ F1<15>(D1,E1,A1,B1,C1,m_M[ 2] ); F5< 9>(D2,E2,A2,B2,C2,m_M[ 7]+MAGIC6);
+ F1<12>(C1,D1,E1,A1,B1,m_M[ 3] ); F5<11>(C2,D2,E2,A2,B2,m_M[ 0]+MAGIC6);
+ F1< 5>(B1,C1,D1,E1,A1,m_M[ 4] ); F5<13>(B2,C2,D2,E2,A2,m_M[ 9]+MAGIC6);
+ F1< 8>(A1,B1,C1,D1,E1,m_M[ 5] ); F5<15>(A2,B2,C2,D2,E2,m_M[ 2]+MAGIC6);
+ F1< 7>(E1,A1,B1,C1,D1,m_M[ 6] ); F5<15>(E2,A2,B2,C2,D2,m_M[11]+MAGIC6);
+ F1< 9>(D1,E1,A1,B1,C1,m_M[ 7] ); F5< 5>(D2,E2,A2,B2,C2,m_M[ 4]+MAGIC6);
+ F1<11>(C1,D1,E1,A1,B1,m_M[ 8] ); F5< 7>(C2,D2,E2,A2,B2,m_M[13]+MAGIC6);
+ F1<13>(B1,C1,D1,E1,A1,m_M[ 9] ); F5< 7>(B2,C2,D2,E2,A2,m_M[ 6]+MAGIC6);
+ F1<14>(A1,B1,C1,D1,E1,m_M[10] ); F5< 8>(A2,B2,C2,D2,E2,m_M[15]+MAGIC6);
+ F1<15>(E1,A1,B1,C1,D1,m_M[11] ); F5<11>(E2,A2,B2,C2,D2,m_M[ 8]+MAGIC6);
+ F1< 6>(D1,E1,A1,B1,C1,m_M[12] ); F5<14>(D2,E2,A2,B2,C2,m_M[ 1]+MAGIC6);
+ F1< 7>(C1,D1,E1,A1,B1,m_M[13] ); F5<14>(C2,D2,E2,A2,B2,m_M[10]+MAGIC6);
+ F1< 9>(B1,C1,D1,E1,A1,m_M[14] ); F5<12>(B2,C2,D2,E2,A2,m_M[ 3]+MAGIC6);
+ F1< 8>(A1,B1,C1,D1,E1,m_M[15] ); F5< 6>(A2,B2,C2,D2,E2,m_M[12]+MAGIC6);
+
+ F2< 7>(E1,A1,B1,C1,D1,m_M[ 7]+MAGIC2); F4< 9>(E2,A2,B2,C2,D2,m_M[ 6]+MAGIC7);
+ F2< 6>(D1,E1,A1,B1,C1,m_M[ 4]+MAGIC2); F4<13>(D2,E2,A2,B2,C2,m_M[11]+MAGIC7);
+ F2< 8>(C1,D1,E1,A1,B1,m_M[13]+MAGIC2); F4<15>(C2,D2,E2,A2,B2,m_M[ 3]+MAGIC7);
+ F2<13>(B1,C1,D1,E1,A1,m_M[ 1]+MAGIC2); F4< 7>(B2,C2,D2,E2,A2,m_M[ 7]+MAGIC7);
+ F2<11>(A1,B1,C1,D1,E1,m_M[10]+MAGIC2); F4<12>(A2,B2,C2,D2,E2,m_M[ 0]+MAGIC7);
+ F2< 9>(E1,A1,B1,C1,D1,m_M[ 6]+MAGIC2); F4< 8>(E2,A2,B2,C2,D2,m_M[13]+MAGIC7);
+ F2< 7>(D1,E1,A1,B1,C1,m_M[15]+MAGIC2); F4< 9>(D2,E2,A2,B2,C2,m_M[ 5]+MAGIC7);
+ F2<15>(C1,D1,E1,A1,B1,m_M[ 3]+MAGIC2); F4<11>(C2,D2,E2,A2,B2,m_M[10]+MAGIC7);
+ F2< 7>(B1,C1,D1,E1,A1,m_M[12]+MAGIC2); F4< 7>(B2,C2,D2,E2,A2,m_M[14]+MAGIC7);
+ F2<12>(A1,B1,C1,D1,E1,m_M[ 0]+MAGIC2); F4< 7>(A2,B2,C2,D2,E2,m_M[15]+MAGIC7);
+ F2<15>(E1,A1,B1,C1,D1,m_M[ 9]+MAGIC2); F4<12>(E2,A2,B2,C2,D2,m_M[ 8]+MAGIC7);
+ F2< 9>(D1,E1,A1,B1,C1,m_M[ 5]+MAGIC2); F4< 7>(D2,E2,A2,B2,C2,m_M[12]+MAGIC7);
+ F2<11>(C1,D1,E1,A1,B1,m_M[ 2]+MAGIC2); F4< 6>(C2,D2,E2,A2,B2,m_M[ 4]+MAGIC7);
+ F2< 7>(B1,C1,D1,E1,A1,m_M[14]+MAGIC2); F4<15>(B2,C2,D2,E2,A2,m_M[ 9]+MAGIC7);
+ F2<13>(A1,B1,C1,D1,E1,m_M[11]+MAGIC2); F4<13>(A2,B2,C2,D2,E2,m_M[ 1]+MAGIC7);
+ F2<12>(E1,A1,B1,C1,D1,m_M[ 8]+MAGIC2); F4<11>(E2,A2,B2,C2,D2,m_M[ 2]+MAGIC7);
+
+ F3<11>(D1,E1,A1,B1,C1,m_M[ 3]+MAGIC3); F3< 9>(D2,E2,A2,B2,C2,m_M[15]+MAGIC8);
+ F3<13>(C1,D1,E1,A1,B1,m_M[10]+MAGIC3); F3< 7>(C2,D2,E2,A2,B2,m_M[ 5]+MAGIC8);
+ F3< 6>(B1,C1,D1,E1,A1,m_M[14]+MAGIC3); F3<15>(B2,C2,D2,E2,A2,m_M[ 1]+MAGIC8);
+ F3< 7>(A1,B1,C1,D1,E1,m_M[ 4]+MAGIC3); F3<11>(A2,B2,C2,D2,E2,m_M[ 3]+MAGIC8);
+ F3<14>(E1,A1,B1,C1,D1,m_M[ 9]+MAGIC3); F3< 8>(E2,A2,B2,C2,D2,m_M[ 7]+MAGIC8);
+ F3< 9>(D1,E1,A1,B1,C1,m_M[15]+MAGIC3); F3< 6>(D2,E2,A2,B2,C2,m_M[14]+MAGIC8);
+ F3<13>(C1,D1,E1,A1,B1,m_M[ 8]+MAGIC3); F3< 6>(C2,D2,E2,A2,B2,m_M[ 6]+MAGIC8);
+ F3<15>(B1,C1,D1,E1,A1,m_M[ 1]+MAGIC3); F3<14>(B2,C2,D2,E2,A2,m_M[ 9]+MAGIC8);
+ F3<14>(A1,B1,C1,D1,E1,m_M[ 2]+MAGIC3); F3<12>(A2,B2,C2,D2,E2,m_M[11]+MAGIC8);
+ F3< 8>(E1,A1,B1,C1,D1,m_M[ 7]+MAGIC3); F3<13>(E2,A2,B2,C2,D2,m_M[ 8]+MAGIC8);
+ F3<13>(D1,E1,A1,B1,C1,m_M[ 0]+MAGIC3); F3< 5>(D2,E2,A2,B2,C2,m_M[12]+MAGIC8);
+ F3< 6>(C1,D1,E1,A1,B1,m_M[ 6]+MAGIC3); F3<14>(C2,D2,E2,A2,B2,m_M[ 2]+MAGIC8);
+ F3< 5>(B1,C1,D1,E1,A1,m_M[13]+MAGIC3); F3<13>(B2,C2,D2,E2,A2,m_M[10]+MAGIC8);
+ F3<12>(A1,B1,C1,D1,E1,m_M[11]+MAGIC3); F3<13>(A2,B2,C2,D2,E2,m_M[ 0]+MAGIC8);
+ F3< 7>(E1,A1,B1,C1,D1,m_M[ 5]+MAGIC3); F3< 7>(E2,A2,B2,C2,D2,m_M[ 4]+MAGIC8);
+ F3< 5>(D1,E1,A1,B1,C1,m_M[12]+MAGIC3); F3< 5>(D2,E2,A2,B2,C2,m_M[13]+MAGIC8);
+
+ F4<11>(C1,D1,E1,A1,B1,m_M[ 1]+MAGIC4); F2<15>(C2,D2,E2,A2,B2,m_M[ 8]+MAGIC9);
+ F4<12>(B1,C1,D1,E1,A1,m_M[ 9]+MAGIC4); F2< 5>(B2,C2,D2,E2,A2,m_M[ 6]+MAGIC9);
+ F4<14>(A1,B1,C1,D1,E1,m_M[11]+MAGIC4); F2< 8>(A2,B2,C2,D2,E2,m_M[ 4]+MAGIC9);
+ F4<15>(E1,A1,B1,C1,D1,m_M[10]+MAGIC4); F2<11>(E2,A2,B2,C2,D2,m_M[ 1]+MAGIC9);
+ F4<14>(D1,E1,A1,B1,C1,m_M[ 0]+MAGIC4); F2<14>(D2,E2,A2,B2,C2,m_M[ 3]+MAGIC9);
+ F4<15>(C1,D1,E1,A1,B1,m_M[ 8]+MAGIC4); F2<14>(C2,D2,E2,A2,B2,m_M[11]+MAGIC9);
+ F4< 9>(B1,C1,D1,E1,A1,m_M[12]+MAGIC4); F2< 6>(B2,C2,D2,E2,A2,m_M[15]+MAGIC9);
+ F4< 8>(A1,B1,C1,D1,E1,m_M[ 4]+MAGIC4); F2<14>(A2,B2,C2,D2,E2,m_M[ 0]+MAGIC9);
+ F4< 9>(E1,A1,B1,C1,D1,m_M[13]+MAGIC4); F2< 6>(E2,A2,B2,C2,D2,m_M[ 5]+MAGIC9);
+ F4<14>(D1,E1,A1,B1,C1,m_M[ 3]+MAGIC4); F2< 9>(D2,E2,A2,B2,C2,m_M[12]+MAGIC9);
+ F4< 5>(C1,D1,E1,A1,B1,m_M[ 7]+MAGIC4); F2<12>(C2,D2,E2,A2,B2,m_M[ 2]+MAGIC9);
+ F4< 6>(B1,C1,D1,E1,A1,m_M[15]+MAGIC4); F2< 9>(B2,C2,D2,E2,A2,m_M[13]+MAGIC9);
+ F4< 8>(A1,B1,C1,D1,E1,m_M[14]+MAGIC4); F2<12>(A2,B2,C2,D2,E2,m_M[ 9]+MAGIC9);
+ F4< 6>(E1,A1,B1,C1,D1,m_M[ 5]+MAGIC4); F2< 5>(E2,A2,B2,C2,D2,m_M[ 7]+MAGIC9);
+ F4< 5>(D1,E1,A1,B1,C1,m_M[ 6]+MAGIC4); F2<15>(D2,E2,A2,B2,C2,m_M[10]+MAGIC9);
+ F4<12>(C1,D1,E1,A1,B1,m_M[ 2]+MAGIC4); F2< 8>(C2,D2,E2,A2,B2,m_M[14]+MAGIC9);
+
+ F5< 9>(B1,C1,D1,E1,A1,m_M[ 4]+MAGIC5); F1< 8>(B2,C2,D2,E2,A2,m_M[12] );
+ F5<15>(A1,B1,C1,D1,E1,m_M[ 0]+MAGIC5); F1< 5>(A2,B2,C2,D2,E2,m_M[15] );
+ F5< 5>(E1,A1,B1,C1,D1,m_M[ 5]+MAGIC5); F1<12>(E2,A2,B2,C2,D2,m_M[10] );
+ F5<11>(D1,E1,A1,B1,C1,m_M[ 9]+MAGIC5); F1< 9>(D2,E2,A2,B2,C2,m_M[ 4] );
+ F5< 6>(C1,D1,E1,A1,B1,m_M[ 7]+MAGIC5); F1<12>(C2,D2,E2,A2,B2,m_M[ 1] );
+ F5< 8>(B1,C1,D1,E1,A1,m_M[12]+MAGIC5); F1< 5>(B2,C2,D2,E2,A2,m_M[ 5] );
+ F5<13>(A1,B1,C1,D1,E1,m_M[ 2]+MAGIC5); F1<14>(A2,B2,C2,D2,E2,m_M[ 8] );
+ F5<12>(E1,A1,B1,C1,D1,m_M[10]+MAGIC5); F1< 6>(E2,A2,B2,C2,D2,m_M[ 7] );
+ F5< 5>(D1,E1,A1,B1,C1,m_M[14]+MAGIC5); F1< 8>(D2,E2,A2,B2,C2,m_M[ 6] );
+ F5<12>(C1,D1,E1,A1,B1,m_M[ 1]+MAGIC5); F1<13>(C2,D2,E2,A2,B2,m_M[ 2] );
+ F5<13>(B1,C1,D1,E1,A1,m_M[ 3]+MAGIC5); F1< 6>(B2,C2,D2,E2,A2,m_M[13] );
+ F5<14>(A1,B1,C1,D1,E1,m_M[ 8]+MAGIC5); F1< 5>(A2,B2,C2,D2,E2,m_M[14] );
+ F5<11>(E1,A1,B1,C1,D1,m_M[11]+MAGIC5); F1<15>(E2,A2,B2,C2,D2,m_M[ 0] );
+ F5< 8>(D1,E1,A1,B1,C1,m_M[ 6]+MAGIC5); F1<13>(D2,E2,A2,B2,C2,m_M[ 3] );
+ F5< 5>(C1,D1,E1,A1,B1,m_M[15]+MAGIC5); F1<11>(C2,D2,E2,A2,B2,m_M[ 9] );
+ F5< 6>(B1,C1,D1,E1,A1,m_M[13]+MAGIC5); F1<11>(B2,C2,D2,E2,A2,m_M[11] );
+
+ C1 = m_digest[1] + C1 + D2;
+ m_digest[1] = m_digest[2] + D1 + E2;
+ m_digest[2] = m_digest[3] + E1 + A2;
+ m_digest[3] = m_digest[4] + A1 + B2;
+ m_digest[4] = m_digest[0] + B1 + C2;
+ m_digest[0] = C1;
+
+ input += hash_block_size();
+ }
+ }
+
+/*
+* Copy out the digest
+*/
+void RIPEMD_160::copy_out(uint8_t output[])
+ {
+ copy_out_vec_le(output, output_length(), m_digest);
+ }
+
+/*
+* Clear memory of sensitive data
+*/
+void RIPEMD_160::clear()
+ {
+ MDx_HashFunction::clear();
+ zeroise(m_M);
+ m_digest[0] = 0x67452301;
+ m_digest[1] = 0xEFCDAB89;
+ m_digest[2] = 0x98BADCFE;
+ m_digest[3] = 0x10325476;
+ m_digest[4] = 0xC3D2E1F0;
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/hash/rmd160/rmd160.h b/comm/third_party/botan/src/lib/hash/rmd160/rmd160.h
new file mode 100644
index 0000000000..2ee3116808
--- /dev/null
+++ b/comm/third_party/botan/src/lib/hash/rmd160/rmd160.h
@@ -0,0 +1,41 @@
+/*
+* RIPEMD-160
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_RIPEMD_160_H_
+#define BOTAN_RIPEMD_160_H_
+
+#include <botan/mdx_hash.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(rmd160.h)
+
+namespace Botan {
+
+/**
+* RIPEMD-160
+*/
+class BOTAN_PUBLIC_API(2,0) RIPEMD_160 final : public MDx_HashFunction
+ {
+ public:
+ std::string name() const override { return "RIPEMD-160"; }
+ size_t output_length() const override { return 20; }
+ HashFunction* clone() const override { return new RIPEMD_160; }
+ std::unique_ptr<HashFunction> copy_state() const override;
+
+ void clear() override;
+
+ RIPEMD_160() : MDx_HashFunction(64, false, true), m_M(16), m_digest(5)
+ { clear(); }
+ private:
+ void compress_n(const uint8_t[], size_t blocks) override;
+ void copy_out(uint8_t[]) override;
+
+ secure_vector<uint32_t> m_M, m_digest;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/hash/sha1/info.txt b/comm/third_party/botan/src/lib/hash/sha1/info.txt
new file mode 100644
index 0000000000..6d326af1c9
--- /dev/null
+++ b/comm/third_party/botan/src/lib/hash/sha1/info.txt
@@ -0,0 +1,7 @@
+<defines>
+SHA1 -> 20131128
+</defines>
+
+<requires>
+mdx_hash
+</requires>
diff --git a/comm/third_party/botan/src/lib/hash/sha1/sha160.cpp b/comm/third_party/botan/src/lib/hash/sha1/sha160.cpp
new file mode 100644
index 0000000000..5893c5dc77
--- /dev/null
+++ b/comm/third_party/botan/src/lib/hash/sha1/sha160.cpp
@@ -0,0 +1,190 @@
+/*
+* SHA-160
+* (C) 1999-2008,2011 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/sha160.h>
+#include <botan/loadstor.h>
+#include <botan/rotate.h>
+#include <botan/cpuid.h>
+
+namespace Botan {
+
+std::unique_ptr<HashFunction> SHA_160::copy_state() const
+ {
+ return std::unique_ptr<HashFunction>(new SHA_160(*this));
+ }
+
+namespace SHA1_F {
+
+namespace {
+
+/*
+* SHA-160 F1 Function
+*/
+inline void F1(uint32_t A, uint32_t& B, uint32_t C, uint32_t D, uint32_t& E, uint32_t msg)
+ {
+ E += (D ^ (B & (C ^ D))) + msg + 0x5A827999 + rotl<5>(A);
+ B = rotl<30>(B);
+ }
+
+/*
+* SHA-160 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-160 F3 Function
+*/
+inline void F3(uint32_t A, uint32_t& B, uint32_t C, uint32_t D, uint32_t& E, uint32_t msg)
+ {
+ E += ((B & C) | ((B | C) & D)) + msg + 0x8F1BBCDC + rotl<5>(A);
+ B = rotl<30>(B);
+ }
+
+/*
+* SHA-160 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);
+ }
+
+}
+
+}
+
+/*
+* SHA-160 Compression Function
+*/
+void SHA_160::compress_n(const uint8_t input[], size_t blocks)
+ {
+ using namespace SHA1_F;
+
+#if defined(BOTAN_HAS_SHA1_X86_SHA_NI)
+ if(CPUID::has_intel_sha())
+ {
+ return sha1_compress_x86(m_digest, input, blocks);
+ }
+#endif
+
+#if defined(BOTAN_HAS_SHA1_ARMV8)
+ if(CPUID::has_arm_sha1())
+ {
+ return sha1_armv8_compress_n(m_digest, input, blocks);
+ }
+#endif
+
+#if defined(BOTAN_HAS_SHA1_SSE2)
+ if(CPUID::has_sse2())
+ {
+ return sse2_compress_n(m_digest, input, blocks);
+ }
+
+#endif
+
+ uint32_t A = m_digest[0], B = m_digest[1], C = m_digest[2],
+ D = m_digest[3], E = m_digest[4];
+
+ m_W.resize(80);
+
+ for(size_t i = 0; i != blocks; ++i)
+ {
+ load_be(m_W.data(), input, 16);
+
+ for(size_t j = 16; j != 80; j += 8)
+ {
+ m_W[j ] = rotl<1>(m_W[j-3] ^ m_W[j-8] ^ m_W[j-14] ^ m_W[j-16]);
+ m_W[j+1] = rotl<1>(m_W[j-2] ^ m_W[j-7] ^ m_W[j-13] ^ m_W[j-15]);
+ m_W[j+2] = rotl<1>(m_W[j-1] ^ m_W[j-6] ^ m_W[j-12] ^ m_W[j-14]);
+ m_W[j+3] = rotl<1>(m_W[j ] ^ m_W[j-5] ^ m_W[j-11] ^ m_W[j-13]);
+ m_W[j+4] = rotl<1>(m_W[j+1] ^ m_W[j-4] ^ m_W[j-10] ^ m_W[j-12]);
+ m_W[j+5] = rotl<1>(m_W[j+2] ^ m_W[j-3] ^ m_W[j- 9] ^ m_W[j-11]);
+ m_W[j+6] = rotl<1>(m_W[j+3] ^ m_W[j-2] ^ m_W[j- 8] ^ m_W[j-10]);
+ m_W[j+7] = rotl<1>(m_W[j+4] ^ m_W[j-1] ^ m_W[j- 7] ^ m_W[j- 9]);
+ }
+
+ F1(A, B, C, D, E, m_W[ 0]); F1(E, A, B, C, D, m_W[ 1]);
+ F1(D, E, A, B, C, m_W[ 2]); F1(C, D, E, A, B, m_W[ 3]);
+ F1(B, C, D, E, A, m_W[ 4]); F1(A, B, C, D, E, m_W[ 5]);
+ F1(E, A, B, C, D, m_W[ 6]); F1(D, E, A, B, C, m_W[ 7]);
+ F1(C, D, E, A, B, m_W[ 8]); F1(B, C, D, E, A, m_W[ 9]);
+ F1(A, B, C, D, E, m_W[10]); F1(E, A, B, C, D, m_W[11]);
+ F1(D, E, A, B, C, m_W[12]); F1(C, D, E, A, B, m_W[13]);
+ F1(B, C, D, E, A, m_W[14]); F1(A, B, C, D, E, m_W[15]);
+ F1(E, A, B, C, D, m_W[16]); F1(D, E, A, B, C, m_W[17]);
+ F1(C, D, E, A, B, m_W[18]); F1(B, C, D, E, A, m_W[19]);
+
+ F2(A, B, C, D, E, m_W[20]); F2(E, A, B, C, D, m_W[21]);
+ F2(D, E, A, B, C, m_W[22]); F2(C, D, E, A, B, m_W[23]);
+ F2(B, C, D, E, A, m_W[24]); F2(A, B, C, D, E, m_W[25]);
+ F2(E, A, B, C, D, m_W[26]); F2(D, E, A, B, C, m_W[27]);
+ F2(C, D, E, A, B, m_W[28]); F2(B, C, D, E, A, m_W[29]);
+ F2(A, B, C, D, E, m_W[30]); F2(E, A, B, C, D, m_W[31]);
+ F2(D, E, A, B, C, m_W[32]); F2(C, D, E, A, B, m_W[33]);
+ F2(B, C, D, E, A, m_W[34]); F2(A, B, C, D, E, m_W[35]);
+ F2(E, A, B, C, D, m_W[36]); F2(D, E, A, B, C, m_W[37]);
+ F2(C, D, E, A, B, m_W[38]); F2(B, C, D, E, A, m_W[39]);
+
+ F3(A, B, C, D, E, m_W[40]); F3(E, A, B, C, D, m_W[41]);
+ F3(D, E, A, B, C, m_W[42]); F3(C, D, E, A, B, m_W[43]);
+ F3(B, C, D, E, A, m_W[44]); F3(A, B, C, D, E, m_W[45]);
+ F3(E, A, B, C, D, m_W[46]); F3(D, E, A, B, C, m_W[47]);
+ F3(C, D, E, A, B, m_W[48]); F3(B, C, D, E, A, m_W[49]);
+ F3(A, B, C, D, E, m_W[50]); F3(E, A, B, C, D, m_W[51]);
+ F3(D, E, A, B, C, m_W[52]); F3(C, D, E, A, B, m_W[53]);
+ F3(B, C, D, E, A, m_W[54]); F3(A, B, C, D, E, m_W[55]);
+ F3(E, A, B, C, D, m_W[56]); F3(D, E, A, B, C, m_W[57]);
+ F3(C, D, E, A, B, m_W[58]); F3(B, C, D, E, A, m_W[59]);
+
+ F4(A, B, C, D, E, m_W[60]); F4(E, A, B, C, D, m_W[61]);
+ F4(D, E, A, B, C, m_W[62]); F4(C, D, E, A, B, m_W[63]);
+ F4(B, C, D, E, A, m_W[64]); F4(A, B, C, D, E, m_W[65]);
+ F4(E, A, B, C, D, m_W[66]); F4(D, E, A, B, C, m_W[67]);
+ F4(C, D, E, A, B, m_W[68]); F4(B, C, D, E, A, m_W[69]);
+ F4(A, B, C, D, E, m_W[70]); F4(E, A, B, C, D, m_W[71]);
+ F4(D, E, A, B, C, m_W[72]); F4(C, D, E, A, B, m_W[73]);
+ F4(B, C, D, E, A, m_W[74]); F4(A, B, C, D, E, m_W[75]);
+ F4(E, A, B, C, D, m_W[76]); F4(D, E, A, B, C, m_W[77]);
+ F4(C, D, E, A, B, m_W[78]); F4(B, C, D, E, A, m_W[79]);
+
+ A = (m_digest[0] += A);
+ B = (m_digest[1] += B);
+ C = (m_digest[2] += C);
+ D = (m_digest[3] += D);
+ E = (m_digest[4] += E);
+
+ input += hash_block_size();
+ }
+ }
+
+/*
+* Copy out the digest
+*/
+void SHA_160::copy_out(uint8_t output[])
+ {
+ copy_out_vec_be(output, output_length(), m_digest);
+ }
+
+/*
+* Clear memory of sensitive data
+*/
+void SHA_160::clear()
+ {
+ MDx_HashFunction::clear();
+ zeroise(m_W);
+ m_digest[0] = 0x67452301;
+ m_digest[1] = 0xEFCDAB89;
+ m_digest[2] = 0x98BADCFE;
+ m_digest[3] = 0x10325476;
+ m_digest[4] = 0xC3D2E1F0;
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/hash/sha1/sha160.h b/comm/third_party/botan/src/lib/hash/sha1/sha160.h
new file mode 100644
index 0000000000..bbab2e8532
--- /dev/null
+++ b/comm/third_party/botan/src/lib/hash/sha1/sha160.h
@@ -0,0 +1,75 @@
+/*
+* SHA-160
+* (C) 1999-2007,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_SHA_160_H_
+#define BOTAN_SHA_160_H_
+
+#include <botan/mdx_hash.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(sha160.h)
+
+namespace Botan {
+
+/**
+* NIST's SHA-160
+*/
+class BOTAN_PUBLIC_API(2,0) SHA_160 final : public MDx_HashFunction
+ {
+ public:
+ std::string name() const override { return "SHA-160"; }
+ size_t output_length() const override { return 20; }
+ HashFunction* clone() const override { return new SHA_160; }
+ std::unique_ptr<HashFunction> copy_state() const override;
+
+ void clear() override;
+
+ SHA_160() : MDx_HashFunction(64, true, true), m_digest(5)
+ {
+ clear();
+ }
+
+ private:
+ void compress_n(const uint8_t[], size_t blocks) override;
+
+#if defined(BOTAN_HAS_SHA1_ARMV8)
+ static void sha1_armv8_compress_n(secure_vector<uint32_t>& digest,
+ const uint8_t blocks[],
+ size_t block_count);
+#endif
+
+#if defined(BOTAN_HAS_SHA1_SSE2)
+ static void sse2_compress_n(secure_vector<uint32_t>& digest,
+ const uint8_t blocks[],
+ size_t block_count);
+#endif
+
+#if defined(BOTAN_HAS_SHA1_X86_SHA_NI)
+ // Using x86 SHA instructions in Intel Goldmont and Cannonlake
+ static void sha1_compress_x86(secure_vector<uint32_t>& digest,
+ const uint8_t blocks[],
+ size_t block_count);
+#endif
+
+
+ void copy_out(uint8_t[]) override;
+
+ /**
+ * The digest value
+ */
+ secure_vector<uint32_t> m_digest;
+
+ /**
+ * The message buffer
+ */
+ secure_vector<uint32_t> m_W;
+ };
+
+typedef SHA_160 SHA_1;
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/hash/sha1/sha1_armv8/info.txt b/comm/third_party/botan/src/lib/hash/sha1/sha1_armv8/info.txt
new file mode 100644
index 0000000000..51409c943c
--- /dev/null
+++ b/comm/third_party/botan/src/lib/hash/sha1/sha1_armv8/info.txt
@@ -0,0 +1,12 @@
+<defines>
+SHA1_ARMV8 -> 20170117
+</defines>
+
+<isa>
+armv8crypto
+</isa>
+
+<cc>
+gcc:4.9
+clang:3.8
+</cc>
diff --git a/comm/third_party/botan/src/lib/hash/sha1/sha1_armv8/sha1_armv8.cpp b/comm/third_party/botan/src/lib/hash/sha1/sha1_armv8/sha1_armv8.cpp
new file mode 100644
index 0000000000..3dc9f43d85
--- /dev/null
+++ b/comm/third_party/botan/src/lib/hash/sha1/sha1_armv8/sha1_armv8.cpp
@@ -0,0 +1,207 @@
+/*
+* SHA-1 using CPU instructions in ARMv8
+*
+* Contributed by Jeffrey Walton. Based on public domain code by
+* Johannes Schneiders, Skip Hovsmith and Barry O'Rourke.
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/sha160.h>
+#include <arm_neon.h>
+
+namespace Botan {
+
+/*
+* SHA-1 using CPU instructions in ARMv8
+*/
+//static
+#if defined(BOTAN_HAS_SHA1_ARMV8)
+BOTAN_FUNC_ISA("+crypto")
+void SHA_160::sha1_armv8_compress_n(secure_vector<uint32_t>& digest, const uint8_t input8[], size_t blocks)
+ {
+ uint32x4_t ABCD;
+ uint32_t E0;
+
+ // 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];
+
+ // Intermediate void* cast due to https://llvm.org/bugs/show_bug.cgi?id=20670
+ const uint32_t* input32 = reinterpret_cast<const uint32_t*>(reinterpret_cast<const void*>(input8));
+
+ while (blocks)
+ {
+ // 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);
+
+ 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);
+
+ // Rounds 0-3
+ 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);
+
+ // Rounds 4-7
+ E0 = vsha1h_u32(vgetq_lane_u32(ABCD, 0));
+ ABCD = vsha1cq_u32(ABCD, E1, TMP1);
+ TMP1 = vaddq_u32(MSG3, C0);
+ MSG0 = vsha1su1q_u32(MSG0, MSG3);
+ MSG1 = vsha1su0q_u32(MSG1, MSG2, MSG3);
+
+ // Rounds 8-11
+ E1 = vsha1h_u32(vgetq_lane_u32(ABCD, 0));
+ ABCD = vsha1cq_u32(ABCD, E0, TMP0);
+ TMP0 = vaddq_u32(MSG0, C0);
+ MSG1 = vsha1su1q_u32(MSG1, MSG0);
+ MSG2 = vsha1su0q_u32(MSG2, MSG3, MSG0);
+
+ // Rounds 12-15
+ E0 = vsha1h_u32(vgetq_lane_u32(ABCD, 0));
+ ABCD = vsha1cq_u32(ABCD, E1, TMP1);
+ TMP1 = vaddq_u32(MSG1, C1);
+ MSG2 = vsha1su1q_u32(MSG2, MSG1);
+ MSG3 = vsha1su0q_u32(MSG3, MSG0, MSG1);
+
+ // Rounds 16-19
+ E1 = vsha1h_u32(vgetq_lane_u32(ABCD, 0));
+ ABCD = vsha1cq_u32(ABCD, E0, TMP0);
+ TMP0 = vaddq_u32(MSG2, C1);
+ MSG3 = vsha1su1q_u32(MSG3, MSG2);
+ MSG0 = vsha1su0q_u32(MSG0, MSG1, MSG2);
+
+ // Rounds 20-23
+ E0 = vsha1h_u32(vgetq_lane_u32(ABCD, 0));
+ ABCD = vsha1pq_u32(ABCD, E1, TMP1);
+ TMP1 = vaddq_u32(MSG3, C1);
+ MSG0 = vsha1su1q_u32(MSG0, MSG3);
+ MSG1 = vsha1su0q_u32(MSG1, MSG2, MSG3);
+
+ // Rounds 24-27
+ E1 = vsha1h_u32(vgetq_lane_u32(ABCD, 0));
+ ABCD = vsha1pq_u32(ABCD, E0, TMP0);
+ TMP0 = vaddq_u32(MSG0, C1);
+ MSG1 = vsha1su1q_u32(MSG1, MSG0);
+ MSG2 = vsha1su0q_u32(MSG2, MSG3, MSG0);
+
+ // Rounds 28-31
+ E0 = vsha1h_u32(vgetq_lane_u32(ABCD, 0));
+ ABCD = vsha1pq_u32(ABCD, E1, TMP1);
+ TMP1 = vaddq_u32(MSG1, C1);
+ MSG2 = vsha1su1q_u32(MSG2, MSG1);
+ MSG3 = vsha1su0q_u32(MSG3, MSG0, MSG1);
+
+ // Rounds 32-35
+ E1 = vsha1h_u32(vgetq_lane_u32(ABCD, 0));
+ ABCD = vsha1pq_u32(ABCD, E0, TMP0);
+ TMP0 = vaddq_u32(MSG2, C2);
+ MSG3 = vsha1su1q_u32(MSG3, MSG2);
+ MSG0 = vsha1su0q_u32(MSG0, MSG1, MSG2);
+
+ // Rounds 36-39
+ E0 = vsha1h_u32(vgetq_lane_u32(ABCD, 0));
+ ABCD = vsha1pq_u32(ABCD, E1, TMP1);
+ TMP1 = vaddq_u32(MSG3, C2);
+ MSG0 = vsha1su1q_u32(MSG0, MSG3);
+ MSG1 = vsha1su0q_u32(MSG1, MSG2, MSG3);
+
+ // Rounds 40-43
+ E1 = vsha1h_u32(vgetq_lane_u32(ABCD, 0));
+ ABCD = vsha1mq_u32(ABCD, E0, TMP0);
+ TMP0 = vaddq_u32(MSG0, C2);
+ MSG1 = vsha1su1q_u32(MSG1, MSG0);
+ MSG2 = vsha1su0q_u32(MSG2, MSG3, MSG0);
+
+ // Rounds 44-47
+ E0 = vsha1h_u32(vgetq_lane_u32(ABCD, 0));
+ ABCD = vsha1mq_u32(ABCD, E1, TMP1);
+ TMP1 = vaddq_u32(MSG1, C2);
+ MSG2 = vsha1su1q_u32(MSG2, MSG1);
+ MSG3 = vsha1su0q_u32(MSG3, MSG0, MSG1);
+
+ // Rounds 48-51
+ E1 = vsha1h_u32(vgetq_lane_u32(ABCD, 0));
+ ABCD = vsha1mq_u32(ABCD, E0, TMP0);
+ TMP0 = vaddq_u32(MSG2, C2);
+ MSG3 = vsha1su1q_u32(MSG3, MSG2);
+ MSG0 = vsha1su0q_u32(MSG0, MSG1, MSG2);
+
+ // Rounds 52-55
+ E0 = vsha1h_u32(vgetq_lane_u32(ABCD, 0));
+ ABCD = vsha1mq_u32(ABCD, E1, TMP1);
+ TMP1 = vaddq_u32(MSG3, C3);
+ MSG0 = vsha1su1q_u32(MSG0, MSG3);
+ MSG1 = vsha1su0q_u32(MSG1, MSG2, MSG3);
+
+ // Rounds 56-59
+ E1 = vsha1h_u32(vgetq_lane_u32(ABCD, 0));
+ ABCD = vsha1mq_u32(ABCD, E0, TMP0);
+ TMP0 = vaddq_u32(MSG0, C3);
+ MSG1 = vsha1su1q_u32(MSG1, MSG0);
+ MSG2 = vsha1su0q_u32(MSG2, MSG3, MSG0);
+
+ // Rounds 60-63
+ E0 = vsha1h_u32(vgetq_lane_u32(ABCD, 0));
+ ABCD = vsha1pq_u32(ABCD, E1, TMP1);
+ TMP1 = vaddq_u32(MSG1, C3);
+ MSG2 = vsha1su1q_u32(MSG2, MSG1);
+ MSG3 = vsha1su0q_u32(MSG3, MSG0, MSG1);
+
+ // Rounds 64-67
+ E1 = vsha1h_u32(vgetq_lane_u32(ABCD, 0));
+ ABCD = vsha1pq_u32(ABCD, E0, TMP0);
+ TMP0 = vaddq_u32(MSG2, C3);
+ MSG3 = vsha1su1q_u32(MSG3, MSG2);
+ MSG0 = vsha1su0q_u32(MSG0, MSG1, MSG2);
+
+ // Rounds 68-71
+ 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));
+ ABCD = vsha1pq_u32(ABCD, E0, TMP0);
+
+ // Rounds 76-79
+ E0 = vsha1h_u32(vgetq_lane_u32(ABCD, 0));
+ ABCD = vsha1pq_u32(ABCD, E1, TMP1);
+
+ // Add state back
+ E0 += E0_SAVED;
+ ABCD = vaddq_u32(ABCD_SAVED, ABCD);
+
+ input32 += 64/4;
+ blocks--;
+ }
+
+ // Save digest
+ vst1q_u32(&digest[0], ABCD);
+ digest[4] = E0;
+ }
+#endif
+
+}
diff --git a/comm/third_party/botan/src/lib/hash/sha1/sha1_sse2/info.txt b/comm/third_party/botan/src/lib/hash/sha1/sha1_sse2/info.txt
new file mode 100644
index 0000000000..2aee95b15c
--- /dev/null
+++ b/comm/third_party/botan/src/lib/hash/sha1/sha1_sse2/info.txt
@@ -0,0 +1,7 @@
+<defines>
+SHA1_SSE2 -> 20160803
+</defines>
+
+<isa>
+sse2
+</isa>
diff --git a/comm/third_party/botan/src/lib/hash/sha1/sha1_sse2/sha1_sse2.cpp b/comm/third_party/botan/src/lib/hash/sha1/sha1_sse2/sha1_sse2.cpp
new file mode 100644
index 0000000000..7371ca08f5
--- /dev/null
+++ b/comm/third_party/botan/src/lib/hash/sha1/sha1_sse2/sha1_sse2.cpp
@@ -0,0 +1,336 @@
+/*
+* SHA-1 using SSE2
+* Based on public domain code by Dean Gaudet
+* (http://arctic.org/~dean/crypto/sha1.html)
+* (C) 2009-2011 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/sha160.h>
+#include <botan/rotate.h>
+#include <emmintrin.h>
+
+namespace Botan {
+
+namespace SHA1_SSE2_F {
+
+namespace {
+
+/*
+* First 16 bytes just need byte swapping. Preparing just means
+* adding in the round constants.
+*/
+
+#define prep00_15(P, W) \
+ do { \
+ W = _mm_shufflehi_epi16(W, _MM_SHUFFLE(2, 3, 0, 1)); \
+ W = _mm_shufflelo_epi16(W, _MM_SHUFFLE(2, 3, 0, 1)); \
+ W = _mm_or_si128(_mm_slli_epi16(W, 8), \
+ _mm_srli_epi16(W, 8)); \
+ P.u128 = _mm_add_epi32(W, K00_19); \
+ } while(0)
+
+/*
+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]
+*/
+
+/* note that there is a step here where i want to do a rol by 1, which
+* normally would look like this:
+*
+* r1 = psrld r0,$31
+* r0 = pslld r0,$1
+* r0 = por r0,r1
+*
+* but instead i do this:
+*
+* r1 = pcmpltd r0,zero
+* r0 = paddd r0,r0
+* r0 = psub r0,r1
+*
+* because pcmpltd and paddd are available in both MMX units on
+* efficeon, pentium-m, and opteron but shifts are available in
+* only one unit.
+*/
+#define prep(prep, XW0, XW1, XW2, XW3, K) \
+ do { \
+ __m128i r0, r1, r2, r3; \
+ \
+ /* load W[t-4] 16-byte aligned, and shift */ \
+ r3 = _mm_srli_si128((XW3), 4); \
+ r0 = (XW0); \
+ /* get high 64-bits of XW0 into low 64-bits */ \
+ r1 = _mm_shuffle_epi32((XW0), _MM_SHUFFLE(1,0,3,2)); \
+ /* load high 64-bits of r1 */ \
+ r1 = _mm_unpacklo_epi64(r1, (XW1)); \
+ r2 = (XW2); \
+ \
+ r0 = _mm_xor_si128(r1, r0); \
+ r2 = _mm_xor_si128(r3, r2); \
+ r0 = _mm_xor_si128(r2, r0); \
+ /* unrotated W[t]..W[t+2] in r0 ... still need W[t+3] */ \
+ \
+ r2 = _mm_slli_si128(r0, 12); \
+ r1 = _mm_cmplt_epi32(r0, _mm_setzero_si128()); \
+ r0 = _mm_add_epi32(r0, r0); /* shift left by 1 */ \
+ r0 = _mm_sub_epi32(r0, r1); /* r0 has W[t]..W[t+2] */ \
+ \
+ r3 = _mm_srli_epi32(r2, 30); \
+ r2 = _mm_slli_epi32(r2, 2); \
+ \
+ r0 = _mm_xor_si128(r0, r3); \
+ r0 = _mm_xor_si128(r0, r2); /* r0 now has W[t+3] */ \
+ \
+ (XW0) = r0; \
+ (prep).u128 = _mm_add_epi32(r0, K); \
+ } while(0)
+
+/*
+* SHA-160 F1 Function
+*/
+inline void F1(uint32_t A, uint32_t& B, uint32_t C, uint32_t D, uint32_t& E, uint32_t msg)
+ {
+ E += (D ^ (B & (C ^ D))) + msg + rotl<5>(A);
+ B = rotl<30>(B);
+ }
+
+/*
+* SHA-160 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-160 F3 Function
+*/
+inline void F3(uint32_t A, uint32_t& B, uint32_t C, uint32_t D, uint32_t& E, uint32_t msg)
+ {
+ E += ((B & C) | ((B | C) & D)) + msg + rotl<5>(A);
+ B = rotl<30>(B);
+ }
+
+/*
+* SHA-160 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);
+ }
+
+}
+
+}
+
+/*
+* SHA-160 Compression Function using SSE for message expansion
+*/
+//static
+BOTAN_FUNC_ISA("sse2")
+void SHA_160::sse2_compress_n(secure_vector<uint32_t>& digest, const uint8_t input[], size_t blocks)
+ {
+ using namespace SHA1_SSE2_F;
+
+ const __m128i K00_19 = _mm_set1_epi32(0x5A827999);
+ const __m128i K20_39 = _mm_set1_epi32(0x6ED9EBA1);
+ const __m128i K40_59 = _mm_set1_epi32(0x8F1BBCDC);
+ const __m128i K60_79 = _mm_set1_epi32(0xCA62C1D6);
+
+ uint32_t A = digest[0],
+ B = digest[1],
+ C = digest[2],
+ D = digest[3],
+ E = digest[4];
+
+ const __m128i* input_mm = reinterpret_cast<const __m128i*>(input);
+
+ for(size_t i = 0; i != blocks; ++i)
+ {
+ union v4si {
+ uint32_t u32[4];
+ __m128i u128;
+ };
+
+ v4si P0, P1, P2, P3;
+
+ __m128i W0 = _mm_loadu_si128(&input_mm[0]);
+ prep00_15(P0, W0);
+
+ __m128i W1 = _mm_loadu_si128(&input_mm[1]);
+ prep00_15(P1, W1);
+
+ __m128i W2 = _mm_loadu_si128(&input_mm[2]);
+ prep00_15(P2, W2);
+
+ __m128i W3 = _mm_loadu_si128(&input_mm[3]);
+ prep00_15(P3, W3);
+
+ /*
+ Using SSE4; slower on Core2 and Nehalem
+ #define GET_P_32(P, i) _mm_extract_epi32(P.u128, i)
+
+ Much slower on all tested platforms
+ #define GET_P_32(P,i) _mm_cvtsi128_si32(_mm_srli_si128(P.u128, i*4))
+ */
+
+#define GET_P_32(P, i) P.u32[i]
+
+ F1(A, B, C, D, E, GET_P_32(P0, 0));
+ F1(E, A, B, C, D, GET_P_32(P0, 1));
+ F1(D, E, A, B, C, GET_P_32(P0, 2));
+ F1(C, D, E, A, B, GET_P_32(P0, 3));
+ prep(P0, W0, W1, W2, W3, K00_19);
+
+ F1(B, C, D, E, A, GET_P_32(P1, 0));
+ F1(A, B, C, D, E, GET_P_32(P1, 1));
+ F1(E, A, B, C, D, GET_P_32(P1, 2));
+ F1(D, E, A, B, C, GET_P_32(P1, 3));
+ prep(P1, W1, W2, W3, W0, K20_39);
+
+ F1(C, D, E, A, B, GET_P_32(P2, 0));
+ F1(B, C, D, E, A, GET_P_32(P2, 1));
+ F1(A, B, C, D, E, GET_P_32(P2, 2));
+ F1(E, A, B, C, D, GET_P_32(P2, 3));
+ prep(P2, W2, W3, W0, W1, K20_39);
+
+ F1(D, E, A, B, C, GET_P_32(P3, 0));
+ F1(C, D, E, A, B, GET_P_32(P3, 1));
+ F1(B, C, D, E, A, GET_P_32(P3, 2));
+ F1(A, B, C, D, E, GET_P_32(P3, 3));
+ prep(P3, W3, W0, W1, W2, K20_39);
+
+ F1(E, A, B, C, D, GET_P_32(P0, 0));
+ F1(D, E, A, B, C, GET_P_32(P0, 1));
+ F1(C, D, E, A, B, GET_P_32(P0, 2));
+ F1(B, C, D, E, A, GET_P_32(P0, 3));
+ prep(P0, W0, W1, W2, W3, K20_39);
+
+ F2(A, B, C, D, E, GET_P_32(P1, 0));
+ F2(E, A, B, C, D, GET_P_32(P1, 1));
+ F2(D, E, A, B, C, GET_P_32(P1, 2));
+ F2(C, D, E, A, B, GET_P_32(P1, 3));
+ prep(P1, W1, W2, W3, W0, K20_39);
+
+ F2(B, C, D, E, A, GET_P_32(P2, 0));
+ F2(A, B, C, D, E, GET_P_32(P2, 1));
+ F2(E, A, B, C, D, GET_P_32(P2, 2));
+ F2(D, E, A, B, C, GET_P_32(P2, 3));
+ prep(P2, W2, W3, W0, W1, K40_59);
+
+ F2(C, D, E, A, B, GET_P_32(P3, 0));
+ F2(B, C, D, E, A, GET_P_32(P3, 1));
+ F2(A, B, C, D, E, GET_P_32(P3, 2));
+ F2(E, A, B, C, D, GET_P_32(P3, 3));
+ prep(P3, W3, W0, W1, W2, K40_59);
+
+ F2(D, E, A, B, C, GET_P_32(P0, 0));
+ F2(C, D, E, A, B, GET_P_32(P0, 1));
+ F2(B, C, D, E, A, GET_P_32(P0, 2));
+ F2(A, B, C, D, E, GET_P_32(P0, 3));
+ prep(P0, W0, W1, W2, W3, K40_59);
+
+ F2(E, A, B, C, D, GET_P_32(P1, 0));
+ F2(D, E, A, B, C, GET_P_32(P1, 1));
+ F2(C, D, E, A, B, GET_P_32(P1, 2));
+ F2(B, C, D, E, A, GET_P_32(P1, 3));
+ prep(P1, W1, W2, W3, W0, K40_59);
+
+ F3(A, B, C, D, E, GET_P_32(P2, 0));
+ F3(E, A, B, C, D, GET_P_32(P2, 1));
+ F3(D, E, A, B, C, GET_P_32(P2, 2));
+ F3(C, D, E, A, B, GET_P_32(P2, 3));
+ prep(P2, W2, W3, W0, W1, K40_59);
+
+ F3(B, C, D, E, A, GET_P_32(P3, 0));
+ F3(A, B, C, D, E, GET_P_32(P3, 1));
+ F3(E, A, B, C, D, GET_P_32(P3, 2));
+ F3(D, E, A, B, C, GET_P_32(P3, 3));
+ prep(P3, W3, W0, W1, W2, K60_79);
+
+ F3(C, D, E, A, B, GET_P_32(P0, 0));
+ F3(B, C, D, E, A, GET_P_32(P0, 1));
+ F3(A, B, C, D, E, GET_P_32(P0, 2));
+ F3(E, A, B, C, D, GET_P_32(P0, 3));
+ prep(P0, W0, W1, W2, W3, K60_79);
+
+ F3(D, E, A, B, C, GET_P_32(P1, 0));
+ F3(C, D, E, A, B, GET_P_32(P1, 1));
+ F3(B, C, D, E, A, GET_P_32(P1, 2));
+ F3(A, B, C, D, E, GET_P_32(P1, 3));
+ prep(P1, W1, W2, W3, W0, K60_79);
+
+ F3(E, A, B, C, D, GET_P_32(P2, 0));
+ F3(D, E, A, B, C, GET_P_32(P2, 1));
+ F3(C, D, E, A, B, GET_P_32(P2, 2));
+ F3(B, C, D, E, A, GET_P_32(P2, 3));
+ prep(P2, W2, W3, W0, W1, K60_79);
+
+ F4(A, B, C, D, E, GET_P_32(P3, 0));
+ F4(E, A, B, C, D, GET_P_32(P3, 1));
+ F4(D, E, A, B, C, GET_P_32(P3, 2));
+ F4(C, D, E, A, B, GET_P_32(P3, 3));
+ prep(P3, W3, W0, W1, W2, K60_79);
+
+ F4(B, C, D, E, A, GET_P_32(P0, 0));
+ F4(A, B, C, D, E, GET_P_32(P0, 1));
+ F4(E, A, B, C, D, GET_P_32(P0, 2));
+ F4(D, E, A, B, C, GET_P_32(P0, 3));
+
+ F4(C, D, E, A, B, GET_P_32(P1, 0));
+ F4(B, C, D, E, A, GET_P_32(P1, 1));
+ F4(A, B, C, D, E, GET_P_32(P1, 2));
+ F4(E, A, B, C, D, GET_P_32(P1, 3));
+
+ F4(D, E, A, B, C, GET_P_32(P2, 0));
+ F4(C, D, E, A, B, GET_P_32(P2, 1));
+ F4(B, C, D, E, A, GET_P_32(P2, 2));
+ F4(A, B, C, D, E, GET_P_32(P2, 3));
+
+ F4(E, A, B, C, D, GET_P_32(P3, 0));
+ F4(D, E, A, B, C, GET_P_32(P3, 1));
+ F4(C, D, E, A, B, GET_P_32(P3, 2));
+ F4(B, C, D, E, A, GET_P_32(P3, 3));
+
+ A = (digest[0] += A);
+ B = (digest[1] += B);
+ C = (digest[2] += C);
+ D = (digest[3] += D);
+ E = (digest[4] += E);
+
+ input_mm += (64 / 16);
+ }
+
+#undef GET_P_32
+ }
+
+#undef prep00_15
+#undef prep
+
+}
diff --git a/comm/third_party/botan/src/lib/hash/sha1/sha1_x86/info.txt b/comm/third_party/botan/src/lib/hash/sha1/sha1_x86/info.txt
new file mode 100644
index 0000000000..0a46d980a0
--- /dev/null
+++ b/comm/third_party/botan/src/lib/hash/sha1/sha1_x86/info.txt
@@ -0,0 +1,16 @@
+<defines>
+SHA1_X86_SHA_NI -> 20170518
+</defines>
+
+<isa>
+sha
+sse2
+ssse3
+sse41
+</isa>
+
+<cc>
+clang:3.9
+gcc:5.0
+msvc:19.0 # MSVS 2015
+</cc>
diff --git a/comm/third_party/botan/src/lib/hash/sha1/sha1_x86/sha1_x86.cpp b/comm/third_party/botan/src/lib/hash/sha1/sha1_x86/sha1_x86.cpp
new file mode 100644
index 0000000000..76feebcea1
--- /dev/null
+++ b/comm/third_party/botan/src/lib/hash/sha1/sha1_x86/sha1_x86.cpp
@@ -0,0 +1,216 @@
+/*
+* 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
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/sha160.h>
+#include <immintrin.h>
+
+namespace Botan {
+
+#if defined(BOTAN_HAS_SHA1_X86_SHA_NI)
+BOTAN_FUNC_ISA("sha,ssse3,sse4.1")
+void SHA_160::sha1_compress_x86(secure_vector<uint32_t>& digest,
+ const uint8_t input[],
+ size_t blocks)
+ {
+ const __m128i MASK = _mm_set_epi64x(0x0001020304050607ULL, 0x08090a0b0c0d0e0fULL);
+ const __m128i* input_mm = reinterpret_cast<const __m128i*>(input);
+
+ 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);
+
+ while (blocks)
+ {
+ // Save current hash
+ const __m128i ABCD_SAVE = ABCD;
+ const __m128i E0_SAVE = E0;
+
+ __m128i MSG0, MSG1, MSG2, MSG3;
+ __m128i E1;
+
+ // 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);
+
+ input_mm += 4;
+ blocks--;
+ }
+
+ // Save state
+ ABCD = _mm_shuffle_epi32(ABCD, 0x1B);
+ _mm_storeu_si128(reinterpret_cast<__m128i*>(state), ABCD);
+ state[4] = _mm_extract_epi32(E0, 3);
+ }
+#endif
+
+}
diff --git a/comm/third_party/botan/src/lib/hash/sha2_32/info.txt b/comm/third_party/botan/src/lib/hash/sha2_32/info.txt
new file mode 100644
index 0000000000..7992eff261
--- /dev/null
+++ b/comm/third_party/botan/src/lib/hash/sha2_32/info.txt
@@ -0,0 +1,7 @@
+<defines>
+SHA2_32 -> 20131128
+</defines>
+
+<requires>
+mdx_hash
+</requires>
diff --git a/comm/third_party/botan/src/lib/hash/sha2_32/sha2_32.cpp b/comm/third_party/botan/src/lib/hash/sha2_32/sha2_32.cpp
new file mode 100644
index 0000000000..61e98d22f2
--- /dev/null
+++ b/comm/third_party/botan/src/lib/hash/sha2_32/sha2_32.cpp
@@ -0,0 +1,278 @@
+/*
+* SHA-{224,256}
+* (C) 1999-2010,2017 Jack Lloyd
+* 2007 FlexSecure GmbH
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/sha2_32.h>
+#include <botan/loadstor.h>
+#include <botan/rotate.h>
+#include <botan/cpuid.h>
+
+namespace Botan {
+
+namespace {
+
+std::string sha256_provider()
+ {
+#if defined(BOTAN_HAS_SHA2_32_X86)
+ if(CPUID::has_intel_sha())
+ {
+ return "shani";
+ }
+#endif
+
+#if defined(BOTAN_HAS_SHA2_32_X86_BMI2)
+ if(CPUID::has_bmi2())
+ {
+ return "bmi2";
+ }
+#endif
+
+#if defined(BOTAN_HAS_SHA2_32_ARMV8)
+ if(CPUID::has_arm_sha2())
+ {
+ return "armv8";
+ }
+#endif
+
+ return "base";
+ }
+
+}
+
+std::unique_ptr<HashFunction> SHA_224::copy_state() const
+ {
+ return std::unique_ptr<HashFunction>(new SHA_224(*this));
+ }
+
+std::unique_ptr<HashFunction> SHA_256::copy_state() const
+ {
+ return std::unique_ptr<HashFunction>(new SHA_256(*this));
+ }
+
+/*
+* SHA-256 F1 Function
+*
+* Use a macro as many compilers won't inline a function this big,
+* even though it is much faster if inlined.
+*/
+#define SHA2_32_F(A, B, C, D, E, F, G, H, M1, M2, M3, M4, magic) do { \
+ uint32_t A_rho = rotr<2>(A) ^ rotr<13>(A) ^ rotr<22>(A); \
+ uint32_t E_rho = rotr<6>(E) ^ rotr<11>(E) ^ rotr<25>(E); \
+ uint32_t M2_sigma = rotr<17>(M2) ^ rotr<19>(M2) ^ (M2 >> 10); \
+ uint32_t M4_sigma = rotr<7>(M4) ^ rotr<18>(M4) ^ (M4 >> 3); \
+ H += magic + E_rho + ((E & F) ^ (~E & G)) + M1; \
+ D += H; \
+ H += A_rho + ((A & B) | ((A | B) & C)); \
+ M1 += M2_sigma + M3 + M4_sigma; \
+ } while(0);
+
+/*
+* SHA-224 / SHA-256 compression function
+*/
+void SHA_256::compress_digest(secure_vector<uint32_t>& digest,
+ const uint8_t input[], size_t blocks)
+ {
+#if defined(BOTAN_HAS_SHA2_32_X86)
+ if(CPUID::has_intel_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);
+ }
+#endif
+
+#if defined(BOTAN_HAS_SHA2_32_ARMV8)
+ if(CPUID::has_arm_sha2())
+ {
+ return SHA_256::compress_digest_armv8(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];
+
+ for(size_t i = 0; i != blocks; ++i)
+ {
+ uint32_t W00 = load_be<uint32_t>(input, 0);
+ uint32_t W01 = load_be<uint32_t>(input, 1);
+ uint32_t W02 = load_be<uint32_t>(input, 2);
+ uint32_t W03 = load_be<uint32_t>(input, 3);
+ uint32_t W04 = load_be<uint32_t>(input, 4);
+ uint32_t W05 = load_be<uint32_t>(input, 5);
+ uint32_t W06 = load_be<uint32_t>(input, 6);
+ uint32_t W07 = load_be<uint32_t>(input, 7);
+ uint32_t W08 = load_be<uint32_t>(input, 8);
+ uint32_t W09 = load_be<uint32_t>(input, 9);
+ uint32_t W10 = load_be<uint32_t>(input, 10);
+ uint32_t W11 = load_be<uint32_t>(input, 11);
+ uint32_t W12 = load_be<uint32_t>(input, 12);
+ uint32_t W13 = load_be<uint32_t>(input, 13);
+ uint32_t W14 = load_be<uint32_t>(input, 14);
+ uint32_t W15 = load_be<uint32_t>(input, 15);
+
+ SHA2_32_F(A, B, C, D, E, F, G, H, W00, W14, W09, W01, 0x428A2F98);
+ SHA2_32_F(H, A, B, C, D, E, F, G, W01, W15, W10, W02, 0x71374491);
+ SHA2_32_F(G, H, A, B, C, D, E, F, W02, W00, W11, W03, 0xB5C0FBCF);
+ SHA2_32_F(F, G, H, A, B, C, D, E, W03, W01, W12, W04, 0xE9B5DBA5);
+ SHA2_32_F(E, F, G, H, A, B, C, D, W04, W02, W13, W05, 0x3956C25B);
+ SHA2_32_F(D, E, F, G, H, A, B, C, W05, W03, W14, W06, 0x59F111F1);
+ SHA2_32_F(C, D, E, F, G, H, A, B, W06, W04, W15, W07, 0x923F82A4);
+ SHA2_32_F(B, C, D, E, F, G, H, A, W07, W05, W00, W08, 0xAB1C5ED5);
+ SHA2_32_F(A, B, C, D, E, F, G, H, W08, W06, W01, W09, 0xD807AA98);
+ SHA2_32_F(H, A, B, C, D, E, F, G, W09, W07, W02, W10, 0x12835B01);
+ SHA2_32_F(G, H, A, B, C, D, E, F, W10, W08, W03, W11, 0x243185BE);
+ SHA2_32_F(F, G, H, A, B, C, D, E, W11, W09, W04, W12, 0x550C7DC3);
+ SHA2_32_F(E, F, G, H, A, B, C, D, W12, W10, W05, W13, 0x72BE5D74);
+ SHA2_32_F(D, E, F, G, H, A, B, C, W13, W11, W06, W14, 0x80DEB1FE);
+ SHA2_32_F(C, D, E, F, G, H, A, B, W14, W12, W07, W15, 0x9BDC06A7);
+ SHA2_32_F(B, C, D, E, F, G, H, A, W15, W13, W08, W00, 0xC19BF174);
+
+ SHA2_32_F(A, B, C, D, E, F, G, H, W00, W14, W09, W01, 0xE49B69C1);
+ SHA2_32_F(H, A, B, C, D, E, F, G, W01, W15, W10, W02, 0xEFBE4786);
+ SHA2_32_F(G, H, A, B, C, D, E, F, W02, W00, W11, W03, 0x0FC19DC6);
+ SHA2_32_F(F, G, H, A, B, C, D, E, W03, W01, W12, W04, 0x240CA1CC);
+ SHA2_32_F(E, F, G, H, A, B, C, D, W04, W02, W13, W05, 0x2DE92C6F);
+ SHA2_32_F(D, E, F, G, H, A, B, C, W05, W03, W14, W06, 0x4A7484AA);
+ SHA2_32_F(C, D, E, F, G, H, A, B, W06, W04, W15, W07, 0x5CB0A9DC);
+ SHA2_32_F(B, C, D, E, F, G, H, A, W07, W05, W00, W08, 0x76F988DA);
+ SHA2_32_F(A, B, C, D, E, F, G, H, W08, W06, W01, W09, 0x983E5152);
+ SHA2_32_F(H, A, B, C, D, E, F, G, W09, W07, W02, W10, 0xA831C66D);
+ SHA2_32_F(G, H, A, B, C, D, E, F, W10, W08, W03, W11, 0xB00327C8);
+ SHA2_32_F(F, G, H, A, B, C, D, E, W11, W09, W04, W12, 0xBF597FC7);
+ SHA2_32_F(E, F, G, H, A, B, C, D, W12, W10, W05, W13, 0xC6E00BF3);
+ SHA2_32_F(D, E, F, G, H, A, B, C, W13, W11, W06, W14, 0xD5A79147);
+ SHA2_32_F(C, D, E, F, G, H, A, B, W14, W12, W07, W15, 0x06CA6351);
+ SHA2_32_F(B, C, D, E, F, G, H, A, W15, W13, W08, W00, 0x14292967);
+
+ SHA2_32_F(A, B, C, D, E, F, G, H, W00, W14, W09, W01, 0x27B70A85);
+ SHA2_32_F(H, A, B, C, D, E, F, G, W01, W15, W10, W02, 0x2E1B2138);
+ SHA2_32_F(G, H, A, B, C, D, E, F, W02, W00, W11, W03, 0x4D2C6DFC);
+ SHA2_32_F(F, G, H, A, B, C, D, E, W03, W01, W12, W04, 0x53380D13);
+ SHA2_32_F(E, F, G, H, A, B, C, D, W04, W02, W13, W05, 0x650A7354);
+ SHA2_32_F(D, E, F, G, H, A, B, C, W05, W03, W14, W06, 0x766A0ABB);
+ SHA2_32_F(C, D, E, F, G, H, A, B, W06, W04, W15, W07, 0x81C2C92E);
+ SHA2_32_F(B, C, D, E, F, G, H, A, W07, W05, W00, W08, 0x92722C85);
+ SHA2_32_F(A, B, C, D, E, F, G, H, W08, W06, W01, W09, 0xA2BFE8A1);
+ SHA2_32_F(H, A, B, C, D, E, F, G, W09, W07, W02, W10, 0xA81A664B);
+ SHA2_32_F(G, H, A, B, C, D, E, F, W10, W08, W03, W11, 0xC24B8B70);
+ SHA2_32_F(F, G, H, A, B, C, D, E, W11, W09, W04, W12, 0xC76C51A3);
+ SHA2_32_F(E, F, G, H, A, B, C, D, W12, W10, W05, W13, 0xD192E819);
+ SHA2_32_F(D, E, F, G, H, A, B, C, W13, W11, W06, W14, 0xD6990624);
+ SHA2_32_F(C, D, E, F, G, H, A, B, W14, W12, W07, W15, 0xF40E3585);
+ SHA2_32_F(B, C, D, E, F, G, H, A, W15, W13, W08, W00, 0x106AA070);
+
+ SHA2_32_F(A, B, C, D, E, F, G, H, W00, W14, W09, W01, 0x19A4C116);
+ SHA2_32_F(H, A, B, C, D, E, F, G, W01, W15, W10, W02, 0x1E376C08);
+ SHA2_32_F(G, H, A, B, C, D, E, F, W02, W00, W11, W03, 0x2748774C);
+ SHA2_32_F(F, G, H, A, B, C, D, E, W03, W01, W12, W04, 0x34B0BCB5);
+ SHA2_32_F(E, F, G, H, A, B, C, D, W04, W02, W13, W05, 0x391C0CB3);
+ SHA2_32_F(D, E, F, G, H, A, B, C, W05, W03, W14, W06, 0x4ED8AA4A);
+ SHA2_32_F(C, D, E, F, G, H, A, B, W06, W04, W15, W07, 0x5B9CCA4F);
+ SHA2_32_F(B, C, D, E, F, G, H, A, W07, W05, W00, W08, 0x682E6FF3);
+ SHA2_32_F(A, B, C, D, E, F, G, H, W08, W06, W01, W09, 0x748F82EE);
+ SHA2_32_F(H, A, B, C, D, E, F, G, W09, W07, W02, W10, 0x78A5636F);
+ SHA2_32_F(G, H, A, B, C, D, E, F, W10, W08, W03, W11, 0x84C87814);
+ SHA2_32_F(F, G, H, A, B, C, D, E, W11, W09, W04, W12, 0x8CC70208);
+ SHA2_32_F(E, F, G, H, A, B, C, D, W12, W10, W05, W13, 0x90BEFFFA);
+ SHA2_32_F(D, E, F, G, H, A, B, C, W13, W11, W06, W14, 0xA4506CEB);
+ SHA2_32_F(C, D, E, F, G, H, A, B, W14, W12, W07, W15, 0xBEF9A3F7);
+ SHA2_32_F(B, C, D, E, F, G, H, A, W15, W13, W08, W00, 0xC67178F2);
+
+ 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);
+
+ input += 64;
+ }
+ }
+
+std::string SHA_224::provider() const
+ {
+ return sha256_provider();
+ }
+
+std::string SHA_256::provider() const
+ {
+ return sha256_provider();
+ }
+
+/*
+* SHA-224 compression function
+*/
+void SHA_224::compress_n(const uint8_t input[], size_t blocks)
+ {
+ SHA_256::compress_digest(m_digest, input, blocks);
+ }
+
+/*
+* Copy out the digest
+*/
+void SHA_224::copy_out(uint8_t output[])
+ {
+ copy_out_vec_be(output, output_length(), m_digest);
+ }
+
+/*
+* Clear memory of sensitive data
+*/
+void SHA_224::clear()
+ {
+ MDx_HashFunction::clear();
+ m_digest[0] = 0xC1059ED8;
+ m_digest[1] = 0x367CD507;
+ m_digest[2] = 0x3070DD17;
+ m_digest[3] = 0xF70E5939;
+ m_digest[4] = 0xFFC00B31;
+ m_digest[5] = 0x68581511;
+ m_digest[6] = 0x64F98FA7;
+ m_digest[7] = 0xBEFA4FA4;
+ }
+
+/*
+* SHA-256 compression function
+*/
+void SHA_256::compress_n(const uint8_t input[], size_t blocks)
+ {
+ SHA_256::compress_digest(m_digest, input, blocks);
+ }
+
+/*
+* Copy out the digest
+*/
+void SHA_256::copy_out(uint8_t output[])
+ {
+ copy_out_vec_be(output, output_length(), m_digest);
+ }
+
+/*
+* Clear memory of sensitive data
+*/
+void SHA_256::clear()
+ {
+ MDx_HashFunction::clear();
+ m_digest[0] = 0x6A09E667;
+ m_digest[1] = 0xBB67AE85;
+ m_digest[2] = 0x3C6EF372;
+ m_digest[3] = 0xA54FF53A;
+ m_digest[4] = 0x510E527F;
+ m_digest[5] = 0x9B05688C;
+ m_digest[6] = 0x1F83D9AB;
+ m_digest[7] = 0x5BE0CD19;
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/hash/sha2_32/sha2_32.h b/comm/third_party/botan/src/lib/hash/sha2_32/sha2_32.h
new file mode 100644
index 0000000000..90a0597212
--- /dev/null
+++ b/comm/third_party/botan/src/lib/hash/sha2_32/sha2_32.h
@@ -0,0 +1,95 @@
+/*
+* SHA-{224,256}
+* (C) 1999-2011 Jack Lloyd
+* 2007 FlexSecure GmbH
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_SHA_224_256_H_
+#define BOTAN_SHA_224_256_H_
+
+#include <botan/mdx_hash.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(sha2_32.h)
+
+namespace Botan {
+
+/**
+* SHA-224
+*/
+class BOTAN_PUBLIC_API(2,0) SHA_224 final : public MDx_HashFunction
+ {
+ public:
+ std::string name() const override { return "SHA-224"; }
+ size_t output_length() const override { return 28; }
+ HashFunction* clone() const override { return new SHA_224; }
+ std::unique_ptr<HashFunction> copy_state() const override;
+
+ void clear() override;
+
+ std::string provider() const override;
+
+ SHA_224() : MDx_HashFunction(64, true, true), m_digest(8)
+ { clear(); }
+ private:
+ void compress_n(const uint8_t[], size_t blocks) override;
+ void copy_out(uint8_t[]) override;
+
+ secure_vector<uint32_t> m_digest;
+ };
+
+/**
+* SHA-256
+*/
+class BOTAN_PUBLIC_API(2,0) SHA_256 final : public MDx_HashFunction
+ {
+ public:
+ std::string name() const override { return "SHA-256"; }
+ size_t output_length() const override { return 32; }
+ HashFunction* clone() const override { return new SHA_256; }
+ std::unique_ptr<HashFunction> copy_state() const override;
+
+ void clear() override;
+
+ std::string provider() const override;
+
+ SHA_256() : MDx_HashFunction(64, true, true), m_digest(8)
+ { clear(); }
+
+ /*
+ * Perform a SHA-256 compression. For internal use
+ */
+ static void compress_digest(secure_vector<uint32_t>& digest,
+ const uint8_t input[],
+ size_t blocks);
+
+ private:
+
+#if defined(BOTAN_HAS_SHA2_32_ARMV8)
+ static void compress_digest_armv8(secure_vector<uint32_t>& digest,
+ const uint8_t input[],
+ size_t blocks);
+#endif
+
+#if defined(BOTAN_HAS_SHA2_32_X86_BMI2)
+ static void compress_digest_x86_bmi2(secure_vector<uint32_t>& digest,
+ const uint8_t input[],
+ size_t blocks);
+#endif
+
+#if defined(BOTAN_HAS_SHA2_32_X86)
+ static void compress_digest_x86(secure_vector<uint32_t>& digest,
+ const uint8_t input[],
+ size_t blocks);
+#endif
+
+ void compress_n(const uint8_t[], size_t blocks) override;
+ void copy_out(uint8_t[]) override;
+
+ secure_vector<uint32_t> m_digest;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/hash/sha2_32/sha2_32_armv8/info.txt b/comm/third_party/botan/src/lib/hash/sha2_32/sha2_32_armv8/info.txt
new file mode 100644
index 0000000000..cd8813b74f
--- /dev/null
+++ b/comm/third_party/botan/src/lib/hash/sha2_32/sha2_32_armv8/info.txt
@@ -0,0 +1,12 @@
+<defines>
+SHA2_32_ARMV8 -> 20170117
+</defines>
+
+<isa>
+armv8crypto
+</isa>
+
+<cc>
+gcc:4.9
+clang:3.8
+</cc>
diff --git a/comm/third_party/botan/src/lib/hash/sha2_32/sha2_32_armv8/sha2_32_armv8.cpp b/comm/third_party/botan/src/lib/hash/sha2_32/sha2_32_armv8/sha2_32_armv8.cpp
new file mode 100644
index 0000000000..1574a32738
--- /dev/null
+++ b/comm/third_party/botan/src/lib/hash/sha2_32/sha2_32_armv8/sha2_32_armv8.cpp
@@ -0,0 +1,204 @@
+/*
+* SHA-256 using CPU instructions in ARMv8
+*
+* Contributed by Jeffrey Walton. Based on public domain code by
+* Johannes Schneiders, Skip Hovsmith and Barry O'Rourke.
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/sha2_32.h>
+#include <arm_neon.h>
+
+namespace Botan {
+
+/*
+* SHA-256 using CPU instructions in ARMv8
+*/
+//static
+#if defined(BOTAN_HAS_SHA2_32_ARMV8)
+BOTAN_FUNC_ISA("+crypto")
+void SHA_256::compress_digest_armv8(secure_vector<uint32_t>& digest, const uint8_t input8[], size_t blocks)
+ {
+ static const uint32_t K[] = {
+ 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,
+ };
+
+ uint32x4_t STATE0, STATE1, ABEF_SAVE, CDGH_SAVE;
+ uint32x4_t MSG0, MSG1, MSG2, MSG3;
+ uint32x4_t TMP0, TMP1, TMP2;
+
+ // Load initial values
+ STATE0 = vld1q_u32(&digest[0]);
+ STATE1 = vld1q_u32(&digest[4]);
+
+ // Intermediate void* cast due to https://llvm.org/bugs/show_bug.cgi?id=20670
+ const uint32_t* input32 = reinterpret_cast<const uint32_t*>(reinterpret_cast<const void*>(input8));
+
+ while (blocks)
+ {
+ // Save current state
+ ABEF_SAVE = STATE0;
+ CDGH_SAVE = STATE1;
+
+ MSG0 = vld1q_u32(input32 + 0);
+ MSG1 = vld1q_u32(input32 + 4);
+ MSG2 = vld1q_u32(input32 + 8);
+ 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, vld1q_u32(&K[0x00]));
+
+ // Rounds 0-3
+ MSG0 = vsha256su0q_u32(MSG0, MSG1);
+ TMP2 = STATE0;
+ TMP1 = vaddq_u32(MSG1, vld1q_u32(&K[0x04]));
+ STATE0 = vsha256hq_u32(STATE0, STATE1, TMP0);
+ STATE1 = vsha256h2q_u32(STATE1, TMP2, TMP0);
+ MSG0 = vsha256su1q_u32(MSG0, MSG2, MSG3);
+
+ // Rounds 4-7
+ MSG1 = vsha256su0q_u32(MSG1, MSG2);
+ TMP2 = STATE0;
+ TMP0 = vaddq_u32(MSG2, vld1q_u32(&K[0x08]));
+ STATE0 = vsha256hq_u32(STATE0, STATE1, TMP1);
+ STATE1 = vsha256h2q_u32(STATE1, TMP2, TMP1);
+ MSG1 = vsha256su1q_u32(MSG1, MSG3, MSG0);
+
+ // Rounds 8-11
+ MSG2 = vsha256su0q_u32(MSG2, MSG3);
+ TMP2 = STATE0;
+ TMP1 = vaddq_u32(MSG3, vld1q_u32(&K[0x0c]));
+ STATE0 = vsha256hq_u32(STATE0, STATE1, TMP0);
+ STATE1 = vsha256h2q_u32(STATE1, TMP2, TMP0);
+ MSG2 = vsha256su1q_u32(MSG2, MSG0, MSG1);
+
+ // Rounds 12-15
+ MSG3 = vsha256su0q_u32(MSG3, MSG0);
+ TMP2 = STATE0;
+ TMP0 = vaddq_u32(MSG0, vld1q_u32(&K[0x10]));
+ STATE0 = vsha256hq_u32(STATE0, STATE1, TMP1);
+ STATE1 = vsha256h2q_u32(STATE1, TMP2, TMP1);
+ MSG3 = vsha256su1q_u32(MSG3, MSG1, MSG2);
+
+ // Rounds 16-19
+ MSG0 = vsha256su0q_u32(MSG0, MSG1);
+ TMP2 = STATE0;
+ TMP1 = vaddq_u32(MSG1, vld1q_u32(&K[0x14]));
+ STATE0 = vsha256hq_u32(STATE0, STATE1, TMP0);
+ STATE1 = vsha256h2q_u32(STATE1, TMP2, TMP0);
+ MSG0 = vsha256su1q_u32(MSG0, MSG2, MSG3);
+
+ // Rounds 20-23
+ MSG1 = vsha256su0q_u32(MSG1, MSG2);
+ TMP2 = STATE0;
+ TMP0 = vaddq_u32(MSG2, vld1q_u32(&K[0x18]));
+ STATE0 = vsha256hq_u32(STATE0, STATE1, TMP1);
+ STATE1 = vsha256h2q_u32(STATE1, TMP2, TMP1);
+ MSG1 = vsha256su1q_u32(MSG1, MSG3, MSG0);
+
+ // Rounds 24-27
+ MSG2 = vsha256su0q_u32(MSG2, MSG3);
+ TMP2 = STATE0;
+ TMP1 = vaddq_u32(MSG3, vld1q_u32(&K[0x1c]));
+ STATE0 = vsha256hq_u32(STATE0, STATE1, TMP0);
+ STATE1 = vsha256h2q_u32(STATE1, TMP2, TMP0);
+ MSG2 = vsha256su1q_u32(MSG2, MSG0, MSG1);
+
+ // Rounds 28-31
+ MSG3 = vsha256su0q_u32(MSG3, MSG0);
+ TMP2 = STATE0;
+ TMP0 = vaddq_u32(MSG0, vld1q_u32(&K[0x20]));
+ STATE0 = vsha256hq_u32(STATE0, STATE1, TMP1);
+ STATE1 = vsha256h2q_u32(STATE1, TMP2, TMP1);
+ MSG3 = vsha256su1q_u32(MSG3, MSG1, MSG2);
+
+ // Rounds 32-35
+ MSG0 = vsha256su0q_u32(MSG0, MSG1);
+ TMP2 = STATE0;
+ TMP1 = vaddq_u32(MSG1, vld1q_u32(&K[0x24]));
+ STATE0 = vsha256hq_u32(STATE0, STATE1, TMP0);
+ STATE1 = vsha256h2q_u32(STATE1, TMP2, TMP0);
+ MSG0 = vsha256su1q_u32(MSG0, MSG2, MSG3);
+
+ // Rounds 36-39
+ MSG1 = vsha256su0q_u32(MSG1, MSG2);
+ TMP2 = STATE0;
+ TMP0 = vaddq_u32(MSG2, vld1q_u32(&K[0x28]));
+ STATE0 = vsha256hq_u32(STATE0, STATE1, TMP1);
+ STATE1 = vsha256h2q_u32(STATE1, TMP2, TMP1);
+ MSG1 = vsha256su1q_u32(MSG1, MSG3, MSG0);
+
+ // Rounds 40-43
+ MSG2 = vsha256su0q_u32(MSG2, MSG3);
+ TMP2 = STATE0;
+ TMP1 = vaddq_u32(MSG3, vld1q_u32(&K[0x2c]));
+ STATE0 = vsha256hq_u32(STATE0, STATE1, TMP0);
+ STATE1 = vsha256h2q_u32(STATE1, TMP2, TMP0);
+ MSG2 = vsha256su1q_u32(MSG2, MSG0, MSG1);
+
+ // Rounds 44-47
+ MSG3 = vsha256su0q_u32(MSG3, MSG0);
+ TMP2 = STATE0;
+ TMP0 = vaddq_u32(MSG0, vld1q_u32(&K[0x30]));
+ STATE0 = vsha256hq_u32(STATE0, STATE1, TMP1);
+ STATE1 = vsha256h2q_u32(STATE1, TMP2, TMP1);
+ MSG3 = vsha256su1q_u32(MSG3, MSG1, MSG2);
+
+ // Rounds 48-51
+ TMP2 = STATE0;
+ TMP1 = vaddq_u32(MSG1, vld1q_u32(&K[0x34]));
+ STATE0 = vsha256hq_u32(STATE0, STATE1, TMP0);
+ STATE1 = vsha256h2q_u32(STATE1, TMP2, TMP0);
+
+ // Rounds 52-55
+ TMP2 = STATE0;
+ TMP0 = vaddq_u32(MSG2, vld1q_u32(&K[0x38]));
+ STATE0 = vsha256hq_u32(STATE0, STATE1, TMP1);
+ STATE1 = vsha256h2q_u32(STATE1, TMP2, TMP1);
+
+ // Rounds 56-59
+ TMP2 = STATE0;
+ TMP1 = vaddq_u32(MSG3, vld1q_u32(&K[0x3c]));
+ STATE0 = vsha256hq_u32(STATE0, STATE1, TMP0);
+ STATE1 = vsha256h2q_u32(STATE1, TMP2, TMP0);
+
+ // Rounds 60-63
+ TMP2 = STATE0;
+ STATE0 = vsha256hq_u32(STATE0, STATE1, TMP1);
+ STATE1 = vsha256h2q_u32(STATE1, TMP2, TMP1);
+
+ // Add back to state
+ STATE0 = vaddq_u32(STATE0, ABEF_SAVE);
+ STATE1 = vaddq_u32(STATE1, CDGH_SAVE);
+
+ input32 += 64/4;
+ blocks--;
+ }
+
+ // Save state
+ vst1q_u32(&digest[0], STATE0);
+ vst1q_u32(&digest[4], STATE1);
+ }
+#endif
+
+}
diff --git a/comm/third_party/botan/src/lib/hash/sha2_32/sha2_32_bmi2/info.txt b/comm/third_party/botan/src/lib/hash/sha2_32/sha2_32_bmi2/info.txt
new file mode 100644
index 0000000000..6918f0a4a3
--- /dev/null
+++ b/comm/third_party/botan/src/lib/hash/sha2_32/sha2_32_bmi2/info.txt
@@ -0,0 +1,12 @@
+<defines>
+SHA2_32_X86_BMI2 -> 20180526
+</defines>
+
+<isa>
+bmi2
+</isa>
+
+<cc>
+gcc
+clang
+</cc>
diff --git a/comm/third_party/botan/src/lib/hash/sha2_32/sha2_32_bmi2/sha2_32_bmi2.cpp b/comm/third_party/botan/src/lib/hash/sha2_32/sha2_32_bmi2/sha2_32_bmi2.cpp
new file mode 100644
index 0000000000..e3194e4afe
--- /dev/null
+++ b/comm/third_party/botan/src/lib/hash/sha2_32/sha2_32_bmi2/sha2_32_bmi2.cpp
@@ -0,0 +1,140 @@
+/*
+* (C) 2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/sha2_32.h>
+#include <botan/loadstor.h>
+#include <botan/rotate.h>
+
+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.
+*/
+
+#define SHA2_32_F(A, B, C, D, E, F, G, H, M1, M2, M3, M4, magic) do { \
+ uint32_t A_rho = rotr<2>(A) ^ rotr<13>(A) ^ rotr<22>(A); \
+ uint32_t E_rho = rotr<6>(E) ^ rotr<11>(E) ^ rotr<25>(E); \
+ uint32_t M2_sigma = rotr<17>(M2) ^ rotr<19>(M2) ^ (M2 >> 10); \
+ uint32_t M4_sigma = rotr<7>(M4) ^ rotr<18>(M4) ^ (M4 >> 3); \
+ H += magic + E_rho + ((E & F) ^ (~E & G)) + M1; \
+ D += H; \
+ H += A_rho + ((A & B) | ((A | B) & C)); \
+ M1 += M2_sigma + M3 + M4_sigma; \
+ } while(0);
+
+void SHA_256::compress_digest_x86_bmi2(secure_vector<uint32_t>& digest,
+ const uint8_t 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];
+
+ for(size_t i = 0; i != blocks; ++i)
+ {
+ uint32_t W00 = load_be<uint32_t>(input, 0);
+ uint32_t W01 = load_be<uint32_t>(input, 1);
+ uint32_t W02 = load_be<uint32_t>(input, 2);
+ uint32_t W03 = load_be<uint32_t>(input, 3);
+ uint32_t W04 = load_be<uint32_t>(input, 4);
+ uint32_t W05 = load_be<uint32_t>(input, 5);
+ uint32_t W06 = load_be<uint32_t>(input, 6);
+ uint32_t W07 = load_be<uint32_t>(input, 7);
+ uint32_t W08 = load_be<uint32_t>(input, 8);
+ uint32_t W09 = load_be<uint32_t>(input, 9);
+ uint32_t W10 = load_be<uint32_t>(input, 10);
+ uint32_t W11 = load_be<uint32_t>(input, 11);
+ uint32_t W12 = load_be<uint32_t>(input, 12);
+ uint32_t W13 = load_be<uint32_t>(input, 13);
+ uint32_t W14 = load_be<uint32_t>(input, 14);
+ uint32_t W15 = load_be<uint32_t>(input, 15);
+
+ SHA2_32_F(A, B, C, D, E, F, G, H, W00, W14, W09, W01, 0x428A2F98);
+ SHA2_32_F(H, A, B, C, D, E, F, G, W01, W15, W10, W02, 0x71374491);
+ SHA2_32_F(G, H, A, B, C, D, E, F, W02, W00, W11, W03, 0xB5C0FBCF);
+ SHA2_32_F(F, G, H, A, B, C, D, E, W03, W01, W12, W04, 0xE9B5DBA5);
+ SHA2_32_F(E, F, G, H, A, B, C, D, W04, W02, W13, W05, 0x3956C25B);
+ SHA2_32_F(D, E, F, G, H, A, B, C, W05, W03, W14, W06, 0x59F111F1);
+ SHA2_32_F(C, D, E, F, G, H, A, B, W06, W04, W15, W07, 0x923F82A4);
+ SHA2_32_F(B, C, D, E, F, G, H, A, W07, W05, W00, W08, 0xAB1C5ED5);
+ SHA2_32_F(A, B, C, D, E, F, G, H, W08, W06, W01, W09, 0xD807AA98);
+ SHA2_32_F(H, A, B, C, D, E, F, G, W09, W07, W02, W10, 0x12835B01);
+ SHA2_32_F(G, H, A, B, C, D, E, F, W10, W08, W03, W11, 0x243185BE);
+ SHA2_32_F(F, G, H, A, B, C, D, E, W11, W09, W04, W12, 0x550C7DC3);
+ SHA2_32_F(E, F, G, H, A, B, C, D, W12, W10, W05, W13, 0x72BE5D74);
+ SHA2_32_F(D, E, F, G, H, A, B, C, W13, W11, W06, W14, 0x80DEB1FE);
+ SHA2_32_F(C, D, E, F, G, H, A, B, W14, W12, W07, W15, 0x9BDC06A7);
+ SHA2_32_F(B, C, D, E, F, G, H, A, W15, W13, W08, W00, 0xC19BF174);
+
+ SHA2_32_F(A, B, C, D, E, F, G, H, W00, W14, W09, W01, 0xE49B69C1);
+ SHA2_32_F(H, A, B, C, D, E, F, G, W01, W15, W10, W02, 0xEFBE4786);
+ SHA2_32_F(G, H, A, B, C, D, E, F, W02, W00, W11, W03, 0x0FC19DC6);
+ SHA2_32_F(F, G, H, A, B, C, D, E, W03, W01, W12, W04, 0x240CA1CC);
+ SHA2_32_F(E, F, G, H, A, B, C, D, W04, W02, W13, W05, 0x2DE92C6F);
+ SHA2_32_F(D, E, F, G, H, A, B, C, W05, W03, W14, W06, 0x4A7484AA);
+ SHA2_32_F(C, D, E, F, G, H, A, B, W06, W04, W15, W07, 0x5CB0A9DC);
+ SHA2_32_F(B, C, D, E, F, G, H, A, W07, W05, W00, W08, 0x76F988DA);
+ SHA2_32_F(A, B, C, D, E, F, G, H, W08, W06, W01, W09, 0x983E5152);
+ SHA2_32_F(H, A, B, C, D, E, F, G, W09, W07, W02, W10, 0xA831C66D);
+ SHA2_32_F(G, H, A, B, C, D, E, F, W10, W08, W03, W11, 0xB00327C8);
+ SHA2_32_F(F, G, H, A, B, C, D, E, W11, W09, W04, W12, 0xBF597FC7);
+ SHA2_32_F(E, F, G, H, A, B, C, D, W12, W10, W05, W13, 0xC6E00BF3);
+ SHA2_32_F(D, E, F, G, H, A, B, C, W13, W11, W06, W14, 0xD5A79147);
+ SHA2_32_F(C, D, E, F, G, H, A, B, W14, W12, W07, W15, 0x06CA6351);
+ SHA2_32_F(B, C, D, E, F, G, H, A, W15, W13, W08, W00, 0x14292967);
+
+ SHA2_32_F(A, B, C, D, E, F, G, H, W00, W14, W09, W01, 0x27B70A85);
+ SHA2_32_F(H, A, B, C, D, E, F, G, W01, W15, W10, W02, 0x2E1B2138);
+ SHA2_32_F(G, H, A, B, C, D, E, F, W02, W00, W11, W03, 0x4D2C6DFC);
+ SHA2_32_F(F, G, H, A, B, C, D, E, W03, W01, W12, W04, 0x53380D13);
+ SHA2_32_F(E, F, G, H, A, B, C, D, W04, W02, W13, W05, 0x650A7354);
+ SHA2_32_F(D, E, F, G, H, A, B, C, W05, W03, W14, W06, 0x766A0ABB);
+ SHA2_32_F(C, D, E, F, G, H, A, B, W06, W04, W15, W07, 0x81C2C92E);
+ SHA2_32_F(B, C, D, E, F, G, H, A, W07, W05, W00, W08, 0x92722C85);
+ SHA2_32_F(A, B, C, D, E, F, G, H, W08, W06, W01, W09, 0xA2BFE8A1);
+ SHA2_32_F(H, A, B, C, D, E, F, G, W09, W07, W02, W10, 0xA81A664B);
+ SHA2_32_F(G, H, A, B, C, D, E, F, W10, W08, W03, W11, 0xC24B8B70);
+ SHA2_32_F(F, G, H, A, B, C, D, E, W11, W09, W04, W12, 0xC76C51A3);
+ SHA2_32_F(E, F, G, H, A, B, C, D, W12, W10, W05, W13, 0xD192E819);
+ SHA2_32_F(D, E, F, G, H, A, B, C, W13, W11, W06, W14, 0xD6990624);
+ SHA2_32_F(C, D, E, F, G, H, A, B, W14, W12, W07, W15, 0xF40E3585);
+ SHA2_32_F(B, C, D, E, F, G, H, A, W15, W13, W08, W00, 0x106AA070);
+
+ SHA2_32_F(A, B, C, D, E, F, G, H, W00, W14, W09, W01, 0x19A4C116);
+ SHA2_32_F(H, A, B, C, D, E, F, G, W01, W15, W10, W02, 0x1E376C08);
+ SHA2_32_F(G, H, A, B, C, D, E, F, W02, W00, W11, W03, 0x2748774C);
+ SHA2_32_F(F, G, H, A, B, C, D, E, W03, W01, W12, W04, 0x34B0BCB5);
+ SHA2_32_F(E, F, G, H, A, B, C, D, W04, W02, W13, W05, 0x391C0CB3);
+ SHA2_32_F(D, E, F, G, H, A, B, C, W05, W03, W14, W06, 0x4ED8AA4A);
+ SHA2_32_F(C, D, E, F, G, H, A, B, W06, W04, W15, W07, 0x5B9CCA4F);
+ SHA2_32_F(B, C, D, E, F, G, H, A, W07, W05, W00, W08, 0x682E6FF3);
+ SHA2_32_F(A, B, C, D, E, F, G, H, W08, W06, W01, W09, 0x748F82EE);
+ SHA2_32_F(H, A, B, C, D, E, F, G, W09, W07, W02, W10, 0x78A5636F);
+ SHA2_32_F(G, H, A, B, C, D, E, F, W10, W08, W03, W11, 0x84C87814);
+ SHA2_32_F(F, G, H, A, B, C, D, E, W11, W09, W04, W12, 0x8CC70208);
+ SHA2_32_F(E, F, G, H, A, B, C, D, W12, W10, W05, W13, 0x90BEFFFA);
+ SHA2_32_F(D, E, F, G, H, A, B, C, W13, W11, W06, W14, 0xA4506CEB);
+ SHA2_32_F(C, D, E, F, G, H, A, B, W14, W12, W07, W15, 0xBEF9A3F7);
+ SHA2_32_F(B, C, D, E, F, G, H, A, W15, W13, W08, W00, 0xC67178F2);
+
+ 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);
+
+ input += 64;
+ }
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/hash/sha2_32/sha2_32_x86/info.txt b/comm/third_party/botan/src/lib/hash/sha2_32/sha2_32_x86/info.txt
new file mode 100644
index 0000000000..8d9fb4149b
--- /dev/null
+++ b/comm/third_party/botan/src/lib/hash/sha2_32/sha2_32_x86/info.txt
@@ -0,0 +1,16 @@
+<defines>
+SHA2_32_X86 -> 20170518
+</defines>
+
+<isa>
+sha
+sse2
+ssse3
+sse41
+</isa>
+
+<cc>
+gcc:5.0
+clang:3.9
+msvc:19.0 # MSVS 2015
+</cc>
diff --git a/comm/third_party/botan/src/lib/hash/sha2_32/sha2_32_x86/sha2_32_x86.cpp b/comm/third_party/botan/src/lib/hash/sha2_32/sha2_32_x86/sha2_32_x86.cpp
new file mode 100644
index 0000000000..a4bd9b72db
--- /dev/null
+++ b/comm/third_party/botan/src/lib/hash/sha2_32/sha2_32_x86/sha2_32_x86.cpp
@@ -0,0 +1,215 @@
+/*
+* Support for SHA-256 x86 instrinsic
+* Based on public domain code by Sean Gulley
+* (https://github.com/mitls/hacl-star/tree/master/experimental/hash)
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/sha2_32.h>
+#include <immintrin.h>
+
+namespace Botan {
+
+// called from sha2_32.cpp
+#if defined(BOTAN_HAS_SHA2_32_X86)
+BOTAN_FUNC_ISA("sha,sse4.1,ssse3")
+void SHA_256::compress_digest_x86(secure_vector<uint32_t>& digest, const uint8_t input[], size_t blocks)
+ {
+ __m128i STATE0, STATE1;
+ __m128i MSG, TMP, MASK;
+ __m128i TMSG0, TMSG1, TMSG2, TMSG3;
+ __m128i ABEF_SAVE, CDGH_SAVE;
+
+ uint32_t* state = &digest[0];
+
+ const __m128i* input_mm = reinterpret_cast<const __m128i*>(input);
+
+ // Load initial values
+ TMP = _mm_loadu_si128(reinterpret_cast<__m128i*>(&state[0]));
+ STATE1 = _mm_loadu_si128(reinterpret_cast<__m128i*>(&state[4]));
+ MASK = _mm_set_epi64x(0x0c0d0e0f08090a0bULL, 0x0405060700010203ULL);
+
+ TMP = _mm_shuffle_epi32(TMP, 0xB1); // CDAB
+ STATE1 = _mm_shuffle_epi32(STATE1, 0x1B); // EFGH
+ STATE0 = _mm_alignr_epi8(TMP, STATE1, 8); // ABEF
+ STATE1 = _mm_blend_epi16(STATE1, TMP, 0xF0); // CDGH
+
+ while (blocks)
+ {
+ // Save current hash
+ ABEF_SAVE = STATE0;
+ CDGH_SAVE = STATE1;
+
+ // Rounds 0-3
+ MSG = _mm_loadu_si128(input_mm);
+ TMSG0 = _mm_shuffle_epi8(MSG, MASK);
+ MSG = _mm_add_epi32(TMSG0, _mm_set_epi64x(0xE9B5DBA5B5C0FBCFULL, 0x71374491428A2F98ULL));
+ STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG);
+ MSG = _mm_shuffle_epi32(MSG, 0x0E);
+ STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG);
+
+ // Rounds 4-7
+ TMSG1 = _mm_loadu_si128(input_mm + 1);
+ TMSG1 = _mm_shuffle_epi8(TMSG1, MASK);
+ MSG = _mm_add_epi32(TMSG1, _mm_set_epi64x(0xAB1C5ED5923F82A4ULL, 0x59F111F13956C25BULL));
+ STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG);
+ MSG = _mm_shuffle_epi32(MSG, 0x0E);
+ STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG);
+ TMSG0 = _mm_sha256msg1_epu32(TMSG0, TMSG1);
+
+ // Rounds 8-11
+ TMSG2 = _mm_loadu_si128(input_mm + 2);
+ TMSG2 = _mm_shuffle_epi8(TMSG2, MASK);
+ MSG = _mm_add_epi32(TMSG2, _mm_set_epi64x(0x550C7DC3243185BEULL, 0x12835B01D807AA98ULL));
+ STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG);
+ MSG = _mm_shuffle_epi32(MSG, 0x0E);
+ STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG);
+ TMSG1 = _mm_sha256msg1_epu32(TMSG1, TMSG2);
+
+ // Rounds 12-15
+ TMSG3 = _mm_loadu_si128(input_mm + 3);
+ TMSG3 = _mm_shuffle_epi8(TMSG3, MASK);
+ MSG = _mm_add_epi32(TMSG3, _mm_set_epi64x(0xC19BF1749BDC06A7ULL, 0x80DEB1FE72BE5D74ULL));
+ STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG);
+ TMP = _mm_alignr_epi8(TMSG3, TMSG2, 4);
+ TMSG0 = _mm_add_epi32(TMSG0, TMP);
+ TMSG0 = _mm_sha256msg2_epu32(TMSG0, TMSG3);
+ MSG = _mm_shuffle_epi32(MSG, 0x0E);
+ STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG);
+ TMSG2 = _mm_sha256msg1_epu32(TMSG2, TMSG3);
+
+ // Rounds 16-19
+ MSG = _mm_add_epi32(TMSG0, _mm_set_epi64x(0x240CA1CC0FC19DC6ULL, 0xEFBE4786E49B69C1ULL));
+ STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG);
+ TMP = _mm_alignr_epi8(TMSG0, TMSG3, 4);
+ TMSG1 = _mm_add_epi32(TMSG1, TMP);
+ TMSG1 = _mm_sha256msg2_epu32(TMSG1, TMSG0);
+ MSG = _mm_shuffle_epi32(MSG, 0x0E);
+ STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG);
+ TMSG3 = _mm_sha256msg1_epu32(TMSG3, TMSG0);
+
+ // Rounds 20-23
+ MSG = _mm_add_epi32(TMSG1, _mm_set_epi64x(0x76F988DA5CB0A9DCULL, 0x4A7484AA2DE92C6FULL));
+ STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG);
+ TMP = _mm_alignr_epi8(TMSG1, TMSG0, 4);
+ TMSG2 = _mm_add_epi32(TMSG2, TMP);
+ TMSG2 = _mm_sha256msg2_epu32(TMSG2, TMSG1);
+ MSG = _mm_shuffle_epi32(MSG, 0x0E);
+ STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG);
+ TMSG0 = _mm_sha256msg1_epu32(TMSG0, TMSG1);
+
+ // Rounds 24-27
+ MSG = _mm_add_epi32(TMSG2, _mm_set_epi64x(0xBF597FC7B00327C8ULL, 0xA831C66D983E5152ULL));
+ STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG);
+ TMP = _mm_alignr_epi8(TMSG2, TMSG1, 4);
+ TMSG3 = _mm_add_epi32(TMSG3, TMP);
+ TMSG3 = _mm_sha256msg2_epu32(TMSG3, TMSG2);
+ MSG = _mm_shuffle_epi32(MSG, 0x0E);
+ STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG);
+ TMSG1 = _mm_sha256msg1_epu32(TMSG1, TMSG2);
+
+ // Rounds 28-31
+ MSG = _mm_add_epi32(TMSG3, _mm_set_epi64x(0x1429296706CA6351ULL, 0xD5A79147C6E00BF3ULL));
+ STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG);
+ TMP = _mm_alignr_epi8(TMSG3, TMSG2, 4);
+ TMSG0 = _mm_add_epi32(TMSG0, TMP);
+ TMSG0 = _mm_sha256msg2_epu32(TMSG0, TMSG3);
+ MSG = _mm_shuffle_epi32(MSG, 0x0E);
+ STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG);
+ TMSG2 = _mm_sha256msg1_epu32(TMSG2, TMSG3);
+
+ // Rounds 32-35
+ MSG = _mm_add_epi32(TMSG0, _mm_set_epi64x(0x53380D134D2C6DFCULL, 0x2E1B213827B70A85ULL));
+ STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG);
+ TMP = _mm_alignr_epi8(TMSG0, TMSG3, 4);
+ TMSG1 = _mm_add_epi32(TMSG1, TMP);
+ TMSG1 = _mm_sha256msg2_epu32(TMSG1, TMSG0);
+ MSG = _mm_shuffle_epi32(MSG, 0x0E);
+ STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG);
+ TMSG3 = _mm_sha256msg1_epu32(TMSG3, TMSG0);
+
+ // Rounds 36-39
+ MSG = _mm_add_epi32(TMSG1, _mm_set_epi64x(0x92722C8581C2C92EULL, 0x766A0ABB650A7354ULL));
+ STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG);
+ TMP = _mm_alignr_epi8(TMSG1, TMSG0, 4);
+ TMSG2 = _mm_add_epi32(TMSG2, TMP);
+ TMSG2 = _mm_sha256msg2_epu32(TMSG2, TMSG1);
+ MSG = _mm_shuffle_epi32(MSG, 0x0E);
+ STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG);
+ TMSG0 = _mm_sha256msg1_epu32(TMSG0, TMSG1);
+
+ // Rounds 40-43
+ MSG = _mm_add_epi32(TMSG2, _mm_set_epi64x(0xC76C51A3C24B8B70ULL, 0xA81A664BA2BFE8A1ULL));
+ STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG);
+ TMP = _mm_alignr_epi8(TMSG2, TMSG1, 4);
+ TMSG3 = _mm_add_epi32(TMSG3, TMP);
+ TMSG3 = _mm_sha256msg2_epu32(TMSG3, TMSG2);
+ MSG = _mm_shuffle_epi32(MSG, 0x0E);
+ STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG);
+ TMSG1 = _mm_sha256msg1_epu32(TMSG1, TMSG2);
+
+ // Rounds 44-47
+ MSG = _mm_add_epi32(TMSG3, _mm_set_epi64x(0x106AA070F40E3585ULL, 0xD6990624D192E819ULL));
+ STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG);
+ TMP = _mm_alignr_epi8(TMSG3, TMSG2, 4);
+ TMSG0 = _mm_add_epi32(TMSG0, TMP);
+ TMSG0 = _mm_sha256msg2_epu32(TMSG0, TMSG3);
+ MSG = _mm_shuffle_epi32(MSG, 0x0E);
+ STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG);
+ TMSG2 = _mm_sha256msg1_epu32(TMSG2, TMSG3);
+
+ // Rounds 48-51
+ MSG = _mm_add_epi32(TMSG0, _mm_set_epi64x(0x34B0BCB52748774CULL, 0x1E376C0819A4C116ULL));
+ STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG);
+ TMP = _mm_alignr_epi8(TMSG0, TMSG3, 4);
+ TMSG1 = _mm_add_epi32(TMSG1, TMP);
+ TMSG1 = _mm_sha256msg2_epu32(TMSG1, TMSG0);
+ MSG = _mm_shuffle_epi32(MSG, 0x0E);
+ STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG);
+ TMSG3 = _mm_sha256msg1_epu32(TMSG3, TMSG0);
+
+ // Rounds 52-55
+ MSG = _mm_add_epi32(TMSG1, _mm_set_epi64x(0x682E6FF35B9CCA4FULL, 0x4ED8AA4A391C0CB3ULL));
+ STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG);
+ TMP = _mm_alignr_epi8(TMSG1, TMSG0, 4);
+ TMSG2 = _mm_add_epi32(TMSG2, TMP);
+ TMSG2 = _mm_sha256msg2_epu32(TMSG2, TMSG1);
+ MSG = _mm_shuffle_epi32(MSG, 0x0E);
+ STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG);
+
+ // Rounds 56-59
+ MSG = _mm_add_epi32(TMSG2, _mm_set_epi64x(0x8CC7020884C87814ULL, 0x78A5636F748F82EEULL));
+ STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG);
+ TMP = _mm_alignr_epi8(TMSG2, TMSG1, 4);
+ TMSG3 = _mm_add_epi32(TMSG3, TMP);
+ TMSG3 = _mm_sha256msg2_epu32(TMSG3, TMSG2);
+ MSG = _mm_shuffle_epi32(MSG, 0x0E);
+ STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG);
+
+ // Rounds 60-63
+ MSG = _mm_add_epi32(TMSG3, _mm_set_epi64x(0xC67178F2BEF9A3F7ULL, 0xA4506CEB90BEFFFAULL));
+ STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG);
+ MSG = _mm_shuffle_epi32(MSG, 0x0E);
+ STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG);
+
+ // Add values back to state
+ STATE0 = _mm_add_epi32(STATE0, ABEF_SAVE);
+ STATE1 = _mm_add_epi32(STATE1, CDGH_SAVE);
+
+ input_mm += 4;
+ blocks--;
+ }
+
+ TMP = _mm_shuffle_epi32(STATE0, 0x1B); // FEBA
+ STATE1 = _mm_shuffle_epi32(STATE1, 0xB1); // DCHG
+ STATE0 = _mm_blend_epi16(TMP, STATE1, 0xF0); // DCBA
+ STATE1 = _mm_alignr_epi8(STATE1, TMP, 8); // ABEF
+
+ // Save state
+ _mm_storeu_si128(reinterpret_cast<__m128i*>(&state[0]), STATE0);
+ _mm_storeu_si128(reinterpret_cast<__m128i*>(&state[4]), STATE1);
+ }
+#endif
+
+}
diff --git a/comm/third_party/botan/src/lib/hash/sha2_64/info.txt b/comm/third_party/botan/src/lib/hash/sha2_64/info.txt
new file mode 100644
index 0000000000..6fb415a6bb
--- /dev/null
+++ b/comm/third_party/botan/src/lib/hash/sha2_64/info.txt
@@ -0,0 +1,7 @@
+<defines>
+SHA2_64 -> 20131128
+</defines>
+
+<requires>
+mdx_hash
+</requires>
diff --git a/comm/third_party/botan/src/lib/hash/sha2_64/sha2_64.cpp b/comm/third_party/botan/src/lib/hash/sha2_64/sha2_64.cpp
new file mode 100644
index 0000000000..01abb4f00c
--- /dev/null
+++ b/comm/third_party/botan/src/lib/hash/sha2_64/sha2_64.cpp
@@ -0,0 +1,281 @@
+/*
+* SHA-{384,512}
+* (C) 1999-2011,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/sha2_64.h>
+#include <botan/loadstor.h>
+#include <botan/rotate.h>
+#include <botan/cpuid.h>
+
+namespace Botan {
+
+namespace {
+
+std::string sha512_provider()
+ {
+#if defined(BOTAN_HAS_SHA2_64_BMI2)
+ if(CPUID::has_bmi2())
+ {
+ return "bmi2";
+ }
+#endif
+
+ return "base";
+ }
+
+}
+
+std::unique_ptr<HashFunction> SHA_384::copy_state() const
+ {
+ return std::unique_ptr<HashFunction>(new SHA_384(*this));
+ }
+
+std::unique_ptr<HashFunction> SHA_512::copy_state() const
+ {
+ return std::unique_ptr<HashFunction>(new SHA_512(*this));
+ }
+
+std::unique_ptr<HashFunction> SHA_512_256::copy_state() const
+ {
+ return std::unique_ptr<HashFunction>(new SHA_512_256(*this));
+ }
+
+/*
+* SHA-512 F1 Function
+*
+* Use a macro as many compilers won't inline a function this big,
+* even though it is much faster if inlined.
+*/
+#define SHA2_64_F(A, B, C, D, E, F, G, H, M1, M2, M3, M4, magic) \
+ do { \
+ const uint64_t E_rho = rotr<14>(E) ^ rotr<18>(E) ^ rotr<41>(E); \
+ const uint64_t A_rho = rotr<28>(A) ^ rotr<34>(A) ^ rotr<39>(A); \
+ const uint64_t M2_sigma = rotr<19>(M2) ^ rotr<61>(M2) ^ (M2 >> 6); \
+ const uint64_t M4_sigma = rotr<1>(M4) ^ rotr<8>(M4) ^ (M4 >> 7); \
+ H += magic + E_rho + ((E & F) ^ (~E & G)) + M1; \
+ D += H; \
+ H += A_rho + ((A & B) | ((A | B) & C)); \
+ M1 += M2_sigma + M3 + M4_sigma; \
+ } while(0);
+
+/*
+* SHA-{384,512} Compression Function
+*/
+//static
+void SHA_512::compress_digest(secure_vector<uint64_t>& digest,
+ const uint8_t input[], size_t blocks)
+ {
+#if defined(BOTAN_HAS_SHA2_64_BMI2)
+ if(CPUID::has_bmi2())
+ {
+ return compress_digest_bmi2(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];
+
+ for(size_t i = 0; i != blocks; ++i)
+ {
+ uint64_t W00 = load_be<uint64_t>(input, 0);
+ uint64_t W01 = load_be<uint64_t>(input, 1);
+ uint64_t W02 = load_be<uint64_t>(input, 2);
+ uint64_t W03 = load_be<uint64_t>(input, 3);
+ uint64_t W04 = load_be<uint64_t>(input, 4);
+ uint64_t W05 = load_be<uint64_t>(input, 5);
+ uint64_t W06 = load_be<uint64_t>(input, 6);
+ uint64_t W07 = load_be<uint64_t>(input, 7);
+ uint64_t W08 = load_be<uint64_t>(input, 8);
+ uint64_t W09 = load_be<uint64_t>(input, 9);
+ uint64_t W10 = load_be<uint64_t>(input, 10);
+ uint64_t W11 = load_be<uint64_t>(input, 11);
+ uint64_t W12 = load_be<uint64_t>(input, 12);
+ uint64_t W13 = load_be<uint64_t>(input, 13);
+ uint64_t W14 = load_be<uint64_t>(input, 14);
+ uint64_t W15 = load_be<uint64_t>(input, 15);
+
+ SHA2_64_F(A, B, C, D, E, F, G, H, W00, W14, W09, W01, 0x428A2F98D728AE22);
+ SHA2_64_F(H, A, B, C, D, E, F, G, W01, W15, W10, W02, 0x7137449123EF65CD);
+ SHA2_64_F(G, H, A, B, C, D, E, F, W02, W00, W11, W03, 0xB5C0FBCFEC4D3B2F);
+ SHA2_64_F(F, G, H, A, B, C, D, E, W03, W01, W12, W04, 0xE9B5DBA58189DBBC);
+ SHA2_64_F(E, F, G, H, A, B, C, D, W04, W02, W13, W05, 0x3956C25BF348B538);
+ SHA2_64_F(D, E, F, G, H, A, B, C, W05, W03, W14, W06, 0x59F111F1B605D019);
+ SHA2_64_F(C, D, E, F, G, H, A, B, W06, W04, W15, W07, 0x923F82A4AF194F9B);
+ SHA2_64_F(B, C, D, E, F, G, H, A, W07, W05, W00, W08, 0xAB1C5ED5DA6D8118);
+ SHA2_64_F(A, B, C, D, E, F, G, H, W08, W06, W01, W09, 0xD807AA98A3030242);
+ SHA2_64_F(H, A, B, C, D, E, F, G, W09, W07, W02, W10, 0x12835B0145706FBE);
+ SHA2_64_F(G, H, A, B, C, D, E, F, W10, W08, W03, W11, 0x243185BE4EE4B28C);
+ SHA2_64_F(F, G, H, A, B, C, D, E, W11, W09, W04, W12, 0x550C7DC3D5FFB4E2);
+ SHA2_64_F(E, F, G, H, A, B, C, D, W12, W10, W05, W13, 0x72BE5D74F27B896F);
+ SHA2_64_F(D, E, F, G, H, A, B, C, W13, W11, W06, W14, 0x80DEB1FE3B1696B1);
+ SHA2_64_F(C, D, E, F, G, H, A, B, W14, W12, W07, W15, 0x9BDC06A725C71235);
+ SHA2_64_F(B, C, D, E, F, G, H, A, W15, W13, W08, W00, 0xC19BF174CF692694);
+ SHA2_64_F(A, B, C, D, E, F, G, H, W00, W14, W09, W01, 0xE49B69C19EF14AD2);
+ SHA2_64_F(H, A, B, C, D, E, F, G, W01, W15, W10, W02, 0xEFBE4786384F25E3);
+ SHA2_64_F(G, H, A, B, C, D, E, F, W02, W00, W11, W03, 0x0FC19DC68B8CD5B5);
+ SHA2_64_F(F, G, H, A, B, C, D, E, W03, W01, W12, W04, 0x240CA1CC77AC9C65);
+ SHA2_64_F(E, F, G, H, A, B, C, D, W04, W02, W13, W05, 0x2DE92C6F592B0275);
+ SHA2_64_F(D, E, F, G, H, A, B, C, W05, W03, W14, W06, 0x4A7484AA6EA6E483);
+ SHA2_64_F(C, D, E, F, G, H, A, B, W06, W04, W15, W07, 0x5CB0A9DCBD41FBD4);
+ SHA2_64_F(B, C, D, E, F, G, H, A, W07, W05, W00, W08, 0x76F988DA831153B5);
+ SHA2_64_F(A, B, C, D, E, F, G, H, W08, W06, W01, W09, 0x983E5152EE66DFAB);
+ SHA2_64_F(H, A, B, C, D, E, F, G, W09, W07, W02, W10, 0xA831C66D2DB43210);
+ SHA2_64_F(G, H, A, B, C, D, E, F, W10, W08, W03, W11, 0xB00327C898FB213F);
+ SHA2_64_F(F, G, H, A, B, C, D, E, W11, W09, W04, W12, 0xBF597FC7BEEF0EE4);
+ SHA2_64_F(E, F, G, H, A, B, C, D, W12, W10, W05, W13, 0xC6E00BF33DA88FC2);
+ SHA2_64_F(D, E, F, G, H, A, B, C, W13, W11, W06, W14, 0xD5A79147930AA725);
+ SHA2_64_F(C, D, E, F, G, H, A, B, W14, W12, W07, W15, 0x06CA6351E003826F);
+ SHA2_64_F(B, C, D, E, F, G, H, A, W15, W13, W08, W00, 0x142929670A0E6E70);
+ SHA2_64_F(A, B, C, D, E, F, G, H, W00, W14, W09, W01, 0x27B70A8546D22FFC);
+ SHA2_64_F(H, A, B, C, D, E, F, G, W01, W15, W10, W02, 0x2E1B21385C26C926);
+ SHA2_64_F(G, H, A, B, C, D, E, F, W02, W00, W11, W03, 0x4D2C6DFC5AC42AED);
+ SHA2_64_F(F, G, H, A, B, C, D, E, W03, W01, W12, W04, 0x53380D139D95B3DF);
+ SHA2_64_F(E, F, G, H, A, B, C, D, W04, W02, W13, W05, 0x650A73548BAF63DE);
+ SHA2_64_F(D, E, F, G, H, A, B, C, W05, W03, W14, W06, 0x766A0ABB3C77B2A8);
+ SHA2_64_F(C, D, E, F, G, H, A, B, W06, W04, W15, W07, 0x81C2C92E47EDAEE6);
+ SHA2_64_F(B, C, D, E, F, G, H, A, W07, W05, W00, W08, 0x92722C851482353B);
+ SHA2_64_F(A, B, C, D, E, F, G, H, W08, W06, W01, W09, 0xA2BFE8A14CF10364);
+ SHA2_64_F(H, A, B, C, D, E, F, G, W09, W07, W02, W10, 0xA81A664BBC423001);
+ SHA2_64_F(G, H, A, B, C, D, E, F, W10, W08, W03, W11, 0xC24B8B70D0F89791);
+ SHA2_64_F(F, G, H, A, B, C, D, E, W11, W09, W04, W12, 0xC76C51A30654BE30);
+ SHA2_64_F(E, F, G, H, A, B, C, D, W12, W10, W05, W13, 0xD192E819D6EF5218);
+ SHA2_64_F(D, E, F, G, H, A, B, C, W13, W11, W06, W14, 0xD69906245565A910);
+ SHA2_64_F(C, D, E, F, G, H, A, B, W14, W12, W07, W15, 0xF40E35855771202A);
+ SHA2_64_F(B, C, D, E, F, G, H, A, W15, W13, W08, W00, 0x106AA07032BBD1B8);
+ SHA2_64_F(A, B, C, D, E, F, G, H, W00, W14, W09, W01, 0x19A4C116B8D2D0C8);
+ SHA2_64_F(H, A, B, C, D, E, F, G, W01, W15, W10, W02, 0x1E376C085141AB53);
+ SHA2_64_F(G, H, A, B, C, D, E, F, W02, W00, W11, W03, 0x2748774CDF8EEB99);
+ SHA2_64_F(F, G, H, A, B, C, D, E, W03, W01, W12, W04, 0x34B0BCB5E19B48A8);
+ SHA2_64_F(E, F, G, H, A, B, C, D, W04, W02, W13, W05, 0x391C0CB3C5C95A63);
+ SHA2_64_F(D, E, F, G, H, A, B, C, W05, W03, W14, W06, 0x4ED8AA4AE3418ACB);
+ SHA2_64_F(C, D, E, F, G, H, A, B, W06, W04, W15, W07, 0x5B9CCA4F7763E373);
+ SHA2_64_F(B, C, D, E, F, G, H, A, W07, W05, W00, W08, 0x682E6FF3D6B2B8A3);
+ SHA2_64_F(A, B, C, D, E, F, G, H, W08, W06, W01, W09, 0x748F82EE5DEFB2FC);
+ SHA2_64_F(H, A, B, C, D, E, F, G, W09, W07, W02, W10, 0x78A5636F43172F60);
+ SHA2_64_F(G, H, A, B, C, D, E, F, W10, W08, W03, W11, 0x84C87814A1F0AB72);
+ SHA2_64_F(F, G, H, A, B, C, D, E, W11, W09, W04, W12, 0x8CC702081A6439EC);
+ SHA2_64_F(E, F, G, H, A, B, C, D, W12, W10, W05, W13, 0x90BEFFFA23631E28);
+ SHA2_64_F(D, E, F, G, H, A, B, C, W13, W11, W06, W14, 0xA4506CEBDE82BDE9);
+ SHA2_64_F(C, D, E, F, G, H, A, B, W14, W12, W07, W15, 0xBEF9A3F7B2C67915);
+ SHA2_64_F(B, C, D, E, F, G, H, A, W15, W13, W08, W00, 0xC67178F2E372532B);
+ SHA2_64_F(A, B, C, D, E, F, G, H, W00, W14, W09, W01, 0xCA273ECEEA26619C);
+ SHA2_64_F(H, A, B, C, D, E, F, G, W01, W15, W10, W02, 0xD186B8C721C0C207);
+ SHA2_64_F(G, H, A, B, C, D, E, F, W02, W00, W11, W03, 0xEADA7DD6CDE0EB1E);
+ SHA2_64_F(F, G, H, A, B, C, D, E, W03, W01, W12, W04, 0xF57D4F7FEE6ED178);
+ SHA2_64_F(E, F, G, H, A, B, C, D, W04, W02, W13, W05, 0x06F067AA72176FBA);
+ SHA2_64_F(D, E, F, G, H, A, B, C, W05, W03, W14, W06, 0x0A637DC5A2C898A6);
+ SHA2_64_F(C, D, E, F, G, H, A, B, W06, W04, W15, W07, 0x113F9804BEF90DAE);
+ SHA2_64_F(B, C, D, E, F, G, H, A, W07, W05, W00, W08, 0x1B710B35131C471B);
+ SHA2_64_F(A, B, C, D, E, F, G, H, W08, W06, W01, W09, 0x28DB77F523047D84);
+ SHA2_64_F(H, A, B, C, D, E, F, G, W09, W07, W02, W10, 0x32CAAB7B40C72493);
+ SHA2_64_F(G, H, A, B, C, D, E, F, W10, W08, W03, W11, 0x3C9EBE0A15C9BEBC);
+ SHA2_64_F(F, G, H, A, B, C, D, E, W11, W09, W04, W12, 0x431D67C49C100D4C);
+ SHA2_64_F(E, F, G, H, A, B, C, D, W12, W10, W05, W13, 0x4CC5D4BECB3E42B6);
+ SHA2_64_F(D, E, F, G, H, A, B, C, W13, W11, W06, W14, 0x597F299CFC657E2A);
+ SHA2_64_F(C, D, E, F, G, H, A, B, W14, W12, W07, W15, 0x5FCB6FAB3AD6FAEC);
+ SHA2_64_F(B, C, D, E, F, G, H, A, W15, W13, W08, W00, 0x6C44198C4A475817);
+
+ 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);
+
+ input += 128;
+ }
+ }
+
+#undef SHA2_64_F
+
+std::string SHA_512_256::provider() const
+ {
+ return sha512_provider();
+ }
+
+std::string SHA_384::provider() const
+ {
+ return sha512_provider();
+ }
+
+std::string SHA_512::provider() const
+ {
+ return sha512_provider();
+ }
+
+void SHA_512_256::compress_n(const uint8_t input[], size_t blocks)
+ {
+ SHA_512::compress_digest(m_digest, input, blocks);
+ }
+
+void SHA_384::compress_n(const uint8_t input[], size_t blocks)
+ {
+ SHA_512::compress_digest(m_digest, input, blocks);
+ }
+
+void SHA_512::compress_n(const uint8_t input[], size_t blocks)
+ {
+ SHA_512::compress_digest(m_digest, input, blocks);
+ }
+
+void SHA_512_256::copy_out(uint8_t output[])
+ {
+ copy_out_vec_be(output, output_length(), m_digest);
+ }
+
+void SHA_384::copy_out(uint8_t output[])
+ {
+ copy_out_vec_be(output, output_length(), m_digest);
+ }
+
+void SHA_512::copy_out(uint8_t output[])
+ {
+ copy_out_vec_be(output, output_length(), m_digest);
+ }
+
+void SHA_512_256::clear()
+ {
+ MDx_HashFunction::clear();
+ m_digest[0] = 0x22312194FC2BF72C;
+ m_digest[1] = 0x9F555FA3C84C64C2;
+ m_digest[2] = 0x2393B86B6F53B151;
+ m_digest[3] = 0x963877195940EABD;
+ m_digest[4] = 0x96283EE2A88EFFE3;
+ m_digest[5] = 0xBE5E1E2553863992;
+ m_digest[6] = 0x2B0199FC2C85B8AA;
+ m_digest[7] = 0x0EB72DDC81C52CA2;
+ }
+
+void SHA_384::clear()
+ {
+ MDx_HashFunction::clear();
+ m_digest[0] = 0xCBBB9D5DC1059ED8;
+ m_digest[1] = 0x629A292A367CD507;
+ m_digest[2] = 0x9159015A3070DD17;
+ m_digest[3] = 0x152FECD8F70E5939;
+ m_digest[4] = 0x67332667FFC00B31;
+ m_digest[5] = 0x8EB44A8768581511;
+ m_digest[6] = 0xDB0C2E0D64F98FA7;
+ m_digest[7] = 0x47B5481DBEFA4FA4;
+ }
+
+void SHA_512::clear()
+ {
+ MDx_HashFunction::clear();
+ m_digest[0] = 0x6A09E667F3BCC908;
+ m_digest[1] = 0xBB67AE8584CAA73B;
+ m_digest[2] = 0x3C6EF372FE94F82B;
+ m_digest[3] = 0xA54FF53A5F1D36F1;
+ m_digest[4] = 0x510E527FADE682D1;
+ m_digest[5] = 0x9B05688C2B3E6C1F;
+ m_digest[6] = 0x1F83D9ABFB41BD6B;
+ m_digest[7] = 0x5BE0CD19137E2179;
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/hash/sha2_64/sha2_64.h b/comm/third_party/botan/src/lib/hash/sha2_64/sha2_64.h
new file mode 100644
index 0000000000..dc5f0dd183
--- /dev/null
+++ b/comm/third_party/botan/src/lib/hash/sha2_64/sha2_64.h
@@ -0,0 +1,102 @@
+/*
+* SHA-{384,512}
+* (C) 1999-2010,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_SHA_64BIT_H_
+#define BOTAN_SHA_64BIT_H_
+
+#include <botan/mdx_hash.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(sha2_64.h)
+
+namespace Botan {
+
+/**
+* SHA-384
+*/
+class BOTAN_PUBLIC_API(2,0) SHA_384 final : public MDx_HashFunction
+ {
+ public:
+ std::string name() const override { return "SHA-384"; }
+ size_t output_length() const override { return 48; }
+ HashFunction* clone() const override { return new SHA_384; }
+ std::unique_ptr<HashFunction> copy_state() const override;
+ std::string provider() const override;
+
+ void clear() override;
+
+ SHA_384() : MDx_HashFunction(128, true, true, 16), m_digest(8)
+ { clear(); }
+ private:
+ void compress_n(const uint8_t[], size_t blocks) override;
+ void copy_out(uint8_t[]) override;
+
+ secure_vector<uint64_t> m_digest;
+ };
+
+/**
+* SHA-512
+*/
+class BOTAN_PUBLIC_API(2,0) SHA_512 final : public MDx_HashFunction
+ {
+ public:
+ std::string name() const override { return "SHA-512"; }
+ size_t output_length() const override { return 64; }
+ HashFunction* clone() const override { return new SHA_512; }
+ std::unique_ptr<HashFunction> copy_state() const override;
+ std::string provider() const override;
+
+ void clear() override;
+
+ /*
+ * Perform a SHA-512 compression. For internal use
+ */
+ static void compress_digest(secure_vector<uint64_t>& digest,
+ const uint8_t input[],
+ size_t blocks);
+
+ SHA_512() : MDx_HashFunction(128, true, true, 16), m_digest(8)
+ { clear(); }
+ private:
+ void compress_n(const uint8_t[], size_t blocks) override;
+ void copy_out(uint8_t[]) override;
+
+ static const uint64_t K[80];
+
+#if defined(BOTAN_HAS_SHA2_64_BMI2)
+ static void compress_digest_bmi2(secure_vector<uint64_t>& digest,
+ const uint8_t input[],
+ size_t blocks);
+#endif
+
+ secure_vector<uint64_t> m_digest;
+ };
+
+/**
+* SHA-512/256
+*/
+class BOTAN_PUBLIC_API(2,0) SHA_512_256 final : public MDx_HashFunction
+ {
+ public:
+ std::string name() const override { return "SHA-512-256"; }
+ size_t output_length() const override { return 32; }
+ HashFunction* clone() const override { return new SHA_512_256; }
+ std::unique_ptr<HashFunction> copy_state() const override;
+ std::string provider() const override;
+
+ void clear() override;
+
+ SHA_512_256() : MDx_HashFunction(128, true, true, 16), m_digest(8) { clear(); }
+ private:
+ void compress_n(const uint8_t[], size_t blocks) override;
+ void copy_out(uint8_t[]) override;
+
+ secure_vector<uint64_t> m_digest;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/hash/sha2_64/sha2_64_bmi2/info.txt b/comm/third_party/botan/src/lib/hash/sha2_64/sha2_64_bmi2/info.txt
new file mode 100644
index 0000000000..0a2ba10fb8
--- /dev/null
+++ b/comm/third_party/botan/src/lib/hash/sha2_64/sha2_64_bmi2/info.txt
@@ -0,0 +1,17 @@
+<defines>
+SHA2_64_BMI2 -> 20190117
+</defines>
+
+<isa>
+bmi2
+</isa>
+
+# Needs 64-bit registers to be useful
+<arch>
+x86_64
+</arch>
+
+<cc>
+gcc
+clang
+</cc>
diff --git a/comm/third_party/botan/src/lib/hash/sha2_64/sha2_64_bmi2/sha2_64_bmi2.cpp b/comm/third_party/botan/src/lib/hash/sha2_64/sha2_64_bmi2/sha2_64_bmi2.cpp
new file mode 100644
index 0000000000..1ef0ecc56a
--- /dev/null
+++ b/comm/third_party/botan/src/lib/hash/sha2_64/sha2_64_bmi2/sha2_64_bmi2.cpp
@@ -0,0 +1,153 @@
+/*
+* (C) 2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/sha2_64.h>
+#include <botan/loadstor.h>
+#include <botan/rotate.h>
+
+namespace Botan {
+
+/*
+* SHA-512 F1 Function
+*
+* Use a macro as many compilers won't inline a function this big,
+* even though it is much faster if inlined.
+*/
+#define SHA2_64_F(A, B, C, D, E, F, G, H, M1, M2, M3, M4, magic) \
+ do { \
+ const uint64_t E_rho = rotr<14>(E) ^ rotr<18>(E) ^ rotr<41>(E); \
+ const uint64_t A_rho = rotr<28>(A) ^ rotr<34>(A) ^ rotr<39>(A); \
+ const uint64_t M2_sigma = rotr<19>(M2) ^ rotr<61>(M2) ^ (M2 >> 6); \
+ const uint64_t M4_sigma = rotr<1>(M4) ^ rotr<8>(M4) ^ (M4 >> 7); \
+ H += magic + E_rho + ((E & F) ^ (~E & G)) + M1; \
+ D += H; \
+ H += A_rho + ((A & B) | ((A | B) & C)); \
+ M1 += M2_sigma + M3 + M4_sigma; \
+ } while(0);
+
+void SHA_512::compress_digest_bmi2(secure_vector<uint64_t>& digest,
+ const uint8_t 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];
+
+ for(size_t i = 0; i != blocks; ++i)
+ {
+ uint64_t W00 = load_be<uint64_t>(input, 0);
+ uint64_t W01 = load_be<uint64_t>(input, 1);
+ uint64_t W02 = load_be<uint64_t>(input, 2);
+ uint64_t W03 = load_be<uint64_t>(input, 3);
+ uint64_t W04 = load_be<uint64_t>(input, 4);
+ uint64_t W05 = load_be<uint64_t>(input, 5);
+ uint64_t W06 = load_be<uint64_t>(input, 6);
+ uint64_t W07 = load_be<uint64_t>(input, 7);
+ uint64_t W08 = load_be<uint64_t>(input, 8);
+ uint64_t W09 = load_be<uint64_t>(input, 9);
+ uint64_t W10 = load_be<uint64_t>(input, 10);
+ uint64_t W11 = load_be<uint64_t>(input, 11);
+ uint64_t W12 = load_be<uint64_t>(input, 12);
+ uint64_t W13 = load_be<uint64_t>(input, 13);
+ uint64_t W14 = load_be<uint64_t>(input, 14);
+ uint64_t W15 = load_be<uint64_t>(input, 15);
+
+ SHA2_64_F(A, B, C, D, E, F, G, H, W00, W14, W09, W01, 0x428A2F98D728AE22);
+ SHA2_64_F(H, A, B, C, D, E, F, G, W01, W15, W10, W02, 0x7137449123EF65CD);
+ SHA2_64_F(G, H, A, B, C, D, E, F, W02, W00, W11, W03, 0xB5C0FBCFEC4D3B2F);
+ SHA2_64_F(F, G, H, A, B, C, D, E, W03, W01, W12, W04, 0xE9B5DBA58189DBBC);
+ SHA2_64_F(E, F, G, H, A, B, C, D, W04, W02, W13, W05, 0x3956C25BF348B538);
+ SHA2_64_F(D, E, F, G, H, A, B, C, W05, W03, W14, W06, 0x59F111F1B605D019);
+ SHA2_64_F(C, D, E, F, G, H, A, B, W06, W04, W15, W07, 0x923F82A4AF194F9B);
+ SHA2_64_F(B, C, D, E, F, G, H, A, W07, W05, W00, W08, 0xAB1C5ED5DA6D8118);
+ SHA2_64_F(A, B, C, D, E, F, G, H, W08, W06, W01, W09, 0xD807AA98A3030242);
+ SHA2_64_F(H, A, B, C, D, E, F, G, W09, W07, W02, W10, 0x12835B0145706FBE);
+ SHA2_64_F(G, H, A, B, C, D, E, F, W10, W08, W03, W11, 0x243185BE4EE4B28C);
+ SHA2_64_F(F, G, H, A, B, C, D, E, W11, W09, W04, W12, 0x550C7DC3D5FFB4E2);
+ SHA2_64_F(E, F, G, H, A, B, C, D, W12, W10, W05, W13, 0x72BE5D74F27B896F);
+ SHA2_64_F(D, E, F, G, H, A, B, C, W13, W11, W06, W14, 0x80DEB1FE3B1696B1);
+ SHA2_64_F(C, D, E, F, G, H, A, B, W14, W12, W07, W15, 0x9BDC06A725C71235);
+ SHA2_64_F(B, C, D, E, F, G, H, A, W15, W13, W08, W00, 0xC19BF174CF692694);
+ SHA2_64_F(A, B, C, D, E, F, G, H, W00, W14, W09, W01, 0xE49B69C19EF14AD2);
+ SHA2_64_F(H, A, B, C, D, E, F, G, W01, W15, W10, W02, 0xEFBE4786384F25E3);
+ SHA2_64_F(G, H, A, B, C, D, E, F, W02, W00, W11, W03, 0x0FC19DC68B8CD5B5);
+ SHA2_64_F(F, G, H, A, B, C, D, E, W03, W01, W12, W04, 0x240CA1CC77AC9C65);
+ SHA2_64_F(E, F, G, H, A, B, C, D, W04, W02, W13, W05, 0x2DE92C6F592B0275);
+ SHA2_64_F(D, E, F, G, H, A, B, C, W05, W03, W14, W06, 0x4A7484AA6EA6E483);
+ SHA2_64_F(C, D, E, F, G, H, A, B, W06, W04, W15, W07, 0x5CB0A9DCBD41FBD4);
+ SHA2_64_F(B, C, D, E, F, G, H, A, W07, W05, W00, W08, 0x76F988DA831153B5);
+ SHA2_64_F(A, B, C, D, E, F, G, H, W08, W06, W01, W09, 0x983E5152EE66DFAB);
+ SHA2_64_F(H, A, B, C, D, E, F, G, W09, W07, W02, W10, 0xA831C66D2DB43210);
+ SHA2_64_F(G, H, A, B, C, D, E, F, W10, W08, W03, W11, 0xB00327C898FB213F);
+ SHA2_64_F(F, G, H, A, B, C, D, E, W11, W09, W04, W12, 0xBF597FC7BEEF0EE4);
+ SHA2_64_F(E, F, G, H, A, B, C, D, W12, W10, W05, W13, 0xC6E00BF33DA88FC2);
+ SHA2_64_F(D, E, F, G, H, A, B, C, W13, W11, W06, W14, 0xD5A79147930AA725);
+ SHA2_64_F(C, D, E, F, G, H, A, B, W14, W12, W07, W15, 0x06CA6351E003826F);
+ SHA2_64_F(B, C, D, E, F, G, H, A, W15, W13, W08, W00, 0x142929670A0E6E70);
+ SHA2_64_F(A, B, C, D, E, F, G, H, W00, W14, W09, W01, 0x27B70A8546D22FFC);
+ SHA2_64_F(H, A, B, C, D, E, F, G, W01, W15, W10, W02, 0x2E1B21385C26C926);
+ SHA2_64_F(G, H, A, B, C, D, E, F, W02, W00, W11, W03, 0x4D2C6DFC5AC42AED);
+ SHA2_64_F(F, G, H, A, B, C, D, E, W03, W01, W12, W04, 0x53380D139D95B3DF);
+ SHA2_64_F(E, F, G, H, A, B, C, D, W04, W02, W13, W05, 0x650A73548BAF63DE);
+ SHA2_64_F(D, E, F, G, H, A, B, C, W05, W03, W14, W06, 0x766A0ABB3C77B2A8);
+ SHA2_64_F(C, D, E, F, G, H, A, B, W06, W04, W15, W07, 0x81C2C92E47EDAEE6);
+ SHA2_64_F(B, C, D, E, F, G, H, A, W07, W05, W00, W08, 0x92722C851482353B);
+ SHA2_64_F(A, B, C, D, E, F, G, H, W08, W06, W01, W09, 0xA2BFE8A14CF10364);
+ SHA2_64_F(H, A, B, C, D, E, F, G, W09, W07, W02, W10, 0xA81A664BBC423001);
+ SHA2_64_F(G, H, A, B, C, D, E, F, W10, W08, W03, W11, 0xC24B8B70D0F89791);
+ SHA2_64_F(F, G, H, A, B, C, D, E, W11, W09, W04, W12, 0xC76C51A30654BE30);
+ SHA2_64_F(E, F, G, H, A, B, C, D, W12, W10, W05, W13, 0xD192E819D6EF5218);
+ SHA2_64_F(D, E, F, G, H, A, B, C, W13, W11, W06, W14, 0xD69906245565A910);
+ SHA2_64_F(C, D, E, F, G, H, A, B, W14, W12, W07, W15, 0xF40E35855771202A);
+ SHA2_64_F(B, C, D, E, F, G, H, A, W15, W13, W08, W00, 0x106AA07032BBD1B8);
+ SHA2_64_F(A, B, C, D, E, F, G, H, W00, W14, W09, W01, 0x19A4C116B8D2D0C8);
+ SHA2_64_F(H, A, B, C, D, E, F, G, W01, W15, W10, W02, 0x1E376C085141AB53);
+ SHA2_64_F(G, H, A, B, C, D, E, F, W02, W00, W11, W03, 0x2748774CDF8EEB99);
+ SHA2_64_F(F, G, H, A, B, C, D, E, W03, W01, W12, W04, 0x34B0BCB5E19B48A8);
+ SHA2_64_F(E, F, G, H, A, B, C, D, W04, W02, W13, W05, 0x391C0CB3C5C95A63);
+ SHA2_64_F(D, E, F, G, H, A, B, C, W05, W03, W14, W06, 0x4ED8AA4AE3418ACB);
+ SHA2_64_F(C, D, E, F, G, H, A, B, W06, W04, W15, W07, 0x5B9CCA4F7763E373);
+ SHA2_64_F(B, C, D, E, F, G, H, A, W07, W05, W00, W08, 0x682E6FF3D6B2B8A3);
+ SHA2_64_F(A, B, C, D, E, F, G, H, W08, W06, W01, W09, 0x748F82EE5DEFB2FC);
+ SHA2_64_F(H, A, B, C, D, E, F, G, W09, W07, W02, W10, 0x78A5636F43172F60);
+ SHA2_64_F(G, H, A, B, C, D, E, F, W10, W08, W03, W11, 0x84C87814A1F0AB72);
+ SHA2_64_F(F, G, H, A, B, C, D, E, W11, W09, W04, W12, 0x8CC702081A6439EC);
+ SHA2_64_F(E, F, G, H, A, B, C, D, W12, W10, W05, W13, 0x90BEFFFA23631E28);
+ SHA2_64_F(D, E, F, G, H, A, B, C, W13, W11, W06, W14, 0xA4506CEBDE82BDE9);
+ SHA2_64_F(C, D, E, F, G, H, A, B, W14, W12, W07, W15, 0xBEF9A3F7B2C67915);
+ SHA2_64_F(B, C, D, E, F, G, H, A, W15, W13, W08, W00, 0xC67178F2E372532B);
+ SHA2_64_F(A, B, C, D, E, F, G, H, W00, W14, W09, W01, 0xCA273ECEEA26619C);
+ SHA2_64_F(H, A, B, C, D, E, F, G, W01, W15, W10, W02, 0xD186B8C721C0C207);
+ SHA2_64_F(G, H, A, B, C, D, E, F, W02, W00, W11, W03, 0xEADA7DD6CDE0EB1E);
+ SHA2_64_F(F, G, H, A, B, C, D, E, W03, W01, W12, W04, 0xF57D4F7FEE6ED178);
+ SHA2_64_F(E, F, G, H, A, B, C, D, W04, W02, W13, W05, 0x06F067AA72176FBA);
+ SHA2_64_F(D, E, F, G, H, A, B, C, W05, W03, W14, W06, 0x0A637DC5A2C898A6);
+ SHA2_64_F(C, D, E, F, G, H, A, B, W06, W04, W15, W07, 0x113F9804BEF90DAE);
+ SHA2_64_F(B, C, D, E, F, G, H, A, W07, W05, W00, W08, 0x1B710B35131C471B);
+ SHA2_64_F(A, B, C, D, E, F, G, H, W08, W06, W01, W09, 0x28DB77F523047D84);
+ SHA2_64_F(H, A, B, C, D, E, F, G, W09, W07, W02, W10, 0x32CAAB7B40C72493);
+ SHA2_64_F(G, H, A, B, C, D, E, F, W10, W08, W03, W11, 0x3C9EBE0A15C9BEBC);
+ SHA2_64_F(F, G, H, A, B, C, D, E, W11, W09, W04, W12, 0x431D67C49C100D4C);
+ SHA2_64_F(E, F, G, H, A, B, C, D, W12, W10, W05, W13, 0x4CC5D4BECB3E42B6);
+ SHA2_64_F(D, E, F, G, H, A, B, C, W13, W11, W06, W14, 0x597F299CFC657E2A);
+ SHA2_64_F(C, D, E, F, G, H, A, B, W14, W12, W07, W15, 0x5FCB6FAB3AD6FAEC);
+ SHA2_64_F(B, C, D, E, F, G, H, A, W15, W13, W08, W00, 0x6C44198C4A475817);
+
+ 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);
+
+ input += 128;
+ }
+ }
+
+#undef SHA2_64_F
+
+}
diff --git a/comm/third_party/botan/src/lib/hash/sha3/info.txt b/comm/third_party/botan/src/lib/hash/sha3/info.txt
new file mode 100644
index 0000000000..76ae81bd8f
--- /dev/null
+++ b/comm/third_party/botan/src/lib/hash/sha3/info.txt
@@ -0,0 +1,3 @@
+<defines>
+SHA3 -> 20161018
+</defines>
diff --git a/comm/third_party/botan/src/lib/hash/sha3/sha3.cpp b/comm/third_party/botan/src/lib/hash/sha3/sha3.cpp
new file mode 100644
index 0000000000..289e451ffa
--- /dev/null
+++ b/comm/third_party/botan/src/lib/hash/sha3/sha3.cpp
@@ -0,0 +1,293 @@
+/*
+* SHA-3
+* (C) 2010,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/sha3.h>
+#include <botan/loadstor.h>
+#include <botan/rotate.h>
+#include <botan/exceptn.h>
+#include <botan/cpuid.h>
+
+#include <tuple>
+
+namespace Botan {
+
+namespace {
+
+// This is a workaround for a suspected bug in clang 12 (and XCode 13)
+// that caused a miscompile of the SHA3 implementation for optimization
+// level -O2 and higher.
+//
+// For details, see: https://github.com/randombit/botan/issues/2802
+#if defined(__clang__) && \
+ (( defined(__apple_build_version__) && __clang_major__ == 13) || \
+ (!defined(__apple_build_version__) && __clang_major__ == 12))
+#define BOTAN_WORKAROUND_MAYBE_INLINE __attribute__((noinline))
+#else
+#define BOTAN_WORKAROUND_MAYBE_INLINE inline
+#endif
+
+BOTAN_WORKAROUND_MAYBE_INLINE std::tuple<uint64_t, uint64_t, uint64_t, uint64_t, uint64_t>
+ xor_CNs(const uint64_t A[25])
+ {
+ return {
+ A[0] ^ A[5] ^ A[10] ^ A[15] ^ A[20],
+ A[1] ^ A[6] ^ A[11] ^ A[16] ^ A[21],
+ A[2] ^ A[7] ^ A[12] ^ A[17] ^ A[22],
+ A[3] ^ A[8] ^ A[13] ^ A[18] ^ A[23],
+ A[4] ^ A[9] ^ A[14] ^ A[19] ^ A[24]};
+ }
+
+#undef BOTAN_WORKAROUND_MAYBE_INLINE
+
+inline void SHA3_round(uint64_t T[25], const uint64_t A[25], uint64_t RC)
+ {
+ const auto Cs = xor_CNs(A);
+
+ const uint64_t D0 = rotl<1>(std::get<0>(Cs)) ^ std::get<3>(Cs);
+ const uint64_t D1 = rotl<1>(std::get<1>(Cs)) ^ std::get<4>(Cs);
+ const uint64_t D2 = rotl<1>(std::get<2>(Cs)) ^ std::get<0>(Cs);
+ const uint64_t D3 = rotl<1>(std::get<3>(Cs)) ^ std::get<1>(Cs);
+ const uint64_t D4 = rotl<1>(std::get<4>(Cs)) ^ std::get<2>(Cs);
+
+ const uint64_t B00 = A[ 0] ^ D1;
+ const uint64_t B01 = rotl<44>(A[ 6] ^ D2);
+ const uint64_t B02 = rotl<43>(A[12] ^ D3);
+ const uint64_t B03 = rotl<21>(A[18] ^ D4);
+ const uint64_t B04 = rotl<14>(A[24] ^ D0);
+ T[ 0] = B00 ^ (~B01 & B02) ^ RC;
+ T[ 1] = B01 ^ (~B02 & B03);
+ T[ 2] = B02 ^ (~B03 & B04);
+ T[ 3] = B03 ^ (~B04 & B00);
+ T[ 4] = B04 ^ (~B00 & B01);
+
+ const uint64_t B05 = rotl<28>(A[ 3] ^ D4);
+ const uint64_t B06 = rotl<20>(A[ 9] ^ D0);
+ const uint64_t B07 = rotl< 3>(A[10] ^ D1);
+ const uint64_t B08 = rotl<45>(A[16] ^ D2);
+ const uint64_t B09 = rotl<61>(A[22] ^ D3);
+ T[ 5] = B05 ^ (~B06 & B07);
+ T[ 6] = B06 ^ (~B07 & B08);
+ T[ 7] = B07 ^ (~B08 & B09);
+ T[ 8] = B08 ^ (~B09 & B05);
+ T[ 9] = B09 ^ (~B05 & B06);
+
+ const uint64_t B10 = rotl< 1>(A[ 1] ^ D2);
+ const uint64_t B11 = rotl< 6>(A[ 7] ^ D3);
+ const uint64_t B12 = rotl<25>(A[13] ^ D4);
+ const uint64_t B13 = rotl< 8>(A[19] ^ D0);
+ const uint64_t B14 = rotl<18>(A[20] ^ D1);
+ T[10] = B10 ^ (~B11 & B12);
+ T[11] = B11 ^ (~B12 & B13);
+ T[12] = B12 ^ (~B13 & B14);
+ T[13] = B13 ^ (~B14 & B10);
+ T[14] = B14 ^ (~B10 & B11);
+
+ const uint64_t B15 = rotl<27>(A[ 4] ^ D0);
+ const uint64_t B16 = rotl<36>(A[ 5] ^ D1);
+ const uint64_t B17 = rotl<10>(A[11] ^ D2);
+ const uint64_t B18 = rotl<15>(A[17] ^ D3);
+ const uint64_t B19 = rotl<56>(A[23] ^ D4);
+ T[15] = B15 ^ (~B16 & B17);
+ T[16] = B16 ^ (~B17 & B18);
+ T[17] = B17 ^ (~B18 & B19);
+ T[18] = B18 ^ (~B19 & B15);
+ T[19] = B19 ^ (~B15 & B16);
+
+ const uint64_t B20 = rotl<62>(A[ 2] ^ D3);
+ const uint64_t B21 = rotl<55>(A[ 8] ^ D4);
+ const uint64_t B22 = rotl<39>(A[14] ^ D0);
+ const uint64_t B23 = rotl<41>(A[15] ^ D1);
+ const uint64_t B24 = rotl< 2>(A[21] ^ D2);
+ T[20] = B20 ^ (~B21 & B22);
+ T[21] = B21 ^ (~B22 & B23);
+ T[22] = B22 ^ (~B23 & B24);
+ T[23] = B23 ^ (~B24 & B20);
+ T[24] = B24 ^ (~B20 & B21);
+ }
+
+}
+
+//static
+void SHA_3::permute(uint64_t A[25])
+ {
+#if defined(BOTAN_HAS_SHA3_BMI2)
+ if(CPUID::has_bmi2())
+ {
+ return permute_bmi2(A);
+ }
+#endif
+
+ 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
+ };
+
+ uint64_t T[25];
+
+ for(size_t i = 0; i != 24; i += 2)
+ {
+ SHA3_round(T, A, RC[i+0]);
+ SHA3_round(A, T, RC[i+1]);
+ }
+ }
+
+//static
+size_t SHA_3::absorb(size_t bitrate,
+ secure_vector<uint64_t>& S, size_t S_pos,
+ const uint8_t input[], size_t length)
+ {
+ while(length > 0)
+ {
+ size_t to_take = std::min(length, bitrate / 8 - S_pos);
+
+ length -= to_take;
+
+ while(to_take && S_pos % 8)
+ {
+ S[S_pos / 8] ^= static_cast<uint64_t>(input[0]) << (8 * (S_pos % 8));
+
+ ++S_pos;
+ ++input;
+ --to_take;
+ }
+
+ while(to_take && to_take % 8 == 0)
+ {
+ S[S_pos / 8] ^= load_le<uint64_t>(input, 0);
+ S_pos += 8;
+ input += 8;
+ to_take -= 8;
+ }
+
+ while(to_take)
+ {
+ S[S_pos / 8] ^= static_cast<uint64_t>(input[0]) << (8 * (S_pos % 8));
+
+ ++S_pos;
+ ++input;
+ --to_take;
+ }
+
+ if(S_pos == bitrate / 8)
+ {
+ SHA_3::permute(S.data());
+ S_pos = 0;
+ }
+ }
+
+ return S_pos;
+ }
+
+//static
+void SHA_3::finish(size_t bitrate,
+ secure_vector<uint64_t>& S, size_t S_pos,
+ uint8_t init_pad, uint8_t fini_pad)
+ {
+ BOTAN_ARG_CHECK(bitrate % 64 == 0, "SHA-3 bitrate must be multiple of 64");
+
+ S[S_pos / 8] ^= static_cast<uint64_t>(init_pad) << (8 * (S_pos % 8));
+ S[(bitrate / 64) - 1] ^= static_cast<uint64_t>(fini_pad) << 56;
+ SHA_3::permute(S.data());
+ }
+
+//static
+void SHA_3::expand(size_t bitrate,
+ secure_vector<uint64_t>& S,
+ uint8_t output[], size_t output_length)
+ {
+ BOTAN_ARG_CHECK(bitrate % 64 == 0, "SHA-3 bitrate must be multiple of 64");
+
+ const size_t byterate = bitrate / 8;
+
+ while(output_length > 0)
+ {
+ const size_t copying = std::min(byterate, output_length);
+
+ copy_out_vec_le(output, copying, S);
+
+ output += copying;
+ output_length -= copying;
+
+ if(output_length > 0)
+ {
+ SHA_3::permute(S.data());
+ }
+ }
+ }
+
+SHA_3::SHA_3(size_t output_bits) :
+ m_output_bits(output_bits),
+ m_bitrate(1600 - 2*output_bits),
+ m_S(25),
+ m_S_pos(0)
+ {
+ // We only support the parameters for SHA-3 in this constructor
+
+ if(output_bits != 224 && output_bits != 256 &&
+ output_bits != 384 && output_bits != 512)
+ throw Invalid_Argument("SHA_3: Invalid output length " +
+ std::to_string(output_bits));
+ }
+
+std::string SHA_3::name() const
+ {
+ return "SHA-3(" + std::to_string(m_output_bits) + ")";
+ }
+
+std::string SHA_3::provider() const
+ {
+#if defined(BOTAN_HAS_SHA3_BMI2)
+ if(CPUID::has_bmi2())
+ {
+ return "bmi2";
+ }
+#endif
+
+ return "base";
+ }
+
+std::unique_ptr<HashFunction> SHA_3::copy_state() const
+ {
+ return std::unique_ptr<HashFunction>(new SHA_3(*this));
+ }
+
+HashFunction* SHA_3::clone() const
+ {
+ return new SHA_3(m_output_bits);
+ }
+
+void SHA_3::clear()
+ {
+ zeroise(m_S);
+ m_S_pos = 0;
+ }
+
+void SHA_3::add_data(const uint8_t input[], size_t length)
+ {
+ m_S_pos = SHA_3::absorb(m_bitrate, m_S, m_S_pos, input, length);
+ }
+
+void SHA_3::final_result(uint8_t output[])
+ {
+ SHA_3::finish(m_bitrate, m_S, m_S_pos, 0x06, 0x80);
+
+ /*
+ * We never have to run the permutation again because we only support
+ * limited output lengths
+ */
+ copy_out_vec_le(output, m_output_bits/8, m_S);
+
+ clear();
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/hash/sha3/sha3.h b/comm/third_party/botan/src/lib/hash/sha3/sha3.h
new file mode 100644
index 0000000000..e290e60a20
--- /dev/null
+++ b/comm/third_party/botan/src/lib/hash/sha3/sha3.h
@@ -0,0 +1,136 @@
+/*
+* SHA-3
+* (C) 2010,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_SHA3_H_
+#define BOTAN_SHA3_H_
+
+#include <botan/hash.h>
+#include <botan/secmem.h>
+#include <string>
+
+BOTAN_FUTURE_INTERNAL_HEADER(sha3.h)
+
+namespace Botan {
+
+/**
+* SHA-3
+*/
+class BOTAN_PUBLIC_API(2,0) SHA_3 : public HashFunction
+ {
+ public:
+
+ /**
+ * @param output_bits the size of the hash output; must be one of
+ * 224, 256, 384, or 512
+ */
+ explicit SHA_3(size_t output_bits);
+
+ size_t hash_block_size() const override { return m_bitrate / 8; }
+ size_t output_length() const override { return m_output_bits / 8; }
+
+ HashFunction* clone() const override;
+ std::unique_ptr<HashFunction> copy_state() const override;
+ std::string name() const override;
+ void clear() override;
+ std::string provider() const override;
+
+ // Static functions for internal usage
+
+ /**
+ * Absorb data into the provided state
+ * @param bitrate the bitrate to absorb into the sponge
+ * @param S the sponge state
+ * @param S_pos where to begin absorbing into S
+ * @param input the input data
+ * @param length size of input in bytes
+ */
+ static size_t absorb(size_t bitrate,
+ secure_vector<uint64_t>& S, size_t S_pos,
+ const uint8_t input[], size_t length);
+
+ /**
+ * Add final padding and permute. The padding is assumed to be
+ * init_pad || 00... || fini_pad
+ *
+ * @param bitrate the bitrate to absorb into the sponge
+ * @param S the sponge state
+ * @param S_pos where to begin absorbing into S
+ * @param init_pad the leading pad bits
+ * @param fini_pad the final pad bits
+ */
+ static void finish(size_t bitrate,
+ secure_vector<uint64_t>& S, size_t S_pos,
+ uint8_t init_pad, uint8_t fini_pad);
+
+ /**
+ * Expand from provided state
+ * @param bitrate sponge parameter
+ * @param S the state
+ * @param output the output buffer
+ * @param output_length the size of output in bytes
+ */
+ static void expand(size_t bitrate,
+ secure_vector<uint64_t>& S,
+ uint8_t output[], size_t output_length);
+
+ /**
+ * The bare Keccak-1600 permutation
+ */
+ static void permute(uint64_t A[25]);
+
+ private:
+ void add_data(const uint8_t input[], size_t length) override;
+ void final_result(uint8_t out[]) override;
+
+#if defined(BOTAN_HAS_SHA3_BMI2)
+ static void permute_bmi2(uint64_t A[25]);
+#endif
+
+ size_t m_output_bits, m_bitrate;
+ secure_vector<uint64_t> m_S;
+ size_t m_S_pos;
+ };
+
+/**
+* SHA-3-224
+*/
+class BOTAN_PUBLIC_API(2,0) SHA_3_224 final : public SHA_3
+ {
+ public:
+ SHA_3_224() : SHA_3(224) {}
+ };
+
+/**
+* SHA-3-256
+*/
+class BOTAN_PUBLIC_API(2,0) SHA_3_256 final : public SHA_3
+ {
+ public:
+ SHA_3_256() : SHA_3(256) {}
+ };
+
+/**
+* SHA-3-384
+*/
+class BOTAN_PUBLIC_API(2,0) SHA_3_384 final : public SHA_3
+ {
+ public:
+ SHA_3_384() : SHA_3(384) {}
+ };
+
+/**
+* SHA-3-512
+*/
+class BOTAN_PUBLIC_API(2,0) SHA_3_512 final : public SHA_3
+ {
+ public:
+ SHA_3_512() : SHA_3(512) {}
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/hash/sha3/sha3_bmi2/info.txt b/comm/third_party/botan/src/lib/hash/sha3/sha3_bmi2/info.txt
new file mode 100644
index 0000000000..5f0db560e5
--- /dev/null
+++ b/comm/third_party/botan/src/lib/hash/sha3/sha3_bmi2/info.txt
@@ -0,0 +1,17 @@
+<defines>
+SHA3_BMI2 -> 20190117
+</defines>
+
+<isa>
+bmi2
+</isa>
+
+# Needs 64-bit registers to be useful
+<arch>
+x86_64
+</arch>
+
+<cc>
+gcc
+clang
+</cc>
diff --git a/comm/third_party/botan/src/lib/hash/sha3/sha3_bmi2/sha3_bmi2.cpp b/comm/third_party/botan/src/lib/hash/sha3/sha3_bmi2/sha3_bmi2.cpp
new file mode 100644
index 0000000000..c7f1914a3b
--- /dev/null
+++ b/comm/third_party/botan/src/lib/hash/sha3/sha3_bmi2/sha3_bmi2.cpp
@@ -0,0 +1,133 @@
+/*
+* SHA-3
+* (C) 2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/sha3.h>
+#include <botan/rotate.h>
+
+#include <tuple>
+
+namespace Botan {
+
+namespace {
+
+// This is a workaround for a suspected bug in clang 12 (and XCode 13)
+// that caused a miscompile of the SHA3 implementation for optimization
+// level -O2 and higher.
+//
+// For details, see: https://github.com/randombit/botan/issues/2802
+#if defined(__clang__) && \
+ (( defined(__apple_build_version__) && __clang_major__ == 13) || \
+ (!defined(__apple_build_version__) && __clang_major__ == 12))
+#define BOTAN_WORKAROUND_MAYBE_INLINE __attribute__((noinline))
+#else
+#define BOTAN_WORKAROUND_MAYBE_INLINE inline
+#endif
+
+BOTAN_WORKAROUND_MAYBE_INLINE std::tuple<uint64_t, uint64_t, uint64_t, uint64_t, uint64_t>
+ xor_CNs(const uint64_t A[25])
+ {
+ return {
+ A[0] ^ A[5] ^ A[10] ^ A[15] ^ A[20],
+ A[1] ^ A[6] ^ A[11] ^ A[16] ^ A[21],
+ A[2] ^ A[7] ^ A[12] ^ A[17] ^ A[22],
+ A[3] ^ A[8] ^ A[13] ^ A[18] ^ A[23],
+ A[4] ^ A[9] ^ A[14] ^ A[19] ^ A[24]};
+ }
+
+#undef BOTAN_WORKAROUND_MAYBE_INLINE
+
+inline void SHA3_BMI2_round(uint64_t T[25], const uint64_t A[25], uint64_t RC)
+ {
+ const auto Cs = xor_CNs(A);
+
+ const uint64_t D0 = rotl<1>(std::get<0>(Cs)) ^ std::get<3>(Cs);
+ const uint64_t D1 = rotl<1>(std::get<1>(Cs)) ^ std::get<4>(Cs);
+ const uint64_t D2 = rotl<1>(std::get<2>(Cs)) ^ std::get<0>(Cs);
+ const uint64_t D3 = rotl<1>(std::get<3>(Cs)) ^ std::get<1>(Cs);
+ const uint64_t D4 = rotl<1>(std::get<4>(Cs)) ^ std::get<2>(Cs);
+
+ const uint64_t B00 = A[ 0] ^ D1;
+ const uint64_t B01 = rotl<44>(A[ 6] ^ D2);
+ const uint64_t B02 = rotl<43>(A[12] ^ D3);
+ const uint64_t B03 = rotl<21>(A[18] ^ D4);
+ const uint64_t B04 = rotl<14>(A[24] ^ D0);
+ T[ 0] = B00 ^ (~B01 & B02) ^ RC;
+ T[ 1] = B01 ^ (~B02 & B03);
+ T[ 2] = B02 ^ (~B03 & B04);
+ T[ 3] = B03 ^ (~B04 & B00);
+ T[ 4] = B04 ^ (~B00 & B01);
+
+ const uint64_t B05 = rotl<28>(A[ 3] ^ D4);
+ const uint64_t B06 = rotl<20>(A[ 9] ^ D0);
+ const uint64_t B07 = rotl< 3>(A[10] ^ D1);
+ const uint64_t B08 = rotl<45>(A[16] ^ D2);
+ const uint64_t B09 = rotl<61>(A[22] ^ D3);
+ T[ 5] = B05 ^ (~B06 & B07);
+ T[ 6] = B06 ^ (~B07 & B08);
+ T[ 7] = B07 ^ (~B08 & B09);
+ T[ 8] = B08 ^ (~B09 & B05);
+ T[ 9] = B09 ^ (~B05 & B06);
+
+ const uint64_t B10 = rotl< 1>(A[ 1] ^ D2);
+ const uint64_t B11 = rotl< 6>(A[ 7] ^ D3);
+ const uint64_t B12 = rotl<25>(A[13] ^ D4);
+ const uint64_t B13 = rotl< 8>(A[19] ^ D0);
+ const uint64_t B14 = rotl<18>(A[20] ^ D1);
+ T[10] = B10 ^ (~B11 & B12);
+ T[11] = B11 ^ (~B12 & B13);
+ T[12] = B12 ^ (~B13 & B14);
+ T[13] = B13 ^ (~B14 & B10);
+ T[14] = B14 ^ (~B10 & B11);
+
+ const uint64_t B15 = rotl<27>(A[ 4] ^ D0);
+ const uint64_t B16 = rotl<36>(A[ 5] ^ D1);
+ const uint64_t B17 = rotl<10>(A[11] ^ D2);
+ const uint64_t B18 = rotl<15>(A[17] ^ D3);
+ const uint64_t B19 = rotl<56>(A[23] ^ D4);
+ T[15] = B15 ^ (~B16 & B17);
+ T[16] = B16 ^ (~B17 & B18);
+ T[17] = B17 ^ (~B18 & B19);
+ T[18] = B18 ^ (~B19 & B15);
+ T[19] = B19 ^ (~B15 & B16);
+
+ const uint64_t B20 = rotl<62>(A[ 2] ^ D3);
+ const uint64_t B21 = rotl<55>(A[ 8] ^ D4);
+ const uint64_t B22 = rotl<39>(A[14] ^ D0);
+ const uint64_t B23 = rotl<41>(A[15] ^ D1);
+ const uint64_t B24 = rotl< 2>(A[21] ^ D2);
+ T[20] = B20 ^ (~B21 & B22);
+ T[21] = B21 ^ (~B22 & B23);
+ T[22] = B22 ^ (~B23 & B24);
+ T[23] = B23 ^ (~B24 & B20);
+ T[24] = B24 ^ (~B20 & B21);
+ }
+
+}
+
+void SHA_3::permute_bmi2(uint64_t A[25])
+ {
+ 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
+ };
+
+ uint64_t T[25];
+
+ for(size_t i = 0; i != 24; i += 2)
+ {
+ SHA3_BMI2_round(T, A, RC[i+0]);
+ SHA3_BMI2_round(A, T, RC[i+1]);
+ }
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/hash/shake/info.txt b/comm/third_party/botan/src/lib/hash/shake/info.txt
new file mode 100644
index 0000000000..59a1a852a9
--- /dev/null
+++ b/comm/third_party/botan/src/lib/hash/shake/info.txt
@@ -0,0 +1,7 @@
+<defines>
+SHAKE -> 20161009
+</defines>
+
+<requires>
+sha3
+</requires>
diff --git a/comm/third_party/botan/src/lib/hash/shake/shake.cpp b/comm/third_party/botan/src/lib/hash/shake/shake.cpp
new file mode 100644
index 0000000000..76ed79a274
--- /dev/null
+++ b/comm/third_party/botan/src/lib/hash/shake/shake.cpp
@@ -0,0 +1,97 @@
+/*
+* SHAKE-128/256 as a hash
+* (C) 2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/shake.h>
+#include <botan/sha3.h>
+#include <botan/exceptn.h>
+
+namespace Botan {
+
+SHAKE_128::SHAKE_128(size_t output_bits) :
+ m_output_bits(output_bits), m_S(25), m_S_pos(0)
+ {
+ if(output_bits % 8 != 0)
+ throw Invalid_Argument("SHAKE_128: Invalid output length " +
+ std::to_string(output_bits));
+ }
+
+std::string SHAKE_128::name() const
+ {
+ return "SHAKE-128(" + std::to_string(m_output_bits) + ")";
+ }
+
+HashFunction* SHAKE_128::clone() const
+ {
+ return new SHAKE_128(m_output_bits);
+ }
+
+std::unique_ptr<HashFunction> SHAKE_128::copy_state() const
+ {
+ return std::unique_ptr<HashFunction>(new SHAKE_128(*this));
+ }
+
+void SHAKE_128::clear()
+ {
+ zeroise(m_S);
+ m_S_pos = 0;
+ }
+
+void SHAKE_128::add_data(const uint8_t input[], size_t length)
+ {
+ m_S_pos = SHA_3::absorb(SHAKE_128_BITRATE, m_S, m_S_pos, input, length);
+ }
+
+void SHAKE_128::final_result(uint8_t output[])
+ {
+ SHA_3::finish(SHAKE_128_BITRATE, m_S, m_S_pos, 0x1F, 0x80);
+ SHA_3::expand(SHAKE_128_BITRATE, m_S, output, output_length());
+ clear();
+ }
+
+SHAKE_256::SHAKE_256(size_t output_bits) :
+ m_output_bits(output_bits), m_S(25), m_S_pos(0)
+ {
+ if(output_bits % 8 != 0)
+ throw Invalid_Argument("SHAKE_256: Invalid output length " +
+ std::to_string(output_bits));
+ }
+
+std::string SHAKE_256::name() const
+ {
+ return "SHAKE-256(" + std::to_string(m_output_bits) + ")";
+ }
+
+HashFunction* SHAKE_256::clone() const
+ {
+ return new SHAKE_256(m_output_bits);
+ }
+
+std::unique_ptr<HashFunction> SHAKE_256::copy_state() const
+ {
+ return std::unique_ptr<HashFunction>(new SHAKE_256(*this));
+ }
+
+void SHAKE_256::clear()
+ {
+ zeroise(m_S);
+ m_S_pos = 0;
+ }
+
+void SHAKE_256::add_data(const uint8_t input[], size_t length)
+ {
+ m_S_pos = SHA_3::absorb(SHAKE_256_BITRATE, m_S, m_S_pos, input, length);
+ }
+
+void SHAKE_256::final_result(uint8_t output[])
+ {
+ SHA_3::finish(SHAKE_256_BITRATE, m_S, m_S_pos, 0x1F, 0x80);
+ SHA_3::expand(SHAKE_256_BITRATE, m_S, output, output_length());
+
+ clear();
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/hash/shake/shake.h b/comm/third_party/botan/src/lib/hash/shake/shake.h
new file mode 100644
index 0000000000..c52df136b7
--- /dev/null
+++ b/comm/third_party/botan/src/lib/hash/shake/shake.h
@@ -0,0 +1,85 @@
+/*
+* SHAKE hash functions
+* (C) 2010,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_SHAKE_HASH_H_
+#define BOTAN_SHAKE_HASH_H_
+
+#include <botan/hash.h>
+#include <botan/secmem.h>
+#include <string>
+
+BOTAN_FUTURE_INTERNAL_HEADER(shake.h)
+
+namespace Botan {
+
+/**
+* SHAKE-128
+*/
+class BOTAN_PUBLIC_API(2,0) SHAKE_128 final : public HashFunction
+ {
+ public:
+
+ /**
+ * @param output_bits the desired output size in bits
+ * must be a multiple of 8
+ */
+ explicit SHAKE_128(size_t output_bits);
+
+ size_t hash_block_size() const override { return SHAKE_128_BITRATE / 8; }
+ size_t output_length() const override { return m_output_bits / 8; }
+
+ HashFunction* clone() const override;
+ std::unique_ptr<HashFunction> copy_state() const override;
+ std::string name() const override;
+ void clear() override;
+
+ private:
+ void add_data(const uint8_t input[], size_t length) override;
+ void final_result(uint8_t out[]) override;
+
+ static const size_t SHAKE_128_BITRATE = 1600 - 256;
+
+ size_t m_output_bits;
+ secure_vector<uint64_t> m_S;
+ size_t m_S_pos;
+ };
+
+/**
+* SHAKE-256
+*/
+class BOTAN_PUBLIC_API(2,0) SHAKE_256 final : public HashFunction
+ {
+ public:
+
+ /**
+ * @param output_bits the desired output size in bits
+ * must be a multiple of 8
+ */
+ explicit SHAKE_256(size_t output_bits);
+
+ size_t hash_block_size() const override { return SHAKE_256_BITRATE / 8; }
+ size_t output_length() const override { return m_output_bits / 8; }
+
+ HashFunction* clone() const override;
+ std::unique_ptr<HashFunction> copy_state() const override;
+ std::string name() const override;
+ void clear() override;
+
+ private:
+ void add_data(const uint8_t input[], size_t length) override;
+ void final_result(uint8_t out[]) override;
+
+ static const size_t SHAKE_256_BITRATE = 1600 - 512;
+
+ size_t m_output_bits;
+ secure_vector<uint64_t> m_S;
+ size_t m_S_pos;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/hash/skein/info.txt b/comm/third_party/botan/src/lib/hash/skein/info.txt
new file mode 100644
index 0000000000..3445c376fe
--- /dev/null
+++ b/comm/third_party/botan/src/lib/hash/skein/info.txt
@@ -0,0 +1,7 @@
+<defines>
+SKEIN_512 -> 20131128
+</defines>
+
+<requires>
+threefish_512
+</requires>
diff --git a/comm/third_party/botan/src/lib/hash/skein/skein_512.cpp b/comm/third_party/botan/src/lib/hash/skein/skein_512.cpp
new file mode 100644
index 0000000000..edf95d5961
--- /dev/null
+++ b/comm/third_party/botan/src/lib/hash/skein/skein_512.cpp
@@ -0,0 +1,178 @@
+/*
+* The Skein-512 hash function
+* (C) 2009,2010,2014 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/skein_512.h>
+#include <botan/loadstor.h>
+#include <botan/exceptn.h>
+#include <algorithm>
+
+namespace Botan {
+
+Skein_512::Skein_512(size_t arg_output_bits,
+ const std::string& arg_personalization) :
+ m_personalization(arg_personalization),
+ m_output_bits(arg_output_bits),
+ m_threefish(new Threefish_512),
+ m_T(2), m_buffer(64), m_buf_pos(0)
+ {
+ if(m_output_bits == 0 || m_output_bits % 8 != 0 || m_output_bits > 512)
+ throw Invalid_Argument("Bad output bits size for Skein-512");
+
+ initial_block();
+ }
+
+std::string Skein_512::name() const
+ {
+ if(m_personalization != "")
+ return "Skein-512(" + std::to_string(m_output_bits) + "," +
+ m_personalization + ")";
+ return "Skein-512(" + std::to_string(m_output_bits) + ")";
+ }
+
+HashFunction* Skein_512::clone() const
+ {
+ return new Skein_512(m_output_bits, m_personalization);
+ }
+
+std::unique_ptr<HashFunction> Skein_512::copy_state() const
+ {
+ std::unique_ptr<Skein_512> copy(new Skein_512(m_output_bits, m_personalization));
+
+ copy->m_threefish->m_K = this->m_threefish->m_K;
+ copy->m_T = this->m_T;
+ copy->m_buffer = this->m_buffer;
+ copy->m_buf_pos = this->m_buf_pos;
+
+ // work around GCC 4.8 bug
+ return std::unique_ptr<HashFunction>(copy.release());
+ }
+
+void Skein_512::clear()
+ {
+ zeroise(m_buffer);
+ m_buf_pos = 0;
+
+ initial_block();
+ }
+
+void Skein_512::reset_tweak(type_code type, bool is_final)
+ {
+ m_T[0] = 0;
+
+ m_T[1] = (static_cast<uint64_t>(type) << 56) |
+ (static_cast<uint64_t>(1) << 62) |
+ (static_cast<uint64_t>(is_final) << 63);
+ }
+
+void Skein_512::initial_block()
+ {
+ const uint8_t zeros[64] = { 0 };
+
+ m_threefish->set_key(zeros, sizeof(zeros));
+
+ // ASCII("SHA3") followed by version (0x0001) code
+ uint8_t config_str[32] = { 0x53, 0x48, 0x41, 0x33, 0x01, 0x00, 0 };
+ store_le(uint32_t(m_output_bits), config_str + 8);
+
+ reset_tweak(SKEIN_CONFIG, true);
+ ubi_512(config_str, sizeof(config_str));
+
+ if(m_personalization != "")
+ {
+ /*
+ This is a limitation of this implementation, and not of the
+ algorithm specification. Could be fixed relatively easily, but
+ doesn't seem worth the trouble.
+ */
+ if(m_personalization.length() > 64)
+ 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());
+ }
+
+ reset_tweak(SKEIN_MSG, false);
+ }
+
+void Skein_512::ubi_512(const uint8_t msg[], size_t msg_len)
+ {
+ secure_vector<uint64_t> M(8);
+
+ do
+ {
+ const size_t to_proc = std::min<size_t>(msg_len, 64);
+ m_T[0] += to_proc;
+
+ load_le(M.data(), msg, to_proc / 8);
+
+ if(to_proc % 8)
+ {
+ for(size_t j = 0; j != to_proc % 8; ++j)
+ M[to_proc/8] |= static_cast<uint64_t>(msg[8*(to_proc/8)+j]) << (8*j);
+ }
+
+ m_threefish->skein_feedfwd(M, m_T);
+
+ // clear first flag if set
+ m_T[1] &= ~(static_cast<uint64_t>(1) << 62);
+
+ msg_len -= to_proc;
+ msg += to_proc;
+ } while(msg_len);
+ }
+
+void Skein_512::add_data(const uint8_t input[], size_t length)
+ {
+ if(length == 0)
+ return;
+
+ if(m_buf_pos)
+ {
+ buffer_insert(m_buffer, m_buf_pos, input, length);
+ if(m_buf_pos + length > 64)
+ {
+ ubi_512(m_buffer.data(), m_buffer.size());
+
+ input += (64 - m_buf_pos);
+ length -= (64 - m_buf_pos);
+ m_buf_pos = 0;
+ }
+ }
+
+ const size_t full_blocks = (length - 1) / 64;
+
+ if(full_blocks)
+ ubi_512(input, 64*full_blocks);
+
+ length -= full_blocks * 64;
+
+ buffer_insert(m_buffer, m_buf_pos, input + full_blocks * 64, length);
+ m_buf_pos += length;
+ }
+
+void Skein_512::final_result(uint8_t out[])
+ {
+ m_T[1] |= (static_cast<uint64_t>(1) << 63); // final block flag
+
+ for(size_t i = m_buf_pos; i != m_buffer.size(); ++i)
+ m_buffer[i] = 0;
+
+ ubi_512(m_buffer.data(), m_buf_pos);
+
+ const uint8_t counter[8] = { 0 };
+
+ reset_tweak(SKEIN_OUTPUT, true);
+ ubi_512(counter, sizeof(counter));
+
+ copy_out_vec_le(out, m_output_bits / 8, m_threefish->m_K);
+
+ m_buf_pos = 0;
+ initial_block();
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/hash/skein/skein_512.h b/comm/third_party/botan/src/lib/hash/skein/skein_512.h
new file mode 100644
index 0000000000..0bf173cebd
--- /dev/null
+++ b/comm/third_party/botan/src/lib/hash/skein/skein_512.h
@@ -0,0 +1,72 @@
+/*
+* The Skein-512 hash function
+* (C) 2009,2014 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_SKEIN_512_H_
+#define BOTAN_SKEIN_512_H_
+
+#include <botan/hash.h>
+#include <botan/threefish_512.h>
+#include <string>
+#include <memory>
+
+BOTAN_FUTURE_INTERNAL_HEADER(skin_512.h)
+
+namespace Botan {
+
+/**
+* Skein-512, a SHA-3 candidate
+*/
+class BOTAN_PUBLIC_API(2,0) Skein_512 final : public HashFunction
+ {
+ public:
+ /**
+ * @param output_bits the output size of Skein in bits
+ * @param personalization is a string that will parameterize the
+ * hash output
+ */
+ Skein_512(size_t output_bits = 512,
+ const std::string& personalization = "");
+
+ size_t hash_block_size() const override { return 64; }
+ size_t output_length() const override { return m_output_bits / 8; }
+
+ HashFunction* clone() const override;
+ std::unique_ptr<HashFunction> copy_state() const override;
+ std::string name() const override;
+ void clear() override;
+ private:
+ enum type_code {
+ SKEIN_KEY = 0,
+ SKEIN_CONFIG = 4,
+ SKEIN_PERSONALIZATION = 8,
+ SKEIN_PUBLIC_KEY = 12,
+ SKEIN_KEY_IDENTIFIER = 16,
+ SKEIN_NONCE = 20,
+ SKEIN_MSG = 48,
+ SKEIN_OUTPUT = 63
+ };
+
+ void add_data(const uint8_t input[], size_t length) override;
+ void final_result(uint8_t out[]) override;
+
+ void ubi_512(const uint8_t msg[], size_t msg_len);
+
+ void initial_block();
+ void reset_tweak(type_code type, bool is_final);
+
+ std::string m_personalization;
+ size_t m_output_bits;
+
+ std::unique_ptr<Threefish_512> m_threefish;
+ secure_vector<uint64_t> m_T;
+ secure_vector<uint8_t> m_buffer;
+ size_t m_buf_pos;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/hash/sm3/info.txt b/comm/third_party/botan/src/lib/hash/sm3/info.txt
new file mode 100644
index 0000000000..e591a6ce00
--- /dev/null
+++ b/comm/third_party/botan/src/lib/hash/sm3/info.txt
@@ -0,0 +1,7 @@
+<defines>
+SM3 -> 20170402
+</defines>
+
+<requires>
+mdx_hash
+</requires>
diff --git a/comm/third_party/botan/src/lib/hash/sm3/sm3.cpp b/comm/third_party/botan/src/lib/hash/sm3/sm3.cpp
new file mode 100644
index 0000000000..cfd1409cd1
--- /dev/null
+++ b/comm/third_party/botan/src/lib/hash/sm3/sm3.cpp
@@ -0,0 +1,259 @@
+/*
+* SM3
+* (C) 2017 Ribose Inc.
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/sm3.h>
+#include <botan/loadstor.h>
+#include <botan/rotate.h>
+
+namespace Botan {
+
+std::unique_ptr<HashFunction> SM3::copy_state() const
+ {
+ return std::unique_ptr<HashFunction>(new SM3(*this));
+ }
+
+namespace {
+
+const uint32_t SM3_IV[] = {
+ 0x7380166fUL, 0x4914b2b9UL, 0x172442d7UL, 0xda8a0600UL,
+ 0xa96f30bcUL, 0x163138aaUL, 0xe38dee4dUL, 0xb0fb0e4eUL
+};
+
+inline uint32_t P0(uint32_t X)
+ {
+ return X ^ rotl<9>(X) ^ rotl<17>(X);
+ }
+
+inline uint32_t FF1(uint32_t X, uint32_t Y, uint32_t Z)
+ {
+ return (X & Y) | ((X | Y) & Z);
+ //return (X & Y) | (X & Z) | (Y & Z);
+ }
+
+inline uint32_t GG1(uint32_t X, uint32_t Y, uint32_t Z)
+ {
+ //return (X & Y) | (~X & Z);
+ return ((Z ^ (X & (Y ^ Z))));
+ }
+
+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 = FF1(A, B, C) + D + (SS1 ^ A12) + Wj;
+ const uint32_t TT2 = GG1(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;
+ }
+
+}
+
+/*
+* SM3 Compression Function
+*/
+void SM3::compress_n(const uint8_t input[], size_t blocks)
+ {
+ uint32_t A = m_digest[0], B = m_digest[1], C = m_digest[2], D = m_digest[3],
+ E = m_digest[4], F = m_digest[5], G = m_digest[6], H = m_digest[7];
+
+ for(size_t i = 0; i != blocks; ++i)
+ {
+ uint32_t W00 = load_be<uint32_t>(input, 0);
+ uint32_t W01 = load_be<uint32_t>(input, 1);
+ uint32_t W02 = load_be<uint32_t>(input, 2);
+ uint32_t W03 = load_be<uint32_t>(input, 3);
+ uint32_t W04 = load_be<uint32_t>(input, 4);
+ uint32_t W05 = load_be<uint32_t>(input, 5);
+ uint32_t W06 = load_be<uint32_t>(input, 6);
+ uint32_t W07 = load_be<uint32_t>(input, 7);
+ uint32_t W08 = load_be<uint32_t>(input, 8);
+ uint32_t W09 = load_be<uint32_t>(input, 9);
+ uint32_t W10 = load_be<uint32_t>(input, 10);
+ uint32_t W11 = load_be<uint32_t>(input, 11);
+ uint32_t W12 = load_be<uint32_t>(input, 12);
+ uint32_t W13 = load_be<uint32_t>(input, 13);
+ uint32_t W14 = load_be<uint32_t>(input, 14);
+ uint32_t W15 = load_be<uint32_t>(input, 15);
+
+ R1(A, B, C, D, E, F, G, H, 0x79CC4519, W00, W00 ^ W04);
+ W00 = SM3_E(W00, W07, W13, W03, W10);
+ R1(D, A, B, C, H, E, F, G, 0xF3988A32, W01, W01 ^ W05);
+ W01 = SM3_E(W01, W08, W14, W04, W11);
+ R1(C, D, A, B, G, H, E, F, 0xE7311465, W02, W02 ^ W06);
+ W02 = SM3_E(W02, W09, W15, W05, W12);
+ R1(B, C, D, A, F, G, H, E, 0xCE6228CB, W03, W03 ^ W07);
+ W03 = SM3_E(W03, W10, W00, W06, W13);
+ R1(A, B, C, D, E, F, G, H, 0x9CC45197, W04, W04 ^ W08);
+ W04 = SM3_E(W04, W11, W01, W07, W14);
+ R1(D, A, B, C, H, E, F, G, 0x3988A32F, W05, W05 ^ W09);
+ W05 = SM3_E(W05, W12, W02, W08, W15);
+ R1(C, D, A, B, G, H, E, F, 0x7311465E, W06, W06 ^ W10);
+ W06 = SM3_E(W06, W13, W03, W09, W00);
+ R1(B, C, D, A, F, G, H, E, 0xE6228CBC, W07, W07 ^ W11);
+ W07 = SM3_E(W07, W14, W04, W10, W01);
+ R1(A, B, C, D, E, F, G, H, 0xCC451979, W08, W08 ^ W12);
+ W08 = SM3_E(W08, W15, W05, W11, W02);
+ R1(D, A, B, C, H, E, F, G, 0x988A32F3, W09, W09 ^ W13);
+ W09 = SM3_E(W09, W00, W06, W12, W03);
+ R1(C, D, A, B, G, H, E, F, 0x311465E7, W10, W10 ^ W14);
+ W10 = SM3_E(W10, W01, W07, W13, W04);
+ R1(B, C, D, A, F, G, H, E, 0x6228CBCE, W11, W11 ^ W15);
+ W11 = SM3_E(W11, W02, W08, W14, W05);
+ R1(A, B, C, D, E, F, G, H, 0xC451979C, W12, W12 ^ W00);
+ W12 = SM3_E(W12, W03, W09, W15, W06);
+ R1(D, A, B, C, H, E, F, G, 0x88A32F39, W13, W13 ^ W01);
+ W13 = SM3_E(W13, W04, W10, W00, W07);
+ R1(C, D, A, B, G, H, E, F, 0x11465E73, W14, W14 ^ W02);
+ W14 = SM3_E(W14, W05, W11, W01, W08);
+ R1(B, C, D, A, F, G, H, E, 0x228CBCE6, W15, W15 ^ W03);
+ W15 = SM3_E(W15, W06, W12, W02, W09);
+ R2(A, B, C, D, E, F, G, H, 0x9D8A7A87, W00, W00 ^ W04);
+ W00 = SM3_E(W00, W07, W13, W03, W10);
+ R2(D, A, B, C, H, E, F, G, 0x3B14F50F, W01, W01 ^ W05);
+ W01 = SM3_E(W01, W08, W14, W04, W11);
+ R2(C, D, A, B, G, H, E, F, 0x7629EA1E, W02, W02 ^ W06);
+ W02 = SM3_E(W02, W09, W15, W05, W12);
+ R2(B, C, D, A, F, G, H, E, 0xEC53D43C, W03, W03 ^ W07);
+ W03 = SM3_E(W03, W10, W00, W06, W13);
+ R2(A, B, C, D, E, F, G, H, 0xD8A7A879, W04, W04 ^ W08);
+ W04 = SM3_E(W04, W11, W01, W07, W14);
+ R2(D, A, B, C, H, E, F, G, 0xB14F50F3, W05, W05 ^ W09);
+ W05 = SM3_E(W05, W12, W02, W08, W15);
+ R2(C, D, A, B, G, H, E, F, 0x629EA1E7, W06, W06 ^ W10);
+ W06 = SM3_E(W06, W13, W03, W09, W00);
+ R2(B, C, D, A, F, G, H, E, 0xC53D43CE, W07, W07 ^ W11);
+ W07 = SM3_E(W07, W14, W04, W10, W01);
+ R2(A, B, C, D, E, F, G, H, 0x8A7A879D, W08, W08 ^ W12);
+ W08 = SM3_E(W08, W15, W05, W11, W02);
+ R2(D, A, B, C, H, E, F, G, 0x14F50F3B, W09, W09 ^ W13);
+ W09 = SM3_E(W09, W00, W06, W12, W03);
+ R2(C, D, A, B, G, H, E, F, 0x29EA1E76, W10, W10 ^ W14);
+ W10 = SM3_E(W10, W01, W07, W13, W04);
+ R2(B, C, D, A, F, G, H, E, 0x53D43CEC, W11, W11 ^ W15);
+ W11 = SM3_E(W11, W02, W08, W14, W05);
+ R2(A, B, C, D, E, F, G, H, 0xA7A879D8, W12, W12 ^ W00);
+ W12 = SM3_E(W12, W03, W09, W15, W06);
+ R2(D, A, B, C, H, E, F, G, 0x4F50F3B1, W13, W13 ^ W01);
+ W13 = SM3_E(W13, W04, W10, W00, W07);
+ R2(C, D, A, B, G, H, E, F, 0x9EA1E762, W14, W14 ^ W02);
+ W14 = SM3_E(W14, W05, W11, W01, W08);
+ R2(B, C, D, A, F, G, H, E, 0x3D43CEC5, W15, W15 ^ W03);
+ W15 = SM3_E(W15, W06, W12, W02, W09);
+ R2(A, B, C, D, E, F, G, H, 0x7A879D8A, W00, W00 ^ W04);
+ W00 = SM3_E(W00, W07, W13, W03, W10);
+ R2(D, A, B, C, H, E, F, G, 0xF50F3B14, W01, W01 ^ W05);
+ W01 = SM3_E(W01, W08, W14, W04, W11);
+ R2(C, D, A, B, G, H, E, F, 0xEA1E7629, W02, W02 ^ W06);
+ W02 = SM3_E(W02, W09, W15, W05, W12);
+ R2(B, C, D, A, F, G, H, E, 0xD43CEC53, W03, W03 ^ W07);
+ W03 = SM3_E(W03, W10, W00, W06, W13);
+ R2(A, B, C, D, E, F, G, H, 0xA879D8A7, W04, W04 ^ W08);
+ W04 = SM3_E(W04, W11, W01, W07, W14);
+ R2(D, A, B, C, H, E, F, G, 0x50F3B14F, W05, W05 ^ W09);
+ W05 = SM3_E(W05, W12, W02, W08, W15);
+ R2(C, D, A, B, G, H, E, F, 0xA1E7629E, W06, W06 ^ W10);
+ W06 = SM3_E(W06, W13, W03, W09, W00);
+ R2(B, C, D, A, F, G, H, E, 0x43CEC53D, W07, W07 ^ W11);
+ W07 = SM3_E(W07, W14, W04, W10, W01);
+ R2(A, B, C, D, E, F, G, H, 0x879D8A7A, W08, W08 ^ W12);
+ W08 = SM3_E(W08, W15, W05, W11, W02);
+ R2(D, A, B, C, H, E, F, G, 0x0F3B14F5, W09, W09 ^ W13);
+ W09 = SM3_E(W09, W00, W06, W12, W03);
+ R2(C, D, A, B, G, H, E, F, 0x1E7629EA, W10, W10 ^ W14);
+ W10 = SM3_E(W10, W01, W07, W13, W04);
+ R2(B, C, D, A, F, G, H, E, 0x3CEC53D4, W11, W11 ^ W15);
+ W11 = SM3_E(W11, W02, W08, W14, W05);
+ R2(A, B, C, D, E, F, G, H, 0x79D8A7A8, W12, W12 ^ W00);
+ W12 = SM3_E(W12, W03, W09, W15, W06);
+ R2(D, A, B, C, H, E, F, G, 0xF3B14F50, W13, W13 ^ W01);
+ W13 = SM3_E(W13, W04, W10, W00, W07);
+ R2(C, D, A, B, G, H, E, F, 0xE7629EA1, W14, W14 ^ W02);
+ W14 = SM3_E(W14, W05, W11, W01, W08);
+ R2(B, C, D, A, F, G, H, E, 0xCEC53D43, W15, W15 ^ W03);
+ W15 = SM3_E(W15, W06, W12, W02, W09);
+ R2(A, B, C, D, E, F, G, H, 0x9D8A7A87, W00, W00 ^ W04);
+ W00 = SM3_E(W00, W07, W13, W03, W10);
+ R2(D, A, B, C, H, E, F, G, 0x3B14F50F, W01, W01 ^ W05);
+ W01 = SM3_E(W01, W08, W14, W04, W11);
+ R2(C, D, A, B, G, H, E, F, 0x7629EA1E, W02, W02 ^ W06);
+ W02 = SM3_E(W02, W09, W15, W05, W12);
+ R2(B, C, D, A, F, G, H, E, 0xEC53D43C, W03, W03 ^ W07);
+ W03 = SM3_E(W03, W10, W00, W06, W13);
+ R2(A, B, C, D, E, F, G, H, 0xD8A7A879, W04, W04 ^ W08);
+ R2(D, A, B, C, H, E, F, G, 0xB14F50F3, W05, W05 ^ W09);
+ R2(C, D, A, B, G, H, E, F, 0x629EA1E7, W06, W06 ^ W10);
+ R2(B, C, D, A, F, G, H, E, 0xC53D43CE, W07, W07 ^ W11);
+ R2(A, B, C, D, E, F, G, H, 0x8A7A879D, W08, W08 ^ W12);
+ R2(D, A, B, C, H, E, F, G, 0x14F50F3B, W09, W09 ^ W13);
+ R2(C, D, A, B, G, H, E, F, 0x29EA1E76, W10, W10 ^ W14);
+ R2(B, C, D, A, F, G, H, E, 0x53D43CEC, W11, W11 ^ W15);
+ R2(A, B, C, D, E, F, G, H, 0xA7A879D8, W12, W12 ^ W00);
+ R2(D, A, B, C, H, E, F, G, 0x4F50F3B1, W13, W13 ^ W01);
+ R2(C, D, A, B, G, H, E, F, 0x9EA1E762, W14, W14 ^ W02);
+ R2(B, C, D, A, F, G, H, E, 0x3D43CEC5, W15, W15 ^ W03);
+
+ A = (m_digest[0] ^= A);
+ B = (m_digest[1] ^= B);
+ C = (m_digest[2] ^= C);
+ D = (m_digest[3] ^= D);
+ E = (m_digest[4] ^= E);
+ F = (m_digest[5] ^= F);
+ G = (m_digest[6] ^= G);
+ H = (m_digest[7] ^= H);
+
+ input += hash_block_size();
+ }
+ }
+
+/*
+* Copy out the digest
+*/
+void SM3::copy_out(uint8_t output[])
+ {
+ copy_out_vec_be(output, output_length(), m_digest);
+ }
+
+/*
+* Clear memory of sensitive data
+*/
+void SM3::clear()
+ {
+ MDx_HashFunction::clear();
+ std::copy(std::begin(SM3_IV), std::end(SM3_IV), m_digest.begin());
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/hash/sm3/sm3.h b/comm/third_party/botan/src/lib/hash/sm3/sm3.h
new file mode 100644
index 0000000000..268bd87fb3
--- /dev/null
+++ b/comm/third_party/botan/src/lib/hash/sm3/sm3.h
@@ -0,0 +1,49 @@
+/*
+* SM3
+* (C) 2017 Ribose Inc.
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_SM3_H_
+#define BOTAN_SM3_H_
+
+#include <botan/mdx_hash.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(sm3.h)
+
+namespace Botan {
+
+enum {
+ SM3_BLOCK_BYTES = 64,
+ SM3_DIGEST_BYTES = 32
+};
+
+/**
+* SM3
+*/
+class BOTAN_PUBLIC_API(2,2) SM3 final : public MDx_HashFunction
+ {
+ public:
+ std::string name() const override { return "SM3"; }
+ size_t output_length() const override { return SM3_DIGEST_BYTES; }
+ HashFunction* clone() const override { return new SM3; }
+ std::unique_ptr<HashFunction> copy_state() const override;
+
+ void clear() override;
+
+ SM3() : MDx_HashFunction(SM3_BLOCK_BYTES, true, true), m_digest(SM3_DIGEST_BYTES)
+ { clear(); }
+ private:
+ void compress_n(const uint8_t[], size_t blocks) override;
+ void copy_out(uint8_t[]) override;
+
+ /**
+ * The digest value
+ */
+ secure_vector<uint32_t> m_digest;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/hash/streebog/info.txt b/comm/third_party/botan/src/lib/hash/streebog/info.txt
new file mode 100644
index 0000000000..1a8bae57fa
--- /dev/null
+++ b/comm/third_party/botan/src/lib/hash/streebog/info.txt
@@ -0,0 +1,3 @@
+<defines>
+STREEBOG -> 20170623
+</defines>
diff --git a/comm/third_party/botan/src/lib/hash/streebog/streebog.cpp b/comm/third_party/botan/src/lib/hash/streebog/streebog.cpp
new file mode 100644
index 0000000000..0134daded2
--- /dev/null
+++ b/comm/third_party/botan/src/lib/hash/streebog/streebog.cpp
@@ -0,0 +1,207 @@
+/*
+* Streebog
+* (C) 2017 Ribose Inc.
+* (C) 2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/streebog.h>
+#include <botan/loadstor.h>
+#include <botan/exceptn.h>
+
+namespace Botan {
+
+extern const uint64_t STREEBOG_Ax[8][256];
+extern const uint64_t STREEBOG_C[12][8];
+
+std::unique_ptr<HashFunction> Streebog::copy_state() const
+ {
+ return std::unique_ptr<HashFunction>(new Streebog(*this));
+ }
+
+Streebog::Streebog(size_t output_bits) :
+ m_output_bits(output_bits),
+ m_count(0),
+ m_position(0),
+ m_buffer(64),
+ m_h(8),
+ m_S(8)
+ {
+ if(output_bits != 256 && output_bits != 512)
+ throw Invalid_Argument("Streebog: Invalid output length " +
+ std::to_string(output_bits));
+
+ clear();
+ }
+
+std::string Streebog::name() const
+ {
+ return "Streebog-" + std::to_string(m_output_bits);
+ }
+
+/*
+* Clear memory of sensitive data
+*/
+void Streebog::clear()
+ {
+ m_count = 0;
+ m_position = 0;
+ zeroise(m_buffer);
+ zeroise(m_S);
+
+ const uint64_t fill = (m_output_bits == 512) ? 0 : 0x0101010101010101;
+ std::fill(m_h.begin(), m_h.end(), fill);
+ }
+
+/*
+* Update the hash
+*/
+void Streebog::add_data(const uint8_t input[], size_t length)
+ {
+ const size_t block_size = m_buffer.size();
+
+ if(m_position)
+ {
+ buffer_insert(m_buffer, m_position, input, length);
+
+ if(m_position + length >= block_size)
+ {
+ compress(m_buffer.data());
+ m_count += 512;
+ input += (block_size - m_position);
+ length -= (block_size - m_position);
+ m_position = 0;
+ }
+ }
+
+ const size_t full_blocks = length / block_size;
+ const size_t remaining = length % block_size;
+
+ for(size_t i = 0; i != full_blocks; ++i)
+ {
+ compress(input + block_size * i);
+ m_count += 512;
+ }
+
+ buffer_insert(m_buffer, m_position, input + full_blocks * block_size, remaining);
+ m_position += remaining;
+ }
+
+/*
+* Finalize a hash
+*/
+void Streebog::final_result(uint8_t output[])
+ {
+ m_buffer[m_position++] = 0x01;
+
+ if(m_position != m_buffer.size())
+ clear_mem(&m_buffer[m_position], m_buffer.size() - m_position);
+
+ compress(m_buffer.data());
+ m_count += (m_position - 1) * 8;
+
+ zeroise(m_buffer);
+ store_le(m_count, m_buffer.data());
+ compress(m_buffer.data(), true);
+
+ compress_64(m_S.data(), true);
+ // FIXME
+ std::memcpy(output, &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<uint8_t*>(&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]]);
+ }
+ }
+
+} //namespace
+
+void Streebog::compress(const uint8_t input[], bool last_block)
+ {
+ uint64_t M[8];
+ std::memcpy(M, input, 64);
+
+ compress_64(M, last_block);
+ }
+
+void Streebog::compress_64(const uint64_t M[], bool last_block)
+ {
+ uint64_t N = force_le(last_block ? 0ULL : m_count);
+
+ uint64_t hN[8];
+ uint64_t A[8];
+
+ copy_mem(hN, m_h.data(), 8);
+ hN[0] ^= N;
+ lps(hN);
+
+ copy_mem(A, hN, 8);
+
+ for(size_t i = 0; i != 8; ++i)
+ {
+ hN[i] ^= M[i];
+ }
+
+ for(size_t i = 0; i < 12; ++i)
+ {
+ for(size_t j = 0; j != 8; ++j)
+ A[j] ^= force_le(STREEBOG_C[i][j]);
+ lps(A);
+
+ lps(hN);
+ for(size_t j = 0; j != 8; ++j)
+ hN[j] ^= A[j];
+ }
+
+ for(size_t i = 0; i != 8; ++i)
+ {
+ m_h[i] ^= hN[i] ^ M[i];
+ }
+
+ if(!last_block)
+ {
+ uint64_t carry = 0;
+ for(int i = 0; i < 8; i++)
+ {
+ const uint64_t m = force_le(M[i]);
+ const uint64_t hi = force_le(m_S[i]);
+ const uint64_t t = hi + m + carry;
+
+ m_S[i] = force_le(t);
+ if(t != m)
+ carry = (t < m);
+ }
+ }
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/hash/streebog/streebog.h b/comm/third_party/botan/src/lib/hash/streebog/streebog.h
new file mode 100644
index 0000000000..a573964d8e
--- /dev/null
+++ b/comm/third_party/botan/src/lib/hash/streebog/streebog.h
@@ -0,0 +1,72 @@
+/*
+* Streebog
+* (C) 2017 Ribose Inc.
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_STREEBOG_H_
+#define BOTAN_STREEBOG_H_
+
+#include <botan/hash.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(streebog.h)
+
+namespace Botan {
+
+/**
+* Streebog (GOST R 34.11-2012)
+* RFC 6986
+*/
+class BOTAN_PUBLIC_API(2,2) Streebog : public HashFunction
+ {
+ public:
+ size_t output_length() const override { return m_output_bits / 8; }
+
+ HashFunction* clone() const override { return new Streebog(m_output_bits); }
+ void clear() override;
+ std::string name() const override;
+ size_t hash_block_size() const override { return 64; }
+
+ std::unique_ptr<HashFunction> copy_state() const override;
+
+ explicit Streebog(size_t output_bits);
+ protected:
+ void add_data(const uint8_t input[], size_t length) override;
+ void final_result(uint8_t out[]) override;
+
+ void compress(const uint8_t input[], bool lastblock = false);
+
+ void compress_64(const uint64_t input[], bool lastblock = false);
+
+ private:
+ const size_t m_output_bits;
+ uint64_t m_count;
+ size_t m_position;
+ secure_vector<uint8_t> m_buffer;
+ secure_vector<uint64_t> m_h;
+ secure_vector<uint64_t> m_S;
+ };
+
+
+/**
+* Streebog-256
+*/
+class BOTAN_PUBLIC_API(2,2) Streebog_256 final : public Streebog
+ {
+ public:
+ Streebog_256() : Streebog(256) {}
+ };
+
+/**
+* Streebog-512
+*/
+class BOTAN_PUBLIC_API(2,2) Streebog_512 final : public Streebog
+ {
+ public:
+ Streebog_512() : Streebog(512) {}
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/hash/streebog/streebog_precalc.cpp b/comm/third_party/botan/src/lib/hash/streebog/streebog_precalc.cpp
new file mode 100644
index 0000000000..cf28379327
--- /dev/null
+++ b/comm/third_party/botan/src/lib/hash/streebog/streebog_precalc.cpp
@@ -0,0 +1,866 @@
+/*
+ * Derived from:
+ * https://github.com/degtyarevalexey/streebog
+ *
+ * Copyright (c) 2013, Alexey Degtyarev <alexey@renatasystems.org>.
+ * 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 <botan/streebog.h>
+
+namespace Botan {
+
+extern const uint64_t STREEBOG_Ax[8][256] =
+ {
+ {
+ 0xd01f715b5c7ef8e6ULL, 0x16fa240980778325ULL, 0xa8a42e857ee049c8ULL,
+ 0x6ac1068fa186465bULL, 0x6e417bd7a2e9320bULL, 0x665c8167a437daabULL,
+ 0x7666681aa89617f6ULL, 0x4b959163700bdcf5ULL, 0xf14be6b78df36248ULL,
+ 0xc585bd689a625cffULL, 0x9557d7fca67d82cbULL, 0x89f0b969af6dd366ULL,
+ 0xb0833d48749f6c35ULL, 0xa1998c23b1ecbc7cULL, 0x8d70c431ac02a736ULL,
+ 0xd6dfbc2fd0a8b69eULL, 0x37aeb3e551fa198bULL, 0x0b7d128a40b5cf9cULL,
+ 0x5a8f2008b5780cbcULL, 0xedec882284e333e5ULL, 0xd25fc177d3c7c2ceULL,
+ 0x5e0f5d50b61778ecULL, 0x1d873683c0c24cb9ULL, 0xad040bcbb45d208cULL,
+ 0x2f89a0285b853c76ULL, 0x5732fff6791b8d58ULL, 0x3e9311439ef6ec3fULL,
+ 0xc9183a809fd3c00fULL, 0x83adf3f5260a01eeULL, 0xa6791941f4e8ef10ULL,
+ 0x103ae97d0ca1cd5dULL, 0x2ce948121dee1b4aULL, 0x39738421dbf2bf53ULL,
+ 0x093da2a6cf0cf5b4ULL, 0xcd9847d89cbcb45fULL, 0xf9561c078b2d8ae8ULL,
+ 0x9c6a755a6971777fULL, 0xbc1ebaa0712ef0c5ULL, 0x72e61542abf963a6ULL,
+ 0x78bb5fde229eb12eULL, 0x14ba94250fceb90dULL, 0x844d6697630e5282ULL,
+ 0x98ea08026a1e032fULL, 0xf06bbea144217f5cULL, 0xdb6263d11ccb377aULL,
+ 0x641c314b2b8ee083ULL, 0x320e96ab9b4770cfULL, 0x1ee7deb986a96b85ULL,
+ 0xe96cf57a878c47b5ULL, 0xfdd6615f8842feb8ULL, 0xc83862965601dd1bULL,
+ 0x2ea9f83e92572162ULL, 0xf876441142ff97fcULL, 0xeb2c455608357d9dULL,
+ 0x5612a7e0b0c9904cULL, 0x6c01cbfb2d500823ULL, 0x4548a6a7fa037a2dULL,
+ 0xabc4c6bf388b6ef4ULL, 0xbade77d4fdf8bebdULL, 0x799b07c8eb4cac3aULL,
+ 0x0c9d87e805b19cf0ULL, 0xcb588aac106afa27ULL, 0xea0c1d40c1e76089ULL,
+ 0x2869354a1e816f1aULL, 0xff96d17307fbc490ULL, 0x9f0a9d602f1a5043ULL,
+ 0x96373fc6e016a5f7ULL, 0x5292dab8b3a6e41cULL, 0x9b8ae0382c752413ULL,
+ 0x4f15ec3b7364a8a5ULL, 0x3fb349555724f12bULL, 0xc7c50d4415db66d7ULL,
+ 0x92b7429ee379d1a7ULL, 0xd37f99611a15dfdaULL, 0x231427c05e34a086ULL,
+ 0xa439a96d7b51d538ULL, 0xb403401077f01865ULL, 0xdda2aea5901d7902ULL,
+ 0x0a5d4a9c8967d288ULL, 0xc265280adf660f93ULL, 0x8bb0094520d4e94eULL,
+ 0x2a29856691385532ULL, 0x42a833c5bf072941ULL, 0x73c64d54622b7eb2ULL,
+ 0x07e095624504536cULL, 0x8a905153e906f45aULL, 0x6f6123c16b3b2f1fULL,
+ 0xc6e55552dc097bc3ULL, 0x4468feb133d16739ULL, 0xe211e7f0c7398829ULL,
+ 0xa2f96419f7879b40ULL, 0x19074bdbc3ad38e9ULL, 0xf4ebc3f9474e0b0cULL,
+ 0x43886bd376d53455ULL, 0xd8028beb5aa01046ULL, 0x51f23282f5cdc320ULL,
+ 0xe7b1c2be0d84e16dULL, 0x081dfab006dee8a0ULL, 0x3b33340d544b857bULL,
+ 0x7f5bcabc679ae242ULL, 0x0edd37c48a08a6d8ULL, 0x81ed43d9a9b33bc6ULL,
+ 0xb1a3655ebd4d7121ULL, 0x69a1eeb5e7ed6167ULL, 0xf6ab73d5c8f73124ULL,
+ 0x1a67a3e185c61fd5ULL, 0x2dc91004d43c065eULL, 0x0240b02c8fb93a28ULL,
+ 0x90f7f2b26cc0eb8fULL, 0x3cd3a16f114fd617ULL, 0xaae49ea9f15973e0ULL,
+ 0x06c0cd748cd64e78ULL, 0xda423bc7d5192a6eULL, 0xc345701c16b41287ULL,
+ 0x6d2193ede4821537ULL, 0xfcf639494190e3acULL, 0x7c3b228621f1c57eULL,
+ 0xfb16ac2b0494b0c0ULL, 0xbf7e529a3745d7f9ULL, 0x6881b6a32e3f7c73ULL,
+ 0xca78d2bad9b8e733ULL, 0xbbfe2fc2342aa3a9ULL, 0x0dbddffecc6381e4ULL,
+ 0x70a6a56e2440598eULL, 0xe4d12a844befc651ULL, 0x8c509c2765d0ba22ULL,
+ 0xee8c6018c28814d9ULL, 0x17da7c1f49a59e31ULL, 0x609c4c1328e194d3ULL,
+ 0xb3e3d57232f44b09ULL, 0x91d7aaa4a512f69bULL, 0x0ffd6fd243dabbccULL,
+ 0x50d26a943c1fde34ULL, 0x6be15e9968545b4fULL, 0x94778fea6faf9fdfULL,
+ 0x2b09dd7058ea4826ULL, 0x677cd9716de5c7bfULL, 0x49d5214fffb2e6ddULL,
+ 0x0360e83a466b273cULL, 0x1fc786af4f7b7691ULL, 0xa0b9d435783ea168ULL,
+ 0xd49f0c035f118cb6ULL, 0x01205816c9d21d14ULL, 0xac2453dd7d8f3d98ULL,
+ 0x545217cc3f70aa64ULL, 0x26b4028e9489c9c2ULL, 0xdec2469fd6765e3eULL,
+ 0x04807d58036f7450ULL, 0xe5f17292823ddb45ULL, 0xf30b569b024a5860ULL,
+ 0x62dcfc3fa758aefbULL, 0xe84cad6c4e5e5aa1ULL, 0xccb81fce556ea94bULL,
+ 0x53b282ae7a74f908ULL, 0x1b47fbf74c1402c1ULL, 0x368eebf39828049fULL,
+ 0x7afbeff2ad278b06ULL, 0xbe5e0a8cfe97caedULL, 0xcfd8f7f413058e77ULL,
+ 0xf78b2bc301252c30ULL, 0x4d555c17fcdd928dULL, 0x5f2f05467fc565f8ULL,
+ 0x24f4b2a21b30f3eaULL, 0x860dd6bbecb768aaULL, 0x4c750401350f8f99ULL,
+ 0x0000000000000000ULL, 0xecccd0344d312ef1ULL, 0xb5231806be220571ULL,
+ 0xc105c030990d28afULL, 0x653c695de25cfd97ULL, 0x159acc33c61ca419ULL,
+ 0xb89ec7f872418495ULL, 0xa9847693b73254dcULL, 0x58cf90243ac13694ULL,
+ 0x59efc832f3132b80ULL, 0x5c4fed7c39ae42c4ULL, 0x828dabe3efd81cfaULL,
+ 0xd13f294d95ace5f2ULL, 0x7d1b7a90e823d86aULL, 0xb643f03cf849224dULL,
+ 0x3df3f979d89dcb03ULL, 0x7426d836272f2ddeULL, 0xdfe21e891fa4432aULL,
+ 0x3a136c1b9d99986fULL, 0xfa36f43dcd46add4ULL, 0xc025982650df35bbULL,
+ 0x856d3e81aadc4f96ULL, 0xc4a5e57e53b041ebULL, 0x4708168b75ba4005ULL,
+ 0xaf44bbe73be41aa4ULL, 0x971767d029c4b8e3ULL, 0xb9be9feebb939981ULL,
+ 0x215497ecd18d9aaeULL, 0x316e7e91dd2c57f3ULL, 0xcef8afe2dad79363ULL,
+ 0x3853dc371220a247ULL, 0x35ee03c9de4323a3ULL, 0xe6919aa8c456fc79ULL,
+ 0xe05157dc4880b201ULL, 0x7bdbb7e464f59612ULL, 0x127a59518318f775ULL,
+ 0x332ecebd52956ddbULL, 0x8f30741d23bb9d1eULL, 0xd922d3fd93720d52ULL,
+ 0x7746300c61440ae2ULL, 0x25d4eab4d2e2eefeULL, 0x75068020eefd30caULL,
+ 0x135a01474acaea61ULL, 0x304e268714fe4ae7ULL, 0xa519f17bb283c82cULL,
+ 0xdc82f6b359cf6416ULL, 0x5baf781e7caa11a8ULL, 0xb2c38d64fb26561dULL,
+ 0x34ce5bdf17913eb7ULL, 0x5d6fb56af07c5fd0ULL, 0x182713cd0a7f25fdULL,
+ 0x9e2ac576e6c84d57ULL, 0x9aaab82ee5a73907ULL, 0xa3d93c0f3e558654ULL,
+ 0x7e7b92aaae48ff56ULL, 0x872d8ead256575beULL, 0x41c8dbfff96c0e7dULL,
+ 0x99ca5014a3cc1e3bULL, 0x40e883e930be1369ULL, 0x1ca76e95091051adULL,
+ 0x4e35b42dbab6b5b1ULL, 0x05a0254ecabd6944ULL, 0xe1710fca8152af15ULL,
+ 0xf22b0e8dcb984574ULL, 0xb763a82a319b3f59ULL, 0x63fca4296e8ab3efULL,
+ 0x9d4a2d4ca0a36a6bULL, 0xe331bfe60eeb953dULL, 0xd5bf541596c391a2ULL,
+ 0xf5cb9bef8e9c1618ULL, 0x46284e9dbc685d11ULL, 0x2074cffa185f87baULL,
+ 0xbd3ee2b6b8fcedd1ULL, 0xae64e3f1f23607b0ULL, 0xfeb68965ce29d984ULL,
+ 0x55724fdaf6a2b770ULL, 0x29496d5cd753720eULL, 0xa75941573d3af204ULL,
+ 0x8e102c0bea69800aULL, 0x111ab16bc573d049ULL, 0xd7ffe439197aab8aULL,
+ 0xefac380e0b5a09cdULL, 0x48f579593660fbc9ULL, 0x22347fd697e6bd92ULL,
+ 0x61bc1405e13389c7ULL, 0x4ab5c975b9d9c1e1ULL, 0x80cd1bcf606126d2ULL,
+ 0x7186fd78ed92449aULL, 0x93971a882aabccb3ULL, 0x88d0e17f66bfce72ULL,
+ 0x27945a985d5bd4d6ULL
+ },
+ {
+ 0xde553f8c05a811c8ULL, 0x1906b59631b4f565ULL, 0x436e70d6b1964ff7ULL,
+ 0x36d343cb8b1e9d85ULL, 0x843dfacc858aab5aULL, 0xfdfc95c299bfc7f9ULL,
+ 0x0f634bdea1d51fa2ULL, 0x6d458b3b76efb3cdULL, 0x85c3f77cf8593f80ULL,
+ 0x3c91315fbe737cb2ULL, 0x2148b03366ace398ULL, 0x18f8b8264c6761bfULL,
+ 0xc830c1c495c9fb0fULL, 0x981a76102086a0aaULL, 0xaa16012142f35760ULL,
+ 0x35cc54060c763cf6ULL, 0x42907d66cc45db2dULL, 0x8203d44b965af4bcULL,
+ 0x3d6f3cefc3a0e868ULL, 0xbc73ff69d292bda7ULL, 0x8722ed0102e20a29ULL,
+ 0x8f8185e8cd34deb7ULL, 0x9b0561dda7ee01d9ULL, 0x5335a0193227fad6ULL,
+ 0xc9cecc74e81a6fd5ULL, 0x54f5832e5c2431eaULL, 0x99e47ba05d553470ULL,
+ 0xf7bee756acd226ceULL, 0x384e05a5571816fdULL, 0xd1367452a47d0e6aULL,
+ 0xf29fde1c386ad85bULL, 0x320c77316275f7caULL, 0xd0c879e2d9ae9ab0ULL,
+ 0xdb7406c69110ef5dULL, 0x45505e51a2461011ULL, 0xfc029872e46c5323ULL,
+ 0xfa3cb6f5f7bc0cc5ULL, 0x031f17cd8768a173ULL, 0xbd8df2d9af41297dULL,
+ 0x9d3b4f5ab43e5e3fULL, 0x4071671b36feee84ULL, 0x716207e7d3e3b83dULL,
+ 0x48d20ff2f9283a1aULL, 0x27769eb4757cbc7eULL, 0x5c56ebc793f2e574ULL,
+ 0xa48b474f9ef5dc18ULL, 0x52cbada94ff46e0cULL, 0x60c7da982d8199c6ULL,
+ 0x0e9d466edc068b78ULL, 0x4eec2175eaf865fcULL, 0x550b8e9e21f7a530ULL,
+ 0x6b7ba5bc653fec2bULL, 0x5eb7f1ba6949d0ddULL, 0x57ea94e3db4c9099ULL,
+ 0xf640eae6d101b214ULL, 0xdd4a284182c0b0bbULL, 0xff1d8fbf6304f250ULL,
+ 0xb8accb933bf9d7e8ULL, 0xe8867c478eb68c4dULL, 0x3f8e2692391bddc1ULL,
+ 0xcb2fd60912a15a7cULL, 0xaec935dbab983d2fULL, 0xf55ffd2b56691367ULL,
+ 0x80e2ce366ce1c115ULL, 0x179bf3f8edb27e1dULL, 0x01fe0db07dd394daULL,
+ 0xda8a0b76ecc37b87ULL, 0x44ae53e1df9584cbULL, 0xb310b4b77347a205ULL,
+ 0xdfab323c787b8512ULL, 0x3b511268d070b78eULL, 0x65e6e3d2b9396753ULL,
+ 0x6864b271e2574d58ULL, 0x259784c98fc789d7ULL, 0x02e11a7dfabb35a9ULL,
+ 0x8841a6dfa337158bULL, 0x7ade78c39b5dcdd0ULL, 0xb7cf804d9a2cc84aULL,
+ 0x20b6bd831b7f7742ULL, 0x75bd331d3a88d272ULL, 0x418f6aab4b2d7a5eULL,
+ 0xd9951cbb6babdaf4ULL, 0xb6318dfde7ff5c90ULL, 0x1f389b112264aa83ULL,
+ 0x492c024284fbaec0ULL, 0xe33a0363c608f9a0ULL, 0x2688930408af28a4ULL,
+ 0xc7538a1a341ce4adULL, 0x5da8e677ee2171aeULL, 0x8c9e92254a5c7fc4ULL,
+ 0x63d8cd55aae938b5ULL, 0x29ebd8daa97a3706ULL, 0x959827b37be88aa1ULL,
+ 0x1484e4356adadf6eULL, 0xa7945082199d7d6bULL, 0xbf6ce8a455fa1cd4ULL,
+ 0x9cc542eac9edcae5ULL, 0x79c16f0e1c356ca3ULL, 0x89bfab6fdee48151ULL,
+ 0xd4174d1830c5f0ffULL, 0x9258048415eb419dULL, 0x6139d72850520d1cULL,
+ 0x6a85a80c18ec78f1ULL, 0xcd11f88e0171059aULL, 0xcceff53e7ca29140ULL,
+ 0xd229639f2315af19ULL, 0x90b91ef9ef507434ULL, 0x5977d28d074a1be1ULL,
+ 0x311360fce51d56b9ULL, 0xc093a92d5a1f2f91ULL, 0x1a19a25bb6dc5416ULL,
+ 0xeb996b8a09de2d3eULL, 0xfee3820f1ed7668aULL, 0xd7085ad5b7ad518cULL,
+ 0x7fff41890fe53345ULL, 0xec5948bd67dde602ULL, 0x2fd5f65dbaaa68e0ULL,
+ 0xa5754affe32648c2ULL, 0xf8ddac880d07396cULL, 0x6fa491468c548664ULL,
+ 0x0c7c5c1326bdbed1ULL, 0x4a33158f03930fb3ULL, 0x699abfc19f84d982ULL,
+ 0xe4fa2054a80b329cULL, 0x6707f9af438252faULL, 0x08a368e9cfd6d49eULL,
+ 0x47b1442c58fd25b8ULL, 0xbbb3dc5ebc91769bULL, 0x1665fe489061eac7ULL,
+ 0x33f27a811fa66310ULL, 0x93a609346838d547ULL, 0x30ed6d4c98cec263ULL,
+ 0x1dd9816cd8df9f2aULL, 0x94662a03063b1e7bULL, 0x83fdd9fbeb896066ULL,
+ 0x7b207573e68e590aULL, 0x5f49fc0a149a4407ULL, 0x343259b671a5a82cULL,
+ 0xfbc2bb458a6f981fULL, 0xc272b350a0a41a38ULL, 0x3aaf1fd8ada32354ULL,
+ 0x6cbb868b0b3c2717ULL, 0xa2b569c88d2583feULL, 0xf180c9d1bf027928ULL,
+ 0xaf37386bd64ba9f5ULL, 0x12bacab2790a8088ULL, 0x4c0d3b0810435055ULL,
+ 0xb2eeb9070e9436dfULL, 0xc5b29067cea7d104ULL, 0xdcb425f1ff132461ULL,
+ 0x4f122cc5972bf126ULL, 0xac282fa651230886ULL, 0xe7e537992f6393efULL,
+ 0xe61b3a2952b00735ULL, 0x709c0a57ae302ce7ULL, 0xe02514ae416058d3ULL,
+ 0xc44c9dd7b37445deULL, 0x5a68c5408022ba92ULL, 0x1c278cdca50c0bf0ULL,
+ 0x6e5a9cf6f18712beULL, 0x86dce0b17f319ef3ULL, 0x2d34ec2040115d49ULL,
+ 0x4bcd183f7e409b69ULL, 0x2815d56ad4a9a3dcULL, 0x24698979f2141d0dULL,
+ 0x0000000000000000ULL, 0x1ec696a15fb73e59ULL, 0xd86b110b16784e2eULL,
+ 0x8e7f8858b0e74a6dULL, 0x063e2e8713d05fe6ULL, 0xe2c40ed3bbdb6d7aULL,
+ 0xb1f1aeca89fc97acULL, 0xe1db191e3cb3cc09ULL, 0x6418ee62c4eaf389ULL,
+ 0xc6ad87aa49cf7077ULL, 0xd6f65765ca7ec556ULL, 0x9afb6c6dda3d9503ULL,
+ 0x7ce05644888d9236ULL, 0x8d609f95378feb1eULL, 0x23a9aa4e9c17d631ULL,
+ 0x6226c0e5d73aac6fULL, 0x56149953a69f0443ULL, 0xeeb852c09d66d3abULL,
+ 0x2b0ac2a753c102afULL, 0x07c023376e03cb3cULL, 0x2ccae1903dc2c993ULL,
+ 0xd3d76e2f5ec63bc3ULL, 0x9e2458973356ff4cULL, 0xa66a5d32644ee9b1ULL,
+ 0x0a427294356de137ULL, 0x783f62be61e6f879ULL, 0x1344c70204d91452ULL,
+ 0x5b96c8f0fdf12e48ULL, 0xa90916ecc59bf613ULL, 0xbe92e5142829880eULL,
+ 0x727d102a548b194eULL, 0x1be7afebcb0fc0ccULL, 0x3e702b2244c8491bULL,
+ 0xd5e940a84d166425ULL, 0x66f9f41f3e51c620ULL, 0xabe80c913f20c3baULL,
+ 0xf07ec461c2d1edf2ULL, 0xf361d3ac45b94c81ULL, 0x0521394a94b8fe95ULL,
+ 0xadd622162cf09c5cULL, 0xe97871f7f3651897ULL, 0xf4a1f09b2bba87bdULL,
+ 0x095d6559b2054044ULL, 0x0bbc7f2448be75edULL, 0x2af4cf172e129675ULL,
+ 0x157ae98517094bb4ULL, 0x9fda55274e856b96ULL, 0x914713499283e0eeULL,
+ 0xb952c623462a4332ULL, 0x74433ead475b46a8ULL, 0x8b5eb112245fb4f8ULL,
+ 0xa34b6478f0f61724ULL, 0x11a5dd7ffe6221fbULL, 0xc16da49d27ccbb4bULL,
+ 0x76a224d0bde07301ULL, 0x8aa0bca2598c2022ULL, 0x4df336b86d90c48fULL,
+ 0xea67663a740db9e4ULL, 0xef465f70e0b54771ULL, 0x39b008152acb8227ULL,
+ 0x7d1e5bf4f55e06ecULL, 0x105bd0cf83b1b521ULL, 0x775c2960c033e7dbULL,
+ 0x7e014c397236a79fULL, 0x811cc386113255cfULL, 0xeda7450d1a0e72d8ULL,
+ 0x5889df3d7a998f3bULL, 0x2e2bfbedc779fc3aULL, 0xce0eef438619a4e9ULL,
+ 0x372d4e7bf6cd095fULL, 0x04df34fae96b6a4fULL, 0xf923a13870d4adb6ULL,
+ 0xa1aa7e050a4d228dULL, 0xa8f71b5cb84862c9ULL, 0xb52e9a306097fde3ULL,
+ 0x0d8251a35b6e2a0bULL, 0x2257a7fee1c442ebULL, 0x73831d9a29588d94ULL,
+ 0x51d4ba64c89ccf7fULL, 0x502ab7d4b54f5ba5ULL, 0x97793dce8153bf08ULL,
+ 0xe5042de4d5d8a646ULL, 0x9687307efc802bd2ULL, 0xa05473b5779eb657ULL,
+ 0xb4d097801d446939ULL, 0xcff0e2f3fbca3033ULL, 0xc38cbee0dd778ee2ULL,
+ 0x464f499c252eb162ULL, 0xcad1dbb96f72cea6ULL, 0xba4dd1eec142e241ULL,
+ 0xb00fa37af42f0376ULL
+ },
+ {
+ 0xcce4cd3aa968b245ULL, 0x089d5484e80b7fafULL, 0x638246c1b3548304ULL,
+ 0xd2fe0ec8c2355492ULL, 0xa7fbdf7ff2374eeeULL, 0x4df1600c92337a16ULL,
+ 0x84e503ea523b12fbULL, 0x0790bbfd53ab0c4aULL, 0x198a780f38f6ea9dULL,
+ 0x2ab30c8f55ec48cbULL, 0xe0f7fed6b2c49db5ULL, 0xb6ecf3f422cadbdcULL,
+ 0x409c9a541358df11ULL, 0xd3ce8a56dfde3fe3ULL, 0xc3e9224312c8c1a0ULL,
+ 0x0d6dfa58816ba507ULL, 0xddf3e1b179952777ULL, 0x04c02a42748bb1d9ULL,
+ 0x94c2abff9f2decb8ULL, 0x4f91752da8f8acf4ULL, 0x78682befb169bf7bULL,
+ 0xe1c77a48af2ff6c4ULL, 0x0c5d7ec69c80ce76ULL, 0x4cc1e4928fd81167ULL,
+ 0xfeed3d24d9997b62ULL, 0x518bb6dfc3a54a23ULL, 0x6dbf2d26151f9b90ULL,
+ 0xb5bc624b05ea664fULL, 0xe86aaa525acfe21aULL, 0x4801ced0fb53a0beULL,
+ 0xc91463e6c00868edULL, 0x1027a815cd16fe43ULL, 0xf67069a0319204cdULL,
+ 0xb04ccc976c8abce7ULL, 0xc0b9b3fc35e87c33ULL, 0xf380c77c58f2de65ULL,
+ 0x50bb3241de4e2152ULL, 0xdf93f490435ef195ULL, 0xf1e0d25d62390887ULL,
+ 0xaf668bfb1a3c3141ULL, 0xbc11b251f00a7291ULL, 0x73a5eed47e427d47ULL,
+ 0x25bee3f6ee4c3b2eULL, 0x43cc0beb34786282ULL, 0xc824e778dde3039cULL,
+ 0xf97d86d98a327728ULL, 0xf2b043e24519b514ULL, 0xe297ebf7880f4b57ULL,
+ 0x3a94a49a98fab688ULL, 0x868516cb68f0c419ULL, 0xeffa11af0964ee50ULL,
+ 0xa4ab4ec0d517f37dULL, 0xa9c6b498547c567aULL, 0x8e18424f80fbbbb6ULL,
+ 0x0bcdc53bcf2bc23cULL, 0x137739aaea3643d0ULL, 0x2c1333ec1bac2ff0ULL,
+ 0x8d48d3f0a7db0625ULL, 0x1e1ac3f26b5de6d7ULL, 0xf520f81f16b2b95eULL,
+ 0x9f0f6ec450062e84ULL, 0x0130849e1deb6b71ULL, 0xd45e31ab8c7533a9ULL,
+ 0x652279a2fd14e43fULL, 0x3209f01e70f1c927ULL, 0xbe71a770cac1a473ULL,
+ 0x0e3d6be7a64b1894ULL, 0x7ec8148cff29d840ULL, 0xcb7476c7fac3be0fULL,
+ 0x72956a4a63a91636ULL, 0x37f95ec21991138fULL, 0x9e3fea5a4ded45f5ULL,
+ 0x7b38ba50964902e8ULL, 0x222e580bbde73764ULL, 0x61e253e0899f55e6ULL,
+ 0xfc8d2805e352ad80ULL, 0x35994be3235ac56dULL, 0x09add01af5e014deULL,
+ 0x5e8659a6780539c6ULL, 0xb17c48097161d796ULL, 0x026015213acbd6e2ULL,
+ 0xd1ae9f77e515e901ULL, 0xb7dc776a3f21b0adULL, 0xaba6a1b96eb78098ULL,
+ 0x9bcf4486248d9f5dULL, 0x582666c536455efdULL, 0xfdbdac9bfeb9c6f1ULL,
+ 0xc47999be4163cdeaULL, 0x765540081722a7efULL, 0x3e548ed8ec710751ULL,
+ 0x3d041f67cb51bac2ULL, 0x7958af71ac82d40aULL, 0x36c9da5c047a78feULL,
+ 0xed9a048e33af38b2ULL, 0x26ee7249c96c86bdULL, 0x900281bdeba65d61ULL,
+ 0x11172c8bd0fd9532ULL, 0xea0abf73600434f8ULL, 0x42fc8f75299309f3ULL,
+ 0x34a9cf7d3eb1ae1cULL, 0x2b838811480723baULL, 0x5ce64c8742ceef24ULL,
+ 0x1adae9b01fd6570eULL, 0x3c349bf9d6bad1b3ULL, 0x82453c891c7b75c0ULL,
+ 0x97923a40b80d512bULL, 0x4a61dbf1c198765cULL, 0xb48ce6d518010d3eULL,
+ 0xcfb45c858e480fd6ULL, 0xd933cbf30d1e96aeULL, 0xd70ea014ab558e3aULL,
+ 0xc189376228031742ULL, 0x9262949cd16d8b83ULL, 0xeb3a3bed7def5f89ULL,
+ 0x49314a4ee6b8cbcfULL, 0xdcc3652f647e4c06ULL, 0xda635a4c2a3e2b3dULL,
+ 0x470c21a940f3d35bULL, 0x315961a157d174b4ULL, 0x6672e81dda3459acULL,
+ 0x5b76f77a1165e36eULL, 0x445cb01667d36ec8ULL, 0xc5491d205c88a69bULL,
+ 0x456c34887a3805b9ULL, 0xffddb9bac4721013ULL, 0x99af51a71e4649bfULL,
+ 0xa15be01cbc7729d5ULL, 0x52db2760e485f7b0ULL, 0x8c78576eba306d54ULL,
+ 0xae560f6507d75a30ULL, 0x95f22f6182c687c9ULL, 0x71c5fbf54489aba5ULL,
+ 0xca44f259e728d57eULL, 0x88b87d2ccebbdc8dULL, 0xbab18d32be4a15aaULL,
+ 0x8be8ec93e99b611eULL, 0x17b713e89ebdf209ULL, 0xb31c5d284baa0174ULL,
+ 0xeeca9531148f8521ULL, 0xb8d198138481c348ULL, 0x8988f9b2d350b7fcULL,
+ 0xb9e11c8d996aa839ULL, 0x5a4673e40c8e881fULL, 0x1687977683569978ULL,
+ 0xbf4123eed72acf02ULL, 0x4ea1f1b3b513c785ULL, 0xe767452be16f91ffULL,
+ 0x7505d1b730021a7cULL, 0xa59bca5ec8fc980cULL, 0xad069eda20f7e7a3ULL,
+ 0x38f4b1bba231606aULL, 0x60d2d77e94743e97ULL, 0x9affc0183966f42cULL,
+ 0x248e6768f3a7505fULL, 0xcdd449a4b483d934ULL, 0x87b59255751baf68ULL,
+ 0x1bea6d2e023d3c7fULL, 0x6b1f12455b5ffcabULL, 0x743555292de9710dULL,
+ 0xd8034f6d10f5fddfULL, 0xc6198c9f7ba81b08ULL, 0xbb8109aca3a17edbULL,
+ 0xfa2d1766ad12cabbULL, 0xc729080166437079ULL, 0x9c5fff7b77269317ULL,
+ 0x0000000000000000ULL, 0x15d706c9a47624ebULL, 0x6fdf38072fd44d72ULL,
+ 0x5fb6dd3865ee52b7ULL, 0xa33bf53d86bcff37ULL, 0xe657c1b5fc84fa8eULL,
+ 0xaa962527735cebe9ULL, 0x39c43525bfda0b1bULL, 0x204e4d2a872ce186ULL,
+ 0x7a083ece8ba26999ULL, 0x554b9c9db72efbfaULL, 0xb22cd9b656416a05ULL,
+ 0x96a2bedea5e63a5aULL, 0x802529a826b0a322ULL, 0x8115ad363b5bc853ULL,
+ 0x8375b81701901eb1ULL, 0x3069e53f4a3a1fc5ULL, 0xbd2136cfede119e0ULL,
+ 0x18bafc91251d81ecULL, 0x1d4a524d4c7d5b44ULL, 0x05f0aedc6960daa8ULL,
+ 0x29e39d3072ccf558ULL, 0x70f57f6b5962c0d4ULL, 0x989fd53903ad22ceULL,
+ 0xf84d024797d91c59ULL, 0x547b1803aac5908bULL, 0xf0d056c37fd263f6ULL,
+ 0xd56eb535919e58d8ULL, 0x1c7ad6d351963035ULL, 0x2e7326cd2167f912ULL,
+ 0xac361a443d1c8cd2ULL, 0x697f076461942a49ULL, 0x4b515f6fdc731d2dULL,
+ 0x8ad8680df4700a6fULL, 0x41ac1eca0eb3b460ULL, 0x7d988533d80965d3ULL,
+ 0xa8f6300649973d0bULL, 0x7765c4960ac9cc9eULL, 0x7ca801adc5e20ea2ULL,
+ 0xdea3700e5eb59ae4ULL, 0xa06b6482a19c42a4ULL, 0x6a2f96db46b497daULL,
+ 0x27def6d7d487edccULL, 0x463ca5375d18b82aULL, 0xa6cb5be1efdc259fULL,
+ 0x53eba3fef96e9cc1ULL, 0xce84d81b93a364a7ULL, 0xf4107c810b59d22fULL,
+ 0x333974806d1aa256ULL, 0x0f0def79bba073e5ULL, 0x231edc95a00c5c15ULL,
+ 0xe437d494c64f2c6cULL, 0x91320523f64d3610ULL, 0x67426c83c7df32ddULL,
+ 0x6eefbc99323f2603ULL, 0x9d6f7be56acdf866ULL, 0x5916e25b2bae358cULL,
+ 0x7ff89012e2c2b331ULL, 0x035091bf2720bd93ULL, 0x561b0d22900e4669ULL,
+ 0x28d319ae6f279e29ULL, 0x2f43a2533c8c9263ULL, 0xd09e1be9f8fe8270ULL,
+ 0xf740ed3e2c796fbcULL, 0xdb53ded237d5404cULL, 0x62b2c25faebfe875ULL,
+ 0x0afd41a5d2c0a94dULL, 0x6412fd3ce0ff8f4eULL, 0xe3a76f6995e42026ULL,
+ 0x6c8fa9b808f4f0e1ULL, 0xc2d9a6dd0f23aad1ULL, 0x8f28c6d19d10d0c7ULL,
+ 0x85d587744fd0798aULL, 0xa20b71a39b579446ULL, 0x684f83fa7c7f4138ULL,
+ 0xe507500adba4471dULL, 0x3f640a46f19a6c20ULL, 0x1247bd34f7dd28a1ULL,
+ 0x2d23b77206474481ULL, 0x93521002cc86e0f2ULL, 0x572b89bc8de52d18ULL,
+ 0xfb1d93f8b0f9a1caULL, 0xe95a2ecc4724896bULL, 0x3ba420048511ddf9ULL,
+ 0xd63e248ab6bee54bULL, 0x5dd6c8195f258455ULL, 0x06a03f634e40673bULL,
+ 0x1f2a476c76b68da6ULL, 0x217ec9b49ac78af7ULL, 0xecaa80102e4453c3ULL,
+ 0x14e78257b99d4f9aULL
+ },
+ {
+ 0x20329b2cc87bba05ULL, 0x4f5eb6f86546a531ULL, 0xd4f44775f751b6b1ULL,
+ 0x8266a47b850dfa8bULL, 0xbb986aa15a6ca985ULL, 0xc979eb08f9ae0f99ULL,
+ 0x2da6f447a2375ea1ULL, 0x1e74275dcd7d8576ULL, 0xbc20180a800bc5f8ULL,
+ 0xb4a2f701b2dc65beULL, 0xe726946f981b6d66ULL, 0x48e6c453bf21c94cULL,
+ 0x42cad9930f0a4195ULL, 0xefa47b64aacccd20ULL, 0x71180a8960409a42ULL,
+ 0x8bb3329bf6a44e0cULL, 0xd34c35de2d36daccULL, 0xa92f5b7cbc23dc96ULL,
+ 0xb31a85aa68bb09c3ULL, 0x13e04836a73161d2ULL, 0xb24dfc4129c51d02ULL,
+ 0x8ae44b70b7da5acdULL, 0xe671ed84d96579a7ULL, 0xa4bb3417d66f3832ULL,
+ 0x4572ab38d56d2de8ULL, 0xb1b47761ea47215cULL, 0xe81c09cf70aba15dULL,
+ 0xffbdb872ce7f90acULL, 0xa8782297fd5dc857ULL, 0x0d946f6b6a4ce4a4ULL,
+ 0xe4df1f4f5b995138ULL, 0x9ebc71edca8c5762ULL, 0x0a2c1dc0b02b88d9ULL,
+ 0x3b503c115d9d7b91ULL, 0xc64376a8111ec3a2ULL, 0xcec199a323c963e4ULL,
+ 0xdc76a87ec58616f7ULL, 0x09d596e073a9b487ULL, 0x14583a9d7d560dafULL,
+ 0xf4c6dc593f2a0cb4ULL, 0xdd21d19584f80236ULL, 0x4a4836983ddde1d3ULL,
+ 0xe58866a41ae745f9ULL, 0xf591a5b27e541875ULL, 0x891dc05074586693ULL,
+ 0x5b068c651810a89eULL, 0xa30346bc0c08544fULL, 0x3dbf3751c684032dULL,
+ 0x2a1e86ec785032dcULL, 0xf73f5779fca830eaULL, 0xb60c05ca30204d21ULL,
+ 0x0cc316802b32f065ULL, 0x8770241bdd96be69ULL, 0xb861e18199ee95dbULL,
+ 0xf805cad91418fcd1ULL, 0x29e70dccbbd20e82ULL, 0xc7140f435060d763ULL,
+ 0x0f3a9da0e8b0cc3bULL, 0xa2543f574d76408eULL, 0xbd7761e1c175d139ULL,
+ 0x4b1f4f737ca3f512ULL, 0x6dc2df1f2fc137abULL, 0xf1d05c3967b14856ULL,
+ 0xa742bf3715ed046cULL, 0x654030141d1697edULL, 0x07b872abda676c7dULL,
+ 0x3ce84eba87fa17ecULL, 0xc1fb0403cb79afdfULL, 0x3e46bc7105063f73ULL,
+ 0x278ae987121cd678ULL, 0xa1adb4778ef47cd0ULL, 0x26dd906c5362c2b9ULL,
+ 0x05168060589b44e2ULL, 0xfbfc41f9d79ac08fULL, 0x0e6de44ba9ced8faULL,
+ 0x9feb08068bf243a3ULL, 0x7b341749d06b129bULL, 0x229c69e74a87929aULL,
+ 0xe09ee6c4427c011bULL, 0x5692e30e725c4c3aULL, 0xda99a33e5e9f6e4bULL,
+ 0x353dd85af453a36bULL, 0x25241b4c90e0fee7ULL, 0x5de987258309d022ULL,
+ 0xe230140fc0802984ULL, 0x93281e86a0c0b3c6ULL, 0xf229d719a4337408ULL,
+ 0x6f6c2dd4ad3d1f34ULL, 0x8ea5b2fbae3f0aeeULL, 0x8331dd90c473ee4aULL,
+ 0x346aa1b1b52db7aaULL, 0xdf8f235e06042aa9ULL, 0xcc6f6b68a1354b7bULL,
+ 0x6c95a6f46ebf236aULL, 0x52d31a856bb91c19ULL, 0x1a35ded6d498d555ULL,
+ 0xf37eaef2e54d60c9ULL, 0x72e181a9a3c2a61cULL, 0x98537aad51952fdeULL,
+ 0x16f6c856ffaa2530ULL, 0xd960281e9d1d5215ULL, 0x3a0745fa1ce36f50ULL,
+ 0x0b7b642bf1559c18ULL, 0x59a87eae9aec8001ULL, 0x5e100c05408bec7cULL,
+ 0x0441f98b19e55023ULL, 0xd70dcc5534d38aefULL, 0x927f676de1bea707ULL,
+ 0x9769e70db925e3e5ULL, 0x7a636ea29115065aULL, 0x468b201816ef11b6ULL,
+ 0xab81a9b73edff409ULL, 0xc0ac7de88a07bb1eULL, 0x1f235eb68c0391b7ULL,
+ 0x6056b074458dd30fULL, 0xbe8eeac102f7ed67ULL, 0xcd381283e04b5fbaULL,
+ 0x5cbefecec277c4e3ULL, 0xd21b4c356c48ce0dULL, 0x1019c31664b35d8cULL,
+ 0x247362a7d19eea26ULL, 0xebe582efb3299d03ULL, 0x02aef2cb82fc289fULL,
+ 0x86275df09ce8aaa8ULL, 0x28b07427faac1a43ULL, 0x38a9b7319e1f47cfULL,
+ 0xc82e92e3b8d01b58ULL, 0x06ef0b409b1978bcULL, 0x62f842bfc771fb90ULL,
+ 0x9904034610eb3b1fULL, 0xded85ab5477a3e68ULL, 0x90d195a663428f98ULL,
+ 0x5384636e2ac708d8ULL, 0xcbd719c37b522706ULL, 0xae9729d76644b0ebULL,
+ 0x7c8c65e20a0c7ee6ULL, 0x80c856b007f1d214ULL, 0x8c0b40302cc32271ULL,
+ 0xdbcedad51fe17a8aULL, 0x740e8ae938dbdea0ULL, 0xa615c6dc549310adULL,
+ 0x19cc55f6171ae90bULL, 0x49b1bdb8fe5fdd8dULL, 0xed0a89af2830e5bfULL,
+ 0x6a7aadb4f5a65bd6ULL, 0x7e22972988f05679ULL, 0xf952b3325566e810ULL,
+ 0x39fecedadf61530eULL, 0x6101c99f04f3c7ceULL, 0x2e5f7f6761b562ffULL,
+ 0xf08725d226cf5c97ULL, 0x63af3b54860fef51ULL, 0x8ff2cb10ef411e2fULL,
+ 0x884ab9bb35267252ULL, 0x4df04433e7ba8daeULL, 0x9afd8866d3690741ULL,
+ 0x66b9bb34de94abb3ULL, 0x9baaf18d92171380ULL, 0x543c11c5f0a064a5ULL,
+ 0x17a1b1bdbed431f1ULL, 0xb5f58eeaf3a2717fULL, 0xc355f6c849858740ULL,
+ 0xec5df044694ef17eULL, 0xd83751f5dc6346d4ULL, 0xfc4433520dfdacf2ULL,
+ 0x0000000000000000ULL, 0x5a51f58e596ebc5fULL, 0x3285aaf12e34cf16ULL,
+ 0x8d5c39db6dbd36b0ULL, 0x12b731dde64f7513ULL, 0x94906c2d7aa7dfbbULL,
+ 0x302b583aacc8e789ULL, 0x9d45facd090e6b3cULL, 0x2165e2c78905aec4ULL,
+ 0x68d45f7f775a7349ULL, 0x189b2c1d5664fdcaULL, 0xe1c99f2f030215daULL,
+ 0x6983269436246788ULL, 0x8489af3b1e148237ULL, 0xe94b702431d5b59cULL,
+ 0x33d2d31a6f4adbd7ULL, 0xbfd9932a4389f9a6ULL, 0xb0e30e8aab39359dULL,
+ 0xd1e2c715afcaf253ULL, 0x150f43763c28196eULL, 0xc4ed846393e2eb3dULL,
+ 0x03f98b20c3823c5eULL, 0xfd134ab94c83b833ULL, 0x556b682eb1de7064ULL,
+ 0x36c4537a37d19f35ULL, 0x7559f30279a5ca61ULL, 0x799ae58252973a04ULL,
+ 0x9c12832648707ffdULL, 0x78cd9c6913e92ec5ULL, 0x1d8dac7d0effb928ULL,
+ 0x439da0784e745554ULL, 0x413352b3cc887dcbULL, 0xbacf134a1b12bd44ULL,
+ 0x114ebafd25cd494dULL, 0x2f08068c20cb763eULL, 0x76a07822ba27f63fULL,
+ 0xeab2fb04f25789c2ULL, 0xe3676de481fe3d45ULL, 0x1b62a73d95e6c194ULL,
+ 0x641749ff5c68832cULL, 0xa5ec4dfc97112cf3ULL, 0xf6682e92bdd6242bULL,
+ 0x3f11c59a44782bb2ULL, 0x317c21d1edb6f348ULL, 0xd65ab5be75ad9e2eULL,
+ 0x6b2dd45fb4d84f17ULL, 0xfaab381296e4d44eULL, 0xd0b5befeeeb4e692ULL,
+ 0x0882ef0b32d7a046ULL, 0x512a91a5a83b2047ULL, 0x963e9ee6f85bf724ULL,
+ 0x4e09cf132438b1f0ULL, 0x77f701c9fb59e2feULL, 0x7ddb1c094b726a27ULL,
+ 0x5f4775ee01f5f8bdULL, 0x9186ec4d223c9b59ULL, 0xfeeac1998f01846dULL,
+ 0xac39db1ce4b89874ULL, 0xb75b7c21715e59e0ULL, 0xafc0503c273aa42aULL,
+ 0x6e3b543fec430bf5ULL, 0x704f7362213e8e83ULL, 0x58ff0745db9294c0ULL,
+ 0x67eec2df9feabf72ULL, 0xa0facd9ccf8a6811ULL, 0xb936986ad890811aULL,
+ 0x95c715c63bd9cb7aULL, 0xca8060283a2c33c7ULL, 0x507de84ee9453486ULL,
+ 0x85ded6d05f6a96f6ULL, 0x1cdad5964f81ade9ULL, 0xd5a33e9eb62fa270ULL,
+ 0x40642b588df6690aULL, 0x7f75eec2c98e42b8ULL, 0x2cf18dace3494a60ULL,
+ 0x23cb100c0bf9865bULL, 0xeef3028febb2d9e1ULL, 0x4425d2d394133929ULL,
+ 0xaad6d05c7fa1e0c8ULL, 0xad6ea2f7a5c68cb5ULL, 0xc2028f2308fb9381ULL,
+ 0x819f2f5b468fc6d5ULL, 0xc5bafd88d29cfffcULL, 0x47dc59f357910577ULL,
+ 0x2b49ff07392e261dULL, 0x57c59ae5332258fbULL, 0x73b6f842e2bcb2ddULL,
+ 0xcf96e04862b77725ULL, 0x4ca73dd8a6c4996fULL, 0x015779eb417e14c1ULL,
+ 0x37932a9176af8bf4ULL
+ },
+ {
+ 0x190a2c9b249df23eULL, 0x2f62f8b62263e1e9ULL, 0x7a7f754740993655ULL,
+ 0x330b7ba4d5564d9fULL, 0x4c17a16a46672582ULL, 0xb22f08eb7d05f5b8ULL,
+ 0x535f47f40bc148ccULL, 0x3aec5d27d4883037ULL, 0x10ed0a1825438f96ULL,
+ 0x516101f72c233d17ULL, 0x13cc6f949fd04eaeULL, 0x739853c441474bfdULL,
+ 0x653793d90d3f5b1bULL, 0x5240647b96b0fc2fULL, 0x0c84890ad27623e0ULL,
+ 0xd7189b32703aaea3ULL, 0x2685de3523bd9c41ULL, 0x99317c5b11bffefaULL,
+ 0x0d9baa854f079703ULL, 0x70b93648fbd48ac5ULL, 0xa80441fce30bc6beULL,
+ 0x7287704bdc36ff1eULL, 0xb65384ed33dc1f13ULL, 0xd36417343ee34408ULL,
+ 0x39cd38ab6e1bf10fULL, 0x5ab861770a1f3564ULL, 0x0ebacf09f594563bULL,
+ 0xd04572b884708530ULL, 0x3cae9722bdb3af47ULL, 0x4a556b6f2f5cbaf2ULL,
+ 0xe1704f1f76c4bd74ULL, 0x5ec4ed7144c6dfcfULL, 0x16afc01d4c7810e6ULL,
+ 0x283f113cd629ca7aULL, 0xaf59a8761741ed2dULL, 0xeed5a3991e215facULL,
+ 0x3bf37ea849f984d4ULL, 0xe413e096a56ce33cULL, 0x2c439d3a98f020d1ULL,
+ 0x637559dc6404c46bULL, 0x9e6c95d1e5f5d569ULL, 0x24bb9836045fe99aULL,
+ 0x44efa466dac8ecc9ULL, 0xc6eab2a5c80895d6ULL, 0x803b50c035220cc4ULL,
+ 0x0321658cba93c138ULL, 0x8f9ebc465dc7ee1cULL, 0xd15a5137190131d3ULL,
+ 0x0fa5ec8668e5e2d8ULL, 0x91c979578d1037b1ULL, 0x0642ca05693b9f70ULL,
+ 0xefca80168350eb4fULL, 0x38d21b24f36a45ecULL, 0xbeab81e1af73d658ULL,
+ 0x8cbfd9cae7542f24ULL, 0xfd19cc0d81f11102ULL, 0x0ac6430fbb4dbc90ULL,
+ 0x1d76a09d6a441895ULL, 0x2a01573ff1cbbfa1ULL, 0xb572e161894fde2bULL,
+ 0x8124734fa853b827ULL, 0x614b1fdf43e6b1b0ULL, 0x68ac395c4238cc18ULL,
+ 0x21d837bfd7f7b7d2ULL, 0x20c714304a860331ULL, 0x5cfaab726324aa14ULL,
+ 0x74c5ba4eb50d606eULL, 0xf3a3030474654739ULL, 0x23e671bcf015c209ULL,
+ 0x45f087e947b9582aULL, 0xd8bd77b418df4c7bULL, 0xe06f6c90ebb50997ULL,
+ 0x0bd96080263c0873ULL, 0x7e03f9410e40dcfeULL, 0xb8e94be4c6484928ULL,
+ 0xfb5b0608e8ca8e72ULL, 0x1a2b49179e0e3306ULL, 0x4e29e76961855059ULL,
+ 0x4f36c4e6fcf4e4baULL, 0x49740ee395cf7bcaULL, 0xc2963ea386d17f7dULL,
+ 0x90d65ad810618352ULL, 0x12d34c1b02a1fa4dULL, 0xfa44258775bb3a91ULL,
+ 0x18150f14b9ec46ddULL, 0x1491861e6b9a653dULL, 0x9a1019d7ab2c3fc2ULL,
+ 0x3668d42d06fe13d7ULL, 0xdcc1fbb25606a6d0ULL, 0x969490dd795a1c22ULL,
+ 0x3549b1a1bc6dd2efULL, 0xc94f5e23a0ed770eULL, 0xb9f6686b5b39fdcbULL,
+ 0xc4d4f4a6efeae00dULL, 0xe732851a1fff2204ULL, 0x94aad6de5eb869f9ULL,
+ 0x3f8ff2ae07206e7fULL, 0xfe38a9813b62d03aULL, 0xa7a1ad7a8bee2466ULL,
+ 0x7b6056c8dde882b6ULL, 0x302a1e286fc58ca7ULL, 0x8da0fa457a259bc7ULL,
+ 0xb3302b64e074415bULL, 0x5402ae7eff8b635fULL, 0x08f8050c9cafc94bULL,
+ 0xae468bf98a3059ceULL, 0x88c355cca98dc58fULL, 0xb10e6d67c7963480ULL,
+ 0xbad70de7e1aa3cf3ULL, 0xbfb4a26e320262bbULL, 0xcb711820870f02d5ULL,
+ 0xce12b7a954a75c9dULL, 0x563ce87dd8691684ULL, 0x9f73b65e7884618aULL,
+ 0x2b1e74b06cba0b42ULL, 0x47cec1ea605b2df1ULL, 0x1c698312f735ac76ULL,
+ 0x5fdbcefed9b76b2cULL, 0x831a354c8fb1cdfcULL, 0x820516c312c0791fULL,
+ 0xb74ca762aeadabf0ULL, 0xfc06ef821c80a5e1ULL, 0x5723cbf24518a267ULL,
+ 0x9d4df05d5f661451ULL, 0x588627742dfd40bfULL, 0xda8331b73f3d39a0ULL,
+ 0x17b0e392d109a405ULL, 0xf965400bcf28fba9ULL, 0x7c3dbf4229a2a925ULL,
+ 0x023e460327e275dbULL, 0x6cd0b55a0ce126b3ULL, 0xe62da695828e96e7ULL,
+ 0x42ad6e63b3f373b9ULL, 0xe50cc319381d57dfULL, 0xc5cbd729729b54eeULL,
+ 0x46d1e265fd2a9912ULL, 0x6428b056904eeff8ULL, 0x8be23040131e04b7ULL,
+ 0x6709d5da2add2ec0ULL, 0x075de98af44a2b93ULL, 0x8447dcc67bfbe66fULL,
+ 0x6616f655b7ac9a23ULL, 0xd607b8bded4b1a40ULL, 0x0563af89d3a85e48ULL,
+ 0x3db1b4ad20c21ba4ULL, 0x11f22997b8323b75ULL, 0x292032b34b587e99ULL,
+ 0x7f1cdace9331681dULL, 0x8e819fc9c0b65affULL, 0xa1e3677fe2d5bb16ULL,
+ 0xcd33d225ee349da5ULL, 0xd9a2543b85aef898ULL, 0x795e10cbfa0af76dULL,
+ 0x25a4bbb9992e5d79ULL, 0x78413344677b438eULL, 0xf0826688cef68601ULL,
+ 0xd27b34bba392f0ebULL, 0x551d8df162fad7bcULL, 0x1e57c511d0d7d9adULL,
+ 0xdeffbdb171e4d30bULL, 0xf4feea8e802f6caaULL, 0xa480c8f6317de55eULL,
+ 0xa0fc44f07fa40ff5ULL, 0x95b5f551c3c9dd1aULL, 0x22f952336d6476eaULL,
+ 0x0000000000000000ULL, 0xa6be8ef5169f9085ULL, 0xcc2cf1aa73452946ULL,
+ 0x2e7ddb39bf12550aULL, 0xd526dd3157d8db78ULL, 0x486b2d6c08becf29ULL,
+ 0x9b0f3a58365d8b21ULL, 0xac78cdfaadd22c15ULL, 0xbc95c7e28891a383ULL,
+ 0x6a927f5f65dab9c3ULL, 0xc3891d2c1ba0cb9eULL, 0xeaa92f9f50f8b507ULL,
+ 0xcf0d9426c9d6e87eULL, 0xca6e3baf1a7eb636ULL, 0xab25247059980786ULL,
+ 0x69b31ad3df4978fbULL, 0xe2512a93cc577c4cULL, 0xff278a0ea61364d9ULL,
+ 0x71a615c766a53e26ULL, 0x89dc764334fc716cULL, 0xf87a638452594f4aULL,
+ 0xf2bc208be914f3daULL, 0x8766b94ac1682757ULL, 0xbbc82e687cdb8810ULL,
+ 0x626a7a53f9757088ULL, 0xa2c202f358467a2eULL, 0x4d0882e5db169161ULL,
+ 0x09e7268301de7da8ULL, 0xe897699c771ac0dcULL, 0xc8507dac3d9cc3edULL,
+ 0xc0a878a0a1330aa6ULL, 0x978bb352e42ba8c1ULL, 0xe9884a13ea6b743fULL,
+ 0x279afdbabecc28a2ULL, 0x047c8c064ed9eaabULL, 0x507e2278b15289f4ULL,
+ 0x599904fbb08cf45cULL, 0xbd8ae46d15e01760ULL, 0x31353da7f2b43844ULL,
+ 0x8558ff49e68a528cULL, 0x76fbfc4d92ef15b5ULL, 0x3456922e211c660cULL,
+ 0x86799ac55c1993b4ULL, 0x3e90d1219a51da9cULL, 0x2d5cbeb505819432ULL,
+ 0x982e5fd48cce4a19ULL, 0xdb9c1238a24c8d43ULL, 0xd439febecaa96f9bULL,
+ 0x418c0bef0960b281ULL, 0x158ea591f6ebd1deULL, 0x1f48e69e4da66d4eULL,
+ 0x8afd13cf8e6fb054ULL, 0xf5e1c9011d5ed849ULL, 0xe34e091c5126c8afULL,
+ 0xad67ee7530a398f6ULL, 0x43b24dec2e82c75aULL, 0x75da99c1287cd48dULL,
+ 0x92e81cdb3783f689ULL, 0xa3dd217cc537cecdULL, 0x60543c50de970553ULL,
+ 0x93f73f54aaf2426aULL, 0xa91b62737e7a725dULL, 0xf19d4507538732e2ULL,
+ 0x77e4dfc20f9ea156ULL, 0x7d229ccdb4d31dc6ULL, 0x1b346a98037f87e5ULL,
+ 0xedf4c615a4b29e94ULL, 0x4093286094110662ULL, 0xb0114ee85ae78063ULL,
+ 0x6ff1d0d6b672e78bULL, 0x6dcf96d591909250ULL, 0xdfe09e3eec9567e8ULL,
+ 0x3214582b4827f97cULL, 0xb46dc2ee143e6ac8ULL, 0xf6c0ac8da7cd1971ULL,
+ 0xebb60c10cd8901e4ULL, 0xf7df8f023abcad92ULL, 0x9c52d3d2c217a0b2ULL,
+ 0x6b8d5cd0f8ab0d20ULL, 0x3777f7a29b8fa734ULL, 0x011f238f9d71b4e3ULL,
+ 0xc1b75b2f3c42be45ULL, 0x5de588fdfe551ef7ULL, 0x6eeef3592b035368ULL,
+ 0xaa3a07ffc4e9b365ULL, 0xecebe59a39c32a77ULL, 0x5ba742f8976e8187ULL,
+ 0x4b4a48e0b22d0e11ULL, 0xddded83dcb771233ULL, 0xa59feb79ac0c51bdULL,
+ 0xc7f5912a55792135ULL
+ },
+ {
+ 0x6d6ae04668a9b08aULL, 0x3ab3f04b0be8c743ULL, 0xe51e166b54b3c908ULL,
+ 0xbe90a9eb35c2f139ULL, 0xb2c7066637f2bec1ULL, 0xaa6945613392202cULL,
+ 0x9a28c36f3b5201ebULL, 0xddce5a93ab536994ULL, 0x0e34133ef6382827ULL,
+ 0x52a02ba1ec55048bULL, 0xa2f88f97c4b2a177ULL, 0x8640e513ca2251a5ULL,
+ 0xcdf1d36258137622ULL, 0xfe6cb708dedf8ddbULL, 0x8a174a9ec8121e5dULL,
+ 0x679896036b81560eULL, 0x59ed033395795feeULL, 0x1dd778ab8b74edafULL,
+ 0xee533ef92d9f926dULL, 0x2a8c79baf8a8d8f5ULL, 0x6bcf398e69b119f6ULL,
+ 0xe20491742fafdd95ULL, 0x276488e0809c2aecULL, 0xea955b82d88f5cceULL,
+ 0x7102c63a99d9e0c4ULL, 0xf9763017a5c39946ULL, 0x429fa2501f151b3dULL,
+ 0x4659c72bea05d59eULL, 0x984b7fdccf5a6634ULL, 0xf742232953fbb161ULL,
+ 0x3041860e08c021c7ULL, 0x747bfd9616cd9386ULL, 0x4bb1367192312787ULL,
+ 0x1b72a1638a6c44d3ULL, 0x4a0e68a6e8359a66ULL, 0x169a5039f258b6caULL,
+ 0xb98a2ef44edee5a4ULL, 0xd9083fe85e43a737ULL, 0x967f6ce239624e13ULL,
+ 0x8874f62d3c1a7982ULL, 0x3c1629830af06e3fULL, 0x9165ebfd427e5a8eULL,
+ 0xb5dd81794ceeaa5cULL, 0x0de8f15a7834f219ULL, 0x70bd98ede3dd5d25ULL,
+ 0xaccc9ca9328a8950ULL, 0x56664eda1945ca28ULL, 0x221db34c0f8859aeULL,
+ 0x26dbd637fa98970dULL, 0x1acdffb4f068f932ULL, 0x4585254f64090fa0ULL,
+ 0x72de245e17d53afaULL, 0x1546b25d7c546cf4ULL, 0x207e0ffffb803e71ULL,
+ 0xfaaad2732bcf4378ULL, 0xb462dfae36ea17bdULL, 0xcf926fd1ac1b11fdULL,
+ 0xe0672dc7dba7ba4aULL, 0xd3fa49ad5d6b41b3ULL, 0x8ba81449b216a3bcULL,
+ 0x14f9ec8a0650d115ULL, 0x40fc1ee3eb1d7ce2ULL, 0x23a2ed9b758ce44fULL,
+ 0x782c521b14fddc7eULL, 0x1c68267cf170504eULL, 0xbcf31558c1ca96e6ULL,
+ 0xa781b43b4ba6d235ULL, 0xf6fd7dfe29ff0c80ULL, 0xb0a4bad5c3fad91eULL,
+ 0xd199f51ea963266cULL, 0x414340349119c103ULL, 0x5405f269ed4dadf7ULL,
+ 0xabd61bb649969dcdULL, 0x6813dbeae7bdc3c8ULL, 0x65fb2ab09f8931d1ULL,
+ 0xf1e7fae152e3181dULL, 0xc1a67cef5a2339daULL, 0x7a4feea8e0f5bba1ULL,
+ 0x1e0b9acf05783791ULL, 0x5b8ebf8061713831ULL, 0x80e53cdbcb3af8d9ULL,
+ 0x7e898bd315e57502ULL, 0xc6bcfbf0213f2d47ULL, 0x95a38e86b76e942dULL,
+ 0x092e94218d243cbaULL, 0x8339debf453622e7ULL, 0xb11be402b9fe64ffULL,
+ 0x57d9100d634177c9ULL, 0xcc4e8db52217cbc3ULL, 0x3b0cae9c71ec7aa2ULL,
+ 0xfb158ca451cbfe99ULL, 0x2b33276d82ac6514ULL, 0x01bf5ed77a04bde1ULL,
+ 0xc5601994af33f779ULL, 0x75c4a3416cc92e67ULL, 0xf3844652a6eb7fc2ULL,
+ 0x3487e375fdd0ef64ULL, 0x18ae430704609eedULL, 0x4d14efb993298efbULL,
+ 0x815a620cb13e4538ULL, 0x125c354207487869ULL, 0x9eeea614ce42cf48ULL,
+ 0xce2d3106d61fac1cULL, 0xbbe99247bad6827bULL, 0x071a871f7b1c149dULL,
+ 0x2e4a1cc10db81656ULL, 0x77a71ff298c149b8ULL, 0x06a5d9c80118a97cULL,
+ 0xad73c27e488e34b1ULL, 0x443a7b981e0db241ULL, 0xe3bbcfa355ab6074ULL,
+ 0x0af276450328e684ULL, 0x73617a896dd1871bULL, 0x58525de4ef7de20fULL,
+ 0xb7be3dcab8e6cd83ULL, 0x19111dd07e64230cULL, 0x842359a03e2a367aULL,
+ 0x103f89f1f3401fb6ULL, 0xdc710444d157d475ULL, 0xb835702334da5845ULL,
+ 0x4320fc876511a6dcULL, 0xd026abc9d3679b8dULL, 0x17250eee885c0b2bULL,
+ 0x90dab52a387ae76fULL, 0x31fed8d972c49c26ULL, 0x89cba8fa461ec463ULL,
+ 0x2ff5421677bcabb7ULL, 0x396f122f85e41d7dULL, 0xa09b332430bac6a8ULL,
+ 0xc888e8ced7070560ULL, 0xaeaf201ac682ee8fULL, 0x1180d7268944a257ULL,
+ 0xf058a43628e7a5fcULL, 0xbd4c4b8fbbce2b07ULL, 0xa1246df34abe7b49ULL,
+ 0x7d5569b79be9af3cULL, 0xa9b5a705bd9efa12ULL, 0xdb6b835baa4bc0e8ULL,
+ 0x05793bac8f147342ULL, 0x21c1512881848390ULL, 0xfdb0556c50d357e5ULL,
+ 0x613d4fcb6a99ff72ULL, 0x03dce2648e0cda3eULL, 0xe949b9e6568386f0ULL,
+ 0xfc0f0bbb2ad7ea04ULL, 0x6a70675913b5a417ULL, 0x7f36d5046fe1c8e3ULL,
+ 0x0c57af8d02304ff8ULL, 0x32223abdfcc84618ULL, 0x0891caf6f720815bULL,
+ 0xa63eeaec31a26fd4ULL, 0x2507345374944d33ULL, 0x49d28ac266394058ULL,
+ 0xf5219f9aa7f3d6beULL, 0x2d96fea583b4cc68ULL, 0x5a31e1571b7585d0ULL,
+ 0x8ed12fe53d02d0feULL, 0xdfade6205f5b0e4bULL, 0x4cabb16ee92d331aULL,
+ 0x04c6657bf510cea3ULL, 0xd73c2cd6a87b8f10ULL, 0xe1d87310a1a307abULL,
+ 0x6cd5be9112ad0d6bULL, 0x97c032354366f3f2ULL, 0xd4e0ceb22677552eULL,
+ 0x0000000000000000ULL, 0x29509bde76a402cbULL, 0xc27a9e8bd42fe3e4ULL,
+ 0x5ef7842cee654b73ULL, 0xaf107ecdbc86536eULL, 0x3fcacbe784fcb401ULL,
+ 0xd55f90655c73e8cfULL, 0xe6c2f40fdabf1336ULL, 0xe8f6e7312c873b11ULL,
+ 0xeb2a0555a28be12fULL, 0xe4a148bc2eb774e9ULL, 0x9b979db84156bc0aULL,
+ 0x6eb60222e6a56ab4ULL, 0x87ffbbc4b026ec44ULL, 0xc703a5275b3b90a6ULL,
+ 0x47e699fc9001687fULL, 0x9c8d1aa73a4aa897ULL, 0x7cea3760e1ed12ddULL,
+ 0x4ec80ddd1d2554c5ULL, 0x13e36b957d4cc588ULL, 0x5d2b66486069914dULL,
+ 0x92b90999cc7280b0ULL, 0x517cc9c56259deb5ULL, 0xc937b619ad03b881ULL,
+ 0xec30824ad997f5b2ULL, 0xa45d565fc5aa080bULL, 0xd6837201d27f32f1ULL,
+ 0x635ef3789e9198adULL, 0x531f75769651b96aULL, 0x4f77530a6721e924ULL,
+ 0x486dd4151c3dfdb9ULL, 0x5f48dafb9461f692ULL, 0x375b011173dc355aULL,
+ 0x3da9775470f4d3deULL, 0x8d0dcd81b30e0ac0ULL, 0x36e45fc609d888bbULL,
+ 0x55baacbe97491016ULL, 0x8cb29356c90ab721ULL, 0x76184125e2c5f459ULL,
+ 0x99f4210bb55edbd5ULL, 0x6f095cf59ca1d755ULL, 0x9f51f8c3b44672a9ULL,
+ 0x3538bda287d45285ULL, 0x50c39712185d6354ULL, 0xf23b1885dcefc223ULL,
+ 0x79930ccc6ef9619fULL, 0xed8fdc9da3934853ULL, 0xcb540aaa590bdf5eULL,
+ 0x5c94389f1a6d2cacULL, 0xe77daad8a0bbaed7ULL, 0x28efc5090ca0bf2aULL,
+ 0xbf2ff73c4fc64cd8ULL, 0xb37858b14df60320ULL, 0xf8c96ec0dfc724a7ULL,
+ 0x828680683f329f06ULL, 0x941cd051cd6a29ccULL, 0xc3c5c05cae2b5e05ULL,
+ 0xb601631dc2e27062ULL, 0xc01922382027843bULL, 0x24b86a840e90f0d2ULL,
+ 0xd245177a276ffc52ULL, 0x0f8b4de98c3c95c6ULL, 0x3e759530fef809e0ULL,
+ 0x0b4d2892792c5b65ULL, 0xc4df4743d5374a98ULL, 0xa5e20888bfaeb5eaULL,
+ 0xba56cc90c0d23f9aULL, 0x38d04cf8ffe0a09cULL, 0x62e1adafe495254cULL,
+ 0x0263bcb3f40867dfULL, 0xcaeb547d230f62bfULL, 0x6082111c109d4293ULL,
+ 0xdad4dd8cd04f7d09ULL, 0xefec602e579b2f8cULL, 0x1fb4c4187f7c8a70ULL,
+ 0xffd3e9dfa4db303aULL, 0x7bf0b07f9af10640ULL, 0xf49ec14dddf76b5fULL,
+ 0x8f6e713247066d1fULL, 0x339d646a86ccfbf9ULL, 0x64447467e58d8c30ULL,
+ 0x2c29a072f9b07189ULL, 0xd8b7613f24471ad6ULL, 0x6627c8d41185ebefULL,
+ 0xa347d140beb61c96ULL, 0xde12b8f7255fb3aaULL, 0x9d324470404e1576ULL,
+ 0x9306574eb6763d51ULL, 0xa80af9d2c79a47f3ULL, 0x859c0777442e8b9bULL,
+ 0x69ac853d9db97e29ULL
+ },
+ {
+ 0xc3407dfc2de6377eULL, 0x5b9e93eea4256f77ULL, 0xadb58fdd50c845e0ULL,
+ 0x5219ff11a75bed86ULL, 0x356b61cfd90b1de9ULL, 0xfb8f406e25abe037ULL,
+ 0x7a5a0231c0f60796ULL, 0x9d3cd216e1f5020bULL, 0x0c6550fb6b48d8f3ULL,
+ 0xf57508c427ff1c62ULL, 0x4ad35ffa71cb407dULL, 0x6290a2da1666aa6dULL,
+ 0xe284ec2349355f9fULL, 0xb3c307c53d7c84ecULL, 0x05e23c0468365a02ULL,
+ 0x190bac4d6c9ebfa8ULL, 0x94bbbee9e28b80faULL, 0xa34fc777529cb9b5ULL,
+ 0xcc7b39f095bcd978ULL, 0x2426addb0ce532e3ULL, 0x7e79329312ce4fc7ULL,
+ 0xab09a72eebec2917ULL, 0xf8d15499f6b9d6c2ULL, 0x1a55b8babf8c895dULL,
+ 0xdb8add17fb769a85ULL, 0xb57f2f368658e81bULL, 0x8acd36f18f3f41f6ULL,
+ 0x5ce3b7bba50f11d3ULL, 0x114dcc14d5ee2f0aULL, 0xb91a7fcded1030e8ULL,
+ 0x81d5425fe55de7a1ULL, 0xb6213bc1554adeeeULL, 0x80144ef95f53f5f2ULL,
+ 0x1e7688186db4c10cULL, 0x3b912965db5fe1bcULL, 0xc281715a97e8252dULL,
+ 0x54a5d7e21c7f8171ULL, 0x4b12535ccbc5522eULL, 0x1d289cefbea6f7f9ULL,
+ 0x6ef5f2217d2e729eULL, 0xe6a7dc819b0d17ceULL, 0x1b94b41c05829b0eULL,
+ 0x33d7493c622f711eULL, 0xdcf7f942fa5ce421ULL, 0x600fba8b7f7a8ecbULL,
+ 0x46b60f011a83988eULL, 0x235b898e0dcf4c47ULL, 0x957ab24f588592a9ULL,
+ 0x4354330572b5c28cULL, 0xa5f3ef84e9b8d542ULL, 0x8c711e02341b2d01ULL,
+ 0x0b1874ae6a62a657ULL, 0x1213d8e306fc19ffULL, 0xfe6d7c6a4d9dba35ULL,
+ 0x65ed868f174cd4c9ULL, 0x88522ea0e6236550ULL, 0x899322065c2d7703ULL,
+ 0xc01e690bfef4018bULL, 0x915982ed8abddaf8ULL, 0xbe675b98ec3a4e4cULL,
+ 0xa996bf7f82f00db1ULL, 0xe1daf8d49a27696aULL, 0x2effd5d3dc8986e7ULL,
+ 0xd153a51f2b1a2e81ULL, 0x18caa0ebd690adfbULL, 0x390e3134b243c51aULL,
+ 0x2778b92cdff70416ULL, 0x029f1851691c24a6ULL, 0x5e7cafeacc133575ULL,
+ 0xfa4e4cc89fa5f264ULL, 0x5a5f9f481e2b7d24ULL, 0x484c47ab18d764dbULL,
+ 0x400a27f2a1a7f479ULL, 0xaeeb9b2a83da7315ULL, 0x721c626879869734ULL,
+ 0x042330a2d2384851ULL, 0x85f672fd3765aff0ULL, 0xba446b3a3e02061dULL,
+ 0x73dd6ecec3888567ULL, 0xffac70ccf793a866ULL, 0xdfa9edb5294ed2d4ULL,
+ 0x6c6aea7014325638ULL, 0x834a5a0e8c41c307ULL, 0xcdba35562fb2cb2bULL,
+ 0x0ad97808d06cb404ULL, 0x0f3b440cb85aee06ULL, 0xe5f9c876481f213bULL,
+ 0x98deee1289c35809ULL, 0x59018bbfcd394bd1ULL, 0xe01bf47220297b39ULL,
+ 0xde68e1139340c087ULL, 0x9fa3ca4788e926adULL, 0xbb85679c840c144eULL,
+ 0x53d8f3b71d55ffd5ULL, 0x0da45c5dd146caa0ULL, 0x6f34fe87c72060cdULL,
+ 0x57fbc315cf6db784ULL, 0xcee421a1fca0fddeULL, 0x3d2d0196607b8d4bULL,
+ 0x642c8a29ad42c69aULL, 0x14aff010bdd87508ULL, 0xac74837beac657b3ULL,
+ 0x3216459ad821634dULL, 0x3fb219c70967a9edULL, 0x06bc28f3bb246cf7ULL,
+ 0xf2082c9126d562c6ULL, 0x66b39278c45ee23cULL, 0xbd394f6f3f2878b9ULL,
+ 0xfd33689d9e8f8cc0ULL, 0x37f4799eb017394fULL, 0x108cc0b26fe03d59ULL,
+ 0xda4bd1b1417888d6ULL, 0xb09d1332ee6eb219ULL, 0x2f3ed975668794b4ULL,
+ 0x58c0871977375982ULL, 0x7561463d78ace990ULL, 0x09876cff037e82f1ULL,
+ 0x7fb83e35a8c05d94ULL, 0x26b9b58a65f91645ULL, 0xef20b07e9873953fULL,
+ 0x3148516d0b3355b8ULL, 0x41cb2b541ba9e62aULL, 0x790416c613e43163ULL,
+ 0xa011d380818e8f40ULL, 0x3a5025c36151f3efULL, 0xd57095bdf92266d0ULL,
+ 0x498d4b0da2d97688ULL, 0x8b0c3a57353153a5ULL, 0x21c491df64d368e1ULL,
+ 0x8f2f0af5e7091bf4ULL, 0x2da1c1240f9bb012ULL, 0xc43d59a92ccc49daULL,
+ 0xbfa6573e56345c1fULL, 0x828b56a8364fd154ULL, 0x9a41f643e0df7cafULL,
+ 0xbcf843c985266aeaULL, 0x2b1de9d7b4bfdce5ULL, 0x20059d79dedd7ab2ULL,
+ 0x6dabe6d6ae3c446bULL, 0x45e81bf6c991ae7bULL, 0x6351ae7cac68b83eULL,
+ 0xa432e32253b6c711ULL, 0xd092a9b991143cd2ULL, 0xcac711032e98b58fULL,
+ 0xd8d4c9e02864ac70ULL, 0xc5fc550f96c25b89ULL, 0xd7ef8dec903e4276ULL,
+ 0x67729ede7e50f06fULL, 0xeac28c7af045cf3dULL, 0xb15c1f945460a04aULL,
+ 0x9cfddeb05bfb1058ULL, 0x93c69abce3a1fe5eULL, 0xeb0380dc4a4bdd6eULL,
+ 0xd20db1e8f8081874ULL, 0x229a8528b7c15e14ULL, 0x44291750739fbc28ULL,
+ 0xd3ccbd4e42060a27ULL, 0xf62b1c33f4ed2a97ULL, 0x86a8660ae4779905ULL,
+ 0xd62e814a2a305025ULL, 0x477703a7a08d8addULL, 0x7b9b0e977af815c5ULL,
+ 0x78c51a60a9ea2330ULL, 0xa6adfb733aaae3b7ULL, 0x97e5aa1e3199b60fULL,
+ 0x0000000000000000ULL, 0xf4b404629df10e31ULL, 0x5564db44a6719322ULL,
+ 0x9207961a59afec0dULL, 0x9624a6b88b97a45cULL, 0x363575380a192b1cULL,
+ 0x2c60cd82b595a241ULL, 0x7d272664c1dc7932ULL, 0x7142769faa94a1c1ULL,
+ 0xa1d0df263b809d13ULL, 0x1630e841d4c451aeULL, 0xc1df65ad44fa13d8ULL,
+ 0x13d2d445bcf20bacULL, 0xd915c546926abe23ULL, 0x38cf3d92084dd749ULL,
+ 0xe766d0272103059dULL, 0xc7634d5effde7f2fULL, 0x077d2455012a7ea4ULL,
+ 0xedbfa82ff16fb199ULL, 0xaf2a978c39d46146ULL, 0x42953fa3c8bbd0dfULL,
+ 0xcb061da59496a7dcULL, 0x25e7a17db6eb20b0ULL, 0x34aa6d6963050fbaULL,
+ 0xa76cf7d580a4f1e4ULL, 0xf7ea10954ee338c4ULL, 0xfcf2643b24819e93ULL,
+ 0xcf252d0746aeef8dULL, 0x4ef06f58a3f3082cULL, 0x563acfb37563a5d7ULL,
+ 0x5086e740ce47c920ULL, 0x2982f186dda3f843ULL, 0x87696aac5e798b56ULL,
+ 0x5d22bb1d1f010380ULL, 0x035e14f7d31236f5ULL, 0x3cec0d30da759f18ULL,
+ 0xf3c920379cdb7095ULL, 0xb8db736b571e22bbULL, 0xdd36f5e44052f672ULL,
+ 0xaac8ab8851e23b44ULL, 0xa857b3d938fe1fe2ULL, 0x17f1e4e76eca43fdULL,
+ 0xec7ea4894b61a3caULL, 0x9e62c6e132e734feULL, 0xd4b1991b432c7483ULL,
+ 0x6ad6c283af163acfULL, 0x1ce9904904a8e5aaULL, 0x5fbda34c761d2726ULL,
+ 0xf910583f4cb7c491ULL, 0xc6a241f845d06d7cULL, 0x4f3163fe19fd1a7fULL,
+ 0xe99c988d2357f9c8ULL, 0x8eee06535d0709a7ULL, 0x0efa48aa0254fc55ULL,
+ 0xb4be23903c56fa48ULL, 0x763f52caabbedf65ULL, 0xeee1bcd8227d876cULL,
+ 0xe345e085f33b4dccULL, 0x3e731561b369bbbeULL, 0x2843fd2067adea10ULL,
+ 0x2adce5710eb1ceb6ULL, 0xb7e03767ef44ccbdULL, 0x8db012a48e153f52ULL,
+ 0x61ceb62dc5749c98ULL, 0xe85d942b9959eb9bULL, 0x4c6f7709caef2c8aULL,
+ 0x84377e5b8d6bbda3ULL, 0x30895dcbb13d47ebULL, 0x74a04a9bc2a2fbc3ULL,
+ 0x6b17ce251518289cULL, 0xe438c4d0f2113368ULL, 0x1fb784bed7bad35fULL,
+ 0x9b80fae55ad16efcULL, 0x77fe5e6c11b0cd36ULL, 0xc858095247849129ULL,
+ 0x08466059b97090a2ULL, 0x01c10ca6ba0e1253ULL, 0x6988d6747c040c3aULL,
+ 0x6849dad2c60a1e69ULL, 0x5147ebe67449db73ULL, 0xc99905f4fd8a837aULL,
+ 0x991fe2b433cd4a5aULL, 0xf09734c04fc94660ULL, 0xa28ecbd1e892abe6ULL,
+ 0xf1563866f5c75433ULL, 0x4dae7baf70e13ed9ULL, 0x7ce62ac27bd26b61ULL,
+ 0x70837a39109ab392ULL, 0x90988e4b30b3c8abULL, 0xb2020b63877296bfULL,
+ 0x156efcb607d6675bULL
+ },
+ {
+ 0xe63f55ce97c331d0ULL, 0x25b506b0015bba16ULL, 0xc8706e29e6ad9ba8ULL,
+ 0x5b43d3775d521f6aULL, 0x0bfa3d577035106eULL, 0xab95fc172afb0e66ULL,
+ 0xf64b63979e7a3276ULL, 0xf58b4562649dad4bULL, 0x48f7c3dbae0c83f1ULL,
+ 0xff31916642f5c8c5ULL, 0xcbb048dc1c4a0495ULL, 0x66b8f83cdf622989ULL,
+ 0x35c130e908e2b9b0ULL, 0x7c761a61f0b34fa1ULL, 0x3601161cf205268dULL,
+ 0x9e54ccfe2219b7d6ULL, 0x8b7d90a538940837ULL, 0x9cd403588ea35d0bULL,
+ 0xbc3c6fea9ccc5b5aULL, 0xe5ff733b6d24aeedULL, 0xceed22de0f7eb8d2ULL,
+ 0xec8581cab1ab545eULL, 0xb96105e88ff8e71dULL, 0x8ca03501871a5eadULL,
+ 0x76ccce65d6db2a2fULL, 0x5883f582a7b58057ULL, 0x3f7be4ed2e8adc3eULL,
+ 0x0fe7be06355cd9c9ULL, 0xee054e6c1d11be83ULL, 0x1074365909b903a6ULL,
+ 0x5dde9f80b4813c10ULL, 0x4a770c7d02b6692cULL, 0x5379c8d5d7809039ULL,
+ 0xb4067448161ed409ULL, 0x5f5e5026183bd6cdULL, 0xe898029bf4c29df9ULL,
+ 0x7fb63c940a54d09cULL, 0xc5171f897f4ba8bcULL, 0xa6f28db7b31d3d72ULL,
+ 0x2e4f3be7716eaa78ULL, 0x0d6771a099e63314ULL, 0x82076254e41bf284ULL,
+ 0x2f0fd2b42733df98ULL, 0x5c9e76d3e2dc49f0ULL, 0x7aeb569619606cdbULL,
+ 0x83478b07b2468764ULL, 0xcfadcb8d5923cd32ULL, 0x85dac7f05b95a41eULL,
+ 0xb5469d1b4043a1e9ULL, 0xb821ecbbd9a592fdULL, 0x1b8e0b0e798c13c8ULL,
+ 0x62a57b6d9a0be02eULL, 0xfcf1b793b81257f8ULL, 0x9d94ea0bd8fe28ebULL,
+ 0x4cea408aeb654a56ULL, 0x23284a47e888996cULL, 0x2d8f1d128b893545ULL,
+ 0xf4cbac3132c0d8abULL, 0xbd7c86b9ca912ebaULL, 0x3a268eef3dbe6079ULL,
+ 0xf0d62f6077a9110cULL, 0x2735c916ade150cbULL, 0x89fd5f03942ee2eaULL,
+ 0x1acee25d2fd16628ULL, 0x90f39bab41181bffULL, 0x430dfe8cde39939fULL,
+ 0xf70b8ac4c8274796ULL, 0x1c53aeaac6024552ULL, 0x13b410acf35e9c9bULL,
+ 0xa532ab4249faa24fULL, 0x2b1251e5625a163fULL, 0xd7e3e676da4841c7ULL,
+ 0xa7b264e4e5404892ULL, 0xda8497d643ae72d3ULL, 0x861ae105a1723b23ULL,
+ 0x38a6414991048aa4ULL, 0x6578dec92585b6b4ULL, 0x0280cfa6acbaeaddULL,
+ 0x88bdb650c273970aULL, 0x9333bd5ebbff84c2ULL, 0x4e6a8f2c47dfa08bULL,
+ 0x321c954db76cef2aULL, 0x418d312a72837942ULL, 0xb29b38bfffcdf773ULL,
+ 0x6c022c38f90a4c07ULL, 0x5a033a240b0f6a8aULL, 0x1f93885f3ce5da6fULL,
+ 0xc38a537e96988bc6ULL, 0x39e6a81ac759ff44ULL, 0x29929e43cee0fce2ULL,
+ 0x40cdd87924de0ca2ULL, 0xe9d8ebc8a29fe819ULL, 0x0c2798f3cfbb46f4ULL,
+ 0x55e484223e53b343ULL, 0x4650948ecd0d2fd8ULL, 0x20e86cb2126f0651ULL,
+ 0x6d42c56baf5739e7ULL, 0xa06fc1405ace1e08ULL, 0x7babbfc54f3d193bULL,
+ 0x424d17df8864e67fULL, 0xd8045870ef14980eULL, 0xc6d7397c85ac3781ULL,
+ 0x21a885e1443273b1ULL, 0x67f8116f893f5c69ULL, 0x24f5efe35706cff6ULL,
+ 0xd56329d076f2ab1aULL, 0x5e1eb9754e66a32dULL, 0x28d2771098bd8902ULL,
+ 0x8f6013f47dfdc190ULL, 0x17a993fdb637553cULL, 0xe0a219397e1012aaULL,
+ 0x786b9930b5da8606ULL, 0x6e82e39e55b0a6daULL, 0x875a0856f72f4ec3ULL,
+ 0x3741ff4fa458536dULL, 0xac4859b3957558fcULL, 0x7ef6d5c75c09a57cULL,
+ 0xc04a758b6c7f14fbULL, 0xf9acdd91ab26ebbfULL, 0x7391a467c5ef9668ULL,
+ 0x335c7c1ee1319acaULL, 0xa91533b18641e4bbULL, 0xe4bf9a683b79db0dULL,
+ 0x8e20faa72ba0b470ULL, 0x51f907737b3a7ae4ULL, 0x2268a314bed5ec8cULL,
+ 0xd944b123b949edeeULL, 0x31dcb3b84d8b7017ULL, 0xd3fe65279f218860ULL,
+ 0x097af2f1dc8ffab3ULL, 0x9b09a6fc312d0b91ULL, 0xcc6ded78a3c4520fULL,
+ 0x3481d9ba5ebfcc50ULL, 0x4f2a667f1182d56bULL, 0xdfd9fdd4509ace94ULL,
+ 0x26752045fbbc252bULL, 0xbffc491f662bc467ULL, 0xdd593272fc202449ULL,
+ 0x3cbbc218d46d4303ULL, 0x91b372f817456e1fULL, 0x681faf69bc6385a0ULL,
+ 0xb686bbeebaa43ed4ULL, 0x1469b5084cd0ca01ULL, 0x98c98009cbca94acULL,
+ 0x6438379a73d8c354ULL, 0xc2caba2dc0c5fe26ULL, 0x3e3b0dbe78d7a9deULL,
+ 0x50b9ee202d670f04ULL, 0x4590b27b37eab0e5ULL, 0x6025b4cb36b10af3ULL,
+ 0xfb2c1237079c0162ULL, 0xa12f28130c936be8ULL, 0x4b37e52e54eb1cccULL,
+ 0x083a1ba28ad28f53ULL, 0xc10a9cd83a22611bULL, 0x9f1425ad7444c236ULL,
+ 0x069d4cf7e9d3237aULL, 0xedc56899e7f621beULL, 0x778c273680865fcfULL,
+ 0x309c5aeb1bd605f7ULL, 0x8de0dc52d1472b4dULL, 0xf8ec34c2fd7b9e5fULL,
+ 0xea18cd3d58787724ULL, 0xaad515447ca67b86ULL, 0x9989695a9d97e14cULL,
+ 0x0000000000000000ULL, 0xf196c63321f464ecULL, 0x71116bc169557cb5ULL,
+ 0xaf887f466f92c7c1ULL, 0x972e3e0ffe964d65ULL, 0x190ec4a8d536f915ULL,
+ 0x95aef1a9522ca7b8ULL, 0xdc19db21aa7d51a9ULL, 0x94ee18fa0471d258ULL,
+ 0x8087adf248a11859ULL, 0xc457f6da2916dd5cULL, 0xfa6cfb6451c17482ULL,
+ 0xf256e0c6db13fbd1ULL, 0x6a9f60cf10d96f7dULL, 0x4daaa9d9bd383fb6ULL,
+ 0x03c026f5fae79f3dULL, 0xde99148706c7bb74ULL, 0x2a52b8b6340763dfULL,
+ 0x6fc20acd03edd33aULL, 0xd423c08320afdefaULL, 0xbbe1ca4e23420dc0ULL,
+ 0x966ed75ca8cb3885ULL, 0xeb58246e0e2502c4ULL, 0x055d6a021334bc47ULL,
+ 0xa47242111fa7d7afULL, 0xe3623fcc84f78d97ULL, 0x81c744a11efc6db9ULL,
+ 0xaec8961539cfb221ULL, 0xf31609958d4e8e31ULL, 0x63e5923ecc5695ceULL,
+ 0x47107ddd9b505a38ULL, 0xa3afe7b5a0298135ULL, 0x792b7063e387f3e6ULL,
+ 0x0140e953565d75e0ULL, 0x12f4f9ffa503e97bULL, 0x750ce8902c3cb512ULL,
+ 0xdbc47e8515f30733ULL, 0x1ed3610c6ab8af8fULL, 0x5239218681dde5d9ULL,
+ 0xe222d69fd2aaf877ULL, 0xfe71783514a8bd25ULL, 0xcaf0a18f4a177175ULL,
+ 0x61655d9860ec7f13ULL, 0xe77fbc9dc19e4430ULL, 0x2ccff441ddd440a5ULL,
+ 0x16e97aaee06a20dcULL, 0xa855dae2d01c915bULL, 0x1d1347f9905f30b2ULL,
+ 0xb7c652bdecf94b34ULL, 0xd03e43d265c6175dULL, 0xfdb15ec0ee4f2218ULL,
+ 0x57644b8492e9599eULL, 0x07dda5a4bf8e569aULL, 0x54a46d71680ec6a3ULL,
+ 0x5624a2d7c4b42c7eULL, 0xbebca04c3076b187ULL, 0x7d36f332a6ee3a41ULL,
+ 0x3b6667bc6be31599ULL, 0x695f463aea3ef040ULL, 0xad08b0e0c3282d1cULL,
+ 0xb15b1e4a052a684eULL, 0x44d05b2861b7c505ULL, 0x15295c5b1a8dbfe1ULL,
+ 0x744c01c37a61c0f2ULL, 0x59c31cd1f1e8f5b7ULL, 0xef45a73f4b4ccb63ULL,
+ 0x6bdf899c46841a9dULL, 0x3dfb2b4b823036e3ULL, 0xa2ef0ee6f674f4d5ULL,
+ 0x184e2dfb836b8cf5ULL, 0x1134df0a5fe47646ULL, 0xbaa1231d751f7820ULL,
+ 0xd17eaa81339b62bdULL, 0xb01bf71953771daeULL, 0x849a2ea30dc8d1feULL,
+ 0x705182923f080955ULL, 0x0ea757556301ac29ULL, 0x041d83514569c9a7ULL,
+ 0x0abad4042668658eULL, 0x49b72a88f851f611ULL, 0x8a3d79f66ec97dd7ULL,
+ 0xcd2d042bf59927efULL, 0xc930877ab0f0ee48ULL, 0x9273540deda2f122ULL,
+ 0xc797d02fd3f14261ULL, 0xe1e2f06a284d674aULL, 0xd2be8c74c97cfd80ULL,
+ 0x9a494faf67707e71ULL, 0xb3dbd1eca9908293ULL, 0x72d14d3493b2e388ULL,
+ 0xd6a30f258c153427ULL
+ }
+ };
+
+extern const uint64_t STREEBOG_C[12][8] =
+ {
+ {
+ 0xdd806559f2a64507ULL,
+ 0x05767436cc744d23ULL,
+ 0xa2422a08a460d315ULL,
+ 0x4b7ce09192676901ULL,
+ 0x714eb88d7585c4fcULL,
+ 0x2f6a76432e45d016ULL,
+ 0xebcb2f81c0657c1fULL,
+ 0xb1085bda1ecadae9ULL
+ },
+ {
+ 0xe679047021b19bb7ULL,
+ 0x55dda21bd7cbcd56ULL,
+ 0x5cb561c2db0aa7caULL,
+ 0x9ab5176b12d69958ULL,
+ 0x61d55e0f16b50131ULL,
+ 0xf3feea720a232b98ULL,
+ 0x4fe39d460f70b5d7ULL,
+ 0x6fa3b58aa99d2f1aULL
+ },
+ {
+ 0x991e96f50aba0ab2ULL,
+ 0xc2b6f443867adb31ULL,
+ 0xc1c93a376062db09ULL,
+ 0xd3e20fe490359eb1ULL,
+ 0xf2ea7514b1297b7bULL,
+ 0x06f15e5f529c1f8bULL,
+ 0x0a39fc286a3d8435ULL,
+ 0xf574dcac2bce2fc7ULL
+ },
+ {
+ 0x220cbebc84e3d12eULL,
+ 0x3453eaa193e837f1ULL,
+ 0xd8b71333935203beULL,
+ 0xa9d72c82ed03d675ULL,
+ 0x9d721cad685e353fULL,
+ 0x488e857e335c3c7dULL,
+ 0xf948e1a05d71e4ddULL,
+ 0xef1fdfb3e81566d2ULL
+ },
+ {
+ 0x601758fd7c6cfe57ULL,
+ 0x7a56a27ea9ea63f5ULL,
+ 0xdfff00b723271a16ULL,
+ 0xbfcd1747253af5a3ULL,
+ 0x359e35d7800fffbdULL,
+ 0x7f151c1f1686104aULL,
+ 0x9a3f410c6ca92363ULL,
+ 0x4bea6bacad474799ULL
+ },
+ {
+ 0xfa68407a46647d6eULL,
+ 0xbf71c57236904f35ULL,
+ 0x0af21f66c2bec6b6ULL,
+ 0xcffaa6b71c9ab7b4ULL,
+ 0x187f9ab49af08ec6ULL,
+ 0x2d66c4f95142a46cULL,
+ 0x6fa4c33b7a3039c0ULL,
+ 0xae4faeae1d3ad3d9ULL
+ },
+ {
+ 0x8886564d3a14d493ULL,
+ 0x3517454ca23c4af3ULL,
+ 0x06476983284a0504ULL,
+ 0x0992abc52d822c37ULL,
+ 0xd3473e33197a93c9ULL,
+ 0x399ec6c7e6bf87c9ULL,
+ 0x51ac86febf240954ULL,
+ 0xf4c70e16eeaac5ecULL
+ },
+ {
+ 0xa47f0dd4bf02e71eULL,
+ 0x36acc2355951a8d9ULL,
+ 0x69d18d2bd1a5c42fULL,
+ 0xf4892bcb929b0690ULL,
+ 0x89b4443b4ddbc49aULL,
+ 0x4eb7f8719c36de1eULL,
+ 0x03e7aa020c6e4141ULL,
+ 0x9b1f5b424d93c9a7ULL
+ },
+ {
+ 0x7261445183235adbULL,
+ 0x0e38dc92cb1f2a60ULL,
+ 0x7b2b8a9aa6079c54ULL,
+ 0x800a440bdbb2ceb1ULL,
+ 0x3cd955b7e00d0984ULL,
+ 0x3a7d3a1b25894224ULL,
+ 0x944c9ad8ec165fdeULL,
+ 0x378f5a541631229bULL
+ },
+ {
+ 0x74b4c7fb98459cedULL,
+ 0x3698fad1153bb6c3ULL,
+ 0x7a1e6c303b7652f4ULL,
+ 0x9fe76702af69334bULL,
+ 0x1fffe18a1b336103ULL,
+ 0x8941e71cff8a78dbULL,
+ 0x382ae548b2e4f3f3ULL,
+ 0xabbedea680056f52ULL
+ },
+ {
+ 0x6bcaa4cd81f32d1bULL,
+ 0xdea2594ac06fd85dULL,
+ 0xefbacd1d7d476e98ULL,
+ 0x8a1d71efea48b9caULL,
+ 0x2001802114846679ULL,
+ 0xd8fa6bbbebab0761ULL,
+ 0x3002c6cd635afe94ULL,
+ 0x7bcd9ed0efc889fbULL
+ },
+ {
+ 0x48bc924af11bd720ULL,
+ 0xfaf417d5d9b21b99ULL,
+ 0xe71da4aa88e12852ULL,
+ 0x5d80ef9d1891cc86ULL,
+ 0xf82012d430219f9bULL,
+ 0xcda43c32bcdf1d77ULL,
+ 0xd21380b00449b17aULL,
+ 0x378ee767f11631baULL
+ }
+ };
+
+}
diff --git a/comm/third_party/botan/src/lib/hash/tiger/info.txt b/comm/third_party/botan/src/lib/hash/tiger/info.txt
new file mode 100644
index 0000000000..a244b2f0b2
--- /dev/null
+++ b/comm/third_party/botan/src/lib/hash/tiger/info.txt
@@ -0,0 +1,7 @@
+<defines>
+TIGER -> 20131128
+</defines>
+
+<requires>
+mdx_hash
+</requires>
diff --git a/comm/third_party/botan/src/lib/hash/tiger/tig_tab.cpp b/comm/third_party/botan/src/lib/hash/tiger/tig_tab.cpp
new file mode 100644
index 0000000000..3d1dc1eeb1
--- /dev/null
+++ b/comm/third_party/botan/src/lib/hash/tiger/tig_tab.cpp
@@ -0,0 +1,364 @@
+/*
+* S-Box Tables for Tiger
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/tiger.h>
+
+namespace Botan {
+
+const uint64_t Tiger::SBOX1[256] = {
+ 0x02AAB17CF7E90C5E, 0xAC424B03E243A8EC, 0x72CD5BE30DD5FCD3,
+ 0x6D019B93F6F97F3A, 0xCD9978FFD21F9193, 0x7573A1C9708029E2,
+ 0xB164326B922A83C3, 0x46883EEE04915870, 0xEAACE3057103ECE6,
+ 0xC54169B808A3535C, 0x4CE754918DDEC47C, 0x0AA2F4DFDC0DF40C,
+ 0x10B76F18A74DBEFA, 0xC6CCB6235AD1AB6A, 0x13726121572FE2FF,
+ 0x1A488C6F199D921E, 0x4BC9F9F4DA0007CA, 0x26F5E6F6E85241C7,
+ 0x859079DBEA5947B6, 0x4F1885C5C99E8C92, 0xD78E761EA96F864B,
+ 0x8E36428C52B5C17D, 0x69CF6827373063C1, 0xB607C93D9BB4C56E,
+ 0x7D820E760E76B5EA, 0x645C9CC6F07FDC42, 0xBF38A078243342E0,
+ 0x5F6B343C9D2E7D04, 0xF2C28AEB600B0EC6, 0x6C0ED85F7254BCAC,
+ 0x71592281A4DB4FE5, 0x1967FA69CE0FED9F, 0xFD5293F8B96545DB,
+ 0xC879E9D7F2A7600B, 0x860248920193194E, 0xA4F9533B2D9CC0B3,
+ 0x9053836C15957613, 0xDB6DCF8AFC357BF1, 0x18BEEA7A7A370F57,
+ 0x037117CA50B99066, 0x6AB30A9774424A35, 0xF4E92F02E325249B,
+ 0x7739DB07061CCAE1, 0xD8F3B49CECA42A05, 0xBD56BE3F51382F73,
+ 0x45FAED5843B0BB28, 0x1C813D5C11BF1F83, 0x8AF0E4B6D75FA169,
+ 0x33EE18A487AD9999, 0x3C26E8EAB1C94410, 0xB510102BC0A822F9,
+ 0x141EEF310CE6123B, 0xFC65B90059DDB154, 0xE0158640C5E0E607,
+ 0x884E079826C3A3CF, 0x930D0D9523C535FD, 0x35638D754E9A2B00,
+ 0x4085FCCF40469DD5, 0xC4B17AD28BE23A4C, 0xCAB2F0FC6A3E6A2E,
+ 0x2860971A6B943FCD, 0x3DDE6EE212E30446, 0x6222F32AE01765AE,
+ 0x5D550BB5478308FE, 0xA9EFA98DA0EDA22A, 0xC351A71686C40DA7,
+ 0x1105586D9C867C84, 0xDCFFEE85FDA22853, 0xCCFBD0262C5EEF76,
+ 0xBAF294CB8990D201, 0xE69464F52AFAD975, 0x94B013AFDF133E14,
+ 0x06A7D1A32823C958, 0x6F95FE5130F61119, 0xD92AB34E462C06C0,
+ 0xED7BDE33887C71D2, 0x79746D6E6518393E, 0x5BA419385D713329,
+ 0x7C1BA6B948A97564, 0x31987C197BFDAC67, 0xDE6C23C44B053D02,
+ 0x581C49FED002D64D, 0xDD474D6338261571, 0xAA4546C3E473D062,
+ 0x928FCE349455F860, 0x48161BBACAAB94D9, 0x63912430770E6F68,
+ 0x6EC8A5E602C6641C, 0x87282515337DDD2B, 0x2CDA6B42034B701B,
+ 0xB03D37C181CB096D, 0xE108438266C71C6F, 0x2B3180C7EB51B255,
+ 0xDF92B82F96C08BBC, 0x5C68C8C0A632F3BA, 0x5504CC861C3D0556,
+ 0xABBFA4E55FB26B8F, 0x41848B0AB3BACEB4, 0xB334A273AA445D32,
+ 0xBCA696F0A85AD881, 0x24F6EC65B528D56C, 0x0CE1512E90F4524A,
+ 0x4E9DD79D5506D35A, 0x258905FAC6CE9779, 0x2019295B3E109B33,
+ 0xF8A9478B73A054CC, 0x2924F2F934417EB0, 0x3993357D536D1BC4,
+ 0x38A81AC21DB6FF8B, 0x47C4FBF17D6016BF, 0x1E0FAADD7667E3F5,
+ 0x7ABCFF62938BEB96, 0xA78DAD948FC179C9, 0x8F1F98B72911E50D,
+ 0x61E48EAE27121A91, 0x4D62F7AD31859808, 0xECEBA345EF5CEAEB,
+ 0xF5CEB25EBC9684CE, 0xF633E20CB7F76221, 0xA32CDF06AB8293E4,
+ 0x985A202CA5EE2CA4, 0xCF0B8447CC8A8FB1, 0x9F765244979859A3,
+ 0xA8D516B1A1240017, 0x0BD7BA3EBB5DC726, 0xE54BCA55B86ADB39,
+ 0x1D7A3AFD6C478063, 0x519EC608E7669EDD, 0x0E5715A2D149AA23,
+ 0x177D4571848FF194, 0xEEB55F3241014C22, 0x0F5E5CA13A6E2EC2,
+ 0x8029927B75F5C361, 0xAD139FABC3D6E436, 0x0D5DF1A94CCF402F,
+ 0x3E8BD948BEA5DFC8, 0xA5A0D357BD3FF77E, 0xA2D12E251F74F645,
+ 0x66FD9E525E81A082, 0x2E0C90CE7F687A49, 0xC2E8BCBEBA973BC5,
+ 0x000001BCE509745F, 0x423777BBE6DAB3D6, 0xD1661C7EAEF06EB5,
+ 0xA1781F354DAACFD8, 0x2D11284A2B16AFFC, 0xF1FC4F67FA891D1F,
+ 0x73ECC25DCB920ADA, 0xAE610C22C2A12651, 0x96E0A810D356B78A,
+ 0x5A9A381F2FE7870F, 0xD5AD62EDE94E5530, 0xD225E5E8368D1427,
+ 0x65977B70C7AF4631, 0x99F889B2DE39D74F, 0x233F30BF54E1D143,
+ 0x9A9675D3D9A63C97, 0x5470554FF334F9A8, 0x166ACB744A4F5688,
+ 0x70C74CAAB2E4AEAD, 0xF0D091646F294D12, 0x57B82A89684031D1,
+ 0xEFD95A5A61BE0B6B, 0x2FBD12E969F2F29A, 0x9BD37013FEFF9FE8,
+ 0x3F9B0404D6085A06, 0x4940C1F3166CFE15, 0x09542C4DCDF3DEFB,
+ 0xB4C5218385CD5CE3, 0xC935B7DC4462A641, 0x3417F8A68ED3B63F,
+ 0xB80959295B215B40, 0xF99CDAEF3B8C8572, 0x018C0614F8FCB95D,
+ 0x1B14ACCD1A3ACDF3, 0x84D471F200BB732D, 0xC1A3110E95E8DA16,
+ 0x430A7220BF1A82B8, 0xB77E090D39DF210E, 0x5EF4BD9F3CD05E9D,
+ 0x9D4FF6DA7E57A444, 0xDA1D60E183D4A5F8, 0xB287C38417998E47,
+ 0xFE3EDC121BB31886, 0xC7FE3CCC980CCBEF, 0xE46FB590189BFD03,
+ 0x3732FD469A4C57DC, 0x7EF700A07CF1AD65, 0x59C64468A31D8859,
+ 0x762FB0B4D45B61F6, 0x155BAED099047718, 0x68755E4C3D50BAA6,
+ 0xE9214E7F22D8B4DF, 0x2ADDBF532EAC95F4, 0x32AE3909B4BD0109,
+ 0x834DF537B08E3450, 0xFA209DA84220728D, 0x9E691D9B9EFE23F7,
+ 0x0446D288C4AE8D7F, 0x7B4CC524E169785B, 0x21D87F0135CA1385,
+ 0xCEBB400F137B8AA5, 0x272E2B66580796BE, 0x3612264125C2B0DE,
+ 0x057702BDAD1EFBB2, 0xD4BABB8EACF84BE9, 0x91583139641BC67B,
+ 0x8BDC2DE08036E024, 0x603C8156F49F68ED, 0xF7D236F7DBEF5111,
+ 0x9727C4598AD21E80, 0xA08A0896670A5FD7, 0xCB4A8F4309EBA9CB,
+ 0x81AF564B0F7036A1, 0xC0B99AA778199ABD, 0x959F1EC83FC8E952,
+ 0x8C505077794A81B9, 0x3ACAAF8F056338F0, 0x07B43F50627A6778,
+ 0x4A44AB49F5ECCC77, 0x3BC3D6E4B679EE98, 0x9CC0D4D1CF14108C,
+ 0x4406C00B206BC8A0, 0x82A18854C8D72D89, 0x67E366B35C3C432C,
+ 0xB923DD61102B37F2, 0x56AB2779D884271D, 0xBE83E1B0FF1525AF,
+ 0xFB7C65D4217E49A9, 0x6BDBE0E76D48E7D4, 0x08DF828745D9179E,
+ 0x22EA6A9ADD53BD34, 0xE36E141C5622200A, 0x7F805D1B8CB750EE,
+ 0xAFE5C7A59F58E837, 0xE27F996A4FB1C23C, 0xD3867DFB0775F0D0,
+ 0xD0E673DE6E88891A, 0x123AEB9EAFB86C25, 0x30F1D5D5C145B895,
+ 0xBB434A2DEE7269E7, 0x78CB67ECF931FA38, 0xF33B0372323BBF9C,
+ 0x52D66336FB279C74, 0x505F33AC0AFB4EAA, 0xE8A5CD99A2CCE187,
+ 0x534974801E2D30BB, 0x8D2D5711D5876D90, 0x1F1A412891BC038E,
+ 0xD6E2E71D82E56648, 0x74036C3A497732B7, 0x89B67ED96361F5AB,
+ 0xFFED95D8F1EA02A2, 0xE72B3BD61464D43D, 0xA6300F170BDC4820,
+ 0xEBC18760ED78A77A };
+
+const uint64_t Tiger::SBOX2[256] = {
+ 0xE6A6BE5A05A12138, 0xB5A122A5B4F87C98, 0x563C6089140B6990,
+ 0x4C46CB2E391F5DD5, 0xD932ADDBC9B79434, 0x08EA70E42015AFF5,
+ 0xD765A6673E478CF1, 0xC4FB757EAB278D99, 0xDF11C6862D6E0692,
+ 0xDDEB84F10D7F3B16, 0x6F2EF604A665EA04, 0x4A8E0F0FF0E0DFB3,
+ 0xA5EDEEF83DBCBA51, 0xFC4F0A2A0EA4371E, 0xE83E1DA85CB38429,
+ 0xDC8FF882BA1B1CE2, 0xCD45505E8353E80D, 0x18D19A00D4DB0717,
+ 0x34A0CFEDA5F38101, 0x0BE77E518887CAF2, 0x1E341438B3C45136,
+ 0xE05797F49089CCF9, 0xFFD23F9DF2591D14, 0x543DDA228595C5CD,
+ 0x661F81FD99052A33, 0x8736E641DB0F7B76, 0x15227725418E5307,
+ 0xE25F7F46162EB2FA, 0x48A8B2126C13D9FE, 0xAFDC541792E76EEA,
+ 0x03D912BFC6D1898F, 0x31B1AAFA1B83F51B, 0xF1AC2796E42AB7D9,
+ 0x40A3A7D7FCD2EBAC, 0x1056136D0AFBBCC5, 0x7889E1DD9A6D0C85,
+ 0xD33525782A7974AA, 0xA7E25D09078AC09B, 0xBD4138B3EAC6EDD0,
+ 0x920ABFBE71EB9E70, 0xA2A5D0F54FC2625C, 0xC054E36B0B1290A3,
+ 0xF6DD59FF62FE932B, 0x3537354511A8AC7D, 0xCA845E9172FADCD4,
+ 0x84F82B60329D20DC, 0x79C62CE1CD672F18, 0x8B09A2ADD124642C,
+ 0xD0C1E96A19D9E726, 0x5A786A9B4BA9500C, 0x0E020336634C43F3,
+ 0xC17B474AEB66D822, 0x6A731AE3EC9BAAC2, 0x8226667AE0840258,
+ 0x67D4567691CAECA5, 0x1D94155C4875ADB5, 0x6D00FD985B813FDF,
+ 0x51286EFCB774CD06, 0x5E8834471FA744AF, 0xF72CA0AEE761AE2E,
+ 0xBE40E4CDAEE8E09A, 0xE9970BBB5118F665, 0x726E4BEB33DF1964,
+ 0x703B000729199762, 0x4631D816F5EF30A7, 0xB880B5B51504A6BE,
+ 0x641793C37ED84B6C, 0x7B21ED77F6E97D96, 0x776306312EF96B73,
+ 0xAE528948E86FF3F4, 0x53DBD7F286A3F8F8, 0x16CADCE74CFC1063,
+ 0x005C19BDFA52C6DD, 0x68868F5D64D46AD3, 0x3A9D512CCF1E186A,
+ 0x367E62C2385660AE, 0xE359E7EA77DCB1D7, 0x526C0773749ABE6E,
+ 0x735AE5F9D09F734B, 0x493FC7CC8A558BA8, 0xB0B9C1533041AB45,
+ 0x321958BA470A59BD, 0x852DB00B5F46C393, 0x91209B2BD336B0E5,
+ 0x6E604F7D659EF19F, 0xB99A8AE2782CCB24, 0xCCF52AB6C814C4C7,
+ 0x4727D9AFBE11727B, 0x7E950D0C0121B34D, 0x756F435670AD471F,
+ 0xF5ADD442615A6849, 0x4E87E09980B9957A, 0x2ACFA1DF50AEE355,
+ 0xD898263AFD2FD556, 0xC8F4924DD80C8FD6, 0xCF99CA3D754A173A,
+ 0xFE477BACAF91BF3C, 0xED5371F6D690C12D, 0x831A5C285E687094,
+ 0xC5D3C90A3708A0A4, 0x0F7F903717D06580, 0x19F9BB13B8FDF27F,
+ 0xB1BD6F1B4D502843, 0x1C761BA38FFF4012, 0x0D1530C4E2E21F3B,
+ 0x8943CE69A7372C8A, 0xE5184E11FEB5CE66, 0x618BDB80BD736621,
+ 0x7D29BAD68B574D0B, 0x81BB613E25E6FE5B, 0x071C9C10BC07913F,
+ 0xC7BEEB7909AC2D97, 0xC3E58D353BC5D757, 0xEB017892F38F61E8,
+ 0xD4EFFB9C9B1CC21A, 0x99727D26F494F7AB, 0xA3E063A2956B3E03,
+ 0x9D4A8B9A4AA09C30, 0x3F6AB7D500090FB4, 0x9CC0F2A057268AC0,
+ 0x3DEE9D2DEDBF42D1, 0x330F49C87960A972, 0xC6B2720287421B41,
+ 0x0AC59EC07C00369C, 0xEF4EAC49CB353425, 0xF450244EEF0129D8,
+ 0x8ACC46E5CAF4DEB6, 0x2FFEAB63989263F7, 0x8F7CB9FE5D7A4578,
+ 0x5BD8F7644E634635, 0x427A7315BF2DC900, 0x17D0C4AA2125261C,
+ 0x3992486C93518E50, 0xB4CBFEE0A2D7D4C3, 0x7C75D6202C5DDD8D,
+ 0xDBC295D8E35B6C61, 0x60B369D302032B19, 0xCE42685FDCE44132,
+ 0x06F3DDB9DDF65610, 0x8EA4D21DB5E148F0, 0x20B0FCE62FCD496F,
+ 0x2C1B912358B0EE31, 0xB28317B818F5A308, 0xA89C1E189CA6D2CF,
+ 0x0C6B18576AAADBC8, 0xB65DEAA91299FAE3, 0xFB2B794B7F1027E7,
+ 0x04E4317F443B5BEB, 0x4B852D325939D0A6, 0xD5AE6BEEFB207FFC,
+ 0x309682B281C7D374, 0xBAE309A194C3B475, 0x8CC3F97B13B49F05,
+ 0x98A9422FF8293967, 0x244B16B01076FF7C, 0xF8BF571C663D67EE,
+ 0x1F0D6758EEE30DA1, 0xC9B611D97ADEB9B7, 0xB7AFD5887B6C57A2,
+ 0x6290AE846B984FE1, 0x94DF4CDEACC1A5FD, 0x058A5BD1C5483AFF,
+ 0x63166CC142BA3C37, 0x8DB8526EB2F76F40, 0xE10880036F0D6D4E,
+ 0x9E0523C9971D311D, 0x45EC2824CC7CD691, 0x575B8359E62382C9,
+ 0xFA9E400DC4889995, 0xD1823ECB45721568, 0xDAFD983B8206082F,
+ 0xAA7D29082386A8CB, 0x269FCD4403B87588, 0x1B91F5F728BDD1E0,
+ 0xE4669F39040201F6, 0x7A1D7C218CF04ADE, 0x65623C29D79CE5CE,
+ 0x2368449096C00BB1, 0xAB9BF1879DA503BA, 0xBC23ECB1A458058E,
+ 0x9A58DF01BB401ECC, 0xA070E868A85F143D, 0x4FF188307DF2239E,
+ 0x14D565B41A641183, 0xEE13337452701602, 0x950E3DCF3F285E09,
+ 0x59930254B9C80953, 0x3BF299408930DA6D, 0xA955943F53691387,
+ 0xA15EDECAA9CB8784, 0x29142127352BE9A0, 0x76F0371FFF4E7AFB,
+ 0x0239F450274F2228, 0xBB073AF01D5E868B, 0xBFC80571C10E96C1,
+ 0xD267088568222E23, 0x9671A3D48E80B5B0, 0x55B5D38AE193BB81,
+ 0x693AE2D0A18B04B8, 0x5C48B4ECADD5335F, 0xFD743B194916A1CA,
+ 0x2577018134BE98C4, 0xE77987E83C54A4AD, 0x28E11014DA33E1B9,
+ 0x270CC59E226AA213, 0x71495F756D1A5F60, 0x9BE853FB60AFEF77,
+ 0xADC786A7F7443DBF, 0x0904456173B29A82, 0x58BC7A66C232BD5E,
+ 0xF306558C673AC8B2, 0x41F639C6B6C9772A, 0x216DEFE99FDA35DA,
+ 0x11640CC71C7BE615, 0x93C43694565C5527, 0xEA038E6246777839,
+ 0xF9ABF3CE5A3E2469, 0x741E768D0FD312D2, 0x0144B883CED652C6,
+ 0xC20B5A5BA33F8552, 0x1AE69633C3435A9D, 0x97A28CA4088CFDEC,
+ 0x8824A43C1E96F420, 0x37612FA66EEEA746, 0x6B4CB165F9CF0E5A,
+ 0x43AA1C06A0ABFB4A, 0x7F4DC26FF162796B, 0x6CBACC8E54ED9B0F,
+ 0xA6B7FFEFD2BB253E, 0x2E25BC95B0A29D4F, 0x86D6A58BDEF1388C,
+ 0xDED74AC576B6F054, 0x8030BDBC2B45805D, 0x3C81AF70E94D9289,
+ 0x3EFF6DDA9E3100DB, 0xB38DC39FDFCC8847, 0x123885528D17B87E,
+ 0xF2DA0ED240B1B642, 0x44CEFADCD54BF9A9, 0x1312200E433C7EE6,
+ 0x9FFCC84F3A78C748, 0xF0CD1F72248576BB, 0xEC6974053638CFE4,
+ 0x2BA7B67C0CEC4E4C, 0xAC2F4DF3E5CE32ED, 0xCB33D14326EA4C11,
+ 0xA4E9044CC77E58BC, 0x5F513293D934FCEF, 0x5DC9645506E55444,
+ 0x50DE418F317DE40A, 0x388CB31A69DDE259, 0x2DB4A83455820A86,
+ 0x9010A91E84711AE9, 0x4DF7F0B7B1498371, 0xD62A2EABC0977179,
+ 0x22FAC097AA8D5C0E };
+
+const uint64_t Tiger::SBOX3[256] = {
+ 0xF49FCC2FF1DAF39B, 0x487FD5C66FF29281, 0xE8A30667FCDCA83F,
+ 0x2C9B4BE3D2FCCE63, 0xDA3FF74B93FBBBC2, 0x2FA165D2FE70BA66,
+ 0xA103E279970E93D4, 0xBECDEC77B0E45E71, 0xCFB41E723985E497,
+ 0xB70AAA025EF75017, 0xD42309F03840B8E0, 0x8EFC1AD035898579,
+ 0x96C6920BE2B2ABC5, 0x66AF4163375A9172, 0x2174ABDCCA7127FB,
+ 0xB33CCEA64A72FF41, 0xF04A4933083066A5, 0x8D970ACDD7289AF5,
+ 0x8F96E8E031C8C25E, 0xF3FEC02276875D47, 0xEC7BF310056190DD,
+ 0xF5ADB0AEBB0F1491, 0x9B50F8850FD58892, 0x4975488358B74DE8,
+ 0xA3354FF691531C61, 0x0702BBE481D2C6EE, 0x89FB24057DEDED98,
+ 0xAC3075138596E902, 0x1D2D3580172772ED, 0xEB738FC28E6BC30D,
+ 0x5854EF8F63044326, 0x9E5C52325ADD3BBE, 0x90AA53CF325C4623,
+ 0xC1D24D51349DD067, 0x2051CFEEA69EA624, 0x13220F0A862E7E4F,
+ 0xCE39399404E04864, 0xD9C42CA47086FCB7, 0x685AD2238A03E7CC,
+ 0x066484B2AB2FF1DB, 0xFE9D5D70EFBF79EC, 0x5B13B9DD9C481854,
+ 0x15F0D475ED1509AD, 0x0BEBCD060EC79851, 0xD58C6791183AB7F8,
+ 0xD1187C5052F3EEE4, 0xC95D1192E54E82FF, 0x86EEA14CB9AC6CA2,
+ 0x3485BEB153677D5D, 0xDD191D781F8C492A, 0xF60866BAA784EBF9,
+ 0x518F643BA2D08C74, 0x8852E956E1087C22, 0xA768CB8DC410AE8D,
+ 0x38047726BFEC8E1A, 0xA67738B4CD3B45AA, 0xAD16691CEC0DDE19,
+ 0xC6D4319380462E07, 0xC5A5876D0BA61938, 0x16B9FA1FA58FD840,
+ 0x188AB1173CA74F18, 0xABDA2F98C99C021F, 0x3E0580AB134AE816,
+ 0x5F3B05B773645ABB, 0x2501A2BE5575F2F6, 0x1B2F74004E7E8BA9,
+ 0x1CD7580371E8D953, 0x7F6ED89562764E30, 0xB15926FF596F003D,
+ 0x9F65293DA8C5D6B9, 0x6ECEF04DD690F84C, 0x4782275FFF33AF88,
+ 0xE41433083F820801, 0xFD0DFE409A1AF9B5, 0x4325A3342CDB396B,
+ 0x8AE77E62B301B252, 0xC36F9E9F6655615A, 0x85455A2D92D32C09,
+ 0xF2C7DEA949477485, 0x63CFB4C133A39EBA, 0x83B040CC6EBC5462,
+ 0x3B9454C8FDB326B0, 0x56F56A9E87FFD78C, 0x2DC2940D99F42BC6,
+ 0x98F7DF096B096E2D, 0x19A6E01E3AD852BF, 0x42A99CCBDBD4B40B,
+ 0xA59998AF45E9C559, 0x366295E807D93186, 0x6B48181BFAA1F773,
+ 0x1FEC57E2157A0A1D, 0x4667446AF6201AD5, 0xE615EBCACFB0F075,
+ 0xB8F31F4F68290778, 0x22713ED6CE22D11E, 0x3057C1A72EC3C93B,
+ 0xCB46ACC37C3F1F2F, 0xDBB893FD02AAF50E, 0x331FD92E600B9FCF,
+ 0xA498F96148EA3AD6, 0xA8D8426E8B6A83EA, 0xA089B274B7735CDC,
+ 0x87F6B3731E524A11, 0x118808E5CBC96749, 0x9906E4C7B19BD394,
+ 0xAFED7F7E9B24A20C, 0x6509EADEEB3644A7, 0x6C1EF1D3E8EF0EDE,
+ 0xB9C97D43E9798FB4, 0xA2F2D784740C28A3, 0x7B8496476197566F,
+ 0x7A5BE3E6B65F069D, 0xF96330ED78BE6F10, 0xEEE60DE77A076A15,
+ 0x2B4BEE4AA08B9BD0, 0x6A56A63EC7B8894E, 0x02121359BA34FEF4,
+ 0x4CBF99F8283703FC, 0x398071350CAF30C8, 0xD0A77A89F017687A,
+ 0xF1C1A9EB9E423569, 0x8C7976282DEE8199, 0x5D1737A5DD1F7ABD,
+ 0x4F53433C09A9FA80, 0xFA8B0C53DF7CA1D9, 0x3FD9DCBC886CCB77,
+ 0xC040917CA91B4720, 0x7DD00142F9D1DCDF, 0x8476FC1D4F387B58,
+ 0x23F8E7C5F3316503, 0x032A2244E7E37339, 0x5C87A5D750F5A74B,
+ 0x082B4CC43698992E, 0xDF917BECB858F63C, 0x3270B8FC5BF86DDA,
+ 0x10AE72BB29B5DD76, 0x576AC94E7700362B, 0x1AD112DAC61EFB8F,
+ 0x691BC30EC5FAA427, 0xFF246311CC327143, 0x3142368E30E53206,
+ 0x71380E31E02CA396, 0x958D5C960AAD76F1, 0xF8D6F430C16DA536,
+ 0xC8FFD13F1BE7E1D2, 0x7578AE66004DDBE1, 0x05833F01067BE646,
+ 0xBB34B5AD3BFE586D, 0x095F34C9A12B97F0, 0x247AB64525D60CA8,
+ 0xDCDBC6F3017477D1, 0x4A2E14D4DECAD24D, 0xBDB5E6D9BE0A1EEB,
+ 0x2A7E70F7794301AB, 0xDEF42D8A270540FD, 0x01078EC0A34C22C1,
+ 0xE5DE511AF4C16387, 0x7EBB3A52BD9A330A, 0x77697857AA7D6435,
+ 0x004E831603AE4C32, 0xE7A21020AD78E312, 0x9D41A70C6AB420F2,
+ 0x28E06C18EA1141E6, 0xD2B28CBD984F6B28, 0x26B75F6C446E9D83,
+ 0xBA47568C4D418D7F, 0xD80BADBFE6183D8E, 0x0E206D7F5F166044,
+ 0xE258A43911CBCA3E, 0x723A1746B21DC0BC, 0xC7CAA854F5D7CDD3,
+ 0x7CAC32883D261D9C, 0x7690C26423BA942C, 0x17E55524478042B8,
+ 0xE0BE477656A2389F, 0x4D289B5E67AB2DA0, 0x44862B9C8FBBFD31,
+ 0xB47CC8049D141365, 0x822C1B362B91C793, 0x4EB14655FB13DFD8,
+ 0x1ECBBA0714E2A97B, 0x6143459D5CDE5F14, 0x53A8FBF1D5F0AC89,
+ 0x97EA04D81C5E5B00, 0x622181A8D4FDB3F3, 0xE9BCD341572A1208,
+ 0x1411258643CCE58A, 0x9144C5FEA4C6E0A4, 0x0D33D06565CF620F,
+ 0x54A48D489F219CA1, 0xC43E5EAC6D63C821, 0xA9728B3A72770DAF,
+ 0xD7934E7B20DF87EF, 0xE35503B61A3E86E5, 0xCAE321FBC819D504,
+ 0x129A50B3AC60BFA6, 0xCD5E68EA7E9FB6C3, 0xB01C90199483B1C7,
+ 0x3DE93CD5C295376C, 0xAED52EDF2AB9AD13, 0x2E60F512C0A07884,
+ 0xBC3D86A3E36210C9, 0x35269D9B163951CE, 0x0C7D6E2AD0CDB5FA,
+ 0x59E86297D87F5733, 0x298EF221898DB0E7, 0x55000029D1A5AA7E,
+ 0x8BC08AE1B5061B45, 0xC2C31C2B6C92703A, 0x94CC596BAF25EF42,
+ 0x0A1D73DB22540456, 0x04B6A0F9D9C4179A, 0xEFFDAFA2AE3D3C60,
+ 0xF7C8075BB49496C4, 0x9CC5C7141D1CD4E3, 0x78BD1638218E5534,
+ 0xB2F11568F850246A, 0xEDFABCFA9502BC29, 0x796CE5F2DA23051B,
+ 0xAAE128B0DC93537C, 0x3A493DA0EE4B29AE, 0xB5DF6B2C416895D7,
+ 0xFCABBD25122D7F37, 0x70810B58105DC4B1, 0xE10FDD37F7882A90,
+ 0x524DCAB5518A3F5C, 0x3C9E85878451255B, 0x4029828119BD34E2,
+ 0x74A05B6F5D3CECCB, 0xB610021542E13ECA, 0x0FF979D12F59E2AC,
+ 0x6037DA27E4F9CC50, 0x5E92975A0DF1847D, 0xD66DE190D3E623FE,
+ 0x5032D6B87B568048, 0x9A36B7CE8235216E, 0x80272A7A24F64B4A,
+ 0x93EFED8B8C6916F7, 0x37DDBFF44CCE1555, 0x4B95DB5D4B99BD25,
+ 0x92D3FDA169812FC0, 0xFB1A4A9A90660BB6, 0x730C196946A4B9B2,
+ 0x81E289AA7F49DA68, 0x64669A0F83B1A05F, 0x27B3FF7D9644F48B,
+ 0xCC6B615C8DB675B3, 0x674F20B9BCEBBE95, 0x6F31238275655982,
+ 0x5AE488713E45CF05, 0xBF619F9954C21157, 0xEABAC46040A8EAE9,
+ 0x454C6FE9F2C0C1CD, 0x419CF6496412691C, 0xD3DC3BEF265B0F70,
+ 0x6D0E60F5C3578A9E };
+
+const uint64_t Tiger::SBOX4[256] = {
+ 0x5B0E608526323C55, 0x1A46C1A9FA1B59F5, 0xA9E245A17C4C8FFA,
+ 0x65CA5159DB2955D7, 0x05DB0A76CE35AFC2, 0x81EAC77EA9113D45,
+ 0x528EF88AB6AC0A0D, 0xA09EA253597BE3FF, 0x430DDFB3AC48CD56,
+ 0xC4B3A67AF45CE46F, 0x4ECECFD8FBE2D05E, 0x3EF56F10B39935F0,
+ 0x0B22D6829CD619C6, 0x17FD460A74DF2069, 0x6CF8CC8E8510ED40,
+ 0xD6C824BF3A6ECAA7, 0x61243D581A817049, 0x048BACB6BBC163A2,
+ 0xD9A38AC27D44CC32, 0x7FDDFF5BAAF410AB, 0xAD6D495AA804824B,
+ 0xE1A6A74F2D8C9F94, 0xD4F7851235DEE8E3, 0xFD4B7F886540D893,
+ 0x247C20042AA4BFDA, 0x096EA1C517D1327C, 0xD56966B4361A6685,
+ 0x277DA5C31221057D, 0x94D59893A43ACFF7, 0x64F0C51CCDC02281,
+ 0x3D33BCC4FF6189DB, 0xE005CB184CE66AF1, 0xFF5CCD1D1DB99BEA,
+ 0xB0B854A7FE42980F, 0x7BD46A6A718D4B9F, 0xD10FA8CC22A5FD8C,
+ 0xD31484952BE4BD31, 0xC7FA975FCB243847, 0x4886ED1E5846C407,
+ 0x28CDDB791EB70B04, 0xC2B00BE2F573417F, 0x5C9590452180F877,
+ 0x7A6BDDFFF370EB00, 0xCE509E38D6D9D6A4, 0xEBEB0F00647FA702,
+ 0x1DCC06CF76606F06, 0xE4D9F28BA286FF0A, 0xD85A305DC918C262,
+ 0x475B1D8732225F54, 0x2D4FB51668CCB5FE, 0xA679B9D9D72BBA20,
+ 0x53841C0D912D43A5, 0x3B7EAA48BF12A4E8, 0x781E0E47F22F1DDF,
+ 0xEFF20CE60AB50973, 0x20D261D19DFFB742, 0x16A12B03062A2E39,
+ 0x1960EB2239650495, 0x251C16FED50EB8B8, 0x9AC0C330F826016E,
+ 0xED152665953E7671, 0x02D63194A6369570, 0x5074F08394B1C987,
+ 0x70BA598C90B25CE1, 0x794A15810B9742F6, 0x0D5925E9FCAF8C6C,
+ 0x3067716CD868744E, 0x910AB077E8D7731B, 0x6A61BBDB5AC42F61,
+ 0x93513EFBF0851567, 0xF494724B9E83E9D5, 0xE887E1985C09648D,
+ 0x34B1D3C675370CFD, 0xDC35E433BC0D255D, 0xD0AAB84234131BE0,
+ 0x08042A50B48B7EAF, 0x9997C4EE44A3AB35, 0x829A7B49201799D0,
+ 0x263B8307B7C54441, 0x752F95F4FD6A6CA6, 0x927217402C08C6E5,
+ 0x2A8AB754A795D9EE, 0xA442F7552F72943D, 0x2C31334E19781208,
+ 0x4FA98D7CEAEE6291, 0x55C3862F665DB309, 0xBD0610175D53B1F3,
+ 0x46FE6CB840413F27, 0x3FE03792DF0CFA59, 0xCFE700372EB85E8F,
+ 0xA7BE29E7ADBCE118, 0xE544EE5CDE8431DD, 0x8A781B1B41F1873E,
+ 0xA5C94C78A0D2F0E7, 0x39412E2877B60728, 0xA1265EF3AFC9A62C,
+ 0xBCC2770C6A2506C5, 0x3AB66DD5DCE1CE12, 0xE65499D04A675B37,
+ 0x7D8F523481BFD216, 0x0F6F64FCEC15F389, 0x74EFBE618B5B13C8,
+ 0xACDC82B714273E1D, 0xDD40BFE003199D17, 0x37E99257E7E061F8,
+ 0xFA52626904775AAA, 0x8BBBF63A463D56F9, 0xF0013F1543A26E64,
+ 0xA8307E9F879EC898, 0xCC4C27A4150177CC, 0x1B432F2CCA1D3348,
+ 0xDE1D1F8F9F6FA013, 0x606602A047A7DDD6, 0xD237AB64CC1CB2C7,
+ 0x9B938E7225FCD1D3, 0xEC4E03708E0FF476, 0xFEB2FBDA3D03C12D,
+ 0xAE0BCED2EE43889A, 0x22CB8923EBFB4F43, 0x69360D013CF7396D,
+ 0x855E3602D2D4E022, 0x073805BAD01F784C, 0x33E17A133852F546,
+ 0xDF4874058AC7B638, 0xBA92B29C678AA14A, 0x0CE89FC76CFAADCD,
+ 0x5F9D4E0908339E34, 0xF1AFE9291F5923B9, 0x6E3480F60F4A265F,
+ 0xEEBF3A2AB29B841C, 0xE21938A88F91B4AD, 0x57DFEFF845C6D3C3,
+ 0x2F006B0BF62CAAF2, 0x62F479EF6F75EE78, 0x11A55AD41C8916A9,
+ 0xF229D29084FED453, 0x42F1C27B16B000E6, 0x2B1F76749823C074,
+ 0x4B76ECA3C2745360, 0x8C98F463B91691BD, 0x14BCC93CF1ADE66A,
+ 0x8885213E6D458397, 0x8E177DF0274D4711, 0xB49B73B5503F2951,
+ 0x10168168C3F96B6B, 0x0E3D963B63CAB0AE, 0x8DFC4B5655A1DB14,
+ 0xF789F1356E14DE5C, 0x683E68AF4E51DAC1, 0xC9A84F9D8D4B0FD9,
+ 0x3691E03F52A0F9D1, 0x5ED86E46E1878E80, 0x3C711A0E99D07150,
+ 0x5A0865B20C4E9310, 0x56FBFC1FE4F0682E, 0xEA8D5DE3105EDF9B,
+ 0x71ABFDB12379187A, 0x2EB99DE1BEE77B9C, 0x21ECC0EA33CF4523,
+ 0x59A4D7521805C7A1, 0x3896F5EB56AE7C72, 0xAA638F3DB18F75DC,
+ 0x9F39358DABE9808E, 0xB7DEFA91C00B72AC, 0x6B5541FD62492D92,
+ 0x6DC6DEE8F92E4D5B, 0x353F57ABC4BEEA7E, 0x735769D6DA5690CE,
+ 0x0A234AA642391484, 0xF6F9508028F80D9D, 0xB8E319A27AB3F215,
+ 0x31AD9C1151341A4D, 0x773C22A57BEF5805, 0x45C7561A07968633,
+ 0xF913DA9E249DBE36, 0xDA652D9B78A64C68, 0x4C27A97F3BC334EF,
+ 0x76621220E66B17F4, 0x967743899ACD7D0B, 0xF3EE5BCAE0ED6782,
+ 0x409F753600C879FC, 0x06D09A39B5926DB6, 0x6F83AEB0317AC588,
+ 0x01E6CA4A86381F21, 0x66FF3462D19F3025, 0x72207C24DDFD3BFB,
+ 0x4AF6B6D3E2ECE2EB, 0x9C994DBEC7EA08DE, 0x49ACE597B09A8BC4,
+ 0xB38C4766CF0797BA, 0x131B9373C57C2A75, 0xB1822CCE61931E58,
+ 0x9D7555B909BA1C0C, 0x127FAFDD937D11D2, 0x29DA3BADC66D92E4,
+ 0xA2C1D57154C2ECBC, 0x58C5134D82F6FE24, 0x1C3AE3515B62274F,
+ 0xE907C82E01CB8126, 0xF8ED091913E37FCB, 0x3249D8F9C80046C9,
+ 0x80CF9BEDE388FB63, 0x1881539A116CF19E, 0x5103F3F76BD52457,
+ 0x15B7E6F5AE47F7A8, 0xDBD7C6DED47E9CCF, 0x44E55C410228BB1A,
+ 0xB647D4255EDB4E99, 0x5D11882BB8AAFC30, 0xF5098BBB29D3212A,
+ 0x8FB5EA14E90296B3, 0x677B942157DD025A, 0xFB58E7C0A390ACB5,
+ 0x89D3674C83BD4A01, 0x9E2DA4DF4BF3B93B, 0xFCC41E328CAB4829,
+ 0x03F38C96BA582C52, 0xCAD1BDBD7FD85DB2, 0xBBB442C16082AE83,
+ 0xB95FE86BA5DA9AB0, 0xB22E04673771A93F, 0x845358C9493152D8,
+ 0xBE2A488697B4541E, 0x95A2DC2DD38E6966, 0xC02C11AC923C852B,
+ 0x2388B1990DF2A87B, 0x7C8008FA1B4F37BE, 0x1F70D0C84D54E503,
+ 0x5490ADEC7ECE57D4, 0x002B3C27D9063A3A, 0x7EAEA3848030A2BF,
+ 0xC602326DED2003C0, 0x83A7287D69A94086, 0xC57A5FCB30F57A8A,
+ 0xB56844E479EBE779, 0xA373B40F05DCBCE9, 0xD71A786E88570EE2,
+ 0x879CBACDBDE8F6A0, 0x976AD1BCC164A32F, 0xAB21E25E9666D78B,
+ 0x901063AAE5E5C33C, 0x9818B34448698D90, 0xE36487AE3E1E8ABB,
+ 0xAFBDF931893BDCB4, 0x6345A0DC5FBBD519, 0x8628FE269B9465CA,
+ 0x1E5D01603F9C51EC, 0x4DE44006A15049B7, 0xBF6C70E5F776CBB1,
+ 0x411218F2EF552BED, 0xCB0C0708705A36A3, 0xE74D14754F986044,
+ 0xCD56D9430EA8280E, 0xC12591D7535F5065, 0xC83223F1720AEF96,
+ 0xC3A0396F7363A51F };
+
+}
diff --git a/comm/third_party/botan/src/lib/hash/tiger/tiger.cpp b/comm/third_party/botan/src/lib/hash/tiger/tiger.cpp
new file mode 100644
index 0000000000..ac2038a0fd
--- /dev/null
+++ b/comm/third_party/botan/src/lib/hash/tiger/tiger.cpp
@@ -0,0 +1,190 @@
+/*
+* Tiger
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/tiger.h>
+#include <botan/loadstor.h>
+#include <botan/exceptn.h>
+
+namespace Botan {
+
+std::unique_ptr<HashFunction> Tiger::copy_state() const
+ {
+ return std::unique_ptr<HashFunction>(new Tiger(*this));
+ }
+
+namespace {
+
+/*
+* Tiger Mixing Function
+*/
+inline void mix(secure_vector<uint64_t>& X)
+ {
+ X[0] -= X[7] ^ 0xA5A5A5A5A5A5A5A5;
+ X[1] ^= X[0];
+ X[2] += X[1];
+ X[3] -= X[2] ^ ((~X[1]) << 19);
+ X[4] ^= X[3];
+ X[5] += X[4];
+ X[6] -= X[5] ^ ((~X[4]) >> 23);
+ X[7] ^= X[6];
+
+ X[0] += X[7];
+ X[1] -= X[0] ^ ((~X[7]) << 19);
+ X[2] ^= X[1];
+ X[3] += X[2];
+ X[4] -= X[3] ^ ((~X[2]) >> 23);
+ X[5] ^= X[4];
+ X[6] += X[5];
+ X[7] -= X[6] ^ 0x0123456789ABCDEF;
+ }
+
+}
+
+/*
+* Tiger Compression Function
+*/
+void Tiger::compress_n(const uint8_t input[], size_t blocks)
+ {
+ uint64_t A = m_digest[0], B = m_digest[1], C = m_digest[2];
+
+ for(size_t i = 0; i != blocks; ++i)
+ {
+ load_le(m_X.data(), input, m_X.size());
+
+ pass(A, B, C, m_X, 5); mix(m_X);
+ pass(C, A, B, m_X, 7); mix(m_X);
+ pass(B, C, A, m_X, 9);
+
+ for(size_t j = 3; j != m_passes; ++j)
+ {
+ mix(m_X);
+ pass(A, B, C, m_X, 9);
+ uint64_t T = A; A = C; C = B; B = T;
+ }
+
+ A = (m_digest[0] ^= A);
+ B = m_digest[1] = B - m_digest[1];
+ C = (m_digest[2] += C);
+
+ input += hash_block_size();
+ }
+ }
+
+/*
+* Copy out the digest
+*/
+void Tiger::copy_out(uint8_t output[])
+ {
+ copy_out_vec_le(output, output_length(), m_digest);
+ }
+
+/*
+* Tiger Pass
+*/
+void Tiger::pass(uint64_t& A, uint64_t& B, uint64_t& C,
+ const secure_vector<uint64_t>& X,
+ uint8_t mul)
+ {
+ C ^= X[0];
+ A -= SBOX1[get_byte(7, C)] ^ SBOX2[get_byte(5, C)] ^
+ SBOX3[get_byte(3, C)] ^ SBOX4[get_byte(1, C)];
+ B += SBOX1[get_byte(0, C)] ^ SBOX2[get_byte(2, C)] ^
+ SBOX3[get_byte(4, C)] ^ SBOX4[get_byte(6, C)];
+ B *= mul;
+
+ A ^= X[1];
+ B -= SBOX1[get_byte(7, A)] ^ SBOX2[get_byte(5, A)] ^
+ SBOX3[get_byte(3, A)] ^ SBOX4[get_byte(1, A)];
+ C += SBOX1[get_byte(0, A)] ^ SBOX2[get_byte(2, A)] ^
+ SBOX3[get_byte(4, A)] ^ SBOX4[get_byte(6, A)];
+ C *= mul;
+
+ B ^= X[2];
+ C -= SBOX1[get_byte(7, B)] ^ SBOX2[get_byte(5, B)] ^
+ SBOX3[get_byte(3, B)] ^ SBOX4[get_byte(1, B)];
+ A += SBOX1[get_byte(0, B)] ^ SBOX2[get_byte(2, B)] ^
+ SBOX3[get_byte(4, B)] ^ SBOX4[get_byte(6, B)];
+ A *= mul;
+
+ C ^= X[3];
+ A -= SBOX1[get_byte(7, C)] ^ SBOX2[get_byte(5, C)] ^
+ SBOX3[get_byte(3, C)] ^ SBOX4[get_byte(1, C)];
+ B += SBOX1[get_byte(0, C)] ^ SBOX2[get_byte(2, C)] ^
+ SBOX3[get_byte(4, C)] ^ SBOX4[get_byte(6, C)];
+ B *= mul;
+
+ A ^= X[4];
+ B -= SBOX1[get_byte(7, A)] ^ SBOX2[get_byte(5, A)] ^
+ SBOX3[get_byte(3, A)] ^ SBOX4[get_byte(1, A)];
+ C += SBOX1[get_byte(0, A)] ^ SBOX2[get_byte(2, A)] ^
+ SBOX3[get_byte(4, A)] ^ SBOX4[get_byte(6, A)];
+ C *= mul;
+
+ B ^= X[5];
+ C -= SBOX1[get_byte(7, B)] ^ SBOX2[get_byte(5, B)] ^
+ SBOX3[get_byte(3, B)] ^ SBOX4[get_byte(1, B)];
+ A += SBOX1[get_byte(0, B)] ^ SBOX2[get_byte(2, B)] ^
+ SBOX3[get_byte(4, B)] ^ SBOX4[get_byte(6, B)];
+ A *= mul;
+
+ C ^= X[6];
+ A -= SBOX1[get_byte(7, C)] ^ SBOX2[get_byte(5, C)] ^
+ SBOX3[get_byte(3, C)] ^ SBOX4[get_byte(1, C)];
+ B += SBOX1[get_byte(0, C)] ^ SBOX2[get_byte(2, C)] ^
+ SBOX3[get_byte(4, C)] ^ SBOX4[get_byte(6, C)];
+ B *= mul;
+
+ A ^= X[7];
+ B -= SBOX1[get_byte(7, A)] ^ SBOX2[get_byte(5, A)] ^
+ SBOX3[get_byte(3, A)] ^ SBOX4[get_byte(1, A)];
+ C += SBOX1[get_byte(0, A)] ^ SBOX2[get_byte(2, A)] ^
+ SBOX3[get_byte(4, A)] ^ SBOX4[get_byte(6, A)];
+ C *= mul;
+ }
+
+/*
+* Clear memory of sensitive data
+*/
+void Tiger::clear()
+ {
+ MDx_HashFunction::clear();
+ zeroise(m_X);
+ m_digest[0] = 0x0123456789ABCDEF;
+ m_digest[1] = 0xFEDCBA9876543210;
+ m_digest[2] = 0xF096A5B4C3B2E187;
+ }
+
+/*
+* Return the name of this type
+*/
+std::string Tiger::name() const
+ {
+ return "Tiger(" + std::to_string(output_length()) + "," +
+ std::to_string(m_passes) + ")";
+ }
+
+/*
+* Tiger Constructor
+*/
+Tiger::Tiger(size_t hash_len, size_t passes) :
+ MDx_HashFunction(64, false, false),
+ m_X(8),
+ m_digest(3),
+ m_hash_len(hash_len),
+ m_passes(passes)
+ {
+ if(output_length() != 16 && output_length() != 20 && output_length() != 24)
+ throw Invalid_Argument("Tiger: Illegal hash output size: " +
+ std::to_string(output_length()));
+
+ if(passes < 3)
+ throw Invalid_Argument("Tiger: Invalid number of passes: "
+ + std::to_string(passes));
+ clear();
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/hash/tiger/tiger.h b/comm/third_party/botan/src/lib/hash/tiger/tiger.h
new file mode 100644
index 0000000000..6e17ce83c9
--- /dev/null
+++ b/comm/third_party/botan/src/lib/hash/tiger/tiger.h
@@ -0,0 +1,59 @@
+/*
+* Tiger
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_TIGER_H_
+#define BOTAN_TIGER_H_
+
+#include <botan/mdx_hash.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(tiger.h)
+
+namespace Botan {
+
+/**
+* Tiger
+*/
+class BOTAN_PUBLIC_API(2,0) Tiger final : public MDx_HashFunction
+ {
+ public:
+ std::string name() const override;
+ size_t output_length() const override { return m_hash_len; }
+
+ HashFunction* clone() const override
+ {
+ return new Tiger(output_length(), m_passes);
+ }
+
+ std::unique_ptr<HashFunction> copy_state() const override;
+
+ void clear() override;
+
+ /**
+ * @param out_size specifies the output length; can be 16, 20, or 24
+ * @param passes to make in the algorithm
+ */
+ Tiger(size_t out_size = 24, size_t passes = 3);
+ private:
+ void compress_n(const uint8_t[], size_t block) override;
+ void copy_out(uint8_t[]) override;
+
+ static void pass(uint64_t& A, uint64_t& B, uint64_t& C,
+ const secure_vector<uint64_t>& M,
+ uint8_t mul);
+
+ static const uint64_t SBOX1[256];
+ static const uint64_t SBOX2[256];
+ static const uint64_t SBOX3[256];
+ static const uint64_t SBOX4[256];
+
+ secure_vector<uint64_t> m_X, m_digest;
+ const size_t m_hash_len, m_passes;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/hash/whirlpool/info.txt b/comm/third_party/botan/src/lib/hash/whirlpool/info.txt
new file mode 100644
index 0000000000..e1a8da77a6
--- /dev/null
+++ b/comm/third_party/botan/src/lib/hash/whirlpool/info.txt
@@ -0,0 +1,7 @@
+<defines>
+WHIRLPOOL -> 20131128
+</defines>
+
+<requires>
+mdx_hash
+</requires>
diff --git a/comm/third_party/botan/src/lib/hash/whirlpool/whirlpool.cpp b/comm/third_party/botan/src/lib/hash/whirlpool/whirlpool.cpp
new file mode 100644
index 0000000000..e1ffa4f20c
--- /dev/null
+++ b/comm/third_party/botan/src/lib/hash/whirlpool/whirlpool.cpp
@@ -0,0 +1,150 @@
+/*
+* Whirlpool
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/whrlpool.h>
+#include <botan/loadstor.h>
+
+namespace Botan {
+
+std::unique_ptr<HashFunction> Whirlpool::copy_state() const
+ {
+ return std::unique_ptr<HashFunction>(new Whirlpool(*this));
+ }
+
+/*
+* Whirlpool Compression Function
+*/
+void Whirlpool::compress_n(const uint8_t in[], size_t blocks)
+ {
+ static const uint64_t RC[10] = {
+ 0x1823C6E887B8014F, 0x36A6D2F5796F9152,
+ 0x60BC9B8EA30C7B35, 0x1DE0D7C22E4BFE57,
+ 0x157737E59FF04ADA, 0x58C9290AB1A06B85,
+ 0xBD5D10F4CB3E0567, 0xE427418BA77D95D8,
+ 0xFBEE7C66DD17479E, 0xCA2DBF07AD5A8333
+ };
+
+ for(size_t i = 0; i != blocks; ++i)
+ {
+ load_be(m_M.data(), in, m_M.size());
+
+ uint64_t K0, K1, K2, K3, K4, K5, K6, K7;
+ K0 = m_digest[0]; K1 = m_digest[1]; K2 = m_digest[2]; K3 = m_digest[3];
+ K4 = m_digest[4]; K5 = m_digest[5]; K6 = m_digest[6]; K7 = m_digest[7];
+
+ uint64_t B0, B1, B2, B3, B4, B5, B6, B7;
+ B0 = K0 ^ m_M[0]; B1 = K1 ^ m_M[1]; B2 = K2 ^ m_M[2]; B3 = K3 ^ m_M[3];
+ B4 = K4 ^ m_M[4]; B5 = K5 ^ m_M[5]; B6 = K6 ^ m_M[6]; B7 = K7 ^ m_M[7];
+
+ for(size_t j = 0; j != 10; ++j)
+ {
+ uint64_t T0, T1, T2, T3, T4, T5, T6, T7;
+ T0 = C0[get_byte(0, K0)] ^ C1[get_byte(1, K7)] ^
+ C2[get_byte(2, K6)] ^ C3[get_byte(3, K5)] ^
+ C4[get_byte(4, K4)] ^ C5[get_byte(5, K3)] ^
+ C6[get_byte(6, K2)] ^ C7[get_byte(7, K1)] ^ RC[j];
+ T1 = C0[get_byte(0, K1)] ^ C1[get_byte(1, K0)] ^
+ C2[get_byte(2, K7)] ^ C3[get_byte(3, K6)] ^
+ C4[get_byte(4, K5)] ^ C5[get_byte(5, K4)] ^
+ C6[get_byte(6, K3)] ^ C7[get_byte(7, K2)];
+ T2 = C0[get_byte(0, K2)] ^ C1[get_byte(1, K1)] ^
+ C2[get_byte(2, K0)] ^ C3[get_byte(3, K7)] ^
+ C4[get_byte(4, K6)] ^ C5[get_byte(5, K5)] ^
+ C6[get_byte(6, K4)] ^ C7[get_byte(7, K3)];
+ T3 = C0[get_byte(0, K3)] ^ C1[get_byte(1, K2)] ^
+ C2[get_byte(2, K1)] ^ C3[get_byte(3, K0)] ^
+ C4[get_byte(4, K7)] ^ C5[get_byte(5, K6)] ^
+ C6[get_byte(6, K5)] ^ C7[get_byte(7, K4)];
+ T4 = C0[get_byte(0, K4)] ^ C1[get_byte(1, K3)] ^
+ C2[get_byte(2, K2)] ^ C3[get_byte(3, K1)] ^
+ C4[get_byte(4, K0)] ^ C5[get_byte(5, K7)] ^
+ C6[get_byte(6, K6)] ^ C7[get_byte(7, K5)];
+ T5 = C0[get_byte(0, K5)] ^ C1[get_byte(1, K4)] ^
+ C2[get_byte(2, K3)] ^ C3[get_byte(3, K2)] ^
+ C4[get_byte(4, K1)] ^ C5[get_byte(5, K0)] ^
+ C6[get_byte(6, K7)] ^ C7[get_byte(7, K6)];
+ T6 = C0[get_byte(0, K6)] ^ C1[get_byte(1, K5)] ^
+ C2[get_byte(2, K4)] ^ C3[get_byte(3, K3)] ^
+ C4[get_byte(4, K2)] ^ C5[get_byte(5, K1)] ^
+ C6[get_byte(6, K0)] ^ C7[get_byte(7, K7)];
+ T7 = C0[get_byte(0, K7)] ^ C1[get_byte(1, K6)] ^
+ C2[get_byte(2, K5)] ^ C3[get_byte(3, K4)] ^
+ C4[get_byte(4, K3)] ^ C5[get_byte(5, K2)] ^
+ C6[get_byte(6, K1)] ^ C7[get_byte(7, K0)];
+
+ K0 = T0; K1 = T1; K2 = T2; K3 = T3;
+ K4 = T4; K5 = T5; K6 = T6; K7 = T7;
+
+ T0 = C0[get_byte(0, B0)] ^ C1[get_byte(1, B7)] ^
+ C2[get_byte(2, B6)] ^ C3[get_byte(3, B5)] ^
+ C4[get_byte(4, B4)] ^ C5[get_byte(5, B3)] ^
+ C6[get_byte(6, B2)] ^ C7[get_byte(7, B1)] ^ K0;
+ T1 = C0[get_byte(0, B1)] ^ C1[get_byte(1, B0)] ^
+ C2[get_byte(2, B7)] ^ C3[get_byte(3, B6)] ^
+ C4[get_byte(4, B5)] ^ C5[get_byte(5, B4)] ^
+ C6[get_byte(6, B3)] ^ C7[get_byte(7, B2)] ^ K1;
+ T2 = C0[get_byte(0, B2)] ^ C1[get_byte(1, B1)] ^
+ C2[get_byte(2, B0)] ^ C3[get_byte(3, B7)] ^
+ C4[get_byte(4, B6)] ^ C5[get_byte(5, B5)] ^
+ C6[get_byte(6, B4)] ^ C7[get_byte(7, B3)] ^ K2;
+ T3 = C0[get_byte(0, B3)] ^ C1[get_byte(1, B2)] ^
+ C2[get_byte(2, B1)] ^ C3[get_byte(3, B0)] ^
+ C4[get_byte(4, B7)] ^ C5[get_byte(5, B6)] ^
+ C6[get_byte(6, B5)] ^ C7[get_byte(7, B4)] ^ K3;
+ T4 = C0[get_byte(0, B4)] ^ C1[get_byte(1, B3)] ^
+ C2[get_byte(2, B2)] ^ C3[get_byte(3, B1)] ^
+ C4[get_byte(4, B0)] ^ C5[get_byte(5, B7)] ^
+ C6[get_byte(6, B6)] ^ C7[get_byte(7, B5)] ^ K4;
+ T5 = C0[get_byte(0, B5)] ^ C1[get_byte(1, B4)] ^
+ C2[get_byte(2, B3)] ^ C3[get_byte(3, B2)] ^
+ C4[get_byte(4, B1)] ^ C5[get_byte(5, B0)] ^
+ C6[get_byte(6, B7)] ^ C7[get_byte(7, B6)] ^ K5;
+ T6 = C0[get_byte(0, B6)] ^ C1[get_byte(1, B5)] ^
+ C2[get_byte(2, B4)] ^ C3[get_byte(3, B3)] ^
+ C4[get_byte(4, B2)] ^ C5[get_byte(5, B1)] ^
+ C6[get_byte(6, B0)] ^ C7[get_byte(7, B7)] ^ K6;
+ T7 = C0[get_byte(0, B7)] ^ C1[get_byte(1, B6)] ^
+ C2[get_byte(2, B5)] ^ C3[get_byte(3, B4)] ^
+ C4[get_byte(4, B3)] ^ C5[get_byte(5, B2)] ^
+ C6[get_byte(6, B1)] ^ C7[get_byte(7, B0)] ^ K7;
+
+ B0 = T0; B1 = T1; B2 = T2; B3 = T3;
+ B4 = T4; B5 = T5; B6 = T6; B7 = T7;
+ }
+
+ m_digest[0] ^= B0 ^ m_M[0];
+ m_digest[1] ^= B1 ^ m_M[1];
+ m_digest[2] ^= B2 ^ m_M[2];
+ m_digest[3] ^= B3 ^ m_M[3];
+ m_digest[4] ^= B4 ^ m_M[4];
+ m_digest[5] ^= B5 ^ m_M[5];
+ m_digest[6] ^= B6 ^ m_M[6];
+ m_digest[7] ^= B7 ^ m_M[7];
+
+ in += hash_block_size();
+ }
+ }
+
+/*
+* Copy out the digest
+*/
+void Whirlpool::copy_out(uint8_t output[])
+ {
+ copy_out_vec_be(output, output_length(), m_digest);
+ }
+
+/*
+* Clear memory of sensitive data
+*/
+void Whirlpool::clear()
+ {
+ MDx_HashFunction::clear();
+ zeroise(m_M);
+ zeroise(m_digest);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/hash/whirlpool/whrl_tab.cpp b/comm/third_party/botan/src/lib/hash/whirlpool/whrl_tab.cpp
new file mode 100644
index 0000000000..e4be36247e
--- /dev/null
+++ b/comm/third_party/botan/src/lib/hash/whirlpool/whrl_tab.cpp
@@ -0,0 +1,540 @@
+/*
+* Diffusion Tables for Whirlpool
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/whrlpool.h>
+
+namespace Botan {
+
+alignas(64) const uint64_t Whirlpool::C0[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 };
+
+alignas(64) const uint64_t Whirlpool::C1[256] = {
+0xD818186018C07830, 0x2623238C2305AF46, 0xB8C6C63FC67EF991, 0xFBE8E887E8136FCD,
+0xCB878726874CA113, 0x11B8B8DAB8A9626D, 0x0901010401080502, 0x0D4F4F214F426E9E,
+0x9B3636D836ADEE6C, 0xFFA6A6A2A6590451, 0x0CD2D26FD2DEBDB9, 0x0EF5F5F3F5FB06F7,
+0x967979F979EF80F2, 0x306F6FA16F5FCEDE, 0x6D91917E91FCEF3F, 0xF852525552AA07A4,
+0x4760609D6027FDC0, 0x35BCBCCABC897665, 0x379B9B569BACCD2B, 0x8A8E8E028E048C01,
+0xD2A3A3B6A371155B, 0x6C0C0C300C603C18, 0x847B7BF17BFF8AF6, 0x803535D435B5E16A,
+0xF51D1D741DE8693A, 0xB3E0E0A7E05347DD, 0x21D7D77BD7F6ACB3, 0x9CC2C22FC25EED99,
+0x432E2EB82E6D965C, 0x294B4B314B627A96, 0x5DFEFEDFFEA321E1, 0xD5575741578216AE,
+0xBD15155415A8412A, 0xE87777C1779FB6EE, 0x923737DC37A5EB6E, 0x9EE5E5B3E57B56D7,
+0x139F9F469F8CD923, 0x23F0F0E7F0D317FD, 0x204A4A354A6A7F94, 0x44DADA4FDA9E95A9,
+0xA258587D58FA25B0, 0xCFC9C903C906CA8F, 0x7C2929A429558D52, 0x5A0A0A280A502214,
+0x50B1B1FEB1E14F7F, 0xC9A0A0BAA0691A5D, 0x146B6BB16B7FDAD6, 0xD985852E855CAB17,
+0x3CBDBDCEBD817367, 0x8F5D5D695DD234BA, 0x9010104010805020, 0x07F4F4F7F4F303F5,
+0xDDCBCB0BCB16C08B, 0xD33E3EF83EEDC67C, 0x2D0505140528110A, 0x78676781671FE6CE,
+0x97E4E4B7E47353D5, 0x0227279C2725BB4E, 0x7341411941325882, 0xA78B8B168B2C9D0B,
+0xF6A7A7A6A7510153, 0xB27D7DE97DCF94FA, 0x4995956E95DCFB37, 0x56D8D847D88E9FAD,
+0x70FBFBCBFB8B30EB, 0xCDEEEE9FEE2371C1, 0xBB7C7CED7CC791F8, 0x716666856617E3CC,
+0x7BDDDD53DDA68EA7, 0xAF17175C17B84B2E, 0x454747014702468E, 0x1A9E9E429E84DC21,
+0xD4CACA0FCA1EC589, 0x582D2DB42D75995A, 0x2EBFBFC6BF917963, 0x3F07071C07381B0E,
+0xACADAD8EAD012347, 0xB05A5A755AEA2FB4, 0xEF838336836CB51B, 0xB63333CC3385FF66,
+0x5C636391633FF2C6, 0x1202020802100A04, 0x93AAAA92AA393849, 0xDE7171D971AFA8E2,
+0xC6C8C807C80ECF8D, 0xD119196419C87D32, 0x3B49493949727092, 0x5FD9D943D9869AAF,
+0x31F2F2EFF2C31DF9, 0xA8E3E3ABE34B48DB, 0xB95B5B715BE22AB6, 0xBC88881A8834920D,
+0x3E9A9A529AA4C829, 0x0B262698262DBE4C, 0xBF3232C8328DFA64, 0x59B0B0FAB0E94A7D,
+0xF2E9E983E91B6ACF, 0x770F0F3C0F78331E, 0x33D5D573D5E6A6B7, 0xF480803A8074BA1D,
+0x27BEBEC2BE997C61, 0xEBCDCD13CD26DE87, 0x893434D034BDE468, 0x3248483D487A7590,
+0x54FFFFDBFFAB24E3, 0x8D7A7AF57AF78FF4, 0x6490907A90F4EA3D, 0x9D5F5F615FC23EBE,
+0x3D202080201DA040, 0x0F6868BD6867D5D0, 0xCA1A1A681AD07234, 0xB7AEAE82AE192C41,
+0x7DB4B4EAB4C95E75, 0xCE54544D549A19A8, 0x7F93937693ECE53B, 0x2F222288220DAA44,
+0x6364648D6407E9C8, 0x2AF1F1E3F1DB12FF, 0xCC7373D173BFA2E6, 0x8212124812905A24,
+0x7A40401D403A5D80, 0x4808082008402810, 0x95C3C32BC356E89B, 0xDFECEC97EC337BC5,
+0x4DDBDB4BDB9690AB, 0xC0A1A1BEA1611F5F, 0x918D8D0E8D1C8307, 0xC83D3DF43DF5C97A,
+0x5B97976697CCF133, 0x0000000000000000, 0xF9CFCF1BCF36D483, 0x6E2B2BAC2B458756,
+0xE17676C57697B3EC, 0xE68282328264B019, 0x28D6D67FD6FEA9B1, 0xC31B1B6C1BD87736,
+0x74B5B5EEB5C15B77, 0xBEAFAF86AF112943, 0x1D6A6AB56A77DFD4, 0xEA50505D50BA0DA0,
+0x5745450945124C8A, 0x38F3F3EBF3CB18FB, 0xAD3030C0309DF060, 0xC4EFEF9BEF2B74C3,
+0xDA3F3FFC3FE5C37E, 0xC755554955921CAA, 0xDBA2A2B2A2791059, 0xE9EAEA8FEA0365C9,
+0x6A656589650FECCA, 0x03BABAD2BAB96869, 0x4A2F2FBC2F65935E, 0x8EC0C027C04EE79D,
+0x60DEDE5FDEBE81A1, 0xFC1C1C701CE06C38, 0x46FDFDD3FDBB2EE7, 0x1F4D4D294D52649A,
+0x7692927292E4E039, 0xFA7575C9758FBCEA, 0x3606061806301E0C, 0xAE8A8A128A249809,
+0x4BB2B2F2B2F94079, 0x85E6E6BFE66359D1, 0x7E0E0E380E70361C, 0xE71F1F7C1FF8633E,
+0x556262956237F7C4, 0x3AD4D477D4EEA3B5, 0x81A8A89AA829324D, 0x5296966296C4F431,
+0x62F9F9C3F99B3AEF, 0xA3C5C533C566F697, 0x102525942535B14A, 0xAB59597959F220B2,
+0xD084842A8454AE15, 0xC57272D572B7A7E4, 0xEC3939E439D5DD72, 0x164C4C2D4C5A6198,
+0x945E5E655ECA3BBC, 0x9F7878FD78E785F0, 0xE53838E038DDD870, 0x988C8C0A8C148605,
+0x17D1D163D1C6B2BF, 0xE4A5A5AEA5410B57, 0xA1E2E2AFE2434DD9, 0x4E616199612FF8C2,
+0x42B3B3F6B3F1457B, 0x342121842115A542, 0x089C9C4A9C94D625, 0xEE1E1E781EF0663C,
+0x6143431143225286, 0xB1C7C73BC776FC93, 0x4FFCFCD7FCB32BE5, 0x2404041004201408,
+0xE351515951B208A2, 0x2599995E99BCC72F, 0x226D6DA96D4FC4DA, 0x650D0D340D68391A,
+0x79FAFACFFA8335E9, 0x69DFDF5BDFB684A3, 0xA97E7EE57ED79BFC, 0x19242490243DB448,
+0xFE3B3BEC3BC5D776, 0x9AABAB96AB313D4B, 0xF0CECE1FCE3ED181, 0x9911114411885522,
+0x838F8F068F0C8903, 0x044E4E254E4A6B9C, 0x66B7B7E6B7D15173, 0xE0EBEB8BEB0B60CB,
+0xC13C3CF03CFDCC78, 0xFD81813E817CBF1F, 0x4094946A94D4FE35, 0x1CF7F7FBF7EB0CF3,
+0x18B9B9DEB9A1676F, 0x8B13134C13985F26, 0x512C2CB02C7D9C58, 0x05D3D36BD3D6B8BB,
+0x8CE7E7BBE76B5CD3, 0x396E6EA56E57CBDC, 0xAAC4C437C46EF395, 0x1B03030C03180F06,
+0xDC565645568A13AC, 0x5E44440D441A4988, 0xA07F7FE17FDF9EFE, 0x88A9A99EA921374F,
+0x672A2AA82A4D8254, 0x0ABBBBD6BBB16D6B, 0x87C1C123C146E29F, 0xF153535153A202A6,
+0x72DCDC57DCAE8BA5, 0x530B0B2C0B582716, 0x019D9D4E9D9CD327, 0x2B6C6CAD6C47C1D8,
+0xA43131C43195F562, 0xF37474CD7487B9E8, 0x15F6F6FFF6E309F1, 0x4C464605460A438C,
+0xA5ACAC8AAC092645, 0xB589891E893C970F, 0xB414145014A04428, 0xBAE1E1A3E15B42DF,
+0xA616165816B04E2C, 0xF73A3AE83ACDD274, 0x066969B9696FD0D2, 0x4109092409482D12,
+0xD77070DD70A7ADE0, 0x6FB6B6E2B6D95471, 0x1ED0D067D0CEB7BD, 0xD6EDED93ED3B7EC7,
+0xE2CCCC17CC2EDB85, 0x68424215422A5784, 0x2C98985A98B4C22D, 0xEDA4A4AAA4490E55,
+0x752828A0285D8850, 0x865C5C6D5CDA31B8, 0x6BF8F8C7F8933FED, 0xC28686228644A411 };
+
+alignas(64) const uint64_t Whirlpool::C2[256] = {
+0x30D818186018C078, 0x462623238C2305AF, 0x91B8C6C63FC67EF9, 0xCDFBE8E887E8136F,
+0x13CB878726874CA1, 0x6D11B8B8DAB8A962, 0x0209010104010805, 0x9E0D4F4F214F426E,
+0x6C9B3636D836ADEE, 0x51FFA6A6A2A65904, 0xB90CD2D26FD2DEBD, 0xF70EF5F5F3F5FB06,
+0xF2967979F979EF80, 0xDE306F6FA16F5FCE, 0x3F6D91917E91FCEF, 0xA4F852525552AA07,
+0xC04760609D6027FD, 0x6535BCBCCABC8976, 0x2B379B9B569BACCD, 0x018A8E8E028E048C,
+0x5BD2A3A3B6A37115, 0x186C0C0C300C603C, 0xF6847B7BF17BFF8A, 0x6A803535D435B5E1,
+0x3AF51D1D741DE869, 0xDDB3E0E0A7E05347, 0xB321D7D77BD7F6AC, 0x999CC2C22FC25EED,
+0x5C432E2EB82E6D96, 0x96294B4B314B627A, 0xE15DFEFEDFFEA321, 0xAED5575741578216,
+0x2ABD15155415A841, 0xEEE87777C1779FB6, 0x6E923737DC37A5EB, 0xD79EE5E5B3E57B56,
+0x23139F9F469F8CD9, 0xFD23F0F0E7F0D317, 0x94204A4A354A6A7F, 0xA944DADA4FDA9E95,
+0xB0A258587D58FA25, 0x8FCFC9C903C906CA, 0x527C2929A429558D, 0x145A0A0A280A5022,
+0x7F50B1B1FEB1E14F, 0x5DC9A0A0BAA0691A, 0xD6146B6BB16B7FDA, 0x17D985852E855CAB,
+0x673CBDBDCEBD8173, 0xBA8F5D5D695DD234, 0x2090101040108050, 0xF507F4F4F7F4F303,
+0x8BDDCBCB0BCB16C0, 0x7CD33E3EF83EEDC6, 0x0A2D050514052811, 0xCE78676781671FE6,
+0xD597E4E4B7E47353, 0x4E0227279C2725BB, 0x8273414119413258, 0x0BA78B8B168B2C9D,
+0x53F6A7A7A6A75101, 0xFAB27D7DE97DCF94, 0x374995956E95DCFB, 0xAD56D8D847D88E9F,
+0xEB70FBFBCBFB8B30, 0xC1CDEEEE9FEE2371, 0xF8BB7C7CED7CC791, 0xCC716666856617E3,
+0xA77BDDDD53DDA68E, 0x2EAF17175C17B84B, 0x8E45474701470246, 0x211A9E9E429E84DC,
+0x89D4CACA0FCA1EC5, 0x5A582D2DB42D7599, 0x632EBFBFC6BF9179, 0x0E3F07071C07381B,
+0x47ACADAD8EAD0123, 0xB4B05A5A755AEA2F, 0x1BEF838336836CB5, 0x66B63333CC3385FF,
+0xC65C636391633FF2, 0x041202020802100A, 0x4993AAAA92AA3938, 0xE2DE7171D971AFA8,
+0x8DC6C8C807C80ECF, 0x32D119196419C87D, 0x923B494939497270, 0xAF5FD9D943D9869A,
+0xF931F2F2EFF2C31D, 0xDBA8E3E3ABE34B48, 0xB6B95B5B715BE22A, 0x0DBC88881A883492,
+0x293E9A9A529AA4C8, 0x4C0B262698262DBE, 0x64BF3232C8328DFA, 0x7D59B0B0FAB0E94A,
+0xCFF2E9E983E91B6A, 0x1E770F0F3C0F7833, 0xB733D5D573D5E6A6, 0x1DF480803A8074BA,
+0x6127BEBEC2BE997C, 0x87EBCDCD13CD26DE, 0x68893434D034BDE4, 0x903248483D487A75,
+0xE354FFFFDBFFAB24, 0xF48D7A7AF57AF78F, 0x3D6490907A90F4EA, 0xBE9D5F5F615FC23E,
+0x403D202080201DA0, 0xD00F6868BD6867D5, 0x34CA1A1A681AD072, 0x41B7AEAE82AE192C,
+0x757DB4B4EAB4C95E, 0xA8CE54544D549A19, 0x3B7F93937693ECE5, 0x442F222288220DAA,
+0xC86364648D6407E9, 0xFF2AF1F1E3F1DB12, 0xE6CC7373D173BFA2, 0x248212124812905A,
+0x807A40401D403A5D, 0x1048080820084028, 0x9B95C3C32BC356E8, 0xC5DFECEC97EC337B,
+0xAB4DDBDB4BDB9690, 0x5FC0A1A1BEA1611F, 0x07918D8D0E8D1C83, 0x7AC83D3DF43DF5C9,
+0x335B97976697CCF1, 0x0000000000000000, 0x83F9CFCF1BCF36D4, 0x566E2B2BAC2B4587,
+0xECE17676C57697B3, 0x19E68282328264B0, 0xB128D6D67FD6FEA9, 0x36C31B1B6C1BD877,
+0x7774B5B5EEB5C15B, 0x43BEAFAF86AF1129, 0xD41D6A6AB56A77DF, 0xA0EA50505D50BA0D,
+0x8A5745450945124C, 0xFB38F3F3EBF3CB18, 0x60AD3030C0309DF0, 0xC3C4EFEF9BEF2B74,
+0x7EDA3F3FFC3FE5C3, 0xAAC755554955921C, 0x59DBA2A2B2A27910, 0xC9E9EAEA8FEA0365,
+0xCA6A656589650FEC, 0x6903BABAD2BAB968, 0x5E4A2F2FBC2F6593, 0x9D8EC0C027C04EE7,
+0xA160DEDE5FDEBE81, 0x38FC1C1C701CE06C, 0xE746FDFDD3FDBB2E, 0x9A1F4D4D294D5264,
+0x397692927292E4E0, 0xEAFA7575C9758FBC, 0x0C3606061806301E, 0x09AE8A8A128A2498,
+0x794BB2B2F2B2F940, 0xD185E6E6BFE66359, 0x1C7E0E0E380E7036, 0x3EE71F1F7C1FF863,
+0xC4556262956237F7, 0xB53AD4D477D4EEA3, 0x4D81A8A89AA82932, 0x315296966296C4F4,
+0xEF62F9F9C3F99B3A, 0x97A3C5C533C566F6, 0x4A102525942535B1, 0xB2AB59597959F220,
+0x15D084842A8454AE, 0xE4C57272D572B7A7, 0x72EC3939E439D5DD, 0x98164C4C2D4C5A61,
+0xBC945E5E655ECA3B, 0xF09F7878FD78E785, 0x70E53838E038DDD8, 0x05988C8C0A8C1486,
+0xBF17D1D163D1C6B2, 0x57E4A5A5AEA5410B, 0xD9A1E2E2AFE2434D, 0xC24E616199612FF8,
+0x7B42B3B3F6B3F145, 0x42342121842115A5, 0x25089C9C4A9C94D6, 0x3CEE1E1E781EF066,
+0x8661434311432252, 0x93B1C7C73BC776FC, 0xE54FFCFCD7FCB32B, 0x0824040410042014,
+0xA2E351515951B208, 0x2F2599995E99BCC7, 0xDA226D6DA96D4FC4, 0x1A650D0D340D6839,
+0xE979FAFACFFA8335, 0xA369DFDF5BDFB684, 0xFCA97E7EE57ED79B, 0x4819242490243DB4,
+0x76FE3B3BEC3BC5D7, 0x4B9AABAB96AB313D, 0x81F0CECE1FCE3ED1, 0x2299111144118855,
+0x03838F8F068F0C89, 0x9C044E4E254E4A6B, 0x7366B7B7E6B7D151, 0xCBE0EBEB8BEB0B60,
+0x78C13C3CF03CFDCC, 0x1FFD81813E817CBF, 0x354094946A94D4FE, 0xF31CF7F7FBF7EB0C,
+0x6F18B9B9DEB9A167, 0x268B13134C13985F, 0x58512C2CB02C7D9C, 0xBB05D3D36BD3D6B8,
+0xD38CE7E7BBE76B5C, 0xDC396E6EA56E57CB, 0x95AAC4C437C46EF3, 0x061B03030C03180F,
+0xACDC565645568A13, 0x885E44440D441A49, 0xFEA07F7FE17FDF9E, 0x4F88A9A99EA92137,
+0x54672A2AA82A4D82, 0x6B0ABBBBD6BBB16D, 0x9F87C1C123C146E2, 0xA6F153535153A202,
+0xA572DCDC57DCAE8B, 0x16530B0B2C0B5827, 0x27019D9D4E9D9CD3, 0xD82B6C6CAD6C47C1,
+0x62A43131C43195F5, 0xE8F37474CD7487B9, 0xF115F6F6FFF6E309, 0x8C4C464605460A43,
+0x45A5ACAC8AAC0926, 0x0FB589891E893C97, 0x28B414145014A044, 0xDFBAE1E1A3E15B42,
+0x2CA616165816B04E, 0x74F73A3AE83ACDD2, 0xD2066969B9696FD0, 0x124109092409482D,
+0xE0D77070DD70A7AD, 0x716FB6B6E2B6D954, 0xBD1ED0D067D0CEB7, 0xC7D6EDED93ED3B7E,
+0x85E2CCCC17CC2EDB, 0x8468424215422A57, 0x2D2C98985A98B4C2, 0x55EDA4A4AAA4490E,
+0x50752828A0285D88, 0xB8865C5C6D5CDA31, 0xED6BF8F8C7F8933F, 0x11C28686228644A4 };
+
+alignas(64) const uint64_t Whirlpool::C3[256] = {
+0x7830D818186018C0, 0xAF462623238C2305, 0xF991B8C6C63FC67E, 0x6FCDFBE8E887E813,
+0xA113CB878726874C, 0x626D11B8B8DAB8A9, 0x0502090101040108, 0x6E9E0D4F4F214F42,
+0xEE6C9B3636D836AD, 0x0451FFA6A6A2A659, 0xBDB90CD2D26FD2DE, 0x06F70EF5F5F3F5FB,
+0x80F2967979F979EF, 0xCEDE306F6FA16F5F, 0xEF3F6D91917E91FC, 0x07A4F852525552AA,
+0xFDC04760609D6027, 0x766535BCBCCABC89, 0xCD2B379B9B569BAC, 0x8C018A8E8E028E04,
+0x155BD2A3A3B6A371, 0x3C186C0C0C300C60, 0x8AF6847B7BF17BFF, 0xE16A803535D435B5,
+0x693AF51D1D741DE8, 0x47DDB3E0E0A7E053, 0xACB321D7D77BD7F6, 0xED999CC2C22FC25E,
+0x965C432E2EB82E6D, 0x7A96294B4B314B62, 0x21E15DFEFEDFFEA3, 0x16AED55757415782,
+0x412ABD15155415A8, 0xB6EEE87777C1779F, 0xEB6E923737DC37A5, 0x56D79EE5E5B3E57B,
+0xD923139F9F469F8C, 0x17FD23F0F0E7F0D3, 0x7F94204A4A354A6A, 0x95A944DADA4FDA9E,
+0x25B0A258587D58FA, 0xCA8FCFC9C903C906, 0x8D527C2929A42955, 0x22145A0A0A280A50,
+0x4F7F50B1B1FEB1E1, 0x1A5DC9A0A0BAA069, 0xDAD6146B6BB16B7F, 0xAB17D985852E855C,
+0x73673CBDBDCEBD81, 0x34BA8F5D5D695DD2, 0x5020901010401080, 0x03F507F4F4F7F4F3,
+0xC08BDDCBCB0BCB16, 0xC67CD33E3EF83EED, 0x110A2D0505140528, 0xE6CE78676781671F,
+0x53D597E4E4B7E473, 0xBB4E0227279C2725, 0x5882734141194132, 0x9D0BA78B8B168B2C,
+0x0153F6A7A7A6A751, 0x94FAB27D7DE97DCF, 0xFB374995956E95DC, 0x9FAD56D8D847D88E,
+0x30EB70FBFBCBFB8B, 0x71C1CDEEEE9FEE23, 0x91F8BB7C7CED7CC7, 0xE3CC716666856617,
+0x8EA77BDDDD53DDA6, 0x4B2EAF17175C17B8, 0x468E454747014702, 0xDC211A9E9E429E84,
+0xC589D4CACA0FCA1E, 0x995A582D2DB42D75, 0x79632EBFBFC6BF91, 0x1B0E3F07071C0738,
+0x2347ACADAD8EAD01, 0x2FB4B05A5A755AEA, 0xB51BEF838336836C, 0xFF66B63333CC3385,
+0xF2C65C636391633F, 0x0A04120202080210, 0x384993AAAA92AA39, 0xA8E2DE7171D971AF,
+0xCF8DC6C8C807C80E, 0x7D32D119196419C8, 0x70923B4949394972, 0x9AAF5FD9D943D986,
+0x1DF931F2F2EFF2C3, 0x48DBA8E3E3ABE34B, 0x2AB6B95B5B715BE2, 0x920DBC88881A8834,
+0xC8293E9A9A529AA4, 0xBE4C0B262698262D, 0xFA64BF3232C8328D, 0x4A7D59B0B0FAB0E9,
+0x6ACFF2E9E983E91B, 0x331E770F0F3C0F78, 0xA6B733D5D573D5E6, 0xBA1DF480803A8074,
+0x7C6127BEBEC2BE99, 0xDE87EBCDCD13CD26, 0xE468893434D034BD, 0x75903248483D487A,
+0x24E354FFFFDBFFAB, 0x8FF48D7A7AF57AF7, 0xEA3D6490907A90F4, 0x3EBE9D5F5F615FC2,
+0xA0403D202080201D, 0xD5D00F6868BD6867, 0x7234CA1A1A681AD0, 0x2C41B7AEAE82AE19,
+0x5E757DB4B4EAB4C9, 0x19A8CE54544D549A, 0xE53B7F93937693EC, 0xAA442F222288220D,
+0xE9C86364648D6407, 0x12FF2AF1F1E3F1DB, 0xA2E6CC7373D173BF, 0x5A24821212481290,
+0x5D807A40401D403A, 0x2810480808200840, 0xE89B95C3C32BC356, 0x7BC5DFECEC97EC33,
+0x90AB4DDBDB4BDB96, 0x1F5FC0A1A1BEA161, 0x8307918D8D0E8D1C, 0xC97AC83D3DF43DF5,
+0xF1335B97976697CC, 0x0000000000000000, 0xD483F9CFCF1BCF36, 0x87566E2B2BAC2B45,
+0xB3ECE17676C57697, 0xB019E68282328264, 0xA9B128D6D67FD6FE, 0x7736C31B1B6C1BD8,
+0x5B7774B5B5EEB5C1, 0x2943BEAFAF86AF11, 0xDFD41D6A6AB56A77, 0x0DA0EA50505D50BA,
+0x4C8A574545094512, 0x18FB38F3F3EBF3CB, 0xF060AD3030C0309D, 0x74C3C4EFEF9BEF2B,
+0xC37EDA3F3FFC3FE5, 0x1CAAC75555495592, 0x1059DBA2A2B2A279, 0x65C9E9EAEA8FEA03,
+0xECCA6A656589650F, 0x686903BABAD2BAB9, 0x935E4A2F2FBC2F65, 0xE79D8EC0C027C04E,
+0x81A160DEDE5FDEBE, 0x6C38FC1C1C701CE0, 0x2EE746FDFDD3FDBB, 0x649A1F4D4D294D52,
+0xE0397692927292E4, 0xBCEAFA7575C9758F, 0x1E0C360606180630, 0x9809AE8A8A128A24,
+0x40794BB2B2F2B2F9, 0x59D185E6E6BFE663, 0x361C7E0E0E380E70, 0x633EE71F1F7C1FF8,
+0xF7C4556262956237, 0xA3B53AD4D477D4EE, 0x324D81A8A89AA829, 0xF4315296966296C4,
+0x3AEF62F9F9C3F99B, 0xF697A3C5C533C566, 0xB14A102525942535, 0x20B2AB59597959F2,
+0xAE15D084842A8454, 0xA7E4C57272D572B7, 0xDD72EC3939E439D5, 0x6198164C4C2D4C5A,
+0x3BBC945E5E655ECA, 0x85F09F7878FD78E7, 0xD870E53838E038DD, 0x8605988C8C0A8C14,
+0xB2BF17D1D163D1C6, 0x0B57E4A5A5AEA541, 0x4DD9A1E2E2AFE243, 0xF8C24E616199612F,
+0x457B42B3B3F6B3F1, 0xA542342121842115, 0xD625089C9C4A9C94, 0x663CEE1E1E781EF0,
+0x5286614343114322, 0xFC93B1C7C73BC776, 0x2BE54FFCFCD7FCB3, 0x1408240404100420,
+0x08A2E351515951B2, 0xC72F2599995E99BC, 0xC4DA226D6DA96D4F, 0x391A650D0D340D68,
+0x35E979FAFACFFA83, 0x84A369DFDF5BDFB6, 0x9BFCA97E7EE57ED7, 0xB44819242490243D,
+0xD776FE3B3BEC3BC5, 0x3D4B9AABAB96AB31, 0xD181F0CECE1FCE3E, 0x5522991111441188,
+0x8903838F8F068F0C, 0x6B9C044E4E254E4A, 0x517366B7B7E6B7D1, 0x60CBE0EBEB8BEB0B,
+0xCC78C13C3CF03CFD, 0xBF1FFD81813E817C, 0xFE354094946A94D4, 0x0CF31CF7F7FBF7EB,
+0x676F18B9B9DEB9A1, 0x5F268B13134C1398, 0x9C58512C2CB02C7D, 0xB8BB05D3D36BD3D6,
+0x5CD38CE7E7BBE76B, 0xCBDC396E6EA56E57, 0xF395AAC4C437C46E, 0x0F061B03030C0318,
+0x13ACDC565645568A, 0x49885E44440D441A, 0x9EFEA07F7FE17FDF, 0x374F88A9A99EA921,
+0x8254672A2AA82A4D, 0x6D6B0ABBBBD6BBB1, 0xE29F87C1C123C146, 0x02A6F153535153A2,
+0x8BA572DCDC57DCAE, 0x2716530B0B2C0B58, 0xD327019D9D4E9D9C, 0xC1D82B6C6CAD6C47,
+0xF562A43131C43195, 0xB9E8F37474CD7487, 0x09F115F6F6FFF6E3, 0x438C4C464605460A,
+0x2645A5ACAC8AAC09, 0x970FB589891E893C, 0x4428B414145014A0, 0x42DFBAE1E1A3E15B,
+0x4E2CA616165816B0, 0xD274F73A3AE83ACD, 0xD0D2066969B9696F, 0x2D12410909240948,
+0xADE0D77070DD70A7, 0x54716FB6B6E2B6D9, 0xB7BD1ED0D067D0CE, 0x7EC7D6EDED93ED3B,
+0xDB85E2CCCC17CC2E, 0x578468424215422A, 0xC22D2C98985A98B4, 0x0E55EDA4A4AAA449,
+0x8850752828A0285D, 0x31B8865C5C6D5CDA, 0x3FED6BF8F8C7F893, 0xA411C28686228644 };
+
+alignas(64) const uint64_t Whirlpool::C4[256] = {
+0xC07830D818186018, 0x05AF462623238C23, 0x7EF991B8C6C63FC6, 0x136FCDFBE8E887E8,
+0x4CA113CB87872687, 0xA9626D11B8B8DAB8, 0x0805020901010401, 0x426E9E0D4F4F214F,
+0xADEE6C9B3636D836, 0x590451FFA6A6A2A6, 0xDEBDB90CD2D26FD2, 0xFB06F70EF5F5F3F5,
+0xEF80F2967979F979, 0x5FCEDE306F6FA16F, 0xFCEF3F6D91917E91, 0xAA07A4F852525552,
+0x27FDC04760609D60, 0x89766535BCBCCABC, 0xACCD2B379B9B569B, 0x048C018A8E8E028E,
+0x71155BD2A3A3B6A3, 0x603C186C0C0C300C, 0xFF8AF6847B7BF17B, 0xB5E16A803535D435,
+0xE8693AF51D1D741D, 0x5347DDB3E0E0A7E0, 0xF6ACB321D7D77BD7, 0x5EED999CC2C22FC2,
+0x6D965C432E2EB82E, 0x627A96294B4B314B, 0xA321E15DFEFEDFFE, 0x8216AED557574157,
+0xA8412ABD15155415, 0x9FB6EEE87777C177, 0xA5EB6E923737DC37, 0x7B56D79EE5E5B3E5,
+0x8CD923139F9F469F, 0xD317FD23F0F0E7F0, 0x6A7F94204A4A354A, 0x9E95A944DADA4FDA,
+0xFA25B0A258587D58, 0x06CA8FCFC9C903C9, 0x558D527C2929A429, 0x5022145A0A0A280A,
+0xE14F7F50B1B1FEB1, 0x691A5DC9A0A0BAA0, 0x7FDAD6146B6BB16B, 0x5CAB17D985852E85,
+0x8173673CBDBDCEBD, 0xD234BA8F5D5D695D, 0x8050209010104010, 0xF303F507F4F4F7F4,
+0x16C08BDDCBCB0BCB, 0xEDC67CD33E3EF83E, 0x28110A2D05051405, 0x1FE6CE7867678167,
+0x7353D597E4E4B7E4, 0x25BB4E0227279C27, 0x3258827341411941, 0x2C9D0BA78B8B168B,
+0x510153F6A7A7A6A7, 0xCF94FAB27D7DE97D, 0xDCFB374995956E95, 0x8E9FAD56D8D847D8,
+0x8B30EB70FBFBCBFB, 0x2371C1CDEEEE9FEE, 0xC791F8BB7C7CED7C, 0x17E3CC7166668566,
+0xA68EA77BDDDD53DD, 0xB84B2EAF17175C17, 0x02468E4547470147, 0x84DC211A9E9E429E,
+0x1EC589D4CACA0FCA, 0x75995A582D2DB42D, 0x9179632EBFBFC6BF, 0x381B0E3F07071C07,
+0x012347ACADAD8EAD, 0xEA2FB4B05A5A755A, 0x6CB51BEF83833683, 0x85FF66B63333CC33,
+0x3FF2C65C63639163, 0x100A041202020802, 0x39384993AAAA92AA, 0xAFA8E2DE7171D971,
+0x0ECF8DC6C8C807C8, 0xC87D32D119196419, 0x7270923B49493949, 0x869AAF5FD9D943D9,
+0xC31DF931F2F2EFF2, 0x4B48DBA8E3E3ABE3, 0xE22AB6B95B5B715B, 0x34920DBC88881A88,
+0xA4C8293E9A9A529A, 0x2DBE4C0B26269826, 0x8DFA64BF3232C832, 0xE94A7D59B0B0FAB0,
+0x1B6ACFF2E9E983E9, 0x78331E770F0F3C0F, 0xE6A6B733D5D573D5, 0x74BA1DF480803A80,
+0x997C6127BEBEC2BE, 0x26DE87EBCDCD13CD, 0xBDE468893434D034, 0x7A75903248483D48,
+0xAB24E354FFFFDBFF, 0xF78FF48D7A7AF57A, 0xF4EA3D6490907A90, 0xC23EBE9D5F5F615F,
+0x1DA0403D20208020, 0x67D5D00F6868BD68, 0xD07234CA1A1A681A, 0x192C41B7AEAE82AE,
+0xC95E757DB4B4EAB4, 0x9A19A8CE54544D54, 0xECE53B7F93937693, 0x0DAA442F22228822,
+0x07E9C86364648D64, 0xDB12FF2AF1F1E3F1, 0xBFA2E6CC7373D173, 0x905A248212124812,
+0x3A5D807A40401D40, 0x4028104808082008, 0x56E89B95C3C32BC3, 0x337BC5DFECEC97EC,
+0x9690AB4DDBDB4BDB, 0x611F5FC0A1A1BEA1, 0x1C8307918D8D0E8D, 0xF5C97AC83D3DF43D,
+0xCCF1335B97976697, 0x0000000000000000, 0x36D483F9CFCF1BCF, 0x4587566E2B2BAC2B,
+0x97B3ECE17676C576, 0x64B019E682823282, 0xFEA9B128D6D67FD6, 0xD87736C31B1B6C1B,
+0xC15B7774B5B5EEB5, 0x112943BEAFAF86AF, 0x77DFD41D6A6AB56A, 0xBA0DA0EA50505D50,
+0x124C8A5745450945, 0xCB18FB38F3F3EBF3, 0x9DF060AD3030C030, 0x2B74C3C4EFEF9BEF,
+0xE5C37EDA3F3FFC3F, 0x921CAAC755554955, 0x791059DBA2A2B2A2, 0x0365C9E9EAEA8FEA,
+0x0FECCA6A65658965, 0xB9686903BABAD2BA, 0x65935E4A2F2FBC2F, 0x4EE79D8EC0C027C0,
+0xBE81A160DEDE5FDE, 0xE06C38FC1C1C701C, 0xBB2EE746FDFDD3FD, 0x52649A1F4D4D294D,
+0xE4E0397692927292, 0x8FBCEAFA7575C975, 0x301E0C3606061806, 0x249809AE8A8A128A,
+0xF940794BB2B2F2B2, 0x6359D185E6E6BFE6, 0x70361C7E0E0E380E, 0xF8633EE71F1F7C1F,
+0x37F7C45562629562, 0xEEA3B53AD4D477D4, 0x29324D81A8A89AA8, 0xC4F4315296966296,
+0x9B3AEF62F9F9C3F9, 0x66F697A3C5C533C5, 0x35B14A1025259425, 0xF220B2AB59597959,
+0x54AE15D084842A84, 0xB7A7E4C57272D572, 0xD5DD72EC3939E439, 0x5A6198164C4C2D4C,
+0xCA3BBC945E5E655E, 0xE785F09F7878FD78, 0xDDD870E53838E038, 0x148605988C8C0A8C,
+0xC6B2BF17D1D163D1, 0x410B57E4A5A5AEA5, 0x434DD9A1E2E2AFE2, 0x2FF8C24E61619961,
+0xF1457B42B3B3F6B3, 0x15A5423421218421, 0x94D625089C9C4A9C, 0xF0663CEE1E1E781E,
+0x2252866143431143, 0x76FC93B1C7C73BC7, 0xB32BE54FFCFCD7FC, 0x2014082404041004,
+0xB208A2E351515951, 0xBCC72F2599995E99, 0x4FC4DA226D6DA96D, 0x68391A650D0D340D,
+0x8335E979FAFACFFA, 0xB684A369DFDF5BDF, 0xD79BFCA97E7EE57E, 0x3DB4481924249024,
+0xC5D776FE3B3BEC3B, 0x313D4B9AABAB96AB, 0x3ED181F0CECE1FCE, 0x8855229911114411,
+0x0C8903838F8F068F, 0x4A6B9C044E4E254E, 0xD1517366B7B7E6B7, 0x0B60CBE0EBEB8BEB,
+0xFDCC78C13C3CF03C, 0x7CBF1FFD81813E81, 0xD4FE354094946A94, 0xEB0CF31CF7F7FBF7,
+0xA1676F18B9B9DEB9, 0x985F268B13134C13, 0x7D9C58512C2CB02C, 0xD6B8BB05D3D36BD3,
+0x6B5CD38CE7E7BBE7, 0x57CBDC396E6EA56E, 0x6EF395AAC4C437C4, 0x180F061B03030C03,
+0x8A13ACDC56564556, 0x1A49885E44440D44, 0xDF9EFEA07F7FE17F, 0x21374F88A9A99EA9,
+0x4D8254672A2AA82A, 0xB16D6B0ABBBBD6BB, 0x46E29F87C1C123C1, 0xA202A6F153535153,
+0xAE8BA572DCDC57DC, 0x582716530B0B2C0B, 0x9CD327019D9D4E9D, 0x47C1D82B6C6CAD6C,
+0x95F562A43131C431, 0x87B9E8F37474CD74, 0xE309F115F6F6FFF6, 0x0A438C4C46460546,
+0x092645A5ACAC8AAC, 0x3C970FB589891E89, 0xA04428B414145014, 0x5B42DFBAE1E1A3E1,
+0xB04E2CA616165816, 0xCDD274F73A3AE83A, 0x6FD0D2066969B969, 0x482D124109092409,
+0xA7ADE0D77070DD70, 0xD954716FB6B6E2B6, 0xCEB7BD1ED0D067D0, 0x3B7EC7D6EDED93ED,
+0x2EDB85E2CCCC17CC, 0x2A57846842421542, 0xB4C22D2C98985A98, 0x490E55EDA4A4AAA4,
+0x5D8850752828A028, 0xDA31B8865C5C6D5C, 0x933FED6BF8F8C7F8, 0x44A411C286862286 };
+
+alignas(64) const uint64_t Whirlpool::C5[256] = {
+0x18C07830D8181860, 0x2305AF462623238C, 0xC67EF991B8C6C63F, 0xE8136FCDFBE8E887,
+0x874CA113CB878726, 0xB8A9626D11B8B8DA, 0x0108050209010104, 0x4F426E9E0D4F4F21,
+0x36ADEE6C9B3636D8, 0xA6590451FFA6A6A2, 0xD2DEBDB90CD2D26F, 0xF5FB06F70EF5F5F3,
+0x79EF80F2967979F9, 0x6F5FCEDE306F6FA1, 0x91FCEF3F6D91917E, 0x52AA07A4F8525255,
+0x6027FDC04760609D, 0xBC89766535BCBCCA, 0x9BACCD2B379B9B56, 0x8E048C018A8E8E02,
+0xA371155BD2A3A3B6, 0x0C603C186C0C0C30, 0x7BFF8AF6847B7BF1, 0x35B5E16A803535D4,
+0x1DE8693AF51D1D74, 0xE05347DDB3E0E0A7, 0xD7F6ACB321D7D77B, 0xC25EED999CC2C22F,
+0x2E6D965C432E2EB8, 0x4B627A96294B4B31, 0xFEA321E15DFEFEDF, 0x578216AED5575741,
+0x15A8412ABD151554, 0x779FB6EEE87777C1, 0x37A5EB6E923737DC, 0xE57B56D79EE5E5B3,
+0x9F8CD923139F9F46, 0xF0D317FD23F0F0E7, 0x4A6A7F94204A4A35, 0xDA9E95A944DADA4F,
+0x58FA25B0A258587D, 0xC906CA8FCFC9C903, 0x29558D527C2929A4, 0x0A5022145A0A0A28,
+0xB1E14F7F50B1B1FE, 0xA0691A5DC9A0A0BA, 0x6B7FDAD6146B6BB1, 0x855CAB17D985852E,
+0xBD8173673CBDBDCE, 0x5DD234BA8F5D5D69, 0x1080502090101040, 0xF4F303F507F4F4F7,
+0xCB16C08BDDCBCB0B, 0x3EEDC67CD33E3EF8, 0x0528110A2D050514, 0x671FE6CE78676781,
+0xE47353D597E4E4B7, 0x2725BB4E0227279C, 0x4132588273414119, 0x8B2C9D0BA78B8B16,
+0xA7510153F6A7A7A6, 0x7DCF94FAB27D7DE9, 0x95DCFB374995956E, 0xD88E9FAD56D8D847,
+0xFB8B30EB70FBFBCB, 0xEE2371C1CDEEEE9F, 0x7CC791F8BB7C7CED, 0x6617E3CC71666685,
+0xDDA68EA77BDDDD53, 0x17B84B2EAF17175C, 0x4702468E45474701, 0x9E84DC211A9E9E42,
+0xCA1EC589D4CACA0F, 0x2D75995A582D2DB4, 0xBF9179632EBFBFC6, 0x07381B0E3F07071C,
+0xAD012347ACADAD8E, 0x5AEA2FB4B05A5A75, 0x836CB51BEF838336, 0x3385FF66B63333CC,
+0x633FF2C65C636391, 0x02100A0412020208, 0xAA39384993AAAA92, 0x71AFA8E2DE7171D9,
+0xC80ECF8DC6C8C807, 0x19C87D32D1191964, 0x497270923B494939, 0xD9869AAF5FD9D943,
+0xF2C31DF931F2F2EF, 0xE34B48DBA8E3E3AB, 0x5BE22AB6B95B5B71, 0x8834920DBC88881A,
+0x9AA4C8293E9A9A52, 0x262DBE4C0B262698, 0x328DFA64BF3232C8, 0xB0E94A7D59B0B0FA,
+0xE91B6ACFF2E9E983, 0x0F78331E770F0F3C, 0xD5E6A6B733D5D573, 0x8074BA1DF480803A,
+0xBE997C6127BEBEC2, 0xCD26DE87EBCDCD13, 0x34BDE468893434D0, 0x487A75903248483D,
+0xFFAB24E354FFFFDB, 0x7AF78FF48D7A7AF5, 0x90F4EA3D6490907A, 0x5FC23EBE9D5F5F61,
+0x201DA0403D202080, 0x6867D5D00F6868BD, 0x1AD07234CA1A1A68, 0xAE192C41B7AEAE82,
+0xB4C95E757DB4B4EA, 0x549A19A8CE54544D, 0x93ECE53B7F939376, 0x220DAA442F222288,
+0x6407E9C86364648D, 0xF1DB12FF2AF1F1E3, 0x73BFA2E6CC7373D1, 0x12905A2482121248,
+0x403A5D807A40401D, 0x0840281048080820, 0xC356E89B95C3C32B, 0xEC337BC5DFECEC97,
+0xDB9690AB4DDBDB4B, 0xA1611F5FC0A1A1BE, 0x8D1C8307918D8D0E, 0x3DF5C97AC83D3DF4,
+0x97CCF1335B979766, 0x0000000000000000, 0xCF36D483F9CFCF1B, 0x2B4587566E2B2BAC,
+0x7697B3ECE17676C5, 0x8264B019E6828232, 0xD6FEA9B128D6D67F, 0x1BD87736C31B1B6C,
+0xB5C15B7774B5B5EE, 0xAF112943BEAFAF86, 0x6A77DFD41D6A6AB5, 0x50BA0DA0EA50505D,
+0x45124C8A57454509, 0xF3CB18FB38F3F3EB, 0x309DF060AD3030C0, 0xEF2B74C3C4EFEF9B,
+0x3FE5C37EDA3F3FFC, 0x55921CAAC7555549, 0xA2791059DBA2A2B2, 0xEA0365C9E9EAEA8F,
+0x650FECCA6A656589, 0xBAB9686903BABAD2, 0x2F65935E4A2F2FBC, 0xC04EE79D8EC0C027,
+0xDEBE81A160DEDE5F, 0x1CE06C38FC1C1C70, 0xFDBB2EE746FDFDD3, 0x4D52649A1F4D4D29,
+0x92E4E03976929272, 0x758FBCEAFA7575C9, 0x06301E0C36060618, 0x8A249809AE8A8A12,
+0xB2F940794BB2B2F2, 0xE66359D185E6E6BF, 0x0E70361C7E0E0E38, 0x1FF8633EE71F1F7C,
+0x6237F7C455626295, 0xD4EEA3B53AD4D477, 0xA829324D81A8A89A, 0x96C4F43152969662,
+0xF99B3AEF62F9F9C3, 0xC566F697A3C5C533, 0x2535B14A10252594, 0x59F220B2AB595979,
+0x8454AE15D084842A, 0x72B7A7E4C57272D5, 0x39D5DD72EC3939E4, 0x4C5A6198164C4C2D,
+0x5ECA3BBC945E5E65, 0x78E785F09F7878FD, 0x38DDD870E53838E0, 0x8C148605988C8C0A,
+0xD1C6B2BF17D1D163, 0xA5410B57E4A5A5AE, 0xE2434DD9A1E2E2AF, 0x612FF8C24E616199,
+0xB3F1457B42B3B3F6, 0x2115A54234212184, 0x9C94D625089C9C4A, 0x1EF0663CEE1E1E78,
+0x4322528661434311, 0xC776FC93B1C7C73B, 0xFCB32BE54FFCFCD7, 0x0420140824040410,
+0x51B208A2E3515159, 0x99BCC72F2599995E, 0x6D4FC4DA226D6DA9, 0x0D68391A650D0D34,
+0xFA8335E979FAFACF, 0xDFB684A369DFDF5B, 0x7ED79BFCA97E7EE5, 0x243DB44819242490,
+0x3BC5D776FE3B3BEC, 0xAB313D4B9AABAB96, 0xCE3ED181F0CECE1F, 0x1188552299111144,
+0x8F0C8903838F8F06, 0x4E4A6B9C044E4E25, 0xB7D1517366B7B7E6, 0xEB0B60CBE0EBEB8B,
+0x3CFDCC78C13C3CF0, 0x817CBF1FFD81813E, 0x94D4FE354094946A, 0xF7EB0CF31CF7F7FB,
+0xB9A1676F18B9B9DE, 0x13985F268B13134C, 0x2C7D9C58512C2CB0, 0xD3D6B8BB05D3D36B,
+0xE76B5CD38CE7E7BB, 0x6E57CBDC396E6EA5, 0xC46EF395AAC4C437, 0x03180F061B03030C,
+0x568A13ACDC565645, 0x441A49885E44440D, 0x7FDF9EFEA07F7FE1, 0xA921374F88A9A99E,
+0x2A4D8254672A2AA8, 0xBBB16D6B0ABBBBD6, 0xC146E29F87C1C123, 0x53A202A6F1535351,
+0xDCAE8BA572DCDC57, 0x0B582716530B0B2C, 0x9D9CD327019D9D4E, 0x6C47C1D82B6C6CAD,
+0x3195F562A43131C4, 0x7487B9E8F37474CD, 0xF6E309F115F6F6FF, 0x460A438C4C464605,
+0xAC092645A5ACAC8A, 0x893C970FB589891E, 0x14A04428B4141450, 0xE15B42DFBAE1E1A3,
+0x16B04E2CA6161658, 0x3ACDD274F73A3AE8, 0x696FD0D2066969B9, 0x09482D1241090924,
+0x70A7ADE0D77070DD, 0xB6D954716FB6B6E2, 0xD0CEB7BD1ED0D067, 0xED3B7EC7D6EDED93,
+0xCC2EDB85E2CCCC17, 0x422A578468424215, 0x98B4C22D2C98985A, 0xA4490E55EDA4A4AA,
+0x285D8850752828A0, 0x5CDA31B8865C5C6D, 0xF8933FED6BF8F8C7, 0x8644A411C2868622 };
+
+alignas(64) const uint64_t Whirlpool::C6[256] = {
+0x6018C07830D81818, 0x8C2305AF46262323, 0x3FC67EF991B8C6C6, 0x87E8136FCDFBE8E8,
+0x26874CA113CB8787, 0xDAB8A9626D11B8B8, 0x0401080502090101, 0x214F426E9E0D4F4F,
+0xD836ADEE6C9B3636, 0xA2A6590451FFA6A6, 0x6FD2DEBDB90CD2D2, 0xF3F5FB06F70EF5F5,
+0xF979EF80F2967979, 0xA16F5FCEDE306F6F, 0x7E91FCEF3F6D9191, 0x5552AA07A4F85252,
+0x9D6027FDC0476060, 0xCABC89766535BCBC, 0x569BACCD2B379B9B, 0x028E048C018A8E8E,
+0xB6A371155BD2A3A3, 0x300C603C186C0C0C, 0xF17BFF8AF6847B7B, 0xD435B5E16A803535,
+0x741DE8693AF51D1D, 0xA7E05347DDB3E0E0, 0x7BD7F6ACB321D7D7, 0x2FC25EED999CC2C2,
+0xB82E6D965C432E2E, 0x314B627A96294B4B, 0xDFFEA321E15DFEFE, 0x41578216AED55757,
+0x5415A8412ABD1515, 0xC1779FB6EEE87777, 0xDC37A5EB6E923737, 0xB3E57B56D79EE5E5,
+0x469F8CD923139F9F, 0xE7F0D317FD23F0F0, 0x354A6A7F94204A4A, 0x4FDA9E95A944DADA,
+0x7D58FA25B0A25858, 0x03C906CA8FCFC9C9, 0xA429558D527C2929, 0x280A5022145A0A0A,
+0xFEB1E14F7F50B1B1, 0xBAA0691A5DC9A0A0, 0xB16B7FDAD6146B6B, 0x2E855CAB17D98585,
+0xCEBD8173673CBDBD, 0x695DD234BA8F5D5D, 0x4010805020901010, 0xF7F4F303F507F4F4,
+0x0BCB16C08BDDCBCB, 0xF83EEDC67CD33E3E, 0x140528110A2D0505, 0x81671FE6CE786767,
+0xB7E47353D597E4E4, 0x9C2725BB4E022727, 0x1941325882734141, 0x168B2C9D0BA78B8B,
+0xA6A7510153F6A7A7, 0xE97DCF94FAB27D7D, 0x6E95DCFB37499595, 0x47D88E9FAD56D8D8,
+0xCBFB8B30EB70FBFB, 0x9FEE2371C1CDEEEE, 0xED7CC791F8BB7C7C, 0x856617E3CC716666,
+0x53DDA68EA77BDDDD, 0x5C17B84B2EAF1717, 0x014702468E454747, 0x429E84DC211A9E9E,
+0x0FCA1EC589D4CACA, 0xB42D75995A582D2D, 0xC6BF9179632EBFBF, 0x1C07381B0E3F0707,
+0x8EAD012347ACADAD, 0x755AEA2FB4B05A5A, 0x36836CB51BEF8383, 0xCC3385FF66B63333,
+0x91633FF2C65C6363, 0x0802100A04120202, 0x92AA39384993AAAA, 0xD971AFA8E2DE7171,
+0x07C80ECF8DC6C8C8, 0x6419C87D32D11919, 0x39497270923B4949, 0x43D9869AAF5FD9D9,
+0xEFF2C31DF931F2F2, 0xABE34B48DBA8E3E3, 0x715BE22AB6B95B5B, 0x1A8834920DBC8888,
+0x529AA4C8293E9A9A, 0x98262DBE4C0B2626, 0xC8328DFA64BF3232, 0xFAB0E94A7D59B0B0,
+0x83E91B6ACFF2E9E9, 0x3C0F78331E770F0F, 0x73D5E6A6B733D5D5, 0x3A8074BA1DF48080,
+0xC2BE997C6127BEBE, 0x13CD26DE87EBCDCD, 0xD034BDE468893434, 0x3D487A7590324848,
+0xDBFFAB24E354FFFF, 0xF57AF78FF48D7A7A, 0x7A90F4EA3D649090, 0x615FC23EBE9D5F5F,
+0x80201DA0403D2020, 0xBD6867D5D00F6868, 0x681AD07234CA1A1A, 0x82AE192C41B7AEAE,
+0xEAB4C95E757DB4B4, 0x4D549A19A8CE5454, 0x7693ECE53B7F9393, 0x88220DAA442F2222,
+0x8D6407E9C8636464, 0xE3F1DB12FF2AF1F1, 0xD173BFA2E6CC7373, 0x4812905A24821212,
+0x1D403A5D807A4040, 0x2008402810480808, 0x2BC356E89B95C3C3, 0x97EC337BC5DFECEC,
+0x4BDB9690AB4DDBDB, 0xBEA1611F5FC0A1A1, 0x0E8D1C8307918D8D, 0xF43DF5C97AC83D3D,
+0x6697CCF1335B9797, 0x0000000000000000, 0x1BCF36D483F9CFCF, 0xAC2B4587566E2B2B,
+0xC57697B3ECE17676, 0x328264B019E68282, 0x7FD6FEA9B128D6D6, 0x6C1BD87736C31B1B,
+0xEEB5C15B7774B5B5, 0x86AF112943BEAFAF, 0xB56A77DFD41D6A6A, 0x5D50BA0DA0EA5050,
+0x0945124C8A574545, 0xEBF3CB18FB38F3F3, 0xC0309DF060AD3030, 0x9BEF2B74C3C4EFEF,
+0xFC3FE5C37EDA3F3F, 0x4955921CAAC75555, 0xB2A2791059DBA2A2, 0x8FEA0365C9E9EAEA,
+0x89650FECCA6A6565, 0xD2BAB9686903BABA, 0xBC2F65935E4A2F2F, 0x27C04EE79D8EC0C0,
+0x5FDEBE81A160DEDE, 0x701CE06C38FC1C1C, 0xD3FDBB2EE746FDFD, 0x294D52649A1F4D4D,
+0x7292E4E039769292, 0xC9758FBCEAFA7575, 0x1806301E0C360606, 0x128A249809AE8A8A,
+0xF2B2F940794BB2B2, 0xBFE66359D185E6E6, 0x380E70361C7E0E0E, 0x7C1FF8633EE71F1F,
+0x956237F7C4556262, 0x77D4EEA3B53AD4D4, 0x9AA829324D81A8A8, 0x6296C4F431529696,
+0xC3F99B3AEF62F9F9, 0x33C566F697A3C5C5, 0x942535B14A102525, 0x7959F220B2AB5959,
+0x2A8454AE15D08484, 0xD572B7A7E4C57272, 0xE439D5DD72EC3939, 0x2D4C5A6198164C4C,
+0x655ECA3BBC945E5E, 0xFD78E785F09F7878, 0xE038DDD870E53838, 0x0A8C148605988C8C,
+0x63D1C6B2BF17D1D1, 0xAEA5410B57E4A5A5, 0xAFE2434DD9A1E2E2, 0x99612FF8C24E6161,
+0xF6B3F1457B42B3B3, 0x842115A542342121, 0x4A9C94D625089C9C, 0x781EF0663CEE1E1E,
+0x1143225286614343, 0x3BC776FC93B1C7C7, 0xD7FCB32BE54FFCFC, 0x1004201408240404,
+0x5951B208A2E35151, 0x5E99BCC72F259999, 0xA96D4FC4DA226D6D, 0x340D68391A650D0D,
+0xCFFA8335E979FAFA, 0x5BDFB684A369DFDF, 0xE57ED79BFCA97E7E, 0x90243DB448192424,
+0xEC3BC5D776FE3B3B, 0x96AB313D4B9AABAB, 0x1FCE3ED181F0CECE, 0x4411885522991111,
+0x068F0C8903838F8F, 0x254E4A6B9C044E4E, 0xE6B7D1517366B7B7, 0x8BEB0B60CBE0EBEB,
+0xF03CFDCC78C13C3C, 0x3E817CBF1FFD8181, 0x6A94D4FE35409494, 0xFBF7EB0CF31CF7F7,
+0xDEB9A1676F18B9B9, 0x4C13985F268B1313, 0xB02C7D9C58512C2C, 0x6BD3D6B8BB05D3D3,
+0xBBE76B5CD38CE7E7, 0xA56E57CBDC396E6E, 0x37C46EF395AAC4C4, 0x0C03180F061B0303,
+0x45568A13ACDC5656, 0x0D441A49885E4444, 0xE17FDF9EFEA07F7F, 0x9EA921374F88A9A9,
+0xA82A4D8254672A2A, 0xD6BBB16D6B0ABBBB, 0x23C146E29F87C1C1, 0x5153A202A6F15353,
+0x57DCAE8BA572DCDC, 0x2C0B582716530B0B, 0x4E9D9CD327019D9D, 0xAD6C47C1D82B6C6C,
+0xC43195F562A43131, 0xCD7487B9E8F37474, 0xFFF6E309F115F6F6, 0x05460A438C4C4646,
+0x8AAC092645A5ACAC, 0x1E893C970FB58989, 0x5014A04428B41414, 0xA3E15B42DFBAE1E1,
+0x5816B04E2CA61616, 0xE83ACDD274F73A3A, 0xB9696FD0D2066969, 0x2409482D12410909,
+0xDD70A7ADE0D77070, 0xE2B6D954716FB6B6, 0x67D0CEB7BD1ED0D0, 0x93ED3B7EC7D6EDED,
+0x17CC2EDB85E2CCCC, 0x15422A5784684242, 0x5A98B4C22D2C9898, 0xAAA4490E55EDA4A4,
+0xA0285D8850752828, 0x6D5CDA31B8865C5C, 0xC7F8933FED6BF8F8, 0x228644A411C28686 };
+
+alignas(64) const uint64_t Whirlpool::C7[256] = {
+0x186018C07830D818, 0x238C2305AF462623, 0xC63FC67EF991B8C6, 0xE887E8136FCDFBE8,
+0x8726874CA113CB87, 0xB8DAB8A9626D11B8, 0x0104010805020901, 0x4F214F426E9E0D4F,
+0x36D836ADEE6C9B36, 0xA6A2A6590451FFA6, 0xD26FD2DEBDB90CD2, 0xF5F3F5FB06F70EF5,
+0x79F979EF80F29679, 0x6FA16F5FCEDE306F, 0x917E91FCEF3F6D91, 0x525552AA07A4F852,
+0x609D6027FDC04760, 0xBCCABC89766535BC, 0x9B569BACCD2B379B, 0x8E028E048C018A8E,
+0xA3B6A371155BD2A3, 0x0C300C603C186C0C, 0x7BF17BFF8AF6847B, 0x35D435B5E16A8035,
+0x1D741DE8693AF51D, 0xE0A7E05347DDB3E0, 0xD77BD7F6ACB321D7, 0xC22FC25EED999CC2,
+0x2EB82E6D965C432E, 0x4B314B627A96294B, 0xFEDFFEA321E15DFE, 0x5741578216AED557,
+0x155415A8412ABD15, 0x77C1779FB6EEE877, 0x37DC37A5EB6E9237, 0xE5B3E57B56D79EE5,
+0x9F469F8CD923139F, 0xF0E7F0D317FD23F0, 0x4A354A6A7F94204A, 0xDA4FDA9E95A944DA,
+0x587D58FA25B0A258, 0xC903C906CA8FCFC9, 0x29A429558D527C29, 0x0A280A5022145A0A,
+0xB1FEB1E14F7F50B1, 0xA0BAA0691A5DC9A0, 0x6BB16B7FDAD6146B, 0x852E855CAB17D985,
+0xBDCEBD8173673CBD, 0x5D695DD234BA8F5D, 0x1040108050209010, 0xF4F7F4F303F507F4,
+0xCB0BCB16C08BDDCB, 0x3EF83EEDC67CD33E, 0x05140528110A2D05, 0x6781671FE6CE7867,
+0xE4B7E47353D597E4, 0x279C2725BB4E0227, 0x4119413258827341, 0x8B168B2C9D0BA78B,
+0xA7A6A7510153F6A7, 0x7DE97DCF94FAB27D, 0x956E95DCFB374995, 0xD847D88E9FAD56D8,
+0xFBCBFB8B30EB70FB, 0xEE9FEE2371C1CDEE, 0x7CED7CC791F8BB7C, 0x66856617E3CC7166,
+0xDD53DDA68EA77BDD, 0x175C17B84B2EAF17, 0x47014702468E4547, 0x9E429E84DC211A9E,
+0xCA0FCA1EC589D4CA, 0x2DB42D75995A582D, 0xBFC6BF9179632EBF, 0x071C07381B0E3F07,
+0xAD8EAD012347ACAD, 0x5A755AEA2FB4B05A, 0x8336836CB51BEF83, 0x33CC3385FF66B633,
+0x6391633FF2C65C63, 0x020802100A041202, 0xAA92AA39384993AA, 0x71D971AFA8E2DE71,
+0xC807C80ECF8DC6C8, 0x196419C87D32D119, 0x4939497270923B49, 0xD943D9869AAF5FD9,
+0xF2EFF2C31DF931F2, 0xE3ABE34B48DBA8E3, 0x5B715BE22AB6B95B, 0x881A8834920DBC88,
+0x9A529AA4C8293E9A, 0x2698262DBE4C0B26, 0x32C8328DFA64BF32, 0xB0FAB0E94A7D59B0,
+0xE983E91B6ACFF2E9, 0x0F3C0F78331E770F, 0xD573D5E6A6B733D5, 0x803A8074BA1DF480,
+0xBEC2BE997C6127BE, 0xCD13CD26DE87EBCD, 0x34D034BDE4688934, 0x483D487A75903248,
+0xFFDBFFAB24E354FF, 0x7AF57AF78FF48D7A, 0x907A90F4EA3D6490, 0x5F615FC23EBE9D5F,
+0x2080201DA0403D20, 0x68BD6867D5D00F68, 0x1A681AD07234CA1A, 0xAE82AE192C41B7AE,
+0xB4EAB4C95E757DB4, 0x544D549A19A8CE54, 0x937693ECE53B7F93, 0x2288220DAA442F22,
+0x648D6407E9C86364, 0xF1E3F1DB12FF2AF1, 0x73D173BFA2E6CC73, 0x124812905A248212,
+0x401D403A5D807A40, 0x0820084028104808, 0xC32BC356E89B95C3, 0xEC97EC337BC5DFEC,
+0xDB4BDB9690AB4DDB, 0xA1BEA1611F5FC0A1, 0x8D0E8D1C8307918D, 0x3DF43DF5C97AC83D,
+0x976697CCF1335B97, 0x0000000000000000, 0xCF1BCF36D483F9CF, 0x2BAC2B4587566E2B,
+0x76C57697B3ECE176, 0x82328264B019E682, 0xD67FD6FEA9B128D6, 0x1B6C1BD87736C31B,
+0xB5EEB5C15B7774B5, 0xAF86AF112943BEAF, 0x6AB56A77DFD41D6A, 0x505D50BA0DA0EA50,
+0x450945124C8A5745, 0xF3EBF3CB18FB38F3, 0x30C0309DF060AD30, 0xEF9BEF2B74C3C4EF,
+0x3FFC3FE5C37EDA3F, 0x554955921CAAC755, 0xA2B2A2791059DBA2, 0xEA8FEA0365C9E9EA,
+0x6589650FECCA6A65, 0xBAD2BAB9686903BA, 0x2FBC2F65935E4A2F, 0xC027C04EE79D8EC0,
+0xDE5FDEBE81A160DE, 0x1C701CE06C38FC1C, 0xFDD3FDBB2EE746FD, 0x4D294D52649A1F4D,
+0x927292E4E0397692, 0x75C9758FBCEAFA75, 0x061806301E0C3606, 0x8A128A249809AE8A,
+0xB2F2B2F940794BB2, 0xE6BFE66359D185E6, 0x0E380E70361C7E0E, 0x1F7C1FF8633EE71F,
+0x62956237F7C45562, 0xD477D4EEA3B53AD4, 0xA89AA829324D81A8, 0x966296C4F4315296,
+0xF9C3F99B3AEF62F9, 0xC533C566F697A3C5, 0x25942535B14A1025, 0x597959F220B2AB59,
+0x842A8454AE15D084, 0x72D572B7A7E4C572, 0x39E439D5DD72EC39, 0x4C2D4C5A6198164C,
+0x5E655ECA3BBC945E, 0x78FD78E785F09F78, 0x38E038DDD870E538, 0x8C0A8C148605988C,
+0xD163D1C6B2BF17D1, 0xA5AEA5410B57E4A5, 0xE2AFE2434DD9A1E2, 0x6199612FF8C24E61,
+0xB3F6B3F1457B42B3, 0x21842115A5423421, 0x9C4A9C94D625089C, 0x1E781EF0663CEE1E,
+0x4311432252866143, 0xC73BC776FC93B1C7, 0xFCD7FCB32BE54FFC, 0x0410042014082404,
+0x515951B208A2E351, 0x995E99BCC72F2599, 0x6DA96D4FC4DA226D, 0x0D340D68391A650D,
+0xFACFFA8335E979FA, 0xDF5BDFB684A369DF, 0x7EE57ED79BFCA97E, 0x2490243DB4481924,
+0x3BEC3BC5D776FE3B, 0xAB96AB313D4B9AAB, 0xCE1FCE3ED181F0CE, 0x1144118855229911,
+0x8F068F0C8903838F, 0x4E254E4A6B9C044E, 0xB7E6B7D1517366B7, 0xEB8BEB0B60CBE0EB,
+0x3CF03CFDCC78C13C, 0x813E817CBF1FFD81, 0x946A94D4FE354094, 0xF7FBF7EB0CF31CF7,
+0xB9DEB9A1676F18B9, 0x134C13985F268B13, 0x2CB02C7D9C58512C, 0xD36BD3D6B8BB05D3,
+0xE7BBE76B5CD38CE7, 0x6EA56E57CBDC396E, 0xC437C46EF395AAC4, 0x030C03180F061B03,
+0x5645568A13ACDC56, 0x440D441A49885E44, 0x7FE17FDF9EFEA07F, 0xA99EA921374F88A9,
+0x2AA82A4D8254672A, 0xBBD6BBB16D6B0ABB, 0xC123C146E29F87C1, 0x535153A202A6F153,
+0xDC57DCAE8BA572DC, 0x0B2C0B582716530B, 0x9D4E9D9CD327019D, 0x6CAD6C47C1D82B6C,
+0x31C43195F562A431, 0x74CD7487B9E8F374, 0xF6FFF6E309F115F6, 0x4605460A438C4C46,
+0xAC8AAC092645A5AC, 0x891E893C970FB589, 0x145014A04428B414, 0xE1A3E15B42DFBAE1,
+0x165816B04E2CA616, 0x3AE83ACDD274F73A, 0x69B9696FD0D20669, 0x092409482D124109,
+0x70DD70A7ADE0D770, 0xB6E2B6D954716FB6, 0xD067D0CEB7BD1ED0, 0xED93ED3B7EC7D6ED,
+0xCC17CC2EDB85E2CC, 0x4215422A57846842, 0x985A98B4C22D2C98, 0xA4AAA4490E55EDA4,
+0x28A0285D88507528, 0x5C6D5CDA31B8865C, 0xF8C7F8933FED6BF8, 0x86228644A411C286 };
+
+}
diff --git a/comm/third_party/botan/src/lib/hash/whirlpool/whrlpool.h b/comm/third_party/botan/src/lib/hash/whirlpool/whrlpool.h
new file mode 100644
index 0000000000..26ee7f775d
--- /dev/null
+++ b/comm/third_party/botan/src/lib/hash/whirlpool/whrlpool.h
@@ -0,0 +1,50 @@
+/*
+* Whirlpool
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_WHIRLPOOL_H_
+#define BOTAN_WHIRLPOOL_H_
+
+#include <botan/mdx_hash.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(whrlpool.h)
+
+namespace Botan {
+
+/**
+* Whirlpool
+*/
+class BOTAN_PUBLIC_API(2,0) Whirlpool final : public MDx_HashFunction
+ {
+ public:
+ std::string name() const override { return "Whirlpool"; }
+ size_t output_length() const override { return 64; }
+ HashFunction* clone() const override { return new Whirlpool; }
+ std::unique_ptr<HashFunction> copy_state() const override;
+
+ void clear() override;
+
+ Whirlpool() : MDx_HashFunction(64, true, true, 32), m_M(8), m_digest(8)
+ { clear(); }
+ private:
+ void compress_n(const uint8_t[], size_t blocks) override;
+ void copy_out(uint8_t[]) override;
+
+ static const uint64_t C0[256];
+ static const uint64_t C1[256];
+ static const uint64_t C2[256];
+ static const uint64_t C3[256];
+ static const uint64_t C4[256];
+ static const uint64_t C5[256];
+ static const uint64_t C6[256];
+ static const uint64_t C7[256];
+
+ secure_vector<uint64_t> m_M, m_digest;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/kdf/hkdf/hkdf.cpp b/comm/third_party/botan/src/lib/kdf/hkdf/hkdf.cpp
new file mode 100644
index 0000000000..0a62648fcd
--- /dev/null
+++ b/comm/third_party/botan/src/lib/kdf/hkdf/hkdf.cpp
@@ -0,0 +1,122 @@
+/*
+* HKDF
+* (C) 2013,2015,2017 Jack Lloyd
+* (C) 2016 René Korthaus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/hkdf.h>
+#include <botan/loadstor.h>
+
+namespace Botan {
+
+size_t HKDF::kdf(uint8_t key[], size_t key_len,
+ const uint8_t secret[], size_t secret_len,
+ const uint8_t salt[], size_t salt_len,
+ const uint8_t label[], size_t label_len) const
+ {
+ HKDF_Extract extract(m_prf->clone());
+ HKDF_Expand expand(m_prf->clone());
+ secure_vector<uint8_t> prk(m_prf->output_length());
+
+ extract.kdf(prk.data(), prk.size(), secret, secret_len, salt, salt_len, nullptr, 0);
+ return expand.kdf(key, key_len, prk.data(), prk.size(), nullptr, 0, label, label_len);
+ }
+
+size_t HKDF_Extract::kdf(uint8_t key[], size_t key_len,
+ const uint8_t secret[], size_t secret_len,
+ const uint8_t salt[], size_t salt_len,
+ const uint8_t[], size_t) const
+ {
+ secure_vector<uint8_t> prk;
+ if(salt_len == 0)
+ {
+ m_prf->set_key(std::vector<uint8_t>(m_prf->output_length()));
+ }
+ else
+ {
+ m_prf->set_key(salt, salt_len);
+ }
+
+ m_prf->update(secret, secret_len);
+ m_prf->final(prk);
+
+ const size_t written = std::min(prk.size(), key_len);
+ copy_mem(&key[0], prk.data(), written);
+ // FIXME: returns truncated output
+ return written;
+ }
+
+size_t HKDF_Expand::kdf(uint8_t key[], size_t key_len,
+ const uint8_t secret[], size_t secret_len,
+ const uint8_t salt[], size_t salt_len,
+ const uint8_t label[], size_t label_len) const
+ {
+ m_prf->set_key(secret, secret_len);
+
+ uint8_t counter = 1;
+ secure_vector<uint8_t> h;
+ size_t offset = 0;
+
+ while(offset != key_len && counter != 0)
+ {
+ m_prf->update(h);
+ m_prf->update(label, label_len);
+ m_prf->update(salt, salt_len);
+ m_prf->update(counter++);
+ m_prf->final(h);
+
+ const size_t written = std::min(h.size(), key_len - offset);
+ copy_mem(&key[offset], h.data(), written);
+ offset += written;
+ }
+
+ // FIXME: returns truncated output
+ return offset;
+ }
+
+secure_vector<uint8_t>
+hkdf_expand_label(const std::string& hash_fn,
+ const uint8_t secret[], size_t secret_len,
+ const std::string& label,
+ const uint8_t hash_val[], size_t hash_val_len,
+ size_t length)
+ {
+ BOTAN_ARG_CHECK(length <= 0xFFFF, "HKDF-Expand-Label requested output too large");
+ BOTAN_ARG_CHECK(label.size() <= 0xFF, "HKDF-Expand-Label label too long");
+ BOTAN_ARG_CHECK(hash_val_len <= 0xFF, "HKDF-Expand-Label hash too long");
+
+ const uint16_t length16 = static_cast<uint16_t>(length);
+
+ auto mac = MessageAuthenticationCode::create_or_throw("HMAC(" + hash_fn + ")");
+
+ HKDF_Expand hkdf(mac.release());
+
+ secure_vector<uint8_t> output(length16);
+ std::vector<uint8_t> prefix(3 + label.size() + 1);
+
+ prefix[0] = get_byte(0, length16);
+ prefix[1] = get_byte(1, length16);
+ prefix[2] = static_cast<uint8_t>(label.size());
+
+ copy_mem(prefix.data() + 3,
+ cast_char_ptr_to_uint8(label.data()),
+ label.size());
+
+ prefix[3 + label.size()] = static_cast<uint8_t>(hash_val_len);
+
+ /*
+ * We do something a little dirty here to avoid copying the hash_val,
+ * making use of the fact that Botan's KDF interface supports label+salt,
+ * and knowing that our HKDF hashes first param label then param salt.
+ */
+ hkdf.kdf(output.data(), output.size(),
+ secret, secret_len,
+ hash_val, hash_val_len,
+ prefix.data(), prefix.size());
+
+ return output;
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/kdf/hkdf/hkdf.h b/comm/third_party/botan/src/lib/kdf/hkdf/hkdf.h
new file mode 100644
index 0000000000..4b1ed2922c
--- /dev/null
+++ b/comm/third_party/botan/src/lib/kdf/hkdf/hkdf.h
@@ -0,0 +1,117 @@
+/*
+* HKDF
+* (C) 2013,2015 Jack Lloyd
+* (C) 2016 René Korthaus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_HKDF_H_
+#define BOTAN_HKDF_H_
+
+#include <botan/mac.h>
+#include <botan/kdf.h>
+
+/*
+* The definitions of HKDF, HKDF_Extract, HKDF_Expand will be made internal
+* in the future. However the function hkdf_expand_label will still be defined.
+*/
+//BOTAN_FUTURE_INTERNAL_HEADER(hkdf.h)
+
+namespace Botan {
+
+/**
+* HKDF from RFC 5869.
+*/
+class BOTAN_PUBLIC_API(2,0) HKDF final : public KDF
+ {
+ public:
+ /**
+ * @param prf MAC algorithm to use
+ */
+ explicit HKDF(MessageAuthenticationCode* prf) : m_prf(prf) {}
+
+ KDF* clone() const override { return new HKDF(m_prf->clone()); }
+
+ std::string name() const override { return "HKDF(" + m_prf->name() + ")"; }
+
+ size_t kdf(uint8_t key[], size_t key_len,
+ const uint8_t secret[], size_t secret_len,
+ const uint8_t salt[], size_t salt_len,
+ const uint8_t label[], size_t label_len) const override;
+
+ private:
+ std::unique_ptr<MessageAuthenticationCode> m_prf;
+ };
+
+/**
+* HKDF Extraction Step from RFC 5869.
+*/
+class BOTAN_PUBLIC_API(2,0) HKDF_Extract final : public KDF
+ {
+ public:
+ /**
+ * @param prf MAC algorithm to use
+ */
+ explicit HKDF_Extract(MessageAuthenticationCode* prf) : m_prf(prf) {}
+
+ KDF* clone() const override { return new HKDF_Extract(m_prf->clone()); }
+
+ std::string name() const override { return "HKDF-Extract(" + m_prf->name() + ")"; }
+
+ size_t kdf(uint8_t key[], size_t key_len,
+ const uint8_t secret[], size_t secret_len,
+ const uint8_t salt[], size_t salt_len,
+ const uint8_t label[], size_t label_len) const override;
+
+ private:
+ std::unique_ptr<MessageAuthenticationCode> m_prf;
+ };
+
+/**
+* HKDF Expansion Step from RFC 5869.
+*/
+class BOTAN_PUBLIC_API(2,0) HKDF_Expand final : public KDF
+ {
+ public:
+ /**
+ * @param prf MAC algorithm to use
+ */
+ explicit HKDF_Expand(MessageAuthenticationCode* prf) : m_prf(prf) {}
+
+ KDF* clone() const override { return new HKDF_Expand(m_prf->clone()); }
+
+ std::string name() const override { return "HKDF-Expand(" + m_prf->name() + ")"; }
+
+ size_t kdf(uint8_t key[], size_t key_len,
+ const uint8_t secret[], size_t secret_len,
+ const uint8_t salt[], size_t salt_len,
+ const uint8_t label[], size_t label_len) const override;
+
+ private:
+ std::unique_ptr<MessageAuthenticationCode> m_prf;
+ };
+
+/**
+* HKDF-Expand-Label from TLS 1.3/QUIC
+* @param hash_fn the hash to use
+* @param secret the secret bits
+* @param secret_len the length of secret
+* @param label the full label (no "TLS 1.3, " or "tls13 " prefix
+* is applied)
+* @param hash_val the previous hash value (used for chaining, may be empty)
+* @param hash_val_len the length of hash_val
+* @param length the desired output length
+*/
+secure_vector<uint8_t>
+BOTAN_PUBLIC_API(2,3) hkdf_expand_label(
+ const std::string& hash_fn,
+ const uint8_t secret[], size_t secret_len,
+ const std::string& label,
+ const uint8_t hash_val[], size_t hash_val_len,
+ size_t length);
+
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/kdf/hkdf/info.txt b/comm/third_party/botan/src/lib/kdf/hkdf/info.txt
new file mode 100644
index 0000000000..9cbd420604
--- /dev/null
+++ b/comm/third_party/botan/src/lib/kdf/hkdf/info.txt
@@ -0,0 +1,7 @@
+<defines>
+HKDF -> 20170927
+</defines>
+
+<requires>
+hmac
+</requires>
diff --git a/comm/third_party/botan/src/lib/kdf/info.txt b/comm/third_party/botan/src/lib/kdf/info.txt
new file mode 100644
index 0000000000..81567300f5
--- /dev/null
+++ b/comm/third_party/botan/src/lib/kdf/info.txt
@@ -0,0 +1,12 @@
+<defines>
+KDF_BASE -> 20131128
+</defines>
+
+<requires>
+mac
+hash
+</requires>
+
+<header:public>
+kdf.h
+</header:public>
diff --git a/comm/third_party/botan/src/lib/kdf/kdf.cpp b/comm/third_party/botan/src/lib/kdf/kdf.cpp
new file mode 100644
index 0000000000..7f7d352dbf
--- /dev/null
+++ b/comm/third_party/botan/src/lib/kdf/kdf.cpp
@@ -0,0 +1,255 @@
+/*
+* KDF Retrieval
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/kdf.h>
+#include <botan/mac.h>
+#include <botan/hash.h>
+#include <botan/scan_name.h>
+#include <botan/exceptn.h>
+
+#if defined(BOTAN_HAS_HKDF)
+#include <botan/hkdf.h>
+#endif
+
+#if defined(BOTAN_HAS_KDF1)
+#include <botan/kdf1.h>
+#endif
+
+#if defined(BOTAN_HAS_KDF2)
+#include <botan/kdf2.h>
+#endif
+
+#if defined(BOTAN_HAS_KDF1_18033)
+#include <botan/kdf1_iso18033.h>
+#endif
+
+#if defined(BOTAN_HAS_TLS_V10_PRF) || defined(BOTAN_HAS_TLS_V12_PRF)
+#include <botan/prf_tls.h>
+#endif
+
+#if defined(BOTAN_HAS_X942_PRF)
+#include <botan/prf_x942.h>
+#endif
+
+#if defined(BOTAN_HAS_SP800_108)
+#include <botan/sp800_108.h>
+#endif
+
+#if defined(BOTAN_HAS_SP800_56A)
+#include <botan/sp800_56a.h>
+#endif
+
+#if defined(BOTAN_HAS_SP800_56C)
+#include <botan/sp800_56c.h>
+#endif
+
+namespace Botan {
+
+namespace {
+
+template<typename KDF_Type>
+std::unique_ptr<KDF>
+kdf_create_mac_or_hash(const std::string& nm)
+ {
+ if(auto mac = MessageAuthenticationCode::create(nm))
+ return std::unique_ptr<KDF>(new KDF_Type(mac.release()));
+
+ if(auto mac = MessageAuthenticationCode::create("HMAC(" + nm + ")"))
+ return std::unique_ptr<KDF>(new KDF_Type(mac.release()));
+
+ return nullptr;
+ }
+
+}
+
+std::unique_ptr<KDF> KDF::create(const std::string& algo_spec,
+ const std::string& provider)
+ {
+ const SCAN_Name req(algo_spec);
+
+#if defined(BOTAN_HAS_HKDF)
+ if(req.algo_name() == "HKDF" && req.arg_count() == 1)
+ {
+ if(provider.empty() || provider == "base")
+ {
+ return kdf_create_mac_or_hash<HKDF>(req.arg(0));
+ }
+ }
+
+ if(req.algo_name() == "HKDF-Extract" && req.arg_count() == 1)
+ {
+ if(provider.empty() || provider == "base")
+ {
+ return kdf_create_mac_or_hash<HKDF_Extract>(req.arg(0));
+ }
+ }
+
+ if(req.algo_name() == "HKDF-Expand" && req.arg_count() == 1)
+ {
+ if(provider.empty() || provider == "base")
+ {
+ return kdf_create_mac_or_hash<HKDF_Expand>(req.arg(0));
+ }
+ }
+#endif
+
+#if defined(BOTAN_HAS_KDF2)
+ if(req.algo_name() == "KDF2" && req.arg_count() == 1)
+ {
+ if(provider.empty() || provider == "base")
+ {
+ if(auto hash = HashFunction::create(req.arg(0)))
+ return std::unique_ptr<KDF>(new KDF2(hash.release()));
+ }
+ }
+#endif
+
+#if defined(BOTAN_HAS_KDF1_18033)
+ if(req.algo_name() == "KDF1-18033" && req.arg_count() == 1)
+ {
+ if(provider.empty() || provider == "base")
+ {
+ if(auto hash = HashFunction::create(req.arg(0)))
+ return std::unique_ptr<KDF>(new KDF1_18033(hash.release()));
+ }
+ }
+#endif
+
+#if defined(BOTAN_HAS_KDF1)
+ if(req.algo_name() == "KDF1" && req.arg_count() == 1)
+ {
+ if(provider.empty() || provider == "base")
+ {
+ if(auto hash = HashFunction::create(req.arg(0)))
+ return std::unique_ptr<KDF>(new KDF1(hash.release()));
+ }
+ }
+#endif
+
+#if defined(BOTAN_HAS_TLS_V10_PRF)
+ if(req.algo_name() == "TLS-PRF" && req.arg_count() == 0)
+ {
+ if(provider.empty() || provider == "base")
+ {
+ auto hmac_md5 = MessageAuthenticationCode::create("HMAC(MD5)");
+ auto hmac_sha1 = MessageAuthenticationCode::create("HMAC(SHA-1)");
+
+ if(hmac_md5 && hmac_sha1)
+ return std::unique_ptr<KDF>(new TLS_PRF(std::move(hmac_md5), std::move(hmac_sha1)));
+ }
+ }
+#endif
+
+#if defined(BOTAN_HAS_TLS_V12_PRF)
+ if(req.algo_name() == "TLS-12-PRF" && req.arg_count() == 1)
+ {
+ if(provider.empty() || provider == "base")
+ {
+ return kdf_create_mac_or_hash<TLS_12_PRF>(req.arg(0));
+ }
+ }
+#endif
+
+#if defined(BOTAN_HAS_X942_PRF)
+ if(req.algo_name() == "X9.42-PRF" && req.arg_count() == 1)
+ {
+ if(provider.empty() || provider == "base")
+ {
+ return std::unique_ptr<KDF>(new X942_PRF(req.arg(0)));
+ }
+ }
+#endif
+
+#if defined(BOTAN_HAS_SP800_108)
+ if(req.algo_name() == "SP800-108-Counter" && req.arg_count() == 1)
+ {
+ if(provider.empty() || provider == "base")
+ {
+ return kdf_create_mac_or_hash<SP800_108_Counter>(req.arg(0));
+ }
+ }
+
+ if(req.algo_name() == "SP800-108-Feedback" && req.arg_count() == 1)
+ {
+ if(provider.empty() || provider == "base")
+ {
+ return kdf_create_mac_or_hash<SP800_108_Feedback>(req.arg(0));
+ }
+ }
+
+ if(req.algo_name() == "SP800-108-Pipeline" && req.arg_count() == 1)
+ {
+ if(provider.empty() || provider == "base")
+ {
+ return kdf_create_mac_or_hash<SP800_108_Pipeline>(req.arg(0));
+ }
+ }
+#endif
+
+#if defined(BOTAN_HAS_SP800_56A)
+ if(req.algo_name() == "SP800-56A" && req.arg_count() == 1)
+ {
+ if(auto hash = HashFunction::create(req.arg(0)))
+ return std::unique_ptr<KDF>(new SP800_56A_Hash(hash.release()));
+ if(auto mac = MessageAuthenticationCode::create(req.arg(0)))
+ return std::unique_ptr<KDF>(new SP800_56A_HMAC(mac.release()));
+ }
+#endif
+
+#if defined(BOTAN_HAS_SP800_56C)
+ if(req.algo_name() == "SP800-56C" && req.arg_count() == 1)
+ {
+ std::unique_ptr<KDF> exp(kdf_create_mac_or_hash<SP800_108_Feedback>(req.arg(0)));
+ if(exp)
+ {
+ if(auto mac = MessageAuthenticationCode::create(req.arg(0)))
+ return std::unique_ptr<KDF>(new SP800_56C(mac.release(), exp.release()));
+
+ if(auto mac = MessageAuthenticationCode::create("HMAC(" + req.arg(0) + ")"))
+ return std::unique_ptr<KDF>(new SP800_56C(mac.release(), exp.release()));
+ }
+ }
+#endif
+
+ BOTAN_UNUSED(req);
+ BOTAN_UNUSED(provider);
+
+ return nullptr;
+ }
+
+//static
+std::unique_ptr<KDF>
+KDF::create_or_throw(const std::string& algo,
+ const std::string& provider)
+ {
+ if(auto kdf = KDF::create(algo, provider))
+ {
+ return kdf;
+ }
+ throw Lookup_Error("KDF", algo, provider);
+ }
+
+std::vector<std::string> KDF::providers(const std::string& algo_spec)
+ {
+ return probe_providers_of<KDF>(algo_spec, { "base" });
+ }
+
+KDF* get_kdf(const std::string& algo_spec)
+ {
+ SCAN_Name request(algo_spec);
+
+ if(request.algo_name() == "Raw")
+ return nullptr; // No KDF
+
+ //return KDF::create_or_throw(algo_spec).release();
+ auto kdf = KDF::create(algo_spec);
+ if(!kdf)
+ throw Algorithm_Not_Found(algo_spec);
+ return kdf.release();
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/kdf/kdf.h b/comm/third_party/botan/src/lib/kdf/kdf.h
new file mode 100644
index 0000000000..dd4cfedf6d
--- /dev/null
+++ b/comm/third_party/botan/src/lib/kdf/kdf.h
@@ -0,0 +1,196 @@
+/*
+* Key Derivation Function interfaces
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_KDF_BASE_H_
+#define BOTAN_KDF_BASE_H_
+
+#include <botan/secmem.h>
+#include <botan/types.h>
+#include <string>
+
+namespace Botan {
+
+/**
+* Key Derivation Function
+*/
+class BOTAN_PUBLIC_API(2,0) KDF
+ {
+ public:
+ virtual ~KDF() = default;
+
+ /**
+ * Create an instance based on a name
+ * If provider is empty then best available is chosen.
+ * @param algo_spec algorithm name
+ * @param provider provider implementation to choose
+ * @return a null pointer if the algo/provider combination cannot be found
+ */
+ static std::unique_ptr<KDF>
+ create(const std::string& algo_spec,
+ const std::string& provider = "");
+
+ /**
+ * Create an instance based on a name, or throw if the
+ * algo/provider combination cannot be found. If provider is
+ * empty then best available is chosen.
+ */
+ static std::unique_ptr<KDF>
+ create_or_throw(const std::string& algo_spec,
+ const std::string& provider = "");
+
+ /**
+ * @return list of available providers for this algorithm, empty if not available
+ */
+ static std::vector<std::string> providers(const std::string& algo_spec);
+
+ /**
+ * @return KDF name
+ */
+ virtual std::string name() const = 0;
+
+ /**
+ * Derive a key
+ * @param key buffer holding the derived key, must be of length key_len
+ * @param key_len the desired output length in bytes
+ * @param secret the secret input
+ * @param secret_len size of secret in bytes
+ * @param salt a diversifier
+ * @param salt_len size of salt in bytes
+ * @param label purpose for the derived keying material
+ * @param label_len size of label in bytes
+ * @return the derived key
+ */
+ virtual size_t kdf(uint8_t key[], size_t key_len,
+ const uint8_t secret[], size_t secret_len,
+ const uint8_t salt[], size_t salt_len,
+ const uint8_t label[], size_t label_len) const = 0;
+
+ /**
+ * Derive a key
+ * @param key_len the desired output length in bytes
+ * @param secret the secret input
+ * @param secret_len size of secret in bytes
+ * @param salt a diversifier
+ * @param salt_len size of salt in bytes
+ * @param label purpose for the derived keying material
+ * @param label_len size of label in bytes
+ * @return the derived key
+ */
+ secure_vector<uint8_t> derive_key(size_t key_len,
+ const uint8_t secret[],
+ size_t secret_len,
+ const uint8_t salt[],
+ size_t salt_len,
+ const uint8_t label[] = nullptr,
+ size_t label_len = 0) const
+ {
+ secure_vector<uint8_t> key(key_len);
+ key.resize(kdf(key.data(), key.size(), secret, secret_len, salt, salt_len, label, label_len));
+ return key;
+ }
+
+ /**
+ * Derive a key
+ * @param key_len the desired output length in bytes
+ * @param secret the secret input
+ * @param salt a diversifier
+ * @param label purpose for the derived keying material
+ * @return the derived key
+ */
+ secure_vector<uint8_t> derive_key(size_t key_len,
+ const secure_vector<uint8_t>& secret,
+ const std::string& salt = "",
+ const std::string& label = "") const
+ {
+ return derive_key(key_len, secret.data(), secret.size(),
+ cast_char_ptr_to_uint8(salt.data()),
+ salt.length(),
+ cast_char_ptr_to_uint8(label.data()),
+ label.length());
+
+ }
+
+ /**
+ * Derive a key
+ * @param key_len the desired output length in bytes
+ * @param secret the secret input
+ * @param salt a diversifier
+ * @param label purpose for the derived keying material
+ * @return the derived key
+ */
+ template<typename Alloc, typename Alloc2, typename Alloc3>
+ secure_vector<uint8_t> derive_key(size_t key_len,
+ const std::vector<uint8_t, Alloc>& secret,
+ const std::vector<uint8_t, Alloc2>& salt,
+ const std::vector<uint8_t, Alloc3>& label) const
+ {
+ return derive_key(key_len,
+ secret.data(), secret.size(),
+ salt.data(), salt.size(),
+ label.data(), label.size());
+ }
+
+ /**
+ * Derive a key
+ * @param key_len the desired output length in bytes
+ * @param secret the secret input
+ * @param salt a diversifier
+ * @param salt_len size of salt in bytes
+ * @param label purpose for the derived keying material
+ * @return the derived key
+ */
+ secure_vector<uint8_t> derive_key(size_t key_len,
+ const secure_vector<uint8_t>& secret,
+ const uint8_t salt[],
+ size_t salt_len,
+ const std::string& label = "") const
+ {
+ return derive_key(key_len,
+ secret.data(), secret.size(),
+ salt, salt_len,
+ cast_char_ptr_to_uint8(label.data()),
+ label.size());
+ }
+
+ /**
+ * Derive a key
+ * @param key_len the desired output length in bytes
+ * @param secret the secret input
+ * @param secret_len size of secret in bytes
+ * @param salt a diversifier
+ * @param label purpose for the derived keying material
+ * @return the derived key
+ */
+ secure_vector<uint8_t> derive_key(size_t key_len,
+ const uint8_t secret[],
+ size_t secret_len,
+ const std::string& salt = "",
+ const std::string& 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 new object representing the same algorithm as *this
+ */
+ virtual KDF* clone() const = 0;
+ };
+
+/**
+* Factory method for KDF (key derivation function)
+* @param algo_spec the name of the KDF to create
+* @return pointer to newly allocated object of that type
+*/
+BOTAN_PUBLIC_API(2,0) KDF* get_kdf(const std::string& algo_spec);
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/kdf/kdf1/info.txt b/comm/third_party/botan/src/lib/kdf/kdf1/info.txt
new file mode 100644
index 0000000000..f88268f93e
--- /dev/null
+++ b/comm/third_party/botan/src/lib/kdf/kdf1/info.txt
@@ -0,0 +1,7 @@
+<defines>
+KDF1 -> 20131128
+</defines>
+
+<requires>
+hash
+</requires>
diff --git a/comm/third_party/botan/src/lib/kdf/kdf1/kdf1.cpp b/comm/third_party/botan/src/lib/kdf/kdf1/kdf1.cpp
new file mode 100644
index 0000000000..3de261c55f
--- /dev/null
+++ b/comm/third_party/botan/src/lib/kdf/kdf1/kdf1.cpp
@@ -0,0 +1,33 @@
+/*
+* KDF1
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/kdf1.h>
+
+namespace Botan {
+
+size_t KDF1::kdf(uint8_t key[], size_t key_len,
+ const uint8_t secret[], size_t secret_len,
+ const uint8_t salt[], size_t salt_len,
+ const uint8_t label[], size_t label_len) const
+ {
+ m_hash->update(secret, secret_len);
+ m_hash->update(label, label_len);
+ m_hash->update(salt, salt_len);
+
+ if(key_len < m_hash->output_length())
+ {
+ secure_vector<uint8_t> v = m_hash->final();
+ copy_mem(key, v.data(), key_len);
+ return key_len;
+ }
+
+ m_hash->final(key);
+ // FIXME: returns truncated output
+ return m_hash->output_length();
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/kdf/kdf1/kdf1.h b/comm/third_party/botan/src/lib/kdf/kdf1/kdf1.h
new file mode 100644
index 0000000000..388b552517
--- /dev/null
+++ b/comm/third_party/botan/src/lib/kdf/kdf1/kdf1.h
@@ -0,0 +1,43 @@
+/*
+* KDF1
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_KDF1_H_
+#define BOTAN_KDF1_H_
+
+#include <botan/kdf.h>
+#include <botan/hash.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(kdf1.h)
+
+namespace Botan {
+
+/**
+* KDF1, from IEEE 1363
+*/
+class BOTAN_PUBLIC_API(2,0) KDF1 final : public KDF
+ {
+ public:
+ std::string name() const override { return "KDF1(" + m_hash->name() + ")"; }
+
+ KDF* clone() const override { return new KDF1(m_hash->clone()); }
+
+ size_t kdf(uint8_t key[], size_t key_len,
+ const uint8_t secret[], size_t secret_len,
+ const uint8_t salt[], size_t salt_len,
+ const uint8_t label[], size_t label_len) const override;
+
+ /**
+ * @param h hash function to use
+ */
+ explicit KDF1(HashFunction* h) : m_hash(h) {}
+ private:
+ std::unique_ptr<HashFunction> m_hash;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/kdf/kdf1_iso18033/info.txt b/comm/third_party/botan/src/lib/kdf/kdf1_iso18033/info.txt
new file mode 100644
index 0000000000..494b8358b0
--- /dev/null
+++ b/comm/third_party/botan/src/lib/kdf/kdf1_iso18033/info.txt
@@ -0,0 +1,7 @@
+<defines>
+KDF1_18033 -> 20160128
+</defines>
+
+<requires>
+hash
+</requires>
diff --git a/comm/third_party/botan/src/lib/kdf/kdf1_iso18033/kdf1_iso18033.cpp b/comm/third_party/botan/src/lib/kdf/kdf1_iso18033/kdf1_iso18033.cpp
new file mode 100644
index 0000000000..c7699d2f25
--- /dev/null
+++ b/comm/third_party/botan/src/lib/kdf/kdf1_iso18033/kdf1_iso18033.cpp
@@ -0,0 +1,38 @@
+/*
+* KDF1 from ISO 18033-2
+* (C) 2016 Philipp Weber
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/kdf1_iso18033.h>
+
+namespace Botan {
+
+size_t KDF1_18033::kdf(uint8_t key[], size_t key_len,
+ const uint8_t secret[], size_t secret_len,
+ const uint8_t salt[], size_t salt_len,
+ const uint8_t label[], size_t label_len) const
+ {
+ uint32_t counter = 0;
+ secure_vector<uint8_t> h;
+
+ size_t offset = 0;
+ while(offset != key_len && counter != 0xFFFFFFFF)
+ {
+ m_hash->update(secret, secret_len);
+ m_hash->update_be(counter++);
+ m_hash->update(label, label_len);
+ m_hash->update(salt, salt_len);
+ m_hash->final(h);
+
+ const size_t added = std::min(h.size(), key_len - offset);
+ copy_mem(&key[offset], h.data(), added);
+ offset += added;
+ }
+
+ // FIXME: returns truncated output
+ return offset;
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/kdf/kdf1_iso18033/kdf1_iso18033.h b/comm/third_party/botan/src/lib/kdf/kdf1_iso18033/kdf1_iso18033.h
new file mode 100644
index 0000000000..5f913057e1
--- /dev/null
+++ b/comm/third_party/botan/src/lib/kdf/kdf1_iso18033/kdf1_iso18033.h
@@ -0,0 +1,43 @@
+/*
+* KDF1 from ISO 18033-2
+* (C) 2016 Philipp Weber
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_KDF1_18033_H_
+#define BOTAN_KDF1_18033_H_
+
+#include <botan/kdf.h>
+#include <botan/hash.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(kdf1_iso18033.h)
+
+namespace Botan {
+
+/**
+* KDF1, from ISO 18033-2
+*/
+class BOTAN_PUBLIC_API(2,0) KDF1_18033 final : public KDF
+ {
+ public:
+ std::string name() const override { return "KDF1-18033(" + m_hash->name() + ")"; }
+
+ KDF* clone() const override { return new KDF1_18033(m_hash->clone()); }
+
+ size_t kdf(uint8_t key[], size_t key_len,
+ const uint8_t secret[], size_t secret_len,
+ const uint8_t salt[], size_t salt_len,
+ const uint8_t label[], size_t label_len) const override;
+
+ /**
+ * @param h hash function to use
+ */
+ explicit KDF1_18033(HashFunction* h) : m_hash(h) {}
+ private:
+ std::unique_ptr<HashFunction> m_hash;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/kdf/kdf2/info.txt b/comm/third_party/botan/src/lib/kdf/kdf2/info.txt
new file mode 100644
index 0000000000..e222a4521f
--- /dev/null
+++ b/comm/third_party/botan/src/lib/kdf/kdf2/info.txt
@@ -0,0 +1,7 @@
+<defines>
+KDF2 -> 20131128
+</defines>
+
+<requires>
+hash
+</requires>
diff --git a/comm/third_party/botan/src/lib/kdf/kdf2/kdf2.cpp b/comm/third_party/botan/src/lib/kdf/kdf2/kdf2.cpp
new file mode 100644
index 0000000000..4e3bb55832
--- /dev/null
+++ b/comm/third_party/botan/src/lib/kdf/kdf2/kdf2.cpp
@@ -0,0 +1,38 @@
+/*
+* KDF2
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/kdf2.h>
+
+namespace Botan {
+
+size_t KDF2::kdf(uint8_t key[], size_t key_len,
+ const uint8_t secret[], size_t secret_len,
+ const uint8_t salt[], size_t salt_len,
+ const uint8_t label[], size_t label_len) const
+ {
+ uint32_t counter = 1;
+ secure_vector<uint8_t> h;
+
+ size_t offset = 0;
+ while(offset != key_len && counter != 0)
+ {
+ m_hash->update(secret, secret_len);
+ m_hash->update_be(counter++);
+ m_hash->update(label, label_len);
+ m_hash->update(salt, salt_len);
+ m_hash->final(h);
+
+ const size_t added = std::min(h.size(), key_len - offset);
+ copy_mem(&key[offset], h.data(), added);
+ offset += added;
+ }
+
+ // FIXME: returns truncated output
+ return offset;
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/kdf/kdf2/kdf2.h b/comm/third_party/botan/src/lib/kdf/kdf2/kdf2.h
new file mode 100644
index 0000000000..43abbf087e
--- /dev/null
+++ b/comm/third_party/botan/src/lib/kdf/kdf2/kdf2.h
@@ -0,0 +1,43 @@
+/*
+* KDF2
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_KDF2_H_
+#define BOTAN_KDF2_H_
+
+#include <botan/kdf.h>
+#include <botan/hash.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(kdf2.h)
+
+namespace Botan {
+
+/**
+* KDF2, from IEEE 1363
+*/
+class BOTAN_PUBLIC_API(2,0) KDF2 final : public KDF
+ {
+ public:
+ std::string name() const override { return "KDF2(" + m_hash->name() + ")"; }
+
+ KDF* clone() const override { return new KDF2(m_hash->clone()); }
+
+ size_t kdf(uint8_t key[], size_t key_len,
+ const uint8_t secret[], size_t secret_len,
+ const uint8_t salt[], size_t salt_len,
+ const uint8_t label[], size_t label_len) const override;
+
+ /**
+ * @param h hash function to use
+ */
+ explicit KDF2(HashFunction* h) : m_hash(h) {}
+ private:
+ std::unique_ptr<HashFunction> m_hash;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/kdf/prf_tls/info.txt b/comm/third_party/botan/src/lib/kdf/prf_tls/info.txt
new file mode 100644
index 0000000000..3d76e4b4cb
--- /dev/null
+++ b/comm/third_party/botan/src/lib/kdf/prf_tls/info.txt
@@ -0,0 +1,8 @@
+<defines>
+TLS_V10_PRF -> 20131128
+TLS_V12_PRF -> 20131128
+</defines>
+
+<requires>
+hmac
+</requires>
diff --git a/comm/third_party/botan/src/lib/kdf/prf_tls/prf_tls.cpp b/comm/third_party/botan/src/lib/kdf/prf_tls/prf_tls.cpp
new file mode 100644
index 0000000000..c98c7d3516
--- /dev/null
+++ b/comm/third_party/botan/src/lib/kdf/prf_tls/prf_tls.cpp
@@ -0,0 +1,96 @@
+/*
+* TLS v1.0 and v1.2 PRFs
+* (C) 2004-2010 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/prf_tls.h>
+#include <botan/exceptn.h>
+
+namespace Botan {
+
+TLS_PRF::TLS_PRF() :
+ TLS_PRF(MessageAuthenticationCode::create_or_throw("HMAC(MD5)"),
+ MessageAuthenticationCode::create_or_throw("HMAC(SHA-1)"))
+ {
+ }
+
+namespace {
+
+/*
+* TLS PRF P_hash function
+*/
+void P_hash(uint8_t out[], size_t out_len,
+ MessageAuthenticationCode& mac,
+ const uint8_t secret[], size_t secret_len,
+ const uint8_t salt[], size_t salt_len)
+ {
+ try
+ {
+ mac.set_key(secret, secret_len);
+ }
+ catch(Invalid_Key_Length&)
+ {
+ throw Internal_Error("The premaster secret of " +
+ std::to_string(secret_len) +
+ " bytes is too long for the PRF");
+ }
+
+ secure_vector<uint8_t> A(salt, salt + salt_len);
+ secure_vector<uint8_t> h;
+
+ size_t offset = 0;
+
+ while(offset != out_len)
+ {
+ A = mac.process(A);
+
+ mac.update(A);
+ mac.update(salt, salt_len);
+ mac.final(h);
+
+ const size_t writing = std::min(h.size(), out_len - offset);
+ xor_buf(&out[offset], h.data(), writing);
+ offset += writing;
+ }
+ }
+
+}
+
+size_t TLS_PRF::kdf(uint8_t key[], size_t key_len,
+ const uint8_t secret[], size_t secret_len,
+ const uint8_t salt[], size_t salt_len,
+ const uint8_t label[], size_t label_len) const
+ {
+ const size_t S1_len = (secret_len + 1) / 2,
+ S2_len = (secret_len + 1) / 2;
+ const uint8_t* S1 = secret;
+ const uint8_t* S2 = secret + (secret_len - S2_len);
+ secure_vector<uint8_t> msg;
+
+ msg.reserve(label_len + salt_len);
+ msg += std::make_pair(label, label_len);
+ msg += std::make_pair(salt, salt_len);
+
+ P_hash(key, key_len, *m_hmac_md5, S1, S1_len, msg.data(), msg.size());
+ P_hash(key, key_len, *m_hmac_sha1, S2, S2_len, msg.data(), msg.size());
+ return key_len;
+ }
+
+size_t TLS_12_PRF::kdf(uint8_t key[], size_t key_len,
+ const uint8_t secret[], size_t secret_len,
+ const uint8_t salt[], size_t salt_len,
+ const uint8_t label[], size_t label_len) const
+ {
+ secure_vector<uint8_t> msg;
+
+ msg.reserve(label_len + salt_len);
+ msg += std::make_pair(label, label_len);
+ msg += std::make_pair(salt, salt_len);
+
+ P_hash(key, key_len, *m_mac, secret, secret_len, msg.data(), msg.size());
+ return key_len;
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/kdf/prf_tls/prf_tls.h b/comm/third_party/botan/src/lib/kdf/prf_tls/prf_tls.h
new file mode 100644
index 0000000000..603086a7e9
--- /dev/null
+++ b/comm/third_party/botan/src/lib/kdf/prf_tls/prf_tls.h
@@ -0,0 +1,70 @@
+/*
+* TLS v1.0 and v1.2 PRFs
+* (C) 2004-2010 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_TLS_PRF_H_
+#define BOTAN_TLS_PRF_H_
+
+#include <botan/kdf.h>
+#include <botan/mac.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(prf_tls.h)
+
+namespace Botan {
+
+/**
+* PRF used in TLS 1.0/1.1
+*/
+class BOTAN_PUBLIC_API(2,0) TLS_PRF final : public KDF
+ {
+ public:
+ std::string name() const override { return "TLS-PRF"; }
+
+ KDF* clone() const override { return new TLS_PRF; }
+
+ size_t kdf(uint8_t key[], size_t key_len,
+ const uint8_t secret[], size_t secret_len,
+ const uint8_t salt[], size_t salt_len,
+ const uint8_t label[], size_t label_len) const override;
+
+ TLS_PRF(std::unique_ptr<MessageAuthenticationCode> hmac_md5,
+ std::unique_ptr<MessageAuthenticationCode> hmac_sha1) :
+ m_hmac_md5(std::move(hmac_md5)),
+ m_hmac_sha1(std::move(hmac_sha1))
+ {}
+
+ TLS_PRF();
+ private:
+ std::unique_ptr<MessageAuthenticationCode> m_hmac_md5;
+ std::unique_ptr<MessageAuthenticationCode> m_hmac_sha1;
+ };
+
+/**
+* PRF used in TLS 1.2
+*/
+class BOTAN_PUBLIC_API(2,0) TLS_12_PRF final : public KDF
+ {
+ public:
+ std::string name() const override { return "TLS-12-PRF(" + m_mac->name() + ")"; }
+
+ KDF* clone() const override { return new TLS_12_PRF(m_mac->clone()); }
+
+ size_t kdf(uint8_t key[], size_t key_len,
+ const uint8_t secret[], size_t secret_len,
+ const uint8_t salt[], size_t salt_len,
+ const uint8_t label[], size_t label_len) const override;
+
+ /**
+ * @param mac MAC algorithm to use
+ */
+ explicit TLS_12_PRF(MessageAuthenticationCode* mac) : m_mac(mac) {}
+ private:
+ std::unique_ptr<MessageAuthenticationCode> m_mac;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/kdf/prf_x942/info.txt b/comm/third_party/botan/src/lib/kdf/prf_x942/info.txt
new file mode 100644
index 0000000000..8226433654
--- /dev/null
+++ b/comm/third_party/botan/src/lib/kdf/prf_x942/info.txt
@@ -0,0 +1,8 @@
+<defines>
+X942_PRF -> 20131128
+</defines>
+
+<requires>
+asn1
+sha1
+</requires>
diff --git a/comm/third_party/botan/src/lib/kdf/prf_x942/prf_x942.cpp b/comm/third_party/botan/src/lib/kdf/prf_x942/prf_x942.cpp
new file mode 100644
index 0000000000..4a4a3a7f09
--- /dev/null
+++ b/comm/third_party/botan/src/lib/kdf/prf_x942/prf_x942.cpp
@@ -0,0 +1,92 @@
+/*
+* X9.42 PRF
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/prf_x942.h>
+#include <botan/der_enc.h>
+#include <botan/hash.h>
+#include <botan/loadstor.h>
+#include <algorithm>
+
+namespace Botan {
+
+namespace {
+
+/*
+* Encode an integer as an OCTET STRING
+*/
+std::vector<uint8_t> encode_x942_int(uint32_t n)
+ {
+ uint8_t n_buf[4] = { 0 };
+ store_be(n, n_buf);
+
+ std::vector<uint8_t> output;
+ DER_Encoder(output).encode(n_buf, 4, OCTET_STRING);
+ return output;
+ }
+
+}
+
+size_t X942_PRF::kdf(uint8_t key[], size_t key_len,
+ const uint8_t secret[], size_t secret_len,
+ const uint8_t salt[], size_t salt_len,
+ const uint8_t label[], size_t label_len) const
+ {
+ std::unique_ptr<HashFunction> hash(HashFunction::create("SHA-160"));
+
+ secure_vector<uint8_t> h;
+ secure_vector<uint8_t> in;
+ size_t offset = 0;
+ uint32_t counter = 1;
+
+ in.reserve(salt_len + label_len);
+ in += std::make_pair(label,label_len);
+ in += std::make_pair(salt,salt_len);
+
+ while(offset != key_len && counter)
+ {
+ hash->update(secret, secret_len);
+
+ hash->update(
+ DER_Encoder().start_cons(SEQUENCE)
+
+ .start_cons(SEQUENCE)
+ .encode(m_key_wrap_oid)
+ .raw_bytes(encode_x942_int(counter))
+ .end_cons()
+
+ .encode_if(salt_len != 0,
+ DER_Encoder()
+ .start_explicit(0)
+ .encode(in, OCTET_STRING)
+ .end_explicit()
+ )
+
+ .start_explicit(2)
+ .raw_bytes(encode_x942_int(static_cast<uint32_t>(8 * key_len)))
+ .end_explicit()
+
+ .end_cons().get_contents()
+ );
+
+ hash->final(h);
+ const size_t copied = std::min(h.size(), key_len - offset);
+ copy_mem(&key[offset], h.data(), copied);
+ offset += copied;
+
+ ++counter;
+ }
+
+ // FIXME: returns truncated output
+ return offset;
+ }
+
+std::string X942_PRF::name() const
+ {
+ return "X9.42-PRF(" + m_key_wrap_oid.to_formatted_string() + ")";
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/kdf/prf_x942/prf_x942.h b/comm/third_party/botan/src/lib/kdf/prf_x942/prf_x942.h
new file mode 100644
index 0000000000..98af7e069b
--- /dev/null
+++ b/comm/third_party/botan/src/lib/kdf/prf_x942/prf_x942.h
@@ -0,0 +1,42 @@
+/*
+* X9.42 PRF
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_ANSI_X942_PRF_H_
+#define BOTAN_ANSI_X942_PRF_H_
+
+#include <botan/kdf.h>
+#include <botan/asn1_obj.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(prf_x942.h)
+
+namespace Botan {
+
+/**
+* PRF from ANSI X9.42
+*/
+class BOTAN_PUBLIC_API(2,0) X942_PRF final : public KDF
+ {
+ public:
+ std::string name() const override;
+
+ KDF* clone() const override { return new X942_PRF(m_key_wrap_oid); }
+
+ size_t kdf(uint8_t key[], size_t key_len,
+ const uint8_t secret[], size_t secret_len,
+ const uint8_t salt[], size_t salt_len,
+ const uint8_t label[], size_t label_len) const override;
+
+ explicit X942_PRF(const std::string& oid) : m_key_wrap_oid(OID::from_string(oid)) {}
+
+ explicit X942_PRF(const OID& oid) : m_key_wrap_oid(oid) {}
+ private:
+ OID m_key_wrap_oid;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/kdf/sp800_108/info.txt b/comm/third_party/botan/src/lib/kdf/sp800_108/info.txt
new file mode 100644
index 0000000000..864a7fb9c3
--- /dev/null
+++ b/comm/third_party/botan/src/lib/kdf/sp800_108/info.txt
@@ -0,0 +1,8 @@
+<defines>
+SP800_108 -> 20160128
+</defines>
+
+<requires>
+mac
+hmac
+</requires>
diff --git a/comm/third_party/botan/src/lib/kdf/sp800_108/sp800_108.cpp b/comm/third_party/botan/src/lib/kdf/sp800_108/sp800_108.cpp
new file mode 100644
index 0000000000..909e8d47d5
--- /dev/null
+++ b/comm/third_party/botan/src/lib/kdf/sp800_108/sp800_108.cpp
@@ -0,0 +1,170 @@
+/*
+* KDFs defined in NIST SP 800-108
+* (C) 2016 Kai Michaelis
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/sp800_108.h>
+#include <botan/loadstor.h>
+#include <botan/exceptn.h>
+#include <iterator>
+
+namespace Botan {
+
+size_t SP800_108_Counter::kdf(uint8_t key[], size_t key_len,
+ const uint8_t secret[], size_t secret_len,
+ const uint8_t salt[], size_t salt_len,
+ const uint8_t label[], size_t label_len) const
+ {
+ const std::size_t prf_len = m_prf->output_length();
+
+ const uint64_t blocks_required = (key_len + prf_len - 1) / prf_len;
+
+ if(blocks_required > 0xFFFFFFFF)
+ throw Invalid_Argument("SP800_108_Counter output size too large");
+
+ const uint8_t delim = 0;
+ const uint32_t length = static_cast<uint32_t>(key_len * 8);
+
+ uint8_t *p = key;
+ uint32_t counter = 1;
+ uint8_t be_len[4] = { 0 };
+ secure_vector<uint8_t> tmp;
+
+ store_be(length, be_len);
+ m_prf->set_key(secret, secret_len);
+
+ while(p < key + key_len)
+ {
+ const std::size_t to_copy = std::min< std::size_t >(key + key_len - p, prf_len);
+ uint8_t be_cnt[4] = { 0 };
+
+ store_be(counter, be_cnt);
+
+ m_prf->update(be_cnt,4);
+ m_prf->update(label,label_len);
+ m_prf->update(delim);
+ m_prf->update(salt,salt_len);
+ m_prf->update(be_len,4);
+ m_prf->final(tmp);
+
+ copy_mem(p, tmp.data(), to_copy);
+ p += to_copy;
+
+ ++counter;
+ BOTAN_ASSERT(counter != 0, "No counter overflow");
+ }
+
+ return key_len;
+ }
+
+size_t SP800_108_Feedback::kdf(uint8_t key[], size_t key_len,
+ const uint8_t secret[], size_t secret_len,
+ const uint8_t salt[], size_t salt_len,
+ const uint8_t label[], size_t label_len) const
+ {
+ const uint32_t length = static_cast<uint32_t>(key_len * 8);
+ const std::size_t prf_len = m_prf->output_length();
+ const std::size_t iv_len = (salt_len >= prf_len ? prf_len : 0);
+ const uint8_t delim = 0;
+
+ const uint64_t blocks_required = (key_len + prf_len - 1) / prf_len;
+
+ if(blocks_required > 0xFFFFFFFF)
+ throw Invalid_Argument("SP800_108_Feedback output size too large");
+
+ uint8_t *p = key;
+ uint32_t counter = 1;
+ uint8_t be_len[4] = { 0 };
+ secure_vector< uint8_t > prev(salt, salt + iv_len);
+ secure_vector< uint8_t > ctx(salt + iv_len, salt + salt_len);
+
+ store_be(length, be_len);
+ m_prf->set_key(secret, secret_len);
+
+ while(p < key + key_len)
+ {
+ const std::size_t to_copy = std::min< std::size_t >(key + key_len - p, prf_len);
+ uint8_t be_cnt[4] = { 0 };
+
+ store_be(counter, be_cnt);
+
+ m_prf->update(prev);
+ m_prf->update(be_cnt,4);
+ m_prf->update(label,label_len);
+ m_prf->update(delim);
+ m_prf->update(ctx);
+ m_prf->update(be_len,4);
+ m_prf->final(prev);
+
+ copy_mem(p, prev.data(), to_copy);
+ p += to_copy;
+
+ ++counter;
+
+ BOTAN_ASSERT(counter != 0, "No overflow");
+ }
+
+ return key_len;
+ }
+
+size_t SP800_108_Pipeline::kdf(uint8_t key[], size_t key_len,
+ const uint8_t secret[], size_t secret_len,
+ const uint8_t salt[], size_t salt_len,
+ const uint8_t label[], size_t label_len) const
+ {
+ const uint32_t length = static_cast<uint32_t>(key_len * 8);
+ const std::size_t prf_len = m_prf->output_length();
+ const uint8_t delim = 0;
+
+ const uint64_t blocks_required = (key_len + prf_len - 1) / prf_len;
+
+ if(blocks_required > 0xFFFFFFFF)
+ throw Invalid_Argument("SP800_108_Feedback output size too large");
+
+ uint8_t *p = key;
+ uint32_t counter = 1;
+ uint8_t be_len[4] = { 0 };
+ secure_vector<uint8_t> ai, ki;
+
+ store_be(length, be_len);
+ m_prf->set_key(secret,secret_len);
+
+ // A(0)
+ std::copy(label,label + label_len,std::back_inserter(ai));
+ ai.emplace_back(delim);
+ std::copy(salt,salt + salt_len,std::back_inserter(ai));
+ std::copy(be_len,be_len + 4,std::back_inserter(ai));
+
+ while(p < key + key_len)
+ {
+ // A(i)
+ m_prf->update(ai);
+ m_prf->final(ai);
+
+ // K(i)
+ const std::size_t to_copy = std::min< std::size_t >(key + key_len - p, prf_len);
+ uint8_t be_cnt[4] = { 0 };
+
+ store_be(counter, be_cnt);
+
+ m_prf->update(ai);
+ m_prf->update(be_cnt,4);
+ m_prf->update(label, label_len);
+ m_prf->update(delim);
+ m_prf->update(salt, salt_len);
+ m_prf->update(be_len,4);
+ m_prf->final(ki);
+
+ copy_mem(p, ki.data(), to_copy);
+ p += to_copy;
+
+ ++counter;
+
+ BOTAN_ASSERT(counter != 0, "No overflow");
+ }
+
+ return key_len;
+ }
+}
diff --git a/comm/third_party/botan/src/lib/kdf/sp800_108/sp800_108.h b/comm/third_party/botan/src/lib/kdf/sp800_108/sp800_108.h
new file mode 100644
index 0000000000..46f734e8ea
--- /dev/null
+++ b/comm/third_party/botan/src/lib/kdf/sp800_108/sp800_108.h
@@ -0,0 +1,135 @@
+/*
+* KDFs defined in NIST SP 800-108
+* (C) 2016 Kai Michaelis
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_SP800_108_H_
+#define BOTAN_SP800_108_H_
+
+#include <botan/kdf.h>
+#include <botan/mac.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(sp800_108.h)
+
+namespace Botan {
+
+/**
+ * NIST SP 800-108 KDF in Counter Mode (5.1)
+ */
+class BOTAN_PUBLIC_API(2,0) SP800_108_Counter final : public KDF
+ {
+ public:
+ std::string name() const override { return "SP800-108-Counter(" + m_prf->name() + ")"; }
+
+ KDF* clone() const override { return new SP800_108_Counter(m_prf->clone()); }
+
+ /**
+ * Derive a key using the SP800-108 KDF in Counter mode.
+ *
+ * The implementation hard codes the length of [L]_2
+ * and [i]_2 (the value r) to 32 bits.
+ *
+ * @param key resulting keying material
+ * @param key_len the desired output length in bytes
+ * @param secret K_I
+ * @param secret_len size of K_I in bytes
+ * @param salt Context
+ * @param salt_len size of Context in bytes
+ * @param label Label
+ * @param label_len size of Label in bytes
+ *
+ * @throws Invalid_Argument key_len > 2^32
+ */
+ size_t kdf(uint8_t key[], size_t key_len,
+ const uint8_t secret[], size_t secret_len,
+ const uint8_t salt[], size_t salt_len,
+ const uint8_t label[], size_t label_len) const override;
+
+ /**
+ * @param mac MAC algorithm to use
+ */
+ explicit SP800_108_Counter(MessageAuthenticationCode* mac) : m_prf(mac) {}
+ private:
+ std::unique_ptr<MessageAuthenticationCode> m_prf;
+ };
+
+/**
+ * NIST SP 800-108 KDF in Feedback Mode (5.2)
+ */
+class BOTAN_PUBLIC_API(2,0) SP800_108_Feedback final : public KDF
+ {
+ public:
+ std::string name() const override { return "SP800-108-Feedback(" + m_prf->name() + ")"; }
+
+ KDF* clone() const override { return new SP800_108_Feedback(m_prf->clone()); }
+
+ /**
+ * Derive a key using the SP800-108 KDF in Feedback mode.
+ *
+ * The implementation uses the optional counter i and hard
+ * codes the length of [L]_2 and [i]_2 (the value r) to 32 bits.
+ *
+ * @param key resulting keying material
+ * @param key_len the desired output length in bytes
+ * @param secret K_I
+ * @param secret_len size of K_I in bytes
+ * @param salt IV || Context
+ * @param salt_len size of Context plus IV in bytes
+ * @param label Label
+ * @param label_len size of Label in bytes
+ *
+ * @throws Invalid_Argument key_len > 2^32
+ */
+ size_t kdf(uint8_t key[], size_t key_len,
+ const uint8_t secret[], size_t secret_len,
+ const uint8_t salt[], size_t salt_len,
+ const uint8_t label[], size_t label_len) const override;
+
+ explicit SP800_108_Feedback(MessageAuthenticationCode* mac) : m_prf(mac) {}
+ private:
+ std::unique_ptr<MessageAuthenticationCode> m_prf;
+ };
+
+/**
+ * NIST SP 800-108 KDF in Double Pipeline Mode (5.3)
+ */
+class BOTAN_PUBLIC_API(2,0) SP800_108_Pipeline final : public KDF
+ {
+ public:
+ std::string name() const override { return "SP800-108-Pipeline(" + m_prf->name() + ")"; }
+
+ KDF* clone() const override { return new SP800_108_Pipeline(m_prf->clone()); }
+
+ /**
+ * Derive a key using the SP800-108 KDF in Double Pipeline mode.
+ *
+ * The implementation uses the optional counter i and hard
+ * codes the length of [L]_2 and [i]_2 (the value r) to 32 bits.
+ *
+ * @param key resulting keying material
+ * @param key_len the desired output length in bytes
+ * @param secret K_I
+ * @param secret_len size of K_I in bytes
+ * @param salt Context
+ * @param salt_len size of Context in bytes
+ * @param label Label
+ * @param label_len size of Label in bytes
+ *
+ * @throws Invalid_Argument key_len > 2^32
+ */
+ size_t kdf(uint8_t key[], size_t key_len,
+ const uint8_t secret[], size_t secret_len,
+ const uint8_t salt[], size_t salt_len,
+ const uint8_t label[], size_t label_len) const override;
+
+ explicit SP800_108_Pipeline(MessageAuthenticationCode* mac) : m_prf(mac) {}
+
+ private:
+ std::unique_ptr<MessageAuthenticationCode> m_prf;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/kdf/sp800_56a/info.txt b/comm/third_party/botan/src/lib/kdf/sp800_56a/info.txt
new file mode 100644
index 0000000000..d8ef51673b
--- /dev/null
+++ b/comm/third_party/botan/src/lib/kdf/sp800_56a/info.txt
@@ -0,0 +1,7 @@
+<defines>
+SP800_56A -> 20170501
+</defines>
+
+<requires>
+hmac
+</requires>
diff --git a/comm/third_party/botan/src/lib/kdf/sp800_56a/sp800_56a.cpp b/comm/third_party/botan/src/lib/kdf/sp800_56a/sp800_56a.cpp
new file mode 100644
index 0000000000..8e9bcf8560
--- /dev/null
+++ b/comm/third_party/botan/src/lib/kdf/sp800_56a/sp800_56a.cpp
@@ -0,0 +1,98 @@
+/*
+* KDF defined in NIST SP 800-56a (Approved Alternative 1)
+*
+* (C) 2017 Ribose Inc. Written by Krzysztof Kwiatkowski.
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/sp800_56a.h>
+#include <botan/scan_name.h>
+#include <botan/exceptn.h>
+
+namespace Botan {
+
+namespace {
+
+template<class AuxiliaryFunction_t>
+size_t SP800_56A_kdf(
+ AuxiliaryFunction_t& auxfunc,
+ uint8_t key[], size_t key_len,
+ const uint8_t secret[], size_t secret_len,
+ const uint8_t label[], size_t label_len)
+ {
+ const uint64_t kRepsUpperBound = (1ULL << 32);
+
+ const size_t digest_len = auxfunc.output_length();
+
+ const size_t reps = key_len / digest_len + ((key_len % digest_len) ? 1 : 0);
+
+ if (reps >= kRepsUpperBound)
+ {
+ // See SP-800-56A, point 5.8.1
+ throw Invalid_Argument("SP800-56A KDF requested output too large");
+ }
+
+ uint32_t counter = 1;
+ secure_vector<uint8_t> result;
+ for(size_t i = 0; i < reps; i++)
+ {
+ auxfunc.update_be(counter++);
+ auxfunc.update(secret, secret_len);
+ auxfunc.update(label, label_len);
+ auxfunc.final(result);
+
+ const size_t offset = digest_len * i;
+ const size_t len = std::min(result.size(), key_len - offset);
+ copy_mem(&key[offset], result.data(), len);
+ }
+
+ return key_len;
+ }
+
+}
+
+size_t SP800_56A_Hash::kdf(uint8_t key[], size_t key_len,
+ const uint8_t secret[], size_t secret_len,
+ const uint8_t salt[], size_t salt_len,
+ const uint8_t label[], size_t label_len) const
+ {
+ /*
+ * TODO: should we reject a non-empty salt with an exception?
+ * Ignoring the salt seems quite dangerous to applications which
+ * don't expect it.
+ */
+ BOTAN_UNUSED(salt, salt_len);
+
+ return SP800_56A_kdf(*m_hash, key, key_len, secret, secret_len, label, label_len);
+ }
+
+SP800_56A_HMAC::SP800_56A_HMAC(MessageAuthenticationCode* mac) : m_mac(mac)
+ {
+ // TODO: we need a MessageAuthenticationCode::is_hmac
+ const SCAN_Name req(m_mac->name());
+ if(req.algo_name() != "HMAC")
+ {
+ throw Algorithm_Not_Found("Only HMAC can be used with KDF SP800-56A");
+ }
+ }
+
+size_t SP800_56A_HMAC::kdf(uint8_t key[], size_t key_len,
+ const uint8_t secret[], size_t secret_len,
+ const uint8_t salt[], size_t salt_len,
+ const uint8_t label[], size_t label_len) const
+ {
+ /*
+ * SP 800-56A specifies if the salt is empty then a block of zeros
+ * equal to the hash's underlying block size are used. However this
+ * is equivalent to setting a zero-length key, so the same call
+ * works for either case.
+ */
+ m_mac->set_key(salt, salt_len);
+
+ return SP800_56A_kdf(*m_mac, key, key_len, secret, secret_len, label, label_len);
+ }
+
+
+
+}
diff --git a/comm/third_party/botan/src/lib/kdf/sp800_56a/sp800_56a.h b/comm/third_party/botan/src/lib/kdf/sp800_56a/sp800_56a.h
new file mode 100644
index 0000000000..e83f117e2e
--- /dev/null
+++ b/comm/third_party/botan/src/lib/kdf/sp800_56a/sp800_56a.h
@@ -0,0 +1,103 @@
+/*
+* KDF defined in NIST SP 800-56a revision 2 (Single-step key-derivation function)
+*
+* (C) 2017 Ribose Inc. Written by Krzysztof Kwiatkowski.
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_SP800_56A_H_
+#define BOTAN_SP800_56A_H_
+
+#include <botan/kdf.h>
+#include <botan/hash.h>
+#include <botan/mac.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(sp800_56a.h)
+
+namespace Botan {
+
+/**
+ * NIST SP 800-56A KDF using hash function
+ * @warning This KDF ignores the provided salt value
+ */
+class BOTAN_PUBLIC_API(2,2) SP800_56A_Hash final : public KDF
+ {
+ public:
+ std::string name() const override { return "SP800-56A(" + m_hash->name() + ")"; }
+
+ KDF* clone() const override { return new SP800_56A_Hash(m_hash->clone()); }
+
+ /**
+ * Derive a key using the SP800-56A KDF.
+ *
+ * The implementation hard codes the context value for the
+ * expansion step to the empty string.
+ *
+ * @param key derived keying material K_M
+ * @param key_len the desired output length in bytes
+ * @param secret shared secret Z
+ * @param secret_len size of Z in bytes
+ * @param salt ignored
+ * @param salt_len ignored
+ * @param label label for the expansion step
+ * @param label_len size of label in bytes
+ *
+ * @throws Invalid_Argument key_len > 2^32
+ */
+ size_t kdf(uint8_t key[], size_t key_len,
+ const uint8_t secret[], size_t secret_len,
+ const uint8_t salt[], size_t salt_len,
+ const uint8_t label[], size_t label_len) const override;
+
+ /**
+ * @param hash the hash function to use as the auxiliary function
+ */
+ explicit SP800_56A_Hash(HashFunction* hash) : m_hash(hash) {}
+ private:
+ std::unique_ptr<HashFunction> m_hash;
+ };
+
+/**
+ * NIST SP 800-56A KDF using HMAC
+ */
+class BOTAN_PUBLIC_API(2,2) SP800_56A_HMAC final : public KDF
+ {
+ public:
+ std::string name() const override { return "SP800-56A(" + m_mac->name() + ")"; }
+
+ KDF* clone() const override { return new SP800_56A_HMAC(m_mac->clone()); }
+
+ /**
+ * Derive a key using the SP800-56A KDF.
+ *
+ * The implementation hard codes the context value for the
+ * expansion step to the empty string.
+ *
+ * @param key derived keying material K_M
+ * @param key_len the desired output length in bytes
+ * @param secret shared secret Z
+ * @param secret_len size of Z in bytes
+ * @param salt ignored
+ * @param salt_len ignored
+ * @param label label for the expansion step
+ * @param label_len size of label in bytes
+ *
+ * @throws Invalid_Argument key_len > 2^32 or MAC is not a HMAC
+ */
+ size_t kdf(uint8_t key[], size_t key_len,
+ const uint8_t secret[], size_t secret_len,
+ const uint8_t salt[], size_t salt_len,
+ const uint8_t label[], size_t label_len) const override;
+
+ /**
+ * @param mac the HMAC to use as the auxiliary function
+ */
+ explicit SP800_56A_HMAC(MessageAuthenticationCode* mac);
+ private:
+ std::unique_ptr<MessageAuthenticationCode> m_mac;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/kdf/sp800_56c/info.txt b/comm/third_party/botan/src/lib/kdf/sp800_56c/info.txt
new file mode 100644
index 0000000000..e598e88454
--- /dev/null
+++ b/comm/third_party/botan/src/lib/kdf/sp800_56c/info.txt
@@ -0,0 +1,8 @@
+<defines>
+SP800_56C -> 20160211
+</defines>
+
+<requires>
+sp800_108
+hmac
+</requires>
diff --git a/comm/third_party/botan/src/lib/kdf/sp800_56c/sp800_56c.cpp b/comm/third_party/botan/src/lib/kdf/sp800_56c/sp800_56c.cpp
new file mode 100644
index 0000000000..c0a1a1f689
--- /dev/null
+++ b/comm/third_party/botan/src/lib/kdf/sp800_56c/sp800_56c.cpp
@@ -0,0 +1,28 @@
+/*
+* KDF defined in NIST SP 800-56c
+* (C) 2016 Kai Michaelis
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/sp800_56c.h>
+
+namespace Botan {
+
+size_t SP800_56C::kdf(uint8_t key[], size_t key_len,
+ const uint8_t secret[], size_t secret_len,
+ const uint8_t salt[], size_t salt_len,
+ const uint8_t label[], size_t label_len) const
+ {
+ // Randomness Extraction
+ secure_vector<uint8_t> k_dk;
+
+ m_prf->set_key(salt, salt_len);
+ m_prf->update(secret, secret_len);
+ m_prf->final(k_dk);
+
+ // Key Expansion
+ return m_exp->kdf(key, key_len, k_dk.data(), k_dk.size(), nullptr, 0, label, label_len);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/kdf/sp800_56c/sp800_56c.h b/comm/third_party/botan/src/lib/kdf/sp800_56c/sp800_56c.h
new file mode 100644
index 0000000000..bdbdfcd9e1
--- /dev/null
+++ b/comm/third_party/botan/src/lib/kdf/sp800_56c/sp800_56c.h
@@ -0,0 +1,61 @@
+/*
+* KDF defined in NIST SP 800-56c
+* (C) 2016 Kai Michaelis
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_SP800_56C_H_
+#define BOTAN_SP800_56C_H_
+
+#include <botan/kdf.h>
+#include <botan/mac.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(sp800_56c.h)
+
+namespace Botan {
+
+/**
+ * NIST SP 800-56C KDF
+ */
+class BOTAN_PUBLIC_API(2,0) SP800_56C final : public KDF
+ {
+ public:
+ std::string name() const override { return "SP800-56C(" + m_prf->name() + ")"; }
+
+ KDF* clone() const override { return new SP800_56C(m_prf->clone(), m_exp->clone()); }
+
+ /**
+ * Derive a key using the SP800-56C KDF.
+ *
+ * The implementation hard codes the context value for the
+ * expansion step to the empty string.
+ *
+ * @param key derived keying material K_M
+ * @param key_len the desired output length in bytes
+ * @param secret shared secret Z
+ * @param secret_len size of Z in bytes
+ * @param salt salt s of the extraction step
+ * @param salt_len size of s in bytes
+ * @param label label for the expansion step
+ * @param label_len size of label in bytes
+ *
+ * @throws Invalid_Argument key_len > 2^32
+ */
+ size_t kdf(uint8_t key[], size_t key_len,
+ const uint8_t secret[], size_t secret_len,
+ const uint8_t salt[], size_t salt_len,
+ const uint8_t label[], size_t label_len) const override;
+
+ /**
+ * @param mac MAC algorithm used for randomness extraction
+ * @param exp KDF used for key expansion
+ */
+ SP800_56C(MessageAuthenticationCode* mac, KDF* exp) : m_prf(mac), m_exp(exp) {}
+ private:
+ std::unique_ptr<MessageAuthenticationCode> m_prf;
+ std::unique_ptr<KDF> m_exp;
+ };
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/mac/cbc_mac/cbc_mac.cpp b/comm/third_party/botan/src/lib/mac/cbc_mac/cbc_mac.cpp
new file mode 100644
index 0000000000..ba403b564a
--- /dev/null
+++ b/comm/third_party/botan/src/lib/mac/cbc_mac/cbc_mac.cpp
@@ -0,0 +1,99 @@
+/*
+* CBC-MAC
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/cbc_mac.h>
+
+namespace Botan {
+
+/*
+* Update an CBC-MAC Calculation
+*/
+void CBC_MAC::add_data(const uint8_t input[], size_t length)
+ {
+ verify_key_set(m_state.empty() == false);
+
+ size_t xored = std::min(output_length() - m_position, length);
+ xor_buf(&m_state[m_position], input, xored);
+ m_position += xored;
+
+ if(m_position < output_length())
+ return;
+
+ m_cipher->encrypt(m_state);
+ input += xored;
+ length -= xored;
+ while(length >= output_length())
+ {
+ xor_buf(m_state, input, output_length());
+ m_cipher->encrypt(m_state);
+ input += output_length();
+ length -= output_length();
+ }
+
+ xor_buf(m_state, input, length);
+ m_position = length;
+ }
+
+/*
+* Finalize an CBC-MAC Calculation
+*/
+void CBC_MAC::final_result(uint8_t mac[])
+ {
+ verify_key_set(m_state.empty() == false);
+
+ if(m_position)
+ m_cipher->encrypt(m_state);
+
+ copy_mem(mac, m_state.data(), m_state.size());
+ zeroise(m_state);
+ m_position = 0;
+ }
+
+/*
+* CBC-MAC Key Schedule
+*/
+void CBC_MAC::key_schedule(const uint8_t key[], size_t length)
+ {
+ m_state.resize(m_cipher->block_size());
+ m_cipher->set_key(key, length);
+ }
+
+/*
+* Clear memory of sensitive data
+*/
+void CBC_MAC::clear()
+ {
+ m_cipher->clear();
+ zap(m_state);
+ m_position = 0;
+ }
+
+/*
+* Return the name of this type
+*/
+std::string CBC_MAC::name() const
+ {
+ return "CBC-MAC(" + m_cipher->name() + ")";
+ }
+
+/*
+* Return a clone of this object
+*/
+MessageAuthenticationCode* CBC_MAC::clone() const
+ {
+ return new CBC_MAC(m_cipher->clone());
+ }
+
+/*
+* CBC-MAC Constructor
+*/
+CBC_MAC::CBC_MAC(BlockCipher* cipher) :
+ m_cipher(cipher)
+ {
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/mac/cbc_mac/cbc_mac.h b/comm/third_party/botan/src/lib/mac/cbc_mac/cbc_mac.h
new file mode 100644
index 0000000000..ed4eb2bd18
--- /dev/null
+++ b/comm/third_party/botan/src/lib/mac/cbc_mac/cbc_mac.h
@@ -0,0 +1,50 @@
+/*
+* CBC-MAC
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_CBC_MAC_H_
+#define BOTAN_CBC_MAC_H_
+
+#include <botan/mac.h>
+#include <botan/block_cipher.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(cbc_mac.h)
+
+namespace Botan {
+
+/**
+* CBC-MAC
+*/
+class BOTAN_PUBLIC_API(2,0) CBC_MAC final : public MessageAuthenticationCode
+ {
+ public:
+ std::string name() const override;
+ MessageAuthenticationCode* clone() const override;
+ size_t output_length() const override { return m_cipher->block_size(); }
+ void clear() override;
+
+ Key_Length_Specification key_spec() const override
+ {
+ return m_cipher->key_spec();
+ }
+
+ /**
+ * @param cipher the block cipher to use
+ */
+ explicit CBC_MAC(BlockCipher* cipher);
+ private:
+ void add_data(const uint8_t[], size_t) override;
+ void final_result(uint8_t[]) override;
+ void key_schedule(const uint8_t[], size_t) override;
+
+ std::unique_ptr<BlockCipher> m_cipher;
+ secure_vector<uint8_t> m_state;
+ size_t m_position = 0;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/mac/cbc_mac/info.txt b/comm/third_party/botan/src/lib/mac/cbc_mac/info.txt
new file mode 100644
index 0000000000..994a63872b
--- /dev/null
+++ b/comm/third_party/botan/src/lib/mac/cbc_mac/info.txt
@@ -0,0 +1,7 @@
+<defines>
+CBC_MAC -> 20131128
+</defines>
+
+<requires>
+block
+</requires>
diff --git a/comm/third_party/botan/src/lib/mac/cmac/cmac.cpp b/comm/third_party/botan/src/lib/mac/cmac/cmac.cpp
new file mode 100644
index 0000000000..38752471dd
--- /dev/null
+++ b/comm/third_party/botan/src/lib/mac/cmac/cmac.cpp
@@ -0,0 +1,139 @@
+/*
+* CMAC
+* (C) 1999-2007,2014 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/cmac.h>
+#include <botan/exceptn.h>
+#include <botan/internal/poly_dbl.h>
+
+namespace Botan {
+
+/*
+* Perform CMAC's multiplication in GF(2^n)
+*/
+secure_vector<uint8_t> CMAC::poly_double(const secure_vector<uint8_t>& in)
+ {
+ secure_vector<uint8_t> out(in.size());
+ poly_double_n(out.data(), in.data(), out.size());
+ return out;
+ }
+
+/*
+* Update an CMAC Calculation
+*/
+void CMAC::add_data(const uint8_t input[], size_t length)
+ {
+ const size_t bs = output_length();
+
+ buffer_insert(m_buffer, m_position, input, length);
+ if(m_position + length > bs)
+ {
+ xor_buf(m_state, m_buffer, bs);
+ m_cipher->encrypt(m_state);
+ input += (bs - m_position);
+ length -= (bs - m_position);
+ while(length > bs)
+ {
+ xor_buf(m_state, input, bs);
+ m_cipher->encrypt(m_state);
+ input += bs;
+ length -= bs;
+ }
+ copy_mem(m_buffer.data(), input, length);
+ m_position = 0;
+ }
+ m_position += length;
+ }
+
+/*
+* Finalize an CMAC Calculation
+*/
+void CMAC::final_result(uint8_t mac[])
+ {
+ xor_buf(m_state, m_buffer, m_position);
+
+ if(m_position == output_length())
+ {
+ xor_buf(m_state, m_B, output_length());
+ }
+ else
+ {
+ m_state[m_position] ^= 0x80;
+ xor_buf(m_state, m_P, output_length());
+ }
+
+ m_cipher->encrypt(m_state);
+
+ copy_mem(mac, m_state.data(), output_length());
+
+ zeroise(m_state);
+ zeroise(m_buffer);
+ m_position = 0;
+ }
+
+/*
+* CMAC Key Schedule
+*/
+void CMAC::key_schedule(const uint8_t key[], size_t length)
+ {
+ clear();
+ m_cipher->set_key(key, length);
+ m_cipher->encrypt(m_B);
+ poly_double_n(m_B.data(), m_B.size());
+ poly_double_n(m_P.data(), m_B.data(), m_P.size());
+ }
+
+/*
+* Clear memory of sensitive data
+*/
+void CMAC::clear()
+ {
+ m_cipher->clear();
+ zeroise(m_state);
+ zeroise(m_buffer);
+ zeroise(m_B);
+ zeroise(m_P);
+ m_position = 0;
+ }
+
+/*
+* Return the name of this type
+*/
+std::string CMAC::name() const
+ {
+ return "CMAC(" + m_cipher->name() + ")";
+ }
+
+/*
+* Return a clone of this object
+*/
+MessageAuthenticationCode* CMAC::clone() const
+ {
+ return new CMAC(m_cipher->clone());
+ }
+
+/*
+* CMAC Constructor
+*/
+CMAC::CMAC(BlockCipher* cipher) :
+ m_cipher(cipher),
+ m_block_size(m_cipher->block_size())
+ {
+ if(poly_double_supported_size(m_block_size) == false)
+ {
+ throw Invalid_Argument("CMAC cannot use the " +
+ std::to_string(m_block_size * 8) +
+ " bit cipher " + m_cipher->name());
+ }
+
+ m_state.resize(output_length());
+ m_buffer.resize(output_length());
+ m_B.resize(output_length());
+ m_P.resize(output_length());
+ m_position = 0;
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/mac/cmac/cmac.h b/comm/third_party/botan/src/lib/mac/cmac/cmac.h
new file mode 100644
index 0000000000..f73167590d
--- /dev/null
+++ b/comm/third_party/botan/src/lib/mac/cmac/cmac.h
@@ -0,0 +1,67 @@
+/*
+* CMAC
+* (C) 1999-2007,2014 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_CMAC_H_
+#define BOTAN_CMAC_H_
+
+#include <botan/mac.h>
+#include <botan/block_cipher.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(cmac.h)
+
+namespace Botan {
+
+/**
+* CMAC, also known as OMAC1
+*/
+class BOTAN_PUBLIC_API(2,0) CMAC final : public MessageAuthenticationCode
+ {
+ public:
+ std::string name() const override;
+ size_t output_length() const override { return m_block_size; }
+ MessageAuthenticationCode* clone() const override;
+
+ void clear() override;
+
+ Key_Length_Specification key_spec() const override
+ {
+ return m_cipher->key_spec();
+ }
+
+ /**
+ * CMAC's polynomial doubling operation
+ *
+ * This function was only exposed for use elsewhere in the library, but it is not
+ * longer used. This function will be removed in a future release.
+ *
+ * @param in the input
+ */
+ static secure_vector<uint8_t>
+ BOTAN_DEPRECATED("This was only for internal use and is no longer used")
+ poly_double(const secure_vector<uint8_t>& in);
+
+ /**
+ * @param cipher the block cipher to use
+ */
+ explicit CMAC(BlockCipher* cipher);
+
+ CMAC(const CMAC&) = delete;
+ CMAC& operator=(const CMAC&) = delete;
+ private:
+ void add_data(const uint8_t[], size_t) override;
+ void final_result(uint8_t[]) override;
+ void key_schedule(const uint8_t[], size_t) override;
+
+ std::unique_ptr<BlockCipher> m_cipher;
+ secure_vector<uint8_t> m_buffer, m_state, m_B, m_P;
+ const size_t m_block_size;
+ size_t m_position;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/mac/cmac/info.txt b/comm/third_party/botan/src/lib/mac/cmac/info.txt
new file mode 100644
index 0000000000..d78b3851ee
--- /dev/null
+++ b/comm/third_party/botan/src/lib/mac/cmac/info.txt
@@ -0,0 +1,8 @@
+<defines>
+CMAC -> 20131128
+</defines>
+
+<requires>
+block
+poly_dbl
+</requires>
diff --git a/comm/third_party/botan/src/lib/mac/gmac/gmac.cpp b/comm/third_party/botan/src/lib/mac/gmac/gmac.cpp
new file mode 100644
index 0000000000..6b162857f3
--- /dev/null
+++ b/comm/third_party/botan/src/lib/mac/gmac/gmac.cpp
@@ -0,0 +1,134 @@
+/*
+ * GMAC
+ * (C) 2016 Matthias Gierlings, René Korthaus
+ * (C) 2017 Jack Lloyd
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ */
+
+#include <botan/gmac.h>
+#include <botan/ghash.h>
+#include <botan/exceptn.h>
+#include <botan/block_cipher.h>
+
+namespace Botan {
+
+GMAC::GMAC(BlockCipher* cipher) :
+ m_cipher(cipher),
+ m_ghash(new GHASH),
+ m_aad_buf(GCM_BS),
+ m_aad_buf_pos(0),
+ m_initialized(false)
+ {
+ }
+
+void GMAC::clear()
+ {
+ m_cipher->clear();
+ m_ghash->clear();
+ zeroise(m_aad_buf);
+ m_aad_buf_pos = 0;
+ m_initialized = false;
+ }
+
+GMAC::~GMAC() { /* for unique_ptr */ }
+
+Key_Length_Specification GMAC::key_spec() const
+ {
+ return m_cipher->key_spec();
+ }
+
+std::string GMAC::name() const
+ {
+ return "GMAC(" + m_cipher->name() + ")";
+ }
+
+size_t GMAC::output_length() const
+ {
+ return GCM_BS;
+ }
+
+void GMAC::add_data(const uint8_t input[], size_t size)
+ {
+ if(m_aad_buf_pos > 0)
+ {
+ const size_t taking = std::min(GCM_BS - m_aad_buf_pos, size);
+ copy_mem(&m_aad_buf[m_aad_buf_pos], input, taking);
+ m_aad_buf_pos += taking;
+ input += taking;
+ size -= taking;
+
+ if(m_aad_buf_pos == GCM_BS)
+ {
+ m_ghash->update_associated_data(m_aad_buf.data(), GCM_BS);
+ m_aad_buf_pos = 0;
+ }
+ }
+
+ const size_t left_over = size % GCM_BS;
+ const size_t full_blocks = size - left_over;
+ m_ghash->update_associated_data(input, full_blocks);
+ input += full_blocks;
+
+ if(left_over > 0)
+ {
+ copy_mem(&m_aad_buf[m_aad_buf_pos], input, left_over);
+ m_aad_buf_pos += left_over;
+ }
+ }
+
+void GMAC::key_schedule(const uint8_t key[], size_t size)
+ {
+ clear();
+ m_cipher->set_key(key, size);
+
+ secure_vector<uint8_t> H(GCM_BS);
+ m_cipher->encrypt(H);
+ m_ghash->set_key(H);
+ }
+
+void GMAC::start_msg(const uint8_t nonce[], size_t nonce_len)
+ {
+ secure_vector<uint8_t> y0(GCM_BS);
+
+ if(nonce_len == 12)
+ {
+ copy_mem(y0.data(), nonce, nonce_len);
+ y0[GCM_BS - 1] = 1;
+ }
+ else
+ {
+ m_ghash->ghash_update(y0, nonce, nonce_len);
+ m_ghash->add_final_block(y0, 0, nonce_len);
+ }
+
+ secure_vector<uint8_t> m_enc_y0(GCM_BS);
+ m_cipher->encrypt(y0.data(), m_enc_y0.data());
+ m_ghash->start(m_enc_y0.data(), m_enc_y0.size());
+ m_initialized = true;
+ }
+
+void GMAC::final_result(uint8_t mac[])
+ {
+ // 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)
+ throw Invalid_State("GMAC was not used with a fresh nonce");
+
+ // process the rest of the aad buffer. Even if it is a partial block only
+ // ghash_update will process it properly.
+ if(m_aad_buf_pos > 0)
+ {
+ m_ghash->update_associated_data(m_aad_buf.data(), m_aad_buf_pos);
+ }
+
+ m_ghash->final(mac, output_length());
+ clear();
+ }
+
+MessageAuthenticationCode* GMAC::clone() const
+ {
+ return new GMAC(m_cipher->clone());
+ }
+}
diff --git a/comm/third_party/botan/src/lib/mac/gmac/gmac.h b/comm/third_party/botan/src/lib/mac/gmac/gmac.h
new file mode 100644
index 0000000000..b78aeec6f1
--- /dev/null
+++ b/comm/third_party/botan/src/lib/mac/gmac/gmac.h
@@ -0,0 +1,64 @@
+/*
+ * GMAC
+ * (C) 2016 Matthias Gierlings, René Korthaus
+ * (C) 2017 Jack Lloyd
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ */
+
+#ifndef BOTAN_GMAC_H_
+#define BOTAN_GMAC_H_
+
+#include <botan/mac.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(gmac.h)
+
+namespace Botan {
+
+class BlockCipher;
+class GHASH;
+
+/**
+* GMAC
+*
+* GMAC requires a unique initialization vector be used for each message.
+* This must be provided via the MessageAuthenticationCode::start() API
+*/
+class BOTAN_PUBLIC_API(2,0) GMAC final : public MessageAuthenticationCode
+ {
+ public:
+ void clear() override;
+ std::string name() const override;
+ size_t output_length() const override;
+ MessageAuthenticationCode* clone() const override;
+
+ Key_Length_Specification key_spec() const override;
+
+ /**
+ * Creates a new GMAC instance.
+ *
+ * @param cipher the underlying block cipher to use
+ */
+ explicit GMAC(BlockCipher* cipher);
+
+ GMAC(const GMAC&) = delete;
+ GMAC& operator=(const GMAC&) = delete;
+
+ ~GMAC();
+
+ private:
+ void add_data(const uint8_t[], size_t) override;
+ void final_result(uint8_t[]) override;
+ void start_msg(const uint8_t nonce[], size_t nonce_len) override;
+ void key_schedule(const uint8_t key[], size_t size) override;
+
+ static const size_t GCM_BS = 16;
+ std::unique_ptr<BlockCipher> m_cipher;
+ std::unique_ptr<GHASH> m_ghash;
+ secure_vector<uint8_t> m_aad_buf;
+ size_t m_aad_buf_pos;
+ bool m_initialized;
+ };
+
+}
+#endif
diff --git a/comm/third_party/botan/src/lib/mac/gmac/info.txt b/comm/third_party/botan/src/lib/mac/gmac/info.txt
new file mode 100644
index 0000000000..cc473feb90
--- /dev/null
+++ b/comm/third_party/botan/src/lib/mac/gmac/info.txt
@@ -0,0 +1,8 @@
+<defines>
+GMAC -> 20160207
+</defines>
+
+<requires>
+ghash
+block
+</requires>
diff --git a/comm/third_party/botan/src/lib/mac/hmac/hmac.cpp b/comm/third_party/botan/src/lib/mac/hmac/hmac.cpp
new file mode 100644
index 0000000000..eada1e1bcf
--- /dev/null
+++ b/comm/third_party/botan/src/lib/mac/hmac/hmac.cpp
@@ -0,0 +1,150 @@
+/*
+* HMAC
+* (C) 1999-2007,2014,2020 Jack Lloyd
+* 2007 Yves Jerschow
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/hmac.h>
+#include <botan/internal/ct_utils.h>
+
+namespace Botan {
+
+/*
+* Update a HMAC Calculation
+*/
+void HMAC::add_data(const uint8_t input[], size_t length)
+ {
+ verify_key_set(m_ikey.empty() == false);
+ m_hash->update(input, length);
+ }
+
+/*
+* Finalize a HMAC Calculation
+*/
+void HMAC::final_result(uint8_t mac[])
+ {
+ verify_key_set(m_okey.empty() == false);
+ m_hash->final(mac);
+ m_hash->update(m_okey);
+ m_hash->update(mac, m_hash_output_length);
+ m_hash->final(mac);
+ m_hash->update(m_ikey);
+ }
+
+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);
+ }
+
+size_t HMAC::output_length() const
+ {
+ return m_hash_output_length;
+ }
+
+/*
+* HMAC Key Schedule
+*/
+void HMAC::key_schedule(const uint8_t key[], size_t length)
+ {
+ const uint8_t ipad = 0x36;
+ const uint8_t opad = 0x5C;
+
+ m_hash->clear();
+
+ m_ikey.resize(m_hash_block_size);
+ m_okey.resize(m_hash_block_size);
+
+ clear_mem(m_ikey.data(), m_ikey.size());
+ clear_mem(m_okey.data(), m_okey.size());
+
+ /*
+ * Sometimes the HMAC key length itself is sensitive, as with PBKDF2 where it
+ * reveals the length of the passphrase. Make some attempt to hide this to
+ * side channels. Clearly if the secret is longer than the block size then the
+ * branch to hash first reveals that. In addition, counting the number of
+ * compression functions executed reveals the size at the granularity of the
+ * hash function's block size.
+ *
+ * The greater concern is for smaller keys; being able to detect when a
+ * passphrase is say 4 bytes may assist choosing weaker targets. Even though
+ * the loop bounds are constant, we can only actually read key[0..length] so
+ * it doesn't seem possible to make this computation truly constant time.
+ *
+ * We don't mind leaking if the length is exactly zero since that's
+ * trivial to simply check.
+ */
+
+ if(length > m_hash_block_size)
+ {
+ m_hash->update(key, length);
+ m_hash->final(m_ikey.data());
+ }
+ else if(length > 0)
+ {
+ for(size_t i = 0, i_mod_length = 0; i != m_hash_block_size; ++i)
+ {
+ /*
+ access key[i % length] but avoiding division due to variable
+ time computation on some processors.
+ */
+ auto needs_reduction = CT::Mask<size_t>::is_lte(length, i_mod_length);
+ i_mod_length = needs_reduction.select(0, i_mod_length);
+ const uint8_t kb = key[i_mod_length];
+
+ auto in_range = CT::Mask<size_t>::is_lt(i, length);
+ m_ikey[i] = static_cast<uint8_t>(in_range.if_set_return(kb));
+ i_mod_length += 1;
+ }
+ }
+
+ for(size_t i = 0; i != m_hash_block_size; ++i)
+ {
+ m_ikey[i] ^= ipad;
+ m_okey[i] = m_ikey[i] ^ ipad ^ opad;
+ }
+
+ m_hash->update(m_ikey);
+ }
+
+/*
+* Clear memory of sensitive data
+*/
+void HMAC::clear()
+ {
+ m_hash->clear();
+ zap(m_ikey);
+ zap(m_okey);
+ }
+
+/*
+* Return the name of this type
+*/
+std::string HMAC::name() const
+ {
+ return "HMAC(" + m_hash->name() + ")";
+ }
+
+/*
+* Return a clone of this object
+*/
+MessageAuthenticationCode* HMAC::clone() const
+ {
+ return new HMAC(m_hash->clone());
+ }
+
+/*
+* HMAC Constructor
+*/
+HMAC::HMAC(HashFunction* hash) :
+ m_hash(hash),
+ m_hash_output_length(m_hash->output_length()),
+ m_hash_block_size(m_hash->hash_block_size())
+ {
+ BOTAN_ARG_CHECK(m_hash_block_size >= m_hash_output_length,
+ "HMAC is not compatible with this hash function");
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/mac/hmac/hmac.h b/comm/third_party/botan/src/lib/mac/hmac/hmac.h
new file mode 100644
index 0000000000..1f2f4227d4
--- /dev/null
+++ b/comm/third_party/botan/src/lib/mac/hmac/hmac.h
@@ -0,0 +1,52 @@
+/*
+* HMAC
+* (C) 1999-2007,2014 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_HMAC_H_
+#define BOTAN_HMAC_H_
+
+#include <botan/mac.h>
+#include <botan/hash.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(hmac.h)
+
+namespace Botan {
+
+/**
+* HMAC
+*/
+class BOTAN_PUBLIC_API(2,0) HMAC final : public MessageAuthenticationCode
+ {
+ public:
+ void clear() override;
+ std::string name() const override;
+ MessageAuthenticationCode* clone() const override;
+
+ size_t output_length() const override;
+
+ Key_Length_Specification key_spec() const override;
+
+ /**
+ * @param hash the hash to use for HMACing
+ */
+ explicit HMAC(HashFunction* hash);
+
+ HMAC(const HMAC&) = delete;
+ HMAC& operator=(const HMAC&) = delete;
+ private:
+ void add_data(const uint8_t[], size_t) override;
+ void final_result(uint8_t[]) override;
+ void key_schedule(const uint8_t[], size_t) override;
+
+ std::unique_ptr<HashFunction> m_hash;
+ secure_vector<uint8_t> m_ikey, m_okey;
+ size_t m_hash_output_length;
+ size_t m_hash_block_size;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/mac/hmac/info.txt b/comm/third_party/botan/src/lib/mac/hmac/info.txt
new file mode 100644
index 0000000000..50dc665dc9
--- /dev/null
+++ b/comm/third_party/botan/src/lib/mac/hmac/info.txt
@@ -0,0 +1,7 @@
+<defines>
+HMAC -> 20131128
+</defines>
+
+<requires>
+hash
+</requires>
diff --git a/comm/third_party/botan/src/lib/mac/info.txt b/comm/third_party/botan/src/lib/mac/info.txt
new file mode 100644
index 0000000000..7aef92b879
--- /dev/null
+++ b/comm/third_party/botan/src/lib/mac/info.txt
@@ -0,0 +1,7 @@
+<defines>
+MAC -> 20150626
+</defines>
+
+<header:public>
+mac.h
+</header:public>
diff --git a/comm/third_party/botan/src/lib/mac/mac.cpp b/comm/third_party/botan/src/lib/mac/mac.cpp
new file mode 100644
index 0000000000..4c3fc5230e
--- /dev/null
+++ b/comm/third_party/botan/src/lib/mac/mac.cpp
@@ -0,0 +1,171 @@
+/*
+* Message Authentication Code base class
+* (C) 1999-2008 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/mac.h>
+#include <botan/exceptn.h>
+#include <botan/scan_name.h>
+#include <botan/mem_ops.h>
+
+#if defined(BOTAN_HAS_CBC_MAC)
+ #include <botan/cbc_mac.h>
+#endif
+
+#if defined(BOTAN_HAS_CMAC)
+ #include <botan/cmac.h>
+#endif
+
+#if defined(BOTAN_HAS_GMAC)
+ #include <botan/gmac.h>
+ #include <botan/block_cipher.h>
+#endif
+
+#if defined(BOTAN_HAS_HMAC)
+ #include <botan/hmac.h>
+ #include <botan/hash.h>
+#endif
+
+#if defined(BOTAN_HAS_POLY1305)
+ #include <botan/poly1305.h>
+#endif
+
+#if defined(BOTAN_HAS_SIPHASH)
+ #include <botan/siphash.h>
+#endif
+
+#if defined(BOTAN_HAS_ANSI_X919_MAC)
+ #include <botan/x919_mac.h>
+#endif
+
+namespace Botan {
+
+std::unique_ptr<MessageAuthenticationCode>
+MessageAuthenticationCode::create(const std::string& algo_spec,
+ const std::string& provider)
+ {
+ const SCAN_Name req(algo_spec);
+
+#if defined(BOTAN_HAS_GMAC)
+ if(req.algo_name() == "GMAC" && req.arg_count() == 1)
+ {
+ if(provider.empty() || provider == "base")
+ {
+ if(auto bc = BlockCipher::create(req.arg(0)))
+ return std::unique_ptr<MessageAuthenticationCode>(new GMAC(bc.release()));
+ }
+ }
+#endif
+
+#if defined(BOTAN_HAS_HMAC)
+ if(req.algo_name() == "HMAC" && req.arg_count() == 1)
+ {
+ // TODO OpenSSL
+ if(provider.empty() || provider == "base")
+ {
+ if(auto h = HashFunction::create(req.arg(0)))
+ return std::unique_ptr<MessageAuthenticationCode>(new HMAC(h.release()));
+ }
+ }
+#endif
+
+#if defined(BOTAN_HAS_POLY1305)
+ if(req.algo_name() == "Poly1305" && req.arg_count() == 0)
+ {
+ if(provider.empty() || provider == "base")
+ return std::unique_ptr<MessageAuthenticationCode>(new Poly1305);
+ }
+#endif
+
+#if defined(BOTAN_HAS_SIPHASH)
+ if(req.algo_name() == "SipHash")
+ {
+ if(provider.empty() || provider == "base")
+ {
+ return std::unique_ptr<MessageAuthenticationCode>(
+ new SipHash(req.arg_as_integer(0, 2), req.arg_as_integer(1, 4)));
+ }
+ }
+#endif
+
+#if defined(BOTAN_HAS_CMAC)
+ if((req.algo_name() == "CMAC" || req.algo_name() == "OMAC") && req.arg_count() == 1)
+ {
+ // TODO: OpenSSL CMAC
+ if(provider.empty() || provider == "base")
+ {
+ if(auto bc = BlockCipher::create(req.arg(0)))
+ return std::unique_ptr<MessageAuthenticationCode>(new CMAC(bc.release()));
+ }
+ }
+#endif
+
+
+#if defined(BOTAN_HAS_CBC_MAC)
+ if(req.algo_name() == "CBC-MAC" && req.arg_count() == 1)
+ {
+ if(provider.empty() || provider == "base")
+ {
+ if(auto bc = BlockCipher::create(req.arg(0)))
+ return std::unique_ptr<MessageAuthenticationCode>(new CBC_MAC(bc.release()));
+ }
+ }
+#endif
+
+#if defined(BOTAN_HAS_ANSI_X919_MAC)
+ if(req.algo_name() == "X9.19-MAC")
+ {
+ if(provider.empty() || provider == "base")
+ {
+ return std::unique_ptr<MessageAuthenticationCode>(new ANSI_X919_MAC);
+ }
+ }
+#endif
+
+ BOTAN_UNUSED(req);
+ BOTAN_UNUSED(provider);
+
+ return nullptr;
+ }
+
+std::vector<std::string>
+MessageAuthenticationCode::providers(const std::string& algo_spec)
+ {
+ return probe_providers_of<MessageAuthenticationCode>(algo_spec, {"base", "openssl"});
+ }
+
+//static
+std::unique_ptr<MessageAuthenticationCode>
+MessageAuthenticationCode::create_or_throw(const std::string& algo,
+ const std::string& provider)
+ {
+ if(auto mac = MessageAuthenticationCode::create(algo, provider))
+ {
+ return mac;
+ }
+ throw Lookup_Error("MAC", algo, provider);
+ }
+
+void MessageAuthenticationCode::start_msg(const uint8_t nonce[], size_t nonce_len)
+ {
+ BOTAN_UNUSED(nonce);
+ if(nonce_len > 0)
+ throw Invalid_IV_Length(name(), nonce_len);
+ }
+
+/*
+* Default (deterministic) MAC verification operation
+*/
+bool MessageAuthenticationCode::verify_mac(const uint8_t mac[], size_t length)
+ {
+ secure_vector<uint8_t> our_mac = final();
+
+ if(our_mac.size() != length)
+ return false;
+
+ return constant_time_compare(our_mac.data(), mac, length);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/mac/mac.h b/comm/third_party/botan/src/lib/mac/mac.h
new file mode 100644
index 0000000000..de30b7dbb2
--- /dev/null
+++ b/comm/third_party/botan/src/lib/mac/mac.h
@@ -0,0 +1,143 @@
+/*
+* Base class for message authentiction codes
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_MESSAGE_AUTH_CODE_BASE_H_
+#define BOTAN_MESSAGE_AUTH_CODE_BASE_H_
+
+#include <botan/buf_comp.h>
+#include <botan/sym_algo.h>
+#include <string>
+#include <memory>
+
+namespace Botan {
+
+/**
+* This class represents Message Authentication Code (MAC) objects.
+*/
+class BOTAN_PUBLIC_API(2,0) MessageAuthenticationCode : public Buffered_Computation,
+ public SymmetricAlgorithm
+ {
+ public:
+ /**
+ * Create an instance based on a name
+ * If provider is empty then best available is chosen.
+ * @param algo_spec algorithm name
+ * @param provider provider implementation to use
+ * @return a null pointer if the algo/provider combination cannot be found
+ */
+ static std::unique_ptr<MessageAuthenticationCode>
+ create(const std::string& algo_spec,
+ const std::string& provider = "");
+
+ /*
+ * Create an instance based on a name
+ * If provider is empty then best available is chosen.
+ * @param algo_spec algorithm name
+ * @param provider provider implementation to use
+ * Throws a Lookup_Error if algo/provider combination cannot be found
+ */
+ static std::unique_ptr<MessageAuthenticationCode>
+ create_or_throw(const std::string& algo_spec,
+ const std::string& provider = "");
+
+ /**
+ * @return list of available providers for this algorithm, empty if not available
+ */
+ static std::vector<std::string> providers(const std::string& algo_spec);
+
+ virtual ~MessageAuthenticationCode() = default;
+
+ /**
+ * Prepare for processing a message under the specified nonce
+ *
+ * Most MACs neither require nor support a nonce; for these algorithms
+ * calling `start_msg` is optional and calling it with anything other than
+ * an empty string is an error. One MAC which *requires* a per-message
+ * nonce be specified is GMAC.
+ *
+ * @param nonce the message nonce bytes
+ * @param nonce_len the size of len in bytes
+ * Default implementation simply rejects all non-empty nonces
+ * since most hash/MAC algorithms do not support randomization
+ */
+ virtual void start_msg(const uint8_t nonce[], size_t nonce_len);
+
+ /**
+ * Begin processing a message with a nonce
+ *
+ * @param nonce the per message nonce
+ */
+ template<typename Alloc>
+ void start(const std::vector<uint8_t, Alloc>& nonce)
+ {
+ start_msg(nonce.data(), nonce.size());
+ }
+
+ /**
+ * Begin processing a message.
+ * @param nonce the per message nonce
+ * @param nonce_len length of nonce
+ */
+ void start(const uint8_t nonce[], size_t nonce_len)
+ {
+ start_msg(nonce, nonce_len);
+ }
+
+ /**
+ * Begin processing a message.
+ */
+ void start()
+ {
+ return start_msg(nullptr, 0);
+ }
+
+ /**
+ * Verify a MAC.
+ * @param in the MAC to verify as a byte array
+ * @param length the length of param in
+ * @return true if the MAC is valid, false otherwise
+ */
+ virtual bool verify_mac(const uint8_t in[], size_t length);
+
+ /**
+ * Verify a MAC.
+ * @param in the MAC to verify as a byte array
+ * @return true if the MAC is valid, false otherwise
+ */
+ virtual bool verify_mac(const std::vector<uint8_t>& in)
+ {
+ return verify_mac(in.data(), in.size());
+ }
+
+ /**
+ * Verify a MAC.
+ * @param in the MAC to verify as a byte array
+ * @return true if the MAC is valid, false otherwise
+ */
+ virtual bool verify_mac(const secure_vector<uint8_t>& in)
+ {
+ return verify_mac(in.data(), in.size());
+ }
+
+ /**
+ * Get a new object representing the same algorithm as *this
+ */
+ virtual MessageAuthenticationCode* clone() const = 0;
+
+ /**
+ * @return provider information about this implementation. Default is "base",
+ * might also return "sse2", "avx2", "openssl", or some other arbitrary string.
+ */
+ virtual std::string provider() const { return "base"; }
+
+ };
+
+typedef MessageAuthenticationCode MAC;
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/mac/poly1305/info.txt b/comm/third_party/botan/src/lib/mac/poly1305/info.txt
new file mode 100644
index 0000000000..868f97241d
--- /dev/null
+++ b/comm/third_party/botan/src/lib/mac/poly1305/info.txt
@@ -0,0 +1,7 @@
+<defines>
+POLY1305 -> 20141227
+</defines>
+
+<header:public>
+poly1305.h
+</header:public>
diff --git a/comm/third_party/botan/src/lib/mac/poly1305/poly1305.cpp b/comm/third_party/botan/src/lib/mac/poly1305/poly1305.cpp
new file mode 100644
index 0000000000..333a21a1a6
--- /dev/null
+++ b/comm/third_party/botan/src/lib/mac/poly1305/poly1305.cpp
@@ -0,0 +1,211 @@
+/*
+* Derived from poly1305-donna-64.h by Andrew Moon <liquidsun@gmail.com>
+* in https://github.com/floodyberry/poly1305-donna
+*
+* (C) 2014 Andrew Moon
+* (C) 2014 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/poly1305.h>
+#include <botan/loadstor.h>
+#include <botan/mul128.h>
+#include <botan/internal/donna128.h>
+#include <botan/internal/ct_utils.h>
+
+namespace Botan {
+
+namespace {
+
+void poly1305_init(secure_vector<uint64_t>& X, const uint8_t key[32])
+ {
+ /* r &= 0xffffffc0ffffffc0ffffffc0fffffff */
+ const uint64_t t0 = load_le<uint64_t>(key, 0);
+ const uint64_t t1 = load_le<uint64_t>(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<uint64_t>(key, 2);
+ X[7] = load_le<uint64_t>(key, 3);
+ }
+
+void poly1305_blocks(secure_vector<uint64_t>& X, const uint8_t *m, size_t blocks, bool is_final = false)
+ {
+#if !defined(BOTAN_TARGET_HAS_NATIVE_UINT128)
+ typedef donna128 uint128_t;
+#endif
+
+ const uint64_t hibit = is_final ? 0 : (static_cast<uint64_t>(1) << 40); /* 1 << 128 */
+
+ 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];
+
+ const uint64_t s1 = r1 * 20;
+ const uint64_t s2 = r2 * 20;
+
+ for(size_t i = 0; i != blocks; ++i)
+ {
+ const uint64_t t0 = load_le<uint64_t>(m, 0);
+ const uint64_t t1 = load_le<uint64_t>(m, 1);
+
+ h0 += (( t0 ) & M44);
+ h1 += (((t0 >> 44) | (t1 << 20)) & M44);
+ h2 += (((t1 >> 24) ) & M42) | hibit;
+
+ 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);
+
+ 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 = d0 & M44;
+ h1 = d1 & M44;
+ h2 = d2 & M42;
+
+ h0 += c2 * 5;
+ h1 += carry_shift(h0, 44);
+ h0 = h0 & M44;
+
+ m += 16;
+ }
+
+ X[3+0] = h0;
+ X[3+1] = h1;
+ X[3+2] = h2;
+ }
+
+void poly1305_finish(secure_vector<uint64_t>& X, uint8_t mac[16])
+ {
+ const uint64_t M44 = 0xFFFFFFFFFFF;
+ const 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 c;
+ c = (h1 >> 44); h1 &= M44;
+ h2 += c; c = (h2 >> 42); h2 &= M42;
+ h0 += c * 5; c = (h0 >> 44); h0 &= M44;
+ h1 += c; c = (h1 >> 44); h1 &= M44;
+ h2 += c; c = (h2 >> 42); h2 &= M42;
+ h0 += c * 5; c = (h0 >> 44); h0 &= M44;
+ h1 += c;
+
+ /* compute h + -p */
+ uint64_t g0 = h0 + 5; c = (g0 >> 44); g0 &= M44;
+ uint64_t g1 = h1 + c; c = (g1 >> 44); g1 &= M44;
+ uint64_t g2 = h2 + c - (static_cast<uint64_t>(1) << 42);
+
+ /* select h if h < p, or h + -p if h >= p */
+ const auto c_mask = CT::Mask<uint64_t>::expand(c);
+ h0 = c_mask.select(g0, h0);
+ h1 = c_mask.select(g1, h1);
+ h2 = c_mask.select(g2, h2);
+
+ /* h = (h + pad) */
+ const uint64_t t0 = X[6];
+ const uint64_t t1 = X[7];
+
+ h0 += (( t0 ) & M44) ; c = (h0 >> 44); h0 &= M44;
+ h1 += (((t0 >> 44) | (t1 << 20)) & M44) + c; c = (h1 >> 44); h1 &= M44;
+ h2 += (((t1 >> 24) ) & M42) + c; h2 &= M42;
+
+ /* mac = h % (2^128) */
+ h0 = ((h0 ) | (h1 << 44));
+ h1 = ((h1 >> 20) | (h2 << 24));
+
+ store_le(mac, h0, h1);
+
+ /* zero out the state */
+ clear_mem(X.data(), X.size());
+ }
+
+}
+
+void Poly1305::clear()
+ {
+ zap(m_poly);
+ zap(m_buf);
+ m_buf_pos = 0;
+ }
+
+void Poly1305::key_schedule(const uint8_t key[], size_t)
+ {
+ m_buf_pos = 0;
+ m_buf.resize(16);
+ m_poly.resize(8);
+
+ poly1305_init(m_poly, key);
+ }
+
+void Poly1305::add_data(const uint8_t input[], size_t length)
+ {
+ verify_key_set(m_poly.size() == 8);
+
+ if(m_buf_pos)
+ {
+ buffer_insert(m_buf, m_buf_pos, input, length);
+
+ if(m_buf_pos + length >= m_buf.size())
+ {
+ poly1305_blocks(m_poly, m_buf.data(), 1);
+ input += (m_buf.size() - m_buf_pos);
+ length -= (m_buf.size() - m_buf_pos);
+ m_buf_pos = 0;
+ }
+ }
+
+ const size_t full_blocks = length / m_buf.size();
+ const size_t remaining = length % m_buf.size();
+
+ if(full_blocks)
+ poly1305_blocks(m_poly, input, full_blocks);
+
+ buffer_insert(m_buf, m_buf_pos, input + full_blocks * m_buf.size(), remaining);
+ m_buf_pos += remaining;
+ }
+
+void Poly1305::final_result(uint8_t out[])
+ {
+ verify_key_set(m_poly.size() == 8);
+
+ if(m_buf_pos != 0)
+ {
+ m_buf[m_buf_pos] = 1;
+ const size_t len = m_buf.size() - m_buf_pos - 1;
+ if (len > 0)
+ {
+ clear_mem(&m_buf[m_buf_pos+1], len);
+ }
+ poly1305_blocks(m_poly, m_buf.data(), 1, true);
+ }
+
+ poly1305_finish(m_poly, out);
+
+ m_poly.clear();
+ m_buf_pos = 0;
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/mac/poly1305/poly1305.h b/comm/third_party/botan/src/lib/mac/poly1305/poly1305.h
new file mode 100644
index 0000000000..fdd01ecd1a
--- /dev/null
+++ b/comm/third_party/botan/src/lib/mac/poly1305/poly1305.h
@@ -0,0 +1,50 @@
+/*
+* Poly1305
+* (C) 2014 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_MAC_POLY1305_H_
+#define BOTAN_MAC_POLY1305_H_
+
+#include <botan/mac.h>
+#include <memory>
+
+BOTAN_FUTURE_INTERNAL_HEADER(poly1305.h)
+
+namespace Botan {
+
+/**
+* DJB's Poly1305
+* Important note: each key can only be used once
+*/
+class BOTAN_PUBLIC_API(2,0) Poly1305 final : public MessageAuthenticationCode
+ {
+ public:
+ std::string name() const override { return "Poly1305"; }
+
+ MessageAuthenticationCode* clone() const override { return new Poly1305; }
+
+ void clear() override;
+
+ size_t output_length() const override { return 16; }
+
+ Key_Length_Specification key_spec() const override
+ {
+ return Key_Length_Specification(32);
+ }
+
+ private:
+ void add_data(const uint8_t[], size_t) override;
+ void final_result(uint8_t[]) override;
+ void key_schedule(const uint8_t[], size_t) override;
+
+ secure_vector<uint64_t> m_poly;
+ secure_vector<uint8_t> m_buf;
+ size_t m_buf_pos = 0;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/mac/siphash/info.txt b/comm/third_party/botan/src/lib/mac/siphash/info.txt
new file mode 100644
index 0000000000..8d4c20d1a8
--- /dev/null
+++ b/comm/third_party/botan/src/lib/mac/siphash/info.txt
@@ -0,0 +1,3 @@
+<defines>
+SIPHASH -> 20150110
+</defines>
diff --git a/comm/third_party/botan/src/lib/mac/siphash/siphash.cpp b/comm/third_party/botan/src/lib/mac/siphash/siphash.cpp
new file mode 100644
index 0000000000..566d5d5def
--- /dev/null
+++ b/comm/third_party/botan/src/lib/mac/siphash/siphash.cpp
@@ -0,0 +1,136 @@
+/*
+* SipHash
+* (C) 2014,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/siphash.h>
+#include <botan/loadstor.h>
+#include <botan/rotate.h>
+
+namespace Botan {
+
+namespace {
+
+void SipRounds(uint64_t M, secure_vector<uint64_t>& V, size_t r)
+ {
+ uint64_t V0 = V[0], V1 = V[1], V2 = V[2], V3 = V[3];
+
+ V3 ^= M;
+ for(size_t i = 0; i != r; ++i)
+ {
+ V0 += V1; V2 += V3;
+ V1 = rotl<13>(V1);
+ V3 = rotl<16>(V3);
+ V1 ^= V0; V3 ^= V2;
+ V0 = rotl<32>(V0);
+
+ V2 += V1; V0 += V3;
+ V1 = rotl<17>(V1);
+ V3 = rotl<21>(V3);
+ V1 ^= V2; V3 ^= V0;
+ V2 = rotl<32>(V2);
+ }
+ V0 ^= M;
+
+ V[0] = V0; V[1] = V1; V[2] = V2; V[3] = V3;
+ }
+
+}
+
+void SipHash::add_data(const uint8_t input[], size_t length)
+ {
+ verify_key_set(m_V.empty() == false);
+
+ // SipHash counts the message length mod 256
+ m_words += static_cast<uint8_t>(length);
+
+ if(m_mbuf_pos)
+ {
+ while(length && m_mbuf_pos != 8)
+ {
+ m_mbuf = (m_mbuf >> 8) | (static_cast<uint64_t>(input[0]) << 56);
+ ++m_mbuf_pos;
+ ++input;
+ length--;
+ }
+
+ if(m_mbuf_pos == 8)
+ {
+ SipRounds(m_mbuf, m_V, m_C);
+ m_mbuf_pos = 0;
+ m_mbuf = 0;
+ }
+ }
+
+ while(length >= 8)
+ {
+ SipRounds(load_le<uint64_t>(input, 0), m_V, m_C);
+ input += 8;
+ length -= 8;
+ }
+
+ for(size_t i = 0; i != length; ++i)
+ {
+ m_mbuf = (m_mbuf >> 8) | (static_cast<uint64_t>(input[i]) << 56);
+ m_mbuf_pos++;
+ }
+ }
+
+void SipHash::final_result(uint8_t mac[])
+ {
+ verify_key_set(m_V.empty() == false);
+
+ if(m_mbuf_pos == 0)
+ {
+ m_mbuf = (static_cast<uint64_t>(m_words) << 56);
+ }
+ else if(m_mbuf_pos < 8)
+ {
+ m_mbuf = (m_mbuf >> (64-m_mbuf_pos*8)) | (static_cast<uint64_t>(m_words) << 56);
+ }
+
+ SipRounds(m_mbuf, m_V, m_C);
+
+ m_V[2] ^= 0xFF;
+ SipRounds(0, m_V, m_D);
+
+ const uint64_t X = m_V[0] ^ m_V[1] ^ m_V[2] ^ m_V[3];
+
+ store_le(X, mac);
+
+ clear();
+ }
+
+void SipHash::key_schedule(const uint8_t key[], size_t)
+ {
+ const uint64_t K0 = load_le<uint64_t>(key, 0);
+ const uint64_t K1 = load_le<uint64_t>(key, 1);
+
+ m_V.resize(4);
+ m_V[0] = K0 ^ 0x736F6D6570736575;
+ m_V[1] = K1 ^ 0x646F72616E646F6D;
+ m_V[2] = K0 ^ 0x6C7967656E657261;
+ m_V[3] = K1 ^ 0x7465646279746573;
+ }
+
+void SipHash::clear()
+ {
+ zap(m_V);
+ m_mbuf = 0;
+ m_mbuf_pos = 0;
+ m_words = 0;
+ }
+
+std::string SipHash::name() const
+ {
+ return "SipHash(" + std::to_string(m_C) + "," + std::to_string(m_D) + ")";
+ }
+
+MessageAuthenticationCode* SipHash::clone() const
+ {
+ return new SipHash(m_C, m_D);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/mac/siphash/siphash.h b/comm/third_party/botan/src/lib/mac/siphash/siphash.h
new file mode 100644
index 0000000000..d60df8dfce
--- /dev/null
+++ b/comm/third_party/botan/src/lib/mac/siphash/siphash.h
@@ -0,0 +1,47 @@
+/*
+* SipHash
+* (C) 2014,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_SIPHASH_H_
+#define BOTAN_SIPHASH_H_
+
+#include <botan/mac.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(siphash.h)
+
+namespace Botan {
+
+class BOTAN_PUBLIC_API(2,0) SipHash final : public MessageAuthenticationCode
+ {
+ public:
+ SipHash(size_t c = 2, size_t d = 4) : m_C(c), m_D(d) {}
+
+ void clear() override;
+ std::string name() const override;
+
+ MessageAuthenticationCode* clone() const override;
+
+ size_t output_length() const override { return 8; }
+
+ Key_Length_Specification key_spec() const override
+ {
+ return Key_Length_Specification(16);
+ }
+ private:
+ void add_data(const uint8_t[], size_t) override;
+ void final_result(uint8_t[]) override;
+ void key_schedule(const uint8_t[], size_t) override;
+
+ const size_t m_C, m_D;
+ secure_vector<uint64_t> m_V;
+ uint64_t m_mbuf = 0;
+ size_t m_mbuf_pos = 0;
+ uint8_t m_words = 0;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/mac/x919_mac/info.txt b/comm/third_party/botan/src/lib/mac/x919_mac/info.txt
new file mode 100644
index 0000000000..65d6cee645
--- /dev/null
+++ b/comm/third_party/botan/src/lib/mac/x919_mac/info.txt
@@ -0,0 +1,7 @@
+<defines>
+ANSI_X919_MAC -> 20131128
+</defines>
+
+<requires>
+des
+</requires>
diff --git a/comm/third_party/botan/src/lib/mac/x919_mac/x919_mac.cpp b/comm/third_party/botan/src/lib/mac/x919_mac/x919_mac.cpp
new file mode 100644
index 0000000000..0cbf087959
--- /dev/null
+++ b/comm/third_party/botan/src/lib/mac/x919_mac/x919_mac.cpp
@@ -0,0 +1,99 @@
+/*
+* ANSI X9.19 MAC
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/x919_mac.h>
+
+namespace Botan {
+
+/*
+* Update an ANSI X9.19 MAC Calculation
+*/
+void ANSI_X919_MAC::add_data(const uint8_t input[], size_t length)
+ {
+ verify_key_set(m_state.empty() == false);
+
+ size_t xored = std::min(8 - m_position, length);
+ xor_buf(&m_state[m_position], input, xored);
+ m_position += xored;
+
+ if(m_position < 8) return;
+
+ m_des1->encrypt(m_state);
+ input += xored;
+ length -= xored;
+ while(length >= 8)
+ {
+ xor_buf(m_state, input, 8);
+ m_des1->encrypt(m_state);
+ input += 8;
+ length -= 8;
+ }
+
+ xor_buf(m_state, input, length);
+ m_position = length;
+ }
+
+/*
+* Finalize an ANSI X9.19 MAC Calculation
+*/
+void ANSI_X919_MAC::final_result(uint8_t mac[])
+ {
+ if(m_position)
+ m_des1->encrypt(m_state);
+ m_des2->decrypt(m_state.data(), mac);
+ m_des1->encrypt(mac);
+ zeroise(m_state);
+ m_position = 0;
+ }
+
+/*
+* ANSI X9.19 MAC Key Schedule
+*/
+void ANSI_X919_MAC::key_schedule(const uint8_t key[], size_t length)
+ {
+ m_state.resize(8);
+
+ m_des1->set_key(key, 8);
+
+ if(length == 16)
+ key += 8;
+
+ m_des2->set_key(key, 8);
+ }
+
+/*
+* Clear memory of sensitive data
+*/
+void ANSI_X919_MAC::clear()
+ {
+ m_des1->clear();
+ m_des2->clear();
+ zap(m_state);
+ m_position = 0;
+ }
+
+std::string ANSI_X919_MAC::name() const
+ {
+ return "X9.19-MAC";
+ }
+
+MessageAuthenticationCode* ANSI_X919_MAC::clone() const
+ {
+ return new ANSI_X919_MAC;
+ }
+
+/*
+* ANSI X9.19 MAC Constructor
+*/
+ANSI_X919_MAC::ANSI_X919_MAC() :
+ m_des1(BlockCipher::create("DES")),
+ m_des2(m_des1->clone()),
+ m_position(0)
+ {
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/mac/x919_mac/x919_mac.h b/comm/third_party/botan/src/lib/mac/x919_mac/x919_mac.h
new file mode 100644
index 0000000000..3df38b9aa6
--- /dev/null
+++ b/comm/third_party/botan/src/lib/mac/x919_mac/x919_mac.h
@@ -0,0 +1,51 @@
+/*
+* ANSI X9.19 MAC
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_ANSI_X919_MAC_H_
+#define BOTAN_ANSI_X919_MAC_H_
+
+#include <botan/mac.h>
+#include <botan/block_cipher.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(x919_mac.h)
+
+namespace Botan {
+
+/**
+* DES/3DES-based MAC from ANSI X9.19
+*/
+class BOTAN_PUBLIC_API(2,0) ANSI_X919_MAC final : public MessageAuthenticationCode
+ {
+ public:
+ void clear() override;
+ std::string name() const override;
+ size_t output_length() const override { return 8; }
+
+ MessageAuthenticationCode* clone() const override;
+
+ Key_Length_Specification key_spec() const override
+ {
+ return Key_Length_Specification(8, 16, 8);
+ }
+
+ ANSI_X919_MAC();
+
+ ANSI_X919_MAC(const ANSI_X919_MAC&) = delete;
+ ANSI_X919_MAC& operator=(const ANSI_X919_MAC&) = delete;
+ private:
+ void add_data(const uint8_t[], size_t) override;
+ void final_result(uint8_t[]) override;
+ void key_schedule(const uint8_t[], size_t) override;
+
+ std::unique_ptr<BlockCipher> m_des1, m_des2;
+ secure_vector<uint8_t> m_state;
+ size_t m_position;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/math/bigint/big_code.cpp b/comm/third_party/botan/src/lib/math/bigint/big_code.cpp
new file mode 100644
index 0000000000..6eb27549e8
--- /dev/null
+++ b/comm/third_party/botan/src/lib/math/bigint/big_code.cpp
@@ -0,0 +1,200 @@
+/*
+* BigInt Encoding/Decoding
+* (C) 1999-2010,2012,2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/bigint.h>
+#include <botan/divide.h>
+#include <botan/charset.h>
+#include <botan/hex.h>
+
+namespace Botan {
+
+std::string BigInt::to_dec_string() const
+ {
+ BigInt copy = *this;
+ copy.set_sign(Positive);
+
+ uint8_t remainder;
+ std::vector<uint8_t> digits;
+
+ while(copy > 0)
+ {
+ ct_divide_u8(copy, 10, copy, remainder);
+ digits.push_back(remainder);
+ }
+
+ std::string s;
+
+ for(auto i = digits.rbegin(); i != digits.rend(); ++i)
+ {
+ s.push_back(Charset::digit2char(*i));
+ }
+
+ if(s.empty())
+ s += "0";
+
+ return s;
+ }
+
+std::string BigInt::to_hex_string() const
+ {
+ const std::vector<uint8_t> bits = BigInt::encode(*this);
+ if(bits.empty())
+ return "00";
+ else
+ return hex_encode(bits);
+ }
+
+/*
+* Encode a BigInt
+*/
+void BigInt::encode(uint8_t output[], const BigInt& n, Base base)
+ {
+ secure_vector<uint8_t> enc = n.encode_locked(base);
+ copy_mem(output, enc.data(), enc.size());
+ }
+
+namespace {
+
+std::vector<uint8_t> str_to_vector(const std::string& s)
+ {
+ std::vector<uint8_t> v(s.size());
+ std::memcpy(v.data(), s.data(), s.size());
+ return v;
+ }
+
+secure_vector<uint8_t> str_to_lvector(const std::string& s)
+ {
+ secure_vector<uint8_t> v(s.size());
+ std::memcpy(v.data(), s.data(), s.size());
+ return v;
+ }
+
+}
+
+/*
+* Encode a BigInt
+*/
+std::vector<uint8_t> BigInt::encode(const BigInt& n, Base base)
+ {
+ if(base == Binary)
+ return BigInt::encode(n);
+ else if(base == Hexadecimal)
+ return str_to_vector(n.to_hex_string());
+ else if(base == Decimal)
+ return str_to_vector(n.to_dec_string());
+ else
+ throw Invalid_Argument("Unknown BigInt encoding base");
+ }
+
+/*
+* Encode a BigInt
+*/
+secure_vector<uint8_t> BigInt::encode_locked(const BigInt& n, Base base)
+ {
+ if(base == Binary)
+ return BigInt::encode_locked(n);
+ else if(base == Hexadecimal)
+ return str_to_lvector(n.to_hex_string());
+ else if(base == Decimal)
+ return str_to_lvector(n.to_dec_string());
+ else
+ throw Invalid_Argument("Unknown BigInt encoding base");
+ }
+
+/*
+* Encode a BigInt, with leading 0s if needed
+*/
+secure_vector<uint8_t> BigInt::encode_1363(const BigInt& n, size_t bytes)
+ {
+ if(n.bytes() > bytes)
+ throw Encoding_Error("encode_1363: n is too large to encode properly");
+
+ secure_vector<uint8_t> output(bytes);
+ n.binary_encode(output.data(), output.size());
+ return output;
+ }
+
+//static
+void BigInt::encode_1363(uint8_t output[], size_t bytes, const BigInt& n)
+ {
+ if(n.bytes() > bytes)
+ throw Encoding_Error("encode_1363: n is too large to encode properly");
+
+ n.binary_encode(output, bytes);
+ }
+
+/*
+* Encode two BigInt, with leading 0s if needed, and concatenate
+*/
+secure_vector<uint8_t> BigInt::encode_fixed_length_int_pair(const BigInt& n1, const BigInt& n2, size_t bytes)
+ {
+ if(n1.bytes() > bytes || n2.bytes() > bytes)
+ throw Encoding_Error("encode_fixed_length_int_pair: values too large to encode properly");
+ secure_vector<uint8_t> output(2 * bytes);
+ n1.binary_encode(output.data() , bytes);
+ n2.binary_encode(output.data() + bytes, bytes);
+ return output;
+ }
+
+/*
+* Decode a BigInt
+*/
+BigInt BigInt::decode(const uint8_t buf[], size_t length, Base base)
+ {
+ BigInt r;
+ if(base == Binary)
+ {
+ r.binary_decode(buf, length);
+ }
+ else if(base == Hexadecimal)
+ {
+ secure_vector<uint8_t> binary;
+
+ if(length % 2)
+ {
+ // Handle lack of leading 0
+ const char buf0_with_leading_0[2] =
+ { '0', static_cast<char>(buf[0]) };
+
+ binary = hex_decode_locked(buf0_with_leading_0, 2);
+
+ 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.binary_decode(binary.data(), binary.size());
+ }
+ else if(base == Decimal)
+ {
+ for(size_t i = 0; i != length; ++i)
+ {
+ if(Charset::is_space(buf[i]))
+ continue;
+
+ if(!Charset::is_digit(buf[i]))
+ throw Invalid_Argument("BigInt::decode: "
+ "Invalid character in decimal input");
+
+ const uint8_t x = Charset::char2digit(buf[i]);
+
+ if(x >= 10)
+ throw Invalid_Argument("BigInt: Invalid decimal string");
+
+ r *= 10;
+ r += x;
+ }
+ }
+ else
+ throw Invalid_Argument("Unknown BigInt decoding method");
+ return r;
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/math/bigint/big_io.cpp b/comm/third_party/botan/src/lib/math/bigint/big_io.cpp
new file mode 100644
index 0000000000..b31315eac4
--- /dev/null
+++ b/comm/third_party/botan/src/lib/math/bigint/big_io.cpp
@@ -0,0 +1,62 @@
+/*
+* BigInt Input/Output
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/bigint.h>
+#include <istream>
+#include <ostream>
+
+namespace Botan {
+
+/*
+* Write the BigInt into a stream
+*/
+std::ostream& operator<<(std::ostream& stream, const BigInt& n)
+ {
+ size_t base = 10;
+ if(stream.flags() & std::ios::hex)
+ base = 16;
+ if(stream.flags() & std::ios::oct)
+ throw Invalid_Argument("Octal output of BigInt not supported");
+
+ if(n == 0)
+ stream.write("0", 1);
+ else
+ {
+ if(n < 0)
+ stream.write("-", 1);
+
+ std::string enc;
+
+ if(base == 10)
+ enc = n.to_dec_string();
+ else
+ enc = n.to_hex_string();
+
+ size_t skip = 0;
+ while(skip < enc.size() && enc[skip] == '0')
+ ++skip;
+ stream.write(&enc[skip], enc.size() - skip);
+ }
+ if(!stream.good())
+ throw Stream_IO_Error("BigInt output operator has failed");
+ return stream;
+ }
+
+/*
+* Read the BigInt from a stream
+*/
+std::istream& operator>>(std::istream& stream, BigInt& n)
+ {
+ std::string str;
+ std::getline(stream, str);
+ if(stream.bad() || (stream.fail() && !stream.eof()))
+ throw Stream_IO_Error("BigInt input operator has failed");
+ n = BigInt(str);
+ return stream;
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/math/bigint/big_ops2.cpp b/comm/third_party/botan/src/lib/math/bigint/big_ops2.cpp
new file mode 100644
index 0000000000..cc85f5e96d
--- /dev/null
+++ b/comm/third_party/botan/src/lib/math/bigint/big_ops2.cpp
@@ -0,0 +1,314 @@
+/*
+* (C) 1999-2007,2018 Jack Lloyd
+* 2016 Matthias Gierlings
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/bigint.h>
+#include <botan/internal/mp_core.h>
+#include <botan/internal/bit_ops.h>
+#include <algorithm>
+
+namespace Botan {
+
+BigInt& BigInt::add(const word y[], size_t y_words, Sign y_sign)
+ {
+ const size_t x_sw = sig_words();
+
+ grow_to(std::max(x_sw, y_words) + 1);
+
+ if(sign() == y_sign)
+ {
+ bigint_add2(mutable_data(), size() - 1, y, y_words);
+ }
+ else
+ {
+ const int32_t relative_size = bigint_cmp(data(), x_sw, y, y_words);
+
+ if(relative_size >= 0)
+ {
+ // *this >= y
+ bigint_sub2(mutable_data(), x_sw, y, y_words);
+ }
+ else
+ {
+ // *this < y
+ 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)
+ set_sign(Positive);
+ }
+
+ return (*this);
+ }
+
+BigInt& BigInt::mod_add(const BigInt& s, const BigInt& mod, secure_vector<word>& ws)
+ {
+ if(this->is_negative() || s.is_negative() || mod.is_negative())
+ throw Invalid_Argument("BigInt::mod_add expects all arguments are positive");
+
+ BOTAN_DEBUG_ASSERT(*this < mod);
+ BOTAN_DEBUG_ASSERT(s < mod);
+
+ /*
+ t + s or t + s - p == t - (p - s)
+
+ So first compute ws = p - s
+
+ Then compute t + s and t - ws
+
+ If t - ws does not borrow, then that is the correct valued
+ */
+
+ const size_t mod_sw = mod.sig_words();
+ BOTAN_ARG_CHECK(mod_sw > 0, "BigInt::mod_add modulus must be positive");
+
+ this->grow_to(mod_sw);
+ s.grow_to(mod_sw);
+
+ // First mod_sw for p - s, 2*mod_sw for bigint_addsub workspace
+ if(ws.size() < 3*mod_sw)
+ ws.resize(3*mod_sw);
+
+ word borrow = bigint_sub3(&ws[0], mod.data(), mod_sw, s.data(), mod_sw);
+ BOTAN_DEBUG_ASSERT(borrow == 0);
+
+ // Compute t - ws
+ 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);
+
+ CT::conditional_copy_mem(borrow, &ws[0], &ws[mod_sw*2], &ws[mod_sw], mod_sw);
+ set_words(&ws[0], mod_sw);
+
+ return (*this);
+ }
+
+BigInt& BigInt::mod_sub(const BigInt& s, const BigInt& mod, secure_vector<word>& ws)
+ {
+ if(this->is_negative() || s.is_negative() || mod.is_negative())
+ throw Invalid_Argument("BigInt::mod_sub expects all arguments are positive");
+
+ // We are assuming in this function that *this and s are no more than mod_sw words long
+ BOTAN_DEBUG_ASSERT(*this < mod);
+ BOTAN_DEBUG_ASSERT(s < mod);
+
+ const size_t mod_sw = mod.sig_words();
+
+ this->grow_to(mod_sw);
+ s.grow_to(mod_sw);
+
+ if(ws.size() < mod_sw)
+ ws.resize(mod_sw);
+
+ if(mod_sw == 4)
+ bigint_mod_sub_n<4>(mutable_data(), s.data(), mod.data(), ws.data());
+ else if(mod_sw == 6)
+ bigint_mod_sub_n<6>(mutable_data(), s.data(), mod.data(), ws.data());
+ else
+ bigint_mod_sub(mutable_data(), s.data(), mod.data(), mod_sw, ws.data());
+
+ return (*this);
+ }
+
+BigInt& BigInt::mod_mul(uint8_t y, const BigInt& mod, secure_vector<word>& ws)
+ {
+ BOTAN_ARG_CHECK(this->is_negative() == false, "*this must be positive");
+ BOTAN_ARG_CHECK(y < 16, "y too large");
+
+ BOTAN_DEBUG_ASSERT(*this < mod);
+
+ *this *= static_cast<word>(y);
+ this->reduce_below(mod, ws);
+ return (*this);
+ }
+
+BigInt& BigInt::rev_sub(const word y[], size_t y_sw, secure_vector<word>& 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);
+
+ return (*this);
+ }
+
+/*
+* Multiplication Operator
+*/
+BigInt& BigInt::operator*=(const BigInt& y)
+ {
+ secure_vector<word> ws;
+ return this->mul(y, ws);
+ }
+
+BigInt& BigInt::mul(const BigInt& y, secure_vector<word>& ws)
+ {
+ const size_t x_sw = sig_words();
+ const size_t y_sw = y.sig_words();
+ set_sign((sign() == y.sign()) ? Positive : Negative);
+
+ if(x_sw == 0 || y_sw == 0)
+ {
+ clear();
+ set_sign(Positive);
+ }
+ else if(x_sw == 1 && y_sw)
+ {
+ 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);
+ secure_vector<word> 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());
+
+ this->swap_reg(z_reg);
+ }
+
+ return (*this);
+ }
+
+BigInt& BigInt::square(secure_vector<word>& ws)
+ {
+ const size_t sw = sig_words();
+
+ secure_vector<word> z(2*sw);
+ ws.resize(z.size());
+
+ bigint_sqr(z.data(), z.size(),
+ data(), size(), sw,
+ ws.data(), ws.size());
+
+ swap_reg(z);
+ set_sign(BigInt::Positive);
+
+ return (*this);
+ }
+
+BigInt& BigInt::operator*=(word y)
+ {
+ if(y == 0)
+ {
+ clear();
+ set_sign(Positive);
+ }
+
+ const word carry = bigint_linmul2(mutable_data(), size(), y);
+ set_word_at(size(), carry);
+
+ return (*this);
+ }
+
+/*
+* Division Operator
+*/
+BigInt& BigInt::operator/=(const BigInt& y)
+ {
+ if(y.sig_words() == 1 && is_power_of_2(y.word_at(0)))
+ (*this) >>= (y.bits() - 1);
+ else
+ (*this) = (*this) / y;
+ return (*this);
+ }
+
+/*
+* Modulo Operator
+*/
+BigInt& BigInt::operator%=(const BigInt& mod)
+ {
+ return (*this = (*this) % mod);
+ }
+
+/*
+* Modulo Operator
+*/
+word BigInt::operator%=(word mod)
+ {
+ if(mod == 0)
+ throw BigInt::DivideByZero();
+
+ word remainder = 0;
+
+ if(is_power_of_2(mod))
+ {
+ remainder = (word_at(0) & (mod - 1));
+ }
+ else
+ {
+ const size_t sw = sig_words();
+ for(size_t i = sw; i > 0; --i)
+ remainder = bigint_modop(remainder, word_at(i-1), mod);
+ }
+
+ if(remainder && sign() == BigInt::Negative)
+ remainder = mod - remainder;
+
+ m_data.set_to_zero();
+ m_data.set_word_at(0, remainder);
+ set_sign(BigInt::Positive);
+ return remainder;
+ }
+
+/*
+* Left Shift Operator
+*/
+BigInt& BigInt::operator<<=(size_t shift)
+ {
+ const size_t shift_words = shift / BOTAN_MP_WORD_BITS;
+ const size_t shift_bits = shift % BOTAN_MP_WORD_BITS;
+ const size_t size = sig_words();
+
+ const size_t bits_free = top_bits_free();
+
+ const size_t new_size = size + shift_words + (bits_free < shift_bits);
+
+ m_data.grow_to(new_size);
+
+ bigint_shl1(m_data.mutable_data(), new_size, size, shift_words, shift_bits);
+
+ return (*this);
+ }
+
+/*
+* Right Shift Operator
+*/
+BigInt& BigInt::operator>>=(size_t shift)
+ {
+ const size_t shift_words = shift / BOTAN_MP_WORD_BITS;
+ const size_t shift_bits = shift % BOTAN_MP_WORD_BITS;
+
+ bigint_shr1(m_data.mutable_data(), m_data.size(), shift_words, shift_bits);
+
+ if(is_negative() && is_zero())
+ set_sign(Positive);
+
+ return (*this);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/math/bigint/big_ops3.cpp b/comm/third_party/botan/src/lib/math/bigint/big_ops3.cpp
new file mode 100644
index 0000000000..11804762b9
--- /dev/null
+++ b/comm/third_party/botan/src/lib/math/bigint/big_ops3.cpp
@@ -0,0 +1,214 @@
+/*
+* BigInt Binary Operators
+* (C) 1999-2007,2018 Jack Lloyd
+* 2016 Matthias Gierlings
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/bigint.h>
+#include <botan/divide.h>
+#include <botan/internal/mp_core.h>
+#include <botan/internal/bit_ops.h>
+#include <algorithm>
+
+namespace Botan {
+
+//static
+BigInt BigInt::add2(const BigInt& x, const word y[], size_t y_words, BigInt::Sign y_sign)
+ {
+ const size_t x_sw = x.sig_words();
+
+ BigInt z(x.sign(), std::max(x_sw, y_words) + 1);
+
+ if(x.sign() == y_sign)
+ {
+ bigint_add3(z.mutable_data(), x.data(), x_sw, y, y_words);
+ }
+ else
+ {
+ const int32_t relative_size = bigint_sub_abs(z.mutable_data(), x.data(), x_sw, y, y_words);
+
+ //z.sign_fixup(relative_size, y_sign);
+ if(relative_size < 0)
+ z.set_sign(y_sign);
+ else if(relative_size == 0)
+ z.set_sign(BigInt::Positive);
+ }
+
+ return z;
+ }
+
+/*
+* Multiplication Operator
+*/
+BigInt operator*(const BigInt& x, const BigInt& y)
+ {
+ const size_t x_sw = x.sig_words();
+ const size_t y_sw = y.sig_words();
+
+ BigInt z(BigInt::Positive, x.size() + y.size());
+
+ if(x_sw == 1 && y_sw)
+ bigint_linmul3(z.mutable_data(), y.data(), y_sw, x.word_at(0));
+ else if(y_sw == 1 && x_sw)
+ bigint_linmul3(z.mutable_data(), x.data(), x_sw, y.word_at(0));
+ else if(x_sw && y_sw)
+ {
+ secure_vector<word> workspace(z.size());
+
+ bigint_mul(z.mutable_data(), z.size(),
+ x.data(), x.size(), x_sw,
+ y.data(), y.size(), y_sw,
+ workspace.data(), workspace.size());
+ }
+
+ z.cond_flip_sign(x_sw > 0 && y_sw > 0 && x.sign() != y.sign());
+
+ return z;
+ }
+
+/*
+* Multiplication Operator
+*/
+BigInt operator*(const BigInt& x, word y)
+ {
+ const size_t x_sw = x.sig_words();
+
+ BigInt z(BigInt::Positive, x_sw + 1);
+
+ if(x_sw && y)
+ {
+ bigint_linmul3(z.mutable_data(), x.data(), x_sw, y);
+ z.set_sign(x.sign());
+ }
+
+ return z;
+ }
+
+/*
+* Division Operator
+*/
+BigInt operator/(const BigInt& x, const BigInt& y)
+ {
+ if(y.sig_words() == 1)
+ {
+ return x / y.word_at(0);
+ }
+
+ BigInt q, r;
+ vartime_divide(x, y, q, r);
+ return q;
+ }
+
+/*
+* Division Operator
+*/
+BigInt operator/(const BigInt& x, word y)
+ {
+ if(y == 0)
+ throw BigInt::DivideByZero();
+ else if(y == 1)
+ return x;
+ else if(y == 2)
+ return (x >> 1);
+ else if(y <= 255)
+ {
+ BigInt q;
+ uint8_t r;
+ ct_divide_u8(x, static_cast<uint8_t>(y), q, r);
+ return q;
+ }
+
+ BigInt q, r;
+ vartime_divide(x, y, q, r);
+ return q;
+ }
+
+/*
+* Modulo Operator
+*/
+BigInt operator%(const BigInt& n, const BigInt& mod)
+ {
+ if(mod.is_zero())
+ throw BigInt::DivideByZero();
+ if(mod.is_negative())
+ throw Invalid_Argument("BigInt::operator%: modulus must be > 0");
+ if(n.is_positive() && mod.is_positive() && n < mod)
+ return n;
+
+ if(mod.sig_words() == 1)
+ {
+ return n % mod.word_at(0);
+ }
+
+ BigInt q, r;
+ vartime_divide(n, mod, q, r);
+ return r;
+ }
+
+/*
+* Modulo Operator
+*/
+word operator%(const BigInt& n, word mod)
+ {
+ if(mod == 0)
+ throw BigInt::DivideByZero();
+
+ if(mod == 1)
+ return 0;
+
+ word remainder = 0;
+
+ if(is_power_of_2(mod))
+ {
+ remainder = (n.word_at(0) & (mod - 1));
+ }
+ else
+ {
+ const size_t sw = n.sig_words();
+ for(size_t i = sw; i > 0; --i)
+ {
+ remainder = bigint_modop(remainder, n.word_at(i-1), mod);
+ }
+ }
+
+ if(remainder && n.sign() == BigInt::Negative)
+ return mod - remainder;
+ return remainder;
+ }
+
+/*
+* Left Shift Operator
+*/
+BigInt operator<<(const BigInt& x, size_t shift)
+ {
+ const size_t shift_words = shift / BOTAN_MP_WORD_BITS,
+ shift_bits = shift % BOTAN_MP_WORD_BITS;
+
+ const size_t x_sw = x.sig_words();
+
+ BigInt y(x.sign(), x_sw + shift_words + (shift_bits ? 1 : 0));
+ bigint_shl2(y.mutable_data(), x.data(), x_sw, shift_words, shift_bits);
+ return y;
+ }
+
+/*
+* 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_bits = shift % BOTAN_MP_WORD_BITS;
+ const size_t x_sw = x.sig_words();
+
+ BigInt y(x.sign(), x_sw - shift_words);
+ bigint_shr2(y.mutable_data(), x.data(), x_sw, shift_words, shift_bits);
+
+ if(x.is_negative() && y.is_zero())
+ y.set_sign(BigInt::Positive);
+
+ return y;
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/math/bigint/big_rand.cpp b/comm/third_party/botan/src/lib/math/bigint/big_rand.cpp
new file mode 100644
index 0000000000..dd4cb5eaba
--- /dev/null
+++ b/comm/third_party/botan/src/lib/math/bigint/big_rand.cpp
@@ -0,0 +1,64 @@
+/*
+* BigInt Random Generation
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/bigint.h>
+#include <botan/rng.h>
+#include <botan/internal/rounding.h>
+
+namespace Botan {
+
+/*
+* Randomize this number
+*/
+void BigInt::randomize(RandomNumberGenerator& rng,
+ size_t bitsize, bool set_high_bit)
+ {
+ set_sign(Positive);
+
+ if(bitsize == 0)
+ {
+ clear();
+ }
+ else
+ {
+ secure_vector<uint8_t> array = rng.random_vec(round_up(bitsize, 8) / 8);
+
+ // Always cut unwanted bits
+ if(bitsize % 8)
+ 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);
+
+ binary_decode(array);
+ }
+ }
+
+/*
+* 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)
+ throw Invalid_Argument("BigInt::random_integer invalid range");
+
+ BigInt r;
+
+ const size_t bits = max.bits();
+
+ do
+ {
+ r.randomize(rng, bits, false);
+ }
+ while(r < min || r >= max);
+
+ return r;
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/math/bigint/bigint.cpp b/comm/third_party/botan/src/lib/math/bigint/bigint.cpp
new file mode 100644
index 0000000000..7bcbaf37f0
--- /dev/null
+++ b/comm/third_party/botan/src/lib/math/bigint/bigint.cpp
@@ -0,0 +1,551 @@
+/*
+* BigInt Base
+* (C) 1999-2011,2012,2014,2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/bigint.h>
+#include <botan/internal/mp_core.h>
+#include <botan/internal/rounding.h>
+#include <botan/internal/bit_ops.h>
+#include <botan/internal/ct_utils.h>
+#include <botan/loadstor.h>
+
+namespace Botan {
+
+BigInt::BigInt(const word words[], size_t length)
+ {
+ m_data.set_words(words, length);
+ }
+
+/*
+* Construct a BigInt from a regular number
+*/
+BigInt::BigInt(uint64_t n)
+ {
+ if(n > 0)
+ {
+#if BOTAN_MP_WORD_BITS == 32
+ m_data.set_word_at(0, static_cast<word>(n));
+ m_data.set_word_at(1, static_cast<word>(n >> 32));
+#else
+ m_data.set_word_at(0, n);
+#endif
+ }
+
+ }
+
+/*
+* Construct a BigInt of the specified size
+*/
+BigInt::BigInt(Sign s, size_t size)
+ {
+ m_data.set_size(size);
+ m_signedness = s;
+ }
+
+/*
+* Construct a BigInt from a string
+*/
+BigInt::BigInt(const std::string& str)
+ {
+ Base base = Decimal;
+ size_t markers = 0;
+ bool negative = false;
+
+ if(str.length() > 0 && str[0] == '-')
+ {
+ markers += 1;
+ negative = true;
+ }
+
+ if(str.length() > markers + 2 && str[markers ] == '0' &&
+ str[markers + 1] == 'x')
+ {
+ markers += 2;
+ base = Hexadecimal;
+ }
+
+ *this = decode(cast_char_ptr_to_uint8(str.data()) + markers,
+ str.length() - markers, base);
+
+ if(negative) set_sign(Negative);
+ else set_sign(Positive);
+ }
+
+BigInt::BigInt(const uint8_t input[], size_t length)
+ {
+ binary_decode(input, length);
+ }
+
+/*
+* Construct a BigInt from an encoded BigInt
+*/
+BigInt::BigInt(const uint8_t input[], size_t length, Base base)
+ {
+ *this = decode(input, length, base);
+ }
+
+BigInt::BigInt(const uint8_t buf[], size_t length, size_t max_bits)
+ {
+ if(8 * length > max_bits)
+ length = (max_bits + 7) / 8;
+
+ binary_decode(buf, length);
+
+ if(8 * length > max_bits)
+ *this >>= (8 - (max_bits % 8));
+ }
+
+/*
+* Construct a BigInt from an encoded BigInt
+*/
+BigInt::BigInt(RandomNumberGenerator& rng, size_t bits, bool set_high_bit)
+ {
+ randomize(rng, bits, set_high_bit);
+ }
+
+uint8_t BigInt::byte_at(size_t n) const
+ {
+ return get_byte(sizeof(word) - (n % sizeof(word)) - 1,
+ word_at(n / sizeof(word)));
+ }
+
+int32_t BigInt::cmp_word(word other) const
+ {
+ if(is_negative())
+ return -1; // other is positive ...
+
+ const size_t sw = this->sig_words();
+ if(sw > 1)
+ return 1; // must be larger since other is just one word ...
+
+ return bigint_cmp(this->data(), sw, &other, 1);
+ }
+
+/*
+* Comparison Function
+*/
+int32_t BigInt::cmp(const BigInt& other, bool check_signs) const
+ {
+ if(check_signs)
+ {
+ if(other.is_positive() && this->is_negative())
+ return -1;
+
+ if(other.is_negative() && this->is_positive())
+ return 1;
+
+ if(other.is_negative() && this->is_negative())
+ return (-bigint_cmp(this->data(), this->size(),
+ other.data(), other.size()));
+ }
+
+ return bigint_cmp(this->data(), this->size(),
+ other.data(), other.size());
+ }
+
+bool BigInt::is_equal(const BigInt& other) const
+ {
+ if(this->sign() != other.sign())
+ return false;
+
+ return bigint_ct_is_eq(this->data(), this->sig_words(),
+ other.data(), other.sig_words()).is_set();
+ }
+
+bool BigInt::is_less_than(const BigInt& other) const
+ {
+ if(this->is_negative() && other.is_positive())
+ return true;
+
+ if(this->is_positive() && other.is_negative())
+ return false;
+
+ if(other.is_negative() && this->is_negative())
+ {
+ return bigint_ct_is_lt(other.data(), other.sig_words(),
+ this->data(), this->sig_words()).is_set();
+ }
+
+ return bigint_ct_is_lt(this->data(), this->sig_words(),
+ other.data(), other.sig_words()).is_set();
+ }
+
+void BigInt::encode_words(word out[], size_t size) const
+ {
+ const size_t words = sig_words();
+
+ if(words > size)
+ throw Encoding_Error("BigInt::encode_words value too large to encode");
+
+ clear_mem(out, size);
+ copy_mem(out, data(), words);
+ }
+
+size_t BigInt::Data::calc_sig_words() const
+ {
+ const size_t sz = m_reg.size();
+ size_t sig = sz;
+
+ word sub = 1;
+
+ for(size_t i = 0; i != sz; ++i)
+ {
+ const word w = m_reg[sz - i - 1];
+ sub &= ct_is_zero(w);
+ sig -= sub;
+ }
+
+ /*
+ * This depends on the data so is poisoned, but unpoison it here as
+ * later conditionals are made on the size.
+ */
+ CT::unpoison(sig);
+
+ return sig;
+ }
+
+/*
+* Return bits {offset...offset+length}
+*/
+uint32_t BigInt::get_substring(size_t offset, size_t length) const
+ {
+ if(length == 0 || length > 32)
+ throw Invalid_Argument("BigInt::get_substring invalid substring length");
+
+ 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);
+
+ /*
+ * The substring is contained within one or at most two words. The
+ * offset and length are not secret, so we can perform conditional
+ * operations on those values.
+ */
+ const word w0 = word_at(word_offset);
+
+ if(wshift == 0 || (offset + length) / BOTAN_MP_WORD_BITS == word_offset)
+ {
+ return static_cast<uint32_t>(w0 >> wshift) & mask;
+ }
+ else
+ {
+ const word w1 = word_at(word_offset + 1);
+ return static_cast<uint32_t>((w0 >> wshift) | (w1 << (BOTAN_MP_WORD_BITS - wshift))) & mask;
+ }
+ }
+
+/*
+* Convert this number to a uint32_t, if possible
+*/
+uint32_t BigInt::to_u32bit() const
+ {
+ if(is_negative())
+ throw Encoding_Error("BigInt::to_u32bit: Number is negative");
+ if(bits() > 32)
+ throw Encoding_Error("BigInt::to_u32bit: Number is too big to convert");
+
+ uint32_t out = 0;
+ for(size_t i = 0; i != 4; ++i)
+ out = (out << 8) | byte_at(3-i);
+ return out;
+ }
+
+/*
+* Set bit number n
+*/
+void BigInt::conditionally_set_bit(size_t n, bool set_it)
+ {
+ const size_t which = n / BOTAN_MP_WORD_BITS;
+ const word mask = static_cast<word>(set_it) << (n % BOTAN_MP_WORD_BITS);
+ m_data.set_word_at(which, word_at(which) | mask);
+ }
+
+/*
+* Clear bit number n
+*/
+void BigInt::clear_bit(size_t n)
+ {
+ const size_t which = n / BOTAN_MP_WORD_BITS;
+
+ if(which < size())
+ {
+ const word mask = ~(static_cast<word>(1) << (n % BOTAN_MP_WORD_BITS));
+ m_data.set_word_at(which, word_at(which) & mask);
+ }
+ }
+
+size_t BigInt::bytes() const
+ {
+ return round_up(bits(), 8) / 8;
+ }
+
+size_t BigInt::top_bits_free() const
+ {
+ const size_t words = sig_words();
+
+ const word top_word = word_at(words - 1);
+ const size_t bits_used = high_bit(top_word);
+ CT::unpoison(bits_used);
+ return BOTAN_MP_WORD_BITS - bits_used;
+ }
+
+size_t BigInt::bits() const
+ {
+ const size_t words = sig_words();
+
+ if(words == 0)
+ 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();
+
+ return full_words + top_bits;
+ }
+
+/*
+* Calcluate the size in a certain base
+*/
+size_t BigInt::encoded_size(Base base) const
+ {
+ static const double LOG_2_BASE_10 = 0.30102999566;
+
+ if(base == Binary)
+ return bytes();
+ else if(base == Hexadecimal)
+ return 2*bytes();
+ else if(base == Decimal)
+ return static_cast<size_t>((bits() * LOG_2_BASE_10) + 1);
+ else
+ throw Invalid_Argument("Unknown base for BigInt encoding");
+ }
+
+/*
+* Return the negation of this number
+*/
+BigInt BigInt::operator-() const
+ {
+ BigInt x = (*this);
+ x.flip_sign();
+ return x;
+ }
+
+size_t BigInt::reduce_below(const BigInt& p, secure_vector<word>& ws)
+ {
+ if(p.is_negative() || this->is_negative())
+ throw Invalid_Argument("BigInt::reduce_below both values must be positive");
+
+ const size_t p_words = p.sig_words();
+
+ if(size() < p_words + 1)
+ grow_to(p_words + 1);
+
+ if(ws.size() < p_words + 1)
+ ws.resize(p_words + 1);
+
+ clear_mem(ws.data(), ws.size());
+
+ size_t reductions = 0;
+
+ for(;;)
+ {
+ word borrow = bigint_sub3(ws.data(), data(), p_words + 1, p.data(), p_words);
+ if(borrow)
+ break;
+
+ ++reductions;
+ swap_reg(ws);
+ }
+
+ return reductions;
+ }
+
+void BigInt::ct_reduce_below(const BigInt& mod, secure_vector<word>& ws, size_t bound)
+ {
+ if(mod.is_negative() || this->is_negative())
+ throw Invalid_Argument("BigInt::ct_reduce_below both values must be positive");
+
+ const size_t mod_words = mod.sig_words();
+
+ grow_to(mod_words);
+
+ const size_t sz = size();
+
+ ws.resize(sz);
+
+ 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);
+
+ CT::Mask<word>::is_zero(borrow).select_n(mutable_data(), ws.data(), data(), sz);
+ }
+ }
+
+/*
+* Return the absolute value of this number
+*/
+BigInt BigInt::abs() const
+ {
+ BigInt x = (*this);
+ x.set_sign(Positive);
+ return x;
+ }
+
+void BigInt::binary_encode(uint8_t buf[]) const
+ {
+ this->binary_encode(buf, bytes());
+ }
+
+/*
+* Encode this number into bytes
+*/
+void BigInt::binary_encode(uint8_t output[], size_t len) const
+ {
+ const size_t full_words = len / sizeof(word);
+ const size_t extra_bytes = len % sizeof(word);
+
+ for(size_t i = 0; i != full_words; ++i)
+ {
+ const word w = word_at(i);
+ store_be(w, output + (len - (i+1)*sizeof(word)));
+ }
+
+ if(extra_bytes > 0)
+ {
+ const word w = word_at(full_words);
+
+ for(size_t i = 0; i != extra_bytes; ++i)
+ {
+ output[extra_bytes - i - 1] = get_byte(sizeof(word) - i - 1, w);
+ }
+ }
+ }
+
+/*
+* Set this number to the value in buf
+*/
+void BigInt::binary_decode(const uint8_t buf[], size_t length)
+ {
+ clear();
+
+ const size_t full_words = length / sizeof(word);
+ const size_t extra_bytes = length % sizeof(word);
+
+ secure_vector<word> reg((round_up(full_words + (extra_bytes > 0 ? 1 : 0), 8)));
+
+ for(size_t i = 0; i != full_words; ++i)
+ {
+ reg[i] = load_be<word>(buf + length - sizeof(word)*(i+1), 0);
+ }
+
+ if(extra_bytes > 0)
+ {
+ for(size_t i = 0; i != extra_bytes; ++i)
+ reg[full_words] = (reg[full_words] << 8) | buf[i];
+ }
+
+ m_data.swap(reg);
+ }
+
+void BigInt::ct_cond_add(bool predicate, const BigInt& value)
+ {
+ if(this->is_negative() || value.is_negative())
+ throw Invalid_Argument("BigInt::ct_cond_add requires both values to be positive");
+ this->grow_to(1 + value.sig_words());
+
+ bigint_cnd_add(static_cast<word>(predicate),
+ this->mutable_data(), this->size(),
+ value.data(), value.sig_words());
+ }
+
+void BigInt::ct_cond_swap(bool predicate, BigInt& other)
+ {
+ const size_t max_words = std::max(size(), other.size());
+ grow_to(max_words);
+ other.grow_to(max_words);
+
+ bigint_cnd_swap(predicate, this->mutable_data(), other.mutable_data(), max_words);
+ }
+
+void BigInt::cond_flip_sign(bool predicate)
+ {
+ // This code is assuming Negative == 0, Positive == 1
+
+ const auto mask = CT::Mask<uint8_t>::expand(predicate);
+
+ const uint8_t current_sign = static_cast<uint8_t>(sign());
+
+ const uint8_t new_sign = mask.select(current_sign ^ 1, current_sign);
+
+ set_sign(static_cast<Sign>(new_sign));
+ }
+
+void BigInt::ct_cond_assign(bool predicate, const BigInt& other)
+ {
+ const size_t t_words = size();
+ const size_t o_words = other.size();
+
+ if(o_words < t_words)
+ grow_to(o_words);
+
+ const size_t r_words = std::max(t_words, o_words);
+
+ const auto mask = CT::Mask<word>::expand(predicate);
+
+ for(size_t i = 0; i != r_words; ++i)
+ {
+ const word o_word = other.word_at(i);
+ const word t_word = this->word_at(i);
+ this->set_word_at(i, mask.select(o_word, t_word));
+ }
+
+ const bool different_sign = sign() != other.sign();
+ cond_flip_sign(predicate && different_sign);
+ }
+
+#if defined(BOTAN_HAS_VALGRIND)
+void BigInt::const_time_poison() const
+ {
+ CT::poison(m_data.const_data(), m_data.size());
+ }
+
+void BigInt::const_time_unpoison() const
+ {
+ CT::unpoison(m_data.const_data(), m_data.size());
+ }
+#endif
+
+void BigInt::const_time_lookup(secure_vector<word>& output,
+ const std::vector<BigInt>& vec,
+ size_t idx)
+ {
+ const size_t words = output.size();
+
+ clear_mem(output.data(), output.size());
+
+ CT::poison(&idx, sizeof(idx));
+
+ for(size_t i = 0; i != vec.size(); ++i)
+ {
+ BOTAN_ASSERT(vec[i].size() >= words,
+ "Word size as expected in const_time_lookup");
+
+ const auto mask = CT::Mask<word>::is_equal(i, idx);
+
+ for(size_t w = 0; w != words; ++w)
+ {
+ const word viw = vec[i].word_at(w);
+ output[w] = mask.if_set_return(viw);
+ }
+ }
+
+ CT::unpoison(idx);
+ CT::unpoison(output.data(), output.size());
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/math/bigint/bigint.h b/comm/third_party/botan/src/lib/math/bigint/bigint.h
new file mode 100644
index 0000000000..33e79d0122
--- /dev/null
+++ b/comm/third_party/botan/src/lib/math/bigint/bigint.h
@@ -0,0 +1,1153 @@
+/*
+* BigInt
+* (C) 1999-2008,2012,2018 Jack Lloyd
+* 2007 FlexSecure
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_BIGINT_H_
+#define BOTAN_BIGINT_H_
+
+#include <botan/types.h>
+#include <botan/secmem.h>
+#include <botan/exceptn.h>
+#include <iosfwd>
+
+namespace Botan {
+
+class RandomNumberGenerator;
+
+/**
+* Arbitrary precision integer
+*/
+class BOTAN_PUBLIC_API(2,0) BigInt final
+ {
+ public:
+ /**
+ * Base enumerator for encoding and decoding
+ */
+ enum Base { Decimal = 10, Hexadecimal = 16, Binary = 256 };
+
+ /**
+ * Sign symbol definitions for positive and negative numbers
+ */
+ enum Sign { Negative = 0, Positive = 1 };
+
+ /**
+ * DivideByZero Exception
+ *
+ * In a future release this exception will be removed and its usage
+ * replaced by Invalid_Argument
+ */
+ class BOTAN_PUBLIC_API(2,0) DivideByZero final : public Invalid_Argument
+ {
+ public:
+ DivideByZero() : Invalid_Argument("BigInt divide by zero") {}
+ };
+
+ /**
+ * Create empty BigInt
+ */
+ BigInt() = default;
+
+ /**
+ * Create BigInt from 64 bit integer
+ * @param n initial value of this BigInt
+ */
+ BigInt(uint64_t n);
+
+ /**
+ * Copy Constructor
+ * @param other the BigInt to copy
+ */
+ BigInt(const BigInt& other) = default;
+
+ /**
+ * Create BigInt from a string. If the string starts with 0x the
+ * rest of the string will be interpreted as hexadecimal digits.
+ * Otherwise, it will be interpreted as a decimal number.
+ *
+ * @param str the string to parse for an integer value
+ */
+ explicit BigInt(const std::string& str);
+
+ /**
+ * Create a BigInt from an integer in a byte array
+ * @param buf the byte array holding the value
+ * @param length size of buf
+ */
+ BigInt(const uint8_t buf[], size_t length);
+
+ /**
+ * Create a BigInt from an integer in a byte array
+ * @param vec the byte vector holding the value
+ */
+ template<typename Alloc>
+ explicit BigInt(const std::vector<uint8_t, Alloc>& vec) : BigInt(vec.data(), vec.size()) {}
+
+ /**
+ * Create a BigInt from an integer in a byte array
+ * @param buf the byte array holding the value
+ * @param length size of buf
+ * @param base is the number base of the integer in buf
+ */
+ BigInt(const uint8_t buf[], size_t length, Base base);
+
+ /**
+ * Create a BigInt from an integer in a byte array
+ * @param buf the byte array holding the value
+ * @param length size of buf
+ * @param max_bits if the resulting integer is more than max_bits,
+ * it will be shifted so it is at most max_bits in length.
+ */
+ BigInt(const uint8_t buf[], size_t length, size_t max_bits);
+
+ /**
+ * Create a BigInt from an array of words
+ * @param words the words
+ * @param length number of words
+ */
+ BigInt(const word words[], size_t length);
+
+ /**
+ * \brief Create a random BigInt of the specified size
+ *
+ * @param rng random number generator
+ * @param bits size in bits
+ * @param set_high_bit if true, the highest bit is always set
+ *
+ * @see randomize
+ */
+ BigInt(RandomNumberGenerator& rng, size_t bits, bool set_high_bit = true);
+
+ /**
+ * Create BigInt of specified size, all zeros
+ * @param sign the sign
+ * @param n size of the internal register in words
+ */
+ BigInt(Sign sign, size_t n);
+
+ /**
+ * Move constructor
+ */
+ BigInt(BigInt&& other)
+ {
+ this->swap(other);
+ }
+
+ ~BigInt() { const_time_unpoison(); }
+
+ /**
+ * Move assignment
+ */
+ BigInt& operator=(BigInt&& other)
+ {
+ if(this != &other)
+ this->swap(other);
+
+ return (*this);
+ }
+
+ /**
+ * Copy assignment
+ */
+ BigInt& operator=(const BigInt&) = default;
+
+ /**
+ * Swap this value with another
+ * @param other BigInt to swap values with
+ */
+ void swap(BigInt& other)
+ {
+ m_data.swap(other.m_data);
+ std::swap(m_signedness, other.m_signedness);
+ }
+
+ void swap_reg(secure_vector<word>& reg)
+ {
+ m_data.swap(reg);
+ // sign left unchanged
+ }
+
+ /**
+ * += operator
+ * @param y the BigInt to add to this
+ */
+ BigInt& operator+=(const BigInt& y)
+ {
+ return add(y.data(), y.sig_words(), y.sign());
+ }
+
+ /**
+ * += operator
+ * @param y the word to add to this
+ */
+ BigInt& operator+=(word y)
+ {
+ return add(&y, 1, Positive);
+ }
+
+ /**
+ * -= operator
+ * @param y the BigInt to subtract from this
+ */
+ BigInt& operator-=(const BigInt& y)
+ {
+ return sub(y.data(), y.sig_words(), y.sign());
+ }
+
+ /**
+ * -= operator
+ * @param y the word to subtract from this
+ */
+ BigInt& operator-=(word y)
+ {
+ return sub(&y, 1, Positive);
+ }
+
+ /**
+ * *= operator
+ * @param y the BigInt to multiply with this
+ */
+ BigInt& operator*=(const BigInt& y);
+
+ /**
+ * *= operator
+ * @param y the word to multiply with this
+ */
+ BigInt& operator*=(word y);
+
+ /**
+ * /= operator
+ * @param y the BigInt to divide this by
+ */
+ BigInt& operator/=(const BigInt& y);
+
+ /**
+ * Modulo operator
+ * @param y the modulus to reduce this by
+ */
+ BigInt& operator%=(const BigInt& y);
+
+ /**
+ * Modulo operator
+ * @param y the modulus (word) to reduce this by
+ */
+ word operator%=(word y);
+
+ /**
+ * Left shift operator
+ * @param shift the number of bits to shift this left by
+ */
+ BigInt& operator<<=(size_t shift);
+
+ /**
+ * Right shift operator
+ * @param shift the number of bits to shift this right by
+ */
+ BigInt& operator>>=(size_t shift);
+
+ /**
+ * Increment operator
+ */
+ BigInt& operator++() { return (*this += 1); }
+
+ /**
+ * Decrement operator
+ */
+ BigInt& operator--() { return (*this -= 1); }
+
+ /**
+ * Postfix increment operator
+ */
+ BigInt operator++(int) { BigInt x = (*this); ++(*this); return x; }
+
+ /**
+ * Postfix decrement operator
+ */
+ BigInt operator--(int) { BigInt x = (*this); --(*this); return x; }
+
+ /**
+ * Unary negation operator
+ * @return negative this
+ */
+ BigInt operator-() const;
+
+ /**
+ * ! operator
+ * @return true iff this is zero, otherwise false
+ */
+ bool operator !() const { return (!is_nonzero()); }
+
+ static BigInt add2(const BigInt& x, const word y[], size_t y_words, Sign y_sign);
+
+ BigInt& add(const word y[], size_t y_words, Sign sign);
+
+ BigInt& sub(const word y[], size_t y_words, Sign sign)
+ {
+ return add(y, y_words, sign == Positive ? Negative : Positive);
+ }
+
+ /**
+ * Multiply this with y
+ * @param y the BigInt to multiply with this
+ * @param ws a temp workspace
+ */
+ BigInt& mul(const BigInt& y, secure_vector<word>& ws);
+
+ /**
+ * Square value of *this
+ * @param ws a temp workspace
+ */
+ BigInt& square(secure_vector<word>& ws);
+
+ /**
+ * Set *this to y - *this
+ * @param y the BigInt to subtract from as a sequence of words
+ * @param y_words length of y in words
+ * @param ws a temp workspace
+ */
+ BigInt& rev_sub(const word y[], size_t y_words, secure_vector<word>& ws);
+
+ /**
+ * Set *this to (*this + y) % mod
+ * This function assumes *this is >= 0 && < mod
+ * @param y the BigInt to add - assumed y >= 0 and y < mod
+ * @param mod the positive modulus
+ * @param ws a temp workspace
+ */
+ BigInt& mod_add(const BigInt& y, const BigInt& mod, secure_vector<word>& ws);
+
+ /**
+ * Set *this to (*this - y) % mod
+ * This function assumes *this is >= 0 && < mod
+ * @param y the BigInt to subtract - assumed y >= 0 and y < mod
+ * @param mod the positive modulus
+ * @param ws a temp workspace
+ */
+ BigInt& mod_sub(const BigInt& y, const BigInt& mod, secure_vector<word>& ws);
+
+ /**
+ * Set *this to (*this * y) % mod
+ * This function assumes *this is >= 0 && < mod
+ * y should be small, less than 16
+ * @param y the small integer to multiply by
+ * @param mod the positive modulus
+ * @param ws a temp workspace
+ */
+ BigInt& mod_mul(uint8_t y, const BigInt& mod, secure_vector<word>& ws);
+
+ /**
+ * Return *this % mod
+ *
+ * Assumes that *this is (if anything) only slightly larger than
+ * mod and performs repeated subtractions. It should not be used if
+ * *this is much larger than mod, instead use modulo operator.
+ */
+ size_t reduce_below(const BigInt& mod, secure_vector<word> &ws);
+
+ /**
+ * Return *this % mod
+ *
+ * Assumes that *this is (if anything) only slightly larger than mod and
+ * performs repeated subtractions. It should not be used if *this is much
+ * larger than mod, instead use modulo operator.
+ *
+ * Performs exactly bound subtractions, so if *this is >= bound*mod then the
+ * result will not be fully reduced. If bound is zero, nothing happens.
+ */
+ void ct_reduce_below(const BigInt& mod, secure_vector<word> &ws, size_t bound);
+
+ /**
+ * Zeroize the BigInt. The size of the underlying register is not
+ * modified.
+ */
+ void clear() { m_data.set_to_zero(); m_signedness = Positive; }
+
+ /**
+ * Compare this to another BigInt
+ * @param n the BigInt value to compare with
+ * @param check_signs include sign in comparison?
+ * @result if (this<n) return -1, if (this>n) return 1, if both
+ * values are identical return 0 [like Perl's <=> operator]
+ */
+ int32_t cmp(const BigInt& n, bool check_signs = true) const;
+
+ /**
+ * Compare this to another BigInt
+ * @param n the BigInt value to compare with
+ * @result true if this == n or false otherwise
+ */
+ bool is_equal(const BigInt& n) const;
+
+ /**
+ * Compare this to another BigInt
+ * @param n the BigInt value to compare with
+ * @result true if this < n or false otherwise
+ */
+ bool is_less_than(const BigInt& n) const;
+
+ /**
+ * Compare this to an integer
+ * @param n the value to compare with
+ * @result if (this<n) return -1, if (this>n) return 1, if both
+ * values are identical return 0 [like Perl's <=> operator]
+ */
+ int32_t cmp_word(word n) const;
+
+ /**
+ * 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); }
+
+ /**
+ * 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); }
+
+ /**
+ * Test if the integer is not zero
+ * @result true if the integer is non-zero, false otherwise
+ */
+ bool is_nonzero() const { return (!is_zero()); }
+
+ /**
+ * Test if the integer is zero
+ * @result true if the integer is zero, false otherwise
+ */
+ bool is_zero() const
+ {
+ return (sig_words() == 0);
+ }
+
+ /**
+ * Set bit at specified position
+ * @param n bit position to set
+ */
+ void set_bit(size_t n)
+ {
+ conditionally_set_bit(n, true);
+ }
+
+ /**
+ * Conditionally set bit at specified position. Note if set_it is
+ * false, nothing happens, and if the bit is already set, it
+ * remains set.
+ *
+ * @param n bit position to set
+ * @param set_it if the bit should be set
+ */
+ void conditionally_set_bit(size_t n, bool set_it);
+
+ /**
+ * Clear bit at specified position
+ * @param n bit position to clear
+ */
+ void clear_bit(size_t n);
+
+ /**
+ * 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);
+ }
+
+ /**
+ * 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);
+ }
+
+ /**
+ * Return (a maximum of) 32 bits of the complete value
+ * @param offset the offset to start extracting
+ * @param length amount of bits to extract (starting at offset)
+ * @result the integer extracted from the register starting at
+ * offset with specified length
+ */
+ uint32_t get_substring(size_t offset, size_t length) const;
+
+ /**
+ * Convert this value into a uint32_t, if it is in the range
+ * [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;
+
+ /**
+ * Convert this value to a decimal string.
+ * Warning: decimal conversions are relatively slow
+ */
+ std::string to_dec_string() const;
+
+ /**
+ * Convert this value to a hexadecimal string.
+ */
+ std::string to_hex_string() const;
+
+ /**
+ * @param n the offset to get a byte from
+ * @result byte at offset n
+ */
+ uint8_t byte_at(size_t n) const;
+
+ /**
+ * Return the word at a specified position of the internal register
+ * @param n position in the register
+ * @return value at position n
+ */
+ word word_at(size_t n) const
+ {
+ return m_data.get_word_at(n);
+ }
+
+ void set_word_at(size_t i, word w)
+ {
+ m_data.set_word_at(i, w);
+ }
+
+ void set_words(const word w[], size_t len)
+ {
+ m_data.set_words(w, len);
+ }
+
+ /**
+ * 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); }
+
+ /**
+ * Tests if the sign of the integer is positive
+ * @result true, iff the integer has a positive sign
+ */
+ bool is_positive() const { return (sign() == Positive); }
+
+ /**
+ * Return the sign of the integer
+ * @result the sign of the integer
+ */
+ Sign sign() const { return (m_signedness); }
+
+ /**
+ * @result the opposite sign of the represented integer value
+ */
+ Sign reverse_sign() const
+ {
+ if(sign() == Positive)
+ return Negative;
+ return Positive;
+ }
+
+ /**
+ * Flip the sign of this BigInt
+ */
+ void flip_sign()
+ {
+ set_sign(reverse_sign());
+ }
+
+ /**
+ * Set sign of the integer
+ * @param sign new Sign to set
+ */
+ void set_sign(Sign sign)
+ {
+ if(sign == Negative && is_zero())
+ sign = Positive;
+
+ m_signedness = sign;
+ }
+
+ /**
+ * @result absolute (positive) value of this
+ */
+ BigInt abs() const;
+
+ /**
+ * Give size of internal register
+ * @result size of internal register in words
+ */
+ size_t size() const { return m_data.size(); }
+
+ /**
+ * Return how many words we need to hold this value
+ * @result significant words of the represented integer value
+ */
+ size_t sig_words() const
+ {
+ return m_data.sig_words();
+ }
+
+ /**
+ * Give byte length of the integer
+ * @result byte length of the represented integer value
+ */
+ size_t bytes() const;
+
+ /**
+ * Get the bit length of the integer
+ * @result bit length of the represented integer value
+ */
+ size_t bits() const;
+
+ /**
+ * Get the number of high bits unset in the top (allocated) word
+ * of this integer. Returns BOTAN_MP_WORD_BITS only iff *this is
+ * zero. Ignores sign.
+ */
+ size_t top_bits_free() const;
+
+ /**
+ * Return a mutable pointer to the register
+ * @result a pointer to the start of the internal register
+ */
+ word* mutable_data() { return m_data.mutable_data(); }
+
+ /**
+ * Return a const pointer to the register
+ * @result a pointer to the start of the internal register
+ */
+ const word* data() const { return m_data.const_data(); }
+
+ /**
+ * Don't use this function in application code
+ */
+ secure_vector<word>& get_word_vector() { return m_data.mutable_vector(); }
+
+ /**
+ * Don't use this function in application code
+ */
+ const secure_vector<word>& get_word_vector() const { return m_data.const_vector(); }
+
+ /**
+ * Increase internal register buffer to at least n words
+ * @param n new size of register
+ */
+ void grow_to(size_t n) const { m_data.grow_to(n); }
+
+ /**
+ * Resize the vector to the minimum word size to hold the integer, or
+ * min_size words, whichever is larger
+ */
+ void BOTAN_DEPRECATED("Use resize if required") shrink_to_fit(size_t min_size = 0)
+ {
+ m_data.shrink_to_fit(min_size);
+ }
+
+ void resize(size_t s) { m_data.resize(s); }
+
+ /**
+ * Fill BigInt with a random number with size of bitsize
+ *
+ * If \p set_high_bit is true, the highest bit will be set, which causes
+ * the entropy to be \a bits-1. Otherwise the highest bit is randomly chosen
+ * by the rng, causing the entropy to be \a bits.
+ *
+ * @param rng the random number generator to use
+ * @param bitsize number of bits the created random value should have
+ * @param set_high_bit if true, the highest bit is always set
+ */
+ void randomize(RandomNumberGenerator& rng, size_t bitsize, bool set_high_bit = true);
+
+ /**
+ * Store BigInt-value in a given byte array
+ * @param buf destination byte array for the integer value
+ */
+ void binary_encode(uint8_t buf[]) const;
+
+ /**
+ * Store BigInt-value in a given byte array. If len is less than
+ * the size of the value, then it will be truncated. If len is
+ * greater than the size of the value, it will be zero-padded.
+ * If len exactly equals this->bytes(), this function behaves identically
+ * to binary_encode.
+ *
+ * @param buf destination byte array for the integer value
+ * @param len how many bytes to write
+ */
+ void binary_encode(uint8_t buf[], size_t len) const;
+
+ /**
+ * Read integer value from a byte array with given size
+ * @param buf byte array buffer containing the integer
+ * @param length size of buf
+ */
+ void binary_decode(const uint8_t buf[], size_t length);
+
+ /**
+ * Read integer value from a byte vector
+ * @param buf the vector to load from
+ */
+ template<typename Alloc>
+ void binary_decode(const std::vector<uint8_t, Alloc>& buf)
+ {
+ binary_decode(buf.data(), buf.size());
+ }
+
+ /**
+ * @param base the base to measure the size for
+ * @return size of this integer in base base
+ *
+ * Deprecated. This is only needed when using the `encode` and
+ * `encode_locked` functions, which are also deprecated.
+ */
+ BOTAN_DEPRECATED("See comments on declaration")
+ size_t encoded_size(Base base = Binary) const;
+
+ /**
+ * Place the value into out, zero-padding up to size words
+ * Throw if *this cannot be represented in size words
+ */
+ void encode_words(word out[], size_t size) const;
+
+ /**
+ * 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);
+
+ /**
+ * If predicate is true swap *this and other
+ * Uses a masked operation to avoid side channels
+ */
+ 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);
+
+ /**
+ * If predicate is true flip the sign of *this
+ */
+ void cond_flip_sign(bool predicate);
+
+#if defined(BOTAN_HAS_VALGRIND)
+ void const_time_poison() const;
+ void const_time_unpoison() const;
+#else
+ void const_time_poison() const {}
+ void const_time_unpoison() const {}
+#endif
+
+ /**
+ * @param rng a random number generator
+ * @param min the minimum value (must be non-negative)
+ * @param max the maximum value (must be non-negative and > min)
+ * @return random integer in [min,max)
+ */
+ static BigInt random_integer(RandomNumberGenerator& rng,
+ const BigInt& min,
+ const BigInt& max);
+
+ /**
+ * Create a power of two
+ * @param n the power of two to create
+ * @return bigint representing 2^n
+ */
+ static BigInt power_of_2(size_t n)
+ {
+ BigInt b;
+ b.set_bit(n);
+ return b;
+ }
+
+ /**
+ * Encode the integer value from a BigInt to a std::vector of bytes
+ * @param n the BigInt to use as integer source
+ * @result secure_vector of bytes containing the bytes of the integer
+ */
+ static std::vector<uint8_t> encode(const BigInt& n)
+ {
+ std::vector<uint8_t> output(n.bytes());
+ n.binary_encode(output.data());
+ return output;
+ }
+
+ /**
+ * Encode the integer value from a BigInt to a secure_vector of bytes
+ * @param n the BigInt to use as integer source
+ * @result secure_vector of bytes containing the bytes of the integer
+ */
+ static secure_vector<uint8_t> encode_locked(const BigInt& n)
+ {
+ secure_vector<uint8_t> output(n.bytes());
+ n.binary_encode(output.data());
+ return output;
+ }
+
+ /**
+ * Encode the integer value from a BigInt to a byte array
+ * @param buf destination byte array for the encoded integer
+ * @param n the BigInt to use as integer source
+ */
+ static BOTAN_DEPRECATED("Use n.binary_encode") void encode(uint8_t buf[], const BigInt& n)
+ {
+ n.binary_encode(buf);
+ }
+
+ /**
+ * Create a BigInt from an integer in a byte array
+ * @param buf the binary value to load
+ * @param length size of buf
+ * @result BigInt representing the integer in the byte array
+ */
+ static BigInt decode(const uint8_t buf[], size_t length)
+ {
+ return BigInt(buf, length);
+ }
+
+ /**
+ * Create a BigInt from an integer in a byte array
+ * @param buf the binary value to load
+ * @result BigInt representing the integer in the byte array
+ */
+ template<typename Alloc>
+ static BigInt decode(const std::vector<uint8_t, Alloc>& buf)
+ {
+ return BigInt(buf);
+ }
+
+ /**
+ * Encode the integer value from a BigInt to a std::vector of bytes
+ * @param n the BigInt to use as integer source
+ * @param base number-base of resulting byte array representation
+ * @result secure_vector of bytes containing the integer with given base
+ *
+ * Deprecated. If you need Binary, call the version of encode that doesn't
+ * take a Base. If you need Hex or Decimal output, use to_hex_string or
+ * to_dec_string resp.
+ */
+ BOTAN_DEPRECATED("See comments on declaration")
+ static std::vector<uint8_t> encode(const BigInt& n, Base base);
+
+ /**
+ * Encode the integer value from a BigInt to a secure_vector of bytes
+ * @param n the BigInt to use as integer source
+ * @param base number-base of resulting byte array representation
+ * @result secure_vector of bytes containing the integer with given base
+ *
+ * Deprecated. If you need Binary, call the version of encode_locked that
+ * doesn't take a Base. If you need Hex or Decimal output, use to_hex_string
+ * or to_dec_string resp.
+ */
+ BOTAN_DEPRECATED("See comments on declaration")
+ static secure_vector<uint8_t> encode_locked(const BigInt& n,
+ Base base);
+
+ /**
+ * Encode the integer value from a BigInt to a byte array
+ * @param buf destination byte array for the encoded integer
+ * value with given base
+ * @param n the BigInt to use as integer source
+ * @param base number-base of resulting byte array representation
+ *
+ * Deprecated. If you need Binary, call binary_encode. If you need
+ * Hex or Decimal output, use to_hex_string or to_dec_string resp.
+ */
+ BOTAN_DEPRECATED("See comments on declaration")
+ static void encode(uint8_t buf[], const BigInt& n, Base base);
+
+ /**
+ * Create a BigInt from an integer in a byte array
+ * @param buf the binary value to load
+ * @param length size of buf
+ * @param base number-base of the integer in buf
+ * @result BigInt representing the integer in the byte array
+ */
+ static BigInt decode(const uint8_t buf[], size_t length,
+ Base base);
+
+ /**
+ * Create a BigInt from an integer in a byte array
+ * @param buf the binary value to load
+ * @param base number-base of the integer in buf
+ * @result BigInt representing the integer in the byte array
+ */
+ template<typename Alloc>
+ static BigInt decode(const std::vector<uint8_t, Alloc>& buf, Base base)
+ {
+ if(base == Binary)
+ return BigInt(buf);
+ return BigInt::decode(buf.data(), buf.size(), base);
+ }
+
+ /**
+ * Encode a BigInt to a byte array according to IEEE 1363
+ * @param n the BigInt to encode
+ * @param bytes the length of the resulting secure_vector<uint8_t>
+ * @result a secure_vector<uint8_t> containing the encoded BigInt
+ */
+ static secure_vector<uint8_t> encode_1363(const BigInt& n, size_t bytes);
+
+ static void encode_1363(uint8_t out[], size_t bytes, const BigInt& n);
+
+ /**
+ * Encode two BigInt to a byte array according to IEEE 1363
+ * @param n1 the first BigInt to encode
+ * @param n2 the second BigInt to encode
+ * @param bytes the length of the encoding of each single BigInt
+ * @result a secure_vector<uint8_t> containing the concatenation of the two encoded BigInt
+ */
+ static secure_vector<uint8_t> encode_fixed_length_int_pair(const BigInt& n1, const BigInt& n2, size_t bytes);
+
+ /**
+ * Set output = vec[idx].m_reg in constant time
+ *
+ * All elements of vec must have the same size, and output must be
+ * pre-allocated with the same size.
+ */
+ static void BOTAN_DEPRECATED("No longer in use") const_time_lookup(
+ secure_vector<word>& output,
+ const std::vector<BigInt>& vec,
+ size_t idx);
+
+ private:
+
+ class Data
+ {
+ public:
+ word* mutable_data()
+ {
+ invalidate_sig_words();
+ return m_reg.data();
+ }
+
+ const word* const_data() const
+ {
+ return m_reg.data();
+ }
+
+ secure_vector<word>& mutable_vector()
+ {
+ invalidate_sig_words();
+ return m_reg;
+ }
+
+ const secure_vector<word>& const_vector() const
+ {
+ return m_reg;
+ }
+
+ word get_word_at(size_t n) const
+ {
+ if(n < m_reg.size())
+ return m_reg[n];
+ return 0;
+ }
+
+ void set_word_at(size_t i, word w)
+ {
+ invalidate_sig_words();
+ if(i >= m_reg.size())
+ {
+ if(w == 0)
+ return;
+ grow_to(i + 1);
+ }
+ m_reg[i] = w;
+ }
+
+ void set_words(const word w[], size_t len)
+ {
+ invalidate_sig_words();
+ 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;
+
+ // if(top_word < sig_words()) ?
+ if(top_word < size())
+ {
+ const word mask = (static_cast<word>(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 grow_to(size_t n) const
+ {
+ if(n > size())
+ {
+ if(n <= m_reg.capacity())
+ m_reg.resize(n);
+ else
+ m_reg.resize(n + (8 - (n % 8)));
+ }
+ }
+
+ size_t size() const { return m_reg.size(); }
+
+ void shrink_to_fit(size_t min_size = 0)
+ {
+ const size_t words = std::max(min_size, sig_words());
+ m_reg.resize(words);
+ }
+
+ void resize(size_t s)
+ {
+ m_reg.resize(s);
+ }
+
+ void swap(Data& other)
+ {
+ m_reg.swap(other.m_reg);
+ std::swap(m_sig_words, other.m_sig_words);
+ }
+
+ void swap(secure_vector<word>& reg)
+ {
+ m_reg.swap(reg);
+ invalidate_sig_words();
+ }
+
+ void invalidate_sig_words() const
+ {
+ 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;
+ }
+ private:
+ static const size_t sig_words_npos = static_cast<size_t>(-1);
+
+ size_t calc_sig_words() const;
+
+ mutable secure_vector<word> m_reg;
+ mutable size_t m_sig_words = sig_words_npos;
+ };
+
+ Data m_data;
+ Sign m_signedness = Positive;
+ };
+
+/*
+* Arithmetic Operators
+*/
+inline BigInt operator+(const BigInt& x, const BigInt& y)
+ {
+ return BigInt::add2(x, y.data(), y.sig_words(), y.sign());
+ }
+
+inline BigInt operator+(const BigInt& x, word y)
+ {
+ return BigInt::add2(x, &y, 1, BigInt::Positive);
+ }
+
+inline BigInt operator+(word x, const BigInt& y)
+ {
+ return y + x;
+ }
+
+inline BigInt operator-(const BigInt& x, const BigInt& y)
+ {
+ return BigInt::add2(x, y.data(), y.sig_words(), y.reverse_sign());
+ }
+
+inline BigInt operator-(const BigInt& x, word y)
+ {
+ return BigInt::add2(x, &y, 1, BigInt::Negative);
+ }
+
+BigInt BOTAN_PUBLIC_API(2,0) operator*(const BigInt& x, const BigInt& y);
+BigInt BOTAN_PUBLIC_API(2,8) operator*(const BigInt& x, word y);
+inline BigInt operator*(word x, const BigInt& y) { return y*x; }
+
+BigInt BOTAN_PUBLIC_API(2,0) operator/(const BigInt& x, const BigInt& d);
+BigInt BOTAN_PUBLIC_API(2,0) operator/(const BigInt& x, word m);
+BigInt BOTAN_PUBLIC_API(2,0) operator%(const BigInt& x, const BigInt& m);
+word BOTAN_PUBLIC_API(2,0) operator%(const BigInt& x, word m);
+BigInt BOTAN_PUBLIC_API(2,0) operator<<(const BigInt& x, size_t n);
+BigInt BOTAN_PUBLIC_API(2,0) operator>>(const BigInt& x, size_t n);
+
+/*
+* Comparison Operators
+*/
+inline bool operator==(const BigInt& a, const BigInt& b)
+ { return a.is_equal(b); }
+inline bool operator!=(const BigInt& a, const BigInt& b)
+ { return !a.is_equal(b); }
+inline bool operator<=(const BigInt& a, const BigInt& b)
+ { return (a.cmp(b) <= 0); }
+inline bool operator>=(const BigInt& a, const BigInt& b)
+ { return (a.cmp(b) >= 0); }
+inline bool operator<(const BigInt& a, const BigInt& b)
+ { return a.is_less_than(b); }
+inline bool operator>(const BigInt& a, const BigInt& b)
+ { return b.is_less_than(a); }
+
+inline bool operator==(const BigInt& a, word b)
+ { return (a.cmp_word(b) == 0); }
+inline bool operator!=(const BigInt& a, word b)
+ { return (a.cmp_word(b) != 0); }
+inline bool operator<=(const BigInt& a, word b)
+ { return (a.cmp_word(b) <= 0); }
+inline bool operator>=(const BigInt& a, word b)
+ { return (a.cmp_word(b) >= 0); }
+inline bool operator<(const BigInt& a, word b)
+ { return (a.cmp_word(b) < 0); }
+inline bool operator>(const BigInt& a, word b)
+ { return (a.cmp_word(b) > 0); }
+
+/*
+* I/O Operators
+*/
+BOTAN_PUBLIC_API(2,0) std::ostream& operator<<(std::ostream&, const BigInt&);
+BOTAN_PUBLIC_API(2,0) std::istream& operator>>(std::istream&, BigInt&);
+
+}
+
+namespace std {
+
+template<>
+inline void swap<Botan::BigInt>(Botan::BigInt& x, Botan::BigInt& y)
+ {
+ x.swap(y);
+ }
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/math/bigint/divide.cpp b/comm/third_party/botan/src/lib/math/bigint/divide.cpp
new file mode 100644
index 0000000000..0b23e2489e
--- /dev/null
+++ b/comm/third_party/botan/src/lib/math/bigint/divide.cpp
@@ -0,0 +1,236 @@
+/*
+* Division Algorithm
+* (C) 1999-2007,2012,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/divide.h>
+#include <botan/internal/mp_core.h>
+#include <botan/internal/mp_madd.h>
+#include <botan/internal/ct_utils.h>
+#include <botan/internal/bit_ops.h>
+
+namespace Botan {
+
+namespace {
+
+/*
+* Handle signed operands, if necessary
+*/
+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())
+ {
+ q -= 1;
+ r = y.abs() - r;
+ }
+ }
+
+inline bool division_check(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)
+ */
+
+ word y3 = 0;
+ 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).is_set();
+ }
+
+}
+
+void ct_divide(const BigInt& x, const BigInt& y, BigInt& q_out, BigInt& r_out)
+ {
+ const size_t x_words = x.sig_words();
+ const size_t y_words = y.sig_words();
+
+ const size_t x_bits = x.bits();
+
+ BigInt q(BigInt::Positive, x_words);
+ BigInt r(BigInt::Positive, y_words);
+ BigInt t(BigInt::Positive, y_words); // a temporary
+
+ for(size_t i = 0; i != x_bits; ++i)
+ {
+ const size_t b = x_bits - 1 - i;
+ const bool x_b = x.get_bit(b);
+
+ r *= 2;
+ r.conditionally_set_bit(0, x_b);
+
+ const bool r_gte_y = bigint_sub3(t.mutable_data(), r.data(), r.size(), y.data(), y_words) == 0;
+
+ q.conditionally_set_bit(b, r_gte_y);
+ r.ct_cond_swap(r_gte_y, t);
+ }
+
+ sign_fixup(x, y, q, r);
+ r_out = r;
+ q_out = q;
+ }
+
+void ct_divide_u8(const BigInt& x, uint8_t y, BigInt& q_out, uint8_t& r_out)
+ {
+ const size_t x_words = x.sig_words();
+ const size_t x_bits = x.bits();
+
+ BigInt q(BigInt::Positive, x_words);
+ uint32_t r = 0;
+
+ for(size_t i = 0; i != x_bits; ++i)
+ {
+ const size_t b = x_bits - 1 - i;
+ const bool x_b = x.get_bit(b);
+
+ r *= 2;
+ r += x_b;
+
+ const auto r_gte_y = CT::Mask<uint32_t>::is_gte(r, y);
+
+ q.conditionally_set_bit(b, r_gte_y.is_set());
+ r = r_gte_y.select(r - y, r);
+ }
+
+ if(x.is_negative())
+ {
+ q.flip_sign();
+ if(r != 0)
+ {
+ --q;
+ r = y - r;
+ }
+ }
+
+ r_out = static_cast<uint8_t>(r);
+ q_out = q;
+ }
+
+BigInt ct_modulo(const BigInt& x, const BigInt& y)
+ {
+ if(y.is_negative() || y.is_zero())
+ throw Invalid_Argument("ct_modulo requires y > 0");
+
+ const size_t y_words = y.sig_words();
+
+ const size_t x_bits = x.bits();
+
+ BigInt r(BigInt::Positive, y_words);
+ BigInt t(BigInt::Positive, y_words);
+
+ for(size_t i = 0; i != x_bits; ++i)
+ {
+ const size_t b = x_bits - 1 - i;
+ const bool x_b = x.get_bit(b);
+
+ r *= 2;
+ r.conditionally_set_bit(0, x_b);
+
+ const bool r_gte_y = bigint_sub3(t.mutable_data(), r.data(), r.size(), y.data(), y_words) == 0;
+
+ r.ct_cond_swap(r_gte_y, t);
+ }
+
+ if(x.is_negative())
+ {
+ if(r.is_nonzero())
+ {
+ r = y - r;
+ }
+ }
+
+ return r;
+ }
+
+/*
+* Solve x = q * y + r
+*
+* See Handbook of Applied Cryptography section 14.2.5
+*/
+void vartime_divide(const BigInt& x, const BigInt& y_arg, BigInt& q_out, BigInt& r_out)
+ {
+ if(y_arg.is_zero())
+ throw BigInt::DivideByZero();
+
+ const size_t y_words = y_arg.sig_words();
+
+ BOTAN_ASSERT_NOMSG(y_words > 0);
+
+ BigInt y = y_arg;
+
+ BigInt r = x;
+ BigInt q = 0;
+ secure_vector<word> ws;
+
+ r.set_sign(BigInt::Positive);
+ y.set_sign(BigInt::Positive);
+
+ // Calculate shifts needed to normalize y with high bit set
+ const size_t shifts = y.top_bits_free();
+
+ 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;
+ const size_t n = std::max(y_words, r.sig_words()) - 1; // r may have changed size however
+
+ BOTAN_ASSERT_NOMSG(n >= t);
+
+ q.grow_to(n - t + 1);
+
+ word* q_words = q.mutable_data();
+
+ BigInt shifted_y = y << (BOTAN_MP_WORD_BITS * (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);
+
+ 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);
+
+ word qjt = bigint_divop(x_j0, x_j1, y_t0);
+
+ qjt = CT::Mask<word>::is_equal(x_j0, y_t0).select(MP_WORD_MAX, qjt);
+
+ // 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);
+
+ shifted_y >>= BOTAN_MP_WORD_BITS;
+ // Now shifted_y == y << (BOTAN_MP_WORD_BITS * (j-t-1))
+
+ // TODO this sequence could be better
+ r -= qjt * shifted_y;
+ qjt -= r.is_negative();
+ r += static_cast<word>(r.is_negative()) * shifted_y;
+
+ q_words[j-t-1] = qjt;
+ }
+
+ r >>= shifts;
+
+ sign_fixup(x, y_arg, q, r);
+
+ r_out = r;
+ q_out = q;
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/math/bigint/divide.h b/comm/third_party/botan/src/lib/math/bigint/divide.h
new file mode 100644
index 0000000000..47141b3e7f
--- /dev/null
+++ b/comm/third_party/botan/src/lib/math/bigint/divide.h
@@ -0,0 +1,101 @@
+/*
+* Division
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_DIVISON_ALGORITHM_H_
+#define BOTAN_DIVISON_ALGORITHM_H_
+
+#include <botan/bigint.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(divide.h)
+
+namespace Botan {
+
+/**
+* BigInt Division
+* @param x an integer
+* @param y a non-zero integer
+* @param q will be set to x / y
+* @param r will be set to x % y
+*/
+void BOTAN_UNSTABLE_API vartime_divide(const BigInt& x,
+ const BigInt& y,
+ BigInt& q,
+ BigInt& r);
+
+/**
+* BigInt division, const time variant
+*
+* This runs with control flow independent of the values of x/y.
+* Warning: the loop bounds still leak the sizes of x and y.
+*
+* @param x an integer
+* @param y a non-zero integer
+* @param q will be set to x / y
+* @param r will be set to x % y
+*/
+void BOTAN_PUBLIC_API(2,9) ct_divide(const BigInt& x,
+ const BigInt& y,
+ BigInt& q,
+ BigInt& r);
+
+inline void divide(const BigInt& x,
+ const BigInt& y,
+ BigInt& q,
+ BigInt& r)
+ {
+ ct_divide(x, y, q, r);
+ }
+
+/**
+* BigInt division, const time variant
+*
+* This runs with control flow independent of the values of x/y.
+* Warning: the loop bounds still leak the sizes of x and y.
+*
+* @param x an integer
+* @param y a non-zero integer
+* @return x/y with remainder discarded
+*/
+inline BigInt ct_divide(const BigInt& x, const BigInt& y)
+ {
+ BigInt q, r;
+ ct_divide(x, y, q, r);
+ return q;
+ }
+
+/**
+* BigInt division, const time variant
+*
+* This runs with control flow independent of the values of x/y.
+* Warning: the loop bounds still leak the sizes of x and y.
+*
+* @param x an integer
+* @param y a non-zero integer
+* @param q will be set to x / y
+* @param r will be set to x % y
+*/
+void BOTAN_PUBLIC_API(2,9) ct_divide_u8(const BigInt& x,
+ uint8_t y,
+ BigInt& q,
+ uint8_t& r);
+
+/**
+* BigInt modulo, const time variant
+*
+* Using this function is (slightly) cheaper than calling ct_divide and
+* using only the remainder.
+*
+* @param x a non-negative integer
+* @param modulo a positive integer
+* @return result x % modulo
+*/
+BigInt BOTAN_PUBLIC_API(2,9) ct_modulo(const BigInt& x,
+ const BigInt& modulo);
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/math/bigint/info.txt b/comm/third_party/botan/src/lib/math/bigint/info.txt
new file mode 100644
index 0000000000..974366b810
--- /dev/null
+++ b/comm/third_party/botan/src/lib/math/bigint/info.txt
@@ -0,0 +1,14 @@
+<defines>
+BIGINT -> 20131128
+</defines>
+
+<header:public>
+bigint.h
+divide.h
+</header:public>
+
+<requires>
+mp
+hex
+rng
+</requires>
diff --git a/comm/third_party/botan/src/lib/math/mp/info.txt b/comm/third_party/botan/src/lib/math/mp/info.txt
new file mode 100644
index 0000000000..cee4325ed8
--- /dev/null
+++ b/comm/third_party/botan/src/lib/math/mp/info.txt
@@ -0,0 +1,10 @@
+<defines>
+BIGINT_MP -> 20151225
+</defines>
+
+<header:internal>
+mp_core.h
+mp_madd.h
+mp_asmi.h
+mp_monty.h
+</header:internal>
diff --git a/comm/third_party/botan/src/lib/math/mp/mp_asmi.h b/comm/third_party/botan/src/lib/math/mp/mp_asmi.h
new file mode 100644
index 0000000000..e1518d51c7
--- /dev/null
+++ b/comm/third_party/botan/src/lib/math/mp/mp_asmi.h
@@ -0,0 +1,611 @@
+/*
+* Lowest Level MPI Algorithms
+* (C) 1999-2010 Jack Lloyd
+* 2006 Luca Piccarreta
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_MP_ASM_INTERNAL_H_
+#define BOTAN_MP_ASM_INTERNAL_H_
+
+#include <botan/internal/mp_madd.h>
+
+namespace Botan {
+
+#if defined(BOTAN_MP_USE_X86_32_ASM)
+
+#define ADDSUB2_OP(OPERATION, INDEX) \
+ ASM("movl 4*" #INDEX "(%[y]), %[carry]") \
+ ASM(OPERATION " %[carry], 4*" #INDEX "(%[x])") \
+
+#define ADDSUB3_OP(OPERATION, INDEX) \
+ ASM("movl 4*" #INDEX "(%[x]), %[carry]") \
+ ASM(OPERATION " 4*" #INDEX "(%[y]), %[carry]") \
+ ASM("movl %[carry], 4*" #INDEX "(%[z])") \
+
+#define LINMUL_OP(WRITE_TO, INDEX) \
+ ASM("movl 4*" #INDEX "(%[x]),%%eax") \
+ ASM("mull %[y]") \
+ ASM("addl %[carry],%%eax") \
+ ASM("adcl $0,%%edx") \
+ ASM("movl %%edx,%[carry]") \
+ ASM("movl %%eax, 4*" #INDEX "(%[" WRITE_TO "])")
+
+#define MULADD_OP(IGNORED, INDEX) \
+ ASM("movl 4*" #INDEX "(%[x]),%%eax") \
+ ASM("mull %[y]") \
+ ASM("addl %[carry],%%eax") \
+ ASM("adcl $0,%%edx") \
+ ASM("addl 4*" #INDEX "(%[z]),%%eax") \
+ ASM("adcl $0,%%edx") \
+ ASM("movl %%edx,%[carry]") \
+ ASM("movl %%eax, 4*" #INDEX " (%[z])")
+
+#define ADD_OR_SUBTRACT(CORE_CODE) \
+ ASM("rorl %[carry]") \
+ CORE_CODE \
+ ASM("sbbl %[carry],%[carry]") \
+ ASM("negl %[carry]")
+
+#elif defined(BOTAN_MP_USE_X86_64_ASM)
+
+#define ADDSUB2_OP(OPERATION, INDEX) \
+ ASM("movq 8*" #INDEX "(%[y]), %[carry]") \
+ ASM(OPERATION " %[carry], 8*" #INDEX "(%[x])") \
+
+#define ADDSUB3_OP(OPERATION, INDEX) \
+ ASM("movq 8*" #INDEX "(%[x]), %[carry]") \
+ ASM(OPERATION " 8*" #INDEX "(%[y]), %[carry]") \
+ ASM("movq %[carry], 8*" #INDEX "(%[z])") \
+
+#define LINMUL_OP(WRITE_TO, INDEX) \
+ ASM("movq 8*" #INDEX "(%[x]),%%rax") \
+ ASM("mulq %[y]") \
+ ASM("addq %[carry],%%rax") \
+ ASM("adcq $0,%%rdx") \
+ ASM("movq %%rdx,%[carry]") \
+ ASM("movq %%rax, 8*" #INDEX "(%[" WRITE_TO "])")
+
+#define MULADD_OP(IGNORED, INDEX) \
+ ASM("movq 8*" #INDEX "(%[x]),%%rax") \
+ ASM("mulq %[y]") \
+ ASM("addq %[carry],%%rax") \
+ ASM("adcq $0,%%rdx") \
+ ASM("addq 8*" #INDEX "(%[z]),%%rax") \
+ ASM("adcq $0,%%rdx") \
+ ASM("movq %%rdx,%[carry]") \
+ ASM("movq %%rax, 8*" #INDEX " (%[z])")
+
+#define ADD_OR_SUBTRACT(CORE_CODE) \
+ ASM("rorq %[carry]") \
+ CORE_CODE \
+ ASM("sbbq %[carry],%[carry]") \
+ ASM("negq %[carry]")
+
+#endif
+
+#if defined(ADD_OR_SUBTRACT)
+
+#define ASM(x) x "\n\t"
+
+#define DO_8_TIMES(MACRO, ARG) \
+ MACRO(ARG, 0) \
+ MACRO(ARG, 1) \
+ MACRO(ARG, 2) \
+ MACRO(ARG, 3) \
+ MACRO(ARG, 4) \
+ MACRO(ARG, 5) \
+ MACRO(ARG, 6) \
+ MACRO(ARG, 7)
+
+#endif
+
+/*
+* Word Addition
+*/
+inline word word_add(word x, word y, word* carry)
+ {
+#if defined(BOTAN_MP_USE_X86_32_ASM)
+ asm(
+ ADD_OR_SUBTRACT(ASM("adcl %[y],%[x]"))
+ : [x]"=r"(x), [carry]"=r"(*carry)
+ : "0"(x), [y]"rm"(y), "1"(*carry)
+ : "cc");
+ return x;
+
+#elif defined(BOTAN_MP_USE_X86_64_ASM)
+
+ asm(
+ ADD_OR_SUBTRACT(ASM("adcq %[y],%[x]"))
+ : [x]"=r"(x), [carry]"=r"(*carry)
+ : "0"(x), [y]"rm"(y), "1"(*carry)
+ : "cc");
+ return x;
+
+#else
+ word z = x + y;
+ word c1 = (z < x);
+ z += *carry;
+ *carry = c1 | (z < *carry);
+ return z;
+#endif
+ }
+
+/*
+* Eight Word Block Addition, Two Argument
+*/
+inline word word8_add2(word x[8], const word y[8], word carry)
+ {
+#if defined(BOTAN_MP_USE_X86_32_ASM)
+ asm(
+ ADD_OR_SUBTRACT(DO_8_TIMES(ADDSUB2_OP, "adcl"))
+ : [carry]"=r"(carry)
+ : [x]"r"(x), [y]"r"(y), "0"(carry)
+ : "cc", "memory");
+ return carry;
+
+#elif defined(BOTAN_MP_USE_X86_64_ASM)
+
+ asm(
+ ADD_OR_SUBTRACT(DO_8_TIMES(ADDSUB2_OP, "adcq"))
+ : [carry]"=r"(carry)
+ : [x]"r"(x), [y]"r"(y), "0"(carry)
+ : "cc", "memory");
+ return carry;
+
+#else
+ x[0] = word_add(x[0], y[0], &carry);
+ x[1] = word_add(x[1], y[1], &carry);
+ x[2] = word_add(x[2], y[2], &carry);
+ x[3] = word_add(x[3], y[3], &carry);
+ x[4] = word_add(x[4], y[4], &carry);
+ x[5] = word_add(x[5], y[5], &carry);
+ x[6] = word_add(x[6], y[6], &carry);
+ x[7] = word_add(x[7], y[7], &carry);
+ return carry;
+#endif
+ }
+
+/*
+* Eight Word Block Addition, Three Argument
+*/
+inline word word8_add3(word z[8], const word x[8],
+ const word y[8], word carry)
+ {
+#if defined(BOTAN_MP_USE_X86_32_ASM)
+ asm(
+ ADD_OR_SUBTRACT(DO_8_TIMES(ADDSUB3_OP, "adcl"))
+ : [carry]"=r"(carry)
+ : [x]"r"(x), [y]"r"(y), [z]"r"(z), "0"(carry)
+ : "cc", "memory");
+ return carry;
+
+#elif defined(BOTAN_MP_USE_X86_64_ASM)
+
+ asm(
+ ADD_OR_SUBTRACT(DO_8_TIMES(ADDSUB3_OP, "adcq"))
+ : [carry]"=r"(carry)
+ : [x]"r"(x), [y]"r"(y), [z]"r"(z), "0"(carry)
+ : "cc", "memory");
+ return carry;
+
+#else
+ 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);
+ z[4] = word_add(x[4], y[4], &carry);
+ z[5] = word_add(x[5], y[5], &carry);
+ z[6] = word_add(x[6], y[6], &carry);
+ z[7] = word_add(x[7], y[7], &carry);
+ return carry;
+#endif
+ }
+
+/*
+* Word Subtraction
+*/
+inline word word_sub(word x, word y, word* carry)
+ {
+#if defined(BOTAN_MP_USE_X86_32_ASM)
+ asm(
+ ADD_OR_SUBTRACT(ASM("sbbl %[y],%[x]"))
+ : [x]"=r"(x), [carry]"=r"(*carry)
+ : "0"(x), [y]"rm"(y), "1"(*carry)
+ : "cc");
+ return x;
+
+#elif defined(BOTAN_MP_USE_X86_64_ASM)
+
+ asm(
+ ADD_OR_SUBTRACT(ASM("sbbq %[y],%[x]"))
+ : [x]"=r"(x), [carry]"=r"(*carry)
+ : "0"(x), [y]"rm"(y), "1"(*carry)
+ : "cc");
+ return x;
+
+#else
+ word t0 = x - y;
+ word c1 = (t0 > x);
+ word z = t0 - *carry;
+ *carry = c1 | (z > t0);
+ return z;
+#endif
+ }
+
+/*
+* Eight Word Block Subtraction, Two Argument
+*/
+inline word word8_sub2(word x[8], const word y[8], word carry)
+ {
+#if defined(BOTAN_MP_USE_X86_32_ASM)
+ asm(
+ ADD_OR_SUBTRACT(DO_8_TIMES(ADDSUB2_OP, "sbbl"))
+ : [carry]"=r"(carry)
+ : [x]"r"(x), [y]"r"(y), "0"(carry)
+ : "cc", "memory");
+ return carry;
+
+#elif defined(BOTAN_MP_USE_X86_64_ASM)
+
+ asm(
+ ADD_OR_SUBTRACT(DO_8_TIMES(ADDSUB2_OP, "sbbq"))
+ : [carry]"=r"(carry)
+ : [x]"r"(x), [y]"r"(y), "0"(carry)
+ : "cc", "memory");
+ return carry;
+
+#else
+ x[0] = word_sub(x[0], y[0], &carry);
+ x[1] = word_sub(x[1], y[1], &carry);
+ x[2] = word_sub(x[2], y[2], &carry);
+ x[3] = word_sub(x[3], y[3], &carry);
+ x[4] = word_sub(x[4], y[4], &carry);
+ x[5] = word_sub(x[5], y[5], &carry);
+ x[6] = word_sub(x[6], y[6], &carry);
+ x[7] = word_sub(x[7], y[7], &carry);
+ return carry;
+#endif
+ }
+
+/*
+* Eight Word Block Subtraction, Two Argument
+*/
+inline word word8_sub2_rev(word x[8], const word y[8], word carry)
+ {
+#if defined(BOTAN_MP_USE_X86_32_ASM)
+ asm(
+ ADD_OR_SUBTRACT(DO_8_TIMES(ADDSUB3_OP, "sbbl"))
+ : [carry]"=r"(carry)
+ : [x]"r"(y), [y]"r"(x), [z]"r"(x), "0"(carry)
+ : "cc", "memory");
+ return carry;
+
+#elif defined(BOTAN_MP_USE_X86_64_ASM)
+
+ 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;
+
+#else
+ 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;
+#endif
+ }
+
+/*
+* Eight Word Block Subtraction, Three Argument
+*/
+inline word word8_sub3(word z[8], const word x[8],
+ const word y[8], word carry)
+ {
+#if defined(BOTAN_MP_USE_X86_32_ASM)
+ asm(
+ ADD_OR_SUBTRACT(DO_8_TIMES(ADDSUB3_OP, "sbbl"))
+ : [carry]"=r"(carry)
+ : [x]"r"(x), [y]"r"(y), [z]"r"(z), "0"(carry)
+ : "cc", "memory");
+ return carry;
+
+#elif defined(BOTAN_MP_USE_X86_64_ASM)
+
+ asm(
+ ADD_OR_SUBTRACT(DO_8_TIMES(ADDSUB3_OP, "sbbq"))
+ : [carry]"=r"(carry)
+ : [x]"r"(x), [y]"r"(y), [z]"r"(z), "0"(carry)
+ : "cc", "memory");
+ return carry;
+
+#else
+ 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);
+ z[4] = word_sub(x[4], y[4], &carry);
+ z[5] = word_sub(x[5], y[5], &carry);
+ z[6] = word_sub(x[6], y[6], &carry);
+ z[7] = word_sub(x[7], y[7], &carry);
+ return carry;
+#endif
+ }
+
+/*
+* Eight Word Block Linear Multiplication
+*/
+inline word word8_linmul2(word x[8], word y, word carry)
+ {
+#if defined(BOTAN_MP_USE_X86_32_ASM)
+ asm(
+ DO_8_TIMES(LINMUL_OP, "x")
+ : [carry]"=r"(carry)
+ : [x]"r"(x), [y]"rm"(y), "0"(carry)
+ : "cc", "%eax", "%edx");
+ return carry;
+
+#elif defined(BOTAN_MP_USE_X86_64_ASM)
+
+ asm(
+ DO_8_TIMES(LINMUL_OP, "x")
+ : [carry]"=r"(carry)
+ : [x]"r"(x), [y]"rm"(y), "0"(carry)
+ : "cc", "%rax", "%rdx");
+ return carry;
+
+#else
+ 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;
+#endif
+ }
+
+/*
+* Eight Word Block Linear Multiplication
+*/
+inline word word8_linmul3(word z[8], const word x[8], word y, word carry)
+ {
+#if defined(BOTAN_MP_USE_X86_32_ASM)
+ asm(
+ DO_8_TIMES(LINMUL_OP, "z")
+ : [carry]"=r"(carry)
+ : [z]"r"(z), [x]"r"(x), [y]"rm"(y), "0"(carry)
+ : "cc", "%eax", "%edx");
+ return carry;
+
+#elif defined(BOTAN_MP_USE_X86_64_ASM)
+ asm(
+ DO_8_TIMES(LINMUL_OP, "z")
+ : [carry]"=r"(carry)
+ : [z]"r"(z), [x]"r"(x), [y]"rm"(y), "0"(carry)
+ : "cc", "%rax", "%rdx");
+ return carry;
+
+#else
+ z[0] = word_madd2(x[0], y, &carry);
+ z[1] = word_madd2(x[1], y, &carry);
+ z[2] = word_madd2(x[2], y, &carry);
+ z[3] = word_madd2(x[3], y, &carry);
+ z[4] = word_madd2(x[4], y, &carry);
+ z[5] = word_madd2(x[5], y, &carry);
+ z[6] = word_madd2(x[6], y, &carry);
+ z[7] = word_madd2(x[7], y, &carry);
+ return carry;
+#endif
+ }
+
+/*
+* Eight Word Block Multiply/Add
+*/
+inline word word8_madd3(word z[8], const word x[8], word y, word carry)
+ {
+#if defined(BOTAN_MP_USE_X86_32_ASM)
+ asm(
+ DO_8_TIMES(MULADD_OP, "")
+ : [carry]"=r"(carry)
+ : [z]"r"(z), [x]"r"(x), [y]"rm"(y), "0"(carry)
+ : "cc", "%eax", "%edx");
+ return carry;
+
+#elif defined(BOTAN_MP_USE_X86_64_ASM)
+
+ asm(
+ DO_8_TIMES(MULADD_OP, "")
+ : [carry]"=r"(carry)
+ : [z]"r"(z), [x]"r"(x), [y]"rm"(y), "0"(carry)
+ : "cc", "%rax", "%rdx");
+ return carry;
+
+#else
+ z[0] = word_madd3(x[0], y, z[0], &carry);
+ z[1] = word_madd3(x[1], y, z[1], &carry);
+ z[2] = word_madd3(x[2], y, z[2], &carry);
+ z[3] = word_madd3(x[3], y, z[3], &carry);
+ z[4] = word_madd3(x[4], y, z[4], &carry);
+ z[5] = word_madd3(x[5], y, z[5], &carry);
+ z[6] = word_madd3(x[6], y, z[6], &carry);
+ z[7] = word_madd3(x[7], y, z[7], &carry);
+ return carry;
+#endif
+ }
+
+/*
+* Multiply-Add Accumulator
+* (w2,w1,w0) += x * y
+*/
+inline void word3_muladd(word* w2, word* w1, word* w0, word x, word y)
+ {
+#if defined(BOTAN_MP_USE_X86_32_ASM)
+ word z0 = 0, z1 = 0;
+
+ asm("mull %[y]"
+ : "=a"(z0),"=d"(z1)
+ : "a"(x), [y]"rm"(y)
+ : "cc");
+
+ asm(R"(
+ addl %[z0],%[w0]
+ adcl %[z1],%[w1]
+ adcl $0,%[w2]
+ )"
+ : [w0]"=r"(*w0), [w1]"=r"(*w1), [w2]"=r"(*w2)
+ : [z0]"r"(z0), [z1]"r"(z1), "0"(*w0), "1"(*w1), "2"(*w2)
+ : "cc");
+
+#elif defined(BOTAN_MP_USE_X86_64_ASM)
+
+ word 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");
+
+#else
+ word carry = *w0;
+ *w0 = word_madd2(x, y, &carry);
+ *w1 += carry;
+ *w2 += (*w1 < carry);
+#endif
+ }
+
+/*
+* 3-word addition
+* (w2,w1,w0) += x
+*/
+inline void word3_add(word* w2, word* w1, word* w0, word x)
+ {
+#if defined(BOTAN_MP_USE_X86_32_ASM)
+ asm(R"(
+ addl %[x],%[w0]
+ adcl $0,%[w1]
+ adcl $0,%[w2]
+ )"
+ : [w0]"=r"(*w0), [w1]"=r"(*w1), [w2]"=r"(*w2)
+ : [x]"r"(x), "0"(*w0), "1"(*w1), "2"(*w2)
+ : "cc");
+
+#elif defined(BOTAN_MP_USE_X86_64_ASM)
+
+ 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");
+
+#else
+ *w0 += x;
+ word c1 = (*w0 < x);
+ *w1 += c1;
+ word c2 = (*w1 < c1);
+ *w2 += c2;
+#endif
+ }
+
+/*
+* Multiply-Add Accumulator
+* (w2,w1,w0) += 2 * x * y
+*/
+inline void word3_muladd_2(word* w2, word* w1, word* w0, word x, word y)
+ {
+#if defined(BOTAN_MP_USE_X86_32_ASM)
+
+ word z0 = 0, z1 = 0;
+
+ asm("mull %[y]"
+ : "=a"(z0),"=d"(z1)
+ : "a"(x), [y]"rm"(y)
+ : "cc");
+
+ asm(R"(
+ addl %[z0],%[w0]
+ adcl %[z1],%[w1]
+ adcl $0,%[w2]
+
+ addl %[z0],%[w0]
+ adcl %[z1],%[w1]
+ adcl $0,%[w2]
+ )"
+ : [w0]"=r"(*w0), [w1]"=r"(*w1), [w2]"=r"(*w2)
+ : [z0]"r"(z0), [z1]"r"(z1), "0"(*w0), "1"(*w1), "2"(*w2)
+ : "cc");
+
+#elif defined(BOTAN_MP_USE_X86_64_ASM)
+
+ word 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");
+
+#else
+ word carry = 0;
+ x = word_madd2(x, y, &carry);
+ y = carry;
+
+ word top = (y >> (BOTAN_MP_WORD_BITS-1));
+ y <<= 1;
+ y |= (x >> (BOTAN_MP_WORD_BITS-1));
+ x <<= 1;
+
+ carry = 0;
+ *w0 = word_add(*w0, x, &carry);
+ *w1 = word_add(*w1, y, &carry);
+ *w2 = word_add(*w2, top, &carry);
+#endif
+ }
+
+#if defined(ASM)
+ #undef ASM
+ #undef DO_8_TIMES
+ #undef ADD_OR_SUBTRACT
+ #undef ADDSUB2_OP
+ #undef ADDSUB3_OP
+ #undef LINMUL_OP
+ #undef MULADD_OP
+#endif
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/math/mp/mp_comba.cpp b/comm/third_party/botan/src/lib/math/mp/mp_comba.cpp
new file mode 100644
index 0000000000..ec527224c8
--- /dev/null
+++ b/comm/third_party/botan/src/lib/math/mp/mp_comba.cpp
@@ -0,0 +1,2211 @@
+/*
+* Comba Multiplication and Squaring
+*
+* This file was automatically generated by ./src/scripts/comba.py on 2018-05-08
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/mp_core.h>
+#include <botan/internal/mp_asmi.h>
+
+namespace Botan {
+
+/*
+* Comba 4x4 Squaring
+*/
+void bigint_comba_sqr4(word z[8], const word x[4])
+ {
+ word w2 = 0, w1 = 0, w0 = 0;
+
+ word3_muladd (&w2, &w1, &w0, x[ 0], x[ 0]);
+ z[ 0] = w0; w0 = 0;
+
+ word3_muladd_2(&w0, &w2, &w1, x[ 0], x[ 1]);
+ z[ 1] = w1; w1 = 0;
+
+ word3_muladd_2(&w1, &w0, &w2, x[ 0], x[ 2]);
+ word3_muladd (&w1, &w0, &w2, x[ 1], x[ 1]);
+ z[ 2] = w2; w2 = 0;
+
+ word3_muladd_2(&w2, &w1, &w0, x[ 0], x[ 3]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 1], x[ 2]);
+ z[ 3] = w0; w0 = 0;
+
+ word3_muladd_2(&w0, &w2, &w1, x[ 1], x[ 3]);
+ word3_muladd (&w0, &w2, &w1, x[ 2], x[ 2]);
+ z[ 4] = w1; w1 = 0;
+
+ word3_muladd_2(&w1, &w0, &w2, x[ 2], x[ 3]);
+ z[ 5] = w2; w2 = 0;
+
+ word3_muladd (&w2, &w1, &w0, x[ 3], x[ 3]);
+ z[ 6] = w0;
+ z[ 7] = w1;
+ }
+
+/*
+* Comba 4x4 Multiplication
+*/
+void bigint_comba_mul4(word z[8], const word x[4], const word y[4])
+ {
+ word w2 = 0, w1 = 0, w0 = 0;
+
+ word3_muladd(&w2, &w1, &w0, x[ 0], y[ 0]);
+ z[ 0] = w0; w0 = 0;
+
+ word3_muladd(&w0, &w2, &w1, x[ 0], y[ 1]);
+ word3_muladd(&w0, &w2, &w1, x[ 1], y[ 0]);
+ z[ 1] = w1; w1 = 0;
+
+ word3_muladd(&w1, &w0, &w2, x[ 0], y[ 2]);
+ word3_muladd(&w1, &w0, &w2, x[ 1], y[ 1]);
+ word3_muladd(&w1, &w0, &w2, x[ 2], y[ 0]);
+ z[ 2] = w2; w2 = 0;
+
+ word3_muladd(&w2, &w1, &w0, x[ 0], y[ 3]);
+ word3_muladd(&w2, &w1, &w0, x[ 1], y[ 2]);
+ word3_muladd(&w2, &w1, &w0, x[ 2], y[ 1]);
+ word3_muladd(&w2, &w1, &w0, x[ 3], y[ 0]);
+ z[ 3] = w0; w0 = 0;
+
+ word3_muladd(&w0, &w2, &w1, x[ 1], y[ 3]);
+ word3_muladd(&w0, &w2, &w1, x[ 2], y[ 2]);
+ word3_muladd(&w0, &w2, &w1, x[ 3], y[ 1]);
+ z[ 4] = w1; w1 = 0;
+
+ word3_muladd(&w1, &w0, &w2, x[ 2], y[ 3]);
+ word3_muladd(&w1, &w0, &w2, x[ 3], y[ 2]);
+ z[ 5] = w2; w2 = 0;
+
+ word3_muladd(&w2, &w1, &w0, x[ 3], y[ 3]);
+ z[ 6] = w0;
+ z[ 7] = w1;
+ }
+
+/*
+* Comba 6x6 Squaring
+*/
+void bigint_comba_sqr6(word z[12], const word x[6])
+ {
+ word w2 = 0, w1 = 0, w0 = 0;
+
+ word3_muladd (&w2, &w1, &w0, x[ 0], x[ 0]);
+ z[ 0] = w0; w0 = 0;
+
+ word3_muladd_2(&w0, &w2, &w1, x[ 0], x[ 1]);
+ z[ 1] = w1; w1 = 0;
+
+ word3_muladd_2(&w1, &w0, &w2, x[ 0], x[ 2]);
+ word3_muladd (&w1, &w0, &w2, x[ 1], x[ 1]);
+ z[ 2] = w2; w2 = 0;
+
+ word3_muladd_2(&w2, &w1, &w0, x[ 0], x[ 3]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 1], x[ 2]);
+ z[ 3] = w0; w0 = 0;
+
+ word3_muladd_2(&w0, &w2, &w1, x[ 0], x[ 4]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 1], x[ 3]);
+ word3_muladd (&w0, &w2, &w1, x[ 2], x[ 2]);
+ z[ 4] = w1; w1 = 0;
+
+ word3_muladd_2(&w1, &w0, &w2, x[ 0], x[ 5]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 1], x[ 4]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 2], x[ 3]);
+ z[ 5] = w2; w2 = 0;
+
+ word3_muladd_2(&w2, &w1, &w0, x[ 1], x[ 5]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 2], x[ 4]);
+ word3_muladd (&w2, &w1, &w0, x[ 3], x[ 3]);
+ z[ 6] = w0; w0 = 0;
+
+ word3_muladd_2(&w0, &w2, &w1, x[ 2], x[ 5]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 3], x[ 4]);
+ z[ 7] = w1; w1 = 0;
+
+ word3_muladd_2(&w1, &w0, &w2, x[ 3], x[ 5]);
+ word3_muladd (&w1, &w0, &w2, x[ 4], x[ 4]);
+ z[ 8] = w2; w2 = 0;
+
+ word3_muladd_2(&w2, &w1, &w0, x[ 4], x[ 5]);
+ z[ 9] = w0; w0 = 0;
+
+ word3_muladd (&w0, &w2, &w1, x[ 5], x[ 5]);
+ z[10] = w1;
+ z[11] = w2;
+ }
+
+/*
+* Comba 6x6 Multiplication
+*/
+void bigint_comba_mul6(word z[12], const word x[6], const word y[6])
+ {
+ word w2 = 0, w1 = 0, w0 = 0;
+
+ word3_muladd(&w2, &w1, &w0, x[ 0], y[ 0]);
+ z[ 0] = w0; w0 = 0;
+
+ word3_muladd(&w0, &w2, &w1, x[ 0], y[ 1]);
+ word3_muladd(&w0, &w2, &w1, x[ 1], y[ 0]);
+ z[ 1] = w1; w1 = 0;
+
+ word3_muladd(&w1, &w0, &w2, x[ 0], y[ 2]);
+ word3_muladd(&w1, &w0, &w2, x[ 1], y[ 1]);
+ word3_muladd(&w1, &w0, &w2, x[ 2], y[ 0]);
+ z[ 2] = w2; w2 = 0;
+
+ word3_muladd(&w2, &w1, &w0, x[ 0], y[ 3]);
+ word3_muladd(&w2, &w1, &w0, x[ 1], y[ 2]);
+ word3_muladd(&w2, &w1, &w0, x[ 2], y[ 1]);
+ word3_muladd(&w2, &w1, &w0, x[ 3], y[ 0]);
+ z[ 3] = w0; w0 = 0;
+
+ word3_muladd(&w0, &w2, &w1, x[ 0], y[ 4]);
+ word3_muladd(&w0, &w2, &w1, x[ 1], y[ 3]);
+ word3_muladd(&w0, &w2, &w1, x[ 2], y[ 2]);
+ word3_muladd(&w0, &w2, &w1, x[ 3], y[ 1]);
+ word3_muladd(&w0, &w2, &w1, x[ 4], y[ 0]);
+ z[ 4] = w1; w1 = 0;
+
+ word3_muladd(&w1, &w0, &w2, x[ 0], y[ 5]);
+ word3_muladd(&w1, &w0, &w2, x[ 1], y[ 4]);
+ word3_muladd(&w1, &w0, &w2, x[ 2], y[ 3]);
+ word3_muladd(&w1, &w0, &w2, x[ 3], y[ 2]);
+ word3_muladd(&w1, &w0, &w2, x[ 4], y[ 1]);
+ word3_muladd(&w1, &w0, &w2, x[ 5], y[ 0]);
+ z[ 5] = w2; w2 = 0;
+
+ word3_muladd(&w2, &w1, &w0, x[ 1], y[ 5]);
+ word3_muladd(&w2, &w1, &w0, x[ 2], y[ 4]);
+ word3_muladd(&w2, &w1, &w0, x[ 3], y[ 3]);
+ word3_muladd(&w2, &w1, &w0, x[ 4], y[ 2]);
+ word3_muladd(&w2, &w1, &w0, x[ 5], y[ 1]);
+ z[ 6] = w0; w0 = 0;
+
+ word3_muladd(&w0, &w2, &w1, x[ 2], y[ 5]);
+ word3_muladd(&w0, &w2, &w1, x[ 3], y[ 4]);
+ word3_muladd(&w0, &w2, &w1, x[ 4], y[ 3]);
+ word3_muladd(&w0, &w2, &w1, x[ 5], y[ 2]);
+ z[ 7] = w1; w1 = 0;
+
+ word3_muladd(&w1, &w0, &w2, x[ 3], y[ 5]);
+ word3_muladd(&w1, &w0, &w2, x[ 4], y[ 4]);
+ word3_muladd(&w1, &w0, &w2, x[ 5], y[ 3]);
+ z[ 8] = w2; w2 = 0;
+
+ word3_muladd(&w2, &w1, &w0, x[ 4], y[ 5]);
+ word3_muladd(&w2, &w1, &w0, x[ 5], y[ 4]);
+ z[ 9] = w0; w0 = 0;
+
+ word3_muladd(&w0, &w2, &w1, x[ 5], y[ 5]);
+ z[10] = w1;
+ z[11] = w2;
+ }
+
+/*
+* Comba 8x8 Squaring
+*/
+void bigint_comba_sqr8(word z[16], const word x[8])
+ {
+ word w2 = 0, w1 = 0, w0 = 0;
+
+ word3_muladd (&w2, &w1, &w0, x[ 0], x[ 0]);
+ z[ 0] = w0; w0 = 0;
+
+ word3_muladd_2(&w0, &w2, &w1, x[ 0], x[ 1]);
+ z[ 1] = w1; w1 = 0;
+
+ word3_muladd_2(&w1, &w0, &w2, x[ 0], x[ 2]);
+ word3_muladd (&w1, &w0, &w2, x[ 1], x[ 1]);
+ z[ 2] = w2; w2 = 0;
+
+ word3_muladd_2(&w2, &w1, &w0, x[ 0], x[ 3]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 1], x[ 2]);
+ z[ 3] = w0; w0 = 0;
+
+ word3_muladd_2(&w0, &w2, &w1, x[ 0], x[ 4]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 1], x[ 3]);
+ word3_muladd (&w0, &w2, &w1, x[ 2], x[ 2]);
+ z[ 4] = w1; w1 = 0;
+
+ word3_muladd_2(&w1, &w0, &w2, x[ 0], x[ 5]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 1], x[ 4]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 2], x[ 3]);
+ z[ 5] = w2; w2 = 0;
+
+ word3_muladd_2(&w2, &w1, &w0, x[ 0], x[ 6]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 1], x[ 5]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 2], x[ 4]);
+ word3_muladd (&w2, &w1, &w0, x[ 3], x[ 3]);
+ z[ 6] = w0; w0 = 0;
+
+ word3_muladd_2(&w0, &w2, &w1, x[ 0], x[ 7]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 1], x[ 6]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 2], x[ 5]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 3], x[ 4]);
+ z[ 7] = w1; w1 = 0;
+
+ word3_muladd_2(&w1, &w0, &w2, x[ 1], x[ 7]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 2], x[ 6]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 3], x[ 5]);
+ word3_muladd (&w1, &w0, &w2, x[ 4], x[ 4]);
+ z[ 8] = w2; w2 = 0;
+
+ word3_muladd_2(&w2, &w1, &w0, x[ 2], x[ 7]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 3], x[ 6]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 4], x[ 5]);
+ z[ 9] = w0; w0 = 0;
+
+ word3_muladd_2(&w0, &w2, &w1, x[ 3], x[ 7]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 4], x[ 6]);
+ word3_muladd (&w0, &w2, &w1, x[ 5], x[ 5]);
+ z[10] = w1; w1 = 0;
+
+ word3_muladd_2(&w1, &w0, &w2, x[ 4], x[ 7]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 5], x[ 6]);
+ z[11] = w2; w2 = 0;
+
+ word3_muladd_2(&w2, &w1, &w0, x[ 5], x[ 7]);
+ word3_muladd (&w2, &w1, &w0, x[ 6], x[ 6]);
+ z[12] = w0; w0 = 0;
+
+ word3_muladd_2(&w0, &w2, &w1, x[ 6], x[ 7]);
+ z[13] = w1; w1 = 0;
+
+ word3_muladd (&w1, &w0, &w2, x[ 7], x[ 7]);
+ z[14] = w2;
+ z[15] = w0;
+ }
+
+/*
+* Comba 8x8 Multiplication
+*/
+void bigint_comba_mul8(word z[16], const word x[8], const word y[8])
+ {
+ word w2 = 0, w1 = 0, w0 = 0;
+
+ word3_muladd(&w2, &w1, &w0, x[ 0], y[ 0]);
+ z[ 0] = w0; w0 = 0;
+
+ word3_muladd(&w0, &w2, &w1, x[ 0], y[ 1]);
+ word3_muladd(&w0, &w2, &w1, x[ 1], y[ 0]);
+ z[ 1] = w1; w1 = 0;
+
+ word3_muladd(&w1, &w0, &w2, x[ 0], y[ 2]);
+ word3_muladd(&w1, &w0, &w2, x[ 1], y[ 1]);
+ word3_muladd(&w1, &w0, &w2, x[ 2], y[ 0]);
+ z[ 2] = w2; w2 = 0;
+
+ word3_muladd(&w2, &w1, &w0, x[ 0], y[ 3]);
+ word3_muladd(&w2, &w1, &w0, x[ 1], y[ 2]);
+ word3_muladd(&w2, &w1, &w0, x[ 2], y[ 1]);
+ word3_muladd(&w2, &w1, &w0, x[ 3], y[ 0]);
+ z[ 3] = w0; w0 = 0;
+
+ word3_muladd(&w0, &w2, &w1, x[ 0], y[ 4]);
+ word3_muladd(&w0, &w2, &w1, x[ 1], y[ 3]);
+ word3_muladd(&w0, &w2, &w1, x[ 2], y[ 2]);
+ word3_muladd(&w0, &w2, &w1, x[ 3], y[ 1]);
+ word3_muladd(&w0, &w2, &w1, x[ 4], y[ 0]);
+ z[ 4] = w1; w1 = 0;
+
+ word3_muladd(&w1, &w0, &w2, x[ 0], y[ 5]);
+ word3_muladd(&w1, &w0, &w2, x[ 1], y[ 4]);
+ word3_muladd(&w1, &w0, &w2, x[ 2], y[ 3]);
+ word3_muladd(&w1, &w0, &w2, x[ 3], y[ 2]);
+ word3_muladd(&w1, &w0, &w2, x[ 4], y[ 1]);
+ word3_muladd(&w1, &w0, &w2, x[ 5], y[ 0]);
+ z[ 5] = w2; w2 = 0;
+
+ word3_muladd(&w2, &w1, &w0, x[ 0], y[ 6]);
+ word3_muladd(&w2, &w1, &w0, x[ 1], y[ 5]);
+ word3_muladd(&w2, &w1, &w0, x[ 2], y[ 4]);
+ word3_muladd(&w2, &w1, &w0, x[ 3], y[ 3]);
+ word3_muladd(&w2, &w1, &w0, x[ 4], y[ 2]);
+ word3_muladd(&w2, &w1, &w0, x[ 5], y[ 1]);
+ word3_muladd(&w2, &w1, &w0, x[ 6], y[ 0]);
+ z[ 6] = w0; w0 = 0;
+
+ word3_muladd(&w0, &w2, &w1, x[ 0], y[ 7]);
+ word3_muladd(&w0, &w2, &w1, x[ 1], y[ 6]);
+ word3_muladd(&w0, &w2, &w1, x[ 2], y[ 5]);
+ word3_muladd(&w0, &w2, &w1, x[ 3], y[ 4]);
+ word3_muladd(&w0, &w2, &w1, x[ 4], y[ 3]);
+ word3_muladd(&w0, &w2, &w1, x[ 5], y[ 2]);
+ word3_muladd(&w0, &w2, &w1, x[ 6], y[ 1]);
+ word3_muladd(&w0, &w2, &w1, x[ 7], y[ 0]);
+ z[ 7] = w1; w1 = 0;
+
+ word3_muladd(&w1, &w0, &w2, x[ 1], y[ 7]);
+ word3_muladd(&w1, &w0, &w2, x[ 2], y[ 6]);
+ word3_muladd(&w1, &w0, &w2, x[ 3], y[ 5]);
+ word3_muladd(&w1, &w0, &w2, x[ 4], y[ 4]);
+ word3_muladd(&w1, &w0, &w2, x[ 5], y[ 3]);
+ word3_muladd(&w1, &w0, &w2, x[ 6], y[ 2]);
+ word3_muladd(&w1, &w0, &w2, x[ 7], y[ 1]);
+ z[ 8] = w2; w2 = 0;
+
+ word3_muladd(&w2, &w1, &w0, x[ 2], y[ 7]);
+ word3_muladd(&w2, &w1, &w0, x[ 3], y[ 6]);
+ word3_muladd(&w2, &w1, &w0, x[ 4], y[ 5]);
+ word3_muladd(&w2, &w1, &w0, x[ 5], y[ 4]);
+ word3_muladd(&w2, &w1, &w0, x[ 6], y[ 3]);
+ word3_muladd(&w2, &w1, &w0, x[ 7], y[ 2]);
+ z[ 9] = w0; w0 = 0;
+
+ word3_muladd(&w0, &w2, &w1, x[ 3], y[ 7]);
+ word3_muladd(&w0, &w2, &w1, x[ 4], y[ 6]);
+ word3_muladd(&w0, &w2, &w1, x[ 5], y[ 5]);
+ word3_muladd(&w0, &w2, &w1, x[ 6], y[ 4]);
+ word3_muladd(&w0, &w2, &w1, x[ 7], y[ 3]);
+ z[10] = w1; w1 = 0;
+
+ word3_muladd(&w1, &w0, &w2, x[ 4], y[ 7]);
+ word3_muladd(&w1, &w0, &w2, x[ 5], y[ 6]);
+ word3_muladd(&w1, &w0, &w2, x[ 6], y[ 5]);
+ word3_muladd(&w1, &w0, &w2, x[ 7], y[ 4]);
+ z[11] = w2; w2 = 0;
+
+ word3_muladd(&w2, &w1, &w0, x[ 5], y[ 7]);
+ word3_muladd(&w2, &w1, &w0, x[ 6], y[ 6]);
+ word3_muladd(&w2, &w1, &w0, x[ 7], y[ 5]);
+ z[12] = w0; w0 = 0;
+
+ word3_muladd(&w0, &w2, &w1, x[ 6], y[ 7]);
+ word3_muladd(&w0, &w2, &w1, x[ 7], y[ 6]);
+ z[13] = w1; w1 = 0;
+
+ word3_muladd(&w1, &w0, &w2, x[ 7], y[ 7]);
+ z[14] = w2;
+ z[15] = w0;
+ }
+
+/*
+* Comba 9x9 Squaring
+*/
+void bigint_comba_sqr9(word z[18], const word x[9])
+ {
+ word w2 = 0, w1 = 0, w0 = 0;
+
+ word3_muladd (&w2, &w1, &w0, x[ 0], x[ 0]);
+ z[ 0] = w0; w0 = 0;
+
+ word3_muladd_2(&w0, &w2, &w1, x[ 0], x[ 1]);
+ z[ 1] = w1; w1 = 0;
+
+ word3_muladd_2(&w1, &w0, &w2, x[ 0], x[ 2]);
+ word3_muladd (&w1, &w0, &w2, x[ 1], x[ 1]);
+ z[ 2] = w2; w2 = 0;
+
+ word3_muladd_2(&w2, &w1, &w0, x[ 0], x[ 3]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 1], x[ 2]);
+ z[ 3] = w0; w0 = 0;
+
+ word3_muladd_2(&w0, &w2, &w1, x[ 0], x[ 4]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 1], x[ 3]);
+ word3_muladd (&w0, &w2, &w1, x[ 2], x[ 2]);
+ z[ 4] = w1; w1 = 0;
+
+ word3_muladd_2(&w1, &w0, &w2, x[ 0], x[ 5]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 1], x[ 4]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 2], x[ 3]);
+ z[ 5] = w2; w2 = 0;
+
+ word3_muladd_2(&w2, &w1, &w0, x[ 0], x[ 6]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 1], x[ 5]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 2], x[ 4]);
+ word3_muladd (&w2, &w1, &w0, x[ 3], x[ 3]);
+ z[ 6] = w0; w0 = 0;
+
+ word3_muladd_2(&w0, &w2, &w1, x[ 0], x[ 7]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 1], x[ 6]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 2], x[ 5]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 3], x[ 4]);
+ z[ 7] = w1; w1 = 0;
+
+ word3_muladd_2(&w1, &w0, &w2, x[ 0], x[ 8]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 1], x[ 7]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 2], x[ 6]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 3], x[ 5]);
+ word3_muladd (&w1, &w0, &w2, x[ 4], x[ 4]);
+ z[ 8] = w2; w2 = 0;
+
+ word3_muladd_2(&w2, &w1, &w0, x[ 1], x[ 8]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 2], x[ 7]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 3], x[ 6]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 4], x[ 5]);
+ z[ 9] = w0; w0 = 0;
+
+ word3_muladd_2(&w0, &w2, &w1, x[ 2], x[ 8]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 3], x[ 7]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 4], x[ 6]);
+ word3_muladd (&w0, &w2, &w1, x[ 5], x[ 5]);
+ z[10] = w1; w1 = 0;
+
+ word3_muladd_2(&w1, &w0, &w2, x[ 3], x[ 8]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 4], x[ 7]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 5], x[ 6]);
+ z[11] = w2; w2 = 0;
+
+ word3_muladd_2(&w2, &w1, &w0, x[ 4], x[ 8]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 5], x[ 7]);
+ word3_muladd (&w2, &w1, &w0, x[ 6], x[ 6]);
+ z[12] = w0; w0 = 0;
+
+ word3_muladd_2(&w0, &w2, &w1, x[ 5], x[ 8]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 6], x[ 7]);
+ z[13] = w1; w1 = 0;
+
+ word3_muladd_2(&w1, &w0, &w2, x[ 6], x[ 8]);
+ word3_muladd (&w1, &w0, &w2, x[ 7], x[ 7]);
+ z[14] = w2; w2 = 0;
+
+ word3_muladd_2(&w2, &w1, &w0, x[ 7], x[ 8]);
+ z[15] = w0; w0 = 0;
+
+ word3_muladd (&w0, &w2, &w1, x[ 8], x[ 8]);
+ z[16] = w1;
+ z[17] = w2;
+ }
+
+/*
+* Comba 9x9 Multiplication
+*/
+void bigint_comba_mul9(word z[18], const word x[9], const word y[9])
+ {
+ word w2 = 0, w1 = 0, w0 = 0;
+
+ word3_muladd(&w2, &w1, &w0, x[ 0], y[ 0]);
+ z[ 0] = w0; w0 = 0;
+
+ word3_muladd(&w0, &w2, &w1, x[ 0], y[ 1]);
+ word3_muladd(&w0, &w2, &w1, x[ 1], y[ 0]);
+ z[ 1] = w1; w1 = 0;
+
+ word3_muladd(&w1, &w0, &w2, x[ 0], y[ 2]);
+ word3_muladd(&w1, &w0, &w2, x[ 1], y[ 1]);
+ word3_muladd(&w1, &w0, &w2, x[ 2], y[ 0]);
+ z[ 2] = w2; w2 = 0;
+
+ word3_muladd(&w2, &w1, &w0, x[ 0], y[ 3]);
+ word3_muladd(&w2, &w1, &w0, x[ 1], y[ 2]);
+ word3_muladd(&w2, &w1, &w0, x[ 2], y[ 1]);
+ word3_muladd(&w2, &w1, &w0, x[ 3], y[ 0]);
+ z[ 3] = w0; w0 = 0;
+
+ word3_muladd(&w0, &w2, &w1, x[ 0], y[ 4]);
+ word3_muladd(&w0, &w2, &w1, x[ 1], y[ 3]);
+ word3_muladd(&w0, &w2, &w1, x[ 2], y[ 2]);
+ word3_muladd(&w0, &w2, &w1, x[ 3], y[ 1]);
+ word3_muladd(&w0, &w2, &w1, x[ 4], y[ 0]);
+ z[ 4] = w1; w1 = 0;
+
+ word3_muladd(&w1, &w0, &w2, x[ 0], y[ 5]);
+ word3_muladd(&w1, &w0, &w2, x[ 1], y[ 4]);
+ word3_muladd(&w1, &w0, &w2, x[ 2], y[ 3]);
+ word3_muladd(&w1, &w0, &w2, x[ 3], y[ 2]);
+ word3_muladd(&w1, &w0, &w2, x[ 4], y[ 1]);
+ word3_muladd(&w1, &w0, &w2, x[ 5], y[ 0]);
+ z[ 5] = w2; w2 = 0;
+
+ word3_muladd(&w2, &w1, &w0, x[ 0], y[ 6]);
+ word3_muladd(&w2, &w1, &w0, x[ 1], y[ 5]);
+ word3_muladd(&w2, &w1, &w0, x[ 2], y[ 4]);
+ word3_muladd(&w2, &w1, &w0, x[ 3], y[ 3]);
+ word3_muladd(&w2, &w1, &w0, x[ 4], y[ 2]);
+ word3_muladd(&w2, &w1, &w0, x[ 5], y[ 1]);
+ word3_muladd(&w2, &w1, &w0, x[ 6], y[ 0]);
+ z[ 6] = w0; w0 = 0;
+
+ word3_muladd(&w0, &w2, &w1, x[ 0], y[ 7]);
+ word3_muladd(&w0, &w2, &w1, x[ 1], y[ 6]);
+ word3_muladd(&w0, &w2, &w1, x[ 2], y[ 5]);
+ word3_muladd(&w0, &w2, &w1, x[ 3], y[ 4]);
+ word3_muladd(&w0, &w2, &w1, x[ 4], y[ 3]);
+ word3_muladd(&w0, &w2, &w1, x[ 5], y[ 2]);
+ word3_muladd(&w0, &w2, &w1, x[ 6], y[ 1]);
+ word3_muladd(&w0, &w2, &w1, x[ 7], y[ 0]);
+ z[ 7] = w1; w1 = 0;
+
+ word3_muladd(&w1, &w0, &w2, x[ 0], y[ 8]);
+ word3_muladd(&w1, &w0, &w2, x[ 1], y[ 7]);
+ word3_muladd(&w1, &w0, &w2, x[ 2], y[ 6]);
+ word3_muladd(&w1, &w0, &w2, x[ 3], y[ 5]);
+ word3_muladd(&w1, &w0, &w2, x[ 4], y[ 4]);
+ word3_muladd(&w1, &w0, &w2, x[ 5], y[ 3]);
+ word3_muladd(&w1, &w0, &w2, x[ 6], y[ 2]);
+ word3_muladd(&w1, &w0, &w2, x[ 7], y[ 1]);
+ word3_muladd(&w1, &w0, &w2, x[ 8], y[ 0]);
+ z[ 8] = w2; w2 = 0;
+
+ word3_muladd(&w2, &w1, &w0, x[ 1], y[ 8]);
+ word3_muladd(&w2, &w1, &w0, x[ 2], y[ 7]);
+ word3_muladd(&w2, &w1, &w0, x[ 3], y[ 6]);
+ word3_muladd(&w2, &w1, &w0, x[ 4], y[ 5]);
+ word3_muladd(&w2, &w1, &w0, x[ 5], y[ 4]);
+ word3_muladd(&w2, &w1, &w0, x[ 6], y[ 3]);
+ word3_muladd(&w2, &w1, &w0, x[ 7], y[ 2]);
+ word3_muladd(&w2, &w1, &w0, x[ 8], y[ 1]);
+ z[ 9] = w0; w0 = 0;
+
+ word3_muladd(&w0, &w2, &w1, x[ 2], y[ 8]);
+ word3_muladd(&w0, &w2, &w1, x[ 3], y[ 7]);
+ word3_muladd(&w0, &w2, &w1, x[ 4], y[ 6]);
+ word3_muladd(&w0, &w2, &w1, x[ 5], y[ 5]);
+ word3_muladd(&w0, &w2, &w1, x[ 6], y[ 4]);
+ word3_muladd(&w0, &w2, &w1, x[ 7], y[ 3]);
+ word3_muladd(&w0, &w2, &w1, x[ 8], y[ 2]);
+ z[10] = w1; w1 = 0;
+
+ word3_muladd(&w1, &w0, &w2, x[ 3], y[ 8]);
+ word3_muladd(&w1, &w0, &w2, x[ 4], y[ 7]);
+ word3_muladd(&w1, &w0, &w2, x[ 5], y[ 6]);
+ word3_muladd(&w1, &w0, &w2, x[ 6], y[ 5]);
+ word3_muladd(&w1, &w0, &w2, x[ 7], y[ 4]);
+ word3_muladd(&w1, &w0, &w2, x[ 8], y[ 3]);
+ z[11] = w2; w2 = 0;
+
+ word3_muladd(&w2, &w1, &w0, x[ 4], y[ 8]);
+ word3_muladd(&w2, &w1, &w0, x[ 5], y[ 7]);
+ word3_muladd(&w2, &w1, &w0, x[ 6], y[ 6]);
+ word3_muladd(&w2, &w1, &w0, x[ 7], y[ 5]);
+ word3_muladd(&w2, &w1, &w0, x[ 8], y[ 4]);
+ z[12] = w0; w0 = 0;
+
+ word3_muladd(&w0, &w2, &w1, x[ 5], y[ 8]);
+ word3_muladd(&w0, &w2, &w1, x[ 6], y[ 7]);
+ word3_muladd(&w0, &w2, &w1, x[ 7], y[ 6]);
+ word3_muladd(&w0, &w2, &w1, x[ 8], y[ 5]);
+ z[13] = w1; w1 = 0;
+
+ word3_muladd(&w1, &w0, &w2, x[ 6], y[ 8]);
+ word3_muladd(&w1, &w0, &w2, x[ 7], y[ 7]);
+ word3_muladd(&w1, &w0, &w2, x[ 8], y[ 6]);
+ z[14] = w2; w2 = 0;
+
+ word3_muladd(&w2, &w1, &w0, x[ 7], y[ 8]);
+ word3_muladd(&w2, &w1, &w0, x[ 8], y[ 7]);
+ z[15] = w0; w0 = 0;
+
+ word3_muladd(&w0, &w2, &w1, x[ 8], y[ 8]);
+ z[16] = w1;
+ z[17] = w2;
+ }
+
+/*
+* Comba 16x16 Squaring
+*/
+void bigint_comba_sqr16(word z[32], const word x[16])
+ {
+ word w2 = 0, w1 = 0, w0 = 0;
+
+ word3_muladd (&w2, &w1, &w0, x[ 0], x[ 0]);
+ z[ 0] = w0; w0 = 0;
+
+ word3_muladd_2(&w0, &w2, &w1, x[ 0], x[ 1]);
+ z[ 1] = w1; w1 = 0;
+
+ word3_muladd_2(&w1, &w0, &w2, x[ 0], x[ 2]);
+ word3_muladd (&w1, &w0, &w2, x[ 1], x[ 1]);
+ z[ 2] = w2; w2 = 0;
+
+ word3_muladd_2(&w2, &w1, &w0, x[ 0], x[ 3]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 1], x[ 2]);
+ z[ 3] = w0; w0 = 0;
+
+ word3_muladd_2(&w0, &w2, &w1, x[ 0], x[ 4]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 1], x[ 3]);
+ word3_muladd (&w0, &w2, &w1, x[ 2], x[ 2]);
+ z[ 4] = w1; w1 = 0;
+
+ word3_muladd_2(&w1, &w0, &w2, x[ 0], x[ 5]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 1], x[ 4]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 2], x[ 3]);
+ z[ 5] = w2; w2 = 0;
+
+ word3_muladd_2(&w2, &w1, &w0, x[ 0], x[ 6]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 1], x[ 5]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 2], x[ 4]);
+ word3_muladd (&w2, &w1, &w0, x[ 3], x[ 3]);
+ z[ 6] = w0; w0 = 0;
+
+ word3_muladd_2(&w0, &w2, &w1, x[ 0], x[ 7]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 1], x[ 6]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 2], x[ 5]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 3], x[ 4]);
+ z[ 7] = w1; w1 = 0;
+
+ word3_muladd_2(&w1, &w0, &w2, x[ 0], x[ 8]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 1], x[ 7]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 2], x[ 6]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 3], x[ 5]);
+ word3_muladd (&w1, &w0, &w2, x[ 4], x[ 4]);
+ z[ 8] = w2; w2 = 0;
+
+ word3_muladd_2(&w2, &w1, &w0, x[ 0], x[ 9]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 1], x[ 8]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 2], x[ 7]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 3], x[ 6]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 4], x[ 5]);
+ z[ 9] = w0; w0 = 0;
+
+ word3_muladd_2(&w0, &w2, &w1, x[ 0], x[10]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 1], x[ 9]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 2], x[ 8]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 3], x[ 7]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 4], x[ 6]);
+ word3_muladd (&w0, &w2, &w1, x[ 5], x[ 5]);
+ z[10] = w1; w1 = 0;
+
+ word3_muladd_2(&w1, &w0, &w2, x[ 0], x[11]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 1], x[10]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 2], x[ 9]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 3], x[ 8]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 4], x[ 7]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 5], x[ 6]);
+ z[11] = w2; w2 = 0;
+
+ word3_muladd_2(&w2, &w1, &w0, x[ 0], x[12]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 1], x[11]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 2], x[10]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 3], x[ 9]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 4], x[ 8]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 5], x[ 7]);
+ word3_muladd (&w2, &w1, &w0, x[ 6], x[ 6]);
+ z[12] = w0; w0 = 0;
+
+ word3_muladd_2(&w0, &w2, &w1, x[ 0], x[13]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 1], x[12]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 2], x[11]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 3], x[10]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 4], x[ 9]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 5], x[ 8]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 6], x[ 7]);
+ z[13] = w1; w1 = 0;
+
+ word3_muladd_2(&w1, &w0, &w2, x[ 0], x[14]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 1], x[13]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 2], x[12]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 3], x[11]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 4], x[10]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 5], x[ 9]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 6], x[ 8]);
+ word3_muladd (&w1, &w0, &w2, x[ 7], x[ 7]);
+ z[14] = w2; w2 = 0;
+
+ word3_muladd_2(&w2, &w1, &w0, x[ 0], x[15]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 1], x[14]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 2], x[13]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 3], x[12]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 4], x[11]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 5], x[10]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 6], x[ 9]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 7], x[ 8]);
+ z[15] = w0; w0 = 0;
+
+ word3_muladd_2(&w0, &w2, &w1, x[ 1], x[15]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 2], x[14]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 3], x[13]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 4], x[12]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 5], x[11]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 6], x[10]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 7], x[ 9]);
+ word3_muladd (&w0, &w2, &w1, x[ 8], x[ 8]);
+ z[16] = w1; w1 = 0;
+
+ word3_muladd_2(&w1, &w0, &w2, x[ 2], x[15]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 3], x[14]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 4], x[13]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 5], x[12]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 6], x[11]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 7], x[10]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 8], x[ 9]);
+ z[17] = w2; w2 = 0;
+
+ word3_muladd_2(&w2, &w1, &w0, x[ 3], x[15]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 4], x[14]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 5], x[13]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 6], x[12]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 7], x[11]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 8], x[10]);
+ word3_muladd (&w2, &w1, &w0, x[ 9], x[ 9]);
+ z[18] = w0; w0 = 0;
+
+ word3_muladd_2(&w0, &w2, &w1, x[ 4], x[15]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 5], x[14]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 6], x[13]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 7], x[12]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 8], x[11]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 9], x[10]);
+ z[19] = w1; w1 = 0;
+
+ word3_muladd_2(&w1, &w0, &w2, x[ 5], x[15]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 6], x[14]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 7], x[13]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 8], x[12]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 9], x[11]);
+ word3_muladd (&w1, &w0, &w2, x[10], x[10]);
+ z[20] = w2; w2 = 0;
+
+ word3_muladd_2(&w2, &w1, &w0, x[ 6], x[15]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 7], x[14]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 8], x[13]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 9], x[12]);
+ word3_muladd_2(&w2, &w1, &w0, x[10], x[11]);
+ z[21] = w0; w0 = 0;
+
+ word3_muladd_2(&w0, &w2, &w1, x[ 7], x[15]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 8], x[14]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 9], x[13]);
+ word3_muladd_2(&w0, &w2, &w1, x[10], x[12]);
+ word3_muladd (&w0, &w2, &w1, x[11], x[11]);
+ z[22] = w1; w1 = 0;
+
+ word3_muladd_2(&w1, &w0, &w2, x[ 8], x[15]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 9], x[14]);
+ word3_muladd_2(&w1, &w0, &w2, x[10], x[13]);
+ word3_muladd_2(&w1, &w0, &w2, x[11], x[12]);
+ z[23] = w2; w2 = 0;
+
+ word3_muladd_2(&w2, &w1, &w0, x[ 9], x[15]);
+ word3_muladd_2(&w2, &w1, &w0, x[10], x[14]);
+ word3_muladd_2(&w2, &w1, &w0, x[11], x[13]);
+ word3_muladd (&w2, &w1, &w0, x[12], x[12]);
+ z[24] = w0; w0 = 0;
+
+ word3_muladd_2(&w0, &w2, &w1, x[10], x[15]);
+ word3_muladd_2(&w0, &w2, &w1, x[11], x[14]);
+ word3_muladd_2(&w0, &w2, &w1, x[12], x[13]);
+ z[25] = w1; w1 = 0;
+
+ word3_muladd_2(&w1, &w0, &w2, x[11], x[15]);
+ word3_muladd_2(&w1, &w0, &w2, x[12], x[14]);
+ word3_muladd (&w1, &w0, &w2, x[13], x[13]);
+ z[26] = w2; w2 = 0;
+
+ word3_muladd_2(&w2, &w1, &w0, x[12], x[15]);
+ word3_muladd_2(&w2, &w1, &w0, x[13], x[14]);
+ z[27] = w0; w0 = 0;
+
+ word3_muladd_2(&w0, &w2, &w1, x[13], x[15]);
+ word3_muladd (&w0, &w2, &w1, x[14], x[14]);
+ z[28] = w1; w1 = 0;
+
+ word3_muladd_2(&w1, &w0, &w2, x[14], x[15]);
+ z[29] = w2; w2 = 0;
+
+ word3_muladd (&w2, &w1, &w0, x[15], x[15]);
+ z[30] = w0;
+ z[31] = w1;
+ }
+
+/*
+* Comba 16x16 Multiplication
+*/
+void bigint_comba_mul16(word z[32], const word x[16], const word y[16])
+ {
+ word w2 = 0, w1 = 0, w0 = 0;
+
+ word3_muladd(&w2, &w1, &w0, x[ 0], y[ 0]);
+ z[ 0] = w0; w0 = 0;
+
+ word3_muladd(&w0, &w2, &w1, x[ 0], y[ 1]);
+ word3_muladd(&w0, &w2, &w1, x[ 1], y[ 0]);
+ z[ 1] = w1; w1 = 0;
+
+ word3_muladd(&w1, &w0, &w2, x[ 0], y[ 2]);
+ word3_muladd(&w1, &w0, &w2, x[ 1], y[ 1]);
+ word3_muladd(&w1, &w0, &w2, x[ 2], y[ 0]);
+ z[ 2] = w2; w2 = 0;
+
+ word3_muladd(&w2, &w1, &w0, x[ 0], y[ 3]);
+ word3_muladd(&w2, &w1, &w0, x[ 1], y[ 2]);
+ word3_muladd(&w2, &w1, &w0, x[ 2], y[ 1]);
+ word3_muladd(&w2, &w1, &w0, x[ 3], y[ 0]);
+ z[ 3] = w0; w0 = 0;
+
+ word3_muladd(&w0, &w2, &w1, x[ 0], y[ 4]);
+ word3_muladd(&w0, &w2, &w1, x[ 1], y[ 3]);
+ word3_muladd(&w0, &w2, &w1, x[ 2], y[ 2]);
+ word3_muladd(&w0, &w2, &w1, x[ 3], y[ 1]);
+ word3_muladd(&w0, &w2, &w1, x[ 4], y[ 0]);
+ z[ 4] = w1; w1 = 0;
+
+ word3_muladd(&w1, &w0, &w2, x[ 0], y[ 5]);
+ word3_muladd(&w1, &w0, &w2, x[ 1], y[ 4]);
+ word3_muladd(&w1, &w0, &w2, x[ 2], y[ 3]);
+ word3_muladd(&w1, &w0, &w2, x[ 3], y[ 2]);
+ word3_muladd(&w1, &w0, &w2, x[ 4], y[ 1]);
+ word3_muladd(&w1, &w0, &w2, x[ 5], y[ 0]);
+ z[ 5] = w2; w2 = 0;
+
+ word3_muladd(&w2, &w1, &w0, x[ 0], y[ 6]);
+ word3_muladd(&w2, &w1, &w0, x[ 1], y[ 5]);
+ word3_muladd(&w2, &w1, &w0, x[ 2], y[ 4]);
+ word3_muladd(&w2, &w1, &w0, x[ 3], y[ 3]);
+ word3_muladd(&w2, &w1, &w0, x[ 4], y[ 2]);
+ word3_muladd(&w2, &w1, &w0, x[ 5], y[ 1]);
+ word3_muladd(&w2, &w1, &w0, x[ 6], y[ 0]);
+ z[ 6] = w0; w0 = 0;
+
+ word3_muladd(&w0, &w2, &w1, x[ 0], y[ 7]);
+ word3_muladd(&w0, &w2, &w1, x[ 1], y[ 6]);
+ word3_muladd(&w0, &w2, &w1, x[ 2], y[ 5]);
+ word3_muladd(&w0, &w2, &w1, x[ 3], y[ 4]);
+ word3_muladd(&w0, &w2, &w1, x[ 4], y[ 3]);
+ word3_muladd(&w0, &w2, &w1, x[ 5], y[ 2]);
+ word3_muladd(&w0, &w2, &w1, x[ 6], y[ 1]);
+ word3_muladd(&w0, &w2, &w1, x[ 7], y[ 0]);
+ z[ 7] = w1; w1 = 0;
+
+ word3_muladd(&w1, &w0, &w2, x[ 0], y[ 8]);
+ word3_muladd(&w1, &w0, &w2, x[ 1], y[ 7]);
+ word3_muladd(&w1, &w0, &w2, x[ 2], y[ 6]);
+ word3_muladd(&w1, &w0, &w2, x[ 3], y[ 5]);
+ word3_muladd(&w1, &w0, &w2, x[ 4], y[ 4]);
+ word3_muladd(&w1, &w0, &w2, x[ 5], y[ 3]);
+ word3_muladd(&w1, &w0, &w2, x[ 6], y[ 2]);
+ word3_muladd(&w1, &w0, &w2, x[ 7], y[ 1]);
+ word3_muladd(&w1, &w0, &w2, x[ 8], y[ 0]);
+ z[ 8] = w2; w2 = 0;
+
+ word3_muladd(&w2, &w1, &w0, x[ 0], y[ 9]);
+ word3_muladd(&w2, &w1, &w0, x[ 1], y[ 8]);
+ word3_muladd(&w2, &w1, &w0, x[ 2], y[ 7]);
+ word3_muladd(&w2, &w1, &w0, x[ 3], y[ 6]);
+ word3_muladd(&w2, &w1, &w0, x[ 4], y[ 5]);
+ word3_muladd(&w2, &w1, &w0, x[ 5], y[ 4]);
+ word3_muladd(&w2, &w1, &w0, x[ 6], y[ 3]);
+ word3_muladd(&w2, &w1, &w0, x[ 7], y[ 2]);
+ word3_muladd(&w2, &w1, &w0, x[ 8], y[ 1]);
+ word3_muladd(&w2, &w1, &w0, x[ 9], y[ 0]);
+ z[ 9] = w0; w0 = 0;
+
+ word3_muladd(&w0, &w2, &w1, x[ 0], y[10]);
+ word3_muladd(&w0, &w2, &w1, x[ 1], y[ 9]);
+ word3_muladd(&w0, &w2, &w1, x[ 2], y[ 8]);
+ word3_muladd(&w0, &w2, &w1, x[ 3], y[ 7]);
+ word3_muladd(&w0, &w2, &w1, x[ 4], y[ 6]);
+ word3_muladd(&w0, &w2, &w1, x[ 5], y[ 5]);
+ word3_muladd(&w0, &w2, &w1, x[ 6], y[ 4]);
+ word3_muladd(&w0, &w2, &w1, x[ 7], y[ 3]);
+ word3_muladd(&w0, &w2, &w1, x[ 8], y[ 2]);
+ word3_muladd(&w0, &w2, &w1, x[ 9], y[ 1]);
+ word3_muladd(&w0, &w2, &w1, x[10], y[ 0]);
+ z[10] = w1; w1 = 0;
+
+ word3_muladd(&w1, &w0, &w2, x[ 0], y[11]);
+ word3_muladd(&w1, &w0, &w2, x[ 1], y[10]);
+ word3_muladd(&w1, &w0, &w2, x[ 2], y[ 9]);
+ word3_muladd(&w1, &w0, &w2, x[ 3], y[ 8]);
+ word3_muladd(&w1, &w0, &w2, x[ 4], y[ 7]);
+ word3_muladd(&w1, &w0, &w2, x[ 5], y[ 6]);
+ word3_muladd(&w1, &w0, &w2, x[ 6], y[ 5]);
+ word3_muladd(&w1, &w0, &w2, x[ 7], y[ 4]);
+ word3_muladd(&w1, &w0, &w2, x[ 8], y[ 3]);
+ word3_muladd(&w1, &w0, &w2, x[ 9], y[ 2]);
+ word3_muladd(&w1, &w0, &w2, x[10], y[ 1]);
+ word3_muladd(&w1, &w0, &w2, x[11], y[ 0]);
+ z[11] = w2; w2 = 0;
+
+ word3_muladd(&w2, &w1, &w0, x[ 0], y[12]);
+ word3_muladd(&w2, &w1, &w0, x[ 1], y[11]);
+ word3_muladd(&w2, &w1, &w0, x[ 2], y[10]);
+ word3_muladd(&w2, &w1, &w0, x[ 3], y[ 9]);
+ word3_muladd(&w2, &w1, &w0, x[ 4], y[ 8]);
+ word3_muladd(&w2, &w1, &w0, x[ 5], y[ 7]);
+ word3_muladd(&w2, &w1, &w0, x[ 6], y[ 6]);
+ word3_muladd(&w2, &w1, &w0, x[ 7], y[ 5]);
+ word3_muladd(&w2, &w1, &w0, x[ 8], y[ 4]);
+ word3_muladd(&w2, &w1, &w0, x[ 9], y[ 3]);
+ word3_muladd(&w2, &w1, &w0, x[10], y[ 2]);
+ word3_muladd(&w2, &w1, &w0, x[11], y[ 1]);
+ word3_muladd(&w2, &w1, &w0, x[12], y[ 0]);
+ z[12] = w0; w0 = 0;
+
+ word3_muladd(&w0, &w2, &w1, x[ 0], y[13]);
+ word3_muladd(&w0, &w2, &w1, x[ 1], y[12]);
+ word3_muladd(&w0, &w2, &w1, x[ 2], y[11]);
+ word3_muladd(&w0, &w2, &w1, x[ 3], y[10]);
+ word3_muladd(&w0, &w2, &w1, x[ 4], y[ 9]);
+ word3_muladd(&w0, &w2, &w1, x[ 5], y[ 8]);
+ word3_muladd(&w0, &w2, &w1, x[ 6], y[ 7]);
+ word3_muladd(&w0, &w2, &w1, x[ 7], y[ 6]);
+ word3_muladd(&w0, &w2, &w1, x[ 8], y[ 5]);
+ word3_muladd(&w0, &w2, &w1, x[ 9], y[ 4]);
+ word3_muladd(&w0, &w2, &w1, x[10], y[ 3]);
+ word3_muladd(&w0, &w2, &w1, x[11], y[ 2]);
+ word3_muladd(&w0, &w2, &w1, x[12], y[ 1]);
+ word3_muladd(&w0, &w2, &w1, x[13], y[ 0]);
+ z[13] = w1; w1 = 0;
+
+ word3_muladd(&w1, &w0, &w2, x[ 0], y[14]);
+ word3_muladd(&w1, &w0, &w2, x[ 1], y[13]);
+ word3_muladd(&w1, &w0, &w2, x[ 2], y[12]);
+ word3_muladd(&w1, &w0, &w2, x[ 3], y[11]);
+ word3_muladd(&w1, &w0, &w2, x[ 4], y[10]);
+ word3_muladd(&w1, &w0, &w2, x[ 5], y[ 9]);
+ word3_muladd(&w1, &w0, &w2, x[ 6], y[ 8]);
+ word3_muladd(&w1, &w0, &w2, x[ 7], y[ 7]);
+ word3_muladd(&w1, &w0, &w2, x[ 8], y[ 6]);
+ word3_muladd(&w1, &w0, &w2, x[ 9], y[ 5]);
+ word3_muladd(&w1, &w0, &w2, x[10], y[ 4]);
+ word3_muladd(&w1, &w0, &w2, x[11], y[ 3]);
+ word3_muladd(&w1, &w0, &w2, x[12], y[ 2]);
+ word3_muladd(&w1, &w0, &w2, x[13], y[ 1]);
+ word3_muladd(&w1, &w0, &w2, x[14], y[ 0]);
+ z[14] = w2; w2 = 0;
+
+ word3_muladd(&w2, &w1, &w0, x[ 0], y[15]);
+ word3_muladd(&w2, &w1, &w0, x[ 1], y[14]);
+ word3_muladd(&w2, &w1, &w0, x[ 2], y[13]);
+ word3_muladd(&w2, &w1, &w0, x[ 3], y[12]);
+ word3_muladd(&w2, &w1, &w0, x[ 4], y[11]);
+ word3_muladd(&w2, &w1, &w0, x[ 5], y[10]);
+ word3_muladd(&w2, &w1, &w0, x[ 6], y[ 9]);
+ word3_muladd(&w2, &w1, &w0, x[ 7], y[ 8]);
+ word3_muladd(&w2, &w1, &w0, x[ 8], y[ 7]);
+ word3_muladd(&w2, &w1, &w0, x[ 9], y[ 6]);
+ word3_muladd(&w2, &w1, &w0, x[10], y[ 5]);
+ word3_muladd(&w2, &w1, &w0, x[11], y[ 4]);
+ word3_muladd(&w2, &w1, &w0, x[12], y[ 3]);
+ word3_muladd(&w2, &w1, &w0, x[13], y[ 2]);
+ word3_muladd(&w2, &w1, &w0, x[14], y[ 1]);
+ word3_muladd(&w2, &w1, &w0, x[15], y[ 0]);
+ z[15] = w0; w0 = 0;
+
+ word3_muladd(&w0, &w2, &w1, x[ 1], y[15]);
+ word3_muladd(&w0, &w2, &w1, x[ 2], y[14]);
+ word3_muladd(&w0, &w2, &w1, x[ 3], y[13]);
+ word3_muladd(&w0, &w2, &w1, x[ 4], y[12]);
+ word3_muladd(&w0, &w2, &w1, x[ 5], y[11]);
+ word3_muladd(&w0, &w2, &w1, x[ 6], y[10]);
+ word3_muladd(&w0, &w2, &w1, x[ 7], y[ 9]);
+ word3_muladd(&w0, &w2, &w1, x[ 8], y[ 8]);
+ word3_muladd(&w0, &w2, &w1, x[ 9], y[ 7]);
+ word3_muladd(&w0, &w2, &w1, x[10], y[ 6]);
+ word3_muladd(&w0, &w2, &w1, x[11], y[ 5]);
+ word3_muladd(&w0, &w2, &w1, x[12], y[ 4]);
+ word3_muladd(&w0, &w2, &w1, x[13], y[ 3]);
+ word3_muladd(&w0, &w2, &w1, x[14], y[ 2]);
+ word3_muladd(&w0, &w2, &w1, x[15], y[ 1]);
+ z[16] = w1; w1 = 0;
+
+ word3_muladd(&w1, &w0, &w2, x[ 2], y[15]);
+ word3_muladd(&w1, &w0, &w2, x[ 3], y[14]);
+ word3_muladd(&w1, &w0, &w2, x[ 4], y[13]);
+ word3_muladd(&w1, &w0, &w2, x[ 5], y[12]);
+ word3_muladd(&w1, &w0, &w2, x[ 6], y[11]);
+ word3_muladd(&w1, &w0, &w2, x[ 7], y[10]);
+ word3_muladd(&w1, &w0, &w2, x[ 8], y[ 9]);
+ word3_muladd(&w1, &w0, &w2, x[ 9], y[ 8]);
+ word3_muladd(&w1, &w0, &w2, x[10], y[ 7]);
+ word3_muladd(&w1, &w0, &w2, x[11], y[ 6]);
+ word3_muladd(&w1, &w0, &w2, x[12], y[ 5]);
+ word3_muladd(&w1, &w0, &w2, x[13], y[ 4]);
+ word3_muladd(&w1, &w0, &w2, x[14], y[ 3]);
+ word3_muladd(&w1, &w0, &w2, x[15], y[ 2]);
+ z[17] = w2; w2 = 0;
+
+ word3_muladd(&w2, &w1, &w0, x[ 3], y[15]);
+ word3_muladd(&w2, &w1, &w0, x[ 4], y[14]);
+ word3_muladd(&w2, &w1, &w0, x[ 5], y[13]);
+ word3_muladd(&w2, &w1, &w0, x[ 6], y[12]);
+ word3_muladd(&w2, &w1, &w0, x[ 7], y[11]);
+ word3_muladd(&w2, &w1, &w0, x[ 8], y[10]);
+ word3_muladd(&w2, &w1, &w0, x[ 9], y[ 9]);
+ word3_muladd(&w2, &w1, &w0, x[10], y[ 8]);
+ word3_muladd(&w2, &w1, &w0, x[11], y[ 7]);
+ word3_muladd(&w2, &w1, &w0, x[12], y[ 6]);
+ word3_muladd(&w2, &w1, &w0, x[13], y[ 5]);
+ word3_muladd(&w2, &w1, &w0, x[14], y[ 4]);
+ word3_muladd(&w2, &w1, &w0, x[15], y[ 3]);
+ z[18] = w0; w0 = 0;
+
+ word3_muladd(&w0, &w2, &w1, x[ 4], y[15]);
+ word3_muladd(&w0, &w2, &w1, x[ 5], y[14]);
+ word3_muladd(&w0, &w2, &w1, x[ 6], y[13]);
+ word3_muladd(&w0, &w2, &w1, x[ 7], y[12]);
+ word3_muladd(&w0, &w2, &w1, x[ 8], y[11]);
+ word3_muladd(&w0, &w2, &w1, x[ 9], y[10]);
+ word3_muladd(&w0, &w2, &w1, x[10], y[ 9]);
+ word3_muladd(&w0, &w2, &w1, x[11], y[ 8]);
+ word3_muladd(&w0, &w2, &w1, x[12], y[ 7]);
+ word3_muladd(&w0, &w2, &w1, x[13], y[ 6]);
+ word3_muladd(&w0, &w2, &w1, x[14], y[ 5]);
+ word3_muladd(&w0, &w2, &w1, x[15], y[ 4]);
+ z[19] = w1; w1 = 0;
+
+ word3_muladd(&w1, &w0, &w2, x[ 5], y[15]);
+ word3_muladd(&w1, &w0, &w2, x[ 6], y[14]);
+ word3_muladd(&w1, &w0, &w2, x[ 7], y[13]);
+ word3_muladd(&w1, &w0, &w2, x[ 8], y[12]);
+ word3_muladd(&w1, &w0, &w2, x[ 9], y[11]);
+ word3_muladd(&w1, &w0, &w2, x[10], y[10]);
+ word3_muladd(&w1, &w0, &w2, x[11], y[ 9]);
+ word3_muladd(&w1, &w0, &w2, x[12], y[ 8]);
+ word3_muladd(&w1, &w0, &w2, x[13], y[ 7]);
+ word3_muladd(&w1, &w0, &w2, x[14], y[ 6]);
+ word3_muladd(&w1, &w0, &w2, x[15], y[ 5]);
+ z[20] = w2; w2 = 0;
+
+ word3_muladd(&w2, &w1, &w0, x[ 6], y[15]);
+ word3_muladd(&w2, &w1, &w0, x[ 7], y[14]);
+ word3_muladd(&w2, &w1, &w0, x[ 8], y[13]);
+ word3_muladd(&w2, &w1, &w0, x[ 9], y[12]);
+ word3_muladd(&w2, &w1, &w0, x[10], y[11]);
+ word3_muladd(&w2, &w1, &w0, x[11], y[10]);
+ word3_muladd(&w2, &w1, &w0, x[12], y[ 9]);
+ word3_muladd(&w2, &w1, &w0, x[13], y[ 8]);
+ word3_muladd(&w2, &w1, &w0, x[14], y[ 7]);
+ word3_muladd(&w2, &w1, &w0, x[15], y[ 6]);
+ z[21] = w0; w0 = 0;
+
+ word3_muladd(&w0, &w2, &w1, x[ 7], y[15]);
+ word3_muladd(&w0, &w2, &w1, x[ 8], y[14]);
+ word3_muladd(&w0, &w2, &w1, x[ 9], y[13]);
+ word3_muladd(&w0, &w2, &w1, x[10], y[12]);
+ word3_muladd(&w0, &w2, &w1, x[11], y[11]);
+ word3_muladd(&w0, &w2, &w1, x[12], y[10]);
+ word3_muladd(&w0, &w2, &w1, x[13], y[ 9]);
+ word3_muladd(&w0, &w2, &w1, x[14], y[ 8]);
+ word3_muladd(&w0, &w2, &w1, x[15], y[ 7]);
+ z[22] = w1; w1 = 0;
+
+ word3_muladd(&w1, &w0, &w2, x[ 8], y[15]);
+ word3_muladd(&w1, &w0, &w2, x[ 9], y[14]);
+ word3_muladd(&w1, &w0, &w2, x[10], y[13]);
+ word3_muladd(&w1, &w0, &w2, x[11], y[12]);
+ word3_muladd(&w1, &w0, &w2, x[12], y[11]);
+ word3_muladd(&w1, &w0, &w2, x[13], y[10]);
+ word3_muladd(&w1, &w0, &w2, x[14], y[ 9]);
+ word3_muladd(&w1, &w0, &w2, x[15], y[ 8]);
+ z[23] = w2; w2 = 0;
+
+ word3_muladd(&w2, &w1, &w0, x[ 9], y[15]);
+ word3_muladd(&w2, &w1, &w0, x[10], y[14]);
+ word3_muladd(&w2, &w1, &w0, x[11], y[13]);
+ word3_muladd(&w2, &w1, &w0, x[12], y[12]);
+ word3_muladd(&w2, &w1, &w0, x[13], y[11]);
+ word3_muladd(&w2, &w1, &w0, x[14], y[10]);
+ word3_muladd(&w2, &w1, &w0, x[15], y[ 9]);
+ z[24] = w0; w0 = 0;
+
+ word3_muladd(&w0, &w2, &w1, x[10], y[15]);
+ word3_muladd(&w0, &w2, &w1, x[11], y[14]);
+ word3_muladd(&w0, &w2, &w1, x[12], y[13]);
+ word3_muladd(&w0, &w2, &w1, x[13], y[12]);
+ word3_muladd(&w0, &w2, &w1, x[14], y[11]);
+ word3_muladd(&w0, &w2, &w1, x[15], y[10]);
+ z[25] = w1; w1 = 0;
+
+ word3_muladd(&w1, &w0, &w2, x[11], y[15]);
+ word3_muladd(&w1, &w0, &w2, x[12], y[14]);
+ word3_muladd(&w1, &w0, &w2, x[13], y[13]);
+ word3_muladd(&w1, &w0, &w2, x[14], y[12]);
+ word3_muladd(&w1, &w0, &w2, x[15], y[11]);
+ z[26] = w2; w2 = 0;
+
+ word3_muladd(&w2, &w1, &w0, x[12], y[15]);
+ word3_muladd(&w2, &w1, &w0, x[13], y[14]);
+ word3_muladd(&w2, &w1, &w0, x[14], y[13]);
+ word3_muladd(&w2, &w1, &w0, x[15], y[12]);
+ z[27] = w0; w0 = 0;
+
+ word3_muladd(&w0, &w2, &w1, x[13], y[15]);
+ word3_muladd(&w0, &w2, &w1, x[14], y[14]);
+ word3_muladd(&w0, &w2, &w1, x[15], y[13]);
+ z[28] = w1; w1 = 0;
+
+ word3_muladd(&w1, &w0, &w2, x[14], y[15]);
+ word3_muladd(&w1, &w0, &w2, x[15], y[14]);
+ z[29] = w2; w2 = 0;
+
+ word3_muladd(&w2, &w1, &w0, x[15], y[15]);
+ z[30] = w0;
+ z[31] = w1;
+ }
+
+/*
+* Comba 24x24 Squaring
+*/
+void bigint_comba_sqr24(word z[48], const word x[24])
+ {
+ word w2 = 0, w1 = 0, w0 = 0;
+
+ word3_muladd (&w2, &w1, &w0, x[ 0], x[ 0]);
+ z[ 0] = w0; w0 = 0;
+
+ word3_muladd_2(&w0, &w2, &w1, x[ 0], x[ 1]);
+ z[ 1] = w1; w1 = 0;
+
+ word3_muladd_2(&w1, &w0, &w2, x[ 0], x[ 2]);
+ word3_muladd (&w1, &w0, &w2, x[ 1], x[ 1]);
+ z[ 2] = w2; w2 = 0;
+
+ word3_muladd_2(&w2, &w1, &w0, x[ 0], x[ 3]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 1], x[ 2]);
+ z[ 3] = w0; w0 = 0;
+
+ word3_muladd_2(&w0, &w2, &w1, x[ 0], x[ 4]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 1], x[ 3]);
+ word3_muladd (&w0, &w2, &w1, x[ 2], x[ 2]);
+ z[ 4] = w1; w1 = 0;
+
+ word3_muladd_2(&w1, &w0, &w2, x[ 0], x[ 5]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 1], x[ 4]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 2], x[ 3]);
+ z[ 5] = w2; w2 = 0;
+
+ word3_muladd_2(&w2, &w1, &w0, x[ 0], x[ 6]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 1], x[ 5]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 2], x[ 4]);
+ word3_muladd (&w2, &w1, &w0, x[ 3], x[ 3]);
+ z[ 6] = w0; w0 = 0;
+
+ word3_muladd_2(&w0, &w2, &w1, x[ 0], x[ 7]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 1], x[ 6]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 2], x[ 5]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 3], x[ 4]);
+ z[ 7] = w1; w1 = 0;
+
+ word3_muladd_2(&w1, &w0, &w2, x[ 0], x[ 8]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 1], x[ 7]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 2], x[ 6]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 3], x[ 5]);
+ word3_muladd (&w1, &w0, &w2, x[ 4], x[ 4]);
+ z[ 8] = w2; w2 = 0;
+
+ word3_muladd_2(&w2, &w1, &w0, x[ 0], x[ 9]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 1], x[ 8]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 2], x[ 7]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 3], x[ 6]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 4], x[ 5]);
+ z[ 9] = w0; w0 = 0;
+
+ word3_muladd_2(&w0, &w2, &w1, x[ 0], x[10]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 1], x[ 9]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 2], x[ 8]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 3], x[ 7]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 4], x[ 6]);
+ word3_muladd (&w0, &w2, &w1, x[ 5], x[ 5]);
+ z[10] = w1; w1 = 0;
+
+ word3_muladd_2(&w1, &w0, &w2, x[ 0], x[11]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 1], x[10]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 2], x[ 9]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 3], x[ 8]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 4], x[ 7]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 5], x[ 6]);
+ z[11] = w2; w2 = 0;
+
+ word3_muladd_2(&w2, &w1, &w0, x[ 0], x[12]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 1], x[11]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 2], x[10]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 3], x[ 9]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 4], x[ 8]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 5], x[ 7]);
+ word3_muladd (&w2, &w1, &w0, x[ 6], x[ 6]);
+ z[12] = w0; w0 = 0;
+
+ word3_muladd_2(&w0, &w2, &w1, x[ 0], x[13]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 1], x[12]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 2], x[11]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 3], x[10]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 4], x[ 9]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 5], x[ 8]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 6], x[ 7]);
+ z[13] = w1; w1 = 0;
+
+ word3_muladd_2(&w1, &w0, &w2, x[ 0], x[14]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 1], x[13]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 2], x[12]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 3], x[11]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 4], x[10]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 5], x[ 9]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 6], x[ 8]);
+ word3_muladd (&w1, &w0, &w2, x[ 7], x[ 7]);
+ z[14] = w2; w2 = 0;
+
+ word3_muladd_2(&w2, &w1, &w0, x[ 0], x[15]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 1], x[14]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 2], x[13]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 3], x[12]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 4], x[11]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 5], x[10]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 6], x[ 9]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 7], x[ 8]);
+ z[15] = w0; w0 = 0;
+
+ word3_muladd_2(&w0, &w2, &w1, x[ 0], x[16]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 1], x[15]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 2], x[14]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 3], x[13]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 4], x[12]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 5], x[11]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 6], x[10]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 7], x[ 9]);
+ word3_muladd (&w0, &w2, &w1, x[ 8], x[ 8]);
+ z[16] = w1; w1 = 0;
+
+ word3_muladd_2(&w1, &w0, &w2, x[ 0], x[17]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 1], x[16]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 2], x[15]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 3], x[14]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 4], x[13]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 5], x[12]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 6], x[11]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 7], x[10]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 8], x[ 9]);
+ z[17] = w2; w2 = 0;
+
+ word3_muladd_2(&w2, &w1, &w0, x[ 0], x[18]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 1], x[17]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 2], x[16]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 3], x[15]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 4], x[14]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 5], x[13]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 6], x[12]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 7], x[11]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 8], x[10]);
+ word3_muladd (&w2, &w1, &w0, x[ 9], x[ 9]);
+ z[18] = w0; w0 = 0;
+
+ word3_muladd_2(&w0, &w2, &w1, x[ 0], x[19]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 1], x[18]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 2], x[17]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 3], x[16]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 4], x[15]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 5], x[14]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 6], x[13]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 7], x[12]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 8], x[11]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 9], x[10]);
+ z[19] = w1; w1 = 0;
+
+ word3_muladd_2(&w1, &w0, &w2, x[ 0], x[20]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 1], x[19]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 2], x[18]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 3], x[17]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 4], x[16]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 5], x[15]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 6], x[14]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 7], x[13]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 8], x[12]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 9], x[11]);
+ word3_muladd (&w1, &w0, &w2, x[10], x[10]);
+ z[20] = w2; w2 = 0;
+
+ word3_muladd_2(&w2, &w1, &w0, x[ 0], x[21]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 1], x[20]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 2], x[19]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 3], x[18]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 4], x[17]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 5], x[16]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 6], x[15]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 7], x[14]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 8], x[13]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 9], x[12]);
+ word3_muladd_2(&w2, &w1, &w0, x[10], x[11]);
+ z[21] = w0; w0 = 0;
+
+ word3_muladd_2(&w0, &w2, &w1, x[ 0], x[22]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 1], x[21]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 2], x[20]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 3], x[19]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 4], x[18]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 5], x[17]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 6], x[16]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 7], x[15]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 8], x[14]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 9], x[13]);
+ word3_muladd_2(&w0, &w2, &w1, x[10], x[12]);
+ word3_muladd (&w0, &w2, &w1, x[11], x[11]);
+ z[22] = w1; w1 = 0;
+
+ word3_muladd_2(&w1, &w0, &w2, x[ 0], x[23]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 1], x[22]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 2], x[21]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 3], x[20]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 4], x[19]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 5], x[18]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 6], x[17]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 7], x[16]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 8], x[15]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 9], x[14]);
+ word3_muladd_2(&w1, &w0, &w2, x[10], x[13]);
+ word3_muladd_2(&w1, &w0, &w2, x[11], x[12]);
+ z[23] = w2; w2 = 0;
+
+ word3_muladd_2(&w2, &w1, &w0, x[ 1], x[23]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 2], x[22]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 3], x[21]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 4], x[20]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 5], x[19]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 6], x[18]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 7], x[17]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 8], x[16]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 9], x[15]);
+ word3_muladd_2(&w2, &w1, &w0, x[10], x[14]);
+ word3_muladd_2(&w2, &w1, &w0, x[11], x[13]);
+ word3_muladd (&w2, &w1, &w0, x[12], x[12]);
+ z[24] = w0; w0 = 0;
+
+ word3_muladd_2(&w0, &w2, &w1, x[ 2], x[23]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 3], x[22]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 4], x[21]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 5], x[20]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 6], x[19]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 7], x[18]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 8], x[17]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 9], x[16]);
+ word3_muladd_2(&w0, &w2, &w1, x[10], x[15]);
+ word3_muladd_2(&w0, &w2, &w1, x[11], x[14]);
+ word3_muladd_2(&w0, &w2, &w1, x[12], x[13]);
+ z[25] = w1; w1 = 0;
+
+ word3_muladd_2(&w1, &w0, &w2, x[ 3], x[23]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 4], x[22]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 5], x[21]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 6], x[20]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 7], x[19]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 8], x[18]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 9], x[17]);
+ word3_muladd_2(&w1, &w0, &w2, x[10], x[16]);
+ word3_muladd_2(&w1, &w0, &w2, x[11], x[15]);
+ word3_muladd_2(&w1, &w0, &w2, x[12], x[14]);
+ word3_muladd (&w1, &w0, &w2, x[13], x[13]);
+ z[26] = w2; w2 = 0;
+
+ word3_muladd_2(&w2, &w1, &w0, x[ 4], x[23]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 5], x[22]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 6], x[21]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 7], x[20]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 8], x[19]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 9], x[18]);
+ word3_muladd_2(&w2, &w1, &w0, x[10], x[17]);
+ word3_muladd_2(&w2, &w1, &w0, x[11], x[16]);
+ word3_muladd_2(&w2, &w1, &w0, x[12], x[15]);
+ word3_muladd_2(&w2, &w1, &w0, x[13], x[14]);
+ z[27] = w0; w0 = 0;
+
+ word3_muladd_2(&w0, &w2, &w1, x[ 5], x[23]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 6], x[22]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 7], x[21]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 8], x[20]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 9], x[19]);
+ word3_muladd_2(&w0, &w2, &w1, x[10], x[18]);
+ word3_muladd_2(&w0, &w2, &w1, x[11], x[17]);
+ word3_muladd_2(&w0, &w2, &w1, x[12], x[16]);
+ word3_muladd_2(&w0, &w2, &w1, x[13], x[15]);
+ word3_muladd (&w0, &w2, &w1, x[14], x[14]);
+ z[28] = w1; w1 = 0;
+
+ word3_muladd_2(&w1, &w0, &w2, x[ 6], x[23]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 7], x[22]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 8], x[21]);
+ word3_muladd_2(&w1, &w0, &w2, x[ 9], x[20]);
+ word3_muladd_2(&w1, &w0, &w2, x[10], x[19]);
+ word3_muladd_2(&w1, &w0, &w2, x[11], x[18]);
+ word3_muladd_2(&w1, &w0, &w2, x[12], x[17]);
+ word3_muladd_2(&w1, &w0, &w2, x[13], x[16]);
+ word3_muladd_2(&w1, &w0, &w2, x[14], x[15]);
+ z[29] = w2; w2 = 0;
+
+ word3_muladd_2(&w2, &w1, &w0, x[ 7], x[23]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 8], x[22]);
+ word3_muladd_2(&w2, &w1, &w0, x[ 9], x[21]);
+ word3_muladd_2(&w2, &w1, &w0, x[10], x[20]);
+ word3_muladd_2(&w2, &w1, &w0, x[11], x[19]);
+ word3_muladd_2(&w2, &w1, &w0, x[12], x[18]);
+ word3_muladd_2(&w2, &w1, &w0, x[13], x[17]);
+ word3_muladd_2(&w2, &w1, &w0, x[14], x[16]);
+ word3_muladd (&w2, &w1, &w0, x[15], x[15]);
+ z[30] = w0; w0 = 0;
+
+ word3_muladd_2(&w0, &w2, &w1, x[ 8], x[23]);
+ word3_muladd_2(&w0, &w2, &w1, x[ 9], x[22]);
+ word3_muladd_2(&w0, &w2, &w1, x[10], x[21]);
+ word3_muladd_2(&w0, &w2, &w1, x[11], x[20]);
+ word3_muladd_2(&w0, &w2, &w1, x[12], x[19]);
+ word3_muladd_2(&w0, &w2, &w1, x[13], x[18]);
+ word3_muladd_2(&w0, &w2, &w1, x[14], x[17]);
+ word3_muladd_2(&w0, &w2, &w1, x[15], x[16]);
+ z[31] = w1; w1 = 0;
+
+ word3_muladd_2(&w1, &w0, &w2, x[ 9], x[23]);
+ word3_muladd_2(&w1, &w0, &w2, x[10], x[22]);
+ word3_muladd_2(&w1, &w0, &w2, x[11], x[21]);
+ word3_muladd_2(&w1, &w0, &w2, x[12], x[20]);
+ word3_muladd_2(&w1, &w0, &w2, x[13], x[19]);
+ word3_muladd_2(&w1, &w0, &w2, x[14], x[18]);
+ word3_muladd_2(&w1, &w0, &w2, x[15], x[17]);
+ word3_muladd (&w1, &w0, &w2, x[16], x[16]);
+ z[32] = w2; w2 = 0;
+
+ word3_muladd_2(&w2, &w1, &w0, x[10], x[23]);
+ word3_muladd_2(&w2, &w1, &w0, x[11], x[22]);
+ word3_muladd_2(&w2, &w1, &w0, x[12], x[21]);
+ word3_muladd_2(&w2, &w1, &w0, x[13], x[20]);
+ word3_muladd_2(&w2, &w1, &w0, x[14], x[19]);
+ word3_muladd_2(&w2, &w1, &w0, x[15], x[18]);
+ word3_muladd_2(&w2, &w1, &w0, x[16], x[17]);
+ z[33] = w0; w0 = 0;
+
+ word3_muladd_2(&w0, &w2, &w1, x[11], x[23]);
+ word3_muladd_2(&w0, &w2, &w1, x[12], x[22]);
+ word3_muladd_2(&w0, &w2, &w1, x[13], x[21]);
+ word3_muladd_2(&w0, &w2, &w1, x[14], x[20]);
+ word3_muladd_2(&w0, &w2, &w1, x[15], x[19]);
+ word3_muladd_2(&w0, &w2, &w1, x[16], x[18]);
+ word3_muladd (&w0, &w2, &w1, x[17], x[17]);
+ z[34] = w1; w1 = 0;
+
+ word3_muladd_2(&w1, &w0, &w2, x[12], x[23]);
+ word3_muladd_2(&w1, &w0, &w2, x[13], x[22]);
+ word3_muladd_2(&w1, &w0, &w2, x[14], x[21]);
+ word3_muladd_2(&w1, &w0, &w2, x[15], x[20]);
+ word3_muladd_2(&w1, &w0, &w2, x[16], x[19]);
+ word3_muladd_2(&w1, &w0, &w2, x[17], x[18]);
+ z[35] = w2; w2 = 0;
+
+ word3_muladd_2(&w2, &w1, &w0, x[13], x[23]);
+ word3_muladd_2(&w2, &w1, &w0, x[14], x[22]);
+ word3_muladd_2(&w2, &w1, &w0, x[15], x[21]);
+ word3_muladd_2(&w2, &w1, &w0, x[16], x[20]);
+ word3_muladd_2(&w2, &w1, &w0, x[17], x[19]);
+ word3_muladd (&w2, &w1, &w0, x[18], x[18]);
+ z[36] = w0; w0 = 0;
+
+ word3_muladd_2(&w0, &w2, &w1, x[14], x[23]);
+ word3_muladd_2(&w0, &w2, &w1, x[15], x[22]);
+ word3_muladd_2(&w0, &w2, &w1, x[16], x[21]);
+ word3_muladd_2(&w0, &w2, &w1, x[17], x[20]);
+ word3_muladd_2(&w0, &w2, &w1, x[18], x[19]);
+ z[37] = w1; w1 = 0;
+
+ word3_muladd_2(&w1, &w0, &w2, x[15], x[23]);
+ word3_muladd_2(&w1, &w0, &w2, x[16], x[22]);
+ word3_muladd_2(&w1, &w0, &w2, x[17], x[21]);
+ word3_muladd_2(&w1, &w0, &w2, x[18], x[20]);
+ word3_muladd (&w1, &w0, &w2, x[19], x[19]);
+ z[38] = w2; w2 = 0;
+
+ word3_muladd_2(&w2, &w1, &w0, x[16], x[23]);
+ word3_muladd_2(&w2, &w1, &w0, x[17], x[22]);
+ word3_muladd_2(&w2, &w1, &w0, x[18], x[21]);
+ word3_muladd_2(&w2, &w1, &w0, x[19], x[20]);
+ z[39] = w0; w0 = 0;
+
+ word3_muladd_2(&w0, &w2, &w1, x[17], x[23]);
+ word3_muladd_2(&w0, &w2, &w1, x[18], x[22]);
+ word3_muladd_2(&w0, &w2, &w1, x[19], x[21]);
+ word3_muladd (&w0, &w2, &w1, x[20], x[20]);
+ z[40] = w1; w1 = 0;
+
+ word3_muladd_2(&w1, &w0, &w2, x[18], x[23]);
+ word3_muladd_2(&w1, &w0, &w2, x[19], x[22]);
+ word3_muladd_2(&w1, &w0, &w2, x[20], x[21]);
+ z[41] = w2; w2 = 0;
+
+ word3_muladd_2(&w2, &w1, &w0, x[19], x[23]);
+ word3_muladd_2(&w2, &w1, &w0, x[20], x[22]);
+ word3_muladd (&w2, &w1, &w0, x[21], x[21]);
+ z[42] = w0; w0 = 0;
+
+ word3_muladd_2(&w0, &w2, &w1, x[20], x[23]);
+ word3_muladd_2(&w0, &w2, &w1, x[21], x[22]);
+ z[43] = w1; w1 = 0;
+
+ word3_muladd_2(&w1, &w0, &w2, x[21], x[23]);
+ word3_muladd (&w1, &w0, &w2, x[22], x[22]);
+ z[44] = w2; w2 = 0;
+
+ word3_muladd_2(&w2, &w1, &w0, x[22], x[23]);
+ z[45] = w0; w0 = 0;
+
+ word3_muladd (&w0, &w2, &w1, x[23], x[23]);
+ z[46] = w1;
+ z[47] = w2;
+ }
+
+/*
+* Comba 24x24 Multiplication
+*/
+void bigint_comba_mul24(word z[48], const word x[24], const word y[24])
+ {
+ word w2 = 0, w1 = 0, w0 = 0;
+
+ word3_muladd(&w2, &w1, &w0, x[ 0], y[ 0]);
+ z[ 0] = w0; w0 = 0;
+
+ word3_muladd(&w0, &w2, &w1, x[ 0], y[ 1]);
+ word3_muladd(&w0, &w2, &w1, x[ 1], y[ 0]);
+ z[ 1] = w1; w1 = 0;
+
+ word3_muladd(&w1, &w0, &w2, x[ 0], y[ 2]);
+ word3_muladd(&w1, &w0, &w2, x[ 1], y[ 1]);
+ word3_muladd(&w1, &w0, &w2, x[ 2], y[ 0]);
+ z[ 2] = w2; w2 = 0;
+
+ word3_muladd(&w2, &w1, &w0, x[ 0], y[ 3]);
+ word3_muladd(&w2, &w1, &w0, x[ 1], y[ 2]);
+ word3_muladd(&w2, &w1, &w0, x[ 2], y[ 1]);
+ word3_muladd(&w2, &w1, &w0, x[ 3], y[ 0]);
+ z[ 3] = w0; w0 = 0;
+
+ word3_muladd(&w0, &w2, &w1, x[ 0], y[ 4]);
+ word3_muladd(&w0, &w2, &w1, x[ 1], y[ 3]);
+ word3_muladd(&w0, &w2, &w1, x[ 2], y[ 2]);
+ word3_muladd(&w0, &w2, &w1, x[ 3], y[ 1]);
+ word3_muladd(&w0, &w2, &w1, x[ 4], y[ 0]);
+ z[ 4] = w1; w1 = 0;
+
+ word3_muladd(&w1, &w0, &w2, x[ 0], y[ 5]);
+ word3_muladd(&w1, &w0, &w2, x[ 1], y[ 4]);
+ word3_muladd(&w1, &w0, &w2, x[ 2], y[ 3]);
+ word3_muladd(&w1, &w0, &w2, x[ 3], y[ 2]);
+ word3_muladd(&w1, &w0, &w2, x[ 4], y[ 1]);
+ word3_muladd(&w1, &w0, &w2, x[ 5], y[ 0]);
+ z[ 5] = w2; w2 = 0;
+
+ word3_muladd(&w2, &w1, &w0, x[ 0], y[ 6]);
+ word3_muladd(&w2, &w1, &w0, x[ 1], y[ 5]);
+ word3_muladd(&w2, &w1, &w0, x[ 2], y[ 4]);
+ word3_muladd(&w2, &w1, &w0, x[ 3], y[ 3]);
+ word3_muladd(&w2, &w1, &w0, x[ 4], y[ 2]);
+ word3_muladd(&w2, &w1, &w0, x[ 5], y[ 1]);
+ word3_muladd(&w2, &w1, &w0, x[ 6], y[ 0]);
+ z[ 6] = w0; w0 = 0;
+
+ word3_muladd(&w0, &w2, &w1, x[ 0], y[ 7]);
+ word3_muladd(&w0, &w2, &w1, x[ 1], y[ 6]);
+ word3_muladd(&w0, &w2, &w1, x[ 2], y[ 5]);
+ word3_muladd(&w0, &w2, &w1, x[ 3], y[ 4]);
+ word3_muladd(&w0, &w2, &w1, x[ 4], y[ 3]);
+ word3_muladd(&w0, &w2, &w1, x[ 5], y[ 2]);
+ word3_muladd(&w0, &w2, &w1, x[ 6], y[ 1]);
+ word3_muladd(&w0, &w2, &w1, x[ 7], y[ 0]);
+ z[ 7] = w1; w1 = 0;
+
+ word3_muladd(&w1, &w0, &w2, x[ 0], y[ 8]);
+ word3_muladd(&w1, &w0, &w2, x[ 1], y[ 7]);
+ word3_muladd(&w1, &w0, &w2, x[ 2], y[ 6]);
+ word3_muladd(&w1, &w0, &w2, x[ 3], y[ 5]);
+ word3_muladd(&w1, &w0, &w2, x[ 4], y[ 4]);
+ word3_muladd(&w1, &w0, &w2, x[ 5], y[ 3]);
+ word3_muladd(&w1, &w0, &w2, x[ 6], y[ 2]);
+ word3_muladd(&w1, &w0, &w2, x[ 7], y[ 1]);
+ word3_muladd(&w1, &w0, &w2, x[ 8], y[ 0]);
+ z[ 8] = w2; w2 = 0;
+
+ word3_muladd(&w2, &w1, &w0, x[ 0], y[ 9]);
+ word3_muladd(&w2, &w1, &w0, x[ 1], y[ 8]);
+ word3_muladd(&w2, &w1, &w0, x[ 2], y[ 7]);
+ word3_muladd(&w2, &w1, &w0, x[ 3], y[ 6]);
+ word3_muladd(&w2, &w1, &w0, x[ 4], y[ 5]);
+ word3_muladd(&w2, &w1, &w0, x[ 5], y[ 4]);
+ word3_muladd(&w2, &w1, &w0, x[ 6], y[ 3]);
+ word3_muladd(&w2, &w1, &w0, x[ 7], y[ 2]);
+ word3_muladd(&w2, &w1, &w0, x[ 8], y[ 1]);
+ word3_muladd(&w2, &w1, &w0, x[ 9], y[ 0]);
+ z[ 9] = w0; w0 = 0;
+
+ word3_muladd(&w0, &w2, &w1, x[ 0], y[10]);
+ word3_muladd(&w0, &w2, &w1, x[ 1], y[ 9]);
+ word3_muladd(&w0, &w2, &w1, x[ 2], y[ 8]);
+ word3_muladd(&w0, &w2, &w1, x[ 3], y[ 7]);
+ word3_muladd(&w0, &w2, &w1, x[ 4], y[ 6]);
+ word3_muladd(&w0, &w2, &w1, x[ 5], y[ 5]);
+ word3_muladd(&w0, &w2, &w1, x[ 6], y[ 4]);
+ word3_muladd(&w0, &w2, &w1, x[ 7], y[ 3]);
+ word3_muladd(&w0, &w2, &w1, x[ 8], y[ 2]);
+ word3_muladd(&w0, &w2, &w1, x[ 9], y[ 1]);
+ word3_muladd(&w0, &w2, &w1, x[10], y[ 0]);
+ z[10] = w1; w1 = 0;
+
+ word3_muladd(&w1, &w0, &w2, x[ 0], y[11]);
+ word3_muladd(&w1, &w0, &w2, x[ 1], y[10]);
+ word3_muladd(&w1, &w0, &w2, x[ 2], y[ 9]);
+ word3_muladd(&w1, &w0, &w2, x[ 3], y[ 8]);
+ word3_muladd(&w1, &w0, &w2, x[ 4], y[ 7]);
+ word3_muladd(&w1, &w0, &w2, x[ 5], y[ 6]);
+ word3_muladd(&w1, &w0, &w2, x[ 6], y[ 5]);
+ word3_muladd(&w1, &w0, &w2, x[ 7], y[ 4]);
+ word3_muladd(&w1, &w0, &w2, x[ 8], y[ 3]);
+ word3_muladd(&w1, &w0, &w2, x[ 9], y[ 2]);
+ word3_muladd(&w1, &w0, &w2, x[10], y[ 1]);
+ word3_muladd(&w1, &w0, &w2, x[11], y[ 0]);
+ z[11] = w2; w2 = 0;
+
+ word3_muladd(&w2, &w1, &w0, x[ 0], y[12]);
+ word3_muladd(&w2, &w1, &w0, x[ 1], y[11]);
+ word3_muladd(&w2, &w1, &w0, x[ 2], y[10]);
+ word3_muladd(&w2, &w1, &w0, x[ 3], y[ 9]);
+ word3_muladd(&w2, &w1, &w0, x[ 4], y[ 8]);
+ word3_muladd(&w2, &w1, &w0, x[ 5], y[ 7]);
+ word3_muladd(&w2, &w1, &w0, x[ 6], y[ 6]);
+ word3_muladd(&w2, &w1, &w0, x[ 7], y[ 5]);
+ word3_muladd(&w2, &w1, &w0, x[ 8], y[ 4]);
+ word3_muladd(&w2, &w1, &w0, x[ 9], y[ 3]);
+ word3_muladd(&w2, &w1, &w0, x[10], y[ 2]);
+ word3_muladd(&w2, &w1, &w0, x[11], y[ 1]);
+ word3_muladd(&w2, &w1, &w0, x[12], y[ 0]);
+ z[12] = w0; w0 = 0;
+
+ word3_muladd(&w0, &w2, &w1, x[ 0], y[13]);
+ word3_muladd(&w0, &w2, &w1, x[ 1], y[12]);
+ word3_muladd(&w0, &w2, &w1, x[ 2], y[11]);
+ word3_muladd(&w0, &w2, &w1, x[ 3], y[10]);
+ word3_muladd(&w0, &w2, &w1, x[ 4], y[ 9]);
+ word3_muladd(&w0, &w2, &w1, x[ 5], y[ 8]);
+ word3_muladd(&w0, &w2, &w1, x[ 6], y[ 7]);
+ word3_muladd(&w0, &w2, &w1, x[ 7], y[ 6]);
+ word3_muladd(&w0, &w2, &w1, x[ 8], y[ 5]);
+ word3_muladd(&w0, &w2, &w1, x[ 9], y[ 4]);
+ word3_muladd(&w0, &w2, &w1, x[10], y[ 3]);
+ word3_muladd(&w0, &w2, &w1, x[11], y[ 2]);
+ word3_muladd(&w0, &w2, &w1, x[12], y[ 1]);
+ word3_muladd(&w0, &w2, &w1, x[13], y[ 0]);
+ z[13] = w1; w1 = 0;
+
+ word3_muladd(&w1, &w0, &w2, x[ 0], y[14]);
+ word3_muladd(&w1, &w0, &w2, x[ 1], y[13]);
+ word3_muladd(&w1, &w0, &w2, x[ 2], y[12]);
+ word3_muladd(&w1, &w0, &w2, x[ 3], y[11]);
+ word3_muladd(&w1, &w0, &w2, x[ 4], y[10]);
+ word3_muladd(&w1, &w0, &w2, x[ 5], y[ 9]);
+ word3_muladd(&w1, &w0, &w2, x[ 6], y[ 8]);
+ word3_muladd(&w1, &w0, &w2, x[ 7], y[ 7]);
+ word3_muladd(&w1, &w0, &w2, x[ 8], y[ 6]);
+ word3_muladd(&w1, &w0, &w2, x[ 9], y[ 5]);
+ word3_muladd(&w1, &w0, &w2, x[10], y[ 4]);
+ word3_muladd(&w1, &w0, &w2, x[11], y[ 3]);
+ word3_muladd(&w1, &w0, &w2, x[12], y[ 2]);
+ word3_muladd(&w1, &w0, &w2, x[13], y[ 1]);
+ word3_muladd(&w1, &w0, &w2, x[14], y[ 0]);
+ z[14] = w2; w2 = 0;
+
+ word3_muladd(&w2, &w1, &w0, x[ 0], y[15]);
+ word3_muladd(&w2, &w1, &w0, x[ 1], y[14]);
+ word3_muladd(&w2, &w1, &w0, x[ 2], y[13]);
+ word3_muladd(&w2, &w1, &w0, x[ 3], y[12]);
+ word3_muladd(&w2, &w1, &w0, x[ 4], y[11]);
+ word3_muladd(&w2, &w1, &w0, x[ 5], y[10]);
+ word3_muladd(&w2, &w1, &w0, x[ 6], y[ 9]);
+ word3_muladd(&w2, &w1, &w0, x[ 7], y[ 8]);
+ word3_muladd(&w2, &w1, &w0, x[ 8], y[ 7]);
+ word3_muladd(&w2, &w1, &w0, x[ 9], y[ 6]);
+ word3_muladd(&w2, &w1, &w0, x[10], y[ 5]);
+ word3_muladd(&w2, &w1, &w0, x[11], y[ 4]);
+ word3_muladd(&w2, &w1, &w0, x[12], y[ 3]);
+ word3_muladd(&w2, &w1, &w0, x[13], y[ 2]);
+ word3_muladd(&w2, &w1, &w0, x[14], y[ 1]);
+ word3_muladd(&w2, &w1, &w0, x[15], y[ 0]);
+ z[15] = w0; w0 = 0;
+
+ word3_muladd(&w0, &w2, &w1, x[ 0], y[16]);
+ word3_muladd(&w0, &w2, &w1, x[ 1], y[15]);
+ word3_muladd(&w0, &w2, &w1, x[ 2], y[14]);
+ word3_muladd(&w0, &w2, &w1, x[ 3], y[13]);
+ word3_muladd(&w0, &w2, &w1, x[ 4], y[12]);
+ word3_muladd(&w0, &w2, &w1, x[ 5], y[11]);
+ word3_muladd(&w0, &w2, &w1, x[ 6], y[10]);
+ word3_muladd(&w0, &w2, &w1, x[ 7], y[ 9]);
+ word3_muladd(&w0, &w2, &w1, x[ 8], y[ 8]);
+ word3_muladd(&w0, &w2, &w1, x[ 9], y[ 7]);
+ word3_muladd(&w0, &w2, &w1, x[10], y[ 6]);
+ word3_muladd(&w0, &w2, &w1, x[11], y[ 5]);
+ word3_muladd(&w0, &w2, &w1, x[12], y[ 4]);
+ word3_muladd(&w0, &w2, &w1, x[13], y[ 3]);
+ word3_muladd(&w0, &w2, &w1, x[14], y[ 2]);
+ word3_muladd(&w0, &w2, &w1, x[15], y[ 1]);
+ word3_muladd(&w0, &w2, &w1, x[16], y[ 0]);
+ z[16] = w1; w1 = 0;
+
+ word3_muladd(&w1, &w0, &w2, x[ 0], y[17]);
+ word3_muladd(&w1, &w0, &w2, x[ 1], y[16]);
+ word3_muladd(&w1, &w0, &w2, x[ 2], y[15]);
+ word3_muladd(&w1, &w0, &w2, x[ 3], y[14]);
+ word3_muladd(&w1, &w0, &w2, x[ 4], y[13]);
+ word3_muladd(&w1, &w0, &w2, x[ 5], y[12]);
+ word3_muladd(&w1, &w0, &w2, x[ 6], y[11]);
+ word3_muladd(&w1, &w0, &w2, x[ 7], y[10]);
+ word3_muladd(&w1, &w0, &w2, x[ 8], y[ 9]);
+ word3_muladd(&w1, &w0, &w2, x[ 9], y[ 8]);
+ word3_muladd(&w1, &w0, &w2, x[10], y[ 7]);
+ word3_muladd(&w1, &w0, &w2, x[11], y[ 6]);
+ word3_muladd(&w1, &w0, &w2, x[12], y[ 5]);
+ word3_muladd(&w1, &w0, &w2, x[13], y[ 4]);
+ word3_muladd(&w1, &w0, &w2, x[14], y[ 3]);
+ word3_muladd(&w1, &w0, &w2, x[15], y[ 2]);
+ word3_muladd(&w1, &w0, &w2, x[16], y[ 1]);
+ word3_muladd(&w1, &w0, &w2, x[17], y[ 0]);
+ z[17] = w2; w2 = 0;
+
+ word3_muladd(&w2, &w1, &w0, x[ 0], y[18]);
+ word3_muladd(&w2, &w1, &w0, x[ 1], y[17]);
+ word3_muladd(&w2, &w1, &w0, x[ 2], y[16]);
+ word3_muladd(&w2, &w1, &w0, x[ 3], y[15]);
+ word3_muladd(&w2, &w1, &w0, x[ 4], y[14]);
+ word3_muladd(&w2, &w1, &w0, x[ 5], y[13]);
+ word3_muladd(&w2, &w1, &w0, x[ 6], y[12]);
+ word3_muladd(&w2, &w1, &w0, x[ 7], y[11]);
+ word3_muladd(&w2, &w1, &w0, x[ 8], y[10]);
+ word3_muladd(&w2, &w1, &w0, x[ 9], y[ 9]);
+ word3_muladd(&w2, &w1, &w0, x[10], y[ 8]);
+ word3_muladd(&w2, &w1, &w0, x[11], y[ 7]);
+ word3_muladd(&w2, &w1, &w0, x[12], y[ 6]);
+ word3_muladd(&w2, &w1, &w0, x[13], y[ 5]);
+ word3_muladd(&w2, &w1, &w0, x[14], y[ 4]);
+ word3_muladd(&w2, &w1, &w0, x[15], y[ 3]);
+ word3_muladd(&w2, &w1, &w0, x[16], y[ 2]);
+ word3_muladd(&w2, &w1, &w0, x[17], y[ 1]);
+ word3_muladd(&w2, &w1, &w0, x[18], y[ 0]);
+ z[18] = w0; w0 = 0;
+
+ word3_muladd(&w0, &w2, &w1, x[ 0], y[19]);
+ word3_muladd(&w0, &w2, &w1, x[ 1], y[18]);
+ word3_muladd(&w0, &w2, &w1, x[ 2], y[17]);
+ word3_muladd(&w0, &w2, &w1, x[ 3], y[16]);
+ word3_muladd(&w0, &w2, &w1, x[ 4], y[15]);
+ word3_muladd(&w0, &w2, &w1, x[ 5], y[14]);
+ word3_muladd(&w0, &w2, &w1, x[ 6], y[13]);
+ word3_muladd(&w0, &w2, &w1, x[ 7], y[12]);
+ word3_muladd(&w0, &w2, &w1, x[ 8], y[11]);
+ word3_muladd(&w0, &w2, &w1, x[ 9], y[10]);
+ word3_muladd(&w0, &w2, &w1, x[10], y[ 9]);
+ word3_muladd(&w0, &w2, &w1, x[11], y[ 8]);
+ word3_muladd(&w0, &w2, &w1, x[12], y[ 7]);
+ word3_muladd(&w0, &w2, &w1, x[13], y[ 6]);
+ word3_muladd(&w0, &w2, &w1, x[14], y[ 5]);
+ word3_muladd(&w0, &w2, &w1, x[15], y[ 4]);
+ word3_muladd(&w0, &w2, &w1, x[16], y[ 3]);
+ word3_muladd(&w0, &w2, &w1, x[17], y[ 2]);
+ word3_muladd(&w0, &w2, &w1, x[18], y[ 1]);
+ word3_muladd(&w0, &w2, &w1, x[19], y[ 0]);
+ z[19] = w1; w1 = 0;
+
+ word3_muladd(&w1, &w0, &w2, x[ 0], y[20]);
+ word3_muladd(&w1, &w0, &w2, x[ 1], y[19]);
+ word3_muladd(&w1, &w0, &w2, x[ 2], y[18]);
+ word3_muladd(&w1, &w0, &w2, x[ 3], y[17]);
+ word3_muladd(&w1, &w0, &w2, x[ 4], y[16]);
+ word3_muladd(&w1, &w0, &w2, x[ 5], y[15]);
+ word3_muladd(&w1, &w0, &w2, x[ 6], y[14]);
+ word3_muladd(&w1, &w0, &w2, x[ 7], y[13]);
+ word3_muladd(&w1, &w0, &w2, x[ 8], y[12]);
+ word3_muladd(&w1, &w0, &w2, x[ 9], y[11]);
+ word3_muladd(&w1, &w0, &w2, x[10], y[10]);
+ word3_muladd(&w1, &w0, &w2, x[11], y[ 9]);
+ word3_muladd(&w1, &w0, &w2, x[12], y[ 8]);
+ word3_muladd(&w1, &w0, &w2, x[13], y[ 7]);
+ word3_muladd(&w1, &w0, &w2, x[14], y[ 6]);
+ word3_muladd(&w1, &w0, &w2, x[15], y[ 5]);
+ word3_muladd(&w1, &w0, &w2, x[16], y[ 4]);
+ word3_muladd(&w1, &w0, &w2, x[17], y[ 3]);
+ word3_muladd(&w1, &w0, &w2, x[18], y[ 2]);
+ word3_muladd(&w1, &w0, &w2, x[19], y[ 1]);
+ word3_muladd(&w1, &w0, &w2, x[20], y[ 0]);
+ z[20] = w2; w2 = 0;
+
+ word3_muladd(&w2, &w1, &w0, x[ 0], y[21]);
+ word3_muladd(&w2, &w1, &w0, x[ 1], y[20]);
+ word3_muladd(&w2, &w1, &w0, x[ 2], y[19]);
+ word3_muladd(&w2, &w1, &w0, x[ 3], y[18]);
+ word3_muladd(&w2, &w1, &w0, x[ 4], y[17]);
+ word3_muladd(&w2, &w1, &w0, x[ 5], y[16]);
+ word3_muladd(&w2, &w1, &w0, x[ 6], y[15]);
+ word3_muladd(&w2, &w1, &w0, x[ 7], y[14]);
+ word3_muladd(&w2, &w1, &w0, x[ 8], y[13]);
+ word3_muladd(&w2, &w1, &w0, x[ 9], y[12]);
+ word3_muladd(&w2, &w1, &w0, x[10], y[11]);
+ word3_muladd(&w2, &w1, &w0, x[11], y[10]);
+ word3_muladd(&w2, &w1, &w0, x[12], y[ 9]);
+ word3_muladd(&w2, &w1, &w0, x[13], y[ 8]);
+ word3_muladd(&w2, &w1, &w0, x[14], y[ 7]);
+ word3_muladd(&w2, &w1, &w0, x[15], y[ 6]);
+ word3_muladd(&w2, &w1, &w0, x[16], y[ 5]);
+ word3_muladd(&w2, &w1, &w0, x[17], y[ 4]);
+ word3_muladd(&w2, &w1, &w0, x[18], y[ 3]);
+ word3_muladd(&w2, &w1, &w0, x[19], y[ 2]);
+ word3_muladd(&w2, &w1, &w0, x[20], y[ 1]);
+ word3_muladd(&w2, &w1, &w0, x[21], y[ 0]);
+ z[21] = w0; w0 = 0;
+
+ word3_muladd(&w0, &w2, &w1, x[ 0], y[22]);
+ word3_muladd(&w0, &w2, &w1, x[ 1], y[21]);
+ word3_muladd(&w0, &w2, &w1, x[ 2], y[20]);
+ word3_muladd(&w0, &w2, &w1, x[ 3], y[19]);
+ word3_muladd(&w0, &w2, &w1, x[ 4], y[18]);
+ word3_muladd(&w0, &w2, &w1, x[ 5], y[17]);
+ word3_muladd(&w0, &w2, &w1, x[ 6], y[16]);
+ word3_muladd(&w0, &w2, &w1, x[ 7], y[15]);
+ word3_muladd(&w0, &w2, &w1, x[ 8], y[14]);
+ word3_muladd(&w0, &w2, &w1, x[ 9], y[13]);
+ word3_muladd(&w0, &w2, &w1, x[10], y[12]);
+ word3_muladd(&w0, &w2, &w1, x[11], y[11]);
+ word3_muladd(&w0, &w2, &w1, x[12], y[10]);
+ word3_muladd(&w0, &w2, &w1, x[13], y[ 9]);
+ word3_muladd(&w0, &w2, &w1, x[14], y[ 8]);
+ word3_muladd(&w0, &w2, &w1, x[15], y[ 7]);
+ word3_muladd(&w0, &w2, &w1, x[16], y[ 6]);
+ word3_muladd(&w0, &w2, &w1, x[17], y[ 5]);
+ word3_muladd(&w0, &w2, &w1, x[18], y[ 4]);
+ word3_muladd(&w0, &w2, &w1, x[19], y[ 3]);
+ word3_muladd(&w0, &w2, &w1, x[20], y[ 2]);
+ word3_muladd(&w0, &w2, &w1, x[21], y[ 1]);
+ word3_muladd(&w0, &w2, &w1, x[22], y[ 0]);
+ z[22] = w1; w1 = 0;
+
+ word3_muladd(&w1, &w0, &w2, x[ 0], y[23]);
+ word3_muladd(&w1, &w0, &w2, x[ 1], y[22]);
+ word3_muladd(&w1, &w0, &w2, x[ 2], y[21]);
+ word3_muladd(&w1, &w0, &w2, x[ 3], y[20]);
+ word3_muladd(&w1, &w0, &w2, x[ 4], y[19]);
+ word3_muladd(&w1, &w0, &w2, x[ 5], y[18]);
+ word3_muladd(&w1, &w0, &w2, x[ 6], y[17]);
+ word3_muladd(&w1, &w0, &w2, x[ 7], y[16]);
+ word3_muladd(&w1, &w0, &w2, x[ 8], y[15]);
+ word3_muladd(&w1, &w0, &w2, x[ 9], y[14]);
+ word3_muladd(&w1, &w0, &w2, x[10], y[13]);
+ word3_muladd(&w1, &w0, &w2, x[11], y[12]);
+ word3_muladd(&w1, &w0, &w2, x[12], y[11]);
+ word3_muladd(&w1, &w0, &w2, x[13], y[10]);
+ word3_muladd(&w1, &w0, &w2, x[14], y[ 9]);
+ word3_muladd(&w1, &w0, &w2, x[15], y[ 8]);
+ word3_muladd(&w1, &w0, &w2, x[16], y[ 7]);
+ word3_muladd(&w1, &w0, &w2, x[17], y[ 6]);
+ word3_muladd(&w1, &w0, &w2, x[18], y[ 5]);
+ word3_muladd(&w1, &w0, &w2, x[19], y[ 4]);
+ word3_muladd(&w1, &w0, &w2, x[20], y[ 3]);
+ word3_muladd(&w1, &w0, &w2, x[21], y[ 2]);
+ word3_muladd(&w1, &w0, &w2, x[22], y[ 1]);
+ word3_muladd(&w1, &w0, &w2, x[23], y[ 0]);
+ z[23] = w2; w2 = 0;
+
+ word3_muladd(&w2, &w1, &w0, x[ 1], y[23]);
+ word3_muladd(&w2, &w1, &w0, x[ 2], y[22]);
+ word3_muladd(&w2, &w1, &w0, x[ 3], y[21]);
+ word3_muladd(&w2, &w1, &w0, x[ 4], y[20]);
+ word3_muladd(&w2, &w1, &w0, x[ 5], y[19]);
+ word3_muladd(&w2, &w1, &w0, x[ 6], y[18]);
+ word3_muladd(&w2, &w1, &w0, x[ 7], y[17]);
+ word3_muladd(&w2, &w1, &w0, x[ 8], y[16]);
+ word3_muladd(&w2, &w1, &w0, x[ 9], y[15]);
+ word3_muladd(&w2, &w1, &w0, x[10], y[14]);
+ word3_muladd(&w2, &w1, &w0, x[11], y[13]);
+ word3_muladd(&w2, &w1, &w0, x[12], y[12]);
+ word3_muladd(&w2, &w1, &w0, x[13], y[11]);
+ word3_muladd(&w2, &w1, &w0, x[14], y[10]);
+ word3_muladd(&w2, &w1, &w0, x[15], y[ 9]);
+ word3_muladd(&w2, &w1, &w0, x[16], y[ 8]);
+ word3_muladd(&w2, &w1, &w0, x[17], y[ 7]);
+ word3_muladd(&w2, &w1, &w0, x[18], y[ 6]);
+ word3_muladd(&w2, &w1, &w0, x[19], y[ 5]);
+ word3_muladd(&w2, &w1, &w0, x[20], y[ 4]);
+ word3_muladd(&w2, &w1, &w0, x[21], y[ 3]);
+ word3_muladd(&w2, &w1, &w0, x[22], y[ 2]);
+ word3_muladd(&w2, &w1, &w0, x[23], y[ 1]);
+ z[24] = w0; w0 = 0;
+
+ word3_muladd(&w0, &w2, &w1, x[ 2], y[23]);
+ word3_muladd(&w0, &w2, &w1, x[ 3], y[22]);
+ word3_muladd(&w0, &w2, &w1, x[ 4], y[21]);
+ word3_muladd(&w0, &w2, &w1, x[ 5], y[20]);
+ word3_muladd(&w0, &w2, &w1, x[ 6], y[19]);
+ word3_muladd(&w0, &w2, &w1, x[ 7], y[18]);
+ word3_muladd(&w0, &w2, &w1, x[ 8], y[17]);
+ word3_muladd(&w0, &w2, &w1, x[ 9], y[16]);
+ word3_muladd(&w0, &w2, &w1, x[10], y[15]);
+ word3_muladd(&w0, &w2, &w1, x[11], y[14]);
+ word3_muladd(&w0, &w2, &w1, x[12], y[13]);
+ word3_muladd(&w0, &w2, &w1, x[13], y[12]);
+ word3_muladd(&w0, &w2, &w1, x[14], y[11]);
+ word3_muladd(&w0, &w2, &w1, x[15], y[10]);
+ word3_muladd(&w0, &w2, &w1, x[16], y[ 9]);
+ word3_muladd(&w0, &w2, &w1, x[17], y[ 8]);
+ word3_muladd(&w0, &w2, &w1, x[18], y[ 7]);
+ word3_muladd(&w0, &w2, &w1, x[19], y[ 6]);
+ word3_muladd(&w0, &w2, &w1, x[20], y[ 5]);
+ word3_muladd(&w0, &w2, &w1, x[21], y[ 4]);
+ word3_muladd(&w0, &w2, &w1, x[22], y[ 3]);
+ word3_muladd(&w0, &w2, &w1, x[23], y[ 2]);
+ z[25] = w1; w1 = 0;
+
+ word3_muladd(&w1, &w0, &w2, x[ 3], y[23]);
+ word3_muladd(&w1, &w0, &w2, x[ 4], y[22]);
+ word3_muladd(&w1, &w0, &w2, x[ 5], y[21]);
+ word3_muladd(&w1, &w0, &w2, x[ 6], y[20]);
+ word3_muladd(&w1, &w0, &w2, x[ 7], y[19]);
+ word3_muladd(&w1, &w0, &w2, x[ 8], y[18]);
+ word3_muladd(&w1, &w0, &w2, x[ 9], y[17]);
+ word3_muladd(&w1, &w0, &w2, x[10], y[16]);
+ word3_muladd(&w1, &w0, &w2, x[11], y[15]);
+ word3_muladd(&w1, &w0, &w2, x[12], y[14]);
+ word3_muladd(&w1, &w0, &w2, x[13], y[13]);
+ word3_muladd(&w1, &w0, &w2, x[14], y[12]);
+ word3_muladd(&w1, &w0, &w2, x[15], y[11]);
+ word3_muladd(&w1, &w0, &w2, x[16], y[10]);
+ word3_muladd(&w1, &w0, &w2, x[17], y[ 9]);
+ word3_muladd(&w1, &w0, &w2, x[18], y[ 8]);
+ word3_muladd(&w1, &w0, &w2, x[19], y[ 7]);
+ word3_muladd(&w1, &w0, &w2, x[20], y[ 6]);
+ word3_muladd(&w1, &w0, &w2, x[21], y[ 5]);
+ word3_muladd(&w1, &w0, &w2, x[22], y[ 4]);
+ word3_muladd(&w1, &w0, &w2, x[23], y[ 3]);
+ z[26] = w2; w2 = 0;
+
+ word3_muladd(&w2, &w1, &w0, x[ 4], y[23]);
+ word3_muladd(&w2, &w1, &w0, x[ 5], y[22]);
+ word3_muladd(&w2, &w1, &w0, x[ 6], y[21]);
+ word3_muladd(&w2, &w1, &w0, x[ 7], y[20]);
+ word3_muladd(&w2, &w1, &w0, x[ 8], y[19]);
+ word3_muladd(&w2, &w1, &w0, x[ 9], y[18]);
+ word3_muladd(&w2, &w1, &w0, x[10], y[17]);
+ word3_muladd(&w2, &w1, &w0, x[11], y[16]);
+ word3_muladd(&w2, &w1, &w0, x[12], y[15]);
+ word3_muladd(&w2, &w1, &w0, x[13], y[14]);
+ word3_muladd(&w2, &w1, &w0, x[14], y[13]);
+ word3_muladd(&w2, &w1, &w0, x[15], y[12]);
+ word3_muladd(&w2, &w1, &w0, x[16], y[11]);
+ word3_muladd(&w2, &w1, &w0, x[17], y[10]);
+ word3_muladd(&w2, &w1, &w0, x[18], y[ 9]);
+ word3_muladd(&w2, &w1, &w0, x[19], y[ 8]);
+ word3_muladd(&w2, &w1, &w0, x[20], y[ 7]);
+ word3_muladd(&w2, &w1, &w0, x[21], y[ 6]);
+ word3_muladd(&w2, &w1, &w0, x[22], y[ 5]);
+ word3_muladd(&w2, &w1, &w0, x[23], y[ 4]);
+ z[27] = w0; w0 = 0;
+
+ word3_muladd(&w0, &w2, &w1, x[ 5], y[23]);
+ word3_muladd(&w0, &w2, &w1, x[ 6], y[22]);
+ word3_muladd(&w0, &w2, &w1, x[ 7], y[21]);
+ word3_muladd(&w0, &w2, &w1, x[ 8], y[20]);
+ word3_muladd(&w0, &w2, &w1, x[ 9], y[19]);
+ word3_muladd(&w0, &w2, &w1, x[10], y[18]);
+ word3_muladd(&w0, &w2, &w1, x[11], y[17]);
+ word3_muladd(&w0, &w2, &w1, x[12], y[16]);
+ word3_muladd(&w0, &w2, &w1, x[13], y[15]);
+ word3_muladd(&w0, &w2, &w1, x[14], y[14]);
+ word3_muladd(&w0, &w2, &w1, x[15], y[13]);
+ word3_muladd(&w0, &w2, &w1, x[16], y[12]);
+ word3_muladd(&w0, &w2, &w1, x[17], y[11]);
+ word3_muladd(&w0, &w2, &w1, x[18], y[10]);
+ word3_muladd(&w0, &w2, &w1, x[19], y[ 9]);
+ word3_muladd(&w0, &w2, &w1, x[20], y[ 8]);
+ word3_muladd(&w0, &w2, &w1, x[21], y[ 7]);
+ word3_muladd(&w0, &w2, &w1, x[22], y[ 6]);
+ word3_muladd(&w0, &w2, &w1, x[23], y[ 5]);
+ z[28] = w1; w1 = 0;
+
+ word3_muladd(&w1, &w0, &w2, x[ 6], y[23]);
+ word3_muladd(&w1, &w0, &w2, x[ 7], y[22]);
+ word3_muladd(&w1, &w0, &w2, x[ 8], y[21]);
+ word3_muladd(&w1, &w0, &w2, x[ 9], y[20]);
+ word3_muladd(&w1, &w0, &w2, x[10], y[19]);
+ word3_muladd(&w1, &w0, &w2, x[11], y[18]);
+ word3_muladd(&w1, &w0, &w2, x[12], y[17]);
+ word3_muladd(&w1, &w0, &w2, x[13], y[16]);
+ word3_muladd(&w1, &w0, &w2, x[14], y[15]);
+ word3_muladd(&w1, &w0, &w2, x[15], y[14]);
+ word3_muladd(&w1, &w0, &w2, x[16], y[13]);
+ word3_muladd(&w1, &w0, &w2, x[17], y[12]);
+ word3_muladd(&w1, &w0, &w2, x[18], y[11]);
+ word3_muladd(&w1, &w0, &w2, x[19], y[10]);
+ word3_muladd(&w1, &w0, &w2, x[20], y[ 9]);
+ word3_muladd(&w1, &w0, &w2, x[21], y[ 8]);
+ word3_muladd(&w1, &w0, &w2, x[22], y[ 7]);
+ word3_muladd(&w1, &w0, &w2, x[23], y[ 6]);
+ z[29] = w2; w2 = 0;
+
+ word3_muladd(&w2, &w1, &w0, x[ 7], y[23]);
+ word3_muladd(&w2, &w1, &w0, x[ 8], y[22]);
+ word3_muladd(&w2, &w1, &w0, x[ 9], y[21]);
+ word3_muladd(&w2, &w1, &w0, x[10], y[20]);
+ word3_muladd(&w2, &w1, &w0, x[11], y[19]);
+ word3_muladd(&w2, &w1, &w0, x[12], y[18]);
+ word3_muladd(&w2, &w1, &w0, x[13], y[17]);
+ word3_muladd(&w2, &w1, &w0, x[14], y[16]);
+ word3_muladd(&w2, &w1, &w0, x[15], y[15]);
+ word3_muladd(&w2, &w1, &w0, x[16], y[14]);
+ word3_muladd(&w2, &w1, &w0, x[17], y[13]);
+ word3_muladd(&w2, &w1, &w0, x[18], y[12]);
+ word3_muladd(&w2, &w1, &w0, x[19], y[11]);
+ word3_muladd(&w2, &w1, &w0, x[20], y[10]);
+ word3_muladd(&w2, &w1, &w0, x[21], y[ 9]);
+ word3_muladd(&w2, &w1, &w0, x[22], y[ 8]);
+ word3_muladd(&w2, &w1, &w0, x[23], y[ 7]);
+ z[30] = w0; w0 = 0;
+
+ word3_muladd(&w0, &w2, &w1, x[ 8], y[23]);
+ word3_muladd(&w0, &w2, &w1, x[ 9], y[22]);
+ word3_muladd(&w0, &w2, &w1, x[10], y[21]);
+ word3_muladd(&w0, &w2, &w1, x[11], y[20]);
+ word3_muladd(&w0, &w2, &w1, x[12], y[19]);
+ word3_muladd(&w0, &w2, &w1, x[13], y[18]);
+ word3_muladd(&w0, &w2, &w1, x[14], y[17]);
+ word3_muladd(&w0, &w2, &w1, x[15], y[16]);
+ word3_muladd(&w0, &w2, &w1, x[16], y[15]);
+ word3_muladd(&w0, &w2, &w1, x[17], y[14]);
+ word3_muladd(&w0, &w2, &w1, x[18], y[13]);
+ word3_muladd(&w0, &w2, &w1, x[19], y[12]);
+ word3_muladd(&w0, &w2, &w1, x[20], y[11]);
+ word3_muladd(&w0, &w2, &w1, x[21], y[10]);
+ word3_muladd(&w0, &w2, &w1, x[22], y[ 9]);
+ word3_muladd(&w0, &w2, &w1, x[23], y[ 8]);
+ z[31] = w1; w1 = 0;
+
+ word3_muladd(&w1, &w0, &w2, x[ 9], y[23]);
+ word3_muladd(&w1, &w0, &w2, x[10], y[22]);
+ word3_muladd(&w1, &w0, &w2, x[11], y[21]);
+ word3_muladd(&w1, &w0, &w2, x[12], y[20]);
+ word3_muladd(&w1, &w0, &w2, x[13], y[19]);
+ word3_muladd(&w1, &w0, &w2, x[14], y[18]);
+ word3_muladd(&w1, &w0, &w2, x[15], y[17]);
+ word3_muladd(&w1, &w0, &w2, x[16], y[16]);
+ word3_muladd(&w1, &w0, &w2, x[17], y[15]);
+ word3_muladd(&w1, &w0, &w2, x[18], y[14]);
+ word3_muladd(&w1, &w0, &w2, x[19], y[13]);
+ word3_muladd(&w1, &w0, &w2, x[20], y[12]);
+ word3_muladd(&w1, &w0, &w2, x[21], y[11]);
+ word3_muladd(&w1, &w0, &w2, x[22], y[10]);
+ word3_muladd(&w1, &w0, &w2, x[23], y[ 9]);
+ z[32] = w2; w2 = 0;
+
+ word3_muladd(&w2, &w1, &w0, x[10], y[23]);
+ word3_muladd(&w2, &w1, &w0, x[11], y[22]);
+ word3_muladd(&w2, &w1, &w0, x[12], y[21]);
+ word3_muladd(&w2, &w1, &w0, x[13], y[20]);
+ word3_muladd(&w2, &w1, &w0, x[14], y[19]);
+ word3_muladd(&w2, &w1, &w0, x[15], y[18]);
+ word3_muladd(&w2, &w1, &w0, x[16], y[17]);
+ word3_muladd(&w2, &w1, &w0, x[17], y[16]);
+ word3_muladd(&w2, &w1, &w0, x[18], y[15]);
+ word3_muladd(&w2, &w1, &w0, x[19], y[14]);
+ word3_muladd(&w2, &w1, &w0, x[20], y[13]);
+ word3_muladd(&w2, &w1, &w0, x[21], y[12]);
+ word3_muladd(&w2, &w1, &w0, x[22], y[11]);
+ word3_muladd(&w2, &w1, &w0, x[23], y[10]);
+ z[33] = w0; w0 = 0;
+
+ word3_muladd(&w0, &w2, &w1, x[11], y[23]);
+ word3_muladd(&w0, &w2, &w1, x[12], y[22]);
+ word3_muladd(&w0, &w2, &w1, x[13], y[21]);
+ word3_muladd(&w0, &w2, &w1, x[14], y[20]);
+ word3_muladd(&w0, &w2, &w1, x[15], y[19]);
+ word3_muladd(&w0, &w2, &w1, x[16], y[18]);
+ word3_muladd(&w0, &w2, &w1, x[17], y[17]);
+ word3_muladd(&w0, &w2, &w1, x[18], y[16]);
+ word3_muladd(&w0, &w2, &w1, x[19], y[15]);
+ word3_muladd(&w0, &w2, &w1, x[20], y[14]);
+ word3_muladd(&w0, &w2, &w1, x[21], y[13]);
+ word3_muladd(&w0, &w2, &w1, x[22], y[12]);
+ word3_muladd(&w0, &w2, &w1, x[23], y[11]);
+ z[34] = w1; w1 = 0;
+
+ word3_muladd(&w1, &w0, &w2, x[12], y[23]);
+ word3_muladd(&w1, &w0, &w2, x[13], y[22]);
+ word3_muladd(&w1, &w0, &w2, x[14], y[21]);
+ word3_muladd(&w1, &w0, &w2, x[15], y[20]);
+ word3_muladd(&w1, &w0, &w2, x[16], y[19]);
+ word3_muladd(&w1, &w0, &w2, x[17], y[18]);
+ word3_muladd(&w1, &w0, &w2, x[18], y[17]);
+ word3_muladd(&w1, &w0, &w2, x[19], y[16]);
+ word3_muladd(&w1, &w0, &w2, x[20], y[15]);
+ word3_muladd(&w1, &w0, &w2, x[21], y[14]);
+ word3_muladd(&w1, &w0, &w2, x[22], y[13]);
+ word3_muladd(&w1, &w0, &w2, x[23], y[12]);
+ z[35] = w2; w2 = 0;
+
+ word3_muladd(&w2, &w1, &w0, x[13], y[23]);
+ word3_muladd(&w2, &w1, &w0, x[14], y[22]);
+ word3_muladd(&w2, &w1, &w0, x[15], y[21]);
+ word3_muladd(&w2, &w1, &w0, x[16], y[20]);
+ word3_muladd(&w2, &w1, &w0, x[17], y[19]);
+ word3_muladd(&w2, &w1, &w0, x[18], y[18]);
+ word3_muladd(&w2, &w1, &w0, x[19], y[17]);
+ word3_muladd(&w2, &w1, &w0, x[20], y[16]);
+ word3_muladd(&w2, &w1, &w0, x[21], y[15]);
+ word3_muladd(&w2, &w1, &w0, x[22], y[14]);
+ word3_muladd(&w2, &w1, &w0, x[23], y[13]);
+ z[36] = w0; w0 = 0;
+
+ word3_muladd(&w0, &w2, &w1, x[14], y[23]);
+ word3_muladd(&w0, &w2, &w1, x[15], y[22]);
+ word3_muladd(&w0, &w2, &w1, x[16], y[21]);
+ word3_muladd(&w0, &w2, &w1, x[17], y[20]);
+ word3_muladd(&w0, &w2, &w1, x[18], y[19]);
+ word3_muladd(&w0, &w2, &w1, x[19], y[18]);
+ word3_muladd(&w0, &w2, &w1, x[20], y[17]);
+ word3_muladd(&w0, &w2, &w1, x[21], y[16]);
+ word3_muladd(&w0, &w2, &w1, x[22], y[15]);
+ word3_muladd(&w0, &w2, &w1, x[23], y[14]);
+ z[37] = w1; w1 = 0;
+
+ word3_muladd(&w1, &w0, &w2, x[15], y[23]);
+ word3_muladd(&w1, &w0, &w2, x[16], y[22]);
+ word3_muladd(&w1, &w0, &w2, x[17], y[21]);
+ word3_muladd(&w1, &w0, &w2, x[18], y[20]);
+ word3_muladd(&w1, &w0, &w2, x[19], y[19]);
+ word3_muladd(&w1, &w0, &w2, x[20], y[18]);
+ word3_muladd(&w1, &w0, &w2, x[21], y[17]);
+ word3_muladd(&w1, &w0, &w2, x[22], y[16]);
+ word3_muladd(&w1, &w0, &w2, x[23], y[15]);
+ z[38] = w2; w2 = 0;
+
+ word3_muladd(&w2, &w1, &w0, x[16], y[23]);
+ word3_muladd(&w2, &w1, &w0, x[17], y[22]);
+ word3_muladd(&w2, &w1, &w0, x[18], y[21]);
+ word3_muladd(&w2, &w1, &w0, x[19], y[20]);
+ word3_muladd(&w2, &w1, &w0, x[20], y[19]);
+ word3_muladd(&w2, &w1, &w0, x[21], y[18]);
+ word3_muladd(&w2, &w1, &w0, x[22], y[17]);
+ word3_muladd(&w2, &w1, &w0, x[23], y[16]);
+ z[39] = w0; w0 = 0;
+
+ word3_muladd(&w0, &w2, &w1, x[17], y[23]);
+ word3_muladd(&w0, &w2, &w1, x[18], y[22]);
+ word3_muladd(&w0, &w2, &w1, x[19], y[21]);
+ word3_muladd(&w0, &w2, &w1, x[20], y[20]);
+ word3_muladd(&w0, &w2, &w1, x[21], y[19]);
+ word3_muladd(&w0, &w2, &w1, x[22], y[18]);
+ word3_muladd(&w0, &w2, &w1, x[23], y[17]);
+ z[40] = w1; w1 = 0;
+
+ word3_muladd(&w1, &w0, &w2, x[18], y[23]);
+ word3_muladd(&w1, &w0, &w2, x[19], y[22]);
+ word3_muladd(&w1, &w0, &w2, x[20], y[21]);
+ word3_muladd(&w1, &w0, &w2, x[21], y[20]);
+ word3_muladd(&w1, &w0, &w2, x[22], y[19]);
+ word3_muladd(&w1, &w0, &w2, x[23], y[18]);
+ z[41] = w2; w2 = 0;
+
+ word3_muladd(&w2, &w1, &w0, x[19], y[23]);
+ word3_muladd(&w2, &w1, &w0, x[20], y[22]);
+ word3_muladd(&w2, &w1, &w0, x[21], y[21]);
+ word3_muladd(&w2, &w1, &w0, x[22], y[20]);
+ word3_muladd(&w2, &w1, &w0, x[23], y[19]);
+ z[42] = w0; w0 = 0;
+
+ word3_muladd(&w0, &w2, &w1, x[20], y[23]);
+ word3_muladd(&w0, &w2, &w1, x[21], y[22]);
+ word3_muladd(&w0, &w2, &w1, x[22], y[21]);
+ word3_muladd(&w0, &w2, &w1, x[23], y[20]);
+ z[43] = w1; w1 = 0;
+
+ word3_muladd(&w1, &w0, &w2, x[21], y[23]);
+ word3_muladd(&w1, &w0, &w2, x[22], y[22]);
+ word3_muladd(&w1, &w0, &w2, x[23], y[21]);
+ z[44] = w2; w2 = 0;
+
+ word3_muladd(&w2, &w1, &w0, x[22], y[23]);
+ word3_muladd(&w2, &w1, &w0, x[23], y[22]);
+ z[45] = w0; w0 = 0;
+
+ word3_muladd(&w0, &w2, &w1, x[23], y[23]);
+ z[46] = w1;
+ z[47] = w2;
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/math/mp/mp_core.h b/comm/third_party/botan/src/lib/math/mp/mp_core.h
new file mode 100644
index 0000000000..c4bf8e8815
--- /dev/null
+++ b/comm/third_party/botan/src/lib/math/mp/mp_core.h
@@ -0,0 +1,819 @@
+/*
+* MPI Algorithms
+* (C) 1999-2010,2018 Jack Lloyd
+* 2006 Luca Piccarreta
+* 2016 Matthias Gierlings
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_MP_CORE_OPS_H_
+#define BOTAN_MP_CORE_OPS_H_
+
+#include <botan/types.h>
+#include <botan/exceptn.h>
+#include <botan/mem_ops.h>
+#include <botan/internal/mp_asmi.h>
+#include <botan/internal/ct_utils.h>
+#include <algorithm>
+
+namespace Botan {
+
+const word MP_WORD_MAX = ~static_cast<word>(0);
+
+/*
+* If cond == 0, does nothing.
+* If cond > 0, swaps x[0:size] with y[0:size]
+* Runs in constant time
+*/
+inline void bigint_cnd_swap(word cnd, word x[], word y[], size_t size)
+ {
+ const auto mask = CT::Mask<word>::expand(cnd);
+
+ for(size_t i = 0; i != size; ++i)
+ {
+ const word a = x[i];
+ const word b = y[i];
+ x[i] = mask.select(b, a);
+ y[i] = mask.select(a, b);
+ }
+ }
+
+inline word bigint_cnd_add(word cnd, word x[], word x_size,
+ const word y[], size_t y_size)
+ {
+ BOTAN_ASSERT(x_size >= y_size, "Expected sizes");
+
+ const auto mask = CT::Mask<word>::expand(cnd);
+
+ word carry = 0;
+
+ const size_t blocks = y_size - (y_size % 8);
+ word z[8] = { 0 };
+
+ for(size_t i = 0; i != blocks; i += 8)
+ {
+ carry = word8_add3(z, x + i, y + i, carry);
+ mask.select_n(x + i, z, x + i, 8);
+ }
+
+ for(size_t i = blocks; i != y_size; ++i)
+ {
+ z[0] = word_add(x[i], y[i], &carry);
+ x[i] = mask.select(z[0], x[i]);
+ }
+
+ for(size_t i = y_size; i != x_size; ++i)
+ {
+ z[0] = word_add(x[i], 0, &carry);
+ x[i] = mask.select(z[0], x[i]);
+ }
+
+ return mask.if_set_return(carry);
+ }
+
+/*
+* If cond > 0 adds x[0:size] and y[0:size] and returns carry
+* Runs in constant time
+*/
+inline word bigint_cnd_add(word cnd, word x[], const word 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
+*/
+inline word bigint_cnd_sub(word cnd,
+ word x[], size_t x_size,
+ const word y[], size_t y_size)
+ {
+ BOTAN_ASSERT(x_size >= y_size, "Expected sizes");
+
+ const auto mask = CT::Mask<word>::expand(cnd);
+
+ word carry = 0;
+
+ const size_t blocks = y_size - (y_size % 8);
+ word z[8] = { 0 };
+
+ for(size_t i = 0; i != blocks; i += 8)
+ {
+ carry = word8_sub3(z, x + i, y + i, carry);
+ mask.select_n(x + i, z, x + i, 8);
+ }
+
+ for(size_t i = blocks; i != y_size; ++i)
+ {
+ z[0] = word_sub(x[i], y[i], &carry);
+ x[i] = mask.select(z[0], x[i]);
+ }
+
+ for(size_t i = y_size; i != x_size; ++i)
+ {
+ z[0] = word_sub(x[i], 0, &carry);
+ x[i] = mask.select(z[0], x[i]);
+ }
+
+ return mask.if_set_return(carry);
+ }
+
+/*
+* If cond > 0 adds x[0:size] and y[0:size] and returns carry
+* Runs in constant time
+*/
+inline word bigint_cnd_sub(word cnd, word x[], const word y[], size_t size)
+ {
+ 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
+*/
+inline void bigint_cnd_add_or_sub(CT::Mask<word> mask, word x[], const word y[], size_t size)
+ {
+ const size_t blocks = size - (size % 8);
+
+ word carry = 0;
+ word borrow = 0;
+
+ word t0[8] = { 0 };
+ word 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);
+
+ for(size_t j = 0; j != 8; ++j)
+ x[i+j] = mask.select(t0[j], t1[j]);
+ }
+
+ for(size_t i = blocks; i != size; ++i)
+ {
+ const word a = word_add(x[i], y[i], &carry);
+ const word 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
+*/
+inline word bigint_cnd_addsub(CT::Mask<word> mask, word x[],
+ const word y[], const word z[],
+ size_t size)
+ {
+ const size_t blocks = size - (size % 8);
+
+ word carry = 0;
+ word borrow = 0;
+
+ word t0[8] = { 0 };
+ word 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);
+
+ for(size_t j = 0; j != 8; ++j)
+ x[i+j] = mask.select(t0[j], t1[j]);
+ }
+
+ 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]);
+ }
+
+ return mask.select(carry, borrow);
+ }
+
+/*
+* 2s complement absolute value
+* If cond > 0 sets x to ~x + 1
+* Runs in constant time
+*/
+inline void bigint_cnd_abs(word cnd, word x[], size_t size)
+ {
+ const auto mask = CT::Mask<word>::expand(cnd);
+
+ word carry = mask.if_set_return(1);
+ for(size_t i = 0; i != size; ++i)
+ {
+ const word z = word_add(~x[i], 0, &carry);
+ x[i] = mask.select(z, x[i]);
+ }
+ }
+
+/**
+* Two operand addition with carry out
+*/
+inline word bigint_add2_nc(word x[], size_t x_size, const word y[], size_t y_size)
+ {
+ word carry = 0;
+
+ BOTAN_ASSERT(x_size >= y_size, "Expected sizes");
+
+ const size_t blocks = y_size - (y_size % 8);
+
+ for(size_t i = 0; i != blocks; i += 8)
+ carry = word8_add2(x + i, y + i, carry);
+
+ for(size_t i = blocks; i != y_size; ++i)
+ x[i] = word_add(x[i], y[i], &carry);
+
+ for(size_t i = y_size; i != x_size; ++i)
+ x[i] = word_add(x[i], 0, &carry);
+
+ return carry;
+ }
+
+/**
+* Three operand addition with carry out
+*/
+inline word bigint_add3_nc(word z[],
+ const word x[], size_t x_size,
+ const word y[], size_t y_size)
+ {
+ if(x_size < y_size)
+ { return bigint_add3_nc(z, y, y_size, x, x_size); }
+
+ word carry = 0;
+
+ const size_t blocks = y_size - (y_size % 8);
+
+ for(size_t i = 0; i != blocks; i += 8)
+ carry = word8_add3(z + i, x + i, y + i, carry);
+
+ for(size_t i = blocks; i != y_size; ++i)
+ z[i] = word_add(x[i], y[i], &carry);
+
+ for(size_t i = y_size; i != x_size; ++i)
+ z[i] = word_add(x[i], 0, &carry);
+
+ return carry;
+ }
+
+/**
+* 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)
+*/
+inline void bigint_add2(word x[], size_t x_size,
+ const word y[], size_t y_size)
+ {
+ x[x_size] += bigint_add2_nc(x, x_size, y, y_size);
+ }
+
+/**
+* Three operand addition
+*/
+inline void bigint_add3(word z[],
+ const word x[], size_t x_size,
+ const word 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
+*/
+inline word bigint_sub2(word x[], size_t x_size,
+ const word y[], size_t y_size)
+ {
+ word borrow = 0;
+
+ BOTAN_ASSERT(x_size >= y_size, "Expected sizes");
+
+ const size_t blocks = y_size - (y_size % 8);
+
+ for(size_t i = 0; i != blocks; i += 8)
+ borrow = word8_sub2(x + i, y + i, borrow);
+
+ for(size_t i = blocks; i != y_size; ++i)
+ x[i] = word_sub(x[i], y[i], &borrow);
+
+ for(size_t i = y_size; i != x_size; ++i)
+ x[i] = word_sub(x[i], 0, &borrow);
+
+ return borrow;
+ }
+
+/**
+* Two operand subtraction, x = y - x; assumes y >= x
+*/
+inline void bigint_sub2_rev(word x[], const word y[], size_t y_size)
+ {
+ word 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)
+ x[i] = word_sub(y[i], x[i], &borrow);
+
+ BOTAN_ASSERT(borrow == 0, "y must be greater than x");
+ }
+
+/**
+* Three operand subtraction
+*/
+inline word bigint_sub3(word z[],
+ const word x[], size_t x_size,
+ const word y[], size_t y_size)
+ {
+ word borrow = 0;
+
+ BOTAN_ASSERT(x_size >= y_size, "Expected sizes");
+
+ const size_t blocks = y_size - (y_size % 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 != y_size; ++i)
+ z[i] = word_sub(x[i], y[i], &borrow);
+
+ for(size_t i = y_size; i != x_size; ++i)
+ z[i] = word_sub(x[i], 0, &borrow);
+
+ return borrow;
+ }
+
+/**
+* Return 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
+*
+* Returns ~0 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
+* @param N length of x and y
+* @param ws array of at least 2*N words
+*/
+inline CT::Mask<word>
+bigint_sub_abs(word z[],
+ const word x[], const word y[], size_t N,
+ word ws[])
+ {
+ // Subtract in both direction then conditional copy out the result
+
+ word* ws0 = ws;
+ word* ws1 = ws + N;
+
+ word borrow0 = 0;
+ word borrow1 = 0;
+
+ const size_t blocks = N - (N % 8);
+
+ for(size_t i = 0; i != blocks; i += 8)
+ {
+ borrow0 = word8_sub3(ws0 + i, x + i, y + i, borrow0);
+ borrow1 = word8_sub3(ws1 + i, y + i, x + i, borrow1);
+ }
+
+ for(size_t i = blocks; i != N; ++i)
+ {
+ ws0[i] = word_sub(x[i], y[i], &borrow0);
+ ws1[i] = word_sub(y[i], x[i], &borrow1);
+ }
+
+ return CT::conditional_copy_mem(borrow0, z, ws1, ws0, N);
+ }
+
+/*
+* Shift Operations
+*/
+inline void bigint_shl1(word x[], size_t x_size, size_t x_words,
+ size_t word_shift, size_t bit_shift)
+ {
+ copy_mem(x + word_shift, x, x_words);
+ clear_mem(x, word_shift);
+
+ const auto carry_mask = CT::Mask<word>::expand(bit_shift);
+ const size_t carry_shift = carry_mask.if_set_return(BOTAN_MP_WORD_BITS - bit_shift);
+
+ word carry = 0;
+ for(size_t i = word_shift; i != x_size; ++i)
+ {
+ const word w = x[i];
+ x[i] = (w << bit_shift) | carry;
+ carry = carry_mask.if_set_return(w >> carry_shift);
+ }
+ }
+
+inline void bigint_shr1(word x[], size_t x_size,
+ size_t word_shift, size_t bit_shift)
+ {
+ const size_t top = x_size >= word_shift ? (x_size - word_shift) : 0;
+
+ if(top > 0)
+ copy_mem(x, x + word_shift, top);
+ clear_mem(x + top, std::min(word_shift, x_size));
+
+ const auto carry_mask = CT::Mask<word>::expand(bit_shift);
+ const size_t carry_shift = carry_mask.if_set_return(BOTAN_MP_WORD_BITS - bit_shift);
+
+ word carry = 0;
+
+ for(size_t i = 0; i != top; ++i)
+ {
+ const word w = x[top - i - 1];
+ x[top-i-1] = (w >> bit_shift) | carry;
+ carry = carry_mask.if_set_return(w << carry_shift);
+ }
+ }
+
+inline void bigint_shl2(word y[], const word x[], size_t x_size,
+ size_t word_shift, size_t bit_shift)
+ {
+ copy_mem(y + word_shift, x, x_size);
+
+ const auto carry_mask = CT::Mask<word>::expand(bit_shift);
+ const size_t carry_shift = carry_mask.if_set_return(BOTAN_MP_WORD_BITS - bit_shift);
+
+ word carry = 0;
+ for(size_t i = word_shift; i != x_size + word_shift + 1; ++i)
+ {
+ const word w = y[i];
+ y[i] = (w << bit_shift) | carry;
+ carry = carry_mask.if_set_return(w >> carry_shift);
+ }
+ }
+
+inline void bigint_shr2(word y[], const word x[], size_t x_size,
+ size_t word_shift, size_t bit_shift)
+ {
+ 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);
+
+ const auto carry_mask = CT::Mask<word>::expand(bit_shift);
+ const size_t carry_shift = carry_mask.if_set_return(BOTAN_MP_WORD_BITS - bit_shift);
+
+ word carry = 0;
+ for(size_t i = new_size; i > 0; --i)
+ {
+ word w = y[i-1];
+ y[i-1] = (w >> bit_shift) | carry;
+ carry = carry_mask.if_set_return(w << carry_shift);
+ }
+ }
+
+/*
+* Linear Multiply - returns the carry
+*/
+inline word BOTAN_WARN_UNUSED_RESULT bigint_linmul2(word x[], size_t x_size, word y)
+ {
+ const size_t blocks = x_size - (x_size % 8);
+
+ word 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)
+ x[i] = word_madd2(x[i], y, &carry);
+
+ return carry;
+ }
+
+inline void bigint_linmul3(word z[], const word x[], size_t x_size, word y)
+ {
+ const size_t blocks = x_size - (x_size % 8);
+
+ word carry = 0;
+
+ for(size_t i = 0; i != blocks; i += 8)
+ carry = word8_linmul3(z + i, x + i, y, carry);
+
+ for(size_t i = blocks; i != x_size; ++i)
+ z[i] = word_madd2(x[i], y, &carry);
+
+ z[x_size] = carry;
+ }
+
+/**
+* Compare x and y
+* Return -1 if x < y
+* Return 0 if x == y
+* Return 1 if x > y
+*/
+inline int32_t bigint_cmp(const word x[], size_t x_size,
+ const word y[], size_t y_size)
+ {
+ static_assert(sizeof(word) >= sizeof(uint32_t), "Size assumption");
+
+ const word LT = static_cast<word>(-1);
+ const word EQ = 0;
+ const word GT = 1;
+
+ const size_t common_elems = std::min(x_size, y_size);
+
+ word result = EQ; // until found otherwise
+
+ for(size_t i = 0; i != common_elems; i++)
+ {
+ const auto is_eq = CT::Mask<word>::is_equal(x[i], y[i]);
+ const auto is_lt = CT::Mask<word>::is_lt(x[i], y[i]);
+
+ result = is_eq.select(result, is_lt.select(LT, GT));
+ }
+
+ if(x_size < y_size)
+ {
+ word mask = 0;
+ for(size_t i = x_size; i != y_size; i++)
+ mask |= y[i];
+
+ // If any bits were set in high part of y, then x < y
+ result = CT::Mask<word>::is_zero(mask).select(result, LT);
+ }
+ else if(y_size < x_size)
+ {
+ word mask = 0;
+ for(size_t i = y_size; i != x_size; i++)
+ mask |= x[i];
+
+ // If any bits were set in high part of x, then x > y
+ result = CT::Mask<word>::is_zero(mask).select(result, GT);
+ }
+
+ CT::unpoison(result);
+ BOTAN_DEBUG_ASSERT(result == LT || result == GT || result == EQ);
+ return static_cast<int32_t>(result);
+ }
+
+/**
+* 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
+*/
+inline CT::Mask<word>
+bigint_ct_is_lt(const word x[], size_t x_size,
+ const word y[], size_t y_size,
+ bool lt_or_equal = false)
+ {
+ const size_t common_elems = std::min(x_size, y_size);
+
+ auto is_lt = CT::Mask<word>::expand(lt_or_equal);
+
+ for(size_t i = 0; i != common_elems; i++)
+ {
+ const auto eq = CT::Mask<word>::is_equal(x[i], y[i]);
+ const auto lt = CT::Mask<word>::is_lt(x[i], y[i]);
+ is_lt = eq.select_mask(is_lt, lt);
+ }
+
+ if(x_size < y_size)
+ {
+ word mask = 0;
+ for(size_t i = x_size; i != y_size; i++)
+ mask |= y[i];
+ // If any bits were set in high part of y, then is_lt should be forced true
+ is_lt |= CT::Mask<word>::expand(mask);
+ }
+ else if(y_size < x_size)
+ {
+ word mask = 0;
+ for(size_t i = y_size; i != x_size; i++)
+ mask |= x[i];
+
+ // If any bits were set in high part of x, then is_lt should be false
+ is_lt &= CT::Mask<word>::is_zero(mask);
+ }
+
+ return is_lt;
+ }
+
+inline CT::Mask<word>
+bigint_ct_is_eq(const word x[], size_t x_size,
+ const word y[], size_t y_size)
+ {
+ const size_t common_elems = std::min(x_size, y_size);
+
+ word diff = 0;
+
+ for(size_t i = 0; i != common_elems; i++)
+ {
+ diff |= (x[i] ^ y[i]);
+ }
+
+ // If any bits were set in high part of x/y, then they are not equal
+ if(x_size < y_size)
+ {
+ for(size_t i = x_size; i != y_size; i++)
+ diff |= y[i];
+ }
+ else if(y_size < x_size)
+ {
+ for(size_t i = y_size; i != x_size; i++)
+ diff |= x[i];
+ }
+
+ return CT::Mask<word>::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
+*/
+inline int32_t
+bigint_sub_abs(word z[],
+ const word x[], size_t x_size,
+ const word 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);
+
+ return relative_size;
+ }
+
+/**
+* Set t to t-s modulo mod
+*
+* @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
+*/
+inline void
+bigint_mod_sub(word t[], const word s[], const word mod[], size_t mod_sw, word ws[])
+ {
+ // is t < s or not?
+ const auto is_lt = bigint_ct_is_lt(t, mod_sw, s, mod_sw);
+
+ // ws = p - s
+ const word borrow = bigint_sub3(ws, mod, mod_sw, s, mod_sw);
+
+ // Compute either (t - s) or (t + (p - s)) depending on mask
+ const word carry = bigint_cnd_addsub(is_lt, t, ws, s, mod_sw);
+
+ BOTAN_DEBUG_ASSERT(borrow == 0 && carry == 0);
+ BOTAN_UNUSED(carry, borrow);
+ }
+
+template<size_t N>
+inline void bigint_mod_sub_n(word t[], const word s[], const word mod[], word ws[])
+ {
+ // is t < s or not?
+ const auto is_lt = bigint_ct_is_lt(t, N, s, N);
+
+ // ws = p - s
+ const word borrow = bigint_sub3(ws, mod, N, s, N);
+
+ // Compute either (t - s) or (t + (p - s)) depending on mask
+ const word carry = bigint_cnd_addsub(is_lt, t, ws, s, N);
+
+ BOTAN_DEBUG_ASSERT(borrow == 0 && carry == 0);
+ BOTAN_UNUSED(carry, borrow);
+ }
+
+/**
+* Compute ((n1<<bits) + n0) / d
+*/
+inline word bigint_divop(word n1, word n0, word d)
+ {
+ if(d == 0)
+ throw Invalid_Argument("bigint_divop divide by zero");
+
+#if defined(BOTAN_HAS_MP_DWORD)
+ return ((static_cast<dword>(n1) << BOTAN_MP_WORD_BITS) | n0) / d;
+#else
+
+ word high = n1 % d;
+ word quotient = 0;
+
+ for(size_t i = 0; i != BOTAN_MP_WORD_BITS; ++i)
+ {
+ const word high_top_bit = high >> (BOTAN_MP_WORD_BITS-1);
+
+ high <<= 1;
+ high |= (n0 >> (BOTAN_MP_WORD_BITS-1-i)) & 1;
+ quotient <<= 1;
+
+ if(high_top_bit || high >= d)
+ {
+ high -= d;
+ quotient |= 1;
+ }
+ }
+
+ return quotient;
+#endif
+ }
+
+/**
+* Compute ((n1<<bits) + n0) % d
+*/
+inline word bigint_modop(word n1, word n0, word d)
+ {
+ if(d == 0)
+ throw Invalid_Argument("bigint_modop divide by zero");
+
+#if defined(BOTAN_HAS_MP_DWORD)
+ return ((static_cast<dword>(n1) << BOTAN_MP_WORD_BITS) | n0) % d;
+#else
+ word z = bigint_divop(n1, n0, d);
+ word dummy = 0;
+ z = word_madd2(z, d, &dummy);
+ return (n0-z);
+#endif
+ }
+
+/*
+* Comba Multiplication / Squaring
+*/
+void bigint_comba_mul4(word z[8], const word x[4], const word y[4]);
+void bigint_comba_mul6(word z[12], const word x[6], const word y[6]);
+void bigint_comba_mul8(word z[16], const word x[8], const word y[8]);
+void bigint_comba_mul9(word z[18], const word x[9], const word y[9]);
+void bigint_comba_mul16(word z[32], const word x[16], const word y[16]);
+void bigint_comba_mul24(word z[48], const word x[24], const word y[24]);
+
+void bigint_comba_sqr4(word out[8], const word in[4]);
+void bigint_comba_sqr6(word out[12], const word in[6]);
+void bigint_comba_sqr8(word out[16], const word in[8]);
+void bigint_comba_sqr9(word out[18], const word in[9]);
+void bigint_comba_sqr16(word out[32], const word in[16]);
+void bigint_comba_sqr24(word out[48], const word in[24]);
+
+/**
+* Montgomery Reduction
+* @param z integer to reduce, of size exactly 2*(p_size+1).
+ Output is in the first p_size+1 words, higher
+ words are set to zero.
+* @param p modulus
+* @param p_size size of p
+* @param p_dash Montgomery value
+* @param workspace array of at least 2*(p_size+1) words
+* @param ws_size size of workspace in words
+*/
+void bigint_monty_redc(word z[],
+ const word p[], size_t p_size,
+ word p_dash,
+ word workspace[],
+ size_t ws_size);
+
+/*
+* High Level Multiplication/Squaring Interfaces
+*/
+
+void bigint_mul(word z[], size_t z_size,
+ const word x[], size_t x_size, size_t x_sw,
+ const word y[], size_t y_size, size_t y_sw,
+ word workspace[], size_t ws_size);
+
+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);
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/math/mp/mp_karat.cpp b/comm/third_party/botan/src/lib/math/mp/mp_karat.cpp
new file mode 100644
index 0000000000..15fcafa5be
--- /dev/null
+++ b/comm/third_party/botan/src/lib/math/mp/mp_karat.cpp
@@ -0,0 +1,408 @@
+/*
+* Multiplication and Squaring
+* (C) 1999-2010,2018 Jack Lloyd
+* 2016 Matthias Gierlings
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/mp_core.h>
+#include <botan/internal/mp_asmi.h>
+#include <botan/internal/ct_utils.h>
+#include <botan/mem_ops.h>
+#include <botan/exceptn.h>
+
+namespace Botan {
+
+namespace {
+
+const size_t KARATSUBA_MULTIPLY_THRESHOLD = 32;
+const size_t KARATSUBA_SQUARE_THRESHOLD = 32;
+
+/*
+* Simple O(N^2) Multiplication
+*/
+void basecase_mul(word z[], size_t z_size,
+ const word x[], size_t x_size,
+ const word y[], size_t y_size)
+ {
+ if(z_size < x_size + y_size)
+ throw Invalid_Argument("basecase_mul z_size too small");
+
+ const size_t x_size_8 = x_size - (x_size % 8);
+
+ clear_mem(z, z_size);
+
+ for(size_t i = 0; i != y_size; ++i)
+ {
+ const word y_i = y[i];
+
+ word carry = 0;
+
+ for(size_t j = 0; j != x_size_8; j += 8)
+ carry = word8_madd3(z + i + j, x + j, y_i, carry);
+
+ for(size_t j = x_size_8; j != x_size; ++j)
+ z[i+j] = word_madd3(x[j], y_i, z[i+j], &carry);
+
+ z[x_size+i] = carry;
+ }
+ }
+
+void basecase_sqr(word z[], size_t z_size,
+ const word x[], size_t x_size)
+ {
+ if(z_size < 2*x_size)
+ throw Invalid_Argument("basecase_sqr z_size too small");
+
+ const size_t x_size_8 = x_size - (x_size % 8);
+
+ clear_mem(z, z_size);
+
+ for(size_t i = 0; i != x_size; ++i)
+ {
+ const word x_i = x[i];
+
+ word carry = 0;
+
+ for(size_t j = 0; j != x_size_8; j += 8)
+ carry = word8_madd3(z + i + j, x + j, x_i, carry);
+
+ for(size_t j = x_size_8; j != x_size; ++j)
+ z[i+j] = word_madd3(x[j], x_i, z[i+j], &carry);
+
+ z[x_size+i] = carry;
+ }
+ }
+
+/*
+* 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)
+ {
+ switch(N)
+ {
+ case 6:
+ return bigint_comba_mul6(z, x, y);
+ case 8:
+ return bigint_comba_mul8(z, x, y);
+ case 9:
+ return bigint_comba_mul9(z, x, y);
+ case 16:
+ return bigint_comba_mul16(z, x, y);
+ case 24:
+ return bigint_comba_mul24(z, x, y);
+ default:
+ return basecase_mul(z, 2*N, x, N, y, N);
+ }
+ }
+
+ const size_t N2 = N / 2;
+
+ const word* x0 = x;
+ const word* x1 = x + N2;
+ const word* y0 = y;
+ const word* y1 = y + N2;
+ word* z0 = z;
+ word* z1 = z + N;
+
+ word* ws0 = workspace;
+ word* ws1 = workspace + N;
+
+ clear_mem(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.
+ *
+ * However we ignore the result of the comparisons and always perform the
+ * subtractions and recursively multiply to avoid the timing channel.
+ */
+
+ // First compute (X_lo - X_hi)*(Y_hi - Y_lo)
+ const auto cmp0 = bigint_sub_abs(z0, x0, x1, N2, workspace);
+ const auto cmp1 = bigint_sub_abs(z1, y1, y0, N2, workspace);
+ const auto neg_mask = ~(cmp0 ^ cmp1);
+
+ karatsuba_mul(ws0, z0, z1, N2, ws1);
+
+ // Compute X_lo * Y_lo
+ karatsuba_mul(z0, x0, y0, N2, ws1);
+
+ // 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);
+
+ z_carry += bigint_add2_nc(z + N + N2, N2, &ws_carry, 1);
+ bigint_add2_nc(z + N + N2, N2, &z_carry, 1);
+
+ clear_mem(workspace + N, N2);
+
+ bigint_cnd_add_or_sub(neg_mask, 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)
+ {
+ switch(N)
+ {
+ case 6:
+ return bigint_comba_sqr6(z, x);
+ case 8:
+ return bigint_comba_sqr8(z, x);
+ case 9:
+ return bigint_comba_sqr9(z, x);
+ case 16:
+ return bigint_comba_sqr16(z, x);
+ case 24:
+ return bigint_comba_sqr24(z, x);
+ default:
+ return basecase_sqr(z, 2*N, x, N);
+ }
+ }
+
+ const size_t N2 = N / 2;
+
+ const word* x0 = x;
+ const word* x1 = x + N2;
+ word* z0 = z;
+ word* z1 = z + N;
+
+ word* ws0 = workspace;
+ word* ws1 = workspace + N;
+
+ clear_mem(workspace, 2*N);
+
+ // See comment in karatsuba_mul
+ bigint_sub_abs(z0, x0, x1, N2, workspace);
+ karatsuba_sqr(ws0, z0, N2, ws1);
+
+ 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);
+
+ z_carry += bigint_add2_nc(z + N + N2, N2, &ws_carry, 1);
+ bigint_add2_nc(z + N + N2, N2, &z_carry, 1);
+
+ /*
+ * This is only actually required if cmp (result of bigint_sub_abs) is != 0,
+ * however if cmp==0 then ws0[0:N] == 0 and avoiding the jump hides a
+ * timing channel.
+ */
+ bigint_sub2(z + N2, 2*N-N2, ws0, N);
+ }
+
+/*
+* Pick a good size for the Karatsuba multiply
+*/
+size_t karatsuba_size(size_t z_size,
+ size_t x_size, size_t x_sw,
+ size_t y_size, size_t y_sw)
+ {
+ if(x_sw > x_size || x_sw > y_size || y_sw > x_size || y_sw > y_size)
+ return 0;
+
+ if(((x_size == x_sw) && (x_size % 2)) ||
+ ((y_size == y_sw) && (y_size % 2)))
+ return 0;
+
+ const size_t start = (x_sw > y_sw) ? x_sw : y_sw;
+ const size_t end = (x_size < y_size) ? x_size : y_size;
+
+ if(start == end)
+ {
+ if(start % 2)
+ return 0;
+ return start;
+ }
+
+ for(size_t j = start; j <= end; ++j)
+ {
+ if(j % 2)
+ continue;
+
+ if(2*j > z_size)
+ return 0;
+
+ if(x_sw <= j && j <= x_size && y_sw <= j && j <= y_size)
+ {
+ if(j % 4 == 2 &&
+ (j+2) <= x_size && (j+2) <= y_size && 2*(j+2) <= z_size)
+ return j+2;
+ return j;
+ }
+ }
+
+ return 0;
+ }
+
+/*
+* Pick a good size for the Karatsuba squaring
+*/
+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)
+ return 0;
+ return x_sw;
+ }
+
+ for(size_t j = x_sw; j <= x_size; ++j)
+ {
+ if(j % 2)
+ continue;
+
+ if(2*j > z_size)
+ return 0;
+
+ if(j % 4 == 2 && (j+2) <= x_size && 2*(j+2) <= z_size)
+ return j+2;
+ return j;
+ }
+
+ return 0;
+ }
+
+template<size_t SZ>
+inline bool sized_for_comba_mul(size_t x_sw, size_t x_size,
+ size_t y_sw, size_t y_size,
+ size_t z_size)
+ {
+ return (x_sw <= SZ && x_size >= SZ &&
+ y_sw <= SZ && y_size >= SZ &&
+ z_size >= 2*SZ);
+ }
+
+template<size_t SZ>
+inline bool sized_for_comba_sqr(size_t x_sw, size_t x_size,
+ size_t z_size)
+ {
+ return (x_sw <= SZ && x_size >= SZ && z_size >= 2*SZ);
+ }
+
+}
+
+void bigint_mul(word z[], size_t z_size,
+ const word x[], size_t x_size, size_t x_sw,
+ const word y[], size_t y_size, size_t y_sw,
+ word workspace[], size_t ws_size)
+ {
+ clear_mem(z, z_size);
+
+ if(x_sw == 1)
+ {
+ bigint_linmul3(z, y, y_sw, x[0]);
+ }
+ else if(y_sw == 1)
+ {
+ bigint_linmul3(z, x, x_sw, y[0]);
+ }
+ else if(sized_for_comba_mul<4>(x_sw, x_size, y_sw, y_size, z_size))
+ {
+ bigint_comba_mul4(z, x, y);
+ }
+ else if(sized_for_comba_mul<6>(x_sw, x_size, y_sw, y_size, z_size))
+ {
+ bigint_comba_mul6(z, x, y);
+ }
+ else if(sized_for_comba_mul<8>(x_sw, x_size, y_sw, y_size, z_size))
+ {
+ bigint_comba_mul8(z, x, y);
+ }
+ else if(sized_for_comba_mul<9>(x_sw, x_size, y_sw, y_size, z_size))
+ {
+ bigint_comba_mul9(z, x, y);
+ }
+ else if(sized_for_comba_mul<16>(x_sw, x_size, y_sw, y_size, z_size))
+ {
+ 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)
+ {
+ 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)
+ karatsuba_mul(z, x, y, N, workspace);
+ else
+ basecase_mul(z, z_size, x, x_sw, y, y_sw);
+ }
+ }
+
+/*
+* 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);
+
+ BOTAN_ASSERT(z_size/2 >= x_sw, "Output size is sufficient");
+
+ if(x_sw == 1)
+ {
+ bigint_linmul3(z, x, x_sw, x[0]);
+ }
+ else if(sized_for_comba_sqr<4>(x_sw, x_size, z_size))
+ {
+ bigint_comba_sqr4(z, x);
+ }
+ else if(sized_for_comba_sqr<6>(x_sw, x_size, z_size))
+ {
+ bigint_comba_sqr6(z, x);
+ }
+ else if(sized_for_comba_sqr<8>(x_sw, x_size, z_size))
+ {
+ bigint_comba_sqr8(z, x);
+ }
+ else if(sized_for_comba_sqr<9>(x_sw, x_size, z_size))
+ {
+ bigint_comba_sqr9(z, x);
+ }
+ else if(sized_for_comba_sqr<16>(x_sw, x_size, z_size))
+ {
+ 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)
+ {
+ 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)
+ karatsuba_sqr(z, x, N, workspace);
+ else
+ basecase_sqr(z, z_size, x, x_sw);
+ }
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/math/mp/mp_madd.h b/comm/third_party/botan/src/lib/math/mp/mp_madd.h
new file mode 100644
index 0000000000..531d6e6634
--- /dev/null
+++ b/comm/third_party/botan/src/lib/math/mp/mp_madd.h
@@ -0,0 +1,146 @@
+/*
+* Lowest Level MPI Algorithms
+* (C) 1999-2008,2013 Jack Lloyd
+* 2006 Luca Piccarreta
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_MP_WORD_MULADD_H_
+#define BOTAN_MP_WORD_MULADD_H_
+
+#include <botan/types.h>
+#include <botan/mul128.h>
+
+namespace Botan {
+
+#if (BOTAN_MP_WORD_BITS == 32)
+ typedef uint64_t dword;
+ #define BOTAN_HAS_MP_DWORD
+
+#elif (BOTAN_MP_WORD_BITS == 64)
+ #if defined(BOTAN_TARGET_HAS_NATIVE_UINT128)
+ typedef uint128_t dword;
+ #define BOTAN_HAS_MP_DWORD
+ #else
+ // No native 128 bit integer type; use mul64x64_128 instead
+ #endif
+
+#else
+ #error BOTAN_MP_WORD_BITS must be 32 or 64
+#endif
+
+#if defined(BOTAN_USE_GCC_INLINE_ASM)
+
+ #if defined(BOTAN_TARGET_ARCH_IS_X86_32) && (BOTAN_MP_WORD_BITS == 32)
+ #define BOTAN_MP_USE_X86_32_ASM
+ #elif defined(BOTAN_TARGET_ARCH_IS_X86_64) && (BOTAN_MP_WORD_BITS == 64)
+ #define BOTAN_MP_USE_X86_64_ASM
+ #endif
+
+#endif
+
+/*
+* Word Multiply/Add
+*/
+inline word word_madd2(word a, word b, word* c)
+ {
+#if defined(BOTAN_MP_USE_X86_32_ASM)
+ asm(R"(
+ mull %[b]
+ addl %[c],%[a]
+ adcl $0,%[carry]
+ )"
+ : [a]"=a"(a), [b]"=rm"(b), [carry]"=&d"(*c)
+ : "0"(a), "1"(b), [c]"g"(*c) : "cc");
+
+ return a;
+
+#elif defined(BOTAN_MP_USE_X86_64_ASM)
+ asm(R"(
+ mulq %[b]
+ addq %[c],%[a]
+ adcq $0,%[carry]
+ )"
+ : [a]"=a"(a), [b]"=rm"(b), [carry]"=&d"(*c)
+ : "0"(a), "1"(b), [c]"g"(*c) : "cc");
+
+ return a;
+
+#elif defined(BOTAN_HAS_MP_DWORD)
+ const dword s = static_cast<dword>(a) * b + *c;
+ *c = static_cast<word>(s >> BOTAN_MP_WORD_BITS);
+ return static_cast<word>(s);
+#else
+ static_assert(BOTAN_MP_WORD_BITS == 64, "Unexpected word size");
+
+ word hi = 0, lo = 0;
+
+ mul64x64_128(a, b, &lo, &hi);
+
+ lo += *c;
+ hi += (lo < *c); // carry?
+
+ *c = hi;
+ return lo;
+#endif
+ }
+
+/*
+* Word Multiply/Add
+*/
+inline word word_madd3(word a, word b, word c, word* d)
+ {
+#if defined(BOTAN_MP_USE_X86_32_ASM)
+ asm(R"(
+ mull %[b]
+
+ addl %[c],%[a]
+ adcl $0,%[carry]
+
+ addl %[d],%[a]
+ adcl $0,%[carry]
+ )"
+ : [a]"=a"(a), [b]"=rm"(b), [carry]"=&d"(*d)
+ : "0"(a), "1"(b), [c]"g"(c), [d]"g"(*d) : "cc");
+
+ return a;
+
+#elif defined(BOTAN_MP_USE_X86_64_ASM)
+ asm(R"(
+ mulq %[b]
+ addq %[c],%[a]
+ adcq $0,%[carry]
+ addq %[d],%[a]
+ adcq $0,%[carry]
+ )"
+ : [a]"=a"(a), [b]"=rm"(b), [carry]"=&d"(*d)
+ : "0"(a), "1"(b), [c]"g"(c), [d]"g"(*d) : "cc");
+
+ return a;
+
+#elif defined(BOTAN_HAS_MP_DWORD)
+ const dword s = static_cast<dword>(a) * b + c + *d;
+ *d = static_cast<word>(s >> BOTAN_MP_WORD_BITS);
+ return static_cast<word>(s);
+#else
+ static_assert(BOTAN_MP_WORD_BITS == 64, "Unexpected word size");
+
+ word hi = 0, lo = 0;
+
+ mul64x64_128(a, b, &lo, &hi);
+
+ lo += c;
+ hi += (lo < c); // carry?
+
+ lo += *d;
+ hi += (lo < *d); // carry?
+
+ *d = hi;
+ return lo;
+#endif
+ }
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/math/mp/mp_monty.cpp b/comm/third_party/botan/src/lib/math/mp/mp_monty.cpp
new file mode 100644
index 0000000000..433d3ff358
--- /dev/null
+++ b/comm/third_party/botan/src/lib/math/mp/mp_monty.cpp
@@ -0,0 +1,133 @@
+/*
+* Montgomery Reduction
+* (C) 1999-2011 Jack Lloyd
+* 2006 Luca Piccarreta
+* 2016 Matthias Gierlings
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/mp_core.h>
+#include <botan/internal/mp_monty.h>
+#include <botan/internal/mp_madd.h>
+#include <botan/internal/mp_asmi.h>
+#include <botan/internal/ct_utils.h>
+#include <botan/mem_ops.h>
+#include <botan/exceptn.h>
+
+namespace Botan {
+
+namespace {
+
+/*
+* Montgomery reduction - product scanning form
+*
+* https://www.iacr.org/archive/ches2005/006.pdf
+* 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[])
+ {
+ word w2 = 0, w1 = 0, w0 = 0;
+
+ w0 = z[0];
+
+ ws[0] = w0 * p_dash;
+
+ word3_muladd(&w2, &w1, &w0, ws[0], p[0]);
+
+ w0 = w1;
+ w1 = w2;
+ w2 = 0;
+
+ for(size_t i = 1; i != p_size; ++i)
+ {
+ for(size_t j = 0; j < i; ++j)
+ {
+ word3_muladd(&w2, &w1, &w0, ws[j], p[i-j]);
+ }
+
+ word3_add(&w2, &w1, &w0, z[i]);
+
+ ws[i] = w0 * p_dash;
+
+ word3_muladd(&w2, &w1, &w0, ws[i], p[0]);
+
+ w0 = w1;
+ w1 = w2;
+ w2 = 0;
+ }
+
+ for(size_t i = 0; i != p_size; ++i)
+ {
+ for(size_t j = i + 1; j != p_size; ++j)
+ {
+ word3_muladd(&w2, &w1, &w0, ws[j], p[p_size + i-j]);
+ }
+
+ word3_add(&w2, &w1, &w0, z[p_size+i]);
+
+ ws[i] = w0;
+ w0 = w1;
+ w1 = w2;
+ w2 = 0;
+ }
+
+ word3_add(&w2, &w1, &w0, z[z_size-1]);
+
+ ws[p_size] = w0;
+ ws[p_size+1] = w1;
+
+ /*
+ * The result might need to be reduced mod p. To avoid a timing
+ * channel, always perform the subtraction. If in the compution
+ * of x - p a borrow is required then x was already < p.
+ *
+ * x starts at ws[0] and is p_size+1 bytes long.
+ * x - p starts at ws[p_size+1] and is also p_size+1 bytes log
+ *
+ * Select which address to copy from indexing off of the final
+ * borrow.
+ */
+
+ // word borrow = bigint_sub3(ws + p_size + 1, ws, p_size + 1, p, p_size);
+ word borrow = 0;
+ for(size_t i = 0; i != p_size; ++i)
+ ws[p_size + 1 + i] = word_sub(ws[i], p[i], &borrow);
+ ws[2*p_size+1] = word_sub(ws[p_size], 0, &borrow);
+
+ BOTAN_DEBUG_ASSERT(borrow == 0 || borrow == 1);
+
+ CT::conditional_copy_mem(borrow, z, ws, ws + (p_size + 1), (p_size + 1));
+ clear_mem(z + p_size, z_size - p_size - 2);
+ }
+
+}
+
+void bigint_monty_redc(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+1);
+
+ BOTAN_ARG_CHECK(ws_size >= z_size, "workspace too small");
+
+ if(p_size == 4)
+ bigint_monty_redc_4(z, p, p_dash, ws);
+ else if(p_size == 6)
+ bigint_monty_redc_6(z, p, p_dash, ws);
+ else if(p_size == 8)
+ bigint_monty_redc_8(z, p, p_dash, ws);
+ else if(p_size == 16)
+ bigint_monty_redc_16(z, p, p_dash, ws);
+ else if(p_size == 24)
+ bigint_monty_redc_24(z, p, p_dash, ws);
+ else if(p_size == 32)
+ bigint_monty_redc_32(z, p, p_dash, ws);
+ else
+ bigint_monty_redc_generic(z, z_size, p, p_size, p_dash, ws);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/math/mp/mp_monty.h b/comm/third_party/botan/src/lib/math/mp/mp_monty.h
new file mode 100644
index 0000000000..7462272d5c
--- /dev/null
+++ b/comm/third_party/botan/src/lib/math/mp/mp_monty.h
@@ -0,0 +1,31 @@
+/*
+* (C) 2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_MP_MONTY_H_
+#define BOTAN_MP_MONTY_H_
+
+#include <botan/types.h>
+
+namespace Botan {
+
+/*
+* Each of these functions makes the following assumptions:
+*
+* z_size >= 2*(p_size + 1)
+* ws_size >= z_size
+*/
+
+void bigint_monty_redc_4(word z[], const word p[], word p_dash, word ws[]);
+void bigint_monty_redc_6(word z[], const word p[], word p_dash, word ws[]);
+void bigint_monty_redc_8(word z[], const word p[], word p_dash, word ws[]);
+void bigint_monty_redc_16(word z[], const word p[], word p_dash, word ws[]);
+void bigint_monty_redc_24(word z[], const word p[], word p_dash, word ws[]);
+void bigint_monty_redc_32(word z[], const word p[], word p_dash, word ws[]);
+
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/math/mp/mp_monty_n.cpp b/comm/third_party/botan/src/lib/math/mp/mp_monty_n.cpp
new file mode 100644
index 0000000000..0331d4a073
--- /dev/null
+++ b/comm/third_party/botan/src/lib/math/mp/mp_monty_n.cpp
@@ -0,0 +1,2614 @@
+/*
+* This file was automatically generated by ./src/scripts/monty.py on 2018-06-11
+* All manual changes will be lost. Edit the script instead.
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/mp_monty.h>
+#include <botan/internal/mp_core.h>
+#include <botan/internal/mp_asmi.h>
+#include <botan/internal/ct_utils.h>
+
+namespace Botan {
+
+void bigint_monty_redc_4(word z[], const word p[4], word p_dash, word ws[])
+ {
+ word w2 = 0, w1 = 0, w0 = 0;
+ w0 = z[0];
+ ws[0] = w0 * p_dash;
+ word3_muladd(&w2, &w1, &w0, ws[0], p[0]);
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[0], p[1]);
+ word3_add(&w2, &w1, &w0, z[1]);
+ ws[1] = w0 * p_dash;
+ word3_muladd(&w2, &w1, &w0, ws[1], p[0]);
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[0], p[2]);
+ word3_muladd(&w2, &w1, &w0, ws[1], p[1]);
+ word3_add(&w2, &w1, &w0, z[2]);
+ ws[2] = w0 * p_dash;
+ word3_muladd(&w2, &w1, &w0, ws[2], p[0]);
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[0], p[3]);
+ word3_muladd(&w2, &w1, &w0, ws[1], p[2]);
+ word3_muladd(&w2, &w1, &w0, ws[2], p[1]);
+ word3_add(&w2, &w1, &w0, z[3]);
+ ws[3] = w0 * p_dash;
+ word3_muladd(&w2, &w1, &w0, ws[3], p[0]);
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[1], p[3]);
+ word3_muladd(&w2, &w1, &w0, ws[2], p[2]);
+ word3_muladd(&w2, &w1, &w0, ws[3], p[1]);
+ word3_add(&w2, &w1, &w0, z[4]);
+ ws[0] = w0;
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[2], p[3]);
+ word3_muladd(&w2, &w1, &w0, ws[3], p[2]);
+ word3_add(&w2, &w1, &w0, z[5]);
+ ws[1] = w0;
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[3], p[3]);
+ word3_add(&w2, &w1, &w0, z[6]);
+ ws[2] = w0;
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_add(&w2, &w1, &w0, z[7]);
+ ws[3] = w0;
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_add(&w2, &w1, &w0, z[9]);
+ ws[4] = w0;
+ ws[5] = w1;
+ word borrow = 0;
+ ws[5] = word_sub(ws[0], p[0], &borrow);
+ ws[6] = word_sub(ws[1], p[1], &borrow);
+ ws[7] = word_sub(ws[2], p[2], &borrow);
+ ws[8] = word_sub(ws[3], p[3], &borrow);
+ ws[9] = word_sub(ws[4], 0, &borrow);
+ CT::conditional_copy_mem(borrow, z, ws, ws + 5, 5);
+ clear_mem(z + 4, 2*(4+1) - 4);
+ }
+
+void bigint_monty_redc_6(word z[], const word p[6], word p_dash, word ws[])
+ {
+ word w2 = 0, w1 = 0, w0 = 0;
+ w0 = z[0];
+ ws[0] = w0 * p_dash;
+ word3_muladd(&w2, &w1, &w0, ws[0], p[0]);
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[0], p[1]);
+ word3_add(&w2, &w1, &w0, z[1]);
+ ws[1] = w0 * p_dash;
+ word3_muladd(&w2, &w1, &w0, ws[1], p[0]);
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[0], p[2]);
+ word3_muladd(&w2, &w1, &w0, ws[1], p[1]);
+ word3_add(&w2, &w1, &w0, z[2]);
+ ws[2] = w0 * p_dash;
+ word3_muladd(&w2, &w1, &w0, ws[2], p[0]);
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[0], p[3]);
+ word3_muladd(&w2, &w1, &w0, ws[1], p[2]);
+ word3_muladd(&w2, &w1, &w0, ws[2], p[1]);
+ word3_add(&w2, &w1, &w0, z[3]);
+ ws[3] = w0 * p_dash;
+ word3_muladd(&w2, &w1, &w0, ws[3], p[0]);
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[0], p[4]);
+ word3_muladd(&w2, &w1, &w0, ws[1], p[3]);
+ word3_muladd(&w2, &w1, &w0, ws[2], p[2]);
+ word3_muladd(&w2, &w1, &w0, ws[3], p[1]);
+ word3_add(&w2, &w1, &w0, z[4]);
+ ws[4] = w0 * p_dash;
+ word3_muladd(&w2, &w1, &w0, ws[4], p[0]);
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[0], p[5]);
+ word3_muladd(&w2, &w1, &w0, ws[1], p[4]);
+ word3_muladd(&w2, &w1, &w0, ws[2], p[3]);
+ word3_muladd(&w2, &w1, &w0, ws[3], p[2]);
+ word3_muladd(&w2, &w1, &w0, ws[4], p[1]);
+ word3_add(&w2, &w1, &w0, z[5]);
+ ws[5] = w0 * p_dash;
+ word3_muladd(&w2, &w1, &w0, ws[5], p[0]);
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[1], p[5]);
+ word3_muladd(&w2, &w1, &w0, ws[2], p[4]);
+ word3_muladd(&w2, &w1, &w0, ws[3], p[3]);
+ word3_muladd(&w2, &w1, &w0, ws[4], p[2]);
+ word3_muladd(&w2, &w1, &w0, ws[5], p[1]);
+ word3_add(&w2, &w1, &w0, z[6]);
+ ws[0] = w0;
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[2], p[5]);
+ word3_muladd(&w2, &w1, &w0, ws[3], p[4]);
+ word3_muladd(&w2, &w1, &w0, ws[4], p[3]);
+ word3_muladd(&w2, &w1, &w0, ws[5], p[2]);
+ word3_add(&w2, &w1, &w0, z[7]);
+ ws[1] = w0;
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[3], p[5]);
+ word3_muladd(&w2, &w1, &w0, ws[4], p[4]);
+ word3_muladd(&w2, &w1, &w0, ws[5], p[3]);
+ word3_add(&w2, &w1, &w0, z[8]);
+ ws[2] = w0;
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[4], p[5]);
+ word3_muladd(&w2, &w1, &w0, ws[5], p[4]);
+ word3_add(&w2, &w1, &w0, z[9]);
+ ws[3] = w0;
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[5], p[5]);
+ word3_add(&w2, &w1, &w0, z[10]);
+ ws[4] = w0;
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_add(&w2, &w1, &w0, z[11]);
+ ws[5] = w0;
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_add(&w2, &w1, &w0, z[13]);
+ ws[6] = w0;
+ ws[7] = w1;
+ word borrow = 0;
+ ws[7] = word_sub(ws[0], p[0], &borrow);
+ ws[8] = word_sub(ws[1], p[1], &borrow);
+ ws[9] = word_sub(ws[2], p[2], &borrow);
+ ws[10] = word_sub(ws[3], p[3], &borrow);
+ ws[11] = word_sub(ws[4], p[4], &borrow);
+ ws[12] = word_sub(ws[5], p[5], &borrow);
+ ws[13] = word_sub(ws[6], 0, &borrow);
+ CT::conditional_copy_mem(borrow, z, ws, ws + 7, 7);
+ clear_mem(z + 6, 2*(6+1) - 6);
+ }
+
+void bigint_monty_redc_8(word z[], const word p[8], word p_dash, word ws[])
+ {
+ word w2 = 0, w1 = 0, w0 = 0;
+ w0 = z[0];
+ ws[0] = w0 * p_dash;
+ word3_muladd(&w2, &w1, &w0, ws[0], p[0]);
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[0], p[1]);
+ word3_add(&w2, &w1, &w0, z[1]);
+ ws[1] = w0 * p_dash;
+ word3_muladd(&w2, &w1, &w0, ws[1], p[0]);
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[0], p[2]);
+ word3_muladd(&w2, &w1, &w0, ws[1], p[1]);
+ word3_add(&w2, &w1, &w0, z[2]);
+ ws[2] = w0 * p_dash;
+ word3_muladd(&w2, &w1, &w0, ws[2], p[0]);
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[0], p[3]);
+ word3_muladd(&w2, &w1, &w0, ws[1], p[2]);
+ word3_muladd(&w2, &w1, &w0, ws[2], p[1]);
+ word3_add(&w2, &w1, &w0, z[3]);
+ ws[3] = w0 * p_dash;
+ word3_muladd(&w2, &w1, &w0, ws[3], p[0]);
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[0], p[4]);
+ word3_muladd(&w2, &w1, &w0, ws[1], p[3]);
+ word3_muladd(&w2, &w1, &w0, ws[2], p[2]);
+ word3_muladd(&w2, &w1, &w0, ws[3], p[1]);
+ word3_add(&w2, &w1, &w0, z[4]);
+ ws[4] = w0 * p_dash;
+ word3_muladd(&w2, &w1, &w0, ws[4], p[0]);
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[0], p[5]);
+ word3_muladd(&w2, &w1, &w0, ws[1], p[4]);
+ word3_muladd(&w2, &w1, &w0, ws[2], p[3]);
+ word3_muladd(&w2, &w1, &w0, ws[3], p[2]);
+ word3_muladd(&w2, &w1, &w0, ws[4], p[1]);
+ word3_add(&w2, &w1, &w0, z[5]);
+ ws[5] = w0 * p_dash;
+ word3_muladd(&w2, &w1, &w0, ws[5], p[0]);
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[0], p[6]);
+ word3_muladd(&w2, &w1, &w0, ws[1], p[5]);
+ word3_muladd(&w2, &w1, &w0, ws[2], p[4]);
+ word3_muladd(&w2, &w1, &w0, ws[3], p[3]);
+ word3_muladd(&w2, &w1, &w0, ws[4], p[2]);
+ word3_muladd(&w2, &w1, &w0, ws[5], p[1]);
+ word3_add(&w2, &w1, &w0, z[6]);
+ ws[6] = w0 * p_dash;
+ word3_muladd(&w2, &w1, &w0, ws[6], p[0]);
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[0], p[7]);
+ word3_muladd(&w2, &w1, &w0, ws[1], p[6]);
+ word3_muladd(&w2, &w1, &w0, ws[2], p[5]);
+ word3_muladd(&w2, &w1, &w0, ws[3], p[4]);
+ word3_muladd(&w2, &w1, &w0, ws[4], p[3]);
+ word3_muladd(&w2, &w1, &w0, ws[5], p[2]);
+ word3_muladd(&w2, &w1, &w0, ws[6], p[1]);
+ word3_add(&w2, &w1, &w0, z[7]);
+ ws[7] = w0 * p_dash;
+ word3_muladd(&w2, &w1, &w0, ws[7], p[0]);
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[1], p[7]);
+ word3_muladd(&w2, &w1, &w0, ws[2], p[6]);
+ word3_muladd(&w2, &w1, &w0, ws[3], p[5]);
+ word3_muladd(&w2, &w1, &w0, ws[4], p[4]);
+ word3_muladd(&w2, &w1, &w0, ws[5], p[3]);
+ word3_muladd(&w2, &w1, &w0, ws[6], p[2]);
+ word3_muladd(&w2, &w1, &w0, ws[7], p[1]);
+ word3_add(&w2, &w1, &w0, z[8]);
+ ws[0] = w0;
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[2], p[7]);
+ word3_muladd(&w2, &w1, &w0, ws[3], p[6]);
+ word3_muladd(&w2, &w1, &w0, ws[4], p[5]);
+ word3_muladd(&w2, &w1, &w0, ws[5], p[4]);
+ word3_muladd(&w2, &w1, &w0, ws[6], p[3]);
+ word3_muladd(&w2, &w1, &w0, ws[7], p[2]);
+ word3_add(&w2, &w1, &w0, z[9]);
+ ws[1] = w0;
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[3], p[7]);
+ word3_muladd(&w2, &w1, &w0, ws[4], p[6]);
+ word3_muladd(&w2, &w1, &w0, ws[5], p[5]);
+ word3_muladd(&w2, &w1, &w0, ws[6], p[4]);
+ word3_muladd(&w2, &w1, &w0, ws[7], p[3]);
+ word3_add(&w2, &w1, &w0, z[10]);
+ ws[2] = w0;
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[4], p[7]);
+ word3_muladd(&w2, &w1, &w0, ws[5], p[6]);
+ word3_muladd(&w2, &w1, &w0, ws[6], p[5]);
+ word3_muladd(&w2, &w1, &w0, ws[7], p[4]);
+ word3_add(&w2, &w1, &w0, z[11]);
+ ws[3] = w0;
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[5], p[7]);
+ word3_muladd(&w2, &w1, &w0, ws[6], p[6]);
+ word3_muladd(&w2, &w1, &w0, ws[7], p[5]);
+ word3_add(&w2, &w1, &w0, z[12]);
+ ws[4] = w0;
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[6], p[7]);
+ word3_muladd(&w2, &w1, &w0, ws[7], p[6]);
+ word3_add(&w2, &w1, &w0, z[13]);
+ ws[5] = w0;
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[7], p[7]);
+ word3_add(&w2, &w1, &w0, z[14]);
+ ws[6] = w0;
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_add(&w2, &w1, &w0, z[15]);
+ ws[7] = w0;
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_add(&w2, &w1, &w0, z[17]);
+ ws[8] = w0;
+ ws[9] = w1;
+ word borrow = 0;
+ ws[9] = word_sub(ws[0], p[0], &borrow);
+ ws[10] = word_sub(ws[1], p[1], &borrow);
+ ws[11] = word_sub(ws[2], p[2], &borrow);
+ ws[12] = word_sub(ws[3], p[3], &borrow);
+ ws[13] = word_sub(ws[4], p[4], &borrow);
+ ws[14] = word_sub(ws[5], p[5], &borrow);
+ ws[15] = word_sub(ws[6], p[6], &borrow);
+ ws[16] = word_sub(ws[7], p[7], &borrow);
+ ws[17] = word_sub(ws[8], 0, &borrow);
+ CT::conditional_copy_mem(borrow, z, ws, ws + 9, 9);
+ clear_mem(z + 8, 2*(8+1) - 8);
+ }
+
+void bigint_monty_redc_16(word z[], const word p[16], word p_dash, word ws[])
+ {
+ word w2 = 0, w1 = 0, w0 = 0;
+ w0 = z[0];
+ ws[0] = w0 * p_dash;
+ word3_muladd(&w2, &w1, &w0, ws[0], p[0]);
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[0], p[1]);
+ word3_add(&w2, &w1, &w0, z[1]);
+ ws[1] = w0 * p_dash;
+ word3_muladd(&w2, &w1, &w0, ws[1], p[0]);
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[0], p[2]);
+ word3_muladd(&w2, &w1, &w0, ws[1], p[1]);
+ word3_add(&w2, &w1, &w0, z[2]);
+ ws[2] = w0 * p_dash;
+ word3_muladd(&w2, &w1, &w0, ws[2], p[0]);
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[0], p[3]);
+ word3_muladd(&w2, &w1, &w0, ws[1], p[2]);
+ word3_muladd(&w2, &w1, &w0, ws[2], p[1]);
+ word3_add(&w2, &w1, &w0, z[3]);
+ ws[3] = w0 * p_dash;
+ word3_muladd(&w2, &w1, &w0, ws[3], p[0]);
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[0], p[4]);
+ word3_muladd(&w2, &w1, &w0, ws[1], p[3]);
+ word3_muladd(&w2, &w1, &w0, ws[2], p[2]);
+ word3_muladd(&w2, &w1, &w0, ws[3], p[1]);
+ word3_add(&w2, &w1, &w0, z[4]);
+ ws[4] = w0 * p_dash;
+ word3_muladd(&w2, &w1, &w0, ws[4], p[0]);
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[0], p[5]);
+ word3_muladd(&w2, &w1, &w0, ws[1], p[4]);
+ word3_muladd(&w2, &w1, &w0, ws[2], p[3]);
+ word3_muladd(&w2, &w1, &w0, ws[3], p[2]);
+ word3_muladd(&w2, &w1, &w0, ws[4], p[1]);
+ word3_add(&w2, &w1, &w0, z[5]);
+ ws[5] = w0 * p_dash;
+ word3_muladd(&w2, &w1, &w0, ws[5], p[0]);
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[0], p[6]);
+ word3_muladd(&w2, &w1, &w0, ws[1], p[5]);
+ word3_muladd(&w2, &w1, &w0, ws[2], p[4]);
+ word3_muladd(&w2, &w1, &w0, ws[3], p[3]);
+ word3_muladd(&w2, &w1, &w0, ws[4], p[2]);
+ word3_muladd(&w2, &w1, &w0, ws[5], p[1]);
+ word3_add(&w2, &w1, &w0, z[6]);
+ ws[6] = w0 * p_dash;
+ word3_muladd(&w2, &w1, &w0, ws[6], p[0]);
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[0], p[7]);
+ word3_muladd(&w2, &w1, &w0, ws[1], p[6]);
+ word3_muladd(&w2, &w1, &w0, ws[2], p[5]);
+ word3_muladd(&w2, &w1, &w0, ws[3], p[4]);
+ word3_muladd(&w2, &w1, &w0, ws[4], p[3]);
+ word3_muladd(&w2, &w1, &w0, ws[5], p[2]);
+ word3_muladd(&w2, &w1, &w0, ws[6], p[1]);
+ word3_add(&w2, &w1, &w0, z[7]);
+ ws[7] = w0 * p_dash;
+ word3_muladd(&w2, &w1, &w0, ws[7], p[0]);
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[0], p[8]);
+ word3_muladd(&w2, &w1, &w0, ws[1], p[7]);
+ word3_muladd(&w2, &w1, &w0, ws[2], p[6]);
+ word3_muladd(&w2, &w1, &w0, ws[3], p[5]);
+ word3_muladd(&w2, &w1, &w0, ws[4], p[4]);
+ word3_muladd(&w2, &w1, &w0, ws[5], p[3]);
+ word3_muladd(&w2, &w1, &w0, ws[6], p[2]);
+ word3_muladd(&w2, &w1, &w0, ws[7], p[1]);
+ word3_add(&w2, &w1, &w0, z[8]);
+ ws[8] = w0 * p_dash;
+ word3_muladd(&w2, &w1, &w0, ws[8], p[0]);
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[0], p[9]);
+ word3_muladd(&w2, &w1, &w0, ws[1], p[8]);
+ word3_muladd(&w2, &w1, &w0, ws[2], p[7]);
+ word3_muladd(&w2, &w1, &w0, ws[3], p[6]);
+ word3_muladd(&w2, &w1, &w0, ws[4], p[5]);
+ word3_muladd(&w2, &w1, &w0, ws[5], p[4]);
+ word3_muladd(&w2, &w1, &w0, ws[6], p[3]);
+ word3_muladd(&w2, &w1, &w0, ws[7], p[2]);
+ word3_muladd(&w2, &w1, &w0, ws[8], p[1]);
+ word3_add(&w2, &w1, &w0, z[9]);
+ ws[9] = w0 * p_dash;
+ word3_muladd(&w2, &w1, &w0, ws[9], p[0]);
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[0], p[10]);
+ word3_muladd(&w2, &w1, &w0, ws[1], p[9]);
+ word3_muladd(&w2, &w1, &w0, ws[2], p[8]);
+ word3_muladd(&w2, &w1, &w0, ws[3], p[7]);
+ word3_muladd(&w2, &w1, &w0, ws[4], p[6]);
+ word3_muladd(&w2, &w1, &w0, ws[5], p[5]);
+ word3_muladd(&w2, &w1, &w0, ws[6], p[4]);
+ word3_muladd(&w2, &w1, &w0, ws[7], p[3]);
+ word3_muladd(&w2, &w1, &w0, ws[8], p[2]);
+ word3_muladd(&w2, &w1, &w0, ws[9], p[1]);
+ word3_add(&w2, &w1, &w0, z[10]);
+ ws[10] = w0 * p_dash;
+ word3_muladd(&w2, &w1, &w0, ws[10], p[0]);
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[0], p[11]);
+ word3_muladd(&w2, &w1, &w0, ws[1], p[10]);
+ word3_muladd(&w2, &w1, &w0, ws[2], p[9]);
+ word3_muladd(&w2, &w1, &w0, ws[3], p[8]);
+ word3_muladd(&w2, &w1, &w0, ws[4], p[7]);
+ word3_muladd(&w2, &w1, &w0, ws[5], p[6]);
+ word3_muladd(&w2, &w1, &w0, ws[6], p[5]);
+ word3_muladd(&w2, &w1, &w0, ws[7], p[4]);
+ word3_muladd(&w2, &w1, &w0, ws[8], p[3]);
+ word3_muladd(&w2, &w1, &w0, ws[9], p[2]);
+ word3_muladd(&w2, &w1, &w0, ws[10], p[1]);
+ word3_add(&w2, &w1, &w0, z[11]);
+ ws[11] = w0 * p_dash;
+ word3_muladd(&w2, &w1, &w0, ws[11], p[0]);
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[0], p[12]);
+ word3_muladd(&w2, &w1, &w0, ws[1], p[11]);
+ word3_muladd(&w2, &w1, &w0, ws[2], p[10]);
+ word3_muladd(&w2, &w1, &w0, ws[3], p[9]);
+ word3_muladd(&w2, &w1, &w0, ws[4], p[8]);
+ word3_muladd(&w2, &w1, &w0, ws[5], p[7]);
+ word3_muladd(&w2, &w1, &w0, ws[6], p[6]);
+ word3_muladd(&w2, &w1, &w0, ws[7], p[5]);
+ word3_muladd(&w2, &w1, &w0, ws[8], p[4]);
+ word3_muladd(&w2, &w1, &w0, ws[9], p[3]);
+ word3_muladd(&w2, &w1, &w0, ws[10], p[2]);
+ word3_muladd(&w2, &w1, &w0, ws[11], p[1]);
+ word3_add(&w2, &w1, &w0, z[12]);
+ ws[12] = w0 * p_dash;
+ word3_muladd(&w2, &w1, &w0, ws[12], p[0]);
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[0], p[13]);
+ word3_muladd(&w2, &w1, &w0, ws[1], p[12]);
+ word3_muladd(&w2, &w1, &w0, ws[2], p[11]);
+ word3_muladd(&w2, &w1, &w0, ws[3], p[10]);
+ word3_muladd(&w2, &w1, &w0, ws[4], p[9]);
+ word3_muladd(&w2, &w1, &w0, ws[5], p[8]);
+ word3_muladd(&w2, &w1, &w0, ws[6], p[7]);
+ word3_muladd(&w2, &w1, &w0, ws[7], p[6]);
+ word3_muladd(&w2, &w1, &w0, ws[8], p[5]);
+ word3_muladd(&w2, &w1, &w0, ws[9], p[4]);
+ word3_muladd(&w2, &w1, &w0, ws[10], p[3]);
+ word3_muladd(&w2, &w1, &w0, ws[11], p[2]);
+ word3_muladd(&w2, &w1, &w0, ws[12], p[1]);
+ word3_add(&w2, &w1, &w0, z[13]);
+ ws[13] = w0 * p_dash;
+ word3_muladd(&w2, &w1, &w0, ws[13], p[0]);
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[0], p[14]);
+ word3_muladd(&w2, &w1, &w0, ws[1], p[13]);
+ word3_muladd(&w2, &w1, &w0, ws[2], p[12]);
+ word3_muladd(&w2, &w1, &w0, ws[3], p[11]);
+ word3_muladd(&w2, &w1, &w0, ws[4], p[10]);
+ word3_muladd(&w2, &w1, &w0, ws[5], p[9]);
+ word3_muladd(&w2, &w1, &w0, ws[6], p[8]);
+ word3_muladd(&w2, &w1, &w0, ws[7], p[7]);
+ word3_muladd(&w2, &w1, &w0, ws[8], p[6]);
+ word3_muladd(&w2, &w1, &w0, ws[9], p[5]);
+ word3_muladd(&w2, &w1, &w0, ws[10], p[4]);
+ word3_muladd(&w2, &w1, &w0, ws[11], p[3]);
+ word3_muladd(&w2, &w1, &w0, ws[12], p[2]);
+ word3_muladd(&w2, &w1, &w0, ws[13], p[1]);
+ word3_add(&w2, &w1, &w0, z[14]);
+ ws[14] = w0 * p_dash;
+ word3_muladd(&w2, &w1, &w0, ws[14], p[0]);
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[0], p[15]);
+ word3_muladd(&w2, &w1, &w0, ws[1], p[14]);
+ word3_muladd(&w2, &w1, &w0, ws[2], p[13]);
+ word3_muladd(&w2, &w1, &w0, ws[3], p[12]);
+ word3_muladd(&w2, &w1, &w0, ws[4], p[11]);
+ word3_muladd(&w2, &w1, &w0, ws[5], p[10]);
+ word3_muladd(&w2, &w1, &w0, ws[6], p[9]);
+ word3_muladd(&w2, &w1, &w0, ws[7], p[8]);
+ word3_muladd(&w2, &w1, &w0, ws[8], p[7]);
+ word3_muladd(&w2, &w1, &w0, ws[9], p[6]);
+ word3_muladd(&w2, &w1, &w0, ws[10], p[5]);
+ word3_muladd(&w2, &w1, &w0, ws[11], p[4]);
+ word3_muladd(&w2, &w1, &w0, ws[12], p[3]);
+ word3_muladd(&w2, &w1, &w0, ws[13], p[2]);
+ word3_muladd(&w2, &w1, &w0, ws[14], p[1]);
+ word3_add(&w2, &w1, &w0, z[15]);
+ ws[15] = w0 * p_dash;
+ word3_muladd(&w2, &w1, &w0, ws[15], p[0]);
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[1], p[15]);
+ word3_muladd(&w2, &w1, &w0, ws[2], p[14]);
+ word3_muladd(&w2, &w1, &w0, ws[3], p[13]);
+ word3_muladd(&w2, &w1, &w0, ws[4], p[12]);
+ word3_muladd(&w2, &w1, &w0, ws[5], p[11]);
+ word3_muladd(&w2, &w1, &w0, ws[6], p[10]);
+ word3_muladd(&w2, &w1, &w0, ws[7], p[9]);
+ word3_muladd(&w2, &w1, &w0, ws[8], p[8]);
+ word3_muladd(&w2, &w1, &w0, ws[9], p[7]);
+ word3_muladd(&w2, &w1, &w0, ws[10], p[6]);
+ word3_muladd(&w2, &w1, &w0, ws[11], p[5]);
+ word3_muladd(&w2, &w1, &w0, ws[12], p[4]);
+ word3_muladd(&w2, &w1, &w0, ws[13], p[3]);
+ word3_muladd(&w2, &w1, &w0, ws[14], p[2]);
+ word3_muladd(&w2, &w1, &w0, ws[15], p[1]);
+ word3_add(&w2, &w1, &w0, z[16]);
+ ws[0] = w0;
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[2], p[15]);
+ word3_muladd(&w2, &w1, &w0, ws[3], p[14]);
+ word3_muladd(&w2, &w1, &w0, ws[4], p[13]);
+ word3_muladd(&w2, &w1, &w0, ws[5], p[12]);
+ word3_muladd(&w2, &w1, &w0, ws[6], p[11]);
+ word3_muladd(&w2, &w1, &w0, ws[7], p[10]);
+ word3_muladd(&w2, &w1, &w0, ws[8], p[9]);
+ word3_muladd(&w2, &w1, &w0, ws[9], p[8]);
+ word3_muladd(&w2, &w1, &w0, ws[10], p[7]);
+ word3_muladd(&w2, &w1, &w0, ws[11], p[6]);
+ word3_muladd(&w2, &w1, &w0, ws[12], p[5]);
+ word3_muladd(&w2, &w1, &w0, ws[13], p[4]);
+ word3_muladd(&w2, &w1, &w0, ws[14], p[3]);
+ word3_muladd(&w2, &w1, &w0, ws[15], p[2]);
+ word3_add(&w2, &w1, &w0, z[17]);
+ ws[1] = w0;
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[3], p[15]);
+ word3_muladd(&w2, &w1, &w0, ws[4], p[14]);
+ word3_muladd(&w2, &w1, &w0, ws[5], p[13]);
+ word3_muladd(&w2, &w1, &w0, ws[6], p[12]);
+ word3_muladd(&w2, &w1, &w0, ws[7], p[11]);
+ word3_muladd(&w2, &w1, &w0, ws[8], p[10]);
+ word3_muladd(&w2, &w1, &w0, ws[9], p[9]);
+ word3_muladd(&w2, &w1, &w0, ws[10], p[8]);
+ word3_muladd(&w2, &w1, &w0, ws[11], p[7]);
+ word3_muladd(&w2, &w1, &w0, ws[12], p[6]);
+ word3_muladd(&w2, &w1, &w0, ws[13], p[5]);
+ word3_muladd(&w2, &w1, &w0, ws[14], p[4]);
+ word3_muladd(&w2, &w1, &w0, ws[15], p[3]);
+ word3_add(&w2, &w1, &w0, z[18]);
+ ws[2] = w0;
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[4], p[15]);
+ word3_muladd(&w2, &w1, &w0, ws[5], p[14]);
+ word3_muladd(&w2, &w1, &w0, ws[6], p[13]);
+ word3_muladd(&w2, &w1, &w0, ws[7], p[12]);
+ word3_muladd(&w2, &w1, &w0, ws[8], p[11]);
+ word3_muladd(&w2, &w1, &w0, ws[9], p[10]);
+ word3_muladd(&w2, &w1, &w0, ws[10], p[9]);
+ word3_muladd(&w2, &w1, &w0, ws[11], p[8]);
+ word3_muladd(&w2, &w1, &w0, ws[12], p[7]);
+ word3_muladd(&w2, &w1, &w0, ws[13], p[6]);
+ word3_muladd(&w2, &w1, &w0, ws[14], p[5]);
+ word3_muladd(&w2, &w1, &w0, ws[15], p[4]);
+ word3_add(&w2, &w1, &w0, z[19]);
+ ws[3] = w0;
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[5], p[15]);
+ word3_muladd(&w2, &w1, &w0, ws[6], p[14]);
+ word3_muladd(&w2, &w1, &w0, ws[7], p[13]);
+ word3_muladd(&w2, &w1, &w0, ws[8], p[12]);
+ word3_muladd(&w2, &w1, &w0, ws[9], p[11]);
+ word3_muladd(&w2, &w1, &w0, ws[10], p[10]);
+ word3_muladd(&w2, &w1, &w0, ws[11], p[9]);
+ word3_muladd(&w2, &w1, &w0, ws[12], p[8]);
+ word3_muladd(&w2, &w1, &w0, ws[13], p[7]);
+ word3_muladd(&w2, &w1, &w0, ws[14], p[6]);
+ word3_muladd(&w2, &w1, &w0, ws[15], p[5]);
+ word3_add(&w2, &w1, &w0, z[20]);
+ ws[4] = w0;
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[6], p[15]);
+ word3_muladd(&w2, &w1, &w0, ws[7], p[14]);
+ word3_muladd(&w2, &w1, &w0, ws[8], p[13]);
+ word3_muladd(&w2, &w1, &w0, ws[9], p[12]);
+ word3_muladd(&w2, &w1, &w0, ws[10], p[11]);
+ word3_muladd(&w2, &w1, &w0, ws[11], p[10]);
+ word3_muladd(&w2, &w1, &w0, ws[12], p[9]);
+ word3_muladd(&w2, &w1, &w0, ws[13], p[8]);
+ word3_muladd(&w2, &w1, &w0, ws[14], p[7]);
+ word3_muladd(&w2, &w1, &w0, ws[15], p[6]);
+ word3_add(&w2, &w1, &w0, z[21]);
+ ws[5] = w0;
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[7], p[15]);
+ word3_muladd(&w2, &w1, &w0, ws[8], p[14]);
+ word3_muladd(&w2, &w1, &w0, ws[9], p[13]);
+ word3_muladd(&w2, &w1, &w0, ws[10], p[12]);
+ word3_muladd(&w2, &w1, &w0, ws[11], p[11]);
+ word3_muladd(&w2, &w1, &w0, ws[12], p[10]);
+ word3_muladd(&w2, &w1, &w0, ws[13], p[9]);
+ word3_muladd(&w2, &w1, &w0, ws[14], p[8]);
+ word3_muladd(&w2, &w1, &w0, ws[15], p[7]);
+ word3_add(&w2, &w1, &w0, z[22]);
+ ws[6] = w0;
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[8], p[15]);
+ word3_muladd(&w2, &w1, &w0, ws[9], p[14]);
+ word3_muladd(&w2, &w1, &w0, ws[10], p[13]);
+ word3_muladd(&w2, &w1, &w0, ws[11], p[12]);
+ word3_muladd(&w2, &w1, &w0, ws[12], p[11]);
+ word3_muladd(&w2, &w1, &w0, ws[13], p[10]);
+ word3_muladd(&w2, &w1, &w0, ws[14], p[9]);
+ word3_muladd(&w2, &w1, &w0, ws[15], p[8]);
+ word3_add(&w2, &w1, &w0, z[23]);
+ ws[7] = w0;
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[9], p[15]);
+ word3_muladd(&w2, &w1, &w0, ws[10], p[14]);
+ word3_muladd(&w2, &w1, &w0, ws[11], p[13]);
+ word3_muladd(&w2, &w1, &w0, ws[12], p[12]);
+ word3_muladd(&w2, &w1, &w0, ws[13], p[11]);
+ word3_muladd(&w2, &w1, &w0, ws[14], p[10]);
+ word3_muladd(&w2, &w1, &w0, ws[15], p[9]);
+ word3_add(&w2, &w1, &w0, z[24]);
+ ws[8] = w0;
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[10], p[15]);
+ word3_muladd(&w2, &w1, &w0, ws[11], p[14]);
+ word3_muladd(&w2, &w1, &w0, ws[12], p[13]);
+ word3_muladd(&w2, &w1, &w0, ws[13], p[12]);
+ word3_muladd(&w2, &w1, &w0, ws[14], p[11]);
+ word3_muladd(&w2, &w1, &w0, ws[15], p[10]);
+ word3_add(&w2, &w1, &w0, z[25]);
+ ws[9] = w0;
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[11], p[15]);
+ word3_muladd(&w2, &w1, &w0, ws[12], p[14]);
+ word3_muladd(&w2, &w1, &w0, ws[13], p[13]);
+ word3_muladd(&w2, &w1, &w0, ws[14], p[12]);
+ word3_muladd(&w2, &w1, &w0, ws[15], p[11]);
+ word3_add(&w2, &w1, &w0, z[26]);
+ ws[10] = w0;
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[12], p[15]);
+ word3_muladd(&w2, &w1, &w0, ws[13], p[14]);
+ word3_muladd(&w2, &w1, &w0, ws[14], p[13]);
+ word3_muladd(&w2, &w1, &w0, ws[15], p[12]);
+ word3_add(&w2, &w1, &w0, z[27]);
+ ws[11] = w0;
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[13], p[15]);
+ word3_muladd(&w2, &w1, &w0, ws[14], p[14]);
+ word3_muladd(&w2, &w1, &w0, ws[15], p[13]);
+ word3_add(&w2, &w1, &w0, z[28]);
+ ws[12] = w0;
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[14], p[15]);
+ word3_muladd(&w2, &w1, &w0, ws[15], p[14]);
+ word3_add(&w2, &w1, &w0, z[29]);
+ ws[13] = w0;
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[15], p[15]);
+ word3_add(&w2, &w1, &w0, z[30]);
+ ws[14] = w0;
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_add(&w2, &w1, &w0, z[31]);
+ ws[15] = w0;
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_add(&w2, &w1, &w0, z[33]);
+ ws[16] = w0;
+ ws[17] = w1;
+ word borrow = bigint_sub3(ws + 16 + 1, ws, 16 + 1, p, 16);
+ CT::conditional_copy_mem(borrow, z, ws, ws + 17, 17);
+ clear_mem(z + 16, 2*(16+1) - 16);
+ }
+
+void bigint_monty_redc_24(word z[], const word p[24], word p_dash, word ws[])
+ {
+ word w2 = 0, w1 = 0, w0 = 0;
+ w0 = z[0];
+ ws[0] = w0 * p_dash;
+ word3_muladd(&w2, &w1, &w0, ws[0], p[0]);
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[0], p[1]);
+ word3_add(&w2, &w1, &w0, z[1]);
+ ws[1] = w0 * p_dash;
+ word3_muladd(&w2, &w1, &w0, ws[1], p[0]);
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[0], p[2]);
+ word3_muladd(&w2, &w1, &w0, ws[1], p[1]);
+ word3_add(&w2, &w1, &w0, z[2]);
+ ws[2] = w0 * p_dash;
+ word3_muladd(&w2, &w1, &w0, ws[2], p[0]);
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[0], p[3]);
+ word3_muladd(&w2, &w1, &w0, ws[1], p[2]);
+ word3_muladd(&w2, &w1, &w0, ws[2], p[1]);
+ word3_add(&w2, &w1, &w0, z[3]);
+ ws[3] = w0 * p_dash;
+ word3_muladd(&w2, &w1, &w0, ws[3], p[0]);
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[0], p[4]);
+ word3_muladd(&w2, &w1, &w0, ws[1], p[3]);
+ word3_muladd(&w2, &w1, &w0, ws[2], p[2]);
+ word3_muladd(&w2, &w1, &w0, ws[3], p[1]);
+ word3_add(&w2, &w1, &w0, z[4]);
+ ws[4] = w0 * p_dash;
+ word3_muladd(&w2, &w1, &w0, ws[4], p[0]);
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[0], p[5]);
+ word3_muladd(&w2, &w1, &w0, ws[1], p[4]);
+ word3_muladd(&w2, &w1, &w0, ws[2], p[3]);
+ word3_muladd(&w2, &w1, &w0, ws[3], p[2]);
+ word3_muladd(&w2, &w1, &w0, ws[4], p[1]);
+ word3_add(&w2, &w1, &w0, z[5]);
+ ws[5] = w0 * p_dash;
+ word3_muladd(&w2, &w1, &w0, ws[5], p[0]);
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[0], p[6]);
+ word3_muladd(&w2, &w1, &w0, ws[1], p[5]);
+ word3_muladd(&w2, &w1, &w0, ws[2], p[4]);
+ word3_muladd(&w2, &w1, &w0, ws[3], p[3]);
+ word3_muladd(&w2, &w1, &w0, ws[4], p[2]);
+ word3_muladd(&w2, &w1, &w0, ws[5], p[1]);
+ word3_add(&w2, &w1, &w0, z[6]);
+ ws[6] = w0 * p_dash;
+ word3_muladd(&w2, &w1, &w0, ws[6], p[0]);
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[0], p[7]);
+ word3_muladd(&w2, &w1, &w0, ws[1], p[6]);
+ word3_muladd(&w2, &w1, &w0, ws[2], p[5]);
+ word3_muladd(&w2, &w1, &w0, ws[3], p[4]);
+ word3_muladd(&w2, &w1, &w0, ws[4], p[3]);
+ word3_muladd(&w2, &w1, &w0, ws[5], p[2]);
+ word3_muladd(&w2, &w1, &w0, ws[6], p[1]);
+ word3_add(&w2, &w1, &w0, z[7]);
+ ws[7] = w0 * p_dash;
+ word3_muladd(&w2, &w1, &w0, ws[7], p[0]);
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[0], p[8]);
+ word3_muladd(&w2, &w1, &w0, ws[1], p[7]);
+ word3_muladd(&w2, &w1, &w0, ws[2], p[6]);
+ word3_muladd(&w2, &w1, &w0, ws[3], p[5]);
+ word3_muladd(&w2, &w1, &w0, ws[4], p[4]);
+ word3_muladd(&w2, &w1, &w0, ws[5], p[3]);
+ word3_muladd(&w2, &w1, &w0, ws[6], p[2]);
+ word3_muladd(&w2, &w1, &w0, ws[7], p[1]);
+ word3_add(&w2, &w1, &w0, z[8]);
+ ws[8] = w0 * p_dash;
+ word3_muladd(&w2, &w1, &w0, ws[8], p[0]);
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[0], p[9]);
+ word3_muladd(&w2, &w1, &w0, ws[1], p[8]);
+ word3_muladd(&w2, &w1, &w0, ws[2], p[7]);
+ word3_muladd(&w2, &w1, &w0, ws[3], p[6]);
+ word3_muladd(&w2, &w1, &w0, ws[4], p[5]);
+ word3_muladd(&w2, &w1, &w0, ws[5], p[4]);
+ word3_muladd(&w2, &w1, &w0, ws[6], p[3]);
+ word3_muladd(&w2, &w1, &w0, ws[7], p[2]);
+ word3_muladd(&w2, &w1, &w0, ws[8], p[1]);
+ word3_add(&w2, &w1, &w0, z[9]);
+ ws[9] = w0 * p_dash;
+ word3_muladd(&w2, &w1, &w0, ws[9], p[0]);
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[0], p[10]);
+ word3_muladd(&w2, &w1, &w0, ws[1], p[9]);
+ word3_muladd(&w2, &w1, &w0, ws[2], p[8]);
+ word3_muladd(&w2, &w1, &w0, ws[3], p[7]);
+ word3_muladd(&w2, &w1, &w0, ws[4], p[6]);
+ word3_muladd(&w2, &w1, &w0, ws[5], p[5]);
+ word3_muladd(&w2, &w1, &w0, ws[6], p[4]);
+ word3_muladd(&w2, &w1, &w0, ws[7], p[3]);
+ word3_muladd(&w2, &w1, &w0, ws[8], p[2]);
+ word3_muladd(&w2, &w1, &w0, ws[9], p[1]);
+ word3_add(&w2, &w1, &w0, z[10]);
+ ws[10] = w0 * p_dash;
+ word3_muladd(&w2, &w1, &w0, ws[10], p[0]);
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[0], p[11]);
+ word3_muladd(&w2, &w1, &w0, ws[1], p[10]);
+ word3_muladd(&w2, &w1, &w0, ws[2], p[9]);
+ word3_muladd(&w2, &w1, &w0, ws[3], p[8]);
+ word3_muladd(&w2, &w1, &w0, ws[4], p[7]);
+ word3_muladd(&w2, &w1, &w0, ws[5], p[6]);
+ word3_muladd(&w2, &w1, &w0, ws[6], p[5]);
+ word3_muladd(&w2, &w1, &w0, ws[7], p[4]);
+ word3_muladd(&w2, &w1, &w0, ws[8], p[3]);
+ word3_muladd(&w2, &w1, &w0, ws[9], p[2]);
+ word3_muladd(&w2, &w1, &w0, ws[10], p[1]);
+ word3_add(&w2, &w1, &w0, z[11]);
+ ws[11] = w0 * p_dash;
+ word3_muladd(&w2, &w1, &w0, ws[11], p[0]);
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[0], p[12]);
+ word3_muladd(&w2, &w1, &w0, ws[1], p[11]);
+ word3_muladd(&w2, &w1, &w0, ws[2], p[10]);
+ word3_muladd(&w2, &w1, &w0, ws[3], p[9]);
+ word3_muladd(&w2, &w1, &w0, ws[4], p[8]);
+ word3_muladd(&w2, &w1, &w0, ws[5], p[7]);
+ word3_muladd(&w2, &w1, &w0, ws[6], p[6]);
+ word3_muladd(&w2, &w1, &w0, ws[7], p[5]);
+ word3_muladd(&w2, &w1, &w0, ws[8], p[4]);
+ word3_muladd(&w2, &w1, &w0, ws[9], p[3]);
+ word3_muladd(&w2, &w1, &w0, ws[10], p[2]);
+ word3_muladd(&w2, &w1, &w0, ws[11], p[1]);
+ word3_add(&w2, &w1, &w0, z[12]);
+ ws[12] = w0 * p_dash;
+ word3_muladd(&w2, &w1, &w0, ws[12], p[0]);
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[0], p[13]);
+ word3_muladd(&w2, &w1, &w0, ws[1], p[12]);
+ word3_muladd(&w2, &w1, &w0, ws[2], p[11]);
+ word3_muladd(&w2, &w1, &w0, ws[3], p[10]);
+ word3_muladd(&w2, &w1, &w0, ws[4], p[9]);
+ word3_muladd(&w2, &w1, &w0, ws[5], p[8]);
+ word3_muladd(&w2, &w1, &w0, ws[6], p[7]);
+ word3_muladd(&w2, &w1, &w0, ws[7], p[6]);
+ word3_muladd(&w2, &w1, &w0, ws[8], p[5]);
+ word3_muladd(&w2, &w1, &w0, ws[9], p[4]);
+ word3_muladd(&w2, &w1, &w0, ws[10], p[3]);
+ word3_muladd(&w2, &w1, &w0, ws[11], p[2]);
+ word3_muladd(&w2, &w1, &w0, ws[12], p[1]);
+ word3_add(&w2, &w1, &w0, z[13]);
+ ws[13] = w0 * p_dash;
+ word3_muladd(&w2, &w1, &w0, ws[13], p[0]);
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[0], p[14]);
+ word3_muladd(&w2, &w1, &w0, ws[1], p[13]);
+ word3_muladd(&w2, &w1, &w0, ws[2], p[12]);
+ word3_muladd(&w2, &w1, &w0, ws[3], p[11]);
+ word3_muladd(&w2, &w1, &w0, ws[4], p[10]);
+ word3_muladd(&w2, &w1, &w0, ws[5], p[9]);
+ word3_muladd(&w2, &w1, &w0, ws[6], p[8]);
+ word3_muladd(&w2, &w1, &w0, ws[7], p[7]);
+ word3_muladd(&w2, &w1, &w0, ws[8], p[6]);
+ word3_muladd(&w2, &w1, &w0, ws[9], p[5]);
+ word3_muladd(&w2, &w1, &w0, ws[10], p[4]);
+ word3_muladd(&w2, &w1, &w0, ws[11], p[3]);
+ word3_muladd(&w2, &w1, &w0, ws[12], p[2]);
+ word3_muladd(&w2, &w1, &w0, ws[13], p[1]);
+ word3_add(&w2, &w1, &w0, z[14]);
+ ws[14] = w0 * p_dash;
+ word3_muladd(&w2, &w1, &w0, ws[14], p[0]);
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[0], p[15]);
+ word3_muladd(&w2, &w1, &w0, ws[1], p[14]);
+ word3_muladd(&w2, &w1, &w0, ws[2], p[13]);
+ word3_muladd(&w2, &w1, &w0, ws[3], p[12]);
+ word3_muladd(&w2, &w1, &w0, ws[4], p[11]);
+ word3_muladd(&w2, &w1, &w0, ws[5], p[10]);
+ word3_muladd(&w2, &w1, &w0, ws[6], p[9]);
+ word3_muladd(&w2, &w1, &w0, ws[7], p[8]);
+ word3_muladd(&w2, &w1, &w0, ws[8], p[7]);
+ word3_muladd(&w2, &w1, &w0, ws[9], p[6]);
+ word3_muladd(&w2, &w1, &w0, ws[10], p[5]);
+ word3_muladd(&w2, &w1, &w0, ws[11], p[4]);
+ word3_muladd(&w2, &w1, &w0, ws[12], p[3]);
+ word3_muladd(&w2, &w1, &w0, ws[13], p[2]);
+ word3_muladd(&w2, &w1, &w0, ws[14], p[1]);
+ word3_add(&w2, &w1, &w0, z[15]);
+ ws[15] = w0 * p_dash;
+ word3_muladd(&w2, &w1, &w0, ws[15], p[0]);
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[0], p[16]);
+ word3_muladd(&w2, &w1, &w0, ws[1], p[15]);
+ word3_muladd(&w2, &w1, &w0, ws[2], p[14]);
+ word3_muladd(&w2, &w1, &w0, ws[3], p[13]);
+ word3_muladd(&w2, &w1, &w0, ws[4], p[12]);
+ word3_muladd(&w2, &w1, &w0, ws[5], p[11]);
+ word3_muladd(&w2, &w1, &w0, ws[6], p[10]);
+ word3_muladd(&w2, &w1, &w0, ws[7], p[9]);
+ word3_muladd(&w2, &w1, &w0, ws[8], p[8]);
+ word3_muladd(&w2, &w1, &w0, ws[9], p[7]);
+ word3_muladd(&w2, &w1, &w0, ws[10], p[6]);
+ word3_muladd(&w2, &w1, &w0, ws[11], p[5]);
+ word3_muladd(&w2, &w1, &w0, ws[12], p[4]);
+ word3_muladd(&w2, &w1, &w0, ws[13], p[3]);
+ word3_muladd(&w2, &w1, &w0, ws[14], p[2]);
+ word3_muladd(&w2, &w1, &w0, ws[15], p[1]);
+ word3_add(&w2, &w1, &w0, z[16]);
+ ws[16] = w0 * p_dash;
+ word3_muladd(&w2, &w1, &w0, ws[16], p[0]);
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[0], p[17]);
+ word3_muladd(&w2, &w1, &w0, ws[1], p[16]);
+ word3_muladd(&w2, &w1, &w0, ws[2], p[15]);
+ word3_muladd(&w2, &w1, &w0, ws[3], p[14]);
+ word3_muladd(&w2, &w1, &w0, ws[4], p[13]);
+ word3_muladd(&w2, &w1, &w0, ws[5], p[12]);
+ word3_muladd(&w2, &w1, &w0, ws[6], p[11]);
+ word3_muladd(&w2, &w1, &w0, ws[7], p[10]);
+ word3_muladd(&w2, &w1, &w0, ws[8], p[9]);
+ word3_muladd(&w2, &w1, &w0, ws[9], p[8]);
+ word3_muladd(&w2, &w1, &w0, ws[10], p[7]);
+ word3_muladd(&w2, &w1, &w0, ws[11], p[6]);
+ word3_muladd(&w2, &w1, &w0, ws[12], p[5]);
+ word3_muladd(&w2, &w1, &w0, ws[13], p[4]);
+ word3_muladd(&w2, &w1, &w0, ws[14], p[3]);
+ word3_muladd(&w2, &w1, &w0, ws[15], p[2]);
+ word3_muladd(&w2, &w1, &w0, ws[16], p[1]);
+ word3_add(&w2, &w1, &w0, z[17]);
+ ws[17] = w0 * p_dash;
+ word3_muladd(&w2, &w1, &w0, ws[17], p[0]);
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[0], p[18]);
+ word3_muladd(&w2, &w1, &w0, ws[1], p[17]);
+ word3_muladd(&w2, &w1, &w0, ws[2], p[16]);
+ word3_muladd(&w2, &w1, &w0, ws[3], p[15]);
+ word3_muladd(&w2, &w1, &w0, ws[4], p[14]);
+ word3_muladd(&w2, &w1, &w0, ws[5], p[13]);
+ word3_muladd(&w2, &w1, &w0, ws[6], p[12]);
+ word3_muladd(&w2, &w1, &w0, ws[7], p[11]);
+ word3_muladd(&w2, &w1, &w0, ws[8], p[10]);
+ word3_muladd(&w2, &w1, &w0, ws[9], p[9]);
+ word3_muladd(&w2, &w1, &w0, ws[10], p[8]);
+ word3_muladd(&w2, &w1, &w0, ws[11], p[7]);
+ word3_muladd(&w2, &w1, &w0, ws[12], p[6]);
+ word3_muladd(&w2, &w1, &w0, ws[13], p[5]);
+ word3_muladd(&w2, &w1, &w0, ws[14], p[4]);
+ word3_muladd(&w2, &w1, &w0, ws[15], p[3]);
+ word3_muladd(&w2, &w1, &w0, ws[16], p[2]);
+ word3_muladd(&w2, &w1, &w0, ws[17], p[1]);
+ word3_add(&w2, &w1, &w0, z[18]);
+ ws[18] = w0 * p_dash;
+ word3_muladd(&w2, &w1, &w0, ws[18], p[0]);
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[0], p[19]);
+ word3_muladd(&w2, &w1, &w0, ws[1], p[18]);
+ word3_muladd(&w2, &w1, &w0, ws[2], p[17]);
+ word3_muladd(&w2, &w1, &w0, ws[3], p[16]);
+ word3_muladd(&w2, &w1, &w0, ws[4], p[15]);
+ word3_muladd(&w2, &w1, &w0, ws[5], p[14]);
+ word3_muladd(&w2, &w1, &w0, ws[6], p[13]);
+ word3_muladd(&w2, &w1, &w0, ws[7], p[12]);
+ word3_muladd(&w2, &w1, &w0, ws[8], p[11]);
+ word3_muladd(&w2, &w1, &w0, ws[9], p[10]);
+ word3_muladd(&w2, &w1, &w0, ws[10], p[9]);
+ word3_muladd(&w2, &w1, &w0, ws[11], p[8]);
+ word3_muladd(&w2, &w1, &w0, ws[12], p[7]);
+ word3_muladd(&w2, &w1, &w0, ws[13], p[6]);
+ word3_muladd(&w2, &w1, &w0, ws[14], p[5]);
+ word3_muladd(&w2, &w1, &w0, ws[15], p[4]);
+ word3_muladd(&w2, &w1, &w0, ws[16], p[3]);
+ word3_muladd(&w2, &w1, &w0, ws[17], p[2]);
+ word3_muladd(&w2, &w1, &w0, ws[18], p[1]);
+ word3_add(&w2, &w1, &w0, z[19]);
+ ws[19] = w0 * p_dash;
+ word3_muladd(&w2, &w1, &w0, ws[19], p[0]);
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[0], p[20]);
+ word3_muladd(&w2, &w1, &w0, ws[1], p[19]);
+ word3_muladd(&w2, &w1, &w0, ws[2], p[18]);
+ word3_muladd(&w2, &w1, &w0, ws[3], p[17]);
+ word3_muladd(&w2, &w1, &w0, ws[4], p[16]);
+ word3_muladd(&w2, &w1, &w0, ws[5], p[15]);
+ word3_muladd(&w2, &w1, &w0, ws[6], p[14]);
+ word3_muladd(&w2, &w1, &w0, ws[7], p[13]);
+ word3_muladd(&w2, &w1, &w0, ws[8], p[12]);
+ word3_muladd(&w2, &w1, &w0, ws[9], p[11]);
+ word3_muladd(&w2, &w1, &w0, ws[10], p[10]);
+ word3_muladd(&w2, &w1, &w0, ws[11], p[9]);
+ word3_muladd(&w2, &w1, &w0, ws[12], p[8]);
+ word3_muladd(&w2, &w1, &w0, ws[13], p[7]);
+ word3_muladd(&w2, &w1, &w0, ws[14], p[6]);
+ word3_muladd(&w2, &w1, &w0, ws[15], p[5]);
+ word3_muladd(&w2, &w1, &w0, ws[16], p[4]);
+ word3_muladd(&w2, &w1, &w0, ws[17], p[3]);
+ word3_muladd(&w2, &w1, &w0, ws[18], p[2]);
+ word3_muladd(&w2, &w1, &w0, ws[19], p[1]);
+ word3_add(&w2, &w1, &w0, z[20]);
+ ws[20] = w0 * p_dash;
+ word3_muladd(&w2, &w1, &w0, ws[20], p[0]);
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[0], p[21]);
+ word3_muladd(&w2, &w1, &w0, ws[1], p[20]);
+ word3_muladd(&w2, &w1, &w0, ws[2], p[19]);
+ word3_muladd(&w2, &w1, &w0, ws[3], p[18]);
+ word3_muladd(&w2, &w1, &w0, ws[4], p[17]);
+ word3_muladd(&w2, &w1, &w0, ws[5], p[16]);
+ word3_muladd(&w2, &w1, &w0, ws[6], p[15]);
+ word3_muladd(&w2, &w1, &w0, ws[7], p[14]);
+ word3_muladd(&w2, &w1, &w0, ws[8], p[13]);
+ word3_muladd(&w2, &w1, &w0, ws[9], p[12]);
+ word3_muladd(&w2, &w1, &w0, ws[10], p[11]);
+ word3_muladd(&w2, &w1, &w0, ws[11], p[10]);
+ word3_muladd(&w2, &w1, &w0, ws[12], p[9]);
+ word3_muladd(&w2, &w1, &w0, ws[13], p[8]);
+ word3_muladd(&w2, &w1, &w0, ws[14], p[7]);
+ word3_muladd(&w2, &w1, &w0, ws[15], p[6]);
+ word3_muladd(&w2, &w1, &w0, ws[16], p[5]);
+ word3_muladd(&w2, &w1, &w0, ws[17], p[4]);
+ word3_muladd(&w2, &w1, &w0, ws[18], p[3]);
+ word3_muladd(&w2, &w1, &w0, ws[19], p[2]);
+ word3_muladd(&w2, &w1, &w0, ws[20], p[1]);
+ word3_add(&w2, &w1, &w0, z[21]);
+ ws[21] = w0 * p_dash;
+ word3_muladd(&w2, &w1, &w0, ws[21], p[0]);
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[0], p[22]);
+ word3_muladd(&w2, &w1, &w0, ws[1], p[21]);
+ word3_muladd(&w2, &w1, &w0, ws[2], p[20]);
+ word3_muladd(&w2, &w1, &w0, ws[3], p[19]);
+ word3_muladd(&w2, &w1, &w0, ws[4], p[18]);
+ word3_muladd(&w2, &w1, &w0, ws[5], p[17]);
+ word3_muladd(&w2, &w1, &w0, ws[6], p[16]);
+ word3_muladd(&w2, &w1, &w0, ws[7], p[15]);
+ word3_muladd(&w2, &w1, &w0, ws[8], p[14]);
+ word3_muladd(&w2, &w1, &w0, ws[9], p[13]);
+ word3_muladd(&w2, &w1, &w0, ws[10], p[12]);
+ word3_muladd(&w2, &w1, &w0, ws[11], p[11]);
+ word3_muladd(&w2, &w1, &w0, ws[12], p[10]);
+ word3_muladd(&w2, &w1, &w0, ws[13], p[9]);
+ word3_muladd(&w2, &w1, &w0, ws[14], p[8]);
+ word3_muladd(&w2, &w1, &w0, ws[15], p[7]);
+ word3_muladd(&w2, &w1, &w0, ws[16], p[6]);
+ word3_muladd(&w2, &w1, &w0, ws[17], p[5]);
+ word3_muladd(&w2, &w1, &w0, ws[18], p[4]);
+ word3_muladd(&w2, &w1, &w0, ws[19], p[3]);
+ word3_muladd(&w2, &w1, &w0, ws[20], p[2]);
+ word3_muladd(&w2, &w1, &w0, ws[21], p[1]);
+ word3_add(&w2, &w1, &w0, z[22]);
+ ws[22] = w0 * p_dash;
+ word3_muladd(&w2, &w1, &w0, ws[22], p[0]);
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[0], p[23]);
+ word3_muladd(&w2, &w1, &w0, ws[1], p[22]);
+ word3_muladd(&w2, &w1, &w0, ws[2], p[21]);
+ word3_muladd(&w2, &w1, &w0, ws[3], p[20]);
+ word3_muladd(&w2, &w1, &w0, ws[4], p[19]);
+ word3_muladd(&w2, &w1, &w0, ws[5], p[18]);
+ word3_muladd(&w2, &w1, &w0, ws[6], p[17]);
+ word3_muladd(&w2, &w1, &w0, ws[7], p[16]);
+ word3_muladd(&w2, &w1, &w0, ws[8], p[15]);
+ word3_muladd(&w2, &w1, &w0, ws[9], p[14]);
+ word3_muladd(&w2, &w1, &w0, ws[10], p[13]);
+ word3_muladd(&w2, &w1, &w0, ws[11], p[12]);
+ word3_muladd(&w2, &w1, &w0, ws[12], p[11]);
+ word3_muladd(&w2, &w1, &w0, ws[13], p[10]);
+ word3_muladd(&w2, &w1, &w0, ws[14], p[9]);
+ word3_muladd(&w2, &w1, &w0, ws[15], p[8]);
+ word3_muladd(&w2, &w1, &w0, ws[16], p[7]);
+ word3_muladd(&w2, &w1, &w0, ws[17], p[6]);
+ word3_muladd(&w2, &w1, &w0, ws[18], p[5]);
+ word3_muladd(&w2, &w1, &w0, ws[19], p[4]);
+ word3_muladd(&w2, &w1, &w0, ws[20], p[3]);
+ word3_muladd(&w2, &w1, &w0, ws[21], p[2]);
+ word3_muladd(&w2, &w1, &w0, ws[22], p[1]);
+ word3_add(&w2, &w1, &w0, z[23]);
+ ws[23] = w0 * p_dash;
+ word3_muladd(&w2, &w1, &w0, ws[23], p[0]);
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[1], p[23]);
+ word3_muladd(&w2, &w1, &w0, ws[2], p[22]);
+ word3_muladd(&w2, &w1, &w0, ws[3], p[21]);
+ word3_muladd(&w2, &w1, &w0, ws[4], p[20]);
+ word3_muladd(&w2, &w1, &w0, ws[5], p[19]);
+ word3_muladd(&w2, &w1, &w0, ws[6], p[18]);
+ word3_muladd(&w2, &w1, &w0, ws[7], p[17]);
+ word3_muladd(&w2, &w1, &w0, ws[8], p[16]);
+ word3_muladd(&w2, &w1, &w0, ws[9], p[15]);
+ word3_muladd(&w2, &w1, &w0, ws[10], p[14]);
+ word3_muladd(&w2, &w1, &w0, ws[11], p[13]);
+ word3_muladd(&w2, &w1, &w0, ws[12], p[12]);
+ word3_muladd(&w2, &w1, &w0, ws[13], p[11]);
+ word3_muladd(&w2, &w1, &w0, ws[14], p[10]);
+ word3_muladd(&w2, &w1, &w0, ws[15], p[9]);
+ word3_muladd(&w2, &w1, &w0, ws[16], p[8]);
+ word3_muladd(&w2, &w1, &w0, ws[17], p[7]);
+ word3_muladd(&w2, &w1, &w0, ws[18], p[6]);
+ word3_muladd(&w2, &w1, &w0, ws[19], p[5]);
+ word3_muladd(&w2, &w1, &w0, ws[20], p[4]);
+ word3_muladd(&w2, &w1, &w0, ws[21], p[3]);
+ word3_muladd(&w2, &w1, &w0, ws[22], p[2]);
+ word3_muladd(&w2, &w1, &w0, ws[23], p[1]);
+ word3_add(&w2, &w1, &w0, z[24]);
+ ws[0] = w0;
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[2], p[23]);
+ word3_muladd(&w2, &w1, &w0, ws[3], p[22]);
+ word3_muladd(&w2, &w1, &w0, ws[4], p[21]);
+ word3_muladd(&w2, &w1, &w0, ws[5], p[20]);
+ word3_muladd(&w2, &w1, &w0, ws[6], p[19]);
+ word3_muladd(&w2, &w1, &w0, ws[7], p[18]);
+ word3_muladd(&w2, &w1, &w0, ws[8], p[17]);
+ word3_muladd(&w2, &w1, &w0, ws[9], p[16]);
+ word3_muladd(&w2, &w1, &w0, ws[10], p[15]);
+ word3_muladd(&w2, &w1, &w0, ws[11], p[14]);
+ word3_muladd(&w2, &w1, &w0, ws[12], p[13]);
+ word3_muladd(&w2, &w1, &w0, ws[13], p[12]);
+ word3_muladd(&w2, &w1, &w0, ws[14], p[11]);
+ word3_muladd(&w2, &w1, &w0, ws[15], p[10]);
+ word3_muladd(&w2, &w1, &w0, ws[16], p[9]);
+ word3_muladd(&w2, &w1, &w0, ws[17], p[8]);
+ word3_muladd(&w2, &w1, &w0, ws[18], p[7]);
+ word3_muladd(&w2, &w1, &w0, ws[19], p[6]);
+ word3_muladd(&w2, &w1, &w0, ws[20], p[5]);
+ word3_muladd(&w2, &w1, &w0, ws[21], p[4]);
+ word3_muladd(&w2, &w1, &w0, ws[22], p[3]);
+ word3_muladd(&w2, &w1, &w0, ws[23], p[2]);
+ word3_add(&w2, &w1, &w0, z[25]);
+ ws[1] = w0;
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[3], p[23]);
+ word3_muladd(&w2, &w1, &w0, ws[4], p[22]);
+ word3_muladd(&w2, &w1, &w0, ws[5], p[21]);
+ word3_muladd(&w2, &w1, &w0, ws[6], p[20]);
+ word3_muladd(&w2, &w1, &w0, ws[7], p[19]);
+ word3_muladd(&w2, &w1, &w0, ws[8], p[18]);
+ word3_muladd(&w2, &w1, &w0, ws[9], p[17]);
+ word3_muladd(&w2, &w1, &w0, ws[10], p[16]);
+ word3_muladd(&w2, &w1, &w0, ws[11], p[15]);
+ word3_muladd(&w2, &w1, &w0, ws[12], p[14]);
+ word3_muladd(&w2, &w1, &w0, ws[13], p[13]);
+ word3_muladd(&w2, &w1, &w0, ws[14], p[12]);
+ word3_muladd(&w2, &w1, &w0, ws[15], p[11]);
+ word3_muladd(&w2, &w1, &w0, ws[16], p[10]);
+ word3_muladd(&w2, &w1, &w0, ws[17], p[9]);
+ word3_muladd(&w2, &w1, &w0, ws[18], p[8]);
+ word3_muladd(&w2, &w1, &w0, ws[19], p[7]);
+ word3_muladd(&w2, &w1, &w0, ws[20], p[6]);
+ word3_muladd(&w2, &w1, &w0, ws[21], p[5]);
+ word3_muladd(&w2, &w1, &w0, ws[22], p[4]);
+ word3_muladd(&w2, &w1, &w0, ws[23], p[3]);
+ word3_add(&w2, &w1, &w0, z[26]);
+ ws[2] = w0;
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[4], p[23]);
+ word3_muladd(&w2, &w1, &w0, ws[5], p[22]);
+ word3_muladd(&w2, &w1, &w0, ws[6], p[21]);
+ word3_muladd(&w2, &w1, &w0, ws[7], p[20]);
+ word3_muladd(&w2, &w1, &w0, ws[8], p[19]);
+ word3_muladd(&w2, &w1, &w0, ws[9], p[18]);
+ word3_muladd(&w2, &w1, &w0, ws[10], p[17]);
+ word3_muladd(&w2, &w1, &w0, ws[11], p[16]);
+ word3_muladd(&w2, &w1, &w0, ws[12], p[15]);
+ word3_muladd(&w2, &w1, &w0, ws[13], p[14]);
+ word3_muladd(&w2, &w1, &w0, ws[14], p[13]);
+ word3_muladd(&w2, &w1, &w0, ws[15], p[12]);
+ word3_muladd(&w2, &w1, &w0, ws[16], p[11]);
+ word3_muladd(&w2, &w1, &w0, ws[17], p[10]);
+ word3_muladd(&w2, &w1, &w0, ws[18], p[9]);
+ word3_muladd(&w2, &w1, &w0, ws[19], p[8]);
+ word3_muladd(&w2, &w1, &w0, ws[20], p[7]);
+ word3_muladd(&w2, &w1, &w0, ws[21], p[6]);
+ word3_muladd(&w2, &w1, &w0, ws[22], p[5]);
+ word3_muladd(&w2, &w1, &w0, ws[23], p[4]);
+ word3_add(&w2, &w1, &w0, z[27]);
+ ws[3] = w0;
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[5], p[23]);
+ word3_muladd(&w2, &w1, &w0, ws[6], p[22]);
+ word3_muladd(&w2, &w1, &w0, ws[7], p[21]);
+ word3_muladd(&w2, &w1, &w0, ws[8], p[20]);
+ word3_muladd(&w2, &w1, &w0, ws[9], p[19]);
+ word3_muladd(&w2, &w1, &w0, ws[10], p[18]);
+ word3_muladd(&w2, &w1, &w0, ws[11], p[17]);
+ word3_muladd(&w2, &w1, &w0, ws[12], p[16]);
+ word3_muladd(&w2, &w1, &w0, ws[13], p[15]);
+ word3_muladd(&w2, &w1, &w0, ws[14], p[14]);
+ word3_muladd(&w2, &w1, &w0, ws[15], p[13]);
+ word3_muladd(&w2, &w1, &w0, ws[16], p[12]);
+ word3_muladd(&w2, &w1, &w0, ws[17], p[11]);
+ word3_muladd(&w2, &w1, &w0, ws[18], p[10]);
+ word3_muladd(&w2, &w1, &w0, ws[19], p[9]);
+ word3_muladd(&w2, &w1, &w0, ws[20], p[8]);
+ word3_muladd(&w2, &w1, &w0, ws[21], p[7]);
+ word3_muladd(&w2, &w1, &w0, ws[22], p[6]);
+ word3_muladd(&w2, &w1, &w0, ws[23], p[5]);
+ word3_add(&w2, &w1, &w0, z[28]);
+ ws[4] = w0;
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[6], p[23]);
+ word3_muladd(&w2, &w1, &w0, ws[7], p[22]);
+ word3_muladd(&w2, &w1, &w0, ws[8], p[21]);
+ word3_muladd(&w2, &w1, &w0, ws[9], p[20]);
+ word3_muladd(&w2, &w1, &w0, ws[10], p[19]);
+ word3_muladd(&w2, &w1, &w0, ws[11], p[18]);
+ word3_muladd(&w2, &w1, &w0, ws[12], p[17]);
+ word3_muladd(&w2, &w1, &w0, ws[13], p[16]);
+ word3_muladd(&w2, &w1, &w0, ws[14], p[15]);
+ word3_muladd(&w2, &w1, &w0, ws[15], p[14]);
+ word3_muladd(&w2, &w1, &w0, ws[16], p[13]);
+ word3_muladd(&w2, &w1, &w0, ws[17], p[12]);
+ word3_muladd(&w2, &w1, &w0, ws[18], p[11]);
+ word3_muladd(&w2, &w1, &w0, ws[19], p[10]);
+ word3_muladd(&w2, &w1, &w0, ws[20], p[9]);
+ word3_muladd(&w2, &w1, &w0, ws[21], p[8]);
+ word3_muladd(&w2, &w1, &w0, ws[22], p[7]);
+ word3_muladd(&w2, &w1, &w0, ws[23], p[6]);
+ word3_add(&w2, &w1, &w0, z[29]);
+ ws[5] = w0;
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[7], p[23]);
+ word3_muladd(&w2, &w1, &w0, ws[8], p[22]);
+ word3_muladd(&w2, &w1, &w0, ws[9], p[21]);
+ word3_muladd(&w2, &w1, &w0, ws[10], p[20]);
+ word3_muladd(&w2, &w1, &w0, ws[11], p[19]);
+ word3_muladd(&w2, &w1, &w0, ws[12], p[18]);
+ word3_muladd(&w2, &w1, &w0, ws[13], p[17]);
+ word3_muladd(&w2, &w1, &w0, ws[14], p[16]);
+ word3_muladd(&w2, &w1, &w0, ws[15], p[15]);
+ word3_muladd(&w2, &w1, &w0, ws[16], p[14]);
+ word3_muladd(&w2, &w1, &w0, ws[17], p[13]);
+ word3_muladd(&w2, &w1, &w0, ws[18], p[12]);
+ word3_muladd(&w2, &w1, &w0, ws[19], p[11]);
+ word3_muladd(&w2, &w1, &w0, ws[20], p[10]);
+ word3_muladd(&w2, &w1, &w0, ws[21], p[9]);
+ word3_muladd(&w2, &w1, &w0, ws[22], p[8]);
+ word3_muladd(&w2, &w1, &w0, ws[23], p[7]);
+ word3_add(&w2, &w1, &w0, z[30]);
+ ws[6] = w0;
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[8], p[23]);
+ word3_muladd(&w2, &w1, &w0, ws[9], p[22]);
+ word3_muladd(&w2, &w1, &w0, ws[10], p[21]);
+ word3_muladd(&w2, &w1, &w0, ws[11], p[20]);
+ word3_muladd(&w2, &w1, &w0, ws[12], p[19]);
+ word3_muladd(&w2, &w1, &w0, ws[13], p[18]);
+ word3_muladd(&w2, &w1, &w0, ws[14], p[17]);
+ word3_muladd(&w2, &w1, &w0, ws[15], p[16]);
+ word3_muladd(&w2, &w1, &w0, ws[16], p[15]);
+ word3_muladd(&w2, &w1, &w0, ws[17], p[14]);
+ word3_muladd(&w2, &w1, &w0, ws[18], p[13]);
+ word3_muladd(&w2, &w1, &w0, ws[19], p[12]);
+ word3_muladd(&w2, &w1, &w0, ws[20], p[11]);
+ word3_muladd(&w2, &w1, &w0, ws[21], p[10]);
+ word3_muladd(&w2, &w1, &w0, ws[22], p[9]);
+ word3_muladd(&w2, &w1, &w0, ws[23], p[8]);
+ word3_add(&w2, &w1, &w0, z[31]);
+ ws[7] = w0;
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[9], p[23]);
+ word3_muladd(&w2, &w1, &w0, ws[10], p[22]);
+ word3_muladd(&w2, &w1, &w0, ws[11], p[21]);
+ word3_muladd(&w2, &w1, &w0, ws[12], p[20]);
+ word3_muladd(&w2, &w1, &w0, ws[13], p[19]);
+ word3_muladd(&w2, &w1, &w0, ws[14], p[18]);
+ word3_muladd(&w2, &w1, &w0, ws[15], p[17]);
+ word3_muladd(&w2, &w1, &w0, ws[16], p[16]);
+ word3_muladd(&w2, &w1, &w0, ws[17], p[15]);
+ word3_muladd(&w2, &w1, &w0, ws[18], p[14]);
+ word3_muladd(&w2, &w1, &w0, ws[19], p[13]);
+ word3_muladd(&w2, &w1, &w0, ws[20], p[12]);
+ word3_muladd(&w2, &w1, &w0, ws[21], p[11]);
+ word3_muladd(&w2, &w1, &w0, ws[22], p[10]);
+ word3_muladd(&w2, &w1, &w0, ws[23], p[9]);
+ word3_add(&w2, &w1, &w0, z[32]);
+ ws[8] = w0;
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[10], p[23]);
+ word3_muladd(&w2, &w1, &w0, ws[11], p[22]);
+ word3_muladd(&w2, &w1, &w0, ws[12], p[21]);
+ word3_muladd(&w2, &w1, &w0, ws[13], p[20]);
+ word3_muladd(&w2, &w1, &w0, ws[14], p[19]);
+ word3_muladd(&w2, &w1, &w0, ws[15], p[18]);
+ word3_muladd(&w2, &w1, &w0, ws[16], p[17]);
+ word3_muladd(&w2, &w1, &w0, ws[17], p[16]);
+ word3_muladd(&w2, &w1, &w0, ws[18], p[15]);
+ word3_muladd(&w2, &w1, &w0, ws[19], p[14]);
+ word3_muladd(&w2, &w1, &w0, ws[20], p[13]);
+ word3_muladd(&w2, &w1, &w0, ws[21], p[12]);
+ word3_muladd(&w2, &w1, &w0, ws[22], p[11]);
+ word3_muladd(&w2, &w1, &w0, ws[23], p[10]);
+ word3_add(&w2, &w1, &w0, z[33]);
+ ws[9] = w0;
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[11], p[23]);
+ word3_muladd(&w2, &w1, &w0, ws[12], p[22]);
+ word3_muladd(&w2, &w1, &w0, ws[13], p[21]);
+ word3_muladd(&w2, &w1, &w0, ws[14], p[20]);
+ word3_muladd(&w2, &w1, &w0, ws[15], p[19]);
+ word3_muladd(&w2, &w1, &w0, ws[16], p[18]);
+ word3_muladd(&w2, &w1, &w0, ws[17], p[17]);
+ word3_muladd(&w2, &w1, &w0, ws[18], p[16]);
+ word3_muladd(&w2, &w1, &w0, ws[19], p[15]);
+ word3_muladd(&w2, &w1, &w0, ws[20], p[14]);
+ word3_muladd(&w2, &w1, &w0, ws[21], p[13]);
+ word3_muladd(&w2, &w1, &w0, ws[22], p[12]);
+ word3_muladd(&w2, &w1, &w0, ws[23], p[11]);
+ word3_add(&w2, &w1, &w0, z[34]);
+ ws[10] = w0;
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[12], p[23]);
+ word3_muladd(&w2, &w1, &w0, ws[13], p[22]);
+ word3_muladd(&w2, &w1, &w0, ws[14], p[21]);
+ word3_muladd(&w2, &w1, &w0, ws[15], p[20]);
+ word3_muladd(&w2, &w1, &w0, ws[16], p[19]);
+ word3_muladd(&w2, &w1, &w0, ws[17], p[18]);
+ word3_muladd(&w2, &w1, &w0, ws[18], p[17]);
+ word3_muladd(&w2, &w1, &w0, ws[19], p[16]);
+ word3_muladd(&w2, &w1, &w0, ws[20], p[15]);
+ word3_muladd(&w2, &w1, &w0, ws[21], p[14]);
+ word3_muladd(&w2, &w1, &w0, ws[22], p[13]);
+ word3_muladd(&w2, &w1, &w0, ws[23], p[12]);
+ word3_add(&w2, &w1, &w0, z[35]);
+ ws[11] = w0;
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[13], p[23]);
+ word3_muladd(&w2, &w1, &w0, ws[14], p[22]);
+ word3_muladd(&w2, &w1, &w0, ws[15], p[21]);
+ word3_muladd(&w2, &w1, &w0, ws[16], p[20]);
+ word3_muladd(&w2, &w1, &w0, ws[17], p[19]);
+ word3_muladd(&w2, &w1, &w0, ws[18], p[18]);
+ word3_muladd(&w2, &w1, &w0, ws[19], p[17]);
+ word3_muladd(&w2, &w1, &w0, ws[20], p[16]);
+ word3_muladd(&w2, &w1, &w0, ws[21], p[15]);
+ word3_muladd(&w2, &w1, &w0, ws[22], p[14]);
+ word3_muladd(&w2, &w1, &w0, ws[23], p[13]);
+ word3_add(&w2, &w1, &w0, z[36]);
+ ws[12] = w0;
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[14], p[23]);
+ word3_muladd(&w2, &w1, &w0, ws[15], p[22]);
+ word3_muladd(&w2, &w1, &w0, ws[16], p[21]);
+ word3_muladd(&w2, &w1, &w0, ws[17], p[20]);
+ word3_muladd(&w2, &w1, &w0, ws[18], p[19]);
+ word3_muladd(&w2, &w1, &w0, ws[19], p[18]);
+ word3_muladd(&w2, &w1, &w0, ws[20], p[17]);
+ word3_muladd(&w2, &w1, &w0, ws[21], p[16]);
+ word3_muladd(&w2, &w1, &w0, ws[22], p[15]);
+ word3_muladd(&w2, &w1, &w0, ws[23], p[14]);
+ word3_add(&w2, &w1, &w0, z[37]);
+ ws[13] = w0;
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[15], p[23]);
+ word3_muladd(&w2, &w1, &w0, ws[16], p[22]);
+ word3_muladd(&w2, &w1, &w0, ws[17], p[21]);
+ word3_muladd(&w2, &w1, &w0, ws[18], p[20]);
+ word3_muladd(&w2, &w1, &w0, ws[19], p[19]);
+ word3_muladd(&w2, &w1, &w0, ws[20], p[18]);
+ word3_muladd(&w2, &w1, &w0, ws[21], p[17]);
+ word3_muladd(&w2, &w1, &w0, ws[22], p[16]);
+ word3_muladd(&w2, &w1, &w0, ws[23], p[15]);
+ word3_add(&w2, &w1, &w0, z[38]);
+ ws[14] = w0;
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[16], p[23]);
+ word3_muladd(&w2, &w1, &w0, ws[17], p[22]);
+ word3_muladd(&w2, &w1, &w0, ws[18], p[21]);
+ word3_muladd(&w2, &w1, &w0, ws[19], p[20]);
+ word3_muladd(&w2, &w1, &w0, ws[20], p[19]);
+ word3_muladd(&w2, &w1, &w0, ws[21], p[18]);
+ word3_muladd(&w2, &w1, &w0, ws[22], p[17]);
+ word3_muladd(&w2, &w1, &w0, ws[23], p[16]);
+ word3_add(&w2, &w1, &w0, z[39]);
+ ws[15] = w0;
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[17], p[23]);
+ word3_muladd(&w2, &w1, &w0, ws[18], p[22]);
+ word3_muladd(&w2, &w1, &w0, ws[19], p[21]);
+ word3_muladd(&w2, &w1, &w0, ws[20], p[20]);
+ word3_muladd(&w2, &w1, &w0, ws[21], p[19]);
+ word3_muladd(&w2, &w1, &w0, ws[22], p[18]);
+ word3_muladd(&w2, &w1, &w0, ws[23], p[17]);
+ word3_add(&w2, &w1, &w0, z[40]);
+ ws[16] = w0;
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[18], p[23]);
+ word3_muladd(&w2, &w1, &w0, ws[19], p[22]);
+ word3_muladd(&w2, &w1, &w0, ws[20], p[21]);
+ word3_muladd(&w2, &w1, &w0, ws[21], p[20]);
+ word3_muladd(&w2, &w1, &w0, ws[22], p[19]);
+ word3_muladd(&w2, &w1, &w0, ws[23], p[18]);
+ word3_add(&w2, &w1, &w0, z[41]);
+ ws[17] = w0;
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[19], p[23]);
+ word3_muladd(&w2, &w1, &w0, ws[20], p[22]);
+ word3_muladd(&w2, &w1, &w0, ws[21], p[21]);
+ word3_muladd(&w2, &w1, &w0, ws[22], p[20]);
+ word3_muladd(&w2, &w1, &w0, ws[23], p[19]);
+ word3_add(&w2, &w1, &w0, z[42]);
+ ws[18] = w0;
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[20], p[23]);
+ word3_muladd(&w2, &w1, &w0, ws[21], p[22]);
+ word3_muladd(&w2, &w1, &w0, ws[22], p[21]);
+ word3_muladd(&w2, &w1, &w0, ws[23], p[20]);
+ word3_add(&w2, &w1, &w0, z[43]);
+ ws[19] = w0;
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[21], p[23]);
+ word3_muladd(&w2, &w1, &w0, ws[22], p[22]);
+ word3_muladd(&w2, &w1, &w0, ws[23], p[21]);
+ word3_add(&w2, &w1, &w0, z[44]);
+ ws[20] = w0;
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[22], p[23]);
+ word3_muladd(&w2, &w1, &w0, ws[23], p[22]);
+ word3_add(&w2, &w1, &w0, z[45]);
+ ws[21] = w0;
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[23], p[23]);
+ word3_add(&w2, &w1, &w0, z[46]);
+ ws[22] = w0;
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_add(&w2, &w1, &w0, z[47]);
+ ws[23] = w0;
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_add(&w2, &w1, &w0, z[49]);
+ ws[24] = w0;
+ ws[25] = w1;
+ word borrow = bigint_sub3(ws + 24 + 1, ws, 24 + 1, p, 24);
+ CT::conditional_copy_mem(borrow, z, ws, ws + 25, 25);
+ clear_mem(z + 24, 2*(24+1) - 24);
+ }
+
+void bigint_monty_redc_32(word z[], const word p[32], word p_dash, word ws[])
+ {
+ word w2 = 0, w1 = 0, w0 = 0;
+ w0 = z[0];
+ ws[0] = w0 * p_dash;
+ word3_muladd(&w2, &w1, &w0, ws[0], p[0]);
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[0], p[1]);
+ word3_add(&w2, &w1, &w0, z[1]);
+ ws[1] = w0 * p_dash;
+ word3_muladd(&w2, &w1, &w0, ws[1], p[0]);
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[0], p[2]);
+ word3_muladd(&w2, &w1, &w0, ws[1], p[1]);
+ word3_add(&w2, &w1, &w0, z[2]);
+ ws[2] = w0 * p_dash;
+ word3_muladd(&w2, &w1, &w0, ws[2], p[0]);
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[0], p[3]);
+ word3_muladd(&w2, &w1, &w0, ws[1], p[2]);
+ word3_muladd(&w2, &w1, &w0, ws[2], p[1]);
+ word3_add(&w2, &w1, &w0, z[3]);
+ ws[3] = w0 * p_dash;
+ word3_muladd(&w2, &w1, &w0, ws[3], p[0]);
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[0], p[4]);
+ word3_muladd(&w2, &w1, &w0, ws[1], p[3]);
+ word3_muladd(&w2, &w1, &w0, ws[2], p[2]);
+ word3_muladd(&w2, &w1, &w0, ws[3], p[1]);
+ word3_add(&w2, &w1, &w0, z[4]);
+ ws[4] = w0 * p_dash;
+ word3_muladd(&w2, &w1, &w0, ws[4], p[0]);
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[0], p[5]);
+ word3_muladd(&w2, &w1, &w0, ws[1], p[4]);
+ word3_muladd(&w2, &w1, &w0, ws[2], p[3]);
+ word3_muladd(&w2, &w1, &w0, ws[3], p[2]);
+ word3_muladd(&w2, &w1, &w0, ws[4], p[1]);
+ word3_add(&w2, &w1, &w0, z[5]);
+ ws[5] = w0 * p_dash;
+ word3_muladd(&w2, &w1, &w0, ws[5], p[0]);
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[0], p[6]);
+ word3_muladd(&w2, &w1, &w0, ws[1], p[5]);
+ word3_muladd(&w2, &w1, &w0, ws[2], p[4]);
+ word3_muladd(&w2, &w1, &w0, ws[3], p[3]);
+ word3_muladd(&w2, &w1, &w0, ws[4], p[2]);
+ word3_muladd(&w2, &w1, &w0, ws[5], p[1]);
+ word3_add(&w2, &w1, &w0, z[6]);
+ ws[6] = w0 * p_dash;
+ word3_muladd(&w2, &w1, &w0, ws[6], p[0]);
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[0], p[7]);
+ word3_muladd(&w2, &w1, &w0, ws[1], p[6]);
+ word3_muladd(&w2, &w1, &w0, ws[2], p[5]);
+ word3_muladd(&w2, &w1, &w0, ws[3], p[4]);
+ word3_muladd(&w2, &w1, &w0, ws[4], p[3]);
+ word3_muladd(&w2, &w1, &w0, ws[5], p[2]);
+ word3_muladd(&w2, &w1, &w0, ws[6], p[1]);
+ word3_add(&w2, &w1, &w0, z[7]);
+ ws[7] = w0 * p_dash;
+ word3_muladd(&w2, &w1, &w0, ws[7], p[0]);
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[0], p[8]);
+ word3_muladd(&w2, &w1, &w0, ws[1], p[7]);
+ word3_muladd(&w2, &w1, &w0, ws[2], p[6]);
+ word3_muladd(&w2, &w1, &w0, ws[3], p[5]);
+ word3_muladd(&w2, &w1, &w0, ws[4], p[4]);
+ word3_muladd(&w2, &w1, &w0, ws[5], p[3]);
+ word3_muladd(&w2, &w1, &w0, ws[6], p[2]);
+ word3_muladd(&w2, &w1, &w0, ws[7], p[1]);
+ word3_add(&w2, &w1, &w0, z[8]);
+ ws[8] = w0 * p_dash;
+ word3_muladd(&w2, &w1, &w0, ws[8], p[0]);
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[0], p[9]);
+ word3_muladd(&w2, &w1, &w0, ws[1], p[8]);
+ word3_muladd(&w2, &w1, &w0, ws[2], p[7]);
+ word3_muladd(&w2, &w1, &w0, ws[3], p[6]);
+ word3_muladd(&w2, &w1, &w0, ws[4], p[5]);
+ word3_muladd(&w2, &w1, &w0, ws[5], p[4]);
+ word3_muladd(&w2, &w1, &w0, ws[6], p[3]);
+ word3_muladd(&w2, &w1, &w0, ws[7], p[2]);
+ word3_muladd(&w2, &w1, &w0, ws[8], p[1]);
+ word3_add(&w2, &w1, &w0, z[9]);
+ ws[9] = w0 * p_dash;
+ word3_muladd(&w2, &w1, &w0, ws[9], p[0]);
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[0], p[10]);
+ word3_muladd(&w2, &w1, &w0, ws[1], p[9]);
+ word3_muladd(&w2, &w1, &w0, ws[2], p[8]);
+ word3_muladd(&w2, &w1, &w0, ws[3], p[7]);
+ word3_muladd(&w2, &w1, &w0, ws[4], p[6]);
+ word3_muladd(&w2, &w1, &w0, ws[5], p[5]);
+ word3_muladd(&w2, &w1, &w0, ws[6], p[4]);
+ word3_muladd(&w2, &w1, &w0, ws[7], p[3]);
+ word3_muladd(&w2, &w1, &w0, ws[8], p[2]);
+ word3_muladd(&w2, &w1, &w0, ws[9], p[1]);
+ word3_add(&w2, &w1, &w0, z[10]);
+ ws[10] = w0 * p_dash;
+ word3_muladd(&w2, &w1, &w0, ws[10], p[0]);
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[0], p[11]);
+ word3_muladd(&w2, &w1, &w0, ws[1], p[10]);
+ word3_muladd(&w2, &w1, &w0, ws[2], p[9]);
+ word3_muladd(&w2, &w1, &w0, ws[3], p[8]);
+ word3_muladd(&w2, &w1, &w0, ws[4], p[7]);
+ word3_muladd(&w2, &w1, &w0, ws[5], p[6]);
+ word3_muladd(&w2, &w1, &w0, ws[6], p[5]);
+ word3_muladd(&w2, &w1, &w0, ws[7], p[4]);
+ word3_muladd(&w2, &w1, &w0, ws[8], p[3]);
+ word3_muladd(&w2, &w1, &w0, ws[9], p[2]);
+ word3_muladd(&w2, &w1, &w0, ws[10], p[1]);
+ word3_add(&w2, &w1, &w0, z[11]);
+ ws[11] = w0 * p_dash;
+ word3_muladd(&w2, &w1, &w0, ws[11], p[0]);
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[0], p[12]);
+ word3_muladd(&w2, &w1, &w0, ws[1], p[11]);
+ word3_muladd(&w2, &w1, &w0, ws[2], p[10]);
+ word3_muladd(&w2, &w1, &w0, ws[3], p[9]);
+ word3_muladd(&w2, &w1, &w0, ws[4], p[8]);
+ word3_muladd(&w2, &w1, &w0, ws[5], p[7]);
+ word3_muladd(&w2, &w1, &w0, ws[6], p[6]);
+ word3_muladd(&w2, &w1, &w0, ws[7], p[5]);
+ word3_muladd(&w2, &w1, &w0, ws[8], p[4]);
+ word3_muladd(&w2, &w1, &w0, ws[9], p[3]);
+ word3_muladd(&w2, &w1, &w0, ws[10], p[2]);
+ word3_muladd(&w2, &w1, &w0, ws[11], p[1]);
+ word3_add(&w2, &w1, &w0, z[12]);
+ ws[12] = w0 * p_dash;
+ word3_muladd(&w2, &w1, &w0, ws[12], p[0]);
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[0], p[13]);
+ word3_muladd(&w2, &w1, &w0, ws[1], p[12]);
+ word3_muladd(&w2, &w1, &w0, ws[2], p[11]);
+ word3_muladd(&w2, &w1, &w0, ws[3], p[10]);
+ word3_muladd(&w2, &w1, &w0, ws[4], p[9]);
+ word3_muladd(&w2, &w1, &w0, ws[5], p[8]);
+ word3_muladd(&w2, &w1, &w0, ws[6], p[7]);
+ word3_muladd(&w2, &w1, &w0, ws[7], p[6]);
+ word3_muladd(&w2, &w1, &w0, ws[8], p[5]);
+ word3_muladd(&w2, &w1, &w0, ws[9], p[4]);
+ word3_muladd(&w2, &w1, &w0, ws[10], p[3]);
+ word3_muladd(&w2, &w1, &w0, ws[11], p[2]);
+ word3_muladd(&w2, &w1, &w0, ws[12], p[1]);
+ word3_add(&w2, &w1, &w0, z[13]);
+ ws[13] = w0 * p_dash;
+ word3_muladd(&w2, &w1, &w0, ws[13], p[0]);
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[0], p[14]);
+ word3_muladd(&w2, &w1, &w0, ws[1], p[13]);
+ word3_muladd(&w2, &w1, &w0, ws[2], p[12]);
+ word3_muladd(&w2, &w1, &w0, ws[3], p[11]);
+ word3_muladd(&w2, &w1, &w0, ws[4], p[10]);
+ word3_muladd(&w2, &w1, &w0, ws[5], p[9]);
+ word3_muladd(&w2, &w1, &w0, ws[6], p[8]);
+ word3_muladd(&w2, &w1, &w0, ws[7], p[7]);
+ word3_muladd(&w2, &w1, &w0, ws[8], p[6]);
+ word3_muladd(&w2, &w1, &w0, ws[9], p[5]);
+ word3_muladd(&w2, &w1, &w0, ws[10], p[4]);
+ word3_muladd(&w2, &w1, &w0, ws[11], p[3]);
+ word3_muladd(&w2, &w1, &w0, ws[12], p[2]);
+ word3_muladd(&w2, &w1, &w0, ws[13], p[1]);
+ word3_add(&w2, &w1, &w0, z[14]);
+ ws[14] = w0 * p_dash;
+ word3_muladd(&w2, &w1, &w0, ws[14], p[0]);
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[0], p[15]);
+ word3_muladd(&w2, &w1, &w0, ws[1], p[14]);
+ word3_muladd(&w2, &w1, &w0, ws[2], p[13]);
+ word3_muladd(&w2, &w1, &w0, ws[3], p[12]);
+ word3_muladd(&w2, &w1, &w0, ws[4], p[11]);
+ word3_muladd(&w2, &w1, &w0, ws[5], p[10]);
+ word3_muladd(&w2, &w1, &w0, ws[6], p[9]);
+ word3_muladd(&w2, &w1, &w0, ws[7], p[8]);
+ word3_muladd(&w2, &w1, &w0, ws[8], p[7]);
+ word3_muladd(&w2, &w1, &w0, ws[9], p[6]);
+ word3_muladd(&w2, &w1, &w0, ws[10], p[5]);
+ word3_muladd(&w2, &w1, &w0, ws[11], p[4]);
+ word3_muladd(&w2, &w1, &w0, ws[12], p[3]);
+ word3_muladd(&w2, &w1, &w0, ws[13], p[2]);
+ word3_muladd(&w2, &w1, &w0, ws[14], p[1]);
+ word3_add(&w2, &w1, &w0, z[15]);
+ ws[15] = w0 * p_dash;
+ word3_muladd(&w2, &w1, &w0, ws[15], p[0]);
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[0], p[16]);
+ word3_muladd(&w2, &w1, &w0, ws[1], p[15]);
+ word3_muladd(&w2, &w1, &w0, ws[2], p[14]);
+ word3_muladd(&w2, &w1, &w0, ws[3], p[13]);
+ word3_muladd(&w2, &w1, &w0, ws[4], p[12]);
+ word3_muladd(&w2, &w1, &w0, ws[5], p[11]);
+ word3_muladd(&w2, &w1, &w0, ws[6], p[10]);
+ word3_muladd(&w2, &w1, &w0, ws[7], p[9]);
+ word3_muladd(&w2, &w1, &w0, ws[8], p[8]);
+ word3_muladd(&w2, &w1, &w0, ws[9], p[7]);
+ word3_muladd(&w2, &w1, &w0, ws[10], p[6]);
+ word3_muladd(&w2, &w1, &w0, ws[11], p[5]);
+ word3_muladd(&w2, &w1, &w0, ws[12], p[4]);
+ word3_muladd(&w2, &w1, &w0, ws[13], p[3]);
+ word3_muladd(&w2, &w1, &w0, ws[14], p[2]);
+ word3_muladd(&w2, &w1, &w0, ws[15], p[1]);
+ word3_add(&w2, &w1, &w0, z[16]);
+ ws[16] = w0 * p_dash;
+ word3_muladd(&w2, &w1, &w0, ws[16], p[0]);
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[0], p[17]);
+ word3_muladd(&w2, &w1, &w0, ws[1], p[16]);
+ word3_muladd(&w2, &w1, &w0, ws[2], p[15]);
+ word3_muladd(&w2, &w1, &w0, ws[3], p[14]);
+ word3_muladd(&w2, &w1, &w0, ws[4], p[13]);
+ word3_muladd(&w2, &w1, &w0, ws[5], p[12]);
+ word3_muladd(&w2, &w1, &w0, ws[6], p[11]);
+ word3_muladd(&w2, &w1, &w0, ws[7], p[10]);
+ word3_muladd(&w2, &w1, &w0, ws[8], p[9]);
+ word3_muladd(&w2, &w1, &w0, ws[9], p[8]);
+ word3_muladd(&w2, &w1, &w0, ws[10], p[7]);
+ word3_muladd(&w2, &w1, &w0, ws[11], p[6]);
+ word3_muladd(&w2, &w1, &w0, ws[12], p[5]);
+ word3_muladd(&w2, &w1, &w0, ws[13], p[4]);
+ word3_muladd(&w2, &w1, &w0, ws[14], p[3]);
+ word3_muladd(&w2, &w1, &w0, ws[15], p[2]);
+ word3_muladd(&w2, &w1, &w0, ws[16], p[1]);
+ word3_add(&w2, &w1, &w0, z[17]);
+ ws[17] = w0 * p_dash;
+ word3_muladd(&w2, &w1, &w0, ws[17], p[0]);
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[0], p[18]);
+ word3_muladd(&w2, &w1, &w0, ws[1], p[17]);
+ word3_muladd(&w2, &w1, &w0, ws[2], p[16]);
+ word3_muladd(&w2, &w1, &w0, ws[3], p[15]);
+ word3_muladd(&w2, &w1, &w0, ws[4], p[14]);
+ word3_muladd(&w2, &w1, &w0, ws[5], p[13]);
+ word3_muladd(&w2, &w1, &w0, ws[6], p[12]);
+ word3_muladd(&w2, &w1, &w0, ws[7], p[11]);
+ word3_muladd(&w2, &w1, &w0, ws[8], p[10]);
+ word3_muladd(&w2, &w1, &w0, ws[9], p[9]);
+ word3_muladd(&w2, &w1, &w0, ws[10], p[8]);
+ word3_muladd(&w2, &w1, &w0, ws[11], p[7]);
+ word3_muladd(&w2, &w1, &w0, ws[12], p[6]);
+ word3_muladd(&w2, &w1, &w0, ws[13], p[5]);
+ word3_muladd(&w2, &w1, &w0, ws[14], p[4]);
+ word3_muladd(&w2, &w1, &w0, ws[15], p[3]);
+ word3_muladd(&w2, &w1, &w0, ws[16], p[2]);
+ word3_muladd(&w2, &w1, &w0, ws[17], p[1]);
+ word3_add(&w2, &w1, &w0, z[18]);
+ ws[18] = w0 * p_dash;
+ word3_muladd(&w2, &w1, &w0, ws[18], p[0]);
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[0], p[19]);
+ word3_muladd(&w2, &w1, &w0, ws[1], p[18]);
+ word3_muladd(&w2, &w1, &w0, ws[2], p[17]);
+ word3_muladd(&w2, &w1, &w0, ws[3], p[16]);
+ word3_muladd(&w2, &w1, &w0, ws[4], p[15]);
+ word3_muladd(&w2, &w1, &w0, ws[5], p[14]);
+ word3_muladd(&w2, &w1, &w0, ws[6], p[13]);
+ word3_muladd(&w2, &w1, &w0, ws[7], p[12]);
+ word3_muladd(&w2, &w1, &w0, ws[8], p[11]);
+ word3_muladd(&w2, &w1, &w0, ws[9], p[10]);
+ word3_muladd(&w2, &w1, &w0, ws[10], p[9]);
+ word3_muladd(&w2, &w1, &w0, ws[11], p[8]);
+ word3_muladd(&w2, &w1, &w0, ws[12], p[7]);
+ word3_muladd(&w2, &w1, &w0, ws[13], p[6]);
+ word3_muladd(&w2, &w1, &w0, ws[14], p[5]);
+ word3_muladd(&w2, &w1, &w0, ws[15], p[4]);
+ word3_muladd(&w2, &w1, &w0, ws[16], p[3]);
+ word3_muladd(&w2, &w1, &w0, ws[17], p[2]);
+ word3_muladd(&w2, &w1, &w0, ws[18], p[1]);
+ word3_add(&w2, &w1, &w0, z[19]);
+ ws[19] = w0 * p_dash;
+ word3_muladd(&w2, &w1, &w0, ws[19], p[0]);
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[0], p[20]);
+ word3_muladd(&w2, &w1, &w0, ws[1], p[19]);
+ word3_muladd(&w2, &w1, &w0, ws[2], p[18]);
+ word3_muladd(&w2, &w1, &w0, ws[3], p[17]);
+ word3_muladd(&w2, &w1, &w0, ws[4], p[16]);
+ word3_muladd(&w2, &w1, &w0, ws[5], p[15]);
+ word3_muladd(&w2, &w1, &w0, ws[6], p[14]);
+ word3_muladd(&w2, &w1, &w0, ws[7], p[13]);
+ word3_muladd(&w2, &w1, &w0, ws[8], p[12]);
+ word3_muladd(&w2, &w1, &w0, ws[9], p[11]);
+ word3_muladd(&w2, &w1, &w0, ws[10], p[10]);
+ word3_muladd(&w2, &w1, &w0, ws[11], p[9]);
+ word3_muladd(&w2, &w1, &w0, ws[12], p[8]);
+ word3_muladd(&w2, &w1, &w0, ws[13], p[7]);
+ word3_muladd(&w2, &w1, &w0, ws[14], p[6]);
+ word3_muladd(&w2, &w1, &w0, ws[15], p[5]);
+ word3_muladd(&w2, &w1, &w0, ws[16], p[4]);
+ word3_muladd(&w2, &w1, &w0, ws[17], p[3]);
+ word3_muladd(&w2, &w1, &w0, ws[18], p[2]);
+ word3_muladd(&w2, &w1, &w0, ws[19], p[1]);
+ word3_add(&w2, &w1, &w0, z[20]);
+ ws[20] = w0 * p_dash;
+ word3_muladd(&w2, &w1, &w0, ws[20], p[0]);
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[0], p[21]);
+ word3_muladd(&w2, &w1, &w0, ws[1], p[20]);
+ word3_muladd(&w2, &w1, &w0, ws[2], p[19]);
+ word3_muladd(&w2, &w1, &w0, ws[3], p[18]);
+ word3_muladd(&w2, &w1, &w0, ws[4], p[17]);
+ word3_muladd(&w2, &w1, &w0, ws[5], p[16]);
+ word3_muladd(&w2, &w1, &w0, ws[6], p[15]);
+ word3_muladd(&w2, &w1, &w0, ws[7], p[14]);
+ word3_muladd(&w2, &w1, &w0, ws[8], p[13]);
+ word3_muladd(&w2, &w1, &w0, ws[9], p[12]);
+ word3_muladd(&w2, &w1, &w0, ws[10], p[11]);
+ word3_muladd(&w2, &w1, &w0, ws[11], p[10]);
+ word3_muladd(&w2, &w1, &w0, ws[12], p[9]);
+ word3_muladd(&w2, &w1, &w0, ws[13], p[8]);
+ word3_muladd(&w2, &w1, &w0, ws[14], p[7]);
+ word3_muladd(&w2, &w1, &w0, ws[15], p[6]);
+ word3_muladd(&w2, &w1, &w0, ws[16], p[5]);
+ word3_muladd(&w2, &w1, &w0, ws[17], p[4]);
+ word3_muladd(&w2, &w1, &w0, ws[18], p[3]);
+ word3_muladd(&w2, &w1, &w0, ws[19], p[2]);
+ word3_muladd(&w2, &w1, &w0, ws[20], p[1]);
+ word3_add(&w2, &w1, &w0, z[21]);
+ ws[21] = w0 * p_dash;
+ word3_muladd(&w2, &w1, &w0, ws[21], p[0]);
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[0], p[22]);
+ word3_muladd(&w2, &w1, &w0, ws[1], p[21]);
+ word3_muladd(&w2, &w1, &w0, ws[2], p[20]);
+ word3_muladd(&w2, &w1, &w0, ws[3], p[19]);
+ word3_muladd(&w2, &w1, &w0, ws[4], p[18]);
+ word3_muladd(&w2, &w1, &w0, ws[5], p[17]);
+ word3_muladd(&w2, &w1, &w0, ws[6], p[16]);
+ word3_muladd(&w2, &w1, &w0, ws[7], p[15]);
+ word3_muladd(&w2, &w1, &w0, ws[8], p[14]);
+ word3_muladd(&w2, &w1, &w0, ws[9], p[13]);
+ word3_muladd(&w2, &w1, &w0, ws[10], p[12]);
+ word3_muladd(&w2, &w1, &w0, ws[11], p[11]);
+ word3_muladd(&w2, &w1, &w0, ws[12], p[10]);
+ word3_muladd(&w2, &w1, &w0, ws[13], p[9]);
+ word3_muladd(&w2, &w1, &w0, ws[14], p[8]);
+ word3_muladd(&w2, &w1, &w0, ws[15], p[7]);
+ word3_muladd(&w2, &w1, &w0, ws[16], p[6]);
+ word3_muladd(&w2, &w1, &w0, ws[17], p[5]);
+ word3_muladd(&w2, &w1, &w0, ws[18], p[4]);
+ word3_muladd(&w2, &w1, &w0, ws[19], p[3]);
+ word3_muladd(&w2, &w1, &w0, ws[20], p[2]);
+ word3_muladd(&w2, &w1, &w0, ws[21], p[1]);
+ word3_add(&w2, &w1, &w0, z[22]);
+ ws[22] = w0 * p_dash;
+ word3_muladd(&w2, &w1, &w0, ws[22], p[0]);
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[0], p[23]);
+ word3_muladd(&w2, &w1, &w0, ws[1], p[22]);
+ word3_muladd(&w2, &w1, &w0, ws[2], p[21]);
+ word3_muladd(&w2, &w1, &w0, ws[3], p[20]);
+ word3_muladd(&w2, &w1, &w0, ws[4], p[19]);
+ word3_muladd(&w2, &w1, &w0, ws[5], p[18]);
+ word3_muladd(&w2, &w1, &w0, ws[6], p[17]);
+ word3_muladd(&w2, &w1, &w0, ws[7], p[16]);
+ word3_muladd(&w2, &w1, &w0, ws[8], p[15]);
+ word3_muladd(&w2, &w1, &w0, ws[9], p[14]);
+ word3_muladd(&w2, &w1, &w0, ws[10], p[13]);
+ word3_muladd(&w2, &w1, &w0, ws[11], p[12]);
+ word3_muladd(&w2, &w1, &w0, ws[12], p[11]);
+ word3_muladd(&w2, &w1, &w0, ws[13], p[10]);
+ word3_muladd(&w2, &w1, &w0, ws[14], p[9]);
+ word3_muladd(&w2, &w1, &w0, ws[15], p[8]);
+ word3_muladd(&w2, &w1, &w0, ws[16], p[7]);
+ word3_muladd(&w2, &w1, &w0, ws[17], p[6]);
+ word3_muladd(&w2, &w1, &w0, ws[18], p[5]);
+ word3_muladd(&w2, &w1, &w0, ws[19], p[4]);
+ word3_muladd(&w2, &w1, &w0, ws[20], p[3]);
+ word3_muladd(&w2, &w1, &w0, ws[21], p[2]);
+ word3_muladd(&w2, &w1, &w0, ws[22], p[1]);
+ word3_add(&w2, &w1, &w0, z[23]);
+ ws[23] = w0 * p_dash;
+ word3_muladd(&w2, &w1, &w0, ws[23], p[0]);
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[0], p[24]);
+ word3_muladd(&w2, &w1, &w0, ws[1], p[23]);
+ word3_muladd(&w2, &w1, &w0, ws[2], p[22]);
+ word3_muladd(&w2, &w1, &w0, ws[3], p[21]);
+ word3_muladd(&w2, &w1, &w0, ws[4], p[20]);
+ word3_muladd(&w2, &w1, &w0, ws[5], p[19]);
+ word3_muladd(&w2, &w1, &w0, ws[6], p[18]);
+ word3_muladd(&w2, &w1, &w0, ws[7], p[17]);
+ word3_muladd(&w2, &w1, &w0, ws[8], p[16]);
+ word3_muladd(&w2, &w1, &w0, ws[9], p[15]);
+ word3_muladd(&w2, &w1, &w0, ws[10], p[14]);
+ word3_muladd(&w2, &w1, &w0, ws[11], p[13]);
+ word3_muladd(&w2, &w1, &w0, ws[12], p[12]);
+ word3_muladd(&w2, &w1, &w0, ws[13], p[11]);
+ word3_muladd(&w2, &w1, &w0, ws[14], p[10]);
+ word3_muladd(&w2, &w1, &w0, ws[15], p[9]);
+ word3_muladd(&w2, &w1, &w0, ws[16], p[8]);
+ word3_muladd(&w2, &w1, &w0, ws[17], p[7]);
+ word3_muladd(&w2, &w1, &w0, ws[18], p[6]);
+ word3_muladd(&w2, &w1, &w0, ws[19], p[5]);
+ word3_muladd(&w2, &w1, &w0, ws[20], p[4]);
+ word3_muladd(&w2, &w1, &w0, ws[21], p[3]);
+ word3_muladd(&w2, &w1, &w0, ws[22], p[2]);
+ word3_muladd(&w2, &w1, &w0, ws[23], p[1]);
+ word3_add(&w2, &w1, &w0, z[24]);
+ ws[24] = w0 * p_dash;
+ word3_muladd(&w2, &w1, &w0, ws[24], p[0]);
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[0], p[25]);
+ word3_muladd(&w2, &w1, &w0, ws[1], p[24]);
+ word3_muladd(&w2, &w1, &w0, ws[2], p[23]);
+ word3_muladd(&w2, &w1, &w0, ws[3], p[22]);
+ word3_muladd(&w2, &w1, &w0, ws[4], p[21]);
+ word3_muladd(&w2, &w1, &w0, ws[5], p[20]);
+ word3_muladd(&w2, &w1, &w0, ws[6], p[19]);
+ word3_muladd(&w2, &w1, &w0, ws[7], p[18]);
+ word3_muladd(&w2, &w1, &w0, ws[8], p[17]);
+ word3_muladd(&w2, &w1, &w0, ws[9], p[16]);
+ word3_muladd(&w2, &w1, &w0, ws[10], p[15]);
+ word3_muladd(&w2, &w1, &w0, ws[11], p[14]);
+ word3_muladd(&w2, &w1, &w0, ws[12], p[13]);
+ word3_muladd(&w2, &w1, &w0, ws[13], p[12]);
+ word3_muladd(&w2, &w1, &w0, ws[14], p[11]);
+ word3_muladd(&w2, &w1, &w0, ws[15], p[10]);
+ word3_muladd(&w2, &w1, &w0, ws[16], p[9]);
+ word3_muladd(&w2, &w1, &w0, ws[17], p[8]);
+ word3_muladd(&w2, &w1, &w0, ws[18], p[7]);
+ word3_muladd(&w2, &w1, &w0, ws[19], p[6]);
+ word3_muladd(&w2, &w1, &w0, ws[20], p[5]);
+ word3_muladd(&w2, &w1, &w0, ws[21], p[4]);
+ word3_muladd(&w2, &w1, &w0, ws[22], p[3]);
+ word3_muladd(&w2, &w1, &w0, ws[23], p[2]);
+ word3_muladd(&w2, &w1, &w0, ws[24], p[1]);
+ word3_add(&w2, &w1, &w0, z[25]);
+ ws[25] = w0 * p_dash;
+ word3_muladd(&w2, &w1, &w0, ws[25], p[0]);
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[0], p[26]);
+ word3_muladd(&w2, &w1, &w0, ws[1], p[25]);
+ word3_muladd(&w2, &w1, &w0, ws[2], p[24]);
+ word3_muladd(&w2, &w1, &w0, ws[3], p[23]);
+ word3_muladd(&w2, &w1, &w0, ws[4], p[22]);
+ word3_muladd(&w2, &w1, &w0, ws[5], p[21]);
+ word3_muladd(&w2, &w1, &w0, ws[6], p[20]);
+ word3_muladd(&w2, &w1, &w0, ws[7], p[19]);
+ word3_muladd(&w2, &w1, &w0, ws[8], p[18]);
+ word3_muladd(&w2, &w1, &w0, ws[9], p[17]);
+ word3_muladd(&w2, &w1, &w0, ws[10], p[16]);
+ word3_muladd(&w2, &w1, &w0, ws[11], p[15]);
+ word3_muladd(&w2, &w1, &w0, ws[12], p[14]);
+ word3_muladd(&w2, &w1, &w0, ws[13], p[13]);
+ word3_muladd(&w2, &w1, &w0, ws[14], p[12]);
+ word3_muladd(&w2, &w1, &w0, ws[15], p[11]);
+ word3_muladd(&w2, &w1, &w0, ws[16], p[10]);
+ word3_muladd(&w2, &w1, &w0, ws[17], p[9]);
+ word3_muladd(&w2, &w1, &w0, ws[18], p[8]);
+ word3_muladd(&w2, &w1, &w0, ws[19], p[7]);
+ word3_muladd(&w2, &w1, &w0, ws[20], p[6]);
+ word3_muladd(&w2, &w1, &w0, ws[21], p[5]);
+ word3_muladd(&w2, &w1, &w0, ws[22], p[4]);
+ word3_muladd(&w2, &w1, &w0, ws[23], p[3]);
+ word3_muladd(&w2, &w1, &w0, ws[24], p[2]);
+ word3_muladd(&w2, &w1, &w0, ws[25], p[1]);
+ word3_add(&w2, &w1, &w0, z[26]);
+ ws[26] = w0 * p_dash;
+ word3_muladd(&w2, &w1, &w0, ws[26], p[0]);
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[0], p[27]);
+ word3_muladd(&w2, &w1, &w0, ws[1], p[26]);
+ word3_muladd(&w2, &w1, &w0, ws[2], p[25]);
+ word3_muladd(&w2, &w1, &w0, ws[3], p[24]);
+ word3_muladd(&w2, &w1, &w0, ws[4], p[23]);
+ word3_muladd(&w2, &w1, &w0, ws[5], p[22]);
+ word3_muladd(&w2, &w1, &w0, ws[6], p[21]);
+ word3_muladd(&w2, &w1, &w0, ws[7], p[20]);
+ word3_muladd(&w2, &w1, &w0, ws[8], p[19]);
+ word3_muladd(&w2, &w1, &w0, ws[9], p[18]);
+ word3_muladd(&w2, &w1, &w0, ws[10], p[17]);
+ word3_muladd(&w2, &w1, &w0, ws[11], p[16]);
+ word3_muladd(&w2, &w1, &w0, ws[12], p[15]);
+ word3_muladd(&w2, &w1, &w0, ws[13], p[14]);
+ word3_muladd(&w2, &w1, &w0, ws[14], p[13]);
+ word3_muladd(&w2, &w1, &w0, ws[15], p[12]);
+ word3_muladd(&w2, &w1, &w0, ws[16], p[11]);
+ word3_muladd(&w2, &w1, &w0, ws[17], p[10]);
+ word3_muladd(&w2, &w1, &w0, ws[18], p[9]);
+ word3_muladd(&w2, &w1, &w0, ws[19], p[8]);
+ word3_muladd(&w2, &w1, &w0, ws[20], p[7]);
+ word3_muladd(&w2, &w1, &w0, ws[21], p[6]);
+ word3_muladd(&w2, &w1, &w0, ws[22], p[5]);
+ word3_muladd(&w2, &w1, &w0, ws[23], p[4]);
+ word3_muladd(&w2, &w1, &w0, ws[24], p[3]);
+ word3_muladd(&w2, &w1, &w0, ws[25], p[2]);
+ word3_muladd(&w2, &w1, &w0, ws[26], p[1]);
+ word3_add(&w2, &w1, &w0, z[27]);
+ ws[27] = w0 * p_dash;
+ word3_muladd(&w2, &w1, &w0, ws[27], p[0]);
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[0], p[28]);
+ word3_muladd(&w2, &w1, &w0, ws[1], p[27]);
+ word3_muladd(&w2, &w1, &w0, ws[2], p[26]);
+ word3_muladd(&w2, &w1, &w0, ws[3], p[25]);
+ word3_muladd(&w2, &w1, &w0, ws[4], p[24]);
+ word3_muladd(&w2, &w1, &w0, ws[5], p[23]);
+ word3_muladd(&w2, &w1, &w0, ws[6], p[22]);
+ word3_muladd(&w2, &w1, &w0, ws[7], p[21]);
+ word3_muladd(&w2, &w1, &w0, ws[8], p[20]);
+ word3_muladd(&w2, &w1, &w0, ws[9], p[19]);
+ word3_muladd(&w2, &w1, &w0, ws[10], p[18]);
+ word3_muladd(&w2, &w1, &w0, ws[11], p[17]);
+ word3_muladd(&w2, &w1, &w0, ws[12], p[16]);
+ word3_muladd(&w2, &w1, &w0, ws[13], p[15]);
+ word3_muladd(&w2, &w1, &w0, ws[14], p[14]);
+ word3_muladd(&w2, &w1, &w0, ws[15], p[13]);
+ word3_muladd(&w2, &w1, &w0, ws[16], p[12]);
+ word3_muladd(&w2, &w1, &w0, ws[17], p[11]);
+ word3_muladd(&w2, &w1, &w0, ws[18], p[10]);
+ word3_muladd(&w2, &w1, &w0, ws[19], p[9]);
+ word3_muladd(&w2, &w1, &w0, ws[20], p[8]);
+ word3_muladd(&w2, &w1, &w0, ws[21], p[7]);
+ word3_muladd(&w2, &w1, &w0, ws[22], p[6]);
+ word3_muladd(&w2, &w1, &w0, ws[23], p[5]);
+ word3_muladd(&w2, &w1, &w0, ws[24], p[4]);
+ word3_muladd(&w2, &w1, &w0, ws[25], p[3]);
+ word3_muladd(&w2, &w1, &w0, ws[26], p[2]);
+ word3_muladd(&w2, &w1, &w0, ws[27], p[1]);
+ word3_add(&w2, &w1, &w0, z[28]);
+ ws[28] = w0 * p_dash;
+ word3_muladd(&w2, &w1, &w0, ws[28], p[0]);
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[0], p[29]);
+ word3_muladd(&w2, &w1, &w0, ws[1], p[28]);
+ word3_muladd(&w2, &w1, &w0, ws[2], p[27]);
+ word3_muladd(&w2, &w1, &w0, ws[3], p[26]);
+ word3_muladd(&w2, &w1, &w0, ws[4], p[25]);
+ word3_muladd(&w2, &w1, &w0, ws[5], p[24]);
+ word3_muladd(&w2, &w1, &w0, ws[6], p[23]);
+ word3_muladd(&w2, &w1, &w0, ws[7], p[22]);
+ word3_muladd(&w2, &w1, &w0, ws[8], p[21]);
+ word3_muladd(&w2, &w1, &w0, ws[9], p[20]);
+ word3_muladd(&w2, &w1, &w0, ws[10], p[19]);
+ word3_muladd(&w2, &w1, &w0, ws[11], p[18]);
+ word3_muladd(&w2, &w1, &w0, ws[12], p[17]);
+ word3_muladd(&w2, &w1, &w0, ws[13], p[16]);
+ word3_muladd(&w2, &w1, &w0, ws[14], p[15]);
+ word3_muladd(&w2, &w1, &w0, ws[15], p[14]);
+ word3_muladd(&w2, &w1, &w0, ws[16], p[13]);
+ word3_muladd(&w2, &w1, &w0, ws[17], p[12]);
+ word3_muladd(&w2, &w1, &w0, ws[18], p[11]);
+ word3_muladd(&w2, &w1, &w0, ws[19], p[10]);
+ word3_muladd(&w2, &w1, &w0, ws[20], p[9]);
+ word3_muladd(&w2, &w1, &w0, ws[21], p[8]);
+ word3_muladd(&w2, &w1, &w0, ws[22], p[7]);
+ word3_muladd(&w2, &w1, &w0, ws[23], p[6]);
+ word3_muladd(&w2, &w1, &w0, ws[24], p[5]);
+ word3_muladd(&w2, &w1, &w0, ws[25], p[4]);
+ word3_muladd(&w2, &w1, &w0, ws[26], p[3]);
+ word3_muladd(&w2, &w1, &w0, ws[27], p[2]);
+ word3_muladd(&w2, &w1, &w0, ws[28], p[1]);
+ word3_add(&w2, &w1, &w0, z[29]);
+ ws[29] = w0 * p_dash;
+ word3_muladd(&w2, &w1, &w0, ws[29], p[0]);
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[0], p[30]);
+ word3_muladd(&w2, &w1, &w0, ws[1], p[29]);
+ word3_muladd(&w2, &w1, &w0, ws[2], p[28]);
+ word3_muladd(&w2, &w1, &w0, ws[3], p[27]);
+ word3_muladd(&w2, &w1, &w0, ws[4], p[26]);
+ word3_muladd(&w2, &w1, &w0, ws[5], p[25]);
+ word3_muladd(&w2, &w1, &w0, ws[6], p[24]);
+ word3_muladd(&w2, &w1, &w0, ws[7], p[23]);
+ word3_muladd(&w2, &w1, &w0, ws[8], p[22]);
+ word3_muladd(&w2, &w1, &w0, ws[9], p[21]);
+ word3_muladd(&w2, &w1, &w0, ws[10], p[20]);
+ word3_muladd(&w2, &w1, &w0, ws[11], p[19]);
+ word3_muladd(&w2, &w1, &w0, ws[12], p[18]);
+ word3_muladd(&w2, &w1, &w0, ws[13], p[17]);
+ word3_muladd(&w2, &w1, &w0, ws[14], p[16]);
+ word3_muladd(&w2, &w1, &w0, ws[15], p[15]);
+ word3_muladd(&w2, &w1, &w0, ws[16], p[14]);
+ word3_muladd(&w2, &w1, &w0, ws[17], p[13]);
+ word3_muladd(&w2, &w1, &w0, ws[18], p[12]);
+ word3_muladd(&w2, &w1, &w0, ws[19], p[11]);
+ word3_muladd(&w2, &w1, &w0, ws[20], p[10]);
+ word3_muladd(&w2, &w1, &w0, ws[21], p[9]);
+ word3_muladd(&w2, &w1, &w0, ws[22], p[8]);
+ word3_muladd(&w2, &w1, &w0, ws[23], p[7]);
+ word3_muladd(&w2, &w1, &w0, ws[24], p[6]);
+ word3_muladd(&w2, &w1, &w0, ws[25], p[5]);
+ word3_muladd(&w2, &w1, &w0, ws[26], p[4]);
+ word3_muladd(&w2, &w1, &w0, ws[27], p[3]);
+ word3_muladd(&w2, &w1, &w0, ws[28], p[2]);
+ word3_muladd(&w2, &w1, &w0, ws[29], p[1]);
+ word3_add(&w2, &w1, &w0, z[30]);
+ ws[30] = w0 * p_dash;
+ word3_muladd(&w2, &w1, &w0, ws[30], p[0]);
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[0], p[31]);
+ word3_muladd(&w2, &w1, &w0, ws[1], p[30]);
+ word3_muladd(&w2, &w1, &w0, ws[2], p[29]);
+ word3_muladd(&w2, &w1, &w0, ws[3], p[28]);
+ word3_muladd(&w2, &w1, &w0, ws[4], p[27]);
+ word3_muladd(&w2, &w1, &w0, ws[5], p[26]);
+ word3_muladd(&w2, &w1, &w0, ws[6], p[25]);
+ word3_muladd(&w2, &w1, &w0, ws[7], p[24]);
+ word3_muladd(&w2, &w1, &w0, ws[8], p[23]);
+ word3_muladd(&w2, &w1, &w0, ws[9], p[22]);
+ word3_muladd(&w2, &w1, &w0, ws[10], p[21]);
+ word3_muladd(&w2, &w1, &w0, ws[11], p[20]);
+ word3_muladd(&w2, &w1, &w0, ws[12], p[19]);
+ word3_muladd(&w2, &w1, &w0, ws[13], p[18]);
+ word3_muladd(&w2, &w1, &w0, ws[14], p[17]);
+ word3_muladd(&w2, &w1, &w0, ws[15], p[16]);
+ word3_muladd(&w2, &w1, &w0, ws[16], p[15]);
+ word3_muladd(&w2, &w1, &w0, ws[17], p[14]);
+ word3_muladd(&w2, &w1, &w0, ws[18], p[13]);
+ word3_muladd(&w2, &w1, &w0, ws[19], p[12]);
+ word3_muladd(&w2, &w1, &w0, ws[20], p[11]);
+ word3_muladd(&w2, &w1, &w0, ws[21], p[10]);
+ word3_muladd(&w2, &w1, &w0, ws[22], p[9]);
+ word3_muladd(&w2, &w1, &w0, ws[23], p[8]);
+ word3_muladd(&w2, &w1, &w0, ws[24], p[7]);
+ word3_muladd(&w2, &w1, &w0, ws[25], p[6]);
+ word3_muladd(&w2, &w1, &w0, ws[26], p[5]);
+ word3_muladd(&w2, &w1, &w0, ws[27], p[4]);
+ word3_muladd(&w2, &w1, &w0, ws[28], p[3]);
+ word3_muladd(&w2, &w1, &w0, ws[29], p[2]);
+ word3_muladd(&w2, &w1, &w0, ws[30], p[1]);
+ word3_add(&w2, &w1, &w0, z[31]);
+ ws[31] = w0 * p_dash;
+ word3_muladd(&w2, &w1, &w0, ws[31], p[0]);
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[1], p[31]);
+ word3_muladd(&w2, &w1, &w0, ws[2], p[30]);
+ word3_muladd(&w2, &w1, &w0, ws[3], p[29]);
+ word3_muladd(&w2, &w1, &w0, ws[4], p[28]);
+ word3_muladd(&w2, &w1, &w0, ws[5], p[27]);
+ word3_muladd(&w2, &w1, &w0, ws[6], p[26]);
+ word3_muladd(&w2, &w1, &w0, ws[7], p[25]);
+ word3_muladd(&w2, &w1, &w0, ws[8], p[24]);
+ word3_muladd(&w2, &w1, &w0, ws[9], p[23]);
+ word3_muladd(&w2, &w1, &w0, ws[10], p[22]);
+ word3_muladd(&w2, &w1, &w0, ws[11], p[21]);
+ word3_muladd(&w2, &w1, &w0, ws[12], p[20]);
+ word3_muladd(&w2, &w1, &w0, ws[13], p[19]);
+ word3_muladd(&w2, &w1, &w0, ws[14], p[18]);
+ word3_muladd(&w2, &w1, &w0, ws[15], p[17]);
+ word3_muladd(&w2, &w1, &w0, ws[16], p[16]);
+ word3_muladd(&w2, &w1, &w0, ws[17], p[15]);
+ word3_muladd(&w2, &w1, &w0, ws[18], p[14]);
+ word3_muladd(&w2, &w1, &w0, ws[19], p[13]);
+ word3_muladd(&w2, &w1, &w0, ws[20], p[12]);
+ word3_muladd(&w2, &w1, &w0, ws[21], p[11]);
+ word3_muladd(&w2, &w1, &w0, ws[22], p[10]);
+ word3_muladd(&w2, &w1, &w0, ws[23], p[9]);
+ word3_muladd(&w2, &w1, &w0, ws[24], p[8]);
+ word3_muladd(&w2, &w1, &w0, ws[25], p[7]);
+ word3_muladd(&w2, &w1, &w0, ws[26], p[6]);
+ word3_muladd(&w2, &w1, &w0, ws[27], p[5]);
+ word3_muladd(&w2, &w1, &w0, ws[28], p[4]);
+ word3_muladd(&w2, &w1, &w0, ws[29], p[3]);
+ word3_muladd(&w2, &w1, &w0, ws[30], p[2]);
+ word3_muladd(&w2, &w1, &w0, ws[31], p[1]);
+ word3_add(&w2, &w1, &w0, z[32]);
+ ws[0] = w0;
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[2], p[31]);
+ word3_muladd(&w2, &w1, &w0, ws[3], p[30]);
+ word3_muladd(&w2, &w1, &w0, ws[4], p[29]);
+ word3_muladd(&w2, &w1, &w0, ws[5], p[28]);
+ word3_muladd(&w2, &w1, &w0, ws[6], p[27]);
+ word3_muladd(&w2, &w1, &w0, ws[7], p[26]);
+ word3_muladd(&w2, &w1, &w0, ws[8], p[25]);
+ word3_muladd(&w2, &w1, &w0, ws[9], p[24]);
+ word3_muladd(&w2, &w1, &w0, ws[10], p[23]);
+ word3_muladd(&w2, &w1, &w0, ws[11], p[22]);
+ word3_muladd(&w2, &w1, &w0, ws[12], p[21]);
+ word3_muladd(&w2, &w1, &w0, ws[13], p[20]);
+ word3_muladd(&w2, &w1, &w0, ws[14], p[19]);
+ word3_muladd(&w2, &w1, &w0, ws[15], p[18]);
+ word3_muladd(&w2, &w1, &w0, ws[16], p[17]);
+ word3_muladd(&w2, &w1, &w0, ws[17], p[16]);
+ word3_muladd(&w2, &w1, &w0, ws[18], p[15]);
+ word3_muladd(&w2, &w1, &w0, ws[19], p[14]);
+ word3_muladd(&w2, &w1, &w0, ws[20], p[13]);
+ word3_muladd(&w2, &w1, &w0, ws[21], p[12]);
+ word3_muladd(&w2, &w1, &w0, ws[22], p[11]);
+ word3_muladd(&w2, &w1, &w0, ws[23], p[10]);
+ word3_muladd(&w2, &w1, &w0, ws[24], p[9]);
+ word3_muladd(&w2, &w1, &w0, ws[25], p[8]);
+ word3_muladd(&w2, &w1, &w0, ws[26], p[7]);
+ word3_muladd(&w2, &w1, &w0, ws[27], p[6]);
+ word3_muladd(&w2, &w1, &w0, ws[28], p[5]);
+ word3_muladd(&w2, &w1, &w0, ws[29], p[4]);
+ word3_muladd(&w2, &w1, &w0, ws[30], p[3]);
+ word3_muladd(&w2, &w1, &w0, ws[31], p[2]);
+ word3_add(&w2, &w1, &w0, z[33]);
+ ws[1] = w0;
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[3], p[31]);
+ word3_muladd(&w2, &w1, &w0, ws[4], p[30]);
+ word3_muladd(&w2, &w1, &w0, ws[5], p[29]);
+ word3_muladd(&w2, &w1, &w0, ws[6], p[28]);
+ word3_muladd(&w2, &w1, &w0, ws[7], p[27]);
+ word3_muladd(&w2, &w1, &w0, ws[8], p[26]);
+ word3_muladd(&w2, &w1, &w0, ws[9], p[25]);
+ word3_muladd(&w2, &w1, &w0, ws[10], p[24]);
+ word3_muladd(&w2, &w1, &w0, ws[11], p[23]);
+ word3_muladd(&w2, &w1, &w0, ws[12], p[22]);
+ word3_muladd(&w2, &w1, &w0, ws[13], p[21]);
+ word3_muladd(&w2, &w1, &w0, ws[14], p[20]);
+ word3_muladd(&w2, &w1, &w0, ws[15], p[19]);
+ word3_muladd(&w2, &w1, &w0, ws[16], p[18]);
+ word3_muladd(&w2, &w1, &w0, ws[17], p[17]);
+ word3_muladd(&w2, &w1, &w0, ws[18], p[16]);
+ word3_muladd(&w2, &w1, &w0, ws[19], p[15]);
+ word3_muladd(&w2, &w1, &w0, ws[20], p[14]);
+ word3_muladd(&w2, &w1, &w0, ws[21], p[13]);
+ word3_muladd(&w2, &w1, &w0, ws[22], p[12]);
+ word3_muladd(&w2, &w1, &w0, ws[23], p[11]);
+ word3_muladd(&w2, &w1, &w0, ws[24], p[10]);
+ word3_muladd(&w2, &w1, &w0, ws[25], p[9]);
+ word3_muladd(&w2, &w1, &w0, ws[26], p[8]);
+ word3_muladd(&w2, &w1, &w0, ws[27], p[7]);
+ word3_muladd(&w2, &w1, &w0, ws[28], p[6]);
+ word3_muladd(&w2, &w1, &w0, ws[29], p[5]);
+ word3_muladd(&w2, &w1, &w0, ws[30], p[4]);
+ word3_muladd(&w2, &w1, &w0, ws[31], p[3]);
+ word3_add(&w2, &w1, &w0, z[34]);
+ ws[2] = w0;
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[4], p[31]);
+ word3_muladd(&w2, &w1, &w0, ws[5], p[30]);
+ word3_muladd(&w2, &w1, &w0, ws[6], p[29]);
+ word3_muladd(&w2, &w1, &w0, ws[7], p[28]);
+ word3_muladd(&w2, &w1, &w0, ws[8], p[27]);
+ word3_muladd(&w2, &w1, &w0, ws[9], p[26]);
+ word3_muladd(&w2, &w1, &w0, ws[10], p[25]);
+ word3_muladd(&w2, &w1, &w0, ws[11], p[24]);
+ word3_muladd(&w2, &w1, &w0, ws[12], p[23]);
+ word3_muladd(&w2, &w1, &w0, ws[13], p[22]);
+ word3_muladd(&w2, &w1, &w0, ws[14], p[21]);
+ word3_muladd(&w2, &w1, &w0, ws[15], p[20]);
+ word3_muladd(&w2, &w1, &w0, ws[16], p[19]);
+ word3_muladd(&w2, &w1, &w0, ws[17], p[18]);
+ word3_muladd(&w2, &w1, &w0, ws[18], p[17]);
+ word3_muladd(&w2, &w1, &w0, ws[19], p[16]);
+ word3_muladd(&w2, &w1, &w0, ws[20], p[15]);
+ word3_muladd(&w2, &w1, &w0, ws[21], p[14]);
+ word3_muladd(&w2, &w1, &w0, ws[22], p[13]);
+ word3_muladd(&w2, &w1, &w0, ws[23], p[12]);
+ word3_muladd(&w2, &w1, &w0, ws[24], p[11]);
+ word3_muladd(&w2, &w1, &w0, ws[25], p[10]);
+ word3_muladd(&w2, &w1, &w0, ws[26], p[9]);
+ word3_muladd(&w2, &w1, &w0, ws[27], p[8]);
+ word3_muladd(&w2, &w1, &w0, ws[28], p[7]);
+ word3_muladd(&w2, &w1, &w0, ws[29], p[6]);
+ word3_muladd(&w2, &w1, &w0, ws[30], p[5]);
+ word3_muladd(&w2, &w1, &w0, ws[31], p[4]);
+ word3_add(&w2, &w1, &w0, z[35]);
+ ws[3] = w0;
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[5], p[31]);
+ word3_muladd(&w2, &w1, &w0, ws[6], p[30]);
+ word3_muladd(&w2, &w1, &w0, ws[7], p[29]);
+ word3_muladd(&w2, &w1, &w0, ws[8], p[28]);
+ word3_muladd(&w2, &w1, &w0, ws[9], p[27]);
+ word3_muladd(&w2, &w1, &w0, ws[10], p[26]);
+ word3_muladd(&w2, &w1, &w0, ws[11], p[25]);
+ word3_muladd(&w2, &w1, &w0, ws[12], p[24]);
+ word3_muladd(&w2, &w1, &w0, ws[13], p[23]);
+ word3_muladd(&w2, &w1, &w0, ws[14], p[22]);
+ word3_muladd(&w2, &w1, &w0, ws[15], p[21]);
+ word3_muladd(&w2, &w1, &w0, ws[16], p[20]);
+ word3_muladd(&w2, &w1, &w0, ws[17], p[19]);
+ word3_muladd(&w2, &w1, &w0, ws[18], p[18]);
+ word3_muladd(&w2, &w1, &w0, ws[19], p[17]);
+ word3_muladd(&w2, &w1, &w0, ws[20], p[16]);
+ word3_muladd(&w2, &w1, &w0, ws[21], p[15]);
+ word3_muladd(&w2, &w1, &w0, ws[22], p[14]);
+ word3_muladd(&w2, &w1, &w0, ws[23], p[13]);
+ word3_muladd(&w2, &w1, &w0, ws[24], p[12]);
+ word3_muladd(&w2, &w1, &w0, ws[25], p[11]);
+ word3_muladd(&w2, &w1, &w0, ws[26], p[10]);
+ word3_muladd(&w2, &w1, &w0, ws[27], p[9]);
+ word3_muladd(&w2, &w1, &w0, ws[28], p[8]);
+ word3_muladd(&w2, &w1, &w0, ws[29], p[7]);
+ word3_muladd(&w2, &w1, &w0, ws[30], p[6]);
+ word3_muladd(&w2, &w1, &w0, ws[31], p[5]);
+ word3_add(&w2, &w1, &w0, z[36]);
+ ws[4] = w0;
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[6], p[31]);
+ word3_muladd(&w2, &w1, &w0, ws[7], p[30]);
+ word3_muladd(&w2, &w1, &w0, ws[8], p[29]);
+ word3_muladd(&w2, &w1, &w0, ws[9], p[28]);
+ word3_muladd(&w2, &w1, &w0, ws[10], p[27]);
+ word3_muladd(&w2, &w1, &w0, ws[11], p[26]);
+ word3_muladd(&w2, &w1, &w0, ws[12], p[25]);
+ word3_muladd(&w2, &w1, &w0, ws[13], p[24]);
+ word3_muladd(&w2, &w1, &w0, ws[14], p[23]);
+ word3_muladd(&w2, &w1, &w0, ws[15], p[22]);
+ word3_muladd(&w2, &w1, &w0, ws[16], p[21]);
+ word3_muladd(&w2, &w1, &w0, ws[17], p[20]);
+ word3_muladd(&w2, &w1, &w0, ws[18], p[19]);
+ word3_muladd(&w2, &w1, &w0, ws[19], p[18]);
+ word3_muladd(&w2, &w1, &w0, ws[20], p[17]);
+ word3_muladd(&w2, &w1, &w0, ws[21], p[16]);
+ word3_muladd(&w2, &w1, &w0, ws[22], p[15]);
+ word3_muladd(&w2, &w1, &w0, ws[23], p[14]);
+ word3_muladd(&w2, &w1, &w0, ws[24], p[13]);
+ word3_muladd(&w2, &w1, &w0, ws[25], p[12]);
+ word3_muladd(&w2, &w1, &w0, ws[26], p[11]);
+ word3_muladd(&w2, &w1, &w0, ws[27], p[10]);
+ word3_muladd(&w2, &w1, &w0, ws[28], p[9]);
+ word3_muladd(&w2, &w1, &w0, ws[29], p[8]);
+ word3_muladd(&w2, &w1, &w0, ws[30], p[7]);
+ word3_muladd(&w2, &w1, &w0, ws[31], p[6]);
+ word3_add(&w2, &w1, &w0, z[37]);
+ ws[5] = w0;
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[7], p[31]);
+ word3_muladd(&w2, &w1, &w0, ws[8], p[30]);
+ word3_muladd(&w2, &w1, &w0, ws[9], p[29]);
+ word3_muladd(&w2, &w1, &w0, ws[10], p[28]);
+ word3_muladd(&w2, &w1, &w0, ws[11], p[27]);
+ word3_muladd(&w2, &w1, &w0, ws[12], p[26]);
+ word3_muladd(&w2, &w1, &w0, ws[13], p[25]);
+ word3_muladd(&w2, &w1, &w0, ws[14], p[24]);
+ word3_muladd(&w2, &w1, &w0, ws[15], p[23]);
+ word3_muladd(&w2, &w1, &w0, ws[16], p[22]);
+ word3_muladd(&w2, &w1, &w0, ws[17], p[21]);
+ word3_muladd(&w2, &w1, &w0, ws[18], p[20]);
+ word3_muladd(&w2, &w1, &w0, ws[19], p[19]);
+ word3_muladd(&w2, &w1, &w0, ws[20], p[18]);
+ word3_muladd(&w2, &w1, &w0, ws[21], p[17]);
+ word3_muladd(&w2, &w1, &w0, ws[22], p[16]);
+ word3_muladd(&w2, &w1, &w0, ws[23], p[15]);
+ word3_muladd(&w2, &w1, &w0, ws[24], p[14]);
+ word3_muladd(&w2, &w1, &w0, ws[25], p[13]);
+ word3_muladd(&w2, &w1, &w0, ws[26], p[12]);
+ word3_muladd(&w2, &w1, &w0, ws[27], p[11]);
+ word3_muladd(&w2, &w1, &w0, ws[28], p[10]);
+ word3_muladd(&w2, &w1, &w0, ws[29], p[9]);
+ word3_muladd(&w2, &w1, &w0, ws[30], p[8]);
+ word3_muladd(&w2, &w1, &w0, ws[31], p[7]);
+ word3_add(&w2, &w1, &w0, z[38]);
+ ws[6] = w0;
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[8], p[31]);
+ word3_muladd(&w2, &w1, &w0, ws[9], p[30]);
+ word3_muladd(&w2, &w1, &w0, ws[10], p[29]);
+ word3_muladd(&w2, &w1, &w0, ws[11], p[28]);
+ word3_muladd(&w2, &w1, &w0, ws[12], p[27]);
+ word3_muladd(&w2, &w1, &w0, ws[13], p[26]);
+ word3_muladd(&w2, &w1, &w0, ws[14], p[25]);
+ word3_muladd(&w2, &w1, &w0, ws[15], p[24]);
+ word3_muladd(&w2, &w1, &w0, ws[16], p[23]);
+ word3_muladd(&w2, &w1, &w0, ws[17], p[22]);
+ word3_muladd(&w2, &w1, &w0, ws[18], p[21]);
+ word3_muladd(&w2, &w1, &w0, ws[19], p[20]);
+ word3_muladd(&w2, &w1, &w0, ws[20], p[19]);
+ word3_muladd(&w2, &w1, &w0, ws[21], p[18]);
+ word3_muladd(&w2, &w1, &w0, ws[22], p[17]);
+ word3_muladd(&w2, &w1, &w0, ws[23], p[16]);
+ word3_muladd(&w2, &w1, &w0, ws[24], p[15]);
+ word3_muladd(&w2, &w1, &w0, ws[25], p[14]);
+ word3_muladd(&w2, &w1, &w0, ws[26], p[13]);
+ word3_muladd(&w2, &w1, &w0, ws[27], p[12]);
+ word3_muladd(&w2, &w1, &w0, ws[28], p[11]);
+ word3_muladd(&w2, &w1, &w0, ws[29], p[10]);
+ word3_muladd(&w2, &w1, &w0, ws[30], p[9]);
+ word3_muladd(&w2, &w1, &w0, ws[31], p[8]);
+ word3_add(&w2, &w1, &w0, z[39]);
+ ws[7] = w0;
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[9], p[31]);
+ word3_muladd(&w2, &w1, &w0, ws[10], p[30]);
+ word3_muladd(&w2, &w1, &w0, ws[11], p[29]);
+ word3_muladd(&w2, &w1, &w0, ws[12], p[28]);
+ word3_muladd(&w2, &w1, &w0, ws[13], p[27]);
+ word3_muladd(&w2, &w1, &w0, ws[14], p[26]);
+ word3_muladd(&w2, &w1, &w0, ws[15], p[25]);
+ word3_muladd(&w2, &w1, &w0, ws[16], p[24]);
+ word3_muladd(&w2, &w1, &w0, ws[17], p[23]);
+ word3_muladd(&w2, &w1, &w0, ws[18], p[22]);
+ word3_muladd(&w2, &w1, &w0, ws[19], p[21]);
+ word3_muladd(&w2, &w1, &w0, ws[20], p[20]);
+ word3_muladd(&w2, &w1, &w0, ws[21], p[19]);
+ word3_muladd(&w2, &w1, &w0, ws[22], p[18]);
+ word3_muladd(&w2, &w1, &w0, ws[23], p[17]);
+ word3_muladd(&w2, &w1, &w0, ws[24], p[16]);
+ word3_muladd(&w2, &w1, &w0, ws[25], p[15]);
+ word3_muladd(&w2, &w1, &w0, ws[26], p[14]);
+ word3_muladd(&w2, &w1, &w0, ws[27], p[13]);
+ word3_muladd(&w2, &w1, &w0, ws[28], p[12]);
+ word3_muladd(&w2, &w1, &w0, ws[29], p[11]);
+ word3_muladd(&w2, &w1, &w0, ws[30], p[10]);
+ word3_muladd(&w2, &w1, &w0, ws[31], p[9]);
+ word3_add(&w2, &w1, &w0, z[40]);
+ ws[8] = w0;
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[10], p[31]);
+ word3_muladd(&w2, &w1, &w0, ws[11], p[30]);
+ word3_muladd(&w2, &w1, &w0, ws[12], p[29]);
+ word3_muladd(&w2, &w1, &w0, ws[13], p[28]);
+ word3_muladd(&w2, &w1, &w0, ws[14], p[27]);
+ word3_muladd(&w2, &w1, &w0, ws[15], p[26]);
+ word3_muladd(&w2, &w1, &w0, ws[16], p[25]);
+ word3_muladd(&w2, &w1, &w0, ws[17], p[24]);
+ word3_muladd(&w2, &w1, &w0, ws[18], p[23]);
+ word3_muladd(&w2, &w1, &w0, ws[19], p[22]);
+ word3_muladd(&w2, &w1, &w0, ws[20], p[21]);
+ word3_muladd(&w2, &w1, &w0, ws[21], p[20]);
+ word3_muladd(&w2, &w1, &w0, ws[22], p[19]);
+ word3_muladd(&w2, &w1, &w0, ws[23], p[18]);
+ word3_muladd(&w2, &w1, &w0, ws[24], p[17]);
+ word3_muladd(&w2, &w1, &w0, ws[25], p[16]);
+ word3_muladd(&w2, &w1, &w0, ws[26], p[15]);
+ word3_muladd(&w2, &w1, &w0, ws[27], p[14]);
+ word3_muladd(&w2, &w1, &w0, ws[28], p[13]);
+ word3_muladd(&w2, &w1, &w0, ws[29], p[12]);
+ word3_muladd(&w2, &w1, &w0, ws[30], p[11]);
+ word3_muladd(&w2, &w1, &w0, ws[31], p[10]);
+ word3_add(&w2, &w1, &w0, z[41]);
+ ws[9] = w0;
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[11], p[31]);
+ word3_muladd(&w2, &w1, &w0, ws[12], p[30]);
+ word3_muladd(&w2, &w1, &w0, ws[13], p[29]);
+ word3_muladd(&w2, &w1, &w0, ws[14], p[28]);
+ word3_muladd(&w2, &w1, &w0, ws[15], p[27]);
+ word3_muladd(&w2, &w1, &w0, ws[16], p[26]);
+ word3_muladd(&w2, &w1, &w0, ws[17], p[25]);
+ word3_muladd(&w2, &w1, &w0, ws[18], p[24]);
+ word3_muladd(&w2, &w1, &w0, ws[19], p[23]);
+ word3_muladd(&w2, &w1, &w0, ws[20], p[22]);
+ word3_muladd(&w2, &w1, &w0, ws[21], p[21]);
+ word3_muladd(&w2, &w1, &w0, ws[22], p[20]);
+ word3_muladd(&w2, &w1, &w0, ws[23], p[19]);
+ word3_muladd(&w2, &w1, &w0, ws[24], p[18]);
+ word3_muladd(&w2, &w1, &w0, ws[25], p[17]);
+ word3_muladd(&w2, &w1, &w0, ws[26], p[16]);
+ word3_muladd(&w2, &w1, &w0, ws[27], p[15]);
+ word3_muladd(&w2, &w1, &w0, ws[28], p[14]);
+ word3_muladd(&w2, &w1, &w0, ws[29], p[13]);
+ word3_muladd(&w2, &w1, &w0, ws[30], p[12]);
+ word3_muladd(&w2, &w1, &w0, ws[31], p[11]);
+ word3_add(&w2, &w1, &w0, z[42]);
+ ws[10] = w0;
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[12], p[31]);
+ word3_muladd(&w2, &w1, &w0, ws[13], p[30]);
+ word3_muladd(&w2, &w1, &w0, ws[14], p[29]);
+ word3_muladd(&w2, &w1, &w0, ws[15], p[28]);
+ word3_muladd(&w2, &w1, &w0, ws[16], p[27]);
+ word3_muladd(&w2, &w1, &w0, ws[17], p[26]);
+ word3_muladd(&w2, &w1, &w0, ws[18], p[25]);
+ word3_muladd(&w2, &w1, &w0, ws[19], p[24]);
+ word3_muladd(&w2, &w1, &w0, ws[20], p[23]);
+ word3_muladd(&w2, &w1, &w0, ws[21], p[22]);
+ word3_muladd(&w2, &w1, &w0, ws[22], p[21]);
+ word3_muladd(&w2, &w1, &w0, ws[23], p[20]);
+ word3_muladd(&w2, &w1, &w0, ws[24], p[19]);
+ word3_muladd(&w2, &w1, &w0, ws[25], p[18]);
+ word3_muladd(&w2, &w1, &w0, ws[26], p[17]);
+ word3_muladd(&w2, &w1, &w0, ws[27], p[16]);
+ word3_muladd(&w2, &w1, &w0, ws[28], p[15]);
+ word3_muladd(&w2, &w1, &w0, ws[29], p[14]);
+ word3_muladd(&w2, &w1, &w0, ws[30], p[13]);
+ word3_muladd(&w2, &w1, &w0, ws[31], p[12]);
+ word3_add(&w2, &w1, &w0, z[43]);
+ ws[11] = w0;
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[13], p[31]);
+ word3_muladd(&w2, &w1, &w0, ws[14], p[30]);
+ word3_muladd(&w2, &w1, &w0, ws[15], p[29]);
+ word3_muladd(&w2, &w1, &w0, ws[16], p[28]);
+ word3_muladd(&w2, &w1, &w0, ws[17], p[27]);
+ word3_muladd(&w2, &w1, &w0, ws[18], p[26]);
+ word3_muladd(&w2, &w1, &w0, ws[19], p[25]);
+ word3_muladd(&w2, &w1, &w0, ws[20], p[24]);
+ word3_muladd(&w2, &w1, &w0, ws[21], p[23]);
+ word3_muladd(&w2, &w1, &w0, ws[22], p[22]);
+ word3_muladd(&w2, &w1, &w0, ws[23], p[21]);
+ word3_muladd(&w2, &w1, &w0, ws[24], p[20]);
+ word3_muladd(&w2, &w1, &w0, ws[25], p[19]);
+ word3_muladd(&w2, &w1, &w0, ws[26], p[18]);
+ word3_muladd(&w2, &w1, &w0, ws[27], p[17]);
+ word3_muladd(&w2, &w1, &w0, ws[28], p[16]);
+ word3_muladd(&w2, &w1, &w0, ws[29], p[15]);
+ word3_muladd(&w2, &w1, &w0, ws[30], p[14]);
+ word3_muladd(&w2, &w1, &w0, ws[31], p[13]);
+ word3_add(&w2, &w1, &w0, z[44]);
+ ws[12] = w0;
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[14], p[31]);
+ word3_muladd(&w2, &w1, &w0, ws[15], p[30]);
+ word3_muladd(&w2, &w1, &w0, ws[16], p[29]);
+ word3_muladd(&w2, &w1, &w0, ws[17], p[28]);
+ word3_muladd(&w2, &w1, &w0, ws[18], p[27]);
+ word3_muladd(&w2, &w1, &w0, ws[19], p[26]);
+ word3_muladd(&w2, &w1, &w0, ws[20], p[25]);
+ word3_muladd(&w2, &w1, &w0, ws[21], p[24]);
+ word3_muladd(&w2, &w1, &w0, ws[22], p[23]);
+ word3_muladd(&w2, &w1, &w0, ws[23], p[22]);
+ word3_muladd(&w2, &w1, &w0, ws[24], p[21]);
+ word3_muladd(&w2, &w1, &w0, ws[25], p[20]);
+ word3_muladd(&w2, &w1, &w0, ws[26], p[19]);
+ word3_muladd(&w2, &w1, &w0, ws[27], p[18]);
+ word3_muladd(&w2, &w1, &w0, ws[28], p[17]);
+ word3_muladd(&w2, &w1, &w0, ws[29], p[16]);
+ word3_muladd(&w2, &w1, &w0, ws[30], p[15]);
+ word3_muladd(&w2, &w1, &w0, ws[31], p[14]);
+ word3_add(&w2, &w1, &w0, z[45]);
+ ws[13] = w0;
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[15], p[31]);
+ word3_muladd(&w2, &w1, &w0, ws[16], p[30]);
+ word3_muladd(&w2, &w1, &w0, ws[17], p[29]);
+ word3_muladd(&w2, &w1, &w0, ws[18], p[28]);
+ word3_muladd(&w2, &w1, &w0, ws[19], p[27]);
+ word3_muladd(&w2, &w1, &w0, ws[20], p[26]);
+ word3_muladd(&w2, &w1, &w0, ws[21], p[25]);
+ word3_muladd(&w2, &w1, &w0, ws[22], p[24]);
+ word3_muladd(&w2, &w1, &w0, ws[23], p[23]);
+ word3_muladd(&w2, &w1, &w0, ws[24], p[22]);
+ word3_muladd(&w2, &w1, &w0, ws[25], p[21]);
+ word3_muladd(&w2, &w1, &w0, ws[26], p[20]);
+ word3_muladd(&w2, &w1, &w0, ws[27], p[19]);
+ word3_muladd(&w2, &w1, &w0, ws[28], p[18]);
+ word3_muladd(&w2, &w1, &w0, ws[29], p[17]);
+ word3_muladd(&w2, &w1, &w0, ws[30], p[16]);
+ word3_muladd(&w2, &w1, &w0, ws[31], p[15]);
+ word3_add(&w2, &w1, &w0, z[46]);
+ ws[14] = w0;
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[16], p[31]);
+ word3_muladd(&w2, &w1, &w0, ws[17], p[30]);
+ word3_muladd(&w2, &w1, &w0, ws[18], p[29]);
+ word3_muladd(&w2, &w1, &w0, ws[19], p[28]);
+ word3_muladd(&w2, &w1, &w0, ws[20], p[27]);
+ word3_muladd(&w2, &w1, &w0, ws[21], p[26]);
+ word3_muladd(&w2, &w1, &w0, ws[22], p[25]);
+ word3_muladd(&w2, &w1, &w0, ws[23], p[24]);
+ word3_muladd(&w2, &w1, &w0, ws[24], p[23]);
+ word3_muladd(&w2, &w1, &w0, ws[25], p[22]);
+ word3_muladd(&w2, &w1, &w0, ws[26], p[21]);
+ word3_muladd(&w2, &w1, &w0, ws[27], p[20]);
+ word3_muladd(&w2, &w1, &w0, ws[28], p[19]);
+ word3_muladd(&w2, &w1, &w0, ws[29], p[18]);
+ word3_muladd(&w2, &w1, &w0, ws[30], p[17]);
+ word3_muladd(&w2, &w1, &w0, ws[31], p[16]);
+ word3_add(&w2, &w1, &w0, z[47]);
+ ws[15] = w0;
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[17], p[31]);
+ word3_muladd(&w2, &w1, &w0, ws[18], p[30]);
+ word3_muladd(&w2, &w1, &w0, ws[19], p[29]);
+ word3_muladd(&w2, &w1, &w0, ws[20], p[28]);
+ word3_muladd(&w2, &w1, &w0, ws[21], p[27]);
+ word3_muladd(&w2, &w1, &w0, ws[22], p[26]);
+ word3_muladd(&w2, &w1, &w0, ws[23], p[25]);
+ word3_muladd(&w2, &w1, &w0, ws[24], p[24]);
+ word3_muladd(&w2, &w1, &w0, ws[25], p[23]);
+ word3_muladd(&w2, &w1, &w0, ws[26], p[22]);
+ word3_muladd(&w2, &w1, &w0, ws[27], p[21]);
+ word3_muladd(&w2, &w1, &w0, ws[28], p[20]);
+ word3_muladd(&w2, &w1, &w0, ws[29], p[19]);
+ word3_muladd(&w2, &w1, &w0, ws[30], p[18]);
+ word3_muladd(&w2, &w1, &w0, ws[31], p[17]);
+ word3_add(&w2, &w1, &w0, z[48]);
+ ws[16] = w0;
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[18], p[31]);
+ word3_muladd(&w2, &w1, &w0, ws[19], p[30]);
+ word3_muladd(&w2, &w1, &w0, ws[20], p[29]);
+ word3_muladd(&w2, &w1, &w0, ws[21], p[28]);
+ word3_muladd(&w2, &w1, &w0, ws[22], p[27]);
+ word3_muladd(&w2, &w1, &w0, ws[23], p[26]);
+ word3_muladd(&w2, &w1, &w0, ws[24], p[25]);
+ word3_muladd(&w2, &w1, &w0, ws[25], p[24]);
+ word3_muladd(&w2, &w1, &w0, ws[26], p[23]);
+ word3_muladd(&w2, &w1, &w0, ws[27], p[22]);
+ word3_muladd(&w2, &w1, &w0, ws[28], p[21]);
+ word3_muladd(&w2, &w1, &w0, ws[29], p[20]);
+ word3_muladd(&w2, &w1, &w0, ws[30], p[19]);
+ word3_muladd(&w2, &w1, &w0, ws[31], p[18]);
+ word3_add(&w2, &w1, &w0, z[49]);
+ ws[17] = w0;
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[19], p[31]);
+ word3_muladd(&w2, &w1, &w0, ws[20], p[30]);
+ word3_muladd(&w2, &w1, &w0, ws[21], p[29]);
+ word3_muladd(&w2, &w1, &w0, ws[22], p[28]);
+ word3_muladd(&w2, &w1, &w0, ws[23], p[27]);
+ word3_muladd(&w2, &w1, &w0, ws[24], p[26]);
+ word3_muladd(&w2, &w1, &w0, ws[25], p[25]);
+ word3_muladd(&w2, &w1, &w0, ws[26], p[24]);
+ word3_muladd(&w2, &w1, &w0, ws[27], p[23]);
+ word3_muladd(&w2, &w1, &w0, ws[28], p[22]);
+ word3_muladd(&w2, &w1, &w0, ws[29], p[21]);
+ word3_muladd(&w2, &w1, &w0, ws[30], p[20]);
+ word3_muladd(&w2, &w1, &w0, ws[31], p[19]);
+ word3_add(&w2, &w1, &w0, z[50]);
+ ws[18] = w0;
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[20], p[31]);
+ word3_muladd(&w2, &w1, &w0, ws[21], p[30]);
+ word3_muladd(&w2, &w1, &w0, ws[22], p[29]);
+ word3_muladd(&w2, &w1, &w0, ws[23], p[28]);
+ word3_muladd(&w2, &w1, &w0, ws[24], p[27]);
+ word3_muladd(&w2, &w1, &w0, ws[25], p[26]);
+ word3_muladd(&w2, &w1, &w0, ws[26], p[25]);
+ word3_muladd(&w2, &w1, &w0, ws[27], p[24]);
+ word3_muladd(&w2, &w1, &w0, ws[28], p[23]);
+ word3_muladd(&w2, &w1, &w0, ws[29], p[22]);
+ word3_muladd(&w2, &w1, &w0, ws[30], p[21]);
+ word3_muladd(&w2, &w1, &w0, ws[31], p[20]);
+ word3_add(&w2, &w1, &w0, z[51]);
+ ws[19] = w0;
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[21], p[31]);
+ word3_muladd(&w2, &w1, &w0, ws[22], p[30]);
+ word3_muladd(&w2, &w1, &w0, ws[23], p[29]);
+ word3_muladd(&w2, &w1, &w0, ws[24], p[28]);
+ word3_muladd(&w2, &w1, &w0, ws[25], p[27]);
+ word3_muladd(&w2, &w1, &w0, ws[26], p[26]);
+ word3_muladd(&w2, &w1, &w0, ws[27], p[25]);
+ word3_muladd(&w2, &w1, &w0, ws[28], p[24]);
+ word3_muladd(&w2, &w1, &w0, ws[29], p[23]);
+ word3_muladd(&w2, &w1, &w0, ws[30], p[22]);
+ word3_muladd(&w2, &w1, &w0, ws[31], p[21]);
+ word3_add(&w2, &w1, &w0, z[52]);
+ ws[20] = w0;
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[22], p[31]);
+ word3_muladd(&w2, &w1, &w0, ws[23], p[30]);
+ word3_muladd(&w2, &w1, &w0, ws[24], p[29]);
+ word3_muladd(&w2, &w1, &w0, ws[25], p[28]);
+ word3_muladd(&w2, &w1, &w0, ws[26], p[27]);
+ word3_muladd(&w2, &w1, &w0, ws[27], p[26]);
+ word3_muladd(&w2, &w1, &w0, ws[28], p[25]);
+ word3_muladd(&w2, &w1, &w0, ws[29], p[24]);
+ word3_muladd(&w2, &w1, &w0, ws[30], p[23]);
+ word3_muladd(&w2, &w1, &w0, ws[31], p[22]);
+ word3_add(&w2, &w1, &w0, z[53]);
+ ws[21] = w0;
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[23], p[31]);
+ word3_muladd(&w2, &w1, &w0, ws[24], p[30]);
+ word3_muladd(&w2, &w1, &w0, ws[25], p[29]);
+ word3_muladd(&w2, &w1, &w0, ws[26], p[28]);
+ word3_muladd(&w2, &w1, &w0, ws[27], p[27]);
+ word3_muladd(&w2, &w1, &w0, ws[28], p[26]);
+ word3_muladd(&w2, &w1, &w0, ws[29], p[25]);
+ word3_muladd(&w2, &w1, &w0, ws[30], p[24]);
+ word3_muladd(&w2, &w1, &w0, ws[31], p[23]);
+ word3_add(&w2, &w1, &w0, z[54]);
+ ws[22] = w0;
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[24], p[31]);
+ word3_muladd(&w2, &w1, &w0, ws[25], p[30]);
+ word3_muladd(&w2, &w1, &w0, ws[26], p[29]);
+ word3_muladd(&w2, &w1, &w0, ws[27], p[28]);
+ word3_muladd(&w2, &w1, &w0, ws[28], p[27]);
+ word3_muladd(&w2, &w1, &w0, ws[29], p[26]);
+ word3_muladd(&w2, &w1, &w0, ws[30], p[25]);
+ word3_muladd(&w2, &w1, &w0, ws[31], p[24]);
+ word3_add(&w2, &w1, &w0, z[55]);
+ ws[23] = w0;
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[25], p[31]);
+ word3_muladd(&w2, &w1, &w0, ws[26], p[30]);
+ word3_muladd(&w2, &w1, &w0, ws[27], p[29]);
+ word3_muladd(&w2, &w1, &w0, ws[28], p[28]);
+ word3_muladd(&w2, &w1, &w0, ws[29], p[27]);
+ word3_muladd(&w2, &w1, &w0, ws[30], p[26]);
+ word3_muladd(&w2, &w1, &w0, ws[31], p[25]);
+ word3_add(&w2, &w1, &w0, z[56]);
+ ws[24] = w0;
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[26], p[31]);
+ word3_muladd(&w2, &w1, &w0, ws[27], p[30]);
+ word3_muladd(&w2, &w1, &w0, ws[28], p[29]);
+ word3_muladd(&w2, &w1, &w0, ws[29], p[28]);
+ word3_muladd(&w2, &w1, &w0, ws[30], p[27]);
+ word3_muladd(&w2, &w1, &w0, ws[31], p[26]);
+ word3_add(&w2, &w1, &w0, z[57]);
+ ws[25] = w0;
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[27], p[31]);
+ word3_muladd(&w2, &w1, &w0, ws[28], p[30]);
+ word3_muladd(&w2, &w1, &w0, ws[29], p[29]);
+ word3_muladd(&w2, &w1, &w0, ws[30], p[28]);
+ word3_muladd(&w2, &w1, &w0, ws[31], p[27]);
+ word3_add(&w2, &w1, &w0, z[58]);
+ ws[26] = w0;
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[28], p[31]);
+ word3_muladd(&w2, &w1, &w0, ws[29], p[30]);
+ word3_muladd(&w2, &w1, &w0, ws[30], p[29]);
+ word3_muladd(&w2, &w1, &w0, ws[31], p[28]);
+ word3_add(&w2, &w1, &w0, z[59]);
+ ws[27] = w0;
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[29], p[31]);
+ word3_muladd(&w2, &w1, &w0, ws[30], p[30]);
+ word3_muladd(&w2, &w1, &w0, ws[31], p[29]);
+ word3_add(&w2, &w1, &w0, z[60]);
+ ws[28] = w0;
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[30], p[31]);
+ word3_muladd(&w2, &w1, &w0, ws[31], p[30]);
+ word3_add(&w2, &w1, &w0, z[61]);
+ ws[29] = w0;
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_muladd(&w2, &w1, &w0, ws[31], p[31]);
+ word3_add(&w2, &w1, &w0, z[62]);
+ ws[30] = w0;
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_add(&w2, &w1, &w0, z[63]);
+ ws[31] = w0;
+ w0 = w1; w1 = w2; w2 = 0;
+ word3_add(&w2, &w1, &w0, z[65]);
+ ws[32] = w0;
+ ws[33] = w1;
+ word borrow = bigint_sub3(ws + 32 + 1, ws, 32 + 1, p, 32);
+ CT::conditional_copy_mem(borrow, z, ws, ws + 33, 33);
+ clear_mem(z + 32, 2*(32+1) - 32);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/math/numbertheory/curve_nistp.h b/comm/third_party/botan/src/lib/math/numbertheory/curve_nistp.h
new file mode 100644
index 0000000000..19d1bd2566
--- /dev/null
+++ b/comm/third_party/botan/src/lib/math/numbertheory/curve_nistp.h
@@ -0,0 +1,49 @@
+/*
+* Arithmetic operations specialized for NIST ECC primes
+* (C) 2014,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_NIST_PRIMES_H_
+#define BOTAN_NIST_PRIMES_H_
+
+#include <botan/bigint.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(curve_nistp.h)
+
+namespace Botan {
+
+/**
+* NIST Prime reduction functions.
+*
+* Reduces the value in place
+*
+* ws is a workspace function which is used as a temporary,
+* and will be resized as needed.
+*/
+BOTAN_PUBLIC_API(2,0) const BigInt& prime_p521();
+BOTAN_PUBLIC_API(2,0) void redc_p521(BigInt& x, secure_vector<word>& ws);
+
+/*
+Previously this macro indicated if the P-{192,224,256,384} reducers
+were available. Now they are always enabled and this macro has no meaning.
+The define will be removed in a future major release.
+*/
+#define BOTAN_HAS_NIST_PRIME_REDUCERS_W32
+
+BOTAN_PUBLIC_API(2,0) const BigInt& prime_p384();
+BOTAN_PUBLIC_API(2,0) void redc_p384(BigInt& x, secure_vector<word>& ws);
+
+BOTAN_PUBLIC_API(2,0) const BigInt& prime_p256();
+BOTAN_PUBLIC_API(2,0) void redc_p256(BigInt& x, secure_vector<word>& ws);
+
+BOTAN_PUBLIC_API(2,0) const BigInt& prime_p224();
+BOTAN_PUBLIC_API(2,0) void redc_p224(BigInt& x, secure_vector<word>& ws);
+
+BOTAN_PUBLIC_API(2,0) const BigInt& prime_p192();
+BOTAN_PUBLIC_API(2,0) void redc_p192(BigInt& x, secure_vector<word>& ws);
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/math/numbertheory/dsa_gen.cpp b/comm/third_party/botan/src/lib/math/numbertheory/dsa_gen.cpp
new file mode 100644
index 0000000000..a5efbc2662
--- /dev/null
+++ b/comm/third_party/botan/src/lib/math/numbertheory/dsa_gen.cpp
@@ -0,0 +1,136 @@
+/*
+* DSA Parameter Generation
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/numthry.h>
+#include <botan/hash.h>
+#include <botan/reducer.h>
+#include <botan/rng.h>
+
+namespace Botan {
+
+namespace {
+
+/*
+* Check if this size is allowed by FIPS 186-3
+*/
+bool fips186_3_valid_size(size_t pbits, size_t qbits)
+ {
+ if(qbits == 160)
+ return (pbits == 1024);
+
+ if(qbits == 224)
+ return (pbits == 2048);
+
+ if(qbits == 256)
+ return (pbits == 2048 || pbits == 3072);
+
+ return false;
+ }
+
+}
+
+/*
+* Attempt DSA prime generation with given seed
+*/
+bool generate_dsa_primes(RandomNumberGenerator& rng,
+ BigInt& p, BigInt& q,
+ size_t pbits, size_t qbits,
+ const std::vector<uint8_t>& seed_c,
+ size_t offset)
+ {
+ if(!fips186_3_valid_size(pbits, qbits))
+ throw Invalid_Argument(
+ "FIPS 186-3 does not allow DSA domain parameters of " +
+ std::to_string(pbits) + "/" + std::to_string(qbits) + " bits long");
+
+ if(seed_c.size() * 8 < qbits)
+ throw Invalid_Argument(
+ "Generating a DSA parameter set with a " + std::to_string(qbits) +
+ " bit long q requires a seed at least as many bits long");
+
+ const std::string hash_name = "SHA-" + std::to_string(qbits);
+ std::unique_ptr<HashFunction> hash(HashFunction::create_or_throw(hash_name));
+
+ const size_t HASH_SIZE = hash->output_length();
+
+ class Seed final
+ {
+ public:
+ explicit Seed(const std::vector<uint8_t>& s) : m_seed(s) {}
+
+ const std::vector<uint8_t>& value() const { return m_seed; }
+
+ Seed& operator++()
+ {
+ for(size_t j = m_seed.size(); j > 0; --j)
+ if(++m_seed[j-1])
+ break;
+ return (*this);
+ }
+ private:
+ std::vector<uint8_t> m_seed;
+ };
+
+ Seed seed(seed_c);
+
+ q.binary_decode(hash->process(seed.value()));
+ q.set_bit(qbits-1);
+ q.set_bit(0);
+
+ if(!is_prime(q, rng, 128, true))
+ return false;
+
+ const size_t n = (pbits-1) / (HASH_SIZE * 8),
+ b = (pbits-1) % (HASH_SIZE * 8);
+
+ BigInt X;
+ std::vector<uint8_t> V(HASH_SIZE * (n+1));
+
+ Modular_Reducer mod_2q(2*q);
+
+ for(size_t j = 0; j != 4*pbits; ++j)
+ {
+ for(size_t k = 0; k <= n; ++k)
+ {
+ ++seed;
+ hash->update(seed.value());
+ hash->final(&V[HASH_SIZE * (n-k)]);
+ }
+
+ if(j >= offset)
+ {
+ X.binary_decode(&V[HASH_SIZE - 1 - b/8],
+ V.size() - (HASH_SIZE - 1 - b/8));
+ X.set_bit(pbits-1);
+
+ p = X - (mod_2q.reduce(X) - 1);
+
+ if(p.bits() == pbits && is_prime(p, rng, 128, true))
+ return true;
+ }
+ }
+ return false;
+ }
+
+/*
+* Generate DSA Primes
+*/
+std::vector<uint8_t> generate_dsa_primes(RandomNumberGenerator& rng,
+ BigInt& p, BigInt& q,
+ size_t pbits, size_t qbits)
+ {
+ while(true)
+ {
+ std::vector<uint8_t> seed(qbits / 8);
+ rng.randomize(seed.data(), seed.size());
+
+ if(generate_dsa_primes(rng, p, q, pbits, qbits, seed))
+ return seed;
+ }
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/math/numbertheory/info.txt b/comm/third_party/botan/src/lib/math/numbertheory/info.txt
new file mode 100644
index 0000000000..4b241c1208
--- /dev/null
+++ b/comm/third_party/botan/src/lib/math/numbertheory/info.txt
@@ -0,0 +1,22 @@
+<defines>
+NUMBERTHEORY -> 20131128
+</defines>
+
+<header:public>
+curve_nistp.h
+numthry.h
+pow_mod.h
+reducer.h
+monty.h
+</header:public>
+
+<header:internal>
+primality.h
+monty_exp.h
+</header:internal>
+
+<requires>
+bigint
+hash
+rng
+</requires>
diff --git a/comm/third_party/botan/src/lib/math/numbertheory/jacobi.cpp b/comm/third_party/botan/src/lib/math/numbertheory/jacobi.cpp
new file mode 100644
index 0000000000..284fc2b204
--- /dev/null
+++ b/comm/third_party/botan/src/lib/math/numbertheory/jacobi.cpp
@@ -0,0 +1,52 @@
+/*
+* Jacobi Function
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/numthry.h>
+
+namespace Botan {
+
+/*
+* Calculate the Jacobi symbol
+*/
+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");
+
+ 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(x.is_zero())
+ return 0;
+
+ 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;
+ }
+
+ if(x % 4 == 3 && y % 4 == 3)
+ J = -J;
+ std::swap(x, y);
+ }
+ return J;
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/math/numbertheory/make_prm.cpp b/comm/third_party/botan/src/lib/math/numbertheory/make_prm.cpp
new file mode 100644
index 0000000000..404e301046
--- /dev/null
+++ b/comm/third_party/botan/src/lib/math/numbertheory/make_prm.cpp
@@ -0,0 +1,293 @@
+/*
+* Prime Generation
+* (C) 1999-2007,2018,2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/numthry.h>
+#include <botan/rng.h>
+#include <botan/internal/bit_ops.h>
+#include <botan/loadstor.h>
+#include <botan/reducer.h>
+#include <botan/internal/primality.h>
+#include <algorithm>
+
+namespace Botan {
+
+namespace {
+
+class Prime_Sieve final
+ {
+ public:
+ Prime_Sieve(const BigInt& init_value, size_t sieve_size) :
+ m_sieve(std::min(sieve_size, PRIME_TABLE_SIZE))
+ {
+ for(size_t i = 0; i != m_sieve.size(); ++i)
+ m_sieve[i] = static_cast<uint16_t>(init_value % PRIMES[i]);
+ }
+
+ void step(word increment)
+ {
+ for(size_t i = 0; i != m_sieve.size(); ++i)
+ {
+ m_sieve[i] = (m_sieve[i] + increment) % PRIMES[i];
+ }
+ }
+
+ bool passes(bool check_2p1 = false) const
+ {
+ for(size_t i = 0; i != m_sieve.size(); ++i)
+ {
+ /*
+ In this case, p is a multiple of PRIMES[i]
+ */
+ if(m_sieve[i] == 0)
+ return false;
+
+ if(check_2p1)
+ {
+ /*
+ In this case, 2*p+1 will be a multiple of PRIMES[i]
+
+ So if potentially generating a safe prime, we want to
+ avoid this value because 2*p+1 will certainly not be prime.
+
+ See "Safe Prime Generation with a Combined Sieve" M. Wiener
+ https://eprint.iacr.org/2003/186.pdf
+ */
+ if(m_sieve[i] == (PRIMES[i] - 1) / 2)
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private:
+ std::vector<uint16_t> m_sieve;
+ };
+
+}
+
+
+/*
+* Generate a random prime
+*/
+BigInt random_prime(RandomNumberGenerator& rng,
+ size_t bits, const BigInt& coprime,
+ size_t equiv, size_t modulo,
+ size_t prob)
+ {
+ 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)
+ {
+ throw Invalid_Argument("random_prime: invalid coprime");
+ }
+ if(modulo == 0)
+ {
+ throw Invalid_Argument("random_prime: Invalid modulo value");
+ }
+
+ equiv %= modulo;
+
+ if(equiv == 0)
+ throw Invalid_Argument("random_prime Invalid value for equiv/modulo");
+
+ // Handle small values:
+
+ if(bits <= 16)
+ {
+ if(equiv != 1 || modulo != 2 || coprime != 0)
+ throw Not_Implemented("random_prime equiv/modulo/coprime options not usable for small primes");
+
+ if(bits == 2)
+ {
+ return ((rng.next_byte() % 2) ? 2 : 3);
+ }
+ else if(bits == 3)
+ {
+ return ((rng.next_byte() % 2) ? 5 : 7);
+ }
+ else if(bits == 4)
+ {
+ return ((rng.next_byte() % 2) ? 11 : 13);
+ }
+ else
+ {
+ for(;;)
+ {
+ // This is slightly biased, but for small primes it does not seem to matter
+ uint8_t b[4];
+ rng.randomize(b, 4);
+ const size_t idx = load_le<uint32_t>(b, 0) % PRIME_TABLE_SIZE;
+ const uint16_t small_prime = PRIMES[idx];
+
+ if(high_bit(small_prime) == bits)
+ return small_prime;
+ }
+ }
+ }
+
+ const size_t MAX_ATTEMPTS = 32*1024;
+
+ const size_t mr_trials = miller_rabin_test_iterations(bits, prob, true);
+
+ while(true)
+ {
+ BigInt p(rng, bits);
+
+ // Force lowest and two top bits on
+ p.set_bit(bits - 1);
+ p.set_bit(bits - 2);
+ p.set_bit(0);
+
+ // Force p to be equal to equiv mod modulo
+ p += (modulo - (p % modulo)) + equiv;
+
+ Prime_Sieve sieve(p, bits);
+
+ for(size_t attempt = 0; attempt <= MAX_ATTEMPTS; ++attempt)
+ {
+ p += modulo;
+
+ sieve.step(modulo);
+
+ // p can be even if modulo is odd, continue on in that case
+ if(p.is_even() || sieve.passes(true) == false)
+ continue;
+
+ Modular_Reducer mod_p(p);
+
+ if(coprime > 1)
+ {
+ /*
+ First do a single M-R iteration to quickly elimate most non-primes,
+ before doing the coprimality check which is expensive
+ */
+ if(is_miller_rabin_probable_prime(p, mod_p, rng, 1) == false)
+ continue;
+
+ /*
+ * Check if p - 1 and coprime are relatively prime, using gcd.
+ * The gcd computation is const-time
+ */
+ if(gcd(p - 1, coprime) > 1)
+ continue;
+ }
+
+ if(p.bits() > bits)
+ break;
+
+ if(is_miller_rabin_probable_prime(p, mod_p, rng, mr_trials) == false)
+ continue;
+
+ if(prob > 32 && !is_lucas_probable_prime(p, mod_p))
+ continue;
+
+ return p;
+ }
+ }
+ }
+
+BigInt generate_rsa_prime(RandomNumberGenerator& keygen_rng,
+ RandomNumberGenerator& prime_test_rng,
+ size_t bits,
+ const BigInt& coprime,
+ size_t prob)
+ {
+ if(bits < 512)
+ throw Invalid_Argument("generate_rsa_prime bits too small");
+
+ /*
+ * The restriction on coprime <= 64 bits is arbitrary but generally speaking
+ * very large RSA public exponents are a bad idea both for performance and due
+ * to attacks on small d.
+ */
+ if(coprime <= 1 || coprime.is_even() || coprime.bits() > 64)
+ throw Invalid_Argument("generate_rsa_prime coprime must be small odd positive integer");
+
+ const size_t MAX_ATTEMPTS = 32*1024;
+
+ const size_t mr_trials = miller_rabin_test_iterations(bits, prob, true);
+
+ while(true)
+ {
+ BigInt p(keygen_rng, bits);
+
+ // Force high two bits so multiplication always results in expected n bit integer
+ p.set_bit(bits - 1);
+ p.set_bit(bits - 2);
+ p.set_bit(0);
+
+ const word step = 2;
+
+ Prime_Sieve sieve(p, bits);
+
+ for(size_t attempt = 0; attempt <= MAX_ATTEMPTS; ++attempt)
+ {
+ p += step;
+
+ sieve.step(step);
+
+ if(sieve.passes() == false)
+ continue;
+
+ Modular_Reducer mod_p(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)
+ continue;
+
+ /*
+ * Check if p - 1 and coprime are relatively prime.
+ */
+ if(gcd(p - 1, coprime) > 1)
+ continue;
+
+ if(p.bits() > bits)
+ break;
+
+ if(is_miller_rabin_probable_prime(p, mod_p, prime_test_rng, mr_trials) == true)
+ return p;
+ }
+ }
+ }
+
+/*
+* Generate a random safe prime
+*/
+BigInt random_safe_prime(RandomNumberGenerator& rng, size_t bits)
+ {
+ if(bits <= 64)
+ throw Invalid_Argument("random_safe_prime: Can't make a prime of " +
+ std::to_string(bits) + " bits");
+
+ const size_t error_bound = 128;
+
+ BigInt q, p;
+ for(;;)
+ {
+ /*
+ Generate q == 2 (mod 3), since otherwise [in the case of q == 1 (mod 3)],
+ 2*q+1 == 3 (mod 3) and so certainly not prime.
+ */
+ q = random_prime(rng, bits - 1, 0, 2, 3, error_bound);
+ p = (q << 1) + 1;
+
+ if(is_prime(p, rng, error_bound, true))
+ {
+ return p;
+ }
+ }
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/math/numbertheory/mod_inv.cpp b/comm/third_party/botan/src/lib/math/numbertheory/mod_inv.cpp
new file mode 100644
index 0000000000..ec3bb33f00
--- /dev/null
+++ b/comm/third_party/botan/src/lib/math/numbertheory/mod_inv.cpp
@@ -0,0 +1,356 @@
+/*
+* (C) 1999-2011,2016,2018,2019,2020 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/numthry.h>
+#include <botan/divide.h>
+#include <botan/internal/ct_utils.h>
+#include <botan/internal/mp_core.h>
+#include <botan/internal/rounding.h>
+
+namespace Botan {
+
+/*
+Sets result to a^-1 * 2^k mod a
+with n <= k <= 2n
+Returns k
+
+"The Montgomery Modular Inverse - Revisited" Çetin Koç, E. Savas
+https://citeseerx.ist.psu.edu/viewdoc/citations?doi=10.1.1.75.8377
+
+A const time implementation of this algorithm is described in
+"Constant Time Modular Inversion" Joppe W. Bos
+http://www.joppebos.com/files/CTInversion.pdf
+*/
+size_t almost_montgomery_inverse(BigInt& result,
+ const BigInt& a,
+ const BigInt& p)
+ {
+ size_t k = 0;
+
+ BigInt u = p, v = a, r = 0, s = 1;
+
+ while(v > 0)
+ {
+ if(u.is_even())
+ {
+ u >>= 1;
+ s <<= 1;
+ }
+ else if(v.is_even())
+ {
+ v >>= 1;
+ r <<= 1;
+ }
+ else if(u > v)
+ {
+ u -= v;
+ u >>= 1;
+ r += s;
+ s <<= 1;
+ }
+ else
+ {
+ v -= u;
+ v >>= 1;
+ s += r;
+ r <<= 1;
+ }
+
+ ++k;
+ }
+
+ if(r >= p)
+ {
+ r -= p;
+ }
+
+ result = p - r;
+
+ return k;
+ }
+
+BigInt normalized_montgomery_inverse(const BigInt& a, const BigInt& p)
+ {
+ BigInt r;
+ size_t k = almost_montgomery_inverse(r, a, p);
+
+ for(size_t i = 0; i != k; ++i)
+ {
+ if(r.is_odd())
+ r += p;
+ r >>= 1;
+ }
+
+ return r;
+ }
+
+namespace {
+
+BigInt inverse_mod_odd_modulus(const BigInt& n, const BigInt& mod)
+ {
+ // Caller should assure these preconditions:
+ BOTAN_DEBUG_ASSERT(n.is_positive());
+ BOTAN_DEBUG_ASSERT(mod.is_positive());
+ BOTAN_DEBUG_ASSERT(n < mod);
+ BOTAN_DEBUG_ASSERT(mod >= 3 && mod.is_odd());
+
+ /*
+ This uses a modular inversion algorithm designed by Niels Möller
+ and implemented in Nettle. The same algorithm was later also
+ adapted to GMP in mpn_sec_invert.
+
+ It can be easily implemented in a way that does not depend on
+ secret branches or memory lookups, providing resistance against
+ some forms of side channel attack.
+
+ There is also a description of the algorithm in Appendix 5 of "Fast
+ Software Polynomial Multiplication on ARM Processors using the NEON Engine"
+ by Danilo Câmara, Conrado P. L. Gouvêa, Julio López, and Ricardo
+ Dahab in LNCS 8182
+ https://conradoplg.cryptoland.net/files/2010/12/mocrysen13.pdf
+
+ Thanks to Niels for creating the algorithm, explaining some things
+ about it, and the reference to the paper.
+ */
+
+ const size_t mod_words = mod.sig_words();
+ BOTAN_ASSERT(mod_words > 0, "Not empty");
+
+ secure_vector<word> tmp_mem(5*mod_words);
+
+ word* v_w = &tmp_mem[0];
+ 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;
+ // v_w = 0
+
+ // compute (mod + 1) / 2 which [because mod is odd] is equal to
+ // (mod / 2) + 1
+ copy_mem(mp1o2, mod.data(), std::min(mod.size(), mod_words));
+ bigint_shr1(mp1o2, mod_words, 0, 1);
+ word carry = bigint_add2_nc(mp1o2, mod_words, u_w, 1);
+ BOTAN_ASSERT_NOMSG(carry == 0);
+
+ // Only n.bits() + mod.bits() iterations are required, but avoid leaking the size of n
+ const size_t execs = 2 * mod.bits();
+
+ for(size_t i = 0; i != execs; ++i)
+ {
+ 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);
+
+ //if(underflow) { b -= a; a = abs(a); swap(u, v); }
+ bigint_cnd_add(underflow, b_w, a_w, mod_words);
+ bigint_cnd_abs(underflow, a_w, mod_words);
+ bigint_cnd_swap(underflow, u_w, v_w, mod_words);
+
+ // a >>= 1
+ bigint_shr1(a_w, mod_words, 0, 1);
+
+ //if(odd_a) u -= v;
+ 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);
+
+ const word odd_u = u_w[0] & 1;
+
+ // u >>= 1
+ bigint_shr1(u_w, mod_words, 0, 1);
+
+ //if(odd_u) u += mp1o2;
+ bigint_cnd_add(odd_u, u_w, mp1o2, mod_words);
+ }
+
+ auto a_is_0 = CT::Mask<word>::set();
+ for(size_t i = 0; i != mod_words; ++i)
+ a_is_0 &= CT::Mask<word>::is_zero(a_w[i]);
+
+ auto b_is_1 = CT::Mask<word>::is_equal(b_w[0], 1);
+ for(size_t i = 1; i != mod_words; ++i)
+ b_is_1 &= CT::Mask<word>::is_zero(b_w[i]);
+
+ BOTAN_ASSERT(a_is_0.is_set(), "A is zero");
+
+ // if b != 1 then gcd(n,mod) > 1 and inverse does not exist
+ // in which case zero out the result to indicate this
+ (~b_is_1).if_set_zero_out(v_w, mod_words);
+
+ /*
+ * We've placed the result in the lowest words of the temp buffer.
+ * So just clear out the other values and then give that buffer to a
+ * BigInt.
+ */
+ clear_mem(&tmp_mem[mod_words], 4*mod_words);
+
+ CT::unpoison(tmp_mem.data(), tmp_mem.size());
+
+ BigInt r;
+ r.swap_reg(tmp_mem);
+ return r;
+ }
+
+BigInt inverse_mod_pow2(const BigInt& a1, size_t k)
+ {
+ /*
+ * From "A New Algorithm for Inversion mod p^k" by Çetin Kaya Koç
+ * https://eprint.iacr.org/2017/411.pdf sections 5 and 7.
+ */
+
+ if(a1.is_even())
+ return 0;
+
+ BigInt a = a1;
+ a.mask_bits(k);
+
+ BigInt b = 1;
+ BigInt X = 0;
+ BigInt newb;
+
+ const size_t a_words = a.sig_words();
+
+ X.grow_to(round_up(k, BOTAN_MP_WORD_BITS) / BOTAN_MP_WORD_BITS);
+ b.grow_to(a_words);
+
+ /*
+ Hide the exact value of k. k is anyway known to word length
+ 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);
+
+ for(size_t i = 0; i != iter; ++i)
+ {
+ const bool b0 = b.get_bit(0);
+ X.conditionally_set_bit(i, b0);
+ newb = b - a;
+ b.ct_cond_assign(b0, newb);
+ b >>= 1;
+ }
+
+ X.mask_bits(k);
+ X.const_time_unpoison();
+ return X;
+ }
+
+}
+
+BigInt inverse_mod(const BigInt& n, const BigInt& mod)
+ {
+ if(mod.is_zero())
+ throw BigInt::DivideByZero();
+ if(mod.is_negative() || n.is_negative())
+ throw Invalid_Argument("inverse_mod: arguments must be non-negative");
+ if(n.is_zero() || (n.is_even() && mod.is_even()))
+ return 0;
+
+ if(mod.is_odd())
+ {
+ /*
+ Fastpath for common case. This leaks information if n > mod
+ but we don't guarantee const time behavior in that case.
+ */
+ if(n < mod)
+ return inverse_mod_odd_modulus(n, mod);
+ else
+ return inverse_mod_odd_modulus(ct_modulo(n, mod), mod);
+ }
+
+ const size_t mod_lz = low_zero_bits(mod);
+ BOTAN_ASSERT_NOMSG(mod_lz > 0);
+ const size_t mod_bits = mod.bits();
+ BOTAN_ASSERT_NOMSG(mod_bits > mod_lz);
+
+ if(mod_lz == mod_bits - 1)
+ {
+ // In this case we are performing an inversion modulo 2^k
+ return inverse_mod_pow2(n, mod_lz);
+ }
+
+ /*
+ * In this case we are performing an inversion modulo 2^k*o for
+ * some k > 1 and some odd (not necessarily prime) integer.
+ * Compute the inversions modulo 2^k and modulo o, then combine them
+ * using CRT, which is possible because 2^k and o are relatively prime.
+ */
+
+ const BigInt o = mod >> mod_lz;
+ const BigInt n_redc = ct_modulo(n, o);
+ const BigInt inv_o = inverse_mod_odd_modulus(n_redc, o);
+ const BigInt inv_2k = inverse_mod_pow2(n, mod_lz);
+
+ // No modular inverse in this case:
+ if(inv_o == 0 || inv_2k == 0)
+ return 0;
+
+ const BigInt m2k = BigInt::power_of_2(mod_lz);
+ // Compute the CRT parameter
+ const BigInt c = inverse_mod_pow2(o, mod_lz);
+
+ // Compute h = c*(inv_2k-inv_o) mod 2^k
+ BigInt h = c * (inv_2k - inv_o);
+ const bool h_neg = h.is_negative();
+ h.set_sign(BigInt::Positive);
+ h.mask_bits(mod_lz);
+ const bool h_nonzero = h.is_nonzero();
+ h.ct_cond_assign(h_nonzero && h_neg, m2k - h);
+
+ // Return result inv_o + h * o
+ h *= o;
+ h += inv_o;
+ return h;
+ }
+
+// Deprecated forwarding functions:
+BigInt inverse_euclid(const BigInt& x, const BigInt& modulus)
+ {
+ return inverse_mod(x, modulus);
+ }
+
+BigInt ct_inverse_mod_odd_modulus(const BigInt& n, const BigInt& mod)
+ {
+ return inverse_mod_odd_modulus(n, mod);
+ }
+
+word monty_inverse(word a)
+ {
+ if(a % 2 == 0)
+ throw Invalid_Argument("monty_inverse only valid for odd integers");
+
+ /*
+ * From "A New Algorithm for Inversion mod p^k" by Çetin Kaya Koç
+ * https://eprint.iacr.org/2017/411.pdf sections 5 and 7.
+ */
+
+ word b = 1;
+ word r = 0;
+
+ for(size_t i = 0; i != BOTAN_MP_WORD_BITS; ++i)
+ {
+ const word bi = b % 2;
+ r >>= 1;
+ r += bi << (BOTAN_MP_WORD_BITS - 1);
+
+ b -= a * bi;
+ b >>= 1;
+ }
+
+ // Now invert in addition space
+ r = (MP_WORD_MAX - r) + 1;
+
+ return r;
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/math/numbertheory/monty.cpp b/comm/third_party/botan/src/lib/math/numbertheory/monty.cpp
new file mode 100644
index 0000000000..ca5eb73dfd
--- /dev/null
+++ b/comm/third_party/botan/src/lib/math/numbertheory/monty.cpp
@@ -0,0 +1,444 @@
+/*
+* (C) 2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/monty.h>
+#include <botan/reducer.h>
+#include <botan/internal/mp_core.h>
+
+namespace Botan {
+
+Montgomery_Params::Montgomery_Params(const BigInt& p,
+ const Modular_Reducer& mod_p)
+ {
+ if(p.is_even() || p < 3)
+ throw Invalid_Argument("Montgomery_Params invalid modulus");
+
+ m_p = p;
+ 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);
+
+ m_r1 = mod_p.reduce(r);
+ m_r2 = mod_p.square(m_r1);
+ m_r3 = mod_p.multiply(m_r1, m_r2);
+ }
+
+Montgomery_Params::Montgomery_Params(const BigInt& p)
+ {
+
+ if(p.is_negative() || p.is_even())
+ throw Invalid_Argument("Montgomery_Params invalid modulus");
+
+ m_p = p;
+ 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);
+
+ // It might be faster to use ct_modulo here vs setting up Barrett reduction?
+ Modular_Reducer mod_p(p);
+
+ m_r1 = mod_p.reduce(r);
+ m_r2 = mod_p.square(m_r1);
+ m_r3 = mod_p.multiply(m_r1, m_r2);
+ }
+
+BigInt Montgomery_Params::inv_mod_p(const BigInt& x) const
+ {
+ // TODO use Montgomery inverse here?
+ return inverse_mod(x, p());
+ }
+
+BigInt Montgomery_Params::redc(const BigInt& x, secure_vector<word>& ws) const
+ {
+ const size_t output_size = 2*m_p_words + 2;
+
+ if(ws.size() < output_size)
+ ws.resize(output_size);
+
+ BigInt z = x;
+ z.grow_to(output_size);
+
+ bigint_monty_redc(z.mutable_data(),
+ m_p.data(), m_p_words, m_p_dash,
+ ws.data(), ws.size());
+
+ return z;
+ }
+
+BigInt Montgomery_Params::mul(const BigInt& x, const BigInt& y,
+ secure_vector<word>& ws) const
+ {
+ const size_t output_size = 2*m_p_words + 2;
+
+ if(ws.size() < output_size)
+ ws.resize(output_size);
+
+ BOTAN_DEBUG_ASSERT(x.sig_words() <= m_p_words);
+ BOTAN_DEBUG_ASSERT(y.sig_words() <= m_p_words);
+
+ BigInt z(BigInt::Positive, output_size);
+ bigint_mul(z.mutable_data(), z.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(), ws.size());
+
+ bigint_monty_redc(z.mutable_data(),
+ m_p.data(), m_p_words, m_p_dash,
+ ws.data(), ws.size());
+
+ return z;
+ }
+
+BigInt Montgomery_Params::mul(const BigInt& x,
+ const secure_vector<word>& y,
+ secure_vector<word>& ws) const
+ {
+ const size_t output_size = 2*m_p_words + 2;
+ if(ws.size() < output_size)
+ ws.resize(output_size);
+ BigInt z(BigInt::Positive, output_size);
+
+ BOTAN_DEBUG_ASSERT(x.sig_words() <= m_p_words);
+
+ bigint_mul(z.mutable_data(), z.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(), ws.size());
+
+ bigint_monty_redc(z.mutable_data(),
+ m_p.data(), m_p_words, m_p_dash,
+ ws.data(), ws.size());
+
+ return z;
+ }
+
+void Montgomery_Params::mul_by(BigInt& x,
+ const secure_vector<word>& y,
+ secure_vector<word>& ws) const
+ {
+ const size_t output_size = 2*m_p_words + 2;
+
+ 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);
+ }
+
+void Montgomery_Params::mul_by(BigInt& x,
+ const BigInt& y,
+ secure_vector<word>& ws) const
+ {
+ const size_t output_size = 2*m_p_words + 2;
+
+ 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 Montgomery_Params::sqr(const BigInt& x, secure_vector<word>& ws) const
+ {
+ const size_t output_size = 2*m_p_words + 2;
+
+ if(ws.size() < output_size)
+ ws.resize(output_size);
+
+ BigInt z(BigInt::Positive, output_size);
+
+ BOTAN_DEBUG_ASSERT(x.sig_words() <= m_p_words);
+
+ bigint_sqr(z.mutable_data(), z.size(),
+ x.data(), x.size(), std::min(m_p_words, 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());
+
+ return z;
+ }
+
+void Montgomery_Params::square_this(BigInt& x,
+ secure_vector<word>& ws) const
+ {
+ const size_t output_size = 2*m_p_words + 2;
+
+ 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 std::shared_ptr<const Montgomery_Params> 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<word> ws;
+ m_v = m_params->mul(v, m_params->R2(), ws);
+ }
+ }
+
+Montgomery_Int::Montgomery_Int(std::shared_ptr<const Montgomery_Params> 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<word> ws;
+ m_v = m_params->mul(m_v, m_params->R2(), ws);
+ }
+ }
+
+Montgomery_Int::Montgomery_Int(std::shared_ptr<const Montgomery_Params> params,
+ const word words[], size_t len,
+ bool redc_needed) :
+ m_params(params),
+ m_v(words, len)
+ {
+ if(redc_needed)
+ {
+ BOTAN_ASSERT_NOMSG(m_v < m_params->p());
+ secure_vector<word> ws;
+ m_v = m_params->mul(m_v, m_params->R2(), ws);
+ }
+ }
+
+void Montgomery_Int::fix_size()
+ {
+ const size_t p_words = m_params->p_words();
+
+ if(m_v.sig_words() > p_words)
+ throw Internal_Error("Montgomery_Int::fix_size v too large");
+
+ 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();
+ }
+
+std::vector<uint8_t> Montgomery_Int::serialize() const
+ {
+ std::vector<uint8_t> v(size());
+ BigInt::encode_1363(v.data(), v.size(), value());
+ return v;
+ }
+
+size_t Montgomery_Int::size() const
+ {
+ return m_params->p().bytes();
+ }
+
+bool Montgomery_Int::is_one() const
+ {
+ return m_v == m_params->R1();
+ }
+
+bool Montgomery_Int::is_zero() const
+ {
+ return m_v.is_zero();
+ }
+
+BigInt Montgomery_Int::value() const
+ {
+ secure_vector<word> ws;
+ return m_params->redc(m_v, ws);
+ }
+
+Montgomery_Int Montgomery_Int::operator+(const Montgomery_Int& other) const
+ {
+ secure_vector<word> 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
+ {
+ secure_vector<word> ws;
+ BigInt z = m_v;
+ z.mod_sub(other.m_v, m_params->p(), ws);
+ return Montgomery_Int(m_params, z, false);
+ }
+
+Montgomery_Int& Montgomery_Int::operator+=(const Montgomery_Int& other)
+ {
+ secure_vector<word> ws;
+ return this->add(other, ws);
+ }
+
+Montgomery_Int& Montgomery_Int::add(const Montgomery_Int& other, secure_vector<word>& ws)
+ {
+ m_v.mod_add(other.m_v, m_params->p(), ws);
+ return (*this);
+ }
+
+Montgomery_Int& Montgomery_Int::operator-=(const Montgomery_Int& other)
+ {
+ secure_vector<word> ws;
+ return this->sub(other, ws);
+ }
+
+Montgomery_Int& Montgomery_Int::sub(const Montgomery_Int& other, secure_vector<word>& ws)
+ {
+ m_v.mod_sub(other.m_v, m_params->p(), ws);
+ return (*this);
+ }
+
+Montgomery_Int Montgomery_Int::operator*(const Montgomery_Int& other) const
+ {
+ secure_vector<word> ws;
+ return Montgomery_Int(m_params, m_params->mul(m_v, other.m_v, ws), false);
+ }
+
+Montgomery_Int Montgomery_Int::mul(const Montgomery_Int& other,
+ secure_vector<word>& ws) const
+ {
+ return Montgomery_Int(m_params, m_params->mul(m_v, other.m_v, ws), false);
+ }
+
+Montgomery_Int& Montgomery_Int::mul_by(const Montgomery_Int& other,
+ secure_vector<word>& ws)
+ {
+ m_params->mul_by(m_v, other.m_v, ws);
+ return (*this);
+ }
+
+Montgomery_Int& Montgomery_Int::mul_by(const secure_vector<word>& other,
+ secure_vector<word>& ws)
+ {
+ m_params->mul_by(m_v, other, ws);
+ return (*this);
+ }
+
+Montgomery_Int& Montgomery_Int::operator*=(const Montgomery_Int& other)
+ {
+ secure_vector<word> ws;
+ return mul_by(other, ws);
+ }
+
+Montgomery_Int& Montgomery_Int::operator*=(const secure_vector<word>& other)
+ {
+ secure_vector<word> ws;
+ return mul_by(other, ws);
+ }
+
+Montgomery_Int& Montgomery_Int::square_this_n_times(secure_vector<word>& ws, size_t n)
+ {
+ for(size_t i = 0; i != n; ++i)
+ m_params->square_this(m_v, ws);
+ return (*this);
+ }
+
+Montgomery_Int& Montgomery_Int::square_this(secure_vector<word>& ws)
+ {
+ m_params->square_this(m_v, ws);
+ return (*this);
+ }
+
+Montgomery_Int Montgomery_Int::square(secure_vector<word>& ws) const
+ {
+ return Montgomery_Int(m_params, m_params->sqr(m_v, ws), false);
+ }
+
+Montgomery_Int Montgomery_Int::multiplicative_inverse() const
+ {
+ secure_vector<word> ws;
+ const BigInt iv = m_params->mul(m_params->inv_mod_p(m_v), m_params->R3(), ws);
+ return Montgomery_Int(m_params, iv, false);
+ }
+
+Montgomery_Int Montgomery_Int::additive_inverse() const
+ {
+ return Montgomery_Int(m_params, m_params->p()) - (*this);
+ }
+
+Montgomery_Int& Montgomery_Int::mul_by_2(secure_vector<word>& ws)
+ {
+ m_v.mod_mul(2, m_params->p(), ws);
+ return (*this);
+ }
+
+Montgomery_Int& Montgomery_Int::mul_by_3(secure_vector<word>& ws)
+ {
+ m_v.mod_mul(3, m_params->p(), ws);
+ return (*this);
+ }
+
+Montgomery_Int& Montgomery_Int::mul_by_4(secure_vector<word>& ws)
+ {
+ m_v.mod_mul(4, m_params->p(), ws);
+ return (*this);
+ }
+
+Montgomery_Int& Montgomery_Int::mul_by_8(secure_vector<word>& ws)
+ {
+ m_v.mod_mul(8, m_params->p(), ws);
+ return (*this);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/math/numbertheory/monty.h b/comm/third_party/botan/src/lib/math/numbertheory/monty.h
new file mode 100644
index 0000000000..8e0cd342fb
--- /dev/null
+++ b/comm/third_party/botan/src/lib/math/numbertheory/monty.h
@@ -0,0 +1,191 @@
+/*
+* (C) 2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_MONTY_INT_H_
+#define BOTAN_MONTY_INT_H_
+
+#include <botan/bigint.h>
+BOTAN_FUTURE_INTERNAL_HEADER(monty.h)
+
+namespace Botan {
+
+class Modular_Reducer;
+
+class Montgomery_Params;
+
+/**
+* The Montgomery representation of an integer
+*/
+class BOTAN_UNSTABLE_API Montgomery_Int final
+ {
+ public:
+ /**
+ * Create a zero-initialized Montgomery_Int
+ */
+ Montgomery_Int(std::shared_ptr<const Montgomery_Params> params) : m_params(params) {}
+
+ /**
+ * Create a Montgomery_Int
+ */
+ Montgomery_Int(std::shared_ptr<const Montgomery_Params> params,
+ const BigInt& v,
+ bool redc_needed = true);
+
+ /**
+ * Create a Montgomery_Int
+ */
+ Montgomery_Int(std::shared_ptr<const Montgomery_Params> params,
+ const uint8_t bits[], size_t len,
+ bool redc_needed = true);
+
+ /**
+ * Create a Montgomery_Int
+ */
+ Montgomery_Int(std::shared_ptr<const Montgomery_Params> params,
+ const word words[], size_t len,
+ bool redc_needed = true);
+
+ bool operator==(const Montgomery_Int& other) const;
+ bool operator!=(const Montgomery_Int& other) const { return (m_v != other.m_v); }
+
+ std::vector<uint8_t> serialize() const;
+
+ size_t size() const;
+ bool is_one() const;
+ bool is_zero() const;
+
+ void fix_size();
+
+ /**
+ * Return the value to normal mod-p space
+ */
+ BigInt value() const;
+
+ /**
+ * Return the Montgomery representation
+ */
+ const BigInt& repr() const { return m_v; }
+
+ Montgomery_Int operator+(const Montgomery_Int& other) const;
+
+ Montgomery_Int operator-(const Montgomery_Int& other) const;
+
+ Montgomery_Int& operator+=(const Montgomery_Int& other);
+
+ Montgomery_Int& operator-=(const Montgomery_Int& other);
+
+ Montgomery_Int operator*(const Montgomery_Int& other) const;
+
+ Montgomery_Int& operator*=(const Montgomery_Int& other);
+
+ Montgomery_Int& operator*=(const secure_vector<word>& other);
+
+ Montgomery_Int& add(const Montgomery_Int& other,
+ secure_vector<word>& ws);
+
+ Montgomery_Int& sub(const Montgomery_Int& other,
+ secure_vector<word>& ws);
+
+ Montgomery_Int mul(const Montgomery_Int& other,
+ secure_vector<word>& ws) const;
+
+ Montgomery_Int& mul_by(const Montgomery_Int& other,
+ secure_vector<word>& ws);
+
+ Montgomery_Int& mul_by(const secure_vector<word>& other,
+ secure_vector<word>& ws);
+
+ Montgomery_Int square(secure_vector<word>& ws) const;
+
+ Montgomery_Int& square_this(secure_vector<word>& ws);
+
+ Montgomery_Int& square_this_n_times(secure_vector<word>& ws, size_t n);
+
+ Montgomery_Int multiplicative_inverse() const;
+
+ Montgomery_Int additive_inverse() const;
+
+ Montgomery_Int& mul_by_2(secure_vector<word>& ws);
+
+ Montgomery_Int& mul_by_3(secure_vector<word>& ws);
+
+ Montgomery_Int& mul_by_4(secure_vector<word>& ws);
+
+ Montgomery_Int& mul_by_8(secure_vector<word>& ws);
+
+ void const_time_poison() const { m_v.const_time_poison(); }
+ void const_time_unpoison() const { return m_v.const_time_unpoison(); }
+
+ private:
+ std::shared_ptr<const Montgomery_Params> m_params;
+ BigInt m_v;
+ };
+
+/**
+* Parameters for Montgomery Reduction
+*/
+class BOTAN_UNSTABLE_API Montgomery_Params final
+ {
+ public:
+ /**
+ * Initialize a set of Montgomery reduction parameters. These values
+ * can be shared by all values in a specific Montgomery domain.
+ */
+ Montgomery_Params(const BigInt& p, const Modular_Reducer& mod_p);
+
+ /**
+ * Initialize a set of Montgomery reduction parameters. These values
+ * can be shared by all values in a specific Montgomery domain.
+ */
+ 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; }
+ const BigInt& R3() const { return m_r3; }
+
+ word p_dash() const { return m_p_dash; }
+
+ size_t p_words() const { return m_p_words; }
+
+ BigInt redc(const BigInt& x,
+ secure_vector<word>& ws) const;
+
+ BigInt mul(const BigInt& x,
+ const BigInt& y,
+ secure_vector<word>& ws) const;
+
+ BigInt mul(const BigInt& x,
+ const secure_vector<word>& y,
+ secure_vector<word>& ws) const;
+
+ void mul_by(BigInt& x,
+ const secure_vector<word>& y,
+ secure_vector<word>& ws) const;
+
+ void mul_by(BigInt& x, const BigInt& y,
+ secure_vector<word>& ws) const;
+
+ BigInt sqr(const BigInt& x,
+ secure_vector<word>& ws) const;
+
+ void square_this(BigInt& x,
+ secure_vector<word>& ws) const;
+
+ BigInt inv_mod_p(const BigInt& x) const;
+
+ private:
+ BigInt m_p;
+ BigInt m_r1;
+ BigInt m_r2;
+ BigInt m_r3;
+ word m_p_dash;
+ size_t m_p_words;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/math/numbertheory/monty_exp.cpp b/comm/third_party/botan/src/lib/math/numbertheory/monty_exp.cpp
new file mode 100644
index 0000000000..02ae795cd9
--- /dev/null
+++ b/comm/third_party/botan/src/lib/math/numbertheory/monty_exp.cpp
@@ -0,0 +1,254 @@
+/*
+* Montgomery Exponentiation
+* (C) 1999-2010,2012,2018 Jack Lloyd
+* 2016 Matthias Gierlings
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/monty_exp.h>
+#include <botan/internal/ct_utils.h>
+#include <botan/internal/rounding.h>
+#include <botan/numthry.h>
+#include <botan/reducer.h>
+#include <botan/monty.h>
+
+namespace Botan {
+
+class Montgomery_Exponentation_State
+ {
+ public:
+ Montgomery_Exponentation_State(std::shared_ptr<const Montgomery_Params> params,
+ const BigInt& g,
+ size_t window_bits,
+ bool const_time);
+
+ BigInt exponentiation(const BigInt& k, size_t max_k_bits) const;
+
+ BigInt exponentiation_vartime(const BigInt& k) const;
+ private:
+ std::shared_ptr<const Montgomery_Params> m_params;
+ std::vector<Montgomery_Int> m_g;
+ size_t m_window_bits;
+ bool m_const_time;
+ };
+
+Montgomery_Exponentation_State::Montgomery_Exponentation_State(std::shared_ptr<const Montgomery_Params> params,
+ const BigInt& g,
+ size_t window_bits,
+ bool const_time) :
+ m_params(params),
+ m_window_bits(window_bits == 0 ? 4 : window_bits),
+ m_const_time(const_time)
+ {
+ BOTAN_ARG_CHECK(g < m_params->p(), "Montgomery base too big");
+
+ if(m_window_bits < 1 || m_window_bits > 12) // really even 8 is too large ...
+ throw Invalid_Argument("Invalid window bits for Montgomery exponentiation");
+
+ const size_t window_size = (static_cast<size_t>(1) << m_window_bits);
+
+ m_g.reserve(window_size);
+
+ m_g.push_back(Montgomery_Int(m_params, m_params->R1(), false));
+
+ m_g.push_back(Montgomery_Int(m_params, g));
+
+ for(size_t i = 2; i != window_size; ++i)
+ {
+ m_g.push_back(m_g[1] * m_g[i - 1]);
+ }
+
+ // Resize each element to exactly p words
+ for(size_t i = 0; i != window_size; ++i)
+ {
+ m_g[i].fix_size();
+ if(const_time)
+ m_g[i].const_time_poison();
+ }
+ }
+
+namespace {
+
+void const_time_lookup(secure_vector<word>& output,
+ const std::vector<Montgomery_Int>& g,
+ size_t nibble)
+ {
+ BOTAN_ASSERT_NOMSG(g.size() % 2 == 0); // actually a power of 2
+
+ const size_t words = output.size();
+
+ clear_mem(output.data(), output.size());
+
+ for(size_t i = 0; i != g.size(); i += 2)
+ {
+ const secure_vector<word>& vec_0 = g[i ].repr().get_word_vector();
+ const secure_vector<word>& vec_1 = g[i+1].repr().get_word_vector();
+
+ BOTAN_ASSERT_NOMSG(vec_0.size() >= words && vec_1.size() >= words);
+
+ const auto mask_0 = CT::Mask<word>::is_equal(nibble, i);
+ const auto mask_1 = CT::Mask<word>::is_equal(nibble, i+1);
+
+ for(size_t w = 0; w != words; ++w)
+ {
+ output[w] |= mask_0.if_set_return(vec_0[w]);
+ output[w] |= mask_1.if_set_return(vec_1[w]);
+ }
+ }
+ }
+
+}
+
+BigInt Montgomery_Exponentation_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
+
+ const size_t exp_nibbles = (max_k_bits + m_window_bits - 1) / m_window_bits;
+
+ if(exp_nibbles == 0)
+ return 1;
+
+ secure_vector<word> e_bits(m_params->p_words());
+ secure_vector<word> ws;
+
+ 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);
+
+ for(size_t i = exp_nibbles - 1; i > 0; --i)
+ {
+ x.square_this_n_times(ws, m_window_bits);
+ const_time_lookup(e_bits, m_g, scalar.get_substring(m_window_bits*(i-1), m_window_bits));
+ x.mul_by(e_bits, ws);
+ }
+
+ x.const_time_unpoison();
+ return x.value();
+ }
+
+BigInt Montgomery_Exponentation_State::exponentiation_vartime(const BigInt& scalar) const
+ {
+ BOTAN_ASSERT_NOMSG(m_const_time == false);
+
+ const size_t exp_nibbles = (scalar.bits() + m_window_bits - 1) / m_window_bits;
+
+ secure_vector<word> ws;
+
+ if(exp_nibbles == 0)
+ return 1;
+
+ Montgomery_Int x = m_g[scalar.get_substring(m_window_bits*(exp_nibbles-1), m_window_bits)];
+
+ for(size_t i = exp_nibbles - 1; i > 0; --i)
+ {
+ x.square_this_n_times(ws, m_window_bits);
+
+ const uint32_t nibble = scalar.get_substring(m_window_bits*(i-1), m_window_bits);
+ if(nibble > 0)
+ x.mul_by(m_g[nibble], ws);
+ }
+
+ x.const_time_unpoison();
+ return x.value();
+ }
+
+std::shared_ptr<const Montgomery_Exponentation_State>
+monty_precompute(std::shared_ptr<const Montgomery_Params> params,
+ const BigInt& g,
+ size_t window_bits,
+ bool const_time)
+ {
+ return std::make_shared<const Montgomery_Exponentation_State>(params, g, window_bits, const_time);
+ }
+
+BigInt monty_execute(const Montgomery_Exponentation_State& precomputed_state,
+ const BigInt& k, size_t max_k_bits)
+ {
+ return precomputed_state.exponentiation(k, max_k_bits);
+ }
+
+BigInt monty_execute_vartime(const Montgomery_Exponentation_State& precomputed_state,
+ const BigInt& k)
+ {
+ return precomputed_state.exponentiation_vartime(k);
+ }
+
+BigInt monty_multi_exp(std::shared_ptr<const Montgomery_Params> params_p,
+ const BigInt& x_bn,
+ const BigInt& z1,
+ const BigInt& y_bn,
+ const BigInt& z2)
+ {
+ if(z1.is_negative() || z2.is_negative())
+ 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<word> ws;
+
+ const Montgomery_Int one(params_p, params_p->R1(), false);
+ //const Montgomery_Int one(params_p, 1);
+
+ const Montgomery_Int x1(params_p, x_bn);
+ const Montgomery_Int x2 = x1.square(ws);
+ const Montgomery_Int x3 = x2.mul(x1, ws);
+
+ const Montgomery_Int y1(params_p, y_bn);
+ const Montgomery_Int y2 = y1.square(ws);
+ const Montgomery_Int y3 = y2.mul(y1, ws);
+
+ const Montgomery_Int y1x1 = y1.mul(x1, ws);
+ const Montgomery_Int y1x2 = y1.mul(x2, ws);
+ const Montgomery_Int y1x3 = y1.mul(x3, ws);
+
+ const Montgomery_Int y2x1 = y2.mul(x1, ws);
+ const Montgomery_Int y2x2 = y2.mul(x2, ws);
+ const Montgomery_Int y2x3 = y2.mul(x3, ws);
+
+ const Montgomery_Int y3x1 = y3.mul(x1, ws);
+ const Montgomery_Int y3x2 = y3.mul(x2, ws);
+ const Montgomery_Int y3x3 = y3.mul(x3, ws);
+
+ const Montgomery_Int* M[16] = {
+ &one,
+ &x1, // 0001
+ &x2, // 0010
+ &x3, // 0011
+ &y1, // 0100
+ &y1x1,
+ &y1x2,
+ &y1x3,
+ &y2, // 1000
+ &y2x1,
+ &y2x2,
+ &y2x3,
+ &y3, // 1100
+ &y3x1,
+ &y3x2,
+ &y3x3
+ };
+
+ Montgomery_Int H = one;
+
+ for(size_t i = 0; i != z_bits; i += 2)
+ {
+ if(i > 0)
+ {
+ H.square_this(ws);
+ H.square_this(ws);
+ }
+
+ const uint32_t z1_b = z1.get_substring(z_bits - i - 2, 2);
+ const uint32_t z2_b = z2.get_substring(z_bits - i - 2, 2);
+
+ const uint32_t z12 = (4*z2_b) + z1_b;
+
+ H.mul_by(*M[z12], ws);
+ }
+
+ return H.value();
+ }
+
+}
+
diff --git a/comm/third_party/botan/src/lib/math/numbertheory/monty_exp.h b/comm/third_party/botan/src/lib/math/numbertheory/monty_exp.h
new file mode 100644
index 0000000000..632d7f7d6e
--- /dev/null
+++ b/comm/third_party/botan/src/lib/math/numbertheory/monty_exp.h
@@ -0,0 +1,54 @@
+/*
+* (C) 2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_MONTY_EXP_H_
+#define BOTAN_MONTY_EXP_H_
+
+#include <memory>
+
+namespace Botan {
+
+class BigInt;
+class Modular_Reducer;
+
+class Montgomery_Params;
+
+class Montgomery_Exponentation_State;
+
+/*
+* Precompute for calculating values g^x mod p
+*/
+std::shared_ptr<const Montgomery_Exponentation_State>
+monty_precompute(std::shared_ptr<const Montgomery_Params> params_p,
+ const BigInt& g,
+ size_t window_bits,
+ bool const_time = true);
+
+/*
+* Return g^k mod p
+*/
+BigInt monty_execute(const Montgomery_Exponentation_State& precomputed_state,
+ const BigInt& k, size_t max_k_bits);
+
+/*
+* Return g^k mod p taking variable time depending on k
+* @warning only use this if k is public
+*/
+BigInt monty_execute_vartime(const Montgomery_Exponentation_State& precomputed_state,
+ const BigInt& k);
+
+/**
+* Return (x^z1 * y^z2) % p
+*/
+BigInt monty_multi_exp(std::shared_ptr<const Montgomery_Params> params_p,
+ const BigInt& x,
+ const BigInt& z1,
+ const BigInt& y,
+ const BigInt& z2);
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/math/numbertheory/mp_numth.cpp b/comm/third_party/botan/src/lib/math/numbertheory/mp_numth.cpp
new file mode 100644
index 0000000000..eef6419965
--- /dev/null
+++ b/comm/third_party/botan/src/lib/math/numbertheory/mp_numth.cpp
@@ -0,0 +1,84 @@
+/*
+* Fused and Important MP Algorithms
+* (C) 1999-2007 Jack Lloyd
+* 2016 Matthias Gierlings
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/numthry.h>
+#include <botan/internal/mp_core.h>
+#include <botan/internal/rounding.h>
+#include <algorithm>
+
+namespace Botan {
+
+/*
+* Square a BigInt
+*/
+BigInt square(const BigInt& x)
+ {
+ BigInt z = x;
+ secure_vector<word> ws;
+ z.square(ws);
+ return z;
+ }
+
+/*
+* Multiply-Add Operation
+*/
+BigInt mul_add(const BigInt& a, const BigInt& b, const BigInt& c)
+ {
+ if(c.is_negative())
+ throw Invalid_Argument("mul_add: Third argument must be > 0");
+
+ BigInt::Sign sign = BigInt::Positive;
+ if(a.sign() != b.sign())
+ sign = BigInt::Negative;
+
+ const size_t a_sw = a.sig_words();
+ const size_t b_sw = b.sig_words();
+ const size_t c_sw = c.sig_words();
+
+ BigInt r(sign, std::max(a_sw + b_sw, c_sw) + 1);
+ secure_vector<word> workspace(r.size());
+
+ bigint_mul(r.mutable_data(), r.size(),
+ a.data(), a.size(), a_sw,
+ b.data(), b.size(), b_sw,
+ workspace.data(), workspace.size());
+
+ const size_t r_size = std::max(r.sig_words(), c_sw);
+ bigint_add2(r.mutable_data(), r_size, c.data(), c_sw);
+ return r;
+ }
+
+/*
+* Subtract-Multiply Operation
+*/
+BigInt sub_mul(const BigInt& a, const BigInt& b, const BigInt& c)
+ {
+ if(a.is_negative() || b.is_negative())
+ throw Invalid_Argument("sub_mul: First two arguments must be >= 0");
+
+ BigInt r = a;
+ r -= b;
+ r *= c;
+ return r;
+ }
+
+/*
+* Multiply-Subtract Operation
+*/
+BigInt mul_sub(const BigInt& a, const BigInt& b, const BigInt& c)
+ {
+ if(c.is_negative() || c.is_zero())
+ throw Invalid_Argument("mul_sub: Third argument must be > 0");
+
+ BigInt r = a;
+ r *= b;
+ r -= c;
+ return r;
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/math/numbertheory/nistp_redc.cpp b/comm/third_party/botan/src/lib/math/numbertheory/nistp_redc.cpp
new file mode 100644
index 0000000000..7f5ff18b95
--- /dev/null
+++ b/comm/third_party/botan/src/lib/math/numbertheory/nistp_redc.cpp
@@ -0,0 +1,583 @@
+/*
+* NIST prime reductions
+* (C) 2014,2015,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/curve_nistp.h>
+#include <botan/internal/mp_core.h>
+#include <botan/internal/mp_asmi.h>
+#include <botan/internal/ct_utils.h>
+
+namespace Botan {
+
+const BigInt& prime_p521()
+ {
+ static const BigInt p521("0x1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
+ "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF");
+
+ return p521;
+ }
+
+void redc_p521(BigInt& x, secure_vector<word>& ws)
+ {
+ const size_t p_full_words = 521 / BOTAN_MP_WORD_BITS;
+ const size_t p_top_bits = 521 % BOTAN_MP_WORD_BITS;
+ const size_t p_words = p_full_words + 1;
+
+#if (BOTAN_MP_WORD_BITS == 64)
+ static const word p521_words[p_words] = {
+ 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF,
+ 0x1FF };
+#else
+ static const word p521_words[p_words] = {
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0x1FF };
+#endif
+
+ if(ws.size() < p_words + 1)
+ ws.resize(p_words + 1);
+
+ clear_mem(ws.data(), ws.size());
+ bigint_shr2(ws.data(), x.data(), std::min(x.size(), 2*p_words), p_full_words, p_top_bits);
+
+ x.mask_bits(521);
+ x.grow_to(p_words);
+
+ // Word-level carry will be zero
+ word carry = bigint_add3_nc(x.mutable_data(), x.data(), p_words, ws.data(), p_words);
+ BOTAN_ASSERT_EQUAL(carry, 0, "Final carry in P-521 reduction");
+
+ const word top_word = x.word_at(p_full_words);
+
+ /*
+ * Check if we need to reduce modulo P
+ * There are two possible cases:
+ * - The result overflowed past 521 bits, in which case bit 522 will be set
+ * - The result is exactly 2**521 - 1
+ */
+ const auto bit_522_set = CT::Mask<word>::expand(top_word >> p_top_bits);
+
+ word and_512 = MP_WORD_MAX;
+ for(size_t i = 0; i != p_full_words; ++i)
+ and_512 &= x.word_at(i);
+ const auto all_512_low_bits_set = CT::Mask<word>::is_equal(and_512, MP_WORD_MAX);
+ const auto has_p521_top_word = CT::Mask<word>::is_equal(top_word, 0x1FF);
+ const auto is_p521 = all_512_low_bits_set & has_p521_top_word;
+
+ const auto needs_reduction = is_p521 | bit_522_set;
+
+ bigint_cnd_sub(needs_reduction.value(), x.mutable_data(), p521_words, p_words);
+ }
+
+namespace {
+
+/**
+* Treating this MPI as a sequence of 32-bit words in big-endian
+* order, return word i. The array is assumed to be large enough.
+*/
+inline uint32_t get_uint32(const word xw[], size_t i)
+ {
+#if (BOTAN_MP_WORD_BITS == 32)
+ return xw[i];
+#else
+ return static_cast<uint32_t>(xw[i/2] >> ((i % 2)*32));
+#endif
+ }
+
+inline void set_words(word x[], size_t i, uint32_t R0, uint32_t R1)
+ {
+#if (BOTAN_MP_WORD_BITS == 32)
+ x[i] = R0;
+ x[i+1] = R1;
+#else
+ x[i/2] = (static_cast<uint64_t>(R1) << 32) | R0;
+#endif
+ }
+
+}
+
+const BigInt& prime_p192()
+ {
+ static const BigInt p192("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF");
+ return p192;
+ }
+
+void redc_p192(BigInt& x, secure_vector<word>& ws)
+ {
+ BOTAN_UNUSED(ws);
+
+ static const size_t p192_limbs = 192 / BOTAN_MP_WORD_BITS;
+
+ x.grow_to(2*p192_limbs);
+ word* xw = x.mutable_data();
+
+ const uint64_t X00 = get_uint32(xw, 0);
+ const uint64_t X01 = get_uint32(xw, 1);
+ const uint64_t X02 = get_uint32(xw, 2);
+ const uint64_t X03 = get_uint32(xw, 3);
+ const uint64_t X04 = get_uint32(xw, 4);
+ const uint64_t X05 = get_uint32(xw, 5);
+ const uint64_t X06 = get_uint32(xw, 6);
+ const uint64_t X07 = get_uint32(xw, 7);
+ const uint64_t X08 = get_uint32(xw, 8);
+ const uint64_t X09 = get_uint32(xw, 9);
+ const uint64_t X10 = get_uint32(xw, 10);
+ const uint64_t X11 = get_uint32(xw, 11);
+
+ const uint64_t S0 = X00 + X06 + X10;
+ const uint64_t S1 = X01 + X07 + X11;
+ const uint64_t S2 = X02 + X06 + X08 + X10;
+ const uint64_t S3 = X03 + X07 + X09 + X11;
+ const uint64_t S4 = X04 + X08 + X10;
+ const uint64_t S5 = X05 + X09 + X11;
+
+ uint64_t S = 0;
+ uint32_t R0 = 0, R1 = 0;
+
+ S += S0;
+ R0 = static_cast<uint32_t>(S);
+ S >>= 32;
+
+ S += S1;
+ R1 = static_cast<uint32_t>(S);
+ S >>= 32;
+
+ set_words(xw, 0, R0, R1);
+
+ S += S2;
+ R0 = static_cast<uint32_t>(S);
+ S >>= 32;
+
+ S += S3;
+ R1 = static_cast<uint32_t>(S);
+ S >>= 32;
+
+ set_words(xw, 2, R0, R1);
+
+ S += S4;
+ R0 = static_cast<uint32_t>(S);
+ S >>= 32;
+
+ S += S5;
+ R1 = static_cast<uint32_t>(S);
+ S >>= 32;
+
+ set_words(xw, 4, R0, R1);
+
+ // No underflow possible
+
+ /*
+ This is a table of (i*P-192) % 2**192 for i in 1...3
+ */
+ static const word p192_mults[3][p192_limbs] = {
+#if (BOTAN_MP_WORD_BITS == 64)
+ {0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFE, 0xFFFFFFFFFFFFFFFF},
+ {0xFFFFFFFFFFFFFFFE, 0xFFFFFFFFFFFFFFFD, 0xFFFFFFFFFFFFFFFF},
+ {0xFFFFFFFFFFFFFFFD, 0xFFFFFFFFFFFFFFFC, 0xFFFFFFFFFFFFFFFF},
+#else
+ {0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFE, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF},
+ {0xFFFFFFFE, 0xFFFFFFFF, 0xFFFFFFFD, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF},
+ {0xFFFFFFFD, 0xFFFFFFFF, 0xFFFFFFFC, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF},
+#endif
+ };
+
+ CT::unpoison(S);
+ BOTAN_ASSERT(S <= 2, "Expected overflow");
+
+ BOTAN_ASSERT_NOMSG(x.size() >= p192_limbs + 1);
+ x.mask_bits(192);
+ word borrow = bigint_sub2(x.mutable_data(), p192_limbs + 1, p192_mults[S], p192_limbs);
+ BOTAN_DEBUG_ASSERT(borrow == 0 || borrow == 1);
+ bigint_cnd_add(borrow, x.mutable_data(), p192_limbs + 1, p192_mults[0], p192_limbs);
+ }
+
+const BigInt& prime_p224()
+ {
+ static const BigInt p224("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000001");
+ return p224;
+ }
+
+void redc_p224(BigInt& x, secure_vector<word>& ws)
+ {
+ static const size_t p224_limbs = (BOTAN_MP_WORD_BITS == 32) ? 7 : 4;
+
+ BOTAN_UNUSED(ws);
+
+ x.grow_to(2*p224_limbs);
+ word* xw = x.mutable_data();
+
+ const int64_t X00 = get_uint32(xw, 0);
+ const int64_t X01 = get_uint32(xw, 1);
+ const int64_t X02 = get_uint32(xw, 2);
+ const int64_t X03 = get_uint32(xw, 3);
+ const int64_t X04 = get_uint32(xw, 4);
+ const int64_t X05 = get_uint32(xw, 5);
+ const int64_t X06 = get_uint32(xw, 6);
+ const int64_t X07 = get_uint32(xw, 7);
+ const int64_t X08 = get_uint32(xw, 8);
+ const int64_t X09 = get_uint32(xw, 9);
+ const int64_t X10 = get_uint32(xw, 10);
+ const int64_t X11 = get_uint32(xw, 11);
+ const int64_t X12 = get_uint32(xw, 12);
+ const int64_t X13 = get_uint32(xw, 13);
+
+ // One full copy of P224 is added, so the result is always positive
+
+ const int64_t S0 = 0x00000001 + X00 - X07 - X11;
+ const int64_t S1 = 0x00000000 + X01 - X08 - X12;
+ const int64_t S2 = 0x00000000 + X02 - X09 - X13;
+ const int64_t S3 = 0xFFFFFFFF + X03 + X07 + X11 - X10;
+ const int64_t S4 = 0xFFFFFFFF + X04 + X08 + X12 - X11;
+ const int64_t S5 = 0xFFFFFFFF + X05 + X09 + X13 - X12;
+ const int64_t S6 = 0xFFFFFFFF + X06 + X10 - X13;
+
+ int64_t S = 0;
+ uint32_t R0 = 0, R1 = 0;
+
+ S += S0;
+ R0 = static_cast<uint32_t>(S);
+ S >>= 32;
+
+ S += S1;
+ R1 = static_cast<uint32_t>(S);
+ S >>= 32;
+
+ set_words(xw, 0, R0, R1);
+
+ S += S2;
+ R0 = static_cast<uint32_t>(S);
+ S >>= 32;
+
+ S += S3;
+ R1 = static_cast<uint32_t>(S);
+ S >>= 32;
+
+ set_words(xw, 2, R0, R1);
+
+ S += S4;
+ R0 = static_cast<uint32_t>(S);
+ S >>= 32;
+
+ S += S5;
+ R1 = static_cast<uint32_t>(S);
+ S >>= 32;
+
+ set_words(xw, 4, R0, R1);
+
+ S += S6;
+ R0 = static_cast<uint32_t>(S);
+ S >>= 32;
+
+ set_words(xw, 6, R0, 0);
+
+ static const word p224_mults[3][p224_limbs] = {
+#if (BOTAN_MP_WORD_BITS == 64)
+ {0x0000000000000001, 0xFFFFFFFF00000000, 0xFFFFFFFFFFFFFFFF, 0x00000000FFFFFFFF},
+ {0x0000000000000002, 0xFFFFFFFE00000000, 0xFFFFFFFFFFFFFFFF, 0x00000000FFFFFFFF},
+ {0x0000000000000003, 0xFFFFFFFD00000000, 0xFFFFFFFFFFFFFFFF, 0x00000000FFFFFFFF},
+#else
+ {0x00000001, 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF},
+ {0x00000002, 0x00000000, 0x00000000, 0xFFFFFFFE, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF},
+ {0x00000003, 0x00000000, 0x00000000, 0xFFFFFFFD, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF}
+#endif
+
+ };
+
+ CT::unpoison(S);
+ BOTAN_ASSERT(S >= 0 && S <= 2, "Expected overflow");
+
+ BOTAN_ASSERT_NOMSG(x.size() >= p224_limbs + 1);
+ x.mask_bits(224);
+ word borrow = bigint_sub2(x.mutable_data(), p224_limbs + 1, p224_mults[S], p224_limbs);
+ BOTAN_DEBUG_ASSERT(borrow == 0 || borrow == 1);
+ bigint_cnd_add(borrow, x.mutable_data(), p224_limbs + 1, p224_mults[0], p224_limbs);
+ }
+
+const BigInt& prime_p256()
+ {
+ static const BigInt p256("0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF");
+ return p256;
+ }
+
+void redc_p256(BigInt& x, secure_vector<word>& ws)
+ {
+ static const size_t p256_limbs = (BOTAN_MP_WORD_BITS == 32) ? 8 : 4;
+
+ BOTAN_UNUSED(ws);
+
+ x.grow_to(2*p256_limbs);
+ word* xw = x.mutable_data();
+
+ const int64_t X00 = get_uint32(xw, 0);
+ const int64_t X01 = get_uint32(xw, 1);
+ const int64_t X02 = get_uint32(xw, 2);
+ const int64_t X03 = get_uint32(xw, 3);
+ const int64_t X04 = get_uint32(xw, 4);
+ const int64_t X05 = get_uint32(xw, 5);
+ const int64_t X06 = get_uint32(xw, 6);
+ const int64_t X07 = get_uint32(xw, 7);
+ const int64_t X08 = get_uint32(xw, 8);
+ const int64_t X09 = get_uint32(xw, 9);
+ const int64_t X10 = get_uint32(xw, 10);
+ const int64_t X11 = get_uint32(xw, 11);
+ const int64_t X12 = get_uint32(xw, 12);
+ const int64_t X13 = get_uint32(xw, 13);
+ const int64_t X14 = get_uint32(xw, 14);
+ const int64_t X15 = get_uint32(xw, 15);
+
+ // Adds 6 * P-256 to prevent underflow
+ const int64_t S0 = 0xFFFFFFFA + X00 + X08 + X09 - (X11 + X12 + X13) - X14;
+ const int64_t S1 = 0xFFFFFFFF + X01 + X09 + X10 - X12 - (X13 + X14 + X15);
+ const int64_t S2 = 0xFFFFFFFF + X02 + X10 + X11 - (X13 + X14 + X15);
+ const int64_t S3 = 0x00000005 + X03 + (X11 + X12)*2 + X13 - X15 - X08 - X09;
+ const int64_t S4 = 0x00000000 + X04 + (X12 + X13)*2 + X14 - X09 - X10;
+ const int64_t S5 = 0x00000000 + X05 + (X13 + X14)*2 + X15 - X10 - X11;
+ const int64_t S6 = 0x00000006 + X06 + X13 + X14*3 + X15*2 - X08 - X09;
+ const int64_t S7 = 0xFFFFFFFA + X07 + X15*3 + X08 - X10 - (X11 + X12 + X13);
+
+ int64_t S = 0;
+
+ uint32_t R0 = 0, R1 = 0;
+
+ S += S0;
+ R0 = static_cast<uint32_t>(S);
+ S >>= 32;
+
+ S += S1;
+ R1 = static_cast<uint32_t>(S);
+ S >>= 32;
+
+ set_words(xw, 0, R0, R1);
+
+ S += S2;
+ R0 = static_cast<uint32_t>(S);
+ S >>= 32;
+
+ S += S3;
+ R1 = static_cast<uint32_t>(S);
+ S >>= 32;
+
+ set_words(xw, 2, R0, R1);
+
+ S += S4;
+ R0 = static_cast<uint32_t>(S);
+ S >>= 32;
+
+ S += S5;
+ R1 = static_cast<uint32_t>(S);
+ S >>= 32;
+
+ set_words(xw, 4, R0, R1);
+
+ S += S6;
+ R0 = static_cast<uint32_t>(S);
+ S >>= 32;
+
+ S += S7;
+ R1 = static_cast<uint32_t>(S);
+ S >>= 32;
+ set_words(xw, 6, R0, R1);
+
+ S += 5; // the top digits of 6*P-256
+
+ /*
+ This is a table of (i*P-256) % 2**256 for i in 1...10
+ */
+ static const word p256_mults[11][p256_limbs] = {
+#if (BOTAN_MP_WORD_BITS == 64)
+ {0xFFFFFFFFFFFFFFFF, 0x00000000FFFFFFFF, 0x0000000000000000, 0xFFFFFFFF00000001},
+ {0xFFFFFFFFFFFFFFFE, 0x00000001FFFFFFFF, 0x0000000000000000, 0xFFFFFFFE00000002},
+ {0xFFFFFFFFFFFFFFFD, 0x00000002FFFFFFFF, 0x0000000000000000, 0xFFFFFFFD00000003},
+ {0xFFFFFFFFFFFFFFFC, 0x00000003FFFFFFFF, 0x0000000000000000, 0xFFFFFFFC00000004},
+ {0xFFFFFFFFFFFFFFFB, 0x00000004FFFFFFFF, 0x0000000000000000, 0xFFFFFFFB00000005},
+ {0xFFFFFFFFFFFFFFFA, 0x00000005FFFFFFFF, 0x0000000000000000, 0xFFFFFFFA00000006},
+ {0xFFFFFFFFFFFFFFF9, 0x00000006FFFFFFFF, 0x0000000000000000, 0xFFFFFFF900000007},
+ {0xFFFFFFFFFFFFFFF8, 0x00000007FFFFFFFF, 0x0000000000000000, 0xFFFFFFF800000008},
+ {0xFFFFFFFFFFFFFFF7, 0x00000008FFFFFFFF, 0x0000000000000000, 0xFFFFFFF700000009},
+ {0xFFFFFFFFFFFFFFF6, 0x00000009FFFFFFFF, 0x0000000000000000, 0xFFFFFFF60000000A},
+ {0xFFFFFFFFFFFFFFF5, 0x0000000AFFFFFFFF, 0x0000000000000000, 0xFFFFFFF50000000B},
+#else
+ {0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000, 0x00000001, 0xFFFFFFFF},
+ {0xFFFFFFFE, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000001, 0x00000000, 0x00000000, 0x00000002, 0xFFFFFFFE},
+ {0xFFFFFFFD, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000002, 0x00000000, 0x00000000, 0x00000003, 0xFFFFFFFD},
+ {0xFFFFFFFC, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000003, 0x00000000, 0x00000000, 0x00000004, 0xFFFFFFFC},
+ {0xFFFFFFFB, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000004, 0x00000000, 0x00000000, 0x00000005, 0xFFFFFFFB},
+ {0xFFFFFFFA, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000005, 0x00000000, 0x00000000, 0x00000006, 0xFFFFFFFA},
+ {0xFFFFFFF9, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000006, 0x00000000, 0x00000000, 0x00000007, 0xFFFFFFF9},
+ {0xFFFFFFF8, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000007, 0x00000000, 0x00000000, 0x00000008, 0xFFFFFFF8},
+ {0xFFFFFFF7, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000008, 0x00000000, 0x00000000, 0x00000009, 0xFFFFFFF7},
+ {0xFFFFFFF6, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000009, 0x00000000, 0x00000000, 0x0000000A, 0xFFFFFFF6},
+ {0xFFFFFFF5, 0xFFFFFFFF, 0xFFFFFFFF, 0x0000000A, 0x00000000, 0x00000000, 0x0000000B, 0xFFFFFFF5},
+#endif
+ };
+
+ CT::unpoison(S);
+ BOTAN_ASSERT(S >= 0 && S <= 10, "Expected overflow");
+
+ BOTAN_ASSERT_NOMSG(x.size() >= p256_limbs + 1);
+ x.mask_bits(256);
+ word borrow = bigint_sub2(x.mutable_data(), p256_limbs + 1, p256_mults[S], p256_limbs);
+ BOTAN_DEBUG_ASSERT(borrow == 0 || borrow == 1);
+ bigint_cnd_add(borrow, x.mutable_data(), p256_limbs + 1, p256_mults[0], p256_limbs);
+ }
+
+const BigInt& prime_p384()
+ {
+ static const BigInt p384("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFF");
+ return p384;
+ }
+
+void redc_p384(BigInt& x, secure_vector<word>& ws)
+ {
+ BOTAN_UNUSED(ws);
+
+ static const size_t p384_limbs = (BOTAN_MP_WORD_BITS == 32) ? 12 : 6;
+
+ x.grow_to(2*p384_limbs);
+ word* xw = x.mutable_data();
+
+ const int64_t X00 = get_uint32(xw, 0);
+ const int64_t X01 = get_uint32(xw, 1);
+ const int64_t X02 = get_uint32(xw, 2);
+ const int64_t X03 = get_uint32(xw, 3);
+ const int64_t X04 = get_uint32(xw, 4);
+ const int64_t X05 = get_uint32(xw, 5);
+ const int64_t X06 = get_uint32(xw, 6);
+ const int64_t X07 = get_uint32(xw, 7);
+ const int64_t X08 = get_uint32(xw, 8);
+ const int64_t X09 = get_uint32(xw, 9);
+ const int64_t X10 = get_uint32(xw, 10);
+ const int64_t X11 = get_uint32(xw, 11);
+ const int64_t X12 = get_uint32(xw, 12);
+ const int64_t X13 = get_uint32(xw, 13);
+ const int64_t X14 = get_uint32(xw, 14);
+ const int64_t X15 = get_uint32(xw, 15);
+ const int64_t X16 = get_uint32(xw, 16);
+ const int64_t X17 = get_uint32(xw, 17);
+ const int64_t X18 = get_uint32(xw, 18);
+ const int64_t X19 = get_uint32(xw, 19);
+ const int64_t X20 = get_uint32(xw, 20);
+ const int64_t X21 = get_uint32(xw, 21);
+ const int64_t X22 = get_uint32(xw, 22);
+ const int64_t X23 = get_uint32(xw, 23);
+
+ // One copy of P-384 is added to prevent underflow
+ const int64_t S0 = 0xFFFFFFFF + X00 + X12 + X20 + X21 - X23;
+ const int64_t S1 = 0x00000000 + X01 + X13 + X22 + X23 - X12 - X20;
+ const int64_t S2 = 0x00000000 + X02 + X14 + X23 - X13 - X21;
+ const int64_t S3 = 0xFFFFFFFF + X03 + X12 + X15 + X20 + X21 - X14 - X22 - X23;
+ const int64_t S4 = 0xFFFFFFFE + X04 + X12 + X13 + X16 + X20 + X21*2 + X22 - X15 - X23*2;
+ const int64_t S5 = 0xFFFFFFFF + X05 + X13 + X14 + X17 + X21 + X22*2 + X23 - X16;
+ const int64_t S6 = 0xFFFFFFFF + X06 + X14 + X15 + X18 + X22 + X23*2 - X17;
+ const int64_t S7 = 0xFFFFFFFF + X07 + X15 + X16 + X19 + X23 - X18;
+ const int64_t S8 = 0xFFFFFFFF + X08 + X16 + X17 + X20 - X19;
+ const int64_t S9 = 0xFFFFFFFF + X09 + X17 + X18 + X21 - X20;
+ const int64_t SA = 0xFFFFFFFF + X10 + X18 + X19 + X22 - X21;
+ const int64_t SB = 0xFFFFFFFF + X11 + X19 + X20 + X23 - X22;
+
+ int64_t S = 0;
+
+ uint32_t R0 = 0, R1 = 0;
+
+ S += S0;
+ R0 = static_cast<uint32_t>(S);
+ S >>= 32;
+
+ S += S1;
+ R1 = static_cast<uint32_t>(S);
+ S >>= 32;
+
+ set_words(xw, 0, R0, R1);
+
+ S += S2;
+ R0 = static_cast<uint32_t>(S);
+ S >>= 32;
+
+ S += S3;
+ R1 = static_cast<uint32_t>(S);
+ S >>= 32;
+
+ set_words(xw, 2, R0, R1);
+
+ S += S4;
+ R0 = static_cast<uint32_t>(S);
+ S >>= 32;
+
+ S += S5;
+ R1 = static_cast<uint32_t>(S);
+ S >>= 32;
+
+ set_words(xw, 4, R0, R1);
+
+ S += S6;
+ R0 = static_cast<uint32_t>(S);
+ S >>= 32;
+
+ S += S7;
+ R1 = static_cast<uint32_t>(S);
+ S >>= 32;
+
+ set_words(xw, 6, R0, R1);
+
+ S += S8;
+ R0 = static_cast<uint32_t>(S);
+ S >>= 32;
+
+ S += S9;
+ R1 = static_cast<uint32_t>(S);
+ S >>= 32;
+
+ set_words(xw, 8, R0, R1);
+
+ S += SA;
+ R0 = static_cast<uint32_t>(S);
+ S >>= 32;
+
+ S += SB;
+ R1 = static_cast<uint32_t>(S);
+ S >>= 32;
+
+ set_words(xw, 10, R0, R1);
+
+ /*
+ This is a table of (i*P-384) % 2**384 for i in 1...4
+ */
+ static const word p384_mults[5][p384_limbs] = {
+#if (BOTAN_MP_WORD_BITS == 64)
+ {0x00000000FFFFFFFF, 0xFFFFFFFF00000000, 0xFFFFFFFFFFFFFFFE, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF},
+ {0x00000001FFFFFFFE, 0xFFFFFFFE00000000, 0xFFFFFFFFFFFFFFFD, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF},
+ {0x00000002FFFFFFFD, 0xFFFFFFFD00000000, 0xFFFFFFFFFFFFFFFC, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF},
+ {0x00000003FFFFFFFC, 0xFFFFFFFC00000000, 0xFFFFFFFFFFFFFFFB, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF},
+ {0x00000004FFFFFFFB, 0xFFFFFFFB00000000, 0xFFFFFFFFFFFFFFFA, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF},
+
+#else
+ {0xFFFFFFFF, 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFE, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF},
+ {0xFFFFFFFE, 0x00000001, 0x00000000, 0xFFFFFFFE, 0xFFFFFFFD, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF},
+ {0xFFFFFFFD, 0x00000002, 0x00000000, 0xFFFFFFFD, 0xFFFFFFFC, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF},
+ {0xFFFFFFFC, 0x00000003, 0x00000000, 0xFFFFFFFC, 0xFFFFFFFB, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF},
+ {0xFFFFFFFB, 0x00000004, 0x00000000, 0xFFFFFFFB, 0xFFFFFFFA, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF},
+#endif
+ };
+
+ CT::unpoison(S);
+ BOTAN_ASSERT(S >= 0 && S <= 4, "Expected overflow");
+
+ BOTAN_ASSERT_NOMSG(x.size() >= p384_limbs + 1);
+ x.mask_bits(384);
+ word borrow = bigint_sub2(x.mutable_data(), p384_limbs + 1, p384_mults[S], p384_limbs);
+ BOTAN_DEBUG_ASSERT(borrow == 0 || borrow == 1);
+ bigint_cnd_add(borrow, x.mutable_data(), p384_limbs + 1, p384_mults[0], p384_limbs);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/math/numbertheory/numthry.cpp b/comm/third_party/botan/src/lib/math/numbertheory/numthry.cpp
new file mode 100644
index 0000000000..51afa94c64
--- /dev/null
+++ b/comm/third_party/botan/src/lib/math/numbertheory/numthry.cpp
@@ -0,0 +1,268 @@
+/*
+* Number Theory Functions
+* (C) 1999-2011,2016,2018,2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/numthry.h>
+#include <botan/reducer.h>
+#include <botan/monty.h>
+#include <botan/divide.h>
+#include <botan/rng.h>
+#include <botan/internal/ct_utils.h>
+#include <botan/internal/mp_core.h>
+#include <botan/internal/monty_exp.h>
+#include <botan/internal/primality.h>
+#include <algorithm>
+
+namespace Botan {
+
+namespace {
+
+void sub_abs(BigInt& z, const BigInt& x, const BigInt& y)
+ {
+ const size_t x_sw = x.sig_words();
+ const size_t y_sw = y.sig_words();
+ z.resize(std::max(x_sw, y_sw));
+
+ bigint_sub_abs(z.mutable_data(),
+ x.data(), x_sw,
+ y.data(), y_sw);
+ }
+
+}
+
+/*
+* Return the number of 0 bits at the end of n
+*/
+size_t low_zero_bits(const BigInt& n)
+ {
+ size_t low_zero = 0;
+
+ auto seen_nonempty_word = CT::Mask<word>::cleared();
+
+ for(size_t i = 0; i != n.size(); ++i)
+ {
+ const word x = n.word_at(i);
+
+ // ctz(0) will return sizeof(word)
+ const size_t tz_x = ctz(x);
+
+ // if x > 0 we want to count tz_x in total but not any
+ // further words, so set the mask after the addition
+ low_zero += seen_nonempty_word.if_not_set_return(tz_x);
+
+ seen_nonempty_word |= CT::Mask<word>::expand(x);
+ }
+
+ // if we saw no words with x > 0 then n == 0 and the value we have
+ // computed is meaningless. Instead return 0 in that case.
+ return seen_nonempty_word.if_set_return(low_zero);
+ }
+
+namespace {
+
+size_t safegcd_loop_bound(size_t f_bits, size_t g_bits)
+ {
+ const size_t d = std::max(f_bits, g_bits);
+
+ if(d < 46)
+ return (49*d + 80) / 17;
+ else
+ return (49*d + 57) / 17;
+ }
+
+}
+
+/*
+* Calculate the GCD
+*/
+BigInt gcd(const BigInt& a, const BigInt& b)
+ {
+ if(a.is_zero())
+ return abs(b);
+ if(b.is_zero())
+ return abs(a);
+ if(a == 1 || b == 1)
+ return 1;
+
+ // See https://gcd.cr.yp.to/safegcd-20190413.pdf fig 1.2
+
+ BigInt f = a;
+ BigInt g = b;
+ f.const_time_poison();
+ g.const_time_poison();
+
+ f.set_sign(BigInt::Positive);
+ g.set_sign(BigInt::Positive);
+
+ const size_t common2s = std::min(low_zero_bits(f), low_zero_bits(g));
+ CT::unpoison(common2s);
+
+ f >>= common2s;
+ g >>= common2s;
+
+ f.ct_cond_swap(f.is_even(), g);
+
+ int32_t delta = 1;
+
+ const size_t loop_cnt = safegcd_loop_bound(f.bits(), g.bits());
+
+ BigInt newg, t;
+ for(size_t i = 0; i != loop_cnt; ++i)
+ {
+ sub_abs(newg, f, g);
+
+ const bool need_swap = (g.is_odd() && delta > 0);
+
+ // if(need_swap) { delta *= -1 } else { delta *= 1 }
+ delta *= CT::Mask<uint8_t>::expand(need_swap).if_not_set_return(2) - 1;
+ f.ct_cond_swap(need_swap, g);
+ g.ct_cond_swap(need_swap, newg);
+
+ delta += 1;
+
+ g.ct_cond_add(g.is_odd(), f);
+ g >>= 1;
+ }
+
+ f <<= common2s;
+
+ f.const_time_unpoison();
+ g.const_time_unpoison();
+
+ BOTAN_ASSERT_NOMSG(g.is_zero());
+
+ return f;
+ }
+
+/*
+* Calculate the LCM
+*/
+BigInt lcm(const BigInt& a, const BigInt& b)
+ {
+ return ct_divide(a * b, gcd(a, b));
+ }
+
+/*
+* Modular Exponentiation
+*/
+BigInt power_mod(const BigInt& base, const BigInt& exp, const BigInt& mod)
+ {
+ if(mod.is_negative() || mod == 1)
+ {
+ return 0;
+ }
+
+ if(base.is_zero() || mod.is_zero())
+ {
+ if(exp.is_zero())
+ return 1;
+ return 0;
+ }
+
+ Modular_Reducer reduce_mod(mod);
+
+ const size_t exp_bits = exp.bits();
+
+ if(mod.is_odd())
+ {
+ const size_t powm_window = 4;
+
+ auto monty_mod = std::make_shared<Montgomery_Params>(mod, reduce_mod);
+ auto powm_base_mod = monty_precompute(monty_mod, reduce_mod.reduce(base), powm_window);
+ return monty_execute(*powm_base_mod, exp, exp_bits);
+ }
+
+ /*
+ Support for even modulus is just a convenience and not considered
+ cryptographically important, so this implementation is slow ...
+ */
+ BigInt accum = 1;
+ BigInt g = reduce_mod.reduce(base);
+ BigInt t;
+
+ for(size_t i = 0; i != exp_bits; ++i)
+ {
+ t = reduce_mod.multiply(g, accum);
+ g = reduce_mod.square(g);
+ accum.ct_cond_assign(exp.get_bit(i), t);
+ }
+ return accum;
+ }
+
+
+BigInt is_perfect_square(const BigInt& C)
+ {
+ if(C < 1)
+ throw Invalid_Argument("is_perfect_square requires C >= 1");
+ if(C == 1)
+ return 1;
+
+ const size_t n = C.bits();
+ const size_t m = (n + 1) / 2;
+ const BigInt B = C + BigInt::power_of_2(m);
+
+ BigInt X = BigInt::power_of_2(m) - 1;
+ BigInt X2 = (X*X);
+
+ for(;;)
+ {
+ X = (X2 + C) / (2*X);
+ X2 = (X*X);
+
+ if(X2 < B)
+ break;
+ }
+
+ if(X2 == C)
+ return X;
+ else
+ return 0;
+ }
+
+/*
+* Test for primality using Miller-Rabin
+*/
+bool is_prime(const BigInt& n,
+ RandomNumberGenerator& rng,
+ size_t prob,
+ bool is_random)
+ {
+ if(n == 2)
+ return true;
+ if(n <= 1 || n.is_even())
+ return false;
+
+ const size_t n_bits = n.bits();
+
+ // Fast path testing for small numbers (<= 65521)
+ if(n_bits <= 16)
+ {
+ const uint16_t num = static_cast<uint16_t>(n.word_at(0));
+
+ return std::binary_search(PRIMES, PRIMES + PRIME_TABLE_SIZE, num);
+ }
+
+ Modular_Reducer mod_n(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)
+ return false;
+
+ if(is_random)
+ return true;
+ else
+ return is_lucas_probable_prime(n, mod_n);
+ }
+ else
+ {
+ return is_bailie_psw_probable_prime(n, mod_n);
+ }
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/math/numbertheory/numthry.h b/comm/third_party/botan/src/lib/math/numbertheory/numthry.h
new file mode 100644
index 0000000000..be9cd985ea
--- /dev/null
+++ b/comm/third_party/botan/src/lib/math/numbertheory/numthry.h
@@ -0,0 +1,296 @@
+/*
+* Number Theory Functions
+* (C) 1999-2007,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_NUMBER_THEORY_H_
+#define BOTAN_NUMBER_THEORY_H_
+
+#include <botan/bigint.h>
+
+namespace Botan {
+
+class RandomNumberGenerator;
+
+/**
+* Fused multiply-add
+* @param a an integer
+* @param b an integer
+* @param c an integer
+* @return (a*b)+c
+*/
+BigInt BOTAN_PUBLIC_API(2,0) BOTAN_DEPRECATED("Just use (a*b)+c")
+ mul_add(const BigInt& a,
+ const BigInt& b,
+ const BigInt& c);
+
+/**
+* Fused subtract-multiply
+* @param a an integer
+* @param b an integer
+* @param c an integer
+* @return (a-b)*c
+*/
+BigInt BOTAN_PUBLIC_API(2,0) BOTAN_DEPRECATED("Just use (a-b)*c")
+ sub_mul(const BigInt& a,
+ const BigInt& b,
+ const BigInt& c);
+
+/**
+* Fused multiply-subtract
+* @param a an integer
+* @param b an integer
+* @param c an integer
+* @return (a*b)-c
+*/
+BigInt BOTAN_PUBLIC_API(2,0) BOTAN_DEPRECATED("Just use (a*b)-c")
+ mul_sub(const BigInt& a,
+ const BigInt& b,
+ const BigInt& c);
+
+/**
+* Return the absolute value
+* @param n an integer
+* @return absolute value of n
+*/
+inline BigInt abs(const BigInt& n) { return n.abs(); }
+
+/**
+* Compute the greatest common divisor
+* @param x a positive integer
+* @param y a positive integer
+* @return gcd(x,y)
+*/
+BigInt BOTAN_PUBLIC_API(2,0) gcd(const BigInt& x, const BigInt& y);
+
+/**
+* Least common multiple
+* @param x a positive integer
+* @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);
+
+/**
+* @param x an integer
+* @return (x*x)
+*/
+BigInt BOTAN_PUBLIC_API(2,0) square(const BigInt& x);
+
+/**
+* Modular inversion. This algorithm is const time with respect to x,
+* as long as x is less than modulus. It also avoids leaking
+* information about the modulus, except that it does leak which of 3
+* categories the modulus is in: an odd integer, a power of 2, or some
+* other even number, and if the modulus is even, leaks the power of 2
+* which divides the modulus.
+*
+* @param x a positive integer
+* @param modulus a positive integer
+* @return y st (x*y) % modulus == 1 or 0 if no such value
+*/
+BigInt BOTAN_PUBLIC_API(2,0) inverse_mod(const BigInt& x,
+ const BigInt& modulus);
+
+/**
+* Deprecated modular inversion function. Use inverse_mod instead.
+* @param x a positive integer
+* @param modulus a positive integer
+* @return y st (x*y) % modulus == 1 or 0 if no such value
+*/
+BigInt BOTAN_DEPRECATED_API("Use inverse_mod") inverse_euclid(const BigInt& x, const BigInt& modulus);
+
+/**
+* Deprecated modular inversion function. Use inverse_mod instead.
+*/
+BigInt BOTAN_DEPRECATED_API("Use inverse_mod") ct_inverse_mod_odd_modulus(const BigInt& x, const BigInt& modulus);
+
+/**
+* Return a^-1 * 2^k mod b
+* Returns k, between n and 2n
+* Not const time
+*/
+size_t BOTAN_PUBLIC_API(2,0) BOTAN_DEPRECATED("Use inverse_mod")
+ almost_montgomery_inverse(BigInt& result,
+ const BigInt& a,
+ const BigInt& b);
+
+/**
+* Call almost_montgomery_inverse and correct the result to a^-1 mod b
+*/
+BigInt BOTAN_PUBLIC_API(2,0) BOTAN_DEPRECATED("Use inverse_mod")
+ normalized_montgomery_inverse(const BigInt& a, const BigInt& b);
+
+
+/**
+* Compute the Jacobi symbol. If n is prime, this is equivalent
+* to the Legendre symbol.
+* @see http://mathworld.wolfram.com/JacobiSymbol.html
+*
+* @param a is a non-negative integer
+* @param n is an odd integer > 1
+* @return (n / m)
+*/
+int32_t BOTAN_PUBLIC_API(2,0) jacobi(const BigInt& a, const BigInt& n);
+
+/**
+* Modular exponentation
+* @param b an integer base
+* @param x a positive exponent
+* @param m a positive modulus
+* @return (b^x) % m
+*/
+BigInt BOTAN_PUBLIC_API(2,0) power_mod(const BigInt& b,
+ const BigInt& x,
+ const BigInt& m);
+
+/**
+* Compute the square root of x modulo a prime using the
+* Tonelli-Shanks algorithm
+*
+* @param x the input
+* @param p the prime
+* @return y such that (y*y)%p == x, or -1 if no such integer
+*/
+BigInt BOTAN_PUBLIC_API(2,0) ressol(const BigInt& x, const BigInt& p);
+
+/*
+* Compute -input^-1 mod 2^MP_WORD_BITS. Throws an exception if input
+* is even. If input is odd, then input and 2^n are relatively prime
+* and an inverse exists.
+*/
+word BOTAN_PUBLIC_API(2,0) BOTAN_DEPRECATED("Use inverse_mod")
+ monty_inverse(word input);
+
+/**
+* @param x an integer
+* @return count of the low zero bits in x, or, equivalently, the
+* 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);
+
+/**
+* Check for primality
+* @param n a positive integer to test for primality
+* @param rng a random number generator
+* @param prob chance of false positive is bounded by 1/2**prob
+* @param is_random true if n was randomly chosen by us
+* @return true if all primality tests passed, otherwise false
+*/
+bool BOTAN_PUBLIC_API(2,0) is_prime(const BigInt& n,
+ RandomNumberGenerator& rng,
+ size_t prob = 64,
+ bool is_random = false);
+
+/**
+* Test if the positive integer x is a perfect square ie if there
+* exists some positive integer y st y*y == x
+* See FIPS 186-4 sec C.4
+* @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);
+
+inline bool BOTAN_DEPRECATED("Use is_prime")
+ quick_check_prime(const BigInt& n, RandomNumberGenerator& rng)
+ { return is_prime(n, rng, 32); }
+
+inline bool BOTAN_DEPRECATED("Use is_prime")
+ check_prime(const BigInt& n, RandomNumberGenerator& rng)
+ { return is_prime(n, rng, 56); }
+
+inline bool BOTAN_DEPRECATED("Use is_prime")
+ verify_prime(const BigInt& n, RandomNumberGenerator& rng)
+ { return is_prime(n, rng, 80); }
+
+/**
+* Randomly generate a prime suitable for discrete logarithm parameters
+* @param rng a random number generator
+* @param bits how large the resulting prime should be in bits
+* @param coprime a positive integer that (prime - 1) should be coprime to
+* @param equiv a non-negative number that the result should be
+ equivalent to modulo equiv_mod
+* @param equiv_mod the modulus equiv should be checked against
+* @param prob use test so false positive is bounded by 1/2**prob
+* @return random prime with the specified criteria
+*/
+BigInt BOTAN_PUBLIC_API(2,0) random_prime(RandomNumberGenerator& rng,
+ size_t bits,
+ const BigInt& coprime = 0,
+ size_t equiv = 1,
+ size_t equiv_mod = 2,
+ size_t prob = 128);
+
+/**
+* Generate a prime suitable for RSA p/q
+* @param keygen_rng a random number generator
+* @param prime_test_rng a random number generator
+* @param bits how large the resulting prime should be in bits (must be >= 512)
+* @param coprime a positive integer that (prime - 1) should be coprime to
+* @param prob use test so false positive is bounded by 1/2**prob
+* @return random prime with the specified criteria
+*/
+BigInt BOTAN_PUBLIC_API(2,7) generate_rsa_prime(RandomNumberGenerator& keygen_rng,
+ RandomNumberGenerator& prime_test_rng,
+ size_t bits,
+ const BigInt& coprime,
+ size_t prob = 128);
+
+/**
+* Return a 'safe' prime, of the form p=2*q+1 with q prime
+* @param rng a random number generator
+* @param bits is how long the resulting prime should be
+* @return prime randomly chosen from safe primes of length bits
+*/
+BigInt BOTAN_PUBLIC_API(2,0) random_safe_prime(RandomNumberGenerator& rng,
+ size_t bits);
+
+/**
+* Generate DSA parameters using the FIPS 186 kosherizer
+* @param rng a random number generator
+* @param p_out where the prime p will be stored
+* @param q_out where the prime q will be stored
+* @param pbits how long p will be in bits
+* @param qbits how long q will be in bits
+* @return random seed used to generate this parameter set
+*/
+std::vector<uint8_t> BOTAN_PUBLIC_API(2,0) BOTAN_DEPRECATED("Use DL_Group")
+generate_dsa_primes(RandomNumberGenerator& rng,
+ BigInt& p_out, BigInt& q_out,
+ size_t pbits, size_t qbits);
+
+/**
+* Generate DSA parameters using the FIPS 186 kosherizer
+* @param rng a random number generator
+* @param p_out where the prime p will be stored
+* @param q_out where the prime q will be stored
+* @param pbits how long p will be in bits
+* @param qbits how long q will be in bits
+* @param seed the seed used to generate the parameters
+* @param offset optional offset from seed to start searching at
+* @return true if seed generated a valid DSA parameter set, otherwise
+ false. p_out and q_out are only valid if true was returned.
+*/
+bool BOTAN_PUBLIC_API(2,0) BOTAN_DEPRECATED("Use DL_Group")
+generate_dsa_primes(RandomNumberGenerator& rng,
+ BigInt& p_out, BigInt& q_out,
+ size_t pbits, size_t qbits,
+ const std::vector<uint8_t>& seed,
+ size_t offset = 0);
+
+/**
+* The size of the PRIMES[] array
+*/
+const size_t PRIME_TABLE_SIZE = 6541;
+
+/**
+* A const array of all odd primes less than 65535
+*/
+extern const uint16_t BOTAN_PUBLIC_API(2,0) PRIMES[];
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/math/numbertheory/pow_mod.cpp b/comm/third_party/botan/src/lib/math/numbertheory/pow_mod.cpp
new file mode 100644
index 0000000000..7b38fad1d8
--- /dev/null
+++ b/comm/third_party/botan/src/lib/math/numbertheory/pow_mod.cpp
@@ -0,0 +1,328 @@
+/*
+* Modular Exponentiation Proxy
+* (C) 1999-2007,2012,2018,2019 Jack Lloyd
+* 2016 Matthias Gierlings
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/pow_mod.h>
+#include <botan/numthry.h>
+#include <botan/reducer.h>
+#include <botan/monty.h>
+#include <botan/internal/monty_exp.h>
+#include <botan/internal/rounding.h>
+#include <vector>
+
+namespace Botan {
+
+class Modular_Exponentiator
+ {
+ public:
+ virtual void set_base(const BigInt&) = 0;
+ virtual void set_exponent(const BigInt&) = 0;
+ virtual BigInt execute() const = 0;
+ virtual Modular_Exponentiator* copy() const = 0;
+
+ Modular_Exponentiator() = default;
+ Modular_Exponentiator(const Modular_Exponentiator&) = default;
+ Modular_Exponentiator & operator=(const Modular_Exponentiator&) = default;
+ virtual ~Modular_Exponentiator() = default;
+ };
+
+namespace {
+
+/**
+* Fixed Window Exponentiator
+*/
+class Fixed_Window_Exponentiator final : public Modular_Exponentiator
+ {
+ public:
+ void set_exponent(const BigInt& e) override { m_exp = e; }
+ void set_base(const BigInt&) override;
+ BigInt execute() const override;
+
+ Modular_Exponentiator* copy() const override
+ { return new Fixed_Window_Exponentiator(*this); }
+
+ Fixed_Window_Exponentiator(const BigInt&, Power_Mod::Usage_Hints);
+ private:
+ Modular_Reducer m_reducer;
+ BigInt m_exp;
+ size_t m_window_bits;
+ std::vector<BigInt> m_g;
+ Power_Mod::Usage_Hints m_hints;
+ };
+
+void Fixed_Window_Exponentiator::set_base(const BigInt& base)
+ {
+ m_window_bits = Power_Mod::window_bits(m_exp.bits(), base.bits(), m_hints);
+
+ m_g.resize(static_cast<size_t>(1) << m_window_bits);
+ m_g[0] = 1;
+ m_g[1] = m_reducer.reduce(base);
+
+ for(size_t i = 2; i != m_g.size(); ++i)
+ m_g[i] = m_reducer.multiply(m_g[i-1], m_g[1]);
+ }
+
+BigInt Fixed_Window_Exponentiator::execute() const
+ {
+ const size_t exp_nibbles = (m_exp.bits() + m_window_bits - 1) / m_window_bits;
+
+ BigInt x = 1;
+
+ for(size_t i = exp_nibbles; i > 0; --i)
+ {
+ for(size_t j = 0; j != m_window_bits; ++j)
+ x = m_reducer.square(x);
+
+ const uint32_t nibble = m_exp.get_substring(m_window_bits*(i-1), m_window_bits);
+
+ // not const time:
+ x = m_reducer.multiply(x, m_g[nibble]);
+ }
+ return x;
+ }
+
+/*
+* Fixed_Window_Exponentiator Constructor
+*/
+Fixed_Window_Exponentiator::Fixed_Window_Exponentiator(const BigInt& n,
+ Power_Mod::Usage_Hints hints)
+ : m_reducer{Modular_Reducer(n)}, m_exp{}, m_window_bits{}, m_g{}, m_hints{hints}
+ {}
+
+class Montgomery_Exponentiator final : public Modular_Exponentiator
+ {
+ public:
+ void set_exponent(const BigInt& e) override { m_e = e; }
+ void set_base(const BigInt&) override;
+ BigInt execute() const override;
+
+ Modular_Exponentiator* copy() const override
+ { return new Montgomery_Exponentiator(*this); }
+
+ Montgomery_Exponentiator(const BigInt&, Power_Mod::Usage_Hints);
+ private:
+ BigInt m_p;
+ Modular_Reducer m_mod_p;
+ std::shared_ptr<const Montgomery_Params> m_monty_params;
+ std::shared_ptr<const Montgomery_Exponentation_State> m_monty;
+
+ BigInt m_e;
+ Power_Mod::Usage_Hints m_hints;
+ };
+
+void Montgomery_Exponentiator::set_base(const BigInt& base)
+ {
+ size_t window_bits = Power_Mod::window_bits(m_e.bits(), base.bits(), m_hints);
+ m_monty = monty_precompute(m_monty_params, m_mod_p.reduce(base), window_bits);
+ }
+
+BigInt Montgomery_Exponentiator::execute() const
+ {
+ /*
+ This leaks size of e via loop iterations, not possible to fix without
+ breaking this API. Round up to avoid leaking fine details.
+ */
+ return monty_execute(*m_monty, m_e, round_up(m_e.bits(), 8));
+ }
+
+Montgomery_Exponentiator::Montgomery_Exponentiator(const BigInt& mod,
+ Power_Mod::Usage_Hints hints) :
+ m_p(mod),
+ m_mod_p(mod),
+ m_monty_params(std::make_shared<Montgomery_Params>(m_p, m_mod_p)),
+ m_hints(hints)
+ {
+ }
+
+}
+
+/*
+* Power_Mod Constructor
+*/
+Power_Mod::Power_Mod(const BigInt& n, Usage_Hints hints, bool disable_monty)
+ {
+ set_modulus(n, hints, disable_monty);
+ }
+
+Power_Mod::~Power_Mod() { /* for ~unique_ptr */ }
+
+/*
+* Power_Mod Copy Constructor
+*/
+Power_Mod::Power_Mod(const Power_Mod& other)
+ {
+ if(other.m_core.get())
+ m_core.reset(other.m_core->copy());
+ }
+
+/*
+* Power_Mod Assignment Operator
+*/
+Power_Mod& Power_Mod::operator=(const Power_Mod& other)
+ {
+ if(this != &other)
+ {
+ if(other.m_core)
+ m_core.reset(other.m_core->copy());
+ else
+ m_core.reset();
+ }
+ return (*this);
+ }
+
+/*
+* Set the modulus
+*/
+void Power_Mod::set_modulus(const BigInt& n, Usage_Hints hints, bool disable_monty) const
+ {
+ // Allow set_modulus(0) to mean "drop old state"
+
+ m_core.reset();
+
+ if(n != 0)
+ {
+ if(n.is_odd() && disable_monty == false)
+ m_core.reset(new Montgomery_Exponentiator(n, hints));
+ else
+ m_core.reset(new Fixed_Window_Exponentiator(n, hints));
+ }
+ }
+
+/*
+* Set the base
+*/
+void Power_Mod::set_base(const BigInt& b) const
+ {
+ if(b.is_negative())
+ throw Invalid_Argument("Power_Mod::set_base: arg must be non-negative");
+
+ if(!m_core)
+ throw Internal_Error("Power_Mod::set_base: m_core was NULL");
+ m_core->set_base(b);
+ }
+
+/*
+* Set the exponent
+*/
+void Power_Mod::set_exponent(const BigInt& e) const
+ {
+ if(e.is_negative())
+ throw Invalid_Argument("Power_Mod::set_exponent: arg must be > 0");
+
+ if(!m_core)
+ throw Internal_Error("Power_Mod::set_exponent: m_core was NULL");
+ m_core->set_exponent(e);
+ }
+
+/*
+* Compute the result
+*/
+BigInt Power_Mod::execute() const
+ {
+ if(!m_core)
+ throw Internal_Error("Power_Mod::execute: m_core was NULL");
+ return m_core->execute();
+ }
+
+/*
+* Try to choose a good window size
+*/
+size_t Power_Mod::window_bits(size_t exp_bits, size_t,
+ Power_Mod::Usage_Hints hints)
+ {
+ static const size_t wsize[][2] = {
+ { 1434, 7 },
+ { 539, 6 },
+ { 197, 4 },
+ { 70, 3 },
+ { 17, 2 },
+ { 0, 0 }
+ };
+
+ size_t window_bits = 1;
+
+ if(exp_bits)
+ {
+ for(size_t j = 0; wsize[j][0]; ++j)
+ {
+ if(exp_bits >= wsize[j][0])
+ {
+ window_bits += wsize[j][1];
+ break;
+ }
+ }
+ }
+
+ if(hints & Power_Mod::BASE_IS_FIXED)
+ window_bits += 2;
+ if(hints & Power_Mod::EXP_IS_LARGE)
+ ++window_bits;
+
+ return window_bits;
+ }
+
+namespace {
+
+/*
+* Choose potentially useful hints
+*/
+Power_Mod::Usage_Hints choose_base_hints(const BigInt& b, const BigInt& n)
+ {
+ if(b == 2)
+ return Power_Mod::Usage_Hints(Power_Mod::BASE_IS_2 |
+ Power_Mod::BASE_IS_SMALL);
+
+ const size_t b_bits = b.bits();
+ const size_t n_bits = n.bits();
+
+ if(b_bits < n_bits / 32)
+ return Power_Mod::BASE_IS_SMALL;
+ if(b_bits > n_bits / 4)
+ return Power_Mod::BASE_IS_LARGE;
+
+ return Power_Mod::NO_HINTS;
+ }
+
+/*
+* Choose potentially useful hints
+*/
+Power_Mod::Usage_Hints choose_exp_hints(const BigInt& e, const BigInt& n)
+ {
+ const size_t e_bits = e.bits();
+ const size_t n_bits = n.bits();
+
+ if(e_bits < n_bits / 32)
+ return Power_Mod::BASE_IS_SMALL;
+ if(e_bits > n_bits / 4)
+ return Power_Mod::BASE_IS_LARGE;
+ return Power_Mod::NO_HINTS;
+ }
+
+}
+
+/*
+* Fixed_Exponent_Power_Mod Constructor
+*/
+Fixed_Exponent_Power_Mod::Fixed_Exponent_Power_Mod(const BigInt& e,
+ const BigInt& n,
+ Usage_Hints hints) :
+ Power_Mod(n, Usage_Hints(hints | EXP_IS_FIXED | choose_exp_hints(e, n)))
+ {
+ set_exponent(e);
+ }
+
+/*
+* Fixed_Base_Power_Mod Constructor
+*/
+Fixed_Base_Power_Mod::Fixed_Base_Power_Mod(const BigInt& b, const BigInt& n,
+ Usage_Hints hints) :
+ Power_Mod(n, Usage_Hints(hints | BASE_IS_FIXED | choose_base_hints(b, n)))
+ {
+ set_base(b);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/math/numbertheory/pow_mod.h b/comm/third_party/botan/src/lib/math/numbertheory/pow_mod.h
new file mode 100644
index 0000000000..b465013e55
--- /dev/null
+++ b/comm/third_party/botan/src/lib/math/numbertheory/pow_mod.h
@@ -0,0 +1,122 @@
+/*
+* Modular Exponentiator
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_POWER_MOD_H_
+#define BOTAN_POWER_MOD_H_
+
+#include <botan/bigint.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(pow_mod.h)
+
+namespace Botan {
+
+class Modular_Exponentiator;
+
+/**
+* Modular Exponentiator Proxy
+*/
+class BOTAN_PUBLIC_API(2,0) Power_Mod
+ {
+ public:
+
+ enum Usage_Hints {
+ NO_HINTS = 0x0000,
+
+ BASE_IS_FIXED = 0x0001,
+ BASE_IS_SMALL = 0x0002,
+ BASE_IS_LARGE = 0x0004,
+ BASE_IS_2 = 0x0008,
+
+ EXP_IS_FIXED = 0x0100,
+ EXP_IS_SMALL = 0x0200,
+ EXP_IS_LARGE = 0x0400
+ };
+
+ /*
+ * Try to choose a good window size
+ */
+ static size_t window_bits(size_t exp_bits, size_t base_bits,
+ Power_Mod::Usage_Hints hints);
+
+ /**
+ * @param modulus the modulus
+ * @param hints Passed to set_modulus if modulus > 0
+ * @param disable_montgomery_arith Disables use of Montgomery
+ * representation. Likely only useful for testing.
+ */
+ void set_modulus(const BigInt& modulus,
+ Usage_Hints hints = NO_HINTS,
+ bool disable_montgomery_arith = false) const;
+
+ /**
+ * Set the base
+ */
+ void set_base(const BigInt& base) const;
+
+ /**
+ * Set the exponent
+ */
+ void set_exponent(const BigInt& exponent) const;
+
+ /**
+ * All three of the above functions must have already been called.
+ * @return result of g^x%p
+ */
+ BigInt execute() const;
+
+ Power_Mod& operator=(const Power_Mod&);
+
+ /**
+ * @param modulus Optionally call set_modulus
+ * @param hints Passed to set_modulus if modulus > 0
+ * @param disable_montgomery_arith Disables use of Montgomery
+ * representation. Likely only useful for testing.
+ */
+ Power_Mod(const BigInt& modulus = 0,
+ Usage_Hints hints = NO_HINTS,
+ bool disable_montgomery_arith = false);
+ Power_Mod(const Power_Mod&);
+ virtual ~Power_Mod();
+ private:
+ mutable std::unique_ptr<Modular_Exponentiator> m_core;
+ };
+
+/**
+* Fixed Exponent Modular Exponentiator Proxy
+*/
+class BOTAN_PUBLIC_API(2,0) Fixed_Exponent_Power_Mod final : public Power_Mod
+ {
+ public:
+ BigInt operator()(const BigInt& b) const
+ { set_base(b); return execute(); }
+
+ Fixed_Exponent_Power_Mod() = default;
+
+ Fixed_Exponent_Power_Mod(const BigInt& exponent,
+ const BigInt& modulus,
+ Usage_Hints hints = NO_HINTS);
+ };
+
+/**
+* Fixed Base Modular Exponentiator Proxy
+*/
+class BOTAN_PUBLIC_API(2,0) Fixed_Base_Power_Mod final : public Power_Mod
+ {
+ public:
+ BigInt operator()(const BigInt& e) const
+ { set_exponent(e); return execute(); }
+
+ Fixed_Base_Power_Mod() = default;
+
+ Fixed_Base_Power_Mod(const BigInt& base,
+ const BigInt& modulus,
+ Usage_Hints hints = NO_HINTS);
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/math/numbertheory/primality.cpp b/comm/third_party/botan/src/lib/math/numbertheory/primality.cpp
new file mode 100644
index 0000000000..eb2be42b13
--- /dev/null
+++ b/comm/third_party/botan/src/lib/math/numbertheory/primality.cpp
@@ -0,0 +1,203 @@
+/*
+* (C) 2016,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/primality.h>
+#include <botan/internal/monty_exp.h>
+#include <botan/bigint.h>
+#include <botan/monty.h>
+#include <botan/reducer.h>
+#include <botan/rng.h>
+#include <algorithm>
+
+namespace Botan {
+
+bool is_lucas_probable_prime(const BigInt& C, const Modular_Reducer& mod_C)
+ {
+ if(C <= 1)
+ return false;
+ else if(C == 2)
+ return true;
+ else if(C.is_even())
+ return false;
+ else if(C == 3 || C == 5 || C == 7 || C == 11 || C == 13)
+ return true;
+
+ BigInt D = 5;
+
+ for(;;)
+ {
+ int32_t j = jacobi(D, C);
+ if(j == 0)
+ return false;
+
+ if(j == -1)
+ break;
+
+ // Check 5, -7, 9, -11, 13, -15, 17, ...
+ if(D.is_negative())
+ {
+ D.flip_sign();
+ D += 2;
+ }
+ else
+ {
+ D += 2;
+ D.flip_sign();
+ }
+
+ if(D == 17 && is_perfect_square(C).is_nonzero())
+ return false;
+ }
+
+ const BigInt K = C + 1;
+ const size_t K_bits = K.bits() - 1;
+
+ BigInt U = 1;
+ BigInt V = 1;
+
+ BigInt Ut, Vt, U2, V2;
+
+ for(size_t i = 0; i != K_bits; ++i)
+ {
+ const bool k_bit = K.get_bit(K_bits - 1 - i);
+
+ Ut = mod_C.multiply(U, V);
+
+ Vt = mod_C.reduce(mod_C.square(V) + mod_C.multiply(D, mod_C.square(U)));
+ Vt.ct_cond_add(Vt.is_odd(), C);
+ Vt >>= 1;
+ Vt = mod_C.reduce(Vt);
+
+ U = Ut;
+ V = Vt;
+
+ U2 = mod_C.reduce(Ut + Vt);
+ U2.ct_cond_add(U2.is_odd(), C);
+ U2 >>= 1;
+
+ V2 = mod_C.reduce(Vt + Ut*D);
+ V2.ct_cond_add(V2.is_odd(), C);
+ V2 >>= 1;
+
+ U.ct_cond_assign(k_bit, U2);
+ V.ct_cond_assign(k_bit, V2);
+ }
+
+ return (U == 0);
+ }
+
+bool is_bailie_psw_probable_prime(const BigInt& n, const Modular_Reducer& mod_n)
+ {
+ auto monty_n = std::make_shared<Montgomery_Params>(n, mod_n);
+ return passes_miller_rabin_test(n, mod_n, monty_n, 2) && is_lucas_probable_prime(n, mod_n);
+ }
+
+bool is_bailie_psw_probable_prime(const BigInt& n)
+ {
+ Modular_Reducer mod_n(n);
+ return is_bailie_psw_probable_prime(n, mod_n);
+ }
+
+bool passes_miller_rabin_test(const BigInt& n,
+ const Modular_Reducer& mod_n,
+ const std::shared_ptr<Montgomery_Params>& monty_n,
+ const BigInt& a)
+ {
+ BOTAN_ASSERT_NOMSG(n > 1);
+
+ const BigInt n_minus_1 = n - 1;
+ const size_t s = low_zero_bits(n_minus_1);
+ const BigInt nm1_s = n_minus_1 >> s;
+ const size_t n_bits = n.bits();
+
+ const size_t powm_window = 4;
+
+ auto powm_a_n = monty_precompute(monty_n, a, powm_window);
+
+ BigInt y = monty_execute(*powm_a_n, nm1_s, n_bits);
+
+ if(y == 1 || y == n_minus_1)
+ return true;
+
+ for(size_t i = 1; i != s; ++i)
+ {
+ y = mod_n.square(y);
+
+ if(y == 1) // found a non-trivial square root
+ return false;
+
+ /*
+ -1 is the trivial square root of unity, so ``a`` is not a
+ witness for this number - give up
+ */
+ if(y == n_minus_1)
+ return true;
+ }
+
+ return false;
+ }
+
+bool is_miller_rabin_probable_prime(const BigInt& n,
+ const Modular_Reducer& mod_n,
+ RandomNumberGenerator& rng,
+ size_t test_iterations)
+ {
+ BOTAN_ASSERT_NOMSG(n > 1);
+
+ auto monty_n = std::make_shared<Montgomery_Params>(n, mod_n);
+
+ for(size_t i = 0; i != test_iterations; ++i)
+ {
+ const BigInt a = BigInt::random_integer(rng, 2, n);
+
+ if(!passes_miller_rabin_test(n, mod_n, monty_n, a))
+ return false;
+ }
+
+ // Failed to find a counterexample
+ return true;
+ }
+
+
+size_t miller_rabin_test_iterations(size_t n_bits, size_t prob, bool random)
+ {
+ const size_t base = (prob + 2) / 2; // worst case 4^-t error rate
+
+ /*
+ * If the candidate prime was maliciously constructed, we can't rely
+ * on arguments based on p being random.
+ */
+ if(random == false)
+ return base;
+
+ /*
+ * For randomly chosen numbers we can use the estimates from
+ * http://www.math.dartmouth.edu/~carlp/PDF/paper88.pdf
+ *
+ * These values are derived from the inequality for p(k,t) given on
+ * the second page.
+ */
+ if(prob <= 128)
+ {
+ if(n_bits >= 1536)
+ return 4; // < 2^-133
+ if(n_bits >= 1024)
+ return 6; // < 2^-133
+ if(n_bits >= 512)
+ return 12; // < 2^-129
+ if(n_bits >= 256)
+ return 29; // < 2^-128
+ }
+
+ /*
+ If the user desires a smaller error probability than we have
+ precomputed error estimates for, just fall back to using the worst
+ case error rate.
+ */
+ return base;
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/math/numbertheory/primality.h b/comm/third_party/botan/src/lib/math/numbertheory/primality.h
new file mode 100644
index 0000000000..db7a76a74d
--- /dev/null
+++ b/comm/third_party/botan/src/lib/math/numbertheory/primality.h
@@ -0,0 +1,100 @@
+/*
+* (C) 2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_PRIMALITY_TEST_H_
+#define BOTAN_PRIMALITY_TEST_H_
+
+#include <botan/types.h>
+#include <memory>
+
+namespace Botan {
+
+class BigInt;
+class Modular_Reducer;
+class Montgomery_Params;
+class RandomNumberGenerator;
+
+/**
+* Perform Lucas primality test
+* @see FIPS 186-4 C.3.3
+*
+* @warning it is possible to construct composite integers which pass
+* this test alone.
+*
+* @param n the positive integer to test
+* @param mod_n a pre-created Modular_Reducer 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);
+
+/**
+* Perform Bailie-PSW primality test
+*
+* This is a combination of Miller-Rabin with base 2 and a Lucas test. No known
+* composite integer passes both tests, though it is conjectured that infinitely
+* many composite counterexamples exist.
+*
+* @param n the positive integer to test
+* @param mod_n a pre-created Modular_Reducer 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);
+
+/**
+* Perform Bailie-PSW primality test
+*
+* This is a combination of Miller-Rabin with base 2 and a Lucas test. No known
+* composite integer passes both tests, though it is conjectured that infinitely
+* many composite counterexamples exist.
+*
+* @param n the positive integer to test
+* @return true if n seems probably prime, false if n is composite
+*/
+bool is_bailie_psw_probable_prime(const BigInt& n);
+
+/**
+* Return required number of Miller-Rabin tests in order to
+* reach the specified probability of error.
+*
+* @param n_bits the bit-length of the integer being tested
+* @param prob chance of false positive is bounded by 1/2**prob
+* @param random is set if (and only if) the integer was randomly generated by us
+* and thus cannot have been maliciously constructed.
+*/
+size_t miller_rabin_test_iterations(size_t n_bits, size_t prob, bool random);
+
+/**
+* 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 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<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 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,
+ RandomNumberGenerator& rng,
+ size_t t);
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/math/numbertheory/primes.cpp b/comm/third_party/botan/src/lib/math/numbertheory/primes.cpp
new file mode 100644
index 0000000000..4a3eb46f2c
--- /dev/null
+++ b/comm/third_party/botan/src/lib/math/numbertheory/primes.cpp
@@ -0,0 +1,609 @@
+/*
+* Small Primes Table
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/numthry.h>
+
+namespace Botan {
+
+const uint16_t PRIMES[PRIME_TABLE_SIZE+1] = {
+ 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37,
+ 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83,
+ 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139,
+ 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197,
+ 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263,
+ 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331,
+ 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397,
+ 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461,
+ 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541,
+ 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607,
+ 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673,
+ 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751,
+ 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827,
+ 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907,
+ 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983,
+ 991, 997, 1009, 1013, 1019, 1021, 1031, 1033, 1039, 1049, 1051,
+ 1061, 1063, 1069, 1087, 1091, 1093, 1097, 1103, 1109, 1117, 1123,
+ 1129, 1151, 1153, 1163, 1171, 1181, 1187, 1193, 1201, 1213, 1217,
+ 1223, 1229, 1231, 1237, 1249, 1259, 1277, 1279, 1283, 1289, 1291,
+ 1297, 1301, 1303, 1307, 1319, 1321, 1327, 1361, 1367, 1373, 1381,
+ 1399, 1409, 1423, 1427, 1429, 1433, 1439, 1447, 1451, 1453, 1459,
+ 1471, 1481, 1483, 1487, 1489, 1493, 1499, 1511, 1523, 1531, 1543,
+ 1549, 1553, 1559, 1567, 1571, 1579, 1583, 1597, 1601, 1607, 1609,
+ 1613, 1619, 1621, 1627, 1637, 1657, 1663, 1667, 1669, 1693, 1697,
+ 1699, 1709, 1721, 1723, 1733, 1741, 1747, 1753, 1759, 1777, 1783,
+ 1787, 1789, 1801, 1811, 1823, 1831, 1847, 1861, 1867, 1871, 1873,
+ 1877, 1879, 1889, 1901, 1907, 1913, 1931, 1933, 1949, 1951, 1973,
+ 1979, 1987, 1993, 1997, 1999, 2003, 2011, 2017, 2027, 2029, 2039,
+ 2053, 2063, 2069, 2081, 2083, 2087, 2089, 2099, 2111, 2113, 2129,
+ 2131, 2137, 2141, 2143, 2153, 2161, 2179, 2203, 2207, 2213, 2221,
+ 2237, 2239, 2243, 2251, 2267, 2269, 2273, 2281, 2287, 2293, 2297,
+ 2309, 2311, 2333, 2339, 2341, 2347, 2351, 2357, 2371, 2377, 2381,
+ 2383, 2389, 2393, 2399, 2411, 2417, 2423, 2437, 2441, 2447, 2459,
+ 2467, 2473, 2477, 2503, 2521, 2531, 2539, 2543, 2549, 2551, 2557,
+ 2579, 2591, 2593, 2609, 2617, 2621, 2633, 2647, 2657, 2659, 2663,
+ 2671, 2677, 2683, 2687, 2689, 2693, 2699, 2707, 2711, 2713, 2719,
+ 2729, 2731, 2741, 2749, 2753, 2767, 2777, 2789, 2791, 2797, 2801,
+ 2803, 2819, 2833, 2837, 2843, 2851, 2857, 2861, 2879, 2887, 2897,
+ 2903, 2909, 2917, 2927, 2939, 2953, 2957, 2963, 2969, 2971, 2999,
+ 3001, 3011, 3019, 3023, 3037, 3041, 3049, 3061, 3067, 3079, 3083,
+ 3089, 3109, 3119, 3121, 3137, 3163, 3167, 3169, 3181, 3187, 3191,
+ 3203, 3209, 3217, 3221, 3229, 3251, 3253, 3257, 3259, 3271, 3299,
+ 3301, 3307, 3313, 3319, 3323, 3329, 3331, 3343, 3347, 3359, 3361,
+ 3371, 3373, 3389, 3391, 3407, 3413, 3433, 3449, 3457, 3461, 3463,
+ 3467, 3469, 3491, 3499, 3511, 3517, 3527, 3529, 3533, 3539, 3541,
+ 3547, 3557, 3559, 3571, 3581, 3583, 3593, 3607, 3613, 3617, 3623,
+ 3631, 3637, 3643, 3659, 3671, 3673, 3677, 3691, 3697, 3701, 3709,
+ 3719, 3727, 3733, 3739, 3761, 3767, 3769, 3779, 3793, 3797, 3803,
+ 3821, 3823, 3833, 3847, 3851, 3853, 3863, 3877, 3881, 3889, 3907,
+ 3911, 3917, 3919, 3923, 3929, 3931, 3943, 3947, 3967, 3989, 4001,
+ 4003, 4007, 4013, 4019, 4021, 4027, 4049, 4051, 4057, 4073, 4079,
+ 4091, 4093, 4099, 4111, 4127, 4129, 4133, 4139, 4153, 4157, 4159,
+ 4177, 4201, 4211, 4217, 4219, 4229, 4231, 4241, 4243, 4253, 4259,
+ 4261, 4271, 4273, 4283, 4289, 4297, 4327, 4337, 4339, 4349, 4357,
+ 4363, 4373, 4391, 4397, 4409, 4421, 4423, 4441, 4447, 4451, 4457,
+ 4463, 4481, 4483, 4493, 4507, 4513, 4517, 4519, 4523, 4547, 4549,
+ 4561, 4567, 4583, 4591, 4597, 4603, 4621, 4637, 4639, 4643, 4649,
+ 4651, 4657, 4663, 4673, 4679, 4691, 4703, 4721, 4723, 4729, 4733,
+ 4751, 4759, 4783, 4787, 4789, 4793, 4799, 4801, 4813, 4817, 4831,
+ 4861, 4871, 4877, 4889, 4903, 4909, 4919, 4931, 4933, 4937, 4943,
+ 4951, 4957, 4967, 4969, 4973, 4987, 4993, 4999, 5003, 5009, 5011,
+ 5021, 5023, 5039, 5051, 5059, 5077, 5081, 5087, 5099, 5101, 5107,
+ 5113, 5119, 5147, 5153, 5167, 5171, 5179, 5189, 5197, 5209, 5227,
+ 5231, 5233, 5237, 5261, 5273, 5279, 5281, 5297, 5303, 5309, 5323,
+ 5333, 5347, 5351, 5381, 5387, 5393, 5399, 5407, 5413, 5417, 5419,
+ 5431, 5437, 5441, 5443, 5449, 5471, 5477, 5479, 5483, 5501, 5503,
+ 5507, 5519, 5521, 5527, 5531, 5557, 5563, 5569, 5573, 5581, 5591,
+ 5623, 5639, 5641, 5647, 5651, 5653, 5657, 5659, 5669, 5683, 5689,
+ 5693, 5701, 5711, 5717, 5737, 5741, 5743, 5749, 5779, 5783, 5791,
+ 5801, 5807, 5813, 5821, 5827, 5839, 5843, 5849, 5851, 5857, 5861,
+ 5867, 5869, 5879, 5881, 5897, 5903, 5923, 5927, 5939, 5953, 5981,
+ 5987, 6007, 6011, 6029, 6037, 6043, 6047, 6053, 6067, 6073, 6079,
+ 6089, 6091, 6101, 6113, 6121, 6131, 6133, 6143, 6151, 6163, 6173,
+ 6197, 6199, 6203, 6211, 6217, 6221, 6229, 6247, 6257, 6263, 6269,
+ 6271, 6277, 6287, 6299, 6301, 6311, 6317, 6323, 6329, 6337, 6343,
+ 6353, 6359, 6361, 6367, 6373, 6379, 6389, 6397, 6421, 6427, 6449,
+ 6451, 6469, 6473, 6481, 6491, 6521, 6529, 6547, 6551, 6553, 6563,
+ 6569, 6571, 6577, 6581, 6599, 6607, 6619, 6637, 6653, 6659, 6661,
+ 6673, 6679, 6689, 6691, 6701, 6703, 6709, 6719, 6733, 6737, 6761,
+ 6763, 6779, 6781, 6791, 6793, 6803, 6823, 6827, 6829, 6833, 6841,
+ 6857, 6863, 6869, 6871, 6883, 6899, 6907, 6911, 6917, 6947, 6949,
+ 6959, 6961, 6967, 6971, 6977, 6983, 6991, 6997, 7001, 7013, 7019,
+ 7027, 7039, 7043, 7057, 7069, 7079, 7103, 7109, 7121, 7127, 7129,
+ 7151, 7159, 7177, 7187, 7193, 7207, 7211, 7213, 7219, 7229, 7237,
+ 7243, 7247, 7253, 7283, 7297, 7307, 7309, 7321, 7331, 7333, 7349,
+ 7351, 7369, 7393, 7411, 7417, 7433, 7451, 7457, 7459, 7477, 7481,
+ 7487, 7489, 7499, 7507, 7517, 7523, 7529, 7537, 7541, 7547, 7549,
+ 7559, 7561, 7573, 7577, 7583, 7589, 7591, 7603, 7607, 7621, 7639,
+ 7643, 7649, 7669, 7673, 7681, 7687, 7691, 7699, 7703, 7717, 7723,
+ 7727, 7741, 7753, 7757, 7759, 7789, 7793, 7817, 7823, 7829, 7841,
+ 7853, 7867, 7873, 7877, 7879, 7883, 7901, 7907, 7919, 7927, 7933,
+ 7937, 7949, 7951, 7963, 7993, 8009, 8011, 8017, 8039, 8053, 8059,
+ 8069, 8081, 8087, 8089, 8093, 8101, 8111, 8117, 8123, 8147, 8161,
+ 8167, 8171, 8179, 8191, 8209, 8219, 8221, 8231, 8233, 8237, 8243,
+ 8263, 8269, 8273, 8287, 8291, 8293, 8297, 8311, 8317, 8329, 8353,
+ 8363, 8369, 8377, 8387, 8389, 8419, 8423, 8429, 8431, 8443, 8447,
+ 8461, 8467, 8501, 8513, 8521, 8527, 8537, 8539, 8543, 8563, 8573,
+ 8581, 8597, 8599, 8609, 8623, 8627, 8629, 8641, 8647, 8663, 8669,
+ 8677, 8681, 8689, 8693, 8699, 8707, 8713, 8719, 8731, 8737, 8741,
+ 8747, 8753, 8761, 8779, 8783, 8803, 8807, 8819, 8821, 8831, 8837,
+ 8839, 8849, 8861, 8863, 8867, 8887, 8893, 8923, 8929, 8933, 8941,
+ 8951, 8963, 8969, 8971, 8999, 9001, 9007, 9011, 9013, 9029, 9041,
+ 9043, 9049, 9059, 9067, 9091, 9103, 9109, 9127, 9133, 9137, 9151,
+ 9157, 9161, 9173, 9181, 9187, 9199, 9203, 9209, 9221, 9227, 9239,
+ 9241, 9257, 9277, 9281, 9283, 9293, 9311, 9319, 9323, 9337, 9341,
+ 9343, 9349, 9371, 9377, 9391, 9397, 9403, 9413, 9419, 9421, 9431,
+ 9433, 9437, 9439, 9461, 9463, 9467, 9473, 9479, 9491, 9497, 9511,
+ 9521, 9533, 9539, 9547, 9551, 9587, 9601, 9613, 9619, 9623, 9629,
+ 9631, 9643, 9649, 9661, 9677, 9679, 9689, 9697, 9719, 9721, 9733,
+ 9739, 9743, 9749, 9767, 9769, 9781, 9787, 9791, 9803, 9811, 9817,
+ 9829, 9833, 9839, 9851, 9857, 9859, 9871, 9883, 9887, 9901, 9907,
+ 9923, 9929, 9931, 9941, 9949, 9967, 9973, 10007, 10009, 10037, 10039,
+10061, 10067, 10069, 10079, 10091, 10093, 10099, 10103, 10111, 10133, 10139,
+10141, 10151, 10159, 10163, 10169, 10177, 10181, 10193, 10211, 10223, 10243,
+10247, 10253, 10259, 10267, 10271, 10273, 10289, 10301, 10303, 10313, 10321,
+10331, 10333, 10337, 10343, 10357, 10369, 10391, 10399, 10427, 10429, 10433,
+10453, 10457, 10459, 10463, 10477, 10487, 10499, 10501, 10513, 10529, 10531,
+10559, 10567, 10589, 10597, 10601, 10607, 10613, 10627, 10631, 10639, 10651,
+10657, 10663, 10667, 10687, 10691, 10709, 10711, 10723, 10729, 10733, 10739,
+10753, 10771, 10781, 10789, 10799, 10831, 10837, 10847, 10853, 10859, 10861,
+10867, 10883, 10889, 10891, 10903, 10909, 10937, 10939, 10949, 10957, 10973,
+10979, 10987, 10993, 11003, 11027, 11047, 11057, 11059, 11069, 11071, 11083,
+11087, 11093, 11113, 11117, 11119, 11131, 11149, 11159, 11161, 11171, 11173,
+11177, 11197, 11213, 11239, 11243, 11251, 11257, 11261, 11273, 11279, 11287,
+11299, 11311, 11317, 11321, 11329, 11351, 11353, 11369, 11383, 11393, 11399,
+11411, 11423, 11437, 11443, 11447, 11467, 11471, 11483, 11489, 11491, 11497,
+11503, 11519, 11527, 11549, 11551, 11579, 11587, 11593, 11597, 11617, 11621,
+11633, 11657, 11677, 11681, 11689, 11699, 11701, 11717, 11719, 11731, 11743,
+11777, 11779, 11783, 11789, 11801, 11807, 11813, 11821, 11827, 11831, 11833,
+11839, 11863, 11867, 11887, 11897, 11903, 11909, 11923, 11927, 11933, 11939,
+11941, 11953, 11959, 11969, 11971, 11981, 11987, 12007, 12011, 12037, 12041,
+12043, 12049, 12071, 12073, 12097, 12101, 12107, 12109, 12113, 12119, 12143,
+12149, 12157, 12161, 12163, 12197, 12203, 12211, 12227, 12239, 12241, 12251,
+12253, 12263, 12269, 12277, 12281, 12289, 12301, 12323, 12329, 12343, 12347,
+12373, 12377, 12379, 12391, 12401, 12409, 12413, 12421, 12433, 12437, 12451,
+12457, 12473, 12479, 12487, 12491, 12497, 12503, 12511, 12517, 12527, 12539,
+12541, 12547, 12553, 12569, 12577, 12583, 12589, 12601, 12611, 12613, 12619,
+12637, 12641, 12647, 12653, 12659, 12671, 12689, 12697, 12703, 12713, 12721,
+12739, 12743, 12757, 12763, 12781, 12791, 12799, 12809, 12821, 12823, 12829,
+12841, 12853, 12889, 12893, 12899, 12907, 12911, 12917, 12919, 12923, 12941,
+12953, 12959, 12967, 12973, 12979, 12983, 13001, 13003, 13007, 13009, 13033,
+13037, 13043, 13049, 13063, 13093, 13099, 13103, 13109, 13121, 13127, 13147,
+13151, 13159, 13163, 13171, 13177, 13183, 13187, 13217, 13219, 13229, 13241,
+13249, 13259, 13267, 13291, 13297, 13309, 13313, 13327, 13331, 13337, 13339,
+13367, 13381, 13397, 13399, 13411, 13417, 13421, 13441, 13451, 13457, 13463,
+13469, 13477, 13487, 13499, 13513, 13523, 13537, 13553, 13567, 13577, 13591,
+13597, 13613, 13619, 13627, 13633, 13649, 13669, 13679, 13681, 13687, 13691,
+13693, 13697, 13709, 13711, 13721, 13723, 13729, 13751, 13757, 13759, 13763,
+13781, 13789, 13799, 13807, 13829, 13831, 13841, 13859, 13873, 13877, 13879,
+13883, 13901, 13903, 13907, 13913, 13921, 13931, 13933, 13963, 13967, 13997,
+13999, 14009, 14011, 14029, 14033, 14051, 14057, 14071, 14081, 14083, 14087,
+14107, 14143, 14149, 14153, 14159, 14173, 14177, 14197, 14207, 14221, 14243,
+14249, 14251, 14281, 14293, 14303, 14321, 14323, 14327, 14341, 14347, 14369,
+14387, 14389, 14401, 14407, 14411, 14419, 14423, 14431, 14437, 14447, 14449,
+14461, 14479, 14489, 14503, 14519, 14533, 14537, 14543, 14549, 14551, 14557,
+14561, 14563, 14591, 14593, 14621, 14627, 14629, 14633, 14639, 14653, 14657,
+14669, 14683, 14699, 14713, 14717, 14723, 14731, 14737, 14741, 14747, 14753,
+14759, 14767, 14771, 14779, 14783, 14797, 14813, 14821, 14827, 14831, 14843,
+14851, 14867, 14869, 14879, 14887, 14891, 14897, 14923, 14929, 14939, 14947,
+14951, 14957, 14969, 14983, 15013, 15017, 15031, 15053, 15061, 15073, 15077,
+15083, 15091, 15101, 15107, 15121, 15131, 15137, 15139, 15149, 15161, 15173,
+15187, 15193, 15199, 15217, 15227, 15233, 15241, 15259, 15263, 15269, 15271,
+15277, 15287, 15289, 15299, 15307, 15313, 15319, 15329, 15331, 15349, 15359,
+15361, 15373, 15377, 15383, 15391, 15401, 15413, 15427, 15439, 15443, 15451,
+15461, 15467, 15473, 15493, 15497, 15511, 15527, 15541, 15551, 15559, 15569,
+15581, 15583, 15601, 15607, 15619, 15629, 15641, 15643, 15647, 15649, 15661,
+15667, 15671, 15679, 15683, 15727, 15731, 15733, 15737, 15739, 15749, 15761,
+15767, 15773, 15787, 15791, 15797, 15803, 15809, 15817, 15823, 15859, 15877,
+15881, 15887, 15889, 15901, 15907, 15913, 15919, 15923, 15937, 15959, 15971,
+15973, 15991, 16001, 16007, 16033, 16057, 16061, 16063, 16067, 16069, 16073,
+16087, 16091, 16097, 16103, 16111, 16127, 16139, 16141, 16183, 16187, 16189,
+16193, 16217, 16223, 16229, 16231, 16249, 16253, 16267, 16273, 16301, 16319,
+16333, 16339, 16349, 16361, 16363, 16369, 16381, 16411, 16417, 16421, 16427,
+16433, 16447, 16451, 16453, 16477, 16481, 16487, 16493, 16519, 16529, 16547,
+16553, 16561, 16567, 16573, 16603, 16607, 16619, 16631, 16633, 16649, 16651,
+16657, 16661, 16673, 16691, 16693, 16699, 16703, 16729, 16741, 16747, 16759,
+16763, 16787, 16811, 16823, 16829, 16831, 16843, 16871, 16879, 16883, 16889,
+16901, 16903, 16921, 16927, 16931, 16937, 16943, 16963, 16979, 16981, 16987,
+16993, 17011, 17021, 17027, 17029, 17033, 17041, 17047, 17053, 17077, 17093,
+17099, 17107, 17117, 17123, 17137, 17159, 17167, 17183, 17189, 17191, 17203,
+17207, 17209, 17231, 17239, 17257, 17291, 17293, 17299, 17317, 17321, 17327,
+17333, 17341, 17351, 17359, 17377, 17383, 17387, 17389, 17393, 17401, 17417,
+17419, 17431, 17443, 17449, 17467, 17471, 17477, 17483, 17489, 17491, 17497,
+17509, 17519, 17539, 17551, 17569, 17573, 17579, 17581, 17597, 17599, 17609,
+17623, 17627, 17657, 17659, 17669, 17681, 17683, 17707, 17713, 17729, 17737,
+17747, 17749, 17761, 17783, 17789, 17791, 17807, 17827, 17837, 17839, 17851,
+17863, 17881, 17891, 17903, 17909, 17911, 17921, 17923, 17929, 17939, 17957,
+17959, 17971, 17977, 17981, 17987, 17989, 18013, 18041, 18043, 18047, 18049,
+18059, 18061, 18077, 18089, 18097, 18119, 18121, 18127, 18131, 18133, 18143,
+18149, 18169, 18181, 18191, 18199, 18211, 18217, 18223, 18229, 18233, 18251,
+18253, 18257, 18269, 18287, 18289, 18301, 18307, 18311, 18313, 18329, 18341,
+18353, 18367, 18371, 18379, 18397, 18401, 18413, 18427, 18433, 18439, 18443,
+18451, 18457, 18461, 18481, 18493, 18503, 18517, 18521, 18523, 18539, 18541,
+18553, 18583, 18587, 18593, 18617, 18637, 18661, 18671, 18679, 18691, 18701,
+18713, 18719, 18731, 18743, 18749, 18757, 18773, 18787, 18793, 18797, 18803,
+18839, 18859, 18869, 18899, 18911, 18913, 18917, 18919, 18947, 18959, 18973,
+18979, 19001, 19009, 19013, 19031, 19037, 19051, 19069, 19073, 19079, 19081,
+19087, 19121, 19139, 19141, 19157, 19163, 19181, 19183, 19207, 19211, 19213,
+19219, 19231, 19237, 19249, 19259, 19267, 19273, 19289, 19301, 19309, 19319,
+19333, 19373, 19379, 19381, 19387, 19391, 19403, 19417, 19421, 19423, 19427,
+19429, 19433, 19441, 19447, 19457, 19463, 19469, 19471, 19477, 19483, 19489,
+19501, 19507, 19531, 19541, 19543, 19553, 19559, 19571, 19577, 19583, 19597,
+19603, 19609, 19661, 19681, 19687, 19697, 19699, 19709, 19717, 19727, 19739,
+19751, 19753, 19759, 19763, 19777, 19793, 19801, 19813, 19819, 19841, 19843,
+19853, 19861, 19867, 19889, 19891, 19913, 19919, 19927, 19937, 19949, 19961,
+19963, 19973, 19979, 19991, 19993, 19997, 20011, 20021, 20023, 20029, 20047,
+20051, 20063, 20071, 20089, 20101, 20107, 20113, 20117, 20123, 20129, 20143,
+20147, 20149, 20161, 20173, 20177, 20183, 20201, 20219, 20231, 20233, 20249,
+20261, 20269, 20287, 20297, 20323, 20327, 20333, 20341, 20347, 20353, 20357,
+20359, 20369, 20389, 20393, 20399, 20407, 20411, 20431, 20441, 20443, 20477,
+20479, 20483, 20507, 20509, 20521, 20533, 20543, 20549, 20551, 20563, 20593,
+20599, 20611, 20627, 20639, 20641, 20663, 20681, 20693, 20707, 20717, 20719,
+20731, 20743, 20747, 20749, 20753, 20759, 20771, 20773, 20789, 20807, 20809,
+20849, 20857, 20873, 20879, 20887, 20897, 20899, 20903, 20921, 20929, 20939,
+20947, 20959, 20963, 20981, 20983, 21001, 21011, 21013, 21017, 21019, 21023,
+21031, 21059, 21061, 21067, 21089, 21101, 21107, 21121, 21139, 21143, 21149,
+21157, 21163, 21169, 21179, 21187, 21191, 21193, 21211, 21221, 21227, 21247,
+21269, 21277, 21283, 21313, 21317, 21319, 21323, 21341, 21347, 21377, 21379,
+21383, 21391, 21397, 21401, 21407, 21419, 21433, 21467, 21481, 21487, 21491,
+21493, 21499, 21503, 21517, 21521, 21523, 21529, 21557, 21559, 21563, 21569,
+21577, 21587, 21589, 21599, 21601, 21611, 21613, 21617, 21647, 21649, 21661,
+21673, 21683, 21701, 21713, 21727, 21737, 21739, 21751, 21757, 21767, 21773,
+21787, 21799, 21803, 21817, 21821, 21839, 21841, 21851, 21859, 21863, 21871,
+21881, 21893, 21911, 21929, 21937, 21943, 21961, 21977, 21991, 21997, 22003,
+22013, 22027, 22031, 22037, 22039, 22051, 22063, 22067, 22073, 22079, 22091,
+22093, 22109, 22111, 22123, 22129, 22133, 22147, 22153, 22157, 22159, 22171,
+22189, 22193, 22229, 22247, 22259, 22271, 22273, 22277, 22279, 22283, 22291,
+22303, 22307, 22343, 22349, 22367, 22369, 22381, 22391, 22397, 22409, 22433,
+22441, 22447, 22453, 22469, 22481, 22483, 22501, 22511, 22531, 22541, 22543,
+22549, 22567, 22571, 22573, 22613, 22619, 22621, 22637, 22639, 22643, 22651,
+22669, 22679, 22691, 22697, 22699, 22709, 22717, 22721, 22727, 22739, 22741,
+22751, 22769, 22777, 22783, 22787, 22807, 22811, 22817, 22853, 22859, 22861,
+22871, 22877, 22901, 22907, 22921, 22937, 22943, 22961, 22963, 22973, 22993,
+23003, 23011, 23017, 23021, 23027, 23029, 23039, 23041, 23053, 23057, 23059,
+23063, 23071, 23081, 23087, 23099, 23117, 23131, 23143, 23159, 23167, 23173,
+23189, 23197, 23201, 23203, 23209, 23227, 23251, 23269, 23279, 23291, 23293,
+23297, 23311, 23321, 23327, 23333, 23339, 23357, 23369, 23371, 23399, 23417,
+23431, 23447, 23459, 23473, 23497, 23509, 23531, 23537, 23539, 23549, 23557,
+23561, 23563, 23567, 23581, 23593, 23599, 23603, 23609, 23623, 23627, 23629,
+23633, 23663, 23669, 23671, 23677, 23687, 23689, 23719, 23741, 23743, 23747,
+23753, 23761, 23767, 23773, 23789, 23801, 23813, 23819, 23827, 23831, 23833,
+23857, 23869, 23873, 23879, 23887, 23893, 23899, 23909, 23911, 23917, 23929,
+23957, 23971, 23977, 23981, 23993, 24001, 24007, 24019, 24023, 24029, 24043,
+24049, 24061, 24071, 24077, 24083, 24091, 24097, 24103, 24107, 24109, 24113,
+24121, 24133, 24137, 24151, 24169, 24179, 24181, 24197, 24203, 24223, 24229,
+24239, 24247, 24251, 24281, 24317, 24329, 24337, 24359, 24371, 24373, 24379,
+24391, 24407, 24413, 24419, 24421, 24439, 24443, 24469, 24473, 24481, 24499,
+24509, 24517, 24527, 24533, 24547, 24551, 24571, 24593, 24611, 24623, 24631,
+24659, 24671, 24677, 24683, 24691, 24697, 24709, 24733, 24749, 24763, 24767,
+24781, 24793, 24799, 24809, 24821, 24841, 24847, 24851, 24859, 24877, 24889,
+24907, 24917, 24919, 24923, 24943, 24953, 24967, 24971, 24977, 24979, 24989,
+25013, 25031, 25033, 25037, 25057, 25073, 25087, 25097, 25111, 25117, 25121,
+25127, 25147, 25153, 25163, 25169, 25171, 25183, 25189, 25219, 25229, 25237,
+25243, 25247, 25253, 25261, 25301, 25303, 25307, 25309, 25321, 25339, 25343,
+25349, 25357, 25367, 25373, 25391, 25409, 25411, 25423, 25439, 25447, 25453,
+25457, 25463, 25469, 25471, 25523, 25537, 25541, 25561, 25577, 25579, 25583,
+25589, 25601, 25603, 25609, 25621, 25633, 25639, 25643, 25657, 25667, 25673,
+25679, 25693, 25703, 25717, 25733, 25741, 25747, 25759, 25763, 25771, 25793,
+25799, 25801, 25819, 25841, 25847, 25849, 25867, 25873, 25889, 25903, 25913,
+25919, 25931, 25933, 25939, 25943, 25951, 25969, 25981, 25997, 25999, 26003,
+26017, 26021, 26029, 26041, 26053, 26083, 26099, 26107, 26111, 26113, 26119,
+26141, 26153, 26161, 26171, 26177, 26183, 26189, 26203, 26209, 26227, 26237,
+26249, 26251, 26261, 26263, 26267, 26293, 26297, 26309, 26317, 26321, 26339,
+26347, 26357, 26371, 26387, 26393, 26399, 26407, 26417, 26423, 26431, 26437,
+26449, 26459, 26479, 26489, 26497, 26501, 26513, 26539, 26557, 26561, 26573,
+26591, 26597, 26627, 26633, 26641, 26647, 26669, 26681, 26683, 26687, 26693,
+26699, 26701, 26711, 26713, 26717, 26723, 26729, 26731, 26737, 26759, 26777,
+26783, 26801, 26813, 26821, 26833, 26839, 26849, 26861, 26863, 26879, 26881,
+26891, 26893, 26903, 26921, 26927, 26947, 26951, 26953, 26959, 26981, 26987,
+26993, 27011, 27017, 27031, 27043, 27059, 27061, 27067, 27073, 27077, 27091,
+27103, 27107, 27109, 27127, 27143, 27179, 27191, 27197, 27211, 27239, 27241,
+27253, 27259, 27271, 27277, 27281, 27283, 27299, 27329, 27337, 27361, 27367,
+27397, 27407, 27409, 27427, 27431, 27437, 27449, 27457, 27479, 27481, 27487,
+27509, 27527, 27529, 27539, 27541, 27551, 27581, 27583, 27611, 27617, 27631,
+27647, 27653, 27673, 27689, 27691, 27697, 27701, 27733, 27737, 27739, 27743,
+27749, 27751, 27763, 27767, 27773, 27779, 27791, 27793, 27799, 27803, 27809,
+27817, 27823, 27827, 27847, 27851, 27883, 27893, 27901, 27917, 27919, 27941,
+27943, 27947, 27953, 27961, 27967, 27983, 27997, 28001, 28019, 28027, 28031,
+28051, 28057, 28069, 28081, 28087, 28097, 28099, 28109, 28111, 28123, 28151,
+28163, 28181, 28183, 28201, 28211, 28219, 28229, 28277, 28279, 28283, 28289,
+28297, 28307, 28309, 28319, 28349, 28351, 28387, 28393, 28403, 28409, 28411,
+28429, 28433, 28439, 28447, 28463, 28477, 28493, 28499, 28513, 28517, 28537,
+28541, 28547, 28549, 28559, 28571, 28573, 28579, 28591, 28597, 28603, 28607,
+28619, 28621, 28627, 28631, 28643, 28649, 28657, 28661, 28663, 28669, 28687,
+28697, 28703, 28711, 28723, 28729, 28751, 28753, 28759, 28771, 28789, 28793,
+28807, 28813, 28817, 28837, 28843, 28859, 28867, 28871, 28879, 28901, 28909,
+28921, 28927, 28933, 28949, 28961, 28979, 29009, 29017, 29021, 29023, 29027,
+29033, 29059, 29063, 29077, 29101, 29123, 29129, 29131, 29137, 29147, 29153,
+29167, 29173, 29179, 29191, 29201, 29207, 29209, 29221, 29231, 29243, 29251,
+29269, 29287, 29297, 29303, 29311, 29327, 29333, 29339, 29347, 29363, 29383,
+29387, 29389, 29399, 29401, 29411, 29423, 29429, 29437, 29443, 29453, 29473,
+29483, 29501, 29527, 29531, 29537, 29567, 29569, 29573, 29581, 29587, 29599,
+29611, 29629, 29633, 29641, 29663, 29669, 29671, 29683, 29717, 29723, 29741,
+29753, 29759, 29761, 29789, 29803, 29819, 29833, 29837, 29851, 29863, 29867,
+29873, 29879, 29881, 29917, 29921, 29927, 29947, 29959, 29983, 29989, 30011,
+30013, 30029, 30047, 30059, 30071, 30089, 30091, 30097, 30103, 30109, 30113,
+30119, 30133, 30137, 30139, 30161, 30169, 30181, 30187, 30197, 30203, 30211,
+30223, 30241, 30253, 30259, 30269, 30271, 30293, 30307, 30313, 30319, 30323,
+30341, 30347, 30367, 30389, 30391, 30403, 30427, 30431, 30449, 30467, 30469,
+30491, 30493, 30497, 30509, 30517, 30529, 30539, 30553, 30557, 30559, 30577,
+30593, 30631, 30637, 30643, 30649, 30661, 30671, 30677, 30689, 30697, 30703,
+30707, 30713, 30727, 30757, 30763, 30773, 30781, 30803, 30809, 30817, 30829,
+30839, 30841, 30851, 30853, 30859, 30869, 30871, 30881, 30893, 30911, 30931,
+30937, 30941, 30949, 30971, 30977, 30983, 31013, 31019, 31033, 31039, 31051,
+31063, 31069, 31079, 31081, 31091, 31121, 31123, 31139, 31147, 31151, 31153,
+31159, 31177, 31181, 31183, 31189, 31193, 31219, 31223, 31231, 31237, 31247,
+31249, 31253, 31259, 31267, 31271, 31277, 31307, 31319, 31321, 31327, 31333,
+31337, 31357, 31379, 31387, 31391, 31393, 31397, 31469, 31477, 31481, 31489,
+31511, 31513, 31517, 31531, 31541, 31543, 31547, 31567, 31573, 31583, 31601,
+31607, 31627, 31643, 31649, 31657, 31663, 31667, 31687, 31699, 31721, 31723,
+31727, 31729, 31741, 31751, 31769, 31771, 31793, 31799, 31817, 31847, 31849,
+31859, 31873, 31883, 31891, 31907, 31957, 31963, 31973, 31981, 31991, 32003,
+32009, 32027, 32029, 32051, 32057, 32059, 32063, 32069, 32077, 32083, 32089,
+32099, 32117, 32119, 32141, 32143, 32159, 32173, 32183, 32189, 32191, 32203,
+32213, 32233, 32237, 32251, 32257, 32261, 32297, 32299, 32303, 32309, 32321,
+32323, 32327, 32341, 32353, 32359, 32363, 32369, 32371, 32377, 32381, 32401,
+32411, 32413, 32423, 32429, 32441, 32443, 32467, 32479, 32491, 32497, 32503,
+32507, 32531, 32533, 32537, 32561, 32563, 32569, 32573, 32579, 32587, 32603,
+32609, 32611, 32621, 32633, 32647, 32653, 32687, 32693, 32707, 32713, 32717,
+32719, 32749, 32771, 32779, 32783, 32789, 32797, 32801, 32803, 32831, 32833,
+32839, 32843, 32869, 32887, 32909, 32911, 32917, 32933, 32939, 32941, 32957,
+32969, 32971, 32983, 32987, 32993, 32999, 33013, 33023, 33029, 33037, 33049,
+33053, 33071, 33073, 33083, 33091, 33107, 33113, 33119, 33149, 33151, 33161,
+33179, 33181, 33191, 33199, 33203, 33211, 33223, 33247, 33287, 33289, 33301,
+33311, 33317, 33329, 33331, 33343, 33347, 33349, 33353, 33359, 33377, 33391,
+33403, 33409, 33413, 33427, 33457, 33461, 33469, 33479, 33487, 33493, 33503,
+33521, 33529, 33533, 33547, 33563, 33569, 33577, 33581, 33587, 33589, 33599,
+33601, 33613, 33617, 33619, 33623, 33629, 33637, 33641, 33647, 33679, 33703,
+33713, 33721, 33739, 33749, 33751, 33757, 33767, 33769, 33773, 33791, 33797,
+33809, 33811, 33827, 33829, 33851, 33857, 33863, 33871, 33889, 33893, 33911,
+33923, 33931, 33937, 33941, 33961, 33967, 33997, 34019, 34031, 34033, 34039,
+34057, 34061, 34123, 34127, 34129, 34141, 34147, 34157, 34159, 34171, 34183,
+34211, 34213, 34217, 34231, 34253, 34259, 34261, 34267, 34273, 34283, 34297,
+34301, 34303, 34313, 34319, 34327, 34337, 34351, 34361, 34367, 34369, 34381,
+34403, 34421, 34429, 34439, 34457, 34469, 34471, 34483, 34487, 34499, 34501,
+34511, 34513, 34519, 34537, 34543, 34549, 34583, 34589, 34591, 34603, 34607,
+34613, 34631, 34649, 34651, 34667, 34673, 34679, 34687, 34693, 34703, 34721,
+34729, 34739, 34747, 34757, 34759, 34763, 34781, 34807, 34819, 34841, 34843,
+34847, 34849, 34871, 34877, 34883, 34897, 34913, 34919, 34939, 34949, 34961,
+34963, 34981, 35023, 35027, 35051, 35053, 35059, 35069, 35081, 35083, 35089,
+35099, 35107, 35111, 35117, 35129, 35141, 35149, 35153, 35159, 35171, 35201,
+35221, 35227, 35251, 35257, 35267, 35279, 35281, 35291, 35311, 35317, 35323,
+35327, 35339, 35353, 35363, 35381, 35393, 35401, 35407, 35419, 35423, 35437,
+35447, 35449, 35461, 35491, 35507, 35509, 35521, 35527, 35531, 35533, 35537,
+35543, 35569, 35573, 35591, 35593, 35597, 35603, 35617, 35671, 35677, 35729,
+35731, 35747, 35753, 35759, 35771, 35797, 35801, 35803, 35809, 35831, 35837,
+35839, 35851, 35863, 35869, 35879, 35897, 35899, 35911, 35923, 35933, 35951,
+35963, 35969, 35977, 35983, 35993, 35999, 36007, 36011, 36013, 36017, 36037,
+36061, 36067, 36073, 36083, 36097, 36107, 36109, 36131, 36137, 36151, 36161,
+36187, 36191, 36209, 36217, 36229, 36241, 36251, 36263, 36269, 36277, 36293,
+36299, 36307, 36313, 36319, 36341, 36343, 36353, 36373, 36383, 36389, 36433,
+36451, 36457, 36467, 36469, 36473, 36479, 36493, 36497, 36523, 36527, 36529,
+36541, 36551, 36559, 36563, 36571, 36583, 36587, 36599, 36607, 36629, 36637,
+36643, 36653, 36671, 36677, 36683, 36691, 36697, 36709, 36713, 36721, 36739,
+36749, 36761, 36767, 36779, 36781, 36787, 36791, 36793, 36809, 36821, 36833,
+36847, 36857, 36871, 36877, 36887, 36899, 36901, 36913, 36919, 36923, 36929,
+36931, 36943, 36947, 36973, 36979, 36997, 37003, 37013, 37019, 37021, 37039,
+37049, 37057, 37061, 37087, 37097, 37117, 37123, 37139, 37159, 37171, 37181,
+37189, 37199, 37201, 37217, 37223, 37243, 37253, 37273, 37277, 37307, 37309,
+37313, 37321, 37337, 37339, 37357, 37361, 37363, 37369, 37379, 37397, 37409,
+37423, 37441, 37447, 37463, 37483, 37489, 37493, 37501, 37507, 37511, 37517,
+37529, 37537, 37547, 37549, 37561, 37567, 37571, 37573, 37579, 37589, 37591,
+37607, 37619, 37633, 37643, 37649, 37657, 37663, 37691, 37693, 37699, 37717,
+37747, 37781, 37783, 37799, 37811, 37813, 37831, 37847, 37853, 37861, 37871,
+37879, 37889, 37897, 37907, 37951, 37957, 37963, 37967, 37987, 37991, 37993,
+37997, 38011, 38039, 38047, 38053, 38069, 38083, 38113, 38119, 38149, 38153,
+38167, 38177, 38183, 38189, 38197, 38201, 38219, 38231, 38237, 38239, 38261,
+38273, 38281, 38287, 38299, 38303, 38317, 38321, 38327, 38329, 38333, 38351,
+38371, 38377, 38393, 38431, 38447, 38449, 38453, 38459, 38461, 38501, 38543,
+38557, 38561, 38567, 38569, 38593, 38603, 38609, 38611, 38629, 38639, 38651,
+38653, 38669, 38671, 38677, 38693, 38699, 38707, 38711, 38713, 38723, 38729,
+38737, 38747, 38749, 38767, 38783, 38791, 38803, 38821, 38833, 38839, 38851,
+38861, 38867, 38873, 38891, 38903, 38917, 38921, 38923, 38933, 38953, 38959,
+38971, 38977, 38993, 39019, 39023, 39041, 39043, 39047, 39079, 39089, 39097,
+39103, 39107, 39113, 39119, 39133, 39139, 39157, 39161, 39163, 39181, 39191,
+39199, 39209, 39217, 39227, 39229, 39233, 39239, 39241, 39251, 39293, 39301,
+39313, 39317, 39323, 39341, 39343, 39359, 39367, 39371, 39373, 39383, 39397,
+39409, 39419, 39439, 39443, 39451, 39461, 39499, 39503, 39509, 39511, 39521,
+39541, 39551, 39563, 39569, 39581, 39607, 39619, 39623, 39631, 39659, 39667,
+39671, 39679, 39703, 39709, 39719, 39727, 39733, 39749, 39761, 39769, 39779,
+39791, 39799, 39821, 39827, 39829, 39839, 39841, 39847, 39857, 39863, 39869,
+39877, 39883, 39887, 39901, 39929, 39937, 39953, 39971, 39979, 39983, 39989,
+40009, 40013, 40031, 40037, 40039, 40063, 40087, 40093, 40099, 40111, 40123,
+40127, 40129, 40151, 40153, 40163, 40169, 40177, 40189, 40193, 40213, 40231,
+40237, 40241, 40253, 40277, 40283, 40289, 40343, 40351, 40357, 40361, 40387,
+40423, 40427, 40429, 40433, 40459, 40471, 40483, 40487, 40493, 40499, 40507,
+40519, 40529, 40531, 40543, 40559, 40577, 40583, 40591, 40597, 40609, 40627,
+40637, 40639, 40693, 40697, 40699, 40709, 40739, 40751, 40759, 40763, 40771,
+40787, 40801, 40813, 40819, 40823, 40829, 40841, 40847, 40849, 40853, 40867,
+40879, 40883, 40897, 40903, 40927, 40933, 40939, 40949, 40961, 40973, 40993,
+41011, 41017, 41023, 41039, 41047, 41051, 41057, 41077, 41081, 41113, 41117,
+41131, 41141, 41143, 41149, 41161, 41177, 41179, 41183, 41189, 41201, 41203,
+41213, 41221, 41227, 41231, 41233, 41243, 41257, 41263, 41269, 41281, 41299,
+41333, 41341, 41351, 41357, 41381, 41387, 41389, 41399, 41411, 41413, 41443,
+41453, 41467, 41479, 41491, 41507, 41513, 41519, 41521, 41539, 41543, 41549,
+41579, 41593, 41597, 41603, 41609, 41611, 41617, 41621, 41627, 41641, 41647,
+41651, 41659, 41669, 41681, 41687, 41719, 41729, 41737, 41759, 41761, 41771,
+41777, 41801, 41809, 41813, 41843, 41849, 41851, 41863, 41879, 41887, 41893,
+41897, 41903, 41911, 41927, 41941, 41947, 41953, 41957, 41959, 41969, 41981,
+41983, 41999, 42013, 42017, 42019, 42023, 42043, 42061, 42071, 42073, 42083,
+42089, 42101, 42131, 42139, 42157, 42169, 42179, 42181, 42187, 42193, 42197,
+42209, 42221, 42223, 42227, 42239, 42257, 42281, 42283, 42293, 42299, 42307,
+42323, 42331, 42337, 42349, 42359, 42373, 42379, 42391, 42397, 42403, 42407,
+42409, 42433, 42437, 42443, 42451, 42457, 42461, 42463, 42467, 42473, 42487,
+42491, 42499, 42509, 42533, 42557, 42569, 42571, 42577, 42589, 42611, 42641,
+42643, 42649, 42667, 42677, 42683, 42689, 42697, 42701, 42703, 42709, 42719,
+42727, 42737, 42743, 42751, 42767, 42773, 42787, 42793, 42797, 42821, 42829,
+42839, 42841, 42853, 42859, 42863, 42899, 42901, 42923, 42929, 42937, 42943,
+42953, 42961, 42967, 42979, 42989, 43003, 43013, 43019, 43037, 43049, 43051,
+43063, 43067, 43093, 43103, 43117, 43133, 43151, 43159, 43177, 43189, 43201,
+43207, 43223, 43237, 43261, 43271, 43283, 43291, 43313, 43319, 43321, 43331,
+43391, 43397, 43399, 43403, 43411, 43427, 43441, 43451, 43457, 43481, 43487,
+43499, 43517, 43541, 43543, 43573, 43577, 43579, 43591, 43597, 43607, 43609,
+43613, 43627, 43633, 43649, 43651, 43661, 43669, 43691, 43711, 43717, 43721,
+43753, 43759, 43777, 43781, 43783, 43787, 43789, 43793, 43801, 43853, 43867,
+43889, 43891, 43913, 43933, 43943, 43951, 43961, 43963, 43969, 43973, 43987,
+43991, 43997, 44017, 44021, 44027, 44029, 44041, 44053, 44059, 44071, 44087,
+44089, 44101, 44111, 44119, 44123, 44129, 44131, 44159, 44171, 44179, 44189,
+44201, 44203, 44207, 44221, 44249, 44257, 44263, 44267, 44269, 44273, 44279,
+44281, 44293, 44351, 44357, 44371, 44381, 44383, 44389, 44417, 44449, 44453,
+44483, 44491, 44497, 44501, 44507, 44519, 44531, 44533, 44537, 44543, 44549,
+44563, 44579, 44587, 44617, 44621, 44623, 44633, 44641, 44647, 44651, 44657,
+44683, 44687, 44699, 44701, 44711, 44729, 44741, 44753, 44771, 44773, 44777,
+44789, 44797, 44809, 44819, 44839, 44843, 44851, 44867, 44879, 44887, 44893,
+44909, 44917, 44927, 44939, 44953, 44959, 44963, 44971, 44983, 44987, 45007,
+45013, 45053, 45061, 45077, 45083, 45119, 45121, 45127, 45131, 45137, 45139,
+45161, 45179, 45181, 45191, 45197, 45233, 45247, 45259, 45263, 45281, 45289,
+45293, 45307, 45317, 45319, 45329, 45337, 45341, 45343, 45361, 45377, 45389,
+45403, 45413, 45427, 45433, 45439, 45481, 45491, 45497, 45503, 45523, 45533,
+45541, 45553, 45557, 45569, 45587, 45589, 45599, 45613, 45631, 45641, 45659,
+45667, 45673, 45677, 45691, 45697, 45707, 45737, 45751, 45757, 45763, 45767,
+45779, 45817, 45821, 45823, 45827, 45833, 45841, 45853, 45863, 45869, 45887,
+45893, 45943, 45949, 45953, 45959, 45971, 45979, 45989, 46021, 46027, 46049,
+46051, 46061, 46073, 46091, 46093, 46099, 46103, 46133, 46141, 46147, 46153,
+46171, 46181, 46183, 46187, 46199, 46219, 46229, 46237, 46261, 46271, 46273,
+46279, 46301, 46307, 46309, 46327, 46337, 46349, 46351, 46381, 46399, 46411,
+46439, 46441, 46447, 46451, 46457, 46471, 46477, 46489, 46499, 46507, 46511,
+46523, 46549, 46559, 46567, 46573, 46589, 46591, 46601, 46619, 46633, 46639,
+46643, 46649, 46663, 46679, 46681, 46687, 46691, 46703, 46723, 46727, 46747,
+46751, 46757, 46769, 46771, 46807, 46811, 46817, 46819, 46829, 46831, 46853,
+46861, 46867, 46877, 46889, 46901, 46919, 46933, 46957, 46993, 46997, 47017,
+47041, 47051, 47057, 47059, 47087, 47093, 47111, 47119, 47123, 47129, 47137,
+47143, 47147, 47149, 47161, 47189, 47207, 47221, 47237, 47251, 47269, 47279,
+47287, 47293, 47297, 47303, 47309, 47317, 47339, 47351, 47353, 47363, 47381,
+47387, 47389, 47407, 47417, 47419, 47431, 47441, 47459, 47491, 47497, 47501,
+47507, 47513, 47521, 47527, 47533, 47543, 47563, 47569, 47581, 47591, 47599,
+47609, 47623, 47629, 47639, 47653, 47657, 47659, 47681, 47699, 47701, 47711,
+47713, 47717, 47737, 47741, 47743, 47777, 47779, 47791, 47797, 47807, 47809,
+47819, 47837, 47843, 47857, 47869, 47881, 47903, 47911, 47917, 47933, 47939,
+47947, 47951, 47963, 47969, 47977, 47981, 48017, 48023, 48029, 48049, 48073,
+48079, 48091, 48109, 48119, 48121, 48131, 48157, 48163, 48179, 48187, 48193,
+48197, 48221, 48239, 48247, 48259, 48271, 48281, 48299, 48311, 48313, 48337,
+48341, 48353, 48371, 48383, 48397, 48407, 48409, 48413, 48437, 48449, 48463,
+48473, 48479, 48481, 48487, 48491, 48497, 48523, 48527, 48533, 48539, 48541,
+48563, 48571, 48589, 48593, 48611, 48619, 48623, 48647, 48649, 48661, 48673,
+48677, 48679, 48731, 48733, 48751, 48757, 48761, 48767, 48779, 48781, 48787,
+48799, 48809, 48817, 48821, 48823, 48847, 48857, 48859, 48869, 48871, 48883,
+48889, 48907, 48947, 48953, 48973, 48989, 48991, 49003, 49009, 49019, 49031,
+49033, 49037, 49043, 49057, 49069, 49081, 49103, 49109, 49117, 49121, 49123,
+49139, 49157, 49169, 49171, 49177, 49193, 49199, 49201, 49207, 49211, 49223,
+49253, 49261, 49277, 49279, 49297, 49307, 49331, 49333, 49339, 49363, 49367,
+49369, 49391, 49393, 49409, 49411, 49417, 49429, 49433, 49451, 49459, 49463,
+49477, 49481, 49499, 49523, 49529, 49531, 49537, 49547, 49549, 49559, 49597,
+49603, 49613, 49627, 49633, 49639, 49663, 49667, 49669, 49681, 49697, 49711,
+49727, 49739, 49741, 49747, 49757, 49783, 49787, 49789, 49801, 49807, 49811,
+49823, 49831, 49843, 49853, 49871, 49877, 49891, 49919, 49921, 49927, 49937,
+49939, 49943, 49957, 49991, 49993, 49999, 50021, 50023, 50033, 50047, 50051,
+50053, 50069, 50077, 50087, 50093, 50101, 50111, 50119, 50123, 50129, 50131,
+50147, 50153, 50159, 50177, 50207, 50221, 50227, 50231, 50261, 50263, 50273,
+50287, 50291, 50311, 50321, 50329, 50333, 50341, 50359, 50363, 50377, 50383,
+50387, 50411, 50417, 50423, 50441, 50459, 50461, 50497, 50503, 50513, 50527,
+50539, 50543, 50549, 50551, 50581, 50587, 50591, 50593, 50599, 50627, 50647,
+50651, 50671, 50683, 50707, 50723, 50741, 50753, 50767, 50773, 50777, 50789,
+50821, 50833, 50839, 50849, 50857, 50867, 50873, 50891, 50893, 50909, 50923,
+50929, 50951, 50957, 50969, 50971, 50989, 50993, 51001, 51031, 51043, 51047,
+51059, 51061, 51071, 51109, 51131, 51133, 51137, 51151, 51157, 51169, 51193,
+51197, 51199, 51203, 51217, 51229, 51239, 51241, 51257, 51263, 51283, 51287,
+51307, 51329, 51341, 51343, 51347, 51349, 51361, 51383, 51407, 51413, 51419,
+51421, 51427, 51431, 51437, 51439, 51449, 51461, 51473, 51479, 51481, 51487,
+51503, 51511, 51517, 51521, 51539, 51551, 51563, 51577, 51581, 51593, 51599,
+51607, 51613, 51631, 51637, 51647, 51659, 51673, 51679, 51683, 51691, 51713,
+51719, 51721, 51749, 51767, 51769, 51787, 51797, 51803, 51817, 51827, 51829,
+51839, 51853, 51859, 51869, 51871, 51893, 51899, 51907, 51913, 51929, 51941,
+51949, 51971, 51973, 51977, 51991, 52009, 52021, 52027, 52051, 52057, 52067,
+52069, 52081, 52103, 52121, 52127, 52147, 52153, 52163, 52177, 52181, 52183,
+52189, 52201, 52223, 52237, 52249, 52253, 52259, 52267, 52289, 52291, 52301,
+52313, 52321, 52361, 52363, 52369, 52379, 52387, 52391, 52433, 52453, 52457,
+52489, 52501, 52511, 52517, 52529, 52541, 52543, 52553, 52561, 52567, 52571,
+52579, 52583, 52609, 52627, 52631, 52639, 52667, 52673, 52691, 52697, 52709,
+52711, 52721, 52727, 52733, 52747, 52757, 52769, 52783, 52807, 52813, 52817,
+52837, 52859, 52861, 52879, 52883, 52889, 52901, 52903, 52919, 52937, 52951,
+52957, 52963, 52967, 52973, 52981, 52999, 53003, 53017, 53047, 53051, 53069,
+53077, 53087, 53089, 53093, 53101, 53113, 53117, 53129, 53147, 53149, 53161,
+53171, 53173, 53189, 53197, 53201, 53231, 53233, 53239, 53267, 53269, 53279,
+53281, 53299, 53309, 53323, 53327, 53353, 53359, 53377, 53381, 53401, 53407,
+53411, 53419, 53437, 53441, 53453, 53479, 53503, 53507, 53527, 53549, 53551,
+53569, 53591, 53593, 53597, 53609, 53611, 53617, 53623, 53629, 53633, 53639,
+53653, 53657, 53681, 53693, 53699, 53717, 53719, 53731, 53759, 53773, 53777,
+53783, 53791, 53813, 53819, 53831, 53849, 53857, 53861, 53881, 53887, 53891,
+53897, 53899, 53917, 53923, 53927, 53939, 53951, 53959, 53987, 53993, 54001,
+54011, 54013, 54037, 54049, 54059, 54083, 54091, 54101, 54121, 54133, 54139,
+54151, 54163, 54167, 54181, 54193, 54217, 54251, 54269, 54277, 54287, 54293,
+54311, 54319, 54323, 54331, 54347, 54361, 54367, 54371, 54377, 54401, 54403,
+54409, 54413, 54419, 54421, 54437, 54443, 54449, 54469, 54493, 54497, 54499,
+54503, 54517, 54521, 54539, 54541, 54547, 54559, 54563, 54577, 54581, 54583,
+54601, 54617, 54623, 54629, 54631, 54647, 54667, 54673, 54679, 54709, 54713,
+54721, 54727, 54751, 54767, 54773, 54779, 54787, 54799, 54829, 54833, 54851,
+54869, 54877, 54881, 54907, 54917, 54919, 54941, 54949, 54959, 54973, 54979,
+54983, 55001, 55009, 55021, 55049, 55051, 55057, 55061, 55073, 55079, 55103,
+55109, 55117, 55127, 55147, 55163, 55171, 55201, 55207, 55213, 55217, 55219,
+55229, 55243, 55249, 55259, 55291, 55313, 55331, 55333, 55337, 55339, 55343,
+55351, 55373, 55381, 55399, 55411, 55439, 55441, 55457, 55469, 55487, 55501,
+55511, 55529, 55541, 55547, 55579, 55589, 55603, 55609, 55619, 55621, 55631,
+55633, 55639, 55661, 55663, 55667, 55673, 55681, 55691, 55697, 55711, 55717,
+55721, 55733, 55763, 55787, 55793, 55799, 55807, 55813, 55817, 55819, 55823,
+55829, 55837, 55843, 55849, 55871, 55889, 55897, 55901, 55903, 55921, 55927,
+55931, 55933, 55949, 55967, 55987, 55997, 56003, 56009, 56039, 56041, 56053,
+56081, 56087, 56093, 56099, 56101, 56113, 56123, 56131, 56149, 56167, 56171,
+56179, 56197, 56207, 56209, 56237, 56239, 56249, 56263, 56267, 56269, 56299,
+56311, 56333, 56359, 56369, 56377, 56383, 56393, 56401, 56417, 56431, 56437,
+56443, 56453, 56467, 56473, 56477, 56479, 56489, 56501, 56503, 56509, 56519,
+56527, 56531, 56533, 56543, 56569, 56591, 56597, 56599, 56611, 56629, 56633,
+56659, 56663, 56671, 56681, 56687, 56701, 56711, 56713, 56731, 56737, 56747,
+56767, 56773, 56779, 56783, 56807, 56809, 56813, 56821, 56827, 56843, 56857,
+56873, 56891, 56893, 56897, 56909, 56911, 56921, 56923, 56929, 56941, 56951,
+56957, 56963, 56983, 56989, 56993, 56999, 57037, 57041, 57047, 57059, 57073,
+57077, 57089, 57097, 57107, 57119, 57131, 57139, 57143, 57149, 57163, 57173,
+57179, 57191, 57193, 57203, 57221, 57223, 57241, 57251, 57259, 57269, 57271,
+57283, 57287, 57301, 57329, 57331, 57347, 57349, 57367, 57373, 57383, 57389,
+57397, 57413, 57427, 57457, 57467, 57487, 57493, 57503, 57527, 57529, 57557,
+57559, 57571, 57587, 57593, 57601, 57637, 57641, 57649, 57653, 57667, 57679,
+57689, 57697, 57709, 57713, 57719, 57727, 57731, 57737, 57751, 57773, 57781,
+57787, 57791, 57793, 57803, 57809, 57829, 57839, 57847, 57853, 57859, 57881,
+57899, 57901, 57917, 57923, 57943, 57947, 57973, 57977, 57991, 58013, 58027,
+58031, 58043, 58049, 58057, 58061, 58067, 58073, 58099, 58109, 58111, 58129,
+58147, 58151, 58153, 58169, 58171, 58189, 58193, 58199, 58207, 58211, 58217,
+58229, 58231, 58237, 58243, 58271, 58309, 58313, 58321, 58337, 58363, 58367,
+58369, 58379, 58391, 58393, 58403, 58411, 58417, 58427, 58439, 58441, 58451,
+58453, 58477, 58481, 58511, 58537, 58543, 58549, 58567, 58573, 58579, 58601,
+58603, 58613, 58631, 58657, 58661, 58679, 58687, 58693, 58699, 58711, 58727,
+58733, 58741, 58757, 58763, 58771, 58787, 58789, 58831, 58889, 58897, 58901,
+58907, 58909, 58913, 58921, 58937, 58943, 58963, 58967, 58979, 58991, 58997,
+59009, 59011, 59021, 59023, 59029, 59051, 59053, 59063, 59069, 59077, 59083,
+59093, 59107, 59113, 59119, 59123, 59141, 59149, 59159, 59167, 59183, 59197,
+59207, 59209, 59219, 59221, 59233, 59239, 59243, 59263, 59273, 59281, 59333,
+59341, 59351, 59357, 59359, 59369, 59377, 59387, 59393, 59399, 59407, 59417,
+59419, 59441, 59443, 59447, 59453, 59467, 59471, 59473, 59497, 59509, 59513,
+59539, 59557, 59561, 59567, 59581, 59611, 59617, 59621, 59627, 59629, 59651,
+59659, 59663, 59669, 59671, 59693, 59699, 59707, 59723, 59729, 59743, 59747,
+59753, 59771, 59779, 59791, 59797, 59809, 59833, 59863, 59879, 59887, 59921,
+59929, 59951, 59957, 59971, 59981, 59999, 60013, 60017, 60029, 60037, 60041,
+60077, 60083, 60089, 60091, 60101, 60103, 60107, 60127, 60133, 60139, 60149,
+60161, 60167, 60169, 60209, 60217, 60223, 60251, 60257, 60259, 60271, 60289,
+60293, 60317, 60331, 60337, 60343, 60353, 60373, 60383, 60397, 60413, 60427,
+60443, 60449, 60457, 60493, 60497, 60509, 60521, 60527, 60539, 60589, 60601,
+60607, 60611, 60617, 60623, 60631, 60637, 60647, 60649, 60659, 60661, 60679,
+60689, 60703, 60719, 60727, 60733, 60737, 60757, 60761, 60763, 60773, 60779,
+60793, 60811, 60821, 60859, 60869, 60887, 60889, 60899, 60901, 60913, 60917,
+60919, 60923, 60937, 60943, 60953, 60961, 61001, 61007, 61027, 61031, 61043,
+61051, 61057, 61091, 61099, 61121, 61129, 61141, 61151, 61153, 61169, 61211,
+61223, 61231, 61253, 61261, 61283, 61291, 61297, 61331, 61333, 61339, 61343,
+61357, 61363, 61379, 61381, 61403, 61409, 61417, 61441, 61463, 61469, 61471,
+61483, 61487, 61493, 61507, 61511, 61519, 61543, 61547, 61553, 61559, 61561,
+61583, 61603, 61609, 61613, 61627, 61631, 61637, 61643, 61651, 61657, 61667,
+61673, 61681, 61687, 61703, 61717, 61723, 61729, 61751, 61757, 61781, 61813,
+61819, 61837, 61843, 61861, 61871, 61879, 61909, 61927, 61933, 61949, 61961,
+61967, 61979, 61981, 61987, 61991, 62003, 62011, 62017, 62039, 62047, 62053,
+62057, 62071, 62081, 62099, 62119, 62129, 62131, 62137, 62141, 62143, 62171,
+62189, 62191, 62201, 62207, 62213, 62219, 62233, 62273, 62297, 62299, 62303,
+62311, 62323, 62327, 62347, 62351, 62383, 62401, 62417, 62423, 62459, 62467,
+62473, 62477, 62483, 62497, 62501, 62507, 62533, 62539, 62549, 62563, 62581,
+62591, 62597, 62603, 62617, 62627, 62633, 62639, 62653, 62659, 62683, 62687,
+62701, 62723, 62731, 62743, 62753, 62761, 62773, 62791, 62801, 62819, 62827,
+62851, 62861, 62869, 62873, 62897, 62903, 62921, 62927, 62929, 62939, 62969,
+62971, 62981, 62983, 62987, 62989, 63029, 63031, 63059, 63067, 63073, 63079,
+63097, 63103, 63113, 63127, 63131, 63149, 63179, 63197, 63199, 63211, 63241,
+63247, 63277, 63281, 63299, 63311, 63313, 63317, 63331, 63337, 63347, 63353,
+63361, 63367, 63377, 63389, 63391, 63397, 63409, 63419, 63421, 63439, 63443,
+63463, 63467, 63473, 63487, 63493, 63499, 63521, 63527, 63533, 63541, 63559,
+63577, 63587, 63589, 63599, 63601, 63607, 63611, 63617, 63629, 63647, 63649,
+63659, 63667, 63671, 63689, 63691, 63697, 63703, 63709, 63719, 63727, 63737,
+63743, 63761, 63773, 63781, 63793, 63799, 63803, 63809, 63823, 63839, 63841,
+63853, 63857, 63863, 63901, 63907, 63913, 63929, 63949, 63977, 63997, 64007,
+64013, 64019, 64033, 64037, 64063, 64067, 64081, 64091, 64109, 64123, 64151,
+64153, 64157, 64171, 64187, 64189, 64217, 64223, 64231, 64237, 64271, 64279,
+64283, 64301, 64303, 64319, 64327, 64333, 64373, 64381, 64399, 64403, 64433,
+64439, 64451, 64453, 64483, 64489, 64499, 64513, 64553, 64567, 64577, 64579,
+64591, 64601, 64609, 64613, 64621, 64627, 64633, 64661, 64663, 64667, 64679,
+64693, 64709, 64717, 64747, 64763, 64781, 64783, 64793, 64811, 64817, 64849,
+64853, 64871, 64877, 64879, 64891, 64901, 64919, 64921, 64927, 64937, 64951,
+64969, 64997, 65003, 65011, 65027, 65029, 65033, 65053, 65063, 65071, 65089,
+65099, 65101, 65111, 65119, 65123, 65129, 65141, 65147, 65167, 65171, 65173,
+65179, 65183, 65203, 65213, 65239, 65257, 65267, 65269, 65287, 65293, 65309,
+65323, 65327, 65353, 65357, 65371, 65381, 65393, 65407, 65413, 65419, 65423,
+65437, 65447, 65449, 65479, 65497, 65519, 65521, 0 };
+
+}
diff --git a/comm/third_party/botan/src/lib/math/numbertheory/reducer.cpp b/comm/third_party/botan/src/lib/math/numbertheory/reducer.cpp
new file mode 100644
index 0000000000..deb3874d3e
--- /dev/null
+++ b/comm/third_party/botan/src/lib/math/numbertheory/reducer.cpp
@@ -0,0 +1,119 @@
+/*
+* Modular Reducer
+* (C) 1999-2011,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/reducer.h>
+#include <botan/internal/ct_utils.h>
+#include <botan/internal/mp_core.h>
+#include <botan/divide.h>
+
+namespace Botan {
+
+/*
+* Modular_Reducer Constructor
+*/
+Modular_Reducer::Modular_Reducer(const BigInt& mod)
+ {
+ if(mod < 0)
+ throw Invalid_Argument("Modular_Reducer: modulus must be positive");
+
+ // Left uninitialized if mod == 0
+ m_mod_words = 0;
+
+ if(mod > 0)
+ {
+ m_modulus = mod;
+ m_mod_words = m_modulus.sig_words();
+
+ // Compute mu = floor(2^{2k} / m)
+ m_mu.set_bit(2 * BOTAN_MP_WORD_BITS * m_mod_words);
+ m_mu = ct_divide(m_mu, m_modulus);
+ }
+ }
+
+BigInt Modular_Reducer::reduce(const BigInt& x) const
+ {
+ BigInt r;
+ secure_vector<word> ws;
+ reduce(r, x, 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<word>& 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(cnd, x.mutable_data(), ws.data(), max_words);
+ }
+
+}
+
+void Modular_Reducer::reduce(BigInt& t1, const BigInt& x, secure_vector<word>& 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);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/math/numbertheory/reducer.h b/comm/third_party/botan/src/lib/math/numbertheory/reducer.h
new file mode 100644
index 0000000000..b1c2c87a9f
--- /dev/null
+++ b/comm/third_party/botan/src/lib/math/numbertheory/reducer.h
@@ -0,0 +1,69 @@
+/*
+* Modular Reducer
+* (C) 1999-2010 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_MODULAR_REDUCER_H_
+#define BOTAN_MODULAR_REDUCER_H_
+
+#include <botan/numthry.h>
+
+namespace Botan {
+
+/**
+* Modular Reducer (using Barrett's technique)
+*/
+class BOTAN_PUBLIC_API(2,0) Modular_Reducer
+ {
+ public:
+ const BigInt& get_modulus() const { return m_modulus; }
+
+ BigInt reduce(const BigInt& x) const;
+
+ /**
+ * Multiply mod p
+ * @param x the first operand
+ * @param y the second operand
+ * @return (x * y) % p
+ */
+ BigInt multiply(const BigInt& x, const BigInt& y) const
+ { return reduce(x * y); }
+
+ /**
+ * Square mod p
+ * @param x the value to square
+ * @return (x * x) % p
+ */
+ BigInt square(const BigInt& x) const
+ { return reduce(Botan::square(x)); }
+
+ /**
+ * Cube mod p
+ * @param x the value to cube
+ * @return (x * x * x) % p
+ */
+ BigInt cube(const BigInt& x) const
+ { return multiply(x, this->square(x)); }
+
+ /**
+ * Low level reduction function. Mostly for internal use.
+ * Sometimes useful for performance by reducing temporaries
+ * Reduce x mod p and place the output in out. ** X and out must not reference each other **
+ * ws is a temporary workspace.
+ */
+ void reduce(BigInt& out, const BigInt& x, secure_vector<word>& ws) const;
+
+ bool initialized() const { return (m_mod_words != 0); }
+
+ Modular_Reducer() { m_mod_words = 0; }
+ explicit Modular_Reducer(const BigInt& mod);
+ private:
+ BigInt m_modulus, m_mu;
+ size_t m_mod_words;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/math/numbertheory/ressol.cpp b/comm/third_party/botan/src/lib/math/numbertheory/ressol.cpp
new file mode 100644
index 0000000000..f9e7e3eb1d
--- /dev/null
+++ b/comm/third_party/botan/src/lib/math/numbertheory/ressol.cpp
@@ -0,0 +1,100 @@
+/*
+* (C) 2007,2008 Falko Strenzke, FlexSecure GmbH
+* (C) 2008 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/numthry.h>
+#include <botan/reducer.h>
+
+namespace Botan {
+
+/*
+* Tonelli-Shanks algorithm
+*/
+BigInt ressol(const BigInt& a, const BigInt& p)
+ {
+ if(p <= 1 || p.is_even())
+ throw Invalid_Argument("ressol: invalid prime");
+
+ if(a == 0)
+ return 0;
+ else if(a < 0)
+ throw Invalid_Argument("ressol: value to solve for must be positive");
+ else if(a >= p)
+ throw Invalid_Argument("ressol: value to solve for must be less than p");
+
+ if(p == 2)
+ return a;
+
+ if(jacobi(a, p) != 1) // not a quadratic residue
+ return -BigInt(1);
+
+ if(p % 4 == 3) // The easy case
+ {
+ return power_mod(a, ((p+1) >> 2), p);
+ }
+
+ size_t s = low_zero_bits(p - 1);
+ BigInt q = p >> s;
+
+ q -= 1;
+ q >>= 1;
+
+ Modular_Reducer mod_p(p);
+
+ BigInt r = power_mod(a, q, p);
+ BigInt n = mod_p.multiply(a, mod_p.square(r));
+ r = mod_p.multiply(r, a);
+
+ if(n == 1)
+ return r;
+
+ // find random quadratic nonresidue z
+ word z = 2;
+ for(;;)
+ {
+ if(jacobi(z, p) == -1) // found one
+ break;
+
+ z += 1; // try next z
+
+ /*
+ * The expected number of tests to find a non-residue modulo a
+ * prime is 2. If we have not found one after 256 then almost
+ * certainly we have been given a non-prime p.
+ */
+ if(z >= 256)
+ return -BigInt(1);
+ }
+
+ BigInt c = power_mod(z, (q << 1) + 1, p);
+
+ while(n > 1)
+ {
+ q = n;
+
+ size_t i = 0;
+ while(q != 1)
+ {
+ q = mod_p.square(q);
+ ++i;
+
+ if(i >= s)
+ {
+ return -BigInt(1);
+ }
+ }
+
+ c = power_mod(c, BigInt::power_of_2(s-i-1), p);
+ r = mod_p.multiply(r, c);
+ c = mod_p.square(c);
+ n = mod_p.multiply(n, c);
+ s = i;
+ }
+
+ return r;
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/misc/aont/info.txt b/comm/third_party/botan/src/lib/misc/aont/info.txt
new file mode 100644
index 0000000000..bbd6449f7e
--- /dev/null
+++ b/comm/third_party/botan/src/lib/misc/aont/info.txt
@@ -0,0 +1,9 @@
+<defines>
+PACKAGE_TRANSFORM -> 20131128
+</defines>
+
+<requires>
+ctr
+rng
+filters
+</requires>
diff --git a/comm/third_party/botan/src/lib/misc/aont/package.cpp b/comm/third_party/botan/src/lib/misc/aont/package.cpp
new file mode 100644
index 0000000000..7cadc62f48
--- /dev/null
+++ b/comm/third_party/botan/src/lib/misc/aont/package.cpp
@@ -0,0 +1,125 @@
+/*
+* Rivest's Package Tranform
+*
+* (C) 2009 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/package.h>
+#include <botan/filters.h>
+#include <botan/ctr.h>
+#include <botan/loadstor.h>
+#include <botan/rng.h>
+
+namespace Botan {
+
+void aont_package(RandomNumberGenerator& rng,
+ BlockCipher* cipher,
+ const uint8_t input[], size_t input_len,
+ uint8_t output[])
+ {
+ if(input_len <= 1)
+ throw Encoding_Error("Package transform cannot encode small inputs");
+
+ const size_t BLOCK_SIZE = cipher->block_size();
+
+ if(!cipher->valid_keylength(BLOCK_SIZE))
+ throw Invalid_Argument("AONT::package: Invalid cipher");
+
+ // The all-zero string which is used both as the CTR IV and as K0
+ const std::string all_zeros(BLOCK_SIZE*2, '0');
+
+ SymmetricKey package_key(rng, BLOCK_SIZE);
+
+ Pipe pipe(new StreamCipher_Filter(new CTR_BE(cipher), package_key));
+
+ pipe.process_msg(input, input_len);
+ const size_t remaining = pipe.remaining();
+ BOTAN_ASSERT_EQUAL(remaining, pipe.read(output, remaining), "Expected read size");
+
+ // Set K0 (the all zero key)
+ cipher->set_key(SymmetricKey(all_zeros));
+
+ secure_vector<uint8_t> buf(BLOCK_SIZE);
+
+ const size_t blocks =
+ (input_len + BLOCK_SIZE - 1) / BLOCK_SIZE;
+
+ uint8_t* final_block = output + input_len;
+ clear_mem(final_block, BLOCK_SIZE);
+
+ // XOR the hash blocks into the final block
+ for(size_t i = 0; i != blocks; ++i)
+ {
+ const size_t left = std::min<size_t>(BLOCK_SIZE,
+ input_len - BLOCK_SIZE * i);
+
+ zeroise(buf);
+ copy_mem(buf.data(), output + (BLOCK_SIZE * i), left);
+
+ for(size_t j = 0; j != sizeof(i); ++j)
+ buf[BLOCK_SIZE - 1 - j] ^= get_byte(sizeof(i)-1-j, i);
+
+ cipher->encrypt(buf.data());
+
+ xor_buf(final_block, buf.data(), BLOCK_SIZE);
+ }
+
+ // XOR the random package key into the final block
+ xor_buf(final_block, package_key.begin(), BLOCK_SIZE);
+ }
+
+void aont_unpackage(BlockCipher* cipher,
+ const uint8_t input[], size_t input_len,
+ uint8_t output[])
+ {
+ const size_t BLOCK_SIZE = cipher->block_size();
+
+ if(!cipher->valid_keylength(BLOCK_SIZE))
+ throw Invalid_Argument("AONT::unpackage: Invalid cipher");
+
+ if(input_len < BLOCK_SIZE)
+ throw Invalid_Argument("AONT::unpackage: Input too short");
+
+ // The all-zero string which is used both as the CTR IV and as K0
+ const std::string all_zeros(BLOCK_SIZE*2, '0');
+
+ cipher->set_key(SymmetricKey(all_zeros));
+
+ secure_vector<uint8_t> package_key(BLOCK_SIZE);
+ secure_vector<uint8_t> buf(BLOCK_SIZE);
+
+ // Copy the package key (masked with the block hashes)
+ copy_mem(package_key.data(),
+ input + (input_len - BLOCK_SIZE),
+ BLOCK_SIZE);
+
+ const size_t blocks = ((input_len - 1) / BLOCK_SIZE);
+
+ // XOR the blocks into the package key bits
+ for(size_t i = 0; i != blocks; ++i)
+ {
+ const size_t left = std::min<size_t>(BLOCK_SIZE,
+ input_len - BLOCK_SIZE * (i+1));
+
+ zeroise(buf);
+ copy_mem(buf.data(), input + (BLOCK_SIZE * i), left);
+
+ for(size_t j = 0; j != sizeof(i); ++j)
+ buf[BLOCK_SIZE - 1 - j] ^= get_byte(sizeof(i)-1-j, i);
+
+ cipher->encrypt(buf.data());
+
+ xor_buf(package_key.data(), buf.data(), BLOCK_SIZE);
+ }
+
+ Pipe pipe(new StreamCipher_Filter(new CTR_BE(cipher), package_key));
+
+ pipe.process_msg(input, input_len - BLOCK_SIZE);
+
+ const size_t remaining = pipe.remaining();
+ BOTAN_ASSERT_EQUAL(remaining, pipe.read(output, remaining), "Expected read size");
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/misc/aont/package.h b/comm/third_party/botan/src/lib/misc/aont/package.h
new file mode 100644
index 0000000000..38e04e4708
--- /dev/null
+++ b/comm/third_party/botan/src/lib/misc/aont/package.h
@@ -0,0 +1,49 @@
+/*
+* Rivest's Package Tranform
+* (C) 2009 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_AONT_PACKAGE_TRANSFORM_H_
+#define BOTAN_AONT_PACKAGE_TRANSFORM_H_
+
+#include <botan/block_cipher.h>
+
+namespace Botan {
+
+class RandomNumberGenerator;
+
+/**
+* Rivest's Package Tranform
+* @param rng the random number generator to use
+* @param cipher the block cipher to use (aont_package takes ownership)
+* @param input the input data buffer
+* @param input_len the length of the input data in bytes
+* @param output the output data buffer (must be at least
+* input_len + cipher->BLOCK_SIZE bytes long)
+*/
+BOTAN_DEPRECATED("Possibly broken, avoid")
+void BOTAN_PUBLIC_API(2,0)
+aont_package(RandomNumberGenerator& rng,
+ BlockCipher* cipher,
+ const uint8_t input[], size_t input_len,
+ uint8_t output[]);
+
+/**
+* Rivest's Package Tranform (Inversion)
+* @param cipher the block cipher to use (aont_package takes ownership)
+* @param input the input data buffer
+* @param input_len the length of the input data in bytes
+* @param output the output data buffer (must be at least
+* input_len - cipher->BLOCK_SIZE bytes long)
+*/
+BOTAN_DEPRECATED("Possibly broken, avoid")
+void BOTAN_PUBLIC_API(2,0)
+aont_unpackage(BlockCipher* cipher,
+ const uint8_t input[], size_t input_len,
+ uint8_t output[]);
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/misc/cryptobox/cryptobox.cpp b/comm/third_party/botan/src/lib/misc/cryptobox/cryptobox.cpp
new file mode 100644
index 0000000000..452d953088
--- /dev/null
+++ b/comm/third_party/botan/src/lib/misc/cryptobox/cryptobox.cpp
@@ -0,0 +1,180 @@
+/*
+* Cryptobox Message Routines
+* (C) 2009 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/cryptobox.h>
+#include <botan/cipher_mode.h>
+#include <botan/mac.h>
+#include <botan/rng.h>
+#include <botan/pbkdf.h>
+#include <botan/data_src.h>
+#include <botan/pem.h>
+#include <botan/loadstor.h>
+#include <botan/mem_ops.h>
+
+namespace Botan {
+
+namespace CryptoBox {
+
+namespace {
+
+/*
+First 24 bits of SHA-256("Botan Cryptobox"), followed by 8 0 bits
+for later use as flags, etc if needed
+*/
+const uint32_t CRYPTOBOX_VERSION_CODE = 0xEFC22400;
+
+const size_t VERSION_CODE_LEN = 4;
+const size_t CIPHER_KEY_LEN = 32;
+const size_t CIPHER_IV_LEN = 16;
+const size_t MAC_KEY_LEN = 32;
+const size_t MAC_OUTPUT_LEN = 20;
+const size_t PBKDF_SALT_LEN = 10;
+const size_t PBKDF_ITERATIONS = 8 * 1024;
+
+const size_t PBKDF_OUTPUT_LEN = CIPHER_KEY_LEN + MAC_KEY_LEN + CIPHER_IV_LEN;
+const size_t CRYPTOBOX_HEADER_LEN = VERSION_CODE_LEN + PBKDF_SALT_LEN + MAC_OUTPUT_LEN;
+
+}
+
+std::string encrypt(const uint8_t input[], size_t input_len,
+ const std::string& passphrase,
+ RandomNumberGenerator& rng)
+ {
+ /*
+ Output format is:
+ version # (4 bytes)
+ salt (10 bytes)
+ mac (20 bytes)
+ ciphertext
+ */
+ secure_vector<uint8_t> out_buf(CRYPTOBOX_HEADER_LEN + input_len);
+ for(size_t i = 0; i != VERSION_CODE_LEN; ++i)
+ out_buf[i] = get_byte(i, CRYPTOBOX_VERSION_CODE);
+ rng.randomize(&out_buf[VERSION_CODE_LEN], PBKDF_SALT_LEN);
+ // space left for MAC here
+ if(input_len > 0)
+ copy_mem(&out_buf[CRYPTOBOX_HEADER_LEN], input, input_len);
+
+ // Generate the keys and IV
+
+ std::unique_ptr<PBKDF> pbkdf(PBKDF::create_or_throw("PBKDF2(HMAC(SHA-512))"));
+
+ OctetString master_key = pbkdf->derive_key(
+ CIPHER_KEY_LEN + MAC_KEY_LEN + CIPHER_IV_LEN,
+ passphrase,
+ &out_buf[VERSION_CODE_LEN],
+ PBKDF_SALT_LEN,
+ PBKDF_ITERATIONS);
+
+ const uint8_t* mk = master_key.begin();
+ const uint8_t* cipher_key = mk;
+ const uint8_t* mac_key = mk + CIPHER_KEY_LEN;
+ const uint8_t* iv = mk + CIPHER_KEY_LEN + MAC_KEY_LEN;
+
+ // Now encrypt and authenticate
+ std::unique_ptr<Cipher_Mode> ctr = Cipher_Mode::create_or_throw("Serpent/CTR-BE", ENCRYPTION);
+ ctr->set_key(cipher_key, CIPHER_KEY_LEN);
+ ctr->start(iv, CIPHER_IV_LEN);
+ ctr->finish(out_buf, CRYPTOBOX_HEADER_LEN);
+
+ std::unique_ptr<MessageAuthenticationCode> hmac =
+ MessageAuthenticationCode::create_or_throw("HMAC(SHA-512)");
+ hmac->set_key(mac_key, MAC_KEY_LEN);
+ if(input_len > 0)
+ hmac->update(&out_buf[CRYPTOBOX_HEADER_LEN], input_len);
+
+ // Can't write directly because of MAC truncation
+ secure_vector<uint8_t> mac = hmac->final();
+ copy_mem(&out_buf[VERSION_CODE_LEN + PBKDF_SALT_LEN], mac.data(), MAC_OUTPUT_LEN);
+
+ return PEM_Code::encode(out_buf, "BOTAN CRYPTOBOX MESSAGE");
+ }
+
+secure_vector<uint8_t>
+decrypt_bin(const uint8_t input[], size_t input_len,
+ const std::string& passphrase)
+ {
+ DataSource_Memory input_src(input, input_len);
+ secure_vector<uint8_t> ciphertext =
+ PEM_Code::decode_check_label(input_src,
+ "BOTAN CRYPTOBOX MESSAGE");
+
+ if(ciphertext.size() < CRYPTOBOX_HEADER_LEN)
+ throw Decoding_Error("Invalid CryptoBox input");
+
+ for(size_t i = 0; i != VERSION_CODE_LEN; ++i)
+ if(ciphertext[i] != get_byte(i, CRYPTOBOX_VERSION_CODE))
+ throw Decoding_Error("Bad CryptoBox version");
+
+ const uint8_t* pbkdf_salt = &ciphertext[VERSION_CODE_LEN];
+ const uint8_t* box_mac = &ciphertext[VERSION_CODE_LEN + PBKDF_SALT_LEN];
+
+ std::unique_ptr<PBKDF> pbkdf(PBKDF::create_or_throw("PBKDF2(HMAC(SHA-512))"));
+
+ OctetString master_key = pbkdf->derive_key(
+ PBKDF_OUTPUT_LEN,
+ passphrase,
+ pbkdf_salt,
+ PBKDF_SALT_LEN,
+ PBKDF_ITERATIONS);
+
+ const uint8_t* mk = master_key.begin();
+ const uint8_t* cipher_key = mk;
+ const uint8_t* mac_key = mk + CIPHER_KEY_LEN;
+ const uint8_t* iv = mk + CIPHER_KEY_LEN + MAC_KEY_LEN;
+
+ // Now authenticate and decrypt
+ std::unique_ptr<MessageAuthenticationCode> hmac =
+ MessageAuthenticationCode::create_or_throw("HMAC(SHA-512)");
+ hmac->set_key(mac_key, MAC_KEY_LEN);
+
+ if(ciphertext.size() > CRYPTOBOX_HEADER_LEN)
+ {
+ hmac->update(&ciphertext[CRYPTOBOX_HEADER_LEN],
+ ciphertext.size() - CRYPTOBOX_HEADER_LEN);
+ }
+ secure_vector<uint8_t> computed_mac = hmac->final();
+
+ if(!constant_time_compare(computed_mac.data(), box_mac, MAC_OUTPUT_LEN))
+ throw Decoding_Error("CryptoBox integrity failure");
+
+ std::unique_ptr<Cipher_Mode> ctr(Cipher_Mode::create_or_throw("Serpent/CTR-BE", DECRYPTION));
+ ctr->set_key(cipher_key, CIPHER_KEY_LEN);
+ ctr->start(iv, CIPHER_IV_LEN);
+ ctr->finish(ciphertext, CRYPTOBOX_HEADER_LEN);
+
+ ciphertext.erase(ciphertext.begin(), ciphertext.begin() + CRYPTOBOX_HEADER_LEN);
+ return ciphertext;
+ }
+
+secure_vector<uint8_t> decrypt_bin(const std::string& input,
+ const std::string& passphrase)
+ {
+ return decrypt_bin(cast_char_ptr_to_uint8(input.data()),
+ input.size(),
+ passphrase);
+ }
+
+std::string decrypt(const uint8_t input[], size_t input_len,
+ const std::string& passphrase)
+ {
+ const secure_vector<uint8_t> bin = decrypt_bin(input, input_len, passphrase);
+
+ return std::string(cast_uint8_ptr_to_char(&bin[0]),
+ bin.size());
+ }
+
+std::string decrypt(const std::string& input,
+ const std::string& passphrase)
+ {
+ return decrypt(cast_char_ptr_to_uint8(input.data()),
+ input.size(), passphrase);
+ }
+
+}
+
+}
diff --git a/comm/third_party/botan/src/lib/misc/cryptobox/cryptobox.h b/comm/third_party/botan/src/lib/misc/cryptobox/cryptobox.h
new file mode 100644
index 0000000000..977ef37d65
--- /dev/null
+++ b/comm/third_party/botan/src/lib/misc/cryptobox/cryptobox.h
@@ -0,0 +1,79 @@
+/*
+* Cryptobox Message Routines
+* (C) 2009 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_CRYPTOBOX_H_
+#define BOTAN_CRYPTOBOX_H_
+
+#include <string>
+#include <botan/symkey.h>
+
+namespace Botan {
+
+class RandomNumberGenerator;
+
+/**
+* This namespace holds various high-level crypto functions
+*/
+namespace CryptoBox {
+
+/**
+* Encrypt a message using a passphrase
+* @param input the input data
+* @param input_len the length of input in bytes
+* @param passphrase the passphrase used to encrypt the message
+* @param rng a ref to a random number generator, such as AutoSeeded_RNG
+*/
+BOTAN_PUBLIC_API(2,0) std::string encrypt(const uint8_t input[], size_t input_len,
+ const std::string& passphrase,
+ RandomNumberGenerator& rng);
+
+
+/**
+* Decrypt a message encrypted with CryptoBox::encrypt
+* @param input the input data
+* @param input_len the length of input in bytes
+* @param passphrase the passphrase used to encrypt the message
+*/
+BOTAN_PUBLIC_API(2,3)
+secure_vector<uint8_t>
+decrypt_bin(const uint8_t input[], size_t input_len,
+ const std::string& passphrase);
+
+/**
+* Decrypt a message encrypted with CryptoBox::encrypt
+* @param input the input data
+* @param passphrase the passphrase used to encrypt the message
+*/
+BOTAN_PUBLIC_API(2,3)
+secure_vector<uint8_t>
+decrypt_bin(const std::string& input,
+ const std::string& passphrase);
+
+/**
+* Decrypt a message encrypted with CryptoBox::encrypt
+* @param input the input data
+* @param input_len the length of input in bytes
+* @param passphrase the passphrase used to encrypt the message
+*/
+BOTAN_PUBLIC_API(2,0)
+std::string decrypt(const uint8_t input[], size_t input_len,
+ const std::string& passphrase);
+
+/**
+* Decrypt a message encrypted with CryptoBox::encrypt
+* @param input the input data
+* @param passphrase the passphrase used to encrypt the message
+*/
+BOTAN_PUBLIC_API(2,0)
+std::string decrypt(const std::string& input,
+ const std::string& passphrase);
+
+}
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/misc/cryptobox/info.txt b/comm/third_party/botan/src/lib/misc/cryptobox/info.txt
new file mode 100644
index 0000000000..770076a402
--- /dev/null
+++ b/comm/third_party/botan/src/lib/misc/cryptobox/info.txt
@@ -0,0 +1,14 @@
+<defines>
+CRYPTO_BOX -> 20131128
+</defines>
+
+<requires>
+base64
+ctr
+hmac
+modes
+pbkdf2
+pem
+serpent
+sha2_64
+</requires>
diff --git a/comm/third_party/botan/src/lib/misc/fpe_fe1/fpe_fe1.cpp b/comm/third_party/botan/src/lib/misc/fpe_fe1/fpe_fe1.cpp
new file mode 100644
index 0000000000..7e3dac5028
--- /dev/null
+++ b/comm/third_party/botan/src/lib/misc/fpe_fe1/fpe_fe1.cpp
@@ -0,0 +1,218 @@
+/*
+* Format Preserving Encryption (FE1 scheme)
+* (C) 2009,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/fpe_fe1.h>
+#include <botan/loadstor.h>
+#include <botan/numthry.h>
+#include <botan/divide.h>
+#include <botan/reducer.h>
+#include <botan/mac.h>
+
+namespace Botan {
+
+namespace {
+
+// Normally FPE is for SSNs, CC#s, etc, nothing too big
+const size_t MAX_N_BYTES = 128/8;
+
+/*
+* Factor n into a and b which are as close together as possible.
+* Assumes n is composed mostly of small factors which is the case for
+* typical uses of FPE (typically, n is a power of 10)
+*/
+void factor(BigInt n, BigInt& a, BigInt& b)
+ {
+ a = 1;
+ b = 1;
+
+ size_t n_low_zero = low_zero_bits(n);
+
+ a <<= (n_low_zero / 2);
+ b <<= n_low_zero - (n_low_zero / 2);
+ n >>= n_low_zero;
+
+ for(size_t i = 0; i != PRIME_TABLE_SIZE; ++i)
+ {
+ while(n % PRIMES[i] == 0)
+ {
+ a *= PRIMES[i];
+ if(a > b)
+ std::swap(a, b);
+ n /= PRIMES[i];
+ }
+ }
+
+ if(a > b)
+ std::swap(a, b);
+ a *= n;
+
+ if(a <= 1 || b <= 1)
+ throw Internal_Error("Could not factor n for use in FPE");
+ }
+
+}
+
+FPE_FE1::FPE_FE1(const BigInt& n,
+ size_t rounds,
+ bool compat_mode,
+ const std::string& mac_algo) :
+ m_rounds(rounds)
+ {
+ if(m_rounds < 3)
+ throw Invalid_Argument("FPE_FE1 rounds too small");
+
+ m_mac = MessageAuthenticationCode::create_or_throw(mac_algo);
+
+ m_n_bytes = BigInt::encode(n);
+
+ if(m_n_bytes.size() > MAX_N_BYTES)
+ throw Invalid_Argument("N is too large for FPE encryption");
+
+ factor(n, m_a, m_b);
+
+ if(compat_mode)
+ {
+ if(m_a < m_b)
+ std::swap(m_a, m_b);
+ }
+ else
+ {
+ if(m_a > m_b)
+ std::swap(m_a, m_b);
+ }
+
+ mod_a.reset(new Modular_Reducer(m_a));
+ }
+
+FPE_FE1::~FPE_FE1()
+ {
+ // for ~unique_ptr
+ }
+
+void FPE_FE1::clear()
+ {
+ m_mac->clear();
+ }
+
+std::string FPE_FE1::name() const
+ {
+ return "FPE_FE1(" + m_mac->name() + "," + std::to_string(m_rounds) + ")";
+ }
+
+Key_Length_Specification FPE_FE1::key_spec() const
+ {
+ return m_mac->key_spec();
+ }
+
+void FPE_FE1::key_schedule(const uint8_t key[], size_t length)
+ {
+ m_mac->set_key(key, length);
+ }
+
+BigInt FPE_FE1::F(const BigInt& R, size_t round,
+ const secure_vector<uint8_t>& tweak_mac,
+ secure_vector<uint8_t>& tmp) const
+ {
+ tmp = BigInt::encode_locked(R);
+
+ m_mac->update(tweak_mac);
+ m_mac->update_be(static_cast<uint32_t>(round));
+
+ m_mac->update_be(static_cast<uint32_t>(tmp.size()));
+ m_mac->update(tmp.data(), tmp.size());
+
+ tmp = m_mac->final();
+ return BigInt(tmp.data(), tmp.size());
+ }
+
+secure_vector<uint8_t> FPE_FE1::compute_tweak_mac(const uint8_t tweak[], size_t tweak_len) const
+ {
+ m_mac->update_be(static_cast<uint32_t>(m_n_bytes.size()));
+ m_mac->update(m_n_bytes.data(), m_n_bytes.size());
+
+ m_mac->update_be(static_cast<uint32_t>(tweak_len));
+ if(tweak_len > 0)
+ m_mac->update(tweak, tweak_len);
+
+ return m_mac->final();
+ }
+
+BigInt FPE_FE1::encrypt(const BigInt& input, const uint8_t tweak[], size_t tweak_len) const
+ {
+ const secure_vector<uint8_t> tweak_mac = compute_tweak_mac(tweak, tweak_len);
+
+ BigInt X = input;
+
+ secure_vector<uint8_t> tmp;
+
+ BigInt L, R, 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);
+ }
+
+ return X;
+ }
+
+BigInt FPE_FE1::decrypt(const BigInt& input, const uint8_t tweak[], size_t tweak_len) const
+ {
+ const secure_vector<uint8_t> tweak_mac = compute_tweak_mac(tweak, tweak_len);
+
+ BigInt X = input;
+ secure_vector<uint8_t> tmp;
+
+ BigInt W, R, 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;
+ }
+
+ return X;
+ }
+
+BigInt FPE_FE1::encrypt(const BigInt& x, uint64_t tweak) const
+ {
+ uint8_t tweak8[8];
+ store_be(tweak, tweak8);
+ return encrypt(x, tweak8, sizeof(tweak8));
+ }
+
+BigInt FPE_FE1::decrypt(const BigInt& x, uint64_t tweak) const
+ {
+ uint8_t tweak8[8];
+ store_be(tweak, tweak8);
+ return decrypt(x, tweak8, sizeof(tweak8));
+ }
+
+namespace FPE {
+
+BigInt fe1_encrypt(const BigInt& n, const BigInt& X,
+ const SymmetricKey& key,
+ const std::vector<uint8_t>& tweak)
+ {
+ FPE_FE1 fpe(n, 3, true, "HMAC(SHA-256)");
+ fpe.set_key(key);
+ return fpe.encrypt(X, tweak.data(), tweak.size());
+ }
+
+BigInt fe1_decrypt(const BigInt& n, const BigInt& X,
+ const SymmetricKey& key,
+ const std::vector<uint8_t>& tweak)
+ {
+ FPE_FE1 fpe(n, 3, true, "HMAC(SHA-256)");
+ fpe.set_key(key);
+ return fpe.decrypt(X, tweak.data(), tweak.size());
+ }
+
+}
+
+}
diff --git a/comm/third_party/botan/src/lib/misc/fpe_fe1/fpe_fe1.h b/comm/third_party/botan/src/lib/misc/fpe_fe1/fpe_fe1.h
new file mode 100644
index 0000000000..d9f760f0da
--- /dev/null
+++ b/comm/third_party/botan/src/lib/misc/fpe_fe1/fpe_fe1.h
@@ -0,0 +1,123 @@
+/*
+* Format Preserving Encryption (FE1 scheme)
+* (C) 2009,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_FPE_FE1_H_
+#define BOTAN_FPE_FE1_H_
+
+#include <botan/sym_algo.h>
+#include <botan/bigint.h>
+
+namespace Botan {
+
+class Modular_Reducer;
+class MessageAuthenticationCode;
+
+/**
+* Format Preserving Encryption using the scheme FE1 from the paper
+* "Format-Preserving Encryption" by Bellare, Rogaway, et al
+* (https://eprint.iacr.org/2009/251)
+*/
+class BOTAN_PUBLIC_API(2,5) FPE_FE1 final : public SymmetricAlgorithm
+ {
+ public:
+
+ /**
+ * @param n the modulus. All plaintext and ciphertext values must be
+ * less than this.
+ * @param rounds the number of rounds to use. Must be at least 3.
+ * @param compat_mode An error in versions before 2.5.0 chose incorrect
+ * 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,
+ const std::string& mac_algo = "HMAC(SHA-256)");
+
+ ~FPE_FE1();
+
+ Key_Length_Specification key_spec() const override;
+
+ std::string name() const override;
+
+ void clear() override;
+
+ /**
+ * Encrypt X from and onto the group Z_n using key and tweak
+ * @param x the plaintext to encrypt <= n
+ * @param tweak will modify the ciphertext
+ * @param tweak_len length of tweak
+ */
+ BigInt encrypt(const BigInt& x, const uint8_t tweak[], size_t tweak_len) const;
+
+ /**
+ * Decrypt X from and onto the group Z_n using key and tweak
+ * @param x the ciphertext to encrypt <= n
+ * @param tweak must match the value used to encrypt
+ * @param tweak_len length of tweak
+ */
+ BigInt decrypt(const BigInt& x, const uint8_t tweak[], size_t tweak_len) const;
+
+ BigInt encrypt(const BigInt& x, uint64_t tweak) const;
+
+ BigInt decrypt(const BigInt& x, uint64_t tweak) const;
+ private:
+ void key_schedule(const uint8_t key[], size_t length) override;
+
+ BigInt F(const BigInt& R, size_t round,
+ const secure_vector<uint8_t>& tweak,
+ secure_vector<uint8_t>& tmp) const;
+
+ secure_vector<uint8_t> compute_tweak_mac(const uint8_t tweak[], size_t tweak_len) const;
+
+ std::unique_ptr<MessageAuthenticationCode> m_mac;
+ std::unique_ptr<Modular_Reducer> mod_a;
+ std::vector<uint8_t> m_n_bytes;
+ BigInt m_a;
+ BigInt m_b;
+ size_t m_rounds;
+ };
+
+namespace FPE {
+
+/**
+* Format Preserving Encryption using the scheme FE1 from the paper
+* "Format-Preserving Encryption" by Bellare, Rogaway, et al
+* (https://eprint.iacr.org/2009/251)
+*
+* Encrypt X from and onto the group Z_n using key and tweak
+* @param n the modulus
+* @param X the plaintext as a BigInt
+* @param key a random key
+* @param tweak will modify the ciphertext (think of as an IV)
+*
+* @warning This function is hardcoded to use only 3 rounds which
+* 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<uint8_t>& tweak);
+
+/**
+* Decrypt X from and onto the group Z_n using key and tweak
+* @param n the modulus
+* @param X the ciphertext as a BigInt
+* @param key is the key used for encryption
+* @param tweak the same tweak used for encryption
+*
+* @warning This function is hardcoded to use only 3 rounds which
+* 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<uint8_t>& tweak);
+
+}
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/misc/fpe_fe1/info.txt b/comm/third_party/botan/src/lib/misc/fpe_fe1/info.txt
new file mode 100644
index 0000000000..1902fa9662
--- /dev/null
+++ b/comm/third_party/botan/src/lib/misc/fpe_fe1/info.txt
@@ -0,0 +1,10 @@
+<defines>
+FPE_FE1 -> 20131128
+</defines>
+
+<requires>
+bigint
+hmac
+numbertheory
+sha2_32
+</requires>
diff --git a/comm/third_party/botan/src/lib/misc/hotp/hotp.cpp b/comm/third_party/botan/src/lib/misc/hotp/hotp.cpp
new file mode 100644
index 0000000000..b9791bc9b0
--- /dev/null
+++ b/comm/third_party/botan/src/lib/misc/hotp/hotp.cpp
@@ -0,0 +1,63 @@
+/*
+* HOTP
+* (C) 2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/otp.h>
+#include <botan/loadstor.h>
+#include <botan/exceptn.h>
+
+namespace Botan {
+
+HOTP::HOTP(const uint8_t key[], size_t key_len,
+ const std::string& hash_algo, size_t digits)
+ {
+ BOTAN_ARG_CHECK(digits == 6 || digits == 7 || digits == 8, "Invalid HOTP digits");
+
+ if(digits == 6)
+ m_digit_mod = 1000000;
+ else if(digits == 7)
+ m_digit_mod = 10000000;
+ else if(digits == 8)
+ m_digit_mod = 100000000;
+
+ /*
+ RFC 4228 only supports SHA-1 but TOTP allows SHA-256 and SHA-512
+ and some HOTP libs support one or both as extensions
+ */
+ if(hash_algo == "SHA-1")
+ m_mac = MessageAuthenticationCode::create_or_throw("HMAC(SHA-1)");
+ else if(hash_algo == "SHA-256")
+ m_mac = MessageAuthenticationCode::create_or_throw("HMAC(SHA-256)");
+ else if(hash_algo == "SHA-512")
+ m_mac = MessageAuthenticationCode::create_or_throw("HMAC(SHA-512)");
+ else
+ throw Invalid_Argument("Unsupported HOTP hash function");
+
+ m_mac->set_key(key, key_len);
+ }
+
+uint32_t HOTP::generate_hotp(uint64_t counter)
+ {
+ m_mac->update_be(counter);
+ const secure_vector<uint8_t> mac = m_mac->final();
+
+ const size_t offset = mac[mac.size()-1] & 0x0F;
+ const uint32_t code = load_be<uint32_t>(mac.data() + offset, 0) & 0x7FFFFFFF;
+ return code % m_digit_mod;
+ }
+
+std::pair<bool,uint64_t> HOTP::verify_hotp(uint32_t otp, uint64_t starting_counter, size_t resync_range)
+ {
+ for(size_t i = 0; i <= resync_range; ++i)
+ {
+ if(generate_hotp(starting_counter + i) == otp)
+ return std::make_pair(true, starting_counter + i + 1);
+ }
+ return std::make_pair(false, starting_counter);
+ }
+
+}
+
diff --git a/comm/third_party/botan/src/lib/misc/hotp/hotp.h b/comm/third_party/botan/src/lib/misc/hotp/hotp.h
new file mode 100644
index 0000000000..d8c545557e
--- /dev/null
+++ b/comm/third_party/botan/src/lib/misc/hotp/hotp.h
@@ -0,0 +1,14 @@
+/*
+* HOTP
+* (C) 2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_HOTP_H_
+#define BOTAN_HOTP_H_
+
+#include <botan/otp.h>
+BOTAN_DEPRECATED_HEADER(hotp.h)
+
+#endif
diff --git a/comm/third_party/botan/src/lib/misc/hotp/info.txt b/comm/third_party/botan/src/lib/misc/hotp/info.txt
new file mode 100644
index 0000000000..880940c59a
--- /dev/null
+++ b/comm/third_party/botan/src/lib/misc/hotp/info.txt
@@ -0,0 +1,9 @@
+<defines>
+HOTP -> 20180816
+TOTP -> 20180816
+</defines>
+
+<requires>
+hmac
+utils
+</requires>
diff --git a/comm/third_party/botan/src/lib/misc/hotp/otp.h b/comm/third_party/botan/src/lib/misc/hotp/otp.h
new file mode 100644
index 0000000000..664f181f11
--- /dev/null
+++ b/comm/third_party/botan/src/lib/misc/hotp/otp.h
@@ -0,0 +1,117 @@
+/*
+* HOTP/TOTP
+* (C) 2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_ONE_TIME_PASSWORDS_H_
+#define BOTAN_ONE_TIME_PASSWORDS_H_
+
+#include <botan/mac.h>
+#include <chrono>
+
+namespace Botan {
+
+/**
+* HOTP one time passwords (RFC 4226)
+*/
+class BOTAN_PUBLIC_API(2,2) HOTP final
+ {
+ public:
+ /**
+ * @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)
+ */
+ HOTP(const SymmetricKey& key, const std::string& hash_algo = "SHA-1", size_t digits = 6) :
+ HOTP(key.begin(), key.size(), hash_algo, digits) {}
+
+ /**
+ * @param key the secret key shared between client and server
+ * @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)
+ */
+ HOTP(const uint8_t key[], size_t key_len,
+ const std::string& hash_algo = "SHA-1",
+ size_t digits = 6);
+
+ /**
+ * Generate the HOTP for a particular counter value
+ * @warning if the counter value is repeated the OTP ceases to be one-time
+ */
+ uint32_t generate_hotp(uint64_t counter);
+
+ /**
+ * Check an OTP value using a starting counter and a resync range
+ * @param otp the client provided OTP
+ * @param starting_counter the server's guess as to the current counter state
+ * @param resync_range if 0 then only HOTP(starting_counter) is accepted
+ * If larger than 0, up to resync_range values after HOTP are also checked.
+ * @return (valid,next_counter). If the OTP does not validate, always
+ * returns (false,starting_counter). Otherwise returns (true,next_counter)
+ * where next_counter is at most starting_counter + resync_range + 1
+ */
+ std::pair<bool,uint64_t> verify_hotp(uint32_t otp, uint64_t starting_counter, size_t resync_range = 0);
+ private:
+ std::unique_ptr<MessageAuthenticationCode> m_mac;
+ uint32_t m_digit_mod;
+ };
+
+/**
+* TOTP (time based) one time passwords (RFC 6238)
+*/
+class BOTAN_PUBLIC_API(2,2) TOTP final
+ {
+ public:
+ /**
+ * @param key the secret key shared between client and server
+ * @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
+ */
+ TOTP(const SymmetricKey& key,
+ const std::string& hash_algo = "SHA-1",
+ size_t digits = 6, size_t time_step = 30) :
+ TOTP(key.begin(), key.size(), hash_algo, digits, time_step) {}
+
+ /**
+ * @param key the secret key shared between client and server
+ * @param key_len length of key
+ * @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
+ */
+ TOTP(const uint8_t key[], size_t key_len,
+ const std::string& hash_algo = "SHA-1",
+ size_t digits = 6,
+ size_t time_step = 30);
+
+ /**
+ * Convert the provided time_point to a Unix timestamp and call generate_totp
+ */
+ uint32_t generate_totp(std::chrono::system_clock::time_point time_point);
+
+ /**
+ * Generate the OTP corresponding the the provided "Unix timestamp" (ie
+ * number of seconds since midnight Jan 1, 1970)
+ */
+ uint32_t generate_totp(uint64_t unix_time);
+
+ bool verify_totp(uint32_t otp,
+ std::chrono::system_clock::time_point time,
+ size_t clock_drift_accepted = 0);
+
+ bool verify_totp(uint32_t otp, uint64_t unix_time,
+ size_t clock_drift_accepted = 0);
+
+ private:
+ HOTP m_hotp;
+ size_t m_time_step;
+ std::chrono::system_clock::time_point m_unix_epoch;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/misc/hotp/totp.cpp b/comm/third_party/botan/src/lib/misc/hotp/totp.cpp
new file mode 100644
index 0000000000..5e1c23f61e
--- /dev/null
+++ b/comm/third_party/botan/src/lib/misc/hotp/totp.cpp
@@ -0,0 +1,63 @@
+/*
+* TOTP
+* (C) 2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/otp.h>
+#include <botan/calendar.h>
+
+namespace Botan {
+
+TOTP::TOTP(const uint8_t key[], size_t key_len,
+ const std::string& hash_algo,
+ size_t digits, size_t time_step)
+ : m_hotp(key, key_len, hash_algo, digits)
+ , m_time_step(time_step)
+ , m_unix_epoch(calendar_point(1970, 1, 1, 0, 0, 0).to_std_timepoint())
+ {
+ /*
+ * Technically any time step except 0 is valid, but 30 is typical
+ * and over 5 minutes seems unlikely.
+ */
+ BOTAN_ARG_CHECK(m_time_step > 0 && m_time_step < 300, "Invalid TOTP time step");
+ }
+
+uint32_t TOTP::generate_totp(std::chrono::system_clock::time_point current_time)
+ {
+ const uint64_t unix_time =
+ std::chrono::duration_cast<std::chrono::seconds>(current_time - m_unix_epoch).count();
+ return this->generate_totp(unix_time);
+ }
+
+uint32_t TOTP::generate_totp(uint64_t unix_time)
+ {
+ return m_hotp.generate_hotp(unix_time / m_time_step);
+ }
+
+bool TOTP::verify_totp(uint32_t otp, std::chrono::system_clock::time_point current_time,
+ size_t clock_drift_accepted)
+ {
+ const uint64_t unix_time =
+ std::chrono::duration_cast<std::chrono::seconds>(current_time - m_unix_epoch).count();
+ return verify_totp(otp, unix_time, clock_drift_accepted);
+ }
+
+bool TOTP::verify_totp(uint32_t otp, uint64_t unix_time,
+ size_t clock_drift_accepted)
+ {
+ 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)
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/misc/hotp/totp.h b/comm/third_party/botan/src/lib/misc/hotp/totp.h
new file mode 100644
index 0000000000..a5a0831927
--- /dev/null
+++ b/comm/third_party/botan/src/lib/misc/hotp/totp.h
@@ -0,0 +1,13 @@
+/*
+* (C) 2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_TOTP_H_
+#define BOTAN_TOTP_H_
+
+#include <botan/otp.h>
+BOTAN_DEPRECATED_HEADER(totp.h)
+
+#endif
diff --git a/comm/third_party/botan/src/lib/misc/nist_keywrap/info.txt b/comm/third_party/botan/src/lib/misc/nist_keywrap/info.txt
new file mode 100644
index 0000000000..b48e84c118
--- /dev/null
+++ b/comm/third_party/botan/src/lib/misc/nist_keywrap/info.txt
@@ -0,0 +1,7 @@
+<defines>
+NIST_KEYWRAP -> 20171119
+</defines>
+
+<requires>
+block
+</requires>
diff --git a/comm/third_party/botan/src/lib/misc/nist_keywrap/nist_keywrap.cpp b/comm/third_party/botan/src/lib/misc/nist_keywrap/nist_keywrap.cpp
new file mode 100644
index 0000000000..671d31ea4d
--- /dev/null
+++ b/comm/third_party/botan/src/lib/misc/nist_keywrap/nist_keywrap.cpp
@@ -0,0 +1,209 @@
+/*
+* (C) 2011,2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/nist_keywrap.h>
+#include <botan/block_cipher.h>
+#include <botan/loadstor.h>
+#include <botan/exceptn.h>
+
+namespace Botan {
+
+namespace {
+
+std::vector<uint8_t>
+raw_nist_key_wrap(const uint8_t input[],
+ size_t input_len,
+ const BlockCipher& bc,
+ uint64_t ICV)
+ {
+ const size_t n = (input_len + 7) / 8;
+
+ secure_vector<uint8_t> R((n + 1) * 8);
+ secure_vector<uint8_t> A(16);
+
+ store_be(ICV, A.data());
+
+ copy_mem(&R[8], input, input_len);
+
+ for(size_t j = 0; j <= 5; ++j)
+ {
+ for(size_t i = 1; i <= n; ++i)
+ {
+ const uint32_t t = static_cast<uint32_t>((n * j) + i);
+
+ copy_mem(&A[8], &R[8*i], 8);
+
+ bc.encrypt(A.data());
+ copy_mem(&R[8*i], &A[8], 8);
+
+ uint8_t t_buf[4] = { 0 };
+ store_be(t, t_buf);
+ xor_buf(&A[4], t_buf, 4);
+ }
+ }
+
+ copy_mem(R.data(), A.data(), 8);
+
+ return std::vector<uint8_t>(R.begin(), R.end());
+ }
+
+secure_vector<uint8_t>
+raw_nist_key_unwrap(const uint8_t input[],
+ size_t input_len,
+ const BlockCipher& bc,
+ uint64_t& ICV_out)
+ {
+ if(input_len < 16 || input_len % 8 != 0)
+ throw Invalid_Argument("Bad input size for NIST key unwrap");
+
+ const size_t n = (input_len - 8) / 8;
+
+ secure_vector<uint8_t> R(n * 8);
+ secure_vector<uint8_t> A(16);
+
+ for(size_t i = 0; i != 8; ++i)
+ A[i] = input[i];
+
+ copy_mem(R.data(), input + 8, input_len - 8);
+
+ for(size_t j = 0; j <= 5; ++j)
+ {
+ for(size_t i = n; i != 0; --i)
+ {
+ const uint32_t t = static_cast<uint32_t>((5 - j) * n + i);
+
+ uint8_t t_buf[4] = { 0 };
+ store_be(t, t_buf);
+
+ xor_buf(&A[4], t_buf, 4);
+
+ copy_mem(&A[8], &R[8*(i-1)], 8);
+
+ bc.decrypt(A.data());
+
+ copy_mem(&R[8*(i-1)], &A[8], 8);
+ }
+ }
+
+ ICV_out = load_be<uint64_t>(A.data(), 0);
+
+ return R;
+ }
+
+}
+
+std::vector<uint8_t>
+nist_key_wrap(const uint8_t input[],
+ size_t input_len,
+ const BlockCipher& bc)
+ {
+ if(bc.block_size() != 16)
+ throw Invalid_Argument("NIST key wrap algorithm requires a 128-bit cipher");
+
+ if(input_len % 8 != 0)
+ throw Invalid_Argument("Bad input size for NIST key wrap");
+
+ return raw_nist_key_wrap(input, input_len, bc, 0xA6A6A6A6A6A6A6A6);
+ }
+
+secure_vector<uint8_t>
+nist_key_unwrap(const uint8_t input[],
+ size_t input_len,
+ const BlockCipher& bc)
+ {
+ if(bc.block_size() != 16)
+ throw Invalid_Argument("NIST key wrap algorithm requires a 128-bit cipher");
+
+ if(input_len < 16 || input_len % 8 != 0)
+ throw Invalid_Argument("Bad input size for NIST key unwrap");
+
+ uint64_t ICV_out = 0;
+
+ secure_vector<uint8_t> R = raw_nist_key_unwrap(input, input_len, bc, ICV_out);
+
+ if(ICV_out != 0xA6A6A6A6A6A6A6A6)
+ throw Invalid_Authentication_Tag("NIST key unwrap failed");
+
+ return R;
+ }
+
+std::vector<uint8_t>
+nist_key_wrap_padded(const uint8_t input[],
+ size_t input_len,
+ const BlockCipher& bc)
+ {
+ if(bc.block_size() != 16)
+ throw Invalid_Argument("NIST key wrap algorithm requires a 128-bit cipher");
+
+ const uint64_t ICV = 0xA65959A600000000 | static_cast<uint32_t>(input_len);
+
+ if(input_len <= 8)
+ {
+ /*
+ * Special case for small inputs: if input <= 8 bytes just use ECB
+ */
+ std::vector<uint8_t> block(16);
+ store_be(ICV, block.data());
+ copy_mem(block.data() + 8, input, input_len);
+ bc.encrypt(block);
+ return block;
+ }
+ else
+ {
+ return raw_nist_key_wrap(input, input_len, bc, ICV);
+ }
+ }
+
+secure_vector<uint8_t>
+nist_key_unwrap_padded(const uint8_t input[],
+ size_t input_len,
+ const BlockCipher& bc)
+ {
+ if(bc.block_size() != 16)
+ throw Invalid_Argument("NIST key wrap algorithm requires a 128-bit cipher");
+
+ if(input_len < 16 || input_len % 8 != 0)
+ throw Invalid_Argument("Bad input size for NIST key unwrap");
+
+ uint64_t ICV_out = 0;
+ secure_vector<uint8_t> R;
+
+ if(input_len == 16)
+ {
+ secure_vector<uint8_t> block(input, input + input_len);
+ bc.decrypt(block);
+
+ ICV_out = load_be<uint64_t>(block.data(), 0);
+ R.resize(8);
+ copy_mem(R.data(), block.data() + 8, 8);
+ }
+ else
+ {
+ R = raw_nist_key_unwrap(input, input_len, bc, ICV_out);
+ }
+
+ if((ICV_out >> 32) != 0xA65959A6)
+ throw Invalid_Authentication_Tag("NIST key unwrap failed");
+
+ const size_t len = (ICV_out & 0xFFFFFFFF);
+
+ if(R.size() < 8 || len > R.size() || len < R.size() - 8)
+ 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");
+ }
+
+ R.resize(R.size() - padding);
+
+ return R;
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/misc/nist_keywrap/nist_keywrap.h b/comm/third_party/botan/src/lib/misc/nist_keywrap/nist_keywrap.h
new file mode 100644
index 0000000000..022b4016f5
--- /dev/null
+++ b/comm/third_party/botan/src/lib/misc/nist_keywrap/nist_keywrap.h
@@ -0,0 +1,67 @@
+/*
+* (C) 2011,2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_NIST_KEY_WRAP_H_
+#define BOTAN_NIST_KEY_WRAP_H_
+
+#include <botan/secmem.h>
+
+namespace Botan {
+
+class BlockCipher;
+
+/**
+* Key wrap. See RFC 3394 and NIST SP800-38F
+* @param input the value to be encrypted
+* @param input_len length of input, must be a multiple of 8
+* @param bc a keyed 128-bit block cipher that will be used to encrypt input
+* @return input encrypted under NIST key wrap algorithm
+*/
+std::vector<uint8_t> BOTAN_PUBLIC_API(2,4)
+nist_key_wrap(const uint8_t input[],
+ size_t input_len,
+ const BlockCipher& 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
+* @return input decrypted under NIST key wrap algorithm
+* Throws an exception if decryption fails.
+*/
+secure_vector<uint8_t> BOTAN_PUBLIC_API(2,4)
+nist_key_unwrap(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 input_len length of input
+* @param bc a keyed 128-bit block cipher that will be used to encrypt input
+* @return input encrypted under NIST key wrap algorithm
+*/
+std::vector<uint8_t> BOTAN_PUBLIC_API(2,4)
+nist_key_wrap_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 input_len length of input
+* @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.
+*/
+secure_vector<uint8_t> BOTAN_PUBLIC_API(2,4)
+nist_key_unwrap_padded(const uint8_t input[],
+ size_t input_len,
+ const BlockCipher& bc);
+
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/misc/rfc3394/info.txt b/comm/third_party/botan/src/lib/misc/rfc3394/info.txt
new file mode 100644
index 0000000000..075834219a
--- /dev/null
+++ b/comm/third_party/botan/src/lib/misc/rfc3394/info.txt
@@ -0,0 +1,8 @@
+<defines>
+RFC3394_KEYWRAP -> 20131128
+</defines>
+
+<requires>
+aes
+nist_keywrap
+</requires>
diff --git a/comm/third_party/botan/src/lib/misc/rfc3394/rfc3394.cpp b/comm/third_party/botan/src/lib/misc/rfc3394/rfc3394.cpp
new file mode 100644
index 0000000000..cb24809984
--- /dev/null
+++ b/comm/third_party/botan/src/lib/misc/rfc3394/rfc3394.cpp
@@ -0,0 +1,44 @@
+/*
+* AES Key Wrap (RFC 3394)
+* (C) 2011 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/rfc3394.h>
+#include <botan/nist_keywrap.h>
+#include <botan/block_cipher.h>
+
+namespace Botan {
+
+secure_vector<uint8_t> rfc3394_keywrap(const secure_vector<uint8_t>& key,
+ const SymmetricKey& kek)
+ {
+ BOTAN_ARG_CHECK(kek.size() == 16 || kek.size() == 24 || kek.size() == 32,
+ "Invalid KEK length for NIST key wrap");
+
+ const std::string cipher_name = "AES-" + std::to_string(8*kek.size());
+ std::unique_ptr<BlockCipher> aes(BlockCipher::create_or_throw(cipher_name));
+ aes->set_key(kek);
+
+ std::vector<uint8_t> wrapped = nist_key_wrap(key.data(), key.size(), *aes);
+ return secure_vector<uint8_t>(wrapped.begin(), wrapped.end());
+ }
+
+secure_vector<uint8_t> rfc3394_keyunwrap(const secure_vector<uint8_t>& key,
+ const SymmetricKey& kek)
+ {
+ BOTAN_ARG_CHECK(kek.size() == 16 || kek.size() == 24 || kek.size() == 32,
+ "Invalid KEK length for NIST key wrap");
+
+ BOTAN_ARG_CHECK(key.size() >= 16 && key.size() % 8 == 0,
+ "Bad input key size for NIST key unwrap");
+
+ const std::string cipher_name = "AES-" + std::to_string(8*kek.size());
+ std::unique_ptr<BlockCipher> aes(BlockCipher::create_or_throw(cipher_name));
+ aes->set_key(kek);
+
+ return nist_key_unwrap(key.data(), key.size(), *aes);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/misc/rfc3394/rfc3394.h b/comm/third_party/botan/src/lib/misc/rfc3394/rfc3394.h
new file mode 100644
index 0000000000..9cfcfaaf6d
--- /dev/null
+++ b/comm/third_party/botan/src/lib/misc/rfc3394/rfc3394.h
@@ -0,0 +1,39 @@
+/*
+* AES Key Wrap (RFC 3394)
+* (C) 2011 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_RFC3394_H_
+#define BOTAN_RFC3394_H_
+
+#include <botan/symkey.h>
+
+namespace Botan {
+
+/**
+* Encrypt a key under a key encryption key using the algorithm
+* described in RFC 3394
+*
+* @param key the plaintext key to encrypt
+* @param kek the key encryption key
+* @return key encrypted under kek
+*/
+secure_vector<uint8_t> BOTAN_PUBLIC_API(2,0) rfc3394_keywrap(const secure_vector<uint8_t>& key,
+ const SymmetricKey& kek);
+
+/**
+* Decrypt a key under a key encryption key using the algorithm
+* described in RFC 3394
+*
+* @param key the encrypted key to decrypt
+* @param kek the key encryption key
+* @return key decrypted under kek
+*/
+secure_vector<uint8_t> BOTAN_PUBLIC_API(2,0) rfc3394_keyunwrap(const secure_vector<uint8_t>& key,
+ const SymmetricKey& kek);
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/misc/roughtime/info.txt b/comm/third_party/botan/src/lib/misc/roughtime/info.txt
new file mode 100644
index 0000000000..560f526660
--- /dev/null
+++ b/comm/third_party/botan/src/lib/misc/roughtime/info.txt
@@ -0,0 +1,10 @@
+<defines>
+ROUGHTIME -> 20190220
+</defines>
+
+<requires>
+ed25519
+rng
+sha2_64
+socket
+</requires>
diff --git a/comm/third_party/botan/src/lib/misc/roughtime/roughtime.cpp b/comm/third_party/botan/src/lib/misc/roughtime/roughtime.cpp
new file mode 100644
index 0000000000..384d40626e
--- /dev/null
+++ b/comm/third_party/botan/src/lib/misc/roughtime/roughtime.cpp
@@ -0,0 +1,466 @@
+/*
+* Roughtime
+* (C) 2019 Nuno Goncalves <nunojpg@gmail.com>
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/roughtime.h>
+
+#include <botan/base64.h>
+#include <botan/hash.h>
+#include <botan/internal/socket_udp.h>
+#include <botan/pubkey.h>
+#include <botan/rng.h>
+
+#include <cmath>
+#include <map>
+#include <sstream>
+
+namespace Botan {
+
+namespace {
+
+// This exists to work around a LGTM false positive
+static_assert(Roughtime::request_min_size == 1024, "Expected minimum size");
+
+template< bool B, class T = void >
+using enable_if_t = typename std::enable_if<B,T>::type;
+
+template<class T>
+struct is_array : std::false_type {};
+
+template<class T, std::size_t N>
+struct is_array<std::array<T,N>>:std::true_type{};
+
+template<typename T>
+T impl_from_little_endian(const uint8_t* t, const size_t i)
+ {
+ static_assert(sizeof(T) <= sizeof(int64_t), "");
+ return T(static_cast<int64_t>(t[i]) << i * 8) + (i == 0 ? T(0) : impl_from_little_endian<T>(t, i - 1));
+ }
+
+template<typename T>
+T from_little_endian(const uint8_t* t)
+ {
+ return impl_from_little_endian<T>(t, sizeof(T) - 1);
+ }
+
+template<typename T, enable_if_t<is_array<T>::value>* = nullptr>
+T copy(const uint8_t* t)
+ {
+ return typecast_copy<T>(t); //arrays are endianess indepedent, so we do a memcpy
+ }
+
+template<typename T, enable_if_t<!is_array<T>::value>* = nullptr>
+T copy(const uint8_t* t)
+ {
+ return from_little_endian<T>(t); //other types are arithmetic, so we account that roughtime serializes as little endian
+ }
+
+template<typename T>
+std::map<std::string, std::vector<uint8_t>> unpack_roughtime_packet(T bytes)
+ {
+ if(bytes.size() < 8)
+ { throw Roughtime::Roughtime_Error("Map length is under minimum of 8 bytes"); }
+ const auto buf = bytes.data();
+ const uint32_t num_tags = buf[0];
+ const uint32_t start_content = num_tags * 8;
+ if(start_content > bytes.size())
+ { throw Roughtime::Roughtime_Error("Map length too small to contain all tags"); }
+ uint32_t start = start_content;
+ std::map<std::string, std::vector<uint8_t>> tags;
+ for(uint32_t i=0; i<num_tags; ++i)
+ {
+ const size_t end = ((i+1) == num_tags) ? bytes.size() : start_content + from_little_endian<uint32_t>(buf + 4 + i*4);
+ if(end > bytes.size())
+ { throw Roughtime::Roughtime_Error("Tag end index out of bounds"); }
+ if(end < start)
+ { throw Roughtime::Roughtime_Error("Tag offset must be more than previous tag offset"); }
+ const char* label_ptr = cast_uint8_ptr_to_char(buf) + (num_tags+i)*4;
+ const char label[] = {label_ptr[0], label_ptr[1], label_ptr[2], label_ptr[3], 0};
+ auto ret = tags.emplace(label, std::vector<uint8_t>(buf+start, buf+end));
+ if(!ret.second)
+ { throw Roughtime::Roughtime_Error(std::string("Map has duplicated tag: ") + label); }
+ start = static_cast<uint32_t>(end);
+ }
+ return tags;
+ }
+
+template<typename T>
+T get(const std::map<std::string, std::vector<uint8_t>>& map, const std::string& label)
+ {
+ const auto& tag = map.find(label);
+ if(tag == map.end())
+ { throw Roughtime::Roughtime_Error("Tag " + label + " not found"); }
+ if(tag->second.size() != sizeof(T))
+ { throw Roughtime::Roughtime_Error("Tag " + label + " has unexpected size"); }
+ return copy<T>(tag->second.data());
+ }
+
+const std::vector<uint8_t>& get_v(const std::map<std::string, std::vector<uint8_t>>& map, const std::string& label)
+ {
+ const auto& tag = map.find(label);
+ if(tag == map.end())
+ { throw Roughtime::Roughtime_Error("Tag " + label + " not found"); }
+ return tag->second;
+ }
+
+bool verify_signature(const std::array<uint8_t, 32>& pk, const std::vector<uint8_t>& payload,
+ const std::array<uint8_t, 64>& signature)
+ {
+ const char context[] = "RoughTime v1 response signature";
+ Ed25519_PublicKey key(std::vector<uint8_t>(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(payload);
+ return verifier.check_signature(signature.data(), signature.size());
+ }
+
+std::array<uint8_t, 64> hashLeaf(const std::array<uint8_t, 64>& leaf)
+ {
+ std::array<uint8_t, 64> ret;
+ std::unique_ptr<HashFunction> hash(HashFunction::create_or_throw("SHA-512"));
+ hash->update(0);
+ hash->update(leaf.data(), leaf.size());
+ hash->final(ret.data());
+ return ret;
+ }
+
+void hashNode(std::array<uint8_t, 64>& hash, const std::array<uint8_t, 64>& node, bool reverse)
+ {
+ std::unique_ptr<HashFunction> h(HashFunction::create_or_throw("SHA-512"));
+ h->update(1);
+ if(reverse)
+ {
+ h->update(node.data(), node.size());
+ h->update(hash.data(), hash.size());
+ }
+ else
+ {
+ h->update(hash.data(), hash.size());
+ h->update(node.data(), node.size());
+ }
+ h->final(hash.data());
+ }
+
+template<size_t N, typename T>
+std::array<uint8_t, N> vector_to_array(std::vector<uint8_t,T> vec)
+ {
+ if(vec.size() != N)
+ { throw std::logic_error("Invalid vector size"); }
+ return typecast_copy<std::array<uint8_t, N>>(vec.data());
+ }
+}
+
+namespace Roughtime {
+
+Nonce::Nonce(const std::vector<uint8_t>& nonce)
+ {
+ if(nonce.size() != 64)
+ { throw Invalid_Argument("Nonce lenght must be 64"); }
+ m_nonce = typecast_copy<std::array<uint8_t, 64>>(nonce.data());
+ }
+Nonce::Nonce(RandomNumberGenerator& rng)
+ {
+ rng.randomize(m_nonce.data(), m_nonce.size());
+ }
+
+std::array<uint8_t, request_min_size> encode_request(const Nonce& nonce)
+ {
+ std::array<uint8_t, request_min_size> buf = {{2, 0, 0, 0, 64, 0, 0, 0, 'N', 'O', 'N', 'C', 'P', 'A', 'D', 0xff}};
+ std::memcpy(buf.data() + 16, nonce.get_nonce().data(), nonce.get_nonce().size());
+ std::memset(buf.data() + 16 + nonce.get_nonce().size(), 0, buf.size() - 16 - nonce.get_nonce().size());
+ return buf;
+ }
+
+Response Response::from_bits(const std::vector<uint8_t>& response,
+ const Nonce& nonce)
+ {
+ const auto response_v = unpack_roughtime_packet(response);
+ const auto cert = unpack_roughtime_packet(get_v(response_v, "CERT"));
+ const auto cert_dele = get<std::array<uint8_t, 72>>(cert, "DELE");
+ const auto cert_sig = get<std::array<uint8_t, 64>>(cert, "SIG");
+ const auto cert_dele_v = unpack_roughtime_packet(cert_dele);
+ const auto srep = get_v(response_v, "SREP");
+ const auto srep_v = unpack_roughtime_packet(srep);
+
+ const auto cert_dele_pubk = get<std::array<uint8_t, 32>>(cert_dele_v, "PUBK");
+ const auto sig = get<std::array<uint8_t, 64>>(response_v, "SIG");
+ if(!verify_signature(cert_dele_pubk, srep, sig))
+ { throw Roughtime_Error("Response signature invalid"); }
+
+ const auto indx = get<uint32_t>(response_v, "INDX");
+ const auto path = get_v(response_v, "PATH");
+ const auto srep_root = get<std::array<uint8_t, 64>>(srep_v, "ROOT");
+ const auto size = path.size();
+ const auto levels = size/64;
+
+ if(size % 64)
+ { throw Roughtime_Error("Merkle tree path size must be multiple of 64 bytes"); }
+ if(indx >= (1u << levels))
+ { throw Roughtime_Error("Merkle tree path is too short"); }
+
+ auto hash = hashLeaf(nonce.get_nonce());
+ auto index = indx;
+ auto level = 0u;
+ while(level < levels)
+ {
+ hashNode(hash, typecast_copy<std::array<uint8_t, 64>>(path.data() + level*64), index&1);
+ ++level;
+ index>>=1;
+ }
+
+ if(srep_root != hash)
+ { throw Roughtime_Error("Nonce verification failed"); }
+
+ const auto cert_dele_maxt = sys_microseconds64(get<microseconds64>(cert_dele_v, "MAXT"));
+ const auto cert_dele_mint = sys_microseconds64(get<microseconds64>(cert_dele_v, "MINT"));
+ const auto srep_midp = sys_microseconds64(get<microseconds64>(srep_v, "MIDP"));
+ const auto srep_radi = get<microseconds32>(srep_v, "RADI");
+ if(srep_midp < cert_dele_mint)
+ { throw Roughtime_Error("Midpoint earlier than delegation start"); }
+ if(srep_midp > cert_dele_maxt)
+ { throw Roughtime_Error("Midpoint later than delegation end"); }
+ return {cert_dele, cert_sig, srep_midp, srep_radi};
+ }
+
+bool Response::validate(const Ed25519_PublicKey& pk) const
+ {
+ const char context[] = "RoughTime v1 delegation signature--";
+ PK_Verifier verifier(pk, "Pure");
+ verifier.update(cast_char_ptr_to_uint8(context), sizeof(context)); //add context including \0
+ verifier.update(m_cert_dele.data(), m_cert_dele.size());
+ return verifier.check_signature(m_cert_sig.data(), m_cert_sig.size());
+ }
+
+Nonce nonce_from_blind(const std::vector<uint8_t>& previous_response,
+ const Nonce& blind)
+ {
+ std::array<uint8_t, 64> ret;
+ const auto blind_arr = blind.get_nonce();
+ std::unique_ptr<Botan::HashFunction> hash(Botan::HashFunction::create_or_throw("SHA-512"));
+ hash->update(previous_response);
+ hash->update(hash->final());
+ hash->update(blind_arr.data(), blind_arr.size());
+ hash->final(ret.data());
+
+ return ret;
+ }
+
+Chain::Chain(const std::string& str)
+ {
+ std::stringstream ss(str);
+ 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;
+ end = s.find(' ', start);
+ if(end == std::string::npos)
+ {
+ throw Decoding_Error(ERROR_MESSAGE);
+ }
+ const auto publicKeyType = s.substr(start, end-start);
+ if(publicKeyType != "ed25519")
+ { throw Not_Implemented("Only ed25519 publicKeyType is implemented"); }
+
+ start = end + 1;
+ end = s.find(' ', start);
+ if(end == std::string::npos)
+ {
+ throw Decoding_Error(ERROR_MESSAGE);
+ }
+ const auto serverPublicKey = Botan::Ed25519_PublicKey(Botan::base64_decode(s.substr(start, end-start)));
+
+ start = end + 1;
+ end = s.find(' ', start);
+ if(end == std::string::npos)
+ {
+ throw Decoding_Error(ERROR_MESSAGE);
+ }
+ if((end - start) != 88)
+ {
+ throw Decoding_Error("Nonce has invalid length");
+ }
+ const auto vec = Botan::base64_decode(s.substr(start, end-start));
+ const auto nonceOrBlind = Nonce(vector_to_array<64>(Botan::base64_decode(s.substr(start, end-start))));
+
+ start = end + 1;
+ end = s.find(' ', start);
+ if(end != std::string::npos)
+ {
+ throw Decoding_Error(ERROR_MESSAGE);
+ }
+ const auto response = Botan::unlock(Botan::base64_decode(s.substr(start)));
+
+ m_links.push_back({response, serverPublicKey, nonceOrBlind});
+ }
+ }
+std::vector<Response> Chain::responses() const
+ {
+ std::vector<Response> responses;
+ for(unsigned 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 response = Response::from_bits(l.response(), nonce);
+ if(!response.validate(l.public_key()))
+ { throw Roughtime_Error("Invalid signature or public key"); }
+ responses.push_back(response);
+ }
+ return responses;
+ }
+Nonce Chain::next_nonce(const Nonce& blind) const
+ {
+ return m_links.empty()
+ ? blind
+ : nonce_from_blind(m_links.back().response(), blind);
+ }
+void Chain::append(const Link& new_link, size_t max_chain_size)
+ {
+ if(max_chain_size <= 0)
+ { throw Invalid_Argument("Max chain size must be positive"); }
+
+ while(m_links.size() >= max_chain_size)
+ {
+ if(m_links.size() == 1)
+ {
+ auto new_link_updated = new_link;
+ new_link_updated.nonce_or_blind() =
+ nonce_from_blind(m_links[0].response(), new_link.nonce_or_blind()); //we need to convert blind to nonce
+ m_links.clear();
+ m_links.push_back(new_link_updated);
+ return;
+ }
+ if(m_links.size() >= 2)
+ {
+ m_links[1].nonce_or_blind() =
+ nonce_from_blind(m_links[0].response(), m_links[1].nonce_or_blind()); //we need to convert blind to nonce
+ }
+ m_links.erase(m_links.begin());
+ }
+ m_links.push_back(new_link);
+ }
+
+std::string Chain::to_string() const
+ {
+ std::string s;
+ s.reserve((7+1 + 88+1 + 44+1 + 480)*m_links.size());
+ for(const auto& link : m_links)
+ {
+ s += "ed25519";
+ s += ' ';
+ s += Botan::base64_encode(link.public_key().get_public_key());
+ s += ' ';
+ s += Botan::base64_encode(link.nonce_or_blind().get_nonce().data(), link.nonce_or_blind().get_nonce().size());
+ s += ' ';
+ s += Botan::base64_encode(link.response());
+ s += '\n';
+ }
+ return s;
+ }
+
+std::vector<uint8_t> online_request(const std::string& uri,
+ const Nonce& nonce,
+ std::chrono::milliseconds timeout)
+ {
+ const std::chrono::system_clock::time_point start_time = std::chrono::system_clock::now();
+ auto socket = OS::open_socket_udp(uri, timeout);
+ if(!socket)
+ { throw Not_Implemented("No socket support enabled in build"); }
+
+ const auto encoded = encode_request(nonce);
+ socket->write(encoded.data(), encoded.size());
+
+ if(std::chrono::system_clock::now() - start_time > timeout)
+ { throw System_Error("Timeout during socket write"); }
+
+ std::vector<uint8_t> buffer;
+ buffer.resize(360+64*10+1); //response basic size is 360 bytes + 64 bytes for each level of merkle tree
+ //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)
+ { throw System_Error("Timeout waiting for response"); }
+
+ if(n == buffer.size())
+ { throw System_Error("Buffer too small"); }
+
+ buffer.resize(n);
+ return buffer;
+ }
+
+std::vector<Server_Information> servers_from_str(const std::string& str)
+ {
+ std::vector<Server_Information> servers;
+ std::stringstream ss(str);
+ 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;
+ end = s.find(' ', start);
+ if(end == std::string::npos)
+ {
+ throw Decoding_Error(ERROR_MESSAGE);
+ }
+ const auto name = s.substr(start, end-start);
+
+ start = end + 1;
+ end = s.find(' ', start);
+ if(end == std::string::npos)
+ {
+ throw Decoding_Error(ERROR_MESSAGE);
+ }
+ const auto publicKeyType = s.substr(start, end-start);
+ if(publicKeyType != "ed25519")
+ { throw Not_Implemented("Only ed25519 publicKeyType is implemented"); }
+
+ start = end + 1;
+ end = s.find(' ', start);
+
+ if(end == std::string::npos)
+ {
+ throw Decoding_Error(ERROR_MESSAGE);
+ }
+ const auto publicKeyBase64 = s.substr(start, end-start);
+ const auto publicKey = Botan::Ed25519_PublicKey(Botan::base64_decode(publicKeyBase64));
+
+ start = end + 1;
+ end = s.find(' ', start);
+ if(end == std::string::npos)
+ {
+ throw Decoding_Error(ERROR_MESSAGE);
+ }
+ const auto protocol = s.substr(start, end-start);
+ if(protocol != "udp")
+ { throw Not_Implemented("Only UDP protocol is implemented"); }
+
+ const auto addresses = [&]()
+ {
+ std::vector<std::string> addr;
+ for(;;)
+ {
+ start = end + 1;
+ end = s.find(' ', start);
+ const auto address = s.substr(start, (end == std::string::npos) ? std::string::npos : end-start);
+ if(address.empty())
+ { return addr; }
+ addr.push_back(address);
+ if(end == std::string::npos)
+ { return addr; }
+ }
+ }
+ ();
+ if(addresses.size() == 0)
+ {
+ throw Decoding_Error(ERROR_MESSAGE);
+ }
+
+ servers.push_back({name, publicKey, std::move(addresses)});
+ }
+ return servers;
+ }
+
+}
+
+}
diff --git a/comm/third_party/botan/src/lib/misc/roughtime/roughtime.h b/comm/third_party/botan/src/lib/misc/roughtime/roughtime.h
new file mode 100644
index 0000000000..e52be25a16
--- /dev/null
+++ b/comm/third_party/botan/src/lib/misc/roughtime/roughtime.h
@@ -0,0 +1,167 @@
+/*
+* Roughtime
+* (C) 2019 Nuno Goncalves <nunojpg@gmail.com>
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_ROUGHTIME_H_
+#define BOTAN_ROUGHTIME_H_
+
+#include <array>
+#include <chrono>
+#include <vector>
+
+#include <botan/ed25519.h>
+
+namespace Botan {
+
+class RandomNumberGenerator;
+
+namespace Roughtime {
+
+const unsigned request_min_size = 1024;
+
+class BOTAN_PUBLIC_API(2, 13) Roughtime_Error final : public Decoding_Error
+ {
+ public:
+ explicit Roughtime_Error(const std::string& s) : Decoding_Error("Roughtime " + s) {}
+ ErrorType error_type() const noexcept override { return ErrorType::RoughtimeError; }
+ };
+
+class BOTAN_PUBLIC_API(2, 13) Nonce final
+ {
+ public:
+ Nonce() = default;
+ Nonce(const std::vector<uint8_t>& nonce);
+ Nonce(RandomNumberGenerator& rng);
+ Nonce(const std::array<uint8_t, 64>& nonce)
+ {
+ m_nonce = nonce;
+ }
+ bool operator==(const Nonce& rhs) const { return m_nonce == rhs.m_nonce; }
+ const std::array<uint8_t, 64>& get_nonce() const { return m_nonce; }
+ private:
+ std::array<uint8_t, 64> m_nonce;
+ };
+
+
+/**
+* An Roughtime request.
+*/
+BOTAN_PUBLIC_API(2, 13)
+std::array<uint8_t, request_min_size> encode_request(const Nonce& nonce);
+
+/**
+* An Roughtime response.
+*/
+class BOTAN_PUBLIC_API(2, 13) Response final
+ {
+ public:
+ using microseconds32 = std::chrono::duration<uint32_t, std::micro>;
+ using microseconds64 = std::chrono::duration<uint64_t, std::micro>;
+ using sys_microseconds64 = std::chrono::time_point<std::chrono::system_clock, microseconds64>;
+
+ static Response from_bits(const std::vector<uint8_t>& response, const Nonce& nonce);
+
+ bool validate(const Ed25519_PublicKey& pk) const;
+
+ sys_microseconds64 utc_midpoint() const { return m_utc_midpoint; }
+
+ microseconds32 utc_radius() const { return m_utc_radius; }
+ private:
+ Response(const std::array<uint8_t, 72>& dele,
+ const std::array<uint8_t, 64>& sig,
+ sys_microseconds64 utc_midp,
+ microseconds32 utc_radius)
+ : m_cert_dele(dele)
+ , m_cert_sig(sig)
+ , m_utc_midpoint {utc_midp}
+ , m_utc_radius {utc_radius}
+ {}
+ const std::array<uint8_t, 72> m_cert_dele;
+ const std::array<uint8_t, 64> m_cert_sig;
+ const sys_microseconds64 m_utc_midpoint;
+ const microseconds32 m_utc_radius;
+ };
+
+class BOTAN_PUBLIC_API(2, 13) Link final
+ {
+ public:
+ Link(const std::vector<uint8_t>& response,
+ const Ed25519_PublicKey& public_key,
+ const Nonce& nonce_or_blind)
+ : m_response{response}
+ , m_public_key{public_key}
+ , m_nonce_or_blind{nonce_or_blind}
+ {}
+ const std::vector<uint8_t>& response() const { return m_response; }
+ const Ed25519_PublicKey& public_key() const { return m_public_key; }
+ const Nonce& nonce_or_blind() const { return m_nonce_or_blind; }
+ Nonce& nonce_or_blind() { return m_nonce_or_blind; }
+
+ private:
+ std::vector<uint8_t> m_response;
+ Ed25519_PublicKey m_public_key;
+ Nonce m_nonce_or_blind;
+ };
+
+class BOTAN_PUBLIC_API(2, 13) Chain final
+ {
+ public:
+ Chain() = default; //empty
+ Chain(const std::string& str);
+ const std::vector<Link>& links() const { return m_links; }
+ std::vector<Response> responses() const;
+ Nonce next_nonce(const Nonce& blind) const;
+ void append(const Link& new_link, size_t max_chain_size);
+ std::string to_string() const;
+ private:
+ std::vector<Link> m_links;
+ };
+
+/**
+*/
+BOTAN_PUBLIC_API(2, 13)
+Nonce nonce_from_blind(const std::vector<uint8_t>& previous_response,
+ const Nonce& blind);
+
+/**
+* Makes an online Roughtime request via UDP and returns the Roughtime response.
+* @param url Roughtime server UDP endpoint (host:port)
+* @param nonce the nonce to send to the server
+* @param timeout a timeout on the UDP request
+* @return Roughtime response
+*/
+BOTAN_PUBLIC_API(2, 13)
+std::vector<uint8_t> online_request(const std::string& url,
+ const Nonce& nonce,
+ std::chrono::milliseconds timeout = std::chrono::seconds(3));
+
+struct BOTAN_PUBLIC_API(2, 13) Server_Information final
+ {
+public:
+ Server_Information(const std::string& name,
+ const Botan::Ed25519_PublicKey& public_key,
+ const std::vector<std::string>& addresses)
+ : m_name { name }
+ , m_public_key { public_key }
+ , m_addresses { addresses }
+ {}
+ const std::string& name() const {return m_name;}
+ const Botan::Ed25519_PublicKey& public_key() const {return m_public_key;}
+ const std::vector<std::string>& addresses() const {return m_addresses;}
+
+private:
+ std::string m_name;
+ Botan::Ed25519_PublicKey m_public_key;
+ std::vector<std::string> m_addresses;
+ };
+
+BOTAN_PUBLIC_API(2, 13)
+std::vector<Server_Information> servers_from_str(const std::string& str);
+
+}
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/misc/srp6/info.txt b/comm/third_party/botan/src/lib/misc/srp6/info.txt
new file mode 100644
index 0000000000..27b7848f5b
--- /dev/null
+++ b/comm/third_party/botan/src/lib/misc/srp6/info.txt
@@ -0,0 +1,8 @@
+<defines>
+SRP6 -> 20161017
+</defines>
+
+<requires>
+bigint
+dl_group
+</requires>
diff --git a/comm/third_party/botan/src/lib/misc/srp6/srp6.cpp b/comm/third_party/botan/src/lib/misc/srp6/srp6.cpp
new file mode 100644
index 0000000000..0bd9b192ae
--- /dev/null
+++ b/comm/third_party/botan/src/lib/misc/srp6/srp6.cpp
@@ -0,0 +1,193 @@
+/*
+* SRP-6a (RFC 5054 compatatible)
+* (C) 2011,2012,2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/srp6.h>
+#include <botan/hash.h>
+#include <botan/dl_group.h>
+#include <botan/numthry.h>
+
+namespace Botan {
+
+namespace {
+
+BigInt hash_seq(const std::string& hash_id,
+ size_t pad_to,
+ const BigInt& in1,
+ const BigInt& in2)
+ {
+ std::unique_ptr<HashFunction> hash_fn(HashFunction::create_or_throw(hash_id));
+
+ hash_fn->update(BigInt::encode_1363(in1, pad_to));
+ hash_fn->update(BigInt::encode_1363(in2, pad_to));
+
+ return BigInt::decode(hash_fn->final());
+ }
+
+BigInt compute_x(const std::string& hash_id,
+ const std::string& identifier,
+ const std::string& password,
+ const std::vector<uint8_t>& salt)
+ {
+ std::unique_ptr<HashFunction> hash_fn(HashFunction::create_or_throw(hash_id));
+
+ hash_fn->update(identifier);
+ hash_fn->update(":");
+ hash_fn->update(password);
+
+ secure_vector<uint8_t> inner_h = hash_fn->final();
+
+ hash_fn->update(salt);
+ hash_fn->update(inner_h);
+
+ secure_vector<uint8_t> outer_h = hash_fn->final();
+
+ return BigInt::decode(outer_h);
+ }
+
+}
+
+std::string srp6_group_identifier(const BigInt& N, const BigInt& g)
+ {
+ /*
+ This function assumes that only one 'standard' SRP parameter set has
+ been defined for a particular bitsize. As of this writing that is the case.
+ */
+ try
+ {
+ const std::string group_name = "modp/srp/" + std::to_string(N.bits());
+
+ DL_Group group(group_name);
+
+ if(group.get_p() == N && group.get_g() == g)
+ return group_name;
+ }
+ catch(...)
+ {
+ }
+
+ // If we didn't return, the group was unknown or did not match
+ throw Invalid_Argument("Invalid or unknown SRP group parameters");
+ }
+
+std::pair<BigInt, SymmetricKey>
+srp6_client_agree(const std::string& identifier,
+ const std::string& password,
+ const std::string& group_id,
+ const std::string& hash_id,
+ const std::vector<uint8_t>& salt,
+ const BigInt& B,
+ RandomNumberGenerator& rng)
+ {
+ DL_Group group(group_id);
+ const size_t a_bits = group.exponent_bits();
+
+ return srp6_client_agree(identifier, password, group, hash_id, salt, B, a_bits, rng);
+ }
+
+std::pair<BigInt, SymmetricKey>
+srp6_client_agree(const std::string& identifier,
+ const std::string& password,
+ const DL_Group& group,
+ const std::string& hash_id,
+ const std::vector<uint8_t>& salt,
+ const BigInt& B,
+ const size_t a_bits,
+ RandomNumberGenerator& rng)
+ {
+ const BigInt& g = group.get_g();
+ const BigInt& p = group.get_p();
+
+ const size_t p_bytes = group.p_bytes();
+
+ if(B <= 0 || B >= p)
+ throw Decoding_Error("Invalid SRP parameter from server");
+
+ const BigInt k = hash_seq(hash_id, p_bytes, p, g);
+
+ const BigInt a(rng, a_bits);
+
+ const BigInt A = group.power_g_p(a, a_bits);
+
+ const BigInt u = hash_seq(hash_id, p_bytes, A, B);
+
+ const BigInt x = compute_x(hash_id, identifier, password, salt);
+
+ const BigInt S = power_mod(group.mod_p(B - (k * power_mod(g, x, p))),
+ group.mod_p(a + (u * x)), p);
+
+ const SymmetricKey Sk(BigInt::encode_1363(S, p_bytes));
+
+ return std::make_pair(A, Sk);
+ }
+
+BigInt generate_srp6_verifier(const std::string& identifier,
+ const std::string& password,
+ const std::vector<uint8_t>& salt,
+ const std::string& group_id,
+ const std::string& hash_id)
+ {
+ DL_Group group(group_id);
+ return generate_srp6_verifier(identifier, password, salt, group, hash_id);
+ }
+
+BigInt generate_srp6_verifier(const std::string& identifier,
+ const std::string& password,
+ const std::vector<uint8_t>& salt,
+ const DL_Group& group,
+ const std::string& hash_id)
+ {
+ const BigInt x = compute_x(hash_id, identifier, password, salt);
+ // FIXME: x should be size of hash fn so avoid computing x.bits() here
+ return group.power_g_p(x, x.bits());
+ }
+
+BigInt SRP6_Server_Session::step1(const BigInt& v,
+ const std::string& group_id,
+ const std::string& hash_id,
+ RandomNumberGenerator& rng)
+ {
+ DL_Group group(group_id);
+ const size_t b_bits = group.exponent_bits();
+
+ return this->step1(v, group, hash_id, b_bits, rng);
+ }
+
+BigInt SRP6_Server_Session::step1(const BigInt& v,
+ const DL_Group& group,
+ const std::string& hash_id,
+ size_t b_bits,
+ RandomNumberGenerator& rng)
+ {
+ const BigInt& g = group.get_g();
+ const BigInt& p = group.get_p();
+
+ m_p_bytes = p.bytes();
+ m_v = v;
+ m_b = BigInt(rng, b_bits);
+ m_p = p;
+ m_hash_id = hash_id;
+
+ const BigInt k = hash_seq(hash_id, m_p_bytes, p, g);
+
+ m_B = group.mod_p(v*k + group.power_g_p(m_b, b_bits));
+
+ return m_B;
+ }
+
+SymmetricKey SRP6_Server_Session::step2(const BigInt& A)
+ {
+ if(A <= 0 || A >= m_p)
+ throw Decoding_Error("Invalid SRP parameter from client");
+
+ const BigInt u = hash_seq(m_hash_id, m_p_bytes, A, m_B);
+
+ const BigInt S = power_mod(A * power_mod(m_v, u, m_p), m_b, m_p);
+
+ return BigInt::encode_1363(S, m_p_bytes);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/misc/srp6/srp6.h b/comm/third_party/botan/src/lib/misc/srp6/srp6.h
new file mode 100644
index 0000000000..6bb4b7e7c5
--- /dev/null
+++ b/comm/third_party/botan/src/lib/misc/srp6/srp6.h
@@ -0,0 +1,155 @@
+/*
+* SRP-6a (RFC 5054 compatatible)
+* (C) 2011,2012,2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_RFC5054_SRP6_H_
+#define BOTAN_RFC5054_SRP6_H_
+
+#include <botan/bigint.h>
+#include <botan/symkey.h>
+#include <string>
+
+namespace Botan {
+
+class DL_Group;
+class RandomNumberGenerator;
+
+/**
+* SRP6a Client side
+* @param username the username we are attempting login for
+* @param password the password we are attempting to use
+* @param group_id specifies the shared SRP group
+* @param hash_id specifies a secure hash function
+* @param salt is the salt value sent by the server
+* @param B is the server's public value
+* @param rng is a random number generator
+*
+* @return (A,K) the client public key and the shared secret key
+*/
+std::pair<BigInt,SymmetricKey>
+BOTAN_PUBLIC_API(2,0) srp6_client_agree(const std::string& username,
+ const std::string& password,
+ const std::string& group_id,
+ const std::string& hash_id,
+ const std::vector<uint8_t>& salt,
+ const BigInt& B,
+ RandomNumberGenerator& rng);
+
+
+/**
+* SRP6a Client side
+* @param username the username we are attempting login for
+* @param password the password we are attempting to use
+* @param group specifies the shared SRP group
+* @param hash_id specifies a secure hash function
+* @param salt is the salt value sent by the server
+* @param B is the server's public value
+* @param a_bits size of secret exponent in bits
+* @param rng is a random number generator
+*
+* @return (A,K) the client public key and the shared secret key
+*/
+std::pair<BigInt,SymmetricKey> BOTAN_PUBLIC_API(2,11)
+ srp6_client_agree(const std::string& username,
+ const std::string& password,
+ const DL_Group& group,
+ const std::string& hash_id,
+ const std::vector<uint8_t>& salt,
+ const BigInt& B,
+ size_t a_bits,
+ RandomNumberGenerator& rng);
+
+/**
+* Generate a new SRP-6 verifier
+* @param identifier a username or other client identifier
+* @param password the secret used to authenticate user
+* @param salt a randomly chosen value, at least 128 bits long
+* @param group_id specifies the shared SRP group
+* @param hash_id specifies a secure hash function
+*/
+BigInt BOTAN_PUBLIC_API(2,0)
+ generate_srp6_verifier(const std::string& identifier,
+ const std::string& password,
+ const std::vector<uint8_t>& salt,
+ const std::string& group_id,
+ const std::string& hash_id);
+
+/**
+* Generate a new SRP-6 verifier
+* @param identifier a username or other client identifier
+* @param password the secret used to authenticate user
+* @param salt a randomly chosen value, at least 128 bits long
+* @param group specifies the shared SRP group
+* @param hash_id specifies a secure hash function
+*/
+BigInt BOTAN_PUBLIC_API(2,11)
+ generate_srp6_verifier(const std::string& identifier,
+ const std::string& password,
+ const std::vector<uint8_t>& salt,
+ const DL_Group& group,
+ const std::string& hash_id);
+
+/**
+* Return the group id for this SRP param set, or else thrown an
+* exception
+* @param N the group modulus
+* @param g the group generator
+* @return group identifier
+*/
+std::string BOTAN_PUBLIC_API(2,0) srp6_group_identifier(const BigInt& N, const BigInt& g);
+
+/**
+* Represents a SRP-6a server session
+*/
+class BOTAN_PUBLIC_API(2,0) SRP6_Server_Session final
+ {
+ public:
+ /**
+ * Server side step 1
+ * @param v the verification value saved from client registration
+ * @param group_id the SRP group id
+ * @param hash_id the SRP hash in use
+ * @param rng a random number generator
+ * @return SRP-6 B value
+ */
+ BigInt step1(const BigInt& v,
+ const std::string& group_id,
+ const std::string& hash_id,
+ RandomNumberGenerator& rng);
+
+ /**
+ * Server side step 1
+ * This version of step1 added in 2.11
+ *
+ * @param v the verification value saved from client registration
+ * @param group the SRP group
+ * @param hash_id the SRP hash in use
+ * @param rng a random number generator
+ * @param b_bits size of secret exponent in bits
+ * @return SRP-6 B value
+ */
+ BigInt step1(const BigInt& v,
+ const DL_Group& group,
+ const std::string& hash_id,
+ const size_t b_bits,
+ RandomNumberGenerator& rng);
+
+ /**
+ * Server side step 2
+ * @param A the client's value
+ * @return shared symmetric key
+ */
+ SymmetricKey step2(const BigInt& A);
+
+ private:
+ std::string m_hash_id;
+ BigInt m_B, m_b, m_v, m_S, m_p;
+ size_t m_p_bytes = 0;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/misc/tss/info.txt b/comm/third_party/botan/src/lib/misc/tss/info.txt
new file mode 100644
index 0000000000..513be70bc5
--- /dev/null
+++ b/comm/third_party/botan/src/lib/misc/tss/info.txt
@@ -0,0 +1,10 @@
+<defines>
+THRESHOLD_SECRET_SHARING -> 20131128
+</defines>
+
+<requires>
+rng
+hex
+sha1
+sha2_32
+</requires>
diff --git a/comm/third_party/botan/src/lib/misc/tss/tss.cpp b/comm/third_party/botan/src/lib/misc/tss/tss.cpp
new file mode 100644
index 0000000000..dd3a294f8a
--- /dev/null
+++ b/comm/third_party/botan/src/lib/misc/tss/tss.cpp
@@ -0,0 +1,333 @@
+/*
+* RTSS (threshold secret sharing)
+* (C) 2009,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/tss.h>
+#include <botan/rng.h>
+#include <botan/hash.h>
+#include <botan/loadstor.h>
+#include <botan/hex.h>
+
+namespace Botan {
+
+namespace {
+
+const size_t RTSS_HEADER_SIZE = 20;
+
+/**
+Table for GF(2^8) arithmetic (exponentials)
+*/
+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 };
+
+/**
+Table for GF(2^8) arithmetic (logarithms)
+*/
+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 rtss_hash_id(const std::string& hash_name)
+ {
+ if(hash_name == "None")
+ return 0;
+ else if(hash_name == "SHA-160" || hash_name == "SHA-1" || hash_name == "SHA1")
+ return 1;
+ else if(hash_name == "SHA-256")
+ return 2;
+ else
+ throw Invalid_Argument("RTSS only supports SHA-1 and SHA-256");
+ }
+
+std::unique_ptr<HashFunction> get_rtss_hash_by_id(uint8_t id)
+ {
+ if(id == 0)
+ return std::unique_ptr<HashFunction>();
+ if(id == 1)
+ return HashFunction::create_or_throw("SHA-1");
+ else if(id == 2)
+ return HashFunction::create_or_throw("SHA-256");
+ else
+ throw Decoding_Error("Unknown RTSS hash identifier");
+ }
+
+}
+
+RTSS_Share::RTSS_Share(const std::string& hex_input)
+ {
+ m_contents = hex_decode_locked(hex_input);
+ }
+
+RTSS_Share::RTSS_Share(const uint8_t bin[], size_t len)
+ {
+ m_contents.assign(bin, bin + len);
+ }
+
+uint8_t RTSS_Share::share_id() const
+ {
+ if(!initialized())
+ throw Invalid_State("RTSS_Share::share_id not initialized");
+
+ if(m_contents.size() < RTSS_HEADER_SIZE + 1)
+ throw Decoding_Error("RTSS_Share::share_id invalid share data");
+
+ return m_contents[20];
+ }
+
+std::string RTSS_Share::to_string() const
+ {
+ return hex_encode(m_contents.data(), m_contents.size());
+ }
+
+std::vector<RTSS_Share>
+RTSS_Share::split(uint8_t M, uint8_t N,
+ const uint8_t S[], uint16_t S_len,
+ const uint8_t identifier[16],
+ RandomNumberGenerator& rng)
+ {
+ return RTSS_Share::split(M, N, S, S_len,
+ std::vector<uint8_t>(identifier, identifier + 16),
+ "SHA-256",
+ rng);
+ }
+
+std::vector<RTSS_Share>
+RTSS_Share::split(uint8_t M, uint8_t N,
+ const uint8_t S[], uint16_t S_len,
+ const std::vector<uint8_t>& identifier,
+ const std::string& hash_fn,
+ RandomNumberGenerator& rng)
+ {
+ if(M <= 1 || N <= 1 || M > N || N >= 255)
+ throw Invalid_Argument("RTSS_Share::split: Invalid N or M");
+
+ if(identifier.size() > 16)
+ throw Invalid_Argument("RTSS_Share::split Invalid identifier size");
+
+ const uint8_t hash_id = rtss_hash_id(hash_fn);
+
+ std::unique_ptr<HashFunction> hash;
+ if(hash_id > 0)
+ hash = HashFunction::create_or_throw(hash_fn);
+
+ // secret = S || H(S)
+ secure_vector<uint8_t> secret(S, S + S_len);
+ if(hash)
+ secret += hash->process(S, S_len);
+
+ if(secret.size() >= 0xFFFE)
+ throw Encoding_Error("RTSS_Share::split secret too large for TSS format");
+
+ // +1 byte for the share ID
+ const uint16_t share_len = static_cast<uint16_t>(secret.size() + 1);
+
+ secure_vector<uint8_t> share_header(RTSS_HEADER_SIZE);
+ copy_mem(&share_header[0], identifier.data(), identifier.size());
+ share_header[16] = hash_id;
+ share_header[17] = M;
+ share_header[18] = get_byte(0, share_len);
+ share_header[19] = get_byte(1, share_len);
+
+ // Create RTSS header in each share
+ std::vector<RTSS_Share> shares(N);
+
+ for(uint8_t i = 0; i != N; ++i)
+ {
+ shares[i].m_contents.reserve(share_header.size() + share_len);
+ shares[i].m_contents = share_header;
+ }
+
+ // Choose sequential values for X starting from 1
+ for(uint8_t i = 0; i != N; ++i)
+ shares[i].m_contents.push_back(i+1);
+
+ for(size_t i = 0; i != secret.size(); ++i)
+ {
+ std::vector<uint8_t> 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 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);
+ }
+
+ shares[j].m_contents.push_back(sum);
+ }
+ }
+
+ return shares;
+ }
+
+secure_vector<uint8_t>
+RTSS_Share::reconstruct(const std::vector<RTSS_Share>& shares)
+ {
+ if(shares.size() <= 1)
+ throw Decoding_Error("Insufficient shares to do TSS reconstruction");
+
+ for(size_t i = 0; i != shares.size(); ++i)
+ {
+ if(shares[i].size() < RTSS_HEADER_SIZE + 1)
+ throw Decoding_Error("Missing or malformed RTSS header");
+
+ if(shares[i].share_id() == 0)
+ throw Decoding_Error("Invalid (id = 0) RTSS share detected");
+
+ if(i > 0)
+ {
+ if(shares[i].size() != shares[0].size())
+ throw Decoding_Error("Different sized RTSS shares detected");
+
+ if(!same_mem(&shares[0].m_contents[0],
+ &shares[i].m_contents[0], RTSS_HEADER_SIZE))
+ throw Decoding_Error("Different RTSS headers detected");
+ }
+ }
+
+ const uint8_t N = shares[0].m_contents[17];
+
+ if(shares.size() < N)
+ throw Decoding_Error("Insufficient shares to do TSS reconstruction");
+
+ const uint16_t share_len = make_uint16(shares[0].m_contents[18],
+ shares[0].m_contents[19]);
+
+ const uint8_t hash_id = shares[0].m_contents[16];
+ std::unique_ptr<HashFunction> hash(get_rtss_hash_by_id(hash_id));
+ const size_t hash_len = (hash ? hash->output_length() : 0);
+
+ if(shares[0].size() != RTSS_HEADER_SIZE + share_len)
+ {
+ /*
+ * This second (laxer) check accomodates 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.
+ */
+ if(shares[0].size() <= RTSS_HEADER_SIZE + 1 + hash_len)
+ throw Decoding_Error("Bad RTSS length field in header");
+ }
+
+ std::vector<uint8_t> V(shares.size());
+ secure_vector<uint8_t> recovered;
+
+ 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];
+
+ 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);
+ }
+ recovered.push_back(r);
+ }
+
+ if(hash)
+ {
+ if(recovered.size() < hash->output_length())
+ throw Decoding_Error("RTSS recovered value too short to be valid");
+
+ const size_t secret_len = recovered.size() - hash->output_length();
+
+ hash->update(recovered.data(), secret_len);
+ secure_vector<uint8_t> hash_check = hash->final();
+
+ if(!constant_time_compare(hash_check.data(),
+ &recovered[secret_len],
+ hash->output_length()))
+ {
+ throw Decoding_Error("RTSS hash check failed");
+ }
+
+ // remove the trailing hash value
+ recovered.resize(secret_len);
+ }
+
+ return recovered;
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/misc/tss/tss.h b/comm/third_party/botan/src/lib/misc/tss/tss.h
new file mode 100644
index 0000000000..89848c43e6
--- /dev/null
+++ b/comm/third_party/botan/src/lib/misc/tss/tss.h
@@ -0,0 +1,104 @@
+/*
+* RTSS (threshold secret sharing)
+* (C) 2009,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_RTSS_H_
+#define BOTAN_RTSS_H_
+
+#include <botan/secmem.h>
+#include <string>
+#include <vector>
+
+namespace Botan {
+
+class RandomNumberGenerator;
+
+/**
+* A split secret, using the format from draft-mcgrew-tss-03
+*/
+class BOTAN_PUBLIC_API(2,0) RTSS_Share final
+ {
+ public:
+ /**
+ * @param M the number of shares needed to reconstruct
+ * @param N the number of shares generated
+ * @param secret the secret to split
+ * @param secret_len the length of the secret
+ * @param identifier the 16 byte share identifier
+ * @param rng the random number generator to use
+ */
+ static std::vector<RTSS_Share>
+ split(uint8_t M, uint8_t N,
+ const uint8_t secret[], uint16_t secret_len,
+ const uint8_t identifier[16],
+ RandomNumberGenerator& rng);
+
+ /**
+ * @param M the number of shares needed to reconstruct
+ * @param N the number of shares generated
+ * @param secret the secret to split
+ * @param secret_len the length of the secret
+ * @param identifier the share identifier
+ * @param hash_fn the hash function to use for a checksum ("None", "SHA-1", "SHA-256")
+ * @param rng the random number generator to use
+ */
+ static std::vector<RTSS_Share>
+ split(uint8_t M, uint8_t N,
+ const uint8_t secret[], uint16_t secret_len,
+ const std::vector<uint8_t>& identifier,
+ const std::string& hash_fn,
+ RandomNumberGenerator& rng);
+
+ /**
+ * @param shares the list of shares
+ */
+ static secure_vector<uint8_t>
+ reconstruct(const std::vector<RTSS_Share>& shares);
+
+ RTSS_Share() = default;
+
+ /**
+ * @param hex_input the share encoded in hexadecimal
+ */
+ explicit RTSS_Share(const std::string& hex_input);
+
+ /**
+ * @param data the shared data
+ * @param len the length of data
+ */
+ RTSS_Share(const uint8_t data[], size_t len);
+
+ /**
+ * @return binary representation
+ */
+ const secure_vector<uint8_t>& data() const { return m_contents; }
+
+ /**
+ * @return hex representation
+ */
+ std::string to_string() const;
+
+ /**
+ * @return share identifier
+ */
+ uint8_t share_id() const;
+
+ /**
+ * @return size of this share in bytes
+ */
+ size_t size() const { return m_contents.size(); }
+
+ /**
+ * @return if this TSS share was initialized or not
+ */
+ bool initialized() const { return (m_contents.size() > 0); }
+ private:
+ secure_vector<uint8_t> m_contents;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/modes/aead/aead.cpp b/comm/third_party/botan/src/lib/modes/aead/aead.cpp
new file mode 100644
index 0000000000..9ad550ffd7
--- /dev/null
+++ b/comm/third_party/botan/src/lib/modes/aead/aead.cpp
@@ -0,0 +1,176 @@
+/*
+* (C) 2013,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/aead.h>
+#include <botan/scan_name.h>
+#include <botan/parsing.h>
+#include <sstream>
+
+#if defined(BOTAN_HAS_BLOCK_CIPHER)
+ #include <botan/block_cipher.h>
+#endif
+
+#if defined(BOTAN_HAS_AEAD_CCM)
+ #include <botan/ccm.h>
+#endif
+
+#if defined(BOTAN_HAS_AEAD_CHACHA20_POLY1305)
+ #include <botan/chacha20poly1305.h>
+#endif
+
+#if defined(BOTAN_HAS_AEAD_EAX)
+ #include <botan/eax.h>
+#endif
+
+#if defined(BOTAN_HAS_AEAD_GCM)
+ #include <botan/gcm.h>
+#endif
+
+#if defined(BOTAN_HAS_AEAD_OCB)
+ #include <botan/ocb.h>
+#endif
+
+#if defined(BOTAN_HAS_AEAD_SIV)
+ #include <botan/siv.h>
+#endif
+
+namespace Botan {
+
+void AEAD_Mode::set_associated_data_n(size_t i, const uint8_t ad[], size_t ad_len)
+ {
+ if(i == 0)
+ this->set_associated_data(ad, ad_len);
+ else
+ throw Invalid_Argument("AEAD '" + name() + "' does not support multiple associated data");
+ }
+
+std::unique_ptr<AEAD_Mode> AEAD_Mode::create_or_throw(const std::string& algo,
+ Cipher_Dir dir,
+ const std::string& provider)
+ {
+ if(auto aead = AEAD_Mode::create(algo, dir, provider))
+ return aead;
+
+ throw Lookup_Error("AEAD", algo, provider);
+ }
+
+std::unique_ptr<AEAD_Mode> AEAD_Mode::create(const std::string& algo,
+ Cipher_Dir dir,
+ const std::string& provider)
+ {
+ BOTAN_UNUSED(provider);
+#if defined(BOTAN_HAS_AEAD_CHACHA20_POLY1305)
+ if(algo == "ChaCha20Poly1305")
+ {
+ if(dir == ENCRYPTION)
+ return std::unique_ptr<AEAD_Mode>(new ChaCha20Poly1305_Encryption);
+ else
+ return std::unique_ptr<AEAD_Mode>(new ChaCha20Poly1305_Decryption);
+
+ }
+#endif
+
+ if(algo.find('/') != std::string::npos)
+ {
+ const std::vector<std::string> algo_parts = split_on(algo, '/');
+ const std::string cipher_name = algo_parts[0];
+ const std::vector<std::string> mode_info = parse_algorithm_name(algo_parts[1]);
+
+ if(mode_info.empty())
+ return std::unique_ptr<AEAD_Mode>();
+
+ std::ostringstream alg_args;
+
+ alg_args << '(' << cipher_name;
+ for(size_t i = 1; i < mode_info.size(); ++i)
+ alg_args << ',' << mode_info[i];
+ for(size_t i = 2; i < algo_parts.size(); ++i)
+ alg_args << ',' << algo_parts[i];
+ alg_args << ')';
+
+ const std::string mode_name = mode_info[0] + alg_args.str();
+ return AEAD_Mode::create(mode_name, dir);
+ }
+
+#if defined(BOTAN_HAS_BLOCK_CIPHER)
+
+ SCAN_Name req(algo);
+
+ if(req.arg_count() == 0)
+ {
+ return std::unique_ptr<AEAD_Mode>();
+ }
+
+ std::unique_ptr<BlockCipher> bc(BlockCipher::create(req.arg(0), provider));
+
+ if(!bc)
+ {
+ return std::unique_ptr<AEAD_Mode>();
+ }
+
+#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);
+ if(dir == ENCRYPTION)
+ return std::unique_ptr<AEAD_Mode>(new CCM_Encryption(bc.release(), tag_len, L_len));
+ else
+ return std::unique_ptr<AEAD_Mode>(new CCM_Decryption(bc.release(), tag_len, L_len));
+ }
+#endif
+
+#if defined(BOTAN_HAS_AEAD_GCM)
+ if(req.algo_name() == "GCM")
+ {
+ size_t tag_len = req.arg_as_integer(1, 16);
+ if(dir == ENCRYPTION)
+ return std::unique_ptr<AEAD_Mode>(new GCM_Encryption(bc.release(), tag_len));
+ else
+ return std::unique_ptr<AEAD_Mode>(new GCM_Decryption(bc.release(), tag_len));
+ }
+#endif
+
+#if defined(BOTAN_HAS_AEAD_OCB)
+ if(req.algo_name() == "OCB")
+ {
+ size_t tag_len = req.arg_as_integer(1, 16);
+ if(dir == ENCRYPTION)
+ return std::unique_ptr<AEAD_Mode>(new OCB_Encryption(bc.release(), tag_len));
+ else
+ return std::unique_ptr<AEAD_Mode>(new OCB_Decryption(bc.release(), tag_len));
+ }
+#endif
+
+#if defined(BOTAN_HAS_AEAD_EAX)
+ if(req.algo_name() == "EAX")
+ {
+ size_t tag_len = req.arg_as_integer(1, bc->block_size());
+ if(dir == ENCRYPTION)
+ return std::unique_ptr<AEAD_Mode>(new EAX_Encryption(bc.release(), tag_len));
+ else
+ return std::unique_ptr<AEAD_Mode>(new EAX_Decryption(bc.release(), tag_len));
+ }
+#endif
+
+#if defined(BOTAN_HAS_AEAD_SIV)
+ if(req.algo_name() == "SIV")
+ {
+ if(dir == ENCRYPTION)
+ return std::unique_ptr<AEAD_Mode>(new SIV_Encryption(bc.release()));
+ else
+ return std::unique_ptr<AEAD_Mode>(new SIV_Decryption(bc.release()));
+ }
+#endif
+
+#endif
+
+ return std::unique_ptr<AEAD_Mode>();
+ }
+
+
+
+}
diff --git a/comm/third_party/botan/src/lib/modes/aead/aead.h b/comm/third_party/botan/src/lib/modes/aead/aead.h
new file mode 100644
index 0000000000..442eb8ed7f
--- /dev/null
+++ b/comm/third_party/botan/src/lib/modes/aead/aead.h
@@ -0,0 +1,147 @@
+/*
+* Interface for AEAD modes
+* (C) 2013 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_AEAD_MODE_H_
+#define BOTAN_AEAD_MODE_H_
+
+#include <botan/cipher_mode.h>
+
+namespace Botan {
+
+/**
+* Interface for AEAD (Authenticated Encryption with Associated Data)
+* modes. These modes provide both encryption and message
+* authentication, and can authenticate additional per-message data
+* which is not included in the ciphertext (for instance a sequence
+* number).
+*/
+class BOTAN_PUBLIC_API(2,0) AEAD_Mode : public Cipher_Mode
+ {
+ public:
+ /**
+ * Create an AEAD mode
+ * @param algo the algorithm to create
+ * @param direction specify if this should be an encryption or decryption AEAD
+ * @param provider optional specification for provider to use
+ * @return an AEAD mode or a null pointer if not available
+ */
+ static std::unique_ptr<AEAD_Mode> create(const std::string& algo,
+ Cipher_Dir direction,
+ const std::string& provider = "");
+
+ /**
+ * Create an AEAD mode, or throw
+ * @param algo the algorithm to create
+ * @param direction specify if this should be an encryption or decryption AEAD
+ * @param provider optional specification for provider to use
+ * @return an AEAD mode, or throw an exception
+ */
+ static std::unique_ptr<AEAD_Mode> create_or_throw(const std::string& algo,
+ Cipher_Dir direction,
+ const std::string& provider = "");
+
+ bool authenticated() const override { return true; }
+
+ /**
+ * Set associated data that is not included in the ciphertext but
+ * that should be authenticated. Must be called after set_key and
+ * before start.
+ *
+ * Unless reset by another call, the associated data is kept
+ * between messages. Thus, if the AD does not change, calling
+ * once (after set_key) is the optimum.
+ *
+ * @param ad the associated data
+ * @param ad_len length of add in bytes
+ */
+ virtual void set_associated_data(const uint8_t ad[], size_t ad_len) = 0;
+
+ /**
+ * Set associated data that is not included in the ciphertext but
+ * that should be authenticated. Must be called after set_key and
+ * before start.
+ *
+ * Unless reset by another call, the associated data is kept
+ * between messages. Thus, if the AD does not change, calling
+ * once (after set_key) is the optimum.
+ *
+ * Some AEADs (namely SIV) support multiple AD inputs. For
+ * all other modes only nominal AD input 0 is supported; all
+ * other values of i will cause an exception.
+ *
+ * @param ad the associated data
+ * @param ad_len length of add in bytes
+ */
+ virtual void set_associated_data_n(size_t i, const uint8_t ad[], size_t ad_len);
+
+ /**
+ * Returns the maximum supported number of associated data inputs which
+ * can be provided to set_associated_data_n
+ *
+ * If returns 0, then no associated data is supported.
+ */
+ virtual size_t maximum_associated_data_inputs() const { return 1; }
+
+ /**
+ * Most AEADs require the key to be set prior to setting the AD
+ * A few allow the AD to be set even before the cipher is keyed.
+ * Such ciphers would return false from this function.
+ */
+ virtual bool associated_data_requires_key() const { return true; }
+
+ /**
+ * Set associated data that is not included in the ciphertext but
+ * that should be authenticated. Must be called after set_key and
+ * before start.
+ *
+ * See @ref set_associated_data().
+ *
+ * @param ad the associated data
+ */
+ template<typename Alloc>
+ void set_associated_data_vec(const std::vector<uint8_t, Alloc>& ad)
+ {
+ set_associated_data(ad.data(), ad.size());
+ }
+
+ /**
+ * Set associated data that is not included in the ciphertext but
+ * that should be authenticated. Must be called after set_key and
+ * before start.
+ *
+ * See @ref set_associated_data().
+ *
+ * @param ad the associated data
+ */
+ template<typename Alloc>
+ void set_ad(const std::vector<uint8_t, Alloc>& ad)
+ {
+ set_associated_data(ad.data(), ad.size());
+ }
+
+ /**
+ * @return default AEAD nonce size (a commonly supported value among AEAD
+ * modes, and large enough that random collisions are unlikely)
+ */
+ size_t default_nonce_length() const override { return 12; }
+
+ virtual ~AEAD_Mode() = default;
+ };
+
+/**
+* Get an AEAD mode by name (eg "AES-128/GCM" or "Serpent/EAX")
+* @param name AEAD name
+* @param direction ENCRYPTION or DECRYPTION
+*/
+inline AEAD_Mode* get_aead(const std::string& name, Cipher_Dir direction)
+ {
+ return AEAD_Mode::create(name, direction, "").release();
+ }
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/modes/aead/ccm/ccm.cpp b/comm/third_party/botan/src/lib/modes/aead/ccm/ccm.cpp
new file mode 100644
index 0000000000..ab2cfcb950
--- /dev/null
+++ b/comm/third_party/botan/src/lib/modes/aead/ccm/ccm.cpp
@@ -0,0 +1,279 @@
+/*
+* CCM Mode Encryption
+* (C) 2013,2018 Jack Lloyd
+* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/ccm.h>
+#include <botan/loadstor.h>
+
+namespace Botan {
+
+// 128-bit cipher is intrinsic to CCM definition
+static const size_t CCM_BS = 16;
+
+/*
+* CCM_Mode Constructor
+*/
+CCM_Mode::CCM_Mode(BlockCipher* cipher, size_t tag_size, size_t L) :
+ m_tag_size(tag_size),
+ m_L(L),
+ m_cipher(cipher)
+ {
+ if(m_cipher->block_size() != CCM_BS)
+ throw Invalid_Argument(m_cipher->name() + " cannot be used with CCM mode");
+
+ if(L < 2 || L > 8)
+ throw Invalid_Argument("Invalid CCM L value " + std::to_string(L));
+
+ if(tag_size < 4 || tag_size > 16 || tag_size % 2 != 0)
+ throw Invalid_Argument("invalid CCM tag length " + std::to_string(tag_size));
+ }
+
+void CCM_Mode::clear()
+ {
+ m_cipher->clear();
+ reset();
+ }
+
+void CCM_Mode::reset()
+ {
+ m_nonce.clear();
+ m_msg_buf.clear();
+ m_ad_buf.clear();
+ }
+
+std::string CCM_Mode::name() const
+ {
+ return (m_cipher->name() + "/CCM(" + std::to_string(tag_size()) + "," + std::to_string(L())) + ")";
+ }
+
+bool CCM_Mode::valid_nonce_length(size_t n) const
+ {
+ return (n == (15-L()));
+ }
+
+size_t CCM_Mode::default_nonce_length() const
+ {
+ return (15-L());
+ }
+
+size_t CCM_Mode::update_granularity() const
+ {
+ /*
+ This value does not particularly matter as regardless CCM_Mode::update
+ buffers all input, so in theory this could be 1. However as for instance
+ Transform_Filter creates update_granularity() uint8_t buffers, use a
+ somewhat large size to avoid bouncing on a tiny buffer.
+ */
+ return m_cipher->parallel_bytes();
+ }
+
+Key_Length_Specification CCM_Mode::key_spec() const
+ {
+ return m_cipher->key_spec();
+ }
+
+void CCM_Mode::key_schedule(const uint8_t key[], size_t length)
+ {
+ m_cipher->set_key(key, length);
+ }
+
+void CCM_Mode::set_associated_data(const uint8_t ad[], size_t length)
+ {
+ m_ad_buf.clear();
+
+ if(length)
+ {
+ // FIXME: support larger AD using length encoding rules
+ BOTAN_ARG_CHECK(length < (0xFFFF - 0xFF), "Supported CCM AD length");
+
+ m_ad_buf.push_back(get_byte(0, static_cast<uint16_t>(length)));
+ m_ad_buf.push_back(get_byte(1, static_cast<uint16_t>(length)));
+ m_ad_buf += std::make_pair(ad, length);
+ while(m_ad_buf.size() % CCM_BS)
+ m_ad_buf.push_back(0); // pad with zeros to full block size
+ }
+ }
+
+void CCM_Mode::start_msg(const uint8_t nonce[], size_t nonce_len)
+ {
+ if(!valid_nonce_length(nonce_len))
+ throw Invalid_IV_Length(name(), nonce_len);
+
+ m_nonce.assign(nonce, nonce + nonce_len);
+ m_msg_buf.clear();
+ }
+
+size_t CCM_Mode::process(uint8_t buf[], size_t sz)
+ {
+ BOTAN_STATE_CHECK(m_nonce.size() > 0);
+ m_msg_buf.insert(m_msg_buf.end(), buf, buf + sz);
+ return 0; // no output until finished
+ }
+
+void CCM_Mode::encode_length(uint64_t len, uint8_t out[])
+ {
+ const size_t len_bytes = L();
+
+ BOTAN_ASSERT_NOMSG(len_bytes >= 2 && len_bytes <= 8);
+
+ for(size_t i = 0; i != len_bytes; ++i)
+ out[len_bytes-1-i] = get_byte(sizeof(uint64_t)-1-i, len);
+
+ if(len_bytes < 8 && (len >> (len_bytes*8)) > 0)
+ throw Encoding_Error("CCM message length too long to encode in L field");
+ }
+
+void CCM_Mode::inc(secure_vector<uint8_t>& C)
+ {
+ for(size_t i = 0; i != C.size(); ++i)
+ if(++C[C.size()-i-1])
+ break;
+ }
+
+secure_vector<uint8_t> CCM_Mode::format_b0(size_t sz)
+ {
+ if(m_nonce.size() != 15-L())
+ throw Invalid_State("CCM mode must set nonce");
+ secure_vector<uint8_t> B0(CCM_BS);
+
+ const uint8_t b_flags =
+ static_cast<uint8_t>((m_ad_buf.size() ? 64 : 0) + (((tag_size()/2)-1) << 3) + (L()-1));
+
+ B0[0] = b_flags;
+ copy_mem(&B0[1], m_nonce.data(), m_nonce.size());
+ encode_length(sz, &B0[m_nonce.size()+1]);
+
+ return B0;
+ }
+
+secure_vector<uint8_t> CCM_Mode::format_c0()
+ {
+ if(m_nonce.size() != 15-L())
+ throw Invalid_State("CCM mode must set nonce");
+ secure_vector<uint8_t> C(CCM_BS);
+
+ const uint8_t a_flags = static_cast<uint8_t>(L() - 1);
+
+ C[0] = a_flags;
+ copy_mem(&C[1], m_nonce.data(), m_nonce.size());
+
+ return C;
+ }
+
+void CCM_Encryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
+ {
+ BOTAN_ARG_CHECK(buffer.size() >= offset, "Offset is sane");
+
+ buffer.insert(buffer.begin() + offset, msg_buf().begin(), msg_buf().end());
+
+ const size_t sz = buffer.size() - offset;
+ uint8_t* buf = buffer.data() + offset;
+
+ const secure_vector<uint8_t>& ad = ad_buf();
+ BOTAN_ARG_CHECK(ad.size() % CCM_BS == 0, "AD is block size multiple");
+
+ const BlockCipher& E = cipher();
+
+ secure_vector<uint8_t> T(CCM_BS);
+ E.encrypt(format_b0(sz), T);
+
+ for(size_t i = 0; i != ad.size(); i += CCM_BS)
+ {
+ xor_buf(T.data(), &ad[i], CCM_BS);
+ E.encrypt(T);
+ }
+
+ secure_vector<uint8_t> C = format_c0();
+ secure_vector<uint8_t> S0(CCM_BS);
+ E.encrypt(C, S0);
+ inc(C);
+
+ secure_vector<uint8_t> X(CCM_BS);
+
+ const uint8_t* buf_end = &buf[sz];
+
+ while(buf != buf_end)
+ {
+ const size_t to_proc = std::min<size_t>(CCM_BS, buf_end - buf);
+
+ xor_buf(T.data(), buf, to_proc);
+ E.encrypt(T);
+
+ E.encrypt(C, X);
+ xor_buf(buf, X.data(), to_proc);
+ inc(C);
+
+ buf += to_proc;
+ }
+
+ T ^= S0;
+
+ buffer += std::make_pair(T.data(), tag_size());
+
+ reset();
+ }
+
+void CCM_Decryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
+ {
+ BOTAN_ARG_CHECK(buffer.size() >= offset, "Offset is sane");
+
+ buffer.insert(buffer.begin() + offset, msg_buf().begin(), msg_buf().end());
+
+ const size_t sz = buffer.size() - offset;
+ uint8_t* buf = buffer.data() + offset;
+
+ BOTAN_ASSERT(sz >= tag_size(), "We have the tag");
+
+ const secure_vector<uint8_t>& ad = ad_buf();
+ BOTAN_ARG_CHECK(ad.size() % CCM_BS == 0, "AD is block size multiple");
+
+ const BlockCipher& E = cipher();
+
+ secure_vector<uint8_t> T(CCM_BS);
+ E.encrypt(format_b0(sz - tag_size()), T);
+
+ for(size_t i = 0; i != ad.size(); i += CCM_BS)
+ {
+ xor_buf(T.data(), &ad[i], CCM_BS);
+ E.encrypt(T);
+ }
+
+ secure_vector<uint8_t> C = format_c0();
+
+ secure_vector<uint8_t> S0(CCM_BS);
+ E.encrypt(C, S0);
+ inc(C);
+
+ secure_vector<uint8_t> X(CCM_BS);
+
+ const uint8_t* buf_end = &buf[sz - tag_size()];
+
+ while(buf != buf_end)
+ {
+ const size_t to_proc = std::min<size_t>(CCM_BS, buf_end - buf);
+
+ E.encrypt(C, X);
+ xor_buf(buf, X.data(), to_proc);
+ inc(C);
+
+ xor_buf(T.data(), buf, to_proc);
+ E.encrypt(T);
+
+ buf += to_proc;
+ }
+
+ T ^= S0;
+
+ if(!constant_time_compare(T.data(), buf_end, tag_size()))
+ throw Invalid_Authentication_Tag("CCM tag check failed");
+
+ buffer.resize(buffer.size() - tag_size());
+
+ reset();
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/modes/aead/ccm/ccm.h b/comm/third_party/botan/src/lib/modes/aead/ccm/ccm.h
new file mode 100644
index 0000000000..9b4bcecbf2
--- /dev/null
+++ b/comm/third_party/botan/src/lib/modes/aead/ccm/ccm.h
@@ -0,0 +1,130 @@
+/*
+* CCM Mode
+* (C) 2013 Jack Lloyd
+* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_AEAD_CCM_H_
+#define BOTAN_AEAD_CCM_H_
+
+#include <botan/aead.h>
+#include <botan/block_cipher.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(ccm.h)
+
+namespace Botan {
+
+/**
+* Base class for CCM encryption and decryption
+* @see RFC 3610
+*/
+class BOTAN_PUBLIC_API(2,0) CCM_Mode : public AEAD_Mode
+ {
+ public:
+ size_t process(uint8_t buf[], size_t sz) override;
+
+ void set_associated_data(const uint8_t ad[], size_t ad_len) override;
+
+ bool associated_data_requires_key() const override { return false; }
+
+ std::string name() const override;
+
+ size_t update_granularity() const override;
+
+ Key_Length_Specification key_spec() const override;
+
+ bool valid_nonce_length(size_t) const override;
+
+ size_t default_nonce_length() const override;
+
+ void clear() override;
+
+ void reset() override;
+
+ size_t tag_size() const override { return m_tag_size; }
+
+ protected:
+ CCM_Mode(BlockCipher* cipher, size_t tag_size, size_t L);
+
+ size_t L() const { return m_L; }
+
+ const BlockCipher& cipher() const { return *m_cipher; }
+
+ void encode_length(uint64_t len, uint8_t out[]);
+
+ void inc(secure_vector<uint8_t>& C);
+
+ const secure_vector<uint8_t>& ad_buf() const { return m_ad_buf; }
+
+ secure_vector<uint8_t>& msg_buf() { return m_msg_buf; }
+
+ secure_vector<uint8_t> format_b0(size_t msg_size);
+ secure_vector<uint8_t> format_c0();
+ private:
+ void start_msg(const uint8_t nonce[], size_t nonce_len) override;
+
+ void key_schedule(const uint8_t key[], size_t length) override;
+
+ const size_t m_tag_size;
+ const size_t m_L;
+
+ std::unique_ptr<BlockCipher> m_cipher;
+ secure_vector<uint8_t> m_nonce, m_msg_buf, m_ad_buf;
+ };
+
+/**
+* CCM Encryption
+*/
+class BOTAN_PUBLIC_API(2,0) CCM_Encryption final : public CCM_Mode
+ {
+ public:
+ /**
+ * @param cipher a 128-bit block cipher
+ * @param tag_size is how big the auth tag will be (even values
+ * between 4 and 16 are accepted)
+ * @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(BlockCipher* cipher, size_t tag_size = 16, size_t L = 3) :
+ CCM_Mode(cipher, tag_size, L) {}
+
+ void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
+
+ size_t output_length(size_t input_length) const override
+ { return input_length + tag_size(); }
+
+ size_t minimum_final_size() const override { return 0; }
+ };
+
+/**
+* CCM Decryption
+*/
+class BOTAN_PUBLIC_API(2,0) CCM_Decryption final : public CCM_Mode
+ {
+ public:
+ /**
+ * @param cipher a 128-bit block cipher
+ * @param tag_size is how big the auth tag will be (even values
+ * between 4 and 16 are accepted)
+ * @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(BlockCipher* cipher, size_t tag_size = 16, size_t L = 3) :
+ CCM_Mode(cipher, tag_size, L) {}
+
+ void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
+
+ size_t output_length(size_t input_length) const override
+ {
+ BOTAN_ASSERT(input_length >= tag_size(), "Sufficient input");
+ return input_length - tag_size();
+ }
+
+ size_t minimum_final_size() const override { return tag_size(); }
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/modes/aead/ccm/info.txt b/comm/third_party/botan/src/lib/modes/aead/ccm/info.txt
new file mode 100644
index 0000000000..9341cea7fc
--- /dev/null
+++ b/comm/third_party/botan/src/lib/modes/aead/ccm/info.txt
@@ -0,0 +1,7 @@
+<defines>
+AEAD_CCM -> 20131128
+</defines>
+
+<requires>
+block
+</requires>
diff --git a/comm/third_party/botan/src/lib/modes/aead/chacha20poly1305/chacha20poly1305.cpp b/comm/third_party/botan/src/lib/modes/aead/chacha20poly1305/chacha20poly1305.cpp
new file mode 100644
index 0000000000..ca02ca5048
--- /dev/null
+++ b/comm/third_party/botan/src/lib/modes/aead/chacha20poly1305/chacha20poly1305.cpp
@@ -0,0 +1,171 @@
+/*
+* ChaCha20Poly1305 AEAD
+* (C) 2014,2016,2018 Jack Lloyd
+* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/chacha20poly1305.h>
+#include <botan/loadstor.h>
+
+namespace Botan {
+
+ChaCha20Poly1305_Mode::ChaCha20Poly1305_Mode() :
+ m_chacha(StreamCipher::create("ChaCha")),
+ m_poly1305(MessageAuthenticationCode::create("Poly1305"))
+ {
+ if(!m_chacha || !m_poly1305)
+ throw Algorithm_Not_Found("ChaCha20Poly1305");
+ }
+
+bool ChaCha20Poly1305_Mode::valid_nonce_length(size_t n) const
+ {
+ return (n == 8 || n == 12 || n == 24);
+ }
+
+void ChaCha20Poly1305_Mode::clear()
+ {
+ m_chacha->clear();
+ m_poly1305->clear();
+ reset();
+ }
+
+void ChaCha20Poly1305_Mode::reset()
+ {
+ m_ad.clear();
+ m_ctext_len = 0;
+ m_nonce_len = 0;
+ }
+
+void ChaCha20Poly1305_Mode::key_schedule(const uint8_t key[], size_t length)
+ {
+ m_chacha->set_key(key, length);
+ }
+
+void ChaCha20Poly1305_Mode::set_associated_data(const uint8_t ad[], size_t length)
+ {
+ if(m_ctext_len > 0 || m_nonce_len > 0)
+ throw Invalid_State("Cannot set AD for ChaCha20Poly1305 while processing a message");
+ m_ad.assign(ad, ad + length);
+ }
+
+void ChaCha20Poly1305_Mode::update_len(size_t len)
+ {
+ uint8_t len8[8] = { 0 };
+ store_le(static_cast<uint64_t>(len), len8);
+ m_poly1305->update(len8, 8);
+ }
+
+void ChaCha20Poly1305_Mode::start_msg(const uint8_t nonce[], size_t nonce_len)
+ {
+ if(!valid_nonce_length(nonce_len))
+ throw Invalid_IV_Length(name(), nonce_len);
+
+ m_ctext_len = 0;
+ m_nonce_len = nonce_len;
+
+ m_chacha->set_iv(nonce, nonce_len);
+
+ uint8_t first_block[64];
+ m_chacha->write_keystream(first_block, sizeof(first_block));
+
+ m_poly1305->set_key(first_block, 32);
+ // Remainder of first block is discarded
+ secure_scrub_memory(first_block, sizeof(first_block));
+
+ m_poly1305->update(m_ad);
+
+ if(cfrg_version())
+ {
+ if(m_ad.size() % 16)
+ {
+ const uint8_t zeros[16] = { 0 };
+ m_poly1305->update(zeros, 16 - m_ad.size() % 16);
+ }
+ }
+ else
+ {
+ update_len(m_ad.size());
+ }
+ }
+
+size_t ChaCha20Poly1305_Encryption::process(uint8_t buf[], size_t sz)
+ {
+ m_chacha->cipher1(buf, sz);
+ m_poly1305->update(buf, sz); // poly1305 of ciphertext
+ m_ctext_len += sz;
+ return sz;
+ }
+
+void ChaCha20Poly1305_Encryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
+ {
+ update(buffer, offset);
+ if(cfrg_version())
+ {
+ if(m_ctext_len % 16)
+ {
+ const uint8_t zeros[16] = { 0 };
+ m_poly1305->update(zeros, 16 - m_ctext_len % 16);
+ }
+ update_len(m_ad.size());
+ }
+ update_len(m_ctext_len);
+
+ buffer.resize(buffer.size() + tag_size());
+ m_poly1305->final(&buffer[buffer.size() - tag_size()]);
+ m_ctext_len = 0;
+ m_nonce_len = 0;
+ }
+
+size_t ChaCha20Poly1305_Decryption::process(uint8_t buf[], size_t sz)
+ {
+ m_poly1305->update(buf, sz); // poly1305 of ciphertext
+ m_chacha->cipher1(buf, sz);
+ m_ctext_len += sz;
+ return sz;
+ }
+
+void ChaCha20Poly1305_Decryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
+ {
+ BOTAN_ASSERT(buffer.size() >= offset, "Offset is sane");
+ const size_t sz = buffer.size() - offset;
+ uint8_t* buf = buffer.data() + offset;
+
+ BOTAN_ASSERT(sz >= tag_size(), "Have the tag as part of final input");
+
+ const size_t remaining = sz - tag_size();
+
+ if(remaining)
+ {
+ 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)
+ {
+ const uint8_t zeros[16] = { 0 };
+ m_poly1305->update(zeros, 16 - m_ctext_len % 16);
+ }
+ update_len(m_ad.size());
+ }
+
+ update_len(m_ctext_len);
+
+ uint8_t mac[16];
+ m_poly1305->final(mac);
+
+ const uint8_t* included_tag = &buf[remaining];
+
+ m_ctext_len = 0;
+ m_nonce_len = 0;
+
+ if(!constant_time_compare(mac, included_tag, tag_size()))
+ throw Invalid_Authentication_Tag("ChaCha20Poly1305 tag check failed");
+ buffer.resize(offset + remaining);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/modes/aead/chacha20poly1305/chacha20poly1305.h b/comm/third_party/botan/src/lib/modes/aead/chacha20poly1305/chacha20poly1305.h
new file mode 100644
index 0000000000..dbba58cc93
--- /dev/null
+++ b/comm/third_party/botan/src/lib/modes/aead/chacha20poly1305/chacha20poly1305.h
@@ -0,0 +1,104 @@
+/*
+* ChaCha20Poly1305 AEAD
+* (C) 2014 Jack Lloyd
+* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_AEAD_CHACHA20_POLY1305_H_
+#define BOTAN_AEAD_CHACHA20_POLY1305_H_
+
+#include <botan/aead.h>
+#include <botan/stream_cipher.h>
+#include <botan/mac.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(chacha20poly1305.h)
+
+namespace Botan {
+
+/**
+* Base class
+* See draft-irtf-cfrg-chacha20-poly1305-03 for specification
+* If a nonce of 64 bits is used the older version described in
+* draft-agl-tls-chacha20poly1305-04 is used instead.
+* If a nonce of 192 bits is used, XChaCha20Poly1305 is selected.
+*/
+class BOTAN_PUBLIC_API(2,0) ChaCha20Poly1305_Mode : public AEAD_Mode
+ {
+ public:
+ void set_associated_data(const uint8_t ad[], size_t ad_len) override;
+
+ bool associated_data_requires_key() const override { return false; }
+
+ std::string name() const override { return "ChaCha20Poly1305"; }
+
+ size_t update_granularity() const override { return 64; }
+
+ Key_Length_Specification key_spec() const override
+ { return Key_Length_Specification(32); }
+
+ bool valid_nonce_length(size_t n) const override;
+
+ size_t tag_size() const override { return 16; }
+
+ void clear() override;
+
+ void reset() override;
+
+ protected:
+ std::unique_ptr<StreamCipher> m_chacha;
+ std::unique_ptr<MessageAuthenticationCode> m_poly1305;
+
+ ChaCha20Poly1305_Mode();
+
+ secure_vector<uint8_t> m_ad;
+ size_t m_nonce_len = 0;
+ size_t m_ctext_len = 0;
+
+ bool cfrg_version() const { return m_nonce_len == 12 || m_nonce_len == 24; }
+ void update_len(size_t len);
+ private:
+ void start_msg(const uint8_t nonce[], size_t nonce_len) override;
+
+ void key_schedule(const uint8_t key[], size_t length) override;
+ };
+
+/**
+* ChaCha20Poly1305 Encryption
+*/
+class BOTAN_PUBLIC_API(2,0) ChaCha20Poly1305_Encryption final : public ChaCha20Poly1305_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; }
+
+ size_t process(uint8_t buf[], size_t size) override;
+
+ void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
+ };
+
+/**
+* ChaCha20Poly1305 Decryption
+*/
+class BOTAN_PUBLIC_API(2,0) ChaCha20Poly1305_Decryption final : public ChaCha20Poly1305_Mode
+ {
+ public:
+ size_t output_length(size_t input_length) const override
+ {
+ BOTAN_ASSERT(input_length >= tag_size(), "Sufficient input");
+ return input_length - tag_size();
+ }
+
+ size_t minimum_final_size() const override { return tag_size(); }
+
+ size_t process(uint8_t buf[], size_t size) override;
+
+ void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/modes/aead/chacha20poly1305/info.txt b/comm/third_party/botan/src/lib/modes/aead/chacha20poly1305/info.txt
new file mode 100644
index 0000000000..c3ea53db09
--- /dev/null
+++ b/comm/third_party/botan/src/lib/modes/aead/chacha20poly1305/info.txt
@@ -0,0 +1,8 @@
+<defines>
+AEAD_CHACHA20_POLY1305 -> 20180807
+</defines>
+
+<requires>
+chacha
+poly1305
+</requires>
diff --git a/comm/third_party/botan/src/lib/modes/aead/eax/eax.cpp b/comm/third_party/botan/src/lib/modes/aead/eax/eax.cpp
new file mode 100644
index 0000000000..4f6b540ffb
--- /dev/null
+++ b/comm/third_party/botan/src/lib/modes/aead/eax/eax.cpp
@@ -0,0 +1,194 @@
+/*
+* EAX Mode Encryption
+* (C) 1999-2007 Jack Lloyd
+* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/eax.h>
+#include <botan/cmac.h>
+#include <botan/ctr.h>
+
+namespace Botan {
+
+namespace {
+
+/*
+* EAX MAC-based PRF
+*/
+secure_vector<uint8_t> eax_prf(uint8_t tag, size_t block_size,
+ MessageAuthenticationCode& mac,
+ const uint8_t in[], size_t length)
+ {
+ for(size_t i = 0; i != block_size - 1; ++i)
+ {
+ mac.update(0);
+ }
+ mac.update(tag);
+ mac.update(in, length);
+ return mac.final();
+ }
+
+}
+
+/*
+* EAX_Mode Constructor
+*/
+EAX_Mode::EAX_Mode(BlockCipher* cipher, size_t tag_size) :
+ m_tag_size(tag_size),
+ m_cipher(cipher),
+ m_ctr(new CTR_BE(m_cipher->clone())),
+ m_cmac(new CMAC(m_cipher->clone()))
+ {
+ if(m_tag_size < 8 || m_tag_size > m_cmac->output_length())
+ throw Invalid_Argument(name() + ": Bad tag size " + std::to_string(tag_size));
+ }
+
+void EAX_Mode::clear()
+ {
+ m_cipher->clear();
+ m_ctr->clear();
+ m_cmac->clear();
+ reset();
+ }
+
+void EAX_Mode::reset()
+ {
+ m_ad_mac.clear();
+ m_nonce_mac.clear();
+
+ // Clear out any data added to the CMAC calculation
+ try {
+ m_cmac->final();
+ }
+ catch(Key_Not_Set&) {}
+ }
+
+std::string EAX_Mode::name() const
+ {
+ return (m_cipher->name() + "/EAX");
+ }
+
+size_t EAX_Mode::update_granularity() const
+ {
+ /*
+ * For EAX this actually can be as low as 1 but that causes problems
+ * for applications which use update_granularity as the buffer size.
+ */
+ return m_cipher->parallel_bytes();
+ }
+
+Key_Length_Specification EAX_Mode::key_spec() const
+ {
+ return m_cipher->key_spec();
+ }
+
+/*
+* Set the EAX key
+*/
+void EAX_Mode::key_schedule(const uint8_t key[], size_t length)
+ {
+ /*
+ * These could share the key schedule, which is one nice part of EAX,
+ * but it's much easier to ignore that here...
+ */
+ m_ctr->set_key(key, length);
+ m_cmac->set_key(key, length);
+ }
+
+/*
+* Set the EAX associated data
+*/
+void EAX_Mode::set_associated_data(const uint8_t ad[], size_t length)
+ {
+ if(m_nonce_mac.empty() == false)
+ throw Invalid_State("Cannot set AD for EAX while processing a message");
+ m_ad_mac = eax_prf(1, block_size(), *m_cmac, ad, length);
+ }
+
+void EAX_Mode::start_msg(const uint8_t nonce[], size_t nonce_len)
+ {
+ if(!valid_nonce_length(nonce_len))
+ throw Invalid_IV_Length(name(), nonce_len);
+
+ m_nonce_mac = eax_prf(0, block_size(), *m_cmac, nonce, nonce_len);
+
+ m_ctr->set_iv(m_nonce_mac.data(), m_nonce_mac.size());
+
+ for(size_t i = 0; i != block_size() - 1; ++i)
+ m_cmac->update(0);
+ m_cmac->update(2);
+ }
+
+size_t EAX_Encryption::process(uint8_t buf[], size_t sz)
+ {
+ BOTAN_STATE_CHECK(m_nonce_mac.size() > 0);
+ m_ctr->cipher(buf, buf, sz);
+ m_cmac->update(buf, sz);
+ return sz;
+ }
+
+void EAX_Encryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
+ {
+ BOTAN_ASSERT_NOMSG(m_nonce_mac.empty() == false);
+ update(buffer, offset);
+
+ secure_vector<uint8_t> data_mac = m_cmac->final();
+ xor_buf(data_mac, m_nonce_mac, data_mac.size());
+
+ if(m_ad_mac.empty())
+ {
+ m_ad_mac = eax_prf(1, block_size(), *m_cmac, nullptr, 0);
+ }
+
+ xor_buf(data_mac, m_ad_mac, data_mac.size());
+
+ buffer += std::make_pair(data_mac.data(), tag_size());
+ }
+
+size_t EAX_Decryption::process(uint8_t buf[], size_t sz)
+ {
+ BOTAN_STATE_CHECK(m_nonce_mac.size() > 0);
+ m_cmac->update(buf, sz);
+ m_ctr->cipher(buf, buf, sz);
+ return sz;
+ }
+
+void EAX_Decryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
+ {
+ BOTAN_ASSERT(buffer.size() >= offset, "Offset is sane");
+ const size_t sz = buffer.size() - offset;
+ uint8_t* buf = buffer.data() + offset;
+
+ BOTAN_ASSERT(sz >= tag_size(), "Have the tag as part of final input");
+
+ const size_t remaining = sz - tag_size();
+
+ if(remaining)
+ {
+ m_cmac->update(buf, remaining);
+ m_ctr->cipher(buf, buf, remaining);
+ }
+
+ const uint8_t* included_tag = &buf[remaining];
+
+ secure_vector<uint8_t> mac = m_cmac->final();
+ mac ^= m_nonce_mac;
+
+ if(m_ad_mac.empty())
+ {
+ m_ad_mac = eax_prf(1, block_size(), *m_cmac, nullptr, 0);
+ }
+
+ mac ^= m_ad_mac;
+
+ if(!constant_time_compare(mac.data(), included_tag, tag_size()))
+ throw Invalid_Authentication_Tag("EAX tag check failed");
+
+ buffer.resize(offset + remaining);
+
+ m_nonce_mac.clear();
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/modes/aead/eax/eax.h b/comm/third_party/botan/src/lib/modes/aead/eax/eax.h
new file mode 100644
index 0000000000..b9b02c192b
--- /dev/null
+++ b/comm/third_party/botan/src/lib/modes/aead/eax/eax.h
@@ -0,0 +1,119 @@
+/*
+* EAX Mode
+* (C) 1999-2007,2013 Jack Lloyd
+* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_AEAD_EAX_H_
+#define BOTAN_AEAD_EAX_H_
+
+#include <botan/aead.h>
+#include <botan/block_cipher.h>
+#include <botan/stream_cipher.h>
+#include <botan/mac.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(eax.h)
+
+namespace Botan {
+
+/**
+* EAX base class
+*/
+class BOTAN_PUBLIC_API(2,0) EAX_Mode : public AEAD_Mode
+ {
+ public:
+ void set_associated_data(const uint8_t ad[], size_t ad_len) override;
+
+ std::string name() const override;
+
+ size_t update_granularity() const override;
+
+ Key_Length_Specification key_spec() const override;
+
+ // EAX supports arbitrary nonce lengths
+ bool valid_nonce_length(size_t) const override { return true; }
+
+ size_t tag_size() const override { return m_tag_size; }
+
+ void clear() override;
+
+ void reset() override;
+
+ protected:
+ /**
+ * @param cipher the cipher to use
+ * @param tag_size is how big the auth tag will be
+ */
+ EAX_Mode(BlockCipher* cipher, size_t tag_size);
+
+ size_t block_size() const { return m_cipher->block_size(); }
+
+ size_t m_tag_size;
+
+ std::unique_ptr<BlockCipher> m_cipher;
+ std::unique_ptr<StreamCipher> m_ctr;
+ std::unique_ptr<MessageAuthenticationCode> m_cmac;
+
+ secure_vector<uint8_t> m_ad_mac;
+
+ secure_vector<uint8_t> m_nonce_mac;
+ private:
+ void start_msg(const uint8_t nonce[], size_t nonce_len) override;
+
+ void key_schedule(const uint8_t key[], size_t length) override;
+ };
+
+/**
+* EAX Encryption
+*/
+class BOTAN_PUBLIC_API(2,0) EAX_Encryption final : public EAX_Mode
+ {
+ public:
+ /**
+ * @param cipher a 128-bit block cipher
+ * @param tag_size is how big the auth tag will be
+ */
+ EAX_Encryption(BlockCipher* cipher, size_t tag_size = 0) :
+ EAX_Mode(cipher, tag_size) {}
+
+ size_t output_length(size_t input_length) const override
+ { return input_length + tag_size(); }
+
+ size_t minimum_final_size() const override { return 0; }
+
+ size_t process(uint8_t buf[], size_t size) override;
+
+ void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
+ };
+
+/**
+* EAX Decryption
+*/
+class BOTAN_PUBLIC_API(2,0) EAX_Decryption final : public EAX_Mode
+ {
+ public:
+ /**
+ * @param cipher a 128-bit block cipher
+ * @param tag_size is how big the auth tag will be
+ */
+ EAX_Decryption(BlockCipher* cipher, size_t tag_size = 0) :
+ EAX_Mode(cipher, tag_size) {}
+
+ size_t output_length(size_t input_length) const override
+ {
+ BOTAN_ASSERT(input_length >= tag_size(), "Sufficient input");
+ return input_length - tag_size();
+ }
+
+ size_t minimum_final_size() const override { return tag_size(); }
+
+ size_t process(uint8_t buf[], size_t size) override;
+
+ void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/modes/aead/eax/info.txt b/comm/third_party/botan/src/lib/modes/aead/eax/info.txt
new file mode 100644
index 0000000000..6bc01ff704
--- /dev/null
+++ b/comm/third_party/botan/src/lib/modes/aead/eax/info.txt
@@ -0,0 +1,8 @@
+<defines>
+AEAD_EAX -> 20131128
+</defines>
+
+<requires>
+cmac
+ctr
+</requires>
diff --git a/comm/third_party/botan/src/lib/modes/aead/gcm/gcm.cpp b/comm/third_party/botan/src/lib/modes/aead/gcm/gcm.cpp
new file mode 100644
index 0000000000..4b9c5b7204
--- /dev/null
+++ b/comm/third_party/botan/src/lib/modes/aead/gcm/gcm.cpp
@@ -0,0 +1,179 @@
+/*
+* GCM Mode Encryption
+* (C) 2013,2015 Jack Lloyd
+* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/gcm.h>
+#include <botan/ghash.h>
+#include <botan/block_cipher.h>
+#include <botan/ctr.h>
+
+namespace Botan {
+
+/*
+* GCM_Mode Constructor
+*/
+GCM_Mode::GCM_Mode(BlockCipher* cipher, size_t tag_size) :
+ m_tag_size(tag_size),
+ m_cipher_name(cipher->name()),
+ m_ctr(new CTR_BE(cipher, 4)),
+ m_ghash(new GHASH)
+ {
+ if(cipher->block_size() != GCM_BS)
+ throw Invalid_Argument("Invalid block cipher for GCM");
+
+ /* We allow any of the values 128, 120, 112, 104, or 96 bits as a tag size */
+ /* 64 bit tag is still supported but deprecated and will be removed in the future */
+ if(m_tag_size != 8 && (m_tag_size < 12 || m_tag_size > 16))
+ throw Invalid_Argument(name() + ": Bad tag size " + std::to_string(m_tag_size));
+ }
+
+GCM_Mode::~GCM_Mode() { /* for unique_ptr */ }
+
+void GCM_Mode::clear()
+ {
+ m_ctr->clear();
+ m_ghash->clear();
+ reset();
+ }
+
+void GCM_Mode::reset()
+ {
+ m_ghash->reset();
+ }
+
+std::string GCM_Mode::name() const
+ {
+ return (m_cipher_name + "/GCM(" + std::to_string(tag_size()) + ")");
+ }
+
+std::string GCM_Mode::provider() const
+ {
+ return m_ghash->provider();
+ }
+
+size_t GCM_Mode::update_granularity() const
+ {
+ return GCM_BS * std::max<size_t>(2, BOTAN_BLOCK_CIPHER_PAR_MULT);
+ }
+
+bool GCM_Mode::valid_nonce_length(size_t len) const
+ {
+ // GCM does not support empty nonces
+ return (len > 0);
+ }
+
+Key_Length_Specification GCM_Mode::key_spec() const
+ {
+ return m_ctr->key_spec();
+ }
+
+void GCM_Mode::key_schedule(const uint8_t key[], size_t keylen)
+ {
+ m_ctr->set_key(key, keylen);
+
+ const std::vector<uint8_t> zeros(GCM_BS);
+ m_ctr->set_iv(zeros.data(), zeros.size());
+
+ secure_vector<uint8_t> H(GCM_BS);
+ m_ctr->encipher(H);
+ m_ghash->set_key(H);
+ }
+
+void GCM_Mode::set_associated_data(const uint8_t ad[], size_t ad_len)
+ {
+ m_ghash->set_associated_data(ad, ad_len);
+ }
+
+void GCM_Mode::start_msg(const uint8_t nonce[], size_t nonce_len)
+ {
+ if(!valid_nonce_length(nonce_len))
+ 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());
+
+ if(nonce_len == 12)
+ {
+ copy_mem(m_y0.data(), nonce, nonce_len);
+ m_y0[15] = 1;
+ }
+ else
+ {
+ m_ghash->nonce_hash(m_y0, nonce, nonce_len);
+ }
+
+ m_ctr->set_iv(m_y0.data(), m_y0.size());
+
+ clear_mem(m_y0.data(), m_y0.size());
+ m_ctr->encipher(m_y0);
+
+ m_ghash->start(m_y0.data(), m_y0.size());
+ clear_mem(m_y0.data(), m_y0.size());
+ }
+
+size_t GCM_Encryption::process(uint8_t buf[], size_t sz)
+ {
+ BOTAN_ARG_CHECK(sz % update_granularity() == 0, "Invalid buffer size");
+ m_ctr->cipher(buf, buf, sz);
+ m_ghash->update(buf, sz);
+ return sz;
+ }
+
+void GCM_Encryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
+ {
+ BOTAN_ARG_CHECK(offset <= buffer.size(), "Invalid offset");
+ const size_t sz = buffer.size() - offset;
+ uint8_t* buf = buffer.data() + offset;
+
+ m_ctr->cipher(buf, buf, sz);
+ m_ghash->update(buf, sz);
+
+ uint8_t mac[16] = { 0 };
+ m_ghash->final(mac, tag_size());
+ buffer += std::make_pair(mac, tag_size());
+ }
+
+size_t GCM_Decryption::process(uint8_t buf[], size_t sz)
+ {
+ BOTAN_ARG_CHECK(sz % update_granularity() == 0, "Invalid buffer size");
+ m_ghash->update(buf, sz);
+ m_ctr->cipher(buf, buf, sz);
+ return sz;
+ }
+
+void GCM_Decryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
+ {
+ BOTAN_ARG_CHECK(offset <= buffer.size(), "Invalid offset");
+ const size_t sz = buffer.size() - offset;
+ uint8_t* buf = buffer.data() + offset;
+
+ if(sz < tag_size())
+ throw Decoding_Error("Insufficient input for GCM decryption, tag missing");
+
+ const size_t remaining = sz - tag_size();
+
+ // handle any final input before the tag
+ if(remaining)
+ {
+ m_ghash->update(buf, remaining);
+ m_ctr->cipher(buf, buf, remaining);
+ }
+
+ uint8_t mac[16] = { 0 };
+ m_ghash->final(mac, tag_size());
+
+ const uint8_t* included_tag = &buffer[remaining+offset];
+
+ if(!constant_time_compare(mac, included_tag, tag_size()))
+ throw Invalid_Authentication_Tag("GCM tag check failed");
+
+ buffer.resize(offset + remaining);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/modes/aead/gcm/gcm.h b/comm/third_party/botan/src/lib/modes/aead/gcm/gcm.h
new file mode 100644
index 0000000000..fe3c405721
--- /dev/null
+++ b/comm/third_party/botan/src/lib/modes/aead/gcm/gcm.h
@@ -0,0 +1,117 @@
+/*
+* GCM Mode
+* (C) 2013 Jack Lloyd
+* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_AEAD_GCM_H_
+#define BOTAN_AEAD_GCM_H_
+
+#include <botan/aead.h>
+#include <botan/sym_algo.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(gcm.h)
+
+namespace Botan {
+
+class BlockCipher;
+class StreamCipher;
+class GHASH;
+
+/**
+* GCM Mode
+*/
+class BOTAN_PUBLIC_API(2,0) GCM_Mode : public AEAD_Mode
+ {
+ public:
+ void set_associated_data(const uint8_t ad[], size_t ad_len) override;
+
+ std::string name() const override;
+
+ size_t update_granularity() const override;
+
+ Key_Length_Specification key_spec() const override;
+
+ bool valid_nonce_length(size_t len) const override;
+
+ size_t tag_size() const override { return m_tag_size; }
+
+ void clear() override;
+
+ void reset() override;
+
+ std::string provider() const override;
+ protected:
+ GCM_Mode(BlockCipher* cipher, size_t tag_size);
+
+ ~GCM_Mode();
+
+ static const size_t GCM_BS = 16;
+
+ const size_t m_tag_size;
+ const std::string m_cipher_name;
+
+ std::unique_ptr<StreamCipher> m_ctr;
+ std::unique_ptr<GHASH> m_ghash;
+ private:
+ void start_msg(const uint8_t nonce[], size_t nonce_len) override;
+
+ void key_schedule(const uint8_t key[], size_t length) override;
+
+ secure_vector<uint8_t> m_y0;
+ };
+
+/**
+* GCM Encryption
+*/
+class BOTAN_PUBLIC_API(2,0) GCM_Encryption final : public GCM_Mode
+ {
+ public:
+ /**
+ * @param cipher the 128 bit block cipher to use
+ * @param tag_size is how big the auth tag will be
+ */
+ GCM_Encryption(BlockCipher* cipher, size_t tag_size = 16) :
+ GCM_Mode(cipher, tag_size) {}
+
+ size_t output_length(size_t input_length) const override
+ { return input_length + tag_size(); }
+
+ size_t minimum_final_size() const override { return 0; }
+
+ size_t process(uint8_t buf[], size_t size) override;
+
+ void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
+ };
+
+/**
+* GCM Decryption
+*/
+class BOTAN_PUBLIC_API(2,0) GCM_Decryption final : public GCM_Mode
+ {
+ public:
+ /**
+ * @param cipher the 128 bit block cipher to use
+ * @param tag_size is how big the auth tag will be
+ */
+ GCM_Decryption(BlockCipher* cipher, size_t tag_size = 16) :
+ GCM_Mode(cipher, tag_size) {}
+
+ size_t output_length(size_t input_length) const override
+ {
+ BOTAN_ASSERT(input_length >= tag_size(), "Sufficient input");
+ return input_length - tag_size();
+ }
+
+ size_t minimum_final_size() const override { return tag_size(); }
+
+ size_t process(uint8_t buf[], size_t size) override;
+
+ void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/modes/aead/gcm/info.txt b/comm/third_party/botan/src/lib/modes/aead/gcm/info.txt
new file mode 100644
index 0000000000..94eb09ab64
--- /dev/null
+++ b/comm/third_party/botan/src/lib/modes/aead/gcm/info.txt
@@ -0,0 +1,9 @@
+<defines>
+AEAD_GCM -> 20131128
+</defines>
+
+<requires>
+block
+ctr
+ghash
+</requires>
diff --git a/comm/third_party/botan/src/lib/modes/aead/info.txt b/comm/third_party/botan/src/lib/modes/aead/info.txt
new file mode 100644
index 0000000000..15106e46ab
--- /dev/null
+++ b/comm/third_party/botan/src/lib/modes/aead/info.txt
@@ -0,0 +1,3 @@
+<defines>
+AEAD_MODES -> 20131128
+</defines>
diff --git a/comm/third_party/botan/src/lib/modes/aead/ocb/info.txt b/comm/third_party/botan/src/lib/modes/aead/ocb/info.txt
new file mode 100644
index 0000000000..4e16e23624
--- /dev/null
+++ b/comm/third_party/botan/src/lib/modes/aead/ocb/info.txt
@@ -0,0 +1,8 @@
+<defines>
+AEAD_OCB -> 20131128
+</defines>
+
+<requires>
+block
+poly_dbl
+</requires>
diff --git a/comm/third_party/botan/src/lib/modes/aead/ocb/ocb.cpp b/comm/third_party/botan/src/lib/modes/aead/ocb/ocb.cpp
new file mode 100644
index 0000000000..6c16203296
--- /dev/null
+++ b/comm/third_party/botan/src/lib/modes/aead/ocb/ocb.cpp
@@ -0,0 +1,533 @@
+/*
+* OCB Mode
+* (C) 2013,2017 Jack Lloyd
+* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/ocb.h>
+#include <botan/block_cipher.h>
+#include <botan/internal/poly_dbl.h>
+#include <botan/internal/bit_ops.h>
+
+namespace Botan {
+
+// Has to be in Botan namespace so unique_ptr can reference it
+class L_computer final
+ {
+ public:
+ explicit L_computer(const BlockCipher& cipher) :
+ m_BS(cipher.block_size()),
+ m_max_blocks(cipher.parallel_bytes() / m_BS)
+ {
+ m_L_star.resize(m_BS);
+ cipher.encrypt(m_L_star);
+ m_L_dollar = poly_double(star());
+ m_L.push_back(poly_double(dollar()));
+
+ while(m_L.size() < 8)
+ m_L.push_back(poly_double(m_L.back()));
+
+ m_offset_buf.resize(m_BS * m_max_blocks);
+ }
+
+ void init(const secure_vector<uint8_t>& offset)
+ {
+ m_offset = offset;
+ }
+
+ bool initialized() const { return m_offset.empty() == false; }
+
+ const secure_vector<uint8_t>& star() const { return m_L_star; }
+ const secure_vector<uint8_t>& dollar() const { return m_L_dollar; }
+ const secure_vector<uint8_t>& offset() const { return m_offset; }
+
+ const secure_vector<uint8_t>& get(size_t i) const
+ {
+ while(m_L.size() <= i)
+ m_L.push_back(poly_double(m_L.back()));
+
+ return m_L[i];
+ }
+
+ const uint8_t*
+ compute_offsets(size_t block_index, size_t blocks)
+ {
+ BOTAN_ASSERT(blocks <= m_max_blocks, "OCB offsets");
+
+ uint8_t* offsets = m_offset_buf.data();
+
+ if(block_index % 4 == 0)
+ {
+ const secure_vector<uint8_t>& L0 = get(0);
+ const secure_vector<uint8_t>& L1 = get(1);
+
+ while(blocks >= 4)
+ {
+ // ntz(4*i+1) == 0
+ // ntz(4*i+2) == 1
+ // ntz(4*i+3) == 0
+ block_index += 4;
+ const size_t ntz4 = var_ctz32(static_cast<uint32_t>(block_index));
+
+ xor_buf(offsets, m_offset.data(), L0.data(), m_BS);
+ offsets += m_BS;
+
+ xor_buf(offsets, offsets - m_BS, L1.data(), m_BS);
+ offsets += m_BS;
+
+ xor_buf(m_offset.data(), L1.data(), m_BS);
+ copy_mem(offsets, m_offset.data(), m_BS);
+ offsets += m_BS;
+
+ xor_buf(m_offset.data(), get(ntz4).data(), m_BS);
+ copy_mem(offsets, m_offset.data(), m_BS);
+ offsets += m_BS;
+
+ blocks -= 4;
+ }
+ }
+
+ for(size_t i = 0; i != blocks; ++i)
+ { // could be done in parallel
+ const size_t ntz = var_ctz32(static_cast<uint32_t>(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;
+ }
+
+ return m_offset_buf.data();
+ }
+
+ private:
+ secure_vector<uint8_t> poly_double(const secure_vector<uint8_t>& in) const
+ {
+ secure_vector<uint8_t> out(in.size());
+ poly_double_n(out.data(), in.data(), out.size());
+ return out;
+ }
+
+ const size_t m_BS, m_max_blocks;
+ secure_vector<uint8_t> m_L_dollar, m_L_star;
+ secure_vector<uint8_t> m_offset;
+ mutable std::vector<secure_vector<uint8_t>> m_L;
+ secure_vector<uint8_t> m_offset_buf;
+ };
+
+namespace {
+
+/*
+* OCB's HASH
+*/
+secure_vector<uint8_t> ocb_hash(const L_computer& L,
+ const BlockCipher& cipher,
+ const uint8_t ad[], size_t ad_len)
+ {
+ const size_t BS = cipher.block_size();
+ secure_vector<uint8_t> sum(BS);
+ secure_vector<uint8_t> offset(BS);
+
+ secure_vector<uint8_t> buf(BS);
+
+ const size_t ad_blocks = (ad_len / BS);
+ const size_t ad_remainder = (ad_len % BS);
+
+ for(size_t i = 0; i != ad_blocks; ++i)
+ {
+ // this loop could run in parallel
+ offset ^= L.get(var_ctz32(static_cast<uint32_t>(i+1)));
+ buf = offset;
+ xor_buf(buf.data(), &ad[BS*i], BS);
+ cipher.encrypt(buf);
+ sum ^= buf;
+ }
+
+ if(ad_remainder)
+ {
+ offset ^= L.star();
+ buf = offset;
+ xor_buf(buf.data(), &ad[BS*ad_blocks], ad_remainder);
+ buf[ad_remainder] ^= 0x80;
+ cipher.encrypt(buf);
+ sum ^= buf;
+ }
+
+ return sum;
+ }
+
+}
+
+OCB_Mode::OCB_Mode(BlockCipher* cipher, size_t tag_size) :
+ m_cipher(cipher),
+ m_checksum(m_cipher->parallel_bytes()),
+ m_ad_hash(m_cipher->block_size()),
+ m_tag_size(tag_size),
+ m_block_size(m_cipher->block_size()),
+ m_par_blocks(m_cipher->parallel_bytes() / m_block_size)
+ {
+ const size_t BS = block_size();
+
+ /*
+ * draft-krovetz-ocb-wide-d1 specifies OCB for several other block
+ * sizes but only 128, 192, 256 and 512 bit are currently supported
+ * by this implementation.
+ */
+ BOTAN_ARG_CHECK(BS == 16 || BS == 24 || BS == 32 || BS == 64,
+ "Invalid block size for OCB");
+
+ BOTAN_ARG_CHECK(m_tag_size % 4 == 0 &&
+ m_tag_size >= 8 && m_tag_size <= BS &&
+ m_tag_size <= 32,
+ "Invalid OCB tag length");
+ }
+
+OCB_Mode::~OCB_Mode() { /* for unique_ptr destructor */ }
+
+void OCB_Mode::clear()
+ {
+ m_cipher->clear();
+ m_L.reset(); // add clear here?
+ reset();
+ }
+
+void OCB_Mode::reset()
+ {
+ m_block_index = 0;
+ zeroise(m_ad_hash);
+ zeroise(m_checksum);
+ m_last_nonce.clear();
+ m_stretch.clear();
+ }
+
+bool OCB_Mode::valid_nonce_length(size_t length) const
+ {
+ if(length == 0)
+ return false;
+ if(block_size() == 16)
+ return length < 16;
+ else
+ return length < (block_size() - 1);
+ }
+
+std::string OCB_Mode::name() const
+ {
+ return m_cipher->name() + "/OCB"; // include tag size?
+ }
+
+size_t OCB_Mode::update_granularity() const
+ {
+ return (m_par_blocks * block_size());
+ }
+
+Key_Length_Specification OCB_Mode::key_spec() const
+ {
+ return m_cipher->key_spec();
+ }
+
+void OCB_Mode::key_schedule(const uint8_t key[], size_t length)
+ {
+ m_cipher->set_key(key, length);
+ m_L.reset(new L_computer(*m_cipher));
+ }
+
+void OCB_Mode::set_associated_data(const uint8_t ad[], size_t ad_len)
+ {
+ verify_key_set(m_L != nullptr);
+ m_ad_hash = ocb_hash(*m_L, *m_cipher, ad, ad_len);
+ }
+
+const secure_vector<uint8_t>&
+OCB_Mode::update_nonce(const uint8_t nonce[], size_t nonce_len)
+ {
+ const size_t BS = block_size();
+
+ BOTAN_ASSERT(BS == 16 || BS == 24 || BS == 32 || BS == 64,
+ "OCB block size is supported");
+
+ const size_t MASKLEN = (BS == 16 ? 6 : ((BS == 24) ? 7 : 8));
+
+ const uint8_t BOTTOM_MASK =
+ static_cast<uint8_t>((static_cast<uint16_t>(1) << MASKLEN) - 1);
+
+ m_nonce_buf.resize(BS);
+ clear_mem(&m_nonce_buf[0], m_nonce_buf.size());
+
+ copy_mem(&m_nonce_buf[BS - nonce_len], nonce, nonce_len);
+ m_nonce_buf[0] = static_cast<uint8_t>(((tag_size()*8) % (BS*8)) << (BS <= 16 ? 1 : 0));
+
+ m_nonce_buf[BS - nonce_len - 1] ^= 1;
+
+ const uint8_t bottom = m_nonce_buf[BS-1] & BOTTOM_MASK;
+ m_nonce_buf[BS-1] &= ~BOTTOM_MASK;
+
+ const bool need_new_stretch = (m_last_nonce != m_nonce_buf);
+
+ if(need_new_stretch)
+ {
+ m_last_nonce = m_nonce_buf;
+
+ m_cipher->encrypt(m_nonce_buf);
+
+ /*
+ The loop bounds (BS vs BS/2) are derived from the relation
+ between the block size and the MASKLEN. Using the terminology
+ of draft-krovetz-ocb-wide, we have to derive enough bits in
+ ShiftedKtop to read up to BLOCKLEN+bottom bits from Stretch.
+
+ +----------+---------+-------+---------+
+ | BLOCKLEN | RESIDUE | SHIFT | MASKLEN |
+ +----------+---------+-------+---------+
+ | 32 | 141 | 17 | 4 |
+ | 64 | 27 | 25 | 5 |
+ | 96 | 1601 | 33 | 6 |
+ | 128 | 135 | 8 | 6 |
+ | 192 | 135 | 40 | 7 |
+ | 256 | 1061 | 1 | 8 |
+ | 384 | 4109 | 80 | 8 |
+ | 512 | 293 | 176 | 8 |
+ | 1024 | 524355 | 352 | 9 |
+ +----------+---------+-------+---------+
+ */
+ if(BS == 16)
+ {
+ for(size_t i = 0; i != BS / 2; ++i)
+ m_nonce_buf.push_back(m_nonce_buf[i] ^ m_nonce_buf[i+1]);
+ }
+ else if(BS == 24)
+ {
+ for(size_t i = 0; i != 16; ++i)
+ m_nonce_buf.push_back(m_nonce_buf[i] ^ m_nonce_buf[i+5]);
+ }
+ else if(BS == 32)
+ {
+ for(size_t i = 0; i != BS; ++i)
+ m_nonce_buf.push_back(m_nonce_buf[i] ^ (m_nonce_buf[i] << 1) ^ (m_nonce_buf[i+1] >> 7));
+ }
+ else if(BS == 64)
+ {
+ for(size_t i = 0; i != BS / 2; ++i)
+ m_nonce_buf.push_back(m_nonce_buf[i] ^ m_nonce_buf[i+22]);
+ }
+
+ m_stretch = m_nonce_buf;
+ }
+
+ // now set the offset from stretch and bottom
+ const size_t shift_bytes = bottom / 8;
+ const size_t shift_bits = bottom % 8;
+
+ BOTAN_ASSERT(m_stretch.size() >= BS + shift_bytes + 1, "Size ok");
+
+ m_offset.resize(BS);
+ for(size_t i = 0; i != BS; ++i)
+ {
+ m_offset[i] = (m_stretch[i+shift_bytes] << shift_bits);
+ m_offset[i] |= (m_stretch[i+shift_bytes+1] >> (8-shift_bits));
+ }
+
+ return m_offset;
+ }
+
+void OCB_Mode::start_msg(const uint8_t nonce[], size_t nonce_len)
+ {
+ if(!valid_nonce_length(nonce_len))
+ throw Invalid_IV_Length(name(), nonce_len);
+
+ verify_key_set(m_L != nullptr);
+
+ m_L->init(update_nonce(nonce, nonce_len));
+ zeroise(m_checksum);
+ m_block_index = 0;
+ }
+
+void OCB_Encryption::encrypt(uint8_t buffer[], size_t blocks)
+ {
+ verify_key_set(m_L != nullptr);
+ BOTAN_STATE_CHECK(m_L->initialized());
+
+ const size_t BS = block_size();
+
+ while(blocks)
+ {
+ 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);
+
+ xor_buf(m_checksum.data(), buffer, proc_bytes);
+
+ m_cipher->encrypt_n_xex(buffer, offsets, proc_blocks);
+
+ buffer += proc_bytes;
+ blocks -= proc_blocks;
+ m_block_index += proc_blocks;
+ }
+ }
+
+size_t OCB_Encryption::process(uint8_t buf[], size_t sz)
+ {
+ BOTAN_ASSERT(sz % update_granularity() == 0, "Invalid OCB input size");
+ encrypt(buf, sz / block_size());
+ return sz;
+ }
+
+void OCB_Encryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
+ {
+ verify_key_set(m_L != nullptr);
+
+ const size_t BS = block_size();
+
+ BOTAN_ASSERT(buffer.size() >= offset, "Offset is sane");
+ const size_t sz = buffer.size() - offset;
+ uint8_t* buf = buffer.data() + offset;
+
+ secure_vector<uint8_t> mac(BS);
+
+ if(sz)
+ {
+ 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)
+ {
+ BOTAN_ASSERT(remainder_bytes < BS, "Only a partial block left");
+ uint8_t* remainder = &buf[sz - remainder_bytes];
+
+ xor_buf(m_checksum.data(), remainder, remainder_bytes);
+ m_checksum[remainder_bytes] ^= 0x80;
+
+ // Offset_*
+ mac ^= m_L->star();
+
+ secure_vector<uint8_t> pad(BS);
+ m_cipher->encrypt(mac, pad);
+ xor_buf(remainder, pad.data(), remainder_bytes);
+ }
+ }
+ else
+ {
+ mac = m_L->offset();
+ }
+
+ // now compute the tag
+
+ // fold checksum
+ for(size_t i = 0; i != m_checksum.size(); i += BS)
+ {
+ xor_buf(mac.data(), m_checksum.data() + i, BS);
+ }
+
+ xor_buf(mac.data(), m_L->dollar().data(), BS);
+ m_cipher->encrypt(mac);
+ xor_buf(mac.data(), m_ad_hash.data(), BS);
+
+ buffer += std::make_pair(mac.data(), tag_size());
+
+ zeroise(m_checksum);
+ m_block_index = 0;
+ }
+
+void OCB_Decryption::decrypt(uint8_t buffer[], size_t blocks)
+ {
+ verify_key_set(m_L != nullptr);
+ BOTAN_STATE_CHECK(m_L->initialized());
+
+ const size_t BS = block_size();
+
+ while(blocks)
+ {
+ 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(m_checksum.data(), buffer, proc_bytes);
+
+ buffer += proc_bytes;
+ blocks -= proc_blocks;
+ m_block_index += proc_blocks;
+ }
+ }
+
+size_t OCB_Decryption::process(uint8_t buf[], size_t sz)
+ {
+ BOTAN_ASSERT(sz % update_granularity() == 0, "Invalid OCB input size");
+ decrypt(buf, sz / block_size());
+ return sz;
+ }
+
+void OCB_Decryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
+ {
+ verify_key_set(m_L != nullptr);
+
+ const size_t BS = block_size();
+
+ BOTAN_ASSERT(buffer.size() >= offset, "Offset is sane");
+ const size_t sz = buffer.size() - offset;
+ uint8_t* buf = buffer.data() + offset;
+
+ BOTAN_ASSERT(sz >= tag_size(), "We have the tag");
+
+ const size_t remaining = sz - tag_size();
+
+ secure_vector<uint8_t> mac(BS);
+
+ if(remaining)
+ {
+ 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)
+ {
+ BOTAN_ASSERT(final_bytes < BS, "Only a partial block left");
+
+ uint8_t* remainder = &buf[remaining - final_bytes];
+
+ mac ^= m_L->star();
+ secure_vector<uint8_t> pad(BS);
+ m_cipher->encrypt(mac, pad); // P_*
+ xor_buf(remainder, pad.data(), final_bytes);
+
+ xor_buf(m_checksum.data(), remainder, final_bytes);
+ m_checksum[final_bytes] ^= 0x80;
+ }
+ }
+ else
+ mac = m_L->offset();
+
+ // compute the mac
+
+ // fold checksum
+ for(size_t i = 0; i != m_checksum.size(); i += BS)
+ {
+ xor_buf(mac.data(), m_checksum.data() + i, BS);
+ }
+
+ mac ^= m_L->dollar();
+ m_cipher->encrypt(mac);
+ mac ^= m_ad_hash;
+
+ // reset state
+ zeroise(m_checksum);
+ m_block_index = 0;
+
+ // compare mac
+ const uint8_t* included_tag = &buf[remaining];
+
+ if(!constant_time_compare(mac.data(), included_tag, tag_size()))
+ throw Invalid_Authentication_Tag("OCB tag check failed");
+
+ // remove tag from end of message
+ buffer.resize(remaining + offset);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/modes/aead/ocb/ocb.h b/comm/third_party/botan/src/lib/modes/aead/ocb/ocb.h
new file mode 100644
index 0000000000..4029fcda25
--- /dev/null
+++ b/comm/third_party/botan/src/lib/modes/aead/ocb/ocb.h
@@ -0,0 +1,137 @@
+/*
+* OCB Mode
+* (C) 2013,2014 Jack Lloyd
+* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_AEAD_OCB_H_
+#define BOTAN_AEAD_OCB_H_
+
+#include <botan/aead.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(ocb.h)
+
+namespace Botan {
+
+class BlockCipher;
+class L_computer;
+
+/**
+* OCB Mode (base class for OCB_Encryption and OCB_Decryption). Note
+* that OCB is patented, but is freely licensed in some circumstances.
+*
+* @see "The OCB Authenticated-Encryption Algorithm" RFC 7253
+* https://tools.ietf.org/html/rfc7253
+* @see "OCB For Block Ciphers Without 128-Bit Blocks"
+* (draft-krovetz-ocb-wide-d3) for the extension of OCB to
+* block ciphers with larger block sizes.
+* @see Free Licenses http://www.cs.ucdavis.edu/~rogaway/ocb/license.htm
+* @see OCB home page http://www.cs.ucdavis.edu/~rogaway/ocb
+*/
+class BOTAN_PUBLIC_API(2,0) OCB_Mode : public AEAD_Mode
+ {
+ public:
+ void set_associated_data(const uint8_t ad[], size_t ad_len) override;
+
+ std::string name() const override;
+
+ size_t update_granularity() const override;
+
+ Key_Length_Specification key_spec() const override;
+
+ bool valid_nonce_length(size_t) const override;
+
+ size_t tag_size() const override { return m_tag_size; }
+
+ void clear() override;
+
+ void reset() override;
+
+ ~OCB_Mode();
+ protected:
+ /**
+ * @param cipher the block cipher to use
+ * @param tag_size is how big the auth tag will be
+ */
+ OCB_Mode(BlockCipher* cipher, size_t tag_size);
+
+ size_t block_size() const { return m_block_size; }
+ size_t par_blocks() const { return m_par_blocks; }
+ size_t par_bytes() const { return m_checksum.size(); }
+
+ // fixme make these private
+ std::unique_ptr<BlockCipher> m_cipher;
+ std::unique_ptr<L_computer> m_L;
+
+ size_t m_block_index = 0;
+
+ secure_vector<uint8_t> m_checksum;
+ secure_vector<uint8_t> m_ad_hash;
+ private:
+ void start_msg(const uint8_t nonce[], size_t nonce_len) override;
+
+ void key_schedule(const uint8_t key[], size_t length) override;
+
+ const secure_vector<uint8_t>& update_nonce(const uint8_t nonce[], size_t nonce_len);
+
+ const size_t m_tag_size;
+ const size_t m_block_size;
+ const size_t m_par_blocks;
+ secure_vector<uint8_t> m_last_nonce;
+ secure_vector<uint8_t> m_stretch;
+ secure_vector<uint8_t> m_nonce_buf;
+ secure_vector<uint8_t> m_offset;
+ };
+
+class BOTAN_PUBLIC_API(2,0) OCB_Encryption final : public OCB_Mode
+ {
+ public:
+ /**
+ * @param cipher the block cipher to use
+ * @param tag_size is how big the auth tag will be
+ */
+ OCB_Encryption(BlockCipher* cipher, size_t tag_size = 16) :
+ OCB_Mode(cipher, tag_size) {}
+
+ size_t output_length(size_t input_length) const override
+ { return input_length + tag_size(); }
+
+ size_t minimum_final_size() const override { return 0; }
+
+ size_t process(uint8_t buf[], size_t size) override;
+
+ void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
+ private:
+ void encrypt(uint8_t input[], size_t blocks);
+ };
+
+class BOTAN_PUBLIC_API(2,0) OCB_Decryption final : public OCB_Mode
+ {
+ public:
+ /**
+ * @param cipher the block cipher to use
+ * @param tag_size is how big the auth tag will be
+ */
+ OCB_Decryption(BlockCipher* cipher, size_t tag_size = 16) :
+ OCB_Mode(cipher, tag_size) {}
+
+ size_t output_length(size_t input_length) const override
+ {
+ BOTAN_ASSERT(input_length >= tag_size(), "Sufficient input");
+ return input_length - tag_size();
+ }
+
+ size_t minimum_final_size() const override { return tag_size(); }
+
+ size_t process(uint8_t buf[], size_t size) override;
+
+ void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
+ private:
+ void decrypt(uint8_t input[], size_t blocks);
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/modes/aead/siv/info.txt b/comm/third_party/botan/src/lib/modes/aead/siv/info.txt
new file mode 100644
index 0000000000..4894896665
--- /dev/null
+++ b/comm/third_party/botan/src/lib/modes/aead/siv/info.txt
@@ -0,0 +1,9 @@
+<defines>
+AEAD_SIV -> 20131202
+</defines>
+
+<requires>
+cmac
+ctr
+poly_dbl
+</requires>
diff --git a/comm/third_party/botan/src/lib/modes/aead/siv/siv.cpp b/comm/third_party/botan/src/lib/modes/aead/siv/siv.cpp
new file mode 100644
index 0000000000..8bd82a4684
--- /dev/null
+++ b/comm/third_party/botan/src/lib/modes/aead/siv/siv.cpp
@@ -0,0 +1,211 @@
+/*
+* SIV Mode Encryption
+* (C) 2013,2017 Jack Lloyd
+* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/siv.h>
+#include <botan/block_cipher.h>
+#include <botan/cmac.h>
+#include <botan/internal/poly_dbl.h>
+#include <botan/ctr.h>
+
+namespace Botan {
+
+SIV_Mode::SIV_Mode(BlockCipher* cipher) :
+ m_name(cipher->name() + "/SIV"),
+ m_ctr(new CTR_BE(cipher->clone(), 8)),
+ m_mac(new CMAC(cipher)),
+ m_bs(cipher->block_size())
+ {
+ // Not really true but only 128 bit allowed at the moment
+ if(m_bs != 16)
+ throw Invalid_Argument("SIV requires a 128 bit block cipher");
+ }
+
+SIV_Mode::~SIV_Mode()
+ {
+ // for ~unique_ptr
+ }
+
+void SIV_Mode::clear()
+ {
+ m_ctr->clear();
+ m_mac->clear();
+ reset();
+ }
+
+void SIV_Mode::reset()
+ {
+ m_nonce.clear();
+ m_msg_buf.clear();
+ m_ad_macs.clear();
+ }
+
+std::string SIV_Mode::name() const
+ {
+ return m_name;
+ }
+
+bool SIV_Mode::valid_nonce_length(size_t) const
+ {
+ return true;
+ }
+
+size_t SIV_Mode::update_granularity() const
+ {
+ /*
+ This value does not particularly matter as regardless SIV_Mode::update
+ buffers all input, so in theory this could be 1. However as for instance
+ Transform_Filter creates update_granularity() uint8_t buffers, use a
+ somewhat large size to avoid bouncing on a tiny buffer.
+ */
+ return 128;
+ }
+
+Key_Length_Specification SIV_Mode::key_spec() const
+ {
+ return m_mac->key_spec().multiple(2);
+ }
+
+void SIV_Mode::key_schedule(const uint8_t key[], size_t length)
+ {
+ const size_t keylen = length / 2;
+ m_mac->set_key(key, keylen);
+ m_ctr->set_key(key + keylen, keylen);
+ m_ad_macs.clear();
+ }
+
+size_t SIV_Mode::maximum_associated_data_inputs() const
+ {
+ return block_size() * 8 - 2;
+ }
+
+void SIV_Mode::set_associated_data_n(size_t n, const uint8_t ad[], size_t length)
+ {
+ const size_t max_ads = maximum_associated_data_inputs();
+ if(n > max_ads)
+ throw Invalid_Argument(name() + " allows no more than " + std::to_string(max_ads) + " ADs");
+
+ if(n >= m_ad_macs.size())
+ m_ad_macs.resize(n+1);
+
+ m_ad_macs[n] = m_mac->process(ad, length);
+ }
+
+void SIV_Mode::start_msg(const uint8_t nonce[], size_t nonce_len)
+ {
+ if(!valid_nonce_length(nonce_len))
+ throw Invalid_IV_Length(name(), nonce_len);
+
+ if(nonce_len)
+ m_nonce = m_mac->process(nonce, nonce_len);
+ else
+ m_nonce.clear();
+
+ m_msg_buf.clear();
+ }
+
+size_t SIV_Mode::process(uint8_t buf[], size_t sz)
+ {
+ // all output is saved for processing in finish
+ m_msg_buf.insert(m_msg_buf.end(), buf, buf + sz);
+ return 0;
+ }
+
+secure_vector<uint8_t> SIV_Mode::S2V(const uint8_t* text, size_t text_len)
+ {
+ const std::vector<uint8_t> zeros(block_size());
+
+ secure_vector<uint8_t> V = m_mac->process(zeros.data(), zeros.size());
+
+ for(size_t i = 0; i != m_ad_macs.size(); ++i)
+ {
+ poly_double_n(V.data(), V.size());
+ V ^= m_ad_macs[i];
+ }
+
+ if(m_nonce.size())
+ {
+ poly_double_n(V.data(), V.size());
+ V ^= m_nonce;
+ }
+
+ if(text_len < block_size())
+ {
+ poly_double_n(V.data(), V.size());
+ xor_buf(V.data(), text, text_len);
+ V[text_len] ^= 0x80;
+ return m_mac->process(V);
+ }
+
+ m_mac->update(text, text_len - block_size());
+ xor_buf(V.data(), &text[text_len - block_size()], block_size());
+ m_mac->update(V);
+
+ return m_mac->final();
+ }
+
+void SIV_Mode::set_ctr_iv(secure_vector<uint8_t> V)
+ {
+ V[m_bs-8] &= 0x7F;
+ V[m_bs-4] &= 0x7F;
+
+ ctr().set_iv(V.data(), V.size());
+ }
+
+void SIV_Encryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
+ {
+ BOTAN_ASSERT(buffer.size() >= offset, "Offset is sane");
+
+ buffer.insert(buffer.begin() + offset, msg_buf().begin(), msg_buf().end());
+ msg_buf().clear();
+
+ const secure_vector<uint8_t> V = S2V(buffer.data() + offset, buffer.size() - offset);
+
+ buffer.insert(buffer.begin() + offset, V.begin(), V.end());
+
+ if(buffer.size() != offset + V.size())
+ {
+ set_ctr_iv(V);
+ ctr().cipher1(&buffer[offset + V.size()], buffer.size() - offset - V.size());
+ }
+ }
+
+void SIV_Decryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
+ {
+ BOTAN_ASSERT(buffer.size() >= offset, "Offset is sane");
+
+ if(msg_buf().size() > 0)
+ {
+ buffer.insert(buffer.begin() + offset, msg_buf().begin(), msg_buf().end());
+ msg_buf().clear();
+ }
+
+ const size_t sz = buffer.size() - offset;
+
+ BOTAN_ASSERT(sz >= tag_size(), "We have the tag");
+
+ secure_vector<uint8_t> V(buffer.data() + offset,
+ buffer.data() + offset + block_size());
+
+ if(buffer.size() != offset + V.size())
+ {
+ set_ctr_iv(V);
+
+ ctr().cipher(buffer.data() + offset + V.size(),
+ buffer.data() + offset,
+ buffer.size() - offset - V.size());
+ }
+
+ const secure_vector<uint8_t> T = S2V(buffer.data() + offset, buffer.size() - offset - V.size());
+
+ if(!constant_time_compare(T.data(), V.data(), T.size()))
+ throw Invalid_Authentication_Tag("SIV tag check failed");
+
+ buffer.resize(buffer.size() - tag_size());
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/modes/aead/siv/siv.h b/comm/third_party/botan/src/lib/modes/aead/siv/siv.h
new file mode 100644
index 0000000000..c76fd32296
--- /dev/null
+++ b/comm/third_party/botan/src/lib/modes/aead/siv/siv.h
@@ -0,0 +1,129 @@
+/*
+* SIV Mode
+* (C) 2013 Jack Lloyd
+* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_AEAD_SIV_H_
+#define BOTAN_AEAD_SIV_H_
+
+#include <botan/aead.h>
+#include <botan/stream_cipher.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(siv.h)
+
+namespace Botan {
+
+class BlockCipher;
+class MessageAuthenticationCode;
+
+/**
+* Base class for SIV encryption and decryption (@see RFC 5297)
+*/
+class BOTAN_PUBLIC_API(2,0) SIV_Mode : public AEAD_Mode
+ {
+ public:
+ size_t process(uint8_t buf[], size_t size) override;
+
+ /**
+ * Sets the nth element of the vector of associated data
+ * @param n index into the AD vector
+ * @param ad associated data
+ * @param ad_len length of associated data in bytes
+ */
+ void set_associated_data_n(size_t n, const uint8_t ad[], size_t ad_len) override;
+
+ size_t maximum_associated_data_inputs() const override;
+
+ void set_associated_data(const uint8_t ad[], size_t ad_len) override
+ {
+ set_associated_data_n(0, ad, ad_len);
+ }
+
+ std::string name() const override;
+
+ size_t update_granularity() const override;
+
+ Key_Length_Specification key_spec() const override;
+
+ bool valid_nonce_length(size_t) const override;
+
+ void clear() override;
+
+ void reset() override;
+
+ size_t tag_size() const override { return 16; }
+
+ ~SIV_Mode();
+
+ protected:
+ explicit SIV_Mode(BlockCipher* cipher);
+
+ size_t block_size() const { return m_bs; }
+
+ StreamCipher& ctr() { return *m_ctr; }
+
+ void set_ctr_iv(secure_vector<uint8_t> V);
+
+ secure_vector<uint8_t>& msg_buf() { return m_msg_buf; }
+
+ secure_vector<uint8_t> S2V(const uint8_t text[], size_t text_len);
+ private:
+ void start_msg(const uint8_t nonce[], size_t nonce_len) override;
+
+ void key_schedule(const uint8_t key[], size_t length) override;
+
+ const std::string m_name;
+ std::unique_ptr<StreamCipher> m_ctr;
+ std::unique_ptr<MessageAuthenticationCode> m_mac;
+ secure_vector<uint8_t> m_nonce, m_msg_buf;
+ std::vector<secure_vector<uint8_t>> m_ad_macs;
+ const size_t m_bs;
+ };
+
+/**
+* SIV Encryption
+*/
+class BOTAN_PUBLIC_API(2,0) SIV_Encryption final : public SIV_Mode
+ {
+ public:
+ /**
+ * @param cipher a block cipher
+ */
+ explicit SIV_Encryption(BlockCipher* cipher) : SIV_Mode(cipher) {}
+
+ void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
+
+ size_t output_length(size_t input_length) const override
+ { return input_length + tag_size(); }
+
+ size_t minimum_final_size() const override { return 0; }
+ };
+
+/**
+* SIV Decryption
+*/
+class BOTAN_PUBLIC_API(2,0) SIV_Decryption final : public SIV_Mode
+ {
+ public:
+ /**
+ * @param cipher a 128-bit block cipher
+ */
+ explicit SIV_Decryption(BlockCipher* cipher) : SIV_Mode(cipher) {}
+
+ void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
+
+ size_t output_length(size_t input_length) const override
+ {
+ BOTAN_ASSERT(input_length >= tag_size(), "Sufficient input");
+ return input_length - tag_size();
+ }
+
+ size_t minimum_final_size() const override { return tag_size(); }
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/modes/cbc/cbc.cpp b/comm/third_party/botan/src/lib/modes/cbc/cbc.cpp
new file mode 100644
index 0000000000..cde08f64a9
--- /dev/null
+++ b/comm/third_party/botan/src/lib/modes/cbc/cbc.cpp
@@ -0,0 +1,323 @@
+/*
+* CBC Mode
+* (C) 1999-2007,2013,2017 Jack Lloyd
+* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
+* (C) 2018 Ribose Inc
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/cbc.h>
+#include <botan/mode_pad.h>
+#include <botan/internal/rounding.h>
+
+namespace Botan {
+
+CBC_Mode::CBC_Mode(BlockCipher* cipher, BlockCipherModePaddingMethod* padding) :
+ m_cipher(cipher),
+ m_padding(padding),
+ m_block_size(cipher->block_size())
+ {
+ if(m_padding && !m_padding->valid_blocksize(m_block_size))
+ throw Invalid_Argument("Padding " + m_padding->name() +
+ " cannot be used with " +
+ cipher->name() + "/CBC");
+ }
+
+void CBC_Mode::clear()
+ {
+ m_cipher->clear();
+ reset();
+ }
+
+void CBC_Mode::reset()
+ {
+ m_state.clear();
+ }
+
+std::string CBC_Mode::name() const
+ {
+ if(m_padding)
+ return cipher().name() + "/CBC/" + padding().name();
+ else
+ return cipher().name() + "/CBC/CTS";
+ }
+
+size_t CBC_Mode::update_granularity() const
+ {
+ return cipher().parallel_bytes();
+ }
+
+Key_Length_Specification CBC_Mode::key_spec() const
+ {
+ return cipher().key_spec();
+ }
+
+size_t CBC_Mode::default_nonce_length() const
+ {
+ return block_size();
+ }
+
+bool CBC_Mode::valid_nonce_length(size_t n) const
+ {
+ return (n == 0 || n == block_size());
+ }
+
+void CBC_Mode::key_schedule(const uint8_t key[], size_t length)
+ {
+ m_cipher->set_key(key, length);
+ m_state.clear();
+ }
+
+void CBC_Mode::start_msg(const uint8_t nonce[], size_t nonce_len)
+ {
+ if(!valid_nonce_length(nonce_len))
+ throw Invalid_IV_Length(name(), nonce_len);
+
+ /*
+ * A nonce of zero length means carry the last ciphertext value over
+ * 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)
+ m_state.assign(nonce, nonce + nonce_len);
+ else if(m_state.empty())
+ m_state.resize(m_cipher->block_size());
+ // else leave the state alone
+ }
+
+size_t CBC_Encryption::minimum_final_size() const
+ {
+ return 0;
+ }
+
+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());
+ }
+
+size_t CBC_Encryption::process(uint8_t buf[], size_t sz)
+ {
+ BOTAN_STATE_CHECK(state().empty() == false);
+ const size_t BS = block_size();
+
+ BOTAN_ASSERT(sz % BS == 0, "CBC input is full blocks");
+ const size_t blocks = sz / BS;
+
+ if(blocks > 0)
+ {
+ xor_buf(&buf[0], state_ptr(), BS);
+ cipher().encrypt(&buf[0]);
+
+ for(size_t i = 1; i != blocks; ++i)
+ {
+ xor_buf(&buf[BS*i], &buf[BS*(i-1)], BS);
+ cipher().encrypt(&buf[BS*i]);
+ }
+
+ state().assign(&buf[BS*(blocks-1)], &buf[BS*blocks]);
+ }
+
+ return sz;
+ }
+
+void CBC_Encryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
+ {
+ BOTAN_STATE_CHECK(state().empty() == false);
+ BOTAN_ASSERT(buffer.size() >= offset, "Offset is sane");
+
+ const size_t BS = block_size();
+
+ const size_t bytes_in_final_block = (buffer.size()-offset) % BS;
+
+ padding().add_padding(buffer, bytes_in_final_block, BS);
+
+ BOTAN_ASSERT_EQUAL(buffer.size() % BS, offset % BS, "Padded to block boundary");
+
+ update(buffer, offset);
+ }
+
+bool CTS_Encryption::valid_nonce_length(size_t n) const
+ {
+ return (n == block_size());
+ }
+
+size_t CTS_Encryption::minimum_final_size() const
+ {
+ return block_size() + 1;
+ }
+
+size_t CTS_Encryption::output_length(size_t input_length) const
+ {
+ return input_length; // no ciphertext expansion in CTS
+ }
+
+void CTS_Encryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
+ {
+ BOTAN_STATE_CHECK(state().empty() == false);
+ BOTAN_ASSERT(buffer.size() >= offset, "Offset is sane");
+ uint8_t* buf = buffer.data() + offset;
+ const size_t sz = buffer.size() - offset;
+
+ const size_t BS = block_size();
+
+ if(sz < BS + 1)
+ throw Encoding_Error(name() + ": insufficient data to encrypt");
+
+ if(sz % BS == 0)
+ {
+ update(buffer, offset);
+
+ // swap last two blocks
+ for(size_t i = 0; i != BS; ++i)
+ std::swap(buffer[buffer.size()-BS+i], buffer[buffer.size()-2*BS+i]);
+ }
+ else
+ {
+ const size_t full_blocks = ((sz / BS) - 1) * BS;
+ const size_t final_bytes = sz - full_blocks;
+ BOTAN_ASSERT(final_bytes > BS && final_bytes < 2*BS, "Left over size in expected range");
+
+ secure_vector<uint8_t> last(buf + full_blocks, buf + full_blocks + final_bytes);
+ buffer.resize(full_blocks + offset);
+ update(buffer, offset);
+
+ xor_buf(last.data(), state_ptr(), BS);
+ cipher().encrypt(last.data());
+
+ for(size_t i = 0; i != final_bytes - BS; ++i)
+ {
+ last[i] ^= last[i + BS];
+ last[i + BS] ^= last[i];
+ }
+
+ cipher().encrypt(last.data());
+
+ buffer += last;
+ }
+ }
+
+size_t CBC_Decryption::output_length(size_t input_length) const
+ {
+ return input_length; // precise for CTS, worst case otherwise
+ }
+
+size_t CBC_Decryption::minimum_final_size() const
+ {
+ return block_size();
+ }
+
+size_t CBC_Decryption::process(uint8_t buf[], size_t sz)
+ {
+ BOTAN_STATE_CHECK(state().empty() == false);
+
+ const size_t BS = block_size();
+
+ BOTAN_ASSERT(sz % BS == 0, "Input is full blocks");
+ size_t blocks = sz / BS;
+
+ while(blocks)
+ {
+ const size_t to_proc = std::min(BS * blocks, m_tempbuf.size());
+
+ cipher().decrypt_n(buf, m_tempbuf.data(), to_proc / BS);
+
+ xor_buf(m_tempbuf.data(), state_ptr(), BS);
+ xor_buf(&m_tempbuf[BS], buf, to_proc - BS);
+ copy_mem(state_ptr(), buf + (to_proc - BS), BS);
+
+ copy_mem(buf, m_tempbuf.data(), to_proc);
+
+ buf += to_proc;
+ blocks -= to_proc / BS;
+ }
+
+ return sz;
+ }
+
+void CBC_Decryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
+ {
+ BOTAN_STATE_CHECK(state().empty() == false);
+ BOTAN_ASSERT(buffer.size() >= offset, "Offset is sane");
+ const size_t sz = buffer.size() - offset;
+
+ const size_t BS = block_size();
+
+ if(sz == 0 || sz % BS)
+ 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);
+ buffer.resize(buffer.size() - pad_bytes); // remove padding
+ if(pad_bytes == 0 && padding().name() != "NoPadding")
+ {
+ throw Decoding_Error("Invalid CBC padding");
+ }
+ }
+
+void CBC_Decryption::reset()
+ {
+ CBC_Mode::reset();
+ zeroise(m_tempbuf);
+ }
+
+bool CTS_Decryption::valid_nonce_length(size_t n) const
+ {
+ return (n == block_size());
+ }
+
+size_t CTS_Decryption::minimum_final_size() const
+ {
+ return block_size() + 1;
+ }
+
+void CTS_Decryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
+ {
+ BOTAN_STATE_CHECK(state().empty() == false);
+ BOTAN_ASSERT(buffer.size() >= offset, "Offset is sane");
+ const size_t sz = buffer.size() - offset;
+ uint8_t* buf = buffer.data() + offset;
+
+ const size_t BS = block_size();
+
+ if(sz < BS + 1)
+ throw Encoding_Error(name() + ": insufficient data to decrypt");
+
+ if(sz % BS == 0)
+ {
+ // swap last two blocks
+
+ for(size_t i = 0; i != BS; ++i)
+ std::swap(buffer[buffer.size()-BS+i], buffer[buffer.size()-2*BS+i]);
+
+ update(buffer, offset);
+ }
+ else
+ {
+ const size_t full_blocks = ((sz / BS) - 1) * BS;
+ const size_t final_bytes = sz - full_blocks;
+ BOTAN_ASSERT(final_bytes > BS && final_bytes < 2*BS, "Left over size in expected range");
+
+ secure_vector<uint8_t> last(buf + full_blocks, buf + full_blocks + final_bytes);
+ buffer.resize(full_blocks + offset);
+ update(buffer, offset);
+
+ cipher().decrypt(last.data());
+
+ xor_buf(last.data(), &last[BS], final_bytes - BS);
+
+ for(size_t i = 0; i != final_bytes - BS; ++i)
+ std::swap(last[i], last[i + BS]);
+
+ cipher().decrypt(last.data());
+ xor_buf(last.data(), state_ptr(), BS);
+
+ buffer += last;
+ }
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/modes/cbc/cbc.h b/comm/third_party/botan/src/lib/modes/cbc/cbc.h
new file mode 100644
index 0000000000..7a488dbd50
--- /dev/null
+++ b/comm/third_party/botan/src/lib/modes/cbc/cbc.h
@@ -0,0 +1,157 @@
+/*
+* CBC mode
+* (C) 1999-2007,2013 Jack Lloyd
+* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_MODE_CBC_H_
+#define BOTAN_MODE_CBC_H_
+
+#include <botan/cipher_mode.h>
+#include <botan/block_cipher.h>
+#include <botan/mode_pad.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(cbc.h)
+
+namespace Botan {
+
+/**
+* CBC Mode
+*/
+class BOTAN_PUBLIC_API(2,0) CBC_Mode : public Cipher_Mode
+ {
+ public:
+ std::string name() const override;
+
+ size_t update_granularity() const override;
+
+ Key_Length_Specification key_spec() const override;
+
+ size_t default_nonce_length() const override;
+
+ bool valid_nonce_length(size_t n) const override;
+
+ void clear() override;
+
+ void reset() override;
+
+ protected:
+ CBC_Mode(BlockCipher* cipher, BlockCipherModePaddingMethod* padding);
+
+ const BlockCipher& cipher() const { return *m_cipher; }
+
+ const BlockCipherModePaddingMethod& padding() const
+ {
+ BOTAN_ASSERT_NONNULL(m_padding);
+ return *m_padding;
+ }
+
+ size_t block_size() const { return m_block_size; }
+
+ secure_vector<uint8_t>& state() { return m_state; }
+
+ uint8_t* state_ptr() { return m_state.data(); }
+
+ private:
+ void start_msg(const uint8_t nonce[], size_t nonce_len) override;
+
+ void key_schedule(const uint8_t key[], size_t length) override;
+
+ std::unique_ptr<BlockCipher> m_cipher;
+ std::unique_ptr<BlockCipherModePaddingMethod> m_padding;
+ secure_vector<uint8_t> m_state;
+ size_t m_block_size;
+ };
+
+/**
+* CBC Encryption
+*/
+class BOTAN_PUBLIC_API(2,0) CBC_Encryption : public CBC_Mode
+ {
+ public:
+ /**
+ * @param cipher block cipher to use
+ * @param padding padding method to use
+ */
+ CBC_Encryption(BlockCipher* cipher, BlockCipherModePaddingMethod* padding) :
+ CBC_Mode(cipher, padding) {}
+
+ size_t process(uint8_t buf[], size_t size) override;
+
+ void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
+
+ size_t output_length(size_t input_length) const override;
+
+ size_t minimum_final_size() const override;
+ };
+
+/**
+* CBC Encryption with ciphertext stealing (CBC-CS3 variant)
+*/
+class BOTAN_PUBLIC_API(2,0) CTS_Encryption final : public CBC_Encryption
+ {
+ public:
+ /**
+ * @param cipher block cipher to use
+ */
+ explicit CTS_Encryption(BlockCipher* cipher) : CBC_Encryption(cipher, nullptr) {}
+
+ size_t output_length(size_t input_length) const override;
+
+ void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
+
+ size_t minimum_final_size() const override;
+
+ bool valid_nonce_length(size_t n) const override;
+ };
+
+/**
+* CBC Decryption
+*/
+class BOTAN_PUBLIC_API(2,0) CBC_Decryption : public CBC_Mode
+ {
+ public:
+ /**
+ * @param cipher block cipher to use
+ * @param padding padding method to use
+ */
+ CBC_Decryption(BlockCipher* cipher, BlockCipherModePaddingMethod* padding) :
+ CBC_Mode(cipher, padding), m_tempbuf(update_granularity()) {}
+
+ size_t process(uint8_t buf[], size_t size) override;
+
+ void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
+
+ size_t output_length(size_t input_length) const override;
+
+ size_t minimum_final_size() const override;
+
+ void reset() override;
+
+ private:
+ secure_vector<uint8_t> m_tempbuf;
+ };
+
+/**
+* CBC Decryption with ciphertext stealing (CBC-CS3 variant)
+*/
+class BOTAN_PUBLIC_API(2,0) CTS_Decryption final : public CBC_Decryption
+ {
+ public:
+ /**
+ * @param cipher block cipher to use
+ */
+ explicit CTS_Decryption(BlockCipher* cipher) : CBC_Decryption(cipher, nullptr) {}
+
+ void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
+
+ size_t minimum_final_size() const override;
+
+ bool valid_nonce_length(size_t n) const override;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/modes/cbc/info.txt b/comm/third_party/botan/src/lib/modes/cbc/info.txt
new file mode 100644
index 0000000000..778ba1e252
--- /dev/null
+++ b/comm/third_party/botan/src/lib/modes/cbc/info.txt
@@ -0,0 +1,8 @@
+<defines>
+MODE_CBC -> 20131128
+</defines>
+
+<requires>
+block
+mode_pad
+</requires>
diff --git a/comm/third_party/botan/src/lib/modes/cfb/cfb.cpp b/comm/third_party/botan/src/lib/modes/cfb/cfb.cpp
new file mode 100644
index 0000000000..0c619e322c
--- /dev/null
+++ b/comm/third_party/botan/src/lib/modes/cfb/cfb.cpp
@@ -0,0 +1,229 @@
+/*
+* CFB Mode
+* (C) 1999-2007,2013,2017 Jack Lloyd
+* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/cfb.h>
+
+namespace Botan {
+
+CFB_Mode::CFB_Mode(BlockCipher* cipher, size_t feedback_bits) :
+ m_cipher(cipher),
+ m_block_size(m_cipher->block_size()),
+ m_feedback_bytes(feedback_bits ? feedback_bits / 8 : m_block_size)
+ {
+ if(feedback_bits % 8 || feedback() > cipher->block_size())
+ throw Invalid_Argument(name() + ": feedback bits " +
+ std::to_string(feedback_bits) + " not supported");
+ }
+
+void CFB_Mode::clear()
+ {
+ m_cipher->clear();
+ m_keystream.clear();
+ reset();
+ }
+
+void CFB_Mode::reset()
+ {
+ m_state.clear();
+ zeroise(m_keystream);
+ }
+
+std::string CFB_Mode::name() const
+ {
+ if(feedback() == cipher().block_size())
+ return cipher().name() + "/CFB";
+ else
+ return cipher().name() + "/CFB(" + std::to_string(feedback()*8) + ")";
+ }
+
+size_t CFB_Mode::output_length(size_t input_length) const
+ {
+ return input_length;
+ }
+
+size_t CFB_Mode::update_granularity() const
+ {
+ return feedback();
+ }
+
+size_t CFB_Mode::minimum_final_size() const
+ {
+ return 0;
+ }
+
+Key_Length_Specification CFB_Mode::key_spec() const
+ {
+ return cipher().key_spec();
+ }
+
+size_t CFB_Mode::default_nonce_length() const
+ {
+ return block_size();
+ }
+
+bool CFB_Mode::valid_nonce_length(size_t n) const
+ {
+ return (n == 0 || n == block_size());
+ }
+
+void CFB_Mode::key_schedule(const uint8_t key[], size_t length)
+ {
+ m_cipher->set_key(key, length);
+ m_keystream.resize(m_cipher->block_size());
+ }
+
+void CFB_Mode::start_msg(const uint8_t nonce[], size_t nonce_len)
+ {
+ if(!valid_nonce_length(nonce_len))
+ throw Invalid_IV_Length(name(), nonce_len);
+
+ verify_key_set(!m_keystream.empty());
+
+ if(nonce_len == 0)
+ {
+ if(m_state.empty())
+ {
+ throw Invalid_State("CFB requires a non-empty initial nonce");
+ }
+ // No reason to encrypt state->keystream_buf, because no change
+ }
+ else
+ {
+ m_state.assign(nonce, nonce + nonce_len);
+ cipher().encrypt(m_state, m_keystream);
+ m_keystream_pos = 0;
+ }
+ }
+
+void CFB_Mode::shift_register()
+ {
+ const size_t shift = feedback();
+ const size_t carryover = block_size() - shift;
+
+ if(carryover > 0)
+ {
+ copy_mem(m_state.data(), &m_state[shift], carryover);
+ }
+ copy_mem(&m_state[carryover], m_keystream.data(), shift);
+ cipher().encrypt(m_state, m_keystream);
+ m_keystream_pos = 0;
+ }
+
+size_t CFB_Encryption::process(uint8_t buf[], size_t sz)
+ {
+ verify_key_set(!m_keystream.empty());
+ BOTAN_STATE_CHECK(m_state.empty() == false);
+
+ const size_t shift = feedback();
+
+ size_t left = sz;
+
+ if(m_keystream_pos != 0)
+ {
+ const size_t take = std::min<size_t>(left, shift - m_keystream_pos);
+
+ xor_buf(m_keystream.data() + m_keystream_pos, buf, take);
+ copy_mem(buf, m_keystream.data() + m_keystream_pos, take);
+
+ m_keystream_pos += take;
+ left -= take;
+ buf += take;
+
+ if(m_keystream_pos == shift)
+ {
+ shift_register();
+ }
+ }
+
+ while(left >= shift)
+ {
+ xor_buf(m_keystream.data(), buf, shift);
+ copy_mem(buf, m_keystream.data(), shift);
+
+ left -= shift;
+ buf += shift;
+ shift_register();
+ }
+
+ if(left > 0)
+ {
+ xor_buf(m_keystream.data(), buf, left);
+ copy_mem(buf, m_keystream.data(), left);
+ m_keystream_pos += left;
+ }
+
+ return sz;
+ }
+
+void CFB_Encryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
+ {
+ update(buffer, offset);
+ }
+
+namespace {
+
+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];
+ key_buf[i] = buf[i];
+ buf[i] ^= k;
+ }
+ }
+
+}
+
+size_t CFB_Decryption::process(uint8_t buf[], size_t sz)
+ {
+ verify_key_set(!m_keystream.empty());
+ BOTAN_STATE_CHECK(m_state.empty() == false);
+
+ const size_t shift = feedback();
+
+ size_t left = sz;
+
+ if(m_keystream_pos != 0)
+ {
+ const size_t take = std::min<size_t>(left, shift - m_keystream_pos);
+
+ xor_copy(buf, m_keystream.data() + m_keystream_pos, take);
+
+ m_keystream_pos += take;
+ left -= take;
+ buf += take;
+
+ if(m_keystream_pos == shift)
+ {
+ shift_register();
+ }
+ }
+
+ while(left >= shift)
+ {
+ xor_copy(buf, m_keystream.data(), shift);
+ left -= shift;
+ buf += shift;
+ shift_register();
+ }
+
+ if(left > 0)
+ {
+ xor_copy(buf, m_keystream.data(), left);
+ m_keystream_pos += left;
+ }
+
+ return sz;
+ }
+
+void CFB_Decryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
+ {
+ update(buffer, offset);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/modes/cfb/cfb.h b/comm/third_party/botan/src/lib/modes/cfb/cfb.h
new file mode 100644
index 0000000000..1f9e554872
--- /dev/null
+++ b/comm/third_party/botan/src/lib/modes/cfb/cfb.h
@@ -0,0 +1,106 @@
+/*
+* CFB mode
+* (C) 1999-2007,2013 Jack Lloyd
+* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_MODE_CFB_H_
+#define BOTAN_MODE_CFB_H_
+
+#include <botan/cipher_mode.h>
+#include <botan/block_cipher.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(cfb.h)
+
+namespace Botan {
+
+/**
+* CFB Mode
+*/
+class BOTAN_PUBLIC_API(2,0) CFB_Mode : public Cipher_Mode
+ {
+ public:
+ std::string name() const override final;
+
+ size_t update_granularity() const override final;
+
+ size_t minimum_final_size() const override final;
+
+ Key_Length_Specification key_spec() const override final;
+
+ size_t output_length(size_t input_length) const override final;
+
+ size_t default_nonce_length() const override final;
+
+ bool valid_nonce_length(size_t n) const override final;
+
+ void clear() override final;
+
+ void reset() override final;
+ protected:
+ CFB_Mode(BlockCipher* cipher, size_t feedback_bits);
+
+ void shift_register();
+
+ size_t feedback() const { return m_feedback_bytes; }
+ const BlockCipher& cipher() const { return *m_cipher; }
+ size_t block_size() const { return m_block_size; }
+
+ secure_vector<uint8_t> m_state;
+ secure_vector<uint8_t> m_keystream;
+ size_t m_keystream_pos = 0;
+
+ private:
+ void start_msg(const uint8_t nonce[], size_t nonce_len) override;
+ void key_schedule(const uint8_t key[], size_t length) override;
+
+ std::unique_ptr<BlockCipher> m_cipher;
+ const size_t m_block_size;
+ const size_t m_feedback_bytes;
+ };
+
+/**
+* CFB Encryption
+*/
+class BOTAN_PUBLIC_API(2,0) CFB_Encryption final : public CFB_Mode
+ {
+ public:
+ /**
+ * If feedback_bits is zero, cipher->block_size() bytes will be used.
+ * @param cipher block cipher to use
+ * @param feedback_bits number of bits fed back into the shift register,
+ * must be a multiple of 8
+ */
+ CFB_Encryption(BlockCipher* cipher, size_t feedback_bits) :
+ CFB_Mode(cipher, feedback_bits) {}
+
+ size_t process(uint8_t buf[], size_t size) override;
+
+ void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
+ };
+
+/**
+* CFB Decryption
+*/
+class BOTAN_PUBLIC_API(2,0) CFB_Decryption final : public CFB_Mode
+ {
+ public:
+ /**
+ * If feedback_bits is zero, cipher->block_size() bytes will be used.
+ * @param cipher block cipher to use
+ * @param feedback_bits number of bits fed back into the shift register,
+ * must be a multiple of 8
+ */
+ CFB_Decryption(BlockCipher* cipher, size_t feedback_bits) :
+ CFB_Mode(cipher, feedback_bits) {}
+
+ size_t process(uint8_t buf[], size_t size) override;
+
+ void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/modes/cfb/info.txt b/comm/third_party/botan/src/lib/modes/cfb/info.txt
new file mode 100644
index 0000000000..77a6af448e
--- /dev/null
+++ b/comm/third_party/botan/src/lib/modes/cfb/info.txt
@@ -0,0 +1,7 @@
+<defines>
+MODE_CFB -> 20131128
+</defines>
+
+<requires>
+block
+</requires>
diff --git a/comm/third_party/botan/src/lib/modes/cipher_mode.cpp b/comm/third_party/botan/src/lib/modes/cipher_mode.cpp
new file mode 100644
index 0000000000..710f16ba22
--- /dev/null
+++ b/comm/third_party/botan/src/lib/modes/cipher_mode.cpp
@@ -0,0 +1,205 @@
+/*
+* Cipher Modes
+* (C) 2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/cipher_mode.h>
+#include <botan/stream_mode.h>
+#include <botan/scan_name.h>
+#include <botan/parsing.h>
+#include <sstream>
+
+#if defined(BOTAN_HAS_BLOCK_CIPHER)
+ #include <botan/block_cipher.h>
+#endif
+
+#if defined(BOTAN_HAS_AEAD_MODES)
+ #include <botan/aead.h>
+#endif
+
+#if defined(BOTAN_HAS_MODE_CBC)
+ #include <botan/cbc.h>
+#endif
+
+#if defined(BOTAN_HAS_MODE_CFB)
+ #include <botan/cfb.h>
+#endif
+
+#if defined(BOTAN_HAS_MODE_XTS)
+ #include <botan/xts.h>
+#endif
+
+#if defined(BOTAN_HAS_OPENSSL)
+ #include <botan/internal/openssl.h>
+#endif
+
+#if defined(BOTAN_HAS_COMMONCRYPTO)
+ #include <botan/internal/commoncrypto.h>
+#endif
+
+namespace Botan {
+
+std::unique_ptr<Cipher_Mode> Cipher_Mode::create_or_throw(const std::string& algo,
+ Cipher_Dir direction,
+ const std::string& provider)
+ {
+ if(auto mode = Cipher_Mode::create(algo, direction, provider))
+ return mode;
+
+ throw Lookup_Error("Cipher mode", algo, provider);
+ }
+
+std::unique_ptr<Cipher_Mode> Cipher_Mode::create(const std::string& algo,
+ Cipher_Dir direction,
+ const std::string& provider)
+ {
+#if defined(BOTAN_HAS_COMMONCRYPTO)
+ if(provider.empty() || provider == "commoncrypto")
+ {
+ std::unique_ptr<Cipher_Mode> commoncrypto_cipher(make_commoncrypto_cipher_mode(algo, direction));
+
+ if(commoncrypto_cipher)
+ return commoncrypto_cipher;
+
+ if(!provider.empty())
+ return std::unique_ptr<Cipher_Mode>();
+ }
+#endif
+
+#if defined(BOTAN_HAS_OPENSSL)
+ if(provider.empty() || provider == "openssl")
+ {
+ std::unique_ptr<Cipher_Mode> openssl_cipher(make_openssl_cipher_mode(algo, direction));
+
+ if(openssl_cipher)
+ return openssl_cipher;
+
+ if(!provider.empty())
+ return std::unique_ptr<Cipher_Mode>();
+ }
+#endif
+
+#if defined(BOTAN_HAS_STREAM_CIPHER)
+ if(auto sc = StreamCipher::create(algo))
+ {
+ return std::unique_ptr<Cipher_Mode>(new Stream_Cipher_Mode(sc.release()));
+ }
+#endif
+
+#if defined(BOTAN_HAS_AEAD_MODES)
+ if(auto aead = AEAD_Mode::create(algo, direction))
+ {
+ return std::unique_ptr<Cipher_Mode>(aead.release());
+ }
+#endif
+
+ if(algo.find('/') != std::string::npos)
+ {
+ const std::vector<std::string> algo_parts = split_on(algo, '/');
+ const std::string cipher_name = algo_parts[0];
+ const std::vector<std::string> mode_info = parse_algorithm_name(algo_parts[1]);
+
+ if(mode_info.empty())
+ return std::unique_ptr<Cipher_Mode>();
+
+ std::ostringstream alg_args;
+
+ alg_args << '(' << cipher_name;
+ for(size_t i = 1; i < mode_info.size(); ++i)
+ alg_args << ',' << mode_info[i];
+ for(size_t i = 2; i < algo_parts.size(); ++i)
+ alg_args << ',' << algo_parts[i];
+ alg_args << ')';
+
+ const std::string mode_name = mode_info[0] + alg_args.str();
+ return Cipher_Mode::create(mode_name, direction, provider);
+ }
+
+#if defined(BOTAN_HAS_BLOCK_CIPHER)
+
+ SCAN_Name spec(algo);
+
+ if(spec.arg_count() == 0)
+ {
+ return std::unique_ptr<Cipher_Mode>();
+ }
+
+ std::unique_ptr<BlockCipher> bc(BlockCipher::create(spec.arg(0), provider));
+
+ if(!bc)
+ {
+ return std::unique_ptr<Cipher_Mode>();
+ }
+
+#if defined(BOTAN_HAS_MODE_CBC)
+ if(spec.algo_name() == "CBC")
+ {
+ const std::string padding = spec.arg(1, "PKCS7");
+
+ if(padding == "CTS")
+ {
+ if(direction == ENCRYPTION)
+ return std::unique_ptr<Cipher_Mode>(new CTS_Encryption(bc.release()));
+ else
+ return std::unique_ptr<Cipher_Mode>(new CTS_Decryption(bc.release()));
+ }
+ else
+ {
+ std::unique_ptr<BlockCipherModePaddingMethod> pad(get_bc_pad(padding));
+
+ if(pad)
+ {
+ if(direction == ENCRYPTION)
+ return std::unique_ptr<Cipher_Mode>(new CBC_Encryption(bc.release(), pad.release()));
+ else
+ return std::unique_ptr<Cipher_Mode>(new CBC_Decryption(bc.release(), pad.release()));
+ }
+ }
+ }
+#endif
+
+#if defined(BOTAN_HAS_MODE_XTS)
+ if(spec.algo_name() == "XTS")
+ {
+ if(direction == ENCRYPTION)
+ return std::unique_ptr<Cipher_Mode>(new XTS_Encryption(bc.release()));
+ else
+ return std::unique_ptr<Cipher_Mode>(new XTS_Decryption(bc.release()));
+ }
+#endif
+
+#if defined(BOTAN_HAS_MODE_CFB)
+ if(spec.algo_name() == "CFB")
+ {
+ const size_t feedback_bits = spec.arg_as_integer(1, 8*bc->block_size());
+ if(direction == ENCRYPTION)
+ return std::unique_ptr<Cipher_Mode>(new CFB_Encryption(bc.release(), feedback_bits));
+ else
+ return std::unique_ptr<Cipher_Mode>(new CFB_Decryption(bc.release(), feedback_bits));
+ }
+#endif
+
+#endif
+
+ return std::unique_ptr<Cipher_Mode>();
+ }
+
+//static
+std::vector<std::string> Cipher_Mode::providers(const std::string& algo_spec)
+ {
+ const std::vector<std::string>& possible = { "base", "openssl", "commoncrypto" };
+ std::vector<std::string> providers;
+ for(auto&& prov : possible)
+ {
+ std::unique_ptr<Cipher_Mode> mode = Cipher_Mode::create(algo_spec, ENCRYPTION, prov);
+ if(mode)
+ {
+ providers.push_back(prov); // available
+ }
+ }
+ return providers;
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/modes/cipher_mode.h b/comm/third_party/botan/src/lib/modes/cipher_mode.h
new file mode 100644
index 0000000000..9bf0b6811e
--- /dev/null
+++ b/comm/third_party/botan/src/lib/modes/cipher_mode.h
@@ -0,0 +1,198 @@
+/*
+* Cipher Modes
+* (C) 2013,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_CIPHER_MODE_H_
+#define BOTAN_CIPHER_MODE_H_
+
+#include <botan/secmem.h>
+#include <botan/sym_algo.h>
+#include <botan/exceptn.h>
+#include <string>
+#include <vector>
+
+namespace Botan {
+
+/**
+* The two possible directions for cipher filters, determining whether they
+* actually perform encryption or decryption.
+*/
+enum Cipher_Dir : int { ENCRYPTION, DECRYPTION };
+
+/**
+* Interface for cipher modes
+*/
+class BOTAN_PUBLIC_API(2,0) Cipher_Mode : public SymmetricAlgorithm
+ {
+ public:
+ /**
+ * @return list of available providers for this algorithm, empty if not available
+ * @param algo_spec algorithm name
+ */
+ static std::vector<std::string> providers(const std::string& algo_spec);
+
+ /**
+ * Create an AEAD mode
+ * @param algo the algorithm to create
+ * @param direction specify if this should be an encryption or decryption AEAD
+ * @param provider optional specification for provider to use
+ * @return an AEAD mode or a null pointer if not available
+ */
+ static std::unique_ptr<Cipher_Mode> create(const std::string& algo,
+ Cipher_Dir direction,
+ const std::string& provider = "");
+
+ /**
+ * Create an AEAD mode, or throw
+ * @param algo the algorithm to create
+ * @param direction specify if this should be an encryption or decryption AEAD
+ * @param provider optional specification for provider to use
+ * @return an AEAD mode, or throw an exception
+ */
+ static std::unique_ptr<Cipher_Mode> create_or_throw(const std::string& algo,
+ Cipher_Dir direction,
+ const std::string& provider = "");
+
+ /*
+ * Prepare for processing a message under the specified nonce
+ */
+ virtual void start_msg(const uint8_t nonce[], size_t nonce_len) = 0;
+
+ /**
+ * Begin processing a message.
+ * @param nonce the per message nonce
+ */
+ template<typename Alloc>
+ void start(const std::vector<uint8_t, Alloc>& nonce)
+ {
+ start_msg(nonce.data(), nonce.size());
+ }
+
+ /**
+ * Begin processing a message.
+ * @param nonce the per message nonce
+ * @param nonce_len length of nonce
+ */
+ void start(const uint8_t nonce[], size_t nonce_len)
+ {
+ start_msg(nonce, nonce_len);
+ }
+
+ /**
+ * Begin processing a message.
+ */
+ void start()
+ {
+ return start_msg(nullptr, 0);
+ }
+
+ /**
+ * Process message blocks
+ *
+ * Input must be a multiple of update_granularity
+ *
+ * Processes msg in place and returns bytes written. Normally
+ * this will be either msg_len (indicating the entire message was
+ * processed) or for certain AEAD modes zero (indicating that the
+ * mode requires the entire message be processed in one pass).
+ *
+ * @param msg the message to be processed
+ * @param msg_len length of the message in bytes
+ */
+ virtual size_t process(uint8_t msg[], size_t msg_len) = 0;
+
+ /**
+ * Process some data. Input must be in size update_granularity() uint8_t blocks.
+ * @param buffer in/out parameter which will possibly be resized
+ * @param offset an offset into blocks to begin processing
+ */
+ void update(secure_vector<uint8_t>& buffer, size_t offset = 0)
+ {
+ BOTAN_ASSERT(buffer.size() >= offset, "Offset ok");
+ uint8_t* buf = buffer.data() + offset;
+ const size_t buf_size = buffer.size() - offset;
+
+ const size_t written = process(buf, buf_size);
+ buffer.resize(offset + written);
+ }
+
+ /**
+ * Complete processing of a message.
+ *
+ * @param final_block in/out parameter which must be at least
+ * minimum_final_size() bytes, and will be set to any final output
+ * @param offset an offset into final_block to begin processing
+ */
+ virtual void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) = 0;
+
+ /**
+ * Returns the size of the output if this transform is used to process a
+ * message with input_length bytes. In most cases the answer is precise.
+ * If it is not possible to precise (namely for CBC decryption) instead a
+ * lower bound is returned.
+ */
+ virtual size_t output_length(size_t input_length) const = 0;
+
+ /**
+ * @return size of required blocks to update
+ */
+ virtual size_t update_granularity() const = 0;
+
+ /**
+ * @return required minimium size to finalize() - may be any
+ * length larger than this.
+ */
+ virtual size_t minimum_final_size() const = 0;
+
+ /**
+ * @return the default size for a nonce
+ */
+ virtual size_t default_nonce_length() const = 0;
+
+ /**
+ * @return true iff nonce_len is a valid length for the nonce
+ */
+ virtual bool valid_nonce_length(size_t nonce_len) const = 0;
+
+ /**
+ * Resets just the message specific state and allows encrypting again under the existing key
+ */
+ virtual void reset() = 0;
+
+ /**
+ * @return true iff this mode provides authentication as well as
+ * confidentiality.
+ */
+ virtual bool authenticated() const { return false; }
+
+ /**
+ * @return the size of the authentication tag used (in bytes)
+ */
+ virtual size_t tag_size() const { return 0; }
+
+ /**
+ * @return provider information about this implementation. Default is "base",
+ * might also return "sse2", "avx2", "openssl", or some other arbitrary string.
+ */
+ virtual std::string provider() const { return "base"; }
+ };
+
+/**
+* Get a cipher mode by name (eg "AES-128/CBC" or "Serpent/XTS")
+* @param algo_spec cipher name
+* @param direction ENCRYPTION or DECRYPTION
+* @param provider provider implementation to choose
+*/
+inline Cipher_Mode* get_cipher_mode(const std::string& algo_spec,
+ Cipher_Dir direction,
+ const std::string& provider = "")
+ {
+ return Cipher_Mode::create(algo_spec, direction, provider).release();
+ }
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/modes/info.txt b/comm/third_party/botan/src/lib/modes/info.txt
new file mode 100644
index 0000000000..4c19db04ca
--- /dev/null
+++ b/comm/third_party/botan/src/lib/modes/info.txt
@@ -0,0 +1,9 @@
+<defines>
+MODES -> 20150626
+CIPHER_MODES -> 20180124
+</defines>
+
+<header:public>
+cipher_mode.h
+stream_mode.h
+</header:public>
diff --git a/comm/third_party/botan/src/lib/modes/mode_pad/info.txt b/comm/third_party/botan/src/lib/modes/mode_pad/info.txt
new file mode 100644
index 0000000000..12b6e5b3a9
--- /dev/null
+++ b/comm/third_party/botan/src/lib/modes/mode_pad/info.txt
@@ -0,0 +1,3 @@
+<defines>
+CIPHER_MODE_PADDING -> 20131128
+</defines>
diff --git a/comm/third_party/botan/src/lib/modes/mode_pad/mode_pad.cpp b/comm/third_party/botan/src/lib/modes/mode_pad/mode_pad.cpp
new file mode 100644
index 0000000000..18bb71af5d
--- /dev/null
+++ b/comm/third_party/botan/src/lib/modes/mode_pad/mode_pad.cpp
@@ -0,0 +1,333 @@
+/*
+* CBC Padding Methods
+* (C) 1999-2007,2013,2018,2020 Jack Lloyd
+* (C) 2016 René Korthaus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/mode_pad.h>
+#include <botan/exceptn.h>
+#include <botan/internal/ct_utils.h>
+
+namespace Botan {
+
+/**
+* Get a block cipher padding method by name
+*/
+BlockCipherModePaddingMethod* get_bc_pad(const std::string& algo_spec)
+ {
+ if(algo_spec == "NoPadding")
+ return new Null_Padding;
+
+ if(algo_spec == "PKCS7")
+ return new PKCS7_Padding;
+
+ if(algo_spec == "OneAndZeros")
+ return new OneAndZeros_Padding;
+
+ if(algo_spec == "X9.23")
+ return new ANSI_X923_Padding;
+
+ if(algo_spec == "ESP")
+ return new ESP_Padding;
+
+ return nullptr;
+ }
+
+/*
+* Pad with PKCS #7 Method
+*/
+void PKCS7_Padding::add_padding(secure_vector<uint8_t>& buffer,
+ size_t last_byte_pos,
+ size_t BS) const
+ {
+ /*
+ Padding format is
+ 01
+ 0202
+ 030303
+ ...
+ */
+ BOTAN_DEBUG_ASSERT(last_byte_pos < BS);
+
+ const uint8_t padding_len = static_cast<uint8_t>(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<uint8_t>(CT::Mask<size_t>::is_gte(i, start_of_padding));
+ buffer[i] = needs_padding.select(padding_len, buffer[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];
+
+ /*
+ The input should == the block size so if the last byte exceeds
+ that then the padding is certainly invalid
+ */
+ auto bad_input = CT::Mask<size_t>::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)
+ {
+ // Does this byte equal the expected pad byte?
+ const auto pad_eq = CT::Mask<size_t>::is_equal(input[i], last_byte);
+
+ // Ignore values that are not part of the padding
+ const auto in_range = CT::Mask<size_t>::is_gte(i, pad_pos);
+ bad_input |= in_range & (~pad_eq);
+ }
+
+ CT::unpoison(input, input_length);
+
+ return bad_input.select_and_unpoison(input_length, pad_pos);
+ }
+
+/*
+* Pad with ANSI X9.23 Method
+*/
+void ANSI_X923_Padding::add_padding(secure_vector<uint8_t>& buffer,
+ size_t last_byte_pos,
+ size_t BS) const
+ {
+ /*
+ Padding format is
+ 01
+ 0002
+ 000003
+ ...
+ */
+ BOTAN_DEBUG_ASSERT(last_byte_pos < BS);
+
+ const uint8_t padding_len = static_cast<uint8_t>(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<uint8_t>(CT::Mask<size_t>::is_gte(i, start_of_padding));
+ buffer[i] = needs_padding.select(0, buffer[i]);
+ }
+
+ buffer[buffer.size()-1] = padding_len;
+ CT::unpoison(buffer.data(), buffer.size());
+ CT::unpoison(last_byte_pos);
+ }
+
+/*
+* 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;
+
+ CT::poison(input, input_length);
+
+ const size_t last_byte = input[input_length-1];
+
+ auto bad_input = CT::Mask<size_t>::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)
+ {
+ // Ignore values that are not part of the padding
+ const auto in_range = CT::Mask<size_t>::is_gte(i, pad_pos);
+ const auto pad_is_nonzero = CT::Mask<size_t>::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);
+ }
+
+/*
+* Pad with One and Zeros Method
+*/
+void OneAndZeros_Padding::add_padding(secure_vector<uint8_t>& buffer,
+ size_t last_byte_pos,
+ size_t BS) const
+ {
+ /*
+ Padding format is
+ 80
+ 8000
+ 800000
+ ...
+ */
+
+ BOTAN_DEBUG_ASSERT(last_byte_pos < BS);
+
+ const uint8_t padding_len = static_cast<uint8_t>(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<uint8_t>(CT::Mask<size_t>::is_equal(i, start_of_padding));
+ auto needs_00 = CT::Mask<uint8_t>(CT::Mask<size_t>::is_gt(i, start_of_padding));
+ buffer[i] = needs_00.select(0x00, needs_80.select(0x80, buffer[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);
+
+ auto bad_input = CT::Mask<uint8_t>::cleared();
+ auto seen_0x80 = CT::Mask<uint8_t>::cleared();
+
+ size_t pad_pos = input_length - 1;
+ size_t i = input_length;
+
+ while(i)
+ {
+ const auto is_0x80 = CT::Mask<uint8_t>::is_equal(input[i-1], 0x80);
+ const auto is_zero = CT::Mask<uint8_t>::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<size_t>::expand(bad_input).select_and_unpoison(input_length, pad_pos);
+ }
+
+/*
+* Pad with ESP Padding Method
+*/
+void ESP_Padding::add_padding(secure_vector<uint8_t>& buffer,
+ size_t last_byte_pos,
+ size_t BS) const
+ {
+ /*
+ Padding format is
+ 01
+ 0102
+ 010203
+ ...
+ */
+ BOTAN_DEBUG_ASSERT(last_byte_pos < BS);
+
+ const uint8_t padding_len = static_cast<uint8_t>(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;
+
+ uint8_t pad_ctr = 0x01;
+
+ for(size_t i = start_of_last_block; i != end_of_last_block; ++i)
+ {
+ auto needs_padding = CT::Mask<uint8_t>(CT::Mask<size_t>::is_gte(i, start_of_padding));
+ buffer[i] = needs_padding.select(pad_ctr, buffer[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<uint8_t>(input_length);
+ const uint8_t last_byte = input[input_length-1];
+
+ auto bad_input = CT::Mask<uint8_t>::is_zero(last_byte) |
+ CT::Mask<uint8_t>::is_gt(last_byte, input_length_8);
+
+ const uint8_t pad_pos = input_length_8 - last_byte;
+ size_t i = input_length_8 - 1;
+ while(i)
+ {
+ const auto in_range = CT::Mask<size_t>::is_gt(i, pad_pos);
+ const auto incrementing = CT::Mask<uint8_t>::is_equal(input[i-1], input[i]-1);
+
+ bad_input |= CT::Mask<uint8_t>(in_range) & ~incrementing;
+ --i;
+ }
+
+ CT::unpoison(input, input_length);
+ return bad_input.select_and_unpoison(input_length_8, pad_pos);
+ }
+
+
+}
diff --git a/comm/third_party/botan/src/lib/modes/mode_pad/mode_pad.h b/comm/third_party/botan/src/lib/modes/mode_pad/mode_pad.h
new file mode 100644
index 0000000000..b0e4a3cfae
--- /dev/null
+++ b/comm/third_party/botan/src/lib/modes/mode_pad/mode_pad.h
@@ -0,0 +1,160 @@
+/*
+* CBC Padding Methods
+* (C) 1999-2008,2013 Jack Lloyd
+* (C) 2016 René Korthaus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_MODE_PADDING_H_
+#define BOTAN_MODE_PADDING_H_
+
+#include <botan/secmem.h>
+#include <string>
+
+BOTAN_FUTURE_INTERNAL_HEADER(mode_pad.h)
+
+namespace Botan {
+
+/**
+* Block Cipher Mode Padding Method
+* This class is pretty limited, it cannot deal well with
+* randomized padding methods, or any padding method that
+* wants to add more than one block. For instance, it should
+* be possible to define cipher text stealing mode as simply
+* a padding mode for CBC, which happens to consume the last
+* two block (and requires use of the block cipher).
+*/
+class BOTAN_PUBLIC_API(2,0) BlockCipherModePaddingMethod
+ {
+ public:
+ /**
+ * Add padding bytes to buffer.
+ * @param buffer data to pad
+ * @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<uint8_t>& buffer,
+ size_t final_block_bytes,
+ size_t block_size) const = 0;
+
+ /**
+ * 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
+ */
+ virtual size_t unpad(const uint8_t block[], size_t len) const = 0;
+
+ /**
+ * @param block_size of the cipher
+ * @return valid block size for this padding mode
+ */
+ virtual bool valid_blocksize(size_t block_size) const = 0;
+
+ /**
+ * @return name of the mode
+ */
+ virtual std::string name() const = 0;
+
+ /**
+ * virtual destructor
+ */
+ virtual ~BlockCipherModePaddingMethod() = default;
+ };
+
+/**
+* PKCS#7 Padding
+*/
+class BOTAN_PUBLIC_API(2,0) PKCS7_Padding final : public BlockCipherModePaddingMethod
+ {
+ public:
+ void add_padding(secure_vector<uint8_t>& buffer,
+ size_t final_block_bytes,
+ size_t block_size) const override;
+
+ size_t unpad(const uint8_t[], size_t) const override;
+
+ bool valid_blocksize(size_t bs) const override { return (bs > 2 && bs < 256); }
+
+ std::string name() const override { return "PKCS7"; }
+ };
+
+/**
+* ANSI X9.23 Padding
+*/
+class BOTAN_PUBLIC_API(2,0) ANSI_X923_Padding final : public BlockCipherModePaddingMethod
+ {
+ public:
+ void add_padding(secure_vector<uint8_t>& buffer,
+ size_t final_block_bytes,
+ size_t block_size) const override;
+
+ size_t unpad(const uint8_t[], size_t) const override;
+
+ bool valid_blocksize(size_t bs) const override { return (bs > 2 && bs < 256); }
+
+ std::string name() const override { return "X9.23"; }
+ };
+
+/**
+* One And Zeros Padding (ISO/IEC 9797-1, padding method 2)
+*/
+class BOTAN_PUBLIC_API(2,0) OneAndZeros_Padding final : public BlockCipherModePaddingMethod
+ {
+ public:
+ void add_padding(secure_vector<uint8_t>& buffer,
+ size_t final_block_bytes,
+ size_t block_size) const override;
+
+ size_t unpad(const uint8_t[], size_t) const override;
+
+ bool valid_blocksize(size_t bs) const override { return (bs > 2); }
+
+ std::string name() const override { return "OneAndZeros"; }
+ };
+
+/**
+* ESP Padding (RFC 4304)
+*/
+class BOTAN_PUBLIC_API(2,0) ESP_Padding final : public BlockCipherModePaddingMethod
+ {
+ public:
+ void add_padding(secure_vector<uint8_t>& buffer,
+ size_t final_block_bytes,
+ size_t block_size) const override;
+
+ size_t unpad(const uint8_t[], size_t) const override;
+
+ bool valid_blocksize(size_t bs) const override { return (bs > 2 && bs < 256); }
+
+ std::string name() const override { return "ESP"; }
+ };
+
+/**
+* Null Padding
+*/
+class BOTAN_PUBLIC_API(2,0) Null_Padding final : public BlockCipherModePaddingMethod
+ {
+ public:
+ void add_padding(secure_vector<uint8_t>&, size_t, size_t) const override
+ {
+ /* no padding */
+ }
+
+ size_t unpad(const uint8_t[], size_t size) const override { return size; }
+
+ bool valid_blocksize(size_t) const override { return true; }
+
+ std::string name() const override { return "NoPadding"; }
+ };
+
+/**
+* Get a block cipher padding mode by name (eg "NoPadding" or "PKCS7")
+* @param algo_spec block cipher padding mode name
+*/
+BOTAN_PUBLIC_API(2,0) BlockCipherModePaddingMethod* get_bc_pad(const std::string& algo_spec);
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/modes/stream_mode.h b/comm/third_party/botan/src/lib/modes/stream_mode.h
new file mode 100644
index 0000000000..da3fc38cf0
--- /dev/null
+++ b/comm/third_party/botan/src/lib/modes/stream_mode.h
@@ -0,0 +1,84 @@
+/*
+* (C) 2015 Jack Lloyd
+* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_STREAM_MODE_H_
+#define BOTAN_STREAM_MODE_H_
+
+#include <botan/cipher_mode.h>
+
+#if defined(BOTAN_HAS_STREAM_CIPHER)
+ #include <botan/stream_cipher.h>
+#endif
+
+BOTAN_FUTURE_INTERNAL_HEADER(stream_mode.h)
+
+namespace Botan {
+
+#if defined(BOTAN_HAS_STREAM_CIPHER)
+
+class BOTAN_PUBLIC_API(2,0) Stream_Cipher_Mode final : public Cipher_Mode
+ {
+ public:
+ /**
+ * @param cipher underyling stream cipher
+ */
+ explicit Stream_Cipher_Mode(StreamCipher* cipher) : m_cipher(cipher) {}
+
+ size_t process(uint8_t buf[], size_t sz) override
+ {
+ m_cipher->cipher1(buf, sz);
+ return sz;
+ }
+
+ void finish(secure_vector<uint8_t>& buf, size_t offset) override
+ { return update(buf, offset); }
+
+ size_t output_length(size_t input_length) const override { return input_length; }
+
+ size_t update_granularity() const override { return 1; }
+
+ size_t minimum_final_size() const override { return 0; }
+
+ size_t default_nonce_length() const override { return 0; }
+
+ bool valid_nonce_length(size_t nonce_len) const override
+ { return m_cipher->valid_iv_length(nonce_len); }
+
+ Key_Length_Specification key_spec() const override { return m_cipher->key_spec(); }
+
+ std::string name() const override { return m_cipher->name(); }
+
+ void clear() override
+ {
+ m_cipher->clear();
+ reset();
+ }
+
+ void reset() override { /* no msg state */ }
+
+ private:
+ void start_msg(const uint8_t nonce[], size_t nonce_len) override
+ {
+ if(nonce_len > 0)
+ {
+ m_cipher->set_iv(nonce, nonce_len);
+ }
+ }
+
+ void key_schedule(const uint8_t key[], size_t length) override
+ {
+ m_cipher->set_key(key, length);
+ }
+
+ std::unique_ptr<StreamCipher> m_cipher;
+ };
+
+#endif
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/modes/xts/info.txt b/comm/third_party/botan/src/lib/modes/xts/info.txt
new file mode 100644
index 0000000000..cee850be78
--- /dev/null
+++ b/comm/third_party/botan/src/lib/modes/xts/info.txt
@@ -0,0 +1,8 @@
+<defines>
+MODE_XTS -> 20131128
+</defines>
+
+<requires>
+block
+poly_dbl
+</requires>
diff --git a/comm/third_party/botan/src/lib/modes/xts/xts.cpp b/comm/third_party/botan/src/lib/modes/xts/xts.cpp
new file mode 100644
index 0000000000..559584b082
--- /dev/null
+++ b/comm/third_party/botan/src/lib/modes/xts/xts.cpp
@@ -0,0 +1,248 @@
+/*
+* XTS Mode
+* (C) 2009,2013 Jack Lloyd
+* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/xts.h>
+#include <botan/internal/poly_dbl.h>
+
+namespace Botan {
+
+XTS_Mode::XTS_Mode(BlockCipher* cipher) :
+ m_cipher(cipher),
+ m_cipher_block_size(m_cipher->block_size()),
+ m_cipher_parallelism(m_cipher->parallel_bytes())
+ {
+ if(poly_double_supported_size(m_cipher_block_size) == false)
+ {
+ throw Invalid_Argument("Cannot use " + cipher->name() + " with XTS");
+ }
+
+ m_tweak_cipher.reset(m_cipher->clone());
+ }
+
+void XTS_Mode::clear()
+ {
+ m_cipher->clear();
+ m_tweak_cipher->clear();
+ reset();
+ }
+
+void XTS_Mode::reset()
+ {
+ m_tweak.clear();
+ }
+
+std::string XTS_Mode::name() const
+ {
+ return cipher().name() + "/XTS";
+ }
+
+size_t XTS_Mode::minimum_final_size() const
+ {
+ return cipher_block_size();
+ }
+
+Key_Length_Specification XTS_Mode::key_spec() const
+ {
+ return cipher().key_spec().multiple(2);
+ }
+
+size_t XTS_Mode::default_nonce_length() const
+ {
+ return cipher_block_size();
+ }
+
+bool XTS_Mode::valid_nonce_length(size_t n) const
+ {
+ return cipher_block_size() == n;
+ }
+
+void XTS_Mode::key_schedule(const uint8_t key[], size_t length)
+ {
+ const size_t key_half = length / 2;
+
+ if(length % 2 == 1 || !m_cipher->valid_keylength(key_half))
+ throw Invalid_Key_Length(name(), length);
+
+ m_cipher->set_key(key, key_half);
+ m_tweak_cipher->set_key(&key[key_half], key_half);
+ }
+
+void XTS_Mode::start_msg(const uint8_t nonce[], size_t nonce_len)
+ {
+ if(!valid_nonce_length(nonce_len))
+ throw Invalid_IV_Length(name(), nonce_len);
+
+ m_tweak.resize(update_granularity());
+ copy_mem(m_tweak.data(), nonce, nonce_len);
+ m_tweak_cipher->encrypt(m_tweak.data());
+
+ update_tweak(0);
+ }
+
+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);
+
+ const size_t blocks_in_tweak = update_granularity() / BS;
+
+ for(size_t i = 1; i < blocks_in_tweak; ++i)
+ poly_double_n_le(&m_tweak[i*BS], &m_tweak[(i-1)*BS], BS);
+ }
+
+size_t XTS_Encryption::output_length(size_t input_length) const
+ {
+ return input_length;
+ }
+
+size_t XTS_Encryption::process(uint8_t buf[], size_t sz)
+ {
+ BOTAN_STATE_CHECK(tweak_set());
+ const size_t BS = cipher_block_size();
+
+ BOTAN_ASSERT(sz % BS == 0, "Input is full blocks");
+ size_t blocks = sz / BS;
+
+ const size_t blocks_in_tweak = update_granularity() / BS;
+
+ while(blocks)
+ {
+ const size_t to_proc = std::min(blocks, blocks_in_tweak);
+
+ cipher().encrypt_n_xex(buf, tweak(), to_proc);
+
+ buf += to_proc * BS;
+ blocks -= to_proc;
+
+ update_tweak(to_proc);
+ }
+
+ return sz;
+ }
+
+void XTS_Encryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
+ {
+ BOTAN_ASSERT(buffer.size() >= offset, "Offset is sane");
+ const size_t sz = buffer.size() - offset;
+ uint8_t* buf = buffer.data() + offset;
+
+ BOTAN_ASSERT(sz >= minimum_final_size(), "Have sufficient final input in XTS encrypt");
+
+ const size_t BS = cipher_block_size();
+
+ if(sz % BS == 0)
+ {
+ update(buffer, offset);
+ }
+ else
+ {
+ // steal ciphertext
+ const size_t full_blocks = ((sz / BS) - 1) * BS;
+ const size_t final_bytes = sz - full_blocks;
+ BOTAN_ASSERT(final_bytes > BS && final_bytes < 2*BS, "Left over size in expected range");
+
+ secure_vector<uint8_t> last(buf + full_blocks, buf + full_blocks + final_bytes);
+ buffer.resize(full_blocks + offset);
+ update(buffer, offset);
+
+ xor_buf(last, tweak(), BS);
+ cipher().encrypt(last);
+ xor_buf(last, tweak(), BS);
+
+ for(size_t i = 0; i != final_bytes - BS; ++i)
+ {
+ last[i] ^= last[i + BS];
+ last[i + BS] ^= last[i];
+ last[i] ^= last[i + BS];
+ }
+
+ xor_buf(last, tweak() + BS, BS);
+ cipher().encrypt(last);
+ xor_buf(last, tweak() + BS, BS);
+
+ buffer += last;
+ }
+ }
+
+size_t XTS_Decryption::output_length(size_t input_length) const
+ {
+ return input_length;
+ }
+
+size_t XTS_Decryption::process(uint8_t buf[], size_t sz)
+ {
+ BOTAN_STATE_CHECK(tweak_set());
+ const size_t BS = cipher_block_size();
+
+ BOTAN_ASSERT(sz % BS == 0, "Input is full blocks");
+ size_t blocks = sz / BS;
+
+ const size_t blocks_in_tweak = update_granularity() / BS;
+
+ while(blocks)
+ {
+ const size_t to_proc = std::min(blocks, blocks_in_tweak);
+
+ cipher().decrypt_n_xex(buf, tweak(), to_proc);
+
+ buf += to_proc * BS;
+ blocks -= to_proc;
+
+ update_tweak(to_proc);
+ }
+
+ return sz;
+ }
+
+void XTS_Decryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
+ {
+ BOTAN_ASSERT(buffer.size() >= offset, "Offset is sane");
+ const size_t sz = buffer.size() - offset;
+ uint8_t* buf = buffer.data() + offset;
+
+ BOTAN_ASSERT(sz >= minimum_final_size(), "Have sufficient final input in XTS decrypt");
+
+ const size_t BS = cipher_block_size();
+
+ if(sz % BS == 0)
+ {
+ update(buffer, offset);
+ }
+ else
+ {
+ // steal ciphertext
+ const size_t full_blocks = ((sz / BS) - 1) * BS;
+ const size_t final_bytes = sz - full_blocks;
+ BOTAN_ASSERT(final_bytes > BS && final_bytes < 2*BS, "Left over size in expected range");
+
+ secure_vector<uint8_t> last(buf + full_blocks, buf + full_blocks + final_bytes);
+ buffer.resize(full_blocks + offset);
+ update(buffer, offset);
+
+ xor_buf(last, tweak() + BS, BS);
+ cipher().decrypt(last);
+ xor_buf(last, tweak() + BS, BS);
+
+ for(size_t i = 0; i != final_bytes - BS; ++i)
+ {
+ last[i] ^= last[i + BS];
+ last[i + BS] ^= last[i];
+ last[i] ^= last[i + BS];
+ }
+
+ xor_buf(last, tweak(), BS);
+ cipher().decrypt(last);
+ xor_buf(last, tweak(), BS);
+
+ buffer += last;
+ }
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/modes/xts/xts.h b/comm/third_party/botan/src/lib/modes/xts/xts.h
new file mode 100644
index 0000000000..75de93c088
--- /dev/null
+++ b/comm/third_party/botan/src/lib/modes/xts/xts.h
@@ -0,0 +1,103 @@
+/*
+* XTS mode, from IEEE P1619
+* (C) 2009,2013 Jack Lloyd
+* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_MODE_XTS_H_
+#define BOTAN_MODE_XTS_H_
+
+#include <botan/cipher_mode.h>
+#include <botan/block_cipher.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(xts.h)
+
+namespace Botan {
+
+/**
+* IEEE P1619 XTS Mode
+*/
+class BOTAN_PUBLIC_API(2,0) XTS_Mode : public Cipher_Mode
+ {
+ public:
+ std::string name() const override;
+
+ size_t update_granularity() const override { return m_cipher_parallelism; }
+
+ size_t minimum_final_size() const override;
+
+ Key_Length_Specification key_spec() const override;
+
+ size_t default_nonce_length() const override;
+
+ bool valid_nonce_length(size_t n) const override;
+
+ void clear() override;
+
+ void reset() override;
+
+ protected:
+ explicit XTS_Mode(BlockCipher* cipher);
+
+ const uint8_t* tweak() const { return m_tweak.data(); }
+
+ bool tweak_set() const { return m_tweak.empty() == false; }
+
+ const BlockCipher& cipher() const { return *m_cipher; }
+
+ void update_tweak(size_t last_used);
+
+ size_t cipher_block_size() const { return m_cipher_block_size; }
+
+ private:
+ void start_msg(const uint8_t nonce[], size_t nonce_len) override;
+ void key_schedule(const uint8_t key[], size_t length) override;
+
+ std::unique_ptr<BlockCipher> m_cipher;
+ std::unique_ptr<BlockCipher> m_tweak_cipher;
+ secure_vector<uint8_t> m_tweak;
+ const size_t m_cipher_block_size;
+ const size_t m_cipher_parallelism;
+ };
+
+/**
+* IEEE P1619 XTS Encryption
+*/
+class BOTAN_PUBLIC_API(2,0) XTS_Encryption final : public XTS_Mode
+ {
+ public:
+ /**
+ * @param cipher underlying block cipher
+ */
+ explicit XTS_Encryption(BlockCipher* cipher) : XTS_Mode(cipher) {}
+
+ size_t process(uint8_t buf[], size_t size) override;
+
+ void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
+
+ size_t output_length(size_t input_length) const override;
+ };
+
+/**
+* IEEE P1619 XTS Decryption
+*/
+class BOTAN_PUBLIC_API(2,0) XTS_Decryption final : public XTS_Mode
+ {
+ public:
+ /**
+ * @param cipher underlying block cipher
+ */
+ explicit XTS_Decryption(BlockCipher* cipher) : XTS_Mode(cipher) {}
+
+ size_t process(uint8_t buf[], size_t size) override;
+
+ void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
+
+ size_t output_length(size_t input_length) const override;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/passhash/bcrypt/bcrypt.cpp b/comm/third_party/botan/src/lib/passhash/bcrypt/bcrypt.cpp
new file mode 100644
index 0000000000..1d28ddfb4d
--- /dev/null
+++ b/comm/third_party/botan/src/lib/passhash/bcrypt/bcrypt.cpp
@@ -0,0 +1,181 @@
+/*
+* Bcrypt Password Hashing
+* (C) 2010,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/bcrypt.h>
+#include <botan/rng.h>
+#include <botan/blowfish.h>
+#include <botan/base64.h>
+#include <botan/parsing.h>
+
+namespace Botan {
+
+namespace {
+
+std::string bcrypt_base64_encode(const uint8_t input[], size_t length)
+ {
+ // Bcrypt uses a non-standard base64 alphabet
+ const uint8_t OPENBSD_BASE64_SUB[256] = {
+ 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x38, 0x80, 0x80, 0x80, 0x39,
+ 0x79, 0x7A, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x2E, 0x2F, 0x41, 0x42, 0x43, 0x44, 0x45,
+ 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51,
+ 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x59, 0x5A, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
+ 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75,
+ 0x76, 0x77, 0x78, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80
+ };
+
+ std::string b64 = base64_encode(input, length);
+
+ while(b64.size() && b64[b64.size()-1] == '=')
+ b64 = b64.substr(0, b64.size() - 1);
+
+ for(size_t i = 0; i != b64.size(); ++i)
+ b64[i] = OPENBSD_BASE64_SUB[static_cast<uint8_t>(b64[i])];
+
+ return b64;
+ }
+
+std::vector<uint8_t> bcrypt_base64_decode(std::string input)
+ {
+ const uint8_t OPENBSD_BASE64_SUB[256] = {
+ 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x41, 0x42,
+ 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x2B, 0x2F, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
+ 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55,
+ 0x56, 0x57, 0x58, 0x59, 0x5A, 0x61, 0x62, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D,
+ 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
+ 0x7A, 0x30, 0x31, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80
+ };
+
+ for(size_t i = 0; i != input.size(); ++i)
+ input[i] = OPENBSD_BASE64_SUB[static_cast<uint8_t>(input[i])];
+
+ return unlock(base64_decode(input));
+ }
+
+std::string make_bcrypt(const std::string& pass,
+ const std::vector<uint8_t>& 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
+ * time being.
+ * Bcrypt allows up to work factor 31 (2^31 iterations)
+ */
+ BOTAN_ARG_CHECK(work_factor >= 4 && work_factor <= 18,
+ "Invalid bcrypt work factor");
+
+ static const uint8_t BCRYPT_MAGIC[8*3] = {
+ 0x4F, 0x72, 0x70, 0x68, 0x65, 0x61, 0x6E, 0x42,
+ 0x65, 0x68, 0x6F, 0x6C, 0x64, 0x65, 0x72, 0x53,
+ 0x63, 0x72, 0x79, 0x44, 0x6F, 0x75, 0x62, 0x74
+ };
+
+ Blowfish blowfish;
+
+ // Include the trailing NULL byte, so we need c_str() not data()
+ blowfish.salted_set_key(cast_char_ptr_to_uint8(pass.c_str()),
+ pass.length() + 1,
+ salt.data(),
+ salt.size(),
+ work_factor);
+
+ std::vector<uint8_t> ctext(BCRYPT_MAGIC, BCRYPT_MAGIC + 8*3);
+
+ for(size_t i = 0; i != 64; ++i)
+ blowfish.encrypt_n(ctext.data(), ctext.data(), 3);
+
+ std::string salt_b64 = bcrypt_base64_encode(salt.data(), salt.size());
+
+ std::string work_factor_str = std::to_string(work_factor);
+ if(work_factor_str.length() == 1)
+ work_factor_str = "0" + work_factor_str;
+
+ return "$2" + std::string(1, version) + "$" + work_factor_str +
+ "$" + salt_b64.substr(0, 22) +
+ bcrypt_base64_encode(ctext.data(), ctext.size() - 1);
+ }
+
+}
+
+std::string generate_bcrypt(const std::string& pass,
+ RandomNumberGenerator& rng,
+ uint16_t work_factor,
+ char version)
+ {
+ /*
+ 2a, 2b and 2y are identical for our purposes because our implementation of 2a
+ never had the truncation or signed char bugs in the first place.
+ */
+
+ if(version != 'a' && version != 'b' && version != 'y')
+ throw Invalid_Argument("Unknown bcrypt version '" + std::string(1, version) + "'");
+
+ std::vector<uint8_t> salt;
+ rng.random_vec(salt, 16);
+ return make_bcrypt(pass, salt, work_factor, version);
+ }
+
+bool check_bcrypt(const std::string& pass, const std::string& hash)
+ {
+ if(hash.size() != 60 ||
+ hash[0] != '$' || hash[1] != '2' || hash[3] != '$' || hash[6] != '$')
+ {
+ return false;
+ }
+
+ const char bcrypt_version = hash[2];
+
+ if(bcrypt_version != 'a' && bcrypt_version != 'b' && bcrypt_version != 'y')
+ {
+ return false;
+ }
+
+ const uint16_t workfactor = to_uint16(hash.substr(4, 2));
+
+ const std::vector<uint8_t> salt = bcrypt_base64_decode(hash.substr(7, 22));
+ if(salt.size() != 16)
+ return false;
+
+ const std::string compare = make_bcrypt(pass, salt, workfactor, bcrypt_version);
+
+ return same_mem(hash.data(), compare.data(), compare.size());
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/passhash/bcrypt/bcrypt.h b/comm/third_party/botan/src/lib/passhash/bcrypt/bcrypt.h
new file mode 100644
index 0000000000..cdf9cf3d19
--- /dev/null
+++ b/comm/third_party/botan/src/lib/passhash/bcrypt/bcrypt.h
@@ -0,0 +1,49 @@
+/*
+* Bcrypt Password Hashing
+* (C) 2011 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_BCRYPT_H_
+#define BOTAN_BCRYPT_H_
+
+#include <botan/types.h>
+#include <string>
+
+namespace Botan {
+
+class RandomNumberGenerator;
+
+/**
+* Create a password hash using Bcrypt
+*
+* @warning The password is truncated at at most 72 characters; characters after
+* that do not have any effect on the resulting hash. To support longer
+* passwords, consider pre-hashing the password, for example by using
+* the hex encoding of SHA-256 of the password as the input to bcrypt.
+*
+* @param password the password.
+* @param rng a random number generator
+* @param work_factor how much work to do to slow down guessing attacks
+* @param version which version to emit (may be 'a', 'b', or 'y' all of which
+* have identical behavior in this implementation).
+*
+* @see https://www.usenix.org/events/usenix99/provos/provos_html/
+*/
+std::string BOTAN_PUBLIC_API(2,0) generate_bcrypt(const std::string& password,
+ RandomNumberGenerator& rng,
+ uint16_t work_factor = 12,
+ char version = 'a');
+
+/**
+* Check a previously created password hash
+* @param password the password to check against
+* @param hash the stored hash to check against
+*/
+bool BOTAN_PUBLIC_API(2,0) check_bcrypt(const std::string& password,
+ const std::string& hash);
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/passhash/bcrypt/info.txt b/comm/third_party/botan/src/lib/passhash/bcrypt/info.txt
new file mode 100644
index 0000000000..6be060ea0f
--- /dev/null
+++ b/comm/third_party/botan/src/lib/passhash/bcrypt/info.txt
@@ -0,0 +1,9 @@
+<defines>
+BCRYPT -> 20131128
+</defines>
+
+<requires>
+blowfish
+rng
+base64
+</requires>
diff --git a/comm/third_party/botan/src/lib/passhash/passhash9/info.txt b/comm/third_party/botan/src/lib/passhash/passhash9/info.txt
new file mode 100644
index 0000000000..525427b45d
--- /dev/null
+++ b/comm/third_party/botan/src/lib/passhash/passhash9/info.txt
@@ -0,0 +1,9 @@
+<defines>
+PASSHASH9 -> 20131128
+</defines>
+
+<requires>
+pbkdf2
+rng
+base64
+</requires>
diff --git a/comm/third_party/botan/src/lib/passhash/passhash9/passhash9.cpp b/comm/third_party/botan/src/lib/passhash/passhash9/passhash9.cpp
new file mode 100644
index 0000000000..98f5a54d51
--- /dev/null
+++ b/comm/third_party/botan/src/lib/passhash/passhash9/passhash9.cpp
@@ -0,0 +1,142 @@
+/*
+* Passhash9 Password Hashing
+* (C) 2010 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/passhash9.h>
+#include <botan/rng.h>
+#include <botan/loadstor.h>
+#include <botan/pbkdf2.h>
+#include <botan/base64.h>
+
+namespace Botan {
+
+namespace {
+
+const std::string MAGIC_PREFIX = "$9$";
+
+const size_t WORKFACTOR_BYTES = 2;
+const size_t ALGID_BYTES = 1;
+const size_t SALT_BYTES = 12; // 96 bits of salt
+const size_t PASSHASH9_PBKDF_OUTPUT_LEN = 24; // 192 bits output
+
+const size_t WORK_FACTOR_SCALE = 10000;
+
+std::unique_ptr<MessageAuthenticationCode> get_pbkdf_prf(uint8_t alg_id)
+ {
+ if(alg_id == 0)
+ return MessageAuthenticationCode::create("HMAC(SHA-1)");
+ else if(alg_id == 1)
+ return MessageAuthenticationCode::create("HMAC(SHA-256)");
+ else if(alg_id == 2)
+ return MessageAuthenticationCode::create("CMAC(Blowfish)");
+ else if(alg_id == 3)
+ return MessageAuthenticationCode::create("HMAC(SHA-384)");
+ else if(alg_id == 4)
+ return MessageAuthenticationCode::create("HMAC(SHA-512)");
+ return nullptr;
+ }
+
+}
+
+std::string generate_passhash9(const std::string& pass,
+ RandomNumberGenerator& rng,
+ uint16_t work_factor,
+ uint8_t alg_id)
+ {
+ BOTAN_ARG_CHECK(work_factor > 0 && work_factor < 512, "Invalid Passhash9 work factor");
+
+ std::unique_ptr<MessageAuthenticationCode> prf = get_pbkdf_prf(alg_id);
+
+ if(!prf)
+ throw Invalid_Argument("Passhash9: Algorithm id " +
+ std::to_string(alg_id) +
+ " is not defined");
+
+ PKCS5_PBKDF2 kdf(prf.release()); // takes ownership of pointer
+
+ secure_vector<uint8_t> salt(SALT_BYTES);
+ rng.randomize(salt.data(), salt.size());
+
+ const size_t kdf_iterations = WORK_FACTOR_SCALE * work_factor;
+
+ secure_vector<uint8_t> blob;
+ blob.push_back(alg_id);
+ blob.push_back(get_byte(0, work_factor));
+ blob.push_back(get_byte(1, work_factor));
+ 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);
+ }
+
+bool check_passhash9(const std::string& pass, const std::string& hash)
+ {
+ const size_t BINARY_LENGTH =
+ ALGID_BYTES +
+ WORKFACTOR_BYTES +
+ PASSHASH9_PBKDF_OUTPUT_LEN +
+ SALT_BYTES;
+
+ const size_t BASE64_LENGTH =
+ MAGIC_PREFIX.size() + (BINARY_LENGTH * 8) / 6;
+
+ if(hash.size() != BASE64_LENGTH)
+ return false;
+
+ for(size_t i = 0; i != MAGIC_PREFIX.size(); ++i)
+ if(hash[i] != MAGIC_PREFIX[i])
+ return false;
+
+ secure_vector<uint8_t> bin = base64_decode(hash.c_str() + MAGIC_PREFIX.size());
+
+ if(bin.size() != BINARY_LENGTH)
+ return false;
+
+ uint8_t alg_id = bin[0];
+
+ const size_t work_factor = load_be<uint16_t>(&bin[ALGID_BYTES], 0);
+
+ // Bug in the format, bad states shouldn't be representable, but are...
+ if(work_factor == 0)
+ return false;
+
+ if(work_factor > 512)
+ throw Invalid_Argument("Requested passhash9 work factor " +
+ std::to_string(work_factor) + " is too large");
+
+ const size_t kdf_iterations = WORK_FACTOR_SCALE * work_factor;
+
+ std::unique_ptr<MessageAuthenticationCode> pbkdf_prf = get_pbkdf_prf(alg_id);
+
+ if(!pbkdf_prf)
+ return false; // unknown algorithm, reject
+
+ PKCS5_PBKDF2 kdf(pbkdf_prf.release()); // takes ownership of pointer
+
+ secure_vector<uint8_t> cmp = kdf.derive_key(
+ PASSHASH9_PBKDF_OUTPUT_LEN,
+ pass,
+ &bin[ALGID_BYTES + WORKFACTOR_BYTES], SALT_BYTES,
+ kdf_iterations).bits_of();
+
+ return constant_time_compare(cmp.data(),
+ &bin[ALGID_BYTES + WORKFACTOR_BYTES + SALT_BYTES],
+ PASSHASH9_PBKDF_OUTPUT_LEN);
+ }
+
+bool is_passhash9_alg_supported(uint8_t alg_id)
+ {
+ if (get_pbkdf_prf(alg_id))
+ {
+ return true;
+ }
+ return false;
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/passhash/passhash9/passhash9.h b/comm/third_party/botan/src/lib/passhash/passhash9/passhash9.h
new file mode 100644
index 0000000000..b312cb1bf9
--- /dev/null
+++ b/comm/third_party/botan/src/lib/passhash/passhash9/passhash9.h
@@ -0,0 +1,52 @@
+/*
+* Passhash9 Password Hashing
+* (C) 2010 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_PASSHASH9_H_
+#define BOTAN_PASSHASH9_H_
+
+#include <botan/types.h>
+#include <string>
+
+namespace Botan {
+
+class RandomNumberGenerator;
+
+/**
+* Create a password hash using PBKDF2
+* @param password the password
+* @param rng a random number generator
+* @param work_factor how much work to do to slow down guessing attacks
+* @param alg_id specifies which PRF to use with PBKDF2
+* 0 is HMAC(SHA-1)
+* 1 is HMAC(SHA-256)
+* 2 is CMAC(Blowfish)
+* 3 is HMAC(SHA-384)
+* 4 is HMAC(SHA-512)
+* all other values are currently undefined
+*/
+std::string BOTAN_PUBLIC_API(2,0) generate_passhash9(const std::string& password,
+ RandomNumberGenerator& rng,
+ uint16_t work_factor = 15,
+ uint8_t alg_id = 4);
+
+/**
+* Check a previously created password hash
+* @param password the password to check against
+* @param hash the stored hash to check against
+*/
+bool BOTAN_PUBLIC_API(2,0) check_passhash9(const std::string& password,
+ const std::string& hash);
+
+/**
+* Check if the PRF used with PBKDF2 is supported
+* @param alg_id alg_id used in generate_passhash9()
+*/
+bool BOTAN_PUBLIC_API(2,3) is_passhash9_alg_supported(uint8_t alg_id);
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/pbkdf/argon2/argon2.cpp b/comm/third_party/botan/src/lib/pbkdf/argon2/argon2.cpp
new file mode 100644
index 0000000000..0d767e04e7
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pbkdf/argon2/argon2.cpp
@@ -0,0 +1,443 @@
+/**
+* (C) 2018,2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/argon2.h>
+#include <botan/loadstor.h>
+#include <botan/hash.h>
+#include <botan/mem_ops.h>
+#include <botan/rotate.h>
+#include <botan/exceptn.h>
+
+namespace Botan {
+
+namespace {
+
+static const size_t SYNC_POINTS = 4;
+
+secure_vector<uint8_t> argon2_H0(HashFunction& blake2b,
+ size_t output_len,
+ const char* password, size_t password_len,
+ const uint8_t salt[], size_t salt_len,
+ const uint8_t key[], size_t key_len,
+ const uint8_t ad[], size_t ad_len,
+ size_t y, size_t p, size_t M, size_t t)
+ {
+ const uint8_t v = 19; // Argon2 version code
+
+ blake2b.update_le(static_cast<uint32_t>(p));
+ blake2b.update_le(static_cast<uint32_t>(output_len));
+ blake2b.update_le(static_cast<uint32_t>(M));
+ blake2b.update_le(static_cast<uint32_t>(t));
+ blake2b.update_le(static_cast<uint32_t>(v));
+ blake2b.update_le(static_cast<uint32_t>(y));
+
+ blake2b.update_le(static_cast<uint32_t>(password_len));
+ blake2b.update(cast_char_ptr_to_uint8(password), password_len);
+
+ blake2b.update_le(static_cast<uint32_t>(salt_len));
+ blake2b.update(salt, salt_len);
+
+ blake2b.update_le(static_cast<uint32_t>(key_len));
+ blake2b.update(key, key_len);
+
+ blake2b.update_le(static_cast<uint32_t>(ad_len));
+ blake2b.update(ad, ad_len);
+
+ return blake2b.final();
+ }
+
+void Htick(secure_vector<uint8_t>& T,
+ uint8_t output[],
+ size_t output_len,
+ HashFunction& blake2b,
+ const secure_vector<uint8_t>& H0,
+ size_t p0, size_t p1)
+ {
+ BOTAN_ASSERT_NOMSG(output_len % 64 == 0);
+
+ blake2b.update_le(static_cast<uint32_t>(output_len));
+ blake2b.update(H0);
+ blake2b.update_le(static_cast<uint32_t>(p0));
+ blake2b.update_le(static_cast<uint32_t>(p1));
+
+ blake2b.final(&T[0]);
+
+ while(output_len > 64)
+ {
+ copy_mem(output, &T[0], 32);
+ output_len -= 32;
+ output += 32;
+
+ blake2b.update(T);
+ blake2b.final(&T[0]);
+ }
+
+ if(output_len > 0)
+ copy_mem(output, &T[0], output_len);
+ }
+
+void extract_key(uint8_t output[], size_t output_len,
+ const secure_vector<uint64_t>& B,
+ size_t memory, size_t threads)
+ {
+ const size_t lanes = memory / threads;
+
+ secure_vector<uint64_t> sum(128);
+
+ for(size_t lane = 0; lane != threads; ++lane)
+ {
+ size_t start = 128*(lane * lanes + lanes - 1);
+ size_t end = 128*(lane * lanes + lanes);
+
+ for(size_t j = start; j != end; ++j)
+ {
+ sum[j % 128] ^= B[j];
+ }
+ }
+
+ secure_vector<uint8_t> sum8(1024);
+ copy_out_le(sum8.data(), 1024, sum.data());
+
+ if(output_len <= 64)
+ {
+ std::unique_ptr<HashFunction> blake2b = HashFunction::create_or_throw("BLAKE2b(" + std::to_string(output_len*8) + ")");
+ blake2b->update_le(static_cast<uint32_t>(output_len));
+ blake2b->update(sum8.data(), sum8.size());
+ blake2b->final(output);
+ }
+ else
+ {
+ secure_vector<uint8_t> T(64);
+
+ std::unique_ptr<HashFunction> blake2b = HashFunction::create_or_throw("BLAKE2b(512)");
+ blake2b->update_le(static_cast<uint32_t>(output_len));
+ blake2b->update(sum8.data(), sum8.size());
+ blake2b->final(&T[0]);
+
+ while(output_len > 64)
+ {
+ copy_mem(output, &T[0], 32);
+ output_len -= 32;
+ output += 32;
+
+ if(output_len > 64)
+ {
+ blake2b->update(T);
+ blake2b->final(&T[0]);
+ }
+ }
+
+ if(output_len == 64)
+ {
+ blake2b->update(T);
+ blake2b->final(output);
+ }
+ else
+ {
+ std::unique_ptr<HashFunction> blake2b_f = HashFunction::create_or_throw("BLAKE2b(" + std::to_string(output_len*8) + ")");
+ blake2b_f->update(T);
+ blake2b_f->final(output);
+ }
+ }
+ }
+
+void init_blocks(secure_vector<uint64_t>& B,
+ HashFunction& blake2b,
+ const secure_vector<uint8_t>& H0,
+ size_t memory,
+ size_t threads)
+ {
+ BOTAN_ASSERT_NOMSG(B.size() >= threads*256);
+
+ secure_vector<uint8_t> H(1024);
+ secure_vector<uint8_t> T(blake2b.output_length());
+
+ for(size_t i = 0; i != threads; ++i)
+ {
+ const size_t B_off = i * (memory / threads);
+
+ BOTAN_ASSERT_NOMSG(B.size() >= 128*(B_off+2));
+
+ Htick(T, &H[0], H.size(), blake2b, H0, 0, i);
+
+ for(size_t j = 0; j != 128; ++j)
+ {
+ B[128*B_off+j] = load_le<uint64_t>(H.data(), j);
+ }
+
+ Htick(T, &H[0], H.size(), blake2b, H0, 1, i);
+
+ for(size_t j = 0; j != 128; ++j)
+ {
+ B[128*(B_off+1)+j] = load_le<uint64_t>(H.data(), j);
+ }
+ }
+ }
+
+inline void blamka_G(uint64_t& A, uint64_t& B, uint64_t& C, uint64_t& D)
+ {
+ A += B + (static_cast<uint64_t>(2) * static_cast<uint32_t>(A)) * static_cast<uint32_t>(B);
+ D = rotr<32>(A ^ D);
+
+ C += D + (static_cast<uint64_t>(2) * static_cast<uint32_t>(C)) * static_cast<uint32_t>(D);
+ B = rotr<24>(B ^ C);
+
+ A += B + (static_cast<uint64_t>(2) * static_cast<uint32_t>(A)) * static_cast<uint32_t>(B);
+ D = rotr<16>(A ^ D);
+
+ C += D + (static_cast<uint64_t>(2) * static_cast<uint32_t>(C)) * static_cast<uint32_t>(D);
+ B = rotr<63>(B ^ C);
+ }
+
+inline void blamka(uint64_t& V0, uint64_t& V1, uint64_t& V2, uint64_t& V3,
+ uint64_t& V4, uint64_t& V5, uint64_t& V6, uint64_t& V7,
+ uint64_t& V8, uint64_t& V9, uint64_t& VA, uint64_t& VB,
+ uint64_t& VC, uint64_t& VD, uint64_t& VE, uint64_t& VF)
+ {
+ blamka_G(V0, V4, V8, VC);
+ blamka_G(V1, V5, V9, VD);
+ blamka_G(V2, V6, VA, VE);
+ blamka_G(V3, V7, VB, VF);
+
+ blamka_G(V0, V5, VA, VF);
+ blamka_G(V1, V6, VB, VC);
+ blamka_G(V2, V7, V8, VD);
+ blamka_G(V3, V4, V9, VE);
+ }
+
+void process_block_xor(secure_vector<uint64_t>& T,
+ secure_vector<uint64_t>& B,
+ size_t offset,
+ size_t prev,
+ size_t new_offset)
+ {
+ for(size_t i = 0; i != 128; ++i)
+ T[i] = B[128*prev+i] ^ B[128*new_offset+i];
+
+ for(size_t i = 0; i != 128; i += 16)
+ {
+ blamka(T[i+ 0], T[i+ 1], T[i+ 2], T[i+ 3],
+ T[i+ 4], T[i+ 5], T[i+ 6], T[i+ 7],
+ T[i+ 8], T[i+ 9], T[i+10], T[i+11],
+ T[i+12], T[i+13], T[i+14], T[i+15]);
+ }
+
+ for(size_t i = 0; i != 128 / 8; i += 2)
+ {
+ blamka(T[ i], T[ i+1], T[ 16+i], T[ 16+i+1],
+ T[ 32+i], T[ 32+i+1], T[ 48+i], T[ 48+i+1],
+ T[ 64+i], T[ 64+i+1], T[ 80+i], T[ 80+i+1],
+ T[ 96+i], T[ 96+i+1], T[112+i], T[112+i+1]);
+ }
+
+ for(size_t i = 0; i != 128; ++i)
+ B[128*offset + i] ^= T[i] ^ B[128*prev+i] ^ B[128*new_offset+i];
+ }
+
+void gen_2i_addresses(secure_vector<uint64_t>& T, secure_vector<uint64_t>& B,
+ size_t n, size_t lane, size_t slice, size_t memory,
+ size_t time, size_t mode, size_t cnt)
+ {
+ BOTAN_ASSERT_NOMSG(B.size() == 128);
+ BOTAN_ASSERT_NOMSG(T.size() == 128);
+
+ clear_mem(B.data(), B.size());
+ B[0] = n;
+ B[1] = lane;
+ B[2] = slice;
+ B[3] = memory;
+ B[4] = time;
+ B[5] = mode;
+ B[6] = cnt;
+
+ for(size_t r = 0; r != 2; ++r)
+ {
+ copy_mem(T.data(), B.data(), B.size());
+
+ for(size_t i = 0; i != 128; i += 16)
+ {
+ blamka(T[i+ 0], T[i+ 1], T[i+ 2], T[i+ 3],
+ T[i+ 4], T[i+ 5], T[i+ 6], T[i+ 7],
+ T[i+ 8], T[i+ 9], T[i+10], T[i+11],
+ T[i+12], T[i+13], T[i+14], T[i+15]);
+ }
+ for(size_t i = 0; i != 128 / 8; i += 2)
+ {
+ blamka(T[ i], T[ i+1], T[ 16+i], T[ 16+i+1],
+ T[ 32+i], T[ 32+i+1], T[ 48+i], T[ 48+i+1],
+ T[ 64+i], T[ 64+i+1], T[ 80+i], T[ 80+i+1],
+ T[ 96+i], T[ 96+i+1], T[112+i], T[112+i+1]);
+ }
+
+ for(size_t i = 0; i != 128; ++i)
+ B[i] ^= T[i];
+ }
+ }
+
+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<uint32_t>(random >> 32) % threads;
+
+ if(n == 0 && slice == 0)
+ ref_lane = lane;
+
+ size_t m = 3*segments;
+ size_t s = ((slice+1) % 4)*segments;
+
+ if(lane == ref_lane)
+ m += index;
+
+ if(n == 0) {
+ m = slice*segments;
+ s = 0;
+ if(slice == 0 || lane == ref_lane)
+ m += index;
+ }
+
+ if(index == 0 || lane == ref_lane)
+ m -= 1;
+
+ uint64_t p = static_cast<uint32_t>(random);
+ p = (p * p) >> 32;
+ p = (p * m) >> 32;
+
+ return static_cast<uint32_t>(ref_lane*lanes + (s + m - (p+1)) % lanes);
+ }
+
+void process_block_argon2d(secure_vector<uint64_t>& T,
+ secure_vector<uint64_t>& B,
+ size_t n, size_t slice, size_t lane,
+ size_t lanes, size_t segments, size_t threads)
+ {
+ size_t index = 0;
+ if(n == 0 && slice == 0)
+ index = 2;
+
+ while(index < segments)
+ {
+ const size_t offset = lane*lanes + slice*segments + index;
+
+ size_t prev = offset - 1;
+ if(index == 0 && slice == 0)
+ prev += lanes;
+
+ const uint64_t random = B.at(128*prev);
+ const size_t new_offset = index_alpha(random, lanes, segments, threads, n, slice, lane, index);
+
+ process_block_xor(T, B, offset, prev, new_offset);
+
+ index += 1;
+ }
+ }
+
+void process_block_argon2i(secure_vector<uint64_t>& T,
+ secure_vector<uint64_t>& B,
+ size_t n, size_t slice, size_t lane,
+ size_t lanes, size_t segments, size_t threads, uint8_t mode,
+ size_t memory, size_t time)
+ {
+ size_t index = 0;
+ if(n == 0 && slice == 0)
+ index = 2;
+
+ secure_vector<uint64_t> addresses(128);
+ size_t address_counter = 1;
+
+ gen_2i_addresses(T, addresses, n, lane, slice, memory, time, mode, address_counter);
+
+ while(index < segments)
+ {
+ const size_t offset = lane*lanes + slice*segments + index;
+
+ size_t prev = offset - 1;
+ if(index == 0 && slice == 0)
+ prev += lanes;
+
+ if(index > 0 && index % 128 == 0)
+ {
+ address_counter += 1;
+ gen_2i_addresses(T, addresses, n, lane, slice, memory, time, mode, address_counter);
+ }
+
+ const uint64_t random = addresses[index % 128];
+ const size_t new_offset = index_alpha(random, lanes, segments, threads, n, slice, lane, index);
+
+ process_block_xor(T, B, offset, prev, new_offset);
+
+ index += 1;
+ }
+ }
+
+void process_blocks(secure_vector<uint64_t>& B,
+ size_t t,
+ size_t memory,
+ size_t threads,
+ uint8_t mode)
+ {
+ const size_t lanes = memory / threads;
+ const size_t segments = lanes / SYNC_POINTS;
+
+ secure_vector<uint64_t> T(128);
+ for(size_t n = 0; n != t; ++n)
+ {
+ for(size_t slice = 0; slice != SYNC_POINTS; ++slice)
+ {
+ // TODO can run this in Thread_Pool
+ for(size_t lane = 0; lane != threads; ++lane)
+ {
+ if(mode == 1 || (mode == 2 && n == 0 && slice < SYNC_POINTS/2))
+ process_block_argon2i(T, B, n, slice, lane, lanes, segments, threads, mode, memory, t);
+ else
+ process_block_argon2d(T, B, n, slice, lane, lanes, segments, threads);
+ }
+ }
+ }
+
+ }
+
+}
+
+void argon2(uint8_t output[], size_t output_len,
+ const char* password, size_t password_len,
+ const uint8_t salt[], size_t salt_len,
+ const uint8_t key[], size_t key_len,
+ const uint8_t ad[], size_t ad_len,
+ uint8_t mode, size_t threads, size_t M, size_t t)
+ {
+ BOTAN_ARG_CHECK(mode == 0 || mode == 1 || mode == 2, "Unknown Argon2 mode parameter");
+ BOTAN_ARG_CHECK(output_len >= 4, "Invalid Argon2 output length");
+ BOTAN_ARG_CHECK(threads >= 1 && threads <= 128, "Invalid Argon2 threads parameter");
+ BOTAN_ARG_CHECK(M >= 8*threads && M <= 8192*1024, "Invalid Argon2 M parameter");
+ BOTAN_ARG_CHECK(t >= 1, "Invalid Argon2 t parameter");
+
+ std::unique_ptr<HashFunction> blake2 = HashFunction::create_or_throw("BLAKE2b");
+
+ const auto H0 = argon2_H0(*blake2, output_len,
+ password, password_len,
+ salt, salt_len,
+ key, key_len,
+ ad, ad_len,
+ mode, threads, M, t);
+
+ const size_t memory = (M / (SYNC_POINTS*threads)) * (SYNC_POINTS*threads);
+
+ secure_vector<uint64_t> B(memory * 1024/8);
+
+ init_blocks(B, *blake2, H0, memory, threads);
+ process_blocks(B, t, memory, threads, mode);
+
+ clear_mem(output, output_len);
+ extract_key(output, output_len, B, memory, threads);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/pbkdf/argon2/argon2.h b/comm/third_party/botan/src/lib/pbkdf/argon2/argon2.h
new file mode 100644
index 0000000000..3a1b859f0c
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pbkdf/argon2/argon2.h
@@ -0,0 +1,118 @@
+/**
+* (C) 2018,2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_ARGON2_H_
+#define BOTAN_ARGON2_H_
+
+#include <botan/pwdhash.h>
+
+//BOTAN_FUTURE_INTERNAL_HEADER(argon2.h)
+
+namespace Botan {
+
+class RandomNumberGenerator;
+
+/**
+* Argon2 key derivation function
+*/
+class BOTAN_PUBLIC_API(2,11) Argon2 final : public PasswordHash
+ {
+ 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
+ */
+ void derive_key(uint8_t out[], size_t out_len,
+ const char* password, size_t password_len,
+ const uint8_t salt[], size_t salt_len) const override;
+
+ std::string to_string() const override;
+
+ size_t M() const { return m_M; }
+ size_t t() const { return m_t; }
+ size_t p() const { return m_p; }
+
+ size_t iterations() const override { return t(); }
+
+ size_t parallelism() const override { return p(); }
+
+ size_t memory_param() const override { return M(); }
+
+ size_t total_memory_usage() const override { return M() * 1024; }
+
+ private:
+ uint8_t m_family;
+ size_t m_M, m_t, m_p;
+ };
+
+class BOTAN_PUBLIC_API(2,11) Argon2_Family final : public PasswordHashFamily
+ {
+ public:
+ Argon2_Family(uint8_t family);
+
+ std::string name() const override;
+
+ std::unique_ptr<PasswordHash> tune(size_t output_length,
+ std::chrono::milliseconds msec,
+ size_t max_memory) const override;
+
+ std::unique_ptr<PasswordHash> default_params() const override;
+
+ std::unique_ptr<PasswordHash> from_iterations(size_t iter) const override;
+
+ std::unique_ptr<PasswordHash> from_params(
+ size_t M, size_t t, size_t p) const override;
+ private:
+ const uint8_t m_family;
+ };
+
+/**
+* Argon2 key derivation function
+*
+* @param output the output will be placed here
+* @param output_len length of output
+* @param password the user password
+* @param password_len the length of password
+* @param salt the salt
+* @param salt_len length of salt
+* @param key an optional secret key
+* @param key_len the length of key
+* @param ad an optional additional input
+* @param ad_len the length of ad
+* @param y the Argon2 variant (0 = Argon2d, 1 = Argon2i, 2 = Argon2id)
+* @param p the parallelization parameter
+* @param M the amount of memory to use in Kb
+* @param t the number of iterations to use
+*/
+void BOTAN_PUBLIC_API(2,11) argon2(uint8_t output[], size_t output_len,
+ const char* password, size_t password_len,
+ const uint8_t salt[], size_t salt_len,
+ const uint8_t key[], size_t key_len,
+ const uint8_t ad[], size_t ad_len,
+ uint8_t y, size_t p, size_t M, size_t t);
+
+std::string BOTAN_PUBLIC_API(2,11)
+ argon2_generate_pwhash(const char* password, size_t password_len,
+ RandomNumberGenerator& rng,
+ size_t p, size_t M, size_t t,
+ uint8_t y = 2, size_t salt_len = 16, size_t output_len = 32);
+
+/**
+* Check a previously created password hash
+* @param password the password to check against
+* @param password_len the length of password
+* @param hash the stored hash to check against
+*/
+bool BOTAN_PUBLIC_API(2,11) argon2_check_pwhash(const char* password, size_t password_len,
+ const std::string& hash);
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/pbkdf/argon2/argon2fmt.cpp b/comm/third_party/botan/src/lib/pbkdf/argon2/argon2fmt.cpp
new file mode 100644
index 0000000000..480be5fa36
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pbkdf/argon2/argon2fmt.cpp
@@ -0,0 +1,125 @@
+/**
+* (C) 2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/argon2.h>
+#include <botan/rng.h>
+#include <botan/base64.h>
+#include <botan/parsing.h>
+#include <sstream>
+
+namespace Botan {
+
+namespace {
+
+std::string strip_padding(std::string s)
+ {
+ while(s.size() > 0 && s[s.size()-1] == '=')
+ s.resize(s.size() - 1);
+ return s;
+ }
+
+}
+
+std::string argon2_generate_pwhash(const char* password, size_t password_len,
+ RandomNumberGenerator& rng,
+ size_t p, size_t M, size_t t,
+ uint8_t y, size_t salt_len, size_t output_len)
+ {
+ std::vector<uint8_t> salt(salt_len);
+ rng.randomize(salt.data(), salt.size());
+
+ std::vector<uint8_t> output(output_len);
+ argon2(output.data(), output.size(),
+ password, password_len,
+ salt.data(), salt.size(),
+ nullptr, 0,
+ nullptr, 0,
+ y, p, M, t);
+
+ std::ostringstream oss;
+
+ if(y == 0)
+ oss << "$argon2d$";
+ else if(y == 1)
+ oss << "$argon2i$";
+ else
+ oss << "$argon2id$";
+
+ oss << "v=19$m=" << M << ",t=" << t << ",p=" << p << "$";
+ oss << strip_padding(base64_encode(salt)) << "$" << strip_padding(base64_encode(output));
+
+ return oss.str();
+ }
+
+bool argon2_check_pwhash(const char* password, size_t password_len,
+ const std::string& input_hash)
+ {
+ const std::vector<std::string> parts = split_on(input_hash, '$');
+
+ if(parts.size() != 5)
+ return false;
+
+ 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[1] != "v=19")
+ return false;
+
+ const std::vector<std::string> params = split_on(parts[2], ',');
+
+ if(params.size() != 3)
+ return false;
+
+ size_t M = 0, t = 0, p = 0;
+
+ for(auto param_str : params)
+ {
+ const std::vector<std::string> param = split_on(param_str, '=');
+
+ if(param.size() != 2)
+ return false;
+
+ const std::string 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;
+ }
+
+ std::vector<uint8_t> salt(base64_decode_max_output(parts[3].size()));
+ salt.resize(base64_decode(salt.data(), parts[3], false));
+
+ std::vector<uint8_t> hash(base64_decode_max_output(parts[4].size()));
+ hash.resize(base64_decode(hash.data(), parts[4], false));
+
+ if(hash.size() < 4)
+ return false;
+
+ std::vector<uint8_t> generated(hash.size());
+ argon2(generated.data(), generated.size(),
+ password, password_len,
+ salt.data(), salt.size(),
+ nullptr, 0,
+ nullptr, 0,
+ family, p, M, t);
+
+ return constant_time_compare(generated.data(), hash.data(), generated.size());
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/pbkdf/argon2/argon2pwhash.cpp b/comm/third_party/botan/src/lib/pbkdf/argon2/argon2pwhash.cpp
new file mode 100644
index 0000000000..96f7f74fee
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pbkdf/argon2/argon2pwhash.cpp
@@ -0,0 +1,154 @@
+/**
+* (C) 2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/argon2.h>
+#include <botan/exceptn.h>
+#include <botan/internal/timer.h>
+#include <algorithm>
+
+namespace Botan {
+
+Argon2::Argon2(uint8_t family, size_t M, size_t t, size_t p) :
+ m_family(family),
+ m_M(M),
+ m_t(t),
+ m_p(p)
+ {}
+
+void Argon2::derive_key(uint8_t output[], size_t output_len,
+ const char* password, size_t password_len,
+ const uint8_t salt[], size_t salt_len) const
+ {
+ argon2(output, output_len,
+ password, password_len,
+ salt, salt_len,
+ nullptr, 0,
+ nullptr, 0,
+ m_family, m_p, m_M, m_t);
+ }
+
+namespace {
+
+std::string argon2_family_name(uint8_t f)
+ {
+ switch(f)
+ {
+ case 0:
+ return "Argon2d";
+ case 1:
+ return "Argon2i";
+ case 2:
+ return "Argon2id";
+ default:
+ throw Invalid_Argument("Unknown Argon2 parameter");
+ }
+ }
+
+}
+
+std::string Argon2::to_string() const
+ {
+ return argon2_family_name(m_family) + "(" +
+ std::to_string(m_M) + "," +
+ std::to_string(m_t) + "," +
+ std::to_string(m_p) + ")";
+ }
+
+Argon2_Family::Argon2_Family(uint8_t family) : m_family(family)
+ {
+ if(m_family != 0 && m_family != 1 && m_family != 2)
+ throw Invalid_Argument("Unknown Argon2 family identifier");
+ }
+
+std::string Argon2_Family::name() const
+ {
+ return argon2_family_name(m_family);
+ }
+
+std::unique_ptr<PasswordHash> Argon2_Family::tune(size_t /*output_length*/,
+ std::chrono::milliseconds msec,
+ size_t max_memory) const
+ {
+ const size_t max_kib = (max_memory == 0) ? 256*1024 : max_memory*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(500) ? 128 : 36) * 1024;
+ const size_t p = 1;
+ size_t t = 1;
+
+ Timer timer("Argon2");
+ const auto tune_time = BOTAN_PBKDF_TUNING_TIME;
+
+ timer.run_until_elapsed(tune_time, [&]() {
+ uint8_t output[64] = { 0 };
+ argon2(output, sizeof(output), "test", 4, nullptr, 0, nullptr, 0, nullptr, 0, m_family, p, tune_M, t);
+ });
+
+ if(timer.events() == 0 || timer.value() == 0)
+ return default_params();
+
+ size_t M = 4*1024;
+
+ const uint64_t measured_time = timer.value() / (timer.events() * (tune_M / M));
+
+ const uint64_t target_nsec = msec.count() * static_cast<uint64_t>(1000000);
+
+ /*
+ * Argon2 scaling rules:
+ * k*M, k*t, k*p all increase cost by about k
+ *
+ * Since we don't even take advantage of p > 1, we prefer increasing
+ * t or M instead.
+ *
+ * If possible to increase M, prefer that.
+ */
+
+ uint64_t est_nsec = measured_time;
+
+ if(est_nsec < target_nsec && M < max_kib)
+ {
+ const uint64_t desired_cost_increase = (target_nsec + est_nsec - 1) / est_nsec;
+ const uint64_t mem_headroom = max_kib / M;
+
+ const uint64_t M_mult = std::min(desired_cost_increase, mem_headroom);
+ M *= static_cast<size_t>(M_mult);
+ est_nsec *= M_mult;
+ }
+
+ if(est_nsec < target_nsec)
+ {
+ const uint64_t desired_cost_increase = (target_nsec + est_nsec - 1) / est_nsec;
+ t *= static_cast<size_t>(desired_cost_increase);
+ }
+
+ return this->from_params(M, t, p);
+ }
+
+std::unique_ptr<PasswordHash> Argon2_Family::default_params() const
+ {
+ return this->from_params(128*1024, 1, 1);
+ }
+
+std::unique_ptr<PasswordHash> Argon2_Family::from_iterations(size_t iter) const
+ {
+ /*
+ These choices are arbitrary, but should not change in future
+ releases since they will break applications expecting deterministic
+ mapping from iteration count to params
+ */
+ const size_t M = iter;
+ const size_t t = 1;
+ const size_t p = 1;
+ return this->from_params(M, t, p);
+ }
+
+std::unique_ptr<PasswordHash> Argon2_Family::from_params(size_t M, size_t t, size_t p) const
+ {
+ return std::unique_ptr<PasswordHash>(new Argon2(m_family, M, t, p));
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/pbkdf/argon2/info.txt b/comm/third_party/botan/src/lib/pbkdf/argon2/info.txt
new file mode 100644
index 0000000000..91e85d0c47
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pbkdf/argon2/info.txt
@@ -0,0 +1,9 @@
+<defines>
+ARGON2 -> 20190824
+</defines>
+
+<requires>
+blake2
+base64
+</requires>
+
diff --git a/comm/third_party/botan/src/lib/pbkdf/bcrypt_pbkdf/bcrypt_pbkdf.cpp b/comm/third_party/botan/src/lib/pbkdf/bcrypt_pbkdf/bcrypt_pbkdf.cpp
new file mode 100644
index 0000000000..2f2c770256
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pbkdf/bcrypt_pbkdf/bcrypt_pbkdf.cpp
@@ -0,0 +1,183 @@
+/*
+* (C) 2018,2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/bcrypt_pbkdf.h>
+#include <botan/loadstor.h>
+#include <botan/blowfish.h>
+#include <botan/hash.h>
+#include <botan/internal/timer.h>
+
+namespace Botan {
+
+void Bcrypt_PBKDF::derive_key(uint8_t output[], size_t output_len,
+ const char* password, size_t password_len,
+ const uint8_t salt[], size_t salt_len) const
+ {
+ bcrypt_pbkdf(output, output_len,
+ password, password_len,
+ salt, salt_len,
+ m_iterations);
+ }
+
+std::string Bcrypt_PBKDF::to_string() const
+ {
+ return "Bcrypt-PBKDF(" + std::to_string(m_iterations) + ")";
+ }
+
+std::string Bcrypt_PBKDF_Family::name() const
+ {
+ return "Bcrypt-PBKDF";
+ }
+
+std::unique_ptr<PasswordHash> Bcrypt_PBKDF_Family::tune(size_t output_length,
+ std::chrono::milliseconds msec,
+ size_t /*max_memory*/) const
+ {
+ Timer timer("Bcrypt_PBKDF");
+ const auto tune_time = BOTAN_PBKDF_TUNING_TIME;
+
+ const size_t blocks = (output_length + 32 - 1) / 32;
+
+ if(blocks == 0)
+ return default_params();
+
+ const size_t starting_iter = 2;
+
+ timer.run_until_elapsed(tune_time, [&]() {
+ uint8_t output[32] = { 0 };
+ bcrypt_pbkdf(output, sizeof(output), "test", 4, nullptr, 0, starting_iter);
+ });
+
+ if(timer.events() < blocks || timer.value() == 0)
+ return default_params();
+
+ const uint64_t measured_time = timer.value() / (timer.events() / blocks);
+
+ const uint64_t target_nsec = msec.count() * static_cast<uint64_t>(1000000);
+
+ const uint64_t desired_increase = target_nsec / measured_time;
+
+ if(desired_increase == 0)
+ return this->from_iterations(starting_iter);
+
+ return this->from_iterations(static_cast<size_t>(desired_increase * starting_iter));
+ }
+
+std::unique_ptr<PasswordHash> Bcrypt_PBKDF_Family::default_params() const
+ {
+ return this->from_iterations(32); // About 100 ms on fast machine
+ }
+
+std::unique_ptr<PasswordHash> Bcrypt_PBKDF_Family::from_iterations(size_t iter) const
+ {
+ return std::unique_ptr<PasswordHash>(new Bcrypt_PBKDF(iter));
+ }
+
+std::unique_ptr<PasswordHash> Bcrypt_PBKDF_Family::from_params(size_t iter, size_t /*t*/, size_t /*p*/) const
+ {
+ return this->from_iterations(iter);
+ }
+
+namespace {
+
+void bcrypt_round(Blowfish& blowfish,
+ const secure_vector<uint8_t>& pass_hash,
+ const secure_vector<uint8_t>& salt_hash,
+ secure_vector<uint8_t>& out,
+ secure_vector<uint8_t>& tmp)
+ {
+ const size_t BCRYPT_PBKDF_OUTPUT = 32;
+
+ // "OxychromaticBlowfishSwatDynamite"
+ static const uint8_t BCRYPT_PBKDF_MAGIC[BCRYPT_PBKDF_OUTPUT] = {
+ 0x4F, 0x78, 0x79, 0x63, 0x68, 0x72, 0x6F, 0x6D,
+ 0x61, 0x74, 0x69, 0x63, 0x42, 0x6C, 0x6F, 0x77,
+ 0x66, 0x69, 0x73, 0x68, 0x53, 0x77, 0x61, 0x74,
+ 0x44, 0x79, 0x6E, 0x61, 0x6D, 0x69, 0x74, 0x65
+ };
+
+ const size_t BCRYPT_PBKDF_WORKFACTOR = 6;
+ const size_t BCRYPT_PBKDF_ROUNDS = 64;
+
+ blowfish.salted_set_key(pass_hash.data(), pass_hash.size(),
+ salt_hash.data(), salt_hash.size(),
+ BCRYPT_PBKDF_WORKFACTOR, true);
+
+ copy_mem(tmp.data(), BCRYPT_PBKDF_MAGIC, BCRYPT_PBKDF_OUTPUT);
+ for(size_t i = 0; i != BCRYPT_PBKDF_ROUNDS; ++i)
+ blowfish.encrypt(tmp);
+
+ /*
+ Bcrypt PBKDF loads the Blowfish output as big endian for no reason
+ in particular. We can't just swap everything once at the end
+ because the (big-endian) values are fed into SHA-512 to generate
+ the salt for the next round
+ */
+ for(size_t i = 0; i != 32/4; ++i)
+ {
+ const uint32_t w = load_le<uint32_t>(tmp.data(), i);
+ store_be(w, &tmp[sizeof(uint32_t)*i]);
+ }
+
+ xor_buf(out.data(), tmp.data(), BCRYPT_PBKDF_OUTPUT);
+ }
+
+}
+
+void bcrypt_pbkdf(uint8_t output[], size_t output_len,
+ const char* pass, size_t pass_len,
+ const uint8_t salt[], size_t salt_len,
+ size_t rounds)
+ {
+ BOTAN_ARG_CHECK(rounds >= 1, "Invalid rounds for Bcrypt PBKDF");
+
+ // No output desired, so we are all done already...
+ if(output_len == 0)
+ return;
+
+ BOTAN_ARG_CHECK(output_len <= 10*1024*1024, "Too much output for Bcrypt PBKDF");
+
+ const size_t BCRYPT_BLOCK_SIZE = 32;
+ const size_t blocks = (output_len + BCRYPT_BLOCK_SIZE - 1) / BCRYPT_BLOCK_SIZE;
+
+ std::unique_ptr<HashFunction> sha512 = HashFunction::create_or_throw("SHA-512");
+ const secure_vector<uint8_t> pass_hash = sha512->process(reinterpret_cast<const uint8_t*>(pass), pass_len);
+
+ secure_vector<uint8_t> salt_hash(sha512->output_length());
+
+ Blowfish blowfish;
+ secure_vector<uint8_t> out(BCRYPT_BLOCK_SIZE);
+ secure_vector<uint8_t> tmp(BCRYPT_BLOCK_SIZE);
+
+ for(size_t block = 0; block != blocks; ++block)
+ {
+ clear_mem(out.data(), out.size());
+
+ sha512->update(salt, salt_len);
+ sha512->update_be(static_cast<uint32_t>(block + 1));
+ sha512->final(salt_hash.data());
+
+ bcrypt_round(blowfish, pass_hash, salt_hash, out, tmp);
+
+ for(size_t r = 1; r != rounds; ++r)
+ {
+ // Next salt is H(prev_output)
+ sha512->update(tmp);
+ sha512->final(salt_hash.data());
+
+ bcrypt_round(blowfish, pass_hash, salt_hash, out, tmp);
+ }
+
+ for(size_t i = 0; i != BCRYPT_BLOCK_SIZE; ++i)
+ {
+ const size_t dest = i * blocks + block;
+ if(dest < output_len)
+ output[dest] = out[i];
+ }
+ }
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/pbkdf/bcrypt_pbkdf/bcrypt_pbkdf.h b/comm/third_party/botan/src/lib/pbkdf/bcrypt_pbkdf/bcrypt_pbkdf.h
new file mode 100644
index 0000000000..0d459e8dbc
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pbkdf/bcrypt_pbkdf/bcrypt_pbkdf.h
@@ -0,0 +1,77 @@
+/*
+* (C) 2018,2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_PBKDF_BCRYPT_H_
+#define BOTAN_PBKDF_BCRYPT_H_
+
+#include <botan/pwdhash.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(bcrypt_pbkdf.h)
+
+namespace Botan {
+
+/**
+* Bcrypt-PBKDF key derivation function
+*/
+class BOTAN_PUBLIC_API(2,11) Bcrypt_PBKDF final : public PasswordHash
+ {
+ public:
+ Bcrypt_PBKDF(size_t iterations) : m_iterations(iterations) {}
+
+ Bcrypt_PBKDF(const Bcrypt_PBKDF& other) = default;
+ Bcrypt_PBKDF& operator=(const Bcrypt_PBKDF&) = default;
+
+ /**
+ * Derive a new key under the current Bcrypt-PBKDF parameter set
+ */
+ void derive_key(uint8_t out[], size_t out_len,
+ const char* password, size_t password_len,
+ const uint8_t salt[], size_t salt_len) const override;
+
+ std::string to_string() const override;
+
+ size_t iterations() const override { return m_iterations; }
+
+ size_t parallelism() const override { return 0; }
+
+ size_t memory_param() const override { return 0; }
+
+ size_t total_memory_usage() const override { return 4096; }
+
+ private:
+ size_t m_iterations;
+ };
+
+class BOTAN_PUBLIC_API(2,11) Bcrypt_PBKDF_Family final : public PasswordHashFamily
+ {
+ public:
+ Bcrypt_PBKDF_Family() {}
+
+ std::string name() const override;
+
+ std::unique_ptr<PasswordHash> tune(size_t output_length,
+ std::chrono::milliseconds msec,
+ size_t max_memory) const override;
+
+ std::unique_ptr<PasswordHash> default_params() const override;
+
+ std::unique_ptr<PasswordHash> from_iterations(size_t iter) const override;
+
+ std::unique_ptr<PasswordHash> from_params(
+ size_t i, size_t, size_t) const override;
+ };
+
+/**
+* Bcrypt PBKDF compatible with OpenBSD bcrypt_pbkdf
+*/
+void BOTAN_UNSTABLE_API bcrypt_pbkdf(uint8_t output[], size_t output_len,
+ const char* pass, size_t pass_len,
+ const uint8_t salt[], size_t salt_len,
+ size_t rounds);
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/pbkdf/bcrypt_pbkdf/info.txt b/comm/third_party/botan/src/lib/pbkdf/bcrypt_pbkdf/info.txt
new file mode 100644
index 0000000000..0e2b10178e
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pbkdf/bcrypt_pbkdf/info.txt
@@ -0,0 +1,8 @@
+<defines>
+PBKDF_BCRYPT -> 20190531
+</defines>
+
+<requires>
+blowfish
+sha2_64
+</requires>
diff --git a/comm/third_party/botan/src/lib/pbkdf/info.txt b/comm/third_party/botan/src/lib/pbkdf/info.txt
new file mode 100644
index 0000000000..650414f41b
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pbkdf/info.txt
@@ -0,0 +1,13 @@
+<defines>
+PBKDF -> 20180902
+</defines>
+
+<requires>
+mac
+hash
+</requires>
+
+<header:public>
+pwdhash.h
+pbkdf.h
+</header:public>
diff --git a/comm/third_party/botan/src/lib/pbkdf/pbkdf.cpp b/comm/third_party/botan/src/lib/pbkdf/pbkdf.cpp
new file mode 100644
index 0000000000..73b482725c
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pbkdf/pbkdf.cpp
@@ -0,0 +1,133 @@
+/*
+* PBKDF
+* (C) 2012 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/pbkdf.h>
+#include <botan/exceptn.h>
+#include <botan/scan_name.h>
+
+#if defined(BOTAN_HAS_PBKDF1)
+#include <botan/pbkdf1.h>
+#endif
+
+#if defined(BOTAN_HAS_PBKDF2)
+#include <botan/pbkdf2.h>
+#endif
+
+#if defined(BOTAN_HAS_PGP_S2K)
+#include <botan/pgp_s2k.h>
+#endif
+
+namespace Botan {
+
+std::unique_ptr<PBKDF> PBKDF::create(const std::string& algo_spec,
+ const std::string& provider)
+ {
+ const SCAN_Name req(algo_spec);
+
+#if defined(BOTAN_HAS_PBKDF2)
+ if(req.algo_name() == "PBKDF2")
+ {
+ // TODO OpenSSL
+
+ if(provider.empty() || provider == "base")
+ {
+ if(auto mac = MessageAuthenticationCode::create(req.arg(0)))
+ return std::unique_ptr<PBKDF>(new PKCS5_PBKDF2(mac.release()));
+
+ if(auto mac = MessageAuthenticationCode::create("HMAC(" + req.arg(0) + ")"))
+ return std::unique_ptr<PBKDF>(new PKCS5_PBKDF2(mac.release()));
+ }
+
+ return nullptr;
+ }
+#endif
+
+#if defined(BOTAN_HAS_PBKDF1)
+ if(req.algo_name() == "PBKDF1" && req.arg_count() == 1)
+ {
+ if(auto hash = HashFunction::create(req.arg(0)))
+ return std::unique_ptr<PBKDF>(new PKCS5_PBKDF1(hash.release()));
+
+ }
+#endif
+
+#if defined(BOTAN_HAS_PGP_S2K)
+ if(req.algo_name() == "OpenPGP-S2K" && req.arg_count() == 1)
+ {
+ if(auto hash = HashFunction::create(req.arg(0)))
+ return std::unique_ptr<PBKDF>(new OpenPGP_S2K(hash.release()));
+ }
+#endif
+
+ BOTAN_UNUSED(req);
+ BOTAN_UNUSED(provider);
+
+ return nullptr;
+ }
+
+//static
+std::unique_ptr<PBKDF>
+PBKDF::create_or_throw(const std::string& algo,
+ const std::string& provider)
+ {
+ if(auto pbkdf = PBKDF::create(algo, provider))
+ {
+ return pbkdf;
+ }
+ throw Lookup_Error("PBKDF", algo, provider);
+ }
+
+std::vector<std::string> PBKDF::providers(const std::string& algo_spec)
+ {
+ return probe_providers_of<PBKDF>(algo_spec, { "base", "openssl" });
+ }
+
+void PBKDF::pbkdf_timed(uint8_t out[], size_t out_len,
+ const std::string& passphrase,
+ const uint8_t salt[], size_t salt_len,
+ std::chrono::milliseconds msec,
+ size_t& iterations) const
+ {
+ iterations = pbkdf(out, out_len, passphrase, salt, salt_len, 0, msec);
+ }
+
+void PBKDF::pbkdf_iterations(uint8_t out[], size_t out_len,
+ const std::string& passphrase,
+ const uint8_t salt[], size_t salt_len,
+ size_t iterations) const
+ {
+ if(iterations == 0)
+ throw Invalid_Argument(name() + ": Invalid iteration count");
+
+ const size_t iterations_run = pbkdf(out, out_len, passphrase,
+ salt, salt_len, iterations,
+ std::chrono::milliseconds(0));
+ BOTAN_ASSERT_EQUAL(iterations, iterations_run, "Expected PBKDF iterations");
+ }
+
+secure_vector<uint8_t> PBKDF::pbkdf_iterations(size_t out_len,
+ const std::string& passphrase,
+ const uint8_t salt[], size_t salt_len,
+ size_t iterations) const
+ {
+ secure_vector<uint8_t> out(out_len);
+ pbkdf_iterations(out.data(), out_len, passphrase, salt, salt_len, iterations);
+ return out;
+ }
+
+secure_vector<uint8_t> PBKDF::pbkdf_timed(size_t out_len,
+ const std::string& passphrase,
+ const uint8_t salt[], size_t salt_len,
+ std::chrono::milliseconds msec,
+ size_t& iterations) const
+ {
+ secure_vector<uint8_t> out(out_len);
+ pbkdf_timed(out.data(), out_len, passphrase, salt, salt_len, msec, iterations);
+ return out;
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/pbkdf/pbkdf.h b/comm/third_party/botan/src/lib/pbkdf/pbkdf.h
new file mode 100644
index 0000000000..e7f0a84eec
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pbkdf/pbkdf.h
@@ -0,0 +1,246 @@
+/*
+* PBKDF
+* (C) 1999-2007,2012,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_PBKDF_H_
+#define BOTAN_PBKDF_H_
+
+#include <botan/symkey.h>
+#include <chrono>
+
+namespace Botan {
+
+/**
+* Base class for PBKDF (password based key derivation function)
+* implementations. Converts a password into a key using a salt
+* 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.
+*/
+class BOTAN_PUBLIC_API(2,0) PBKDF
+ {
+ public:
+ /**
+ * Create an instance based on a name
+ * If provider is empty then best available is chosen.
+ * @param algo_spec algorithm name
+ * @param provider provider implementation to choose
+ * @return a null pointer if the algo/provider combination cannot be found
+ */
+ static std::unique_ptr<PBKDF> create(const std::string& algo_spec,
+ const std::string& provider = "");
+
+ /**
+ * Create an instance based on a name, or throw if the
+ * algo/provider combination cannot be found. If provider is
+ * empty then best available is chosen.
+ */
+ static std::unique_ptr<PBKDF>
+ create_or_throw(const std::string& algo_spec,
+ const std::string& provider = "");
+
+ /**
+ * @return list of available providers for this algorithm, empty if not available
+ */
+ static std::vector<std::string> providers(const std::string& algo_spec);
+
+ /**
+ * @return new instance of this same algorithm
+ */
+ virtual PBKDF* clone() const = 0;
+
+ /**
+ * @return name of this PBKDF
+ */
+ virtual std::string name() const = 0;
+
+ virtual ~PBKDF() = default;
+
+ /**
+ * Derive a key from a passphrase for a number of iterations
+ * specified by either iterations or if iterations == 0 then
+ * running until msec time has elapsed.
+ *
+ * @param out buffer to store the derived key, must be of out_len bytes
+ * @param out_len the desired length of the key to produce
+ * @param passphrase the password to derive the key from
+ * @param salt a randomly chosen salt
+ * @param salt_len length of salt in bytes
+ * @param iterations the number of iterations to use (use 10K or more)
+ * @param msec if iterations is zero, then instead the PBKDF is
+ * run until msec milliseconds has passed.
+ * @return the number of iterations performed
+ */
+ virtual size_t pbkdf(uint8_t out[], size_t out_len,
+ const std::string& passphrase,
+ const uint8_t salt[], size_t salt_len,
+ size_t iterations,
+ std::chrono::milliseconds msec) const = 0;
+
+ /**
+ * Derive a key from a passphrase for a number of iterations.
+ *
+ * @param out buffer to store the derived key, must be of out_len bytes
+ * @param out_len the desired length of the key to produce
+ * @param passphrase the password to derive the key from
+ * @param salt a randomly chosen salt
+ * @param salt_len length of salt in bytes
+ * @param iterations the number of iterations to use (use 10K or more)
+ */
+ void pbkdf_iterations(uint8_t out[], size_t out_len,
+ const std::string& passphrase,
+ const uint8_t salt[], size_t salt_len,
+ size_t iterations) const;
+
+ /**
+ * Derive a key from a passphrase, running until msec time has elapsed.
+ *
+ * @param out buffer to store the derived key, must be of out_len bytes
+ * @param out_len the desired length of the key to produce
+ * @param passphrase the password to derive the key from
+ * @param salt a randomly chosen salt
+ * @param salt_len length of salt in bytes
+ * @param msec if iterations is zero, then instead the PBKDF is
+ * run until msec milliseconds has passed.
+ * @param iterations set to the number iterations executed
+ */
+ void pbkdf_timed(uint8_t out[], size_t out_len,
+ const std::string& passphrase,
+ const uint8_t salt[], size_t salt_len,
+ std::chrono::milliseconds msec,
+ size_t& iterations) const;
+
+ /**
+ * Derive a key from a passphrase for a number of iterations.
+ *
+ * @param out_len the desired length of the key to produce
+ * @param passphrase the password to derive the key from
+ * @param salt a randomly chosen salt
+ * @param salt_len length of salt in bytes
+ * @param iterations the number of iterations to use (use 10K or more)
+ * @return the derived key
+ */
+ secure_vector<uint8_t> pbkdf_iterations(size_t out_len,
+ const std::string& passphrase,
+ const uint8_t salt[], size_t salt_len,
+ size_t iterations) const;
+
+ /**
+ * Derive a key from a passphrase, running until msec time has elapsed.
+ *
+ * @param out_len the desired length of the key to produce
+ * @param passphrase the password to derive the key from
+ * @param salt a randomly chosen salt
+ * @param salt_len length of salt in bytes
+ * @param msec if iterations is zero, then instead the PBKDF is
+ * run until msec milliseconds has passed.
+ * @param iterations set to the number iterations executed
+ * @return the derived key
+ */
+ secure_vector<uint8_t> pbkdf_timed(size_t out_len,
+ const std::string& passphrase,
+ const uint8_t salt[], size_t salt_len,
+ std::chrono::milliseconds msec,
+ size_t& iterations) const;
+
+ // Following kept for compat with 1.10:
+
+ /**
+ * Derive a key from a passphrase
+ * @param out_len the desired length of the key to produce
+ * @param passphrase the password to derive the key from
+ * @param salt a randomly chosen salt
+ * @param salt_len length of salt in bytes
+ * @param iterations the number of iterations to use (use 10K or more)
+ */
+ OctetString derive_key(size_t out_len,
+ const std::string& passphrase,
+ const uint8_t salt[], size_t salt_len,
+ size_t iterations) const
+ {
+ return pbkdf_iterations(out_len, passphrase, salt, salt_len, iterations);
+ }
+
+ /**
+ * Derive a key from a passphrase
+ * @param out_len the desired length of the key to produce
+ * @param passphrase the password to derive the key from
+ * @param salt a randomly chosen salt
+ * @param iterations the number of iterations to use (use 10K or more)
+ */
+ template<typename Alloc>
+ OctetString derive_key(size_t out_len,
+ const std::string& passphrase,
+ const std::vector<uint8_t, Alloc>& salt,
+ size_t iterations) const
+ {
+ return pbkdf_iterations(out_len, passphrase, salt.data(), salt.size(), iterations);
+ }
+
+ /**
+ * Derive a key from a passphrase
+ * @param out_len the desired length of the key to produce
+ * @param passphrase the password to derive the key from
+ * @param salt a randomly chosen salt
+ * @param salt_len length of salt in bytes
+ * @param msec is how long to run the PBKDF
+ * @param iterations is set to the number of iterations used
+ */
+ OctetString derive_key(size_t out_len,
+ const std::string& passphrase,
+ const uint8_t salt[], size_t salt_len,
+ std::chrono::milliseconds msec,
+ size_t& iterations) const
+ {
+ return pbkdf_timed(out_len, passphrase, salt, salt_len, msec, iterations);
+ }
+
+ /**
+ * Derive a key from a passphrase using a certain amount of time
+ * @param out_len the desired length of the key to produce
+ * @param passphrase the password to derive the key from
+ * @param salt a randomly chosen salt
+ * @param msec is how long to run the PBKDF
+ * @param iterations is set to the number of iterations used
+ */
+ template<typename Alloc>
+ OctetString derive_key(size_t out_len,
+ const std::string& passphrase,
+ const std::vector<uint8_t, Alloc>& salt,
+ std::chrono::milliseconds msec,
+ size_t& iterations) const
+ {
+ return pbkdf_timed(out_len, passphrase, salt.data(), salt.size(), msec, iterations);
+ }
+ };
+
+/*
+* Compatibility typedef
+*/
+typedef PBKDF S2K;
+
+/**
+* Password based key derivation function factory method
+* @param algo_spec the name of the desired PBKDF algorithm
+* @param provider the provider to use
+* @return pointer to newly allocated object of that type
+*/
+inline PBKDF* get_pbkdf(const std::string& algo_spec,
+ const std::string& provider = "")
+ {
+ return PBKDF::create_or_throw(algo_spec, provider).release();
+ }
+
+inline PBKDF* get_s2k(const std::string& algo_spec)
+ {
+ return get_pbkdf(algo_spec);
+ }
+
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/pbkdf/pbkdf1/info.txt b/comm/third_party/botan/src/lib/pbkdf/pbkdf1/info.txt
new file mode 100644
index 0000000000..e4b972e4b6
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pbkdf/pbkdf1/info.txt
@@ -0,0 +1,7 @@
+<defines>
+PBKDF1 -> 20131128
+</defines>
+
+<requires>
+hash
+</requires>
diff --git a/comm/third_party/botan/src/lib/pbkdf/pbkdf1/pbkdf1.cpp b/comm/third_party/botan/src/lib/pbkdf/pbkdf1/pbkdf1.cpp
new file mode 100644
index 0000000000..ad922ce9c6
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pbkdf/pbkdf1/pbkdf1.cpp
@@ -0,0 +1,54 @@
+/*
+* PBKDF1
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/pbkdf1.h>
+#include <botan/exceptn.h>
+
+namespace Botan {
+
+size_t PKCS5_PBKDF1::pbkdf(uint8_t output_buf[], size_t output_len,
+ const std::string& passphrase,
+ const uint8_t salt[], size_t salt_len,
+ size_t iterations,
+ std::chrono::milliseconds msec) const
+ {
+ if(output_len > m_hash->output_length())
+ throw Invalid_Argument("PKCS5_PBKDF1: Requested output length too long");
+
+ m_hash->update(passphrase);
+ m_hash->update(salt, salt_len);
+ secure_vector<uint8_t> key = m_hash->final();
+
+ const auto start = std::chrono::high_resolution_clock::now();
+ size_t iterations_performed = 1;
+
+ while(true)
+ {
+ if(iterations == 0)
+ {
+ if(iterations_performed % 10000 == 0)
+ {
+ auto time_taken = std::chrono::high_resolution_clock::now() - start;
+ auto msec_taken = std::chrono::duration_cast<std::chrono::milliseconds>(time_taken);
+ if(msec_taken > msec)
+ break;
+ }
+ }
+ else if(iterations_performed == iterations)
+ break;
+
+ m_hash->update(key);
+ m_hash->final(key.data());
+
+ ++iterations_performed;
+ }
+
+ copy_mem(output_buf, key.data(), output_len);
+ return iterations_performed;
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/pbkdf/pbkdf1/pbkdf1.h b/comm/third_party/botan/src/lib/pbkdf/pbkdf1/pbkdf1.h
new file mode 100644
index 0000000000..f5e95b8368
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pbkdf/pbkdf1/pbkdf1.h
@@ -0,0 +1,53 @@
+/*
+* PBKDF1
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_PBKDF1_H_
+#define BOTAN_PBKDF1_H_
+
+#include <botan/pbkdf.h>
+#include <botan/hash.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(pbkdf1.h)
+
+namespace Botan {
+
+/**
+* PKCS #5 v1 PBKDF, aka PBKDF1
+* Can only generate a key up to the size of the hash output.
+* Unless needed for backwards compatibility, use PKCS5_PBKDF2
+*/
+class BOTAN_PUBLIC_API(2,0) PKCS5_PBKDF1 final : public PBKDF
+ {
+ public:
+ /**
+ * Create a PKCS #5 instance using the specified hash function.
+ * @param hash pointer to a hash function object to use
+ */
+ explicit PKCS5_PBKDF1(HashFunction* hash) : m_hash(hash) {}
+
+ std::string name() const override
+ {
+ return "PBKDF1(" + m_hash->name() + ")";
+ }
+
+ PBKDF* clone() const override
+ {
+ return new PKCS5_PBKDF1(m_hash->clone());
+ }
+
+ size_t pbkdf(uint8_t output_buf[], size_t output_len,
+ const std::string& passphrase,
+ const uint8_t salt[], size_t salt_len,
+ size_t iterations,
+ std::chrono::milliseconds msec) const override;
+ private:
+ std::unique_ptr<HashFunction> m_hash;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/pbkdf/pbkdf2/info.txt b/comm/third_party/botan/src/lib/pbkdf/pbkdf2/info.txt
new file mode 100644
index 0000000000..b6c6fb4e65
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pbkdf/pbkdf2/info.txt
@@ -0,0 +1,7 @@
+<defines>
+PBKDF2 -> 20180902
+</defines>
+
+<requires>
+hmac
+</requires>
diff --git a/comm/third_party/botan/src/lib/pbkdf/pbkdf2/pbkdf2.cpp b/comm/third_party/botan/src/lib/pbkdf/pbkdf2/pbkdf2.cpp
new file mode 100644
index 0000000000..122d0fae3e
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pbkdf/pbkdf2/pbkdf2.cpp
@@ -0,0 +1,228 @@
+/*
+* PBKDF2
+* (C) 1999-2007 Jack Lloyd
+* (C) 2018 Ribose Inc
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/pbkdf2.h>
+#include <botan/exceptn.h>
+#include <botan/internal/rounding.h>
+#include <botan/internal/timer.h>
+
+namespace Botan {
+
+namespace {
+
+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);
+ }
+ catch(Invalid_Key_Length&)
+ {
+ throw Invalid_Argument("PBKDF2 cannot accept passphrase of the given size");
+ }
+ }
+
+}
+
+size_t
+pbkdf2(MessageAuthenticationCode& prf,
+ uint8_t out[],
+ size_t out_len,
+ const std::string& password,
+ const uint8_t salt[], size_t salt_len,
+ size_t iterations,
+ std::chrono::milliseconds msec)
+ {
+ if(iterations == 0)
+ {
+ iterations = PBKDF2(prf, out_len, msec).iterations();
+ }
+
+ PBKDF2 pbkdf2(prf, iterations);
+
+ pbkdf2.derive_key(out, out_len,
+ password.c_str(), password.size(),
+ salt, salt_len);
+
+ return iterations;
+ }
+
+namespace {
+
+size_t tune_pbkdf2(MessageAuthenticationCode& prf,
+ size_t output_length,
+ uint32_t msec)
+ {
+ if(output_length == 0)
+ output_length = 1;
+
+ const size_t prf_sz = prf.output_length();
+ BOTAN_ASSERT_NOMSG(prf_sz > 0);
+ secure_vector<uint8_t> U(prf_sz);
+
+ const size_t trial_iterations = 2000;
+
+ // Short output ensures we only need a single PBKDF2 block
+
+ Timer timer("PBKDF2");
+
+ const auto tune_time = BOTAN_PBKDF_TUNING_TIME;
+
+ prf.set_key(nullptr, 0);
+
+ timer.run_until_elapsed(tune_time, [&]() {
+ uint8_t out[12] = { 0 };
+ uint8_t salt[12] = { 0 };
+ pbkdf2(prf, out, sizeof(out), salt, sizeof(salt), trial_iterations);
+ });
+
+ if(timer.events() == 0)
+ return trial_iterations;
+
+ const uint64_t duration_nsec = timer.value() / timer.events();
+
+ const uint64_t desired_nsec = static_cast<uint64_t>(msec) * 1000000;
+
+ if(duration_nsec > desired_nsec)
+ return trial_iterations;
+
+ const size_t blocks_needed = (output_length + prf_sz - 1) / prf_sz;
+
+ const size_t multiplier = static_cast<size_t>(desired_nsec / duration_nsec / blocks_needed);
+
+ if(multiplier == 0)
+ return trial_iterations;
+ else
+ return trial_iterations * multiplier;
+ }
+
+}
+
+void pbkdf2(MessageAuthenticationCode& prf,
+ uint8_t out[],
+ size_t out_len,
+ const uint8_t salt[],
+ size_t salt_len,
+ size_t iterations)
+ {
+ if(iterations == 0)
+ throw Invalid_Argument("PBKDF2: Invalid iteration count");
+
+ clear_mem(out, out_len);
+
+ if(out_len == 0)
+ return;
+
+ const size_t prf_sz = prf.output_length();
+ BOTAN_ASSERT_NOMSG(prf_sz > 0);
+
+ secure_vector<uint8_t> U(prf_sz);
+
+ uint32_t counter = 1;
+ while(out_len)
+ {
+ const size_t prf_output = std::min<size_t>(prf_sz, out_len);
+
+ prf.update(salt, salt_len);
+ prf.update_be(counter++);
+ prf.final(U.data());
+
+ xor_buf(out, U.data(), prf_output);
+
+ for(size_t i = 1; i != iterations; ++i)
+ {
+ prf.update(U);
+ prf.final(U.data());
+ xor_buf(out, U.data(), prf_output);
+ }
+
+ out_len -= prf_output;
+ out += prf_output;
+ }
+ }
+
+// PBKDF interface
+size_t
+PKCS5_PBKDF2::pbkdf(uint8_t key[], size_t key_len,
+ const std::string& password,
+ const uint8_t salt[], size_t salt_len,
+ size_t iterations,
+ std::chrono::milliseconds msec) const
+ {
+ if(iterations == 0)
+ {
+ iterations = PBKDF2(*m_mac, key_len, msec).iterations();
+ }
+
+ PBKDF2 pbkdf2(*m_mac, iterations);
+
+ pbkdf2.derive_key(key, key_len,
+ password.c_str(), password.size(),
+ salt, salt_len);
+
+ return iterations;
+ }
+
+std::string PKCS5_PBKDF2::name() const
+ {
+ return "PBKDF2(" + m_mac->name() + ")";
+ }
+
+PBKDF* PKCS5_PBKDF2::clone() const
+ {
+ return new PKCS5_PBKDF2(m_mac->clone());
+ }
+
+// PasswordHash interface
+
+PBKDF2::PBKDF2(const MessageAuthenticationCode& prf, size_t olen, std::chrono::milliseconds msec) :
+ m_prf(prf.clone()),
+ m_iterations(tune_pbkdf2(*m_prf, olen, static_cast<uint32_t>(msec.count())))
+ {}
+
+std::string PBKDF2::to_string() const
+ {
+ return "PBKDF2(" + m_prf->name() + "," + std::to_string(m_iterations) + ")";
+ }
+
+void PBKDF2::derive_key(uint8_t out[], size_t out_len,
+ const char* password, const size_t password_len,
+ const uint8_t salt[], size_t salt_len) const
+ {
+ pbkdf2_set_key(*m_prf, password, password_len);
+ pbkdf2(*m_prf, out, out_len, salt, salt_len, m_iterations);
+ }
+
+std::string PBKDF2_Family::name() const
+ {
+ return "PBKDF2(" + m_prf->name() + ")";
+ }
+
+std::unique_ptr<PasswordHash> PBKDF2_Family::tune(size_t output_len, std::chrono::milliseconds msec, size_t) const
+ {
+ return std::unique_ptr<PasswordHash>(new PBKDF2(*m_prf, output_len, msec));
+ }
+
+std::unique_ptr<PasswordHash> PBKDF2_Family::default_params() const
+ {
+ return std::unique_ptr<PasswordHash>(new PBKDF2(*m_prf, 150000));
+ }
+
+std::unique_ptr<PasswordHash> PBKDF2_Family::from_params(size_t iter, size_t, size_t) const
+ {
+ return std::unique_ptr<PasswordHash>(new PBKDF2(*m_prf, iter));
+ }
+
+std::unique_ptr<PasswordHash> PBKDF2_Family::from_iterations(size_t iter) const
+ {
+ return std::unique_ptr<PasswordHash>(new PBKDF2(*m_prf, iter));
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/pbkdf/pbkdf2/pbkdf2.h b/comm/third_party/botan/src/lib/pbkdf/pbkdf2/pbkdf2.h
new file mode 100644
index 0000000000..9f90799c40
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pbkdf/pbkdf2/pbkdf2.h
@@ -0,0 +1,117 @@
+/*
+* PBKDF2
+* (C) 1999-2007,2012 Jack Lloyd
+* (C) 2018 Ribose Inc
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_PBKDF2_H_
+#define BOTAN_PBKDF2_H_
+
+#include <botan/pbkdf.h>
+#include <botan/pwdhash.h>
+#include <botan/mac.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(pbkdf2.h)
+
+namespace Botan {
+
+BOTAN_PUBLIC_API(2,0) size_t pbkdf2(MessageAuthenticationCode& prf,
+ uint8_t out[],
+ size_t out_len,
+ const std::string& passphrase,
+ const uint8_t salt[], size_t salt_len,
+ size_t iterations,
+ std::chrono::milliseconds msec);
+
+/**
+* Perform PBKDF2. The prf is assumed to be keyed already.
+*/
+BOTAN_PUBLIC_API(2,8) void pbkdf2(MessageAuthenticationCode& prf,
+ uint8_t out[], size_t out_len,
+ const uint8_t salt[], size_t salt_len,
+ size_t iterations);
+
+/**
+* PBKDF2
+*/
+class BOTAN_PUBLIC_API(2,8) PBKDF2 final : public PasswordHash
+ {
+ public:
+ PBKDF2(const MessageAuthenticationCode& prf, size_t iter) :
+ m_prf(prf.clone()),
+ m_iterations(iter)
+ {}
+
+ PBKDF2(const MessageAuthenticationCode& prf, size_t olen, std::chrono::milliseconds msec);
+
+ size_t iterations() const override { return m_iterations; }
+
+ std::string to_string() const override;
+
+ void derive_key(uint8_t out[], size_t out_len,
+ const char* password, size_t password_len,
+ const uint8_t salt[], size_t salt_len) const override;
+ private:
+ std::unique_ptr<MessageAuthenticationCode> m_prf;
+ size_t m_iterations;
+ };
+
+/**
+* Family of PKCS #5 PBKDF2 operations
+*/
+class BOTAN_PUBLIC_API(2,8) PBKDF2_Family final : public PasswordHashFamily
+ {
+ public:
+ PBKDF2_Family(MessageAuthenticationCode* prf) : m_prf(prf) {}
+
+ std::string name() const override;
+
+ std::unique_ptr<PasswordHash> tune(size_t output_len,
+ std::chrono::milliseconds msec,
+ size_t max_memory) const override;
+
+ /**
+ * Return some default parameter set for this PBKDF that should be good
+ * enough for most users. The value returned may change over time as
+ * processing power and attacks improve.
+ */
+ std::unique_ptr<PasswordHash> default_params() const override;
+
+ std::unique_ptr<PasswordHash> from_iterations(size_t iter) const override;
+
+ std::unique_ptr<PasswordHash> from_params(
+ size_t iter, size_t, size_t) const override;
+ private:
+ std::unique_ptr<MessageAuthenticationCode> m_prf;
+ };
+
+/**
+* PKCS #5 PBKDF2 (old interface)
+*/
+class BOTAN_PUBLIC_API(2,0) PKCS5_PBKDF2 final : public PBKDF
+ {
+ public:
+ std::string name() const override;
+
+ PBKDF* clone() const override;
+
+ size_t pbkdf(uint8_t output_buf[], size_t output_len,
+ const std::string& passphrase,
+ const uint8_t salt[], size_t salt_len,
+ size_t iterations,
+ std::chrono::milliseconds msec) const override;
+
+ /**
+ * Create a PKCS #5 instance using the specified message auth code
+ * @param mac_fn the MAC object to use as PRF
+ */
+ explicit PKCS5_PBKDF2(MessageAuthenticationCode* mac_fn) : m_mac(mac_fn) {}
+ private:
+ std::unique_ptr<MessageAuthenticationCode> m_mac;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/pbkdf/pgp_s2k/info.txt b/comm/third_party/botan/src/lib/pbkdf/pgp_s2k/info.txt
new file mode 100644
index 0000000000..5aabc09195
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pbkdf/pgp_s2k/info.txt
@@ -0,0 +1,7 @@
+<defines>
+PGP_S2K -> 20170527
+</defines>
+
+<requires>
+hash
+</requires>
diff --git a/comm/third_party/botan/src/lib/pbkdf/pgp_s2k/pgp_s2k.cpp b/comm/third_party/botan/src/lib/pbkdf/pgp_s2k/pgp_s2k.cpp
new file mode 100644
index 0000000000..8bcf9239fc
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pbkdf/pgp_s2k/pgp_s2k.cpp
@@ -0,0 +1,219 @@
+/*
+* OpenPGP S2K
+* (C) 1999-2007,2017 Jack Lloyd
+* (C) 2018 Ribose Inc
+*
+* Distributed under the terms of the Botan license
+*/
+
+#include <botan/pgp_s2k.h>
+#include <botan/exceptn.h>
+#include <botan/internal/timer.h>
+#include <algorithm>
+
+namespace Botan {
+
+/*
+PGP stores the iteration count as a single byte
+Thus it can only actually take on one of 256 values, based on the
+formula in RFC 4880 section 3.6.1.3
+*/
+static const uint32_t OPENPGP_S2K_ITERS[256] = {
+ 1024, 1088, 1152, 1216, 1280, 1344, 1408, 1472, 1536, 1600,
+ 1664, 1728, 1792, 1856, 1920, 1984, 2048, 2176, 2304, 2432,
+ 2560, 2688, 2816, 2944, 3072, 3200, 3328, 3456, 3584, 3712,
+ 3840, 3968, 4096, 4352, 4608, 4864, 5120, 5376, 5632, 5888,
+ 6144, 6400, 6656, 6912, 7168, 7424, 7680, 7936, 8192, 8704,
+ 9216, 9728, 10240, 10752, 11264, 11776, 12288, 12800, 13312,
+ 13824, 14336, 14848, 15360, 15872, 16384, 17408, 18432, 19456,
+ 20480, 21504, 22528, 23552, 24576, 25600, 26624, 27648, 28672,
+ 29696, 30720, 31744, 32768, 34816, 36864, 38912, 40960, 43008,
+ 45056, 47104, 49152, 51200, 53248, 55296, 57344, 59392, 61440,
+ 63488, 65536, 69632, 73728, 77824, 81920, 86016, 90112, 94208,
+ 98304, 102400, 106496, 110592, 114688, 118784, 122880, 126976,
+ 131072, 139264, 147456, 155648, 163840, 172032, 180224, 188416,
+ 196608, 204800, 212992, 221184, 229376, 237568, 245760, 253952,
+ 262144, 278528, 294912, 311296, 327680, 344064, 360448, 376832,
+ 393216, 409600, 425984, 442368, 458752, 475136, 491520, 507904,
+ 524288, 557056, 589824, 622592, 655360, 688128, 720896, 753664,
+ 786432, 819200, 851968, 884736, 917504, 950272, 983040, 1015808,
+ 1048576, 1114112, 1179648, 1245184, 1310720, 1376256, 1441792,
+ 1507328, 1572864, 1638400, 1703936, 1769472, 1835008, 1900544,
+ 1966080, 2031616, 2097152, 2228224, 2359296, 2490368, 2621440,
+ 2752512, 2883584, 3014656, 3145728, 3276800, 3407872, 3538944,
+ 3670016, 3801088, 3932160, 4063232, 4194304, 4456448, 4718592,
+ 4980736, 5242880, 5505024, 5767168, 6029312, 6291456, 6553600,
+ 6815744, 7077888, 7340032, 7602176, 7864320, 8126464, 8388608,
+ 8912896, 9437184, 9961472, 10485760, 11010048, 11534336,
+ 12058624, 12582912, 13107200, 13631488, 14155776, 14680064,
+ 15204352, 15728640, 16252928, 16777216, 17825792, 18874368,
+ 19922944, 20971520, 22020096, 23068672, 24117248, 25165824,
+ 26214400, 27262976, 28311552, 29360128, 30408704, 31457280,
+ 32505856, 33554432, 35651584, 37748736, 39845888, 41943040,
+ 44040192, 46137344, 48234496, 50331648, 52428800, 54525952,
+ 56623104, 58720256, 60817408, 62914560, 65011712 };
+
+uint8_t RFC4880_encode_count(size_t desired_iterations)
+ {
+ if(desired_iterations <= OPENPGP_S2K_ITERS[0])
+ return 0;
+
+ if(desired_iterations >= OPENPGP_S2K_ITERS[255])
+ return 255;
+
+ auto i = std::lower_bound(OPENPGP_S2K_ITERS, OPENPGP_S2K_ITERS + 256, desired_iterations);
+
+ return static_cast<uint8_t>(i - OPENPGP_S2K_ITERS);
+ }
+
+size_t RFC4880_decode_count(uint8_t iter)
+ {
+ return OPENPGP_S2K_ITERS[iter];
+ }
+
+namespace {
+
+void pgp_s2k(HashFunction& hash,
+ uint8_t output_buf[], size_t output_len,
+ const char* password, const size_t password_size,
+ const uint8_t salt[], size_t salt_len,
+ size_t iterations)
+ {
+ if(iterations > 1 && salt_len == 0)
+ throw Invalid_Argument("OpenPGP S2K requires a salt in iterated mode");
+
+ secure_vector<uint8_t> input_buf(salt_len + password_size);
+ if(salt_len > 0)
+ {
+ copy_mem(&input_buf[0], salt, salt_len);
+ }
+ if(password_size > 0)
+ {
+ copy_mem(&input_buf[salt_len],
+ cast_char_ptr_to_uint8(password),
+ password_size);
+ }
+
+ secure_vector<uint8_t> hash_buf(hash.output_length());
+
+ size_t pass = 0;
+ size_t generated = 0;
+
+ while(generated != output_len)
+ {
+ const size_t output_this_pass =
+ std::min(hash_buf.size(), output_len - generated);
+
+ // Preload some number of zero bytes (empty first iteration)
+ std::vector<uint8_t> zero_padding(pass);
+ hash.update(zero_padding);
+
+ // The input is always fully processed even if iterations is very small
+ if(input_buf.empty() == false)
+ {
+ size_t left = std::max(iterations, input_buf.size());
+ while(left > 0)
+ {
+ const size_t input_to_take = std::min(left, input_buf.size());
+ hash.update(input_buf.data(), input_to_take);
+ left -= input_to_take;
+ }
+ }
+
+ hash.final(hash_buf.data());
+ copy_mem(output_buf + generated, hash_buf.data(), output_this_pass);
+ generated += output_this_pass;
+ ++pass;
+ }
+ }
+
+}
+
+size_t OpenPGP_S2K::pbkdf(uint8_t output_buf[], size_t output_len,
+ const std::string& password,
+ const uint8_t salt[], size_t salt_len,
+ size_t iterations,
+ std::chrono::milliseconds msec) const
+ {
+ std::unique_ptr<PasswordHash> pwdhash;
+
+ if(iterations == 0)
+ {
+ RFC4880_S2K_Family s2k_params(m_hash->clone());
+ iterations = s2k_params.tune(output_len, msec, 0)->iterations();
+ }
+
+ pgp_s2k(*m_hash, output_buf, output_len,
+ password.c_str(), password.size(),
+ salt, salt_len,
+ iterations);
+
+ return iterations;
+ }
+
+std::string RFC4880_S2K_Family::name() const
+ {
+ return "OpenPGP-S2K(" + m_hash->name() + ")";
+ }
+
+std::unique_ptr<PasswordHash> RFC4880_S2K_Family::tune(size_t output_len, std::chrono::milliseconds msec, size_t) const
+ {
+ const auto tune_time = BOTAN_PBKDF_TUNING_TIME;
+
+ const size_t buf_size = 1024;
+ std::vector<uint8_t> buffer(buf_size);
+
+ Timer timer("RFC4880_S2K", buf_size);
+ timer.run_until_elapsed(tune_time, [&]() {
+ m_hash->update(buffer);
+ });
+
+ const double hash_bytes_per_second = timer.bytes_per_second();
+ const uint64_t desired_nsec = msec.count() * 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);
+
+ const double bytes_to_be_hashed = (hash_bytes_per_second * (desired_nsec / 1000000000.0)) / blocks_required;
+ const size_t iterations = RFC4880_round_iterations(static_cast<size_t>(bytes_to_be_hashed));
+
+ return std::unique_ptr<PasswordHash>(new RFC4880_S2K(m_hash->clone(), iterations));
+ }
+
+std::unique_ptr<PasswordHash> RFC4880_S2K_Family::from_params(size_t iter, size_t, size_t) const
+ {
+ return std::unique_ptr<PasswordHash>(new RFC4880_S2K(m_hash->clone(), iter));
+ }
+
+std::unique_ptr<PasswordHash> RFC4880_S2K_Family::default_params() const
+ {
+ return std::unique_ptr<PasswordHash>(new RFC4880_S2K(m_hash->clone(), 50331648));
+ }
+
+std::unique_ptr<PasswordHash> RFC4880_S2K_Family::from_iterations(size_t iter) const
+ {
+ return std::unique_ptr<PasswordHash>(new RFC4880_S2K(m_hash->clone(), iter));
+ }
+
+RFC4880_S2K::RFC4880_S2K(HashFunction* hash, size_t iterations) :
+ m_hash(hash),
+ m_iterations(iterations)
+ {
+ }
+
+std::string RFC4880_S2K::to_string() const
+ {
+ return "OpenPGP-S2K(" + m_hash->name() + "," + std::to_string(m_iterations) + ")";
+ }
+
+void RFC4880_S2K::derive_key(uint8_t out[], size_t out_len,
+ const char* password, const size_t password_len,
+ const uint8_t salt[], size_t salt_len) const
+ {
+ pgp_s2k(*m_hash, out, out_len,
+ password, password_len,
+ salt, salt_len,
+ m_iterations);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/pbkdf/pgp_s2k/pgp_s2k.h b/comm/third_party/botan/src/lib/pbkdf/pgp_s2k/pgp_s2k.h
new file mode 100644
index 0000000000..7fda724c2a
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pbkdf/pgp_s2k/pgp_s2k.h
@@ -0,0 +1,164 @@
+/*
+* OpenPGP PBKDF
+* (C) 1999-2007,2017 Jack Lloyd
+* (C) 2018 Ribose Inc
+*
+* Distributed under the terms of the Botan license
+*/
+
+#ifndef BOTAN_OPENPGP_S2K_H_
+#define BOTAN_OPENPGP_S2K_H_
+
+#include <botan/pbkdf.h>
+#include <botan/pwdhash.h>
+#include <botan/hash.h>
+
+/*
+This header will not be fully internal - the RFC4880 count
+encoding functions will remain here. But the definition of
+OpenPGP_S2K will be made internal
+*/
+
+//BOTAN_FUTURE_INTERNAL_HEADER(pgp_s2k.h)
+
+namespace Botan {
+
+/**
+* RFC 4880 encodes the iteration count to a single-byte value
+*/
+uint8_t BOTAN_PUBLIC_API(2,8) RFC4880_encode_count(size_t iterations);
+
+/**
+* Decode the iteration count from RFC 4880 encoding
+*/
+size_t BOTAN_PUBLIC_API(2,8) RFC4880_decode_count(uint8_t encoded_iter);
+
+/**
+* Round an arbitrary iteration count to next largest iteration count
+* supported by RFC4880 encoding.
+*/
+inline size_t RFC4880_round_iterations(size_t iterations)
+ {
+ return RFC4880_decode_count(RFC4880_encode_count(iterations));
+ }
+
+/**
+* OpenPGP's S2K
+*
+* See RFC 4880 sections 3.7.1.1, 3.7.1.2, and 3.7.1.3
+* If the salt is empty and iterations == 1, "simple" S2K is used
+* If the salt is non-empty and iterations == 1, "salted" S2K is used
+* If the salt is non-empty and iterations > 1, "iterated" S2K is used
+*
+* Due to complexities of the PGP S2K algorithm, time-based derivation
+* is not supported. So if iterations == 0 and msec.count() > 0, an
+* exception is thrown. In the future this may be supported, in which
+* case "iterated" S2K will be used and the number of iterations
+* performed is returned.
+*
+* Note that unlike PBKDF2, OpenPGP S2K's "iterations" are defined as
+* the number of bytes hashed.
+*/
+class BOTAN_PUBLIC_API(2,2) OpenPGP_S2K final : public PBKDF
+ {
+ public:
+ /**
+ * @param hash the hash function to use
+ */
+ explicit OpenPGP_S2K(HashFunction* hash) : m_hash(hash) {}
+
+ std::string name() const override
+ {
+ return "OpenPGP-S2K(" + m_hash->name() + ")";
+ }
+
+ PBKDF* clone() const override
+ {
+ return new OpenPGP_S2K(m_hash->clone());
+ }
+
+ size_t pbkdf(uint8_t output_buf[], size_t output_len,
+ const std::string& passphrase,
+ const uint8_t salt[], size_t salt_len,
+ size_t iterations,
+ std::chrono::milliseconds msec) const override;
+
+ /**
+ * RFC 4880 encodes the iteration count to a single-byte value
+ */
+ static uint8_t encode_count(size_t iterations)
+ {
+ return RFC4880_encode_count(iterations);
+ }
+
+ static size_t decode_count(uint8_t encoded_iter)
+ {
+ return RFC4880_decode_count(encoded_iter);
+ }
+
+ private:
+ std::unique_ptr<HashFunction> m_hash;
+ };
+
+/**
+* OpenPGP's S2K
+*
+* See RFC 4880 sections 3.7.1.1, 3.7.1.2, and 3.7.1.3
+* If the salt is empty and iterations == 1, "simple" S2K is used
+* If the salt is non-empty and iterations == 1, "salted" S2K is used
+* If the salt is non-empty and iterations > 1, "iterated" S2K is used
+*
+* Note that unlike PBKDF2, OpenPGP S2K's "iterations" are defined as
+* the number of bytes hashed.
+*/
+class BOTAN_PUBLIC_API(2,8) RFC4880_S2K final : public PasswordHash
+ {
+ public:
+ /**
+ * @param hash the hash function to use
+ * @param iterations is rounded due to PGP formatting
+ */
+ RFC4880_S2K(HashFunction* hash, size_t iterations);
+
+ std::string to_string() const override;
+
+ size_t iterations() const override { return m_iterations; }
+
+ void derive_key(uint8_t out[], size_t out_len,
+ const char* password, size_t password_len,
+ const uint8_t salt[], size_t salt_len) const override;
+
+ private:
+ std::unique_ptr<HashFunction> m_hash;
+ size_t m_iterations;
+ };
+
+class BOTAN_PUBLIC_API(2,8) RFC4880_S2K_Family final : public PasswordHashFamily
+ {
+ public:
+ RFC4880_S2K_Family(HashFunction* hash) : m_hash(hash) {}
+
+ std::string name() const override;
+
+ std::unique_ptr<PasswordHash> tune(size_t output_len,
+ std::chrono::milliseconds msec,
+ size_t max_mem) const override;
+
+ /**
+ * Return some default parameter set for this PBKDF that should be good
+ * enough for most users. The value returned may change over time as
+ * processing power and attacks improve.
+ */
+ std::unique_ptr<PasswordHash> default_params() const override;
+
+ std::unique_ptr<PasswordHash> from_iterations(size_t iter) const override;
+
+ std::unique_ptr<PasswordHash> from_params(
+ size_t iter, size_t, size_t) const override;
+ private:
+ std::unique_ptr<HashFunction> m_hash;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/pbkdf/pwdhash.cpp b/comm/third_party/botan/src/lib/pbkdf/pwdhash.cpp
new file mode 100644
index 0000000000..718024a227
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pbkdf/pwdhash.cpp
@@ -0,0 +1,118 @@
+/*
+* (C) 2018 Ribose Inc
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/pwdhash.h>
+#include <botan/exceptn.h>
+#include <botan/scan_name.h>
+
+#if defined(BOTAN_HAS_PBKDF2)
+ #include <botan/pbkdf2.h>
+#endif
+
+#if defined(BOTAN_HAS_PGP_S2K)
+ #include <botan/pgp_s2k.h>
+#endif
+
+#if defined(BOTAN_HAS_SCRYPT)
+ #include <botan/scrypt.h>
+#endif
+
+#if defined(BOTAN_HAS_ARGON2)
+ #include <botan/argon2.h>
+#endif
+
+#if defined(BOTAN_HAS_PBKDF_BCRYPT)
+ #include <botan/bcrypt_pbkdf.h>
+#endif
+
+namespace Botan {
+
+std::unique_ptr<PasswordHashFamily> PasswordHashFamily::create(const std::string& algo_spec,
+ const std::string& provider)
+ {
+ const SCAN_Name req(algo_spec);
+
+#if defined(BOTAN_HAS_PBKDF2)
+ if(req.algo_name() == "PBKDF2")
+ {
+ // TODO OpenSSL
+
+ if(provider.empty() || provider == "base")
+ {
+ if(auto mac = MessageAuthenticationCode::create(req.arg(0)))
+ return std::unique_ptr<PasswordHashFamily>(new PBKDF2_Family(mac.release()));
+
+ if(auto mac = MessageAuthenticationCode::create("HMAC(" + req.arg(0) + ")"))
+ return std::unique_ptr<PasswordHashFamily>(new PBKDF2_Family(mac.release()));
+ }
+
+ return nullptr;
+ }
+#endif
+
+#if defined(BOTAN_HAS_SCRYPT)
+ if(req.algo_name() == "Scrypt")
+ {
+ return std::unique_ptr<PasswordHashFamily>(new Scrypt_Family);
+ }
+#endif
+
+#if defined(BOTAN_HAS_ARGON2)
+ if(req.algo_name() == "Argon2d")
+ {
+ return std::unique_ptr<PasswordHashFamily>(new Argon2_Family(0));
+ }
+ else if(req.algo_name() == "Argon2i")
+ {
+ return std::unique_ptr<PasswordHashFamily>(new Argon2_Family(1));
+ }
+ else if(req.algo_name() == "Argon2id")
+ {
+ return std::unique_ptr<PasswordHashFamily>(new Argon2_Family(2));
+ }
+#endif
+
+#if defined(BOTAN_HAS_PBKDF_BCRYPT)
+ if(req.algo_name() == "Bcrypt-PBKDF")
+ {
+ return std::unique_ptr<PasswordHashFamily>(new Bcrypt_PBKDF_Family);
+ }
+#endif
+
+#if defined(BOTAN_HAS_PGP_S2K)
+ if(req.algo_name() == "OpenPGP-S2K" && req.arg_count() == 1)
+ {
+ if(auto hash = HashFunction::create(req.arg(0)))
+ {
+ return std::unique_ptr<PasswordHashFamily>(new RFC4880_S2K_Family(hash.release()));
+ }
+ }
+#endif
+
+ BOTAN_UNUSED(req);
+ BOTAN_UNUSED(provider);
+
+ return nullptr;
+ }
+
+//static
+std::unique_ptr<PasswordHashFamily>
+PasswordHashFamily::create_or_throw(const std::string& algo,
+ const std::string& provider)
+ {
+ if(auto pbkdf = PasswordHashFamily::create(algo, provider))
+ {
+ return pbkdf;
+ }
+ throw Lookup_Error("PasswordHashFamily", algo, provider);
+ }
+
+std::vector<std::string> PasswordHashFamily::providers(const std::string& algo_spec)
+ {
+ return probe_providers_of<PasswordHashFamily>(algo_spec, { "base", "openssl" });
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/pbkdf/pwdhash.h b/comm/third_party/botan/src/lib/pbkdf/pwdhash.h
new file mode 100644
index 0000000000..ba64a73fd5
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pbkdf/pwdhash.h
@@ -0,0 +1,162 @@
+/*
+* (C) 2018 Ribose Inc
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_PWDHASH_H_
+#define BOTAN_PWDHASH_H_
+
+#include <botan/types.h>
+#include <string>
+#include <memory>
+#include <vector>
+#include <chrono>
+
+namespace Botan {
+
+/**
+* Base class for password based key derivation functions.
+*
+* 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
+ {
+ public:
+ virtual ~PasswordHash() = default;
+
+ virtual std::string to_string() const = 0;
+
+ /**
+ * Most password hashes have some notion of iterations.
+ */
+ virtual size_t iterations() const = 0;
+
+ /**
+ * Some password hashing algorithms have a parameter which controls how
+ * much memory is used. If not supported by some algorithm, returns 0.
+ */
+ virtual size_t memory_param() const { return 0; }
+
+ /**
+ * Some password hashing algorithms have a parallelism parameter.
+ * If the algorithm does not support this notion, then the
+ * function returns zero. This allows distinguishing between a
+ * password hash which just does not support parallel operation,
+ * vs one that does support parallel operation but which has been
+ * configured to use a single lane.
+ */
+ virtual size_t parallelism() const { return 0; }
+
+ /**
+ * Returns an estimate of the total memory usage required to perform this
+ * key derivation.
+ *
+ * If this algorithm uses a small and constant amount of memory, with no
+ * effort made towards being memory hard, this function returns 0.
+ */
+ virtual size_t total_memory_usage() const { return 0; }
+
+ /**
+ * Derive a key from a password
+ *
+ * @param out buffer to store the derived key, must be of out_len bytes
+ * @param out_len the desired length of the key to produce
+ * @param password the password to derive the key from
+ * @param password_len the length of password in bytes
+ * @param salt a randomly chosen salt
+ * @param salt_len length of salt in bytes
+ *
+ * This function is const, but is not thread safe. Different threads should
+ * either use unique objects, or serialize all access.
+ */
+ virtual void derive_key(uint8_t out[], size_t out_len,
+ const char* password, size_t password_len,
+ const uint8_t salt[], size_t salt_len) const = 0;
+ };
+
+class BOTAN_PUBLIC_API(2,8) PasswordHashFamily
+ {
+ public:
+ /**
+ * Create an instance based on a name
+ * If provider is empty then best available is chosen.
+ * @param algo_spec algorithm name
+ * @param provider provider implementation to choose
+ * @return a null pointer if the algo/provider combination cannot be found
+ */
+ static std::unique_ptr<PasswordHashFamily> create(const std::string& algo_spec,
+ const std::string& provider = "");
+
+ /**
+ * Create an instance based on a name, or throw if the
+ * algo/provider combination cannot be found. If provider is
+ * empty then best available is chosen.
+ */
+ static std::unique_ptr<PasswordHashFamily>
+ create_or_throw(const std::string& algo_spec,
+ const std::string& provider = "");
+
+ /**
+ * @return list of available providers for this algorithm, empty if not available
+ */
+ static std::vector<std::string> providers(const std::string& algo_spec);
+
+ virtual ~PasswordHashFamily() = default;
+
+ /**
+ * @return name of this PasswordHash
+ */
+ virtual std::string name() const = 0;
+
+ /**
+ * Return a new parameter set tuned for this machine
+ * @param output_length how long the output length will be
+ * @param 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 zero to place no
+ * limit at all.
+ */
+ virtual std::unique_ptr<PasswordHash> tune(size_t output_length,
+ std::chrono::milliseconds msec,
+ size_t max_memory_usage_mb = 0) const = 0;
+
+ /**
+ * Return some default parameter set for this PBKDF that should be good
+ * enough for most users. The value returned may change over time as
+ * processing power and attacks improve.
+ */
+ virtual std::unique_ptr<PasswordHash> default_params() const = 0;
+
+ /**
+ * Return a parameter chosen based on a rough approximation with the
+ * specified iteration count. The exact value this returns for a particular
+ * algorithm may change from over time. Think of it as an alternative to
+ * tune, where time is expressed in terms of PBKDF2 iterations rather than
+ * milliseconds.
+ */
+ virtual std::unique_ptr<PasswordHash> from_iterations(size_t iterations) const = 0;
+
+ /**
+ * Create a password hash using some scheme specific format.
+ * Eg PBKDF2 and PGP-S2K set iterations in i1
+ * Scrypt uses N,r,p in i{1-3}
+ * Bcrypt-PBKDF just has iterations
+ * Argon2{i,d,id} would use iterations, memory, parallelism for i{1-3},
+ * and Argon2 type is part of the family.
+ *
+ * Values not needed should be set to 0
+ */
+ virtual std::unique_ptr<PasswordHash> from_params(
+ size_t i1,
+ size_t i2 = 0,
+ size_t i3 = 0) const = 0;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/pbkdf/scrypt/info.txt b/comm/third_party/botan/src/lib/pbkdf/scrypt/info.txt
new file mode 100644
index 0000000000..3ba48fd726
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pbkdf/scrypt/info.txt
@@ -0,0 +1,10 @@
+<defines>
+SCRYPT -> 20180902
+</defines>
+
+<requires>
+salsa20
+pbkdf2
+hmac
+sha2_32
+</requires>
diff --git a/comm/third_party/botan/src/lib/pbkdf/scrypt/scrypt.cpp b/comm/third_party/botan/src/lib/pbkdf/scrypt/scrypt.cpp
new file mode 100644
index 0000000000..8275ac9567
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pbkdf/scrypt/scrypt.cpp
@@ -0,0 +1,249 @@
+/**
+* (C) 2018 Jack Lloyd
+* (C) 2018 Ribose Inc
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/scrypt.h>
+#include <botan/pbkdf2.h>
+#include <botan/salsa20.h>
+#include <botan/loadstor.h>
+#include <botan/exceptn.h>
+#include <botan/internal/bit_ops.h>
+#include <botan/internal/timer.h>
+#include <sstream>
+
+namespace Botan {
+
+std::string Scrypt_Family::name() const
+ {
+ return "Scrypt";
+ }
+
+std::unique_ptr<PasswordHash> Scrypt_Family::default_params() const
+ {
+ return std::unique_ptr<PasswordHash>(new Scrypt(32768, 8, 1));
+ }
+
+std::unique_ptr<PasswordHash> Scrypt_Family::tune(size_t output_length,
+ std::chrono::milliseconds msec,
+ size_t max_memory_usage_mb) const
+ {
+ BOTAN_UNUSED(output_length);
+
+ /*
+ * 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:
+ * 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
+ *
+ * Compute stime(8192,1,1) as baseline and extrapolate
+ */
+
+ const size_t max_memory_usage = max_memory_usage_mb * 1024 * 1024;
+ // Starting parameters
+ size_t N = 8192;
+ size_t r = 1;
+ size_t p = 1;
+
+ Timer timer("Scrypt");
+ const auto tune_time = BOTAN_PBKDF_TUNING_TIME;
+
+ timer.run_until_elapsed(tune_time, [&]() {
+ uint8_t output[32] = { 0 };
+ scrypt(output, sizeof(output), "test", 4, nullptr, 0, N, r, p);
+ });
+
+ // No timer events seems strange, perhaps something is wrong - give
+ // up on this and just return default params
+ if(timer.events() == 0)
+ return default_params();
+
+ // nsec per eval of scrypt with initial params
+ const uint64_t measured_time = timer.value() / timer.events();
+
+ const uint64_t target_nsec = msec.count() * static_cast<uint64_t>(1000000);
+
+ uint64_t est_nsec = measured_time;
+
+ // First move increase r by 8x if possible
+
+ if(max_memory_usage == 0 || scrypt_memory_usage(N, r, p)*8 < max_memory_usage)
+ {
+ if(target_nsec / est_nsec >= 5)
+ {
+ r *= 8;
+ est_nsec *= 5;
+ }
+ }
+
+ // Now double N as many times as we can
+
+ while(max_memory_usage == 0 || scrypt_memory_usage(N, r, p)*2 < max_memory_usage)
+ {
+ if(target_nsec / est_nsec >= 2)
+ {
+ N *= 2;
+ est_nsec *= 2;
+ }
+ else
+ break;
+ }
+
+ // If we have extra runtime budget, increment p
+
+ if(target_nsec / est_nsec > 2)
+ p *= std::min<size_t>(1024, static_cast<size_t>(target_nsec / est_nsec));
+
+ return std::unique_ptr<PasswordHash>(new Scrypt(N, r, p));
+ }
+
+std::unique_ptr<PasswordHash> Scrypt_Family::from_params(size_t N, size_t r, size_t p) const
+ {
+ return std::unique_ptr<PasswordHash>(new Scrypt(N, r, p));
+ }
+
+std::unique_ptr<PasswordHash> Scrypt_Family::from_iterations(size_t iter) const
+ {
+ const size_t r = 8;
+ const size_t p = 1;
+
+ size_t N = 8192;
+
+ if(iter > 50000)
+ N = 16384;
+ if(iter > 100000)
+ N = 32768;
+ if(iter > 150000)
+ N = 65536;
+
+ return std::unique_ptr<PasswordHash>(new Scrypt(N, r, p));
+ }
+
+Scrypt::Scrypt(size_t N, size_t r, size_t p) :
+ m_N(N), m_r(r), m_p(p)
+ {
+ if(!is_power_of_2(N))
+ throw Invalid_Argument("Scrypt N parameter must be a power of 2");
+
+ if(p == 0 || p > 1024)
+ throw Invalid_Argument("Invalid or unsupported scrypt p");
+ if(r == 0 || r > 256)
+ throw Invalid_Argument("Invalid or unsupported scrypt r");
+ if(N < 1 || N > 4194304)
+ throw Invalid_Argument("Invalid or unsupported scrypt N");
+ }
+
+std::string Scrypt::to_string() const
+ {
+ std::ostringstream oss;
+ oss << "Scrypt(" << m_N << "," << m_r << "," << m_p << ")";
+ return oss.str();
+ }
+
+size_t Scrypt::total_memory_usage() const
+ {
+ return scrypt_memory_usage(m_N, m_r, m_p);
+ }
+
+void Scrypt::derive_key(uint8_t output[], size_t output_len,
+ const char* password, size_t password_len,
+ const uint8_t salt[], size_t salt_len) const
+ {
+ scrypt(output, output_len,
+ password, password_len,
+ salt, salt_len,
+ N(), r(), p());
+ }
+
+namespace {
+
+void scryptBlockMix(size_t r, uint8_t* B, uint8_t* Y)
+ {
+ uint32_t B32[16];
+ secure_vector<uint8_t> X(64);
+ copy_mem(X.data(), &B[(2*r-1)*64], 64);
+
+ for(size_t i = 0; i != 2*r; i++)
+ {
+ xor_buf(X.data(), &B[64*i], 64);
+ load_le<uint32_t>(B32, X.data(), 16);
+ Salsa20::salsa_core(X.data(), B32, 8);
+ copy_mem(&Y[64*i], X.data(), 64);
+ }
+
+ for(size_t i = 0; i < r; ++i)
+ {
+ copy_mem(&B[i*64], &Y[(i * 2) * 64], 64);
+ }
+
+ for(size_t i = 0; i < r; ++i)
+ {
+ copy_mem(&B[(i + r) * 64], &Y[(i * 2 + 1) * 64], 64);
+ }
+ }
+
+void scryptROMmix(size_t r, size_t N, uint8_t* B, secure_vector<uint8_t>& V)
+ {
+ const size_t S = 128 * r;
+
+ for(size_t i = 0; i != N; ++i)
+ {
+ copy_mem(&V[S*i], B, S);
+ scryptBlockMix(r, B, &V[N*S]);
+ }
+
+ for(size_t i = 0; i != N; ++i)
+ {
+ // compiler doesn't know here that N is power of 2
+ const size_t j = load_le<uint32_t>(&B[(2*r-1)*64], 0) & (N - 1);
+ xor_buf(B, &V[j*S], S);
+ scryptBlockMix(r, B, &V[N*S]);
+ }
+ }
+
+}
+
+void scrypt(uint8_t output[], size_t output_len,
+ const char* password, size_t password_len,
+ const uint8_t salt[], size_t salt_len,
+ size_t N, size_t r, size_t p)
+ {
+ const size_t S = 128 * r;
+ secure_vector<uint8_t> B(p * S);
+ // temp space
+ secure_vector<uint8_t> V((N+1) * S);
+
+ auto hmac_sha256 = MessageAuthenticationCode::create_or_throw("HMAC(SHA-256)");
+
+ try
+ {
+ hmac_sha256->set_key(cast_char_ptr_to_uint8(password), password_len);
+ }
+ catch(Invalid_Key_Length&)
+ {
+ throw Invalid_Argument("Scrypt cannot accept passphrases of the provided length");
+ }
+
+ pbkdf2(*hmac_sha256.get(),
+ B.data(), B.size(),
+ salt, salt_len,
+ 1);
+
+ // these can be parallel
+ for(size_t i = 0; i != p; ++i)
+ {
+ scryptROMmix(r, N, &B[128*r*i], V);
+ }
+
+ pbkdf2(*hmac_sha256.get(),
+ output, output_len,
+ B.data(), B.size(),
+ 1);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/pbkdf/scrypt/scrypt.h b/comm/third_party/botan/src/lib/pbkdf/scrypt/scrypt.h
new file mode 100644
index 0000000000..60cdbd0460
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pbkdf/scrypt/scrypt.h
@@ -0,0 +1,127 @@
+/**
+* (C) 2018 Jack Lloyd
+* (C) 2018 Ribose Inc
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_SCRYPT_H_
+#define BOTAN_SCRYPT_H_
+
+#include <botan/pwdhash.h>
+
+//BOTAN_FUTURE_INTERNAL_HEADER(scrypt.h)
+
+namespace Botan {
+
+/**
+* Scrypt key derivation function (RFC 7914)
+*/
+class BOTAN_PUBLIC_API(2,8) Scrypt final : public PasswordHash
+ {
+ 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
+ */
+ void derive_key(uint8_t out[], size_t out_len,
+ const char* password, size_t password_len,
+ const uint8_t salt[], size_t salt_len) const override;
+
+ std::string to_string() const override;
+
+ size_t N() const { return m_N; }
+ size_t r() const { return m_r; }
+ size_t p() const { return m_p; }
+
+ size_t iterations() const override { return r(); }
+
+ size_t parallelism() const override { return p(); }
+
+ size_t memory_param() const override { return N(); }
+
+ size_t total_memory_usage() const override;
+
+ private:
+ size_t m_N, m_r, m_p;
+ };
+
+class BOTAN_PUBLIC_API(2,8) Scrypt_Family final : public PasswordHashFamily
+ {
+ public:
+ std::string name() const override;
+
+ std::unique_ptr<PasswordHash> tune(size_t output_length,
+ std::chrono::milliseconds msec,
+ size_t max_memory) const override;
+
+ std::unique_ptr<PasswordHash> default_params() const override;
+
+ std::unique_ptr<PasswordHash> from_iterations(size_t iter) const override;
+
+ std::unique_ptr<PasswordHash> from_params(
+ size_t N, size_t r, size_t p) const override;
+ };
+
+/**
+* Scrypt key derivation function (RFC 7914)
+*
+* @param output the output will be placed here
+* @param output_len length of output
+* @param password the user password
+* @param password_len length of password
+* @param salt the salt
+* @param salt_len length of salt
+* @param N the CPU/Memory cost parameter, must be power of 2
+* @param r the block size parameter
+* @param p the parallelization parameter
+*
+* Suitable parameters for most uses would be N = 32768, r = 8, p = 1
+*
+* Scrypt uses approximately (p + N + 1) * 128 * r bytes of memory
+*/
+void BOTAN_PUBLIC_API(2,8) scrypt(uint8_t output[], size_t output_len,
+ const char* password, size_t password_len,
+ const uint8_t salt[], size_t salt_len,
+ size_t N, size_t r, size_t p);
+
+/**
+* Scrypt key derivation function (RFC 7914)
+* Before 2.8 this function was the primary interface for scrypt
+*
+* @param output the output will be placed here
+* @param output_len length of output
+* @param password the user password
+* @param salt the salt
+* @param salt_len length of salt
+* @param N the CPU/Memory cost parameter, must be power of 2
+* @param r the block size parameter
+* @param p the parallelization parameter
+*
+* Suitable parameters for most uses would be N = 32768, r = 8, p = 1
+*
+* Scrypt uses approximately (p + N + 1) * 128 * r bytes of memory
+*/
+inline void scrypt(uint8_t output[], size_t output_len,
+ const std::string& password,
+ const uint8_t salt[], size_t salt_len,
+ size_t N, size_t r, size_t p)
+ {
+ return scrypt(output, output_len,
+ password.c_str(), password.size(),
+ salt, salt_len,
+ N, r, p);
+ }
+
+inline size_t scrypt_memory_usage(size_t N, size_t r, size_t p)
+ {
+ return 128 * r * (N + p);
+ }
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/pk_pad/eme.cpp b/comm/third_party/botan/src/lib/pk_pad/eme.cpp
new file mode 100644
index 0000000000..ffedac923d
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pk_pad/eme.cpp
@@ -0,0 +1,94 @@
+/*
+* EME Base Class
+* (C) 1999-2008 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/eme.h>
+#include <botan/scan_name.h>
+#include <botan/exceptn.h>
+#include <botan/parsing.h>
+
+#if defined(BOTAN_HAS_EME_OAEP)
+#include <botan/oaep.h>
+#endif
+
+#if defined(BOTAN_HAS_EME_PKCS1)
+#include <botan/eme_pkcs.h>
+#endif
+
+#if defined(BOTAN_HAS_EME_RAW)
+#include <botan/eme_raw.h>
+#endif
+
+namespace Botan {
+
+EME* get_eme(const std::string& algo_spec)
+ {
+#if defined(BOTAN_HAS_EME_RAW)
+ if(algo_spec == "Raw")
+ return new EME_Raw;
+#endif
+
+#if defined(BOTAN_HAS_EME_PKCS1)
+ if(algo_spec == "PKCS1v15" || algo_spec == "EME-PKCS1-v1_5")
+ return new EME_PKCS1v15;
+#endif
+
+#if defined(BOTAN_HAS_EME_OAEP)
+ SCAN_Name req(algo_spec);
+
+ 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 new OAEP(hash.release(), 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 new OAEP(hash.release(), mgf1_hash.release(), req.arg(2, ""));
+ }
+ }
+ }
+ }
+#endif
+
+ throw Algorithm_Not_Found(algo_spec);
+ }
+
+/*
+* Encode a message
+*/
+secure_vector<uint8_t> EME::encode(const uint8_t msg[], size_t msg_len,
+ size_t key_bits,
+ RandomNumberGenerator& rng) const
+ {
+ return pad(msg, msg_len, key_bits, rng);
+ }
+
+/*
+* Encode a message
+*/
+secure_vector<uint8_t> EME::encode(const secure_vector<uint8_t>& msg,
+ size_t key_bits,
+ RandomNumberGenerator& rng) const
+ {
+ return pad(msg.data(), msg.size(), key_bits, rng);
+ }
+
+
+}
diff --git a/comm/third_party/botan/src/lib/pk_pad/eme.h b/comm/third_party/botan/src/lib/pk_pad/eme.h
new file mode 100644
index 0000000000..38cce4b093
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pk_pad/eme.h
@@ -0,0 +1,94 @@
+/*
+* EME Classes
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_PUBKEY_EME_ENCRYPTION_PAD_H_
+#define BOTAN_PUBKEY_EME_ENCRYPTION_PAD_H_
+
+#include <botan/secmem.h>
+#include <string>
+
+BOTAN_FUTURE_INTERNAL_HEADER(eme.h)
+
+namespace Botan {
+
+class RandomNumberGenerator;
+
+/**
+* Encoding Method for Encryption
+*/
+class BOTAN_PUBLIC_API(2,0) EME
+ {
+ public:
+ virtual ~EME() = default;
+
+ /**
+ * 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 in the plaintext
+ * @param in_length length of plaintext in bytes
+ * @param key_length length of the key in bits
+ * @param rng a random number generator
+ * @return encoded plaintext
+ */
+ secure_vector<uint8_t> encode(const uint8_t in[],
+ size_t in_length,
+ size_t key_length,
+ RandomNumberGenerator& rng) const;
+
+ /**
+ * Encode an input
+ * @param in the plaintext
+ * @param key_length length of the key in bits
+ * @param rng a random number generator
+ * @return encoded plaintext
+ */
+ secure_vector<uint8_t> encode(const secure_vector<uint8_t>& in,
+ size_t key_length,
+ RandomNumberGenerator& rng) const;
+
+ /**
+ * Decode an input
+ * @param valid_mask written to specifies if output is valid
+ * @param in the encoded plaintext
+ * @param in_len length of encoded plaintext in bytes
+ * @return bytes of out[] written to along with
+ * validity mask (0xFF if valid, else 0x00)
+ */
+ virtual secure_vector<uint8_t> unpad(uint8_t& valid_mask,
+ const uint8_t in[],
+ size_t in_len) const = 0;
+
+ /**
+ * Encode an input
+ * @param in the plaintext
+ * @param in_length length of plaintext in bytes
+ * @param key_length length of the key in bits
+ * @param rng a random number generator
+ * @return encoded plaintext
+ */
+ virtual secure_vector<uint8_t> pad(const uint8_t in[],
+ size_t in_length,
+ size_t key_length,
+ RandomNumberGenerator& rng) const = 0;
+ };
+
+/**
+* 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
+*/
+BOTAN_PUBLIC_API(2,0) EME* get_eme(const std::string& algo_spec);
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/pk_pad/eme_oaep/info.txt b/comm/third_party/botan/src/lib/pk_pad/eme_oaep/info.txt
new file mode 100644
index 0000000000..cabe23fb85
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pk_pad/eme_oaep/info.txt
@@ -0,0 +1,7 @@
+<defines>
+EME_OAEP -> 20180305
+</defines>
+
+<requires>
+mgf1
+</requires>
diff --git a/comm/third_party/botan/src/lib/pk_pad/eme_oaep/oaep.cpp b/comm/third_party/botan/src/lib/pk_pad/eme_oaep/oaep.cpp
new file mode 100644
index 0000000000..428b6ac3bf
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pk_pad/eme_oaep/oaep.cpp
@@ -0,0 +1,168 @@
+/*
+* OAEP
+* (C) 1999-2010,2015,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/oaep.h>
+#include <botan/mgf1.h>
+#include <botan/exceptn.h>
+#include <botan/rng.h>
+#include <botan/internal/ct_utils.h>
+
+namespace Botan {
+
+/*
+* OAEP Pad Operation
+*/
+secure_vector<uint8_t> OAEP::pad(const uint8_t in[], size_t in_length,
+ size_t key_length,
+ RandomNumberGenerator& rng) const
+ {
+ key_length /= 8;
+
+ if(in_length > maximum_input_size(key_length * 8))
+ {
+ throw Invalid_Argument("OAEP: Input is too large");
+ }
+
+ secure_vector<uint8_t> out(key_length);
+
+ rng.randomize(out.data(), m_Phash.size());
+
+ buffer_insert(out, m_Phash.size(), m_Phash.data(), m_Phash.size());
+ out[out.size() - in_length - 1] = 0x01;
+ buffer_insert(out, out.size() - in_length, in, in_length);
+
+ mgf1_mask(*m_mgf1_hash,
+ out.data(), m_Phash.size(),
+ &out[m_Phash.size()], out.size() - m_Phash.size());
+
+ mgf1_mask(*m_mgf1_hash,
+ &out[m_Phash.size()], out.size() - m_Phash.size(),
+ out.data(), m_Phash.size());
+
+ return out;
+ }
+
+/*
+* OAEP Unpad Operation
+*/
+secure_vector<uint8_t> OAEP::unpad(uint8_t& valid_mask,
+ const uint8_t in[], size_t in_length) const
+ {
+ /*
+ 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 (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 can always be skipped safely.
+ */
+
+ const auto leading_0 = CT::Mask<uint8_t>::is_zero(in[0]);
+
+ secure_vector<uint8_t> input(in + 1, in + in_length);
+
+ const size_t hlen = m_Phash.size();
+
+ mgf1_mask(*m_mgf1_hash,
+ &input[hlen], input.size() - hlen,
+ input.data(), hlen);
+
+ mgf1_mask(*m_mgf1_hash,
+ input.data(), hlen,
+ &input[hlen], input.size() - hlen);
+
+ auto unpadded = oaep_find_delim(valid_mask, input.data(), input.size(), m_Phash);
+ valid_mask &= leading_0.unpoisoned_value();
+ return unpadded;
+ }
+
+secure_vector<uint8_t>
+oaep_find_delim(uint8_t& valid_mask,
+ const uint8_t input[], size_t input_len,
+ const secure_vector<uint8_t>& Phash)
+ {
+ const size_t hlen = Phash.size();
+
+ // Too short to be valid, reject immediately
+ if(input_len < 1 + 2*hlen)
+ {
+ return secure_vector<uint8_t>();
+ }
+
+ CT::poison(input, input_len);
+
+ size_t delim_idx = 2 * hlen;
+ CT::Mask<uint8_t> waiting_for_delim = CT::Mask<uint8_t>::set();
+ CT::Mask<uint8_t> bad_input_m = CT::Mask<uint8_t>::cleared();
+
+ for(size_t i = delim_idx; i < input_len; ++i)
+ {
+ const auto zero_m = CT::Mask<uint8_t>::is_zero(input[i]);
+ const auto one_m = CT::Mask<uint8_t>::is_equal(input[i], 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;
+ bad_input_m |= CT::Mask<uint8_t>::is_zero(ct_compare_u8(&input[hlen], Phash.data(), hlen));
+
+ delim_idx += 1;
+
+ valid_mask = (~bad_input_m).unpoisoned_value();
+ const secure_vector<uint8_t> output = CT::copy_output(bad_input_m, input, input_len, delim_idx);
+
+ CT::unpoison(input, input_len);
+
+ return output;
+ }
+
+/*
+* 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 Constructor
+*/
+OAEP::OAEP(HashFunction* hash, const std::string& P) : m_mgf1_hash(hash)
+ {
+ m_Phash = m_mgf1_hash->process(P);
+ }
+
+OAEP::OAEP(HashFunction* hash,
+ HashFunction* mgf1_hash,
+ const std::string& P) : m_mgf1_hash(mgf1_hash)
+ {
+ std::unique_ptr<HashFunction> phash(hash); // takes ownership
+ m_Phash = phash->process(P);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/pk_pad/eme_oaep/oaep.h b/comm/third_party/botan/src/lib/pk_pad/eme_oaep/oaep.h
new file mode 100644
index 0000000000..383617b82c
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pk_pad/eme_oaep/oaep.h
@@ -0,0 +1,62 @@
+/*
+* OAEP
+* (C) 1999-2007,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_OAEP_H_
+#define BOTAN_OAEP_H_
+
+#include <botan/eme.h>
+#include <botan/hash.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(oaep.h)
+
+namespace Botan {
+
+/**
+* OAEP (called EME1 in IEEE 1363 and in earlier versions of the library)
+* as specified in PKCS#1 v2.0 (RFC 2437)
+*/
+class BOTAN_PUBLIC_API(2,0) 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(HashFunction* hash, const std::string& 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(HashFunction* hash,
+ HashFunction* mgf1_hash,
+ const std::string& P = "");
+ private:
+ secure_vector<uint8_t> pad(const uint8_t in[],
+ size_t in_length,
+ size_t key_length,
+ RandomNumberGenerator& rng) const override;
+
+ secure_vector<uint8_t> unpad(uint8_t& valid_mask,
+ const uint8_t in[],
+ size_t in_len) const override;
+
+ secure_vector<uint8_t> m_Phash;
+ std::unique_ptr<HashFunction> m_mgf1_hash;
+ };
+
+secure_vector<uint8_t>
+BOTAN_TEST_API oaep_find_delim(uint8_t& valid_mask,
+ const uint8_t input[], size_t input_len,
+ const secure_vector<uint8_t>& Phash);
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/pk_pad/eme_pkcs1/eme_pkcs.cpp b/comm/third_party/botan/src/lib/pk_pad/eme_pkcs1/eme_pkcs.cpp
new file mode 100644
index 0000000000..eac3da5d9a
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pk_pad/eme_pkcs1/eme_pkcs.cpp
@@ -0,0 +1,109 @@
+/*
+* PKCS #1 v1.5 Type 2 (encryption) padding
+* (C) 1999-2007,2015,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/eme_pkcs.h>
+#include <botan/exceptn.h>
+#include <botan/rng.h>
+#include <botan/internal/ct_utils.h>
+
+namespace Botan {
+
+/*
+* PKCS1 Pad Operation
+*/
+secure_vector<uint8_t> EME_PKCS1v15::pad(const uint8_t in[], size_t inlen,
+ size_t key_length,
+ RandomNumberGenerator& rng) const
+ {
+ key_length /= 8;
+
+ if(inlen > maximum_input_size(key_length * 8))
+ {
+ throw Invalid_Argument("PKCS1: Input is too large");
+ }
+
+ secure_vector<uint8_t> out(key_length);
+
+ out[0] = 0x02;
+ rng.randomize(out.data() + 1, (key_length - inlen - 2));
+
+ for(size_t j = 1; j != key_length - inlen - 1; ++j)
+ {
+ if(out[j] == 0)
+ {
+ out[j] = rng.next_nonzero_byte();
+ }
+ }
+
+ buffer_insert(out, key_length - inlen, in, inlen);
+
+ return out;
+ }
+
+/*
+* PKCS1 Unpad Operation
+*/
+secure_vector<uint8_t> EME_PKCS1v15::unpad(uint8_t& valid_mask,
+ const uint8_t in[], size_t inlen) const
+ {
+ /*
+ * 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(inlen < 11)
+ {
+ valid_mask = false;
+ return secure_vector<uint8_t>();
+ }
+
+ CT::poison(in, inlen);
+
+ CT::Mask<uint8_t> bad_input_m = CT::Mask<uint8_t>::cleared();
+ CT::Mask<uint8_t> seen_zero_m = CT::Mask<uint8_t>::cleared();
+ size_t delim_idx = 2; // initial 0002
+
+ bad_input_m |= ~CT::Mask<uint8_t>::is_equal(in[0], 0);
+ bad_input_m |= ~CT::Mask<uint8_t>::is_equal(in[1], 2);
+
+ for(size_t i = 2; i < inlen; ++i)
+ {
+ const auto is_zero_m = CT::Mask<uint8_t>::is_zero(in[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<uint8_t>(CT::Mask<size_t>::is_lt(delim_idx, 11));
+
+ valid_mask = (~bad_input_m).unpoisoned_value();
+ const secure_vector<uint8_t> output = CT::copy_output(bad_input_m, in, inlen, delim_idx);
+
+ CT::unpoison(in, inlen);
+
+ return output;
+ }
+
+/*
+* 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;
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/pk_pad/eme_pkcs1/eme_pkcs.h b/comm/third_party/botan/src/lib/pk_pad/eme_pkcs1/eme_pkcs.h
new file mode 100644
index 0000000000..fb7cf14197
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pk_pad/eme_pkcs1/eme_pkcs.h
@@ -0,0 +1,35 @@
+/*
+* 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 <botan/eme.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(eme_pkcs.h)
+
+namespace Botan {
+
+/**
+* EME from PKCS #1 v1.5
+*/
+class BOTAN_PUBLIC_API(2,0) EME_PKCS1v15 final : public EME
+ {
+ public:
+ size_t maximum_input_size(size_t) const override;
+
+ secure_vector<uint8_t> pad(const uint8_t[], size_t, size_t,
+ RandomNumberGenerator&) const override;
+
+ secure_vector<uint8_t> unpad(uint8_t& valid_mask,
+ const uint8_t in[],
+ size_t in_len) const override;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/pk_pad/eme_pkcs1/info.txt b/comm/third_party/botan/src/lib/pk_pad/eme_pkcs1/info.txt
new file mode 100644
index 0000000000..772806e429
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pk_pad/eme_pkcs1/info.txt
@@ -0,0 +1,4 @@
+<defines>
+EME_PKCS1v15 -> 20131128
+EME_PKCS1 -> 20190426
+</defines>
diff --git a/comm/third_party/botan/src/lib/pk_pad/eme_raw/eme_raw.cpp b/comm/third_party/botan/src/lib/pk_pad/eme_raw/eme_raw.cpp
new file mode 100644
index 0000000000..066e7afd8c
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pk_pad/eme_raw/eme_raw.cpp
@@ -0,0 +1,31 @@
+/*
+* (C) 2015,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/eme_raw.h>
+#include <botan/internal/bit_ops.h>
+#include <botan/internal/ct_utils.h>
+
+namespace Botan {
+
+secure_vector<uint8_t> EME_Raw::pad(const uint8_t in[], size_t in_length,
+ size_t,
+ RandomNumberGenerator&) const
+ {
+ return secure_vector<uint8_t>(in, in + in_length);
+ }
+
+secure_vector<uint8_t> EME_Raw::unpad(uint8_t& valid_mask,
+ const uint8_t in[], size_t in_length) const
+ {
+ valid_mask = 0xFF;
+ return CT::strip_leading_zeros(in, in_length);
+ }
+
+size_t EME_Raw::maximum_input_size(size_t keybits) const
+ {
+ return keybits / 8;
+ }
+}
diff --git a/comm/third_party/botan/src/lib/pk_pad/eme_raw/eme_raw.h b/comm/third_party/botan/src/lib/pk_pad/eme_raw/eme_raw.h
new file mode 100644
index 0000000000..840448c559
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pk_pad/eme_raw/eme_raw.h
@@ -0,0 +1,33 @@
+/*
+* (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 <botan/eme.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(eme_raw.h)
+
+namespace Botan {
+
+class BOTAN_PUBLIC_API(2,0) EME_Raw final : public EME
+ {
+ public:
+ size_t maximum_input_size(size_t i) const override;
+
+ EME_Raw() = default;
+ private:
+ secure_vector<uint8_t> pad(const uint8_t[], size_t, size_t,
+ RandomNumberGenerator&) const override;
+
+ secure_vector<uint8_t> unpad(uint8_t& valid_mask,
+ const uint8_t in[],
+ size_t in_len) const override;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/pk_pad/eme_raw/info.txt b/comm/third_party/botan/src/lib/pk_pad/eme_raw/info.txt
new file mode 100644
index 0000000000..c42365e98a
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pk_pad/eme_raw/info.txt
@@ -0,0 +1,3 @@
+<defines>
+EME_RAW -> 20150313
+</defines>
diff --git a/comm/third_party/botan/src/lib/pk_pad/emsa.cpp b/comm/third_party/botan/src/lib/pk_pad/emsa.cpp
new file mode 100644
index 0000000000..a3e4486862
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pk_pad/emsa.cpp
@@ -0,0 +1,207 @@
+/*
+* (C) 2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/emsa.h>
+#include <botan/hash.h>
+#include <botan/scan_name.h>
+#include <botan/exceptn.h>
+
+#if defined(BOTAN_HAS_EMSA1)
+ #include <botan/emsa1.h>
+#endif
+
+#if defined(BOTAN_HAS_EMSA_X931)
+ #include <botan/emsa_x931.h>
+#endif
+
+#if defined(BOTAN_HAS_EMSA_PKCS1)
+ #include <botan/emsa_pkcs1.h>
+#endif
+
+#if defined(BOTAN_HAS_EMSA_PSSR)
+ #include <botan/pssr.h>
+#endif
+
+#if defined(BOTAN_HAS_EMSA_RAW)
+ #include <botan/emsa_raw.h>
+#endif
+
+#if defined(BOTAN_HAS_ISO_9796)
+ #include <botan/iso9796.h>
+#endif
+
+namespace Botan {
+
+AlgorithmIdentifier EMSA::config_for_x509(const Private_Key&,
+ const std::string&) const
+ {
+ throw Not_Implemented("Encoding " + name() + " not supported for signing X509 objects");
+ }
+
+EMSA* get_emsa(const std::string& algo_spec)
+ {
+ SCAN_Name req(algo_spec);
+
+#if defined(BOTAN_HAS_EMSA1)
+ if(req.algo_name() == "EMSA1" && req.arg_count() == 1)
+ {
+ if(auto hash = HashFunction::create(req.arg(0)))
+ return new EMSA1(hash.release());
+ }
+#endif
+
+#if defined(BOTAN_HAS_EMSA_PKCS1)
+ 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 new EMSA_PKCS1v15_Raw(req.arg(1));
+ }
+ else if(req.arg_count() == 1)
+ {
+ if(req.arg(0) == "Raw")
+ {
+ return new EMSA_PKCS1v15_Raw;
+ }
+ else
+ {
+ if(auto hash = HashFunction::create(req.arg(0)))
+ {
+ return new EMSA_PKCS1v15(hash.release());
+ }
+ }
+ }
+ }
+#endif
+
+#if defined(BOTAN_HAS_EMSA_PSSR)
+ 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 h = HashFunction::create(req.arg(0)))
+ {
+ if(req.arg_count() == 3)
+ {
+ const size_t salt_size = req.arg_as_integer(2, 0);
+ return new PSSR_Raw(h.release(), salt_size);
+ }
+ else
+ {
+ return new PSSR_Raw(h.release());
+ }
+ }
+ }
+ }
+
+ 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 h = HashFunction::create(req.arg(0)))
+ {
+ if(req.arg_count() == 3)
+ {
+ const size_t salt_size = req.arg_as_integer(2, 0);
+ return new PSSR(h.release(), salt_size);
+ }
+ else
+ {
+ return new PSSR(h.release());
+ }
+ }
+ }
+ }
+#endif
+
+#if defined(BOTAN_HAS_ISO_9796)
+ if(req.algo_name() == "ISO_9796_DS2")
+ {
+ if(req.arg_count_between(1, 3))
+ {
+ if(auto h = HashFunction::create(req.arg(0)))
+ {
+ const size_t salt_size = req.arg_as_integer(2, h->output_length());
+ const bool implicit = req.arg(1, "exp") == "imp";
+ return new ISO_9796_DS2(h.release(), 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 h = HashFunction::create(req.arg(0)))
+ {
+ const bool implicit = req.arg(1, "exp") == "imp";
+ return new ISO_9796_DS3(h.release(), implicit);
+ }
+ }
+ }
+#endif
+
+#if defined(BOTAN_HAS_EMSA_X931)
+ 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 new EMSA_X931(hash.release());
+ }
+ }
+ }
+#endif
+
+#if defined(BOTAN_HAS_EMSA_RAW)
+ if(req.algo_name() == "Raw")
+ {
+ if(req.arg_count() == 0)
+ {
+ return new EMSA_Raw;
+ }
+ else
+ {
+ auto hash = HashFunction::create(req.arg(0));
+ if(hash)
+ return new EMSA_Raw(hash->output_length());
+ }
+ }
+#endif
+
+ throw Algorithm_Not_Found(algo_spec);
+ }
+
+std::string hash_for_emsa(const std::string& algo_spec)
+ {
+ SCAN_Name emsa_name(algo_spec);
+
+ if(emsa_name.arg_count() > 0)
+ {
+ const std::string pos_hash = emsa_name.arg(0);
+ return pos_hash;
+ }
+
+ // If we don't understand what this is return a safe default
+#if defined(BOTAN_HAS_SHA2_64)
+ return "SHA-512";
+#else
+ return "SHA-256";
+#endif
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/pk_pad/emsa.h b/comm/third_party/botan/src/lib/pk_pad/emsa.h
new file mode 100644
index 0000000000..7178eae237
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pk_pad/emsa.h
@@ -0,0 +1,107 @@
+/*
+* 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 <botan/secmem.h>
+#include <botan/asn1_obj.h>
+#include <string>
+
+BOTAN_FUTURE_INTERNAL_HEADER(emsa.h)
+
+namespace Botan {
+
+class Private_Key;
+class RandomNumberGenerator;
+
+/**
+* EMSA, from IEEE 1363s Encoding Method for Signatures, Appendix
+*
+* Any way of encoding/padding signatures
+*/
+class BOTAN_PUBLIC_API(2,0) EMSA
+ {
+ public:
+ virtual ~EMSA() = default;
+
+ /**
+ * 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 secure_vector<uint8_t> 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 secure_vector<uint8_t> encoding_of(const secure_vector<uint8_t>& 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 secure_vector<uint8_t>& coded,
+ const secure_vector<uint8_t>& raw,
+ size_t key_bits) = 0;
+
+ /**
+ * Prepare sig_algo for use in choose_sig_format for x509 certs
+ *
+ * @param key used for checking compatibility with the encoding scheme
+ * @param cert_hash_name is checked to equal the hash for the encoding
+ * @return algorithm identifier to signatures created using this key,
+ * padding method and hash.
+ */
+ virtual AlgorithmIdentifier config_for_x509(const Private_Key& key,
+ const std::string& cert_hash_name) const;
+
+ /**
+ * @return a new object representing the same encoding method as *this
+ */
+ virtual EMSA* clone() = 0;
+
+ /**
+ * @return the SCAN name of the encoding/padding scheme
+ */
+ virtual std::string name() const = 0;
+ };
+
+/**
+* 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
+*/
+BOTAN_PUBLIC_API(2,0) EMSA* get_emsa(const std::string& algo_spec);
+
+/**
+* Returns the hash function used in the given EMSA scheme
+* If the hash function is not specified or not understood,
+* returns "SHA-512"
+* @param algo_spec the name of the EMSA
+* @return hash function used in the given EMSA scheme
+*/
+BOTAN_PUBLIC_API(2,0) std::string hash_for_emsa(const std::string& algo_spec);
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/pk_pad/emsa1/emsa1.cpp b/comm/third_party/botan/src/lib/pk_pad/emsa1/emsa1.cpp
new file mode 100644
index 0000000000..f7293db270
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pk_pad/emsa1/emsa1.cpp
@@ -0,0 +1,133 @@
+/*
+* EMSA1
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/emsa1.h>
+#include <botan/exceptn.h>
+#include <botan/pk_keys.h>
+#include <botan/internal/padding.h>
+
+namespace Botan {
+
+namespace {
+
+secure_vector<uint8_t> emsa1_encoding(const secure_vector<uint8_t>& msg,
+ size_t output_bits)
+ {
+ if(8*msg.size() <= output_bits)
+ return msg;
+
+ size_t shift = 8*msg.size() - output_bits;
+
+ size_t byte_shift = shift / 8, bit_shift = shift % 8;
+ secure_vector<uint8_t> digest(msg.size() - byte_shift);
+
+ for(size_t j = 0; j != msg.size() - byte_shift; ++j)
+ digest[j] = msg[j];
+
+ if(bit_shift)
+ {
+ uint8_t carry = 0;
+ for(size_t j = 0; j != digest.size(); ++j)
+ {
+ uint8_t temp = digest[j];
+ digest[j] = (temp >> bit_shift) | carry;
+ carry = (temp << (8 - bit_shift));
+ }
+ }
+ return digest;
+ }
+
+}
+
+std::string EMSA1::name() const
+ {
+ return "EMSA1(" + m_hash->name() + ")";
+ }
+
+EMSA* EMSA1::clone()
+ {
+ return new EMSA1(m_hash->clone());
+ }
+
+void EMSA1::update(const uint8_t input[], size_t length)
+ {
+ m_hash->update(input, length);
+ }
+
+secure_vector<uint8_t> EMSA1::raw_data()
+ {
+ return m_hash->final();
+ }
+
+secure_vector<uint8_t> EMSA1::encoding_of(const secure_vector<uint8_t>& msg,
+ size_t output_bits,
+ RandomNumberGenerator&)
+ {
+ if(msg.size() != hash_output_length())
+ throw Encoding_Error("EMSA1::encoding_of: Invalid size for input");
+ return emsa1_encoding(msg, output_bits);
+ }
+
+bool EMSA1::verify(const secure_vector<uint8_t>& input,
+ const secure_vector<uint8_t>& raw,
+ size_t key_bits)
+ {
+ if(raw.size() != m_hash->output_length())
+ return false;
+
+ // Call emsa1_encoding to handle any required bit shifting
+ const secure_vector<uint8_t> our_coding = emsa1_encoding(raw, key_bits);
+
+ if(our_coding.size() < input.size())
+ return false;
+
+ const size_t offset = our_coding.size() - input.size(); // must be >= 0 per check above
+
+ // If our encoding is longer, all the bytes in it must be zero
+ for(size_t i = 0; i != offset; ++i)
+ if(our_coding[i] != 0)
+ return false;
+
+ return constant_time_compare(input.data(), &our_coding[offset], input.size());
+ }
+
+AlgorithmIdentifier EMSA1::config_for_x509(const Private_Key& key,
+ const std::string& cert_hash_name) const
+ {
+ if(cert_hash_name != m_hash->name())
+ throw Invalid_Argument("Hash function from opts and hash_fn argument"
+ " need to be identical");
+ // check that the signature algorithm and the padding scheme fit
+ if(!sig_algo_and_pad_ok(key.algo_name(), "EMSA1"))
+ {
+ throw Invalid_Argument("Encoding scheme with canonical name EMSA1"
+ " not supported for signature algorithm " + key.algo_name());
+ }
+
+ const OID oid = OID::from_string(key.algo_name() + "/" + name());
+
+ const std::string algo_name = key.algo_name();
+ std::vector<uint8_t> parameters;
+ if(algo_name == "DSA" ||
+ algo_name == "ECDSA" ||
+ algo_name == "ECGDSA" ||
+ algo_name == "ECKCDSA" ||
+ algo_name == "GOST-34.10" ||
+ algo_name == "GOST-34.10-2012-256" ||
+ algo_name == "GOST-34.10-2012-512")
+ {
+ // for DSA, ECDSA, GOST parameters "SHALL" be empty
+ }
+ else
+ {
+ parameters = key.algorithm_identifier().get_parameters();
+ }
+
+ return AlgorithmIdentifier(oid, parameters);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/pk_pad/emsa1/emsa1.h b/comm/third_party/botan/src/lib/pk_pad/emsa1/emsa1.h
new file mode 100644
index 0000000000..76736bc276
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pk_pad/emsa1/emsa1.h
@@ -0,0 +1,55 @@
+/*
+* EMSA1
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_EMSA1_H_
+#define BOTAN_EMSA1_H_
+
+#include <botan/emsa.h>
+#include <botan/hash.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(emsa1.h)
+
+namespace Botan {
+
+/**
+* EMSA1 from IEEE 1363
+* Essentially, sign the hash directly
+*/
+class BOTAN_PUBLIC_API(2,0) EMSA1 final : public EMSA
+ {
+ public:
+ /**
+ * @param hash the hash function to use
+ */
+ explicit EMSA1(HashFunction* hash) : m_hash(hash) {}
+
+ EMSA* clone() override;
+
+ std::string name() const override;
+
+ AlgorithmIdentifier config_for_x509(const Private_Key& key,
+ const std::string& cert_hash_name) const override;
+ private:
+ size_t hash_output_length() const { return m_hash->output_length(); }
+
+ void update(const uint8_t[], size_t) override;
+ secure_vector<uint8_t> raw_data() override;
+
+ secure_vector<uint8_t> encoding_of(const secure_vector<uint8_t>& msg,
+ size_t output_bits,
+ RandomNumberGenerator& rng) override;
+
+ bool verify(const secure_vector<uint8_t>& coded,
+ const secure_vector<uint8_t>& raw,
+ size_t key_bits) override;
+
+ std::unique_ptr<HashFunction> m_hash;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/pk_pad/emsa1/info.txt b/comm/third_party/botan/src/lib/pk_pad/emsa1/info.txt
new file mode 100644
index 0000000000..5b5bf1f6b0
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pk_pad/emsa1/info.txt
@@ -0,0 +1,3 @@
+<defines>
+EMSA1 -> 20131128
+</defines>
diff --git a/comm/third_party/botan/src/lib/pk_pad/emsa_pkcs1/emsa_pkcs1.cpp b/comm/third_party/botan/src/lib/pk_pad/emsa_pkcs1/emsa_pkcs1.cpp
new file mode 100644
index 0000000000..85556a39ed
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pk_pad/emsa_pkcs1/emsa_pkcs1.cpp
@@ -0,0 +1,166 @@
+/*
+* PKCS #1 v1.5 signature padding
+* (C) 1999-2008 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/emsa_pkcs1.h>
+#include <botan/hash_id.h>
+#include <botan/exceptn.h>
+#include <botan/pk_keys.h>
+#include <botan/internal/padding.h>
+
+namespace Botan {
+
+namespace {
+
+secure_vector<uint8_t> emsa3_encoding(const secure_vector<uint8_t>& msg,
+ size_t output_bits,
+ const uint8_t hash_id[],
+ size_t hash_id_length)
+ {
+ size_t output_length = output_bits / 8;
+ if(output_length < hash_id_length + msg.size() + 10)
+ throw Encoding_Error("emsa3_encoding: Output length is too small");
+
+ secure_vector<uint8_t> T(output_length);
+ const size_t P_LENGTH = output_length - msg.size() - hash_id_length - 2;
+
+ T[0] = 0x01;
+ set_mem(&T[1], P_LENGTH, 0xFF);
+ T[P_LENGTH+1] = 0x00;
+
+ if(hash_id_length > 0)
+ {
+ BOTAN_ASSERT_NONNULL(hash_id);
+ buffer_insert(T, P_LENGTH+2, hash_id, hash_id_length);
+ }
+
+ buffer_insert(T, output_length-msg.size(), msg.data(), msg.size());
+ return T;
+ }
+
+}
+
+void EMSA_PKCS1v15::update(const uint8_t input[], size_t length)
+ {
+ m_hash->update(input, length);
+ }
+
+secure_vector<uint8_t> EMSA_PKCS1v15::raw_data()
+ {
+ return m_hash->final();
+ }
+
+secure_vector<uint8_t>
+EMSA_PKCS1v15::encoding_of(const secure_vector<uint8_t>& msg,
+ size_t output_bits,
+ RandomNumberGenerator&)
+ {
+ if(msg.size() != m_hash->output_length())
+ throw Encoding_Error("EMSA_PKCS1v15::encoding_of: Bad input length");
+
+ return emsa3_encoding(msg, output_bits,
+ m_hash_id.data(), m_hash_id.size());
+ }
+
+bool EMSA_PKCS1v15::verify(const secure_vector<uint8_t>& coded,
+ const secure_vector<uint8_t>& raw,
+ size_t key_bits)
+ {
+ if(raw.size() != m_hash->output_length())
+ return false;
+
+ try
+ {
+ return (coded == emsa3_encoding(raw, key_bits,
+ m_hash_id.data(), m_hash_id.size()));
+ }
+ catch(...)
+ {
+ return false;
+ }
+ }
+
+AlgorithmIdentifier EMSA_PKCS1v15::config_for_x509(const Private_Key& key,
+ const std::string& cert_hash_name) const
+ {
+ if(cert_hash_name != m_hash->name())
+ throw Invalid_Argument("Hash function from opts and hash_fn argument"
+ " need to be identical");
+ // check that the signature algorithm and the padding scheme fit
+ if(!sig_algo_and_pad_ok(key.algo_name(), "EMSA3"))
+ {
+ throw Invalid_Argument("Encoding scheme with canonical name EMSA3"
+ " not supported for signature algorithm " + key.algo_name());
+ }
+
+ // for RSA PKCSv1.5 parameters "SHALL" be NULL
+
+ const OID oid = OID::from_string(key.algo_name() + "/" + name());
+ return AlgorithmIdentifier(oid, AlgorithmIdentifier::USE_NULL_PARAM);
+ }
+
+EMSA_PKCS1v15::EMSA_PKCS1v15(HashFunction* hash) : m_hash(hash)
+ {
+ m_hash_id = pkcs_hash_id(m_hash->name());
+ }
+
+EMSA_PKCS1v15_Raw::EMSA_PKCS1v15_Raw(const std::string& hash_algo)
+ {
+ if(!hash_algo.empty())
+ {
+ m_hash_id = pkcs_hash_id(hash_algo);
+ std::unique_ptr<HashFunction> hash(HashFunction::create_or_throw(hash_algo));
+ m_hash_name = hash->name();
+ m_hash_output_len = hash->output_length();
+ }
+ else
+ {
+ m_hash_output_len = 0;
+ }
+ }
+
+void EMSA_PKCS1v15_Raw::update(const uint8_t input[], size_t length)
+ {
+ m_message += std::make_pair(input, length);
+ }
+
+secure_vector<uint8_t> EMSA_PKCS1v15_Raw::raw_data()
+ {
+ secure_vector<uint8_t> 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;
+ }
+
+secure_vector<uint8_t>
+EMSA_PKCS1v15_Raw::encoding_of(const secure_vector<uint8_t>& msg,
+ size_t output_bits,
+ RandomNumberGenerator&)
+ {
+ return emsa3_encoding(msg, output_bits, m_hash_id.data(), m_hash_id.size());
+ }
+
+bool EMSA_PKCS1v15_Raw::verify(const secure_vector<uint8_t>& coded,
+ const secure_vector<uint8_t>& raw,
+ size_t key_bits)
+ {
+ if(m_hash_output_len > 0 && raw.size() != m_hash_output_len)
+ return false;
+
+ try
+ {
+ return (coded == emsa3_encoding(raw, key_bits, m_hash_id.data(), m_hash_id.size()));
+ }
+ catch(...)
+ {
+ return false;
+ }
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/pk_pad/emsa_pkcs1/emsa_pkcs1.h b/comm/third_party/botan/src/lib/pk_pad/emsa_pkcs1/emsa_pkcs1.h
new file mode 100644
index 0000000000..5b9cf7aea7
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pk_pad/emsa_pkcs1/emsa_pkcs1.h
@@ -0,0 +1,94 @@
+/*
+* 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 <botan/emsa.h>
+#include <botan/hash.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(emsa_pkcs1.h)
+
+namespace Botan {
+
+/**
+* PKCS #1 v1.5 signature padding
+* aka PKCS #1 block type 1
+* aka EMSA3 from IEEE 1363
+*/
+class BOTAN_PUBLIC_API(2,0) EMSA_PKCS1v15 final : public EMSA
+ {
+ public:
+ /**
+ * @param hash the hash function to use
+ */
+ explicit EMSA_PKCS1v15(HashFunction* hash);
+
+ EMSA* clone() override { return new EMSA_PKCS1v15(m_hash->clone()); }
+
+ void update(const uint8_t[], size_t) override;
+
+ secure_vector<uint8_t> raw_data() override;
+
+ secure_vector<uint8_t> encoding_of(const secure_vector<uint8_t>&, size_t,
+ RandomNumberGenerator& rng) override;
+
+ bool verify(const secure_vector<uint8_t>&, const secure_vector<uint8_t>&,
+ size_t) override;
+
+ std::string name() const override
+ { return "EMSA3(" + m_hash->name() + ")"; }
+
+ AlgorithmIdentifier config_for_x509(const Private_Key& key,
+ const std::string& cert_hash_name) const override;
+ private:
+ std::unique_ptr<HashFunction> m_hash;
+ std::vector<uint8_t> 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 BOTAN_PUBLIC_API(2,0) EMSA_PKCS1v15_Raw final : public EMSA
+ {
+ public:
+ EMSA* clone() override { return new EMSA_PKCS1v15_Raw(); }
+
+ void update(const uint8_t[], size_t) override;
+
+ secure_vector<uint8_t> raw_data() override;
+
+ secure_vector<uint8_t> encoding_of(const secure_vector<uint8_t>&, size_t,
+ RandomNumberGenerator& rng) override;
+
+ bool verify(const secure_vector<uint8_t>&, const secure_vector<uint8_t>&,
+ size_t) override;
+
+ /**
+ * @param hash_algo if non-empty, the digest id for that hash is
+ * included in the signature.
+ */
+ EMSA_PKCS1v15_Raw(const std::string& hash_algo = "");
+
+ std::string name() const override
+ {
+ if(m_hash_name.empty()) return "EMSA3(Raw)";
+ else return "EMSA3(Raw," + m_hash_name + ")";
+ }
+
+ private:
+ size_t m_hash_output_len = 0;
+ std::string m_hash_name;
+ std::vector<uint8_t> m_hash_id;
+ secure_vector<uint8_t> m_message;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/pk_pad/emsa_pkcs1/info.txt b/comm/third_party/botan/src/lib/pk_pad/emsa_pkcs1/info.txt
new file mode 100644
index 0000000000..b70f4e2445
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pk_pad/emsa_pkcs1/info.txt
@@ -0,0 +1,7 @@
+<defines>
+EMSA_PKCS1 -> 20140118
+</defines>
+
+<requires>
+hash_id
+</requires>
diff --git a/comm/third_party/botan/src/lib/pk_pad/emsa_pssr/info.txt b/comm/third_party/botan/src/lib/pk_pad/emsa_pssr/info.txt
new file mode 100644
index 0000000000..f514936c37
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pk_pad/emsa_pssr/info.txt
@@ -0,0 +1,7 @@
+<defines>
+EMSA_PSSR -> 20131128
+</defines>
+
+<requires>
+mgf1
+</requires>
diff --git a/comm/third_party/botan/src/lib/pk_pad/emsa_pssr/pssr.cpp b/comm/third_party/botan/src/lib/pk_pad/emsa_pssr/pssr.cpp
new file mode 100644
index 0000000000..652a7628ba
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pk_pad/emsa_pssr/pssr.cpp
@@ -0,0 +1,291 @@
+/*
+* PSSR
+* (C) 1999-2007,2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/pssr.h>
+#include <botan/exceptn.h>
+#include <botan/rng.h>
+#include <botan/mgf1.h>
+#include <botan/internal/bit_ops.h>
+#include <botan/der_enc.h>
+#include <botan/pk_keys.h>
+#include <botan/internal/padding.h>
+
+namespace Botan {
+
+namespace {
+
+/*
+* PSSR Encode Operation
+*/
+secure_vector<uint8_t> pss_encode(HashFunction& hash,
+ const secure_vector<uint8_t>& msg,
+ const secure_vector<uint8_t>& salt,
+ size_t output_bits)
+ {
+ const size_t HASH_SIZE = hash.output_length();
+ const size_t SALT_SIZE = salt.size();
+
+ 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 = (output_bits + 7) / 8;
+
+ for(size_t i = 0; i != 8; ++i)
+ hash.update(0);
+ hash.update(msg);
+ hash.update(salt);
+ secure_vector<uint8_t> H = hash.final();
+
+ secure_vector<uint8_t> EM(output_length);
+
+ EM[output_length - HASH_SIZE - SALT_SIZE - 2] = 0x01;
+ buffer_insert(EM, output_length - 1 - HASH_SIZE - SALT_SIZE, salt);
+ mgf1_mask(hash, H.data(), HASH_SIZE, EM.data(), output_length - HASH_SIZE - 1);
+ EM[0] &= 0xFF >> (8 * ((output_bits + 7) / 8) - output_bits);
+ buffer_insert(EM, output_length - 1 - HASH_SIZE, H);
+ EM[output_length-1] = 0xBC;
+ return EM;
+ }
+
+bool pss_verify(HashFunction& hash,
+ const secure_vector<uint8_t>& pss_repr,
+ const secure_vector<uint8_t>& message_hash,
+ size_t key_bits,
+ size_t* out_salt_size)
+ {
+ const size_t HASH_SIZE = hash.output_length();
+ const size_t KEY_BYTES = (key_bits + 7) / 8;
+
+ 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;
+
+ secure_vector<uint8_t> coded = pss_repr;
+ if(coded.size() < KEY_BYTES)
+ {
+ secure_vector<uint8_t> temp(KEY_BYTES);
+ buffer_insert(temp, KEY_BYTES - coded.size(), 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;
+
+ for(size_t j = 0; j != 8; ++j)
+ hash.update(0);
+ hash.update(message_hash);
+ hash.update(&DB[salt_offset], salt_size);
+
+ const secure_vector<uint8_t> H2 = hash.final();
+
+ const bool ok = constant_time_compare(H, H2.data(), HASH_SIZE);
+
+ if(out_salt_size && ok)
+ *out_salt_size = salt_size;
+
+ return ok;
+ }
+
+}
+
+PSSR::PSSR(HashFunction* h) :
+ m_hash(h),
+ m_salt_size(m_hash->output_length()),
+ m_required_salt_len(false)
+ {
+ }
+
+PSSR::PSSR(HashFunction* h, size_t salt_size) :
+ m_hash(h),
+ 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
+*/
+secure_vector<uint8_t> PSSR::raw_data()
+ {
+ return m_hash->final();
+ }
+
+secure_vector<uint8_t> PSSR::encoding_of(const secure_vector<uint8_t>& msg,
+ size_t output_bits,
+ RandomNumberGenerator& rng)
+ {
+ const secure_vector<uint8_t> salt = rng.random_vec(m_salt_size);
+ return pss_encode(*m_hash, msg, salt, output_bits);
+ }
+
+/*
+* PSSR Decode/Verify Operation
+*/
+bool PSSR::verify(const secure_vector<uint8_t>& coded,
+ const secure_vector<uint8_t>& 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;
+ }
+
+EMSA* PSSR::clone()
+ {
+ return new PSSR(m_hash->clone(), m_salt_size);
+ }
+
+std::string PSSR::name() const
+ {
+ return "EMSA4(" + m_hash->name() + ",MGF1," + std::to_string(m_salt_size) + ")";
+ }
+
+AlgorithmIdentifier PSSR::config_for_x509(const Private_Key& key,
+ const std::string& cert_hash_name) const
+ {
+ if(cert_hash_name != m_hash->name())
+ throw Invalid_Argument("Hash function from opts and hash_fn argument"
+ " need to be identical");
+ // check that the signature algorithm and the padding scheme fit
+ if(!sig_algo_and_pad_ok(key.algo_name(), "EMSA4"))
+ {
+ throw Invalid_Argument("Encoding scheme with canonical name EMSA4"
+ " not supported for signature algorithm " + key.algo_name());
+ }
+
+ const AlgorithmIdentifier hash_id(cert_hash_name, AlgorithmIdentifier::USE_NULL_PARAM);
+ const AlgorithmIdentifier mgf_id("MGF1", hash_id.BER_encode());
+
+ std::vector<uint8_t> parameters;
+ DER_Encoder(parameters)
+ .start_cons(SEQUENCE)
+ .start_cons(ASN1_Tag(0), CONTEXT_SPECIFIC).encode(hash_id).end_cons()
+ .start_cons(ASN1_Tag(1), CONTEXT_SPECIFIC).encode(mgf_id).end_cons()
+ .start_cons(ASN1_Tag(2), CONTEXT_SPECIFIC).encode(m_salt_size).end_cons()
+ .start_cons(ASN1_Tag(3), CONTEXT_SPECIFIC).encode(size_t(1)).end_cons() // trailer field
+ .end_cons();
+
+ // hardcoded as RSA is the only valid algorithm for EMSA4 at the moment
+ return AlgorithmIdentifier("RSA/EMSA4", parameters);
+ }
+
+PSSR_Raw::PSSR_Raw(HashFunction* h) :
+ m_hash(h),
+ m_salt_size(m_hash->output_length()),
+ m_required_salt_len(false)
+ {
+ }
+
+PSSR_Raw::PSSR_Raw(HashFunction* h, size_t salt_size) :
+ m_hash(h),
+ 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
+*/
+secure_vector<uint8_t> PSSR_Raw::raw_data()
+ {
+ secure_vector<uint8_t> 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;
+ }
+
+secure_vector<uint8_t> PSSR_Raw::encoding_of(const secure_vector<uint8_t>& msg,
+ size_t output_bits,
+ RandomNumberGenerator& rng)
+ {
+ secure_vector<uint8_t> 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 secure_vector<uint8_t>& coded,
+ const secure_vector<uint8_t>& 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;
+ }
+
+EMSA* PSSR_Raw::clone()
+ {
+ return new PSSR_Raw(m_hash->clone(), m_salt_size);
+ }
+
+std::string PSSR_Raw::name() const
+ {
+ return "PSSR_Raw(" + m_hash->name() + ",MGF1," + std::to_string(m_salt_size) + ")";
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/pk_pad/emsa_pssr/pssr.h b/comm/third_party/botan/src/lib/pk_pad/emsa_pssr/pssr.h
new file mode 100644
index 0000000000..47efacb518
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pk_pad/emsa_pssr/pssr.h
@@ -0,0 +1,103 @@
+/*
+* 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 <botan/emsa.h>
+#include <botan/hash.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(pssr.h)
+
+namespace Botan {
+
+/**
+* PSSR (called EMSA4 in IEEE 1363 and in old versions of the library)
+*/
+class BOTAN_PUBLIC_API(2,0) PSSR final : public EMSA
+ {
+ public:
+
+ /**
+ * @param hash the hash function to use
+ */
+ explicit PSSR(HashFunction* hash);
+
+ /**
+ * @param hash the hash function to use
+ * @param salt_size the size of the salt to use in bytes
+ */
+ PSSR(HashFunction* hash, size_t salt_size);
+
+ EMSA* clone() override;
+
+ std::string name() const override;
+
+ AlgorithmIdentifier config_for_x509(const Private_Key& key,
+ const std::string& cert_hash_name) const override;
+ private:
+ void update(const uint8_t input[], size_t length) override;
+
+ secure_vector<uint8_t> raw_data() override;
+
+ secure_vector<uint8_t> encoding_of(const secure_vector<uint8_t>& msg,
+ size_t output_bits,
+ RandomNumberGenerator& rng) override;
+
+ bool verify(const secure_vector<uint8_t>& coded,
+ const secure_vector<uint8_t>& raw,
+ size_t key_bits) override;
+
+ std::unique_ptr<HashFunction> m_hash;
+ size_t m_salt_size;
+ bool m_required_salt_len;
+ };
+
+/**
+* PSSR_Raw
+* This accepts a pre-hashed buffer
+*/
+class BOTAN_PUBLIC_API(2,3) PSSR_Raw final : public EMSA
+ {
+ public:
+
+ /**
+ * @param hash the hash function to use
+ */
+ explicit PSSR_Raw(HashFunction* hash);
+
+ /**
+ * @param hash the hash function to use
+ * @param salt_size the size of the salt to use in bytes
+ */
+ PSSR_Raw(HashFunction* hash, size_t salt_size);
+
+ EMSA* clone() override;
+
+ std::string name() const override;
+ private:
+ void update(const uint8_t input[], size_t length) override;
+
+ secure_vector<uint8_t> raw_data() override;
+
+ secure_vector<uint8_t> encoding_of(const secure_vector<uint8_t>& msg,
+ size_t output_bits,
+ RandomNumberGenerator& rng) override;
+
+ bool verify(const secure_vector<uint8_t>& coded,
+ const secure_vector<uint8_t>& raw,
+ size_t key_bits) override;
+
+ std::unique_ptr<HashFunction> m_hash;
+ secure_vector<uint8_t> m_msg;
+ size_t m_salt_size;
+ bool m_required_salt_len;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/pk_pad/emsa_raw/emsa_raw.cpp b/comm/third_party/botan/src/lib/pk_pad/emsa_raw/emsa_raw.cpp
new file mode 100644
index 0000000000..0ac11dc5ad
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pk_pad/emsa_raw/emsa_raw.cpp
@@ -0,0 +1,92 @@
+/*
+* EMSA-Raw
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/emsa_raw.h>
+#include <botan/exceptn.h>
+
+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
+*/
+secure_vector<uint8_t> 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");
+
+ secure_vector<uint8_t> output;
+ std::swap(m_message, output);
+ return output;
+ }
+
+/*
+* EMSA-Raw Encode Operation
+*/
+secure_vector<uint8_t>
+EMSA_Raw::encoding_of(const secure_vector<uint8_t>& msg,
+ size_t,
+ RandomNumberGenerator&)
+ {
+ 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 secure_vector<uint8_t>& coded,
+ const secure_vector<uint8_t>& raw,
+ size_t)
+ {
+ 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(!constant_time_compare(coded.data(), raw.data() + leading_zeros_expected, coded.size()))
+ same_modulo_leading_zeros = false;
+
+ return same_modulo_leading_zeros;
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/pk_pad/emsa_raw/emsa_raw.h b/comm/third_party/botan/src/lib/pk_pad/emsa_raw/emsa_raw.h
new file mode 100644
index 0000000000..dc01b94a2d
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pk_pad/emsa_raw/emsa_raw.h
@@ -0,0 +1,47 @@
+/*
+* 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 <botan/emsa.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(emsa_raw.h)
+
+namespace Botan {
+
+/**
+* EMSA-Raw - sign inputs directly
+* Don't use this unless you know what you are doing.
+*/
+class BOTAN_PUBLIC_API(2,0) EMSA_Raw final : public EMSA
+ {
+ public:
+ EMSA* clone() override { return new EMSA_Raw(); }
+
+ explicit EMSA_Raw(size_t expected_hash_size = 0) :
+ m_expected_size(expected_hash_size) {}
+
+ std::string name() const override;
+ private:
+ void update(const uint8_t[], size_t) override;
+ secure_vector<uint8_t> raw_data() override;
+
+ secure_vector<uint8_t> encoding_of(const secure_vector<uint8_t>&, size_t,
+ RandomNumberGenerator&) override;
+
+ bool verify(const secure_vector<uint8_t>&,
+ const secure_vector<uint8_t>&,
+ size_t) override;
+
+ const size_t m_expected_size;
+ secure_vector<uint8_t> m_message;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/pk_pad/emsa_raw/info.txt b/comm/third_party/botan/src/lib/pk_pad/emsa_raw/info.txt
new file mode 100644
index 0000000000..ffd3cbe3b3
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pk_pad/emsa_raw/info.txt
@@ -0,0 +1,3 @@
+<defines>
+EMSA_RAW -> 20131128
+</defines>
diff --git a/comm/third_party/botan/src/lib/pk_pad/emsa_x931/emsa_x931.cpp b/comm/third_party/botan/src/lib/pk_pad/emsa_x931/emsa_x931.cpp
new file mode 100644
index 0000000000..b1f698f86d
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pk_pad/emsa_x931/emsa_x931.cpp
@@ -0,0 +1,102 @@
+/*
+* EMSA_X931
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/emsa_x931.h>
+#include <botan/exceptn.h>
+#include <botan/hash_id.h>
+
+namespace Botan {
+
+namespace {
+
+secure_vector<uint8_t> emsa2_encoding(const secure_vector<uint8_t>& msg,
+ size_t output_bits,
+ const secure_vector<uint8_t>& empty_hash,
+ uint8_t hash_id)
+ {
+ const size_t HASH_SIZE = empty_hash.size();
+
+ 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);
+
+ secure_vector<uint8_t> output(output_length);
+
+ output[0] = (empty_input ? 0x4B : 0x6B);
+ output[output_length - 3 - HASH_SIZE] = 0xBA;
+ set_mem(&output[1], output_length - 4 - HASH_SIZE, 0xBB);
+ buffer_insert(output, output_length - (HASH_SIZE + 2), msg.data(), msg.size());
+ output[output_length-2] = hash_id;
+ output[output_length-1] = 0xCC;
+
+ return output;
+ }
+
+}
+
+std::string EMSA_X931::name() const
+ {
+ return "EMSA2(" + m_hash->name() + ")";
+ }
+
+void EMSA_X931::update(const uint8_t input[], size_t length)
+ {
+ m_hash->update(input, length);
+ }
+
+secure_vector<uint8_t> EMSA_X931::raw_data()
+ {
+ return m_hash->final();
+ }
+
+/*
+* EMSA_X931 Encode Operation
+*/
+secure_vector<uint8_t> EMSA_X931::encoding_of(const secure_vector<uint8_t>& msg,
+ size_t output_bits,
+ RandomNumberGenerator&)
+ {
+ return emsa2_encoding(msg, output_bits, m_empty_hash, m_hash_id);
+ }
+
+/*
+* EMSA_X931 Verify Operation
+*/
+bool EMSA_X931::verify(const secure_vector<uint8_t>& coded,
+ const secure_vector<uint8_t>& 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(HashFunction* hash) : m_hash(hash)
+ {
+ m_empty_hash = m_hash->final();
+
+ m_hash_id = ieee1363_hash_id(hash->name());
+
+ if(!m_hash_id)
+ throw Encoding_Error("EMSA_X931 no hash identifier for " + hash->name());
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/pk_pad/emsa_x931/emsa_x931.h b/comm/third_party/botan/src/lib/pk_pad/emsa_x931/emsa_x931.h
new file mode 100644
index 0000000000..a2fef0468a
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pk_pad/emsa_x931/emsa_x931.h
@@ -0,0 +1,52 @@
+/*
+* 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 <botan/emsa.h>
+#include <botan/hash.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(emsa_x931.h)
+
+namespace Botan {
+
+/**
+* EMSA from X9.31 (EMSA2 in IEEE 1363)
+* Useful for Rabin-Williams, also sometimes used with RSA in
+* odd protocols.
+*/
+class BOTAN_PUBLIC_API(2,0) EMSA_X931 final : public EMSA
+ {
+ public:
+ /**
+ * @param hash the hash function to use
+ */
+ explicit EMSA_X931(HashFunction* hash);
+
+ EMSA* clone() override { return new EMSA_X931(m_hash->clone()); }
+
+ std::string name() const override;
+
+ private:
+ void update(const uint8_t[], size_t) override;
+ secure_vector<uint8_t> raw_data() override;
+
+ secure_vector<uint8_t> encoding_of(const secure_vector<uint8_t>&, size_t,
+ RandomNumberGenerator& rng) override;
+
+ bool verify(const secure_vector<uint8_t>&, const secure_vector<uint8_t>&,
+ size_t) override;
+
+ secure_vector<uint8_t> m_empty_hash;
+ std::unique_ptr<HashFunction> m_hash;
+ uint8_t m_hash_id;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/pk_pad/emsa_x931/info.txt b/comm/third_party/botan/src/lib/pk_pad/emsa_x931/info.txt
new file mode 100644
index 0000000000..d3c5ca76ea
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pk_pad/emsa_x931/info.txt
@@ -0,0 +1,7 @@
+<defines>
+EMSA_X931 -> 20140118
+</defines>
+
+<requires>
+hash_id
+</requires>
diff --git a/comm/third_party/botan/src/lib/pk_pad/hash_id/hash_id.cpp b/comm/third_party/botan/src/lib/pk_pad/hash_id/hash_id.cpp
new file mode 100644
index 0000000000..ec317f9692
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pk_pad/hash_id/hash_id.cpp
@@ -0,0 +1,163 @@
+/*
+* Hash Function Identification
+* (C) 1999-2008 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/hash_id.h>
+#include <botan/exceptn.h>
+
+namespace Botan {
+
+namespace {
+
+const uint8_t MD5_PKCS_ID[] = {
+0x30, 0x20, 0x30, 0x0C, 0x06, 0x08, 0x2A, 0x86, 0x48, 0x86,
+0xF7, 0x0D, 0x02, 0x05, 0x05, 0x00, 0x04, 0x10 };
+
+const uint8_t RIPEMD_160_PKCS_ID[] = {
+0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2B, 0x24, 0x03, 0x02,
+0x01, 0x05, 0x00, 0x04, 0x14 };
+
+const uint8_t SHA_160_PKCS_ID[] = {
+0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2B, 0x0E, 0x03, 0x02,
+0x1A, 0x05, 0x00, 0x04, 0x14 };
+
+const uint8_t SHA_224_PKCS_ID[] = {
+0x30, 0x2D, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
+0x65, 0x03, 0x04, 0x02, 0x04, 0x05, 0x00, 0x04, 0x1C };
+
+const uint8_t SHA_256_PKCS_ID[] = {
+0x30, 0x31, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
+0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20 };
+
+const uint8_t SHA_384_PKCS_ID[] = {
+0x30, 0x41, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
+0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 0x04, 0x30 };
+
+const uint8_t SHA_512_PKCS_ID[] = {
+0x30, 0x51, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
+0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0x04, 0x40 };
+
+const uint8_t SHA_512_256_PKCS_ID[] = {
+0x30, 0x31, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
+0x65, 0x03, 0x04, 0x02, 0x06, 0x05, 0x00, 0x04, 0x20 };
+
+const uint8_t SHA3_224_PKCS_ID[] = {
+0x30, 0x2D, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65,
+0x03, 0x04, 0x02, 0x07, 0x05, 0x00, 0x04, 0x1C };
+
+const uint8_t SHA3_256_PKCS_ID[] = {
+0x30, 0x31, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65,
+0x03, 0x04, 0x02, 0x08, 0x05, 0x00, 0x04, 0x20 };
+
+const uint8_t SHA3_384_PKCS_ID[] = {
+0x30, 0x41, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65,
+0x03, 0x04, 0x02, 0x09, 0x05, 0x00, 0x04, 0x30 };
+
+const uint8_t SHA3_512_PKCS_ID[] = {
+0x30, 0x51, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65,
+0x03, 0x04, 0x02, 0x0A, 0x05, 0x00, 0x04, 0x40 };
+
+const uint8_t SM3_PKCS_ID[] = {
+0x30, 0x30, 0x30, 0x0C, 0x06, 0x08, 0x2A, 0x81, 0x1C, 0xCF,
+0x55, 0x01, 0x83, 0x11, 0x05, 0x00, 0x04, 0x20,
+};
+
+const uint8_t TIGER_PKCS_ID[] = {
+0x30, 0x29, 0x30, 0x0D, 0x06, 0x09, 0x2B, 0x06, 0x01, 0x04,
+0x01, 0xDA, 0x47, 0x0C, 0x02, 0x05, 0x00, 0x04, 0x18 };
+
+}
+
+/*
+* HashID as specified by PKCS
+*/
+std::vector<uint8_t> pkcs_hash_id(const std::string& name)
+ {
+ // Special case for SSL/TLS RSA signatures
+ if(name == "Parallel(MD5,SHA-160)")
+ return std::vector<uint8_t>();
+
+ // If you add a value to this function, also update test_hash_id.cpp
+
+ if(name == "MD5")
+ return std::vector<uint8_t>(MD5_PKCS_ID,
+ MD5_PKCS_ID + sizeof(MD5_PKCS_ID));
+
+ if(name == "RIPEMD-160")
+ return std::vector<uint8_t>(RIPEMD_160_PKCS_ID,
+ RIPEMD_160_PKCS_ID + sizeof(RIPEMD_160_PKCS_ID));
+
+ if(name == "SHA-160" || name == "SHA-1" || name == "SHA1")
+ return std::vector<uint8_t>(SHA_160_PKCS_ID,
+ SHA_160_PKCS_ID + sizeof(SHA_160_PKCS_ID));
+
+ if(name == "SHA-224")
+ return std::vector<uint8_t>(SHA_224_PKCS_ID,
+ SHA_224_PKCS_ID + sizeof(SHA_224_PKCS_ID));
+
+ if(name == "SHA-256")
+ return std::vector<uint8_t>(SHA_256_PKCS_ID,
+ SHA_256_PKCS_ID + sizeof(SHA_256_PKCS_ID));
+
+ if(name == "SHA-384")
+ return std::vector<uint8_t>(SHA_384_PKCS_ID,
+ SHA_384_PKCS_ID + sizeof(SHA_384_PKCS_ID));
+
+ if(name == "SHA-512")
+ return std::vector<uint8_t>(SHA_512_PKCS_ID,
+ SHA_512_PKCS_ID + sizeof(SHA_512_PKCS_ID));
+
+ if(name == "SHA-512-256")
+ return std::vector<uint8_t>(SHA_512_256_PKCS_ID,
+ SHA_512_256_PKCS_ID + sizeof(SHA_512_256_PKCS_ID));
+
+ if(name == "SHA-3(224)")
+ return std::vector<uint8_t>(SHA3_224_PKCS_ID,
+ SHA3_224_PKCS_ID + sizeof(SHA3_224_PKCS_ID));
+
+ if(name == "SHA-3(256)")
+ return std::vector<uint8_t>(SHA3_256_PKCS_ID,
+ SHA3_256_PKCS_ID + sizeof(SHA3_256_PKCS_ID));
+
+ if(name == "SHA-3(384)")
+ return std::vector<uint8_t>(SHA3_384_PKCS_ID,
+ SHA3_384_PKCS_ID + sizeof(SHA3_384_PKCS_ID));
+
+ if(name == "SHA-3(512)")
+ return std::vector<uint8_t>(SHA3_512_PKCS_ID,
+ SHA3_512_PKCS_ID + sizeof(SHA3_512_PKCS_ID));
+
+ if(name == "SM3")
+ return std::vector<uint8_t>(SM3_PKCS_ID, SM3_PKCS_ID + sizeof(SM3_PKCS_ID));
+
+ if(name == "Tiger(24,3)")
+ return std::vector<uint8_t>(TIGER_PKCS_ID,
+ TIGER_PKCS_ID + sizeof(TIGER_PKCS_ID));
+
+ throw Invalid_Argument("No PKCS #1 identifier for " + name);
+ }
+
+/*
+* HashID as specified by IEEE 1363/X9.31
+*/
+uint8_t ieee1363_hash_id(const std::string& name)
+ {
+ if(name == "SHA-160" || name == "SHA-1" || name == "SHA1")
+ return 0x33;
+
+ if(name == "SHA-224") return 0x38;
+ if(name == "SHA-256") return 0x34;
+ if(name == "SHA-384") return 0x36;
+ if(name == "SHA-512") return 0x35;
+
+ if(name == "RIPEMD-160") return 0x31;
+
+ if(name == "Whirlpool") return 0x37;
+
+ return 0;
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/pk_pad/hash_id/hash_id.h b/comm/third_party/botan/src/lib/pk_pad/hash_id/hash_id.h
new file mode 100644
index 0000000000..75c86c0c65
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pk_pad/hash_id/hash_id.h
@@ -0,0 +1,34 @@
+/*
+* Hash Function Identification
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_HASHID_H_
+#define BOTAN_HASHID_H_
+
+#include <botan/secmem.h>
+#include <string>
+
+namespace Botan {
+
+/**
+* Return the PKCS #1 hash identifier
+* @see RFC 3447 section 9.2
+* @param hash_name the name of the hash function
+* @return uint8_t sequence identifying the hash
+* @throw Invalid_Argument if the hash has no known PKCS #1 hash id
+*/
+BOTAN_PUBLIC_API(2,0) std::vector<uint8_t> pkcs_hash_id(const std::string& hash_name);
+
+/**
+* Return the IEEE 1363 hash identifier
+* @param hash_name the name of the hash function
+* @return uint8_t code identifying the hash, or 0 if not known
+*/
+BOTAN_PUBLIC_API(2,0) uint8_t ieee1363_hash_id(const std::string& hash_name);
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/pk_pad/hash_id/info.txt b/comm/third_party/botan/src/lib/pk_pad/hash_id/info.txt
new file mode 100644
index 0000000000..8cd930a9fe
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pk_pad/hash_id/info.txt
@@ -0,0 +1,3 @@
+<defines>
+HASH_ID -> 20131128
+</defines>
diff --git a/comm/third_party/botan/src/lib/pk_pad/info.txt b/comm/third_party/botan/src/lib/pk_pad/info.txt
new file mode 100644
index 0000000000..e8fb00c8e3
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pk_pad/info.txt
@@ -0,0 +1,18 @@
+<defines>
+PK_PADDING -> 20131128
+</defines>
+
+<requires>
+asn1
+rng
+pubkey
+</requires>
+
+<header:internal>
+padding.h
+</header:internal>
+
+<header:public>
+eme.h
+emsa.h
+</header:public>
diff --git a/comm/third_party/botan/src/lib/pk_pad/iso9796/info.txt b/comm/third_party/botan/src/lib/pk_pad/iso9796/info.txt
new file mode 100644
index 0000000000..92bdb3c7d2
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pk_pad/iso9796/info.txt
@@ -0,0 +1,9 @@
+<defines>
+ISO_9796 -> 20161121
+</defines>
+
+<requires>
+mgf1
+hash_id
+</requires>
+
diff --git a/comm/third_party/botan/src/lib/pk_pad/iso9796/iso9796.cpp b/comm/third_party/botan/src/lib/pk_pad/iso9796/iso9796.cpp
new file mode 100644
index 0000000000..9fdb87e3b5
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pk_pad/iso9796/iso9796.cpp
@@ -0,0 +1,322 @@
+/*
+ * 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 <botan/iso9796.h>
+#include <botan/rng.h>
+#include <botan/exceptn.h>
+#include <botan/mgf1.h>
+#include <botan/hash_id.h>
+#include <botan/internal/bit_ops.h>
+#include <botan/internal/ct_utils.h>
+
+namespace Botan {
+
+namespace {
+
+secure_vector<uint8_t> iso9796_encoding(const secure_vector<uint8_t>& msg,
+ size_t output_bits,
+ std::unique_ptr<HashFunction>& hash,
+ size_t SALT_SIZE,
+ bool implicit,
+ RandomNumberGenerator& rng)
+ {
+ const size_t output_length = (output_bits + 7) / 8;
+
+ //set trailer length
+ size_t tLength = 1;
+ if(!implicit)
+ {
+ tLength = 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 msg2 the unrecoverable message part.
+ secure_vector<uint8_t> msg1;
+ secure_vector<uint8_t> msg2;
+ if(msg.size() > capacity)
+ {
+ msg1 = secure_vector<uint8_t>(msg.begin(), msg.begin() + capacity);
+ msg2 = secure_vector<uint8_t>(msg.begin() + capacity, msg.end());
+ hash->update(msg2);
+ }
+ else
+ {
+ msg1 = msg;
+ }
+ msg2 = hash->final();
+
+ //compute H(C||msg1 ||H(msg2)||S)
+ const size_t msgLength = msg1.size();
+ secure_vector<uint8_t> salt = rng.random_vec(SALT_SIZE);
+ hash->update_be(static_cast<uint64_t>(msgLength) * 8);
+ hash->update(msg1);
+ hash->update(msg2);
+ hash->update(salt);
+ secure_vector<uint8_t> H = hash->final();
+
+ secure_vector<uint8_t> EM(output_length);
+
+ //compute message offset.
+ const size_t offset = output_length - HASH_SIZE - SALT_SIZE - tLength - msgLength - 1;
+
+ //insert message border (0x01), msg1 and salt into the output buffer
+ EM[offset] = 0x01;
+ buffer_insert(EM, offset + 1, msg1);
+ buffer_insert(EM, offset + 1 + msgLength, salt);
+
+ //apply mask
+ mgf1_mask(*hash, H.data(), HASH_SIZE, EM.data(),
+ output_length - HASH_SIZE - tLength);
+ buffer_insert(EM, output_length - HASH_SIZE - tLength, H);
+ //set implicit/ISO trailer
+ if(!implicit)
+ {
+ 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());
+ }
+ EM[output_length - 1] = 0xCC;
+ EM[output_length - 2] = hash_id;
+
+ }
+ else
+ {
+ EM[output_length - 1] = 0xBC;
+ }
+ //clear the leftmost bit (confer bouncy castle)
+ EM[0] &= 0x7F;
+
+ return EM;
+ }
+
+bool iso9796_verification(const secure_vector<uint8_t>& const_coded,
+ const secure_vector<uint8_t>& raw, size_t key_bits, std::unique_ptr<HashFunction>& 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;
+ }
+
+ secure_vector<uint8_t> 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<uint8_t>::set();
+ auto bad_input = CT::Mask<uint8_t>::cleared();
+
+ for(size_t j = 0; j < DB_size; ++j)
+ {
+ const auto is_zero = CT::Mask<uint8_t>::is_zero(DB[j]);
+ const auto is_one = CT::Mask<uint8_t>::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<size_t>::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<size_t>::expand(bad_input.value()).if_not_set_return(msg1_offset);
+
+ CT::unpoison(coded.data(), coded.size());
+ CT::unpoison(msg1_offset);
+
+ secure_vector<uint8_t> msg1(coded.begin() + msg1_offset,
+ coded.end() - tLength - HASH_SIZE - SALT_SIZE);
+ secure_vector<uint8_t> 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;
+ secure_vector<uint8_t> msg1raw;
+ secure_vector<uint8_t> msg2;
+ if(raw.size() > capacity)
+ {
+ msg1raw = secure_vector<uint8_t> (raw.begin(), raw.begin() + capacity);
+ msg2 = secure_vector<uint8_t> (raw.begin() + capacity, raw.end());
+ hash->update(msg2);
+ }
+ else
+ {
+ msg1raw = raw;
+ }
+ msg2 = hash->final();
+
+ const uint64_t msg1rawLength = msg1raw.size();
+ hash->update_be(msg1rawLength * 8);
+ hash->update(msg1raw);
+ hash->update(msg2);
+ hash->update(salt);
+ secure_vector<uint8_t> H3 = hash->final();
+
+ //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(msg2);
+ hash->update(salt);
+ secure_vector<uint8_t> H2 = hash->final();
+
+ //check if H3 == H2
+ bad_input |= CT::Mask<uint8_t>::is_zero(ct_compare_u8(H3.data(), H2.data(), HASH_SIZE));
+
+ CT::unpoison(bad_input);
+ return (bad_input.is_set() == false);
+ }
+
+}
+
+EMSA* ISO_9796_DS2::clone()
+ {
+ return new ISO_9796_DS2(m_hash->clone(), m_implicit, m_SALT_SIZE);
+ }
+
+/*
+ * 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
+ */
+secure_vector<uint8_t> ISO_9796_DS2::raw_data()
+ {
+ secure_vector<uint8_t> retbuffer = m_msg_buffer;
+ m_msg_buffer.clear();
+ return retbuffer;
+ }
+
+/*
+ * ISO-9796-2 scheme 2 encode operation
+ */
+secure_vector<uint8_t> ISO_9796_DS2::encoding_of(const secure_vector<uint8_t>& 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 secure_vector<uint8_t>& const_coded,
+ const secure_vector<uint8_t>& 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 "ISO_9796_DS2(" + m_hash->name() + ","
+ + (m_implicit ? "imp" : "exp") + "," + std::to_string(m_SALT_SIZE) + ")";
+ }
+
+EMSA* ISO_9796_DS3::clone()
+ {
+ return new ISO_9796_DS3(m_hash->clone(), m_implicit);
+ }
+
+/*
+ * 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
+ */
+secure_vector<uint8_t> ISO_9796_DS3::raw_data()
+ {
+ secure_vector<uint8_t> retbuffer = m_msg_buffer;
+ m_msg_buffer.clear();
+ return retbuffer;
+ }
+
+/*
+ * ISO-9796-2 scheme 3 encode operation
+ */
+secure_vector<uint8_t> ISO_9796_DS3::encoding_of(const secure_vector<uint8_t>& 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 secure_vector<uint8_t>& const_coded,
+ const secure_vector<uint8_t>& 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 "ISO_9796_DS3(" + m_hash->name() + "," +
+ (m_implicit ? "imp" : "exp") + ")";
+ }
+}
diff --git a/comm/third_party/botan/src/lib/pk_pad/iso9796/iso9796.h b/comm/third_party/botan/src/lib/pk_pad/iso9796/iso9796.h
new file mode 100644
index 0000000000..9782e53b23
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pk_pad/iso9796/iso9796.h
@@ -0,0 +1,98 @@
+/*
+ * 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 <botan/emsa.h>
+#include <botan/hash.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(iso9796.h)
+
+namespace Botan {
+
+/**
+* ISO-9796-2 - Digital signature scheme 2 (probabilistic)
+*/
+class BOTAN_PUBLIC_API(2,0) 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(HashFunction* hash, bool implicit = false) : m_hash(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(HashFunction* hash, bool implicit, size_t salt_size) : m_hash(hash), m_implicit(implicit),
+ m_SALT_SIZE(salt_size) {}
+
+ EMSA* clone() override;
+
+ std::string name() const override;
+ private:
+ void update(const uint8_t input[], size_t length) override;
+
+ secure_vector<uint8_t> raw_data() override;
+
+ secure_vector<uint8_t> encoding_of(const secure_vector<uint8_t>& msg,
+ size_t output_bits,
+ RandomNumberGenerator& rng) override;
+
+ bool verify(const secure_vector<uint8_t>& coded,
+ const secure_vector<uint8_t>& raw,
+ size_t key_bits) override;
+
+ std::unique_ptr<HashFunction> m_hash;
+ bool m_implicit;
+ size_t m_SALT_SIZE;
+ secure_vector<uint8_t> m_msg_buffer;
+ };
+
+/**
+* ISO-9796-2 - Digital signature scheme 3 (deterministic)
+*/
+class BOTAN_PUBLIC_API(2,0) ISO_9796_DS3 final : public EMSA
+ {
+ public:
+ /**
+ * @param hash function to use
+ * @param implicit whether or not the trailer is implicit
+ */
+ ISO_9796_DS3(HashFunction* hash, bool implicit = false) : m_hash(hash), m_implicit(implicit)
+ {}
+
+ EMSA* clone() override;
+
+ std::string name() const override;
+ private:
+ void update(const uint8_t input[], size_t length) override;
+
+ secure_vector<uint8_t> raw_data() override;
+
+ secure_vector<uint8_t> encoding_of(const secure_vector<uint8_t>& msg,
+ size_t output_bits,
+ RandomNumberGenerator& rng) override;
+
+ bool verify(const secure_vector<uint8_t>& coded,
+ const secure_vector<uint8_t>& raw,
+ size_t key_bits) override;
+
+ std::unique_ptr<HashFunction> m_hash;
+ bool m_implicit;
+ secure_vector<uint8_t> m_msg_buffer;
+ };
+
+}
+
+#endif
+
diff --git a/comm/third_party/botan/src/lib/pk_pad/mgf1/info.txt b/comm/third_party/botan/src/lib/pk_pad/mgf1/info.txt
new file mode 100644
index 0000000000..f17c72fda4
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pk_pad/mgf1/info.txt
@@ -0,0 +1,3 @@
+<defines>
+MGF1 -> 20140118
+</defines>
diff --git a/comm/third_party/botan/src/lib/pk_pad/mgf1/mgf1.cpp b/comm/third_party/botan/src/lib/pk_pad/mgf1/mgf1.cpp
new file mode 100644
index 0000000000..2e7ef99734
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pk_pad/mgf1/mgf1.cpp
@@ -0,0 +1,36 @@
+/*
+* MGF1
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/mgf1.h>
+#include <botan/hash.h>
+#include <algorithm>
+
+namespace Botan {
+
+void mgf1_mask(HashFunction& hash,
+ const uint8_t in[], size_t in_len,
+ uint8_t out[], size_t out_len)
+ {
+ uint32_t counter = 0;
+
+ secure_vector<uint8_t> buffer(hash.output_length());
+ while(out_len)
+ {
+ hash.update(in, in_len);
+ hash.update_be(counter);
+ hash.final(buffer.data());
+
+ const size_t xored = std::min<size_t>(buffer.size(), out_len);
+ xor_buf(out, buffer.data(), xored);
+ out += xored;
+ out_len -= xored;
+
+ ++counter;
+ }
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/pk_pad/mgf1/mgf1.h b/comm/third_party/botan/src/lib/pk_pad/mgf1/mgf1.h
new file mode 100644
index 0000000000..9eb652a825
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pk_pad/mgf1/mgf1.h
@@ -0,0 +1,31 @@
+/*
+* MGF1
+* (C) 1999-2007,2014 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_MGF1_H_
+#define BOTAN_MGF1_H_
+
+#include <botan/types.h>
+
+namespace Botan {
+
+class HashFunction;
+
+/**
+* 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
+* @param out_len size of the output buffer in bytes
+*/
+void BOTAN_PUBLIC_API(2,0) mgf1_mask(HashFunction& hash,
+ const uint8_t in[], size_t in_len,
+ uint8_t out[], size_t out_len);
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/pk_pad/padding.cpp b/comm/third_party/botan/src/lib/pk_pad/padding.cpp
new file mode 100644
index 0000000000..bac3fcd7eb
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pk_pad/padding.cpp
@@ -0,0 +1,44 @@
+/*
+* Sets of allowed padding schemes for public key types
+*
+* This file was automatically generated by ./src/scripts/oids.py on 2017-12-20
+*
+* 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 <botan/internal/padding.h>
+#include <map>
+#include <vector>
+#include <string>
+#include <algorithm>
+
+namespace Botan {
+
+const std::map<const std::string, std::vector<std::string>> allowed_signature_paddings =
+ {
+ { "DSA", {"EMSA1"} },
+ { "ECDSA", {"EMSA1"} },
+ { "ECGDSA", {"EMSA1"} },
+ { "ECKCDSA", {"EMSA1"} },
+ { "GOST-34.10", {"EMSA1"} },
+ { "GOST-34.10-2012-256", {"EMSA1"} },
+ { "GOST-34.10-2012-512", {"EMSA1"} },
+ { "RSA", {"EMSA4", "EMSA3"} },
+ };
+
+const std::vector<std::string> get_sig_paddings(const std::string algo)
+ {
+ if(allowed_signature_paddings.count(algo) > 0)
+ return allowed_signature_paddings.at(algo);
+ return {};
+ }
+
+bool sig_algo_and_pad_ok(const std::string algo, const std::string padding)
+ {
+ std::vector<std::string> pads = get_sig_paddings(algo);
+ return std::find(pads.begin(), pads.end(), padding) != pads.end();
+ }
+}
diff --git a/comm/third_party/botan/src/lib/pk_pad/padding.h b/comm/third_party/botan/src/lib/pk_pad/padding.h
new file mode 100644
index 0000000000..ed05ec3819
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pk_pad/padding.h
@@ -0,0 +1,36 @@
+/*
+* (C) 2017 Fabian Weissberg, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_PADDING_H_
+#define BOTAN_PADDING_H_
+
+#include <botan/build.h>
+#include <string>
+#include <vector>
+
+namespace Botan {
+
+/**
+* Returns the allowed padding schemes when using the given
+* algorithm (key type) for creating digital signatures.
+*
+* @param algo the algorithm for which to look up supported padding schemes
+* @return a vector of supported padding schemes
+*/
+BOTAN_TEST_API const std::vector<std::string> get_sig_paddings(const std::string algo);
+
+/**
+* Returns true iff the given padding scheme is valid for the given
+* signature algorithm (key type).
+*
+* @param algo the signature algorithm to be used
+* @param padding the padding scheme to be used
+*/
+bool sig_algo_and_pad_ok(const std::string algo, const std::string padding);
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/prov/commoncrypto/commoncrypto.h b/comm/third_party/botan/src/lib/prov/commoncrypto/commoncrypto.h
new file mode 100644
index 0000000000..31fa110fc3
--- /dev/null
+++ b/comm/third_party/botan/src/lib/prov/commoncrypto/commoncrypto.h
@@ -0,0 +1,62 @@
+/*
+* Utils for calling CommonCrypto
+* (C) 2018 Jose Pereira
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_INTERNAL_COMMONCRYPTO_H_
+#define BOTAN_INTERNAL_COMMONCRYPTO_H_
+
+#include <botan/pk_ops_fwd.h>
+#include <botan/secmem.h>
+#include <botan/exceptn.h>
+#include <memory>
+#include <string>
+
+namespace Botan {
+
+class Cipher_Mode;
+class BlockCipher;
+class HashFunction;
+enum Cipher_Dir : int;
+typedef int32_t CCCryptorStatus;
+
+class BOTAN_PUBLIC_API(2, 0) CommonCrypto_Error final : public Exception
+ {
+ public:
+ CommonCrypto_Error(const std::string& what) :
+ Exception(what + " failed."),
+ m_rc(0) {}
+
+ CommonCrypto_Error(const std::string& what, int32_t status) :
+ Exception(what + std::string(" failed. Status: ") + ccryptorstatus_to_string(status)),
+ m_rc(status) {}
+
+ ErrorType error_type() const noexcept override { return ErrorType::CommonCryptoError; }
+
+ int error_code() const noexcept override { return m_rc; }
+
+ private:
+ std::string ccryptorstatus_to_string(CCCryptorStatus status);
+
+ int32_t m_rc;
+ };
+
+/* Cipher Modes */
+
+Cipher_Mode*
+make_commoncrypto_cipher_mode(const std::string& name, Cipher_Dir direction);
+
+/* Block Ciphers */
+
+std::unique_ptr<BlockCipher>
+make_commoncrypto_block_cipher(const std::string& name);
+
+/* Hash */
+
+std::unique_ptr<HashFunction> make_commoncrypto_hash(const std::string& name);
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/prov/commoncrypto/commoncrypto_block.cpp b/comm/third_party/botan/src/lib/prov/commoncrypto/commoncrypto_block.cpp
new file mode 100644
index 0000000000..417e765bd6
--- /dev/null
+++ b/comm/third_party/botan/src/lib/prov/commoncrypto/commoncrypto_block.cpp
@@ -0,0 +1,164 @@
+/*
+* Block Ciphers via CommonCrypto
+* (C) 2018 Jose Luis Pereira
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/commoncrypto.h>
+#include <botan/internal/commoncrypto_utils.h>
+#include <botan/hex.h>
+#include <botan/block_cipher.h>
+
+#include <CommonCrypto/CommonCrypto.h>
+
+namespace Botan {
+
+namespace {
+
+class CommonCrypto_BlockCipher final : public BlockCipher
+ {
+ public:
+ CommonCrypto_BlockCipher(const std::string& name, const CommonCryptor_Opts& opts);
+
+ ~CommonCrypto_BlockCipher();
+
+ void clear() override;
+ std::string provider() const override { return "commoncrypto"; }
+ std::string name() const override { return m_cipher_name; }
+ BlockCipher* clone() const override;
+
+ size_t block_size() const override { return m_opts.block_size; }
+
+ Key_Length_Specification key_spec() const override { return m_opts.key_spec; }
+
+ void encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override
+ {
+ verify_key_set(m_key_set);
+ size_t total_len = blocks * m_opts.block_size;
+ size_t out_len = 0;
+
+ CCCryptorStatus status = CCCryptorUpdate(m_encrypt, in, total_len,
+ out, total_len, &out_len);
+ if(status != kCCSuccess)
+ {
+ throw CommonCrypto_Error("CCCryptorUpdate encrypt", status);
+ }
+ }
+
+ void decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override
+ {
+ verify_key_set(m_key_set);
+ size_t total_len = blocks * m_opts.block_size;
+ size_t out_len = 0;
+
+ CCCryptorStatus status = CCCryptorUpdate(m_decrypt, in, total_len,
+ out, total_len, &out_len);
+ if(status != kCCSuccess)
+ {
+ throw CommonCrypto_Error("CCCryptorUpdate decrypt", status);
+ }
+ }
+
+ void key_schedule(const uint8_t key[], size_t key_len) override;
+
+ std::string m_cipher_name;
+ CommonCryptor_Opts m_opts;
+
+ CCCryptorRef m_encrypt = nullptr;
+ CCCryptorRef m_decrypt = nullptr;
+ bool m_key_set;
+ };
+
+CommonCrypto_BlockCipher::CommonCrypto_BlockCipher(const std::string& algo_name,
+ const CommonCryptor_Opts& opts) :
+ m_cipher_name(algo_name),
+ m_opts(opts),
+ m_key_set(false)
+ {
+ }
+
+CommonCrypto_BlockCipher::~CommonCrypto_BlockCipher()
+ {
+ if(m_encrypt)
+ {
+ CCCryptorRelease(m_encrypt);
+ }
+ if(m_decrypt)
+ {
+ CCCryptorRelease(m_decrypt);
+ }
+ }
+
+/*
+* Set the key
+*/
+void CommonCrypto_BlockCipher::key_schedule(const uint8_t key[], size_t length)
+ {
+ secure_vector<uint8_t> full_key(key, key + length);
+
+ clear();
+ commoncrypto_adjust_key_size(key, length, m_opts, full_key);
+
+ CCCryptorStatus status;
+ status = CCCryptorCreate(kCCEncrypt, m_opts.algo, kCCOptionECBMode,
+ full_key.data(), full_key.size(), nullptr, &m_encrypt);
+ if(status != kCCSuccess)
+ {
+ throw CommonCrypto_Error("CCCryptorCreate encrypt", status);
+ }
+ status = CCCryptorCreate(kCCDecrypt, m_opts.algo, kCCOptionECBMode,
+ full_key.data(), full_key.size(), nullptr, &m_decrypt);
+ if(status != kCCSuccess)
+ {
+ throw CommonCrypto_Error("CCCryptorCreate decrypt", status);
+ }
+
+ m_key_set = true;
+ }
+
+/*
+* Return a clone of this object
+*/
+BlockCipher* CommonCrypto_BlockCipher::clone() const
+ {
+ return new CommonCrypto_BlockCipher(m_cipher_name, m_opts);
+ }
+
+/*
+* Clear memory of sensitive data
+*/
+void CommonCrypto_BlockCipher::clear()
+ {
+ m_key_set = false;
+
+ if(m_encrypt)
+ {
+ CCCryptorRelease(m_encrypt);
+ m_encrypt = nullptr;
+ }
+
+ if(m_decrypt)
+ {
+ CCCryptorRelease(m_decrypt);
+ m_decrypt = nullptr;
+ }
+ }
+}
+
+std::unique_ptr<BlockCipher>
+make_commoncrypto_block_cipher(const std::string& name)
+ {
+
+ try
+ {
+ CommonCryptor_Opts opts = commoncrypto_opts_from_algo_name(name);
+ return std::unique_ptr<BlockCipher>(new CommonCrypto_BlockCipher(name, opts));
+ }
+ catch(CommonCrypto_Error& e)
+ {
+ return nullptr;
+ }
+ }
+}
+
diff --git a/comm/third_party/botan/src/lib/prov/commoncrypto/commoncrypto_hash.cpp b/comm/third_party/botan/src/lib/prov/commoncrypto/commoncrypto_hash.cpp
new file mode 100644
index 0000000000..f6203fb417
--- /dev/null
+++ b/comm/third_party/botan/src/lib/prov/commoncrypto/commoncrypto_hash.cpp
@@ -0,0 +1,146 @@
+/*
+* CommonCrypto Hash Functions
+* (C) 2018 Jose Pereira
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/commoncrypto.h>
+#include <botan/hash.h>
+#include <unordered_map>
+
+#include <CommonCrypto/CommonCrypto.h>
+
+namespace Botan {
+
+namespace {
+
+template <class CTX>
+class CommonCrypto_HashFunction final : public HashFunction
+ {
+ public:
+
+ struct digest_config_t {
+ std::string name;
+ size_t digestLength;
+ size_t blockSize;
+ int (*init)(CTX *);
+ int (*update)(CTX *, const void *, CC_LONG len);
+ int (*final)(unsigned char *, CTX*);
+ };
+
+ void clear() override
+ {
+ if(m_info.init(&m_ctx) != 1)
+ throw CommonCrypto_Error("CC_" + m_info.name + "_Init");
+ }
+
+ std::string provider() const override { return "commoncrypto"; }
+ std::string name() const override { return m_info.name; }
+
+ HashFunction* clone() const override
+ {
+ return new CommonCrypto_HashFunction(m_info);
+ }
+
+ std::unique_ptr<HashFunction> copy_state() const override
+ {
+ return std::unique_ptr<CommonCrypto_HashFunction>(
+ new CommonCrypto_HashFunction(m_info, m_ctx));
+ }
+
+ size_t output_length() const override
+ {
+ return m_info.digestLength;
+ }
+
+ size_t hash_block_size() const override
+ {
+ return m_info.blockSize;
+ }
+
+ CommonCrypto_HashFunction(const digest_config_t& info) :
+ m_info(info)
+ {
+ clear();
+ }
+
+ CommonCrypto_HashFunction(const digest_config_t& info, const CTX &ctx) :
+ m_ctx(ctx), m_info(info) {}
+
+ private:
+ void add_data(const uint8_t input[], size_t length) override
+ {
+ /* update len parameter is 32 bit unsigned integer, feed input in parts */
+ while (length > 0)
+ {
+ CC_LONG update_len = (length > 0xFFFFFFFFUL) ? 0xFFFFFFFFUL : static_cast<CC_LONG>(length);
+ m_info.update(&m_ctx, input, update_len);
+ input += update_len;
+ length -= update_len;
+ }
+ }
+
+ void final_result(uint8_t output[]) override
+ {
+ if(m_info.final(output, &m_ctx) != 1)
+ throw CommonCrypto_Error("CC_" + m_info.name + "_Final");
+ clear();
+ }
+
+ CTX m_ctx;
+ digest_config_t m_info;
+ };
+}
+
+std::unique_ptr<HashFunction>
+make_commoncrypto_hash(const std::string& name)
+ {
+#define MAKE_COMMONCRYPTO_HASH_3(name, hash, ctx) \
+ std::unique_ptr<HashFunction>( \
+ new CommonCrypto_HashFunction<CC_ ## ctx ## _CTX >({ \
+ name, \
+ CC_ ## hash ## _DIGEST_LENGTH, \
+ CC_ ## hash ## _BLOCK_BYTES, \
+ CC_ ## hash ## _Init, \
+ CC_ ## hash ## _Update, \
+ CC_ ## hash ## _Final \
+ }));
+
+#define MAKE_COMMONCRYPTO_HASH_2(name, id) \
+ MAKE_COMMONCRYPTO_HASH_3(name, id, id)
+
+#define MAKE_COMMONCRYPTO_HASH_1(id) \
+ MAKE_COMMONCRYPTO_HASH_2(#id, id)
+
+#if defined(BOTAN_HAS_SHA2_32)
+ if(name == "SHA-224")
+ return MAKE_COMMONCRYPTO_HASH_3(name, SHA224, SHA256);
+ if(name == "SHA-256")
+ return MAKE_COMMONCRYPTO_HASH_2(name, SHA256);
+#endif
+#if defined(BOTAN_HAS_SHA2_64)
+ if(name == "SHA-384")
+ return MAKE_COMMONCRYPTO_HASH_3(name, SHA384, SHA512);
+ if(name == "SHA-512")
+ return MAKE_COMMONCRYPTO_HASH_2(name, SHA512);
+#endif
+
+#if defined(BOTAN_HAS_SHA1)
+ if(name == "SHA-160" || name == "SHA-1" || name == "SHA1")
+ return MAKE_COMMONCRYPTO_HASH_2(name, SHA1);
+#endif
+
+#if defined(BOTAN_HAS_MD5)
+ if(name == "MD5")
+ return MAKE_COMMONCRYPTO_HASH_1(MD5);
+#endif
+
+#if defined(BOTAN_HAS_MD4)
+ if(name == "MD4")
+ return MAKE_COMMONCRYPTO_HASH_1(MD4);
+#endif
+ return nullptr;
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/prov/commoncrypto/commoncrypto_mode.cpp b/comm/third_party/botan/src/lib/prov/commoncrypto/commoncrypto_mode.cpp
new file mode 100644
index 0000000000..82d4bd5526
--- /dev/null
+++ b/comm/third_party/botan/src/lib/prov/commoncrypto/commoncrypto_mode.cpp
@@ -0,0 +1,247 @@
+/*
+* Cipher Modes via CommonCrypto
+* (C) 2018 Jose Pereira
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/commoncrypto.h>
+#include <botan/internal/commoncrypto_utils.h>
+#include <botan/cipher_mode.h>
+#include <botan/internal/rounding.h>
+
+#include <limits.h>
+
+namespace Botan {
+
+namespace {
+
+class CommonCrypto_Cipher_Mode final : public Cipher_Mode
+ {
+ public:
+ CommonCrypto_Cipher_Mode(const std::string& name,
+ Cipher_Dir direction,
+ const CommonCryptor_Opts& opts);
+
+ ~CommonCrypto_Cipher_Mode();
+
+ std::string provider() const override { return "commoncrypto"; }
+ std::string name() const override { return m_mode_name; }
+
+ void start_msg(const uint8_t nonce[], size_t nonce_len) override;
+ size_t process(uint8_t msg[], size_t msg_len) override;
+ void finish(secure_vector<uint8_t>& final_block, size_t offset0) override;
+ size_t output_length(size_t input_length) const override;
+ size_t update_granularity() const override;
+ size_t minimum_final_size() const override;
+ size_t default_nonce_length() const override;
+ bool valid_nonce_length(size_t nonce_len) const override;
+ void clear() override;
+ void reset() override;
+ Key_Length_Specification key_spec() const override;
+
+ private:
+ void key_schedule(const uint8_t key[], size_t length) override;
+
+ const std::string m_mode_name;
+ Cipher_Dir m_direction;
+ CommonCryptor_Opts m_opts;
+ CCCryptorRef m_cipher = nullptr;
+ bool m_key_set;
+ bool m_nonce_set;
+ };
+
+CommonCrypto_Cipher_Mode::CommonCrypto_Cipher_Mode(const std::string& name,
+ Cipher_Dir direction, const CommonCryptor_Opts& opts) :
+ m_mode_name(name),
+ m_direction(direction),
+ m_opts(opts),
+ m_key_set(false),
+ m_nonce_set(false)
+ {
+ }
+
+CommonCrypto_Cipher_Mode::~CommonCrypto_Cipher_Mode()
+ {
+ if(m_cipher)
+ {
+ CCCryptorRelease(m_cipher);
+ }
+ }
+
+void CommonCrypto_Cipher_Mode::start_msg(const uint8_t nonce[], size_t nonce_len)
+ {
+ verify_key_set(m_key_set);
+
+ if(!valid_nonce_length(nonce_len))
+ { throw Invalid_IV_Length(name(), nonce_len); }
+ if(nonce_len)
+ {
+ CCCryptorStatus status = CCCryptorReset(m_cipher, nonce);
+ if(status != kCCSuccess)
+ {
+ throw CommonCrypto_Error("CCCryptorReset on start_msg", status);
+ }
+ }
+ m_nonce_set = true;
+ }
+
+size_t CommonCrypto_Cipher_Mode::process(uint8_t msg[], size_t msg_len)
+ {
+ verify_key_set(m_key_set);
+ BOTAN_STATE_CHECK(m_nonce_set);
+
+ if(msg_len == 0)
+ { return 0; }
+ if(msg_len > INT_MAX)
+ { throw Internal_Error("msg_len overflow"); }
+ size_t outl = CCCryptorGetOutputLength(m_cipher, msg_len, false);
+
+ secure_vector<uint8_t> out(outl);
+
+ if(m_opts.padding == ccNoPadding && msg_len % m_opts.block_size)
+ {
+ msg_len = outl;
+ }
+
+ CCCryptorStatus status = CCCryptorUpdate(m_cipher, msg, msg_len,
+ out.data(), outl, &outl);
+ if(status != kCCSuccess)
+ {
+ throw CommonCrypto_Error("CCCryptorUpdate", status);
+ }
+ copy_mem(msg, out.data(), outl);
+
+ return outl;
+ }
+
+void CommonCrypto_Cipher_Mode::finish(secure_vector<uint8_t>& buffer,
+ size_t offset)
+ {
+ verify_key_set(m_key_set);
+ BOTAN_STATE_CHECK(m_nonce_set);
+
+ BOTAN_ASSERT(buffer.size() >= offset, "Offset ok");
+ uint8_t* buf = buffer.data() + offset;
+ const size_t buf_size = buffer.size() - offset;
+
+ size_t written = process(buf, buf_size);
+
+ size_t outl = CCCryptorGetOutputLength(m_cipher, buf_size - written, true);
+ secure_vector<uint8_t> out(outl);
+
+ CCCryptorStatus status = CCCryptorFinal(
+ m_cipher, out.data(), outl, &outl);
+ if(status != kCCSuccess)
+ {
+ throw CommonCrypto_Error("CCCryptorFinal", status);
+ }
+
+ size_t new_len = offset + written + outl;
+ if(m_opts.padding != ccNoPadding || buffer.size() < new_len)
+ {
+ buffer.resize(new_len);
+ }
+ copy_mem(buffer.data() - offset + written, out.data(), outl);
+ written += outl;
+ }
+
+size_t CommonCrypto_Cipher_Mode::update_granularity() const
+ {
+ return m_opts.block_size * BOTAN_BLOCK_CIPHER_PAR_MULT;
+ }
+
+size_t CommonCrypto_Cipher_Mode::minimum_final_size() const
+ {
+ if(m_direction == ENCRYPTION)
+ return 0;
+ else
+ return m_opts.block_size;
+ }
+
+size_t CommonCrypto_Cipher_Mode::default_nonce_length() const
+ {
+ return m_opts.block_size;
+ }
+
+bool CommonCrypto_Cipher_Mode::valid_nonce_length(size_t nonce_len) const
+ {
+ return (nonce_len == 0 || nonce_len == m_opts.block_size);
+ }
+
+size_t CommonCrypto_Cipher_Mode::output_length(size_t input_length) const
+ {
+ if(input_length == 0)
+ { return m_opts.block_size; }
+ else
+ { return round_up(input_length, m_opts.block_size); }
+ }
+
+void CommonCrypto_Cipher_Mode::clear()
+ {
+ m_key_set = false;
+
+ if(m_cipher == nullptr)
+ {
+ return;
+ }
+
+ if(m_cipher)
+ {
+ CCCryptorRelease(m_cipher);
+ m_cipher = nullptr;
+ }
+ }
+
+void CommonCrypto_Cipher_Mode::reset()
+ {
+ if(m_cipher == nullptr)
+ {
+ return;
+ }
+
+ m_nonce_set = false;
+
+ CCCryptorStatus status = CCCryptorReset(m_cipher, nullptr);
+ if(status != kCCSuccess)
+ {
+ throw CommonCrypto_Error("CCCryptorReset", status);
+ }
+ }
+
+Key_Length_Specification CommonCrypto_Cipher_Mode::key_spec() const
+ {
+ return m_opts.key_spec;
+ }
+
+void CommonCrypto_Cipher_Mode::key_schedule(const uint8_t key[], size_t length)
+ {
+ CCCryptorStatus status;
+ CCOperation op = m_direction == ENCRYPTION ? kCCEncrypt : kCCDecrypt;
+ status = CCCryptorCreateWithMode(op, m_opts.mode, m_opts.algo, m_opts.padding,
+ nullptr, key, length, nullptr, 0, 0, 0, &m_cipher);
+ if(status != kCCSuccess)
+ {
+ throw CommonCrypto_Error("CCCryptorCreate", status);
+ }
+
+ m_key_set = true;
+ m_nonce_set = false;
+ }
+}
+
+Cipher_Mode*
+make_commoncrypto_cipher_mode(const std::string& name, Cipher_Dir direction)
+ {
+
+ try
+ {
+ CommonCryptor_Opts opts = commoncrypto_opts_from_algo(name);
+ return new CommonCrypto_Cipher_Mode(name, direction, opts);
+ }
+ catch(CommonCrypto_Error& e)
+ {
+ return nullptr;
+ }
+ }
+}
diff --git a/comm/third_party/botan/src/lib/prov/commoncrypto/commoncrypto_utils.cpp b/comm/third_party/botan/src/lib/prov/commoncrypto/commoncrypto_utils.cpp
new file mode 100644
index 0000000000..59e501dd7e
--- /dev/null
+++ b/comm/third_party/botan/src/lib/prov/commoncrypto/commoncrypto_utils.cpp
@@ -0,0 +1,196 @@
+/*
+* Cipher Modes via CommonCrypto
+* (C) 2018 Jose Pereira
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/commoncrypto.h>
+#include <botan/internal/commoncrypto_utils.h>
+#include <botan/cipher_mode.h>
+#include <botan/parsing.h>
+#include <botan/internal/rounding.h>
+#include <botan/scan_name.h>
+
+namespace Botan {
+
+std::string CommonCrypto_Error::ccryptorstatus_to_string(CCCryptorStatus status)
+ {
+ switch(status)
+ {
+ case kCCSuccess:
+ return "Success";
+ case kCCParamError:
+ return "ParamError";
+ case kCCBufferTooSmall:
+ return "BufferTooSmall";
+ case kCCMemoryFailure:
+ return "MemoryFailure";
+ case kCCAlignmentError:
+ return "AlignmentError";
+ case kCCDecodeError:
+ return "DecodeError";
+ case kCCUnimplemented:
+ return "Unimplemented";
+ case kCCOverflow:
+ return "Overflow";
+ case kCCRNGFailure:
+ return "RNGFailure";
+ case kCCUnspecifiedError:
+ return "UnspecifiedError";
+ case kCCCallSequenceError:
+ return "CallSequenceError";
+ case kCCKeySizeError:
+ return "KeySizeError";
+ default:
+ return "Unknown";
+ }
+ };
+
+
+CommonCryptor_Opts commoncrypto_opts_from_algo_name(const std::string& algo_name)
+ {
+ CommonCryptor_Opts opts;
+
+ if(algo_name.compare(0, 3, "AES") == 0)
+ {
+ opts.algo = kCCAlgorithmAES;
+ opts.block_size = kCCBlockSizeAES128;
+ if(algo_name == "AES-128")
+ {
+ opts.key_spec = Key_Length_Specification(kCCKeySizeAES128);
+ }
+ else if(algo_name == "AES-192")
+ {
+ opts.key_spec = Key_Length_Specification(kCCKeySizeAES192);
+ }
+ else if(algo_name == "AES-256")
+ {
+ opts.key_spec = Key_Length_Specification(kCCKeySizeAES256);
+ }
+ else
+ {
+ throw CommonCrypto_Error("Unknown AES algorithm");
+ }
+ }
+ else if(algo_name == "DES")
+ {
+ opts.algo = kCCAlgorithmDES;
+ opts.block_size = kCCBlockSizeDES;
+ opts.key_spec = Key_Length_Specification(kCCKeySizeDES);
+ }
+ else if(algo_name == "TripleDES")
+ {
+ opts.algo = kCCAlgorithm3DES;
+ opts.block_size = kCCBlockSize3DES;
+ opts.key_spec = Key_Length_Specification(16, kCCKeySize3DES, 8);
+ }
+ else if(algo_name == "Blowfish")
+ {
+ opts.algo = kCCAlgorithmBlowfish;
+ opts.block_size = kCCBlockSizeBlowfish;
+ opts.key_spec = Key_Length_Specification(1, kCCKeySizeMaxBlowfish, 1);
+ }
+ else if(algo_name == "CAST-128")
+ {
+ opts.algo = kCCAlgorithmCAST;
+ opts.block_size = kCCBlockSizeCAST;
+ // Botan's base implementation of CAST does not support shorter keys
+ // so we limit its minimum key size to 11 here.
+ opts.key_spec = Key_Length_Specification(11, kCCKeySizeMaxCAST, 1);
+ }
+ else
+ {
+ throw CommonCrypto_Error("Unsupported cipher");
+ }
+
+ return opts;
+ }
+
+
+CommonCryptor_Opts commoncrypto_opts_from_algo(const std::string& algo)
+ {
+ SCAN_Name spec(algo);
+
+ std::string algo_name = spec.algo_name();
+ std::string cipher_mode = spec.cipher_mode();
+ std::string cipher_mode_padding = spec.cipher_mode_pad();
+
+ CommonCryptor_Opts opts = commoncrypto_opts_from_algo_name(algo_name);
+
+ //TODO add CFB and XTS support
+ if(cipher_mode.empty() || cipher_mode == "ECB")
+ {
+ opts.mode = kCCModeECB;
+ }
+ else if(cipher_mode == "CBC")
+ {
+ opts.mode = kCCModeCBC;
+ }
+ else if(cipher_mode == "CTR")
+ {
+ opts.mode = kCCModeCTR;
+ }
+ else if(cipher_mode == "OFB")
+ {
+ opts.mode = kCCModeOFB;
+ }
+ else
+ {
+ throw CommonCrypto_Error("Unsupported cipher mode!");
+ }
+
+ if(cipher_mode_padding == "NoPadding")
+ {
+ opts.padding = ccNoPadding;
+ }
+ /*
+ else if(cipher_mode_padding.empty() || cipher_mode_padding == "PKCS7")
+ {
+ opts.padding = ccPKCS7Padding;
+ }
+ */
+ else
+ {
+ throw CommonCrypto_Error("Unsupported cipher mode padding!");
+ }
+
+ return opts;
+ }
+
+
+void commoncrypto_adjust_key_size(const uint8_t key[], size_t length,
+ const CommonCryptor_Opts& opts, secure_vector<uint8_t>& full_key)
+ {
+
+ if(opts.algo == kCCAlgorithmBlowfish && length < 8)
+ {
+ size_t repeat;
+ switch(length)
+ {
+ case 1:
+ repeat = 8;
+ break;
+ case 2:
+ repeat = 4;
+ break;
+ case 3:
+ repeat = 3;
+ break;
+ default:
+ repeat = 2;
+ break;
+ }
+
+ full_key.resize(length * repeat);
+ for(size_t i = 0; i < repeat; i++)
+ {
+ copy_mem(full_key.data() + i * length, key, length);
+ }
+ }
+ else if(opts.algo == kCCAlgorithm3DES && length == 16)
+ {
+ full_key += std::make_pair(key, 8);
+ }
+ }
+}
diff --git a/comm/third_party/botan/src/lib/prov/commoncrypto/commoncrypto_utils.h b/comm/third_party/botan/src/lib/prov/commoncrypto/commoncrypto_utils.h
new file mode 100644
index 0000000000..c20f0d81d7
--- /dev/null
+++ b/comm/third_party/botan/src/lib/prov/commoncrypto/commoncrypto_utils.h
@@ -0,0 +1,35 @@
+/*
+* Utils for calling CommonCrypto
+* (C) 2018 Jose Pereira
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_INTERNAL_COMMONCRYPTO_UTILS_H_
+#define BOTAN_INTERNAL_COMMONCRYPTO_UTILS_H_
+
+#include <botan/sym_algo.h>
+
+#include <CommonCrypto/CommonCrypto.h>
+
+namespace Botan {
+
+struct CommonCryptor_Opts
+ {
+ CCAlgorithm algo;
+ CCMode mode;
+ CCPadding padding;
+ size_t block_size;
+ Key_Length_Specification key_spec{0};
+ };
+
+CommonCryptor_Opts commoncrypto_opts_from_algo_name(const std::string& algo_name);
+CommonCryptor_Opts commoncrypto_opts_from_algo(const std::string& algo);
+
+void commoncrypto_adjust_key_size(const uint8_t key[], size_t length,
+ const CommonCryptor_Opts& opts, secure_vector<uint8_t>& full_key);
+
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/prov/commoncrypto/info.txt b/comm/third_party/botan/src/lib/prov/commoncrypto/info.txt
new file mode 100644
index 0000000000..b95d4547ec
--- /dev/null
+++ b/comm/third_party/botan/src/lib/prov/commoncrypto/info.txt
@@ -0,0 +1,19 @@
+<defines>
+COMMONCRYPTO -> 20180903
+</defines>
+
+load_on vendor
+
+<header:internal>
+commoncrypto.h
+commoncrypto_utils.h
+</header:internal>
+
+<os_features>
+commoncrypto
+</os_features>
+
+<requires>
+modes
+block
+</requires>
diff --git a/comm/third_party/botan/src/lib/prov/openssl/info.txt b/comm/third_party/botan/src/lib/prov/openssl/info.txt
new file mode 100644
index 0000000000..32e71a8481
--- /dev/null
+++ b/comm/third_party/botan/src/lib/prov/openssl/info.txt
@@ -0,0 +1,21 @@
+<defines>
+OPENSSL -> 20151219
+</defines>
+
+load_on vendor
+
+<header:internal>
+openssl.h
+</header:internal>
+
+<libs>
+all!windows -> crypto
+windows -> libeay32
+</libs>
+
+<requires>
+block
+stream
+modes
+pubkey
+</requires>
diff --git a/comm/third_party/botan/src/lib/prov/openssl/openssl.h b/comm/third_party/botan/src/lib/prov/openssl/openssl.h
new file mode 100644
index 0000000000..a68dda5af0
--- /dev/null
+++ b/comm/third_party/botan/src/lib/prov/openssl/openssl.h
@@ -0,0 +1,118 @@
+/*
+* Utils for calling OpenSSL
+* (C) 2015,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_INTERNAL_OPENSSL_H_
+#define BOTAN_INTERNAL_OPENSSL_H_
+
+#include <botan/pk_ops_fwd.h>
+#include <botan/secmem.h>
+#include <botan/exceptn.h>
+#include <memory>
+#include <string>
+
+#include <openssl/err.h>
+#include <openssl/evp.h>
+
+#if defined(BOTAN_HAS_RC4)
+#include <openssl/rc4.h>
+#endif
+
+namespace Botan {
+
+class BlockCipher;
+class Cipher_Mode;
+class StreamCipher;
+class HashFunction;
+class RandomNumberGenerator;
+enum Cipher_Dir : int;
+
+class BOTAN_PUBLIC_API(2,0) OpenSSL_Error final : public Exception
+ {
+ public:
+ OpenSSL_Error(const std::string& what, int err) :
+ Exception(what + " failed: " + ERR_error_string(err, nullptr)),
+ m_err(err) {}
+
+ ErrorType error_type() const noexcept override { return ErrorType::OpenSSLError; }
+
+ int error_code() const noexcept override { return m_err; }
+
+ private:
+ int m_err;
+ };
+
+/* Block Ciphers */
+
+std::unique_ptr<BlockCipher>
+make_openssl_block_cipher(const std::string& name);
+
+/* Cipher Modes */
+
+Cipher_Mode*
+make_openssl_cipher_mode(const std::string& name, Cipher_Dir direction);
+
+/* Hash */
+
+std::unique_ptr<HashFunction>
+make_openssl_hash(const std::string& name);
+
+/* RSA */
+
+#if defined(BOTAN_HAS_RSA)
+
+class RSA_PublicKey;
+class RSA_PrivateKey;
+
+std::unique_ptr<PK_Ops::Encryption>
+make_openssl_rsa_enc_op(const RSA_PublicKey& key, const std::string& params);
+std::unique_ptr<PK_Ops::Decryption>
+make_openssl_rsa_dec_op(const RSA_PrivateKey& key, const std::string& params);
+
+std::unique_ptr<PK_Ops::Verification>
+make_openssl_rsa_ver_op(const RSA_PublicKey& key, const std::string& params);
+std::unique_ptr<PK_Ops::Signature>
+make_openssl_rsa_sig_op(const RSA_PrivateKey& key, const std::string& params);
+std::unique_ptr<RSA_PrivateKey>
+make_openssl_rsa_private_key(RandomNumberGenerator& rng, size_t rsa_bits);
+
+#endif
+
+/* ECDSA */
+
+#if defined(BOTAN_HAS_ECDSA)
+
+class ECDSA_PublicKey;
+class ECDSA_PrivateKey;
+
+std::unique_ptr<PK_Ops::Verification>
+make_openssl_ecdsa_ver_op(const ECDSA_PublicKey& key, const std::string& params);
+std::unique_ptr<PK_Ops::Signature>
+make_openssl_ecdsa_sig_op(const ECDSA_PrivateKey& key, const std::string& params);
+
+#endif
+
+/* ECDH */
+
+#if defined(BOTAN_HAS_ECDH)
+
+class ECDH_PrivateKey;
+
+std::unique_ptr<PK_Ops::Key_Agreement>
+make_openssl_ecdh_ka_op(const ECDH_PrivateKey& key, const std::string& params);
+
+#endif
+
+#if defined(BOTAN_HAS_RC4)
+
+std::unique_ptr<StreamCipher>
+make_openssl_rc4(size_t skip);
+
+#endif
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/prov/openssl/openssl_block.cpp b/comm/third_party/botan/src/lib/prov/openssl/openssl_block.cpp
new file mode 100644
index 0000000000..7f9bdcf6c5
--- /dev/null
+++ b/comm/third_party/botan/src/lib/prov/openssl/openssl_block.cpp
@@ -0,0 +1,234 @@
+/*
+* Block Ciphers via OpenSSL
+* (C) 1999-2010,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/block_cipher.h>
+#include <botan/internal/openssl.h>
+#include <openssl/evp.h>
+
+namespace Botan {
+
+namespace {
+
+class OpenSSL_BlockCipher final : public BlockCipher
+ {
+ public:
+ OpenSSL_BlockCipher(const std::string& name,
+ const EVP_CIPHER* cipher);
+
+ OpenSSL_BlockCipher(const std::string& name,
+ const EVP_CIPHER* cipher,
+ size_t kl_min, size_t kl_max, size_t kl_mod);
+
+ ~OpenSSL_BlockCipher();
+
+ void clear() override;
+ std::string provider() const override { return "openssl"; }
+ std::string name() const override { return m_cipher_name; }
+ BlockCipher* clone() const override;
+
+ size_t block_size() const override { return m_block_sz; }
+
+ Key_Length_Specification key_spec() const override { return m_cipher_key_spec; }
+
+ void encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override
+ {
+ verify_key_set(m_key_set);
+ int out_len = 0;
+ if(!EVP_EncryptUpdate(m_encrypt, out, &out_len, in, blocks * m_block_sz))
+ throw OpenSSL_Error("EVP_EncryptUpdate", ERR_get_error());
+ }
+
+ void decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const override
+ {
+ verify_key_set(m_key_set);
+ int out_len = 0;
+ if(!EVP_DecryptUpdate(m_decrypt, out, &out_len, in, blocks * m_block_sz))
+ throw OpenSSL_Error("EVP_DecryptUpdate", ERR_get_error());
+ }
+
+ void key_schedule(const uint8_t key[], size_t key_len) override;
+
+ size_t m_block_sz;
+ Key_Length_Specification m_cipher_key_spec;
+ std::string m_cipher_name;
+ EVP_CIPHER_CTX *m_encrypt;
+ EVP_CIPHER_CTX *m_decrypt;
+ bool m_key_set;
+ };
+
+OpenSSL_BlockCipher::OpenSSL_BlockCipher(const std::string& algo_name,
+ const EVP_CIPHER* algo) :
+ m_block_sz(EVP_CIPHER_block_size(algo)),
+ m_cipher_key_spec(EVP_CIPHER_key_length(algo)),
+ m_cipher_name(algo_name),
+ m_key_set(false)
+ {
+ if(EVP_CIPHER_mode(algo) != EVP_CIPH_ECB_MODE)
+ throw Invalid_Argument("OpenSSL_BlockCipher: Non-ECB EVP was passed in");
+
+ m_encrypt = EVP_CIPHER_CTX_new();
+ m_decrypt = EVP_CIPHER_CTX_new();
+ if (m_encrypt == nullptr || m_decrypt == nullptr)
+ throw OpenSSL_Error("Can't allocate new context", ERR_get_error());
+
+ EVP_CIPHER_CTX_init(m_encrypt);
+ EVP_CIPHER_CTX_init(m_decrypt);
+
+ if(!EVP_EncryptInit_ex(m_encrypt, algo, nullptr, nullptr, nullptr))
+ throw OpenSSL_Error("EVP_EncryptInit_ex", ERR_get_error());
+ if(!EVP_DecryptInit_ex(m_decrypt, algo, nullptr, nullptr, nullptr))
+ throw OpenSSL_Error("EVP_DecryptInit_ex", ERR_get_error());
+
+ if(!EVP_CIPHER_CTX_set_padding(m_encrypt, 0))
+ throw OpenSSL_Error("EVP_CIPHER_CTX_set_padding encrypt", ERR_get_error());
+ if(!EVP_CIPHER_CTX_set_padding(m_decrypt, 0))
+ throw OpenSSL_Error("EVP_CIPHER_CTX_set_padding decrypt", ERR_get_error());
+ }
+
+OpenSSL_BlockCipher::OpenSSL_BlockCipher(const std::string& algo_name,
+ const EVP_CIPHER* algo,
+ size_t key_min,
+ size_t key_max,
+ size_t key_mod) :
+ m_block_sz(EVP_CIPHER_block_size(algo)),
+ m_cipher_key_spec(key_min, key_max, key_mod),
+ m_cipher_name(algo_name),
+ m_key_set(false)
+ {
+ if(EVP_CIPHER_mode(algo) != EVP_CIPH_ECB_MODE)
+ throw Invalid_Argument("OpenSSL_BlockCipher: Non-ECB EVP was passed in");
+
+ m_encrypt = EVP_CIPHER_CTX_new();
+ m_decrypt = EVP_CIPHER_CTX_new();
+ if (m_encrypt == nullptr || m_decrypt == nullptr)
+ throw OpenSSL_Error("Can't allocate new context", ERR_get_error());
+
+ EVP_CIPHER_CTX_init(m_encrypt);
+ EVP_CIPHER_CTX_init(m_decrypt);
+
+ if(!EVP_EncryptInit_ex(m_encrypt, algo, nullptr, nullptr, nullptr))
+ throw OpenSSL_Error("EVP_EncryptInit_ex", ERR_get_error());
+ if(!EVP_DecryptInit_ex(m_decrypt, algo, nullptr, nullptr, nullptr))
+ throw OpenSSL_Error("EVP_DecryptInit_ex", ERR_get_error());
+
+ if(!EVP_CIPHER_CTX_set_padding(m_encrypt, 0))
+ throw OpenSSL_Error("EVP_CIPHER_CTX_set_padding encrypt", ERR_get_error());
+ if(!EVP_CIPHER_CTX_set_padding(m_decrypt, 0))
+ throw OpenSSL_Error("EVP_CIPHER_CTX_set_padding decrypt", ERR_get_error());
+ }
+
+OpenSSL_BlockCipher::~OpenSSL_BlockCipher()
+ {
+ EVP_CIPHER_CTX_cleanup(m_encrypt);
+ EVP_CIPHER_CTX_cleanup(m_decrypt);
+
+ EVP_CIPHER_CTX_free(m_encrypt);
+ EVP_CIPHER_CTX_free(m_decrypt);
+ }
+
+/*
+* Set the key
+*/
+void OpenSSL_BlockCipher::key_schedule(const uint8_t key[], size_t length)
+ {
+ secure_vector<uint8_t> full_key(key, key + length);
+
+ if(m_cipher_name == "TripleDES" && length == 16)
+ {
+ full_key += std::make_pair(key, 8);
+ }
+ else
+ {
+ if(EVP_CIPHER_CTX_set_key_length(m_encrypt, length) == 0 ||
+ EVP_CIPHER_CTX_set_key_length(m_decrypt, length) == 0)
+ throw Invalid_Argument("OpenSSL_BlockCipher: Bad key length for " +
+ m_cipher_name);
+ }
+
+ if(!EVP_EncryptInit_ex(m_encrypt, nullptr, nullptr, full_key.data(), nullptr))
+ throw OpenSSL_Error("EVP_EncryptInit_ex", ERR_get_error());
+ if(!EVP_DecryptInit_ex(m_decrypt, nullptr, nullptr, full_key.data(), nullptr))
+ throw OpenSSL_Error("EVP_DecryptInit_ex", ERR_get_error());
+
+ m_key_set = true;
+ }
+
+/*
+* Return a clone of this object
+*/
+BlockCipher* OpenSSL_BlockCipher::clone() const
+ {
+ return new OpenSSL_BlockCipher(m_cipher_name,
+ EVP_CIPHER_CTX_cipher(m_encrypt),
+ m_cipher_key_spec.minimum_keylength(),
+ m_cipher_key_spec.maximum_keylength(),
+ m_cipher_key_spec.keylength_multiple());
+ }
+
+/*
+* Clear memory of sensitive data
+*/
+void OpenSSL_BlockCipher::clear()
+ {
+ const EVP_CIPHER* algo = EVP_CIPHER_CTX_cipher(m_encrypt);
+
+ m_key_set = false;
+
+ if(!EVP_CIPHER_CTX_cleanup(m_encrypt))
+ throw OpenSSL_Error("EVP_CIPHER_CTX_cleanup encrypt", ERR_get_error());
+ if(!EVP_CIPHER_CTX_cleanup(m_decrypt))
+ throw OpenSSL_Error("EVP_CIPHER_CTX_cleanup decrypt", ERR_get_error());
+ EVP_CIPHER_CTX_init(m_encrypt);
+ EVP_CIPHER_CTX_init(m_decrypt);
+ if(!EVP_EncryptInit_ex(m_encrypt, algo, nullptr, nullptr, nullptr))
+ throw OpenSSL_Error("EVP_EncryptInit_ex", ERR_get_error());
+ if(!EVP_DecryptInit_ex(m_decrypt, algo, nullptr, nullptr, nullptr))
+ throw OpenSSL_Error("EVP_DecryptInit_ex", ERR_get_error());
+ if(!EVP_CIPHER_CTX_set_padding(m_encrypt, 0))
+ throw OpenSSL_Error("EVP_CIPHER_CTX_set_padding encrypt", ERR_get_error());
+ if(!EVP_CIPHER_CTX_set_padding(m_decrypt, 0))
+ throw OpenSSL_Error("EVP_CIPHER_CTX_set_padding decrypt", ERR_get_error());
+ }
+
+}
+
+std::unique_ptr<BlockCipher>
+make_openssl_block_cipher(const std::string& name)
+ {
+#define MAKE_OPENSSL_BLOCK(evp_fn) \
+ std::unique_ptr<BlockCipher>(new OpenSSL_BlockCipher(name, evp_fn()))
+#define MAKE_OPENSSL_BLOCK_KEYLEN(evp_fn, kl_min, kl_max, kl_mod) \
+ std::unique_ptr<BlockCipher>(new OpenSSL_BlockCipher(name, evp_fn(), kl_min, kl_max, kl_mod))
+
+#if defined(BOTAN_HAS_AES) && !defined(OPENSSL_NO_AES)
+ if(name == "AES-128")
+ return MAKE_OPENSSL_BLOCK(EVP_aes_128_ecb);
+ if(name == "AES-192")
+ return MAKE_OPENSSL_BLOCK(EVP_aes_192_ecb);
+ if(name == "AES-256")
+ return MAKE_OPENSSL_BLOCK(EVP_aes_256_ecb);
+#endif
+
+#if defined(BOTAN_HAS_CAMELLIA) && !defined(OPENSSL_NO_CAMELLIA)
+ if(name == "Camellia-128")
+ return MAKE_OPENSSL_BLOCK(EVP_camellia_128_ecb);
+ if(name == "Camellia-192")
+ return MAKE_OPENSSL_BLOCK(EVP_camellia_192_ecb);
+ if(name == "Camellia-256")
+ return MAKE_OPENSSL_BLOCK(EVP_camellia_256_ecb);
+#endif
+
+#if defined(BOTAN_HAS_DES) && !defined(OPENSSL_NO_DES)
+ if(name == "TripleDES")
+ return MAKE_OPENSSL_BLOCK_KEYLEN(EVP_des_ede3_ecb, 16, 24, 8);
+#endif
+
+ return nullptr;
+ }
+
+}
+
diff --git a/comm/third_party/botan/src/lib/prov/openssl/openssl_ec.cpp b/comm/third_party/botan/src/lib/prov/openssl/openssl_ec.cpp
new file mode 100644
index 0000000000..3f691f68ac
--- /dev/null
+++ b/comm/third_party/botan/src/lib/prov/openssl/openssl_ec.cpp
@@ -0,0 +1,383 @@
+/*
+* ECDSA and ECDH via OpenSSL
+* (C) 2015,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/openssl.h>
+
+#if defined(BOTAN_HAS_ECC_PUBLIC_KEY_CRYPTO)
+ #include <botan/der_enc.h>
+ #include <botan/pkcs8.h>
+ #include <botan/internal/pk_ops_impl.h>
+#endif
+
+#if defined(BOTAN_HAS_ECDSA)
+ #include <botan/ecdsa.h>
+#endif
+
+#if defined(BOTAN_HAS_ECDH)
+ #include <botan/ecdh.h>
+#endif
+
+#include <openssl/x509.h>
+#include <openssl/objects.h>
+
+#if !defined(OPENSSL_NO_EC)
+ #include <openssl/ec.h>
+#endif
+
+#if !defined(OPENSSL_NO_ECDSA)
+ #include <openssl/ecdsa.h>
+#endif
+
+#if !defined(OPENSSL_NO_ECDH)
+ #include <openssl/ecdh.h>
+#endif
+
+namespace Botan {
+
+#if defined(BOTAN_HAS_ECC_PUBLIC_KEY_CRYPTO)
+
+namespace {
+
+secure_vector<uint8_t> PKCS8_for_openssl(const EC_PrivateKey& ec)
+ {
+ const PointGFp& pub_key = ec.public_point();
+ const BigInt& priv_key = ec.private_value();
+
+ return DER_Encoder()
+ .start_cons(SEQUENCE)
+ .encode(static_cast<size_t>(1))
+ .encode(BigInt::encode_1363(priv_key, priv_key.bytes()), OCTET_STRING)
+ .start_cons(ASN1_Tag(0), PRIVATE)
+ .raw_bytes(ec.domain().DER_encode(EC_DOMPAR_ENC_OID))
+ .end_cons()
+ .start_cons(ASN1_Tag(1), PRIVATE)
+ .encode(pub_key.encode(PointGFp::UNCOMPRESSED), BIT_STRING)
+ .end_cons()
+ .end_cons()
+ .get_contents();
+ }
+
+int OpenSSL_EC_curve_builtin(int nid)
+ {
+ // the NID macro is still defined even though the curve may not be
+ // supported, so we need to check the list of builtin curves at runtime
+ EC_builtin_curve builtin_curves[100];
+ size_t num = 0;
+
+ if (!(num = EC_get_builtin_curves(builtin_curves, sizeof(builtin_curves))))
+ {
+ return -1;
+ }
+
+ for(size_t i = 0; i < num; ++i)
+ {
+ if(builtin_curves[i].nid == nid)
+ {
+ return nid;
+ }
+ }
+
+ return -1;
+ }
+
+int OpenSSL_EC_nid_for(const OID& oid)
+ {
+ if(oid.empty())
+ return -1;
+
+ const std::string name = oid.to_formatted_string();
+
+ if(name == "secp192r1")
+ return OpenSSL_EC_curve_builtin(NID_X9_62_prime192v1);
+ if(name == "secp224r1")
+ return OpenSSL_EC_curve_builtin(NID_secp224r1);
+ if(name == "secp256r1")
+ return OpenSSL_EC_curve_builtin(NID_X9_62_prime256v1);
+ if(name == "secp384r1")
+ return OpenSSL_EC_curve_builtin(NID_secp384r1);
+ if(name == "secp521r1")
+ return OpenSSL_EC_curve_builtin(NID_secp521r1);
+
+ // OpenSSL 1.0.2 added brainpool curves
+#if OPENSSL_VERSION_NUMBER >= 0x1000200fL
+ if(name == "brainpool160r1")
+ return OpenSSL_EC_curve_builtin(NID_brainpoolP160r1);
+ if(name == "brainpool192r1")
+ return OpenSSL_EC_curve_builtin(NID_brainpoolP192r1);
+ if(name == "brainpool224r1")
+ return OpenSSL_EC_curve_builtin(NID_brainpoolP224r1);
+ if(name == "brainpool256r1")
+ return OpenSSL_EC_curve_builtin(NID_brainpoolP256r1);
+ if(name == "brainpool320r1")
+ return OpenSSL_EC_curve_builtin(NID_brainpoolP320r1);
+ if(name == "brainpool384r1")
+ return OpenSSL_EC_curve_builtin(NID_brainpoolP384r1);
+ if(name == "brainpool512r1")
+ return OpenSSL_EC_curve_builtin(NID_brainpoolP512r1);
+#endif
+
+ return -1;
+ }
+
+}
+
+#endif
+
+#if defined(BOTAN_HAS_ECDSA) && !defined(OPENSSL_NO_ECDSA)
+
+namespace {
+
+class OpenSSL_ECDSA_Verification_Operation final : public PK_Ops::Verification_with_EMSA
+ {
+ public:
+ OpenSSL_ECDSA_Verification_Operation(const ECDSA_PublicKey& ecdsa, const std::string& emsa, int nid) :
+ PK_Ops::Verification_with_EMSA(emsa), m_ossl_ec(::EC_KEY_new(), ::EC_KEY_free)
+ {
+ std::unique_ptr<::EC_GROUP, std::function<void (::EC_GROUP*)>> grp(::EC_GROUP_new_by_curve_name(nid),
+ ::EC_GROUP_free);
+
+ if(!grp)
+ throw OpenSSL_Error("EC_GROUP_new_by_curve_name", ERR_get_error());
+
+ if(!::EC_KEY_set_group(m_ossl_ec.get(), grp.get()))
+ throw OpenSSL_Error("EC_KEY_set_group", ERR_get_error());
+
+ const std::vector<uint8_t> enc = ecdsa.public_point().encode(PointGFp::UNCOMPRESSED);
+ const uint8_t* enc_ptr = enc.data();
+ EC_KEY* key_ptr = m_ossl_ec.get();
+ if(!::o2i_ECPublicKey(&key_ptr, &enc_ptr, enc.size()))
+ throw OpenSSL_Error("o2i_ECPublicKey", ERR_get_error());
+
+ const EC_GROUP* group = ::EC_KEY_get0_group(m_ossl_ec.get());
+ m_order_bits = ::EC_GROUP_get_degree(group);
+ }
+
+ size_t max_input_bits() const override { return m_order_bits; }
+
+ bool with_recovery() const override { return false; }
+
+ bool verify(const uint8_t msg[], size_t msg_len,
+ const uint8_t sig_bytes[], size_t sig_len) override
+ {
+ const size_t order_bytes = (m_order_bits + 7) / 8;
+ if(sig_len != 2 * order_bytes)
+ return false;
+
+ std::unique_ptr<ECDSA_SIG, std::function<void (ECDSA_SIG*)>> sig(nullptr, ECDSA_SIG_free);
+ sig.reset(::ECDSA_SIG_new());
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+ sig->r = BN_bin2bn(sig_bytes , sig_len / 2, sig->r);
+ sig->s = BN_bin2bn(sig_bytes + sig_len / 2, sig_len / 2, sig->s);
+#else
+ BIGNUM* r = BN_bin2bn(sig_bytes , sig_len / 2, nullptr);
+ BIGNUM* s = BN_bin2bn(sig_bytes + sig_len / 2, sig_len / 2, nullptr);
+ if(r == nullptr || s == nullptr)
+ throw OpenSSL_Error("BN_bin2bn sig s", ERR_get_error());
+
+ ECDSA_SIG_set0(sig.get(), r, s);
+#endif
+
+ const int res = ECDSA_do_verify(msg, msg_len, sig.get(), m_ossl_ec.get());
+ if(res < 0)
+ {
+ int err = ERR_get_error();
+
+ bool hard_error = true;
+
+#if defined(EC_R_BAD_SIGNATURE)
+ if(ERR_GET_REASON(err) == EC_R_BAD_SIGNATURE)
+ hard_error = false;
+#endif
+#if defined(EC_R_POINT_AT_INFINITY)
+ if(ERR_GET_REASON(err) == EC_R_POINT_AT_INFINITY)
+ hard_error = false;
+#endif
+#if defined(ECDSA_R_BAD_SIGNATURE)
+ if(ERR_GET_REASON(err) == ECDSA_R_BAD_SIGNATURE)
+ hard_error = false;
+#endif
+
+ if(hard_error)
+ throw OpenSSL_Error("ECDSA_do_verify", err);
+ }
+ return (res == 1);
+ }
+
+ private:
+ std::unique_ptr<EC_KEY, std::function<void (EC_KEY*)>> m_ossl_ec;
+ size_t m_order_bits = 0;
+ };
+
+class OpenSSL_ECDSA_Signing_Operation final : public PK_Ops::Signature_with_EMSA
+ {
+ public:
+ OpenSSL_ECDSA_Signing_Operation(const ECDSA_PrivateKey& ecdsa, const std::string& emsa) :
+ PK_Ops::Signature_with_EMSA(emsa),
+ m_ossl_ec(nullptr, ::EC_KEY_free)
+ {
+ const secure_vector<uint8_t> der = PKCS8_for_openssl(ecdsa);
+ const uint8_t* der_ptr = der.data();
+ m_ossl_ec.reset(d2i_ECPrivateKey(nullptr, &der_ptr, der.size()));
+ if(!m_ossl_ec)
+ throw OpenSSL_Error("d2i_ECPrivateKey", ERR_get_error());
+
+ const EC_GROUP* group = ::EC_KEY_get0_group(m_ossl_ec.get());
+ m_order_bits = ::EC_GROUP_get_degree(group);
+ m_order_bytes = (m_order_bits + 7) / 8;
+ }
+
+ size_t signature_length() const override { return 2*m_order_bytes; }
+
+ secure_vector<uint8_t> raw_sign(const uint8_t msg[], size_t msg_len,
+ RandomNumberGenerator&) override
+ {
+ std::unique_ptr<ECDSA_SIG, std::function<void (ECDSA_SIG*)>> sig(nullptr, ECDSA_SIG_free);
+ sig.reset(::ECDSA_do_sign(msg, msg_len, m_ossl_ec.get()));
+
+ if(!sig)
+ throw OpenSSL_Error("ECDSA_do_sign", ERR_get_error());
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+ const BIGNUM* r = sig->r;
+ const BIGNUM* s = sig->s;
+#else
+ const BIGNUM* r;
+ const BIGNUM* s;
+ ECDSA_SIG_get0(sig.get(), &r, &s);
+#endif
+
+ const size_t r_bytes = BN_num_bytes(r);
+ const size_t s_bytes = BN_num_bytes(s);
+ secure_vector<uint8_t> sigval(2*m_order_bytes);
+ BN_bn2bin(r, &sigval[m_order_bytes - r_bytes]);
+ BN_bn2bin(s, &sigval[2*m_order_bytes - s_bytes]);
+ return sigval;
+ }
+
+ size_t max_input_bits() const override { return m_order_bits; }
+
+ private:
+ std::unique_ptr<EC_KEY, std::function<void (EC_KEY*)>> m_ossl_ec;
+ size_t m_order_bits;
+ size_t m_order_bytes;
+ };
+
+}
+
+std::unique_ptr<PK_Ops::Verification>
+make_openssl_ecdsa_ver_op(const ECDSA_PublicKey& key, const std::string& params)
+ {
+ const int nid = OpenSSL_EC_nid_for(key.domain().get_curve_oid());
+ if(nid < 0)
+ {
+ throw Lookup_Error("OpenSSL ECDSA does not support this curve");
+ }
+
+ try
+ {
+ return std::unique_ptr<PK_Ops::Verification>(new OpenSSL_ECDSA_Verification_Operation(key, params, nid));
+ }
+ catch(OpenSSL_Error&)
+ {
+ throw Lookup_Error("OpenSSL ECDSA does not support this key");
+ }
+ }
+
+std::unique_ptr<PK_Ops::Signature>
+make_openssl_ecdsa_sig_op(const ECDSA_PrivateKey& key, const std::string& params)
+ {
+ const int nid = OpenSSL_EC_nid_for(key.domain().get_curve_oid());
+ if(nid < 0)
+ {
+ throw Lookup_Error("OpenSSL ECDSA does not support this curve");
+ }
+ return std::unique_ptr<PK_Ops::Signature>(new OpenSSL_ECDSA_Signing_Operation(key, params));
+ }
+
+#endif
+
+#if defined(BOTAN_HAS_ECDH) && !defined(OPENSSL_NO_ECDH)
+
+namespace {
+
+class OpenSSL_ECDH_KA_Operation final : public PK_Ops::Key_Agreement_with_KDF
+ {
+ public:
+
+ OpenSSL_ECDH_KA_Operation(const ECDH_PrivateKey& ecdh, const std::string& kdf) :
+ PK_Ops::Key_Agreement_with_KDF(kdf), m_ossl_ec(::EC_KEY_new(), ::EC_KEY_free)
+ {
+ m_value_size = ecdh.domain().get_p_bytes();
+ const secure_vector<uint8_t> der = PKCS8_for_openssl(ecdh);
+ const uint8_t* der_ptr = der.data();
+ m_ossl_ec.reset(d2i_ECPrivateKey(nullptr, &der_ptr, der.size()));
+ if(!m_ossl_ec)
+ throw OpenSSL_Error("d2i_ECPrivateKey", ERR_get_error());
+ }
+
+ size_t agreed_value_size() const override { return m_value_size; }
+
+ secure_vector<uint8_t> raw_agree(const uint8_t w[], size_t w_len) override
+ {
+ const EC_GROUP* group = ::EC_KEY_get0_group(m_ossl_ec.get());
+ const size_t out_len = (::EC_GROUP_get_degree(group) + 7) / 8;
+ secure_vector<uint8_t> out(out_len);
+
+ std::unique_ptr<EC_POINT, std::function<void (EC_POINT*)>> pub_key(
+ ::EC_POINT_new(group), ::EC_POINT_free);
+
+ if(!pub_key)
+ throw OpenSSL_Error("EC_POINT_new", ERR_get_error());
+
+ const int os2ecp_rc =
+ ::EC_POINT_oct2point(group, pub_key.get(), w, w_len, nullptr);
+
+ if(os2ecp_rc != 1)
+ throw OpenSSL_Error("EC_POINT_oct2point", ERR_get_error());
+
+ const int ecdh_rc = ::ECDH_compute_key(out.data(),
+ out.size(),
+ pub_key.get(),
+ m_ossl_ec.get(),
+ /*KDF*/nullptr);
+
+ if(ecdh_rc <= 0)
+ throw OpenSSL_Error("ECDH_compute_key", ERR_get_error());
+
+ const size_t ecdh_sz = static_cast<size_t>(ecdh_rc);
+
+ if(ecdh_sz > out.size())
+ throw Internal_Error("OpenSSL ECDH returned more than requested");
+
+ out.resize(ecdh_sz);
+ return out;
+ }
+
+ private:
+ std::unique_ptr<EC_KEY, std::function<void (EC_KEY*)>> m_ossl_ec;
+ size_t m_value_size;
+ };
+
+}
+
+std::unique_ptr<PK_Ops::Key_Agreement>
+make_openssl_ecdh_ka_op(const ECDH_PrivateKey& key, const std::string& params)
+ {
+ const int nid = OpenSSL_EC_nid_for(key.domain().get_curve_oid());
+ if(nid < 0)
+ {
+ throw Lookup_Error("OpenSSL ECDH does not support this curve");
+ }
+
+ return std::unique_ptr<PK_Ops::Key_Agreement>(new OpenSSL_ECDH_KA_Operation(key, params));
+ }
+
+#endif
+
+}
+
diff --git a/comm/third_party/botan/src/lib/prov/openssl/openssl_hash.cpp b/comm/third_party/botan/src/lib/prov/openssl/openssl_hash.cpp
new file mode 100644
index 0000000000..9a56228c7b
--- /dev/null
+++ b/comm/third_party/botan/src/lib/prov/openssl/openssl_hash.cpp
@@ -0,0 +1,136 @@
+/*
+* OpenSSL Hash Functions
+* (C) 1999-2007,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/hash.h>
+#include <botan/internal/openssl.h>
+#include <openssl/evp.h>
+#include <unordered_map>
+
+namespace Botan {
+
+namespace {
+
+class OpenSSL_HashFunction final : public HashFunction
+ {
+ public:
+ void clear() override
+ {
+ const EVP_MD* algo = EVP_MD_CTX_md(m_md);
+ if(!EVP_DigestInit_ex(m_md, algo, nullptr))
+ throw OpenSSL_Error("EVP_DigestInit_ex", ERR_get_error());
+ }
+
+ std::string provider() const override { return "openssl"; }
+ std::string name() const override { return m_name; }
+
+ HashFunction* clone() const override
+ {
+ const EVP_MD* algo = EVP_MD_CTX_md(m_md);
+ return new OpenSSL_HashFunction(name(), algo);
+ }
+
+ std::unique_ptr<HashFunction> copy_state() const override
+ {
+ std::unique_ptr<OpenSSL_HashFunction> copy(new OpenSSL_HashFunction(m_name, nullptr));
+ EVP_MD_CTX_copy(copy->m_md, m_md);
+ return std::unique_ptr<HashFunction>(copy.release());
+ }
+
+ size_t output_length() const override
+ {
+ return EVP_MD_size(EVP_MD_CTX_md(m_md));
+ }
+
+ size_t hash_block_size() const override
+ {
+ return EVP_MD_block_size(EVP_MD_CTX_md(m_md));
+ }
+
+ OpenSSL_HashFunction(const std::string& name, const EVP_MD* md) : m_name(name)
+ {
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+ m_md = EVP_MD_CTX_create();
+#else
+ m_md = EVP_MD_CTX_new();
+#endif
+
+ if(m_md == nullptr)
+ throw OpenSSL_Error("Can't allocate new context", ERR_get_error());
+ EVP_MD_CTX_init(m_md);
+ if(md && !EVP_DigestInit_ex(m_md, md, nullptr))
+ throw OpenSSL_Error("EVP_DigestInit_ex", ERR_get_error());
+ }
+
+ OpenSSL_HashFunction(EVP_MD_CTX* ctx) : m_md(ctx)
+ {
+ }
+
+ ~OpenSSL_HashFunction()
+ {
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+ EVP_MD_CTX_destroy(m_md);
+#else
+ EVP_MD_CTX_free(m_md);
+#endif
+ }
+
+ private:
+ void add_data(const uint8_t input[], size_t length) override
+ {
+ if(!EVP_DigestUpdate(m_md, input, length))
+ throw OpenSSL_Error("EVP_DigestUpdate", ERR_get_error());
+ }
+
+ void final_result(uint8_t output[]) override
+ {
+ if(!EVP_DigestFinal_ex(m_md, output, nullptr))
+ throw OpenSSL_Error("EVP_DigestFinal_ex", ERR_get_error());
+ const EVP_MD* algo = EVP_MD_CTX_md(m_md);
+ if(!EVP_DigestInit_ex(m_md, algo, nullptr))
+ throw OpenSSL_Error("EVP_DigestInit_ex", ERR_get_error());
+ }
+
+ std::string m_name;
+ EVP_MD_CTX* m_md;
+ };
+
+}
+
+std::unique_ptr<HashFunction>
+make_openssl_hash(const std::string& name)
+ {
+#define MAKE_OPENSSL_HASH(fn) \
+ std::unique_ptr<HashFunction>(new OpenSSL_HashFunction(name, fn ()))
+
+#if defined(BOTAN_HAS_SHA2_32) && !defined(OPENSSL_NO_SHA256)
+ if(name == "SHA-224")
+ return MAKE_OPENSSL_HASH(EVP_sha224);
+ if(name == "SHA-256")
+ return MAKE_OPENSSL_HASH(EVP_sha256);
+#endif
+
+#if defined(BOTAN_HAS_SHA2_64) && !defined(OPENSSL_NO_SHA512)
+ if(name == "SHA-384")
+ return MAKE_OPENSSL_HASH(EVP_sha384);
+ if(name == "SHA-512")
+ return MAKE_OPENSSL_HASH(EVP_sha512);
+#endif
+
+#if defined(BOTAN_HAS_SHA1) && !defined(OPENSSL_NO_SHA)
+ if(name == "SHA-160" || name == "SHA-1" || name == "SHA1")
+ return MAKE_OPENSSL_HASH(EVP_sha1);
+#endif
+
+#if defined(BOTAN_HAS_MD5) && !defined(OPENSSL_NO_MD5)
+ if(name == "MD5")
+ return MAKE_OPENSSL_HASH(EVP_md5);
+ #endif
+
+ return nullptr;
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/prov/openssl/openssl_mode.cpp b/comm/third_party/botan/src/lib/prov/openssl/openssl_mode.cpp
new file mode 100644
index 0000000000..81f8413a2a
--- /dev/null
+++ b/comm/third_party/botan/src/lib/prov/openssl/openssl_mode.cpp
@@ -0,0 +1,233 @@
+/*
+* Cipher Modes via OpenSSL
+* (C) 1999-2010,2015 Jack Lloyd
+* (C) 2017 Alexander Bluhm (genua GmbH)
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/cipher_mode.h>
+#include <botan/internal/rounding.h>
+#include <botan/internal/openssl.h>
+#include <openssl/evp.h>
+#include <limits.h>
+
+namespace Botan {
+
+namespace {
+
+class OpenSSL_Cipher_Mode final : public Cipher_Mode
+ {
+ public:
+ OpenSSL_Cipher_Mode(const std::string& name,
+ const EVP_CIPHER* cipher,
+ Cipher_Dir direction);
+ ~OpenSSL_Cipher_Mode();
+
+ std::string provider() const override { return "openssl"; }
+ std::string name() const override { return m_mode_name; }
+
+ void start_msg(const uint8_t nonce[], size_t nonce_len) override;
+ size_t process(uint8_t msg[], size_t msg_len) override;
+ void finish(secure_vector<uint8_t>& final_block, size_t offset0) override;
+ size_t output_length(size_t input_length) const override;
+ size_t update_granularity() const override;
+ size_t minimum_final_size() const override;
+ size_t default_nonce_length() const override;
+ bool valid_nonce_length(size_t nonce_len) const override;
+ void clear() override;
+ void reset() override;
+ Key_Length_Specification key_spec() const override;
+
+ private:
+ void key_schedule(const uint8_t key[], size_t length) override;
+
+ const std::string m_mode_name;
+ const Cipher_Dir m_direction;
+ size_t m_block_size;
+ EVP_CIPHER_CTX* m_cipher;
+ bool m_key_set;
+ bool m_nonce_set;
+ };
+
+OpenSSL_Cipher_Mode::OpenSSL_Cipher_Mode(const std::string& name,
+ const EVP_CIPHER* algo,
+ Cipher_Dir direction) :
+ m_mode_name(name),
+ m_direction(direction),
+ m_key_set(false),
+ m_nonce_set(false)
+ {
+ m_block_size = EVP_CIPHER_block_size(algo);
+
+ if(EVP_CIPHER_mode(algo) != EVP_CIPH_CBC_MODE)
+ throw Invalid_Argument("OpenSSL_BlockCipher: Non-CBC EVP was passed in");
+
+ m_cipher = EVP_CIPHER_CTX_new();
+ if (m_cipher == nullptr)
+ throw OpenSSL_Error("Can't allocate new context", ERR_get_error());
+
+ EVP_CIPHER_CTX_init(m_cipher);
+ if(!EVP_CipherInit_ex(m_cipher, algo, nullptr, nullptr, nullptr,
+ m_direction == ENCRYPTION ? 1 : 0))
+ throw OpenSSL_Error("EVP_CipherInit_ex", ERR_get_error());
+ if(!EVP_CIPHER_CTX_set_padding(m_cipher, 0))
+ throw OpenSSL_Error("EVP_CIPHER_CTX_set_padding", ERR_get_error());
+ }
+
+OpenSSL_Cipher_Mode::~OpenSSL_Cipher_Mode()
+ {
+ EVP_CIPHER_CTX_free(m_cipher);
+ }
+
+void OpenSSL_Cipher_Mode::start_msg(const uint8_t nonce[], size_t nonce_len)
+ {
+ verify_key_set(m_key_set);
+
+ if(!valid_nonce_length(nonce_len))
+ throw Invalid_IV_Length(name(), nonce_len);
+
+ if(nonce_len)
+ {
+ if(!EVP_CipherInit_ex(m_cipher, nullptr, nullptr, nullptr, nonce, -1))
+ throw OpenSSL_Error("EVP_CipherInit_ex nonce", ERR_get_error());
+ }
+ else if(m_nonce_set == false)
+ {
+ const std::vector<uint8_t> zeros(m_block_size);
+ if(!EVP_CipherInit_ex(m_cipher, nullptr, nullptr, nullptr, zeros.data(), -1))
+ throw OpenSSL_Error("EVP_CipherInit_ex nonce", ERR_get_error());
+ }
+ // otherwise existing CBC state left unchanged
+
+ m_nonce_set = true;
+ }
+
+size_t OpenSSL_Cipher_Mode::process(uint8_t msg[], size_t msg_len)
+ {
+ verify_key_set(m_key_set);
+ BOTAN_STATE_CHECK(m_nonce_set);
+
+ if(msg_len == 0)
+ return 0;
+ if(msg_len > INT_MAX)
+ throw Internal_Error("msg_len overflow");
+ int outl = msg_len;
+ secure_vector<uint8_t> out(outl);
+
+ if(!EVP_CipherUpdate(m_cipher, out.data(), &outl, msg, msg_len))
+ throw OpenSSL_Error("EVP_CipherUpdate", ERR_get_error());
+ copy_mem(msg, out.data(), outl);
+ return outl;
+ }
+
+void OpenSSL_Cipher_Mode::finish(secure_vector<uint8_t>& buffer,
+ size_t offset)
+ {
+ verify_key_set(m_key_set);
+ BOTAN_STATE_CHECK(m_nonce_set);
+
+ BOTAN_ASSERT(buffer.size() >= offset, "Offset ok");
+ uint8_t* buf = buffer.data() + offset;
+ const size_t buf_size = buffer.size() - offset;
+
+ size_t written = process(buf, buf_size);
+ int outl = buf_size - written;
+ secure_vector<uint8_t> out(outl);
+
+ if(!EVP_CipherFinal_ex(m_cipher, out.data(), &outl))
+ throw OpenSSL_Error("EVP_CipherFinal_ex", ERR_get_error());
+ copy_mem(buf + written, out.data(), outl);
+ written += outl;
+ buffer.resize(offset + written);
+ }
+
+size_t OpenSSL_Cipher_Mode::update_granularity() const
+ {
+ return m_block_size * BOTAN_BLOCK_CIPHER_PAR_MULT;
+ }
+
+size_t OpenSSL_Cipher_Mode::minimum_final_size() const
+ {
+ return 0; // no padding
+ }
+
+size_t OpenSSL_Cipher_Mode::default_nonce_length() const
+ {
+ return m_block_size;
+ }
+
+bool OpenSSL_Cipher_Mode::valid_nonce_length(size_t nonce_len) const
+ {
+ return (nonce_len == 0 || nonce_len == m_block_size);
+ }
+
+size_t OpenSSL_Cipher_Mode::output_length(size_t input_length) const
+ {
+ if(input_length == 0)
+ return m_block_size;
+ else
+ return round_up(input_length, m_block_size);
+ }
+
+void OpenSSL_Cipher_Mode::clear()
+ {
+ m_key_set = false;
+ m_nonce_set = false;
+
+ const EVP_CIPHER* algo = EVP_CIPHER_CTX_cipher(m_cipher);
+
+ if(!EVP_CIPHER_CTX_cleanup(m_cipher))
+ throw OpenSSL_Error("EVP_CIPHER_CTX_cleanup", ERR_get_error());
+ EVP_CIPHER_CTX_init(m_cipher);
+ if(!EVP_CipherInit_ex(m_cipher, algo, nullptr, nullptr, nullptr,
+ m_direction == ENCRYPTION ? 1 : 0))
+ throw OpenSSL_Error("EVP_CipherInit_ex clear", ERR_get_error());
+ if(!EVP_CIPHER_CTX_set_padding(m_cipher, 0))
+ throw OpenSSL_Error("EVP_CIPHER_CTX_set_padding clear", ERR_get_error());
+ }
+
+void OpenSSL_Cipher_Mode::reset()
+ {
+ if(!EVP_CipherInit_ex(m_cipher, nullptr, nullptr, nullptr, nullptr, -1))
+ throw OpenSSL_Error("EVP_CipherInit_ex clear", ERR_get_error());
+ m_nonce_set = false;
+ }
+
+Key_Length_Specification OpenSSL_Cipher_Mode::key_spec() const
+ {
+ return Key_Length_Specification(EVP_CIPHER_CTX_key_length(m_cipher));
+ }
+
+void OpenSSL_Cipher_Mode::key_schedule(const uint8_t key[], size_t length)
+ {
+ if(!EVP_CIPHER_CTX_set_key_length(m_cipher, length))
+ throw OpenSSL_Error("EVP_CIPHER_CTX_set_key_length", ERR_get_error());
+ if(!EVP_CipherInit_ex(m_cipher, nullptr, nullptr, key, nullptr, -1))
+ throw OpenSSL_Error("EVP_CipherInit_ex key", ERR_get_error());
+ m_key_set = true;
+ m_nonce_set = false;
+ }
+
+}
+
+Cipher_Mode*
+make_openssl_cipher_mode(const std::string& name, Cipher_Dir direction)
+ {
+#define MAKE_OPENSSL_MODE(evp_fn) \
+ new OpenSSL_Cipher_Mode(name, (evp_fn)(), direction)
+
+#if defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_MODE_CBC) && !defined(OPENSSL_NO_AES)
+ if(name == "AES-128/CBC/NoPadding")
+ return MAKE_OPENSSL_MODE(EVP_aes_128_cbc);
+ if(name == "AES-192/CBC/NoPadding")
+ return MAKE_OPENSSL_MODE(EVP_aes_192_cbc);
+ if(name == "AES-256/CBC/NoPadding")
+ return MAKE_OPENSSL_MODE(EVP_aes_256_cbc);
+#endif
+
+#undef MAKE_OPENSSL_MODE
+ return nullptr;
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/prov/openssl/openssl_rc4.cpp b/comm/third_party/botan/src/lib/prov/openssl/openssl_rc4.cpp
new file mode 100644
index 0000000000..dbda890f82
--- /dev/null
+++ b/comm/third_party/botan/src/lib/prov/openssl/openssl_rc4.cpp
@@ -0,0 +1,93 @@
+/*
+* OpenSSL RC4
+* (C) 1999-2007,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/stream_cipher.h>
+
+#if defined(BOTAN_HAS_OPENSSL) && defined(BOTAN_HAS_RC4)
+
+#include <botan/internal/openssl.h>
+#include <botan/parsing.h>
+#include <botan/exceptn.h>
+#include <openssl/rc4.h>
+
+namespace Botan {
+
+namespace {
+
+class OpenSSL_RC4 final : public StreamCipher
+ {
+ public:
+ void clear() override { clear_mem(&m_rc4, 1); m_key_set = false; }
+
+ std::string provider() const override { return "openssl"; }
+
+ std::string name() const override
+ {
+ switch(m_skip)
+ {
+ case 0:
+ return "RC4";
+ case 256:
+ return "MARK-4";
+ default:
+ return "RC4(" + std::to_string(m_skip) + ")";
+ }
+ }
+
+ StreamCipher* clone() const override { return new OpenSSL_RC4(m_skip); }
+
+ Key_Length_Specification key_spec() const override
+ {
+ return Key_Length_Specification(1, 32);
+ }
+
+ explicit OpenSSL_RC4(size_t skip = 0) : m_skip(skip) { clear(); }
+ ~OpenSSL_RC4() { clear(); }
+
+ void set_iv(const uint8_t*, size_t len) override
+ {
+ if(len > 0)
+ throw Invalid_IV_Length("RC4", len);
+ }
+
+ void seek(uint64_t) override
+ {
+ throw Not_Implemented("RC4 does not support seeking");
+ }
+ private:
+ void cipher(const uint8_t in[], uint8_t out[], size_t length) override
+ {
+ verify_key_set(m_key_set);
+ ::RC4(&m_rc4, length, in, out);
+ }
+
+ void key_schedule(const uint8_t key[], size_t length) override
+ {
+ ::RC4_set_key(&m_rc4, length, key);
+ uint8_t d = 0;
+ for(size_t i = 0; i != m_skip; ++i)
+ ::RC4(&m_rc4, 1, &d, &d);
+ m_key_set = true;
+ }
+
+ size_t m_skip;
+ RC4_KEY m_rc4;
+ bool m_key_set;
+ };
+
+}
+
+std::unique_ptr<StreamCipher>
+make_openssl_rc4(size_t skip)
+ {
+ return std::unique_ptr<StreamCipher>(new OpenSSL_RC4(skip));
+ }
+
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/prov/openssl/openssl_rsa.cpp b/comm/third_party/botan/src/lib/prov/openssl/openssl_rsa.cpp
new file mode 100644
index 0000000000..6744b35b2c
--- /dev/null
+++ b/comm/third_party/botan/src/lib/prov/openssl/openssl_rsa.cpp
@@ -0,0 +1,312 @@
+/*
+* RSA operations provided by OpenSSL
+* (C) 2015 Jack Lloyd
+* (C) 2017 Alexander Bluhm
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/openssl.h>
+
+#if defined(BOTAN_HAS_RSA)
+
+#include <botan/rsa.h>
+#include <botan/rng.h>
+#include <botan/internal/pk_ops_impl.h>
+#include <botan/internal/ct_utils.h>
+
+#include <functional>
+#include <memory>
+#include <cstdlib>
+
+#include <openssl/rsa.h>
+#include <openssl/x509.h>
+#include <openssl/err.h>
+#include <openssl/rand.h>
+#include <limits.h>
+
+namespace Botan {
+
+namespace {
+
+std::pair<int, size_t> get_openssl_enc_pad(const std::string& eme)
+ {
+ if(eme == "Raw")
+ return std::make_pair(RSA_NO_PADDING, 0);
+ else if(eme == "EME-PKCS1-v1_5")
+ return std::make_pair(RSA_PKCS1_PADDING, 11);
+ else if(eme == "OAEP(SHA-1)" || eme == "EME1(SHA-1)")
+ return std::make_pair(RSA_PKCS1_OAEP_PADDING, 41);
+ else
+ throw Lookup_Error("OpenSSL RSA does not support EME " + eme);
+ }
+
+class OpenSSL_RSA_Encryption_Operation final : public PK_Ops::Encryption
+ {
+ public:
+
+ OpenSSL_RSA_Encryption_Operation(const RSA_PublicKey& rsa, int pad, size_t pad_overhead) :
+ m_openssl_rsa(nullptr, ::RSA_free), m_padding(pad)
+ {
+ const std::vector<uint8_t> der = rsa.public_key_bits();
+ const uint8_t* der_ptr = der.data();
+ m_openssl_rsa.reset(::d2i_RSAPublicKey(nullptr, &der_ptr, der.size()));
+ if(!m_openssl_rsa)
+ throw OpenSSL_Error("d2i_RSAPublicKey", ERR_get_error());
+
+ m_bits = 8 * (n_size() - pad_overhead) - 1;
+ }
+
+ size_t ciphertext_length(size_t) const override { return ::RSA_size(m_openssl_rsa.get()); }
+
+ size_t max_input_bits() const override { return m_bits; };
+
+ secure_vector<uint8_t> encrypt(const uint8_t msg[], size_t msg_len,
+ RandomNumberGenerator&) override
+ {
+ const size_t mod_sz = n_size();
+
+ if(msg_len > mod_sz)
+ throw Invalid_Argument("Input too large for RSA key");
+
+ secure_vector<uint8_t> outbuf(mod_sz);
+
+ secure_vector<uint8_t> inbuf;
+
+ if(m_padding == RSA_NO_PADDING)
+ {
+ inbuf.resize(mod_sz);
+ copy_mem(&inbuf[mod_sz - msg_len], msg, msg_len);
+ }
+ else
+ {
+ inbuf.assign(msg, msg + msg_len);
+ }
+
+ int rc = ::RSA_public_encrypt(inbuf.size(), inbuf.data(), outbuf.data(),
+ m_openssl_rsa.get(), m_padding);
+ if(rc < 0)
+ throw OpenSSL_Error("RSA_public_encrypt", ERR_get_error());
+
+ return outbuf;
+ }
+
+ private:
+ size_t n_size() const { return ::RSA_size(m_openssl_rsa.get()); }
+ std::unique_ptr<RSA, std::function<void (RSA*)>> m_openssl_rsa;
+ size_t m_bits = 0;
+ int m_padding = 0;
+ };
+
+class OpenSSL_RSA_Decryption_Operation final : public PK_Ops::Decryption
+ {
+ public:
+
+ OpenSSL_RSA_Decryption_Operation(const RSA_PrivateKey& rsa, int pad) :
+ m_openssl_rsa(nullptr, ::RSA_free), m_padding(pad)
+ {
+ const secure_vector<uint8_t> der = rsa.private_key_bits();
+ const uint8_t* der_ptr = der.data();
+ m_openssl_rsa.reset(d2i_RSAPrivateKey(nullptr, &der_ptr, der.size()));
+ if(!m_openssl_rsa)
+ throw OpenSSL_Error("d2i_RSAPrivateKey", ERR_get_error());
+ }
+
+ size_t plaintext_length(size_t) const override { return ::RSA_size(m_openssl_rsa.get()); }
+
+ secure_vector<uint8_t> decrypt(uint8_t& valid_mask,
+ const uint8_t msg[], size_t msg_len) override
+ {
+ secure_vector<uint8_t> buf(::RSA_size(m_openssl_rsa.get()));
+ int rc = ::RSA_private_decrypt(msg_len, msg, buf.data(), m_openssl_rsa.get(), m_padding);
+ if(rc < 0 || static_cast<size_t>(rc) > buf.size())
+ {
+ valid_mask = 0;
+ buf.resize(0);
+ }
+ else
+ {
+ valid_mask = 0xFF;
+ buf.resize(rc);
+ }
+
+ if(m_padding == RSA_NO_PADDING)
+ {
+ return CT::strip_leading_zeros(buf);
+ }
+
+ return buf;
+ }
+
+ private:
+ std::unique_ptr<RSA, std::function<void (RSA*)>> m_openssl_rsa;
+ int m_padding = 0;
+ };
+
+class OpenSSL_RSA_Verification_Operation final : public PK_Ops::Verification_with_EMSA
+ {
+ public:
+
+ OpenSSL_RSA_Verification_Operation(const RSA_PublicKey& rsa, const std::string& emsa) :
+ PK_Ops::Verification_with_EMSA(emsa),
+ m_openssl_rsa(nullptr, ::RSA_free)
+ {
+ const std::vector<uint8_t> der = rsa.public_key_bits();
+ const uint8_t* der_ptr = der.data();
+ m_openssl_rsa.reset(::d2i_RSAPublicKey(nullptr, &der_ptr, der.size()));
+ if(!m_openssl_rsa)
+ throw OpenSSL_Error("d2i_RSAPublicKey", ERR_get_error());
+ }
+
+ size_t max_input_bits() const override
+ {
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+ return ::BN_num_bits(m_openssl_rsa->n) - 1;
+#else
+ return ::RSA_bits(m_openssl_rsa.get()) - 1;
+#endif
+ }
+
+ bool with_recovery() const override { return true; }
+
+ secure_vector<uint8_t> verify_mr(const uint8_t msg[], size_t msg_len) override
+ {
+ const size_t mod_sz = ::RSA_size(m_openssl_rsa.get());
+
+ if(msg_len > mod_sz)
+ throw Invalid_Argument("OpenSSL RSA verify input too large");
+
+ secure_vector<uint8_t> inbuf(mod_sz);
+
+ if(msg_len > 0)
+ copy_mem(&inbuf[mod_sz - msg_len], msg, msg_len);
+
+ secure_vector<uint8_t> outbuf(mod_sz);
+
+ int rc = ::RSA_public_decrypt(inbuf.size(), inbuf.data(), outbuf.data(),
+ m_openssl_rsa.get(), RSA_NO_PADDING);
+ if(rc < 0)
+ throw Invalid_Argument("RSA_public_decrypt");
+
+ return CT::strip_leading_zeros(outbuf);
+ }
+ private:
+ std::unique_ptr<RSA, std::function<void (RSA*)>> m_openssl_rsa;
+ };
+
+class OpenSSL_RSA_Signing_Operation final : public PK_Ops::Signature_with_EMSA
+ {
+ public:
+
+ OpenSSL_RSA_Signing_Operation(const RSA_PrivateKey& rsa, const std::string& emsa) :
+ PK_Ops::Signature_with_EMSA(emsa),
+ m_openssl_rsa(nullptr, ::RSA_free)
+ {
+ const secure_vector<uint8_t> der = rsa.private_key_bits();
+ const uint8_t* der_ptr = der.data();
+ m_openssl_rsa.reset(d2i_RSAPrivateKey(nullptr, &der_ptr, der.size()));
+ if(!m_openssl_rsa)
+ throw OpenSSL_Error("d2i_RSAPrivateKey", ERR_get_error());
+ }
+
+ size_t signature_length() const override { return ::RSA_size(m_openssl_rsa.get()); }
+
+ secure_vector<uint8_t> raw_sign(const uint8_t msg[], size_t msg_len,
+ RandomNumberGenerator&) override
+ {
+ const size_t mod_sz = ::RSA_size(m_openssl_rsa.get());
+
+ if(msg_len > mod_sz)
+ throw Invalid_Argument("OpenSSL RSA sign input too large");
+
+ secure_vector<uint8_t> inbuf(mod_sz);
+ copy_mem(&inbuf[mod_sz - msg_len], msg, msg_len);
+
+ secure_vector<uint8_t> outbuf(mod_sz);
+
+ int rc = ::RSA_private_encrypt(inbuf.size(), inbuf.data(), outbuf.data(),
+ m_openssl_rsa.get(), RSA_NO_PADDING);
+ if(rc < 0)
+ throw OpenSSL_Error("RSA_private_encrypt", ERR_get_error());
+
+ return outbuf;
+ }
+
+ size_t max_input_bits() const override
+ {
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+ return ::BN_num_bits(m_openssl_rsa->n) - 1;
+#else
+ return ::RSA_bits(m_openssl_rsa.get()) - 1;
+#endif
+ }
+
+ private:
+ std::unique_ptr<RSA, std::function<void (RSA*)>> m_openssl_rsa;
+ };
+
+}
+
+std::unique_ptr<PK_Ops::Encryption>
+make_openssl_rsa_enc_op(const RSA_PublicKey& key, const std::string& params)
+ {
+ auto pad_info = get_openssl_enc_pad(params);
+ return std::unique_ptr<PK_Ops::Encryption>(
+ new OpenSSL_RSA_Encryption_Operation(key, pad_info.first, pad_info.second));
+ }
+
+std::unique_ptr<PK_Ops::Decryption>
+make_openssl_rsa_dec_op(const RSA_PrivateKey& key, const std::string& params)
+ {
+ auto pad_info = get_openssl_enc_pad(params);
+ return std::unique_ptr<PK_Ops::Decryption>(new OpenSSL_RSA_Decryption_Operation(key, pad_info.first));
+ }
+
+std::unique_ptr<PK_Ops::Verification>
+make_openssl_rsa_ver_op(const RSA_PublicKey& key, const std::string& params)
+ {
+ return std::unique_ptr<PK_Ops::Verification>(new OpenSSL_RSA_Verification_Operation(key, params));
+ }
+
+std::unique_ptr<PK_Ops::Signature>
+make_openssl_rsa_sig_op(const RSA_PrivateKey& key, const std::string& params)
+ {
+ return std::unique_ptr<PK_Ops::Signature>(new OpenSSL_RSA_Signing_Operation(key, params));
+ }
+
+std::unique_ptr<RSA_PrivateKey>
+make_openssl_rsa_private_key(RandomNumberGenerator& rng, size_t rsa_bits)
+ {
+ if (rsa_bits > INT_MAX)
+ throw Internal_Error("rsa_bits overflow");
+
+ secure_vector<uint8_t> seed(BOTAN_SYSTEM_RNG_POLL_REQUEST);
+ rng.randomize(seed.data(), seed.size());
+ RAND_seed(seed.data(), seed.size());
+
+ std::unique_ptr<BIGNUM, std::function<void (BIGNUM*)>> bn(BN_new(), BN_free);
+ if(!bn)
+ throw OpenSSL_Error("BN_new", ERR_get_error());
+ if(!BN_set_word(bn.get(), RSA_F4))
+ throw OpenSSL_Error("BN_set_word", ERR_get_error());
+
+ std::unique_ptr<RSA, std::function<void (RSA*)>> rsa(RSA_new(), RSA_free);
+ if(!rsa)
+ throw OpenSSL_Error("RSA_new", ERR_get_error());
+ if(!RSA_generate_key_ex(rsa.get(), rsa_bits, bn.get(), nullptr))
+ throw OpenSSL_Error("RSA_generate_key_ex", ERR_get_error());
+
+ uint8_t* der = nullptr;
+ int bytes = i2d_RSAPrivateKey(rsa.get(), &der);
+ if(bytes < 0)
+ throw OpenSSL_Error("i2d_RSAPrivateKey", ERR_get_error());
+
+ const secure_vector<uint8_t> keydata(der, der + bytes);
+ secure_scrub_memory(der, bytes);
+ std::free(der);
+ return std::unique_ptr<Botan::RSA_PrivateKey>
+ (new RSA_PrivateKey(AlgorithmIdentifier(), keydata));
+ }
+}
+
+#endif // BOTAN_HAS_RSA
diff --git a/comm/third_party/botan/src/lib/prov/pkcs11/info.txt b/comm/third_party/botan/src/lib/prov/pkcs11/info.txt
new file mode 100644
index 0000000000..0f8aab8e09
--- /dev/null
+++ b/comm/third_party/botan/src/lib/prov/pkcs11/info.txt
@@ -0,0 +1,36 @@
+<defines>
+PKCS11 -> 20160219
+</defines>
+
+<requires>
+dyn_load
+rng
+pubkey
+pk_pad
+</requires>
+
+<header:internal>
+p11_mechanism.h
+</header:internal>
+
+<header:external>
+pkcs11.h
+pkcs11f.h
+pkcs11t.h
+</header:external>
+
+<header:public>
+p11.h
+p11_ecc_key.h
+p11_ecdh.h
+p11_ecdsa.h
+p11_object.h
+p11_randomgenerator.h
+p11_rsa.h
+p11_types.h
+p11_x509.h
+
+p11_module.h
+p11_slot.h
+p11_session.h
+</header:public>
diff --git a/comm/third_party/botan/src/lib/prov/pkcs11/p11.cpp b/comm/third_party/botan/src/lib/prov/pkcs11/p11.cpp
new file mode 100644
index 0000000000..334a75caaf
--- /dev/null
+++ b/comm/third_party/botan/src/lib/prov/pkcs11/p11.cpp
@@ -0,0 +1,767 @@
+/*
+* PKCS#11
+* (C) 2016 Daniel Neus, Sirrix AG
+* (C) 2016 Philipp Weber, Sirrix AG
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/p11.h>
+#include <botan/p11_types.h>
+#include <botan/dyn_load.h>
+
+#include <cstdint>
+#include <string>
+#include <functional>
+
+namespace Botan {
+namespace PKCS11 {
+
+ReturnValue* ThrowException = reinterpret_cast< ReturnValue* >(-1);
+
+namespace {
+/// @param function_result Return value of the PKCS11 module function
+/// @param return_value if (`ThrowException`) is passed the function throws an exception, otherwise if a non-NULL pointer is passed:
+/// return_value receives the return value of the PKCS#11 function and no exception is thrown.
+/// @return true if function call was successful, false otherwise
+bool handle_return_value(const CK_RV function_result, ReturnValue* return_value)
+ {
+ if(return_value == ThrowException)
+ {
+ if(static_cast< ReturnValue >(function_result) != ReturnValue::OK)
+ {
+ // caller wants exception
+ throw PKCS11_ReturnError(static_cast< ReturnValue >(function_result));
+ }
+ }
+ else if(return_value != nullptr)
+ {
+ // caller wants return value
+ *return_value = static_cast< ReturnValue >(function_result);
+ }
+
+ return static_cast< ReturnValue >(function_result) == ReturnValue::OK;
+ }
+}
+
+void initialize_token(Slot& slot, const std::string& label, const secure_string& so_pin, const secure_string& pin)
+ {
+ slot.initialize(label, so_pin);
+ set_pin(slot, so_pin, pin);
+ }
+
+void change_pin(Slot& slot, const secure_string& old_pin, const secure_string& new_pin)
+ {
+ Session session(slot, false);
+ session.login(UserType::User, old_pin);
+ session.set_pin(old_pin, new_pin);
+ }
+
+void change_so_pin(Slot& slot, const secure_string& old_so_pin, const secure_string& new_so_pin)
+ {
+ Session session(slot, false);
+ session.login(UserType::SO, old_so_pin);
+ session.set_pin(old_so_pin, new_so_pin);
+ }
+
+void set_pin(Slot& slot, const secure_string& so_pin, const secure_string& pin)
+ {
+ Session session(slot, false);
+ session.login(UserType::SO, so_pin);
+ session.init_pin(pin);
+ }
+
+LowLevel::LowLevel(FunctionListPtr ptr) :
+ m_func_list_ptr(ptr)
+ {
+ if(m_func_list_ptr == nullptr)
+ {
+ throw Invalid_Argument("Invalid PKCS#11 function list ptr");
+ }
+ }
+
+/****************************** General purpose functions ******************************/
+
+bool LowLevel::C_Initialize(VoidPtr init_args,
+ ReturnValue* return_value) const
+ {
+ return handle_return_value(m_func_list_ptr->C_Initialize(init_args), return_value);
+ }
+
+bool LowLevel::C_Finalize(VoidPtr reserved,
+ ReturnValue* return_value) const
+ {
+ return handle_return_value(m_func_list_ptr->C_Finalize(reserved), return_value);
+ }
+
+bool LowLevel::C_GetInfo(Info* info_ptr,
+ ReturnValue* return_value) const
+ {
+ return handle_return_value(m_func_list_ptr->C_GetInfo(info_ptr), return_value);
+ }
+
+bool LowLevel::C_GetFunctionList(Dynamically_Loaded_Library& pkcs11_module, FunctionListPtr* function_list_ptr_ptr,
+ ReturnValue* return_value)
+ {
+ using get_function_list = CK_RV(*)(FunctionListPtr*);
+
+ get_function_list get_function_list_ptr = pkcs11_module.resolve<get_function_list>("C_GetFunctionList");
+
+ return handle_return_value(get_function_list_ptr(function_list_ptr_ptr), return_value);
+ }
+
+/****************************** Slot and token management functions ******************************/
+
+bool LowLevel::C_GetSlotList(Bbool token_present,
+ SlotId* slot_list_ptr,
+ Ulong* count_ptr,
+ ReturnValue* return_value) const
+ {
+ return handle_return_value(m_func_list_ptr->C_GetSlotList(token_present, slot_list_ptr, count_ptr), return_value);
+ }
+
+bool LowLevel::C_GetSlotList(bool token_present,
+ std::vector<SlotId>& slot_ids,
+ ReturnValue* return_value) const
+ {
+ slot_ids.clear();
+
+ // first get available slots
+ Ulong number_slots = 0;
+
+ bool success = C_GetSlotList(token_present, nullptr, &number_slots, return_value);
+
+ if(!success || !number_slots)
+ {
+ return success;
+ }
+
+ // get actual slot ids
+ slot_ids.resize(number_slots);
+ return C_GetSlotList(token_present, slot_ids.data(), &number_slots, return_value);
+ }
+
+bool LowLevel::C_GetSlotInfo(SlotId slot_id,
+ SlotInfo* info_ptr,
+ ReturnValue* return_value) const
+ {
+ return handle_return_value(m_func_list_ptr->C_GetSlotInfo(slot_id, info_ptr), return_value);
+ }
+
+bool LowLevel::C_GetTokenInfo(SlotId slot_id,
+ TokenInfo* info_ptr,
+ ReturnValue* return_value) const
+ {
+ return handle_return_value(m_func_list_ptr->C_GetTokenInfo(slot_id, info_ptr), return_value);
+ }
+
+bool LowLevel::C_WaitForSlotEvent(Flags flags,
+ SlotId* slot_ptr,
+ VoidPtr reserved,
+ ReturnValue* return_value) const
+ {
+ return handle_return_value(m_func_list_ptr->C_WaitForSlotEvent(flags, slot_ptr, reserved), return_value);
+ }
+
+bool LowLevel::C_GetMechanismList(SlotId slot_id,
+ MechanismType* mechanism_list_ptr,
+ Ulong* count_ptr,
+ ReturnValue* return_value) const
+ {
+ return handle_return_value(m_func_list_ptr->C_GetMechanismList(slot_id,
+ reinterpret_cast< CK_MECHANISM_TYPE_PTR >(mechanism_list_ptr), count_ptr), return_value);
+ }
+
+bool LowLevel::C_GetMechanismList(SlotId slot_id,
+ std::vector<MechanismType>& mechanisms,
+ ReturnValue* return_value) const
+ {
+ mechanisms.clear();
+
+ // first get number of mechanisms
+ Ulong number_mechanisms = 0;
+
+ bool success = C_GetMechanismList(slot_id, nullptr, &number_mechanisms, return_value);
+
+ if(!success || !number_mechanisms)
+ {
+ return success;
+ }
+
+ // get actual mechanisms
+ mechanisms.resize(number_mechanisms);
+ return C_GetMechanismList(slot_id, reinterpret_cast< MechanismType* >(mechanisms.data()), &number_mechanisms,
+ return_value);
+ }
+
+bool LowLevel::C_GetMechanismInfo(SlotId slot_id,
+ MechanismType type,
+ MechanismInfo* info_ptr,
+ ReturnValue* return_value) const
+ {
+ return handle_return_value(m_func_list_ptr->C_GetMechanismInfo(slot_id, static_cast< CK_MECHANISM_TYPE >(type),
+ info_ptr), return_value);
+ }
+
+bool LowLevel::C_InitToken(SlotId slot_id,
+ Utf8Char* so_pin_ptr,
+ Ulong so_pin_len,
+ Utf8Char* label_ptr,
+ ReturnValue* return_value) const
+ {
+ return handle_return_value(m_func_list_ptr->C_InitToken(slot_id, so_pin_ptr, so_pin_len, label_ptr), return_value);
+ }
+
+bool LowLevel::C_InitPIN(SessionHandle session,
+ Utf8Char* pin_ptr,
+ Ulong pin_len,
+ ReturnValue* return_value) const
+ {
+ return handle_return_value(m_func_list_ptr->C_InitPIN(session, pin_ptr, pin_len), return_value);
+ }
+
+bool LowLevel::C_SetPIN(SessionHandle session,
+ Utf8Char* old_pin_ptr,
+ Ulong old_len,
+ Utf8Char* new_pin_ptr,
+ Ulong new_len,
+ ReturnValue* return_value) const
+ {
+ return handle_return_value(m_func_list_ptr->C_SetPIN(session, old_pin_ptr, old_len, new_pin_ptr, new_len),
+ return_value);
+ }
+
+/****************************** Session management ******************************/
+
+bool LowLevel::C_OpenSession(SlotId slot_id,
+ Flags flags,
+ VoidPtr application,
+ Notify notify,
+ SessionHandle* session_ptr,
+ ReturnValue* return_value) const
+ {
+ return handle_return_value(m_func_list_ptr->C_OpenSession(slot_id, flags, application, notify, session_ptr),
+ return_value);
+ }
+
+bool LowLevel::C_CloseSession(SessionHandle session,
+ ReturnValue* return_value) const
+ {
+ return handle_return_value(m_func_list_ptr->C_CloseSession(session), return_value);
+ }
+
+bool LowLevel::C_CloseAllSessions(SlotId slot_id,
+ ReturnValue* return_value) const
+ {
+ return handle_return_value(m_func_list_ptr->C_CloseAllSessions(slot_id), return_value);
+ }
+
+bool LowLevel::C_GetSessionInfo(SessionHandle session,
+ SessionInfo* info_ptr,
+ ReturnValue* return_value) const
+ {
+ return handle_return_value(m_func_list_ptr->C_GetSessionInfo(session, info_ptr), return_value);
+ }
+
+bool LowLevel::C_GetOperationState(SessionHandle session,
+ Byte* operation_state_ptr,
+ Ulong* operation_state_len_ptr,
+ ReturnValue* return_value) const
+ {
+ return handle_return_value(m_func_list_ptr->C_GetOperationState(session, operation_state_ptr, operation_state_len_ptr),
+ return_value);
+ }
+
+bool LowLevel::C_SetOperationState(SessionHandle session,
+ Byte* operation_state_ptr,
+ Ulong operation_state_len,
+ ObjectHandle encryption_key,
+ ObjectHandle authentication_key,
+ ReturnValue* return_value) const
+ {
+ return handle_return_value(m_func_list_ptr->C_SetOperationState(session, operation_state_ptr, operation_state_len,
+ encryption_key, authentication_key), return_value);
+ }
+
+bool LowLevel::C_Login(SessionHandle session,
+ UserType user_type,
+ Utf8Char* pin_ptr,
+ Ulong pin_len,
+ ReturnValue* return_value) const
+ {
+ return handle_return_value(m_func_list_ptr->C_Login(session, static_cast< CK_USER_TYPE >(user_type), pin_ptr, pin_len),
+ return_value);
+ }
+
+bool LowLevel::C_Logout(SessionHandle session,
+ ReturnValue* return_value) const
+ {
+ return handle_return_value(m_func_list_ptr->C_Logout(session), return_value);
+ }
+
+/****************************** Object management functions ******************************/
+
+bool LowLevel::C_CreateObject(SessionHandle session,
+ Attribute* attribute_template_ptr,
+ Ulong count,
+ ObjectHandle* object_ptr,
+ ReturnValue* return_value) const
+ {
+ return handle_return_value(m_func_list_ptr->C_CreateObject(session, attribute_template_ptr, count, object_ptr),
+ return_value);
+ }
+
+bool LowLevel::C_CopyObject(SessionHandle session,
+ ObjectHandle object,
+ Attribute* attribute_template_ptr,
+ Ulong count,
+ ObjectHandle* new_object_ptr,
+ ReturnValue* return_value) const
+ {
+ return handle_return_value(m_func_list_ptr->C_CopyObject(session, object, attribute_template_ptr, count,
+ new_object_ptr), return_value);
+ }
+
+bool LowLevel::C_DestroyObject(SessionHandle session,
+ ObjectHandle object,
+ ReturnValue* return_value) const
+ {
+ return handle_return_value(m_func_list_ptr->C_DestroyObject(session, object), return_value);
+ }
+
+bool LowLevel::C_GetObjectSize(SessionHandle session,
+ ObjectHandle object,
+ Ulong* size_ptr,
+ ReturnValue* return_value) const
+ {
+ return handle_return_value(m_func_list_ptr->C_GetObjectSize(session, object, size_ptr), return_value);
+ }
+
+bool LowLevel::C_GetAttributeValue(SessionHandle session,
+ ObjectHandle object,
+ Attribute* attribute_template_ptr,
+ Ulong count,
+ ReturnValue* return_value) const
+ {
+ return handle_return_value(m_func_list_ptr->C_GetAttributeValue(session, object, attribute_template_ptr, count),
+ return_value);
+ }
+
+bool LowLevel::C_SetAttributeValue(SessionHandle session,
+ ObjectHandle object,
+ Attribute* attribute_template_ptr,
+ Ulong count,
+ ReturnValue* return_value) const
+ {
+ return handle_return_value(m_func_list_ptr->C_SetAttributeValue(session, object, attribute_template_ptr, count),
+ return_value);
+ }
+
+bool LowLevel::C_FindObjectsInit(SessionHandle session,
+ Attribute* attribute_template_ptr,
+ Ulong count,
+ ReturnValue* return_value) const
+ {
+ return handle_return_value(m_func_list_ptr->C_FindObjectsInit(session, attribute_template_ptr, count), return_value);
+ }
+
+bool LowLevel::C_FindObjects(SessionHandle session,
+ ObjectHandle* object_ptr,
+ Ulong max_object_count,
+ Ulong* object_count_ptr,
+ ReturnValue* return_value) const
+ {
+ return handle_return_value(m_func_list_ptr->C_FindObjects(session, object_ptr, max_object_count, object_count_ptr),
+ return_value);
+ }
+
+bool LowLevel::C_FindObjectsFinal(SessionHandle session,
+ ReturnValue* return_value) const
+ {
+ return handle_return_value(m_func_list_ptr->C_FindObjectsFinal(session), return_value);
+ }
+
+/****************************** Encryption functions ******************************/
+
+bool LowLevel::C_EncryptInit(SessionHandle session,
+ Mechanism* mechanism_ptr,
+ ObjectHandle key,
+ ReturnValue* return_value) const
+ {
+ return handle_return_value(m_func_list_ptr->C_EncryptInit(session, mechanism_ptr, key), return_value);
+ }
+
+bool LowLevel::C_Encrypt(SessionHandle session,
+ Byte* data_ptr,
+ Ulong data_len,
+ Byte* encrypted_data_ptr,
+ Ulong* encrypted_data_len_ptr,
+ ReturnValue* return_value) const
+ {
+ return handle_return_value(m_func_list_ptr->C_Encrypt(session, data_ptr, data_len, encrypted_data_ptr,
+ encrypted_data_len_ptr), return_value);
+ }
+
+bool LowLevel::C_EncryptUpdate(SessionHandle session,
+ Byte* part_ptr,
+ Ulong part_len,
+ Byte* encrypted_part_ptr,
+ Ulong* encrypted_part_len_ptr,
+ ReturnValue* return_value) const
+ {
+ return handle_return_value(m_func_list_ptr->C_EncryptUpdate(session, part_ptr, part_len, encrypted_part_ptr,
+ encrypted_part_len_ptr), return_value);
+ }
+
+bool LowLevel::C_EncryptFinal(SessionHandle session,
+ Byte* last_encrypted_part_ptr,
+ Ulong* last_encrypted_part_len_ptr,
+ ReturnValue* return_value) const
+ {
+ return handle_return_value(m_func_list_ptr->C_EncryptFinal(session, last_encrypted_part_ptr,
+ last_encrypted_part_len_ptr), return_value);
+ }
+
+/****************************** Decryption functions ******************************/
+
+bool LowLevel::C_DecryptInit(SessionHandle session,
+ Mechanism* mechanism_ptr,
+ ObjectHandle key,
+ ReturnValue* return_value) const
+ {
+ return handle_return_value(m_func_list_ptr->C_DecryptInit(session, mechanism_ptr, key), return_value);
+ }
+
+bool LowLevel::C_Decrypt(SessionHandle session,
+ Byte* encrypted_data_ptr,
+ Ulong encrypted_data_len,
+ Byte* data_ptr,
+ Ulong* data_len_ptr,
+ ReturnValue* return_value) const
+ {
+ return handle_return_value(m_func_list_ptr->C_Decrypt(session, encrypted_data_ptr, encrypted_data_len, data_ptr,
+ data_len_ptr), return_value);
+ }
+
+bool LowLevel::C_DecryptUpdate(SessionHandle session,
+ Byte* encrypted_part_ptr,
+ Ulong encrypted_part_len,
+ Byte* part_ptr,
+ Ulong* part_len_ptr,
+ ReturnValue* return_value) const
+ {
+ return handle_return_value(m_func_list_ptr->C_DecryptUpdate(session, encrypted_part_ptr, encrypted_part_len, part_ptr,
+ part_len_ptr), return_value);
+ }
+
+bool LowLevel::C_DecryptFinal(SessionHandle session,
+ Byte* last_part_ptr,
+ Ulong* last_part_len_ptr,
+ ReturnValue* return_value) const
+ {
+ return handle_return_value(m_func_list_ptr->C_DecryptFinal(session, last_part_ptr, last_part_len_ptr), return_value);
+ }
+
+/****************************** Message digesting functions ******************************/
+
+bool LowLevel::C_DigestInit(SessionHandle session,
+ Mechanism* mechanism,
+ ReturnValue* return_value) const
+ {
+ return handle_return_value(m_func_list_ptr->C_DigestInit(session, mechanism), return_value);
+ }
+
+bool LowLevel::C_Digest(SessionHandle session,
+ Byte* data_ptr,
+ Ulong data_len,
+ Byte* digest_ptr,
+ Ulong* digest_len_ptr,
+ ReturnValue* return_value) const
+ {
+ return handle_return_value(m_func_list_ptr->C_Digest(session, data_ptr, data_len, digest_ptr, digest_len_ptr),
+ return_value);
+ }
+
+bool LowLevel::C_DigestUpdate(SessionHandle session,
+ Byte* part_ptr,
+ Ulong part_len,
+ ReturnValue* return_value) const
+ {
+ return handle_return_value(m_func_list_ptr->C_DigestUpdate(session, part_ptr, part_len), return_value);
+ }
+
+bool LowLevel::C_DigestKey(SessionHandle session,
+ ObjectHandle key,
+ ReturnValue* return_value) const
+ {
+ return handle_return_value(m_func_list_ptr->C_DigestKey(session, key), return_value);
+ }
+
+bool LowLevel::C_DigestFinal(SessionHandle session,
+ Byte* digest_ptr,
+ Ulong* digest_len_ptr,
+ ReturnValue* return_value) const
+ {
+ return handle_return_value(m_func_list_ptr->C_DigestFinal(session, digest_ptr, digest_len_ptr), return_value);
+ }
+
+/****************************** Signing and MACing functions ******************************/
+
+bool LowLevel::C_SignInit(SessionHandle session,
+ Mechanism* mechanism_ptr,
+ ObjectHandle key,
+ ReturnValue* return_value) const
+ {
+ return handle_return_value(m_func_list_ptr->C_SignInit(session, mechanism_ptr, key), return_value);
+ }
+
+bool LowLevel::C_Sign(SessionHandle session,
+ Byte* data_ptr,
+ Ulong data_len,
+ Byte* signature_ptr,
+ Ulong* signature_len_ptr,
+ ReturnValue* return_value) const
+ {
+ return handle_return_value(m_func_list_ptr->C_Sign(session, data_ptr, data_len, signature_ptr, signature_len_ptr),
+ return_value);
+ }
+
+bool LowLevel::C_SignUpdate(SessionHandle session,
+ Byte* part_ptr,
+ Ulong part_len,
+ ReturnValue* return_value) const
+ {
+ return handle_return_value(m_func_list_ptr->C_SignUpdate(session, part_ptr, part_len), return_value);
+ }
+
+bool LowLevel::C_SignFinal(SessionHandle session,
+ Byte* signature_ptr,
+ Ulong* signature_len_ptr,
+ ReturnValue* return_value) const
+ {
+ return handle_return_value(m_func_list_ptr->C_SignFinal(session, signature_ptr, signature_len_ptr), return_value);
+ }
+
+bool LowLevel::C_SignRecoverInit(SessionHandle session,
+ Mechanism* mechanism_ptr,
+ ObjectHandle key,
+ ReturnValue* return_value) const
+ {
+ return handle_return_value(m_func_list_ptr->C_SignRecoverInit(session, mechanism_ptr, key), return_value);
+ }
+
+bool LowLevel::C_SignRecover(SessionHandle session,
+ Byte* data,
+ Ulong data_len,
+ Byte* signature,
+ Ulong* signature_len,
+ ReturnValue* return_value) const
+ {
+ return handle_return_value(m_func_list_ptr->C_SignRecover(session, data, data_len, signature, signature_len),
+ return_value);
+ }
+
+/****************************** Functions for verifying signatures and MACs ******************************/
+
+bool LowLevel::C_VerifyInit(SessionHandle session,
+ Mechanism* mechanism_ptr,
+ ObjectHandle key,
+ ReturnValue* return_value) const
+ {
+ return handle_return_value(m_func_list_ptr->C_VerifyInit(session, mechanism_ptr, key), return_value);
+ }
+
+bool LowLevel::C_Verify(SessionHandle session,
+ Byte* data_ptr,
+ Ulong data_len,
+ Byte* signature_ptr,
+ Ulong signature_len,
+ ReturnValue* return_value) const
+ {
+ return handle_return_value(m_func_list_ptr->C_Verify(session, data_ptr, data_len, signature_ptr, signature_len),
+ return_value);
+ }
+
+bool LowLevel::C_VerifyUpdate(SessionHandle session,
+ Byte* part_ptr,
+ Ulong part_len,
+ ReturnValue* return_value) const
+ {
+ return handle_return_value(m_func_list_ptr->C_VerifyUpdate(session, part_ptr, part_len), return_value);
+ }
+
+bool LowLevel::C_VerifyFinal(SessionHandle session,
+ Byte* signature_ptr,
+ Ulong signature_len,
+ ReturnValue* return_value) const
+ {
+ return handle_return_value(m_func_list_ptr->C_VerifyFinal(session, signature_ptr, signature_len), return_value);
+ }
+
+bool LowLevel::C_VerifyRecoverInit(SessionHandle session,
+ Mechanism* mechanism_ptr,
+ ObjectHandle key,
+ ReturnValue* return_value) const
+ {
+ return handle_return_value(m_func_list_ptr->C_VerifyRecoverInit(session, mechanism_ptr, key), return_value);
+ }
+
+bool LowLevel::C_VerifyRecover(SessionHandle session,
+ Byte* signature_ptr,
+ Ulong signature_len,
+ Byte* data_ptr,
+ Ulong* data_len_ptr,
+ ReturnValue* return_value) const
+ {
+ return handle_return_value(m_func_list_ptr->C_VerifyRecover(session, signature_ptr, signature_len, data_ptr,
+ data_len_ptr), return_value);
+ }
+
+/****************************** Dual-purpose cryptographic functions ******************************/
+
+bool LowLevel::C_DigestEncryptUpdate(SessionHandle session,
+ Byte* part_ptr,
+ Ulong part_len,
+ Byte* encrypted_part_ptr,
+ Ulong* encrypted_part_len_ptr,
+ ReturnValue* return_value) const
+ {
+ return handle_return_value(m_func_list_ptr->C_DigestEncryptUpdate(session, part_ptr, part_len, encrypted_part_ptr,
+ encrypted_part_len_ptr), return_value);
+ }
+
+bool LowLevel::C_DecryptDigestUpdate(SessionHandle session,
+ Byte* encrypted_part_ptr,
+ Ulong encrypted_part_len,
+ Byte* part_ptr,
+ Ulong* part_len_ptr,
+ ReturnValue* return_value) const
+ {
+ return handle_return_value(m_func_list_ptr->C_DecryptDigestUpdate(session, encrypted_part_ptr, encrypted_part_len,
+ part_ptr, part_len_ptr), return_value);
+ }
+
+bool LowLevel::C_SignEncryptUpdate(SessionHandle session,
+ Byte* part_ptr,
+ Ulong part_len,
+ Byte* encrypted_part_ptr,
+ Ulong* encrypted_part_len_ptr,
+ ReturnValue* return_value) const
+ {
+ return handle_return_value(m_func_list_ptr->C_SignEncryptUpdate(session, part_ptr, part_len, encrypted_part_ptr,
+ encrypted_part_len_ptr), return_value);
+ }
+
+bool LowLevel::C_DecryptVerifyUpdate(SessionHandle session,
+ Byte* encrypted_part_ptr,
+ Ulong encrypted_part_len,
+ Byte* part_ptr,
+ Ulong* part_len_ptr,
+ ReturnValue* return_value) const
+ {
+ return handle_return_value(m_func_list_ptr->C_DecryptVerifyUpdate(session, encrypted_part_ptr, encrypted_part_len,
+ part_ptr, part_len_ptr), return_value);
+ }
+
+/****************************** Key management functions ******************************/
+
+bool LowLevel::C_GenerateKey(SessionHandle session,
+ Mechanism* mechanism_ptr,
+ Attribute* attribute_template_ptr,
+ Ulong count,
+ ObjectHandle* key_ptr,
+ ReturnValue* return_value) const
+ {
+ return handle_return_value(m_func_list_ptr->C_GenerateKey(session, mechanism_ptr, attribute_template_ptr, count,
+ key_ptr), return_value);
+ }
+
+bool LowLevel::C_GenerateKeyPair(SessionHandle session,
+ Mechanism* mechanism_ptr,
+ Attribute* public_key_template_ptr,
+ Ulong public_key_attribute_count,
+ Attribute* private_key_template_ptr,
+ Ulong private_key_attribute_count,
+ ObjectHandle* public_key_ptr,
+ ObjectHandle* private_key_ptr,
+ ReturnValue* return_value) const
+ {
+ return handle_return_value(m_func_list_ptr->C_GenerateKeyPair(session, mechanism_ptr, public_key_template_ptr,
+ public_key_attribute_count, private_key_template_ptr,
+ private_key_attribute_count, public_key_ptr, private_key_ptr), return_value);
+ }
+
+bool LowLevel::C_WrapKey(SessionHandle session,
+ Mechanism* mechanism_ptr,
+ ObjectHandle wrapping_key,
+ ObjectHandle key,
+ Byte* wrapped_key_ptr,
+ Ulong* wrapped_key_len_ptr,
+ ReturnValue* return_value) const
+ {
+ return handle_return_value(m_func_list_ptr->C_WrapKey(session, mechanism_ptr, wrapping_key, key, wrapped_key_ptr,
+ wrapped_key_len_ptr), return_value);
+ }
+
+bool LowLevel::C_UnwrapKey(SessionHandle session,
+ Mechanism* mechanism_ptr,
+ ObjectHandle unwrapping_key,
+ Byte* wrapped_key_ptr,
+ Ulong wrapped_key_len,
+ Attribute* attribute_template_ptr,
+ Ulong attribute_count,
+ ObjectHandle* key_ptr,
+ ReturnValue* return_value) const
+ {
+ return handle_return_value(m_func_list_ptr->C_UnwrapKey(session, mechanism_ptr, unwrapping_key, wrapped_key_ptr,
+ wrapped_key_len, attribute_template_ptr,
+ attribute_count, key_ptr), return_value);
+ }
+
+bool LowLevel::C_DeriveKey(SessionHandle session,
+ Mechanism* mechanism_ptr,
+ ObjectHandle base_key,
+ Attribute* attribute_template_ptr,
+ Ulong attribute_count,
+ ObjectHandle* key_ptr,
+ ReturnValue* return_value) const
+ {
+ return handle_return_value(m_func_list_ptr->C_DeriveKey(session, mechanism_ptr, base_key, attribute_template_ptr,
+ attribute_count, key_ptr), return_value);
+ }
+
+/****************************** Random number generation functions ******************************/
+
+bool LowLevel::C_SeedRandom(SessionHandle session,
+ Byte* seed_ptr,
+ Ulong seed_len,
+ ReturnValue* return_value) const
+ {
+ return handle_return_value(m_func_list_ptr->C_SeedRandom(session, seed_ptr, seed_len), return_value);
+ }
+
+bool LowLevel::C_GenerateRandom(SessionHandle session,
+ Byte* random_data_ptr,
+ Ulong random_len,
+ ReturnValue* return_value) const
+ {
+ return handle_return_value(m_func_list_ptr->C_GenerateRandom(session, random_data_ptr, random_len), return_value);
+ }
+
+/****************************** Parallel function management functions ******************************/
+
+bool LowLevel::C_GetFunctionStatus(SessionHandle session,
+ ReturnValue* return_value) const
+ {
+ return handle_return_value(m_func_list_ptr->C_GetFunctionStatus(session), return_value);
+ }
+
+bool LowLevel::C_CancelFunction(SessionHandle session,
+ ReturnValue* return_value) const
+ {
+ return handle_return_value(m_func_list_ptr->C_CancelFunction(session), return_value);
+ }
+
+}
+
+}
diff --git a/comm/third_party/botan/src/lib/prov/pkcs11/p11.h b/comm/third_party/botan/src/lib/prov/pkcs11/p11.h
new file mode 100644
index 0000000000..0fae1c2c58
--- /dev/null
+++ b/comm/third_party/botan/src/lib/prov/pkcs11/p11.h
@@ -0,0 +1,2930 @@
+/*
+* PKCS#11
+* (C) 2016 Daniel Neus, Sirrix AG
+* (C) 2016 Philipp Weber, Sirrix AG
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_P11_H_
+#define BOTAN_P11_H_
+
+#include <botan/secmem.h>
+#include <botan/exceptn.h>
+
+#include <vector>
+#include <string>
+#include <map>
+
+#define CK_PTR *
+
+#if defined(_MSC_VER)
+#define CK_DECLARE_FUNCTION(returnType, name) \
+ returnType __declspec(dllimport) name
+#else
+#define CK_DECLARE_FUNCTION(returnType, name) \
+ returnType name
+#endif
+
+#if defined(_MSC_VER)
+#define CK_DECLARE_FUNCTION_POINTER(returnType, name) \
+ returnType __declspec(dllimport) (* name)
+#else
+#define CK_DECLARE_FUNCTION_POINTER(returnType, name) \
+ returnType (* name)
+#endif
+
+#define CK_CALLBACK_FUNCTION(returnType, name) \
+ returnType (* name)
+
+#ifndef NULL_PTR
+ #define NULL_PTR nullptr
+#endif
+
+#if defined(_MSC_VER)
+ #pragma pack(push, cryptoki, 1)
+#endif
+
+#include "pkcs11.h"
+
+#if defined(_MSC_VER)
+ #pragma pack(pop, cryptoki)
+#endif
+
+static_assert(CRYPTOKI_VERSION_MAJOR == 2 && CRYPTOKI_VERSION_MINOR == 40,
+ "The Botan PKCS#11 module was implemented against PKCS#11 v2.40. Please use the correct PKCS#11 headers.");
+
+namespace Botan {
+
+class Dynamically_Loaded_Library;
+
+namespace PKCS11 {
+
+using secure_string = secure_vector<uint8_t>;
+
+enum class AttributeType : CK_ATTRIBUTE_TYPE
+ {
+ Class = CKA_CLASS,
+ Token = CKA_TOKEN,
+ Private = CKA_PRIVATE,
+ Label = CKA_LABEL,
+ Application = CKA_APPLICATION,
+ Value = CKA_VALUE,
+ ObjectId = CKA_OBJECT_ID,
+ CertificateType = CKA_CERTIFICATE_TYPE,
+ Issuer = CKA_ISSUER,
+ SerialNumber = CKA_SERIAL_NUMBER,
+ AcIssuer = CKA_AC_ISSUER,
+ Owner = CKA_OWNER,
+ AttrTypes = CKA_ATTR_TYPES,
+ Trusted = CKA_TRUSTED,
+ CertificateCategory = CKA_CERTIFICATE_CATEGORY,
+ JavaMidpSecurityDomain = CKA_JAVA_MIDP_SECURITY_DOMAIN,
+ Url = CKA_URL,
+ HashOfSubjectPublicKey = CKA_HASH_OF_SUBJECT_PUBLIC_KEY,
+ HashOfIssuerPublicKey = CKA_HASH_OF_ISSUER_PUBLIC_KEY,
+ NameHashAlgorithm = CKA_NAME_HASH_ALGORITHM,
+ CheckValue = CKA_CHECK_VALUE,
+ KeyType = CKA_KEY_TYPE,
+ Subject = CKA_SUBJECT,
+ Id = CKA_ID,
+ Sensitive = CKA_SENSITIVE,
+ Encrypt = CKA_ENCRYPT,
+ Decrypt = CKA_DECRYPT,
+ Wrap = CKA_WRAP,
+ Unwrap = CKA_UNWRAP,
+ Sign = CKA_SIGN,
+ SignRecover = CKA_SIGN_RECOVER,
+ Verify = CKA_VERIFY,
+ VerifyRecover = CKA_VERIFY_RECOVER,
+ Derive = CKA_DERIVE,
+ StartDate = CKA_START_DATE,
+ EndDate = CKA_END_DATE,
+ Modulus = CKA_MODULUS,
+ ModulusBits = CKA_MODULUS_BITS,
+ PublicExponent = CKA_PUBLIC_EXPONENT,
+ PrivateExponent = CKA_PRIVATE_EXPONENT,
+ Prime1 = CKA_PRIME_1,
+ Prime2 = CKA_PRIME_2,
+ Exponent1 = CKA_EXPONENT_1,
+ Exponent2 = CKA_EXPONENT_2,
+ Coefficient = CKA_COEFFICIENT,
+ PublicKeyInfo = CKA_PUBLIC_KEY_INFO,
+ Prime = CKA_PRIME,
+ Subprime = CKA_SUBPRIME,
+ Base = CKA_BASE,
+ PrimeBits = CKA_PRIME_BITS,
+ SubprimeBits = CKA_SUBPRIME_BITS,
+ SubPrimeBits = CKA_SUB_PRIME_BITS,
+ ValueBits = CKA_VALUE_BITS,
+ ValueLen = CKA_VALUE_LEN,
+ Extractable = CKA_EXTRACTABLE,
+ Local = CKA_LOCAL,
+ NeverExtractable = CKA_NEVER_EXTRACTABLE,
+ AlwaysSensitive = CKA_ALWAYS_SENSITIVE,
+ KeyGenMechanism = CKA_KEY_GEN_MECHANISM,
+ Modifiable = CKA_MODIFIABLE,
+ Copyable = CKA_COPYABLE,
+ Destroyable = CKA_DESTROYABLE,
+ EcdsaParams = CKA_ECDSA_PARAMS,
+ EcParams = CKA_EC_PARAMS,
+ EcPoint = CKA_EC_POINT,
+ SecondaryAuth = CKA_SECONDARY_AUTH,
+ AuthPinFlags = CKA_AUTH_PIN_FLAGS,
+ AlwaysAuthenticate = CKA_ALWAYS_AUTHENTICATE,
+ WrapWithTrusted = CKA_WRAP_WITH_TRUSTED,
+ WrapTemplate = CKA_WRAP_TEMPLATE,
+ UnwrapTemplate = CKA_UNWRAP_TEMPLATE,
+ DeriveTemplate = CKA_DERIVE_TEMPLATE,
+ OtpFormat = CKA_OTP_FORMAT,
+ OtpLength = CKA_OTP_LENGTH,
+ OtpTimeInterval = CKA_OTP_TIME_INTERVAL,
+ OtpUserFriendlyMode = CKA_OTP_USER_FRIENDLY_MODE,
+ OtpChallengeRequirement = CKA_OTP_CHALLENGE_REQUIREMENT,
+ OtpTimeRequirement = CKA_OTP_TIME_REQUIREMENT,
+ OtpCounterRequirement = CKA_OTP_COUNTER_REQUIREMENT,
+ OtpPinRequirement = CKA_OTP_PIN_REQUIREMENT,
+ OtpCounter = CKA_OTP_COUNTER,
+ OtpTime = CKA_OTP_TIME,
+ OtpUserIdentifier = CKA_OTP_USER_IDENTIFIER,
+ OtpServiceIdentifier = CKA_OTP_SERVICE_IDENTIFIER,
+ OtpServiceLogo = CKA_OTP_SERVICE_LOGO,
+ OtpServiceLogoType = CKA_OTP_SERVICE_LOGO_TYPE,
+ Gostr3410Params = CKA_GOSTR3410_PARAMS,
+ Gostr3411Params = CKA_GOSTR3411_PARAMS,
+ Gost28147Params = CKA_GOST28147_PARAMS,
+ HwFeatureType = CKA_HW_FEATURE_TYPE,
+ ResetOnInit = CKA_RESET_ON_INIT,
+ HasReset = CKA_HAS_RESET,
+ PixelX = CKA_PIXEL_X,
+ PixelY = CKA_PIXEL_Y,
+ Resolution = CKA_RESOLUTION,
+ CharRows = CKA_CHAR_ROWS,
+ CharColumns = CKA_CHAR_COLUMNS,
+ Color = CKA_COLOR,
+ BitsPerPixel = CKA_BITS_PER_PIXEL,
+ CharSets = CKA_CHAR_SETS,
+ EncodingMethods = CKA_ENCODING_METHODS,
+ MimeTypes = CKA_MIME_TYPES,
+ MechanismType = CKA_MECHANISM_TYPE,
+ RequiredCmsAttributes = CKA_REQUIRED_CMS_ATTRIBUTES,
+ DefaultCmsAttributes = CKA_DEFAULT_CMS_ATTRIBUTES,
+ SupportedCmsAttributes = CKA_SUPPORTED_CMS_ATTRIBUTES,
+ AllowedMechanisms = CKA_ALLOWED_MECHANISMS,
+ VendorDefined = CKA_VENDOR_DEFINED,
+ };
+
+enum class CertificateType : CK_CERTIFICATE_TYPE
+ {
+ X509 = CKC_X_509,
+ X509AttrCert = CKC_X_509_ATTR_CERT,
+ Wtls = CKC_WTLS,
+ VendorDefined = CKC_VENDOR_DEFINED,
+ };
+
+/// Indicates if a stored certificate is a user certificate for which the corresponding private key is available
+/// on the token ("token user"), a CA certificate ("authority"), or another end-entity certificate ("other entity").
+enum class CertificateCategory : CK_ULONG
+ {
+ Unspecified = CK_CERTIFICATE_CATEGORY_UNSPECIFIED,
+ TokenUser = CK_CERTIFICATE_CATEGORY_TOKEN_USER,
+ Authority = CK_CERTIFICATE_CATEGORY_AUTHORITY,
+ OtherEntity = CK_CERTIFICATE_CATEGORY_OTHER_ENTITY
+ };
+
+enum class KeyDerivation : CK_ULONG
+ {
+ Null = CKD_NULL,
+ Sha1Kdf = CKD_SHA1_KDF,
+ Sha1KdfAsn1 = CKD_SHA1_KDF_ASN1,
+ Sha1KdfConcatenate = CKD_SHA1_KDF_CONCATENATE,
+ Sha224Kdf = CKD_SHA224_KDF,
+ Sha256Kdf = CKD_SHA256_KDF,
+ Sha384Kdf = CKD_SHA384_KDF,
+ Sha512Kdf = CKD_SHA512_KDF,
+ CpdiversifyKdf = CKD_CPDIVERSIFY_KDF,
+ };
+
+enum class Flag : CK_FLAGS
+ {
+ None = 0,
+ TokenPresent = CKF_TOKEN_PRESENT,
+ RemovableDevice = CKF_REMOVABLE_DEVICE,
+ HwSlot = CKF_HW_SLOT,
+ Rng = CKF_RNG,
+ WriteProtected = CKF_WRITE_PROTECTED,
+ LoginRequired = CKF_LOGIN_REQUIRED,
+ UserPinInitialized = CKF_USER_PIN_INITIALIZED,
+ RestoreKeyNotNeeded = CKF_RESTORE_KEY_NOT_NEEDED,
+ ClockOnToken = CKF_CLOCK_ON_TOKEN,
+ ProtectedAuthenticationPath = CKF_PROTECTED_AUTHENTICATION_PATH,
+ DualCryptoOperations = CKF_DUAL_CRYPTO_OPERATIONS,
+ TokenInitialized = CKF_TOKEN_INITIALIZED,
+ SecondaryAuthentication = CKF_SECONDARY_AUTHENTICATION,
+ UserPinCountLow = CKF_USER_PIN_COUNT_LOW,
+ UserPinFinalTry = CKF_USER_PIN_FINAL_TRY,
+ UserPinLocked = CKF_USER_PIN_LOCKED,
+ UserPinToBeChanged = CKF_USER_PIN_TO_BE_CHANGED,
+ SoPinCountLow = CKF_SO_PIN_COUNT_LOW,
+ SoPinFinalTry = CKF_SO_PIN_FINAL_TRY,
+ SoPinLocked = CKF_SO_PIN_LOCKED,
+ SoPinToBeChanged = CKF_SO_PIN_TO_BE_CHANGED,
+ ErrorState = CKF_ERROR_STATE,
+ RwSession = CKF_RW_SESSION,
+ SerialSession = CKF_SERIAL_SESSION,
+ ArrayAttribute = CKF_ARRAY_ATTRIBUTE,
+ Hw = CKF_HW,
+ Encrypt = CKF_ENCRYPT,
+ Decrypt = CKF_DECRYPT,
+ Digest = CKF_DIGEST,
+ Sign = CKF_SIGN,
+ SignRecover = CKF_SIGN_RECOVER,
+ Verify = CKF_VERIFY,
+ VerifyRecover = CKF_VERIFY_RECOVER,
+ Generate = CKF_GENERATE,
+ GenerateKeyPair = CKF_GENERATE_KEY_PAIR,
+ Wrap = CKF_WRAP,
+ Unwrap = CKF_UNWRAP,
+ Derive = CKF_DERIVE,
+ EcFP = CKF_EC_F_P,
+ EcF2m = CKF_EC_F_2M,
+ EcEcparameters = CKF_EC_ECPARAMETERS,
+ EcNamedcurve = CKF_EC_NAMEDCURVE,
+ EcUncompress = CKF_EC_UNCOMPRESS,
+ EcCompress = CKF_EC_COMPRESS,
+ Extension = CKF_EXTENSION,
+ LibraryCantCreateOsThreads = CKF_LIBRARY_CANT_CREATE_OS_THREADS,
+ OsLockingOk = CKF_OS_LOCKING_OK,
+ DontBlock = CKF_DONT_BLOCK,
+ NextOtp = CKF_NEXT_OTP,
+ ExcludeTime = CKF_EXCLUDE_TIME,
+ ExcludeCounter = CKF_EXCLUDE_COUNTER,
+ ExcludeChallenge = CKF_EXCLUDE_CHALLENGE,
+ ExcludePin = CKF_EXCLUDE_PIN,
+ UserFriendlyOtp = CKF_USER_FRIENDLY_OTP,
+ };
+
+inline Flag operator | (Flag a, Flag b)
+ {
+ return static_cast< Flag >(static_cast< CK_FLAGS >(a) | static_cast< CK_FLAGS >(b));
+ }
+
+enum class MGF : CK_RSA_PKCS_MGF_TYPE
+ {
+ Mgf1Sha1 = CKG_MGF1_SHA1,
+ Mgf1Sha256 = CKG_MGF1_SHA256,
+ Mgf1Sha384 = CKG_MGF1_SHA384,
+ Mgf1Sha512 = CKG_MGF1_SHA512,
+ Mgf1Sha224 = CKG_MGF1_SHA224,
+ };
+
+enum class HardwareType : CK_HW_FEATURE_TYPE
+ {
+ MonotonicCounter = CKH_MONOTONIC_COUNTER,
+ Clock = CKH_CLOCK,
+ UserInterface = CKH_USER_INTERFACE,
+ VendorDefined = CKH_VENDOR_DEFINED,
+ };
+
+enum class KeyType : CK_KEY_TYPE
+ {
+ Rsa = CKK_RSA,
+ Dsa = CKK_DSA,
+ Dh = CKK_DH,
+ Ecdsa = CKK_ECDSA,
+ Ec = CKK_EC,
+ X942Dh = CKK_X9_42_DH,
+ Kea = CKK_KEA,
+ GenericSecret = CKK_GENERIC_SECRET,
+ Rc2 = CKK_RC2,
+ Rc4 = CKK_RC4,
+ Des = CKK_DES,
+ Des2 = CKK_DES2,
+ Des3 = CKK_DES3,
+ Cast = CKK_CAST,
+ Cast3 = CKK_CAST3,
+ Cast5 = CKK_CAST5,
+ Cast128 = CKK_CAST128,
+ Rc5 = CKK_RC5,
+ Idea = CKK_IDEA,
+ Skipjack = CKK_SKIPJACK,
+ Baton = CKK_BATON,
+ Juniper = CKK_JUNIPER,
+ Cdmf = CKK_CDMF,
+ Aes = CKK_AES,
+ Blowfish = CKK_BLOWFISH,
+ Twofish = CKK_TWOFISH,
+ Securid = CKK_SECURID,
+ Hotp = CKK_HOTP,
+ Acti = CKK_ACTI,
+ Camellia = CKK_CAMELLIA,
+ Aria = CKK_ARIA,
+ Md5Hmac = CKK_MD5_HMAC,
+ Sha1Hmac = CKK_SHA_1_HMAC,
+ Ripemd128Hmac = CKK_RIPEMD128_HMAC,
+ Ripemd160Hmac = CKK_RIPEMD160_HMAC,
+ Sha256Hmac = CKK_SHA256_HMAC,
+ Sha384Hmac = CKK_SHA384_HMAC,
+ Sha512Hmac = CKK_SHA512_HMAC,
+ Sha224Hmac = CKK_SHA224_HMAC,
+ Seed = CKK_SEED,
+ Gostr3410 = CKK_GOSTR3410,
+ Gostr3411 = CKK_GOSTR3411,
+ Gost28147 = CKK_GOST28147,
+ VendorDefined = CKK_VENDOR_DEFINED,
+ };
+
+enum class MechanismType : CK_MECHANISM_TYPE
+ {
+ RsaPkcsKeyPairGen = CKM_RSA_PKCS_KEY_PAIR_GEN,
+ RsaPkcs = CKM_RSA_PKCS,
+ Rsa9796 = CKM_RSA_9796,
+ RsaX509 = CKM_RSA_X_509,
+ Md2RsaPkcs = CKM_MD2_RSA_PKCS,
+ Md5RsaPkcs = CKM_MD5_RSA_PKCS,
+ Sha1RsaPkcs = CKM_SHA1_RSA_PKCS,
+ Ripemd128RsaPkcs = CKM_RIPEMD128_RSA_PKCS,
+ Ripemd160RsaPkcs = CKM_RIPEMD160_RSA_PKCS,
+ RsaPkcsOaep = CKM_RSA_PKCS_OAEP,
+ RsaX931KeyPairGen = CKM_RSA_X9_31_KEY_PAIR_GEN,
+ RsaX931 = CKM_RSA_X9_31,
+ Sha1RsaX931 = CKM_SHA1_RSA_X9_31,
+ RsaPkcsPss = CKM_RSA_PKCS_PSS,
+ Sha1RsaPkcsPss = CKM_SHA1_RSA_PKCS_PSS,
+ DsaKeyPairGen = CKM_DSA_KEY_PAIR_GEN,
+ Dsa = CKM_DSA,
+ DsaSha1 = CKM_DSA_SHA1,
+ DsaSha224 = CKM_DSA_SHA224,
+ DsaSha256 = CKM_DSA_SHA256,
+ DsaSha384 = CKM_DSA_SHA384,
+ DsaSha512 = CKM_DSA_SHA512,
+ DhPkcsKeyPairGen = CKM_DH_PKCS_KEY_PAIR_GEN,
+ DhPkcsDerive = CKM_DH_PKCS_DERIVE,
+ X942DhKeyPairGen = CKM_X9_42_DH_KEY_PAIR_GEN,
+ X942DhDerive = CKM_X9_42_DH_DERIVE,
+ X942DhHybridDerive = CKM_X9_42_DH_HYBRID_DERIVE,
+ X942MqvDerive = CKM_X9_42_MQV_DERIVE,
+ Sha256RsaPkcs = CKM_SHA256_RSA_PKCS,
+ Sha384RsaPkcs = CKM_SHA384_RSA_PKCS,
+ Sha512RsaPkcs = CKM_SHA512_RSA_PKCS,
+ Sha256RsaPkcsPss = CKM_SHA256_RSA_PKCS_PSS,
+ Sha384RsaPkcsPss = CKM_SHA384_RSA_PKCS_PSS,
+ Sha512RsaPkcsPss = CKM_SHA512_RSA_PKCS_PSS,
+ Sha224RsaPkcs = CKM_SHA224_RSA_PKCS,
+ Sha224RsaPkcsPss = CKM_SHA224_RSA_PKCS_PSS,
+ Sha512224 = CKM_SHA512_224,
+ Sha512224Hmac = CKM_SHA512_224_HMAC,
+ Sha512224HmacGeneral = CKM_SHA512_224_HMAC_GENERAL,
+ Sha512224KeyDerivation = CKM_SHA512_224_KEY_DERIVATION,
+ Sha512256 = CKM_SHA512_256,
+ Sha512256Hmac = CKM_SHA512_256_HMAC,
+ Sha512256HmacGeneral = CKM_SHA512_256_HMAC_GENERAL,
+ Sha512256KeyDerivation = CKM_SHA512_256_KEY_DERIVATION,
+ Sha512T = CKM_SHA512_T,
+ Sha512THmac = CKM_SHA512_T_HMAC,
+ Sha512THmacGeneral = CKM_SHA512_T_HMAC_GENERAL,
+ Sha512TKeyDerivation = CKM_SHA512_T_KEY_DERIVATION,
+ Rc2KeyGen = CKM_RC2_KEY_GEN,
+ Rc2Ecb = CKM_RC2_ECB,
+ Rc2Cbc = CKM_RC2_CBC,
+ Rc2Mac = CKM_RC2_MAC,
+ Rc2MacGeneral = CKM_RC2_MAC_GENERAL,
+ Rc2CbcPad = CKM_RC2_CBC_PAD,
+ Rc4KeyGen = CKM_RC4_KEY_GEN,
+ Rc4 = CKM_RC4,
+ DesKeyGen = CKM_DES_KEY_GEN,
+ DesEcb = CKM_DES_ECB,
+ DesCbc = CKM_DES_CBC,
+ DesMac = CKM_DES_MAC,
+ DesMacGeneral = CKM_DES_MAC_GENERAL,
+ DesCbcPad = CKM_DES_CBC_PAD,
+ Des2KeyGen = CKM_DES2_KEY_GEN,
+ Des3KeyGen = CKM_DES3_KEY_GEN,
+ Des3Ecb = CKM_DES3_ECB,
+ Des3Cbc = CKM_DES3_CBC,
+ Des3Mac = CKM_DES3_MAC,
+ Des3MacGeneral = CKM_DES3_MAC_GENERAL,
+ Des3CbcPad = CKM_DES3_CBC_PAD,
+ Des3CmacGeneral = CKM_DES3_CMAC_GENERAL,
+ Des3Cmac = CKM_DES3_CMAC,
+ CdmfKeyGen = CKM_CDMF_KEY_GEN,
+ CdmfEcb = CKM_CDMF_ECB,
+ CdmfCbc = CKM_CDMF_CBC,
+ CdmfMac = CKM_CDMF_MAC,
+ CdmfMacGeneral = CKM_CDMF_MAC_GENERAL,
+ CdmfCbcPad = CKM_CDMF_CBC_PAD,
+ DesOfb64 = CKM_DES_OFB64,
+ DesOfb8 = CKM_DES_OFB8,
+ DesCfb64 = CKM_DES_CFB64,
+ DesCfb8 = CKM_DES_CFB8,
+ Md2 = CKM_MD2,
+ Md2Hmac = CKM_MD2_HMAC,
+ Md2HmacGeneral = CKM_MD2_HMAC_GENERAL,
+ Md5 = CKM_MD5,
+ Md5Hmac = CKM_MD5_HMAC,
+ Md5HmacGeneral = CKM_MD5_HMAC_GENERAL,
+ Sha1 = CKM_SHA_1,
+ Sha1Hmac = CKM_SHA_1_HMAC,
+ Sha1HmacGeneral = CKM_SHA_1_HMAC_GENERAL,
+ Ripemd128 = CKM_RIPEMD128,
+ Ripemd128Hmac = CKM_RIPEMD128_HMAC,
+ Ripemd128HmacGeneral = CKM_RIPEMD128_HMAC_GENERAL,
+ Ripemd160 = CKM_RIPEMD160,
+ Ripemd160Hmac = CKM_RIPEMD160_HMAC,
+ Ripemd160HmacGeneral = CKM_RIPEMD160_HMAC_GENERAL,
+ Sha256 = CKM_SHA256,
+ Sha256Hmac = CKM_SHA256_HMAC,
+ Sha256HmacGeneral = CKM_SHA256_HMAC_GENERAL,
+ Sha224 = CKM_SHA224,
+ Sha224Hmac = CKM_SHA224_HMAC,
+ Sha224HmacGeneral = CKM_SHA224_HMAC_GENERAL,
+ Sha384 = CKM_SHA384,
+ Sha384Hmac = CKM_SHA384_HMAC,
+ Sha384HmacGeneral = CKM_SHA384_HMAC_GENERAL,
+ Sha512 = CKM_SHA512,
+ Sha512Hmac = CKM_SHA512_HMAC,
+ Sha512HmacGeneral = CKM_SHA512_HMAC_GENERAL,
+ SecuridKeyGen = CKM_SECURID_KEY_GEN,
+ Securid = CKM_SECURID,
+ HotpKeyGen = CKM_HOTP_KEY_GEN,
+ Hotp = CKM_HOTP,
+ Acti = CKM_ACTI,
+ ActiKeyGen = CKM_ACTI_KEY_GEN,
+ CastKeyGen = CKM_CAST_KEY_GEN,
+ CastEcb = CKM_CAST_ECB,
+ CastCbc = CKM_CAST_CBC,
+ CastMac = CKM_CAST_MAC,
+ CastMacGeneral = CKM_CAST_MAC_GENERAL,
+ CastCbcPad = CKM_CAST_CBC_PAD,
+ Cast3KeyGen = CKM_CAST3_KEY_GEN,
+ Cast3Ecb = CKM_CAST3_ECB,
+ Cast3Cbc = CKM_CAST3_CBC,
+ Cast3Mac = CKM_CAST3_MAC,
+ Cast3MacGeneral = CKM_CAST3_MAC_GENERAL,
+ Cast3CbcPad = CKM_CAST3_CBC_PAD,
+ Cast5KeyGen = CKM_CAST5_KEY_GEN,
+ Cast128KeyGen = CKM_CAST128_KEY_GEN,
+ Cast5Ecb = CKM_CAST5_ECB,
+ Cast128Ecb = CKM_CAST128_ECB,
+ Cast5Cbc = CKM_CAST5_CBC,
+ Cast128Cbc = CKM_CAST128_CBC,
+ Cast5Mac = CKM_CAST5_MAC,
+ Cast128Mac = CKM_CAST128_MAC,
+ Cast5MacGeneral = CKM_CAST5_MAC_GENERAL,
+ Cast128MacGeneral = CKM_CAST128_MAC_GENERAL,
+ Cast5CbcPad = CKM_CAST5_CBC_PAD,
+ Cast128CbcPad = CKM_CAST128_CBC_PAD,
+ Rc5KeyGen = CKM_RC5_KEY_GEN,
+ Rc5Ecb = CKM_RC5_ECB,
+ Rc5Cbc = CKM_RC5_CBC,
+ Rc5Mac = CKM_RC5_MAC,
+ Rc5MacGeneral = CKM_RC5_MAC_GENERAL,
+ Rc5CbcPad = CKM_RC5_CBC_PAD,
+ IdeaKeyGen = CKM_IDEA_KEY_GEN,
+ IdeaEcb = CKM_IDEA_ECB,
+ IdeaCbc = CKM_IDEA_CBC,
+ IdeaMac = CKM_IDEA_MAC,
+ IdeaMacGeneral = CKM_IDEA_MAC_GENERAL,
+ IdeaCbcPad = CKM_IDEA_CBC_PAD,
+ GenericSecretKeyGen = CKM_GENERIC_SECRET_KEY_GEN,
+ ConcatenateBaseAndKey = CKM_CONCATENATE_BASE_AND_KEY,
+ ConcatenateBaseAndData = CKM_CONCATENATE_BASE_AND_DATA,
+ ConcatenateDataAndBase = CKM_CONCATENATE_DATA_AND_BASE,
+ XorBaseAndData = CKM_XOR_BASE_AND_DATA,
+ ExtractKeyFromKey = CKM_EXTRACT_KEY_FROM_KEY,
+ Ssl3PreMasterKeyGen = CKM_SSL3_PRE_MASTER_KEY_GEN,
+ Ssl3MasterKeyDerive = CKM_SSL3_MASTER_KEY_DERIVE,
+ Ssl3KeyAndMacDerive = CKM_SSL3_KEY_AND_MAC_DERIVE,
+ Ssl3MasterKeyDeriveDh = CKM_SSL3_MASTER_KEY_DERIVE_DH,
+ TlsPreMasterKeyGen = CKM_TLS_PRE_MASTER_KEY_GEN,
+ TlsMasterKeyDerive = CKM_TLS_MASTER_KEY_DERIVE,
+ TlsKeyAndMacDerive = CKM_TLS_KEY_AND_MAC_DERIVE,
+ TlsMasterKeyDeriveDh = CKM_TLS_MASTER_KEY_DERIVE_DH,
+ TlsPrf = CKM_TLS_PRF,
+ Ssl3Md5Mac = CKM_SSL3_MD5_MAC,
+ Ssl3Sha1Mac = CKM_SSL3_SHA1_MAC,
+ Md5KeyDerivation = CKM_MD5_KEY_DERIVATION,
+ Md2KeyDerivation = CKM_MD2_KEY_DERIVATION,
+ Sha1KeyDerivation = CKM_SHA1_KEY_DERIVATION,
+ Sha256KeyDerivation = CKM_SHA256_KEY_DERIVATION,
+ Sha384KeyDerivation = CKM_SHA384_KEY_DERIVATION,
+ Sha512KeyDerivation = CKM_SHA512_KEY_DERIVATION,
+ Sha224KeyDerivation = CKM_SHA224_KEY_DERIVATION,
+ PbeMd2DesCbc = CKM_PBE_MD2_DES_CBC,
+ PbeMd5DesCbc = CKM_PBE_MD5_DES_CBC,
+ PbeMd5CastCbc = CKM_PBE_MD5_CAST_CBC,
+ PbeMd5Cast3Cbc = CKM_PBE_MD5_CAST3_CBC,
+ PbeMd5Cast5Cbc = CKM_PBE_MD5_CAST5_CBC,
+ PbeMd5Cast128Cbc = CKM_PBE_MD5_CAST128_CBC,
+ PbeSha1Cast5Cbc = CKM_PBE_SHA1_CAST5_CBC,
+ PbeSha1Cast128Cbc = CKM_PBE_SHA1_CAST128_CBC,
+ PbeSha1Rc4128 = CKM_PBE_SHA1_RC4_128,
+ PbeSha1Rc440 = CKM_PBE_SHA1_RC4_40,
+ PbeSha1Des3EdeCbc = CKM_PBE_SHA1_DES3_EDE_CBC,
+ PbeSha1Des2EdeCbc = CKM_PBE_SHA1_DES2_EDE_CBC,
+ PbeSha1Rc2128Cbc = CKM_PBE_SHA1_RC2_128_CBC,
+ PbeSha1Rc240Cbc = CKM_PBE_SHA1_RC2_40_CBC,
+ Pkcs5Pbkd2 = CKM_PKCS5_PBKD2,
+ PbaSha1WithSha1Hmac = CKM_PBA_SHA1_WITH_SHA1_HMAC,
+ WtlsPreMasterKeyGen = CKM_WTLS_PRE_MASTER_KEY_GEN,
+ WtlsMasterKeyDerive = CKM_WTLS_MASTER_KEY_DERIVE,
+ WtlsMasterKeyDeriveDhEcc = CKM_WTLS_MASTER_KEY_DERIVE_DH_ECC,
+ WtlsPrf = CKM_WTLS_PRF,
+ WtlsServerKeyAndMacDerive = CKM_WTLS_SERVER_KEY_AND_MAC_DERIVE,
+ WtlsClientKeyAndMacDerive = CKM_WTLS_CLIENT_KEY_AND_MAC_DERIVE,
+ Tls10MacServer = CKM_TLS10_MAC_SERVER,
+ Tls10MacClient = CKM_TLS10_MAC_CLIENT,
+ Tls12Mac = CKM_TLS12_MAC,
+ Tls12Kdf = CKM_TLS12_KDF,
+ Tls12MasterKeyDerive = CKM_TLS12_MASTER_KEY_DERIVE,
+ Tls12KeyAndMacDerive = CKM_TLS12_KEY_AND_MAC_DERIVE,
+ Tls12MasterKeyDeriveDh = CKM_TLS12_MASTER_KEY_DERIVE_DH,
+ Tls12KeySafeDerive = CKM_TLS12_KEY_SAFE_DERIVE,
+ TlsMac = CKM_TLS_MAC,
+ TlsKdf = CKM_TLS_KDF,
+ KeyWrapLynks = CKM_KEY_WRAP_LYNKS,
+ KeyWrapSetOaep = CKM_KEY_WRAP_SET_OAEP,
+ CmsSig = CKM_CMS_SIG,
+ KipDerive = CKM_KIP_DERIVE,
+ KipWrap = CKM_KIP_WRAP,
+ KipMac = CKM_KIP_MAC,
+ CamelliaKeyGen = CKM_CAMELLIA_KEY_GEN,
+ CamelliaEcb = CKM_CAMELLIA_ECB,
+ CamelliaCbc = CKM_CAMELLIA_CBC,
+ CamelliaMac = CKM_CAMELLIA_MAC,
+ CamelliaMacGeneral = CKM_CAMELLIA_MAC_GENERAL,
+ CamelliaCbcPad = CKM_CAMELLIA_CBC_PAD,
+ CamelliaEcbEncryptData = CKM_CAMELLIA_ECB_ENCRYPT_DATA,
+ CamelliaCbcEncryptData = CKM_CAMELLIA_CBC_ENCRYPT_DATA,
+ CamelliaCtr = CKM_CAMELLIA_CTR,
+ AriaKeyGen = CKM_ARIA_KEY_GEN,
+ AriaEcb = CKM_ARIA_ECB,
+ AriaCbc = CKM_ARIA_CBC,
+ AriaMac = CKM_ARIA_MAC,
+ AriaMacGeneral = CKM_ARIA_MAC_GENERAL,
+ AriaCbcPad = CKM_ARIA_CBC_PAD,
+ AriaEcbEncryptData = CKM_ARIA_ECB_ENCRYPT_DATA,
+ AriaCbcEncryptData = CKM_ARIA_CBC_ENCRYPT_DATA,
+ SeedKeyGen = CKM_SEED_KEY_GEN,
+ SeedEcb = CKM_SEED_ECB,
+ SeedCbc = CKM_SEED_CBC,
+ SeedMac = CKM_SEED_MAC,
+ SeedMacGeneral = CKM_SEED_MAC_GENERAL,
+ SeedCbcPad = CKM_SEED_CBC_PAD,
+ SeedEcbEncryptData = CKM_SEED_ECB_ENCRYPT_DATA,
+ SeedCbcEncryptData = CKM_SEED_CBC_ENCRYPT_DATA,
+ SkipjackKeyGen = CKM_SKIPJACK_KEY_GEN,
+ SkipjackEcb64 = CKM_SKIPJACK_ECB64,
+ SkipjackCbc64 = CKM_SKIPJACK_CBC64,
+ SkipjackOfb64 = CKM_SKIPJACK_OFB64,
+ SkipjackCfb64 = CKM_SKIPJACK_CFB64,
+ SkipjackCfb32 = CKM_SKIPJACK_CFB32,
+ SkipjackCfb16 = CKM_SKIPJACK_CFB16,
+ SkipjackCfb8 = CKM_SKIPJACK_CFB8,
+ SkipjackWrap = CKM_SKIPJACK_WRAP,
+ SkipjackPrivateWrap = CKM_SKIPJACK_PRIVATE_WRAP,
+ SkipjackRelayx = CKM_SKIPJACK_RELAYX,
+ KeaKeyPairGen = CKM_KEA_KEY_PAIR_GEN,
+ KeaKeyDerive = CKM_KEA_KEY_DERIVE,
+ KeaDerive = CKM_KEA_DERIVE,
+ FortezzaTimestamp = CKM_FORTEZZA_TIMESTAMP,
+ BatonKeyGen = CKM_BATON_KEY_GEN,
+ BatonEcb128 = CKM_BATON_ECB128,
+ BatonEcb96 = CKM_BATON_ECB96,
+ BatonCbc128 = CKM_BATON_CBC128,
+ BatonCounter = CKM_BATON_COUNTER,
+ BatonShuffle = CKM_BATON_SHUFFLE,
+ BatonWrap = CKM_BATON_WRAP,
+ EcdsaKeyPairGen = CKM_ECDSA_KEY_PAIR_GEN,
+ EcKeyPairGen = CKM_EC_KEY_PAIR_GEN,
+ Ecdsa = CKM_ECDSA,
+ EcdsaSha1 = CKM_ECDSA_SHA1,
+ EcdsaSha224 = CKM_ECDSA_SHA224,
+ EcdsaSha256 = CKM_ECDSA_SHA256,
+ EcdsaSha384 = CKM_ECDSA_SHA384,
+ EcdsaSha512 = CKM_ECDSA_SHA512,
+ Ecdh1Derive = CKM_ECDH1_DERIVE,
+ Ecdh1CofactorDerive = CKM_ECDH1_COFACTOR_DERIVE,
+ EcmqvDerive = CKM_ECMQV_DERIVE,
+ EcdhAesKeyWrap = CKM_ECDH_AES_KEY_WRAP,
+ RsaAesKeyWrap = CKM_RSA_AES_KEY_WRAP,
+ JuniperKeyGen = CKM_JUNIPER_KEY_GEN,
+ JuniperEcb128 = CKM_JUNIPER_ECB128,
+ JuniperCbc128 = CKM_JUNIPER_CBC128,
+ JuniperCounter = CKM_JUNIPER_COUNTER,
+ JuniperShuffle = CKM_JUNIPER_SHUFFLE,
+ JuniperWrap = CKM_JUNIPER_WRAP,
+ Fasthash = CKM_FASTHASH,
+ AesKeyGen = CKM_AES_KEY_GEN,
+ AesEcb = CKM_AES_ECB,
+ AesCbc = CKM_AES_CBC,
+ AesMac = CKM_AES_MAC,
+ AesMacGeneral = CKM_AES_MAC_GENERAL,
+ AesCbcPad = CKM_AES_CBC_PAD,
+ AesCtr = CKM_AES_CTR,
+ AesGcm = CKM_AES_GCM,
+ AesCcm = CKM_AES_CCM,
+ AesCts = CKM_AES_CTS,
+ AesCmac = CKM_AES_CMAC,
+ AesCmacGeneral = CKM_AES_CMAC_GENERAL,
+ AesXcbcMac = CKM_AES_XCBC_MAC,
+ AesXcbcMac96 = CKM_AES_XCBC_MAC_96,
+ AesGmac = CKM_AES_GMAC,
+ BlowfishKeyGen = CKM_BLOWFISH_KEY_GEN,
+ BlowfishCbc = CKM_BLOWFISH_CBC,
+ TwofishKeyGen = CKM_TWOFISH_KEY_GEN,
+ TwofishCbc = CKM_TWOFISH_CBC,
+ BlowfishCbcPad = CKM_BLOWFISH_CBC_PAD,
+ TwofishCbcPad = CKM_TWOFISH_CBC_PAD,
+ DesEcbEncryptData = CKM_DES_ECB_ENCRYPT_DATA,
+ DesCbcEncryptData = CKM_DES_CBC_ENCRYPT_DATA,
+ Des3EcbEncryptData = CKM_DES3_ECB_ENCRYPT_DATA,
+ Des3CbcEncryptData = CKM_DES3_CBC_ENCRYPT_DATA,
+ AesEcbEncryptData = CKM_AES_ECB_ENCRYPT_DATA,
+ AesCbcEncryptData = CKM_AES_CBC_ENCRYPT_DATA,
+ Gostr3410KeyPairGen = CKM_GOSTR3410_KEY_PAIR_GEN,
+ Gostr3410 = CKM_GOSTR3410,
+ Gostr3410WithGostr3411 = CKM_GOSTR3410_WITH_GOSTR3411,
+ Gostr3410KeyWrap = CKM_GOSTR3410_KEY_WRAP,
+ Gostr3410Derive = CKM_GOSTR3410_DERIVE,
+ Gostr3411 = CKM_GOSTR3411,
+ Gostr3411Hmac = CKM_GOSTR3411_HMAC,
+ Gost28147KeyGen = CKM_GOST28147_KEY_GEN,
+ Gost28147Ecb = CKM_GOST28147_ECB,
+ Gost28147 = CKM_GOST28147,
+ Gost28147Mac = CKM_GOST28147_MAC,
+ Gost28147KeyWrap = CKM_GOST28147_KEY_WRAP,
+ DsaParameterGen = CKM_DSA_PARAMETER_GEN,
+ DhPkcsParameterGen = CKM_DH_PKCS_PARAMETER_GEN,
+ X942DhParameterGen = CKM_X9_42_DH_PARAMETER_GEN,
+ DsaProbablisticParameterGen = CKM_DSA_PROBABLISTIC_PARAMETER_GEN,
+ DsaShaweTaylorParameterGen = CKM_DSA_SHAWE_TAYLOR_PARAMETER_GEN,
+ AesOfb = CKM_AES_OFB,
+ AesCfb64 = CKM_AES_CFB64,
+ AesCfb8 = CKM_AES_CFB8,
+ AesCfb128 = CKM_AES_CFB128,
+ AesCfb1 = CKM_AES_CFB1,
+ AesKeyWrap = CKM_AES_KEY_WRAP,
+ AesKeyWrapPad = CKM_AES_KEY_WRAP_PAD,
+ RsaPkcsTpm11 = CKM_RSA_PKCS_TPM_1_1,
+ RsaPkcsOaepTpm11 = CKM_RSA_PKCS_OAEP_TPM_1_1,
+ VendorDefined = CKM_VENDOR_DEFINED,
+ };
+
+enum class Notification : CK_NOTIFICATION
+ {
+ Surrender = CKN_SURRENDER,
+ OtpChanged = CKN_OTP_CHANGED,
+ };
+
+enum class ObjectClass : CK_OBJECT_CLASS
+ {
+ Data = CKO_DATA,
+ Certificate = CKO_CERTIFICATE,
+ PublicKey = CKO_PUBLIC_KEY,
+ PrivateKey = CKO_PRIVATE_KEY,
+ SecretKey = CKO_SECRET_KEY,
+ HwFeature = CKO_HW_FEATURE,
+ DomainParameters = CKO_DOMAIN_PARAMETERS,
+ Mechanism = CKO_MECHANISM,
+ OtpKey = CKO_OTP_KEY,
+ VendorDefined = CKO_VENDOR_DEFINED,
+ };
+
+enum class PseudoRandom : CK_PKCS5_PBKD2_PSEUDO_RANDOM_FUNCTION_TYPE
+ {
+ Pkcs5Pbkd2HmacSha1 = CKP_PKCS5_PBKD2_HMAC_SHA1,
+ Pkcs5Pbkd2HmacGostr3411 = CKP_PKCS5_PBKD2_HMAC_GOSTR3411,
+ Pkcs5Pbkd2HmacSha224 = CKP_PKCS5_PBKD2_HMAC_SHA224,
+ Pkcs5Pbkd2HmacSha256 = CKP_PKCS5_PBKD2_HMAC_SHA256,
+ Pkcs5Pbkd2HmacSha384 = CKP_PKCS5_PBKD2_HMAC_SHA384,
+ Pkcs5Pbkd2HmacSha512 = CKP_PKCS5_PBKD2_HMAC_SHA512,
+ Pkcs5Pbkd2HmacSha512224 = CKP_PKCS5_PBKD2_HMAC_SHA512_224,
+ Pkcs5Pbkd2HmacSha512256 = CKP_PKCS5_PBKD2_HMAC_SHA512_256,
+ };
+
+enum class SessionState : CK_STATE
+ {
+ RoPublicSession = CKS_RO_PUBLIC_SESSION,
+ RoUserFunctions = CKS_RO_USER_FUNCTIONS,
+ RwPublicSession = CKS_RW_PUBLIC_SESSION,
+ RwUserFunctions = CKS_RW_USER_FUNCTIONS,
+ RwSoFunctions = CKS_RW_SO_FUNCTIONS,
+ };
+
+enum class ReturnValue : CK_RV
+ {
+ OK = CKR_OK,
+ Cancel = CKR_CANCEL,
+ HostMemory = CKR_HOST_MEMORY,
+ SlotIdInvalid = CKR_SLOT_ID_INVALID,
+ GeneralError = CKR_GENERAL_ERROR,
+ FunctionFailed = CKR_FUNCTION_FAILED,
+ ArgumentsBad = CKR_ARGUMENTS_BAD,
+ NoEvent = CKR_NO_EVENT,
+ NeedToCreateThreads = CKR_NEED_TO_CREATE_THREADS,
+ CantLock = CKR_CANT_LOCK,
+ AttributeReadOnly = CKR_ATTRIBUTE_READ_ONLY,
+ AttributeSensitive = CKR_ATTRIBUTE_SENSITIVE,
+ AttributeTypeInvalid = CKR_ATTRIBUTE_TYPE_INVALID,
+ AttributeValueInvalid = CKR_ATTRIBUTE_VALUE_INVALID,
+ ActionProhibited = CKR_ACTION_PROHIBITED,
+ DataInvalid = CKR_DATA_INVALID,
+ DataLenRange = CKR_DATA_LEN_RANGE,
+ DeviceError = CKR_DEVICE_ERROR,
+ DeviceMemory = CKR_DEVICE_MEMORY,
+ DeviceRemoved = CKR_DEVICE_REMOVED,
+ EncryptedDataInvalid = CKR_ENCRYPTED_DATA_INVALID,
+ EncryptedDataLenRange = CKR_ENCRYPTED_DATA_LEN_RANGE,
+ FunctionCanceled = CKR_FUNCTION_CANCELED,
+ FunctionNotParallel = CKR_FUNCTION_NOT_PARALLEL,
+ FunctionNotSupported = CKR_FUNCTION_NOT_SUPPORTED,
+ KeyHandleInvalid = CKR_KEY_HANDLE_INVALID,
+ KeySizeRange = CKR_KEY_SIZE_RANGE,
+ KeyTypeInconsistent = CKR_KEY_TYPE_INCONSISTENT,
+ KeyNotNeeded = CKR_KEY_NOT_NEEDED,
+ KeyChanged = CKR_KEY_CHANGED,
+ KeyNeeded = CKR_KEY_NEEDED,
+ KeyIndigestible = CKR_KEY_INDIGESTIBLE,
+ KeyFunctionNotPermitted = CKR_KEY_FUNCTION_NOT_PERMITTED,
+ KeyNotWrappable = CKR_KEY_NOT_WRAPPABLE,
+ KeyUnextractable = CKR_KEY_UNEXTRACTABLE,
+ MechanismInvalid = CKR_MECHANISM_INVALID,
+ MechanismParamInvalid = CKR_MECHANISM_PARAM_INVALID,
+ ObjectHandleInvalid = CKR_OBJECT_HANDLE_INVALID,
+ OperationActive = CKR_OPERATION_ACTIVE,
+ OperationNotInitialized = CKR_OPERATION_NOT_INITIALIZED,
+ PinIncorrect = CKR_PIN_INCORRECT,
+ PinInvalid = CKR_PIN_INVALID,
+ PinLenRange = CKR_PIN_LEN_RANGE,
+ PinExpired = CKR_PIN_EXPIRED,
+ PinLocked = CKR_PIN_LOCKED,
+ SessionClosed = CKR_SESSION_CLOSED,
+ SessionCount = CKR_SESSION_COUNT,
+ SessionHandleInvalid = CKR_SESSION_HANDLE_INVALID,
+ SessionParallelNotSupported = CKR_SESSION_PARALLEL_NOT_SUPPORTED,
+ SessionReadOnly = CKR_SESSION_READ_ONLY,
+ SessionExists = CKR_SESSION_EXISTS,
+ SessionReadOnlyExists = CKR_SESSION_READ_ONLY_EXISTS,
+ SessionReadWriteSoExists = CKR_SESSION_READ_WRITE_SO_EXISTS,
+ SignatureInvalid = CKR_SIGNATURE_INVALID,
+ SignatureLenRange = CKR_SIGNATURE_LEN_RANGE,
+ TemplateIncomplete = CKR_TEMPLATE_INCOMPLETE,
+ TemplateInconsistent = CKR_TEMPLATE_INCONSISTENT,
+ TokenNotPresent = CKR_TOKEN_NOT_PRESENT,
+ TokenNotRecognized = CKR_TOKEN_NOT_RECOGNIZED,
+ TokenWriteProtected = CKR_TOKEN_WRITE_PROTECTED,
+ UnwrappingKeyHandleInvalid = CKR_UNWRAPPING_KEY_HANDLE_INVALID,
+ UnwrappingKeySizeRange = CKR_UNWRAPPING_KEY_SIZE_RANGE,
+ UnwrappingKeyTypeInconsistent = CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT,
+ UserAlreadyLoggedIn = CKR_USER_ALREADY_LOGGED_IN,
+ UserNotLoggedIn = CKR_USER_NOT_LOGGED_IN,
+ UserPinNotInitialized = CKR_USER_PIN_NOT_INITIALIZED,
+ UserTypeInvalid = CKR_USER_TYPE_INVALID,
+ UserAnotherAlreadyLoggedIn = CKR_USER_ANOTHER_ALREADY_LOGGED_IN,
+ UserTooManyTypes = CKR_USER_TOO_MANY_TYPES,
+ WrappedKeyInvalid = CKR_WRAPPED_KEY_INVALID,
+ WrappedKeyLenRange = CKR_WRAPPED_KEY_LEN_RANGE,
+ WrappingKeyHandleInvalid = CKR_WRAPPING_KEY_HANDLE_INVALID,
+ WrappingKeySizeRange = CKR_WRAPPING_KEY_SIZE_RANGE,
+ WrappingKeyTypeInconsistent = CKR_WRAPPING_KEY_TYPE_INCONSISTENT,
+ RandomSeedNotSupported = CKR_RANDOM_SEED_NOT_SUPPORTED,
+ RandomNoRng = CKR_RANDOM_NO_RNG,
+ DomainParamsInvalid = CKR_DOMAIN_PARAMS_INVALID,
+ CurveNotSupported = CKR_CURVE_NOT_SUPPORTED,
+ BufferTooSmall = CKR_BUFFER_TOO_SMALL,
+ SavedStateInvalid = CKR_SAVED_STATE_INVALID,
+ InformationSensitive = CKR_INFORMATION_SENSITIVE,
+ StateUnsaveable = CKR_STATE_UNSAVEABLE,
+ CryptokiNotInitialized = CKR_CRYPTOKI_NOT_INITIALIZED,
+ CryptokiAlreadyInitialized = CKR_CRYPTOKI_ALREADY_INITIALIZED,
+ MutexBad = CKR_MUTEX_BAD,
+ MutexNotLocked = CKR_MUTEX_NOT_LOCKED,
+ NewPinMode = CKR_NEW_PIN_MODE,
+ NextOtp = CKR_NEXT_OTP,
+ ExceededMaxIterations = CKR_EXCEEDED_MAX_ITERATIONS,
+ FipsSelfTestFailed = CKR_FIPS_SELF_TEST_FAILED,
+ LibraryLoadFailed = CKR_LIBRARY_LOAD_FAILED,
+ PinTooWeak = CKR_PIN_TOO_WEAK,
+ PublicKeyInvalid = CKR_PUBLIC_KEY_INVALID,
+ FunctionRejected = CKR_FUNCTION_REJECTED,
+ VendorDefined = CKR_VENDOR_DEFINED,
+ };
+
+enum class UserType : CK_USER_TYPE
+ {
+ SO = CKU_SO,
+ User = CKU_USER,
+ ContextSpecific = CKU_CONTEXT_SPECIFIC,
+ };
+
+enum class PublicPointEncoding : uint32_t
+ {
+ Raw,
+ Der
+ };
+
+using FunctionListPtr = CK_FUNCTION_LIST_PTR;
+using VoidPtr = CK_VOID_PTR;
+using C_InitializeArgs = CK_C_INITIALIZE_ARGS;
+using CreateMutex = CK_CREATEMUTEX;
+using DestroyMutex = CK_DESTROYMUTEX;
+using LockMutex = CK_LOCKMUTEX;
+using UnlockMutex = CK_UNLOCKMUTEX;
+using Flags = CK_FLAGS;
+using Info = CK_INFO;
+using Bbool = CK_BBOOL;
+using SlotId = CK_SLOT_ID;
+using Ulong = CK_ULONG;
+using SlotInfo = CK_SLOT_INFO;
+using TokenInfo = CK_TOKEN_INFO;
+using Mechanism = CK_MECHANISM;
+using MechanismInfo = CK_MECHANISM_INFO;
+using Utf8Char = CK_UTF8CHAR;
+using Notify = CK_NOTIFY;
+using SessionHandle = CK_SESSION_HANDLE;
+using SessionInfo = CK_SESSION_INFO;
+using Attribute = CK_ATTRIBUTE;
+using ObjectHandle = CK_OBJECT_HANDLE;
+using Byte = CK_BYTE;
+using RsaPkcsOaepParams = CK_RSA_PKCS_OAEP_PARAMS;
+using RsaPkcsPssParams = CK_RSA_PKCS_PSS_PARAMS;
+using Ecdh1DeriveParams = CK_ECDH1_DERIVE_PARAMS;
+using Date = CK_DATE;
+
+BOTAN_PUBLIC_API(2,0) extern ReturnValue* ThrowException;
+
+const Bbool True = CK_TRUE;
+const Bbool False = CK_FALSE;
+
+inline Flags flags(Flag flags)
+ {
+ return static_cast<Flags>(flags);
+ }
+
+class Slot;
+
+/**
+* Initializes a token
+* @param slot The slot with the attached token that should be initialized
+* @param label The token label
+* @param so_pin PIN of the security officer. Will be set if the token is uninitialized other this has to be the current SO_PIN
+* @param pin The user PIN that will be set
+*/
+BOTAN_PUBLIC_API(2,0) void initialize_token(Slot& slot, const std::string& label, const secure_string& so_pin,
+ const secure_string& pin);
+
+/**
+* Change PIN with old PIN to new PIN
+* @param slot The slot with the attached token
+* @param old_pin The old user PIN
+* @param new_pin The new user PIN
+*/
+
+BOTAN_PUBLIC_API(2,0) void change_pin(Slot& slot, const secure_string& old_pin, const secure_string& new_pin);
+
+/**
+* Change SO_PIN with old SO_PIN to new SO_PIN
+* @param slot The slot with the attached token
+* @param old_so_pin The old SO_PIN
+* @param new_so_pin The new SO_PIN
+*/
+BOTAN_PUBLIC_API(2,0) void change_so_pin(Slot& slot, const secure_string& old_so_pin, const secure_string& new_so_pin);
+
+/**
+* Sets user PIN with SO_PIN
+* @param slot The slot with the attached token
+* @param so_pin PIN of the security officer
+* @param pin The user PIN that should be set
+*/
+BOTAN_PUBLIC_API(2,0) void set_pin(Slot& slot, const secure_string& so_pin, const secure_string& pin);
+
+/// Provides access to all PKCS#11 functions
+class BOTAN_PUBLIC_API(2,0) LowLevel
+ {
+ public:
+ /// @param ptr the functon list pointer to use. Can be retrieved via `LowLevel::C_GetFunctionList`
+ explicit LowLevel(FunctionListPtr ptr);
+
+ /****************************** General purpose functions ******************************/
+
+ /**
+ * C_Initialize initializes the Cryptoki library.
+ * @param init_args if this is not nullptr, it gets cast to (`C_InitializeArgs`) and dereferenced
+ * @param return_value default value (`ThrowException`): throw exception on error.
+ * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+ * At least the following PKCS#11 return values may be returned:
+ * \li ArgumentsBad \li CantLock \li CryptokiAlreadyInitialized
+ * \li FunctionFailed \li GeneralError \li HostMemory
+ * \li NeedToCreateThreads \li OK
+ * @return true on success, false otherwise
+ */
+ bool C_Initialize(VoidPtr init_args,
+ ReturnValue* return_value = ThrowException) const;
+
+ /**
+ * C_Finalize indicates that an application is done with the Cryptoki library.
+ * @param reserved reserved. Should be nullptr
+ * @param return_value default value (`ThrowException`): throw exception on error.
+ * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+ * At least the following PKCS#11 return values may be returned:
+ * \li ArgumentsBad \li CryptokiNotInitialized \li FunctionFailed
+ * \li GeneralError \li HostMemory \li OK
+ * @return true on success, false otherwise
+ */
+ bool C_Finalize(VoidPtr reserved,
+ ReturnValue* return_value = ThrowException) const;
+
+ /**
+ * C_GetInfo returns general information about Cryptoki.
+ * @param info_ptr location that receives information
+ * @param return_value default value (`ThrowException`): throw exception on error.
+ * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+ * At least the following PKCS#11 return values may be returned:
+ * \li ArgumentsBad \li CryptokiNotInitialized \li FunctionFailed
+ * \li GeneralError \li HostMemory \li OK
+ * @return true on success, false otherwise
+ */
+ bool C_GetInfo(Info* info_ptr,
+ ReturnValue* return_value = ThrowException) const;
+
+ /**
+ * C_GetFunctionList returns the function list.
+ * @param pkcs11_module The PKCS#11 module
+ * @param function_list_ptr_ptr receives pointer to function list
+ * @param return_value default value (`ThrowException`): throw exception on error.
+ * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+ * At least the following PKCS#11 return values may be returned:
+ * \li ArgumentsBad \li FunctionFailed \li GeneralError
+ * \li HostMemory \li OK
+ * @return true on success, false otherwise
+ */
+ static bool C_GetFunctionList(Dynamically_Loaded_Library& pkcs11_module, FunctionListPtr* function_list_ptr_ptr,
+ ReturnValue* return_value = ThrowException);
+
+ /****************************** Slot and token management functions ******************************/
+
+ /**
+ * C_GetSlotList obtains a list of slots in the system.
+ * @param token_present only slots with tokens
+ * @param slot_list_ptr receives array of slot IDs
+ * @param count_ptr receives number of slots
+ * @param return_value default value (`ThrowException`): throw exception on error.
+ * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+ * At least the following PKCS#11 return values may be returned:
+ * \li ArgumentsBad \li BufferTooSmall \li CryptokiNotInitialized
+ * \li FunctionFailed \li GeneralError \li HostMemory
+ * \li OK
+ * @return true on success, false otherwise
+ */
+ bool C_GetSlotList(Bbool token_present,
+ SlotId* slot_list_ptr,
+ Ulong* count_ptr,
+ ReturnValue* return_value = ThrowException) const;
+
+ /**
+ * C_GetSlotList obtains a list of slots in the system.
+ * @param token_present only slots with tokens
+ * @param slot_ids receives vector of slot IDs
+ * @param return_value default value (`ThrowException`): throw exception on error.
+ * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+ * At least the following PKCS#11 return values may be returned:
+ * \li ArgumentsBad \li BufferTooSmall \li CryptokiNotInitialized
+ * \li FunctionFailed \li GeneralError \li HostMemory
+ * \li OK
+ * @return true on success, false otherwise
+ */
+ bool C_GetSlotList(bool token_present,
+ std::vector<SlotId>& slot_ids,
+ ReturnValue* return_value = ThrowException) const;
+
+ /**
+ * C_GetSlotInfo obtains information about a particular slot in the system.
+ * @param slot_id the ID of the slot
+ * @param info_ptr receives the slot information
+ * @param return_value default value (`ThrowException`): throw exception on error.
+ * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+ * At least the following PKCS#11 return values may be returned:
+ * \li ArgumentsBad \li CryptokiNotInitialized \li DeviceError
+ * \li FunctionFailed \li GeneralError \li HostMemory
+ * \li OK \li SlotIdInvalid
+ * @return true on success, false otherwise
+ */
+ bool C_GetSlotInfo(SlotId slot_id,
+ SlotInfo* info_ptr,
+ ReturnValue* return_value = ThrowException) const;
+
+ /**
+ * C_GetTokenInfo obtains information about a particular token in the system.
+ * @param slot_id ID of the token's slot
+ * @param info_ptr receives the token information
+ * @param return_value default value (`ThrowException`): throw exception on error.
+ * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+ * At least the following PKCS#11 return values may be returned:
+ * \li CryptokiNotInitialized \li DeviceError \li DeviceMemory
+ * \li DeviceRemoved \li FunctionFailed \li GeneralError
+ * \li HostMemory \li OK \li SlotIdInvalid
+ * \li TokenNotPresent \li TokenNotRecognized \li ArgumentsBad
+ * @return true on success, false otherwise
+ */
+ bool C_GetTokenInfo(SlotId slot_id,
+ TokenInfo* info_ptr,
+ ReturnValue* return_value = ThrowException) const;
+
+ /**
+ * C_WaitForSlotEvent waits for a slot event (token insertion, removal, etc.) to occur.
+ * @param flags blocking/nonblocking flag
+ * @param slot_ptr location that receives the slot ID
+ * @param reserved reserved. Should be NULL_PTR
+ * @param return_value default value (`ThrowException`): throw exception on error.
+ * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+ * At least the following PKCS#11 return values may be returned:
+ * \li ArgumentsBad \li CryptokiNotInitialized \li FunctionFailed
+ * \li GeneralError \li HostMemory \li NoEvent
+ * \li OK
+ * @return true on success, false otherwise
+ */
+ bool C_WaitForSlotEvent(Flags flags,
+ SlotId* slot_ptr,
+ VoidPtr reserved,
+ ReturnValue* return_value = ThrowException) const;
+
+ /**
+ * C_GetMechanismList obtains a list of mechanism types supported by a token.
+ * @param slot_id ID of token's slot
+ * @param mechanism_list_ptr gets mech. array
+ * @param count_ptr gets # of mechs.
+ * @param return_value default value (`ThrowException`): throw exception on error.
+ * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+ * At least the following PKCS#11 return values may be returned:
+ * \li BufferTooSmall \li CryptokiNotInitialized \li DeviceError
+ * \li DeviceMemory \li DeviceRemoved \li FunctionFailed
+ * \li GeneralError \li HostMemory \li OK
+ * \li SlotIdInvalid \li TokenNotPresent \li TokenNotRecognized
+ * \li ArgumentsBad
+ * @return true on success, false otherwise
+ */
+ bool C_GetMechanismList(SlotId slot_id,
+ MechanismType* mechanism_list_ptr,
+ Ulong* count_ptr,
+ ReturnValue* return_value = ThrowException) const;
+
+ /**
+ * C_GetMechanismList obtains a list of mechanism types supported by a token.
+ * @param slot_id ID of token's slot
+ * @param mechanisms receives vector of supported mechanisms
+ * @param return_value default value (`ThrowException`): throw exception on error.
+ * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+ * At least the following PKCS#11 return values may be returned:
+ * \li BufferTooSmall \li CryptokiNotInitialized \li DeviceError
+ * \li DeviceMemory \li DeviceRemoved \li FunctionFailed
+ * \li GeneralError \li HostMemory \li OK
+ * \li SlotIdInvalid \li TokenNotPresent \li TokenNotRecognized
+ * \li ArgumentsBad
+ * @return true on success, false otherwise
+ */
+ bool C_GetMechanismList(SlotId slot_id,
+ std::vector<MechanismType>& mechanisms,
+ ReturnValue* return_value = ThrowException) const;
+
+ /**
+ * C_GetMechanismInfo obtains information about a particular mechanism possibly supported by a token.
+ * @param slot_id ID of the token's slot
+ * @param type type of mechanism
+ * @param info_ptr receives mechanism info
+ * @param return_value default value (`ThrowException`): throw exception on error.
+ * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+ * At least the following PKCS#11 return values may be returned:
+ * \li CryptokiNotInitialized \li DeviceError \li DeviceMemory
+ * \li DeviceRemoved \li FunctionFailed \li GeneralError
+ * \li HostMemory \li MechanismInvalid \li OK
+ * \li SlotIdInvalid \li TokenNotPresent \li TokenNotRecognized
+ * \li ArgumentsBad
+ * @return true on success, false otherwise
+ */
+ bool C_GetMechanismInfo(SlotId slot_id,
+ MechanismType type,
+ MechanismInfo* info_ptr,
+ ReturnValue* return_value = ThrowException) const;
+
+ /**
+ * C_InitToken initializes a token.
+ * @param slot_id ID of the token's slot
+ * @param so_pin_ptr the SO's initial PIN
+ * @param so_pin_len length in bytes of the SO_PIN
+ * @param label_ptr 32-byte token label (blank padded)
+ * @param return_value default value (`ThrowException`): throw exception on error.
+ * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+ * At least the following PKCS#11 return values may be returned:
+ * \li CryptokiNotInitialized \li DeviceError \li DeviceMemory
+ * \li DeviceRemoved \li FunctionCanceled \li FunctionFailed
+ * \li GeneralError \li HostMemory \li OK
+ * \li PinIncorrect \li PinLocked \li SessionExists
+ * \li SlotIdInvalid \li TokenNotPresent \li TokenNotRecognized
+ * \li TokenWriteProtected \li ArgumentsBad
+ * @return true on success, false otherwise
+ */
+ bool C_InitToken(SlotId slot_id,
+ Utf8Char* so_pin_ptr,
+ Ulong so_pin_len,
+ Utf8Char* label_ptr,
+ ReturnValue* return_value = ThrowException) const;
+
+ /**
+ * C_InitToken initializes a token.
+ * @param slot_id ID of the token's slot
+ * @param so_pin the SO's initial PIN
+ * @param label token label (at max 32 bytes long)
+ * @param return_value default value (`ThrowException`): throw exception on error.
+ * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+ * At least the following PKCS#11 return values may be returned:
+ * \li CryptokiNotInitialized \li DeviceError \li DeviceMemory
+ * \li DeviceRemoved \li FunctionCanceled \li FunctionFailed
+ * \li GeneralError \li HostMemory \li OK
+ * \li PinIncorrect \li PinLocked \li SessionExists
+ * \li SlotIdInvalid \li TokenNotPresent \li TokenNotRecognized
+ * \li TokenWriteProtected \li ArgumentsBad
+ * @return true on success, false otherwise
+ */
+ template<typename TAlloc>
+ bool C_InitToken(SlotId slot_id,
+ const std::vector<uint8_t, TAlloc>& so_pin,
+ const std::string& label,
+ ReturnValue* return_value = ThrowException) const
+ {
+ std::string padded_label = label;
+ if(label.size() < 32)
+ {
+ padded_label.insert(padded_label.end(), 32 - label.size(), ' ');
+ }
+
+ return C_InitToken(slot_id,
+ reinterpret_cast< Utf8Char* >(const_cast< uint8_t* >(so_pin.data())),
+ static_cast<Ulong>(so_pin.size()),
+ reinterpret_cast< Utf8Char* >(const_cast< char* >(padded_label.c_str())),
+ return_value);
+ }
+
+ /**
+ * C_InitPIN initializes the normal user's PIN.
+ * @param session the session's handle
+ * @param pin_ptr the normal user's PIN
+ * @param pin_len length in bytes of the PIN
+ * @param return_value default value (`ThrowException`): throw exception on error.
+ * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+ * At least the following PKCS#11 return values may be returned:
+ * \li CryptokiNotInitialized \li DeviceError \li DeviceMemory
+ * \li DeviceRemoved \li FunctionCanceled \li FunctionFailed
+ * \li GeneralError \li HostMemory \li OK
+ * \li PinInvalid \li PinLenRange \li SessionClosed
+ * \li SessionReadOnly \li SessionHandleInvalid \li TokenWriteProtected
+ * \li UserNotLoggedIn \li ArgumentsBad
+ * @return true on success, false otherwise
+ */
+ bool C_InitPIN(SessionHandle session,
+ Utf8Char* pin_ptr,
+ Ulong pin_len,
+ ReturnValue* return_value = ThrowException) const;
+
+ /**
+ * C_InitPIN initializes the normal user's PIN.
+ * @param session the session's handle
+ * @param pin the normal user's PIN
+ * @param return_value default value (`ThrowException`): throw exception on error.
+ * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+ * At least the following PKCS#11 return values may be returned:
+ * \li CryptokiNotInitialized \li DeviceError \li DeviceMemory
+ * \li DeviceRemoved \li FunctionCanceled \li FunctionFailed
+ * \li GeneralError \li HostMemory \li OK
+ * \li PinInvalid \li PinLenRange \li SessionClosed
+ * \li SessionReadOnly \li SessionHandleInvalid \li TokenWriteProtected
+ * \li UserNotLoggedIn \li ArgumentsBad
+ * @return true on success, false otherwise
+ */
+ template<typename TAlloc>
+ bool C_InitPIN(SessionHandle session,
+ const std::vector<uint8_t, TAlloc>& pin,
+ ReturnValue* return_value = ThrowException) const
+ {
+ return C_InitPIN(session,
+ reinterpret_cast< Utf8Char* >(const_cast< uint8_t* >(pin.data())),
+ static_cast<Ulong>(pin.size()),
+ return_value);
+ }
+
+ /**
+ * C_SetPIN modifies the PIN of the user who is logged in.
+ * @param session the session's handle
+ * @param old_pin_ptr the old PIN
+ * @param old_len length of the old PIN
+ * @param new_pin_ptr the new PIN
+ * @param new_len length of the new PIN
+ * @param return_value default value (`ThrowException`): throw exception on error.
+ * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+ * At least the following PKCS#11 return values may be returned:
+ * \li CryptokiNotInitialized \li DeviceError \li DeviceMemory
+ * \li DeviceRemoved \li FunctionCanceled \li FunctionFailed
+ * \li GeneralError \li HostMemory \li OK
+ * \li PinIncorrect \li PinInvalid \li PinLenRange
+ * \li PinLocked \li SessionClosed \li SessionHandleInvalid
+ * \li SessionReadOnly \li TokenWriteProtected \li ArgumentsBad
+ * @return true on success, false otherwise
+ */
+ bool C_SetPIN(SessionHandle session,
+ Utf8Char* old_pin_ptr,
+ Ulong old_len,
+ Utf8Char* new_pin_ptr,
+ Ulong new_len,
+ ReturnValue* return_value = ThrowException) const;
+
+ /**
+ * C_SetPIN modifies the PIN of the user who is logged in.
+ * @param session the session's handle
+ * @param old_pin the old PIN
+ * @param new_pin the new PIN
+ * @param return_value default value (`ThrowException`): throw exception on error.
+ * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+ * At least the following PKCS#11 return values may be returned:
+ * \li CryptokiNotInitialized \li DeviceError \li DeviceMemory
+ * \li DeviceRemoved \li FunctionCanceled \li FunctionFailed
+ * \li GeneralError \li HostMemory \li OK
+ * \li PinIncorrect \li PinInvalid \li PinLenRange
+ * \li PinLocked \li SessionClosed \li SessionHandleInvalid
+ * \li SessionReadOnly \li TokenWriteProtected \li ArgumentsBad
+ * @return true on success, false otherwise
+ */
+ template<typename TAlloc>
+ bool C_SetPIN(SessionHandle session,
+ const std::vector<uint8_t, TAlloc>& old_pin,
+ const std::vector<uint8_t, TAlloc>& new_pin,
+ ReturnValue* return_value = ThrowException) const
+ {
+ return C_SetPIN(session,
+ reinterpret_cast< Utf8Char* >(const_cast< uint8_t* >(old_pin.data())),
+ static_cast<Ulong>(old_pin.size()),
+ reinterpret_cast< Utf8Char* >(const_cast< uint8_t* >(new_pin.data())),
+ static_cast<Ulong>(new_pin.size()),
+ return_value);
+ }
+
+
+ /****************************** Session management ******************************/
+
+ /**
+ * C_OpenSession opens a session between an application and a token.
+ * @param slot_id the slot's ID
+ * @param flags from CK_SESSION_INFO
+ * @param application passed to callback
+ * @param notify callback function
+ * @param session_ptr gets session handle
+ * @param return_value default value (`ThrowException`): throw exception on error.
+ * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+ * At least the following PKCS#11 return values may be returned:
+ * \li CryptokiNotInitialized \li DeviceError \li DeviceMemory
+ * \li DeviceRemoved \li FunctionFailed \li GeneralError
+ * \li HostMemory \li OK \li SessionCount
+ * \li SessionParallelNotSupported \li SessionReadWriteSoExists \li SlotIdInvalid
+ * \li TokenNotPresent \li TokenNotRecognized \li TokenWriteProtected
+ * \li ArgumentsBad
+ * @return true on success, false otherwise
+ */
+ bool C_OpenSession(SlotId slot_id,
+ Flags flags,
+ VoidPtr application,
+ Notify notify,
+ SessionHandle* session_ptr,
+ ReturnValue* return_value = ThrowException) const;
+
+ /**
+ * C_CloseSession closes a session between an application and a token.
+ * @param session the session's handle
+ * @param return_value default value (`ThrowException`): throw exception on error.
+ * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+ * At least the following PKCS#11 return values may be returned:
+ * \li CryptokiNotInitialized \li DeviceError \li DeviceMemory
+ * \li DeviceRemoved \li FunctionFailed \li GeneralError
+ * \li HostMemory \li OK \li SessionClosed
+ * \li SessionHandleInvalid
+ * @return true on success, false otherwise
+ */
+ bool C_CloseSession(SessionHandle session,
+ ReturnValue* return_value = ThrowException) const;
+
+ /**
+ * C_CloseAllSessions closes all sessions with a token.
+ * @param slot_id the token's slot
+ * @param return_value default value (`ThrowException`): throw exception on error.
+ * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+ * At least the following PKCS#11 return values may be returned:
+ * \li CryptokiNotInitialized \li DeviceError \li DeviceMemory
+ * \li DeviceRemoved \li FunctionFailed \li GeneralError
+ * \li HostMemory \li OK \li SlotIdInvalid
+ * \li TokenNotPresent
+ * @return true on success, false otherwise
+ */
+ bool C_CloseAllSessions(SlotId slot_id,
+ ReturnValue* return_value = ThrowException) const;
+
+ /**
+ * C_GetSessionInfo obtains information about the session.
+ * @param session the session's handle
+ * @param info_ptr receives session info
+ * @param return_value default value (`ThrowException`): throw exception on error.
+ * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+ * At least the following PKCS#11 return values may be returned:
+ * \li CryptokiNotInitialized \li DeviceError \li DeviceMemory
+ * \li DeviceRemoved \li FunctionFailed \li GeneralError
+ * \li HostMemory \li OK \li SessionClosed
+ * \li SessionHandleInvalid \li ArgumentsBad
+ * @return true on success, false otherwise
+ */
+ bool C_GetSessionInfo(SessionHandle session,
+ SessionInfo* info_ptr,
+ ReturnValue* return_value = ThrowException) const;
+
+ /**
+ * C_GetOperationState obtains the state of the cryptographic operation in a session.
+ * @param session session's handle
+ * @param operation_state_ptr gets state
+ * @param operation_state_len_ptr gets state length
+ * @param return_value default value (`ThrowException`): throw exception on error.
+ * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+ * At least the following PKCS#11 return values may be returned:
+ * \li BufferTooSmall \li CryptokiNotInitialized \li DeviceError
+ * \li DeviceMemory \li DeviceRemoved \li FunctionFailed
+ * \li GeneralError \li HostMemory \li OK
+ * \li OperationNotInitialized \li SessionClosed \li SessionHandleInvalid
+ * \li StateUnsaveable \li ArgumentsBad
+ * @return true on success, false otherwise
+ */
+ bool C_GetOperationState(SessionHandle session,
+ Byte* operation_state_ptr,
+ Ulong* operation_state_len_ptr,
+ ReturnValue* return_value = ThrowException) const;
+
+ /**
+ * C_SetOperationState restores the state of the cryptographic operation in a session.
+ * @param session session's handle
+ * @param operation_state_ptr holds state
+ * @param operation_state_len holds state length
+ * @param encryption_key en/decryption key
+ * @param authentication_key sign/verify key
+ * @param return_value default value (`ThrowException`): throw exception on error.
+ * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+ * At least the following PKCS#11 return values may be returned:
+ * \li CryptokiNotInitialized \li DeviceError \li DeviceMemory
+ * \li DeviceRemoved \li FunctionFailed \li GeneralError
+ * \li HostMemory \li KeyChanged \li KeyNeeded
+ * \li KeyNotNeeded \li OK \li SavedStateInvalid
+ * \li SessionClosed \li SessionHandleInvalid \li ArgumentsBad
+ * @return true on success, false otherwise
+ */
+ bool C_SetOperationState(SessionHandle session,
+ Byte* operation_state_ptr,
+ Ulong operation_state_len,
+ ObjectHandle encryption_key,
+ ObjectHandle authentication_key,
+ ReturnValue* return_value = ThrowException) const;
+
+ /**
+ * C_Login logs a user into a token.
+ * @param session the session's handle
+ * @param user_type the user type
+ * @param pin_ptr the user's PIN
+ * @param pin_len the length of the PIN
+ * @param return_value default value (`ThrowException`): throw exception on error.
+ * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+ * At least the following PKCS#11 return values may be returned:
+ * \li ArgumentsBad \li CryptokiNotInitialized \li DeviceError
+ * \li DeviceMemory \li DeviceRemoved \li FunctionCanceled
+ * \li FunctionFailed \li GeneralError \li HostMemory
+ * \li OK \li OperationNotInitialized \li PinIncorrect
+ * \li PinLocked \li SessionClosed \li SessionHandleInvalid
+ * \li SessionReadOnlyExists \li UserAlreadyLoggedIn \li UserAnotherAlreadyLoggedIn
+ * \li UserPinNotInitialized \li UserTooManyTypes \li UserTypeInvalid
+ * @return true on success, false otherwise
+ */
+ bool C_Login(SessionHandle session,
+ UserType user_type,
+ Utf8Char* pin_ptr,
+ Ulong pin_len,
+ ReturnValue* return_value = ThrowException) const;
+
+ /**
+ * C_Login logs a user into a token.
+ * @param session the session's handle
+ * @param user_type the user type
+ * @param pin the user or security officer's PIN
+ * @param return_value default value (`ThrowException`): throw exception on error.
+ * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+ * At least the following PKCS#11 return values may be returned:
+ * \li ArgumentsBad \li CryptokiNotInitialized \li DeviceError
+ * \li DeviceMemory \li DeviceRemoved \li FunctionCanceled
+ * \li FunctionFailed \li GeneralError \li HostMemory
+ * \li OK \li OperationNotInitialized \li PinIncorrect
+ * \li PinLocked \li SessionClosed \li SessionHandleInvalid
+ * \li SessionReadOnlyExists \li UserAlreadyLoggedIn \li UserAnotherAlreadyLoggedIn
+ * \li UserPinNotInitialized \li UserTooManyTypes \li UserTypeInvalid
+ * @return true on success, false otherwise
+ */
+ template<typename TAlloc>
+ bool C_Login(SessionHandle session,
+ UserType user_type,
+ const std::vector<uint8_t, TAlloc>& pin,
+ ReturnValue* return_value = ThrowException) const
+ {
+ return C_Login(session, user_type,
+ reinterpret_cast< Utf8Char* >(const_cast< uint8_t* >(pin.data())),
+ static_cast<Ulong>(pin.size()),
+ return_value);
+ }
+
+ /**
+ * C_Logout logs a user out from a token.
+ * @param session the session's handle
+ * @param return_value default value (`ThrowException`): throw exception on error.
+ * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+ * At least the following PKCS#11 return values may be returned:
+ * \li CryptokiNotInitialized \li DeviceError \li DeviceMemory
+ * \li DeviceRemoved \li FunctionFailed \li GeneralError
+ * \li HostMemory \li OK \li SessionClosed
+ * \li SessionHandleInvalid \li UserNotLoggedIn
+ * @return true on success, false otherwise
+ */
+ bool C_Logout(SessionHandle session,
+ ReturnValue* return_value = ThrowException) const;
+
+ /****************************** Object management functions ******************************/
+
+ /**
+ * C_CreateObject creates a new object.
+ * @param session the session's handle
+ * @param attribute_template_ptr the object's template
+ * @param count attributes in template
+ * @param object_ptr gets new object's handle.
+ * @param return_value default value (`ThrowException`): throw exception on error.
+ * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+ * At least the following PKCS#11 return values may be returned:
+ * \li ArgumentsBad \li AttributeReadOnly \li AttributeTypeInvalid
+ * \li AttributeValueInvalid \li CryptokiNotInitialized \li CurveNotSupported
+ * \li DeviceError \li DeviceMemory \li DeviceRemoved
+ * \li DomainParamsInvalid \li FunctionFailed \li GeneralError
+ * \li HostMemory \li OK \li PinExpired
+ * \li SessionClosed \li SessionHandleInvalid \li SessionReadOnly
+ * \li TemplateIncomplete \li TemplateInconsistent \li TokenWriteProtected
+ * \li UserNotLoggedIn
+ * @return true on success, false otherwise
+ */
+ bool C_CreateObject(SessionHandle session,
+ Attribute* attribute_template_ptr,
+ Ulong count,
+ ObjectHandle* object_ptr,
+ ReturnValue* return_value = ThrowException) const;
+
+ /**
+ * C_CopyObject copies an object, creating a new object for the copy.
+ * @param session the session's handle
+ * @param object the object's handle
+ * @param attribute_template_ptr template for new object
+ * @param count attributes in template
+ * @param new_object_ptr receives handle of copy
+ * @param return_value default value (`ThrowException`): throw exception on error.
+ * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+ * At least the following PKCS#11 return values may be returned:
+ * \li ActionProhibited \li ArgumentsBad \li AttributeReadOnly
+ * \li AttributeTypeInvalid \li AttributeValueInvalid \li CryptokiNotInitialized
+ * \li DeviceError \li DeviceMemory \li DeviceRemoved
+ * \li FunctionFailed \li GeneralError \li HostMemory
+ * \li ObjectHandleInvalid \li OK \li PinExpired
+ * \li SessionClosed \li SessionHandleInvalid \li SessionReadOnly
+ * \li TemplateInconsistent \li TokenWriteProtected \li UserNotLoggedIn
+ * @return true on success, false otherwise
+ */
+ bool C_CopyObject(SessionHandle session,
+ ObjectHandle object,
+ Attribute* attribute_template_ptr,
+ Ulong count,
+ ObjectHandle* new_object_ptr,
+ ReturnValue* return_value = ThrowException) const;
+
+ /**
+ * C_DestroyObject destroys an object.
+ * @param session the session's handle
+ * @param object the object's handle
+ * @param return_value default value (`ThrowException`): throw exception on error.
+ * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+ * At least the following PKCS#11 return values may be returned:
+ * \li ActionProhibited \li CryptokiNotInitialized \li DeviceError
+ * \li DeviceMemory \li DeviceRemoved \li FunctionFailed
+ * \li GeneralError \li HostMemory \li ObjectHandleInvalid
+ * \li OK \li PinExpired \li SessionClosed
+ * \li SessionHandleInvalid \li SessionReadOnly \li TokenWriteProtected
+ * @return true on success, false otherwise
+ */
+ bool C_DestroyObject(SessionHandle session,
+ ObjectHandle object,
+ ReturnValue* return_value = ThrowException) const;
+
+ /**
+ * C_GetObjectSize gets the size of an object in bytes.
+ * @param session the session's handle
+ * @param object the object's handle
+ * @param size_ptr receives size of object
+ * @param return_value default value (`ThrowException`): throw exception on error.
+ * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+ * At least the following PKCS#11 return values may be returned:
+ * \li ArgumentsBad \li CryptokiNotInitialized \li DeviceError
+ * \li DeviceMemory \li DeviceRemoved \li FunctionFailed
+ * \li GeneralError \li HostMemory \li InformationSensitive
+ * \li ObjectHandleInvalid \li OK \li SessionClosed
+ * \li SessionHandleInvalid
+ * @return true on success, false otherwise
+ */
+ bool C_GetObjectSize(SessionHandle session,
+ ObjectHandle object,
+ Ulong* size_ptr,
+ ReturnValue* return_value = ThrowException) const;
+
+ /**
+ * C_GetAttributeValue obtains the value of one or more object attributes.
+ * @param session the session's handle
+ * @param object the object's handle
+ * @param attribute_template_ptr specifies attrs; gets vals
+ * @param count attributes in template
+ * @param return_value default value (`ThrowException`): throw exception on error.
+ * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+ * At least the following PKCS#11 return values may be returned:
+ * \li ArgumentsBad \li AttributeSensitive \li AttributeTypeInvalid
+ * \li BufferTooSmall \li CryptokiNotInitialized \li DeviceError
+ * \li DeviceMemory \li DeviceRemoved \li FunctionFailed
+ * \li GeneralError \li HostMemory \li ObjectHandleInvalid
+ * \li OK \li SessionClosed \li SessionHandleInvalid
+ * @return true on success, false otherwise
+ */
+ bool C_GetAttributeValue(SessionHandle session,
+ ObjectHandle object,
+ Attribute* attribute_template_ptr,
+ Ulong count,
+ ReturnValue* return_value = ThrowException) const;
+
+ /**
+ * C_GetAttributeValue obtains the value of one or more object attributes.
+ * @param session the session's handle
+ * @param object the object's handle
+ * @param attribute_values specifies attrs; gets vals
+ * @param return_value default value (`ThrowException`): throw exception on error.
+ * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+ * At least the following PKCS#11 return values may be returned:
+ * \li ArgumentsBad \li AttributeSensitive \li AttributeTypeInvalid
+ * \li BufferTooSmall \li CryptokiNotInitialized \li DeviceError
+ * \li DeviceMemory \li DeviceRemoved \li FunctionFailed
+ * \li GeneralError \li HostMemory \li ObjectHandleInvalid
+ * \li OK \li SessionClosed \li SessionHandleInvalid
+ * @return true on success, false otherwise
+ */
+ template<typename TAlloc>
+ bool C_GetAttributeValue(SessionHandle session,
+ ObjectHandle object,
+ std::map<AttributeType, std::vector<uint8_t, TAlloc>>& attribute_values,
+ ReturnValue* return_value = ThrowException) const
+ {
+ std::vector<Attribute> getter_template;
+
+ for(const auto& entry : attribute_values)
+ {
+ getter_template.emplace_back(Attribute{ static_cast< CK_ATTRIBUTE_TYPE >(entry.first), nullptr, 0 });
+ }
+
+ bool success = C_GetAttributeValue(session,
+ object,
+ const_cast< Attribute* >(getter_template.data()),
+ static_cast<Ulong>(getter_template.size()),
+ return_value);
+
+ if(!success)
+ {
+ return success;
+ }
+
+ size_t i = 0;
+ for(auto& entry : attribute_values)
+ {
+ entry.second.clear();
+ entry.second.resize(getter_template.at(i).ulValueLen);
+ getter_template.at(i).pValue = const_cast< uint8_t* >(entry.second.data());
+ i++;
+ }
+
+ return C_GetAttributeValue(session, object,
+ const_cast< Attribute* >(getter_template.data()),
+ static_cast<Ulong>(getter_template.size()),
+ return_value);
+ }
+
+ /**
+ * C_SetAttributeValue modifies the value of one or more object attributes.
+ * @param session the session's handle
+ * @param object the object's handle
+ * @param attribute_template_ptr specifies attrs and values
+ * @param count attributes in template
+ * @param return_value default value (`ThrowException`): throw exception on error.
+ * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+ * At least the following PKCS#11 return values may be returned:
+ * \li ActionProhibited \li ArgumentsBad \li AttributeReadOnly
+ * \li AttributeTypeInvalid \li AttributeValueInvalid \li CryptokiNotInitialized
+ * \li DeviceError \li DeviceMemory \li DeviceRemoved
+ * \li FunctionFailed \li GeneralError \li HostMemory
+ * \li ObjectHandleInvalid \li OK \li SessionClosed
+ * \li SessionHandleInvalid \li SessionReadOnly \li TemplateInconsistent
+ * \li TokenWriteProtected \li UserNotLoggedIn
+ * @return true on success, false otherwise
+ */
+ bool C_SetAttributeValue(SessionHandle session,
+ ObjectHandle object,
+ Attribute* attribute_template_ptr,
+ Ulong count,
+ ReturnValue* return_value = ThrowException) const;
+
+ /**
+ * C_SetAttributeValue modifies the value of one or more object attributes.
+ * @param session the session's handle
+ * @param object the object's handle
+ * @param attribute_values specifies attrs and values
+ * @param return_value default value (`ThrowException`): throw exception on error.
+ * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+ * At least the following PKCS#11 return values may be returned:
+ * \li ActionProhibited \li ArgumentsBad \li AttributeReadOnly
+ * \li AttributeTypeInvalid \li AttributeValueInvalid \li CryptokiNotInitialized
+ * \li DeviceError \li DeviceMemory \li DeviceRemoved
+ * \li FunctionFailed \li GeneralError \li HostMemory
+ * \li ObjectHandleInvalid \li OK \li SessionClosed
+ * \li SessionHandleInvalid \li SessionReadOnly \li TemplateInconsistent
+ * \li TokenWriteProtected \li UserNotLoggedIn
+ * @return true on success, false otherwise
+ */
+ template<typename TAlloc>
+ bool C_SetAttributeValue(SessionHandle session,
+ ObjectHandle object,
+ std::map<AttributeType, std::vector<uint8_t, TAlloc>>& attribute_values,
+ ReturnValue* return_value = ThrowException) const
+ {
+ std::vector<Attribute> setter_template;
+
+ for(auto& entry : attribute_values)
+ {
+ setter_template.emplace_back(Attribute{ static_cast< CK_ATTRIBUTE_TYPE >(entry.first), entry.second.data(), static_cast<CK_ULONG>(entry.second.size()) });
+ }
+
+ return C_SetAttributeValue(session, object,
+ const_cast< Attribute* >(setter_template.data()),
+ static_cast<Ulong>(setter_template.size()),
+ return_value);
+ }
+
+ /**
+ * C_FindObjectsInit initializes a search for token and session objects that match a template.
+ * @param session the session's handle
+ * @param attribute_template_ptr attribute values to match
+ * @param count attrs in search template
+ * @param return_value default value (`ThrowException`): throw exception on error.
+ * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+ * At least the following PKCS#11 return values may be returned:
+ * \li ArgumentsBad \li AttributeTypeInvalid \li AttributeValueInvalid
+ * \li CryptokiNotInitialized \li DeviceError \li DeviceMemory
+ * \li DeviceRemoved \li FunctionFailed \li GeneralError
+ * \li HostMemory \li OK \li OperationActive
+ * \li PinExpired \li SessionClosed \li SessionHandleInvalid
+ * @return true on success, false otherwise
+ */
+ bool C_FindObjectsInit(SessionHandle session,
+ Attribute* attribute_template_ptr,
+ Ulong count,
+ ReturnValue* return_value = ThrowException) const;
+
+ /**
+ * C_FindObjects continues a search for token and session objects that match a template, obtaining additional object handles.
+ * @param session session's handle
+ * @param object_ptr gets obj. handles
+ * @param max_object_count max handles to get
+ * @param object_count_ptr actual # returned
+ * @param return_value default value (`ThrowException`): throw exception on error.
+ * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+ * At least the following PKCS#11 return values may be returned:
+ * \li ArgumentsBad \li CryptokiNotInitialized \li DeviceError
+ * \li DeviceMemory \li DeviceRemoved \li FunctionFailed
+ * \li GeneralError \li HostMemory \li OK
+ * \li OperationNotInitialized \li SessionClosed \li SessionHandleInvalid
+ * @return true on success, false otherwise
+ */
+ bool C_FindObjects(SessionHandle session,
+ ObjectHandle* object_ptr,
+ Ulong max_object_count,
+ Ulong* object_count_ptr,
+ ReturnValue* return_value = ThrowException) const;
+
+ /**
+ * C_FindObjectsFinal finishes a search for token and session objects.
+ * @param session the session's handle
+ * @param return_value default value (`ThrowException`): throw exception on error.
+ * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+ * At least the following PKCS#11 return values may be returned:
+ * \li CryptokiNotInitialized \li DeviceError \li DeviceMemory
+ * \li DeviceRemoved \li FunctionFailed \li GeneralError
+ * \li HostMemory \li OK \li OperationNotInitialized
+ * \li SessionClosed \li SessionHandleInvalid
+ * @return true on success, false otherwise
+ */
+ bool C_FindObjectsFinal(SessionHandle session,
+ ReturnValue* return_value = ThrowException) const;
+
+ /****************************** Encryption functions ******************************/
+
+ /**
+ * C_EncryptInit initializes an encryption operation.
+ * @param session the session's handle
+ * @param mechanism_ptr the encryption mechanism
+ * @param key handle of encryption key
+ * @param return_value default value (`ThrowException`): throw exception on error.
+ * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+ * At least the following PKCS#11 return values may be returned:
+ * \li CryptokiNotInitialized \li DeviceError \li DeviceMemory
+ * \li DeviceRemoved \li FunctionCanceled \li FunctionFailed
+ * \li GeneralError \li HostMemory \li KeyFunctionNotPermitted
+ * \li KeyHandleInvalid \li KeySizeRange \li KeyTypeInconsistent
+ * \li MechanismInvalid \li MechanismParamInvalid \li OK
+ * \li OperationActive \li PinExpired \li SessionClosed
+ * \li SessionHandleInvalid \li UserNotLoggedIn
+ * @return true on success, false otherwise
+ */
+ bool C_EncryptInit(SessionHandle session,
+ Mechanism* mechanism_ptr,
+ ObjectHandle key,
+ ReturnValue* return_value = ThrowException) const;
+
+ /**
+ * C_Encrypt encrypts single-part data.
+ * @param session session's handle
+ * @param data_ptr the plaintext data
+ * @param data_len size of plaintext data in bytes
+ * @param encrypted_data gets ciphertext
+ * @param encrypted_data_len_ptr gets c-text size
+ * @param return_value default value (`ThrowException`): throw exception on error.
+ * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+ * At least the following PKCS#11 return values may be returned:
+ * \li ArgumentsBad \li BufferTooSmall \li CryptokiNotInitialized
+ * \li DataInvalid \li DataLenRange \li DeviceError
+ * \li DeviceMemory \li DeviceRemoved \li FunctionCanceled
+ * \li FunctionFailed \li GeneralError \li HostMemory
+ * \li OK \li OperationNotInitialized \li SessionClosed
+ * \li SessionHandleInvalid
+ * @return true on success, false otherwise
+ */
+ bool C_Encrypt(SessionHandle session,
+ Byte* data_ptr,
+ Ulong data_len,
+ Byte* encrypted_data,
+ Ulong* encrypted_data_len_ptr,
+ ReturnValue* return_value = ThrowException) const;
+
+ /**
+ * C_Encrypt encrypts single-part data.
+ * @param session session's handle
+ * @param plaintext_data the plaintext data
+ * @param encrypted_data gets ciphertext
+ * @param return_value default value (`ThrowException`): throw exception on error.
+ * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+ * At least the following PKCS#11 return values may be returned:
+ * \li ArgumentsBad \li BufferTooSmall \li CryptokiNotInitialized
+ * \li DataInvalid \li DataLenRange \li DeviceError
+ * \li DeviceMemory \li DeviceRemoved \li FunctionCanceled
+ * \li FunctionFailed \li GeneralError \li HostMemory
+ * \li OK \li OperationNotInitialized \li SessionClosed
+ * \li SessionHandleInvalid
+ * @return true on success, false otherwise
+ */
+ template<typename TAllocA, typename TAllocB>
+ bool C_Encrypt(SessionHandle session,
+ const std::vector<uint8_t, TAllocA>& plaintext_data,
+ std::vector<uint8_t, TAllocB>& encrypted_data,
+ ReturnValue* return_value = ThrowException) const
+ {
+ Ulong encrypted_size = 0;
+ if(!C_Encrypt(session,
+ const_cast<Byte*>((plaintext_data.data())),
+ static_cast<Ulong>(plaintext_data.size()),
+ nullptr, &encrypted_size,
+ return_value))
+ {
+ return false;
+ }
+
+ encrypted_data.resize(encrypted_size);
+ if (!C_Encrypt(session,
+ const_cast<Byte*>(plaintext_data.data()),
+ static_cast<Ulong>(plaintext_data.size()),
+ encrypted_data.data(),
+ &encrypted_size, return_value))
+ {
+ return false;
+ }
+ encrypted_data.resize(encrypted_size);
+ return true;
+ }
+
+ /**
+ * C_EncryptUpdate continues a multiple-part encryption operation.
+ * @param session session's handle
+ * @param part_ptr the plaintext data
+ * @param part_len plaintext data len
+ * @param encrypted_part_ptr gets ciphertext
+ * @param encrypted_part_len_ptr gets c-text size
+ * @param return_value default value (`ThrowException`): throw exception on error.
+ * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+ * At least the following PKCS#11 return values may be returned:
+ * \li ArgumentsBad \li BufferTooSmall \li CryptokiNotInitialized
+ * \li DataLenRange \li DeviceError \li DeviceMemory
+ * \li DeviceRemoved \li FunctionCanceled \li FunctionFailed
+ * \li GeneralError \li HostMemory \li OK
+ * \li OperationNotInitialized \li SessionClosed \li SessionHandleInvalid
+ * @return true on success, false otherwise
+ */
+ bool C_EncryptUpdate(SessionHandle session,
+ Byte* part_ptr,
+ Ulong part_len,
+ Byte* encrypted_part_ptr,
+ Ulong* encrypted_part_len_ptr,
+ ReturnValue* return_value = ThrowException) const;
+
+ /**
+ * C_EncryptFinal finishes a multiple-part encryption operation.
+ * @param session session handle
+ * @param last_encrypted_part_ptr last c-text
+ * @param last_encrypted_part_len_ptr gets last size
+ * @param return_value default value (`ThrowException`): throw exception on error.
+ * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+ * At least the following PKCS#11 return values may be returned:
+ * \li ArgumentsBad \li BufferTooSmall \li CryptokiNotInitialized
+ * \li DataLenRange \li DeviceError \li DeviceMemory
+ * \li DeviceRemoved \li FunctionCanceled \li FunctionFailed
+ * \li GeneralError \li HostMemory \li OK
+ * \li OperationNotInitialized \li SessionClosed \li SessionHandleInvalid
+ * @return true on success, false otherwise
+ */
+ bool C_EncryptFinal(SessionHandle session,
+ Byte* last_encrypted_part_ptr,
+ Ulong* last_encrypted_part_len_ptr,
+ ReturnValue* return_value = ThrowException) const;
+
+ /****************************** Decryption functions ******************************/
+
+ /**
+ * C_DecryptInit initializes a decryption operation.
+ * @param session the session's handle
+ * @param mechanism_ptr the decryption mechanism
+ * @param key handle of decryption key
+ * @param return_value default value (`ThrowException`): throw exception on error.
+ * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+ * At least the following PKCS#11 return values may be returned:
+ * \li ArgumentsBad \li CryptokiNotInitialized \li DeviceError
+ * \li DeviceMemory \li DeviceRemoved \li FunctionCanceled
+ * \li FunctionFailed \li GeneralError \li HostMemory
+ * \li KeyFunctionNotPermitted \li KeyHandleInvalid \li KeySizeRange
+ * \li KeyTypeInconsistent \li MechanismInvalid \li MechanismParamInvalid
+ * \li OK \li OperationActive \li PinExpired
+ * \li SessionClosed \li SessionHandleInvalid \li UserNotLoggedIn
+ * @return true on success, false otherwise
+ */
+ bool C_DecryptInit(SessionHandle session,
+ Mechanism* mechanism_ptr,
+ ObjectHandle key,
+ ReturnValue* return_value = ThrowException) const;
+
+ /**
+ * C_Decrypt decrypts encrypted data in a single part.
+ * @param session session's handle
+ * @param encrypted_data_ptr ciphertext
+ * @param encrypted_data_len ciphertext length
+ * @param data_ptr gets plaintext
+ * @param data_len_ptr gets p-text size
+ * @param return_value default value (`ThrowException`): throw exception on error.
+ * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+ * At least the following PKCS#11 return values may be returned:
+ * \li ArgumentsBad \li BufferTooSmall \li CryptokiNotInitialized
+ * \li DeviceError \li DeviceMemory \li DeviceRemoved
+ * \li EncryptedDataInvalid \li EncryptedDataLenRange \li FunctionCanceled
+ * \li FunctionFailed \li GeneralError \li HostMemory
+ * \li OK \li OperationNotInitialized \li SessionClosed
+ * \li SessionHandleInvalid \li UserNotLoggedIn
+ * @return true on success, false otherwise
+ */
+ bool C_Decrypt(SessionHandle session,
+ Byte* encrypted_data_ptr,
+ Ulong encrypted_data_len,
+ Byte* data_ptr,
+ Ulong* data_len_ptr,
+ ReturnValue* return_value = ThrowException) const;
+
+ /**
+ * C_Decrypt decrypts encrypted data in a single part.
+ * @param session session's handle
+ * @param encrypted_data ciphertext
+ * @param decrypted_data gets plaintext
+ * @param return_value default value (`ThrowException`): throw exception on error.
+ * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+ * At least the following PKCS#11 return values may be returned:
+ * \li ArgumentsBad \li BufferTooSmall \li CryptokiNotInitialized
+ * \li DeviceError \li DeviceMemory \li DeviceRemoved
+ * \li EncryptedDataInvalid \li EncryptedDataLenRange \li FunctionCanceled
+ * \li FunctionFailed \li GeneralError \li HostMemory
+ * \li OK \li OperationNotInitialized \li SessionClosed
+ * \li SessionHandleInvalid \li UserNotLoggedIn
+ * @return true on success, false otherwise
+ */
+ template<typename TAllocA, typename TAllocB>
+ bool C_Decrypt(SessionHandle session,
+ const std::vector<uint8_t, TAllocA>& encrypted_data,
+ std::vector<uint8_t, TAllocB>& decrypted_data,
+ ReturnValue* return_value = ThrowException) const
+ {
+ Ulong decrypted_size = 0;
+ if(!C_Decrypt(session,
+ const_cast<Byte*>((encrypted_data.data())),
+ static_cast<Ulong>(encrypted_data.size()),
+ nullptr, &decrypted_size,
+ return_value))
+ {
+ return false;
+ }
+
+ decrypted_data.resize(decrypted_size);
+ if(!C_Decrypt(session,
+ const_cast<Byte*>(encrypted_data.data()),
+ static_cast<Ulong>(encrypted_data.size()),
+ decrypted_data.data(),
+ &decrypted_size, return_value))
+ {
+ return false;
+ }
+ decrypted_data.resize(decrypted_size);
+ return true;
+ }
+
+ /**
+ * C_DecryptUpdate continues a multiple-part decryption operation.
+ * @param session session's handle
+ * @param encrypted_part_ptr encrypted data
+ * @param encrypted_part_len input length
+ * @param part_ptr gets plaintext
+ * @param part_len_ptr p-text size
+ * @param return_value default value (`ThrowException`): throw exception on error.
+ * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+ * At least the following PKCS#11 return values may be returned:
+ * \li ArgumentsBad \li BufferTooSmall \li CryptokiNotInitialized
+ * \li DeviceError \li DeviceMemory \li DeviceRemoved
+ * \li EncryptedDataInvalid \li EncryptedDataLenRange \li FunctionCanceled
+ * \li FunctionFailed \li GeneralError \li HostMemory
+ * \li OK \li OperationNotInitialized \li SessionClosed
+ * \li SessionHandleInvalid \li UserNotLoggedIn
+ * @return true on success, false otherwise
+ */
+ bool C_DecryptUpdate(SessionHandle session,
+ Byte* encrypted_part_ptr,
+ Ulong encrypted_part_len,
+ Byte* part_ptr,
+ Ulong* part_len_ptr,
+ ReturnValue* return_value = ThrowException) const;
+
+ /**
+ * C_DecryptFinal finishes a multiple-part decryption operation.
+ * @param session the session's handle
+ * @param last_part_ptr gets plaintext
+ * @param last_part_len_ptr p-text size
+ * @param return_value default value (`ThrowException`): throw exception on error.
+ * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+ * At least the following PKCS#11 return values may be returned:
+ * \li ArgumentsBad \li BufferTooSmall \li CryptokiNotInitialized
+ * \li DeviceError \li DeviceMemory \li DeviceRemoved
+ * \li EncryptedDataInvalid \li EncryptedDataLenRange \li FunctionCanceled
+ * \li FunctionFailed \li GeneralError \li HostMemory
+ * \li OK \li OperationNotInitialized \li SessionClosed
+ * \li SessionHandleInvalid \li UserNotLoggedIn
+ * @return true on success, false otherwise
+ */
+ bool C_DecryptFinal(SessionHandle session,
+ Byte* last_part_ptr,
+ Ulong* last_part_len_ptr,
+ ReturnValue* return_value = ThrowException) const;
+
+ /****************************** Message digesting functions ******************************/
+
+ /**
+ * C_DigestInit initializes a message-digesting operation.
+ * @param session the session's handle
+ * @param mechanism_ptr the digesting mechanism
+ * @param return_value default value (`ThrowException`): throw exception on error.
+ * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+ * At least the following PKCS#11 return values may be returned:
+ * \li ArgumentsBad \li CryptokiNotInitialized \li DeviceError
+ * \li DeviceMemory \li DeviceRemoved \li FunctionCanceled
+ * \li FunctionFailed \li GeneralError \li HostMemory
+ * \li MechanismInvalid \li MechanismParamInvalid \li OK
+ * \li OperationActive \li PinExpired \li SessionClosed
+ * \li SessionHandleInvalid \li UserNotLoggedIn
+ * @return true on success, false otherwise
+ */
+ bool C_DigestInit(SessionHandle session,
+ Mechanism* mechanism_ptr,
+ ReturnValue* return_value = ThrowException) const;
+
+ /**
+ * C_Digest digests data in a single part.
+ * @param session the session's handle
+ * @param data_ptr data to be digested
+ * @param data_len bytes of data to digest
+ * @param digest_ptr gets the message digest
+ * @param digest_len_ptr gets digest length
+ * @param return_value default value (`ThrowException`): throw exception on error.
+ * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+ * At least the following PKCS#11 return values may be returned:
+ * \li ArgumentsBad \li BufferTooSmall \li CryptokiNotInitialized
+ * \li DeviceError \li DeviceMemory \li DeviceRemoved
+ * \li FunctionCanceled \li FunctionFailed \li GeneralError
+ * \li HostMemory \li OK \li OperationNotInitialized
+ * \li SessionClosed \li SessionHandleInvalid
+ * @return true on success, false otherwise
+ */
+ bool C_Digest(SessionHandle session,
+ Byte* data_ptr,
+ Ulong data_len,
+ Byte* digest_ptr,
+ Ulong* digest_len_ptr,
+ ReturnValue* return_value = ThrowException) const;
+
+ /**
+ * C_DigestUpdate continues a multiple-part message-digesting operation.
+ * @param session the session's handle
+ * @param part_ptr data to be digested
+ * @param part_len bytes of data to be digested
+ * @param return_value default value (`ThrowException`): throw exception on error.
+ * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+ * At least the following PKCS#11 return values may be returned:
+ * \li ArgumentsBad \li CryptokiNotInitialized \li DeviceError
+ * \li DeviceMemory \li DeviceRemoved \li FunctionCanceled
+ * \li FunctionFailed \li GeneralError \li HostMemory
+ * \li OK \li OperationNotInitialized \li SessionClosed
+ * \li SessionHandleInvalid
+ * @return true on success, false otherwise
+ */
+ bool C_DigestUpdate(SessionHandle session,
+ Byte* part_ptr,
+ Ulong part_len,
+ ReturnValue* return_value = ThrowException) const;
+
+ /**
+ * C_DigestKey continues a multi-part message-digesting operation, by digesting the value of a secret key as part of the data already digested.
+ * @param session the session's handle
+ * @param key secret key to digest
+ * @param return_value default value (`ThrowException`): throw exception on error.
+ * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+ * At least the following PKCS#11 return values may be returned:
+ * \li CryptokiNotInitialized \li DeviceError \li DeviceMemory
+ * \li DeviceRemoved \li FunctionCanceled \li FunctionFailed
+ * \li GeneralError \li HostMemory \li KeyHandleInvalid
+ * \li KeyIndigestible \li KeySizeRange \li OK
+ * \li OperationNotInitialized \li SessionClosed \li SessionHandleInvalid
+ * @return true on success, false otherwise
+ */
+ bool C_DigestKey(SessionHandle session,
+ ObjectHandle key,
+ ReturnValue* return_value = ThrowException) const;
+
+ /**
+ * C_DigestFinal finishes a multiple-part message-digesting operation.
+ * @param session the session's handle
+ * @param digest_ptr gets the message digest
+ * @param digest_len_ptr gets uint8_t count of digest
+ * @param return_value default value (`ThrowException`): throw exception on error.
+ * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+ * At least the following PKCS#11 return values may be returned:
+ * \li ArgumentsBad \li BufferTooSmall \li CryptokiNotInitialized
+ * \li DeviceError \li DeviceMemory \li DeviceRemoved
+ * \li FunctionCanceled \li FunctionFailed \li GeneralError
+ * \li HostMemory \li OK \li OperationNotInitialized
+ * \li SessionClosed \li SessionHandleInvalid
+ * @return true on success, false otherwise
+ */
+ bool C_DigestFinal(SessionHandle session,
+ Byte* digest_ptr,
+ Ulong* digest_len_ptr,
+ ReturnValue* return_value = ThrowException) const;
+
+ /****************************** Signing and MACing functions ******************************/
+
+ /**
+ * C_SignInit initializes a signature (private key encryption) operation, where the signature is (will be) an appendix to the data, and plaintext cannot be recovered from the signature.
+ * @param session the session's handle
+ * @param mechanism_ptr the signature mechanism
+ * @param key handle of signature key
+ * @param return_value default value (`ThrowException`): throw exception on error.
+ * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+ * At least the following PKCS#11 return values may be returned:
+ * \li ArgumentsBad \li CryptokiNotInitialized \li DeviceError
+ * \li DeviceMemory \li DeviceRemoved \li FunctionCanceled
+ * \li FunctionFailed \li GeneralError \li HostMemory
+ * \li KeyFunctionNotPermitted \li KeyHandleInvalid \li KeySizeRange
+ * \li KeyTypeInconsistent \li MechanismInvalid \li MechanismParamInvalid
+ * \li OK \li OperationActive \li PinExpired
+ * \li SessionClosed \li SessionHandleInvalid \li UserNotLoggedIn
+ * @return true on success, false otherwise
+ */
+ bool C_SignInit(SessionHandle session,
+ Mechanism* mechanism_ptr,
+ ObjectHandle key,
+ ReturnValue* return_value = ThrowException) const;
+
+ /**
+ * C_Sign signs (encrypts with private key) data in a single part, where the signature is (will be) an appendix to the data, and plaintext cannot be recovered from the signature.
+ * @param session the session's handle
+ * @param data_ptr the data to sign
+ * @param data_len count of bytes to sign
+ * @param signature_ptr gets the signature
+ * @param signature_len_ptr gets signature length
+ * @param return_value default value (`ThrowException`): throw exception on error.
+ * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+ * At least the following PKCS#11 return values may be returned:
+ * \li ArgumentsBad \li BufferTooSmall \li CryptokiNotInitialized
+ * \li DataInvalid \li DataLenRange \li DeviceError
+ * \li DeviceMemory \li DeviceRemoved \li FunctionCanceled
+ * \li FunctionFailed \li GeneralError \li HostMemory
+ * \li OK \li OperationNotInitialized \li SessionClosed
+ * \li SessionHandleInvalid \li UserNotLoggedIn \li FunctionRejected
+ * @return true on success, false otherwise
+ */
+ bool C_Sign(SessionHandle session,
+ Byte* data_ptr,
+ Ulong data_len,
+ Byte* signature_ptr,
+ Ulong* signature_len_ptr,
+ ReturnValue* return_value = ThrowException) const;
+
+ /**
+ * C_Sign signs (encrypts with private key) data in a single part, where the signature is (will be) an appendix to the data, and plaintext cannot be recovered from the signature.
+ * @param session the session's handle
+ * @param data the data to sign
+ * @param signature gets the signature
+ * @param return_value default value (`ThrowException`): throw exception on error.
+ * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+ * At least the following PKCS#11 return values may be returned:
+ * \li ArgumentsBad \li BufferTooSmall \li CryptokiNotInitialized
+ * \li DataInvalid \li DataLenRange \li DeviceError
+ * \li DeviceMemory \li DeviceRemoved \li FunctionCanceled
+ * \li FunctionFailed \li GeneralError \li HostMemory
+ * \li OK \li OperationNotInitialized \li SessionClosed
+ * \li SessionHandleInvalid \li UserNotLoggedIn \li FunctionRejected
+ * @return true on success, false otherwise
+ */
+ template<typename TAllocA, typename TAllocB>
+ bool C_Sign(SessionHandle session,
+ const std::vector<uint8_t, TAllocA>& data,
+ std::vector<uint8_t, TAllocB>& signature,
+ ReturnValue* return_value = ThrowException) const
+ {
+ Ulong signature_size = 0;
+ if(!C_Sign(session,
+ const_cast<Byte*>((data.data())),
+ static_cast<Ulong>(data.size()),
+ nullptr,
+ &signature_size,
+ return_value))
+ {
+ return false;
+ }
+
+ signature.resize(signature_size);
+ if (!C_Sign(session,
+ const_cast<Byte*>(data.data()),
+ static_cast<Ulong>(data.size()),
+ signature.data(),
+ &signature_size,
+ return_value))
+ {
+ return false;
+ }
+ signature.resize(signature_size);
+ return true;
+ }
+
+ /**
+ * C_SignUpdate continues a multiple-part signature operation, where the signature is (will be) an appendix to the data, and plaintext cannot be recovered from the signature.
+ * @param session the session's handle
+ * @param part_ptr the data to sign
+ * @param part_len count of bytes to sign
+ * @param return_value default value (`ThrowException`): throw exception on error.
+ * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+ * At least the following PKCS#11 return values may be returned:
+ * \li ArgumentsBad \li CryptokiNotInitialized \li DataLenRange
+ * \li DeviceError \li DeviceMemory \li DeviceRemoved
+ * \li FunctionCanceled \li FunctionFailed \li GeneralError
+ * \li HostMemory \li OK \li OperationNotInitialized
+ * \li SessionClosed \li SessionHandleInvalid \li UserNotLoggedIn
+ * @return true on success, false otherwise
+ */
+ bool C_SignUpdate(SessionHandle session,
+ Byte* part_ptr,
+ Ulong part_len,
+ ReturnValue* return_value = ThrowException) const;
+
+ /**
+ * C_SignUpdate continues a multiple-part signature operation, where the signature is (will be) an appendix to the data, and plaintext cannot be recovered from the signature.
+ * @param session the session's handle
+ * @param part the data to sign
+ * @param return_value default value (`ThrowException`): throw exception on error.
+ * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+ * At least the following PKCS#11 return values may be returned:
+ * \li ArgumentsBad \li CryptokiNotInitialized \li DataLenRange
+ * \li DeviceError \li DeviceMemory \li DeviceRemoved
+ * \li FunctionCanceled \li FunctionFailed \li GeneralError
+ * \li HostMemory \li OK \li OperationNotInitialized
+ * \li SessionClosed \li SessionHandleInvalid \li UserNotLoggedIn
+ * @return true on success, false otherwise
+ */
+ template<typename TAlloc>
+ bool C_SignUpdate(SessionHandle session,
+ const std::vector<uint8_t, TAlloc>& part,
+ ReturnValue* return_value = ThrowException) const
+ {
+ return C_SignUpdate(session,
+ const_cast<Byte*>(part.data()),
+ static_cast<Ulong>(part.size()),
+ return_value);
+ }
+
+ /**
+ * C_SignFinal finishes a multiple-part signature operation, returning the signature.
+ * @param session the session's handle
+ * @param signature_ptr gets the signature
+ * @param signature_len_ptr gets signature length
+ * @param return_value default value (`ThrowException`): throw exception on error.
+ * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+ * At least the following PKCS#11 return values may be returned:
+ * \li ArgumentsBad \li BufferTooSmall \li CryptokiNotInitialized
+ * \li DataLenRange \li DeviceError \li DeviceMemory
+ * \li DeviceRemoved \li FunctionCanceled \li FunctionFailed
+ * \li GeneralError \li HostMemory \li OK
+ * \li OperationNotInitialized \li SessionClosed \li SessionHandleInvalid
+ * \li UserNotLoggedIn \li FunctionRejected
+ * @return true on success, false otherwise
+ */
+ bool C_SignFinal(SessionHandle session,
+ Byte* signature_ptr,
+ Ulong* signature_len_ptr,
+ ReturnValue* return_value = ThrowException) const;
+
+ /**
+ * C_SignFinal finishes a multiple-part signature operation, returning the signature.
+ * @param session the session's handle
+ * @param signature gets the signature
+ * @param return_value default value (`ThrowException`): throw exception on error.
+ * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+ * At least the following PKCS#11 return values may be returned:
+ * \li ArgumentsBad \li BufferTooSmall \li CryptokiNotInitialized
+ * \li DataLenRange \li DeviceError \li DeviceMemory
+ * \li DeviceRemoved \li FunctionCanceled \li FunctionFailed
+ * \li GeneralError \li HostMemory \li OK
+ * \li OperationNotInitialized \li SessionClosed \li SessionHandleInvalid
+ * \li UserNotLoggedIn \li FunctionRejected
+ * @return true on success, false otherwise
+ */
+ template<typename TAlloc>
+ bool C_SignFinal(SessionHandle session,
+ std::vector<uint8_t, TAlloc>& signature,
+ ReturnValue* return_value = ThrowException) const
+ {
+ Ulong signature_size = 0;
+ if(!C_SignFinal(session, nullptr, &signature_size, return_value))
+ {
+ return false;
+ }
+
+ signature.resize(signature_size);
+ if (!C_SignFinal(session, signature.data(), &signature_size, return_value))
+ {
+ return false;
+ }
+ signature.resize(signature_size);
+ return true;
+ }
+
+ /**
+ * C_SignRecoverInit initializes a signature operation, where the data can be recovered from the signature.
+ * @param session the session's handle
+ * @param mechanism_ptr the signature mechanism
+ * @param key handle of the signature key
+ * @param return_value default value (`ThrowException`): throw exception on error.
+ * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+ * At least the following PKCS#11 return values may be returned:
+ * \li ArgumentsBad \li CryptokiNotInitialized \li DeviceError
+ * \li DeviceMemory \li DeviceRemoved \li FunctionCanceled
+ * \li FunctionFailed \li GeneralError \li HostMemory
+ * \li KeyFunctionNotPermitted \li KeyHandleInvalid \li KeySizeRange
+ * \li KeyTypeInconsistent \li MechanismInvalid \li MechanismParamInvalid
+ * \li OK \li OperationActive \li PinExpired
+ * \li SessionClosed \li SessionHandleInvalid \li UserNotLoggedIn
+ * @return true on success, false otherwise
+ */
+ bool C_SignRecoverInit(SessionHandle session,
+ Mechanism* mechanism_ptr,
+ ObjectHandle key,
+ ReturnValue* return_value = ThrowException) const;
+
+ /**
+ * C_SignRecover signs data in a single operation, where the data can be recovered from the signature.
+ * @param session the session's handle
+ * @param data_ptr the data to sign
+ * @param data_len count of bytes to sign
+ * @param signature_ptr gets the signature
+ * @param signature_len_ptr gets signature length
+ * @param return_value default value (`ThrowException`): throw exception on error.
+ * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+ * At least the following PKCS#11 return values may be returned:
+ * \li ArgumentsBad \li BufferTooSmall \li CryptokiNotInitialized
+ * \li DataInvalid \li DataLenRange \li DeviceError
+ * \li DeviceMemory \li DeviceRemoved \li FunctionCanceled
+ * \li FunctionFailed \li GeneralError \li HostMemory
+ * \li OK \li OperationNotInitialized \li SessionClosed
+ * \li SessionHandleInvalid \li UserNotLoggedIn
+ * @return true on success, false otherwise
+ */
+ bool C_SignRecover(SessionHandle session,
+ Byte* data_ptr,
+ Ulong data_len,
+ Byte* signature_ptr,
+ Ulong* signature_len_ptr,
+ ReturnValue* return_value = ThrowException) const;
+
+ /****************************** Functions for verifying signatures and MACs ******************************/
+
+ /**
+ * C_VerifyInit initializes a verification operation, where the signature is an appendix to the data, and plaintext cannot be recovered from the signature (e.g. DSA).
+ * @param session the session's handle
+ * @param mechanism_ptr the verification mechanism
+ * @param key verification key
+ * @param return_value default value (`ThrowException`): throw exception on error.
+ * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+ * At least the following PKCS#11 return values may be returned:
+ * \li ArgumentsBad \li CryptokiNotInitialized \li DeviceError
+ * \li DeviceMemory \li DeviceRemoved \li FunctionCanceled
+ * \li FunctionFailed \li GeneralError \li HostMemory
+ * \li KeyFunctionNotPermitted \li KeyHandleInvalid \li KeySizeRange
+ * \li KeyTypeInconsistent \li MechanismInvalid \li MechanismParamInvalid
+ * \li OK \li OperationActive \li PinExpired
+ * \li SessionClosed \li SessionHandleInvalid \li UserNotLoggedIn
+ * @return true on success, false otherwise
+ */
+ bool C_VerifyInit(SessionHandle session,
+ Mechanism* mechanism_ptr,
+ ObjectHandle key,
+ ReturnValue* return_value = ThrowException) const;
+
+ /**
+ * C_Verify verifies a signature in a single-part operation, where the signature is an appendix to the data, and plaintext cannot be recovered from the signature.
+ * @param session the session's handle
+ * @param data_ptr signed data
+ * @param data_len length of signed data
+ * @param signature_ptr signature
+ * @param signature_len signature length
+ * @param return_value default value (`ThrowException`): throw exception on error.
+ * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+ * At least the following PKCS#11 return values may be returned:
+ * \li ArgumentsBad \li CryptokiNotInitialized \li DataInvalid
+ * \li DataLenRange \li DeviceError \li DeviceMemory
+ * \li DeviceRemoved \li FunctionCanceled \li FunctionFailed
+ * \li GeneralError \li HostMemory \li OK
+ * \li OperationNotInitialized \li SessionClosed \li SessionHandleInvalid
+ * \li SignatureInvalid \li SignatureLenRange
+ * @return true on success, false otherwise
+ */
+ bool C_Verify(SessionHandle session,
+ Byte* data_ptr,
+ Ulong data_len,
+ Byte* signature_ptr,
+ Ulong signature_len,
+ ReturnValue* return_value = ThrowException) const;
+
+ /**
+ * C_Verify verifies a signature in a single-part operation, where the signature is an appendix to the data, and plaintext cannot be recovered from the signature.
+ * @param session the session's handle
+ * @param data signed data
+ * @param signature signature
+ * @param return_value default value (`ThrowException`): throw exception on error.
+ * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+ * At least the following PKCS#11 return values may be returned:
+ * \li ArgumentsBad \li CryptokiNotInitialized \li DataInvalid
+ * \li DataLenRange \li DeviceError \li DeviceMemory
+ * \li DeviceRemoved \li FunctionCanceled \li FunctionFailed
+ * \li GeneralError \li HostMemory \li OK
+ * \li OperationNotInitialized \li SessionClosed \li SessionHandleInvalid
+ * \li SignatureInvalid \li SignatureLenRange
+ * @return true on success, false otherwise
+ */
+ template<typename TAllocA, typename TAllocB>
+ bool C_Verify(SessionHandle session,
+ const std::vector<uint8_t, TAllocA>& data,
+ std::vector<uint8_t, TAllocB>& signature,
+ ReturnValue* return_value = ThrowException) const
+ {
+ return C_Verify(session,
+ const_cast<Byte*>(data.data()),
+ static_cast<Ulong>(data.size()),
+ signature.data(),
+ static_cast<Ulong>(signature.size()),
+ return_value);
+ }
+
+ /**
+ * C_VerifyUpdate continues a multiple-part verification operation, where the signature is an appendix to the data, and plaintext cannot be recovered from the signature.
+ * @param session the session's handle
+ * @param part_ptr signed data
+ * @param part_len length of signed data
+ * @param return_value default value (`ThrowException`): throw exception on error.
+ * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+ * At least the following PKCS#11 return values may be returned:
+ * \li ArgumentsBad \li CryptokiNotInitialized \li DataLenRange
+ * \li DeviceError \li DeviceMemory \li DeviceRemoved
+ * \li FunctionCanceled \li FunctionFailed \li GeneralError
+ * \li HostMemory \li OK \li OperationNotInitialized
+ * \li SessionClosed \li SessionHandleInvalid
+ * @return true on success, false otherwise
+ */
+ bool C_VerifyUpdate(SessionHandle session,
+ Byte* part_ptr,
+ Ulong part_len,
+ ReturnValue* return_value = ThrowException) const;
+
+ /**
+ * C_VerifyUpdate continues a multiple-part verification operation, where the signature is an appendix to the data, and plaintext cannot be recovered from the signature.
+ * @param session the session's handle
+ * @param part signed data
+ * @param return_value default value (`ThrowException`): throw exception on error.
+ * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+ * At least the following PKCS#11 return values may be returned:
+ * \li ArgumentsBad \li CryptokiNotInitialized \li DataLenRange
+ * \li DeviceError \li DeviceMemory \li DeviceRemoved
+ * \li FunctionCanceled \li FunctionFailed \li GeneralError
+ * \li HostMemory \li OK \li OperationNotInitialized
+ * \li SessionClosed \li SessionHandleInvalid
+ * @return true on success, false otherwise
+ */
+ template<typename TAlloc>
+ bool C_VerifyUpdate(SessionHandle session,
+ std::vector<uint8_t, TAlloc> part,
+ ReturnValue* return_value = ThrowException) const
+ {
+ return C_VerifyUpdate(session, part.data(), static_cast<Ulong>(part.size()), return_value);
+ }
+
+ /**
+ * C_VerifyFinal finishes a multiple-part verification operation, checking the signature.
+ * @param session the session's handle
+ * @param signature_ptr signature to verify
+ * @param signature_len signature length
+ * @param return_value default value (`ThrowException`): throw exception on error.
+ * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+ * At least the following PKCS#11 return values may be returned:
+ * \li ArgumentsBad \li CryptokiNotInitialized \li DataLenRange
+ * \li DeviceError \li DeviceMemory \li DeviceRemoved
+ * \li FunctionCanceled \li FunctionFailed \li GeneralError
+ * \li HostMemory \li OK \li OperationNotInitialized
+ * \li SessionClosed \li SessionHandleInvalid \li SignatureInvalid
+ * \li SignatureLenRange
+ * @return true on success, false otherwise
+ */
+ bool C_VerifyFinal(SessionHandle session,
+ Byte* signature_ptr,
+ Ulong signature_len,
+ ReturnValue* return_value = ThrowException) const;
+
+ /**
+ * C_VerifyRecoverInit initializes a signature verification operation, where the data is recovered from the signature.
+ * @param session the session's handle
+ * @param mechanism_ptr the verification mechanism
+ * @param key verification key
+ * @param return_value default value (`ThrowException`): throw exception on error.
+ * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+ * At least the following PKCS#11 return values may be returned:
+ * \li ArgumentsBad \li CryptokiNotInitialized \li DeviceError
+ * \li DeviceMemory \li DeviceRemoved \li FunctionCanceled
+ * \li FunctionFailed \li GeneralError \li HostMemory
+ * \li KeyFunctionNotPermitted \li KeyHandleInvalid \li KeySizeRange
+ * \li KeyTypeInconsistent \li MechanismInvalid \li MechanismParamInvalid
+ * \li OK \li OperationActive \li PinExpired
+ * \li SessionClosed \li SessionHandleInvalid \li UserNotLoggedIn
+ * @return true on success, false otherwise
+ */
+ bool C_VerifyRecoverInit(SessionHandle session,
+ Mechanism* mechanism_ptr,
+ ObjectHandle key,
+ ReturnValue* return_value = ThrowException) const;
+
+ /**
+ * C_VerifyRecover verifies a signature in a single-part operation, where the data is recovered from the signature.
+ * @param session the session's handle
+ * @param signature_ptr signature to verify
+ * @param signature_len signature length
+ * @param data_ptr gets signed data
+ * @param data_len_ptr gets signed data len
+ * @param return_value default value (`ThrowException`): throw exception on error.
+ * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+ * At least the following PKCS#11 return values may be returned:
+ * \li ArgumentsBad \li BufferTooSmall \li CryptokiNotInitialized
+ * \li DataInvalid \li DataLenRange \li DeviceError
+ * \li DeviceMemory \li DeviceRemoved \li FunctionCanceled
+ * \li FunctionFailed \li GeneralError \li HostMemory
+ * \li OK \li OperationNotInitialized \li SessionClosed
+ * \li SessionHandleInvalid \li SignatureLenRange \li SignatureInvalid
+ * @return true on success, false otherwise
+ */
+ bool C_VerifyRecover(SessionHandle session,
+ Byte* signature_ptr,
+ Ulong signature_len,
+ Byte* data_ptr,
+ Ulong* data_len_ptr,
+ ReturnValue* return_value = ThrowException) const;
+
+ /****************************** Dual-purpose cryptographic functions ******************************/
+
+ /**
+ * C_DigestEncryptUpdate continues a multiple-part digesting and encryption operation.
+ * @param session session's handle
+ * @param part_ptr the plaintext data
+ * @param part_len plaintext length
+ * @param encrypted_part_ptr gets ciphertext
+ * @param encrypted_part_len_ptr gets c-text length
+ * @param return_value default value (`ThrowException`): throw exception on error.
+ * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+ * At least the following PKCS#11 return values may be returned:
+ * \li ArgumentsBad \li BufferTooSmall \li CryptokiNotInitialized
+ * \li DataLenRange \li DeviceError \li DeviceMemory
+ * \li DeviceRemoved \li FunctionCanceled \li FunctionFailed
+ * \li GeneralError \li HostMemory \li OK
+ * \li OperationNotInitialized \li SessionClosed \li SessionHandleInvalid
+ * @return true on success, false otherwise
+ */
+ bool C_DigestEncryptUpdate(SessionHandle session,
+ Byte* part_ptr,
+ Ulong part_len,
+ Byte* encrypted_part_ptr,
+ Ulong* encrypted_part_len_ptr,
+ ReturnValue* return_value = ThrowException) const ;
+
+ /**
+ * C_DecryptDigestUpdate continues a multiple-part decryption and digesting operation.
+ * @param session session's handle
+ * @param encrypted_part_ptr ciphertext
+ * @param encrypted_part_len ciphertext length
+ * @param part_ptr gets plaintext
+ * @param part_len_ptr gets plaintext len
+ * @param return_value default value (`ThrowException`): throw exception on error.
+ * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+ * At least the following PKCS#11 return values may be returned:
+ * \li ArgumentsBad \li BufferTooSmall \li CryptokiNotInitialized
+ * \li DeviceError \li DeviceMemory \li DeviceRemoved
+ * \li EncryptedDataInvalid \li EncryptedDataLenRange \li FunctionCanceled
+ * \li FunctionFailed \li GeneralError \li HostMemory
+ * \li OK \li OperationNotInitialized \li SessionClosed
+ * \li SessionHandleInvalid
+ * @return true on success, false otherwise
+ */
+ bool C_DecryptDigestUpdate(SessionHandle session,
+ Byte* encrypted_part_ptr,
+ Ulong encrypted_part_len,
+ Byte* part_ptr,
+ Ulong* part_len_ptr,
+ ReturnValue* return_value = ThrowException) const;
+
+ /**
+ * C_SignEncryptUpdate continues a multiple-part signing and encryption operation.
+ * @param session session's handle
+ * @param part_ptr the plaintext data
+ * @param part_len plaintext length
+ * @param encrypted_part_ptr gets ciphertext
+ * @param encrypted_part_len_ptr gets c-text length
+ * @param return_value default value (`ThrowException`): throw exception on error.
+ * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+ * At least the following PKCS#11 return values may be returned:
+ * \li ArgumentsBad \li BufferTooSmall \li CryptokiNotInitialized
+ * \li DataLenRange \li DeviceError \li DeviceMemory
+ * \li DeviceRemoved \li FunctionCanceled \li FunctionFailed
+ * \li GeneralError \li HostMemory \li OK
+ * \li OperationNotInitialized \li SessionClosed \li SessionHandleInvalid
+ * \li UserNotLoggedIn
+ * @return true on success, false otherwise
+ */
+ bool C_SignEncryptUpdate(SessionHandle session,
+ Byte* part_ptr,
+ Ulong part_len,
+ Byte* encrypted_part_ptr,
+ Ulong* encrypted_part_len_ptr,
+ ReturnValue* return_value = ThrowException) const;
+
+ /**
+ * C_DecryptVerifyUpdate continues a multiple-part decryption and verify operation.
+ * @param session session's handle
+ * @param encrypted_part_ptr ciphertext
+ * @param encrypted_part_len ciphertext length
+ * @param part_ptr gets plaintext
+ * @param part_len_ptr gets p-text length
+ * @param return_value default value (`ThrowException`): throw exception on error.
+ * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+ * At least the following PKCS#11 return values may be returned:
+ * \li ArgumentsBad \li BufferTooSmall \li CryptokiNotInitialized
+ * \li DataLenRange \li DeviceError \li DeviceMemory
+ * \li DeviceRemoved \li EncryptedDataInvalid \li EncryptedDataLenRange
+ * \li FunctionCanceled \li FunctionFailed \li GeneralError
+ * \li HostMemory \li OK \li OperationNotInitialized
+ * \li SessionClosed \li SessionHandleInvalid
+ * @return true on success, false otherwise
+ */
+ bool C_DecryptVerifyUpdate(SessionHandle session,
+ Byte* encrypted_part_ptr,
+ Ulong encrypted_part_len,
+ Byte* part_ptr,
+ Ulong* part_len_ptr,
+ ReturnValue* return_value = ThrowException) const;
+
+ /****************************** Key management functions ******************************/
+
+ /**
+ * C_GenerateKey generates a secret key, creating a new key object.
+ * @param session the session's handle
+ * @param mechanism_ptr key generation mech.
+ * @param attribute_template_ptr template for new key
+ * @param count # of attrs in template
+ * @param key_ptr gets handle of new key
+ * @param return_value default value (`ThrowException`): throw exception on error.
+ * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+ * At least the following PKCS#11 return values may be returned:
+ * \li ArgumentsBad \li AttributeReadOnly \li AttributeTypeInvalid
+ * \li AttributeValueInvalid \li CryptokiNotInitialized \li CurveNotSupported
+ * \li DeviceError \li DeviceMemory \li DeviceRemoved
+ * \li FunctionCanceled \li FunctionFailed \li GeneralError
+ * \li HostMemory \li MechanismInvalid \li MechanismParamInvalid
+ * \li OK \li OperationActive \li PinExpired
+ * \li SessionClosed \li SessionHandleInvalid \li SessionReadOnly
+ * \li TemplateIncomplete \li TemplateInconsistent \li TokenWriteProtected
+ * \li UserNotLoggedIn
+ * @return true on success, false otherwise
+ */
+ bool C_GenerateKey(SessionHandle session,
+ Mechanism* mechanism_ptr,
+ Attribute* attribute_template_ptr,
+ Ulong count,
+ ObjectHandle* key_ptr,
+ ReturnValue* return_value = ThrowException) const;
+
+ /**
+ * C_GenerateKeyPair generates a public-key/private-key pair, creating new key objects.
+ * @param session session handle
+ * @param mechanism_ptr key-gen mech.
+ * @param public_key_template_ptr template for pub. key
+ * @param public_key_attribute_count # pub. attrs.
+ * @param private_key_template_ptr template for priv. key
+ * @param private_key_attribute_count # priv. attrs.
+ * @param public_key_ptr gets pub. key handle
+ * @param private_key_ptr gets priv. key handle
+ * @param return_value default value (`ThrowException`): throw exception on error.
+ * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+ * At least the following PKCS#11 return values may be returned:
+ * \li ArgumentsBad \li AttributeReadOnly \li AttributeTypeInvalid
+ * \li AttributeValueInvalid \li CryptokiNotInitialized \li CurveNotSupported
+ * \li DeviceError \li DeviceMemory \li DeviceRemoved
+ * \li DomainParamsInvalid \li FunctionCanceled \li FunctionFailed
+ * \li GeneralError \li HostMemory \li MechanismInvalid
+ * \li MechanismParamInvalid \li OK \li OperationActive
+ * \li PinExpired \li SessionClosed \li SessionHandleInvalid
+ * \li SessionReadOnly \li TemplateIncomplete \li TemplateInconsistent
+ * \li TokenWriteProtected \li UserNotLoggedIn
+ * @return true on success, false otherwise
+ */
+ bool C_GenerateKeyPair(SessionHandle session,
+ Mechanism* mechanism_ptr,
+ Attribute* public_key_template_ptr,
+ Ulong public_key_attribute_count,
+ Attribute* private_key_template_ptr,
+ Ulong private_key_attribute_count,
+ ObjectHandle* public_key_ptr,
+ ObjectHandle* private_key_ptr,
+ ReturnValue* return_value = ThrowException) const;
+
+ /**
+ * C_WrapKey wraps (i.e., encrypts) a key.
+ * @param session the session's handle
+ * @param mechanism_ptr the wrapping mechanism
+ * @param wrapping_key wrapping key
+ * @param key key to be wrapped
+ * @param wrapped_key_ptr gets wrapped key
+ * @param wrapped_key_len_ptr gets wrapped key size
+ * @param return_value default value (`ThrowException`): throw exception on error.
+ * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+ * At least the following PKCS#11 return values may be returned:
+ * \li ArgumentsBad \li BufferTooSmall \li CryptokiNotInitialized
+ * \li DeviceError \li DeviceMemory \li DeviceRemoved
+ * \li FunctionCanceled \li FunctionFailed \li GeneralError
+ * \li HostMemory \li KeyHandleInvalid \li KeyNotWrappable
+ * \li KeySizeRange \li KeyUnextractable \li MechanismInvalid
+ * \li MechanismParamInvalid \li OK \li OperationActive
+ * \li PinExpired \li SessionClosed \li SessionHandleInvalid
+ * \li UserNotLoggedIn \li WrappingKeyHandleInvalid \li WrappingKeySizeRange
+ * \li WrappingKeyTypeInconsistent
+ * @return true on success, false otherwise
+ */
+ bool C_WrapKey(SessionHandle session,
+ Mechanism* mechanism_ptr,
+ ObjectHandle wrapping_key,
+ ObjectHandle key,
+ Byte* wrapped_key_ptr,
+ Ulong* wrapped_key_len_ptr,
+ ReturnValue* return_value = ThrowException) const;
+
+ /**
+ * C_UnwrapKey unwraps (decrypts) a wrapped key, creating a new key object.
+ * @param session session's handle
+ * @param mechanism_ptr unwrapping mech.
+ * @param unwrapping_key unwrapping key
+ * @param wrapped_key_ptr the wrapped key
+ * @param wrapped_key_len wrapped key len
+ * @param attribute_template_ptr new key template
+ * @param attribute_count template length
+ * @param key_ptr gets new handle
+ * @param return_value default value (`ThrowException`): throw exception on error.
+ * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+ * At least the following PKCS#11 return values may be returned:
+ * \li ArgumentsBad \li AttributeReadOnly \li AttributeTypeInvalid
+ * \li AttributeValueInvalid \li BufferTooSmall \li CryptokiNotInitialized
+ * \li CurveNotSupported \li DeviceError \li DeviceMemory
+ * \li DeviceRemoved \li DomainParamsInvalid \li FunctionCanceled
+ * \li FunctionFailed \li GeneralError \li HostMemory
+ * \li MechanismInvalid \li MechanismParamInvalid \li OK
+ * \li OperationActive \li PinExpired \li SessionClosed
+ * \li SessionHandleInvalid \li SessionReadOnly \li TemplateIncomplete
+ * \li TemplateInconsistent \li TokenWriteProtected \li UnwrappingKeyHandleInvalid
+ * \li UnwrappingKeySizeRange \li UnwrappingKeyTypeInconsistent \li UserNotLoggedIn
+ * \li WrappedKeyInvalid \li WrappedKeyLenRange
+ * @return true on success, false otherwise
+ */
+ bool C_UnwrapKey(SessionHandle session,
+ Mechanism* mechanism_ptr,
+ ObjectHandle unwrapping_key,
+ Byte* wrapped_key_ptr,
+ Ulong wrapped_key_len,
+ Attribute* attribute_template_ptr,
+ Ulong attribute_count,
+ ObjectHandle* key_ptr,
+ ReturnValue* return_value = ThrowException) const;
+
+ /**
+ * C_DeriveKey derives a key from a base key, creating a new key object.
+ * @param session session's handle
+ * @param mechanism_ptr key deriv. mech.
+ * @param base_key base key
+ * @param attribute_template_ptr new key template
+ * @param attribute_count template length
+ * @param key_ptr gets new handle
+ * @param return_value default value (`ThrowException`): throw exception on error.
+ * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+ * At least the following PKCS#11 return values may be returned:
+ * \li ArgumentsBad \li AttributeReadOnly \li AttributeTypeInvalid
+ * \li AttributeValueInvalid \li CryptokiNotInitialized \li CurveNotSupported
+ * \li DeviceError \li DeviceMemory \li DeviceRemoved
+ * \li DomainParamsInvalid \li FunctionCanceled \li FunctionFailed
+ * \li GeneralError \li HostMemory \li KeyHandleInvalid
+ * \li KeySizeRange \li KeyTypeInconsistent \li MechanismInvalid
+ * \li MechanismParamInvalid \li OK \li OperationActive
+ * \li PinExpired \li SessionClosed \li SessionHandleInvalid
+ * \li SessionReadOnly \li TemplateIncomplete \li TemplateInconsistent
+ * \li TokenWriteProtected \li UserNotLoggedIn
+ * @return true on success, false otherwise
+ */
+ bool C_DeriveKey(SessionHandle session,
+ Mechanism* mechanism_ptr,
+ ObjectHandle base_key,
+ Attribute* attribute_template_ptr,
+ Ulong attribute_count,
+ ObjectHandle* key_ptr,
+ ReturnValue* return_value = ThrowException) const;
+
+ /****************************** Random number generation functions ******************************/
+
+ /**
+ * C_SeedRandom mixes additional seed material into the token's random number generator.
+ * @param session the session's handle
+ * @param seed_ptr the seed material
+ * @param seed_len length of seed material
+ * @param return_value default value (`ThrowException`): throw exception on error.
+ * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+ * At least the following PKCS#11 return values may be returned:
+ * \li ArgumentsBad \li CryptokiNotInitialized \li DeviceError
+ * \li DeviceMemory \li DeviceRemoved \li FunctionCanceled
+ * \li FunctionFailed \li GeneralError \li HostMemory
+ * \li OK \li OperationActive \li RandomSeedNotSupported
+ * \li RandomNoRng \li SessionClosed \li SessionHandleInvalid
+ * \li UserNotLoggedIn
+ * @return true on success, false otherwise
+ */
+ bool C_SeedRandom(SessionHandle session,
+ Byte* seed_ptr,
+ Ulong seed_len,
+ ReturnValue* return_value = ThrowException) const;
+
+ /**
+ * C_GenerateRandom generates random data.
+ * @param session the session's handle
+ * @param random_data_ptr receives the random data
+ * @param random_len # of bytes to generate
+ * @param return_value default value (`ThrowException`): throw exception on error.
+ * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+ * At least the following PKCS#11 return values may be returned:
+ * \li ArgumentsBad \li CryptokiNotInitialized \li DeviceError
+ * \li DeviceMemory \li DeviceRemoved \li FunctionCanceled
+ * \li FunctionFailed \li GeneralError \li HostMemory
+ * \li OK \li OperationActive \li RandomNoRng
+ * \li SessionClosed \li SessionHandleInvalid \li UserNotLoggedIn
+ * @return true on success, false otherwise
+ */
+ bool C_GenerateRandom(SessionHandle session,
+ Byte* random_data_ptr,
+ Ulong random_len,
+ ReturnValue* return_value = ThrowException) const;
+
+ /****************************** Parallel function management functions ******************************/
+
+ /**
+ * C_GetFunctionStatus is a legacy function; it obtains an updated status of a function running in parallel with an application.
+ * @param session the session's handle
+ * @param return_value default value (`ThrowException`): throw exception on error.
+ * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+ * At least the following PKCS#11 return values may be returned:
+ * \li CryptokiNotInitialized \li FunctionFailed \li FunctionNotParallel
+ * \li GeneralError \li HostMemory \li SessionHandleInvalid
+ * \li SessionClosed
+ * @return true on success, false otherwise
+ */
+ bool C_GetFunctionStatus(SessionHandle session,
+ ReturnValue* return_value = ThrowException) const;
+
+ /**
+ * C_CancelFunction is a legacy function; it cancels a function running in parallel.
+ * @param session the session's handle
+ * @param return_value default value (`ThrowException`): throw exception on error.
+ * if a non-NULL pointer is passed: return_value receives the return value of the PKCS#11 function and no exception is thrown.
+ * At least the following PKCS#11 return values may be returned:
+ * \li CryptokiNotInitialized \li FunctionFailed \li FunctionNotParallel
+ * \li GeneralError \li HostMemory \li SessionHandleInvalid
+ * \li SessionClosed
+ * @return true on success, false otherwise
+ */
+ bool C_CancelFunction(SessionHandle session,
+ ReturnValue* return_value = ThrowException) const;
+
+ private:
+ const FunctionListPtr m_func_list_ptr;
+ };
+
+class BOTAN_PUBLIC_API(2,0) PKCS11_Error : public Exception
+ {
+ public:
+ explicit PKCS11_Error(const std::string& what) :
+ Exception("PKCS11 error", what)
+ {
+ }
+
+ ErrorType error_type() const noexcept override { return ErrorType::Pkcs11Error; }
+ };
+
+class BOTAN_PUBLIC_API(2,0) PKCS11_ReturnError final : public PKCS11_Error
+ {
+ public:
+ explicit PKCS11_ReturnError(ReturnValue return_val) :
+ PKCS11_Error(std::to_string(static_cast< uint32_t >(return_val))),
+ m_return_val(return_val)
+ {}
+
+ inline ReturnValue get_return_value() const
+ {
+ return m_return_val;
+ }
+
+ int error_code() const noexcept override
+ {
+ return static_cast<int>(m_return_val);
+ }
+
+ private:
+ const ReturnValue m_return_val;
+ };
+
+}
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/prov/pkcs11/p11_ecc_key.cpp b/comm/third_party/botan/src/lib/prov/pkcs11/p11_ecc_key.cpp
new file mode 100644
index 0000000000..874621b780
--- /dev/null
+++ b/comm/third_party/botan/src/lib/prov/pkcs11/p11_ecc_key.cpp
@@ -0,0 +1,136 @@
+/*
+* PKCS#11 ECC
+* (C) 2016 Daniel Neus, Sirrix AG
+* (C) 2016 Philipp Weber, Sirrix AG
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/p11_ecc_key.h>
+#include <botan/pk_keys.h>
+
+#if defined(BOTAN_HAS_ECC_PUBLIC_KEY_CRYPTO)
+
+#include <botan/workfactor.h>
+#include <botan/ber_dec.h>
+
+namespace Botan {
+namespace PKCS11 {
+namespace {
+/// Converts a DER-encoded ANSI X9.62 ECPoint to PointGFp
+PointGFp decode_public_point(const secure_vector<uint8_t>& ec_point_data, const EC_Group& group)
+ {
+ secure_vector<uint8_t> ec_point;
+ BER_Decoder(ec_point_data).decode(ec_point, OCTET_STRING);
+ return group.OS2ECP(ec_point);
+ }
+}
+
+EC_PublicKeyGenerationProperties::EC_PublicKeyGenerationProperties(const std::vector<uint8_t>& ec_params)
+ : PublicKeyProperties(KeyType::Ec), m_ec_params(ec_params)
+ {
+ add_binary(AttributeType::EcParams, m_ec_params);
+ }
+
+EC_PublicKeyImportProperties::EC_PublicKeyImportProperties(const std::vector<uint8_t>& ec_params,
+ const std::vector<uint8_t>& ec_point)
+ : PublicKeyProperties(KeyType::Ec), m_ec_params(ec_params), m_ec_point(ec_point)
+ {
+ add_binary(AttributeType::EcParams, m_ec_params);
+ add_binary(AttributeType::EcPoint, m_ec_point);
+ }
+
+PKCS11_EC_PublicKey::PKCS11_EC_PublicKey(Session& session, ObjectHandle handle)
+ : Object(session, handle)
+ {
+ secure_vector<uint8_t> ec_parameters = get_attribute_value(AttributeType::EcParams);
+ m_domain_params = EC_Group(unlock(ec_parameters));
+ m_public_key = decode_public_point(get_attribute_value(AttributeType::EcPoint), m_domain_params);
+ m_domain_encoding = EC_DOMPAR_ENC_EXPLICIT;
+ }
+
+PKCS11_EC_PublicKey::PKCS11_EC_PublicKey(Session& session, const EC_PublicKeyImportProperties& props)
+ : Object(session, props)
+ {
+ m_domain_params = EC_Group(props.ec_params());
+
+ secure_vector<uint8_t> ec_point;
+ BER_Decoder(props.ec_point()).decode(ec_point, OCTET_STRING);
+ m_public_key = m_domain_params.OS2ECP(ec_point);
+ m_domain_encoding = EC_DOMPAR_ENC_EXPLICIT;
+ }
+
+EC_PrivateKeyImportProperties::EC_PrivateKeyImportProperties(const std::vector<uint8_t>& ec_params, const BigInt& value)
+ : PrivateKeyProperties(KeyType::Ec), m_ec_params(ec_params), m_value(value)
+ {
+ add_binary(AttributeType::EcParams, m_ec_params);
+ add_binary(AttributeType::Value, BigInt::encode(m_value));
+ }
+
+PKCS11_EC_PrivateKey::PKCS11_EC_PrivateKey(Session& session, ObjectHandle handle)
+ : Object(session, handle), m_domain_params(), m_public_key()
+ {
+ secure_vector<uint8_t> ec_parameters = get_attribute_value(AttributeType::EcParams);
+ m_domain_params = EC_Group(unlock(ec_parameters));
+ }
+
+PKCS11_EC_PrivateKey::PKCS11_EC_PrivateKey(Session& session, const EC_PrivateKeyImportProperties& props)
+ : Object(session, props)
+ {
+ m_domain_params = EC_Group(props.ec_params());
+ }
+
+PKCS11_EC_PrivateKey::PKCS11_EC_PrivateKey(Session& session, const std::vector<uint8_t>& ec_params,
+ const EC_PrivateKeyGenerationProperties& props)
+ : Object(session)
+ {
+ m_domain_params = EC_Group(ec_params);
+
+ EC_PublicKeyGenerationProperties pub_key_props(ec_params);
+ pub_key_props.set_verify(true);
+ pub_key_props.set_private(false);
+ pub_key_props.set_token(false); // don't create a persistent public key object
+
+ ObjectHandle pub_key_handle = CK_INVALID_HANDLE;
+ ObjectHandle priv_key_handle = CK_INVALID_HANDLE;
+ Mechanism mechanism = { CKM_EC_KEY_PAIR_GEN, nullptr, 0 };
+ session.module()->C_GenerateKeyPair(session.handle(), &mechanism,
+ pub_key_props.data(), static_cast<Ulong>(pub_key_props.count()),
+ props.data(), static_cast<Ulong>(props.count()),
+ &pub_key_handle, &priv_key_handle);
+
+ this->reset_handle(priv_key_handle);
+
+ Object public_key(session, pub_key_handle);
+ m_public_key = decode_public_point(public_key.get_attribute_value(AttributeType::EcPoint), m_domain_params);
+ }
+
+size_t PKCS11_EC_PrivateKey::key_length() const
+ {
+ return m_domain_params.get_order().bits();
+ }
+
+std::vector<uint8_t> PKCS11_EC_PrivateKey::public_key_bits() const
+ {
+ return public_point().encode(PointGFp::COMPRESSED);
+ }
+
+size_t PKCS11_EC_PrivateKey::estimated_strength() const
+ {
+ return ecp_work_factor(key_length());
+ }
+
+bool PKCS11_EC_PrivateKey::check_key(RandomNumberGenerator&, bool) const
+ {
+ return m_public_key.on_the_curve();
+ }
+
+AlgorithmIdentifier PKCS11_EC_PrivateKey::algorithm_identifier() const
+ {
+ return AlgorithmIdentifier(get_oid(), domain().DER_encode(EC_DOMPAR_ENC_EXPLICIT));
+ }
+}
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/prov/pkcs11/p11_ecc_key.h b/comm/third_party/botan/src/lib/prov/pkcs11/p11_ecc_key.h
new file mode 100644
index 0000000000..e2fd35b1a3
--- /dev/null
+++ b/comm/third_party/botan/src/lib/prov/pkcs11/p11_ecc_key.h
@@ -0,0 +1,223 @@
+/*
+* PKCS#11 ECC
+* (C) 2016 Daniel Neus, Sirrix AG
+* (C) 2016 Philipp Weber, Sirrix AG
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_P11_ECC_H_
+#define BOTAN_P11_ECC_H_
+
+#include <botan/p11_object.h>
+#include <botan/pk_keys.h>
+
+#if defined(BOTAN_HAS_ECC_PUBLIC_KEY_CRYPTO)
+#include <botan/ecc_key.h>
+#include <botan/ec_group.h>
+#include <botan/asn1_obj.h>
+#include <vector>
+
+namespace Botan {
+namespace PKCS11 {
+
+class Session;
+
+/// Properties for generating a PKCS#11 EC public key
+class BOTAN_PUBLIC_API(2,0) EC_PublicKeyGenerationProperties final : public PublicKeyProperties
+ {
+ public:
+ /// @param ec_params DER-encoding of an ANSI X9.62 Parameters value
+ EC_PublicKeyGenerationProperties(const std::vector<uint8_t>& ec_params);
+
+ /// @return the DER-encoding of the ec parameters according to ANSI X9.62
+ inline const std::vector<uint8_t>& ec_params() const
+ {
+ return m_ec_params;
+ }
+
+ private:
+ const std::vector<uint8_t> m_ec_params;
+ };
+
+/// Properties for importing a PKCS#11 EC public key
+class BOTAN_PUBLIC_API(2,0) EC_PublicKeyImportProperties final : public PublicKeyProperties
+ {
+ public:
+ /**
+ * @param ec_params DER-encoding of an ANSI X9.62 Parameters value
+ * @param ec_point DER-encoding of ANSI X9.62 ECPoint value Q
+ */
+ EC_PublicKeyImportProperties(const std::vector<uint8_t>& ec_params, const std::vector<uint8_t>& ec_point);
+
+ /// @return the DER-encoding of the ec parameters according to ANSI X9.62
+ inline const std::vector<uint8_t>& ec_params() const
+ {
+ return m_ec_params;
+ }
+
+ /// @return the DER-encoding of the ec public point according to ANSI X9.62
+ inline const std::vector<uint8_t>& ec_point() const
+ {
+ return m_ec_point;
+ }
+
+ private:
+ const std::vector<uint8_t> m_ec_params;
+ const std::vector<uint8_t> m_ec_point;
+ };
+
+/// Represents a PKCS#11 EC public key
+class BOTAN_PUBLIC_API(2,0) PKCS11_EC_PublicKey : public virtual EC_PublicKey,
+ public Object
+ {
+ public:
+ static const ObjectClass Class = ObjectClass::PublicKey;
+
+ /**
+ * Creates a PKCS11_EC_PublicKey object from an existing PKCS#11 EC public key
+ * @param session the session to use
+ * @param handle the handle of the ecc public key
+ */
+ PKCS11_EC_PublicKey(Session& session, ObjectHandle handle);
+
+ /**
+ * Imports an EC public key
+ * @param session the session to use
+ * @param props the attributes of the public key
+ */
+ PKCS11_EC_PublicKey(Session& session, const EC_PublicKeyImportProperties& props);
+ };
+
+/// Properties for generating a PKCS#11 EC private key
+class BOTAN_PUBLIC_API(2,0) EC_PrivateKeyGenerationProperties final : public PrivateKeyProperties
+ {
+ public:
+ EC_PrivateKeyGenerationProperties()
+ : PrivateKeyProperties(KeyType::Ec)
+ {}
+ };
+
+/// Properties for importing a PKCS#11 EC private key
+class BOTAN_PUBLIC_API(2,0) EC_PrivateKeyImportProperties final : public PrivateKeyProperties
+ {
+ public:
+ /**
+ * @param ec_params DER-encoding of an ANSI X9.62 Parameters value
+ * @param value ANSI X9.62 private value d
+ */
+ EC_PrivateKeyImportProperties(const std::vector<uint8_t>& ec_params, const BigInt& value);
+
+ /// @return the DER-encoding of the ec parameters according to ANSI X9.62
+ inline const std::vector<uint8_t>& ec_params() const
+ {
+ return m_ec_params;
+ }
+
+ /// @return the value of the ec private key
+ inline const BigInt& value() const
+ {
+ return m_value;
+ }
+
+ private:
+ const std::vector<uint8_t> m_ec_params;
+ const BigInt m_value;
+ };
+
+// note: don't inherit from PKCS11_EC_PublicKey: a private key object IS NOT A public key object on a smartcard (-> two different objects)
+// note: don't inherit from EC_PublicKey: the public key can not be extracted from a PKCS11-EC-PrivateKey (its only attributes are CKA_EC_PARAMS and CKA_VALUE)
+/// Represents a PKCS#11 EC private key
+class BOTAN_PUBLIC_API(2,0) PKCS11_EC_PrivateKey : public virtual Private_Key,
+ public Object
+ {
+ public:
+ static const ObjectClass Class = ObjectClass::PrivateKey;
+
+ /**
+ * Creates a PKCS11_EC_PrivateKey object from an existing PKCS#11 EC private key
+ * @param session the session to use
+ * @param handle the handle of the EC private key
+ */
+ PKCS11_EC_PrivateKey(Session& session, ObjectHandle handle);
+
+ /**
+ * Imports an EC private key
+ * @param session the session to use
+ * @param props the attributes of the private key
+ */
+ PKCS11_EC_PrivateKey(Session& session, const EC_PrivateKeyImportProperties& props);
+
+ /**
+ * Generates a PKCS#11 EC private key
+ * @param session the session to use
+ * @param ec_params DER-encoding of an ANSI X9.62 Parameters value
+ * @param props the attributes of the private key
+ * @note no persistent public key object will be created
+ */
+ PKCS11_EC_PrivateKey(Session& session, const std::vector<uint8_t>& ec_params,
+ const EC_PrivateKeyGenerationProperties& props);
+
+ /// @returns the domain of the EC private key
+ inline const EC_Group& domain() const
+ {
+ return m_domain_params;
+ }
+
+ /**
+ * Sets the associated public point of this private key
+ * @param point the public point
+ * @param point_encoding encoding of the point (default DER-encoded)
+ */
+ void set_public_point(const PointGFp& point, PublicPointEncoding point_encoding = PublicPointEncoding::Der)
+ {
+ m_public_key = point;
+ m_point_encoding = point_encoding;
+ }
+
+ /**
+ * Gets the public_point
+ * @note the public key must be set using `set_public_point`
+ * because it is not possible to infer the public key from a PKCS#11 EC private key
+ * @return the public point of the private key
+ * @throws Exception if the public point was not set using set_public_point()
+ */
+ const PointGFp& public_point() const
+ {
+ if(m_public_key.is_zero())
+ {
+ throw Invalid_State("Public point not set. Inferring the public key from a PKCS#11 ec private key is not possible.");
+ }
+ return m_public_key;
+ }
+
+ /// @return the encoding format for the public point when it is passed to cryptoki functions as an argument
+ PublicPointEncoding point_encoding() const
+ {
+ return m_point_encoding;
+ }
+
+ // Private_Key methods
+
+ std::vector<uint8_t> public_key_bits() const override;
+
+ std::size_t key_length() const override;
+
+ std::size_t estimated_strength() const override;
+
+ bool check_key(RandomNumberGenerator&, bool) const override;
+
+ AlgorithmIdentifier algorithm_identifier() const override;
+
+ private:
+ EC_Group m_domain_params;
+ PointGFp m_public_key;
+ PublicPointEncoding m_point_encoding = PublicPointEncoding::Der;
+ };
+}
+
+}
+
+#endif
+
+#endif
diff --git a/comm/third_party/botan/src/lib/prov/pkcs11/p11_ecdh.cpp b/comm/third_party/botan/src/lib/prov/pkcs11/p11_ecdh.cpp
new file mode 100644
index 0000000000..4110fd486e
--- /dev/null
+++ b/comm/third_party/botan/src/lib/prov/pkcs11/p11_ecdh.cpp
@@ -0,0 +1,125 @@
+/*
+* PKCS#11 ECDH
+* (C) 2016 Daniel Neus, Sirrix AG
+* (C) 2016 Philipp Weber, Sirrix AG
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/p11_ecdh.h>
+
+#if defined(BOTAN_HAS_ECDH)
+
+#include <botan/internal/p11_mechanism.h>
+#include <botan/der_enc.h>
+#include <botan/pk_ops.h>
+#include <botan/rng.h>
+
+namespace Botan {
+
+namespace PKCS11 {
+
+ECDH_PublicKey PKCS11_ECDH_PublicKey::export_key() const
+ {
+ return ECDH_PublicKey(domain(), public_point());
+ }
+
+ECDH_PrivateKey PKCS11_ECDH_PrivateKey::export_key() const
+ {
+ auto priv_key = get_attribute_value(AttributeType::Value);
+
+ Null_RNG rng;
+ return ECDH_PrivateKey(rng, domain(), BigInt::decode(priv_key));
+ }
+
+secure_vector<uint8_t> PKCS11_ECDH_PrivateKey::private_key_bits() const
+ {
+ return export_key().private_key_bits();
+ }
+
+namespace {
+class PKCS11_ECDH_KA_Operation final : public PK_Ops::Key_Agreement
+ {
+ public:
+ PKCS11_ECDH_KA_Operation(const PKCS11_EC_PrivateKey& key, const std::string& params)
+ : PK_Ops::Key_Agreement(), m_key(key), m_mechanism(MechanismWrapper::create_ecdh_mechanism(params))
+ {}
+
+ size_t agreed_value_size() const override { return m_key.domain().get_p_bytes(); }
+
+ /// The encoding in V2.20 was not specified and resulted in different implementations choosing different encodings.
+ /// Applications relying only on a V2.20 encoding (e.g. the DER variant) other than the one specified now (raw) may not work with all V2.30 compliant tokens.
+ secure_vector<uint8_t> agree(size_t key_len, const uint8_t other_key[], size_t other_key_len, const uint8_t salt[],
+ size_t salt_len) override
+ {
+ std::vector<uint8_t> der_encoded_other_key;
+ if(m_key.point_encoding() == PublicPointEncoding::Der)
+ {
+ DER_Encoder(der_encoded_other_key).encode(other_key, other_key_len, OCTET_STRING);
+ m_mechanism.set_ecdh_other_key(der_encoded_other_key.data(), der_encoded_other_key.size());
+ }
+ else
+ {
+ m_mechanism.set_ecdh_other_key(other_key, other_key_len);
+ }
+
+ if(salt != nullptr && salt_len > 0)
+ {
+ m_mechanism.set_ecdh_salt(salt, salt_len);
+ }
+
+ ObjectHandle secret_handle = 0;
+ AttributeContainer attributes;
+ attributes.add_bool(AttributeType::Sensitive, false);
+ attributes.add_bool(AttributeType::Extractable, true);
+ attributes.add_numeric(AttributeType::Class, static_cast< CK_OBJECT_CLASS >(ObjectClass::SecretKey));
+ attributes.add_numeric(AttributeType::KeyType, static_cast< CK_KEY_TYPE >(KeyType::GenericSecret));
+ attributes.add_numeric(AttributeType::ValueLen, static_cast< CK_ULONG >(key_len));
+ m_key.module()->C_DeriveKey(m_key.session().handle(), m_mechanism.data(), m_key.handle(), attributes.data(),
+ static_cast<Ulong>(attributes.count()), &secret_handle);
+
+ Object secret_object(m_key.session(), secret_handle);
+ secure_vector<uint8_t> secret = secret_object.get_attribute_value(AttributeType::Value);
+ if(secret.size() < key_len)
+ {
+ throw PKCS11_Error("ECDH key derivation secret length is too short");
+ }
+ secret.resize(key_len);
+ return secret;
+ }
+
+ private:
+ const PKCS11_EC_PrivateKey& m_key;
+ MechanismWrapper m_mechanism;
+ };
+
+}
+
+std::unique_ptr<PK_Ops::Key_Agreement>
+PKCS11_ECDH_PrivateKey::create_key_agreement_op(RandomNumberGenerator&,
+ const std::string& params,
+ const std::string& /*provider*/) const
+ {
+ return std::unique_ptr<PK_Ops::Key_Agreement>(new PKCS11_ECDH_KA_Operation(*this, params));
+ }
+
+PKCS11_ECDH_KeyPair generate_ecdh_keypair(Session& session, const EC_PublicKeyGenerationProperties& pub_props,
+ const EC_PrivateKeyGenerationProperties& priv_props)
+ {
+ ObjectHandle pub_key_handle = 0;
+ ObjectHandle priv_key_handle = 0;
+
+ Mechanism mechanism = { static_cast< CK_MECHANISM_TYPE >(MechanismType::EcKeyPairGen), nullptr, 0 };
+
+ session.module()->C_GenerateKeyPair(session.handle(), &mechanism,
+ pub_props.data(), static_cast<Ulong>(pub_props.count()),
+ priv_props.data(), static_cast<Ulong>(priv_props.count()),
+ &pub_key_handle, &priv_key_handle);
+
+ return std::make_pair(PKCS11_ECDH_PublicKey(session, pub_key_handle), PKCS11_ECDH_PrivateKey(session, priv_key_handle));
+ }
+
+}
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/prov/pkcs11/p11_ecdh.h b/comm/third_party/botan/src/lib/prov/pkcs11/p11_ecdh.h
new file mode 100644
index 0000000000..bbef9a3e5b
--- /dev/null
+++ b/comm/third_party/botan/src/lib/prov/pkcs11/p11_ecdh.h
@@ -0,0 +1,127 @@
+/*
+* PKCS#11 ECDH
+* (C) 2016 Daniel Neus, Sirrix AG
+* (C) 2016 Philipp Weber, Sirrix AG
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_P11_ECDH_H_
+#define BOTAN_P11_ECDH_H_
+
+#include <botan/p11.h>
+
+#if defined(BOTAN_HAS_ECDH)
+
+#include <botan/p11_ecc_key.h>
+#include <botan/ecdh.h>
+
+#include <string>
+#include <vector>
+
+namespace Botan {
+namespace PKCS11 {
+class Session;
+
+/// Represents a PKCS#11 ECDH public key
+class BOTAN_PUBLIC_API(2,0) PKCS11_ECDH_PublicKey : public PKCS11_EC_PublicKey
+ {
+ public:
+ /**
+ * Create a PKCS11_ECDH_PublicKey object from an existing PKCS#11 ECDH public key
+ * @param session the session to use
+ * @param handle the handle of the ECDH public key
+ */
+ PKCS11_ECDH_PublicKey(Session& session, ObjectHandle handle)
+ : EC_PublicKey(), PKCS11_EC_PublicKey(session, handle)
+ {}
+
+ /**
+ * Imports a ECDH public key
+ * @param session the session to use
+ * @param props the attributes of the public key
+ */
+ PKCS11_ECDH_PublicKey(Session& session, const EC_PublicKeyImportProperties& props)
+ : EC_PublicKey(), PKCS11_EC_PublicKey(session, props)
+ {}
+
+ inline std::string algo_name() const override
+ {
+ return "ECDH";
+ }
+
+ /// @return the exported ECDH public key
+ ECDH_PublicKey export_key() const;
+ };
+
+/// Represents a PKCS#11 ECDH private key
+class BOTAN_PUBLIC_API(2,0) PKCS11_ECDH_PrivateKey final : public virtual PKCS11_EC_PrivateKey, public virtual PK_Key_Agreement_Key
+ {
+ public:
+ /**
+ * Creates a PKCS11_ECDH_PrivateKey object from an existing PKCS#11 ECDH private key
+ * @param session the session to use
+ * @param handle the handle of the ECDH private key
+ */
+ PKCS11_ECDH_PrivateKey(Session& session, ObjectHandle handle)
+ : PKCS11_EC_PrivateKey(session, handle)
+ {}
+
+ /**
+ * Imports an ECDH private key
+ * @param session the session to use
+ * @param props the attributes of the private key
+ */
+ PKCS11_ECDH_PrivateKey(Session& session, const EC_PrivateKeyImportProperties& props)
+ : PKCS11_EC_PrivateKey(session, props)
+ {}
+
+ /**
+ * Generates a PKCS#11 ECDH private key
+ * @param session the session to use
+ * @param ec_params DER-encoding of an ANSI X9.62 Parameters value
+ * @param props the attributes of the private key
+ * @note no persistent public key object will be created
+ */
+ PKCS11_ECDH_PrivateKey(Session& session, const std::vector<uint8_t>& ec_params,
+ const EC_PrivateKeyGenerationProperties& props)
+ : PKCS11_EC_PrivateKey(session, ec_params, props)
+ {}
+
+ inline std::string algo_name() const override
+ {
+ return "ECDH";
+ }
+
+ inline std::vector<uint8_t> public_value() const override
+ {
+ return public_point().encode(PointGFp::UNCOMPRESSED);
+ }
+
+ /// @return the exported ECDH private key
+ ECDH_PrivateKey export_key() const;
+
+ secure_vector<uint8_t> private_key_bits() const override;
+
+ std::unique_ptr<PK_Ops::Key_Agreement>
+ create_key_agreement_op(RandomNumberGenerator& rng,
+ const std::string& params,
+ const std::string& provider) const override;
+ };
+
+using PKCS11_ECDH_KeyPair = std::pair<PKCS11_ECDH_PublicKey, PKCS11_ECDH_PrivateKey>;
+
+/**
+* PKCS#11 ECDH key pair generation
+* @param session the session that should be used for the key generation
+* @param pub_props the properties of the public key
+* @param priv_props the properties of the private key
+*/
+BOTAN_PUBLIC_API(2,0) PKCS11_ECDH_KeyPair generate_ecdh_keypair(Session& session, const EC_PublicKeyGenerationProperties& pub_props,
+ const EC_PrivateKeyGenerationProperties& priv_props);
+}
+
+}
+
+#endif
+#endif
diff --git a/comm/third_party/botan/src/lib/prov/pkcs11/p11_ecdsa.cpp b/comm/third_party/botan/src/lib/prov/pkcs11/p11_ecdsa.cpp
new file mode 100644
index 0000000000..a990933da1
--- /dev/null
+++ b/comm/third_party/botan/src/lib/prov/pkcs11/p11_ecdsa.cpp
@@ -0,0 +1,213 @@
+/*
+* PKCS#11 ECDSA
+* (C) 2016 Daniel Neus, Sirrix AG
+* (C) 2016 Philipp Weber, Sirrix AG
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/p11_ecdsa.h>
+
+#if defined(BOTAN_HAS_ECDSA)
+
+#include <botan/internal/p11_mechanism.h>
+#include <botan/pk_ops.h>
+#include <botan/keypair.h>
+#include <botan/rng.h>
+
+namespace Botan {
+namespace PKCS11 {
+
+ECDSA_PublicKey PKCS11_ECDSA_PublicKey::export_key() const
+ {
+ return ECDSA_PublicKey(domain(), public_point());
+ }
+
+bool PKCS11_ECDSA_PrivateKey::check_key(RandomNumberGenerator& rng, bool strong) const
+ {
+ if(!public_point().on_the_curve())
+ {
+ return false;
+ }
+
+ if(!strong)
+ {
+ return true;
+ }
+
+ ECDSA_PublicKey pubkey(domain(), public_point());
+ return KeyPair::signature_consistency_check(rng, *this, pubkey, "EMSA1(SHA-256)");
+ }
+
+ECDSA_PrivateKey PKCS11_ECDSA_PrivateKey::export_key() const
+ {
+ auto priv_key = get_attribute_value(AttributeType::Value);
+
+ Null_RNG rng;
+ return ECDSA_PrivateKey(rng, domain(), BigInt::decode(priv_key));
+ }
+
+secure_vector<uint8_t> PKCS11_ECDSA_PrivateKey::private_key_bits() const
+ {
+ return export_key().private_key_bits();
+ }
+
+namespace {
+
+class PKCS11_ECDSA_Signature_Operation final : public PK_Ops::Signature
+ {
+ public:
+ PKCS11_ECDSA_Signature_Operation(const PKCS11_EC_PrivateKey& key, const std::string& emsa)
+ : PK_Ops::Signature(), m_key(key), m_order(key.domain().get_order()), m_mechanism(MechanismWrapper::create_ecdsa_mechanism(emsa))
+ {}
+
+ void update(const uint8_t msg[], size_t msg_len) override
+ {
+ if(!m_initialized)
+ {
+ // first call to update: initialize and cache message because we can not determine yet whether a single- or multiple-part operation will be performed
+ m_key.module()->C_SignInit(m_key.session().handle(), m_mechanism.data(), m_key.handle());
+ m_initialized = true;
+ m_first_message = secure_vector<uint8_t>(msg, msg + msg_len);
+ return;
+ }
+
+ if(!m_first_message.empty())
+ {
+ // second call to update: start multiple-part operation
+ m_key.module()->C_SignUpdate(m_key.session().handle(), m_first_message);
+ m_first_message.clear();
+ }
+
+ m_key.module()->C_SignUpdate(m_key.session().handle(), const_cast<Byte*>(msg), static_cast<Ulong>(msg_len));
+ }
+
+ secure_vector<uint8_t> sign(RandomNumberGenerator&) override
+ {
+ secure_vector<uint8_t> signature;
+ if(!m_first_message.empty())
+ {
+ // single call to update: perform single-part operation
+ m_key.module()->C_Sign(m_key.session().handle(), m_first_message, signature);
+ m_first_message.clear();
+ }
+ else
+ {
+ // multiple calls to update (or none): finish multiple-part operation
+ m_key.module()->C_SignFinal(m_key.session().handle(), signature);
+ }
+ m_initialized = false;
+ return signature;
+ }
+
+ size_t signature_length() const override { return 2*m_order.bytes(); }
+
+ private:
+ const PKCS11_EC_PrivateKey& m_key;
+ const BigInt& m_order;
+ MechanismWrapper m_mechanism;
+ secure_vector<uint8_t> m_first_message;
+ bool m_initialized = false;
+ };
+
+
+class PKCS11_ECDSA_Verification_Operation final : public PK_Ops::Verification
+ {
+ public:
+ PKCS11_ECDSA_Verification_Operation(const PKCS11_EC_PublicKey& key, const std::string& emsa)
+ : PK_Ops::Verification(), m_key(key), m_order(key.domain().get_order()), m_mechanism(MechanismWrapper::create_ecdsa_mechanism(emsa))
+ {}
+
+ void update(const uint8_t msg[], size_t msg_len) override
+ {
+ if(!m_initialized)
+ {
+ // first call to update: initialize and cache message because we can not determine yet whether a single- or multiple-part operation will be performed
+ m_key.module()->C_VerifyInit(m_key.session().handle(), m_mechanism.data(), m_key.handle());
+ m_initialized = true;
+ m_first_message = secure_vector<uint8_t>(msg, msg + msg_len);
+ return;
+ }
+
+ if(!m_first_message.empty())
+ {
+ // second call to update: start multiple-part operation
+ m_key.module()->C_VerifyUpdate(m_key.session().handle(), m_first_message);
+ m_first_message.clear();
+ }
+
+ m_key.module()->C_VerifyUpdate(m_key.session().handle(), const_cast<Byte*>(msg), static_cast<Ulong>(msg_len));
+ }
+
+ bool is_valid_signature(const uint8_t sig[], size_t sig_len) override
+ {
+ ReturnValue return_value = ReturnValue::SignatureInvalid;
+ if(!m_first_message.empty())
+ {
+ // single call to update: perform single-part operation
+ m_key.module()->C_Verify(m_key.session().handle(),
+ m_first_message.data(), static_cast<Ulong>(m_first_message.size()),
+ const_cast<Byte*>(sig), static_cast<Ulong>(sig_len),
+ &return_value);
+ m_first_message.clear();
+ }
+ else
+ {
+ // multiple calls to update (or none): finish multiple-part operation
+ m_key.module()->C_VerifyFinal(m_key.session().handle(), const_cast<Byte*>(sig), static_cast<Ulong>(sig_len), &return_value);
+ }
+ m_initialized = false;
+ if(return_value != ReturnValue::OK && return_value != ReturnValue::SignatureInvalid)
+ {
+ throw PKCS11_ReturnError(return_value);
+ }
+ return return_value == ReturnValue::OK;
+ }
+
+ private:
+ const PKCS11_EC_PublicKey& m_key;
+ const BigInt& m_order;
+ MechanismWrapper m_mechanism;
+ secure_vector<uint8_t> m_first_message;
+ bool m_initialized = false;
+ };
+
+}
+
+std::unique_ptr<PK_Ops::Verification>
+PKCS11_ECDSA_PublicKey::create_verification_op(const std::string& params,
+ const std::string& /*provider*/) const
+ {
+ return std::unique_ptr<PK_Ops::Verification>(new PKCS11_ECDSA_Verification_Operation(*this, params));
+ }
+
+std::unique_ptr<PK_Ops::Signature>
+PKCS11_ECDSA_PrivateKey::create_signature_op(RandomNumberGenerator& /*rng*/,
+ const std::string& params,
+ const std::string& /*provider*/) const
+ {
+ return std::unique_ptr<PK_Ops::Signature>(new PKCS11_ECDSA_Signature_Operation(*this, params));
+ }
+
+PKCS11_ECDSA_KeyPair generate_ecdsa_keypair(Session& session, const EC_PublicKeyGenerationProperties& pub_props,
+ const EC_PrivateKeyGenerationProperties& priv_props)
+ {
+ ObjectHandle pub_key_handle = 0;
+ ObjectHandle priv_key_handle = 0;
+
+ Mechanism mechanism = { static_cast<CK_MECHANISM_TYPE>(MechanismType::EcKeyPairGen), nullptr, 0 };
+
+ session.module()->C_GenerateKeyPair(session.handle(), &mechanism,
+ pub_props.data(), static_cast<Ulong>(pub_props.count()),
+ priv_props.data(), static_cast<Ulong>(priv_props.count()),
+ &pub_key_handle, &priv_key_handle);
+
+ return std::make_pair(PKCS11_ECDSA_PublicKey(session, pub_key_handle), PKCS11_ECDSA_PrivateKey(session,
+ priv_key_handle));
+ }
+
+}
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/prov/pkcs11/p11_ecdsa.h b/comm/third_party/botan/src/lib/prov/pkcs11/p11_ecdsa.h
new file mode 100644
index 0000000000..82721a4666
--- /dev/null
+++ b/comm/third_party/botan/src/lib/prov/pkcs11/p11_ecdsa.h
@@ -0,0 +1,133 @@
+/*
+* PKCS#11 ECDSA
+* (C) 2016 Daniel Neus, Sirrix AG
+* (C) 2016 Philipp Weber, Sirrix AG
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_P11_ECDSA_H_
+#define BOTAN_P11_ECDSA_H_
+
+#include <botan/p11.h>
+#include <botan/pk_keys.h>
+
+#if defined(BOTAN_HAS_ECDSA)
+
+#include <botan/p11_ecc_key.h>
+#include <botan/ecdsa.h>
+
+#include <string>
+
+namespace Botan {
+namespace PKCS11 {
+class Session;
+
+/// Represents a PKCS#11 ECDSA public key
+class BOTAN_PUBLIC_API(2,0) PKCS11_ECDSA_PublicKey final : public PKCS11_EC_PublicKey, public virtual ECDSA_PublicKey
+ {
+ public:
+ /**
+ * Creates a PKCS11_ECDSA_PublicKey object from an existing PKCS#11 ECDSA public key
+ * @param session the session to use
+ * @param handle the handle of the ECDSA public key
+ */
+ PKCS11_ECDSA_PublicKey(Session& session, ObjectHandle handle)
+ : EC_PublicKey(), PKCS11_EC_PublicKey(session, handle)
+ {}
+
+ /**
+ * Imports an ECDSA public key
+ * @param session the session to use
+ * @param props the attributes of the public key
+ */
+ PKCS11_ECDSA_PublicKey(Session& session, const EC_PublicKeyImportProperties& props)
+ : EC_PublicKey(), PKCS11_EC_PublicKey(session, props)
+ {}
+
+ inline std::string algo_name() const override
+ {
+ return "ECDSA";
+ }
+
+ /// @return the exported ECDSA public key
+ ECDSA_PublicKey export_key() const;
+
+ std::unique_ptr<PK_Ops::Verification>
+ create_verification_op(const std::string& params,
+ const std::string& provider) const override;
+ };
+
+/// Represents a PKCS#11 ECDSA private key
+class BOTAN_PUBLIC_API(2,0) PKCS11_ECDSA_PrivateKey final : public PKCS11_EC_PrivateKey
+ {
+ public:
+ /**
+ * Creates a PKCS11_ECDSA_PrivateKey object from an existing PKCS#11 ECDSA private key
+ * @param session the session to use
+ * @param handle the handle of the ECDSA private key
+ */
+ PKCS11_ECDSA_PrivateKey(Session& session, ObjectHandle handle)
+ : PKCS11_EC_PrivateKey(session, handle)
+ {}
+
+ /**
+ * Imports a ECDSA private key
+ * @param session the session to use
+ * @param props the attributes of the private key
+ */
+ PKCS11_ECDSA_PrivateKey(Session& session, const EC_PrivateKeyImportProperties& props)
+ : PKCS11_EC_PrivateKey(session, props)
+ {}
+
+ /**
+ * Generates a PKCS#11 ECDSA private key
+ * @param session the session to use
+ * @param ec_params DER-encoding of an ANSI X9.62 Parameters value
+ * @param props the attributes of the private key
+ * @note no persistent public key object will be created
+ */
+ PKCS11_ECDSA_PrivateKey(Session& session, const std::vector<uint8_t>& ec_params,
+ const EC_PrivateKeyGenerationProperties& props)
+ : PKCS11_EC_PrivateKey(session, ec_params, props)
+ {}
+
+ inline std::string algo_name() const override
+ {
+ return "ECDSA";
+ }
+
+ size_t message_parts() const override { return 2; }
+
+ size_t message_part_size() const override
+ { return domain().get_order().bytes(); }
+
+ /// @return the exported ECDSA private key
+ ECDSA_PrivateKey export_key() const;
+
+ secure_vector<uint8_t> private_key_bits() const override;
+
+ bool check_key(RandomNumberGenerator&, bool) const override;
+
+ std::unique_ptr<PK_Ops::Signature>
+ create_signature_op(RandomNumberGenerator& rng,
+ const std::string& params,
+ const std::string& provider) const override;
+ };
+
+using PKCS11_ECDSA_KeyPair = std::pair<PKCS11_ECDSA_PublicKey, PKCS11_ECDSA_PrivateKey>;
+
+/**
+* ECDSA key pair generation
+* @param session the session that should be used for the key generation
+* @param pub_props the properties of the public key
+* @param priv_props the properties of the private key
+*/
+BOTAN_PUBLIC_API(2,0) PKCS11_ECDSA_KeyPair generate_ecdsa_keypair(Session& session,
+ const EC_PublicKeyGenerationProperties& pub_props, const EC_PrivateKeyGenerationProperties& priv_props);
+}
+
+}
+
+#endif
+#endif
diff --git a/comm/third_party/botan/src/lib/prov/pkcs11/p11_mechanism.cpp b/comm/third_party/botan/src/lib/prov/pkcs11/p11_mechanism.cpp
new file mode 100644
index 0000000000..e06dcfc1f4
--- /dev/null
+++ b/comm/third_party/botan/src/lib/prov/pkcs11/p11_mechanism.cpp
@@ -0,0 +1,278 @@
+/*
+* PKCS#11 Mechanism
+* (C) 2016 Daniel Neus, Sirrix AG
+* (C) 2016 Philipp Weber, Sirrix AG
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/p11_mechanism.h>
+#include <botan/scan_name.h>
+#include <botan/parsing.h>
+#include <botan/emsa.h>
+
+#include <tuple>
+
+namespace Botan {
+namespace PKCS11 {
+
+namespace {
+using PSS_Params = std::tuple<size_t, MechanismType, MGF>;
+
+// maps a PSS mechanism type to the number of bytes used for the salt, the mechanism type of the underlying hash algorithm and the MGF
+static const std::map<MechanismType, PSS_Params> PssOptions =
+ {
+ { MechanismType::RsaPkcsPss, PSS_Params(0, MechanismType::Sha1, MGF::Mgf1Sha1) },
+ { MechanismType::Sha1RsaPkcsPss, PSS_Params(20, MechanismType::Sha1, MGF::Mgf1Sha1) },
+ { MechanismType::Sha224RsaPkcsPss, PSS_Params(28, MechanismType::Sha224, MGF::Mgf1Sha224) },
+ { MechanismType::Sha256RsaPkcsPss, PSS_Params(32, MechanismType::Sha256, MGF::Mgf1Sha256) },
+ { MechanismType::Sha384RsaPkcsPss, PSS_Params(48, MechanismType::Sha384, MGF::Mgf1Sha384) },
+ { MechanismType::Sha512RsaPkcsPss, PSS_Params(64, MechanismType::Sha512, MGF::Mgf1Sha512) }
+ };
+
+struct MechanismData
+ {
+ explicit MechanismData(MechanismType _type)
+ : type(_type)
+ {}
+
+ MechanismData(MechanismData const&) = default;
+ MechanismData& operator=(MechanismData const&) = default;
+ virtual ~MechanismData() = default;
+
+ // the mechanism to perform
+ MechanismType type;
+ };
+
+struct RSA_SignMechanism final : public MechanismData
+ {
+ explicit RSA_SignMechanism(MechanismType _type)
+ : MechanismData(_type), hash(static_cast<MechanismType>(0)), mgf(static_cast<MGF>(0)), salt_size(0)
+ {
+ auto pss_option = PssOptions.find(type);
+ if(pss_option != PssOptions.end())
+ {
+ hash = std::get<1>(pss_option->second);
+ mgf = std::get<2>(pss_option->second);
+ salt_size = std::get<0>(pss_option->second);
+ }
+ }
+
+ // hash algorithm used in the PSS encoding; if the signature mechanism does not include message hashing,
+ // then this value must be the mechanism used by the application to generate the message hash;
+ // if the signature mechanism includes hashing, then this value must match the hash algorithm indicated by the signature mechanism
+ MechanismType hash;
+
+ // mask generation function to use on the encoded block
+ MGF mgf;
+
+ // length, in bytes, of the salt value used in the PSS encoding; typical values are the length of the message hash and zero
+ size_t salt_size;
+ };
+
+// note: when updating this map, update the documentation for `MechanismWrapper::create_rsa_sign_mechanism`
+static std::map<std::string, RSA_SignMechanism> SignMechanisms =
+ {
+ { "Raw", RSA_SignMechanism(MechanismType::RsaX509) },
+
+ { "EMSA2(Raw)", RSA_SignMechanism(MechanismType::RsaX931) },
+ { "EMSA2(SHA-1)", RSA_SignMechanism(MechanismType::Sha1RsaX931) },
+
+ // RSASSA PKCS#1 v1.5
+ { "EMSA3(Raw)", RSA_SignMechanism(MechanismType::RsaPkcs) },
+ { "EMSA3(SHA-1)", RSA_SignMechanism(MechanismType::Sha1RsaPkcs) },
+ { "EMSA3(SHA-224)", RSA_SignMechanism(MechanismType::Sha224RsaPkcs) },
+ { "EMSA3(SHA-256)", RSA_SignMechanism(MechanismType::Sha256RsaPkcs) },
+ { "EMSA3(SHA-384)", RSA_SignMechanism(MechanismType::Sha384RsaPkcs) },
+ { "EMSA3(SHA-512)", RSA_SignMechanism(MechanismType::Sha512RsaPkcs) },
+
+ { "EMSA_PKCS1(SHA-1)", RSA_SignMechanism(MechanismType::Sha1RsaPkcs) },
+ { "EMSA_PKCS1(SHA-224)", RSA_SignMechanism(MechanismType::Sha224RsaPkcs) },
+ { "EMSA_PKCS1(SHA-256)", RSA_SignMechanism(MechanismType::Sha256RsaPkcs) },
+ { "EMSA_PKCS1(SHA-384)", RSA_SignMechanism(MechanismType::Sha384RsaPkcs) },
+ { "EMSA_PKCS1(SHA-512)", RSA_SignMechanism(MechanismType::Sha512RsaPkcs) },
+
+ // RSASSA PKCS#1 PSS
+ { "EMSA4(Raw)", RSA_SignMechanism(MechanismType::RsaPkcsPss) },
+ { "EMSA4(SHA-1)", RSA_SignMechanism(MechanismType::Sha1RsaPkcsPss) },
+ { "EMSA4(SHA-224)", RSA_SignMechanism(MechanismType::Sha224RsaPkcsPss) },
+
+ { "EMSA4(SHA-256)", RSA_SignMechanism(MechanismType::Sha256RsaPkcsPss) },
+ { "EMSA4(SHA-256,MGF1,32)", RSA_SignMechanism(MechanismType::Sha256RsaPkcsPss) },
+ { "PSSR(SHA-256,MGF1,32)", RSA_SignMechanism(MechanismType::Sha256RsaPkcsPss) },
+
+ { "EMSA4(SHA-384)", RSA_SignMechanism(MechanismType::Sha384RsaPkcsPss) },
+ { "EMSA4(SHA-384,MGF1,48)", RSA_SignMechanism(MechanismType::Sha384RsaPkcsPss) },
+ { "PSSR(SHA-384,MGF1,48)", RSA_SignMechanism(MechanismType::Sha384RsaPkcsPss) },
+
+ { "EMSA4(SHA-512)", RSA_SignMechanism(MechanismType::Sha512RsaPkcsPss) },
+ { "EMSA4(SHA-512,MGF1,64)", RSA_SignMechanism(MechanismType::Sha512RsaPkcsPss) },
+ { "PSSR(SHA-512,MGF1,64)", RSA_SignMechanism(MechanismType::Sha512RsaPkcsPss) },
+
+ { "ISO9796", RSA_SignMechanism(MechanismType::Rsa9796) }
+ };
+
+struct RSA_CryptMechanism final : public MechanismData
+ {
+ RSA_CryptMechanism(MechanismType _type, size_t _padding_size, MechanismType _hash, MGF _mgf)
+ : MechanismData(_type), hash(_hash), mgf(_mgf), padding_size(_padding_size)
+ {}
+
+ RSA_CryptMechanism(MechanismType _type, size_t _padding_size)
+ : RSA_CryptMechanism(_type, _padding_size, static_cast<MechanismType>(0), static_cast<MGF>(0))
+ {}
+
+ // mechanism ID of the message digest algorithm used to calculate the digest of the encoding parameter
+ MechanismType hash;
+
+ // mask generation function to use on the encoded block
+ MGF mgf;
+
+ // number of bytes required for the padding
+ size_t padding_size;
+ };
+
+// note: when updating this map, update the documentation for `MechanismWrapper::create_rsa_crypt_mechanism`
+static const std::map<std::string, RSA_CryptMechanism> CryptMechanisms =
+ {
+ { "Raw", RSA_CryptMechanism(MechanismType::RsaX509, 0) },
+ { "EME-PKCS1-v1_5", RSA_CryptMechanism(MechanismType::RsaPkcs, 11) },
+ { "OAEP(SHA-1)", RSA_CryptMechanism(MechanismType::RsaPkcsOaep, 2 + 2 * 20, MechanismType::Sha1, MGF::Mgf1Sha1) },
+ { "OAEP(SHA-224)", RSA_CryptMechanism(MechanismType::RsaPkcsOaep, 2 + 2 * 28, MechanismType::Sha224, MGF::Mgf1Sha224) },
+ { "OAEP(SHA-256)", RSA_CryptMechanism(MechanismType::RsaPkcsOaep, 2 + 2 * 32, MechanismType::Sha256, MGF::Mgf1Sha256) },
+ { "OAEP(SHA-384)", RSA_CryptMechanism(MechanismType::RsaPkcsOaep, 2 + 2 * 48, MechanismType::Sha384, MGF::Mgf1Sha384) },
+ { "OAEP(SHA-512)", RSA_CryptMechanism(MechanismType::RsaPkcsOaep, 2 + 2 * 64, MechanismType::Sha512, MGF::Mgf1Sha512) }
+ };
+
+// note: when updating this map, update the documentation for `MechanismWrapper::create_ecdsa_mechanism`
+static std::map<std::string, MechanismType> EcdsaHash =
+ {
+ { "Raw", MechanismType::Ecdsa },
+ { "SHA-160", MechanismType::EcdsaSha1 },
+ { "SHA-224", MechanismType::EcdsaSha224 },
+ { "SHA-256", MechanismType::EcdsaSha256 },
+ { "SHA-384", MechanismType::EcdsaSha384 },
+ { "SHA-512", MechanismType::EcdsaSha512 }
+ };
+
+// note: when updating this map, update the documentation for `MechanismWrapper::create_ecdh_mechanism`
+static std::map<std::string, KeyDerivation> EcdhHash =
+ {
+ { "Raw", KeyDerivation::Null },
+ { "SHA-160", KeyDerivation::Sha1Kdf },
+ { "SHA-224", KeyDerivation::Sha224Kdf },
+ { "SHA-256", KeyDerivation::Sha256Kdf },
+ { "SHA-384", KeyDerivation::Sha384Kdf },
+ { "SHA-512", KeyDerivation::Sha512Kdf }
+ };
+}
+
+MechanismWrapper::MechanismWrapper(MechanismType mechanism_type)
+ : m_mechanism( { static_cast<CK_MECHANISM_TYPE>(mechanism_type), nullptr, 0 }), m_parameters(nullptr)
+ {}
+
+MechanismWrapper MechanismWrapper::create_rsa_crypt_mechanism(const std::string& padding)
+ {
+ auto mechanism_info_it = CryptMechanisms.find(padding);
+ if(mechanism_info_it == CryptMechanisms.end())
+ {
+ // at this point it would be possible to support additional configurations that are not predefined above by parsing `padding`
+ throw Lookup_Error("PKCS#11 RSA encrypt/decrypt does not support EME " + padding);
+ }
+ RSA_CryptMechanism mechanism_info = mechanism_info_it->second;
+
+ MechanismWrapper mech(mechanism_info.type);
+ if(mechanism_info.type == MechanismType::RsaPkcsOaep)
+ {
+ mech.m_parameters = std::make_shared<MechanismParameters>();
+ mech.m_parameters->oaep_params.hashAlg = static_cast<CK_MECHANISM_TYPE>(mechanism_info.hash);
+ mech.m_parameters->oaep_params.mgf = static_cast<CK_RSA_PKCS_MGF_TYPE>(mechanism_info.mgf);
+ mech.m_parameters->oaep_params.source = CKZ_DATA_SPECIFIED;
+ mech.m_parameters->oaep_params.pSourceData = nullptr;
+ mech.m_parameters->oaep_params.ulSourceDataLen = 0;
+ mech.m_mechanism.pParameter = mech.m_parameters.get();
+ mech.m_mechanism.ulParameterLen = sizeof(RsaPkcsOaepParams);
+ }
+ mech.m_padding_size = mechanism_info.padding_size;
+ return mech;
+ }
+
+MechanismWrapper MechanismWrapper::create_rsa_sign_mechanism(const std::string& padding)
+ {
+ auto mechanism_info_it = SignMechanisms.find(padding);
+ if(mechanism_info_it == SignMechanisms.end())
+ {
+ // at this point it would be possible to support additional configurations that are not predefined above by parsing `padding`
+ throw Lookup_Error("PKCS#11 RSA sign/verify does not support EMSA " + padding);
+ }
+ RSA_SignMechanism mechanism_info = mechanism_info_it->second;
+
+ MechanismWrapper mech(mechanism_info.type);
+ if(PssOptions.find(mechanism_info.type) != PssOptions.end())
+ {
+ mech.m_parameters = std::make_shared<MechanismParameters>();
+ mech.m_parameters->pss_params.hashAlg = static_cast<CK_MECHANISM_TYPE>(mechanism_info.hash);
+ mech.m_parameters->pss_params.mgf = static_cast<CK_RSA_PKCS_MGF_TYPE>(mechanism_info.mgf);
+ mech.m_parameters->pss_params.sLen = static_cast<Ulong>(mechanism_info.salt_size);
+ mech.m_mechanism.pParameter = mech.m_parameters.get();
+ mech.m_mechanism.ulParameterLen = sizeof(RsaPkcsPssParams);
+ }
+ return mech;
+ }
+
+MechanismWrapper MechanismWrapper::create_ecdsa_mechanism(const std::string& hash)
+ {
+ std::string hash_name = hash;
+
+ if(hash_name != "Raw")
+ {
+ hash_name = hash_for_emsa(hash);
+ }
+
+ auto mechanism_type = EcdsaHash.find(hash_name);
+ if(mechanism_type == EcdsaHash.end())
+ {
+ throw Lookup_Error("PKCS#11 ECDSA sign/verify does not support " + hash);
+ }
+ return MechanismWrapper(mechanism_type->second);
+ }
+
+MechanismWrapper MechanismWrapper::create_ecdh_mechanism(const std::string& params)
+ {
+ std::vector<std::string> param_parts = split_on(params, ',');
+
+ if(param_parts.empty() || param_parts.size() > 2)
+ throw Invalid_Argument("PKCS #11 ECDH key derivation bad params " + params);
+
+ const bool use_cofactor =
+ (param_parts[0] == "Cofactor") ||
+ (param_parts.size() == 2 && param_parts[1] == "Cofactor");
+
+ std::string kdf_name = (param_parts[0] == "Cofactor" ? param_parts[1] : param_parts[0]);
+ std::string hash = kdf_name;
+
+ if(kdf_name != "Raw")
+ {
+ SCAN_Name kdf_hash(kdf_name);
+
+ if(kdf_hash.arg_count() > 0)
+ {
+ hash = kdf_hash.arg(0);
+ }
+ }
+
+ auto kdf = EcdhHash.find(hash);
+ if(kdf == EcdhHash.end())
+ {
+ throw Lookup_Error("PKCS#11 ECDH key derivation does not support KDF " + kdf_name);
+ }
+ MechanismWrapper mech(use_cofactor ? MechanismType::Ecdh1CofactorDerive : MechanismType::Ecdh1Derive);
+ mech.m_parameters = std::make_shared<MechanismParameters>();
+ mech.m_parameters->ecdh_params.kdf = static_cast<CK_EC_KDF_TYPE>(kdf->second);
+ mech.m_mechanism.pParameter = mech.m_parameters.get();
+ mech.m_mechanism.ulParameterLen = sizeof(Ecdh1DeriveParams);
+ return mech;
+ }
+
+}
+}
diff --git a/comm/third_party/botan/src/lib/prov/pkcs11/p11_mechanism.h b/comm/third_party/botan/src/lib/prov/pkcs11/p11_mechanism.h
new file mode 100644
index 0000000000..8947372fda
--- /dev/null
+++ b/comm/third_party/botan/src/lib/prov/pkcs11/p11_mechanism.h
@@ -0,0 +1,118 @@
+/*
+* PKCS#11 Mechanism
+* (C) 2016 Daniel Neus, Sirrix AG
+* (C) 2016 Philipp Weber, Sirrix AG
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_P11_MECHANISM_H_
+#define BOTAN_P11_MECHANISM_H_
+
+#include <botan/p11.h>
+
+#include <utility>
+#include <string>
+#include <memory>
+
+namespace Botan {
+namespace PKCS11 {
+
+/**
+* Simple class to build and hold the data for a CK_MECHANISM struct
+* for RSA (encryption/decryption, signature/verification)
+* and EC (ECDSA signature/verification, ECDH key derivation).
+*/
+class MechanismWrapper final
+ {
+ public:
+ /// @param mechanism_type the CK_MECHANISM_TYPE for the `mechanism` field of the CK_MECHANISM struct
+ explicit MechanismWrapper(MechanismType mechanism_type);
+
+ /**
+ * Creates the CK_MECHANISM data for RSA encryption/decryption
+ * @param padding supported paddings are Raw (X.509), EME-PKCS1-v1_5 (PKCS#1 v1.5) and OAEP (PKCS#1 OAEP)
+ */
+ static MechanismWrapper create_rsa_crypt_mechanism(const std::string& padding);
+
+ /**
+ * Creates the CK_MECHANISM data for RSA signature/verification
+ * @param padding supported paddings are Raw (X.509), EMSA3 (PKCS#1 v1.5), EMSA4 (PKCS#1 PSS),
+ * EMSA2 (ANSI X9.31) and ISO9796 (ISO/IEC 9796)
+ */
+ static MechanismWrapper create_rsa_sign_mechanism(const std::string& padding);
+
+ /**
+ * Creates the CK_MECHANISM data for ECDSA signature/verification
+ * @param hash the hash algorithm used to hash the data to sign.
+ * supported hash functions are Raw and SHA-160 to SHA-512
+ */
+ static MechanismWrapper create_ecdsa_mechanism(const std::string& hash);
+
+ /**
+ * Creates the CK_MECHANISM data for ECDH key derivation (CKM_ECDH1_DERIVE or CKM_ECDH1_COFACTOR_DERIVE)
+ * @param params specifies the key derivation function to use.
+ * Supported KDFs are Raw and SHA-160 to SHA-512.
+ * Params can also include the string "Cofactor" if the cofactor
+ * key derivation mechanism should be used, for example "SHA-512,Cofactor"
+ */
+ static MechanismWrapper create_ecdh_mechanism(const std::string& params);
+
+ /**
+ * Sets the salt for the ECDH mechanism parameters.
+ * @param salt the salt
+ * @param salt_len size of the salt in bytes
+ */
+ inline void set_ecdh_salt(const uint8_t salt[], size_t salt_len)
+ {
+ m_parameters->ecdh_params.pSharedData = const_cast<uint8_t*>(salt);
+ m_parameters->ecdh_params.ulSharedDataLen = static_cast<Ulong>(salt_len);
+ }
+
+ /**
+ * Sets the public key of the other party for the ECDH mechanism parameters.
+ * @param other_key key of the other party
+ * @param other_key_len size of the key of the other party in bytes
+ */
+ inline void set_ecdh_other_key(const uint8_t other_key[], size_t other_key_len)
+ {
+ m_parameters->ecdh_params.pPublicData = const_cast<uint8_t*>(other_key);
+ m_parameters->ecdh_params.ulPublicDataLen = static_cast<Ulong>(other_key_len);
+ }
+
+ /// @return a pointer to the CK_MECHANISM struct that can be passed to the cryptoki functions
+ inline Mechanism* data() const
+ {
+ return const_cast<Mechanism*>(&m_mechanism);
+ }
+
+ /// @return the size of the padding in bytes (for encryption/decryption)
+ inline size_t padding_size() const
+ {
+ return m_padding_size;
+ }
+
+ /// Holds the mechanism parameters for OAEP, PSS and ECDH
+ union MechanismParameters
+ {
+ MechanismParameters()
+ {
+ clear_mem(this, 1);
+ }
+
+ RsaPkcsOaepParams oaep_params;
+ RsaPkcsPssParams pss_params;
+ Ecdh1DeriveParams ecdh_params;
+ };
+
+ private:
+ Mechanism m_mechanism;
+ std::shared_ptr<MechanismParameters> m_parameters;
+ size_t m_padding_size = 0;
+ };
+
+}
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/prov/pkcs11/p11_module.cpp b/comm/third_party/botan/src/lib/prov/pkcs11/p11_module.cpp
new file mode 100644
index 0000000000..1c84d415cb
--- /dev/null
+++ b/comm/third_party/botan/src/lib/prov/pkcs11/p11_module.cpp
@@ -0,0 +1,53 @@
+/*
+* PKCS#11 Module
+* (C) 2016 Daniel Neus, Sirrix AG
+* (C) 2016 Philipp Weber, Sirrix AG
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/p11_types.h>
+#include <botan/dyn_load.h>
+
+namespace Botan {
+
+namespace PKCS11 {
+
+Module::Module(Module&&) = default;
+
+Module::Module(const std::string& file_path, C_InitializeArgs init_args)
+ : m_file_path(file_path)
+ {
+ if(file_path.empty())
+ throw Invalid_Argument("PKCS11 no module path specified");
+ reload(init_args);
+ }
+
+Module::~Module() noexcept
+ {
+ try
+ {
+ m_low_level->C_Finalize(nullptr, nullptr);
+ }
+ catch(...)
+ {
+ // we are noexcept and must swallow any exception here
+ }
+ }
+
+void Module::reload(C_InitializeArgs init_args)
+ {
+ if(m_low_level)
+ {
+ m_low_level->C_Finalize(nullptr);
+ }
+
+ m_library.reset(new Dynamically_Loaded_Library(m_file_path));
+ LowLevel::C_GetFunctionList(*m_library, &m_func_list);
+ m_low_level.reset(new LowLevel(m_func_list));
+
+ m_low_level->C_Initialize(&init_args);
+ }
+
+}
+}
diff --git a/comm/third_party/botan/src/lib/prov/pkcs11/p11_module.h b/comm/third_party/botan/src/lib/prov/pkcs11/p11_module.h
new file mode 100644
index 0000000000..0c8a6a6db4
--- /dev/null
+++ b/comm/third_party/botan/src/lib/prov/pkcs11/p11_module.h
@@ -0,0 +1,15 @@
+/*
+* PKCS#11 Module
+* (C) 2016 Daniel Neus, Sirrix AG
+* (C) 2016 Philipp Weber, Sirrix AG
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_P11_MODULE_H_
+#define BOTAN_P11_MODULE_H_
+
+#include <botan/p11_types.h>
+BOTAN_DEPRECATED_HEADER(p11_module.h)
+
+#endif
diff --git a/comm/third_party/botan/src/lib/prov/pkcs11/p11_object.cpp b/comm/third_party/botan/src/lib/prov/pkcs11/p11_object.cpp
new file mode 100644
index 0000000000..4dd191efe2
--- /dev/null
+++ b/comm/third_party/botan/src/lib/prov/pkcs11/p11_object.cpp
@@ -0,0 +1,227 @@
+/*
+* PKCS#11 Object
+* (C) 2016 Daniel Neus, Sirrix AG
+* (C) 2016 Philipp Weber, Sirrix AG
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/p11_object.h>
+#include <map>
+
+namespace Botan {
+
+namespace PKCS11 {
+
+AttributeContainer::AttributeContainer(ObjectClass object_class)
+ {
+ add_class(object_class);
+ }
+
+void AttributeContainer::add_class(ObjectClass object_class)
+ {
+ m_numerics.emplace_back(static_cast< uint64_t >(object_class));
+ add_attribute(AttributeType::Class,
+ reinterpret_cast< uint8_t* >(&m_numerics.back()),
+ static_cast<Ulong>(sizeof(ObjectClass)));
+ }
+
+void AttributeContainer::add_string(AttributeType attribute, const std::string& value)
+ {
+ m_strings.push_back(value);
+ add_attribute(attribute, reinterpret_cast<const uint8_t*>(m_strings.back().data()), static_cast<Ulong>(value.size()));
+ }
+
+void AttributeContainer::add_binary(AttributeType attribute, const uint8_t* value, size_t length)
+ {
+ m_vectors.push_back(secure_vector<uint8_t>(value, value + length));
+ add_attribute(attribute, reinterpret_cast< const uint8_t* >(m_vectors.back().data()), static_cast<Ulong>(length));
+ }
+
+void AttributeContainer::add_bool(AttributeType attribute, bool value)
+ {
+ m_numerics.push_back(value ? True : False);
+ add_attribute(attribute, reinterpret_cast< uint8_t* >(&m_numerics.back()), sizeof(Bbool));
+ }
+
+void AttributeContainer::add_attribute(AttributeType attribute, const uint8_t* value, uint32_t size)
+ {
+ bool exists = false;
+ // check if the attribute has been added already
+ for(auto& existing_attribute : m_attributes)
+ {
+ if(existing_attribute.type == static_cast< CK_ATTRIBUTE_TYPE >(attribute))
+ {
+ // remove old entries
+ m_strings.erase(std::remove_if(m_strings.begin(), m_strings.end(), [ &existing_attribute ](const std::string& data)
+ {
+ return data.data() == existing_attribute.pValue;
+ }), m_strings.end());
+
+ m_numerics.erase(std::remove_if(m_numerics.begin(), m_numerics.end(), [ &existing_attribute ](const uint64_t& data)
+ {
+ return &data == existing_attribute.pValue;
+ }), m_numerics.end());
+
+ m_vectors.erase(std::remove_if(m_vectors.begin(),
+ m_vectors.end(), [ &existing_attribute ](const secure_vector<uint8_t>& data)
+ {
+ return data.data() == existing_attribute.pValue;
+ }), m_vectors.end());
+
+ existing_attribute.pValue = const_cast< uint8_t* >(value);
+ existing_attribute.ulValueLen = size;
+ exists = true;
+ break;
+ }
+ }
+
+ if(!exists)
+ {
+ m_attributes.push_back(Attribute{ static_cast< CK_ATTRIBUTE_TYPE >(attribute), const_cast< uint8_t* >(value), size });
+ }
+ }
+
+// ====================================================================================================
+
+ObjectFinder::ObjectFinder(Session& session, const std::vector<Attribute>& search_template)
+ : m_session(session), m_search_terminated(false)
+ {
+ module()->C_FindObjectsInit(m_session.get().handle(),
+ const_cast< Attribute* >(search_template.data()),
+ static_cast<Ulong>(search_template.size()));
+ }
+
+ObjectFinder::~ObjectFinder() noexcept
+ {
+ try
+ {
+ if(m_search_terminated == false)
+ {
+ module()->C_FindObjectsFinal(m_session.get().handle(), nullptr);
+ }
+ }
+ catch(...)
+ {
+ // ignore error during noexcept function
+ }
+ }
+
+std::vector<ObjectHandle> ObjectFinder::find(uint32_t max_count) const
+ {
+ std::vector<ObjectHandle> result(max_count);
+ Ulong objectCount = 0;
+ module()->C_FindObjects(m_session.get().handle(), result.data(), max_count, &objectCount);
+ if(objectCount < max_count)
+ {
+ result.resize(objectCount);
+ }
+ return result;
+ }
+
+void ObjectFinder::finish()
+ {
+ module()->C_FindObjectsFinal(m_session.get().handle());
+ m_search_terminated = true;
+ }
+
+// ====================================================================================================
+
+ObjectProperties::ObjectProperties(ObjectClass object_class)
+ : AttributeContainer(object_class), m_object_class(object_class)
+ {}
+
+// ====================================================================================================
+
+StorageObjectProperties::StorageObjectProperties(ObjectClass object_class)
+ : ObjectProperties(object_class)
+ {}
+
+// ====================================================================================================
+
+DataObjectProperties::DataObjectProperties()
+ : StorageObjectProperties(ObjectClass::Data)
+ {}
+
+// ====================================================================================================
+
+CertificateProperties::CertificateProperties(CertificateType cert_type)
+ : StorageObjectProperties(ObjectClass::Certificate), m_cert_type(cert_type)
+ {
+ add_numeric(AttributeType::CertificateType, static_cast< CK_CERTIFICATE_TYPE >(m_cert_type));
+ }
+
+// ====================================================================================================
+
+KeyProperties::KeyProperties(ObjectClass object_class, KeyType key_type)
+ : StorageObjectProperties(object_class), m_key_type(key_type)
+ {
+ add_numeric(AttributeType::KeyType, static_cast< CK_ULONG >(m_key_type));
+ }
+
+// ====================================================================================================
+
+PublicKeyProperties::PublicKeyProperties(KeyType key_type)
+ : KeyProperties(ObjectClass::PublicKey, key_type)
+ {}
+
+// ====================================================================================================
+
+PrivateKeyProperties::PrivateKeyProperties(KeyType key_type)
+ : KeyProperties(ObjectClass::PrivateKey, key_type)
+ {}
+
+// ====================================================================================================
+
+SecretKeyProperties::SecretKeyProperties(KeyType key_type)
+ : KeyProperties(ObjectClass::SecretKey, key_type)
+ {}
+
+// ====================================================================================================
+
+DomainParameterProperties::DomainParameterProperties(KeyType key_type)
+ : StorageObjectProperties(ObjectClass::DomainParameters), m_key_type(key_type)
+ {
+ add_numeric(AttributeType::KeyType, static_cast< CK_ULONG >(m_key_type));
+ }
+
+// ====================================================================================================
+
+Object::Object(Session& session, ObjectHandle handle)
+ : m_session(session), m_handle(handle)
+ {}
+
+Object::Object(Session& session, const ObjectProperties& obj_props)
+ : m_session(session), m_handle(0)
+ {
+ m_session.get().module()->C_CreateObject(m_session.get().handle(), obj_props.data(), static_cast<Ulong>(obj_props.count()), &m_handle);
+ }
+
+secure_vector<uint8_t> Object::get_attribute_value(AttributeType attribute) const
+ {
+ std::map<AttributeType, secure_vector<uint8_t>> attribute_map = { { attribute, secure_vector<uint8_t>() } };
+ module()->C_GetAttributeValue(m_session.get().handle(), m_handle, attribute_map);
+ return attribute_map.at(attribute);
+ }
+
+void Object::set_attribute_value(AttributeType attribute, const secure_vector<uint8_t>& value) const
+ {
+ std::map<AttributeType, secure_vector<uint8_t>> attribute_map = { { attribute, value } };
+ module()->C_SetAttributeValue(m_session.get().handle(), m_handle, attribute_map);
+ }
+
+void Object::destroy() const
+ {
+ module()->C_DestroyObject(m_session.get().handle(), m_handle);
+ }
+
+ObjectHandle Object::copy(const AttributeContainer& modified_attributes) const
+ {
+ ObjectHandle copied_handle;
+ module()->C_CopyObject(m_session.get().handle(), m_handle,
+ modified_attributes.data(), static_cast<Ulong>(modified_attributes.count()),
+ &copied_handle);
+ return copied_handle;
+ }
+}
+}
diff --git a/comm/third_party/botan/src/lib/prov/pkcs11/p11_object.h b/comm/third_party/botan/src/lib/prov/pkcs11/p11_object.h
new file mode 100644
index 0000000000..a0da1b5cac
--- /dev/null
+++ b/comm/third_party/botan/src/lib/prov/pkcs11/p11_object.h
@@ -0,0 +1,773 @@
+/*
+* PKCS#11 Object
+* (C) 2016 Daniel Neus, Sirrix AG
+* (C) 2016 Philipp Weber, Sirrix AG
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_P11_OBJECT_H_
+#define BOTAN_P11_OBJECT_H_
+
+#include <botan/p11.h>
+#include <botan/p11_types.h>
+#include <botan/secmem.h>
+
+#include <vector>
+#include <string>
+#include <type_traits>
+#include <list>
+#include <functional>
+
+namespace Botan {
+namespace PKCS11 {
+
+class Module;
+
+/// Helper class to build the Attribute / CK_ATTRIBUTE structures
+class BOTAN_PUBLIC_API(2,0) AttributeContainer
+ {
+ public:
+ AttributeContainer() = default;
+
+ /// @param object_class the class type of this container
+ AttributeContainer(ObjectClass object_class);
+
+ virtual ~AttributeContainer() = default;
+
+ AttributeContainer(AttributeContainer&& other) = default;
+ AttributeContainer& operator=(AttributeContainer&& other) = default;
+
+ // Warning when implementing copy/assignment: m_attributes contains pointers to the other members which must be updated after a copy
+ AttributeContainer(const AttributeContainer& other) = delete;
+ AttributeContainer& operator=(const AttributeContainer& other) = delete;
+
+ /// @return the attributes this container contains
+ inline const std::vector<Attribute>& attributes() const
+ {
+ return m_attributes;
+ }
+
+ /// @return raw attribute data
+ inline Attribute* data() const
+ {
+ return const_cast< Attribute* >(m_attributes.data());
+ }
+
+ /// @return the number of attributes in this container
+ inline size_t count() const
+ {
+ return m_attributes.size();
+ }
+
+ /**
+ * Add a class attribute (CKA_CLASS / AttributeType::Class).
+ * @param object_class class attribute to add
+ */
+ void add_class(ObjectClass object_class);
+
+ /**
+ * Add a string attribute (e.g. CKA_LABEL / AttributeType::Label).
+ * @param attribute attribute type
+ * @param value string value to add
+ */
+ void add_string(AttributeType attribute, const std::string& value);
+
+ /**
+ * Add a binary attribute (e.g. CKA_ID / AttributeType::Id).
+ * @param attribute attribute type
+ * @param value binary attribute value to add
+ * @param length size of the binary attribute value in bytes
+ */
+ void add_binary(AttributeType attribute, const uint8_t* value, size_t length);
+
+ /**
+ * Add a binary attribute (e.g. CKA_ID / AttributeType::Id).
+ * @param attribute attribute type
+ * @param binary binary attribute value to add
+ */
+ template<typename TAlloc>
+ void add_binary(AttributeType attribute, const std::vector<uint8_t, TAlloc>& binary)
+ {
+ add_binary(attribute, binary.data(), binary.size());
+ }
+
+ /**
+ * Add a bool attribute (e.g. CKA_SENSITIVE / AttributeType::Sensitive).
+ * @param attribute attribute type
+ * @param value boolean value to add
+ */
+ void add_bool(AttributeType attribute, bool value);
+
+ /**
+ * Add a numeric attribute (e.g. CKA_MODULUS_BITS / AttributeType::ModulusBits).
+ * @param attribute attribute type
+ * @param value numeric value to add
+ */
+ template<typename T>
+ void add_numeric(AttributeType attribute, T value)
+ {
+ static_assert(std::is_integral<T>::value, "Numeric value required.");
+ m_numerics.push_back(static_cast< uint64_t >(value));
+ add_attribute(attribute, reinterpret_cast< uint8_t* >(&m_numerics.back()), sizeof(T));
+ }
+
+ protected:
+ /// Add an attribute with the given value and size to the attribute collection `m_attributes`
+ void add_attribute(AttributeType attribute, const uint8_t* value, uint32_t size);
+
+ private:
+ std::vector<Attribute> m_attributes;
+ std::list<uint64_t> m_numerics;
+ std::list<std::string> m_strings;
+ std::list<secure_vector<uint8_t>> m_vectors;
+ };
+
+/// Manages calls to C_FindObjects* functions (C_FindObjectsInit -> C_FindObjects -> C_FindObjectsFinal)
+class BOTAN_PUBLIC_API(2,0) ObjectFinder final
+ {
+ public:
+ /**
+ * Initializes a search for token and session objects that match a template (calls C_FindObjectsInit)
+ * @param session the session to use for the search
+ * @param search_template the search_template as a vector of `Attribute`
+ */
+ ObjectFinder(Session& session, const std::vector<Attribute>& search_template);
+
+ ObjectFinder(const ObjectFinder& other) = default;
+ ObjectFinder& operator=(const ObjectFinder& other) = delete;
+
+ ObjectFinder(ObjectFinder&& other) = default;
+ ObjectFinder& operator=(ObjectFinder&& other) = delete;
+
+ /// Terminates a search for token and session objects (calls C_FindObjectsFinal)
+ ~ObjectFinder() noexcept;
+
+ /**
+ * Starts or continues a search for token and session objects that match a template, obtaining additional object handles (calls C_FindObjects)
+ * @param max_count maximum amount of object handles to retrieve. Default = 100
+ * @return the result of the search as a vector of `ObjectHandle`
+ */
+ std::vector<ObjectHandle> find(std::uint32_t max_count = 100) const;
+
+ /// Finishes the search operation manually to allow a new ObjectFinder to exist
+ void finish();
+
+ /// @return the module this `ObjectFinder` belongs to
+ inline Module& module() const
+ {
+ return m_session.get().module();
+ }
+
+ private:
+ const std::reference_wrapper<Session> m_session;
+ bool m_search_terminated;
+ };
+
+/// Common attributes of all objects
+class BOTAN_PUBLIC_API(2,0) ObjectProperties : public AttributeContainer
+ {
+ public:
+ /// @param object_class the object class of the object
+ ObjectProperties(ObjectClass object_class);
+
+ /// @return the object class of this object
+ inline ObjectClass object_class() const
+ {
+ return m_object_class;
+ }
+
+ private:
+ const ObjectClass m_object_class;
+ };
+
+/// Common attributes of all storage objects
+class BOTAN_PUBLIC_API(2,0) StorageObjectProperties : public ObjectProperties
+ {
+ public:
+ /// @param object_class the CK_OBJECT_CLASS this storage object belongs to
+ StorageObjectProperties(ObjectClass object_class);
+
+ /// @param label description of the object (RFC2279 string)
+ inline void set_label(const std::string& label)
+ {
+ add_string(AttributeType::Label, label);
+ }
+
+ /// @param value if true the object is a token object; otherwise the object is a session object
+ inline void set_token(bool value)
+ {
+ add_bool(AttributeType::Token, value);
+ }
+
+ /**
+ * @param value if true the object is a private object; otherwise the object is a public object
+ * When private, a user may not access the object until the user has been authenticated to the token
+ */
+ inline void set_private(bool value)
+ {
+ add_bool(AttributeType::Private, value);
+ }
+
+ /// @param value if true the object can be modified, otherwise it is read-only
+ void set_modifiable(bool value)
+ {
+ add_bool(AttributeType::Modifiable, value);
+ }
+
+ /// @param value if true the object can be copied using C_CopyObject
+ void set_copyable(bool value)
+ {
+ add_bool(AttributeType::Copyable, value);
+ }
+
+ /// @param value if true the object can be destroyed using C_DestroyObject
+ void set_destroyable(bool value)
+ {
+ add_bool(AttributeType::Destroyable, value);
+ }
+ };
+
+/// Common attributes of all data objects
+class BOTAN_PUBLIC_API(2,0) DataObjectProperties final : public StorageObjectProperties
+ {
+ public:
+ DataObjectProperties();
+
+ /// @param value description of the application that manages the object (RFC2279 string)
+ inline void set_application(const std::string& value)
+ {
+ add_string(AttributeType::Application, value);
+ }
+
+ /// @param object_id DER-encoding of the object identifier indicating the data object type
+ inline void set_object_id(const std::vector<uint8_t>& object_id)
+ {
+ add_binary(AttributeType::ObjectId, object_id);
+ }
+
+ /// @param value value of the object
+ inline void set_value(const secure_vector<uint8_t>& value)
+ {
+ add_binary(AttributeType::Value, value);
+ }
+ };
+
+/// Common attributes of all certificate objects
+class BOTAN_PUBLIC_API(2,0) CertificateProperties : public StorageObjectProperties
+ {
+ public:
+ /// @param cert_type type of certificate
+ CertificateProperties(CertificateType cert_type);
+
+ /// @param value the certificate can be trusted for the application that it was created (can only be set to true by SO user)
+ inline void set_trusted(bool value)
+ {
+ add_bool(AttributeType::Trusted, value);
+ }
+
+ /// @param category one of `CertificateCategory`
+ inline void set_category(CertificateCategory category)
+ {
+ add_numeric(AttributeType::CertificateCategory, static_cast< CK_CERTIFICATE_CATEGORY >(category));
+ }
+
+ /**
+ * @param checksum the value of this attribute is derived from the certificate by taking the
+ * first three bytes of the SHA - 1 hash of the certificate object's `CKA_VALUE` attribute
+ */
+ inline void set_check_value(const std::vector<uint8_t>& checksum)
+ {
+ add_binary(AttributeType::CheckValue, checksum);
+ }
+
+ /// @param date start date for the certificate
+ inline void set_start_date(Date date)
+ {
+ add_binary(AttributeType::StartDate, reinterpret_cast<uint8_t*>(&date), sizeof(Date));
+ }
+
+ /// @param date end date for the certificate
+ inline void set_end_date(Date date)
+ {
+ add_binary(AttributeType::EndDate, reinterpret_cast<uint8_t*>(&date), sizeof(Date));
+ }
+
+ /// @param pubkey_info DER-encoding of the SubjectPublicKeyInfo for the public key contained in this certificate
+ inline void set_public_key_info(const std::vector<uint8_t>& pubkey_info)
+ {
+ add_binary(AttributeType::PublicKeyInfo, pubkey_info);
+ }
+
+ /// @return the certificate type of this certificate object
+ inline CertificateType cert_type() const
+ {
+ return m_cert_type;
+ }
+
+ private:
+ const CertificateType m_cert_type;
+ };
+
+/// Common attributes of all key objects
+class BOTAN_PUBLIC_API(2,0) KeyProperties : public StorageObjectProperties
+ {
+ public:
+ /**
+ * @param object_class the `CK_OBJECT_CLASS` this key object belongs to
+ * @param key_type type of key
+ */
+ KeyProperties(ObjectClass object_class, KeyType key_type);
+
+ /// @param id key identifier for key
+ inline void set_id(const std::vector<uint8_t>& id)
+ {
+ add_binary(AttributeType::Id, id);
+ }
+
+ /// @param date start date for the key
+ inline void set_start_date(Date date)
+ {
+ add_binary(AttributeType::StartDate, reinterpret_cast<uint8_t*>(&date), sizeof(Date));
+ }
+
+ /// @param date end date for the key
+ inline void set_end_date(Date date)
+ {
+ add_binary(AttributeType::EndDate, reinterpret_cast<uint8_t*>(&date), sizeof(Date));
+ }
+
+ /// @param value true if key supports key derivation (i.e., if other keys can be derived from this one)
+ inline void set_derive(bool value)
+ {
+ add_bool(AttributeType::Derive, value);
+ }
+
+ /**
+ * Sets a list of mechanisms allowed to be used with this key
+ * Not implemented
+ */
+ inline void set_allowed_mechanisms(const std::vector<MechanismType>&)
+ {
+ throw Not_Implemented("KeyProperties::set_allowed_mechanisms");
+ }
+
+ /// @return the key type of this key object
+ inline KeyType key_type() const
+ {
+ return m_key_type;
+ }
+
+ private:
+ const KeyType m_key_type;
+ };
+
+/// Common attributes of all public key objects
+class BOTAN_PUBLIC_API(2,0) PublicKeyProperties : public KeyProperties
+ {
+ public:
+ /// @param key_type type of key
+ PublicKeyProperties(KeyType key_type);
+
+ /// @param subject DER-encoding of the key subject name
+ inline void set_subject(const std::vector<uint8_t>& subject)
+ {
+ add_binary(AttributeType::Subject, subject);
+ }
+
+ /// @param value true if the key supports encryption
+ inline void set_encrypt(bool value)
+ {
+ add_bool(AttributeType::Encrypt, value);
+ }
+
+ /// @param value true if the key supports verification where the signature is an appendix to the data
+ inline void set_verify(bool value)
+ {
+ add_bool(AttributeType::Verify, value);
+ }
+
+ /// @param value true if the key supports verification where the data is recovered from the signature
+ inline void set_verify_recover(bool value)
+ {
+ add_bool(AttributeType::VerifyRecover, value);
+ }
+
+ /// @param value true if the key supports wrapping (i.e., can be used to wrap other keys)
+ inline void set_wrap(bool value)
+ {
+ add_bool(AttributeType::Wrap, value);
+ }
+
+ /**
+ * @param value true if the key can be trusted for the application that it was created.
+ * The wrapping key can be used to wrap keys with `CKA_WRAP_WITH_TRUSTED` set to `CK_TRUE`
+ */
+ inline void set_trusted(bool value)
+ {
+ add_bool(AttributeType::Trusted, value);
+ }
+
+ /**
+ * For wrapping keys
+ * The attribute template to match against any keys wrapped using this wrapping key.
+ * Keys that do not match cannot be wrapped
+ * Not implemented
+ */
+ inline void set_wrap_template(const AttributeContainer&)
+ {
+ throw Not_Implemented("PublicKeyProperties::set_wrap_template");
+ }
+
+ /// @param pubkey_info DER-encoding of the SubjectPublicKeyInfo for this public key
+ inline void set_public_key_info(const std::vector<uint8_t>& pubkey_info)
+ {
+ add_binary(AttributeType::PublicKeyInfo, pubkey_info);
+ }
+ };
+
+/// Common attributes of all private keys
+class BOTAN_PUBLIC_API(2,0) PrivateKeyProperties : public KeyProperties
+ {
+ public:
+ /// @param key_type type of key
+ PrivateKeyProperties(KeyType key_type);
+
+ /// @param subject DER-encoding of the key subject name
+ inline void set_subject(const std::vector<uint8_t>& subject)
+ {
+ add_binary(AttributeType::Subject, subject);
+ }
+
+ /// @param value true if the key is sensitive
+ inline void set_sensitive(bool value)
+ {
+ add_bool(AttributeType::Sensitive, value);
+ }
+
+ /// @param value true if the key supports decryption
+ inline void set_decrypt(bool value)
+ {
+ add_bool(AttributeType::Decrypt, value);
+ }
+
+ /// @param value true if the key supports signatures where the signature is an appendix to the data
+ inline void set_sign(bool value)
+ {
+ add_bool(AttributeType::Sign, value);
+ }
+
+ /// @param value true if the key supports signatures where the data can be recovered from the signature
+ inline void set_sign_recover(bool value)
+ {
+ add_bool(AttributeType::SignRecover, value);
+ }
+
+ /// @param value true if the key supports unwrapping (i.e., can be used to unwrap other keys)
+ inline void set_unwrap(bool value)
+ {
+ add_bool(AttributeType::Unwrap, value);
+ }
+
+ /// @param value true if the key is extractable and can be wrapped
+ inline void set_extractable(bool value)
+ {
+ add_bool(AttributeType::Extractable, value);
+ }
+
+ /// @param value true if the key can only be wrapped with a wrapping key that has `CKA_TRUSTED` set to `CK_TRUE`
+ inline void set_wrap_with_trusted(bool value)
+ {
+ add_bool(AttributeType::WrapWithTrusted, value);
+ }
+
+ /// @param value If true, the user has to supply the PIN for each use (sign or decrypt) with the key
+ inline void set_always_authenticate(bool value)
+ {
+ add_bool(AttributeType::AlwaysAuthenticate, value);
+ }
+
+ /**
+ * For wrapping keys
+ * The attribute template to apply to any keys unwrapped using this wrapping key.
+ * Any user supplied template is applied after this template as if the object has already been created
+ * Not implemented
+ */
+ inline void set_unwrap_template(const AttributeContainer&)
+ {
+ throw Not_Implemented("PrivateKeyProperties::set_unwrap_template");
+ }
+
+ /// @param pubkey_info DER-encoding of the SubjectPublicKeyInfo for this public key
+ inline void set_public_key_info(const std::vector<uint8_t>& pubkey_info)
+ {
+ add_binary(AttributeType::PublicKeyInfo, pubkey_info);
+ }
+ };
+
+/// Common attributes of all secret (symmetric) keys
+class BOTAN_PUBLIC_API(2,0) SecretKeyProperties final : public KeyProperties
+ {
+ public:
+ /// @param key_type type of key
+ SecretKeyProperties(KeyType key_type);
+
+ /// @param value true if the key is sensitive
+ inline void set_sensitive(bool value)
+ {
+ add_bool(AttributeType::Sensitive, value);
+ }
+
+ /// @param value true if the key supports encryption
+ inline void set_encrypt(bool value)
+ {
+ add_bool(AttributeType::Encrypt, value);
+ }
+
+ /// @param value true if the key supports decryption
+ inline void set_decrypt(bool value)
+ {
+ add_bool(AttributeType::Decrypt, value);
+ }
+
+ /// @param value true if the key supports signatures where the signature is an appendix to the data
+ inline void set_sign(bool value)
+ {
+ add_bool(AttributeType::Sign, value);
+ }
+
+ /// @param value true if the key supports verification where the signature is an appendix to the data
+ inline void set_verify(bool value)
+ {
+ add_bool(AttributeType::Verify, value);
+ }
+
+ /// @param value true if the key supports unwrapping (i.e., can be used to unwrap other keys)
+ inline void set_unwrap(bool value)
+ {
+ add_bool(AttributeType::Unwrap, value);
+ }
+
+ /// @param value true if the key is extractable and can be wrapped
+ inline void set_extractable(bool value)
+ {
+ add_bool(AttributeType::Extractable, value);
+ }
+
+ /// @param value true if the key can only be wrapped with a wrapping key that has `CKA_TRUSTED` set to `CK_TRUE`
+ inline void set_wrap_with_trusted(bool value)
+ {
+ add_bool(AttributeType::WrapWithTrusted, value);
+ }
+
+ /// @param value if true, the user has to supply the PIN for each use (sign or decrypt) with the key
+ inline void set_always_authenticate(bool value)
+ {
+ add_bool(AttributeType::AlwaysAuthenticate, value);
+ }
+
+ /// @param value true if the key supports wrapping (i.e., can be used to wrap other keys)
+ inline void set_wrap(bool value)
+ {
+ add_bool(AttributeType::Wrap, value);
+ }
+
+ /**
+ * @param value the key can be trusted for the application that it was created.
+ * The wrapping key can be used to wrap keys with `CKA_WRAP_WITH_TRUSTED` set to `CK_TRUE`
+ */
+ inline void set_trusted(bool value)
+ {
+ add_bool(AttributeType::Trusted, value);
+ }
+
+ /// @param checksum the key check value of this key
+ inline void set_check_value(const std::vector<uint8_t>& checksum)
+ {
+ add_binary(AttributeType::CheckValue, checksum);
+ }
+
+ /**
+ * For wrapping keys
+ * The attribute template to match against any keys wrapped using this wrapping key.
+ * Keys that do not match cannot be wrapped
+ * Not implemented
+ */
+ inline void set_wrap_template(const AttributeContainer&)
+ {
+ throw Not_Implemented("SecretKeyProperties::set_wrap_template");
+ }
+
+ /**
+ * For wrapping keys
+ * The attribute template to apply to any keys unwrapped using this wrapping key
+ * Any user supplied template is applied after this template as if the object has already been created
+ * Not Implemented
+ */
+ inline void set_unwrap_template(const AttributeContainer&)
+ {
+ throw Not_Implemented("SecretKeyProperties::set_unwrap_template");
+ }
+ };
+
+/// Common attributes of domain parameter
+class BOTAN_PUBLIC_API(2,0) DomainParameterProperties final : public StorageObjectProperties
+ {
+ public:
+ /// @param key_type type of key the domain parameters can be used to generate
+ DomainParameterProperties(KeyType key_type);
+
+ /// @return the key type
+ inline KeyType key_type() const
+ {
+ return m_key_type;
+ }
+
+ private:
+ const KeyType m_key_type;
+ };
+
+/**
+* Represents a PKCS#11 object.
+*/
+class BOTAN_PUBLIC_API(2,0) Object
+ {
+ public:
+ /**
+ * Creates an `Object` from an existing PKCS#11 object
+ * @param session the session the object belongs to
+ * @param handle handle of the object
+ */
+
+ Object(Session& session, ObjectHandle handle);
+
+ /**
+ * Creates the object
+ * @param session the session in which the object should be created
+ * @param obj_props properties of this object
+ */
+ Object(Session& session, const ObjectProperties& obj_props);
+
+ Object(const Object&) = default;
+ Object& operator=(const Object&) = delete;
+ virtual ~Object() = default;
+
+ /// Searches for all objects of the given type that match `search_template`
+ template<typename T>
+ static std::vector<T> search(Session& session, const std::vector<Attribute>& search_template);
+
+ /// Searches for all objects of the given type using the label (`CKA_LABEL`)
+ template<typename T>
+ static std::vector<T> search(Session& session, const std::string& label);
+
+ /// Searches for all objects of the given type using the id (`CKA_ID`)
+ template<typename T>
+ static std::vector<T> search(Session& session, const std::vector<uint8_t>& id);
+
+ /// Searches for all objects of the given type using the label (`CKA_LABEL`) and id (`CKA_ID`)
+ template<typename T>
+ static std::vector<T> search(Session& session, const std::string& label, const std::vector<uint8_t>& id);
+
+ /// Searches for all objects of the given type
+ template<typename T>
+ static std::vector<T> search(Session& session);
+
+ /// @returns the value of the given attribute (using `C_GetAttributeValue`)
+ secure_vector<uint8_t> get_attribute_value(AttributeType attribute) const;
+
+ /// Sets the given value for the attribute (using `C_SetAttributeValue`)
+ void set_attribute_value(AttributeType attribute, const secure_vector<uint8_t>& value) const;
+
+ /// Destroys the object
+ void destroy() const;
+
+ /**
+ * Copies the object
+ * @param modified_attributes the attributes of the copied object
+ */
+ ObjectHandle copy(const AttributeContainer& modified_attributes) const;
+
+ /// @return the handle of this object.
+ inline ObjectHandle handle() const
+ {
+ return m_handle;
+ }
+
+ /// @return the session this objects belongs to
+ inline Session& session() const
+ {
+ return m_session;
+ }
+
+ /// @return the module this object belongs to
+ inline Module& module() const
+ {
+ return m_session.get().module();
+ }
+ protected:
+ Object(Session& session)
+ : m_session(session)
+ {}
+
+ void reset_handle(ObjectHandle handle)
+ {
+ if(m_handle != CK_INVALID_HANDLE)
+ throw Invalid_Argument("Cannot reset handle on already valid PKCS11 object");
+ m_handle = handle;
+ }
+
+ private:
+ const std::reference_wrapper<Session> m_session;
+ ObjectHandle m_handle = CK_INVALID_HANDLE;
+ };
+
+template<typename T>
+std::vector<T> Object::search(Session& session, const std::vector<Attribute>& search_template)
+ {
+ ObjectFinder finder(session, search_template);
+ std::vector<ObjectHandle> handles = finder.find();
+ std::vector<T> result;
+ result.reserve(handles.size());
+ for(const auto& handle : handles)
+ {
+ result.emplace_back(T(session, handle));
+ }
+ return result;
+ }
+
+template<typename T>
+std::vector<T> Object::search(Session& session, const std::string& label)
+ {
+ AttributeContainer search_template(T::Class);
+ search_template.add_string(AttributeType::Label, label);
+ return search<T>(session, search_template.attributes());
+ }
+
+template<typename T>
+std::vector<T> Object::search(Session& session, const std::vector<uint8_t>& id)
+ {
+ AttributeContainer search_template(T::Class);
+ search_template.add_binary(AttributeType::Id, id);
+ return search<T>(session, search_template.attributes());
+ }
+
+template<typename T>
+std::vector<T> Object::search(Session& session, const std::string& label, const std::vector<uint8_t>& id)
+ {
+ AttributeContainer search_template(T::Class);
+ search_template.add_string(AttributeType::Label, label);
+ search_template.add_binary(AttributeType::Id, id);
+ return search<T>(session, search_template.attributes());
+ }
+
+template<typename T>
+std::vector<T> Object::search(Session& session)
+ {
+ return search<T>(session, AttributeContainer(T::Class).attributes());
+ }
+
+}
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/prov/pkcs11/p11_randomgenerator.cpp b/comm/third_party/botan/src/lib/prov/pkcs11/p11_randomgenerator.cpp
new file mode 100644
index 0000000000..2f4e4c2ec9
--- /dev/null
+++ b/comm/third_party/botan/src/lib/prov/pkcs11/p11_randomgenerator.cpp
@@ -0,0 +1,31 @@
+/*
+* PKCS#11 Random Generator
+* (C) 2016 Daniel Neus, Sirrix AG
+* (C) 2016 Philipp Weber, Sirrix AG
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/p11_randomgenerator.h>
+
+namespace Botan {
+
+namespace PKCS11 {
+
+PKCS11_RNG::PKCS11_RNG(Session& session)
+ : m_session(session)
+ {}
+
+void PKCS11_RNG::randomize(uint8_t output[], std::size_t length)
+ {
+ module()->C_GenerateRandom(m_session.get().handle(), output, Ulong(length));
+ }
+
+void PKCS11_RNG::add_entropy(const uint8_t in[], std::size_t length)
+ {
+ module()->C_SeedRandom(m_session.get().handle(), const_cast<uint8_t*>(in), Ulong(length));
+ }
+
+}
+}
+
diff --git a/comm/third_party/botan/src/lib/prov/pkcs11/p11_randomgenerator.h b/comm/third_party/botan/src/lib/prov/pkcs11/p11_randomgenerator.h
new file mode 100644
index 0000000000..339cb95a5c
--- /dev/null
+++ b/comm/third_party/botan/src/lib/prov/pkcs11/p11_randomgenerator.h
@@ -0,0 +1,70 @@
+/*
+* PKCS#11 Random Generator
+* (C) 2016 Daniel Neus, Sirrix AG
+* (C) 2016 Philipp Weber, Sirrix AG
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_P11_RNG_H_
+#define BOTAN_P11_RNG_H_
+
+#include <botan/rng.h>
+#include <botan/p11_types.h>
+#include <botan/entropy_src.h>
+
+#include <string>
+#include <functional>
+
+namespace Botan {
+namespace PKCS11 {
+
+class Module;
+
+/// A random generator that only fetches random from the PKCS#11 RNG
+class BOTAN_PUBLIC_API(2,0) PKCS11_RNG final : public Hardware_RNG
+ {
+ public:
+ /// Initialize the RNG with the PKCS#11 session that provides access to the cryptoki functions
+ explicit PKCS11_RNG(Session& session);
+
+ std::string name() const override
+ {
+ return "PKCS11_RNG";
+ }
+
+ /// Always returns true
+ bool is_seeded() const override
+ {
+ return true;
+ }
+
+ /// No operation - always returns 0
+ size_t reseed(Entropy_Sources&, size_t, std::chrono::milliseconds) override
+ {
+ return 0;
+ }
+
+ /// @return the module used by this RNG
+ inline Module& module() const
+ {
+ return m_session.get().module();
+ }
+
+ /// Calls `C_GenerateRandom` to generate random data
+ void randomize(uint8_t output[], std::size_t length) override;
+
+ /// Calls `C_SeedRandom` to add entropy to the random generation function of the token/middleware
+ void add_entropy(const uint8_t in[], std::size_t length) override;
+
+ // C_SeedRandom may suceed
+ bool accepts_input() const override { return true; }
+
+ private:
+ const std::reference_wrapper<Session> m_session;
+ };
+}
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/prov/pkcs11/p11_rsa.cpp b/comm/third_party/botan/src/lib/prov/pkcs11/p11_rsa.cpp
new file mode 100644
index 0000000000..0e434c5b34
--- /dev/null
+++ b/comm/third_party/botan/src/lib/prov/pkcs11/p11_rsa.cpp
@@ -0,0 +1,373 @@
+/*
+* PKCS#11 RSA
+* (C) 2016 Daniel Neus, Sirrix AG
+* (C) 2016 Philipp Weber, Sirrix AG
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/p11_rsa.h>
+#include <botan/pk_keys.h>
+
+#if defined(BOTAN_HAS_RSA)
+
+#include <botan/internal/p11_mechanism.h>
+#include <botan/pk_ops.h>
+#include <botan/rng.h>
+#include <botan/blinding.h>
+
+namespace Botan {
+
+namespace PKCS11 {
+
+RSA_PublicKeyImportProperties::RSA_PublicKeyImportProperties(const BigInt& modulus, const BigInt& pub_exponent)
+ : PublicKeyProperties(KeyType::Rsa), m_modulus(modulus), m_pub_exponent(pub_exponent)
+ {
+ add_binary(AttributeType::Modulus, BigInt::encode(m_modulus));
+ add_binary(AttributeType::PublicExponent, BigInt::encode(m_pub_exponent));
+ }
+
+RSA_PublicKeyGenerationProperties::RSA_PublicKeyGenerationProperties(Ulong bits)
+ : PublicKeyProperties(KeyType::Rsa)
+ {
+ add_numeric(AttributeType::ModulusBits, bits);
+ }
+
+PKCS11_RSA_PublicKey::PKCS11_RSA_PublicKey(Session& session, ObjectHandle handle)
+ : Object(session, handle),
+ RSA_PublicKey(BigInt::decode(get_attribute_value(AttributeType::Modulus)),
+ BigInt::decode(get_attribute_value(AttributeType::PublicExponent)))
+ {
+ }
+
+PKCS11_RSA_PublicKey::PKCS11_RSA_PublicKey(Session& session, const RSA_PublicKeyImportProperties& pubkey_props)
+ : Object(session, pubkey_props), RSA_PublicKey(pubkey_props.modulus(), pubkey_props.pub_exponent())
+ {}
+
+
+RSA_PrivateKeyImportProperties::RSA_PrivateKeyImportProperties(const BigInt& modulus, const BigInt& priv_exponent)
+ : PrivateKeyProperties(KeyType::Rsa), m_modulus(modulus), m_priv_exponent(priv_exponent)
+ {
+ add_binary(AttributeType::Modulus, BigInt::encode(m_modulus));
+ add_binary(AttributeType::PrivateExponent, BigInt::encode(m_priv_exponent));
+ }
+
+
+PKCS11_RSA_PrivateKey::PKCS11_RSA_PrivateKey(Session& session, ObjectHandle handle)
+ : Object(session, handle),
+ RSA_PublicKey(BigInt::decode(get_attribute_value(AttributeType::Modulus)),
+ BigInt::decode(get_attribute_value(AttributeType::PublicExponent)))
+ {
+ }
+
+PKCS11_RSA_PrivateKey::PKCS11_RSA_PrivateKey(Session& session, const RSA_PrivateKeyImportProperties& priv_key_props)
+ : Object(session, priv_key_props),
+ RSA_PublicKey(priv_key_props.modulus(),
+ BigInt::decode(get_attribute_value(AttributeType::PublicExponent)))
+ {
+ }
+
+PKCS11_RSA_PrivateKey::PKCS11_RSA_PrivateKey(Session& session, uint32_t bits,
+ const RSA_PrivateKeyGenerationProperties& priv_key_props)
+ : Object(session), RSA_PublicKey()
+ {
+ RSA_PublicKeyGenerationProperties pub_key_props(bits);
+ pub_key_props.set_encrypt(true);
+ pub_key_props.set_verify(true);
+ pub_key_props.set_token(false); // don't create a persistent public key object
+
+ ObjectHandle pub_key_handle = CK_INVALID_HANDLE;
+ ObjectHandle priv_key_handle = CK_INVALID_HANDLE;
+ Mechanism mechanism = { static_cast< CK_MECHANISM_TYPE >(MechanismType::RsaPkcsKeyPairGen), nullptr, 0 };
+ session.module()->C_GenerateKeyPair(session.handle(), &mechanism,
+ pub_key_props.data(), static_cast<Ulong>(pub_key_props.count()),
+ priv_key_props.data(), static_cast<Ulong>(priv_key_props.count()),
+ &pub_key_handle, &priv_key_handle);
+
+ this->reset_handle(priv_key_handle);
+
+ BigInt n = BigInt::decode(get_attribute_value(AttributeType::Modulus));
+ BigInt e = BigInt::decode(get_attribute_value(AttributeType::PublicExponent));
+ RSA_PublicKey::init(std::move(n), std::move(e));
+ }
+
+RSA_PrivateKey PKCS11_RSA_PrivateKey::export_key() const
+ {
+ auto p = get_attribute_value(AttributeType::Prime1);
+ auto q = get_attribute_value(AttributeType::Prime2);
+ auto e = get_attribute_value(AttributeType::PublicExponent);
+ auto d = get_attribute_value(AttributeType::PrivateExponent);
+ auto n = get_attribute_value(AttributeType::Modulus);
+
+ return RSA_PrivateKey( BigInt::decode(p)
+ , BigInt::decode(q)
+ , BigInt::decode(e)
+ , BigInt::decode(d)
+ , BigInt::decode(n));
+ }
+
+secure_vector<uint8_t> PKCS11_RSA_PrivateKey::private_key_bits() const
+ {
+ return export_key().private_key_bits();
+ }
+
+
+namespace {
+// note: multiple-part decryption operations (with C_DecryptUpdate/C_DecryptFinal)
+// are not supported (PK_Ops::Decryption does not provide an `update` method)
+class PKCS11_RSA_Decryption_Operation final : public PK_Ops::Decryption
+ {
+ public:
+
+ PKCS11_RSA_Decryption_Operation(const PKCS11_RSA_PrivateKey& key,
+ const std::string& padding,
+ RandomNumberGenerator& rng)
+ : m_key(key),
+ m_mechanism(MechanismWrapper::create_rsa_crypt_mechanism(padding)),
+ m_blinder(m_key.get_n(), rng,
+ [ this ](const BigInt& k) { return power_mod(k, m_key.get_e(), m_key.get_n()); },
+ [ this ](const BigInt& k) { return inverse_mod(k, m_key.get_n()); })
+ {
+ m_bits = m_key.get_n().bits() - 1;
+ }
+
+ size_t plaintext_length(size_t) const override { return m_key.get_n().bytes(); }
+
+ secure_vector<uint8_t> decrypt(uint8_t& valid_mask, const uint8_t ciphertext[], size_t ciphertext_len) override
+ {
+ valid_mask = 0;
+ m_key.module()->C_DecryptInit(m_key.session().handle(), m_mechanism.data(), m_key.handle());
+
+ std::vector<uint8_t> encrypted_data(ciphertext, ciphertext + ciphertext_len);
+
+ // blind for RSA/RAW decryption
+ if(! m_mechanism.padding_size())
+ {
+ encrypted_data = BigInt::encode(m_blinder.blind(BigInt::decode(encrypted_data)));
+ }
+
+ secure_vector<uint8_t> decrypted_data;
+ m_key.module()->C_Decrypt(m_key.session().handle(), encrypted_data, decrypted_data);
+
+ // Unblind for RSA/RAW decryption
+ if(!m_mechanism.padding_size())
+ {
+ decrypted_data = BigInt::encode_1363(m_blinder.unblind(BigInt::decode(decrypted_data)), m_key.get_n().bits() / 8 );
+ }
+
+ valid_mask = 0xFF;
+ return decrypted_data;
+ }
+
+ private:
+ const PKCS11_RSA_PrivateKey& m_key;
+ MechanismWrapper m_mechanism;
+ size_t m_bits = 0;
+ Blinder m_blinder;
+ };
+
+// note: multiple-part encryption operations (with C_EncryptUpdate/C_EncryptFinal)
+// are not supported (PK_Ops::Encryption does not provide an `update` method)
+class PKCS11_RSA_Encryption_Operation final : public PK_Ops::Encryption
+ {
+ public:
+
+ PKCS11_RSA_Encryption_Operation(const PKCS11_RSA_PublicKey& key, const std::string& padding)
+ : m_key(key), m_mechanism(MechanismWrapper::create_rsa_crypt_mechanism(padding))
+ {
+ m_bits = 8 * (key.get_n().bytes() - m_mechanism.padding_size()) - 1;
+ }
+
+ size_t ciphertext_length(size_t) const override { return m_key.get_n().bytes(); }
+
+ size_t max_input_bits() const override
+ {
+ return m_bits;
+ }
+
+ secure_vector<uint8_t> encrypt(const uint8_t msg[], size_t msg_len, RandomNumberGenerator&) override
+ {
+ m_key.module()->C_EncryptInit(m_key.session().handle(), m_mechanism.data(), m_key.handle());
+
+ secure_vector<uint8_t> encrytped_data;
+ m_key.module()->C_Encrypt(m_key.session().handle(), secure_vector<uint8_t>(msg, msg + msg_len), encrytped_data);
+ return encrytped_data;
+ }
+
+ private:
+ const PKCS11_RSA_PublicKey& m_key;
+ MechanismWrapper m_mechanism;
+ size_t m_bits = 0;
+ };
+
+
+class PKCS11_RSA_Signature_Operation final : public PK_Ops::Signature
+ {
+ public:
+
+ PKCS11_RSA_Signature_Operation(const PKCS11_RSA_PrivateKey& key, const std::string& padding)
+ : m_key(key), m_mechanism(MechanismWrapper::create_rsa_sign_mechanism(padding))
+ {}
+
+ size_t signature_length() const override { return m_key.get_n().bytes(); }
+
+ void update(const uint8_t msg[], size_t msg_len) override
+ {
+ if(!m_initialized)
+ {
+ // first call to update: initialize and cache message because we can not determine yet whether a single- or multiple-part operation will be performed
+ m_key.module()->C_SignInit(m_key.session().handle(), m_mechanism.data(), m_key.handle());
+ m_initialized = true;
+ m_first_message = secure_vector<uint8_t>(msg, msg + msg_len);
+ return;
+ }
+
+ if(!m_first_message.empty())
+ {
+ // second call to update: start multiple-part operation
+ m_key.module()->C_SignUpdate(m_key.session().handle(), m_first_message);
+ m_first_message.clear();
+ }
+
+ m_key.module()->C_SignUpdate(m_key.session().handle(), const_cast< Byte* >(msg), static_cast<Ulong>(msg_len));
+ }
+
+ secure_vector<uint8_t> sign(RandomNumberGenerator&) override
+ {
+ secure_vector<uint8_t> signature;
+ if(!m_first_message.empty())
+ {
+ // single call to update: perform single-part operation
+ m_key.module()->C_Sign(m_key.session().handle(), m_first_message, signature);
+ m_first_message.clear();
+ }
+ else
+ {
+ // multiple calls to update (or none): finish multiple-part operation
+ m_key.module()->C_SignFinal(m_key.session().handle(), signature);
+ }
+ m_initialized = false;
+ return signature;
+ }
+
+ private:
+ const PKCS11_RSA_PrivateKey& m_key;
+ bool m_initialized = false;
+ secure_vector<uint8_t> m_first_message;
+ MechanismWrapper m_mechanism;
+ };
+
+
+class PKCS11_RSA_Verification_Operation final : public PK_Ops::Verification
+ {
+ public:
+
+ PKCS11_RSA_Verification_Operation(const PKCS11_RSA_PublicKey& key, const std::string& padding)
+ : m_key(key), m_mechanism(MechanismWrapper::create_rsa_sign_mechanism(padding))
+ {}
+
+ void update(const uint8_t msg[], size_t msg_len) override
+ {
+ if(!m_initialized)
+ {
+ // first call to update: initialize and cache message because we can not determine yet whether a single- or multiple-part operation will be performed
+ m_key.module()->C_VerifyInit(m_key.session().handle(), m_mechanism.data(), m_key.handle());
+ m_initialized = true;
+ m_first_message = secure_vector<uint8_t>(msg, msg + msg_len);
+ return;
+ }
+
+ if(!m_first_message.empty())
+ {
+ // second call to update: start multiple-part operation
+ m_key.module()->C_VerifyUpdate(m_key.session().handle(), m_first_message);
+ m_first_message.clear();
+ }
+
+ m_key.module()->C_VerifyUpdate(m_key.session().handle(), const_cast< Byte* >(msg), static_cast<Ulong>(msg_len));
+ }
+
+ bool is_valid_signature(const uint8_t sig[], size_t sig_len) override
+ {
+ ReturnValue return_value = ReturnValue::SignatureInvalid;
+ if(!m_first_message.empty())
+ {
+ // single call to update: perform single-part operation
+ m_key.module()->C_Verify(m_key.session().handle(),
+ m_first_message.data(), static_cast<Ulong>(m_first_message.size()),
+ const_cast< Byte* >(sig), static_cast<Ulong>(sig_len), &return_value);
+ m_first_message.clear();
+ }
+ else
+ {
+ // multiple calls to update (or none): finish multiple-part operation
+ m_key.module()->C_VerifyFinal(m_key.session().handle(), const_cast< Byte* >(sig), static_cast<Ulong>(sig_len), &return_value);
+ }
+ m_initialized = false;
+ if(return_value != ReturnValue::OK && return_value != ReturnValue::SignatureInvalid)
+ {
+ throw PKCS11_ReturnError(return_value);
+ }
+ return return_value == ReturnValue::OK;
+ }
+
+ private:
+ const PKCS11_RSA_PublicKey& m_key;
+ bool m_initialized = false;
+ secure_vector<uint8_t> m_first_message;
+ MechanismWrapper m_mechanism;
+ };
+
+}
+
+std::unique_ptr<PK_Ops::Encryption>
+PKCS11_RSA_PublicKey::create_encryption_op(RandomNumberGenerator& /*rng*/,
+ const std::string& params,
+ const std::string& /*provider*/) const
+ {
+ return std::unique_ptr<PK_Ops::Encryption>(new PKCS11_RSA_Encryption_Operation(*this, params));
+ }
+
+std::unique_ptr<PK_Ops::Verification>
+PKCS11_RSA_PublicKey::create_verification_op(const std::string& params,
+ const std::string& /*provider*/) const
+ {
+ return std::unique_ptr<PK_Ops::Verification>(new PKCS11_RSA_Verification_Operation(*this, params));
+ }
+
+std::unique_ptr<PK_Ops::Decryption>
+PKCS11_RSA_PrivateKey::create_decryption_op(RandomNumberGenerator& rng,
+ const std::string& params,
+ const std::string& /*provider*/) const
+ {
+ return std::unique_ptr<PK_Ops::Decryption>(new PKCS11_RSA_Decryption_Operation(*this, params, rng));
+ }
+
+std::unique_ptr<PK_Ops::Signature>
+PKCS11_RSA_PrivateKey::create_signature_op(RandomNumberGenerator& /*rng*/,
+ const std::string& params,
+ const std::string& /*provider*/) const
+ {
+ return std::unique_ptr<PK_Ops::Signature>(new PKCS11_RSA_Signature_Operation(*this, params));
+ }
+
+PKCS11_RSA_KeyPair generate_rsa_keypair(Session& session, const RSA_PublicKeyGenerationProperties& pub_props,
+ const RSA_PrivateKeyGenerationProperties& priv_props)
+ {
+ ObjectHandle pub_key_handle = 0;
+ ObjectHandle priv_key_handle = 0;
+
+ Mechanism mechanism = { static_cast< CK_MECHANISM_TYPE >(MechanismType::RsaPkcsKeyPairGen), nullptr, 0 };
+
+ session.module()->C_GenerateKeyPair(session.handle(), &mechanism,
+ pub_props.data(), static_cast<Ulong>(pub_props.count()),
+ priv_props.data(), static_cast<Ulong>(priv_props.count()),
+ &pub_key_handle, &priv_key_handle);
+
+ return std::make_pair(PKCS11_RSA_PublicKey(session, pub_key_handle), PKCS11_RSA_PrivateKey(session, priv_key_handle));
+ }
+
+}
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/prov/pkcs11/p11_rsa.h b/comm/third_party/botan/src/lib/prov/pkcs11/p11_rsa.h
new file mode 100644
index 0000000000..41d9bc1348
--- /dev/null
+++ b/comm/third_party/botan/src/lib/prov/pkcs11/p11_rsa.h
@@ -0,0 +1,229 @@
+/*
+* PKCS#11 RSA
+* (C) 2016 Daniel Neus, Sirrix AG
+* (C) 2016 Philipp Weber, Sirrix AG
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_P11_RSA_H_
+#define BOTAN_P11_RSA_H_
+
+#include <botan/p11_types.h>
+#include <botan/p11_object.h>
+#include <botan/pk_keys.h>
+#include <botan/bigint.h>
+
+#if defined(BOTAN_HAS_RSA)
+#include <botan/rsa.h>
+#include <utility>
+
+namespace Botan {
+namespace PKCS11 {
+
+/// Properties for generating a PKCS#11 RSA public key
+class BOTAN_PUBLIC_API(2,0) RSA_PublicKeyGenerationProperties final : public PublicKeyProperties
+ {
+ public:
+ /// @param bits length in bits of modulus n
+ explicit RSA_PublicKeyGenerationProperties(Ulong bits);
+
+ /// @param pub_exponent public exponent e
+ inline void set_pub_exponent(const BigInt& pub_exponent = BigInt(0x10001))
+ {
+ add_binary(AttributeType::PublicExponent, BigInt::encode(pub_exponent));
+ }
+
+ virtual ~RSA_PublicKeyGenerationProperties() = default;
+ };
+
+/// Properties for importing a PKCS#11 RSA public key
+class BOTAN_PUBLIC_API(2,0) RSA_PublicKeyImportProperties final : public PublicKeyProperties
+ {
+ public:
+ /// @param modulus modulus n
+ /// @param pub_exponent public exponent e
+ RSA_PublicKeyImportProperties(const BigInt& modulus, const BigInt& pub_exponent);
+
+ /// @return the modulus
+ inline const BigInt& modulus() const
+ {
+ return m_modulus;
+ }
+
+ /// @return the public exponent
+ inline const BigInt& pub_exponent() const
+ {
+ return m_pub_exponent;
+ }
+
+ virtual ~RSA_PublicKeyImportProperties() = default;
+ private:
+ const BigInt m_modulus;
+ const BigInt m_pub_exponent;
+ };
+
+/// Represents a PKCS#11 RSA public key
+class BOTAN_PUBLIC_API(2,0) PKCS11_RSA_PublicKey : public Object, public RSA_PublicKey
+ {
+ public:
+ static const ObjectClass Class = ObjectClass::PublicKey;
+
+ /**
+ * Creates a PKCS11_RSA_PublicKey object from an existing PKCS#11 RSA public key
+ * @param session the session to use
+ * @param handle the handle of the RSA public key
+ */
+ PKCS11_RSA_PublicKey(Session& session, ObjectHandle handle);
+
+ /**
+ * Imports a RSA public key
+ * @param session the session to use
+ * @param pubkey_props the attributes of the public key
+ */
+ PKCS11_RSA_PublicKey(Session& session, const RSA_PublicKeyImportProperties& pubkey_props);
+
+ std::unique_ptr<PK_Ops::Encryption>
+ create_encryption_op(RandomNumberGenerator& rng,
+ const std::string& params,
+ const std::string& provider) const override;
+
+ std::unique_ptr<PK_Ops::Verification>
+ create_verification_op(const std::string& params,
+ const std::string& provider) const override;
+ };
+
+/// Properties for importing a PKCS#11 RSA private key
+class BOTAN_PUBLIC_API(2,0) RSA_PrivateKeyImportProperties final : public PrivateKeyProperties
+ {
+ public:
+ /**
+ * @param modulus modulus n
+ * @param priv_exponent private exponent d
+ */
+ RSA_PrivateKeyImportProperties(const BigInt& modulus, const BigInt& priv_exponent);
+
+ /// @param pub_exponent public exponent e
+ inline void set_pub_exponent(const BigInt& pub_exponent)
+ {
+ add_binary(AttributeType::PublicExponent, BigInt::encode(pub_exponent));
+ }
+
+ /// @param prime1 prime p
+ inline void set_prime_1(const BigInt& prime1)
+ {
+ add_binary(AttributeType::Prime1, BigInt::encode(prime1));
+ }
+
+ /// @param prime2 prime q
+ inline void set_prime_2(const BigInt& prime2)
+ {
+ add_binary(AttributeType::Prime2, BigInt::encode(prime2));
+ }
+
+ /// @param exp1 private exponent d modulo p-1
+ inline void set_exponent_1(const BigInt& exp1)
+ {
+ add_binary(AttributeType::Exponent1, BigInt::encode(exp1));
+ }
+
+ /// @param exp2 private exponent d modulo q-1
+ inline void set_exponent_2(const BigInt& exp2)
+ {
+ add_binary(AttributeType::Exponent2, BigInt::encode(exp2));
+ }
+
+ /// @param coeff CRT coefficient q^-1 mod p
+ inline void set_coefficient(const BigInt& coeff)
+ {
+ add_binary(AttributeType::Coefficient, BigInt::encode(coeff));
+ }
+
+ /// @return the modulus
+ inline const BigInt& modulus() const
+ {
+ return m_modulus;
+ }
+
+ /// @return the private exponent
+ inline const BigInt& priv_exponent() const
+ {
+ return m_priv_exponent;
+ }
+
+ virtual ~RSA_PrivateKeyImportProperties() = default;
+
+ private:
+ const BigInt m_modulus;
+ const BigInt m_priv_exponent;
+ };
+
+/// Properties for generating a PKCS#11 RSA private key
+class BOTAN_PUBLIC_API(2,0) RSA_PrivateKeyGenerationProperties final : public PrivateKeyProperties
+ {
+ public:
+ RSA_PrivateKeyGenerationProperties()
+ : PrivateKeyProperties(KeyType::Rsa)
+ {}
+
+ virtual ~RSA_PrivateKeyGenerationProperties() = default;
+ };
+
+/// Represents a PKCS#11 RSA private key
+class BOTAN_PUBLIC_API(2,0) PKCS11_RSA_PrivateKey final :
+ public Object, public Private_Key, public RSA_PublicKey
+ {
+ public:
+ static const ObjectClass Class = ObjectClass::PrivateKey;
+
+ /// Creates a PKCS11_RSA_PrivateKey object from an existing PKCS#11 RSA private key
+ PKCS11_RSA_PrivateKey(Session& session, ObjectHandle handle);
+
+ /**
+ * Imports a RSA private key
+ * @param session the session to use
+ * @param priv_key_props the properties of the RSA private key
+ */
+ PKCS11_RSA_PrivateKey(Session& session, const RSA_PrivateKeyImportProperties& priv_key_props);
+
+ /**
+ * Generates a PKCS#11 RSA private key
+ * @param session the session to use
+ * @param bits length in bits of modulus n
+ * @param priv_key_props the properties of the RSA private key
+ * @note no persistent public key object will be created
+ */
+ PKCS11_RSA_PrivateKey(Session& session, uint32_t bits, const RSA_PrivateKeyGenerationProperties& priv_key_props);
+
+ /// @return the exported RSA private key
+ RSA_PrivateKey export_key() const;
+
+ secure_vector<uint8_t> private_key_bits() const override;
+
+ std::unique_ptr<PK_Ops::Decryption>
+ create_decryption_op(RandomNumberGenerator& rng,
+ const std::string& params,
+ const std::string& provider) const override;
+
+ std::unique_ptr<PK_Ops::Signature>
+ create_signature_op(RandomNumberGenerator& rng,
+ const std::string& params,
+ const std::string& provider) const override;
+ };
+
+using PKCS11_RSA_KeyPair = std::pair<PKCS11_RSA_PublicKey, PKCS11_RSA_PrivateKey>;
+
+/**
+* RSA key pair generation
+* @param session the session that should be used for the key generation
+* @param pub_props properties of the public key
+* @param priv_props properties of the private key
+*/
+BOTAN_PUBLIC_API(2,0) PKCS11_RSA_KeyPair generate_rsa_keypair(Session& session, const RSA_PublicKeyGenerationProperties& pub_props,
+ const RSA_PrivateKeyGenerationProperties& priv_props);
+}
+
+}
+#endif
+
+#endif
diff --git a/comm/third_party/botan/src/lib/prov/pkcs11/p11_session.cpp b/comm/third_party/botan/src/lib/prov/pkcs11/p11_session.cpp
new file mode 100644
index 0000000000..3f5bfc001e
--- /dev/null
+++ b/comm/third_party/botan/src/lib/prov/pkcs11/p11_session.cpp
@@ -0,0 +1,96 @@
+/*
+* PKCS#11 Session
+* (C) 2016 Daniel Neus, Sirrix AG
+* (C) 2016 Philipp Weber, Sirrix AG
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/p11_types.h>
+
+namespace Botan {
+namespace PKCS11 {
+
+Session::Session(Slot& slot, bool read_only)
+ : Session(slot, PKCS11::flags(Flag::SerialSession | (read_only ? Flag::None : Flag::RwSession)), nullptr, nullptr)
+ {}
+
+Session::Session(Slot& slot, Flags flags, VoidPtr callback_data, Notify notify_callback)
+ : m_slot(slot), m_handle(0), m_logged_in(false)
+ {
+ module()->C_OpenSession(m_slot.slot_id(), flags, callback_data, notify_callback, &m_handle);
+ }
+
+Session::Session(Slot& slot, SessionHandle handle)
+ : m_slot(slot), m_handle(handle)
+ {
+ SessionInfo info = get_info();
+ if(info.state == static_cast<CK_STATE>(SessionState::RoPublicSession)
+ || info.state == static_cast<CK_STATE>(SessionState::RwPublicSession))
+ {
+ m_logged_in = false;
+ }
+ else
+ {
+ m_logged_in = true;
+ }
+ }
+
+Session::~Session() noexcept
+ {
+ try
+ {
+ if(m_handle)
+ {
+ if(m_logged_in)
+ {
+ module()->C_Logout(m_handle, nullptr);
+ }
+ module()->C_CloseSession(m_handle, nullptr);
+ m_handle = 0;
+ }
+ }
+ catch(...)
+ {
+ // exception during noexcept destructor is ignored
+ }
+ }
+
+SessionHandle Session::release()
+ {
+ SessionHandle handle = 0;
+ std::swap(handle, m_handle);
+ return handle;
+ }
+
+void Session::login(UserType user_type, const secure_string& pin)
+ {
+ module()->C_Login(m_handle, user_type, pin);
+ m_logged_in = true;
+ }
+
+void Session::logoff()
+ {
+ module()->C_Logout(m_handle);
+ m_logged_in = false;
+ }
+
+SessionInfo Session::get_info() const
+ {
+ SessionInfo info;
+ module()->C_GetSessionInfo(m_handle, &info);
+ return info;
+ }
+
+void Session::set_pin(const secure_string& old_pin, const secure_string& new_pin) const
+ {
+ module()->C_SetPIN(m_handle, old_pin, new_pin);
+ }
+
+void Session::init_pin(const secure_string& new_pin)
+ {
+ module()->C_InitPIN(m_handle, new_pin);
+ }
+
+}
+}
diff --git a/comm/third_party/botan/src/lib/prov/pkcs11/p11_session.h b/comm/third_party/botan/src/lib/prov/pkcs11/p11_session.h
new file mode 100644
index 0000000000..39b7877cbf
--- /dev/null
+++ b/comm/third_party/botan/src/lib/prov/pkcs11/p11_session.h
@@ -0,0 +1,15 @@
+/*
+* PKCS#11 Session
+* (C) 2016 Daniel Neus, Sirrix AG
+* (C) 2016 Philipp Weber, Sirrix AG
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_P11_SESSION_H_
+#define BOTAN_P11_SESSION_H_
+
+#include <botan/p11_types.h>
+BOTAN_DEPRECATED_HEADER(p11_session.h)
+
+#endif
diff --git a/comm/third_party/botan/src/lib/prov/pkcs11/p11_slot.cpp b/comm/third_party/botan/src/lib/prov/pkcs11/p11_slot.cpp
new file mode 100644
index 0000000000..5c6ce91ad4
--- /dev/null
+++ b/comm/third_party/botan/src/lib/prov/pkcs11/p11_slot.cpp
@@ -0,0 +1,60 @@
+/*
+* PKCS#11 Slot
+* (C) 2016 Daniel Neus, Sirrix AG
+* (C) 2016 Philipp Weber, Sirrix AG
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/p11_types.h>
+
+namespace Botan {
+
+namespace PKCS11 {
+
+Slot::Slot(Module& module, SlotId slot_id)
+ : m_module(module), m_slot_id(slot_id)
+ {}
+
+SlotInfo Slot::get_slot_info() const
+ {
+ SlotInfo slot_info = {};
+ m_module.get()->C_GetSlotInfo(m_slot_id, &slot_info);
+ return slot_info;
+ }
+
+std::vector<MechanismType> Slot::get_mechanism_list() const
+ {
+ std::vector<MechanismType> mechanism_list;
+ m_module.get()->C_GetMechanismList(m_slot_id, mechanism_list);
+ return mechanism_list;
+ }
+
+MechanismInfo Slot::get_mechanism_info(MechanismType mechanism_type) const
+ {
+ MechanismInfo mechanism_info = {};
+ m_module.get()->C_GetMechanismInfo(m_slot_id, mechanism_type, &mechanism_info);
+ return mechanism_info;
+ }
+
+std::vector<SlotId> Slot::get_available_slots(Module& module, bool token_present)
+ {
+ std::vector<SlotId> slot_vec;
+ module->C_GetSlotList(token_present, slot_vec);
+ return slot_vec;
+ }
+
+TokenInfo Slot::get_token_info() const
+ {
+ TokenInfo token_info;
+ m_module.get()->C_GetTokenInfo(m_slot_id, &token_info);
+ return token_info;
+ }
+
+void Slot::initialize(const std::string& label, const secure_string& so_pin) const
+ {
+ m_module.get()->C_InitToken(m_slot_id, so_pin, label);
+ }
+}
+
+}
diff --git a/comm/third_party/botan/src/lib/prov/pkcs11/p11_slot.h b/comm/third_party/botan/src/lib/prov/pkcs11/p11_slot.h
new file mode 100644
index 0000000000..0a33ed5f41
--- /dev/null
+++ b/comm/third_party/botan/src/lib/prov/pkcs11/p11_slot.h
@@ -0,0 +1,15 @@
+/*
+* PKCS#11 Slot
+* (C) 2016 Daniel Neus
+* (C) 2016 Philipp Weber
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_P11_SLOT_H_
+#define BOTAN_P11_SLOT_H_
+
+#include <botan/p11_types.h>
+BOTAN_DEPRECATED_HEADER(p11_slot.h)
+
+#endif
diff --git a/comm/third_party/botan/src/lib/prov/pkcs11/p11_types.h b/comm/third_party/botan/src/lib/prov/pkcs11/p11_types.h
new file mode 100644
index 0000000000..bd445da3ca
--- /dev/null
+++ b/comm/third_party/botan/src/lib/prov/pkcs11/p11_types.h
@@ -0,0 +1,209 @@
+/*
+* PKCS#11 Module/Slot/Session
+* (C) 2016 Daniel Neus, Sirrix AG
+* (C) 2016 Philipp Weber, Sirrix AG
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_P11_TYPES_H_
+#define BOTAN_P11_TYPES_H_
+
+#include <botan/p11.h>
+#include <string>
+#include <memory>
+#include <functional>
+#include <utility>
+
+namespace Botan {
+
+class Dynamically_Loaded_Library;
+
+namespace PKCS11 {
+
+/**
+* Loads the PKCS#11 shared library
+* Calls C_Initialize on load and C_Finalize on destruction
+*/
+class BOTAN_PUBLIC_API(2,0) Module final
+ {
+ public:
+ /**
+ * Loads the shared library and calls C_Initialize
+ * @param file_path the path to the PKCS#11 shared library
+ * @param init_args flags to use for `C_Initialize`
+ */
+ Module(const std::string& file_path, C_InitializeArgs init_args = { nullptr, nullptr, nullptr, nullptr, static_cast< CK_FLAGS >(Flag::OsLockingOk), nullptr });
+
+ Module(Module&& other);
+ Module& operator=(Module&& other) = delete;
+
+ // Dtor calls C_Finalize(). A copy could be deleted while the origin still exists
+ // Furthermore std::unique_ptr member -> not copyable
+ Module(const Module& other) = delete;
+ Module& operator=(const Module& other) = delete;
+
+ /// Calls C_Finalize()
+ ~Module() noexcept;
+
+ /**
+ * Reloads the module and reinitializes it
+ * @param init_args flags to use for `C_Initialize`
+ */
+ void reload(C_InitializeArgs init_args = { nullptr, nullptr, nullptr, nullptr, static_cast< CK_FLAGS >(Flag::OsLockingOk), nullptr });
+
+ inline LowLevel* operator->() const
+ {
+ return m_low_level.get();
+ }
+
+ /// @return general information about Cryptoki
+ inline Info get_info() const
+ {
+ Info info;
+ m_low_level->C_GetInfo(&info);
+ return info;
+ }
+
+ private:
+ const std::string m_file_path;
+ FunctionListPtr m_func_list = nullptr;
+ std::unique_ptr<Dynamically_Loaded_Library> m_library;
+ std::unique_ptr<LowLevel> m_low_level = nullptr;
+ };
+
+/// Represents a PKCS#11 Slot, i.e., a card reader
+class BOTAN_PUBLIC_API(2,0) Slot final
+ {
+ public:
+ /**
+ * @param module the PKCS#11 module to use
+ * @param slot_id the slot id to use
+ */
+ Slot(Module& module, SlotId slot_id);
+
+ /// @return a reference to the module that is used
+ inline Module& module() const
+ {
+ return m_module;
+ }
+
+ /// @return the slot id
+ inline SlotId slot_id() const
+ {
+ return m_slot_id;
+ }
+
+ /**
+ * Get available slots
+ * @param module the module to use
+ * @param token_present true if only slots with attached tokens should be returned, false for all slots
+ * @return a list of available slots (calls C_GetSlotList)
+ */
+ static std::vector<SlotId> get_available_slots(Module& module, bool token_present);
+
+ /// @return information about the slot (`C_GetSlotInfo`)
+ SlotInfo get_slot_info() const;
+
+ /// Obtains a list of mechanism types supported by the slot (`C_GetMechanismList`)
+ std::vector<MechanismType> get_mechanism_list() const;
+
+ /// Obtains information about a particular mechanism possibly supported by a slot (`C_GetMechanismInfo`)
+ MechanismInfo get_mechanism_info(MechanismType mechanism_type) const;
+
+ /// Obtains information about a particular token in the system (`C_GetTokenInfo`)
+ TokenInfo get_token_info() const;
+
+ /**
+ * Calls `C_InitToken` to initialize the token
+ * @param label the label for the token (must not exceed 32 bytes according to PKCS#11)
+ * @param so_pin the PIN of the security officer
+ */
+ void initialize(const std::string& label, const secure_string& so_pin) const;
+
+ private:
+ const std::reference_wrapper<Module> m_module;
+ const SlotId m_slot_id;
+ };
+
+/// Represents a PKCS#11 session
+class BOTAN_PUBLIC_API(2,0) Session final
+ {
+ public:
+ /**
+ * @param slot the slot to use
+ * @param read_only true if the session should be read only, false to create a read-write session
+ */
+ Session(Slot& slot, bool read_only);
+
+ /**
+ * @param slot the slot to use
+ * @param flags the flags to use for the session. Remark: Flag::SerialSession is mandatory
+ * @param callback_data application-defined pointer to be passed to the notification callback
+ * @param notify_callback address of the notification callback function
+ */
+ Session(Slot& slot, Flags flags, VoidPtr callback_data, Notify notify_callback);
+
+ /// Takes ownership of a session
+ Session(Slot& slot, SessionHandle handle);
+
+ Session(Session&& other) = default;
+ Session& operator=(Session&& other) = delete;
+
+ // Dtor calls C_CloseSession() and eventually C_Logout. A copy could close the session while the origin still exists
+ Session(const Session& other) = delete;
+ Session& operator=(const Session& other) = delete;
+
+ /// Logout user and close the session on destruction
+ ~Session() noexcept;
+
+ /// @return a reference to the slot
+ inline const Slot& slot() const
+ {
+ return m_slot;
+ }
+
+ /// @return the session handle of this session
+ inline SessionHandle handle() const
+ {
+ return m_handle;
+ }
+
+ /// @return a reference to the used module
+ inline Module& module() const
+ {
+ return m_slot.module();
+ }
+
+ /// @return the released session handle
+ SessionHandle release();
+
+ /**
+ * Login to this session
+ * @param userType the user type to use for the login
+ * @param pin the PIN of the user
+ */
+ void login(UserType userType, const secure_string& pin);
+
+ /// Logout from this session
+ void logoff();
+
+ /// @return information about this session
+ SessionInfo get_info() const;
+
+ /// Calls `C_SetPIN` to change the PIN using the old PIN (requires a logged in session)
+ void set_pin(const secure_string& old_pin, const secure_string& new_pin) const;
+
+ /// Calls `C_InitPIN` to change or initialize the PIN using the SO_PIN (requires a logged in session)
+ void init_pin(const secure_string& new_pin);
+
+ private:
+ const Slot& m_slot;
+ SessionHandle m_handle;
+ bool m_logged_in;
+ };
+
+}
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/prov/pkcs11/p11_x509.cpp b/comm/third_party/botan/src/lib/prov/pkcs11/p11_x509.cpp
new file mode 100644
index 0000000000..5c6accdf08
--- /dev/null
+++ b/comm/third_party/botan/src/lib/prov/pkcs11/p11_x509.cpp
@@ -0,0 +1,37 @@
+/*
+* PKCS#11 X.509
+* (C) 2016 Daniel Neus, Sirrix AG
+* (C) 2016 Philipp Weber, Sirrix AG
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/p11_x509.h>
+
+#if defined(BOTAN_HAS_X509_CERTIFICATES)
+
+namespace Botan {
+namespace PKCS11 {
+
+X509_CertificateProperties::X509_CertificateProperties(const std::vector<uint8_t>& subject, const std::vector<uint8_t>& value)
+ : CertificateProperties(CertificateType::X509), m_subject(subject), m_value(value)
+ {
+ add_binary(AttributeType::Subject, m_subject);
+ add_binary(AttributeType::Value, m_value);
+ }
+
+PKCS11_X509_Certificate::PKCS11_X509_Certificate(Session& session, ObjectHandle handle)
+ : Object(session, handle), X509_Certificate(unlock(get_attribute_value(AttributeType::Value)))
+ {
+ }
+
+PKCS11_X509_Certificate::PKCS11_X509_Certificate(Session& session, const X509_CertificateProperties& props)
+ : Object(session, props), X509_Certificate(props.value())
+ {
+ }
+
+}
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/prov/pkcs11/p11_x509.h b/comm/third_party/botan/src/lib/prov/pkcs11/p11_x509.h
new file mode 100644
index 0000000000..d3eafbe352
--- /dev/null
+++ b/comm/third_party/botan/src/lib/prov/pkcs11/p11_x509.h
@@ -0,0 +1,117 @@
+/*
+* PKCS#11 X.509
+* (C) 2016 Daniel Neus, Sirrix AG
+* (C) 2016 Philipp Weber, Sirrix AG
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_P11_X509_H_
+#define BOTAN_P11_X509_H_
+
+#include <botan/p11_object.h>
+
+#if defined(BOTAN_HAS_X509_CERTIFICATES)
+
+#include <botan/x509cert.h>
+#include <vector>
+
+namespace Botan {
+namespace PKCS11 {
+
+class Session;
+
+/// Common attributes of all PKCS#11 X509 certificates
+class BOTAN_PUBLIC_API(2,0) X509_CertificateProperties final : public CertificateProperties
+ {
+ public:
+ /**
+ * @param subject DER-encoding of the certificate subject name
+ * @param value BER-encoding of the certificate
+ */
+ X509_CertificateProperties(const std::vector<uint8_t>& subject, const std::vector<uint8_t>& value);
+
+ X509_CertificateProperties(const X509_Certificate& cert) :
+ X509_CertificateProperties(cert.raw_subject_dn(), cert.BER_encode())
+ {}
+
+ /// @param id key identifier for public/private key pair
+ inline void set_id(const std::vector<uint8_t>& id)
+ {
+ add_binary(AttributeType::Id, id);
+ }
+
+ /// @param issuer DER-encoding of the certificate issuer name
+ inline void set_issuer(const std::vector<uint8_t>& issuer)
+ {
+ add_binary(AttributeType::Issuer, issuer);
+ }
+
+ /// @param serial DER-encoding of the certificate serial number
+ inline void set_serial(const std::vector<uint8_t>& serial)
+ {
+ add_binary(AttributeType::SerialNumber, serial);
+ }
+
+ /// @param hash hash value of the subject public key
+ inline void set_subject_pubkey_hash(const std::vector<uint8_t>& hash)
+ {
+ add_binary(AttributeType::HashOfSubjectPublicKey, hash);
+ }
+
+ /// @param hash hash value of the issuer public key
+ inline void set_issuer_pubkey_hash(const std::vector<uint8_t>& hash)
+ {
+ add_binary(AttributeType::HashOfIssuerPublicKey, hash);
+ }
+
+ /// @param alg defines the mechanism used to calculate `CKA_HASH_OF_SUBJECT_PUBLIC_KEY` and `CKA_HASH_OF_ISSUER_PUBLIC_KEY`
+ inline void set_hash_alg(MechanismType alg)
+ {
+ add_numeric(AttributeType::NameHashAlgorithm, static_cast<Ulong>(alg));
+ }
+
+ /// @return the subject
+ inline const std::vector<uint8_t>& subject() const
+ {
+ return m_subject;
+ }
+
+ /// @return the BER-encoding of the certificate
+ inline const std::vector<uint8_t>& value() const
+ {
+ return m_value;
+ }
+
+ private:
+ const std::vector<uint8_t> m_subject;
+ const std::vector<uint8_t> m_value;
+ };
+
+/// Represents a PKCS#11 X509 certificate
+class BOTAN_PUBLIC_API(2,0) PKCS11_X509_Certificate final : public Object, public X509_Certificate
+ {
+ public:
+ static const ObjectClass Class = ObjectClass::Certificate;
+
+ /**
+ * Create a PKCS11_X509_Certificate object from an existing PKCS#11 X509 cert
+ * @param session the session to use
+ * @param handle the handle of the X.509 certificate
+ */
+ PKCS11_X509_Certificate(Session& session, ObjectHandle handle);
+
+ /**
+ * Imports a X.509 certificate
+ * @param session the session to use
+ * @param props the attributes of the X.509 certificate
+ */
+ PKCS11_X509_Certificate(Session& session, const X509_CertificateProperties& props);
+ };
+
+}
+}
+
+#endif
+
+#endif
diff --git a/comm/third_party/botan/src/lib/prov/pkcs11/pkcs11.h b/comm/third_party/botan/src/lib/prov/pkcs11/pkcs11.h
new file mode 100644
index 0000000000..c66b0bca98
--- /dev/null
+++ b/comm/third_party/botan/src/lib/prov/pkcs11/pkcs11.h
@@ -0,0 +1,264 @@
+/*
+ * PKCS #11 Cryptographic Token Interface Base Specification Version 2.40 Errata 01
+ * Committee Specification Draft 01 / Public Review Draft 01
+ * 09 December 2015
+ * Copyright (c) OASIS Open 2015. All Rights Reserved.
+ * Source: http://docs.oasis-open.org/pkcs11/pkcs11-base/v2.40/errata01/csprd01/include/pkcs11-v2.40/
+ * Latest version of the specification: http://docs.oasis-open.org/pkcs11/pkcs11-base/v2.40/pkcs11-base-v2.40.html
+ * https://www.oasis-open.org/policies-guidelines/ipr
+ */
+
+#ifndef _PKCS11_H_
+#define _PKCS11_H_ 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Before including this file (pkcs11.h) (or pkcs11t.h by
+ * itself), 5 platform-specific macros must be defined. These
+ * macros are described below, and typical definitions for them
+ * are also given. Be advised that these definitions can depend
+ * on both the platform and the compiler used (and possibly also
+ * on whether a Cryptoki library is linked statically or
+ * dynamically).
+ *
+ * In addition to defining these 5 macros, the packing convention
+ * for Cryptoki structures should be set. The Cryptoki
+ * convention on packing is that structures should be 1-byte
+ * aligned.
+ *
+ * If you're using Microsoft Developer Studio 5.0 to produce
+ * Win32 stuff, this might be done by using the following
+ * preprocessor directive before including pkcs11.h or pkcs11t.h:
+ *
+ * #pragma pack(push, cryptoki, 1)
+ *
+ * and using the following preprocessor directive after including
+ * pkcs11.h or pkcs11t.h:
+ *
+ * #pragma pack(pop, cryptoki)
+ *
+ * If you're using an earlier version of Microsoft Developer
+ * Studio to produce Win16 stuff, this might be done by using
+ * the following preprocessor directive before including
+ * pkcs11.h or pkcs11t.h:
+ *
+ * #pragma pack(1)
+ *
+ * In a UNIX environment, you're on your own for this. You might
+ * not need to do (or be able to do!) anything.
+ *
+ *
+ * Now for the macros:
+ *
+ *
+ * 1. CK_PTR: The indirection string for making a pointer to an
+ * object. It can be used like this:
+ *
+ * typedef CK_BYTE CK_PTR CK_BYTE_PTR;
+ *
+ * If you're using Microsoft Developer Studio 5.0 to produce
+ * Win32 stuff, it might be defined by:
+ *
+ * #define CK_PTR *
+ *
+ * If you're using an earlier version of Microsoft Developer
+ * Studio to produce Win16 stuff, it might be defined by:
+ *
+ * #define CK_PTR far *
+ *
+ * In a typical UNIX environment, it might be defined by:
+ *
+ * #define CK_PTR *
+ *
+ *
+ * 2. CK_DECLARE_FUNCTION(returnType, name): A macro which makes
+ * an importable Cryptoki library function declaration out of a
+ * return type and a function name. It should be used in the
+ * following fashion:
+ *
+ * extern CK_DECLARE_FUNCTION(CK_RV, C_Initialize)(
+ * CK_VOID_PTR pReserved
+ * );
+ *
+ * If you're using Microsoft Developer Studio 5.0 to declare a
+ * function in a Win32 Cryptoki .dll, it might be defined by:
+ *
+ * #define CK_DECLARE_FUNCTION(returnType, name) \
+ * returnType __declspec(dllimport) name
+ *
+ * If you're using an earlier version of Microsoft Developer
+ * Studio to declare a function in a Win16 Cryptoki .dll, it
+ * might be defined by:
+ *
+ * #define CK_DECLARE_FUNCTION(returnType, name) \
+ * returnType __export _far _pascal name
+ *
+ * In a UNIX environment, it might be defined by:
+ *
+ * #define CK_DECLARE_FUNCTION(returnType, name) \
+ * returnType name
+ *
+ *
+ * 3. CK_DECLARE_FUNCTION_POINTER(returnType, name): A macro
+ * which makes a Cryptoki API function pointer declaration or
+ * function pointer type declaration out of a return type and a
+ * function name. It should be used in the following fashion:
+ *
+ * // Define funcPtr to be a pointer to a Cryptoki API function
+ * // taking arguments args and returning CK_RV.
+ * CK_DECLARE_FUNCTION_POINTER(CK_RV, funcPtr)(args);
+ *
+ * or
+ *
+ * // Define funcPtrType to be the type of a pointer to a
+ * // Cryptoki API function taking arguments args and returning
+ * // CK_RV, and then define funcPtr to be a variable of type
+ * // funcPtrType.
+ * typedef CK_DECLARE_FUNCTION_POINTER(CK_RV, funcPtrType)(args);
+ * funcPtrType funcPtr;
+ *
+ * If you're using Microsoft Developer Studio 5.0 to access
+ * functions in a Win32 Cryptoki .dll, in might be defined by:
+ *
+ * #define CK_DECLARE_FUNCTION_POINTER(returnType, name) \
+ * returnType __declspec(dllimport) (* name)
+ *
+ * If you're using an earlier version of Microsoft Developer
+ * Studio to access functions in a Win16 Cryptoki .dll, it might
+ * be defined by:
+ *
+ * #define CK_DECLARE_FUNCTION_POINTER(returnType, name) \
+ * returnType __export _far _pascal (* name)
+ *
+ * In a UNIX environment, it might be defined by:
+ *
+ * #define CK_DECLARE_FUNCTION_POINTER(returnType, name) \
+ * returnType (* name)
+ *
+ *
+ * 4. CK_CALLBACK_FUNCTION(returnType, name): A macro which makes
+ * a function pointer type for an application callback out of
+ * a return type for the callback and a name for the callback.
+ * It should be used in the following fashion:
+ *
+ * CK_CALLBACK_FUNCTION(CK_RV, myCallback)(args);
+ *
+ * to declare a function pointer, myCallback, to a callback
+ * which takes arguments args and returns a CK_RV. It can also
+ * be used like this:
+ *
+ * typedef CK_CALLBACK_FUNCTION(CK_RV, myCallbackType)(args);
+ * myCallbackType myCallback;
+ *
+ * If you're using Microsoft Developer Studio 5.0 to do Win32
+ * Cryptoki development, it might be defined by:
+ *
+ * #define CK_CALLBACK_FUNCTION(returnType, name) \
+ * returnType (* name)
+ *
+ * If you're using an earlier version of Microsoft Developer
+ * Studio to do Win16 development, it might be defined by:
+ *
+ * #define CK_CALLBACK_FUNCTION(returnType, name) \
+ * returnType _far _pascal (* name)
+ *
+ * In a UNIX environment, it might be defined by:
+ *
+ * #define CK_CALLBACK_FUNCTION(returnType, name) \
+ * returnType (* name)
+ *
+ *
+ * 5. NULL_PTR: This macro is the value of a NULL pointer.
+ *
+ * In any ANSI/ISO C environment (and in many others as well),
+ * this should best be defined by
+ *
+ * #ifndef NULL_PTR
+ * #define NULL_PTR 0
+ * #endif
+ */
+
+
+/* All the various Cryptoki types and #define'd values are in the
+ * file pkcs11t.h.
+ */
+#include "pkcs11t.h"
+
+#define __PASTE(x,y) x##y
+
+
+/* ==============================================================
+ * Define the "extern" form of all the entry points.
+ * ==============================================================
+ */
+
+#define CK_NEED_ARG_LIST 1
+#define CK_PKCS11_FUNCTION_INFO(name) \
+ extern CK_DECLARE_FUNCTION(CK_RV, name)
+
+/* pkcs11f.h has all the information about the Cryptoki
+ * function prototypes.
+ */
+#include "pkcs11f.h"
+
+#undef CK_NEED_ARG_LIST
+#undef CK_PKCS11_FUNCTION_INFO
+
+
+/* ==============================================================
+ * Define the typedef form of all the entry points. That is, for
+ * each Cryptoki function C_XXX, define a type CK_C_XXX which is
+ * a pointer to that kind of function.
+ * ==============================================================
+ */
+
+#define CK_NEED_ARG_LIST 1
+#define CK_PKCS11_FUNCTION_INFO(name) \
+ typedef CK_DECLARE_FUNCTION_POINTER(CK_RV, __PASTE(CK_,name))
+
+/* pkcs11f.h has all the information about the Cryptoki
+ * function prototypes.
+ */
+#include "pkcs11f.h"
+
+#undef CK_NEED_ARG_LIST
+#undef CK_PKCS11_FUNCTION_INFO
+
+
+/* ==============================================================
+ * Define structed vector of entry points. A CK_FUNCTION_LIST
+ * contains a CK_VERSION indicating a library's Cryptoki version
+ * and then a whole slew of function pointers to the routines in
+ * the library. This type was declared, but not defined, in
+ * pkcs11t.h.
+ * ==============================================================
+ */
+
+#define CK_PKCS11_FUNCTION_INFO(name) \
+ __PASTE(CK_,name) name;
+
+struct CK_FUNCTION_LIST {
+
+ CK_VERSION version; /* Cryptoki version */
+
+/* Pile all the function pointers into the CK_FUNCTION_LIST. */
+/* pkcs11f.h has all the information about the Cryptoki
+ * function prototypes.
+ */
+#include "pkcs11f.h"
+
+};
+
+#undef CK_PKCS11_FUNCTION_INFO
+
+
+#undef __PASTE
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _PKCS11_H_ */
+
diff --git a/comm/third_party/botan/src/lib/prov/pkcs11/pkcs11f.h b/comm/third_party/botan/src/lib/prov/pkcs11/pkcs11f.h
new file mode 100644
index 0000000000..48ba5726f0
--- /dev/null
+++ b/comm/third_party/botan/src/lib/prov/pkcs11/pkcs11f.h
@@ -0,0 +1,938 @@
+/*
+ * PKCS #11 Cryptographic Token Interface Base Specification Version 2.40 Errata 01
+ * Committee Specification Draft 01 / Public Review Draft 01
+ * 09 December 2015
+ * Copyright (c) OASIS Open 2015. All Rights Reserved.
+ * Source: http://docs.oasis-open.org/pkcs11/pkcs11-base/v2.40/errata01/csprd01/include/pkcs11-v2.40/
+ * Latest version of the specification: http://docs.oasis-open.org/pkcs11/pkcs11-base/v2.40/pkcs11-base-v2.40.html
+ * https://www.oasis-open.org/policies-guidelines/ipr
+ */
+
+/* This header file contains pretty much everything about all the
+ * Cryptoki function prototypes. Because this information is
+ * used for more than just declaring function prototypes, the
+ * order of the functions appearing herein is important, and
+ * should not be altered.
+ */
+
+/* General-purpose */
+
+/* C_Initialize initializes the Cryptoki library. */
+CK_PKCS11_FUNCTION_INFO(C_Initialize)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_VOID_PTR pInitArgs /* if this is not NULL_PTR, it gets
+ * cast to CK_C_INITIALIZE_ARGS_PTR
+ * and dereferenced
+ */
+);
+#endif
+
+
+/* C_Finalize indicates that an application is done with the
+ * Cryptoki library.
+ */
+CK_PKCS11_FUNCTION_INFO(C_Finalize)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_VOID_PTR pReserved /* reserved. Should be NULL_PTR */
+);
+#endif
+
+
+/* C_GetInfo returns general information about Cryptoki. */
+CK_PKCS11_FUNCTION_INFO(C_GetInfo)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_INFO_PTR pInfo /* location that receives information */
+);
+#endif
+
+
+/* C_GetFunctionList returns the function list. */
+CK_PKCS11_FUNCTION_INFO(C_GetFunctionList)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_FUNCTION_LIST_PTR_PTR ppFunctionList /* receives pointer to
+ * function list
+ */
+);
+#endif
+
+
+
+/* Slot and token management */
+
+/* C_GetSlotList obtains a list of slots in the system. */
+CK_PKCS11_FUNCTION_INFO(C_GetSlotList)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_BBOOL tokenPresent, /* only slots with tokens */
+ CK_SLOT_ID_PTR pSlotList, /* receives array of slot IDs */
+ CK_ULONG_PTR pulCount /* receives number of slots */
+);
+#endif
+
+
+/* C_GetSlotInfo obtains information about a particular slot in
+ * the system.
+ */
+CK_PKCS11_FUNCTION_INFO(C_GetSlotInfo)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SLOT_ID slotID, /* the ID of the slot */
+ CK_SLOT_INFO_PTR pInfo /* receives the slot information */
+);
+#endif
+
+
+/* C_GetTokenInfo obtains information about a particular token
+ * in the system.
+ */
+CK_PKCS11_FUNCTION_INFO(C_GetTokenInfo)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SLOT_ID slotID, /* ID of the token's slot */
+ CK_TOKEN_INFO_PTR pInfo /* receives the token information */
+);
+#endif
+
+
+/* C_GetMechanismList obtains a list of mechanism types
+ * supported by a token.
+ */
+CK_PKCS11_FUNCTION_INFO(C_GetMechanismList)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SLOT_ID slotID, /* ID of token's slot */
+ CK_MECHANISM_TYPE_PTR pMechanismList, /* gets mech. array */
+ CK_ULONG_PTR pulCount /* gets # of mechs. */
+);
+#endif
+
+
+/* C_GetMechanismInfo obtains information about a particular
+ * mechanism possibly supported by a token.
+ */
+CK_PKCS11_FUNCTION_INFO(C_GetMechanismInfo)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SLOT_ID slotID, /* ID of the token's slot */
+ CK_MECHANISM_TYPE type, /* type of mechanism */
+ CK_MECHANISM_INFO_PTR pInfo /* receives mechanism info */
+);
+#endif
+
+
+/* C_InitToken initializes a token. */
+CK_PKCS11_FUNCTION_INFO(C_InitToken)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SLOT_ID slotID, /* ID of the token's slot */
+ CK_UTF8CHAR_PTR pPin, /* the SO's initial PIN */
+ CK_ULONG ulPinLen, /* length in bytes of the PIN */
+ CK_UTF8CHAR_PTR pLabel /* 32-byte token label (blank padded) */
+);
+#endif
+
+
+/* C_InitPIN initializes the normal user's PIN. */
+CK_PKCS11_FUNCTION_INFO(C_InitPIN)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_UTF8CHAR_PTR pPin, /* the normal user's PIN */
+ CK_ULONG ulPinLen /* length in bytes of the PIN */
+);
+#endif
+
+
+/* C_SetPIN modifies the PIN of the user who is logged in. */
+CK_PKCS11_FUNCTION_INFO(C_SetPIN)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_UTF8CHAR_PTR pOldPin, /* the old PIN */
+ CK_ULONG ulOldLen, /* length of the old PIN */
+ CK_UTF8CHAR_PTR pNewPin, /* the new PIN */
+ CK_ULONG ulNewLen /* length of the new PIN */
+);
+#endif
+
+
+
+/* Session management */
+
+/* C_OpenSession opens a session between an application and a
+ * token.
+ */
+CK_PKCS11_FUNCTION_INFO(C_OpenSession)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SLOT_ID slotID, /* the slot's ID */
+ CK_FLAGS flags, /* from CK_SESSION_INFO */
+ CK_VOID_PTR pApplication, /* passed to callback */
+ CK_NOTIFY Notify, /* callback function */
+ CK_SESSION_HANDLE_PTR phSession /* gets session handle */
+);
+#endif
+
+
+/* C_CloseSession closes a session between an application and a
+ * token.
+ */
+CK_PKCS11_FUNCTION_INFO(C_CloseSession)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession /* the session's handle */
+);
+#endif
+
+
+/* C_CloseAllSessions closes all sessions with a token. */
+CK_PKCS11_FUNCTION_INFO(C_CloseAllSessions)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SLOT_ID slotID /* the token's slot */
+);
+#endif
+
+
+/* C_GetSessionInfo obtains information about the session. */
+CK_PKCS11_FUNCTION_INFO(C_GetSessionInfo)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_SESSION_INFO_PTR pInfo /* receives session info */
+);
+#endif
+
+
+/* C_GetOperationState obtains the state of the cryptographic operation
+ * in a session.
+ */
+CK_PKCS11_FUNCTION_INFO(C_GetOperationState)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* session's handle */
+ CK_BYTE_PTR pOperationState, /* gets state */
+ CK_ULONG_PTR pulOperationStateLen /* gets state length */
+);
+#endif
+
+
+/* C_SetOperationState restores the state of the cryptographic
+ * operation in a session.
+ */
+CK_PKCS11_FUNCTION_INFO(C_SetOperationState)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* session's handle */
+ CK_BYTE_PTR pOperationState, /* holds state */
+ CK_ULONG ulOperationStateLen, /* holds state length */
+ CK_OBJECT_HANDLE hEncryptionKey, /* en/decryption key */
+ CK_OBJECT_HANDLE hAuthenticationKey /* sign/verify key */
+);
+#endif
+
+
+/* C_Login logs a user into a token. */
+CK_PKCS11_FUNCTION_INFO(C_Login)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_USER_TYPE userType, /* the user type */
+ CK_UTF8CHAR_PTR pPin, /* the user's PIN */
+ CK_ULONG ulPinLen /* the length of the PIN */
+);
+#endif
+
+
+/* C_Logout logs a user out from a token. */
+CK_PKCS11_FUNCTION_INFO(C_Logout)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession /* the session's handle */
+);
+#endif
+
+
+
+/* Object management */
+
+/* C_CreateObject creates a new object. */
+CK_PKCS11_FUNCTION_INFO(C_CreateObject)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_ATTRIBUTE_PTR pTemplate, /* the object's template */
+ CK_ULONG ulCount, /* attributes in template */
+ CK_OBJECT_HANDLE_PTR phObject /* gets new object's handle. */
+);
+#endif
+
+
+/* C_CopyObject copies an object, creating a new object for the
+ * copy.
+ */
+CK_PKCS11_FUNCTION_INFO(C_CopyObject)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_OBJECT_HANDLE hObject, /* the object's handle */
+ CK_ATTRIBUTE_PTR pTemplate, /* template for new object */
+ CK_ULONG ulCount, /* attributes in template */
+ CK_OBJECT_HANDLE_PTR phNewObject /* receives handle of copy */
+);
+#endif
+
+
+/* C_DestroyObject destroys an object. */
+CK_PKCS11_FUNCTION_INFO(C_DestroyObject)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_OBJECT_HANDLE hObject /* the object's handle */
+);
+#endif
+
+
+/* C_GetObjectSize gets the size of an object in bytes. */
+CK_PKCS11_FUNCTION_INFO(C_GetObjectSize)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_OBJECT_HANDLE hObject, /* the object's handle */
+ CK_ULONG_PTR pulSize /* receives size of object */
+);
+#endif
+
+
+/* C_GetAttributeValue obtains the value of one or more object
+ * attributes.
+ */
+CK_PKCS11_FUNCTION_INFO(C_GetAttributeValue)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_OBJECT_HANDLE hObject, /* the object's handle */
+ CK_ATTRIBUTE_PTR pTemplate, /* specifies attrs; gets vals */
+ CK_ULONG ulCount /* attributes in template */
+);
+#endif
+
+
+/* C_SetAttributeValue modifies the value of one or more object
+ * attributes.
+ */
+CK_PKCS11_FUNCTION_INFO(C_SetAttributeValue)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_OBJECT_HANDLE hObject, /* the object's handle */
+ CK_ATTRIBUTE_PTR pTemplate, /* specifies attrs and values */
+ CK_ULONG ulCount /* attributes in template */
+);
+#endif
+
+
+/* C_FindObjectsInit initializes a search for token and session
+ * objects that match a template.
+ */
+CK_PKCS11_FUNCTION_INFO(C_FindObjectsInit)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_ATTRIBUTE_PTR pTemplate, /* attribute values to match */
+ CK_ULONG ulCount /* attrs in search template */
+);
+#endif
+
+
+/* C_FindObjects continues a search for token and session
+ * objects that match a template, obtaining additional object
+ * handles.
+ */
+CK_PKCS11_FUNCTION_INFO(C_FindObjects)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* session's handle */
+ CK_OBJECT_HANDLE_PTR phObject, /* gets obj. handles */
+ CK_ULONG ulMaxObjectCount, /* max handles to get */
+ CK_ULONG_PTR pulObjectCount /* actual # returned */
+);
+#endif
+
+
+/* C_FindObjectsFinal finishes a search for token and session
+ * objects.
+ */
+CK_PKCS11_FUNCTION_INFO(C_FindObjectsFinal)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession /* the session's handle */
+);
+#endif
+
+
+
+/* Encryption and decryption */
+
+/* C_EncryptInit initializes an encryption operation. */
+CK_PKCS11_FUNCTION_INFO(C_EncryptInit)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_MECHANISM_PTR pMechanism, /* the encryption mechanism */
+ CK_OBJECT_HANDLE hKey /* handle of encryption key */
+);
+#endif
+
+
+/* C_Encrypt encrypts single-part data. */
+CK_PKCS11_FUNCTION_INFO(C_Encrypt)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* session's handle */
+ CK_BYTE_PTR pData, /* the plaintext data */
+ CK_ULONG ulDataLen, /* bytes of plaintext */
+ CK_BYTE_PTR pEncryptedData, /* gets ciphertext */
+ CK_ULONG_PTR pulEncryptedDataLen /* gets c-text size */
+);
+#endif
+
+
+/* C_EncryptUpdate continues a multiple-part encryption
+ * operation.
+ */
+CK_PKCS11_FUNCTION_INFO(C_EncryptUpdate)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* session's handle */
+ CK_BYTE_PTR pPart, /* the plaintext data */
+ CK_ULONG ulPartLen, /* plaintext data len */
+ CK_BYTE_PTR pEncryptedPart, /* gets ciphertext */
+ CK_ULONG_PTR pulEncryptedPartLen /* gets c-text size */
+);
+#endif
+
+
+/* C_EncryptFinal finishes a multiple-part encryption
+ * operation.
+ */
+CK_PKCS11_FUNCTION_INFO(C_EncryptFinal)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* session handle */
+ CK_BYTE_PTR pLastEncryptedPart, /* last c-text */
+ CK_ULONG_PTR pulLastEncryptedPartLen /* gets last size */
+);
+#endif
+
+
+/* C_DecryptInit initializes a decryption operation. */
+CK_PKCS11_FUNCTION_INFO(C_DecryptInit)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_MECHANISM_PTR pMechanism, /* the decryption mechanism */
+ CK_OBJECT_HANDLE hKey /* handle of decryption key */
+);
+#endif
+
+
+/* C_Decrypt decrypts encrypted data in a single part. */
+CK_PKCS11_FUNCTION_INFO(C_Decrypt)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* session's handle */
+ CK_BYTE_PTR pEncryptedData, /* ciphertext */
+ CK_ULONG ulEncryptedDataLen, /* ciphertext length */
+ CK_BYTE_PTR pData, /* gets plaintext */
+ CK_ULONG_PTR pulDataLen /* gets p-text size */
+);
+#endif
+
+
+/* C_DecryptUpdate continues a multiple-part decryption
+ * operation.
+ */
+CK_PKCS11_FUNCTION_INFO(C_DecryptUpdate)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* session's handle */
+ CK_BYTE_PTR pEncryptedPart, /* encrypted data */
+ CK_ULONG ulEncryptedPartLen, /* input length */
+ CK_BYTE_PTR pPart, /* gets plaintext */
+ CK_ULONG_PTR pulPartLen /* p-text size */
+);
+#endif
+
+
+/* C_DecryptFinal finishes a multiple-part decryption
+ * operation.
+ */
+CK_PKCS11_FUNCTION_INFO(C_DecryptFinal)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_BYTE_PTR pLastPart, /* gets plaintext */
+ CK_ULONG_PTR pulLastPartLen /* p-text size */
+);
+#endif
+
+
+
+/* Message digesting */
+
+/* C_DigestInit initializes a message-digesting operation. */
+CK_PKCS11_FUNCTION_INFO(C_DigestInit)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_MECHANISM_PTR pMechanism /* the digesting mechanism */
+);
+#endif
+
+
+/* C_Digest digests data in a single part. */
+CK_PKCS11_FUNCTION_INFO(C_Digest)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_BYTE_PTR pData, /* data to be digested */
+ CK_ULONG ulDataLen, /* bytes of data to digest */
+ CK_BYTE_PTR pDigest, /* gets the message digest */
+ CK_ULONG_PTR pulDigestLen /* gets digest length */
+);
+#endif
+
+
+/* C_DigestUpdate continues a multiple-part message-digesting
+ * operation.
+ */
+CK_PKCS11_FUNCTION_INFO(C_DigestUpdate)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_BYTE_PTR pPart, /* data to be digested */
+ CK_ULONG ulPartLen /* bytes of data to be digested */
+);
+#endif
+
+
+/* C_DigestKey continues a multi-part message-digesting
+ * operation, by digesting the value of a secret key as part of
+ * the data already digested.
+ */
+CK_PKCS11_FUNCTION_INFO(C_DigestKey)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_OBJECT_HANDLE hKey /* secret key to digest */
+);
+#endif
+
+
+/* C_DigestFinal finishes a multiple-part message-digesting
+ * operation.
+ */
+CK_PKCS11_FUNCTION_INFO(C_DigestFinal)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_BYTE_PTR pDigest, /* gets the message digest */
+ CK_ULONG_PTR pulDigestLen /* gets byte count of digest */
+);
+#endif
+
+
+
+/* Signing and MACing */
+
+/* C_SignInit initializes a signature (private key encryption)
+ * operation, where the signature is (will be) an appendix to
+ * the data, and plaintext cannot be recovered from the
+ * signature.
+ */
+CK_PKCS11_FUNCTION_INFO(C_SignInit)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_MECHANISM_PTR pMechanism, /* the signature mechanism */
+ CK_OBJECT_HANDLE hKey /* handle of signature key */
+);
+#endif
+
+
+/* C_Sign signs (encrypts with private key) data in a single
+ * part, where the signature is (will be) an appendix to the
+ * data, and plaintext cannot be recovered from the signature.
+ */
+CK_PKCS11_FUNCTION_INFO(C_Sign)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_BYTE_PTR pData, /* the data to sign */
+ CK_ULONG ulDataLen, /* count of bytes to sign */
+ CK_BYTE_PTR pSignature, /* gets the signature */
+ CK_ULONG_PTR pulSignatureLen /* gets signature length */
+);
+#endif
+
+
+/* C_SignUpdate continues a multiple-part signature operation,
+ * where the signature is (will be) an appendix to the data,
+ * and plaintext cannot be recovered from the signature.
+ */
+CK_PKCS11_FUNCTION_INFO(C_SignUpdate)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_BYTE_PTR pPart, /* the data to sign */
+ CK_ULONG ulPartLen /* count of bytes to sign */
+);
+#endif
+
+
+/* C_SignFinal finishes a multiple-part signature operation,
+ * returning the signature.
+ */
+CK_PKCS11_FUNCTION_INFO(C_SignFinal)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_BYTE_PTR pSignature, /* gets the signature */
+ CK_ULONG_PTR pulSignatureLen /* gets signature length */
+);
+#endif
+
+
+/* C_SignRecoverInit initializes a signature operation, where
+ * the data can be recovered from the signature.
+ */
+CK_PKCS11_FUNCTION_INFO(C_SignRecoverInit)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_MECHANISM_PTR pMechanism, /* the signature mechanism */
+ CK_OBJECT_HANDLE hKey /* handle of the signature key */
+);
+#endif
+
+
+/* C_SignRecover signs data in a single operation, where the
+ * data can be recovered from the signature.
+ */
+CK_PKCS11_FUNCTION_INFO(C_SignRecover)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_BYTE_PTR pData, /* the data to sign */
+ CK_ULONG ulDataLen, /* count of bytes to sign */
+ CK_BYTE_PTR pSignature, /* gets the signature */
+ CK_ULONG_PTR pulSignatureLen /* gets signature length */
+);
+#endif
+
+
+
+/* Verifying signatures and MACs */
+
+/* C_VerifyInit initializes a verification operation, where the
+ * signature is an appendix to the data, and plaintext cannot
+ * cannot be recovered from the signature (e.g. DSA).
+ */
+CK_PKCS11_FUNCTION_INFO(C_VerifyInit)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_MECHANISM_PTR pMechanism, /* the verification mechanism */
+ CK_OBJECT_HANDLE hKey /* verification key */
+);
+#endif
+
+
+/* C_Verify verifies a signature in a single-part operation,
+ * where the signature is an appendix to the data, and plaintext
+ * cannot be recovered from the signature.
+ */
+CK_PKCS11_FUNCTION_INFO(C_Verify)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_BYTE_PTR pData, /* signed data */
+ CK_ULONG ulDataLen, /* length of signed data */
+ CK_BYTE_PTR pSignature, /* signature */
+ CK_ULONG ulSignatureLen /* signature length*/
+);
+#endif
+
+
+/* C_VerifyUpdate continues a multiple-part verification
+ * operation, where the signature is an appendix to the data,
+ * and plaintext cannot be recovered from the signature.
+ */
+CK_PKCS11_FUNCTION_INFO(C_VerifyUpdate)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_BYTE_PTR pPart, /* signed data */
+ CK_ULONG ulPartLen /* length of signed data */
+);
+#endif
+
+
+/* C_VerifyFinal finishes a multiple-part verification
+ * operation, checking the signature.
+ */
+CK_PKCS11_FUNCTION_INFO(C_VerifyFinal)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_BYTE_PTR pSignature, /* signature to verify */
+ CK_ULONG ulSignatureLen /* signature length */
+);
+#endif
+
+
+/* C_VerifyRecoverInit initializes a signature verification
+ * operation, where the data is recovered from the signature.
+ */
+CK_PKCS11_FUNCTION_INFO(C_VerifyRecoverInit)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_MECHANISM_PTR pMechanism, /* the verification mechanism */
+ CK_OBJECT_HANDLE hKey /* verification key */
+);
+#endif
+
+
+/* C_VerifyRecover verifies a signature in a single-part
+ * operation, where the data is recovered from the signature.
+ */
+CK_PKCS11_FUNCTION_INFO(C_VerifyRecover)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_BYTE_PTR pSignature, /* signature to verify */
+ CK_ULONG ulSignatureLen, /* signature length */
+ CK_BYTE_PTR pData, /* gets signed data */
+ CK_ULONG_PTR pulDataLen /* gets signed data len */
+);
+#endif
+
+
+
+/* Dual-function cryptographic operations */
+
+/* C_DigestEncryptUpdate continues a multiple-part digesting
+ * and encryption operation.
+ */
+CK_PKCS11_FUNCTION_INFO(C_DigestEncryptUpdate)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* session's handle */
+ CK_BYTE_PTR pPart, /* the plaintext data */
+ CK_ULONG ulPartLen, /* plaintext length */
+ CK_BYTE_PTR pEncryptedPart, /* gets ciphertext */
+ CK_ULONG_PTR pulEncryptedPartLen /* gets c-text length */
+);
+#endif
+
+
+/* C_DecryptDigestUpdate continues a multiple-part decryption and
+ * digesting operation.
+ */
+CK_PKCS11_FUNCTION_INFO(C_DecryptDigestUpdate)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* session's handle */
+ CK_BYTE_PTR pEncryptedPart, /* ciphertext */
+ CK_ULONG ulEncryptedPartLen, /* ciphertext length */
+ CK_BYTE_PTR pPart, /* gets plaintext */
+ CK_ULONG_PTR pulPartLen /* gets plaintext len */
+);
+#endif
+
+
+/* C_SignEncryptUpdate continues a multiple-part signing and
+ * encryption operation.
+ */
+CK_PKCS11_FUNCTION_INFO(C_SignEncryptUpdate)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* session's handle */
+ CK_BYTE_PTR pPart, /* the plaintext data */
+ CK_ULONG ulPartLen, /* plaintext length */
+ CK_BYTE_PTR pEncryptedPart, /* gets ciphertext */
+ CK_ULONG_PTR pulEncryptedPartLen /* gets c-text length */
+);
+#endif
+
+
+/* C_DecryptVerifyUpdate continues a multiple-part decryption and
+ * verify operation.
+ */
+CK_PKCS11_FUNCTION_INFO(C_DecryptVerifyUpdate)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* session's handle */
+ CK_BYTE_PTR pEncryptedPart, /* ciphertext */
+ CK_ULONG ulEncryptedPartLen, /* ciphertext length */
+ CK_BYTE_PTR pPart, /* gets plaintext */
+ CK_ULONG_PTR pulPartLen /* gets p-text length */
+);
+#endif
+
+
+
+/* Key management */
+
+/* C_GenerateKey generates a secret key, creating a new key
+ * object.
+ */
+CK_PKCS11_FUNCTION_INFO(C_GenerateKey)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_MECHANISM_PTR pMechanism, /* key generation mech. */
+ CK_ATTRIBUTE_PTR pTemplate, /* template for new key */
+ CK_ULONG ulCount, /* # of attrs in template */
+ CK_OBJECT_HANDLE_PTR phKey /* gets handle of new key */
+);
+#endif
+
+
+/* C_GenerateKeyPair generates a public-key/private-key pair,
+ * creating new key objects.
+ */
+CK_PKCS11_FUNCTION_INFO(C_GenerateKeyPair)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* session handle */
+ CK_MECHANISM_PTR pMechanism, /* key-gen mech. */
+ CK_ATTRIBUTE_PTR pPublicKeyTemplate, /* template for pub. key */
+ CK_ULONG ulPublicKeyAttributeCount, /* # pub. attrs. */
+ CK_ATTRIBUTE_PTR pPrivateKeyTemplate, /* template for priv. key */
+ CK_ULONG ulPrivateKeyAttributeCount, /* # priv. attrs. */
+ CK_OBJECT_HANDLE_PTR phPublicKey, /* gets pub. key handle */
+ CK_OBJECT_HANDLE_PTR phPrivateKey /* gets priv. key handle */
+);
+#endif
+
+
+/* C_WrapKey wraps (i.e., encrypts) a key. */
+CK_PKCS11_FUNCTION_INFO(C_WrapKey)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_MECHANISM_PTR pMechanism, /* the wrapping mechanism */
+ CK_OBJECT_HANDLE hWrappingKey, /* wrapping key */
+ CK_OBJECT_HANDLE hKey, /* key to be wrapped */
+ CK_BYTE_PTR pWrappedKey, /* gets wrapped key */
+ CK_ULONG_PTR pulWrappedKeyLen /* gets wrapped key size */
+);
+#endif
+
+
+/* C_UnwrapKey unwraps (decrypts) a wrapped key, creating a new
+ * key object.
+ */
+CK_PKCS11_FUNCTION_INFO(C_UnwrapKey)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* session's handle */
+ CK_MECHANISM_PTR pMechanism, /* unwrapping mech. */
+ CK_OBJECT_HANDLE hUnwrappingKey, /* unwrapping key */
+ CK_BYTE_PTR pWrappedKey, /* the wrapped key */
+ CK_ULONG ulWrappedKeyLen, /* wrapped key len */
+ CK_ATTRIBUTE_PTR pTemplate, /* new key template */
+ CK_ULONG ulAttributeCount, /* template length */
+ CK_OBJECT_HANDLE_PTR phKey /* gets new handle */
+);
+#endif
+
+
+/* C_DeriveKey derives a key from a base key, creating a new key
+ * object.
+ */
+CK_PKCS11_FUNCTION_INFO(C_DeriveKey)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* session's handle */
+ CK_MECHANISM_PTR pMechanism, /* key deriv. mech. */
+ CK_OBJECT_HANDLE hBaseKey, /* base key */
+ CK_ATTRIBUTE_PTR pTemplate, /* new key template */
+ CK_ULONG ulAttributeCount, /* template length */
+ CK_OBJECT_HANDLE_PTR phKey /* gets new handle */
+);
+#endif
+
+
+
+/* Random number generation */
+
+/* C_SeedRandom mixes additional seed material into the token's
+ * random number generator.
+ */
+CK_PKCS11_FUNCTION_INFO(C_SeedRandom)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_BYTE_PTR pSeed, /* the seed material */
+ CK_ULONG ulSeedLen /* length of seed material */
+);
+#endif
+
+
+/* C_GenerateRandom generates random data. */
+CK_PKCS11_FUNCTION_INFO(C_GenerateRandom)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_BYTE_PTR RandomData, /* receives the random data */
+ CK_ULONG ulRandomLen /* # of bytes to generate */
+);
+#endif
+
+
+
+/* Parallel function management */
+
+/* C_GetFunctionStatus is a legacy function; it obtains an
+ * updated status of a function running in parallel with an
+ * application.
+ */
+CK_PKCS11_FUNCTION_INFO(C_GetFunctionStatus)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession /* the session's handle */
+);
+#endif
+
+
+/* C_CancelFunction is a legacy function; it cancels a function
+ * running in parallel.
+ */
+CK_PKCS11_FUNCTION_INFO(C_CancelFunction)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession /* the session's handle */
+);
+#endif
+
+
+/* C_WaitForSlotEvent waits for a slot event (token insertion,
+ * removal, etc.) to occur.
+ */
+CK_PKCS11_FUNCTION_INFO(C_WaitForSlotEvent)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_FLAGS flags, /* blocking/nonblocking flag */
+ CK_SLOT_ID_PTR pSlot, /* location that receives the slot ID */
+ CK_VOID_PTR pRserved /* reserved. Should be NULL_PTR */
+);
+#endif
+
diff --git a/comm/third_party/botan/src/lib/prov/pkcs11/pkcs11t.h b/comm/third_party/botan/src/lib/prov/pkcs11/pkcs11t.h
new file mode 100644
index 0000000000..c183ea9741
--- /dev/null
+++ b/comm/third_party/botan/src/lib/prov/pkcs11/pkcs11t.h
@@ -0,0 +1,2002 @@
+/*
+ * PKCS #11 Cryptographic Token Interface Base Specification Version 2.40 Errata 01
+ * Committee Specification Draft 01 / Public Review Draft 01
+ * 09 December 2015
+ * Copyright (c) OASIS Open 2015. All Rights Reserved.
+ * Source: http://docs.oasis-open.org/pkcs11/pkcs11-base/v2.40/errata01/csprd01/include/pkcs11-v2.40/
+ * Latest version of the specification: http://docs.oasis-open.org/pkcs11/pkcs11-base/v2.40/pkcs11-base-v2.40.html
+ * https://www.oasis-open.org/policies-guidelines/ipr
+ */
+
+/* See top of pkcs11.h for information about the macros that
+ * must be defined and the structure-packing conventions that
+ * must be set before including this file.
+ */
+
+#ifndef _PKCS11T_H_
+#define _PKCS11T_H_ 1
+
+#define CRYPTOKI_VERSION_MAJOR 2
+#define CRYPTOKI_VERSION_MINOR 40
+#define CRYPTOKI_VERSION_AMENDMENT 0
+
+#define CK_TRUE 1
+#define CK_FALSE 0
+
+#ifndef CK_DISABLE_TRUE_FALSE
+#ifndef FALSE
+#define FALSE CK_FALSE
+#endif
+#ifndef TRUE
+#define TRUE CK_TRUE
+#endif
+#endif
+
+/* an unsigned 8-bit value */
+typedef unsigned char CK_BYTE;
+
+/* an unsigned 8-bit character */
+typedef CK_BYTE CK_CHAR;
+
+/* an 8-bit UTF-8 character */
+typedef CK_BYTE CK_UTF8CHAR;
+
+/* a BYTE-sized Boolean flag */
+typedef CK_BYTE CK_BBOOL;
+
+/* an unsigned value, at least 32 bits long */
+typedef unsigned long int CK_ULONG;
+
+/* a signed value, the same size as a CK_ULONG */
+typedef long int CK_LONG;
+
+/* at least 32 bits; each bit is a Boolean flag */
+typedef CK_ULONG CK_FLAGS;
+
+
+/* some special values for certain CK_ULONG variables */
+#define CK_UNAVAILABLE_INFORMATION (~0UL)
+#define CK_EFFECTIVELY_INFINITE 0UL
+
+
+typedef CK_BYTE CK_PTR CK_BYTE_PTR;
+typedef CK_CHAR CK_PTR CK_CHAR_PTR;
+typedef CK_UTF8CHAR CK_PTR CK_UTF8CHAR_PTR;
+typedef CK_ULONG CK_PTR CK_ULONG_PTR;
+typedef void CK_PTR CK_VOID_PTR;
+
+/* Pointer to a CK_VOID_PTR-- i.e., pointer to pointer to void */
+typedef CK_VOID_PTR CK_PTR CK_VOID_PTR_PTR;
+
+
+/* The following value is always invalid if used as a session
+ * handle or object handle
+ */
+#define CK_INVALID_HANDLE 0UL
+
+
+typedef struct CK_VERSION {
+ CK_BYTE major; /* integer portion of version number */
+ CK_BYTE minor; /* 1/100ths portion of version number */
+} CK_VERSION;
+
+typedef CK_VERSION CK_PTR CK_VERSION_PTR;
+
+
+typedef struct CK_INFO {
+ CK_VERSION cryptokiVersion; /* Cryptoki interface ver */
+ CK_UTF8CHAR manufacturerID[32]; /* blank padded */
+ CK_FLAGS flags; /* must be zero */
+ CK_UTF8CHAR libraryDescription[32]; /* blank padded */
+ CK_VERSION libraryVersion; /* version of library */
+} CK_INFO;
+
+typedef CK_INFO CK_PTR CK_INFO_PTR;
+
+
+/* CK_NOTIFICATION enumerates the types of notifications that
+ * Cryptoki provides to an application
+ */
+typedef CK_ULONG CK_NOTIFICATION;
+#define CKN_SURRENDER 0UL
+#define CKN_OTP_CHANGED 1UL
+
+typedef CK_ULONG CK_SLOT_ID;
+
+typedef CK_SLOT_ID CK_PTR CK_SLOT_ID_PTR;
+
+
+/* CK_SLOT_INFO provides information about a slot */
+typedef struct CK_SLOT_INFO {
+ CK_UTF8CHAR slotDescription[64]; /* blank padded */
+ CK_UTF8CHAR manufacturerID[32]; /* blank padded */
+ CK_FLAGS flags;
+
+ CK_VERSION hardwareVersion; /* version of hardware */
+ CK_VERSION firmwareVersion; /* version of firmware */
+} CK_SLOT_INFO;
+
+/* flags: bit flags that provide capabilities of the slot
+ * Bit Flag Mask Meaning
+ */
+#define CKF_TOKEN_PRESENT 0x00000001UL /* a token is there */
+#define CKF_REMOVABLE_DEVICE 0x00000002UL /* removable devices*/
+#define CKF_HW_SLOT 0x00000004UL /* hardware slot */
+
+typedef CK_SLOT_INFO CK_PTR CK_SLOT_INFO_PTR;
+
+
+/* CK_TOKEN_INFO provides information about a token */
+typedef struct CK_TOKEN_INFO {
+ CK_UTF8CHAR label[32]; /* blank padded */
+ CK_UTF8CHAR manufacturerID[32]; /* blank padded */
+ CK_UTF8CHAR model[16]; /* blank padded */
+ CK_CHAR serialNumber[16]; /* blank padded */
+ CK_FLAGS flags; /* see below */
+
+ CK_ULONG ulMaxSessionCount; /* max open sessions */
+ CK_ULONG ulSessionCount; /* sess. now open */
+ CK_ULONG ulMaxRwSessionCount; /* max R/W sessions */
+ CK_ULONG ulRwSessionCount; /* R/W sess. now open */
+ CK_ULONG ulMaxPinLen; /* in bytes */
+ CK_ULONG ulMinPinLen; /* in bytes */
+ CK_ULONG ulTotalPublicMemory; /* in bytes */
+ CK_ULONG ulFreePublicMemory; /* in bytes */
+ CK_ULONG ulTotalPrivateMemory; /* in bytes */
+ CK_ULONG ulFreePrivateMemory; /* in bytes */
+ CK_VERSION hardwareVersion; /* version of hardware */
+ CK_VERSION firmwareVersion; /* version of firmware */
+ CK_CHAR utcTime[16]; /* time */
+} CK_TOKEN_INFO;
+
+/* The flags parameter is defined as follows:
+ * Bit Flag Mask Meaning
+ */
+#define CKF_RNG 0x00000001UL /* has random # generator */
+#define CKF_WRITE_PROTECTED 0x00000002UL /* token is write-protected */
+#define CKF_LOGIN_REQUIRED 0x00000004UL /* user must login */
+#define CKF_USER_PIN_INITIALIZED 0x00000008UL /* normal user's PIN is set */
+
+/* CKF_RESTORE_KEY_NOT_NEEDED. If it is set,
+ * that means that *every* time the state of cryptographic
+ * operations of a session is successfully saved, all keys
+ * needed to continue those operations are stored in the state
+ */
+#define CKF_RESTORE_KEY_NOT_NEEDED 0x00000020UL
+
+/* CKF_CLOCK_ON_TOKEN. If it is set, that means
+ * that the token has some sort of clock. The time on that
+ * clock is returned in the token info structure
+ */
+#define CKF_CLOCK_ON_TOKEN 0x00000040UL
+
+/* CKF_PROTECTED_AUTHENTICATION_PATH. If it is
+ * set, that means that there is some way for the user to login
+ * without sending a PIN through the Cryptoki library itself
+ */
+#define CKF_PROTECTED_AUTHENTICATION_PATH 0x00000100UL
+
+/* CKF_DUAL_CRYPTO_OPERATIONS. If it is true,
+ * that means that a single session with the token can perform
+ * dual simultaneous cryptographic operations (digest and
+ * encrypt; decrypt and digest; sign and encrypt; and decrypt
+ * and sign)
+ */
+#define CKF_DUAL_CRYPTO_OPERATIONS 0x00000200UL
+
+/* CKF_TOKEN_INITIALIZED. If it is true, the
+ * token has been initialized using C_InitializeToken or an
+ * equivalent mechanism outside the scope of PKCS #11.
+ * Calling C_InitializeToken when this flag is set will cause
+ * the token to be reinitialized.
+ */
+#define CKF_TOKEN_INITIALIZED 0x00000400UL
+
+/* CKF_SECONDARY_AUTHENTICATION. If it is
+ * true, the token supports secondary authentication for
+ * private key objects.
+ */
+#define CKF_SECONDARY_AUTHENTICATION 0x00000800UL
+
+/* CKF_USER_PIN_COUNT_LOW. If it is true, an
+ * incorrect user login PIN has been entered at least once
+ * since the last successful authentication.
+ */
+#define CKF_USER_PIN_COUNT_LOW 0x00010000UL
+
+/* CKF_USER_PIN_FINAL_TRY. If it is true,
+ * supplying an incorrect user PIN will it to become locked.
+ */
+#define CKF_USER_PIN_FINAL_TRY 0x00020000UL
+
+/* CKF_USER_PIN_LOCKED. If it is true, the
+ * user PIN has been locked. User login to the token is not
+ * possible.
+ */
+#define CKF_USER_PIN_LOCKED 0x00040000UL
+
+/* CKF_USER_PIN_TO_BE_CHANGED. If it is true,
+ * the user PIN value is the default value set by token
+ * initialization or manufacturing, or the PIN has been
+ * expired by the card.
+ */
+#define CKF_USER_PIN_TO_BE_CHANGED 0x00080000UL
+
+/* CKF_SO_PIN_COUNT_LOW. If it is true, an
+ * incorrect SO login PIN has been entered at least once since
+ * the last successful authentication.
+ */
+#define CKF_SO_PIN_COUNT_LOW 0x00100000UL
+
+/* CKF_SO_PIN_FINAL_TRY. If it is true,
+ * supplying an incorrect SO PIN will it to become locked.
+ */
+#define CKF_SO_PIN_FINAL_TRY 0x00200000UL
+
+/* CKF_SO_PIN_LOCKED. If it is true, the SO
+ * PIN has been locked. SO login to the token is not possible.
+ */
+#define CKF_SO_PIN_LOCKED 0x00400000UL
+
+/* CKF_SO_PIN_TO_BE_CHANGED. If it is true,
+ * the SO PIN value is the default value set by token
+ * initialization or manufacturing, or the PIN has been
+ * expired by the card.
+ */
+#define CKF_SO_PIN_TO_BE_CHANGED 0x00800000UL
+
+#define CKF_ERROR_STATE 0x01000000UL
+
+typedef CK_TOKEN_INFO CK_PTR CK_TOKEN_INFO_PTR;
+
+
+/* CK_SESSION_HANDLE is a Cryptoki-assigned value that
+ * identifies a session
+ */
+typedef CK_ULONG CK_SESSION_HANDLE;
+
+typedef CK_SESSION_HANDLE CK_PTR CK_SESSION_HANDLE_PTR;
+
+
+/* CK_USER_TYPE enumerates the types of Cryptoki users */
+typedef CK_ULONG CK_USER_TYPE;
+/* Security Officer */
+#define CKU_SO 0UL
+/* Normal user */
+#define CKU_USER 1UL
+/* Context specific */
+#define CKU_CONTEXT_SPECIFIC 2UL
+
+/* CK_STATE enumerates the session states */
+typedef CK_ULONG CK_STATE;
+#define CKS_RO_PUBLIC_SESSION 0UL
+#define CKS_RO_USER_FUNCTIONS 1UL
+#define CKS_RW_PUBLIC_SESSION 2UL
+#define CKS_RW_USER_FUNCTIONS 3UL
+#define CKS_RW_SO_FUNCTIONS 4UL
+
+/* CK_SESSION_INFO provides information about a session */
+typedef struct CK_SESSION_INFO {
+ CK_SLOT_ID slotID;
+ CK_STATE state;
+ CK_FLAGS flags; /* see below */
+ CK_ULONG ulDeviceError; /* device-dependent error code */
+} CK_SESSION_INFO;
+
+/* The flags are defined in the following table:
+ * Bit Flag Mask Meaning
+ */
+#define CKF_RW_SESSION 0x00000002UL /* session is r/w */
+#define CKF_SERIAL_SESSION 0x00000004UL /* no parallel */
+
+typedef CK_SESSION_INFO CK_PTR CK_SESSION_INFO_PTR;
+
+
+/* CK_OBJECT_HANDLE is a token-specific identifier for an
+ * object
+ */
+typedef CK_ULONG CK_OBJECT_HANDLE;
+
+typedef CK_OBJECT_HANDLE CK_PTR CK_OBJECT_HANDLE_PTR;
+
+
+/* CK_OBJECT_CLASS is a value that identifies the classes (or
+ * types) of objects that Cryptoki recognizes. It is defined
+ * as follows:
+ */
+typedef CK_ULONG CK_OBJECT_CLASS;
+
+/* The following classes of objects are defined: */
+#define CKO_DATA 0x00000000UL
+#define CKO_CERTIFICATE 0x00000001UL
+#define CKO_PUBLIC_KEY 0x00000002UL
+#define CKO_PRIVATE_KEY 0x00000003UL
+#define CKO_SECRET_KEY 0x00000004UL
+#define CKO_HW_FEATURE 0x00000005UL
+#define CKO_DOMAIN_PARAMETERS 0x00000006UL
+#define CKO_MECHANISM 0x00000007UL
+#define CKO_OTP_KEY 0x00000008UL
+
+#define CKO_VENDOR_DEFINED 0x80000000UL
+
+typedef CK_OBJECT_CLASS CK_PTR CK_OBJECT_CLASS_PTR;
+
+/* CK_HW_FEATURE_TYPE is a value that identifies the hardware feature type
+ * of an object with CK_OBJECT_CLASS equal to CKO_HW_FEATURE.
+ */
+typedef CK_ULONG CK_HW_FEATURE_TYPE;
+
+/* The following hardware feature types are defined */
+#define CKH_MONOTONIC_COUNTER 0x00000001UL
+#define CKH_CLOCK 0x00000002UL
+#define CKH_USER_INTERFACE 0x00000003UL
+#define CKH_VENDOR_DEFINED 0x80000000UL
+
+/* CK_KEY_TYPE is a value that identifies a key type */
+typedef CK_ULONG CK_KEY_TYPE;
+
+/* the following key types are defined: */
+#define CKK_RSA 0x00000000UL
+#define CKK_DSA 0x00000001UL
+#define CKK_DH 0x00000002UL
+#define CKK_ECDSA 0x00000003UL /* Deprecated */
+#define CKK_EC 0x00000003UL
+#define CKK_X9_42_DH 0x00000004UL
+#define CKK_KEA 0x00000005UL
+#define CKK_GENERIC_SECRET 0x00000010UL
+#define CKK_RC2 0x00000011UL
+#define CKK_RC4 0x00000012UL
+#define CKK_DES 0x00000013UL
+#define CKK_DES2 0x00000014UL
+#define CKK_DES3 0x00000015UL
+#define CKK_CAST 0x00000016UL
+#define CKK_CAST3 0x00000017UL
+#define CKK_CAST5 0x00000018UL /* Deprecated */
+#define CKK_CAST128 0x00000018UL
+#define CKK_RC5 0x00000019UL
+#define CKK_IDEA 0x0000001AUL
+#define CKK_SKIPJACK 0x0000001BUL
+#define CKK_BATON 0x0000001CUL
+#define CKK_JUNIPER 0x0000001DUL
+#define CKK_CDMF 0x0000001EUL
+#define CKK_AES 0x0000001FUL
+#define CKK_BLOWFISH 0x00000020UL
+#define CKK_TWOFISH 0x00000021UL
+#define CKK_SECURID 0x00000022UL
+#define CKK_HOTP 0x00000023UL
+#define CKK_ACTI 0x00000024UL
+#define CKK_CAMELLIA 0x00000025UL
+#define CKK_ARIA 0x00000026UL
+
+#define CKK_MD5_HMAC 0x00000027UL
+#define CKK_SHA_1_HMAC 0x00000028UL
+#define CKK_RIPEMD128_HMAC 0x00000029UL
+#define CKK_RIPEMD160_HMAC 0x0000002AUL
+#define CKK_SHA256_HMAC 0x0000002BUL
+#define CKK_SHA384_HMAC 0x0000002CUL
+#define CKK_SHA512_HMAC 0x0000002DUL
+#define CKK_SHA224_HMAC 0x0000002EUL
+
+#define CKK_SEED 0x0000002FUL
+#define CKK_GOSTR3410 0x00000030UL
+#define CKK_GOSTR3411 0x00000031UL
+#define CKK_GOST28147 0x00000032UL
+
+
+
+#define CKK_VENDOR_DEFINED 0x80000000UL
+
+
+/* CK_CERTIFICATE_TYPE is a value that identifies a certificate
+ * type
+ */
+typedef CK_ULONG CK_CERTIFICATE_TYPE;
+
+#define CK_CERTIFICATE_CATEGORY_UNSPECIFIED 0UL
+#define CK_CERTIFICATE_CATEGORY_TOKEN_USER 1UL
+#define CK_CERTIFICATE_CATEGORY_AUTHORITY 2UL
+#define CK_CERTIFICATE_CATEGORY_OTHER_ENTITY 3UL
+
+#define CK_SECURITY_DOMAIN_UNSPECIFIED 0UL
+#define CK_SECURITY_DOMAIN_MANUFACTURER 1UL
+#define CK_SECURITY_DOMAIN_OPERATOR 2UL
+#define CK_SECURITY_DOMAIN_THIRD_PARTY 3UL
+
+
+/* The following certificate types are defined: */
+#define CKC_X_509 0x00000000UL
+#define CKC_X_509_ATTR_CERT 0x00000001UL
+#define CKC_WTLS 0x00000002UL
+#define CKC_VENDOR_DEFINED 0x80000000UL
+
+
+/* CK_ATTRIBUTE_TYPE is a value that identifies an attribute
+ * type
+ */
+typedef CK_ULONG CK_ATTRIBUTE_TYPE;
+
+/* The CKF_ARRAY_ATTRIBUTE flag identifies an attribute which
+ * consists of an array of values.
+ */
+#define CKF_ARRAY_ATTRIBUTE 0x40000000UL
+
+/* The following OTP-related defines relate to the CKA_OTP_FORMAT attribute */
+#define CK_OTP_FORMAT_DECIMAL 0UL
+#define CK_OTP_FORMAT_HEXADECIMAL 1UL
+#define CK_OTP_FORMAT_ALPHANUMERIC 2UL
+#define CK_OTP_FORMAT_BINARY 3UL
+
+/* The following OTP-related defines relate to the CKA_OTP_..._REQUIREMENT
+ * attributes
+ */
+#define CK_OTP_PARAM_IGNORED 0UL
+#define CK_OTP_PARAM_OPTIONAL 1UL
+#define CK_OTP_PARAM_MANDATORY 2UL
+
+/* The following attribute types are defined: */
+#define CKA_CLASS 0x00000000UL
+#define CKA_TOKEN 0x00000001UL
+#define CKA_PRIVATE 0x00000002UL
+#define CKA_LABEL 0x00000003UL
+#define CKA_APPLICATION 0x00000010UL
+#define CKA_VALUE 0x00000011UL
+#define CKA_OBJECT_ID 0x00000012UL
+#define CKA_CERTIFICATE_TYPE 0x00000080UL
+#define CKA_ISSUER 0x00000081UL
+#define CKA_SERIAL_NUMBER 0x00000082UL
+#define CKA_AC_ISSUER 0x00000083UL
+#define CKA_OWNER 0x00000084UL
+#define CKA_ATTR_TYPES 0x00000085UL
+#define CKA_TRUSTED 0x00000086UL
+#define CKA_CERTIFICATE_CATEGORY 0x00000087UL
+#define CKA_JAVA_MIDP_SECURITY_DOMAIN 0x00000088UL
+#define CKA_URL 0x00000089UL
+#define CKA_HASH_OF_SUBJECT_PUBLIC_KEY 0x0000008AUL
+#define CKA_HASH_OF_ISSUER_PUBLIC_KEY 0x0000008BUL
+#define CKA_NAME_HASH_ALGORITHM 0x0000008CUL
+#define CKA_CHECK_VALUE 0x00000090UL
+
+#define CKA_KEY_TYPE 0x00000100UL
+#define CKA_SUBJECT 0x00000101UL
+#define CKA_ID 0x00000102UL
+#define CKA_SENSITIVE 0x00000103UL
+#define CKA_ENCRYPT 0x00000104UL
+#define CKA_DECRYPT 0x00000105UL
+#define CKA_WRAP 0x00000106UL
+#define CKA_UNWRAP 0x00000107UL
+#define CKA_SIGN 0x00000108UL
+#define CKA_SIGN_RECOVER 0x00000109UL
+#define CKA_VERIFY 0x0000010AUL
+#define CKA_VERIFY_RECOVER 0x0000010BUL
+#define CKA_DERIVE 0x0000010CUL
+#define CKA_START_DATE 0x00000110UL
+#define CKA_END_DATE 0x00000111UL
+#define CKA_MODULUS 0x00000120UL
+#define CKA_MODULUS_BITS 0x00000121UL
+#define CKA_PUBLIC_EXPONENT 0x00000122UL
+#define CKA_PRIVATE_EXPONENT 0x00000123UL
+#define CKA_PRIME_1 0x00000124UL
+#define CKA_PRIME_2 0x00000125UL
+#define CKA_EXPONENT_1 0x00000126UL
+#define CKA_EXPONENT_2 0x00000127UL
+#define CKA_COEFFICIENT 0x00000128UL
+#define CKA_PUBLIC_KEY_INFO 0x00000129UL
+#define CKA_PRIME 0x00000130UL
+#define CKA_SUBPRIME 0x00000131UL
+#define CKA_BASE 0x00000132UL
+
+#define CKA_PRIME_BITS 0x00000133UL
+#define CKA_SUBPRIME_BITS 0x00000134UL
+#define CKA_SUB_PRIME_BITS CKA_SUBPRIME_BITS
+
+#define CKA_VALUE_BITS 0x00000160UL
+#define CKA_VALUE_LEN 0x00000161UL
+#define CKA_EXTRACTABLE 0x00000162UL
+#define CKA_LOCAL 0x00000163UL
+#define CKA_NEVER_EXTRACTABLE 0x00000164UL
+#define CKA_ALWAYS_SENSITIVE 0x00000165UL
+#define CKA_KEY_GEN_MECHANISM 0x00000166UL
+
+#define CKA_MODIFIABLE 0x00000170UL
+#define CKA_COPYABLE 0x00000171UL
+
+#define CKA_DESTROYABLE 0x00000172UL
+
+#define CKA_ECDSA_PARAMS 0x00000180UL /* Deprecated */
+#define CKA_EC_PARAMS 0x00000180UL
+
+#define CKA_EC_POINT 0x00000181UL
+
+#define CKA_SECONDARY_AUTH 0x00000200UL /* Deprecated */
+#define CKA_AUTH_PIN_FLAGS 0x00000201UL /* Deprecated */
+
+#define CKA_ALWAYS_AUTHENTICATE 0x00000202UL
+
+#define CKA_WRAP_WITH_TRUSTED 0x00000210UL
+#define CKA_WRAP_TEMPLATE (CKF_ARRAY_ATTRIBUTE|0x00000211UL)
+#define CKA_UNWRAP_TEMPLATE (CKF_ARRAY_ATTRIBUTE|0x00000212UL)
+#define CKA_DERIVE_TEMPLATE (CKF_ARRAY_ATTRIBUTE|0x00000213UL)
+
+#define CKA_OTP_FORMAT 0x00000220UL
+#define CKA_OTP_LENGTH 0x00000221UL
+#define CKA_OTP_TIME_INTERVAL 0x00000222UL
+#define CKA_OTP_USER_FRIENDLY_MODE 0x00000223UL
+#define CKA_OTP_CHALLENGE_REQUIREMENT 0x00000224UL
+#define CKA_OTP_TIME_REQUIREMENT 0x00000225UL
+#define CKA_OTP_COUNTER_REQUIREMENT 0x00000226UL
+#define CKA_OTP_PIN_REQUIREMENT 0x00000227UL
+#define CKA_OTP_COUNTER 0x0000022EUL
+#define CKA_OTP_TIME 0x0000022FUL
+#define CKA_OTP_USER_IDENTIFIER 0x0000022AUL
+#define CKA_OTP_SERVICE_IDENTIFIER 0x0000022BUL
+#define CKA_OTP_SERVICE_LOGO 0x0000022CUL
+#define CKA_OTP_SERVICE_LOGO_TYPE 0x0000022DUL
+
+#define CKA_GOSTR3410_PARAMS 0x00000250UL
+#define CKA_GOSTR3411_PARAMS 0x00000251UL
+#define CKA_GOST28147_PARAMS 0x00000252UL
+
+#define CKA_HW_FEATURE_TYPE 0x00000300UL
+#define CKA_RESET_ON_INIT 0x00000301UL
+#define CKA_HAS_RESET 0x00000302UL
+
+#define CKA_PIXEL_X 0x00000400UL
+#define CKA_PIXEL_Y 0x00000401UL
+#define CKA_RESOLUTION 0x00000402UL
+#define CKA_CHAR_ROWS 0x00000403UL
+#define CKA_CHAR_COLUMNS 0x00000404UL
+#define CKA_COLOR 0x00000405UL
+#define CKA_BITS_PER_PIXEL 0x00000406UL
+#define CKA_CHAR_SETS 0x00000480UL
+#define CKA_ENCODING_METHODS 0x00000481UL
+#define CKA_MIME_TYPES 0x00000482UL
+#define CKA_MECHANISM_TYPE 0x00000500UL
+#define CKA_REQUIRED_CMS_ATTRIBUTES 0x00000501UL
+#define CKA_DEFAULT_CMS_ATTRIBUTES 0x00000502UL
+#define CKA_SUPPORTED_CMS_ATTRIBUTES 0x00000503UL
+#define CKA_ALLOWED_MECHANISMS (CKF_ARRAY_ATTRIBUTE|0x00000600UL)
+
+#define CKA_VENDOR_DEFINED 0x80000000UL
+
+/* CK_ATTRIBUTE is a structure that includes the type, length
+ * and value of an attribute
+ */
+typedef struct CK_ATTRIBUTE {
+ CK_ATTRIBUTE_TYPE type;
+ CK_VOID_PTR pValue;
+ CK_ULONG ulValueLen; /* in bytes */
+} CK_ATTRIBUTE;
+
+typedef CK_ATTRIBUTE CK_PTR CK_ATTRIBUTE_PTR;
+
+/* CK_DATE is a structure that defines a date */
+typedef struct CK_DATE{
+ CK_CHAR year[4]; /* the year ("1900" - "9999") */
+ CK_CHAR month[2]; /* the month ("01" - "12") */
+ CK_CHAR day[2]; /* the day ("01" - "31") */
+} CK_DATE;
+
+
+/* CK_MECHANISM_TYPE is a value that identifies a mechanism
+ * type
+ */
+typedef CK_ULONG CK_MECHANISM_TYPE;
+
+/* the following mechanism types are defined: */
+#define CKM_RSA_PKCS_KEY_PAIR_GEN 0x00000000UL
+#define CKM_RSA_PKCS 0x00000001UL
+#define CKM_RSA_9796 0x00000002UL
+#define CKM_RSA_X_509 0x00000003UL
+
+#define CKM_MD2_RSA_PKCS 0x00000004UL
+#define CKM_MD5_RSA_PKCS 0x00000005UL
+#define CKM_SHA1_RSA_PKCS 0x00000006UL
+
+#define CKM_RIPEMD128_RSA_PKCS 0x00000007UL
+#define CKM_RIPEMD160_RSA_PKCS 0x00000008UL
+#define CKM_RSA_PKCS_OAEP 0x00000009UL
+
+#define CKM_RSA_X9_31_KEY_PAIR_GEN 0x0000000AUL
+#define CKM_RSA_X9_31 0x0000000BUL
+#define CKM_SHA1_RSA_X9_31 0x0000000CUL
+#define CKM_RSA_PKCS_PSS 0x0000000DUL
+#define CKM_SHA1_RSA_PKCS_PSS 0x0000000EUL
+
+#define CKM_DSA_KEY_PAIR_GEN 0x00000010UL
+#define CKM_DSA 0x00000011UL
+#define CKM_DSA_SHA1 0x00000012UL
+#define CKM_DSA_SHA224 0x00000013UL
+#define CKM_DSA_SHA256 0x00000014UL
+#define CKM_DSA_SHA384 0x00000015UL
+#define CKM_DSA_SHA512 0x00000016UL
+
+#define CKM_DH_PKCS_KEY_PAIR_GEN 0x00000020UL
+#define CKM_DH_PKCS_DERIVE 0x00000021UL
+
+#define CKM_X9_42_DH_KEY_PAIR_GEN 0x00000030UL
+#define CKM_X9_42_DH_DERIVE 0x00000031UL
+#define CKM_X9_42_DH_HYBRID_DERIVE 0x00000032UL
+#define CKM_X9_42_MQV_DERIVE 0x00000033UL
+
+#define CKM_SHA256_RSA_PKCS 0x00000040UL
+#define CKM_SHA384_RSA_PKCS 0x00000041UL
+#define CKM_SHA512_RSA_PKCS 0x00000042UL
+#define CKM_SHA256_RSA_PKCS_PSS 0x00000043UL
+#define CKM_SHA384_RSA_PKCS_PSS 0x00000044UL
+#define CKM_SHA512_RSA_PKCS_PSS 0x00000045UL
+
+#define CKM_SHA224_RSA_PKCS 0x00000046UL
+#define CKM_SHA224_RSA_PKCS_PSS 0x00000047UL
+
+#define CKM_SHA512_224 0x00000048UL
+#define CKM_SHA512_224_HMAC 0x00000049UL
+#define CKM_SHA512_224_HMAC_GENERAL 0x0000004AUL
+#define CKM_SHA512_224_KEY_DERIVATION 0x0000004BUL
+#define CKM_SHA512_256 0x0000004CUL
+#define CKM_SHA512_256_HMAC 0x0000004DUL
+#define CKM_SHA512_256_HMAC_GENERAL 0x0000004EUL
+#define CKM_SHA512_256_KEY_DERIVATION 0x0000004FUL
+
+#define CKM_SHA512_T 0x00000050UL
+#define CKM_SHA512_T_HMAC 0x00000051UL
+#define CKM_SHA512_T_HMAC_GENERAL 0x00000052UL
+#define CKM_SHA512_T_KEY_DERIVATION 0x00000053UL
+
+#define CKM_RC2_KEY_GEN 0x00000100UL
+#define CKM_RC2_ECB 0x00000101UL
+#define CKM_RC2_CBC 0x00000102UL
+#define CKM_RC2_MAC 0x00000103UL
+
+#define CKM_RC2_MAC_GENERAL 0x00000104UL
+#define CKM_RC2_CBC_PAD 0x00000105UL
+
+#define CKM_RC4_KEY_GEN 0x00000110UL
+#define CKM_RC4 0x00000111UL
+#define CKM_DES_KEY_GEN 0x00000120UL
+#define CKM_DES_ECB 0x00000121UL
+#define CKM_DES_CBC 0x00000122UL
+#define CKM_DES_MAC 0x00000123UL
+
+#define CKM_DES_MAC_GENERAL 0x00000124UL
+#define CKM_DES_CBC_PAD 0x00000125UL
+
+#define CKM_DES2_KEY_GEN 0x00000130UL
+#define CKM_DES3_KEY_GEN 0x00000131UL
+#define CKM_DES3_ECB 0x00000132UL
+#define CKM_DES3_CBC 0x00000133UL
+#define CKM_DES3_MAC 0x00000134UL
+
+#define CKM_DES3_MAC_GENERAL 0x00000135UL
+#define CKM_DES3_CBC_PAD 0x00000136UL
+#define CKM_DES3_CMAC_GENERAL 0x00000137UL
+#define CKM_DES3_CMAC 0x00000138UL
+#define CKM_CDMF_KEY_GEN 0x00000140UL
+#define CKM_CDMF_ECB 0x00000141UL
+#define CKM_CDMF_CBC 0x00000142UL
+#define CKM_CDMF_MAC 0x00000143UL
+#define CKM_CDMF_MAC_GENERAL 0x00000144UL
+#define CKM_CDMF_CBC_PAD 0x00000145UL
+
+#define CKM_DES_OFB64 0x00000150UL
+#define CKM_DES_OFB8 0x00000151UL
+#define CKM_DES_CFB64 0x00000152UL
+#define CKM_DES_CFB8 0x00000153UL
+
+#define CKM_MD2 0x00000200UL
+
+#define CKM_MD2_HMAC 0x00000201UL
+#define CKM_MD2_HMAC_GENERAL 0x00000202UL
+
+#define CKM_MD5 0x00000210UL
+
+#define CKM_MD5_HMAC 0x00000211UL
+#define CKM_MD5_HMAC_GENERAL 0x00000212UL
+
+#define CKM_SHA_1 0x00000220UL
+
+#define CKM_SHA_1_HMAC 0x00000221UL
+#define CKM_SHA_1_HMAC_GENERAL 0x00000222UL
+
+#define CKM_RIPEMD128 0x00000230UL
+#define CKM_RIPEMD128_HMAC 0x00000231UL
+#define CKM_RIPEMD128_HMAC_GENERAL 0x00000232UL
+#define CKM_RIPEMD160 0x00000240UL
+#define CKM_RIPEMD160_HMAC 0x00000241UL
+#define CKM_RIPEMD160_HMAC_GENERAL 0x00000242UL
+
+#define CKM_SHA256 0x00000250UL
+#define CKM_SHA256_HMAC 0x00000251UL
+#define CKM_SHA256_HMAC_GENERAL 0x00000252UL
+#define CKM_SHA224 0x00000255UL
+#define CKM_SHA224_HMAC 0x00000256UL
+#define CKM_SHA224_HMAC_GENERAL 0x00000257UL
+#define CKM_SHA384 0x00000260UL
+#define CKM_SHA384_HMAC 0x00000261UL
+#define CKM_SHA384_HMAC_GENERAL 0x00000262UL
+#define CKM_SHA512 0x00000270UL
+#define CKM_SHA512_HMAC 0x00000271UL
+#define CKM_SHA512_HMAC_GENERAL 0x00000272UL
+#define CKM_SECURID_KEY_GEN 0x00000280UL
+#define CKM_SECURID 0x00000282UL
+#define CKM_HOTP_KEY_GEN 0x00000290UL
+#define CKM_HOTP 0x00000291UL
+#define CKM_ACTI 0x000002A0UL
+#define CKM_ACTI_KEY_GEN 0x000002A1UL
+
+#define CKM_CAST_KEY_GEN 0x00000300UL
+#define CKM_CAST_ECB 0x00000301UL
+#define CKM_CAST_CBC 0x00000302UL
+#define CKM_CAST_MAC 0x00000303UL
+#define CKM_CAST_MAC_GENERAL 0x00000304UL
+#define CKM_CAST_CBC_PAD 0x00000305UL
+#define CKM_CAST3_KEY_GEN 0x00000310UL
+#define CKM_CAST3_ECB 0x00000311UL
+#define CKM_CAST3_CBC 0x00000312UL
+#define CKM_CAST3_MAC 0x00000313UL
+#define CKM_CAST3_MAC_GENERAL 0x00000314UL
+#define CKM_CAST3_CBC_PAD 0x00000315UL
+/* Note that CAST128 and CAST5 are the same algorithm */
+#define CKM_CAST5_KEY_GEN 0x00000320UL
+#define CKM_CAST128_KEY_GEN 0x00000320UL
+#define CKM_CAST5_ECB 0x00000321UL
+#define CKM_CAST128_ECB 0x00000321UL
+#define CKM_CAST5_CBC 0x00000322UL /* Deprecated */
+#define CKM_CAST128_CBC 0x00000322UL
+#define CKM_CAST5_MAC 0x00000323UL /* Deprecated */
+#define CKM_CAST128_MAC 0x00000323UL
+#define CKM_CAST5_MAC_GENERAL 0x00000324UL /* Deprecated */
+#define CKM_CAST128_MAC_GENERAL 0x00000324UL
+#define CKM_CAST5_CBC_PAD 0x00000325UL /* Deprecated */
+#define CKM_CAST128_CBC_PAD 0x00000325UL
+#define CKM_RC5_KEY_GEN 0x00000330UL
+#define CKM_RC5_ECB 0x00000331UL
+#define CKM_RC5_CBC 0x00000332UL
+#define CKM_RC5_MAC 0x00000333UL
+#define CKM_RC5_MAC_GENERAL 0x00000334UL
+#define CKM_RC5_CBC_PAD 0x00000335UL
+#define CKM_IDEA_KEY_GEN 0x00000340UL
+#define CKM_IDEA_ECB 0x00000341UL
+#define CKM_IDEA_CBC 0x00000342UL
+#define CKM_IDEA_MAC 0x00000343UL
+#define CKM_IDEA_MAC_GENERAL 0x00000344UL
+#define CKM_IDEA_CBC_PAD 0x00000345UL
+#define CKM_GENERIC_SECRET_KEY_GEN 0x00000350UL
+#define CKM_CONCATENATE_BASE_AND_KEY 0x00000360UL
+#define CKM_CONCATENATE_BASE_AND_DATA 0x00000362UL
+#define CKM_CONCATENATE_DATA_AND_BASE 0x00000363UL
+#define CKM_XOR_BASE_AND_DATA 0x00000364UL
+#define CKM_EXTRACT_KEY_FROM_KEY 0x00000365UL
+#define CKM_SSL3_PRE_MASTER_KEY_GEN 0x00000370UL
+#define CKM_SSL3_MASTER_KEY_DERIVE 0x00000371UL
+#define CKM_SSL3_KEY_AND_MAC_DERIVE 0x00000372UL
+
+#define CKM_SSL3_MASTER_KEY_DERIVE_DH 0x00000373UL
+#define CKM_TLS_PRE_MASTER_KEY_GEN 0x00000374UL
+#define CKM_TLS_MASTER_KEY_DERIVE 0x00000375UL
+#define CKM_TLS_KEY_AND_MAC_DERIVE 0x00000376UL
+#define CKM_TLS_MASTER_KEY_DERIVE_DH 0x00000377UL
+
+#define CKM_TLS_PRF 0x00000378UL
+
+#define CKM_SSL3_MD5_MAC 0x00000380UL
+#define CKM_SSL3_SHA1_MAC 0x00000381UL
+#define CKM_MD5_KEY_DERIVATION 0x00000390UL
+#define CKM_MD2_KEY_DERIVATION 0x00000391UL
+#define CKM_SHA1_KEY_DERIVATION 0x00000392UL
+
+#define CKM_SHA256_KEY_DERIVATION 0x00000393UL
+#define CKM_SHA384_KEY_DERIVATION 0x00000394UL
+#define CKM_SHA512_KEY_DERIVATION 0x00000395UL
+#define CKM_SHA224_KEY_DERIVATION 0x00000396UL
+
+#define CKM_PBE_MD2_DES_CBC 0x000003A0UL
+#define CKM_PBE_MD5_DES_CBC 0x000003A1UL
+#define CKM_PBE_MD5_CAST_CBC 0x000003A2UL
+#define CKM_PBE_MD5_CAST3_CBC 0x000003A3UL
+#define CKM_PBE_MD5_CAST5_CBC 0x000003A4UL /* Deprecated */
+#define CKM_PBE_MD5_CAST128_CBC 0x000003A4UL
+#define CKM_PBE_SHA1_CAST5_CBC 0x000003A5UL /* Deprecated */
+#define CKM_PBE_SHA1_CAST128_CBC 0x000003A5UL
+#define CKM_PBE_SHA1_RC4_128 0x000003A6UL
+#define CKM_PBE_SHA1_RC4_40 0x000003A7UL
+#define CKM_PBE_SHA1_DES3_EDE_CBC 0x000003A8UL
+#define CKM_PBE_SHA1_DES2_EDE_CBC 0x000003A9UL
+#define CKM_PBE_SHA1_RC2_128_CBC 0x000003AAUL
+#define CKM_PBE_SHA1_RC2_40_CBC 0x000003ABUL
+
+#define CKM_PKCS5_PBKD2 0x000003B0UL
+
+#define CKM_PBA_SHA1_WITH_SHA1_HMAC 0x000003C0UL
+
+#define CKM_WTLS_PRE_MASTER_KEY_GEN 0x000003D0UL
+#define CKM_WTLS_MASTER_KEY_DERIVE 0x000003D1UL
+#define CKM_WTLS_MASTER_KEY_DERIVE_DH_ECC 0x000003D2UL
+#define CKM_WTLS_PRF 0x000003D3UL
+#define CKM_WTLS_SERVER_KEY_AND_MAC_DERIVE 0x000003D4UL
+#define CKM_WTLS_CLIENT_KEY_AND_MAC_DERIVE 0x000003D5UL
+
+#define CKM_TLS10_MAC_SERVER 0x000003D6UL
+#define CKM_TLS10_MAC_CLIENT 0x000003D7UL
+#define CKM_TLS12_MAC 0x000003D8UL
+#define CKM_TLS12_KDF 0x000003D9UL
+#define CKM_TLS12_MASTER_KEY_DERIVE 0x000003E0UL
+#define CKM_TLS12_KEY_AND_MAC_DERIVE 0x000003E1UL
+#define CKM_TLS12_MASTER_KEY_DERIVE_DH 0x000003E2UL
+#define CKM_TLS12_KEY_SAFE_DERIVE 0x000003E3UL
+#define CKM_TLS_MAC 0x000003E4UL
+#define CKM_TLS_KDF 0x000003E5UL
+
+#define CKM_KEY_WRAP_LYNKS 0x00000400UL
+#define CKM_KEY_WRAP_SET_OAEP 0x00000401UL
+
+#define CKM_CMS_SIG 0x00000500UL
+#define CKM_KIP_DERIVE 0x00000510UL
+#define CKM_KIP_WRAP 0x00000511UL
+#define CKM_KIP_MAC 0x00000512UL
+
+#define CKM_CAMELLIA_KEY_GEN 0x00000550UL
+#define CKM_CAMELLIA_ECB 0x00000551UL
+#define CKM_CAMELLIA_CBC 0x00000552UL
+#define CKM_CAMELLIA_MAC 0x00000553UL
+#define CKM_CAMELLIA_MAC_GENERAL 0x00000554UL
+#define CKM_CAMELLIA_CBC_PAD 0x00000555UL
+#define CKM_CAMELLIA_ECB_ENCRYPT_DATA 0x00000556UL
+#define CKM_CAMELLIA_CBC_ENCRYPT_DATA 0x00000557UL
+#define CKM_CAMELLIA_CTR 0x00000558UL
+
+#define CKM_ARIA_KEY_GEN 0x00000560UL
+#define CKM_ARIA_ECB 0x00000561UL
+#define CKM_ARIA_CBC 0x00000562UL
+#define CKM_ARIA_MAC 0x00000563UL
+#define CKM_ARIA_MAC_GENERAL 0x00000564UL
+#define CKM_ARIA_CBC_PAD 0x00000565UL
+#define CKM_ARIA_ECB_ENCRYPT_DATA 0x00000566UL
+#define CKM_ARIA_CBC_ENCRYPT_DATA 0x00000567UL
+
+#define CKM_SEED_KEY_GEN 0x00000650UL
+#define CKM_SEED_ECB 0x00000651UL
+#define CKM_SEED_CBC 0x00000652UL
+#define CKM_SEED_MAC 0x00000653UL
+#define CKM_SEED_MAC_GENERAL 0x00000654UL
+#define CKM_SEED_CBC_PAD 0x00000655UL
+#define CKM_SEED_ECB_ENCRYPT_DATA 0x00000656UL
+#define CKM_SEED_CBC_ENCRYPT_DATA 0x00000657UL
+
+#define CKM_SKIPJACK_KEY_GEN 0x00001000UL
+#define CKM_SKIPJACK_ECB64 0x00001001UL
+#define CKM_SKIPJACK_CBC64 0x00001002UL
+#define CKM_SKIPJACK_OFB64 0x00001003UL
+#define CKM_SKIPJACK_CFB64 0x00001004UL
+#define CKM_SKIPJACK_CFB32 0x00001005UL
+#define CKM_SKIPJACK_CFB16 0x00001006UL
+#define CKM_SKIPJACK_CFB8 0x00001007UL
+#define CKM_SKIPJACK_WRAP 0x00001008UL
+#define CKM_SKIPJACK_PRIVATE_WRAP 0x00001009UL
+#define CKM_SKIPJACK_RELAYX 0x0000100aUL
+#define CKM_KEA_KEY_PAIR_GEN 0x00001010UL
+#define CKM_KEA_KEY_DERIVE 0x00001011UL
+#define CKM_KEA_DERIVE 0x00001012UL
+#define CKM_FORTEZZA_TIMESTAMP 0x00001020UL
+#define CKM_BATON_KEY_GEN 0x00001030UL
+#define CKM_BATON_ECB128 0x00001031UL
+#define CKM_BATON_ECB96 0x00001032UL
+#define CKM_BATON_CBC128 0x00001033UL
+#define CKM_BATON_COUNTER 0x00001034UL
+#define CKM_BATON_SHUFFLE 0x00001035UL
+#define CKM_BATON_WRAP 0x00001036UL
+
+#define CKM_ECDSA_KEY_PAIR_GEN 0x00001040UL /* Deprecated */
+#define CKM_EC_KEY_PAIR_GEN 0x00001040UL
+
+#define CKM_ECDSA 0x00001041UL
+#define CKM_ECDSA_SHA1 0x00001042UL
+#define CKM_ECDSA_SHA224 0x00001043UL
+#define CKM_ECDSA_SHA256 0x00001044UL
+#define CKM_ECDSA_SHA384 0x00001045UL
+#define CKM_ECDSA_SHA512 0x00001046UL
+
+#define CKM_ECDH1_DERIVE 0x00001050UL
+#define CKM_ECDH1_COFACTOR_DERIVE 0x00001051UL
+#define CKM_ECMQV_DERIVE 0x00001052UL
+
+#define CKM_ECDH_AES_KEY_WRAP 0x00001053UL
+#define CKM_RSA_AES_KEY_WRAP 0x00001054UL
+
+#define CKM_JUNIPER_KEY_GEN 0x00001060UL
+#define CKM_JUNIPER_ECB128 0x00001061UL
+#define CKM_JUNIPER_CBC128 0x00001062UL
+#define CKM_JUNIPER_COUNTER 0x00001063UL
+#define CKM_JUNIPER_SHUFFLE 0x00001064UL
+#define CKM_JUNIPER_WRAP 0x00001065UL
+#define CKM_FASTHASH 0x00001070UL
+
+#define CKM_AES_KEY_GEN 0x00001080UL
+#define CKM_AES_ECB 0x00001081UL
+#define CKM_AES_CBC 0x00001082UL
+#define CKM_AES_MAC 0x00001083UL
+#define CKM_AES_MAC_GENERAL 0x00001084UL
+#define CKM_AES_CBC_PAD 0x00001085UL
+#define CKM_AES_CTR 0x00001086UL
+#define CKM_AES_GCM 0x00001087UL
+#define CKM_AES_CCM 0x00001088UL
+#define CKM_AES_CTS 0x00001089UL
+#define CKM_AES_CMAC 0x0000108AUL
+#define CKM_AES_CMAC_GENERAL 0x0000108BUL
+
+#define CKM_AES_XCBC_MAC 0x0000108CUL
+#define CKM_AES_XCBC_MAC_96 0x0000108DUL
+#define CKM_AES_GMAC 0x0000108EUL
+
+#define CKM_BLOWFISH_KEY_GEN 0x00001090UL
+#define CKM_BLOWFISH_CBC 0x00001091UL
+#define CKM_TWOFISH_KEY_GEN 0x00001092UL
+#define CKM_TWOFISH_CBC 0x00001093UL
+#define CKM_BLOWFISH_CBC_PAD 0x00001094UL
+#define CKM_TWOFISH_CBC_PAD 0x00001095UL
+
+#define CKM_DES_ECB_ENCRYPT_DATA 0x00001100UL
+#define CKM_DES_CBC_ENCRYPT_DATA 0x00001101UL
+#define CKM_DES3_ECB_ENCRYPT_DATA 0x00001102UL
+#define CKM_DES3_CBC_ENCRYPT_DATA 0x00001103UL
+#define CKM_AES_ECB_ENCRYPT_DATA 0x00001104UL
+#define CKM_AES_CBC_ENCRYPT_DATA 0x00001105UL
+
+#define CKM_GOSTR3410_KEY_PAIR_GEN 0x00001200UL
+#define CKM_GOSTR3410 0x00001201UL
+#define CKM_GOSTR3410_WITH_GOSTR3411 0x00001202UL
+#define CKM_GOSTR3410_KEY_WRAP 0x00001203UL
+#define CKM_GOSTR3410_DERIVE 0x00001204UL
+#define CKM_GOSTR3411 0x00001210UL
+#define CKM_GOSTR3411_HMAC 0x00001211UL
+#define CKM_GOST28147_KEY_GEN 0x00001220UL
+#define CKM_GOST28147_ECB 0x00001221UL
+#define CKM_GOST28147 0x00001222UL
+#define CKM_GOST28147_MAC 0x00001223UL
+#define CKM_GOST28147_KEY_WRAP 0x00001224UL
+
+#define CKM_DSA_PARAMETER_GEN 0x00002000UL
+#define CKM_DH_PKCS_PARAMETER_GEN 0x00002001UL
+#define CKM_X9_42_DH_PARAMETER_GEN 0x00002002UL
+#define CKM_DSA_PROBABLISTIC_PARAMETER_GEN 0x00002003UL
+#define CKM_DSA_SHAWE_TAYLOR_PARAMETER_GEN 0x00002004UL
+
+#define CKM_AES_OFB 0x00002104UL
+#define CKM_AES_CFB64 0x00002105UL
+#define CKM_AES_CFB8 0x00002106UL
+#define CKM_AES_CFB128 0x00002107UL
+
+#define CKM_AES_CFB1 0x00002108UL
+#define CKM_AES_KEY_WRAP 0x00002109UL /* WAS: 0x00001090 */
+#define CKM_AES_KEY_WRAP_PAD 0x0000210AUL /* WAS: 0x00001091 */
+
+#define CKM_RSA_PKCS_TPM_1_1 0x00004001UL
+#define CKM_RSA_PKCS_OAEP_TPM_1_1 0x00004002UL
+
+#define CKM_VENDOR_DEFINED 0x80000000UL
+
+typedef CK_MECHANISM_TYPE CK_PTR CK_MECHANISM_TYPE_PTR;
+
+
+/* CK_MECHANISM is a structure that specifies a particular
+ * mechanism
+ */
+typedef struct CK_MECHANISM {
+ CK_MECHANISM_TYPE mechanism;
+ CK_VOID_PTR pParameter;
+ CK_ULONG ulParameterLen; /* in bytes */
+} CK_MECHANISM;
+
+typedef CK_MECHANISM CK_PTR CK_MECHANISM_PTR;
+
+
+/* CK_MECHANISM_INFO provides information about a particular
+ * mechanism
+ */
+typedef struct CK_MECHANISM_INFO {
+ CK_ULONG ulMinKeySize;
+ CK_ULONG ulMaxKeySize;
+ CK_FLAGS flags;
+} CK_MECHANISM_INFO;
+
+/* The flags are defined as follows:
+ * Bit Flag Mask Meaning */
+#define CKF_HW 0x00000001UL /* performed by HW */
+
+/* Specify whether or not a mechanism can be used for a particular task */
+#define CKF_ENCRYPT 0x00000100UL
+#define CKF_DECRYPT 0x00000200UL
+#define CKF_DIGEST 0x00000400UL
+#define CKF_SIGN 0x00000800UL
+#define CKF_SIGN_RECOVER 0x00001000UL
+#define CKF_VERIFY 0x00002000UL
+#define CKF_VERIFY_RECOVER 0x00004000UL
+#define CKF_GENERATE 0x00008000UL
+#define CKF_GENERATE_KEY_PAIR 0x00010000UL
+#define CKF_WRAP 0x00020000UL
+#define CKF_UNWRAP 0x00040000UL
+#define CKF_DERIVE 0x00080000UL
+
+/* Describe a token's EC capabilities not available in mechanism
+ * information.
+ */
+#define CKF_EC_F_P 0x00100000UL
+#define CKF_EC_F_2M 0x00200000UL
+#define CKF_EC_ECPARAMETERS 0x00400000UL
+#define CKF_EC_NAMEDCURVE 0x00800000UL
+#define CKF_EC_UNCOMPRESS 0x01000000UL
+#define CKF_EC_COMPRESS 0x02000000UL
+
+#define CKF_EXTENSION 0x80000000UL
+
+typedef CK_MECHANISM_INFO CK_PTR CK_MECHANISM_INFO_PTR;
+
+/* CK_RV is a value that identifies the return value of a
+ * Cryptoki function
+ */
+typedef CK_ULONG CK_RV;
+
+#define CKR_OK 0x00000000UL
+#define CKR_CANCEL 0x00000001UL
+#define CKR_HOST_MEMORY 0x00000002UL
+#define CKR_SLOT_ID_INVALID 0x00000003UL
+
+#define CKR_GENERAL_ERROR 0x00000005UL
+#define CKR_FUNCTION_FAILED 0x00000006UL
+
+#define CKR_ARGUMENTS_BAD 0x00000007UL
+#define CKR_NO_EVENT 0x00000008UL
+#define CKR_NEED_TO_CREATE_THREADS 0x00000009UL
+#define CKR_CANT_LOCK 0x0000000AUL
+
+#define CKR_ATTRIBUTE_READ_ONLY 0x00000010UL
+#define CKR_ATTRIBUTE_SENSITIVE 0x00000011UL
+#define CKR_ATTRIBUTE_TYPE_INVALID 0x00000012UL
+#define CKR_ATTRIBUTE_VALUE_INVALID 0x00000013UL
+
+#define CKR_ACTION_PROHIBITED 0x0000001BUL
+
+#define CKR_DATA_INVALID 0x00000020UL
+#define CKR_DATA_LEN_RANGE 0x00000021UL
+#define CKR_DEVICE_ERROR 0x00000030UL
+#define CKR_DEVICE_MEMORY 0x00000031UL
+#define CKR_DEVICE_REMOVED 0x00000032UL
+#define CKR_ENCRYPTED_DATA_INVALID 0x00000040UL
+#define CKR_ENCRYPTED_DATA_LEN_RANGE 0x00000041UL
+#define CKR_FUNCTION_CANCELED 0x00000050UL
+#define CKR_FUNCTION_NOT_PARALLEL 0x00000051UL
+
+#define CKR_FUNCTION_NOT_SUPPORTED 0x00000054UL
+
+#define CKR_KEY_HANDLE_INVALID 0x00000060UL
+
+#define CKR_KEY_SIZE_RANGE 0x00000062UL
+#define CKR_KEY_TYPE_INCONSISTENT 0x00000063UL
+
+#define CKR_KEY_NOT_NEEDED 0x00000064UL
+#define CKR_KEY_CHANGED 0x00000065UL
+#define CKR_KEY_NEEDED 0x00000066UL
+#define CKR_KEY_INDIGESTIBLE 0x00000067UL
+#define CKR_KEY_FUNCTION_NOT_PERMITTED 0x00000068UL
+#define CKR_KEY_NOT_WRAPPABLE 0x00000069UL
+#define CKR_KEY_UNEXTRACTABLE 0x0000006AUL
+
+#define CKR_MECHANISM_INVALID 0x00000070UL
+#define CKR_MECHANISM_PARAM_INVALID 0x00000071UL
+
+#define CKR_OBJECT_HANDLE_INVALID 0x00000082UL
+#define CKR_OPERATION_ACTIVE 0x00000090UL
+#define CKR_OPERATION_NOT_INITIALIZED 0x00000091UL
+#define CKR_PIN_INCORRECT 0x000000A0UL
+#define CKR_PIN_INVALID 0x000000A1UL
+#define CKR_PIN_LEN_RANGE 0x000000A2UL
+
+#define CKR_PIN_EXPIRED 0x000000A3UL
+#define CKR_PIN_LOCKED 0x000000A4UL
+
+#define CKR_SESSION_CLOSED 0x000000B0UL
+#define CKR_SESSION_COUNT 0x000000B1UL
+#define CKR_SESSION_HANDLE_INVALID 0x000000B3UL
+#define CKR_SESSION_PARALLEL_NOT_SUPPORTED 0x000000B4UL
+#define CKR_SESSION_READ_ONLY 0x000000B5UL
+#define CKR_SESSION_EXISTS 0x000000B6UL
+
+#define CKR_SESSION_READ_ONLY_EXISTS 0x000000B7UL
+#define CKR_SESSION_READ_WRITE_SO_EXISTS 0x000000B8UL
+
+#define CKR_SIGNATURE_INVALID 0x000000C0UL
+#define CKR_SIGNATURE_LEN_RANGE 0x000000C1UL
+#define CKR_TEMPLATE_INCOMPLETE 0x000000D0UL
+#define CKR_TEMPLATE_INCONSISTENT 0x000000D1UL
+#define CKR_TOKEN_NOT_PRESENT 0x000000E0UL
+#define CKR_TOKEN_NOT_RECOGNIZED 0x000000E1UL
+#define CKR_TOKEN_WRITE_PROTECTED 0x000000E2UL
+#define CKR_UNWRAPPING_KEY_HANDLE_INVALID 0x000000F0UL
+#define CKR_UNWRAPPING_KEY_SIZE_RANGE 0x000000F1UL
+#define CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT 0x000000F2UL
+#define CKR_USER_ALREADY_LOGGED_IN 0x00000100UL
+#define CKR_USER_NOT_LOGGED_IN 0x00000101UL
+#define CKR_USER_PIN_NOT_INITIALIZED 0x00000102UL
+#define CKR_USER_TYPE_INVALID 0x00000103UL
+
+#define CKR_USER_ANOTHER_ALREADY_LOGGED_IN 0x00000104UL
+#define CKR_USER_TOO_MANY_TYPES 0x00000105UL
+
+#define CKR_WRAPPED_KEY_INVALID 0x00000110UL
+#define CKR_WRAPPED_KEY_LEN_RANGE 0x00000112UL
+#define CKR_WRAPPING_KEY_HANDLE_INVALID 0x00000113UL
+#define CKR_WRAPPING_KEY_SIZE_RANGE 0x00000114UL
+#define CKR_WRAPPING_KEY_TYPE_INCONSISTENT 0x00000115UL
+#define CKR_RANDOM_SEED_NOT_SUPPORTED 0x00000120UL
+
+#define CKR_RANDOM_NO_RNG 0x00000121UL
+
+#define CKR_DOMAIN_PARAMS_INVALID 0x00000130UL
+
+#define CKR_CURVE_NOT_SUPPORTED 0x00000140UL
+
+#define CKR_BUFFER_TOO_SMALL 0x00000150UL
+#define CKR_SAVED_STATE_INVALID 0x00000160UL
+#define CKR_INFORMATION_SENSITIVE 0x00000170UL
+#define CKR_STATE_UNSAVEABLE 0x00000180UL
+
+#define CKR_CRYPTOKI_NOT_INITIALIZED 0x00000190UL
+#define CKR_CRYPTOKI_ALREADY_INITIALIZED 0x00000191UL
+#define CKR_MUTEX_BAD 0x000001A0UL
+#define CKR_MUTEX_NOT_LOCKED 0x000001A1UL
+
+#define CKR_NEW_PIN_MODE 0x000001B0UL
+#define CKR_NEXT_OTP 0x000001B1UL
+
+#define CKR_EXCEEDED_MAX_ITERATIONS 0x000001B5UL
+#define CKR_FIPS_SELF_TEST_FAILED 0x000001B6UL
+#define CKR_LIBRARY_LOAD_FAILED 0x000001B7UL
+#define CKR_PIN_TOO_WEAK 0x000001B8UL
+#define CKR_PUBLIC_KEY_INVALID 0x000001B9UL
+
+#define CKR_FUNCTION_REJECTED 0x00000200UL
+
+#define CKR_VENDOR_DEFINED 0x80000000UL
+
+
+/* CK_NOTIFY is an application callback that processes events */
+typedef CK_CALLBACK_FUNCTION(CK_RV, CK_NOTIFY)(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_NOTIFICATION event,
+ CK_VOID_PTR pApplication /* passed to C_OpenSession */
+);
+
+
+/* CK_FUNCTION_LIST is a structure holding a Cryptoki spec
+ * version and pointers of appropriate types to all the
+ * Cryptoki functions
+ */
+typedef struct CK_FUNCTION_LIST CK_FUNCTION_LIST;
+
+typedef CK_FUNCTION_LIST CK_PTR CK_FUNCTION_LIST_PTR;
+
+typedef CK_FUNCTION_LIST_PTR CK_PTR CK_FUNCTION_LIST_PTR_PTR;
+
+
+/* CK_CREATEMUTEX is an application callback for creating a
+ * mutex object
+ */
+typedef CK_CALLBACK_FUNCTION(CK_RV, CK_CREATEMUTEX)(
+ CK_VOID_PTR_PTR ppMutex /* location to receive ptr to mutex */
+);
+
+
+/* CK_DESTROYMUTEX is an application callback for destroying a
+ * mutex object
+ */
+typedef CK_CALLBACK_FUNCTION(CK_RV, CK_DESTROYMUTEX)(
+ CK_VOID_PTR pMutex /* pointer to mutex */
+);
+
+
+/* CK_LOCKMUTEX is an application callback for locking a mutex */
+typedef CK_CALLBACK_FUNCTION(CK_RV, CK_LOCKMUTEX)(
+ CK_VOID_PTR pMutex /* pointer to mutex */
+);
+
+
+/* CK_UNLOCKMUTEX is an application callback for unlocking a
+ * mutex
+ */
+typedef CK_CALLBACK_FUNCTION(CK_RV, CK_UNLOCKMUTEX)(
+ CK_VOID_PTR pMutex /* pointer to mutex */
+);
+
+
+/* CK_C_INITIALIZE_ARGS provides the optional arguments to
+ * C_Initialize
+ */
+typedef struct CK_C_INITIALIZE_ARGS {
+ CK_CREATEMUTEX CreateMutex;
+ CK_DESTROYMUTEX DestroyMutex;
+ CK_LOCKMUTEX LockMutex;
+ CK_UNLOCKMUTEX UnlockMutex;
+ CK_FLAGS flags;
+ CK_VOID_PTR pReserved;
+} CK_C_INITIALIZE_ARGS;
+
+/* flags: bit flags that provide capabilities of the slot
+ * Bit Flag Mask Meaning
+ */
+#define CKF_LIBRARY_CANT_CREATE_OS_THREADS 0x00000001UL
+#define CKF_OS_LOCKING_OK 0x00000002UL
+
+typedef CK_C_INITIALIZE_ARGS CK_PTR CK_C_INITIALIZE_ARGS_PTR;
+
+
+/* additional flags for parameters to functions */
+
+/* CKF_DONT_BLOCK is for the function C_WaitForSlotEvent */
+#define CKF_DONT_BLOCK 1
+
+/* CK_RSA_PKCS_MGF_TYPE is used to indicate the Message
+ * Generation Function (MGF) applied to a message block when
+ * formatting a message block for the PKCS #1 OAEP encryption
+ * scheme.
+ */
+typedef CK_ULONG CK_RSA_PKCS_MGF_TYPE;
+
+typedef CK_RSA_PKCS_MGF_TYPE CK_PTR CK_RSA_PKCS_MGF_TYPE_PTR;
+
+/* The following MGFs are defined */
+#define CKG_MGF1_SHA1 0x00000001UL
+#define CKG_MGF1_SHA256 0x00000002UL
+#define CKG_MGF1_SHA384 0x00000003UL
+#define CKG_MGF1_SHA512 0x00000004UL
+#define CKG_MGF1_SHA224 0x00000005UL
+
+/* CK_RSA_PKCS_OAEP_SOURCE_TYPE is used to indicate the source
+ * of the encoding parameter when formatting a message block
+ * for the PKCS #1 OAEP encryption scheme.
+ */
+typedef CK_ULONG CK_RSA_PKCS_OAEP_SOURCE_TYPE;
+
+typedef CK_RSA_PKCS_OAEP_SOURCE_TYPE CK_PTR CK_RSA_PKCS_OAEP_SOURCE_TYPE_PTR;
+
+/* The following encoding parameter sources are defined */
+#define CKZ_DATA_SPECIFIED 0x00000001UL
+
+/* CK_RSA_PKCS_OAEP_PARAMS provides the parameters to the
+ * CKM_RSA_PKCS_OAEP mechanism.
+ */
+typedef struct CK_RSA_PKCS_OAEP_PARAMS {
+ CK_MECHANISM_TYPE hashAlg;
+ CK_RSA_PKCS_MGF_TYPE mgf;
+ CK_RSA_PKCS_OAEP_SOURCE_TYPE source;
+ CK_VOID_PTR pSourceData;
+ CK_ULONG ulSourceDataLen;
+} CK_RSA_PKCS_OAEP_PARAMS;
+
+typedef CK_RSA_PKCS_OAEP_PARAMS CK_PTR CK_RSA_PKCS_OAEP_PARAMS_PTR;
+
+/* CK_RSA_PKCS_PSS_PARAMS provides the parameters to the
+ * CKM_RSA_PKCS_PSS mechanism(s).
+ */
+typedef struct CK_RSA_PKCS_PSS_PARAMS {
+ CK_MECHANISM_TYPE hashAlg;
+ CK_RSA_PKCS_MGF_TYPE mgf;
+ CK_ULONG sLen;
+} CK_RSA_PKCS_PSS_PARAMS;
+
+typedef CK_RSA_PKCS_PSS_PARAMS CK_PTR CK_RSA_PKCS_PSS_PARAMS_PTR;
+
+typedef CK_ULONG CK_EC_KDF_TYPE;
+
+/* The following EC Key Derivation Functions are defined */
+#define CKD_NULL 0x00000001UL
+#define CKD_SHA1_KDF 0x00000002UL
+
+/* The following X9.42 DH key derivation functions are defined */
+#define CKD_SHA1_KDF_ASN1 0x00000003UL
+#define CKD_SHA1_KDF_CONCATENATE 0x00000004UL
+#define CKD_SHA224_KDF 0x00000005UL
+#define CKD_SHA256_KDF 0x00000006UL
+#define CKD_SHA384_KDF 0x00000007UL
+#define CKD_SHA512_KDF 0x00000008UL
+#define CKD_CPDIVERSIFY_KDF 0x00000009UL
+
+
+/* CK_ECDH1_DERIVE_PARAMS provides the parameters to the
+ * CKM_ECDH1_DERIVE and CKM_ECDH1_COFACTOR_DERIVE mechanisms,
+ * where each party contributes one key pair.
+ */
+typedef struct CK_ECDH1_DERIVE_PARAMS {
+ CK_EC_KDF_TYPE kdf;
+ CK_ULONG ulSharedDataLen;
+ CK_BYTE_PTR pSharedData;
+ CK_ULONG ulPublicDataLen;
+ CK_BYTE_PTR pPublicData;
+} CK_ECDH1_DERIVE_PARAMS;
+
+typedef CK_ECDH1_DERIVE_PARAMS CK_PTR CK_ECDH1_DERIVE_PARAMS_PTR;
+
+/*
+ * CK_ECDH2_DERIVE_PARAMS provides the parameters to the
+ * CKM_ECMQV_DERIVE mechanism, where each party contributes two key pairs.
+ */
+typedef struct CK_ECDH2_DERIVE_PARAMS {
+ CK_EC_KDF_TYPE kdf;
+ CK_ULONG ulSharedDataLen;
+ CK_BYTE_PTR pSharedData;
+ CK_ULONG ulPublicDataLen;
+ CK_BYTE_PTR pPublicData;
+ CK_ULONG ulPrivateDataLen;
+ CK_OBJECT_HANDLE hPrivateData;
+ CK_ULONG ulPublicDataLen2;
+ CK_BYTE_PTR pPublicData2;
+} CK_ECDH2_DERIVE_PARAMS;
+
+typedef CK_ECDH2_DERIVE_PARAMS CK_PTR CK_ECDH2_DERIVE_PARAMS_PTR;
+
+typedef struct CK_ECMQV_DERIVE_PARAMS {
+ CK_EC_KDF_TYPE kdf;
+ CK_ULONG ulSharedDataLen;
+ CK_BYTE_PTR pSharedData;
+ CK_ULONG ulPublicDataLen;
+ CK_BYTE_PTR pPublicData;
+ CK_ULONG ulPrivateDataLen;
+ CK_OBJECT_HANDLE hPrivateData;
+ CK_ULONG ulPublicDataLen2;
+ CK_BYTE_PTR pPublicData2;
+ CK_OBJECT_HANDLE publicKey;
+} CK_ECMQV_DERIVE_PARAMS;
+
+typedef CK_ECMQV_DERIVE_PARAMS CK_PTR CK_ECMQV_DERIVE_PARAMS_PTR;
+
+/* Typedefs and defines for the CKM_X9_42_DH_KEY_PAIR_GEN and the
+ * CKM_X9_42_DH_PARAMETER_GEN mechanisms
+ */
+typedef CK_ULONG CK_X9_42_DH_KDF_TYPE;
+typedef CK_X9_42_DH_KDF_TYPE CK_PTR CK_X9_42_DH_KDF_TYPE_PTR;
+
+/* CK_X9_42_DH1_DERIVE_PARAMS provides the parameters to the
+ * CKM_X9_42_DH_DERIVE key derivation mechanism, where each party
+ * contributes one key pair
+ */
+typedef struct CK_X9_42_DH1_DERIVE_PARAMS {
+ CK_X9_42_DH_KDF_TYPE kdf;
+ CK_ULONG ulOtherInfoLen;
+ CK_BYTE_PTR pOtherInfo;
+ CK_ULONG ulPublicDataLen;
+ CK_BYTE_PTR pPublicData;
+} CK_X9_42_DH1_DERIVE_PARAMS;
+
+typedef struct CK_X9_42_DH1_DERIVE_PARAMS CK_PTR CK_X9_42_DH1_DERIVE_PARAMS_PTR;
+
+/* CK_X9_42_DH2_DERIVE_PARAMS provides the parameters to the
+ * CKM_X9_42_DH_HYBRID_DERIVE and CKM_X9_42_MQV_DERIVE key derivation
+ * mechanisms, where each party contributes two key pairs
+ */
+typedef struct CK_X9_42_DH2_DERIVE_PARAMS {
+ CK_X9_42_DH_KDF_TYPE kdf;
+ CK_ULONG ulOtherInfoLen;
+ CK_BYTE_PTR pOtherInfo;
+ CK_ULONG ulPublicDataLen;
+ CK_BYTE_PTR pPublicData;
+ CK_ULONG ulPrivateDataLen;
+ CK_OBJECT_HANDLE hPrivateData;
+ CK_ULONG ulPublicDataLen2;
+ CK_BYTE_PTR pPublicData2;
+} CK_X9_42_DH2_DERIVE_PARAMS;
+
+typedef CK_X9_42_DH2_DERIVE_PARAMS CK_PTR CK_X9_42_DH2_DERIVE_PARAMS_PTR;
+
+typedef struct CK_X9_42_MQV_DERIVE_PARAMS {
+ CK_X9_42_DH_KDF_TYPE kdf;
+ CK_ULONG ulOtherInfoLen;
+ CK_BYTE_PTR pOtherInfo;
+ CK_ULONG ulPublicDataLen;
+ CK_BYTE_PTR pPublicData;
+ CK_ULONG ulPrivateDataLen;
+ CK_OBJECT_HANDLE hPrivateData;
+ CK_ULONG ulPublicDataLen2;
+ CK_BYTE_PTR pPublicData2;
+ CK_OBJECT_HANDLE publicKey;
+} CK_X9_42_MQV_DERIVE_PARAMS;
+
+typedef CK_X9_42_MQV_DERIVE_PARAMS CK_PTR CK_X9_42_MQV_DERIVE_PARAMS_PTR;
+
+/* CK_KEA_DERIVE_PARAMS provides the parameters to the
+ * CKM_KEA_DERIVE mechanism
+ */
+typedef struct CK_KEA_DERIVE_PARAMS {
+ CK_BBOOL isSender;
+ CK_ULONG ulRandomLen;
+ CK_BYTE_PTR pRandomA;
+ CK_BYTE_PTR pRandomB;
+ CK_ULONG ulPublicDataLen;
+ CK_BYTE_PTR pPublicData;
+} CK_KEA_DERIVE_PARAMS;
+
+typedef CK_KEA_DERIVE_PARAMS CK_PTR CK_KEA_DERIVE_PARAMS_PTR;
+
+
+/* CK_RC2_PARAMS provides the parameters to the CKM_RC2_ECB and
+ * CKM_RC2_MAC mechanisms. An instance of CK_RC2_PARAMS just
+ * holds the effective keysize
+ */
+typedef CK_ULONG CK_RC2_PARAMS;
+
+typedef CK_RC2_PARAMS CK_PTR CK_RC2_PARAMS_PTR;
+
+
+/* CK_RC2_CBC_PARAMS provides the parameters to the CKM_RC2_CBC
+ * mechanism
+ */
+typedef struct CK_RC2_CBC_PARAMS {
+ CK_ULONG ulEffectiveBits; /* effective bits (1-1024) */
+ CK_BYTE iv[8]; /* IV for CBC mode */
+} CK_RC2_CBC_PARAMS;
+
+typedef CK_RC2_CBC_PARAMS CK_PTR CK_RC2_CBC_PARAMS_PTR;
+
+
+/* CK_RC2_MAC_GENERAL_PARAMS provides the parameters for the
+ * CKM_RC2_MAC_GENERAL mechanism
+ */
+typedef struct CK_RC2_MAC_GENERAL_PARAMS {
+ CK_ULONG ulEffectiveBits; /* effective bits (1-1024) */
+ CK_ULONG ulMacLength; /* Length of MAC in bytes */
+} CK_RC2_MAC_GENERAL_PARAMS;
+
+typedef CK_RC2_MAC_GENERAL_PARAMS CK_PTR \
+ CK_RC2_MAC_GENERAL_PARAMS_PTR;
+
+
+/* CK_RC5_PARAMS provides the parameters to the CKM_RC5_ECB and
+ * CKM_RC5_MAC mechanisms
+ */
+typedef struct CK_RC5_PARAMS {
+ CK_ULONG ulWordsize; /* wordsize in bits */
+ CK_ULONG ulRounds; /* number of rounds */
+} CK_RC5_PARAMS;
+
+typedef CK_RC5_PARAMS CK_PTR CK_RC5_PARAMS_PTR;
+
+
+/* CK_RC5_CBC_PARAMS provides the parameters to the CKM_RC5_CBC
+ * mechanism
+ */
+typedef struct CK_RC5_CBC_PARAMS {
+ CK_ULONG ulWordsize; /* wordsize in bits */
+ CK_ULONG ulRounds; /* number of rounds */
+ CK_BYTE_PTR pIv; /* pointer to IV */
+ CK_ULONG ulIvLen; /* length of IV in bytes */
+} CK_RC5_CBC_PARAMS;
+
+typedef CK_RC5_CBC_PARAMS CK_PTR CK_RC5_CBC_PARAMS_PTR;
+
+
+/* CK_RC5_MAC_GENERAL_PARAMS provides the parameters for the
+ * CKM_RC5_MAC_GENERAL mechanism
+ */
+typedef struct CK_RC5_MAC_GENERAL_PARAMS {
+ CK_ULONG ulWordsize; /* wordsize in bits */
+ CK_ULONG ulRounds; /* number of rounds */
+ CK_ULONG ulMacLength; /* Length of MAC in bytes */
+} CK_RC5_MAC_GENERAL_PARAMS;
+
+typedef CK_RC5_MAC_GENERAL_PARAMS CK_PTR \
+ CK_RC5_MAC_GENERAL_PARAMS_PTR;
+
+/* CK_MAC_GENERAL_PARAMS provides the parameters to most block
+ * ciphers' MAC_GENERAL mechanisms. Its value is the length of
+ * the MAC
+ */
+typedef CK_ULONG CK_MAC_GENERAL_PARAMS;
+
+typedef CK_MAC_GENERAL_PARAMS CK_PTR CK_MAC_GENERAL_PARAMS_PTR;
+
+typedef struct CK_DES_CBC_ENCRYPT_DATA_PARAMS {
+ CK_BYTE iv[8];
+ CK_BYTE_PTR pData;
+ CK_ULONG length;
+} CK_DES_CBC_ENCRYPT_DATA_PARAMS;
+
+typedef CK_DES_CBC_ENCRYPT_DATA_PARAMS CK_PTR CK_DES_CBC_ENCRYPT_DATA_PARAMS_PTR;
+
+typedef struct CK_AES_CBC_ENCRYPT_DATA_PARAMS {
+ CK_BYTE iv[16];
+ CK_BYTE_PTR pData;
+ CK_ULONG length;
+} CK_AES_CBC_ENCRYPT_DATA_PARAMS;
+
+typedef CK_AES_CBC_ENCRYPT_DATA_PARAMS CK_PTR CK_AES_CBC_ENCRYPT_DATA_PARAMS_PTR;
+
+/* CK_SKIPJACK_PRIVATE_WRAP_PARAMS provides the parameters to the
+ * CKM_SKIPJACK_PRIVATE_WRAP mechanism
+ */
+typedef struct CK_SKIPJACK_PRIVATE_WRAP_PARAMS {
+ CK_ULONG ulPasswordLen;
+ CK_BYTE_PTR pPassword;
+ CK_ULONG ulPublicDataLen;
+ CK_BYTE_PTR pPublicData;
+ CK_ULONG ulPAndGLen;
+ CK_ULONG ulQLen;
+ CK_ULONG ulRandomLen;
+ CK_BYTE_PTR pRandomA;
+ CK_BYTE_PTR pPrimeP;
+ CK_BYTE_PTR pBaseG;
+ CK_BYTE_PTR pSubprimeQ;
+} CK_SKIPJACK_PRIVATE_WRAP_PARAMS;
+
+typedef CK_SKIPJACK_PRIVATE_WRAP_PARAMS CK_PTR \
+ CK_SKIPJACK_PRIVATE_WRAP_PARAMS_PTR;
+
+
+/* CK_SKIPJACK_RELAYX_PARAMS provides the parameters to the
+ * CKM_SKIPJACK_RELAYX mechanism
+ */
+typedef struct CK_SKIPJACK_RELAYX_PARAMS {
+ CK_ULONG ulOldWrappedXLen;
+ CK_BYTE_PTR pOldWrappedX;
+ CK_ULONG ulOldPasswordLen;
+ CK_BYTE_PTR pOldPassword;
+ CK_ULONG ulOldPublicDataLen;
+ CK_BYTE_PTR pOldPublicData;
+ CK_ULONG ulOldRandomLen;
+ CK_BYTE_PTR pOldRandomA;
+ CK_ULONG ulNewPasswordLen;
+ CK_BYTE_PTR pNewPassword;
+ CK_ULONG ulNewPublicDataLen;
+ CK_BYTE_PTR pNewPublicData;
+ CK_ULONG ulNewRandomLen;
+ CK_BYTE_PTR pNewRandomA;
+} CK_SKIPJACK_RELAYX_PARAMS;
+
+typedef CK_SKIPJACK_RELAYX_PARAMS CK_PTR \
+ CK_SKIPJACK_RELAYX_PARAMS_PTR;
+
+
+typedef struct CK_PBE_PARAMS {
+ CK_BYTE_PTR pInitVector;
+ CK_UTF8CHAR_PTR pPassword;
+ CK_ULONG ulPasswordLen;
+ CK_BYTE_PTR pSalt;
+ CK_ULONG ulSaltLen;
+ CK_ULONG ulIteration;
+} CK_PBE_PARAMS;
+
+typedef CK_PBE_PARAMS CK_PTR CK_PBE_PARAMS_PTR;
+
+
+/* CK_KEY_WRAP_SET_OAEP_PARAMS provides the parameters to the
+ * CKM_KEY_WRAP_SET_OAEP mechanism
+ */
+typedef struct CK_KEY_WRAP_SET_OAEP_PARAMS {
+ CK_BYTE bBC; /* block contents byte */
+ CK_BYTE_PTR pX; /* extra data */
+ CK_ULONG ulXLen; /* length of extra data in bytes */
+} CK_KEY_WRAP_SET_OAEP_PARAMS;
+
+typedef CK_KEY_WRAP_SET_OAEP_PARAMS CK_PTR CK_KEY_WRAP_SET_OAEP_PARAMS_PTR;
+
+typedef struct CK_SSL3_RANDOM_DATA {
+ CK_BYTE_PTR pClientRandom;
+ CK_ULONG ulClientRandomLen;
+ CK_BYTE_PTR pServerRandom;
+ CK_ULONG ulServerRandomLen;
+} CK_SSL3_RANDOM_DATA;
+
+
+typedef struct CK_SSL3_MASTER_KEY_DERIVE_PARAMS {
+ CK_SSL3_RANDOM_DATA RandomInfo;
+ CK_VERSION_PTR pVersion;
+} CK_SSL3_MASTER_KEY_DERIVE_PARAMS;
+
+typedef struct CK_SSL3_MASTER_KEY_DERIVE_PARAMS CK_PTR \
+ CK_SSL3_MASTER_KEY_DERIVE_PARAMS_PTR;
+
+typedef struct CK_SSL3_KEY_MAT_OUT {
+ CK_OBJECT_HANDLE hClientMacSecret;
+ CK_OBJECT_HANDLE hServerMacSecret;
+ CK_OBJECT_HANDLE hClientKey;
+ CK_OBJECT_HANDLE hServerKey;
+ CK_BYTE_PTR pIVClient;
+ CK_BYTE_PTR pIVServer;
+} CK_SSL3_KEY_MAT_OUT;
+
+typedef CK_SSL3_KEY_MAT_OUT CK_PTR CK_SSL3_KEY_MAT_OUT_PTR;
+
+
+typedef struct CK_SSL3_KEY_MAT_PARAMS {
+ CK_ULONG ulMacSizeInBits;
+ CK_ULONG ulKeySizeInBits;
+ CK_ULONG ulIVSizeInBits;
+ CK_BBOOL bIsExport;
+ CK_SSL3_RANDOM_DATA RandomInfo;
+ CK_SSL3_KEY_MAT_OUT_PTR pReturnedKeyMaterial;
+} CK_SSL3_KEY_MAT_PARAMS;
+
+typedef CK_SSL3_KEY_MAT_PARAMS CK_PTR CK_SSL3_KEY_MAT_PARAMS_PTR;
+
+typedef struct CK_TLS_PRF_PARAMS {
+ CK_BYTE_PTR pSeed;
+ CK_ULONG ulSeedLen;
+ CK_BYTE_PTR pLabel;
+ CK_ULONG ulLabelLen;
+ CK_BYTE_PTR pOutput;
+ CK_ULONG_PTR pulOutputLen;
+} CK_TLS_PRF_PARAMS;
+
+typedef CK_TLS_PRF_PARAMS CK_PTR CK_TLS_PRF_PARAMS_PTR;
+
+typedef struct CK_WTLS_RANDOM_DATA {
+ CK_BYTE_PTR pClientRandom;
+ CK_ULONG ulClientRandomLen;
+ CK_BYTE_PTR pServerRandom;
+ CK_ULONG ulServerRandomLen;
+} CK_WTLS_RANDOM_DATA;
+
+typedef CK_WTLS_RANDOM_DATA CK_PTR CK_WTLS_RANDOM_DATA_PTR;
+
+typedef struct CK_WTLS_MASTER_KEY_DERIVE_PARAMS {
+ CK_MECHANISM_TYPE DigestMechanism;
+ CK_WTLS_RANDOM_DATA RandomInfo;
+ CK_BYTE_PTR pVersion;
+} CK_WTLS_MASTER_KEY_DERIVE_PARAMS;
+
+typedef CK_WTLS_MASTER_KEY_DERIVE_PARAMS CK_PTR \
+ CK_WTLS_MASTER_KEY_DERIVE_PARAMS_PTR;
+
+typedef struct CK_WTLS_PRF_PARAMS {
+ CK_MECHANISM_TYPE DigestMechanism;
+ CK_BYTE_PTR pSeed;
+ CK_ULONG ulSeedLen;
+ CK_BYTE_PTR pLabel;
+ CK_ULONG ulLabelLen;
+ CK_BYTE_PTR pOutput;
+ CK_ULONG_PTR pulOutputLen;
+} CK_WTLS_PRF_PARAMS;
+
+typedef CK_WTLS_PRF_PARAMS CK_PTR CK_WTLS_PRF_PARAMS_PTR;
+
+typedef struct CK_WTLS_KEY_MAT_OUT {
+ CK_OBJECT_HANDLE hMacSecret;
+ CK_OBJECT_HANDLE hKey;
+ CK_BYTE_PTR pIV;
+} CK_WTLS_KEY_MAT_OUT;
+
+typedef CK_WTLS_KEY_MAT_OUT CK_PTR CK_WTLS_KEY_MAT_OUT_PTR;
+
+typedef struct CK_WTLS_KEY_MAT_PARAMS {
+ CK_MECHANISM_TYPE DigestMechanism;
+ CK_ULONG ulMacSizeInBits;
+ CK_ULONG ulKeySizeInBits;
+ CK_ULONG ulIVSizeInBits;
+ CK_ULONG ulSequenceNumber;
+ CK_BBOOL bIsExport;
+ CK_WTLS_RANDOM_DATA RandomInfo;
+ CK_WTLS_KEY_MAT_OUT_PTR pReturnedKeyMaterial;
+} CK_WTLS_KEY_MAT_PARAMS;
+
+typedef CK_WTLS_KEY_MAT_PARAMS CK_PTR CK_WTLS_KEY_MAT_PARAMS_PTR;
+
+typedef struct CK_CMS_SIG_PARAMS {
+ CK_OBJECT_HANDLE certificateHandle;
+ CK_MECHANISM_PTR pSigningMechanism;
+ CK_MECHANISM_PTR pDigestMechanism;
+ CK_UTF8CHAR_PTR pContentType;
+ CK_BYTE_PTR pRequestedAttributes;
+ CK_ULONG ulRequestedAttributesLen;
+ CK_BYTE_PTR pRequiredAttributes;
+ CK_ULONG ulRequiredAttributesLen;
+} CK_CMS_SIG_PARAMS;
+
+typedef CK_CMS_SIG_PARAMS CK_PTR CK_CMS_SIG_PARAMS_PTR;
+
+typedef struct CK_KEY_DERIVATION_STRING_DATA {
+ CK_BYTE_PTR pData;
+ CK_ULONG ulLen;
+} CK_KEY_DERIVATION_STRING_DATA;
+
+typedef CK_KEY_DERIVATION_STRING_DATA CK_PTR \
+ CK_KEY_DERIVATION_STRING_DATA_PTR;
+
+
+/* The CK_EXTRACT_PARAMS is used for the
+ * CKM_EXTRACT_KEY_FROM_KEY mechanism. It specifies which bit
+ * of the base key should be used as the first bit of the
+ * derived key
+ */
+typedef CK_ULONG CK_EXTRACT_PARAMS;
+
+typedef CK_EXTRACT_PARAMS CK_PTR CK_EXTRACT_PARAMS_PTR;
+
+/* CK_PKCS5_PBKD2_PSEUDO_RANDOM_FUNCTION_TYPE is used to
+ * indicate the Pseudo-Random Function (PRF) used to generate
+ * key bits using PKCS #5 PBKDF2.
+ */
+typedef CK_ULONG CK_PKCS5_PBKD2_PSEUDO_RANDOM_FUNCTION_TYPE;
+
+typedef CK_PKCS5_PBKD2_PSEUDO_RANDOM_FUNCTION_TYPE CK_PTR \
+ CK_PKCS5_PBKD2_PSEUDO_RANDOM_FUNCTION_TYPE_PTR;
+
+#define CKP_PKCS5_PBKD2_HMAC_SHA1 0x00000001UL
+#define CKP_PKCS5_PBKD2_HMAC_GOSTR3411 0x00000002UL
+#define CKP_PKCS5_PBKD2_HMAC_SHA224 0x00000003UL
+#define CKP_PKCS5_PBKD2_HMAC_SHA256 0x00000004UL
+#define CKP_PKCS5_PBKD2_HMAC_SHA384 0x00000005UL
+#define CKP_PKCS5_PBKD2_HMAC_SHA512 0x00000006UL
+#define CKP_PKCS5_PBKD2_HMAC_SHA512_224 0x00000007UL
+#define CKP_PKCS5_PBKD2_HMAC_SHA512_256 0x00000008UL
+
+/* CK_PKCS5_PBKDF2_SALT_SOURCE_TYPE is used to indicate the
+ * source of the salt value when deriving a key using PKCS #5
+ * PBKDF2.
+ */
+typedef CK_ULONG CK_PKCS5_PBKDF2_SALT_SOURCE_TYPE;
+
+typedef CK_PKCS5_PBKDF2_SALT_SOURCE_TYPE CK_PTR \
+ CK_PKCS5_PBKDF2_SALT_SOURCE_TYPE_PTR;
+
+/* The following salt value sources are defined in PKCS #5 v2.0. */
+#define CKZ_SALT_SPECIFIED 0x00000001UL
+
+/* CK_PKCS5_PBKD2_PARAMS is a structure that provides the
+ * parameters to the CKM_PKCS5_PBKD2 mechanism.
+ */
+typedef struct CK_PKCS5_PBKD2_PARAMS {
+ CK_PKCS5_PBKDF2_SALT_SOURCE_TYPE saltSource;
+ CK_VOID_PTR pSaltSourceData;
+ CK_ULONG ulSaltSourceDataLen;
+ CK_ULONG iterations;
+ CK_PKCS5_PBKD2_PSEUDO_RANDOM_FUNCTION_TYPE prf;
+ CK_VOID_PTR pPrfData;
+ CK_ULONG ulPrfDataLen;
+ CK_UTF8CHAR_PTR pPassword;
+ CK_ULONG_PTR ulPasswordLen;
+} CK_PKCS5_PBKD2_PARAMS;
+
+typedef CK_PKCS5_PBKD2_PARAMS CK_PTR CK_PKCS5_PBKD2_PARAMS_PTR;
+
+/* CK_PKCS5_PBKD2_PARAMS2 is a corrected version of the CK_PKCS5_PBKD2_PARAMS
+ * structure that provides the parameters to the CKM_PKCS5_PBKD2 mechanism
+ * noting that the ulPasswordLen field is a CK_ULONG and not a CK_ULONG_PTR.
+ */
+typedef struct CK_PKCS5_PBKD2_PARAMS2 {
+ CK_PKCS5_PBKDF2_SALT_SOURCE_TYPE saltSource;
+ CK_VOID_PTR pSaltSourceData;
+ CK_ULONG ulSaltSourceDataLen;
+ CK_ULONG iterations;
+ CK_PKCS5_PBKD2_PSEUDO_RANDOM_FUNCTION_TYPE prf;
+ CK_VOID_PTR pPrfData;
+ CK_ULONG ulPrfDataLen;
+ CK_UTF8CHAR_PTR pPassword;
+ CK_ULONG ulPasswordLen;
+} CK_PKCS5_PBKD2_PARAMS2;
+
+typedef CK_PKCS5_PBKD2_PARAMS2 CK_PTR CK_PKCS5_PBKD2_PARAMS2_PTR;
+
+typedef CK_ULONG CK_OTP_PARAM_TYPE;
+typedef CK_OTP_PARAM_TYPE CK_PARAM_TYPE; /* backward compatibility */
+
+typedef struct CK_OTP_PARAM {
+ CK_OTP_PARAM_TYPE type;
+ CK_VOID_PTR pValue;
+ CK_ULONG ulValueLen;
+} CK_OTP_PARAM;
+
+typedef CK_OTP_PARAM CK_PTR CK_OTP_PARAM_PTR;
+
+typedef struct CK_OTP_PARAMS {
+ CK_OTP_PARAM_PTR pParams;
+ CK_ULONG ulCount;
+} CK_OTP_PARAMS;
+
+typedef CK_OTP_PARAMS CK_PTR CK_OTP_PARAMS_PTR;
+
+typedef struct CK_OTP_SIGNATURE_INFO {
+ CK_OTP_PARAM_PTR pParams;
+ CK_ULONG ulCount;
+} CK_OTP_SIGNATURE_INFO;
+
+typedef CK_OTP_SIGNATURE_INFO CK_PTR CK_OTP_SIGNATURE_INFO_PTR;
+
+#define CK_OTP_VALUE 0UL
+#define CK_OTP_PIN 1UL
+#define CK_OTP_CHALLENGE 2UL
+#define CK_OTP_TIME 3UL
+#define CK_OTP_COUNTER 4UL
+#define CK_OTP_FLAGS 5UL
+#define CK_OTP_OUTPUT_LENGTH 6UL
+#define CK_OTP_OUTPUT_FORMAT 7UL
+
+#define CKF_NEXT_OTP 0x00000001UL
+#define CKF_EXCLUDE_TIME 0x00000002UL
+#define CKF_EXCLUDE_COUNTER 0x00000004UL
+#define CKF_EXCLUDE_CHALLENGE 0x00000008UL
+#define CKF_EXCLUDE_PIN 0x00000010UL
+#define CKF_USER_FRIENDLY_OTP 0x00000020UL
+
+typedef struct CK_KIP_PARAMS {
+ CK_MECHANISM_PTR pMechanism;
+ CK_OBJECT_HANDLE hKey;
+ CK_BYTE_PTR pSeed;
+ CK_ULONG ulSeedLen;
+} CK_KIP_PARAMS;
+
+typedef CK_KIP_PARAMS CK_PTR CK_KIP_PARAMS_PTR;
+
+typedef struct CK_AES_CTR_PARAMS {
+ CK_ULONG ulCounterBits;
+ CK_BYTE cb[16];
+} CK_AES_CTR_PARAMS;
+
+typedef CK_AES_CTR_PARAMS CK_PTR CK_AES_CTR_PARAMS_PTR;
+
+typedef struct CK_GCM_PARAMS {
+ CK_BYTE_PTR pIv;
+ CK_ULONG ulIvLen;
+ CK_ULONG ulIvBits;
+ CK_BYTE_PTR pAAD;
+ CK_ULONG ulAADLen;
+ CK_ULONG ulTagBits;
+} CK_GCM_PARAMS;
+
+typedef CK_GCM_PARAMS CK_PTR CK_GCM_PARAMS_PTR;
+
+typedef struct CK_CCM_PARAMS {
+ CK_ULONG ulDataLen;
+ CK_BYTE_PTR pNonce;
+ CK_ULONG ulNonceLen;
+ CK_BYTE_PTR pAAD;
+ CK_ULONG ulAADLen;
+ CK_ULONG ulMACLen;
+} CK_CCM_PARAMS;
+
+typedef CK_CCM_PARAMS CK_PTR CK_CCM_PARAMS_PTR;
+
+/* Deprecated. Use CK_GCM_PARAMS */
+typedef struct CK_AES_GCM_PARAMS {
+ CK_BYTE_PTR pIv;
+ CK_ULONG ulIvLen;
+ CK_ULONG ulIvBits;
+ CK_BYTE_PTR pAAD;
+ CK_ULONG ulAADLen;
+ CK_ULONG ulTagBits;
+} CK_AES_GCM_PARAMS;
+
+typedef CK_AES_GCM_PARAMS CK_PTR CK_AES_GCM_PARAMS_PTR;
+
+/* Deprecated. Use CK_CCM_PARAMS */
+typedef struct CK_AES_CCM_PARAMS {
+ CK_ULONG ulDataLen;
+ CK_BYTE_PTR pNonce;
+ CK_ULONG ulNonceLen;
+ CK_BYTE_PTR pAAD;
+ CK_ULONG ulAADLen;
+ CK_ULONG ulMACLen;
+} CK_AES_CCM_PARAMS;
+
+typedef CK_AES_CCM_PARAMS CK_PTR CK_AES_CCM_PARAMS_PTR;
+
+typedef struct CK_CAMELLIA_CTR_PARAMS {
+ CK_ULONG ulCounterBits;
+ CK_BYTE cb[16];
+} CK_CAMELLIA_CTR_PARAMS;
+
+typedef CK_CAMELLIA_CTR_PARAMS CK_PTR CK_CAMELLIA_CTR_PARAMS_PTR;
+
+typedef struct CK_CAMELLIA_CBC_ENCRYPT_DATA_PARAMS {
+ CK_BYTE iv[16];
+ CK_BYTE_PTR pData;
+ CK_ULONG length;
+} CK_CAMELLIA_CBC_ENCRYPT_DATA_PARAMS;
+
+typedef CK_CAMELLIA_CBC_ENCRYPT_DATA_PARAMS CK_PTR \
+ CK_CAMELLIA_CBC_ENCRYPT_DATA_PARAMS_PTR;
+
+typedef struct CK_ARIA_CBC_ENCRYPT_DATA_PARAMS {
+ CK_BYTE iv[16];
+ CK_BYTE_PTR pData;
+ CK_ULONG length;
+} CK_ARIA_CBC_ENCRYPT_DATA_PARAMS;
+
+typedef CK_ARIA_CBC_ENCRYPT_DATA_PARAMS CK_PTR \
+ CK_ARIA_CBC_ENCRYPT_DATA_PARAMS_PTR;
+
+typedef struct CK_DSA_PARAMETER_GEN_PARAM {
+ CK_MECHANISM_TYPE hash;
+ CK_BYTE_PTR pSeed;
+ CK_ULONG ulSeedLen;
+ CK_ULONG ulIndex;
+} CK_DSA_PARAMETER_GEN_PARAM;
+
+typedef CK_DSA_PARAMETER_GEN_PARAM CK_PTR CK_DSA_PARAMETER_GEN_PARAM_PTR;
+
+typedef struct CK_ECDH_AES_KEY_WRAP_PARAMS {
+ CK_ULONG ulAESKeyBits;
+ CK_EC_KDF_TYPE kdf;
+ CK_ULONG ulSharedDataLen;
+ CK_BYTE_PTR pSharedData;
+} CK_ECDH_AES_KEY_WRAP_PARAMS;
+
+typedef CK_ECDH_AES_KEY_WRAP_PARAMS CK_PTR CK_ECDH_AES_KEY_WRAP_PARAMS_PTR;
+
+typedef CK_ULONG CK_JAVA_MIDP_SECURITY_DOMAIN;
+
+typedef CK_ULONG CK_CERTIFICATE_CATEGORY;
+
+typedef struct CK_RSA_AES_KEY_WRAP_PARAMS {
+ CK_ULONG ulAESKeyBits;
+ CK_RSA_PKCS_OAEP_PARAMS_PTR pOAEPParams;
+} CK_RSA_AES_KEY_WRAP_PARAMS;
+
+typedef CK_RSA_AES_KEY_WRAP_PARAMS CK_PTR CK_RSA_AES_KEY_WRAP_PARAMS_PTR;
+
+typedef struct CK_TLS12_MASTER_KEY_DERIVE_PARAMS {
+ CK_SSL3_RANDOM_DATA RandomInfo;
+ CK_VERSION_PTR pVersion;
+ CK_MECHANISM_TYPE prfHashMechanism;
+} CK_TLS12_MASTER_KEY_DERIVE_PARAMS;
+
+typedef CK_TLS12_MASTER_KEY_DERIVE_PARAMS CK_PTR \
+ CK_TLS12_MASTER_KEY_DERIVE_PARAMS_PTR;
+
+typedef struct CK_TLS12_KEY_MAT_PARAMS {
+ CK_ULONG ulMacSizeInBits;
+ CK_ULONG ulKeySizeInBits;
+ CK_ULONG ulIVSizeInBits;
+ CK_BBOOL bIsExport;
+ CK_SSL3_RANDOM_DATA RandomInfo;
+ CK_SSL3_KEY_MAT_OUT_PTR pReturnedKeyMaterial;
+ CK_MECHANISM_TYPE prfHashMechanism;
+} CK_TLS12_KEY_MAT_PARAMS;
+
+typedef CK_TLS12_KEY_MAT_PARAMS CK_PTR CK_TLS12_KEY_MAT_PARAMS_PTR;
+
+typedef struct CK_TLS_KDF_PARAMS {
+ CK_MECHANISM_TYPE prfMechanism;
+ CK_BYTE_PTR pLabel;
+ CK_ULONG ulLabelLength;
+ CK_SSL3_RANDOM_DATA RandomInfo;
+ CK_BYTE_PTR pContextData;
+ CK_ULONG ulContextDataLength;
+} CK_TLS_KDF_PARAMS;
+
+typedef CK_TLS_KDF_PARAMS CK_PTR CK_TLS_KDF_PARAMS_PTR;
+
+typedef struct CK_TLS_MAC_PARAMS {
+ CK_MECHANISM_TYPE prfHashMechanism;
+ CK_ULONG ulMacLength;
+ CK_ULONG ulServerOrClient;
+} CK_TLS_MAC_PARAMS;
+
+typedef CK_TLS_MAC_PARAMS CK_PTR CK_TLS_MAC_PARAMS_PTR;
+
+typedef struct CK_GOSTR3410_DERIVE_PARAMS {
+ CK_EC_KDF_TYPE kdf;
+ CK_BYTE_PTR pPublicData;
+ CK_ULONG ulPublicDataLen;
+ CK_BYTE_PTR pUKM;
+ CK_ULONG ulUKMLen;
+} CK_GOSTR3410_DERIVE_PARAMS;
+
+typedef CK_GOSTR3410_DERIVE_PARAMS CK_PTR CK_GOSTR3410_DERIVE_PARAMS_PTR;
+
+typedef struct CK_GOSTR3410_KEY_WRAP_PARAMS {
+ CK_BYTE_PTR pWrapOID;
+ CK_ULONG ulWrapOIDLen;
+ CK_BYTE_PTR pUKM;
+ CK_ULONG ulUKMLen;
+ CK_OBJECT_HANDLE hKey;
+} CK_GOSTR3410_KEY_WRAP_PARAMS;
+
+typedef CK_GOSTR3410_KEY_WRAP_PARAMS CK_PTR CK_GOSTR3410_KEY_WRAP_PARAMS_PTR;
+
+typedef struct CK_SEED_CBC_ENCRYPT_DATA_PARAMS {
+ CK_BYTE iv[16];
+ CK_BYTE_PTR pData;
+ CK_ULONG length;
+} CK_SEED_CBC_ENCRYPT_DATA_PARAMS;
+
+typedef CK_SEED_CBC_ENCRYPT_DATA_PARAMS CK_PTR \
+ CK_SEED_CBC_ENCRYPT_DATA_PARAMS_PTR;
+
+#endif /* _PKCS11T_H_ */
+
diff --git a/comm/third_party/botan/src/lib/prov/tpm/info.txt b/comm/third_party/botan/src/lib/prov/tpm/info.txt
new file mode 100644
index 0000000000..3993efc530
--- /dev/null
+++ b/comm/third_party/botan/src/lib/prov/tpm/info.txt
@@ -0,0 +1,16 @@
+<defines>
+TPM -> 20151126
+</defines>
+
+load_on vendor
+
+<libs>
+all -> tspi
+</libs>
+
+<requires>
+uuid
+hash_id
+rsa
+rng
+</requires>
diff --git a/comm/third_party/botan/src/lib/prov/tpm/tpm.cpp b/comm/third_party/botan/src/lib/prov/tpm/tpm.cpp
new file mode 100644
index 0000000000..dec2316b10
--- /dev/null
+++ b/comm/third_party/botan/src/lib/prov/tpm/tpm.cpp
@@ -0,0 +1,460 @@
+/*
+* TPM 1.2 interface
+* (C) 2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/tpm.h>
+#include <botan/rsa.h>
+#include <botan/hash.h>
+#include <botan/hash_id.h>
+#include <botan/der_enc.h>
+#include <botan/workfactor.h>
+#include <botan/pk_ops.h>
+#include <sstream>
+
+#include <tss/platform.h>
+#include <tss/tspi.h>
+#include <trousers/trousers.h>
+
+// TODO: dynamically load the TPM libraries?
+
+namespace Botan {
+
+namespace {
+
+void tss_error(TSS_RESULT res, const char* expr, const char* file, int line)
+ {
+ std::ostringstream err;
+ err << "TPM error " << Trspi_Error_String(res)
+ << " layer " << Trspi_Error_Layer(res)
+ << " in " << expr << " at " << file << ":" << line;
+
+ throw TPM_Error(err.str());
+ }
+
+TSS_FLAG bit_flag(size_t bits)
+ {
+ switch(bits)
+ {
+ // 512 supported, but ignored and rejected here
+ case 1024:
+ return TSS_KEY_SIZE_1024;
+ case 2048:
+ return TSS_KEY_SIZE_2048;
+
+ // Most? v1.2 TPMs only support 1024 and 2048 bit keys ...
+ case 4096:
+ return TSS_KEY_SIZE_4096;
+ case 8192:
+ return TSS_KEY_SIZE_8192;
+ case 16384:
+ return TSS_KEY_SIZE_16384;
+ default:
+ throw Invalid_Argument("Unsupported TPM key size " + std::to_string(bits));
+ }
+ }
+
+#if 0
+bool is_srk_uuid(const UUID& uuid)
+ {
+ static const uint8_t srk[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 };
+ const std::vector<uint8_t>& b = uuid.binary_value();
+ return (b.size() == 16 && same_mem(b.data(), srk, 16));
+ }
+#endif
+
+#define TSPI_CHECK_SUCCESS(expr) do { \
+ TSS_RESULT res = expr; \
+ if(res != TSS_SUCCESS) \
+ tss_error(res, #expr, __FILE__, __LINE__); \
+ } while(0)
+
+std::vector<uint8_t> get_obj_attr(TSS_HCONTEXT ctx,
+ TSS_HOBJECT obj,
+ TSS_FLAG flag,
+ TSS_FLAG sub_flag)
+ {
+ BYTE *data = nullptr;
+ UINT32 data_len = 0;
+ TSPI_CHECK_SUCCESS(::Tspi_GetAttribData(obj, flag, sub_flag, &data_len, &data));
+
+ std::vector<uint8_t> r(data, data + data_len);
+
+ TSPI_CHECK_SUCCESS(::Tspi_Context_FreeMemory(ctx, data));
+
+ return r;
+ }
+
+void set_policy_secret(TSS_HPOLICY policy, const char* secret)
+ {
+ if(secret)
+ {
+ BYTE* as_b = const_cast<BYTE*>(reinterpret_cast<const BYTE*>(secret));
+ TSPI_CHECK_SUCCESS(::Tspi_Policy_SetSecret(policy,
+ TSS_SECRET_MODE_PLAIN,
+ std::strlen(secret),
+ as_b));
+ }
+ else
+ {
+ static const uint8_t nullpass[20] = { 0 };
+
+ TSPI_CHECK_SUCCESS(::Tspi_Policy_SetSecret(policy,
+ TSS_SECRET_MODE_SHA1,
+ sizeof(nullpass),
+ const_cast<BYTE*>(nullpass)));
+ }
+ }
+
+TSS_UUID to_tss_uuid(const UUID& uuid)
+ {
+ static_assert(sizeof(TSS_UUID) == 16, "Expected size of packed UUID");
+
+ TSS_UUID tss_uuid;
+ typecast_copy(tss_uuid, uuid.binary_value().data());
+ return tss_uuid;
+ }
+
+UUID from_tss_uuid(const TSS_UUID& tss_uuid)
+ {
+ static_assert(sizeof(TSS_UUID) == 16, "Expected size of packed UUID");
+
+ std::vector<uint8_t> mem(16);
+ typecast_copy(mem.data(), tss_uuid);
+ UUID uuid(std::move(mem));
+ return uuid;
+ }
+
+TPM_Storage_Type storage_type_from_tss_flag(TSS_FLAG flag)
+ {
+ if(flag == TSS_PS_TYPE_USER)
+ return TPM_Storage_Type::User;
+ else if(flag == TSS_PS_TYPE_SYSTEM)
+ return TPM_Storage_Type::System;
+ else
+ throw TPM_Error("Invalid storage flag " + std::to_string(flag));
+ }
+
+std::string format_url(const UUID& uuid, TPM_Storage_Type storage)
+ {
+ std::string storage_str = (storage == TPM_Storage_Type::User) ? "user" : "system";
+ return "tpmkey:uuid=" + uuid.to_string() + ";storage=" + storage_str;
+ }
+
+std::string format_url(const TSS_UUID& tss_uuid, TSS_FLAG store_type)
+ {
+ UUID uuid = from_tss_uuid(tss_uuid);
+
+ return format_url(from_tss_uuid(tss_uuid),
+ storage_type_from_tss_flag(store_type));
+ }
+
+}
+
+TPM_Context::TPM_Context(pin_cb cb, const char* srk_password) :
+ m_pin_cb(cb),
+ m_srk_policy(0)
+ {
+ TSPI_CHECK_SUCCESS(::Tspi_Context_Create(&m_ctx));
+ TSPI_CHECK_SUCCESS(::Tspi_Context_Connect(m_ctx, nullptr));
+
+ TSPI_CHECK_SUCCESS(::Tspi_Context_GetTpmObject(m_ctx, &m_tpm));
+
+ const TSS_UUID SRK_UUID = TSS_UUID_SRK;
+
+ TSPI_CHECK_SUCCESS(::Tspi_Context_LoadKeyByUUID(m_ctx, TSS_PS_TYPE_SYSTEM, SRK_UUID, &m_srk));
+
+ TSPI_CHECK_SUCCESS(::Tspi_GetPolicyObject(m_srk, TSS_POLICY_USAGE, &m_srk_policy));
+ set_policy_secret(m_srk_policy, srk_password);
+
+ // TODO: do we have to cache it?
+ // TODO: try to use SRK with null, if it fails call the pin cb?
+ }
+
+TPM_Context::~TPM_Context()
+ {
+ TSPI_CHECK_SUCCESS(::Tspi_Context_CloseObject(m_ctx, m_srk));
+ //TSPI_CHECK_SUCCESS(::Tspi_Context_CloseObject(m_ctx, m_tpm));
+ TSPI_CHECK_SUCCESS(::Tspi_Context_Close(m_srk_policy));
+ TSPI_CHECK_SUCCESS(::Tspi_Context_Close(m_ctx));
+ }
+
+uint32_t TPM_Context::current_counter()
+ {
+ uint32_t r = 0;
+ TSPI_CHECK_SUCCESS(::Tspi_TPM_ReadCounter(m_tpm, &r));
+ return r;
+ }
+
+void TPM_Context::gen_random(uint8_t out[], size_t out_len)
+ {
+ BYTE* mem;
+ TSPI_CHECK_SUCCESS(::Tspi_TPM_GetRandom(m_tpm, out_len, &mem));
+ copy_mem(out, reinterpret_cast<const uint8_t*>(mem), out_len);
+ TSPI_CHECK_SUCCESS(::Tspi_Context_FreeMemory(m_ctx, mem));
+ }
+
+void TPM_Context::stir_random(const uint8_t in[], size_t in_len)
+ {
+ TSPI_CHECK_SUCCESS(::Tspi_TPM_StirRandom(m_tpm, in_len, const_cast<BYTE*>(in)));
+ }
+
+TPM_PrivateKey::TPM_PrivateKey(TPM_Context& ctx, size_t bits,
+ const char* key_password) : m_ctx(ctx)
+ {
+ // TODO: can also do OAEP decryption via binding keys
+ // TODO: offer signing, binding (decrypt), or legacy (sign + decrypt) keys?
+
+ TSS_FLAG key_flags = bit_flag(bits) | TSS_KEY_VOLATILE | TSS_KEY_TYPE_SIGNING;
+
+ TSS_HKEY key;
+ TSPI_CHECK_SUCCESS(::Tspi_Context_CreateObject(m_ctx.handle(), TSS_OBJECT_TYPE_RSAKEY, key_flags, &key));
+
+ TSPI_CHECK_SUCCESS(::Tspi_SetAttribUint32(key, TSS_TSPATTRIB_KEY_INFO,
+ TSS_TSPATTRIB_KEYINFO_SIGSCHEME,
+ TSS_SS_RSASSAPKCS1V15_DER));
+
+ TSS_HPOLICY policy;
+ TSPI_CHECK_SUCCESS(::Tspi_Context_CreateObject(m_ctx.handle(), TSS_OBJECT_TYPE_POLICY, TSS_POLICY_USAGE, &policy));
+ set_policy_secret(policy, key_password);
+ TSPI_CHECK_SUCCESS(::Tspi_Policy_AssignToObject(policy, key));
+
+ TSPI_CHECK_SUCCESS(::Tspi_Key_CreateKey(key, ctx.srk(), 0));
+ m_key = key;
+ }
+
+// reference a registered TPM key
+TPM_PrivateKey::TPM_PrivateKey(TPM_Context& ctx, const std::string& uuid_str,
+ TPM_Storage_Type storage_type) :
+ m_ctx(ctx),
+ m_uuid(uuid_str),
+ m_storage(storage_type)
+ {
+ const TSS_FLAG key_ps_type =
+ (m_storage == TPM_Storage_Type::User) ? TSS_PS_TYPE_USER : TSS_PS_TYPE_SYSTEM;
+
+ TSPI_CHECK_SUCCESS(::Tspi_Context_LoadKeyByUUID(m_ctx.handle(),
+ key_ps_type,
+ to_tss_uuid(m_uuid),
+ &m_key));
+ }
+
+TPM_PrivateKey::TPM_PrivateKey(TPM_Context& ctx,
+ const std::vector<uint8_t>& blob) : m_ctx(ctx)
+ {
+ TSPI_CHECK_SUCCESS(::Tspi_Context_LoadKeyByBlob(m_ctx.handle(), m_ctx.srk(), blob.size(),
+ const_cast<uint8_t*>(blob.data()),
+ &m_key));
+
+ //TSPI_CHECK_SUCCESS(::Tspi_Key_LoadKey(m_key, m_ctx.srk()));
+ }
+
+std::string TPM_PrivateKey::register_key(TPM_Storage_Type storage_type)
+ {
+ if(!m_uuid.is_valid())
+ {
+ TPM_RNG rng(ctx()); // use system_rng or arg RNG& instead?
+ m_uuid = UUID(rng);
+ m_storage = storage_type;
+
+ const TSS_UUID key_uuid = to_tss_uuid(m_uuid);
+ const TSS_FLAG key_ps_type =
+ (storage_type == TPM_Storage_Type::User) ? TSS_PS_TYPE_USER : TSS_PS_TYPE_SYSTEM;
+
+ const TSS_UUID srk_uuid = TSS_UUID_SRK;
+
+ TSPI_CHECK_SUCCESS(::Tspi_Context_RegisterKey(m_ctx.handle(),
+ m_key,
+ key_ps_type,
+ key_uuid,
+ TSS_PS_TYPE_SYSTEM,
+ srk_uuid));
+
+ }
+
+ // Presumably we could re-register in the other store and same UUID
+ // Doesn't seem like what is desired most of the time here
+ if(storage_type != m_storage)
+ {
+ throw TPM_Error("TPM key " + m_uuid.to_string() +
+ " already registered with different storage type");
+ }
+
+ return format_url(m_uuid, m_storage);
+ }
+
+std::vector<std::string> TPM_PrivateKey::registered_keys(TPM_Context& ctx)
+ {
+ TSS_KM_KEYINFO2* key_info;
+ UINT32 key_info_size;
+
+ // TODO: does the PS type matter here at all?
+ TSPI_CHECK_SUCCESS(::Tspi_Context_GetRegisteredKeysByUUID2(ctx.handle(),
+ TSS_PS_TYPE_SYSTEM,
+ nullptr,
+ &key_info_size,
+ &key_info));
+
+ std::vector<std::string> r(key_info_size);
+
+ for(size_t i = 0; i != key_info_size; ++i)
+ {
+ r[i] = format_url(key_info[i].keyUUID, key_info[i].persistentStorageType);
+ }
+
+ // TODO: are we supposed to free this memory and if so how?
+ //TSPI_CHECK_SUCCESS(::Tspi_Context_FreeMemory(ctx.handle(), key_info));
+
+ return r;
+ }
+
+BigInt TPM_PrivateKey::get_n() const
+ {
+ if(m_n == 0)
+ {
+ m_n = BigInt::decode(get_obj_attr(m_ctx.handle(), m_key,
+ TSS_TSPATTRIB_RSAKEY_INFO,
+ TSS_TSPATTRIB_KEYINFO_RSA_MODULUS));
+ }
+
+ return m_n;
+ }
+
+BigInt TPM_PrivateKey::get_e() const
+ {
+ if(m_e == 0)
+ {
+ m_e = BigInt::decode(get_obj_attr(m_ctx.handle(), m_key,
+ TSS_TSPATTRIB_RSAKEY_INFO,
+ TSS_TSPATTRIB_KEYINFO_RSA_EXPONENT));
+ }
+
+ return m_e;
+ }
+
+size_t TPM_PrivateKey::estimated_strength() const
+ {
+ return if_work_factor(key_length());
+ }
+
+size_t TPM_PrivateKey::key_length() const
+ {
+ return get_n().bits();
+ }
+
+AlgorithmIdentifier TPM_PrivateKey::algorithm_identifier() const
+ {
+ return AlgorithmIdentifier(get_oid(),
+ AlgorithmIdentifier::USE_NULL_PARAM);
+ }
+
+std::vector<uint8_t> TPM_PrivateKey::public_key_bits() const
+ {
+ std::vector<uint8_t> bits;
+ DER_Encoder(bits)
+ .start_cons(SEQUENCE)
+ .encode(get_n())
+ .encode(get_e())
+ .end_cons();
+ return bits;
+ }
+
+secure_vector<uint8_t> TPM_PrivateKey::private_key_bits() const
+ {
+ throw TPM_Error("Private key export not supported for TPM keys");
+ }
+
+std::vector<uint8_t> TPM_PrivateKey::export_blob() const
+ {
+ return get_obj_attr(m_ctx.handle(), m_key,
+ TSS_TSPATTRIB_KEY_BLOB,
+ TSS_TSPATTRIB_KEYBLOB_BLOB);
+ }
+
+std::unique_ptr<Public_Key> TPM_PrivateKey::public_key() const
+ {
+ return std::unique_ptr<Public_Key>(new RSA_PublicKey(get_n(), get_e()));
+ }
+
+bool TPM_PrivateKey::check_key(RandomNumberGenerator&, bool) const
+ {
+ return true; // TODO do a kat or pairwise check
+ }
+
+namespace {
+
+class TPM_Signing_Operation final : public PK_Ops::Signature
+ {
+ public:
+ TPM_Signing_Operation(const TPM_PrivateKey& key,
+ const std::string& hash_name) :
+ m_key(key),
+ m_hash(HashFunction::create(hash_name)),
+ m_hash_id(pkcs_hash_id(hash_name))
+ {
+ }
+
+ size_t signature_length() const override
+ {
+ return m_key.get_n().bytes();
+ }
+
+ void update(const uint8_t msg[], size_t msg_len) override
+ {
+ m_hash->update(msg, msg_len);
+ }
+
+ secure_vector<uint8_t> sign(RandomNumberGenerator&) override
+ {
+ /*
+ * v1.2 TPMs will only sign with PKCS #1 v1.5 padding. SHA-1 is built
+ * in, all other hash inputs (TSS_HASH_OTHER) are treated as the
+ * concatenation of the hash OID and hash value and signed with just the
+ * 01FFFF... prefix. Even when using SHA-1 we compute the hash locally
+ * since it is going to be much faster than pushing data over the LPC bus.
+ */
+ secure_vector<uint8_t> msg_hash = m_hash->final();
+
+ std::vector<uint8_t> id_and_msg;
+ id_and_msg.reserve(m_hash_id.size() + msg_hash.size());
+ id_and_msg.insert(id_and_msg.end(), m_hash_id.begin(), m_hash_id.end());
+ id_and_msg.insert(id_and_msg.end(), msg_hash.begin(), msg_hash.end());
+
+ TSS_HCONTEXT ctx = m_key.ctx().handle();
+ TSS_HHASH tpm_hash;
+ TSPI_CHECK_SUCCESS(::Tspi_Context_CreateObject(ctx, TSS_OBJECT_TYPE_HASH, TSS_HASH_OTHER, &tpm_hash));
+ TSPI_CHECK_SUCCESS(::Tspi_Hash_SetHashValue(tpm_hash, id_and_msg.size(), id_and_msg.data()));
+
+ BYTE* sig_bytes = nullptr;
+ UINT32 sig_len = 0;
+ TSPI_CHECK_SUCCESS(::Tspi_Hash_Sign(tpm_hash, m_key.handle(), &sig_len, &sig_bytes));
+ secure_vector<uint8_t> sig(sig_bytes, sig_bytes + sig_len);
+
+ // TODO: RAII for Context_FreeMemory
+ TSPI_CHECK_SUCCESS(::Tspi_Context_FreeMemory(ctx, sig_bytes));
+
+ // TODO: RAII for Context_CloseObject
+ TSPI_CHECK_SUCCESS(::Tspi_Context_CloseObject(ctx, tpm_hash));
+
+ return sig;
+ }
+
+ private:
+ const TPM_PrivateKey& m_key;
+ std::unique_ptr<HashFunction> m_hash;
+ std::vector<uint8_t> m_hash_id;
+ };
+
+}
+
+std::unique_ptr<PK_Ops::Signature>
+TPM_PrivateKey::create_signature_op(RandomNumberGenerator& /*rng*/,
+ const std::string& params,
+ const std::string& /*provider*/) const
+ {
+ return std::unique_ptr<PK_Ops::Signature>(new TPM_Signing_Operation(*this, params));
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/prov/tpm/tpm.h b/comm/third_party/botan/src/lib/prov/tpm/tpm.h
new file mode 100644
index 0000000000..8a25458b7c
--- /dev/null
+++ b/comm/third_party/botan/src/lib/prov/tpm/tpm.h
@@ -0,0 +1,194 @@
+
+/*
+* TPM 1.2 interface
+* (C) 2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_TPM_H_
+#define BOTAN_TPM_H_
+
+#include <botan/exceptn.h>
+#include <botan/pk_keys.h>
+#include <botan/bigint.h>
+#include <botan/rng.h>
+#include <botan/uuid.h>
+#include <functional>
+
+//TODO remove this
+#include <tss/tspi.h>
+
+namespace Botan {
+
+class BOTAN_PUBLIC_API(2,0) TPM_Error final : public Exception
+ {
+ public:
+ TPM_Error(const std::string& err) : Exception(err) {}
+ ErrorType error_type() const noexcept override { return ErrorType::TPMError; }
+ };
+
+/**
+* Creates a connection to the TPM. All other TPM types take and hold
+* a TPM_Context reference, so all other objects must be deallocated
+* before ~TPM_Context runs.
+*
+* Use nullptr for the srk_password to indicate the well known secret
+* (ie, an unencrypted SRK). This is usually what you want.
+*
+* TODO: handling owner password?
+*/
+class BOTAN_PUBLIC_API(2,0) TPM_Context final
+ {
+ public:
+ /**
+ * User callback for getting the PIN. Will be passed the best available
+ * description of what we are attempting to load.
+ */
+ typedef std::function<std::string (std::string)> pin_cb;
+
+ TPM_Context(pin_cb cb, const char* srk_password);
+
+ ~TPM_Context();
+
+ // Get data from the TPM's RNG, whatever that is
+ void gen_random(uint8_t out[], size_t out_len);
+
+ // Uses Tspi_TPM_StirRandom to add data to TPM's internal pool
+ void stir_random(const uint8_t in[], size_t in_len);
+
+ std::string get_user_pin(const std::string& who)
+ {
+ return m_pin_cb(who);
+ }
+
+ uint32_t current_counter();
+
+ TSS_HCONTEXT handle() const { return m_ctx; }
+ TSS_HKEY srk() const { return m_srk; }
+
+ private:
+ std::function<std::string (std::string)> m_pin_cb;
+ TSS_HCONTEXT m_ctx;
+ TSS_HKEY m_srk;
+ TSS_HTPM m_tpm;
+ TSS_HPOLICY m_srk_policy;
+ };
+
+class BOTAN_PUBLIC_API(2,0) TPM_RNG final : public Hardware_RNG
+ {
+ public:
+ TPM_RNG(TPM_Context& ctx) : m_ctx(ctx) {}
+
+ bool accepts_input() const override { return true; }
+
+ void add_entropy(const uint8_t in[], size_t in_len) override
+ {
+ m_ctx.stir_random(in, in_len);
+ }
+
+ void randomize(uint8_t out[], size_t out_len) override
+ {
+ m_ctx.gen_random(out, out_len);
+ }
+
+ std::string name() const override { return "TPM_RNG"; }
+
+ bool is_seeded() const override { return true; }
+
+ private:
+ TPM_Context& m_ctx;
+};
+
+enum class TPM_Storage_Type { User, System };
+
+/*
+* Also implements the public interface, but does not have usable
+* TODO: derive from RSA_PublicKey???
+*/
+class BOTAN_PUBLIC_API(2,0) TPM_PrivateKey final : public Private_Key
+ {
+ public:
+ // TODO: key import?
+
+ /*
+ * Create a new key on the TPM parented to the SRK
+ * @param bits must be 1024 or 2048
+ */
+ TPM_PrivateKey(TPM_Context& ctx, size_t bits, const char* key_password);
+
+ // reference an existing TPM key using URL syntax from GnuTLS
+ // "tpmkey:uuid=79f07ca9-73ac-478a-9093-11ca6702e774;storage=user"
+ //TPM_PrivateKey(TPM_Context& ctx, const std::string& tpm_url);
+
+ TPM_PrivateKey(TPM_Context& ctx,
+ const std::string& uuid,
+ TPM_Storage_Type storage_type);
+
+ TPM_PrivateKey(TPM_Context& ctx,
+ const std::vector<uint8_t>& blob);
+
+ /**
+ * If the key is not currently registered under a known UUID,
+ * generates a new random UUID and registers the key.
+ * Returns the access URL.
+ */
+ std::string register_key(TPM_Storage_Type storage_type);
+
+ /**
+ * Returns a copy of the public key
+ */
+ std::unique_ptr<Public_Key> public_key() const;
+
+ std::vector<uint8_t> export_blob() const;
+
+ TPM_Context& ctx() const { return m_ctx; }
+
+ TSS_HKEY handle() const { return m_key; }
+
+ /*
+ * Returns the list of all keys (in URL format) registered with the system
+ */
+ static std::vector<std::string> registered_keys(TPM_Context& ctx);
+
+ size_t estimated_strength() const override;
+
+ size_t key_length() const override;
+
+ AlgorithmIdentifier algorithm_identifier() const override;
+
+ std::vector<uint8_t> public_key_bits() const override;
+
+ secure_vector<uint8_t> private_key_bits() const override;
+
+ bool check_key(RandomNumberGenerator& rng, bool) const override;
+
+ BigInt get_n() const;
+
+ BigInt get_e() const;
+
+ std::string algo_name() const override { return "RSA"; } // ???
+
+ std::unique_ptr<PK_Ops::Signature>
+ create_signature_op(RandomNumberGenerator& rng,
+ const std::string& params,
+ const std::string& provider) const override;
+
+ private:
+ TPM_Context& m_ctx;
+ TSS_HKEY m_key;
+
+ // Only set for registered keys
+ UUID m_uuid;
+ TPM_Storage_Type m_storage;
+
+ // Lazily computed in get_n, get_e
+ mutable BigInt m_n, m_e;
+ };
+
+// TODO: NVRAM interface
+// TODO: PCR measurement, writing, key locking
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/psk_db/info.txt b/comm/third_party/botan/src/lib/psk_db/info.txt
new file mode 100644
index 0000000000..4b1d0747ed
--- /dev/null
+++ b/comm/third_party/botan/src/lib/psk_db/info.txt
@@ -0,0 +1,11 @@
+<defines>
+PSK_DB -> 20171119
+</defines>
+
+<requires>
+aes
+hmac
+base64
+sha2_32
+nist_keywrap
+</requires>
diff --git a/comm/third_party/botan/src/lib/psk_db/psk_db.cpp b/comm/third_party/botan/src/lib/psk_db/psk_db.cpp
new file mode 100644
index 0000000000..59fa768934
--- /dev/null
+++ b/comm/third_party/botan/src/lib/psk_db/psk_db.cpp
@@ -0,0 +1,105 @@
+/*
+* (C) 2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/psk_db.h>
+#include <botan/exceptn.h>
+#include <botan/nist_keywrap.h>
+#include <botan/base64.h>
+#include <botan/mac.h>
+#include <botan/block_cipher.h>
+
+namespace Botan {
+
+Encrypted_PSK_Database::Encrypted_PSK_Database(const secure_vector<uint8_t>& master_key)
+ {
+ m_cipher = BlockCipher::create_or_throw("AES-256");
+ m_hmac = MessageAuthenticationCode::create_or_throw("HMAC(SHA-256)");
+ m_hmac->set_key(master_key);
+
+ m_cipher->set_key(m_hmac->process("wrap"));
+ m_hmac->set_key(m_hmac->process("hmac"));
+ }
+
+Encrypted_PSK_Database::~Encrypted_PSK_Database()
+ {
+ // for ~unique_ptr
+ }
+
+std::set<std::string> Encrypted_PSK_Database::list_names() const
+ {
+ const std::set<std::string> encrypted_names = kv_get_all();
+
+ std::set<std::string> names;
+
+ for(std::string enc_name : encrypted_names)
+ {
+ try
+ {
+ const secure_vector<uint8_t> raw_name = base64_decode(enc_name);
+ const secure_vector<uint8_t> 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());
+ names.insert(pt_name);
+ }
+ catch(Invalid_Authentication_Tag&)
+ {
+ }
+ }
+
+ return names;
+ }
+
+void Encrypted_PSK_Database::remove(const std::string& name)
+ {
+ const std::vector<uint8_t> wrapped_name =
+ nist_key_wrap_padded(cast_char_ptr_to_uint8(name.data()),
+ name.size(),
+ *m_cipher);
+
+ this->kv_del(base64_encode(wrapped_name));
+ }
+
+secure_vector<uint8_t> Encrypted_PSK_Database::get(const std::string& name) const
+ {
+ const std::vector<uint8_t> wrapped_name =
+ nist_key_wrap_padded(cast_char_ptr_to_uint8(name.data()),
+ name.size(),
+ *m_cipher);
+
+ const std::string val_base64 = kv_get(base64_encode(wrapped_name));
+
+ if(val_base64.empty())
+ throw Invalid_Argument("Named PSK not located");
+
+ const secure_vector<uint8_t> val = base64_decode(val_base64);
+
+ std::unique_ptr<BlockCipher> wrap_cipher(m_cipher->clone());
+ wrap_cipher->set_key(m_hmac->process(wrapped_name));
+
+ return nist_key_unwrap_padded(val.data(), val.size(), *wrap_cipher);
+ }
+
+void Encrypted_PSK_Database::set(const std::string& name, const uint8_t val[], size_t len)
+ {
+ /*
+ * Both as a basic precaution wrt key seperation, 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<uint8_t> wrapped_name =
+ nist_key_wrap_padded(cast_char_ptr_to_uint8(name.data()),
+ name.size(),
+ *m_cipher);
+
+ std::unique_ptr<BlockCipher> wrap_cipher(m_cipher->clone());
+ wrap_cipher->set_key(m_hmac->process(wrapped_name));
+ const std::vector<uint8_t> wrapped_key = nist_key_wrap_padded(val, len, *wrap_cipher);
+
+ this->kv_set(base64_encode(wrapped_name), base64_encode(wrapped_key));
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/psk_db/psk_db.h b/comm/third_party/botan/src/lib/psk_db/psk_db.h
new file mode 100644
index 0000000000..06358935c1
--- /dev/null
+++ b/comm/third_party/botan/src/lib/psk_db/psk_db.h
@@ -0,0 +1,166 @@
+/*
+* (C) 2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_PSK_DB_H_
+#define BOTAN_PSK_DB_H_
+
+#include <botan/secmem.h>
+#include <memory>
+#include <string>
+#include <set>
+
+namespace Botan {
+
+class BlockCipher;
+class MessageAuthenticationCode;
+
+/**
+* This is an interface to a generic PSK (pre-shared key) database.
+* 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
+ {
+ public:
+ /**
+ * Return the set of names for which get() will return a value.
+ */
+ virtual std::set<std::string> list_names() const = 0;
+
+ /**
+ * Return the value associated with the specified @param name or otherwise
+ * throw an exception.
+ */
+ virtual secure_vector<uint8_t> get(const std::string& name) const = 0;
+
+ /**
+ * Set a value that can later be accessed with get().
+ * If name already exists in the database, the old value will be overwritten.
+ */
+ virtual void set(const std::string& name, const uint8_t psk[], size_t psk_len) = 0;
+
+ /**
+ * Remove a PSK from the database
+ */
+ virtual void remove(const std::string& name) = 0;
+
+ /**
+ * Returns if the values in the PSK database are encrypted. If
+ * false, saved values are being stored in plaintext.
+ */
+ virtual bool is_encrypted() const = 0;
+
+ /**
+ * Get a PSK in the form of a string (eg if the PSK is a password)
+ */
+ std::string get_str(const std::string& name) const
+ {
+ secure_vector<uint8_t> psk = get(name);
+ return std::string(cast_uint8_ptr_to_char(psk.data()), psk.size());
+ }
+
+ void set_str(const std::string& name, const std::string& psk)
+ {
+ set(name, cast_char_ptr_to_uint8(psk.data()), psk.size());
+ }
+
+ template<typename Alloc>
+ void set_vec(const std::string& name,
+ const std::vector<uint8_t, Alloc>& psk)
+
+ {
+ set(name, psk.data(), psk.size());
+ }
+
+ virtual ~PSK_Database() = default;
+ };
+
+/**
+* A mixin for an encrypted PSK database.
+* Both keys and values are encrypted with NIST AES-256 key wrapping.
+* Values are padded to obscure their length before encryption, allowing
+* it to be used as a password vault.
+*
+* 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
+ {
+ public:
+ /**
+ * @param master_key specifies the master key used to encrypt all
+ * keys and value. It can be of any length, but should be at least 256 bits.
+ *
+ * Subkeys for the cryptographic algorithms used are derived from this
+ * master key. No key stretching is performed; if encrypting a PSK database
+ * using a password, it is recommended to use PBKDF2 to derive the database
+ * master key.
+ */
+ Encrypted_PSK_Database(const secure_vector<uint8_t>& master_key);
+
+ ~Encrypted_PSK_Database();
+
+ std::set<std::string> list_names() const override;
+
+ secure_vector<uint8_t> get(const std::string& name) const override;
+
+ void set(const std::string& name, const uint8_t psk[], size_t psk_len) override;
+
+ void remove(const std::string& name) override;
+
+ bool is_encrypted() const override { return true; }
+
+ protected:
+ /**
+ * Save a encrypted (name.value) pair to the database. Both will be base64 encoded strings.
+ */
+ virtual void kv_set(const std::string& index, const std::string& value) = 0;
+
+ /**
+ * Get a value previously saved with set_raw_value. Should return an empty
+ * string if index is not found.
+ */
+ virtual std::string kv_get(const std::string& index) const = 0;
+
+ /**
+ * Remove an index
+ */
+ virtual void kv_del(const std::string& index) = 0;
+
+ /**
+ * Return all indexes in the table.
+ */
+ virtual std::set<std::string> kv_get_all() const = 0;
+
+ private:
+ std::unique_ptr<BlockCipher> m_cipher;
+ std::unique_ptr<MessageAuthenticationCode> m_hmac;
+ secure_vector<uint8_t> m_wrap_key;
+ };
+
+class SQL_Database;
+
+class BOTAN_PUBLIC_API(2,4) Encrypted_PSK_Database_SQL : public Encrypted_PSK_Database
+ {
+ public:
+ Encrypted_PSK_Database_SQL(const secure_vector<uint8_t>& master_key,
+ std::shared_ptr<SQL_Database> db,
+ const std::string& table_name);
+
+ ~Encrypted_PSK_Database_SQL();
+ private:
+ void kv_set(const std::string& index, const std::string& value) override;
+ std::string kv_get(const std::string& index) const override;
+ void kv_del(const std::string& index) override;
+ std::set<std::string> kv_get_all() const override;
+
+ std::shared_ptr<SQL_Database> m_db;
+ const std::string m_table_name;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/psk_db/psk_db_sql.cpp b/comm/third_party/botan/src/lib/psk_db/psk_db_sql.cpp
new file mode 100644
index 0000000000..92dcb5f1dc
--- /dev/null
+++ b/comm/third_party/botan/src/lib/psk_db/psk_db_sql.cpp
@@ -0,0 +1,75 @@
+/*
+* (C) 2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/psk_db.h>
+#include <botan/database.h>
+
+namespace Botan {
+
+Encrypted_PSK_Database_SQL::Encrypted_PSK_Database_SQL(const secure_vector<uint8_t>& master_key,
+ std::shared_ptr<SQL_Database> db,
+ const std::string& table_name) :
+ Encrypted_PSK_Database(master_key),
+ m_db(db),
+ m_table_name(table_name)
+ {
+ m_db->create_table(
+ "create table if not exists " + m_table_name +
+ "(psk_name TEXT PRIMARY KEY, psk_value TEXT)");
+ }
+
+Encrypted_PSK_Database_SQL::~Encrypted_PSK_Database_SQL()
+ {
+ /* for ~unique_ptr */
+ }
+
+void Encrypted_PSK_Database_SQL::kv_del(const std::string& name)
+ {
+ auto stmt = m_db->new_statement("delete from " + m_table_name + " where psk_name=?1");
+ stmt->bind(1, name);
+ stmt->spin();
+ }
+
+void Encrypted_PSK_Database_SQL::kv_set(const std::string& name, const std::string& value)
+ {
+ auto stmt = m_db->new_statement("insert or replace into " + m_table_name + " values(?1, ?2)");
+
+ stmt->bind(1, name);
+ stmt->bind(2, value);
+
+ stmt->spin();
+ }
+
+std::string Encrypted_PSK_Database_SQL::kv_get(const std::string& name) const
+ {
+ auto stmt = m_db->new_statement("select psk_value from " + m_table_name +
+ " where psk_name = ?1");
+
+ stmt->bind(1, name);
+
+ while(stmt->step())
+ {
+ return stmt->get_str(0);
+ }
+ return "";
+ }
+
+std::set<std::string> Encrypted_PSK_Database_SQL::kv_get_all() const
+ {
+ std::set<std::string> names;
+
+ auto stmt = m_db->new_statement("select psk_name from " + m_table_name);
+
+ while(stmt->step())
+ {
+ names.insert(stmt->get_str(0));
+ }
+
+ return names;
+ }
+
+}
+
diff --git a/comm/third_party/botan/src/lib/psk_db/psk_db_sql.h b/comm/third_party/botan/src/lib/psk_db/psk_db_sql.h
new file mode 100644
index 0000000000..170ca674c5
--- /dev/null
+++ b/comm/third_party/botan/src/lib/psk_db/psk_db_sql.h
@@ -0,0 +1,13 @@
+/*
+* (C) 2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_PSK_DB_SQL_H_
+#define BOTAN_PSK_DB_SQL_H_
+
+#include <botan/psk_db.h>
+BOTAN_DEPRECATED_HEADER(psk_db_sql.h)
+
+#endif
diff --git a/comm/third_party/botan/src/lib/pubkey/blinding.cpp b/comm/third_party/botan/src/lib/pubkey/blinding.cpp
new file mode 100644
index 0000000000..d1f299229a
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/blinding.cpp
@@ -0,0 +1,66 @@
+/*
+* Blinding for public key operations
+* (C) 1999-2010,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/blinding.h>
+
+namespace Botan {
+
+Blinder::Blinder(const BigInt& modulus,
+ RandomNumberGenerator& rng,
+ std::function<BigInt (const BigInt&)> fwd,
+ std::function<BigInt (const BigInt&)> inv) :
+ m_reducer(modulus),
+ m_rng(rng),
+ m_fwd_fn(fwd),
+ m_inv_fn(inv),
+ m_modulus_bits(modulus.bits()),
+ m_e{},
+ m_d{},
+ m_counter{}
+ {
+ const BigInt k = blinding_nonce();
+ m_e = m_fwd_fn(k);
+ m_d = m_inv_fn(k);
+ }
+
+BigInt Blinder::blinding_nonce() const
+ {
+ return BigInt(m_rng, m_modulus_bits - 1);
+ }
+
+BigInt Blinder::blind(const BigInt& i) const
+ {
+ if(!m_reducer.initialized())
+ throw Invalid_State("Blinder not initialized, cannot blind");
+
+ ++m_counter;
+
+ if((BOTAN_BLINDING_REINIT_INTERVAL > 0) && (m_counter > BOTAN_BLINDING_REINIT_INTERVAL))
+ {
+ const BigInt k = blinding_nonce();
+ m_e = m_fwd_fn(k);
+ m_d = m_inv_fn(k);
+ m_counter = 0;
+ }
+ else
+ {
+ m_e = m_reducer.square(m_e);
+ m_d = m_reducer.square(m_d);
+ }
+
+ return m_reducer.multiply(i, m_e);
+ }
+
+BigInt Blinder::unblind(const BigInt& i) const
+ {
+ if(!m_reducer.initialized())
+ throw Invalid_State("Blinder not initialized, cannot unblind");
+
+ return m_reducer.multiply(i, m_d);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/pubkey/blinding.h b/comm/third_party/botan/src/lib/pubkey/blinding.h
new file mode 100644
index 0000000000..988a41a357
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/blinding.h
@@ -0,0 +1,80 @@
+/*
+* Blinding for public key operations
+* (C) 1999-2010,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_BLINDER_H_
+#define BOTAN_BLINDER_H_
+
+#include <botan/bigint.h>
+#include <botan/reducer.h>
+#include <functional>
+
+BOTAN_FUTURE_INTERNAL_HEADER(blinding.h)
+
+namespace Botan {
+
+class RandomNumberGenerator;
+
+/**
+* Blinding Function Object.
+*/
+class BOTAN_PUBLIC_API(2,0) Blinder final
+ {
+ public:
+ /**
+ * 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.
+ * @param x value to blind
+ * @return blinded value
+ */
+ BigInt blind(const BigInt& x) const;
+
+ /**
+ * Unblind a value.
+ * @param x value to unblind
+ * @return unblinded value
+ */
+ BigInt unblind(const BigInt& x) const;
+
+ /**
+ * @param modulus the modulus
+ * @param rng the RNG to use for generating the nonce
+ * @param fwd_func a function that calculates the modular
+ * exponentiation of the public exponent and the given value (the nonce)
+ * @param inv_func a function that calculates the modular inverse
+ * of the given value (the nonce)
+ */
+ Blinder(const BigInt& modulus,
+ RandomNumberGenerator& rng,
+ std::function<BigInt (const BigInt&)> fwd_func,
+ std::function<BigInt (const BigInt&)> inv_func);
+
+ Blinder(const Blinder&) = delete;
+
+ Blinder& operator=(const Blinder&) = delete;
+
+ RandomNumberGenerator& rng() const { return m_rng; }
+
+ private:
+ BigInt blinding_nonce() const;
+
+ Modular_Reducer m_reducer;
+ RandomNumberGenerator& m_rng;
+ std::function<BigInt (const BigInt&)> m_fwd_fn;
+ std::function<BigInt (const BigInt&)> m_inv_fn;
+ size_t m_modulus_bits = 0;
+
+ mutable BigInt m_e, m_d;
+ mutable size_t m_counter = 0;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/pubkey/cecpq1/cecpq1.cpp b/comm/third_party/botan/src/lib/pubkey/cecpq1/cecpq1.cpp
new file mode 100644
index 0000000000..e11a1e0839
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/cecpq1/cecpq1.cpp
@@ -0,0 +1,51 @@
+/*
+* CECPQ1 (x25519 + NewHope)
+* (C) 2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/cecpq1.h>
+#include <botan/newhope.h>
+#include <botan/curve25519.h>
+#include <botan/rng.h>
+
+namespace Botan {
+
+void CECPQ1_offer(uint8_t send[CECPQ1_OFFER_BYTES],
+ CECPQ1_key* offer_key_output,
+ RandomNumberGenerator& rng)
+ {
+ offer_key_output->m_x25519 = rng.random_vec(32);
+ curve25519_basepoint(send, offer_key_output->m_x25519.data());
+
+ newhope_keygen(send + 32, &offer_key_output->m_newhope,
+ rng, Newhope_Mode::BoringSSL);
+ }
+
+void CECPQ1_accept(uint8_t shared_key[CECPQ1_SHARED_KEY_BYTES],
+ uint8_t send[CECPQ1_ACCEPT_BYTES],
+ const uint8_t received[CECPQ1_OFFER_BYTES],
+ RandomNumberGenerator& rng)
+ {
+ secure_vector<uint8_t> x25519_key = rng.random_vec(32);
+
+ curve25519_basepoint(send, x25519_key.data());
+
+ curve25519_donna(shared_key, x25519_key.data(), received);
+
+ newhope_sharedb(shared_key + 32, send + 32, received + 32,
+ rng, Newhope_Mode::BoringSSL);
+ }
+
+void CECPQ1_finish(uint8_t shared_key[CECPQ1_SHARED_KEY_BYTES],
+ const CECPQ1_key& offer_key,
+ const uint8_t received[CECPQ1_ACCEPT_BYTES])
+ {
+ curve25519_donna(shared_key, offer_key.m_x25519.data(), received);
+
+ newhope_shareda(shared_key + 32, &offer_key.m_newhope, received + 32,
+ Newhope_Mode::BoringSSL);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/pubkey/cecpq1/cecpq1.h b/comm/third_party/botan/src/lib/pubkey/cecpq1/cecpq1.h
new file mode 100644
index 0000000000..a722899c67
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/cecpq1/cecpq1.h
@@ -0,0 +1,38 @@
+/*
+* CECPQ1 (x25519 + NewHope)
+* (C) 2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_CECPQ1_H_
+#define BOTAN_CECPQ1_H_
+
+#include <botan/secmem.h>
+#include <botan/newhope.h>
+
+namespace Botan {
+
+class CECPQ1_key final
+ {
+ public:
+ secure_vector<uint8_t> m_x25519;
+ newhope_poly m_newhope;
+ };
+
+void BOTAN_PUBLIC_API(2,0) CECPQ1_offer(uint8_t* offer_message,
+ CECPQ1_key* offer_key_output,
+ RandomNumberGenerator& rng);
+
+void BOTAN_PUBLIC_API(2,0) CECPQ1_accept(uint8_t* shared_key,
+ uint8_t* accept_message,
+ const uint8_t* offer_message,
+ RandomNumberGenerator& rng);
+
+void BOTAN_PUBLIC_API(2,0) CECPQ1_finish(uint8_t* shared_key,
+ const CECPQ1_key& offer_key,
+ const uint8_t* accept_message);
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/pubkey/cecpq1/info.txt b/comm/third_party/botan/src/lib/pubkey/cecpq1/info.txt
new file mode 100644
index 0000000000..1e50f4c880
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/cecpq1/info.txt
@@ -0,0 +1,9 @@
+<defines>
+CECPQ1 -> 20161116
+</defines>
+
+<requires>
+newhope
+curve25519
+</requires>
+
diff --git a/comm/third_party/botan/src/lib/pubkey/curve25519/curve25519.cpp b/comm/third_party/botan/src/lib/pubkey/curve25519/curve25519.cpp
new file mode 100644
index 0000000000..515fdc5e68
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/curve25519/curve25519.cpp
@@ -0,0 +1,143 @@
+/*
+* Curve25519
+* (C) 2014 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/curve25519.h>
+#include <botan/internal/pk_ops_impl.h>
+#include <botan/ber_dec.h>
+#include <botan/der_enc.h>
+#include <botan/rng.h>
+
+namespace Botan {
+
+void curve25519_basepoint(uint8_t mypublic[32], const uint8_t secret[32])
+ {
+ const uint8_t basepoint[32] = { 9 };
+ curve25519_donna(mypublic, secret, basepoint);
+ }
+
+namespace {
+
+void size_check(size_t size, const char* thing)
+ {
+ if(size != 32)
+ throw Decoding_Error("Invalid size " + std::to_string(size) + " for Curve25519 " + thing);
+ }
+
+secure_vector<uint8_t> curve25519(const secure_vector<uint8_t>& secret,
+ const uint8_t pubval[32])
+ {
+ secure_vector<uint8_t> out(32);
+ curve25519_donna(out.data(), secret.data(), pubval);
+ return out;
+ }
+
+}
+
+AlgorithmIdentifier Curve25519_PublicKey::algorithm_identifier() const
+ {
+ return AlgorithmIdentifier(get_oid(), AlgorithmIdentifier::USE_EMPTY_PARAM);
+ }
+
+bool Curve25519_PublicKey::check_key(RandomNumberGenerator&, bool) const
+ {
+ return true; // no tests possible?
+ }
+
+Curve25519_PublicKey::Curve25519_PublicKey(const AlgorithmIdentifier&,
+ const std::vector<uint8_t>& key_bits)
+ {
+ m_public = key_bits;
+
+ size_check(m_public.size(), "public key");
+ }
+
+std::vector<uint8_t> Curve25519_PublicKey::public_key_bits() const
+ {
+ return m_public;
+ }
+
+Curve25519_PrivateKey::Curve25519_PrivateKey(const secure_vector<uint8_t>& secret_key)
+ {
+ if(secret_key.size() != 32)
+ throw Decoding_Error("Invalid size for Curve25519 private key");
+
+ m_public.resize(32);
+ m_private = secret_key;
+ curve25519_basepoint(m_public.data(), m_private.data());
+ }
+
+Curve25519_PrivateKey::Curve25519_PrivateKey(RandomNumberGenerator& rng)
+ {
+ m_private = rng.random_vec(32);
+ m_public.resize(32);
+ curve25519_basepoint(m_public.data(), m_private.data());
+ }
+
+Curve25519_PrivateKey::Curve25519_PrivateKey(const AlgorithmIdentifier&,
+ const secure_vector<uint8_t>& key_bits)
+ {
+ BER_Decoder(key_bits).decode(m_private, OCTET_STRING).discard_remaining();
+
+ size_check(m_private.size(), "private key");
+ m_public.resize(32);
+ curve25519_basepoint(m_public.data(), m_private.data());
+ }
+
+secure_vector<uint8_t> Curve25519_PrivateKey::private_key_bits() const
+ {
+ return DER_Encoder().encode(m_private, OCTET_STRING).get_contents();
+ }
+
+bool Curve25519_PrivateKey::check_key(RandomNumberGenerator&, bool) const
+ {
+ std::vector<uint8_t> public_point(32);
+ curve25519_basepoint(public_point.data(), m_private.data());
+ return public_point == m_public;
+ }
+
+secure_vector<uint8_t> Curve25519_PrivateKey::agree(const uint8_t w[], size_t w_len) const
+ {
+ size_check(w_len, "public value");
+ return curve25519(m_private, w);
+ }
+
+namespace {
+
+/**
+* Curve25519 operation
+*/
+class Curve25519_KA_Operation final : public PK_Ops::Key_Agreement_with_KDF
+ {
+ public:
+
+ Curve25519_KA_Operation(const Curve25519_PrivateKey& key, const std::string& kdf) :
+ PK_Ops::Key_Agreement_with_KDF(kdf),
+ m_key(key) {}
+
+ size_t agreed_value_size() const override { return 32; }
+
+ secure_vector<uint8_t> raw_agree(const uint8_t w[], size_t w_len) override
+ {
+ return m_key.agree(w, w_len);
+ }
+ private:
+ const Curve25519_PrivateKey& m_key;
+ };
+
+}
+
+std::unique_ptr<PK_Ops::Key_Agreement>
+Curve25519_PrivateKey::create_key_agreement_op(RandomNumberGenerator& /*rng*/,
+ const std::string& params,
+ const std::string& provider) const
+ {
+ if(provider == "base" || provider.empty())
+ return std::unique_ptr<PK_Ops::Key_Agreement>(new Curve25519_KA_Operation(*this, params));
+ throw Provider_Not_Found(algo_name(), provider);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/pubkey/curve25519/curve25519.h b/comm/third_party/botan/src/lib/pubkey/curve25519/curve25519.h
new file mode 100644
index 0000000000..c2f8f42b33
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/curve25519/curve25519.h
@@ -0,0 +1,123 @@
+/*
+* Curve25519
+* (C) 2014 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_CURVE_25519_H_
+#define BOTAN_CURVE_25519_H_
+
+#include <botan/pk_keys.h>
+
+namespace Botan {
+
+class BOTAN_PUBLIC_API(2,0) Curve25519_PublicKey : public virtual Public_Key
+ {
+ public:
+ std::string algo_name() const override { return "Curve25519"; }
+
+ size_t estimated_strength() const override { return 128; }
+
+ size_t key_length() const override { return 255; }
+
+ bool check_key(RandomNumberGenerator& rng, bool strong) const override;
+
+ AlgorithmIdentifier algorithm_identifier() const override;
+
+ std::vector<uint8_t> public_key_bits() const override;
+
+ std::vector<uint8_t> public_value() const { return m_public; }
+
+ /**
+ * Create a Curve25519 Public Key.
+ * @param alg_id the X.509 algorithm identifier
+ * @param key_bits DER encoded public key bits
+ */
+ Curve25519_PublicKey(const AlgorithmIdentifier& alg_id,
+ const std::vector<uint8_t>& key_bits);
+
+ /**
+ * Create a Curve25519 Public Key.
+ * @param pub 32-byte raw public key
+ */
+ explicit Curve25519_PublicKey(const std::vector<uint8_t>& pub) : m_public(pub) {}
+
+ /**
+ * Create a Curve25519 Public Key.
+ * @param pub 32-byte raw public key
+ */
+ explicit Curve25519_PublicKey(const secure_vector<uint8_t>& pub) :
+ m_public(pub.begin(), pub.end()) {}
+
+ protected:
+ Curve25519_PublicKey() = default;
+ std::vector<uint8_t> m_public;
+ };
+
+class BOTAN_PUBLIC_API(2,0) Curve25519_PrivateKey final : public Curve25519_PublicKey,
+ public virtual Private_Key,
+ public virtual PK_Key_Agreement_Key
+ {
+ public:
+ /**
+ * Construct a private key from the specified parameters.
+ * @param alg_id the X.509 algorithm identifier
+ * @param key_bits PKCS #8 structure
+ */
+ Curve25519_PrivateKey(const AlgorithmIdentifier& alg_id,
+ const secure_vector<uint8_t>& key_bits);
+
+ /**
+ * Generate a private key.
+ * @param rng the RNG to use
+ */
+ explicit Curve25519_PrivateKey(RandomNumberGenerator& rng);
+
+ /**
+ * Construct a private key from the specified parameters.
+ * @param secret_key the private key
+ */
+ explicit Curve25519_PrivateKey(const secure_vector<uint8_t>& secret_key);
+
+ std::vector<uint8_t> public_value() const override { return Curve25519_PublicKey::public_value(); }
+
+ secure_vector<uint8_t> agree(const uint8_t w[], size_t w_len) const;
+
+ const secure_vector<uint8_t>& get_x() const { return m_private; }
+
+ secure_vector<uint8_t> private_key_bits() const override;
+
+ bool check_key(RandomNumberGenerator& rng, bool strong) const override;
+
+ std::unique_ptr<PK_Ops::Key_Agreement>
+ create_key_agreement_op(RandomNumberGenerator& rng,
+ const std::string& params,
+ const std::string& provider) const override;
+
+ private:
+ secure_vector<uint8_t> m_private;
+ };
+
+typedef Curve25519_PublicKey X25519_PublicKey;
+typedef Curve25519_PrivateKey X25519_PrivateKey;
+
+/*
+* The types above are just wrappers for curve25519_donna, plus defining
+* encodings for public and private keys.
+*/
+void BOTAN_PUBLIC_API(2,0) curve25519_donna(uint8_t mypublic[32],
+ const uint8_t secret[32],
+ const uint8_t basepoint[32]);
+
+/**
+* Exponentiate by the x25519 base point
+* @param mypublic output value
+* @param secret random scalar
+*/
+void BOTAN_PUBLIC_API(2,0) curve25519_basepoint(uint8_t mypublic[32],
+ const uint8_t secret[32]);
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/pubkey/curve25519/donna.cpp b/comm/third_party/botan/src/lib/pubkey/curve25519/donna.cpp
new file mode 100644
index 0000000000..352e081dcd
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/curve25519/donna.cpp
@@ -0,0 +1,464 @@
+/*
+* Based on curve25519-donna-c64.c from github.com/agl/curve25519-donna
+* revision 80ad9b9930c9baef5829dd2a235b6b7646d32a8e
+*
+* Further changes
+* (C) 2014,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+/* Copyright 2008, Google Inc.
+* All rights reserved.
+*
+* Code released into the public domain.
+*
+* curve25519-donna: Curve25519 elliptic curve, public key function
+*
+* https://code.google.com/p/curve25519-donna/
+*
+* Adam Langley <agl@imperialviolet.org>
+*
+* Derived from public domain C code by Daniel J. Bernstein <djb@cr.yp.to>
+*
+* More information about curve25519 can be found here
+* https://cr.yp.to/ecdh.html
+*
+* djb's sample implementation of curve25519 is written in a special assembly
+* language called qhasm and uses the floating point registers.
+*
+* This is, almost, a clean room reimplementation from the curve25519 paper. It
+* uses many of the tricks described therein. Only the crecip function is taken
+* from the sample implementation.
+*/
+
+#include <botan/curve25519.h>
+#include <botan/mul128.h>
+#include <botan/internal/ct_utils.h>
+#include <botan/internal/donna128.h>
+#include <botan/loadstor.h>
+
+namespace Botan {
+
+namespace {
+
+#if !defined(BOTAN_TARGET_HAS_NATIVE_UINT128)
+typedef donna128 uint128_t;
+#endif
+
+/* Sum two numbers: output += in */
+inline void fsum(uint64_t out[5], const uint64_t in[5])
+ {
+ out[0] += in[0];
+ out[1] += in[1];
+ out[2] += in[2];
+ out[3] += in[3];
+ out[4] += in[4];
+ }
+
+/* Find the difference of two numbers: out = in - out
+* (note the order of the arguments!)
+*
+* Assumes that out[i] < 2**52
+* On return, out[i] < 2**55
+*/
+inline void fdifference_backwards(uint64_t out[5], const uint64_t in[5])
+ {
+ /* 152 is 19 << 3 */
+ const uint64_t two54m152 = (static_cast<uint64_t>(1) << 54) - 152;
+ const uint64_t two54m8 = (static_cast<uint64_t>(1) << 54) - 8;
+
+ out[0] = in[0] + two54m152 - out[0];
+ out[1] = in[1] + two54m8 - out[1];
+ out[2] = in[2] + two54m8 - out[2];
+ out[3] = in[3] + two54m8 - out[3];
+ out[4] = in[4] + two54m8 - out[4];
+ }
+
+inline void fadd_sub(uint64_t x[5],
+ uint64_t y[5])
+ {
+ // TODO merge these and avoid the tmp array
+ uint64_t tmp[5];
+ copy_mem(tmp, y, 5);
+ fsum(y, x);
+ fdifference_backwards(x, tmp); // does x - z
+ }
+
+/* Multiply a number by a scalar: out = in * scalar */
+inline void fscalar_product(uint64_t out[5], const uint64_t in[5], const uint64_t scalar)
+ {
+ uint128_t a = uint128_t(in[0]) * scalar;
+ out[0] = a & 0x7ffffffffffff;
+
+ a = uint128_t(in[1]) * scalar + carry_shift(a, 51);
+ out[1] = a & 0x7ffffffffffff;
+
+ a = uint128_t(in[2]) * scalar + carry_shift(a, 51);
+ out[2] = a & 0x7ffffffffffff;
+
+ a = uint128_t(in[3]) * scalar + carry_shift(a, 51);
+ out[3] = a & 0x7ffffffffffff;
+
+ a = uint128_t(in[4]) * scalar + carry_shift(a, 51);
+ out[4] = a & 0x7ffffffffffff;
+
+ out[0] += carry_shift(a, 51) * 19;
+ }
+
+/* Multiply two numbers: out = in2 * in
+*
+* out must be distinct to both inputs. The inputs are reduced coefficient
+* form, the output is not.
+*
+* Assumes that in[i] < 2**55 and likewise for in2.
+* 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];
+
+ uint64_t r0 = in[0];
+ uint64_t r1 = in[1];
+ uint64_t r2 = in[2];
+ uint64_t r3 = in[3];
+ uint64_t r4 = in[4];
+
+ uint128_t t0 = r0 * s0;
+ uint128_t t1 = r0 * s1 + r1 * s0;
+ uint128_t t2 = r0 * s2 + r2 * s0 + r1 * s1;
+ uint128_t t3 = r0 * s3 + r3 * s0 + r1 * s2 + r2 * s1;
+ uint128_t t4 = r0 * s4 + r4 * s0 + r3 * s1 + r1 * s3 + r2 * s2;
+
+ r4 *= 19;
+ r1 *= 19;
+ r2 *= 19;
+ r3 *= 19;
+
+ t0 += r4 * s1 + r1 * s4 + r2 * s3 + r3 * s2;
+ t1 += r4 * s2 + r2 * s4 + r3 * s3;
+ t2 += r4 * s3 + r3 * s4;
+ t3 += r4 * s4;
+
+ r0 = t0 & 0x7ffffffffffff; t1 += carry_shift(t0, 51);
+ r1 = t1 & 0x7ffffffffffff; t2 += carry_shift(t1, 51);
+ r2 = t2 & 0x7ffffffffffff; t3 += carry_shift(t2, 51);
+ r3 = t3 & 0x7ffffffffffff; t4 += carry_shift(t3, 51);
+ r4 = t4 & 0x7ffffffffffff; uint64_t c = carry_shift(t4, 51);
+
+ r0 += c * 19; c = r0 >> 51; r0 = r0 & 0x7ffffffffffff;
+ r1 += c; c = r1 >> 51; r1 = r1 & 0x7ffffffffffff;
+ r2 += c;
+
+ out[0] = r0;
+ out[1] = r1;
+ out[2] = r2;
+ out[3] = r3;
+ out[4] = r4;
+ }
+
+inline void fsquare(uint64_t out[5], const uint64_t in[5], size_t count = 1)
+ {
+ uint64_t r0 = in[0];
+ uint64_t r1 = in[1];
+ uint64_t r2 = in[2];
+ uint64_t r3 = in[3];
+ uint64_t r4 = in[4];
+
+ for(size_t i = 0; i != count; ++i)
+ {
+ const uint64_t d0 = r0 * 2;
+ const uint64_t d1 = r1 * 2;
+ const uint64_t d2 = r2 * 2 * 19;
+ 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 );
+ 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 );
+ uint128_t t4 = uint128_t(d0) * r4 + uint128_t(d1) * r3 + uint128_t(r2) * (r2 );
+
+ r0 = t0 & 0x7ffffffffffff; t1 += carry_shift(t0, 51);
+ r1 = t1 & 0x7ffffffffffff; t2 += carry_shift(t1, 51);
+ r2 = t2 & 0x7ffffffffffff; t3 += carry_shift(t2, 51);
+ r3 = t3 & 0x7ffffffffffff; t4 += carry_shift(t3, 51);
+ r4 = t4 & 0x7ffffffffffff; uint64_t c = carry_shift(t4, 51);
+
+ r0 += c * 19; c = r0 >> 51; r0 = r0 & 0x7ffffffffffff;
+ r1 += c; c = r1 >> 51; r1 = r1 & 0x7ffffffffffff;
+ r2 += c;
+ }
+
+ out[0] = r0;
+ out[1] = r1;
+ out[2] = r2;
+ out[3] = r3;
+ out[4] = r4;
+ }
+
+/* Take a little-endian, 32-byte number and expand it into polynomial form */
+inline void fexpand(uint64_t *out, const uint8_t *in)
+ {
+ out[0] = load_le<uint64_t>(in, 0) & 0x7ffffffffffff;
+ out[1] = (load_le<uint64_t>(in+6, 0) >> 3) & 0x7ffffffffffff;
+ out[2] = (load_le<uint64_t>(in+12, 0) >> 6) & 0x7ffffffffffff;
+ out[3] = (load_le<uint64_t>(in+19, 0) >> 1) & 0x7ffffffffffff;
+ out[4] = (load_le<uint64_t>(in+24, 0) >> 12) & 0x7ffffffffffff;
+ }
+
+/* Take a fully reduced polynomial form number and contract it into a
+* 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];
+
+ for(size_t i = 0; i != 2; ++i)
+ {
+ t1 += t0 >> 51; t0 &= 0x7ffffffffffff;
+ t2 += t1 >> 51; t1 &= 0x7ffffffffffff;
+ t3 += t2 >> 51; t2 &= 0x7ffffffffffff;
+ t4 += t3 >> 51; t3 &= 0x7ffffffffffff;
+ t0 += (t4 >> 51) * 19; t4 &= 0x7ffffffffffff;
+ }
+
+ /* now t is between 0 and 2^255-1, properly carried. */
+ /* case 1: between 0 and 2^255-20. case 2: between 2^255-19 and 2^255-1. */
+
+ t0 += 19;
+
+ t1 += t0 >> 51; t0 &= 0x7ffffffffffff;
+ t2 += t1 >> 51; t1 &= 0x7ffffffffffff;
+ t3 += t2 >> 51; t2 &= 0x7ffffffffffff;
+ t4 += t3 >> 51; t3 &= 0x7ffffffffffff;
+ t0 += (t4 >> 51) * 19; t4 &= 0x7ffffffffffff;
+
+ /* now between 19 and 2^255-1 in both cases, and offset by 19. */
+
+ t0 += 0x8000000000000 - 19;
+ t1 += 0x8000000000000 - 1;
+ t2 += 0x8000000000000 - 1;
+ t3 += 0x8000000000000 - 1;
+ t4 += 0x8000000000000 - 1;
+
+ /* now between 2^255 and 2^256-20, and offset by 2^255. */
+
+ t1 += t0 >> 51; t0 &= 0x7ffffffffffff;
+ t2 += t1 >> 51; t1 &= 0x7ffffffffffff;
+ t3 += t2 >> 51; t2 &= 0x7ffffffffffff;
+ t4 += t3 >> 51; t3 &= 0x7ffffffffffff;
+ t4 &= 0x7ffffffffffff;
+
+ store_le(out,
+ combine_lower(t0, 0, t1, 51),
+ combine_lower(t1, 13, t2, 38),
+ combine_lower(t2, 26, t3, 25),
+ combine_lower(t3, 39, t4, 12));
+ }
+
+/* Input: Q, Q', Q-Q'
+* Out: 2Q, Q+Q'
+*
+* result.two_q (2*Q): long form
+* result.q_plus_q_dash (Q + Q): long form
+* in_q: short form, destroyed
+* in_q_dash: short form, destroyed
+* in_q_minus_q_dash: short form, preserved
+*/
+void fmonty(uint64_t result_two_q_x[5],
+ uint64_t result_two_q_z[5],
+ uint64_t result_q_plus_q_dash_x[5],
+ uint64_t result_q_plus_q_dash_z[5],
+ uint64_t in_q_x[5],
+ uint64_t in_q_z[5],
+ uint64_t in_q_dash_x[5],
+ uint64_t in_q_dash_z[5],
+ const uint64_t q_minus_q_dash[5])
+ {
+ uint64_t zzz[5];
+ uint64_t xx[5];
+ uint64_t zz[5];
+ uint64_t xxprime[5];
+ uint64_t zzprime[5];
+ uint64_t zzzprime[5];
+
+ fadd_sub(in_q_z, in_q_x);
+ fadd_sub(in_q_dash_z, in_q_dash_x);
+
+ fmul(xxprime, in_q_dash_x, in_q_z);
+ fmul(zzprime, in_q_dash_z, in_q_x);
+
+ fadd_sub(zzprime, xxprime);
+
+ fsquare(result_q_plus_q_dash_x, xxprime);
+ fsquare(zzzprime, zzprime);
+ fmul(result_q_plus_q_dash_z, zzzprime, q_minus_q_dash);
+
+ fsquare(xx, in_q_x);
+ fsquare(zz, in_q_z);
+ fmul(result_two_q_x, xx, zz);
+
+ fdifference_backwards(zz, xx); // does zz = xx - zz
+ fscalar_product(zzz, zz, 121665);
+ fsum(zzz, xx);
+
+ fmul(result_two_q_z, zz, zzz);
+ }
+
+/*
+* Maybe swap the contents of two uint64_t arrays (@a and @b),
+* Param @iswap is assumed to be either 0 or 1
+*
+* This function performs the swap without leaking any side-channel
+* information.
+*/
+inline void swap_conditional(uint64_t a[5], uint64_t b[5],
+ uint64_t c[5], uint64_t d[5],
+ uint64_t iswap)
+ {
+ const uint64_t swap = 0 - iswap;
+
+ for(size_t i = 0; i < 5; ++i)
+ {
+ const uint64_t x0 = swap & (a[i] ^ b[i]);
+ const uint64_t x1 = swap & (c[i] ^ d[i]);
+ a[i] ^= x0;
+ b[i] ^= x0;
+ c[i] ^= x1;
+ d[i] ^= x1;
+ }
+ }
+
+/* Calculates nQ where Q is the x-coordinate of a point on the curve
+*
+* resultx/resultz: the x/z coordinate of the resulting curve point (short form)
+* n: a little endian, 32-byte number
+* q: a point of the curve (short form)
+*/
+void cmult(uint64_t resultx[5], uint64_t resultz[5], const uint8_t n[32], const uint64_t q[5])
+ {
+ uint64_t a[5] = {0}; // nqpqx
+ uint64_t b[5] = {1}; // npqpz
+ uint64_t c[5] = {1}; // nqx
+ uint64_t d[5] = {0}; // nqz
+ uint64_t e[5] = {0}; // npqqx2
+ uint64_t f[5] = {1}; // npqqz2
+ uint64_t g[5] = {0}; // nqx2
+ uint64_t h[5] = {1}; // nqz2
+
+ copy_mem(a, q, 5);
+
+ for(size_t i = 0; i < 32; ++i)
+ {
+ const uint64_t bit0 = (n[31 - i] >> 7) & 1;
+ const uint64_t bit1 = (n[31 - i] >> 6) & 1;
+ const uint64_t bit2 = (n[31 - i] >> 5) & 1;
+ const uint64_t bit3 = (n[31 - i] >> 4) & 1;
+ const uint64_t bit4 = (n[31 - i] >> 3) & 1;
+ const uint64_t bit5 = (n[31 - i] >> 2) & 1;
+ const uint64_t bit6 = (n[31 - i] >> 1) & 1;
+ const uint64_t bit7 = (n[31 - i] >> 0) & 1;
+
+ swap_conditional(c, a, d, b, bit0);
+ fmonty(g, h, e, f, c, d, a, b, q);
+
+ swap_conditional(g, e, h, f, bit0 ^ bit1);
+ fmonty(c, d, a, b, g, h, e, f, q);
+
+ swap_conditional(c, a, d, b, bit1 ^ bit2);
+ fmonty(g, h, e, f, c, d, a, b, q);
+
+ swap_conditional(g, e, h, f, bit2 ^ bit3);
+ fmonty(c, d, a, b, g, h, e, f, q);
+
+ swap_conditional(c, a, d, b, bit3 ^ bit4);
+ fmonty(g, h, e, f, c, d, a, b, q);
+
+ swap_conditional(g, e, h, f, bit4 ^ bit5);
+ fmonty(c, d, a, b, g, h, e, f, q);
+
+ swap_conditional(c, a, d, b, bit5 ^ bit6);
+ fmonty(g, h, e, f, c, d, a, b, q);
+
+ swap_conditional(g, e, h, f, bit6 ^ bit7);
+ fmonty(c, d, a, b, g, h, e, f, q);
+
+ swap_conditional(c, a, d, b, bit7);
+ }
+
+ copy_mem(resultx, c, 5);
+ copy_mem(resultz, d, 5);
+ }
+
+
+// -----------------------------------------------------------------------------
+// Shamelessly copied from djb's code, tightened a little
+// -----------------------------------------------------------------------------
+void crecip(uint64_t out[5], const uint64_t z[5])
+ {
+ uint64_t a[5];
+ uint64_t b[5];
+ uint64_t c[5];
+ uint64_t t0[5];
+
+ fsquare(a, z); // 2
+ fsquare(t0, a, 2); // 8
+ fmul(b, t0, z); // 9
+ fmul(a, b, a); // 11
+ fsquare(t0, a); // 22
+ fmul(b, t0, b); // 2^5 - 2^0 = 31
+ fsquare(t0, b, 5); // 2^10 - 2^5
+ fmul(b, t0, b); // 2^10 - 2^0
+ fsquare(t0, b, 10); // 2^20 - 2^10
+ fmul(c, t0, b); // 2^20 - 2^0
+ fsquare(t0, c, 20); // 2^40 - 2^20
+ fmul(t0, t0, c); // 2^40 - 2^0
+ fsquare(t0, t0, 10); // 2^50 - 2^10
+ fmul(b, t0, b); // 2^50 - 2^0
+ fsquare(t0, b, 50); // 2^100 - 2^50
+ fmul(c, t0, b); // 2^100 - 2^0
+ fsquare(t0, c, 100); // 2^200 - 2^100
+ fmul(t0, t0, c); // 2^200 - 2^0
+ fsquare(t0, t0, 50); // 2^250 - 2^50
+ fmul(t0, t0, b); // 2^250 - 2^0
+ fsquare(t0, t0, 5); // 2^255 - 2^5
+ fmul(out, t0, a); // 2^255 - 21
+ }
+
+}
+
+void
+curve25519_donna(uint8_t mypublic[32], const uint8_t secret[32], const uint8_t basepoint[32])
+ {
+ CT::poison(secret, 32);
+ CT::poison(basepoint, 32);
+
+ uint64_t bp[5], x[5], z[5], zmone[5];
+ uint8_t e[32];
+
+ copy_mem(e, secret, 32);
+ e[ 0] &= 248;
+ e[31] &= 127;
+ e[31] |= 64;
+
+ fexpand(bp, basepoint);
+ cmult(x, z, e, bp);
+ crecip(zmone, z);
+ fmul(z, x, zmone);
+ fcontract(mypublic, z);
+
+ CT::unpoison(secret, 32);
+ CT::unpoison(basepoint, 32);
+ CT::unpoison(mypublic, 32);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/pubkey/curve25519/info.txt b/comm/third_party/botan/src/lib/pubkey/curve25519/info.txt
new file mode 100644
index 0000000000..3818b0606b
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/curve25519/info.txt
@@ -0,0 +1,8 @@
+<defines>
+CURVE_25519 -> 20170621
+X25519 -> 20180910
+</defines>
+
+<header:public>
+curve25519.h
+</header:public>
diff --git a/comm/third_party/botan/src/lib/pubkey/dh/dh.cpp b/comm/third_party/botan/src/lib/pubkey/dh/dh.cpp
new file mode 100644
index 0000000000..687032a696
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/dh/dh.cpp
@@ -0,0 +1,142 @@
+/*
+* Diffie-Hellman
+* (C) 1999-2007,2016,2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/dh.h>
+#include <botan/internal/pk_ops_impl.h>
+#include <botan/internal/monty_exp.h>
+#include <botan/blinding.h>
+
+namespace Botan {
+
+/*
+* DH_PublicKey Constructor
+*/
+DH_PublicKey::DH_PublicKey(const DL_Group& grp, const BigInt& y1)
+ {
+ m_group = grp;
+ m_y = y1;
+ }
+
+/*
+* Return the public value for key agreement
+*/
+std::vector<uint8_t> DH_PublicKey::public_value() const
+ {
+ return unlock(BigInt::encode_1363(m_y, group_p().bytes()));
+ }
+
+/*
+* Create a DH private key
+*/
+DH_PrivateKey::DH_PrivateKey(RandomNumberGenerator& rng,
+ const DL_Group& grp,
+ const BigInt& x_arg)
+ {
+ m_group = grp;
+
+ if(x_arg == 0)
+ {
+ const size_t exp_bits = grp.exponent_bits();
+ m_x.randomize(rng, exp_bits);
+ m_y = m_group.power_g_p(m_x, exp_bits);
+ }
+ else
+ {
+ m_x = x_arg;
+
+ if(m_y == 0)
+ m_y = m_group.power_g_p(m_x, grp.p_bits());
+ }
+ }
+
+/*
+* Load a DH private key
+*/
+DH_PrivateKey::DH_PrivateKey(const AlgorithmIdentifier& alg_id,
+ const secure_vector<uint8_t>& key_bits) :
+ DL_Scheme_PrivateKey(alg_id, key_bits, DL_Group::ANSI_X9_42)
+ {
+ if(m_y.is_zero())
+ {
+ m_y = m_group.power_g_p(m_x, m_group.p_bits());
+ }
+ }
+
+/*
+* Return the public value for key agreement
+*/
+std::vector<uint8_t> DH_PrivateKey::public_value() const
+ {
+ return DH_PublicKey::public_value();
+ }
+
+namespace {
+
+/**
+* DH operation
+*/
+class DH_KA_Operation final : public PK_Ops::Key_Agreement_with_KDF
+ {
+ public:
+
+ DH_KA_Operation(const DH_PrivateKey& key, const std::string& kdf, RandomNumberGenerator& rng) :
+ PK_Ops::Key_Agreement_with_KDF(kdf),
+ m_p(key.group_p()),
+ m_x(key.get_x()),
+ m_x_bits(m_x.bits()),
+ m_monty_p(key.get_group().monty_params_p()),
+ m_blinder(m_p,
+ rng,
+ [](const BigInt& k) { return k; },
+ [this](const BigInt& k) { return powermod_x_p(inverse_mod(k, m_p)); })
+ {}
+
+ size_t agreed_value_size() const override { return m_p.bytes(); }
+
+ secure_vector<uint8_t> raw_agree(const uint8_t w[], size_t w_len) override;
+ private:
+ BigInt powermod_x_p(const BigInt& v) const
+ {
+ const size_t powm_window = 4;
+ auto powm_v_p = monty_precompute(m_monty_p, v, powm_window);
+ return monty_execute(*powm_v_p, m_x, m_x_bits);
+ }
+
+ const BigInt& m_p;
+ const BigInt& m_x;
+ const size_t m_x_bits;
+ std::shared_ptr<const Montgomery_Params> m_monty_p;
+ Blinder m_blinder;
+ };
+
+secure_vector<uint8_t> DH_KA_Operation::raw_agree(const uint8_t w[], size_t w_len)
+ {
+ BigInt v = BigInt::decode(w, w_len);
+
+ if(v <= 1 || v >= m_p - 1)
+ throw Invalid_Argument("DH agreement - invalid key provided");
+
+ v = m_blinder.blind(v);
+ v = powermod_x_p(v);
+ v = m_blinder.unblind(v);
+
+ return BigInt::encode_1363(v, m_p.bytes());
+ }
+
+}
+
+std::unique_ptr<PK_Ops::Key_Agreement>
+DH_PrivateKey::create_key_agreement_op(RandomNumberGenerator& rng,
+ const std::string& params,
+ const std::string& provider) const
+ {
+ if(provider == "base" || provider.empty())
+ return std::unique_ptr<PK_Ops::Key_Agreement>(new DH_KA_Operation(*this, params, rng));
+ throw Provider_Not_Found(algo_name(), provider);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/pubkey/dh/dh.h b/comm/third_party/botan/src/lib/pubkey/dh/dh.h
new file mode 100644
index 0000000000..e3aa0d2c5b
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/dh/dh.h
@@ -0,0 +1,81 @@
+/*
+* Diffie-Hellman
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_DIFFIE_HELLMAN_H_
+#define BOTAN_DIFFIE_HELLMAN_H_
+
+#include <botan/dl_algo.h>
+
+namespace Botan {
+
+/**
+* This class represents Diffie-Hellman public keys.
+*/
+class BOTAN_PUBLIC_API(2,0) DH_PublicKey : public virtual DL_Scheme_PublicKey
+ {
+ public:
+ std::string algo_name() const override { return "DH"; }
+
+ std::vector<uint8_t> public_value() const;
+
+ DL_Group::Format group_format() const override { return DL_Group::ANSI_X9_42; }
+
+ /**
+ * Create a public key.
+ * @param alg_id the X.509 algorithm identifier
+ * @param key_bits DER encoded public key bits
+ */
+ DH_PublicKey(const AlgorithmIdentifier& alg_id,
+ const std::vector<uint8_t>& key_bits) :
+ DL_Scheme_PublicKey(alg_id, key_bits, DL_Group::ANSI_X9_42) {}
+
+ /**
+ * Construct a public key with the specified parameters.
+ * @param grp the DL group to use in the key
+ * @param y the public value y
+ */
+ DH_PublicKey(const DL_Group& grp, const BigInt& y);
+ protected:
+ DH_PublicKey() = default;
+ };
+
+/**
+* This class represents Diffie-Hellman private keys.
+*/
+class BOTAN_PUBLIC_API(2,0) DH_PrivateKey final : public DH_PublicKey,
+ public PK_Key_Agreement_Key,
+ public virtual DL_Scheme_PrivateKey
+ {
+ public:
+ std::vector<uint8_t> public_value() const override;
+
+ /**
+ * Load a private key.
+ * @param alg_id the X.509 algorithm identifier
+ * @param key_bits PKCS #8 structure
+ */
+ DH_PrivateKey(const AlgorithmIdentifier& alg_id,
+ const secure_vector<uint8_t>& key_bits);
+
+ /**
+ * Create a private key.
+ * @param rng random number generator to use
+ * @param grp the group to be used in the key
+ * @param x the key's secret value (or if zero, generate a new key)
+ */
+ DH_PrivateKey(RandomNumberGenerator& rng, const DL_Group& grp,
+ const BigInt& x = 0);
+
+ std::unique_ptr<PK_Ops::Key_Agreement>
+ create_key_agreement_op(RandomNumberGenerator& rng,
+ const std::string& params,
+ const std::string& provider) const override;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/pubkey/dh/info.txt b/comm/third_party/botan/src/lib/pubkey/dh/info.txt
new file mode 100644
index 0000000000..1b9ba24948
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/dh/info.txt
@@ -0,0 +1,13 @@
+<defines>
+DIFFIE_HELLMAN -> 20131128
+</defines>
+
+<header:public>
+dh.h
+</header:public>
+
+<requires>
+dl_algo
+dl_group
+numbertheory
+</requires>
diff --git a/comm/third_party/botan/src/lib/pubkey/dl_algo/dl_algo.cpp b/comm/third_party/botan/src/lib/pubkey/dl_algo/dl_algo.cpp
new file mode 100644
index 0000000000..15b0b175e4
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/dl_algo/dl_algo.cpp
@@ -0,0 +1,84 @@
+/*
+* DL Scheme
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/dl_algo.h>
+#include <botan/numthry.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
+
+namespace Botan {
+
+size_t DL_Scheme_PublicKey::key_length() const
+ {
+ return m_group.p_bits();
+ }
+
+size_t DL_Scheme_PublicKey::estimated_strength() const
+ {
+ return m_group.estimated_strength();
+ }
+
+AlgorithmIdentifier DL_Scheme_PublicKey::algorithm_identifier() const
+ {
+ return AlgorithmIdentifier(get_oid(),
+ m_group.DER_encode(group_format()));
+ }
+
+std::vector<uint8_t> DL_Scheme_PublicKey::public_key_bits() const
+ {
+ std::vector<uint8_t> output;
+ DER_Encoder(output).encode(m_y);
+ return output;
+ }
+
+DL_Scheme_PublicKey::DL_Scheme_PublicKey(const DL_Group& group, const BigInt& y) :
+ m_y(y),
+ m_group(group)
+ {
+ }
+
+DL_Scheme_PublicKey::DL_Scheme_PublicKey(const AlgorithmIdentifier& alg_id,
+ const std::vector<uint8_t>& key_bits,
+ DL_Group::Format format) :
+ m_group(alg_id.get_parameters(), format)
+ {
+ BER_Decoder(key_bits).decode(m_y);
+ }
+
+secure_vector<uint8_t> DL_Scheme_PrivateKey::private_key_bits() const
+ {
+ return DER_Encoder().encode(m_x).get_contents();
+ }
+
+DL_Scheme_PrivateKey::DL_Scheme_PrivateKey(const AlgorithmIdentifier& alg_id,
+ const secure_vector<uint8_t>& key_bits,
+ DL_Group::Format format)
+ {
+ m_group.BER_decode(alg_id.get_parameters(), format);
+
+ BER_Decoder(key_bits).decode(m_x);
+ }
+
+/*
+* Check Public DL Parameters
+*/
+bool DL_Scheme_PublicKey::check_key(RandomNumberGenerator& rng,
+ bool strong) const
+ {
+ return m_group.verify_group(rng, strong) && m_group.verify_public_element(m_y);
+ }
+
+/*
+* Check DL Scheme Private Parameters
+*/
+bool DL_Scheme_PrivateKey::check_key(RandomNumberGenerator& rng,
+ bool strong) const
+ {
+ return m_group.verify_group(rng, strong) && m_group.verify_element_pair(m_y, m_x);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/pubkey/dl_algo/dl_algo.h b/comm/third_party/botan/src/lib/pubkey/dl_algo/dl_algo.h
new file mode 100644
index 0000000000..af01bc217a
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/dl_algo/dl_algo.h
@@ -0,0 +1,140 @@
+/*
+* DL Scheme
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_DL_ALGO_H_
+#define BOTAN_DL_ALGO_H_
+
+#include <botan/dl_group.h>
+#include <botan/pk_keys.h>
+
+namespace Botan {
+
+/**
+* This class represents discrete logarithm (DL) public keys.
+*/
+class BOTAN_PUBLIC_API(2,0) DL_Scheme_PublicKey : public virtual Public_Key
+ {
+ public:
+ bool check_key(RandomNumberGenerator& rng, bool) const override;
+
+ AlgorithmIdentifier algorithm_identifier() const override;
+
+ std::vector<uint8_t> public_key_bits() const override;
+
+ /**
+ * Get the DL domain parameters of this key.
+ * @return DL domain parameters of this key
+ */
+ const DL_Group& get_domain() const { return m_group; }
+
+ /**
+ * Get the DL domain parameters of this key.
+ * @return DL domain parameters of this key
+ */
+ const DL_Group& get_group() const { return m_group; }
+
+ /**
+ * Get the public value y with y = g^x mod p where x is the secret key.
+ */
+ const BigInt& get_y() const { return m_y; }
+
+ /**
+ * Get the prime p of the underlying DL group.
+ * @return prime p
+ */
+ const BigInt& group_p() const { return m_group.get_p(); }
+
+ /**
+ * Get the prime q of the underlying DL group.
+ * @return prime q
+ */
+ const BigInt& group_q() const { return m_group.get_q(); }
+
+ /**
+ * Get the generator g of the underlying DL group.
+ * @return generator g
+ */
+ const BigInt& group_g() const { return m_group.get_g(); }
+
+ /**
+ * Get the underlying groups encoding format.
+ * @return encoding format
+ */
+ virtual DL_Group::Format group_format() const = 0;
+
+ size_t key_length() const override;
+ size_t estimated_strength() const override;
+
+ DL_Scheme_PublicKey& operator=(const DL_Scheme_PublicKey& other) = default;
+
+ protected:
+ DL_Scheme_PublicKey() = default;
+
+ /**
+ * Create a public key.
+ * @param alg_id the X.509 algorithm identifier
+ * @param key_bits DER encoded public key bits
+ * @param group_format the underlying groups encoding format
+ */
+ DL_Scheme_PublicKey(const AlgorithmIdentifier& alg_id,
+ const std::vector<uint8_t>& key_bits,
+ DL_Group::Format group_format);
+
+ DL_Scheme_PublicKey(const DL_Group& group, const BigInt& y);
+
+ /**
+ * The DL public key
+ */
+ BigInt m_y;
+
+ /**
+ * The DL group
+ */
+ DL_Group m_group;
+ };
+
+/**
+* This class represents discrete logarithm (DL) private keys.
+*/
+class BOTAN_PUBLIC_API(2,0) DL_Scheme_PrivateKey : public virtual DL_Scheme_PublicKey,
+ public virtual Private_Key
+ {
+ public:
+ bool check_key(RandomNumberGenerator& rng, bool) const override;
+
+ /**
+ * Get the secret key x.
+ * @return secret key
+ */
+ const BigInt& get_x() const { return m_x; }
+
+ secure_vector<uint8_t> private_key_bits() const override;
+
+ DL_Scheme_PrivateKey& operator=(const DL_Scheme_PrivateKey& other) = default;
+
+ protected:
+ /**
+ * Create a private key.
+ * @param alg_id the X.509 algorithm identifier
+ * @param key_bits DER encoded private key bits
+ * @param group_format the underlying groups encoding format
+ */
+ DL_Scheme_PrivateKey(const AlgorithmIdentifier& alg_id,
+ const secure_vector<uint8_t>& key_bits,
+ DL_Group::Format group_format);
+
+ DL_Scheme_PrivateKey() = default;
+
+ /**
+ * The DL private key
+ */
+ BigInt m_x;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/pubkey/dl_algo/info.txt b/comm/third_party/botan/src/lib/pubkey/dl_algo/info.txt
new file mode 100644
index 0000000000..44e649cd67
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/dl_algo/info.txt
@@ -0,0 +1,10 @@
+<defines>
+DL_PUBLIC_KEY_FAMILY -> 20131128
+</defines>
+
+<requires>
+asn1
+dl_group
+numbertheory
+rng
+</requires>
diff --git a/comm/third_party/botan/src/lib/pubkey/dl_group/dl_group.cpp b/comm/third_party/botan/src/lib/pubkey/dl_group/dl_group.cpp
new file mode 100644
index 0000000000..05f9640e30
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/dl_group/dl_group.cpp
@@ -0,0 +1,646 @@
+/*
+* Discrete Logarithm Parameters
+* (C) 1999-2008,2015,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/dl_group.h>
+#include <botan/numthry.h>
+#include <botan/reducer.h>
+#include <botan/monty.h>
+#include <botan/divide.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
+#include <botan/pem.h>
+#include <botan/workfactor.h>
+#include <botan/internal/monty_exp.h>
+
+namespace Botan {
+
+class DL_Group_Data final
+ {
+ public:
+ 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(p),
+ m_mod_q(q),
+ m_monty_params(std::make_shared<Montgomery_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()),
+ m_estimated_strength(dl_work_factor(m_p_bits)),
+ m_exponent_bits(dl_exponent_size(m_p_bits)),
+ m_source(source)
+ {
+ }
+
+ ~DL_Group_Data() = default;
+
+ DL_Group_Data(const DL_Group_Data& other) = delete;
+ DL_Group_Data& operator=(const DL_Group_Data& other) = delete;
+
+ const BigInt& p() const { return m_p; }
+ const BigInt& q() const { return m_q; }
+ const BigInt& g() const { return m_g; }
+
+ BigInt mod_p(const BigInt& x) const { return m_mod_p.reduce(x); }
+
+ BigInt multiply_mod_p(const BigInt& x, const BigInt& y) const
+ {
+ return m_mod_p.multiply(x, y);
+ }
+
+ BigInt mod_q(const BigInt& x) const { return m_mod_q.reduce(x); }
+
+ BigInt multiply_mod_q(const BigInt& x, const BigInt& y) const
+ {
+ return m_mod_q.multiply(x, y);
+ }
+
+ BigInt square_mod_q(const BigInt& x) const
+ {
+ return m_mod_q.square(x);
+ }
+
+ std::shared_ptr<const Montgomery_Params> monty_params_p() const
+ { return m_monty_params; }
+
+ size_t p_bits() const { return m_p_bits; }
+ size_t q_bits() const { return m_q_bits; }
+ size_t p_bytes() const { return (m_p_bits + 7) / 8; }
+ size_t q_bytes() const { return (m_q_bits + 7) / 8; }
+
+ size_t estimated_strength() const { return m_estimated_strength; }
+
+ size_t exponent_bits() const { return m_exponent_bits; }
+
+ BigInt power_g_p(const BigInt& k, size_t max_k_bits) const
+ {
+ return monty_execute(*m_monty, k, max_k_bits);
+ }
+
+ bool q_is_set() const { return m_q_bits > 0; }
+
+ void assert_q_is_set(const std::string& function) const
+ {
+ if(q_is_set() == false)
+ throw Invalid_State("DL_Group::" + function + " q is not set for this group");
+ }
+
+ DL_Group_Source source() const { return m_source; }
+
+ private:
+ BigInt m_p;
+ BigInt m_q;
+ BigInt m_g;
+ Modular_Reducer m_mod_p;
+ Modular_Reducer m_mod_q;
+ std::shared_ptr<const Montgomery_Params> m_monty_params;
+ std::shared_ptr<const Montgomery_Exponentation_State> m_monty;
+ size_t m_p_bits;
+ size_t m_q_bits;
+ size_t m_estimated_strength;
+ size_t m_exponent_bits;
+ DL_Group_Source m_source;
+ };
+
+//static
+std::shared_ptr<DL_Group_Data> DL_Group::BER_decode_DL_group(const uint8_t data[], size_t data_len,
+ DL_Group::Format format,
+ DL_Group_Source source)
+ {
+ BigInt p, q, g;
+
+ BER_Decoder decoder(data, data_len);
+ BER_Decoder ber = decoder.start_cons(SEQUENCE);
+
+ if(format == DL_Group::ANSI_X9_57)
+ {
+ ber.decode(p)
+ .decode(q)
+ .decode(g)
+ .verify_end();
+ }
+ else if(format == DL_Group::ANSI_X9_42)
+ {
+ ber.decode(p)
+ .decode(g)
+ .decode(q)
+ .discard_remaining();
+ }
+ else if(format == DL_Group::PKCS_3)
+ {
+ // q is left as zero
+ ber.decode(p)
+ .decode(g)
+ .discard_remaining();
+ }
+ else
+ throw Invalid_Argument("Unknown DL_Group encoding " + std::to_string(format));
+
+ return std::make_shared<DL_Group_Data>(p, q, g, source);
+ }
+
+//static
+std::shared_ptr<DL_Group_Data>
+DL_Group::load_DL_group_info(const char* p_str,
+ const char* q_str,
+ const char* g_str)
+ {
+ const BigInt p(p_str);
+ const BigInt q(q_str);
+ const BigInt g(g_str);
+
+ return std::make_shared<DL_Group_Data>(p, q, g, DL_Group_Source::Builtin);
+ }
+
+//static
+std::shared_ptr<DL_Group_Data>
+DL_Group::load_DL_group_info(const char* p_str,
+ const char* g_str)
+ {
+ const BigInt p(p_str);
+ const BigInt q = (p - 1) / 2;
+ const BigInt g(g_str);
+
+ return std::make_shared<DL_Group_Data>(p, q, g, DL_Group_Source::Builtin);
+ }
+
+namespace {
+
+DL_Group::Format pem_label_to_dl_format(const std::string& label)
+ {
+ if(label == "DH PARAMETERS")
+ return DL_Group::PKCS_3;
+ else if(label == "DSA PARAMETERS")
+ return DL_Group::ANSI_X9_57;
+ else if(label == "X942 DH PARAMETERS" || label == "X9.42 DH PARAMETERS")
+ return DL_Group::ANSI_X9_42;
+ else
+ throw Decoding_Error("DL_Group: Invalid PEM label " + label);
+ }
+
+}
+
+/*
+* DL_Group Constructor
+*/
+DL_Group::DL_Group(const std::string& str)
+ {
+ // Either a name or a PEM block, try name first
+ m_data = DL_group_info(str);
+
+ if(m_data == nullptr)
+ {
+ try
+ {
+ std::string label;
+ const std::vector<uint8_t> ber = unlock(PEM_Code::decode(str, label));
+ Format format = pem_label_to_dl_format(label);
+
+ m_data = BER_decode_DL_group(ber.data(), ber.size(), format, DL_Group_Source::ExternalSource);
+ }
+ catch(...) {}
+ }
+
+ if(m_data == nullptr)
+ throw Invalid_Argument("DL_Group: Unknown group " + str);
+ }
+
+namespace {
+
+/*
+* Create generator of the q-sized subgroup (DSA style generator)
+*/
+BigInt make_dsa_generator(const BigInt& p, const BigInt& q)
+ {
+ BigInt e, 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");
+
+ for(size_t i = 0; i != PRIME_TABLE_SIZE; ++i)
+ {
+ // TODO precompute!
+ BigInt g = power_mod(PRIMES[i], e, p);
+ if(g > 1)
+ return g;
+ }
+
+ throw Internal_Error("DL_Group: Couldn't create a suitable generator");
+ }
+
+}
+
+/*
+* DL_Group Constructor
+*/
+DL_Group::DL_Group(RandomNumberGenerator& rng,
+ PrimeType type, size_t pbits, size_t qbits)
+ {
+ if(pbits < 1024)
+ throw Invalid_Argument("DL_Group: prime size " + std::to_string(pbits) + " is too small");
+
+ if(type == Strong)
+ {
+ if(qbits != 0 && qbits != pbits - 1)
+ throw Invalid_Argument("Cannot create strong-prime DL_Group with specified q bits");
+
+ const BigInt p = random_safe_prime(rng, pbits);
+ 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.
+ */
+ BigInt g = 2;
+ if(jacobi(g, p) != 1)
+ {
+ // prime table does not contain 2
+ for(size_t i = 0; i < PRIME_TABLE_SIZE; ++i)
+ {
+ g = PRIMES[i];
+ if(jacobi(g, p) == 1)
+ break;
+ }
+ }
+
+ m_data = std::make_shared<DL_Group_Data>(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);
+ Modular_Reducer mod_2q(2*q);
+ 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;
+ }
+
+ const BigInt g = make_dsa_generator(p, q);
+ m_data = std::make_shared<DL_Group_Data>(p, q, g, DL_Group_Source::RandomlyGenerated);
+ }
+ else if(type == DSA_Kosherizer)
+ {
+ if(qbits == 0)
+ qbits = ((pbits <= 1024) ? 160 : 256);
+
+ BigInt p, q;
+ generate_dsa_primes(rng, p, q, pbits, qbits);
+ const BigInt g = make_dsa_generator(p, q);
+ m_data = std::make_shared<DL_Group_Data>(p, q, g, DL_Group_Source::RandomlyGenerated);
+ }
+ else
+ {
+ throw Invalid_Argument("DL_Group unknown PrimeType");
+ }
+ }
+
+/*
+* DL_Group Constructor
+*/
+DL_Group::DL_Group(RandomNumberGenerator& rng,
+ const std::vector<uint8_t>& seed,
+ size_t pbits, size_t qbits)
+ {
+ BigInt p, 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);
+
+ m_data = std::make_shared<DL_Group_Data>(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<DL_Group_Data>(p, 0, g, DL_Group_Source::ExternalSource);
+ }
+
+/*
+* DL_Group Constructor
+*/
+DL_Group::DL_Group(const BigInt& p, const BigInt& q, const BigInt& g)
+ {
+ m_data = std::make_shared<DL_Group_Data>(p, q, g, DL_Group_Source::ExternalSource);
+ }
+
+const DL_Group_Data& DL_Group::data() const
+ {
+ if(m_data)
+ return *m_data;
+
+ throw Invalid_State("DL_Group uninitialized");
+ }
+
+bool DL_Group::verify_public_element(const BigInt& y) const
+ {
+ const BigInt& p = get_p();
+ const BigInt& q = get_q();
+
+ if(y <= 1 || y >= p)
+ return false;
+
+ if(q.is_zero() == false)
+ {
+ if(power_mod(y, q, p) != 1)
+ return false;
+ }
+
+ return true;
+ }
+
+bool DL_Group::verify_element_pair(const BigInt& y, const BigInt& x) const
+ {
+ const BigInt& p = get_p();
+
+ if(y <= 1 || y >= p || x <= 1 || x >= p)
+ return false;
+
+ if(y != power_g_p(x))
+ return false;
+
+ return true;
+ }
+
+/*
+* Verify the parameters
+*/
+bool DL_Group::verify_group(RandomNumberGenerator& rng,
+ bool strong) const
+ {
+ const bool from_builtin = (source() == DL_Group_Source::Builtin);
+
+ if(!strong && from_builtin)
+ return true;
+
+ const BigInt& p = get_p();
+ const BigInt& q = get_q();
+ const BigInt& g = get_g();
+
+ if(g < 2 || p < 3 || q < 0)
+ return false;
+
+ const size_t test_prob = 128;
+ const bool is_randomly_generated = (source() != DL_Group_Source::ExternalSource);
+
+ if(q != 0)
+ {
+ if((p - 1) % q != 0)
+ {
+ return false;
+ }
+ if(this->power_g_p(q) != 1)
+ {
+ return false;
+ }
+ if(!is_prime(q, rng, test_prob, is_randomly_generated))
+ {
+ return false;
+ }
+ }
+
+ if(!is_prime(p, rng, test_prob, is_randomly_generated))
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+/*
+* Return the prime
+*/
+const BigInt& DL_Group::get_p() const
+ {
+ return data().p();
+ }
+
+/*
+* Return the generator
+*/
+const BigInt& DL_Group::get_g() const
+ {
+ return data().g();
+ }
+
+/*
+* Return the subgroup
+*/
+const BigInt& DL_Group::get_q() const
+ {
+ return data().q();
+ }
+
+std::shared_ptr<const Montgomery_Params> DL_Group::monty_params_p() const
+ {
+ return data().monty_params_p();
+ }
+
+size_t DL_Group::p_bits() const
+ {
+ return data().p_bits();
+ }
+
+size_t DL_Group::p_bytes() const
+ {
+ return data().p_bytes();
+ }
+
+size_t DL_Group::q_bits() const
+ {
+ data().assert_q_is_set("q_bits");
+ return data().q_bits();
+ }
+
+size_t DL_Group::q_bytes() const
+ {
+ data().assert_q_is_set("q_bytes");
+ return data().q_bytes();
+ }
+
+size_t DL_Group::estimated_strength() const
+ {
+ return data().estimated_strength();
+ }
+
+size_t DL_Group::exponent_bits() const
+ {
+ return data().exponent_bits();
+ }
+
+BigInt DL_Group::inverse_mod_p(const BigInt& x) const
+ {
+ // precompute??
+ return inverse_mod(x, get_p());
+ }
+
+BigInt DL_Group::mod_p(const BigInt& x) const
+ {
+ return data().mod_p(x);
+ }
+
+BigInt DL_Group::multiply_mod_p(const BigInt& x, const BigInt& y) const
+ {
+ return data().multiply_mod_p(x, y);
+ }
+
+BigInt DL_Group::inverse_mod_q(const BigInt& x) const
+ {
+ data().assert_q_is_set("inverse_mod_q");
+ // precompute??
+ return inverse_mod(x, get_q());
+ }
+
+BigInt DL_Group::mod_q(const BigInt& x) const
+ {
+ data().assert_q_is_set("mod_q");
+ return data().mod_q(x);
+ }
+
+BigInt DL_Group::multiply_mod_q(const BigInt& x, const BigInt& y) const
+ {
+ data().assert_q_is_set("multiply_mod_q");
+ return data().multiply_mod_q(x, y);
+ }
+
+BigInt DL_Group::multiply_mod_q(const BigInt& x, const BigInt& y, const BigInt& z) const
+ {
+ data().assert_q_is_set("multiply_mod_q");
+ return data().multiply_mod_q(data().multiply_mod_q(x, y), z);
+ }
+
+BigInt DL_Group::square_mod_q(const BigInt& x) const
+ {
+ data().assert_q_is_set("square_mod_q");
+ return data().square_mod_q(x);
+ }
+
+BigInt DL_Group::multi_exponentiate(const BigInt& x, const BigInt& y, const BigInt& z) const
+ {
+ return monty_multi_exp(data().monty_params_p(), get_g(), x, y, z);
+ }
+
+BigInt DL_Group::power_g_p(const BigInt& x) const
+ {
+ return data().power_g_p(x, x.bits());
+ }
+
+BigInt DL_Group::power_g_p(const BigInt& x, size_t max_x_bits) const
+ {
+ return data().power_g_p(x, max_x_bits);
+ }
+
+DL_Group_Source DL_Group::source() const
+ {
+ return data().source();
+ }
+
+/*
+* DER encode the parameters
+*/
+std::vector<uint8_t> DL_Group::DER_encode(Format format) const
+ {
+ if(get_q().is_zero() && (format == ANSI_X9_57 || format == ANSI_X9_42))
+ throw Encoding_Error("Cannot encode DL_Group in ANSI formats when q param is missing");
+
+ std::vector<uint8_t> output;
+ DER_Encoder der(output);
+
+ if(format == ANSI_X9_57)
+ {
+ der.start_cons(SEQUENCE)
+ .encode(get_p())
+ .encode(get_q())
+ .encode(get_g())
+ .end_cons();
+ }
+ else if(format == ANSI_X9_42)
+ {
+ der.start_cons(SEQUENCE)
+ .encode(get_p())
+ .encode(get_g())
+ .encode(get_q())
+ .end_cons();
+ }
+ else if(format == PKCS_3)
+ {
+ der.start_cons(SEQUENCE)
+ .encode(get_p())
+ .encode(get_g())
+ .end_cons();
+ }
+ else
+ throw Invalid_Argument("Unknown DL_Group encoding " + std::to_string(format));
+
+ return output;
+ }
+
+/*
+* PEM encode the parameters
+*/
+std::string DL_Group::PEM_encode(Format format) const
+ {
+ const std::vector<uint8_t> encoding = DER_encode(format);
+
+ if(format == PKCS_3)
+ return PEM_Code::encode(encoding, "DH PARAMETERS");
+ else if(format == ANSI_X9_57)
+ return PEM_Code::encode(encoding, "DSA PARAMETERS");
+ else if(format == ANSI_X9_42)
+ return PEM_Code::encode(encoding, "X9.42 DH PARAMETERS");
+ else
+ throw Invalid_Argument("Unknown DL_Group encoding " + std::to_string(format));
+ }
+
+DL_Group::DL_Group(const uint8_t ber[], size_t ber_len, Format format)
+ {
+ m_data = BER_decode_DL_group(ber, ber_len, format, DL_Group_Source::ExternalSource);
+ }
+
+void DL_Group::BER_decode(const std::vector<uint8_t>& ber, Format format)
+ {
+ m_data = BER_decode_DL_group(ber.data(), ber.size(), format, DL_Group_Source::ExternalSource);
+ }
+
+//static
+DL_Group DL_Group::DL_Group_from_PEM(const std::string& pem)
+ {
+ std::string label;
+ const std::vector<uint8_t> ber = unlock(PEM_Code::decode(pem, label));
+ Format format = pem_label_to_dl_format(label);
+ return DL_Group(ber, format);
+ }
+
+/*
+* Decode PEM encoded parameters
+*/
+void DL_Group::PEM_decode(const std::string& pem)
+ {
+ std::string label;
+ const std::vector<uint8_t> ber = unlock(PEM_Code::decode(pem, label));
+ Format format = pem_label_to_dl_format(label);
+
+ m_data = BER_decode_DL_group(ber.data(), ber.size(), format, DL_Group_Source::ExternalSource);
+ }
+
+//static
+std::string DL_Group::PEM_for_named_group(const std::string& name)
+ {
+ DL_Group group(name);
+ DL_Group::Format format = group.get_q().is_zero() ? DL_Group::PKCS_3 : DL_Group::ANSI_X9_42;
+ return group.PEM_encode(format);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/pubkey/dl_group/dl_group.h b/comm/third_party/botan/src/lib/pubkey/dl_group/dl_group.h
new file mode 100644
index 0000000000..98ce7a7ad7
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/dl_group/dl_group.h
@@ -0,0 +1,357 @@
+/*
+* Discrete Logarithm Group
+* (C) 1999-2008,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_DL_PARAM_H_
+#define BOTAN_DL_PARAM_H_
+
+#include <botan/bigint.h>
+
+namespace Botan {
+
+class Montgomery_Params;
+class DL_Group_Data;
+
+enum class DL_Group_Source {
+ Builtin,
+ RandomlyGenerated,
+ ExternalSource,
+};
+
+/**
+* This class represents discrete logarithm groups. It holds a prime
+* modulus p, a generator g, and (optionally) a prime q which is a
+* factor of (p-1). In most cases g generates the order-q subgroup.
+*/
+class BOTAN_PUBLIC_API(2,0) DL_Group final
+ {
+ public:
+ /**
+ * Determine the prime creation for DL groups.
+ */
+ enum PrimeType { Strong, Prime_Subgroup, DSA_Kosherizer };
+
+ /**
+ * The DL group encoding format variants.
+ */
+ enum Format {
+ ANSI_X9_42,
+ ANSI_X9_57,
+ PKCS_3,
+
+ DSA_PARAMETERS = ANSI_X9_57,
+ DH_PARAMETERS = ANSI_X9_42,
+ ANSI_X9_42_DH_PARAMETERS = ANSI_X9_42,
+ PKCS3_DH_PARAMETERS = PKCS_3
+ };
+
+ /**
+ * Construct a DL group with uninitialized internal value.
+ * Use this constructor is you wish to set the groups values
+ * from a DER or PEM encoded group.
+ */
+ DL_Group() = default;
+
+ /**
+ * Construct a DL group that is registered in the configuration.
+ * @param name the name of the group, for example "modp/ietf/3072"
+ *
+ * @warning This constructor also accepts PEM inputs. This behavior is
+ * deprecated and will be removed in a future major release. Instead
+ * use DL_Group_from_PEM function
+ */
+ explicit DL_Group(const std::string& name);
+
+ /*
+ * Read a PEM representation
+ */
+ static DL_Group DL_Group_from_PEM(const std::string& pem);
+
+ /**
+ * Create a new group randomly.
+ * @param rng the random number generator to use
+ * @param type specifies how the creation of primes p and q shall
+ * be performed. If type=Strong, then p will be determined as a
+ * safe prime, and q will be chosen as (p-1)/2. If
+ * type=Prime_Subgroup and qbits = 0, then the size of q will be
+ * determined according to the estimated difficulty of the DL
+ * problem. If type=DSA_Kosherizer, DSA primes will be created.
+ * @param pbits the number of bits of p
+ * @param qbits the number of bits of q. Leave it as 0 to have
+ * the value determined according to pbits.
+ */
+ DL_Group(RandomNumberGenerator& rng, PrimeType type,
+ size_t pbits, size_t qbits = 0);
+
+ /**
+ * Create a DSA group with a given seed.
+ * @param rng the random number generator to use
+ * @param seed the seed to use to create the random primes
+ * @param pbits the desired bit size of the prime p
+ * @param qbits the desired bit size of the prime q.
+ */
+ DL_Group(RandomNumberGenerator& rng,
+ const std::vector<uint8_t>& seed,
+ size_t pbits = 1024, size_t qbits = 0);
+
+ /**
+ * Create a DL group.
+ * @param p the prime p
+ * @param g the base g
+ */
+ DL_Group(const BigInt& p, const BigInt& g);
+
+ /**
+ * Create a DL group.
+ * @param p the prime p
+ * @param q the prime q
+ * @param g the base g
+ */
+ DL_Group(const BigInt& p, const BigInt& q, const BigInt& g);
+
+ /**
+ * Decode a BER-encoded DL group param
+ */
+ DL_Group(const uint8_t ber[], size_t ber_len, Format format);
+
+ /**
+ * Decode a BER-encoded DL group param
+ */
+ template<typename Alloc>
+ DL_Group(const std::vector<uint8_t, Alloc>& ber, Format format) :
+ DL_Group(ber.data(), ber.size(), format) {}
+
+ /**
+ * Get the prime p.
+ * @return prime p
+ */
+ const BigInt& get_p() const;
+
+ /**
+ * Get the prime q, returns zero if q is not used
+ * @return prime q
+ */
+ const BigInt& get_q() const;
+
+ /**
+ * Get the base g.
+ * @return base g
+ */
+ const BigInt& get_g() const;
+
+ /**
+ * Perform validity checks on the group.
+ * @param rng the rng to use
+ * @param strong whether to perform stronger by lengthier tests
+ * @return true if the object is consistent, false otherwise
+ */
+ bool verify_group(RandomNumberGenerator& rng, bool strong = true) const;
+
+ /**
+ * Verify a public element, ie check if y = g^x for some x.
+ *
+ * This is not a perfect test. It verifies that 1 < y < p and (if q is set)
+ * that y is in the subgroup of size q.
+ */
+ bool verify_public_element(const BigInt& y) const;
+
+ /**
+ * Verify a pair of elements y = g^x
+ *
+ * This verifies that 1 < x,y < p and that y=g^x mod p
+ */
+ bool verify_element_pair(const BigInt& y, const BigInt& x) const;
+
+ /**
+ * Encode this group into a string using PEM encoding.
+ * @param format the encoding format
+ * @return string holding the PEM encoded group
+ */
+ std::string PEM_encode(Format format) const;
+
+ /**
+ * Encode this group into a string using DER encoding.
+ * @param format the encoding format
+ * @return string holding the DER encoded group
+ */
+ std::vector<uint8_t> DER_encode(Format format) const;
+
+ /**
+ * Reduce an integer modulo p
+ * @return x % p
+ */
+ BigInt mod_p(const BigInt& x) const;
+
+ /**
+ * Multiply and reduce an integer modulo p
+ * @return (x*y) % p
+ */
+ BigInt multiply_mod_p(const BigInt& x, const BigInt& y) const;
+
+ /**
+ * Return the inverse of x mod p
+ */
+ BigInt inverse_mod_p(const BigInt& x) const;
+
+ /**
+ * Reduce an integer modulo q
+ * Throws if q is unset on this DL_Group
+ * @return x % q
+ */
+ BigInt mod_q(const BigInt& x) const;
+
+ /**
+ * Multiply and reduce an integer modulo q
+ * Throws if q is unset on this DL_Group
+ * @return (x*y) % q
+ */
+ BigInt multiply_mod_q(const BigInt& x, const BigInt& y) const;
+
+ /**
+ * Multiply and reduce an integer modulo q
+ * Throws if q is unset on this DL_Group
+ * @return (x*y*z) % q
+ */
+ BigInt multiply_mod_q(const BigInt& x, const BigInt& y, const BigInt& z) const;
+
+ /**
+ * Square and reduce an integer modulo q
+ * Throws if q is unset on this DL_Group
+ * @return (x*x) % q
+ */
+ BigInt square_mod_q(const BigInt& x) const;
+
+ /**
+ * Return the inverse of x mod q
+ * Throws if q is unset on this DL_Group
+ */
+ BigInt inverse_mod_q(const BigInt& x) const;
+
+ /**
+ * Modular exponentiation
+ *
+ * @warning this function leaks the size of x via the number of
+ * loop iterations. Use the version taking the maximum size to
+ * avoid this.
+ *
+ * @return (g^x) % p
+ */
+ BigInt power_g_p(const BigInt& x) const;
+
+ /**
+ * Modular exponentiation
+ * @param x the exponent
+ * @param max_x_bits x is assumed to be at most this many bits long.
+ *
+ * @return (g^x) % p
+ */
+ BigInt power_g_p(const BigInt& x, size_t max_x_bits) const;
+
+ /**
+ * Multi-exponentiate
+ * Return (g^x * y^z) % p
+ */
+ BigInt multi_exponentiate(const BigInt& x, const BigInt& y, const BigInt& z) const;
+
+ /**
+ * Return parameters for Montgomery reduction/exponentiation mod p
+ */
+ std::shared_ptr<const Montgomery_Params> monty_params_p() const;
+
+ /**
+ * Return the size of p in bits
+ * Same as get_p().bits()
+ */
+ size_t p_bits() const;
+
+ /**
+ * Return the size of p in bytes
+ * Same as get_p().bytes()
+ */
+ size_t p_bytes() const;
+
+ /**
+ * Return the size of q in bits
+ * Same as get_q().bits()
+ * Throws if q is unset
+ */
+ size_t q_bits() const;
+
+ /**
+ * Return the size of q in bytes
+ * Same as get_q().bytes()
+ * Throws if q is unset
+ */
+ size_t q_bytes() const;
+
+ /**
+ * Return size in bits of a secret exponent
+ *
+ * This attempts to balance between the attack costs of NFS
+ * (which depends on the size of the modulus) and Pollard's rho
+ * (which depends on the size of the exponent).
+ *
+ * It may vary over time for a particular group, if the attack
+ * costs change.
+ */
+ size_t exponent_bits() const;
+
+ /**
+ * Return an estimate of the strength of this group against
+ * discrete logarithm attacks (eg NFS). Warning: since this only
+ * takes into account known attacks it is by necessity an
+ * overestimate of the actual strength.
+ */
+ size_t estimated_strength() const;
+
+ /**
+ * Decode a DER/BER encoded group into this instance.
+ * @param ber a vector containing the DER/BER encoded group
+ * @param format the format of the encoded group
+ *
+ * @warning avoid this. Instead use the DL_Group constructor
+ */
+ void BER_decode(const std::vector<uint8_t>& ber, Format format);
+
+ /**
+ * Decode a PEM encoded group into this instance.
+ * @param pem the PEM encoding of the group
+ */
+ void BOTAN_DEPRECATED("Use DL_Group_from_PEM") PEM_decode(const std::string& pem);
+
+ DL_Group_Source source() const;
+
+ /**
+ * Return PEM representation of named DL group
+ */
+ static std::string BOTAN_DEPRECATED("Use DL_Group(name).PEM_encode()")
+ PEM_for_named_group(const std::string& name);
+
+ /*
+ * For internal use only
+ */
+ static std::shared_ptr<DL_Group_Data> DL_group_info(const std::string& name);
+
+ private:
+ static std::shared_ptr<DL_Group_Data> load_DL_group_info(const char* p_str,
+ const char* q_str,
+ const char* g_str);
+
+ static std::shared_ptr<DL_Group_Data> load_DL_group_info(const char* p_str,
+ const char* g_str);
+
+ static std::shared_ptr<DL_Group_Data>
+ BER_decode_DL_group(const uint8_t data[], size_t data_len,
+ DL_Group::Format format,
+ DL_Group_Source source);
+
+ const DL_Group_Data& data() const;
+ std::shared_ptr<DL_Group_Data> m_data;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/pubkey/dl_group/dl_named.cpp b/comm/third_party/botan/src/lib/pubkey/dl_group/dl_named.cpp
new file mode 100644
index 0000000000..4d7b71bc1d
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/dl_group/dl_named.cpp
@@ -0,0 +1,175 @@
+/*
+* List of discrete log groups
+* (C) 2013 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/dl_group.h>
+
+namespace Botan {
+
+//static
+std::shared_ptr<DL_Group_Data> DL_Group::DL_group_info(const std::string& name)
+ {
+ /* TLS FFDHE groups */
+
+ if(name == "ffdhe/ietf/2048")
+ {
+ return load_DL_group_info("0xFFFFFFFFFFFFFFFFADF85458A2BB4A9AAFDC5620273D3CF1D8B9C583CE2D3695A9E13641146433FBCC939DCE249B3EF97D2FE363630C75D8F681B202AEC4617AD3DF1ED5D5FD65612433F51F5F066ED0856365553DED1AF3B557135E7F57C935984F0C70E0E68B77E2A689DAF3EFE8721DF158A136ADE73530ACCA4F483A797ABC0AB182B324FB61D108A94BB2C8E3FBB96ADAB760D7F4681D4F42A3DE394DF4AE56EDE76372BB190B07A7C8EE0A6D709E02FCE1CDF7E2ECC03404CD28342F619172FE9CE98583FF8E4F1232EEF28183C3FE3B1B4C6FAD733BB5FCBC2EC22005C58EF1837D1683B2C6F34A26C1B2EFFA886B423861285C97FFFFFFFFFFFFFFFF",
+ "0x2");
+ }
+
+ if(name == "ffdhe/ietf/3072")
+ {
+ return load_DL_group_info("0xFFFFFFFFFFFFFFFFADF85458A2BB4A9AAFDC5620273D3CF1D8B9C583CE2D3695A9E13641146433FBCC939DCE249B3EF97D2FE363630C75D8F681B202AEC4617AD3DF1ED5D5FD65612433F51F5F066ED0856365553DED1AF3B557135E7F57C935984F0C70E0E68B77E2A689DAF3EFE8721DF158A136ADE73530ACCA4F483A797ABC0AB182B324FB61D108A94BB2C8E3FBB96ADAB760D7F4681D4F42A3DE394DF4AE56EDE76372BB190B07A7C8EE0A6D709E02FCE1CDF7E2ECC03404CD28342F619172FE9CE98583FF8E4F1232EEF28183C3FE3B1B4C6FAD733BB5FCBC2EC22005C58EF1837D1683B2C6F34A26C1B2EFFA886B4238611FCFDCDE355B3B6519035BBC34F4DEF99C023861B46FC9D6E6C9077AD91D2691F7F7EE598CB0FAC186D91CAEFE130985139270B4130C93BC437944F4FD4452E2D74DD364F2E21E71F54BFF5CAE82AB9C9DF69EE86D2BC522363A0DABC521979B0DEADA1DBF9A42D5C4484E0ABCD06BFA53DDEF3C1B20EE3FD59D7C25E41D2B66C62E37FFFFFFFFFFFFFFFF",
+ "0x2");
+ }
+
+ if(name == "ffdhe/ietf/4096")
+ {
+ return load_DL_group_info("0xFFFFFFFFFFFFFFFFADF85458A2BB4A9AAFDC5620273D3CF1D8B9C583CE2D3695A9E13641146433FBCC939DCE249B3EF97D2FE363630C75D8F681B202AEC4617AD3DF1ED5D5FD65612433F51F5F066ED0856365553DED1AF3B557135E7F57C935984F0C70E0E68B77E2A689DAF3EFE8721DF158A136ADE73530ACCA4F483A797ABC0AB182B324FB61D108A94BB2C8E3FBB96ADAB760D7F4681D4F42A3DE394DF4AE56EDE76372BB190B07A7C8EE0A6D709E02FCE1CDF7E2ECC03404CD28342F619172FE9CE98583FF8E4F1232EEF28183C3FE3B1B4C6FAD733BB5FCBC2EC22005C58EF1837D1683B2C6F34A26C1B2EFFA886B4238611FCFDCDE355B3B6519035BBC34F4DEF99C023861B46FC9D6E6C9077AD91D2691F7F7EE598CB0FAC186D91CAEFE130985139270B4130C93BC437944F4FD4452E2D74DD364F2E21E71F54BFF5CAE82AB9C9DF69EE86D2BC522363A0DABC521979B0DEADA1DBF9A42D5C4484E0ABCD06BFA53DDEF3C1B20EE3FD59D7C25E41D2B669E1EF16E6F52C3164DF4FB7930E9E4E58857B6AC7D5F42D69F6D187763CF1D5503400487F55BA57E31CC7A7135C886EFB4318AED6A1E012D9E6832A907600A918130C46DC778F971AD0038092999A333CB8B7A1A1DB93D7140003C2A4ECEA9F98D0ACC0A8291CDCEC97DCF8EC9B55A7F88A46B4DB5A851F44182E1C68A007E5E655F6AFFFFFFFFFFFFFFFF",
+ "0x2");
+ }
+
+ if(name == "ffdhe/ietf/6144")
+ {
+ return load_DL_group_info("0xFFFFFFFFFFFFFFFFADF85458A2BB4A9AAFDC5620273D3CF1D8B9C583CE2D3695A9E13641146433FBCC939DCE249B3EF97D2FE363630C75D8F681B202AEC4617AD3DF1ED5D5FD65612433F51F5F066ED0856365553DED1AF3B557135E7F57C935984F0C70E0E68B77E2A689DAF3EFE8721DF158A136ADE73530ACCA4F483A797ABC0AB182B324FB61D108A94BB2C8E3FBB96ADAB760D7F4681D4F42A3DE394DF4AE56EDE76372BB190B07A7C8EE0A6D709E02FCE1CDF7E2ECC03404CD28342F619172FE9CE98583FF8E4F1232EEF28183C3FE3B1B4C6FAD733BB5FCBC2EC22005C58EF1837D1683B2C6F34A26C1B2EFFA886B4238611FCFDCDE355B3B6519035BBC34F4DEF99C023861B46FC9D6E6C9077AD91D2691F7F7EE598CB0FAC186D91CAEFE130985139270B4130C93BC437944F4FD4452E2D74DD364F2E21E71F54BFF5CAE82AB9C9DF69EE86D2BC522363A0DABC521979B0DEADA1DBF9A42D5C4484E0ABCD06BFA53DDEF3C1B20EE3FD59D7C25E41D2B669E1EF16E6F52C3164DF4FB7930E9E4E58857B6AC7D5F42D69F6D187763CF1D5503400487F55BA57E31CC7A7135C886EFB4318AED6A1E012D9E6832A907600A918130C46DC778F971AD0038092999A333CB8B7A1A1DB93D7140003C2A4ECEA9F98D0ACC0A8291CDCEC97DCF8EC9B55A7F88A46B4DB5A851F44182E1C68A007E5E0DD9020BFD64B645036C7A4E677D2C38532A3A23BA4442CAF53EA63BB454329B7624C8917BDD64B1C0FD4CB38E8C334C701C3ACDAD0657FCCFEC719B1F5C3E4E46041F388147FB4CFDB477A52471F7A9A96910B855322EDB6340D8A00EF092350511E30ABEC1FFF9E3A26E7FB29F8C183023C3587E38DA0077D9B4763E4E4B94B2BBC194C6651E77CAF992EEAAC0232A281BF6B3A739C1226116820AE8DB5847A67CBEF9C9091B462D538CD72B03746AE77F5E62292C311562A846505DC82DB854338AE49F5235C95B91178CCF2DD5CACEF403EC9D1810C6272B045B3B71F9DC6B80D63FDD4A8E9ADB1E6962A69526D43161C1A41D570D7938DAD4A40E329CD0E40E65FFFFFFFFFFFFFFFF",
+ "0x2");
+ }
+
+ if(name == "ffdhe/ietf/8192")
+ {
+ return load_DL_group_info("0xFFFFFFFFFFFFFFFFADF85458A2BB4A9AAFDC5620273D3CF1D8B9C583CE2D3695A9E13641146433FBCC939DCE249B3EF97D2FE363630C75D8F681B202AEC4617AD3DF1ED5D5FD65612433F51F5F066ED0856365553DED1AF3B557135E7F57C935984F0C70E0E68B77E2A689DAF3EFE8721DF158A136ADE73530ACCA4F483A797ABC0AB182B324FB61D108A94BB2C8E3FBB96ADAB760D7F4681D4F42A3DE394DF4AE56EDE76372BB190B07A7C8EE0A6D709E02FCE1CDF7E2ECC03404CD28342F619172FE9CE98583FF8E4F1232EEF28183C3FE3B1B4C6FAD733BB5FCBC2EC22005C58EF1837D1683B2C6F34A26C1B2EFFA886B4238611FCFDCDE355B3B6519035BBC34F4DEF99C023861B46FC9D6E6C9077AD91D2691F7F7EE598CB0FAC186D91CAEFE130985139270B4130C93BC437944F4FD4452E2D74DD364F2E21E71F54BFF5CAE82AB9C9DF69EE86D2BC522363A0DABC521979B0DEADA1DBF9A42D5C4484E0ABCD06BFA53DDEF3C1B20EE3FD59D7C25E41D2B669E1EF16E6F52C3164DF4FB7930E9E4E58857B6AC7D5F42D69F6D187763CF1D5503400487F55BA57E31CC7A7135C886EFB4318AED6A1E012D9E6832A907600A918130C46DC778F971AD0038092999A333CB8B7A1A1DB93D7140003C2A4ECEA9F98D0ACC0A8291CDCEC97DCF8EC9B55A7F88A46B4DB5A851F44182E1C68A007E5E0DD9020BFD64B645036C7A4E677D2C38532A3A23BA4442CAF53EA63BB454329B7624C8917BDD64B1C0FD4CB38E8C334C701C3ACDAD0657FCCFEC719B1F5C3E4E46041F388147FB4CFDB477A52471F7A9A96910B855322EDB6340D8A00EF092350511E30ABEC1FFF9E3A26E7FB29F8C183023C3587E38DA0077D9B4763E4E4B94B2BBC194C6651E77CAF992EEAAC0232A281BF6B3A739C1226116820AE8DB5847A67CBEF9C9091B462D538CD72B03746AE77F5E62292C311562A846505DC82DB854338AE49F5235C95B91178CCF2DD5CACEF403EC9D1810C6272B045B3B71F9DC6B80D63FDD4A8E9ADB1E6962A69526D43161C1A41D570D7938DAD4A40E329CCFF46AAA36AD004CF600C8381E425A31D951AE64FDB23FCEC9509D43687FEB69EDD1CC5E0B8CC3BDF64B10EF86B63142A3AB8829555B2F747C932665CB2C0F1CC01BD70229388839D2AF05E454504AC78B7582822846C0BA35C35F5C59160CC046FD8251541FC68C9C86B022BB7099876A460E7451A8A93109703FEE1C217E6C3826E52C51AA691E0E423CFC99E9E31650C1217B624816CDAD9A95F9D5B8019488D9C0A0A1FE3075A577E23183F81D4A3F2FA4571EFC8CE0BA8A4FE8B6855DFE72B0A66EDED2FBABFBE58A30FAFABE1C5D71A87E2F741EF8C1FE86FEA6BBFDE530677F0D97D11D49F7A8443D0822E506A9F4614E011E2A94838FF88CD68C8BB7C5C6424CFFFFFFFFFFFFFFFF",
+ "0x2");
+ }
+
+ /* IETF IPsec groups */
+
+ if(name == "modp/ietf/1024")
+ {
+ return load_DL_group_info("0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF",
+ "0x2");
+ }
+
+ if(name == "modp/ietf/1536")
+ {
+ return load_DL_group_info("0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA237327FFFFFFFFFFFFFFFF",
+ "0x2");
+ }
+
+ if(name == "modp/ietf/2048")
+ {
+ return load_DL_group_info("0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF",
+ "0x2");
+ }
+
+ if(name == "modp/ietf/3072")
+ {
+ return load_DL_group_info("0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF",
+ "0x2");
+ }
+
+ if(name == "modp/ietf/4096")
+ {
+ return load_DL_group_info("0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199FFFFFFFFFFFFFFFF",
+ "0x2");
+ }
+
+ if(name == "modp/ietf/6144")
+ {
+ return load_DL_group_info("0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C93402849236C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AACC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E6DCC4024FFFFFFFFFFFFFFFF",
+ "0x2");
+ }
+
+ if(name == "modp/ietf/8192")
+ {
+ return load_DL_group_info("0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C93402849236C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AACC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E6DBE115974A3926F12FEE5E438777CB6A932DF8CD8BEC4D073B931BA3BC832B68D9DD300741FA7BF8AFC47ED2576F6936BA424663AAB639C5AE4F5683423B4742BF1C978238F16CBE39D652DE3FDB8BEFC848AD922222E04A4037C0713EB57A81A23F0C73473FC646CEA306B4BCBC8862F8385DDFA9D4B7FA2C087E879683303ED5BDD3A062B3CF5B3A278A66D2A13F83F44F82DDF310EE074AB6A364597E899A0255DC164F31CC50846851DF9AB48195DED7EA1B1D510BD7EE74D73FAF36BC31ECFA268359046F4EB879F924009438B481C6CD7889A002ED5EE382BC9190DA6FC026E479558E4475677E9AA9E3050E2765694DFC81F56E880B96E7160C980DD98EDD3DFFFFFFFFFFFFFFFFF",
+ "0x2");
+ }
+
+ /* SRP groups
+
+ SRP groups have a p st (p-1)/2 is prime, but g is not a generator
+ of subgroup of size q, so set q == 0 to bypass generator check
+
+ Missing q doesn't matter for SRP, and nothing but SRP should be
+ using these parameters.
+ */
+
+ if(name == "modp/srp/1024")
+ {
+ return load_DL_group_info("0xEEAF0AB9ADB38DD69C33F80AFA8FC5E86072618775FF3C0B9EA2314C9C256576D674DF7496EA81D3383B4813D692C6E0E0D5D8E250B98BE48E495C1D6089DAD15DC7D7B46154D6B6CE8EF4AD69B15D4982559B297BCF1885C529F566660E57EC68EDBC3C05726CC02FD4CBF4976EAA9AFD5138FE8376435B9FC61D2FC0EB06E3",
+ "0",
+ "0x2");
+ }
+
+ if(name == "modp/srp/1536")
+ {
+ return load_DL_group_info("0x9DEF3CAFB939277AB1F12A8617A47BBBDBA51DF499AC4C80BEEEA9614B19CC4D5F4F5F556E27CBDE51C6A94BE4607A291558903BA0D0F84380B655BB9A22E8DCDF028A7CEC67F0D08134B1C8B97989149B609E0BE3BAB63D47548381DBC5B1FC764E3F4B53DD9DA1158BFD3E2B9C8CF56EDF019539349627DB2FD53D24B7C48665772E437D6C7F8CE442734AF7CCB7AE837C264AE3A9BEB87F8A2FE9B8B5292E5A021FFF5E91479E8CE7A28C2442C6F315180F93499A234DCF76E3FED135F9BB",
+ "0",
+ "0x2");
+ }
+
+ if(name == "modp/srp/2048")
+ {
+ return load_DL_group_info("0xAC6BDB41324A9A9BF166DE5E1389582FAF72B6651987EE07FC3192943DB56050A37329CBB4A099ED8193E0757767A13DD52312AB4B03310DCD7F48A9DA04FD50E8083969EDB767B0CF6095179A163AB3661A05FBD5FAAAE82918A9962F0B93B855F97993EC975EEAA80D740ADBF4FF747359D041D5C33EA71D281E446B14773BCA97B43A23FB801676BD207A436C6481F1D2B9078717461A5B9D32E688F87748544523B524B0D57D5EA77A2775D2ECFA032CFBDBF52FB3786160279004E57AE6AF874E7303CE53299CCC041C7BC308D82A5698F3A8D0C38271AE35F8E9DBFBB694B5C803D89F7AE435DE236D525F54759B65E372FCD68EF20FA7111F9E4AFF73",
+ "0",
+ "0x2");
+ }
+
+ if(name == "modp/srp/3072")
+ {
+ return load_DL_group_info("0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF",
+ "0",
+ "0x5");
+ }
+
+ if(name == "modp/srp/4096")
+ {
+ return load_DL_group_info("0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199FFFFFFFFFFFFFFFF",
+ "0",
+ "0x5");
+ }
+
+ if(name == "modp/srp/6144")
+ {
+ return load_DL_group_info("0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C93402849236C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AACC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E6DCC4024FFFFFFFFFFFFFFFF",
+ "0",
+ "0x5");
+ }
+
+ if(name == "modp/srp/8192")
+ {
+ return load_DL_group_info("0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C93402849236C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AACC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E6DBE115974A3926F12FEE5E438777CB6A932DF8CD8BEC4D073B931BA3BC832B68D9DD300741FA7BF8AFC47ED2576F6936BA424663AAB639C5AE4F5683423B4742BF1C978238F16CBE39D652DE3FDB8BEFC848AD922222E04A4037C0713EB57A81A23F0C73473FC646CEA306B4BCBC8862F8385DDFA9D4B7FA2C087E879683303ED5BDD3A062B3CF5B3A278A66D2A13F83F44F82DDF310EE074AB6A364597E899A0255DC164F31CC50846851DF9AB48195DED7EA1B1D510BD7EE74D73FAF36BC31ECFA268359046F4EB879F924009438B481C6CD7889A002ED5EE382BC9190DA6FC026E479558E4475677E9AA9E3050E2765694DFC81F56E880B96E7160C980DD98EDD3DFFFFFFFFFFFFFFFFF",
+ "0",
+ "0x13");
+ }
+
+ /* DSA groups */
+
+ if(name == "dsa/jce/1024")
+ {
+ return load_DL_group_info("0xFD7F53811D75122952DF4A9C2EECE4E7F611B7523CEF4400C31E3F80B6512669455D402251FB593D8D58FABFC5F5BA30F6CB9B556CD7813B801D346FF26660B76B9950A5A49F9FE8047B1022C24FBBA9D7FEB7C61BF83B57E7C6A8A6150F04FB83F6D3C51EC3023554135A169132F675F3AE2B61D72AEFF22203199DD14801C7",
+ "0x9760508F15230BCCB292B982A2EB840BF0581CF5",
+ "0x469603512E30278CD3947595DB22EEC9826A6322ADC97344F41D740C325724C8F9EFBAA7D4D803FF8C609DCD100EBC5BDFCFAD7C6A425FAEA786EA2050EBE98351EA1FDA1FDF24D6947AA6B9AA23766953802F4D7D4A8ECBA06D19768A2491FFB16D0EF9C43A99B5F71672FF6F0A24B444D0736D04D38A1A1322DAF6CDD88C9D");
+ }
+
+ if(name == "dsa/botan/2048")
+ {
+ return load_DL_group_info("0x91C48A4FDFBCF7C02AE95E7DA126122B5DD2864F559B87E8E74A286D52F59BD1DE68DFD645D0E00C60C080031891980374EEB594A532BFD67B9A09EAC4B8663A07910E68F39465FB7040D25DF13932EBAC4347A530ECBA61C854F9B880D3C0C3660080587C45566DADE26BD5A394BE093B4C0F24B5AFFEF8EC6C5B3E57FB89025A9BC16769932131E16D3C94EFCAB18D0DF061203CC53E6103BC72D5594BFD40CA65380F44A9A851DCB075495FC033A8A58071A1BD78FE052F66555648EB4B719D2AFE8B4880F8DAD6F15818BA178F89274C870BE9B96EB08C46C40040CC2EFE1DFB1B1868DD319DE3C34A32A63AB6EB1224209A419680CC7902D1728D4DF9E1",
+ "0x8CD7D450F86F0AD94EEE4CE469A8756D1EBD1058241943EAFFB0B354585E924D",
+ "0xD9F5E0761B4DBD1833D6AB1A961A0996C5F22303F72D84C140F67C431D94AB5715BEA81A0C98D39CE4BCF78D6B9EBC895D34FE89D94091D5848615EF15F5E86F11D96F6C969E203DDFA58356420A49CB444B595B901A933CFE0767B594F18A07B7F91DECDBA446B88990F78F2FF91F2FE7CD43FD2E46D18EADA1F7BB6602C617F6EF3A4B284F2FD9BA10A36042DE8FA87A2CA36597FEC81157A1485E44041DF02830111CB880BBE6ED494814886F965CDC3135F5CCF1383728BF65B806F9692C0B10D6C4C09C75A6CA3B4013CB16AB2C105F6BE23AEA9000EAB2178985F972C98057E1C86E44E7218688EA4AE0F3636DCCA745C9DCD4E6AFFB67CCBC13D6131");
+ }
+
+ if(name == "dsa/botan/3072")
+ {
+ return load_DL_group_info("0xE4B50880759663585E142460CA2D9DFF132F8AE4C840DDA3A2666889124FE5638B84E8A29B7AF3FA1209BE6BFC4B5072ED3B2B7387BAF3F857F478A80228EF3600B76B3DCFB61D20D34465B2506D2CAF87DF6E7DC0CE91BD2D167A46F6ADCC31C531E4F9C7ABBDB92ADDF35B0A806C66292A5F5E17E964DD099903733AC428AB35D80EA6F685BFBA8BE4068E5418AE5ECAD9E8FF073DE2B63E4E7EAD35C8A9B70B5BD47CFB88D373B66F37931939B0AB71BD5595809086DA0155337D185A0E4FB36A519B1B6202B8591E6002449CF1CD3A66384F6D2073B1CD73BECA93BAF1E1A6117D0238F222AE1ED7FED185A890E7F67FAB8FEB9753CC134A5183DFE87AE2595F7B5C2D9FBB42249FDD59513E1D3396B3EB2FD86684F285A8448FE757A029881C40760B94EF919BDF9740C38389599EC51A6E9BB519A8E068491E9CE0A2FCFE3CB60D66CF0DFAD20A8EC684048684A61444575BD1724D7352B44A760077B3BD6BD385CE5B0A7250CC0BF768DA82923806EB9CFBB138843731B618208C759B",
+ "0xB3EBD364EC69EF8CF3BAF643B75734B16339B2E49E5CDE1B59C1E9FB40EE0C5B",
+ "0x2BED21EEF83964A230AE89BBA71D9F7C39C52FC8229B4E3BC7E5944D329DA10F010EAC9E7BAF6C009FC4EB2960723E2B56DF4663E4C3AC800E9258DE2F7649D206782893F865EFCA498D2EEF30074EA5E8A7AB262712A4D94A2F3B0B9A92EE400FB38A3CC59A5DC7E436D5C004B22E35028381B51C93407EB32D4AE0FD42CB45E12D0ECEE8A26238EDE2082A7B1522113C66CEF8D745C6CF3CB945F84D2F4DE16D44A71DE198270E13F03553C88B8D323AD0B948A1BF2103A949979B6ED16FB5F3C953D95B7C8E88CA67DCF5A636FB9CA39D924215F7A884ED6C7EE3C96D8D9715427974B7C4351282E13D3773F7D28B452F10892A13C7587328DEA4827B6B369B2A8DC172ADC583F51F2A6598C5483E5BC467B02F91D059C402D18E2C2680F776AA06F49280A2C72C17CC42D5B6E740C5C4B1AB3C51C2ED092BE2A2D8B053AE5773D1425ED2B08F06E2DD50592DF1A478C15591CDFD11564FF88FF38B721D42392FDA473212DCFD8D2D88A976A00AFFE6FFFB430A359E64CA2B351CA2412394");
+ }
+
+ return std::shared_ptr<DL_Group_Data>();
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/pubkey/dl_group/info.txt b/comm/third_party/botan/src/lib/pubkey/dl_group/info.txt
new file mode 100644
index 0000000000..a73edb18c1
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/dl_group/info.txt
@@ -0,0 +1,10 @@
+<defines>
+DL_GROUP -> 20131128
+</defines>
+
+<requires>
+asn1
+bigint
+numbertheory
+pem
+</requires>
diff --git a/comm/third_party/botan/src/lib/pubkey/dlies/dlies.cpp b/comm/third_party/botan/src/lib/pubkey/dlies/dlies.cpp
new file mode 100644
index 0000000000..4aee3ffb34
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/dlies/dlies.cpp
@@ -0,0 +1,219 @@
+/*
+* DLIES
+* (C) 1999-2007 Jack Lloyd
+* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/dlies.h>
+#include <limits>
+
+namespace Botan {
+
+DLIES_Encryptor::DLIES_Encryptor(const DH_PrivateKey& own_priv_key,
+ RandomNumberGenerator& rng,
+ KDF* kdf,
+ MessageAuthenticationCode* mac,
+ size_t mac_key_length) :
+ DLIES_Encryptor(own_priv_key, rng, kdf, nullptr, 0, mac, mac_key_length)
+ {
+ }
+
+DLIES_Encryptor::DLIES_Encryptor(const DH_PrivateKey& own_priv_key,
+ RandomNumberGenerator& rng,
+ KDF* kdf,
+ Cipher_Mode* cipher,
+ size_t cipher_key_len,
+ MessageAuthenticationCode* 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(kdf),
+ m_cipher(cipher),
+ m_cipher_key_len(cipher_key_len),
+ m_mac(mac),
+ m_mac_keylen(mac_key_length),
+ m_iv()
+ {
+ BOTAN_ASSERT_NONNULL(kdf);
+ BOTAN_ASSERT_NONNULL(mac);
+ }
+
+std::vector<uint8_t> DLIES_Encryptor::enc(const uint8_t in[], size_t length,
+ RandomNumberGenerator&) const
+ {
+ if(m_other_pub_key.empty())
+ {
+ throw Invalid_State("DLIES: The other key was never set");
+ }
+
+ // calculate secret value
+ const SymmetricKey secret_value = m_ka.derive_key(0, m_other_pub_key);
+
+ // derive secret key from secret value
+ const size_t required_key_length = m_cipher ? m_cipher_key_len + m_mac_keylen : length + m_mac_keylen;
+ const secure_vector<uint8_t> secret_keys = m_kdf->derive_key(required_key_length, secret_value.bits_of());
+
+ if(secret_keys.size() != required_key_length)
+ {
+ throw Encoding_Error("DLIES: KDF did not provide sufficient output");
+ }
+
+ secure_vector<uint8_t> ciphertext(in, in + length);
+ 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);
+ m_cipher->set_key(enc_key);
+
+ if(m_iv.size() == 0 && !m_cipher->valid_nonce_length(m_iv.size()))
+ throw Invalid_Argument("DLIES with " + m_cipher->name() + " requires an IV be set");
+ m_cipher->start(m_iv.bits_of());
+ m_cipher->finish(ciphertext);
+ }
+ else
+ {
+ xor_buf(ciphertext, secret_keys, cipher_key_len);
+ }
+
+ // calculate MAC
+ m_mac->set_key(secret_keys.data() + cipher_key_len, m_mac_keylen);
+ secure_vector<uint8_t> tag = m_mac->process(ciphertext);
+
+ // out = (ephemeral) public key + ciphertext + tag
+ secure_vector<uint8_t> out(m_own_pub_key.size() + ciphertext.size() + tag.size());
+ buffer_insert(out, 0, m_own_pub_key);
+ buffer_insert(out, 0 + m_own_pub_key.size(), ciphertext);
+ buffer_insert(out, 0 + m_own_pub_key.size() + ciphertext.size(), tag);
+
+ return unlock(out);
+ }
+
+/**
+* Return the max size, in bytes, of a message
+* We assume DLIES is only used for key transport and limit the maximum size
+* to 512 bits
+*/
+size_t DLIES_Encryptor::maximum_input_size() const
+ {
+ return 64;
+ }
+
+size_t DLIES_Encryptor::ciphertext_length(size_t ptext_len) const
+ {
+ return m_own_pub_key.size() + m_mac->output_length() + m_cipher->output_length(ptext_len);
+ }
+
+DLIES_Decryptor::DLIES_Decryptor(const DH_PrivateKey& own_priv_key,
+ RandomNumberGenerator& rng,
+ KDF* kdf,
+ Cipher_Mode* cipher,
+ size_t cipher_key_len,
+ MessageAuthenticationCode* mac,
+ size_t mac_key_length) :
+ m_pub_key_size(own_priv_key.public_value().size()),
+ m_ka(own_priv_key, rng, "Raw"),
+ m_kdf(kdf),
+ m_cipher(cipher),
+ m_cipher_key_len(cipher_key_len),
+ m_mac(mac),
+ m_mac_keylen(mac_key_length),
+ m_iv()
+ {
+ BOTAN_ASSERT_NONNULL(kdf);
+ BOTAN_ASSERT_NONNULL(mac);
+ }
+
+DLIES_Decryptor::DLIES_Decryptor(const DH_PrivateKey& own_priv_key,
+ RandomNumberGenerator& rng,
+ KDF* kdf,
+ MessageAuthenticationCode* mac,
+ size_t mac_key_length) :
+ DLIES_Decryptor(own_priv_key, rng, kdf, nullptr, 0, mac, mac_key_length)
+ {}
+
+size_t DLIES_Decryptor::plaintext_length(size_t ctext_len) const
+ {
+ if(ctext_len < m_pub_key_size + m_mac->output_length())
+ return 0; // will throw if attempted
+
+ return ctext_len - (m_pub_key_size + m_mac->output_length());
+ }
+
+secure_vector<uint8_t> DLIES_Decryptor::do_decrypt(uint8_t& valid_mask,
+ const uint8_t msg[], size_t length) const
+ {
+ if(length < m_pub_key_size + m_mac->output_length())
+ {
+ throw Decoding_Error("DLIES decryption: ciphertext is too short");
+ }
+
+ // calculate secret value
+ std::vector<uint8_t> other_pub_key(msg, msg + m_pub_key_size);
+ 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;
+
+ // derive secret key from secret value
+ const size_t required_key_length = cipher_key_len + m_mac_keylen;
+ secure_vector<uint8_t> secret_keys = m_kdf->derive_key(required_key_length, secret_value.bits_of());
+
+ if(secret_keys.size() != required_key_length)
+ {
+ throw Encoding_Error("DLIES: KDF did not provide sufficient output");
+ }
+
+ secure_vector<uint8_t> ciphertext(msg + m_pub_key_size, msg + m_pub_key_size + ciphertext_len);
+
+ // calculate MAC
+ m_mac->set_key(secret_keys.data() + cipher_key_len, m_mac_keylen);
+ secure_vector<uint8_t> calculated_tag = m_mac->process(ciphertext);
+
+ // calculated tag == received tag ?
+ secure_vector<uint8_t> tag(msg + m_pub_key_size + ciphertext_len,
+ msg + m_pub_key_size + ciphertext_len + m_mac->output_length());
+
+ valid_mask = ct_compare_u8(tag.data(), calculated_tag.data(), tag.size());
+
+ // decrypt
+ if(m_cipher)
+ {
+ if(valid_mask)
+ {
+ SymmetricKey dec_key(secret_keys.data(), cipher_key_len);
+ m_cipher->set_key(dec_key);
+
+ try
+ {
+ // the decryption can fail:
+ // e.g. Invalid_Authentication_Tag is thrown if GCM is used and the message does not have a valid tag
+
+ if(m_iv.size() == 0 && !m_cipher->valid_nonce_length(m_iv.size()))
+ throw Invalid_Argument("DLIES with " + m_cipher->name() + " requires an IV be set");
+ m_cipher->start(m_iv.bits_of());
+ m_cipher->finish(ciphertext);
+ }
+ catch(...)
+ {
+ valid_mask = 0;
+ }
+
+ }
+ else
+ {
+ return secure_vector<uint8_t>();
+ }
+ }
+ else
+ {
+ xor_buf(ciphertext, secret_keys.data(), cipher_key_len);
+ }
+
+ return ciphertext;
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/pubkey/dlies/dlies.h b/comm/third_party/botan/src/lib/pubkey/dlies/dlies.h
new file mode 100644
index 0000000000..640a8655eb
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/dlies/dlies.h
@@ -0,0 +1,163 @@
+/*
+* DLIES
+* (C) 1999-2007 Jack Lloyd
+* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_DLIES_H_
+#define BOTAN_DLIES_H_
+
+#include <botan/pubkey.h>
+#include <botan/mac.h>
+#include <botan/kdf.h>
+#include <botan/dh.h>
+#include <botan/cipher_mode.h>
+
+namespace Botan {
+
+/**
+* DLIES Encryption
+*/
+class BOTAN_PUBLIC_API(2,0) DLIES_Encryptor final : public PK_Encryptor
+ {
+ public:
+ /**
+ * Stream mode: use KDF to provide a stream of bytes to xor with the message
+ *
+ * @param own_priv_key own (ephemeral) DH private key
+ * @param rng the RNG to use
+ * @param kdf the KDF that should be used
+ * @param mac the MAC function that should be used
+ * @param mac_key_len key length of the MAC function. Default = 20 bytes
+ *
+ * output = (ephemeral) public key + ciphertext + tag
+ */
+ DLIES_Encryptor(const DH_PrivateKey& own_priv_key,
+ RandomNumberGenerator& rng,
+ KDF* kdf,
+ MessageAuthenticationCode* mac,
+ size_t mac_key_len = 20);
+
+ /**
+ * Block cipher mode
+ *
+ * @param own_priv_key own (ephemeral) DH private key
+ * @param rng the RNG to use
+ * @param kdf the KDF that should be used
+ * @param cipher the block cipher that should be used
+ * @param cipher_key_len the key length of the block cipher
+ * @param mac the MAC function that should be used
+ * @param mac_key_len key length of the MAC function. Default = 20 bytes
+ *
+ * output = (ephemeral) public key + ciphertext + tag
+ */
+ DLIES_Encryptor(const DH_PrivateKey& own_priv_key,
+ RandomNumberGenerator& rng,
+ KDF* kdf,
+ Cipher_Mode* cipher,
+ size_t cipher_key_len,
+ MessageAuthenticationCode* mac,
+ size_t mac_key_len = 20);
+
+ // Set the other parties public key
+ inline void set_other_key(const std::vector<uint8_t>& other_pub_key)
+ {
+ m_other_pub_key = other_pub_key;
+ }
+
+ /// Set the initialization vector for the data encryption method
+ inline void set_initialization_vector(const InitializationVector& iv)
+ {
+ m_iv = iv;
+ }
+
+ private:
+ std::vector<uint8_t> enc(const uint8_t[], size_t,
+ RandomNumberGenerator&) const override;
+
+ size_t maximum_input_size() const override;
+
+ size_t ciphertext_length(size_t ptext_len) const override;
+
+ std::vector<uint8_t> m_other_pub_key;
+ std::vector<uint8_t> m_own_pub_key;
+ PK_Key_Agreement m_ka;
+ std::unique_ptr<KDF> m_kdf;
+ std::unique_ptr<Cipher_Mode> m_cipher;
+ const size_t m_cipher_key_len;
+ std::unique_ptr<MessageAuthenticationCode> m_mac;
+ const size_t m_mac_keylen;
+ InitializationVector m_iv;
+ };
+
+/**
+* DLIES Decryption
+*/
+class BOTAN_PUBLIC_API(2,0) DLIES_Decryptor final : public PK_Decryptor
+ {
+ public:
+ /**
+ * Stream mode: use KDF to provide a stream of bytes to xor with the message
+ *
+ * @param own_priv_key own (ephemeral) DH private key
+ * @param rng the RNG to use
+ * @param kdf the KDF that should be used
+ * @param mac the MAC function that should be used
+ * @param mac_key_len key length of the MAC function. Default = 20 bytes
+ *
+ * input = (ephemeral) public key + ciphertext + tag
+ */
+ DLIES_Decryptor(const DH_PrivateKey& own_priv_key,
+ RandomNumberGenerator& rng,
+ KDF* kdf,
+ MessageAuthenticationCode* mac,
+ size_t mac_key_len = 20);
+
+ /**
+ * Block cipher mode
+ *
+ * @param own_priv_key own (ephemeral) DH private key
+ * @param rng the RNG to use
+ * @param kdf the KDF that should be used
+ * @param cipher the block cipher that should be used
+ * @param cipher_key_len the key length of the block cipher
+ * @param mac the MAC function that should be used
+ * @param mac_key_len key length of the MAC function. Default = 20 bytes
+ *
+ * input = (ephemeral) public key + ciphertext + tag
+ */
+ DLIES_Decryptor(const DH_PrivateKey& own_priv_key,
+ RandomNumberGenerator& rng,
+ KDF* kdf,
+ Cipher_Mode* cipher,
+ size_t cipher_key_len,
+ MessageAuthenticationCode* mac,
+ size_t mac_key_len = 20);
+
+ /// Set the initialization vector for the data decryption method
+ inline void set_initialization_vector(const InitializationVector& iv)
+ {
+ m_iv = iv;
+ }
+
+ private:
+ secure_vector<uint8_t> do_decrypt(uint8_t& valid_mask,
+ const uint8_t in[], size_t in_len) const override;
+
+ size_t plaintext_length(size_t ctext_len) const override;
+
+ const size_t m_pub_key_size;
+ PK_Key_Agreement m_ka;
+ std::unique_ptr<KDF> m_kdf;
+ std::unique_ptr<Cipher_Mode> m_cipher;
+ const size_t m_cipher_key_len;
+ std::unique_ptr<MessageAuthenticationCode> m_mac;
+ const size_t m_mac_keylen;
+ InitializationVector m_iv;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/pubkey/dlies/info.txt b/comm/third_party/botan/src/lib/pubkey/dlies/info.txt
new file mode 100644
index 0000000000..80c0466f46
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/dlies/info.txt
@@ -0,0 +1,10 @@
+<defines>
+DLIES -> 20160713
+</defines>
+
+<requires>
+dh
+kdf
+mac
+modes
+</requires>
diff --git a/comm/third_party/botan/src/lib/pubkey/dsa/dsa.cpp b/comm/third_party/botan/src/lib/pubkey/dsa/dsa.cpp
new file mode 100644
index 0000000000..4da347c5e3
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/dsa/dsa.cpp
@@ -0,0 +1,230 @@
+/*
+* DSA
+* (C) 1999-2010,2014,2016 Jack Lloyd
+* (C) 2016 René Korthaus
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/dsa.h>
+#include <botan/keypair.h>
+#include <botan/reducer.h>
+#include <botan/rng.h>
+#include <botan/divide.h>
+#include <botan/internal/pk_ops_impl.h>
+
+#if defined(BOTAN_HAS_RFC6979_GENERATOR)
+ #include <botan/emsa.h>
+ #include <botan/rfc6979.h>
+#endif
+
+namespace Botan {
+
+/*
+* DSA_PublicKey Constructor
+*/
+DSA_PublicKey::DSA_PublicKey(const DL_Group& grp, const BigInt& y1)
+ {
+ m_group = grp;
+ m_y = y1;
+ }
+
+/*
+* Create a DSA private key
+*/
+DSA_PrivateKey::DSA_PrivateKey(RandomNumberGenerator& rng,
+ const DL_Group& grp,
+ const BigInt& x_arg)
+ {
+ m_group = grp;
+
+ if(x_arg == 0)
+ m_x = BigInt::random_integer(rng, 2, group_q());
+ else
+ m_x = x_arg;
+
+ m_y = m_group.power_g_p(m_x, m_group.q_bits());
+ }
+
+DSA_PrivateKey::DSA_PrivateKey(const AlgorithmIdentifier& alg_id,
+ const secure_vector<uint8_t>& key_bits) :
+ DL_Scheme_PrivateKey(alg_id, key_bits, DL_Group::ANSI_X9_57)
+ {
+ m_y = m_group.power_g_p(m_x, m_group.q_bits());
+ }
+
+/*
+* Check Private DSA Parameters
+*/
+bool DSA_PrivateKey::check_key(RandomNumberGenerator& rng, bool strong) const
+ {
+ if(!DL_Scheme_PrivateKey::check_key(rng, strong) || m_x >= group_q())
+ return false;
+
+ if(!strong)
+ return true;
+
+ return KeyPair::signature_consistency_check(rng, *this, "EMSA1(SHA-256)");
+ }
+
+namespace {
+
+/**
+* Object that can create a DSA signature
+*/
+class DSA_Signature_Operation final : public PK_Ops::Signature_with_EMSA
+ {
+ public:
+ DSA_Signature_Operation(const DSA_PrivateKey& dsa,
+ const std::string& emsa,
+ RandomNumberGenerator& rng) :
+ PK_Ops::Signature_with_EMSA(emsa),
+ m_group(dsa.get_group()),
+ m_x(dsa.get_x())
+ {
+#if defined(BOTAN_HAS_RFC6979_GENERATOR)
+ m_rfc6979_hash = hash_for_emsa(emsa);
+#endif
+
+ m_b = BigInt::random_integer(rng, 2, dsa.group_q());
+ m_b_inv = m_group.inverse_mod_q(m_b);
+ }
+
+ size_t signature_length() const override { return 2*m_group.q_bytes(); }
+ size_t max_input_bits() const override { return m_group.q_bits(); }
+
+ secure_vector<uint8_t> raw_sign(const uint8_t msg[], size_t msg_len,
+ RandomNumberGenerator& rng) override;
+ private:
+ const DL_Group m_group;
+ const BigInt& m_x;
+#if defined(BOTAN_HAS_RFC6979_GENERATOR)
+ std::string m_rfc6979_hash;
+#endif
+
+ BigInt m_b, m_b_inv;
+ };
+
+secure_vector<uint8_t>
+DSA_Signature_Operation::raw_sign(const uint8_t msg[], size_t msg_len,
+ RandomNumberGenerator& rng)
+ {
+ const BigInt& q = m_group.get_q();
+
+ BigInt m(msg, msg_len, m_group.q_bits());
+
+ while(m >= q)
+ m -= q;
+
+#if defined(BOTAN_HAS_RFC6979_GENERATOR)
+ BOTAN_UNUSED(rng);
+ const BigInt k = generate_rfc6979_nonce(m_x, q, m, m_rfc6979_hash);
+#else
+ const BigInt k = BigInt::random_integer(rng, 1, q);
+#endif
+
+ const BigInt k_inv = m_group.inverse_mod_q(k);
+
+ /*
+ * It may not be strictly necessary for the reduction (g^k mod p) mod q to be
+ * const time, since r is published as part of the signature, and deriving
+ * anything useful about k from g^k mod p would seem to require computing a
+ * discrete logarithm.
+ *
+ * However it only increases the cost of signatures by about 7-10%, and DSA is
+ * only for legacy use anyway so we don't care about the performance so much.
+ */
+ const BigInt r = ct_modulo(m_group.power_g_p(k, m_group.q_bits()), m_group.get_q());
+
+ /*
+ * Blind the input message and compute x*r+m as (x*r*b + m*b)/b
+ */
+ m_b = m_group.square_mod_q(m_b);
+ m_b_inv = m_group.square_mod_q(m_b_inv);
+
+ m = m_group.multiply_mod_q(m_b, m);
+ const BigInt xr = m_group.multiply_mod_q(m_b, m_x, r);
+
+ const BigInt s = m_group.multiply_mod_q(m_b_inv, k_inv, m_group.mod_q(xr+m));
+
+ // With overwhelming probability, a bug rather than actual zero r/s
+ if(r.is_zero() || s.is_zero())
+ throw Internal_Error("Computed zero r/s during DSA signature");
+
+ return BigInt::encode_fixed_length_int_pair(r, s, q.bytes());
+ }
+
+/**
+* Object that can verify a DSA signature
+*/
+class DSA_Verification_Operation final : public PK_Ops::Verification_with_EMSA
+ {
+ public:
+ DSA_Verification_Operation(const DSA_PublicKey& dsa,
+ const std::string& emsa) :
+ PK_Ops::Verification_with_EMSA(emsa),
+ m_group(dsa.get_group()),
+ m_y(dsa.get_y())
+ {
+ }
+
+ size_t max_input_bits() const override { return m_group.q_bits(); }
+
+ bool with_recovery() const override { return false; }
+
+ bool verify(const uint8_t msg[], size_t msg_len,
+ const uint8_t sig[], size_t sig_len) override;
+ private:
+ const DL_Group m_group;
+ const BigInt& m_y;
+ };
+
+bool DSA_Verification_Operation::verify(const uint8_t msg[], size_t msg_len,
+ const uint8_t sig[], size_t sig_len)
+ {
+ const BigInt& q = m_group.get_q();
+ const size_t q_bytes = q.bytes();
+
+ if(sig_len != 2*q_bytes || msg_len > q_bytes)
+ return false;
+
+ BigInt r(sig, q_bytes);
+ BigInt s(sig + q_bytes, q_bytes);
+ BigInt i(msg, msg_len, q.bits());
+
+ if(r <= 0 || r >= q || s <= 0 || s >= q)
+ return false;
+
+ s = inverse_mod(s, q);
+
+ const BigInt sr = m_group.multiply_mod_q(s, r);
+ const BigInt si = m_group.multiply_mod_q(s, i);
+
+ s = m_group.multi_exponentiate(si, m_y, sr);
+
+ // s is too big for Barrett, and verification doesn't need to be const-time
+ return (s % m_group.get_q() == r);
+ }
+
+}
+
+std::unique_ptr<PK_Ops::Verification>
+DSA_PublicKey::create_verification_op(const std::string& params,
+ const std::string& provider) const
+ {
+ if(provider == "base" || provider.empty())
+ return std::unique_ptr<PK_Ops::Verification>(new DSA_Verification_Operation(*this, params));
+ throw Provider_Not_Found(algo_name(), provider);
+ }
+
+std::unique_ptr<PK_Ops::Signature>
+DSA_PrivateKey::create_signature_op(RandomNumberGenerator& rng,
+ const std::string& params,
+ const std::string& provider) const
+ {
+ if(provider == "base" || provider.empty())
+ return std::unique_ptr<PK_Ops::Signature>(new DSA_Signature_Operation(*this, params, rng));
+ throw Provider_Not_Found(algo_name(), provider);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/pubkey/dsa/dsa.h b/comm/third_party/botan/src/lib/pubkey/dsa/dsa.h
new file mode 100644
index 0000000000..b219a1cf37
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/dsa/dsa.h
@@ -0,0 +1,87 @@
+/*
+* DSA
+* (C) 1999-2010 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_DSA_H_
+#define BOTAN_DSA_H_
+
+#include <botan/dl_algo.h>
+
+namespace Botan {
+
+/**
+* DSA Public Key
+*/
+class BOTAN_PUBLIC_API(2,0) DSA_PublicKey : public virtual DL_Scheme_PublicKey
+ {
+ public:
+ std::string algo_name() const override { return "DSA"; }
+
+ DL_Group::Format group_format() const override { return DL_Group::ANSI_X9_57; }
+ size_t message_parts() const override { return 2; }
+ size_t message_part_size() const override { return group_q().bytes(); }
+
+ /**
+ * Load a public key.
+ * @param alg_id the X.509 algorithm identifier
+ * @param key_bits DER encoded public key bits
+ */
+ DSA_PublicKey(const AlgorithmIdentifier& alg_id,
+ const std::vector<uint8_t>& key_bits) :
+ DL_Scheme_PublicKey(alg_id, key_bits, DL_Group::ANSI_X9_57)
+ {
+ }
+
+ /**
+ * Create a public key.
+ * @param group the underlying DL group
+ * @param y the public value y = g^x mod p
+ */
+ DSA_PublicKey(const DL_Group& group, const BigInt& y);
+
+ std::unique_ptr<PK_Ops::Verification>
+ create_verification_op(const std::string& params,
+ const std::string& provider) const override;
+ protected:
+ DSA_PublicKey() = default;
+ };
+
+/**
+* DSA Private Key
+*/
+class BOTAN_PUBLIC_API(2,0) DSA_PrivateKey final : public DSA_PublicKey,
+ public virtual DL_Scheme_PrivateKey
+ {
+ public:
+ /**
+ * Load a private key.
+ * @param alg_id the X.509 algorithm identifier
+ * @param key_bits DER encoded key bits in ANSI X9.57 format
+ */
+ DSA_PrivateKey(const AlgorithmIdentifier& alg_id,
+ const secure_vector<uint8_t>& key_bits);
+
+ /**
+ * Create a private key.
+ * @param rng the RNG to use
+ * @param group the underlying DL group
+ * @param private_key the private key (if zero, a new random key is generated)
+ */
+ DSA_PrivateKey(RandomNumberGenerator& rng,
+ const DL_Group& group,
+ const BigInt& private_key = 0);
+
+ bool check_key(RandomNumberGenerator& rng, bool strong) const override;
+
+ std::unique_ptr<PK_Ops::Signature>
+ create_signature_op(RandomNumberGenerator& rng,
+ const std::string& params,
+ const std::string& provider) const override;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/pubkey/dsa/info.txt b/comm/third_party/botan/src/lib/pubkey/dsa/info.txt
new file mode 100644
index 0000000000..a9f288edea
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/dsa/info.txt
@@ -0,0 +1,12 @@
+<defines>
+DSA -> 20131128
+</defines>
+
+<requires>
+dl_algo
+dl_group
+keypair
+numbertheory
+emsa1
+sha2_32
+</requires>
diff --git a/comm/third_party/botan/src/lib/pubkey/ec_group/curve_gfp.cpp b/comm/third_party/botan/src/lib/pubkey/ec_group/curve_gfp.cpp
new file mode 100644
index 0000000000..9957bb0853
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/ec_group/curve_gfp.cpp
@@ -0,0 +1,568 @@
+/*
+* Elliptic curves over GF(p) Montgomery Representation
+* (C) 2014,2015,2018 Jack Lloyd
+* 2016 Matthias Gierlings
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/curve_gfp.h>
+#include <botan/curve_nistp.h>
+#include <botan/numthry.h>
+#include <botan/reducer.h>
+#include <botan/internal/mp_core.h>
+#include <botan/internal/mp_asmi.h>
+
+namespace Botan {
+
+namespace {
+
+class CurveGFp_Montgomery final : public CurveGFp_Repr
+ {
+ public:
+ CurveGFp_Montgomery(const BigInt& p, const BigInt& a, const BigInt& b) :
+ m_p(p), m_a(a), m_b(b),
+ m_p_words(m_p.sig_words()),
+ m_p_dash(monty_inverse(m_p.word_at(0)))
+ {
+ Modular_Reducer mod_p(m_p);
+
+ m_r.set_bit(m_p_words * BOTAN_MP_WORD_BITS);
+ m_r = mod_p.reduce(m_r);
+
+ m_r2 = mod_p.square(m_r);
+ m_r3 = mod_p.multiply(m_r, m_r2);
+ m_a_r = mod_p.multiply(m_r, m_a);
+ m_b_r = mod_p.multiply(m_r, m_b);
+
+ m_a_is_zero = m_a.is_zero();
+ m_a_is_minus_3 = (m_a + 3 == m_p);
+ }
+
+ bool a_is_zero() const override { return m_a_is_zero; }
+ bool a_is_minus_3() const override { return m_a_is_minus_3; }
+
+ const BigInt& get_a() const override { return m_a; }
+
+ const BigInt& get_b() const override { return m_b; }
+
+ const BigInt& get_p() const override { return m_p; }
+
+ const BigInt& get_a_rep() const override { return m_a_r; }
+
+ const BigInt& get_b_rep() const override { return m_b_r; }
+
+ const BigInt& get_1_rep() const override { return m_r; }
+
+ bool is_one(const BigInt& x) const override { return x == m_r; }
+
+ size_t get_p_words() const override { return m_p_words; }
+
+ size_t get_ws_size() const override { return 2*m_p_words + 4; }
+
+ BigInt invert_element(const BigInt& x, secure_vector<word>& ws) const override;
+
+ void to_curve_rep(BigInt& x, secure_vector<word>& ws) const override;
+
+ void from_curve_rep(BigInt& x, secure_vector<word>& ws) const override;
+
+ void curve_mul_words(BigInt& z,
+ const word x_words[],
+ const size_t x_size,
+ const BigInt& y,
+ secure_vector<word>& ws) const override;
+
+ void curve_sqr_words(BigInt& z,
+ const word x_words[],
+ size_t x_size,
+ secure_vector<word>& ws) const override;
+
+ private:
+ BigInt m_p;
+ BigInt m_a, m_b;
+ BigInt m_a_r, m_b_r;
+ size_t m_p_words; // cache of m_p.sig_words()
+
+ // Montgomery parameters
+ BigInt m_r, m_r2, m_r3;
+ word m_p_dash;
+
+ bool m_a_is_zero;
+ bool m_a_is_minus_3;
+ };
+
+BigInt CurveGFp_Montgomery::invert_element(const BigInt& x, secure_vector<word>& ws) const
+ {
+ // Should we use Montgomery inverse instead?
+ const BigInt inv = inverse_mod(x, m_p);
+ BigInt res;
+ curve_mul(res, inv, m_r3, ws);
+ return res;
+ }
+
+void CurveGFp_Montgomery::to_curve_rep(BigInt& x, secure_vector<word>& ws) const
+ {
+ const BigInt tx = x;
+ curve_mul(x, tx, m_r2, ws);
+ }
+
+void CurveGFp_Montgomery::from_curve_rep(BigInt& z, secure_vector<word>& ws) const
+ {
+ if(ws.size() < get_ws_size())
+ ws.resize(get_ws_size());
+
+ const size_t output_size = 2*m_p_words + 2;
+ if(z.size() < output_size)
+ z.grow_to(output_size);
+
+ bigint_monty_redc(z.mutable_data(),
+ m_p.data(), m_p_words, m_p_dash,
+ ws.data(), ws.size());
+ }
+
+void CurveGFp_Montgomery::curve_mul_words(BigInt& z,
+ const word x_w[],
+ size_t x_size,
+ const BigInt& y,
+ secure_vector<word>& ws) const
+ {
+ BOTAN_DEBUG_ASSERT(y.sig_words() <= m_p_words);
+
+ if(ws.size() < get_ws_size())
+ ws.resize(get_ws_size());
+
+ const size_t output_size = 2*m_p_words + 2;
+ if(z.size() < output_size)
+ z.grow_to(output_size);
+
+ bigint_mul(z.mutable_data(), z.size(),
+ x_w, x_size, std::min(m_p_words, x_size),
+ y.data(), y.size(), std::min(m_p_words, 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 CurveGFp_Montgomery::curve_sqr_words(BigInt& z,
+ const word x[],
+ size_t x_size,
+ secure_vector<word>& ws) const
+ {
+ if(ws.size() < get_ws_size())
+ ws.resize(get_ws_size());
+
+ const size_t output_size = 2*m_p_words + 2;
+ if(z.size() < output_size)
+ z.grow_to(output_size);
+
+ bigint_sqr(z.mutable_data(), z.size(),
+ x, x_size, std::min(m_p_words, 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());
+ }
+
+class CurveGFp_NIST : public CurveGFp_Repr
+ {
+ public:
+ CurveGFp_NIST(size_t p_bits, const BigInt& a, const BigInt& b) :
+ m_1(1), m_a(a), m_b(b), m_p_words((p_bits + BOTAN_MP_WORD_BITS - 1) / BOTAN_MP_WORD_BITS)
+ {
+ // All Solinas prime curves are assumed a == -3
+ }
+
+ bool a_is_zero() const override { return false; }
+ bool a_is_minus_3() const override { return true; }
+
+ const BigInt& get_a() const override { return m_a; }
+
+ const BigInt& get_b() const override { return m_b; }
+
+ const BigInt& get_1_rep() const override { return m_1; }
+
+ size_t get_p_words() const override { return m_p_words; }
+
+ size_t get_ws_size() const override { return 2*m_p_words + 4; }
+
+ const BigInt& get_a_rep() const override { return m_a; }
+
+ const BigInt& get_b_rep() const override { return m_b; }
+
+ bool is_one(const BigInt& x) const override { return x == 1; }
+
+ void to_curve_rep(BigInt& x, secure_vector<word>& ws) const override
+ { redc_mod_p(x, ws); }
+
+ void from_curve_rep(BigInt& x, secure_vector<word>& ws) const override
+ { redc_mod_p(x, ws); }
+
+ virtual void redc_mod_p(BigInt& z, secure_vector<word>& ws) const = 0;
+
+ BigInt invert_element(const BigInt& x, secure_vector<word>& ws) const override;
+
+ void curve_mul_words(BigInt& z,
+ const word x_words[],
+ const size_t x_size,
+ const BigInt& y,
+ secure_vector<word>& ws) const override;
+
+ void curve_mul_tmp(BigInt& x, const BigInt& y, BigInt& tmp, secure_vector<word>& ws) const
+ {
+ curve_mul(tmp, x, y, ws);
+ x.swap(tmp);
+ }
+
+ void curve_sqr_tmp(BigInt& x, BigInt& tmp, secure_vector<word>& ws) const
+ {
+ curve_sqr(tmp, x, ws);
+ x.swap(tmp);
+ }
+
+ void curve_sqr_words(BigInt& z,
+ const word x_words[],
+ size_t x_size,
+ secure_vector<word>& ws) const override;
+ private:
+ // Curve parameters
+ BigInt m_1;
+ BigInt m_a, m_b;
+ size_t m_p_words; // cache of m_p.sig_words()
+ };
+
+BigInt CurveGFp_NIST::invert_element(const BigInt& x, secure_vector<word>& ws) const
+ {
+ BOTAN_UNUSED(ws);
+ return inverse_mod(x, get_p());
+ }
+
+void CurveGFp_NIST::curve_mul_words(BigInt& z,
+ const word x_w[],
+ size_t x_size,
+ const BigInt& y,
+ secure_vector<word>& ws) const
+ {
+ BOTAN_DEBUG_ASSERT(y.sig_words() <= m_p_words);
+
+ if(ws.size() < get_ws_size())
+ ws.resize(get_ws_size());
+
+ const size_t output_size = 2*m_p_words + 2;
+ if(z.size() < output_size)
+ z.grow_to(output_size);
+
+ bigint_mul(z.mutable_data(), z.size(),
+ x_w, x_size, std::min(m_p_words, x_size),
+ y.data(), y.size(), std::min(m_p_words, y.size()),
+ ws.data(), ws.size());
+
+ this->redc_mod_p(z, ws);
+ }
+
+void CurveGFp_NIST::curve_sqr_words(BigInt& z, const word x[], size_t x_size,
+ secure_vector<word>& ws) const
+ {
+ if(ws.size() < get_ws_size())
+ ws.resize(get_ws_size());
+
+ const size_t output_size = 2*m_p_words + 2;
+ if(z.size() < output_size)
+ z.grow_to(output_size);
+
+ bigint_sqr(z.mutable_data(), output_size,
+ x, x_size, std::min(m_p_words, x_size),
+ ws.data(), ws.size());
+
+ this->redc_mod_p(z, ws);
+ }
+
+/**
+* The NIST P-192 curve
+*/
+class CurveGFp_P192 final : public CurveGFp_NIST
+ {
+ public:
+ CurveGFp_P192(const BigInt& a, const BigInt& b) : CurveGFp_NIST(192, a, b) {}
+ const BigInt& get_p() const override { return prime_p192(); }
+ private:
+ void redc_mod_p(BigInt& x, secure_vector<word>& ws) const override { redc_p192(x, ws); }
+ };
+
+/**
+* The NIST P-224 curve
+*/
+class CurveGFp_P224 final : public CurveGFp_NIST
+ {
+ public:
+ CurveGFp_P224(const BigInt& a, const BigInt& b) : CurveGFp_NIST(224, a, b) {}
+ const BigInt& get_p() const override { return prime_p224(); }
+ private:
+ void redc_mod_p(BigInt& x, secure_vector<word>& ws) const override { redc_p224(x, ws); }
+ };
+
+/**
+* The NIST P-256 curve
+*/
+class CurveGFp_P256 final : public CurveGFp_NIST
+ {
+ public:
+ CurveGFp_P256(const BigInt& a, const BigInt& b) : CurveGFp_NIST(256, a, b) {}
+ const BigInt& get_p() const override { return prime_p256(); }
+ private:
+ void redc_mod_p(BigInt& x, secure_vector<word>& ws) const override { redc_p256(x, ws); }
+ BigInt invert_element(const BigInt& x, secure_vector<word>& ws) const override;
+ };
+
+BigInt CurveGFp_P256::invert_element(const BigInt& x, secure_vector<word>& ws) const
+ {
+ BigInt r, p2, p4, p8, p16, p32, tmp;
+
+ curve_sqr(r, x, ws);
+
+ curve_mul(p2, r, x, ws);
+ curve_sqr(r, p2, ws);
+ curve_sqr_tmp(r, tmp, ws);
+
+ curve_mul(p4, r, p2, ws);
+
+ curve_sqr(r, p4, ws);
+ for(size_t i = 0; i != 3; ++i)
+ curve_sqr_tmp(r, tmp, ws);
+ curve_mul(p8, r, p4, ws);
+
+ curve_sqr(r, p8, ws);
+ for(size_t i = 0; i != 7; ++i)
+ curve_sqr_tmp(r, tmp, ws);
+ curve_mul(p16, r, p8, ws);
+
+ curve_sqr(r, p16, ws);
+ for(size_t i = 0; i != 15; ++i)
+ curve_sqr_tmp(r, tmp, ws);
+ curve_mul(p32, r, p16, ws);
+
+ curve_sqr(r, p32, ws);
+ for(size_t i = 0; i != 31; ++i)
+ curve_sqr_tmp(r, tmp, ws);
+ curve_mul_tmp(r, x, tmp, ws);
+
+ for(size_t i = 0; i != 32*4; ++i)
+ curve_sqr_tmp(r, tmp, ws);
+ curve_mul_tmp(r, p32, tmp, ws);
+
+ for(size_t i = 0; i != 32; ++i)
+ curve_sqr_tmp(r, tmp, ws);
+ curve_mul_tmp(r, p32, tmp, ws);
+
+ for(size_t i = 0; i != 16; ++i)
+ curve_sqr_tmp(r, tmp, ws);
+ curve_mul_tmp(r, p16, tmp, ws);
+ for(size_t i = 0; i != 8; ++i)
+ curve_sqr_tmp(r, tmp, ws);
+ curve_mul_tmp(r, p8, tmp, ws);
+
+ for(size_t i = 0; i != 4; ++i)
+ curve_sqr_tmp(r, tmp, ws);
+ curve_mul_tmp(r, p4, tmp, ws);
+
+ for(size_t i = 0; i != 2; ++i)
+ curve_sqr_tmp(r, tmp, ws);
+ curve_mul_tmp(r, p2, tmp, ws);
+
+ for(size_t i = 0; i != 2; ++i)
+ curve_sqr_tmp(r, tmp, ws);
+ curve_mul_tmp(r, x, tmp, ws);
+
+ return r;
+ }
+
+/**
+* The NIST P-384 curve
+*/
+class CurveGFp_P384 final : public CurveGFp_NIST
+ {
+ public:
+ CurveGFp_P384(const BigInt& a, const BigInt& b) : CurveGFp_NIST(384, a, b) {}
+ const BigInt& get_p() const override { return prime_p384(); }
+ private:
+ void redc_mod_p(BigInt& x, secure_vector<word>& ws) const override { redc_p384(x, ws); }
+ BigInt invert_element(const BigInt& x, secure_vector<word>& ws) const override;
+ };
+
+BigInt CurveGFp_P384::invert_element(const BigInt& x, secure_vector<word>& ws) const
+ {
+ // From https://briansmith.org/ecc-inversion-addition-chains-01
+
+ BigInt r, x2, x3, x15, x30, tmp, rl;
+
+ r = x;
+ curve_sqr_tmp(r, tmp, ws);
+ curve_mul_tmp(r, x, tmp, ws);
+ x2 = r;
+
+ curve_sqr_tmp(r, tmp, ws);
+ curve_mul_tmp(r, x, tmp, ws);
+
+ x3 = r;
+
+ for(size_t i = 0; i != 3; ++i)
+ curve_sqr_tmp(r, tmp, ws);
+ curve_mul_tmp(r, x3, tmp, ws);
+
+ rl = r;
+ for(size_t i = 0; i != 6; ++i)
+ curve_sqr_tmp(r, tmp, ws);
+ curve_mul_tmp(r, rl, tmp, ws);
+
+ for(size_t i = 0; i != 3; ++i)
+ curve_sqr_tmp(r, tmp, ws);
+ curve_mul_tmp(r, x3, tmp, ws);
+
+ x15 = r;
+ for(size_t i = 0; i != 15; ++i)
+ curve_sqr_tmp(r, tmp, ws);
+ curve_mul_tmp(r, x15, tmp, ws);
+
+ x30 = r;
+ for(size_t i = 0; i != 30; ++i)
+ curve_sqr_tmp(r, tmp, ws);
+ curve_mul_tmp(r, x30, tmp, ws);
+
+ rl = r;
+ for(size_t i = 0; i != 60; ++i)
+ curve_sqr_tmp(r, tmp, ws);
+ curve_mul_tmp(r, rl, tmp, ws);
+
+ rl = r;
+ for(size_t i = 0; i != 120; ++i)
+ curve_sqr_tmp(r, tmp, ws);
+ curve_mul_tmp(r, rl, tmp, ws);
+
+ for(size_t i = 0; i != 15; ++i)
+ curve_sqr_tmp(r, tmp, ws);
+ curve_mul_tmp(r, x15, tmp, ws);
+
+ for(size_t i = 0; i != 31; ++i)
+ curve_sqr_tmp(r, tmp, ws);
+ curve_mul_tmp(r, x30, tmp, ws);
+
+ for(size_t i = 0; i != 2; ++i)
+ curve_sqr_tmp(r, tmp, ws);
+ curve_mul_tmp(r, x2, tmp, ws);
+
+ for(size_t i = 0; i != 94; ++i)
+ curve_sqr_tmp(r, tmp, ws);
+ curve_mul_tmp(r, x30, tmp, ws);
+
+ for(size_t i = 0; i != 2; ++i)
+ curve_sqr_tmp(r, tmp, ws);
+
+ curve_mul_tmp(r, x, tmp, ws);
+
+ return r;
+ }
+
+/**
+* The NIST P-521 curve
+*/
+class CurveGFp_P521 final : public CurveGFp_NIST
+ {
+ public:
+ CurveGFp_P521(const BigInt& a, const BigInt& b) : CurveGFp_NIST(521, a, b) {}
+ const BigInt& get_p() const override { return prime_p521(); }
+ private:
+ void redc_mod_p(BigInt& x, secure_vector<word>& ws) const override { redc_p521(x, ws); }
+ BigInt invert_element(const BigInt& x, secure_vector<word>& ws) const override;
+ };
+
+BigInt CurveGFp_P521::invert_element(const BigInt& x, secure_vector<word>& ws) const
+ {
+ // Addition chain from https://eprint.iacr.org/2014/852.pdf section
+
+ BigInt r;
+ BigInt rl;
+ BigInt a7;
+ BigInt tmp;
+
+ curve_sqr(r, x, ws);
+ curve_mul_tmp(r, x, tmp, ws);
+
+ curve_sqr_tmp(r, tmp, ws);
+ curve_mul_tmp(r, x, tmp, ws);
+
+ rl = r;
+
+ for(size_t i = 0; i != 3; ++i)
+ curve_sqr_tmp(r, tmp, ws);
+ curve_mul_tmp(r, rl, tmp, ws);
+
+ curve_sqr_tmp(r, tmp, ws);
+ curve_mul_tmp(r, x, tmp, ws);
+ a7 = r; // need this value later
+
+ curve_sqr_tmp(r, tmp, ws);
+ curve_mul_tmp(r, x, tmp, ws);
+
+ rl = r;
+ for(size_t i = 0; i != 8; ++i)
+ curve_sqr_tmp(r, tmp, ws);
+ curve_mul_tmp(r, rl, tmp, ws);
+
+ rl = r;
+ for(size_t i = 0; i != 16; ++i)
+ curve_sqr_tmp(r, tmp, ws);
+ curve_mul_tmp(r, rl, tmp, ws);
+
+ rl = r;
+ for(size_t i = 0; i != 32; ++i)
+ curve_sqr_tmp(r, tmp, ws);
+ curve_mul_tmp(r, rl, tmp, ws);
+
+ rl = r;
+ for(size_t i = 0; i != 64; ++i)
+ curve_sqr_tmp(r, tmp, ws);
+ curve_mul_tmp(r, rl, tmp, ws);
+
+ rl = r;
+ for(size_t i = 0; i != 128; ++i)
+ curve_sqr_tmp(r, tmp, ws);
+ curve_mul_tmp(r, rl, tmp, ws);
+
+ rl = r;
+ for(size_t i = 0; i != 256; ++i)
+ curve_sqr_tmp(r, tmp, ws);
+ curve_mul_tmp(r, rl, tmp, ws);
+
+ for(size_t i = 0; i != 7; ++i)
+ curve_sqr_tmp(r, tmp, ws);
+ curve_mul_tmp(r, a7, tmp, ws);
+
+ for(size_t i = 0; i != 2; ++i)
+ curve_sqr_tmp(r, tmp, ws);
+ curve_mul_tmp(r, x, tmp, ws);
+
+ return r;
+ }
+
+}
+
+std::shared_ptr<CurveGFp_Repr>
+CurveGFp::choose_repr(const BigInt& p, const BigInt& a, const BigInt& b)
+ {
+ if(p == prime_p192())
+ return std::shared_ptr<CurveGFp_Repr>(new CurveGFp_P192(a, b));
+ if(p == prime_p224())
+ return std::shared_ptr<CurveGFp_Repr>(new CurveGFp_P224(a, b));
+ if(p == prime_p256())
+ return std::shared_ptr<CurveGFp_Repr>(new CurveGFp_P256(a, b));
+ if(p == prime_p384())
+ return std::shared_ptr<CurveGFp_Repr>(new CurveGFp_P384(a, b));
+ if(p == prime_p521())
+ return std::shared_ptr<CurveGFp_Repr>(new CurveGFp_P521(a, b));
+
+ return std::shared_ptr<CurveGFp_Repr>(new CurveGFp_Montgomery(p, a, b));
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/pubkey/ec_group/curve_gfp.h b/comm/third_party/botan/src/lib/pubkey/ec_group/curve_gfp.h
new file mode 100644
index 0000000000..77c04ebf36
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/ec_group/curve_gfp.h
@@ -0,0 +1,265 @@
+/*
+* Elliptic curves over GF(p)
+*
+* (C) 2007 Martin Doering, Christoph Ludwig, Falko Strenzke
+* 2010-2011,2012,2014 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_GFP_CURVE_H_
+#define BOTAN_GFP_CURVE_H_
+
+#include <botan/bigint.h>
+#include <memory>
+
+// Currently exposed in PointGFp
+//BOTAN_FUTURE_INTERNAL_HEADER(curve_gfp.h)
+
+namespace Botan {
+
+class BOTAN_UNSTABLE_API CurveGFp_Repr
+ {
+ public:
+ virtual ~CurveGFp_Repr() = default;
+
+ virtual const BigInt& get_p() const = 0;
+ virtual const BigInt& get_a() const = 0;
+ virtual const BigInt& get_b() const = 0;
+
+ virtual size_t get_p_words() const = 0;
+
+ virtual size_t get_ws_size() const = 0;
+
+ virtual bool is_one(const BigInt& x) const = 0;
+
+ virtual bool a_is_zero() const = 0;
+
+ virtual bool a_is_minus_3() const = 0;
+
+ /*
+ * Returns to_curve_rep(get_a())
+ */
+ virtual const BigInt& get_a_rep() const = 0;
+
+ /*
+ * Returns to_curve_rep(get_b())
+ */
+ virtual const BigInt& get_b_rep() const = 0;
+
+ /*
+ * Returns to_curve_rep(1)
+ */
+ virtual const BigInt& get_1_rep() const = 0;
+
+ virtual BigInt invert_element(const BigInt& x, secure_vector<word>& ws) const = 0;
+
+ virtual void to_curve_rep(BigInt& x, secure_vector<word>& ws) const = 0;
+
+ virtual void from_curve_rep(BigInt& x, secure_vector<word>& ws) const = 0;
+
+ void curve_mul(BigInt& z, const BigInt& x, const BigInt& y,
+ secure_vector<word>& ws) const
+ {
+ BOTAN_DEBUG_ASSERT(x.sig_words() <= get_p_words());
+ curve_mul_words(z, x.data(), x.size(), y, ws);
+ }
+
+ virtual void curve_mul_words(BigInt& z,
+ const word x_words[],
+ const size_t x_size,
+ const BigInt& y,
+ secure_vector<word>& ws) const = 0;
+
+ void curve_sqr(BigInt& z, const BigInt& x,
+ secure_vector<word>& ws) const
+ {
+ BOTAN_DEBUG_ASSERT(x.sig_words() <= get_p_words());
+ curve_sqr_words(z, x.data(), x.size(), ws);
+ }
+
+ virtual void curve_sqr_words(BigInt& z,
+ const word x_words[],
+ size_t x_size,
+ secure_vector<word>& ws) const = 0;
+ };
+
+/**
+* This class represents an elliptic curve over GF(p)
+*
+* There should not be any reason for applications to use this type.
+* If you need EC primitives use the interfaces EC_Group and PointGFp
+*
+* It is likely this class will be removed entirely in a future major
+* release.
+*/
+class BOTAN_UNSTABLE_API CurveGFp final
+ {
+ public:
+
+ /**
+ * Create an uninitialized CurveGFp
+ */
+ CurveGFp() = default;
+
+ /**
+ * Construct the elliptic curve E: y^2 = x^3 + ax + b over GF(p)
+ * @param p prime number of the field
+ * @param a first coefficient
+ * @param b second coefficient
+ */
+ CurveGFp(const BigInt& p, const BigInt& a, const BigInt& b) :
+ m_repr(choose_repr(p, a, b))
+ {
+ }
+
+ CurveGFp(const CurveGFp&) = default;
+
+ CurveGFp& operator=(const CurveGFp&) = default;
+
+ /**
+ * @return curve coefficient a
+ */
+ const BigInt& get_a() const { return m_repr->get_a(); }
+
+ /**
+ * @return curve coefficient b
+ */
+ const BigInt& get_b() const { return m_repr->get_b(); }
+
+ /**
+ * Get prime modulus of the field of the curve
+ * @return prime modulus of the field of the curve
+ */
+ const BigInt& get_p() const { return m_repr->get_p(); }
+
+ size_t get_p_words() const { return m_repr->get_p_words(); }
+
+ size_t get_ws_size() const { return m_repr->get_ws_size(); }
+
+ const BigInt& get_a_rep() const { return m_repr->get_a_rep(); }
+
+ const BigInt& get_b_rep() const { return m_repr->get_b_rep(); }
+
+ const BigInt& get_1_rep() const { return m_repr->get_1_rep(); }
+
+ bool a_is_minus_3() const { return m_repr->a_is_minus_3(); }
+ bool a_is_zero() const { return m_repr->a_is_zero(); }
+
+ bool is_one(const BigInt& x) const { return m_repr->is_one(x); }
+
+ BigInt invert_element(const BigInt& x, secure_vector<word>& ws) const
+ {
+ return m_repr->invert_element(x, ws);
+ }
+
+ void to_rep(BigInt& x, secure_vector<word>& ws) const
+ {
+ m_repr->to_curve_rep(x, ws);
+ }
+
+ void from_rep(BigInt& x, secure_vector<word>& ws) const
+ {
+ m_repr->from_curve_rep(x, ws);
+ }
+
+ BigInt from_rep_to_tmp(const BigInt& x, secure_vector<word>& ws) const
+ {
+ BigInt xt(x);
+ m_repr->from_curve_rep(xt, ws);
+ return xt;
+ }
+
+ // TODO: from_rep taking && ref
+
+ void mul(BigInt& z, const BigInt& x, const BigInt& y, secure_vector<word>& ws) const
+ {
+ m_repr->curve_mul(z, x, y, ws);
+ }
+
+ void mul(BigInt& z, const word x_w[], size_t x_size,
+ const BigInt& y, secure_vector<word>& ws) const
+ {
+ m_repr->curve_mul_words(z, x_w, x_size, y, ws);
+ }
+
+ void sqr(BigInt& z, const BigInt& x, secure_vector<word>& ws) const
+ {
+ m_repr->curve_sqr(z, x, ws);
+ }
+
+ void sqr(BigInt& z, const word x_w[], size_t x_size, secure_vector<word>& ws) const
+ {
+ m_repr->curve_sqr_words(z, x_w, x_size, ws);
+ }
+
+ BigInt mul(const BigInt& x, const BigInt& y, secure_vector<word>& ws) const
+ {
+ return mul_to_tmp(x, y, ws);
+ }
+
+ BigInt sqr(const BigInt& x, secure_vector<word>& ws) const
+ {
+ return sqr_to_tmp(x, ws);
+ }
+
+ BigInt mul_to_tmp(const BigInt& x, const BigInt& y, secure_vector<word>& ws) const
+ {
+ BigInt z;
+ m_repr->curve_mul(z, x, y, ws);
+ return z;
+ }
+
+ BigInt sqr_to_tmp(const BigInt& x, secure_vector<word>& ws) const
+ {
+ BigInt z;
+ m_repr->curve_sqr(z, x, ws);
+ return z;
+ }
+
+ void swap(CurveGFp& other)
+ {
+ std::swap(m_repr, other.m_repr);
+ }
+
+ /**
+ * Equality operator
+ * @param other a curve
+ * @return true iff *this is the same as other
+ */
+ inline bool operator==(const CurveGFp& other) const
+ {
+ if(m_repr.get() == other.m_repr.get())
+ return true;
+
+ return (get_p() == other.get_p()) &&
+ (get_a() == other.get_a()) &&
+ (get_b() == other.get_b());
+ }
+
+ private:
+ static std::shared_ptr<CurveGFp_Repr>
+ choose_repr(const BigInt& p, const BigInt& a, const BigInt& b);
+
+ std::shared_ptr<CurveGFp_Repr> m_repr;
+ };
+
+inline bool operator!=(const CurveGFp& lhs, const CurveGFp& rhs)
+ {
+ return !(lhs == rhs);
+ }
+
+}
+
+namespace std {
+
+template<> inline
+void swap<Botan::CurveGFp>(Botan::CurveGFp& curve1,
+ Botan::CurveGFp& curve2) noexcept
+ {
+ curve1.swap(curve2);
+ }
+
+} // namespace std
+
+#endif
diff --git a/comm/third_party/botan/src/lib/pubkey/ec_group/ec_group.cpp b/comm/third_party/botan/src/lib/pubkey/ec_group/ec_group.cpp
new file mode 100644
index 0000000000..bb60bacf7b
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/ec_group/ec_group.cpp
@@ -0,0 +1,796 @@
+/*
+* ECC Domain Parameters
+*
+* (C) 2007 Falko Strenzke, FlexSecure GmbH
+* (C) 2008,2018 Jack Lloyd
+* (C) 2018 Tobias Niemann
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/ec_group.h>
+#include <botan/internal/point_mul.h>
+#include <botan/internal/primality.h>
+#include <botan/ber_dec.h>
+#include <botan/der_enc.h>
+#include <botan/pem.h>
+#include <botan/reducer.h>
+#include <botan/mutex.h>
+#include <botan/rng.h>
+#include <vector>
+
+namespace Botan {
+
+class EC_Group_Data final
+ {
+ public:
+
+ EC_Group_Data(const BigInt& p,
+ const BigInt& a,
+ const BigInt& b,
+ const BigInt& g_x,
+ const BigInt& g_y,
+ const BigInt& order,
+ const BigInt& cofactor,
+ const OID& oid,
+ EC_Group_Source source) :
+ m_curve(p, a, b),
+ m_base_point(m_curve, g_x, g_y),
+ m_g_x(g_x),
+ m_g_y(g_y),
+ m_order(order),
+ m_cofactor(cofactor),
+ m_mod_order(order),
+ m_base_mult(m_base_point, m_mod_order),
+ m_oid(oid),
+ m_p_bits(p.bits()),
+ m_order_bits(order.bits()),
+ m_a_is_minus_3(a == p - 3),
+ m_a_is_zero(a.is_zero()),
+ m_source(source)
+ {
+ }
+
+ bool match(const BigInt& p, const BigInt& a, const BigInt& b,
+ const BigInt& g_x, 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);
+ }
+
+ void set_oid(const OID& oid)
+ {
+ BOTAN_STATE_CHECK(m_oid.empty());
+ m_oid = oid;
+ }
+
+ const OID& oid() const { return m_oid; }
+ const BigInt& p() const { return m_curve.get_p(); }
+ const BigInt& a() const { return m_curve.get_a(); }
+ const BigInt& b() const { return m_curve.get_b(); }
+ const BigInt& order() const { return m_order; }
+ const BigInt& cofactor() const { return m_cofactor; }
+ const BigInt& g_x() const { return m_g_x; }
+ const BigInt& g_y() const { return m_g_y; }
+
+ size_t p_bits() const { return m_p_bits; }
+ size_t p_bytes() const { return (m_p_bits + 7) / 8; }
+
+ size_t order_bits() const { return m_order_bits; }
+ size_t order_bytes() const { return (m_order_bits + 7) / 8; }
+
+ const CurveGFp& curve() const { return m_curve; }
+ const PointGFp& base_point() const { return m_base_point; }
+
+ bool a_is_minus_3() const { return m_a_is_minus_3; }
+ bool a_is_zero() const { return m_a_is_zero; }
+
+ BigInt mod_order(const BigInt& x) const { return m_mod_order.reduce(x); }
+
+ BigInt square_mod_order(const BigInt& x) const
+ {
+ return m_mod_order.square(x);
+ }
+
+ BigInt multiply_mod_order(const BigInt& x, const BigInt& y) const
+ {
+ return m_mod_order.multiply(x, y);
+ }
+
+ BigInt multiply_mod_order(const BigInt& x, const BigInt& y, const BigInt& z) const
+ {
+ return m_mod_order.multiply(m_mod_order.multiply(x, y), z);
+ }
+
+ BigInt inverse_mod_order(const BigInt& x) const
+ {
+ return inverse_mod(x, m_order);
+ }
+
+ PointGFp blinded_base_point_multiply(const BigInt& k,
+ RandomNumberGenerator& rng,
+ std::vector<BigInt>& ws) const
+ {
+ return m_base_mult.mul(k, rng, m_order, ws);
+ }
+
+ EC_Group_Source source() const { return m_source; }
+
+ private:
+ CurveGFp m_curve;
+ PointGFp m_base_point;
+
+ BigInt m_g_x;
+ BigInt m_g_y;
+ BigInt m_order;
+ BigInt m_cofactor;
+ Modular_Reducer m_mod_order;
+ PointGFp_Base_Point_Precompute m_base_mult;
+ OID m_oid;
+ size_t m_p_bits;
+ size_t m_order_bits;
+ bool m_a_is_minus_3;
+ bool m_a_is_zero;
+ EC_Group_Source m_source;
+ };
+
+class EC_Group_Data_Map final
+ {
+ public:
+ EC_Group_Data_Map() {}
+
+ size_t clear()
+ {
+ lock_guard_type<mutex_type> lock(m_mutex);
+ size_t count = m_registered_curves.size();
+ m_registered_curves.clear();
+ return count;
+ }
+
+ std::shared_ptr<EC_Group_Data> lookup(const OID& oid)
+ {
+ lock_guard_type<mutex_type> lock(m_mutex);
+
+ for(auto i : m_registered_curves)
+ {
+ if(i->oid() == oid)
+ return i;
+ }
+
+ // Not found, check hardcoded data
+ std::shared_ptr<EC_Group_Data> data = EC_Group::EC_group_info(oid);
+
+ if(data)
+ {
+ m_registered_curves.push_back(data);
+ return data;
+ }
+
+ // Nope, unknown curve
+ return std::shared_ptr<EC_Group_Data>();
+ }
+
+ std::shared_ptr<EC_Group_Data> lookup_or_create(const BigInt& p,
+ const BigInt& a,
+ const BigInt& b,
+ const BigInt& g_x,
+ const BigInt& g_y,
+ const BigInt& order,
+ const BigInt& cofactor,
+ const OID& oid,
+ EC_Group_Source source)
+ {
+ lock_guard_type<mutex_type> lock(m_mutex);
+
+ for(auto i : m_registered_curves)
+ {
+ if(!oid.empty())
+ {
+ if(i->oid() == oid)
+ {
+ if(!i->match(p, a, b, g_x, g_y, order, cofactor))
+ throw Invalid_Argument("Attempting to register a curve using OID " + oid.to_string() +
+ " but another curve is already registered using that OID");
+ return i;
+ }
+ else if(i->oid().has_value())
+ continue; // distinct OIDs so not a match
+ }
+
+ if(i->match(p, a, b, g_x, g_y, order, cofactor))
+ {
+ /*
+ * If the same curve was previously created without an OID
+ * but has been registered again using an OID, save that OID.
+ */
+ if(oid.empty() == false)
+ {
+ if(i->oid().empty() == true)
+ {
+ i->set_oid(oid);
+ }
+ else
+ {
+ throw Invalid_Argument("Cannot register ECC group with OID " + oid.to_string() +
+ " already registered using " + i->oid().to_string());
+ }
+ }
+ return i;
+ }
+ }
+
+ // Not found - if OID is set try looking up that way
+
+ if(oid.has_value())
+ {
+ // Not located in existing store - try hardcoded data set
+ std::shared_ptr<EC_Group_Data> data = EC_Group::EC_group_info(oid);
+
+ if(data)
+ {
+ m_registered_curves.push_back(data);
+ return data;
+ }
+ }
+
+ // Not found or no OID, add data and return
+ std::shared_ptr<EC_Group_Data> d =
+ std::make_shared<EC_Group_Data>(p, a, b, g_x, g_y, order, cofactor, oid, source);
+
+ m_registered_curves.push_back(d);
+ return d;
+ }
+
+ private:
+ mutex_type m_mutex;
+ std::vector<std::shared_ptr<EC_Group_Data>> m_registered_curves;
+ };
+
+//static
+EC_Group_Data_Map& EC_Group::ec_group_data()
+ {
+ /*
+ * This exists purely to ensure the allocator is constructed before g_ec_data,
+ * which ensures that its destructor runs after ~g_ec_data is complete.
+ */
+
+ static Allocator_Initializer g_init_allocator;
+ static EC_Group_Data_Map g_ec_data;
+ return g_ec_data;
+ }
+
+//static
+size_t EC_Group::clear_registered_curve_data()
+ {
+ return ec_group_data().clear();
+ }
+
+//static
+std::shared_ptr<EC_Group_Data>
+EC_Group::load_EC_group_info(const char* p_str,
+ const char* a_str,
+ const char* b_str,
+ const char* g_x_str,
+ const char* g_y_str,
+ const char* order_str,
+ const OID& oid)
+ {
+ const BigInt p(p_str);
+ const BigInt a(a_str);
+ const BigInt b(b_str);
+ const BigInt g_x(g_x_str);
+ const BigInt g_y(g_y_str);
+ const BigInt order(order_str);
+ const BigInt cofactor(1); // implicit
+
+ return std::make_shared<EC_Group_Data>(p, a, b, g_x, g_y, order, cofactor, oid, EC_Group_Source::Builtin);
+ }
+
+//static
+std::shared_ptr<EC_Group_Data> EC_Group::BER_decode_EC_group(const uint8_t bits[], size_t len,
+ EC_Group_Source source)
+ {
+ BER_Decoder ber(bits, len);
+ BER_Object obj = ber.get_next_object();
+
+ if(obj.type() == NULL_TAG)
+ {
+ throw Decoding_Error("Cannot handle ImplicitCA ECC parameters");
+ }
+ else if(obj.type() == OBJECT_ID)
+ {
+ OID dom_par_oid;
+ BER_Decoder(bits, len).decode(dom_par_oid);
+ return ec_group_data().lookup(dom_par_oid);
+ }
+ else if(obj.type() == SEQUENCE)
+ {
+ BigInt p, a, b, order, cofactor;
+ std::vector<uint8_t> base_pt;
+ std::vector<uint8_t> seed;
+
+ BER_Decoder(bits, len)
+ .start_cons(SEQUENCE)
+ .decode_and_check<size_t>(1, "Unknown ECC param version code")
+ .start_cons(SEQUENCE)
+ .decode_and_check(OID("1.2.840.10045.1.1"),
+ "Only prime ECC fields supported")
+ .decode(p)
+ .end_cons()
+ .start_cons(SEQUENCE)
+ .decode_octet_string_bigint(a)
+ .decode_octet_string_bigint(b)
+ .decode_optional_string(seed, BIT_STRING, BIT_STRING)
+ .end_cons()
+ .decode(base_pt, OCTET_STRING)
+ .decode(order)
+ .decode(cofactor)
+ .end_cons()
+ .verify_end();
+
+ if(p.bits() < 64 || p.is_negative() || !is_bailie_psw_probable_prime(p))
+ throw Decoding_Error("Invalid ECC p parameter");
+
+ if(a.is_negative() || a >= p)
+ throw Decoding_Error("Invalid ECC a parameter");
+
+ if(b <= 0 || b >= p)
+ throw Decoding_Error("Invalid ECC b parameter");
+
+ if(order <= 0 || !is_bailie_psw_probable_prime(order))
+ throw Decoding_Error("Invalid ECC order parameter");
+
+ if(cofactor <= 0 || cofactor >= 16)
+ throw Decoding_Error("Invalid ECC cofactor parameter");
+
+ std::pair<BigInt, BigInt> base_xy = Botan::OS2ECP(base_pt.data(), base_pt.size(), p, a, b);
+
+ return ec_group_data().lookup_or_create(p, a, b, base_xy.first, base_xy.second,
+ order, cofactor, OID(), source);
+ }
+ else
+ {
+ throw Decoding_Error("Unexpected tag while decoding ECC domain params");
+ }
+ }
+
+EC_Group::EC_Group()
+ {
+ }
+
+EC_Group::~EC_Group()
+ {
+ // shared_ptr possibly freed here
+ }
+
+EC_Group::EC_Group(const OID& domain_oid)
+ {
+ this->m_data = ec_group_data().lookup(domain_oid);
+ if(!this->m_data)
+ throw Invalid_Argument("Unknown EC_Group " + domain_oid.to_string());
+ }
+
+EC_Group::EC_Group(const std::string& str)
+ {
+ if(str == "")
+ return; // no initialization / uninitialized
+
+ try
+ {
+ const OID oid = OID::from_string(str);
+ if(oid.has_value())
+ m_data = ec_group_data().lookup(oid);
+ }
+ catch(...)
+ {
+ }
+
+ if(m_data == nullptr)
+ {
+ if(str.size() > 30 && str.substr(0, 29) == "-----BEGIN EC PARAMETERS-----")
+ {
+ // OK try it as PEM ...
+ secure_vector<uint8_t> ber = PEM_Code::decode_check_label(str, "EC PARAMETERS");
+ this->m_data = BER_decode_EC_group(ber.data(), ber.size(), EC_Group_Source::ExternalSource);
+ }
+ }
+
+ if(m_data == nullptr)
+ throw Invalid_Argument("Unknown ECC group '" + str + "'");
+ }
+
+//static
+EC_Group EC_Group::EC_Group_from_PEM(const std::string& pem)
+ {
+ const auto ber = PEM_Code::decode_check_label(pem, "EC PARAMETERS");
+ return EC_Group(ber.data(), ber.size());
+ }
+
+//static
+std::string EC_Group::PEM_for_named_group(const std::string& name)
+ {
+ try
+ {
+ EC_Group group(name);
+ return group.PEM_encode();
+ }
+ catch(...)
+ {
+ return "";
+ }
+ }
+
+EC_Group::EC_Group(const BigInt& p,
+ const BigInt& a,
+ const BigInt& b,
+ const BigInt& base_x,
+ const BigInt& base_y,
+ 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);
+ }
+
+EC_Group::EC_Group(const uint8_t ber[], size_t ber_len)
+ {
+ m_data = BER_decode_EC_group(ber, ber_len, EC_Group_Source::ExternalSource);
+ }
+
+const EC_Group_Data& EC_Group::data() const
+ {
+ if(m_data == nullptr)
+ throw Invalid_State("EC_Group uninitialized");
+ return *m_data;
+ }
+
+const CurveGFp& EC_Group::get_curve() const
+ {
+ return data().curve();
+ }
+
+bool EC_Group::a_is_minus_3() const
+ {
+ return data().a_is_minus_3();
+ }
+
+bool EC_Group::a_is_zero() const
+ {
+ return data().a_is_zero();
+ }
+
+size_t EC_Group::get_p_bits() const
+ {
+ return data().p_bits();
+ }
+
+size_t EC_Group::get_p_bytes() const
+ {
+ return data().p_bytes();
+ }
+
+size_t EC_Group::get_order_bits() const
+ {
+ return data().order_bits();
+ }
+
+size_t EC_Group::get_order_bytes() const
+ {
+ return data().order_bytes();
+ }
+
+const BigInt& EC_Group::get_p() const
+ {
+ return data().p();
+ }
+
+const BigInt& EC_Group::get_a() const
+ {
+ return data().a();
+ }
+
+const BigInt& EC_Group::get_b() const
+ {
+ return data().b();
+ }
+
+const PointGFp& EC_Group::get_base_point() const
+ {
+ return data().base_point();
+ }
+
+const BigInt& EC_Group::get_order() const
+ {
+ return data().order();
+ }
+
+const BigInt& EC_Group::get_g_x() const
+ {
+ return data().g_x();
+ }
+
+const BigInt& EC_Group::get_g_y() const
+ {
+ return data().g_y();
+ }
+
+const BigInt& EC_Group::get_cofactor() const
+ {
+ return data().cofactor();
+ }
+
+BigInt EC_Group::mod_order(const BigInt& k) const
+ {
+ return data().mod_order(k);
+ }
+
+BigInt EC_Group::square_mod_order(const BigInt& x) const
+ {
+ return data().square_mod_order(x);
+ }
+
+BigInt EC_Group::multiply_mod_order(const BigInt& x, const BigInt& y) const
+ {
+ return data().multiply_mod_order(x, y);
+ }
+
+BigInt EC_Group::multiply_mod_order(const BigInt& x, const BigInt& y, const BigInt& z) const
+ {
+ return data().multiply_mod_order(x, y, z);
+ }
+
+BigInt EC_Group::inverse_mod_order(const BigInt& x) const
+ {
+ return data().inverse_mod_order(x);
+ }
+
+const OID& EC_Group::get_curve_oid() const
+ {
+ return data().oid();
+ }
+
+EC_Group_Source EC_Group::source() const
+ {
+ return data().source();
+ }
+
+size_t EC_Group::point_size(PointGFp::Compression_Type format) const
+ {
+ // Hybrid and standard format are (x,y), compressed is y, +1 format byte
+ if(format == PointGFp::COMPRESSED)
+ return (1 + get_p_bytes());
+ else
+ return (1 + 2*get_p_bytes());
+ }
+
+PointGFp EC_Group::OS2ECP(const uint8_t bits[], size_t len) const
+ {
+ return Botan::OS2ECP(bits, len, data().curve());
+ }
+
+PointGFp EC_Group::point(const BigInt& x, const BigInt& y) const
+ {
+ // TODO: randomize the representation?
+ return PointGFp(data().curve(), x, y);
+ }
+
+PointGFp EC_Group::point_multiply(const BigInt& x, const PointGFp& pt, const BigInt& y) const
+ {
+ PointGFp_Multi_Point_Precompute xy_mul(get_base_point(), pt);
+ return xy_mul.multi_exp(x, y);
+ }
+
+PointGFp EC_Group::blinded_base_point_multiply(const BigInt& k,
+ RandomNumberGenerator& rng,
+ std::vector<BigInt>& ws) const
+ {
+ return data().blinded_base_point_multiply(k, rng, ws);
+ }
+
+BigInt EC_Group::blinded_base_point_multiply_x(const BigInt& k,
+ RandomNumberGenerator& rng,
+ std::vector<BigInt>& ws) const
+ {
+ const PointGFp pt = data().blinded_base_point_multiply(k, rng, ws);
+
+ if(pt.is_zero())
+ return 0;
+ return pt.get_affine_x();
+ }
+
+BigInt EC_Group::random_scalar(RandomNumberGenerator& rng) const
+ {
+ return BigInt::random_integer(rng, 1, get_order());
+ }
+
+PointGFp EC_Group::blinded_var_point_multiply(const PointGFp& point,
+ const BigInt& k,
+ RandomNumberGenerator& rng,
+ std::vector<BigInt>& ws) const
+ {
+ PointGFp_Var_Point_Precompute mul(point, rng, ws);
+ return mul.mul(k, rng, get_order(), ws);
+ }
+
+PointGFp EC_Group::zero_point() const
+ {
+ return PointGFp(data().curve());
+ }
+
+std::vector<uint8_t>
+EC_Group::DER_encode(EC_Group_Encoding form) const
+ {
+ std::vector<uint8_t> output;
+
+ DER_Encoder der(output);
+
+ if(form == EC_DOMPAR_ENC_EXPLICIT)
+ {
+ const size_t ecpVers1 = 1;
+ const OID curve_type("1.2.840.10045.1.1"); // prime field
+
+ const size_t p_bytes = get_p_bytes();
+
+ der.start_cons(SEQUENCE)
+ .encode(ecpVers1)
+ .start_cons(SEQUENCE)
+ .encode(curve_type)
+ .encode(get_p())
+ .end_cons()
+ .start_cons(SEQUENCE)
+ .encode(BigInt::encode_1363(get_a(), p_bytes),
+ OCTET_STRING)
+ .encode(BigInt::encode_1363(get_b(), p_bytes),
+ OCTET_STRING)
+ .end_cons()
+ .encode(get_base_point().encode(PointGFp::UNCOMPRESSED), OCTET_STRING)
+ .encode(get_order())
+ .encode(get_cofactor())
+ .end_cons();
+ }
+ else if(form == EC_DOMPAR_ENC_OID)
+ {
+ const OID oid = get_curve_oid();
+ if(oid.empty())
+ {
+ throw Encoding_Error("Cannot encode EC_Group as OID because OID not set");
+ }
+ der.encode(oid);
+ }
+ else if(form == EC_DOMPAR_ENC_IMPLICITCA)
+ {
+ der.encode_null();
+ }
+ else
+ {
+ throw Internal_Error("EC_Group::DER_encode: Unknown encoding");
+ }
+
+ return output;
+ }
+
+std::string EC_Group::PEM_encode() const
+ {
+ const std::vector<uint8_t> der = DER_encode(EC_DOMPAR_ENC_EXPLICIT);
+ return PEM_Code::encode(der, "EC PARAMETERS");
+ }
+
+bool EC_Group::operator==(const EC_Group& other) const
+ {
+ if(m_data == other.m_data)
+ return true; // same shared rep
+
+ /*
+ * No point comparing order/cofactor as they are uniquely determined
+ * by the curve equation (p,a,b) and the base point.
+ */
+ return (get_p() == other.get_p() &&
+ get_a() == other.get_a() &&
+ get_b() == other.get_b() &&
+ get_g_x() == other.get_g_x() &&
+ get_g_y() == other.get_g_y());
+ }
+
+bool EC_Group::verify_public_element(const PointGFp& point) const
+ {
+ //check that public point is not at infinity
+ if(point.is_zero())
+ return false;
+
+ //check that public point is on the curve
+ if(point.on_the_curve() == false)
+ return false;
+
+ //check that public point has order q
+ if((point * get_order()).is_zero() == false)
+ return false;
+
+ if(get_cofactor() > 1)
+ {
+ if((point * get_cofactor()).is_zero())
+ return false;
+ }
+
+ return true;
+ }
+
+bool EC_Group::verify_group(RandomNumberGenerator& rng,
+ bool strong) const
+ {
+ const bool is_builtin = source() == EC_Group_Source::Builtin;
+
+ if(is_builtin && !strong)
+ return true;
+
+ const BigInt& p = get_p();
+ const BigInt& a = get_a();
+ const BigInt& b = get_b();
+ const BigInt& order = get_order();
+ const PointGFp& base_point = get_base_point();
+
+ if(p <= 3 || order <= 0)
+ return false;
+ if(a < 0 || a >= p)
+ return false;
+ if(b <= 0 || b >= p)
+ return false;
+
+ const size_t test_prob = 128;
+ const bool is_randomly_generated = is_builtin;
+
+ //check if field modulus is prime
+ if(!is_prime(p, rng, test_prob, is_randomly_generated))
+ {
+ return false;
+ }
+
+ //check if order is prime
+ if(!is_prime(order, rng, test_prob, is_randomly_generated))
+ {
+ return false;
+ }
+
+ //compute the discriminant: 4*a^3 + 27*b^2 which must be nonzero
+ const Modular_Reducer mod_p(p);
+
+ const BigInt discriminant = mod_p.reduce(
+ mod_p.multiply(4, mod_p.cube(a)) +
+ mod_p.multiply(27, mod_p.square(b)));
+
+ if(discriminant == 0)
+ {
+ return false;
+ }
+
+ //check for valid cofactor
+ if(get_cofactor() < 1)
+ {
+ return false;
+ }
+
+ //check if the base point is on the curve
+ if(!base_point.on_the_curve())
+ {
+ return false;
+ }
+ if((base_point * get_cofactor()).is_zero())
+ {
+ return false;
+ }
+ //check if order of the base point is correct
+ if(!(base_point * order).is_zero())
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/pubkey/ec_group/ec_group.h b/comm/third_party/botan/src/lib/pubkey/ec_group/ec_group.h
new file mode 100644
index 0000000000..8f583a3092
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/ec_group/ec_group.h
@@ -0,0 +1,398 @@
+/*
+* ECC Domain Parameters
+*
+* (C) 2007 Falko Strenzke, FlexSecure GmbH
+* 2008-2010 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_ECC_DOMAIN_PARAMETERS_H_
+#define BOTAN_ECC_DOMAIN_PARAMETERS_H_
+
+#include <botan/point_gfp.h>
+#include <botan/asn1_obj.h>
+#include <memory>
+#include <set>
+
+namespace Botan {
+
+/**
+* This class represents elliptic curce domain parameters
+*/
+enum EC_Group_Encoding {
+ EC_DOMPAR_ENC_EXPLICIT = 0,
+ EC_DOMPAR_ENC_IMPLICITCA = 1,
+ EC_DOMPAR_ENC_OID = 2
+};
+
+enum class EC_Group_Source {
+ Builtin,
+ ExternalSource,
+};
+
+class CurveGFp;
+
+class EC_Group_Data;
+class EC_Group_Data_Map;
+
+/**
+* Class representing an elliptic curve
+*
+* The internal representation is stored in a shared_ptr, so copying an
+* EC_Group is inexpensive.
+*/
+class BOTAN_PUBLIC_API(2,0) EC_Group final
+ {
+ public:
+
+ /**
+ * Construct Domain paramers from specified parameters
+ * @param curve elliptic curve
+ * @param base_point a base point
+ * @param order the order of the base point
+ * @param cofactor the cofactor
+ */
+ BOTAN_DEPRECATED("Use version taking all BigInts")
+ EC_Group(const CurveGFp& curve,
+ const PointGFp& base_point,
+ const BigInt& order,
+ const BigInt& cofactor) :
+ EC_Group(curve.get_p(),
+ curve.get_a(),
+ curve.get_b(),
+ base_point.get_affine_x(),
+ base_point.get_affine_y(),
+ order,
+ cofactor) {}
+
+ /**
+ * Construct Domain paramers from specified parameters
+ * @param p the elliptic curve p
+ * @param a the elliptic curve a param
+ * @param b the elliptic curve b param
+ * @param base_x the x coordinate of the base point
+ * @param base_y the y coordinate of the base point
+ * @param order the order of the base point
+ * @param cofactor the cofactor
+ * @param oid an optional OID used to identify this curve
+ */
+ EC_Group(const BigInt& p,
+ const BigInt& a,
+ const BigInt& b,
+ const BigInt& base_x,
+ const BigInt& base_y,
+ const BigInt& order,
+ const BigInt& cofactor,
+ const OID& oid = OID());
+
+ /**
+ * Decode a BER encoded ECC domain parameter set
+ * @param ber the bytes of the BER encoding
+ * @param ber_len the length of ber
+ */
+ explicit EC_Group(const uint8_t ber[], size_t ber_len);
+
+ template<typename Alloc>
+ EC_Group(const std::vector<uint8_t, Alloc>& ber) :
+ EC_Group(ber.data(), ber.size()) {}
+
+ /**
+ * Create an EC domain by OID (or throw if unknown)
+ * @param oid the OID of the EC domain to create
+ */
+ explicit EC_Group(const OID& oid);
+
+ /**
+ * Create an EC domain from PEM encoding (as from PEM_encode), or
+ * from an OID name (eg "secp256r1", or "1.2.840.10045.3.1.7")
+ * @param pem_or_oid PEM-encoded data, or an OID
+
+ * @warning Support for PEM in this function is deprecated. Use
+ * EC_Group_from_PEM
+ */
+ explicit EC_Group(const std::string& pem_or_oid);
+
+ static EC_Group EC_Group_from_PEM(const std::string& pem);
+
+ /**
+ * Create an uninitialized EC_Group
+ */
+ EC_Group();
+
+ ~EC_Group();
+
+ EC_Group(const EC_Group&) = default;
+ EC_Group(EC_Group&&) = default;
+
+ EC_Group& operator=(const EC_Group&) = default;
+ EC_Group& operator=(EC_Group&&) = default;
+
+ /**
+ * Create the DER encoding of this domain
+ * @param form of encoding to use
+ * @returns bytes encododed as DER
+ */
+ std::vector<uint8_t> DER_encode(EC_Group_Encoding form) const;
+
+ /**
+ * Return the PEM encoding (always in explicit form)
+ * @return string containing PEM data
+ */
+ std::string PEM_encode() const;
+
+ /**
+ * Return domain parameter curve
+ * @result domain parameter curve
+ */
+ BOTAN_DEPRECATED("Avoid CurveGFp") const CurveGFp& get_curve() const;
+
+ /**
+ * Return if a == -3 mod p
+ */
+ bool a_is_minus_3() const;
+
+ /**
+ * Return if a == 0 mod p
+ */
+ bool a_is_zero() const;
+
+ /**
+ * Return the size of p in bits (same as get_p().bits())
+ */
+ size_t get_p_bits() const;
+
+ /**
+ * Return the size of p in bits (same as get_p().bytes())
+ */
+ size_t get_p_bytes() const;
+
+ /**
+ * Return the size of group order in bits (same as get_order().bits())
+ */
+ size_t get_order_bits() const;
+
+ /**
+ * Return the size of p in bytes (same as get_order().bytes())
+ */
+ size_t get_order_bytes() const;
+
+ /**
+ * Return the prime modulus of the field
+ */
+ const BigInt& get_p() const;
+
+ /**
+ * Return the a parameter of the elliptic curve equation
+ */
+ const BigInt& get_a() const;
+
+ /**
+ * Return the b parameter of the elliptic curve equation
+ */
+ const BigInt& get_b() const;
+
+ /**
+ * Return group base point
+ * @result base point
+ */
+ const PointGFp& get_base_point() const;
+
+ /**
+ * Return the x coordinate of the base point
+ */
+ const BigInt& get_g_x() const;
+
+ /**
+ * Return the y coordinate of the base point
+ */
+ const BigInt& get_g_y() const;
+
+ /**
+ * Return the order of the base point
+ * @result order of the base point
+ */
+ const BigInt& get_order() const;
+
+ /*
+ * Reduce x modulo the order
+ */
+ BigInt mod_order(const BigInt& x) const;
+
+ /*
+ * Return inverse of x modulo the order
+ */
+ BigInt inverse_mod_order(const BigInt& x) const;
+
+ /*
+ * Reduce (x*x) modulo the order
+ */
+ BigInt square_mod_order(const BigInt& x) const;
+
+ /*
+ * Reduce (x*y) modulo the order
+ */
+ BigInt multiply_mod_order(const BigInt& x, const BigInt& y) const;
+
+ /*
+ * Reduce (x*y*z) modulo the order
+ */
+ BigInt multiply_mod_order(const BigInt& x, const BigInt& y, const BigInt& z) const;
+
+ /**
+ * Return the cofactor
+ * @result the cofactor
+ */
+ const BigInt& get_cofactor() const;
+
+ /**
+ * Check if y is a plausible point on the curve
+ *
+ * In particular, checks that it is a point on the curve, not infinity,
+ * and that it has order matching the group.
+ */
+ bool verify_public_element(const PointGFp& y) const;
+
+ /**
+ * Return the OID of these domain parameters
+ * @result the OID as a string
+ */
+ std::string BOTAN_DEPRECATED("Use get_curve_oid") get_oid() const { return get_curve_oid().to_string(); }
+
+ /**
+ * Return the OID of these domain parameters
+ * @result the OID
+ */
+ const OID& get_curve_oid() const;
+
+ /**
+ * Return a point on this curve with the affine values x, y
+ */
+ PointGFp point(const BigInt& x, const BigInt& y) const;
+
+ /**
+ * Multi exponentiate. Not constant time.
+ * @return base_point*x + pt*y
+ */
+ PointGFp point_multiply(const BigInt& x, const PointGFp& pt, const BigInt& y) const;
+
+ /**
+ * Blinded point multiplication, attempts resistance to side channels
+ * @param k the scalar
+ * @param rng a random number generator
+ * @param ws a temp workspace
+ * @return base_point*k
+ */
+ PointGFp blinded_base_point_multiply(const BigInt& k,
+ RandomNumberGenerator& rng,
+ std::vector<BigInt>& ws) const;
+
+ /**
+ * Blinded point multiplication, attempts resistance to side channels
+ * Returns just the x coordinate of the point
+ *
+ * @param k the scalar
+ * @param rng a random number generator
+ * @param ws a temp workspace
+ * @return x coordinate of base_point*k
+ */
+ BigInt blinded_base_point_multiply_x(const BigInt& k,
+ RandomNumberGenerator& rng,
+ std::vector<BigInt>& ws) const;
+
+ /**
+ * Blinded point multiplication, attempts resistance to side channels
+ * @param point input point
+ * @param k the scalar
+ * @param rng a random number generator
+ * @param ws a temp workspace
+ * @return point*k
+ */
+ PointGFp blinded_var_point_multiply(const PointGFp& point,
+ const BigInt& k,
+ RandomNumberGenerator& rng,
+ std::vector<BigInt>& ws) const;
+
+ /**
+ * Return a random scalar ie an integer in [1,order)
+ */
+ BigInt random_scalar(RandomNumberGenerator& rng) const;
+
+ /**
+ * Return the zero (or infinite) point on this curve
+ */
+ PointGFp zero_point() const;
+
+ size_t point_size(PointGFp::Compression_Type format) const;
+
+ PointGFp OS2ECP(const uint8_t bits[], size_t len) const;
+
+ template<typename Alloc>
+ PointGFp OS2ECP(const std::vector<uint8_t, Alloc>& vec) const
+ {
+ return this->OS2ECP(vec.data(), vec.size());
+ }
+
+ bool initialized() const { return (m_data != nullptr); }
+
+ /**
+ * Verify EC_Group domain
+ * @returns true if group is valid. false otherwise
+ */
+ bool verify_group(RandomNumberGenerator& rng,
+ bool strong = false) const;
+
+ bool operator==(const EC_Group& other) const;
+
+ EC_Group_Source source() const;
+
+ /**
+ * Return PEM representation of named EC group
+ * Deprecated: Use EC_Group(name).PEM_encode() if this is needed
+ */
+ static std::string BOTAN_DEPRECATED("See header comment") PEM_for_named_group(const std::string& name);
+
+ /**
+ * Return a set of known named EC groups
+ */
+ static const std::set<std::string>& known_named_groups();
+
+ /*
+ * For internal use only
+ */
+ static std::shared_ptr<EC_Group_Data> EC_group_info(const OID& oid);
+
+ static size_t clear_registered_curve_data();
+
+ private:
+ static EC_Group_Data_Map& ec_group_data();
+
+ static std::shared_ptr<EC_Group_Data> BER_decode_EC_group(const uint8_t bits[], size_t len,
+ EC_Group_Source source);
+
+ static std::shared_ptr<EC_Group_Data>
+ load_EC_group_info(const char* p,
+ const char* a,
+ const char* b,
+ const char* g_x,
+ const char* g_y,
+ const char* order,
+ const OID& oid);
+
+ // Member data
+ const EC_Group_Data& data() const;
+ std::shared_ptr<EC_Group_Data> m_data;
+ };
+
+inline bool operator!=(const EC_Group& lhs,
+ const EC_Group& rhs)
+ {
+ return !(lhs == rhs);
+ }
+
+// For compatibility with 1.8
+typedef EC_Group EC_Domain_Params;
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/pubkey/ec_group/ec_named.cpp b/comm/third_party/botan/src/lib/pubkey/ec_group/ec_named.cpp
new file mode 100644
index 0000000000..7fe1517e44
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/ec_group/ec_named.cpp
@@ -0,0 +1,301 @@
+/*
+* List of ECC groups
+* (C) 2013,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/ec_group.h>
+
+namespace Botan {
+
+//static
+std::shared_ptr<EC_Group_Data> EC_Group::EC_group_info(const OID& oid)
+ {
+ // P-256
+ if(oid == OID{1,2,840,10045,3,1,7})
+ return load_EC_group_info("0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF",
+ "0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC",
+ "0x5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B",
+ "0x6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296",
+ "0x4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5",
+ "0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551",
+ oid);
+
+ // P-384
+ if(oid == OID{1,3,132,0,34})
+ return load_EC_group_info("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFF",
+ "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFC",
+ "0xB3312FA7E23EE7E4988E056BE3F82D19181D9C6EFE8141120314088F5013875AC656398D8A2ED19D2A85C8EDD3EC2AEF",
+ "0xAA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB7",
+ "0x3617DE4A96262C6F5D9E98BF9292DC29F8F41DBD289A147CE9DA3113B5F0B8C00A60B1CE1D7E819D7A431D7C90EA0E5F",
+ "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973",
+ oid);
+ // P-521
+ if(oid == OID{1,3,132,0,35})
+ return load_EC_group_info("0x1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",
+ "0x1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC",
+ "0x51953EB9618E1C9A1F929A21A0B68540EEA2DA725B99B315F3B8B489918EF109E156193951EC7E937B1652C0BD3BB1BF073573DF883D2C34F1EF451FD46B503F00",
+ "0xC6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66",
+ "0x11839296A789A3BC0045C8A5FB42C7D1BD998F54449579B446817AFBD17273E662C97EE72995EF42640C550B9013FAD0761353C7086A272C24088BE94769FD16650",
+ "0x1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386409",
+ oid);
+
+ // brainpool160r1
+ if(oid == OID{1,3,36,3,3,2,8,1,1,1})
+ return load_EC_group_info("0xE95E4A5F737059DC60DFC7AD95B3D8139515620F",
+ "0x340E7BE2A280EB74E2BE61BADA745D97E8F7C300",
+ "0x1E589A8595423412134FAA2DBDEC95C8D8675E58",
+ "0xBED5AF16EA3F6A4F62938C4631EB5AF7BDBCDBC3",
+ "0x1667CB477A1A8EC338F94741669C976316DA6321",
+ "0xE95E4A5F737059DC60DF5991D45029409E60FC09",
+ oid);
+ // brainpool192r1
+ if(oid == OID{1,3,36,3,3,2,8,1,1,3})
+ return load_EC_group_info("0xC302F41D932A36CDA7A3463093D18DB78FCE476DE1A86297",
+ "0x6A91174076B1E0E19C39C031FE8685C1CAE040E5C69A28EF",
+ "0x469A28EF7C28CCA3DC721D044F4496BCCA7EF4146FBF25C9",
+ "0xC0A0647EAAB6A48753B033C56CB0F0900A2F5C4853375FD6",
+ "0x14B690866ABD5BB88B5F4828C1490002E6773FA2FA299B8F",
+ "0xC302F41D932A36CDA7A3462F9E9E916B5BE8F1029AC4ACC1",
+ oid);
+ // brainpool224r1
+ if(oid == OID{1,3,36,3,3,2,8,1,1,5})
+ return load_EC_group_info("0xD7C134AA264366862A18302575D1D787B09F075797DA89F57EC8C0FF",
+ "0x68A5E62CA9CE6C1C299803A6C1530B514E182AD8B0042A59CAD29F43",
+ "0x2580F63CCFE44138870713B1A92369E33E2135D266DBB372386C400B",
+ "0xD9029AD2C7E5CF4340823B2A87DC68C9E4CE3174C1E6EFDEE12C07D",
+ "0x58AA56F772C0726F24C6B89E4ECDAC24354B9E99CAA3F6D3761402CD",
+ "0xD7C134AA264366862A18302575D0FB98D116BC4B6DDEBCA3A5A7939F",
+ oid);
+ // brainpool256r1
+ if(oid == OID{1,3,36,3,3,2,8,1,1,7})
+ return load_EC_group_info("0xA9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E5377",
+ "0x7D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9",
+ "0x26DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B6",
+ "0x8BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262",
+ "0x547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F046997",
+ "0xA9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7",
+ oid);
+ // brainpool320r1
+ if(oid == OID{1,3,36,3,3,2,8,1,1,9})
+ return load_EC_group_info("0xD35E472036BC4FB7E13C785ED201E065F98FCFA6F6F40DEF4F92B9EC7893EC28FCD412B1F1B32E27",
+ "0x3EE30B568FBAB0F883CCEBD46D3F3BB8A2A73513F5EB79DA66190EB085FFA9F492F375A97D860EB4",
+ "0x520883949DFDBC42D3AD198640688A6FE13F41349554B49ACC31DCCD884539816F5EB4AC8FB1F1A6",
+ "0x43BD7E9AFB53D8B85289BCC48EE5BFE6F20137D10A087EB6E7871E2A10A599C710AF8D0D39E20611",
+ "0x14FDD05545EC1CC8AB4093247F77275E0743FFED117182EAA9C77877AAAC6AC7D35245D1692E8EE1",
+ "0xD35E472036BC4FB7E13C785ED201E065F98FCFA5B68F12A32D482EC7EE8658E98691555B44C59311",
+ oid);
+ // brainpool384r1
+ if(oid == OID{1,3,36,3,3,2,8,1,1,11})
+ return load_EC_group_info("0x8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B412B1DA197FB71123ACD3A729901D1A71874700133107EC53",
+ "0x7BC382C63D8C150C3C72080ACE05AFA0C2BEA28E4FB22787139165EFBA91F90F8AA5814A503AD4EB04A8C7DD22CE2826",
+ "0x4A8C7DD22CE28268B39B55416F0447C2FB77DE107DCD2A62E880EA53EEB62D57CB4390295DBC9943AB78696FA504C11",
+ "0x1D1C64F068CF45FFA2A63A81B7C13F6B8847A3E77EF14FE3DB7FCAFE0CBD10E8E826E03436D646AAEF87B2E247D4AF1E",
+ "0x8ABE1D7520F9C2A45CB1EB8E95CFD55262B70B29FEEC5864E19C054FF99129280E4646217791811142820341263C5315",
+ "0x8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B31F166E6CAC0425A7CF3AB6AF6B7FC3103B883202E9046565",
+ oid);
+ // brainpool512r1
+ if(oid == OID{1,3,36,3,3,2,8,1,1,13})
+ return load_EC_group_info("0xAADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA703308717D4D9B009BC66842AECDA12AE6A380E62881FF2F2D82C68528AA6056583A48F3",
+ "0x7830A3318B603B89E2327145AC234CC594CBDD8D3DF91610A83441CAEA9863BC2DED5D5AA8253AA10A2EF1C98B9AC8B57F1117A72BF2C7B9E7C1AC4D77FC94CA",
+ "0x3DF91610A83441CAEA9863BC2DED5D5AA8253AA10A2EF1C98B9AC8B57F1117A72BF2C7B9E7C1AC4D77FC94CADC083E67984050B75EBAE5DD2809BD638016F723",
+ "0x81AEE4BDD82ED9645A21322E9C4C6A9385ED9F70B5D916C1B43B62EEF4D0098EFF3B1F78E2D0D48D50D1687B93B97D5F7C6D5047406A5E688B352209BCB9F822",
+ "0x7DDE385D566332ECC0EABFA9CF7822FDF209F70024A57B1AA000C55B881F8111B2DCDE494A5F485E5BCA4BD88A2763AED1CA2B2FA8F0540678CD1E0F3AD80892",
+ "0xAADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA70330870553E5C414CA92619418661197FAC10471DB1D381085DDADDB58796829CA90069",
+ oid);
+ // frp256v1
+ if(oid == OID{1,2,250,1,223,101,256,1})
+ return load_EC_group_info("0xF1FD178C0B3AD58F10126DE8CE42435B3961ADBCABC8CA6DE8FCF353D86E9C03",
+ "0xF1FD178C0B3AD58F10126DE8CE42435B3961ADBCABC8CA6DE8FCF353D86E9C00",
+ "0xEE353FCA5428A9300D4ABA754A44C00FDFEC0C9AE4B1A1803075ED967B7BB73F",
+ "0xB6B3D4C356C139EB31183D4749D423958C27D2DCAF98B70164C97A2DD98F5CFF",
+ "0x6142E0F7C8B204911F9271F0F3ECEF8C2701C307E8E4C9E183115A1554062CFB",
+ "0xF1FD178C0B3AD58F10126DE8CE42435B53DC67E140D2BF941FFDD459C6D655E1",
+ oid);
+ // gost_256A
+ if(oid == OID{1,2,643,2,2,35,1} || oid == OID{1,2,643,2,2,36,0} || oid == OID{1,2,643,7,1,2,1,1,1})
+ return load_EC_group_info("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD97",
+ "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD94",
+ "0xA6",
+ "0x1",
+ "0x8D91E471E0989CDA27DF505A453F2B7635294F2DDF23E3B122ACC99C9E9F1E14",
+ "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6C611070995AD10045841B09B761B893",
+ OID{1,2,643,7,1,2,1,1,1});
+
+ // gost_512A
+ if(oid == OID{1,2,643,7,1,2,1,2,1})
+ return load_EC_group_info("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDC7",
+ "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDC4",
+ "0xE8C2505DEDFC86DDC1BD0B2B6667F1DA34B82574761CB0E879BD081CFD0B6265EE3CB090F30D27614CB4574010DA90DD862EF9D4EBEE4761503190785A71C760",
+ "3",
+ "0x7503CFE87A836AE3A61B8816E25450E6CE5E1C93ACF1ABC1778064FDCBEFA921DF1626BE4FD036E93D75E6A50E3A41E98028FE5FC235F5B889A589CB5215F2A4",
+ "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF27E69532F48D89116FF22B8D4E0560609B4B38ABFAD2B85DCACDB1411F10B275",
+ oid);
+
+ // secp160k1
+ if(oid == OID{1,3,132,0,9})
+ return load_EC_group_info("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFAC73",
+ "0x0",
+ "0x7",
+ "0x3B4C382CE37AA192A4019E763036F4F5DD4D7EBB",
+ "0x938CF935318FDCED6BC28286531733C3F03C4FEE",
+ "0x100000000000000000001B8FA16DFAB9ACA16B6B3",
+ oid);
+ // secp160r1
+ if(oid == OID{1,3,132,0,8})
+ return load_EC_group_info("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFF",
+ "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFC",
+ "0x1C97BEFC54BD7A8B65ACF89F81D4D4ADC565FA45",
+ "0x4A96B5688EF573284664698968C38BB913CBFC82",
+ "0x23A628553168947D59DCC912042351377AC5FB32",
+ "0x100000000000000000001F4C8F927AED3CA752257",
+ oid);
+ // secp160r2
+ if(oid == OID{1,3,132,0,30})
+ return load_EC_group_info("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFAC73",
+ "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFAC70",
+ "0xB4E134D3FB59EB8BAB57274904664D5AF50388BA",
+ "0x52DCB034293A117E1F4FF11B30F7199D3144CE6D",
+ "0xFEAFFEF2E331F296E071FA0DF9982CFEA7D43F2E",
+ "0x100000000000000000000351EE786A818F3A1A16B",
+ oid);
+ // secp192k1
+ if(oid == OID{1,3,132,0,31})
+ return load_EC_group_info("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFEE37",
+ "0x0",
+ "0x3",
+ "0xDB4FF10EC057E9AE26B07D0280B7F4341DA5D1B1EAE06C7D",
+ "0x9B2F2F6D9C5628A7844163D015BE86344082AA88D95E2F9D",
+ "0xFFFFFFFFFFFFFFFFFFFFFFFE26F2FC170F69466A74DEFD8D",
+ oid);
+ // secp192r1
+ if(oid == OID{1,2,840,10045,3,1,1})
+ return load_EC_group_info("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF",
+ "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFC",
+ "0x64210519E59C80E70FA7E9AB72243049FEB8DEECC146B9B1",
+ "0x188DA80EB03090F67CBF20EB43A18800F4FF0AFD82FF1012",
+ "0x7192B95FFC8DA78631011ED6B24CDD573F977A11E794811",
+ "0xFFFFFFFFFFFFFFFFFFFFFFFF99DEF836146BC9B1B4D22831",
+ oid);
+ // secp224k1
+ if(oid == OID{1,3,132,0,32})
+ return load_EC_group_info("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFE56D",
+ "0x0",
+ "0x5",
+ "0xA1455B334DF099DF30FC28A169A467E9E47075A90F7E650EB6B7A45C",
+ "0x7E089FED7FBA344282CAFBD6F7E319F7C0B0BD59E2CA4BDB556D61A5",
+ "0x10000000000000000000000000001DCE8D2EC6184CAF0A971769FB1F7",
+ oid);
+ // secp224r1
+ if(oid == OID{1,3,132,0,33})
+ return load_EC_group_info("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000001",
+ "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFE",
+ "0xB4050A850C04B3ABF54132565044B0B7D7BFD8BA270B39432355FFB4",
+ "0xB70E0CBD6BB4BF7F321390B94A03C1D356C21122343280D6115C1D21",
+ "0xBD376388B5F723FB4C22DFE6CD4375A05A07476444D5819985007E34",
+ "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF16A2E0B8F03E13DD29455C5C2A3D",
+ oid);
+ // secp256k1
+ if(oid == OID{1,3,132,0,10})
+ return load_EC_group_info("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F",
+ "0x0",
+ "0x7",
+ "0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798",
+ "0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8",
+ "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141",
+ oid);
+
+ // sm2p256v1
+ if(oid == OID{1,2,156,10197,1,301})
+ return load_EC_group_info("0xFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF",
+ "0xFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC",
+ "0x28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93",
+ "0x32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7",
+ "0xBC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0",
+ "0xFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123",
+ oid);
+ // x962_p192v2
+ if(oid == OID{1,2,840,10045,3,1,2})
+ return load_EC_group_info("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF",
+ "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFC",
+ "0xCC22D6DFB95C6B25E49C0D6364A4E5980C393AA21668D953",
+ "0xEEA2BAE7E1497842F2DE7769CFE9C989C072AD696F48034A",
+ "0x6574D11D69B6EC7A672BB82A083DF2F2B0847DE970B2DE15",
+ "0xFFFFFFFFFFFFFFFFFFFFFFFE5FB1A724DC80418648D8DD31",
+ oid);
+ // x962_p192v3
+ if(oid == OID{1,2,840,10045,3,1,3})
+ return load_EC_group_info("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF",
+ "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFC",
+ "0x22123DC2395A05CAA7423DAECCC94760A7D462256BD56916",
+ "0x7D29778100C65A1DA1783716588DCE2B8B4AEE8E228F1896",
+ "0x38A90F22637337334B49DCB66A6DC8F9978ACA7648A943B0",
+ "0xFFFFFFFFFFFFFFFFFFFFFFFF7A62D031C83F4294F640EC13",
+ oid);
+ // x962_p239v1
+ if(oid == OID{1,2,840,10045,3,1,4})
+ return load_EC_group_info("0x7FFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFF8000000000007FFFFFFFFFFF",
+ "0x7FFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFF8000000000007FFFFFFFFFFC",
+ "0x6B016C3BDCF18941D0D654921475CA71A9DB2FB27D1D37796185C2942C0A",
+ "0xFFA963CDCA8816CCC33B8642BEDF905C3D358573D3F27FBBD3B3CB9AAAF",
+ "0x7DEBE8E4E90A5DAE6E4054CA530BA04654B36818CE226B39FCCB7B02F1AE",
+ "0x7FFFFFFFFFFFFFFFFFFFFFFF7FFFFF9E5E9A9F5D9071FBD1522688909D0B",
+ oid);
+ // x962_p239v2
+ if(oid == OID{1,2,840,10045,3,1,5})
+ return load_EC_group_info("0x7FFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFF8000000000007FFFFFFFFFFF",
+ "0x7FFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFF8000000000007FFFFFFFFFFC",
+ "0x617FAB6832576CBBFED50D99F0249C3FEE58B94BA0038C7AE84C8C832F2C",
+ "0x38AF09D98727705120C921BB5E9E26296A3CDCF2F35757A0EAFD87B830E7",
+ "0x5B0125E4DBEA0EC7206DA0FC01D9B081329FB555DE6EF460237DFF8BE4BA",
+ "0x7FFFFFFFFFFFFFFFFFFFFFFF800000CFA7E8594377D414C03821BC582063",
+ oid);
+ // x962_p239v3
+ if(oid == OID{1,2,840,10045,3,1,6})
+ return load_EC_group_info("0x7FFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFF8000000000007FFFFFFFFFFF",
+ "0x7FFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFF8000000000007FFFFFFFFFFC",
+ "0x255705FA2A306654B1F4CB03D6A750A30C250102D4988717D9BA15AB6D3E",
+ "0x6768AE8E18BB92CFCF005C949AA2C6D94853D0E660BBF854B1C9505FE95A",
+ "0x1607E6898F390C06BC1D552BAD226F3B6FCFE48B6E818499AF18E3ED6CF3",
+ "0x7FFFFFFFFFFFFFFFFFFFFFFF7FFFFF975DEB41B3A6057C3C432146526551",
+ oid);
+
+ return std::shared_ptr<EC_Group_Data>();
+ }
+
+//static
+const std::set<std::string>& EC_Group::known_named_groups()
+ {
+ static const std::set<std::string> named_groups = {
+ "secp160k1",
+ "secp160r1",
+ "secp160r2",
+ "secp192k1",
+ "secp192r1",
+ "secp224k1",
+ "secp224r1",
+ "secp256k1",
+ "secp256r1",
+ "secp384r1",
+ "secp521r1",
+ "brainpool160r1",
+ "brainpool192r1",
+ "brainpool224r1",
+ "brainpool256r1",
+ "brainpool320r1",
+ "brainpool384r1",
+ "brainpool512r1",
+ "x962_p192v2",
+ "x962_p192v3",
+ "x962_p239v1",
+ "x962_p239v2",
+ "x962_p239v3",
+ "gost_256A",
+ "gost_512A",
+ "frp256v1",
+ "sm2p256v1"
+ };
+ return named_groups;
+ }
+}
diff --git a/comm/third_party/botan/src/lib/pubkey/ec_group/info.txt b/comm/third_party/botan/src/lib/pubkey/ec_group/info.txt
new file mode 100644
index 0000000000..e382e25a5e
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/ec_group/info.txt
@@ -0,0 +1,20 @@
+<defines>
+ECC_GROUP -> 20170225
+EC_CURVE_GFP -> 20131128
+</defines>
+
+<requires>
+asn1
+numbertheory
+pem
+</requires>
+
+<header:internal>
+point_mul.h
+</header:internal>
+
+<header:public>
+curve_gfp.h
+ec_group.h
+point_gfp.h
+</header:public>
diff --git a/comm/third_party/botan/src/lib/pubkey/ec_group/point_gfp.cpp b/comm/third_party/botan/src/lib/pubkey/ec_group/point_gfp.cpp
new file mode 100644
index 0000000000..6c2302bd66
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/ec_group/point_gfp.cpp
@@ -0,0 +1,731 @@
+/*
+* Point arithmetic on elliptic curves over GF(p)
+*
+* (C) 2007 Martin Doering, Christoph Ludwig, Falko Strenzke
+* 2008-2011,2012,2014,2015,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/point_gfp.h>
+#include <botan/numthry.h>
+#include <botan/rng.h>
+#include <botan/internal/rounding.h>
+#include <botan/internal/ct_utils.h>
+
+namespace Botan {
+
+PointGFp::PointGFp(const CurveGFp& curve) :
+ m_curve(curve),
+ m_coord_x(0),
+ m_coord_y(curve.get_1_rep()),
+ m_coord_z(0)
+ {
+ // Assumes Montgomery rep of zero is zero
+ }
+
+PointGFp::PointGFp(const CurveGFp& curve, const BigInt& x, const BigInt& y) :
+ m_curve(curve),
+ m_coord_x(x),
+ m_coord_y(y),
+ m_coord_z(m_curve.get_1_rep())
+ {
+ if(x < 0 || x >= curve.get_p())
+ throw Invalid_Argument("Invalid PointGFp affine x");
+ if(y < 0 || y >= curve.get_p())
+ throw Invalid_Argument("Invalid PointGFp affine y");
+
+ secure_vector<word> monty_ws(m_curve.get_ws_size());
+ m_curve.to_rep(m_coord_x, monty_ws);
+ m_curve.to_rep(m_coord_y, monty_ws);
+ }
+
+void PointGFp::randomize_repr(RandomNumberGenerator& rng)
+ {
+ secure_vector<word> ws(m_curve.get_ws_size());
+ randomize_repr(rng, ws);
+ }
+
+void PointGFp::randomize_repr(RandomNumberGenerator& rng, secure_vector<word>& ws)
+ {
+ const BigInt mask = BigInt::random_integer(rng, 2, m_curve.get_p());
+
+ /*
+ * No reason to convert this to Montgomery representation first,
+ * just pretend the random mask was chosen as Redc(mask) and the
+ * random mask we generated above is in the Montgomery
+ * representation.
+ * //m_curve.to_rep(mask, ws);
+ */
+ const BigInt mask2 = m_curve.sqr_to_tmp(mask, ws);
+ const BigInt mask3 = m_curve.mul_to_tmp(mask2, mask, ws);
+
+ m_coord_x = m_curve.mul_to_tmp(m_coord_x, mask2, ws);
+ m_coord_y = m_curve.mul_to_tmp(m_coord_y, mask3, ws);
+ m_coord_z = m_curve.mul_to_tmp(m_coord_z, mask, ws);
+ }
+
+namespace {
+
+inline void resize_ws(std::vector<BigInt>& ws_bn, size_t cap_size)
+ {
+ BOTAN_ASSERT(ws_bn.size() >= PointGFp::WORKSPACE_SIZE,
+ "Expected size for PointGFp workspace");
+
+ for(size_t i = 0; i != ws_bn.size(); ++i)
+ if(ws_bn[i].size() < cap_size)
+ ws_bn[i].get_word_vector().resize(cap_size);
+ }
+
+inline word all_zeros(const word x[], size_t len)
+ {
+ word z = 0;
+ for(size_t i = 0; i != len; ++i)
+ z |= x[i];
+ return CT::Mask<word>::is_zero(z).value();
+ }
+
+}
+
+void PointGFp::add_affine(const word x_words[], size_t x_size,
+ const word y_words[], size_t y_size,
+ std::vector<BigInt>& ws_bn)
+ {
+ if(all_zeros(x_words, x_size) & all_zeros(y_words, y_size))
+ {
+ return;
+ }
+
+ if(is_zero())
+ {
+ m_coord_x.set_words(x_words, x_size);
+ m_coord_y.set_words(y_words, y_size);
+ m_coord_z = m_curve.get_1_rep();
+ return;
+ }
+
+ resize_ws(ws_bn, m_curve.get_ws_size());
+
+ secure_vector<word>& ws = ws_bn[0].get_word_vector();
+ secure_vector<word>& sub_ws = ws_bn[1].get_word_vector();
+
+ BigInt& T0 = ws_bn[2];
+ BigInt& T1 = ws_bn[3];
+ BigInt& T2 = ws_bn[4];
+ BigInt& T3 = ws_bn[5];
+ BigInt& T4 = ws_bn[6];
+
+ /*
+ https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#addition-add-1998-cmo-2
+ simplified with Z2 = 1
+ */
+
+ const BigInt& p = m_curve.get_p();
+
+ m_curve.sqr(T3, m_coord_z, ws); // z1^2
+ m_curve.mul(T4, x_words, x_size, T3, ws); // x2*z1^2
+
+ m_curve.mul(T2, m_coord_z, T3, ws); // z1^3
+ m_curve.mul(T0, y_words, y_size, T2, ws); // y2*z1^3
+
+ T4.mod_sub(m_coord_x, p, sub_ws); // x2*z1^2 - x1*z2^2
+
+ T0.mod_sub(m_coord_y, p, sub_ws);
+
+ if(T4.is_zero())
+ {
+ if(T0.is_zero())
+ {
+ mult2(ws_bn);
+ return;
+ }
+
+ // setting to zero:
+ m_coord_x.clear();
+ m_coord_y = m_curve.get_1_rep();
+ m_coord_z.clear();
+ return;
+ }
+
+ m_curve.sqr(T2, T4, ws);
+
+ m_curve.mul(T3, m_coord_x, T2, ws);
+
+ m_curve.mul(T1, T2, T4, ws);
+
+ m_curve.sqr(m_coord_x, T0, ws);
+ m_coord_x.mod_sub(T1, p, sub_ws);
+
+ m_coord_x.mod_sub(T3, p, sub_ws);
+ m_coord_x.mod_sub(T3, p, sub_ws);
+
+ T3.mod_sub(m_coord_x, p, sub_ws);
+
+ m_curve.mul(T2, T0, T3, ws);
+ m_curve.mul(T0, m_coord_y, T1, ws);
+ T2.mod_sub(T0, p, sub_ws);
+ m_coord_y.swap(T2);
+
+ m_curve.mul(T0, m_coord_z, T4, ws);
+ m_coord_z.swap(T0);
+ }
+
+void PointGFp::add(const word x_words[], size_t x_size,
+ const word y_words[], size_t y_size,
+ const word z_words[], size_t z_size,
+ std::vector<BigInt>& ws_bn)
+ {
+ if(all_zeros(x_words, x_size) & all_zeros(z_words, z_size))
+ return;
+
+ if(is_zero())
+ {
+ m_coord_x.set_words(x_words, x_size);
+ m_coord_y.set_words(y_words, y_size);
+ m_coord_z.set_words(z_words, z_size);
+ return;
+ }
+
+ resize_ws(ws_bn, m_curve.get_ws_size());
+
+ secure_vector<word>& ws = ws_bn[0].get_word_vector();
+ secure_vector<word>& sub_ws = ws_bn[1].get_word_vector();
+
+ BigInt& T0 = ws_bn[2];
+ BigInt& T1 = ws_bn[3];
+ BigInt& T2 = ws_bn[4];
+ BigInt& T3 = ws_bn[5];
+ BigInt& T4 = ws_bn[6];
+ BigInt& T5 = ws_bn[7];
+
+ /*
+ https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#addition-add-1998-cmo-2
+ */
+
+ const BigInt& p = m_curve.get_p();
+
+ m_curve.sqr(T0, z_words, z_size, ws); // z2^2
+ m_curve.mul(T1, m_coord_x, T0, ws); // x1*z2^2
+ m_curve.mul(T3, z_words, z_size, T0, ws); // z2^3
+ m_curve.mul(T2, m_coord_y, T3, ws); // y1*z2^3
+
+ m_curve.sqr(T3, m_coord_z, ws); // z1^2
+ m_curve.mul(T4, x_words, x_size, T3, ws); // x2*z1^2
+
+ m_curve.mul(T5, m_coord_z, T3, ws); // z1^3
+ m_curve.mul(T0, y_words, y_size, T5, ws); // y2*z1^3
+
+ T4.mod_sub(T1, p, sub_ws); // x2*z1^2 - x1*z2^2
+
+ T0.mod_sub(T2, p, sub_ws);
+
+ if(T4.is_zero())
+ {
+ if(T0.is_zero())
+ {
+ mult2(ws_bn);
+ return;
+ }
+
+ // setting to zero:
+ m_coord_x.clear();
+ m_coord_y = m_curve.get_1_rep();
+ m_coord_z.clear();
+ return;
+ }
+
+ m_curve.sqr(T5, T4, ws);
+
+ m_curve.mul(T3, T1, T5, ws);
+
+ m_curve.mul(T1, T5, T4, ws);
+
+ m_curve.sqr(m_coord_x, T0, ws);
+ m_coord_x.mod_sub(T1, p, sub_ws);
+ m_coord_x.mod_sub(T3, p, sub_ws);
+ m_coord_x.mod_sub(T3, p, sub_ws);
+
+ T3.mod_sub(m_coord_x, p, sub_ws);
+
+ m_curve.mul(m_coord_y, T0, T3, ws);
+ m_curve.mul(T3, T2, T1, ws);
+
+ m_coord_y.mod_sub(T3, p, sub_ws);
+
+ m_curve.mul(T3, z_words, z_size, m_coord_z, ws);
+ m_curve.mul(m_coord_z, T3, T4, ws);
+ }
+
+void PointGFp::mult2i(size_t iterations, std::vector<BigInt>& ws_bn)
+ {
+ if(iterations == 0)
+ return;
+
+ if(m_coord_y.is_zero())
+ {
+ *this = PointGFp(m_curve); // setting myself to zero
+ return;
+ }
+
+ /*
+ TODO we can save 2 squarings per iteration by computing
+ a*Z^4 using values cached from previous iteration
+ */
+ for(size_t i = 0; i != iterations; ++i)
+ mult2(ws_bn);
+ }
+
+// *this *= 2
+void PointGFp::mult2(std::vector<BigInt>& ws_bn)
+ {
+ if(is_zero())
+ return;
+
+ if(m_coord_y.is_zero())
+ {
+ *this = PointGFp(m_curve); // setting myself to zero
+ return;
+ }
+
+ resize_ws(ws_bn, m_curve.get_ws_size());
+
+ secure_vector<word>& ws = ws_bn[0].get_word_vector();
+ secure_vector<word>& sub_ws = ws_bn[1].get_word_vector();
+
+ BigInt& T0 = ws_bn[2];
+ BigInt& T1 = ws_bn[3];
+ BigInt& T2 = ws_bn[4];
+ BigInt& T3 = ws_bn[5];
+ BigInt& T4 = ws_bn[6];
+
+ /*
+ https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#doubling-dbl-1986-cc
+ */
+ const BigInt& p = m_curve.get_p();
+
+ m_curve.sqr(T0, m_coord_y, ws);
+
+ m_curve.mul(T1, m_coord_x, T0, ws);
+ T1.mod_mul(4, p, sub_ws);
+
+ if(m_curve.a_is_zero())
+ {
+ // if a == 0 then 3*x^2 + a*z^4 is just 3*x^2
+ m_curve.sqr(T4, m_coord_x, ws); // x^2
+ T4.mod_mul(3, p, sub_ws); // 3*x^2
+ }
+ else if(m_curve.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)
+ */
+ m_curve.sqr(T3, m_coord_z, ws); // z^2
+
+ // (x-z^2)
+ T2 = m_coord_x;
+ T2.mod_sub(T3, p, sub_ws);
+
+ // (x+z^2)
+ T3.mod_add(m_coord_x, p, sub_ws);
+
+ m_curve.mul(T4, T2, T3, ws); // (x-z^2)*(x+z^2)
+
+ T4.mod_mul(3, p, sub_ws); // 3*(x-z^2)*(x+z^2)
+ }
+ else
+ {
+ m_curve.sqr(T3, m_coord_z, ws); // z^2
+ m_curve.sqr(T4, T3, ws); // z^4
+ m_curve.mul(T3, m_curve.get_a_rep(), T4, ws); // a*z^4
+
+ m_curve.sqr(T4, m_coord_x, ws); // x^2
+ T4.mod_mul(3, p, sub_ws);
+ T4.mod_add(T3, p, sub_ws); // 3*x^2 + a*z^4
+ }
+
+ m_curve.sqr(T2, T4, ws);
+ T2.mod_sub(T1, p, sub_ws);
+ T2.mod_sub(T1, p, sub_ws);
+
+ m_curve.sqr(T3, T0, ws);
+ T3.mod_mul(8, p, sub_ws);
+
+ T1.mod_sub(T2, p, sub_ws);
+
+ m_curve.mul(T0, T4, T1, ws);
+ T0.mod_sub(T3, p, sub_ws);
+
+ m_coord_x.swap(T2);
+
+ m_curve.mul(T2, m_coord_y, m_coord_z, ws);
+ T2.mod_mul(2, p, sub_ws);
+
+ m_coord_y.swap(T0);
+ m_coord_z.swap(T2);
+ }
+
+// arithmetic operators
+PointGFp& PointGFp::operator+=(const PointGFp& rhs)
+ {
+ std::vector<BigInt> ws(PointGFp::WORKSPACE_SIZE);
+ add(rhs, ws);
+ return *this;
+ }
+
+PointGFp& PointGFp::operator-=(const PointGFp& rhs)
+ {
+ PointGFp minus_rhs = PointGFp(rhs).negate();
+
+ if(is_zero())
+ *this = minus_rhs;
+ else
+ *this += minus_rhs;
+
+ return *this;
+ }
+
+PointGFp& PointGFp::operator*=(const BigInt& scalar)
+ {
+ *this = scalar * *this;
+ return *this;
+ }
+
+PointGFp operator*(const BigInt& scalar, const PointGFp& point)
+ {
+ BOTAN_DEBUG_ASSERT(point.on_the_curve());
+
+ const size_t scalar_bits = scalar.bits();
+
+ std::vector<BigInt> ws(PointGFp::WORKSPACE_SIZE);
+
+ PointGFp R[2] = { point.zero(), point };
+
+ for(size_t i = scalar_bits; i > 0; i--)
+ {
+ const size_t b = scalar.get_bit(i - 1);
+ R[b ^ 1].add(R[b], ws);
+ R[b].mult2(ws);
+ }
+
+ if(scalar.is_negative())
+ R[0].negate();
+
+ BOTAN_DEBUG_ASSERT(R[0].on_the_curve());
+
+ return R[0];
+ }
+
+//static
+void PointGFp::force_all_affine(std::vector<PointGFp>& points,
+ secure_vector<word>& ws)
+ {
+ if(points.size() <= 1)
+ {
+ for(size_t i = 0; i != points.size(); ++i)
+ points[i].force_affine();
+ return;
+ }
+
+ for(size_t i = 0; i != points.size(); ++i)
+ {
+ if(points[i].is_zero())
+ throw Invalid_State("Cannot convert zero ECC point to affine");
+ }
+
+ /*
+ For >= 2 points use Montgomery's trick
+
+ See Algorithm 2.26 in "Guide to Elliptic Curve Cryptography"
+ (Hankerson, Menezes, Vanstone)
+
+ TODO is it really necessary to save all k points in c?
+ */
+
+ const CurveGFp& curve = points[0].m_curve;
+ const BigInt& rep_1 = curve.get_1_rep();
+
+ if(ws.size() < curve.get_ws_size())
+ ws.resize(curve.get_ws_size());
+
+ std::vector<BigInt> c(points.size());
+ c[0] = points[0].m_coord_z;
+
+ for(size_t i = 1; i != points.size(); ++i)
+ {
+ curve.mul(c[i], c[i-1], points[i].m_coord_z, ws);
+ }
+
+ BigInt s_inv = curve.invert_element(c[c.size()-1], ws);
+
+ BigInt z_inv, z2_inv, z3_inv;
+
+ for(size_t i = points.size() - 1; i != 0; i--)
+ {
+ PointGFp& point = points[i];
+
+ curve.mul(z_inv, s_inv, c[i-1], ws);
+
+ s_inv = curve.mul_to_tmp(s_inv, point.m_coord_z, ws);
+
+ curve.sqr(z2_inv, z_inv, ws);
+ curve.mul(z3_inv, z2_inv, z_inv, ws);
+ point.m_coord_x = curve.mul_to_tmp(point.m_coord_x, z2_inv, ws);
+ point.m_coord_y = curve.mul_to_tmp(point.m_coord_y, z3_inv, ws);
+ point.m_coord_z = rep_1;
+ }
+
+ curve.sqr(z2_inv, s_inv, ws);
+ curve.mul(z3_inv, z2_inv, s_inv, ws);
+ points[0].m_coord_x = curve.mul_to_tmp(points[0].m_coord_x, z2_inv, ws);
+ points[0].m_coord_y = curve.mul_to_tmp(points[0].m_coord_y, z3_inv, ws);
+ points[0].m_coord_z = rep_1;
+ }
+
+void PointGFp::force_affine()
+ {
+ if(is_zero())
+ throw Invalid_State("Cannot convert zero ECC point to affine");
+
+ secure_vector<word> ws;
+
+ const BigInt z_inv = m_curve.invert_element(m_coord_z, ws);
+ const BigInt z2_inv = m_curve.sqr_to_tmp(z_inv, ws);
+ const BigInt z3_inv = m_curve.mul_to_tmp(z_inv, z2_inv, ws);
+ m_coord_x = m_curve.mul_to_tmp(m_coord_x, z2_inv, ws);
+ m_coord_y = m_curve.mul_to_tmp(m_coord_y, z3_inv, ws);
+ m_coord_z = m_curve.get_1_rep();
+ }
+
+bool PointGFp::is_affine() const
+ {
+ return m_curve.is_one(m_coord_z);
+ }
+
+BigInt PointGFp::get_affine_x() const
+ {
+ if(is_zero())
+ throw Illegal_Transformation("Cannot convert zero point to affine");
+
+ secure_vector<word> monty_ws;
+
+ if(is_affine())
+ return m_curve.from_rep_to_tmp(m_coord_x, monty_ws);
+
+ BigInt z2 = m_curve.sqr_to_tmp(m_coord_z, monty_ws);
+ z2 = m_curve.invert_element(z2, monty_ws);
+
+ BigInt r;
+ m_curve.mul(r, m_coord_x, z2, monty_ws);
+ m_curve.from_rep(r, monty_ws);
+ return r;
+ }
+
+BigInt PointGFp::get_affine_y() const
+ {
+ if(is_zero())
+ throw Illegal_Transformation("Cannot convert zero point to affine");
+
+ secure_vector<word> monty_ws;
+
+ if(is_affine())
+ return m_curve.from_rep_to_tmp(m_coord_y, monty_ws);
+
+ const BigInt z2 = m_curve.sqr_to_tmp(m_coord_z, monty_ws);
+ const BigInt z3 = m_curve.mul_to_tmp(m_coord_z, z2, monty_ws);
+ const BigInt z3_inv = m_curve.invert_element(z3, monty_ws);
+
+ BigInt r;
+ m_curve.mul(r, m_coord_y, z3_inv, monty_ws);
+ m_curve.from_rep(r, monty_ws);
+ return r;
+ }
+
+bool PointGFp::on_the_curve() const
+ {
+ /*
+ Is the point still on the curve?? (If everything is correct, the
+ point is always on its curve; then the function will return true.
+ If somehow the state is corrupted, which suggests a fault attack
+ (or internal computational error), then return false.
+ */
+ if(is_zero())
+ return true;
+
+ secure_vector<word> monty_ws;
+
+ const BigInt y2 = m_curve.from_rep_to_tmp(m_curve.sqr_to_tmp(m_coord_y, monty_ws), monty_ws);
+ const BigInt x3 = m_curve.mul_to_tmp(m_coord_x, m_curve.sqr_to_tmp(m_coord_x, monty_ws), monty_ws);
+ const BigInt ax = m_curve.mul_to_tmp(m_coord_x, m_curve.get_a_rep(), monty_ws);
+ const BigInt z2 = m_curve.sqr_to_tmp(m_coord_z, monty_ws);
+
+ if(m_coord_z == z2) // Is z equal to 1 (in Montgomery form)?
+ {
+ if(y2 != m_curve.from_rep_to_tmp(x3 + ax + m_curve.get_b_rep(), monty_ws))
+ return false;
+ }
+
+ const BigInt z3 = m_curve.mul_to_tmp(m_coord_z, z2, monty_ws);
+ const BigInt ax_z4 = m_curve.mul_to_tmp(ax, m_curve.sqr_to_tmp(z2, monty_ws), monty_ws);
+ const BigInt b_z6 = m_curve.mul_to_tmp(m_curve.get_b_rep(), m_curve.sqr_to_tmp(z3, monty_ws), monty_ws);
+
+ if(y2 != m_curve.from_rep_to_tmp(x3 + ax_z4 + b_z6, monty_ws))
+ return false;
+
+ return true;
+ }
+
+// swaps the states of *this and other, does not throw!
+void PointGFp::swap(PointGFp& other)
+ {
+ m_curve.swap(other.m_curve);
+ m_coord_x.swap(other.m_coord_x);
+ m_coord_y.swap(other.m_coord_y);
+ m_coord_z.swap(other.m_coord_z);
+ }
+
+bool PointGFp::operator==(const PointGFp& other) const
+ {
+ if(m_curve != other.m_curve)
+ return false;
+
+ // If this is zero, only equal if other is also zero
+ if(is_zero())
+ return other.is_zero();
+
+ return (get_affine_x() == other.get_affine_x() &&
+ get_affine_y() == other.get_affine_y());
+ }
+
+// encoding and decoding
+std::vector<uint8_t> PointGFp::encode(PointGFp::Compression_Type format) const
+ {
+ if(is_zero())
+ return std::vector<uint8_t>(1); // single 0 byte
+
+ const size_t p_bytes = m_curve.get_p().bytes();
+
+ const BigInt x = get_affine_x();
+ const BigInt y = get_affine_y();
+
+ std::vector<uint8_t> result;
+
+ if(format == PointGFp::UNCOMPRESSED)
+ {
+ result.resize(1 + 2*p_bytes);
+ result[0] = 0x04;
+ BigInt::encode_1363(&result[1], p_bytes, x);
+ BigInt::encode_1363(&result[1+p_bytes], p_bytes, y);
+ }
+ else if(format == PointGFp::COMPRESSED)
+ {
+ result.resize(1 + p_bytes);
+ result[0] = 0x02 | static_cast<uint8_t>(y.get_bit(0));
+ BigInt::encode_1363(&result[1], p_bytes, x);
+ }
+ else if(format == PointGFp::HYBRID)
+ {
+ result.resize(1 + 2*p_bytes);
+ result[0] = 0x06 | static_cast<uint8_t>(y.get_bit(0));
+ BigInt::encode_1363(&result[1], p_bytes, x);
+ BigInt::encode_1363(&result[1+p_bytes], p_bytes, y);
+ }
+ else
+ throw Invalid_Argument("EC2OSP illegal point encoding");
+
+ return result;
+ }
+
+namespace {
+
+BigInt decompress_point(bool yMod2,
+ const BigInt& x,
+ const BigInt& curve_p,
+ const BigInt& curve_a,
+ const BigInt& curve_b)
+ {
+ BigInt xpow3 = x * x * x;
+
+ BigInt g = curve_a * x;
+ g += xpow3;
+ g += curve_b;
+ g = g % curve_p;
+
+ BigInt z = ressol(g, curve_p);
+
+ if(z < 0)
+ throw Illegal_Point("error during EC point decompression");
+
+ if(z.get_bit(0) != yMod2)
+ z = curve_p - z;
+
+ return z;
+ }
+
+}
+
+PointGFp OS2ECP(const uint8_t data[], size_t data_len,
+ const CurveGFp& curve)
+ {
+ // Should we really be doing this?
+ if(data_len <= 1)
+ return PointGFp(curve); // return zero
+
+ std::pair<BigInt, BigInt> xy = OS2ECP(data, data_len, curve.get_p(), curve.get_a(), curve.get_b());
+
+ PointGFp point(curve, xy.first, xy.second);
+
+ if(!point.on_the_curve())
+ throw Illegal_Point("OS2ECP: Decoded point was not on the curve");
+
+ return point;
+ }
+
+std::pair<BigInt, BigInt> OS2ECP(const uint8_t data[], size_t data_len,
+ const BigInt& curve_p,
+ const BigInt& curve_a,
+ const BigInt& curve_b)
+ {
+ if(data_len <= 1)
+ throw Decoding_Error("OS2ECP invalid point");
+
+ const uint8_t pc = data[0];
+
+ BigInt x, y;
+
+ if(pc == 2 || pc == 3)
+ {
+ //compressed form
+ x = BigInt::decode(&data[1], data_len - 1);
+
+ const bool y_mod_2 = ((pc & 0x01) == 1);
+ y = decompress_point(y_mod_2, x, curve_p, curve_a, curve_b);
+ }
+ else if(pc == 4)
+ {
+ const size_t l = (data_len - 1) / 2;
+
+ // uncompressed form
+ x = BigInt::decode(&data[1], l);
+ y = BigInt::decode(&data[l+1], l);
+ }
+ else if(pc == 6 || pc == 7)
+ {
+ const size_t l = (data_len - 1) / 2;
+
+ // hybrid form
+ x = BigInt::decode(&data[1], l);
+ y = BigInt::decode(&data[l+1], l);
+
+ const bool y_mod_2 = ((pc & 0x01) == 1);
+
+ if(decompress_point(y_mod_2, x, curve_p, curve_a, curve_b) != y)
+ throw Illegal_Point("OS2ECP: Decoding error in hybrid format");
+ }
+ else
+ throw Invalid_Argument("OS2ECP: Unknown format type " + std::to_string(pc));
+
+ return std::make_pair(x, y);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/pubkey/ec_group/point_gfp.h b/comm/third_party/botan/src/lib/pubkey/ec_group/point_gfp.h
new file mode 100644
index 0000000000..13752c4a48
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/ec_group/point_gfp.h
@@ -0,0 +1,447 @@
+/*
+* Point arithmetic on elliptic curves over GF(p)
+*
+* (C) 2007 Martin Doering, Christoph Ludwig, Falko Strenzke
+* 2008-2011,2014,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_POINT_GFP_H_
+#define BOTAN_POINT_GFP_H_
+
+#include <botan/curve_gfp.h>
+#include <botan/exceptn.h>
+#include <vector>
+
+namespace Botan {
+
+/**
+* Exception thrown if you try to convert a zero point to an affine
+* coordinate
+*
+* In a future major release this exception type will be removed and its
+* usage replaced by Invalid_State
+*/
+class BOTAN_PUBLIC_API(2,0) Illegal_Transformation final : public Invalid_State
+ {
+ public:
+ explicit Illegal_Transformation(const std::string& err) : Invalid_State(err) {}
+ };
+
+/**
+* Exception thrown if some form of illegal point is decoded
+*
+* In a future major release this exception type will be removed and its
+* usage replaced by Decoding_Error
+*/
+class BOTAN_PUBLIC_API(2,0) Illegal_Point final : public Decoding_Error
+ {
+ public:
+ explicit Illegal_Point(const std::string& err) : Decoding_Error(err) {}
+ };
+
+/**
+* This class represents one point on a curve of GF(p)
+*/
+class BOTAN_PUBLIC_API(2,0) PointGFp final
+ {
+ public:
+ enum Compression_Type {
+ UNCOMPRESSED = 0,
+ COMPRESSED = 1,
+ HYBRID = 2
+ };
+
+ enum { WORKSPACE_SIZE = 8 };
+
+ /**
+ * Construct an uninitialized PointGFp
+ */
+ PointGFp() = default;
+
+ /**
+ * Construct the zero point
+ * @param curve The base curve
+ */
+ explicit PointGFp(const CurveGFp& curve);
+
+ /**
+ * Copy constructor
+ */
+ PointGFp(const PointGFp&) = default;
+
+ /**
+ * Move Constructor
+ */
+ PointGFp(PointGFp&& other)
+ {
+ this->swap(other);
+ }
+
+ /**
+ * Standard Assignment
+ */
+ PointGFp& operator=(const PointGFp&) = default;
+
+ /**
+ * Move Assignment
+ */
+ PointGFp& operator=(PointGFp&& other)
+ {
+ if(this != &other)
+ this->swap(other);
+ return (*this);
+ }
+
+ /**
+ * Construct a point from its affine coordinates
+ * Prefer EC_Group::point(x,y) for this operation.
+ * @param curve the base curve
+ * @param x affine x coordinate
+ * @param y affine y coordinate
+ */
+ PointGFp(const CurveGFp& curve, const BigInt& x, const BigInt& y);
+
+ /**
+ * EC2OSP - elliptic curve to octet string primitive
+ * @param format which format to encode using
+ */
+ std::vector<uint8_t> encode(PointGFp::Compression_Type format) const;
+
+ /**
+ * += Operator
+ * @param rhs the PointGFp to add to the local value
+ * @result resulting PointGFp
+ */
+ PointGFp& operator+=(const PointGFp& rhs);
+
+ /**
+ * -= Operator
+ * @param rhs the PointGFp to subtract from the local value
+ * @result resulting PointGFp
+ */
+ PointGFp& operator-=(const PointGFp& rhs);
+
+ /**
+ * *= Operator
+ * @param scalar the PointGFp to multiply with *this
+ * @result resulting PointGFp
+ */
+ PointGFp& operator*=(const BigInt& scalar);
+
+ /**
+ * Negate this point
+ * @return *this
+ */
+ PointGFp& negate()
+ {
+ if(!is_zero())
+ m_coord_y = m_curve.get_p() - m_coord_y;
+ return *this;
+ }
+
+ /**
+ * get affine x coordinate
+ * @result affine x coordinate
+ */
+ BigInt get_affine_x() const;
+
+ /**
+ * get affine y coordinate
+ * @result affine y coordinate
+ */
+ BigInt get_affine_y() const;
+
+ const BigInt& get_x() const { return m_coord_x; }
+ const BigInt& get_y() const { return m_coord_y; }
+ const BigInt& get_z() const { return m_coord_z; }
+
+ void swap_coords(BigInt& new_x, BigInt& new_y, BigInt& new_z)
+ {
+ m_coord_x.swap(new_x);
+ m_coord_y.swap(new_y);
+ m_coord_z.swap(new_z);
+ }
+
+ /**
+ * Force this point to affine coordinates
+ */
+ void force_affine();
+
+ /**
+ * Force all points on the list to affine coordinates
+ */
+ static void force_all_affine(std::vector<PointGFp>& points,
+ secure_vector<word>& ws);
+
+ bool is_affine() const;
+
+ /**
+ * Is this the point at infinity?
+ * @result true, if this point is at infinity, false otherwise.
+ */
+ bool is_zero() const { return m_coord_z.is_zero(); }
+
+ /**
+ * Checks whether the point is to be found on the underlying
+ * curve; used to prevent fault attacks.
+ * @return if the point is on the curve
+ */
+ bool on_the_curve() const;
+
+ /**
+ * swaps the states of *this and other, does not throw!
+ * @param other the object to swap values with
+ */
+ void swap(PointGFp& other);
+
+ /**
+ * Randomize the point representation
+ * The actual value (get_affine_x, get_affine_y) does not change
+ */
+ void randomize_repr(RandomNumberGenerator& rng);
+
+ /**
+ * Randomize the point representation
+ * The actual value (get_affine_x, get_affine_y) does not change
+ */
+ void randomize_repr(RandomNumberGenerator& rng, secure_vector<word>& ws);
+
+ /**
+ * Equality operator
+ */
+ bool operator==(const PointGFp& other) const;
+
+ /**
+ * Point addition
+ * @param other the point to add to *this
+ * @param workspace temp space, at least WORKSPACE_SIZE elements
+ */
+ void add(const PointGFp& other, std::vector<BigInt>& workspace)
+ {
+ BOTAN_ASSERT_NOMSG(m_curve == other.m_curve);
+
+ const size_t p_words = m_curve.get_p_words();
+
+ add(other.m_coord_x.data(), std::min(p_words, other.m_coord_x.size()),
+ other.m_coord_y.data(), std::min(p_words, other.m_coord_y.size()),
+ other.m_coord_z.data(), std::min(p_words, other.m_coord_z.size()),
+ workspace);
+ }
+
+ /**
+ * Point addition. Array version.
+ *
+ * @param x_words the words of the x coordinate of the other point
+ * @param x_size size of x_words
+ * @param y_words the words of the y coordinate of the other point
+ * @param y_size size of y_words
+ * @param z_words the words of the z coordinate of the other point
+ * @param z_size size of z_words
+ * @param workspace temp space, at least WORKSPACE_SIZE elements
+ */
+ void add(const word x_words[], size_t x_size,
+ const word y_words[], size_t y_size,
+ const word z_words[], size_t z_size,
+ std::vector<BigInt>& workspace);
+
+ /**
+ * Point addition - mixed J+A
+ * @param other affine point to add - assumed to be affine!
+ * @param workspace temp space, at least WORKSPACE_SIZE elements
+ */
+ void add_affine(const PointGFp& other, std::vector<BigInt>& 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_coord_x.data(), std::min(p_words, other.m_coord_x.size()),
+ other.m_coord_y.data(), std::min(p_words, other.m_coord_y.size()),
+ workspace);
+ }
+
+ /**
+ * Point addition - mixed J+A. Array version.
+ *
+ * @param x_words the words of the x coordinate of the other point
+ * @param x_size size of x_words
+ * @param y_words the words of the y coordinate of the other point
+ * @param y_size size of y_words
+ * @param workspace temp space, at least WORKSPACE_SIZE elements
+ */
+ void add_affine(const word x_words[], size_t x_size,
+ const word y_words[], size_t y_size,
+ std::vector<BigInt>& workspace);
+
+ /**
+ * Point doubling
+ * @param workspace temp space, at least WORKSPACE_SIZE elements
+ */
+ void mult2(std::vector<BigInt>& workspace);
+
+ /**
+ * Repeated point doubling
+ * @param i number of doublings to perform
+ * @param workspace temp space, at least WORKSPACE_SIZE elements
+ */
+ void mult2i(size_t i, std::vector<BigInt>& workspace);
+
+ /**
+ * Point addition
+ * @param other the point to add to *this
+ * @param workspace temp space, at least WORKSPACE_SIZE elements
+ * @return other plus *this
+ */
+ PointGFp plus(const PointGFp& other, std::vector<BigInt>& workspace) const
+ {
+ PointGFp x = (*this);
+ x.add(other, workspace);
+ return x;
+ }
+
+ /**
+ * Point doubling
+ * @param workspace temp space, at least WORKSPACE_SIZE elements
+ * @return *this doubled
+ */
+ PointGFp double_of(std::vector<BigInt>& workspace) const
+ {
+ PointGFp x = (*this);
+ x.mult2(workspace);
+ return x;
+ }
+
+ /**
+ * Return the zero (aka infinite) point associated with this curve
+ */
+ PointGFp zero() const { return PointGFp(m_curve); }
+
+ /**
+ * Return base curve of this point
+ * @result the curve over GF(p) of this point
+ *
+ * You should not need to use this
+ */
+ const CurveGFp& get_curve() const { return m_curve; }
+
+ private:
+ CurveGFp m_curve;
+ BigInt m_coord_x, m_coord_y, m_coord_z;
+ };
+
+/**
+* Point multiplication operator
+* @param scalar the scalar value
+* @param point the point value
+* @return scalar*point on the curve
+*/
+BOTAN_PUBLIC_API(2,0) PointGFp operator*(const BigInt& scalar, const PointGFp& point);
+
+/**
+* ECC point multiexponentiation - not constant time!
+* @param p1 a point
+* @param z1 a scalar
+* @param p2 a point
+* @param z2 a scalar
+* @result (p1 * z1 + p2 * z2)
+*/
+BOTAN_PUBLIC_API(2,0) PointGFp multi_exponentiate(
+ const PointGFp& p1, const BigInt& z1,
+ const PointGFp& p2, const BigInt& z2);
+
+// relational operators
+inline bool operator!=(const PointGFp& lhs, const PointGFp& rhs)
+ {
+ return !(rhs == lhs);
+ }
+
+// arithmetic operators
+inline PointGFp operator-(const PointGFp& lhs)
+ {
+ return PointGFp(lhs).negate();
+ }
+
+inline PointGFp operator+(const PointGFp& lhs, const PointGFp& rhs)
+ {
+ PointGFp tmp(lhs);
+ return tmp += rhs;
+ }
+
+inline PointGFp operator-(const PointGFp& lhs, const PointGFp& rhs)
+ {
+ PointGFp tmp(lhs);
+ return tmp -= rhs;
+ }
+
+inline PointGFp operator*(const PointGFp& point, const BigInt& scalar)
+ {
+ return scalar * point;
+ }
+
+// encoding and decoding
+inline secure_vector<uint8_t> BOTAN_DEPRECATED("Use PointGFp::encode")
+ EC2OSP(const PointGFp& point, uint8_t format)
+ {
+ std::vector<uint8_t> enc = point.encode(static_cast<PointGFp::Compression_Type>(format));
+ return secure_vector<uint8_t>(enc.begin(), enc.end());
+ }
+
+/**
+* Perform point decoding
+* Use EC_Group::OS2ECP instead
+*/
+PointGFp BOTAN_PUBLIC_API(2,0) OS2ECP(const uint8_t data[], size_t data_len,
+ const CurveGFp& curve);
+
+/**
+* Perform point decoding
+* Use EC_Group::OS2ECP instead
+*
+* @param data the encoded point
+* @param data_len length of data in bytes
+* @param curve_p the curve equation prime
+* @param curve_a the curve equation a parameter
+* @param curve_b the curve equation b parameter
+*/
+std::pair<BigInt, BigInt> BOTAN_UNSTABLE_API OS2ECP(const uint8_t data[], size_t data_len,
+ const BigInt& curve_p,
+ const BigInt& curve_a,
+ const BigInt& curve_b);
+
+template<typename Alloc>
+PointGFp OS2ECP(const std::vector<uint8_t, Alloc>& data, const CurveGFp& curve)
+ { return OS2ECP(data.data(), data.size(), curve); }
+
+class PointGFp_Var_Point_Precompute;
+
+/**
+* Deprecated API for point multiplication
+* Use EC_Group::blinded_base_point_multiply or EC_Group::blinded_var_point_multiply
+*/
+class BOTAN_PUBLIC_API(2,0) Blinded_Point_Multiply final
+ {
+ public:
+ Blinded_Point_Multiply(const PointGFp& base, const BigInt& order, size_t h = 0);
+
+ ~Blinded_Point_Multiply();
+
+ PointGFp BOTAN_DEPRECATED("Use alternative APIs") blinded_multiply(const BigInt& scalar, RandomNumberGenerator& rng);
+ private:
+ std::vector<BigInt> m_ws;
+ const BigInt& m_order;
+ std::unique_ptr<PointGFp_Var_Point_Precompute> m_point_mul;
+ };
+
+}
+
+namespace std {
+
+template<>
+inline void swap<Botan::PointGFp>(Botan::PointGFp& x, Botan::PointGFp& y)
+ { x.swap(y); }
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/pubkey/ec_group/point_mul.cpp b/comm/third_party/botan/src/lib/pubkey/ec_group/point_mul.cpp
new file mode 100644
index 0000000000..2dff959d7c
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/ec_group/point_mul.cpp
@@ -0,0 +1,431 @@
+/*
+* (C) 2015,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/point_mul.h>
+#include <botan/rng.h>
+#include <botan/reducer.h>
+#include <botan/internal/rounding.h>
+#include <botan/internal/ct_utils.h>
+#include <iostream>
+
+namespace Botan {
+
+namespace {
+
+size_t blinding_size(const BigInt& group_order)
+ {
+ return (group_order.bits() + 1) / 2;
+ }
+
+}
+
+PointGFp multi_exponentiate(const PointGFp& x, const BigInt& z1,
+ const PointGFp& y, const BigInt& z2)
+ {
+ PointGFp_Multi_Point_Precompute xy_mul(x, y);
+ return xy_mul.multi_exp(z1, z2);
+ }
+
+Blinded_Point_Multiply::Blinded_Point_Multiply(const PointGFp& base,
+ const BigInt& order,
+ size_t h) :
+ m_ws(PointGFp::WORKSPACE_SIZE),
+ m_order(order)
+ {
+ BOTAN_UNUSED(h);
+ Null_RNG null_rng;
+ m_point_mul.reset(new PointGFp_Var_Point_Precompute(base, null_rng, m_ws));
+ }
+
+Blinded_Point_Multiply::~Blinded_Point_Multiply()
+ {
+ /* for ~unique_ptr */
+ }
+
+PointGFp Blinded_Point_Multiply::blinded_multiply(const BigInt& scalar,
+ RandomNumberGenerator& rng)
+ {
+ return m_point_mul->mul(scalar, rng, m_order, m_ws);
+ }
+
+PointGFp_Base_Point_Precompute::PointGFp_Base_Point_Precompute(const PointGFp& base,
+ const Modular_Reducer& mod_order) :
+ m_base_point(base),
+ m_mod_order(mod_order),
+ m_p_words(base.get_curve().get_p().sig_words())
+ {
+ std::vector<BigInt> ws(PointGFp::WORKSPACE_SIZE);
+
+ const size_t p_bits = base.get_curve().get_p().bits();
+
+ /*
+ * Some of the curves (eg secp160k1) have an order slightly larger than
+ * the size of the prime modulus. In all cases they are at most 1 bit
+ * longer. The +1 compensates for this.
+ */
+ const size_t T_bits = round_up(p_bits + blinding_size(mod_order.get_modulus()) + 1, WINDOW_BITS) / WINDOW_BITS;
+
+ std::vector<PointGFp> T(WINDOW_SIZE*T_bits);
+
+ PointGFp g = base;
+ PointGFp g2, g4;
+
+ for(size_t i = 0; i != T_bits; i++)
+ {
+ g2 = g;
+ g2.mult2(ws);
+ g4 = g2;
+ g4.mult2(ws);
+
+ T[7*i+0] = g;
+ T[7*i+1] = std::move(g2);
+ T[7*i+2] = T[7*i+1].plus(T[7*i+0], ws); // g2+g
+ T[7*i+3] = g4;
+ T[7*i+4] = T[7*i+3].plus(T[7*i+0], ws); // g4+g
+ T[7*i+5] = T[7*i+3].plus(T[7*i+1], ws); // g4+g2
+ T[7*i+6] = T[7*i+3].plus(T[7*i+2], ws); // g4+g2+g
+
+ g.swap(g4);
+ g.mult2(ws);
+ }
+
+ PointGFp::force_all_affine(T, ws[0].get_word_vector());
+
+ 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);
+ p += m_p_words;
+ T[i].get_y().encode_words(p, m_p_words);
+ p += m_p_words;
+ }
+ }
+
+PointGFp PointGFp_Base_Point_Precompute::mul(const BigInt& k,
+ RandomNumberGenerator& rng,
+ const BigInt& group_order,
+ std::vector<BigInt>& ws) const
+ {
+ if(k.is_negative())
+ throw Invalid_Argument("PointGFp_Base_Point_Precompute scalar must be positive");
+
+ // Instead of reducing k mod group order should we alter the mask size??
+ BigInt scalar = m_mod_order.reduce(k);
+
+ if(rng.is_seeded())
+ {
+ // Choose a small mask m and use k' = k + m*order (Coron's 1st countermeasure)
+ const BigInt mask(rng, blinding_size(group_order));
+ scalar += group_order * mask;
+ }
+ else
+ {
+ /*
+ When we don't have an RNG we cannot do scalar blinding. Instead use the
+ same trick as OpenSSL and add one or two copies of the order to normalize
+ the length of the scalar at order.bits()+1. This at least ensures the loop
+ bound does not leak information about the high bits of the scalar.
+ */
+ scalar += group_order;
+ if(scalar.bits() == group_order.bits())
+ scalar += group_order;
+ BOTAN_DEBUG_ASSERT(scalar.bits() == group_order.bits() + 1);
+ }
+
+ const size_t windows = round_up(scalar.bits(), WINDOW_BITS) / WINDOW_BITS;
+
+ const size_t elem_size = 2*m_p_words;
+
+ BOTAN_ASSERT(windows <= m_W.size() / (3*elem_size),
+ "Precomputed sufficient values for scalar mult");
+
+ PointGFp R = m_base_point.zero();
+
+ if(ws.size() < PointGFp::WORKSPACE_SIZE)
+ ws.resize(PointGFp::WORKSPACE_SIZE);
+
+ // the precomputed multiples are not secret so use std::vector
+ std::vector<word> Wt(elem_size);
+
+ 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 word w = scalar.get_substring(WINDOW_BITS*window, WINDOW_BITS);
+
+ const auto w_is_1 = CT::Mask<word>::is_equal(w, 1);
+ const auto w_is_2 = CT::Mask<word>::is_equal(w, 2);
+ const auto w_is_3 = CT::Mask<word>::is_equal(w, 3);
+ const auto w_is_4 = CT::Mask<word>::is_equal(w, 4);
+ const auto w_is_5 = CT::Mask<word>::is_equal(w, 5);
+ const auto w_is_6 = CT::Mask<word>::is_equal(w, 6);
+ const auto w_is_7 = CT::Mask<word>::is_equal(w, 7);
+
+ for(size_t j = 0; j != elem_size; ++j)
+ {
+ const word w1 = w_is_1.if_set_return(m_W[base_addr + 0*elem_size + j]);
+ const word w2 = w_is_2.if_set_return(m_W[base_addr + 1*elem_size + j]);
+ const word w3 = w_is_3.if_set_return(m_W[base_addr + 2*elem_size + j]);
+ const word w4 = w_is_4.if_set_return(m_W[base_addr + 3*elem_size + j]);
+ const word w5 = w_is_5.if_set_return(m_W[base_addr + 4*elem_size + j]);
+ const word w6 = w_is_6.if_set_return(m_W[base_addr + 5*elem_size + j]);
+ const word w7 = w_is_7.if_set_return(m_W[base_addr + 6*elem_size + j]);
+
+ 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);
+
+ if(i == 0 && rng.is_seeded())
+ {
+ /*
+ * Since we start with the top bit of the exponent we know the
+ * first window must have a non-zero element, and thus R is
+ * now a point other than the point at infinity.
+ */
+ BOTAN_DEBUG_ASSERT(w != 0);
+ R.randomize_repr(rng, ws[0].get_word_vector());
+ }
+ }
+
+ BOTAN_DEBUG_ASSERT(R.on_the_curve());
+
+ return R;
+ }
+
+PointGFp_Var_Point_Precompute::PointGFp_Var_Point_Precompute(const PointGFp& point,
+ RandomNumberGenerator& rng,
+ std::vector<BigInt>& ws) :
+ m_curve(point.get_curve()),
+ m_p_words(m_curve.get_p().sig_words()),
+ m_window_bits(4)
+ {
+ if(ws.size() < PointGFp::WORKSPACE_SIZE)
+ ws.resize(PointGFp::WORKSPACE_SIZE);
+
+ std::vector<PointGFp> U(static_cast<size_t>(1) << m_window_bits);
+ U[0] = point.zero();
+ U[1] = point;
+
+ for(size_t i = 2; i < U.size(); i += 2)
+ {
+ U[i] = U[i/2].double_of(ws);
+ U[i+1] = U[i].plus(point, ws);
+ }
+
+ // Hack to handle Blinded_Point_Multiply
+ if(rng.is_seeded())
+ {
+ BigInt& mask = ws[0];
+ BigInt& mask2 = ws[1];
+ BigInt& mask3 = ws[2];
+ BigInt& new_x = ws[3];
+ BigInt& new_y = ws[4];
+ BigInt& new_z = ws[5];
+ secure_vector<word>& tmp = ws[6].get_word_vector();
+
+ const CurveGFp& curve = U[0].get_curve();
+
+ const size_t p_bits = curve.get_p().bits();
+
+ // Skipping zero point since it can't be randomized
+ for(size_t i = 1; i != U.size(); ++i)
+ {
+ mask.randomize(rng, p_bits - 1, false);
+ // Easy way of ensuring mask != 0
+ mask.set_bit(0);
+
+ curve.sqr(mask2, mask, tmp);
+ curve.mul(mask3, mask, mask2, tmp);
+
+ curve.mul(new_x, U[i].get_x(), mask2, tmp);
+ curve.mul(new_y, U[i].get_y(), mask3, tmp);
+ curve.mul(new_z, U[i].get_z(), mask, tmp);
+
+ U[i].swap_coords(new_x, new_y, new_z);
+ }
+ }
+
+ 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);
+ p += 3*m_p_words;
+ }
+ }
+
+PointGFp PointGFp_Var_Point_Precompute::mul(const BigInt& k,
+ RandomNumberGenerator& rng,
+ const BigInt& group_order,
+ std::vector<BigInt>& ws) const
+ {
+ if(k.is_negative())
+ throw Invalid_Argument("PointGFp_Var_Point_Precompute scalar must be positive");
+ if(ws.size() < PointGFp::WORKSPACE_SIZE)
+ ws.resize(PointGFp::WORKSPACE_SIZE);
+
+ // Choose a small mask m and use k' = k + m*order (Coron's 1st countermeasure)
+ const BigInt mask(rng, blinding_size(group_order), false);
+ const BigInt scalar = k + group_order * mask;
+
+ const size_t elem_size = 3*m_p_words;
+ const size_t window_elems = (1ULL << m_window_bits);
+
+ size_t windows = round_up(scalar.bits(), m_window_bits) / m_window_bits;
+ PointGFp R(m_curve);
+ secure_vector<word> e(elem_size);
+
+ if(windows > 0)
+ {
+ windows--;
+
+ const uint32_t w = scalar.get_substring(windows*m_window_bits, m_window_bits);
+
+ clear_mem(e.data(), e.size());
+ for(size_t i = 1; i != window_elems; ++i)
+ {
+ const auto wmask = CT::Mask<word>::is_equal(w, i);
+
+ for(size_t j = 0; j != elem_size; ++j)
+ {
+ e[j] |= wmask.if_set_return(m_T[i * elem_size + j]);
+ }
+ }
+
+ R.add(&e[0], 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
+ is zero, and we cannot effectively randomize the point
+ representation of the zero point.
+ */
+ R.randomize_repr(rng, ws[0].get_word_vector());
+ }
+
+ while(windows)
+ {
+ R.mult2i(m_window_bits, ws);
+
+ const uint32_t w = scalar.get_substring((windows-1)*m_window_bits, m_window_bits);
+
+ clear_mem(e.data(), e.size());
+ for(size_t i = 1; i != window_elems; ++i)
+ {
+ const auto wmask = CT::Mask<word>::is_equal(w, i);
+
+ for(size_t j = 0; j != elem_size; ++j)
+ {
+ e[j] |= wmask.if_set_return(m_T[i * elem_size + j]);
+ }
+ }
+
+ R.add(&e[0], m_p_words, &e[m_p_words], m_p_words, &e[2*m_p_words], m_p_words, ws);
+
+ windows--;
+ }
+
+ BOTAN_DEBUG_ASSERT(R.on_the_curve());
+
+ return R;
+ }
+
+
+PointGFp_Multi_Point_Precompute::PointGFp_Multi_Point_Precompute(const PointGFp& x,
+ const PointGFp& y)
+ {
+ std::vector<BigInt> ws(PointGFp::WORKSPACE_SIZE);
+
+ PointGFp x2 = x;
+ x2.mult2(ws);
+
+ const PointGFp x3(x2.plus(x, ws));
+
+ PointGFp y2 = y;
+ y2.mult2(ws);
+
+ const PointGFp y3(y2.plus(y, ws));
+
+ m_M.reserve(15);
+
+ m_M.push_back(x);
+ m_M.push_back(x2);
+ m_M.push_back(x3);
+
+ m_M.push_back(y);
+ m_M.push_back(y.plus(x, ws));
+ m_M.push_back(y.plus(x2, ws));
+ m_M.push_back(y.plus(x3, ws));
+
+ m_M.push_back(y2);
+ m_M.push_back(y2.plus(x, ws));
+ m_M.push_back(y2.plus(x2, ws));
+ m_M.push_back(y2.plus(x3, ws));
+
+ m_M.push_back(y3);
+ m_M.push_back(y3.plus(x, ws));
+ m_M.push_back(y3.plus(x2, ws));
+ m_M.push_back(y3.plus(x3, ws));
+
+ bool no_infinity = true;
+ for(auto& pt : m_M)
+ {
+ if(pt.is_zero())
+ no_infinity = false;
+ }
+
+ if(no_infinity)
+ {
+ PointGFp::force_all_affine(m_M, ws[0].get_word_vector());
+ }
+
+ m_no_infinity = no_infinity;
+ }
+
+PointGFp PointGFp_Multi_Point_Precompute::multi_exp(const BigInt& z1,
+ const BigInt& z2) const
+ {
+ std::vector<BigInt> ws(PointGFp::WORKSPACE_SIZE);
+
+ const size_t z_bits = round_up(std::max(z1.bits(), z2.bits()), 2);
+
+ PointGFp H = m_M[0].zero();
+
+ for(size_t i = 0; i != z_bits; i += 2)
+ {
+ if(i > 0)
+ {
+ H.mult2i(2, ws);
+ }
+
+ const uint32_t z1_b = z1.get_substring(z_bits - i - 2, 2);
+ const uint32_t z2_b = z2.get_substring(z_bits - i - 2, 2);
+
+ const uint32_t z12 = (4*z2_b) + z1_b;
+
+ // This function is not intended to be const time
+ if(z12)
+ {
+ if(m_no_infinity)
+ H.add_affine(m_M[z12-1], ws);
+ else
+ H.add(m_M[z12-1], ws);
+ }
+ }
+
+ if(z1.is_negative() != z2.is_negative())
+ H.negate();
+
+ return H;
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/pubkey/ec_group/point_mul.h b/comm/third_party/botan/src/lib/pubkey/ec_group/point_mul.h
new file mode 100644
index 0000000000..0956204289
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/ec_group/point_mul.h
@@ -0,0 +1,85 @@
+/*
+* (C) 2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_POINT_MUL_H_
+#define BOTAN_POINT_MUL_H_
+
+#include <botan/point_gfp.h>
+
+namespace Botan {
+
+class Modular_Reducer;
+
+class PointGFp_Base_Point_Precompute final
+ {
+ public:
+ PointGFp_Base_Point_Precompute(const PointGFp& base_point,
+ const Modular_Reducer& mod_order);
+
+ PointGFp mul(const BigInt& k,
+ RandomNumberGenerator& rng,
+ const BigInt& group_order,
+ std::vector<BigInt>& ws) const;
+ private:
+ const PointGFp& m_base_point;
+ const Modular_Reducer& m_mod_order;
+
+ enum { WINDOW_BITS = 3 };
+ enum { WINDOW_SIZE = (1 << WINDOW_BITS) - 1 };
+
+ const size_t m_p_words;
+
+ /*
+ * This is a table of T_size * 3*p_word words
+ */
+ std::vector<word> m_W;
+ };
+
+class PointGFp_Var_Point_Precompute final
+ {
+ public:
+ PointGFp_Var_Point_Precompute(const PointGFp& point,
+ RandomNumberGenerator& rng,
+ std::vector<BigInt>& ws);
+
+ PointGFp mul(const BigInt& k,
+ RandomNumberGenerator& rng,
+ const BigInt& group_order,
+ std::vector<BigInt>& ws) const;
+ private:
+ 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
+ * Kept in locked vector since the base point might be sensitive
+ * (normally isn't in most protocols but hard to say anything
+ * categorically.)
+ */
+ secure_vector<word> m_T;
+ };
+
+class PointGFp_Multi_Point_Precompute final
+ {
+ public:
+ PointGFp_Multi_Point_Precompute(const PointGFp& g1,
+ const PointGFp& g2);
+
+ /*
+ * Return (g1*k1 + g2*k2)
+ * Not constant time, intended to use with public inputs
+ */
+ PointGFp multi_exp(const BigInt& k1,
+ const BigInt& k2) const;
+ private:
+ std::vector<PointGFp> m_M;
+ bool m_no_infinity;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/pubkey/ecc_key/ecc_key.cpp b/comm/third_party/botan/src/lib/pubkey/ecc_key/ecc_key.cpp
new file mode 100644
index 0000000000..5a97e7a501
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/ecc_key/ecc_key.cpp
@@ -0,0 +1,205 @@
+/*
+* ECC Key implemenation
+* (C) 2007 Manuel Hartl, FlexSecure GmbH
+* Falko Strenzke, FlexSecure GmbH
+* 2008-2010 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/ecc_key.h>
+#include <botan/numthry.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
+#include <botan/secmem.h>
+#include <botan/point_gfp.h>
+#include <botan/workfactor.h>
+
+namespace Botan {
+
+size_t EC_PublicKey::key_length() const
+ {
+ return domain().get_p_bits();
+ }
+
+size_t EC_PublicKey::estimated_strength() const
+ {
+ return ecp_work_factor(key_length());
+ }
+
+EC_PublicKey::EC_PublicKey(const EC_Group& dom_par,
+ const PointGFp& pub_point) :
+ m_domain_params(dom_par), m_public_key(pub_point)
+ {
+ if (!dom_par.get_curve_oid().empty())
+ m_domain_encoding = EC_DOMPAR_ENC_OID;
+ else
+ m_domain_encoding = EC_DOMPAR_ENC_EXPLICIT;
+
+#if 0
+ if(domain().get_curve() != public_point().get_curve())
+ throw Invalid_Argument("EC_PublicKey: curve mismatch in constructor");
+#endif
+ }
+
+EC_PublicKey::EC_PublicKey(const AlgorithmIdentifier& alg_id,
+ const std::vector<uint8_t>& key_bits) :
+ m_domain_params{EC_Group(alg_id.get_parameters())},
+ m_public_key{domain().OS2ECP(key_bits)}
+ {
+ if (!domain().get_curve_oid().empty())
+ m_domain_encoding = EC_DOMPAR_ENC_OID;
+ else
+ m_domain_encoding = EC_DOMPAR_ENC_EXPLICIT;
+ }
+
+bool EC_PublicKey::check_key(RandomNumberGenerator& rng,
+ bool) const
+ {
+ return m_domain_params.verify_group(rng) &&
+ m_domain_params.verify_public_element(public_point());
+ }
+
+
+AlgorithmIdentifier EC_PublicKey::algorithm_identifier() const
+ {
+ return AlgorithmIdentifier(get_oid(), DER_domain());
+ }
+
+std::vector<uint8_t> EC_PublicKey::public_key_bits() const
+ {
+ return public_point().encode(point_encoding());
+ }
+
+void EC_PublicKey::set_point_encoding(PointGFp::Compression_Type enc)
+ {
+ if(enc != PointGFp::COMPRESSED &&
+ enc != PointGFp::UNCOMPRESSED &&
+ enc != PointGFp::HYBRID)
+ throw Invalid_Argument("Invalid point encoding for EC_PublicKey");
+
+ m_point_encoding = enc;
+ }
+
+void EC_PublicKey::set_parameter_encoding(EC_Group_Encoding form)
+ {
+ if(form != EC_DOMPAR_ENC_EXPLICIT &&
+ form != EC_DOMPAR_ENC_IMPLICITCA &&
+ form != EC_DOMPAR_ENC_OID)
+ throw Invalid_Argument("Invalid encoding form for EC-key object specified");
+
+ if((form == EC_DOMPAR_ENC_OID) && (m_domain_params.get_curve_oid().empty()))
+ throw Invalid_Argument("Invalid encoding form OID specified for "
+ "EC-key object whose corresponding domain "
+ "parameters are without oid");
+
+ m_domain_encoding = form;
+ }
+
+const BigInt& EC_PrivateKey::private_value() const
+ {
+ if(m_private_key == 0)
+ throw Invalid_State("EC_PrivateKey::private_value - uninitialized");
+
+ return m_private_key;
+ }
+
+/**
+* EC_PrivateKey constructor
+*/
+EC_PrivateKey::EC_PrivateKey(RandomNumberGenerator& rng,
+ const EC_Group& ec_group,
+ const BigInt& x,
+ bool with_modular_inverse)
+ {
+ m_domain_params = ec_group;
+ if (!ec_group.get_curve_oid().empty())
+ m_domain_encoding = EC_DOMPAR_ENC_OID;
+ else
+ m_domain_encoding = EC_DOMPAR_ENC_EXPLICIT;
+
+ if(x == 0)
+ {
+ m_private_key = ec_group.random_scalar(rng);
+ }
+ else
+ {
+ m_private_key = x;
+ }
+
+ std::vector<BigInt> ws;
+
+ if(with_modular_inverse)
+ {
+ // ECKCDSA
+ m_public_key = domain().blinded_base_point_multiply(
+ m_domain_params.inverse_mod_order(m_private_key), rng, ws);
+ }
+ else
+ {
+ m_public_key = domain().blinded_base_point_multiply(m_private_key, rng, ws);
+ }
+
+ BOTAN_ASSERT(m_public_key.on_the_curve(),
+ "Generated public key point was on the curve");
+ }
+
+secure_vector<uint8_t> EC_PrivateKey::private_key_bits() const
+ {
+ return DER_Encoder()
+ .start_cons(SEQUENCE)
+ .encode(static_cast<size_t>(1))
+ .encode(BigInt::encode_1363(m_private_key, m_private_key.bytes()), OCTET_STRING)
+ .start_cons(ASN1_Tag(1), PRIVATE)
+ .encode(m_public_key.encode(PointGFp::Compression_Type::UNCOMPRESSED), BIT_STRING)
+ .end_cons()
+ .end_cons()
+ .get_contents();
+ }
+
+EC_PrivateKey::EC_PrivateKey(const AlgorithmIdentifier& alg_id,
+ const secure_vector<uint8_t>& key_bits,
+ bool with_modular_inverse)
+ {
+ m_domain_params = EC_Group(alg_id.get_parameters());
+ m_domain_encoding = EC_DOMPAR_ENC_EXPLICIT;
+
+ if (!domain().get_curve_oid().empty())
+ m_domain_encoding = EC_DOMPAR_ENC_OID;
+ else
+ m_domain_encoding = EC_DOMPAR_ENC_EXPLICIT;
+
+ OID key_parameters;
+ secure_vector<uint8_t> public_key_bits;
+
+ BER_Decoder(key_bits)
+ .start_cons(SEQUENCE)
+ .decode_and_check<size_t>(1, "Unknown version code for ECC key")
+ .decode_octet_string_bigint(m_private_key)
+ .decode_optional(key_parameters, ASN1_Tag(0), PRIVATE)
+ .decode_optional_string(public_key_bits, BIT_STRING, 1, PRIVATE)
+ .end_cons();
+
+ if(public_key_bits.empty())
+ {
+ if(with_modular_inverse)
+ {
+ // ECKCDSA
+ m_public_key = domain().get_base_point() * m_domain_params.inverse_mod_order(m_private_key);
+ }
+ else
+ {
+ m_public_key = domain().get_base_point() * m_private_key;
+ }
+
+ BOTAN_ASSERT(m_public_key.on_the_curve(),
+ "Public point derived from loaded key was on the curve");
+ }
+ else
+ {
+ m_public_key = domain().OS2ECP(public_key_bits);
+ // OS2ECP verifies that the point is on the curve
+ }
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/pubkey/ecc_key/ecc_key.h b/comm/third_party/botan/src/lib/pubkey/ecc_key/ecc_key.h
new file mode 100644
index 0000000000..ec2b5f9be3
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/ecc_key/ecc_key.h
@@ -0,0 +1,172 @@
+/*
+* ECDSA
+* (C) 2007 Falko Strenzke, FlexSecure GmbH
+* Manuel Hartl, FlexSecure GmbH
+* (C) 2008-2010 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_ECC_PUBLIC_KEY_BASE_H_
+#define BOTAN_ECC_PUBLIC_KEY_BASE_H_
+
+#include <botan/ec_group.h>
+#include <botan/pk_keys.h>
+
+namespace Botan {
+
+/**
+* This class represents abstract ECC public keys. When encoding a key
+* via an encoder that can be accessed via the corresponding member
+* functions, the key will decide upon its internally stored encoding
+* information whether to encode itself with or without domain
+* parameters, or using the domain parameter oid. Furthermore, a public
+* key without domain parameters can be decoded. In that case, it
+* cannot be used for verification until its domain parameters are set
+* by calling the corresponding member function.
+*/
+class BOTAN_PUBLIC_API(2,0) EC_PublicKey : public virtual Public_Key
+ {
+ public:
+ /**
+ * Create a public key.
+ * @param dom_par EC domain parameters
+ * @param pub_point public point on the curve
+ */
+ EC_PublicKey(const EC_Group& dom_par,
+ const PointGFp& pub_point);
+
+ /**
+ * Load a public key.
+ * @param alg_id the X.509 algorithm identifier
+ * @param key_bits DER encoded public key bits
+ */
+ EC_PublicKey(const AlgorithmIdentifier& alg_id,
+ const std::vector<uint8_t>& key_bits);
+
+ EC_PublicKey(const EC_PublicKey& other) = default;
+ EC_PublicKey& operator=(const EC_PublicKey& other) = default;
+ virtual ~EC_PublicKey() = default;
+
+ /**
+ * Get the public point of this key.
+ * @throw Invalid_State is thrown if the
+ * domain parameters of this point are not set
+ * @result the public point of this key
+ */
+ const PointGFp& public_point() const { return m_public_key; }
+
+ AlgorithmIdentifier algorithm_identifier() const override;
+
+ std::vector<uint8_t> public_key_bits() const override;
+
+ bool check_key(RandomNumberGenerator& rng,
+ bool strong) const override;
+
+ /**
+ * Get the domain parameters of this key.
+ * @throw Invalid_State is thrown if the
+ * domain parameters of this point are not set
+ * @result the domain parameters of this key
+ */
+ const EC_Group& domain() const { return m_domain_params; }
+
+ /**
+ * Set the domain parameter encoding to be used when encoding this key.
+ * @param enc the encoding to use
+ */
+ void set_parameter_encoding(EC_Group_Encoding enc);
+
+ /**
+ * Set the point encoding method to be used when encoding this key.
+ * @param enc the encoding to use
+ */
+ void set_point_encoding(PointGFp::Compression_Type enc);
+
+ /**
+ * Return the DER encoding of this keys domain in whatever format
+ * is preset for this particular key
+ */
+ std::vector<uint8_t> DER_domain() const
+ { return domain().DER_encode(domain_format()); }
+
+ /**
+ * Get the domain parameter encoding to be used when encoding this key.
+ * @result the encoding to use
+ */
+ EC_Group_Encoding domain_format() const
+ { return m_domain_encoding; }
+
+ /**
+ * Get the point encoding method to be used when encoding this key.
+ * @result the encoding to use
+ */
+ PointGFp::Compression_Type point_encoding() const
+ { return m_point_encoding; }
+
+ size_t key_length() const override;
+ size_t estimated_strength() const override;
+
+ protected:
+ EC_PublicKey() : m_domain_params{}, m_public_key{}, m_domain_encoding(EC_DOMPAR_ENC_EXPLICIT)
+ {}
+
+ EC_Group m_domain_params;
+ PointGFp m_public_key;
+ EC_Group_Encoding m_domain_encoding;
+ PointGFp::Compression_Type m_point_encoding = PointGFp::UNCOMPRESSED;
+ };
+
+/**
+* This abstract class represents ECC private keys
+*/
+class BOTAN_PUBLIC_API(2,0) EC_PrivateKey : public virtual EC_PublicKey,
+ public virtual Private_Key
+ {
+ public:
+ /*
+ * If x=0, creates a new private key in the domain
+ * using the given rng. If with_modular_inverse is set,
+ * the public key will be calculated by 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,
+ const EC_Group& domain,
+ const BigInt& x,
+ bool with_modular_inverse=false);
+
+ /*
+ * Creates a new private key object from the
+ * ECPrivateKey structure given in key_bits.
+ * If with_modular_inverse is set,
+ * the public key will be calculated by 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(const AlgorithmIdentifier& alg_id,
+ const secure_vector<uint8_t>& key_bits,
+ bool with_modular_inverse=false);
+
+ secure_vector<uint8_t> private_key_bits() const override;
+
+ /**
+ * Get the private key value of this key object.
+ * @result the private key value of this key object
+ */
+ const BigInt& private_value() const;
+
+ EC_PrivateKey(const EC_PrivateKey& other) = default;
+ EC_PrivateKey& operator=(const EC_PrivateKey& other) = default;
+ ~EC_PrivateKey() = default;
+ protected:
+ EC_PrivateKey() = default;
+
+ BigInt m_private_key;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/pubkey/ecc_key/info.txt b/comm/third_party/botan/src/lib/pubkey/ecc_key/info.txt
new file mode 100644
index 0000000000..32d05f2f90
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/ecc_key/info.txt
@@ -0,0 +1,11 @@
+<defines>
+ECC_PUBLIC_KEY_CRYPTO -> 20131128
+ECC_KEY -> 20190801
+</defines>
+
+<requires>
+asn1
+bigint
+ec_group
+numbertheory
+</requires>
diff --git a/comm/third_party/botan/src/lib/pubkey/ecdh/ecdh.cpp b/comm/third_party/botan/src/lib/pubkey/ecdh/ecdh.cpp
new file mode 100644
index 0000000000..e7e49a74fd
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/ecdh/ecdh.cpp
@@ -0,0 +1,87 @@
+/*
+* ECDH implemenation
+* (C) 2007 Manuel Hartl, FlexSecure GmbH
+* 2007 Falko Strenzke, FlexSecure GmbH
+* 2008-2010 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/ecdh.h>
+#include <botan/numthry.h>
+#include <botan/internal/pk_ops_impl.h>
+
+#if defined(BOTAN_HAS_OPENSSL)
+ #include <botan/internal/openssl.h>
+#endif
+
+namespace Botan {
+
+namespace {
+
+/**
+* ECDH operation
+*/
+class ECDH_KA_Operation final : public PK_Ops::Key_Agreement_with_KDF
+ {
+ public:
+
+ ECDH_KA_Operation(const ECDH_PrivateKey& key, const std::string& kdf, RandomNumberGenerator& rng) :
+ PK_Ops::Key_Agreement_with_KDF(kdf),
+ m_group(key.domain()),
+ m_rng(rng)
+ {
+ m_l_times_priv = m_group.inverse_mod_order(m_group.get_cofactor()) * key.private_value();
+ }
+
+ size_t agreed_value_size() const override { return m_group.get_p_bytes(); }
+
+ secure_vector<uint8_t> raw_agree(const uint8_t w[], size_t w_len) override
+ {
+ PointGFp input_point = m_group.get_cofactor() * m_group.OS2ECP(w, w_len);
+ input_point.randomize_repr(m_rng);
+
+ const PointGFp S = m_group.blinded_var_point_multiply(
+ input_point, m_l_times_priv, m_rng, m_ws);
+
+ if(S.on_the_curve() == false)
+ throw Internal_Error("ECDH agreed value was not on the curve");
+ return BigInt::encode_1363(S.get_affine_x(), m_group.get_p_bytes());
+ }
+ private:
+ const EC_Group m_group;
+ BigInt m_l_times_priv;
+ RandomNumberGenerator& m_rng;
+ std::vector<BigInt> m_ws;
+ };
+
+}
+
+std::unique_ptr<PK_Ops::Key_Agreement>
+ECDH_PrivateKey::create_key_agreement_op(RandomNumberGenerator& rng,
+ const std::string& params,
+ const std::string& provider) const
+ {
+#if defined(BOTAN_HAS_OPENSSL)
+ if(provider == "openssl" || provider.empty())
+ {
+ try
+ {
+ return make_openssl_ecdh_ka_op(*this, params);
+ }
+ catch(Lookup_Error&)
+ {
+ if(provider == "openssl")
+ throw;
+ }
+ }
+#endif
+
+ if(provider == "base" || provider.empty())
+ return std::unique_ptr<PK_Ops::Key_Agreement>(new ECDH_KA_Operation(*this, params, rng));
+
+ throw Provider_Not_Found(algo_name(), provider);
+ }
+
+
+}
diff --git a/comm/third_party/botan/src/lib/pubkey/ecdh/ecdh.h b/comm/third_party/botan/src/lib/pubkey/ecdh/ecdh.h
new file mode 100644
index 0000000000..f88955ac40
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/ecdh/ecdh.h
@@ -0,0 +1,106 @@
+/*
+* ECDH
+* (C) 2007 Falko Strenzke, FlexSecure GmbH
+* Manuel Hartl, FlexSecure GmbH
+* (C) 2008-2010 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_ECDH_KEY_H_
+#define BOTAN_ECDH_KEY_H_
+
+#include <botan/ecc_key.h>
+
+namespace Botan {
+
+/**
+* This class represents ECDH Public Keys.
+*/
+class BOTAN_PUBLIC_API(2,0) ECDH_PublicKey : public virtual EC_PublicKey
+ {
+ public:
+ /**
+ * Create an ECDH public key.
+ * @param alg_id algorithm identifier
+ * @param key_bits DER encoded public key bits
+ */
+ ECDH_PublicKey(const AlgorithmIdentifier& alg_id,
+ const std::vector<uint8_t>& key_bits) :
+ EC_PublicKey(alg_id, key_bits) {}
+
+ /**
+ * Construct a public key from a given public point.
+ * @param dom_par the domain parameters associated with this key
+ * @param public_point the public point defining this key
+ */
+ ECDH_PublicKey(const EC_Group& dom_par,
+ const PointGFp& public_point) :
+ EC_PublicKey(dom_par, public_point) {}
+
+ /**
+ * Get this keys algorithm name.
+ * @return this keys algorithm name
+ */
+ std::string algo_name() const override { return "ECDH"; }
+
+ /**
+ * @return public point value
+ */
+ std::vector<uint8_t> public_value() const
+ { return public_point().encode(PointGFp::UNCOMPRESSED); }
+
+ /**
+ * @return public point value
+ */
+ std::vector<uint8_t> public_value(PointGFp::Compression_Type format) const
+ { return public_point().encode(format); }
+
+ protected:
+ ECDH_PublicKey() = default;
+ };
+
+/**
+* This class represents ECDH Private Keys.
+*/
+class BOTAN_PUBLIC_API(2,0) ECDH_PrivateKey final : public ECDH_PublicKey,
+ public EC_PrivateKey,
+ public PK_Key_Agreement_Key
+ {
+ public:
+
+ /**
+ * Load a private key.
+ * @param alg_id the X.509 algorithm identifier
+ * @param key_bits ECPrivateKey bits
+ */
+ ECDH_PrivateKey(const AlgorithmIdentifier& alg_id,
+ const secure_vector<uint8_t>& key_bits) :
+ EC_PrivateKey(alg_id, key_bits) {}
+
+ /**
+ * Generate a new private key
+ * @param rng a random number generator
+ * @param domain parameters to used for this key
+ * @param x the private key; if zero, a new random key is generated
+ */
+ ECDH_PrivateKey(RandomNumberGenerator& rng,
+ const EC_Group& domain,
+ const BigInt& x = 0) :
+ EC_PrivateKey(rng, domain, x) {}
+
+ std::vector<uint8_t> public_value() const override
+ { return ECDH_PublicKey::public_value(PointGFp::UNCOMPRESSED); }
+
+ std::vector<uint8_t> public_value(PointGFp::Compression_Type type) const
+ { return ECDH_PublicKey::public_value(type); }
+
+ std::unique_ptr<PK_Ops::Key_Agreement>
+ create_key_agreement_op(RandomNumberGenerator& rng,
+ const std::string& params,
+ const std::string& provider) const override;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/pubkey/ecdh/info.txt b/comm/third_party/botan/src/lib/pubkey/ecdh/info.txt
new file mode 100644
index 0000000000..11ca921dab
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/ecdh/info.txt
@@ -0,0 +1,10 @@
+<defines>
+ECDH -> 20131128
+</defines>
+
+<requires>
+asn1
+ec_group
+ecc_key
+numbertheory
+</requires>
diff --git a/comm/third_party/botan/src/lib/pubkey/ecdsa/ecdsa.cpp b/comm/third_party/botan/src/lib/pubkey/ecdsa/ecdsa.cpp
new file mode 100644
index 0000000000..ebe9268cc9
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/ecdsa/ecdsa.cpp
@@ -0,0 +1,308 @@
+/*
+* ECDSA implemenation
+* (C) 2007 Manuel Hartl, FlexSecure GmbH
+* 2007 Falko Strenzke, FlexSecure GmbH
+* 2008-2010,2015,2016,2018 Jack Lloyd
+* 2016 René Korthaus
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/ecdsa.h>
+#include <botan/internal/pk_ops_impl.h>
+#include <botan/internal/point_mul.h>
+#include <botan/keypair.h>
+#include <botan/reducer.h>
+#include <botan/emsa.h>
+
+#if defined(BOTAN_HAS_RFC6979_GENERATOR)
+ #include <botan/rfc6979.h>
+#endif
+
+#if defined(BOTAN_HAS_OPENSSL)
+ #include <botan/internal/openssl.h>
+#endif
+
+namespace Botan {
+
+namespace {
+
+PointGFp recover_ecdsa_public_key(const EC_Group& group,
+ const std::vector<uint8_t>& msg,
+ const BigInt& r,
+ const BigInt& s,
+ uint8_t v)
+ {
+ if(group.get_cofactor() != 1)
+ throw Invalid_Argument("ECDSA public key recovery only supported for prime order groups");
+
+ if(v > 4)
+ throw Invalid_Argument("Unexpected v param for ECDSA public key recovery");
+
+ const uint8_t y_odd = v % 2;
+ const uint8_t add_order = v >> 1;
+
+ const BigInt& group_order = group.get_order();
+ const size_t p_bytes = group.get_p_bytes();
+
+ try
+ {
+ const BigInt e(msg.data(), msg.size(), group.get_order_bits());
+ const BigInt r_inv = group.inverse_mod_order(r);
+
+ BigInt x = r + add_order*group_order;
+
+ std::vector<uint8_t> X(p_bytes + 1);
+
+ X[0] = 0x02 | y_odd;
+ BigInt::encode_1363(&X[1], p_bytes, x);
+
+ const PointGFp R = group.OS2ECP(X);
+
+ if((R*group_order).is_zero() == false)
+ throw Decoding_Error("Unable to recover ECDSA public key");
+
+ // Compute r_inv * (s*R - eG)
+ PointGFp_Multi_Point_Precompute RG_mul(R, group.get_base_point());
+ const BigInt ne = group.mod_order(group_order - e);
+ return r_inv * RG_mul.multi_exp(s, ne);
+ }
+ catch(...)
+ {
+ // continue on and throw
+ }
+
+ throw Decoding_Error("Failed to recover ECDSA public key from signature/msg pair");
+ }
+
+}
+
+ECDSA_PublicKey::ECDSA_PublicKey(const EC_Group& group,
+ const std::vector<uint8_t>& msg,
+ const BigInt& r,
+ const BigInt& s,
+ uint8_t v) :
+ EC_PublicKey(group, recover_ecdsa_public_key(group, msg, r, s, v)) {}
+
+
+uint8_t ECDSA_PublicKey::recovery_param(const std::vector<uint8_t>& msg,
+ const BigInt& r,
+ const BigInt& s) const
+ {
+ for(uint8_t v = 0; v != 4; ++v)
+ {
+ try
+ {
+ PointGFp R = recover_ecdsa_public_key(this->domain(), msg, r, s, v);
+
+ if(R == this->public_point())
+ {
+ return v;
+ }
+ }
+ catch(Decoding_Error&)
+ {
+ // try the next v
+ }
+ }
+
+ throw Internal_Error("Could not determine ECDSA recovery parameter");
+ }
+
+bool ECDSA_PrivateKey::check_key(RandomNumberGenerator& rng,
+ bool strong) const
+ {
+ if(!public_point().on_the_curve())
+ return false;
+
+ if(!strong)
+ return true;
+
+ return KeyPair::signature_consistency_check(rng, *this, "EMSA1(SHA-256)");
+ }
+
+namespace {
+
+/**
+* ECDSA signature operation
+*/
+class ECDSA_Signature_Operation final : public PK_Ops::Signature_with_EMSA
+ {
+ public:
+
+ ECDSA_Signature_Operation(const ECDSA_PrivateKey& ecdsa,
+ const std::string& emsa,
+ RandomNumberGenerator& rng) :
+ PK_Ops::Signature_with_EMSA(emsa),
+ m_group(ecdsa.domain()),
+ m_x(ecdsa.private_value())
+ {
+#if defined(BOTAN_HAS_RFC6979_GENERATOR)
+ m_rfc6979.reset(new RFC6979_Nonce_Generator(hash_for_emsa(emsa), m_group.get_order(), m_x));
+#endif
+
+ m_b = m_group.random_scalar(rng);
+ m_b_inv = m_group.inverse_mod_order(m_b);
+ }
+
+ size_t signature_length() const override { return 2*m_group.get_order_bytes(); }
+
+ size_t max_input_bits() const override { return m_group.get_order_bits(); }
+
+ secure_vector<uint8_t> raw_sign(const uint8_t msg[], size_t msg_len,
+ RandomNumberGenerator& rng) override;
+
+ private:
+ const EC_Group m_group;
+ const BigInt& m_x;
+
+#if defined(BOTAN_HAS_RFC6979_GENERATOR)
+ std::unique_ptr<RFC6979_Nonce_Generator> m_rfc6979;
+#endif
+
+ std::vector<BigInt> m_ws;
+
+ BigInt m_b, m_b_inv;
+ };
+
+secure_vector<uint8_t>
+ECDSA_Signature_Operation::raw_sign(const uint8_t msg[], size_t msg_len,
+ RandomNumberGenerator& rng)
+ {
+ BigInt m(msg, msg_len, m_group.get_order_bits());
+
+#if defined(BOTAN_HAS_RFC6979_GENERATOR)
+ const BigInt k = m_rfc6979->nonce_for(m);
+#else
+ const BigInt k = m_group.random_scalar(rng);
+#endif
+
+ const BigInt r = m_group.mod_order(
+ m_group.blinded_base_point_multiply_x(k, rng, m_ws));
+
+ const BigInt k_inv = m_group.inverse_mod_order(k);
+
+ /*
+ * Blind the input message and compute x*r+m as (x*r*b + m*b)/b
+ */
+ m_b = m_group.square_mod_order(m_b);
+ m_b_inv = m_group.square_mod_order(m_b_inv);
+
+ m = m_group.multiply_mod_order(m_b, m_group.mod_order(m));
+ const BigInt xr_m = m_group.mod_order(m_group.multiply_mod_order(m_x, m_b, r) + m);
+
+ const BigInt s = m_group.multiply_mod_order(k_inv, xr_m, m_b_inv);
+
+ // With overwhelming probability, a bug rather than actual zero r/s
+ if(r.is_zero() || s.is_zero())
+ throw Internal_Error("During ECDSA signature generated zero r/s");
+
+ return BigInt::encode_fixed_length_int_pair(r, s, m_group.get_order_bytes());
+ }
+
+/**
+* ECDSA verification operation
+*/
+class ECDSA_Verification_Operation final : public PK_Ops::Verification_with_EMSA
+ {
+ public:
+ ECDSA_Verification_Operation(const ECDSA_PublicKey& ecdsa,
+ const std::string& emsa) :
+ PK_Ops::Verification_with_EMSA(emsa),
+ m_group(ecdsa.domain()),
+ m_gy_mul(m_group.get_base_point(), ecdsa.public_point())
+ {
+ }
+
+ size_t max_input_bits() const override { return m_group.get_order_bits(); }
+
+ bool with_recovery() const override { return false; }
+
+ bool verify(const uint8_t msg[], size_t msg_len,
+ const uint8_t sig[], size_t sig_len) override;
+ private:
+ const EC_Group m_group;
+ const PointGFp_Multi_Point_Precompute m_gy_mul;
+ };
+
+bool ECDSA_Verification_Operation::verify(const uint8_t msg[], size_t msg_len,
+ const uint8_t sig[], size_t sig_len)
+ {
+ if(sig_len != m_group.get_order_bytes() * 2)
+ return false;
+
+ const BigInt e(msg, msg_len, m_group.get_order_bits());
+
+ const BigInt r(sig, sig_len / 2);
+ const BigInt s(sig + sig_len / 2, sig_len / 2);
+
+ if(r <= 0 || r >= m_group.get_order() || s <= 0 || s >= m_group.get_order())
+ return false;
+
+ const BigInt w = m_group.inverse_mod_order(s);
+
+ const BigInt u1 = m_group.multiply_mod_order(m_group.mod_order(e), w);
+ const BigInt u2 = m_group.multiply_mod_order(r, w);
+ const PointGFp R = m_gy_mul.multi_exp(u1, u2);
+
+ if(R.is_zero())
+ return false;
+
+ const BigInt v = m_group.mod_order(R.get_affine_x());
+ return (v == r);
+ }
+
+}
+
+std::unique_ptr<PK_Ops::Verification>
+ECDSA_PublicKey::create_verification_op(const std::string& params,
+ const std::string& provider) const
+ {
+#if defined(BOTAN_HAS_OPENSSL)
+ if(provider == "openssl" || provider.empty())
+ {
+ try
+ {
+ return make_openssl_ecdsa_ver_op(*this, params);
+ }
+ catch(Lookup_Error& e)
+ {
+ if(provider == "openssl")
+ throw;
+ }
+ }
+#endif
+
+ if(provider == "base" || provider.empty())
+ return std::unique_ptr<PK_Ops::Verification>(new ECDSA_Verification_Operation(*this, params));
+
+ throw Provider_Not_Found(algo_name(), provider);
+ }
+
+std::unique_ptr<PK_Ops::Signature>
+ECDSA_PrivateKey::create_signature_op(RandomNumberGenerator& rng,
+ const std::string& params,
+ const std::string& provider) const
+ {
+#if defined(BOTAN_HAS_OPENSSL)
+ if(provider == "openssl" || provider.empty())
+ {
+ try
+ {
+ return make_openssl_ecdsa_sig_op(*this, params);
+ }
+ catch(Lookup_Error& e)
+ {
+ if(provider == "openssl")
+ throw;
+ }
+ }
+#endif
+
+ if(provider == "base" || provider.empty())
+ return std::unique_ptr<PK_Ops::Signature>(new ECDSA_Signature_Operation(*this, params, rng));
+
+ throw Provider_Not_Found(algo_name(), provider);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/pubkey/ecdsa/ecdsa.h b/comm/third_party/botan/src/lib/pubkey/ecdsa/ecdsa.h
new file mode 100644
index 0000000000..8423a9c205
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/ecdsa/ecdsa.h
@@ -0,0 +1,117 @@
+/*
+* ECDSA
+* (C) 2007 Falko Strenzke, FlexSecure GmbH
+* Manuel Hartl, FlexSecure GmbH
+* (C) 2008-2010 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_ECDSA_KEY_H_
+#define BOTAN_ECDSA_KEY_H_
+
+#include <botan/ecc_key.h>
+
+namespace Botan {
+
+/**
+* This class represents ECDSA Public Keys.
+*/
+class BOTAN_PUBLIC_API(2,0) ECDSA_PublicKey : public virtual EC_PublicKey
+ {
+ public:
+
+ /**
+ * Create a public key from a given public point.
+ * @param dom_par the domain parameters associated with this key
+ * @param public_point the public point defining this key
+ */
+ ECDSA_PublicKey(const EC_Group& dom_par,
+ const PointGFp& public_point) :
+ EC_PublicKey(dom_par, public_point) {}
+
+ /**
+ * Load a public key.
+ * @param alg_id the X.509 algorithm identifier
+ * @param key_bits DER encoded public key bits
+ */
+ ECDSA_PublicKey(const AlgorithmIdentifier& alg_id,
+ const std::vector<uint8_t>& key_bits) :
+ EC_PublicKey(alg_id, key_bits) {}
+
+ /**
+ * Recover a public key from a signature/msg pair
+ * 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 v the recovery ID
+ */
+ ECDSA_PublicKey(const EC_Group& group,
+ const std::vector<uint8_t>& msg,
+ const BigInt& r,
+ const BigInt& s,
+ uint8_t v);
+
+ /**
+ * Get this keys algorithm name.
+ * @result this keys algorithm name ("ECDSA")
+ */
+ std::string algo_name() const override { return "ECDSA"; }
+
+ size_t message_parts() const override { return 2; }
+
+ size_t message_part_size() const override
+ { return domain().get_order().bytes(); }
+
+ uint8_t recovery_param(const std::vector<uint8_t>& msg,
+ const BigInt& r,
+ const BigInt& s) const;
+
+ std::unique_ptr<PK_Ops::Verification>
+ create_verification_op(const std::string& params,
+ const std::string& provider) const override;
+ protected:
+ ECDSA_PublicKey() = default;
+ };
+
+/**
+* This class represents ECDSA Private Keys
+*/
+class BOTAN_PUBLIC_API(2,0) ECDSA_PrivateKey final : public ECDSA_PublicKey,
+ public EC_PrivateKey
+ {
+ public:
+
+ /**
+ * Load a private key
+ * @param alg_id the X.509 algorithm identifier
+ * @param key_bits ECPrivateKey bits
+ */
+ ECDSA_PrivateKey(const AlgorithmIdentifier& alg_id,
+ const secure_vector<uint8_t>& key_bits) :
+ EC_PrivateKey(alg_id, key_bits) {}
+
+ /**
+ * Create a private key.
+ * @param rng a random number generator
+ * @param domain parameters to used for this key
+ * @param x the private key (if zero, generate a new random key)
+ */
+ ECDSA_PrivateKey(RandomNumberGenerator& rng,
+ const EC_Group& domain,
+ const BigInt& x = 0) :
+ EC_PrivateKey(rng, domain, x) {}
+
+ bool check_key(RandomNumberGenerator& rng, bool) const override;
+
+ std::unique_ptr<PK_Ops::Signature>
+ create_signature_op(RandomNumberGenerator& rng,
+ const std::string& params,
+ const std::string& provider) const override;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/pubkey/ecdsa/info.txt b/comm/third_party/botan/src/lib/pubkey/ecdsa/info.txt
new file mode 100644
index 0000000000..6bd32ca175
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/ecdsa/info.txt
@@ -0,0 +1,14 @@
+<defines>
+ECDSA -> 20131128
+</defines>
+
+<requires>
+asn1
+ec_group
+ecc_key
+keypair
+numbertheory
+rng
+emsa1
+sha2_32
+</requires>
diff --git a/comm/third_party/botan/src/lib/pubkey/ecgdsa/ecgdsa.cpp b/comm/third_party/botan/src/lib/pubkey/ecgdsa/ecgdsa.cpp
new file mode 100644
index 0000000000..7ed8763158
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/ecgdsa/ecgdsa.cpp
@@ -0,0 +1,155 @@
+/*
+* ECGDSA (BSI-TR-03111, version 2.0)
+* (C) 2016 René Korthaus
+* (C) 2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/ecgdsa.h>
+#include <botan/keypair.h>
+#include <botan/reducer.h>
+#include <botan/internal/pk_ops_impl.h>
+#include <botan/internal/point_mul.h>
+
+namespace Botan {
+
+bool ECGDSA_PrivateKey::check_key(RandomNumberGenerator& rng,
+ bool strong) const
+ {
+ if(!public_point().on_the_curve())
+ return false;
+
+ if(!strong)
+ return true;
+
+ return KeyPair::signature_consistency_check(rng, *this, "EMSA1(SHA-256)");
+ }
+
+namespace {
+
+/**
+* ECGDSA signature operation
+*/
+class ECGDSA_Signature_Operation final : public PK_Ops::Signature_with_EMSA
+ {
+ public:
+
+ ECGDSA_Signature_Operation(const ECGDSA_PrivateKey& ecgdsa,
+ const std::string& emsa) :
+ PK_Ops::Signature_with_EMSA(emsa),
+ m_group(ecgdsa.domain()),
+ m_x(ecgdsa.private_value())
+ {
+ }
+
+ secure_vector<uint8_t> raw_sign(const uint8_t msg[], size_t msg_len,
+ RandomNumberGenerator& rng) override;
+
+ size_t signature_length() const override { return 2*m_group.get_order_bytes(); }
+
+ size_t max_input_bits() const override { return m_group.get_order_bits(); }
+
+ private:
+ const EC_Group m_group;
+ const BigInt& m_x;
+ std::vector<BigInt> m_ws;
+ };
+
+secure_vector<uint8_t>
+ECGDSA_Signature_Operation::raw_sign(const uint8_t msg[], size_t msg_len,
+ RandomNumberGenerator& rng)
+ {
+ const BigInt m(msg, msg_len, m_group.get_order_bits());
+
+ const BigInt k = m_group.random_scalar(rng);
+
+ const BigInt r = m_group.mod_order(
+ m_group.blinded_base_point_multiply_x(k, rng, m_ws));
+
+ const BigInt kr = m_group.multiply_mod_order(k, r);
+
+ const BigInt s = m_group.multiply_mod_order(m_x, kr - m);
+
+ // With overwhelming probability, a bug rather than actual zero r/s
+ if(r.is_zero() || s.is_zero())
+ throw Internal_Error("During ECGDSA signature generated zero r/s");
+
+ return BigInt::encode_fixed_length_int_pair(r, s, m_group.get_order_bytes());
+ }
+
+/**
+* ECGDSA verification operation
+*/
+class ECGDSA_Verification_Operation final : public PK_Ops::Verification_with_EMSA
+ {
+ public:
+
+ ECGDSA_Verification_Operation(const ECGDSA_PublicKey& ecgdsa,
+ const std::string& emsa) :
+ PK_Ops::Verification_with_EMSA(emsa),
+ m_group(ecgdsa.domain()),
+ m_gy_mul(m_group.get_base_point(), ecgdsa.public_point())
+ {
+ }
+
+ size_t max_input_bits() const override { return m_group.get_order_bits(); }
+
+ bool with_recovery() const override { return false; }
+
+ bool verify(const uint8_t msg[], size_t msg_len,
+ const uint8_t sig[], size_t sig_len) override;
+ private:
+ const EC_Group m_group;
+ const PointGFp_Multi_Point_Precompute m_gy_mul;
+ };
+
+bool ECGDSA_Verification_Operation::verify(const uint8_t msg[], size_t msg_len,
+ const uint8_t sig[], size_t sig_len)
+ {
+ if(sig_len != m_group.get_order_bytes() * 2)
+ return false;
+
+ const BigInt e(msg, msg_len, m_group.get_order_bits());
+
+ const BigInt r(sig, sig_len / 2);
+ const BigInt s(sig + sig_len / 2, sig_len / 2);
+
+ if(r <= 0 || r >= m_group.get_order() || s <= 0 || s >= m_group.get_order())
+ return false;
+
+ const BigInt w = m_group.inverse_mod_order(r);
+
+ const BigInt u1 = m_group.multiply_mod_order(e, w);
+ const BigInt u2 = m_group.multiply_mod_order(s, w);
+ const PointGFp R = m_gy_mul.multi_exp(u1, u2);
+
+ if(R.is_zero())
+ return false;
+
+ const BigInt v = m_group.mod_order(R.get_affine_x());
+ return (v == r);
+ }
+
+}
+
+std::unique_ptr<PK_Ops::Verification>
+ECGDSA_PublicKey::create_verification_op(const std::string& params,
+ const std::string& provider) const
+ {
+ if(provider == "base" || provider.empty())
+ return std::unique_ptr<PK_Ops::Verification>(new ECGDSA_Verification_Operation(*this, params));
+ throw Provider_Not_Found(algo_name(), provider);
+ }
+
+std::unique_ptr<PK_Ops::Signature>
+ECGDSA_PrivateKey::create_signature_op(RandomNumberGenerator& /*rng*/,
+ const std::string& params,
+ const std::string& provider) const
+ {
+ if(provider == "base" || provider.empty())
+ return std::unique_ptr<PK_Ops::Signature>(new ECGDSA_Signature_Operation(*this, params));
+ throw Provider_Not_Found(algo_name(), provider);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/pubkey/ecgdsa/ecgdsa.h b/comm/third_party/botan/src/lib/pubkey/ecgdsa/ecgdsa.h
new file mode 100644
index 0000000000..31d0e2be55
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/ecgdsa/ecgdsa.h
@@ -0,0 +1,96 @@
+/*
+* ECGDSA (BSI-TR-03111, version 2.0)
+* (C) 2016 René Korthaus
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_ECGDSA_KEY_H_
+#define BOTAN_ECGDSA_KEY_H_
+
+#include <botan/ecc_key.h>
+
+namespace Botan {
+
+/**
+* This class represents ECGDSA public keys.
+*/
+class BOTAN_PUBLIC_API(2,0) ECGDSA_PublicKey : public virtual EC_PublicKey
+ {
+ public:
+
+ /**
+ * Construct a public key from a given public point.
+ * @param dom_par the domain parameters associated with this key
+ * @param public_point the public point defining this key
+ */
+ ECGDSA_PublicKey(const EC_Group& dom_par,
+ const PointGFp& public_point) :
+ EC_PublicKey(dom_par, public_point) {}
+
+ /**
+ * Load a public key.
+ * @param alg_id the X.509 algorithm identifier
+ * @param key_bits DER encoded public key bits
+ */
+ ECGDSA_PublicKey(const AlgorithmIdentifier& alg_id,
+ const std::vector<uint8_t>& key_bits) :
+ EC_PublicKey(alg_id, key_bits) {}
+
+ /**
+ * Get this keys algorithm name.
+ * @result this keys algorithm name ("ECGDSA")
+ */
+ std::string algo_name() const override { return "ECGDSA"; }
+
+ size_t message_parts() const override { return 2; }
+
+ size_t message_part_size() const override
+ { return domain().get_order().bytes(); }
+
+ std::unique_ptr<PK_Ops::Verification>
+ create_verification_op(const std::string& params,
+ const std::string& provider) const override;
+ protected:
+ ECGDSA_PublicKey() = default;
+ };
+
+/**
+* This class represents ECGDSA private keys.
+*/
+class BOTAN_PUBLIC_API(2,0) ECGDSA_PrivateKey final : public ECGDSA_PublicKey,
+ public EC_PrivateKey
+ {
+ public:
+
+ /**
+ * Load a private key.
+ * @param alg_id the X.509 algorithm identifier
+ * @param key_bits ECPrivateKey bits
+ */
+ ECGDSA_PrivateKey(const AlgorithmIdentifier& alg_id,
+ const secure_vector<uint8_t>& key_bits) :
+ EC_PrivateKey(alg_id, key_bits, true) {}
+
+ /**
+ * Generate a new private key.
+ * @param rng a random number generator
+ * @param domain parameters to used for this key
+ * @param x the private key (if zero, generate a new random key)
+ */
+ ECGDSA_PrivateKey(RandomNumberGenerator& rng,
+ const EC_Group& domain,
+ const BigInt& x = 0) :
+ EC_PrivateKey(rng, domain, x, true) {}
+
+ bool check_key(RandomNumberGenerator& rng, bool) const override;
+
+ std::unique_ptr<PK_Ops::Signature>
+ create_signature_op(RandomNumberGenerator& rng,
+ const std::string& params,
+ const std::string& provider) const override;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/pubkey/ecgdsa/info.txt b/comm/third_party/botan/src/lib/pubkey/ecgdsa/info.txt
new file mode 100644
index 0000000000..3f967cfcf9
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/ecgdsa/info.txt
@@ -0,0 +1,15 @@
+<defines>
+ECGDSA -> 20160301
+</defines>
+
+<requires>
+asn1
+bigint
+ec_group
+ecc_key
+keypair
+numbertheory
+rng
+emsa1
+sha2_32
+</requires>
diff --git a/comm/third_party/botan/src/lib/pubkey/ecies/ecies.cpp b/comm/third_party/botan/src/lib/pubkey/ecies/ecies.cpp
new file mode 100644
index 0000000000..a8c277b3aa
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/ecies/ecies.cpp
@@ -0,0 +1,415 @@
+/*
+* ECIES
+* (C) 2016 Philipp Weber
+* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/ecies.h>
+#include <botan/numthry.h>
+#include <botan/cipher_mode.h>
+#include <botan/mac.h>
+#include <botan/internal/pk_ops_impl.h>
+
+namespace Botan {
+
+namespace {
+
+/**
+* Private key type for ECIES_ECDH_KA_Operation
+*/
+class ECIES_PrivateKey final : public EC_PrivateKey, public PK_Key_Agreement_Key
+ {
+ public:
+ explicit ECIES_PrivateKey(const ECDH_PrivateKey& private_key) :
+ EC_PublicKey(private_key),
+ EC_PrivateKey(private_key),
+ PK_Key_Agreement_Key(),
+ m_key(private_key)
+ {
+ }
+
+ std::vector<uint8_t> public_value() const override
+ {
+ return m_key.public_value();
+ }
+
+ std::string algo_name() const override
+ {
+ return "ECIES";
+ }
+
+ std::unique_ptr<PK_Ops::Key_Agreement>
+ create_key_agreement_op(RandomNumberGenerator& rng,
+ const std::string& params,
+ const std::string& provider) const override;
+
+ private:
+ ECDH_PrivateKey m_key;
+ };
+
+/**
+* Implements ECDH key agreement without using the cofactor mode
+*/
+class ECIES_ECDH_KA_Operation final : public PK_Ops::Key_Agreement_with_KDF
+ {
+ public:
+ ECIES_ECDH_KA_Operation(const ECIES_PrivateKey& private_key, RandomNumberGenerator& rng) :
+ PK_Ops::Key_Agreement_with_KDF("Raw"),
+ m_key(private_key),
+ m_rng(rng)
+ {
+ }
+
+ size_t agreed_value_size() const override { return m_key.domain().get_p_bytes(); }
+
+ secure_vector<uint8_t> raw_agree(const uint8_t w[], size_t w_len) override
+ {
+ const EC_Group& group = m_key.domain();
+
+ PointGFp input_point = group.OS2ECP(w, w_len);
+ input_point.randomize_repr(m_rng);
+
+ const PointGFp S = group.blinded_var_point_multiply(
+ input_point, m_key.private_value(), m_rng, m_ws);
+
+ if(S.on_the_curve() == false)
+ throw Internal_Error("ECDH agreed value was not on the curve");
+ return BigInt::encode_1363(S.get_affine_x(), group.get_p_bytes());
+ }
+
+ private:
+ ECIES_PrivateKey m_key;
+ RandomNumberGenerator& m_rng;
+ std::vector<BigInt> m_ws;
+ };
+
+std::unique_ptr<PK_Ops::Key_Agreement>
+ECIES_PrivateKey::create_key_agreement_op(RandomNumberGenerator& rng,
+ const std::string& /*params*/,
+ const std::string& /*provider*/) const
+ {
+ return std::unique_ptr<PK_Ops::Key_Agreement>(new ECIES_ECDH_KA_Operation(*this, rng));
+ }
+
+/**
+* Creates a PK_Key_Agreement instance for the given key and ecies_params
+* Returns either ECIES_ECDH_KA_Operation or the default implementation for the given key,
+* depending on the key and ecies_params
+* @param private_key the private key used for the key agreement
+* @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)
+*/
+PK_Key_Agreement create_key_agreement(const PK_Key_Agreement_Key& private_key,
+ const ECIES_KA_Params& ecies_params,
+ bool for_encryption,
+ RandomNumberGenerator& rng)
+ {
+ const ECDH_PrivateKey* ecdh_key = dynamic_cast<const ECDH_PrivateKey*>(&private_key);
+
+ if(ecdh_key == nullptr && (ecies_params.cofactor_mode() || ecies_params.old_cofactor_mode()
+ || ecies_params.check_mode()))
+ {
+ // assume we have a private key from an external provider (e.g. pkcs#11):
+ // there is no way to determine or control whether the provider uses cofactor mode or not.
+ // ISO 18033 does not allow cofactor mode in combination with old cofactor mode or check mode
+ // => disable cofactor mode, old cofactor mode and check mode for unknown keys/providers (as a precaution).
+ 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()))
+ {
+ // 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");
+ }
+
+ return PK_Key_Agreement(private_key, rng, "Raw"); // use default implementation
+ }
+}
+
+ECIES_KA_Operation::ECIES_KA_Operation(const PK_Key_Agreement_Key& private_key,
+ const ECIES_KA_Params& ecies_params,
+ bool for_encryption,
+ RandomNumberGenerator& rng) :
+ m_ka(create_key_agreement(private_key, ecies_params, for_encryption, rng)),
+ m_params(ecies_params)
+ {
+ }
+
+/**
+* ECIES secret derivation according to ISO 18033-2
+*/
+SymmetricKey ECIES_KA_Operation::derive_secret(const std::vector<uint8_t>& eph_public_key_bin,
+ const PointGFp& other_public_key_point) const
+ {
+ if(other_public_key_point.is_zero())
+ {
+ throw Invalid_Argument("ECIES: other public key point is zero");
+ }
+
+ std::unique_ptr<KDF> kdf = Botan::KDF::create_or_throw(m_params.kdf_spec());
+
+ PointGFp other_point = other_public_key_point;
+
+ // ISO 18033: step b
+ if(m_params.old_cofactor_mode())
+ {
+ other_point *= m_params.domain().get_cofactor();
+ }
+
+ secure_vector<uint8_t> derivation_input;
+
+ // ISO 18033: encryption step e / decryption step g
+ if(!m_params.single_hash_mode())
+ {
+ derivation_input += eph_public_key_bin;
+ }
+
+ // ISO 18033: encryption step f / decryption step h
+ std::vector<uint8_t> other_public_key_bin = other_point.encode(m_params.compression_type());
+ // 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());
+ derivation_input.insert(derivation_input.end(), peh.begin(), peh.end());
+
+ // ISO 18033: encryption step g / decryption step i
+ return kdf->derive_key(m_params.secret_length(), derivation_input);
+ }
+
+
+ECIES_KA_Params::ECIES_KA_Params(const EC_Group& domain, const std::string& kdf_spec, size_t length,
+ PointGFp::Compression_Type 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_System_Params::ECIES_System_Params(const EC_Group& domain, const std::string& kdf_spec,
+ const std::string& dem_algo_spec, size_t dem_key_len,
+ const std::string& mac_spec, size_t mac_key_len,
+ PointGFp::Compression_Type compression_type, ECIES_Flags flags) :
+ ECIES_KA_Params(domain, kdf_spec, dem_key_len + mac_key_len, compression_type, flags),
+ m_dem_spec(dem_algo_spec),
+ m_dem_keylen(dem_key_len),
+ m_mac_spec(mac_spec),
+ m_mac_keylen(mac_key_len)
+ {
+ // ISO 18033: "At most one of CofactorMode, OldCofactorMode, and CheckMode may be 1."
+ if(size_t(cofactor_mode()) + size_t(old_cofactor_mode()) + size_t(check_mode()) > 1)
+ {
+ throw Invalid_Argument("ECIES: only one of cofactor_mode, old_cofactor_mode and check_mode can be set");
+ }
+ }
+
+ECIES_System_Params::ECIES_System_Params(const EC_Group& domain, const std::string& kdf_spec,
+ const std::string& dem_algo_spec, size_t dem_key_len,
+ const std::string& mac_spec, size_t mac_key_len) :
+ ECIES_System_Params(domain, kdf_spec, dem_algo_spec, dem_key_len, mac_spec, mac_key_len, PointGFp::UNCOMPRESSED,
+ ECIES_Flags::NONE)
+ {
+ }
+
+std::unique_ptr<MessageAuthenticationCode> ECIES_System_Params::create_mac() const
+ {
+ return Botan::MessageAuthenticationCode::create_or_throw(m_mac_spec);
+ }
+
+std::unique_ptr<Cipher_Mode> ECIES_System_Params::create_cipher(Botan::Cipher_Dir direction) const
+ {
+ return Cipher_Mode::create_or_throw(m_dem_spec, direction);
+ }
+
+
+/*
+* ECIES_Encryptor Constructor
+*/
+ECIES_Encryptor::ECIES_Encryptor(const PK_Key_Agreement_Key& private_key,
+ const ECIES_System_Params& ecies_params,
+ 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() != PointGFp::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 = m_params.domain().OS2ECP(m_eph_public_key_bin).encode(ecies_params.compression_type());
+ }
+ m_mac = m_params.create_mac();
+ m_cipher = m_params.create_cipher(ENCRYPTION);
+ }
+
+/*
+* 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)
+ {
+ }
+
+size_t ECIES_Encryptor::maximum_input_size() const
+ {
+ /*
+ ECIES should just be used for key transport so this (arbitrary) limit
+ seems sufficient
+ */
+ return 64;
+ }
+
+size_t ECIES_Encryptor::ciphertext_length(size_t ptext_len) const
+ {
+ return m_eph_public_key_bin.size() +
+ m_mac->output_length() +
+ m_cipher->output_length(ptext_len);
+ }
+
+/*
+* ECIES Encryption according to ISO 18033-2
+*/
+std::vector<uint8_t> ECIES_Encryptor::enc(const uint8_t data[], size_t length, RandomNumberGenerator&) const
+ {
+ if(m_other_point.is_zero())
+ {
+ throw Invalid_State("ECIES: the other key is zero");
+ }
+
+ const SymmetricKey secret_key = m_ka.derive_secret(m_eph_public_key_bin, m_other_point);
+
+ // encryption
+
+ m_cipher->set_key(SymmetricKey(secret_key.begin(), m_params.dem_keylen()));
+ if(m_iv.size() == 0 && !m_cipher->valid_nonce_length(m_iv.size()))
+ throw Invalid_Argument("ECIES with " + m_cipher->name() + " requires an IV be set");
+
+ m_cipher->start(m_iv.bits_of());
+
+ secure_vector<uint8_t> encrypted_data(data, data + length);
+ m_cipher->finish(encrypted_data);
+
+ // concat elements
+
+ std::vector<uint8_t> out(m_eph_public_key_bin.size() + encrypted_data.size() + m_mac->output_length());
+ buffer_insert(out, 0, m_eph_public_key_bin);
+ buffer_insert(out, m_eph_public_key_bin.size(), encrypted_data);
+
+ // mac
+ m_mac->set_key(secret_key.begin() + m_params.dem_keylen(), m_params.mac_keylen());
+ m_mac->update(encrypted_data);
+ if(!m_label.empty())
+ {
+ m_mac->update(m_label);
+ }
+ m_mac->final(out.data() + m_eph_public_key_bin.size() + encrypted_data.size());
+
+ return out;
+ }
+
+
+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 Botan::BigInt& cofactor = m_params.domain().get_cofactor();
+ if(cofactor > 1 && Botan::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_mac = m_params.create_mac();
+ m_cipher = m_params.create_cipher(DECRYPTION);
+ }
+
+size_t ECIES_Decryptor::plaintext_length(size_t ctext_len) const
+ {
+ const size_t point_size = m_params.domain().point_size(m_params.compression_type());
+ const size_t overhead = point_size + m_mac->output_length();
+
+ if(ctext_len < overhead)
+ return 0;
+
+ return m_cipher->output_length(ctext_len - overhead);
+ }
+
+/**
+* ECIES Decryption according to ISO 18033-2
+*/
+secure_vector<uint8_t> ECIES_Decryptor::do_decrypt(uint8_t& valid_mask, const uint8_t in[], size_t in_len) const
+ {
+ const size_t point_size = m_params.domain().point_size(m_params.compression_type());
+
+ if(in_len < point_size + m_mac->output_length())
+ {
+ throw Decoding_Error("ECIES decryption: ciphertext is too short");
+ }
+
+ // extract data
+ const std::vector<uint8_t> other_public_key_bin(in, in + point_size); // the received (ephemeral) public key
+ const std::vector<uint8_t> encrypted_data(in + point_size, in + in_len - m_mac->output_length());
+ const std::vector<uint8_t> mac_data(in + in_len - m_mac->output_length(), in + in_len);
+
+ // ISO 18033: step a
+ PointGFp other_public_key = m_params.domain().OS2ECP(other_public_key_bin);
+
+ // ISO 18033: step b
+ if(m_params.check_mode() && !other_public_key.on_the_curve())
+ {
+ throw Decoding_Error("ECIES decryption: received public key is not on the curve");
+ }
+
+ // ISO 18033: step e (and step f because get_affine_x (called by ECDH_KA_Operation::raw_agree)
+ // throws Illegal_Transformation if the point is zero)
+ const SymmetricKey secret_key = m_ka.derive_secret(other_public_key_bin, other_public_key);
+
+ // validate mac
+ m_mac->set_key(secret_key.begin() + m_params.dem_keylen(), m_params.mac_keylen());
+ m_mac->update(encrypted_data);
+ if(!m_label.empty())
+ {
+ m_mac->update(m_label);
+ }
+ const secure_vector<uint8_t> calculated_mac = m_mac->final();
+ valid_mask = ct_compare_u8(mac_data.data(), calculated_mac.data(), mac_data.size());
+
+ if(valid_mask)
+ {
+ // decrypt data
+
+ m_cipher->set_key(SymmetricKey(secret_key.begin(), m_params.dem_keylen()));
+ if(m_iv.size() == 0 && !m_cipher->valid_nonce_length(m_iv.size()))
+ throw Invalid_Argument("ECIES with " + m_cipher->name() + " requires an IV be set");
+ m_cipher->start(m_iv.bits_of());
+
+ try
+ {
+ // the decryption can fail:
+ // e.g. Invalid_Authentication_Tag is thrown if GCM is used and the message does not have a valid tag
+ secure_vector<uint8_t> decrypted_data(encrypted_data.begin(), encrypted_data.end());
+ m_cipher->finish(decrypted_data);
+ return decrypted_data;
+ }
+ catch(...)
+ {
+ valid_mask = 0;
+ }
+ }
+ return secure_vector<uint8_t>();
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/pubkey/ecies/ecies.h b/comm/third_party/botan/src/lib/pubkey/ecies/ecies.h
new file mode 100644
index 0000000000..1b35c8cc79
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/ecies/ecies.h
@@ -0,0 +1,314 @@
+/*
+* ECIES
+* (C) 2016 Philipp Weber
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_ECIES_H_
+#define BOTAN_ECIES_H_
+
+#include <botan/ecdh.h>
+#include <botan/ec_group.h>
+#include <botan/cipher_mode.h>
+#include <botan/point_gfp.h>
+#include <botan/pubkey.h>
+#include <botan/secmem.h>
+#include <botan/symkey.h>
+#include <botan/mac.h>
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace Botan {
+
+class RandomNumberGenerator;
+
+enum class ECIES_Flags : uint32_t
+ {
+ NONE = 0,
+
+ /// if set: prefix the input of the (ecdh) key agreement with the encoded (ephemeral) public key
+ SINGLE_HASH_MODE = 1,
+
+ /// (decryption only) if set: use cofactor multiplication during (ecdh) key agreement
+ COFACTOR_MODE = 2,
+
+ /// if set: use ecdhc instead of ecdh
+ OLD_COFACTOR_MODE = 4,
+
+ /// (decryption only) if set: test if the (ephemeral) public key is on the curve
+ CHECK_MODE = 8
+ };
+
+inline ECIES_Flags operator |(ECIES_Flags a, ECIES_Flags b)
+ {
+ return static_cast<ECIES_Flags>(static_cast<uint32_t>(a) | static_cast<uint32_t>(b));
+ }
+
+inline ECIES_Flags operator &(ECIES_Flags a, ECIES_Flags b)
+ {
+ return static_cast<ECIES_Flags>(static_cast<uint32_t>(a) & static_cast<uint32_t>(b));
+ }
+
+/**
+* Parameters for ECIES secret derivation
+*/
+class BOTAN_PUBLIC_API(2,0) ECIES_KA_Params
+ {
+ public:
+ /**
+ * @param domain 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 flags options, see documentation of ECIES_Flags
+ */
+ ECIES_KA_Params(const EC_Group& domain, const std::string& kdf_spec, size_t length,
+ PointGFp::Compression_Type compression_type, ECIES_Flags flags);
+
+ ECIES_KA_Params(const ECIES_KA_Params&) = default;
+ ECIES_KA_Params& operator=(const ECIES_KA_Params&) = delete;
+
+ virtual ~ECIES_KA_Params() = default;
+
+ inline const EC_Group& domain() const
+ {
+ return m_domain;
+ }
+
+ inline size_t secret_length() const
+ {
+ return m_length;
+ }
+
+ inline bool single_hash_mode() const
+ {
+ return (m_flags & ECIES_Flags::SINGLE_HASH_MODE) == ECIES_Flags::SINGLE_HASH_MODE;
+ }
+
+ inline bool cofactor_mode() const
+ {
+ return (m_flags & ECIES_Flags::COFACTOR_MODE) == ECIES_Flags::COFACTOR_MODE;
+ }
+
+ inline bool old_cofactor_mode() const
+ {
+ return (m_flags & ECIES_Flags::OLD_COFACTOR_MODE) == ECIES_Flags::OLD_COFACTOR_MODE;
+ }
+
+ inline bool check_mode() const
+ {
+ return (m_flags & ECIES_Flags::CHECK_MODE) == ECIES_Flags::CHECK_MODE;
+ }
+
+ inline PointGFp::Compression_Type compression_type() const
+ {
+ return m_compression_mode;
+ }
+
+ const std::string& kdf_spec() const
+ {
+ return m_kdf_spec;
+ }
+
+ private:
+ const EC_Group m_domain;
+ const std::string m_kdf_spec;
+ const size_t m_length;
+ const PointGFp::Compression_Type m_compression_mode;
+ const ECIES_Flags m_flags;
+ };
+
+
+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 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, const std::string& kdf_spec, const std::string& dem_algo_spec,
+ size_t dem_key_len, const std::string& mac_spec, size_t mac_key_len);
+
+ /**
+ * @param domain 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 flags options, see documentation of ECIES_Flags
+ */
+ ECIES_System_Params(const EC_Group& domain, const std::string& kdf_spec, const std::string& dem_algo_spec,
+ size_t dem_key_len, const std::string& mac_spec, size_t mac_key_len,
+ PointGFp::Compression_Type compression_type, ECIES_Flags flags);
+
+ ECIES_System_Params(const ECIES_System_Params&) = default;
+ ECIES_System_Params& operator=(const ECIES_System_Params&) = delete;
+ virtual ~ECIES_System_Params() = default;
+
+ /// creates an instance of the message authentication code
+ std::unique_ptr<MessageAuthenticationCode> create_mac() const;
+
+ /// creates an instance of the data encryption method
+ std::unique_ptr<Cipher_Mode> create_cipher(Botan::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;
+ }
+
+ /// returns the length of the key used by the message authentication code
+ inline size_t mac_keylen() const
+ {
+ return m_mac_keylen;
+ }
+
+ private:
+ const std::string m_dem_spec;
+ const size_t m_dem_keylen;
+ const std::string m_mac_spec;
+ const size_t m_mac_keylen;
+ };
+
+
+/**
+* ECIES secret derivation according to ISO 18033-2
+*/
+class BOTAN_PUBLIC_API(2,0) ECIES_KA_Operation
+ {
+ public:
+ /**
+ * @param private_key the (ephemeral) private key which is used to derive the secret
+ * @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)
+ * @param rng the RNG to use
+ */
+ ECIES_KA_Operation(const PK_Key_Agreement_Key& private_key,
+ const ECIES_KA_Params& ecies_params,
+ bool for_encryption,
+ RandomNumberGenerator& rng);
+
+ /**
+ * Performs a key agreement with the provided keys and derives the secret from the result
+ * @param eph_public_key_bin the encoded (ephemeral) public key which belongs to the used (ephemeral) private key
+ * @param other_public_key_point public key point of the other party
+ */
+ SymmetricKey derive_secret(const std::vector<uint8_t>& eph_public_key_bin,
+ const PointGFp& other_public_key_point) const;
+
+ private:
+ const PK_Key_Agreement m_ka;
+ const ECIES_KA_Params m_params;
+ };
+
+
+/**
+* ECIES Encryption according to ISO 18033-2
+*/
+class BOTAN_PUBLIC_API(2,0) ECIES_Encryptor final : public PK_Encryptor
+ {
+ public:
+ /**
+ * @param private_key the (ephemeral) private key which is used for the key agreement
+ * @param ecies_params settings for ecies
+ * @param rng random generator to use
+ */
+ ECIES_Encryptor(const PK_Key_Agreement_Key& private_key,
+ const ECIES_System_Params& ecies_params,
+ RandomNumberGenerator& rng);
+
+ /**
+ * Creates an ephemeral private key which is used for the key agreement
+ * @param rng random generator used during private key generation
+ * @param ecies_params settings for ecies
+ */
+ ECIES_Encryptor(RandomNumberGenerator& rng, const ECIES_System_Params& ecies_params);
+
+ /// Set the public key of the other party
+ inline void set_other_key(const Botan::PointGFp& public_point)
+ {
+ m_other_point = public_point;
+ }
+
+ /// Set the initialization vector for the data encryption method
+ inline 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(const std::string& label)
+ {
+ m_label = std::vector<uint8_t>(label.begin(), label.end());
+ }
+
+ private:
+ std::vector<uint8_t> enc(const uint8_t data[], size_t length, RandomNumberGenerator&) const override;
+
+ size_t maximum_input_size() const override;
+
+ size_t ciphertext_length(size_t ptext_len) const override;
+
+ const ECIES_KA_Operation m_ka;
+ const ECIES_System_Params m_params;
+ std::unique_ptr<MessageAuthenticationCode> m_mac;
+ std::unique_ptr<Cipher_Mode> m_cipher;
+ std::vector<uint8_t> m_eph_public_key_bin;
+ InitializationVector m_iv;
+ PointGFp m_other_point;
+ std::vector<uint8_t> m_label;
+ };
+
+
+/**
+* ECIES Decryption according to ISO 18033-2
+*/
+class BOTAN_PUBLIC_API(2,0) ECIES_Decryptor final : public PK_Decryptor
+ {
+ public:
+ /**
+ * @param private_key the private key which is used for the key agreement
+ * @param ecies_params settings for ecies
+ * @param rng the random generator to use
+ */
+ ECIES_Decryptor(const PK_Key_Agreement_Key& private_key,
+ const ECIES_System_Params& ecies_params,
+ RandomNumberGenerator& rng);
+
+ /// Set the initialization vector for the data encryption method
+ inline 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(const std::string& label)
+ {
+ m_label = std::vector<uint8_t>(label.begin(), label.end());
+ }
+
+ private:
+ secure_vector<uint8_t> do_decrypt(uint8_t& valid_mask, const uint8_t in[], size_t in_len) const override;
+
+ size_t plaintext_length(size_t ctext_len) const override;
+
+ const ECIES_KA_Operation m_ka;
+ const ECIES_System_Params m_params;
+ std::unique_ptr<MessageAuthenticationCode> m_mac;
+ std::unique_ptr<Cipher_Mode> m_cipher;
+ InitializationVector m_iv;
+ std::vector<uint8_t> m_label;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/pubkey/ecies/info.txt b/comm/third_party/botan/src/lib/pubkey/ecies/info.txt
new file mode 100644
index 0000000000..530cc3970f
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/ecies/info.txt
@@ -0,0 +1,10 @@
+<defines>
+ECIES -> 20160128
+</defines>
+
+<requires>
+kdf
+mac
+ecdh
+modes
+</requires>
diff --git a/comm/third_party/botan/src/lib/pubkey/eckcdsa/eckcdsa.cpp b/comm/third_party/botan/src/lib/pubkey/eckcdsa/eckcdsa.cpp
new file mode 100644
index 0000000000..40d9425143
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/eckcdsa/eckcdsa.cpp
@@ -0,0 +1,208 @@
+/*
+* ECKCDSA (ISO/IEC 14888-3:2006/Cor.2:2009)
+* (C) 2016 René Korthaus, Sirrix AG
+* (C) 2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/eckcdsa.h>
+#include <botan/internal/pk_ops_impl.h>
+#include <botan/internal/point_mul.h>
+#include <botan/keypair.h>
+#include <botan/reducer.h>
+#include <botan/emsa.h>
+#include <botan/hash.h>
+#include <botan/rng.h>
+
+namespace Botan {
+
+bool ECKCDSA_PrivateKey::check_key(RandomNumberGenerator& rng,
+ bool strong) const
+ {
+ if(!public_point().on_the_curve())
+ {
+ return false;
+ }
+
+ if(!strong)
+ {
+ return true;
+ }
+
+ return KeyPair::signature_consistency_check(rng, *this, "EMSA1(SHA-256)");
+ }
+
+namespace {
+
+/**
+* ECKCDSA signature operation
+*/
+class ECKCDSA_Signature_Operation final : public PK_Ops::Signature_with_EMSA
+ {
+ public:
+
+ ECKCDSA_Signature_Operation(const ECKCDSA_PrivateKey& eckcdsa,
+ const std::string& emsa) :
+ PK_Ops::Signature_with_EMSA(emsa),
+ m_group(eckcdsa.domain()),
+ m_x(eckcdsa.private_value()),
+ m_prefix()
+ {
+ const BigInt public_point_x = eckcdsa.public_point().get_affine_x();
+ const BigInt public_point_y = eckcdsa.public_point().get_affine_y();
+
+ m_prefix.resize(public_point_x.bytes() + public_point_y.bytes());
+ public_point_x.binary_encode(m_prefix.data());
+ public_point_y.binary_encode(&m_prefix[public_point_x.bytes()]);
+ m_prefix.resize(HashFunction::create(hash_for_signature())->hash_block_size()); // use only the "hash input block size" leftmost bits
+ }
+
+ secure_vector<uint8_t> raw_sign(const uint8_t msg[], size_t msg_len,
+ RandomNumberGenerator& rng) override;
+
+ size_t signature_length() const override { return 2*m_group.get_order_bytes(); }
+ size_t max_input_bits() const override { return m_group.get_order_bits(); }
+
+ bool has_prefix() override { return true; }
+ secure_vector<uint8_t> message_prefix() const override { return m_prefix; }
+
+ private:
+ const EC_Group m_group;
+ const BigInt& m_x;
+ secure_vector<uint8_t> m_prefix;
+ std::vector<BigInt> m_ws;
+ };
+
+secure_vector<uint8_t>
+ECKCDSA_Signature_Operation::raw_sign(const uint8_t msg[], size_t,
+ RandomNumberGenerator& rng)
+ {
+ const BigInt k = m_group.random_scalar(rng);
+ const BigInt k_times_P_x = m_group.blinded_base_point_multiply_x(k, rng, m_ws);
+
+ secure_vector<uint8_t> to_be_hashed(k_times_P_x.bytes());
+ k_times_P_x.binary_encode(to_be_hashed.data());
+
+ std::unique_ptr<EMSA> emsa = this->clone_emsa();
+ emsa->update(to_be_hashed.data(), to_be_hashed.size());
+ secure_vector<uint8_t> c = emsa->raw_data();
+ c = emsa->encoding_of(c, max_input_bits(), rng);
+
+ const BigInt r(c.data(), c.size());
+
+ xor_buf(c, msg, c.size());
+ BigInt w(c.data(), c.size());
+ w = m_group.mod_order(w);
+
+ const BigInt s = m_group.multiply_mod_order(m_x, k - w);
+ if(s.is_zero())
+ throw Internal_Error("During ECKCDSA signature generation created zero s");
+
+ secure_vector<uint8_t> output = BigInt::encode_1363(r, c.size());
+ output += BigInt::encode_1363(s, m_group.get_order_bytes());
+ return output;
+ }
+
+/**
+* ECKCDSA verification operation
+*/
+class ECKCDSA_Verification_Operation final : public PK_Ops::Verification_with_EMSA
+ {
+ public:
+
+ ECKCDSA_Verification_Operation(const ECKCDSA_PublicKey& eckcdsa,
+ const std::string& emsa) :
+ PK_Ops::Verification_with_EMSA(emsa),
+ m_group(eckcdsa.domain()),
+ m_gy_mul(m_group.get_base_point(), eckcdsa.public_point()),
+ m_prefix()
+ {
+ const BigInt public_point_x = eckcdsa.public_point().get_affine_x();
+ const BigInt public_point_y = eckcdsa.public_point().get_affine_y();
+
+ m_prefix.resize(public_point_x.bytes() + public_point_y.bytes());
+ public_point_x.binary_encode(&m_prefix[0]);
+ public_point_y.binary_encode(&m_prefix[public_point_x.bytes()]);
+ m_prefix.resize(HashFunction::create(hash_for_signature())->hash_block_size()); // use only the "hash input block size" leftmost bits
+ }
+
+ bool has_prefix() override { return true; }
+ secure_vector<uint8_t> message_prefix() const override { return m_prefix; }
+
+ size_t max_input_bits() const override { return m_group.get_order_bits(); }
+
+ bool with_recovery() const override { return false; }
+
+ bool verify(const uint8_t msg[], size_t msg_len,
+ const uint8_t sig[], size_t sig_len) override;
+ private:
+ const EC_Group m_group;
+ const PointGFp_Multi_Point_Precompute m_gy_mul;
+ secure_vector<uint8_t> m_prefix;
+ };
+
+bool ECKCDSA_Verification_Operation::verify(const uint8_t msg[], size_t,
+ const uint8_t sig[], size_t sig_len)
+ {
+ const std::unique_ptr<HashFunction> hash = HashFunction::create(hash_for_signature());
+ //calculate size of r
+
+ const size_t order_bytes = m_group.get_order_bytes();
+
+ const size_t size_r = std::min(hash -> output_length(), order_bytes);
+ if(sig_len != size_r + order_bytes)
+ {
+ return false;
+ }
+
+ secure_vector<uint8_t> r(sig, sig + size_r);
+
+ // check that 0 < s < q
+ const BigInt s(sig + size_r, order_bytes);
+
+ if(s <= 0 || s >= m_group.get_order())
+ {
+ return false;
+ }
+
+ secure_vector<uint8_t> r_xor_e(r);
+ xor_buf(r_xor_e, msg, r.size());
+ BigInt w(r_xor_e.data(), r_xor_e.size());
+ w = m_group.mod_order(w);
+
+ const PointGFp q = m_gy_mul.multi_exp(w, s);
+ const BigInt q_x = q.get_affine_x();
+ secure_vector<uint8_t> c(q_x.bytes());
+ q_x.binary_encode(c.data());
+ std::unique_ptr<EMSA> emsa = this->clone_emsa();
+ emsa->update(c.data(), c.size());
+ secure_vector<uint8_t> v = emsa->raw_data();
+ Null_RNG rng;
+ v = emsa->encoding_of(v, max_input_bits(), rng);
+
+ return (v == r);
+ }
+
+}
+
+std::unique_ptr<PK_Ops::Verification>
+ECKCDSA_PublicKey::create_verification_op(const std::string& params,
+ const std::string& provider) const
+ {
+ if(provider == "base" || provider.empty())
+ return std::unique_ptr<PK_Ops::Verification>(new ECKCDSA_Verification_Operation(*this, params));
+ throw Provider_Not_Found(algo_name(), provider);
+ }
+
+std::unique_ptr<PK_Ops::Signature>
+ECKCDSA_PrivateKey::create_signature_op(RandomNumberGenerator& /*rng*/,
+ const std::string& params,
+ const std::string& provider) const
+ {
+ if(provider == "base" || provider.empty())
+ return std::unique_ptr<PK_Ops::Signature>(new ECKCDSA_Signature_Operation(*this, params));
+ throw Provider_Not_Found(algo_name(), provider);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/pubkey/eckcdsa/eckcdsa.h b/comm/third_party/botan/src/lib/pubkey/eckcdsa/eckcdsa.h
new file mode 100644
index 0000000000..aa04cb146d
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/eckcdsa/eckcdsa.h
@@ -0,0 +1,96 @@
+/*
+* ECKCDSA (ISO/IEC 14888-3:2006/Cor.2:2009)
+* (C) 2016 René Korthaus, Sirrix AG
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_ECKCDSA_KEY_H_
+#define BOTAN_ECKCDSA_KEY_H_
+
+#include <botan/ecc_key.h>
+
+namespace Botan {
+
+/**
+* This class represents ECKCDSA public keys.
+*/
+class BOTAN_PUBLIC_API(2,0) ECKCDSA_PublicKey : public virtual EC_PublicKey
+ {
+ public:
+
+ /**
+ * Construct a public key from a given public point.
+ * @param dom_par the domain parameters associated with this key
+ * @param public_point the public point defining this key
+ */
+ ECKCDSA_PublicKey(const EC_Group& dom_par,
+ const PointGFp& public_point) :
+ EC_PublicKey(dom_par, public_point) {}
+
+ /**
+ * Load a public key.
+ * @param alg_id the X.509 algorithm identifier
+ * @param key_bits DER encoded public key bits
+ */
+ ECKCDSA_PublicKey(const AlgorithmIdentifier& alg_id,
+ const std::vector<uint8_t>& key_bits) :
+ EC_PublicKey(alg_id, key_bits) {}
+
+ /**
+ * Get this keys algorithm name.
+ * @result this keys algorithm name ("ECGDSA")
+ */
+ std::string algo_name() const override { return "ECKCDSA"; }
+
+ size_t message_parts() const override { return 2; }
+
+ size_t message_part_size() const override
+ { return domain().get_order().bytes(); }
+
+ std::unique_ptr<PK_Ops::Verification>
+ create_verification_op(const std::string& params,
+ const std::string& provider) const override;
+ protected:
+ ECKCDSA_PublicKey() = default;
+ };
+
+/**
+* This class represents ECKCDSA private keys.
+*/
+class BOTAN_PUBLIC_API(2,0) ECKCDSA_PrivateKey final : public ECKCDSA_PublicKey,
+ public EC_PrivateKey
+ {
+ public:
+
+ /**
+ * Load a private key.
+ * @param alg_id the X.509 algorithm identifier
+ * @param key_bits ECPrivateKey bits
+ */
+ ECKCDSA_PrivateKey(const AlgorithmIdentifier& alg_id,
+ const secure_vector<uint8_t>& key_bits) :
+ EC_PrivateKey(alg_id, key_bits, true) {}
+
+ /**
+ * Create a private key.
+ * @param rng a random number generator
+ * @param domain parameters to used for this key
+ * @param x the private key (if zero, generate a new random key)
+ */
+ ECKCDSA_PrivateKey(RandomNumberGenerator& rng,
+ const EC_Group& domain,
+ const BigInt& x = 0) :
+ EC_PrivateKey(rng, domain, x, true) {}
+
+ bool check_key(RandomNumberGenerator& rng, bool) const override;
+
+ std::unique_ptr<PK_Ops::Signature>
+ create_signature_op(RandomNumberGenerator& rng,
+ const std::string& params,
+ const std::string& provider) const override;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/pubkey/eckcdsa/info.txt b/comm/third_party/botan/src/lib/pubkey/eckcdsa/info.txt
new file mode 100644
index 0000000000..2bce4aa79b
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/eckcdsa/info.txt
@@ -0,0 +1,17 @@
+<defines>
+ECKCDSA -> 20160413
+</defines>
+
+<requires>
+asn1
+bigint
+ec_group
+ecc_key
+emsa1
+hash
+keypair
+numbertheory
+pk_pad
+rng
+sha2_32
+</requires>
diff --git a/comm/third_party/botan/src/lib/pubkey/ed25519/ed25519.cpp b/comm/third_party/botan/src/lib/pubkey/ed25519/ed25519.cpp
new file mode 100644
index 0000000000..624f82657a
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/ed25519/ed25519.cpp
@@ -0,0 +1,102 @@
+/*
+* 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
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/ed25519.h>
+#include <botan/internal/ed25519_internal.h>
+#include <botan/sha2_64.h>
+#include <botan/rng.h>
+
+namespace Botan {
+
+void ed25519_gen_keypair(uint8_t* pk, uint8_t* sk, const uint8_t seed[32])
+ {
+ uint8_t az[64];
+
+ SHA_512 sha;
+ sha.update(seed, 32);
+ sha.final(az);
+ az[0] &= 248;
+ az[31] &= 63;
+ az[31] |= 64;
+
+ ge_scalarmult_base(pk, az);
+
+ // todo copy_mem
+ copy_mem(sk, seed, 32);
+ copy_mem(sk + 32, pk, 32);
+ }
+
+void ed25519_sign(uint8_t sig[64],
+ const uint8_t m[], size_t mlen,
+ const uint8_t sk[64],
+ const uint8_t domain_sep[], size_t domain_sep_len)
+ {
+ uint8_t az[64];
+ uint8_t nonce[64];
+ uint8_t hram[64];
+
+ SHA_512 sha;
+
+ sha.update(sk, 32);
+ sha.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);
+
+ sc_reduce(nonce);
+ ge_scalarmult_base(sig, nonce);
+
+ sha.update(domain_sep, domain_sep_len);
+ sha.update(sig, 32);
+ sha.update(sk + 32, 32);
+ sha.update(m, mlen);
+ sha.final(hram);
+
+ sc_reduce(hram);
+ sc_muladd(sig + 32, hram, az, nonce);
+ }
+
+bool ed25519_verify(const uint8_t* m, size_t mlen,
+ const uint8_t sig[64],
+ 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)
+ {
+ return false;
+ }
+
+ 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);
+
+ ge_double_scalarmult_vartime(rcheck, h, &A, sig + 32);
+
+ return constant_time_compare(rcheck, sig, 32);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/pubkey/ed25519/ed25519.h b/comm/third_party/botan/src/lib/pubkey/ed25519/ed25519.h
new file mode 100644
index 0000000000..97ed023f27
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/ed25519/ed25519.h
@@ -0,0 +1,113 @@
+/*
+* 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
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_ED25519_H_
+#define BOTAN_ED25519_H_
+
+#include <botan/pk_keys.h>
+
+namespace Botan {
+
+class BOTAN_PUBLIC_API(2,2) Ed25519_PublicKey : public virtual Public_Key
+ {
+ public:
+ std::string algo_name() const override { return "Ed25519"; }
+
+ size_t estimated_strength() const override { return 128; }
+
+ size_t key_length() const override { return 255; }
+
+ bool check_key(RandomNumberGenerator& rng, bool strong) const override;
+
+ AlgorithmIdentifier algorithm_identifier() const override;
+
+ std::vector<uint8_t> public_key_bits() const override;
+
+ /**
+ * Create a Ed25519 Public Key.
+ * @param alg_id the X.509 algorithm identifier
+ * @param key_bits DER encoded public key bits
+ */
+ Ed25519_PublicKey(const AlgorithmIdentifier& alg_id,
+ const std::vector<uint8_t>& key_bits);
+
+ template<typename Alloc>
+ Ed25519_PublicKey(const std::vector<uint8_t, Alloc>& pub) :
+ Ed25519_PublicKey(pub.data(), pub.size()) {}
+
+ Ed25519_PublicKey(const uint8_t pub_key[], size_t len);
+
+ std::unique_ptr<PK_Ops::Verification>
+ create_verification_op(const std::string& params,
+ const std::string& provider) const override;
+
+ const std::vector<uint8_t>& get_public_key() const { return m_public; }
+
+ protected:
+ Ed25519_PublicKey() = default;
+ std::vector<uint8_t> m_public;
+ };
+
+class BOTAN_PUBLIC_API(2,2) Ed25519_PrivateKey final : public Ed25519_PublicKey,
+ public virtual Private_Key
+ {
+ public:
+ /**
+ * Construct a private key from the specified parameters.
+ * @param alg_id the X.509 algorithm identifier
+ * @param key_bits PKCS #8 structure
+ */
+ Ed25519_PrivateKey(const AlgorithmIdentifier& alg_id,
+ const secure_vector<uint8_t>& key_bits);
+
+ /**
+ * Generate a 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
+ */
+ explicit Ed25519_PrivateKey(const secure_vector<uint8_t>& secret_key);
+
+ const secure_vector<uint8_t>& get_private_key() const { return m_private; }
+
+ secure_vector<uint8_t> private_key_bits() const override;
+
+ bool check_key(RandomNumberGenerator& rng, bool strong) const override;
+
+ std::unique_ptr<PK_Ops::Signature>
+ create_signature_op(RandomNumberGenerator& rng,
+ const std::string& params,
+ const std::string& provider) const override;
+
+ private:
+ secure_vector<uint8_t> m_private;
+ };
+
+void ed25519_gen_keypair(uint8_t pk[32], uint8_t sk[64], const uint8_t seed[32]);
+
+void ed25519_sign(uint8_t sig[64],
+ const uint8_t msg[],
+ size_t msg_len,
+ const uint8_t sk[64],
+ const uint8_t domain_sep[], size_t domain_sep_len);
+
+bool ed25519_verify(const uint8_t msg[],
+ size_t msg_len,
+ const uint8_t sig[64],
+ const uint8_t pk[32],
+ const uint8_t domain_sep[], size_t domain_sep_len);
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/pubkey/ed25519/ed25519_fe.cpp b/comm/third_party/botan/src/lib/pubkey/ed25519/ed25519_fe.cpp
new file mode 100644
index 0000000000..135813d393
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/ed25519/ed25519_fe.cpp
@@ -0,0 +1,754 @@
+/*
+* Ed25519 field element
+* (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
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/ed25519_fe.h>
+#include <botan/internal/ed25519_internal.h>
+
+namespace Botan {
+
+//static
+FE_25519 FE_25519::invert(const FE_25519& z)
+ {
+ fe t0;
+ fe t1;
+ fe t2;
+ fe 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);
+
+ fe_mul(t0, t1, t0);
+ return t0;
+ }
+
+FE_25519 FE_25519::pow_22523(const fe& z)
+ {
+ fe t0;
+ fe t1;
+ fe 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);
+
+ fe_mul(t0, t0, z);
+ return t0;
+ }
+
+/*
+h = f * g
+Can overlap h with f or g.
+
+Preconditions:
+|f| bounded by 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc.
+|g| bounded by 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc.
+
+Postconditions:
+|h| bounded by 1.01*2^25,1.01*2^24,1.01*2^25,1.01*2^24,etc.
+*/
+
+/*
+Notes on implementation strategy:
+
+Using schoolbook multiplication.
+Karatsuba would save a little in some cost models.
+
+Most multiplications by 2 and 19 are 32-bit precomputations;
+cheaper than 64-bit postcomputations.
+
+There is one remaining multiplication by 19 in the carry chain;
+one *19 precomputation can be merged into this,
+but the resulting data flow is considerably less clean.
+
+There are 12 carries below.
+10 of them are 2-way parallelizable and vectorizable.
+Can get away with 11 carries, but then data flow is much deeper.
+
+With tighter constraints on inputs can squeeze carries into int32.
+*/
+
+//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];
+
+ const int32_t g1_19 = 19 * g1; /* 1.959375*2^29 */
+ const int32_t g2_19 = 19 * g2; /* 1.959375*2^30; still ok */
+ const int32_t g3_19 = 19 * g3;
+ const int32_t g4_19 = 19 * g4;
+ const int32_t g5_19 = 19 * g5;
+ const int32_t g6_19 = 19 * g6;
+ const int32_t g7_19 = 19 * g7;
+ const int32_t g8_19 = 19 * g8;
+ const int32_t g9_19 = 19 * g9;
+ const int32_t f1_2 = 2 * f1;
+ const int32_t f3_2 = 2 * f3;
+ const int32_t f5_2 = 2 * f5;
+ const int32_t f7_2 = 2 * f7;
+ const int32_t f9_2 = 2 * f9;
+
+ const int64_t f0g0 = f0 * static_cast<int64_t>(g0);
+ const int64_t f0g1 = f0 * static_cast<int64_t>(g1);
+ const int64_t f0g2 = f0 * static_cast<int64_t>(g2);
+ const int64_t f0g3 = f0 * static_cast<int64_t>(g3);
+ const int64_t f0g4 = f0 * static_cast<int64_t>(g4);
+ const int64_t f0g5 = f0 * static_cast<int64_t>(g5);
+ const int64_t f0g6 = f0 * static_cast<int64_t>(g6);
+ const int64_t f0g7 = f0 * static_cast<int64_t>(g7);
+ const int64_t f0g8 = f0 * static_cast<int64_t>(g8);
+ const int64_t f0g9 = f0 * static_cast<int64_t>(g9);
+ const int64_t f1g0 = f1 * static_cast<int64_t>(g0);
+ const int64_t f1g1_2 = f1_2 * static_cast<int64_t>(g1);
+ const int64_t f1g2 = f1 * static_cast<int64_t>(g2);
+ const int64_t f1g3_2 = f1_2 * static_cast<int64_t>(g3);
+ const int64_t f1g4 = f1 * static_cast<int64_t>(g4);
+ const int64_t f1g5_2 = f1_2 * static_cast<int64_t>(g5);
+ const int64_t f1g6 = f1 * static_cast<int64_t>(g6);
+ const int64_t f1g7_2 = f1_2 * static_cast<int64_t>(g7);
+ const int64_t f1g8 = f1 * static_cast<int64_t>(g8);
+ const int64_t f1g9_38 = f1_2 * static_cast<int64_t>(g9_19);
+ const int64_t f2g0 = f2 * static_cast<int64_t>(g0);
+ const int64_t f2g1 = f2 * static_cast<int64_t>(g1);
+ const int64_t f2g2 = f2 * static_cast<int64_t>(g2);
+ const int64_t f2g3 = f2 * static_cast<int64_t>(g3);
+ const int64_t f2g4 = f2 * static_cast<int64_t>(g4);
+ const int64_t f2g5 = f2 * static_cast<int64_t>(g5);
+ const int64_t f2g6 = f2 * static_cast<int64_t>(g6);
+ const int64_t f2g7 = f2 * static_cast<int64_t>(g7);
+ const int64_t f2g8_19 = f2 * static_cast<int64_t>(g8_19);
+ const int64_t f2g9_19 = f2 * static_cast<int64_t>(g9_19);
+ const int64_t f3g0 = f3 * static_cast<int64_t>(g0);
+ const int64_t f3g1_2 = f3_2 * static_cast<int64_t>(g1);
+ const int64_t f3g2 = f3 * static_cast<int64_t>(g2);
+ const int64_t f3g3_2 = f3_2 * static_cast<int64_t>(g3);
+ const int64_t f3g4 = f3 * static_cast<int64_t>(g4);
+ const int64_t f3g5_2 = f3_2 * static_cast<int64_t>(g5);
+ const int64_t f3g6 = f3 * static_cast<int64_t>(g6);
+ const int64_t f3g7_38 = f3_2 * static_cast<int64_t>(g7_19);
+ const int64_t f3g8_19 = f3 * static_cast<int64_t>(g8_19);
+ const int64_t f3g9_38 = f3_2 * static_cast<int64_t>(g9_19);
+ const int64_t f4g0 = f4 * static_cast<int64_t>(g0);
+ const int64_t f4g1 = f4 * static_cast<int64_t>(g1);
+ const int64_t f4g2 = f4 * static_cast<int64_t>(g2);
+ const int64_t f4g3 = f4 * static_cast<int64_t>(g3);
+ const int64_t f4g4 = f4 * static_cast<int64_t>(g4);
+ const int64_t f4g5 = f4 * static_cast<int64_t>(g5);
+ const int64_t f4g6_19 = f4 * static_cast<int64_t>(g6_19);
+ const int64_t f4g7_19 = f4 * static_cast<int64_t>(g7_19);
+ const int64_t f4g8_19 = f4 * static_cast<int64_t>(g8_19);
+ const int64_t f4g9_19 = f4 * static_cast<int64_t>(g9_19);
+ const int64_t f5g0 = f5 * static_cast<int64_t>(g0);
+ const int64_t f5g1_2 = f5_2 * static_cast<int64_t>(g1);
+ const int64_t f5g2 = f5 * static_cast<int64_t>(g2);
+ const int64_t f5g3_2 = f5_2 * static_cast<int64_t>(g3);
+ const int64_t f5g4 = f5 * static_cast<int64_t>(g4);
+ const int64_t f5g5_38 = f5_2 * static_cast<int64_t>(g5_19);
+ const int64_t f5g6_19 = f5 * static_cast<int64_t>(g6_19);
+ const int64_t f5g7_38 = f5_2 * static_cast<int64_t>(g7_19);
+ const int64_t f5g8_19 = f5 * static_cast<int64_t>(g8_19);
+ const int64_t f5g9_38 = f5_2 * static_cast<int64_t>(g9_19);
+ const int64_t f6g0 = f6 * static_cast<int64_t>(g0);
+ const int64_t f6g1 = f6 * static_cast<int64_t>(g1);
+ const int64_t f6g2 = f6 * static_cast<int64_t>(g2);
+ const int64_t f6g3 = f6 * static_cast<int64_t>(g3);
+ const int64_t f6g4_19 = f6 * static_cast<int64_t>(g4_19);
+ const int64_t f6g5_19 = f6 * static_cast<int64_t>(g5_19);
+ const int64_t f6g6_19 = f6 * static_cast<int64_t>(g6_19);
+ const int64_t f6g7_19 = f6 * static_cast<int64_t>(g7_19);
+ const int64_t f6g8_19 = f6 * static_cast<int64_t>(g8_19);
+ const int64_t f6g9_19 = f6 * static_cast<int64_t>(g9_19);
+ const int64_t f7g0 = f7 * static_cast<int64_t>(g0);
+ const int64_t f7g1_2 = f7_2 * static_cast<int64_t>(g1);
+ const int64_t f7g2 = f7 * static_cast<int64_t>(g2);
+ const int64_t f7g3_38 = f7_2 * static_cast<int64_t>(g3_19);
+ const int64_t f7g4_19 = f7 * static_cast<int64_t>(g4_19);
+ const int64_t f7g5_38 = f7_2 * static_cast<int64_t>(g5_19);
+ const int64_t f7g6_19 = f7 * static_cast<int64_t>(g6_19);
+ const int64_t f7g7_38 = f7_2 * static_cast<int64_t>(g7_19);
+ const int64_t f7g8_19 = f7 * static_cast<int64_t>(g8_19);
+ const int64_t f7g9_38 = f7_2 * static_cast<int64_t>(g9_19);
+ const int64_t f8g0 = f8 * static_cast<int64_t>(g0);
+ const int64_t f8g1 = f8 * static_cast<int64_t>(g1);
+ const int64_t f8g2_19 = f8 * static_cast<int64_t>(g2_19);
+ const int64_t f8g3_19 = f8 * static_cast<int64_t>(g3_19);
+ const int64_t f8g4_19 = f8 * static_cast<int64_t>(g4_19);
+ const int64_t f8g5_19 = f8 * static_cast<int64_t>(g5_19);
+ const int64_t f8g6_19 = f8 * static_cast<int64_t>(g6_19);
+ const int64_t f8g7_19 = f8 * static_cast<int64_t>(g7_19);
+ const int64_t f8g8_19 = f8 * static_cast<int64_t>(g8_19);
+ const int64_t f8g9_19 = f8 * static_cast<int64_t>(g9_19);
+ const int64_t f9g0 = f9 * static_cast<int64_t>(g0);
+ const int64_t f9g1_38 = f9_2 * static_cast<int64_t>(g1_19);
+ const int64_t f9g2_19 = f9 * static_cast<int64_t>(g2_19);
+ const int64_t f9g3_38 = f9_2 * static_cast<int64_t>(g3_19);
+ const int64_t f9g4_19 = f9 * static_cast<int64_t>(g4_19);
+ const int64_t f9g5_38 = f9_2 * static_cast<int64_t>(g5_19);
+ const int64_t f9g6_19 = f9 * static_cast<int64_t>(g6_19);
+ const int64_t f9g7_38 = f9_2 * static_cast<int64_t>(g7_19);
+ const int64_t f9g8_19 = f9 * static_cast<int64_t>(g8_19);
+ const int64_t f9g9_38 = f9_2 * static_cast<int64_t>(g9_19);
+
+ int64_t h0 = f0g0+f1g9_38+f2g8_19+f3g7_38+f4g6_19+f5g5_38+f6g4_19+f7g3_38+f8g2_19+f9g1_38;
+ int64_t h1 = f0g1+f1g0 +f2g9_19+f3g8_19+f4g7_19+f5g6_19+f6g5_19+f7g4_19+f8g3_19+f9g2_19;
+ int64_t h2 = f0g2+f1g1_2 +f2g0 +f3g9_38+f4g8_19+f5g7_38+f6g6_19+f7g5_38+f8g4_19+f9g3_38;
+ int64_t h3 = f0g3+f1g2 +f2g1 +f3g0 +f4g9_19+f5g8_19+f6g7_19+f7g6_19+f8g5_19+f9g4_19;
+ int64_t h4 = f0g4+f1g3_2 +f2g2 +f3g1_2 +f4g0 +f5g9_38+f6g8_19+f7g7_38+f8g6_19+f9g5_38;
+ int64_t h5 = f0g5+f1g4 +f2g3 +f3g2 +f4g1 +f5g0 +f6g9_19+f7g8_19+f8g7_19+f9g6_19;
+ int64_t h6 = f0g6+f1g5_2 +f2g4 +f3g3_2 +f4g2 +f5g1_2 +f6g0 +f7g9_38+f8g8_19+f9g7_38;
+ int64_t h7 = f0g7+f1g6 +f2g5 +f3g4 +f4g3 +f5g2 +f6g1 +f7g0 +f8g9_19+f9g8_19;
+ int64_t h8 = f0g8+f1g7_2 +f2g6 +f3g5_2 +f4g4 +f5g3_2 +f6g2 +f7g1_2 +f8g0 +f9g9_38;
+ int64_t h9 = f0g9+f1g8 +f2g7 +f3g6 +f4g5 +f5g4 +f6g3 +f7g2 +f8g1 +f9g0 ;
+
+ /*
+ |h0| <= (1.65*1.65*2^52*(1+19+19+19+19)+1.65*1.65*2^50*(38+38+38+38+38))
+ i.e. |h0| <= 1.4*2^60; narrower ranges for h2, h4, h6, h8
+ |h1| <= (1.65*1.65*2^51*(1+1+19+19+19+19+19+19+19+19))
+ i.e. |h1| <= 1.7*2^59; narrower ranges for h3, h5, h7, h9
+ */
+ carry<26>(h0, h1);
+ carry<26>(h4, h5);
+
+ /* |h0| <= 2^25 */
+ /* |h4| <= 2^25 */
+ /* |h1| <= 1.71*2^59 */
+ /* |h5| <= 1.71*2^59 */
+
+ carry<25>(h1, h2);
+ carry<25>(h5, h6);
+
+ /* |h1| <= 2^24; from now on fits into int32 */
+ /* |h5| <= 2^24; from now on fits into int32 */
+ /* |h2| <= 1.41*2^60 */
+ /* |h6| <= 1.41*2^60 */
+
+ carry<26>(h2, h3);
+ carry<26>(h6, h7);
+ /* |h2| <= 2^25; from now on fits into int32 unchanged */
+ /* |h6| <= 2^25; from now on fits into int32 unchanged */
+ /* |h3| <= 1.71*2^59 */
+ /* |h7| <= 1.71*2^59 */
+
+ carry<25>(h3, h4);
+ carry<25>(h7, h8);
+ /* |h3| <= 2^24; from now on fits into int32 unchanged */
+ /* |h7| <= 2^24; from now on fits into int32 unchanged */
+ /* |h4| <= 1.72*2^34 */
+ /* |h8| <= 1.41*2^60 */
+
+ carry<26>(h4, h5);
+ carry<26>(h8, h9);
+ /* |h4| <= 2^25; from now on fits into int32 unchanged */
+ /* |h8| <= 2^25; from now on fits into int32 unchanged */
+ /* |h5| <= 1.01*2^24 */
+ /* |h9| <= 1.71*2^59 */
+
+ carry<25, 19>(h9, h0);
+
+ /* |h9| <= 2^24; from now on fits into int32 unchanged */
+ /* |h0| <= 1.1*2^39 */
+
+ carry<26>(h0, h1);
+ /* |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);
+ }
+
+/*
+h = f * f
+Can overlap h with f.
+
+Preconditions:
+|f| bounded by 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc.
+
+Postconditions:
+|h| bounded by 1.01*2^25,1.01*2^24,1.01*2^25,1.01*2^24,etc.
+*/
+
+/*
+See fe_mul.c for discussion of implementation strategy.
+*/
+
+//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];
+
+ for(size_t i = 0; i != iter; ++i)
+ {
+ const int32_t f0_2 = 2 * f0;
+ const int32_t f1_2 = 2 * f1;
+ const int32_t f2_2 = 2 * f2;
+ const int32_t f3_2 = 2 * f3;
+ const int32_t f4_2 = 2 * f4;
+ const int32_t f5_2 = 2 * f5;
+ const int32_t f6_2 = 2 * f6;
+ const int32_t f7_2 = 2 * f7;
+ const int32_t f5_38 = 38 * f5; /* 1.959375*2^30 */
+ const int32_t f6_19 = 19 * f6; /* 1.959375*2^30 */
+ const int32_t f7_38 = 38 * f7; /* 1.959375*2^30 */
+ const int32_t f8_19 = 19 * f8; /* 1.959375*2^30 */
+ const int32_t f9_38 = 38 * f9; /* 1.959375*2^30 */
+
+ const int64_t f0f0 = f0 * static_cast<int64_t>(f0);
+ const int64_t f0f1_2 = f0_2 * static_cast<int64_t>(f1);
+ const int64_t f0f2_2 = f0_2 * static_cast<int64_t>(f2);
+ const int64_t f0f3_2 = f0_2 * static_cast<int64_t>(f3);
+ const int64_t f0f4_2 = f0_2 * static_cast<int64_t>(f4);
+ const int64_t f0f5_2 = f0_2 * static_cast<int64_t>(f5);
+ const int64_t f0f6_2 = f0_2 * static_cast<int64_t>(f6);
+ const int64_t f0f7_2 = f0_2 * static_cast<int64_t>(f7);
+ const int64_t f0f8_2 = f0_2 * static_cast<int64_t>(f8);
+ const int64_t f0f9_2 = f0_2 * static_cast<int64_t>(f9);
+ const int64_t f1f1_2 = f1_2 * static_cast<int64_t>(f1);
+ const int64_t f1f2_2 = f1_2 * static_cast<int64_t>(f2);
+ const int64_t f1f3_4 = f1_2 * static_cast<int64_t>(f3_2);
+ const int64_t f1f4_2 = f1_2 * static_cast<int64_t>(f4);
+ const int64_t f1f5_4 = f1_2 * static_cast<int64_t>(f5_2);
+ const int64_t f1f6_2 = f1_2 * static_cast<int64_t>(f6);
+ const int64_t f1f7_4 = f1_2 * static_cast<int64_t>(f7_2);
+ const int64_t f1f8_2 = f1_2 * static_cast<int64_t>(f8);
+ const int64_t f1f9_76 = f1_2 * static_cast<int64_t>(f9_38);
+ const int64_t f2f2 = f2 * static_cast<int64_t>(f2);
+ const int64_t f2f3_2 = f2_2 * static_cast<int64_t>(f3);
+ const int64_t f2f4_2 = f2_2 * static_cast<int64_t>(f4);
+ const int64_t f2f5_2 = f2_2 * static_cast<int64_t>(f5);
+ const int64_t f2f6_2 = f2_2 * static_cast<int64_t>(f6);
+ const int64_t f2f7_2 = f2_2 * static_cast<int64_t>(f7);
+ const int64_t f2f8_38 = f2_2 * static_cast<int64_t>(f8_19);
+ const int64_t f2f9_38 = f2 * static_cast<int64_t>(f9_38);
+ const int64_t f3f3_2 = f3_2 * static_cast<int64_t>(f3);
+ const int64_t f3f4_2 = f3_2 * static_cast<int64_t>(f4);
+ const int64_t f3f5_4 = f3_2 * static_cast<int64_t>(f5_2);
+ const int64_t f3f6_2 = f3_2 * static_cast<int64_t>(f6);
+ const int64_t f3f7_76 = f3_2 * static_cast<int64_t>(f7_38);
+ const int64_t f3f8_38 = f3_2 * static_cast<int64_t>(f8_19);
+ const int64_t f3f9_76 = f3_2 * static_cast<int64_t>(f9_38);
+ const int64_t f4f4 = f4 * static_cast<int64_t>(f4);
+ const int64_t f4f5_2 = f4_2 * static_cast<int64_t>(f5);
+ const int64_t f4f6_38 = f4_2 * static_cast<int64_t>(f6_19);
+ const int64_t f4f7_38 = f4 * static_cast<int64_t>(f7_38);
+ const int64_t f4f8_38 = f4_2 * static_cast<int64_t>(f8_19);
+ const int64_t f4f9_38 = f4 * static_cast<int64_t>(f9_38);
+ const int64_t f5f5_38 = f5 * static_cast<int64_t>(f5_38);
+ const int64_t f5f6_38 = f5_2 * static_cast<int64_t>(f6_19);
+ const int64_t f5f7_76 = f5_2 * static_cast<int64_t>(f7_38);
+ const int64_t f5f8_38 = f5_2 * static_cast<int64_t>(f8_19);
+ const int64_t f5f9_76 = f5_2 * static_cast<int64_t>(f9_38);
+ const int64_t f6f6_19 = f6 * static_cast<int64_t>(f6_19);
+ const int64_t f6f7_38 = f6 * static_cast<int64_t>(f7_38);
+ const int64_t f6f8_38 = f6_2 * static_cast<int64_t>(f8_19);
+ const int64_t f6f9_38 = f6 * static_cast<int64_t>(f9_38);
+ const int64_t f7f7_38 = f7 * static_cast<int64_t>(f7_38);
+ const int64_t f7f8_38 = f7_2 * static_cast<int64_t>(f8_19);
+ const int64_t f7f9_76 = f7_2 * static_cast<int64_t>(f9_38);
+ const int64_t f8f8_19 = f8 * static_cast<int64_t>(f8_19);
+ const int64_t f8f9_38 = f8 * static_cast<int64_t>(f9_38);
+ const int64_t f9f9_38 = f9 * static_cast<int64_t>(f9_38);
+
+ int64_t h0 = f0f0 +f1f9_76+f2f8_38+f3f7_76+f4f6_38+f5f5_38;
+ int64_t h1 = f0f1_2+f2f9_38+f3f8_38+f4f7_38+f5f6_38;
+ int64_t h2 = f0f2_2+f1f1_2 +f3f9_76+f4f8_38+f5f7_76+f6f6_19;
+ int64_t h3 = f0f3_2+f1f2_2 +f4f9_38+f5f8_38+f6f7_38;
+ int64_t h4 = f0f4_2+f1f3_4 +f2f2 +f5f9_76+f6f8_38+f7f7_38;
+ int64_t h5 = f0f5_2+f1f4_2 +f2f3_2 +f6f9_38+f7f8_38;
+ int64_t h6 = f0f6_2+f1f5_4 +f2f4_2 +f3f3_2 +f7f9_76+f8f8_19;
+ int64_t h7 = f0f7_2+f1f6_2 +f2f5_2 +f3f4_2 +f8f9_38;
+ int64_t h8 = f0f8_2+f1f7_4 +f2f6_2 +f3f5_4 +f4f4 +f9f9_38;
+ int64_t h9 = f0f9_2+f1f8_2 +f2f7_2 +f3f6_2 +f4f5_2;
+
+ carry<26>(h0, h1);
+ carry<26>(h4, h5);
+ carry<25>(h1, h2);
+ carry<25>(h5, h6);
+ carry<26>(h2, h3);
+ carry<26>(h6, h7);
+
+ carry<25>(h3, h4);
+ carry<25>(h7, h8);
+
+ carry<26>(h4, h5);
+ carry<26>(h8, h9);
+ carry<25,19>(h9, h0);
+ carry<26>(h0, h1);
+
+ f0 = static_cast<int32_t>(h0);
+ f1 = static_cast<int32_t>(h1);
+ f2 = static_cast<int32_t>(h2);
+ f3 = static_cast<int32_t>(h3);
+ f4 = static_cast<int32_t>(h4);
+ f5 = static_cast<int32_t>(h5);
+ f6 = static_cast<int32_t>(h6);
+ f7 = static_cast<int32_t>(h7);
+ f8 = static_cast<int32_t>(h8);
+ f9 = static_cast<int32_t>(h9);
+ }
+
+ return FE_25519(f0, f1, f2, f3, f4, f5, f6, f7, f8, f9);
+ }
+
+/*
+h = 2 * f * f
+Can overlap h with f.
+
+Preconditions:
+|f| bounded by 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc.
+
+Postconditions:
+|h| bounded by 1.01*2^25,1.01*2^24,1.01*2^25,1.01*2^24,etc.
+*/
+
+/*
+See fe_mul.c for discussion of implementation strategy.
+*/
+
+//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];
+ const int32_t f0_2 = 2 * f0;
+ const int32_t f1_2 = 2 * f1;
+ const int32_t f2_2 = 2 * f2;
+ const int32_t f3_2 = 2 * f3;
+ const int32_t f4_2 = 2 * f4;
+ const int32_t f5_2 = 2 * f5;
+ const int32_t f6_2 = 2 * f6;
+ const int32_t f7_2 = 2 * f7;
+ const int32_t f5_38 = 38 * f5; /* 1.959375*2^30 */
+ const int32_t f6_19 = 19 * f6; /* 1.959375*2^30 */
+ const int32_t f7_38 = 38 * f7; /* 1.959375*2^30 */
+ const int32_t f8_19 = 19 * f8; /* 1.959375*2^30 */
+ const int32_t f9_38 = 38 * f9; /* 1.959375*2^30 */
+ const int64_t f0f0 = f0 * static_cast<int64_t>(f0);
+ const int64_t f0f1_2 = f0_2 * static_cast<int64_t>(f1);
+ const int64_t f0f2_2 = f0_2 * static_cast<int64_t>(f2);
+ const int64_t f0f3_2 = f0_2 * static_cast<int64_t>(f3);
+ const int64_t f0f4_2 = f0_2 * static_cast<int64_t>(f4);
+ const int64_t f0f5_2 = f0_2 * static_cast<int64_t>(f5);
+ const int64_t f0f6_2 = f0_2 * static_cast<int64_t>(f6);
+ const int64_t f0f7_2 = f0_2 * static_cast<int64_t>(f7);
+ const int64_t f0f8_2 = f0_2 * static_cast<int64_t>(f8);
+ const int64_t f0f9_2 = f0_2 * static_cast<int64_t>(f9);
+ const int64_t f1f1_2 = f1_2 * static_cast<int64_t>(f1);
+ const int64_t f1f2_2 = f1_2 * static_cast<int64_t>(f2);
+ const int64_t f1f3_4 = f1_2 * static_cast<int64_t>(f3_2);
+ const int64_t f1f4_2 = f1_2 * static_cast<int64_t>(f4);
+ const int64_t f1f5_4 = f1_2 * static_cast<int64_t>(f5_2);
+ const int64_t f1f6_2 = f1_2 * static_cast<int64_t>(f6);
+ const int64_t f1f7_4 = f1_2 * static_cast<int64_t>(f7_2);
+ const int64_t f1f8_2 = f1_2 * static_cast<int64_t>(f8);
+ const int64_t f1f9_76 = f1_2 * static_cast<int64_t>(f9_38);
+ const int64_t f2f2 = f2 * static_cast<int64_t>(f2);
+ const int64_t f2f3_2 = f2_2 * static_cast<int64_t>(f3);
+ const int64_t f2f4_2 = f2_2 * static_cast<int64_t>(f4);
+ const int64_t f2f5_2 = f2_2 * static_cast<int64_t>(f5);
+ const int64_t f2f6_2 = f2_2 * static_cast<int64_t>(f6);
+ const int64_t f2f7_2 = f2_2 * static_cast<int64_t>(f7);
+ const int64_t f2f8_38 = f2_2 * static_cast<int64_t>(f8_19);
+ const int64_t f2f9_38 = f2 * static_cast<int64_t>(f9_38);
+ const int64_t f3f3_2 = f3_2 * static_cast<int64_t>(f3);
+ const int64_t f3f4_2 = f3_2 * static_cast<int64_t>(f4);
+ const int64_t f3f5_4 = f3_2 * static_cast<int64_t>(f5_2);
+ const int64_t f3f6_2 = f3_2 * static_cast<int64_t>(f6);
+ const int64_t f3f7_76 = f3_2 * static_cast<int64_t>(f7_38);
+ const int64_t f3f8_38 = f3_2 * static_cast<int64_t>(f8_19);
+ const int64_t f3f9_76 = f3_2 * static_cast<int64_t>(f9_38);
+ const int64_t f4f4 = f4 * static_cast<int64_t>(f4);
+ const int64_t f4f5_2 = f4_2 * static_cast<int64_t>(f5);
+ const int64_t f4f6_38 = f4_2 * static_cast<int64_t>(f6_19);
+ const int64_t f4f7_38 = f4 * static_cast<int64_t>(f7_38);
+ const int64_t f4f8_38 = f4_2 * static_cast<int64_t>(f8_19);
+ const int64_t f4f9_38 = f4 * static_cast<int64_t>(f9_38);
+ const int64_t f5f5_38 = f5 * static_cast<int64_t>(f5_38);
+ const int64_t f5f6_38 = f5_2 * static_cast<int64_t>(f6_19);
+ const int64_t f5f7_76 = f5_2 * static_cast<int64_t>(f7_38);
+ const int64_t f5f8_38 = f5_2 * static_cast<int64_t>(f8_19);
+ const int64_t f5f9_76 = f5_2 * static_cast<int64_t>(f9_38);
+ const int64_t f6f6_19 = f6 * static_cast<int64_t>(f6_19);
+ const int64_t f6f7_38 = f6 * static_cast<int64_t>(f7_38);
+ const int64_t f6f8_38 = f6_2 * static_cast<int64_t>(f8_19);
+ const int64_t f6f9_38 = f6 * static_cast<int64_t>(f9_38);
+ const int64_t f7f7_38 = f7 * static_cast<int64_t>(f7_38);
+ const int64_t f7f8_38 = f7_2 * static_cast<int64_t>(f8_19);
+ const int64_t f7f9_76 = f7_2 * static_cast<int64_t>(f9_38);
+ const int64_t f8f8_19 = f8 * static_cast<int64_t>(f8_19);
+ const int64_t f8f9_38 = f8 * static_cast<int64_t>(f9_38);
+ const int64_t f9f9_38 = f9 * static_cast<int64_t>(f9_38);
+
+ int64_t h0 = f0f0 +f1f9_76+f2f8_38+f3f7_76+f4f6_38+f5f5_38;
+ int64_t h1 = f0f1_2+f2f9_38+f3f8_38+f4f7_38+f5f6_38;
+ int64_t h2 = f0f2_2+f1f1_2 +f3f9_76+f4f8_38+f5f7_76+f6f6_19;
+ int64_t h3 = f0f3_2+f1f2_2 +f4f9_38+f5f8_38+f6f7_38;
+ int64_t h4 = f0f4_2+f1f3_4 +f2f2 +f5f9_76+f6f8_38+f7f7_38;
+ int64_t h5 = f0f5_2+f1f4_2 +f2f3_2 +f6f9_38+f7f8_38;
+ int64_t h6 = f0f6_2+f1f5_4 +f2f4_2 +f3f3_2 +f7f9_76+f8f8_19;
+ int64_t h7 = f0f7_2+f1f6_2 +f2f5_2 +f3f4_2 +f8f9_38;
+ int64_t h8 = f0f8_2+f1f7_4 +f2f6_2 +f3f5_4 +f4f4 +f9f9_38;
+ int64_t h9 = f0f9_2+f1f8_2 +f2f7_2 +f3f6_2 +f4f5_2;
+
+ h0 += h0;
+ h1 += h1;
+ h2 += h2;
+ h3 += h3;
+ h4 += h4;
+ h5 += h5;
+ h6 += h6;
+ h7 += h7;
+ h8 += h8;
+ h9 += h9;
+
+ carry<26>(h0, h1);
+ carry<26>(h4, h5);
+
+ carry<25>(h1, h2);
+ carry<25>(h5, h6);
+
+ carry<26>(h2, h3);
+ carry<26>(h6, h7);
+
+ carry<25>(h3, h4);
+ carry<25>(h7, h8);
+ carry<26>(h4, h5);
+ carry<26>(h8, h9);
+ carry<25,19>(h9, h0);
+ carry<26>(h0, h1);
+
+ return FE_25519(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])
+ {
+ int64_t h0 = load_4(s);
+ int64_t h1 = load_3(s + 4) << 6;
+ int64_t h2 = load_3(s + 7) << 5;
+ int64_t h3 = load_3(s + 10) << 3;
+ int64_t h4 = load_3(s + 13) << 2;
+ int64_t h5 = load_4(s + 16);
+ int64_t h6 = load_3(s + 20) << 7;
+ int64_t h7 = load_3(s + 23) << 5;
+ int64_t h8 = load_3(s + 26) << 4;
+ int64_t h9 = (load_3(s + 29) & 0x7fffff) << 2;
+
+ carry<25,19>(h9, h0);
+ carry<25>(h1, h2);
+ carry<25>(h3, h4);
+ carry<25>(h5, h6);
+ carry<25>(h7, h8);
+
+ carry<26>(h0, h1);
+ carry<26>(h2, h3);
+ carry<26>(h4, h5);
+ carry<26>(h6, h7);
+ carry<26>(h8, h9);
+
+ m_fe[0] = static_cast<int32_t>(h0);
+ m_fe[1] = static_cast<int32_t>(h1);
+ m_fe[2] = static_cast<int32_t>(h2);
+ m_fe[3] = static_cast<int32_t>(h3);
+ m_fe[4] = static_cast<int32_t>(h4);
+ m_fe[5] = static_cast<int32_t>(h5);
+ m_fe[6] = static_cast<int32_t>(h6);
+ m_fe[7] = static_cast<int32_t>(h7);
+ m_fe[8] = static_cast<int32_t>(h8);
+ m_fe[9] = static_cast<int32_t>(h9);
+ }
+
+/*
+Preconditions:
+|h| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc.
+
+Write p=2^255-19; q=floor(h/p).
+Basic claim: q = floor(2^(-255)(h + 19 2^(-25)h9 + 2^(-1))).
+
+Proof:
+Have |h|<=p so |q|<=1 so |19^2 2^(-255) q|<1/4.
+Also have |h-2^230 h9|<2^231 so |19 2^(-255)(h-2^230 h9)|<1/4.
+
+Write y=2^(-1)-19^2 2^(-255)q-19 2^(-255)(h-2^230 h9).
+Then 0<y<1.
+
+Write r=h-pq.
+Have 0<=r<=p-1=2^255-20.
+Thus 0<=r+19(2^-255)r<r+19(2^-255)2^255<=2^255-1.
+
+Write x=r+19(2^-255)r+y.
+Then 0<x<2^255 so floor(2^(-255)x) = 0 so floor(q+2^(-255)x) = q.
+
+Have q+2^(-255)x = 2^(-255)(h + 19 2^(-25) h9 + 2^(-1))
+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);
+
+ int32_t h0 = m_fe[0];
+ int32_t h1 = m_fe[1];
+ int32_t h2 = m_fe[2];
+ int32_t h3 = m_fe[3];
+ int32_t h4 = m_fe[4];
+ int32_t h5 = m_fe[5];
+ int32_t h6 = m_fe[6];
+ 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<int32_t>(1) << 24))) >> 25;
+ q = (h0 + q) >> 26;
+ q = (h1 + q) >> 25;
+ q = (h2 + q) >> 26;
+ q = (h3 + q) >> 25;
+ q = (h4 + q) >> 26;
+ q = (h5 + q) >> 25;
+ q = (h6 + q) >> 26;
+ q = (h7 + q) >> 25;
+ q = (h8 + q) >> 26;
+ q = (h9 + q) >> 25;
+
+ /* Goal: Output h-(2^255-19)q, which is between 0 and 2^255-20. */
+ h0 += 19 * q;
+ /* Goal: Output h-2^255 q, which is between 0 and 2^255-20. */
+
+ carry0<26>(h0, h1);
+ carry0<25>(h1, h2);
+ carry0<26>(h2, h3);
+ carry0<25>(h3, h4);
+ carry0<26>(h4, h5);
+ carry0<25>(h5, h6);
+ carry0<26>(h6, h7);
+ carry0<25>(h7, h8);
+ carry0<26>(h8, h9);
+
+ int32_t carry9 = h9 >> 25;
+ h9 -= carry9 * X25;
+ /* h10 = carry9 */
+
+ /*
+ Goal: Output h0+...+2^255 h10-2^255 q, which is between 0 and 2^255-20.
+ Have h0+...+2^230 h9 between 0 and 2^255-1;
+ evidently 2^255 h10-2^255 q = 0.
+ Goal: Output h0+...+2^230 h9.
+ */
+
+ s[0] = static_cast<uint8_t>(h0 >> 0);
+ s[1] = static_cast<uint8_t>(h0 >> 8);
+ s[2] = static_cast<uint8_t>(h0 >> 16);
+ s[3] = static_cast<uint8_t>((h0 >> 24) | (h1 << 2));
+ s[4] = static_cast<uint8_t>(h1 >> 6);
+ s[5] = static_cast<uint8_t>(h1 >> 14);
+ s[6] = static_cast<uint8_t>((h1 >> 22) | (h2 << 3));
+ s[7] = static_cast<uint8_t>(h2 >> 5);
+ s[8] = static_cast<uint8_t>(h2 >> 13);
+ s[9] = static_cast<uint8_t>((h2 >> 21) | (h3 << 5));
+ s[10] = static_cast<uint8_t>(h3 >> 3);
+ s[11] = static_cast<uint8_t>(h3 >> 11);
+ s[12] = static_cast<uint8_t>((h3 >> 19) | (h4 << 6));
+ s[13] = static_cast<uint8_t>(h4 >> 2);
+ s[14] = static_cast<uint8_t>(h4 >> 10);
+ s[15] = static_cast<uint8_t>(h4 >> 18);
+ s[16] = static_cast<uint8_t>(h5 >> 0);
+ s[17] = static_cast<uint8_t>(h5 >> 8);
+ s[18] = static_cast<uint8_t>(h5 >> 16);
+ s[19] = static_cast<uint8_t>((h5 >> 24) | (h6 << 1));
+ s[20] = static_cast<uint8_t>(h6 >> 7);
+ s[21] = static_cast<uint8_t>(h6 >> 15);
+ s[22] = static_cast<uint8_t>((h6 >> 23) | (h7 << 3));
+ s[23] = static_cast<uint8_t>(h7 >> 5);
+ s[24] = static_cast<uint8_t>(h7 >> 13);
+ s[25] = static_cast<uint8_t>((h7 >> 21) | (h8 << 4));
+ s[26] = static_cast<uint8_t>(h8 >> 4);
+ s[27] = static_cast<uint8_t>(h8 >> 12);
+ s[28] = static_cast<uint8_t>((h8 >> 20) | (h9 << 6));
+ s[29] = static_cast<uint8_t>(h9 >> 2);
+ s[30] = static_cast<uint8_t>(h9 >> 10);
+ s[31] = static_cast<uint8_t>(h9 >> 18);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/pubkey/ed25519/ed25519_fe.h b/comm/third_party/botan/src/lib/pubkey/ed25519/ed25519_fe.h
new file mode 100644
index 0000000000..bcdc36a5e2
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/ed25519/ed25519_fe.h
@@ -0,0 +1,227 @@
+/*
+* Ed25519 field element
+* (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
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_ED25519_FE_H_
+#define BOTAN_ED25519_FE_H_
+
+#include <botan/mem_ops.h>
+#include <botan/exceptn.h>
+
+namespace Botan {
+
+/**
+* An element of the field \\Z/(2^255-19)
+*/
+class FE_25519
+ {
+ public:
+ ~FE_25519() { secure_scrub_memory(m_fe, sizeof(m_fe)); }
+
+ /**
+ * Zero element
+ */
+ 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;
+ }
+
+ FE_25519(std::initializer_list<int32_t> x)
+ {
+ if(x.size() != 10)
+ throw Invalid_Argument("Invalid FE_25519 initializer list");
+ 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)
+ {
+ m_fe[0] = static_cast<int32_t>(h0);
+ m_fe[1] = static_cast<int32_t>(h1);
+ m_fe[2] = static_cast<int32_t>(h2);
+ m_fe[3] = static_cast<int32_t>(h3);
+ m_fe[4] = static_cast<int32_t>(h4);
+ m_fe[5] = static_cast<int32_t>(h5);
+ m_fe[6] = static_cast<int32_t>(h6);
+ m_fe[7] = static_cast<int32_t>(h7);
+ m_fe[8] = static_cast<int32_t>(h8);
+ m_fe[9] = static_cast<int32_t>(h9);
+ }
+
+ FE_25519(const FE_25519& other) = default;
+ FE_25519& operator=(const FE_25519& other) = default;
+
+ 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;
+
+ 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]; }
+
+ // TODO avoid ternary here
+ return (sum == 0) ? 1 : 0;
+ }
+
+ /*
+ return 1 if f is in {1,3,5,...,q-2}
+ 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;
+ }
+
+ static FE_25519 add(const FE_25519& a, const FE_25519& b)
+ {
+ FE_25519 z;
+ for(size_t i = 0; i != 10; ++i)
+ { z[i] = a[i] + b[i]; }
+ return z;
+ }
+
+ static FE_25519 sub(const FE_25519& a, const FE_25519& b)
+ {
+ FE_25519 z;
+ for(size_t i = 0; i != 10; ++i)
+ { z[i] = a[i] - b[i]; }
+ return z;
+ }
+
+ static FE_25519 negate(const FE_25519& a)
+ {
+ FE_25519 z;
+ for(size_t i = 0; i != 10; ++i)
+ { z[i] = -a[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 FE_25519 sqr(const FE_25519& a) { return sqr_iter(a, 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);
+
+ // TODO remove
+ int32_t operator[](size_t i) const { return m_fe[i]; }
+ int32_t& operator[](size_t i) { return m_fe[i]; }
+
+ private:
+
+ int32_t m_fe[10];
+ };
+
+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 void fe_sq2(fe& x, const fe& z)
+ {
+ x = FE_25519::sqr2(z);
+ }
+
+inline void fe_invert(fe& x, const fe& z)
+ {
+ x = FE_25519::invert(z);
+ }
+
+inline void fe_pow22523(fe& x, const fe& y)
+ {
+ x = FE_25519::pow_22523(y);
+ }
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/pubkey/ed25519/ed25519_internal.h b/comm/third_party/botan/src/lib/pubkey/ed25519/ed25519_internal.h
new file mode 100644
index 0000000000..cb67a43fd2
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/ed25519/ed25519_internal.h
@@ -0,0 +1,119 @@
+/*
+* 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
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_ED25519_INT_H_
+#define BOTAN_ED25519_INT_H_
+
+#include <botan/internal/ed25519_fe.h>
+#include <botan/loadstor.h>
+
+namespace Botan {
+
+inline uint64_t load_3(const uint8_t in[3])
+ {
+ return static_cast<uint64_t>(in[0]) |
+ (static_cast<uint64_t>(in[1]) << 8) |
+ (static_cast<uint64_t>(in[2]) << 16);
+ }
+
+inline uint64_t load_4(const uint8_t* in)
+ {
+ return load_le<uint32_t>(in, 0);
+ }
+
+template<size_t S, int64_t MUL=1>
+inline void carry(int64_t& h0, int64_t& h1)
+ {
+ static_assert(S > 0 && S < 64, "Shift in range");
+
+ const int64_t X1 = (static_cast<int64_t>(1) << S);
+ const int64_t X2 = (static_cast<int64_t>(1) << (S - 1));
+ int64_t c = (h0 + X2) >> S;
+ h1 += c * MUL;
+ h0 -= c * X1;
+ }
+
+template<size_t S>
+inline void carry0(int64_t& h0, int64_t& h1)
+ {
+ static_assert(S > 0 && S < 64, "Shift in range");
+
+ const int64_t X1 = (static_cast<int64_t>(1) << S);
+ int64_t c = h0 >> S;
+ h1 += c;
+ h0 -= c * X1;
+ }
+
+template<size_t S>
+inline void carry0(int32_t& h0, int32_t& h1)
+ {
+ static_assert(S > 0 && S < 32, "Shift in range");
+
+ const int32_t X1 = (static_cast<int64_t>(1) << S);
+ int32_t c = h0 >> S;
+ h1 += c;
+ h0 -= c * X1;
+ }
+
+inline void redc_mul(int64_t& s1,
+ int64_t& s2,
+ int64_t& s3,
+ int64_t& s4,
+ int64_t& s5,
+ int64_t& s6,
+ int64_t& X)
+ {
+ s1 += X * 666643;
+ s2 += X * 470296;
+ s3 += X * 654183;
+ s4 -= X * 997805;
+ s5 += X * 136657;
+ s6 -= X * 683901;
+ 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
+*/
+
+typedef struct
+ {
+ fe X;
+ fe Y;
+ fe Z;
+ fe T;
+ } ge_p3;
+
+int ge_frombytes_negate_vartime(ge_p3*, const uint8_t*);
+void ge_scalarmult_base(uint8_t out[32], 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[]);
+
+/*
+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*);
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/pubkey/ed25519/ed25519_key.cpp b/comm/third_party/botan/src/lib/pubkey/ed25519/ed25519_key.cpp
new file mode 100644
index 0000000000..c4b260c4cc
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/ed25519/ed25519_key.cpp
@@ -0,0 +1,288 @@
+/*
+* 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
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/ed25519.h>
+#include <botan/internal/pk_ops_impl.h>
+#include <botan/hash.h>
+#include <botan/ber_dec.h>
+#include <botan/der_enc.h>
+#include <botan/rng.h>
+
+namespace Botan {
+
+AlgorithmIdentifier Ed25519_PublicKey::algorithm_identifier() const
+ {
+ return AlgorithmIdentifier(get_oid(), AlgorithmIdentifier::USE_EMPTY_PARAM);
+ }
+
+bool Ed25519_PublicKey::check_key(RandomNumberGenerator&, bool) const
+ {
+ return true; // no tests possible?
+ // TODO could check cofactor
+ }
+
+Ed25519_PublicKey::Ed25519_PublicKey(const uint8_t pub_key[], size_t pub_len)
+ {
+ if(pub_len != 32)
+ throw Decoding_Error("Invalid length for Ed25519 key");
+ m_public.assign(pub_key, pub_key + pub_len);
+ }
+
+Ed25519_PublicKey::Ed25519_PublicKey(const AlgorithmIdentifier&,
+ const std::vector<uint8_t>& key_bits)
+ {
+ m_public = key_bits;
+
+ if(m_public.size() != 32)
+ throw Decoding_Error("Invalid size for Ed25519 public key");
+ }
+
+std::vector<uint8_t> Ed25519_PublicKey::public_key_bits() const
+ {
+ return m_public;
+ }
+
+Ed25519_PrivateKey::Ed25519_PrivateKey(const secure_vector<uint8_t>& secret_key)
+ {
+ if(secret_key.size() == 64)
+ {
+ m_private = secret_key;
+ m_public.assign(m_private.begin() + 32, m_private.end());
+ }
+ else if(secret_key.size() == 32)
+ {
+ m_public.resize(32);
+ m_private.resize(64);
+ ed25519_gen_keypair(m_public.data(), m_private.data(), secret_key.data());
+ }
+ else
+ throw Decoding_Error("Invalid size for Ed25519 private key");
+ }
+
+Ed25519_PrivateKey::Ed25519_PrivateKey(RandomNumberGenerator& rng)
+ {
+ const secure_vector<uint8_t> seed = rng.random_vec(32);
+ m_public.resize(32);
+ m_private.resize(64);
+ ed25519_gen_keypair(m_public.data(), m_private.data(), seed.data());
+ }
+
+Ed25519_PrivateKey::Ed25519_PrivateKey(const AlgorithmIdentifier&,
+ const secure_vector<uint8_t>& key_bits)
+ {
+ secure_vector<uint8_t> bits;
+ BER_Decoder(key_bits).decode(bits, OCTET_STRING).discard_remaining();
+
+ if(bits.size() != 32)
+ throw Decoding_Error("Invalid size for Ed25519 private key");
+ m_public.resize(32);
+ m_private.resize(64);
+ ed25519_gen_keypair(m_public.data(), m_private.data(), bits.data());
+ }
+
+secure_vector<uint8_t> Ed25519_PrivateKey::private_key_bits() const
+ {
+ secure_vector<uint8_t> bits(&m_private[0], &m_private[32]);
+ return DER_Encoder().encode(bits, OCTET_STRING).get_contents();
+ }
+
+bool Ed25519_PrivateKey::check_key(RandomNumberGenerator&, bool) const
+ {
+ return true; // ???
+ }
+
+namespace {
+
+/**
+* Ed25519 verifying operation
+*/
+class Ed25519_Pure_Verify_Operation final : public PK_Ops::Verification
+ {
+ public:
+ Ed25519_Pure_Verify_Operation(const Ed25519_PublicKey& key) : m_key(key)
+ {
+ }
+
+ void update(const uint8_t msg[], size_t msg_len) override
+ {
+ m_msg.insert(m_msg.end(), msg, msg + msg_len);
+ }
+
+ bool is_valid_signature(const uint8_t sig[], size_t sig_len) override
+ {
+ if(sig_len != 64)
+ return false;
+
+ const std::vector<uint8_t>& pub_key = m_key.get_public_key();
+ BOTAN_ASSERT_EQUAL(pub_key.size(), 32, "Expected size");
+ const bool ok = ed25519_verify(m_msg.data(), m_msg.size(), sig, pub_key.data(), nullptr, 0);
+ m_msg.clear();
+ return ok;
+ }
+
+ private:
+ std::vector<uint8_t> m_msg;
+ const Ed25519_PublicKey& m_key;
+ };
+
+/**
+* Ed25519 verifying operation with pre-hash
+*/
+class Ed25519_Hashed_Verify_Operation final : public PK_Ops::Verification
+ {
+ public:
+ Ed25519_Hashed_Verify_Operation(const Ed25519_PublicKey& key, const std::string& hash, bool rfc8032) :
+ m_key(key)
+ {
+ m_hash = HashFunction::create_or_throw(hash);
+
+ 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, 0x6C, 0x6C, 0x69, 0x73, 0x69, 0x6F, 0x6E, 0x73,
+ 0x01, 0x00 };
+ }
+ }
+
+ void update(const uint8_t msg[], size_t msg_len) override
+ {
+ m_hash->update(msg, msg_len);
+ }
+
+ bool is_valid_signature(const uint8_t sig[], size_t sig_len) override
+ {
+ if(sig_len != 64)
+ return false;
+ std::vector<uint8_t> msg_hash(m_hash->output_length());
+ m_hash->final(msg_hash.data());
+
+ const std::vector<uint8_t>& pub_key = m_key.get_public_key();
+ BOTAN_ASSERT_EQUAL(pub_key.size(), 32, "Expected size");
+ return ed25519_verify(msg_hash.data(), msg_hash.size(), sig, pub_key.data(), m_domain_sep.data(), m_domain_sep.size());
+ }
+
+ private:
+ std::unique_ptr<HashFunction> m_hash;
+ const Ed25519_PublicKey& m_key;
+ std::vector<uint8_t> m_domain_sep;
+ };
+
+/**
+* Ed25519 signing operation ('pure' - signs message directly)
+*/
+class Ed25519_Pure_Sign_Operation final : public PK_Ops::Signature
+ {
+ public:
+ Ed25519_Pure_Sign_Operation(const Ed25519_PrivateKey& key) : m_key(key)
+ {
+ }
+
+ void update(const uint8_t msg[], size_t msg_len) override
+ {
+ m_msg.insert(m_msg.end(), msg, msg + msg_len);
+ }
+
+ secure_vector<uint8_t> sign(RandomNumberGenerator&) override
+ {
+ secure_vector<uint8_t> sig(64);
+ ed25519_sign(sig.data(), m_msg.data(), m_msg.size(), m_key.get_private_key().data(), nullptr, 0);
+ m_msg.clear();
+ return sig;
+ }
+
+ size_t signature_length() const override { return 64; }
+
+ private:
+ std::vector<uint8_t> m_msg;
+ const Ed25519_PrivateKey& m_key;
+ };
+
+/**
+* Ed25519 signing operation with pre-hash
+*/
+class Ed25519_Hashed_Sign_Operation final : public PK_Ops::Signature
+ {
+ public:
+ Ed25519_Hashed_Sign_Operation(const Ed25519_PrivateKey& key, const std::string& hash, bool rfc8032) :
+ m_key(key)
+ {
+ m_hash = HashFunction::create_or_throw(hash);
+
+ if(rfc8032)
+ {
+ m_domain_sep = std::vector<uint8_t>{
+ 0x53, 0x69, 0x67, 0x45, 0x64, 0x32, 0x35, 0x35, 0x31, 0x39, 0x20, 0x6E, 0x6F, 0x20, 0x45, 0x64,
+ 0x32, 0x35, 0x35, 0x31, 0x39, 0x20, 0x63, 0x6F, 0x6C, 0x6C, 0x69, 0x73, 0x69, 0x6F, 0x6E, 0x73,
+ 0x01, 0x00 };
+ }
+ }
+
+ size_t signature_length() const override { return 64; }
+
+ void update(const uint8_t msg[], size_t msg_len) override
+ {
+ m_hash->update(msg, msg_len);
+ }
+
+ secure_vector<uint8_t> sign(RandomNumberGenerator&) override
+ {
+ secure_vector<uint8_t> sig(64);
+ std::vector<uint8_t> msg_hash(m_hash->output_length());
+ m_hash->final(msg_hash.data());
+ ed25519_sign(sig.data(),
+ msg_hash.data(), msg_hash.size(),
+ m_key.get_private_key().data(),
+ m_domain_sep.data(), m_domain_sep.size());
+ return sig;
+ }
+
+ private:
+ std::unique_ptr<HashFunction> m_hash;
+ const Ed25519_PrivateKey& m_key;
+ std::vector<uint8_t> m_domain_sep;
+ };
+
+}
+
+std::unique_ptr<PK_Ops::Verification>
+Ed25519_PublicKey::create_verification_op(const std::string& params,
+ const std::string& provider) const
+ {
+ if(provider == "base" || provider.empty())
+ {
+ if(params == "" || params == "Identity" || params == "Pure")
+ return std::unique_ptr<PK_Ops::Verification>(new Ed25519_Pure_Verify_Operation(*this));
+ else if(params == "Ed25519ph")
+ return std::unique_ptr<PK_Ops::Verification>(new Ed25519_Hashed_Verify_Operation(*this, "SHA-512", true));
+ else
+ return std::unique_ptr<PK_Ops::Verification>(new Ed25519_Hashed_Verify_Operation(*this, params, false));
+ }
+ throw Provider_Not_Found(algo_name(), provider);
+ }
+
+std::unique_ptr<PK_Ops::Signature>
+Ed25519_PrivateKey::create_signature_op(RandomNumberGenerator&,
+ const std::string& params,
+ const std::string& provider) const
+ {
+ if(provider == "base" || provider.empty())
+ {
+ if(params == "" || params == "Identity" || params == "Pure")
+ return std::unique_ptr<PK_Ops::Signature>(new Ed25519_Pure_Sign_Operation(*this));
+ else if(params == "Ed25519ph")
+ return std::unique_ptr<PK_Ops::Signature>(new Ed25519_Hashed_Sign_Operation(*this, "SHA-512", true));
+ else
+ return std::unique_ptr<PK_Ops::Signature>(new Ed25519_Hashed_Sign_Operation(*this, params, false));
+ }
+ throw Provider_Not_Found(algo_name(), provider);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/pubkey/ed25519/ge.cpp b/comm/third_party/botan/src/lib/pubkey/ed25519/ge.cpp
new file mode 100644
index 0000000000..7d3fae2fbc
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/ed25519/ge.cpp
@@ -0,0 +1,2174 @@
+/*
+* Ed25519 group operations
+* (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
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/ed25519_internal.h>
+#include <assert.h>
+
+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)
+*/
+typedef struct
+ {
+ fe X;
+ fe Y;
+ fe Z;
+ } ge_p2;
+
+typedef struct
+ {
+ fe X;
+ fe Y;
+ fe Z;
+ fe T;
+ } ge_p1p1;
+
+typedef struct
+ {
+ fe yplusx;
+ fe yminusx;
+ fe xy2d;
+ } ge_precomp;
+
+typedef struct
+ {
+ fe YplusX;
+ fe YminusX;
+ fe Z;
+ fe T2d;
+ } ge_cached;
+
+/*
+r = p + q
+*/
+
+void ge_add(ge_p1p1* r, const ge_p3* p, const ge_cached* q)
+ {
+ fe t0;
+ /* qhasm: YpX1 = Y1+X1 */
+ /* asm 1: fe_add(>YpX1=fe#1,<Y1=fe#12,<X1=fe#11); */
+ /* asm 2: fe_add(>YpX1=r->X,<Y1=p->Y,<X1=p->X); */
+ fe_add(r->X, p->Y, p->X);
+
+ /* qhasm: YmX1 = Y1-X1 */
+ /* asm 1: fe_sub(>YmX1=fe#2,<Y1=fe#12,<X1=fe#11); */
+ /* asm 2: fe_sub(>YmX1=r->Y,<Y1=p->Y,<X1=p->X); */
+ fe_sub(r->Y, p->Y, p->X);
+
+ /* qhasm: A = YpX1*YpX2 */
+ /* asm 1: fe_mul(>A=fe#3,<YpX1=fe#1,<YpX2=fe#15); */
+ /* asm 2: fe_mul(>A=r->Z,<YpX1=r->X,<YpX2=q->YplusX); */
+ fe_mul(r->Z, r->X, q->YplusX);
+
+ /* qhasm: B = YmX1*YmX2 */
+ /* asm 1: fe_mul(>B=fe#2,<YmX1=fe#2,<YmX2=fe#16); */
+ /* asm 2: fe_mul(>B=r->Y,<YmX1=r->Y,<YmX2=q->YminusX); */
+ fe_mul(r->Y, r->Y, q->YminusX);
+
+ /* qhasm: C = T2d2*T1 */
+ /* asm 1: fe_mul(>C=fe#4,<T2d2=fe#18,<T1=fe#14); */
+ /* asm 2: fe_mul(>C=r->T,<T2d2=q->T2d,<T1=p->T); */
+ fe_mul(r->T, q->T2d, p->T);
+
+ /* qhasm: ZZ = Z1*Z2 */
+ /* asm 1: fe_mul(>ZZ=fe#1,<Z1=fe#13,<Z2=fe#17); */
+ /* asm 2: fe_mul(>ZZ=r->X,<Z1=p->Z,<Z2=q->Z); */
+ fe_mul(r->X, p->Z, q->Z);
+
+ /* qhasm: D = 2*ZZ */
+ /* asm 1: fe_add(>D=fe#5,<ZZ=fe#1,<ZZ=fe#1); */
+ /* asm 2: fe_add(>D=t0,<ZZ=r->X,<ZZ=r->X); */
+ fe_add(t0, r->X, r->X);
+
+ /* qhasm: X3 = A-B */
+ /* asm 1: fe_sub(>X3=fe#1,<A=fe#3,<B=fe#2); */
+ /* asm 2: fe_sub(>X3=r->X,<A=r->Z,<B=r->Y); */
+ fe_sub(r->X, r->Z, r->Y);
+
+ /* qhasm: Y3 = A+B */
+ /* asm 1: fe_add(>Y3=fe#2,<A=fe#3,<B=fe#2); */
+ /* asm 2: fe_add(>Y3=r->Y,<A=r->Z,<B=r->Y); */
+ fe_add(r->Y, r->Z, r->Y);
+
+ /* qhasm: Z3 = D+C */
+ /* asm 1: fe_add(>Z3=fe#3,<D=fe#5,<C=fe#4); */
+ /* asm 2: fe_add(>Z3=r->Z,<D=t0,<C=r->T); */
+ fe_add(r->Z, t0, r->T);
+
+ /* qhasm: T3 = D-C */
+ /* asm 1: fe_sub(>T3=fe#4,<D=fe#5,<C=fe#4); */
+ /* asm 2: fe_sub(>T3=r->T,<D=t0,<C=r->T); */
+ fe_sub(r->T, t0, r->T);
+ }
+
+/*
+r = p + q
+*/
+
+void ge_madd(ge_p1p1* r, const ge_p3* p, const ge_precomp* q)
+ {
+ fe 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);
+
+ /* qhasm: X3 = A-B */
+ fe_sub(r->X, r->Z, r->Y);
+
+ /* qhasm: Y3 = A+B */
+ fe_add(r->Y, r->Z, r->Y);
+
+ /* qhasm: Z3 = D+C */
+ fe_add(r->Z, t0, r->T);
+
+ /* qhasm: T3 = D-C */
+ fe_sub(r->T, t0, r->T);
+ }
+
+/*
+r = p - q
+*/
+
+void ge_msub(ge_p1p1* r, const ge_p3* p, const ge_precomp* q)
+ {
+ fe t0;
+
+ /* qhasm: YpX1 = Y1+X1 */
+ /* asm 1: fe_add(>YpX1=fe#1,<Y1=fe#12,<X1=fe#11); */
+ /* asm 2: fe_add(>YpX1=r->X,<Y1=p->Y,<X1=p->X); */
+ fe_add(r->X, p->Y, p->X);
+
+ /* qhasm: YmX1 = Y1-X1 */
+ /* asm 1: fe_sub(>YmX1=fe#2,<Y1=fe#12,<X1=fe#11); */
+ /* asm 2: fe_sub(>YmX1=r->Y,<Y1=p->Y,<X1=p->X); */
+ fe_sub(r->Y, p->Y, p->X);
+
+ /* qhasm: A = YpX1*ymx2 */
+ /* asm 1: fe_mul(>A=fe#3,<YpX1=fe#1,<ymx2=fe#16); */
+ /* asm 2: fe_mul(>A=r->Z,<YpX1=r->X,<ymx2=q->yminusx); */
+ fe_mul(r->Z, r->X, q->yminusx);
+
+ /* qhasm: B = YmX1*ypx2 */
+ /* asm 1: fe_mul(>B=fe#2,<YmX1=fe#2,<ypx2=fe#15); */
+ /* asm 2: fe_mul(>B=r->Y,<YmX1=r->Y,<ypx2=q->yplusx); */
+ fe_mul(r->Y, r->Y, q->yplusx);
+
+ /* qhasm: C = xy2d2*T1 */
+ /* asm 1: fe_mul(>C=fe#4,<xy2d2=fe#17,<T1=fe#14); */
+ /* asm 2: fe_mul(>C=r->T,<xy2d2=q->xy2d,<T1=p->T); */
+ fe_mul(r->T, q->xy2d, p->T);
+
+ /* qhasm: D = 2*Z1 */
+ /* asm 1: fe_add(>D=fe#5,<Z1=fe#13,<Z1=fe#13); */
+ /* asm 2: fe_add(>D=t0,<Z1=p->Z,<Z1=p->Z); */
+ fe_add(t0, p->Z, p->Z);
+
+ /* qhasm: X3 = A-B */
+ /* asm 1: fe_sub(>X3=fe#1,<A=fe#3,<B=fe#2); */
+ /* asm 2: fe_sub(>X3=r->X,<A=r->Z,<B=r->Y); */
+ fe_sub(r->X, r->Z, r->Y);
+
+ /* qhasm: Y3 = A+B */
+ /* asm 1: fe_add(>Y3=fe#2,<A=fe#3,<B=fe#2); */
+ /* asm 2: fe_add(>Y3=r->Y,<A=r->Z,<B=r->Y); */
+ fe_add(r->Y, r->Z, r->Y);
+
+ /* qhasm: Z3 = D-C */
+ /* asm 1: fe_sub(>Z3=fe#3,<D=fe#5,<C=fe#4); */
+ /* asm 2: fe_sub(>Z3=r->Z,<D=t0,<C=r->T); */
+ fe_sub(r->Z, t0, r->T);
+
+ /* qhasm: T3 = D+C */
+ /* asm 1: fe_add(>T3=fe#4,<D=fe#5,<C=fe#4); */
+ /* asm 2: fe_add(>T3=r->T,<D=t0,<C=r->T); */
+ fe_add(r->T, t0, r->T);
+
+ }
+
+/*
+r = p
+*/
+
+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);
+ }
+
+/*
+r = p
+*/
+
+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);
+ }
+
+/*
+r = 2 * p
+*/
+
+void ge_p2_dbl(ge_p1p1* r, const ge_p2* p)
+ {
+ fe t0;
+ /* qhasm: XX=X1^2 */
+ /* asm 1: fe_sq(>XX=fe#1,<X1=fe#11); */
+ /* asm 2: fe_sq(>XX=r->X,<X1=p->X); */
+ fe_sq(r->X, p->X);
+
+ /* qhasm: YY=Y1^2 */
+ /* asm 1: fe_sq(>YY=fe#3,<Y1=fe#12); */
+ /* asm 2: fe_sq(>YY=r->Z,<Y1=p->Y); */
+ fe_sq(r->Z, p->Y);
+
+ /* qhasm: B=2*Z1^2 */
+ /* asm 1: fe_sq2(>B=fe#4,<Z1=fe#13); */
+ /* asm 2: fe_sq2(>B=r->T,<Z1=p->Z); */
+ fe_sq2(r->T, p->Z);
+
+ /* qhasm: A=X1+Y1 */
+ /* asm 1: fe_add(>A=fe#2,<X1=fe#11,<Y1=fe#12); */
+ /* asm 2: fe_add(>A=r->Y,<X1=p->X,<Y1=p->Y); */
+ fe_add(r->Y, p->X, p->Y);
+
+ /* qhasm: AA=A^2 */
+ /* asm 1: fe_sq(>AA=fe#5,<A=fe#2); */
+ /* asm 2: fe_sq(>AA=t0,<A=r->Y); */
+ fe_sq(t0, r->Y);
+
+ /* qhasm: Y3=YY+XX */
+ /* asm 1: fe_add(>Y3=fe#2,<YY=fe#3,<XX=fe#1); */
+ /* asm 2: fe_add(>Y3=r->Y,<YY=r->Z,<XX=r->X); */
+ fe_add(r->Y, r->Z, r->X);
+
+ /* qhasm: Z3=YY-XX */
+ /* asm 1: fe_sub(>Z3=fe#3,<YY=fe#3,<XX=fe#1); */
+ /* asm 2: fe_sub(>Z3=r->Z,<YY=r->Z,<XX=r->X); */
+ fe_sub(r->Z, r->Z, r->X);
+
+ /* qhasm: X3=AA-Y3 */
+ /* asm 1: fe_sub(>X3=fe#1,<AA=fe#5,<Y3=fe#2); */
+ /* asm 2: fe_sub(>X3=r->X,<AA=t0,<Y3=r->Y); */
+ fe_sub(r->X, t0, r->Y);
+
+ /* qhasm: T3=B-Z3 */
+ /* asm 1: fe_sub(>T3=fe#4,<B=fe#4,<Z3=fe#3); */
+ /* asm 2: fe_sub(>T3=r->T,<B=r->T,<Z3=r->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);
+ }
+
+/*
+r = 2 * p
+*/
+
+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);
+ }
+
+/*
+r = p
+*/
+
+
+void ge_p3_to_cached(ge_cached* r, const ge_p3* p)
+ {
+ static const fe 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);
+ }
+
+/*
+r = p - q
+*/
+
+void ge_sub(ge_p1p1* r, const ge_p3* p, const ge_cached* q)
+ {
+ fe t0;
+ /* qhasm: YpX1 = Y1+X1 */
+ /* asm 1: fe_add(>YpX1=fe#1,<Y1=fe#12,<X1=fe#11); */
+ /* asm 2: fe_add(>YpX1=r->X,<Y1=p->Y,<X1=p->X); */
+ fe_add(r->X, p->Y, p->X);
+
+ /* qhasm: YmX1 = Y1-X1 */
+ /* asm 1: fe_sub(>YmX1=fe#2,<Y1=fe#12,<X1=fe#11); */
+ /* asm 2: fe_sub(>YmX1=r->Y,<Y1=p->Y,<X1=p->X); */
+ fe_sub(r->Y, p->Y, p->X);
+
+ /* qhasm: A = YpX1*YmX2 */
+ /* asm 1: fe_mul(>A=fe#3,<YpX1=fe#1,<YmX2=fe#16); */
+ /* asm 2: fe_mul(>A=r->Z,<YpX1=r->X,<YmX2=q->YminusX); */
+ fe_mul(r->Z, r->X, q->YminusX);
+
+ /* qhasm: B = YmX1*YpX2 */
+ /* asm 1: fe_mul(>B=fe#2,<YmX1=fe#2,<YpX2=fe#15); */
+ /* asm 2: fe_mul(>B=r->Y,<YmX1=r->Y,<YpX2=q->YplusX); */
+ fe_mul(r->Y, r->Y, q->YplusX);
+
+ /* qhasm: C = T2d2*T1 */
+ /* asm 1: fe_mul(>C=fe#4,<T2d2=fe#18,<T1=fe#14); */
+ /* asm 2: fe_mul(>C=r->T,<T2d2=q->T2d,<T1=p->T); */
+ fe_mul(r->T, q->T2d, p->T);
+
+ /* qhasm: ZZ = Z1*Z2 */
+ /* asm 1: fe_mul(>ZZ=fe#1,<Z1=fe#13,<Z2=fe#17); */
+ /* asm 2: fe_mul(>ZZ=r->X,<Z1=p->Z,<Z2=q->Z); */
+ fe_mul(r->X, p->Z, q->Z);
+
+ /* qhasm: D = 2*ZZ */
+ /* asm 1: fe_add(>D=fe#5,<ZZ=fe#1,<ZZ=fe#1); */
+ /* asm 2: fe_add(>D=t0,<ZZ=r->X,<ZZ=r->X); */
+ fe_add(t0, r->X, r->X);
+
+ /* qhasm: X3 = A-B */
+ /* asm 1: fe_sub(>X3=fe#1,<A=fe#3,<B=fe#2); */
+ /* asm 2: fe_sub(>X3=r->X,<A=r->Z,<B=r->Y); */
+ fe_sub(r->X, r->Z, r->Y);
+
+ /* qhasm: Y3 = A+B */
+ /* asm 1: fe_add(>Y3=fe#2,<A=fe#3,<B=fe#2); */
+ /* asm 2: fe_add(>Y3=r->Y,<A=r->Z,<B=r->Y); */
+ fe_add(r->Y, r->Z, r->Y);
+
+ /* qhasm: Z3 = D-C */
+ /* asm 1: fe_sub(>Z3=fe#3,<D=fe#5,<C=fe#4); */
+ /* asm 2: fe_sub(>Z3=r->Z,<D=t0,<C=r->T); */
+ fe_sub(r->Z, t0, r->T);
+
+ /* qhasm: T3 = D+C */
+ /* asm 1: fe_add(>T3=fe#4,<D=fe#5,<C=fe#4); */
+ /* asm 2: fe_add(>T3=r->T,<D=t0,<C=r->T); */
+ fe_add(r->T, t0, r->T);
+
+ }
+
+void slide(int8_t* r, const uint8_t* a)
+ {
+ 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])
+ {
+ for(size_t b = 1; b <= 6 && i + b < 256; ++b)
+ {
+ if(r[i + b])
+ {
+ 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])
+ {
+ r[k] = 1;
+ break;
+ }
+ r[k] = 0;
+ }
+ }
+ else
+ { break; }
+ }
+ }
+ }
+ }
+ }
+
+void ge_tobytes(uint8_t* s, const ge_p2* h)
+ {
+ fe recip;
+ fe x;
+ fe 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);
+ }
+
+}
+
+int ge_frombytes_negate_vartime(ge_p3* h, const uint8_t* s)
+ {
+ static const fe d =
+ {
+ -10913610, 13857413, -15372611, 6949391, 114729, -8787816, -6275908, -3247719, -18696448, -12055116
+ } ;
+ static const fe sqrtm1 =
+ {
+ -32595792, -7943725, 9377950, 3500415, 12389472, -272473, -25146209, -2005654, 326686, 11406482
+ } ;
+
+ fe u;
+ fe v;
+ fe v3;
+ fe vxx;
+ fe 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;
+ }
+ fe_mul(h->X, h->X, sqrtm1);
+ }
+
+ if(fe_isnegative(h->X) == (s[31] >> 7))
+ { fe_neg(h->X, h->X); }
+
+ fe_mul(h->T, h->X, h->Y);
+ return 0;
+ }
+
+/*
+r = a * A + b * B
+where a = a[0]+256*a[1]+...+256^31 a[31].
+and b = b[0]+256*b[1]+...+256^31 b[31].
+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] =
+ {
+ {
+ { 25967493, -14356035, 29566456, 3660896, -12694345, 4014787, 27544626, -11754271, -6079156, 2047605 },
+ { -12545711, 934262, -2722910, 3049990, -727428, 9406986, 12720692, 5043384, 19500929, -15469378 },
+ { -8738181, 4489570, 9688441, -14785194, 10184609, -12363380, 29287919, 11864899, -24514362, -4438546 },
+ },
+ {
+ { 15636291, -9688557, 24204773, -7912398, 616977, -16685262, 27787600, -14772189, 28944400, -1550024 },
+ { 16568933, 4717097, -11556148, -1102322, 15682896, -11807043, 16354577, -11775962, 7689662, 11199574 },
+ { 30464156, -5976125, -11779434, -15670865, 23220365, 15915852, 7512774, 10017326, -17749093, -9920357 },
+ },
+ {
+ { 10861363, 11473154, 27284546, 1981175, -30064349, 12577861, 32867885, 14515107, -15438304, 10819380 },
+ { 4708026, 6336745, 20377586, 9066809, -11272109, 6594696, -25653668, 12483688, -12668491, 5581306 },
+ { 19563160, 16186464, -29386857, 4097519, 10237984, -4348115, 28542350, 13850243, -23678021, -15815942 },
+ },
+ {
+ { 5153746, 9909285, 1723747, -2777874, 30523605, 5516873, 19480852, 5230134, -23952439, -15175766 },
+ { -30269007, -3463509, 7665486, 10083793, 28475525, 1649722, 20654025, 16520125, 30598449, 7715701 },
+ { 28881845, 14381568, 9657904, 3680757, -20181635, 7843316, -31400660, 1370708, 29794553, -1409300 },
+ },
+ {
+ { -22518993, -6692182, 14201702, -8745502, -23510406, 8844726, 18474211, -1361450, -13062696, 13821877 },
+ { -6455177, -7839871, 3374702, -4740862, -27098617, -10571707, 31655028, -7212327, 18853322, -14220951 },
+ { 4566830, -12963868, -28974889, -12240689, -7602672, -2830569, -8514358, -10431137, 2207753, -3209784 },
+ },
+ {
+ { -25154831, -4185821, 29681144, 7868801, -6854661, -9423865, -12437364, -663000, -31111463, -16132436 },
+ { 25576264, -2703214, 7349804, -11814844, 16472782, 9300885, 3844789, 15725684, 171356, 6466918 },
+ { 23103977, 13316479, 9739013, -16149481, 817875, -15038942, 8965339, -14088058, -30714912, 16193877 },
+ },
+ {
+ { -33521811, 3180713, -2394130, 14003687, -16903474, -16270840, 17238398, 4729455, -18074513, 9256800 },
+ { -25182317, -4174131, 32336398, 5036987, -21236817, 11360617, 22616405, 9761698, -19827198, 630305 },
+ { -13720693, 2639453, -24237460, -7406481, 9494427, -5774029, -6554551, -15960994, -2449256, -14291300 },
+ },
+ {
+ { -3151181, -5046075, 9282714, 6866145, -31907062, -863023, -18940575, 15033784, 25105118, -7894876 },
+ { -24326370, 15950226, -31801215, -14592823, -11662737, -5090925, 1573892, -2625887, 2198790, -15804619 },
+ { -3099351, 10324967, -2241613, 7453183, -5446979, -2735503, -13812022, -16236442, -32461234, -12290683 },
+ },
+ } ;
+
+ 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;
+ }
+ }
+
+ for(; i >= 0; --i)
+ {
+ ge_p2_dbl(&t, &r);
+
+ if(aslide[i] > 0)
+ {
+ ge_p1p1_to_p3(&u, &t);
+ ge_add(&t, &u, &Ai[aslide[i] >> 1]);
+ }
+ else if(aslide[i] < 0)
+ {
+ ge_p1p1_to_p3(&u, &t);
+ ge_sub(&t, &u, &Ai[(-aslide[i]) >> 1]);
+ }
+
+ if(bslide[i] > 0)
+ {
+ ge_p1p1_to_p3(&u, &t);
+ ge_madd(&t, &u, &Bi[bslide[i] >> 1]);
+ }
+ else if(bslide[i] < 0)
+ {
+ ge_p1p1_to_p3(&u, &t);
+ ge_msub(&t, &u, &Bi[(-bslide[i]) >> 1]);
+ }
+
+ ge_p1p1_to_p2(&r, &t);
+ }
+
+ ge_tobytes(out, &r);
+ }
+
+/* base[i][j] = (j+1)*256^i*B */
+static const ge_precomp B_precomp[32][8] =
+ {
+ {
+ {
+ { 25967493, -14356035, 29566456, 3660896, -12694345, 4014787, 27544626, -11754271, -6079156, 2047605 },
+ { -12545711, 934262, -2722910, 3049990, -727428, 9406986, 12720692, 5043384, 19500929, -15469378 },
+ { -8738181, 4489570, 9688441, -14785194, 10184609, -12363380, 29287919, 11864899, -24514362, -4438546 },
+ },
+ {
+ { -12815894, -12976347, -21581243, 11784320, -25355658, -2750717, -11717903, -3814571, -358445, -10211303 },
+ { -21703237, 6903825, 27185491, 6451973, -29577724, -9554005, -15616551, 11189268, -26829678, -5319081 },
+ { 26966642, 11152617, 32442495, 15396054, 14353839, -12752335, -3128826, -9541118, -15472047, -4166697 },
+ },
+ {
+ { 15636291, -9688557, 24204773, -7912398, 616977, -16685262, 27787600, -14772189, 28944400, -1550024 },
+ { 16568933, 4717097, -11556148, -1102322, 15682896, -11807043, 16354577, -11775962, 7689662, 11199574 },
+ { 30464156, -5976125, -11779434, -15670865, 23220365, 15915852, 7512774, 10017326, -17749093, -9920357 },
+ },
+ {
+ { -17036878, 13921892, 10945806, -6033431, 27105052, -16084379, -28926210, 15006023, 3284568, -6276540 },
+ { 23599295, -8306047, -11193664, -7687416, 13236774, 10506355, 7464579, 9656445, 13059162, 10374397 },
+ { 7798556, 16710257, 3033922, 2874086, 28997861, 2835604, 32406664, -3839045, -641708, -101325 },
+ },
+ {
+ { 10861363, 11473154, 27284546, 1981175, -30064349, 12577861, 32867885, 14515107, -15438304, 10819380 },
+ { 4708026, 6336745, 20377586, 9066809, -11272109, 6594696, -25653668, 12483688, -12668491, 5581306 },
+ { 19563160, 16186464, -29386857, 4097519, 10237984, -4348115, 28542350, 13850243, -23678021, -15815942 },
+ },
+ {
+ { -15371964, -12862754, 32573250, 4720197, -26436522, 5875511, -19188627, -15224819, -9818940, -12085777 },
+ { -8549212, 109983, 15149363, 2178705, 22900618, 4543417, 3044240, -15689887, 1762328, 14866737 },
+ { -18199695, -15951423, -10473290, 1707278, -17185920, 3916101, -28236412, 3959421, 27914454, 4383652 },
+ },
+ {
+ { 5153746, 9909285, 1723747, -2777874, 30523605, 5516873, 19480852, 5230134, -23952439, -15175766 },
+ { -30269007, -3463509, 7665486, 10083793, 28475525, 1649722, 20654025, 16520125, 30598449, 7715701 },
+ { 28881845, 14381568, 9657904, 3680757, -20181635, 7843316, -31400660, 1370708, 29794553, -1409300 },
+ },
+ {
+ { 14499471, -2729599, -33191113, -4254652, 28494862, 14271267, 30290735, 10876454, -33154098, 2381726 },
+ { -7195431, -2655363, -14730155, 462251, -27724326, 3941372, -6236617, 3696005, -32300832, 15351955 },
+ { 27431194, 8222322, 16448760, -3907995, -18707002, 11938355, -32961401, -2970515, 29551813, 10109425 },
+ },
+ },
+ {
+ {
+ { -13657040, -13155431, -31283750, 11777098, 21447386, 6519384, -2378284, -1627556, 10092783, -4764171 },
+ { 27939166, 14210322, 4677035, 16277044, -22964462, -12398139, -32508754, 12005538, -17810127, 12803510 },
+ { 17228999, -15661624, -1233527, 300140, -1224870, -11714777, 30364213, -9038194, 18016357, 4397660 },
+ },
+ {
+ { -10958843, -7690207, 4776341, -14954238, 27850028, -15602212, -26619106, 14544525, -17477504, 982639 },
+ { 29253598, 15796703, -2863982, -9908884, 10057023, 3163536, 7332899, -4120128, -21047696, 9934963 },
+ { 5793303, 16271923, -24131614, -10116404, 29188560, 1206517, -14747930, 4559895, -30123922, -10897950 },
+ },
+ {
+ { -27643952, -11493006, 16282657, -11036493, 28414021, -15012264, 24191034, 4541697, -13338309, 5500568 },
+ { 12650548, -1497113, 9052871, 11355358, -17680037, -8400164, -17430592, 12264343, 10874051, 13524335 },
+ { 25556948, -3045990, 714651, 2510400, 23394682, -10415330, 33119038, 5080568, -22528059, 5376628 },
+ },
+ {
+ { -26088264, -4011052, -17013699, -3537628, -6726793, 1920897, -22321305, -9447443, 4535768, 1569007 },
+ { -2255422, 14606630, -21692440, -8039818, 28430649, 8775819, -30494562, 3044290, 31848280, 12543772 },
+ { -22028579, 2943893, -31857513, 6777306, 13784462, -4292203, -27377195, -2062731, 7718482, 14474653 },
+ },
+ {
+ { 2385315, 2454213, -22631320, 46603, -4437935, -15680415, 656965, -7236665, 24316168, -5253567 },
+ { 13741529, 10911568, -33233417, -8603737, -20177830, -1033297, 33040651, -13424532, -20729456, 8321686 },
+ { 21060490, -2212744, 15712757, -4336099, 1639040, 10656336, 23845965, -11874838, -9984458, 608372 },
+ },
+ {
+ { -13672732, -15087586, -10889693, -7557059, -6036909, 11305547, 1123968, -6780577, 27229399, 23887 },
+ { -23244140, -294205, -11744728, 14712571, -29465699, -2029617, 12797024, -6440308, -1633405, 16678954 },
+ { -29500620, 4770662, -16054387, 14001338, 7830047, 9564805, -1508144, -4795045, -17169265, 4904953 },
+ },
+ {
+ { 24059557, 14617003, 19037157, -15039908, 19766093, -14906429, 5169211, 16191880, 2128236, -4326833 },
+ { -16981152, 4124966, -8540610, -10653797, 30336522, -14105247, -29806336, 916033, -6882542, -2986532 },
+ { -22630907, 12419372, -7134229, -7473371, -16478904, 16739175, 285431, 2763829, 15736322, 4143876 },
+ },
+ {
+ { 2379352, 11839345, -4110402, -5988665, 11274298, 794957, 212801, -14594663, 23527084, -16458268 },
+ { 33431127, -11130478, -17838966, -15626900, 8909499, 8376530, -32625340, 4087881, -15188911, -14416214 },
+ { 1767683, 7197987, -13205226, -2022635, -13091350, 448826, 5799055, 4357868, -4774191, -16323038 },
+ },
+ },
+ {
+ {
+ { 6721966, 13833823, -23523388, -1551314, 26354293, -11863321, 23365147, -3949732, 7390890, 2759800 },
+ { 4409041, 2052381, 23373853, 10530217, 7676779, -12885954, 21302353, -4264057, 1244380, -12919645 },
+ { -4421239, 7169619, 4982368, -2957590, 30256825, -2777540, 14086413, 9208236, 15886429, 16489664 },
+ },
+ {
+ { 1996075, 10375649, 14346367, 13311202, -6874135, -16438411, -13693198, 398369, -30606455, -712933 },
+ { -25307465, 9795880, -2777414, 14878809, -33531835, 14780363, 13348553, 12076947, -30836462, 5113182 },
+ { -17770784, 11797796, 31950843, 13929123, -25888302, 12288344, -30341101, -7336386, 13847711, 5387222 },
+ },
+ {
+ { -18582163, -3416217, 17824843, -2340966, 22744343, -10442611, 8763061, 3617786, -19600662, 10370991 },
+ { 20246567, -14369378, 22358229, -543712, 18507283, -10413996, 14554437, -8746092, 32232924, 16763880 },
+ { 9648505, 10094563, 26416693, 14745928, -30374318, -6472621, 11094161, 15689506, 3140038, -16510092 },
+ },
+ {
+ { -16160072, 5472695, 31895588, 4744994, 8823515, 10365685, -27224800, 9448613, -28774454, 366295 },
+ { 19153450, 11523972, -11096490, -6503142, -24647631, 5420647, 28344573, 8041113, 719605, 11671788 },
+ { 8678025, 2694440, -6808014, 2517372, 4964326, 11152271, -15432916, -15266516, 27000813, -10195553 },
+ },
+ {
+ { -15157904, 7134312, 8639287, -2814877, -7235688, 10421742, 564065, 5336097, 6750977, -14521026 },
+ { 11836410, -3979488, 26297894, 16080799, 23455045, 15735944, 1695823, -8819122, 8169720, 16220347 },
+ { -18115838, 8653647, 17578566, -6092619, -8025777, -16012763, -11144307, -2627664, -5990708, -14166033 },
+ },
+ {
+ { -23308498, -10968312, 15213228, -10081214, -30853605, -11050004, 27884329, 2847284, 2655861, 1738395 },
+ { -27537433, -14253021, -25336301, -8002780, -9370762, 8129821, 21651608, -3239336, -19087449, -11005278 },
+ { 1533110, 3437855, 23735889, 459276, 29970501, 11335377, 26030092, 5821408, 10478196, 8544890 },
+ },
+ {
+ { 32173121, -16129311, 24896207, 3921497, 22579056, -3410854, 19270449, 12217473, 17789017, -3395995 },
+ { -30552961, -2228401, -15578829, -10147201, 13243889, 517024, 15479401, -3853233, 30460520, 1052596 },
+ { -11614875, 13323618, 32618793, 8175907, -15230173, 12596687, 27491595, -4612359, 3179268, -9478891 },
+ },
+ {
+ { 31947069, -14366651, -4640583, -15339921, -15125977, -6039709, -14756777, -16411740, 19072640, -9511060 },
+ { 11685058, 11822410, 3158003, -13952594, 33402194, -4165066, 5977896, -5215017, 473099, 5040608 },
+ { -20290863, 8198642, -27410132, 11602123, 1290375, -2799760, 28326862, 1721092, -19558642, -3131606 },
+ },
+ },
+ {
+ {
+ { 7881532, 10687937, 7578723, 7738378, -18951012, -2553952, 21820786, 8076149, -27868496, 11538389 },
+ { -19935666, 3899861, 18283497, -6801568, -15728660, -11249211, 8754525, 7446702, -5676054, 5797016 },
+ { -11295600, -3793569, -15782110, -7964573, 12708869, -8456199, 2014099, -9050574, -2369172, -5877341 },
+ },
+ {
+ { -22472376, -11568741, -27682020, 1146375, 18956691, 16640559, 1192730, -3714199, 15123619, 10811505 },
+ { 14352098, -3419715, -18942044, 10822655, 32750596, 4699007, -70363, 15776356, -28886779, -11974553 },
+ { -28241164, -8072475, -4978962, -5315317, 29416931, 1847569, -20654173, -16484855, 4714547, -9600655 },
+ },
+ {
+ { 15200332, 8368572, 19679101, 15970074, -31872674, 1959451, 24611599, -4543832, -11745876, 12340220 },
+ { 12876937, -10480056, 33134381, 6590940, -6307776, 14872440, 9613953, 8241152, 15370987, 9608631 },
+ { -4143277, -12014408, 8446281, -391603, 4407738, 13629032, -7724868, 15866074, -28210621, -8814099 },
+ },
+ {
+ { 26660628, -15677655, 8393734, 358047, -7401291, 992988, -23904233, 858697, 20571223, 8420556 },
+ { 14620715, 13067227, -15447274, 8264467, 14106269, 15080814, 33531827, 12516406, -21574435, -12476749 },
+ { 236881, 10476226, 57258, -14677024, 6472998, 2466984, 17258519, 7256740, 8791136, 15069930 },
+ },
+ {
+ { 1276410, -9371918, 22949635, -16322807, -23493039, -5702186, 14711875, 4874229, -30663140, -2331391 },
+ { 5855666, 4990204, -13711848, 7294284, -7804282, 1924647, -1423175, -7912378, -33069337, 9234253 },
+ { 20590503, -9018988, 31529744, -7352666, -2706834, 10650548, 31559055, -11609587, 18979186, 13396066 },
+ },
+ {
+ { 24474287, 4968103, 22267082, 4407354, 24063882, -8325180, -18816887, 13594782, 33514650, 7021958 },
+ { -11566906, -6565505, -21365085, 15928892, -26158305, 4315421, -25948728, -3916677, -21480480, 12868082 },
+ { -28635013, 13504661, 19988037, -2132761, 21078225, 6443208, -21446107, 2244500, -12455797, -8089383 },
+ },
+ {
+ { -30595528, 13793479, -5852820, 319136, -25723172, -6263899, 33086546, 8957937, -15233648, 5540521 },
+ { -11630176, -11503902, -8119500, -7643073, 2620056, 1022908, -23710744, -1568984, -16128528, -14962807 },
+ { 23152971, 775386, 27395463, 14006635, -9701118, 4649512, 1689819, 892185, -11513277, -15205948 },
+ },
+ {
+ { 9770129, 9586738, 26496094, 4324120, 1556511, -3550024, 27453819, 4763127, -19179614, 5867134 },
+ { -32765025, 1927590, 31726409, -4753295, 23962434, -16019500, 27846559, 5931263, -29749703, -16108455 },
+ { 27461885, -2977536, 22380810, 1815854, -23033753, -3031938, 7283490, -15148073, -19526700, 7734629 },
+ },
+ },
+ {
+ {
+ { -8010264, -9590817, -11120403, 6196038, 29344158, -13430885, 7585295, -3176626, 18549497, 15302069 },
+ { -32658337, -6171222, -7672793, -11051681, 6258878, 13504381, 10458790, -6418461, -8872242, 8424746 },
+ { 24687205, 8613276, -30667046, -3233545, 1863892, -1830544, 19206234, 7134917, -11284482, -828919 },
+ },
+ {
+ { 11334899, -9218022, 8025293, 12707519, 17523892, -10476071, 10243738, -14685461, -5066034, 16498837 },
+ { 8911542, 6887158, -9584260, -6958590, 11145641, -9543680, 17303925, -14124238, 6536641, 10543906 },
+ { -28946384, 15479763, -17466835, 568876, -1497683, 11223454, -2669190, -16625574, -27235709, 8876771 },
+ },
+ {
+ { -25742899, -12566864, -15649966, -846607, -33026686, -796288, -33481822, 15824474, -604426, -9039817 },
+ { 10330056, 70051, 7957388, -9002667, 9764902, 15609756, 27698697, -4890037, 1657394, 3084098 },
+ { 10477963, -7470260, 12119566, -13250805, 29016247, -5365589, 31280319, 14396151, -30233575, 15272409 },
+ },
+ {
+ { -12288309, 3169463, 28813183, 16658753, 25116432, -5630466, -25173957, -12636138, -25014757, 1950504 },
+ { -26180358, 9489187, 11053416, -14746161, -31053720, 5825630, -8384306, -8767532, 15341279, 8373727 },
+ { 28685821, 7759505, -14378516, -12002860, -31971820, 4079242, 298136, -10232602, -2878207, 15190420 },
+ },
+ {
+ { -32932876, 13806336, -14337485, -15794431, -24004620, 10940928, 8669718, 2742393, -26033313, -6875003 },
+ { -1580388, -11729417, -25979658, -11445023, -17411874, -10912854, 9291594, -16247779, -12154742, 6048605 },
+ { -30305315, 14843444, 1539301, 11864366, 20201677, 1900163, 13934231, 5128323, 11213262, 9168384 },
+ },
+ {
+ { -26280513, 11007847, 19408960, -940758, -18592965, -4328580, -5088060, -11105150, 20470157, -16398701 },
+ { -23136053, 9282192, 14855179, -15390078, -7362815, -14408560, -22783952, 14461608, 14042978, 5230683 },
+ { 29969567, -2741594, -16711867, -8552442, 9175486, -2468974, 21556951, 3506042, -5933891, -12449708 },
+ },
+ {
+ { -3144746, 8744661, 19704003, 4581278, -20430686, 6830683, -21284170, 8971513, -28539189, 15326563 },
+ { -19464629, 10110288, -17262528, -3503892, -23500387, 1355669, -15523050, 15300988, -20514118, 9168260 },
+ { -5353335, 4488613, -23803248, 16314347, 7780487, -15638939, -28948358, 9601605, 33087103, -9011387 },
+ },
+ {
+ { -19443170, -15512900, -20797467, -12445323, -29824447, 10229461, -27444329, -15000531, -5996870, 15664672 },
+ { 23294591, -16632613, -22650781, -8470978, 27844204, 11461195, 13099750, -2460356, 18151676, 13417686 },
+ { -24722913, -4176517, -31150679, 5988919, -26858785, 6685065, 1661597, -12551441, 15271676, -15452665 },
+ },
+ },
+ {
+ {
+ { 11433042, -13228665, 8239631, -5279517, -1985436, -725718, -18698764, 2167544, -6921301, -13440182 },
+ { -31436171, 15575146, 30436815, 12192228, -22463353, 9395379, -9917708, -8638997, 12215110, 12028277 },
+ { 14098400, 6555944, 23007258, 5757252, -15427832, -12950502, 30123440, 4617780, -16900089, -655628 },
+ },
+ {
+ { -4026201, -15240835, 11893168, 13718664, -14809462, 1847385, -15819999, 10154009, 23973261, -12684474 },
+ { -26531820, -3695990, -1908898, 2534301, -31870557, -16550355, 18341390, -11419951, 32013174, -10103539 },
+ { -25479301, 10876443, -11771086, -14625140, -12369567, 1838104, 21911214, 6354752, 4425632, -837822 },
+ },
+ {
+ { -10433389, -14612966, 22229858, -3091047, -13191166, 776729, -17415375, -12020462, 4725005, 14044970 },
+ { 19268650, -7304421, 1555349, 8692754, -21474059, -9910664, 6347390, -1411784, -19522291, -16109756 },
+ { -24864089, 12986008, -10898878, -5558584, -11312371, -148526, 19541418, 8180106, 9282262, 10282508 },
+ },
+ {
+ { -26205082, 4428547, -8661196, -13194263, 4098402, -14165257, 15522535, 8372215, 5542595, -10702683 },
+ { -10562541, 14895633, 26814552, -16673850, -17480754, -2489360, -2781891, 6993761, -18093885, 10114655 },
+ { -20107055, -929418, 31422704, 10427861, -7110749, 6150669, -29091755, -11529146, 25953725, -106158 },
+ },
+ {
+ { -4234397, -8039292, -9119125, 3046000, 2101609, -12607294, 19390020, 6094296, -3315279, 12831125 },
+ { -15998678, 7578152, 5310217, 14408357, -33548620, -224739, 31575954, 6326196, 7381791, -2421839 },
+ { -20902779, 3296811, 24736065, -16328389, 18374254, 7318640, 6295303, 8082724, -15362489, 12339664 },
+ },
+ {
+ { 27724736, 2291157, 6088201, -14184798, 1792727, 5857634, 13848414, 15768922, 25091167, 14856294 },
+ { -18866652, 8331043, 24373479, 8541013, -701998, -9269457, 12927300, -12695493, -22182473, -9012899 },
+ { -11423429, -5421590, 11632845, 3405020, 30536730, -11674039, -27260765, 13866390, 30146206, 9142070 },
+ },
+ {
+ { 3924129, -15307516, -13817122, -10054960, 12291820, -668366, -27702774, 9326384, -8237858, 4171294 },
+ { -15921940, 16037937, 6713787, 16606682, -21612135, 2790944, 26396185, 3731949, 345228, -5462949 },
+ { -21327538, 13448259, 25284571, 1143661, 20614966, -8849387, 2031539, -12391231, -16253183, -13582083 },
+ },
+ {
+ { 31016211, -16722429, 26371392, -14451233, -5027349, 14854137, 17477601, 3842657, 28012650, -16405420 },
+ { -5075835, 9368966, -8562079, -4600902, -15249953, 6970560, -9189873, 16292057, -8867157, 3507940 },
+ { 29439664, 3537914, 23333589, 6997794, -17555561, -11018068, -15209202, -15051267, -9164929, 6580396 },
+ },
+ },
+ {
+ {
+ { -12185861, -7679788, 16438269, 10826160, -8696817, -6235611, 17860444, -9273846, -2095802, 9304567 },
+ { 20714564, -4336911, 29088195, 7406487, 11426967, -5095705, 14792667, -14608617, 5289421, -477127 },
+ { -16665533, -10650790, -6160345, -13305760, 9192020, -1802462, 17271490, 12349094, 26939669, -3752294 },
+ },
+ {
+ { -12889898, 9373458, 31595848, 16374215, 21471720, 13221525, -27283495, -12348559, -3698806, 117887 },
+ { 22263325, -6560050, 3984570, -11174646, -15114008, -566785, 28311253, 5358056, -23319780, 541964 },
+ { 16259219, 3261970, 2309254, -15534474, -16885711, -4581916, 24134070, -16705829, -13337066, -13552195 },
+ },
+ {
+ { 9378160, -13140186, -22845982, -12745264, 28198281, -7244098, -2399684, -717351, 690426, 14876244 },
+ { 24977353, -314384, -8223969, -13465086, 28432343, -1176353, -13068804, -12297348, -22380984, 6618999 },
+ { -1538174, 11685646, 12944378, 13682314, -24389511, -14413193, 8044829, -13817328, 32239829, -5652762 },
+ },
+ {
+ { -18603066, 4762990, -926250, 8885304, -28412480, -3187315, 9781647, -10350059, 32779359, 5095274 },
+ { -33008130, -5214506, -32264887, -3685216, 9460461, -9327423, -24601656, 14506724, 21639561, -2630236 },
+ { -16400943, -13112215, 25239338, 15531969, 3987758, -4499318, -1289502, -6863535, 17874574, 558605 },
+ },
+ {
+ { -13600129, 10240081, 9171883, 16131053, -20869254, 9599700, 33499487, 5080151, 2085892, 5119761 },
+ { -22205145, -2519528, -16381601, 414691, -25019550, 2170430, 30634760, -8363614, -31999993, -5759884 },
+ { -6845704, 15791202, 8550074, -1312654, 29928809, -12092256, 27534430, -7192145, -22351378, 12961482 },
+ },
+ {
+ { -24492060, -9570771, 10368194, 11582341, -23397293, -2245287, 16533930, 8206996, -30194652, -5159638 },
+ { -11121496, -3382234, 2307366, 6362031, -135455, 8868177, -16835630, 7031275, 7589640, 8945490 },
+ { -32152748, 8917967, 6661220, -11677616, -1192060, -15793393, 7251489, -11182180, 24099109, -14456170 },
+ },
+ {
+ { 5019558, -7907470, 4244127, -14714356, -26933272, 6453165, -19118182, -13289025, -6231896, -10280736 },
+ { 10853594, 10721687, 26480089, 5861829, -22995819, 1972175, -1866647, -10557898, -3363451, -6441124 },
+ { -17002408, 5906790, 221599, -6563147, 7828208, -13248918, 24362661, -2008168, -13866408, 7421392 },
+ },
+ {
+ { 8139927, -6546497, 32257646, -5890546, 30375719, 1886181, -21175108, 15441252, 28826358, -4123029 },
+ { 6267086, 9695052, 7709135, -16603597, -32869068, -1886135, 14795160, -7840124, 13746021, -1742048 },
+ { 28584902, 7787108, -6732942, -15050729, 22846041, -7571236, -3181936, -363524, 4771362, -8419958 },
+ },
+ },
+ {
+ {
+ { 24949256, 6376279, -27466481, -8174608, -18646154, -9930606, 33543569, -12141695, 3569627, 11342593 },
+ { 26514989, 4740088, 27912651, 3697550, 19331575, -11472339, 6809886, 4608608, 7325975, -14801071 },
+ { -11618399, -14554430, -24321212, 7655128, -1369274, 5214312, -27400540, 10258390, -17646694, -8186692 },
+ },
+ {
+ { 11431204, 15823007, 26570245, 14329124, 18029990, 4796082, -31446179, 15580664, 9280358, -3973687 },
+ { -160783, -10326257, -22855316, -4304997, -20861367, -13621002, -32810901, -11181622, -15545091, 4387441 },
+ { -20799378, 12194512, 3937617, -5805892, -27154820, 9340370, -24513992, 8548137, 20617071, -7482001 },
+ },
+ {
+ { -938825, -3930586, -8714311, 16124718, 24603125, -6225393, -13775352, -11875822, 24345683, 10325460 },
+ { -19855277, -1568885, -22202708, 8714034, 14007766, 6928528, 16318175, -1010689, 4766743, 3552007 },
+ { -21751364, -16730916, 1351763, -803421, -4009670, 3950935, 3217514, 14481909, 10988822, -3994762 },
+ },
+ {
+ { 15564307, -14311570, 3101243, 5684148, 30446780, -8051356, 12677127, -6505343, -8295852, 13296005 },
+ { -9442290, 6624296, -30298964, -11913677, -4670981, -2057379, 31521204, 9614054, -30000824, 12074674 },
+ { 4771191, -135239, 14290749, -13089852, 27992298, 14998318, -1413936, -1556716, 29832613, -16391035 },
+ },
+ {
+ { 7064884, -7541174, -19161962, -5067537, -18891269, -2912736, 25825242, 5293297, -27122660, 13101590 },
+ { -2298563, 2439670, -7466610, 1719965, -27267541, -16328445, 32512469, -5317593, -30356070, -4190957 },
+ { -30006540, 10162316, -33180176, 3981723, -16482138, -13070044, 14413974, 9515896, 19568978, 9628812 },
+ },
+ {
+ { 33053803, 199357, 15894591, 1583059, 27380243, -4580435, -17838894, -6106839, -6291786, 3437740 },
+ { -18978877, 3884493, 19469877, 12726490, 15913552, 13614290, -22961733, 70104, 7463304, 4176122 },
+ { -27124001, 10659917, 11482427, -16070381, 12771467, -6635117, -32719404, -5322751, 24216882, 5944158 },
+ },
+ {
+ { 8894125, 7450974, -2664149, -9765752, -28080517, -12389115, 19345746, 14680796, 11632993, 5847885 },
+ { 26942781, -2315317, 9129564, -4906607, 26024105, 11769399, -11518837, 6367194, -9727230, 4782140 },
+ { 19916461, -4828410, -22910704, -11414391, 25606324, -5972441, 33253853, 8220911, 6358847, -1873857 },
+ },
+ {
+ { 801428, -2081702, 16569428, 11065167, 29875704, 96627, 7908388, -4480480, -13538503, 1387155 },
+ { 19646058, 5720633, -11416706, 12814209, 11607948, 12749789, 14147075, 15156355, -21866831, 11835260 },
+ { 19299512, 1155910, 28703737, 14890794, 2925026, 7269399, 26121523, 15467869, -26560550, 5052483 },
+ },
+ },
+ {
+ {
+ { -3017432, 10058206, 1980837, 3964243, 22160966, 12322533, -6431123, -12618185, 12228557, -7003677 },
+ { 32944382, 14922211, -22844894, 5188528, 21913450, -8719943, 4001465, 13238564, -6114803, 8653815 },
+ { 22865569, -4652735, 27603668, -12545395, 14348958, 8234005, 24808405, 5719875, 28483275, 2841751 },
+ },
+ {
+ { -16420968, -1113305, -327719, -12107856, 21886282, -15552774, -1887966, -315658, 19932058, -12739203 },
+ { -11656086, 10087521, -8864888, -5536143, -19278573, -3055912, 3999228, 13239134, -4777469, -13910208 },
+ { 1382174, -11694719, 17266790, 9194690, -13324356, 9720081, 20403944, 11284705, -14013818, 3093230 },
+ },
+ {
+ { 16650921, -11037932, -1064178, 1570629, -8329746, 7352753, -302424, 16271225, -24049421, -6691850 },
+ { -21911077, -5927941, -4611316, -5560156, -31744103, -10785293, 24123614, 15193618, -21652117, -16739389 },
+ { -9935934, -4289447, -25279823, 4372842, 2087473, 10399484, 31870908, 14690798, 17361620, 11864968 },
+ },
+ {
+ { -11307610, 6210372, 13206574, 5806320, -29017692, -13967200, -12331205, -7486601, -25578460, -16240689 },
+ { 14668462, -12270235, 26039039, 15305210, 25515617, 4542480, 10453892, 6577524, 9145645, -6443880 },
+ { 5974874, 3053895, -9433049, -10385191, -31865124, 3225009, -7972642, 3936128, -5652273, -3050304 },
+ },
+ {
+ { 30625386, -4729400, -25555961, -12792866, -20484575, 7695099, 17097188, -16303496, -27999779, 1803632 },
+ { -3553091, 9865099, -5228566, 4272701, -5673832, -16689700, 14911344, 12196514, -21405489, 7047412 },
+ { 20093277, 9920966, -11138194, -5343857, 13161587, 12044805, -32856851, 4124601, -32343828, -10257566 },
+ },
+ {
+ { -20788824, 14084654, -13531713, 7842147, 19119038, -13822605, 4752377, -8714640, -21679658, 2288038 },
+ { -26819236, -3283715, 29965059, 3039786, -14473765, 2540457, 29457502, 14625692, -24819617, 12570232 },
+ { -1063558, -11551823, 16920318, 12494842, 1278292, -5869109, -21159943, -3498680, -11974704, 4724943 },
+ },
+ {
+ { 17960970, -11775534, -4140968, -9702530, -8876562, -1410617, -12907383, -8659932, -29576300, 1903856 },
+ { 23134274, -14279132, -10681997, -1611936, 20684485, 15770816, -12989750, 3190296, 26955097, 14109738 },
+ { 15308788, 5320727, -30113809, -14318877, 22902008, 7767164, 29425325, -11277562, 31960942, 11934971 },
+ },
+ {
+ { -27395711, 8435796, 4109644, 12222639, -24627868, 14818669, 20638173, 4875028, 10491392, 1379718 },
+ { -13159415, 9197841, 3875503, -8936108, -1383712, -5879801, 33518459, 16176658, 21432314, 12180697 },
+ { -11787308, 11500838, 13787581, -13832590, -22430679, 10140205, 1465425, 12689540, -10301319, -13872883 },
+ },
+ },
+ {
+ {
+ { 5414091, -15386041, -21007664, 9643570, 12834970, 1186149, -2622916, -1342231, 26128231, 6032912 },
+ { -26337395, -13766162, 32496025, -13653919, 17847801, -12669156, 3604025, 8316894, -25875034, -10437358 },
+ { 3296484, 6223048, 24680646, -12246460, -23052020, 5903205, -8862297, -4639164, 12376617, 3188849 },
+ },
+ {
+ { 29190488, -14659046, 27549113, -1183516, 3520066, -10697301, 32049515, -7309113, -16109234, -9852307 },
+ { -14744486, -9309156, 735818, -598978, -20407687, -5057904, 25246078, -15795669, 18640741, -960977 },
+ { -6928835, -16430795, 10361374, 5642961, 4910474, 12345252, -31638386, -494430, 10530747, 1053335 },
+ },
+ {
+ { -29265967, -14186805, -13538216, -12117373, -19457059, -10655384, -31462369, -2948985, 24018831, 15026644 },
+ { -22592535, -3145277, -2289276, 5953843, -13440189, 9425631, 25310643, 13003497, -2314791, -15145616 },
+ { -27419985, -603321, -8043984, -1669117, -26092265, 13987819, -27297622, 187899, -23166419, -2531735 },
+ },
+ {
+ { -21744398, -13810475, 1844840, 5021428, -10434399, -15911473, 9716667, 16266922, -5070217, 726099 },
+ { 29370922, -6053998, 7334071, -15342259, 9385287, 2247707, -13661962, -4839461, 30007388, -15823341 },
+ { -936379, 16086691, 23751945, -543318, -1167538, -5189036, 9137109, 730663, 9835848, 4555336 },
+ },
+ {
+ { -23376435, 1410446, -22253753, -12899614, 30867635, 15826977, 17693930, 544696, -11985298, 12422646 },
+ { 31117226, -12215734, -13502838, 6561947, -9876867, -12757670, -5118685, -4096706, 29120153, 13924425 },
+ { -17400879, -14233209, 19675799, -2734756, -11006962, -5858820, -9383939, -11317700, 7240931, -237388 },
+ },
+ {
+ { -31361739, -11346780, -15007447, -5856218, -22453340, -12152771, 1222336, 4389483, 3293637, -15551743 },
+ { -16684801, -14444245, 11038544, 11054958, -13801175, -3338533, -24319580, 7733547, 12796905, -6335822 },
+ { -8759414, -10817836, -25418864, 10783769, -30615557, -9746811, -28253339, 3647836, 3222231, -11160462 },
+ },
+ {
+ { 18606113, 1693100, -25448386, -15170272, 4112353, 10045021, 23603893, -2048234, -7550776, 2484985 },
+ { 9255317, -3131197, -12156162, -1004256, 13098013, -9214866, 16377220, -2102812, -19802075, -3034702 },
+ { -22729289, 7496160, -5742199, 11329249, 19991973, -3347502, -31718148, 9936966, -30097688, -10618797 },
+ },
+ {
+ { 21878590, -5001297, 4338336, 13643897, -3036865, 13160960, 19708896, 5415497, -7360503, -4109293 },
+ { 27736861, 10103576, 12500508, 8502413, -3413016, -9633558, 10436918, -1550276, -23659143, -8132100 },
+ { 19492550, -12104365, -29681976, -852630, -3208171, 12403437, 30066266, 8367329, 13243957, 8709688 },
+ },
+ },
+ {
+ {
+ { 12015105, 2801261, 28198131, 10151021, 24818120, -4743133, -11194191, -5645734, 5150968, 7274186 },
+ { 2831366, -12492146, 1478975, 6122054, 23825128, -12733586, 31097299, 6083058, 31021603, -9793610 },
+ { -2529932, -2229646, 445613, 10720828, -13849527, -11505937, -23507731, 16354465, 15067285, -14147707 },
+ },
+ {
+ { 7840942, 14037873, -33364863, 15934016, -728213, -3642706, 21403988, 1057586, -19379462, -12403220 },
+ { 915865, -16469274, 15608285, -8789130, -24357026, 6060030, -17371319, 8410997, -7220461, 16527025 },
+ { 32922597, -556987, 20336074, -16184568, 10903705, -5384487, 16957574, 52992, 23834301, 6588044 },
+ },
+ {
+ { 32752030, 11232950, 3381995, -8714866, 22652988, -10744103, 17159699, 16689107, -20314580, -1305992 },
+ { -4689649, 9166776, -25710296, -10847306, 11576752, 12733943, 7924251, -2752281, 1976123, -7249027 },
+ { 21251222, 16309901, -2983015, -6783122, 30810597, 12967303, 156041, -3371252, 12331345, -8237197 },
+ },
+ {
+ { 8651614, -4477032, -16085636, -4996994, 13002507, 2950805, 29054427, -5106970, 10008136, -4667901 },
+ { 31486080, 15114593, -14261250, 12951354, 14369431, -7387845, 16347321, -13662089, 8684155, -10532952 },
+ { 19443825, 11385320, 24468943, -9659068, -23919258, 2187569, -26263207, -6086921, 31316348, 14219878 },
+ },
+ {
+ { -28594490, 1193785, 32245219, 11392485, 31092169, 15722801, 27146014, 6992409, 29126555, 9207390 },
+ { 32382935, 1110093, 18477781, 11028262, -27411763, -7548111, -4980517, 10843782, -7957600, -14435730 },
+ { 2814918, 7836403, 27519878, -7868156, -20894015, -11553689, -21494559, 8550130, 28346258, 1994730 },
+ },
+ {
+ { -19578299, 8085545, -14000519, -3948622, 2785838, -16231307, -19516951, 7174894, 22628102, 8115180 },
+ { -30405132, 955511, -11133838, -15078069, -32447087, -13278079, -25651578, 3317160, -9943017, 930272 },
+ { -15303681, -6833769, 28856490, 1357446, 23421993, 1057177, 24091212, -1388970, -22765376, -10650715 },
+ },
+ {
+ { -22751231, -5303997, -12907607, -12768866, -15811511, -7797053, -14839018, -16554220, -1867018, 8398970 },
+ { -31969310, 2106403, -4736360, 1362501, 12813763, 16200670, 22981545, -6291273, 18009408, -15772772 },
+ { -17220923, -9545221, -27784654, 14166835, 29815394, 7444469, 29551787, -3727419, 19288549, 1325865 },
+ },
+ {
+ { 15100157, -15835752, -23923978, -1005098, -26450192, 15509408, 12376730, -3479146, 33166107, -8042750 },
+ { 20909231, 13023121, -9209752, 16251778, -5778415, -8094914, 12412151, 10018715, 2213263, -13878373 },
+ { 32529814, -11074689, 30361439, -16689753, -9135940, 1513226, 22922121, 6382134, -5766928, 8371348 },
+ },
+ },
+ {
+ {
+ { 9923462, 11271500, 12616794, 3544722, -29998368, -1721626, 12891687, -8193132, -26442943, 10486144 },
+ { -22597207, -7012665, 8587003, -8257861, 4084309, -12970062, 361726, 2610596, -23921530, -11455195 },
+ { 5408411, -1136691, -4969122, 10561668, 24145918, 14240566, 31319731, -4235541, 19985175, -3436086 },
+ },
+ {
+ { -13994457, 16616821, 14549246, 3341099, 32155958, 13648976, -17577068, 8849297, 65030, 8370684 },
+ { -8320926, -12049626, 31204563, 5839400, -20627288, -1057277, -19442942, 6922164, 12743482, -9800518 },
+ { -2361371, 12678785, 28815050, 4759974, -23893047, 4884717, 23783145, 11038569, 18800704, 255233 },
+ },
+ {
+ { -5269658, -1773886, 13957886, 7990715, 23132995, 728773, 13393847, 9066957, 19258688, -14753793 },
+ { -2936654, -10827535, -10432089, 14516793, -3640786, 4372541, -31934921, 2209390, -1524053, 2055794 },
+ { 580882, 16705327, 5468415, -2683018, -30926419, -14696000, -7203346, -8994389, -30021019, 7394435 },
+ },
+ {
+ { 23838809, 1822728, -15738443, 15242727, 8318092, -3733104, -21672180, -3492205, -4821741, 14799921 },
+ { 13345610, 9759151, 3371034, -16137791, 16353039, 8577942, 31129804, 13496856, -9056018, 7402518 },
+ { 2286874, -4435931, -20042458, -2008336, -13696227, 5038122, 11006906, -15760352, 8205061, 1607563 },
+ },
+ {
+ { 14414086, -8002132, 3331830, -3208217, 22249151, -5594188, 18364661, -2906958, 30019587, -9029278 },
+ { -27688051, 1585953, -10775053, 931069, -29120221, -11002319, -14410829, 12029093, 9944378, 8024 },
+ { 4368715, -3709630, 29874200, -15022983, -20230386, -11410704, -16114594, -999085, -8142388, 5640030 },
+ },
+ {
+ { 10299610, 13746483, 11661824, 16234854, 7630238, 5998374, 9809887, -16694564, 15219798, -14327783 },
+ { 27425505, -5719081, 3055006, 10660664, 23458024, 595578, -15398605, -1173195, -18342183, 9742717 },
+ { 6744077, 2427284, 26042789, 2720740, -847906, 1118974, 32324614, 7406442, 12420155, 1994844 },
+ },
+ {
+ { 14012521, -5024720, -18384453, -9578469, -26485342, -3936439, -13033478, -10909803, 24319929, -6446333 },
+ { 16412690, -4507367, 10772641, 15929391, -17068788, -4658621, 10555945, -10484049, -30102368, -4739048 },
+ { 22397382, -7767684, -9293161, -12792868, 17166287, -9755136, -27333065, 6199366, 21880021, -12250760 },
+ },
+ {
+ { -4283307, 5368523, -31117018, 8163389, -30323063, 3209128, 16557151, 8890729, 8840445, 4957760 },
+ { -15447727, 709327, -6919446, -10870178, -29777922, 6522332, -21720181, 12130072, -14796503, 5005757 },
+ { -2114751, -14308128, 23019042, 15765735, -25269683, 6002752, 10183197, -13239326, -16395286, -2176112 },
+ },
+ },
+ {
+ {
+ { -19025756, 1632005, 13466291, -7995100, -23640451, 16573537, -32013908, -3057104, 22208662, 2000468 },
+ { 3065073, -1412761, -25598674, -361432, -17683065, -5703415, -8164212, 11248527, -3691214, -7414184 },
+ { 10379208, -6045554, 8877319, 1473647, -29291284, -12507580, 16690915, 2553332, -3132688, 16400289 },
+ },
+ {
+ { 15716668, 1254266, -18472690, 7446274, -8448918, 6344164, -22097271, -7285580, 26894937, 9132066 },
+ { 24158887, 12938817, 11085297, -8177598, -28063478, -4457083, -30576463, 64452, -6817084, -2692882 },
+ { 13488534, 7794716, 22236231, 5989356, 25426474, -12578208, 2350710, -3418511, -4688006, 2364226 },
+ },
+ {
+ { 16335052, 9132434, 25640582, 6678888, 1725628, 8517937, -11807024, -11697457, 15445875, -7798101 },
+ { 29004207, -7867081, 28661402, -640412, -12794003, -7943086, 31863255, -4135540, -278050, -15759279 },
+ { -6122061, -14866665, -28614905, 14569919, -10857999, -3591829, 10343412, -6976290, -29828287, -10815811 },
+ },
+ {
+ { 27081650, 3463984, 14099042, -4517604, 1616303, -6205604, 29542636, 15372179, 17293797, 960709 },
+ { 20263915, 11434237, -5765435, 11236810, 13505955, -10857102, -16111345, 6493122, -19384511, 7639714 },
+ { -2830798, -14839232, 25403038, -8215196, -8317012, -16173699, 18006287, -16043750, 29994677, -15808121 },
+ },
+ {
+ { 9769828, 5202651, -24157398, -13631392, -28051003, -11561624, -24613141, -13860782, -31184575, 709464 },
+ { 12286395, 13076066, -21775189, -1176622, -25003198, 4057652, -32018128, -8890874, 16102007, 13205847 },
+ { 13733362, 5599946, 10557076, 3195751, -5557991, 8536970, -25540170, 8525972, 10151379, 10394400 },
+ },
+ {
+ { 4024660, -16137551, 22436262, 12276534, -9099015, -2686099, 19698229, 11743039, -33302334, 8934414 },
+ { -15879800, -4525240, -8580747, -2934061, 14634845, -698278, -9449077, 3137094, -11536886, 11721158 },
+ { 17555939, -5013938, 8268606, 2331751, -22738815, 9761013, 9319229, 8835153, -9205489, -1280045 },
+ },
+ {
+ { -461409, -7830014, 20614118, 16688288, -7514766, -4807119, 22300304, 505429, 6108462, -6183415 },
+ { -5070281, 12367917, -30663534, 3234473, 32617080, -8422642, 29880583, -13483331, -26898490, -7867459 },
+ { -31975283, 5726539, 26934134, 10237677, -3173717, -605053, 24199304, 3795095, 7592688, -14992079 },
+ },
+ {
+ { 21594432, -14964228, 17466408, -4077222, 32537084, 2739898, 6407723, 12018833, -28256052, 4298412 },
+ { -20650503, -11961496, -27236275, 570498, 3767144, -1717540, 13891942, -1569194, 13717174, 10805743 },
+ { -14676630, -15644296, 15287174, 11927123, 24177847, -8175568, -796431, 14860609, -26938930, -5863836 },
+ },
+ },
+ {
+ {
+ { 12962541, 5311799, -10060768, 11658280, 18855286, -7954201, 13286263, -12808704, -4381056, 9882022 },
+ { 18512079, 11319350, -20123124, 15090309, 18818594, 5271736, -22727904, 3666879, -23967430, -3299429 },
+ { -6789020, -3146043, 16192429, 13241070, 15898607, -14206114, -10084880, -6661110, -2403099, 5276065 },
+ },
+ {
+ { 30169808, -5317648, 26306206, -11750859, 27814964, 7069267, 7152851, 3684982, 1449224, 13082861 },
+ { 10342826, 3098505, 2119311, 193222, 25702612, 12233820, 23697382, 15056736, -21016438, -8202000 },
+ { -33150110, 3261608, 22745853, 7948688, 19370557, -15177665, -26171976, 6482814, -10300080, -11060101 },
+ },
+ {
+ { 32869458, -5408545, 25609743, 15678670, -10687769, -15471071, 26112421, 2521008, -22664288, 6904815 },
+ { 29506923, 4457497, 3377935, -9796444, -30510046, 12935080, 1561737, 3841096, -29003639, -6657642 },
+ { 10340844, -6630377, -18656632, -2278430, 12621151, -13339055, 30878497, -11824370, -25584551, 5181966 },
+ },
+ {
+ { 25940115, -12658025, 17324188, -10307374, -8671468, 15029094, 24396252, -16450922, -2322852, -12388574 },
+ { -21765684, 9916823, -1300409, 4079498, -1028346, 11909559, 1782390, 12641087, 20603771, -6561742 },
+ { -18882287, -11673380, 24849422, 11501709, 13161720, -4768874, 1925523, 11914390, 4662781, 7820689 },
+ },
+ {
+ { 12241050, -425982, 8132691, 9393934, 32846760, -1599620, 29749456, 12172924, 16136752, 15264020 },
+ { -10349955, -14680563, -8211979, 2330220, -17662549, -14545780, 10658213, 6671822, 19012087, 3772772 },
+ { 3753511, -3421066, 10617074, 2028709, 14841030, -6721664, 28718732, -15762884, 20527771, 12988982 },
+ },
+ {
+ { -14822485, -5797269, -3707987, 12689773, -898983, -10914866, -24183046, -10564943, 3299665, -12424953 },
+ { -16777703, -15253301, -9642417, 4978983, 3308785, 8755439, 6943197, 6461331, -25583147, 8991218 },
+ { -17226263, 1816362, -1673288, -6086439, 31783888, -8175991, -32948145, 7417950, -30242287, 1507265 },
+ },
+ {
+ { 29692663, 6829891, -10498800, 4334896, 20945975, -11906496, -28887608, 8209391, 14606362, -10647073 },
+ { -3481570, 8707081, 32188102, 5672294, 22096700, 1711240, -33020695, 9761487, 4170404, -2085325 },
+ { -11587470, 14855945, -4127778, -1531857, -26649089, 15084046, 22186522, 16002000, -14276837, -8400798 },
+ },
+ {
+ { -4811456, 13761029, -31703877, -2483919, -3312471, 7869047, -7113572, -9620092, 13240845, 10965870 },
+ { -7742563, -8256762, -14768334, -13656260, -23232383, 12387166, 4498947, 14147411, 29514390, 4302863 },
+ { -13413405, -12407859, 20757302, -13801832, 14785143, 8976368, -5061276, -2144373, 17846988, -13971927 },
+ },
+ },
+ {
+ {
+ { -2244452, -754728, -4597030, -1066309, -6247172, 1455299, -21647728, -9214789, -5222701, 12650267 },
+ { -9906797, -16070310, 21134160, 12198166, -27064575, 708126, 387813, 13770293, -19134326, 10958663 },
+ { 22470984, 12369526, 23446014, -5441109, -21520802, -9698723, -11772496, -11574455, -25083830, 4271862 },
+ },
+ {
+ { -25169565, -10053642, -19909332, 15361595, -5984358, 2159192, 75375, -4278529, -32526221, 8469673 },
+ { 15854970, 4148314, -8893890, 7259002, 11666551, 13824734, -30531198, 2697372, 24154791, -9460943 },
+ { 15446137, -15806644, 29759747, 14019369, 30811221, -9610191, -31582008, 12840104, 24913809, 9815020 },
+ },
+ {
+ { -4709286, -5614269, -31841498, -12288893, -14443537, 10799414, -9103676, 13438769, 18735128, 9466238 },
+ { 11933045, 9281483, 5081055, -5183824, -2628162, -4905629, -7727821, -10896103, -22728655, 16199064 },
+ { 14576810, 379472, -26786533, -8317236, -29426508, -10812974, -102766, 1876699, 30801119, 2164795 },
+ },
+ {
+ { 15995086, 3199873, 13672555, 13712240, -19378835, -4647646, -13081610, -15496269, -13492807, 1268052 },
+ { -10290614, -3659039, -3286592, 10948818, 23037027, 3794475, -3470338, -12600221, -17055369, 3565904 },
+ { 29210088, -9419337, -5919792, -4952785, 10834811, -13327726, -16512102, -10820713, -27162222, -14030531 },
+ },
+ {
+ { -13161890, 15508588, 16663704, -8156150, -28349942, 9019123, -29183421, -3769423, 2244111, -14001979 },
+ { -5152875, -3800936, -9306475, -6071583, 16243069, 14684434, -25673088, -16180800, 13491506, 4641841 },
+ { 10813417, 643330, -19188515, -728916, 30292062, -16600078, 27548447, -7721242, 14476989, -12767431 },
+ },
+ {
+ { 10292079, 9984945, 6481436, 8279905, -7251514, 7032743, 27282937, -1644259, -27912810, 12651324 },
+ { -31185513, -813383, 22271204, 11835308, 10201545, 15351028, 17099662, 3988035, 21721536, -3148940 },
+ { 10202177, -6545839, -31373232, -9574638, -32150642, -8119683, -12906320, 3852694, 13216206, 14842320 },
+ },
+ {
+ { -15815640, -10601066, -6538952, -7258995, -6984659, -6581778, -31500847, 13765824, -27434397, 9900184 },
+ { 14465505, -13833331, -32133984, -14738873, -27443187, 12990492, 33046193, 15796406, -7051866, -8040114 },
+ { 30924417, -8279620, 6359016, -12816335, 16508377, 9071735, -25488601, 15413635, 9524356, -7018878 },
+ },
+ {
+ { 12274201, -13175547, 32627641, -1785326, 6736625, 13267305, 5237659, -5109483, 15663516, 4035784 },
+ { -2951309, 8903985, 17349946, 601635, -16432815, -4612556, -13732739, -15889334, -22258478, 4659091 },
+ { -16916263, -4952973, -30393711, -15158821, 20774812, 15897498, 5736189, 15026997, -2178256, -13455585 },
+ },
+ },
+ {
+ {
+ { -8858980, -2219056, 28571666, -10155518, -474467, -10105698, -3801496, 278095, 23440562, -290208 },
+ { 10226241, -5928702, 15139956, 120818, -14867693, 5218603, 32937275, 11551483, -16571960, -7442864 },
+ { 17932739, -12437276, -24039557, 10749060, 11316803, 7535897, 22503767, 5561594, -3646624, 3898661 },
+ },
+ {
+ { 7749907, -969567, -16339731, -16464, -25018111, 15122143, -1573531, 7152530, 21831162, 1245233 },
+ { 26958459, -14658026, 4314586, 8346991, -5677764, 11960072, -32589295, -620035, -30402091, -16716212 },
+ { -12165896, 9166947, 33491384, 13673479, 29787085, 13096535, 6280834, 14587357, -22338025, 13987525 },
+ },
+ {
+ { -24349909, 7778775, 21116000, 15572597, -4833266, -5357778, -4300898, -5124639, -7469781, -2858068 },
+ { 9681908, -6737123, -31951644, 13591838, -6883821, 386950, 31622781, 6439245, -14581012, 4091397 },
+ { -8426427, 1470727, -28109679, -1596990, 3978627, -5123623, -19622683, 12092163, 29077877, -14741988 },
+ },
+ {
+ { 5269168, -6859726, -13230211, -8020715, 25932563, 1763552, -5606110, -5505881, -20017847, 2357889 },
+ { 32264008, -15407652, -5387735, -1160093, -2091322, -3946900, 23104804, -12869908, 5727338, 189038 },
+ { 14609123, -8954470, -6000566, -16622781, -14577387, -7743898, -26745169, 10942115, -25888931, -14884697 },
+ },
+ {
+ { 20513500, 5557931, -15604613, 7829531, 26413943, -2019404, -21378968, 7471781, 13913677, -5137875 },
+ { -25574376, 11967826, 29233242, 12948236, -6754465, 4713227, -8940970, 14059180, 12878652, 8511905 },
+ { -25656801, 3393631, -2955415, -7075526, -2250709, 9366908, -30223418, 6812974, 5568676, -3127656 },
+ },
+ {
+ { 11630004, 12144454, 2116339, 13606037, 27378885, 15676917, -17408753, -13504373, -14395196, 8070818 },
+ { 27117696, -10007378, -31282771, -5570088, 1127282, 12772488, -29845906, 10483306, -11552749, -1028714 },
+ { 10637467, -5688064, 5674781, 1072708, -26343588, -6982302, -1683975, 9177853, -27493162, 15431203 },
+ },
+ {
+ { 20525145, 10892566, -12742472, 12779443, -29493034, 16150075, -28240519, 14943142, -15056790, -7935931 },
+ { -30024462, 5626926, -551567, -9981087, 753598, 11981191, 25244767, -3239766, -3356550, 9594024 },
+ { -23752644, 2636870, -5163910, -10103818, 585134, 7877383, 11345683, -6492290, 13352335, -10977084 },
+ },
+ {
+ { -1931799, -5407458, 3304649, -12884869, 17015806, -4877091, -29783850, -7752482, -13215537, -319204 },
+ { 20239939, 6607058, 6203985, 3483793, -18386976, -779229, -20723742, 15077870, -22750759, 14523817 },
+ { 27406042, -6041657, 27423596, -4497394, 4996214, 10002360, -28842031, -4545494, -30172742, -4805667 },
+ },
+ },
+ {
+ {
+ { 11374242, 12660715, 17861383, -12540833, 10935568, 1099227, -13886076, -9091740, -27727044, 11358504 },
+ { -12730809, 10311867, 1510375, 10778093, -2119455, -9145702, 32676003, 11149336, -26123651, 4985768 },
+ { -19096303, 341147, -6197485, -239033, 15756973, -8796662, -983043, 13794114, -19414307, -15621255 },
+ },
+ {
+ { 6490081, 11940286, 25495923, -7726360, 8668373, -8751316, 3367603, 6970005, -1691065, -9004790 },
+ { 1656497, 13457317, 15370807, 6364910, 13605745, 8362338, -19174622, -5475723, -16796596, -5031438 },
+ { -22273315, -13524424, -64685, -4334223, -18605636, -10921968, -20571065, -7007978, -99853, -10237333 },
+ },
+ {
+ { 17747465, 10039260, 19368299, -4050591, -20630635, -16041286, 31992683, -15857976, -29260363, -5511971 },
+ { 31932027, -4986141, -19612382, 16366580, 22023614, 88450, 11371999, -3744247, 4882242, -10626905 },
+ { 29796507, 37186, 19818052, 10115756, -11829032, 3352736, 18551198, 3272828, -5190932, -4162409 },
+ },
+ {
+ { 12501286, 4044383, -8612957, -13392385, -32430052, 5136599, -19230378, -3529697, 330070, -3659409 },
+ { 6384877, 2899513, 17807477, 7663917, -2358888, 12363165, 25366522, -8573892, -271295, 12071499 },
+ { -8365515, -4042521, 25133448, -4517355, -6211027, 2265927, -32769618, 1936675, -5159697, 3829363 },
+ },
+ {
+ { 28425966, -5835433, -577090, -4697198, -14217555, 6870930, 7921550, -6567787, 26333140, 14267664 },
+ { -11067219, 11871231, 27385719, -10559544, -4585914, -11189312, 10004786, -8709488, -21761224, 8930324 },
+ { -21197785, -16396035, 25654216, -1725397, 12282012, 11008919, 1541940, 4757911, -26491501, -16408940 },
+ },
+ {
+ { 13537262, -7759490, -20604840, 10961927, -5922820, -13218065, -13156584, 6217254, -15943699, 13814990 },
+ { -17422573, 15157790, 18705543, 29619, 24409717, -260476, 27361681, 9257833, -1956526, -1776914 },
+ { -25045300, -10191966, 15366585, 15166509, -13105086, 8423556, -29171540, 12361135, -18685978, 4578290 },
+ },
+ {
+ { 24579768, 3711570, 1342322, -11180126, -27005135, 14124956, -22544529, 14074919, 21964432, 8235257 },
+ { -6528613, -2411497, 9442966, -5925588, 12025640, -1487420, -2981514, -1669206, 13006806, 2355433 },
+ { -16304899, -13605259, -6632427, -5142349, 16974359, -10911083, 27202044, 1719366, 1141648, -12796236 },
+ },
+ {
+ { -12863944, -13219986, -8318266, -11018091, -6810145, -4843894, 13475066, -3133972, 32674895, 13715045 },
+ { 11423335, -5468059, 32344216, 8962751, 24989809, 9241752, -13265253, 16086212, -28740881, -15642093 },
+ { -1409668, 12530728, -6368726, 10847387, 19531186, -14132160, -11709148, 7791794, -27245943, 4383347 },
+ },
+ },
+ {
+ {
+ { -28970898, 5271447, -1266009, -9736989, -12455236, 16732599, -4862407, -4906449, 27193557, 6245191 },
+ { -15193956, 5362278, -1783893, 2695834, 4960227, 12840725, 23061898, 3260492, 22510453, 8577507 },
+ { -12632451, 11257346, -32692994, 13548177, -721004, 10879011, 31168030, 13952092, -29571492, -3635906 },
+ },
+ {
+ { 3877321, -9572739, 32416692, 5405324, -11004407, -13656635, 3759769, 11935320, 5611860, 8164018 },
+ { -16275802, 14667797, 15906460, 12155291, -22111149, -9039718, 32003002, -8832289, 5773085, -8422109 },
+ { -23788118, -8254300, 1950875, 8937633, 18686727, 16459170, -905725, 12376320, 31632953, 190926 },
+ },
+ {
+ { -24593607, -16138885, -8423991, 13378746, 14162407, 6901328, -8288749, 4508564, -25341555, -3627528 },
+ { 8884438, -5884009, 6023974, 10104341, -6881569, -4941533, 18722941, -14786005, -1672488, 827625 },
+ { -32720583, -16289296, -32503547, 7101210, 13354605, 2659080, -1800575, -14108036, -24878478, 1541286 },
+ },
+ {
+ { 2901347, -1117687, 3880376, -10059388, -17620940, -3612781, -21802117, -3567481, 20456845, -1885033 },
+ { 27019610, 12299467, -13658288, -1603234, -12861660, -4861471, -19540150, -5016058, 29439641, 15138866 },
+ { 21536104, -6626420, -32447818, -10690208, -22408077, 5175814, -5420040, -16361163, 7779328, 109896 },
+ },
+ {
+ { 30279744, 14648750, -8044871, 6425558, 13639621, -743509, 28698390, 12180118, 23177719, -554075 },
+ { 26572847, 3405927, -31701700, 12890905, -19265668, 5335866, -6493768, 2378492, 4439158, -13279347 },
+ { -22716706, 3489070, -9225266, -332753, 18875722, -1140095, 14819434, -12731527, -17717757, -5461437 },
+ },
+ {
+ { -5056483, 16566551, 15953661, 3767752, -10436499, 15627060, -820954, 2177225, 8550082, -15114165 },
+ { -18473302, 16596775, -381660, 15663611, 22860960, 15585581, -27844109, -3582739, -23260460, -8428588 },
+ { -32480551, 15707275, -8205912, -5652081, 29464558, 2713815, -22725137, 15860482, -21902570, 1494193 },
+ },
+ {
+ { -19562091, -14087393, -25583872, -9299552, 13127842, 759709, 21923482, 16529112, 8742704, 12967017 },
+ { -28464899, 1553205, 32536856, -10473729, -24691605, -406174, -8914625, -2933896, -29903758, 15553883 },
+ { 21877909, 3230008, 9881174, 10539357, -4797115, 2841332, 11543572, 14513274, 19375923, -12647961 },
+ },
+ {
+ { 8832269, -14495485, 13253511, 5137575, 5037871, 4078777, 24880818, -6222716, 2862653, 9455043 },
+ { 29306751, 5123106, 20245049, -14149889, 9592566, 8447059, -2077124, -2990080, 15511449, 4789663 },
+ { -20679756, 7004547, 8824831, -9434977, -4045704, -3750736, -5754762, 108893, 23513200, 16652362 },
+ },
+ },
+ {
+ {
+ { -33256173, 4144782, -4476029, -6579123, 10770039, -7155542, -6650416, -12936300, -18319198, 10212860 },
+ { 2756081, 8598110, 7383731, -6859892, 22312759, -1105012, 21179801, 2600940, -9988298, -12506466 },
+ { -24645692, 13317462, -30449259, -15653928, 21365574, -10869657, 11344424, 864440, -2499677, -16710063 },
+ },
+ {
+ { -26432803, 6148329, -17184412, -14474154, 18782929, -275997, -22561534, 211300, 2719757, 4940997 },
+ { -1323882, 3911313, -6948744, 14759765, -30027150, 7851207, 21690126, 8518463, 26699843, 5276295 },
+ { -13149873, -6429067, 9396249, 365013, 24703301, -10488939, 1321586, 149635, -15452774, 7159369 },
+ },
+ {
+ { 9987780, -3404759, 17507962, 9505530, 9731535, -2165514, 22356009, 8312176, 22477218, -8403385 },
+ { 18155857, -16504990, 19744716, 9006923, 15154154, -10538976, 24256460, -4864995, -22548173, 9334109 },
+ { 2986088, -4911893, 10776628, -3473844, 10620590, -7083203, -21413845, 14253545, -22587149, 536906 },
+ },
+ {
+ { 4377756, 8115836, 24567078, 15495314, 11625074, 13064599, 7390551, 10589625, 10838060, -15420424 },
+ { -19342404, 867880, 9277171, -3218459, -14431572, -1986443, 19295826, -15796950, 6378260, 699185 },
+ { 7895026, 4057113, -7081772, -13077756, -17886831, -323126, -716039, 15693155, -5045064, -13373962 },
+ },
+ {
+ { -7737563, -5869402, -14566319, -7406919, 11385654, 13201616, 31730678, -10962840, -3918636, -9669325 },
+ { 10188286, -15770834, -7336361, 13427543, 22223443, 14896287, 30743455, 7116568, -21786507, 5427593 },
+ { 696102, 13206899, 27047647, -10632082, 15285305, -9853179, 10798490, -4578720, 19236243, 12477404 },
+ },
+ {
+ { -11229439, 11243796, -17054270, -8040865, -788228, -8167967, -3897669, 11180504, -23169516, 7733644 },
+ { 17800790, -14036179, -27000429, -11766671, 23887827, 3149671, 23466177, -10538171, 10322027, 15313801 },
+ { 26246234, 11968874, 32263343, -5468728, 6830755, -13323031, -15794704, -101982, -24449242, 10890804 },
+ },
+ {
+ { -31365647, 10271363, -12660625, -6267268, 16690207, -13062544, -14982212, 16484931, 25180797, -5334884 },
+ { -586574, 10376444, -32586414, -11286356, 19801893, 10997610, 2276632, 9482883, 316878, 13820577 },
+ { -9882808, -4510367, -2115506, 16457136, -11100081, 11674996, 30756178, -7515054, 30696930, -3712849 },
+ },
+ {
+ { 32988917, -9603412, 12499366, 7910787, -10617257, -11931514, -7342816, -9985397, -32349517, 7392473 },
+ { -8855661, 15927861, 9866406, -3649411, -2396914, -16655781, -30409476, -9134995, 25112947, -2926644 },
+ { -2504044, -436966, 25621774, -5678772, 15085042, -5479877, -24884878, -13526194, 5537438, -13914319 },
+ },
+ },
+ {
+ {
+ { -11225584, 2320285, -9584280, 10149187, -33444663, 5808648, -14876251, -1729667, 31234590, 6090599 },
+ { -9633316, 116426, 26083934, 2897444, -6364437, -2688086, 609721, 15878753, -6970405, -9034768 },
+ { -27757857, 247744, -15194774, -9002551, 23288161, -10011936, -23869595, 6503646, 20650474, 1804084 },
+ },
+ {
+ { -27589786, 15456424, 8972517, 8469608, 15640622, 4439847, 3121995, -10329713, 27842616, -202328 },
+ { -15306973, 2839644, 22530074, 10026331, 4602058, 5048462, 28248656, 5031932, -11375082, 12714369 },
+ { 20807691, -7270825, 29286141, 11421711, -27876523, -13868230, -21227475, 1035546, -19733229, 12796920 },
+ },
+ {
+ { 12076899, -14301286, -8785001, -11848922, -25012791, 16400684, -17591495, -12899438, 3480665, -15182815 },
+ { -32361549, 5457597, 28548107, 7833186, 7303070, -11953545, -24363064, -15921875, -33374054, 2771025 },
+ { -21389266, 421932, 26597266, 6860826, 22486084, -6737172, -17137485, -4210226, -24552282, 15673397 },
+ },
+ {
+ { -20184622, 2338216, 19788685, -9620956, -4001265, -8740893, -20271184, 4733254, 3727144, -12934448 },
+ { 6120119, 814863, -11794402, -622716, 6812205, -15747771, 2019594, 7975683, 31123697, -10958981 },
+ { 30069250, -11435332, 30434654, 2958439, 18399564, -976289, 12296869, 9204260, -16432438, 9648165 },
+ },
+ {
+ { 32705432, -1550977, 30705658, 7451065, -11805606, 9631813, 3305266, 5248604, -26008332, -11377501 },
+ { 17219865, 2375039, -31570947, -5575615, -19459679, 9219903, 294711, 15298639, 2662509, -16297073 },
+ { -1172927, -7558695, -4366770, -4287744, -21346413, -8434326, 32087529, -1222777, 32247248, -14389861 },
+ },
+ {
+ { 14312628, 1221556, 17395390, -8700143, -4945741, -8684635, -28197744, -9637817, -16027623, -13378845 },
+ { -1428825, -9678990, -9235681, 6549687, -7383069, -468664, 23046502, 9803137, 17597934, 2346211 },
+ { 18510800, 15337574, 26171504, 981392, -22241552, 7827556, -23491134, -11323352, 3059833, -11782870 },
+ },
+ {
+ { 10141598, 6082907, 17829293, -1947643, 9830092, 13613136, -25556636, -5544586, -33502212, 3592096 },
+ { 33114168, -15889352, -26525686, -13343397, 33076705, 8716171, 1151462, 1521897, -982665, -6837803 },
+ { -32939165, -4255815, 23947181, -324178, -33072974, -12305637, -16637686, 3891704, 26353178, 693168 },
+ },
+ {
+ { 30374239, 1595580, -16884039, 13186931, 4600344, 406904, 9585294, -400668, 31375464, 14369965 },
+ { -14370654, -7772529, 1510301, 6434173, -18784789, -6262728, 32732230, -13108839, 17901441, 16011505 },
+ { 18171223, -11934626, -12500402, 15197122, -11038147, -15230035, -19172240, -16046376, 8764035, 12309598 },
+ },
+ },
+ {
+ {
+ { 5975908, -5243188, -19459362, -9681747, -11541277, 14015782, -23665757, 1228319, 17544096, -10593782 },
+ { 5811932, -1715293, 3442887, -2269310, -18367348, -8359541, -18044043, -15410127, -5565381, 12348900 },
+ { -31399660, 11407555, 25755363, 6891399, -3256938, 14872274, -24849353, 8141295, -10632534, -585479 },
+ },
+ {
+ { -12675304, 694026, -5076145, 13300344, 14015258, -14451394, -9698672, -11329050, 30944593, 1130208 },
+ { 8247766, -6710942, -26562381, -7709309, -14401939, -14648910, 4652152, 2488540, 23550156, -271232 },
+ { 17294316, -3788438, 7026748, 15626851, 22990044, 113481, 2267737, -5908146, -408818, -137719 },
+ },
+ {
+ { 16091085, -16253926, 18599252, 7340678, 2137637, -1221657, -3364161, 14550936, 3260525, -7166271 },
+ { -4910104, -13332887, 18550887, 10864893, -16459325, -7291596, -23028869, -13204905, -12748722, 2701326 },
+ { -8574695, 16099415, 4629974, -16340524, -20786213, -6005432, -10018363, 9276971, 11329923, 1862132 },
+ },
+ {
+ { 14763076, -15903608, -30918270, 3689867, 3511892, 10313526, -21951088, 12219231, -9037963, -940300 },
+ { 8894987, -3446094, 6150753, 3013931, 301220, 15693451, -31981216, -2909717, -15438168, 11595570 },
+ { 15214962, 3537601, -26238722, -14058872, 4418657, -15230761, 13947276, 10730794, -13489462, -4363670 },
+ },
+ {
+ { -2538306, 7682793, 32759013, 263109, -29984731, -7955452, -22332124, -10188635, 977108, 699994 },
+ { -12466472, 4195084, -9211532, 550904, -15565337, 12917920, 19118110, -439841, -30534533, -14337913 },
+ { 31788461, -14507657, 4799989, 7372237, 8808585, -14747943, 9408237, -10051775, 12493932, -5409317 },
+ },
+ {
+ { -25680606, 5260744, -19235809, -6284470, -3695942, 16566087, 27218280, 2607121, 29375955, 6024730 },
+ { 842132, -2794693, -4763381, -8722815, 26332018, -12405641, 11831880, 6985184, -9940361, 2854096 },
+ { -4847262, -7969331, 2516242, -5847713, 9695691, -7221186, 16512645, 960770, 12121869, 16648078 },
+ },
+ {
+ { -15218652, 14667096, -13336229, 2013717, 30598287, -464137, -31504922, -7882064, 20237806, 2838411 },
+ { -19288047, 4453152, 15298546, -16178388, 22115043, -15972604, 12544294, -13470457, 1068881, -12499905 },
+ { -9558883, -16518835, 33238498, 13506958, 30505848, -1114596, -8486907, -2630053, 12521378, 4845654 },
+ },
+ {
+ { -28198521, 10744108, -2958380, 10199664, 7759311, -13088600, 3409348, -873400, -6482306, -12885870 },
+ { -23561822, 6230156, -20382013, 10655314, -24040585, -11621172, 10477734, -1240216, -3113227, 13974498 },
+ { 12966261, 15550616, -32038948, -1615346, 21025980, -629444, 5642325, 7188737, 18895762, 12629579 },
+ },
+ },
+ {
+ {
+ { 14741879, -14946887, 22177208, -11721237, 1279741, 8058600, 11758140, 789443, 32195181, 3895677 },
+ { 10758205, 15755439, -4509950, 9243698, -4879422, 6879879, -2204575, -3566119, -8982069, 4429647 },
+ { -2453894, 15725973, -20436342, -10410672, -5803908, -11040220, -7135870, -11642895, 18047436, -15281743 },
+ },
+ {
+ { -25173001, -11307165, 29759956, 11776784, -22262383, -15820455, 10993114, -12850837, -17620701, -9408468 },
+ { 21987233, 700364, -24505048, 14972008, -7774265, -5718395, 32155026, 2581431, -29958985, 8773375 },
+ { -25568350, 454463, -13211935, 16126715, 25240068, 8594567, 20656846, 12017935, -7874389, -13920155 },
+ },
+ {
+ { 6028182, 6263078, -31011806, -11301710, -818919, 2461772, -31841174, -5468042, -1721788, -2776725 },
+ { -12278994, 16624277, 987579, -5922598, 32908203, 1248608, 7719845, -4166698, 28408820, 6816612 },
+ { -10358094, -8237829, 19549651, -12169222, 22082623, 16147817, 20613181, 13982702, -10339570, 5067943 },
+ },
+ {
+ { -30505967, -3821767, 12074681, 13582412, -19877972, 2443951, -19719286, 12746132, 5331210, -10105944 },
+ { 30528811, 3601899, -1957090, 4619785, -27361822, -15436388, 24180793, -12570394, 27679908, -1648928 },
+ { 9402404, -13957065, 32834043, 10838634, -26580150, -13237195, 26653274, -8685565, 22611444, -12715406 },
+ },
+ {
+ { 22190590, 1118029, 22736441, 15130463, -30460692, -5991321, 19189625, -4648942, 4854859, 6622139 },
+ { -8310738, -2953450, -8262579, -3388049, -10401731, -271929, 13424426, -3567227, 26404409, 13001963 },
+ { -31241838, -15415700, -2994250, 8939346, 11562230, -12840670, -26064365, -11621720, -15405155, 11020693 },
+ },
+ {
+ { 1866042, -7949489, -7898649, -10301010, 12483315, 13477547, 3175636, -12424163, 28761762, 1406734 },
+ { -448555, -1777666, 13018551, 3194501, -9580420, -11161737, 24760585, -4347088, 25577411, -13378680 },
+ { -24290378, 4759345, -690653, -1852816, 2066747, 10693769, -29595790, 9884936, -9368926, 4745410 },
+ },
+ {
+ { -9141284, 6049714, -19531061, -4341411, -31260798, 9944276, -15462008, -11311852, 10931924, -11931931 },
+ { -16561513, 14112680, -8012645, 4817318, -8040464, -11414606, -22853429, 10856641, -20470770, 13434654 },
+ { 22759489, -10073434, -16766264, -1871422, 13637442, -10168091, 1765144, -12654326, 28445307, -5364710 },
+ },
+ {
+ { 29875063, 12493613, 2795536, -3786330, 1710620, 15181182, -10195717, -8788675, 9074234, 1167180 },
+ { -26205683, 11014233, -9842651, -2635485, -26908120, 7532294, -18716888, -9535498, 3843903, 9367684 },
+ { -10969595, -6403711, 9591134, 9582310, 11349256, 108879, 16235123, 8601684, -139197, 4242895 },
+ },
+ },
+ {
+ {
+ { 22092954, -13191123, -2042793, -11968512, 32186753, -11517388, -6574341, 2470660, -27417366, 16625501 },
+ { -11057722, 3042016, 13770083, -9257922, 584236, -544855, -7770857, 2602725, -27351616, 14247413 },
+ { 6314175, -10264892, -32772502, 15957557, -10157730, 168750, -8618807, 14290061, 27108877, -1180880 },
+ },
+ {
+ { -8586597, -7170966, 13241782, 10960156, -32991015, -13794596, 33547976, -11058889, -27148451, 981874 },
+ { 22833440, 9293594, -32649448, -13618667, -9136966, 14756819, -22928859, -13970780, -10479804, -16197962 },
+ { -7768587, 3326786, -28111797, 10783824, 19178761, 14905060, 22680049, 13906969, -15933690, 3797899 },
+ },
+ {
+ { 21721356, -4212746, -12206123, 9310182, -3882239, -13653110, 23740224, -2709232, 20491983, -8042152 },
+ { 9209270, -15135055, -13256557, -6167798, -731016, 15289673, 25947805, 15286587, 30997318, -6703063 },
+ { 7392032, 16618386, 23946583, -8039892, -13265164, -1533858, -14197445, -2321576, 17649998, -250080 },
+ },
+ {
+ { -9301088, -14193827, 30609526, -3049543, -25175069, -1283752, -15241566, -9525724, -2233253, 7662146 },
+ { -17558673, 1763594, -33114336, 15908610, -30040870, -12174295, 7335080, -8472199, -3174674, 3440183 },
+ { -19889700, -5977008, -24111293, -9688870, 10799743, -16571957, 40450, -4431835, 4862400, 1133 },
+ },
+ {
+ { -32856209, -7873957, -5422389, 14860950, -16319031, 7956142, 7258061, 311861, -30594991, -7379421 },
+ { -3773428, -1565936, 28985340, 7499440, 24445838, 9325937, 29727763, 16527196, 18278453, 15405622 },
+ { -4381906, 8508652, -19898366, -3674424, -5984453, 15149970, -13313598, 843523, -21875062, 13626197 },
+ },
+ {
+ { 2281448, -13487055, -10915418, -2609910, 1879358, 16164207, -10783882, 3953792, 13340839, 15928663 },
+ { 31727126, -7179855, -18437503, -8283652, 2875793, -16390330, -25269894, -7014826, -23452306, 5964753 },
+ { 4100420, -5959452, -17179337, 6017714, -18705837, 12227141, -26684835, 11344144, 2538215, -7570755 },
+ },
+ {
+ { -9433605, 6123113, 11159803, -2156608, 30016280, 14966241, -20474983, 1485421, -629256, -15958862 },
+ { -26804558, 4260919, 11851389, 9658551, -32017107, 16367492, -20205425, -13191288, 11659922, -11115118 },
+ { 26180396, 10015009, -30844224, -8581293, 5418197, 9480663, 2231568, -10170080, 33100372, -1306171 },
+ },
+ {
+ { 15121113, -5201871, -10389905, 15427821, -27509937, -15992507, 21670947, 4486675, -5931810, -14466380 },
+ { 16166486, -9483733, -11104130, 6023908, -31926798, -1364923, 2340060, -16254968, -10735770, -10039824 },
+ { 28042865, -3557089, -12126526, 12259706, -3717498, -6945899, 6766453, -8689599, 18036436, 5803270 },
+ },
+ },
+ {
+ {
+ { -817581, 6763912, 11803561, 1585585, 10958447, -2671165, 23855391, 4598332, -6159431, -14117438 },
+ { -31031306, -14256194, 17332029, -2383520, 31312682, -5967183, 696309, 50292, -20095739, 11763584 },
+ { -594563, -2514283, -32234153, 12643980, 12650761, 14811489, 665117, -12613632, -19773211, -10713562 },
+ },
+ {
+ { 30464590, -11262872, -4127476, -12734478, 19835327, -7105613, -24396175, 2075773, -17020157, 992471 },
+ { 18357185, -6994433, 7766382, 16342475, -29324918, 411174, 14578841, 8080033, -11574335, -10601610 },
+ { 19598397, 10334610, 12555054, 2555664, 18821899, -10339780, 21873263, 16014234, 26224780, 16452269 },
+ },
+ {
+ { -30223925, 5145196, 5944548, 16385966, 3976735, 2009897, -11377804, -7618186, -20533829, 3698650 },
+ { 14187449, 3448569, -10636236, -10810935, -22663880, -3433596, 7268410, -10890444, 27394301, 12015369 },
+ { 19695761, 16087646, 28032085, 12999827, 6817792, 11427614, 20244189, -1312777, -13259127, -3402461 },
+ },
+ {
+ { 30860103, 12735208, -1888245, -4699734, -16974906, 2256940, -8166013, 12298312, -8550524, -10393462 },
+ { -5719826, -11245325, -1910649, 15569035, 26642876, -7587760, -5789354, -15118654, -4976164, 12651793 },
+ { -2848395, 9953421, 11531313, -5282879, 26895123, -12697089, -13118820, -16517902, 9768698, -2533218 },
+ },
+ {
+ { -24719459, 1894651, -287698, -4704085, 15348719, -8156530, 32767513, 12765450, 4940095, 10678226 },
+ { 18860224, 15980149, -18987240, -1562570, -26233012, -11071856, -7843882, 13944024, -24372348, 16582019 },
+ { -15504260, 4970268, -29893044, 4175593, -20993212, -2199756, -11704054, 15444560, -11003761, 7989037 },
+ },
+ {
+ { 31490452, 5568061, -2412803, 2182383, -32336847, 4531686, -32078269, 6200206, -19686113, -14800171 },
+ { -17308668, -15879940, -31522777, -2831, -32887382, 16375549, 8680158, -16371713, 28550068, -6857132 },
+ { -28126887, -5688091, 16837845, -1820458, -6850681, 12700016, -30039981, 4364038, 1155602, 5988841 },
+ },
+ {
+ { 21890435, -13272907, -12624011, 12154349, -7831873, 15300496, 23148983, -4470481, 24618407, 8283181 },
+ { -33136107, -10512751, 9975416, 6841041, -31559793, 16356536, 3070187, -7025928, 1466169, 10740210 },
+ { -1509399, -15488185, -13503385, -10655916, 32799044, 909394, -13938903, -5779719, -32164649, -15327040 },
+ },
+ {
+ { 3960823, -14267803, -28026090, -15918051, -19404858, 13146868, 15567327, 951507, -3260321, -573935 },
+ { 24740841, 5052253, -30094131, 8961361, 25877428, 6165135, -24368180, 14397372, -7380369, -6144105 },
+ { -28888365, 3510803, -28103278, -1158478, -11238128, -10631454, -15441463, -14453128, -1625486, -6494814 },
+ },
+ },
+ {
+ {
+ { 793299, -9230478, 8836302, -6235707, -27360908, -2369593, 33152843, -4885251, -9906200, -621852 },
+ { 5666233, 525582, 20782575, -8038419, -24538499, 14657740, 16099374, 1468826, -6171428, -15186581 },
+ { -4859255, -3779343, -2917758, -6748019, 7778750, 11688288, -30404353, -9871238, -1558923, -9863646 },
+ },
+ {
+ { 10896332, -7719704, 824275, 472601, -19460308, 3009587, 25248958, 14783338, -30581476, -15757844 },
+ { 10566929, 12612572, -31944212, 11118703, -12633376, 12362879, 21752402, 8822496, 24003793, 14264025 },
+ { 27713862, -7355973, -11008240, 9227530, 27050101, 2504721, 23886875, -13117525, 13958495, -5732453 },
+ },
+ {
+ { -23481610, 4867226, -27247128, 3900521, 29838369, -8212291, -31889399, -10041781, 7340521, -15410068 },
+ { 4646514, -8011124, -22766023, -11532654, 23184553, 8566613, 31366726, -1381061, -15066784, -10375192 },
+ { -17270517, 12723032, -16993061, 14878794, 21619651, -6197576, 27584817, 3093888, -8843694, 3849921 },
+ },
+ {
+ { -9064912, 2103172, 25561640, -15125738, -5239824, 9582958, 32477045, -9017955, 5002294, -15550259 },
+ { -12057553, -11177906, 21115585, -13365155, 8808712, -12030708, 16489530, 13378448, -25845716, 12741426 },
+ { -5946367, 10645103, -30911586, 15390284, -3286982, -7118677, 24306472, 15852464, 28834118, -7646072 },
+ },
+ {
+ { -17335748, -9107057, -24531279, 9434953, -8472084, -583362, -13090771, 455841, 20461858, 5491305 },
+ { 13669248, -16095482, -12481974, -10203039, -14569770, -11893198, -24995986, 11293807, -28588204, -9421832 },
+ { 28497928, 6272777, -33022994, 14470570, 8906179, -1225630, 18504674, -14165166, 29867745, -8795943 },
+ },
+ {
+ { -16207023, 13517196, -27799630, -13697798, 24009064, -6373891, -6367600, -13175392, 22853429, -4012011 },
+ { 24191378, 16712145, -13931797, 15217831, 14542237, 1646131, 18603514, -11037887, 12876623, -2112447 },
+ { 17902668, 4518229, -411702, -2829247, 26878217, 5258055, -12860753, 608397, 16031844, 3723494 },
+ },
+ {
+ { -28632773, 12763728, -20446446, 7577504, 33001348, -13017745, 17558842, -7872890, 23896954, -4314245 },
+ { -20005381, -12011952, 31520464, 605201, 2543521, 5991821, -2945064, 7229064, -9919646, -8826859 },
+ { 28816045, 298879, -28165016, -15920938, 19000928, -1665890, -12680833, -2949325, -18051778, -2082915 },
+ },
+ {
+ { 16000882, -344896, 3493092, -11447198, -29504595, -13159789, 12577740, 16041268, -19715240, 7847707 },
+ { 10151868, 10572098, 27312476, 7922682, 14825339, 4723128, -32855931, -6519018, -10020567, 3852848 },
+ { -11430470, 15697596, -21121557, -4420647, 5386314, 15063598, 16514493, -15932110, 29330899, -15076224 },
+ },
+ },
+ {
+ {
+ { -25499735, -4378794, -15222908, -6901211, 16615731, 2051784, 3303702, 15490, -27548796, 12314391 },
+ { 15683520, -6003043, 18109120, -9980648, 15337968, -5997823, -16717435, 15921866, 16103996, -3731215 },
+ { -23169824, -10781249, 13588192, -1628807, -3798557, -1074929, -19273607, 5402699, -29815713, -9841101 },
+ },
+ {
+ { 23190676, 2384583, -32714340, 3462154, -29903655, -1529132, -11266856, 8911517, -25205859, 2739713 },
+ { 21374101, -3554250, -33524649, 9874411, 15377179, 11831242, -33529904, 6134907, 4931255, 11987849 },
+ { -7732, -2978858, -16223486, 7277597, 105524, -322051, -31480539, 13861388, -30076310, 10117930 },
+ },
+ {
+ { -29501170, -10744872, -26163768, 13051539, -25625564, 5089643, -6325503, 6704079, 12890019, 15728940 },
+ { -21972360, -11771379, -951059, -4418840, 14704840, 2695116, 903376, -10428139, 12885167, 8311031 },
+ { -17516482, 5352194, 10384213, -13811658, 7506451, 13453191, 26423267, 4384730, 1888765, -5435404 },
+ },
+ {
+ { -25817338, -3107312, -13494599, -3182506, 30896459, -13921729, -32251644, -12707869, -19464434, -3340243 },
+ { -23607977, -2665774, -526091, 4651136, 5765089, 4618330, 6092245, 14845197, 17151279, -9854116 },
+ { -24830458, -12733720, -15165978, 10367250, -29530908, -265356, 22825805, -7087279, -16866484, 16176525 },
+ },
+ {
+ { -23583256, 6564961, 20063689, 3798228, -4740178, 7359225, 2006182, -10363426, -28746253, -10197509 },
+ { -10626600, -4486402, -13320562, -5125317, 3432136, -6393229, 23632037, -1940610, 32808310, 1099883 },
+ { 15030977, 5768825, -27451236, -2887299, -6427378, -15361371, -15277896, -6809350, 2051441, -15225865 },
+ },
+ {
+ { -3362323, -7239372, 7517890, 9824992, 23555850, 295369, 5148398, -14154188, -22686354, 16633660 },
+ { 4577086, -16752288, 13249841, -15304328, 19958763, -14537274, 18559670, -10759549, 8402478, -9864273 },
+ { -28406330, -1051581, -26790155, -907698, -17212414, -11030789, 9453451, -14980072, 17983010, 9967138 },
+ },
+ {
+ { -25762494, 6524722, 26585488, 9969270, 24709298, 1220360, -1677990, 7806337, 17507396, 3651560 },
+ { -10420457, -4118111, 14584639, 15971087, -15768321, 8861010, 26556809, -5574557, -18553322, -11357135 },
+ { 2839101, 14284142, 4029895, 3472686, 14402957, 12689363, -26642121, 8459447, -5605463, -7621941 },
+ },
+ {
+ { -4839289, -3535444, 9744961, 2871048, 25113978, 3187018, -25110813, -849066, 17258084, -7977739 },
+ { 18164541, -10595176, -17154882, -1542417, 19237078, -9745295, 23357533, -15217008, 26908270, 12150756 },
+ { -30264870, -7647865, 5112249, -7036672, -1499807, -6974257, 43168, -5537701, -32302074, 16215819 },
+ },
+ },
+ {
+ {
+ { -6898905, 9824394, -12304779, -4401089, -31397141, -6276835, 32574489, 12532905, -7503072, -8675347 },
+ { -27343522, -16515468, -27151524, -10722951, 946346, 16291093, 254968, 7168080, 21676107, -1943028 },
+ { 21260961, -8424752, -16831886, -11920822, -23677961, 3968121, -3651949, -6215466, -3556191, -7913075 },
+ },
+ {
+ { 16544754, 13250366, -16804428, 15546242, -4583003, 12757258, -2462308, -8680336, -18907032, -9662799 },
+ { -2415239, -15577728, 18312303, 4964443, -15272530, -12653564, 26820651, 16690659, 25459437, -4564609 },
+ { -25144690, 11425020, 28423002, -11020557, -6144921, -15826224, 9142795, -2391602, -6432418, -1644817 },
+ },
+ {
+ { -23104652, 6253476, 16964147, -3768872, -25113972, -12296437, -27457225, -16344658, 6335692, 7249989 },
+ { -30333227, 13979675, 7503222, -12368314, -11956721, -4621693, -30272269, 2682242, 25993170, -12478523 },
+ { 4364628, 5930691, 32304656, -10044554, -8054781, 15091131, 22857016, -10598955, 31820368, 15075278 },
+ },
+ {
+ { 31879134, -8918693, 17258761, 90626, -8041836, -4917709, 24162788, -9650886, -17970238, 12833045 },
+ { 19073683, 14851414, -24403169, -11860168, 7625278, 11091125, -19619190, 2074449, -9413939, 14905377 },
+ { 24483667, -11935567, -2518866, -11547418, -1553130, 15355506, -25282080, 9253129, 27628530, -7555480 },
+ },
+ {
+ { 17597607, 8340603, 19355617, 552187, 26198470, -3176583, 4593324, -9157582, -14110875, 15297016 },
+ { 510886, 14337390, -31785257, 16638632, 6328095, 2713355, -20217417, -11864220, 8683221, 2921426 },
+ { 18606791, 11874196, 27155355, -5281482, -24031742, 6265446, -25178240, -1278924, 4674690, 13890525 },
+ },
+ {
+ { 13609624, 13069022, -27372361, -13055908, 24360586, 9592974, 14977157, 9835105, 4389687, 288396 },
+ { 9922506, -519394, 13613107, 5883594, -18758345, -434263, -12304062, 8317628, 23388070, 16052080 },
+ { 12720016, 11937594, -31970060, -5028689, 26900120, 8561328, -20155687, -11632979, -14754271, -10812892 },
+ },
+ {
+ { 15961858, 14150409, 26716931, -665832, -22794328, 13603569, 11829573, 7467844, -28822128, 929275 },
+ { 11038231, -11582396, -27310482, -7316562, -10498527, -16307831, -23479533, -9371869, -21393143, 2465074 },
+ { 20017163, -4323226, 27915242, 1529148, 12396362, 15675764, 13817261, -9658066, 2463391, -4622140 },
+ },
+ {
+ { -16358878, -12663911, -12065183, 4996454, -1256422, 1073572, 9583558, 12851107, 4003896, 12673717 },
+ { -1731589, -15155870, -3262930, 16143082, 19294135, 13385325, 14741514, -9103726, 7903886, 2348101 },
+ { 24536016, -16515207, 12715592, -3862155, 1511293, 10047386, -3842346, -7129159, -28377538, 10048127 },
+ },
+ },
+ {
+ {
+ { -12622226, -6204820, 30718825, 2591312, -10617028, 12192840, 18873298, -7297090, -32297756, 15221632 },
+ { -26478122, -11103864, 11546244, -1852483, 9180880, 7656409, -21343950, 2095755, 29769758, 6593415 },
+ { -31994208, -2907461, 4176912, 3264766, 12538965, -868111, 26312345, -6118678, 30958054, 8292160 },
+ },
+ {
+ { 31429822, -13959116, 29173532, 15632448, 12174511, -2760094, 32808831, 3977186, 26143136, -3148876 },
+ { 22648901, 1402143, -22799984, 13746059, 7936347, 365344, -8668633, -1674433, -3758243, -2304625 },
+ { -15491917, 8012313, -2514730, -12702462, -23965846, -10254029, -1612713, -1535569, -16664475, 8194478 },
+ },
+ {
+ { 27338066, -7507420, -7414224, 10140405, -19026427, -6589889, 27277191, 8855376, 28572286, 3005164 },
+ { 26287124, 4821776, 25476601, -4145903, -3764513, -15788984, -18008582, 1182479, -26094821, -13079595 },
+ { -7171154, 3178080, 23970071, 6201893, -17195577, -4489192, -21876275, -13982627, 32208683, -1198248 },
+ },
+ {
+ { -16657702, 2817643, -10286362, 14811298, 6024667, 13349505, -27315504, -10497842, -27672585, -11539858 },
+ { 15941029, -9405932, -21367050, 8062055, 31876073, -238629, -15278393, -1444429, 15397331, -4130193 },
+ { 8934485, -13485467, -23286397, -13423241, -32446090, 14047986, 31170398, -1441021, -27505566, 15087184 },
+ },
+ {
+ { -18357243, -2156491, 24524913, -16677868, 15520427, -6360776, -15502406, 11461896, 16788528, -5868942 },
+ { -1947386, 16013773, 21750665, 3714552, -17401782, -16055433, -3770287, -10323320, 31322514, -11615635 },
+ { 21426655, -5650218, -13648287, -5347537, -28812189, -4920970, -18275391, -14621414, 13040862, -12112948 },
+ },
+ {
+ { 11293895, 12478086, -27136401, 15083750, -29307421, 14748872, 14555558, -13417103, 1613711, 4896935 },
+ { -25894883, 15323294, -8489791, -8057900, 25967126, -13425460, 2825960, -4897045, -23971776, -11267415 },
+ { -15924766, -5229880, -17443532, 6410664, 3622847, 10243618, 20615400, 12405433, -23753030, -8436416 },
+ },
+ {
+ { -7091295, 12556208, -20191352, 9025187, -17072479, 4333801, 4378436, 2432030, 23097949, -566018 },
+ { 4565804, -16025654, 20084412, -7842817, 1724999, 189254, 24767264, 10103221, -18512313, 2424778 },
+ { 366633, -11976806, 8173090, -6890119, 30788634, 5745705, -7168678, 1344109, -3642553, 12412659 },
+ },
+ {
+ { -24001791, 7690286, 14929416, -168257, -32210835, -13412986, 24162697, -15326504, -3141501, 11179385 },
+ { 18289522, -14724954, 8056945, 16430056, -21729724, 7842514, -6001441, -1486897, -18684645, -11443503 },
+ { 476239, 6601091, -6152790, -9723375, 17503545, -4863900, 27672959, 13403813, 11052904, 5219329 },
+ },
+ },
+ {
+ {
+ { 20678546, -8375738, -32671898, 8849123, -5009758, 14574752, 31186971, -3973730, 9014762, -8579056 },
+ { -13644050, -10350239, -15962508, 5075808, -1514661, -11534600, -33102500, 9160280, 8473550, -3256838 },
+ { 24900749, 14435722, 17209120, -15292541, -22592275, 9878983, -7689309, -16335821, -24568481, 11788948 },
+ },
+ {
+ { -3118155, -11395194, -13802089, 14797441, 9652448, -6845904, -20037437, 10410733, -24568470, -1458691 },
+ { -15659161, 16736706, -22467150, 10215878, -9097177, 7563911, 11871841, -12505194, -18513325, 8464118 },
+ { -23400612, 8348507, -14585951, -861714, -3950205, -6373419, 14325289, 8628612, 33313881, -8370517 },
+ },
+ {
+ { -20186973, -4967935, 22367356, 5271547, -1097117, -4788838, -24805667, -10236854, -8940735, -5818269 },
+ { -6948785, -1795212, -32625683, -16021179, 32635414, -7374245, 15989197, -12838188, 28358192, -4253904 },
+ { -23561781, -2799059, -32351682, -1661963, -9147719, 10429267, -16637684, 4072016, -5351664, 5596589 },
+ },
+ {
+ { -28236598, -3390048, 12312896, 6213178, 3117142, 16078565, 29266239, 2557221, 1768301, 15373193 },
+ { -7243358, -3246960, -4593467, -7553353, -127927, -912245, -1090902, -4504991, -24660491, 3442910 },
+ { -30210571, 5124043, 14181784, 8197961, 18964734, -11939093, 22597931, 7176455, -18585478, 13365930 },
+ },
+ {
+ { -7877390, -1499958, 8324673, 4690079, 6261860, 890446, 24538107, -8570186, -9689599, -3031667 },
+ { 25008904, -10771599, -4305031, -9638010, 16265036, 15721635, 683793, -11823784, 15723479, -15163481 },
+ { -9660625, 12374379, -27006999, -7026148, -7724114, -12314514, 11879682, 5400171, 519526, -1235876 },
+ },
+ {
+ { 22258397, -16332233, -7869817, 14613016, -22520255, -2950923, -20353881, 7315967, 16648397, 7605640 },
+ { -8081308, -8464597, -8223311, 9719710, 19259459, -15348212, 23994942, -5281555, -9468848, 4763278 },
+ { -21699244, 9220969, -15730624, 1084137, -25476107, -2852390, 31088447, -7764523, -11356529, 728112 },
+ },
+ {
+ { 26047220, -11751471, -6900323, -16521798, 24092068, 9158119, -4273545, -12555558, -29365436, -5498272 },
+ { 17510331, -322857, 5854289, 8403524, 17133918, -3112612, -28111007, 12327945, 10750447, 10014012 },
+ { -10312768, 3936952, 9156313, -8897683, 16498692, -994647, -27481051, -666732, 3424691, 7540221 },
+ },
+ {
+ { 30322361, -6964110, 11361005, -4143317, 7433304, 4989748, -7071422, -16317219, -9244265, 15258046 },
+ { 13054562, -2779497, 19155474, 469045, -12482797, 4566042, 5631406, 2711395, 1062915, -5136345 },
+ { -19240248, -11254599, -29509029, -7499965, -5835763, 13005411, -6066489, 12194497, 32960380, 1459310 },
+ },
+ },
+ {
+ {
+ { 19852034, 7027924, 23669353, 10020366, 8586503, -6657907, 394197, -6101885, 18638003, -11174937 },
+ { 31395534, 15098109, 26581030, 8030562, -16527914, -5007134, 9012486, -7584354, -6643087, -5442636 },
+ { -9192165, -2347377, -1997099, 4529534, 25766844, 607986, -13222, 9677543, -32294889, -6456008 },
+ },
+ {
+ { -2444496, -149937, 29348902, 8186665, 1873760, 12489863, -30934579, -7839692, -7852844, -8138429 },
+ { -15236356, -15433509, 7766470, 746860, 26346930, -10221762, -27333451, 10754588, -9431476, 5203576 },
+ { 31834314, 14135496, -770007, 5159118, 20917671, -16768096, -7467973, -7337524, 31809243, 7347066 },
+ },
+ {
+ { -9606723, -11874240, 20414459, 13033986, 13716524, -11691881, 19797970, -12211255, 15192876, -2087490 },
+ { -12663563, -2181719, 1168162, -3804809, 26747877, -14138091, 10609330, 12694420, 33473243, -13382104 },
+ { 33184999, 11180355, 15832085, -11385430, -1633671, 225884, 15089336, -11023903, -6135662, 14480053 },
+ },
+ {
+ { 31308717, -5619998, 31030840, -1897099, 15674547, -6582883, 5496208, 13685227, 27595050, 8737275 },
+ { -20318852, -15150239, 10933843, -16178022, 8335352, -7546022, -31008351, -12610604, 26498114, 66511 },
+ { 22644454, -8761729, -16671776, 4884562, -3105614, -13559366, 30540766, -4286747, -13327787, -7515095 },
+ },
+ {
+ { -28017847, 9834845, 18617207, -2681312, -3401956, -13307506, 8205540, 13585437, -17127465, 15115439 },
+ { 23711543, -672915, 31206561, -8362711, 6164647, -9709987, -33535882, -1426096, 8236921, 16492939 },
+ { -23910559, -13515526, -26299483, -4503841, 25005590, -7687270, 19574902, 10071562, 6708380, -6222424 },
+ },
+ {
+ { 2101391, -4930054, 19702731, 2367575, -15427167, 1047675, 5301017, 9328700, 29955601, -11678310 },
+ { 3096359, 9271816, -21620864, -15521844, -14847996, -7592937, -25892142, -12635595, -9917575, 6216608 },
+ { -32615849, 338663, -25195611, 2510422, -29213566, -13820213, 24822830, -6146567, -26767480, 7525079 },
+ },
+ {
+ { -23066649, -13985623, 16133487, -7896178, -3389565, 778788, -910336, -2782495, -19386633, 11994101 },
+ { 21691500, -13624626, -641331, -14367021, 3285881, -3483596, -25064666, 9718258, -7477437, 13381418 },
+ { 18445390, -4202236, 14979846, 11622458, -1727110, -3582980, 23111648, -6375247, 28535282, 15779576 },
+ },
+ {
+ { 30098053, 3089662, -9234387, 16662135, -21306940, 11308411, -14068454, 12021730, 9955285, -16303356 },
+ { 9734894, -14576830, -7473633, -9138735, 2060392, 11313496, -18426029, 9924399, 20194861, 13380996 },
+ { -26378102, -7965207, -22167821, 15789297, -18055342, -6168792, -1984914, 15707771, 26342023, 10146099 },
+ },
+ },
+ {
+ {
+ { -26016874, -219943, 21339191, -41388, 19745256, -2878700, -29637280, 2227040, 21612326, -545728 },
+ { -13077387, 1184228, 23562814, -5970442, -20351244, -6348714, 25764461, 12243797, -20856566, 11649658 },
+ { -10031494, 11262626, 27384172, 2271902, 26947504, -15997771, 39944, 6114064, 33514190, 2333242 },
+ },
+ {
+ { -21433588, -12421821, 8119782, 7219913, -21830522, -9016134, -6679750, -12670638, 24350578, -13450001 },
+ { -4116307, -11271533, -23886186, 4843615, -30088339, 690623, -31536088, -10406836, 8317860, 12352766 },
+ { 18200138, -14475911, -33087759, -2696619, -23702521, -9102511, -23552096, -2287550, 20712163, 6719373 },
+ },
+ {
+ { 26656208, 6075253, -7858556, 1886072, -28344043, 4262326, 11117530, -3763210, 26224235, -3297458 },
+ { -17168938, -14854097, -3395676, -16369877, -19954045, 14050420, 21728352, 9493610, 18620611, -16428628 },
+ { -13323321, 13325349, 11432106, 5964811, 18609221, 6062965, -5269471, -9725556, -30701573, -16479657 },
+ },
+ {
+ { -23860538, -11233159, 26961357, 1640861, -32413112, -16737940, 12248509, -5240639, 13735342, 1934062 },
+ { 25089769, 6742589, 17081145, -13406266, 21909293, -16067981, -15136294, -3765346, -21277997, 5473616 },
+ { 31883677, -7961101, 1083432, -11572403, 22828471, 13290673, -7125085, 12469656, 29111212, -5451014 },
+ },
+ {
+ { 24244947, -15050407, -26262976, 2791540, -14997599, 16666678, 24367466, 6388839, -10295587, 452383 },
+ { -25640782, -3417841, 5217916, 16224624, 19987036, -4082269, -24236251, -5915248, 15766062, 8407814 },
+ { -20406999, 13990231, 15495425, 16395525, 5377168, 15166495, -8917023, -4388953, -8067909, 2276718 },
+ },
+ {
+ { 30157918, 12924066, -17712050, 9245753, 19895028, 3368142, -23827587, 5096219, 22740376, -7303417 },
+ { 2041139, -14256350, 7783687, 13876377, -25946985, -13352459, 24051124, 13742383, -15637599, 13295222 },
+ { 33338237, -8505733, 12532113, 7977527, 9106186, -1715251, -17720195, -4612972, -4451357, -14669444 },
+ },
+ {
+ { -20045281, 5454097, -14346548, 6447146, 28862071, 1883651, -2469266, -4141880, 7770569, 9620597 },
+ { 23208068, 7979712, 33071466, 8149229, 1758231, -10834995, 30945528, -1694323, -33502340, -14767970 },
+ { 1439958, -16270480, -1079989, -793782, 4625402, 10647766, -5043801, 1220118, 30494170, -11440799 },
+ },
+ {
+ { -5037580, -13028295, -2970559, -3061767, 15640974, -6701666, -26739026, 926050, -1684339, -13333647 },
+ { 13908495, -3549272, 30919928, -6273825, -21521863, 7989039, 9021034, 9078865, 3353509, 4033511 },
+ { -29663431, -15113610, 32259991, -344482, 24295849, -12912123, 23161163, 8839127, 27485041, 7356032 },
+ },
+ },
+ {
+ {
+ { 9661027, 705443, 11980065, -5370154, -1628543, 14661173, -6346142, 2625015, 28431036, -16771834 },
+ { -23839233, -8311415, -25945511, 7480958, -17681669, -8354183, -22545972, 14150565, 15970762, 4099461 },
+ { 29262576, 16756590, 26350592, -8793563, 8529671, -11208050, 13617293, -9937143, 11465739, 8317062 },
+ },
+ {
+ { -25493081, -6962928, 32500200, -9419051, -23038724, -2302222, 14898637, 3848455, 20969334, -5157516 },
+ { -20384450, -14347713, -18336405, 13884722, -33039454, 2842114, -21610826, -3649888, 11177095, 14989547 },
+ { -24496721, -11716016, 16959896, 2278463, 12066309, 10137771, 13515641, 2581286, -28487508, 9930240 },
+ },
+ {
+ { -17751622, -2097826, 16544300, -13009300, -15914807, -14949081, 18345767, -13403753, 16291481, -5314038 },
+ { -33229194, 2553288, 32678213, 9875984, 8534129, 6889387, -9676774, 6957617, 4368891, 9788741 },
+ { 16660756, 7281060, -10830758, 12911820, 20108584, -8101676, -21722536, -8613148, 16250552, -11111103 },
+ },
+ {
+ { -19765507, 2390526, -16551031, 14161980, 1905286, 6414907, 4689584, 10604807, -30190403, 4782747 },
+ { -1354539, 14736941, -7367442, -13292886, 7710542, -14155590, -9981571, 4383045, 22546403, 437323 },
+ { 31665577, -12180464, -16186830, 1491339, -18368625, 3294682, 27343084, 2786261, -30633590, -14097016 },
+ },
+ {
+ { -14467279, -683715, -33374107, 7448552, 19294360, 14334329, -19690631, 2355319, -19284671, -6114373 },
+ { 15121312, -15796162, 6377020, -6031361, -10798111, -12957845, 18952177, 15496498, -29380133, 11754228 },
+ { -2637277, -13483075, 8488727, -14303896, 12728761, -1622493, 7141596, 11724556, 22761615, -10134141 },
+ },
+ {
+ { 16918416, 11729663, -18083579, 3022987, -31015732, -13339659, -28741185, -12227393, 32851222, 11717399 },
+ { 11166634, 7338049, -6722523, 4531520, -29468672, -7302055, 31474879, 3483633, -1193175, -4030831 },
+ { -185635, 9921305, 31456609, -13536438, -12013818, 13348923, 33142652, 6546660, -19985279, -3948376 },
+ },
+ {
+ { -32460596, 11266712, -11197107, -7899103, 31703694, 3855903, -8537131, -12833048, -30772034, -15486313 },
+ { -18006477, 12709068, 3991746, -6479188, -21491523, -10550425, -31135347, -16049879, 10928917, 3011958 },
+ { -6957757, -15594337, 31696059, 334240, 29576716, 14796075, -30831056, -12805180, 18008031, 10258577 },
+ },
+ {
+ { -22448644, 15655569, 7018479, -4410003, -30314266, -1201591, -1853465, 1367120, 25127874, 6671743 },
+ { 29701166, -14373934, -10878120, 9279288, -17568, 13127210, 21382910, 11042292, 25838796, 4642684 },
+ { -20430234, 14955537, -24126347, 8124619, -5369288, -5990470, 30468147, -13900640, 18423289, 4177476 },
+ },
+ },
+ } ;
+
+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<uint8_t>(y);
+ }
+
+inline int32_t equal32(int8_t b, int8_t c)
+ {
+ return -static_cast<int32_t>(equal(b, c));
+ }
+
+inline uint8_t negative(int8_t b)
+ {
+ uint64_t x = b; /* 18446744073709551361..18446744073709551615: yes; 0..255: no */
+ x >>= 63; /* 1: yes; 0: no */
+ return static_cast<uint8_t>(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<int>(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 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 recip;
+ fe x;
+ fe 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;
+ }
+
+}
+
+/*
+h = a * B
+where a = a[0]+256*a[1]+...+256^31 a[31]
+B is the Ed25519 base point (x,4/5) with x positive.
+
+Preconditions:
+ a[31] <= 127
+*/
+
+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 */
+ /* e[63] is between 0 and 7 */
+
+ carry = 0;
+ for(i = 0; i < 63; ++i)
+ {
+ e[i] += carry;
+ carry = e[i] + 8;
+ carry >>= 4;
+ e[i] -= carry << 4;
+ }
+ 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);
+ }
+
+ 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);
+ }
+
+ ge_p3_tobytes(out, &h);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/pubkey/ed25519/info.txt b/comm/third_party/botan/src/lib/pubkey/ed25519/info.txt
new file mode 100644
index 0000000000..f17b86cbf8
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/ed25519/info.txt
@@ -0,0 +1,17 @@
+<defines>
+ED25519 -> 20170607
+</defines>
+
+<requires>
+sha2_64
+</requires>
+
+<header:public>
+ed25519.h
+</header:public>
+
+<header:internal>
+ed25519_fe.h
+ed25519_internal.h
+</header:internal>
+
diff --git a/comm/third_party/botan/src/lib/pubkey/ed25519/sc_muladd.cpp b/comm/third_party/botan/src/lib/pubkey/ed25519/sc_muladd.cpp
new file mode 100644
index 0000000000..4a88be22de
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/ed25519/sc_muladd.cpp
@@ -0,0 +1,221 @@
+/*
+* 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
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/ed25519_internal.h>
+
+namespace Botan {
+
+/*
+Input:
+ a[0]+256*a[1]+...+256^31*a[31] = a
+ b[0]+256*b[1]+...+256^31*b[31] = b
+ c[0]+256*c[1]+...+256^31*c[31] = c
+
+Output:
+ s[0]+256*s[1]+...+256^31*s[31] = (ab+c) mod l
+ where l = 2^252 + 27742317777372353535851937790883648493.
+*/
+
+void sc_muladd(uint8_t* s, const uint8_t* a, const uint8_t* b, const uint8_t* c)
+ {
+ const int32_t MASK = 0x1fffff;
+
+ const int64_t a0 = MASK & load_3(a);
+ const int64_t a1 = MASK & (load_4(a + 2) >> 5);
+ const int64_t a2 = MASK & (load_3(a + 5) >> 2);
+ const int64_t a3 = MASK & (load_4(a + 7) >> 7);
+ const int64_t a4 = MASK & (load_4(a + 10) >> 4);
+ const int64_t a5 = MASK & (load_3(a + 13) >> 1);
+ const int64_t a6 = MASK & (load_4(a + 15) >> 6);
+ const int64_t a7 = MASK & (load_3(a + 18) >> 3);
+ const int64_t a8 = MASK & load_3(a + 21);
+ const int64_t a9 = MASK & (load_4(a + 23) >> 5);
+ const int64_t a10 = MASK & (load_3(a + 26) >> 2);
+ const int64_t a11 = (load_4(a + 28) >> 7);
+ const int64_t b0 = MASK & load_3(b);
+ const int64_t b1 = MASK & (load_4(b + 2) >> 5);
+ const int64_t b2 = MASK & (load_3(b + 5) >> 2);
+ const int64_t b3 = MASK & (load_4(b + 7) >> 7);
+ const int64_t b4 = MASK & (load_4(b + 10) >> 4);
+ const int64_t b5 = MASK & (load_3(b + 13) >> 1);
+ const int64_t b6 = MASK & (load_4(b + 15) >> 6);
+ const int64_t b7 = MASK & (load_3(b + 18) >> 3);
+ const int64_t b8 = MASK & load_3(b + 21);
+ const int64_t b9 = MASK & (load_4(b + 23) >> 5);
+ const int64_t b10 = MASK & (load_3(b + 26) >> 2);
+ const int64_t b11 = (load_4(b + 28) >> 7);
+ const int64_t c0 = MASK & load_3(c);
+ const int64_t c1 = MASK & (load_4(c + 2) >> 5);
+ const int64_t c2 = MASK & (load_3(c + 5) >> 2);
+ const int64_t c3 = MASK & (load_4(c + 7) >> 7);
+ const int64_t c4 = MASK & (load_4(c + 10) >> 4);
+ const int64_t c5 = MASK & (load_3(c + 13) >> 1);
+ const int64_t c6 = MASK & (load_4(c + 15) >> 6);
+ const int64_t c7 = MASK & (load_3(c + 18) >> 3);
+ const int64_t c8 = MASK & load_3(c + 21);
+ const int64_t c9 = MASK & (load_4(c + 23) >> 5);
+ const int64_t c10 = MASK & (load_3(c + 26) >> 2);
+ const int64_t c11 = (load_4(c + 28) >> 7);
+
+ int64_t s0 = c0 + a0*b0;
+ int64_t s1 = c1 + a0*b1 + a1*b0;
+ int64_t s2 = c2 + a0*b2 + a1*b1 + a2*b0;
+ int64_t s3 = c3 + a0*b3 + a1*b2 + a2*b1 + a3*b0;
+ int64_t s4 = c4 + a0*b4 + a1*b3 + a2*b2 + a3*b1 + a4*b0;
+ int64_t s5 = c5 + a0*b5 + a1*b4 + a2*b3 + a3*b2 + a4*b1 + a5*b0;
+ int64_t s6 = c6 + a0*b6 + a1*b5 + a2*b4 + a3*b3 + a4*b2 + a5*b1 + a6*b0;
+ int64_t s7 = c7 + a0*b7 + a1*b6 + a2*b5 + a3*b4 + a4*b3 + a5*b2 + a6*b1 + a7*b0;
+ int64_t s8 = c8 + a0*b8 + a1*b7 + a2*b6 + a3*b5 + a4*b4 + a5*b3 + a6*b2 + a7*b1 + a8*b0;
+ int64_t s9 = c9 + a0*b9 + a1*b8 + a2*b7 + a3*b6 + a4*b5 + a5*b4 + a6*b3 + a7*b2 + a8*b1 + a9*b0;
+ int64_t s10 = c10 + a0*b10 + a1*b9 + a2*b8 + a3*b7 + a4*b6 + a5*b5 + a6*b4 + a7*b3 + a8*b2 + a9*b1 + a10*b0;
+ int64_t s11 = c11 + a0*b11 + a1*b10 + a2*b9 + a3*b8 + a4*b7 + a5*b6 + a6*b5 + a7*b4 + a8*b3 + a9*b2 + a10*b1 + a11*b0;
+ int64_t s12 = a1*b11 + a2*b10 + a3*b9 + a4*b8 + a5*b7 + a6*b6 + a7*b5 + a8*b4 + a9*b3 + a10*b2 + a11*b1;
+ int64_t s13 = a2*b11 + a3*b10 + a4*b9 + a5*b8 + a6*b7 + a7*b6 + a8*b5 + a9*b4 + a10*b3 + a11*b2;
+ int64_t s14 = a3*b11 + a4*b10 + a5*b9 + a6*b8 + a7*b7 + a8*b6 + a9*b5 + a10*b4 + a11*b3;
+ int64_t s15 = a4*b11 + a5*b10 + a6*b9 + a7*b8 + a8*b7 + a9*b6 + a10*b5 + a11*b4;
+ int64_t s16 = a5*b11 + a6*b10 + a7*b9 + a8*b8 + a9*b7 + a10*b6 + a11*b5;
+ int64_t s17 = a6*b11 + a7*b10 + a8*b9 + a9*b8 + a10*b7 + a11*b6;
+ int64_t s18 = a7*b11 + a8*b10 + a9*b9 + a10*b8 + a11*b7;
+ int64_t s19 = a8*b11 + a9*b10 + a10*b9 + a11*b8;
+ int64_t s20 = a9*b11 + a10*b10 + a11*b9;
+ int64_t s21 = a10*b11 + a11*b10;
+ int64_t s22 = a11*b11;
+ int64_t s23 = 0;
+
+ carry<21>(s0, s1);
+ carry<21>(s2, s3);
+ carry<21>(s4, s5);
+ carry<21>(s6, s7);
+ carry<21>(s8, s9);
+ carry<21>(s10, s11);
+ carry<21>(s12, s13);
+ carry<21>(s14, s15);
+ carry<21>(s16, s17);
+ carry<21>(s18, s19);
+ carry<21>(s20, s21);
+ carry<21>(s22, s23);
+
+ carry<21>(s1, s2);
+ carry<21>(s3, s4);
+ carry<21>(s5, s6);
+ carry<21>(s7, s8);
+ carry<21>(s9, s10);
+ carry<21>(s11, s12);
+ carry<21>(s13, s14);
+ carry<21>(s15, s16);
+ carry<21>(s17, s18);
+ carry<21>(s19, s20);
+ carry<21>(s21, s22);
+
+ redc_mul(s11, s12, s13, s14, s15, s16, s23);
+ redc_mul(s10, s11, s12, s13, s14, s15, s22);
+ redc_mul( s9, s10, s11, s12, s13, s14, s21);
+ redc_mul( s8, s9, s10, s11, s12, s13, s20);
+ redc_mul( s7, s8, s9, s10, s11, s12, s19);
+ redc_mul( s6, s7, s8, s9, s10, s11, s18);
+
+ carry<21>(s6, s7);
+ carry<21>(s8, s9);
+ carry<21>(s10, s11);
+ carry<21>(s12, s13);
+ carry<21>(s14, s15);
+ carry<21>(s16, s17);
+
+ carry<21>(s7, s8);
+ carry<21>(s9, s10);
+ carry<21>(s11, s12);
+ carry<21>(s13, s14);
+ carry<21>(s15, s16);
+
+ redc_mul(s5, s6, s7, s8, s9, s10, s17);
+ redc_mul(s4, s5, s6, s7, s8, s9, s16);
+ redc_mul(s3, s4, s5, s6, s7, s8, s15);
+ redc_mul(s2, s3, s4, s5, s6, s7, s14);
+ redc_mul(s1, s2, s3, s4, s5, s6, s13);
+ redc_mul(s0, s1, s2, s3, s4, s5, s12);
+
+ carry<21>(s0, s1);
+ carry<21>(s2, s3);
+ carry<21>(s4, s5);
+ carry<21>(s6, s7);
+ carry<21>(s8, s9);
+ carry<21>(s10, s11);
+
+ carry<21>(s1, s2);
+ carry<21>(s3, s4);
+ carry<21>(s5, s6);
+ carry<21>(s7, s8);
+ carry<21>(s9, s10);
+ carry<21>(s11, s12);
+
+ redc_mul(s0, s1, s2, s3, s4, s5, s12);
+
+ carry0<21>(s0, s1);
+ carry0<21>(s1, s2);
+ carry0<21>(s2, s3);
+ carry0<21>(s3, s4);
+ carry0<21>(s4, s5);
+ carry0<21>(s5, s6);
+ carry0<21>(s6, s7);
+ carry0<21>(s7, s8);
+ carry0<21>(s8, s9);
+ carry0<21>(s9, s10);
+ carry0<21>(s10, s11);
+ carry0<21>(s11, s12);
+
+ redc_mul(s0, s1, s2, s3, s4, s5, s12);
+
+ carry0<21>(s0, s1);
+ carry0<21>(s1, s2);
+ carry0<21>(s2, s3);
+ carry0<21>(s3, s4);
+ carry0<21>(s4, s5);
+ carry0<21>(s5, s6);
+ carry0<21>(s6, s7);
+ carry0<21>(s7, s8);
+ carry0<21>(s8, s9);
+ carry0<21>(s9, s10);
+ carry0<21>(s10, s11);
+
+ s[0] = static_cast<uint8_t>(s0 >> 0);
+ s[1] = static_cast<uint8_t>(s0 >> 8);
+ s[2] = static_cast<uint8_t>((s0 >> 16) | (s1 << 5));
+ s[3] = static_cast<uint8_t>(s1 >> 3);
+ s[4] = static_cast<uint8_t>(s1 >> 11);
+ s[5] = static_cast<uint8_t>((s1 >> 19) | (s2 << 2));
+ s[6] = static_cast<uint8_t>(s2 >> 6);
+ s[7] = static_cast<uint8_t>((s2 >> 14) | (s3 << 7));
+ s[8] = static_cast<uint8_t>(s3 >> 1);
+ s[9] = static_cast<uint8_t>(s3 >> 9);
+ s[10] = static_cast<uint8_t>((s3 >> 17) | (s4 << 4));
+ s[11] = static_cast<uint8_t>(s4 >> 4);
+ s[12] = static_cast<uint8_t>(s4 >> 12);
+ s[13] = static_cast<uint8_t>((s4 >> 20) | (s5 << 1));
+ s[14] = static_cast<uint8_t>(s5 >> 7);
+ s[15] = static_cast<uint8_t>((s5 >> 15) | (s6 << 6));
+ s[16] = static_cast<uint8_t>(s6 >> 2);
+ s[17] = static_cast<uint8_t>(s6 >> 10);
+ s[18] = static_cast<uint8_t>((s6 >> 18) | (s7 << 3));
+ s[19] = static_cast<uint8_t>(s7 >> 5);
+ s[20] = static_cast<uint8_t>(s7 >> 13);
+ s[21] = static_cast<uint8_t>(s8 >> 0);
+ s[22] = static_cast<uint8_t>(s8 >> 8);
+ s[23] = static_cast<uint8_t>((s8 >> 16) | (s9 << 5));
+ s[24] = static_cast<uint8_t>(s9 >> 3);
+ s[25] = static_cast<uint8_t>(s9 >> 11);
+ s[26] = static_cast<uint8_t>((s9 >> 19) | (s10 << 2));
+ s[27] = static_cast<uint8_t>(s10 >> 6);
+ s[28] = static_cast<uint8_t>((s10 >> 14) | (s11 << 7));
+ s[29] = static_cast<uint8_t>(s11 >> 1);
+ s[30] = static_cast<uint8_t>(s11 >> 9);
+ s[31] = static_cast<uint8_t>(s11 >> 17);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/pubkey/ed25519/sc_reduce.cpp b/comm/third_party/botan/src/lib/pubkey/ed25519/sc_reduce.cpp
new file mode 100644
index 0000000000..c69dd5eccd
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/ed25519/sc_reduce.cpp
@@ -0,0 +1,159 @@
+/*
+* 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
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/ed25519_internal.h>
+
+namespace Botan {
+
+/*
+Input:
+ s[0]+256*s[1]+...+256^63*s[63] = s
+
+Output:
+ s[0]+256*s[1]+...+256^31*s[31] = s mod l
+ where l = 2^252 + 27742317777372353535851937790883648493.
+ Overwrites s in place.
+*/
+
+void sc_reduce(uint8_t* s)
+ {
+ const uint32_t MASK = 0x1fffff;
+
+ int64_t s0 = MASK & load_3(s);
+ int64_t s1 = MASK & (load_4(s + 2) >> 5);
+ int64_t s2 = MASK & (load_3(s + 5) >> 2);
+ int64_t s3 = MASK & (load_4(s + 7) >> 7);
+ int64_t s4 = MASK & (load_4(s + 10) >> 4);
+ int64_t s5 = MASK & (load_3(s + 13) >> 1);
+ int64_t s6 = MASK & (load_4(s + 15) >> 6);
+ int64_t s7 = MASK & (load_3(s + 18) >> 3);
+ int64_t s8 = MASK & load_3(s + 21);
+ int64_t s9 = MASK & (load_4(s + 23) >> 5);
+ int64_t s10 = MASK & (load_3(s + 26) >> 2);
+ int64_t s11 = MASK & (load_4(s + 28) >> 7);
+ int64_t s12 = MASK & (load_4(s + 31) >> 4);
+ int64_t s13 = MASK & (load_3(s + 34) >> 1);
+ int64_t s14 = MASK & (load_4(s + 36) >> 6);
+ int64_t s15 = MASK & (load_3(s + 39) >> 3);
+ int64_t s16 = MASK & load_3(s + 42);
+ int64_t s17 = MASK & (load_4(s + 44) >> 5);
+ int64_t s18 = MASK & (load_3(s + 47) >> 2);
+ int64_t s19 = MASK & (load_4(s + 49) >> 7);
+ int64_t s20 = MASK & (load_4(s + 52) >> 4);
+ int64_t s21 = MASK & (load_3(s + 55) >> 1);
+ int64_t s22 = MASK & (load_4(s + 57) >> 6);
+ int64_t s23 = (load_4(s + 60) >> 3);
+
+ redc_mul(s11, s12, s13, s14, s15, s16, s23);
+ redc_mul(s10, s11, s12, s13, s14, s15, s22);
+ redc_mul( s9, s10, s11, s12, s13, s14, s21);
+ redc_mul( s8, s9, s10, s11, s12, s13, s20);
+ redc_mul( s7, s8, s9, s10, s11, s12, s19);
+ redc_mul( s6, s7, s8, s9, s10, s11, s18);
+
+ carry<21>(s6, s7);
+ carry<21>(s8, s9);
+ carry<21>(s10, s11);
+ carry<21>(s12, s13);
+ carry<21>(s14, s15);
+ carry<21>(s16, s17);
+
+ carry<21>(s7, s8);
+ carry<21>(s9, s10);
+ carry<21>(s11, s12);
+ carry<21>(s13, s14);
+ carry<21>(s15, s16);
+
+ redc_mul(s5, s6, s7, s8, s9, s10, s17);
+ redc_mul(s4, s5, s6, s7, s8, s9, s16);
+ redc_mul(s3, s4, s5, s6, s7, s8, s15);
+ redc_mul(s2, s3, s4, s5, s6, s7, s14);
+ redc_mul(s1, s2, s3, s4, s5, s6, s13);
+ redc_mul(s0, s1, s2, s3, s4, s5, s12);
+
+ carry<21>(s0, s1);
+ carry<21>(s2, s3);
+ carry<21>(s4, s5);
+ carry<21>(s6, s7);
+ carry<21>(s8, s9);
+ carry<21>(s10, s11);
+
+ carry<21>(s1, s2);
+ carry<21>(s3, s4);
+ carry<21>(s5, s6);
+ carry<21>(s7, s8);
+ carry<21>(s9, s10);
+ carry<21>(s11, s12);
+
+ redc_mul(s0, s1, s2, s3, s4, s5, s12);
+
+ carry0<21>(s0, s1);
+ carry0<21>(s1, s2);
+ carry0<21>(s2, s3);
+ carry0<21>(s3, s4);
+ carry0<21>(s4, s5);
+ carry0<21>(s5, s6);
+ carry0<21>(s6, s7);
+ carry0<21>(s7, s8);
+ carry0<21>(s8, s9);
+ carry0<21>(s9, s10);
+ carry0<21>(s10, s11);
+ carry0<21>(s11, s12);
+
+ redc_mul(s0, s1, s2, s3, s4, s5, s12);
+
+ carry0<21>(s0, s1);
+ carry0<21>(s1, s2);
+ carry0<21>(s2, s3);
+ carry0<21>(s3, s4);
+ carry0<21>(s4, s5);
+ carry0<21>(s5, s6);
+ carry0<21>(s6, s7);
+ carry0<21>(s7, s8);
+ carry0<21>(s8, s9);
+ carry0<21>(s9, s10);
+ carry0<21>(s10, s11);
+ carry0<21>(s11, s12);
+
+ s[0] = static_cast<uint8_t>(s0 >> 0);
+ s[1] = static_cast<uint8_t>(s0 >> 8);
+ s[2] = static_cast<uint8_t>((s0 >> 16) | (s1 << 5));
+ s[3] = static_cast<uint8_t>(s1 >> 3);
+ s[4] = static_cast<uint8_t>(s1 >> 11);
+ s[5] = static_cast<uint8_t>((s1 >> 19) | (s2 << 2));
+ s[6] = static_cast<uint8_t>(s2 >> 6);
+ s[7] = static_cast<uint8_t>((s2 >> 14) | (s3 << 7));
+ s[8] = static_cast<uint8_t>(s3 >> 1);
+ s[9] = static_cast<uint8_t>(s3 >> 9);
+ s[10] = static_cast<uint8_t>((s3 >> 17) | (s4 << 4));
+ s[11] = static_cast<uint8_t>(s4 >> 4);
+ s[12] = static_cast<uint8_t>(s4 >> 12);
+ s[13] = static_cast<uint8_t>((s4 >> 20) | (s5 << 1));
+ s[14] = static_cast<uint8_t>(s5 >> 7);
+ s[15] = static_cast<uint8_t>((s5 >> 15) | (s6 << 6));
+ s[16] = static_cast<uint8_t>(s6 >> 2);
+ s[17] = static_cast<uint8_t>(s6 >> 10);
+ s[18] = static_cast<uint8_t>((s6 >> 18) | (s7 << 3));
+ s[19] = static_cast<uint8_t>(s7 >> 5);
+ s[20] = static_cast<uint8_t>(s7 >> 13);
+ s[21] = static_cast<uint8_t>(s8 >> 0);
+ s[22] = static_cast<uint8_t>(s8 >> 8);
+ s[23] = static_cast<uint8_t>((s8 >> 16) | (s9 << 5));
+ s[24] = static_cast<uint8_t>(s9 >> 3);
+ s[25] = static_cast<uint8_t>(s9 >> 11);
+ s[26] = static_cast<uint8_t>((s9 >> 19) | (s10 << 2));
+ s[27] = static_cast<uint8_t>(s10 >> 6);
+ s[28] = static_cast<uint8_t>((s10 >> 14) | (s11 << 7));
+ s[29] = static_cast<uint8_t>(s11 >> 1);
+ s[30] = static_cast<uint8_t>(s11 >> 9);
+ s[31] = static_cast<uint8_t>(s11 >> 17);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/pubkey/elgamal/elgamal.cpp b/comm/third_party/botan/src/lib/pubkey/elgamal/elgamal.cpp
new file mode 100644
index 0000000000..21e126031a
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/elgamal/elgamal.cpp
@@ -0,0 +1,213 @@
+/*
+* ElGamal
+* (C) 1999-2007,2018,2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/elgamal.h>
+#include <botan/internal/pk_ops_impl.h>
+#include <botan/internal/monty_exp.h>
+#include <botan/keypair.h>
+#include <botan/blinding.h>
+
+namespace Botan {
+
+/*
+* ElGamal_PublicKey Constructor
+*/
+ElGamal_PublicKey::ElGamal_PublicKey(const DL_Group& group, const BigInt& y) :
+ DL_Scheme_PublicKey(group, y)
+ {
+ }
+
+/*
+* ElGamal_PrivateKey Constructor
+*/
+ElGamal_PrivateKey::ElGamal_PrivateKey(RandomNumberGenerator& rng,
+ const DL_Group& group,
+ const BigInt& x)
+ {
+ m_x = x;
+ m_group = group;
+
+ if(m_x.is_zero())
+ {
+ const size_t exp_bits = m_group.exponent_bits();
+ m_x.randomize(rng, exp_bits);
+ m_y = m_group.power_g_p(m_x, exp_bits);
+ }
+ else
+ {
+ m_y = m_group.power_g_p(m_x, m_group.p_bits());
+ }
+ }
+
+ElGamal_PrivateKey::ElGamal_PrivateKey(const AlgorithmIdentifier& alg_id,
+ const secure_vector<uint8_t>& key_bits) :
+ DL_Scheme_PrivateKey(alg_id, key_bits, DL_Group::ANSI_X9_42)
+ {
+ m_y = m_group.power_g_p(m_x, m_group.p_bits());
+ }
+
+/*
+* Check Private ElGamal Parameters
+*/
+bool ElGamal_PrivateKey::check_key(RandomNumberGenerator& rng,
+ bool strong) const
+ {
+ if(!DL_Scheme_PrivateKey::check_key(rng, strong))
+ return false;
+
+ if(!strong)
+ return true;
+
+ return KeyPair::encryption_consistency_check(rng, *this, "OAEP(SHA-256)");
+ }
+
+namespace {
+
+/**
+* ElGamal encryption operation
+*/
+class ElGamal_Encryption_Operation final : public PK_Ops::Encryption_with_EME
+ {
+ public:
+
+ size_t ciphertext_length(size_t) const override { return 2*m_group.p_bytes(); }
+
+ size_t max_raw_input_bits() const override { return m_group.p_bits() - 1; }
+
+ ElGamal_Encryption_Operation(const ElGamal_PublicKey& key, const std::string& eme);
+
+ secure_vector<uint8_t> raw_encrypt(const uint8_t msg[], size_t msg_len,
+ RandomNumberGenerator& rng) override;
+
+ private:
+ const DL_Group m_group;
+ std::shared_ptr<const Montgomery_Exponentation_State> m_monty_y_p;
+ };
+
+ElGamal_Encryption_Operation::ElGamal_Encryption_Operation(const ElGamal_PublicKey& key,
+ const std::string& eme) :
+ PK_Ops::Encryption_with_EME(eme),
+ m_group(key.get_group())
+ {
+ const size_t powm_window = 4;
+ m_monty_y_p = monty_precompute(key.get_group().monty_params_p(),
+ key.get_y(),
+ powm_window);
+ }
+
+secure_vector<uint8_t>
+ElGamal_Encryption_Operation::raw_encrypt(const uint8_t msg[], size_t msg_len,
+ RandomNumberGenerator& rng)
+ {
+ BigInt m(msg, msg_len);
+
+ if(m >= m_group.get_p())
+ throw Invalid_Argument("ElGamal encryption: Input is too large");
+
+ /*
+ Some ElGamal implementations generate keys where using short exponents
+ is unsafe. Always use full length exponents to avoid this.
+
+ See https://eprint.iacr.org/2021/923 for details.
+ */
+ const size_t k_bits = m_group.p_bits() - 1;
+ const BigInt k(rng, k_bits, false);
+
+ const BigInt a = m_group.power_g_p(k, k_bits);
+ const BigInt b = m_group.multiply_mod_p(m, monty_execute(*m_monty_y_p, k, k_bits));
+
+ return BigInt::encode_fixed_length_int_pair(a, b, m_group.p_bytes());
+ }
+
+/**
+* ElGamal decryption operation
+*/
+class ElGamal_Decryption_Operation final : public PK_Ops::Decryption_with_EME
+ {
+ public:
+
+ ElGamal_Decryption_Operation(const ElGamal_PrivateKey& key,
+ const std::string& eme,
+ RandomNumberGenerator& rng);
+
+ size_t plaintext_length(size_t) const override { return m_group.p_bytes(); }
+
+ secure_vector<uint8_t> raw_decrypt(const uint8_t msg[], size_t msg_len) override;
+ private:
+ BigInt powermod_x_p(const BigInt& v) const
+ {
+ const size_t powm_window = 4;
+ auto powm_v_p = monty_precompute(m_monty_p, v, powm_window);
+ return monty_execute(*powm_v_p, m_x, m_x_bits);
+ }
+
+ const DL_Group m_group;
+ const BigInt& m_x;
+ const size_t m_x_bits;
+ std::shared_ptr<const Montgomery_Params> m_monty_p;
+ Blinder m_blinder;
+ };
+
+ElGamal_Decryption_Operation::ElGamal_Decryption_Operation(const ElGamal_PrivateKey& key,
+ const std::string& eme,
+ RandomNumberGenerator& rng) :
+ PK_Ops::Decryption_with_EME(eme),
+ m_group(key.get_group()),
+ m_x(key.get_x()),
+ m_x_bits(m_x.bits()),
+ m_monty_p(key.get_group().monty_params_p()),
+ m_blinder(m_group.get_p(),
+ rng,
+ [](const BigInt& k) { return k; },
+ [this](const BigInt& k) { return powermod_x_p(k); })
+ {
+ }
+
+secure_vector<uint8_t>
+ElGamal_Decryption_Operation::raw_decrypt(const uint8_t msg[], size_t msg_len)
+ {
+ const size_t p_bytes = m_group.p_bytes();
+
+ if(msg_len != 2 * p_bytes)
+ throw Invalid_Argument("ElGamal decryption: Invalid message");
+
+ BigInt a(msg, p_bytes);
+ const BigInt b(msg + p_bytes, p_bytes);
+
+ if(a >= m_group.get_p() || b >= m_group.get_p())
+ throw Invalid_Argument("ElGamal decryption: Invalid message");
+
+ a = m_blinder.blind(a);
+
+ const BigInt r = m_group.multiply_mod_p(m_group.inverse_mod_p(powermod_x_p(a)), b);
+
+ return BigInt::encode_1363(m_blinder.unblind(r), p_bytes);
+ }
+
+}
+
+std::unique_ptr<PK_Ops::Encryption>
+ElGamal_PublicKey::create_encryption_op(RandomNumberGenerator& /*rng*/,
+ const std::string& params,
+ const std::string& provider) const
+ {
+ if(provider == "base" || provider.empty())
+ return std::unique_ptr<PK_Ops::Encryption>(new ElGamal_Encryption_Operation(*this, params));
+ throw Provider_Not_Found(algo_name(), provider);
+ }
+
+std::unique_ptr<PK_Ops::Decryption>
+ElGamal_PrivateKey::create_decryption_op(RandomNumberGenerator& rng,
+ const std::string& params,
+ const std::string& provider) const
+ {
+ if(provider == "base" || provider.empty())
+ return std::unique_ptr<PK_Ops::Decryption>(new ElGamal_Decryption_Operation(*this, params, rng));
+ throw Provider_Not_Found(algo_name(), provider);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/pubkey/elgamal/elgamal.h b/comm/third_party/botan/src/lib/pubkey/elgamal/elgamal.h
new file mode 100644
index 0000000000..e4d932f6aa
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/elgamal/elgamal.h
@@ -0,0 +1,85 @@
+/*
+* ElGamal
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_ELGAMAL_H_
+#define BOTAN_ELGAMAL_H_
+
+#include <botan/dl_algo.h>
+
+namespace Botan {
+
+/**
+* ElGamal Public Key
+*/
+class BOTAN_PUBLIC_API(2,0) ElGamal_PublicKey : public virtual DL_Scheme_PublicKey
+ {
+ public:
+ std::string algo_name() const override { return "ElGamal"; }
+ DL_Group::Format group_format() const override { return DL_Group::ANSI_X9_42; }
+
+ /**
+ * Load a public key.
+ * @param alg_id the X.509 algorithm identifier
+ * @param key_bits DER encoded public key bits
+ */
+ ElGamal_PublicKey(const AlgorithmIdentifier& alg_id,
+ const std::vector<uint8_t>& key_bits) :
+ DL_Scheme_PublicKey(alg_id, key_bits, DL_Group::ANSI_X9_42)
+ {}
+
+ /**
+ * Create a public key.
+ * @param group the underlying DL group
+ * @param y the public value y = g^x mod p
+ */
+ ElGamal_PublicKey(const DL_Group& group, const BigInt& y);
+
+ std::unique_ptr<PK_Ops::Encryption>
+ create_encryption_op(RandomNumberGenerator& rng,
+ const std::string& params,
+ const std::string& provider) const override;
+
+ protected:
+ ElGamal_PublicKey() = default;
+ };
+
+/**
+* ElGamal Private Key
+*/
+class BOTAN_PUBLIC_API(2,0) ElGamal_PrivateKey final : public ElGamal_PublicKey,
+ public virtual DL_Scheme_PrivateKey
+ {
+ public:
+ bool check_key(RandomNumberGenerator& rng, bool) const override;
+
+ /**
+ * Load a private key.
+ * @param alg_id the X.509 algorithm identifier
+ * @param key_bits DER encoded key bits in ANSI X9.42 format
+ */
+ ElGamal_PrivateKey(const AlgorithmIdentifier& alg_id,
+ const secure_vector<uint8_t>& key_bits);
+
+ /**
+ * Create a private key.
+ * @param rng random number generator to use
+ * @param group the group to be used in the key
+ * @param priv_key the key's secret value (or if zero, generate a new key)
+ */
+ ElGamal_PrivateKey(RandomNumberGenerator& rng,
+ const DL_Group& group,
+ const BigInt& priv_key = 0);
+
+ std::unique_ptr<PK_Ops::Decryption>
+ create_decryption_op(RandomNumberGenerator& rng,
+ const std::string& params,
+ const std::string& provider) const override;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/pubkey/elgamal/info.txt b/comm/third_party/botan/src/lib/pubkey/elgamal/info.txt
new file mode 100644
index 0000000000..0b36658bcd
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/elgamal/info.txt
@@ -0,0 +1,10 @@
+<defines>
+ELGAMAL -> 20131128
+</defines>
+
+<requires>
+dl_algo
+dl_group
+keypair
+numbertheory
+</requires>
diff --git a/comm/third_party/botan/src/lib/pubkey/gost_3410/gost_3410.cpp b/comm/third_party/botan/src/lib/pubkey/gost_3410/gost_3410.cpp
new file mode 100644
index 0000000000..d1f8fc7867
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/gost_3410/gost_3410.cpp
@@ -0,0 +1,253 @@
+/*
+* GOST 34.10-2012
+* (C) 2007 Falko Strenzke, FlexSecure GmbH
+* Manuel Hartl, FlexSecure GmbH
+* (C) 2008-2010,2015,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/gost_3410.h>
+#include <botan/internal/pk_ops_impl.h>
+#include <botan/internal/point_mul.h>
+#include <botan/reducer.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
+
+namespace Botan {
+
+std::vector<uint8_t> GOST_3410_PublicKey::public_key_bits() const
+ {
+ const BigInt x = public_point().get_affine_x();
+ const BigInt y = public_point().get_affine_y();
+
+ size_t part_size = std::max(x.bytes(), y.bytes());
+
+ std::vector<uint8_t> bits(2*part_size);
+
+ x.binary_encode(&bits[part_size - x.bytes()]);
+ y.binary_encode(&bits[2*part_size - y.bytes()]);
+
+ // Keys are stored in little endian format (WTF)
+ for(size_t i = 0; i != part_size / 2; ++i)
+ {
+ std::swap(bits[i], bits[part_size-1-i]);
+ std::swap(bits[part_size+i], bits[2*part_size-1-i]);
+ }
+
+ std::vector<uint8_t> output;
+ DER_Encoder(output).encode(bits, OCTET_STRING);
+ return output;
+ }
+
+std::string GOST_3410_PublicKey::algo_name() const
+ {
+ const size_t p_bits = domain().get_p_bits();
+
+ if(p_bits == 256 || p_bits == 512)
+ return "GOST-34.10-2012-" + std::to_string(p_bits);
+ else
+ throw Encoding_Error("GOST-34.10-2012 is not defined for parameters of this size");
+ }
+
+AlgorithmIdentifier GOST_3410_PublicKey::algorithm_identifier() const
+ {
+ std::vector<uint8_t> params;
+
+ const OID gost_oid = get_oid();
+ const OID domain_oid = domain().get_curve_oid();
+
+ DER_Encoder(params).start_cons(SEQUENCE).encode(domain_oid).end_cons();
+
+ return AlgorithmIdentifier(gost_oid, params);
+ }
+
+GOST_3410_PublicKey::GOST_3410_PublicKey(const AlgorithmIdentifier& alg_id,
+ const std::vector<uint8_t>& key_bits)
+ {
+ OID ecc_param_id;
+
+ // The parameters also includes hash and cipher OIDs
+ BER_Decoder(alg_id.get_parameters()).start_cons(SEQUENCE).decode(ecc_param_id);
+
+ m_domain_params = EC_Group(ecc_param_id);
+
+ const size_t p_bits = m_domain_params.get_p_bits();
+ if(p_bits != 256 && p_bits != 512)
+ throw Decoding_Error("GOST-34.10-2012 is not defined for parameters of size " +
+ std::to_string(p_bits));
+
+ secure_vector<uint8_t> bits;
+ BER_Decoder(key_bits).decode(bits, OCTET_STRING);
+
+ const size_t part_size = bits.size() / 2;
+
+ // Keys are stored in little endian format (WTF)
+ for(size_t i = 0; i != part_size / 2; ++i)
+ {
+ std::swap(bits[i], bits[part_size-1-i]);
+ std::swap(bits[part_size+i], bits[2*part_size-1-i]);
+ }
+
+ BigInt x(bits.data(), part_size);
+ BigInt y(&bits[part_size], part_size);
+
+ m_public_key = domain().point(x, y);
+
+ BOTAN_ASSERT(m_public_key.on_the_curve(),
+ "Loaded GOST 34.10 public key is on the curve");
+ }
+
+GOST_3410_PrivateKey::GOST_3410_PrivateKey(RandomNumberGenerator& rng,
+ const EC_Group& domain,
+ const BigInt& x) :
+ EC_PrivateKey(rng, domain, x)
+ {
+ const size_t p_bits = m_domain_params.get_p_bits();
+ if(p_bits != 256 && p_bits != 512)
+ throw Decoding_Error("GOST-34.10-2012 is not defined for parameters of size " +
+ std::to_string(p_bits));
+ }
+
+namespace {
+
+BigInt decode_le(const uint8_t msg[], size_t msg_len)
+ {
+ secure_vector<uint8_t> msg_le(msg, msg + msg_len);
+
+ for(size_t i = 0; i != msg_le.size() / 2; ++i)
+ std::swap(msg_le[i], msg_le[msg_le.size()-1-i]);
+
+ return BigInt(msg_le.data(), msg_le.size());
+ }
+
+/**
+* GOST-34.10 signature operation
+*/
+class GOST_3410_Signature_Operation final : public PK_Ops::Signature_with_EMSA
+ {
+ public:
+ GOST_3410_Signature_Operation(const GOST_3410_PrivateKey& gost_3410,
+ const std::string& emsa) :
+ PK_Ops::Signature_with_EMSA(emsa),
+ m_group(gost_3410.domain()),
+ m_x(gost_3410.private_value())
+ {}
+
+ size_t signature_length() const override { return 2*m_group.get_order_bytes(); }
+
+ size_t max_input_bits() const override { return m_group.get_order_bits(); }
+
+ secure_vector<uint8_t> raw_sign(const uint8_t msg[], size_t msg_len,
+ RandomNumberGenerator& rng) override;
+
+ private:
+ const EC_Group m_group;
+ const BigInt& m_x;
+ std::vector<BigInt> m_ws;
+ };
+
+secure_vector<uint8_t>
+GOST_3410_Signature_Operation::raw_sign(const uint8_t msg[], size_t msg_len,
+ RandomNumberGenerator& rng)
+ {
+ const BigInt k = m_group.random_scalar(rng);
+
+ BigInt e = decode_le(msg, msg_len);
+
+ e = m_group.mod_order(e);
+ if(e == 0)
+ e = 1;
+
+ const BigInt r = m_group.mod_order(
+ m_group.blinded_base_point_multiply_x(k, rng, m_ws));
+
+ const BigInt s = m_group.mod_order(
+ m_group.multiply_mod_order(r, m_x) +
+ m_group.multiply_mod_order(k, e));
+
+ if(r == 0 || s == 0)
+ throw Internal_Error("GOST 34.10 signature generation failed, r/s equal to zero");
+
+ return BigInt::encode_fixed_length_int_pair(s, r, m_group.get_order_bytes());
+ }
+
+/**
+* GOST-34.10 verification operation
+*/
+class GOST_3410_Verification_Operation final : public PK_Ops::Verification_with_EMSA
+ {
+ public:
+
+ GOST_3410_Verification_Operation(const GOST_3410_PublicKey& gost,
+ const std::string& emsa) :
+ PK_Ops::Verification_with_EMSA(emsa),
+ m_group(gost.domain()),
+ m_gy_mul(m_group.get_base_point(), gost.public_point())
+ {}
+
+ size_t max_input_bits() const override { return m_group.get_order_bits(); }
+
+ bool with_recovery() const override { return false; }
+
+ bool verify(const uint8_t msg[], size_t msg_len,
+ const uint8_t sig[], size_t sig_len) override;
+ private:
+ const EC_Group m_group;
+ const PointGFp_Multi_Point_Precompute m_gy_mul;
+ };
+
+bool GOST_3410_Verification_Operation::verify(const uint8_t msg[], size_t msg_len,
+ const uint8_t sig[], size_t sig_len)
+ {
+ if(sig_len != m_group.get_order_bytes() * 2)
+ return false;
+
+ const BigInt s(sig, sig_len / 2);
+ const BigInt r(sig + sig_len / 2, sig_len / 2);
+
+ const BigInt& order = m_group.get_order();
+
+ if(r <= 0 || r >= order || s <= 0 || s >= order)
+ return false;
+
+ BigInt e = decode_le(msg, msg_len);
+ e = m_group.mod_order(e);
+ if(e == 0)
+ e = 1;
+
+ const BigInt v = m_group.inverse_mod_order(e);
+
+ const BigInt z1 = m_group.multiply_mod_order(s, v);
+ const BigInt z2 = m_group.multiply_mod_order(-r, v);
+
+ const PointGFp R = m_gy_mul.multi_exp(z1, z2);
+
+ if(R.is_zero())
+ return false;
+
+ return (R.get_affine_x() == r);
+ }
+
+}
+
+std::unique_ptr<PK_Ops::Verification>
+GOST_3410_PublicKey::create_verification_op(const std::string& params,
+ const std::string& provider) const
+ {
+ if(provider == "base" || provider.empty())
+ return std::unique_ptr<PK_Ops::Verification>(new GOST_3410_Verification_Operation(*this, params));
+ throw Provider_Not_Found(algo_name(), provider);
+ }
+
+std::unique_ptr<PK_Ops::Signature>
+GOST_3410_PrivateKey::create_signature_op(RandomNumberGenerator& /*rng*/,
+ const std::string& params,
+ const std::string& provider) const
+ {
+ if(provider == "base" || provider.empty())
+ return std::unique_ptr<PK_Ops::Signature>(new GOST_3410_Signature_Operation(*this, params));
+ throw Provider_Not_Found(algo_name(), provider);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/pubkey/gost_3410/gost_3410.h b/comm/third_party/botan/src/lib/pubkey/gost_3410/gost_3410.h
new file mode 100644
index 0000000000..9eedaf1224
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/gost_3410/gost_3410.h
@@ -0,0 +1,104 @@
+/*
+* GOST 34.10-2001
+* (C) 2007 Falko Strenzke, FlexSecure GmbH
+* Manuel Hartl, FlexSecure GmbH
+* (C) 2008-2010 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_GOST_3410_KEY_H_
+#define BOTAN_GOST_3410_KEY_H_
+
+#include <botan/ecc_key.h>
+
+namespace Botan {
+
+/**
+* GOST-34.10 Public Key
+*/
+class BOTAN_PUBLIC_API(2,0) GOST_3410_PublicKey : public virtual EC_PublicKey
+ {
+ public:
+
+ /**
+ * Construct a public key from a given public point.
+ * @param dom_par the domain parameters associated with this key
+ * @param public_point the public point defining this key
+ */
+ GOST_3410_PublicKey(const EC_Group& dom_par,
+ const PointGFp& public_point) :
+ EC_PublicKey(dom_par, public_point) {}
+
+ /**
+ * Load a public key.
+ * @param alg_id the X.509 algorithm identifier
+ * @param key_bits DER encoded public key bits
+ */
+ GOST_3410_PublicKey(const AlgorithmIdentifier& alg_id,
+ const std::vector<uint8_t>& key_bits);
+
+ /**
+ * Get this keys algorithm name.
+ * @result this keys algorithm name
+ */
+ std::string algo_name() const override;
+
+ AlgorithmIdentifier algorithm_identifier() const override;
+
+ std::vector<uint8_t> public_key_bits() const override;
+
+ size_t message_parts() const override { return 2; }
+
+ size_t message_part_size() const override
+ { return domain().get_order().bytes(); }
+
+ Signature_Format default_x509_signature_format() const override
+ { return IEEE_1363; }
+
+ std::unique_ptr<PK_Ops::Verification>
+ create_verification_op(const std::string& params,
+ const std::string& provider) const override;
+
+ protected:
+ GOST_3410_PublicKey() = default;
+ };
+
+/**
+* GOST-34.10 Private Key
+*/
+class BOTAN_PUBLIC_API(2,0) GOST_3410_PrivateKey final :
+ public GOST_3410_PublicKey, public EC_PrivateKey
+ {
+ public:
+ /**
+ * Load a private key.
+ * @param alg_id the X.509 algorithm identifier
+ * @param key_bits ECPrivateKey bits
+ */
+ GOST_3410_PrivateKey(const AlgorithmIdentifier& alg_id,
+ const secure_vector<uint8_t>& key_bits) :
+ EC_PrivateKey(alg_id, key_bits) {}
+
+ /**
+ * Generate a new private key
+ * @param rng a random number generator
+ * @param domain parameters to used for this key
+ * @param x the private key; if zero, a new random key is generated
+ */
+ GOST_3410_PrivateKey(RandomNumberGenerator& rng,
+ const EC_Group& domain,
+ const BigInt& x = 0);
+
+ AlgorithmIdentifier pkcs8_algorithm_identifier() const override
+ { return EC_PublicKey::algorithm_identifier(); }
+
+ std::unique_ptr<PK_Ops::Signature>
+ create_signature_op(RandomNumberGenerator& rng,
+ const std::string& params,
+ const std::string& provider) const override;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/pubkey/gost_3410/info.txt b/comm/third_party/botan/src/lib/pubkey/gost_3410/info.txt
new file mode 100644
index 0000000000..805e31485a
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/gost_3410/info.txt
@@ -0,0 +1,12 @@
+<defines>
+GOST_34_10_2001 -> 20131128
+GOST_34_10_2012 -> 20190801
+</defines>
+
+<requires>
+asn1
+ec_group
+ecc_key
+numbertheory
+rng
+</requires>
diff --git a/comm/third_party/botan/src/lib/pubkey/info.txt b/comm/third_party/botan/src/lib/pubkey/info.txt
new file mode 100644
index 0000000000..c6e8036e59
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/info.txt
@@ -0,0 +1,31 @@
+<defines>
+PUBLIC_KEY_CRYPTO -> 20131128
+</defines>
+
+<header:public>
+blinding.h
+pk_algs.h
+pk_keys.h
+pk_ops.h
+pk_ops_fwd.h
+pkcs8.h
+pubkey.h
+workfactor.h
+x509_key.h
+</header:public>
+
+<header:internal>
+pk_ops_impl.h
+</header:internal>
+
+<requires>
+asn1
+bigint
+kdf
+pem
+pk_pad
+numbertheory
+rng
+hash
+hex
+</requires>
diff --git a/comm/third_party/botan/src/lib/pubkey/keypair/info.txt b/comm/third_party/botan/src/lib/pubkey/keypair/info.txt
new file mode 100644
index 0000000000..ed85abf691
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/keypair/info.txt
@@ -0,0 +1,6 @@
+<defines>
+KEYPAIR_TESTING -> 20131128
+</defines>
+
+<requires>
+</requires>
diff --git a/comm/third_party/botan/src/lib/pubkey/keypair/keypair.cpp b/comm/third_party/botan/src/lib/pubkey/keypair/keypair.cpp
new file mode 100644
index 0000000000..d5cd001720
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/keypair/keypair.cpp
@@ -0,0 +1,85 @@
+/*
+* Keypair Checks
+* (C) 1999-2010 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/keypair.h>
+#include <botan/pubkey.h>
+#include <botan/rng.h>
+
+namespace Botan {
+
+namespace KeyPair {
+
+/*
+* Check an encryption key pair for consistency
+*/
+bool encryption_consistency_check(RandomNumberGenerator& rng,
+ const Private_Key& private_key,
+ const Public_Key& public_key,
+ const std::string& padding)
+ {
+ PK_Encryptor_EME encryptor(public_key, rng, padding);
+ PK_Decryptor_EME decryptor(private_key, rng, padding);
+
+ /*
+ Weird corner case, if the key is too small to encrypt anything at
+ all. This can happen with very small RSA keys with PSS
+ */
+ if(encryptor.maximum_input_size() == 0)
+ return true;
+
+ std::vector<uint8_t> plaintext;
+ rng.random_vec(plaintext, encryptor.maximum_input_size() - 1);
+
+ std::vector<uint8_t> ciphertext = encryptor.encrypt(plaintext, rng);
+ if(ciphertext == plaintext)
+ return false;
+
+ std::vector<uint8_t> decrypted = unlock(decryptor.decrypt(ciphertext));
+
+ return (plaintext == decrypted);
+ }
+
+/*
+* Check a signature key pair for consistency
+*/
+bool signature_consistency_check(RandomNumberGenerator& rng,
+ const Private_Key& private_key,
+ const Public_Key& public_key,
+ const std::string& padding)
+ {
+ PK_Signer signer(private_key, rng, padding);
+ PK_Verifier verifier(public_key, padding);
+
+ std::vector<uint8_t> message(32);
+ rng.randomize(message.data(), message.size());
+
+ std::vector<uint8_t> signature;
+
+ try
+ {
+ signature = signer.sign_message(message, rng);
+ }
+ catch(Encoding_Error&)
+ {
+ return false;
+ }
+
+ if(!verifier.verify_message(message, signature))
+ return false;
+
+ // Now try to check a corrupt signature, ensure it does not succeed
+ ++signature[0];
+
+ if(verifier.verify_message(message, signature))
+ return false;
+
+ return true;
+ }
+
+}
+
+}
diff --git a/comm/third_party/botan/src/lib/pubkey/keypair/keypair.h b/comm/third_party/botan/src/lib/pubkey/keypair/keypair.h
new file mode 100644
index 0000000000..6900faa359
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/keypair/keypair.h
@@ -0,0 +1,85 @@
+/*
+* Keypair Checks
+* (C) 1999-2010 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_KEYPAIR_CHECKS_H_
+#define BOTAN_KEYPAIR_CHECKS_H_
+
+#include <botan/pk_keys.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(keypair.h)
+
+namespace Botan {
+
+namespace KeyPair {
+
+/**
+* Tests whether the key is consistent for encryption; whether
+* encrypting and then decrypting gives to the original plaintext.
+* @param rng the rng to use
+* @param private_key the key to test
+* @param public_key the key to test
+* @param padding the encryption padding method to use
+* @return true if consistent otherwise false
+*/
+BOTAN_PUBLIC_API(2,0) bool
+encryption_consistency_check(RandomNumberGenerator& rng,
+ const Private_Key& private_key,
+ const Public_Key& public_key,
+ const std::string& padding);
+
+/**
+* Tests whether the key is consistent for signatures; whether a
+* signature can be created and then verified
+* @param rng the rng to use
+* @param private_key the key to test
+* @param public_key the key to test
+* @param padding the signature padding method to use
+* @return true if consistent otherwise false
+*/
+BOTAN_PUBLIC_API(2,0) bool
+signature_consistency_check(RandomNumberGenerator& rng,
+ const Private_Key& private_key,
+ const Public_Key& public_key,
+ const std::string& padding);
+
+/**
+* Tests whether the key is consistent for encryption; whether
+* encrypting and then decrypting gives to the original plaintext.
+* @param rng the rng to use
+* @param key the key to test
+* @param padding the encryption padding method to use
+* @return true if consistent otherwise false
+*/
+inline bool
+encryption_consistency_check(RandomNumberGenerator& rng,
+ const Private_Key& key,
+ const std::string& padding)
+ {
+ return encryption_consistency_check(rng, key, key, padding);
+ }
+
+/**
+* Tests whether the key is consistent for signatures; whether a
+* signature can be created and then verified
+* @param rng the rng to use
+* @param key the key to test
+* @param padding the signature padding method to use
+* @return true if consistent otherwise false
+*/
+inline bool
+signature_consistency_check(RandomNumberGenerator& rng,
+ const Private_Key& key,
+ const std::string& padding)
+ {
+ return signature_consistency_check(rng, key, key, padding);
+ }
+
+}
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/pubkey/mce/code_based_key_gen.cpp b/comm/third_party/botan/src/lib/pubkey/mce/code_based_key_gen.cpp
new file mode 100644
index 0000000000..8dc3a3178b
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/mce/code_based_key_gen.cpp
@@ -0,0 +1,298 @@
+/*
+ * (C) Copyright Projet SECRET, INRIA, Rocquencourt
+ * (C) Bhaskar Biswas and Nicolas Sendrier
+ *
+ * (C) 2014 cryptosource GmbH
+ * (C) 2014 Falko Strenzke fstrenzke@cryptosource.de
+ * (C) 2015 Jack Lloyd
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ *
+ */
+
+#include <botan/mceliece.h>
+#include <botan/internal/mce_internal.h>
+#include <botan/internal/code_based_util.h>
+#include <botan/polyn_gf2m.h>
+#include <botan/loadstor.h>
+
+namespace Botan {
+
+namespace {
+
+class binary_matrix final
+ {
+ public:
+ binary_matrix(size_t m_rown, size_t m_coln);
+
+ void row_xor(size_t a, size_t b);
+ secure_vector<size_t> row_reduced_echelon_form();
+
+ /**
+ * return the coefficient out of F_2
+ */
+ uint32_t coef(size_t i, size_t j)
+ {
+ return (m_elem[(i) * m_rwdcnt + (j) / 32] >> (j % 32)) & 1;
+ }
+
+ void set_coef_to_one(size_t i, size_t j)
+ {
+ m_elem[(i) * m_rwdcnt + (j) / 32] |= (static_cast<uint32_t>(1) << ((j) % 32)) ;
+ }
+
+ void toggle_coeff(size_t i, size_t j)
+ {
+ m_elem[(i) * m_rwdcnt + (j) / 32] ^= (static_cast<uint32_t>(1) << ((j) % 32)) ;
+ }
+
+ size_t rows() const { return m_rown; }
+
+ size_t columns() const { return m_coln; }
+
+ private:
+ size_t m_rown; // number of rows.
+ size_t m_coln; // number of columns.
+ size_t m_rwdcnt; // number of words in a row
+ public:
+ // TODO this should be private
+ std::vector<uint32_t> 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);
+ m_elem = std::vector<uint32_t>(m_rown * m_rwdcnt);
+ }
+
+void binary_matrix::row_xor(size_t a, size_t b)
+ {
+ for(size_t i = 0; i != m_rwdcnt; i++)
+ {
+ m_elem[a*m_rwdcnt+i] ^= m_elem[b*m_rwdcnt+i];
+ }
+ }
+
+//the matrix is reduced from LSB...(from right)
+secure_vector<size_t> binary_matrix::row_reduced_echelon_form()
+ {
+ secure_vector<size_t> perm(m_coln);
+ for(size_t i = 0; i != m_coln; i++)
+ {
+ perm[i] = i; // initialize permutation.
+ }
+
+ uint32_t failcnt = 0;
+
+ size_t max = m_coln - 1;
+ for(size_t i = 0; i != m_rown; i++, max--)
+ {
+ bool found_row = false;
+
+ for(size_t j = i; !found_row && j != m_rown; j++)
+ {
+ if(coef(j, max))
+ {
+ if(i != j) //not needed as ith row is 0 and jth row is 1.
+ {
+ row_xor(i, j);//xor to the row.(swap)?
+ }
+
+ found_row = true;
+ }
+ }
+
+ //if no row with a 1 found then swap last column and the column with no 1 down.
+ if(!found_row)
+ {
+ perm[m_coln - m_rown - 1 - failcnt] = static_cast<int>(max);
+ failcnt++;
+ if(!max)
+ {
+ perm.resize(0);
+ }
+ i--;
+ }
+ else
+ {
+ 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))
+ {
+ 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))
+ {
+ row_xor(j - 1, i);
+ }
+ }
+ }
+ }//end for(i)
+ return perm;
+ }
+
+void randomize_support(std::vector<gf2m>& L, RandomNumberGenerator& rng)
+ {
+ for(size_t i = 0; i != L.size(); ++i)
+ {
+ 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()]);
+ }
+ }
+
+std::unique_ptr<binary_matrix> generate_R(std::vector<gf2m> &L, polyn_gf2m* g, std::shared_ptr<GF2m_Field> sp_field, size_t code_length, size_t t)
+ {
+ //L- Support
+ //t- Number of errors
+ //n- Length of the Goppa code
+ //m- The extension degree of the GF
+ //g- The generator polynomial.
+
+ const size_t r = t * sp_field->get_extension_degree();
+
+ binary_matrix H(r, code_length);
+
+ for(size_t i = 0; i != code_length; i++)
+ {
+ gf2m x = g->eval(lex_to_gray(L[i]));//evaluate the polynomial at the point L[i].
+ x = sp_field->gf_inv(x);
+ 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))
+ {
+ //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);
+ }
+ }
+ y = sp_field->gf_mul(y,lex_to_gray(L[i]));
+ }
+ }//The H matrix is fed.
+
+ secure_vector<size_t> perm = H.row_reduced_echelon_form();
+ if(perm.size() == 0)
+ {
+ throw Invalid_State("McEliece keygen failed - could not bring matrix to row reduced echelon form");
+ }
+
+ std::unique_ptr<binary_matrix> result(new binary_matrix(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]))
+ {
+ result->toggle_coeff(i,j);
+ }
+ }
+ }
+
+ std::vector<gf2m> Laux(code_length);
+ for(size_t i = 0; i < code_length; ++i)
+ {
+ Laux[i] = L[perm[i]];
+ }
+
+ for(size_t i = 0; i < code_length; ++i)
+ {
+ L[i] = Laux[i];
+ }
+ return result;
+ }
+}
+
+McEliece_PrivateKey generate_mceliece_key(RandomNumberGenerator & rng, size_t ext_deg, size_t code_length, size_t t)
+ {
+ const size_t codimension = t * ext_deg;
+
+ if(code_length <= codimension)
+ {
+ throw Invalid_Argument("invalid McEliece parameters");
+ }
+
+ std::shared_ptr<GF2m_Field> sp_field(new GF2m_Field(ext_deg));
+
+ //pick the support.........
+ std::vector<gf2m> L(code_length);
+
+ for(size_t i = 0; i != L.size(); i++)
+ {
+ L[i] = static_cast<gf2m>(i);
+ }
+ randomize_support(L, rng);
+ polyn_gf2m g(sp_field); // create as zero
+
+ bool success = false;
+ std::unique_ptr<binary_matrix> R;
+
+ do
+ {
+ // create a random irreducible polynomial
+ g = polyn_gf2m(t, rng, sp_field);
+
+ try
+ {
+ R = generate_R(L, &g, sp_field, code_length, t);
+ success = true;
+ }
+ catch(const Invalid_State &)
+ {
+ }
+ } while (!success);
+
+ std::vector<polyn_gf2m> sqrtmod = polyn_gf2m::sqrt_mod_init( g);
+ std::vector<polyn_gf2m> F = syndrome_init(g, L, static_cast<int>(code_length));
+
+ // Each F[i] is the (precomputed) syndrome of the error vector with
+ // a single '1' in i-th position.
+ // We do not store the F[i] as polynomials of degree t , but
+ // as binary vectors of length ext_deg * t (this will
+ // speed up the syndrome computation)
+ //
+ std::vector<uint32_t> H(bit_size_to_32bit_size(codimension) * code_length);
+ uint32_t* sk = H.data();
+ for(size_t i = 0; i < code_length; ++i)
+ {
+ for(size_t l = 0; l < t; ++l)
+ {
+ const size_t k = (l * ext_deg) / 32;
+ const uint8_t j = (l * ext_deg) % 32;
+ sk[k] ^= static_cast<uint32_t>(F[i].get_coef(l)) << j;
+ if(j + ext_deg > 32)
+ {
+ sk[k + 1] ^= F[i].get_coef(l) >> (32 - j);
+ }
+ }
+ sk += bit_size_to_32bit_size(codimension);
+ }
+
+ // We need the support L for decoding (decryption). In fact the
+ // inverse is needed
+
+ std::vector<gf2m> Linv(code_length) ;
+ for(size_t i = 0; i != Linv.size(); ++i)
+ {
+ Linv[L[i]] = static_cast<gf2m>(i);
+ }
+ std::vector<uint8_t> pubmat(R->m_elem.size() * 4);
+ for(size_t i = 0; i < R->m_elem.size(); i++)
+ {
+ store_le(R->m_elem[i], &pubmat[i*4]);
+ }
+
+ return McEliece_PrivateKey(g, H, sqrtmod, Linv, pubmat);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/pubkey/mce/code_based_util.h b/comm/third_party/botan/src/lib/pubkey/mce/code_based_util.h
new file mode 100644
index 0000000000..5b0a9195a0
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/mce/code_based_util.h
@@ -0,0 +1,57 @@
+/*
+ * (C) Copyright Projet SECRET, INRIA, Rocquencourt
+ * (C) Bhaskar Biswas and Nicolas Sendrier
+ *
+ * (C) 2014 cryptosource GmbH
+ * (C) 2014 Falko Strenzke fstrenzke@cryptosource.de
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ *
+ */
+
+#ifndef BOTAN_CODE_BASED_UTIL_H_
+#define BOTAN_CODE_BASED_UTIL_H_
+
+#include <botan/gf2m_small_m.h>
+
+namespace Botan {
+
+/**
+* Expand an input to a bit mask depending on it being being zero or non-zero
+* @param tst the input
+* @return the mask 0xFFFF if tst is non-zero and 0 otherwise
+*/
+template<typename T>
+uint16_t expand_mask_16bit(T tst)
+ {
+ const uint16_t result = (tst != 0);
+ return ~(result - 1);
+ }
+
+inline gf2m gray_to_lex(gf2m gray)
+ {
+ gf2m result = gray ^ (gray >> 8);
+ result ^= (result >> 4);
+ result ^= (result >> 2);
+ result ^= (result >> 1);
+ return result;
+ }
+
+inline gf2m lex_to_gray(gf2m lex)
+ {
+ return (lex >> 1) ^ lex;
+ }
+
+inline size_t bit_size_to_byte_size(size_t bit_size)
+ {
+ return (bit_size - 1) / 8 + 1;
+ }
+
+inline size_t bit_size_to_32bit_size(size_t bit_size)
+ {
+ return (bit_size - 1) / 32 + 1;
+ }
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/pubkey/mce/gf2m_rootfind_dcmp.cpp b/comm/third_party/botan/src/lib/pubkey/mce/gf2m_rootfind_dcmp.cpp
new file mode 100644
index 0000000000..5fd88f3d71
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/mce/gf2m_rootfind_dcmp.cpp
@@ -0,0 +1,314 @@
+/*
+ * (C) 2014 cryptosource GmbH
+ * (C) 2014 Falko Strenzke fstrenzke@cryptosource.de
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ *
+ */
+
+#include <botan/polyn_gf2m.h>
+#include <botan/internal/bit_ops.h>
+#include <botan/internal/code_based_util.h>
+#include <botan/exceptn.h>
+
+namespace Botan {
+
+namespace {
+
+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);
+ cond_mask = expand_mask_16bit(cond_mask);
+ cond_mask = ~cond_mask; /* now cond = 1 if not enough roots */
+ patch_elem &= cond_mask;
+ for(size_t i = 0; i < res_root_arr_len; i++)
+ {
+ gf2m masked_patch_elem = (patch_elem++) & cond_mask;
+ res_root_arr[i] ^= masked_patch_elem++;
+ }
+ }
+
+class gf2m_decomp_rootfind_state
+ {
+ public:
+ gf2m_decomp_rootfind_state(const polyn_gf2m & p_polyn, size_t code_length);
+
+ void calc_LiK(const polyn_gf2m & sigma);
+ gf2m calc_Fxj_j_neq_0( const polyn_gf2m & sigma, gf2m j_gray);
+ void calc_next_Aij();
+ void calc_Ai_zero(const polyn_gf2m & sigma);
+ secure_vector<gf2m> find_roots(const polyn_gf2m & sigma);
+
+ private:
+ size_t m_code_length;
+ secure_vector<gf2m> m_Lik; // size is outer_summands * m
+ secure_vector<gf2m> m_Aij; // ...
+ uint32_t m_outer_summands;
+ gf2m m_j;
+ gf2m m_j_gray;
+ gf2m m_sigma_3_l;
+ gf2m m_sigma_3_neq_0_mask;
+ };
+
+/*
+* !! Attention: assumes gf2m is 16bit !!
+*/
+#if 0
+gf2m brootf_decomp_gray_to_lex(gf2m gray)
+ {
+ static_assert(sizeof(gf2m) == 2, "Expected size");
+ gf2m result = gray ^ (gray>>8);
+ result ^= (result >> 4);
+ result ^= (result >> 2);
+ result ^= (result >> 1);
+ return result;
+ }
+#endif
+
+/**
+* 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;
+ result += 4;
+ result /= 5;
+ return result;
+ }
+
+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<GF2m_Field> sp_field = polyn.get_sp_field();
+ 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 */
+ 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;
+ }
+ else
+ {
+ // dummy value needed for timing countermeasure
+ this->m_sigma_3_l = sp_field->gf_l_from_n(coeff_head);
+ this->m_sigma_3_neq_0_mask = 0 ;
+ }
+
+ this->m_outer_summands = 1 + brootf_decomp_calc_sum_limit(deg_sigma);
+ this->m_Lik.resize(this->m_outer_summands * sp_field->get_extension_degree());
+ this->m_Aij.resize(this->m_outer_summands);
+ }
+
+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++)
+ {
+ this->m_Aij[i] = sigma.get_coef(5*i);
+ }
+ this->m_j = 0;
+ this->m_j_gray = 0;
+ }
+
+void gf2m_decomp_rootfind_state::calc_next_Aij()
+ {
+ /*
+ * upon function entry, we have in the state j, Aij.
+ * 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;
+
+ this->m_j++;
+
+ new_j_gray = lex_to_gray(this->m_j);
+
+ if(this->m_j & 1) /* half of the times */
+ {
+ Lik_pos_base = 0;
+ }
+ else if(this->m_j & 2) /* one quarter of the times */
+ {
+ Lik_pos_base = this->m_outer_summands;
+ }
+ else if( this->m_j & 4) /* one eighth of the times */
+ {
+ Lik_pos_base = this->m_outer_summands * 2;
+ }
+ else if( this->m_j & 8) /* one sixteenth of the times */
+ {
+ Lik_pos_base = this->m_outer_summands * 3;
+ }
+ else if( this->m_j & 16) /* ... */
+ {
+ Lik_pos_base = this->m_outer_summands * 4;
+ }
+ else
+ {
+ gf2m delta_offs = 5;
+ diff = this->m_j_gray ^ new_j_gray;
+ while(((static_cast<gf2m>(1) << delta_offs) & diff) == 0)
+ {
+ delta_offs++;
+
+ }
+ Lik_pos_base = delta_offs * this->m_outer_summands;
+ }
+ this->m_j_gray = new_j_gray;
+
+ i = 0;
+ for(; 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<GF2m_Field> 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;
+ gf2m alpha_l_k_tt2_ttj[4];
+ alpha_l_k_tt2_ttj[0] = sp_field->gf_l_from_n(static_cast<gf2m>(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;
+ this->m_Lik[Lik_pos] = 0;
+ for(j = 0; j <= 3; j++)
+ {
+ gf2m f, x;
+ uint32_t f_ind = five_i + (static_cast<uint32_t>(1) << j);
+ if(f_ind > d)
+ {
+ break;
+ }
+ f = sigma.get_coef( f_ind);
+
+ x = sp_field->gf_mul_zrz(alpha_l_k_tt2_ttj[j], f);
+ this->m_Lik[Lik_pos] ^= x;
+ }
+ }
+ }
+ }
+
+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<GF2m_Field> 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);
+ 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);
+ sum &= this->m_sigma_3_neq_0_mask;
+ /* here, we rely on compiler to be unable to optimize
+ * for the state->sigma_3_neq_0_mask value
+ */
+ /* treat i = 0 special: */
+ sum ^= this->m_Aij[0];
+ /* 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 */
+ sum ^= x;
+ }
+
+ gf2m xl_j_tt_5i = xl_j_tt_5;
+
+ for(i = 2; i < this->m_outer_summands; i++)
+ {
+ gf2m x;
+ 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) */
+ sum ^= x;
+ }
+ return sum;
+ }
+
+secure_vector<gf2m> gf2m_decomp_rootfind_state::find_roots(const polyn_gf2m & sigma)
+ {
+ const int sigma_degree = sigma.get_degree();
+ BOTAN_ASSERT(sigma_degree > 0, "Valid sigma");
+ secure_vector<gf2m> result(sigma_degree);
+ uint32_t root_pos = 0;
+
+ this->calc_Ai_zero(sigma);
+ this->calc_LiK(sigma);
+ for(;;)
+ {
+ gf2m eval_result;
+
+ if(this->m_j_gray == 0)
+ {
+ eval_result = sigma.get_coef(0);
+ }
+ else
+ {
+ eval_result = this->calc_Fxj_j_neq_0(sigma, this->m_j_gray);
+ }
+
+ if(eval_result == 0)
+ {
+ result[root_pos] = this->m_j_gray;
+ root_pos++;
+ }
+
+ if(this->m_j + static_cast<uint32_t>(1) == m_code_length)
+ {
+ break;
+ }
+ this->calc_next_Aij();
+ }
+
+ // side channel / fault attack countermeasure:
+ patch_root_array(result.data(), result.size(), root_pos);
+ return result;
+ }
+
+} // end anonymous namespace
+
+secure_vector<gf2m> find_roots_gf2m_decomp(const polyn_gf2m & polyn, size_t code_length)
+ {
+ gf2m_decomp_rootfind_state state(polyn, code_length);
+ return state.find_roots(polyn);
+ }
+
+} // end namespace Botan
diff --git a/comm/third_party/botan/src/lib/pubkey/mce/gf2m_small_m.cpp b/comm/third_party/botan/src/lib/pubkey/mce/gf2m_small_m.cpp
new file mode 100644
index 0000000000..341b45d034
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/mce/gf2m_small_m.cpp
@@ -0,0 +1,126 @@
+/*
+* (C) Copyright Projet SECRET, INRIA, Rocquencourt
+* (C) Bhaskar Biswas and Nicolas Sendrier
+*
+* (C) 2014 cryptosource GmbH
+* (C) 2014 Falko Strenzke fstrenzke@cryptosource.de
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/gf2m_small_m.h>
+#include <botan/exceptn.h>
+#include <string>
+
+namespace Botan {
+
+#define MAX_EXT_DEG 16
+
+namespace {
+
+gf2m prim_poly[MAX_EXT_DEG + 1] = {
+ 01, /* extension degree 0 (!) never used */
+ 03, /* extension degree 1 (!) never used */
+ 07, /* extension degree 2 */
+ 013, /* extension degree 3 */
+ 023, /* extension degree 4 */
+ 045, /* extension degree 5 */
+ 0103, /* extension degree 6 */
+ 0203, /* extension degree 7 */
+ 0435, /* extension degree 8 */
+ 01041, /* extension degree 9 */
+ 02011, /* extension degree 10 */
+ 04005, /* extension degree 11 */
+ 010123, /* extension degree 12 */
+ 020033, /* extension degree 13 */
+ 042103, /* extension degree 14 */
+ 0100003, /* extension degree 15 */
+};
+
+std::vector<gf2m> gf_exp_table(size_t deg, gf2m prime_poly)
+ {
+ // construct the table gf_exp[i]=alpha^i
+
+ std::vector<gf2m> tab((static_cast<size_t>(1) << deg) + 1);
+
+ tab[0] = 1;
+ for(size_t i = 1; i < tab.size(); ++i)
+ {
+ const gf2m overflow = tab[i-1] >> (deg - 1);
+ tab[i] = (tab[i-1] << 1) ^ (overflow * prime_poly);
+ }
+
+ return tab;
+ }
+
+const std::vector<gf2m>& exp_table(size_t deg)
+ {
+ static std::vector<gf2m> tabs[MAX_EXT_DEG + 1];
+
+ if(deg < 2 || deg > MAX_EXT_DEG)
+ throw Invalid_Argument("GF2m_Field does not support degree " + std::to_string(deg));
+
+ if(tabs[deg].empty())
+ tabs[deg] = gf_exp_table(deg, prim_poly[deg]);
+
+ return tabs[deg];
+ }
+
+std::vector<gf2m> gf_log_table(size_t deg, const std::vector<gf2m>& exp)
+ {
+ std::vector<gf2m> tab(static_cast<size_t>(1) << deg);
+
+ tab[0] = static_cast<gf2m>((static_cast<gf2m>(1) << deg) - 1); // log of 0 is the order by convention
+ for(size_t i = 0; i < tab.size(); ++i)
+ {
+ tab[exp[i]] = static_cast<gf2m>(i);
+ }
+ return tab;
+ }
+
+const std::vector<gf2m>& log_table(size_t deg)
+ {
+ static std::vector<gf2m> tabs[MAX_EXT_DEG + 1];
+
+ if(deg < 2 || deg > MAX_EXT_DEG)
+ throw Invalid_Argument("GF2m_Field does not support degree " + std::to_string(deg));
+
+ if(tabs[deg].empty())
+ tabs[deg] = gf_log_table(deg, exp_table(deg));
+
+ return tabs[deg];
+ }
+
+}
+
+uint32_t encode_gf2m(gf2m to_enc, uint8_t* mem)
+ {
+ mem[0] = to_enc >> 8;
+ mem[1] = to_enc & 0xFF;
+ return sizeof(to_enc);
+ }
+
+gf2m decode_gf2m(const uint8_t* mem)
+ {
+ gf2m result;
+ result = mem[0] << 8;
+ result |= mem[1];
+ return result;
+ }
+
+GF2m_Field::GF2m_Field(size_t extdeg) : m_gf_extension_degree(extdeg),
+ m_gf_multiplicative_order((1 << extdeg) - 1),
+ m_gf_log_table(log_table(m_gf_extension_degree)),
+ m_gf_exp_table(exp_table(m_gf_extension_degree))
+ {
+ }
+
+gf2m GF2m_Field::gf_div(gf2m x, gf2m y) const
+ {
+ const int32_t sub_res = static_cast<int32_t>(gf_log(x) - static_cast<int32_t>(gf_log(y)));
+ const gf2m modq_res = _gf_modq_1(sub_res);
+ const int32_t div_res = static_cast<int32_t>(x) ? static_cast<int32_t>(gf_exp(modq_res)) : 0;
+ return static_cast<gf2m>(div_res);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/pubkey/mce/gf2m_small_m.h b/comm/third_party/botan/src/lib/pubkey/mce/gf2m_small_m.h
new file mode 100644
index 0000000000..ae3eab2264
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/mce/gf2m_small_m.h
@@ -0,0 +1,221 @@
+/*
+ * (C) Copyright Projet SECRET, INRIA, Rocquencourt
+ * (C) Bhaskar Biswas and Nicolas Sendrier
+ *
+ * (C) 2014 cryptosource GmbH
+ * (C) 2014 Falko Strenzke fstrenzke@cryptosource.de
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ *
+ */
+
+#ifndef BOTAN_GF2M_SMALL_M_H_
+#define BOTAN_GF2M_SMALL_M_H_
+
+#include <botan/types.h>
+#include <vector>
+
+BOTAN_FUTURE_INTERNAL_HEADER(gf2m_small_m.h)
+
+namespace Botan {
+
+typedef uint16_t gf2m;
+
+/**
+* GF(2^m) field for m = [2...16]
+*/
+class BOTAN_PUBLIC_API(2,0) GF2m_Field
+ {
+ 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_square(gf2m x) const
+ {
+ return ((x) ? 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);
+ }
+
+ /*
+ naming convention of GF(2^m) field operations:
+ l logarithmic, unreduced
+ r logarithmic, reduced
+ n normal, non-zero
+ z normal, might be zero
+ */
+
+ gf2m gf_mul_lll(gf2m a, gf2m b) const
+ {
+ return (a + b);
+ }
+
+ gf2m gf_mul_rrr(gf2m a, gf2m b) const
+ {
+ return (_gf_modq_1(gf_mul_lll(a, b)));
+ }
+
+ gf2m gf_mul_nrr(gf2m a, gf2m b) const
+ {
+ return (gf_exp(gf_mul_rrr(a, b)));
+ }
+
+ gf2m gf_mul_rrn(gf2m a, gf2m y) const
+ {
+ return _gf_modq_1(gf_mul_lll(a, gf_log(y)));
+ }
+
+ gf2m gf_mul_rnr(gf2m y, gf2m a) const
+ {
+ return gf_mul_rrn(a, y);
+ }
+
+ gf2m gf_mul_lnn(gf2m x, gf2m y) const
+ {
+ return (gf_log(x) + gf_log(y));
+ }
+
+ gf2m gf_mul_rnn(gf2m x, gf2m y) const
+ {
+ return _gf_modq_1(gf_mul_lnn(x, y));
+ }
+
+ gf2m gf_mul_nrn(gf2m a, gf2m y) const
+ {
+ return gf_exp(_gf_modq_1((a) + gf_log(y)));
+ }
+
+ /**
+ * zero operand allowed
+ */
+ gf2m gf_mul_zrz(gf2m a, gf2m y) const
+ {
+ return ( (y == 0) ? 0 : gf_mul_nrn(a, y) );
+ }
+
+ gf2m gf_mul_zzr(gf2m a, gf2m y) const
+ {
+ return gf_mul_zrz(y, a);
+ }
+
+ /**
+ * non-zero operand
+ */
+ 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_div_rnn(gf2m x, gf2m y) const
+ {
+ return _gf_modq_1(gf_log(x) - gf_log(y));
+ }
+
+ gf2m gf_div_rnr(gf2m x, gf2m b) const
+ {
+ return _gf_modq_1(gf_log(x) - b);
+ }
+
+ 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_inv(gf2m x) const
+ {
+ return gf_exp(gf_ord() - gf_log(x));
+ }
+
+ gf2m gf_inv_rn(gf2m x) const
+ {
+ return (gf_ord() - gf_log(x));
+ }
+
+ gf2m gf_square_ln(gf2m x) const
+ {
+ return gf_log(x) << 1;
+ }
+
+ gf2m gf_square_rr(gf2m a) const
+ {
+ return a << 1;
+ }
+
+ gf2m gf_l_from_n(gf2m x) const
+ {
+ return gf_log(x);
+ }
+
+ gf2m gf_div(gf2m x, gf2m y) const;
+
+ gf2m gf_exp(gf2m i) const
+ {
+ return m_gf_exp_table.at(i); /* alpha^i */
+ }
+
+ gf2m gf_log(gf2m i) const
+ {
+ return m_gf_log_table.at(i); /* return i when x=alpha^i */
+ }
+
+ gf2m gf_ord() const
+ {
+ return m_gf_multiplicative_order;
+ }
+
+ size_t get_extension_degree() const
+ {
+ return m_gf_extension_degree;
+ }
+
+ gf2m get_cardinality() const
+ {
+ return static_cast<gf2m>(1 << get_extension_degree());
+ }
+
+ private:
+ gf2m _gf_modq_1(int32_t d) const
+ {
+ /* residual modulo q-1
+ when -q < d < 0, we get (q-1+d)
+ when 0 <= d < q, we get (d)
+ when q <= d < 2q-1, we get (d-q+1)
+ */
+ return static_cast<gf2m>(((d) & gf_ord()) + ((d) >> get_extension_degree()));
+ }
+
+ const size_t m_gf_extension_degree;
+ const gf2m m_gf_multiplicative_order;
+ const std::vector<gf2m>& m_gf_log_table;
+ const std::vector<gf2m>& m_gf_exp_table;
+ };
+
+uint32_t encode_gf2m(gf2m to_enc, uint8_t* mem);
+
+gf2m decode_gf2m(const uint8_t* mem);
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/pubkey/mce/goppa_code.cpp b/comm/third_party/botan/src/lib/pubkey/mce/goppa_code.cpp
new file mode 100644
index 0000000000..f26716fabd
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/mce/goppa_code.cpp
@@ -0,0 +1,234 @@
+/*
+ * (C) Copyright Projet SECRET, INRIA, Rocquencourt
+ * (C) Bhaskar Biswas and Nicolas Sendrier
+ *
+ * (C) 2014 cryptosource GmbH
+ * (C) 2014 Falko Strenzke fstrenzke@cryptosource.de
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ *
+ */
+
+#include <botan/internal/mce_internal.h>
+#include <botan/internal/code_based_util.h>
+
+namespace Botan {
+
+namespace {
+
+void matrix_arr_mul(std::vector<uint32_t> matrix,
+ size_t numo_rows,
+ size_t words_per_row,
+ const uint8_t input_vec[],
+ 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)
+ {
+ for(size_t i = 0; i < output_vec_len; i++)
+ {
+ output_vec[i] ^= matrix[j * (words_per_row) + i];
+ }
+ }
+ }
+ }
+
+/**
+* returns the error vector to the syndrome
+*/
+secure_vector<gf2m> goppa_decode(const polyn_gf2m & syndrom_polyn,
+ const polyn_gf2m & g,
+ const std::vector<polyn_gf2m> & sqrtmod,
+ const std::vector<gf2m> & Linv)
+ {
+ const size_t code_length = Linv.size();
+ gf2m a;
+ uint32_t t = g.get_degree();
+
+ std::shared_ptr<GF2m_Field> sp_field = g.get_sp_field();
+
+ std::pair<polyn_gf2m, polyn_gf2m> h_aux = polyn_gf2m::eea_with_coefficients( syndrom_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);
+ for(int i = 0; i <= h.get_degree(); ++i)
+ {
+ h.set_coef(i,sp_field->gf_mul_zrz(log_a,h.get_coef(i)));
+ }
+
+ // compute h(z) += z
+ h.add_to_coef( 1, 1);
+ // compute S square root of h (using sqrtmod)
+ polyn_gf2m S(t - 1, g.get_sp_field());
+
+ for(uint32_t i=0;i<t;i++)
+ {
+ a = sp_field->gf_sqrt(h.get_coef(i));
+
+ if(i & 1)
+ {
+ for(uint32_t j=0;j<t;j++)
+ {
+ S.add_to_coef( j, sp_field->gf_mul(a, sqrtmod[i/2].get_coef(j)));
+ }
+ }
+ else
+ {
+ S.add_to_coef( i/2, a);
+ }
+ } /* end for loop (i) */
+
+
+ S.get_degree();
+
+ std::pair<polyn_gf2m, polyn_gf2m> v_u = polyn_gf2m::eea_with_coefficients(S, g, t/2+1);
+ polyn_gf2m & u = v_u.second;
+ polyn_gf2m & v = v_u.first;
+
+ // sigma = u^2+z*v^2
+ polyn_gf2m sigma ( t , g.get_sp_field());
+
+ const int u_deg = u.get_degree();
+ BOTAN_ASSERT(u_deg >= 0, "Valid degree");
+ for(int i = 0; i <= u_deg; ++i)
+ {
+ sigma.set_coef(2*i, sp_field->gf_square(u.get_coef(i)));
+ }
+
+ const int v_deg = v.get_degree();
+ BOTAN_ASSERT(v_deg >= 0, "Valid degree");
+ for(int i = 0; i <= v_deg; ++i)
+ {
+ sigma.set_coef(2*i+1, sp_field->gf_square(v.get_coef(i)));
+ }
+
+ secure_vector<gf2m> res = find_roots_gf2m_decomp(sigma, code_length);
+ size_t d = res.size();
+
+ secure_vector<gf2m> result(d);
+ for(uint32_t i = 0; i < d; ++i)
+ {
+ gf2m current = res[i];
+
+ gf2m tmp;
+ tmp = gray_to_lex(current);
+ /// XXX double assignment, possible bug?
+ if(tmp >= code_length) /* invalid root */
+ {
+ result[i] = static_cast<gf2m>(i);
+ }
+ result[i] = Linv[tmp];
+ }
+
+ return result;
+ }
+}
+
+void mceliece_decrypt(secure_vector<uint8_t>& plaintext_out,
+ secure_vector<uint8_t>& error_mask_out,
+ const secure_vector<uint8_t>& ciphertext,
+ const McEliece_PrivateKey& key)
+ {
+ mceliece_decrypt(plaintext_out, error_mask_out, ciphertext.data(), ciphertext.size(), key);
+ }
+
+void mceliece_decrypt(
+ secure_vector<uint8_t>& plaintext,
+ secure_vector<uint8_t> & error_mask,
+ const uint8_t ciphertext[],
+ size_t ciphertext_len,
+ const McEliece_PrivateKey & key)
+ {
+ secure_vector<gf2m> error_pos;
+ plaintext = mceliece_decrypt(error_pos, ciphertext, ciphertext_len, key);
+
+ const size_t code_length = key.get_code_length();
+ secure_vector<uint8_t> result((code_length+7)/8);
+ for(auto&& pos : error_pos)
+ {
+ if(pos > code_length)
+ {
+ throw Invalid_Argument("error position larger than code size");
+ }
+ result[pos / 8] |= (1 << (pos % 8));
+ }
+
+ error_mask = result;
+ }
+
+/**
+* @p p_err_pos_len must point to the available length of @p error_pos on input, the
+* function will set it to the actual number of errors returned in the @p error_pos
+* array */
+secure_vector<uint8_t> mceliece_decrypt(
+ secure_vector<gf2m> & error_pos,
+ const uint8_t *ciphertext, size_t ciphertext_len,
+ const McEliece_PrivateKey & key)
+ {
+
+ const size_t dimension = key.get_dimension();
+ const size_t codimension = key.get_codimension();
+ const uint32_t t = key.get_goppa_polyn().get_degree();
+ polyn_gf2m syndrome_polyn(key.get_goppa_polyn().get_sp_field()); // init as zero polyn
+ const unsigned unused_pt_bits = dimension % 8;
+ const uint8_t unused_pt_bits_mask = (1 << unused_pt_bits) - 1;
+
+ if(ciphertext_len != (key.get_code_length()+7)/8)
+ {
+ throw Invalid_Argument("wrong size of McEliece ciphertext");
+ }
+ const size_t cleartext_len = (key.get_message_word_bit_length()+7)/8;
+
+ if(cleartext_len != bit_size_to_byte_size(dimension))
+ {
+ throw Invalid_Argument("mce-decryption: wrong length of cleartext buffer");
+ }
+
+ secure_vector<uint32_t> syndrome_vec(bit_size_to_32bit_size(codimension));
+ matrix_arr_mul(key.get_H_coeffs(),
+ key.get_code_length(),
+ bit_size_to_32bit_size(codimension),
+ ciphertext,
+ syndrome_vec.data(), syndrome_vec.size());
+
+ secure_vector<uint8_t> syndrome_byte_vec(bit_size_to_byte_size(codimension));
+ const size_t syndrome_byte_vec_size = syndrome_byte_vec.size();
+ for(size_t i = 0; i < syndrome_byte_vec_size; i++)
+ {
+ syndrome_byte_vec[i] = static_cast<uint8_t>(syndrome_vec[i/4] >> (8 * (i % 4)));
+ }
+
+ syndrome_polyn = polyn_gf2m(t-1, syndrome_byte_vec.data(), bit_size_to_byte_size(codimension), key.get_goppa_polyn().get_sp_field());
+
+ syndrome_polyn.get_degree();
+ error_pos = goppa_decode(syndrome_polyn, key.get_goppa_polyn(), key.get_sqrtmod(), key.get_Linv());
+
+ const size_t nb_err = error_pos.size();
+
+ secure_vector<uint8_t> cleartext(cleartext_len);
+ copy_mem(cleartext.data(), ciphertext, cleartext_len);
+
+ for(size_t i = 0; i < nb_err; i++)
+ {
+ gf2m current = error_pos[i];
+
+ if(current >= cleartext_len * 8)
+ {
+ // an invalid position, this shouldn't happen
+ continue;
+ }
+ cleartext[current / 8] ^= (1 << (current % 8));
+ }
+
+ if(unused_pt_bits)
+ {
+ cleartext[cleartext_len - 1] &= unused_pt_bits_mask;
+ }
+
+ return cleartext;
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/pubkey/mce/info.txt b/comm/third_party/botan/src/lib/pubkey/mce/info.txt
new file mode 100644
index 0000000000..becf616b3e
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/mce/info.txt
@@ -0,0 +1,18 @@
+<defines>
+MCELIECE -> 20150922
+</defines>
+
+<header:public>
+mceliece.h
+polyn_gf2m.h
+gf2m_small_m.h
+</header:public>
+
+<header:internal>
+code_based_util.h
+mce_internal.h
+</header:internal>
+
+<requires>
+sha2_64
+</requires>
diff --git a/comm/third_party/botan/src/lib/pubkey/mce/mce_internal.h b/comm/third_party/botan/src/lib/pubkey/mce/mce_internal.h
new file mode 100644
index 0000000000..7059f4e2ec
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/mce/mce_internal.h
@@ -0,0 +1,53 @@
+/*
+ * (C) Copyright Projet SECRET, INRIA, Rocquencourt
+ * (C) Bhaskar Biswas and Nicolas Sendrier
+ *
+ * (C) 2014 cryptosource GmbH
+ * (C) 2014 Falko Strenzke fstrenzke@cryptosource.de
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ *
+ */
+
+#ifndef BOTAN_MCELIECE_INTERNAL_H_
+#define BOTAN_MCELIECE_INTERNAL_H_
+
+#include <botan/secmem.h>
+#include <botan/types.h>
+#include <botan/pk_ops.h>
+#include <botan/mceliece.h>
+#include <botan/polyn_gf2m.h>
+
+namespace Botan {
+
+void mceliece_decrypt(secure_vector<uint8_t>& plaintext_out,
+ secure_vector<uint8_t>& error_mask_out,
+ const uint8_t ciphertext[],
+ size_t ciphertext_len,
+ const McEliece_PrivateKey& key);
+
+void mceliece_decrypt(secure_vector<uint8_t>& plaintext_out,
+ secure_vector<uint8_t>& error_mask_out,
+ const secure_vector<uint8_t>& ciphertext,
+ const McEliece_PrivateKey& key);
+
+secure_vector<uint8_t> mceliece_decrypt(
+ secure_vector<gf2m> & error_pos,
+ const uint8_t *ciphertext, size_t ciphertext_len,
+ const McEliece_PrivateKey & key);
+
+void mceliece_encrypt(secure_vector<uint8_t>& ciphertext_out,
+ secure_vector<uint8_t>& error_mask_out,
+ const secure_vector<uint8_t>& plaintext,
+ const McEliece_PublicKey& key,
+ RandomNumberGenerator& rng);
+
+McEliece_PrivateKey generate_mceliece_key(RandomNumberGenerator &rng,
+ size_t ext_deg,
+ size_t code_length,
+ size_t t);
+
+}
+
+
+#endif
diff --git a/comm/third_party/botan/src/lib/pubkey/mce/mce_workfactor.cpp b/comm/third_party/botan/src/lib/pubkey/mce/mce_workfactor.cpp
new file mode 100644
index 0000000000..ce38781c8b
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/mce/mce_workfactor.cpp
@@ -0,0 +1,112 @@
+/*
+ * (C) Copyright Projet SECRET, INRIA, Rocquencourt
+ * (C) Bhaskar Biswas and Nicolas Sendrier
+ * (C) 2014 Jack Lloyd
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ *
+ */
+
+#include <botan/mceliece.h>
+#include <botan/internal/bit_ops.h>
+#include <cmath>
+
+namespace Botan {
+
+namespace {
+
+double binomial(size_t n, size_t k)
+ {
+ double x = 1;
+
+ for(size_t i = 0; i != k; ++i)
+ {
+ x *= n - i;
+ x /= k -i;
+ }
+
+ return x;
+ }
+
+double log_binomial(size_t n, size_t k)
+ {
+ double x = 0;
+
+ for(size_t i = 0; i != k; ++i)
+ {
+ x += std::log(n - i);
+ x -= std::log(k - i);
+ }
+
+ return x / std::log(2);
+ }
+
+double nb_iter(size_t n, size_t k, size_t w, size_t p, size_t l)
+ {
+ double x = 2 * log_binomial(k / 2, p);
+ x += log_binomial(n - k - l, w - 2 * p);
+ x = log_binomial(n, w) - x;
+ return x;
+ }
+
+double cout_iter(size_t n, size_t k, size_t p, size_t l)
+ {
+ double x = binomial(k / 2, p);
+ const size_t i = static_cast<size_t>(std::log(x) / std::log(2));
+ double res = 2 * p * (n - k - l) * std::ldexp(x * x, -static_cast<int>(l));
+
+ // x <- binomial(k/2,p)*2*(2*l+log[2](binomial(k/2,p)))
+ x *= 2 * (2 * l + i);
+
+ // res <- k*(n-k)/2 +
+ // binomial(k/2,p)*2*(2*l+log[2](binomial(k/2,p))) +
+ // 2*p*(n-k-l)*binomial(k/2,p)^2/2^l
+ res += x + k * ((n - k) / 2.0);
+
+ return std::log(res) / std::log(2); // convert to bits
+ }
+
+double cout_total(size_t n, size_t k, size_t w, size_t p, size_t l)
+ {
+ return nb_iter(n, k, w, p, l) + cout_iter(n, k, p, l);
+ }
+
+double best_wf(size_t n, size_t k, size_t w, size_t p)
+ {
+ if(p >= k / 2)
+ return -1;
+
+ double min = cout_total(n, k, w, p, 0);
+
+ for(size_t l = 1; l < n - k; ++l)
+ {
+ const double lwf = cout_total(n, k, w, p, l);
+ if(lwf < min)
+ min = lwf;
+ else
+ break;
+ }
+
+ return min;
+ }
+
+}
+
+size_t mceliece_work_factor(size_t n, size_t t)
+ {
+ const size_t k = n - ceil_log2(n) * t;
+
+ 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);
+ if(lwf < 0)
+ break;
+
+ min = std::min(min, lwf);
+ }
+
+ return static_cast<size_t>(min);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/pubkey/mce/mceliece.cpp b/comm/third_party/botan/src/lib/pubkey/mce/mceliece.cpp
new file mode 100644
index 0000000000..c5fe74b058
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/mce/mceliece.cpp
@@ -0,0 +1,139 @@
+/*
+ * (C) Copyright Projet SECRET, INRIA, Rocquencourt
+ * (C) Bhaskar Biswas and Nicolas Sendrier
+ *
+ * (C) 2014 cryptosource GmbH
+ * (C) 2014 Falko Strenzke fstrenzke@cryptosource.de
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ *
+ */
+
+#include <botan/internal/mce_internal.h>
+#include <botan/mceliece.h>
+#include <botan/internal/code_based_util.h>
+#include <botan/internal/bit_ops.h>
+
+namespace Botan {
+
+namespace {
+
+secure_vector<uint8_t> concat_vectors(const secure_vector<uint8_t>& a,
+ const secure_vector<uint8_t>& b,
+ size_t dimension,
+ size_t codimension)
+ {
+ secure_vector<uint8_t> x(bit_size_to_byte_size(dimension) + bit_size_to_byte_size(codimension));
+
+ const size_t final_bits = dimension % 8;
+
+ 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[dim_bytes], b.data(), bit_size_to_byte_size(codimension));
+ }
+ else
+ {
+ copy_mem(&x[0], a.data(), (dimension / 8));
+ size_t l = dimension / 8;
+ x[l] = static_cast<uint8_t>(a[l] & ((1 << final_bits) - 1));
+
+ for(size_t k = 0; k < codimension / 8; ++k)
+ {
+ x[l] ^= static_cast<uint8_t>(b[k] << final_bits);
+ ++l;
+ x[l] = static_cast<uint8_t>(b[k] >> (8 - final_bits));
+ }
+ x[l] ^= static_cast<uint8_t>(b[codimension/8] << final_bits);
+ }
+
+ return x;
+ }
+
+secure_vector<uint8_t> mult_by_pubkey(const secure_vector<uint8_t>& cleartext,
+ std::vector<uint8_t> const& public_matrix,
+ size_t code_length, size_t t)
+ {
+ const size_t ext_deg = ceil_log2(code_length);
+ const size_t codimension = ext_deg * t;
+ const size_t dimension = code_length - codimension;
+ secure_vector<uint8_t> cR(bit_size_to_32bit_size(codimension) * sizeof(uint32_t));
+
+ const uint8_t* pt = public_matrix.data();
+
+ for(size_t i = 0; i < dimension / 8; ++i)
+ {
+ for(size_t j = 0; j < 8; ++j)
+ {
+ if(cleartext[i] & (1 << j))
+ {
+ xor_buf(cR.data(), pt, cR.size());
+ }
+ pt += cR.size();
+ }
+ }
+
+ for(size_t i = 0; i < dimension % 8 ; ++i)
+ {
+ if(cleartext[dimension/8] & (1 << i))
+ {
+ xor_buf(cR.data(), pt, cR.size());
+ }
+ pt += cR.size();
+ }
+
+ secure_vector<uint8_t> ciphertext = concat_vectors(cleartext, cR, dimension, codimension);
+ ciphertext.resize((code_length+7)/8);
+ return ciphertext;
+ }
+
+secure_vector<uint8_t> create_random_error_vector(size_t code_length,
+ size_t error_weight,
+ RandomNumberGenerator& rng)
+ {
+ secure_vector<uint8_t> result((code_length+7)/8);
+
+ size_t bits_set = 0;
+
+ while(bits_set < error_weight)
+ {
+ gf2m x = random_code_element(static_cast<uint16_t>(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)
+ continue; // already set this bit
+
+ result[byte_pos] |= mask;
+ bits_set++;
+ }
+
+ return result;
+ }
+
+}
+
+void mceliece_encrypt(secure_vector<uint8_t>& ciphertext_out,
+ secure_vector<uint8_t>& error_mask_out,
+ const secure_vector<uint8_t>& plaintext,
+ const McEliece_PublicKey& key,
+ RandomNumberGenerator& rng)
+ {
+ const uint16_t code_length = static_cast<uint16_t>(key.get_code_length());
+
+ secure_vector<uint8_t> error_mask = create_random_error_vector(code_length, key.get_t(), rng);
+
+ secure_vector<uint8_t> ciphertext = mult_by_pubkey(plaintext, key.get_public_matrix(),
+ key.get_code_length(), key.get_t());
+
+ ciphertext ^= error_mask;
+
+ ciphertext_out.swap(ciphertext);
+ error_mask_out.swap(error_mask);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/pubkey/mce/mceliece.h b/comm/third_party/botan/src/lib/pubkey/mce/mceliece.h
new file mode 100644
index 0000000000..ba044ef7dc
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/mce/mceliece.h
@@ -0,0 +1,141 @@
+/*
+ * (C) Copyright Projet SECRET, INRIA, Rocquencourt
+ * (C) Bhaskar Biswas and Nicolas Sendrier
+ *
+ * (C) 2014 cryptosource GmbH
+ * (C) 2014 Falko Strenzke fstrenzke@cryptosource.de
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ *
+ */
+
+#ifndef BOTAN_MCELIECE_KEY_H_
+#define BOTAN_MCELIECE_KEY_H_
+
+#include <botan/pk_keys.h>
+#include <botan/polyn_gf2m.h>
+
+namespace Botan {
+
+typedef uint16_t gf2m;
+
+class polyn_gf2m;
+
+class BOTAN_PUBLIC_API(2,0) McEliece_PublicKey : public virtual Public_Key
+ {
+ public:
+ explicit McEliece_PublicKey(const std::vector<uint8_t>& key_bits);
+
+ McEliece_PublicKey(const std::vector<uint8_t>& pub_matrix, size_t t, size_t the_code_length) :
+ m_public_matrix(pub_matrix),
+ m_t(t),
+ m_code_length(the_code_length){}
+
+ McEliece_PublicKey(const McEliece_PublicKey& other) = default;
+ McEliece_PublicKey& operator=(const McEliece_PublicKey& other) = default;
+ virtual ~McEliece_PublicKey()= default;
+
+ secure_vector<uint8_t> random_plaintext_element(RandomNumberGenerator& rng) const;
+
+ std::string algo_name() const override { return "McEliece"; }
+
+ AlgorithmIdentifier algorithm_identifier() const override;
+
+ size_t key_length() const override;
+ size_t estimated_strength() const override;
+
+ std::vector<uint8_t> public_key_bits() const override;
+
+ bool check_key(RandomNumberGenerator&, bool) const override
+ { return true; }
+
+ size_t get_t() const { return m_t; }
+ size_t get_code_length() const { return m_code_length; }
+ size_t get_message_word_bit_length() const;
+ const std::vector<uint8_t>& get_public_matrix() const { return m_public_matrix; }
+
+ bool operator==(const McEliece_PublicKey& other) const;
+ bool operator!=(const McEliece_PublicKey& other) const { return !(*this == other); }
+
+ std::unique_ptr<PK_Ops::KEM_Encryption>
+ create_kem_encryption_op(RandomNumberGenerator& rng,
+ const std::string& params,
+ const std::string& provider) const override;
+
+ protected:
+ McEliece_PublicKey() : m_t(0), m_code_length(0) {}
+
+ std::vector<uint8_t> m_public_matrix;
+ size_t m_t;
+ size_t m_code_length;
+ };
+
+class BOTAN_PUBLIC_API(2,0) McEliece_PrivateKey final : public virtual McEliece_PublicKey,
+ public virtual Private_Key
+ {
+ public:
+
+ /**
+ Generate a McEliece key pair
+
+ Suggested parameters for a given security level (SL)
+
+ SL=80 n=1632 t=33 - 59 KB pubkey 140 KB privkey
+ SL=107 n=2480 t=45 - 128 KB pubkey 300 KB privkey
+ SL=128 n=2960 t=57 - 195 KB pubkey 459 KB privkey
+ SL=147 n=3408 t=67 - 265 KB pubkey 622 KB privkey
+ SL=191 n=4624 t=95 - 516 KB pubkey 1234 KB privkey
+ SL=256 n=6624 t=115 - 942 KB pubkey 2184 KB privkey
+ */
+ McEliece_PrivateKey(RandomNumberGenerator& rng, size_t code_length, size_t t);
+
+ explicit McEliece_PrivateKey(const secure_vector<uint8_t>& key_bits);
+
+ McEliece_PrivateKey(polyn_gf2m const& goppa_polyn,
+ std::vector<uint32_t> const& parity_check_matrix_coeffs,
+ std::vector<polyn_gf2m> const& square_root_matrix,
+ std::vector<gf2m> const& inverse_support,
+ std::vector<uint8_t> const& public_matrix );
+
+ ~McEliece_PrivateKey();
+
+ bool check_key(RandomNumberGenerator& rng, bool strong) const override;
+
+ polyn_gf2m const& get_goppa_polyn() const;
+ std::vector<uint32_t> const& get_H_coeffs() const { return m_coeffs; }
+ std::vector<gf2m> const& get_Linv() const { return m_Linv; }
+ std::vector<polyn_gf2m> const& get_sqrtmod() const { return m_sqrtmod; }
+
+ inline size_t get_dimension() const { return m_dimension; }
+
+ inline size_t get_codimension() const { return m_codimension; }
+
+ secure_vector<uint8_t> private_key_bits() const override;
+
+ bool operator==(const McEliece_PrivateKey & other) const;
+
+ bool operator!=(const McEliece_PrivateKey& other) const { return !(*this == other); }
+
+ std::unique_ptr<PK_Ops::KEM_Decryption>
+ create_kem_decryption_op(RandomNumberGenerator& rng,
+ const std::string& params,
+ const std::string& provider) const override;
+ private:
+ std::vector<polyn_gf2m> m_g; // single element
+ std::vector<polyn_gf2m> m_sqrtmod;
+ std::vector<gf2m> m_Linv;
+ std::vector<uint32_t> m_coeffs;
+
+ size_t m_codimension;
+ size_t m_dimension;
+ };
+
+/**
+* Estimate work factor for McEliece
+* @return estimated security level for these key parameters
+*/
+BOTAN_PUBLIC_API(2,0) size_t mceliece_work_factor(size_t code_size, size_t t);
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/pubkey/mce/mceliece_key.cpp b/comm/third_party/botan/src/lib/pubkey/mce/mceliece_key.cpp
new file mode 100644
index 0000000000..283421be40
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/mce/mceliece_key.cpp
@@ -0,0 +1,386 @@
+/*
+ * (C) Copyright Projet SECRET, INRIA, Rocquencourt
+ * (C) Bhaskar Biswas and Nicolas Sendrier
+ *
+ * (C) 2014 cryptosource GmbH
+ * (C) 2014 Falko Strenzke fstrenzke@cryptosource.de
+ * (C) 2015 Jack Lloyd
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ *
+ */
+
+#include <botan/mceliece.h>
+#include <botan/polyn_gf2m.h>
+#include <botan/internal/mce_internal.h>
+#include <botan/internal/bit_ops.h>
+#include <botan/internal/code_based_util.h>
+#include <botan/internal/pk_ops_impl.h>
+#include <botan/loadstor.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
+#include <botan/rng.h>
+
+namespace Botan {
+
+McEliece_PrivateKey::McEliece_PrivateKey(polyn_gf2m const& goppa_polyn,
+ std::vector<uint32_t> const& parity_check_matrix_coeffs,
+ std::vector<polyn_gf2m> const& square_root_matrix,
+ std::vector<gf2m> const& inverse_support,
+ std::vector<uint8_t> const& public_matrix) :
+ McEliece_PublicKey(public_matrix, goppa_polyn.get_degree(), inverse_support.size()),
+ m_g{goppa_polyn},
+ m_sqrtmod(square_root_matrix),
+ m_Linv(inverse_support),
+ m_coeffs(parity_check_matrix_coeffs),
+ m_codimension(static_cast<size_t>(ceil_log2(inverse_support.size())) * goppa_polyn.get_degree()),
+ m_dimension(inverse_support.size() - m_codimension)
+ {
+ }
+
+McEliece_PrivateKey::McEliece_PrivateKey(RandomNumberGenerator& rng, size_t code_length, size_t t)
+ {
+ uint32_t ext_deg = ceil_log2(code_length);
+ *this = generate_mceliece_key(rng, ext_deg, code_length, t);
+ }
+
+McEliece_PrivateKey::~McEliece_PrivateKey() = default;
+
+const polyn_gf2m& McEliece_PrivateKey::get_goppa_polyn() const
+ {
+ return m_g[0];
+ }
+
+size_t McEliece_PublicKey::get_message_word_bit_length() const
+ {
+ size_t codimension = ceil_log2(m_code_length) * m_t;
+ return m_code_length - codimension;
+ }
+
+secure_vector<uint8_t> McEliece_PublicKey::random_plaintext_element(RandomNumberGenerator& rng) const
+ {
+ const size_t bits = get_message_word_bit_length();
+
+ secure_vector<uint8_t> plaintext((bits+7)/8);
+ rng.randomize(plaintext.data(), plaintext.size());
+
+ // unset unused bits in the last plaintext byte
+ if(uint32_t used = bits % 8)
+ {
+ const uint8_t mask = (1 << used) - 1;
+ plaintext[plaintext.size() - 1] &= mask;
+ }
+
+ return plaintext;
+ }
+
+AlgorithmIdentifier McEliece_PublicKey::algorithm_identifier() const
+ {
+ return AlgorithmIdentifier(get_oid(), AlgorithmIdentifier::USE_EMPTY_PARAM);
+ }
+
+std::vector<uint8_t> McEliece_PublicKey::public_key_bits() const
+ {
+ std::vector<uint8_t> output;
+ DER_Encoder(output)
+ .start_cons(SEQUENCE)
+ .start_cons(SEQUENCE)
+ .encode(static_cast<size_t>(get_code_length()))
+ .encode(static_cast<size_t>(get_t()))
+ .end_cons()
+ .encode(m_public_matrix, OCTET_STRING)
+ .end_cons();
+ return output;
+ }
+
+size_t McEliece_PublicKey::key_length() const
+ {
+ return m_code_length;
+ }
+
+size_t McEliece_PublicKey::estimated_strength() const
+ {
+ return mceliece_work_factor(m_code_length, m_t);
+ }
+
+McEliece_PublicKey::McEliece_PublicKey(const std::vector<uint8_t>& key_bits)
+ {
+ BER_Decoder dec(key_bits);
+ size_t n;
+ size_t t;
+ dec.start_cons(SEQUENCE)
+ .start_cons(SEQUENCE)
+ .decode(n)
+ .decode(t)
+ .end_cons()
+ .decode(m_public_matrix, OCTET_STRING)
+ .end_cons();
+ m_t = t;
+ m_code_length = n;
+ }
+
+secure_vector<uint8_t> McEliece_PrivateKey::private_key_bits() const
+ {
+ DER_Encoder enc;
+ enc.start_cons(SEQUENCE)
+ .start_cons(SEQUENCE)
+ .encode(static_cast<size_t>(get_code_length()))
+ .encode(static_cast<size_t>(get_t()))
+ .end_cons()
+ .encode(m_public_matrix, OCTET_STRING)
+ .encode(m_g[0].encode(), OCTET_STRING); // g as octet string
+ enc.start_cons(SEQUENCE);
+ for(size_t i = 0; i < m_sqrtmod.size(); i++)
+ {
+ enc.encode(m_sqrtmod[i].encode(), OCTET_STRING);
+ }
+ enc.end_cons();
+ secure_vector<uint8_t> enc_support;
+
+ for(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, OCTET_STRING);
+ secure_vector<uint8_t> enc_H;
+ for(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));
+ enc_H.push_back(get_byte(3, coef));
+ }
+ enc.encode(enc_H, OCTET_STRING);
+ enc.end_cons();
+ return enc.get_contents();
+ }
+
+bool McEliece_PrivateKey::check_key(RandomNumberGenerator& rng, bool) const
+ {
+ const secure_vector<uint8_t> plaintext = this->random_plaintext_element(rng);
+
+ secure_vector<uint8_t> ciphertext;
+ secure_vector<uint8_t> errors;
+ mceliece_encrypt(ciphertext, errors, plaintext, *this, rng);
+
+ secure_vector<uint8_t> plaintext_out;
+ secure_vector<uint8_t> errors_out;
+ mceliece_decrypt(plaintext_out, errors_out, ciphertext, *this);
+
+ if(errors != errors_out || plaintext != plaintext_out)
+ return false;
+
+ return true;
+ }
+
+McEliece_PrivateKey::McEliece_PrivateKey(const secure_vector<uint8_t>& key_bits)
+ {
+ size_t n, t;
+ secure_vector<uint8_t> enc_g;
+ BER_Decoder dec_base(key_bits);
+ BER_Decoder dec = dec_base.start_cons(SEQUENCE)
+ .start_cons(SEQUENCE)
+ .decode(n)
+ .decode(t)
+ .end_cons()
+ .decode(m_public_matrix, OCTET_STRING)
+ .decode(enc_g, OCTET_STRING);
+
+ if(t == 0 || n == 0)
+ throw Decoding_Error("invalid McEliece parameters");
+
+ uint32_t ext_deg = ceil_log2(n);
+ m_code_length = n;
+ m_t = t;
+ m_codimension = (ext_deg * t);
+ m_dimension = (n - m_codimension);
+
+ std::shared_ptr<GF2m_Field> sp_field(new GF2m_Field(ext_deg));
+ m_g = { polyn_gf2m(enc_g, sp_field) };
+ if(m_g[0].get_degree() != static_cast<int>(t))
+ {
+ throw Decoding_Error("degree of decoded Goppa polynomial is incorrect");
+ }
+ BER_Decoder dec2 = dec.start_cons(SEQUENCE);
+ for(uint32_t i = 0; i < t/2; i++)
+ {
+ secure_vector<uint8_t> sqrt_enc;
+ dec2.decode(sqrt_enc, OCTET_STRING);
+ while(sqrt_enc.size() < (t*2))
+ {
+ // ensure that the length is always t
+ sqrt_enc.push_back(0);
+ sqrt_enc.push_back(0);
+ }
+ if(sqrt_enc.size() != t*2)
+ {
+ throw Decoding_Error("length of square root polynomial entry is too large");
+ }
+ m_sqrtmod.push_back(polyn_gf2m(sqrt_enc, sp_field));
+ }
+ secure_vector<uint8_t> enc_support;
+ BER_Decoder dec3 = dec2.end_cons()
+ .decode(enc_support, OCTET_STRING);
+ if(enc_support.size() % 2)
+ {
+ 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];
+ m_Linv.push_back(el);
+ }
+ secure_vector<uint8_t> enc_H;
+ dec3.decode(enc_H, OCTET_STRING)
+ .end_cons();
+ if(enc_H.size() % 4)
+ {
+ 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)
+ {
+ throw Decoding_Error("encoded parity check matrix has wrong length");
+ }
+
+ 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];
+ m_coeffs.push_back(coeff);
+ }
+
+ }
+
+bool McEliece_PrivateKey::operator==(const McEliece_PrivateKey & other) const
+ {
+ if(*static_cast<const McEliece_PublicKey*>(this) != *static_cast<const McEliece_PublicKey*>(&other))
+ {
+ return false;
+ }
+ if(m_g != other.m_g)
+ {
+ return false;
+ }
+
+ if( m_sqrtmod != other.m_sqrtmod)
+ {
+ return false;
+ }
+ if( m_Linv != other.m_Linv)
+ {
+ return false;
+ }
+ if( m_coeffs != other.m_coeffs)
+ {
+ return false;
+ }
+
+ if(m_codimension != other.m_codimension || m_dimension != other.m_dimension)
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+bool McEliece_PublicKey::operator==(const McEliece_PublicKey& other) const
+ {
+ if(m_public_matrix != other.m_public_matrix)
+ {
+ return false;
+ }
+ if(m_t != other.m_t)
+ {
+ return false;
+ }
+ if( m_code_length != other.m_code_length)
+ {
+ return false;
+ }
+ return true;
+ }
+
+namespace {
+
+class MCE_KEM_Encryptor final : public PK_Ops::KEM_Encryption_with_KDF
+ {
+ public:
+
+ MCE_KEM_Encryptor(const McEliece_PublicKey& key,
+ const std::string& kdf) :
+ KEM_Encryption_with_KDF(kdf), m_key(key) {}
+
+ private:
+ void raw_kem_encrypt(secure_vector<uint8_t>& out_encapsulated_key,
+ secure_vector<uint8_t>& raw_shared_key,
+ Botan::RandomNumberGenerator& rng) override
+ {
+ secure_vector<uint8_t> plaintext = m_key.random_plaintext_element(rng);
+
+ secure_vector<uint8_t> ciphertext, error_mask;
+ mceliece_encrypt(ciphertext, error_mask, plaintext, m_key, rng);
+
+ raw_shared_key.clear();
+ raw_shared_key += plaintext;
+ raw_shared_key += error_mask;
+
+ out_encapsulated_key.swap(ciphertext);
+ }
+
+ const McEliece_PublicKey& m_key;
+ };
+
+class MCE_KEM_Decryptor final : public PK_Ops::KEM_Decryption_with_KDF
+ {
+ public:
+
+ MCE_KEM_Decryptor(const McEliece_PrivateKey& key,
+ const std::string& kdf) :
+ KEM_Decryption_with_KDF(kdf), m_key(key) {}
+
+ private:
+ secure_vector<uint8_t>
+ raw_kem_decrypt(const uint8_t encap_key[], size_t len) override
+ {
+ secure_vector<uint8_t> plaintext, error_mask;
+ mceliece_decrypt(plaintext, error_mask, encap_key, len, m_key);
+
+ secure_vector<uint8_t> output;
+ output.reserve(plaintext.size() + error_mask.size());
+ output.insert(output.end(), plaintext.begin(), plaintext.end());
+ output.insert(output.end(), error_mask.begin(), error_mask.end());
+ return output;
+ }
+
+ const McEliece_PrivateKey& m_key;
+ };
+
+}
+
+std::unique_ptr<PK_Ops::KEM_Encryption>
+McEliece_PublicKey::create_kem_encryption_op(RandomNumberGenerator& /*rng*/,
+ const std::string& params,
+ const std::string& provider) const
+ {
+ if(provider == "base" || provider.empty())
+ return std::unique_ptr<PK_Ops::KEM_Encryption>(new MCE_KEM_Encryptor(*this, params));
+ throw Provider_Not_Found(algo_name(), provider);
+ }
+
+std::unique_ptr<PK_Ops::KEM_Decryption>
+McEliece_PrivateKey::create_kem_decryption_op(RandomNumberGenerator& /*rng*/,
+ const std::string& params,
+ const std::string& provider) const
+ {
+ if(provider == "base" || provider.empty())
+ return std::unique_ptr<PK_Ops::KEM_Decryption>(new MCE_KEM_Decryptor(*this, params));
+ throw Provider_Not_Found(algo_name(), provider);
+ }
+
+}
+
+
diff --git a/comm/third_party/botan/src/lib/pubkey/mce/polyn_gf2m.cpp b/comm/third_party/botan/src/lib/pubkey/mce/polyn_gf2m.cpp
new file mode 100644
index 0000000000..592ab72625
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/mce/polyn_gf2m.cpp
@@ -0,0 +1,806 @@
+/*
+ * (C) Copyright Projet SECRET, INRIA, Rocquencourt
+ * (C) Bhaskar Biswas and Nicolas Sendrier
+ *
+ * (C) 2014 cryptosource GmbH
+ * (C) 2014 Falko Strenzke fstrenzke@cryptosource.de
+ * (C) 2015 Jack Lloyd
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ *
+ */
+
+#include <botan/polyn_gf2m.h>
+#include <botan/internal/code_based_util.h>
+#include <botan/internal/bit_ops.h>
+#include <botan/rng.h>
+#include <botan/exceptn.h>
+#include <botan/loadstor.h>
+
+namespace Botan {
+
+namespace {
+
+gf2m generate_gf2m_mask(gf2m a)
+ {
+ gf2m result = (a != 0);
+ return ~(result - 1);
+ }
+
+/**
+* number of leading zeros
+*/
+unsigned nlz_16bit(uint16_t x)
+ {
+ unsigned n;
+ if(x == 0) return 16;
+ n = 0;
+ if(x <= 0x00FF) {n = n + 8; x = x << 8;}
+ if(x <= 0x0FFF) {n = n + 4; x = x << 4;}
+ if(x <= 0x3FFF) {n = n + 2; x = x << 2;}
+ if(x <= 0x7FFF) {n = n + 1;}
+ return n;
+ }
+}
+
+int polyn_gf2m::calc_degree_secure() const
+ {
+ int i = static_cast<int>(this->coeff.size()) - 1;
+ int result = 0;
+ uint32_t found_mask = 0;
+ uint32_t tracker_mask = 0xffff;
+ for( ; i >= 0; i--)
+ {
+ found_mask = expand_mask_16bit(this->coeff[i]);
+ result |= i & found_mask & tracker_mask;
+ // tracker mask shall become zero once found mask is set
+ // it shall remain zero from then on
+ tracker_mask = tracker_mask & ~found_mask;
+ }
+ const_cast<polyn_gf2m*>(this)->m_deg = result;
+ return result;
+ }
+
+gf2m random_gf2m(RandomNumberGenerator& rng)
+ {
+ uint8_t b[2];
+ rng.randomize(b, sizeof(b));
+ return make_uint16(b[1], b[0]);
+ }
+
+gf2m random_code_element(uint16_t code_length, RandomNumberGenerator& rng)
+ {
+ if(code_length == 0)
+ {
+ throw Invalid_Argument("random_code_element() was supplied a code length of zero");
+ }
+ const unsigned nlz = nlz_16bit(code_length-1);
+ const gf2m mask = (1 << (16-nlz)) - 1;
+
+ gf2m result;
+
+ do
+ {
+ result = random_gf2m(rng);
+ result &= mask;
+ } while(result >= code_length); // rejection sampling
+
+ return result;
+ }
+
+polyn_gf2m::polyn_gf2m(polyn_gf2m const& other)
+ :m_deg(other.m_deg),
+ coeff(other.coeff),
+ m_sp_field(other.m_sp_field)
+ { }
+
+polyn_gf2m::polyn_gf2m(int d, std::shared_ptr<GF2m_Field> sp_field)
+ :m_deg(-1),
+ coeff(d+1),
+ m_sp_field(sp_field)
+ {
+ }
+
+std::string polyn_gf2m::to_string() const
+ {
+ int d = get_degree();
+ std::string result;
+ for(int i = 0; i <= d; i ++)
+ {
+ result += std::to_string(this->coeff[i]);
+ if(i != d)
+ {
+ result += ", ";
+ }
+ }
+ return result;
+ }
+/**
+* doesn't save coefficients:
+*/
+void polyn_gf2m::realloc(uint32_t new_size)
+ {
+ this->coeff = secure_vector<gf2m>(new_size);
+ }
+
+polyn_gf2m::polyn_gf2m(const uint8_t* mem, uint32_t mem_len, std::shared_ptr<GF2m_Field> sp_field) :
+ m_deg(-1), m_sp_field(sp_field)
+ {
+ if(mem_len % sizeof(gf2m))
+ {
+ throw Decoding_Error("illegal length of memory to decode ");
+ }
+
+ uint32_t size = (mem_len / sizeof(this->coeff[0])) ;
+ this->coeff = secure_vector<gf2m>(size);
+ this->m_deg = -1;
+ for(uint32_t i = 0; i < size; i++)
+ {
+ this->coeff[i] = decode_gf2m(mem);
+ mem += sizeof(this->coeff[0]);
+ }
+ for(uint32_t i = 0; i < size; i++)
+ {
+ if(this->coeff[i] >= (1 << sp_field->get_extension_degree()))
+ {
+ throw Decoding_Error("error decoding polynomial");
+ }
+ }
+ this->get_degree();
+ }
+
+
+polyn_gf2m::polyn_gf2m( std::shared_ptr<GF2m_Field> sp_field) :
+ m_deg(-1), coeff(1), m_sp_field(sp_field)
+ {}
+
+polyn_gf2m::polyn_gf2m(int degree, const uint8_t* mem, size_t mem_byte_len, std::shared_ptr<GF2m_Field> sp_field)
+ :m_sp_field(sp_field)
+ {
+ uint32_t j, k, l;
+ gf2m a;
+ uint32_t polyn_size;
+ 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->coeff = secure_vector<gf2m>(degree+1);
+ gf2m ext_deg = static_cast<gf2m>(this->m_sp_field->get_extension_degree());
+ for (l = 0; l < polyn_size; l++)
+ {
+ k = (l * ext_deg) / 8;
+
+ j = (l * ext_deg) % 8;
+ a = mem[k] >> j;
+ if (j + ext_deg > 8)
+ {
+ a ^= mem[k + 1] << (8- j);
+ }
+ if(j + ext_deg > 16)
+ {
+ a ^= mem[k + 2] << (16- j);
+ }
+ a &= ((1 << ext_deg) - 1);
+ (*this).set_coef( l, a);
+ }
+
+ this->get_degree();
+ }
+
+#if 0
+void polyn_gf2m::encode(uint32_t min_numo_coeffs, uint8_t* mem, uint32_t mem_len) const
+ {
+ uint32_t i;
+ uint32_t numo_coeffs, needed_size;
+ this->get_degree();
+ numo_coeffs = (min_numo_coeffs > static_cast<uint32_t>(this->m_deg+1)) ? min_numo_coeffs : this->m_deg+1;
+ needed_size = sizeof(this->coeff[0]) * numo_coeffs;
+ if(mem_len < needed_size)
+ {
+ Invalid_Argument("provided memory too small to encode polynomial");
+ }
+
+ for(i = 0; i < numo_coeffs; i++)
+ {
+ gf2m to_enc;
+ if(i >= static_cast<uint32_t>(this->m_deg+1))
+ {
+ /* encode a zero */
+ to_enc = 0;
+ }
+ else
+ {
+ to_enc = this->coeff[i];
+ }
+ mem += encode_gf2m(to_enc, mem);
+ }
+ }
+#endif
+
+
+void polyn_gf2m::set_to_zero()
+ {
+ clear_mem(&this->coeff[0], this->coeff.size());
+ this->m_deg = -1;
+ }
+
+int polyn_gf2m::get_degree() const
+ {
+ int d = static_cast<int>(this->coeff.size()) - 1;
+ while ((d >= 0) && (this->coeff[d] == 0))
+ --d;
+ const_cast<polyn_gf2m*>(this)->m_deg = d;
+ return d;
+ }
+
+
+static gf2m eval_aux(const gf2m * /*restrict*/ coeff, gf2m a, int d, std::shared_ptr<GF2m_Field> sp_field)
+ {
+ gf2m b;
+ b = coeff[d--];
+ for (; d >= 0; --d)
+ if (b != 0)
+ {
+ b = sp_field->gf_mul(b, a) ^ coeff[d];
+ }
+ else
+ {
+ b = coeff[d];
+ }
+ return b;
+ }
+
+gf2m polyn_gf2m::eval(gf2m a)
+ {
+ return eval_aux(&this->coeff[0], 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<GF2m_Field> m_sp_field = g.m_sp_field;
+ d = p.get_degree() - g.get_degree();
+ if (d >= 0) {
+ gf2m la = m_sp_field->gf_inv_rn(g.get_lead_coef());
+
+ const int p_degree = p.get_degree();
+
+ BOTAN_ASSERT(p_degree > 0, "Valid polynomial");
+
+ for (i = p_degree; d >= 0; --i, --d) {
+ if (p[i] != 0) {
+ 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]);
+ }
+ (*&p).set_coef( i, 0);
+ }
+ }
+ p.set_degree( g.get_degree() - 1);
+ while ((p.get_degree() >= 0) && (p[p.get_degree()] == 0))
+ p.set_degree( p.get_degree() - 1);
+ }
+ }
+
+std::vector<polyn_gf2m> polyn_gf2m::sqmod_init(const polyn_gf2m & g)
+ {
+ std::vector<polyn_gf2m> sq;
+ const int signed_deg = g.get_degree();
+ if(signed_deg <= 0)
+ throw Invalid_Argument("cannot compute sqmod for such low degree");
+
+ const uint32_t d = static_cast<uint32_t>(signed_deg);
+ uint32_t t = g.m_deg;
+ // create t zero polynomials
+ uint32_t i;
+ for (i = 0; i < t; ++i)
+ {
+ sq.push_back(polyn_gf2m(t+1, g.get_sp_field()));
+ }
+ for (i = 0; i < d / 2; ++i)
+ {
+ sq[i].set_degree( 2 * i);
+ (*&sq[i]).set_coef( 2 * i, 1);
+ }
+
+ for (; i < d; ++i)
+ {
+ clear_mem(&sq[i].coeff[0], 2);
+ copy_mem(&sq[i].coeff[0] + 2, &sq[i - 1].coeff[0], d);
+ sq[i].set_degree( sq[i - 1].get_degree() + 2);
+ polyn_gf2m::remainder(sq[i], g);
+ }
+ return sq;
+ }
+
+/*Modulo p square of a certain polynomial g, sq[] contains the square
+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<polyn_gf2m> & sq, int d)
+ {
+ int i, j;
+ gf2m la;
+ std::shared_ptr<GF2m_Field> sp_field = this->m_sp_field;
+
+ polyn_gf2m result(d - 1, sp_field);
+ // terms of low degree
+ for (i = 0; i < d / 2; ++i)
+ {
+ (*&result).set_coef( i * 2, sp_field->gf_square((*this)[i]));
+ }
+
+ // terms of high degree
+ for (; i < d; ++i)
+ {
+ 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)
+ {
+ result[j] ^= sp_field->gf_mul_zrz(la, sq[i][j]);
+ }
+ }
+ }
+
+ // Update degre
+ result.set_degree( d - 1);
+ while ((result.get_degree() >= 0) && (result[result.get_degree()] == 0))
+ result.set_degree( result.get_degree() - 1);
+ return result;
+ }
+
+
+// destructive
+polyn_gf2m polyn_gf2m::gcd_aux(polyn_gf2m& p1, polyn_gf2m& p2)
+ {
+ if (p2.get_degree() == -1)
+ return p1;
+ else {
+ polyn_gf2m::remainder(p1, p2);
+ return polyn_gf2m::gcd_aux(p2, p1);
+ }
+ }
+
+
+polyn_gf2m polyn_gf2m::gcd(polyn_gf2m const& p1, polyn_gf2m const& p2)
+ {
+ polyn_gf2m a(p1);
+ polyn_gf2m b(p2);
+ if (a.get_degree() < b.get_degree())
+ {
+ return polyn_gf2m(polyn_gf2m::gcd_aux(b, a));
+ }
+ else
+ {
+ return polyn_gf2m(polyn_gf2m::gcd_aux(a, b));
+ }
+ }
+
+
+
+
+
+// Returns the degree of the smallest factor
+size_t polyn_gf2m::degppf(const polyn_gf2m& g)
+ {
+ polyn_gf2m s(g.get_sp_field());
+
+ const size_t ext_deg = g.m_sp_field->get_extension_degree();
+ const int d = g.get_degree();
+ std::vector<polyn_gf2m> u = polyn_gf2m::sqmod_init(g);
+
+ polyn_gf2m p(d - 1, g.m_sp_field);
+
+ p.set_degree(1);
+ (*&p).set_coef(1, 1);
+ size_t result = static_cast<size_t>(d);
+ for(size_t i = 1; i <= (d / 2) * ext_deg; ++i)
+ {
+ polyn_gf2m r = p.sqmod(u, d);
+ if ((i % ext_deg) == 0)
+ {
+ r[1] ^= 1;
+ r.get_degree(); // The degree may change
+ s = polyn_gf2m::gcd( g, r);
+
+ if(s.get_degree() > 0)
+ {
+ result = i / ext_deg;
+ break;
+ }
+ r[1] ^= 1;
+ r.get_degree(); // The degree may change
+ }
+ // No need for the exchange s
+ s = p;
+ p = r;
+ r = s;
+ }
+
+ return result;
+ }
+
+void polyn_gf2m::patchup_deg_secure( uint32_t trgt_deg, volatile gf2m patch_elem)
+ {
+ uint32_t i;
+ if(this->coeff.size() < trgt_deg)
+ {
+ return;
+ }
+ for(i = 0; i < this->coeff.size(); i++)
+ {
+ uint32_t equal, equal_mask;
+ this->coeff[i] |= patch_elem;
+ equal = (i == trgt_deg);
+ equal_mask = expand_mask_16bit(equal);
+ patch_elem &= ~equal_mask;
+ }
+ this->calc_degree_secure();
+ }
+// We suppose m_deg(g) >= m_deg(p)
+// v is the problem
+std::pair<polyn_gf2m, polyn_gf2m> polyn_gf2m::eea_with_coefficients( const polyn_gf2m & p, const polyn_gf2m & g, int break_deg)
+ {
+
+ std::shared_ptr<GF2m_Field> m_sp_field = g.m_sp_field;
+ int i, j, dr, du, delta;
+ gf2m a;
+ polyn_gf2m aux;
+
+ // initialisation of the local variables
+ // r0 <- g, r1 <- p, u0 <- 0, u1 <- 1
+ dr = g.get_degree();
+
+ BOTAN_ASSERT(dr > 3, "Valid polynomial");
+
+ polyn_gf2m r0(dr, g.m_sp_field);
+ polyn_gf2m r1(dr - 1, g.m_sp_field);
+ polyn_gf2m u0(dr - 1, g.m_sp_field);
+ polyn_gf2m u1(dr - 1, g.m_sp_field);
+
+ r0 = g;
+ r1 = p;
+ u0.set_to_zero();
+ u1.set_to_zero();
+ (*&u1).set_coef( 0, 1);
+ u1.set_degree( 0);
+
+
+ // invariants:
+ // r1 = u1 * p + v1 * g
+ // r0 = u0 * p + v0 * g
+ // and m_deg(u1) = m_deg(g) - m_deg(r0)
+ // It stops when m_deg (r1) <t (m_deg (r0)> = t)
+ // And therefore m_deg (u1) = m_deg (g) - m_deg (r0) <m_deg (g) - break_deg
+ du = 0;
+ dr = r1.get_degree();
+ delta = r0.get_degree() - dr;
+
+
+ while (dr >= break_deg)
+ {
+
+ for (j = delta; j >= 0; --j)
+ {
+ a = m_sp_field->gf_div(r0[dr + j], r1[dr]);
+ if (a != 0)
+ {
+ 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]);
+ }
+ // r0(z) <- r0(z) + a * r1(z) * z^j
+ for (i = 0; i <= dr; ++i)
+ {
+ r0[i + j] ^= m_sp_field->gf_mul_zrz(la, r1[i]);
+ }
+ }
+ } // end loop over j
+
+ if(break_deg != 1) /* key eq. solving */
+ {
+ /* [ssms_icisc09] Countermeasure
+ * d_break from paper equals break_deg - 1
+ * */
+
+ volatile gf2m fake_elem = 0x01;
+ volatile gf2m cond1, cond2;
+ int trgt_deg = r1.get_degree() - 1;
+ r0.calc_degree_secure();
+ u0.calc_degree_secure();
+ if(!(g.get_degree() % 2))
+ {
+ /* t even */
+ cond1 = r0.get_degree() < break_deg - 1;
+ }
+ else
+ {
+ /* t odd */
+ cond1 = r0.get_degree() < break_deg;
+ cond2 = u0.get_degree() < break_deg - 1;
+ cond1 &= cond2;
+ }
+ /* expand cond1 to a full mask */
+ gf2m mask = generate_gf2m_mask(cond1);
+ fake_elem &= mask;
+ r0.patchup_deg_secure(trgt_deg, fake_elem);
+ }
+ if(break_deg == 1) /* syndrome inversion */
+ {
+ volatile gf2m fake_elem = 0x00;
+ volatile uint32_t trgt_deg = 0;
+ r0.calc_degree_secure();
+ u0.calc_degree_secure();
+ /**
+ * countermeasure against the low weight attacks for w=4, w=6 and w=8.
+ * Higher values are not covered since for w=8 we already have a
+ * probability for a positive of 1/n^3 from random ciphertexts with the
+ * given weight. For w = 10 it would be 1/n^4 and so on. Thus attacks
+ * based on such high values of w are considered impractical.
+ *
+ * The outer test for the degree of u ( Omega in the paper ) needs not to
+ * be disguised. Each of the three is performed at most once per EEA
+ * (syndrome inversion) execution, the attacker knows this already when
+ * preparing the ciphertext with the given weight. Inside these three
+ * cases however, we must use timing neutral (branch free) operations to
+ * implement the condition detection and the counteractions.
+ *
+ */
+ if(u0.get_degree() == 4)
+ {
+ uint32_t mask = 0;
+ /**
+ * Condition that the EEA would break now
+ */
+ 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
+ * 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
+ * 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.coeff[1], m_sp_field->gf_inv(r0.coeff[0])) == 1;
+
+ // Condition sigma_3 = 0:
+ int cond_u3 = u0.coeff[3] == 0;
+ // combine the conditions:
+ cond_r &= (cond_u1 & cond_u3);
+ // mask generation:
+ mask = expand_mask_16bit(cond_r);
+ trgt_deg = 2 & mask;
+ fake_elem = 1 & mask;
+ }
+ 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.coeff[1], m_sp_field->gf_inv(r0.coeff[0])) == 1;
+ int cond_u3 = u0.coeff[3] == 0;
+
+ int cond_u5 = u0.coeff[5] == 0;
+
+ cond_r &= (cond_u1 & cond_u3 & cond_u5);
+ mask = expand_mask_16bit(cond_r);
+ trgt_deg = 4 & mask;
+ fake_elem = 1 & mask;
+ }
+ 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.coeff[3] == 0;
+
+ int cond_u5 = u0.coeff[5] == 0;
+
+ int cond_u7 = u0.coeff[7] == 0;
+
+ cond_r &= (cond_u1 & cond_u3 & cond_u5 & cond_u7);
+ mask = expand_mask_16bit(cond_r);
+ trgt_deg = 6 & mask;
+ fake_elem = 1 & mask;
+ }
+ r0.patchup_deg_secure(trgt_deg, fake_elem);
+ }
+ // exchange
+ aux = r0; r0 = r1; r1 = aux;
+ aux = u0; u0 = u1; u1 = aux;
+
+ du = du + delta;
+ delta = 1;
+ while (r1[dr - delta] == 0)
+ {
+ delta++;
+ }
+
+
+ dr -= delta;
+ } /* end while loop (dr >= break_deg) */
+
+
+ u1.set_degree( du);
+ r1.set_degree( dr);
+ //return u1 and r1;
+ return std::make_pair(u1,r1); // coefficients u,v
+ }
+
+polyn_gf2m::polyn_gf2m(size_t t, RandomNumberGenerator& rng, std::shared_ptr<GF2m_Field> sp_field)
+ :m_deg(static_cast<int>(t)),
+ coeff(t+1),
+ m_sp_field(sp_field)
+ {
+ this->set_coef(t, 1);
+ for(;;)
+ {
+ for(size_t i = 0; i < t; ++i)
+ {
+ this->set_coef(i, random_code_element(sp_field->get_cardinality(), rng));
+ }
+
+ const size_t degree = polyn_gf2m::degppf(*this);
+
+ if(degree >= t)
+ break;
+ }
+ }
+
+void polyn_gf2m::poly_shiftmod( const polyn_gf2m & g)
+ {
+ if(g.get_degree() <= 1)
+ {
+ throw Invalid_Argument("shiftmod cannot be called on polynomials of degree 1 or less");
+ }
+ std::shared_ptr<GF2m_Field> field = g.m_sp_field;
+
+ int t = g.get_degree();
+ gf2m a = field->gf_div(this->coeff[t-1], g.coeff[t]);
+ for (int i = t - 1; i > 0; --i)
+ {
+ this->coeff[i] = this->coeff[i - 1] ^ this->m_sp_field->gf_mul(a, g.coeff[i]);
+ }
+ this->coeff[0] = field->gf_mul(a, g.coeff[0]);
+ }
+
+std::vector<polyn_gf2m> polyn_gf2m::sqrt_mod_init(const polyn_gf2m & g)
+ {
+ uint32_t i, t;
+ uint32_t nb_polyn_sqrt_mat;
+ std::shared_ptr<GF2m_Field> m_sp_field = g.m_sp_field;
+ std::vector<polyn_gf2m> result;
+ t = g.get_degree();
+ nb_polyn_sqrt_mat = t/2;
+
+ std::vector<polyn_gf2m> sq_aux = polyn_gf2m::sqmod_init(g);
+
+
+ polyn_gf2m p( t - 1, g.get_sp_field());
+ p.set_degree( 1);
+
+ (*&p).set_coef( 1, 1);
+ // q(z) = 0, p(z) = z
+ for (i = 0; i < t * m_sp_field->get_extension_degree() - 1; ++i)
+ {
+ // q(z) <- p(z)^2 mod g(z)
+ polyn_gf2m q = p.sqmod(sq_aux, t);
+ // q(z) <-> p(z)
+ polyn_gf2m aux = q;
+ q = p;
+ p = aux;
+ }
+ // p(z) = z^(2^(tm-1)) mod g(z) = sqrt(z) mod g(z)
+
+ for (i = 0; i < nb_polyn_sqrt_mat; ++i)
+ {
+ result.push_back(polyn_gf2m(t - 1, g.get_sp_field()));
+ }
+
+ result[0] = p;
+ result[0].get_degree();
+ for(i = 1; i < nb_polyn_sqrt_mat; i++)
+ {
+ result[i] = result[i - 1];
+ result[i].poly_shiftmod(g),
+ result[i].get_degree();
+ }
+
+ return result;
+ }
+
+std::vector<polyn_gf2m> syndrome_init(polyn_gf2m const& generator, std::vector<gf2m> const& support, int n)
+ {
+ int i,j,t;
+ gf2m a;
+
+
+ std::shared_ptr<GF2m_Field> m_sp_field = generator.m_sp_field;
+
+ std::vector<polyn_gf2m> result;
+ t = generator.get_degree();
+
+ //g(z)=g_t+g_(t-1).z^(t-1)+......+g_1.z+g_0
+ //f(z)=f_(t-1).z^(t-1)+......+f_1.z+f_0
+
+ for(j=0;j<n;j++)
+ {
+ result.push_back(polyn_gf2m( t-1, m_sp_field));
+
+ (*&result[j]).set_coef(t-1,1);
+ for(i=t-2;i>=0;i--)
+ {
+ (*&result[j]).set_coef(i, (generator)[i+1] ^
+ m_sp_field->gf_mul(lex_to_gray(support[j]),result[j][i+1]));
+ }
+ a = ((generator)[0] ^ m_sp_field->gf_mul(lex_to_gray(support[j]),result[j][0]));
+ for(i=0;i<t;i++)
+ {
+ (*&result[j]).set_coef(i, m_sp_field->gf_div(result[j][i],a));
+ }
+ }
+ return result;
+ }
+
+polyn_gf2m::polyn_gf2m(const secure_vector<uint8_t>& encoded, std::shared_ptr<GF2m_Field> sp_field )
+ :m_sp_field(sp_field)
+ {
+ if(encoded.size() % 2)
+ {
+ 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];
+ coeff.push_back(el);
+ }
+ get_degree();
+
+ }
+secure_vector<uint8_t> polyn_gf2m::encode() const
+ {
+ secure_vector<uint8_t> result;
+
+ if(m_deg < 1)
+ {
+ result.push_back(0);
+ result.push_back(0);
+ return result;
+ }
+
+ 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, coeff[i]));
+ result.push_back(get_byte(1, coeff[i]));
+ }
+ return result;
+ }
+
+void polyn_gf2m::swap(polyn_gf2m& other)
+ {
+ std::swap(this->m_deg, other.m_deg);
+ std::swap(this->m_sp_field, other.m_sp_field);
+ std::swap(this->coeff, other.coeff);
+ }
+
+bool polyn_gf2m::operator==(const polyn_gf2m & other) const
+ {
+ if(m_deg != other.m_deg || coeff != other.coeff)
+ {
+ return false;
+ }
+ return true;
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/pubkey/mce/polyn_gf2m.h b/comm/third_party/botan/src/lib/pubkey/mce/polyn_gf2m.h
new file mode 100644
index 0000000000..0f9bf07f9a
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/mce/polyn_gf2m.h
@@ -0,0 +1,174 @@
+/*
+ * (C) Copyright Projet SECRET, INRIA, Rocquencourt
+ * (C) Bhaskar Biswas and Nicolas Sendrier
+ *
+ * (C) 2014 cryptosource GmbH
+ * (C) 2014 Falko Strenzke fstrenzke@cryptosource.de
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ *
+ */
+
+#ifndef BOTAN_POLYN_GF2M_H_
+#define BOTAN_POLYN_GF2M_H_
+
+#include <botan/secmem.h>
+#include <utility>
+#include <string>
+
+// Currently must be visible for MSVC
+//BOTAN_FUTURE_INTERNAL_HEADER(polyn_gf2m.h)
+
+namespace Botan {
+
+typedef uint16_t gf2m;
+
+class GF2m_Field;
+
+class RandomNumberGenerator;
+
+class polyn_gf2m
+ {
+ public:
+ /**
+ * create a zero polynomial:
+ */
+ explicit polyn_gf2m(std::shared_ptr<GF2m_Field> sp_field);
+
+ polyn_gf2m() : m_deg(-1) {}
+
+ polyn_gf2m(const secure_vector<uint8_t>& encoded, std::shared_ptr<GF2m_Field> sp_field);
+
+ polyn_gf2m& operator=(const polyn_gf2m&) = default;
+
+ /**
+ * create zero polynomial with reservation of space for a degree d polynomial
+ */
+ polyn_gf2m(int d, std::shared_ptr<GF2m_Field> sp_field);
+
+ polyn_gf2m(polyn_gf2m const& other);
+
+ /**
+ * random irreducible polynomial of degree t
+ */
+ polyn_gf2m(size_t t, RandomNumberGenerator& rng, std::shared_ptr<GF2m_Field> sp_field);
+
+ /** decode a polynomial from memory: **/
+ polyn_gf2m(const uint8_t* mem, uint32_t mem_len, std::shared_ptr<GF2m_Field> sp_field);
+
+ /**
+ * create a polynomial from memory area (encoded)
+ */
+ polyn_gf2m(int degree, const uint8_t* mem, size_t mem_byte_len, std::shared_ptr<GF2m_Field> sp_field);
+
+ bool operator==(const polyn_gf2m & other) const ;
+
+ bool operator!=(const polyn_gf2m & other) const { return !(*this == other); }
+
+ polyn_gf2m(polyn_gf2m&& other)
+ {
+ this->swap(other);
+ }
+
+ polyn_gf2m & operator=(polyn_gf2m&& other)
+ {
+ if(this != &other)
+ {
+ this->swap(other);
+ }
+ return *this;
+ }
+
+ void swap(polyn_gf2m& other);
+
+ secure_vector<uint8_t> encode() const;
+
+ std::shared_ptr<GF2m_Field> get_sp_field() const
+ { return m_sp_field; }
+
+ gf2m& operator[](size_t i) { return coeff[i]; }
+
+ gf2m operator[](size_t i) const { return coeff[i]; }
+
+ gf2m get_lead_coef() const { return coeff[m_deg]; }
+
+ gf2m get_coef(size_t i) const { return coeff[i]; }
+
+ inline void set_coef(size_t i, gf2m v)
+ {
+ coeff[i] = v;
+ }
+
+ inline void add_to_coef(size_t i, gf2m v)
+ {
+ coeff[i] ^= v;
+ }
+
+ std::string to_string() const;
+
+ void encode(uint32_t min_numo_coeffs, uint8_t* mem, uint32_t mem_len) const;
+
+ int get_degree() const;
+
+ /**
+ * determine the degree in a timing secure manner. the timing of this function
+ * only depends on the number of allocated coefficients, not on the actual
+ * degree
+ */
+ int calc_degree_secure() const;
+
+ size_t degppf(const polyn_gf2m& g);
+
+ static std::vector<polyn_gf2m> sqmod_init(const polyn_gf2m & g);
+
+ static std::vector<polyn_gf2m> sqrt_mod_init(const polyn_gf2m & g);
+
+
+ polyn_gf2m sqmod(const std::vector<polyn_gf2m> & sq, int d);
+ void set_to_zero();
+ gf2m eval(gf2m a);
+
+ static std::pair<polyn_gf2m, polyn_gf2m> eea_with_coefficients(const polyn_gf2m & p,
+ const polyn_gf2m & g,
+ int break_deg);
+
+ void patchup_deg_secure( uint32_t trgt_deg, volatile gf2m patch_elem);
+
+ private:
+
+ void set_degree(int d) { m_deg = d; }
+
+ void poly_shiftmod( const polyn_gf2m & g);
+ void realloc(uint32_t new_size);
+ static polyn_gf2m gcd(polyn_gf2m const& p1, polyn_gf2m const& p2);
+
+ /**
+ * destructive:
+ */
+ static void remainder(polyn_gf2m & p, const polyn_gf2m & g);
+
+ static polyn_gf2m gcd_aux(polyn_gf2m& p1, polyn_gf2m& p2);
+ public:
+ // public member variable:
+ int m_deg;
+
+ // public member variable:
+ secure_vector<gf2m> coeff;
+
+ // public member variable:
+ std::shared_ptr<GF2m_Field> m_sp_field;
+ };
+
+gf2m random_gf2m(RandomNumberGenerator& rng);
+gf2m random_code_element(uint16_t code_length, RandomNumberGenerator& rng);
+
+std::vector<polyn_gf2m> syndrome_init(polyn_gf2m const& generator, std::vector<gf2m> const& support, int n);
+
+/**
+* Find the roots of a polynomial over GF(2^m) using the method by Federenko et al.
+*/
+secure_vector<gf2m> find_roots_gf2m_decomp(const polyn_gf2m & polyn, size_t code_length);
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/pubkey/mceies/info.txt b/comm/third_party/botan/src/lib/pubkey/mceies/info.txt
new file mode 100644
index 0000000000..cf5e011540
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/mceies/info.txt
@@ -0,0 +1,10 @@
+<defines>
+MCEIES -> 20150706
+</defines>
+
+<requires>
+aes
+mce
+ocb
+kdf1
+</requires>
diff --git a/comm/third_party/botan/src/lib/pubkey/mceies/mceies.cpp b/comm/third_party/botan/src/lib/pubkey/mceies/mceies.cpp
new file mode 100644
index 0000000000..4d62889fee
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/mceies/mceies.cpp
@@ -0,0 +1,110 @@
+/*
+* McEliece Integrated Encryption System
+* (C) 2014,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/mceies.h>
+#include <botan/aead.h>
+#include <botan/rng.h>
+#include <botan/mceliece.h>
+#include <botan/pubkey.h>
+
+namespace Botan {
+
+namespace {
+
+secure_vector<uint8_t> aead_key(const secure_vector<uint8_t>& mk,
+ const AEAD_Mode& aead)
+ {
+ // Fold the key as required for the AEAD mode in use
+ if(aead.valid_keylength(mk.size()))
+ return mk;
+
+ secure_vector<uint8_t> r(aead.key_spec().maximum_keylength());
+ BOTAN_ASSERT_NOMSG(r.size() > 0);
+ for(size_t i = 0; i != mk.size(); ++i)
+ r[i % r.size()] ^= mk[i];
+ return r;
+ }
+
+}
+
+secure_vector<uint8_t>
+mceies_encrypt(const McEliece_PublicKey& pubkey,
+ const uint8_t pt[], size_t pt_len,
+ const uint8_t ad[], size_t ad_len,
+ RandomNumberGenerator& rng,
+ const std::string& algo)
+ {
+ PK_KEM_Encryptor kem_op(pubkey, rng, "KDF1(SHA-512)");
+
+ secure_vector<uint8_t> mce_ciphertext, mce_key;
+ kem_op.encrypt(mce_ciphertext, mce_key, 64, rng);
+
+ const size_t mce_code_bytes = (pubkey.get_code_length() + 7) / 8;
+
+ BOTAN_ASSERT(mce_ciphertext.size() == mce_code_bytes, "Unexpected size");
+
+ std::unique_ptr<AEAD_Mode> aead = AEAD_Mode::create_or_throw(algo, ENCRYPTION);
+
+ const size_t nonce_len = aead->default_nonce_length();
+
+ aead->set_key(aead_key(mce_key, *aead));
+ aead->set_associated_data(ad, ad_len);
+
+ const secure_vector<uint8_t> nonce = rng.random_vec(nonce_len);
+
+ secure_vector<uint8_t> msg(mce_ciphertext.size() + nonce.size() + pt_len);
+ copy_mem(msg.data(), mce_ciphertext.data(), mce_ciphertext.size());
+ copy_mem(msg.data() + mce_ciphertext.size(), nonce.data(), nonce.size());
+ copy_mem(msg.data() + mce_ciphertext.size() + nonce.size(), pt, pt_len);
+
+ aead->start(nonce);
+ aead->finish(msg, mce_ciphertext.size() + nonce.size());
+ return msg;
+ }
+
+secure_vector<uint8_t>
+mceies_decrypt(const McEliece_PrivateKey& privkey,
+ const uint8_t ct[], size_t ct_len,
+ const uint8_t ad[], size_t ad_len,
+ const std::string& algo)
+ {
+ try
+ {
+ Null_RNG null_rng;
+ PK_KEM_Decryptor kem_op(privkey, null_rng, "KDF1(SHA-512)");
+
+ const size_t mce_code_bytes = (privkey.get_code_length() + 7) / 8;
+
+ std::unique_ptr<AEAD_Mode> aead = AEAD_Mode::create_or_throw(algo, DECRYPTION);
+
+ const size_t nonce_len = aead->default_nonce_length();
+
+ if(ct_len < mce_code_bytes + nonce_len + aead->tag_size())
+ throw Decoding_Error("Input message too small to be valid");
+
+ const secure_vector<uint8_t> mce_key = kem_op.decrypt(ct, mce_code_bytes, 64);
+
+ aead->set_key(aead_key(mce_key, *aead));
+ aead->set_associated_data(ad, ad_len);
+
+ secure_vector<uint8_t> pt(ct + mce_code_bytes + nonce_len, ct + ct_len);
+
+ aead->start(&ct[mce_code_bytes], nonce_len);
+ aead->finish(pt, 0);
+ return pt;
+ }
+ catch(Invalid_Authentication_Tag&)
+ {
+ throw;
+ }
+ catch(std::exception& e)
+ {
+ throw Decoding_Error("mce_decrypt failed: " + std::string(e.what()));
+ }
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/pubkey/mceies/mceies.h b/comm/third_party/botan/src/lib/pubkey/mceies/mceies.h
new file mode 100644
index 0000000000..c9b3f7efd5
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/mceies/mceies.h
@@ -0,0 +1,46 @@
+/*
+* McEliece Integrated Encryption System
+* (C) 2014,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_MCEIES_H_
+#define BOTAN_MCEIES_H_
+
+#include <botan/secmem.h>
+#include <string>
+
+namespace Botan {
+
+class RandomNumberGenerator;
+class McEliece_PublicKey;
+class McEliece_PrivateKey;
+
+/**
+* McEliece Integrated Encryption System
+* Derive a shared key using MCE KEM and encrypt/authenticate the
+* plaintext and AD using AES-256 in OCB mode.
+*/
+secure_vector<uint8_t>
+BOTAN_PUBLIC_API(2,0) mceies_encrypt(const McEliece_PublicKey& pubkey,
+ const uint8_t pt[], size_t pt_len,
+ const uint8_t ad[], size_t ad_len,
+ RandomNumberGenerator& rng,
+ const std::string& aead = "AES-256/OCB");
+
+/**
+* McEliece Integrated Encryption System
+* Derive a shared key using MCE KEM and decrypt/authenticate the
+* ciphertext and AD using AES-256 in OCB mode.
+*/
+secure_vector<uint8_t>
+BOTAN_PUBLIC_API(2,0) mceies_decrypt(const McEliece_PrivateKey& privkey,
+ const uint8_t ct[], size_t ct_len,
+ const uint8_t ad[], size_t ad_len,
+ const std::string& aead = "AES-256/OCB");
+
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/pubkey/newhope/info.txt b/comm/third_party/botan/src/lib/pubkey/newhope/info.txt
new file mode 100644
index 0000000000..4877a138bb
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/newhope/info.txt
@@ -0,0 +1,12 @@
+<defines>
+NEWHOPE -> 20161018
+</defines>
+
+<requires>
+sha3
+shake_cipher
+
+sha2_32
+ctr
+aes
+</requires>
diff --git a/comm/third_party/botan/src/lib/pubkey/newhope/newhope.cpp b/comm/third_party/botan/src/lib/pubkey/newhope/newhope.cpp
new file mode 100644
index 0000000000..6a0440139e
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/newhope/newhope.cpp
@@ -0,0 +1,800 @@
+/*
+* NEWHOPE Ring-LWE scheme
+* Based on the public domain reference implementation by the
+* designers (https://github.com/tpoeppelmann/newhope)
+*
+* Further changes
+* (C) 2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/newhope.h>
+#include <botan/hash.h>
+#include <botan/rng.h>
+#include <botan/stream_cipher.h>
+#include <botan/loadstor.h>
+
+namespace Botan {
+
+newhope_poly::~newhope_poly()
+ {
+ secure_scrub_memory(coeffs, sizeof(coeffs));
+ }
+
+typedef newhope_poly poly;
+
+namespace {
+
+static const uint16_t PARAM_Q = 12289;
+static const size_t PARAM_N = 1024;
+
+/* Incomplete-reduction routines; for details on allowed input ranges
+ * and produced output ranges, see the description in the paper:
+ * https://cryptojedi.org/papers/#newhope */
+
+inline uint16_t montgomery_reduce(uint32_t a)
+ {
+ const uint32_t qinv = 12287; // -inverse_mod(p,2^18)
+ const uint32_t rlog = 18;
+ const uint32_t rlog_mask = ((1 << rlog) - 1);
+
+ uint32_t u = (a * qinv);
+ u &= rlog_mask;
+ u *= PARAM_Q;
+ u += a;
+ return (u >> rlog);
+ }
+
+inline uint16_t barrett_reduce(uint16_t a)
+ {
+ uint32_t u = (static_cast<uint32_t>(a) * 5) >> 16;
+ u *= PARAM_Q;
+ a = static_cast<uint16_t>(a - u);
+ return a;
+ }
+
+inline void mul_coefficients(uint16_t* poly, const uint16_t* factors)
+ {
+ for(size_t i = 0; i < PARAM_N; i++)
+ {
+ poly[i] = montgomery_reduce(poly[i] * factors[i]);
+ }
+ }
+
+/* GS_bo_to_no; omegas need to be in Montgomery domain */
+inline void ntt(uint16_t* a, const uint16_t* omega)
+ {
+ for(size_t i = 0; i < 10; i+=2)
+ {
+ // Even level
+ size_t distance = (1ULL << i);
+ for(size_t start = 0; start < distance; start++)
+ {
+ size_t jTwiddle = 0;
+ for(size_t j = start; j < PARAM_N-1; j += 2*distance)
+ {
+ uint16_t W = omega[jTwiddle++];
+ uint16_t temp = a[j];
+ a[j] = (temp + a[j + distance]); // Omit reduction (be lazy)
+ a[j + distance] = montgomery_reduce((W * (static_cast<uint32_t>(temp) + 3*PARAM_Q - a[j + distance])));
+ }
+ }
+
+ // Odd level
+ distance <<= 1;
+ for(size_t start = 0; start < distance; start++)
+ {
+ size_t jTwiddle = 0;
+ for(size_t j = start; j < PARAM_N-1; j += 2*distance)
+ {
+ uint16_t W = omega[jTwiddle++];
+ uint16_t temp = a[j];
+ a[j] = barrett_reduce((temp + a[j + distance]));
+ a[j + distance] = montgomery_reduce((W * (static_cast<uint32_t>(temp) + 3*PARAM_Q - a[j + distance])));
+ }
+ }
+ }
+ }
+
+inline void poly_frombytes(poly* r, const uint8_t* a)
+ {
+ for(size_t i = 0; i < PARAM_N/4; i++)
+ {
+ r->coeffs[4*i+0] = a[7*i+0] | ((static_cast<uint16_t>(a[7*i+1]) & 0x3f) << 8);
+ r->coeffs[4*i+1] = (a[7*i+1] >> 6) | (static_cast<uint16_t>(a[7*i+2]) << 2) | (static_cast<uint16_t>
+ (a[7*i+3] & 0x0f) << 10);
+ r->coeffs[4*i+2] = (a[7*i+3] >> 4) | (static_cast<uint16_t>(a[7*i+4]) << 4) | (static_cast<uint16_t>
+ (a[7*i+5] & 0x03) << 12);
+ r->coeffs[4*i+3] = (a[7*i+5] >> 2) | (static_cast<uint16_t>(a[7*i+6]) << 6);
+ }
+ }
+
+inline void poly_tobytes(uint8_t* r, const poly* p)
+ {
+ for(size_t i = 0; i < PARAM_N/4; i++)
+ {
+ uint16_t t0 = barrett_reduce(p->coeffs[4*i+0]); //Make sure that coefficients have only 14 bits
+ uint16_t t1 = barrett_reduce(p->coeffs[4*i+1]);
+ uint16_t t2 = barrett_reduce(p->coeffs[4*i+2]);
+ uint16_t t3 = barrett_reduce(p->coeffs[4*i+3]);
+
+ uint16_t m;
+ int16_t c;
+
+ m = t0 - PARAM_Q;
+ c = m;
+ c >>= 15;
+ t0 = m ^ ((t0^m)&c); // <Make sure that coefficients are in [0,q]
+
+ m = t1 - PARAM_Q;
+ c = m;
+ c >>= 15;
+ t1 = m ^ ((t1^m)&c); // <Make sure that coefficients are in [0,q]
+
+ m = t2 - PARAM_Q;
+ c = m;
+ c >>= 15;
+ t2 = m ^ ((t2^m)&c); // <Make sure that coefficients are in [0,q]
+
+ m = t3 - PARAM_Q;
+ c = m;
+ c >>= 15;
+ t3 = m ^ ((t3^m)&c); // <Make sure that coefficients are in [0,q]
+
+ r[7*i+0] = t0 & 0xff;
+ r[7*i+1] = static_cast<uint8_t>((t0 >> 8) | (t1 << 6));
+ r[7*i+2] = static_cast<uint8_t>((t1 >> 2));
+ r[7*i+3] = static_cast<uint8_t>((t1 >> 10) | (t2 << 4));
+ r[7*i+4] = static_cast<uint8_t>((t2 >> 4));
+ r[7*i+5] = static_cast<uint8_t>((t2 >> 12) | (t3 << 2));
+ r[7*i+6] = static_cast<uint8_t>((t3 >> 6));
+ }
+ }
+
+inline void poly_getnoise(Botan::RandomNumberGenerator& rng, poly* r)
+ {
+ uint8_t buf[4*PARAM_N];
+
+ rng.randomize(buf, 4*PARAM_N);
+
+ for(size_t i = 0; i < PARAM_N; i++)
+ {
+ const uint32_t t = load_le<uint32_t>(buf, i);
+ uint32_t d = 0;
+ for(size_t j = 0; j < 8; j++)
+ {
+ d += (t >> j) & 0x01010101;
+ }
+ const uint32_t a = ((d >> 8) & 0xff) + (d & 0xff);
+ const uint32_t b = (d >> 24) + ((d >> 16) & 0xff);
+ r->coeffs[i] = static_cast<uint16_t>(a + PARAM_Q - b);
+ }
+ }
+
+inline void poly_pointwise(poly* r, const poly* a, const poly* b)
+ {
+ for(size_t i = 0; i < PARAM_N; i++)
+ {
+ const uint16_t t = montgomery_reduce(3186*b->coeffs[i]); /* t is now in Montgomery domain */
+ r->coeffs[i] = montgomery_reduce(a->coeffs[i] * t); /* r->coeffs[i] is back in normal domain */
+ }
+ }
+
+inline void poly_add(poly* r, const poly* a, const poly* b)
+ {
+ for(size_t i = 0; i < PARAM_N; i++)
+ {
+ r->coeffs[i] = barrett_reduce(a->coeffs[i] + b->coeffs[i]);
+ }
+ }
+
+inline void poly_ntt(poly* r)
+ {
+ static const uint16_t omegas_montgomery[PARAM_N/2] =
+ {
+ 4075, 6974, 7373, 7965, 3262, 5079, 522, 2169, 6364, 1018, 1041, 8775, 2344,
+ 11011, 5574, 1973, 4536, 1050, 6844, 3860, 3818, 6118, 2683, 1190, 4789,
+ 7822, 7540, 6752, 5456, 4449, 3789, 12142, 11973, 382, 3988, 468, 6843, 5339,
+ 6196, 3710, 11316, 1254, 5435, 10930, 3998, 10256, 10367, 3879, 11889, 1728,
+ 6137, 4948, 5862, 6136, 3643, 6874, 8724, 654, 10302, 1702, 7083, 6760, 56,
+ 3199, 9987, 605, 11785, 8076, 5594, 9260, 6403, 4782, 6212, 4624, 9026, 8689,
+ 4080, 11868, 6221, 3602, 975, 8077, 8851, 9445, 5681, 3477, 1105, 142, 241,
+ 12231, 1003, 3532, 5009, 1956, 6008, 11404, 7377, 2049, 10968, 12097, 7591,
+ 5057, 3445, 4780, 2920, 7048, 3127, 8120, 11279, 6821, 11502, 8807, 12138,
+ 2127, 2839, 3957, 431, 1579, 6383, 9784, 5874, 677, 3336, 6234, 2766, 1323,
+ 9115, 12237, 2031, 6956, 6413, 2281, 3969, 3991, 12133, 9522, 4737, 10996,
+ 4774, 5429, 11871, 3772, 453, 5908, 2882, 1805, 2051, 1954, 11713, 3963,
+ 2447, 6142, 8174, 3030, 1843, 2361, 12071, 2908, 3529, 3434, 3202, 7796,
+ 2057, 5369, 11939, 1512, 6906, 10474, 11026, 49, 10806, 5915, 1489, 9789,
+ 5942, 10706, 10431, 7535, 426, 8974, 3757, 10314, 9364, 347, 5868, 9551,
+ 9634, 6554, 10596, 9280, 11566, 174, 2948, 2503, 6507, 10723, 11606, 2459,
+ 64, 3656, 8455, 5257, 5919, 7856, 1747, 9166, 5486, 9235, 6065, 835, 3570,
+ 4240, 11580, 4046, 10970, 9139, 1058, 8210, 11848, 922, 7967, 1958, 10211,
+ 1112, 3728, 4049, 11130, 5990, 1404, 325, 948, 11143, 6190, 295, 11637, 5766,
+ 8212, 8273, 2919, 8527, 6119, 6992, 8333, 1360, 2555, 6167, 1200, 7105, 7991,
+ 3329, 9597, 12121, 5106, 5961, 10695, 10327, 3051, 9923, 4896, 9326, 81,
+ 3091, 1000, 7969, 4611, 726, 1853, 12149, 4255, 11112, 2768, 10654, 1062,
+ 2294, 3553, 4805, 2747, 4846, 8577, 9154, 1170, 2319, 790, 11334, 9275, 9088,
+ 1326, 5086, 9094, 6429, 11077, 10643, 3504, 3542, 8668, 9744, 1479, 1, 8246,
+ 7143, 11567, 10984, 4134, 5736, 4978, 10938, 5777, 8961, 4591, 5728, 6461,
+ 5023, 9650, 7468, 949, 9664, 2975, 11726, 2744, 9283, 10092, 5067, 12171,
+ 2476, 3748, 11336, 6522, 827, 9452, 5374, 12159, 7935, 3296, 3949, 9893,
+ 4452, 10908, 2525, 3584, 8112, 8011, 10616, 4989, 6958, 11809, 9447, 12280,
+ 1022, 11950, 9821, 11745, 5791, 5092, 2089, 9005, 2881, 3289, 2013, 9048,
+ 729, 7901, 1260, 5755, 4632, 11955, 2426, 10593, 1428, 4890, 5911, 3932,
+ 9558, 8830, 3637, 5542, 145, 5179, 8595, 3707, 10530, 355, 3382, 4231, 9741,
+ 1207, 9041, 7012, 1168, 10146, 11224, 4645, 11885, 10911, 10377, 435, 7952,
+ 4096, 493, 9908, 6845, 6039, 2422, 2187, 9723, 8643, 9852, 9302, 6022, 7278,
+ 1002, 4284, 5088, 1607, 7313, 875, 8509, 9430, 1045, 2481, 5012, 7428, 354,
+ 6591, 9377, 11847, 2401, 1067, 7188, 11516, 390, 8511, 8456, 7270, 545, 8585,
+ 9611, 12047, 1537, 4143, 4714, 4885, 1017, 5084, 1632, 3066, 27, 1440, 8526,
+ 9273, 12046, 11618, 9289, 3400, 9890, 3136, 7098, 8758, 11813, 7384, 3985,
+ 11869, 6730, 10745, 10111, 2249, 4048, 2884, 11136, 2126, 1630, 9103, 5407,
+ 2686, 9042, 2969, 8311, 9424, 9919, 8779, 5332, 10626, 1777, 4654, 10863,
+ 7351, 3636, 9585, 5291, 8374, 2166, 4919, 12176, 9140, 12129, 7852, 12286,
+ 4895, 10805, 2780, 5195, 2305, 7247, 9644, 4053, 10600, 3364, 3271, 4057,
+ 4414, 9442, 7917, 2174
+ };
+
+ static const uint16_t psis_bitrev_montgomery[PARAM_N] =
+ {
+ 4075, 6974, 7373, 7965, 3262, 5079, 522, 2169, 6364, 1018, 1041, 8775, 2344,
+ 11011, 5574, 1973, 4536, 1050, 6844, 3860, 3818, 6118, 2683, 1190, 4789,
+ 7822, 7540, 6752, 5456, 4449, 3789, 12142, 11973, 382, 3988, 468, 6843, 5339,
+ 6196, 3710, 11316, 1254, 5435, 10930, 3998, 10256, 10367, 3879, 11889, 1728,
+ 6137, 4948, 5862, 6136, 3643, 6874, 8724, 654, 10302, 1702, 7083, 6760, 56,
+ 3199, 9987, 605, 11785, 8076, 5594, 9260, 6403, 4782, 6212, 4624, 9026, 8689,
+ 4080, 11868, 6221, 3602, 975, 8077, 8851, 9445, 5681, 3477, 1105, 142, 241,
+ 12231, 1003, 3532, 5009, 1956, 6008, 11404, 7377, 2049, 10968, 12097, 7591,
+ 5057, 3445, 4780, 2920, 7048, 3127, 8120, 11279, 6821, 11502, 8807, 12138,
+ 2127, 2839, 3957, 431, 1579, 6383, 9784, 5874, 677, 3336, 6234, 2766, 1323,
+ 9115, 12237, 2031, 6956, 6413, 2281, 3969, 3991, 12133, 9522, 4737, 10996,
+ 4774, 5429, 11871, 3772, 453, 5908, 2882, 1805, 2051, 1954, 11713, 3963,
+ 2447, 6142, 8174, 3030, 1843, 2361, 12071, 2908, 3529, 3434, 3202, 7796,
+ 2057, 5369, 11939, 1512, 6906, 10474, 11026, 49, 10806, 5915, 1489, 9789,
+ 5942, 10706, 10431, 7535, 426, 8974, 3757, 10314, 9364, 347, 5868, 9551,
+ 9634, 6554, 10596, 9280, 11566, 174, 2948, 2503, 6507, 10723, 11606, 2459,
+ 64, 3656, 8455, 5257, 5919, 7856, 1747, 9166, 5486, 9235, 6065, 835, 3570,
+ 4240, 11580, 4046, 10970, 9139, 1058, 8210, 11848, 922, 7967, 1958, 10211,
+ 1112, 3728, 4049, 11130, 5990, 1404, 325, 948, 11143, 6190, 295, 11637, 5766,
+ 8212, 8273, 2919, 8527, 6119, 6992, 8333, 1360, 2555, 6167, 1200, 7105, 7991,
+ 3329, 9597, 12121, 5106, 5961, 10695, 10327, 3051, 9923, 4896, 9326, 81,
+ 3091, 1000, 7969, 4611, 726, 1853, 12149, 4255, 11112, 2768, 10654, 1062,
+ 2294, 3553, 4805, 2747, 4846, 8577, 9154, 1170, 2319, 790, 11334, 9275, 9088,
+ 1326, 5086, 9094, 6429, 11077, 10643, 3504, 3542, 8668, 9744, 1479, 1, 8246,
+ 7143, 11567, 10984, 4134, 5736, 4978, 10938, 5777, 8961, 4591, 5728, 6461,
+ 5023, 9650, 7468, 949, 9664, 2975, 11726, 2744, 9283, 10092, 5067, 12171,
+ 2476, 3748, 11336, 6522, 827, 9452, 5374, 12159, 7935, 3296, 3949, 9893,
+ 4452, 10908, 2525, 3584, 8112, 8011, 10616, 4989, 6958, 11809, 9447, 12280,
+ 1022, 11950, 9821, 11745, 5791, 5092, 2089, 9005, 2881, 3289, 2013, 9048,
+ 729, 7901, 1260, 5755, 4632, 11955, 2426, 10593, 1428, 4890, 5911, 3932,
+ 9558, 8830, 3637, 5542, 145, 5179, 8595, 3707, 10530, 355, 3382, 4231, 9741,
+ 1207, 9041, 7012, 1168, 10146, 11224, 4645, 11885, 10911, 10377, 435, 7952,
+ 4096, 493, 9908, 6845, 6039, 2422, 2187, 9723, 8643, 9852, 9302, 6022, 7278,
+ 1002, 4284, 5088, 1607, 7313, 875, 8509, 9430, 1045, 2481, 5012, 7428, 354,
+ 6591, 9377, 11847, 2401, 1067, 7188, 11516, 390, 8511, 8456, 7270, 545, 8585,
+ 9611, 12047, 1537, 4143, 4714, 4885, 1017, 5084, 1632, 3066, 27, 1440, 8526,
+ 9273, 12046, 11618, 9289, 3400, 9890, 3136, 7098, 8758, 11813, 7384, 3985,
+ 11869, 6730, 10745, 10111, 2249, 4048, 2884, 11136, 2126, 1630, 9103, 5407,
+ 2686, 9042, 2969, 8311, 9424, 9919, 8779, 5332, 10626, 1777, 4654, 10863,
+ 7351, 3636, 9585, 5291, 8374, 2166, 4919, 12176, 9140, 12129, 7852, 12286,
+ 4895, 10805, 2780, 5195, 2305, 7247, 9644, 4053, 10600, 3364, 3271, 4057,
+ 4414, 9442, 7917, 2174, 3947, 11951, 2455, 6599, 10545, 10975, 3654, 2894,
+ 7681, 7126, 7287, 12269, 4119, 3343, 2151, 1522, 7174, 7350, 11041, 2442,
+ 2148, 5959, 6492, 8330, 8945, 5598, 3624, 10397, 1325, 6565, 1945, 11260,
+ 10077, 2674, 3338, 3276, 11034, 506, 6505, 1392, 5478, 8778, 1178, 2776,
+ 3408, 10347, 11124, 2575, 9489, 12096, 6092, 10058, 4167, 6085, 923, 11251,
+ 11912, 4578, 10669, 11914, 425, 10453, 392, 10104, 8464, 4235, 8761, 7376,
+ 2291, 3375, 7954, 8896, 6617, 7790, 1737, 11667, 3982, 9342, 6680, 636, 6825,
+ 7383, 512, 4670, 2900, 12050, 7735, 994, 1687, 11883, 7021, 146, 10485, 1403,
+ 5189, 6094, 2483, 2054, 3042, 10945, 3981, 10821, 11826, 8882, 8151, 180,
+ 9600, 7684, 5219, 10880, 6780, 204, 11232, 2600, 7584, 3121, 3017, 11053,
+ 7814, 7043, 4251, 4739, 11063, 6771, 7073, 9261, 2360, 11925, 1928, 11825,
+ 8024, 3678, 3205, 3359, 11197, 5209, 8581, 3238, 8840, 1136, 9363, 1826,
+ 3171, 4489, 7885, 346, 2068, 1389, 8257, 3163, 4840, 6127, 8062, 8921, 612,
+ 4238, 10763, 8067, 125, 11749, 10125, 5416, 2110, 716, 9839, 10584, 11475,
+ 11873, 3448, 343, 1908, 4538, 10423, 7078, 4727, 1208, 11572, 3589, 2982,
+ 1373, 1721, 10753, 4103, 2429, 4209, 5412, 5993, 9011, 438, 3515, 7228, 1218,
+ 8347, 5232, 8682, 1327, 7508, 4924, 448, 1014, 10029, 12221, 4566, 5836,
+ 12229, 2717, 1535, 3200, 5588, 5845, 412, 5102, 7326, 3744, 3056, 2528, 7406,
+ 8314, 9202, 6454, 6613, 1417, 10032, 7784, 1518, 3765, 4176, 5063, 9828,
+ 2275, 6636, 4267, 6463, 2065, 7725, 3495, 8328, 8755, 8144, 10533, 5966,
+ 12077, 9175, 9520, 5596, 6302, 8400, 579, 6781, 11014, 5734, 11113, 11164,
+ 4860, 1131, 10844, 9068, 8016, 9694, 3837, 567, 9348, 7000, 6627, 7699, 5082,
+ 682, 11309, 5207, 4050, 7087, 844, 7434, 3769, 293, 9057, 6940, 9344, 10883,
+ 2633, 8190, 3944, 5530, 5604, 3480, 2171, 9282, 11024, 2213, 8136, 3805, 767,
+ 12239, 216, 11520, 6763, 10353, 7, 8566, 845, 7235, 3154, 4360, 3285, 10268,
+ 2832, 3572, 1282, 7559, 3229, 8360, 10583, 6105, 3120, 6643, 6203, 8536,
+ 8348, 6919, 3536, 9199, 10891, 11463, 5043, 1658, 5618, 8787, 5789, 4719,
+ 751, 11379, 6389, 10783, 3065, 7806, 6586, 2622, 5386, 510, 7628, 6921, 578,
+ 10345, 11839, 8929, 4684, 12226, 7154, 9916, 7302, 8481, 3670, 11066, 2334,
+ 1590, 7878, 10734, 1802, 1891, 5103, 6151, 8820, 3418, 7846, 9951, 4693, 417,
+ 9996, 9652, 4510, 2946, 5461, 365, 881, 1927, 1015, 11675, 11009, 1371,
+ 12265, 2485, 11385, 5039, 6742, 8449, 1842, 12217, 8176, 9577, 4834, 7937,
+ 9461, 2643, 11194, 3045, 6508, 4094, 3451, 7911, 11048, 5406, 4665, 3020,
+ 6616, 11345, 7519, 3669, 5287, 1790, 7014, 5410, 11038, 11249, 2035, 6125,
+ 10407, 4565, 7315, 5078, 10506, 2840, 2478, 9270, 4194, 9195, 4518, 7469,
+ 1160, 6878, 2730, 10421, 10036, 1734, 3815, 10939, 5832, 10595, 10759, 4423,
+ 8420, 9617, 7119, 11010, 11424, 9173, 189, 10080, 10526, 3466, 10588, 7592,
+ 3578, 11511, 7785, 9663, 530, 12150, 8957, 2532, 3317, 9349, 10243, 1481,
+ 9332, 3454, 3758, 7899, 4218, 2593, 11410, 2276, 982, 6513, 1849, 8494, 9021,
+ 4523, 7988, 8, 457, 648, 150, 8000, 2307, 2301, 874, 5650, 170, 9462, 2873,
+ 9855, 11498, 2535, 11169, 5808, 12268, 9687, 1901, 7171, 11787, 3846, 1573,
+ 6063, 3793, 466, 11259, 10608, 3821, 6320, 4649, 6263, 2929
+ };
+
+ mul_coefficients(r->coeffs, psis_bitrev_montgomery);
+ ntt(r->coeffs, omegas_montgomery);
+ }
+
+inline void bitrev_vector(uint16_t* poly)
+ {
+ static const uint16_t bitrev_table[1024] =
+ {
+ 0, 512, 256, 768, 128, 640, 384, 896, 64, 576, 320, 832, 192, 704, 448, 960, 32, 544, 288, 800, 160, 672, 416, 928, 96, 608, 352, 864, 224, 736, 480, 992,
+ 16, 528, 272, 784, 144, 656, 400, 912, 80, 592, 336, 848, 208, 720, 464, 976, 48, 560, 304, 816, 176, 688, 432, 944, 112, 624, 368, 880, 240, 752, 496, 1008,
+ 8, 520, 264, 776, 136, 648, 392, 904, 72, 584, 328, 840, 200, 712, 456, 968, 40, 552, 296, 808, 168, 680, 424, 936, 104, 616, 360, 872, 232, 744, 488, 1000,
+ 24, 536, 280, 792, 152, 664, 408, 920, 88, 600, 344, 856, 216, 728, 472, 984, 56, 568, 312, 824, 184, 696, 440, 952, 120, 632, 376, 888, 248, 760, 504, 1016,
+ 4, 516, 260, 772, 132, 644, 388, 900, 68, 580, 324, 836, 196, 708, 452, 964, 36, 548, 292, 804, 164, 676, 420, 932, 100, 612, 356, 868, 228, 740, 484, 996,
+ 20, 532, 276, 788, 148, 660, 404, 916, 84, 596, 340, 852, 212, 724, 468, 980, 52, 564, 308, 820, 180, 692, 436, 948, 116, 628, 372, 884, 244, 756, 500, 1012,
+ 12, 524, 268, 780, 140, 652, 396, 908, 76, 588, 332, 844, 204, 716, 460, 972, 44, 556, 300, 812, 172, 684, 428, 940, 108, 620, 364, 876, 236, 748, 492, 1004,
+ 28, 540, 284, 796, 156, 668, 412, 924, 92, 604, 348, 860, 220, 732, 476, 988, 60, 572, 316, 828, 188, 700, 444, 956, 124, 636, 380, 892, 252, 764, 508, 1020,
+ 2, 514, 258, 770, 130, 642, 386, 898, 66, 578, 322, 834, 194, 706, 450, 962, 34, 546, 290, 802, 162, 674, 418, 930, 98, 610, 354, 866, 226, 738, 482, 994,
+ 18, 530, 274, 786, 146, 658, 402, 914, 82, 594, 338, 850, 210, 722, 466, 978, 50, 562, 306, 818, 178, 690, 434, 946, 114, 626, 370, 882, 242, 754, 498, 1010,
+ 10, 522, 266, 778, 138, 650, 394, 906, 74, 586, 330, 842, 202, 714, 458, 970, 42, 554, 298, 810, 170, 682, 426, 938, 106, 618, 362, 874, 234, 746, 490, 1002,
+ 26, 538, 282, 794, 154, 666, 410, 922, 90, 602, 346, 858, 218, 730, 474, 986, 58, 570, 314, 826, 186, 698, 442, 954, 122, 634, 378, 890, 250, 762, 506, 1018,
+ 6, 518, 262, 774, 134, 646, 390, 902, 70, 582, 326, 838, 198, 710, 454, 966, 38, 550, 294, 806, 166, 678, 422, 934, 102, 614, 358, 870, 230, 742, 486, 998,
+ 22, 534, 278, 790, 150, 662, 406, 918, 86, 598, 342, 854, 214, 726, 470, 982, 54, 566, 310, 822, 182, 694, 438, 950, 118, 630, 374, 886, 246, 758, 502, 1014,
+ 14, 526, 270, 782, 142, 654, 398, 910, 78, 590, 334, 846, 206, 718, 462, 974, 46, 558, 302, 814, 174, 686, 430, 942, 110, 622, 366, 878, 238, 750, 494, 1006,
+ 30, 542, 286, 798, 158, 670, 414, 926, 94, 606, 350, 862, 222, 734, 478, 990, 62, 574, 318, 830, 190, 702, 446, 958, 126, 638, 382, 894, 254, 766, 510, 1022,
+ 1, 513, 257, 769, 129, 641, 385, 897, 65, 577, 321, 833, 193, 705, 449, 961, 33, 545, 289, 801, 161, 673, 417, 929, 97, 609, 353, 865, 225, 737, 481, 993,
+ 17, 529, 273, 785, 145, 657, 401, 913, 81, 593, 337, 849, 209, 721, 465, 977, 49, 561, 305, 817, 177, 689, 433, 945, 113, 625, 369, 881, 241, 753, 497, 1009,
+ 9, 521, 265, 777, 137, 649, 393, 905, 73, 585, 329, 841, 201, 713, 457, 969, 41, 553, 297, 809, 169, 681, 425, 937, 105, 617, 361, 873, 233, 745, 489, 1001,
+ 25, 537, 281, 793, 153, 665, 409, 921, 89, 601, 345, 857, 217, 729, 473, 985, 57, 569, 313, 825, 185, 697, 441, 953, 121, 633, 377, 889, 249, 761, 505, 1017,
+ 5, 517, 261, 773, 133, 645, 389, 901, 69, 581, 325, 837, 197, 709, 453, 965, 37, 549, 293, 805, 165, 677, 421, 933, 101, 613, 357, 869, 229, 741, 485, 997,
+ 21, 533, 277, 789, 149, 661, 405, 917, 85, 597, 341, 853, 213, 725, 469, 981, 53, 565, 309, 821, 181, 693, 437, 949, 117, 629, 373, 885, 245, 757, 501, 1013,
+ 13, 525, 269, 781, 141, 653, 397, 909, 77, 589, 333, 845, 205, 717, 461, 973, 45, 557, 301, 813, 173, 685, 429, 941, 109, 621, 365, 877, 237, 749, 493, 1005,
+ 29, 541, 285, 797, 157, 669, 413, 925, 93, 605, 349, 861, 221, 733, 477, 989, 61, 573, 317, 829, 189, 701, 445, 957, 125, 637, 381, 893, 253, 765, 509, 1021,
+ 3, 515, 259, 771, 131, 643, 387, 899, 67, 579, 323, 835, 195, 707, 451, 963, 35, 547, 291, 803, 163, 675, 419, 931, 99, 611, 355, 867, 227, 739, 483, 995,
+ 19, 531, 275, 787, 147, 659, 403, 915, 83, 595, 339, 851, 211, 723, 467, 979, 51, 563, 307, 819, 179, 691, 435, 947, 115, 627, 371, 883, 243, 755, 499, 1011,
+ 11, 523, 267, 779, 139, 651, 395, 907, 75, 587, 331, 843, 203, 715, 459, 971, 43, 555, 299, 811, 171, 683, 427, 939, 107, 619, 363, 875, 235, 747, 491, 1003,
+ 27, 539, 283, 795, 155, 667, 411, 923, 91, 603, 347, 859, 219, 731, 475, 987, 59, 571, 315, 827, 187, 699, 443, 955, 123, 635, 379, 891, 251, 763, 507, 1019,
+ 7, 519, 263, 775, 135, 647, 391, 903, 71, 583, 327, 839, 199, 711, 455, 967, 39, 551, 295, 807, 167, 679, 423, 935, 103, 615, 359, 871, 231, 743, 487, 999,
+ 23, 535, 279, 791, 151, 663, 407, 919, 87, 599, 343, 855, 215, 727, 471, 983, 55, 567, 311, 823, 183, 695, 439, 951, 119, 631, 375, 887, 247, 759, 503, 1015,
+ 15, 527, 271, 783, 143, 655, 399, 911, 79, 591, 335, 847, 207, 719, 463, 975, 47, 559, 303, 815, 175, 687, 431, 943, 111, 623, 367, 879, 239, 751, 495, 1007,
+ 31, 543, 287, 799, 159, 671, 415, 927, 95, 607, 351, 863, 223, 735, 479, 991, 63, 575, 319, 831, 191, 703, 447, 959, 127, 639, 383, 895, 255, 767, 511, 1023
+ };
+
+ for(size_t i = 0; i < PARAM_N; i++)
+ {
+ const uint16_t r = bitrev_table[i];
+ if(i < r)
+ {
+ const uint16_t tmp = poly[i];
+ poly[i] = poly[r];
+ poly[r] = tmp;
+ }
+ }
+ }
+
+inline void poly_invntt(poly* r)
+ {
+ static const uint16_t omegas_inv_montgomery[PARAM_N/2] =
+ {
+ 4075, 5315, 4324, 4916, 10120, 11767, 7210, 9027, 10316, 6715, 1278, 9945,
+ 3514, 11248, 11271, 5925, 147, 8500, 7840, 6833, 5537, 4749, 4467, 7500,
+ 11099, 9606, 6171, 8471, 8429, 5445, 11239, 7753, 9090, 12233, 5529, 5206,
+ 10587, 1987, 11635, 3565, 5415, 8646, 6153, 6427, 7341, 6152, 10561, 400,
+ 8410, 1922, 2033, 8291, 1359, 6854, 11035, 973, 8579, 6093, 6950, 5446,
+ 11821, 8301, 11907, 316, 52, 3174, 10966, 9523, 6055, 8953, 11612, 6415,
+ 2505, 5906, 10710, 11858, 8332, 9450, 10162, 151, 3482, 787, 5468, 1010,
+ 4169, 9162, 5241, 9369, 7509, 8844, 7232, 4698, 192, 1321, 10240, 4912, 885,
+ 6281, 10333, 7280, 8757, 11286, 58, 12048, 12147, 11184, 8812, 6608, 2844,
+ 3438, 4212, 11314, 8687, 6068, 421, 8209, 3600, 3263, 7665, 6077, 7507, 5886,
+ 3029, 6695, 4213, 504, 11684, 2302, 1962, 1594, 6328, 7183, 168, 2692, 8960,
+ 4298, 5184, 11089, 6122, 9734, 10929, 3956, 5297, 6170, 3762, 9370, 4016,
+ 4077, 6523, 652, 11994, 6099, 1146, 11341, 11964, 10885, 6299, 1159, 8240,
+ 8561, 11177, 2078, 10331, 4322, 11367, 441, 4079, 11231, 3150, 1319, 8243,
+ 709, 8049, 8719, 11454, 6224, 3054, 6803, 3123, 10542, 4433, 6370, 7032,
+ 3834, 8633, 12225, 9830, 683, 1566, 5782, 9786, 9341, 12115, 723, 3009, 1693,
+ 5735, 2655, 2738, 6421, 11942, 2925, 1975, 8532, 3315, 11863, 4754, 1858,
+ 1583, 6347, 2500, 10800, 6374, 1483, 12240, 1263, 1815, 5383, 10777, 350,
+ 6920, 10232, 4493, 9087, 8855, 8760, 9381, 218, 9928, 10446, 9259, 4115,
+ 6147, 9842, 8326, 576, 10335, 10238, 10484, 9407, 6381, 11836, 8517, 418,
+ 6860, 7515, 1293, 7552, 2767, 156, 8298, 8320, 10008, 5876, 5333, 10258,
+ 10115, 4372, 2847, 7875, 8232, 9018, 8925, 1689, 8236, 2645, 5042, 9984,
+ 7094, 9509, 1484, 7394, 3, 4437, 160, 3149, 113, 7370, 10123, 3915, 6998,
+ 2704, 8653, 4938, 1426, 7635, 10512, 1663, 6957, 3510, 2370, 2865, 3978,
+ 9320, 3247, 9603, 6882, 3186, 10659, 10163, 1153, 9405, 8241, 10040, 2178,
+ 1544, 5559, 420, 8304, 4905, 476, 3531, 5191, 9153, 2399, 8889, 3000, 671,
+ 243, 3016, 3763, 10849, 12262, 9223, 10657, 7205, 11272, 7404, 7575, 8146,
+ 10752, 242, 2678, 3704, 11744, 5019, 3833, 3778, 11899, 773, 5101, 11222,
+ 9888, 442, 2912, 5698, 11935, 4861, 7277, 9808, 11244, 2859, 3780, 11414,
+ 4976, 10682, 7201, 8005, 11287, 5011, 6267, 2987, 2437, 3646, 2566, 10102,
+ 9867, 6250, 5444, 2381, 11796, 8193, 4337, 11854, 1912, 1378, 404, 7644,
+ 1065, 2143, 11121, 5277, 3248, 11082, 2548, 8058, 8907, 11934, 1759, 8582,
+ 3694, 7110, 12144, 6747, 8652, 3459, 2731, 8357, 6378, 7399, 10861, 1696,
+ 9863, 334, 7657, 6534, 11029, 4388, 11560, 3241, 10276, 9000, 9408, 3284,
+ 10200, 7197, 6498, 544, 2468, 339, 11267, 9, 2842, 480, 5331, 7300, 1673,
+ 4278, 4177, 8705, 9764, 1381, 7837, 2396, 8340, 8993, 4354, 130, 6915, 2837,
+ 11462, 5767, 953, 8541, 9813, 118, 7222, 2197, 3006, 9545, 563, 9314, 2625,
+ 11340, 4821, 2639, 7266, 5828, 6561, 7698, 3328, 6512, 1351, 7311, 6553,
+ 8155, 1305, 722, 5146, 4043, 12288, 10810, 2545, 3621, 8747, 8785, 1646,
+ 1212, 5860, 3195, 7203, 10963, 3201, 3014, 955, 11499, 9970, 11119, 3135,
+ 3712, 7443, 9542, 7484, 8736, 9995, 11227, 1635, 9521, 1177, 8034, 140,
+ 10436, 11563, 7678, 4320, 11289, 9198, 12208, 2963, 7393, 2366, 9238
+ };
+
+ static const uint16_t psis_inv_montgomery[PARAM_N] =
+ {
+ 256, 10570, 1510, 7238, 1034, 7170, 6291, 7921, 11665, 3422, 4000, 2327,
+ 2088, 5565, 795, 10647, 1521, 5484, 2539, 7385, 1055, 7173, 8047, 11683,
+ 1669, 1994, 3796, 5809, 4341, 9398, 11876, 12230, 10525, 12037, 12253, 3506,
+ 4012, 9351, 4847, 2448, 7372, 9831, 3160, 2207, 5582, 2553, 7387, 6322, 9681,
+ 1383, 10731, 1533, 219, 5298, 4268, 7632, 6357, 9686, 8406, 4712, 9451,
+ 10128, 4958, 5975, 11387, 8649, 11769, 6948, 11526, 12180, 1740, 10782, 6807,
+ 2728, 7412, 4570, 4164, 4106, 11120, 12122, 8754, 11784, 3439, 5758, 11356,
+ 6889, 9762, 11928, 1704, 1999, 10819, 12079, 12259, 7018, 11536, 1648, 1991,
+ 2040, 2047, 2048, 10826, 12080, 8748, 8272, 8204, 1172, 1923, 7297, 2798,
+ 7422, 6327, 4415, 7653, 6360, 11442, 12168, 7005, 8023, 9924, 8440, 8228,
+ 2931, 7441, 1063, 3663, 5790, 9605, 10150, 1450, 8985, 11817, 10466, 10273,
+ 12001, 3470, 7518, 1074, 1909, 7295, 9820, 4914, 702, 5367, 7789, 8135, 9940,
+ 1420, 3714, 11064, 12114, 12264, 1752, 5517, 9566, 11900, 1700, 3754, 5803,
+ 829, 1874, 7290, 2797, 10933, 5073, 7747, 8129, 6428, 6185, 11417, 1631, 233,
+ 5300, 9535, 10140, 11982, 8734, 8270, 2937, 10953, 8587, 8249, 2934, 9197,
+ 4825, 5956, 4362, 9401, 1343, 3703, 529, 10609, 12049, 6988, 6265, 895, 3639,
+ 4031, 4087, 4095, 585, 10617, 8539, 4731, 4187, 9376, 3095, 9220, 10095,
+ 10220, 1460, 10742, 12068, 1724, 5513, 11321, 6884, 2739, 5658, 6075, 4379,
+ 11159, 10372, 8504, 4726, 9453, 3106, 7466, 11600, 10435, 8513, 9994, 8450,
+ 9985, 3182, 10988, 8592, 2983, 9204, 4826, 2445, 5616, 6069, 867, 3635, 5786,
+ 11360, 5134, 2489, 10889, 12089, 1727, 7269, 2794, 9177, 1311, 5454, 9557,
+ 6632, 2703, 9164, 10087, 1441, 3717, 531, 3587, 2268, 324, 5313, 759, 1864,
+ 5533, 2546, 7386, 9833, 8427, 4715, 11207, 1601, 7251, 4547, 11183, 12131,
+ 1733, 10781, 10318, 1474, 10744, 5046, 4232, 11138, 10369, 6748, 964, 7160,
+ 4534, 7670, 8118, 8182, 4680, 11202, 6867, 981, 8918, 1274, 182, 26, 7026,
+ 8026, 11680, 12202, 10521, 1503, 7237, 4545, 5916, 9623, 8397, 11733, 10454,
+ 3249, 9242, 6587, 941, 1890, 270, 10572, 6777, 9746, 6659, 6218, 6155, 6146,
+ 878, 1881, 7291, 11575, 12187, 1741, 7271, 8061, 11685, 6936, 4502, 9421,
+ 4857, 4205, 7623, 1089, 10689, 1527, 8996, 10063, 11971, 10488, 6765, 2722,
+ 3900, 9335, 11867, 6962, 11528, 5158, 4248, 4118, 5855, 2592, 5637, 6072,
+ 2623, 7397, 8079, 9932, 4930, 5971, 853, 3633, 519, 8852, 11798, 3441, 11025,
+ 1575, 225, 8810, 11792, 12218, 3501, 9278, 3081, 9218, 4828, 7712, 8124,
+ 11694, 12204, 3499, 4011, 573, 3593, 5780, 7848, 9899, 10192, 1456, 208,
+ 7052, 2763, 7417, 11593, 10434, 12024, 8740, 11782, 10461, 3250, 5731, 7841,
+ 9898, 1414, 202, 3540, 7528, 2831, 2160, 10842, 5060, 4234, 4116, 588, 84,
+ 12, 7024, 2759, 9172, 6577, 11473, 1639, 9012, 3043, 7457, 6332, 11438, 1634,
+ 1989, 9062, 11828, 8712, 11778, 12216, 10523, 6770, 9745, 10170, 4964, 9487,
+ 6622, 946, 8913, 6540, 6201, 4397, 9406, 8366, 9973, 8447, 8229, 11709, 8695,
+ 10020, 3187, 5722, 2573, 10901, 6824, 4486, 4152, 9371, 8361, 2950, 2177,
+ 311, 1800, 9035, 8313, 11721, 3430, 490, 70, 10, 1757, 251, 3547, 7529,
+ 11609, 3414, 7510, 4584, 4166, 9373, 1339, 5458, 7802, 11648, 1664, 7260,
+ 9815, 10180, 6721, 9738, 10169, 8475, 8233, 9954, 1422, 8981, 1283, 5450,
+ 11312, 1616, 3742, 11068, 10359, 4991, 713, 3613, 9294, 8350, 4704, 672, 96,
+ 7036, 9783, 11931, 3460, 5761, 823, 10651, 12055, 10500, 1500, 5481, 783,
+ 3623, 11051, 8601, 8251, 8201, 11705, 10450, 5004, 4226, 7626, 2845, 2162,
+ 3820, 7568, 9859, 3164, 452, 10598, 1514, 5483, 6050, 6131, 4387, 7649, 8115,
+ 6426, 918, 8909, 8295, 1185, 5436, 11310, 8638, 1234, 5443, 11311, 5127,
+ 2488, 2111, 10835, 5059, 7745, 2862, 3920, 560, 80, 1767, 2008, 3798, 11076,
+ 6849, 2734, 10924, 12094, 8750, 1250, 10712, 6797, 971, 7161, 1023, 8924,
+ 4786, 7706, 4612, 4170, 7618, 6355, 4419, 5898, 11376, 10403, 10264, 6733,
+ 4473, 639, 5358, 2521, 9138, 3061, 5704, 4326, 618, 5355, 765, 5376, 768,
+ 7132, 4530, 9425, 3102, 9221, 6584, 11474, 10417, 10266, 12000, 6981, 6264,
+ 4406, 2385, 7363, 4563, 4163, 7617, 9866, 3165, 9230, 11852, 10471, 5007,
+ 5982, 11388, 5138, 734, 3616, 11050, 12112, 6997, 11533, 12181, 10518, 12036,
+ 3475, 2252, 7344, 9827, 4915, 9480, 6621, 4457, 7659, 9872, 6677, 4465, 4149,
+ 7615, 4599, 657, 3605, 515, 10607, 6782, 4480, 640, 1847, 3775, 5806, 2585,
+ 5636, 9583, 1369, 10729, 8555, 10000, 11962, 5220, 7768, 8132, 8184, 9947,
+ 1421, 203, 29, 8782, 11788, 1684, 10774, 10317, 4985, 9490, 8378, 4708,
+ 11206, 5112, 5997, 7879, 11659, 12199, 8765, 10030, 4944, 5973, 6120, 6141,
+ 6144, 7900, 11662, 1666, 238, 34, 3516, 5769, 9602, 8394, 9977, 6692, 956,
+ 10670, 6791, 9748, 11926, 8726, 11780, 5194, 742, 106, 8793, 10034, 3189,
+ 10989, 5081, 4237, 5872, 4350, 2377, 10873, 6820, 6241, 11425, 10410, 10265,
+ 3222, 5727, 9596, 4882, 2453, 2106, 3812, 11078, 12116, 5242, 4260, 11142,
+ 8614, 11764, 12214, 5256, 4262, 4120, 11122, 5100, 11262, 5120, 2487, 5622,
+ 9581, 8391, 8221, 2930, 10952, 12098, 6995, 6266, 9673, 4893, 699, 3611,
+ 4027, 5842, 11368, 1624, 232, 8811, 8281, 1183, 169, 8802, 3013, 2186, 5579,
+ 797, 3625, 4029, 11109, 1587, 7249, 11569, 8675, 6506, 2685, 10917, 12093,
+ 12261, 12285, 1755, 7273, 1039, 1904, 272, 3550, 9285, 3082, 5707, 6082,
+ 4380, 7648, 11626, 5172, 4250, 9385, 8363, 8217, 4685, 5936, 848, 8899, 6538,
+ 934, 1889, 3781, 9318, 10109, 10222, 6727, 961, 5404, 772, 5377, 9546, 8386,
+ 1198, 8949, 3034, 2189, 7335, 4559, 5918, 2601, 10905, 5069, 9502, 3113,
+ 7467, 8089, 11689, 5181, 9518, 8382, 2953, 3933, 4073, 4093, 7607, 8109,
+ 2914, 5683, 4323, 11151, 1593, 10761, 6804, 972, 3650, 2277, 5592, 4310,
+ 7638, 9869, 4921, 703, 1856, 9043, 4803, 9464, 1352, 8971, 11815, 5199, 7765,
+ 6376, 4422, 7654, 2849, 407, 8836, 6529, 7955, 2892, 9191, 1313, 10721,
+ 12065, 12257, 1751, 9028, 8312, 2943, 2176, 3822, 546, 78, 8789, 11789,
+ 10462, 12028, 6985, 4509, 9422, 1346, 5459, 4291, 613, 10621, 6784, 9747,
+ 3148, 7472, 2823, 5670, 810, 7138, 8042, 4660, 7688, 6365, 6176, 6149, 2634,
+ 5643, 9584, 10147, 11983, 5223, 9524, 11894, 10477, 8519, 1217, 3685, 2282,
+ 326, 10580, 3267, 7489, 4581, 2410, 5611, 11335, 6886, 8006, 8166, 11700,
+ 3427, 11023, 8597, 10006, 3185, 455, 65, 5276, 7776, 4622, 5927, 7869, 9902,
+ 11948, 5218, 2501, 5624, 2559, 10899, 1557, 1978, 10816, 10323, 8497, 4725,
+ 675, 1852, 10798, 12076, 10503, 3256, 9243, 3076, 2195, 10847, 12083, 10504,
+ 12034, 10497
+ };
+
+ bitrev_vector(r->coeffs);
+ ntt(r->coeffs, omegas_inv_montgomery);
+ mul_coefficients(r->coeffs, psis_inv_montgomery);
+ }
+
+inline void encode_a(uint8_t* r, const poly* pk, const uint8_t* seed)
+ {
+ poly_tobytes(r, pk);
+ for(size_t i = 0; i < NEWHOPE_SEED_BYTES; i++)
+ {
+ r[NEWHOPE_POLY_BYTES+i] = seed[i];
+ }
+ }
+
+inline void decode_a(poly* pk, uint8_t* seed, const uint8_t* r)
+ {
+ poly_frombytes(pk, r);
+ for(size_t i = 0; i < NEWHOPE_SEED_BYTES; i++)
+ {
+ seed[i] = r[NEWHOPE_POLY_BYTES+i];
+ }
+ }
+
+inline void encode_b(uint8_t* r, const poly* b, const poly* c)
+ {
+ poly_tobytes(r, b);
+ for(size_t i = 0; i < PARAM_N/4; i++)
+ {
+ r[NEWHOPE_POLY_BYTES+i] = static_cast<uint8_t>(c->coeffs[4*i] |
+ (c->coeffs[4*i+1] << 2) |
+ (c->coeffs[4*i+2] << 4) |
+ (c->coeffs[4*i+3] << 6));
+ }
+ }
+
+inline void decode_b(poly* b, poly* c, const uint8_t* r)
+ {
+ poly_frombytes(b, r);
+ for(size_t i = 0; i < PARAM_N/4; i++)
+ {
+ c->coeffs[4*i+0] = r[NEWHOPE_POLY_BYTES+i] & 0x03;
+ c->coeffs[4*i+1] = (r[NEWHOPE_POLY_BYTES+i] >> 2) & 0x03;
+ c->coeffs[4*i+2] = (r[NEWHOPE_POLY_BYTES+i] >> 4) & 0x03;
+ c->coeffs[4*i+3] = (r[NEWHOPE_POLY_BYTES+i] >> 6);
+ }
+ }
+
+inline int32_t ct_abs(int32_t v)
+ {
+ int32_t mask = v >> 31;
+ return (v ^ mask) - mask;
+ }
+
+inline int32_t f(int32_t* v0, int32_t* v1, int32_t x)
+ {
+ int32_t xit, t, r, b;
+
+ // Next 6 lines compute t = x/PARAM_Q;
+ b = x*2730;
+ t = b >> 25;
+ b = x - t*12289;
+ b = 12288 - b;
+ b >>= 31;
+ t -= b;
+
+ r = t & 1;
+ xit = (t>>1);
+ *v0 = xit+r; // v0 = round(x/(2*PARAM_Q))
+
+ t -= 1;
+ r = t & 1;
+ *v1 = (t>>1)+r;
+
+ return ct_abs(x-((*v0)*2*PARAM_Q));
+ }
+
+inline void helprec(poly* c, const poly* v, RandomNumberGenerator& rng)
+ {
+ uint8_t rand[32];
+
+ rng.randomize(rand, 32);
+
+ for(size_t i = 0; i < 256; i++)
+ {
+ int32_t v0[4], v1[4];
+ uint8_t rbit = (rand[i>>3] >> (i&7)) & 1;
+ int32_t k;
+
+ k = f(v0+0, v1+0, 8*v->coeffs[ 0+i] + 4*rbit);
+ k += f(v0+1, v1+1, 8*v->coeffs[256+i] + 4*rbit);
+ k += f(v0+2, v1+2, 8*v->coeffs[512+i] + 4*rbit);
+ k += f(v0+3, v1+3, 8*v->coeffs[768+i] + 4*rbit);
+
+ k = (2*PARAM_Q-1-k) >> 31;
+
+ int32_t v_tmp[4];
+ v_tmp[0] = ((~k) & v0[0]) ^ (k & v1[0]);
+ v_tmp[1] = ((~k) & v0[1]) ^ (k & v1[1]);
+ v_tmp[2] = ((~k) & v0[2]) ^ (k & v1[2]);
+ v_tmp[3] = ((~k) & v0[3]) ^ (k & v1[3]);
+
+ c->coeffs[ 0+i] = (v_tmp[0] - v_tmp[3]) & 3;
+ c->coeffs[256+i] = (v_tmp[1] - v_tmp[3]) & 3;
+ c->coeffs[512+i] = (v_tmp[2] - v_tmp[3]) & 3;
+ c->coeffs[768+i] = (- k + 2*v_tmp[3]) & 3;
+ }
+ }
+
+inline int32_t g(int32_t x)
+ {
+ int32_t t, c, b;
+
+ // Next 6 lines compute t = x/(4*PARAM_Q);
+ b = x*2730;
+ t = b >> 27;
+ b = x - t*49156;
+ b = 49155 - b;
+ b >>= 31;
+ t -= b;
+
+ c = t & 1;
+ t = (t >> 1) + c; // t = round(x/(8*PARAM_Q))
+
+ t *= 8*PARAM_Q;
+
+ return ct_abs(t - x);
+ }
+
+inline int16_t LDDecode(int32_t xi0, int32_t xi1, int32_t xi2, int32_t xi3)
+ {
+ int32_t t;
+
+ t = g(xi0);
+ t += g(xi1);
+ t += g(xi2);
+ t += g(xi3);
+
+ t -= 8*PARAM_Q;
+ t >>= 31;
+ return t&1;
+ }
+
+inline void rec(uint8_t* key, const poly* v, const poly* c)
+ {
+ clear_mem(key, 32);
+
+ for(size_t i = 0; i < 256; i++)
+ {
+ const int32_t tmp0 = 16*PARAM_Q + 8*static_cast<int32_t>(v->coeffs[ 0+i]) - PARAM_Q * (2*c->coeffs[ 0+i]+c->coeffs[768+i]);
+ const int32_t tmp1 = 16*PARAM_Q + 8*static_cast<int32_t>(v->coeffs[256+i]) - PARAM_Q * (2*c->coeffs[256+i]+c->coeffs[768+i]);
+ const int32_t tmp2 = 16*PARAM_Q + 8*static_cast<int32_t>(v->coeffs[512+i]) - PARAM_Q * (2*c->coeffs[512+i]+c->coeffs[768+i]);
+ const int32_t tmp3 = 16*PARAM_Q + 8*static_cast<int32_t>(v->coeffs[768+i]) - PARAM_Q * (c->coeffs[768+i]);
+
+ key[i>>3] |= LDDecode(tmp0, tmp1, tmp2, tmp3) << (i & 7);
+ }
+ }
+
+void gen_a(poly* a, const uint8_t* seed, Newhope_Mode mode)
+ {
+ std::vector<uint8_t> buf(168*16);
+
+ std::unique_ptr<StreamCipher> xof;
+
+ if(mode == Newhope_Mode::BoringSSL)
+ {
+ xof = StreamCipher::create_or_throw("CTR-BE(AES-128)");
+ xof->set_key(seed, 16);
+ xof->set_iv(seed + 16, 16);
+ }
+ else
+ {
+ xof = StreamCipher::create_or_throw("SHAKE-128");
+ xof->set_key(seed, NEWHOPE_SEED_BYTES);
+ }
+
+ zeroise(buf);
+ xof->encrypt(buf);
+
+ size_t pos = 0, ctr = 0;
+
+ while(ctr < PARAM_N)
+ {
+ // Specialized for q = 12889
+ const uint16_t val = (buf[pos] | (static_cast<uint16_t>(buf[pos+1]) << 8)) & 0x3fff;
+
+ if(val < PARAM_Q)
+ {
+ a->coeffs[ctr++] = val;
+ }
+ pos += 2;
+ if(pos >= buf.size())
+ {
+ zeroise(buf);
+ xof->encrypt(buf);
+ pos = 0;
+ }
+ }
+ }
+
+}
+
+// API FUNCTIONS
+
+void newhope_keygen(uint8_t* send, poly* sk, RandomNumberGenerator& rng,
+ Newhope_Mode mode)
+ {
+ poly a, e, r, pk;
+ uint8_t seed[NEWHOPE_SEED_BYTES];
+
+ rng.randomize(seed, NEWHOPE_SEED_BYTES);
+
+ gen_a(&a, seed, mode);
+
+ poly_getnoise(rng, sk);
+ poly_ntt(sk);
+
+ poly_getnoise(rng, &e);
+ poly_ntt(&e);
+
+ poly_pointwise(&r, sk, &a);
+ poly_add(&pk, &e, &r);
+
+ encode_a(send, &pk, seed);
+ }
+
+void newhope_sharedb(uint8_t* sharedkey, uint8_t* send, const uint8_t* received,
+ RandomNumberGenerator& rng,
+ Newhope_Mode mode)
+ {
+ poly sp, ep, v, a, pka, c, epp, bp;
+ uint8_t seed[NEWHOPE_SEED_BYTES];
+
+ decode_a(&pka, seed, received);
+ gen_a(&a, seed, mode);
+
+ poly_getnoise(rng, &sp);
+ poly_ntt(&sp);
+ poly_getnoise(rng, &ep);
+ poly_ntt(&ep);
+
+ poly_pointwise(&bp, &a, &sp);
+ poly_add(&bp, &bp, &ep);
+
+ poly_pointwise(&v, &pka, &sp);
+ poly_invntt(&v);
+
+ poly_getnoise(rng, &epp);
+ poly_add(&v, &v, &epp);
+
+ helprec(&c, &v, rng);
+
+ encode_b(send, &bp, &c);
+
+ rec(sharedkey, &v, &c);
+
+ const std::string kdf_hash = (mode == Newhope_Mode::SHA3) ? "SHA-3(256)" : "SHA-256";
+ std::unique_ptr<HashFunction> hash = HashFunction::create_or_throw(kdf_hash);
+
+ hash->update(sharedkey, 32);
+ hash->final(sharedkey);
+ }
+
+void newhope_shareda(uint8_t sharedkey[],
+ const poly* sk,
+ const uint8_t received[],
+ Newhope_Mode mode)
+ {
+ poly v, bp, c;
+
+ decode_b(&bp, &c, received);
+
+ poly_pointwise(&v, sk, &bp);
+ poly_invntt(&v);
+
+ rec(sharedkey, &v, &c);
+
+ const std::string kdf_hash = (mode == Newhope_Mode::SHA3) ? "SHA-3(256)" : "SHA-256";
+ std::unique_ptr<HashFunction> hash = HashFunction::create_or_throw(kdf_hash);
+
+ hash->update(sharedkey, 32);
+ hash->final(sharedkey);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/pubkey/newhope/newhope.h b/comm/third_party/botan/src/lib/pubkey/newhope/newhope.h
new file mode 100644
index 0000000000..8840ef5603
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/newhope/newhope.h
@@ -0,0 +1,85 @@
+/*
+* NEWHOPE Ring-LWE scheme
+* Based on the public domain reference implementation by the
+* designers (https://github.com/tpoeppelmann/newhope)
+*
+* Further changes
+* (C) 2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_NEWHOPE_H_
+#define BOTAN_NEWHOPE_H_
+
+#include <botan/types.h>
+
+namespace Botan {
+
+class RandomNumberGenerator;
+
+/*
+* WARNING: This API is preliminary and will change
+* Currently pubkey.h does not support a 2-phase KEM scheme of
+* the sort NEWHOPE exports.
+*/
+
+// TODO: change to just a secure_vector
+class BOTAN_UNSTABLE_API newhope_poly final
+ {
+ public:
+ uint16_t coeffs[1024];
+ ~newhope_poly();
+ };
+
+enum Newhope_Params
+ {
+ NEWHOPE_SENDABYTES = 1824,
+ NEWHOPE_SENDBBYTES = 2048,
+
+ NEWHOPE_OFFER_BYTES = 1824,
+ NEWHOPE_ACCEPT_BYTES = 2048,
+ NEWHOPE_SHARED_KEY_BYTES = 32,
+
+ NEWHOPE_SEED_BYTES = 32,
+ NEWHOPE_POLY_BYTES = 1792,
+
+ CECPQ1_OFFER_BYTES = NEWHOPE_OFFER_BYTES + 32,
+ CECPQ1_ACCEPT_BYTES = NEWHOPE_ACCEPT_BYTES + 32,
+ CECPQ1_SHARED_KEY_BYTES = NEWHOPE_SHARED_KEY_BYTES + 32
+ };
+
+/**
+* This chooses the XOF + hash for NewHope
+* The official NewHope specification and reference implementation use
+* SHA-3 and SHAKE-128. BoringSSL instead uses SHA-256 and AES-128 in
+* CTR mode. CECPQ1 (x25519+NewHope) always uses BoringSSL's mode
+*/
+enum class Newhope_Mode
+ {
+ SHA3,
+ BoringSSL
+ };
+
+// offer
+void BOTAN_PUBLIC_API(2,0) newhope_keygen(uint8_t send[NEWHOPE_SENDABYTES],
+ newhope_poly* sk,
+ RandomNumberGenerator& rng,
+ Newhope_Mode = Newhope_Mode::SHA3);
+
+// accept
+void BOTAN_PUBLIC_API(2,0) newhope_sharedb(uint8_t sharedkey[NEWHOPE_SHARED_KEY_BYTES],
+ uint8_t send[],
+ const uint8_t* received,
+ RandomNumberGenerator& rng,
+ Newhope_Mode mode = Newhope_Mode::SHA3);
+
+// finish
+void BOTAN_PUBLIC_API(2,0) newhope_shareda(uint8_t sharedkey[NEWHOPE_SHARED_KEY_BYTES],
+ const newhope_poly* ska,
+ const uint8_t* received,
+ Newhope_Mode mode = Newhope_Mode::SHA3);
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/pubkey/pbes2/info.txt b/comm/third_party/botan/src/lib/pubkey/pbes2/info.txt
new file mode 100644
index 0000000000..f8c6d37196
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/pbes2/info.txt
@@ -0,0 +1,10 @@
+<defines>
+PKCS5_PBES2 -> 20141119
+</defines>
+
+<requires>
+asn1
+cbc
+hmac
+pbkdf2
+</requires>
diff --git a/comm/third_party/botan/src/lib/pubkey/pbes2/pbes2.cpp b/comm/third_party/botan/src/lib/pubkey/pbes2/pbes2.cpp
new file mode 100644
index 0000000000..1360de67c5
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/pbes2/pbes2.cpp
@@ -0,0 +1,339 @@
+/*
+* PKCS #5 PBES2
+* (C) 1999-2008,2014 Jack Lloyd
+* (C) 2018 Ribose Inc
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/pbes2.h>
+#include <botan/cipher_mode.h>
+#include <botan/pbkdf.h>
+#include <botan/pwdhash.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
+#include <botan/parsing.h>
+#include <botan/asn1_obj.h>
+#include <botan/oids.h>
+#include <botan/rng.h>
+
+#if defined(BOTAN_HAS_SCRYPT)
+ #include <botan/scrypt.h>
+#endif
+
+namespace Botan {
+
+namespace {
+
+bool known_pbes_cipher_mode(const std::string& mode)
+ {
+ return (mode == "CBC" || mode == "GCM" || mode == "SIV");
+ }
+
+SymmetricKey derive_key(const std::string& passphrase,
+ const AlgorithmIdentifier& kdf_algo,
+ size_t default_key_size)
+ {
+ if(kdf_algo.get_oid() == OID::from_string("PKCS5.PBKDF2"))
+ {
+ secure_vector<uint8_t> salt;
+ size_t iterations = 0, key_length = 0;
+
+ AlgorithmIdentifier prf_algo;
+ BER_Decoder(kdf_algo.get_parameters())
+ .start_cons(SEQUENCE)
+ .decode(salt, OCTET_STRING)
+ .decode(iterations)
+ .decode_optional(key_length, INTEGER, UNIVERSAL)
+ .decode_optional(prf_algo, SEQUENCE, CONSTRUCTED,
+ AlgorithmIdentifier("HMAC(SHA-160)",
+ AlgorithmIdentifier::USE_NULL_PARAM))
+ .end_cons();
+
+ if(salt.size() < 8)
+ throw Decoding_Error("PBE-PKCS5 v2.0: Encoded salt is too small");
+
+ if(key_length == 0)
+ key_length = default_key_size;
+
+ const std::string prf = OIDS::oid2str_or_throw(prf_algo.get_oid());
+ std::unique_ptr<PBKDF> pbkdf(get_pbkdf("PBKDF2(" + prf + ")"));
+ return pbkdf->pbkdf_iterations(key_length, passphrase, salt.data(), salt.size(), iterations);
+ }
+#if defined(BOTAN_HAS_SCRYPT)
+ else if(kdf_algo.get_oid() == OID::from_string("Scrypt"))
+ {
+ secure_vector<uint8_t> salt;
+ size_t N = 0, r = 0, p = 0;
+ size_t key_length = 0;
+
+ AlgorithmIdentifier prf_algo;
+ BER_Decoder(kdf_algo.get_parameters())
+ .start_cons(SEQUENCE)
+ .decode(salt, OCTET_STRING)
+ .decode(N)
+ .decode(r)
+ .decode(p)
+ .decode_optional(key_length, INTEGER, UNIVERSAL)
+ .end_cons();
+
+ if(key_length == 0)
+ key_length = default_key_size;
+
+ secure_vector<uint8_t> output(key_length);
+ scrypt(output.data(), output.size(), passphrase,
+ salt.data(), salt.size(), N, r, p);
+
+ return SymmetricKey(output);
+ }
+#endif
+ else
+ throw Decoding_Error("PBE-PKCS5 v2.0: Unknown KDF algorithm " +
+ kdf_algo.get_oid().to_string());
+ }
+
+secure_vector<uint8_t> derive_key(const std::string& passphrase,
+ const std::string& digest,
+ RandomNumberGenerator& rng,
+ size_t* msec_in_iterations_out,
+ size_t iterations_if_msec_null,
+ size_t key_length,
+ AlgorithmIdentifier& kdf_algo)
+ {
+ const secure_vector<uint8_t> salt = rng.random_vec(12);
+
+ if(digest == "Scrypt")
+ {
+#if defined(BOTAN_HAS_SCRYPT)
+
+ std::unique_ptr<PasswordHashFamily> pwhash_fam = PasswordHashFamily::create_or_throw("Scrypt");
+
+ std::unique_ptr<PasswordHash> pwhash;
+
+ if(msec_in_iterations_out)
+ {
+ const std::chrono::milliseconds msec(*msec_in_iterations_out);
+ pwhash = pwhash_fam->tune(key_length, msec);
+ }
+ else
+ {
+ pwhash = pwhash_fam->from_iterations(iterations_if_msec_null);
+ }
+
+ secure_vector<uint8_t> key(key_length);
+ pwhash->derive_key(key.data(), key.size(),
+ passphrase.c_str(), passphrase.size(),
+ salt.data(), salt.size());
+
+ const size_t N = pwhash->memory_param();
+ const size_t r = pwhash->iterations();
+ const size_t p = pwhash->parallelism();
+
+ if(msec_in_iterations_out)
+ *msec_in_iterations_out = 0;
+
+ std::vector<uint8_t> scrypt_params;
+ DER_Encoder(scrypt_params)
+ .start_cons(SEQUENCE)
+ .encode(salt, OCTET_STRING)
+ .encode(N)
+ .encode(r)
+ .encode(p)
+ .encode(key_length)
+ .end_cons();
+
+ kdf_algo = AlgorithmIdentifier(OID::from_string("Scrypt"), scrypt_params);
+ return key;
+#else
+ throw Not_Implemented("Scrypt is not available in this build");
+#endif
+ }
+ else
+ {
+ const std::string prf = "HMAC(" + digest + ")";
+ const std::string pbkdf_name = "PBKDF2(" + prf + ")";
+
+ std::unique_ptr<PasswordHashFamily> pwhash_fam = PasswordHashFamily::create(pbkdf_name);
+ if(!pwhash_fam)
+ throw Invalid_Argument("Unknown password hash digest " + digest);
+
+ std::unique_ptr<PasswordHash> pwhash;
+
+ if(msec_in_iterations_out)
+ {
+ const std::chrono::milliseconds msec(*msec_in_iterations_out);
+ pwhash = pwhash_fam->tune(key_length, msec);
+ }
+ else
+ {
+ pwhash = pwhash_fam->from_iterations(iterations_if_msec_null);
+ }
+
+ secure_vector<uint8_t> key(key_length);
+ pwhash->derive_key(key.data(), key.size(),
+ passphrase.c_str(), passphrase.size(),
+ salt.data(), salt.size());
+
+ std::vector<uint8_t> pbkdf2_params;
+
+ const size_t iterations = pwhash->iterations();
+
+ if(msec_in_iterations_out)
+ *msec_in_iterations_out = iterations;
+
+ DER_Encoder(pbkdf2_params)
+ .start_cons(SEQUENCE)
+ .encode(salt, OCTET_STRING)
+ .encode(iterations)
+ .encode(key_length)
+ .encode_if(prf != "HMAC(SHA-160)",
+ AlgorithmIdentifier(prf, AlgorithmIdentifier::USE_NULL_PARAM))
+ .end_cons();
+
+ kdf_algo = AlgorithmIdentifier("PKCS5.PBKDF2", pbkdf2_params);
+ return key;
+ }
+ }
+
+/*
+* PKCS#5 v2.0 PBE Encryption
+*/
+std::pair<AlgorithmIdentifier, std::vector<uint8_t>>
+pbes2_encrypt_shared(const secure_vector<uint8_t>& key_bits,
+ const std::string& passphrase,
+ size_t* msec_in_iterations_out,
+ size_t iterations_if_msec_null,
+ const std::string& cipher,
+ const std::string& prf,
+ RandomNumberGenerator& rng)
+ {
+ const std::vector<std::string> cipher_spec = split_on(cipher, '/');
+ if(cipher_spec.size() != 2)
+ throw Encoding_Error("PBE-PKCS5 v2.0: Invalid cipher spec " + cipher);
+
+ if(!known_pbes_cipher_mode(cipher_spec[1]))
+ throw Encoding_Error("PBE-PKCS5 v2.0: Don't know param format for " + cipher);
+
+ const OID cipher_oid = OIDS::str2oid_or_empty(cipher);
+ if(cipher_oid.empty())
+ throw Encoding_Error("PBE-PKCS5 v2.0: No OID assigned for " + cipher);
+
+ std::unique_ptr<Cipher_Mode> enc = Cipher_Mode::create(cipher, ENCRYPTION);
+
+ if(!enc)
+ throw Decoding_Error("PBE-PKCS5 cannot encrypt no cipher " + cipher);
+
+ const size_t key_length = enc->key_spec().maximum_keylength();
+
+ const secure_vector<uint8_t> iv = rng.random_vec(enc->default_nonce_length());
+
+ AlgorithmIdentifier kdf_algo;
+
+ const secure_vector<uint8_t> derived_key =
+ derive_key(passphrase, prf, rng,
+ msec_in_iterations_out, iterations_if_msec_null,
+ key_length, kdf_algo);
+
+ enc->set_key(derived_key);
+ enc->start(iv);
+ secure_vector<uint8_t> ctext = key_bits;
+ enc->finish(ctext);
+
+ std::vector<uint8_t> encoded_iv;
+ DER_Encoder(encoded_iv).encode(iv, OCTET_STRING);
+
+ std::vector<uint8_t> pbes2_params;
+ DER_Encoder(pbes2_params)
+ .start_cons(SEQUENCE)
+ .encode(kdf_algo)
+ .encode(AlgorithmIdentifier(cipher, encoded_iv))
+ .end_cons();
+
+ AlgorithmIdentifier id(OID::from_string("PBE-PKCS5v20"), pbes2_params);
+
+ return std::make_pair(id, unlock(ctext));
+ }
+
+}
+
+std::pair<AlgorithmIdentifier, std::vector<uint8_t>>
+pbes2_encrypt(const secure_vector<uint8_t>& key_bits,
+ const std::string& passphrase,
+ std::chrono::milliseconds msec,
+ const std::string& cipher,
+ const std::string& digest,
+ RandomNumberGenerator& rng)
+ {
+ size_t msec_in_iterations_out = static_cast<size_t>(msec.count());
+ return pbes2_encrypt_shared(key_bits, passphrase, &msec_in_iterations_out, 0, cipher, digest, rng);
+ // return value msec_in_iterations_out discarded
+ }
+
+std::pair<AlgorithmIdentifier, std::vector<uint8_t>>
+pbes2_encrypt_msec(const secure_vector<uint8_t>& key_bits,
+ const std::string& passphrase,
+ std::chrono::milliseconds msec,
+ size_t* out_iterations_if_nonnull,
+ const std::string& cipher,
+ const std::string& digest,
+ RandomNumberGenerator& rng)
+ {
+ size_t msec_in_iterations_out = static_cast<size_t>(msec.count());
+
+ auto ret = pbes2_encrypt_shared(key_bits, passphrase, &msec_in_iterations_out, 0, cipher, digest, rng);
+
+ if(out_iterations_if_nonnull)
+ *out_iterations_if_nonnull = msec_in_iterations_out;
+
+ return ret;
+ }
+
+std::pair<AlgorithmIdentifier, std::vector<uint8_t>>
+pbes2_encrypt_iter(const secure_vector<uint8_t>& key_bits,
+ const std::string& passphrase,
+ size_t pbkdf_iter,
+ const std::string& cipher,
+ const std::string& digest,
+ RandomNumberGenerator& rng)
+ {
+ return pbes2_encrypt_shared(key_bits, passphrase, nullptr, pbkdf_iter, cipher, digest, rng);
+ }
+
+secure_vector<uint8_t>
+pbes2_decrypt(const secure_vector<uint8_t>& key_bits,
+ const std::string& passphrase,
+ const std::vector<uint8_t>& params)
+ {
+ AlgorithmIdentifier kdf_algo, enc_algo;
+
+ BER_Decoder(params)
+ .start_cons(SEQUENCE)
+ .decode(kdf_algo)
+ .decode(enc_algo)
+ .end_cons();
+
+ const std::string cipher = OIDS::oid2str_or_throw(enc_algo.get_oid());
+ const std::vector<std::string> cipher_spec = split_on(cipher, '/');
+ if(cipher_spec.size() != 2)
+ throw Decoding_Error("PBE-PKCS5 v2.0: Invalid cipher spec " + cipher);
+ if(!known_pbes_cipher_mode(cipher_spec[1]))
+ throw Decoding_Error("PBE-PKCS5 v2.0: Don't know param format for " + cipher);
+
+ secure_vector<uint8_t> iv;
+ BER_Decoder(enc_algo.get_parameters()).decode(iv, OCTET_STRING).verify_end();
+
+ std::unique_ptr<Cipher_Mode> dec = Cipher_Mode::create(cipher, DECRYPTION);
+ if(!dec)
+ throw Decoding_Error("PBE-PKCS5 cannot decrypt no cipher " + cipher);
+
+ dec->set_key(derive_key(passphrase, kdf_algo, dec->key_spec().maximum_keylength()));
+
+ dec->start(iv);
+
+ secure_vector<uint8_t> buf = key_bits;
+ dec->finish(buf);
+
+ return buf;
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/pubkey/pbes2/pbes2.h b/comm/third_party/botan/src/lib/pubkey/pbes2/pbes2.h
new file mode 100644
index 0000000000..a5051a8fd9
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/pbes2/pbes2.h
@@ -0,0 +1,87 @@
+/*
+* PKCS #5 v2.0 PBE
+* (C) 1999-2007,2014 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_PBE_PKCS_v20_H_
+#define BOTAN_PBE_PKCS_v20_H_
+
+#include <botan/asn1_obj.h>
+#include <chrono>
+
+BOTAN_FUTURE_INTERNAL_HEADER(pbes2.h)
+
+namespace Botan {
+
+class RandomNumberGenerator;
+
+/**
+* Encrypt with PBES2 from PKCS #5 v2.0
+* @param key_bits the input
+* @param passphrase the passphrase to use for encryption
+* @param msec how many milliseconds to run PBKDF2
+* @param cipher specifies the block cipher to use to encrypt
+* @param digest specifies the PRF to use with PBKDF2 (eg "HMAC(SHA-1)")
+* @param rng a random number generator
+*/
+std::pair<AlgorithmIdentifier, std::vector<uint8_t>>
+BOTAN_PUBLIC_API(2,0) pbes2_encrypt(const secure_vector<uint8_t>& key_bits,
+ const std::string& passphrase,
+ std::chrono::milliseconds msec,
+ const std::string& cipher,
+ const std::string& digest,
+ RandomNumberGenerator& rng);
+
+/**
+* Encrypt with PBES2 from PKCS #5 v2.0
+* @param key_bits the input
+* @param passphrase the passphrase to use for encryption
+* @param msec how many milliseconds to run PBKDF2
+* @param out_iterations_if_nonnull if not null, set to the number
+* of PBKDF iterations used
+* @param cipher specifies the block cipher to use to encrypt
+* @param digest specifies the PRF to use with PBKDF2 (eg "HMAC(SHA-1)")
+* @param rng a random number generator
+*/
+std::pair<AlgorithmIdentifier, std::vector<uint8_t>>
+BOTAN_PUBLIC_API(2,1) pbes2_encrypt_msec(const secure_vector<uint8_t>& key_bits,
+ const std::string& passphrase,
+ std::chrono::milliseconds msec,
+ size_t* out_iterations_if_nonnull,
+ const std::string& cipher,
+ const std::string& digest,
+ RandomNumberGenerator& rng);
+
+/**
+* Encrypt with PBES2 from PKCS #5 v2.0
+* @param key_bits the input
+* @param passphrase the passphrase to use for encryption
+* @param iterations how many iterations to run PBKDF2
+* @param cipher specifies the block cipher to use to encrypt
+* @param digest specifies the PRF to use with PBKDF2 (eg "HMAC(SHA-1)")
+* @param rng a random number generator
+*/
+std::pair<AlgorithmIdentifier, std::vector<uint8_t>>
+BOTAN_PUBLIC_API(2,1) pbes2_encrypt_iter(const secure_vector<uint8_t>& key_bits,
+ const std::string& passphrase,
+ size_t iterations,
+ const std::string& cipher,
+ const std::string& digest,
+ RandomNumberGenerator& rng);
+
+/**
+* Decrypt a PKCS #5 v2.0 encrypted stream
+* @param key_bits the input
+* @param passphrase the passphrase to use for decryption
+* @param params the PBES2 parameters
+*/
+secure_vector<uint8_t>
+BOTAN_PUBLIC_API(2,0) pbes2_decrypt(const secure_vector<uint8_t>& key_bits,
+ const std::string& passphrase,
+ const std::vector<uint8_t>& params);
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/pubkey/pem/info.txt b/comm/third_party/botan/src/lib/pubkey/pem/info.txt
new file mode 100644
index 0000000000..471d9abd63
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/pem/info.txt
@@ -0,0 +1,7 @@
+<defines>
+PEM_CODEC -> 20131128
+</defines>
+
+<requires>
+base64
+</requires>
diff --git a/comm/third_party/botan/src/lib/pubkey/pem/pem.cpp b/comm/third_party/botan/src/lib/pubkey/pem/pem.cpp
new file mode 100644
index 0000000000..d2433860dd
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/pem/pem.cpp
@@ -0,0 +1,169 @@
+/*
+* PEM Encoding/Decoding
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/pem.h>
+#include <botan/data_src.h>
+#include <botan/base64.h>
+#include <botan/exceptn.h>
+
+namespace Botan {
+
+namespace PEM_Code {
+
+namespace {
+
+std::string linewrap(size_t width, const std::string& in)
+ {
+ std::string out;
+ for(size_t i = 0; i != in.size(); ++i)
+ {
+ if(i > 0 && i % width == 0)
+ {
+ out.push_back('\n');
+ }
+ out.push_back(in[i]);
+ }
+ if(out.size() > 0 && out[out.size()-1] != '\n')
+ {
+ out.push_back('\n');
+ }
+
+ return out;
+ }
+
+}
+
+/*
+* PEM encode BER/DER-encoded objects
+*/
+std::string encode(const uint8_t der[], size_t length, const std::string& label, size_t width)
+ {
+ const std::string PEM_HEADER = "-----BEGIN " + label + "-----\n";
+ const std::string PEM_TRAILER = "-----END " + label + "-----\n";
+
+ return (PEM_HEADER + linewrap(width, base64_encode(der, length)) + PEM_TRAILER);
+ }
+
+/*
+* Decode PEM down to raw BER/DER
+*/
+secure_vector<uint8_t> decode_check_label(DataSource& source,
+ const std::string& label_want)
+ {
+ std::string label_got;
+ secure_vector<uint8_t> ber = decode(source, label_got);
+ if(label_got != label_want)
+ throw Decoding_Error("PEM: Label mismatch, wanted " + label_want +
+ ", got " + label_got);
+ return ber;
+ }
+
+/*
+* Decode PEM down to raw BER/DER
+*/
+secure_vector<uint8_t> decode(DataSource& source, std::string& label)
+ {
+ const size_t RANDOM_CHAR_LIMIT = 8;
+
+ label.clear();
+
+ const std::string PEM_HEADER1 = "-----BEGIN ";
+ const std::string PEM_HEADER2 = "-----";
+ size_t position = 0;
+
+ while(position != PEM_HEADER1.length())
+ {
+ uint8_t b;
+ if(!source.read_byte(b))
+ throw Decoding_Error("PEM: No PEM header found");
+ if(b == PEM_HEADER1[position])
+ ++position;
+ else if(position >= RANDOM_CHAR_LIMIT)
+ throw Decoding_Error("PEM: Malformed PEM header");
+ else
+ position = 0;
+ }
+ position = 0;
+ while(position != PEM_HEADER2.length())
+ {
+ uint8_t b;
+ if(!source.read_byte(b))
+ throw Decoding_Error("PEM: No PEM header found");
+ if(b == PEM_HEADER2[position])
+ ++position;
+ else if(position)
+ throw Decoding_Error("PEM: Malformed PEM header");
+
+ if(position == 0)
+ label += static_cast<char>(b);
+ }
+
+ std::vector<char> b64;
+
+ const std::string PEM_TRAILER = "-----END " + label + "-----";
+ position = 0;
+ while(position != PEM_TRAILER.length())
+ {
+ uint8_t b;
+ if(!source.read_byte(b))
+ throw Decoding_Error("PEM: No PEM trailer found");
+ if(b == PEM_TRAILER[position])
+ ++position;
+ else if(position)
+ throw Decoding_Error("PEM: Malformed PEM trailer");
+
+ if(position == 0)
+ b64.push_back(b);
+ }
+
+ return base64_decode(b64.data(), b64.size());
+ }
+
+secure_vector<uint8_t> decode_check_label(const std::string& pem,
+ const std::string& label_want)
+ {
+ DataSource_Memory src(pem);
+ return decode_check_label(src, label_want);
+ }
+
+secure_vector<uint8_t> decode(const std::string& pem, std::string& label)
+ {
+ DataSource_Memory src(pem);
+ return decode(src, label);
+ }
+
+/*
+* Search for a PEM signature
+*/
+bool matches(DataSource& source, const std::string& extra,
+ size_t search_range)
+ {
+ const std::string PEM_HEADER = "-----BEGIN " + extra;
+
+ secure_vector<uint8_t> search_buf(search_range);
+ size_t got = source.peek(search_buf.data(), search_buf.size(), 0);
+
+ if(got < PEM_HEADER.length())
+ return false;
+
+ size_t index = 0;
+
+ for(size_t j = 0; j != got; ++j)
+ {
+ if(search_buf[j] == PEM_HEADER[index])
+ ++index;
+ else
+ index = 0;
+ if(index == PEM_HEADER.size())
+ return true;
+ }
+ return false;
+ }
+
+}
+
+}
diff --git a/comm/third_party/botan/src/lib/pubkey/pem/pem.h b/comm/third_party/botan/src/lib/pubkey/pem/pem.h
new file mode 100644
index 0000000000..c02294dce5
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/pem/pem.h
@@ -0,0 +1,91 @@
+/*
+* PEM Encoding/Decoding
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_PEM_H_
+#define BOTAN_PEM_H_
+
+#include <botan/secmem.h>
+#include <string>
+
+namespace Botan {
+
+class DataSource;
+
+namespace PEM_Code {
+
+/**
+* Encode some binary data in PEM format
+* @param data binary data to encode
+* @param data_len length of binary data in bytes
+* @param label PEM label put after BEGIN and END
+* @param line_width after this many characters, a new line is inserted
+*/
+BOTAN_PUBLIC_API(2,0) std::string encode(const uint8_t data[],
+ size_t data_len,
+ const std::string& label,
+ size_t line_width = 64);
+
+/**
+* Encode some binary data in PEM format
+* @param data binary data to encode
+* @param label PEM label
+* @param line_width after this many characters, a new line is inserted
+*/
+template<typename Alloc>
+std::string encode(const std::vector<uint8_t, Alloc>& data,
+ const std::string& label,
+ size_t line_width = 64)
+ {
+ return encode(data.data(), data.size(), label, line_width);
+ }
+
+/**
+* Decode PEM data
+* @param pem a datasource containing PEM encoded data
+* @param label is set to the PEM label found for later inspection
+*/
+BOTAN_PUBLIC_API(2,0) secure_vector<uint8_t> decode(DataSource& pem,
+ std::string& label);
+
+/**
+* Decode PEM data
+* @param pem a string containing PEM encoded data
+* @param label is set to the PEM label found for later inspection
+*/
+BOTAN_PUBLIC_API(2,0) secure_vector<uint8_t> decode(const std::string& pem,
+ std::string& label);
+
+/**
+* Decode PEM data
+* @param pem a datasource containing PEM encoded data
+* @param label is what we expect the label to be
+*/
+BOTAN_PUBLIC_API(2,0)
+secure_vector<uint8_t> decode_check_label(DataSource& pem,
+ const std::string& label);
+
+/**
+* Decode PEM data
+* @param pem a string containing PEM encoded data
+* @param label is what we expect the label to be
+*/
+BOTAN_PUBLIC_API(2,0)
+secure_vector<uint8_t> decode_check_label(const std::string& pem,
+ const std::string& label);
+
+/**
+* Heuristic test for PEM data.
+*/
+BOTAN_PUBLIC_API(2,0) bool matches(DataSource& source,
+ const std::string& extra = "",
+ size_t search_range = 4096);
+
+}
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/pubkey/pk_algs.cpp b/comm/third_party/botan/src/lib/pubkey/pk_algs.cpp
new file mode 100644
index 0000000000..fc86975852
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/pk_algs.cpp
@@ -0,0 +1,426 @@
+/*
+* PK Key
+* (C) 1999-2010,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/pk_algs.h>
+#include <botan/parsing.h>
+
+#if defined(BOTAN_HAS_RSA)
+ #include <botan/rsa.h>
+#endif
+
+#if defined(BOTAN_HAS_DSA)
+ #include <botan/dsa.h>
+#endif
+
+#if defined(BOTAN_HAS_DL_GROUP)
+ #include <botan/dl_group.h>
+#endif
+
+#if defined(BOTAN_HAS_DIFFIE_HELLMAN)
+ #include <botan/dh.h>
+#endif
+
+#if defined(BOTAN_HAS_ECC_PUBLIC_KEY_CRYPTO)
+ #include <botan/ecc_key.h>
+#endif
+
+#if defined(BOTAN_HAS_ECDSA)
+ #include <botan/ecdsa.h>
+#endif
+
+#if defined(BOTAN_HAS_ECGDSA)
+ #include <botan/ecgdsa.h>
+#endif
+
+#if defined(BOTAN_HAS_ECKCDSA)
+ #include <botan/eckcdsa.h>
+#endif
+
+#if defined(BOTAN_HAS_ED25519)
+ #include <botan/ed25519.h>
+#endif
+
+#if defined(BOTAN_HAS_GOST_34_10_2001)
+ #include <botan/gost_3410.h>
+#endif
+
+#if defined(BOTAN_HAS_ELGAMAL)
+ #include <botan/elgamal.h>
+#endif
+
+#if defined(BOTAN_HAS_ECDH)
+ #include <botan/ecdh.h>
+#endif
+
+#if defined(BOTAN_HAS_CURVE_25519)
+ #include <botan/curve25519.h>
+#endif
+
+#if defined(BOTAN_HAS_MCELIECE)
+ #include <botan/mceliece.h>
+#endif
+
+#if defined(BOTAN_HAS_XMSS_RFC8391)
+ #include <botan/xmss.h>
+#endif
+
+#if defined(BOTAN_HAS_SM2)
+ #include <botan/sm2.h>
+#endif
+
+#if defined(BOTAN_HAS_OPENSSL)
+ #include <botan/internal/openssl.h>
+#endif
+
+namespace Botan {
+
+std::unique_ptr<Public_Key>
+load_public_key(const AlgorithmIdentifier& alg_id,
+ const std::vector<uint8_t>& key_bits)
+ {
+ const std::string oid_str = alg_id.get_oid().to_formatted_string();
+ const std::vector<std::string> alg_info = split_on(oid_str, '/');
+ const std::string alg_name = alg_info[0];
+
+#if defined(BOTAN_HAS_RSA)
+ if(alg_name == "RSA")
+ return std::unique_ptr<Public_Key>(new RSA_PublicKey(alg_id, key_bits));
+#endif
+
+#if defined(BOTAN_HAS_CURVE_25519)
+ if(alg_name == "Curve25519")
+ return std::unique_ptr<Public_Key>(new Curve25519_PublicKey(alg_id, key_bits));
+#endif
+
+#if defined(BOTAN_HAS_MCELIECE)
+ if(alg_name == "McEliece")
+ return std::unique_ptr<Public_Key>(new McEliece_PublicKey(key_bits));
+#endif
+
+#if defined(BOTAN_HAS_ECDSA)
+ if(alg_name == "ECDSA")
+ return std::unique_ptr<Public_Key>(new ECDSA_PublicKey(alg_id, key_bits));
+#endif
+
+#if defined(BOTAN_HAS_ECDH)
+ if(alg_name == "ECDH")
+ return std::unique_ptr<Public_Key>(new ECDH_PublicKey(alg_id, key_bits));
+#endif
+
+#if defined(BOTAN_HAS_DIFFIE_HELLMAN)
+ if(alg_name == "DH")
+ return std::unique_ptr<Public_Key>(new DH_PublicKey(alg_id, key_bits));
+#endif
+
+#if defined(BOTAN_HAS_DSA)
+ if(alg_name == "DSA")
+ return std::unique_ptr<Public_Key>(new DSA_PublicKey(alg_id, key_bits));
+#endif
+
+#if defined(BOTAN_HAS_ELGAMAL)
+ if(alg_name == "ElGamal")
+ return std::unique_ptr<Public_Key>(new ElGamal_PublicKey(alg_id, key_bits));
+#endif
+
+#if defined(BOTAN_HAS_ECGDSA)
+ if(alg_name == "ECGDSA")
+ return std::unique_ptr<Public_Key>(new ECGDSA_PublicKey(alg_id, key_bits));
+#endif
+
+#if defined(BOTAN_HAS_ECKCDSA)
+ if(alg_name == "ECKCDSA")
+ return std::unique_ptr<Public_Key>(new ECKCDSA_PublicKey(alg_id, key_bits));
+#endif
+
+#if defined(BOTAN_HAS_ED25519)
+ if(alg_name == "Ed25519")
+ return std::unique_ptr<Public_Key>(new Ed25519_PublicKey(alg_id, key_bits));
+#endif
+
+#if defined(BOTAN_HAS_GOST_34_10_2001)
+ if(alg_name == "GOST-34.10" || alg_name == "GOST-34.10-2012-256" || alg_name == "GOST-34.10-2012-512")
+ return std::unique_ptr<Public_Key>(new GOST_3410_PublicKey(alg_id, key_bits));
+#endif
+
+#if defined(BOTAN_HAS_SM2)
+ if(alg_name == "SM2" || alg_name == "SM2_Sig" || alg_name == "SM2_Enc")
+ return std::unique_ptr<Public_Key>(new SM2_PublicKey(alg_id, key_bits));
+#endif
+
+#if defined(BOTAN_HAS_XMSS_RFC8391)
+ if(alg_name == "XMSS")
+ return std::unique_ptr<Public_Key>(new XMSS_PublicKey(key_bits));
+#endif
+
+ throw Decoding_Error("Unknown or unavailable public key algorithm " + alg_name);
+ }
+
+std::unique_ptr<Private_Key>
+load_private_key(const AlgorithmIdentifier& alg_id,
+ const secure_vector<uint8_t>& key_bits)
+ {
+ const std::string alg_name = alg_id.get_oid().to_formatted_string();
+
+#if defined(BOTAN_HAS_RSA)
+ if(alg_name == "RSA")
+ return std::unique_ptr<Private_Key>(new RSA_PrivateKey(alg_id, key_bits));
+#endif
+
+#if defined(BOTAN_HAS_CURVE_25519)
+ if(alg_name == "Curve25519")
+ return std::unique_ptr<Private_Key>(new Curve25519_PrivateKey(alg_id, key_bits));
+#endif
+
+#if defined(BOTAN_HAS_ECDSA)
+ if(alg_name == "ECDSA")
+ return std::unique_ptr<Private_Key>(new ECDSA_PrivateKey(alg_id, key_bits));
+#endif
+
+#if defined(BOTAN_HAS_ECDH)
+ if(alg_name == "ECDH")
+ return std::unique_ptr<Private_Key>(new ECDH_PrivateKey(alg_id, key_bits));
+#endif
+
+#if defined(BOTAN_HAS_DIFFIE_HELLMAN)
+ if(alg_name == "DH")
+ return std::unique_ptr<Private_Key>(new DH_PrivateKey(alg_id, key_bits));
+#endif
+
+#if defined(BOTAN_HAS_DSA)
+ if(alg_name == "DSA")
+ return std::unique_ptr<Private_Key>(new DSA_PrivateKey(alg_id, key_bits));
+#endif
+
+#if defined(BOTAN_HAS_MCELIECE)
+ if(alg_name == "McEliece")
+ return std::unique_ptr<Private_Key>(new McEliece_PrivateKey(key_bits));
+#endif
+
+#if defined(BOTAN_HAS_ECGDSA)
+ if(alg_name == "ECGDSA")
+ return std::unique_ptr<Private_Key>(new ECGDSA_PrivateKey(alg_id, key_bits));
+#endif
+
+#if defined(BOTAN_HAS_ECKCDSA)
+ if(alg_name == "ECKCDSA")
+ return std::unique_ptr<Private_Key>(new ECKCDSA_PrivateKey(alg_id, key_bits));
+#endif
+
+#if defined(BOTAN_HAS_ED25519)
+ if(alg_name == "Ed25519")
+ return std::unique_ptr<Private_Key>(new Ed25519_PrivateKey(alg_id, key_bits));
+#endif
+
+#if defined(BOTAN_HAS_GOST_34_10_2001)
+ if(alg_name == "GOST-34.10" || alg_name == "GOST-34.10-2012-256" || alg_name == "GOST-34.10-2012-512")
+ return std::unique_ptr<Private_Key>(new GOST_3410_PrivateKey(alg_id, key_bits));
+#endif
+
+#if defined(BOTAN_HAS_SM2)
+ if(alg_name == "SM2" || alg_name == "SM2_Sig" || alg_name == "SM2_Enc")
+ return std::unique_ptr<Private_Key>(new SM2_PrivateKey(alg_id, key_bits));
+#endif
+
+#if defined(BOTAN_HAS_ELGAMAL)
+ if(alg_name == "ElGamal")
+ return std::unique_ptr<Private_Key>(new ElGamal_PrivateKey(alg_id, key_bits));
+#endif
+
+#if defined(BOTAN_HAS_XMSS_RFC8391)
+ if(alg_name == "XMSS")
+ return std::unique_ptr<Private_Key>(new XMSS_PrivateKey(key_bits));
+#endif
+
+ throw Decoding_Error("Unknown or unavailable public key algorithm " + alg_name);
+ }
+
+#if defined(BOTAN_HAS_ECC_GROUP)
+
+namespace {
+
+std::string default_ec_group_for(const std::string& alg_name)
+ {
+ if(alg_name == "SM2" || alg_name == "SM2_Enc" || alg_name == "SM2_Sig")
+ return "sm2p256v1";
+ if(alg_name == "GOST-34.10" || alg_name == "GOST-34.10-2012-256")
+ return "gost_256A";
+ if(alg_name == "GOST-34.10-2012-512")
+ return "gost_512A";
+ if(alg_name == "ECGDSA")
+ return "brainpool256r1";
+ return "secp256r1";
+
+ }
+
+}
+
+#endif
+
+std::unique_ptr<Private_Key>
+create_private_key(const std::string& alg_name,
+ RandomNumberGenerator& rng,
+ const std::string& params,
+ const std::string& provider)
+ {
+ /*
+ * Default paramaters are chosen for work factor > 2**128 where possible
+ */
+
+#if defined(BOTAN_HAS_CURVE_25519)
+ if(alg_name == "Curve25519")
+ return std::unique_ptr<Private_Key>(new Curve25519_PrivateKey(rng));
+#endif
+
+#if defined(BOTAN_HAS_RSA)
+ if(alg_name == "RSA")
+ {
+ const size_t rsa_bits = (params.empty() ? 3072 : to_u32bit(params));
+#if defined(BOTAN_HAS_OPENSSL)
+ if(provider.empty() || provider == "openssl")
+ {
+ std::unique_ptr<Botan::Private_Key> pk;
+ if((pk = make_openssl_rsa_private_key(rng, rsa_bits)))
+ return pk;
+
+ if(!provider.empty())
+ return nullptr;
+ }
+#endif
+ return std::unique_ptr<Private_Key>(new RSA_PrivateKey(rng, rsa_bits));
+ }
+#endif
+
+#if defined(BOTAN_HAS_MCELIECE)
+ if(alg_name == "McEliece")
+ {
+ std::vector<std::string> mce_param =
+ Botan::split_on(params.empty() ? "2960,57" : params, ',');
+
+ if(mce_param.size() != 2)
+ throw Invalid_Argument("create_private_key bad McEliece parameters " + params);
+
+ size_t mce_n = Botan::to_u32bit(mce_param[0]);
+ size_t mce_t = Botan::to_u32bit(mce_param[1]);
+
+ return std::unique_ptr<Botan::Private_Key>(new Botan::McEliece_PrivateKey(rng, mce_n, mce_t));
+ }
+#endif
+
+#if defined(BOTAN_HAS_XMSS_RFC8391)
+ if(alg_name == "XMSS")
+ {
+ return std::unique_ptr<Private_Key>(
+ new XMSS_PrivateKey(XMSS_Parameters(params.empty() ? "XMSS-SHA2_10_512" : params).oid(), rng));
+ }
+#endif
+
+#if defined(BOTAN_HAS_ED25519)
+ if(alg_name == "Ed25519")
+ {
+ return std::unique_ptr<Private_Key>(new Ed25519_PrivateKey(rng));
+ }
+#endif
+
+ // ECC crypto
+#if defined(BOTAN_HAS_ECC_PUBLIC_KEY_CRYPTO)
+
+ if(alg_name == "ECDSA" ||
+ alg_name == "ECDH" ||
+ alg_name == "ECKCDSA" ||
+ alg_name == "ECGDSA" ||
+ alg_name == "SM2" ||
+ alg_name == "SM2_Sig" ||
+ alg_name == "SM2_Enc" ||
+ alg_name == "GOST-34.10" ||
+ alg_name == "GOST-34.10-2012-256" ||
+ alg_name == "GOST-34.10-2012-512")
+ {
+ const EC_Group ec_group(params.empty() ? default_ec_group_for(alg_name) : params);
+
+#if defined(BOTAN_HAS_ECDSA)
+ if(alg_name == "ECDSA")
+ return std::unique_ptr<Private_Key>(new ECDSA_PrivateKey(rng, ec_group));
+#endif
+
+#if defined(BOTAN_HAS_ECDH)
+ if(alg_name == "ECDH")
+ return std::unique_ptr<Private_Key>(new ECDH_PrivateKey(rng, ec_group));
+#endif
+
+#if defined(BOTAN_HAS_ECKCDSA)
+ if(alg_name == "ECKCDSA")
+ return std::unique_ptr<Private_Key>(new ECKCDSA_PrivateKey(rng, ec_group));
+#endif
+
+#if defined(BOTAN_HAS_GOST_34_10_2001)
+ if(alg_name == "GOST-34.10" || alg_name == "GOST-34.10-2012-256" || alg_name == "GOST-34.10-2012-512")
+ return std::unique_ptr<Private_Key>(new GOST_3410_PrivateKey(rng, ec_group));
+#endif
+
+#if defined(BOTAN_HAS_SM2)
+ if(alg_name == "SM2" || alg_name == "SM2_Sig" || alg_name == "SM2_Enc")
+ return std::unique_ptr<Private_Key>(new SM2_PrivateKey(rng, ec_group));
+#endif
+
+#if defined(BOTAN_HAS_ECGDSA)
+ if(alg_name == "ECGDSA")
+ return std::unique_ptr<Private_Key>(new ECGDSA_PrivateKey(rng, ec_group));
+#endif
+ }
+#endif
+
+ // DL crypto
+#if defined(BOTAN_HAS_DL_GROUP)
+ if(alg_name == "DH" || alg_name == "DSA" || alg_name == "ElGamal")
+ {
+ std::string default_group = (alg_name == "DSA") ? "dsa/botan/2048" : "modp/ietf/2048";
+ DL_Group modp_group(params.empty() ? default_group : params);
+
+#if defined(BOTAN_HAS_DIFFIE_HELLMAN)
+ if(alg_name == "DH")
+ return std::unique_ptr<Private_Key>(new DH_PrivateKey(rng, modp_group));
+#endif
+
+#if defined(BOTAN_HAS_DSA)
+ if(alg_name == "DSA")
+ return std::unique_ptr<Private_Key>(new DSA_PrivateKey(rng, modp_group));
+#endif
+
+#if defined(BOTAN_HAS_ELGAMAL)
+ if(alg_name == "ElGamal")
+ return std::unique_ptr<Private_Key>(new ElGamal_PrivateKey(rng, modp_group));
+#endif
+ }
+#endif
+
+ BOTAN_UNUSED(alg_name, rng, params, provider);
+
+ return std::unique_ptr<Private_Key>();
+ }
+
+std::vector<std::string>
+probe_provider_private_key(const std::string& alg_name,
+ const std::vector<std::string> possible)
+ {
+ std::vector<std::string> providers;
+ for(auto&& prov : possible)
+ {
+ if(prov == "base" ||
+#if defined(BOTAN_HAS_OPENSSL)
+ (prov == "openssl" && alg_name == "RSA") ||
+#endif
+ 0)
+ {
+ providers.push_back(prov); // available
+ }
+ }
+
+ BOTAN_UNUSED(alg_name);
+
+ return providers;
+ }
+}
diff --git a/comm/third_party/botan/src/lib/pubkey/pk_algs.h b/comm/third_party/botan/src/lib/pubkey/pk_algs.h
new file mode 100644
index 0000000000..12514908ec
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/pk_algs.h
@@ -0,0 +1,46 @@
+/*
+* PK Key Factory
+* (C) 1999-2010,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_PK_KEY_FACTORY_H_
+#define BOTAN_PK_KEY_FACTORY_H_
+
+#include <botan/pk_keys.h>
+#include <botan/asn1_obj.h>
+#include <memory>
+
+namespace Botan {
+
+BOTAN_PUBLIC_API(2,0) std::unique_ptr<Public_Key>
+load_public_key(const AlgorithmIdentifier& alg_id,
+ const std::vector<uint8_t>& key_bits);
+
+BOTAN_PUBLIC_API(2,0) std::unique_ptr<Private_Key>
+load_private_key(const AlgorithmIdentifier& alg_id,
+ const secure_vector<uint8_t>& key_bits);
+
+/**
+* Create a new key
+* For ECC keys, algo_params specifies EC group (eg, "secp256r1")
+* For DH/DSA/ElGamal keys, algo_params is DL group (eg, "modp/ietf/2048")
+* For RSA, algo_params is integer keylength
+* For McEliece, algo_params is n,t
+* If algo_params is left empty, suitable default parameters are chosen.
+*/
+BOTAN_PUBLIC_API(2,0) std::unique_ptr<Private_Key>
+create_private_key(const std::string& algo_name,
+ RandomNumberGenerator& rng,
+ const std::string& algo_params = "",
+ const std::string& provider = "");
+
+BOTAN_PUBLIC_API(2,2)
+std::vector<std::string>
+probe_provider_private_key(const std::string& algo_name,
+ const std::vector<std::string> possible);
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/pubkey/pk_keys.cpp b/comm/third_party/botan/src/lib/pubkey/pk_keys.cpp
new file mode 100644
index 0000000000..c5a98d72fe
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/pk_keys.cpp
@@ -0,0 +1,145 @@
+/*
+* PK Key Types
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/pk_keys.h>
+#include <botan/pk_ops.h>
+#include <botan/der_enc.h>
+#include <botan/oids.h>
+#include <botan/hash.h>
+#include <botan/hex.h>
+
+namespace Botan {
+
+std::string create_hex_fingerprint(const uint8_t bits[],
+ size_t bits_len,
+ const std::string& hash_name)
+ {
+ std::unique_ptr<HashFunction> hash_fn(HashFunction::create_or_throw(hash_name));
+ const std::string hex_hash = hex_encode(hash_fn->process(bits, bits_len));
+
+ std::string fprint;
+
+ for(size_t i = 0; i != hex_hash.size(); i += 2)
+ {
+ if(i != 0)
+ fprint.push_back(':');
+
+ fprint.push_back(hex_hash[i]);
+ fprint.push_back(hex_hash[i+1]);
+ }
+
+ return fprint;
+ }
+
+std::vector<uint8_t> Public_Key::subject_public_key() const
+ {
+ std::vector<uint8_t> output;
+
+ DER_Encoder(output).start_cons(SEQUENCE)
+ .encode(algorithm_identifier())
+ .encode(public_key_bits(), BIT_STRING)
+ .end_cons();
+
+ return output;
+ }
+
+/*
+* Default OID access
+*/
+OID Public_Key::get_oid() const
+ {
+ const OID o = OIDS::str2oid_or_empty(algo_name());
+ if(o.empty())
+ throw Lookup_Error("PK algo " + algo_name() + " has no defined OIDs");
+ return o;
+ }
+
+secure_vector<uint8_t> Private_Key::private_key_info() const
+ {
+ const size_t PKCS8_VERSION = 0;
+
+ return DER_Encoder()
+ .start_cons(SEQUENCE)
+ .encode(PKCS8_VERSION)
+ .encode(pkcs8_algorithm_identifier())
+ .encode(private_key_bits(), OCTET_STRING)
+ .end_cons()
+ .get_contents();
+ }
+
+/*
+* Hash of the X.509 subjectPublicKey encoding
+*/
+std::string Public_Key::fingerprint_public(const std::string& hash_algo) const
+ {
+ return create_hex_fingerprint(subject_public_key(), hash_algo);
+ }
+
+/*
+* Hash of the PKCS #8 encoding for this key object
+*/
+std::string Private_Key::fingerprint_private(const std::string& hash_algo) const
+ {
+ return create_hex_fingerprint(private_key_bits(), hash_algo);
+ }
+
+std::unique_ptr<PK_Ops::Encryption>
+Public_Key::create_encryption_op(RandomNumberGenerator& /*rng*/,
+ const std::string& /*params*/,
+ const std::string& /*provider*/) const
+ {
+ throw Lookup_Error(algo_name() + " does not support encryption");
+ }
+
+std::unique_ptr<PK_Ops::KEM_Encryption>
+Public_Key::create_kem_encryption_op(RandomNumberGenerator& /*rng*/,
+ const std::string& /*params*/,
+ const std::string& /*provider*/) const
+ {
+ throw Lookup_Error(algo_name() + " does not support KEM encryption");
+ }
+
+std::unique_ptr<PK_Ops::Verification>
+Public_Key::create_verification_op(const std::string& /*params*/,
+ const std::string& /*provider*/) const
+ {
+ throw Lookup_Error(algo_name() + " does not support verification");
+ }
+
+std::unique_ptr<PK_Ops::Decryption>
+Private_Key::create_decryption_op(RandomNumberGenerator& /*rng*/,
+ const std::string& /*params*/,
+ const std::string& /*provider*/) const
+ {
+ throw Lookup_Error(algo_name() + " does not support decryption");
+ }
+
+std::unique_ptr<PK_Ops::KEM_Decryption>
+Private_Key::create_kem_decryption_op(RandomNumberGenerator& /*rng*/,
+ const std::string& /*params*/,
+ const std::string& /*provider*/) const
+ {
+ throw Lookup_Error(algo_name() + " does not support KEM decryption");
+ }
+
+std::unique_ptr<PK_Ops::Signature>
+Private_Key::create_signature_op(RandomNumberGenerator& /*rng*/,
+ const std::string& /*params*/,
+ const std::string& /*provider*/) const
+ {
+ throw Lookup_Error(algo_name() + " does not support signatures");
+ }
+
+std::unique_ptr<PK_Ops::Key_Agreement>
+Private_Key::create_key_agreement_op(RandomNumberGenerator& /*rng*/,
+ const std::string& /*params*/,
+ const std::string& /*provider*/) const
+ {
+ throw Lookup_Error(algo_name() + " does not support key agreement");
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/pubkey/pk_keys.h b/comm/third_party/botan/src/lib/pubkey/pk_keys.h
new file mode 100644
index 0000000000..bf2be6bbd2
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/pk_keys.h
@@ -0,0 +1,329 @@
+/*
+* PK Key Types
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_PK_KEYS_H_
+#define BOTAN_PK_KEYS_H_
+
+#include <botan/secmem.h>
+#include <botan/asn1_obj.h>
+#include <botan/pk_ops_fwd.h>
+#include <string>
+
+namespace Botan {
+
+class RandomNumberGenerator;
+
+/**
+* The two types of signature format supported by Botan.
+*/
+enum Signature_Format { IEEE_1363, DER_SEQUENCE };
+
+/**
+* Public Key Base Class.
+*/
+class BOTAN_PUBLIC_API(2,0) Public_Key
+ {
+ public:
+ Public_Key() =default;
+ Public_Key(const Public_Key& other) = default;
+ Public_Key& operator=(const Public_Key& other) = default;
+ virtual ~Public_Key() = default;
+
+ /**
+ * Get the name of the underlying public key scheme.
+ * @return name of the public key scheme
+ */
+ virtual std::string algo_name() const = 0;
+
+ /**
+ * Return the estimated strength of the underlying key against
+ * the best currently known attack. Note that this ignores anything
+ * but pure attacks against the key itself and do not take into
+ * account padding schemes, usage mistakes, etc which might reduce
+ * the strength. However it does suffice to provide an upper bound.
+ *
+ * @return estimated strength in bits
+ */
+ virtual size_t estimated_strength() const = 0;
+
+ /**
+ * Return an integer value best approximating the length of the
+ * primary security parameter. For example for RSA this will be
+ * the size of the modulus, for ECDSA the size of the ECC group,
+ * and for McEliece the size of the code will be returned.
+ */
+ virtual size_t key_length() const = 0;
+
+ /**
+ * Get the OID of the underlying public key scheme.
+ * @return OID of the public key scheme
+ */
+ virtual OID get_oid() const;
+
+ /**
+ * 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
+ */
+ virtual bool check_key(RandomNumberGenerator& rng,
+ bool strong) const = 0;
+
+
+ /**
+ * @return X.509 AlgorithmIdentifier for this key
+ */
+ virtual AlgorithmIdentifier algorithm_identifier() const = 0;
+
+ /**
+ * @return BER encoded public key bits
+ */
+ virtual std::vector<uint8_t> public_key_bits() const = 0;
+
+ /**
+ * @return X.509 subject key encoding for this key object
+ */
+ std::vector<uint8_t> subject_public_key() const;
+
+ /**
+ * @return Hash of the subject public key
+ */
+ std::string fingerprint_public(const std::string& alg = "SHA-256") const;
+
+ // Internal or non-public declarations follow
+
+ /**
+ * Returns more than 1 if the output of this algorithm
+ * (ciphertext, signature) should be treated as more than one
+ * value. This is used for algorithms like DSA and ECDSA, where
+ * the (r,s) output pair can be encoded as either a plain binary
+ * list or a TLV tagged DER encoding depending on the protocol.
+ *
+ * This function is public but applications should have few
+ * reasons to ever call this.
+ *
+ * @return number of message parts
+ */
+ virtual size_t message_parts() const { return 1; }
+
+ /**
+ * Returns how large each of the message parts refered to
+ * by message_parts() is
+ *
+ * This function is public but applications should have few
+ * reasons to ever call this.
+ *
+ * @return size of the message parts in bits
+ */
+ virtual size_t message_part_size() const { return 0; }
+
+ virtual Signature_Format default_x509_signature_format() const
+ {
+ return (this->message_parts() >= 2) ? DER_SEQUENCE : IEEE_1363;
+ }
+
+ /**
+ * This is an internal library function exposed on key types.
+ * In almost all cases applications should use wrappers in pubkey.h
+ *
+ * Return an encryption operation for this key/params or throw
+ *
+ * @param rng a random number generator. The PK_Op may maintain a
+ * reference to the RNG and use it many times. The rng must outlive
+ * any operations which reference it.
+ * @param params additional parameters
+ * @param provider the provider to use
+ */
+ virtual std::unique_ptr<PK_Ops::Encryption>
+ create_encryption_op(RandomNumberGenerator& rng,
+ const std::string& params,
+ const std::string& provider) const;
+
+ /**
+ * This is an internal library function exposed on key types.
+ * In almost all cases applications should use wrappers in pubkey.h
+ *
+ * Return a KEM encryption operation for this key/params or throw
+ *
+ * @param rng a random number generator. The PK_Op may maintain a
+ * reference to the RNG and use it many times. The rng must outlive
+ * any operations which reference it.
+ * @param params additional parameters
+ * @param provider the provider to use
+ */
+ virtual std::unique_ptr<PK_Ops::KEM_Encryption>
+ create_kem_encryption_op(RandomNumberGenerator& rng,
+ const std::string& params,
+ const std::string& provider) const;
+
+ /**
+ * This is an internal library function exposed on key types.
+ * In almost all cases applications should use wrappers in pubkey.h
+ *
+ * Return a verification operation for this key/params or throw
+ * @param params additional parameters
+ * @param provider the provider to use
+ */
+ virtual std::unique_ptr<PK_Ops::Verification>
+ create_verification_op(const std::string& params,
+ const std::string& provider) const;
+ };
+
+/**
+* Private Key Base Class
+*/
+class BOTAN_PUBLIC_API(2,0) Private_Key : public virtual Public_Key
+ {
+ public:
+ Private_Key() = default;
+ Private_Key(const Private_Key& other) = default;
+ Private_Key& operator=(const Private_Key& other) = default;
+ virtual ~Private_Key() = default;
+
+ virtual bool stateful_operation() const { return false; }
+
+ /**
+ * @return BER encoded private key bits
+ */
+ virtual secure_vector<uint8_t> private_key_bits() const = 0;
+
+ /**
+ * @return PKCS #8 private key encoding for this key object
+ */
+ secure_vector<uint8_t> private_key_info() const;
+
+ /**
+ * @return PKCS #8 AlgorithmIdentifier for this key
+ * Might be different from the X.509 identifier, but normally is not
+ */
+ virtual AlgorithmIdentifier pkcs8_algorithm_identifier() const
+ { return algorithm_identifier(); }
+
+ // Internal or non-public declarations follow
+
+ /**
+ * @return Hash of the PKCS #8 encoding for this key object
+ */
+ std::string fingerprint_private(const std::string& alg) const;
+
+ BOTAN_DEPRECATED("Use fingerprint_private or fingerprint_public")
+ inline std::string fingerprint(const std::string& alg) const
+ {
+ return fingerprint_private(alg); // match behavior in previous versions
+ }
+
+ /**
+ * This is an internal library function exposed on key types.
+ * In almost all cases applications should use wrappers in pubkey.h
+ *
+ * Return an decryption operation for this key/params or throw
+ *
+ * @param rng a random number generator. The PK_Op may maintain a
+ * reference to the RNG and use it many times. The rng must outlive
+ * any operations which reference it.
+ * @param params additional parameters
+ * @param provider the provider to use
+ *
+ */
+ virtual std::unique_ptr<PK_Ops::Decryption>
+ create_decryption_op(RandomNumberGenerator& rng,
+ const std::string& params,
+ const std::string& provider) const;
+
+ /**
+ * This is an internal library function exposed on key types.
+ * In almost all cases applications should use wrappers in pubkey.h
+ *
+ * Return a KEM decryption operation for this key/params or throw
+ *
+ * @param rng a random number generator. The PK_Op may maintain a
+ * reference to the RNG and use it many times. The rng must outlive
+ * any operations which reference it.
+ * @param params additional parameters
+ * @param provider the provider to use
+ */
+ virtual std::unique_ptr<PK_Ops::KEM_Decryption>
+ create_kem_decryption_op(RandomNumberGenerator& rng,
+ const std::string& params,
+ const std::string& provider) const;
+
+ /**
+ * This is an internal library function exposed on key types.
+ * In almost all cases applications should use wrappers in pubkey.h
+ *
+ * Return a signature operation for this key/params or throw
+ *
+ * @param rng a random number generator. The PK_Op may maintain a
+ * reference to the RNG and use it many times. The rng must outlive
+ * any operations which reference it.
+ * @param params additional parameters
+ * @param provider the provider to use
+ */
+ virtual std::unique_ptr<PK_Ops::Signature>
+ create_signature_op(RandomNumberGenerator& rng,
+ const std::string& params,
+ const std::string& provider) const;
+
+ /**
+ * This is an internal library function exposed on key types.
+ * In almost all cases applications should use wrappers in pubkey.h
+ *
+ * Return a key agreement operation for this key/params or throw
+ *
+ * @param rng a random number generator. The PK_Op may maintain a
+ * reference to the RNG and use it many times. The rng must outlive
+ * any operations which reference it.
+ * @param params additional parameters
+ * @param provider the provider to use
+ */
+ virtual std::unique_ptr<PK_Ops::Key_Agreement>
+ create_key_agreement_op(RandomNumberGenerator& rng,
+ const std::string& params,
+ const std::string& provider) const;
+ };
+
+/**
+* PK Secret Value Derivation Key
+*/
+class BOTAN_PUBLIC_API(2,0) PK_Key_Agreement_Key : public virtual Private_Key
+ {
+ public:
+ /*
+ * @return public component of this key
+ */
+ virtual std::vector<uint8_t> public_value() const = 0;
+
+ PK_Key_Agreement_Key() = default;
+ PK_Key_Agreement_Key(const PK_Key_Agreement_Key&) = default;
+ PK_Key_Agreement_Key& operator=(const PK_Key_Agreement_Key&) = default;
+ virtual ~PK_Key_Agreement_Key() = default;
+ };
+
+/*
+* Old compat typedefs
+* TODO: remove these?
+*/
+typedef PK_Key_Agreement_Key PK_KA_Key;
+typedef Public_Key X509_PublicKey;
+typedef Private_Key PKCS8_PrivateKey;
+
+std::string BOTAN_PUBLIC_API(2,4)
+ create_hex_fingerprint(const uint8_t bits[], size_t len,
+ const std::string& hash_name);
+
+template<typename Alloc>
+std::string create_hex_fingerprint(const std::vector<uint8_t, Alloc>& vec,
+ const std::string& hash_name)
+ {
+ return create_hex_fingerprint(vec.data(), vec.size(), hash_name);
+ }
+
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/pubkey/pk_ops.cpp b/comm/third_party/botan/src/lib/pubkey/pk_ops.cpp
new file mode 100644
index 0000000000..025836878b
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/pk_ops.cpp
@@ -0,0 +1,173 @@
+/*
+* PK Operation Types
+* (C) 2010,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/pk_ops_impl.h>
+#include <botan/internal/bit_ops.h>
+#include <botan/rng.h>
+
+namespace Botan {
+
+PK_Ops::Encryption_with_EME::Encryption_with_EME(const std::string& eme)
+ {
+ m_eme.reset(get_eme(eme));
+ if(!m_eme.get())
+ throw Algorithm_Not_Found(eme);
+ }
+
+size_t PK_Ops::Encryption_with_EME::max_input_bits() const
+ {
+ return 8 * m_eme->maximum_input_size(max_raw_input_bits());
+ }
+
+secure_vector<uint8_t> PK_Ops::Encryption_with_EME::encrypt(const uint8_t msg[], size_t msg_len,
+ RandomNumberGenerator& rng)
+ {
+ const size_t max_raw = max_raw_input_bits();
+ const std::vector<uint8_t> encoded = unlock(m_eme->encode(msg, msg_len, max_raw, rng));
+ return raw_encrypt(encoded.data(), encoded.size(), rng);
+ }
+
+PK_Ops::Decryption_with_EME::Decryption_with_EME(const std::string& eme)
+ {
+ m_eme.reset(get_eme(eme));
+ if(!m_eme.get())
+ throw Algorithm_Not_Found(eme);
+ }
+
+secure_vector<uint8_t>
+PK_Ops::Decryption_with_EME::decrypt(uint8_t& valid_mask,
+ const uint8_t ciphertext[],
+ size_t ciphertext_len)
+ {
+ const secure_vector<uint8_t> raw = raw_decrypt(ciphertext, ciphertext_len);
+ return m_eme->unpad(valid_mask, raw.data(), raw.size());
+ }
+
+PK_Ops::Key_Agreement_with_KDF::Key_Agreement_with_KDF(const std::string& kdf)
+ {
+ if(kdf != "Raw")
+ m_kdf.reset(get_kdf(kdf));
+ }
+
+secure_vector<uint8_t> PK_Ops::Key_Agreement_with_KDF::agree(size_t key_len,
+ const uint8_t w[], size_t w_len,
+ const uint8_t salt[], size_t salt_len)
+ {
+ secure_vector<uint8_t> z = raw_agree(w, w_len);
+ if(m_kdf)
+ return m_kdf->derive_key(key_len, z, salt, salt_len);
+ return z;
+ }
+
+PK_Ops::Signature_with_EMSA::Signature_with_EMSA(const std::string& emsa) :
+ Signature(),
+ m_emsa(get_emsa(emsa)),
+ m_hash(hash_for_emsa(emsa)),
+ m_prefix_used(false)
+ {
+ if(!m_emsa)
+ throw Algorithm_Not_Found(emsa);
+ }
+
+void PK_Ops::Signature_with_EMSA::update(const uint8_t msg[], size_t msg_len)
+ {
+ if(has_prefix() && !m_prefix_used)
+ {
+ m_prefix_used = true;
+ secure_vector<uint8_t> prefix = message_prefix();
+ m_emsa->update(prefix.data(), prefix.size());
+ }
+ m_emsa->update(msg, msg_len);
+ }
+
+secure_vector<uint8_t> PK_Ops::Signature_with_EMSA::sign(RandomNumberGenerator& rng)
+ {
+ m_prefix_used = false;
+ const secure_vector<uint8_t> msg = m_emsa->raw_data();
+ const auto padded = m_emsa->encoding_of(msg, this->max_input_bits(), rng);
+ return raw_sign(padded.data(), padded.size(), rng);
+ }
+
+PK_Ops::Verification_with_EMSA::Verification_with_EMSA(const std::string& emsa) :
+ Verification(),
+ m_emsa(get_emsa(emsa)),
+ m_hash(hash_for_emsa(emsa)),
+ m_prefix_used(false)
+ {
+ if(!m_emsa)
+ throw Algorithm_Not_Found(emsa);
+ }
+
+void PK_Ops::Verification_with_EMSA::update(const uint8_t msg[], size_t msg_len)
+ {
+ if(has_prefix() && !m_prefix_used)
+ {
+ m_prefix_used = true;
+ secure_vector<uint8_t> prefix = message_prefix();
+ m_emsa->update(prefix.data(), prefix.size());
+ }
+ m_emsa->update(msg, msg_len);
+ }
+
+bool PK_Ops::Verification_with_EMSA::is_valid_signature(const uint8_t sig[], size_t sig_len)
+ {
+ m_prefix_used = false;
+ const secure_vector<uint8_t> msg = m_emsa->raw_data();
+
+ if(with_recovery())
+ {
+ secure_vector<uint8_t> output_of_key = verify_mr(sig, sig_len);
+ return m_emsa->verify(output_of_key, msg, max_input_bits());
+ }
+ else
+ {
+ Null_RNG rng;
+ secure_vector<uint8_t> encoded = m_emsa->encoding_of(msg, max_input_bits(), rng);
+ return verify(encoded.data(), encoded.size(), sig, sig_len);
+ }
+ }
+
+void PK_Ops::KEM_Encryption_with_KDF::kem_encrypt(secure_vector<uint8_t>& out_encapsulated_key,
+ secure_vector<uint8_t>& out_shared_key,
+ size_t desired_shared_key_len,
+ Botan::RandomNumberGenerator& rng,
+ const uint8_t salt[],
+ size_t salt_len)
+ {
+ secure_vector<uint8_t> raw_shared;
+ this->raw_kem_encrypt(out_encapsulated_key, raw_shared, rng);
+
+ out_shared_key = m_kdf->derive_key(desired_shared_key_len,
+ raw_shared.data(), raw_shared.size(),
+ salt, salt_len);
+ }
+
+PK_Ops::KEM_Encryption_with_KDF::KEM_Encryption_with_KDF(const std::string& kdf)
+ {
+ m_kdf.reset(get_kdf(kdf));
+ }
+
+secure_vector<uint8_t>
+PK_Ops::KEM_Decryption_with_KDF::kem_decrypt(const uint8_t encap_key[],
+ size_t len,
+ size_t desired_shared_key_len,
+ const uint8_t salt[],
+ size_t salt_len)
+ {
+ secure_vector<uint8_t> raw_shared = this->raw_kem_decrypt(encap_key, len);
+
+ return m_kdf->derive_key(desired_shared_key_len,
+ raw_shared.data(), raw_shared.size(),
+ salt, salt_len);
+ }
+
+PK_Ops::KEM_Decryption_with_KDF::KEM_Decryption_with_KDF(const std::string& kdf)
+ {
+ m_kdf.reset(get_kdf(kdf));
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/pubkey/pk_ops.h b/comm/third_party/botan/src/lib/pubkey/pk_ops.h
new file mode 100644
index 0000000000..63ef9fa9bd
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/pk_ops.h
@@ -0,0 +1,161 @@
+/*
+* (C) 2010,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_PK_OPERATIONS_H_
+#define BOTAN_PK_OPERATIONS_H_
+
+/**
+* Ordinary applications should never need to include or use this
+* header. It is exposed only for specialized applications which want
+* to implement new versions of public key crypto without merging them
+* as changes to the library. One actual example of such usage is an
+* application which creates RSA signatures using a custom TPM library.
+* Unless you're doing something like that, you don't need anything
+* here. Instead use pubkey.h which wraps these types safely and
+* provides a stable application-oriented API.
+*/
+
+#include <botan/pk_keys.h>
+#include <botan/secmem.h>
+
+namespace Botan {
+
+class RandomNumberGenerator;
+class EME;
+class KDF;
+class EMSA;
+
+namespace PK_Ops {
+
+/**
+* Public key encryption interface
+*/
+class BOTAN_PUBLIC_API(2,0) Encryption
+ {
+ public:
+ virtual secure_vector<uint8_t> encrypt(const uint8_t msg[],
+ size_t msg_len,
+ RandomNumberGenerator& rng) = 0;
+
+ virtual size_t max_input_bits() const = 0;
+
+ virtual size_t ciphertext_length(size_t ptext_len) const = 0;
+
+ virtual ~Encryption() = default;
+ };
+
+/**
+* Public key decryption interface
+*/
+class BOTAN_PUBLIC_API(2,0) Decryption
+ {
+ public:
+ virtual secure_vector<uint8_t> decrypt(uint8_t& valid_mask,
+ const uint8_t ciphertext[],
+ size_t ciphertext_len) = 0;
+
+ virtual size_t plaintext_length(size_t ctext_len) const = 0;
+
+ virtual ~Decryption() = default;
+ };
+
+/**
+* Public key signature verification interface
+*/
+class BOTAN_PUBLIC_API(2,0) Verification
+ {
+ public:
+ /*
+ * Add more data to the message currently being signed
+ * @param msg the message
+ * @param msg_len the length of msg in bytes
+ */
+ virtual void update(const uint8_t msg[], size_t msg_len) = 0;
+
+ /*
+ * Perform a verification operation
+ * @param rng a random number generator
+ */
+ virtual bool is_valid_signature(const uint8_t sig[], size_t sig_len) = 0;
+
+ virtual ~Verification() = default;
+ };
+
+/**
+* Public key signature creation interface
+*/
+class BOTAN_PUBLIC_API(2,0) Signature
+ {
+ public:
+ /*
+ * Add more data to the message currently being signed
+ * @param msg the message
+ * @param msg_len the length of msg in bytes
+ */
+ virtual void update(const uint8_t msg[], size_t msg_len) = 0;
+
+ /*
+ * Perform a signature operation
+ * @param rng a random number generator
+ */
+ virtual secure_vector<uint8_t> sign(RandomNumberGenerator& rng) = 0;
+
+ /*
+ * Return an upper bound on the length of the output signature
+ */
+ virtual size_t signature_length() const = 0;
+
+ virtual ~Signature() = default;
+ };
+
+/**
+* A generic key agreement operation (eg DH or ECDH)
+*/
+class BOTAN_PUBLIC_API(2,0) Key_Agreement
+ {
+ public:
+ virtual secure_vector<uint8_t> agree(size_t key_len,
+ const uint8_t other_key[], size_t other_key_len,
+ const uint8_t salt[], size_t salt_len) = 0;
+
+ virtual size_t agreed_value_size() const = 0;
+
+ virtual ~Key_Agreement() = default;
+ };
+
+/**
+* KEM (key encapsulation)
+*/
+class BOTAN_PUBLIC_API(2,0) KEM_Encryption
+ {
+ public:
+ virtual void kem_encrypt(secure_vector<uint8_t>& out_encapsulated_key,
+ secure_vector<uint8_t>& out_shared_key,
+ size_t desired_shared_key_len,
+ Botan::RandomNumberGenerator& rng,
+ const uint8_t salt[],
+ size_t salt_len) = 0;
+
+ virtual ~KEM_Encryption() = default;
+ };
+
+class BOTAN_PUBLIC_API(2,0) KEM_Decryption
+ {
+ public:
+ virtual secure_vector<uint8_t> kem_decrypt(const uint8_t encap_key[],
+ size_t len,
+ size_t desired_shared_key_len,
+ const uint8_t salt[],
+ size_t salt_len) = 0;
+
+ virtual ~KEM_Decryption() = default;
+ };
+
+}
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/pubkey/pk_ops_fwd.h b/comm/third_party/botan/src/lib/pubkey/pk_ops_fwd.h
new file mode 100644
index 0000000000..92a3c2a969
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/pk_ops_fwd.h
@@ -0,0 +1,27 @@
+/*
+* PK Operation Types Forward Decls
+* (C) 2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_PK_OPERATIONS_FWD_H_
+#define BOTAN_PK_OPERATIONS_FWD_H_
+
+namespace Botan {
+
+namespace PK_Ops {
+
+class Encryption;
+class Decryption;
+class Verification;
+class Signature;
+class Key_Agreement;
+class KEM_Encryption;
+class KEM_Decryption;
+
+}
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/pubkey/pk_ops_impl.h b/comm/third_party/botan/src/lib/pubkey/pk_ops_impl.h
new file mode 100644
index 0000000000..6bab2143e1
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/pk_ops_impl.h
@@ -0,0 +1,231 @@
+
+/*
+* (C) 2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_PK_OPERATION_IMPL_H_
+#define BOTAN_PK_OPERATION_IMPL_H_
+
+#include <botan/pk_ops.h>
+#include <botan/eme.h>
+#include <botan/kdf.h>
+#include <botan/emsa.h>
+
+namespace Botan {
+
+namespace PK_Ops {
+
+class Encryption_with_EME : public Encryption
+ {
+ public:
+ size_t max_input_bits() const override;
+
+ secure_vector<uint8_t> encrypt(const uint8_t msg[], size_t msg_len,
+ RandomNumberGenerator& rng) override;
+
+ ~Encryption_with_EME() = default;
+ protected:
+ explicit Encryption_with_EME(const std::string& eme);
+ private:
+ virtual size_t max_raw_input_bits() const = 0;
+
+ virtual secure_vector<uint8_t> raw_encrypt(const uint8_t msg[], size_t len,
+ RandomNumberGenerator& rng) = 0;
+ std::unique_ptr<EME> m_eme;
+ };
+
+class Decryption_with_EME : public Decryption
+ {
+ public:
+ secure_vector<uint8_t> decrypt(uint8_t& valid_mask,
+ const uint8_t msg[], size_t msg_len) override;
+
+ ~Decryption_with_EME() = default;
+ protected:
+ explicit Decryption_with_EME(const std::string& eme);
+ private:
+ virtual secure_vector<uint8_t> raw_decrypt(const uint8_t msg[], size_t len) = 0;
+ std::unique_ptr<EME> m_eme;
+ };
+
+class Verification_with_EMSA : public Verification
+ {
+ public:
+ ~Verification_with_EMSA() = default;
+
+ void update(const uint8_t msg[], size_t msg_len) override;
+ bool is_valid_signature(const uint8_t sig[], size_t sig_len) override;
+
+ bool do_check(const secure_vector<uint8_t>& msg,
+ const uint8_t sig[], size_t sig_len);
+
+ std::string hash_for_signature() { return m_hash; }
+
+ protected:
+ explicit Verification_with_EMSA(const std::string& emsa);
+
+ /**
+ * Get the maximum message size in bits supported by this public key.
+ * @return maximum message in bits
+ */
+ virtual size_t max_input_bits() const = 0;
+
+ /**
+ * @return boolean specifying if this signature scheme uses
+ * a message prefix returned by message_prefix()
+ */
+ virtual bool has_prefix() { return false; }
+
+ /**
+ * @return the message prefix if this signature scheme uses
+ * a message prefix, signaled via has_prefix()
+ */
+ virtual secure_vector<uint8_t> message_prefix() const { throw Invalid_State("No prefix"); }
+
+ /**
+ * @return boolean specifying if this key type supports message
+ * recovery and thus if you need to call verify() or verify_mr()
+ */
+ virtual bool with_recovery() const = 0;
+
+ /*
+ * Perform a signature check operation
+ * @param msg the message
+ * @param msg_len the length of msg in bytes
+ * @param sig the signature
+ * @param sig_len the length of sig in bytes
+ * @returns if signature is a valid one for message
+ */
+ virtual bool verify(const uint8_t[], size_t,
+ const uint8_t[], size_t)
+ {
+ throw Invalid_State("Message recovery required");
+ }
+
+ /*
+ * Perform a signature operation (with message recovery)
+ * Only call this if with_recovery() returns true
+ * @param msg the message
+ * @param msg_len the length of msg in bytes
+ * @returns recovered message
+ */
+ virtual secure_vector<uint8_t> verify_mr(const uint8_t[], size_t)
+ {
+ throw Invalid_State("Message recovery not supported");
+ }
+
+ std::unique_ptr<EMSA> clone_emsa() const { return std::unique_ptr<EMSA>(m_emsa->clone()); }
+
+ private:
+ std::unique_ptr<EMSA> m_emsa;
+ const std::string m_hash;
+ bool m_prefix_used;
+ };
+
+class Signature_with_EMSA : public Signature
+ {
+ public:
+ void update(const uint8_t msg[], size_t msg_len) override;
+
+ secure_vector<uint8_t> sign(RandomNumberGenerator& rng) override;
+ protected:
+ explicit Signature_with_EMSA(const std::string& emsa);
+ ~Signature_with_EMSA() = default;
+
+ std::string hash_for_signature() { return m_hash; }
+
+ /**
+ * @return boolean specifying if this signature scheme uses
+ * a message prefix returned by message_prefix()
+ */
+ virtual bool has_prefix() { return false; }
+
+ /**
+ * @return the message prefix if this signature scheme uses
+ * a message prefix, signaled via has_prefix()
+ */
+ virtual secure_vector<uint8_t> message_prefix() const { throw Invalid_State("No prefix"); }
+
+ std::unique_ptr<EMSA> clone_emsa() const { return std::unique_ptr<EMSA>(m_emsa->clone()); }
+
+ private:
+
+ /**
+ * Get the maximum message size in bits supported by this public key.
+ * @return maximum message in bits
+ */
+ virtual size_t max_input_bits() const = 0;
+
+ bool self_test_signature(const std::vector<uint8_t>& msg,
+ const std::vector<uint8_t>& sig) const;
+
+ virtual secure_vector<uint8_t> raw_sign(const uint8_t msg[], size_t msg_len,
+ RandomNumberGenerator& rng) = 0;
+
+ std::unique_ptr<EMSA> m_emsa;
+ const std::string m_hash;
+ bool m_prefix_used;
+ };
+
+class Key_Agreement_with_KDF : public Key_Agreement
+ {
+ public:
+ secure_vector<uint8_t> agree(size_t key_len,
+ const uint8_t other_key[], size_t other_key_len,
+ const uint8_t salt[], size_t salt_len) override;
+
+ protected:
+ explicit Key_Agreement_with_KDF(const std::string& kdf);
+ ~Key_Agreement_with_KDF() = default;
+ private:
+ virtual secure_vector<uint8_t> raw_agree(const uint8_t w[], size_t w_len) = 0;
+ std::unique_ptr<KDF> m_kdf;
+ };
+
+class KEM_Encryption_with_KDF : public KEM_Encryption
+ {
+ public:
+ void kem_encrypt(secure_vector<uint8_t>& out_encapsulated_key,
+ secure_vector<uint8_t>& out_shared_key,
+ size_t desired_shared_key_len,
+ Botan::RandomNumberGenerator& rng,
+ const uint8_t salt[],
+ size_t salt_len) override;
+
+ protected:
+ virtual void raw_kem_encrypt(secure_vector<uint8_t>& out_encapsulated_key,
+ secure_vector<uint8_t>& raw_shared_key,
+ Botan::RandomNumberGenerator& rng) = 0;
+
+ explicit KEM_Encryption_with_KDF(const std::string& kdf);
+ ~KEM_Encryption_with_KDF() = default;
+ private:
+ std::unique_ptr<KDF> m_kdf;
+ };
+
+class KEM_Decryption_with_KDF : public KEM_Decryption
+ {
+ public:
+ secure_vector<uint8_t> kem_decrypt(const uint8_t encap_key[],
+ size_t len,
+ size_t desired_shared_key_len,
+ const uint8_t salt[],
+ size_t salt_len) override;
+
+ protected:
+ virtual secure_vector<uint8_t>
+ raw_kem_decrypt(const uint8_t encap_key[], size_t len) = 0;
+
+ explicit KEM_Decryption_with_KDF(const std::string& kdf);
+ ~KEM_Decryption_with_KDF() = default;
+ private:
+ std::unique_ptr<KDF> m_kdf;
+ };
+
+}
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/pubkey/pkcs8.cpp b/comm/third_party/botan/src/lib/pubkey/pkcs8.cpp
new file mode 100644
index 0000000000..2989e20aa0
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/pkcs8.cpp
@@ -0,0 +1,490 @@
+/*
+* PKCS #8
+* (C) 1999-2010,2014,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/pkcs8.h>
+#include <botan/rng.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
+#include <botan/asn1_obj.h>
+#include <botan/oids.h>
+#include <botan/pem.h>
+#include <botan/scan_name.h>
+#include <botan/pk_algs.h>
+
+#if defined(BOTAN_HAS_PKCS5_PBES2)
+ #include <botan/pbes2.h>
+#endif
+
+namespace Botan {
+
+namespace PKCS8 {
+
+namespace {
+
+/*
+* Get info from an EncryptedPrivateKeyInfo
+*/
+secure_vector<uint8_t> PKCS8_extract(DataSource& source,
+ AlgorithmIdentifier& pbe_alg_id)
+ {
+ secure_vector<uint8_t> key_data;
+
+ BER_Decoder(source)
+ .start_cons(SEQUENCE)
+ .decode(pbe_alg_id)
+ .decode(key_data, OCTET_STRING)
+ .verify_end();
+
+ return key_data;
+ }
+
+/*
+* PEM decode and/or decrypt a private key
+*/
+secure_vector<uint8_t> PKCS8_decode(
+ DataSource& source,
+ std::function<std::string ()> get_passphrase,
+ AlgorithmIdentifier& pk_alg_id,
+ bool is_encrypted)
+ {
+ AlgorithmIdentifier pbe_alg_id;
+ secure_vector<uint8_t> key_data, key;
+
+ try {
+ if(ASN1::maybe_BER(source) && !PEM_Code::matches(source))
+ {
+ if(is_encrypted)
+ {
+ 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);
+ }
+ }
+ }
+ }
+ else
+ {
+ std::string label;
+ key_data = PEM_Code::decode(source, label);
+
+ // todo remove autodetect for pem as well?
+ if(label == "PRIVATE KEY")
+ is_encrypted = false;
+ else if(label == "ENCRYPTED PRIVATE KEY")
+ {
+ DataSource_Memory key_source(key_data);
+ key_data = PKCS8_extract(key_source, pbe_alg_id);
+ }
+ else
+ throw PKCS8_Exception("Unknown PEM label " + label);
+ }
+
+ if(key_data.empty())
+ throw PKCS8_Exception("No key data found");
+ }
+ catch(Decoding_Error& e)
+ {
+ throw Decoding_Error("PKCS #8 private key decoding", e);
+ }
+
+ try
+ {
+ if(is_encrypted)
+ {
+ if(OIDS::oid2str_or_throw(pbe_alg_id.get_oid()) != "PBE-PKCS5v20")
+ throw PKCS8_Exception("Unknown PBE type " + pbe_alg_id.get_oid().to_string());
+#if defined(BOTAN_HAS_PKCS5_PBES2)
+ key = pbes2_decrypt(key_data, get_passphrase(), pbe_alg_id.get_parameters());
+#else
+ BOTAN_UNUSED(get_passphrase);
+ throw Decoding_Error("Private key is encrypted but PBES2 was disabled in build");
+#endif
+ }
+ else
+ key = key_data;
+
+ BER_Decoder(key)
+ .start_cons(SEQUENCE)
+ .decode_and_check<size_t>(0, "Unknown PKCS #8 version number")
+ .decode(pk_alg_id)
+ .decode(key, OCTET_STRING)
+ .discard_remaining()
+ .end_cons();
+ }
+ catch(std::exception& e)
+ {
+ throw Decoding_Error("PKCS #8 private key decoding", e);
+ }
+ return key;
+ }
+
+}
+
+/*
+* BER encode a PKCS #8 private key, unencrypted
+*/
+secure_vector<uint8_t> BER_encode(const Private_Key& key)
+ {
+ // keeping around for compat
+ return key.private_key_info();
+ }
+
+/*
+* PEM encode a PKCS #8 private key, unencrypted
+*/
+std::string PEM_encode(const Private_Key& key)
+ {
+ return PEM_Code::encode(PKCS8::BER_encode(key), "PRIVATE KEY");
+ }
+
+#if defined(BOTAN_HAS_PKCS5_PBES2)
+
+namespace {
+
+std::pair<std::string, std::string>
+choose_pbe_params(const std::string& pbe_algo, const std::string& key_algo)
+ {
+ if(pbe_algo.empty())
+ {
+ /*
+ * For algorithms where we are using a non-RFC format anyway, default to
+ * SIV or GCM. For others (RSA, ECDSA, ...) default to something widely
+ * compatible.
+ */
+ const bool nonstandard_pk = (key_algo == "McEliece" || key_algo == "XMSS");
+
+ if(nonstandard_pk)
+ {
+#if defined(BOTAN_HAS_AEAD_SIV) && defined(BOTAN_HAS_SHA2_64)
+ return std::make_pair("AES-256/SIV", "SHA-512");
+#elif defined(BOTAN_HAS_AEAD_GCM) && defined(BOTAN_HAS_SHA2_64)
+ return std::make_pair("AES-256/GCM", "SHA-512");
+#endif
+ }
+
+ // Default is something compatible with everyone else
+ return std::make_pair("AES-256/CBC", "SHA-256");
+ }
+
+ SCAN_Name request(pbe_algo);
+
+ if(request.arg_count() != 2 ||
+ (request.algo_name() != "PBE-PKCS5v20" && request.algo_name() != "PBES2"))
+ {
+ throw Invalid_Argument("Unsupported PBE " + pbe_algo);
+ }
+
+ return std::make_pair(request.arg(0), request.arg(1));
+ }
+
+}
+
+#endif
+
+/*
+* BER encode a PKCS #8 private key, encrypted
+*/
+std::vector<uint8_t> BER_encode(const Private_Key& key,
+ RandomNumberGenerator& rng,
+ const std::string& pass,
+ std::chrono::milliseconds msec,
+ const std::string& pbe_algo)
+ {
+#if defined(BOTAN_HAS_PKCS5_PBES2)
+ const auto pbe_params = choose_pbe_params(pbe_algo, key.algo_name());
+
+ const std::pair<AlgorithmIdentifier, std::vector<uint8_t>> pbe_info =
+ pbes2_encrypt_msec(PKCS8::BER_encode(key), pass, msec, nullptr,
+ pbe_params.first, pbe_params.second, rng);
+
+ std::vector<uint8_t> output;
+ DER_Encoder der(output);
+ der.start_cons(SEQUENCE)
+ .encode(pbe_info.first)
+ .encode(pbe_info.second, OCTET_STRING)
+ .end_cons();
+
+ return output;
+#else
+ BOTAN_UNUSED(key, rng, pass, msec, pbe_algo);
+ throw Encoding_Error("PKCS8::BER_encode cannot encrypt because PBES2 was disabled in build");
+#endif
+ }
+
+/*
+* PEM encode a PKCS #8 private key, encrypted
+*/
+std::string PEM_encode(const Private_Key& key,
+ RandomNumberGenerator& rng,
+ const std::string& pass,
+ std::chrono::milliseconds msec,
+ const std::string& pbe_algo)
+ {
+ if(pass.empty())
+ return PEM_encode(key);
+
+ return PEM_Code::encode(PKCS8::BER_encode(key, rng, pass, msec, pbe_algo),
+ "ENCRYPTED PRIVATE KEY");
+ }
+
+/*
+* BER encode a PKCS #8 private key, encrypted
+*/
+std::vector<uint8_t> BER_encode_encrypted_pbkdf_iter(const Private_Key& key,
+ RandomNumberGenerator& rng,
+ const std::string& pass,
+ size_t pbkdf_iterations,
+ const std::string& cipher,
+ const std::string& pbkdf_hash)
+ {
+#if defined(BOTAN_HAS_PKCS5_PBES2)
+ const std::pair<AlgorithmIdentifier, std::vector<uint8_t>> pbe_info =
+ pbes2_encrypt_iter(key.private_key_info(),
+ pass, pbkdf_iterations,
+ cipher.empty() ? "AES-256/CBC" : cipher,
+ pbkdf_hash.empty() ? "SHA-256" : pbkdf_hash,
+ rng);
+
+ std::vector<uint8_t> output;
+ DER_Encoder der(output);
+ der.start_cons(SEQUENCE)
+ .encode(pbe_info.first)
+ .encode(pbe_info.second, OCTET_STRING)
+ .end_cons();
+
+ return output;
+
+#else
+ BOTAN_UNUSED(key, rng, pass, pbkdf_iterations, cipher, pbkdf_hash);
+ throw Encoding_Error("PKCS8::BER_encode_encrypted_pbkdf_iter cannot encrypt because PBES2 disabled in build");
+#endif
+ }
+
+/*
+* PEM encode a PKCS #8 private key, encrypted
+*/
+std::string PEM_encode_encrypted_pbkdf_iter(const Private_Key& key,
+ RandomNumberGenerator& rng,
+ const std::string& pass,
+ size_t pbkdf_iterations,
+ const std::string& cipher,
+ const std::string& pbkdf_hash)
+ {
+ return PEM_Code::encode(
+ PKCS8::BER_encode_encrypted_pbkdf_iter(key, rng, pass, pbkdf_iterations, cipher, pbkdf_hash),
+ "ENCRYPTED PRIVATE KEY");
+ }
+
+/*
+* BER encode a PKCS #8 private key, encrypted
+*/
+std::vector<uint8_t> BER_encode_encrypted_pbkdf_msec(const Private_Key& key,
+ RandomNumberGenerator& rng,
+ const std::string& pass,
+ std::chrono::milliseconds pbkdf_msec,
+ size_t* pbkdf_iterations,
+ const std::string& cipher,
+ const std::string& pbkdf_hash)
+ {
+#if defined(BOTAN_HAS_PKCS5_PBES2)
+ const std::pair<AlgorithmIdentifier, std::vector<uint8_t>> pbe_info =
+ pbes2_encrypt_msec(key.private_key_info(), pass,
+ pbkdf_msec, pbkdf_iterations,
+ cipher.empty() ? "AES-256/CBC" : cipher,
+ pbkdf_hash.empty() ? "SHA-256" : pbkdf_hash,
+ rng);
+
+ std::vector<uint8_t> output;
+ DER_Encoder(output)
+ .start_cons(SEQUENCE)
+ .encode(pbe_info.first)
+ .encode(pbe_info.second, OCTET_STRING)
+ .end_cons();
+
+ return output;
+#else
+ BOTAN_UNUSED(key, rng, pass, pbkdf_msec, pbkdf_iterations, cipher, pbkdf_hash);
+ throw Encoding_Error("BER_encode_encrypted_pbkdf_msec cannot encrypt because PBES2 disabled in build");
+#endif
+ }
+
+/*
+* PEM encode a PKCS #8 private key, encrypted
+*/
+std::string PEM_encode_encrypted_pbkdf_msec(const Private_Key& key,
+ RandomNumberGenerator& rng,
+ const std::string& pass,
+ std::chrono::milliseconds pbkdf_msec,
+ size_t* pbkdf_iterations,
+ const std::string& cipher,
+ const std::string& pbkdf_hash)
+ {
+ return PEM_Code::encode(
+ PKCS8::BER_encode_encrypted_pbkdf_msec(key, rng, pass, pbkdf_msec, pbkdf_iterations, cipher, pbkdf_hash),
+ "ENCRYPTED PRIVATE KEY");
+ }
+
+namespace {
+
+/*
+* Extract a private key (encrypted/unencrypted) and return it
+*/
+std::unique_ptr<Private_Key>
+load_key(DataSource& source,
+ std::function<std::string ()> get_pass,
+ bool is_encrypted)
+ {
+ AlgorithmIdentifier alg_id;
+ secure_vector<uint8_t> pkcs8_key = PKCS8_decode(source, get_pass, alg_id, is_encrypted);
+
+ const std::string alg_name = OIDS::oid2str_or_empty(alg_id.get_oid());
+ if(alg_name.empty())
+ throw PKCS8_Exception("Unknown algorithm OID: " +
+ alg_id.get_oid().to_string());
+
+ return load_private_key(alg_id, pkcs8_key);
+ }
+
+}
+
+/*
+* Extract an encrypted private key and return it
+*/
+std::unique_ptr<Private_Key> load_key(DataSource& source,
+ std::function<std::string ()> get_pass)
+ {
+ return load_key(source, get_pass, true);
+ }
+
+/*
+* Extract an encrypted private key and return it
+*/
+std::unique_ptr<Private_Key> load_key(DataSource& source,
+ const std::string& pass)
+ {
+ // We need to use bind rather than a lambda capturing `pass` here in order to avoid a Clang 8 bug.
+ // See https://github.com/randombit/botan/issues/2255.
+ return load_key(source, std::bind([](const std::string p) { return p; }, pass), true);
+ }
+
+/*
+* Extract an unencrypted private key and return it
+*/
+std::unique_ptr<Private_Key> load_key(DataSource& source)
+ {
+ auto fail_fn = []() -> std::string {
+ throw PKCS8_Exception("Internal error: Attempt to read password for unencrypted key");
+ };
+
+ return load_key(source, fail_fn, false);
+ }
+
+/*
+* Make a copy of this private key
+*/
+std::unique_ptr<Private_Key> copy_key(const Private_Key& key)
+ {
+ DataSource_Memory source(PEM_encode(key));
+ return PKCS8::load_key(source);
+ }
+
+/*
+* Extract an encrypted private key and return it
+*/
+Private_Key* load_key(DataSource& source,
+ RandomNumberGenerator& rng,
+ std::function<std::string ()> get_pass)
+ {
+ BOTAN_UNUSED(rng);
+ return PKCS8::load_key(source, get_pass).release();
+ }
+
+/*
+* Extract an encrypted private key and return it
+*/
+Private_Key* load_key(DataSource& source,
+ RandomNumberGenerator& rng,
+ const std::string& pass)
+ {
+ BOTAN_UNUSED(rng);
+ return PKCS8::load_key(source, pass).release();
+ }
+
+/*
+* Extract an unencrypted private key and return it
+*/
+Private_Key* load_key(DataSource& source,
+ RandomNumberGenerator& rng)
+ {
+ BOTAN_UNUSED(rng);
+ return PKCS8::load_key(source).release();
+ }
+
+#if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
+
+/*
+* Extract an encrypted private key and return it
+*/
+Private_Key* load_key(const std::string& fsname,
+ RandomNumberGenerator& rng,
+ std::function<std::string ()> get_pass)
+ {
+ BOTAN_UNUSED(rng);
+ DataSource_Stream in(fsname);
+ return PKCS8::load_key(in, get_pass).release();
+ }
+
+/*
+* Extract an encrypted private key and return it
+*/
+Private_Key* load_key(const std::string& fsname,
+ RandomNumberGenerator& rng,
+ const std::string& pass)
+ {
+ BOTAN_UNUSED(rng);
+ DataSource_Stream in(fsname);
+ // We need to use bind rather than a lambda capturing `pass` here in order to avoid a Clang 8 bug.
+ // See https://github.com/randombit/botan/issues/2255.
+ return PKCS8::load_key(in, std::bind([](const std::string p) { return p; }, pass)).release();
+ }
+
+/*
+* Extract an unencrypted private key and return it
+*/
+Private_Key* load_key(const std::string& fsname,
+ RandomNumberGenerator& rng)
+ {
+ BOTAN_UNUSED(rng);
+ DataSource_Stream in(fsname);
+ return PKCS8::load_key(in).release();
+ }
+#endif
+
+/*
+* Make a copy of this private key
+*/
+Private_Key* copy_key(const Private_Key& key,
+ RandomNumberGenerator& rng)
+ {
+ BOTAN_UNUSED(rng);
+ return PKCS8::copy_key(key).release();
+ }
+
+
+
+}
+
+}
diff --git a/comm/third_party/botan/src/lib/pubkey/pkcs8.h b/comm/third_party/botan/src/lib/pubkey/pkcs8.h
new file mode 100644
index 0000000000..a243c4fda4
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/pkcs8.h
@@ -0,0 +1,288 @@
+/*
+* PKCS #8
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_PKCS8_H_
+#define BOTAN_PKCS8_H_
+
+#include <botan/pk_keys.h>
+#include <botan/exceptn.h>
+#include <botan/secmem.h>
+#include <functional>
+#include <chrono>
+#include <memory>
+
+namespace Botan {
+
+class DataSource;
+class RandomNumberGenerator;
+
+/**
+* PKCS #8 General Exception
+*/
+class BOTAN_PUBLIC_API(2,0) PKCS8_Exception final : public Decoding_Error
+ {
+ public:
+ explicit PKCS8_Exception(const std::string& error) :
+ Decoding_Error("PKCS #8: " + error) {}
+ };
+
+/**
+* This namespace contains functions for handling PKCS #8 private keys
+*/
+namespace PKCS8 {
+
+/**
+* BER encode a private key
+* @param key the private key to encode
+* @return BER encoded key
+*/
+BOTAN_PUBLIC_API(2,0) secure_vector<uint8_t> BER_encode(const Private_Key& key);
+
+/**
+* Get a string containing a PEM encoded private key.
+* @param key the key to encode
+* @return encoded key
+*/
+BOTAN_PUBLIC_API(2,0) std::string PEM_encode(const Private_Key& key);
+
+/**
+* Encrypt a key using PKCS #8 encryption
+* @param key the key to encode
+* @param rng the rng to use
+* @param pass the password to use for encryption
+* @param msec number of milliseconds to run the password derivation
+* @param pbe_algo the name of the desired password-based encryption
+* algorithm; if empty ("") a reasonable (portable/secure)
+* default will be chosen.
+* @return encrypted key in binary BER form
+*/
+BOTAN_PUBLIC_API(2,0) std::vector<uint8_t>
+BER_encode(const Private_Key& key,
+ RandomNumberGenerator& rng,
+ const std::string& pass,
+ std::chrono::milliseconds msec = std::chrono::milliseconds(300),
+ const std::string& pbe_algo = "");
+
+/**
+* Get a string containing a PEM encoded private key, encrypting it with a
+* password.
+* @param key the key to encode
+* @param rng the rng to use
+* @param pass the password to use for encryption
+* @param msec number of milliseconds to run the password derivation
+* @param pbe_algo the name of the desired password-based encryption
+* algorithm; if empty ("") a reasonable (portable/secure)
+* default will be chosen.
+* @return encrypted key in PEM form
+*/
+BOTAN_PUBLIC_API(2,0) std::string
+PEM_encode(const Private_Key& key,
+ RandomNumberGenerator& rng,
+ const std::string& pass,
+ std::chrono::milliseconds msec = std::chrono::milliseconds(300),
+ const std::string& pbe_algo = "");
+
+/**
+* Encrypt a key using PKCS #8 encryption and a fixed iteration count
+* @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 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.
+* @param pbkdf_hash if non-empty specifies the PBKDF hash function to use.
+* For example "SHA-256" or "SHA-384". If empty a suitable default is chosen.
+* @return encrypted key in binary BER form
+*/
+BOTAN_PUBLIC_API(2,1) std::vector<uint8_t>
+BER_encode_encrypted_pbkdf_iter(const Private_Key& key,
+ RandomNumberGenerator& rng,
+ const std::string& pass,
+ size_t pbkdf_iter,
+ const std::string& cipher = "",
+ const std::string& pbkdf_hash = "");
+
+/**
+* Get a string containing a PEM encoded private key, encrypting it with a
+* password.
+* @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 iterations to run PBKDF
+* @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.
+* @param pbkdf_hash if non-empty specifies the PBKDF hash function to use.
+* For example "SHA-256" or "SHA-384". If empty a suitable default is chosen.
+* @return encrypted key in PEM form
+*/
+BOTAN_PUBLIC_API(2,1) std::string
+PEM_encode_encrypted_pbkdf_iter(const Private_Key& key,
+ RandomNumberGenerator& rng,
+ const std::string& pass,
+ size_t pbkdf_iter,
+ const std::string& cipher = "",
+ const std::string& pbkdf_hash = "");
+
+/**
+* Encrypt a key using PKCS #8 encryption and a variable iteration count
+* @param key the key to encode
+* @param rng the rng to use
+* @param pass the password to use for encryption
+* @param pbkdf_msec how long to run PBKDF2
+* @param pbkdf_iterations if non-null, set to the number of iterations used
+* @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.
+* @param pbkdf_hash if non-empty specifies the PBKDF hash function to use.
+* For example "SHA-256" or "SHA-384". If empty a suitable default is chosen.
+* @return encrypted key in binary BER form
+*/
+BOTAN_PUBLIC_API(2,1) std::vector<uint8_t>
+BER_encode_encrypted_pbkdf_msec(const Private_Key& key,
+ RandomNumberGenerator& rng,
+ const std::string& pass,
+ std::chrono::milliseconds pbkdf_msec,
+ size_t* pbkdf_iterations,
+ const std::string& cipher = "",
+ const std::string& pbkdf_hash = "");
+
+/**
+* Get a string containing a PEM encoded private key, encrypting it with a
+* password.
+* @param key the key to encode
+* @param rng the rng to use
+* @param pass the password to use for encryption
+* @param pbkdf_msec how long in milliseconds to run PBKDF2
+* @param pbkdf_iterations (output argument) number of iterations of PBKDF
+* that ended up being used
+* @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.
+* @param pbkdf_hash if non-empty specifies the PBKDF hash function to use.
+* For example "SHA-256" or "SHA-384". If empty a suitable default is chosen.
+* @return encrypted key in PEM form
+*/
+BOTAN_PUBLIC_API(2,1) std::string
+PEM_encode_encrypted_pbkdf_msec(const Private_Key& key,
+ RandomNumberGenerator& rng,
+ const std::string& pass,
+ std::chrono::milliseconds pbkdf_msec,
+ size_t* pbkdf_iterations,
+ const std::string& cipher = "",
+ const std::string& pbkdf_hash = "");
+
+/**
+* Load an encrypted key from a data source.
+* @param source the data source providing the encoded key
+* @param rng ignored for compatibility
+* @param get_passphrase a function that returns passphrases
+* @return loaded private key object
+*/
+BOTAN_PUBLIC_API(2,0) Private_Key* load_key(DataSource& source,
+ RandomNumberGenerator& rng,
+ std::function<std::string ()> get_passphrase);
+
+/** Load an encrypted key from a data source.
+* @param source the data source providing the encoded key
+* @param rng ignored for compatibility
+* @param pass the passphrase to decrypt the key
+* @return loaded private key object
+*/
+BOTAN_PUBLIC_API(2,0) Private_Key* load_key(DataSource& source,
+ RandomNumberGenerator& rng,
+ const std::string& pass);
+
+/** Load an unencrypted key from a data source.
+* @param source the data source providing the encoded key
+* @param rng ignored for compatibility
+* @return loaded private key object
+*/
+BOTAN_PUBLIC_API(2,0) Private_Key* load_key(DataSource& source,
+ RandomNumberGenerator& rng);
+
+#if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
+/**
+* Load an encrypted key from a file.
+* @param filename the path to the file containing the encoded key
+* @param rng ignored for compatibility
+* @param get_passphrase a function that returns passphrases
+* @return loaded private key object
+*/
+BOTAN_PUBLIC_API(2,0) Private_Key* load_key(const std::string& filename,
+ RandomNumberGenerator& rng,
+ std::function<std::string ()> get_passphrase);
+
+/** Load an encrypted key from a file.
+* @param filename the path to the file containing the encoded key
+* @param rng ignored for compatibility
+* @param pass the passphrase to decrypt the key
+* @return loaded private key object
+*/
+BOTAN_PUBLIC_API(2,0) Private_Key* load_key(const std::string& filename,
+ RandomNumberGenerator& rng,
+ const std::string& pass);
+
+/** Load an unencrypted key from a file.
+* @param filename the path to the file containing the encoded key
+* @param rng ignored for compatibility
+* @return loaded private key object
+*/
+BOTAN_PUBLIC_API(2,0) Private_Key* load_key(const std::string& filename,
+ RandomNumberGenerator& rng);
+#endif
+
+/**
+* Copy an existing encoded key object.
+* @param key the key to copy
+* @param rng ignored for compatibility
+* @return new copy of the key
+*/
+BOTAN_PUBLIC_API(2,0) Private_Key* copy_key(const Private_Key& key,
+ RandomNumberGenerator& rng);
+
+
+/**
+* Load an encrypted key from a data source.
+* @param source the data source providing the encoded key
+* @param get_passphrase a function that returns passphrases
+* @return loaded private key object
+*/
+BOTAN_PUBLIC_API(2,3)
+std::unique_ptr<Private_Key> load_key(DataSource& source,
+ std::function<std::string ()> get_passphrase);
+
+/** Load an encrypted key from a data source.
+* @param source the data source providing the encoded key
+* @param pass the passphrase to decrypt the key
+* @return loaded private key object
+*/
+BOTAN_PUBLIC_API(2,3)
+std::unique_ptr<Private_Key> load_key(DataSource& source,
+ const std::string& pass);
+
+/** Load an unencrypted key from a data source.
+* @param source the data source providing the encoded key
+* @return loaded private key object
+*/
+BOTAN_PUBLIC_API(2,3)
+std::unique_ptr<Private_Key> load_key(DataSource& source);
+
+/**
+* Copy an existing encoded key object.
+* @param key the key to copy
+* @return new copy of the key
+*/
+BOTAN_PUBLIC_API(2,3)
+std::unique_ptr<Private_Key> copy_key(const Private_Key& key);
+
+}
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/pubkey/pubkey.cpp b/comm/third_party/botan/src/lib/pubkey/pubkey.cpp
new file mode 100644
index 0000000000..3834489ef8
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/pubkey.cpp
@@ -0,0 +1,388 @@
+/*
+* (C) 1999-2010,2015,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/pubkey.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
+#include <botan/bigint.h>
+#include <botan/pk_ops.h>
+#include <botan/internal/ct_utils.h>
+#include <botan/rng.h>
+
+namespace Botan {
+
+secure_vector<uint8_t> PK_Decryptor::decrypt(const uint8_t in[], size_t length) const
+ {
+ uint8_t valid_mask = 0;
+
+ secure_vector<uint8_t> decoded = do_decrypt(valid_mask, in, length);
+
+ if(valid_mask == 0)
+ throw Decoding_Error("Invalid public key ciphertext, cannot decrypt");
+
+ return decoded;
+ }
+
+secure_vector<uint8_t>
+PK_Decryptor::decrypt_or_random(const uint8_t in[],
+ size_t length,
+ size_t expected_pt_len,
+ RandomNumberGenerator& rng,
+ const uint8_t required_content_bytes[],
+ const uint8_t required_content_offsets[],
+ size_t required_contents_length) const
+ {
+ const secure_vector<uint8_t> fake_pms = rng.random_vec(expected_pt_len);
+
+ uint8_t decrypt_valid = 0;
+ secure_vector<uint8_t> decoded = do_decrypt(decrypt_valid, in, length);
+
+ auto valid_mask = CT::Mask<uint8_t>::is_equal(decrypt_valid, 0xFF);
+ valid_mask &= CT::Mask<uint8_t>(CT::Mask<size_t>::is_zero(decoded.size() ^ expected_pt_len));
+
+ decoded.resize(expected_pt_len);
+
+ for(size_t i = 0; i != required_contents_length; ++i)
+ {
+ /*
+ These values are chosen by the application and for TLS are constants,
+ so this early failure via assert is fine since we know 0,1 < 48
+
+ If there is a protocol that has content checks on the key where
+ the expected offsets are controllable by the attacker this could
+ still leak.
+
+ Alternately could always reduce the offset modulo the length?
+ */
+
+ const uint8_t exp = required_content_bytes[i];
+ const uint8_t off = required_content_offsets[i];
+
+ BOTAN_ASSERT(off < expected_pt_len, "Offset in range of plaintext");
+
+ auto eq = CT::Mask<uint8_t>::is_equal(decoded[off], exp);
+
+ valid_mask &= eq;
+ }
+
+ // If valid_mask is false, assign fake pre master instead
+ valid_mask.select_n(decoded.data(), decoded.data(), fake_pms.data(), expected_pt_len);
+
+ return decoded;
+ }
+
+secure_vector<uint8_t>
+PK_Decryptor::decrypt_or_random(const uint8_t in[],
+ size_t length,
+ size_t expected_pt_len,
+ RandomNumberGenerator& rng) const
+ {
+ return decrypt_or_random(in, length, expected_pt_len, rng,
+ nullptr, nullptr, 0);
+ }
+
+PK_Encryptor_EME::PK_Encryptor_EME(const Public_Key& key,
+ RandomNumberGenerator& rng,
+ const std::string& padding,
+ const std::string& provider)
+ {
+ m_op = key.create_encryption_op(rng, padding, provider);
+ if(!m_op)
+ throw Invalid_Argument("Key type " + key.algo_name() + " does not support encryption");
+ }
+
+PK_Encryptor_EME::~PK_Encryptor_EME() { /* for unique_ptr */ }
+
+size_t PK_Encryptor_EME::ciphertext_length(size_t ptext_len) const
+ {
+ return m_op->ciphertext_length(ptext_len);
+ }
+
+std::vector<uint8_t>
+PK_Encryptor_EME::enc(const uint8_t in[], size_t length, RandomNumberGenerator& rng) const
+ {
+ return unlock(m_op->encrypt(in, length, rng));
+ }
+
+size_t PK_Encryptor_EME::maximum_input_size() const
+ {
+ return m_op->max_input_bits() / 8;
+ }
+
+PK_Decryptor_EME::PK_Decryptor_EME(const Private_Key& key,
+ RandomNumberGenerator& rng,
+ const std::string& padding,
+ const std::string& provider)
+ {
+ m_op = key.create_decryption_op(rng, padding, provider);
+ if(!m_op)
+ throw Invalid_Argument("Key type " + key.algo_name() + " does not support decryption");
+ }
+
+PK_Decryptor_EME::~PK_Decryptor_EME() { /* for unique_ptr */ }
+
+size_t PK_Decryptor_EME::plaintext_length(size_t ctext_len) const
+ {
+ return m_op->plaintext_length(ctext_len);
+ }
+
+secure_vector<uint8_t> PK_Decryptor_EME::do_decrypt(uint8_t& valid_mask,
+ const uint8_t in[], size_t in_len) const
+ {
+ return m_op->decrypt(valid_mask, in, in_len);
+ }
+
+PK_KEM_Encryptor::PK_KEM_Encryptor(const Public_Key& key,
+ RandomNumberGenerator& rng,
+ const std::string& param,
+ const std::string& provider)
+ {
+ m_op = key.create_kem_encryption_op(rng, param, provider);
+ if(!m_op)
+ throw Invalid_Argument("Key type " + key.algo_name() + " does not support KEM encryption");
+ }
+
+PK_KEM_Encryptor::~PK_KEM_Encryptor() { /* for unique_ptr */ }
+
+void PK_KEM_Encryptor::encrypt(secure_vector<uint8_t>& out_encapsulated_key,
+ secure_vector<uint8_t>& out_shared_key,
+ size_t desired_shared_key_len,
+ Botan::RandomNumberGenerator& rng,
+ const uint8_t salt[],
+ size_t salt_len)
+ {
+ m_op->kem_encrypt(out_encapsulated_key,
+ out_shared_key,
+ desired_shared_key_len,
+ rng,
+ salt,
+ salt_len);
+ }
+
+PK_KEM_Decryptor::PK_KEM_Decryptor(const Private_Key& key,
+ RandomNumberGenerator& rng,
+ const std::string& param,
+ const std::string& provider)
+ {
+ m_op = key.create_kem_decryption_op(rng, param, provider);
+ if(!m_op)
+ throw Invalid_Argument("Key type " + key.algo_name() + " does not support KEM decryption");
+ }
+
+PK_KEM_Decryptor::~PK_KEM_Decryptor() { /* for unique_ptr */ }
+
+secure_vector<uint8_t> PK_KEM_Decryptor::decrypt(const uint8_t encap_key[],
+ size_t encap_key_len,
+ size_t desired_shared_key_len,
+ const uint8_t salt[],
+ size_t salt_len)
+ {
+ return m_op->kem_decrypt(encap_key, encap_key_len,
+ desired_shared_key_len,
+ salt, salt_len);
+ }
+
+PK_Key_Agreement::PK_Key_Agreement(const Private_Key& key,
+ RandomNumberGenerator& rng,
+ const std::string& kdf,
+ const std::string& provider)
+ {
+ m_op = key.create_key_agreement_op(rng, kdf, provider);
+ if(!m_op)
+ throw Invalid_Argument("Key type " + key.algo_name() + " does not support key agreement");
+ }
+
+PK_Key_Agreement::~PK_Key_Agreement() { /* for unique_ptr */ }
+
+PK_Key_Agreement& PK_Key_Agreement::operator=(PK_Key_Agreement&& other)
+ {
+ if(this != &other)
+ {
+ m_op = std::move(other.m_op);
+ }
+ return (*this);
+ }
+
+PK_Key_Agreement::PK_Key_Agreement(PK_Key_Agreement&& other) :
+ m_op(std::move(other.m_op))
+ {}
+
+size_t PK_Key_Agreement::agreed_value_size() const
+ {
+ return m_op->agreed_value_size();
+ }
+
+SymmetricKey PK_Key_Agreement::derive_key(size_t key_len,
+ const uint8_t in[], size_t in_len,
+ const uint8_t salt[],
+ size_t salt_len) const
+ {
+ return m_op->agree(key_len, in, in_len, salt, salt_len);
+ }
+
+static void check_der_format_supported(Signature_Format format, size_t parts)
+ {
+ if(format != IEEE_1363 && parts == 1)
+ throw Invalid_Argument("PK: This algorithm does not support DER encoding");
+ }
+
+PK_Signer::PK_Signer(const Private_Key& key,
+ RandomNumberGenerator& rng,
+ const std::string& emsa,
+ Signature_Format format,
+ const std::string& provider)
+ {
+ m_op = key.create_signature_op(rng, emsa, provider);
+ if(!m_op)
+ throw Invalid_Argument("Key type " + key.algo_name() + " does not support signature generation");
+ m_sig_format = format;
+ m_parts = key.message_parts();
+ m_part_size = key.message_part_size();
+ check_der_format_supported(format, m_parts);
+ }
+
+PK_Signer::~PK_Signer() { /* for unique_ptr */ }
+
+void PK_Signer::update(const uint8_t in[], size_t length)
+ {
+ m_op->update(in, length);
+ }
+
+namespace {
+
+std::vector<uint8_t> der_encode_signature(const std::vector<uint8_t>& sig,
+ size_t parts,
+ size_t part_size)
+ {
+ if(sig.size() % parts != 0 || sig.size() != parts * part_size)
+ throw Encoding_Error("Unexpected size for DER signature");
+
+ std::vector<BigInt> sig_parts(parts);
+ for(size_t i = 0; i != sig_parts.size(); ++i)
+ sig_parts[i].binary_decode(&sig[part_size*i], part_size);
+
+ std::vector<uint8_t> output;
+ DER_Encoder(output)
+ .start_cons(SEQUENCE)
+ .encode_list(sig_parts)
+ .end_cons();
+ return output;
+ }
+
+}
+
+size_t PK_Signer::signature_length() const
+ {
+ if(m_sig_format == IEEE_1363)
+ {
+ return m_op->signature_length();
+ }
+ else if(m_sig_format == DER_SEQUENCE)
+ {
+ // This is a large over-estimate but its easier than computing
+ // the exact value
+ return m_op->signature_length() + (8 + 4*m_parts);
+ }
+ else
+ throw Internal_Error("PK_Signer: Invalid signature format enum");
+ }
+
+std::vector<uint8_t> PK_Signer::signature(RandomNumberGenerator& rng)
+ {
+ const std::vector<uint8_t> sig = unlock(m_op->sign(rng));
+
+ if(m_sig_format == IEEE_1363)
+ {
+ return sig;
+ }
+ else if(m_sig_format == DER_SEQUENCE)
+ {
+ return der_encode_signature(sig, m_parts, m_part_size);
+ }
+ else
+ throw Internal_Error("PK_Signer: Invalid signature format enum");
+ }
+
+PK_Verifier::PK_Verifier(const Public_Key& key,
+ const std::string& emsa,
+ Signature_Format format,
+ const std::string& provider)
+ {
+ m_op = key.create_verification_op(emsa, provider);
+ if(!m_op)
+ throw Invalid_Argument("Key type " + key.algo_name() + " does not support signature verification");
+ m_sig_format = format;
+ m_parts = key.message_parts();
+ m_part_size = key.message_part_size();
+ check_der_format_supported(format, m_parts);
+ }
+
+PK_Verifier::~PK_Verifier() { /* for unique_ptr */ }
+
+void PK_Verifier::set_input_format(Signature_Format format)
+ {
+ check_der_format_supported(format, m_parts);
+ m_sig_format = format;
+ }
+
+bool PK_Verifier::verify_message(const uint8_t msg[], size_t msg_length,
+ const uint8_t sig[], size_t sig_length)
+ {
+ update(msg, msg_length);
+ return check_signature(sig, sig_length);
+ }
+
+void PK_Verifier::update(const uint8_t in[], size_t length)
+ {
+ m_op->update(in, length);
+ }
+
+bool PK_Verifier::check_signature(const uint8_t sig[], size_t length)
+ {
+ try {
+ if(m_sig_format == IEEE_1363)
+ {
+ return m_op->is_valid_signature(sig, length);
+ }
+ else if(m_sig_format == DER_SEQUENCE)
+ {
+ std::vector<uint8_t> real_sig;
+ BER_Decoder decoder(sig, length);
+ BER_Decoder ber_sig = decoder.start_cons(SEQUENCE);
+
+ BOTAN_ASSERT_NOMSG(m_parts != 0 && m_part_size != 0);
+
+ size_t count = 0;
+
+ while(ber_sig.more_items())
+ {
+ BigInt sig_part;
+ ber_sig.decode(sig_part);
+ real_sig += BigInt::encode_1363(sig_part, m_part_size);
+ ++count;
+ }
+
+ if(count != m_parts)
+ throw Decoding_Error("PK_Verifier: signature size invalid");
+
+ const std::vector<uint8_t> reencoded =
+ der_encode_signature(real_sig, m_parts, m_part_size);
+
+ if(reencoded.size() != length ||
+ same_mem(reencoded.data(), sig, reencoded.size()) == false)
+ {
+ throw Decoding_Error("PK_Verifier: signature is not the canonical DER encoding");
+ }
+
+ return m_op->is_valid_signature(real_sig.data(), real_sig.size());
+ }
+ else
+ throw Internal_Error("PK_Verifier: Invalid signature format enum");
+ }
+ catch(Invalid_Argument&) { return false; }
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/pubkey/pubkey.h b/comm/third_party/botan/src/lib/pubkey/pubkey.h
new file mode 100644
index 0000000000..2aa8ea9164
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/pubkey.h
@@ -0,0 +1,800 @@
+/*
+* Public Key Interface
+* (C) 1999-2010 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_PUBKEY_H_
+#define BOTAN_PUBKEY_H_
+
+#include <botan/pk_keys.h>
+#include <botan/pk_ops_fwd.h>
+#include <botan/symkey.h>
+#include <string>
+
+#if defined(BOTAN_HAS_SYSTEM_RNG)
+ #include <botan/system_rng.h>
+ #define BOTAN_PUBKEY_INCLUDE_DEPRECATED_CONSTRUCTORS
+#endif
+
+namespace Botan {
+
+class RandomNumberGenerator;
+
+/**
+* Public Key Encryptor
+* This is the primary interface for public key encryption
+*/
+class BOTAN_PUBLIC_API(2,0) PK_Encryptor
+ {
+ public:
+
+ /**
+ * Encrypt a message.
+ * @param in the message as a byte array
+ * @param length the length of the above byte array
+ * @param rng the random number source to use
+ * @return encrypted message
+ */
+ std::vector<uint8_t> encrypt(const uint8_t in[], size_t length,
+ RandomNumberGenerator& rng) const
+ {
+ return enc(in, length, rng);
+ }
+
+ /**
+ * Encrypt a message.
+ * @param in the message
+ * @param rng the random number source to use
+ * @return encrypted message
+ */
+ template<typename Alloc>
+ std::vector<uint8_t> encrypt(const std::vector<uint8_t, Alloc>& in,
+ RandomNumberGenerator& rng) const
+ {
+ return enc(in.data(), in.size(), rng);
+ }
+
+ /**
+ * Return the maximum allowed message size in bytes.
+ * @return maximum message size in bytes
+ */
+ virtual size_t maximum_input_size() const = 0;
+
+ /**
+ * Return an upper bound on the ciphertext length
+ */
+ virtual size_t ciphertext_length(size_t ctext_len) const = 0;
+
+ PK_Encryptor() = default;
+ virtual ~PK_Encryptor() = default;
+
+ PK_Encryptor(const PK_Encryptor&) = delete;
+ PK_Encryptor& operator=(const PK_Encryptor&) = delete;
+
+ private:
+ virtual std::vector<uint8_t> enc(const uint8_t[], size_t,
+ RandomNumberGenerator&) const = 0;
+ };
+
+/**
+* Public Key Decryptor
+*/
+class BOTAN_PUBLIC_API(2,0) PK_Decryptor
+ {
+ public:
+ /**
+ * Decrypt a ciphertext, throwing an exception if the input
+ * seems to be invalid (eg due to an accidental or malicious
+ * error in the ciphertext).
+ *
+ * @param in the ciphertext as a byte array
+ * @param length the length of the above byte array
+ * @return decrypted message
+ */
+ secure_vector<uint8_t> decrypt(const uint8_t in[], size_t length) const;
+
+ /**
+ * Same as above, but taking a vector
+ * @param in the ciphertext
+ * @return decrypted message
+ */
+ template<typename Alloc>
+ secure_vector<uint8_t> decrypt(const std::vector<uint8_t, Alloc>& in) const
+ {
+ return decrypt(in.data(), in.size());
+ }
+
+ /**
+ * Decrypt a ciphertext. If the ciphertext is invalid (eg due to
+ * invalid padding) or is not the expected length, instead
+ * returns a random string of the expected length. Use to avoid
+ * oracle attacks, especially against PKCS #1 v1.5 decryption.
+ */
+ secure_vector<uint8_t>
+ decrypt_or_random(const uint8_t in[],
+ size_t length,
+ size_t expected_pt_len,
+ RandomNumberGenerator& rng) const;
+
+ /**
+ * Decrypt a ciphertext. If the ciphertext is invalid (eg due to
+ * invalid padding) or is not the expected length, instead
+ * returns a random string of the expected length. Use to avoid
+ * oracle attacks, especially against PKCS #1 v1.5 decryption.
+ *
+ * Additionally checks (also in const time) that:
+ * contents[required_content_offsets[i]] == required_content_bytes[i]
+ * for 0 <= i < required_contents
+ *
+ * Used for example in TLS, which encodes the client version in
+ * the content bytes: if there is any timing variation the version
+ * check can be used as an oracle to recover the key.
+ */
+ secure_vector<uint8_t>
+ decrypt_or_random(const uint8_t in[],
+ size_t length,
+ size_t expected_pt_len,
+ RandomNumberGenerator& rng,
+ const uint8_t required_content_bytes[],
+ const uint8_t required_content_offsets[],
+ size_t required_contents) const;
+
+ /**
+ * Return an upper bound on the plaintext length for a particular
+ * ciphertext input length
+ */
+ virtual size_t plaintext_length(size_t ctext_len) const = 0;
+
+ PK_Decryptor() = default;
+ virtual ~PK_Decryptor() = default;
+
+ PK_Decryptor(const PK_Decryptor&) = delete;
+ PK_Decryptor& operator=(const PK_Decryptor&) = delete;
+
+ private:
+ virtual secure_vector<uint8_t> do_decrypt(uint8_t& valid_mask,
+ const uint8_t in[], size_t in_len) const = 0;
+ };
+
+/**
+* Public Key Signer. Use the sign_message() functions for small
+* messages. Use multiple calls update() to process large messages and
+* generate the signature by finally calling signature().
+*/
+class BOTAN_PUBLIC_API(2,0) PK_Signer final
+ {
+ public:
+
+ /**
+ * Construct a PK Signer.
+ * @param key the key to use inside this signer
+ * @param rng the random generator to use
+ * @param emsa the EMSA to use
+ * An example would be "EMSA1(SHA-224)".
+ * @param format the signature format to use
+ * @param provider the provider to use
+ */
+ PK_Signer(const Private_Key& key,
+ RandomNumberGenerator& rng,
+ const std::string& emsa,
+ Signature_Format format = IEEE_1363,
+ const std::string& provider = "");
+
+#if defined(BOTAN_PUBKEY_INCLUDE_DEPRECATED_CONSTRUCTORS)
+ /**
+ * Construct a PK Signer.
+ * @param key the key to use inside this signer
+ * @param emsa the EMSA to use
+ * An example would be "EMSA1(SHA-224)".
+ * @param format the signature format to use
+ */
+ BOTAN_DEPRECATED("Use constructor taking a RNG object")
+ PK_Signer(const Private_Key& key,
+ const std::string& emsa,
+ Signature_Format format = IEEE_1363,
+ const std::string& provider = "") :
+ PK_Signer(key, system_rng(), emsa, format, provider)
+ {}
+#endif
+
+ ~PK_Signer();
+
+ PK_Signer(const PK_Signer&) = delete;
+ PK_Signer& operator=(const PK_Signer&) = delete;
+
+ /**
+ * Sign a message all in one go
+ * @param in the message to sign as a byte array
+ * @param length the length of the above byte array
+ * @param rng the rng to use
+ * @return signature
+ */
+ std::vector<uint8_t> sign_message(const uint8_t in[], size_t length,
+ RandomNumberGenerator& rng)
+ {
+ this->update(in, length);
+ return this->signature(rng);
+ }
+
+ /**
+ * Sign a message.
+ * @param in the message to sign
+ * @param rng the rng to use
+ * @return signature
+ */
+ template<typename Alloc>
+ std::vector<uint8_t> sign_message(const std::vector<uint8_t, Alloc>& in,
+ RandomNumberGenerator& rng)
+ {
+ return sign_message(in.data(), in.size(), rng);
+ }
+
+ /**
+ * Add a message part (single byte).
+ * @param in the byte to add
+ */
+ void update(uint8_t in) { update(&in, 1); }
+
+ /**
+ * Add a message part.
+ * @param in the message part to add as a byte array
+ * @param length the length of the above byte array
+ */
+ void update(const uint8_t in[], size_t length);
+
+ /**
+ * Add a message part.
+ * @param in the message part to add
+ */
+ template<typename Alloc>
+ void update(const std::vector<uint8_t, Alloc>& in)
+ {
+ update(in.data(), in.size());
+ }
+
+ /**
+ * Add a message part.
+ * @param in the message part to add
+ */
+ void update(const std::string& in)
+ {
+ update(cast_char_ptr_to_uint8(in.data()), in.size());
+ }
+
+ /**
+ * Get the signature of the so far processed message (provided by the
+ * calls to update()).
+ * @param rng the rng to use
+ * @return signature of the total message
+ */
+ std::vector<uint8_t> signature(RandomNumberGenerator& rng);
+
+
+ /**
+ * Set the output format of the signature.
+ * @param format the signature format to use
+ */
+ void set_output_format(Signature_Format format) { m_sig_format = format; }
+
+ /**
+ * Return an upper bound on the length of the signatures this
+ * PK_Signer will produce
+ */
+ size_t signature_length() const;
+
+ private:
+ std::unique_ptr<PK_Ops::Signature> m_op;
+ Signature_Format m_sig_format;
+ size_t m_parts, m_part_size;
+ };
+
+/**
+* Public Key Verifier. Use the verify_message() functions for small
+* messages. Use multiple calls update() to process large messages and
+* verify the signature by finally calling check_signature().
+*/
+class BOTAN_PUBLIC_API(2,0) PK_Verifier final
+ {
+ public:
+ /**
+ * Construct a PK Verifier.
+ * @param pub_key the public key to verify against
+ * @param emsa the EMSA to use (eg "EMSA3(SHA-1)")
+ * @param format the signature format to use
+ * @param provider the provider to use
+ */
+ PK_Verifier(const Public_Key& pub_key,
+ const std::string& emsa,
+ Signature_Format format = IEEE_1363,
+ const std::string& provider = "");
+
+ ~PK_Verifier();
+
+ PK_Verifier& operator=(const PK_Verifier&) = delete;
+ PK_Verifier(const PK_Verifier&) = delete;
+
+ /**
+ * Verify a signature.
+ * @param msg the message that the signature belongs to, as a byte array
+ * @param msg_length the length of the above byte array msg
+ * @param sig the signature as a byte array
+ * @param sig_length the length of the above byte array sig
+ * @return true if the signature is valid
+ */
+ bool verify_message(const uint8_t msg[], size_t msg_length,
+ const uint8_t sig[], size_t sig_length);
+ /**
+ * Verify a signature.
+ * @param msg the message that the signature belongs to
+ * @param sig the signature
+ * @return true if the signature is valid
+ */
+ template<typename Alloc, typename Alloc2>
+ bool verify_message(const std::vector<uint8_t, Alloc>& msg,
+ const std::vector<uint8_t, Alloc2>& sig)
+ {
+ return verify_message(msg.data(), msg.size(),
+ sig.data(), sig.size());
+ }
+
+ /**
+ * Add a message part (single byte) of the message corresponding to the
+ * signature to be verified.
+ * @param in the byte to add
+ */
+ void update(uint8_t in) { update(&in, 1); }
+
+ /**
+ * Add a message part of the message corresponding to the
+ * signature to be verified.
+ * @param msg_part the new message part as a byte array
+ * @param length the length of the above byte array
+ */
+ void update(const uint8_t msg_part[], size_t length);
+
+ /**
+ * Add a message part of the message corresponding to the
+ * signature to be verified.
+ * @param in the new message part
+ */
+ template<typename Alloc>
+ void update(const std::vector<uint8_t, Alloc>& in)
+ {
+ update(in.data(), in.size());
+ }
+
+ /**
+ * Add a message part of the message corresponding to the
+ * signature to be verified.
+ */
+ void update(const std::string& in)
+ {
+ update(cast_char_ptr_to_uint8(in.data()), in.size());
+ }
+
+ /**
+ * Check the signature of the buffered message, i.e. the one build
+ * by successive calls to update.
+ * @param sig the signature to be verified as a byte array
+ * @param length the length of the above byte array
+ * @return true if the signature is valid, false otherwise
+ */
+ bool check_signature(const uint8_t sig[], size_t length);
+
+ /**
+ * Check the signature of the buffered message, i.e. the one build
+ * by successive calls to update.
+ * @param sig the signature to be verified
+ * @return true if the signature is valid, false otherwise
+ */
+ template<typename Alloc>
+ bool check_signature(const std::vector<uint8_t, Alloc>& sig)
+ {
+ return check_signature(sig.data(), sig.size());
+ }
+
+ /**
+ * Set the format of the signatures fed to this verifier.
+ * @param format the signature format to use
+ */
+ void set_input_format(Signature_Format format);
+
+ private:
+ std::unique_ptr<PK_Ops::Verification> m_op;
+ Signature_Format m_sig_format;
+ size_t m_parts, m_part_size;
+ };
+
+/**
+* Object used for key agreement
+*/
+class BOTAN_PUBLIC_API(2,0) PK_Key_Agreement final
+ {
+ public:
+
+ /**
+ * Construct a PK Key Agreement.
+ * @param key the key to use
+ * @param rng the random generator to use
+ * @param kdf name of the KDF to use (or 'Raw' for no KDF)
+ * @param provider the algo provider to use (or empty for default)
+ */
+ PK_Key_Agreement(const Private_Key& key,
+ RandomNumberGenerator& rng,
+ const std::string& kdf,
+ const std::string& provider = "");
+
+#if defined(BOTAN_PUBKEY_INCLUDE_DEPRECATED_CONSTRUCTORS)
+ /**
+ * Construct a PK Key Agreement.
+ * @param key the key to use
+ * @param kdf name of the KDF to use (or 'Raw' for no KDF)
+ * @param provider the algo provider to use (or empty for default)
+ */
+ BOTAN_DEPRECATED("Use constructor taking a RNG object")
+ PK_Key_Agreement(const Private_Key& key,
+ const std::string& kdf,
+ const std::string& provider = "") :
+ PK_Key_Agreement(key, system_rng(), kdf, provider)
+ {}
+#endif
+
+ ~PK_Key_Agreement();
+
+ // For ECIES
+ PK_Key_Agreement& operator=(PK_Key_Agreement&&);
+ PK_Key_Agreement(PK_Key_Agreement&&);
+
+ PK_Key_Agreement& operator=(const PK_Key_Agreement&) = delete;
+ PK_Key_Agreement(const PK_Key_Agreement&) = delete;
+
+ /**
+ * Perform Key Agreement Operation
+ * @param key_len the desired key output size
+ * @param in the other parties key
+ * @param in_len the length of in in bytes
+ * @param params extra derivation params
+ * @param params_len the length of params in bytes
+ */
+ SymmetricKey derive_key(size_t key_len,
+ const uint8_t in[],
+ size_t in_len,
+ const uint8_t params[],
+ size_t params_len) const;
+
+ /**
+ * Perform Key Agreement Operation
+ * @param key_len the desired key output size
+ * @param in the other parties key
+ * @param params extra derivation params
+ * @param params_len the length of params in bytes
+ */
+ SymmetricKey derive_key(size_t key_len,
+ const std::vector<uint8_t>& in,
+ const uint8_t params[],
+ size_t params_len) const
+ {
+ return derive_key(key_len, in.data(), in.size(),
+ params, params_len);
+ }
+
+ /**
+ * Perform Key Agreement Operation
+ * @param key_len the desired key output size
+ * @param in the other parties key
+ * @param in_len the length of in in bytes
+ * @param params extra derivation params
+ */
+ SymmetricKey derive_key(size_t key_len,
+ const uint8_t in[], size_t in_len,
+ const std::string& params = "") const
+ {
+ return derive_key(key_len, in, in_len,
+ cast_char_ptr_to_uint8(params.data()),
+ params.length());
+ }
+
+ /**
+ * Perform Key Agreement Operation
+ * @param key_len the desired key output size
+ * @param in the other parties key
+ * @param params extra derivation params
+ */
+ SymmetricKey derive_key(size_t key_len,
+ const std::vector<uint8_t>& in,
+ const std::string& params = "") const
+ {
+ return derive_key(key_len, in.data(), in.size(),
+ cast_char_ptr_to_uint8(params.data()),
+ params.length());
+ }
+
+ /**
+ * Return the underlying size of the value that is agreed.
+ * If derive_key is called with a length of 0 with a "Raw"
+ * KDF, it will return a value of this size.
+ */
+ size_t agreed_value_size() const;
+
+ private:
+ std::unique_ptr<PK_Ops::Key_Agreement> m_op;
+ };
+
+/**
+* Encryption using a standard message recovery algorithm like RSA or
+* ElGamal, paired with an encoding scheme like OAEP.
+*/
+class BOTAN_PUBLIC_API(2,0) PK_Encryptor_EME final : public PK_Encryptor
+ {
+ public:
+ size_t maximum_input_size() const override;
+
+ /**
+ * Construct an instance.
+ * @param key the key to use inside the encryptor
+ * @param rng the RNG to use
+ * @param padding the message encoding scheme to use (eg "OAEP(SHA-256)")
+ * @param provider the provider to use
+ */
+ PK_Encryptor_EME(const Public_Key& key,
+ RandomNumberGenerator& rng,
+ const std::string& padding,
+ const std::string& provider = "");
+
+#if defined(BOTAN_PUBKEY_INCLUDE_DEPRECATED_CONSTRUCTORS)
+ /**
+ * Construct an instance.
+ * @param key the key to use inside the encryptor
+ * @param padding the message encoding scheme to use (eg "OAEP(SHA-256)")
+ */
+ BOTAN_DEPRECATED("Use constructor taking a RNG object")
+ PK_Encryptor_EME(const Public_Key& key,
+ const std::string& padding,
+ const std::string& provider = "") :
+ PK_Encryptor_EME(key, system_rng(), padding, provider) {}
+#endif
+
+ ~PK_Encryptor_EME();
+
+ PK_Encryptor_EME& operator=(const PK_Encryptor_EME&) = delete;
+ PK_Encryptor_EME(const PK_Encryptor_EME&) = delete;
+
+ /**
+ * Return an upper bound on the ciphertext length for a particular
+ * plaintext input length
+ */
+ size_t ciphertext_length(size_t ptext_len) const override;
+ private:
+ std::vector<uint8_t> enc(const uint8_t[], size_t,
+ RandomNumberGenerator& rng) const override;
+
+ std::unique_ptr<PK_Ops::Encryption> m_op;
+ };
+
+/**
+* Decryption with an MR algorithm and an EME.
+*/
+class BOTAN_PUBLIC_API(2,0) PK_Decryptor_EME final : public PK_Decryptor
+ {
+ public:
+ /**
+ * 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 provider the provider to use
+ */
+ PK_Decryptor_EME(const Private_Key& key,
+ RandomNumberGenerator& rng,
+ const std::string& eme,
+ const std::string& provider = "");
+
+
+#if defined(BOTAN_PUBKEY_INCLUDE_DEPRECATED_CONSTRUCTORS)
+ /**
+ * Construct an instance.
+ * @param key the key to use inside the decryptor
+ * @param eme the message encoding scheme to use (eg "OAEP(SHA-256)")
+ */
+ BOTAN_DEPRECATED("Use constructor taking a RNG object")
+ PK_Decryptor_EME(const Private_Key& key,
+ const std::string& eme,
+ const std::string& provider = "") :
+ PK_Decryptor_EME(key, system_rng(), eme, provider) {}
+#endif
+
+ size_t plaintext_length(size_t ptext_len) const override;
+
+ ~PK_Decryptor_EME();
+ PK_Decryptor_EME& operator=(const PK_Decryptor_EME&) = delete;
+ PK_Decryptor_EME(const PK_Decryptor_EME&) = delete;
+ private:
+ secure_vector<uint8_t> do_decrypt(uint8_t& valid_mask,
+ const uint8_t in[],
+ size_t in_len) const override;
+
+ std::unique_ptr<PK_Ops::Decryption> m_op;
+ };
+
+/**
+* Public Key Key Encapsulation Mechanism Encryption.
+*/
+class BOTAN_PUBLIC_API(2,0) PK_KEM_Encryptor final
+ {
+ public:
+ /**
+ * Construct an instance.
+ * @param key the key to use inside the encryptor
+ * @param rng the RNG to use
+ * @param kem_param additional KEM parameters
+ * @param provider the provider to use
+ */
+ PK_KEM_Encryptor(const Public_Key& key,
+ RandomNumberGenerator& rng,
+ const std::string& kem_param = "",
+ const std::string& provider = "");
+
+#if defined(BOTAN_PUBKEY_INCLUDE_DEPRECATED_CONSTRUCTORS)
+ BOTAN_DEPRECATED("Use constructor taking a RNG object")
+ PK_KEM_Encryptor(const Public_Key& key,
+ const std::string& kem_param = "",
+ const std::string& provider = "") :
+ PK_KEM_Encryptor(key, system_rng(), kem_param, provider) {}
+#endif
+
+ ~PK_KEM_Encryptor();
+
+ PK_KEM_Encryptor& operator=(const PK_KEM_Encryptor&) = delete;
+ PK_KEM_Encryptor(const PK_KEM_Encryptor&) = delete;
+
+ /**
+ * Generate a shared key for data encryption.
+ * @param out_encapsulated_key the generated encapsulated key
+ * @param out_shared_key the generated shared key
+ * @param desired_shared_key_len desired size of the shared key in bytes
+ * @param rng the RNG to use
+ * @param salt a salt value used in the KDF
+ * @param salt_len size of the salt value in bytes
+ */
+ void encrypt(secure_vector<uint8_t>& out_encapsulated_key,
+ secure_vector<uint8_t>& out_shared_key,
+ size_t desired_shared_key_len,
+ Botan::RandomNumberGenerator& rng,
+ const uint8_t salt[],
+ size_t salt_len);
+
+ /**
+ * Generate a shared key for data encryption.
+ * @param out_encapsulated_key the generated encapsulated key
+ * @param out_shared_key the generated shared key
+ * @param desired_shared_key_len desired size of the shared key in bytes
+ * @param rng the RNG to use
+ * @param salt a salt value used in the KDF
+ */
+ template<typename Alloc>
+ void encrypt(secure_vector<uint8_t>& out_encapsulated_key,
+ secure_vector<uint8_t>& out_shared_key,
+ size_t desired_shared_key_len,
+ Botan::RandomNumberGenerator& rng,
+ const std::vector<uint8_t, Alloc>& salt)
+ {
+ this->encrypt(out_encapsulated_key,
+ out_shared_key,
+ desired_shared_key_len,
+ rng,
+ salt.data(), salt.size());
+ }
+
+
+ /**
+ * Generate a shared key for data encryption.
+ * @param out_encapsulated_key the generated encapsulated key
+ * @param out_shared_key the generated shared key
+ * @param desired_shared_key_len desired size of the shared key in bytes
+ * @param rng the RNG to use
+ */
+ void encrypt(secure_vector<uint8_t>& out_encapsulated_key,
+ secure_vector<uint8_t>& out_shared_key,
+ size_t desired_shared_key_len,
+ Botan::RandomNumberGenerator& rng)
+ {
+ this->encrypt(out_encapsulated_key,
+ out_shared_key,
+ desired_shared_key_len,
+ rng,
+ nullptr,
+ 0);
+ }
+
+ private:
+ std::unique_ptr<PK_Ops::KEM_Encryption> m_op;
+ };
+
+/**
+* Public Key Key Encapsulation Mechanism Decryption.
+*/
+class BOTAN_PUBLIC_API(2,0) PK_KEM_Decryptor final
+ {
+ public:
+ /**
+ * Construct an instance.
+ * @param key the key to use inside the decryptor
+ * @param rng the RNG to use
+ * @param kem_param additional KEM parameters
+ * @param provider the provider to use
+ */
+ PK_KEM_Decryptor(const Private_Key& key,
+ RandomNumberGenerator& rng,
+ const std::string& kem_param = "",
+ const std::string& provider = "");
+
+#if defined(BOTAN_PUBKEY_INCLUDE_DEPRECATED_CONSTRUCTORS)
+ BOTAN_DEPRECATED("Use constructor taking a RNG object")
+ PK_KEM_Decryptor(const Private_Key& key,
+ const std::string& kem_param = "",
+ const std::string& provider = "") :
+ PK_KEM_Decryptor(key, system_rng(), kem_param, provider)
+ {}
+#endif
+
+ ~PK_KEM_Decryptor();
+ PK_KEM_Decryptor& operator=(const PK_KEM_Decryptor&) = delete;
+ PK_KEM_Decryptor(const PK_KEM_Decryptor&) = delete;
+
+ /**
+ * Decrypts the shared key for data encryption.
+ * @param encap_key the encapsulated key
+ * @param encap_key_len size of the encapsulated key in bytes
+ * @param desired_shared_key_len desired size of the shared key in bytes
+ * @param salt a salt value used in the KDF
+ * @param salt_len size of the salt value in bytes
+ * @return the shared data encryption key
+ */
+ secure_vector<uint8_t> decrypt(const uint8_t encap_key[],
+ size_t encap_key_len,
+ size_t desired_shared_key_len,
+ const uint8_t salt[],
+ size_t salt_len);
+
+ /**
+ * Decrypts the shared key for data encryption.
+ * @param encap_key the encapsulated key
+ * @param encap_key_len size of the encapsulated key in bytes
+ * @param desired_shared_key_len desired size of the shared key in bytes
+ * @return the shared data encryption key
+ */
+ secure_vector<uint8_t> decrypt(const uint8_t encap_key[],
+ size_t encap_key_len,
+ size_t desired_shared_key_len)
+ {
+ return this->decrypt(encap_key, encap_key_len,
+ desired_shared_key_len,
+ nullptr, 0);
+ }
+
+ /**
+ * Decrypts the shared key for data encryption.
+ * @param encap_key the encapsulated key
+ * @param desired_shared_key_len desired size of the shared key in bytes
+ * @param salt a salt value used in the KDF
+ * @return the shared data encryption key
+ */
+ template<typename Alloc1, typename Alloc2>
+ secure_vector<uint8_t> decrypt(const std::vector<uint8_t, Alloc1>& encap_key,
+ size_t desired_shared_key_len,
+ const std::vector<uint8_t, Alloc2>& salt)
+ {
+ return this->decrypt(encap_key.data(), encap_key.size(),
+ desired_shared_key_len,
+ salt.data(), salt.size());
+ }
+
+ private:
+ std::unique_ptr<PK_Ops::KEM_Decryption> m_op;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/pubkey/rfc6979/info.txt b/comm/third_party/botan/src/lib/pubkey/rfc6979/info.txt
new file mode 100644
index 0000000000..72a61301c5
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/rfc6979/info.txt
@@ -0,0 +1,8 @@
+<defines>
+RFC6979_GENERATOR -> 20140321
+</defines>
+
+<requires>
+bigint
+hmac_drbg
+</requires>
diff --git a/comm/third_party/botan/src/lib/pubkey/rfc6979/rfc6979.cpp b/comm/third_party/botan/src/lib/pubkey/rfc6979/rfc6979.cpp
new file mode 100644
index 0000000000..94b313c3a2
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/rfc6979/rfc6979.cpp
@@ -0,0 +1,59 @@
+/*
+* RFC 6979 Deterministic Nonce Generator
+* (C) 2014,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/rfc6979.h>
+#include <botan/hmac_drbg.h>
+#include <botan/mac.h>
+
+namespace Botan {
+
+RFC6979_Nonce_Generator::RFC6979_Nonce_Generator(const std::string& hash,
+ const BigInt& order,
+ const BigInt& x) :
+ m_order(order),
+ m_qlen(m_order.bits()),
+ m_rlen(m_qlen / 8 + (m_qlen % 8 ? 1 : 0)),
+ m_rng_in(m_rlen * 2),
+ m_rng_out(m_rlen)
+ {
+ m_hmac_drbg.reset(new HMAC_DRBG(MessageAuthenticationCode::create("HMAC(" + hash + ")")));
+ BigInt::encode_1363(m_rng_in.data(), m_rlen, x);
+ }
+
+RFC6979_Nonce_Generator::~RFC6979_Nonce_Generator()
+ {
+ // for ~unique_ptr
+ }
+
+const BigInt& RFC6979_Nonce_Generator::nonce_for(const BigInt& m)
+ {
+ BigInt::encode_1363(&m_rng_in[m_rlen], m_rlen, m);
+ m_hmac_drbg->clear();
+ m_hmac_drbg->initialize_with(m_rng_in.data(), m_rng_in.size());
+
+ do
+ {
+ m_hmac_drbg->randomize(m_rng_out.data(), m_rng_out.size());
+ m_k.binary_decode(m_rng_out.data(), m_rng_out.size());
+ m_k >>= (8*m_rlen - m_qlen);
+ }
+ while(m_k == 0 || m_k >= m_order);
+
+ return m_k;
+ }
+
+BigInt generate_rfc6979_nonce(const BigInt& x,
+ const BigInt& q,
+ const BigInt& h,
+ const std::string& hash)
+ {
+ RFC6979_Nonce_Generator gen(hash, q, x);
+ BigInt k = gen.nonce_for(h);
+ return k;
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/pubkey/rfc6979/rfc6979.h b/comm/third_party/botan/src/lib/pubkey/rfc6979/rfc6979.h
new file mode 100644
index 0000000000..54134a682f
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/rfc6979/rfc6979.h
@@ -0,0 +1,55 @@
+/*
+* RFC 6979 Deterministic Nonce Generator
+* (C) 2014,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_RFC6979_GENERATOR_H_
+#define BOTAN_RFC6979_GENERATOR_H_
+
+#include <botan/bigint.h>
+#include <string>
+#include <memory>
+
+BOTAN_FUTURE_INTERNAL_HEADER(rfc6979.h)
+
+namespace Botan {
+
+class HMAC_DRBG;
+
+class BOTAN_PUBLIC_API(2,0) RFC6979_Nonce_Generator final
+ {
+ public:
+ /**
+ * Note: keeps persistent reference to order
+ */
+ RFC6979_Nonce_Generator(const std::string& hash,
+ const BigInt& order,
+ const BigInt& x);
+
+ ~RFC6979_Nonce_Generator();
+
+ const BigInt& nonce_for(const BigInt& m);
+ private:
+ const BigInt& m_order;
+ BigInt m_k;
+ size_t m_qlen, m_rlen;
+ std::unique_ptr<HMAC_DRBG> m_hmac_drbg;
+ secure_vector<uint8_t> m_rng_in, m_rng_out;
+ };
+
+/**
+* @param x the secret (EC)DSA key
+* @param q the group order
+* @param h the message hash already reduced mod q
+* @param hash the hash function used to generate h
+*/
+BigInt BOTAN_PUBLIC_API(2,0) generate_rfc6979_nonce(const BigInt& x,
+ const BigInt& q,
+ const BigInt& h,
+ const std::string& hash);
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/pubkey/rsa/info.txt b/comm/third_party/botan/src/lib/pubkey/rsa/info.txt
new file mode 100644
index 0000000000..9fc9354b83
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/rsa/info.txt
@@ -0,0 +1,10 @@
+<defines>
+RSA -> 20160730
+</defines>
+
+<requires>
+keypair
+numbertheory
+emsa_pssr
+sha2_32
+</requires>
diff --git a/comm/third_party/botan/src/lib/pubkey/rsa/rsa.cpp b/comm/third_party/botan/src/lib/pubkey/rsa/rsa.cpp
new file mode 100644
index 0000000000..96f405892a
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/rsa/rsa.cpp
@@ -0,0 +1,753 @@
+/*
+* RSA
+* (C) 1999-2010,2015,2016,2018,2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/rsa.h>
+#include <botan/internal/pk_ops_impl.h>
+#include <botan/keypair.h>
+#include <botan/blinding.h>
+#include <botan/reducer.h>
+#include <botan/workfactor.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
+#include <botan/monty.h>
+#include <botan/divide.h>
+#include <botan/internal/monty_exp.h>
+
+#if defined(BOTAN_HAS_OPENSSL)
+ #include <botan/internal/openssl.h>
+#endif
+
+#if defined(BOTAN_HAS_THREAD_UTILS)
+ #include <botan/internal/thread_pool.h>
+#endif
+
+namespace Botan {
+
+class RSA_Public_Data final
+ {
+ public:
+ RSA_Public_Data(BigInt&& n, BigInt&& e) :
+ m_n(n),
+ m_e(e),
+ m_monty_n(std::make_shared<Montgomery_Params>(m_n)),
+ m_public_modulus_bits(m_n.bits()),
+ m_public_modulus_bytes(m_n.bytes())
+ {}
+
+ BigInt public_op(const BigInt& m) const
+ {
+ const size_t powm_window = 1;
+ auto powm_m_n = monty_precompute(m_monty_n, m, powm_window, false);
+ return monty_execute_vartime(*powm_m_n, m_e);
+ }
+
+ const BigInt& get_n() const { return m_n; }
+ const BigInt& get_e() const { return m_e; }
+ size_t public_modulus_bits() const { return m_public_modulus_bits; }
+ size_t public_modulus_bytes() const { return m_public_modulus_bytes; }
+
+ private:
+ BigInt m_n;
+ BigInt m_e;
+ std::shared_ptr<const Montgomery_Params> m_monty_n;
+ size_t m_public_modulus_bits;
+ size_t m_public_modulus_bytes;
+ };
+
+class RSA_Private_Data final
+ {
+ public:
+ RSA_Private_Data(BigInt&& d, BigInt&& p, BigInt&& q,
+ BigInt&& d1, BigInt&& d2, BigInt&& c) :
+ m_d(d),
+ m_p(p),
+ m_q(q),
+ m_d1(d1),
+ m_d2(d2),
+ m_c(c),
+ m_mod_p(m_p),
+ m_mod_q(m_q),
+ m_monty_p(std::make_shared<Montgomery_Params>(m_p, m_mod_p)),
+ m_monty_q(std::make_shared<Montgomery_Params>(m_q, m_mod_q)),
+ m_p_bits(m_p.bits()),
+ m_q_bits(m_q.bits())
+ {}
+
+ const BigInt& get_d() const { return m_d; }
+ const BigInt& get_p() const { return m_p; }
+ const BigInt& get_q() const { return m_q; }
+ const BigInt& get_d1() const { return m_d1; }
+ const BigInt& get_d2() const { return m_d2; }
+ const BigInt& get_c() const { return m_c; }
+
+ //private:
+ BigInt m_d;
+ BigInt m_p;
+ BigInt m_q;
+ BigInt m_d1;
+ BigInt m_d2;
+ BigInt m_c;
+
+ Modular_Reducer m_mod_p;
+ Modular_Reducer m_mod_q;
+ std::shared_ptr<const Montgomery_Params> m_monty_p;
+ std::shared_ptr<const Montgomery_Params> m_monty_q;
+ size_t m_p_bits;
+ size_t m_q_bits;
+ };
+
+std::shared_ptr<const RSA_Public_Data> RSA_PublicKey::public_data() const
+ {
+ return m_public;
+ }
+
+const BigInt& RSA_PublicKey::get_n() const { return m_public->get_n(); }
+const BigInt& RSA_PublicKey::get_e() const { return m_public->get_e(); }
+
+void RSA_PublicKey::init(BigInt&& n, BigInt&& e)
+ {
+ if(n.is_negative() || n.is_even() || e.is_negative() || e.is_even())
+ throw Decoding_Error("Invalid RSA public key parameters");
+ m_public = std::make_shared<RSA_Public_Data>(std::move(n), std::move(e));
+ }
+
+RSA_PublicKey::RSA_PublicKey(const AlgorithmIdentifier&,
+ const std::vector<uint8_t>& key_bits)
+ {
+ BigInt n, e;
+ BER_Decoder(key_bits)
+ .start_cons(SEQUENCE)
+ .decode(n)
+ .decode(e)
+ .end_cons();
+
+ init(std::move(n), std::move(e));
+ }
+
+RSA_PublicKey::RSA_PublicKey(const BigInt& modulus, const BigInt& exponent)
+ {
+ BigInt n = modulus;
+ BigInt e = exponent;
+ init(std::move(n), std::move(e));
+ }
+
+size_t RSA_PublicKey::key_length() const
+ {
+ return m_public->public_modulus_bits();
+ }
+
+size_t RSA_PublicKey::estimated_strength() const
+ {
+ return if_work_factor(key_length());
+ }
+
+AlgorithmIdentifier RSA_PublicKey::algorithm_identifier() const
+ {
+ return AlgorithmIdentifier(get_oid(), AlgorithmIdentifier::USE_NULL_PARAM);
+ }
+
+std::vector<uint8_t> RSA_PublicKey::public_key_bits() const
+ {
+ std::vector<uint8_t> output;
+ DER_Encoder der(output);
+ der.start_cons(SEQUENCE)
+ .encode(get_n())
+ .encode(get_e())
+ .end_cons();
+
+ return output;
+ }
+
+/*
+* Check RSA Public Parameters
+*/
+bool RSA_PublicKey::check_key(RandomNumberGenerator&, bool) const
+ {
+ if(get_n() < 35 || get_n().is_even() || get_e() < 3 || get_e().is_even())
+ return false;
+ return true;
+ }
+
+std::shared_ptr<const RSA_Private_Data> RSA_PrivateKey::private_data() const
+ {
+ return m_private;
+ }
+
+secure_vector<uint8_t> RSA_PrivateKey::private_key_bits() const
+ {
+ return DER_Encoder()
+ .start_cons(SEQUENCE)
+ .encode(static_cast<size_t>(0))
+ .encode(get_n())
+ .encode(get_e())
+ .encode(get_d())
+ .encode(get_p())
+ .encode(get_q())
+ .encode(get_d1())
+ .encode(get_d2())
+ .encode(get_c())
+ .end_cons()
+ .get_contents();
+ }
+
+const BigInt& RSA_PrivateKey::get_p() const { return m_private->get_p(); }
+const BigInt& RSA_PrivateKey::get_q() const { return m_private->get_q(); }
+const BigInt& RSA_PrivateKey::get_d() const { return m_private->get_d(); }
+const BigInt& RSA_PrivateKey::get_c() const { return m_private->get_c(); }
+const BigInt& RSA_PrivateKey::get_d1() const { return m_private->get_d1(); }
+const BigInt& RSA_PrivateKey::get_d2() const { return m_private->get_d2(); }
+
+void RSA_PrivateKey::init(BigInt&& d, BigInt&& p, BigInt&& q,
+ BigInt&& d1, BigInt&& d2, BigInt&& c)
+ {
+ m_private = std::make_shared<RSA_Private_Data>(
+ std::move(d), std::move(p), std::move(q), std::move(d1), std::move(d2), std::move(c));
+ }
+
+RSA_PrivateKey::RSA_PrivateKey(const AlgorithmIdentifier&,
+ const secure_vector<uint8_t>& key_bits)
+ {
+ BigInt n, e, d, p, q, d1, d2, c;
+
+ BER_Decoder(key_bits)
+ .start_cons(SEQUENCE)
+ .decode_and_check<size_t>(0, "Unknown PKCS #1 key format version")
+ .decode(n)
+ .decode(e)
+ .decode(d)
+ .decode(p)
+ .decode(q)
+ .decode(d1)
+ .decode(d2)
+ .decode(c)
+ .end_cons();
+
+ RSA_PublicKey::init(std::move(n), std::move(e));
+
+ RSA_PrivateKey::init(std::move(d), std::move(p), std::move(q),
+ std::move(d1), std::move(d2), std::move(c));
+ }
+
+RSA_PrivateKey::RSA_PrivateKey(const BigInt& prime1,
+ const BigInt& prime2,
+ const BigInt& exp,
+ const BigInt& d_exp,
+ const BigInt& mod)
+ {
+ BigInt p = prime1;
+ BigInt q = prime2;
+ BigInt n = mod;
+ if(n.is_zero())
+ n = p * q;
+
+ BigInt e = exp;
+
+ BigInt d = d_exp;
+
+ const BigInt p_minus_1 = p - 1;
+ const BigInt q_minus_1 = q - 1;
+
+ if(d.is_zero())
+ {
+ const BigInt phi_n = lcm(p_minus_1, q_minus_1);
+ d = inverse_mod(e, phi_n);
+ }
+
+ BigInt d1 = ct_modulo(d, p_minus_1);
+ BigInt d2 = ct_modulo(d, q_minus_1);
+ BigInt c = inverse_mod(q, p);
+
+ RSA_PublicKey::init(std::move(n), std::move(e));
+
+ RSA_PrivateKey::init(std::move(d), std::move(p), std::move(q),
+ std::move(d1), std::move(d2), std::move(c));
+ }
+
+/*
+* Create a RSA private key
+*/
+RSA_PrivateKey::RSA_PrivateKey(RandomNumberGenerator& rng,
+ size_t bits, size_t exp)
+ {
+ if(bits < 1024)
+ throw Invalid_Argument(algo_name() + ": Can't make a key that is only " +
+ std::to_string(bits) + " bits long");
+ if(exp < 3 || exp % 2 == 0)
+ throw Invalid_Argument(algo_name() + ": Invalid encryption exponent");
+
+ BigInt n, e, d, p, q, d1, d2, c;
+
+ e = exp;
+
+ const size_t p_bits = (bits + 1) / 2;
+ const size_t q_bits = bits - p_bits;
+
+ do
+ {
+ // TODO could generate primes in thread pool
+ p = generate_rsa_prime(rng, rng, p_bits, e);
+ q = generate_rsa_prime(rng, rng, q_bits, e);
+
+ if(p == q)
+ throw Internal_Error("RNG failure during RSA key generation");
+
+ n = p * q;
+ } while(n.bits() != bits);
+
+ const BigInt p_minus_1 = p - 1;
+ const BigInt q_minus_1 = q - 1;
+
+ const BigInt phi_n = lcm(p_minus_1, q_minus_1);
+ d = inverse_mod(e, phi_n);
+ d1 = ct_modulo(d, p_minus_1);
+ d2 = ct_modulo(d, q_minus_1);
+ c = inverse_mod(q, p);
+
+ RSA_PublicKey::init(std::move(n), std::move(e));
+
+ RSA_PrivateKey::init(std::move(d), std::move(p), std::move(q),
+ std::move(d1), std::move(d2), std::move(c));
+ }
+
+/*
+* Check Private RSA Parameters
+*/
+bool RSA_PrivateKey::check_key(RandomNumberGenerator& rng, bool strong) const
+ {
+ if(get_n() < 35 || get_n().is_even() || get_e() < 3 || get_e().is_even())
+ return false;
+
+ if(get_d() < 2 || get_p() < 3 || get_q() < 3)
+ return false;
+
+ if(get_p() * get_q() != get_n())
+ return false;
+
+ if(get_p() == get_q())
+ return false;
+
+ if(get_d1() != ct_modulo(get_d(), get_p() - 1))
+ return false;
+ if(get_d2() != ct_modulo(get_d(), get_q() - 1))
+ return false;
+ if(get_c() != inverse_mod(get_q(), get_p()))
+ return false;
+
+ const size_t prob = (strong) ? 128 : 12;
+
+ if(!is_prime(get_p(), rng, prob))
+ return false;
+ if(!is_prime(get_q(), rng, prob))
+ return false;
+
+ if(strong)
+ {
+ if(ct_modulo(get_e() * get_d(), lcm(get_p() - 1, get_q() - 1)) != 1)
+ return false;
+
+ return KeyPair::signature_consistency_check(rng, *this, "EMSA4(SHA-256)");
+ }
+
+ return true;
+ }
+
+namespace {
+
+/**
+* RSA private (decrypt/sign) operation
+*/
+class RSA_Private_Operation
+ {
+ protected:
+ size_t public_modulus_bits() const { return m_public->public_modulus_bits(); }
+ size_t public_modulus_bytes() const { return m_public->public_modulus_bytes(); }
+
+ explicit RSA_Private_Operation(const RSA_PrivateKey& rsa, RandomNumberGenerator& rng) :
+ m_public(rsa.public_data()),
+ m_private(rsa.private_data()),
+ m_blinder(m_public->get_n(), rng,
+ [this](const BigInt& k) { return m_public->public_op(k); },
+ [this](const BigInt& k) { return inverse_mod(k, m_public->get_n()); }),
+ m_blinding_bits(64),
+ m_max_d1_bits(m_private->m_p_bits + m_blinding_bits),
+ m_max_d2_bits(m_private->m_q_bits + m_blinding_bits)
+ {
+ }
+
+ secure_vector<uint8_t> raw_op(const uint8_t input[], size_t input_len)
+ {
+ const BigInt input_bn(input, input_len);
+ if(input_bn >= m_public->get_n())
+ throw Invalid_Argument("RSA private op - input is too large");
+
+ // TODO: This should be a function on blinder
+ // BigInt Blinder::run_blinded_function(std::function<BigInt, BigInt> fn, const BigInt& input);
+
+ const BigInt recovered = m_blinder.unblind(rsa_private_op(m_blinder.blind(input_bn)));
+ BOTAN_ASSERT(input_bn == m_public->public_op(recovered), "RSA consistency check");
+ return BigInt::encode_1363(recovered, m_public->public_modulus_bytes());
+ }
+
+ private:
+
+ BigInt rsa_private_op(const BigInt& m) const
+ {
+ /*
+ TODO
+ Consider using Montgomery reduction instead of Barrett, using
+ the "Smooth RSA-CRT" method. https://eprint.iacr.org/2007/039.pdf
+ */
+
+ static constexpr size_t powm_window = 4;
+
+ // Compute this in main thread to avoid racing on the rng
+ const BigInt d1_mask(m_blinder.rng(), m_blinding_bits);
+
+#if defined(BOTAN_HAS_THREAD_UTILS) && !defined(BOTAN_HAS_VALGRIND)
+ #define BOTAN_RSA_USE_ASYNC
+#endif
+
+#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
+ * 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));
+ auto powm_d1_p = monty_precompute(m_private->m_monty_p, m_private->m_mod_p.reduce(m), powm_window);
+ BigInt j1 = monty_execute(*powm_d1_p, masked_d1, m_max_d1_bits);
+
+#if defined(BOTAN_RSA_USE_ASYNC)
+ return j1;
+ });
+#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));
+ auto powm_d2_q = monty_precompute(m_private->m_monty_q, m_private->m_mod_q.reduce(m), powm_window);
+ const BigInt j2 = monty_execute(*powm_d2_q, masked_d2, m_max_d2_bits);
+
+#if defined(BOTAN_RSA_USE_ASYNC)
+ BigInt 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
+ *
+ * We must avoid leaking if j1 >= j2 or not, as doing so allows deriving
+ * information about the secret prime. Do this by first adding p to j1,
+ * which should ensure the subtraction of j2 does not underflow. But
+ * this may still underflow if p and q are imbalanced in size.
+ */
+
+ j1 = m_private->m_mod_p.multiply(m_private->m_mod_p.reduce((m_private->get_p() + j1) - j2), m_private->get_c());
+ return mul_add(j1, m_private->get_q(), j2);
+ }
+
+ std::shared_ptr<const RSA_Public_Data> m_public;
+ std::shared_ptr<const RSA_Private_Data> m_private;
+
+ // XXX could the blinder starting pair be shared?
+ Blinder m_blinder;
+ const size_t m_blinding_bits;
+ const size_t m_max_d1_bits;
+ const size_t m_max_d2_bits;
+ };
+
+class RSA_Signature_Operation final : public PK_Ops::Signature_with_EMSA,
+ private RSA_Private_Operation
+ {
+ public:
+ size_t max_input_bits() const override { return public_modulus_bits() - 1; }
+
+ size_t signature_length() const override { return public_modulus_bytes(); }
+
+ RSA_Signature_Operation(const RSA_PrivateKey& rsa, const std::string& emsa, RandomNumberGenerator& rng) :
+ PK_Ops::Signature_with_EMSA(emsa),
+ RSA_Private_Operation(rsa, rng)
+ {
+ }
+
+ secure_vector<uint8_t> raw_sign(const uint8_t input[], size_t input_len,
+ RandomNumberGenerator&) override
+ {
+ return raw_op(input, input_len);
+ }
+ };
+
+class RSA_Decryption_Operation final : public PK_Ops::Decryption_with_EME,
+ private RSA_Private_Operation
+ {
+ public:
+
+ RSA_Decryption_Operation(const RSA_PrivateKey& rsa, const std::string& eme, RandomNumberGenerator& rng) :
+ PK_Ops::Decryption_with_EME(eme),
+ RSA_Private_Operation(rsa, rng)
+ {
+ }
+
+ size_t plaintext_length(size_t) const override { return public_modulus_bytes(); }
+
+ secure_vector<uint8_t> raw_decrypt(const uint8_t input[], size_t input_len) override
+ {
+ return raw_op(input, input_len);
+ }
+ };
+
+class RSA_KEM_Decryption_Operation final : public PK_Ops::KEM_Decryption_with_KDF,
+ private RSA_Private_Operation
+ {
+ public:
+
+ RSA_KEM_Decryption_Operation(const RSA_PrivateKey& key,
+ const std::string& kdf,
+ RandomNumberGenerator& rng) :
+ PK_Ops::KEM_Decryption_with_KDF(kdf),
+ RSA_Private_Operation(key, rng)
+ {}
+
+ secure_vector<uint8_t>
+ raw_kem_decrypt(const uint8_t encap_key[], size_t len) override
+ {
+ return raw_op(encap_key, len);
+ }
+ };
+
+/**
+* RSA public (encrypt/verify) operation
+*/
+class RSA_Public_Operation
+ {
+ public:
+ explicit RSA_Public_Operation(const RSA_PublicKey& rsa) :
+ m_public(rsa.public_data())
+ {}
+
+ size_t get_max_input_bits() const
+ {
+ const size_t n_bits = m_public->public_modulus_bits();
+
+ /*
+ Make Coverity happy that n_bits - 1 won't underflow
+
+ 5 bit minimum: smallest possible RSA key is 3*5
+ */
+ BOTAN_ASSERT_NOMSG(n_bits >= 5);
+ return n_bits - 1;
+ }
+
+ protected:
+ BigInt public_op(const BigInt& m) const
+ {
+ if(m >= m_public->get_n())
+ throw Invalid_Argument("RSA public op - input is too large");
+
+ return m_public->public_op(m);
+ }
+
+ size_t public_modulus_bytes() const { return m_public->public_modulus_bytes(); }
+
+ const BigInt& get_n() const { return m_public->get_n(); }
+
+ std::shared_ptr<const RSA_Public_Data> m_public;
+ };
+
+class RSA_Encryption_Operation final : public PK_Ops::Encryption_with_EME,
+ private RSA_Public_Operation
+ {
+ public:
+
+ RSA_Encryption_Operation(const RSA_PublicKey& rsa, const std::string& eme) :
+ PK_Ops::Encryption_with_EME(eme),
+ RSA_Public_Operation(rsa)
+ {
+ }
+
+ size_t ciphertext_length(size_t) const override { return public_modulus_bytes(); }
+
+ size_t max_raw_input_bits() const override { return get_max_input_bits(); }
+
+ secure_vector<uint8_t> raw_encrypt(const uint8_t input[], size_t input_len,
+ RandomNumberGenerator&) override
+ {
+ BigInt input_bn(input, input_len);
+ return BigInt::encode_1363(public_op(input_bn), public_modulus_bytes());
+ }
+ };
+
+class RSA_Verify_Operation final : public PK_Ops::Verification_with_EMSA,
+ private RSA_Public_Operation
+ {
+ public:
+
+ size_t max_input_bits() const override { return get_max_input_bits(); }
+
+ RSA_Verify_Operation(const RSA_PublicKey& rsa, const std::string& emsa) :
+ PK_Ops::Verification_with_EMSA(emsa),
+ RSA_Public_Operation(rsa)
+ {
+ }
+
+ bool with_recovery() const override { return true; }
+
+ secure_vector<uint8_t> verify_mr(const uint8_t input[], size_t input_len) override
+ {
+ BigInt input_bn(input, input_len);
+ return BigInt::encode_locked(public_op(input_bn));
+ }
+ };
+
+class RSA_KEM_Encryption_Operation final : public PK_Ops::KEM_Encryption_with_KDF,
+ private RSA_Public_Operation
+ {
+ public:
+
+ RSA_KEM_Encryption_Operation(const RSA_PublicKey& key,
+ const std::string& kdf) :
+ PK_Ops::KEM_Encryption_with_KDF(kdf),
+ RSA_Public_Operation(key) {}
+
+ private:
+ void raw_kem_encrypt(secure_vector<uint8_t>& out_encapsulated_key,
+ secure_vector<uint8_t>& raw_shared_key,
+ Botan::RandomNumberGenerator& rng) override
+ {
+ const BigInt r = BigInt::random_integer(rng, 1, get_n());
+ const BigInt c = public_op(r);
+
+ out_encapsulated_key = BigInt::encode_locked(c);
+ raw_shared_key = BigInt::encode_locked(r);
+ }
+ };
+
+}
+
+std::unique_ptr<PK_Ops::Encryption>
+RSA_PublicKey::create_encryption_op(RandomNumberGenerator& /*rng*/,
+ const std::string& params,
+ const std::string& provider) const
+ {
+#if defined(BOTAN_HAS_OPENSSL)
+ if(provider == "openssl" || provider.empty())
+ {
+ try
+ {
+ return make_openssl_rsa_enc_op(*this, params);
+ }
+ catch(Exception& e)
+ {
+ /*
+ * If OpenSSL for some reason could not handle this (eg due to OAEP params),
+ * throw if openssl was specifically requested but otherwise just fall back
+ * to the normal version.
+ */
+ if(provider == "openssl")
+ throw Lookup_Error("OpenSSL RSA provider rejected key:" + std::string(e.what()));
+ }
+ }
+#endif
+
+ if(provider == "base" || provider.empty())
+ return std::unique_ptr<PK_Ops::Encryption>(new RSA_Encryption_Operation(*this, params));
+ throw Provider_Not_Found(algo_name(), provider);
+ }
+
+std::unique_ptr<PK_Ops::KEM_Encryption>
+RSA_PublicKey::create_kem_encryption_op(RandomNumberGenerator& /*rng*/,
+ const std::string& params,
+ const std::string& provider) const
+ {
+ if(provider == "base" || provider.empty())
+ return std::unique_ptr<PK_Ops::KEM_Encryption>(new RSA_KEM_Encryption_Operation(*this, params));
+ throw Provider_Not_Found(algo_name(), provider);
+ }
+
+std::unique_ptr<PK_Ops::Verification>
+RSA_PublicKey::create_verification_op(const std::string& params,
+ const std::string& provider) const
+ {
+#if defined(BOTAN_HAS_OPENSSL)
+ if(provider == "openssl" || provider.empty())
+ {
+ std::unique_ptr<PK_Ops::Verification> res = make_openssl_rsa_ver_op(*this, params);
+ if(res)
+ return res;
+ }
+#endif
+
+ if(provider == "base" || provider.empty())
+ return std::unique_ptr<PK_Ops::Verification>(new RSA_Verify_Operation(*this, params));
+
+ throw Provider_Not_Found(algo_name(), provider);
+ }
+
+std::unique_ptr<PK_Ops::Decryption>
+RSA_PrivateKey::create_decryption_op(RandomNumberGenerator& rng,
+ const std::string& params,
+ const std::string& provider) const
+ {
+#if defined(BOTAN_HAS_OPENSSL)
+ if(provider == "openssl" || provider.empty())
+ {
+ try
+ {
+ return make_openssl_rsa_dec_op(*this, params);
+ }
+ catch(Exception& e)
+ {
+ if(provider == "openssl")
+ throw Lookup_Error("OpenSSL RSA provider rejected key:" + std::string(e.what()));
+ }
+ }
+#endif
+
+ if(provider == "base" || provider.empty())
+ return std::unique_ptr<PK_Ops::Decryption>(new RSA_Decryption_Operation(*this, params, rng));
+
+ throw Provider_Not_Found(algo_name(), provider);
+ }
+
+std::unique_ptr<PK_Ops::KEM_Decryption>
+RSA_PrivateKey::create_kem_decryption_op(RandomNumberGenerator& rng,
+ const std::string& params,
+ const std::string& provider) const
+ {
+ if(provider == "base" || provider.empty())
+ return std::unique_ptr<PK_Ops::KEM_Decryption>(new RSA_KEM_Decryption_Operation(*this, params, rng));
+
+ throw Provider_Not_Found(algo_name(), provider);
+ }
+
+std::unique_ptr<PK_Ops::Signature>
+RSA_PrivateKey::create_signature_op(RandomNumberGenerator& rng,
+ const std::string& params,
+ const std::string& provider) const
+ {
+#if defined(BOTAN_HAS_OPENSSL)
+ if(provider == "openssl" || provider.empty())
+ {
+ std::unique_ptr<PK_Ops::Signature> res = make_openssl_rsa_sig_op(*this, params);
+ if(res)
+ return res;
+ }
+#endif
+
+ if(provider == "base" || provider.empty())
+ return std::unique_ptr<PK_Ops::Signature>(new RSA_Signature_Operation(*this, params, rng));
+
+ throw Provider_Not_Found(algo_name(), provider);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/pubkey/rsa/rsa.h b/comm/third_party/botan/src/lib/pubkey/rsa/rsa.h
new file mode 100644
index 0000000000..2a02c89d56
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/rsa/rsa.h
@@ -0,0 +1,180 @@
+/*
+* RSA
+* (C) 1999-2008,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_RSA_H_
+#define BOTAN_RSA_H_
+
+#include <botan/pk_keys.h>
+#include <botan/bigint.h>
+#include <string>
+#include <memory>
+#include <vector>
+
+namespace Botan {
+
+class RSA_Public_Data;
+class RSA_Private_Data;
+
+/**
+* RSA Public Key
+*/
+class BOTAN_PUBLIC_API(2,0) RSA_PublicKey : public virtual Public_Key
+ {
+ public:
+ /**
+ * Load a public key.
+ * @param alg_id the X.509 algorithm identifier
+ * @param key_bits DER encoded public key bits
+ */
+ RSA_PublicKey(const AlgorithmIdentifier& alg_id,
+ const std::vector<uint8_t>& key_bits);
+
+ /**
+ * Create a public key.
+ * @arg n the modulus
+ * @arg e the exponent
+ */
+ RSA_PublicKey(const BigInt& n, const BigInt& e);
+
+ std::string algo_name() const override { return "RSA"; }
+
+ bool check_key(RandomNumberGenerator& rng, bool) const override;
+
+ AlgorithmIdentifier algorithm_identifier() const override;
+
+ std::vector<uint8_t> public_key_bits() const override;
+
+ /**
+ * @return public modulus
+ */
+ const BigInt& get_n() const;
+
+ /**
+ * @return public exponent
+ */
+ const BigInt& get_e() const;
+
+ size_t key_length() const override;
+ size_t estimated_strength() const override;
+
+ // internal functions:
+ std::shared_ptr<const RSA_Public_Data> public_data() const;
+
+ std::unique_ptr<PK_Ops::Encryption>
+ create_encryption_op(RandomNumberGenerator& rng,
+ const std::string& params,
+ const std::string& provider) const override;
+
+ std::unique_ptr<PK_Ops::KEM_Encryption>
+ create_kem_encryption_op(RandomNumberGenerator& rng,
+ const std::string& params,
+ const std::string& provider) const override;
+
+ std::unique_ptr<PK_Ops::Verification>
+ create_verification_op(const std::string& params,
+ const std::string& provider) const override;
+
+ protected:
+ RSA_PublicKey() = default;
+
+ void init(BigInt&& n, BigInt&& e);
+
+ std::shared_ptr<const RSA_Public_Data> m_public;
+ };
+
+/**
+* RSA Private Key
+*/
+class BOTAN_PUBLIC_API(2,0) RSA_PrivateKey final : public Private_Key, public RSA_PublicKey
+ {
+ public:
+ /**
+ * Load a private key.
+ * @param alg_id the X.509 algorithm identifier
+ * @param key_bits PKCS#1 RSAPrivateKey bits
+ */
+ RSA_PrivateKey(const AlgorithmIdentifier& alg_id,
+ const secure_vector<uint8_t>& key_bits);
+
+ /**
+ * Construct a private key from the specified parameters.
+ * @param p the first prime
+ * @param q the second prime
+ * @param e the exponent
+ * @param d if specified, this has to be d with
+ * exp * d = 1 mod (p - 1, q - 1). Leave it as 0 if you wish to
+ * the constructor to calculate it.
+ * @param n if specified, this must be n = p * q. Leave it as 0
+ * if you wish to the constructor to calculate it.
+ */
+ RSA_PrivateKey(const BigInt& p, const BigInt& q,
+ const BigInt& e, const BigInt& d = 0,
+ const BigInt& n = 0);
+
+ /**
+ * Create a new private key with the specified bit length
+ * @param rng the random number generator to use
+ * @param bits the desired bit length of the private key
+ * @param exp the public exponent to be used
+ */
+ RSA_PrivateKey(RandomNumberGenerator& rng,
+ size_t bits, size_t exp = 65537);
+
+ bool check_key(RandomNumberGenerator& rng, bool) const override;
+
+ /**
+ * Get the first prime p.
+ * @return prime p
+ */
+ const BigInt& get_p() const;
+
+ /**
+ * Get the second prime q.
+ * @return prime q
+ */
+ const BigInt& get_q() const;
+
+ /**
+ * Get d with exp * d = 1 mod (p - 1, q - 1).
+ * @return d
+ */
+ const BigInt& get_d() const;
+
+ const BigInt& get_c() const;
+ const BigInt& get_d1() const;
+ const BigInt& get_d2() const;
+
+ secure_vector<uint8_t> private_key_bits() const override;
+
+ // internal functions:
+ std::shared_ptr<const RSA_Private_Data> private_data() const;
+
+ std::unique_ptr<PK_Ops::Decryption>
+ create_decryption_op(RandomNumberGenerator& rng,
+ const std::string& params,
+ const std::string& provider) const override;
+
+ std::unique_ptr<PK_Ops::KEM_Decryption>
+ create_kem_decryption_op(RandomNumberGenerator& rng,
+ const std::string& params,
+ const std::string& provider) const override;
+
+ std::unique_ptr<PK_Ops::Signature>
+ create_signature_op(RandomNumberGenerator& rng,
+ const std::string& params,
+ const std::string& provider) const override;
+
+ private:
+
+ void init(BigInt&& d, BigInt&& p, BigInt&& q, BigInt&& d1, BigInt&& d2, BigInt&& c);
+
+ std::shared_ptr<const RSA_Private_Data> m_private;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/pubkey/sm2/info.txt b/comm/third_party/botan/src/lib/pubkey/sm2/info.txt
new file mode 100644
index 0000000000..a3f756820a
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/sm2/info.txt
@@ -0,0 +1,14 @@
+<defines>
+SM2 -> 20180801
+</defines>
+
+<requires>
+asn1
+ec_group
+ecc_key
+keypair
+numbertheory
+rng
+sm3
+kdf2
+</requires>
diff --git a/comm/third_party/botan/src/lib/pubkey/sm2/sm2.cpp b/comm/third_party/botan/src/lib/pubkey/sm2/sm2.cpp
new file mode 100644
index 0000000000..a976c097b3
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/sm2/sm2.cpp
@@ -0,0 +1,306 @@
+/*
+* SM2 Signatures
+* (C) 2017,2018 Ribose Inc
+* (C) 2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/sm2.h>
+#include <botan/internal/pk_ops_impl.h>
+#include <botan/internal/point_mul.h>
+#include <botan/loadstor.h>
+#include <botan/numthry.h>
+#include <botan/keypair.h>
+#include <botan/hash.h>
+#include <botan/parsing.h>
+
+namespace Botan {
+
+std::string SM2_PublicKey::algo_name() const
+ {
+ return "SM2";
+ }
+
+bool SM2_PrivateKey::check_key(RandomNumberGenerator& rng,
+ bool strong) const
+ {
+ if(!public_point().on_the_curve())
+ return false;
+
+ if(!strong)
+ return true;
+
+ return KeyPair::signature_consistency_check(rng, *this, "user@example.com,SM3");
+ }
+
+SM2_PrivateKey::SM2_PrivateKey(const AlgorithmIdentifier& alg_id,
+ const secure_vector<uint8_t>& key_bits) :
+ EC_PrivateKey(alg_id, key_bits)
+ {
+ m_da_inv = domain().inverse_mod_order(m_private_key + 1);
+ }
+
+SM2_PrivateKey::SM2_PrivateKey(RandomNumberGenerator& rng,
+ const EC_Group& domain,
+ const BigInt& x) :
+ EC_PrivateKey(rng, domain, x)
+ {
+ m_da_inv = domain.inverse_mod_order(m_private_key + 1);
+ }
+
+std::vector<uint8_t> sm2_compute_za(HashFunction& hash,
+ const std::string& user_id,
+ const EC_Group& domain,
+ const PointGFp& pubkey)
+ {
+ if(user_id.size() >= 8192)
+ throw Invalid_Argument("SM2 user id too long to represent");
+
+ const uint16_t uid_len = static_cast<uint16_t>(8 * user_id.size());
+
+ hash.update(get_byte(0, uid_len));
+ hash.update(get_byte(1, uid_len));
+ hash.update(user_id);
+
+ const size_t p_bytes = domain.get_p_bytes();
+
+ hash.update(BigInt::encode_1363(domain.get_a(), p_bytes));
+ hash.update(BigInt::encode_1363(domain.get_b(), p_bytes));
+ hash.update(BigInt::encode_1363(domain.get_g_x(), p_bytes));
+ hash.update(BigInt::encode_1363(domain.get_g_y(), p_bytes));
+ hash.update(BigInt::encode_1363(pubkey.get_affine_x(), p_bytes));
+ hash.update(BigInt::encode_1363(pubkey.get_affine_y(), p_bytes));
+
+ std::vector<uint8_t> za(hash.output_length());
+ hash.final(za.data());
+
+ return za;
+ }
+
+namespace {
+
+/**
+* SM2 signature operation
+*/
+class SM2_Signature_Operation final : public PK_Ops::Signature
+ {
+ public:
+
+ SM2_Signature_Operation(const SM2_PrivateKey& sm2,
+ const std::string& ident,
+ const std::string& hash) :
+ m_group(sm2.domain()),
+ m_x(sm2.private_value()),
+ m_da_inv(sm2.get_da_inv())
+ {
+ if(hash == "Raw")
+ {
+ // m_hash is null, m_za is empty
+ }
+ else
+ {
+ m_hash = HashFunction::create_or_throw(hash);
+ // ZA=H256(ENTLA || IDA || a || b || xG || yG || xA || yA)
+ m_za = sm2_compute_za(*m_hash, ident, m_group, sm2.public_point());
+ m_hash->update(m_za);
+ }
+ }
+
+ size_t signature_length() const override { return 2*m_group.get_order_bytes(); }
+
+ void update(const uint8_t msg[], size_t msg_len) override
+ {
+ if(m_hash)
+ {
+ m_hash->update(msg, msg_len);
+ }
+ else
+ {
+ m_digest.insert(m_digest.end(), msg, msg + msg_len);
+ }
+ }
+
+ secure_vector<uint8_t> sign(RandomNumberGenerator& rng) override;
+
+ private:
+ const EC_Group m_group;
+ const BigInt& m_x;
+ const BigInt& m_da_inv;
+
+ std::vector<uint8_t> m_za;
+ secure_vector<uint8_t> m_digest;
+ std::unique_ptr<HashFunction> m_hash;
+ std::vector<BigInt> m_ws;
+ };
+
+secure_vector<uint8_t>
+SM2_Signature_Operation::sign(RandomNumberGenerator& rng)
+ {
+ BigInt e;
+ if(m_hash)
+ {
+ e = BigInt::decode(m_hash->final());
+ // prepend ZA for next signature if any
+ m_hash->update(m_za);
+ }
+ else
+ {
+ e = BigInt::decode(m_digest);
+ m_digest.clear();
+ }
+
+ const BigInt k = m_group.random_scalar(rng);
+
+ const BigInt r = m_group.mod_order(
+ m_group.blinded_base_point_multiply_x(k, rng, m_ws) + e);
+ const BigInt s = m_group.multiply_mod_order(m_da_inv, m_group.mod_order(k - r*m_x));
+
+ return BigInt::encode_fixed_length_int_pair(r, s, m_group.get_order().bytes());
+ }
+
+/**
+* SM2 verification operation
+*/
+class SM2_Verification_Operation final : public PK_Ops::Verification
+ {
+ public:
+ SM2_Verification_Operation(const SM2_PublicKey& sm2,
+ const std::string& ident,
+ const std::string& hash) :
+ m_group(sm2.domain()),
+ m_gy_mul(m_group.get_base_point(), sm2.public_point())
+ {
+ if(hash == "Raw")
+ {
+ // m_hash is null, m_za is empty
+ }
+ else
+ {
+ m_hash = HashFunction::create_or_throw(hash);
+ // ZA=H256(ENTLA || IDA || a || b || xG || yG || xA || yA)
+ m_za = sm2_compute_za(*m_hash, ident, m_group, sm2.public_point());
+ m_hash->update(m_za);
+ }
+ }
+
+ void update(const uint8_t msg[], size_t msg_len) override
+ {
+ if(m_hash)
+ {
+ m_hash->update(msg, msg_len);
+ }
+ else
+ {
+ m_digest.insert(m_digest.end(), msg, msg + msg_len);
+ }
+ }
+
+ bool is_valid_signature(const uint8_t sig[], size_t sig_len) override;
+ private:
+ const EC_Group m_group;
+ const PointGFp_Multi_Point_Precompute m_gy_mul;
+ secure_vector<uint8_t> m_digest;
+ std::vector<uint8_t> m_za;
+ std::unique_ptr<HashFunction> m_hash;
+ };
+
+bool SM2_Verification_Operation::is_valid_signature(const uint8_t sig[], size_t sig_len)
+ {
+ BigInt e;
+ if(m_hash)
+ {
+ e = BigInt::decode(m_hash->final());
+ // prepend ZA for next signature if any
+ m_hash->update(m_za);
+ }
+ else
+ {
+ e = BigInt::decode(m_digest);
+ m_digest.clear();
+ }
+
+ if(sig_len != m_group.get_order().bytes()*2)
+ return false;
+
+ const BigInt r(sig, sig_len / 2);
+ const BigInt s(sig + sig_len / 2, sig_len / 2);
+
+ if(r <= 0 || r >= m_group.get_order() || s <= 0 || s >= m_group.get_order())
+ return false;
+
+ const BigInt t = m_group.mod_order(r + s);
+
+ if(t == 0)
+ return false;
+
+ const PointGFp R = m_gy_mul.multi_exp(s, t);
+
+ // ???
+ if(R.is_zero())
+ return false;
+
+ return (m_group.mod_order(R.get_affine_x() + e) == r);
+ }
+
+void parse_sm2_param_string(const std::string& params,
+ std::string& userid,
+ std::string& hash)
+ {
+ // GM/T 0009-2012 specifies this as the default userid
+ const std::string default_userid = "1234567812345678";
+
+ // defaults:
+ userid = default_userid;
+ hash = "SM3";
+
+ /*
+ * SM2 parameters have the following possible formats:
+ * Ident [since 2.2.0]
+ * Ident,Hash [since 2.3.0]
+ */
+
+ auto comma = params.find(',');
+ if(comma == std::string::npos)
+ {
+ userid = params;
+ }
+ else
+ {
+ userid = params.substr(0, comma);
+ hash = params.substr(comma+1, std::string::npos);
+ }
+ }
+
+}
+
+std::unique_ptr<PK_Ops::Verification>
+SM2_PublicKey::create_verification_op(const std::string& params,
+ const std::string& provider) const
+ {
+ if(provider == "base" || provider.empty())
+ {
+ std::string userid, hash;
+ parse_sm2_param_string(params, userid, hash);
+ return std::unique_ptr<PK_Ops::Verification>(new SM2_Verification_Operation(*this, userid, hash));
+ }
+
+ throw Provider_Not_Found(algo_name(), provider);
+ }
+
+std::unique_ptr<PK_Ops::Signature>
+SM2_PrivateKey::create_signature_op(RandomNumberGenerator& /*rng*/,
+ const std::string& params,
+ const std::string& provider) const
+ {
+ if(provider == "base" || provider.empty())
+ {
+ std::string userid, hash;
+ parse_sm2_param_string(params, userid, hash);
+ return std::unique_ptr<PK_Ops::Signature>(new SM2_Signature_Operation(*this, userid, hash));
+ }
+
+ throw Provider_Not_Found(algo_name(), provider);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/pubkey/sm2/sm2.h b/comm/third_party/botan/src/lib/pubkey/sm2/sm2.h
new file mode 100644
index 0000000000..7b5f38858b
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/sm2/sm2.h
@@ -0,0 +1,124 @@
+/*
+* SM2
+* (C) 2017 Ribose Inc
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_SM2_KEY_H_
+#define BOTAN_SM2_KEY_H_
+
+#include <botan/ecc_key.h>
+
+namespace Botan {
+
+/**
+* This class represents SM2 public keys
+*/
+class BOTAN_PUBLIC_API(2,2) SM2_PublicKey : public virtual EC_PublicKey
+ {
+ public:
+
+ /**
+ * Create a public key from a given public point.
+ * @param dom_par the domain parameters associated with this key
+ * @param public_point the public point defining this key
+ */
+ SM2_PublicKey(const EC_Group& dom_par,
+ const PointGFp& public_point) :
+ EC_PublicKey(dom_par, public_point) {}
+
+ /**
+ * Load a public key.
+ * @param alg_id the X.509 algorithm identifier
+ * @param key_bits DER encoded public key bits
+ */
+ SM2_PublicKey(const AlgorithmIdentifier& alg_id,
+ const std::vector<uint8_t>& key_bits) :
+ EC_PublicKey(alg_id, key_bits) {}
+
+ /**
+ * Get this keys algorithm name.
+ * @result this keys algorithm name
+ */
+ std::string algo_name() const override;
+
+ size_t message_parts() const override { return 2; }
+
+ size_t message_part_size() const override
+ { return domain().get_order().bytes(); }
+
+ std::unique_ptr<PK_Ops::Verification>
+ create_verification_op(const std::string& params,
+ const std::string& provider) const override;
+
+ std::unique_ptr<PK_Ops::Encryption>
+ create_encryption_op(RandomNumberGenerator& rng,
+ const std::string& params,
+ const std::string& provider) const override;
+
+ protected:
+ SM2_PublicKey() = default;
+ };
+
+/**
+* This class represents SM2 private keys
+*/
+class BOTAN_PUBLIC_API(2,2) SM2_PrivateKey final :
+ public SM2_PublicKey, public EC_PrivateKey
+ {
+ public:
+
+ /**
+ * Load a private key
+ * @param alg_id the X.509 algorithm identifier
+ * @param key_bits ECPrivateKey bits
+ */
+ SM2_PrivateKey(const AlgorithmIdentifier& alg_id,
+ const secure_vector<uint8_t>& key_bits);
+
+ /**
+ * Create a private key.
+ * @param rng a random number generator
+ * @param domain parameters to used for this key
+ * @param x the private key (if zero, generate a new random key)
+ */
+ SM2_PrivateKey(RandomNumberGenerator& rng,
+ const EC_Group& domain,
+ const BigInt& x = 0);
+
+ bool check_key(RandomNumberGenerator& rng, bool) const override;
+
+ std::unique_ptr<PK_Ops::Signature>
+ create_signature_op(RandomNumberGenerator& rng,
+ const std::string& params,
+ const std::string& provider) const override;
+
+ std::unique_ptr<PK_Ops::Decryption>
+ create_decryption_op(RandomNumberGenerator& rng,
+ const std::string& params,
+ const std::string& provider) const override;
+
+ const BigInt& get_da_inv() const { return m_da_inv; }
+ private:
+ BigInt m_da_inv;
+ };
+
+class HashFunction;
+
+std::vector<uint8_t>
+BOTAN_PUBLIC_API(2,5) sm2_compute_za(HashFunction& hash,
+ const std::string& user_id,
+ const EC_Group& domain,
+ const PointGFp& pubkey);
+
+// For compat with versions 2.2 - 2.7
+typedef SM2_PublicKey SM2_Signature_PublicKey;
+typedef SM2_PublicKey SM2_Encryption_PublicKey;
+
+typedef SM2_PrivateKey SM2_Signature_PrivateKey;
+typedef SM2_PrivateKey SM2_Encryption_PrivateKey;
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/pubkey/sm2/sm2_enc.cpp b/comm/third_party/botan/src/lib/pubkey/sm2/sm2_enc.cpp
new file mode 100644
index 0000000000..55549afe30
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/sm2/sm2_enc.cpp
@@ -0,0 +1,267 @@
+/*
+* SM2 Encryption
+* (C) 2017 Ribose Inc
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/sm2.h>
+#include <botan/internal/point_mul.h>
+#include <botan/pk_ops.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
+#include <botan/kdf.h>
+#include <botan/hash.h>
+
+namespace Botan {
+
+namespace {
+
+class SM2_Encryption_Operation final : public PK_Ops::Encryption
+ {
+ public:
+ SM2_Encryption_Operation(const SM2_Encryption_PublicKey& key,
+ RandomNumberGenerator& rng,
+ const std::string& kdf_hash) :
+ m_group(key.domain()),
+ m_kdf_hash(kdf_hash),
+ m_ws(PointGFp::WORKSPACE_SIZE),
+ m_mul_public_point(key.public_point(), rng, m_ws)
+ {
+ std::unique_ptr<HashFunction> hash = HashFunction::create_or_throw(m_kdf_hash);
+ m_hash_size = hash->output_length();
+ }
+
+ size_t max_input_bits() const override
+ {
+ // This is arbitrary, but assumes SM2 is used for key encapsulation
+ return 512;
+ }
+
+ size_t ciphertext_length(size_t ptext_len) const override
+ {
+ const size_t elem_size = m_group.get_order_bytes();
+ const size_t der_overhead = 16;
+
+ return der_overhead + 2*elem_size + m_hash_size + ptext_len;
+ }
+
+ secure_vector<uint8_t> encrypt(const uint8_t msg[],
+ size_t msg_len,
+ RandomNumberGenerator& rng) override
+ {
+ std::unique_ptr<HashFunction> hash = HashFunction::create_or_throw(m_kdf_hash);
+ std::unique_ptr<KDF> kdf = KDF::create_or_throw("KDF2(" + m_kdf_hash + ")");
+
+ const size_t p_bytes = m_group.get_p_bytes();
+
+ const BigInt k = m_group.random_scalar(rng);
+
+ const PointGFp C1 = m_group.blinded_base_point_multiply(k, rng, m_ws);
+ const BigInt x1 = C1.get_affine_x();
+ const BigInt y1 = C1.get_affine_y();
+ std::vector<uint8_t> x1_bytes(p_bytes);
+ std::vector<uint8_t> y1_bytes(p_bytes);
+ BigInt::encode_1363(x1_bytes.data(), x1_bytes.size(), x1);
+ BigInt::encode_1363(y1_bytes.data(), y1_bytes.size(), y1);
+
+ const PointGFp kPB = m_mul_public_point.mul(k, rng, m_group.get_order(), m_ws);
+
+ const BigInt x2 = kPB.get_affine_x();
+ const BigInt y2 = kPB.get_affine_y();
+ std::vector<uint8_t> x2_bytes(p_bytes);
+ std::vector<uint8_t> y2_bytes(p_bytes);
+ BigInt::encode_1363(x2_bytes.data(), x2_bytes.size(), x2);
+ BigInt::encode_1363(y2_bytes.data(), y2_bytes.size(), y2);
+
+ secure_vector<uint8_t> kdf_input;
+ kdf_input += x2_bytes;
+ kdf_input += y2_bytes;
+
+ const secure_vector<uint8_t> kdf_output =
+ kdf->derive_key(msg_len, kdf_input.data(), kdf_input.size());
+
+ secure_vector<uint8_t> masked_msg(msg_len);
+ xor_buf(masked_msg.data(), msg, kdf_output.data(), msg_len);
+
+ hash->update(x2_bytes);
+ hash->update(msg, msg_len);
+ hash->update(y2_bytes);
+ std::vector<uint8_t> C3(hash->output_length());
+ hash->final(C3.data());
+
+ return DER_Encoder()
+ .start_cons(SEQUENCE)
+ .encode(x1)
+ .encode(y1)
+ .encode(C3, OCTET_STRING)
+ .encode(masked_msg, OCTET_STRING)
+ .end_cons()
+ .get_contents();
+ }
+
+ private:
+ const EC_Group m_group;
+ const std::string m_kdf_hash;
+
+ std::vector<BigInt> m_ws;
+ PointGFp_Var_Point_Precompute m_mul_public_point;
+ size_t m_hash_size;
+ };
+
+class SM2_Decryption_Operation final : public PK_Ops::Decryption
+ {
+ public:
+ SM2_Decryption_Operation(const SM2_Encryption_PrivateKey& key,
+ RandomNumberGenerator& rng,
+ const std::string& kdf_hash) :
+ m_key(key),
+ m_rng(rng),
+ m_kdf_hash(kdf_hash)
+ {
+ std::unique_ptr<HashFunction> hash = HashFunction::create_or_throw(m_kdf_hash);
+ m_hash_size = hash->output_length();
+ }
+
+ size_t plaintext_length(size_t ptext_len) const override
+ {
+ /*
+ * This ignores the DER encoding and so overestimates the
+ * plaintext length by 12 bytes or so
+ */
+ const size_t elem_size = m_key.domain().get_order_bytes();
+
+ if(ptext_len < 2*elem_size + m_hash_size)
+ return 0;
+
+ return ptext_len - (2*elem_size + m_hash_size);
+ }
+
+ secure_vector<uint8_t> decrypt(uint8_t& valid_mask,
+ const uint8_t ciphertext[],
+ size_t ciphertext_len) override
+ {
+ const EC_Group& group = m_key.domain();
+ const BigInt& cofactor = group.get_cofactor();
+ const size_t p_bytes = group.get_p_bytes();
+
+ valid_mask = 0x00;
+
+ std::unique_ptr<HashFunction> hash = HashFunction::create_or_throw(m_kdf_hash);
+ std::unique_ptr<KDF> kdf = KDF::create_or_throw("KDF2(" + m_kdf_hash + ")");
+
+ // Too short to be valid - no timing problem from early return
+ if(ciphertext_len < 1 + p_bytes*2 + hash->output_length())
+ {
+ return secure_vector<uint8_t>();
+ }
+
+ BigInt x1, y1;
+ secure_vector<uint8_t> C3, masked_msg;
+
+ BER_Decoder(ciphertext, ciphertext_len)
+ .start_cons(SEQUENCE)
+ .decode(x1)
+ .decode(y1)
+ .decode(C3, OCTET_STRING)
+ .decode(masked_msg, OCTET_STRING)
+ .end_cons()
+ .verify_end();
+
+ std::vector<uint8_t> recode_ctext;
+ DER_Encoder(recode_ctext)
+ .start_cons(SEQUENCE)
+ .encode(x1)
+ .encode(y1)
+ .encode(C3, OCTET_STRING)
+ .encode(masked_msg, OCTET_STRING)
+ .end_cons();
+
+ if(recode_ctext.size() != ciphertext_len)
+ return secure_vector<uint8_t>();
+
+ if(same_mem(recode_ctext.data(), ciphertext, ciphertext_len) == false)
+ return secure_vector<uint8_t>();
+
+ PointGFp C1 = group.point(x1, y1);
+ C1.randomize_repr(m_rng);
+
+ // Here C1 is publically invalid, so no problem with early return:
+ if(!C1.on_the_curve())
+ return secure_vector<uint8_t>();
+
+ if(cofactor > 1 && (C1 * cofactor).is_zero())
+ {
+ return secure_vector<uint8_t>();
+ }
+
+ const PointGFp dbC1 = group.blinded_var_point_multiply(
+ C1, m_key.private_value(), m_rng, m_ws);
+
+ const BigInt x2 = dbC1.get_affine_x();
+ const BigInt y2 = dbC1.get_affine_y();
+
+ secure_vector<uint8_t> x2_bytes(p_bytes);
+ secure_vector<uint8_t> y2_bytes(p_bytes);
+ BigInt::encode_1363(x2_bytes.data(), x2_bytes.size(), x2);
+ BigInt::encode_1363(y2_bytes.data(), y2_bytes.size(), y2);
+
+ secure_vector<uint8_t> kdf_input;
+ kdf_input += x2_bytes;
+ kdf_input += y2_bytes;
+
+ const secure_vector<uint8_t> kdf_output =
+ kdf->derive_key(masked_msg.size(), kdf_input.data(), kdf_input.size());
+
+ xor_buf(masked_msg.data(), kdf_output.data(), kdf_output.size());
+
+ hash->update(x2_bytes);
+ hash->update(masked_msg);
+ hash->update(y2_bytes);
+ secure_vector<uint8_t> u = hash->final();
+
+ if(constant_time_compare(u.data(), C3.data(), hash->output_length()) == false)
+ return secure_vector<uint8_t>();
+
+ valid_mask = 0xFF;
+ return masked_msg;
+ }
+ private:
+ const SM2_Encryption_PrivateKey& m_key;
+ RandomNumberGenerator& m_rng;
+ const std::string m_kdf_hash;
+ std::vector<BigInt> m_ws;
+ size_t m_hash_size;
+ };
+
+}
+
+std::unique_ptr<PK_Ops::Encryption>
+SM2_PublicKey::create_encryption_op(RandomNumberGenerator& rng,
+ const std::string& params,
+ const std::string& provider) const
+ {
+ if(provider == "base" || provider.empty())
+ {
+ const std::string kdf_hash = (params.empty() ? "SM3" : params);
+ return std::unique_ptr<PK_Ops::Encryption>(new SM2_Encryption_Operation(*this, rng, kdf_hash));
+ }
+
+ throw Provider_Not_Found(algo_name(), provider);
+ }
+
+std::unique_ptr<PK_Ops::Decryption>
+SM2_PrivateKey::create_decryption_op(RandomNumberGenerator& rng,
+ const std::string& params,
+ const std::string& provider) const
+ {
+ if(provider == "base" || provider.empty())
+ {
+ const std::string kdf_hash = (params.empty() ? "SM3" : params);
+ return std::unique_ptr<PK_Ops::Decryption>(new SM2_Decryption_Operation(*this, rng, kdf_hash));
+ }
+
+ throw Provider_Not_Found(algo_name(), provider);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/pubkey/sm2/sm2_enc.h b/comm/third_party/botan/src/lib/pubkey/sm2/sm2_enc.h
new file mode 100644
index 0000000000..ea8c43d9d5
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/sm2/sm2_enc.h
@@ -0,0 +1,15 @@
+/*
+* SM2 Encryption
+* (C) 2017 Ribose Inc
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_SM2_ENC_KEY_H_
+#define BOTAN_SM2_ENC_KEY_H_
+
+#include <botan/sm2.h>
+
+BOTAN_DEPRECATED_HEADER(sm2_enc.h)
+
+#endif
diff --git a/comm/third_party/botan/src/lib/pubkey/workfactor.cpp b/comm/third_party/botan/src/lib/pubkey/workfactor.cpp
new file mode 100644
index 0000000000..bb4fd56ca5
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/workfactor.cpp
@@ -0,0 +1,66 @@
+/*
+* Public Key Work Factor Functions
+* (C) 1999-2007,2012 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/workfactor.h>
+#include <algorithm>
+#include <cmath>
+
+namespace Botan {
+
+size_t ecp_work_factor(size_t bits)
+ {
+ return bits / 2;
+ }
+
+namespace {
+
+size_t nfs_workfactor(size_t bits, double log2_k)
+ {
+ // approximates natural logarithm of an integer of given bitsize
+ const double log2_e = 1.44269504088896340736;
+ const double log_p = bits / log2_e;
+
+ const double log_log_p = std::log(log_p);
+
+ // RFC 3766: k * e^((1.92 + o(1)) * cubrt(ln(n) * (ln(ln(n)))^2))
+ const double est = 1.92 * std::pow(log_p * log_log_p * log_log_p, 1.0/3.0);
+
+ // return log2 of the workfactor
+ return static_cast<size_t>(log2_k + log2_e * est);
+ }
+
+}
+
+size_t if_work_factor(size_t bits)
+ {
+ // RFC 3766 estimates k at .02 and o(1) to be effectively zero for sizes of interest
+
+ const double log2_k = -5.6438; // log2(.02)
+ return nfs_workfactor(bits, log2_k);
+ }
+
+size_t dl_work_factor(size_t bits)
+ {
+ // Lacking better estimates...
+ return if_work_factor(bits);
+ }
+
+size_t dl_exponent_size(size_t bits)
+ {
+ /*
+ This uses a slightly tweaked version of the standard work factor
+ function above. It assumes k is 1 (thus overestimating the strength
+ of the prime group by 5-6 bits), and always returns at least 128 bits
+ (this only matters for very small primes).
+ */
+ const size_t min_workfactor = 64;
+ const double log2_k = 0;
+
+ return 2 * std::max<size_t>(min_workfactor, nfs_workfactor(bits, log2_k));
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/pubkey/workfactor.h b/comm/third_party/botan/src/lib/pubkey/workfactor.h
new file mode 100644
index 0000000000..2c8c876420
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/workfactor.h
@@ -0,0 +1,51 @@
+/*
+* Public Key Work Factor Functions
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_WORKFACTOR_H_
+#define BOTAN_WORKFACTOR_H_
+
+#include <botan/types.h>
+BOTAN_FUTURE_INTERNAL_HEADER(workfactor.h)
+
+namespace Botan {
+
+/**
+* Estimate work factor for discrete logarithm
+* @param prime_group_size size of the group in bits
+* @return estimated security level for this group
+*/
+BOTAN_PUBLIC_API(2,0) size_t dl_work_factor(size_t prime_group_size);
+
+/**
+* Return the appropriate exponent size to use for a particular prime
+* group. This is twice the size of the estimated cost of breaking the
+* key using an index calculus attack; the assumption is that if an
+* arbitrary discrete log on a group of size bits would take about 2^n
+* effort, and thus using an exponent of size 2^(2*n) implies that all
+* available attacks are about as easy (as e.g Pollard's kangaroo
+* algorithm can compute the DL in sqrt(x) operations) while minimizing
+* the exponent size for performance reasons.
+*/
+BOTAN_PUBLIC_API(2,0) size_t dl_exponent_size(size_t prime_group_size);
+
+/**
+* Estimate work factor for integer factorization
+* @param n_bits size of modulus in bits
+* @return estimated security level for this modulus
+*/
+BOTAN_PUBLIC_API(2,0) size_t if_work_factor(size_t n_bits);
+
+/**
+* Estimate work factor for EC discrete logarithm
+* @param prime_group_size size of the group in bits
+* @return estimated security level for this group
+*/
+BOTAN_PUBLIC_API(2,0) size_t ecp_work_factor(size_t prime_group_size);
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/pubkey/x509_key.cpp b/comm/third_party/botan/src/lib/pubkey/x509_key.cpp
new file mode 100644
index 0000000000..716cb1ba4b
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/x509_key.cpp
@@ -0,0 +1,106 @@
+/*
+* X.509 Public Key
+* (C) 1999-2010 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/x509_key.h>
+#include <botan/data_src.h>
+#include <botan/ber_dec.h>
+#include <botan/pem.h>
+#include <botan/asn1_obj.h>
+#include <botan/pk_algs.h>
+
+namespace Botan {
+
+namespace X509 {
+
+std::vector<uint8_t> BER_encode(const Public_Key& key)
+ {
+ // keeping it around for compat
+ return key.subject_public_key();
+ }
+
+/*
+* PEM encode a X.509 public key
+*/
+std::string PEM_encode(const Public_Key& key)
+ {
+ return PEM_Code::encode(key.subject_public_key(),
+ "PUBLIC KEY");
+ }
+
+/*
+* Extract a public key and return it
+*/
+Public_Key* load_key(DataSource& source)
+ {
+ try {
+ AlgorithmIdentifier alg_id;
+ std::vector<uint8_t> key_bits;
+
+ if(ASN1::maybe_BER(source) && !PEM_Code::matches(source))
+ {
+ BER_Decoder(source)
+ .start_cons(SEQUENCE)
+ .decode(alg_id)
+ .decode(key_bits, BIT_STRING)
+ .end_cons();
+ }
+ else
+ {
+ DataSource_Memory ber(
+ PEM_Code::decode_check_label(source, "PUBLIC KEY")
+ );
+
+ BER_Decoder(ber)
+ .start_cons(SEQUENCE)
+ .decode(alg_id)
+ .decode(key_bits, BIT_STRING)
+ .end_cons();
+ }
+
+ if(key_bits.empty())
+ throw Decoding_Error("X.509 public key decoding");
+
+ return load_public_key(alg_id, key_bits).release();
+ }
+ catch(Decoding_Error& e)
+ {
+ throw Decoding_Error("X.509 public key decoding", e);
+ }
+ }
+
+#if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
+/*
+* Extract a public key and return it
+*/
+Public_Key* load_key(const std::string& fsname)
+ {
+ DataSource_Stream source(fsname, true);
+ return X509::load_key(source);
+ }
+#endif
+
+/*
+* Extract a public key and return it
+*/
+Public_Key* load_key(const std::vector<uint8_t>& mem)
+ {
+ DataSource_Memory source(mem);
+ return X509::load_key(source);
+ }
+
+/*
+* Make a copy of this public key
+*/
+Public_Key* copy_key(const Public_Key& key)
+ {
+ DataSource_Memory source(PEM_encode(key));
+ return X509::load_key(source);
+ }
+
+}
+
+}
diff --git a/comm/third_party/botan/src/lib/pubkey/x509_key.h b/comm/third_party/botan/src/lib/pubkey/x509_key.h
new file mode 100644
index 0000000000..58d537bbe7
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/x509_key.h
@@ -0,0 +1,80 @@
+/*
+* X.509 Public Key
+* (C) 1999-2010 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_X509_PUBLIC_KEY_H_
+#define BOTAN_X509_PUBLIC_KEY_H_
+
+#include <botan/pk_keys.h>
+#include <botan/types.h>
+#include <string>
+#include <vector>
+
+namespace Botan {
+
+class RandomNumberGenerator;
+class DataSource;
+
+/**
+* The two types of X509 encoding supported by Botan.
+* This enum is not used anymore, and will be removed in a future major release.
+*/
+enum X509_Encoding { RAW_BER, PEM };
+
+/**
+* This namespace contains functions for handling X.509 public keys
+*/
+namespace X509 {
+
+/**
+* BER encode a key
+* @param key the public key to encode
+* @return BER encoding of this key
+*/
+BOTAN_PUBLIC_API(2,0) std::vector<uint8_t> BER_encode(const Public_Key& key);
+
+/**
+* PEM encode a public key into a string.
+* @param key the key to encode
+* @return PEM encoded key
+*/
+BOTAN_PUBLIC_API(2,0) std::string PEM_encode(const Public_Key& key);
+
+/**
+* Create a public key from a data source.
+* @param source the source providing the DER or PEM encoded key
+* @return new public key object
+*/
+BOTAN_PUBLIC_API(2,0) Public_Key* load_key(DataSource& source);
+
+#if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
+/**
+* Create a public key from a file
+* @param filename pathname to the file to load
+* @return new public key object
+*/
+BOTAN_PUBLIC_API(2,0) Public_Key* load_key(const std::string& filename);
+#endif
+
+/**
+* Create a public key from a memory region.
+* @param enc the memory region containing the DER or PEM encoded key
+* @return new public key object
+*/
+BOTAN_PUBLIC_API(2,0) Public_Key* load_key(const std::vector<uint8_t>& enc);
+
+/**
+* Copy a key.
+* @param key the public key to copy
+* @return new public key object
+*/
+BOTAN_PUBLIC_API(2,0) Public_Key* copy_key(const Public_Key& key);
+
+}
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/pubkey/xmss/atomic.h b/comm/third_party/botan/src/lib/pubkey/xmss/atomic.h
new file mode 100644
index 0000000000..a542d4c005
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/xmss/atomic.h
@@ -0,0 +1,55 @@
+/*
+ * 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 <botan/types.h>
+#include <atomic>
+#include <memory>
+
+//BOTAN_FUTURE_INTERNAL_HEADER(atomic.h)
+
+namespace Botan {
+
+template <typename T>
+/**
+ * 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<T>& data) : m_data(data.load()) {}
+ ~Atomic() = default;
+
+ Atomic& operator=(const Atomic& a)
+ {
+ m_data.store(a.m_data.load());
+ return *this;
+ }
+
+ Atomic& operator=(const std::atomic<T>& a)
+ {
+ m_data.store(a.load());
+ return *this;
+ }
+
+ operator std::atomic<T>& () { return m_data; }
+ operator T() { return m_data.load(); }
+
+ private:
+ std::atomic<T> m_data;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/pubkey/xmss/info.txt b/comm/third_party/botan/src/lib/pubkey/xmss/info.txt
new file mode 100644
index 0000000000..ae290f5fc9
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/xmss/info.txt
@@ -0,0 +1,40 @@
+<defines>
+XMSS_RFC8391 -> 20201101
+</defines>
+
+<header:public>
+xmss.h
+xmss_hash.h
+xmss_wots.h
+xmss_parameters.h
+xmss_key_pair.h
+xmss_privatekey.h
+xmss_publickey.h
+xmss_wots_parameters.h
+xmss_wots_privatekey.h
+xmss_wots_publickey.h
+</header:public>
+
+<header:internal>
+atomic.h
+xmss_address.h
+xmss_common_ops.h
+xmss_index_registry.h
+xmss_signature.h
+xmss_signature_operation.h
+xmss_tools.h
+xmss_verification_operation.h
+xmss_wots_addressed_privatekey.h
+xmss_wots_addressed_publickey.h
+</header:internal>
+
+<requires>
+asn1
+rng
+hash
+sha2_32
+</requires>
+
+<os_features>
+atomics
+</os_features>
diff --git a/comm/third_party/botan/src/lib/pubkey/xmss/xmss.h b/comm/third_party/botan/src/lib/pubkey/xmss/xmss.h
new file mode 100644
index 0000000000..af8e8a41e8
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/xmss/xmss.h
@@ -0,0 +1,459 @@
+/*
+ * XMSS Keys
+ * (C) 2016,2017 Matthias Gierlings
+ * (C) 2019 René Korthaus, Rohde & Schwarz Cybersecurity
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ **/
+
+#ifndef BOTAN_XMSS_H_
+#define BOTAN_XMSS_H_
+
+#include <botan/pk_keys.h>
+#include <botan/exceptn.h>
+#include <botan/xmss_parameters.h>
+#include <botan/xmss_wots.h>
+
+namespace Botan {
+
+class RandomNumberGenerator;
+class XMSS_Verification_Operation;
+
+/**
+ * An XMSS: Extended Hash-Based Signature public key.
+ *
+ * [1] XMSS: Extended Hash-Based Signatures,
+ * Request for Comments: 8391
+ * Release: May 2018.
+ * https://datatracker.ietf.org/doc/rfc8391/
+ **/
+class BOTAN_PUBLIC_API(2,0) XMSS_PublicKey : public virtual Public_Key
+ {
+ public:
+ /**
+ * Creates a new XMSS public key for the chosen XMSS signature method.
+ * New public and prf seeds are generated using rng. The appropriate WOTS
+ * signature method will be automatically set based on the chosen XMSS
+ * signature method.
+ *
+ * @param xmss_oid Identifier for the selected XMSS signature method.
+ * @param rng A random number generator to use for key generation.
+ **/
+ XMSS_PublicKey(XMSS_Parameters::xmss_algorithm_t xmss_oid,
+ RandomNumberGenerator& rng);
+
+ /**
+ * Loads a public key.
+ *
+ * Public key must be encoded as in RFC
+ * draft-vangeest-x509-hash-sigs-03.
+ *
+ * @param key_bits DER encoded public key bits
+ */
+ XMSS_PublicKey(const std::vector<uint8_t>& key_bits);
+
+ /**
+ * Creates a new XMSS public key for a chosen XMSS signature method as
+ * well as pre-computed root node and public_seed values.
+ *
+ * @param xmss_oid Identifier for the selected XMSS signature method.
+ * @param root Root node value.
+ * @param public_seed Public seed value.
+ **/
+ XMSS_PublicKey(XMSS_Parameters::xmss_algorithm_t xmss_oid,
+ const secure_vector<uint8_t>& root,
+ const secure_vector<uint8_t>& public_seed)
+ : m_xmss_params(xmss_oid), m_wots_params(m_xmss_params.ots_oid()),
+ m_root(root), m_public_seed(public_seed) {}
+
+ /**
+ * Creates a new XMSS public key for a chosen XMSS signature method as
+ * well as pre-computed root node and public_seed values.
+ *
+ * @param xmss_oid Identifier for the selected XMSS signature method.
+ * @param root Root node value.
+ * @param public_seed Public seed value.
+ **/
+ XMSS_PublicKey(XMSS_Parameters::xmss_algorithm_t xmss_oid,
+ secure_vector<uint8_t>&& root,
+ secure_vector<uint8_t>&& 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)) {}
+
+ /**
+ * Retrieves the chosen XMSS signature method.
+ *
+ * @return XMSS signature method identifier.
+ **/
+ XMSS_Parameters::xmss_algorithm_t xmss_oid() const
+ {
+ return m_xmss_params.oid();
+ }
+
+ /**
+ * Sets the chosen XMSS signature method
+ **/
+ void set_xmss_oid(XMSS_Parameters::xmss_algorithm_t xmss_oid)
+ {
+ m_xmss_params = XMSS_Parameters(xmss_oid);
+ m_wots_params = XMSS_WOTS_Parameters(m_xmss_params.ots_oid());
+ }
+
+ /**
+ * Retrieves the XMSS parameters determined by the chosen XMSS Signature
+ * method.
+ *
+ * @return XMSS parameters.
+ **/
+ const XMSS_Parameters& xmss_parameters() const
+ {
+ return m_xmss_params;
+ }
+
+ /**
+ * Retrieves the XMSS parameters determined by the chosen XMSS Signature
+ * method.
+ *
+ * @return XMSS parameters.
+ **/
+ std::string xmss_hash_function() const
+ {
+ return m_xmss_params.hash_function_name();
+ }
+
+ /**
+ * Retrieves the Winternitz One Time Signature (WOTS) method,
+ * corresponding to the chosen XMSS signature method.
+ *
+ * @return XMSS WOTS signature method identifier.
+ **/
+ XMSS_WOTS_Parameters::ots_algorithm_t wots_oid() const
+ {
+ return m_wots_params.oid();
+ }
+
+ /**
+ * Retrieves the Winternitz One Time Signature (WOTS) parameters
+ * corresponding to the chosen XMSS signature method.
+ *
+ * @return XMSS WOTS signature method parameters.
+ **/
+ const XMSS_WOTS_Parameters& wots_parameters() const
+ {
+ return m_wots_params;
+ }
+
+ secure_vector<uint8_t>& root()
+ {
+ return m_root;
+ }
+
+ void set_root(const secure_vector<uint8_t>& root)
+ {
+ m_root = root;
+ }
+
+ void set_root(secure_vector<uint8_t>&& root)
+ {
+ m_root = std::move(root);
+ }
+
+ const secure_vector<uint8_t>& root() const
+ {
+ return m_root;
+ }
+
+ virtual secure_vector<uint8_t>& public_seed()
+ {
+ return m_public_seed;
+ }
+
+ virtual void set_public_seed(const secure_vector<uint8_t>& public_seed)
+ {
+ m_public_seed = public_seed;
+ }
+
+ virtual void set_public_seed(secure_vector<uint8_t>&& public_seed)
+ {
+ m_public_seed = std::move(public_seed);
+ }
+
+ virtual const secure_vector<uint8_t>& public_seed() const
+ {
+ return m_public_seed;
+ }
+
+ std::string algo_name() const override
+ {
+ return "XMSS";
+ }
+
+ AlgorithmIdentifier algorithm_identifier() const override
+ {
+ return AlgorithmIdentifier(get_oid(), AlgorithmIdentifier::USE_EMPTY_PARAM);
+ }
+
+ bool check_key(RandomNumberGenerator&, bool) const override
+ {
+ return true;
+ }
+
+ std::unique_ptr<PK_Ops::Verification>
+ create_verification_op(const std::string&,
+ const std::string& provider) const override;
+
+ size_t estimated_strength() const override
+ {
+ return m_xmss_params.estimated_strength();
+ }
+
+ size_t key_length() const override
+ {
+ return m_xmss_params.estimated_strength();
+ }
+
+ /**
+ * Returns the encoded public key as defined in RFC
+ * draft-vangeest-x509-hash-sigs-03.
+ *
+ * @return encoded public key bits
+ **/
+ std::vector<uint8_t> public_key_bits() const override;
+
+ /**
+ * Size in bytes of the serialized XMSS public key produced by
+ * raw_public_key().
+ *
+ * @return size in bytes of serialized Public Key.
+ **/
+ virtual size_t size() const
+ {
+ return sizeof(uint32_t) + 2 * m_xmss_params.element_size();
+ }
+
+ /**
+ * Generates a byte sequence representing the XMSS
+ * public key, as defined in [1] (p. 23, "XMSS Public Key")
+ *
+ * @return 4-byte OID, followed by n-byte root node, followed by
+ * public seed.
+ **/
+ virtual std::vector<uint8_t> raw_public_key() const;
+
+ protected:
+ std::vector<uint8_t> m_raw_key;
+ XMSS_Parameters m_xmss_params;
+ XMSS_WOTS_Parameters m_wots_params;
+ secure_vector<uint8_t> m_root;
+ secure_vector<uint8_t> m_public_seed;
+
+ private:
+ XMSS_Parameters::xmss_algorithm_t deserialize_xmss_oid(
+ const std::vector<uint8_t>& raw_key);
+ };
+
+template<typename> class Atomic;
+
+class XMSS_Index_Registry;
+
+/**
+ * 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.
+ *
+ * [1] XMSS: Extended Hash-Based Signatures,
+ * Request for Comments: 8391
+ * Release: May 2018.
+ * https://datatracker.ietf.org/doc/rfc8391/
+ **/
+class BOTAN_PUBLIC_API(2,0) XMSS_PrivateKey final : public virtual XMSS_PublicKey,
+ public virtual Private_Key
+ {
+ public:
+ /**
+ * Creates a new XMSS private key for the chosen XMSS signature method.
+ * New seeds for public/private key and pseudo random function input are
+ * generated using the provided RNG. The appropriate WOTS signature method
+ * will be automatically set based on the chosen XMSS signature method.
+ *
+ * @param xmss_algo_id Identifier for the selected XMSS signature method.
+ * @param rng A random number generator to use for key generation.
+ **/
+ XMSS_PrivateKey(XMSS_Parameters::xmss_algorithm_t xmss_algo_id,
+ RandomNumberGenerator& rng);
+
+ /**
+ * Creates an XMSS_PrivateKey from a byte sequence produced by
+ * raw_private_key().
+ *
+ * @param raw_key An XMSS private key serialized using raw_private_key().
+ **/
+ XMSS_PrivateKey(const secure_vector<uint8_t>& raw_key);
+
+ /**
+ * Creates a new XMSS private key for the chosen XMSS signature method
+ * using precomputed seeds for public/private keys and pseudo random
+ * function input. The appropriate WOTS signature method will be
+ * automatically set, based on the chosen XMSS signature method.
+ *
+ * @param xmss_algo_id Identifier for the selected XMSS signature method.
+ * @param idx_leaf Index of the next unused leaf.
+ * @param wots_priv_seed A seed to generate a Winternitz-One-Time-
+ * Signature private key from.
+ * @param prf a secret n-byte key sourced from a secure source
+ * of uniformly random data.
+ * @param root Root node of the binary hash tree.
+ * @param public_seed The public seed.
+ **/
+ XMSS_PrivateKey(XMSS_Parameters::xmss_algorithm_t xmss_algo_id,
+ size_t idx_leaf,
+ const secure_vector<uint8_t>& wots_priv_seed,
+ const secure_vector<uint8_t>& prf,
+ const secure_vector<uint8_t>& root,
+ const secure_vector<uint8_t>& public_seed);
+
+ bool stateful_operation() const override { return true; }
+
+ /**
+ * Retrieves the last unused leaf index of the private key. Reusing a leaf
+ * by utilizing leaf indices lower than the last unused leaf index will
+ * compromise security.
+ *
+ * @return Index of the last unused leaf.
+ **/
+ size_t unused_leaf_index() const;
+
+ /**
+ * Sets the last unused leaf index of the private key. The leaf index
+ * will be updated automatically during every signing operation, and
+ * should not be set manually.
+ *
+ * @param idx Index of the last unused leaf.
+ **/
+ void set_unused_leaf_index(size_t idx);
+
+ size_t reserve_unused_leaf_index();
+
+ /**
+ * Winternitz One Time Signature Scheme key utilized for signing
+ * operations.
+ *
+ * @return WOTS+ private key.
+ **/
+ const XMSS_WOTS_PrivateKey& wots_private_key() const
+ {
+ return m_wots_priv_key;
+ }
+
+ /**
+ * Winternitz One Time Signature Scheme key utilized for signing
+ * operations.
+ *
+ * @return WOTS+ private key.
+ **/
+ XMSS_WOTS_PrivateKey& wots_private_key()
+ {
+ return m_wots_priv_key;
+ }
+
+ const secure_vector<uint8_t>& prf() const
+ {
+ return m_prf;
+ }
+
+ secure_vector<uint8_t>& prf()
+ {
+ return m_prf;
+ }
+
+ void set_public_seed(
+ const secure_vector<uint8_t>& public_seed) override
+ {
+ m_public_seed = public_seed;
+ m_wots_priv_key.set_public_seed(public_seed);
+ }
+
+ void set_public_seed(secure_vector<uint8_t>&& public_seed) override
+ {
+ m_public_seed = std::move(public_seed);
+ m_wots_priv_key.set_public_seed(m_public_seed);
+ }
+
+ const secure_vector<uint8_t>& public_seed() const override
+ {
+ return m_public_seed;
+ }
+
+ std::unique_ptr<PK_Ops::Signature>
+ create_signature_op(RandomNumberGenerator&,
+ const std::string&,
+ const std::string& provider) const override;
+
+ secure_vector<uint8_t> private_key_bits() const override;
+
+ size_t size() const override
+ {
+ return XMSS_PublicKey::size() +
+ sizeof(uint32_t) +
+ 2 * XMSS_PublicKey::m_xmss_params.element_size();
+ }
+
+ /**
+ * Generates a non standartized 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.
+ **/
+ secure_vector<uint8_t> raw_private_key() const;
+ /**
+ * Algorithm 9: "treeHash"
+ * Computes the internal n-byte nodes of a Merkle tree.
+ *
+ * @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.
+ *
+ * @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<uint8_t> tree_hash(
+ size_t start_idx,
+ size_t target_node_height,
+ XMSS_Address& adrs);
+
+ private:
+ /**
+ * Fetches shared unused leaf index from the index registry
+ **/
+ std::shared_ptr<Atomic<size_t>> recover_global_leaf_index() const;
+
+ inline void tree_hash_subtree(secure_vector<uint8_t>& 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_hash);
+ }
+
+
+ /**
+ * Helper for multithreaded tree hashing.
+ */
+ void tree_hash_subtree(secure_vector<uint8_t>& result,
+ size_t start_idx,
+ size_t target_node_height,
+ XMSS_Address& adrs,
+ XMSS_Hash& hash);
+
+ XMSS_WOTS_PrivateKey m_wots_priv_key;
+ XMSS_Hash m_hash;
+ secure_vector<uint8_t> m_prf;
+ XMSS_Index_Registry& m_index_reg;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/pubkey/xmss/xmss_address.h b/comm/third_party/botan/src/lib/pubkey/xmss/xmss_address.h
new file mode 100644
index 0000000000..05a78f3b88
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/xmss/xmss_address.h
@@ -0,0 +1,405 @@
+/*
+ * XMSS Address
+ * (C) 2016 Matthias Gierlings
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ **/
+
+#ifndef BOTAN_XMSS_ADDRESS_H_
+#define BOTAN_XMSS_ADDRESS_H_
+
+#include <botan/types.h>
+
+namespace Botan {
+
+/**
+ * Generic XMSS Address type holding 256 Bits of data. Properties
+ * of all three address formats L-Tree-Address, Hash-Tree-Address,
+ * OTS-Hash-Address can be called depending on the type currently
+ * assigned to the XMSS address using set_type().
+ **/
+class XMSS_Address final
+ {
+ public:
+ /**
+ * Distinct types an XMSS_Address can represent. The available types
+ * are specified in [1] - 2.5 Hash Function Address Scheme.
+ **/
+ enum class Type : uint8_t
+ {
+ None = 255,
+ OTS_Hash_Address = 0,
+ LTree_Address = 1,
+ Hash_Tree_Address = 2
+ };
+
+ /**
+ * The available modes for an XMSS Address:
+ * - Key_Mode: Used to generate the key.
+ * - Mask_Mode: Sets the n-byte bitmask (OTS-Hash-Address)
+ * - Mask_MSB_Mode: Used to generate the b most significant bytes of
+ * the 2n-byte bitmask (LTree Address and Hash Tree Address).
+ * - Mask_LSB_Mode: Used to generated the b least significant bytes
+ * of the 2n-byte bitmask. (LTree Address and Hash Tree Address).
+ **/
+ enum class Key_Mask : uint8_t
+ {
+ Key_Mode = 0,
+ Mask_Mode = 1,
+ Mask_MSB_Mode = 1,
+ Mask_LSB_Mode = 2
+ };
+
+ /**
+ * Layer Address for XMSS is constantly zero and can not be changed this
+ * property is only of relevance to XMSS_MT.
+ *
+ * @return Layer address, which is constant 0 for XMSS.
+ **/
+ uint8_t get_layer_addr() const { return 0; }
+
+ /**
+ * Layer Address for XMSS is constantly zero and can not be changed this
+ * property is only of relevance to XMSS_MT. Calling this method for
+ * XMSS will result in an error.
+ **/
+ void set_layer_addr()
+ {
+ BOTAN_ASSERT(false, "Only available in XMSS_MT.");
+ }
+
+ /**
+ * Tree Address for XMSS is constantly zero and can not be changed this
+ * property is only of relevance to XMSS_MT.
+ *
+ * @return Tree address, which is constant 0 for XMSS.
+ **/
+ uint64_t get_tree_addr() const { return 0; }
+
+ /**
+ * Tree Address for XMSS is constantly zero and can not be changed this
+ * property is only of relevance to XMSS_MT. Calling this method for
+ * XMSS will result in an error.
+ **/
+ void set_tree_addr()
+ {
+ BOTAN_ASSERT(false, "Only available in XMSS_MT.");
+ }
+
+ /**
+ * retrieves the logical type currently assigned to the XMSS Address
+ * instance.
+ *
+ * @return Type of the address (OTS_Hash_Address, LTree_Address or
+ * Hash_Tree_Address)
+ **/
+ Type get_type() const
+ {
+ return static_cast<Type>(m_data[15]);
+ }
+
+ /**
+ * Changes the logical type currently assigned to the XMSS Address
+ * instance. Please note that changing the type will automatically
+ * reset the 128 LSBs of the Address to zero. This affects the
+ * key_mask_mode property as well as all properties identified by
+ * XMSS_Address::Property.
+ *
+ * @param type Type that shall be assigned to the address
+ * (OTS_Hash_Address, LTree_Address or Hash_Tree_Address)
+ **/
+ void set_type(Type type)
+ {
+ m_data[15] = static_cast<uint8_t>(type);
+ std::fill(m_data.begin() + 16, m_data.end(), static_cast<uint8_t>(0));
+ }
+
+ /**
+ * Retrieves the mode the address os currently set to. (See
+ * XMSS_Address::Key_Mask for details.)
+ *
+ * @return currently active mode
+ **/
+ Key_Mask get_key_mask_mode() const
+ {
+ return Key_Mask(m_data[31]);
+ }
+
+ /**
+ * Changes the mode the address currently used address mode.
+ * (XMSS_Address::Key_Mask for details.)
+ *
+ * @param value Target mode.
+ **/
+ void set_key_mask_mode(Key_Mask value)
+ {
+ BOTAN_ASSERT(value != Key_Mask::Mask_LSB_Mode ||
+ get_type() != Type::OTS_Hash_Address,
+ "Invalid Key_Mask for current XMSS_Address::Type.");
+ m_data[31] = static_cast<uint8_t>(value);
+ }
+
+ /**
+ * Retrieve the index of the OTS key pair within the tree. A call to
+ * this method is only valid, if the address type is set to
+ * Type::OTS_Hash_Address.
+ *
+ * @return index of OTS key pair.
+ **/
+ uint32_t get_ots_address() const
+ {
+ BOTAN_ASSERT(get_type() == Type::OTS_Hash_Address,
+ "get_ots_address() requires XMSS_Address::Type::"
+ "OTS_Hash_Address.");
+ return get_hi32(2);
+ }
+
+ /**
+ * Sets the index of the OTS key pair within the tree. A call to this
+ * method is only valid, if the address type is set to
+ * Type::OTS_Hash_Address.
+ *
+ * @param value index of OTS key pair.
+ **/
+ void set_ots_address(uint32_t value)
+ {
+ BOTAN_ASSERT(get_type() == Type::OTS_Hash_Address,
+ "set_ots_address() requires XMSS_Address::Type::"
+ "OTS_Hash_Address.");
+ set_hi32(2, value);
+ }
+
+ /**
+ * Retrieves the index of the leaf computed with this LTree. A call to
+ * this method is only valid, if the address type is set to
+ * Type::LTree_Address.
+ *
+ * @return index of the leaf.
+ **/
+ uint32_t get_ltree_address() const
+ {
+ BOTAN_ASSERT(get_type() == Type::LTree_Address,
+ "set_ltree_address() requires XMSS_Address::Type::"
+ "LTree_Address.");
+ return get_hi32(2);
+ }
+
+ /**
+ * Sets the index of the leaf computed with this LTree. A call to this
+ * method is only valid, if the address type is set to
+ * Type::LTree_Address.
+ *
+ * @param value index of the leaf.
+ **/
+ void set_ltree_address(uint32_t value)
+ {
+ BOTAN_ASSERT(get_type() == Type::LTree_Address,
+ "set_ltree_address() requires XMSS_Address::Type::"
+ "LTree_Address.");
+ set_hi32(2, value);
+ }
+
+ /**
+ * Retrieve the chain address. A call to this method is only valid, if
+ * the address type is set to Type::OTS_Hash_Address.
+ *
+ * @return chain address.
+ **/
+ uint32_t get_chain_address() const
+ {
+ BOTAN_ASSERT(get_type() == Type::OTS_Hash_Address,
+ "get_chain_address() requires XMSS_Address::Type::"
+ "OTS_Hash_Address.");
+ return get_lo32(2);
+ }
+
+ /**
+ * Set the chain address. A call to this method is only valid, if
+ * the address type is set to Type::OTS_Hash_Address.
+ **/
+ void set_chain_address(uint32_t value)
+ {
+ BOTAN_ASSERT(get_type() == Type::OTS_Hash_Address,
+ "set_chain_address() requires XMSS_Address::Type::"
+ "OTS_Hash_Address.");
+ set_lo32(2, value);
+ }
+
+ /**
+ * Retrieves the height of the tree node to be computed within the
+ * tree. A call to this method is only valid, if the address type is
+ * set to Type::LTree_Address or Type::Hash_Tree_Address.
+ *
+ * @return height of the tree node.
+ **/
+ uint32_t get_tree_height() const
+ {
+ BOTAN_ASSERT(get_type() == Type::LTree_Address ||
+ get_type() == Type::Hash_Tree_Address,
+ "get_tree_height() requires XMSS_Address::Type::"
+ "LTree_Address or XMSS_Address::Type::Hash_Tree_Address.");
+ return get_lo32(2);
+ }
+
+ /**
+ * Sets the height of the tree node to be computed within the
+ * tree. A call to this method is only valid, if the address type is
+ * set to Type::LTree_Address or Type::Hash_Tree_Address.
+ *
+ * @param value height of the tree node.
+ **/
+ void set_tree_height(uint32_t value)
+ {
+ BOTAN_ASSERT(get_type() == Type::LTree_Address ||
+ get_type() == Type::Hash_Tree_Address,
+ "set_tree_height() requires XMSS_Address::Type::"
+ "LTree_Address or XMSS_Address::Type::Hash_Tree_Address.");
+ set_lo32(2, value);
+ }
+
+ /**
+ * Retrieves the address of the hash function call within the chain.
+ * A call to this method is only valid, if the address type is
+ * set to Type::OTS_Hash_Address.
+ *
+ * @return address of the hash function call within chain.
+ **/
+ uint32_t get_hash_address() const
+ {
+ BOTAN_ASSERT(get_type() == Type::OTS_Hash_Address,
+ "get_hash_address() requires XMSS_Address::Type::"
+ "OTS_Hash_Address.");
+ return get_hi32(3);
+ }
+
+ /**
+ * Sets the address of the hash function call within the chain.
+ * A call to this method is only valid, if the address type is
+ * set to Type::OTS_Hash_Address.
+ *
+ * @param value address of the hash function call within chain.
+ **/
+ void set_hash_address(uint32_t value)
+ {
+ BOTAN_ASSERT(get_type() == Type::OTS_Hash_Address,
+ "set_hash_address() requires XMSS_Address::Type::"
+ "OTS_Hash_Address.");
+ set_hi32(3, value);
+ }
+
+ /**
+ * Retrieves the index of the tree node at current tree height in the
+ * tree. A call to this method is only valid, if the address type is
+ * set to Type::LTree_Address or Type::Hash_Tree_Address.
+ *
+ * @return index of the tree node at current height.
+ **/
+ uint32_t get_tree_index() const
+ {
+ BOTAN_ASSERT(get_type() == Type::LTree_Address ||
+ get_type() == Type::Hash_Tree_Address,
+ "get_tree_index() requires XMSS_Address::Type::"
+ "LTree_Address or XMSS_Address::Type::Hash_Tree_Address.");
+ return get_hi32(3);
+ }
+
+ /**
+ * Sets the index of the tree node at current tree height in the
+ * tree. A call to this method is only valid, if the address type is
+ * set to Type::LTree_Address or Type::Hash_Tree_Address.
+ *
+ * @param value index of the tree node at current height.
+ **/
+ void set_tree_index(uint32_t value)
+ {
+ BOTAN_ASSERT(get_type() == Type::LTree_Address ||
+ get_type() == Type::Hash_Tree_Address,
+ "set_tree_index() requires XMSS_Address::Type::"
+ "LTree_Address or XMSS_Address::Type::Hash_Tree_Address.");
+ set_hi32(3, value);
+ }
+
+ const secure_vector<uint8_t>& bytes() const
+ {
+ return m_data;
+ }
+
+ secure_vector<uint8_t>& bytes()
+ {
+ return 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(Type type)
+ : m_data(m_address_size)
+ {
+ set_type(type);
+ }
+
+ XMSS_Address(const secure_vector<uint8_t>& data) : m_data(data)
+ {
+ BOTAN_ASSERT(m_data.size() == m_address_size,
+ "XMSS_Address must be of 256 bits size.");
+ }
+
+ XMSS_Address(secure_vector<uint8_t>&& data) : m_data(std::move(data))
+ {
+ BOTAN_ASSERT(m_data.size() == m_address_size,
+ "XMSS_Address must be of 256 bits size.");
+ }
+
+ protected:
+ secure_vector<uint8_t> m_data;
+
+ 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);
+ }
+
+ inline void set_hi32(size_t offset, uint32_t value)
+ {
+ m_data[offset * 8 ] = ((value >> 24) & 0xFF);
+ m_data[offset * 8 + 1] = ((value >> 16) & 0xFF);
+ m_data[offset * 8 + 2] = ((value >> 8) & 0xFF);
+ m_data[offset * 8 + 3] = ((value ) & 0xFF);
+ }
+
+ inline uint32_t get_lo32(size_t offset) const
+ {
+ return ((0x000000FF & m_data[8 * offset + 7]) |
+ (0x000000FF & m_data[8 * offset + 6]) << 8 |
+ (0x000000FF & m_data[8 * offset + 5]) << 16 |
+ (0x000000FF & m_data[8 * offset + 4]) << 24);
+ }
+
+ inline void set_lo32(size_t offset, uint32_t value)
+ {
+ m_data[offset * 8 + 4] = ((value >> 24) & 0xFF);
+ m_data[offset * 8 + 5] = ((value >> 16) & 0xFF);
+ m_data[offset * 8 + 6] = ((value >> 8) & 0xFF);
+ m_data[offset * 8 + 7] = ((value ) & 0xFF);
+ }
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/pubkey/xmss/xmss_common_ops.cpp b/comm/third_party/botan/src/lib/pubkey/xmss/xmss_common_ops.cpp
new file mode 100644
index 0000000000..9a3fe085ab
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/xmss/xmss_common_ops.cpp
@@ -0,0 +1,74 @@
+/*
+ * XMSS Common Ops
+ * Operations shared by XMSS signature generation and verification operations.
+ * (C) 2016,2017 Matthias Gierlings
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ **/
+
+#include <botan/internal/xmss_common_ops.h>
+
+namespace Botan {
+
+void
+XMSS_Common_Ops::randomize_tree_hash(secure_vector<uint8_t>& result,
+ const secure_vector<uint8_t>& left,
+ const secure_vector<uint8_t>& right,
+ XMSS_Address& adrs,
+ const secure_vector<uint8_t>& seed,
+ XMSS_Hash& hash,
+ const XMSS_Parameters& params)
+ {
+ adrs.set_key_mask_mode(XMSS_Address::Key_Mask::Key_Mode);
+ secure_vector<uint8_t> key { hash.prf(seed, adrs.bytes()) };
+
+ adrs.set_key_mask_mode(XMSS_Address::Key_Mask::Mask_MSB_Mode);
+ secure_vector<uint8_t> bitmask_l { hash.prf(seed, adrs.bytes()) };
+
+ adrs.set_key_mask_mode(XMSS_Address::Key_Mask::Mask_LSB_Mode);
+ secure_vector<uint8_t> bitmask_r { hash.prf(seed, adrs.bytes()) };
+
+ BOTAN_ASSERT(bitmask_l.size() == left.size() &&
+ bitmask_r.size() == right.size(),
+ "Bitmask size doesn't match node size.");
+
+ secure_vector<uint8_t> concat_xor(params.element_size() * 2);
+ for(size_t i = 0; i < left.size(); i++)
+ {
+ concat_xor[i] = left[i] ^ bitmask_l[i];
+ concat_xor[i + left.size()] = right[i] ^ bitmask_r[i];
+ }
+
+ hash.h(result, key, concat_xor);
+ }
+
+
+void
+XMSS_Common_Ops::create_l_tree(secure_vector<uint8_t>& result,
+ wots_keysig_t pk,
+ XMSS_Address& adrs,
+ const secure_vector<uint8_t>& seed,
+ XMSS_Hash& hash,
+ const XMSS_Parameters& params)
+ {
+ size_t l = params.len();
+ adrs.set_tree_height(0);
+
+ while(l > 1)
+ {
+ for(size_t i = 0; i < l >> 1; i++)
+ {
+ adrs.set_tree_index(static_cast<uint32_t>(i));
+ randomize_tree_hash(pk[i], pk[2 * i], pk[2 * i + 1], adrs, seed, hash, params);
+ }
+ if(l & 0x01)
+ {
+ pk[l >> 1] = pk[l - 1];
+ }
+ l = (l >> 1) + (l & 0x01);
+ adrs.set_tree_height(adrs.get_tree_height() + 1);
+ }
+ result = pk[0];
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/pubkey/xmss/xmss_common_ops.h b/comm/third_party/botan/src/lib/pubkey/xmss/xmss_common_ops.h
new file mode 100644
index 0000000000..77fdc9dc1e
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/xmss/xmss_common_ops.h
@@ -0,0 +1,83 @@
+/*
+ * XMSS Common Ops
+ * (C) 2016,2017 Matthias Gierlings
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ **/
+
+#ifndef BOTAN_XMSS_COMMON_OPS_H_
+#define BOTAN_XMSS_COMMON_OPS_H_
+
+#include <vector>
+#include <botan/secmem.h>
+#include <botan/xmss_parameters.h>
+#include <botan/internal/xmss_address.h>
+#include <botan/xmss_hash.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(xmss_common_ops.h)
+
+namespace Botan {
+
+typedef std::vector<secure_vector<uint8_t>> wots_keysig_t;
+
+/**
+ * Operations shared by XMSS signature generation and verification operations.
+ **/
+class XMSS_Common_Ops
+ {
+ public:
+ /**
+ * Algorithm 7: "RAND_HASH"
+ *
+ * Generates a randomized hash.
+ *
+ * This overload is used in multithreaded scenarios, where it is
+ * required to provide seperate 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] seed The seed for G.
+ * @param[in] hash Instance of XMSS_Hash, that may only by the thead
+ * executing generate_public_key.
+ * @param[in] params
+ **/
+ static void randomize_tree_hash(
+ secure_vector<uint8_t>& result,
+ const secure_vector<uint8_t>& left,
+ const secure_vector<uint8_t>& right,
+ XMSS_Address& adrs,
+ const secure_vector<uint8_t>& seed,
+ XMSS_Hash& hash,
+ const XMSS_Parameters& params);
+
+ /**
+ * Algorithm 8: "ltree"
+ * Create an L-tree used to compute the leaves of the binary hash tree.
+ * 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.
+ *
+ * @param[out] result Public key compressed to a single n-byte value
+ * pk[0].
+ * @param[in] pk Winternitz One Time Signatures+ public key.
+ * @param[in] adrs Address encoding the address of the L-Tree
+ * @param[in] seed The seed generated during the public key generation.
+ * @param[in] hash Instance of XMSS_Hash, that may only be used by the
+ * thead executing create_l_tree.
+ * @param[in] params
+ **/
+ static void create_l_tree(secure_vector<uint8_t>& result,
+ wots_keysig_t pk,
+ XMSS_Address& adrs,
+ const secure_vector<uint8_t>& seed,
+ XMSS_Hash& hash,
+ const XMSS_Parameters& params);
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/pubkey/xmss/xmss_hash.cpp b/comm/third_party/botan/src/lib/pubkey/xmss/xmss_hash.cpp
new file mode 100644
index 0000000000..cd714873ca
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/xmss/xmss_hash.cpp
@@ -0,0 +1,80 @@
+/*
+ * XMSS Hash
+ * A collection of pseudorandom hash functions required for XMSS and WOTS
+ * computations.
+ * (C) 2016,2017 Matthias Gierlings
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ **/
+
+#include <botan/xmss_hash.h>
+#include <botan/exceptn.h>
+
+namespace Botan {
+
+XMSS_Hash::XMSS_Hash(const XMSS_Hash& hash)
+ : XMSS_Hash(hash.m_hash_func_name)
+ {
+ }
+
+XMSS_Hash::XMSS_Hash(const std::string& h_func_name) :
+ m_hash(HashFunction::create(h_func_name)),
+ m_hash_func_name(h_func_name)
+ {
+ if(!m_hash)
+ throw Lookup_Error("XMSS cannot use hash " + h_func_name +
+ " because it is unavailable");
+
+ m_output_length = m_hash->output_length();
+ BOTAN_ASSERT(m_output_length > 0, "Hash output length of zero is invalid.");
+
+ m_zero_padding.resize(m_output_length - 1);
+ m_msg_hash.reset(m_hash->clone());
+ }
+
+void
+XMSS_Hash::h(secure_vector<uint8_t>& result,
+ const secure_vector<uint8_t>& key,
+ const secure_vector<uint8_t>& data)
+ {
+ m_hash->update(m_zero_padding);
+ m_hash->update(m_id_h);
+ m_hash->update(key);
+ m_hash->update(data);
+ m_hash->final(result);
+ }
+
+void XMSS_Hash::h_msg_init(const secure_vector<uint8_t>& randomness,
+ const secure_vector<uint8_t>& root,
+ const secure_vector<uint8_t>& index_bytes)
+ {
+ m_msg_hash->clear();
+ m_msg_hash->update(m_zero_padding);
+ m_msg_hash->update(m_id_hmsg);
+ m_msg_hash->update(randomness);
+ m_msg_hash->update(root);
+ m_msg_hash->update(index_bytes);
+ }
+
+void XMSS_Hash::h_msg_update(const uint8_t data[], size_t size)
+ {
+ m_msg_hash->update(data, size);
+ }
+
+secure_vector<uint8_t> XMSS_Hash::h_msg_final()
+ {
+ return m_msg_hash->final();
+ }
+
+secure_vector<uint8_t>
+XMSS_Hash::h_msg(const secure_vector<uint8_t>& randomness,
+ const secure_vector<uint8_t>& root,
+ const secure_vector<uint8_t>& index_bytes,
+ const secure_vector<uint8_t>& data)
+ {
+ h_msg_init(randomness, root, index_bytes);
+ m_msg_hash->update(data);
+ return m_msg_hash->final();
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/pubkey/xmss/xmss_hash.h b/comm/third_party/botan/src/lib/pubkey/xmss/xmss_hash.h
new file mode 100644
index 0000000000..5d8cbab538
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/xmss/xmss_hash.h
@@ -0,0 +1,156 @@
+/*
+ * XMSS Hash
+ * (C) 2016,2017 Matthias Gierlings
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ **/
+
+#ifndef BOTAN_XMSS_HASH_H_
+#define BOTAN_XMSS_HASH_H_
+
+#include <botan/hash.h>
+
+//BOTAN_FUTURE_INTERNAL_HEADER(xmss_hash.h)
+
+namespace Botan {
+
+/**
+ * A collection of pseudorandom hash functions required for XMSS and WOTS
+ * computations.
+ **/
+class XMSS_Hash final
+ {
+ public:
+ XMSS_Hash(const std::string& h_func_name);
+ XMSS_Hash(const XMSS_Hash& hash);
+
+ /**
+ * Pseudoranom function creating a hash out of a key and data using
+ * a cryptographic hash function.
+ *
+ * @param[out] result The hash calculated using key and data.
+ * @param[in] key An n-byte key value.
+ * @param[in] data A 32-byte XMSS_Address data value
+ **/
+ inline void prf(secure_vector<uint8_t>& result,
+ const secure_vector<uint8_t>& key,
+ const secure_vector<uint8_t>& data)
+ {
+ m_hash->update(m_zero_padding);
+ m_hash->update(m_id_prf);
+ m_hash->update(key);
+ m_hash->update(data);
+ m_hash->final(result);
+ }
+
+ /**
+ * Pseudoranom function creating a hash out of a key and data using
+ * a cryptographic hash function.
+ *
+ * @param[in] key An n-byte key value.
+ * @param[in] data A 32-byte XMSS_Address data value
+ * @return result The hash calculated using key and data.
+ **/
+ inline secure_vector<uint8_t> prf(const secure_vector<uint8_t>& key,
+ const secure_vector<uint8_t>& data)
+ {
+ m_hash->update(m_zero_padding);
+ m_hash->update(m_id_prf);
+ m_hash->update(key);
+ m_hash->update(data);
+ return m_hash->final();
+ }
+
+ /**
+ * F is a keyed cryptographic hash function used by the WOTS+ algorithm.
+ *
+ * @param[out] result The hash calculated using key and data.
+ * @param[in] key key of length n bytes.
+ * @param[in] data string of arbitrary length.
+ **/
+ void f(secure_vector<uint8_t>& result,
+ const secure_vector<uint8_t>& key,
+ const secure_vector<uint8_t>& data)
+ {
+ m_hash->update(m_zero_padding);
+ m_hash->update(m_id_f);
+ m_hash->update(key);
+ m_hash->update(data);
+ m_hash->final(result);
+ }
+
+ /**
+ * Cryptographic hash function h accepting n byte keys and 2n byte
+ * strings of data.
+ *
+ * @param[out] result The hash calculated using key and data.
+ * @param[in] key key of length n bytes.
+ * @param[in] data string of 2n bytes length.
+ **/
+ void h(secure_vector<uint8_t>& result,
+ const secure_vector<uint8_t>& key,
+ const secure_vector<uint8_t>& data);
+
+ /**
+ * Cryptographic hash function h accepting 3n byte keys and data
+ * strings of arbitrary length.
+ *
+ * @param randomness n-byte value.
+ * @param root n-byte root node.
+ * @param index_bytes Index value padded with leading zeros.
+ * @param data string of arbitrary length.
+ *
+ * @return hash value of n-bytes length.
+ **/
+ secure_vector<uint8_t> h_msg(const secure_vector<uint8_t>& randomness,
+ const secure_vector<uint8_t>& root,
+ const secure_vector<uint8_t>& index_bytes,
+ const secure_vector<uint8_t>& data);
+
+ /**
+ * Initializes buffered h_msg computation with prefix data.
+ *
+ * @param randomness random n-byte value.
+ * @param root n-byte root node.
+ * @param index_bytes Index value padded with leading zeros.
+ **/
+ void h_msg_init(const secure_vector<uint8_t>& randomness,
+ const secure_vector<uint8_t>& root,
+ const secure_vector<uint8_t>& index_bytes);
+
+ /**
+ * Adds a message block to buffered h_msg computation.
+ *
+ * @param data A message block
+ * @param size Length of the message block in bytes.
+ **/
+ void h_msg_update(const uint8_t data[], size_t size);
+
+ /**
+ * Finalizes buffered h_msg computation and retrieves the result.
+ *
+ * @return Hash calculated using the prefix set by h_msg_init() and
+ * message blocks provided through calls to h_msg_update().
+ **/
+ secure_vector<uint8_t> h_msg_final();
+
+ size_t output_length() const { return m_output_length; }
+
+ private:
+ static const uint8_t m_id_f = 0x00;
+ static const uint8_t m_id_h = 0x01;
+ static const uint8_t m_id_hmsg = 0x02;
+ static const uint8_t m_id_prf = 0x03;
+
+ std::unique_ptr<HashFunction> m_hash;
+ std::unique_ptr<HashFunction> m_msg_hash;
+ //32 byte id prefixes prepended to the hash input.
+ std::vector<uint8_t> m_zero_padding;
+ size_t m_output_length;
+ const std::string m_hash_func_name;
+
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/pubkey/xmss/xmss_index_registry.cpp b/comm/third_party/botan/src/lib/pubkey/xmss/xmss_index_registry.cpp
new file mode 100644
index 0000000000..8709d8026f
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/xmss/xmss_index_registry.cpp
@@ -0,0 +1,84 @@
+/*
+ * 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 <botan/internal/xmss_index_registry.h>
+#include <botan/hash.h>
+#include <limits>
+
+namespace Botan {
+
+const std::string XMSS_Index_Registry::m_index_hash_function = "SHA-256";
+
+uint64_t XMSS_Index_Registry::make_key_id(
+ const secure_vector<uint8_t>& private_seed,
+ const secure_vector<uint8_t>& prf) const
+ {
+ std::unique_ptr<HashFunction> 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<uint8_t> 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<Atomic<size_t>>
+XMSS_Index_Registry::get(const secure_vector<uint8_t>& private_seed,
+ const secure_vector<uint8_t>& prf)
+ {
+ size_t pos = get(make_key_id(private_seed, prf));
+
+ if(pos < std::numeric_limits<size_t>::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<size_t>::max();
+ }
+
+size_t XMSS_Index_Registry::add(uint64_t id, size_t last_unused)
+ {
+ lock_guard_type<mutex_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<Atomic<size_t>>(last_unused);
+ }
+ return pos;
+ }
+
+ m_key_ids.push_back(id);
+ m_leaf_indices.push_back(std::make_shared<Atomic<size_t>>(last_unused));
+ return m_key_ids.size() - 1;
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/pubkey/xmss/xmss_index_registry.h b/comm/third_party/botan/src/lib/pubkey/xmss/xmss_index_registry.h
new file mode 100644
index 0000000000..91166db4ba
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/xmss/xmss_index_registry.h
@@ -0,0 +1,105 @@
+/*
+ * 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 <string>
+
+#include <botan/secmem.h>
+#include <botan/internal/atomic.h>
+#include <botan/mutex.h>
+
+//BOTAN_FUTURE_INTERNAL_HEADER(xmss_index_registry.h)
+
+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<Atomic<size_t>>
+ get(const secure_vector<uint8_t>& private_seed,
+ const secure_vector<uint8_t>& 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.
+ **/
+ uint64_t make_key_id(const secure_vector<uint8_t>& private_seed,
+ const secure_vector<uint8_t>& prf) const;
+
+ /**
+ * 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<uint64_t> m_key_ids;
+ std::vector<std::shared_ptr<Atomic<size_t>>> m_leaf_indices;
+ mutex_type m_mutex;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/pubkey/xmss/xmss_key_pair.h b/comm/third_party/botan/src/lib/pubkey/xmss/xmss_key_pair.h
new file mode 100644
index 0000000000..19e23c7776
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/xmss/xmss_key_pair.h
@@ -0,0 +1,49 @@
+/*
+ * XMSS Key Pair
+ * (C) 2016 Matthias Gierlings
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ **/
+
+#ifndef BOTAN_XMSS_KEY_PAIR_H_
+#define BOTAN_XMSS_KEY_PAIR_H_
+
+#include <botan/xmss.h>
+
+BOTAN_DEPRECATED_HEADER(xmss_key_pair.h)
+
+namespace Botan {
+
+/**
+ * A pair of XMSS public and private key.
+ **/
+class BOTAN_PUBLIC_API(2,0) XMSS_Key_Pair
+ {
+ public:
+ XMSS_Key_Pair(XMSS_Parameters::xmss_algorithm_t xmss_oid,
+ RandomNumberGenerator& rng)
+ : m_priv_key(xmss_oid, rng), m_pub_key(m_priv_key) {}
+
+ XMSS_Key_Pair(const XMSS_PublicKey& pub_key,
+ const XMSS_PrivateKey& priv_key)
+ : m_priv_key(priv_key), m_pub_key(pub_key)
+ {}
+
+ XMSS_Key_Pair(XMSS_PublicKey&& pub_key,
+ XMSS_PrivateKey&& priv_key)
+ : m_priv_key(std::move(priv_key)), m_pub_key(std::move(pub_key)) {}
+
+ const XMSS_PublicKey& public_key() const { return m_pub_key; }
+ XMSS_PublicKey& public_key() { return m_pub_key; }
+
+ const XMSS_PrivateKey& private_key() const { return m_priv_key; }
+ XMSS_PrivateKey& private_key() { return m_priv_key; }
+
+ private:
+ XMSS_PrivateKey m_priv_key;
+ XMSS_PublicKey m_pub_key;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/pubkey/xmss/xmss_parameters.cpp b/comm/third_party/botan/src/lib/pubkey/xmss/xmss_parameters.cpp
new file mode 100644
index 0000000000..55084c8b92
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/xmss/xmss_parameters.cpp
@@ -0,0 +1,184 @@
+/*
+ * XMSS Parameters
+ * Descibes a signature method for XMSS, as defined in:
+ * [1] XMSS: Extended Hash-Based Signatures,
+ * Request for Comments: 8391
+ * Release: May 2018.
+ * https://datatracker.ietf.org/doc/rfc8391/
+ *
+ * (C) 2016,2017,2018 Matthias Gierlings
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ **/
+
+#include <botan/xmss_parameters.h>
+#include <botan/exceptn.h>
+
+namespace Botan {
+
+XMSS_Parameters::xmss_algorithm_t XMSS_Parameters::xmss_id_from_string(const std::string& param_set)
+ {
+ if(param_set == "XMSS-SHA2_10_256")
+ { return XMSS_SHA2_10_256; }
+ if(param_set == "XMSS-SHA2_16_256")
+ { return XMSS_SHA2_16_256; }
+ if(param_set == "XMSS-SHA2_20_256")
+ { return XMSS_SHA2_20_256; }
+ if(param_set == "XMSS-SHA2_10_512")
+ { return XMSS_SHA2_10_512; }
+ if(param_set == "XMSS-SHA2_16_512")
+ { return XMSS_SHA2_16_512; }
+ if(param_set == "XMSS-SHA2_20_512")
+ { return XMSS_SHA2_20_512; }
+ if(param_set == "XMSS-SHAKE_10_256")
+ { return XMSS_SHAKE_10_256; }
+ if(param_set == "XMSS-SHAKE_16_256")
+ { return XMSS_SHAKE_16_256; }
+ if(param_set == "XMSS-SHAKE_20_256")
+ { return XMSS_SHAKE_20_256; }
+ if(param_set == "XMSS-SHAKE_10_512")
+ { return XMSS_SHAKE_10_512; }
+ if(param_set == "XMSS-SHAKE_16_512")
+ { return XMSS_SHAKE_16_512; }
+ if(param_set == "XMSS-SHAKE_20_512")
+ { return XMSS_SHAKE_20_512; }
+ throw Lookup_Error("Unknown XMSS algorithm param '" + param_set + "'");
+ }
+
+XMSS_Parameters::XMSS_Parameters(const std::string& param_set)
+ : XMSS_Parameters(XMSS_Parameters::xmss_id_from_string(param_set))
+ {
+ }
+
+XMSS_Parameters::XMSS_Parameters(xmss_algorithm_t oid)
+ : m_oid(oid)
+ {
+ switch(oid)
+ {
+ case XMSS_SHA2_10_256:
+ m_element_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;
+ case XMSS_SHA2_16_256:
+ m_element_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;
+ case XMSS_SHA2_20_256:
+ m_element_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;
+ case XMSS_SHA2_10_512:
+ m_element_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;
+ case XMSS_SHA2_16_512:
+ m_element_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;
+ case XMSS_SHA2_20_512:
+ m_element_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;
+ case XMSS_SHAKE_10_256:
+ m_element_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;
+ case XMSS_SHAKE_16_256:
+ m_element_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;
+ case XMSS_SHAKE_20_256:
+ m_element_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;
+ case XMSS_SHAKE_10_512:
+ m_element_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;
+ case XMSS_SHAKE_16_512:
+ m_element_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;
+ case XMSS_SHAKE_20_512:
+ m_element_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;
+ default:
+ throw Not_Implemented("Algorithm id does not match any known XMSS algorithm id:" + std::to_string(oid));
+ break;
+ }
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/pubkey/xmss/xmss_parameters.h b/comm/third_party/botan/src/lib/pubkey/xmss/xmss_parameters.h
new file mode 100644
index 0000000000..2f186ac53b
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/xmss/xmss_parameters.h
@@ -0,0 +1,119 @@
+/*
+ * XMSS Parameters
+ * (C) 2016,2018 Matthias Gierlings
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ **/
+
+#ifndef BOTAN_XMSS_PARAMETERS_H_
+#define BOTAN_XMSS_PARAMETERS_H_
+
+#include <botan/xmss_wots.h>
+#include <string>
+
+namespace Botan {
+
+/**
+ * Descibes a signature method for XMSS, as defined in:
+ * [1] XMSS: Extended Hash-Based Signatures,
+ * Request for Comments: 8391
+ * Release: May 2018.
+ * https://datatracker.ietf.org/doc/rfc8391/
+ **/
+class BOTAN_PUBLIC_API(2,0) XMSS_Parameters
+ {
+ public:
+ enum xmss_algorithm_t
+ {
+ XMSS_SHA2_10_256 = 0x00000001,
+ XMSS_SHA2_16_256 = 0x00000002,
+ XMSS_SHA2_20_256 = 0x00000003,
+ XMSS_SHA2_10_512 = 0x00000004,
+ XMSS_SHA2_16_512 = 0x00000005,
+ XMSS_SHA2_20_512 = 0x00000006,
+ XMSS_SHAKE_10_256 = 0x00000007,
+ XMSS_SHAKE_16_256 = 0x00000008,
+ XMSS_SHAKE_20_256 = 0x00000009,
+ XMSS_SHAKE_10_512 = 0x0000000a,
+ XMSS_SHAKE_16_512 = 0x0000000b,
+ XMSS_SHAKE_20_512 = 0x0000000c
+ };
+
+ static xmss_algorithm_t xmss_id_from_string(const std::string& algo_name);
+
+ XMSS_Parameters(const std::string& algo_name);
+ XMSS_Parameters(xmss_algorithm_t oid);
+
+ /**
+ * @return XMSS registry name for the chosen parameter set.
+ **/
+ const std::string& name() const
+ {
+ return m_name;
+ }
+
+ const std::string& hash_function_name() const
+ {
+ return m_hash_name;
+ }
+
+ /**
+ * Retrieves the uniform length of a message, and the size of
+ * each node. This correlates to XMSS parameter "n" defined
+ * in [1].
+ *
+ * @return element length in bytes.
+ **/
+ size_t element_size() const { return m_element_size; }
+
+ /**
+ * @returns The height (number of levels - 1) of the tree
+ **/
+ size_t tree_height() const { return m_tree_height; }
+
+ /**
+ * The Winternitz parameter.
+ *
+ * @return numeric base used for internal representation of
+ * data.
+ **/
+ size_t wots_parameter() const { return m_w; }
+
+ size_t len() const { return m_len; }
+
+ xmss_algorithm_t oid() const { return m_oid; }
+
+ XMSS_WOTS_Parameters::ots_algorithm_t ots_oid() const
+ {
+ return m_wots_oid;
+ }
+
+ /**
+ * Returns the estimated pre-quantum security level of
+ * the chosen algorithm.
+ **/
+ size_t estimated_strength() const
+ {
+ return m_strength;
+ }
+
+ bool operator==(const XMSS_Parameters& p) const
+ {
+ return m_oid == p.m_oid;
+ }
+
+ private:
+ 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_tree_height;
+ size_t m_w;
+ size_t m_len;
+ size_t m_strength;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/pubkey/xmss/xmss_privatekey.cpp b/comm/third_party/botan/src/lib/pubkey/xmss/xmss_privatekey.cpp
new file mode 100644
index 0000000000..c497be003a
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/xmss/xmss_privatekey.cpp
@@ -0,0 +1,405 @@
+/*
+ * XMSS Private Key
+ * An XMSS: Extended Hash-Based Siganture private key.
+ * The XMSS private key does not support the X509 and PKCS7 standard. Instead
+ * the raw format described in [1] is used.
+ *
+ * [1] XMSS: Extended Hash-Based Signatures,
+ * Request for Comments: 8391
+ * Release: May 2018.
+ * https://datatracker.ietf.org/doc/rfc8391/
+ *
+ * (C) 2016,2017,2018 Matthias Gierlings
+ * (C) 2019 Jack Lloyd
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ **/
+
+#include <botan/xmss.h>
+#include <botan/internal/xmss_signature_operation.h>
+#include <botan/internal/xmss_index_registry.h>
+#include <botan/internal/xmss_common_ops.h>
+#include <botan/ber_dec.h>
+#include <botan/der_enc.h>
+#include <iterator>
+
+#if defined(BOTAN_HAS_THREAD_UTILS)
+ #include <botan/internal/thread_pool.h>
+#endif
+
+namespace Botan {
+
+namespace {
+
+// fall back to raw decoding for previous versions, which did not encode an OCTET STRING
+secure_vector<uint8_t> extract_raw_key(const secure_vector<uint8_t>& key_bits)
+ {
+ secure_vector<uint8_t> raw_key;
+ try
+ {
+ BER_Decoder(key_bits).decode(raw_key, OCTET_STRING);
+ }
+ catch(Decoding_Error&)
+ {
+ raw_key = key_bits;
+ }
+ return raw_key;
+ }
+
+}
+
+XMSS_PrivateKey::XMSS_PrivateKey(const secure_vector<uint8_t>& key_bits)
+ : XMSS_PublicKey(unlock(key_bits)),
+ m_wots_priv_key(m_wots_params.oid(), m_public_seed),
+ m_hash(xmss_hash_function()),
+ m_index_reg(XMSS_Index_Registry::get_instance())
+ {
+ /*
+ The code requires sizeof(size_t) >= ceil(tree_height / 8)
+
+ Maximum supported tree height is 20, ceil(20/8) == 3, so 4 byte
+ size_t is sufficient for all defined parameters, or even a
+ (hypothetical) tree height 32, which would be extremely slow to
+ compute.
+ */
+ static_assert(sizeof(size_t) >= 4, "size_t is big enough to support leaf index");
+
+ secure_vector<uint8_t> raw_key = extract_raw_key(key_bits);
+
+ if(raw_key.size() != XMSS_PrivateKey::size())
+ {
+ throw Decoding_Error("Invalid XMSS private key size");
+ }
+
+ // extract & copy unused leaf index from raw_key
+ uint64_t unused_leaf = 0;
+ auto begin = (raw_key.begin() + XMSS_PublicKey::size());
+ auto end = raw_key.begin() + XMSS_PublicKey::size() + sizeof(uint32_t);
+
+ for(auto& i = begin; i != end; i++)
+ {
+ unused_leaf = ((unused_leaf << 8) | *i);
+ }
+
+ if(unused_leaf >= (1ull << XMSS_PublicKey::m_xmss_params.tree_height()))
+ {
+ throw Decoding_Error("XMSS private key leaf index out of bounds");
+ }
+
+ begin = end;
+ end = begin + XMSS_PublicKey::m_xmss_params.element_size();
+ m_prf.clear();
+ m_prf.reserve(XMSS_PublicKey::m_xmss_params.element_size());
+ std::copy(begin, end, std::back_inserter(m_prf));
+
+ begin = end;
+ end = begin + m_wots_params.element_size();
+ m_wots_priv_key.set_private_seed(secure_vector<uint8_t>(begin, end));
+ set_unused_leaf_index(static_cast<size_t>(unused_leaf));
+ }
+
+XMSS_PrivateKey::XMSS_PrivateKey(
+ XMSS_Parameters::xmss_algorithm_t xmss_algo_id,
+ RandomNumberGenerator& rng)
+ : XMSS_PublicKey(xmss_algo_id, rng),
+ m_wots_priv_key(XMSS_PublicKey::m_xmss_params.ots_oid(),
+ public_seed(),
+ rng),
+ m_hash(xmss_hash_function()),
+ m_prf(rng.random_vec(XMSS_PublicKey::m_xmss_params.element_size())),
+ m_index_reg(XMSS_Index_Registry::get_instance())
+ {
+ XMSS_Address adrs;
+ set_root(tree_hash(0,
+ XMSS_PublicKey::m_xmss_params.tree_height(),
+ adrs));
+ }
+
+
+XMSS_PrivateKey::XMSS_PrivateKey(XMSS_Parameters::xmss_algorithm_t xmss_algo_id,
+ size_t idx_leaf,
+ const secure_vector<uint8_t>& wots_priv_seed,
+ const secure_vector<uint8_t>& prf,
+ const secure_vector<uint8_t>& root,
+ const secure_vector<uint8_t>& public_seed)
+ : XMSS_PublicKey(xmss_algo_id, root, public_seed),
+ m_wots_priv_key(XMSS_PublicKey::m_xmss_params.ots_oid(),
+ public_seed,
+ wots_priv_seed),
+ m_hash(XMSS_PublicKey::m_xmss_params.hash_function_name()),
+ m_prf(prf),
+ m_index_reg(XMSS_Index_Registry::get_instance())
+ {
+ set_unused_leaf_index(idx_leaf);
+ }
+
+secure_vector<uint8_t>
+XMSS_PrivateKey::tree_hash(size_t start_idx,
+ size_t target_node_height,
+ XMSS_Address& adrs)
+ {
+ BOTAN_ASSERT_NOMSG(target_node_height <= 30);
+ BOTAN_ASSERT((start_idx % (static_cast<size_t>(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.
+
+ Thread_Pool& thread_pool = Thread_Pool::global_instance();
+
+ const size_t split_level = std::min(target_node_height, thread_pool.worker_count());
+
+ // skip parallelization overhead for leaf nodes.
+ if(split_level == 0)
+ {
+ secure_vector<uint8_t> result;
+ tree_hash_subtree(result, start_idx, target_node_height, adrs);
+ return result;
+ }
+
+ const size_t subtrees = static_cast<size_t>(1) << split_level;
+ const size_t last_idx = (static_cast<size_t>(1) << (target_node_height)) + start_idx;
+ const size_t offs = (last_idx - start_idx) / subtrees;
+ // this cast cannot overflow because target_node_height is limited
+ uint8_t level = static_cast<uint8_t>(split_level); // current level in the tree
+
+ BOTAN_ASSERT((last_idx - start_idx) % subtrees == 0,
+ "Number of worker threads in tree_hash need to divide range "
+ "of calculated nodes.");
+
+ std::vector<secure_vector<uint8_t>> nodes(
+ subtrees,
+ secure_vector<uint8_t>(XMSS_PublicKey::m_xmss_params.element_size()));
+ std::vector<XMSS_Address> node_addresses(subtrees, adrs);
+ std::vector<XMSS_Hash> xmss_hash(subtrees, m_hash);
+ std::vector<std::future<void>> 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<uint8_t>&,
+ size_t,
+ size_t,
+ XMSS_Address&,
+ XMSS_Hash&);
+
+ tree_hash_subtree_fn_t work_fn = &XMSS_PrivateKey::tree_hash_subtree;
+
+ work.push_back(thread_pool.run(
+ work_fn,
+ this,
+ std::ref(nodes[i]),
+ start_idx + i * offs,
+ target_node_height - split_level,
+ std::ref(node_addresses[i]),
+ std::ref(xmss_hash[i])));
+ }
+
+ for(auto& w : work)
+ {
+ w.get();
+ }
+ work.clear();
+
+ // Parallelize the top tree levels horizontally
+ while(level-- > 1)
+ {
+ std::vector<secure_vector<uint8_t>> ro_nodes(
+ nodes.begin(), nodes.begin() + (static_cast<size_t>(1) << (level+1)));
+
+ for(size_t i = 0; i < (static_cast<size_t>(1) << level); i++)
+ {
+ BOTAN_ASSERT_NOMSG(xmss_hash.size() > i);
+
+ node_addresses[i].set_tree_height(static_cast<uint32_t>(target_node_height - (level + 1)));
+ node_addresses[i].set_tree_index(
+ (node_addresses[2 * i + 1].get_tree_index() - 1) >> 1);
+
+ work.push_back(thread_pool.run(
+ &XMSS_Common_Ops::randomize_tree_hash,
+ std::ref(nodes[i]),
+ std::cref(ro_nodes[2 * i]),
+ std::cref(ro_nodes[2 * i + 1]),
+ std::ref(node_addresses[i]),
+ std::cref(this->public_seed()),
+ std::ref(xmss_hash[i]),
+ std::cref(m_xmss_params)));
+ }
+
+ for(auto &w : work)
+ {
+ w.get();
+ }
+ work.clear();
+ }
+
+ // Avoid creation an extra thread to calculate root node.
+ node_addresses[0].set_tree_height(static_cast<uint32_t>(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_hash,
+ m_xmss_params);
+ return nodes[0];
+#else
+ secure_vector<uint8_t> result;
+ tree_hash_subtree(result, start_idx, target_node_height, adrs, m_hash);
+ return result;
+#endif
+ }
+
+void
+XMSS_PrivateKey::tree_hash_subtree(secure_vector<uint8_t>& result,
+ size_t start_idx,
+ size_t target_node_height,
+ XMSS_Address& adrs,
+ XMSS_Hash& hash)
+ {
+ const secure_vector<uint8_t>& seed = this->public_seed();
+
+ std::vector<secure_vector<uint8_t>> nodes(
+ target_node_height + 1,
+ secure_vector<uint8_t>(XMSS_PublicKey::m_xmss_params.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
+ // a pending element, meaning it is not regarded as element on the stack
+ // until level is increased.
+ std::vector<uint8_t> node_levels(target_node_height + 1);
+
+ uint8_t level = 0; // current level on the node stack.
+ XMSS_WOTS_PublicKey pk(m_wots_priv_key.wots_parameters().oid(), seed);
+ const size_t last_idx = (static_cast<size_t>(1) << target_node_height) + start_idx;
+
+ for(size_t i = start_idx; i < last_idx; i++)
+ {
+ adrs.set_type(XMSS_Address::Type::OTS_Hash_Address);
+ adrs.set_ots_address(static_cast<uint32_t>(i));
+ this->wots_private_key().generate_public_key(
+ pk,
+ // getWOTS_SK(SK, s + i), reference implementation uses adrs
+ // instead of zero padded index s + i.
+ this->wots_private_key().at(adrs, hash),
+ adrs,
+ hash);
+ adrs.set_type(XMSS_Address::Type::LTree_Address);
+ adrs.set_ltree_address(static_cast<uint32_t>(i));
+ XMSS_Common_Ops::create_l_tree(nodes[level], pk, adrs, seed, hash, m_xmss_params);
+ node_levels[level] = 0;
+
+ adrs.set_type(XMSS_Address::Type::Hash_Tree_Address);
+ adrs.set_tree_height(0);
+ adrs.set_tree_index(static_cast<uint32_t>(i));
+
+ 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);
+ node_levels[level - 1]++;
+ level--; //Pop stack top element
+ adrs.set_tree_height(adrs.get_tree_height() + 1);
+ }
+ level++; //push temporary node to stack
+ }
+ result = nodes[level - 1];
+ }
+
+secure_vector<uint8_t> XMSS_PrivateKey::private_key_bits() const
+ {
+ return DER_Encoder().encode(raw_private_key(), OCTET_STRING).get_contents();
+ }
+
+std::shared_ptr<Atomic<size_t>>
+XMSS_PrivateKey::recover_global_leaf_index() const
+ {
+ BOTAN_ASSERT(m_wots_priv_key.private_seed().size() ==
+ XMSS_PublicKey::m_xmss_params.element_size() &&
+ m_prf.size() == XMSS_PublicKey::m_xmss_params.element_size(),
+ "Trying to retrieve index for partially initialized key");
+ return m_index_reg.get(m_wots_priv_key.private_seed(), m_prf);
+ }
+
+void XMSS_PrivateKey::set_unused_leaf_index(size_t idx)
+ {
+ if(idx >= (1ull << XMSS_PublicKey::m_xmss_params.tree_height()))
+ {
+ throw Decoding_Error("XMSS private key leaf index out of bounds");
+ }
+ else
+ {
+ std::atomic<size_t>& index =
+ static_cast<std::atomic<size_t>&>(*recover_global_leaf_index());
+ size_t current = 0;
+
+ do
+ {
+ current = index.load();
+ if(current > idx)
+ { return; }
+ }
+ while(!index.compare_exchange_strong(current, idx));
+ }
+ }
+
+size_t XMSS_PrivateKey::reserve_unused_leaf_index()
+ {
+ size_t idx = (static_cast<std::atomic<size_t>&>(
+ *recover_global_leaf_index())).fetch_add(1);
+ if(idx >= (1ull << XMSS_PublicKey::m_xmss_params.tree_height()))
+ {
+ throw Decoding_Error("XMSS private key, one time signatures exhaused");
+ }
+ return idx;
+ }
+
+size_t XMSS_PrivateKey::unused_leaf_index() const
+ {
+ return *recover_global_leaf_index();
+ }
+
+secure_vector<uint8_t> XMSS_PrivateKey::raw_private_key() const
+ {
+ std::vector<uint8_t> pk { raw_public_key() };
+ secure_vector<uint8_t> result(pk.begin(), pk.end());
+ result.reserve(size());
+
+ for(int i = 3; i >= 0; i--)
+ {
+ result.push_back(
+ static_cast<uint8_t>(
+ static_cast<uint64_t>(unused_leaf_index()) >> 8 * i));
+ }
+
+ std::copy(m_prf.begin(), m_prf.end(), std::back_inserter(result));
+ std::copy(m_wots_priv_key.private_seed().begin(),
+ m_wots_priv_key.private_seed().end(),
+ std::back_inserter(result));
+
+ return result;
+ }
+
+std::unique_ptr<PK_Ops::Signature>
+XMSS_PrivateKey::create_signature_op(RandomNumberGenerator&,
+ const std::string&,
+ const std::string& provider) const
+ {
+ if(provider == "base" || provider.empty())
+ return std::unique_ptr<PK_Ops::Signature>(
+ new XMSS_Signature_Operation(*this));
+
+ throw Provider_Not_Found(algo_name(), provider);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/pubkey/xmss/xmss_privatekey.h b/comm/third_party/botan/src/lib/pubkey/xmss/xmss_privatekey.h
new file mode 100644
index 0000000000..dc040e4437
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/xmss/xmss_privatekey.h
@@ -0,0 +1,13 @@
+/*
+ * (C) 2016,2017,2018 Matthias Gierlings
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ **/
+
+#ifndef BOTAN_XMSS_PRIVATEKEY_H_
+#define BOTAN_XMSS_PRIVATEKEY_H_
+
+#include <botan/xmss.h>
+BOTAN_DEPRECATED_HEADER(xmss_privatekey.h)
+
+#endif
diff --git a/comm/third_party/botan/src/lib/pubkey/xmss/xmss_publickey.cpp b/comm/third_party/botan/src/lib/pubkey/xmss/xmss_publickey.cpp
new file mode 100644
index 0000000000..f6db6d5026
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/xmss/xmss_publickey.cpp
@@ -0,0 +1,129 @@
+/*
+ * XMSS Public Key
+ * An XMSS: Extended Hash-Based Siganture public key.
+ * The XMSS public key does not support the X509 standard. Instead the
+ * raw format described in [1] is used.
+ *
+ * [1] XMSS: Extended Hash-Based Signatures,
+ * Request for Comments: 8391
+ * Release: May 2018.
+ * https://datatracker.ietf.org/doc/rfc8391/
+ *
+ * (C) 2016,2017 Matthias Gierlings
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ **/
+
+#include <botan/xmss.h>
+#include <botan/internal/xmss_verification_operation.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
+#include <iterator>
+
+namespace Botan {
+
+namespace {
+
+// fall back to raw decoding for previous versions, which did not encode an OCTET STRING
+std::vector<uint8_t> extract_raw_key(const std::vector<uint8_t>& key_bits)
+ {
+ std::vector<uint8_t> raw_key;
+ try
+ {
+ BER_Decoder(key_bits).decode(raw_key, OCTET_STRING);
+ }
+ catch(Decoding_Error&)
+ {
+ raw_key = key_bits;
+ }
+ return raw_key;
+ }
+
+}
+
+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(const std::vector<uint8_t>& key_bits)
+ : m_raw_key(extract_raw_key(key_bits)),
+ m_xmss_params(XMSS_PublicKey::deserialize_xmss_oid(m_raw_key)),
+ m_wots_params(m_xmss_params.ots_oid())
+ {
+ if(m_raw_key.size() < XMSS_PublicKey::size())
+ {
+ throw Decoding_Error("Invalid XMSS public key size detected");
+ }
+
+ // extract & copy root from raw key
+ m_root.clear();
+ m_root.reserve(m_xmss_params.element_size());
+ auto begin = m_raw_key.begin() + sizeof(uint32_t);
+ auto end = begin + m_xmss_params.element_size();
+ std::copy(begin, end, std::back_inserter(m_root));
+
+ // extract & copy public seed from raw key
+ begin = end;
+ end = begin + m_xmss_params.element_size();
+ m_public_seed.clear();
+ m_public_seed.reserve(m_xmss_params.element_size());
+ std::copy(begin, end, std::back_inserter(m_public_seed));
+ }
+
+XMSS_Parameters::xmss_algorithm_t
+XMSS_PublicKey::deserialize_xmss_oid(const std::vector<uint8_t>& raw_key)
+ {
+ if(raw_key.size() < 4)
+ {
+ throw Decoding_Error("XMSS signature OID missing.");
+ }
+
+ // extract and convert algorithm id to enum type
+ uint32_t raw_id = 0;
+ for(size_t i = 0; i < 4; i++)
+ { raw_id = ((raw_id << 8) | raw_key[i]); }
+
+ return static_cast<XMSS_Parameters::xmss_algorithm_t>(raw_id);
+ }
+
+std::unique_ptr<PK_Ops::Verification>
+XMSS_PublicKey::create_verification_op(const std::string&,
+ const std::string& provider) const
+ {
+ if(provider == "base" || provider.empty())
+ {
+ return std::unique_ptr<PK_Ops::Verification>(
+ new XMSS_Verification_Operation(*this));
+ }
+ throw Provider_Not_Found(algo_name(), provider);
+ }
+
+std::vector<uint8_t> XMSS_PublicKey::raw_public_key() const
+ {
+ std::vector<uint8_t> result
+ {
+ static_cast<uint8_t>(m_xmss_params.oid() >> 24),
+ static_cast<uint8_t>(m_xmss_params.oid() >> 16),
+ static_cast<uint8_t>(m_xmss_params.oid() >> 8),
+ static_cast<uint8_t>(m_xmss_params.oid())
+ };
+
+ std::copy(m_root.begin(), m_root.end(), std::back_inserter(result));
+ std::copy(m_public_seed.begin(),
+ m_public_seed.end(),
+ std::back_inserter(result));
+
+ return result;
+ }
+
+std::vector<uint8_t> XMSS_PublicKey::public_key_bits() const
+ {
+ std::vector<uint8_t> output;
+ DER_Encoder(output).encode(raw_public_key(), OCTET_STRING);
+ return output;
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/pubkey/xmss/xmss_publickey.h b/comm/third_party/botan/src/lib/pubkey/xmss/xmss_publickey.h
new file mode 100644
index 0000000000..eba27fc95f
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/xmss/xmss_publickey.h
@@ -0,0 +1,14 @@
+/*
+ * (C) 2016,2017,2018 Matthias Gierlings
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ **/
+
+#ifndef BOTAN_XMSS_PUBLICKEY_H_
+#define BOTAN_XMSS_PUBLICKEY_H_
+
+#include <botan/xmss.h>
+BOTAN_DEPRECATED_HEADER(xmss_publickey.h)
+
+#endif
+
diff --git a/comm/third_party/botan/src/lib/pubkey/xmss/xmss_signature.cpp b/comm/third_party/botan/src/lib/pubkey/xmss/xmss_signature.cpp
new file mode 100644
index 0000000000..98fadff358
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/xmss/xmss_signature.cpp
@@ -0,0 +1,92 @@
+/*
+ * XMSS Signature
+ * (C) 2016,2017,2018 Matthias Gierlings
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ **/
+
+#include <botan/internal/xmss_signature.h>
+#include <iterator>
+
+namespace Botan {
+
+XMSS_Signature::XMSS_Signature(XMSS_Parameters::xmss_algorithm_t oid,
+ const secure_vector<uint8_t>& raw_sig)
+ : m_leaf_idx(0), m_randomness(0, 0x00), m_tree_sig()
+ {
+ XMSS_Parameters xmss_params(oid);
+
+ if(raw_sig.size() != (xmss_params.len() + xmss_params.tree_height() + 1)
+ * xmss_params.element_size() + sizeof(uint32_t))
+ {
+ throw Decoding_Error("XMSS signature size invalid.");
+ }
+
+ for(size_t i = 0; i < 4; i++)
+ { m_leaf_idx = ((m_leaf_idx << 8) | raw_sig[i]); }
+
+ if(m_leaf_idx >= (1ull << xmss_params.tree_height()))
+ {
+ 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();
+ std::copy(begin, end, std::back_inserter(m_randomness));
+
+ for(size_t i = 0; i < xmss_params.len(); i++)
+ {
+ begin = end;
+ end = begin + xmss_params.element_size();
+ m_tree_sig.ots_signature().push_back(secure_vector<uint8_t>(0));
+ m_tree_sig.ots_signature().back().reserve(
+ xmss_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++)
+ {
+ begin = end;
+ end = begin + xmss_params.element_size();
+ m_tree_sig.authentication_path().push_back(secure_vector<uint8_t>(0));
+ m_tree_sig.authentication_path().back().reserve(
+ xmss_params.element_size());
+ std::copy(begin,
+ end,
+ std::back_inserter(m_tree_sig.authentication_path().back()));
+ }
+ }
+
+secure_vector<uint8_t> XMSS_Signature::bytes() const
+ {
+ secure_vector<uint8_t> result
+ {
+ static_cast<uint8_t>(m_leaf_idx >> 24U),
+ static_cast<uint8_t>(m_leaf_idx >> 16U),
+ static_cast<uint8_t>(m_leaf_idx >> 8U),
+ static_cast<uint8_t>(m_leaf_idx)
+ };
+
+ std::copy(m_randomness.begin(),
+ m_randomness.end(),
+ std::back_inserter(result));
+
+ for(const auto& sig : tree().ots_signature())
+ {
+ std::copy(sig.begin(),
+ sig.end(),
+ std::back_inserter(result));
+ }
+
+ for(const auto& auth : tree().authentication_path())
+ {
+ std::copy(auth.begin(),
+ auth.end(),
+ std::back_inserter(result));
+ }
+ return result;
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/pubkey/xmss/xmss_signature.h b/comm/third_party/botan/src/lib/pubkey/xmss/xmss_signature.h
new file mode 100644
index 0000000000..d791dbedb8
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/xmss/xmss_signature.h
@@ -0,0 +1,127 @@
+/*
+ * XMSS Signature
+ * (C) 2016 Matthias Gierlings
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ **/
+
+#ifndef BOTAN_XMSS_SIGNATURE_H_
+#define BOTAN_XMSS_SIGNATURE_H_
+
+#include <cstddef>
+#include <botan/exceptn.h>
+#include <botan/types.h>
+#include <botan/secmem.h>
+#include <botan/xmss_parameters.h>
+#include <botan/xmss_wots.h>
+
+namespace Botan {
+
+class XMSS_Signature final
+ {
+ public:
+ /**
+ * Creates a signature from an XMSS signature method and a uint8_t sequence
+ * representing a raw signature.
+ *
+ * @param oid XMSS signature method
+ * @param raw_sig An XMSS signature serialized using
+ * XMSS_Signature::bytes().
+ **/
+ XMSS_Signature(XMSS_Parameters::xmss_algorithm_t oid,
+ const secure_vector<uint8_t>& raw_sig);
+
+ /**
+ * Creates an XMSS Signature from a leaf index used for signature
+ * generation, a random value and a tree signature.
+ *
+ * @param leaf_idx Leaf index used to generate the signature.
+ * @param randomness A random value.
+ * @param tree_sig A tree signature.
+ **/
+ XMSS_Signature(size_t leaf_idx,
+ const secure_vector<uint8_t>& randomness,
+ const XMSS_WOTS_PublicKey::TreeSignature& tree_sig)
+ : m_leaf_idx(leaf_idx), m_randomness(randomness),
+ m_tree_sig(tree_sig) {}
+
+ /**
+ * Creates an XMSS Signature from a leaf index used for signature
+ * generation, a random value and a tree signature.
+ *
+ * @param leaf_idx Leaf index used to generate the signature.
+ * @param randomness A random value.
+ * @param tree_sig A tree signature.
+ **/
+ XMSS_Signature(size_t leaf_idx,
+ secure_vector<uint8_t>&& randomness,
+ XMSS_WOTS_PublicKey::TreeSignature&& tree_sig)
+ : m_leaf_idx(leaf_idx), m_randomness(std::move(randomness)),
+ m_tree_sig(std::move(tree_sig)) {}
+
+ size_t unused_leaf_index() const { return m_leaf_idx; }
+ void set_unused_leaf_idx(size_t idx) { m_leaf_idx = idx; }
+
+ const secure_vector<uint8_t> randomness() const
+ {
+ return m_randomness;
+ }
+
+ secure_vector<uint8_t>& randomness()
+ {
+ return m_randomness;
+ }
+
+ void set_randomness(const secure_vector<uint8_t>& randomness)
+ {
+ m_randomness = randomness;
+ }
+
+ void set_randomness(secure_vector<uint8_t>&& randomness)
+ {
+ m_randomness = std::move(randomness);
+ }
+
+ const XMSS_WOTS_PublicKey::TreeSignature& tree() const
+ {
+ return m_tree_sig;
+ }
+
+ XMSS_WOTS_PublicKey::TreeSignature& tree()
+ {
+ return m_tree_sig;
+ }
+
+ void set_tree(const XMSS_WOTS_PublicKey::TreeSignature& tree_sig)
+ {
+ m_tree_sig = tree_sig;
+ }
+
+ void set_tree(XMSS_WOTS_PublicKey::TreeSignature&& tree_sig)
+ {
+ m_tree_sig = std::move(tree_sig);
+ }
+
+ /**
+ * Generates a serialized representation of XMSS Signature by
+ * concatenating the following elements in order:
+ * 4-byte leaf index, n-bytes randomness, ots_signature,
+ * authentication path.
+ *
+ * n is the element_size(), len equal to len(), h the tree height
+ * defined by the chosen XMSS signature method.
+ *
+ * @return serialized signature, a sequence of
+ * 4+(len + h + 1)n bytes.
+ **/
+ secure_vector<uint8_t> bytes() const;
+
+ private:
+ size_t m_leaf_idx;
+ secure_vector<uint8_t> m_randomness;
+ XMSS_WOTS_PublicKey::TreeSignature m_tree_sig;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/pubkey/xmss/xmss_signature_operation.cpp b/comm/third_party/botan/src/lib/pubkey/xmss/xmss_signature_operation.cpp
new file mode 100644
index 0000000000..49f1041d90
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/xmss/xmss_signature_operation.cpp
@@ -0,0 +1,120 @@
+/*
+ * XMSS Signature Operation
+ * Signature generation operation for Extended Hash-Based Signatures (XMSS) as
+ * defined in:
+ *
+ * [1] XMSS: Extended Hash-Based Signatures,
+ * Request for Comments: 8391
+ * Release: May 2018.
+ * https://datatracker.ietf.org/doc/rfc8391/
+ *
+ * (C) 2016,2017,2018 Matthias Gierlings
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ **/
+
+#include <botan/internal/xmss_signature_operation.h>
+#include <botan/internal/xmss_tools.h>
+
+namespace Botan {
+
+XMSS_Signature_Operation::XMSS_Signature_Operation(
+ const XMSS_PrivateKey& private_key) :
+ m_priv_key(private_key),
+ m_xmss_params(private_key.xmss_oid()),
+ m_hash(private_key.xmss_hash_function()),
+ m_randomness(0),
+ m_leaf_idx(0),
+ m_is_initialized(false)
+ {}
+
+XMSS_WOTS_PublicKey::TreeSignature
+XMSS_Signature_Operation::generate_tree_signature(const secure_vector<uint8_t>& msg,
+ XMSS_PrivateKey& xmss_priv_key,
+ XMSS_Address& adrs)
+ {
+
+ wots_keysig_t auth_path = build_auth_path(xmss_priv_key, adrs);
+ adrs.set_type(XMSS_Address::Type::OTS_Hash_Address);
+ adrs.set_ots_address(m_leaf_idx);
+
+ wots_keysig_t sig_ots = xmss_priv_key.wots_private_key().sign(msg, adrs);
+ return XMSS_WOTS_PublicKey::TreeSignature(sig_ots, auth_path);
+ }
+
+XMSS_Signature
+XMSS_Signature_Operation::sign(const secure_vector<uint8_t>& 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
+ {
+ return sizeof(uint64_t) + // size of leaf index
+ m_xmss_params.element_size() +
+ m_xmss_params.len() * m_xmss_params.element_size() +
+ m_xmss_params.tree_height() * m_xmss_params.element_size();
+ }
+
+wots_keysig_t
+XMSS_Signature_Operation::build_auth_path(XMSS_PrivateKey& priv_key,
+ XMSS_Address& adrs)
+ {
+ wots_keysig_t auth_path(m_xmss_params.tree_height());
+ adrs.set_type(XMSS_Address::Type::Hash_Tree_Address);
+
+ for(size_t j = 0; j < m_xmss_params.tree_height(); j++)
+ {
+ size_t k = (m_leaf_idx / (1ULL << j)) ^ 0x01;
+ auth_path[j] = priv_key.tree_hash(k * (1ULL << j), j, adrs);
+ }
+
+ return auth_path;
+ }
+
+void XMSS_Signature_Operation::update(const uint8_t msg[], size_t msg_len)
+ {
+ initialize();
+ m_hash.h_msg_update(msg, msg_len);
+ }
+
+secure_vector<uint8_t>
+XMSS_Signature_Operation::sign(RandomNumberGenerator&)
+ {
+ initialize();
+ secure_vector<uint8_t> signature(sign(m_hash.h_msg_final(),
+ m_priv_key).bytes());
+ m_is_initialized = false;
+ return signature;
+ }
+
+void XMSS_Signature_Operation::initialize()
+ {
+ // return if we already initialized and reserved a leaf index for signing.
+ if(m_is_initialized)
+ { return; }
+
+ secure_vector<uint8_t> index_bytes;
+ // reserve leaf index so it can not be reused in by another signature
+ // operation using the same private key.
+ m_leaf_idx = static_cast<uint32_t>(m_priv_key.reserve_unused_leaf_index());
+
+ // write prefix for message hashing into buffer.
+ XMSS_Tools::concat(index_bytes, m_leaf_idx, 32);
+ m_randomness = m_hash.prf(m_priv_key.prf(), index_bytes);
+ index_bytes.clear();
+ XMSS_Tools::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 --git a/comm/third_party/botan/src/lib/pubkey/xmss/xmss_signature_operation.h b/comm/third_party/botan/src/lib/pubkey/xmss/xmss_signature_operation.h
new file mode 100644
index 0000000000..e6fb2c7114
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/xmss/xmss_signature_operation.h
@@ -0,0 +1,89 @@
+/*
+ * XMSS Signature Operation
+ * (C) 2016,2017,2018 Matthias Gierlings
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ **/
+
+#ifndef BOTAN_XMSS_SIGNATURE_OPERATION_H_
+#define BOTAN_XMSS_SIGNATURE_OPERATION_H_
+
+#include <botan/pk_ops.h>
+#include <botan/xmss.h>
+#include <botan/internal/xmss_address.h>
+#include <botan/internal/xmss_signature.h>
+#include <botan/xmss_wots.h>
+
+namespace Botan {
+
+/**
+ * Signature generation operation for Extended Hash-Based Signatures (XMSS) as
+ * defined in:
+ *
+ * [1] XMSS: Extended Hash-Based Signatures,
+ * Request for Comments: 8391
+ * Release: May 2018.
+ * https://datatracker.ietf.org/doc/rfc8391/
+ **/
+class XMSS_Signature_Operation final : public virtual PK_Ops::Signature
+ {
+ public:
+ XMSS_Signature_Operation(const XMSS_PrivateKey& private_key);
+
+ /**
+ * Creates an XMSS signature for the message provided through call to
+ * update().
+ *
+ * @return serialized XMSS signature.
+ **/
+ secure_vector<uint8_t> sign(RandomNumberGenerator&) override;
+
+ void update(const uint8_t msg[], size_t msg_len) override;
+
+ size_t signature_length() const override;
+
+ 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_WOTS_PublicKey::TreeSignature generate_tree_signature(
+ const secure_vector<uint8_t>& 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<uint8_t>& 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;
+ const XMSS_Parameters m_xmss_params;
+ XMSS_Hash m_hash;
+ secure_vector<uint8_t> m_randomness;
+ uint32_t m_leaf_idx;
+ bool m_is_initialized;
+ };
+
+}
+
+#endif
+
diff --git a/comm/third_party/botan/src/lib/pubkey/xmss/xmss_tools.h b/comm/third_party/botan/src/lib/pubkey/xmss/xmss_tools.h
new file mode 100644
index 0000000000..81d17f5bfe
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/xmss/xmss_tools.h
@@ -0,0 +1,108 @@
+/*
+ * XMSS Tools
+ * (C) 2016,2017 Matthias Gierlings
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ **/
+
+#ifndef BOTAN_XMSS_TOOLS_H_
+#define BOTAN_XMSS_TOOLS_H_
+
+#include <botan/cpuid.h>
+#include <botan/secmem.h>
+#include <iterator>
+#include <type_traits>
+
+//BOTAN_FUTURE_INTERNAL_HEADER(xmss_tools.h)
+
+namespace Botan {
+
+/**
+ * Helper tools for low level byte operations required
+ * for the XMSS implementation.
+ **/
+class XMSS_Tools final
+ {
+ public:
+ 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<typename T,
+ typename U = typename std::enable_if<std::is_integral<T>::value,
+ void>::type>
+ static void concat(secure_vector<uint8_t>& 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 <typename T,
+ typename U = typename std::enable_if<std::is_integral<T>::value,
+ void>::type>
+ static void concat(secure_vector<uint8_t>& target, const T& src, size_t len);
+
+ private:
+ XMSS_Tools();
+ };
+
+template <typename T, typename U>
+void XMSS_Tools::concat(secure_vector<uint8_t>& target, const T& src)
+ {
+ const uint8_t* src_bytes = reinterpret_cast<const uint8_t*>(&src);
+ if(CPUID::is_little_endian())
+ {
+ 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 <typename T, typename U>
+void XMSS_Tools::concat(secure_vector<uint8_t>& target,
+ const T& src,
+ size_t len)
+ {
+ size_t c = static_cast<size_t>(std::min(len, sizeof(src)));
+ if(len > sizeof(src))
+ {
+ target.resize(target.size() + len - sizeof(src), 0);
+ }
+
+ const uint8_t* src_bytes = reinterpret_cast<const uint8_t*>(&src);
+ if(CPUID::is_little_endian())
+ {
+ 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));
+ }
+ }
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/pubkey/xmss/xmss_verification_operation.cpp b/comm/third_party/botan/src/lib/pubkey/xmss/xmss_verification_operation.cpp
new file mode 100644
index 0000000000..b9442aa03d
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/xmss/xmss_verification_operation.cpp
@@ -0,0 +1,138 @@
+/*
+ * XMSS Verification Operation
+ * Provides signature verification capabilities for Extended Hash-Based
+ * Signatures (XMSS).
+ *
+ * (C) 2016,2017 Matthias Gierlings
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ **/
+
+#include <botan/internal/xmss_verification_operation.h>
+#include <botan/internal/xmss_common_ops.h>
+#include <botan/internal/xmss_tools.h>
+#include <array>
+
+namespace Botan {
+
+XMSS_Verification_Operation::XMSS_Verification_Operation(
+ const XMSS_PublicKey& public_key) :
+ m_pub_key(public_key),
+ m_hash(public_key.xmss_hash_function()),
+ m_msg_buf(0)
+ {
+ }
+
+secure_vector<uint8_t>
+XMSS_Verification_Operation::root_from_signature(const XMSS_Signature& sig,
+ const secure_vector<uint8_t>& msg,
+ XMSS_Address& adrs,
+ const secure_vector<uint8_t>& seed)
+ {
+ const auto params = m_pub_key.xmss_parameters();
+
+ const uint32_t next_index = static_cast<uint32_t>(sig.unused_leaf_index());
+ adrs.set_type(XMSS_Address::Type::OTS_Hash_Address);
+ adrs.set_ots_address(next_index);
+
+ XMSS_WOTS_PublicKey pub_key_ots(m_pub_key.wots_parameters().oid(),
+ msg,
+ sig.tree().ots_signature(),
+ adrs,
+ seed);
+
+ adrs.set_type(XMSS_Address::Type::LTree_Address);
+ adrs.set_ltree_address(next_index);
+
+ std::array<secure_vector<uint8_t>, 2> node;
+ XMSS_Common_Ops::create_l_tree(node[0], pub_key_ots, adrs, seed, m_hash, params);
+
+ adrs.set_type(XMSS_Address::Type::Hash_Tree_Address);
+ adrs.set_tree_index(next_index);
+
+ for(size_t k = 0; k < params.tree_height(); k++)
+ {
+ adrs.set_tree_height(static_cast<uint32_t>(k));
+ if(((next_index / (static_cast<size_t>(1) << k)) & 0x01) == 0)
+ {
+ adrs.set_tree_index(adrs.get_tree_index() >> 1);
+ XMSS_Common_Ops::randomize_tree_hash(node[1],
+ node[0],
+ sig.tree().authentication_path()[k],
+ adrs,
+ seed,
+ m_hash,
+ params);
+ }
+ else
+ {
+ adrs.set_tree_index((adrs.get_tree_index() - 1) >> 1);
+ XMSS_Common_Ops::randomize_tree_hash(node[1],
+ sig.tree().authentication_path()[k],
+ node[0],
+ adrs,
+ seed,
+ m_hash,
+ params);
+ }
+ node[0] = node[1];
+ }
+ return node[0];
+ }
+
+bool
+XMSS_Verification_Operation::verify(const XMSS_Signature& sig,
+ const secure_vector<uint8_t>& msg,
+ const XMSS_PublicKey& public_key)
+ {
+ XMSS_Address adrs;
+ secure_vector<uint8_t> index_bytes;
+ XMSS_Tools::concat(index_bytes,
+ sig.unused_leaf_index(),
+ m_pub_key.xmss_parameters().element_size());
+ secure_vector<uint8_t> msg_digest =
+ m_hash.h_msg(sig.randomness(),
+ public_key.root(),
+ index_bytes,
+ msg);
+
+ secure_vector<uint8_t> node = root_from_signature(sig,
+ msg_digest,
+ adrs,
+ public_key.public_seed());
+
+ return (node == public_key.root());
+ }
+
+// FIXME: XMSS signature verification requires the "randomness" parameter out
+// of the XMSS signature, which is part of the prefix that is hashed before
+// msg. Since the signature is unknown till sign() is called all message
+// content has to be buffered. For large messages this can be inconvenient or
+// impossible.
+// Possible solution: Change PK_Ops::Verification interface to take the
+// signature as constructor argument, make sign a parameterless member call.
+void XMSS_Verification_Operation::update(const uint8_t msg[], size_t msg_len)
+ {
+ std::copy(msg, msg + msg_len, std::back_inserter(m_msg_buf));
+ }
+
+bool XMSS_Verification_Operation::is_valid_signature(const uint8_t sig[],
+ size_t sig_len)
+ {
+ try
+ {
+ XMSS_Signature signature(m_pub_key.xmss_parameters().oid(),
+ secure_vector<uint8_t>(sig, sig + sig_len));
+ bool result = verify(signature, m_msg_buf, m_pub_key);
+ m_msg_buf.clear();
+ return result;
+ }
+ catch(...)
+ {
+ m_msg_buf.clear();
+ return false;
+ }
+ }
+
+}
+
diff --git a/comm/third_party/botan/src/lib/pubkey/xmss/xmss_verification_operation.h b/comm/third_party/botan/src/lib/pubkey/xmss/xmss_verification_operation.h
new file mode 100644
index 0000000000..f96b3803bc
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/xmss/xmss_verification_operation.h
@@ -0,0 +1,71 @@
+/*
+ * XMSS Verification Operation
+ * (C) 2016 Matthias Gierlings
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ **/
+
+#ifndef BOTAN_XMSS_VERIFICATION_OPERATION_H_
+#define BOTAN_XMSS_VERIFICATION_OPERATION_H_
+
+#include <botan/pk_ops.h>
+#include <botan/xmss.h>
+#include <botan/internal/xmss_signature.h>
+
+namespace Botan {
+
+/**
+ * Provides signature verification capabilities for Extended Hash-Based
+ * Signatures (XMSS).
+ **/
+ class XMSS_Verification_Operation final : public virtual PK_Ops::Verification
+ {
+ public:
+ XMSS_Verification_Operation(
+ const XMSS_PublicKey& public_key);
+
+ bool is_valid_signature(const uint8_t sig[], size_t sig_len) override;
+
+ void update(const uint8_t msg[], size_t msg_len) override;
+
+ private:
+ /**
+ * Algorithm 13: "XMSS_rootFromSig"
+ * Computes a root node using an XMSS signature, a message and a seed.
+ *
+ * @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
+ * defined by the input parameters.
+ **/
+ secure_vector<uint8_t> root_from_signature(
+ const XMSS_Signature& sig,
+ const secure_vector<uint8_t>& msg,
+ XMSS_Address& ards,
+ const secure_vector<uint8_t>& seed);
+
+ /**
+ * Algorithm 14: "XMSS_verify"
+ * Verifies a XMSS signature using the corresponding XMSS public key.
+ *
+ * @param sig A XMSS signature.
+ * @param msg The message signed with sig.
+ * @param pub_key the public key
+ *
+ * @return true if signature sig is valid for msg, false otherwise.
+ **/
+ bool verify(const XMSS_Signature& sig,
+ const secure_vector<uint8_t>& msg,
+ const XMSS_PublicKey& pub_key);
+
+ const XMSS_PublicKey& m_pub_key;
+ XMSS_Hash m_hash;
+ secure_vector<uint8_t> m_msg_buf;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/pubkey/xmss/xmss_wots.h b/comm/third_party/botan/src/lib/pubkey/xmss/xmss_wots.h
new file mode 100644
index 0000000000..d85e889bfb
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/xmss/xmss_wots.h
@@ -0,0 +1,752 @@
+/*
+ * XMSS WOTS
+ * (C) 2016,2018 Matthias Gierlings
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ **/
+
+#ifndef BOTAN_XMSS_WOTS_H_
+#define BOTAN_XMSS_WOTS_H_
+
+#include <botan/asn1_obj.h>
+#include <botan/exceptn.h>
+#include <botan/pk_keys.h>
+#include <botan/rng.h>
+#include <botan/secmem.h>
+#include <botan/xmss_hash.h>
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace Botan {
+
+/**
+ * Descibes a signature method for XMSS Winternitz One Time Signatures,
+ * as defined in:
+ * [1] XMSS: Extended Hash-Based Signatures,
+ * Request for Comments: 8391
+ * Release: May 2018.
+ * https://datatracker.ietf.org/doc/rfc8391/
+ **/
+class XMSS_WOTS_Parameters final
+ {
+ public:
+ enum ots_algorithm_t
+ {
+ WOTSP_SHA2_256 = 0x00000001,
+ WOTSP_SHA2_512 = 0x00000002,
+ WOTSP_SHAKE_256 = 0x00000003,
+ WOTSP_SHAKE_512 = 0x00000004
+ };
+
+ XMSS_WOTS_Parameters(const std::string& algo_name);
+ XMSS_WOTS_Parameters(ots_algorithm_t ots_spec);
+
+ static ots_algorithm_t xmss_wots_id_from_string(const std::string& 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<uint8_t> base_w(const secure_vector<uint8_t>& msg, size_t out_size) const;
+
+ secure_vector<uint8_t> base_w(size_t value) const;
+
+ void append_checksum(secure_vector<uint8_t>& data);
+
+ /**
+ * @return XMSS WOTS registry name for the chosen parameter set.
+ **/
+ const std::string& name() const
+ {
+ return m_name;
+ }
+
+ /**
+ * @return Botan name for the hash function used.
+ **/
+ const std::string& hash_function_name() const
+ {
+ return m_hash_name;
+ }
+
+ /**
+ * Retrieves the uniform length of a message, and the size of
+ * each node. This correlates to XMSS parameter "n" defined
+ * in [1].
+ *
+ * @return element length in bytes.
+ **/
+ size_t element_size() const { return m_element_size; }
+
+ /**
+ * The Winternitz parameter.
+ *
+ * @return numeric base used for internal representation of
+ * data.
+ **/
+ size_t wots_parameter() const { return m_w; }
+
+ size_t len() const { return m_len; }
+
+ size_t len_1() const { return m_len_1; }
+
+ 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; }
+
+ size_t estimated_strength() const { return m_strength; }
+
+ bool operator==(const XMSS_WOTS_Parameters& p) const
+ {
+ return m_oid == p.m_oid;
+ }
+
+ private:
+ static const std::map<std::string, ots_algorithm_t> m_oid_name_lut;
+ ots_algorithm_t m_oid;
+ std::string m_name;
+ std::string m_hash_name;
+ size_t m_element_size;
+ size_t m_w;
+ size_t m_len_1;
+ size_t m_len_2;
+ size_t m_len;
+ size_t m_strength;
+ uint8_t m_lg_w;
+ };
+
+class XMSS_Address;
+
+typedef std::vector<secure_vector<uint8_t>> wots_keysig_t;
+
+/**
+ * A Winternitz One Time Signature public key for use with Extended Hash-Based
+ * Signatures.
+ **/
+class XMSS_WOTS_PublicKey : virtual public Public_Key
+ {
+ public:
+ class TreeSignature final
+ {
+ public:
+ TreeSignature() = default;
+
+ TreeSignature(const wots_keysig_t& ots_sig,
+ const wots_keysig_t& auth_path)
+ : m_ots_sig(ots_sig), m_auth_path(auth_path)
+ {}
+
+ TreeSignature(wots_keysig_t&& ots_sig,
+ wots_keysig_t&& auth_path)
+ : m_ots_sig(std::move(ots_sig)),
+ m_auth_path(std::move(auth_path))
+ {}
+
+ const wots_keysig_t& ots_signature() const
+ {
+ return m_ots_sig;
+ }
+
+ wots_keysig_t& ots_signature()
+ {
+ return m_ots_sig;
+ }
+
+ const wots_keysig_t& authentication_path() const
+ {
+ return m_auth_path;
+ }
+
+ wots_keysig_t& authentication_path()
+ {
+ return m_auth_path;
+ }
+
+ private:
+ wots_keysig_t m_ots_sig;
+ wots_keysig_t m_auth_path;
+ };
+
+ /**
+ * Creates a XMSS_WOTS_PublicKey for the signature method identified by
+ * oid. The public seed for this key will be initialized with a
+ * uniformly random n-byte value, where "n" is the element size of the
+ * selected signature method.
+ *
+ * @param oid Identifier for the selected signature method.
+ **/
+ XMSS_WOTS_PublicKey(XMSS_WOTS_Parameters::ots_algorithm_t oid)
+ : m_wots_params(oid),
+ m_hash(m_wots_params.hash_function_name()) {}
+
+ /**
+ * Creates a XMSS_WOTS_PublicKey for the signature method identified by
+ * oid. The public seed for this key will be initialized with a
+ * uniformly random n-byte value, where "n" is the element size of the
+ * selected signature method.
+ *
+ * @param oid Identifier for the selected signature method.
+ * @param rng A random number generate used to generate the public seed.
+ **/
+ XMSS_WOTS_PublicKey(XMSS_WOTS_Parameters::ots_algorithm_t oid,
+ RandomNumberGenerator& rng)
+ : m_wots_params(oid),
+ m_hash(m_wots_params.hash_function_name()),
+ m_public_seed(rng.random_vec(m_wots_params.element_size())) {}
+
+ /**
+ * Creates a XMSS_WOTS_PrivateKey for the signature method identified by
+ * oid, with a precomputed public seed.
+ *
+ * @param oid Identifier for the selected signature method.
+ * @param public_seed A precomputed public seed of n-bytes length.
+ **/
+ XMSS_WOTS_PublicKey(XMSS_WOTS_Parameters::ots_algorithm_t oid,
+ secure_vector<uint8_t> public_seed)
+ : m_wots_params(oid),
+ m_hash(m_wots_params.hash_function_name()),
+ m_public_seed(public_seed) {}
+
+ /**
+ * Creates a XMSS_WOTS_PublicKey for the signature method identified by
+ * oid. The public seed will be initialized with a precomputed seed and
+ * and precomputed key data which should be derived from a
+ * XMSS_WOTS_PrivateKey.
+ *
+ * @param oid Ident:s/ifier for the selected signature methods.
+ * @param public_seed A precomputed public seed of n-bytes length.
+ * @param key Precomputed raw key data of the XMSS_WOTS_PublicKey.
+ **/
+ XMSS_WOTS_PublicKey(XMSS_WOTS_Parameters::ots_algorithm_t oid,
+ secure_vector<uint8_t>&& public_seed,
+ wots_keysig_t&& key)
+ : m_wots_params(oid),
+ m_hash(m_wots_params.hash_function_name()),
+ m_key(std::move(key)),
+ m_public_seed(std::move(public_seed))
+ {}
+
+ /**
+ * Creates a XMSS_WOTS_PublicKey for the signature method identified by
+ * oid. The public seed will be initialized with a precomputed seed and
+ * and precomputed key data which should be derived from a
+ * XMSS_WOTS_PrivateKey.
+ *
+ * @param oid Identifier for the selected signature methods.
+ * @param public_seed A precomputed public seed of n-bytes length.
+ * @param key Precomputed raw key data of the XMSS_WOTS_PublicKey.
+ **/
+ XMSS_WOTS_PublicKey(XMSS_WOTS_Parameters::ots_algorithm_t oid,
+ const secure_vector<uint8_t>& public_seed,
+ const wots_keysig_t& key)
+ : m_wots_params(oid),
+ m_hash(m_wots_params.hash_function_name()),
+ m_key(key),
+ m_public_seed(public_seed)
+ {}
+
+ /**
+ * Creates a XMSS_WOTS_PublicKey form a message and signature using
+ * Algorithm 6 WOTS_pkFromSig defined in the XMSS standard. This
+ * overload is used to verify a message using a public key.
+ *
+ * @param oid WOTSP algorithm identifier.
+ * @param msg A message.
+ * @param sig A WOTS signature for msg.
+ * @param adrs An XMSS_Address.
+ * @param public_seed The public public_seed.
+ **/
+ XMSS_WOTS_PublicKey(XMSS_WOTS_Parameters::ots_algorithm_t oid,
+ const secure_vector<uint8_t>& msg,
+ const wots_keysig_t& sig,
+ XMSS_Address& adrs,
+ const secure_vector<uint8_t>& public_seed)
+ : m_wots_params(oid),
+ m_hash(m_wots_params.hash_function_name()),
+ m_key(pub_key_from_signature(msg,
+ sig,
+ adrs,
+ public_seed)),
+ m_public_seed(public_seed)
+ {}
+
+ /**
+ * Retrieves the i-th element out of the length len chain of
+ * n-byte elements contained in the public key.
+ *
+ * @param i index of the element.
+ * @returns n-byte element addressed by i.
+ **/
+ const secure_vector<uint8_t>& operator[](size_t i) const { return m_key[i]; }
+ secure_vector<uint8_t>& operator[](size_t i) { return m_key[i]; }
+
+ /**
+ * Convert the key into the raw key data. The key becomes a length
+ * len vector of n-byte elements.
+ **/
+ operator const wots_keysig_t& () const { return m_key; }
+
+ /**
+ * Convert the key into the raw key data. The key becomes a length
+ * len vector of n-byte elements.
+ **/
+ operator wots_keysig_t& () { return m_key; }
+
+ const secure_vector<uint8_t>& public_seed() const { return m_public_seed; }
+
+ secure_vector<uint8_t>& public_seed() { return m_public_seed; }
+
+ void set_public_seed(const secure_vector<uint8_t>& public_seed)
+ {
+ m_public_seed = public_seed;
+ }
+
+ void set_public_seed(secure_vector<uint8_t>&& public_seed)
+ {
+ m_public_seed = std::move(public_seed);
+ }
+
+ const wots_keysig_t& key_data() const { return m_key; }
+
+ wots_keysig_t& key_data() { return m_key; }
+
+ void set_key_data(const wots_keysig_t& key_data)
+ {
+ m_key = key_data;
+ }
+
+ void set_key_data(wots_keysig_t&& key_data)
+ {
+ m_key = std::move(key_data);
+ }
+
+ const XMSS_WOTS_Parameters& wots_parameters() const
+ {
+ return m_wots_params;
+ }
+
+ std::string algo_name() const override
+ {
+ return m_wots_params.name();
+ }
+
+ AlgorithmIdentifier algorithm_identifier() const override
+ {
+ throw Not_Implemented("No AlgorithmIdentifier available for XMSS-WOTS.");
+ }
+
+ bool check_key(RandomNumberGenerator&, bool) const override
+ {
+ return true;
+ }
+
+ size_t estimated_strength() const override
+ {
+ return m_wots_params.estimated_strength();
+ }
+
+ size_t key_length() const override
+ {
+ return m_wots_params.estimated_strength();
+ }
+
+ std::vector<uint8_t> public_key_bits() const override
+ {
+ throw Not_Implemented("No key format defined for XMSS-WOTS");
+ }
+
+ bool operator==(const XMSS_WOTS_PublicKey& key)
+ {
+ return m_key == key.m_key;
+ }
+
+ bool operator!=(const XMSS_WOTS_PublicKey& key)
+ {
+ return !(*this == key);
+ }
+
+ protected:
+ /**
+ * Algorithm 2: Chaining Function.
+ *
+ * Takes an n-byte input string and transforms it into a the function
+ * result iterating the cryptographic hash function "F" steps times on
+ * 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
+ * thread.
+ *
+ * @param[out] x An n-byte input string, that will be transformed into
+ * the chaining function result.
+ * @param start_idx The start index.
+ * @param steps A number of steps.
+ * @param adrs An OTS Hash Address.
+ * @param public_seed A public seed.
+ * @param hash Instance of XMSS_Hash, that may only by the thead
+ * executing chain.
+ **/
+ void chain(secure_vector<uint8_t>& x,
+ size_t start_idx,
+ size_t steps,
+ XMSS_Address& adrs,
+ const secure_vector<uint8_t>& public_seed,
+ XMSS_Hash& hash);
+
+ /**
+ * Algorithm 2: Chaining Function.
+ *
+ * Takes an n-byte input string and transforms it into a the function
+ * result iterating the cryptographic hash function "F" steps times on
+ * the input x using the outputs of the PRNG "G".
+ *
+ * @param[out] x An n-byte input string, that will be transformed into
+ * the chaining function result.
+ * @param start_idx The start index.
+ * @param steps A number of steps.
+ * @param adrs An OTS Hash Address.
+ * @param public_seed A public seed.
+ **/
+ inline void chain(secure_vector<uint8_t>& x,
+ size_t start_idx,
+ size_t steps,
+ XMSS_Address& adrs,
+ const secure_vector<uint8_t>& public_seed)
+ {
+ chain(x, start_idx, steps, adrs, public_seed, m_hash);
+ }
+
+ XMSS_WOTS_Parameters m_wots_params;
+ XMSS_Hash m_hash;
+ wots_keysig_t m_key;
+ secure_vector<uint8_t> m_public_seed;
+
+ private:
+ /**
+ * Algorithm 6: "WOTS_pkFromSig"
+ * Computes a Winternitz One Time Signature+ public key from a message and
+ * its signature.
+ *
+ * @param msg A message.
+ * @param sig The signature for msg.
+ * @param adrs An address.
+ * @param public_seed A public_seed.
+ *
+ * @return Temporary WOTS+ public key.
+ **/
+ wots_keysig_t pub_key_from_signature(
+ const secure_vector<uint8_t>& msg,
+ const wots_keysig_t& sig,
+ XMSS_Address& adrs,
+ const secure_vector<uint8_t>& public_seed);
+ };
+
+/** A Winternitz One Time Signature private key for use with Extended Hash-Based
+ * Signatures.
+ **/
+class XMSS_WOTS_PrivateKey final : public virtual XMSS_WOTS_PublicKey,
+ public virtual Private_Key
+ {
+ public:
+ /**
+ * Creates a WOTS private key for the chosen XMSS WOTS signature method.
+ * Members need to be initialized manually.
+ *
+ * @param oid Identifier for the selected signature method.
+ **/
+ XMSS_WOTS_PrivateKey(XMSS_WOTS_Parameters::ots_algorithm_t oid)
+ : XMSS_WOTS_PublicKey(oid)
+ {}
+
+ /**
+ * Creates a WOTS private key for the chosen XMSS WOTS signature method.
+ *
+ * @param oid Identifier for the selected signature method.
+ * @param rng A random number generator to use for key generation.
+ **/
+ XMSS_WOTS_PrivateKey(XMSS_WOTS_Parameters::ots_algorithm_t oid,
+ RandomNumberGenerator& rng)
+ : XMSS_WOTS_PublicKey(oid, rng),
+ m_private_seed(rng.random_vec(m_wots_params.element_size()))
+ {
+ set_key_data(generate(m_private_seed));
+ }
+
+ /**
+ * Constructs a WOTS private key. Chains will be generated on demand
+ * applying a hash function to a unique value generated from a secret
+ * seed and a counter. The secret seed of length n, will be
+ * automatically generated using AutoSeeded_RNG(). "n" equals
+ * the element size of the chosen WOTS security parameter set.
+ *
+ * @param oid Identifier for the selected signature method.
+ * @param public_seed A public seed used for the pseudo random generation
+ * of public keys derived from this private key.
+ * @param rng A random number generator to use for key generation.
+ **/
+ XMSS_WOTS_PrivateKey(XMSS_WOTS_Parameters::ots_algorithm_t oid,
+ const secure_vector<uint8_t>& public_seed,
+ RandomNumberGenerator& rng)
+ : XMSS_WOTS_PublicKey(oid, public_seed),
+ m_private_seed(rng.random_vec(m_wots_params.element_size()))
+ {
+ set_key_data(generate(m_private_seed));
+ }
+
+ /**
+ * Constructs a WOTS private key. Chains will be generated on demand
+ * applying a hash function to a unique value generated from a secret
+ * seed and a counter. The secret seed of length n, will be
+ * automatically generated using AutoSeeded_RNG(). "n" equals
+ * the element size of the chosen WOTS security parameter set.
+ *
+ * @param oid Identifier for the selected signature method.
+ * @param public_seed A public seed used for the pseudo random generation
+ * of public keys derived from this private key.
+ **/
+ XMSS_WOTS_PrivateKey(XMSS_WOTS_Parameters::ots_algorithm_t oid,
+ const secure_vector<uint8_t>& public_seed)
+ : XMSS_WOTS_PublicKey(oid, public_seed)
+ {}
+
+ /**
+ * Constructs a WOTS private key. Chains will be generated on demand
+ * applying a hash function to a unique value generated from the
+ * secret seed and a counter.
+ *
+ * @param oid Identifier for the selected signature method.
+ * @param public_seed A public seed used for the pseudo random generation
+ * of public keys derived from this private key.
+ * @param private_seed A secret uniformly random n-byte value.
+ **/
+ XMSS_WOTS_PrivateKey(XMSS_WOTS_Parameters::ots_algorithm_t oid,
+ const secure_vector<uint8_t>& public_seed,
+ const secure_vector<uint8_t>& private_seed)
+ : XMSS_WOTS_PublicKey(oid, public_seed),
+ m_private_seed(private_seed)
+ {
+ set_key_data(generate(private_seed));
+ }
+
+ /**
+ * Retrieves the i-th WOTS private key using pseudo random key
+ * (re-)generation.
+ *
+ * This overload is used in multithreaded scenarios, where it is
+ * required to provide seperate instances of XMSS_Hash to each
+ * thread.
+ *
+ * @param i Index of the key to retrieve.
+ * @param hash Instance of XMSS_Hash, that may only be used by the
+ * thead executing at.
+ *
+ * @return WOTS secret key.
+ **/
+ wots_keysig_t at(size_t i, XMSS_Hash& hash);
+
+ /**
+ * Retrieves the i-th WOTS private key using pseudo random key
+ * (re-)generation.
+ *
+ * @param i Index of the key to retrieve.
+ *
+ * @return WOTS secret key.
+ **/
+ inline wots_keysig_t operator[](size_t i)
+ {
+ return this->at(i, m_hash);
+ }
+
+ /**
+ * Retrieves the i-th WOTS private key using pseudo random key
+ * (re-)generation.
+ *
+ * This overload is used in multithreaded scenarios, where it is
+ * required to provide seperate instances of XMSS_Hash to each
+ * thread.
+ *
+ * @param adrs The address of the key to retrieve.
+ * @param hash Instance of XMSS_Hash, that may only be used by the
+ * thead executing at.
+ *
+ * @return WOTS secret key.
+ **/
+ wots_keysig_t at(const XMSS_Address& adrs, XMSS_Hash& hash);
+
+ inline wots_keysig_t operator[](const XMSS_Address& adrs)
+ {
+ return this->at(adrs, m_hash);
+ }
+
+ wots_keysig_t generate_private_key(const secure_vector<uint8_t>& priv_seed);
+
+ /**
+ * Algorithm 4: "WOTS_genPK"
+ * Generates a Winternitz One Time Signature+ (WOTS+) Public Key from a
+ * given private key.
+ *
+ * @param adrs Hash function address encoding the address of the WOTS+
+ * key pair within a greater structure.
+ *
+ * @return A XMSS_WOTS_PublicKey.
+ **/
+ XMSS_WOTS_PublicKey generate_public_key(XMSS_Address& adrs);
+
+ /**
+ * Algorithm 4: "WOTS_genPK"
+ * Initializes a Winternitz One Time Signature+ (WOTS+) Public Key's
+ * key_data() member, with data derived from in_key_data using the
+ * WOTS chaining function.
+ *
+ * This overload is used in multithreaded scenarios, where it is
+ * required to provide seperate instances of XMSS_Hash to each
+ * thread.
+ *
+ * @param[out] pub_key Public key to initialize key_data() member on.
+ * @param in_key_data Input key material from private key used for
+ * public key generation.
+ * @param adrs Hash function address encoding the address of
+ * the WOTS+ key pair within a greater structure.
+ * @param hash Instance of XMSS_Hash, that may only by the thead
+ * executing generate_public_key.
+ **/
+ void generate_public_key(XMSS_WOTS_PublicKey& pub_key,
+ wots_keysig_t&& in_key_data,
+ XMSS_Address& adrs,
+ XMSS_Hash& hash);
+ /**
+ * Algorithm 4: "WOTS_genPK"
+ * Initializes a Winternitz One Time Signature+ (WOTS+) Public Key's
+ * key_data() member, with data derived from in_key_data using the
+ * WOTS chaining function.
+ *
+ * @param[out] pub_key Public key to initialize key_data() member on.
+ * @param in_key_data Input key material from private key used for
+ * public key generation.
+ * @param adrs Hash function address encoding the address of
+ * the WOTS+ key pair within a greater structure.
+ **/
+ inline void generate_public_key(XMSS_WOTS_PublicKey& pub_key,
+ wots_keysig_t&& in_key_data,
+ XMSS_Address& adrs)
+ {
+ generate_public_key(pub_key, std::forward<wots_keysig_t>(in_key_data), adrs, m_hash);
+ }
+
+ /**
+ * Algorithm 5: "WOTS_sign"
+ * Generates a signature from a private key and a message.
+ *
+ * @param msg A message to sign.
+ * @param adrs An OTS hash address identifying the WOTS+ key pair
+ * used for signing.
+ *
+ * @return signature for msg.
+ **/
+ inline wots_keysig_t sign(const secure_vector<uint8_t>& msg,
+ XMSS_Address& adrs)
+ {
+ return sign(msg, adrs, m_hash);
+ }
+
+ /**
+ * Algorithm 5: "WOTS_sign"
+ * 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
+ * thread.
+ *
+ * @param msg A message to sign.
+ * @param adrs An OTS hash address identifying the WOTS+ key pair
+ * used for signing.
+ * @param hash Instance of XMSS_Hash, that may only be used by the
+ * thead executing sign.
+ *
+ * @return signature for msg.
+ **/
+ wots_keysig_t sign(const secure_vector<uint8_t>& msg,
+ XMSS_Address& adrs,
+ XMSS_Hash& hash);
+
+ /**
+ * Retrieves the secret seed used to generate WOTS+ chains. The seed
+ * should be a uniformly random n-byte value.
+ *
+ * @return secret seed.
+ **/
+ const secure_vector<uint8_t>& private_seed() const
+ {
+ return m_private_seed;
+ }
+
+ /**
+ * Sets the secret seed used to generate WOTS+ chains. The seed
+ * should be a uniformly random n-byte value.
+ *
+ * @param private_seed Uniformly random n-byte value.
+ **/
+ void set_private_seed(const secure_vector<uint8_t>& private_seed)
+ {
+ m_private_seed = private_seed;
+ }
+
+ /**
+ * Sets the secret seed used to generate WOTS+ chains. The seed
+ * should be a uniformly random n-byte value.
+ *
+ * @param private_seed Uniformly random n-byte value.
+ **/
+ void set_private_seed(secure_vector<uint8_t>&& private_seed)
+ {
+ m_private_seed = std::move(private_seed);
+ }
+
+ AlgorithmIdentifier
+ pkcs8_algorithm_identifier() const override
+ {
+ throw Not_Implemented("No AlgorithmIdentifier available for XMSS-WOTS.");
+ }
+
+ secure_vector<uint8_t> private_key_bits() const override
+ {
+ throw Not_Implemented("No PKCS8 key format defined for XMSS-WOTS.");
+ }
+
+ private:
+ /**
+ * Algorithm 3: "Generating a WOTS+ Private Key".
+ * Generates a private key.
+ *
+ * This overload is used in multithreaded scenarios, where it is
+ * required to provide seperate instances of XMSS_Hash to each thread.
+ *
+ * @param private_seed Uniformly random n-byte value.
+ * @param[in] hash Instance of XMSS_Hash, that may only be used by the
+ * thead executing generate.
+ *
+ * @returns a vector of length key_size() of vectors of n bytes length
+ * containing uniformly random data.
+ **/
+ wots_keysig_t generate(const secure_vector<uint8_t>& private_seed,
+ XMSS_Hash& hash);
+
+ inline wots_keysig_t generate(const secure_vector<uint8_t>& private_seed)
+ {
+ return generate(private_seed, m_hash);
+ }
+
+ secure_vector<uint8_t> m_private_seed;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/pubkey/xmss/xmss_wots_addressed_privatekey.h b/comm/third_party/botan/src/lib/pubkey/xmss/xmss_wots_addressed_privatekey.h
new file mode 100644
index 0000000000..debd00ff18
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/xmss/xmss_wots_addressed_privatekey.h
@@ -0,0 +1,68 @@
+/**
+ * XMSS WOTS Addressed Private Key
+ * (C) 2016 Matthias Gierlings
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ **/
+
+#ifndef BOTAN_XMSS_WOTS_ADDRESSED_PRIVATEKEY_H_
+#define BOTAN_XMSS_WOTS_ADDRESSED_PRIVATEKEY_H_
+
+#include <botan/internal/xmss_address.h>
+#include <botan/internal/xmss_wots_addressed_publickey.h>
+#include <botan/xmss_wots.h>
+
+namespace Botan {
+
+/**
+ * Wrapper class to pair an XMSS_WOTS_PrivateKey with an XMSS Address. Since
+ * the PK_Ops::Signature interface does not allow an extra address
+ * parameter to be passed to the sign(RandomNumberGenerator&), the address
+ * needs to be stored together with the key and passed to the
+ * XMSS_WOTS_Signature_Operation() on creation.
+ **/
+class XMSS_WOTS_Addressed_PrivateKey final :
+ public virtual XMSS_WOTS_Addressed_PublicKey,
+ public virtual Private_Key
+ {
+ public:
+ XMSS_WOTS_Addressed_PrivateKey(const XMSS_WOTS_PrivateKey& private_key)
+ : XMSS_WOTS_Addressed_PublicKey(private_key),
+ m_priv_key(private_key) {}
+
+ XMSS_WOTS_Addressed_PrivateKey(const XMSS_WOTS_PrivateKey& private_key,
+ const XMSS_Address& adrs)
+ : XMSS_WOTS_Addressed_PublicKey(private_key, adrs),
+ m_priv_key(private_key) {}
+
+ XMSS_WOTS_Addressed_PrivateKey(XMSS_WOTS_PrivateKey&& private_key)
+ : XMSS_WOTS_Addressed_PublicKey(XMSS_WOTS_PublicKey(private_key)),
+ m_priv_key(std::move(private_key)) {}
+
+ XMSS_WOTS_Addressed_PrivateKey(XMSS_WOTS_PrivateKey&& private_key,
+ XMSS_Address&& adrs)
+ : XMSS_WOTS_Addressed_PublicKey(XMSS_WOTS_PublicKey(private_key),
+ std::move(adrs)),
+ m_priv_key(std::move(private_key)) {}
+
+ const XMSS_WOTS_PrivateKey& private_key() const { return m_priv_key; }
+ XMSS_WOTS_PrivateKey& private_key() { return m_priv_key; }
+
+ AlgorithmIdentifier
+ pkcs8_algorithm_identifier() const override
+ {
+ return m_priv_key.pkcs8_algorithm_identifier();
+ }
+
+ secure_vector<uint8_t> private_key_bits() const override
+ {
+ return m_priv_key.private_key_bits();
+ }
+
+ private:
+ XMSS_WOTS_PrivateKey m_priv_key;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/pubkey/xmss/xmss_wots_addressed_publickey.h b/comm/third_party/botan/src/lib/pubkey/xmss/xmss_wots_addressed_publickey.h
new file mode 100644
index 0000000000..78d150d214
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/xmss/xmss_wots_addressed_publickey.h
@@ -0,0 +1,96 @@
+/**
+ * XMSS WOTS Addressed Public Key
+ * (C) 2016,2017 Matthias Gierlings
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ **/
+
+
+#ifndef BOTAN_XMSS_WOTS_ADDRESSED_PUBLICKEY_H_
+#define BOTAN_XMSS_WOTS_ADDRESSED_PUBLICKEY_H_
+
+#include <botan/internal/xmss_address.h>
+#include <botan/xmss_wots.h>
+
+namespace Botan {
+
+/**
+ * Wrapper class to pair a XMSS_WOTS_PublicKey with an XMSS Address. Since
+ * the PK_Ops::Verification interface does not allow an extra address
+ * parameter to be passed to the sign(RandomNumberGenerator&), the address
+ * needs to be stored together with the key and passed to the
+ * XMSS_WOTS_Verification_Operation() on creation.
+ **/
+class XMSS_WOTS_Addressed_PublicKey : public virtual Public_Key
+ {
+ public:
+ XMSS_WOTS_Addressed_PublicKey(const XMSS_WOTS_PublicKey& public_key)
+ : m_pub_key(public_key), m_adrs() {}
+
+ XMSS_WOTS_Addressed_PublicKey(const XMSS_WOTS_PublicKey& public_key,
+ const XMSS_Address& adrs)
+ : m_pub_key(public_key), m_adrs(adrs) {}
+
+ XMSS_WOTS_Addressed_PublicKey(XMSS_WOTS_PublicKey&& public_key)
+ : m_pub_key(std::move(public_key)), m_adrs() {}
+
+ XMSS_WOTS_Addressed_PublicKey(XMSS_WOTS_PublicKey&& public_key,
+ XMSS_Address&& adrs)
+ : m_pub_key(std::move(public_key)), m_adrs(std::move(adrs)) {}
+
+ const XMSS_WOTS_PublicKey& public_key() const { return m_pub_key; }
+ XMSS_WOTS_PublicKey& public_key() { return m_pub_key; }
+
+ const XMSS_Address& address() const { return m_adrs; }
+ XMSS_Address& address() { return m_adrs; }
+
+ std::string algo_name() const override
+ {
+ return m_pub_key.algo_name();
+ }
+
+ AlgorithmIdentifier algorithm_identifier() const override
+ {
+ return m_pub_key.algorithm_identifier();
+ }
+
+ bool check_key(RandomNumberGenerator& rng, bool strong) const override
+ {
+ return m_pub_key.check_key(rng, strong);
+ }
+
+ std::unique_ptr<PK_Ops::Verification>
+ create_verification_op(const std::string& params,
+ const std::string& provider) const override
+ {
+ return m_pub_key.create_verification_op(params, provider);
+ }
+
+ OID get_oid() const override
+ {
+ return m_pub_key.get_oid();
+ }
+
+ size_t estimated_strength() const override
+ {
+ return m_pub_key.estimated_strength();
+ }
+
+ size_t key_length() const override
+ {
+ return m_pub_key.estimated_strength();
+ }
+
+ std::vector<uint8_t> public_key_bits() const override
+ {
+ return m_pub_key.public_key_bits();
+ }
+
+ protected:
+ XMSS_WOTS_PublicKey m_pub_key;
+ XMSS_Address m_adrs;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/pubkey/xmss/xmss_wots_parameters.cpp b/comm/third_party/botan/src/lib/pubkey/xmss/xmss_wots_parameters.cpp
new file mode 100644
index 0000000000..ef89c081c0
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/xmss/xmss_wots_parameters.cpp
@@ -0,0 +1,137 @@
+/*
+ * XMSS WOTS Parameters
+ * Descibes a signature method for XMSS Winternitz One Time Signatures,
+ * as defined in:
+ * [1] XMSS: Extended Hash-Based Signatures,
+ * Request for Comments: 8391
+ * Release: May 2018.
+ * https://datatracker.ietf.org/doc/rfc8391/
+ *
+ * (C) 2016,2017,2018 Matthias Gierlings
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ **/
+
+#include <botan/xmss_wots.h>
+#include <botan/internal/xmss_tools.h>
+#include <botan/exceptn.h>
+#include <cmath>
+
+namespace Botan {
+
+XMSS_WOTS_Parameters::ots_algorithm_t
+XMSS_WOTS_Parameters::xmss_wots_id_from_string(const std::string& 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; }
+ throw Invalid_Argument("Unknown XMSS-WOTS algorithm param '" + param_set + "'");
+ }
+
+XMSS_WOTS_Parameters::XMSS_WOTS_Parameters(const std::string& 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)
+ {
+ 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;
+ 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;
+ 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;
+ 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;
+ default:
+ throw Not_Implemented("Algorithm id does not match any known XMSS WOTS algorithm id.");
+ break;
+ }
+
+ m_lg_w = (m_w == 16) ? 4 : 2;
+ m_len_1 = static_cast<size_t>(std::ceil((8 * element_size()) / m_lg_w));
+ m_len_2 = static_cast<size_t>(
+ 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\" detedted.");
+ }
+
+secure_vector<uint8_t>
+XMSS_WOTS_Parameters::base_w(const secure_vector<uint8_t>& msg, size_t out_size) const
+ {
+ secure_vector<uint8_t> result;
+ 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<uint8_t>((total >> bits) & (m_w - 1)));
+ }
+ return result;
+ }
+
+secure_vector<uint8_t>
+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<size_t>(
+ std::ceil(static_cast<float>(m_len_2 * m_lg_w) / 8.f));
+ secure_vector<uint8_t> result;
+ XMSS_Tools::concat(result, value, len_2_bytes);
+ return base_w(result, m_len_2);
+ }
+
+void
+XMSS_WOTS_Parameters::append_checksum(secure_vector<uint8_t>& data)
+ {
+ size_t csum = 0;
+
+ for(size_t i = 0; i < data.size(); i++)
+ {
+ csum += wots_parameter() - 1 - data[i];
+ }
+
+ secure_vector<uint8_t> csum_bytes = base_w(csum);
+ std::move(csum_bytes.begin(), csum_bytes.end(), std::back_inserter(data));
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/pubkey/xmss/xmss_wots_parameters.h b/comm/third_party/botan/src/lib/pubkey/xmss/xmss_wots_parameters.h
new file mode 100644
index 0000000000..e8c3e2b4a3
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/xmss/xmss_wots_parameters.h
@@ -0,0 +1,14 @@
+/*
+ * XMSS WOTS Parameters
+ * (C) 2016,2018 Matthias Gierlings
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ **/
+
+#ifndef BOTAN_XMSS_WOTS_PARAMETERS_H_
+#define BOTAN_XMSS_WOTS_PARAMETERS_H_
+
+#include <botan/xmss_wots.h>
+BOTAN_DEPRECATED_HEADER(xmss_wots_parameters.h)
+
+#endif
diff --git a/comm/third_party/botan/src/lib/pubkey/xmss/xmss_wots_privatekey.cpp b/comm/third_party/botan/src/lib/pubkey/xmss/xmss_wots_privatekey.cpp
new file mode 100644
index 0000000000..7293f2538a
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/xmss/xmss_wots_privatekey.cpp
@@ -0,0 +1,99 @@
+/*
+ * XMSS WOTS Private Key
+ * A Winternitz One Time Signature private key for use with Extended Hash-Based
+ * Signatures.
+ *
+ * (C) 2016,2017 Matthias Gierlings
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ **/
+
+#include <botan/xmss_wots.h>
+#include <botan/internal/xmss_tools.h>
+#include <botan/internal/xmss_address.h>
+
+namespace Botan {
+
+wots_keysig_t
+XMSS_WOTS_PrivateKey::generate(const secure_vector<uint8_t>& priv_seed,
+ XMSS_Hash& hash)
+ {
+ wots_keysig_t priv_key(m_wots_params.len(),
+ secure_vector<uint8_t>(0));
+
+ for(size_t i = 0; i < m_wots_params.len(); i++)
+ {
+ XMSS_Tools::concat<size_t>(priv_key[i], i, 32);
+ hash.prf(priv_key[i], priv_seed, priv_key[i]);
+ }
+ return priv_key;
+ }
+
+
+XMSS_WOTS_PublicKey
+XMSS_WOTS_PrivateKey::generate_public_key(XMSS_Address& adrs)
+ {
+ XMSS_WOTS_PublicKey pub_key(m_wots_params.oid(),
+ public_seed());
+ generate_public_key(pub_key, wots_keysig_t((*this)[adrs]), adrs);
+ return pub_key;
+ }
+
+void
+XMSS_WOTS_PrivateKey::generate_public_key(XMSS_WOTS_PublicKey& pub_key,
+ wots_keysig_t&& in_key_data,
+ XMSS_Address& adrs,
+ XMSS_Hash& hash)
+ {
+ BOTAN_ASSERT(wots_parameters() == pub_key.wots_parameters() &&
+ public_seed() == pub_key.public_seed(),
+ "Conflicting public key data.");
+
+ pub_key.set_key_data(std::move(in_key_data));
+ for(size_t i = 0; i < m_wots_params.len(); i++)
+ {
+ adrs.set_chain_address(static_cast<uint32_t>(i));
+ chain(pub_key[i], 0, m_wots_params.wots_parameter() - 1, adrs,
+ public_seed(), hash);
+ }
+ }
+
+wots_keysig_t
+XMSS_WOTS_PrivateKey::sign(const secure_vector<uint8_t>& msg,
+ XMSS_Address& adrs,
+ XMSS_Hash& hash)
+
+ {
+ secure_vector<uint8_t> msg_digest
+ {
+ m_wots_params.base_w(msg, m_wots_params.len_1())
+ };
+
+ m_wots_params.append_checksum(msg_digest);
+ wots_keysig_t sig(this->at(adrs, hash));
+
+ for(size_t i = 0; i < m_wots_params.len(); i++)
+ {
+ adrs.set_chain_address(static_cast<uint32_t>(i));
+ chain(sig[i], 0 , msg_digest[i], adrs, m_public_seed, hash);
+ }
+
+ return sig;
+ }
+
+wots_keysig_t XMSS_WOTS_PrivateKey::at(const XMSS_Address& adrs, XMSS_Hash& hash)
+ {
+ secure_vector<uint8_t> result;
+ hash.prf(result, m_private_seed, adrs.bytes());
+ return generate(result, hash);
+ }
+
+wots_keysig_t XMSS_WOTS_PrivateKey::at(size_t i, XMSS_Hash& hash)
+ {
+ secure_vector<uint8_t> idx_bytes;
+ XMSS_Tools::concat(idx_bytes, i, m_wots_params.element_size());
+ hash.h(idx_bytes, m_private_seed, idx_bytes);
+ return generate(idx_bytes, hash);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/pubkey/xmss/xmss_wots_privatekey.h b/comm/third_party/botan/src/lib/pubkey/xmss/xmss_wots_privatekey.h
new file mode 100644
index 0000000000..2d631598bb
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/xmss/xmss_wots_privatekey.h
@@ -0,0 +1,15 @@
+/*
+ * XMSS WOTS Private Key
+ * (C) 2016,2017 Matthias Gierlings
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ **/
+
+#ifndef BOTAN_XMSS_WOTS_PRIVATEKEY_H_
+#define BOTAN_XMSS_WOTS_PRIVATEKEY_H_
+
+#include <botan/xmss.h>
+BOTAN_DEPRECATED_HEADER(xmss_wots_privatekey.h)
+
+#endif
+
diff --git a/comm/third_party/botan/src/lib/pubkey/xmss/xmss_wots_publickey.cpp b/comm/third_party/botan/src/lib/pubkey/xmss/xmss_wots_publickey.cpp
new file mode 100644
index 0000000000..70ff1b7bad
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/xmss/xmss_wots_publickey.cpp
@@ -0,0 +1,72 @@
+/*
+ * XMSS WOTS Public Key
+ * A Winternitz One Time Signature public key for use with Extended Hash-Based
+ * Signatures.
+ *
+ * (C) 2016,2017,2018 Matthias Gierlings
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ **/
+
+#include <botan/xmss_wots.h>
+#include <botan/internal/xmss_address.h>
+
+namespace Botan {
+
+void
+XMSS_WOTS_PublicKey::chain(secure_vector<uint8_t>& result,
+ size_t start_idx,
+ size_t steps,
+ XMSS_Address& adrs,
+ const secure_vector<uint8_t>& seed,
+ XMSS_Hash& hash)
+ {
+ secure_vector<uint8_t> prf_output(hash.output_length());
+
+ for(size_t i = start_idx;
+ i < (start_idx + steps) && i < m_wots_params.wots_parameter();
+ i++)
+ {
+ adrs.set_hash_address(static_cast<uint32_t>(i));
+
+ //Calculate tmp XOR bitmask
+ adrs.set_key_mask_mode(XMSS_Address::Key_Mask::Mask_Mode);
+ hash.prf(prf_output, seed, adrs.bytes());
+ xor_buf(result, prf_output, result.size());
+
+ // Calculate key
+ adrs.set_key_mask_mode(XMSS_Address::Key_Mask::Key_Mode);
+
+ //Calculate f(key, tmp XOR bitmask)
+ hash.prf(prf_output, seed, adrs.bytes());
+ hash.f(result, prf_output, result);
+ }
+ }
+
+wots_keysig_t
+XMSS_WOTS_PublicKey::pub_key_from_signature(const secure_vector<uint8_t>& msg,
+ const wots_keysig_t& sig,
+ XMSS_Address& adrs,
+ const secure_vector<uint8_t>& seed)
+ {
+ secure_vector<uint8_t> msg_digest
+ {
+ m_wots_params.base_w(msg, m_wots_params.len_1())
+ };
+
+ m_wots_params.append_checksum(msg_digest);
+ wots_keysig_t result(sig);
+
+ for(size_t i = 0; i < m_wots_params.len(); i++)
+ {
+ adrs.set_chain_address(static_cast<uint32_t>(i));
+ chain(result[i],
+ msg_digest[i],
+ m_wots_params.wots_parameter() - 1 - msg_digest[i],
+ adrs,
+ seed);
+ }
+ return result;
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/pubkey/xmss/xmss_wots_publickey.h b/comm/third_party/botan/src/lib/pubkey/xmss/xmss_wots_publickey.h
new file mode 100644
index 0000000000..796bf4c308
--- /dev/null
+++ b/comm/third_party/botan/src/lib/pubkey/xmss/xmss_wots_publickey.h
@@ -0,0 +1,14 @@
+/*
+ * XMSS WOTS Public Key
+ * (C) 2016,2017,2018 Matthias Gierlings
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ **/
+
+#ifndef BOTAN_XMSS_WOTS_PUBLICKEY_H_
+#define BOTAN_XMSS_WOTS_PUBLICKEY_H_
+
+#include <botan/xmss.h>
+BOTAN_DEPRECATED_HEADER(xmss_wots_publickey.h)
+
+#endif
diff --git a/comm/third_party/botan/src/lib/rng/auto_rng/auto_rng.cpp b/comm/third_party/botan/src/lib/rng/auto_rng/auto_rng.cpp
new file mode 100644
index 0000000000..a13429e9b5
--- /dev/null
+++ b/comm/third_party/botan/src/lib/rng/auto_rng/auto_rng.cpp
@@ -0,0 +1,112 @@
+/*
+* (C) 2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/auto_rng.h>
+#include <botan/entropy_src.h>
+#include <botan/hmac_drbg.h>
+
+#if defined(BOTAN_HAS_SYSTEM_RNG)
+ #include <botan/system_rng.h>
+#endif
+
+#if !defined(BOTAN_AUTO_RNG_HMAC)
+#error "No hash function defined for AutoSeeded_RNG in build.h (try enabling sha2_32)"
+#endif
+
+namespace Botan {
+
+AutoSeeded_RNG::~AutoSeeded_RNG()
+ {
+ // for unique_ptr
+ }
+
+AutoSeeded_RNG::AutoSeeded_RNG(RandomNumberGenerator& underlying_rng,
+ size_t reseed_interval)
+ {
+ m_rng.reset(new HMAC_DRBG(MessageAuthenticationCode::create_or_throw(BOTAN_AUTO_RNG_HMAC),
+ underlying_rng,
+ reseed_interval));
+ force_reseed();
+ }
+
+AutoSeeded_RNG::AutoSeeded_RNG(Entropy_Sources& entropy_sources,
+ size_t reseed_interval)
+ {
+ m_rng.reset(new HMAC_DRBG(MessageAuthenticationCode::create_or_throw(BOTAN_AUTO_RNG_HMAC),
+ entropy_sources,
+ reseed_interval));
+ force_reseed();
+ }
+
+AutoSeeded_RNG::AutoSeeded_RNG(RandomNumberGenerator& underlying_rng,
+ Entropy_Sources& entropy_sources,
+ size_t reseed_interval)
+ {
+ m_rng.reset(new HMAC_DRBG(
+ MessageAuthenticationCode::create_or_throw(BOTAN_AUTO_RNG_HMAC),
+ underlying_rng, entropy_sources, reseed_interval));
+ force_reseed();
+ }
+
+AutoSeeded_RNG::AutoSeeded_RNG(size_t reseed_interval) :
+#if defined(BOTAN_HAS_SYSTEM_RNG)
+ AutoSeeded_RNG(system_rng(), reseed_interval)
+#else
+ AutoSeeded_RNG(Entropy_Sources::global_sources(), reseed_interval)
+#endif
+ {
+ }
+
+void AutoSeeded_RNG::force_reseed()
+ {
+ m_rng->force_reseed();
+ m_rng->next_byte();
+
+ if(!m_rng->is_seeded())
+ {
+ throw Internal_Error("AutoSeeded_RNG reseeding failed");
+ }
+ }
+
+bool AutoSeeded_RNG::is_seeded() const
+ {
+ return m_rng->is_seeded();
+ }
+
+void AutoSeeded_RNG::clear()
+ {
+ m_rng->clear();
+ }
+
+std::string AutoSeeded_RNG::name() const
+ {
+ return m_rng->name();
+ }
+
+void AutoSeeded_RNG::add_entropy(const uint8_t in[], size_t len)
+ {
+ m_rng->add_entropy(in, len);
+ }
+
+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);
+ }
+
+void AutoSeeded_RNG::randomize(uint8_t output[], size_t output_len)
+ {
+ m_rng->randomize_with_ts_input(output, output_len);
+ }
+
+void AutoSeeded_RNG::randomize_with_input(uint8_t output[], size_t output_len,
+ const uint8_t ad[], size_t ad_len)
+ {
+ m_rng->randomize_with_input(output, output_len, ad, ad_len);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/rng/auto_rng/auto_rng.h b/comm/third_party/botan/src/lib/rng/auto_rng/auto_rng.h
new file mode 100644
index 0000000000..8cb2c4a127
--- /dev/null
+++ b/comm/third_party/botan/src/lib/rng/auto_rng/auto_rng.h
@@ -0,0 +1,102 @@
+/*
+* Auto Seeded RNG
+* (C) 2008,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_AUTO_SEEDING_RNG_H_
+#define BOTAN_AUTO_SEEDING_RNG_H_
+
+#include <botan/rng.h>
+
+namespace Botan {
+
+class Stateful_RNG;
+
+/**
+* A userspace PRNG
+*/
+class BOTAN_PUBLIC_API(2,0) AutoSeeded_RNG final : public RandomNumberGenerator
+ {
+ public:
+ void randomize(uint8_t out[], size_t len) override;
+
+ void randomize_with_input(uint8_t output[], size_t output_len,
+ const uint8_t input[], size_t input_len) override;
+
+ bool is_seeded() const override;
+
+ bool accepts_input() const override { return true; }
+
+ /**
+ * Mark state as requiring a reseed on next use
+ */
+ 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;
+
+ void add_entropy(const uint8_t in[], size_t len) override;
+
+ std::string name() const override;
+
+ void clear() override;
+
+ /**
+ * Uses the system RNG (if available) or else a default group of
+ * entropy sources (all other systems) to gather seed material.
+ *
+ * @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);
+
+ /**
+ * Create an AutoSeeded_RNG which will get seed material from some other
+ * RNG instance. For example you could provide a reference to the system
+ * RNG or a hardware RNG.
+ *
+ * @param underlying_rng is a reference to some RNG which will be used
+ * to perform the periodic reseeding
+ * @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);
+
+ /**
+ * Create an AutoSeeded_RNG which will get seed material from a set of
+ * entropy sources.
+ *
+ * @param entropy_sources will be polled to perform reseeding periodically
+ * @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);
+
+ /**
+ * Create an AutoSeeded_RNG which will get seed material from both an
+ * underlying RNG and a set of entropy sources.
+ *
+ * @param underlying_rng is a reference to some RNG which will be used
+ * to perform the periodic reseeding
+ * @param entropy_sources will be polled to perform reseeding periodically
+ * @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,
+ Entropy_Sources& entropy_sources,
+ size_t reseed_interval = BOTAN_RNG_DEFAULT_RESEED_INTERVAL);
+
+ ~AutoSeeded_RNG();
+
+ private:
+ std::unique_ptr<Stateful_RNG> m_rng;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/rng/auto_rng/info.txt b/comm/third_party/botan/src/lib/rng/auto_rng/info.txt
new file mode 100644
index 0000000000..f1adcc8001
--- /dev/null
+++ b/comm/third_party/botan/src/lib/rng/auto_rng/info.txt
@@ -0,0 +1,8 @@
+<defines>
+AUTO_SEEDING_RNG -> 20160821
+AUTO_RNG -> 20161126
+</defines>
+
+<requires>
+hmac_drbg
+</requires>
diff --git a/comm/third_party/botan/src/lib/rng/chacha_rng/chacha_rng.cpp b/comm/third_party/botan/src/lib/rng/chacha_rng/chacha_rng.cpp
new file mode 100644
index 0000000000..3dc69ec1b8
--- /dev/null
+++ b/comm/third_party/botan/src/lib/rng/chacha_rng/chacha_rng.cpp
@@ -0,0 +1,87 @@
+/*
+* ChaCha_RNG
+* (C) 2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/chacha_rng.h>
+
+namespace Botan {
+
+ChaCha_RNG::ChaCha_RNG() : Stateful_RNG()
+ {
+ m_hmac = MessageAuthenticationCode::create_or_throw("HMAC(SHA-256)");
+ m_chacha = StreamCipher::create_or_throw("ChaCha(20)");
+ clear();
+ }
+
+ChaCha_RNG::ChaCha_RNG(const secure_vector<uint8_t>& seed) : Stateful_RNG()
+ {
+ m_hmac = MessageAuthenticationCode::create_or_throw("HMAC(SHA-256)");
+ m_chacha = StreamCipher::create_or_throw("ChaCha(20)");
+ clear();
+ add_entropy(seed.data(), seed.size());
+ }
+
+ChaCha_RNG::ChaCha_RNG(RandomNumberGenerator& underlying_rng,
+ size_t reseed_interval) :
+ Stateful_RNG(underlying_rng, reseed_interval)
+ {
+ m_hmac = MessageAuthenticationCode::create_or_throw("HMAC(SHA-256)");
+ m_chacha = StreamCipher::create_or_throw("ChaCha(20)");
+ clear();
+ }
+
+ChaCha_RNG::ChaCha_RNG(RandomNumberGenerator& underlying_rng,
+ Entropy_Sources& entropy_sources,
+ size_t reseed_interval) :
+ Stateful_RNG(underlying_rng, entropy_sources, reseed_interval)
+ {
+ m_hmac = MessageAuthenticationCode::create_or_throw("HMAC(SHA-256)");
+ m_chacha = StreamCipher::create_or_throw("ChaCha(20)");
+ clear();
+ }
+
+ChaCha_RNG::ChaCha_RNG(Entropy_Sources& entropy_sources,
+ size_t reseed_interval) :
+ Stateful_RNG(entropy_sources, reseed_interval)
+ {
+ m_hmac = MessageAuthenticationCode::create_or_throw("HMAC(SHA-256)");
+ m_chacha = StreamCipher::create_or_throw("ChaCha(20)");
+ clear();
+ }
+
+void ChaCha_RNG::clear_state()
+ {
+ m_hmac->set_key(std::vector<uint8_t>(m_hmac->output_length(), 0x00));
+ m_chacha->set_key(m_hmac->final());
+ }
+
+void ChaCha_RNG::generate_output(uint8_t output[], size_t output_len,
+ const uint8_t input[], size_t input_len)
+ {
+ if(input_len > 0)
+ {
+ update(input, input_len);
+ }
+
+ m_chacha->write_keystream(output, output_len);
+ }
+
+void ChaCha_RNG::update(const uint8_t input[], size_t input_len)
+ {
+ m_hmac->update(input, input_len);
+ m_chacha->set_key(m_hmac->final());
+
+ secure_vector<uint8_t> mac_key(m_hmac->output_length());
+ m_chacha->write_keystream(mac_key.data(), mac_key.size());
+ m_hmac->set_key(mac_key);
+ }
+
+size_t ChaCha_RNG::security_level() const
+ {
+ return 256;
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/rng/chacha_rng/chacha_rng.h b/comm/third_party/botan/src/lib/rng/chacha_rng/chacha_rng.h
new file mode 100644
index 0000000000..c50c2d0c25
--- /dev/null
+++ b/comm/third_party/botan/src/lib/rng/chacha_rng/chacha_rng.h
@@ -0,0 +1,125 @@
+/*
+* ChaCha_RNG
+* (C) 2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_CHACHA_RNG_H_
+#define BOTAN_CHACHA_RNG_H_
+
+#include <botan/stateful_rng.h>
+#include <botan/stream_cipher.h>
+#include <botan/mac.h>
+
+namespace Botan {
+
+class Entropy_Sources;
+
+/**
+* ChaCha_RNG is a very fast but completely ad-hoc RNG created by
+* creating a 256-bit random value and using it as a key for ChaCha20.
+*
+* The RNG maintains two 256-bit keys, one for HMAC_SHA256 (HK) and the
+* other for ChaCha20 (CK). To compute a new key in response to
+* reseeding request or add_entropy calls, ChaCha_RNG computes
+* CK' = HMAC_SHA256(HK, input_material)
+* Then a new HK' is computed by running ChaCha20 with the new key to
+* output 32 bytes:
+* HK' = ChaCha20(CK')
+*
+* Now output can be produced by continuing to produce output with ChaCha20
+* under CK'
+*
+* The first HK (before seeding occurs) is taken as the all zero value.
+*
+* @warning This RNG construction is probably fine but is non-standard.
+* The primary reason to use it is in cases where the other RNGs are
+* not fast enough.
+*/
+class BOTAN_PUBLIC_API(2,3) ChaCha_RNG final : public Stateful_RNG
+ {
+ public:
+ /**
+ * Automatic reseeding is disabled completely, as it has no access to
+ * any source for seed material.
+ *
+ * If a fork is detected, the RNG will be unable to reseed itself
+ * in response. In this case, an exception will be thrown rather
+ * than generating duplicated output.
+ */
+ ChaCha_RNG();
+
+ /**
+ * Provide an initial seed to the RNG, without providing an
+ * underlying RNG or entropy source. Automatic reseeding is
+ * disabled completely, as it has no access to any source for
+ * seed material.
+ *
+ * If a fork is detected, the RNG will be unable to reseed itself
+ * in response. In this case, an exception will be thrown rather
+ * than generating duplicated output.
+ *
+ * @param seed the seed material, should be at least 256 bits
+ */
+ ChaCha_RNG(const secure_vector<uint8_t>& seed);
+
+ /**
+ * Automatic reseeding from @p underlying_rng will take place after
+ * @p reseed_interval many requests or after a fork was detected.
+ *
+ * @param underlying_rng is a reference to some RNG which will be used
+ * to perform the periodic reseeding
+ * @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);
+
+ /**
+ * Automatic reseeding from @p entropy_sources will take place after
+ * @p reseed_interval many requests or after a fork was detected.
+ *
+ * @param entropy_sources will be polled to perform reseeding periodically
+ * @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);
+
+ /**
+ * Automatic reseeding from @p underlying_rng and @p entropy_sources
+ * will take place after @p reseed_interval many requests or after
+ * a fork was detected.
+ *
+ * @param underlying_rng is a reference to some RNG which will be used
+ * to perform the periodic reseeding
+ * @param entropy_sources will be polled to perform reseeding periodically
+ * @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,
+ Entropy_Sources& entropy_sources,
+ size_t reseed_interval = BOTAN_RNG_DEFAULT_RESEED_INTERVAL);
+
+ std::string name() const override { return "ChaCha_RNG"; }
+
+ size_t security_level() const override;
+
+ size_t max_number_of_bytes_per_request() const override { return 0; }
+
+ private:
+ void update(const uint8_t input[], size_t input_len) override;
+
+ void generate_output(uint8_t output[], size_t output_len,
+ const uint8_t input[], size_t input_len) override;
+
+ void clear_state() override;
+
+ std::unique_ptr<MessageAuthenticationCode> m_hmac;
+ std::unique_ptr<StreamCipher> m_chacha;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/rng/chacha_rng/info.txt b/comm/third_party/botan/src/lib/rng/chacha_rng/info.txt
new file mode 100644
index 0000000000..3f51bf4d0e
--- /dev/null
+++ b/comm/third_party/botan/src/lib/rng/chacha_rng/info.txt
@@ -0,0 +1,10 @@
+<defines>
+CHACHA_RNG -> 20170728
+</defines>
+
+<requires>
+hmac
+sha2_32
+chacha
+stateful_rng
+</requires>
diff --git a/comm/third_party/botan/src/lib/rng/hmac_drbg/hmac_drbg.cpp b/comm/third_party/botan/src/lib/rng/hmac_drbg/hmac_drbg.cpp
new file mode 100644
index 0000000000..2b66a839c3
--- /dev/null
+++ b/comm/third_party/botan/src/lib/rng/hmac_drbg/hmac_drbg.cpp
@@ -0,0 +1,197 @@
+/*
+* HMAC_DRBG
+* (C) 2014,2015,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/hmac_drbg.h>
+#include <algorithm>
+
+namespace Botan {
+
+namespace {
+
+size_t hmac_drbg_security_level(size_t mac_output_length)
+ {
+ // security strength of the hash function
+ // for pre-image resistance (see NIST SP 800-57)
+ // SHA-160: 128 bits
+ // SHA-224, SHA-512/224: 192 bits,
+ // 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 < 32)
+ {
+ return (mac_output_length - 4) * 8;
+ }
+ else
+ {
+ return 32 * 8;
+ }
+ }
+
+void check_limits(size_t reseed_interval,
+ size_t max_number_of_bytes_per_request)
+ {
+ // SP800-90A permits up to 2^48, but it is not usable on 32 bit
+ // platforms, so we only allow up to 2^24, which is still reasonably high
+ if(reseed_interval == 0 || reseed_interval > static_cast<size_t>(1) << 24)
+ {
+ throw Invalid_Argument("Invalid value for reseed_interval");
+ }
+
+ if(max_number_of_bytes_per_request == 0 || max_number_of_bytes_per_request > 64 * 1024)
+ {
+ throw Invalid_Argument("Invalid value for max_number_of_bytes_per_request");
+ }
+ }
+
+}
+
+HMAC_DRBG::HMAC_DRBG(std::unique_ptr<MessageAuthenticationCode> 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_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<MessageAuthenticationCode> prf,
+ RandomNumberGenerator& underlying_rng,
+ Entropy_Sources& entropy_sources,
+ 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_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<MessageAuthenticationCode> prf,
+ Entropy_Sources& entropy_sources,
+ size_t reseed_interval,
+ size_t max_number_of_bytes_per_request) :
+ Stateful_RNG(entropy_sources, reseed_interval),
+ m_mac(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<MessageAuthenticationCode> prf) :
+ Stateful_RNG(),
+ m_mac(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(const std::string& hmac_hash) :
+ Stateful_RNG(),
+ m_mac(MessageAuthenticationCode::create_or_throw("HMAC(" + hmac_hash + ")")),
+ m_max_number_of_bytes_per_request(64 * 1024),
+ m_security_level(hmac_drbg_security_level(m_mac->output_length()))
+ {
+ clear();
+ }
+
+void HMAC_DRBG::clear_state()
+ {
+ if(m_V.size() == 0)
+ {
+ const size_t output_length = m_mac->output_length();
+ m_V.resize(output_length);
+ }
+
+ for(size_t i = 0; i != m_V.size(); ++i)
+ m_V[i] = 0x01;
+ m_mac->set_key(std::vector<uint8_t>(m_V.size(), 0x00));
+ }
+
+std::string HMAC_DRBG::name() const
+ {
+ return "HMAC_DRBG(" + m_mac->name() + ")";
+ }
+
+/*
+* HMAC_DRBG generation
+* See NIST SP800-90A section 10.1.2.5
+*/
+void HMAC_DRBG::generate_output(uint8_t output[], size_t output_len,
+ const uint8_t input[], size_t input_len)
+ {
+ if(input_len > 0)
+ {
+ update(input, input_len);
+ }
+
+ while(output_len > 0)
+ {
+ const size_t to_copy = std::min(output_len, m_V.size());
+ m_mac->update(m_V.data(), m_V.size());
+ m_mac->final(m_V.data());
+ copy_mem(output, m_V.data(), to_copy);
+
+ output += to_copy;
+ output_len -= to_copy;
+ }
+
+ update(input, input_len);
+ }
+
+/*
+* Reset V and the mac key with new values
+* See NIST SP800-90A section 10.1.2.2
+*/
+void HMAC_DRBG::update(const uint8_t input[], size_t input_len)
+ {
+ secure_vector<uint8_t> T(m_V.size());
+ m_mac->update(m_V);
+ m_mac->update(0x00);
+ m_mac->update(input, input_len);
+ m_mac->final(T.data());
+ m_mac->set_key(T);
+
+ m_mac->update(m_V.data(), m_V.size());
+ m_mac->final(m_V.data());
+
+ if(input_len > 0)
+ {
+ m_mac->update(m_V);
+ m_mac->update(0x01);
+ m_mac->update(input, input_len);
+ m_mac->final(T.data());
+ m_mac->set_key(T);
+
+ m_mac->update(m_V.data(), m_V.size());
+ m_mac->final(m_V.data());
+ }
+ }
+
+size_t HMAC_DRBG::security_level() const
+ {
+ return m_security_level;
+ }
+}
diff --git a/comm/third_party/botan/src/lib/rng/hmac_drbg/hmac_drbg.h b/comm/third_party/botan/src/lib/rng/hmac_drbg/hmac_drbg.h
new file mode 100644
index 0000000000..a4c288c74b
--- /dev/null
+++ b/comm/third_party/botan/src/lib/rng/hmac_drbg/hmac_drbg.h
@@ -0,0 +1,150 @@
+/*
+* HMAC_DRBG (SP800-90A)
+* (C) 2014,2015,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_HMAC_DRBG_H_
+#define BOTAN_HMAC_DRBG_H_
+
+#include <botan/stateful_rng.h>
+#include <botan/mac.h>
+
+namespace Botan {
+
+class Entropy_Sources;
+
+/**
+* HMAC_DRBG from NIST SP800-90A
+*/
+class BOTAN_PUBLIC_API(2,0) HMAC_DRBG final : public Stateful_RNG
+ {
+ public:
+ /**
+ * Initialize an HMAC_DRBG instance with the given MAC as PRF (normally HMAC)
+ *
+ * Automatic reseeding is disabled completely, as it has no access to
+ * any source for seed material.
+ *
+ * If a fork is detected, the RNG will be unable to reseed itself
+ * in response. In this case, an exception will be thrown rather
+ * than generating duplicated output.
+ */
+ explicit HMAC_DRBG(std::unique_ptr<MessageAuthenticationCode> prf);
+
+ /**
+ * Constructor taking a string for the hash
+ */
+ explicit HMAC_DRBG(const std::string& hmac_hash);
+
+ /**
+ * Initialize an HMAC_DRBG instance with the given MAC as PRF (normally HMAC)
+ *
+ * Automatic reseeding from @p underlying_rng will take place after
+ * @p reseed_interval many requests or after a fork was detected.
+ *
+ * @param prf MAC to use as a PRF
+ * @param underlying_rng is a reference to some RNG which will be used
+ * to perform the periodic reseeding
+ * @param reseed_interval specifies a limit of how many times
+ * the RNG will be called before automatic reseeding is performed (max. 2^24)
+ * @param max_number_of_bytes_per_request requests that are in size higher
+ * than max_number_of_bytes_per_request are treated as if multiple single
+ * requests of max_number_of_bytes_per_request size had been made.
+ * In theory SP 800-90A requires that we reject any request for a DRBG
+ * output longer than max_number_of_bytes_per_request. To avoid inconveniencing
+ * the caller who wants an output larger than max_number_of_bytes_per_request,
+ * instead treat these requests as if multiple requests of
+ * max_number_of_bytes_per_request size had been made. NIST requires for
+ * HMAC_DRBG that every implementation set a value no more than 2**19 bits
+ * (or 64 KiB). Together with @p reseed_interval = 1 you can enforce that for
+ * example every 512 bit automatic reseeding occurs.
+ */
+ HMAC_DRBG(std::unique_ptr<MessageAuthenticationCode> prf,
+ RandomNumberGenerator& underlying_rng,
+ size_t reseed_interval = BOTAN_RNG_DEFAULT_RESEED_INTERVAL,
+ size_t max_number_of_bytes_per_request = 64 * 1024);
+
+ /**
+ * Initialize an HMAC_DRBG instance with the given MAC as PRF (normally HMAC)
+ *
+ * Automatic reseeding from @p entropy_sources will take place after
+ * @p reseed_interval many requests or after a fork was detected.
+ *
+ * @param prf MAC to use as a PRF
+ * @param entropy_sources will be polled to perform reseeding periodically
+ * @param reseed_interval specifies a limit of how many times
+ * the RNG will be called before automatic reseeding is performed (max. 2^24)
+ * @param max_number_of_bytes_per_request requests that are in size higher
+ * than max_number_of_bytes_per_request are treated as if multiple single
+ * requests of max_number_of_bytes_per_request size had been made.
+ * In theory SP 800-90A requires that we reject any request for a DRBG
+ * output longer than max_number_of_bytes_per_request. To avoid inconveniencing
+ * the caller who wants an output larger than max_number_of_bytes_per_request,
+ * instead treat these requests as if multiple requests of
+ * max_number_of_bytes_per_request size had been made. NIST requires for
+ * HMAC_DRBG that every implementation set a value no more than 2**19 bits
+ * (or 64 KiB). Together with @p reseed_interval = 1 you can enforce that for
+ * example every 512 bit automatic reseeding occurs.
+ */
+ HMAC_DRBG(std::unique_ptr<MessageAuthenticationCode> prf,
+ Entropy_Sources& entropy_sources,
+ size_t reseed_interval = BOTAN_RNG_DEFAULT_RESEED_INTERVAL,
+ size_t max_number_of_bytes_per_request = 64 * 1024);
+
+ /**
+ * Initialize an HMAC_DRBG instance with the given MAC as PRF (normally HMAC)
+ *
+ * Automatic reseeding from @p underlying_rng and @p entropy_sources
+ * will take place after @p reseed_interval many requests or after
+ * a fork was detected.
+ *
+ * @param prf MAC to use as a PRF
+ * @param underlying_rng is a reference to some RNG which will be used
+ * to perform the periodic reseeding
+ * @param entropy_sources will be polled to perform reseeding periodically
+ * @param reseed_interval specifies a limit of how many times
+ * the RNG will be called before automatic reseeding is performed (max. 2^24)
+ * @param max_number_of_bytes_per_request requests that are in size higher
+ * than max_number_of_bytes_per_request are treated as if multiple single
+ * requests of max_number_of_bytes_per_request size had been made.
+ * In theory SP 800-90A requires that we reject any request for a DRBG
+ * output longer than max_number_of_bytes_per_request. To avoid inconveniencing
+ * the caller who wants an output larger than max_number_of_bytes_per_request,
+ * instead treat these requests as if multiple requests of
+ * max_number_of_bytes_per_request size had been made. NIST requires for
+ * HMAC_DRBG that every implementation set a value no more than 2**19 bits
+ * (or 64 KiB). Together with @p reseed_interval = 1 you can enforce that for
+ * example every 512 bit automatic reseeding occurs.
+ */
+ HMAC_DRBG(std::unique_ptr<MessageAuthenticationCode> prf,
+ RandomNumberGenerator& underlying_rng,
+ Entropy_Sources& entropy_sources,
+ size_t reseed_interval = BOTAN_RNG_DEFAULT_RESEED_INTERVAL,
+ size_t max_number_of_bytes_per_request = 64 * 1024);
+
+ std::string name() const override;
+
+ size_t security_level() const override;
+
+ size_t max_number_of_bytes_per_request() const override
+ { return m_max_number_of_bytes_per_request; }
+
+ private:
+ void update(const uint8_t input[], size_t input_len) override;
+
+ void generate_output(uint8_t output[], size_t output_len,
+ const uint8_t input[], size_t input_len) override;
+
+ void clear_state() override;
+
+ std::unique_ptr<MessageAuthenticationCode> m_mac;
+ secure_vector<uint8_t> m_V;
+ const size_t m_max_number_of_bytes_per_request;
+ const size_t m_security_level;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/rng/hmac_drbg/info.txt b/comm/third_party/botan/src/lib/rng/hmac_drbg/info.txt
new file mode 100644
index 0000000000..a8922bdf0e
--- /dev/null
+++ b/comm/third_party/botan/src/lib/rng/hmac_drbg/info.txt
@@ -0,0 +1,8 @@
+<defines>
+HMAC_DRBG -> 20140319
+</defines>
+
+<requires>
+hmac
+stateful_rng
+</requires>
diff --git a/comm/third_party/botan/src/lib/rng/info.txt b/comm/third_party/botan/src/lib/rng/info.txt
new file mode 100644
index 0000000000..4c88ba3826
--- /dev/null
+++ b/comm/third_party/botan/src/lib/rng/info.txt
@@ -0,0 +1,3 @@
+<requires>
+entropy
+</requires>
diff --git a/comm/third_party/botan/src/lib/rng/processor_rng/info.txt b/comm/third_party/botan/src/lib/rng/processor_rng/info.txt
new file mode 100644
index 0000000000..61d10f11b4
--- /dev/null
+++ b/comm/third_party/botan/src/lib/rng/processor_rng/info.txt
@@ -0,0 +1,20 @@
+<defines>
+PROCESSOR_RNG -> 20200508
+</defines>
+
+<cc>
+gcc
+clang
+icc
+msvc
+</cc>
+
+<arch>
+x86_32
+x86_64
+ppc64
+</arch>
+
+<isa>
+ppc64:power9
+</isa>
diff --git a/comm/third_party/botan/src/lib/rng/processor_rng/processor_rng.cpp b/comm/third_party/botan/src/lib/rng/processor_rng/processor_rng.cpp
new file mode 100644
index 0000000000..ca52d05e67
--- /dev/null
+++ b/comm/third_party/botan/src/lib/rng/processor_rng/processor_rng.cpp
@@ -0,0 +1,157 @@
+/*
+* (C) 2016,2019,2020 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/processor_rng.h>
+#include <botan/loadstor.h>
+#include <botan/cpuid.h>
+
+#if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY) && !defined(BOTAN_USE_GCC_INLINE_ASM)
+ #include <immintrin.h>
+#endif
+
+namespace Botan {
+
+namespace {
+
+#if defined(BOTAN_TARGET_CPU_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;
+
+#else
+ /*
+ * Lacking specific guidance we give the CPU quite a bit of leeway
+ */
+ const size_t HWRNG_RETRIES = 512;
+#endif
+
+#if defined(BOTAN_TARGET_ARCH_IS_X86_32)
+ typedef uint32_t hwrng_output;
+#else
+ typedef uint64_t hwrng_output;
+#endif
+
+hwrng_output read_hwrng(bool& success)
+ {
+ hwrng_output output = 0;
+ success = false;
+
+#if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
+ int cf = 0;
+#if defined(BOTAN_USE_GCC_INLINE_ASM)
+ // same asm seq works for 32 and 64 bit
+ 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);
+#else
+ cf = _rdrand64_step(&output);
+#endif
+ success = (1 == cf);
+
+#elif defined(BOTAN_TARGET_CPU_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;
+ // 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));
+
+ if((~output) != 0 && (~output2) != 0)
+ {
+ output ^= output2;
+ success = true;
+ }
+
+#endif
+
+ if(success)
+ return output;
+
+ return 0;
+ }
+
+hwrng_output read_hwrng()
+ {
+ for(size_t i = 0; i < HWRNG_RETRIES; ++i)
+ {
+ bool success = false;
+ hwrng_output output = read_hwrng(success);
+
+ if(success)
+ return output;
+ }
+
+ throw PRNG_Unseeded("Processor RNG instruction failed to produce output within expected iterations");
+ }
+
+}
+
+//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();
+#else
+ return false;
+#endif
+ }
+
+std::string Processor_RNG::name() const
+ {
+#if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
+ return "rdrand";
+#elif defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY)
+ return "darn";
+#else
+ return "hwrng";
+#endif
+ }
+
+void Processor_RNG::randomize(uint8_t out[], size_t out_len)
+ {
+ while(out_len >= sizeof(hwrng_output))
+ {
+ const hwrng_output r = read_hwrng();
+ store_le(r, out);
+ out += sizeof(hwrng_output);
+ out_len -= sizeof(hwrng_output);
+ }
+
+ if(out_len > 0) // at most sizeof(hwrng_output)-1
+ {
+ const hwrng_output r = read_hwrng();
+ for(size_t i = 0; i != out_len; ++i)
+ out[i] = get_byte(i, r);
+ }
+ }
+
+Processor_RNG::Processor_RNG()
+ {
+ if(!Processor_RNG::available())
+ throw Invalid_State("Current CPU does not support RNG instruction");
+ }
+
+void Processor_RNG::add_entropy(const uint8_t[], size_t)
+ {
+ /* no way to add entropy */
+ }
+
+size_t Processor_RNG::reseed(Entropy_Sources&, size_t, std::chrono::milliseconds)
+ {
+ /* no way to add entropy */
+ return 0;
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/rng/processor_rng/processor_rng.h b/comm/third_party/botan/src/lib/rng/processor_rng/processor_rng.h
new file mode 100644
index 0000000000..5900e386e0
--- /dev/null
+++ b/comm/third_party/botan/src/lib/rng/processor_rng/processor_rng.h
@@ -0,0 +1,52 @@
+/*
+* (C) 2016,2019,2020 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_RNG_PROCESSOR_RNG_H_
+#define BOTAN_RNG_PROCESSOR_RNG_H_
+
+#include <botan/rng.h>
+
+namespace Botan {
+
+/**
+* Directly invokes a CPU specific instruction to generate random numbers.
+* On x86, the RDRAND instruction is used.
+* on POWER, the DARN instruction is used.
+*/
+class BOTAN_PUBLIC_API(2,15) Processor_RNG final : public Hardware_RNG
+ {
+ public:
+ /**
+ * Constructor will throw if CPU does not have RDRAND bit set
+ */
+ Processor_RNG();
+
+ /**
+ * Return true if RNG instruction is available on the current processor
+ */
+ static bool available();
+
+ bool accepts_input() const override { return false; }
+ bool is_seeded() const override { return true; }
+
+ void randomize(uint8_t out[], size_t out_len) override;
+
+ /*
+ * No way to provide entropy to RDRAND generator, so add_entropy is ignored
+ */
+ void add_entropy(const uint8_t[], size_t) override;
+
+ /*
+ * 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;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/rng/rdrand_rng/info.txt b/comm/third_party/botan/src/lib/rng/rdrand_rng/info.txt
new file mode 100644
index 0000000000..5cc616deae
--- /dev/null
+++ b/comm/third_party/botan/src/lib/rng/rdrand_rng/info.txt
@@ -0,0 +1,13 @@
+<defines>
+RDRAND_RNG -> 20160619
+</defines>
+
+<requires>
+processor_rng
+</requires>
+
+# Avoid building RDRAND_RNG on non-x86 since that would be confusing
+<arch>
+x86_32
+x86_64
+</arch>
diff --git a/comm/third_party/botan/src/lib/rng/rdrand_rng/rdrand_rng.cpp b/comm/third_party/botan/src/lib/rng/rdrand_rng/rdrand_rng.cpp
new file mode 100644
index 0000000000..fade5a1992
--- /dev/null
+++ b/comm/third_party/botan/src/lib/rng/rdrand_rng/rdrand_rng.cpp
@@ -0,0 +1,67 @@
+/*
+* RDRAND RNG
+* (C) 2016,2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/rdrand_rng.h>
+#include <botan/processor_rng.h>
+#include <botan/loadstor.h>
+
+namespace Botan {
+
+void RDRAND_RNG::randomize(uint8_t out[], size_t out_len)
+ {
+ Processor_RNG rng;
+ rng.randomize(out, out_len);
+ }
+
+RDRAND_RNG::RDRAND_RNG()
+ {
+ // Will throw if instruction is not available
+ Processor_RNG rng;
+ }
+
+//static
+bool RDRAND_RNG::available()
+ {
+ return Processor_RNG::available();
+ }
+
+//static
+uint32_t RDRAND_RNG::rdrand()
+ {
+ Processor_RNG rng;
+
+ for(;;)
+ {
+ try
+ {
+ uint8_t out[4];
+ rng.randomize(out, 4);
+ return load_le<uint32_t>(out, 0);
+ }
+ catch(PRNG_Unseeded&) {}
+ }
+ }
+
+//static
+uint32_t RDRAND_RNG::rdrand_status(bool& ok)
+ {
+ ok = false;
+ Processor_RNG rng;
+
+ try
+ {
+ uint8_t out[4];
+ rng.randomize(out, 4);
+ ok = true;
+ return load_le<uint32_t>(out, 0);
+ }
+ catch(PRNG_Unseeded&) {}
+
+ return 0;
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/rng/rdrand_rng/rdrand_rng.h b/comm/third_party/botan/src/lib/rng/rdrand_rng/rdrand_rng.h
new file mode 100644
index 0000000000..1b6977eac3
--- /dev/null
+++ b/comm/third_party/botan/src/lib/rng/rdrand_rng/rdrand_rng.h
@@ -0,0 +1,68 @@
+/*
+* RDRAND RNG
+* (C) 2016,2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_RNG_RDRAND_H_
+#define BOTAN_RNG_RDRAND_H_
+
+#include <botan/rng.h>
+
+namespace Botan {
+
+class BOTAN_PUBLIC_API(2,0) RDRAND_RNG final : public Hardware_RNG
+ {
+ public:
+ /**
+ * Constructor will throw if CPU does not have RDRAND bit set
+ */
+ BOTAN_DEPRECATED("Use Processor_RNG instead") RDRAND_RNG();
+
+ /**
+ * Return true if RDRAND is available on the current processor
+ */
+ static bool available();
+
+ bool accepts_input() const override { return false; }
+
+ /**
+ * Uses RDRAND to produce output
+ */
+ void randomize(uint8_t out[], size_t out_len) override;
+
+ /*
+ * No way to provide entropy to RDRAND generator, so add_entropy is ignored
+ */
+ void add_entropy(const uint8_t[], size_t) override
+ { /* no op */ }
+
+ /*
+ * No way to reseed RDRAND generator, so reseed is ignored
+ */
+ size_t reseed(Entropy_Sources&, size_t, std::chrono::milliseconds) override
+ { return 0; /* no op */ }
+
+ std::string name() const override { return "RDRAND"; }
+
+ bool is_seeded() const override { return true; }
+
+ /**
+ * On correctly working hardware, RDRAND is always supposed to
+ * succeed within a set number of retries. If after that many
+ * retries RDRAND has still not suceeded, sets ok = false and
+ * returns 0.
+ */
+ static uint32_t BOTAN_DEPRECATED("Use Processor_RNG::randomize") rdrand_status(bool& ok);
+
+ /*
+ * Calls RDRAND until it succeeds, this could hypothetically
+ * loop forever on broken hardware.
+ */
+ static uint32_t BOTAN_DEPRECATED("Use Processor_RNG::randomize") rdrand();
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/rng/rng.cpp b/comm/third_party/botan/src/lib/rng/rng.cpp
new file mode 100644
index 0000000000..743f7c7aa2
--- /dev/null
+++ b/comm/third_party/botan/src/lib/rng/rng.cpp
@@ -0,0 +1,91 @@
+/*
+* (C) 2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/rng.h>
+#include <botan/entropy_src.h>
+#include <botan/loadstor.h>
+#include <botan/internal/os_utils.h>
+
+#if defined(BOTAN_HAS_AUTO_SEEDING_RNG)
+ #include <botan/auto_rng.h>
+#endif
+
+namespace Botan {
+
+void RandomNumberGenerator::randomize_with_ts_input(uint8_t output[], size_t output_len)
+ {
+ if(this->accepts_input())
+ {
+ /*
+ Form additional input which is provided to the PRNG implementation
+ to paramaterize the KDF output.
+ */
+ uint8_t additional_input[16] = { 0 };
+ store_le(OS::get_system_timestamp_ns(), additional_input);
+ store_le(OS::get_high_resolution_clock(), additional_input + 8);
+
+ this->randomize_with_input(output, output_len, additional_input, sizeof(additional_input));
+ }
+ else
+ {
+ this->randomize(output, output_len);
+ }
+ }
+
+void RandomNumberGenerator::randomize_with_input(uint8_t output[], size_t output_len,
+ const uint8_t input[], size_t input_len)
+ {
+ this->add_entropy(input, input_len);
+ this->randomize(output, output_len);
+ }
+
+size_t RandomNumberGenerator::reseed(Entropy_Sources& srcs,
+ size_t poll_bits,
+ std::chrono::milliseconds poll_timeout)
+ {
+ if(this->accepts_input())
+ {
+ return srcs.poll(*this, poll_bits, poll_timeout);
+ }
+ else
+ {
+ return 0;
+ }
+ }
+
+void RandomNumberGenerator::reseed_from_rng(RandomNumberGenerator& rng, size_t poll_bits)
+ {
+ if(this->accepts_input())
+ {
+ secure_vector<uint8_t> buf(poll_bits / 8);
+ rng.randomize(buf.data(), buf.size());
+ this->add_entropy(buf.data(), buf.size());
+ }
+ }
+
+RandomNumberGenerator* RandomNumberGenerator::make_rng()
+ {
+#if defined(BOTAN_HAS_AUTO_SEEDING_RNG)
+ return new AutoSeeded_RNG;
+#else
+ throw Not_Implemented("make_rng failed, no AutoSeeded_RNG in this build");
+#endif
+ }
+
+#if defined(BOTAN_TARGET_OS_HAS_THREADS)
+
+#if defined(BOTAN_HAS_AUTO_SEEDING_RNG)
+Serialized_RNG::Serialized_RNG() : m_rng(new AutoSeeded_RNG) {}
+#else
+Serialized_RNG::Serialized_RNG()
+ {
+ throw Not_Implemented("Serialized_RNG default constructor failed: AutoSeeded_RNG disabled in build");
+ }
+#endif
+
+#endif
+
+}
diff --git a/comm/third_party/botan/src/lib/rng/rng.h b/comm/third_party/botan/src/lib/rng/rng.h
new file mode 100644
index 0000000000..54a8ea8319
--- /dev/null
+++ b/comm/third_party/botan/src/lib/rng/rng.h
@@ -0,0 +1,297 @@
+/*
+* Random Number Generator base classes
+* (C) 1999-2009,2015,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_RANDOM_NUMBER_GENERATOR_H_
+#define BOTAN_RANDOM_NUMBER_GENERATOR_H_
+
+#include <botan/secmem.h>
+#include <botan/exceptn.h>
+#include <botan/mutex.h>
+#include <type_traits>
+#include <chrono>
+#include <string>
+
+namespace Botan {
+
+class Entropy_Sources;
+
+/**
+* An interface to a cryptographic random number generator
+*/
+class BOTAN_PUBLIC_API(2,0) RandomNumberGenerator
+ {
+ public:
+ virtual ~RandomNumberGenerator() = default;
+
+ RandomNumberGenerator() = default;
+
+ /*
+ * Never copy a RNG, create a new one
+ */
+ RandomNumberGenerator(const RandomNumberGenerator& rng) = delete;
+ RandomNumberGenerator& operator=(const RandomNumberGenerator& rng) = delete;
+
+ /**
+ * Randomize a byte array.
+ * @param output the byte array to hold the random output.
+ * @param length the length of the byte array output in bytes.
+ */
+ virtual void randomize(uint8_t output[], size_t length) = 0;
+
+ /**
+ * Returns false if it is known that this RNG object is not able to accept
+ * externally provided inputs (via add_entropy, randomize_with_input, etc).
+ * In this case, any such provided inputs are ignored.
+ *
+ * If this function returns true, then inputs may or may not be accepted.
+ */
+ virtual bool accepts_input() const = 0;
+
+ /**
+ * Incorporate some additional data into the RNG state. For
+ * example adding nonces or timestamps from a peer's protocol
+ * message can help hedge against VM state rollback attacks.
+ * A few RNG types do not accept any externally provided input,
+ * in which case this function is a no-op.
+ *
+ * @param input a byte array containg the entropy to be added
+ * @param length the length of the byte array in
+ */
+ virtual void add_entropy(const uint8_t input[], size_t length) = 0;
+
+ /**
+ * Incorporate some additional data into the RNG state.
+ */
+ template<typename T> void add_entropy_T(const T& t)
+ {
+ static_assert(std::is_standard_layout<T>::value && std::is_trivial<T>::value, "add_entropy_T data must be POD");
+ this->add_entropy(reinterpret_cast<const uint8_t*>(&t), sizeof(T));
+ }
+
+ /**
+ * Incorporate entropy into the RNG state then produce output.
+ * Some RNG types implement this using a single operation, default
+ * calls add_entropy + randomize in sequence.
+ *
+ * Use this to further bind the outputs to your current
+ * process/protocol state. For instance if generating a new key
+ * for use in a session, include a session ID or other such
+ * value. See NIST SP 800-90 A, B, C series for more ideas.
+ *
+ * @param output buffer to hold the random output
+ * @param output_len size of the output buffer in bytes
+ * @param input entropy buffer to incorporate
+ * @param input_len size of the input buffer in bytes
+ */
+ virtual void randomize_with_input(uint8_t output[], size_t output_len,
+ const uint8_t input[], size_t input_len);
+
+ /**
+ * This calls `randomize_with_input` using some timestamps as extra input.
+ *
+ * For a stateful RNG using non-random but potentially unique data the
+ * extra input can help protect against problems with fork, VM state
+ * rollback, or other cases where somehow an RNG state is duplicated. If
+ * both of the duplicated RNG states later incorporate a timestamp (and the
+ * timestamps don't themselves repeat), their outputs will diverge.
+ */
+ virtual void randomize_with_ts_input(uint8_t output[], size_t output_len);
+
+ /**
+ * @return the name of this RNG type
+ */
+ virtual std::string name() const = 0;
+
+ /**
+ * Clear all internally held values of this RNG
+ * @post is_seeded() == false
+ */
+ virtual void clear() = 0;
+
+ /**
+ * Check whether this RNG is seeded.
+ * @return true if this RNG was already seeded, false otherwise.
+ */
+ 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.
+ */
+ 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);
+
+ /**
+ * Reseed by reading specified bits from the RNG
+ */
+ virtual void reseed_from_rng(RandomNumberGenerator& rng,
+ size_t poll_bits = BOTAN_RNG_RESEED_POLL_BITS);
+
+ // Some utility functions built on the interface above:
+
+ /**
+ * Return a random vector
+ * @param bytes number of bytes in the result
+ * @return randomized vector of length bytes
+ */
+ secure_vector<uint8_t> random_vec(size_t bytes)
+ {
+ secure_vector<uint8_t> output;
+ random_vec(output, bytes);
+ return output;
+ }
+
+ template<typename Alloc>
+ void random_vec(std::vector<uint8_t, Alloc>& v, size_t bytes)
+ {
+ v.resize(bytes);
+ this->randomize(v.data(), v.size());
+ }
+
+ /**
+ * Return a random byte
+ * @return random byte
+ */
+ uint8_t next_byte()
+ {
+ uint8_t b;
+ this->randomize(&b, 1);
+ return b;
+ }
+
+ /**
+ * @return a random byte that is greater than zero
+ */
+ uint8_t next_nonzero_byte()
+ {
+ uint8_t b = this->next_byte();
+ while(b == 0)
+ b = this->next_byte();
+ return b;
+ }
+
+ /**
+ * Create a seeded and active RNG object for general application use
+ * Added in 1.8.0
+ * Use AutoSeeded_RNG instead
+ */
+ BOTAN_DEPRECATED("Use AutoSeeded_RNG")
+ static RandomNumberGenerator* make_rng();
+ };
+
+/**
+* Convenience typedef
+*/
+typedef RandomNumberGenerator RNG;
+
+/**
+* Hardware_RNG exists to tag hardware RNG types (PKCS11_RNG, TPM_RNG, Processor_RNG)
+*/
+class BOTAN_PUBLIC_API(2,0) Hardware_RNG : public RandomNumberGenerator
+ {
+ public:
+ virtual void clear() final override { /* no way to clear state of hardware RNG */ }
+ };
+
+/**
+* Null/stub RNG - fails if you try to use it for anything
+* This is not generally useful except for in certain tests
+*/
+class BOTAN_PUBLIC_API(2,0) Null_RNG final : public RandomNumberGenerator
+ {
+ public:
+ bool is_seeded() const override { return false; }
+
+ bool accepts_input() const override { return false; }
+
+ void clear() override {}
+
+ void randomize(uint8_t[], size_t) override
+ {
+ throw PRNG_Unseeded("Null_RNG called");
+ }
+
+ void add_entropy(const uint8_t[], size_t) override {}
+
+ std::string name() const override { return "Null_RNG"; }
+ };
+
+#if defined(BOTAN_TARGET_OS_HAS_THREADS)
+/**
+* Wraps access to a RNG in a mutex
+* Note that most of the time it's much better to use a RNG per thread
+* otherwise the RNG will act as an unnecessary contention point
+*
+* Since 2.16.0 all Stateful_RNG instances have an internal lock, so
+* this class is no longer needed. It will be removed in a future major
+* release.
+*/
+class BOTAN_PUBLIC_API(2,0) Serialized_RNG final : public RandomNumberGenerator
+ {
+ public:
+ void randomize(uint8_t out[], size_t len) override
+ {
+ lock_guard_type<mutex_type> lock(m_mutex);
+ m_rng->randomize(out, len);
+ }
+
+ bool accepts_input() const override
+ {
+ lock_guard_type<mutex_type> lock(m_mutex);
+ return m_rng->accepts_input();
+ }
+
+ bool is_seeded() const override
+ {
+ lock_guard_type<mutex_type> lock(m_mutex);
+ return m_rng->is_seeded();
+ }
+
+ void clear() override
+ {
+ lock_guard_type<mutex_type> lock(m_mutex);
+ m_rng->clear();
+ }
+
+ std::string name() const override
+ {
+ lock_guard_type<mutex_type> lock(m_mutex);
+ return m_rng->name();
+ }
+
+ size_t reseed(Entropy_Sources& src,
+ size_t poll_bits = BOTAN_RNG_RESEED_POLL_BITS,
+ std::chrono::milliseconds poll_timeout = BOTAN_RNG_RESEED_DEFAULT_TIMEOUT) override
+ {
+ lock_guard_type<mutex_type> lock(m_mutex);
+ return m_rng->reseed(src, poll_bits, poll_timeout);
+ }
+
+ void add_entropy(const uint8_t in[], size_t len) override
+ {
+ lock_guard_type<mutex_type> lock(m_mutex);
+ m_rng->add_entropy(in, len);
+ }
+
+ BOTAN_DEPRECATED("Use Serialized_RNG(new AutoSeeded_RNG) instead") Serialized_RNG();
+
+ /*
+ * Since 2.16.0 this is no longer needed for any RNG type. This
+ * class will be removed in a future major release.
+ */
+ explicit Serialized_RNG(RandomNumberGenerator* rng) : m_rng(rng) {}
+ private:
+ mutable mutex_type m_mutex;
+ std::unique_ptr<RandomNumberGenerator> m_rng;
+ };
+#endif
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/rng/stateful_rng/info.txt b/comm/third_party/botan/src/lib/rng/stateful_rng/info.txt
new file mode 100644
index 0000000000..edc2d91694
--- /dev/null
+++ b/comm/third_party/botan/src/lib/rng/stateful_rng/info.txt
@@ -0,0 +1,3 @@
+<defines>
+STATEFUL_RNG -> 20160819
+</defines>
diff --git a/comm/third_party/botan/src/lib/rng/stateful_rng/stateful_rng.cpp b/comm/third_party/botan/src/lib/rng/stateful_rng/stateful_rng.cpp
new file mode 100644
index 0000000000..c7b3484ee0
--- /dev/null
+++ b/comm/third_party/botan/src/lib/rng/stateful_rng/stateful_rng.cpp
@@ -0,0 +1,190 @@
+/*
+* (C) 2016,2020 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/stateful_rng.h>
+#include <botan/internal/os_utils.h>
+#include <botan/loadstor.h>
+
+#if defined(BOTAN_HAS_SYSTEM_RNG)
+ #include <botan/system_rng.h>
+#endif
+
+namespace Botan {
+
+void Stateful_RNG::clear()
+ {
+ lock_guard_type<recursive_mutex_type> lock(m_mutex);
+ m_reseed_counter = 0;
+ m_last_pid = 0;
+ clear_state();
+ }
+
+void Stateful_RNG::force_reseed()
+ {
+ lock_guard_type<recursive_mutex_type> lock(m_mutex);
+ m_reseed_counter = 0;
+ }
+
+bool Stateful_RNG::is_seeded() const
+ {
+ lock_guard_type<recursive_mutex_type> lock(m_mutex);
+ return m_reseed_counter > 0;
+ }
+
+void Stateful_RNG::add_entropy(const uint8_t input[], size_t input_len)
+ {
+ lock_guard_type<recursive_mutex_type> lock(m_mutex);
+
+ update(input, input_len);
+
+ if(8*input_len >= security_level())
+ {
+ reset_reseed_counter();
+ }
+ }
+
+void Stateful_RNG::initialize_with(const uint8_t input[], size_t len)
+ {
+ lock_guard_type<recursive_mutex_type> lock(m_mutex);
+
+ clear();
+ add_entropy(input, len);
+ }
+
+void Stateful_RNG::randomize(uint8_t output[], size_t output_len)
+ {
+ randomize_with_input(output, output_len, nullptr, 0);
+ }
+
+void Stateful_RNG::randomize_with_ts_input(uint8_t output[], size_t output_len)
+ {
+ uint8_t additional_input[20] = { 0 };
+
+ store_le(OS::get_high_resolution_clock(), additional_input);
+
+#if defined(BOTAN_HAS_SYSTEM_RNG)
+ System_RNG system_rng;
+ system_rng.randomize(additional_input + 8, sizeof(additional_input) - 8);
+#else
+ store_le(OS::get_system_timestamp_ns(), additional_input + 8);
+ store_le(OS::get_process_id(), additional_input + 16);
+#endif
+
+ randomize_with_input(output, output_len, additional_input, sizeof(additional_input));
+ }
+
+void Stateful_RNG::randomize_with_input(uint8_t output[], size_t output_len,
+ const uint8_t input[], size_t input_len)
+ {
+ if(output_len == 0)
+ return;
+
+ lock_guard_type<recursive_mutex_type> lock(m_mutex);
+
+ const size_t max_per_request = max_number_of_bytes_per_request();
+
+ if(max_per_request == 0) // no limit
+ {
+ reseed_check();
+ this->generate_output(output, output_len, input, input_len);
+ }
+ else
+ {
+ while(output_len > 0)
+ {
+ const size_t this_req = std::min(max_per_request, output_len);
+
+ /*
+ * We split the request into several requests to the underlying DRBG but
+ * pass the input to each invocation. It might be more sensible to only
+ * provide it for the first invocation, however between 2.0 and 2.15
+ * HMAC_DRBG always provided it for all requests so retain that here.
+ */
+
+ reseed_check();
+ this->generate_output(output, this_req, input, input_len);
+
+ output += this_req;
+ output_len -= this_req;
+ }
+ }
+ }
+
+size_t Stateful_RNG::reseed(Entropy_Sources& srcs,
+ size_t poll_bits,
+ std::chrono::milliseconds poll_timeout)
+ {
+ lock_guard_type<recursive_mutex_type> lock(m_mutex);
+
+ const size_t bits_collected = RandomNumberGenerator::reseed(srcs, poll_bits, poll_timeout);
+
+ if(bits_collected >= security_level())
+ {
+ reset_reseed_counter();
+ }
+
+ return bits_collected;
+ }
+
+void Stateful_RNG::reseed_from_rng(RandomNumberGenerator& rng, size_t poll_bits)
+ {
+ lock_guard_type<recursive_mutex_type> lock(m_mutex);
+
+ RandomNumberGenerator::reseed_from_rng(rng, poll_bits);
+
+ if(poll_bits >= security_level())
+ {
+ reset_reseed_counter();
+ }
+ }
+
+void Stateful_RNG::reset_reseed_counter()
+ {
+ // Lock is held whenever this function is called
+ m_reseed_counter = 1;
+ }
+
+void Stateful_RNG::reseed_check()
+ {
+ // Lock is held whenever this function is called
+
+ const uint32_t cur_pid = OS::get_process_id();
+
+ const bool fork_detected = (m_last_pid > 0) && (cur_pid != m_last_pid);
+
+ if(is_seeded() == false ||
+ fork_detected ||
+ (m_reseed_interval > 0 && m_reseed_counter >= m_reseed_interval))
+ {
+ m_reseed_counter = 0;
+ m_last_pid = cur_pid;
+
+ if(m_underlying_rng)
+ {
+ reseed_from_rng(*m_underlying_rng, security_level());
+ }
+
+ if(m_entropy_sources)
+ {
+ reseed(*m_entropy_sources, security_level());
+ }
+
+ if(!is_seeded())
+ {
+ if(fork_detected)
+ throw Invalid_State("Detected use of fork but cannot reseed DRBG");
+ else
+ throw PRNG_Unseeded(name());
+ }
+ }
+ else
+ {
+ BOTAN_ASSERT(m_reseed_counter != 0, "RNG is seeded");
+ m_reseed_counter += 1;
+ }
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/rng/stateful_rng/stateful_rng.h b/comm/third_party/botan/src/lib/rng/stateful_rng/stateful_rng.h
new file mode 100644
index 0000000000..e1311fcc9f
--- /dev/null
+++ b/comm/third_party/botan/src/lib/rng/stateful_rng/stateful_rng.h
@@ -0,0 +1,166 @@
+/*
+* (C) 2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_STATEFUL_RNG_H_
+#define BOTAN_STATEFUL_RNG_H_
+
+#include <botan/rng.h>
+#include <botan/mutex.h>
+
+namespace Botan {
+
+/**
+* Inherited by RNGs which maintain in-process state, like HMAC_DRBG.
+* On Unix these RNGs are vulnerable to problems with fork, where the
+* RNG state is duplicated, and the parent and child process RNGs will
+* produce identical output until one of them reseeds. Stateful_RNG
+* reseeds itself whenever a fork is detected, or after a set number of
+* bytes have been output.
+*
+* Not implemented by RNGs which access an external RNG, such as the
+* system PRNG or a hardware RNG.
+*/
+class BOTAN_PUBLIC_API(2,0) Stateful_RNG : public RandomNumberGenerator
+ {
+ public:
+ /**
+ * @param rng is a reference to some RNG which will be used
+ * to perform the periodic reseeding
+ * @param entropy_sources will be polled to perform reseeding periodically
+ * @param reseed_interval specifies a limit of how many times
+ * the RNG will be called before automatic reseeding is performed
+ */
+ Stateful_RNG(RandomNumberGenerator& rng,
+ Entropy_Sources& entropy_sources,
+ size_t reseed_interval) :
+ m_underlying_rng(&rng),
+ m_entropy_sources(&entropy_sources),
+ m_reseed_interval(reseed_interval)
+ {}
+
+ /**
+ * @param rng is a reference to some RNG which will be used
+ * to perform the periodic reseeding
+ * @param reseed_interval specifies a limit of how many times
+ * the RNG will be called before automatic reseeding is performed
+ */
+ Stateful_RNG(RandomNumberGenerator& rng, size_t reseed_interval) :
+ m_underlying_rng(&rng),
+ m_reseed_interval(reseed_interval)
+ {}
+
+ /**
+ * @param entropy_sources will be polled to perform reseeding periodically
+ * @param reseed_interval specifies a limit of how many times
+ * the RNG will be called before automatic reseeding is performed
+ */
+ Stateful_RNG(Entropy_Sources& entropy_sources, size_t reseed_interval) :
+ m_entropy_sources(&entropy_sources),
+ m_reseed_interval(reseed_interval)
+ {}
+
+ /**
+ * In this case, automatic reseeding is impossible
+ */
+ Stateful_RNG() : m_reseed_interval(0) {}
+
+ /**
+ * Consume this input and mark the RNG as initialized regardless
+ * of the length of the input or the current seeded state of
+ * the RNG.
+ */
+ void initialize_with(const uint8_t input[], size_t length);
+
+ bool is_seeded() const override final;
+
+ bool accepts_input() const override final { return true; }
+
+ /**
+ * Mark state as requiring a reseed on next use
+ */
+ void force_reseed();
+
+ void reseed_from_rng(RandomNumberGenerator& rng,
+ size_t poll_bits = BOTAN_RNG_RESEED_POLL_BITS) override final;
+
+ void add_entropy(const uint8_t input[], size_t input_len) override final;
+
+ void randomize(uint8_t output[], size_t output_len) override final;
+
+ void randomize_with_input(uint8_t output[], size_t output_len,
+ const uint8_t input[], size_t input_len) override final;
+
+ /**
+ * Overrides default implementation and also includes the current
+ * process ID and the reseed counter.
+ */
+ void randomize_with_ts_input(uint8_t output[], size_t output_len) override 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.
+ */
+ 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;
+
+ /**
+ * @return intended security level of this DRBG
+ */
+ virtual size_t security_level() const = 0;
+
+ /**
+ * Some DRBGs have a notion of the maximum number of bytes per
+ * request. Longer requests (to randomize) will be treated as
+ * multiple requests, and may initiate reseeding multiple times,
+ * depending on the values of max_number_of_bytes_per_request and
+ * reseed_interval(). This function returns zero if the RNG in
+ * question does not have such a notion.
+ *
+ * @return max number of bytes per request (or zero)
+ */
+ virtual size_t max_number_of_bytes_per_request() const = 0;
+
+ size_t reseed_interval() const { return m_reseed_interval; }
+
+ void clear() override final;
+
+ protected:
+ void reseed_check();
+
+ virtual void generate_output(uint8_t output[], size_t output_len,
+ const uint8_t input[], size_t input_len) = 0;
+
+ virtual void update(const uint8_t input[], size_t input_len) = 0;
+
+ virtual void clear_state() = 0;
+
+ private:
+ void reset_reseed_counter();
+
+ mutable recursive_mutex_type m_mutex;
+
+ // A non-owned and possibly null pointer to shared RNG
+ RandomNumberGenerator* m_underlying_rng = nullptr;
+
+ // A non-owned and possibly null pointer to a shared Entropy_Source
+ Entropy_Sources* m_entropy_sources = nullptr;
+
+ const size_t m_reseed_interval;
+ uint32_t m_last_pid = 0;
+
+ /*
+ * Set to 1 after a successful seeding, then incremented. Reset
+ * to 0 by clear() or a fork. This logic is used even if
+ * automatic reseeding is disabled (via m_reseed_interval = 0)
+ */
+ size_t m_reseed_counter = 0;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/rng/system_rng/info.txt b/comm/third_party/botan/src/lib/rng/system_rng/info.txt
new file mode 100644
index 0000000000..e77328b820
--- /dev/null
+++ b/comm/third_party/botan/src/lib/rng/system_rng/info.txt
@@ -0,0 +1,18 @@
+<defines>
+SYSTEM_RNG -> 20141202
+</defines>
+
+<os_features>
+dev_random,posix1
+arc4random
+rtlgenrandom
+crypto_ng
+</os_features>
+
+<libs>
+uwp -> bcrypt
+</libs>
+
+<requires>
+rtlgenrandom?dyn_load
+</requires>
diff --git a/comm/third_party/botan/src/lib/rng/system_rng/system_rng.cpp b/comm/third_party/botan/src/lib/rng/system_rng/system_rng.cpp
new file mode 100644
index 0000000000..0c5641e60d
--- /dev/null
+++ b/comm/third_party/botan/src/lib/rng/system_rng/system_rng.cpp
@@ -0,0 +1,289 @@
+/*
+* System RNG
+* (C) 2014,2015,2017,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/system_rng.h>
+
+#if defined(BOTAN_TARGET_OS_HAS_RTLGENRANDOM)
+ #include <botan/dyn_load.h>
+ #define NOMINMAX 1
+ #define _WINSOCKAPI_ // stop windows.h including winsock.h
+ #include <windows.h>
+
+#elif defined(BOTAN_TARGET_OS_HAS_CRYPTO_NG)
+ #include <bcrypt.h>
+
+#elif defined(BOTAN_TARGET_OS_HAS_ARC4RANDOM)
+ #include <stdlib.h>
+
+#elif defined(BOTAN_TARGET_OS_HAS_GETRANDOM)
+ #include <sys/random.h>
+ #include <errno.h>
+
+#elif defined(BOTAN_TARGET_OS_HAS_DEV_RANDOM)
+ #include <sys/types.h>
+ #include <sys/stat.h>
+ #include <fcntl.h>
+ #include <unistd.h>
+ #include <errno.h>
+#endif
+
+namespace Botan {
+
+namespace {
+
+#if defined(BOTAN_TARGET_OS_HAS_RTLGENRANDOM)
+
+class System_RNG_Impl final : public RandomNumberGenerator
+ {
+ public:
+ System_RNG_Impl() : m_advapi("advapi32.dll")
+ {
+ // This throws if the function is not found
+ m_rtlgenrandom = m_advapi.resolve<RtlGenRandom_fptr>("SystemFunction036");
+ }
+
+ void randomize(uint8_t buf[], size_t len) override
+ {
+ bool success = m_rtlgenrandom(buf, ULONG(len)) == TRUE;
+ if(!success)
+ throw System_Error("RtlGenRandom failed");
+ }
+
+ void add_entropy(const uint8_t[], size_t) override { /* ignored */ }
+ bool is_seeded() const override { return true; }
+ bool accepts_input() const override { return false; }
+ void clear() override { /* not possible */ }
+ std::string name() const override { return "RtlGenRandom"; }
+ private:
+ // Use type BYTE instead of BOOLEAN because of a naming conflict
+ // https://msdn.microsoft.com/en-us/library/windows/desktop/aa387694(v=vs.85).aspx
+ // https://msdn.microsoft.com/en-us/library/windows/desktop/aa383751(v=vs.85).aspx
+ using RtlGenRandom_fptr = BYTE (NTAPI *)(PVOID, ULONG);
+
+ Dynamically_Loaded_Library m_advapi;
+ RtlGenRandom_fptr m_rtlgenrandom;
+ };
+
+#elif defined(BOTAN_TARGET_OS_HAS_CRYPTO_NG)
+
+class System_RNG_Impl final : public RandomNumberGenerator
+ {
+ public:
+ System_RNG_Impl()
+ {
+ NTSTATUS ret = ::BCryptOpenAlgorithmProvider(&m_prov,
+ BCRYPT_RNG_ALGORITHM,
+ MS_PRIMITIVE_PROVIDER, 0);
+ if(ret != STATUS_SUCCESS)
+ throw System_Error("System_RNG failed to acquire crypto provider", ret);
+ }
+
+ ~System_RNG_Impl()
+ {
+ ::BCryptCloseAlgorithmProvider(m_prov, 0);
+ }
+
+ void randomize(uint8_t buf[], size_t len) override
+ {
+ NTSTATUS ret = ::BCryptGenRandom(m_prov, static_cast<PUCHAR>(buf), static_cast<ULONG>(len), 0);
+ if(ret != STATUS_SUCCESS)
+ throw System_Error("System_RNG call to BCryptGenRandom failed", ret);
+ }
+
+ void add_entropy(const uint8_t in[], size_t length) override
+ {
+ /*
+ There is a flag BCRYPT_RNG_USE_ENTROPY_IN_BUFFER to provide
+ entropy inputs, but it is ignored in Windows 8 and later.
+ */
+ }
+
+ bool is_seeded() const override { return true; }
+ bool accepts_input() const override { return false; }
+ void clear() override { /* not possible */ }
+ std::string name() const override { return "crypto_ng"; }
+ private:
+ BCRYPT_ALG_HANDLE m_prov;
+ };
+
+#elif defined(BOTAN_TARGET_OS_HAS_ARC4RANDOM)
+
+class System_RNG_Impl final : public RandomNumberGenerator
+ {
+ public:
+ // No constructor or destructor needed as no userland state maintained
+
+ void randomize(uint8_t buf[], size_t len) override
+ {
+ // macOS 10.15 arc4random crashes if called with buf == nullptr && len == 0
+ if(len > 0)
+ {
+ ::arc4random_buf(buf, len);
+ }
+ }
+
+ bool accepts_input() const override { return false; }
+ void add_entropy(const uint8_t[], size_t) override { /* ignored */ }
+ bool is_seeded() const override { return true; }
+ void clear() override { /* not possible */ }
+ std::string name() const override { return "arc4random"; }
+ };
+
+#elif defined(BOTAN_TARGET_OS_HAS_GETRANDOM)
+
+class System_RNG_Impl final : public RandomNumberGenerator
+ {
+ public:
+ // No constructor or destructor needed as no userland state maintained
+
+ void randomize(uint8_t buf[], size_t len) override
+ {
+ const unsigned int flags = 0;
+
+ while(len > 0)
+ {
+ const ssize_t got = ::getrandom(buf, len, flags);
+
+ if(got < 0)
+ {
+ if(errno == EINTR)
+ continue;
+ throw System_Error("System_RNG getrandom failed", errno);
+ }
+
+ buf += got;
+ len -= got;
+ }
+ }
+
+ bool accepts_input() const override { return false; }
+ void add_entropy(const uint8_t[], size_t) override { /* ignored */ }
+ bool is_seeded() const override { return true; }
+ void clear() override { /* not possible */ }
+ std::string name() const override { return "getrandom"; }
+ };
+
+
+#elif defined(BOTAN_TARGET_OS_HAS_DEV_RANDOM)
+
+// Read a random device
+
+class System_RNG_Impl final : public RandomNumberGenerator
+ {
+ public:
+ System_RNG_Impl()
+ {
+#ifndef O_NOCTTY
+#define O_NOCTTY 0
+#endif
+
+ m_fd = ::open(BOTAN_SYSTEM_RNG_DEVICE, O_RDWR | O_NOCTTY);
+
+ if(m_fd >= 0)
+ {
+ m_writable = true;
+ }
+ else
+ {
+ /*
+ Cannot open in read-write mode. Fall back to read-only,
+ calls to add_entropy will fail, but randomize will work
+ */
+ m_fd = ::open(BOTAN_SYSTEM_RNG_DEVICE, O_RDONLY | O_NOCTTY);
+ m_writable = false;
+ }
+
+ if(m_fd < 0)
+ throw System_Error("System_RNG failed to open RNG device", errno);
+ }
+
+ ~System_RNG_Impl()
+ {
+ ::close(m_fd);
+ m_fd = -1;
+ }
+
+ void randomize(uint8_t buf[], size_t len) override;
+ void add_entropy(const uint8_t in[], size_t length) override;
+ bool is_seeded() const override { return true; }
+ bool accepts_input() const override { return m_writable; }
+ void clear() override { /* not possible */ }
+ std::string name() const override { return BOTAN_SYSTEM_RNG_DEVICE; }
+ private:
+ int m_fd;
+ bool m_writable;
+ };
+
+void System_RNG_Impl::randomize(uint8_t buf[], size_t len)
+ {
+ while(len)
+ {
+ ssize_t got = ::read(m_fd, buf, len);
+
+ if(got < 0)
+ {
+ if(errno == EINTR)
+ continue;
+ throw System_Error("System_RNG read failed", errno);
+ }
+ if(got == 0)
+ throw System_Error("System_RNG EOF on device"); // ?!?
+
+ buf += got;
+ len -= got;
+ }
+ }
+
+void System_RNG_Impl::add_entropy(const uint8_t input[], size_t len)
+ {
+ if(!m_writable)
+ return;
+
+ while(len)
+ {
+ ssize_t got = ::write(m_fd, input, len);
+
+ if(got < 0)
+ {
+ if(errno == EINTR)
+ continue;
+
+ /*
+ * This is seen on OS X CI, despite the fact that the man page
+ * for macOS urandom explicitly states that writing to it is
+ * supported, and write(2) does not document EPERM at all.
+ * But in any case EPERM seems indicative of a policy decision
+ * by the OS or sysadmin that additional entropy is not wanted
+ * in the system pool, so we accept that and return here,
+ * since there is no corrective action possible.
+ *
+ * In Linux EBADF or EPERM is returned if m_fd is not opened for
+ * writing.
+ */
+ if(errno == EPERM || errno == EBADF)
+ return;
+
+ // maybe just ignore any failure here and return?
+ throw System_Error("System_RNG write failed", errno);
+ }
+
+ input += got;
+ len -= got;
+ }
+ }
+
+#endif
+
+}
+
+RandomNumberGenerator& system_rng()
+ {
+ static System_RNG_Impl g_system_rng;
+ return g_system_rng;
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/rng/system_rng/system_rng.h b/comm/third_party/botan/src/lib/rng/system_rng/system_rng.h
new file mode 100644
index 0000000000..e0f0181ca1
--- /dev/null
+++ b/comm/third_party/botan/src/lib/rng/system_rng/system_rng.h
@@ -0,0 +1,43 @@
+/*
+* System RNG interface
+* (C) 2014,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_SYSTEM_RNG_H_
+#define BOTAN_SYSTEM_RNG_H_
+
+#include <botan/rng.h>
+
+namespace Botan {
+
+/**
+* Return a shared reference to a global PRNG instance provided by the
+* operating system. For instance might be instantiated by /dev/urandom
+* or CryptGenRandom.
+*/
+BOTAN_PUBLIC_API(2,0) RandomNumberGenerator& system_rng();
+
+/*
+* Instantiable reference to the system RNG.
+*/
+class BOTAN_PUBLIC_API(2,0) System_RNG final : public RandomNumberGenerator
+ {
+ public:
+ std::string name() const override { return system_rng().name(); }
+
+ void randomize(uint8_t out[], size_t len) override { system_rng().randomize(out, len); }
+
+ void add_entropy(const uint8_t in[], size_t length) override { system_rng().add_entropy(in, length); }
+
+ bool is_seeded() const override { return system_rng().is_seeded(); }
+
+ bool accepts_input() const override { return system_rng().accepts_input(); }
+
+ void clear() override { system_rng().clear(); }
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/stream/chacha/chacha.cpp b/comm/third_party/botan/src/lib/stream/chacha/chacha.cpp
new file mode 100644
index 0000000000..c8d567e5e0
--- /dev/null
+++ b/comm/third_party/botan/src/lib/stream/chacha/chacha.cpp
@@ -0,0 +1,384 @@
+/*
+* ChaCha
+* (C) 2014,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/chacha.h>
+#include <botan/exceptn.h>
+#include <botan/loadstor.h>
+#include <botan/rotate.h>
+#include <botan/cpuid.h>
+
+namespace Botan {
+
+namespace {
+
+#define CHACHA_QUARTER_ROUND(a, b, c, d) \
+ do { \
+ a += b; d ^= a; d = rotl<16>(d); \
+ c += d; b ^= c; b = rotl<12>(b); \
+ a += b; d ^= a; d = rotl<8>(d); \
+ c += d; b ^= c; b = rotl<7>(b); \
+ } while(0)
+
+/*
+* Generate HChaCha cipher stream (for XChaCha IV setup)
+*/
+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];
+
+ for(size_t i = 0; i != rounds / 2; ++i)
+ {
+ CHACHA_QUARTER_ROUND(x00, x04, x08, x12);
+ CHACHA_QUARTER_ROUND(x01, x05, x09, x13);
+ CHACHA_QUARTER_ROUND(x02, x06, x10, x14);
+ CHACHA_QUARTER_ROUND(x03, x07, x11, x15);
+
+ CHACHA_QUARTER_ROUND(x00, x05, x10, x15);
+ CHACHA_QUARTER_ROUND(x01, x06, x11, x12);
+ CHACHA_QUARTER_ROUND(x02, x07, x08, x13);
+ CHACHA_QUARTER_ROUND(x03, x04, x09, x14);
+ }
+
+ output[0] = x00;
+ output[1] = x01;
+ output[2] = x02;
+ output[3] = x03;
+ output[4] = x12;
+ output[5] = x13;
+ output[6] = x14;
+ output[7] = x15;
+ }
+
+}
+
+ChaCha::ChaCha(size_t rounds) : m_rounds(rounds)
+ {
+ BOTAN_ARG_CHECK(m_rounds == 8 || m_rounds == 12 || m_rounds == 20,
+ "ChaCha only supports 8, 12 or 20 rounds");
+ }
+
+std::string ChaCha::provider() const
+ {
+#if defined(BOTAN_HAS_CHACHA_AVX2)
+ if(CPUID::has_avx2())
+ {
+ return "avx2";
+ }
+#endif
+
+#if defined(BOTAN_HAS_CHACHA_SIMD32)
+ if(CPUID::has_simd_32())
+ {
+ return "simd32";
+ }
+#endif
+
+ return "base";
+ }
+
+//static
+void ChaCha::chacha_x8(uint8_t output[64*8], uint32_t input[16], size_t rounds)
+ {
+ BOTAN_ASSERT(rounds % 2 == 0, "Valid rounds");
+
+#if defined(BOTAN_HAS_CHACHA_AVX2)
+ if(CPUID::has_avx2())
+ {
+ return ChaCha::chacha_avx2_x8(output, input, rounds);
+ }
+#endif
+
+#if defined(BOTAN_HAS_CHACHA_SIMD32)
+ if(CPUID::has_simd_32())
+ {
+ ChaCha::chacha_simd32_x4(output, input, rounds);
+ ChaCha::chacha_simd32_x4(output + 4*64, input, rounds);
+ return;
+ }
+#endif
+
+ // TODO interleave rounds
+ for(size_t i = 0; i != 8; ++i)
+ {
+ 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];
+
+ for(size_t r = 0; r != rounds / 2; ++r)
+ {
+ CHACHA_QUARTER_ROUND(x00, x04, x08, x12);
+ CHACHA_QUARTER_ROUND(x01, x05, x09, x13);
+ CHACHA_QUARTER_ROUND(x02, x06, x10, x14);
+ CHACHA_QUARTER_ROUND(x03, x07, x11, x15);
+
+ CHACHA_QUARTER_ROUND(x00, x05, x10, x15);
+ CHACHA_QUARTER_ROUND(x01, x06, x11, x12);
+ CHACHA_QUARTER_ROUND(x02, x07, x08, x13);
+ CHACHA_QUARTER_ROUND(x03, x04, x09, x14);
+ }
+
+ 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];
+
+ store_le(x00, output + 64 * i + 4 * 0);
+ store_le(x01, output + 64 * i + 4 * 1);
+ store_le(x02, output + 64 * i + 4 * 2);
+ store_le(x03, output + 64 * i + 4 * 3);
+ store_le(x04, output + 64 * i + 4 * 4);
+ store_le(x05, output + 64 * i + 4 * 5);
+ store_le(x06, output + 64 * i + 4 * 6);
+ store_le(x07, output + 64 * i + 4 * 7);
+ store_le(x08, output + 64 * i + 4 * 8);
+ store_le(x09, output + 64 * i + 4 * 9);
+ store_le(x10, output + 64 * i + 4 * 10);
+ store_le(x11, output + 64 * i + 4 * 11);
+ store_le(x12, output + 64 * i + 4 * 12);
+ store_le(x13, output + 64 * i + 4 * 13);
+ store_le(x14, output + 64 * i + 4 * 14);
+ store_le(x15, output + 64 * i + 4 * 15);
+
+ input[12]++;
+ input[13] += (input[12] == 0);
+ }
+ }
+
+#undef CHACHA_QUARTER_ROUND
+
+/*
+* Combine cipher stream with message
+*/
+void ChaCha::cipher(const uint8_t in[], uint8_t out[], size_t length)
+ {
+ verify_key_set(m_state.empty() == false);
+
+ while(length >= m_buffer.size() - m_position)
+ {
+ const size_t available = m_buffer.size() - m_position;
+
+ xor_buf(out, in, &m_buffer[m_position], available);
+ chacha_x8(m_buffer.data(), m_state.data(), m_rounds);
+
+ length -= available;
+ in += available;
+ out += available;
+ m_position = 0;
+ }
+
+ xor_buf(out, in, &m_buffer[m_position], length);
+
+ m_position += length;
+ }
+
+void ChaCha::write_keystream(uint8_t out[], size_t length)
+ {
+ verify_key_set(m_state.empty() == false);
+
+ while(length >= m_buffer.size() - m_position)
+ {
+ const size_t available = m_buffer.size() - m_position;
+
+ copy_mem(out, &m_buffer[m_position], available);
+ chacha_x8(m_buffer.data(), m_state.data(), m_rounds);
+
+ length -= available;
+ out += available;
+ m_position = 0;
+ }
+
+ copy_mem(out, &m_buffer[m_position], length);
+
+ m_position += length;
+ }
+
+void ChaCha::initialize_state()
+ {
+ static const uint32_t TAU[] =
+ { 0x61707865, 0x3120646e, 0x79622d36, 0x6b206574 };
+
+ static const uint32_t SIGMA[] =
+ { 0x61707865, 0x3320646e, 0x79622d32, 0x6b206574 };
+
+ m_state[4] = m_key[0];
+ m_state[5] = m_key[1];
+ m_state[6] = m_key[2];
+ m_state[7] = m_key[3];
+
+ if(m_key.size() == 4)
+ {
+ m_state[0] = TAU[0];
+ m_state[1] = TAU[1];
+ m_state[2] = TAU[2];
+ m_state[3] = TAU[3];
+
+ m_state[8] = m_key[0];
+ m_state[9] = m_key[1];
+ m_state[10] = m_key[2];
+ m_state[11] = m_key[3];
+ }
+ else
+ {
+ m_state[0] = SIGMA[0];
+ m_state[1] = SIGMA[1];
+ m_state[2] = SIGMA[2];
+ m_state[3] = SIGMA[3];
+
+ m_state[8] = m_key[4];
+ m_state[9] = m_key[5];
+ m_state[10] = m_key[6];
+ m_state[11] = m_key[7];
+ }
+
+ m_state[12] = 0;
+ m_state[13] = 0;
+ m_state[14] = 0;
+ m_state[15] = 0;
+
+ m_position = 0;
+ }
+
+/*
+* ChaCha Key Schedule
+*/
+void ChaCha::key_schedule(const uint8_t key[], size_t length)
+ {
+ m_key.resize(length / 4);
+ load_le<uint32_t>(m_key.data(), key, m_key.size());
+
+ m_state.resize(16);
+
+ const size_t chacha_parallelism = 8; // chacha_x8
+ const size_t chacha_block = 64;
+ m_buffer.resize(chacha_parallelism * chacha_block);
+
+ set_iv(nullptr, 0);
+ }
+
+size_t ChaCha::default_iv_length() const
+ {
+ return 24;
+ }
+
+Key_Length_Specification ChaCha::key_spec() const
+ {
+ return Key_Length_Specification(16, 32, 16);
+ }
+
+StreamCipher* ChaCha::clone() const
+ {
+ return new ChaCha(m_rounds);
+ }
+
+bool ChaCha::valid_iv_length(size_t iv_len) const
+ {
+ return (iv_len == 0 || iv_len == 8 || iv_len == 12 || iv_len == 24);
+ }
+
+void ChaCha::set_iv(const uint8_t iv[], size_t length)
+ {
+ verify_key_set(m_state.empty() == false);
+
+ if(!valid_iv_length(length))
+ throw Invalid_IV_Length(name(), length);
+
+ initialize_state();
+
+ if(length == 0)
+ {
+ // Treat zero length IV same as an all-zero IV
+ m_state[14] = 0;
+ m_state[15] = 0;
+ }
+ else if(length == 8)
+ {
+ m_state[14] = load_le<uint32_t>(iv, 0);
+ m_state[15] = load_le<uint32_t>(iv, 1);
+ }
+ else if(length == 12)
+ {
+ m_state[13] = load_le<uint32_t>(iv, 0);
+ m_state[14] = load_le<uint32_t>(iv, 1);
+ m_state[15] = load_le<uint32_t>(iv, 2);
+ }
+ else if(length == 24)
+ {
+ m_state[12] = load_le<uint32_t>(iv, 0);
+ m_state[13] = load_le<uint32_t>(iv, 1);
+ m_state[14] = load_le<uint32_t>(iv, 2);
+ m_state[15] = load_le<uint32_t>(iv, 3);
+
+ secure_vector<uint32_t> hc(8);
+ hchacha(hc.data(), m_state.data(), m_rounds);
+
+ m_state[ 4] = hc[0];
+ m_state[ 5] = hc[1];
+ m_state[ 6] = hc[2];
+ m_state[ 7] = hc[3];
+ m_state[ 8] = hc[4];
+ m_state[ 9] = hc[5];
+ m_state[10] = hc[6];
+ m_state[11] = hc[7];
+ m_state[12] = 0;
+ m_state[13] = 0;
+ m_state[14] = load_le<uint32_t>(iv, 4);
+ m_state[15] = load_le<uint32_t>(iv, 5);
+ }
+
+ chacha_x8(m_buffer.data(), m_state.data(), m_rounds);
+ m_position = 0;
+ }
+
+void ChaCha::clear()
+ {
+ zap(m_key);
+ zap(m_state);
+ zap(m_buffer);
+ m_position = 0;
+ }
+
+std::string ChaCha::name() const
+ {
+ return "ChaCha(" + std::to_string(m_rounds) + ")";
+ }
+
+void ChaCha::seek(uint64_t offset)
+ {
+ verify_key_set(m_state.empty() == false);
+
+ // Find the block offset
+ const uint64_t counter = offset / 64;
+
+ uint8_t out[8];
+
+ store_le(counter, out);
+
+ m_state[12] = load_le<uint32_t>(out, 0);
+ m_state[13] += load_le<uint32_t>(out, 1);
+
+ chacha_x8(m_buffer.data(), m_state.data(), m_rounds);
+ m_position = offset % 64;
+ }
+}
diff --git a/comm/third_party/botan/src/lib/stream/chacha/chacha.h b/comm/third_party/botan/src/lib/stream/chacha/chacha.h
new file mode 100644
index 0000000000..1749127f28
--- /dev/null
+++ b/comm/third_party/botan/src/lib/stream/chacha/chacha.h
@@ -0,0 +1,82 @@
+/*
+* ChaCha20
+* (C) 2014,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_CHACHA_H_
+#define BOTAN_CHACHA_H_
+
+#include <botan/stream_cipher.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(chacha.h)
+
+namespace Botan {
+
+/**
+* DJB's ChaCha (https://cr.yp.to/chacha.html)
+*/
+class BOTAN_PUBLIC_API(2,0) ChaCha final : public StreamCipher
+ {
+ public:
+ /**
+ * @param rounds number of rounds
+ * @note Currently only 8, 12 or 20 rounds are supported, all others
+ * will throw an exception
+ */
+ explicit ChaCha(size_t rounds = 20);
+
+ std::string provider() const override;
+
+ void cipher(const uint8_t in[], uint8_t out[], size_t length) override;
+
+ void write_keystream(uint8_t out[], size_t len) override;
+
+ void set_iv(const uint8_t iv[], size_t iv_len) override;
+
+ /*
+ * ChaCha accepts 0, 8, 12 or 24 byte IVs.
+ * The default IV is a 8 zero bytes.
+ * An IV of length 0 is treated the same as the default zero IV.
+ * An IV of length 24 selects XChaCha mode
+ */
+ bool valid_iv_length(size_t iv_len) const override;
+
+ size_t default_iv_length() const override;
+
+ Key_Length_Specification key_spec() const override;
+
+ void clear() override;
+
+ StreamCipher* clone() const override;
+
+ std::string name() const override;
+
+ void seek(uint64_t offset) override;
+
+ private:
+ void key_schedule(const uint8_t key[], size_t key_len) override;
+
+ void initialize_state();
+
+ void chacha_x8(uint8_t output[64*8], uint32_t state[16], size_t rounds);
+
+#if defined(BOTAN_HAS_CHACHA_SIMD32)
+ void chacha_simd32_x4(uint8_t output[64*4], uint32_t state[16], size_t rounds);
+#endif
+
+#if defined(BOTAN_HAS_CHACHA_AVX2)
+ void chacha_avx2_x8(uint8_t output[64*8], uint32_t state[16], size_t rounds);
+#endif
+
+ size_t m_rounds;
+ secure_vector<uint32_t> m_key;
+ secure_vector<uint32_t> m_state;
+ secure_vector<uint8_t> m_buffer;
+ size_t m_position = 0;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/stream/chacha/chacha_avx2/chacha_avx2.cpp b/comm/third_party/botan/src/lib/stream/chacha/chacha_avx2/chacha_avx2.cpp
new file mode 100644
index 0000000000..78d2365214
--- /dev/null
+++ b/comm/third_party/botan/src/lib/stream/chacha/chacha_avx2/chacha_avx2.cpp
@@ -0,0 +1,207 @@
+/*
+* (C) 2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/chacha.h>
+#include <botan/internal/simd_avx2.h>
+
+namespace Botan {
+
+//static
+BOTAN_FUNC_ISA("avx2")
+void 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];
+ 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]);
+ SIMD_8x32 R01 = SIMD_8x32::splat(state[ 1]);
+ SIMD_8x32 R02 = SIMD_8x32::splat(state[ 2]);
+ SIMD_8x32 R03 = SIMD_8x32::splat(state[ 3]);
+ SIMD_8x32 R04 = SIMD_8x32::splat(state[ 4]);
+ SIMD_8x32 R05 = SIMD_8x32::splat(state[ 5]);
+ SIMD_8x32 R06 = SIMD_8x32::splat(state[ 6]);
+ SIMD_8x32 R07 = SIMD_8x32::splat(state[ 7]);
+ SIMD_8x32 R08 = SIMD_8x32::splat(state[ 8]);
+ SIMD_8x32 R09 = SIMD_8x32::splat(state[ 9]);
+ SIMD_8x32 R10 = SIMD_8x32::splat(state[10]);
+ SIMD_8x32 R11 = SIMD_8x32::splat(state[11]);
+ SIMD_8x32 R12 = SIMD_8x32::splat(state[12]) + CTR0;
+ SIMD_8x32 R13 = SIMD_8x32::splat(state[13]) + CTR1;
+ SIMD_8x32 R14 = SIMD_8x32::splat(state[14]);
+ SIMD_8x32 R15 = SIMD_8x32::splat(state[15]);
+
+ for(size_t r = 0; r != rounds / 2; ++r)
+ {
+ R00 += R04;
+ R01 += R05;
+ R02 += R06;
+ R03 += R07;
+
+ R12 ^= R00;
+ R13 ^= R01;
+ R14 ^= R02;
+ R15 ^= R03;
+
+ R12 = R12.rotl<16>();
+ R13 = R13.rotl<16>();
+ R14 = R14.rotl<16>();
+ R15 = R15.rotl<16>();
+
+ R08 += R12;
+ R09 += R13;
+ R10 += R14;
+ R11 += R15;
+
+ R04 ^= R08;
+ R05 ^= R09;
+ R06 ^= R10;
+ R07 ^= R11;
+
+ R04 = R04.rotl<12>();
+ R05 = R05.rotl<12>();
+ R06 = R06.rotl<12>();
+ R07 = R07.rotl<12>();
+
+ R00 += R04;
+ R01 += R05;
+ R02 += R06;
+ R03 += R07;
+
+ R12 ^= R00;
+ R13 ^= R01;
+ R14 ^= R02;
+ R15 ^= R03;
+
+ R12 = R12.rotl<8>();
+ R13 = R13.rotl<8>();
+ R14 = R14.rotl<8>();
+ R15 = R15.rotl<8>();
+
+ R08 += R12;
+ R09 += R13;
+ R10 += R14;
+ R11 += R15;
+
+ R04 ^= R08;
+ R05 ^= R09;
+ R06 ^= R10;
+ R07 ^= R11;
+
+ R04 = R04.rotl<7>();
+ R05 = R05.rotl<7>();
+ R06 = R06.rotl<7>();
+ R07 = R07.rotl<7>();
+
+ R00 += R05;
+ R01 += R06;
+ R02 += R07;
+ R03 += R04;
+
+ R15 ^= R00;
+ R12 ^= R01;
+ R13 ^= R02;
+ R14 ^= R03;
+
+ R15 = R15.rotl<16>();
+ R12 = R12.rotl<16>();
+ R13 = R13.rotl<16>();
+ R14 = R14.rotl<16>();
+
+ R10 += R15;
+ R11 += R12;
+ R08 += R13;
+ R09 += R14;
+
+ R05 ^= R10;
+ R06 ^= R11;
+ R07 ^= R08;
+ R04 ^= R09;
+
+ R05 = R05.rotl<12>();
+ R06 = R06.rotl<12>();
+ R07 = R07.rotl<12>();
+ R04 = R04.rotl<12>();
+
+ R00 += R05;
+ R01 += R06;
+ R02 += R07;
+ R03 += R04;
+
+ R15 ^= R00;
+ R12 ^= R01;
+ R13 ^= R02;
+ R14 ^= R03;
+
+ R15 = R15.rotl<8>();
+ R12 = R12.rotl<8>();
+ R13 = R13.rotl<8>();
+ R14 = R14.rotl<8>();
+
+ R10 += R15;
+ R11 += R12;
+ R08 += R13;
+ R09 += R14;
+
+ R05 ^= R10;
+ R06 ^= R11;
+ R07 ^= R08;
+ R04 ^= R09;
+
+ R05 = R05.rotl<7>();
+ R06 = R06.rotl<7>();
+ R07 = R07.rotl<7>();
+ R04 = R04.rotl<7>();
+ }
+
+ R00 += SIMD_8x32::splat(state[0]);
+ R01 += SIMD_8x32::splat(state[1]);
+ R02 += SIMD_8x32::splat(state[2]);
+ R03 += SIMD_8x32::splat(state[3]);
+ R04 += SIMD_8x32::splat(state[4]);
+ R05 += SIMD_8x32::splat(state[5]);
+ R06 += SIMD_8x32::splat(state[6]);
+ R07 += SIMD_8x32::splat(state[7]);
+ R08 += SIMD_8x32::splat(state[8]);
+ R09 += SIMD_8x32::splat(state[9]);
+ R10 += SIMD_8x32::splat(state[10]);
+ R11 += SIMD_8x32::splat(state[11]);
+ R12 += SIMD_8x32::splat(state[12]) + CTR0;
+ R13 += SIMD_8x32::splat(state[13]) + CTR1;
+ R14 += SIMD_8x32::splat(state[14]);
+ R15 += SIMD_8x32::splat(state[15]);
+
+ SIMD_8x32::transpose(R00, R01, R02, R03, R04, R05, R06, R07);
+ SIMD_8x32::transpose(R08, R09, R10, R11, R12, R13, R14, R15);
+
+ R00.store_le(output);
+ R08.store_le(output + 32*1);
+ R01.store_le(output + 32*2);
+ R09.store_le(output + 32*3);
+ R02.store_le(output + 32*4);
+ R10.store_le(output + 32*5);
+ R03.store_le(output + 32*6);
+ R11.store_le(output + 32*7);
+ R04.store_le(output + 32*8);
+ R12.store_le(output + 32*9);
+ R05.store_le(output + 32*10);
+ R13.store_le(output + 32*11);
+ R06.store_le(output + 32*12);
+ R14.store_le(output + 32*13);
+ R07.store_le(output + 32*14);
+ R15.store_le(output + 32*15);
+
+ SIMD_8x32::zero_registers();
+
+ state[12] += 8;
+ if(state[12] < 8)
+ state[13]++;
+ }
+}
diff --git a/comm/third_party/botan/src/lib/stream/chacha/chacha_avx2/info.txt b/comm/third_party/botan/src/lib/stream/chacha/chacha_avx2/info.txt
new file mode 100644
index 0000000000..64f102cc58
--- /dev/null
+++ b/comm/third_party/botan/src/lib/stream/chacha/chacha_avx2/info.txt
@@ -0,0 +1,11 @@
+<defines>
+CHACHA_AVX2 -> 20180418
+</defines>
+
+<isa>
+avx2
+</isa>
+
+<requires>
+simd_avx2
+</requires>
diff --git a/comm/third_party/botan/src/lib/stream/chacha/chacha_simd32/chacha_simd32.cpp b/comm/third_party/botan/src/lib/stream/chacha/chacha_simd32/chacha_simd32.cpp
new file mode 100644
index 0000000000..6cd6acd0d8
--- /dev/null
+++ b/comm/third_party/botan/src/lib/stream/chacha/chacha_simd32/chacha_simd32.cpp
@@ -0,0 +1,205 @@
+/*
+* (C) 2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/chacha.h>
+#include <botan/internal/simd_32.h>
+
+namespace Botan {
+
+//static
+void 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];
+ const SIMD_4x32 CTR1 = SIMD_4x32(0, C < 1, C < 2, C < 3);
+
+ SIMD_4x32 R00 = SIMD_4x32::splat(state[ 0]);
+ SIMD_4x32 R01 = SIMD_4x32::splat(state[ 1]);
+ SIMD_4x32 R02 = SIMD_4x32::splat(state[ 2]);
+ SIMD_4x32 R03 = SIMD_4x32::splat(state[ 3]);
+ SIMD_4x32 R04 = SIMD_4x32::splat(state[ 4]);
+ SIMD_4x32 R05 = SIMD_4x32::splat(state[ 5]);
+ SIMD_4x32 R06 = SIMD_4x32::splat(state[ 6]);
+ SIMD_4x32 R07 = SIMD_4x32::splat(state[ 7]);
+ SIMD_4x32 R08 = SIMD_4x32::splat(state[ 8]);
+ SIMD_4x32 R09 = SIMD_4x32::splat(state[ 9]);
+ SIMD_4x32 R10 = SIMD_4x32::splat(state[10]);
+ SIMD_4x32 R11 = SIMD_4x32::splat(state[11]);
+ SIMD_4x32 R12 = SIMD_4x32::splat(state[12]) + CTR0;
+ SIMD_4x32 R13 = SIMD_4x32::splat(state[13]) + CTR1;
+ SIMD_4x32 R14 = SIMD_4x32::splat(state[14]);
+ SIMD_4x32 R15 = SIMD_4x32::splat(state[15]);
+
+ for(size_t r = 0; r != rounds / 2; ++r)
+ {
+ R00 += R04;
+ R01 += R05;
+ R02 += R06;
+ R03 += R07;
+
+ R12 ^= R00;
+ R13 ^= R01;
+ R14 ^= R02;
+ R15 ^= R03;
+
+ R12 = R12.rotl<16>();
+ R13 = R13.rotl<16>();
+ R14 = R14.rotl<16>();
+ R15 = R15.rotl<16>();
+
+ R08 += R12;
+ R09 += R13;
+ R10 += R14;
+ R11 += R15;
+
+ R04 ^= R08;
+ R05 ^= R09;
+ R06 ^= R10;
+ R07 ^= R11;
+
+ R04 = R04.rotl<12>();
+ R05 = R05.rotl<12>();
+ R06 = R06.rotl<12>();
+ R07 = R07.rotl<12>();
+
+ R00 += R04;
+ R01 += R05;
+ R02 += R06;
+ R03 += R07;
+
+ R12 ^= R00;
+ R13 ^= R01;
+ R14 ^= R02;
+ R15 ^= R03;
+
+ R12 = R12.rotl<8>();
+ R13 = R13.rotl<8>();
+ R14 = R14.rotl<8>();
+ R15 = R15.rotl<8>();
+
+ R08 += R12;
+ R09 += R13;
+ R10 += R14;
+ R11 += R15;
+
+ R04 ^= R08;
+ R05 ^= R09;
+ R06 ^= R10;
+ R07 ^= R11;
+
+ R04 = R04.rotl<7>();
+ R05 = R05.rotl<7>();
+ R06 = R06.rotl<7>();
+ R07 = R07.rotl<7>();
+
+ R00 += R05;
+ R01 += R06;
+ R02 += R07;
+ R03 += R04;
+
+ R15 ^= R00;
+ R12 ^= R01;
+ R13 ^= R02;
+ R14 ^= R03;
+
+ R15 = R15.rotl<16>();
+ R12 = R12.rotl<16>();
+ R13 = R13.rotl<16>();
+ R14 = R14.rotl<16>();
+
+ R10 += R15;
+ R11 += R12;
+ R08 += R13;
+ R09 += R14;
+
+ R05 ^= R10;
+ R06 ^= R11;
+ R07 ^= R08;
+ R04 ^= R09;
+
+ R05 = R05.rotl<12>();
+ R06 = R06.rotl<12>();
+ R07 = R07.rotl<12>();
+ R04 = R04.rotl<12>();
+
+ R00 += R05;
+ R01 += R06;
+ R02 += R07;
+ R03 += R04;
+
+ R15 ^= R00;
+ R12 ^= R01;
+ R13 ^= R02;
+ R14 ^= R03;
+
+ R15 = R15.rotl<8>();
+ R12 = R12.rotl<8>();
+ R13 = R13.rotl<8>();
+ R14 = R14.rotl<8>();
+
+ R10 += R15;
+ R11 += R12;
+ R08 += R13;
+ R09 += R14;
+
+ R05 ^= R10;
+ R06 ^= R11;
+ R07 ^= R08;
+ R04 ^= R09;
+
+ R05 = R05.rotl<7>();
+ R06 = R06.rotl<7>();
+ R07 = R07.rotl<7>();
+ R04 = R04.rotl<7>();
+ }
+
+ R00 += SIMD_4x32::splat(state[0]);
+ R01 += SIMD_4x32::splat(state[1]);
+ R02 += SIMD_4x32::splat(state[2]);
+ R03 += SIMD_4x32::splat(state[3]);
+ R04 += SIMD_4x32::splat(state[4]);
+ R05 += SIMD_4x32::splat(state[5]);
+ R06 += SIMD_4x32::splat(state[6]);
+ R07 += SIMD_4x32::splat(state[7]);
+ R08 += SIMD_4x32::splat(state[8]);
+ R09 += SIMD_4x32::splat(state[9]);
+ R10 += SIMD_4x32::splat(state[10]);
+ R11 += SIMD_4x32::splat(state[11]);
+ R12 += SIMD_4x32::splat(state[12]) + CTR0;
+ R13 += SIMD_4x32::splat(state[13]) + CTR1;
+ R14 += SIMD_4x32::splat(state[14]);
+ R15 += SIMD_4x32::splat(state[15]);
+
+ SIMD_4x32::transpose(R00, R01, R02, R03);
+ SIMD_4x32::transpose(R04, R05, R06, R07);
+ SIMD_4x32::transpose(R08, R09, R10, R11);
+ SIMD_4x32::transpose(R12, R13, R14, R15);
+
+ R00.store_le(output + 0*16);
+ R04.store_le(output + 1*16);
+ R08.store_le(output + 2*16);
+ R12.store_le(output + 3*16);
+ R01.store_le(output + 4*16);
+ R05.store_le(output + 5*16);
+ R09.store_le(output + 6*16);
+ R13.store_le(output + 7*16);
+ R02.store_le(output + 8*16);
+ R06.store_le(output + 9*16);
+ R10.store_le(output + 10*16);
+ R14.store_le(output + 11*16);
+ R03.store_le(output + 12*16);
+ R07.store_le(output + 13*16);
+ R11.store_le(output + 14*16);
+ R15.store_le(output + 15*16);
+
+ state[12] += 4;
+ if(state[12] < 4)
+ state[13]++;
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/stream/chacha/chacha_simd32/info.txt b/comm/third_party/botan/src/lib/stream/chacha/chacha_simd32/info.txt
new file mode 100644
index 0000000000..1ec932f811
--- /dev/null
+++ b/comm/third_party/botan/src/lib/stream/chacha/chacha_simd32/info.txt
@@ -0,0 +1,7 @@
+<defines>
+CHACHA_SIMD32 -> 20181104
+</defines>
+
+<requires>
+simd
+</requires>
diff --git a/comm/third_party/botan/src/lib/stream/chacha/info.txt b/comm/third_party/botan/src/lib/stream/chacha/info.txt
new file mode 100644
index 0000000000..ba9c3da341
--- /dev/null
+++ b/comm/third_party/botan/src/lib/stream/chacha/info.txt
@@ -0,0 +1,3 @@
+<defines>
+CHACHA -> 20180807
+</defines>
diff --git a/comm/third_party/botan/src/lib/stream/ctr/ctr.cpp b/comm/third_party/botan/src/lib/stream/ctr/ctr.cpp
new file mode 100644
index 0000000000..e2ed0e7126
--- /dev/null
+++ b/comm/third_party/botan/src/lib/stream/ctr/ctr.cpp
@@ -0,0 +1,256 @@
+/*
+* Counter mode
+* (C) 1999-2011,2014 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/ctr.h>
+#include <botan/exceptn.h>
+#include <botan/loadstor.h>
+#include <botan/internal/bit_ops.h>
+
+namespace Botan {
+
+CTR_BE::CTR_BE(BlockCipher* ciph) :
+ m_cipher(ciph),
+ m_block_size(m_cipher->block_size()),
+ m_ctr_size(m_block_size),
+ m_ctr_blocks(m_cipher->parallel_bytes() / m_block_size),
+ m_counter(m_cipher->parallel_bytes()),
+ m_pad(m_counter.size()),
+ m_pad_pos(0)
+ {
+ }
+
+CTR_BE::CTR_BE(BlockCipher* cipher, size_t ctr_size) :
+ m_cipher(cipher),
+ m_block_size(m_cipher->block_size()),
+ m_ctr_size(ctr_size),
+ m_ctr_blocks(m_cipher->parallel_bytes() / m_block_size),
+ m_counter(m_cipher->parallel_bytes()),
+ m_pad(m_counter.size()),
+ m_pad_pos(0)
+ {
+ BOTAN_ARG_CHECK(m_ctr_size >= 4 && m_ctr_size <= m_block_size,
+ "Invalid CTR-BE counter size");
+ }
+
+void CTR_BE::clear()
+ {
+ m_cipher->clear();
+ zeroise(m_pad);
+ zeroise(m_counter);
+ zap(m_iv);
+ m_pad_pos = 0;
+ }
+
+size_t CTR_BE::default_iv_length() const
+ {
+ return m_block_size;
+ }
+
+bool CTR_BE::valid_iv_length(size_t iv_len) const
+ {
+ return (iv_len <= m_block_size);
+ }
+
+Key_Length_Specification CTR_BE::key_spec() const
+ {
+ return m_cipher->key_spec();
+ }
+
+CTR_BE* CTR_BE::clone() const
+ {
+ return new CTR_BE(m_cipher->clone(), m_ctr_size);
+ }
+
+void CTR_BE::key_schedule(const uint8_t key[], size_t key_len)
+ {
+ m_cipher->set_key(key, key_len);
+
+ // Set a default all-zeros IV
+ set_iv(nullptr, 0);
+ }
+
+std::string CTR_BE::name() const
+ {
+ if(m_ctr_size == m_block_size)
+ return ("CTR-BE(" + m_cipher->name() + ")");
+ else
+ return ("CTR-BE(" + m_cipher->name() + "," + std::to_string(m_ctr_size) + ")");
+
+ }
+
+void CTR_BE::cipher(const uint8_t in[], uint8_t out[], size_t length)
+ {
+ verify_key_set(m_iv.empty() == false);
+
+ const uint8_t* pad_bits = &m_pad[0];
+ const size_t pad_size = m_pad.size();
+
+ if(m_pad_pos > 0)
+ {
+ const size_t avail = pad_size - m_pad_pos;
+ const size_t take = std::min(length, avail);
+ xor_buf(out, in, pad_bits + m_pad_pos, take);
+ length -= take;
+ in += take;
+ out += take;
+ m_pad_pos += take;
+
+ if(take == avail)
+ {
+ add_counter(m_ctr_blocks);
+ m_cipher->encrypt_n(m_counter.data(), m_pad.data(), m_ctr_blocks);
+ m_pad_pos = 0;
+ }
+ }
+
+ while(length >= pad_size)
+ {
+ xor_buf(out, in, pad_bits, pad_size);
+ length -= pad_size;
+ in += pad_size;
+ out += pad_size;
+
+ add_counter(m_ctr_blocks);
+ m_cipher->encrypt_n(m_counter.data(), m_pad.data(), m_ctr_blocks);
+ }
+
+ xor_buf(out, in, pad_bits, length);
+ m_pad_pos += length;
+ }
+
+void CTR_BE::set_iv(const uint8_t iv[], size_t iv_len)
+ {
+ if(!valid_iv_length(iv_len))
+ throw Invalid_IV_Length(name(), iv_len);
+
+ m_iv.resize(m_block_size);
+ zeroise(m_iv);
+ buffer_insert(m_iv, 0, iv, iv_len);
+
+ seek(0);
+ }
+
+void CTR_BE::add_counter(const uint64_t counter)
+ {
+ const size_t ctr_size = m_ctr_size;
+ const size_t ctr_blocks = m_ctr_blocks;
+ const size_t BS = m_block_size;
+
+ if(ctr_size == 4)
+ {
+ const size_t off = (BS - 4);
+ const uint32_t low32 = static_cast<uint32_t>(counter + load_be<uint32_t>(&m_counter[off], 0));
+
+ for(size_t i = 0; i != ctr_blocks; ++i)
+ {
+ store_be(uint32_t(low32 + i), &m_counter[i*BS+off]);
+ }
+ }
+ else if(ctr_size == 8)
+ {
+ const size_t off = (BS - 8);
+ const uint64_t low64 = counter + load_be<uint64_t>(&m_counter[off], 0);
+
+ for(size_t i = 0; i != ctr_blocks; ++i)
+ {
+ store_be(uint64_t(low64 + i), &m_counter[i*BS+off]);
+ }
+ }
+ else if(ctr_size == 16)
+ {
+ const size_t off = (BS - 16);
+ uint64_t b0 = load_be<uint64_t>(&m_counter[off], 0);
+ uint64_t b1 = load_be<uint64_t>(&m_counter[off], 1);
+ b1 += counter;
+ b0 += (b1 < counter) ? 1 : 0; // carry
+
+ for(size_t i = 0; i != ctr_blocks; ++i)
+ {
+ store_be(b0, &m_counter[i*BS+off]);
+ store_be(b1, &m_counter[i*BS+off+8]);
+ b1 += 1;
+ b0 += (b1 == 0); // carry
+ }
+ }
+ else
+ {
+ for(size_t i = 0; i != ctr_blocks; ++i)
+ {
+ uint64_t local_counter = counter;
+ uint16_t carry = static_cast<uint8_t>(local_counter);
+ for(size_t j = 0; (carry || local_counter) && j != ctr_size; ++j)
+ {
+ const size_t off = i*BS + (BS-1-j);
+ const uint16_t cnt = static_cast<uint16_t>(m_counter[off]) + carry;
+ m_counter[off] = static_cast<uint8_t>(cnt);
+ local_counter = (local_counter >> 8);
+ carry = (cnt >> 8) + static_cast<uint8_t>(local_counter);
+ }
+ }
+ }
+ }
+
+void CTR_BE::seek(uint64_t offset)
+ {
+ verify_key_set(m_iv.empty() == false);
+
+ const uint64_t base_counter = m_ctr_blocks * (offset / m_counter.size());
+
+ zeroise(m_counter);
+ buffer_insert(m_counter, 0, m_iv);
+
+ const size_t BS = m_block_size;
+
+ // Set m_counter blocks to IV, IV + 1, ... IV + n
+
+ if(m_ctr_size == 4 && BS >= 8)
+ {
+ const uint32_t low32 = load_be<uint32_t>(&m_counter[BS-4], 0);
+
+ 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);
+ written *= 2;
+ }
+ }
+ else
+ {
+ for(size_t i = 1; i != m_ctr_blocks; ++i)
+ {
+ copy_mem(&m_counter[i*BS], &m_counter[0], BS - 4);
+ }
+ }
+
+ for(size_t i = 1; i != m_ctr_blocks; ++i)
+ {
+ const uint32_t c = static_cast<uint32_t>(low32 + i);
+ store_be(c, &m_counter[(BS-4)+i*BS]);
+ }
+ }
+ else
+ {
+ // do everything sequentially:
+ for(size_t i = 1; i != m_ctr_blocks; ++i)
+ {
+ buffer_insert(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)])
+ break;
+ }
+ }
+
+ if(base_counter > 0)
+ add_counter(base_counter);
+
+ m_cipher->encrypt_n(m_counter.data(), m_pad.data(), m_ctr_blocks);
+ m_pad_pos = offset % m_counter.size();
+ }
+}
diff --git a/comm/third_party/botan/src/lib/stream/ctr/ctr.h b/comm/third_party/botan/src/lib/stream/ctr/ctr.h
new file mode 100644
index 0000000000..0687c606e1
--- /dev/null
+++ b/comm/third_party/botan/src/lib/stream/ctr/ctr.h
@@ -0,0 +1,65 @@
+/*
+* CTR-BE Mode
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_CTR_BE_H_
+#define BOTAN_CTR_BE_H_
+
+#include <botan/block_cipher.h>
+#include <botan/stream_cipher.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(ctr.h)
+
+namespace Botan {
+
+/**
+* CTR-BE (Counter mode, big-endian)
+*/
+class BOTAN_PUBLIC_API(2,0) CTR_BE final : public StreamCipher
+ {
+ public:
+ void cipher(const uint8_t in[], uint8_t out[], size_t length) override;
+
+ void set_iv(const uint8_t iv[], size_t iv_len) override;
+
+ size_t default_iv_length() const override;
+
+ bool valid_iv_length(size_t iv_len) const override;
+
+ Key_Length_Specification key_spec() const override;
+
+ std::string name() const override;
+
+ CTR_BE* clone() const override;
+
+ void clear() override;
+
+ /**
+ * @param cipher the block cipher to use
+ */
+ explicit CTR_BE(BlockCipher* cipher);
+
+ CTR_BE(BlockCipher* cipher, size_t ctr_size);
+
+ void seek(uint64_t offset) override;
+ private:
+ void key_schedule(const uint8_t key[], size_t key_len) override;
+ void add_counter(const uint64_t counter);
+
+ std::unique_ptr<BlockCipher> m_cipher;
+
+ const size_t m_block_size;
+ const size_t m_ctr_size;
+ const size_t m_ctr_blocks;
+
+ secure_vector<uint8_t> m_counter, m_pad;
+ std::vector<uint8_t> m_iv;
+ size_t m_pad_pos;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/stream/ctr/info.txt b/comm/third_party/botan/src/lib/stream/ctr/info.txt
new file mode 100644
index 0000000000..270ceecf8b
--- /dev/null
+++ b/comm/third_party/botan/src/lib/stream/ctr/info.txt
@@ -0,0 +1,7 @@
+<defines>
+CTR_BE -> 20131128
+</defines>
+
+<requires>
+block
+</requires>
diff --git a/comm/third_party/botan/src/lib/stream/info.txt b/comm/third_party/botan/src/lib/stream/info.txt
new file mode 100644
index 0000000000..4f62c5a7c1
--- /dev/null
+++ b/comm/third_party/botan/src/lib/stream/info.txt
@@ -0,0 +1,7 @@
+<defines>
+STREAM_CIPHER -> 20131128
+</defines>
+
+<header:public>
+stream_cipher.h
+</header:public>
diff --git a/comm/third_party/botan/src/lib/stream/ofb/info.txt b/comm/third_party/botan/src/lib/stream/ofb/info.txt
new file mode 100644
index 0000000000..2675e78573
--- /dev/null
+++ b/comm/third_party/botan/src/lib/stream/ofb/info.txt
@@ -0,0 +1,7 @@
+<defines>
+OFB -> 20131128
+</defines>
+
+<requires>
+block
+</requires>
diff --git a/comm/third_party/botan/src/lib/stream/ofb/ofb.cpp b/comm/third_party/botan/src/lib/stream/ofb/ofb.cpp
new file mode 100644
index 0000000000..dde4681171
--- /dev/null
+++ b/comm/third_party/botan/src/lib/stream/ofb/ofb.cpp
@@ -0,0 +1,92 @@
+/*
+* OFB Mode
+* (C) 1999-2007,2014 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/ofb.h>
+#include <botan/exceptn.h>
+
+namespace Botan {
+
+OFB::OFB(BlockCipher* cipher) :
+ m_cipher(cipher),
+ m_buffer(m_cipher->block_size()),
+ m_buf_pos(0)
+ {
+ }
+
+void OFB::clear()
+ {
+ m_cipher->clear();
+ zeroise(m_buffer);
+ m_buf_pos = 0;
+ }
+
+void OFB::key_schedule(const uint8_t key[], size_t key_len)
+ {
+ m_cipher->set_key(key, key_len);
+
+ // Set a default all-zeros IV
+ set_iv(nullptr, 0);
+ }
+
+std::string OFB::name() const
+ {
+ return "OFB(" + m_cipher->name() + ")";
+ }
+
+size_t OFB::default_iv_length() const
+ {
+ return m_cipher->block_size();
+ }
+
+bool OFB::valid_iv_length(size_t iv_len) const
+ {
+ return (iv_len <= m_cipher->block_size());
+ }
+
+Key_Length_Specification OFB::key_spec() const
+ {
+ return m_cipher->key_spec();
+ }
+
+OFB* OFB::clone() const
+ {
+ return new OFB(m_cipher->clone());
+ }
+
+void OFB::cipher(const uint8_t in[], uint8_t out[], size_t length)
+ {
+ while(length >= m_buffer.size() - m_buf_pos)
+ {
+ xor_buf(out, in, &m_buffer[m_buf_pos], m_buffer.size() - m_buf_pos);
+ length -= (m_buffer.size() - m_buf_pos);
+ in += (m_buffer.size() - m_buf_pos);
+ out += (m_buffer.size() - m_buf_pos);
+ m_cipher->encrypt(m_buffer);
+ m_buf_pos = 0;
+ }
+ xor_buf(out, in, &m_buffer[m_buf_pos], length);
+ m_buf_pos += length;
+ }
+
+void OFB::set_iv(const uint8_t iv[], size_t iv_len)
+ {
+ if(!valid_iv_length(iv_len))
+ throw Invalid_IV_Length(name(), iv_len);
+
+ zeroise(m_buffer);
+ buffer_insert(m_buffer, 0, iv, iv_len);
+
+ m_cipher->encrypt(m_buffer);
+ m_buf_pos = 0;
+ }
+
+
+void OFB::seek(uint64_t)
+ {
+ throw Not_Implemented("OFB does not support seeking");
+ }
+}
diff --git a/comm/third_party/botan/src/lib/stream/ofb/ofb.h b/comm/third_party/botan/src/lib/stream/ofb/ofb.h
new file mode 100644
index 0000000000..994d3d198d
--- /dev/null
+++ b/comm/third_party/botan/src/lib/stream/ofb/ofb.h
@@ -0,0 +1,56 @@
+/*
+* OFB Mode
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_OUTPUT_FEEDBACK_MODE_H_
+#define BOTAN_OUTPUT_FEEDBACK_MODE_H_
+
+#include <botan/stream_cipher.h>
+#include <botan/block_cipher.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(ofb.h)
+
+namespace Botan {
+
+/**
+* Output Feedback Mode
+*/
+class BOTAN_PUBLIC_API(2,0) OFB final : public StreamCipher
+ {
+ public:
+ void cipher(const uint8_t in[], uint8_t out[], size_t length) override;
+
+ void set_iv(const uint8_t iv[], size_t iv_len) override;
+
+ size_t default_iv_length() const override;
+
+ bool valid_iv_length(size_t iv_len) const override;
+
+ Key_Length_Specification key_spec() const override;
+
+ std::string name() const override;
+
+ OFB* clone() const override;
+
+ void clear() override;
+
+ /**
+ * @param cipher the block cipher to use
+ */
+ explicit OFB(BlockCipher* cipher);
+
+ void seek(uint64_t offset) override;
+ private:
+ void key_schedule(const uint8_t key[], size_t key_len) override;
+
+ std::unique_ptr<BlockCipher> m_cipher;
+ secure_vector<uint8_t> m_buffer;
+ size_t m_buf_pos;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/stream/rc4/info.txt b/comm/third_party/botan/src/lib/stream/rc4/info.txt
new file mode 100644
index 0000000000..97cc227602
--- /dev/null
+++ b/comm/third_party/botan/src/lib/stream/rc4/info.txt
@@ -0,0 +1,3 @@
+<defines>
+RC4 -> 20131128
+</defines>
diff --git a/comm/third_party/botan/src/lib/stream/rc4/rc4.cpp b/comm/third_party/botan/src/lib/stream/rc4/rc4.cpp
new file mode 100644
index 0000000000..8bb01a2387
--- /dev/null
+++ b/comm/third_party/botan/src/lib/stream/rc4/rc4.cpp
@@ -0,0 +1,133 @@
+/*
+* RC4
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/rc4.h>
+#include <botan/exceptn.h>
+
+namespace Botan {
+
+/*
+* Combine cipher stream with message
+*/
+void RC4::cipher(const uint8_t in[], uint8_t out[], size_t length)
+ {
+ verify_key_set(m_state.empty() == false);
+
+ while(length >= m_buffer.size() - m_position)
+ {
+ xor_buf(out, in, &m_buffer[m_position], m_buffer.size() - m_position);
+ length -= (m_buffer.size() - m_position);
+ in += (m_buffer.size() - m_position);
+ out += (m_buffer.size() - m_position);
+ generate();
+ }
+ xor_buf(out, in, &m_buffer[m_position], length);
+ m_position += length;
+ }
+
+StreamCipher* RC4::clone() const
+ {
+ return new RC4(m_SKIP);
+ }
+
+Key_Length_Specification RC4::key_spec() const
+ {
+ return Key_Length_Specification(1, 256);
+ }
+
+void RC4::set_iv(const uint8_t*, size_t length)
+ {
+ if(length > 0)
+ throw Invalid_IV_Length("RC4", length);
+ }
+
+/*
+* 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]; m_Y = (m_Y + SX) % 256; SY = m_state[m_Y];
+ m_state[m_X+1] = SY; m_state[m_Y] = SX;
+ m_buffer[i] = m_state[(SX + SY) % 256];
+
+ SX = m_state[m_X+2]; m_Y = (m_Y + SX) % 256; SY = m_state[m_Y];
+ m_state[m_X+2] = SY; m_state[m_Y] = SX;
+ m_buffer[i+1] = m_state[(SX + SY) % 256];
+
+ SX = m_state[m_X+3]; m_Y = (m_Y + SX) % 256; SY = m_state[m_Y];
+ m_state[m_X+3] = SY; m_state[m_Y] = SX;
+ m_buffer[i+2] = m_state[(SX + SY) % 256];
+
+ m_X = (m_X + 4) % 256;
+ SX = m_state[m_X]; m_Y = (m_Y + SX) % 256; SY = m_state[m_Y];
+ m_state[m_X] = SY; m_state[m_Y] = SX;
+ m_buffer[i+3] = m_state[(SX + SY) % 256];
+ }
+ m_position = 0;
+ }
+
+/*
+* RC4 Key Schedule
+*/
+void RC4::key_schedule(const uint8_t key[], size_t length)
+ {
+ m_state.resize(256);
+ m_buffer.resize(256);
+
+ m_position = m_X = m_Y = 0;
+
+ for(size_t i = 0; i != 256; ++i)
+ m_state[i] = static_cast<uint8_t>(i);
+
+ for(size_t i = 0, state_index = 0; i != 256; ++i)
+ {
+ state_index = (state_index + key[i % length] + m_state[i]) % 256;
+ std::swap(m_state[i], m_state[state_index]);
+ }
+
+ for(size_t i = 0; i <= m_SKIP; i += m_buffer.size())
+ generate();
+
+ m_position += (m_SKIP % m_buffer.size());
+ }
+
+/*
+* Return the name of this type
+*/
+std::string RC4::name() const
+ {
+ if(m_SKIP == 0)
+ return "RC4";
+ else if(m_SKIP == 256)
+ return "MARK-4";
+ else
+ return "RC4(" + std::to_string(m_SKIP) + ")";
+ }
+
+/*
+* Clear memory of sensitive data
+*/
+void RC4::clear()
+ {
+ zap(m_state);
+ zap(m_buffer);
+ m_position = m_X = m_Y = 0;
+ }
+
+/*
+* RC4 Constructor
+*/
+RC4::RC4(size_t s) : m_SKIP(s) {}
+
+void RC4::seek(uint64_t)
+ {
+ throw Not_Implemented("RC4 does not support seeking");
+ }
+}
diff --git a/comm/third_party/botan/src/lib/stream/rc4/rc4.h b/comm/third_party/botan/src/lib/stream/rc4/rc4.h
new file mode 100644
index 0000000000..eff3c568db
--- /dev/null
+++ b/comm/third_party/botan/src/lib/stream/rc4/rc4.h
@@ -0,0 +1,57 @@
+/*
+* RC4
+* (C) 1999-2008 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_RC4_H_
+#define BOTAN_RC4_H_
+
+#include <botan/stream_cipher.h>
+#include <botan/types.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(rc4.h)
+
+namespace Botan {
+
+/**
+* RC4 stream cipher
+*/
+class BOTAN_PUBLIC_API(2,0) RC4 final : public StreamCipher
+ {
+ public:
+ void cipher(const uint8_t in[], uint8_t out[], size_t length) override;
+
+ void set_iv(const uint8_t iv[], size_t iv_len) override;
+
+ void clear() override;
+ std::string name() const override;
+
+ StreamCipher* clone() const override;
+
+ Key_Length_Specification key_spec() const override;
+
+ /**
+ * @param skip skip this many initial bytes in the keystream
+ */
+ explicit RC4(size_t skip = 0);
+
+ ~RC4() { clear(); }
+
+ void seek(uint64_t offset) override;
+ private:
+ void key_schedule(const uint8_t[], size_t) override;
+ void generate();
+
+ const size_t m_SKIP;
+ uint8_t m_X = 0;
+ uint8_t m_Y = 0;
+ secure_vector<uint8_t> m_state;
+ secure_vector<uint8_t> m_buffer;
+ size_t m_position = 0;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/stream/salsa20/info.txt b/comm/third_party/botan/src/lib/stream/salsa20/info.txt
new file mode 100644
index 0000000000..8e9bfa5683
--- /dev/null
+++ b/comm/third_party/botan/src/lib/stream/salsa20/info.txt
@@ -0,0 +1,3 @@
+<defines>
+SALSA20 -> 20171114
+</defines>
diff --git a/comm/third_party/botan/src/lib/stream/salsa20/salsa20.cpp b/comm/third_party/botan/src/lib/stream/salsa20/salsa20.cpp
new file mode 100644
index 0000000000..1e30391d21
--- /dev/null
+++ b/comm/third_party/botan/src/lib/stream/salsa20/salsa20.cpp
@@ -0,0 +1,302 @@
+/*
+* Salsa20 / XSalsa20
+* (C) 1999-2010,2014 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/salsa20.h>
+#include <botan/exceptn.h>
+#include <botan/loadstor.h>
+#include <botan/rotate.h>
+
+namespace Botan {
+
+#define SALSA20_QUARTER_ROUND(x1, x2, x3, x4) \
+ do { \
+ x2 ^= rotl<7>(x1 + x4); \
+ x3 ^= rotl<9>(x2 + x1); \
+ x4 ^= rotl<13>(x3 + x2); \
+ x1 ^= rotl<18>(x4 + x3); \
+ } while(0)
+
+/*
+* Generate HSalsa20 cipher stream (for XSalsa20 IV setup)
+*/
+//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];
+
+ for(size_t i = 0; i != 10; ++i)
+ {
+ SALSA20_QUARTER_ROUND(x00, x04, x08, x12);
+ SALSA20_QUARTER_ROUND(x05, x09, x13, x01);
+ SALSA20_QUARTER_ROUND(x10, x14, x02, x06);
+ SALSA20_QUARTER_ROUND(x15, x03, x07, x11);
+
+ SALSA20_QUARTER_ROUND(x00, x01, x02, x03);
+ SALSA20_QUARTER_ROUND(x05, x06, x07, x04);
+ SALSA20_QUARTER_ROUND(x10, x11, x08, x09);
+ SALSA20_QUARTER_ROUND(x15, x12, x13, x14);
+ }
+
+ output[0] = x00;
+ output[1] = x05;
+ output[2] = x10;
+ output[3] = x15;
+ output[4] = x06;
+ output[5] = x07;
+ output[6] = x08;
+ output[7] = x09;
+ }
+
+/*
+* Generate Salsa20 cipher stream
+*/
+//static
+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];
+
+ for(size_t i = 0; i != rounds / 2; ++i)
+ {
+ SALSA20_QUARTER_ROUND(x00, x04, x08, x12);
+ SALSA20_QUARTER_ROUND(x05, x09, x13, x01);
+ SALSA20_QUARTER_ROUND(x10, x14, x02, x06);
+ SALSA20_QUARTER_ROUND(x15, x03, x07, x11);
+
+ SALSA20_QUARTER_ROUND(x00, x01, x02, x03);
+ SALSA20_QUARTER_ROUND(x05, x06, x07, x04);
+ SALSA20_QUARTER_ROUND(x10, x11, x08, x09);
+ SALSA20_QUARTER_ROUND(x15, x12, x13, x14);
+ }
+
+ store_le(x00 + input[ 0], output + 4 * 0);
+ store_le(x01 + input[ 1], output + 4 * 1);
+ store_le(x02 + input[ 2], output + 4 * 2);
+ store_le(x03 + input[ 3], output + 4 * 3);
+ store_le(x04 + input[ 4], output + 4 * 4);
+ store_le(x05 + input[ 5], output + 4 * 5);
+ store_le(x06 + input[ 6], output + 4 * 6);
+ store_le(x07 + input[ 7], output + 4 * 7);
+ store_le(x08 + input[ 8], output + 4 * 8);
+ store_le(x09 + input[ 9], output + 4 * 9);
+ store_le(x10 + input[10], output + 4 * 10);
+ store_le(x11 + input[11], output + 4 * 11);
+ store_le(x12 + input[12], output + 4 * 12);
+ store_le(x13 + input[13], output + 4 * 13);
+ store_le(x14 + input[14], output + 4 * 14);
+ store_le(x15 + input[15], output + 4 * 15);
+ }
+
+#undef SALSA20_QUARTER_ROUND
+
+/*
+* Combine cipher stream with message
+*/
+void Salsa20::cipher(const uint8_t in[], uint8_t out[], size_t length)
+ {
+ verify_key_set(m_state.empty() == false);
+
+ while(length >= m_buffer.size() - m_position)
+ {
+ const size_t available = m_buffer.size() - m_position;
+
+ xor_buf(out, in, &m_buffer[m_position], available);
+ salsa_core(m_buffer.data(), m_state.data(), 20);
+
+ ++m_state[8];
+ m_state[9] += (m_state[8] == 0);
+
+ length -= available;
+ in += available;
+ out += available;
+
+ m_position = 0;
+ }
+
+ xor_buf(out, in, &m_buffer[m_position], length);
+
+ m_position += length;
+ }
+
+void Salsa20::initialize_state()
+ {
+ static const uint32_t TAU[] =
+ { 0x61707865, 0x3120646e, 0x79622d36, 0x6b206574 };
+
+ static const uint32_t SIGMA[] =
+ { 0x61707865, 0x3320646e, 0x79622d32, 0x6b206574 };
+
+ m_state[1] = m_key[0];
+ m_state[2] = m_key[1];
+ m_state[3] = m_key[2];
+ m_state[4] = m_key[3];
+
+ if(m_key.size() == 4)
+ {
+ m_state[0] = TAU[0];
+ m_state[5] = TAU[1];
+ m_state[10] = TAU[2];
+ m_state[15] = TAU[3];
+ m_state[11] = m_key[0];
+ m_state[12] = m_key[1];
+ m_state[13] = m_key[2];
+ m_state[14] = m_key[3];
+ }
+ else
+ {
+ m_state[0] = SIGMA[0];
+ m_state[5] = SIGMA[1];
+ m_state[10] = SIGMA[2];
+ m_state[15] = SIGMA[3];
+ m_state[11] = m_key[4];
+ m_state[12] = m_key[5];
+ m_state[13] = m_key[6];
+ m_state[14] = m_key[7];
+ }
+
+ m_state[6] = 0;
+ m_state[7] = 0;
+ m_state[8] = 0;
+ m_state[9] = 0;
+
+ m_position = 0;
+ }
+
+/*
+* Salsa20 Key Schedule
+*/
+void Salsa20::key_schedule(const uint8_t key[], size_t length)
+ {
+ m_key.resize(length / 4);
+ load_le<uint32_t>(m_key.data(), key, m_key.size());
+
+ m_state.resize(16);
+ m_buffer.resize(64);
+
+ set_iv(nullptr, 0);
+ }
+
+/*
+* Set the Salsa IV
+*/
+void Salsa20::set_iv(const uint8_t iv[], size_t length)
+ {
+ verify_key_set(m_state.empty() == false);
+
+ if(!valid_iv_length(length))
+ throw Invalid_IV_Length(name(), length);
+
+ initialize_state();
+
+ if(length == 0)
+ {
+ // Salsa20 null IV
+ m_state[6] = 0;
+ m_state[7] = 0;
+ }
+ else if(length == 8)
+ {
+ // Salsa20
+ m_state[6] = load_le<uint32_t>(iv, 0);
+ m_state[7] = load_le<uint32_t>(iv, 1);
+ }
+ else
+ {
+ // XSalsa20
+ m_state[6] = load_le<uint32_t>(iv, 0);
+ m_state[7] = load_le<uint32_t>(iv, 1);
+ m_state[8] = load_le<uint32_t>(iv, 2);
+ m_state[9] = load_le<uint32_t>(iv, 3);
+
+ secure_vector<uint32_t> hsalsa(8);
+ hsalsa20(hsalsa.data(), m_state.data());
+
+ m_state[ 1] = hsalsa[0];
+ m_state[ 2] = hsalsa[1];
+ m_state[ 3] = hsalsa[2];
+ m_state[ 4] = hsalsa[3];
+ m_state[ 6] = load_le<uint32_t>(iv, 4);
+ m_state[ 7] = load_le<uint32_t>(iv, 5);
+ m_state[11] = hsalsa[4];
+ m_state[12] = hsalsa[5];
+ m_state[13] = hsalsa[6];
+ m_state[14] = hsalsa[7];
+ }
+
+ m_state[8] = 0;
+ m_state[9] = 0;
+
+ salsa_core(m_buffer.data(), m_state.data(), 20);
+ ++m_state[8];
+ m_state[9] += (m_state[8] == 0);
+
+ m_position = 0;
+ }
+
+bool Salsa20::valid_iv_length(size_t iv_len) const
+ {
+ return (iv_len == 0 || iv_len == 8 || iv_len == 24);
+ }
+
+size_t Salsa20::default_iv_length() const
+ {
+ return 24;
+ }
+
+Key_Length_Specification Salsa20::key_spec() const
+ {
+ return Key_Length_Specification(16, 32, 16);
+ }
+
+StreamCipher* Salsa20::clone() const
+ {
+ return new Salsa20;
+ }
+
+std::string Salsa20::name() const
+ {
+ return "Salsa20";
+ }
+
+/*
+* Clear memory of sensitive data
+*/
+void Salsa20::clear()
+ {
+ zap(m_key);
+ zap(m_state);
+ zap(m_buffer);
+ m_position = 0;
+ }
+
+void Salsa20::seek(uint64_t offset)
+ {
+ verify_key_set(m_state.empty() == false);
+
+ // Find the block offset
+ const uint64_t counter = offset / 64;
+ uint8_t counter8[8];
+ store_le(counter, counter8);
+
+ m_state[8] = load_le<uint32_t>(counter8, 0);
+ m_state[9] += load_le<uint32_t>(counter8, 1);
+
+ salsa_core(m_buffer.data(), m_state.data(), 20);
+
+ ++m_state[8];
+ m_state[9] += (m_state[8] == 0);
+
+ m_position = offset % 64;
+ }
+}
diff --git a/comm/third_party/botan/src/lib/stream/salsa20/salsa20.h b/comm/third_party/botan/src/lib/stream/salsa20/salsa20.h
new file mode 100644
index 0000000000..6ad0da7709
--- /dev/null
+++ b/comm/third_party/botan/src/lib/stream/salsa20/salsa20.h
@@ -0,0 +1,54 @@
+/*
+* Salsa20 / XSalsa20
+* (C) 1999-2010 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_SALSA20_H_
+#define BOTAN_SALSA20_H_
+
+#include <botan/stream_cipher.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(salsa20.h)
+
+namespace Botan {
+
+/**
+* DJB's Salsa20 (and XSalsa20)
+*/
+class BOTAN_PUBLIC_API(2,0) Salsa20 final : public StreamCipher
+ {
+ public:
+ void cipher(const uint8_t in[], uint8_t out[], size_t length) override;
+
+ void set_iv(const uint8_t iv[], size_t iv_len) override;
+
+ bool valid_iv_length(size_t iv_len) const override;
+
+ size_t default_iv_length() const override;
+
+ Key_Length_Specification key_spec() const override;
+
+ void clear() override;
+ std::string name() const override;
+ StreamCipher* clone() const override;
+
+ static void salsa_core(uint8_t output[64], const uint32_t input[16], size_t rounds);
+ static void hsalsa20(uint32_t output[8], const uint32_t input[16]);
+
+ void seek(uint64_t offset) override;
+ private:
+ void key_schedule(const uint8_t key[], size_t key_len) override;
+
+ void initialize_state();
+
+ secure_vector<uint32_t> m_key;
+ secure_vector<uint32_t> m_state;
+ secure_vector<uint8_t> m_buffer;
+ size_t m_position = 0;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/stream/shake_cipher/info.txt b/comm/third_party/botan/src/lib/stream/shake_cipher/info.txt
new file mode 100644
index 0000000000..ca2a0c079c
--- /dev/null
+++ b/comm/third_party/botan/src/lib/stream/shake_cipher/info.txt
@@ -0,0 +1,7 @@
+<defines>
+SHAKE_CIPHER -> 20161018
+</defines>
+
+<requires>
+sha3
+</requires>
diff --git a/comm/third_party/botan/src/lib/stream/shake_cipher/shake_cipher.cpp b/comm/third_party/botan/src/lib/stream/shake_cipher/shake_cipher.cpp
new file mode 100644
index 0000000000..f1920959e3
--- /dev/null
+++ b/comm/third_party/botan/src/lib/stream/shake_cipher/shake_cipher.cpp
@@ -0,0 +1,90 @@
+/*
+* SHAKE-128
+* (C) 2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/shake_cipher.h>
+#include <botan/exceptn.h>
+#include <botan/sha3.h>
+#include <botan/loadstor.h>
+
+namespace Botan {
+
+SHAKE_128_Cipher::SHAKE_128_Cipher() :
+ m_buf_pos(0)
+ {}
+
+void SHAKE_128_Cipher::cipher(const uint8_t in[], uint8_t out[], size_t length)
+ {
+ const size_t SHAKE_128_BYTERATE = (1600-256)/8;
+
+ verify_key_set(m_state.empty() == false);
+
+ while(length >= SHAKE_128_BYTERATE - m_buf_pos)
+ {
+ xor_buf(out, in, &m_buffer[m_buf_pos], SHAKE_128_BYTERATE - m_buf_pos);
+ length -= (SHAKE_128_BYTERATE - m_buf_pos);
+ in += (SHAKE_128_BYTERATE - m_buf_pos);
+ out += (SHAKE_128_BYTERATE - m_buf_pos);
+
+ SHA_3::permute(m_state.data());
+ copy_out_le(m_buffer.data(), SHAKE_128_BYTERATE, m_state.data());
+
+ m_buf_pos = 0;
+ }
+ xor_buf(out, in, &m_buffer[m_buf_pos], length);
+ m_buf_pos += length;
+ }
+
+void SHAKE_128_Cipher::key_schedule(const uint8_t key[], size_t length)
+ {
+ const size_t SHAKE_128_BITRATE = (1600-256);
+ m_state.resize(25);
+ m_buffer.resize(SHAKE_128_BITRATE/8);
+ zeroise(m_state);
+
+ const size_t S_pos = SHA_3::absorb(SHAKE_128_BITRATE, m_state, 0, key, length);
+ SHA_3::finish(SHAKE_128_BITRATE, m_state, S_pos, 0x1F, 0x80);
+ copy_out_le(m_buffer.data(), m_buffer.size(), m_state.data());
+ }
+
+void SHAKE_128_Cipher::clear()
+ {
+ zap(m_state);
+ zap(m_buffer);
+ m_buf_pos = 0;
+ }
+
+void SHAKE_128_Cipher::set_iv(const uint8_t[], size_t length)
+ {
+ /*
+ * This could be supported in some way (say, by treating iv as
+ * a prefix or suffix of the key).
+ */
+ if(length != 0)
+ throw Invalid_IV_Length(name(), length);
+ }
+
+void SHAKE_128_Cipher::seek(uint64_t)
+ {
+ throw Not_Implemented("SHAKE_128_Cipher::seek");
+ }
+
+Key_Length_Specification SHAKE_128_Cipher::key_spec() const
+ {
+ return Key_Length_Specification(1, 160);
+ }
+
+std::string SHAKE_128_Cipher::name() const
+ {
+ return "SHAKE-128";
+ }
+
+StreamCipher* SHAKE_128_Cipher::clone() const
+ {
+ return new SHAKE_128_Cipher;
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/stream/shake_cipher/shake_cipher.h b/comm/third_party/botan/src/lib/stream/shake_cipher/shake_cipher.h
new file mode 100644
index 0000000000..85eaec2a85
--- /dev/null
+++ b/comm/third_party/botan/src/lib/stream/shake_cipher/shake_cipher.h
@@ -0,0 +1,57 @@
+/*
+* SHAKE-128 as a stream cipher
+* (C) 2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_SHAKE128_CIPHER_H_
+#define BOTAN_SHAKE128_CIPHER_H_
+
+#include <botan/stream_cipher.h>
+#include <botan/secmem.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(shake_cipher.h)
+
+namespace Botan {
+
+/**
+* SHAKE-128 XOF presented as a stream cipher
+*/
+class BOTAN_PUBLIC_API(2,0) SHAKE_128_Cipher final : public StreamCipher
+ {
+ public:
+ SHAKE_128_Cipher();
+
+ /**
+ * Produce more XOF output
+ */
+ void cipher(const uint8_t in[], uint8_t out[], size_t length) override;
+
+ /**
+ * Seeking is not supported, this function will throw
+ */
+ void seek(uint64_t offset) override;
+
+ /**
+ * IV not supported, this function will throw unless iv_len == 0
+ */
+ void set_iv(const uint8_t iv[], size_t iv_len) override;
+
+ Key_Length_Specification key_spec() const override;
+
+ void clear() override;
+ std::string name() const override;
+ StreamCipher* clone() const override;
+
+ private:
+ void key_schedule(const uint8_t key[], size_t key_len) override;
+
+ secure_vector<uint64_t> m_state; // internal state
+ secure_vector<uint8_t> m_buffer; // ciphertext buffer
+ size_t m_buf_pos; // position in m_buffer
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/stream/stream_cipher.cpp b/comm/third_party/botan/src/lib/stream/stream_cipher.cpp
new file mode 100644
index 0000000000..340682ce25
--- /dev/null
+++ b/comm/third_party/botan/src/lib/stream/stream_cipher.cpp
@@ -0,0 +1,149 @@
+/*
+* Stream Ciphers
+* (C) 2015,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/stream_cipher.h>
+#include <botan/scan_name.h>
+#include <botan/exceptn.h>
+
+#if defined(BOTAN_HAS_CHACHA)
+ #include <botan/chacha.h>
+#endif
+
+#if defined(BOTAN_HAS_SALSA20)
+ #include <botan/salsa20.h>
+#endif
+
+#if defined(BOTAN_HAS_SHAKE_CIPHER)
+ #include <botan/shake_cipher.h>
+#endif
+
+#if defined(BOTAN_HAS_CTR_BE)
+ #include <botan/ctr.h>
+#endif
+
+#if defined(BOTAN_HAS_OFB)
+ #include <botan/ofb.h>
+#endif
+
+#if defined(BOTAN_HAS_RC4)
+ #include <botan/rc4.h>
+#endif
+
+#if defined(BOTAN_HAS_OPENSSL)
+ #include <botan/internal/openssl.h>
+#endif
+
+namespace Botan {
+
+std::unique_ptr<StreamCipher> StreamCipher::create(const std::string& algo_spec,
+ const std::string& provider)
+ {
+ const SCAN_Name req(algo_spec);
+
+#if defined(BOTAN_HAS_CTR_BE)
+ if((req.algo_name() == "CTR-BE" || req.algo_name() == "CTR") && req.arg_count_between(1,2))
+ {
+ 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());
+ return std::unique_ptr<StreamCipher>(new CTR_BE(cipher.release(), ctr_size));
+ }
+ }
+ }
+#endif
+
+#if defined(BOTAN_HAS_CHACHA)
+ if(req.algo_name() == "ChaCha")
+ {
+ if(provider.empty() || provider == "base")
+ return std::unique_ptr<StreamCipher>(new ChaCha(req.arg_as_integer(0, 20)));
+ }
+
+ if(req.algo_name() == "ChaCha20")
+ {
+ if(provider.empty() || provider == "base")
+ return std::unique_ptr<StreamCipher>(new ChaCha(20));
+ }
+#endif
+
+#if defined(BOTAN_HAS_SALSA20)
+ if(req.algo_name() == "Salsa20")
+ {
+ if(provider.empty() || provider == "base")
+ return std::unique_ptr<StreamCipher>(new Salsa20);
+ }
+#endif
+
+#if defined(BOTAN_HAS_SHAKE_CIPHER)
+ if(req.algo_name() == "SHAKE-128" || req.algo_name() == "SHAKE-128-XOF")
+ {
+ if(provider.empty() || provider == "base")
+ return std::unique_ptr<StreamCipher>(new SHAKE_128_Cipher);
+ }
+#endif
+
+#if defined(BOTAN_HAS_OFB)
+ if(req.algo_name() == "OFB" && req.arg_count() == 1)
+ {
+ if(provider.empty() || provider == "base")
+ {
+ if(auto c = BlockCipher::create(req.arg(0)))
+ return std::unique_ptr<StreamCipher>(new OFB(c.release()));
+ }
+ }
+#endif
+
+#if defined(BOTAN_HAS_RC4)
+
+ if(req.algo_name() == "RC4" ||
+ req.algo_name() == "ARC4" ||
+ req.algo_name() == "MARK-4")
+ {
+ const size_t skip = (req.algo_name() == "MARK-4") ? 256 : req.arg_as_integer(0, 0);
+
+#if defined(BOTAN_HAS_OPENSSL)
+ if(provider.empty() || provider == "openssl")
+ {
+ return std::unique_ptr<StreamCipher>(make_openssl_rc4(skip));
+ }
+#endif
+
+ if(provider.empty() || provider == "base")
+ {
+ return std::unique_ptr<StreamCipher>(new RC4(skip));
+ }
+ }
+
+#endif
+
+ BOTAN_UNUSED(req);
+ BOTAN_UNUSED(provider);
+
+ return nullptr;
+ }
+
+//static
+std::unique_ptr<StreamCipher>
+StreamCipher::create_or_throw(const std::string& algo,
+ const std::string& provider)
+ {
+ if(auto sc = StreamCipher::create(algo, provider))
+ {
+ return sc;
+ }
+ throw Lookup_Error("Stream cipher", algo, provider);
+ }
+
+std::vector<std::string> StreamCipher::providers(const std::string& algo_spec)
+ {
+ return probe_providers_of<StreamCipher>(algo_spec, {"base", "openssl"});
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/stream/stream_cipher.h b/comm/third_party/botan/src/lib/stream/stream_cipher.h
new file mode 100644
index 0000000000..a07f210413
--- /dev/null
+++ b/comm/third_party/botan/src/lib/stream/stream_cipher.h
@@ -0,0 +1,147 @@
+/*
+* Stream Cipher
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_STREAM_CIPHER_H_
+#define BOTAN_STREAM_CIPHER_H_
+
+#include <botan/sym_algo.h>
+#include <string>
+#include <memory>
+#include <vector>
+
+namespace Botan {
+
+/**
+* Base class for all stream ciphers
+*/
+class BOTAN_PUBLIC_API(2,0) StreamCipher : public SymmetricAlgorithm
+ {
+ public:
+ virtual ~StreamCipher() = default;
+
+ /**
+ * Create an instance based on a name
+ * If provider is empty then best available is chosen.
+ * @param algo_spec algorithm name
+ * @param provider provider implementation to use
+ * @return a null pointer if the algo/provider combination cannot be found
+ */
+ static std::unique_ptr<StreamCipher>
+ create(const std::string& algo_spec,
+ const std::string& provider = "");
+
+ /**
+ * Create an instance based on a name
+ * If provider is empty then best available is chosen.
+ * @param algo_spec algorithm name
+ * @param provider provider implementation to use
+ * Throws a Lookup_Error if the algo/provider combination cannot be found
+ */
+ static std::unique_ptr<StreamCipher>
+ create_or_throw(const std::string& algo_spec,
+ const std::string& provider = "");
+
+ /**
+ * @return list of available providers for this algorithm, empty if not available
+ */
+ static std::vector<std::string> providers(const std::string& algo_spec);
+
+ /**
+ * Encrypt or decrypt a message
+ * @param in the plaintext
+ * @param out the byte array to hold the output, i.e. the ciphertext
+ * @param len the length of both in and out in bytes
+ */
+ virtual void cipher(const uint8_t in[], uint8_t out[], size_t len) = 0;
+
+ /**
+ * Write keystream bytes to a buffer
+ * @param out the byte array to hold the keystream
+ * @param len the length of out in bytes
+ */
+ virtual void write_keystream(uint8_t out[], size_t len)
+ {
+ clear_mem(out, len);
+ cipher1(out, len);
+ }
+
+ /**
+ * Encrypt or decrypt a message
+ * The message is encrypted/decrypted in place.
+ * @param buf the plaintext / ciphertext
+ * @param len the length of buf in bytes
+ */
+ void cipher1(uint8_t buf[], size_t len)
+ { cipher(buf, buf, len); }
+
+ /**
+ * Encrypt a message
+ * The message is encrypted/decrypted in place.
+ * @param inout the plaintext / ciphertext
+ */
+ template<typename Alloc>
+ void encipher(std::vector<uint8_t, Alloc>& inout)
+ { cipher(inout.data(), inout.data(), inout.size()); }
+
+ /**
+ * Encrypt a message
+ * The message is encrypted in place.
+ * @param inout the plaintext / ciphertext
+ */
+ template<typename Alloc>
+ void encrypt(std::vector<uint8_t, Alloc>& inout)
+ { cipher(inout.data(), inout.data(), inout.size()); }
+
+ /**
+ * Decrypt a message in place
+ * The message is decrypted in place.
+ * @param inout the plaintext / ciphertext
+ */
+ template<typename Alloc>
+ void decrypt(std::vector<uint8_t, Alloc>& inout)
+ { cipher(inout.data(), inout.data(), inout.size()); }
+
+ /**
+ * Resync the cipher using the IV
+ * @param iv the initialization vector
+ * @param iv_len the length of the IV in bytes
+ */
+ virtual void set_iv(const uint8_t iv[], size_t iv_len) = 0;
+
+ /**
+ * Return the default (preferred) nonce length
+ * If this function returns 0, then this cipher does not support nonces
+ */
+ virtual size_t default_iv_length() const { return 0; }
+
+ /**
+ * @param iv_len the length of the IV in bytes
+ * @return if the length is valid for this algorithm
+ */
+ virtual bool valid_iv_length(size_t iv_len) const { return (iv_len == 0); }
+
+ /**
+ * @return a new object representing the same algorithm as *this
+ */
+ virtual StreamCipher* clone() const = 0;
+
+ /**
+ * Set the offset and the state used later to generate the keystream
+ * @param offset the offset where we begin to generate the keystream
+ */
+ virtual void seek(uint64_t offset) = 0;
+
+ /**
+ * @return provider information about this implementation. Default is "base",
+ * might also return "sse2", "avx2", "openssl", or some other arbitrary string.
+ */
+ virtual std::string provider() const { return "base"; }
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/tls/asio/asio_async_ops.h b/comm/third_party/botan/src/lib/tls/asio/asio_async_ops.h
new file mode 100644
index 0000000000..47267a569d
--- /dev/null
+++ b/comm/third_party/botan/src/lib/tls/asio/asio_async_ops.h
@@ -0,0 +1,355 @@
+/*
+* Helpers for TLS ASIO Stream
+* (C) 2018-2020 Jack Lloyd
+* 2018-2020 Hannes Rantzsch, Tim Oesterreich, Rene Meusel
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_ASIO_ASYNC_OPS_H_
+#define BOTAN_ASIO_ASYNC_OPS_H_
+
+#include <botan/build.h>
+
+#include <boost/version.hpp>
+#if BOOST_VERSION >= 106600
+
+#include <botan/asio_error.h>
+
+// We need to define BOOST_ASIO_DISABLE_SERIAL_PORT before any asio imports. Otherwise asio will include <termios.h>,
+// which interferes with Botan's amalgamation by defining macros like 'B0' and 'FF1'.
+#define BOOST_ASIO_DISABLE_SERIAL_PORT
+#include <boost/asio.hpp>
+#include <boost/asio/yield.hpp>
+
+namespace Botan {
+namespace TLS {
+namespace detail {
+
+/**
+ * Base class for asynchronous stream operations.
+ *
+ * Asynchronous operations, used for example to implement an interface for boost::asio::async_read_some and
+ * boost::asio::async_write_some, are based on boost::asio::coroutines.
+ * Derived operations should implement a call operator and invoke it with the correct parameters upon construction. The
+ * call operator needs to make sure that the user-provided handler is not called directly. Typically, yield / reenter is
+ * used for this in the following fashion:
+ *
+ * ```
+ * void operator()(boost::system::error_code ec, std::size_t bytes_transferred, bool isContinuation = true)
+ * {
+ * reenter(this)
+ * {
+ * // operation specific logic, repeatedly interacting with the stream_core and the next_layer (socket)
+ *
+ * // make sure intermediate initiating function is called
+ * if(!isContinuation)
+ * {
+ * yield next_layer.async_operation(empty_buffer, this);
+ * }
+ *
+ * // call the completion handler
+ * complete_now(error_code, bytes_transferred);
+ * }
+ * }
+ * ```
+ *
+ * Once the operation is completed and ready to call the completion handler it checks if an intermediate initiating
+ * function has been called using the `isContinuation` parameter. If not, it will call an asynchronous operation, such
+ * as `async_read_some`, with and empty buffer, set the object itself as the handler, and `yield`. As a result, the call
+ * operator will be invoked again, this time as a continuation, and will jump to the location where it yielded before
+ * using `reenter`. It is now safe to call the handler function via `complete_now`.
+ *
+ * \tparam Handler Type of the completion handler
+ * \tparam Executor1 Type of the asio executor (usually derived from the lower layer)
+ * \tparam Allocator Type of the allocator to be used
+ */
+template <class Handler, class Executor1, class Allocator>
+class AsyncBase : public boost::asio::coroutine
+ {
+ public:
+ using allocator_type = boost::asio::associated_allocator_t<Handler, Allocator>;
+ using executor_type = boost::asio::associated_executor_t<Handler, Executor1>;
+
+ allocator_type get_allocator() const noexcept
+ {
+ return boost::asio::get_associated_allocator(m_handler);
+ }
+
+ executor_type get_executor() const noexcept
+ {
+ return boost::asio::get_associated_executor(m_handler, m_work_guard_1.get_executor());
+ }
+
+ protected:
+ template <class HandlerT>
+ AsyncBase(HandlerT&& handler, const Executor1& executor)
+ : m_handler(std::forward<HandlerT>(handler))
+ , m_work_guard_1(executor)
+ {
+ }
+
+ /**
+ * Call the completion handler.
+ *
+ * This function should only be called after an intermediate initiating function has been called.
+ *
+ * @param args Arguments forwarded to the completion handler function.
+ */
+ template<class... Args>
+ void complete_now(Args&& ... args)
+ {
+ m_work_guard_1.reset();
+ m_handler(std::forward<Args>(args)...);
+ }
+
+ Handler m_handler;
+ boost::asio::executor_work_guard<Executor1> m_work_guard_1;
+ };
+
+template <class Handler, class Stream, class MutableBufferSequence, class Allocator = std::allocator<void>>
+class AsyncReadOperation : public AsyncBase<Handler, typename Stream::executor_type, Allocator>
+ {
+ public:
+ /**
+ * Construct and invoke an AsyncReadOperation.
+ *
+ * @param handler Handler function to be called upon completion.
+ * @param stream The stream from which the data will be read
+ * @param buffers The buffers into which the data will be read.
+ * @param ec Optional error code; used to report an error to the handler function.
+ */
+ template <class HandlerT>
+ AsyncReadOperation(HandlerT&& handler,
+ Stream& stream,
+ const MutableBufferSequence& buffers,
+ const boost::system::error_code& ec = {})
+ : AsyncBase<Handler, typename Stream::executor_type, Allocator>(
+ std::forward<HandlerT>(handler),
+ stream.get_executor())
+ , m_stream(stream)
+ , m_buffers(buffers)
+ , m_decodedBytes(0)
+ {
+ this->operator()(ec, std::size_t(0), false);
+ }
+
+ AsyncReadOperation(AsyncReadOperation&&) = default;
+
+ void operator()(boost::system::error_code ec, std::size_t bytes_transferred, bool isContinuation = true)
+ {
+ reenter(this)
+ {
+ if(bytes_transferred > 0 && !ec)
+ {
+ // We have received encrypted data from the network, now hand it to TLS::Channel for decryption.
+ boost::asio::const_buffer read_buffer{m_stream.input_buffer().data(), bytes_transferred};
+ m_stream.process_encrypted_data(read_buffer, ec);
+ }
+
+ if (m_stream.shutdown_received())
+ {
+ // we just received a 'close_notify' from the peer and don't expect any more data
+ ec = boost::asio::error::eof;
+ }
+ else if (ec == boost::asio::error::eof)
+ {
+ // we did not expect this disconnection from the peer
+ ec = StreamError::StreamTruncated;
+ }
+
+ if(!m_stream.has_received_data() && !ec && boost::asio::buffer_size(m_buffers) > 0)
+ {
+ // The channel did not decrypt a complete record yet, we need more data from the socket.
+ m_stream.next_layer().async_read_some(m_stream.input_buffer(), std::move(*this));
+ return;
+ }
+
+ if(m_stream.has_received_data() && !ec)
+ {
+ // The channel has decrypted a TLS record, now copy it to the output buffers.
+ m_decodedBytes = m_stream.copy_received_data(m_buffers);
+ }
+
+ if(!isContinuation)
+ {
+ // Make sure the handler is not called without an intermediate initiating function.
+ // "Reading" into a zero-byte buffer will complete immediately.
+ m_ec = ec;
+ yield m_stream.next_layer().async_read_some(boost::asio::mutable_buffer(), std::move(*this));
+ ec = m_ec;
+ }
+
+ this->complete_now(ec, m_decodedBytes);
+ }
+ }
+
+ private:
+ Stream& m_stream;
+ MutableBufferSequence m_buffers;
+ std::size_t m_decodedBytes;
+ boost::system::error_code m_ec;
+ };
+
+template <typename Handler, class Stream, class Allocator = std::allocator<void>>
+class AsyncWriteOperation : public AsyncBase<Handler, typename Stream::executor_type, Allocator>
+ {
+ public:
+ /**
+ * Construct and invoke an AsyncWriteOperation.
+ *
+ * @param handler Handler function to be called upon completion.
+ * @param stream The stream from which the data will be read
+ * @param plainBytesTransferred Number of bytes to be reported to the user-provided handler function as
+ * bytes_transferred. This needs to be provided since the amount of plaintext data
+ * consumed from the input buffer can differ from the amount of encrypted data written
+ * to the next layer.
+ * @param ec Optional error code; used to report an error to the handler function.
+ */
+ template <class HandlerT>
+ AsyncWriteOperation(HandlerT&& handler,
+ Stream& stream,
+ std::size_t plainBytesTransferred,
+ const boost::system::error_code& ec = {})
+ : AsyncBase<Handler, typename Stream::executor_type, Allocator>(
+ std::forward<HandlerT>(handler),
+ stream.get_executor())
+ , m_stream(stream)
+ , m_plainBytesTransferred(plainBytesTransferred)
+ {
+ this->operator()(ec, std::size_t(0), false);
+ }
+
+ AsyncWriteOperation(AsyncWriteOperation&&) = default;
+
+ void operator()(boost::system::error_code ec, std::size_t bytes_transferred, bool isContinuation = true)
+ {
+ reenter(this)
+ {
+ // mark the number of encrypted bytes sent to the network as "consumed"
+ // Note: bytes_transferred will be zero on first call
+ m_stream.consume_send_buffer(bytes_transferred);
+
+ if(m_stream.has_data_to_send() && !ec)
+ {
+ m_stream.next_layer().async_write_some(m_stream.send_buffer(), std::move(*this));
+ return;
+ }
+
+ if (ec == boost::asio::error::eof && !m_stream.shutdown_received())
+ {
+ // transport layer was closed by peer without receiving 'close_notify'
+ ec = StreamError::StreamTruncated;
+ }
+
+ if(!isContinuation)
+ {
+ // Make sure the handler is not called without an intermediate initiating function.
+ // "Writing" to a zero-byte buffer will complete immediately.
+ m_ec = ec;
+ yield m_stream.next_layer().async_write_some(boost::asio::const_buffer(), std::move(*this));
+ ec = m_ec;
+ }
+
+ // The size of the sent TLS record can differ from the size of the payload due to TLS encryption. We need to
+ // tell the handler how many bytes of the original data we already processed.
+ this->complete_now(ec, m_plainBytesTransferred);
+ }
+ }
+
+ private:
+ Stream& m_stream;
+ std::size_t m_plainBytesTransferred;
+ boost::system::error_code m_ec;
+ };
+
+template <class Handler, class Stream, class Allocator = std::allocator<void>>
+class AsyncHandshakeOperation : public AsyncBase<Handler, typename Stream::executor_type, Allocator>
+ {
+ public:
+ /**
+ * Construct and invoke an AsyncHandshakeOperation.
+ *
+ * @param handler Handler function to be called upon completion.
+ * @param stream The stream from which the data will be read
+ * @param ec Optional error code; used to report an error to the handler function.
+ */
+ template<class HandlerT>
+ AsyncHandshakeOperation(
+ HandlerT&& handler,
+ Stream& stream,
+ const boost::system::error_code& ec = {})
+ : AsyncBase<Handler, typename Stream::executor_type, Allocator>(
+ std::forward<HandlerT>(handler),
+ stream.get_executor())
+ , m_stream(stream)
+ {
+ this->operator()(ec, std::size_t(0), false);
+ }
+
+ AsyncHandshakeOperation(AsyncHandshakeOperation&&) = default;
+
+ void operator()(boost::system::error_code ec, std::size_t bytesTransferred, bool isContinuation = true)
+ {
+ reenter(this)
+ {
+ if(ec == boost::asio::error::eof)
+ {
+ ec = StreamError::StreamTruncated;
+ }
+
+ if(bytesTransferred > 0 && !ec)
+ {
+ // Provide encrypted TLS data received from the network to TLS::Channel for decryption
+ boost::asio::const_buffer read_buffer {m_stream.input_buffer().data(), bytesTransferred};
+ m_stream.process_encrypted_data(read_buffer, ec);
+ }
+
+ if(m_stream.has_data_to_send() && !ec)
+ {
+ // Write encrypted TLS data provided by the TLS::Channel on the wire
+
+ // Note: we construct `AsyncWriteOperation` with 0 as its last parameter (`plainBytesTransferred`). This
+ // 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<AsyncHandshakeOperation<typename std::decay<Handler>::type, Stream, Allocator>,
+ Stream,
+ Allocator>
+ op{std::move(*this), m_stream, 0};
+ return;
+ }
+
+ if(!m_stream.native_handle()->is_active() && !ec)
+ {
+ // Read more encrypted TLS data from the network
+ m_stream.next_layer().async_read_some(m_stream.input_buffer(), std::move(*this));
+ return;
+ }
+
+ if(!isContinuation)
+ {
+ // Make sure the handler is not called without an intermediate initiating function.
+ // "Reading" into a zero-byte buffer will complete immediately.
+ m_ec = ec;
+ yield m_stream.next_layer().async_read_some(boost::asio::mutable_buffer(), std::move(*this));
+ ec = m_ec;
+ }
+
+ this->complete_now(ec);
+ }
+ }
+
+ private:
+ Stream& m_stream;
+ boost::system::error_code m_ec;
+ };
+
+} // namespace detail
+} // namespace TLS
+} // namespace Botan
+
+#include <boost/asio/unyield.hpp>
+
+#endif // BOOST_VERSION
+#endif // BOTAN_ASIO_ASYNC_OPS_H_
diff --git a/comm/third_party/botan/src/lib/tls/asio/asio_context.h b/comm/third_party/botan/src/lib/tls/asio/asio_context.h
new file mode 100644
index 0000000000..e225fde6a2
--- /dev/null
+++ b/comm/third_party/botan/src/lib/tls/asio/asio_context.h
@@ -0,0 +1,120 @@
+/*
+ * TLS Context
+ * (C) 2018-2020 Jack Lloyd
+ * 2018-2020 Hannes Rantzsch, Tim Oesterreich, Rene Meusel
+ *
+ * Botan is released under the Simplified BSD License (see license.txt)
+ */
+
+#ifndef BOTAN_ASIO_TLS_CONTEXT_H_
+#define BOTAN_ASIO_TLS_CONTEXT_H_
+
+#include <botan/build.h>
+
+#include <boost/version.hpp>
+#if BOOST_VERSION >= 106600
+
+#include <functional>
+
+#include <botan/credentials_manager.h>
+#include <botan/ocsp.h>
+#include <botan/rng.h>
+#include <botan/tls_callbacks.h>
+#include <botan/tls_policy.h>
+#include <botan/tls_server_info.h>
+#include <botan/tls_session_manager.h>
+
+namespace Botan {
+namespace TLS {
+
+namespace detail {
+template <typename FunT>
+struct fn_signature_helper : public std::false_type {};
+
+template <typename R, typename D, typename... Args>
+struct fn_signature_helper<R(D::*)(Args...)>
+ {
+ using type = std::function<R(Args...)>;
+ };
+} // namespace detail
+
+/**
+ * A helper class to initialize and configure Botan::TLS::Stream
+ */
+class Context
+ {
+ public:
+ // statically extract the function signature type from Callbacks::tls_verify_cert_chain
+ // and reuse it as an std::function<> for the verify callback signature
+ /**
+ * The signature of the callback function should correspond to the signature of
+ * Callbacks::tls_verify_cert_chain
+ */
+ using Verify_Callback =
+ detail::fn_signature_helper<decltype(&Callbacks::tls_verify_cert_chain)>::type;
+
+ Context(Credentials_Manager& credentials_manager,
+ RandomNumberGenerator& rng,
+ Session_Manager& session_manager,
+ Policy& policy,
+ Server_Information server_info = Server_Information()) :
+ m_credentials_manager(credentials_manager),
+ m_rng(rng),
+ m_session_manager(session_manager),
+ m_policy(policy),
+ m_server_info(server_info)
+ {}
+
+ virtual ~Context() = default;
+
+ Context(Context&&) = default;
+ Context(const Context&) = delete;
+ Context& operator=(const Context&) = delete;
+ Context& operator=(Context&&) = delete;
+
+ /**
+ * @brief Override the tls_verify_cert_chain callback
+ *
+ * This changes the verify_callback in the stream's TLS::Context, and hence the tls_verify_cert_chain callback
+ * used in the handshake.
+ * Using this function is equivalent to setting the callback via @see Botan::TLS::Stream::set_verify_callback
+ *
+ * @note This function should only be called before initiating the TLS handshake
+ */
+ void set_verify_callback(Verify_Callback callback)
+ {
+ m_verify_callback = std::move(callback);
+ }
+
+ bool has_verify_callback() const
+ {
+ return static_cast<bool>(m_verify_callback);
+ }
+
+ const Verify_Callback& get_verify_callback() const
+ {
+ return m_verify_callback;
+ }
+
+ void set_server_info(const Server_Information& server_info)
+ {
+ m_server_info = server_info;
+ }
+
+ protected:
+ template <class S, class C> friend class Stream;
+
+ Credentials_Manager& m_credentials_manager;
+ RandomNumberGenerator& m_rng;
+ Session_Manager& m_session_manager;
+ Policy& m_policy;
+
+ Server_Information m_server_info;
+ Verify_Callback m_verify_callback;
+ };
+
+} // namespace TLS
+} // namespace Botan
+
+#endif // BOOST_VERSION
+#endif // BOTAN_ASIO_TLS_CONTEXT_H_
diff --git a/comm/third_party/botan/src/lib/tls/asio/asio_error.h b/comm/third_party/botan/src/lib/tls/asio/asio_error.h
new file mode 100644
index 0000000000..bdaf8473f9
--- /dev/null
+++ b/comm/third_party/botan/src/lib/tls/asio/asio_error.h
@@ -0,0 +1,151 @@
+/*
+* TLS Stream Errors
+* (C) 2018-2020 Jack Lloyd
+* 2018-2020 Hannes Rantzsch, Tim Oesterreich, Rene Meusel
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_ASIO_ERROR_H_
+#define BOTAN_ASIO_ERROR_H_
+
+#include <botan/build.h>
+
+#include <boost/version.hpp>
+#if BOOST_VERSION >= 106600
+
+#include <boost/system/system_error.hpp>
+
+#include <botan/exceptn.h>
+#include <botan/tls_alert.h>
+#include <botan/tls_exceptn.h>
+
+/*
+ * This file defines Botan-specific subclasses of boost::system::error_category.
+ * In addition to the class definition, each category class is accompanied by function `make_error_code` used to create
+ * a `boost::system::error_code` of the category from some other kind of error in Botan (for example, a TLS alert).
+ * Since error_category instances should be singletons, there's also a method to get/create the instance for each class.
+ */
+
+namespace Botan {
+namespace TLS {
+
+enum StreamError
+ {
+ StreamTruncated = 1
+ };
+
+//! @brief An error category for errors from the TLS::Stream
+struct StreamCategory : public boost::system::error_category
+ {
+ public:
+ const char* name() const noexcept override
+ {
+ return "Botan TLS Stream";
+ }
+
+ std::string message(int value) const override
+ {
+ switch(value)
+ {
+ case StreamTruncated:
+ return "stream truncated";
+ default:
+ return "generic error";
+ }
+ }
+ };
+
+inline const StreamCategory& botan_stream_category()
+ {
+ static StreamCategory category;
+ return category;
+ }
+
+inline boost::system::error_code make_error_code(Botan::TLS::StreamError e)
+ {
+ return boost::system::error_code(static_cast<int>(e), Botan::TLS::botan_stream_category());
+ }
+
+//! @brief An error category for TLS alerts
+struct BotanAlertCategory : boost::system::error_category
+ {
+ const char* name() const noexcept override
+ {
+ return "Botan TLS Alert";
+ }
+
+ std::string message(int ev) const override
+ {
+ Botan::TLS::Alert alert(static_cast<Botan::TLS::Alert::Type>(ev));
+ return alert.type_string();
+ }
+ };
+
+inline const BotanAlertCategory& botan_alert_category() noexcept
+ {
+ static BotanAlertCategory category;
+ return category;
+ }
+
+inline boost::system::error_code make_error_code(Botan::TLS::Alert::Type c)
+ {
+ return boost::system::error_code(static_cast<int>(c), Botan::TLS::botan_alert_category());
+ }
+
+} // namespace TLS
+
+//! @brief An error category for errors from Botan (other than TLS alerts)
+struct BotanErrorCategory : boost::system::error_category
+ {
+ const char* name() const noexcept override
+ {
+ return "Botan";
+ }
+
+ std::string message(int ev) const override
+ {
+ return Botan::to_string(static_cast<Botan::ErrorType>(ev));
+ }
+ };
+
+inline const BotanErrorCategory& botan_category() noexcept
+ {
+ static BotanErrorCategory category;
+ return category;
+ }
+
+inline boost::system::error_code make_error_code(Botan::ErrorType e)
+ {
+ return boost::system::error_code(static_cast<int>(e), Botan::botan_category());
+ }
+
+} // namespace Botan
+
+ /*
+ * Add a template specialization of `is_error_code_enum` for each kind of error to allow automatic conversion to an
+ * error code.
+ */
+namespace boost {
+namespace system {
+
+template<> struct is_error_code_enum<Botan::TLS::Alert::Type>
+ {
+ static const bool value = true;
+ };
+
+template<> struct is_error_code_enum<Botan::TLS::StreamError>
+ {
+ static const bool value = true;
+ };
+
+template<> struct is_error_code_enum<Botan::ErrorType>
+ {
+ static const bool value = true;
+ };
+
+} // namespace system
+} // namespace boost
+
+#endif // BOOST_VERSION
+#endif // BOTAN_ASIO_ERROR_H_
diff --git a/comm/third_party/botan/src/lib/tls/asio/asio_stream.h b/comm/third_party/botan/src/lib/tls/asio/asio_stream.h
new file mode 100644
index 0000000000..bef95e44a0
--- /dev/null
+++ b/comm/third_party/botan/src/lib/tls/asio/asio_stream.h
@@ -0,0 +1,835 @@
+/*
+* TLS ASIO Stream
+* (C) 2018-2020 Jack Lloyd
+* 2018-2020 Hannes Rantzsch, Tim Oesterreich, Rene Meusel
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_ASIO_STREAM_H_
+#define BOTAN_ASIO_STREAM_H_
+
+#include <botan/build.h>
+
+// first version to be compatible with Networking TS (N4656) and boost::beast
+#include <boost/version.hpp>
+#if BOOST_VERSION >= 106600
+
+#include <botan/asio_async_ops.h>
+#include <botan/asio_context.h>
+#include <botan/asio_error.h>
+
+#include <botan/tls_callbacks.h>
+#include <botan/tls_channel.h>
+#include <botan/tls_client.h>
+#include <botan/tls_magic.h>
+#include <botan/tls_server.h>
+
+// We need to define BOOST_ASIO_DISABLE_SERIAL_PORT before any asio imports. Otherwise asio will include <termios.h>,
+// which interferes with Botan's amalgamation by defining macros like 'B0' and 'FF1'.
+#define BOOST_ASIO_DISABLE_SERIAL_PORT
+#include <boost/asio.hpp>
+#include <boost/beast/core.hpp>
+
+#include <algorithm>
+#include <memory>
+#include <type_traits>
+
+namespace Botan {
+namespace TLS {
+
+/**
+ * @brief boost::asio compatible SSL/TLS stream
+ *
+ * @tparam StreamLayer type of the next layer, usually a network socket
+ * @tparam ChannelT type of the native_handle, defaults to Botan::TLS::Channel, only needed for testing purposes
+ */
+template <class StreamLayer, class ChannelT = Channel>
+class Stream
+ {
+ public:
+ //! \name construction
+ //! @{
+
+ /**
+ * @brief Construct a new Stream
+ *
+ * @param context The context parameter is used to set up the underlying native handle. Using code is
+ * responsible for lifetime management of the context and must ensure that it is available for the
+ * lifetime of the stream.
+ * @param args Arguments to be forwarded to the construction of the next layer.
+ */
+ template <typename... Args>
+ explicit Stream(Context& context, Args&& ... args)
+ : m_context(context)
+ , m_nextLayer(std::forward<Args>(args)...)
+ , m_core(*this)
+ , m_shutdown_received(false)
+ , m_input_buffer_space(MAX_CIPHERTEXT_SIZE, '\0')
+ , m_input_buffer(m_input_buffer_space.data(), m_input_buffer_space.size())
+ {}
+
+ /**
+ * @brief Construct a new Stream
+ *
+ * Convenience overload for boost::asio::ssl::stream compatibility.
+ *
+ * @param arg This argument is forwarded to the construction of the next layer.
+ * @param context The context parameter is used to set up the underlying native handle. Using code is
+ * responsible for lifetime management of the context and must ensure that is available for the
+ * lifetime of the stream.
+ */
+ template <typename Arg>
+ explicit Stream(Arg&& arg, Context& context)
+ : m_context(context)
+ , m_nextLayer(std::forward<Arg>(arg))
+ , m_core(*this)
+ , m_shutdown_received(false)
+ , m_input_buffer_space(MAX_CIPHERTEXT_SIZE, '\0')
+ , m_input_buffer(m_input_buffer_space.data(), m_input_buffer_space.size())
+ {}
+
+ virtual ~Stream() = default;
+
+ Stream(Stream&& other) = default;
+ Stream& operator=(Stream&& other) = default;
+
+ Stream(const Stream& other) = delete;
+ Stream& operator=(const Stream& other) = delete;
+
+ //! @}
+ //! \name boost::asio accessor methods
+ //! @{
+
+ using next_layer_type = typename std::remove_reference<StreamLayer>::type;
+
+ const next_layer_type& next_layer() const { return m_nextLayer; }
+ next_layer_type& next_layer() { return m_nextLayer; }
+
+#if BOOST_VERSION >= 107000
+ /*
+ * From Boost 1.70 onwards Beast types no longer provide public access to the member function `lowest_layer()`.
+ * Instead, the new free-standing functions in Beast need to be used.
+ * See also: https://github.com/boostorg/beast/commit/6a658b5c3a36f8d58334f8b6582c01c3e87768ae
+ */
+ using lowest_layer_type = typename boost::beast::lowest_layer_type<StreamLayer>;
+
+ lowest_layer_type& lowest_layer() { return boost::beast::get_lowest_layer(m_nextLayer); }
+ const lowest_layer_type& lowest_layer() const { return boost::beast::get_lowest_layer(m_nextLayer); }
+#else
+ using lowest_layer_type = typename next_layer_type::lowest_layer_type;
+
+ lowest_layer_type& lowest_layer() { return m_nextLayer.lowest_layer(); }
+ const lowest_layer_type& lowest_layer() const { return m_nextLayer.lowest_layer(); }
+#endif
+
+ using executor_type = typename next_layer_type::executor_type;
+ executor_type get_executor() noexcept { return m_nextLayer.get_executor(); }
+
+ using native_handle_type = typename std::add_pointer<ChannelT>::type;
+ native_handle_type native_handle()
+ {
+ if(m_native_handle == nullptr)
+ { throw Invalid_State("Invalid handshake state"); }
+ return m_native_handle.get();
+ }
+
+ //! @}
+ //! \name configuration and callback setters
+ //! @{
+
+ /**
+ * @brief Override the tls_verify_cert_chain callback
+ *
+ * This changes the verify_callback in the stream's TLS::Context, and hence the tls_verify_cert_chain callback
+ * used in the handshake.
+ * Using this function is equivalent to setting the callback via @see Botan::TLS::Context::set_verify_callback
+ *
+ * @note This function should only be called before initiating the TLS handshake
+ */
+ void set_verify_callback(Context::Verify_Callback callback)
+ {
+ m_context.set_verify_callback(std::move(callback));
+ }
+
+ /**
+ * @brief Compatibility overload of @ref set_verify_callback
+ *
+ * @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);
+ m_context.set_verify_callback(std::move(callback));
+ }
+
+ //! @throws Not_Implemented
+ void set_verify_depth(int depth)
+ {
+ BOTAN_UNUSED(depth);
+ throw Not_Implemented("set_verify_depth is not implemented");
+ }
+
+ /**
+ * Not Implemented.
+ * @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);
+ ec = Botan::ErrorType::NotImplemented;
+ }
+
+ //! @throws Not_Implemented
+ template <typename verify_mode>
+ void set_verify_mode(verify_mode v)
+ {
+ BOTAN_UNUSED(v);
+ throw Not_Implemented("set_verify_mode is not implemented");
+ }
+
+ /**
+ * Not Implemented.
+ * @param v the desired verify mode
+ * @param ec Will be set to `Botan::ErrorType::NotImplemented`
+ */
+ template <typename verify_mode>
+ void set_verify_mode(verify_mode v, boost::system::error_code& ec)
+ {
+ BOTAN_UNUSED(v);
+ ec = Botan::ErrorType::NotImplemented;
+ }
+
+ //! @}
+ //! \name handshake methods
+ //! @{
+
+ /**
+ * @brief Performs SSL handshaking.
+ *
+ * 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
+ */
+ void handshake(Connection_Side side)
+ {
+ boost::system::error_code ec;
+ handshake(side, ec);
+ boost::asio::detail::throw_error(ec, "handshake");
+ }
+
+ /**
+ * @brief Performs SSL handshaking.
+ *
+ * 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.
+ * @param ec Set to indicate what error occurred, if any.
+ */
+ void handshake(Connection_Side side, boost::system::error_code& ec)
+ {
+ setup_native_handle(side, ec);
+
+ if(side == CLIENT)
+ {
+ // send client hello, which was written to the send buffer on client instantiation
+ send_pending_encrypted_data(ec);
+ }
+
+ while(!native_handle()->is_active() && !ec)
+ {
+ boost::asio::const_buffer read_buffer{input_buffer().data(), m_nextLayer.read_some(input_buffer(), ec)};
+ if(ec)
+ { return; }
+
+ process_encrypted_data(read_buffer, ec);
+
+ send_pending_encrypted_data(ec);
+ }
+ }
+
+ /**
+ * @brief Starts an asynchronous SSL handshake.
+ *
+ * This function call always returns immediately.
+ *
+ * @param side The type of handshaking to be performed, i.e. as a client or as a server.
+ * @param handler The handler to be called when the handshake operation completes.
+ * The equivalent function signature of the handler must be: void(boost::system::error_code)
+ */
+ template <typename HandshakeHandler>
+ auto async_handshake(Connection_Side side, HandshakeHandler&& handler) ->
+ BOOST_ASIO_INITFN_RESULT_TYPE(HandshakeHandler, void(boost::system::error_code))
+ {
+ BOOST_ASIO_HANDSHAKE_HANDLER_CHECK(HandshakeHandler, handler) type_check;
+
+ boost::system::error_code ec;
+ setup_native_handle(side, ec);
+ // If ec is set by setup_native_handle, the AsyncHandshakeOperation created below will do nothing but call the
+ // handler with the error_code set appropriately - no need to early return here.
+
+ boost::asio::async_completion<HandshakeHandler, void(boost::system::error_code)> init(handler);
+
+ detail::AsyncHandshakeOperation<typename std::decay<HandshakeHandler>::type, Stream>
+ op{std::move(init.completion_handler), *this, ec};
+
+ return init.result.get();
+ }
+
+ //! @throws Not_Implemented
+ template <typename ConstBufferSequence, typename BufferedHandshakeHandler>
+ BOOST_ASIO_INITFN_RESULT_TYPE(BufferedHandshakeHandler,
+ void(boost::system::error_code, std::size_t))
+ async_handshake(Connection_Side side, const ConstBufferSequence& buffers,
+ BufferedHandshakeHandler&& handler)
+ {
+ BOTAN_UNUSED(side, buffers, handler);
+ BOOST_ASIO_HANDSHAKE_HANDLER_CHECK(BufferedHandshakeHandler, handler) type_check;
+ throw Not_Implemented("buffered async handshake is not implemented");
+ }
+
+ //! @}
+ //! \name shutdown methods
+ //! @{
+
+ /**
+ * @brief Shut down SSL on the stream.
+ *
+ * This function is used to shut down SSL on the stream. The function call will block until SSL has been shut down
+ * or an error occurs. Note that this will not close the lowest layer.
+ *
+ * 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.
+ */
+ void shutdown(boost::system::error_code& ec)
+ {
+ try_with_error_code([&]
+ {
+ native_handle()->close();
+ }, ec);
+
+ send_pending_encrypted_data(ec);
+ }
+
+ /**
+ * @brief Shut down SSL on the stream.
+ *
+ * This function is used to shut down SSL on the stream. The function call will block until SSL has been shut down
+ * or an error occurs. Note that this will not close the lowest layer.
+ *
+ * Note that this can be used in reaction of a received shutdown alert from the peer.
+ *
+ * @throws boost::system::system_error if error occured
+ */
+ void shutdown()
+ {
+ boost::system::error_code ec;
+ shutdown(ec);
+ boost::asio::detail::throw_error(ec, "shutdown");
+ }
+
+ private:
+ /**
+ * @brief Internal wrapper type to adapt the expected signature of `async_shutdown` to the completion handler
+ * signature of `AsyncWriteOperation`.
+ *
+ * This is boilerplate to ignore the `size_t` parameter that is passed to the completion handler of
+ * `AsyncWriteOperation`. Note that it needs to retain the wrapped handler's executor.
+ */
+ template <typename Handler, typename Executor>
+ struct Wrapper
+ {
+ void operator()(boost::system::error_code ec, std::size_t)
+ {
+ handler(ec);
+ }
+
+ using executor_type = boost::asio::associated_executor_t<Handler, Executor>;
+
+ executor_type get_executor() const noexcept
+ {
+ return boost::asio::get_associated_executor(handler, io_executor);
+ }
+
+ using allocator_type = boost::asio::associated_allocator_t<Handler>;
+
+ allocator_type get_allocator() const noexcept
+ {
+ return boost::asio::get_associated_allocator(handler);
+ }
+
+ Handler handler;
+ Executor io_executor;
+ };
+
+ public:
+ /**
+ * @brief Asynchronously shut down SSL on the stream.
+ *
+ * This function call always returns immediately.
+ *
+ * Note that this can be used in reaction of a received shutdown alert from the peer.
+ *
+ * @param handler The handler to be called when the shutdown operation completes.
+ * The equivalent function signature of the handler must be: void(boost::system::error_code)
+ */
+ template <typename ShutdownHandler>
+ void async_shutdown(ShutdownHandler&& handler)
+ {
+ boost::system::error_code ec;
+ try_with_error_code([&]
+ {
+ native_handle()->close();
+ }, ec);
+ // If ec is set by native_handle->close(), the AsyncWriteOperation created below will do nothing but call the
+ // handler with the error_code set appropriately - no need to early return here.
+
+ using ShutdownHandlerWrapper = Wrapper<ShutdownHandler, typename Stream::executor_type>;
+
+ ShutdownHandlerWrapper w{std::forward<ShutdownHandler>(handler), get_executor()};
+ BOOST_ASIO_SHUTDOWN_HANDLER_CHECK(ShutdownHandler, w) type_check;
+
+ boost::asio::async_completion<ShutdownHandlerWrapper, void(boost::system::error_code, std::size_t)>
+ init(w);
+
+ detail::AsyncWriteOperation<typename std::decay<ShutdownHandlerWrapper>::type, Stream>
+ op{std::move(init.completion_handler), *this, boost::asio::buffer_size(send_buffer())};
+
+ return init.result.get();
+ }
+
+ //! @}
+ //! \name I/O methods
+ //! @{
+
+ /**
+ * @brief Read some data from the stream.
+ *
+ * The function call will block until one or more bytes of data has been read successfully, or until an error
+ * occurs.
+ *
+ * @param buffers The buffers into which the data will be read.
+ * @param ec Set to indicate what error occurred, if any. Specifically, StreamTruncated will be set if the peer
+ * has closed the connection but did not properly shut down the SSL connection.
+ * @return The number of bytes read. Returns 0 if an error occurred.
+ */
+ template <typename MutableBufferSequence>
+ std::size_t read_some(const MutableBufferSequence& buffers,
+ boost::system::error_code& ec)
+ {
+ if(has_received_data())
+ { return copy_received_data(buffers); }
+
+ boost::asio::const_buffer read_buffer{input_buffer().data(), m_nextLayer.read_some(input_buffer(), ec)};
+ if(ec)
+ { return 0; }
+
+ process_encrypted_data(read_buffer, ec);
+
+ if(ec) // something went wrong in process_encrypted_data()
+ { return 0; }
+
+ if(shutdown_received())
+ {
+ // we just received a 'close_notify' from the peer and don't expect any more data
+ ec = boost::asio::error::eof;
+ }
+ else if(ec == boost::asio::error::eof)
+ {
+ // we did not expect this disconnection from the peer
+ ec = StreamError::StreamTruncated;
+ }
+
+ return !ec ? copy_received_data(buffers) : 0;
+ }
+
+ /**
+ * @brief Read some data from the stream.
+ *
+ * The function call will block until one or more bytes of data has been read successfully, or until an error
+ * occurs.
+ *
+ * @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
+ */
+ template <typename MutableBufferSequence>
+ std::size_t read_some(const MutableBufferSequence& buffers)
+ {
+ boost::system::error_code ec;
+ auto const n = read_some(buffers, ec);
+ boost::asio::detail::throw_error(ec, "read_some");
+ return n;
+ }
+
+ /**
+ * @brief Write some data to the stream.
+ *
+ * The function call will block until one or more bytes of data has been written successfully, or until an error
+ * occurs.
+ *
+ * @param buffers The data to be written.
+ * @param ec Set to indicate what error occurred, if any.
+ * @return The number of bytes processed from the input buffers.
+ */
+ template <typename ConstBufferSequence>
+ std::size_t write_some(const ConstBufferSequence& buffers,
+ boost::system::error_code& ec)
+ {
+ tls_encrypt(buffers, ec);
+ send_pending_encrypted_data(ec);
+ return !ec ? boost::asio::buffer_size(buffers) : 0;
+ }
+
+ /**
+ * @brief Write some data to the stream.
+ *
+ * The function call will block until one or more bytes of data has been written successfully, or until an error
+ * occurs.
+ *
+ * @param buffers The data to be written.
+ * @return The number of bytes written.
+ * @throws boost::system::system_error if error occured
+ */
+ template <typename ConstBufferSequence>
+ std::size_t write_some(const ConstBufferSequence& buffers)
+ {
+ boost::system::error_code ec;
+ auto const n = write_some(buffers, ec);
+ boost::asio::detail::throw_error(ec, "write_some");
+ return n;
+ }
+
+ /**
+ * @brief Start an asynchronous write. The function call always returns immediately.
+ *
+ * @param buffers The data to be written.
+ * @param handler The handler to be called when the write operation completes. Copies will be made of the handler
+ * as required. The equivalent function signature of the handler must be:
+ * void(boost::system::error_code, std::size_t)
+ */
+ template <typename ConstBufferSequence, typename WriteHandler>
+ auto async_write_some(const ConstBufferSequence& buffers, WriteHandler&& handler) ->
+ BOOST_ASIO_INITFN_RESULT_TYPE(WriteHandler,
+ void(boost::system::error_code, std::size_t))
+ {
+ BOOST_ASIO_WRITE_HANDLER_CHECK(WriteHandler, handler) type_check;
+
+ boost::asio::async_completion<WriteHandler, void(boost::system::error_code, std::size_t)> init(handler);
+
+ boost::system::error_code ec;
+ tls_encrypt(buffers, ec);
+ if(ec)
+ {
+ // we cannot be sure how many bytes were committed here so clear the send_buffer and let the
+ // AsyncWriteOperation call the handler with the error_code set
+ consume_send_buffer(m_send_buffer.size());
+ detail::AsyncWriteOperation<typename std::decay<WriteHandler>::type, Stream>
+ op{std::move(init.completion_handler), *this, std::size_t(0), ec};
+ return init.result.get();
+ }
+
+ detail::AsyncWriteOperation<typename std::decay<WriteHandler>::type, Stream>
+ op{std::move(init.completion_handler), *this, boost::asio::buffer_size(buffers)};
+
+ return init.result.get();
+ }
+
+ /**
+ * @brief Start an asynchronous read. The function call always returns immediately.
+ *
+ * @param buffers The buffers into which the data will be read. Although the buffers object may be copied as
+ * necessary, ownership of the underlying buffers is retained by the caller, which must guarantee
+ * that they remain valid until the handler is called.
+ * @param handler The handler to be called when the read operation completes. The equivalent function signature of
+ * the handler must be:
+ * void(boost::system::error_code, std::size_t)
+ */
+ template <typename MutableBufferSequence, typename ReadHandler>
+ auto async_read_some(const MutableBufferSequence& buffers, ReadHandler&& handler) ->
+ BOOST_ASIO_INITFN_RESULT_TYPE(ReadHandler,
+ void(boost::system::error_code, std::size_t))
+ {
+ BOOST_ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check;
+
+ boost::asio::async_completion<ReadHandler, void(boost::system::error_code, std::size_t)> init(handler);
+
+ detail::AsyncReadOperation<typename std::decay<ReadHandler>::type, Stream, MutableBufferSequence>
+ op{std::move(init.completion_handler), *this, buffers};
+ return init.result.get();
+ }
+
+ //! @}
+
+ //! @brief Indicates whether a close_notify alert has been received from the peer.
+ bool shutdown_received() const
+ {
+ return m_shutdown_received;
+ }
+
+ protected:
+ template <class H, class S, class M, class A> friend class detail::AsyncReadOperation;
+ template <class H, class S, class A> friend class detail::AsyncWriteOperation;
+ template <class H, class S, class A> friend class detail::AsyncHandshakeOperation;
+
+ /**
+ * @brief Helper class that implements Botan::TLS::Callbacks
+ *
+ * This class is provided to the stream's native_handle (Botan::TLS::Channel) and implements the callback
+ * functions triggered by the native_handle.
+ *
+ * @param receive_buffer reference to the buffer where decrypted data should be placed
+ * @param send_buffer reference to the buffer where encrypted data should be placed
+ */
+ class StreamCore : public Botan::TLS::Callbacks
+ {
+ public:
+ StreamCore(Stream& stream) : m_stream(stream) {}
+
+ virtual ~StreamCore() = default;
+
+ void tls_emit_data(const uint8_t data[], std::size_t size) override
+ {
+ m_stream.m_send_buffer.commit(
+ boost::asio::buffer_copy(m_stream.m_send_buffer.prepare(size), boost::asio::buffer(data, size))
+ );
+ }
+
+ void tls_record_received(uint64_t, const uint8_t data[], std::size_t size) override
+ {
+ m_stream.m_receive_buffer.commit(
+ boost::asio::buffer_copy(m_stream.m_receive_buffer.prepare(size), boost::asio::const_buffer(data, size))
+ );
+ }
+
+ void tls_alert(Botan::TLS::Alert alert) override
+ {
+ if(alert.type() == Botan::TLS::Alert::CLOSE_NOTIFY)
+ {
+ m_stream.set_shutdown_received();
+ // Channel::process_alert will automatically write the corresponding close_notify response to the
+ // send_buffer and close the native_handle after this function returns.
+ }
+ }
+
+ std::chrono::milliseconds tls_verify_cert_chain_ocsp_timeout() const override
+ {
+ return std::chrono::milliseconds(1000);
+ }
+
+ bool tls_session_established(const Botan::TLS::Session&) override
+ {
+ // TODO: it should be possible to configure this in the using application (via callback?)
+ return true;
+ }
+
+ void tls_verify_cert_chain(
+ const std::vector<X509_Certificate>& cert_chain,
+ const std::vector<std::shared_ptr<const OCSP::Response>>& ocsp_responses,
+ const std::vector<Certificate_Store*>& trusted_roots,
+ Usage_Type usage,
+ const std::string& hostname,
+ const TLS::Policy& policy) override
+ {
+ if(m_stream.m_context.has_verify_callback())
+ {
+ m_stream.m_context.get_verify_callback()(cert_chain, ocsp_responses, trusted_roots, usage, hostname, policy);
+ }
+ else
+ {
+ Callbacks::tls_verify_cert_chain(cert_chain, ocsp_responses, trusted_roots, usage, hostname, policy);
+ }
+ }
+
+ private:
+ Stream& m_stream;
+ };
+
+ const boost::asio::mutable_buffer& input_buffer() { return m_input_buffer; }
+ boost::asio::const_buffer send_buffer() const { return m_send_buffer.data(); }
+
+ //! @brief Check if decrypted data is available in the receive buffer
+ bool has_received_data() const { return m_receive_buffer.size() > 0; }
+
+ //! @brief Copy decrypted data into the user-provided buffer
+ template <typename MutableBufferSequence>
+ std::size_t copy_received_data(MutableBufferSequence buffers)
+ {
+ // Note: It would be nice to avoid this buffer copy. This could be achieved by equipping the StreamCore with
+ // the user's desired target buffer once a read is started, and reading directly into that buffer in tls_record
+ // received. However, we need to deal with the case that the receive buffer provided by the caller is smaller
+ // than the decrypted record, so this optimization might not be worth the additional complexity.
+ const auto copiedBytes = boost::asio::buffer_copy(buffers, m_receive_buffer.data());
+ m_receive_buffer.consume(copiedBytes);
+ return copiedBytes;
+ }
+
+ //! @brief Check if encrypted data is available in the send buffer
+ bool has_data_to_send() const { return m_send_buffer.size() > 0; }
+
+ //! @brief Mark bytes in the send buffer as consumed, removing them from the buffer
+ void consume_send_buffer(std::size_t bytesConsumed) { m_send_buffer.consume(bytesConsumed); }
+
+ // This is a helper construct to allow mocking the native_handle in test code. It is activated by explicitly
+ // specifying a (mocked) channel type template parameter when constructing the stream and does not attempt to
+ // instantiate the native_handle.
+ // Note: once we have C++17 we can achieve this much more elegantly using constexpr if.
+ template<class T = ChannelT>
+ typename std::enable_if<!std::is_same<Channel, T>::value>::type
+ setup_native_handle(Connection_Side, boost::system::error_code&) {}
+
+ /**
+ * @brief Create the native handle.
+ *
+ * Depending on the desired connection side, this function will create a Botan::TLS::Client or a
+ * Botan::TLS::Server.
+ *
+ * @param side The desired connection side (client or server)
+ * @param ec Set to indicate what error occurred, if any.
+ */
+ template<class T = ChannelT>
+ typename std::enable_if<std::is_same<Channel, T>::value>::type
+ setup_native_handle(Connection_Side side, boost::system::error_code& ec)
+ {
+ try_with_error_code([&]
+ {
+ if(side == CLIENT)
+ {
+ m_native_handle = std::unique_ptr<Client>(
+ new Client(m_core,
+ m_context.m_session_manager,
+ m_context.m_credentials_manager,
+ m_context.m_policy,
+ m_context.m_rng,
+ m_context.m_server_info,
+ Protocol_Version::latest_tls_version()));
+ }
+ else
+ {
+ m_native_handle = std::unique_ptr<Server>(
+ new Server(m_core,
+ m_context.m_session_manager,
+ m_context.m_credentials_manager,
+ m_context.m_policy,
+ m_context.m_rng,
+ false /* no DTLS */));
+ }
+ }, ec);
+ }
+
+ /** @brief Synchronously write encrypted data from the send buffer to the next layer.
+ *
+ * If this function is called with an error code other than 'Success', it will do nothing and return 0.
+ *
+ * @param ec Set to indicate what error occurred, if any. Specifically, StreamTruncated will be set if the peer
+ * has closed the connection but did not properly shut down the SSL connection.
+ * @return The number of bytes written.
+ */
+ size_t send_pending_encrypted_data(boost::system::error_code& ec)
+ {
+ if(ec)
+ { return 0; }
+
+ auto writtenBytes = boost::asio::write(m_nextLayer, send_buffer(), ec);
+ consume_send_buffer(writtenBytes);
+
+ if(ec == boost::asio::error::eof && !shutdown_received())
+ {
+ // transport layer was closed by peer without receiving 'close_notify'
+ ec = StreamError::StreamTruncated;
+ }
+
+ return writtenBytes;
+ }
+
+ /**
+ * @brief Pass plaintext data to the native handle for processing.
+ *
+ * The native handle will then create TLS records and hand them back to the Stream via the tls_emit_data callback.
+ */
+ template <typename ConstBufferSequence>
+ void tls_encrypt(const ConstBufferSequence& buffers, boost::system::error_code& ec)
+ {
+ // NOTE: This is not asynchronous: it encrypts the data synchronously.
+ // The data encrypted by native_handle()->send() is synchronously stored in the send_buffer of m_core,
+ // but is not actually written to the wire, yet.
+ for(auto it = boost::asio::buffer_sequence_begin(buffers);
+ !ec && it != boost::asio::buffer_sequence_end(buffers);
+ it++)
+ {
+ const boost::asio::const_buffer buffer = *it;
+ try_with_error_code([&]
+ {
+ native_handle()->send(static_cast<const uint8_t*>(buffer.data()), buffer.size());
+ }, ec);
+ }
+ }
+
+ /**
+ * @brief Pass encrypted data to the native handle for processing.
+ *
+ * If an exception occurs while processing the data, an error code will be set.
+ *
+ * @param read_buffer Input buffer containing the encrypted data.
+ * @param ec Set to indicate what error occurred, if any.
+ */
+ void process_encrypted_data(const boost::asio::const_buffer& read_buffer, boost::system::error_code& ec)
+ {
+ try_with_error_code([&]
+ {
+ native_handle()->received_data(static_cast<const uint8_t*>(read_buffer.data()), read_buffer.size());
+ }, ec);
+ }
+
+ //! @brief Catch exceptions and set an error_code
+ template <typename Fun>
+ void try_with_error_code(Fun f, boost::system::error_code& ec)
+ {
+ try
+ {
+ f();
+ }
+ catch(const TLS_Exception& e)
+ {
+ ec = e.type();
+ }
+ catch(const Botan::Exception& e)
+ {
+ ec = e.error_type();
+ }
+ catch(const std::exception&)
+ {
+ ec = Botan::ErrorType::Unknown;
+ }
+ }
+
+ void set_shutdown_received()
+ {
+ m_shutdown_received = true;
+ }
+
+ Context& m_context;
+ StreamLayer m_nextLayer;
+
+ boost::beast::flat_buffer m_receive_buffer;
+ boost::beast::flat_buffer m_send_buffer;
+
+ StreamCore m_core;
+ std::unique_ptr<ChannelT> m_native_handle;
+
+ bool m_shutdown_received;
+
+ // Buffer space used to read input intended for the core
+ std::vector<uint8_t> m_input_buffer_space;
+ const boost::asio::mutable_buffer m_input_buffer;
+ };
+
+} // namespace TLS
+} // namespace Botan
+
+#endif // BOOST_VERSION
+#endif // BOTAN_ASIO_STREAM_H_
diff --git a/comm/third_party/botan/src/lib/tls/asio/info.txt b/comm/third_party/botan/src/lib/tls/asio/info.txt
new file mode 100644
index 0000000000..7862a1c284
--- /dev/null
+++ b/comm/third_party/botan/src/lib/tls/asio/info.txt
@@ -0,0 +1,15 @@
+<defines>
+TLS_ASIO_STREAM -> 20181218
+</defines>
+
+<header:public>
+asio_context.h
+asio_stream.h
+asio_error.h
+asio_async_ops.h
+</header:public>
+
+<requires>
+boost
+tls
+</requires>
diff --git a/comm/third_party/botan/src/lib/tls/credentials_manager.cpp b/comm/third_party/botan/src/lib/tls/credentials_manager.cpp
new file mode 100644
index 0000000000..0c5ae9718f
--- /dev/null
+++ b/comm/third_party/botan/src/lib/tls/credentials_manager.cpp
@@ -0,0 +1,105 @@
+/*
+* Credentials Manager
+* (C) 2011,2012 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/credentials_manager.h>
+#include <botan/pkix_types.h>
+
+namespace Botan {
+
+std::string Credentials_Manager::psk_identity_hint(const std::string&,
+ const std::string&)
+ {
+ return "";
+ }
+
+std::string Credentials_Manager::psk_identity(const std::string&,
+ const std::string&,
+ const std::string&)
+ {
+ return "";
+ }
+
+SymmetricKey Credentials_Manager::psk(const std::string&,
+ const std::string&,
+ const std::string& identity)
+ {
+ throw Internal_Error("No PSK set for identity " + identity);
+ }
+
+bool Credentials_Manager::attempt_srp(const std::string&,
+ const std::string&)
+ {
+ return false;
+ }
+
+std::string Credentials_Manager::srp_identifier(const std::string&,
+ const std::string&)
+ {
+ return "";
+ }
+
+std::string Credentials_Manager::srp_password(const std::string&,
+ const std::string&,
+ const std::string&)
+ {
+ return "";
+ }
+
+bool Credentials_Manager::srp_verifier(const std::string&,
+ const std::string&,
+ const std::string&,
+ std::string&,
+ BigInt&,
+ std::vector<uint8_t>&,
+ bool)
+ {
+ return false;
+ }
+
+std::vector<X509_Certificate> Credentials_Manager::find_cert_chain(
+ const std::vector<std::string>& key_types,
+ const std::vector<X509_DN>&,
+ const std::string& type,
+ const std::string& context)
+ {
+ return cert_chain(key_types, type, context);
+ }
+
+std::vector<X509_Certificate> Credentials_Manager::cert_chain(
+ const std::vector<std::string>&,
+ const std::string&,
+ const std::string&)
+ {
+ return std::vector<X509_Certificate>();
+ }
+
+std::vector<X509_Certificate> Credentials_Manager::cert_chain_single_type(
+ const std::string& cert_key_type,
+ const std::string& type,
+ const std::string& context)
+ {
+ std::vector<std::string> cert_types;
+ cert_types.push_back(cert_key_type);
+ return find_cert_chain(cert_types, std::vector<X509_DN>(), type, context);
+ }
+
+Private_Key* Credentials_Manager::private_key_for(const X509_Certificate&,
+ const std::string&,
+ const std::string&)
+ {
+ return nullptr;
+ }
+
+std::vector<Certificate_Store*>
+Credentials_Manager::trusted_certificate_authorities(
+ const std::string&,
+ const std::string&)
+ {
+ return std::vector<Certificate_Store*>();
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/tls/credentials_manager.h b/comm/third_party/botan/src/lib/tls/credentials_manager.h
new file mode 100644
index 0000000000..627894a87f
--- /dev/null
+++ b/comm/third_party/botan/src/lib/tls/credentials_manager.h
@@ -0,0 +1,196 @@
+/*
+* Credentials Manager
+* (C) 2011,2012 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_CREDENTIALS_MANAGER_H_
+#define BOTAN_CREDENTIALS_MANAGER_H_
+
+#include <botan/pk_keys.h>
+#include <botan/x509cert.h>
+#include <botan/certstor.h>
+#include <botan/symkey.h>
+#include <string>
+
+namespace Botan {
+
+class X509_DN;
+class BigInt;
+
+/**
+* Interface for a credentials manager.
+*
+* A type is a fairly static value that represents the general nature
+* of the transaction occurring. Currently used values are "tls-client"
+* and "tls-server". Context represents a hostname, email address,
+* username, or other identifier.
+*/
+class BOTAN_PUBLIC_API(2,0) Credentials_Manager
+ {
+ public:
+ virtual ~Credentials_Manager() = default;
+
+ /**
+ * Return a list of the certificates of CAs that we trust in this
+ * type/context.
+ *
+ * @param type specifies the type of operation occurring
+ *
+ * @param context specifies a context relative to type. For instance
+ * for type "tls-client", context specifies the servers name.
+ */
+ virtual std::vector<Certificate_Store*> trusted_certificate_authorities(
+ const std::string& type,
+ const std::string& context);
+
+ /**
+ * Return a cert chain we can use, ordered from leaf to root,
+ * or else an empty vector.
+ *
+ * It is assumed that the caller can get the private key of the
+ * leaf with private_key_for
+ *
+ * @param cert_key_types specifies the key types desired ("RSA",
+ * "DSA", "ECDSA", etc), or empty if there
+ * is no preference by the caller.
+ *
+ * @param acceptable_CAs the CAs the requestor will accept (possibly empty)
+ * @param type specifies the type of operation occurring
+ * @param context specifies a context relative to type.
+ */
+ virtual std::vector<X509_Certificate> find_cert_chain(
+ const std::vector<std::string>& cert_key_types,
+ const std::vector<X509_DN>& acceptable_CAs,
+ const std::string& type,
+ const std::string& context);
+
+ /**
+ * Return a cert chain we can use, ordered from leaf to root,
+ * or else an empty vector.
+ *
+ * This virtual function is deprecated, and will be removed in a
+ * future release. Use (and override) find_cert_chain instead.
+ *
+ * It is assumed that the caller can get the private key of the
+ * leaf with private_key_for
+ *
+ * @param cert_key_types specifies the key types desired ("RSA",
+ * "DSA", "ECDSA", etc), or empty if there
+ * is no preference by the caller.
+ *
+ * @param type specifies the type of operation occurring
+ *
+ * @param context specifies a context relative to type.
+ */
+ virtual std::vector<X509_Certificate> cert_chain(
+ const std::vector<std::string>& cert_key_types,
+ const std::string& type,
+ const std::string& context);
+
+ /**
+ * Return a cert chain we can use, ordered from leaf to root,
+ * or else an empty vector.
+ *
+ * It is assumed that the caller can get the private key of the
+ * leaf with private_key_for
+ *
+ * @param cert_key_type specifies the type of key requested
+ * ("RSA", "DSA", "ECDSA", etc)
+ *
+ * @param type specifies the type of operation occurring
+ *
+ * @param context specifies a context relative to type.
+ */
+ std::vector<X509_Certificate> cert_chain_single_type(
+ const std::string& cert_key_type,
+ const std::string& type,
+ const std::string& context);
+
+ /**
+ * @return private key associated with this certificate if we should
+ * use it with this context. cert was returned by cert_chain
+ * @note this object should retain ownership of the returned key;
+ * it should not be deleted by the caller.
+ */
+ virtual Private_Key* private_key_for(const X509_Certificate& cert,
+ const std::string& type,
+ const std::string& context);
+
+ /**
+ * @param type specifies the type of operation occurring
+ * @param context specifies a context relative to type.
+ * @return true if we should attempt SRP authentication
+ */
+ virtual bool attempt_srp(const std::string& type,
+ const std::string& context);
+
+ /**
+ * @param type specifies the type of operation occurring
+ * @param context specifies a context relative to type.
+ * @return identifier for client-side SRP auth, if available
+ for this type/context. Should return empty string
+ if password auth not desired/available.
+ */
+ virtual std::string srp_identifier(const std::string& type,
+ const std::string& context);
+
+ /**
+ * @param type specifies the type of operation occurring
+ * @param context specifies a context relative to type.
+ * @param identifier specifies what identifier we want the
+ * password for. This will be a value previously returned
+ * by srp_identifier.
+ * @return password for client-side SRP auth, if available
+ for this identifier/type/context.
+ */
+ virtual std::string srp_password(const std::string& type,
+ const std::string& context,
+ const std::string& identifier);
+
+ /**
+ * Retrieve SRP verifier parameters
+ */
+ virtual bool srp_verifier(const std::string& type,
+ const std::string& context,
+ const std::string& identifier,
+ std::string& group_name,
+ BigInt& verifier,
+ std::vector<uint8_t>& salt,
+ bool generate_fake_on_unknown);
+
+ /**
+ * @param type specifies the type of operation occurring
+ * @param context specifies a context relative to type.
+ * @return the PSK identity hint for this type/context
+ */
+ virtual std::string psk_identity_hint(const std::string& type,
+ const std::string& context);
+
+ /**
+ * @param type specifies the type of operation occurring
+ * @param context specifies a context relative to type.
+ * @param identity_hint was passed by the server (but may be empty)
+ * @return the PSK identity we want to use
+ */
+ virtual std::string psk_identity(const std::string& type,
+ const std::string& context,
+ const std::string& identity_hint);
+
+ /**
+ * @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
+ psk_identity for the same type and context.
+ * @return the PSK used for identity, or throw an exception if no
+ * key exists
+ */
+ virtual SymmetricKey psk(const std::string& type,
+ const std::string& context,
+ const std::string& identity);
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/tls/info.txt b/comm/third_party/botan/src/lib/tls/info.txt
new file mode 100644
index 0000000000..690bd41501
--- /dev/null
+++ b/comm/third_party/botan/src/lib/tls/info.txt
@@ -0,0 +1,54 @@
+<defines>
+TLS -> 20191210
+</defines>
+
+<header:public>
+credentials_manager.h
+tls_alert.h
+tls_algos.h
+tls_blocking.h
+tls_callbacks.h
+tls_channel.h
+tls_ciphersuite.h
+tls_client.h
+tls_exceptn.h
+tls_extensions.h
+tls_handshake_msg.h
+tls_magic.h
+tls_messages.h
+tls_server_info.h
+tls_policy.h
+tls_server.h
+tls_session.h
+tls_session_manager.h
+tls_version.h
+</header:public>
+
+<header:internal>
+tls_handshake_hash.h
+tls_handshake_io.h
+tls_handshake_state.h
+tls_reader.h
+tls_record.h
+tls_seq_numbers.h
+tls_session_key.h
+</header:internal>
+
+<requires>
+aead
+aes
+asn1
+dh
+ecdh
+ecdsa
+eme_pkcs1
+emsa_pkcs1
+gcm
+hmac
+prf_tls
+rng
+rsa
+sha2_32
+sha2_64
+x509
+</requires>
diff --git a/comm/third_party/botan/src/lib/tls/msg_cert_req.cpp b/comm/third_party/botan/src/lib/tls/msg_cert_req.cpp
new file mode 100644
index 0000000000..9e8a4d803c
--- /dev/null
+++ b/comm/third_party/botan/src/lib/tls/msg_cert_req.cpp
@@ -0,0 +1,156 @@
+/*
+* Certificate Request Message
+* (C) 2004-2006,2012 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/tls_messages.h>
+#include <botan/tls_extensions.h>
+#include <botan/internal/tls_reader.h>
+#include <botan/internal/tls_handshake_io.h>
+#include <botan/internal/tls_handshake_hash.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
+
+namespace Botan {
+
+namespace TLS {
+
+namespace {
+
+std::string cert_type_code_to_name(uint8_t code)
+ {
+ switch(code)
+ {
+ case 1:
+ return "RSA";
+ case 2:
+ return "DSA";
+ case 64:
+ return "ECDSA";
+ default:
+ return ""; // DH or something else
+ }
+ }
+
+uint8_t cert_type_name_to_code(const std::string& name)
+ {
+ if(name == "RSA")
+ return 1;
+ if(name == "DSA")
+ return 2;
+ if(name == "ECDSA")
+ return 64;
+
+ throw Invalid_Argument("Unknown cert type " + name);
+ }
+
+}
+
+/**
+* Create a new Certificate Request message
+*/
+Certificate_Req::Certificate_Req(Handshake_IO& io,
+ Handshake_Hash& hash,
+ const Policy& policy,
+ const std::vector<X509_DN>& ca_certs,
+ Protocol_Version version) :
+ m_names(ca_certs),
+ m_cert_key_types({ "RSA", "ECDSA", "DSA" })
+ {
+ if(version.supports_negotiable_signature_algorithms())
+ {
+ m_schemes = policy.allowed_signature_schemes();
+ }
+
+ hash.update(io.send(*this));
+ }
+
+/**
+* Deserialize a Certificate Request message
+*/
+Certificate_Req::Certificate_Req(const std::vector<uint8_t>& buf,
+ Protocol_Version version)
+ {
+ if(buf.size() < 4)
+ throw Decoding_Error("Certificate_Req: Bad certificate request");
+
+ TLS_Data_Reader reader("CertificateRequest", buf);
+
+ std::vector<uint8_t> cert_type_codes = reader.get_range_vector<uint8_t>(1, 1, 255);
+
+ for(size_t i = 0; i != cert_type_codes.size(); ++i)
+ {
+ const std::string cert_type_name = cert_type_code_to_name(cert_type_codes[i]);
+
+ if(cert_type_name.empty()) // something we don't know
+ continue;
+
+ m_cert_key_types.emplace_back(cert_type_name);
+ }
+
+ if(version.supports_negotiable_signature_algorithms())
+ {
+ const std::vector<uint8_t> algs = reader.get_range_vector<uint8_t>(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.push_back(static_cast<Signature_Scheme>(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<uint8_t> name_bits = reader.get_range_vector<uint8_t>(2, 0, 65535);
+
+ BER_Decoder decoder(name_bits.data(), name_bits.size());
+ X509_DN name;
+ decoder.decode(name);
+ m_names.emplace_back(name);
+ }
+ }
+
+/**
+* Serialize a Certificate Request message
+*/
+std::vector<uint8_t> Certificate_Req::serialize() const
+ {
+ std::vector<uint8_t> buf;
+
+ std::vector<uint8_t> cert_types;
+
+ for(size_t i = 0; i != m_cert_key_types.size(); ++i)
+ cert_types.push_back(cert_type_name_to_code(m_cert_key_types[i]));
+
+ append_tls_length_value(buf, cert_types, 1);
+
+ if(m_schemes.size() > 0)
+ buf += Signature_Algorithms(m_schemes).serialize(Connection_Side::SERVER);
+
+ std::vector<uint8_t> encoded_names;
+
+ for(size_t i = 0; i != m_names.size(); ++i)
+ {
+ DER_Encoder encoder;
+ encoder.encode(m_names[i]);
+
+ append_tls_length_value(encoded_names, encoder.get_contents(), 2);
+ }
+
+ append_tls_length_value(buf, encoded_names, 2);
+
+ return buf;
+ }
+
+}
+
+}
diff --git a/comm/third_party/botan/src/lib/tls/msg_cert_status.cpp b/comm/third_party/botan/src/lib/tls/msg_cert_status.cpp
new file mode 100644
index 0000000000..ecc649a13c
--- /dev/null
+++ b/comm/third_party/botan/src/lib/tls/msg_cert_status.cpp
@@ -0,0 +1,71 @@
+/*
+* Certificate Status
+* (C) 2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/tls_messages.h>
+#include <botan/tls_extensions.h>
+#include <botan/internal/tls_reader.h>
+#include <botan/internal/tls_handshake_io.h>
+#include <botan/internal/tls_handshake_hash.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
+
+namespace Botan {
+
+namespace TLS {
+
+Certificate_Status::Certificate_Status(const std::vector<uint8_t>& buf)
+ {
+ 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,
+ std::shared_ptr<const OCSP::Response> ocsp) :
+ m_response(ocsp->raw_bits())
+ {
+ hash.update(io.send(*this));
+ }
+
+Certificate_Status::Certificate_Status(Handshake_IO& io,
+ Handshake_Hash& hash,
+ const std::vector<uint8_t>& raw_response_bytes) :
+ m_response(raw_response_bytes)
+ {
+ hash.update(io.send(*this));
+ }
+
+std::vector<uint8_t> 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<uint32_t>(m_response.size());
+
+ std::vector<uint8_t> buf;
+ buf.push_back(1); // type OCSP
+ for(size_t i = 1; i < 4; ++i)
+ buf.push_back(get_byte(i, response_len));
+
+ buf += m_response;
+ return buf;
+ }
+
+}
+
+}
diff --git a/comm/third_party/botan/src/lib/tls/msg_cert_verify.cpp b/comm/third_party/botan/src/lib/tls/msg_cert_verify.cpp
new file mode 100644
index 0000000000..711566bd08
--- /dev/null
+++ b/comm/third_party/botan/src/lib/tls/msg_cert_verify.cpp
@@ -0,0 +1,110 @@
+/*
+* 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 <botan/tls_messages.h>
+#include <botan/tls_extensions.h>
+#include <botan/internal/tls_reader.h>
+#include <botan/internal/tls_handshake_io.h>
+#include <botan/internal/tls_handshake_state.h>
+
+namespace Botan {
+
+namespace TLS {
+
+/*
+* Create a new Certificate Verify message
+*/
+Certificate_Verify::Certificate_Verify(Handshake_IO& io,
+ Handshake_State& state,
+ const Policy& policy,
+ RandomNumberGenerator& rng,
+ const Private_Key* priv_key)
+ {
+ BOTAN_ASSERT_NONNULL(priv_key);
+
+ std::pair<std::string, Signature_Format> 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<uint8_t>& buf,
+ Protocol_Version version)
+ {
+ TLS_Data_Reader reader("CertificateVerify", buf);
+
+ if(version.supports_negotiable_signature_algorithms())
+ {
+ m_scheme = static_cast<Signature_Scheme>(reader.get_uint16_t());
+ }
+
+ m_signature = reader.get_range<uint8_t>(2, 0, 65535);
+ reader.assert_done();
+ }
+
+/*
+* Serialize a Certificate Verify message
+*/
+std::vector<uint8_t> Certificate_Verify::serialize() const
+ {
+ std::vector<uint8_t> buf;
+
+ if(m_scheme != Signature_Scheme::NONE)
+ {
+ const uint16_t scheme_code = static_cast<uint16_t>(m_scheme);
+ buf.push_back(get_byte(0, scheme_code));
+ buf.push_back(get_byte(1, scheme_code));
+ }
+
+ if(m_signature.size() > 0xFFFF)
+ throw Encoding_Error("Certificate_Verify signature too long to encode");
+
+ const uint16_t sig_len = static_cast<uint16_t>(m_signature.size());
+ buf.push_back(get_byte(0, sig_len));
+ buf.push_back(get_byte(1, sig_len));
+ buf += m_signature;
+
+ return buf;
+ }
+
+/*
+* Verify a Certificate Verify message
+*/
+bool Certificate_Verify::verify(const X509_Certificate& cert,
+ const Handshake_State& state,
+ const Policy& policy) const
+ {
+ std::unique_ptr<Public_Key> key(cert.subject_public_key());
+
+ policy.check_peer_key_acceptable(*key);
+
+ std::pair<std::string, Signature_Format> format =
+ state.parse_sig_format(*key.get(), m_scheme, 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
+ }
+
+}
+
+}
diff --git a/comm/third_party/botan/src/lib/tls/msg_certificate.cpp b/comm/third_party/botan/src/lib/tls/msg_certificate.cpp
new file mode 100644
index 0000000000..25aeeefd27
--- /dev/null
+++ b/comm/third_party/botan/src/lib/tls/msg_certificate.cpp
@@ -0,0 +1,109 @@
+/*
+* Certificate Message
+* (C) 2004-2006,2012,2020 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/tls_messages.h>
+#include <botan/tls_extensions.h>
+#include <botan/tls_exceptn.h>
+#include <botan/tls_alert.h>
+#include <botan/internal/tls_reader.h>
+#include <botan/internal/tls_handshake_io.h>
+#include <botan/internal/tls_handshake_hash.h>
+#include <botan/loadstor.h>
+#include <botan/data_src.h>
+
+namespace Botan {
+
+namespace TLS {
+
+/**
+* Create a new Certificate message
+*/
+Certificate::Certificate(Handshake_IO& io,
+ Handshake_Hash& hash,
+ const std::vector<X509_Certificate>& cert_list) :
+ m_certs(cert_list)
+ {
+ hash.update(io.send(*this));
+ }
+
+/**
+* Deserialize a Certificate message
+*/
+Certificate::Certificate(const std::vector<uint8_t>& buf, const Policy& policy)
+ {
+ if(buf.size() < 3)
+ throw Decoding_Error("Certificate: Message malformed");
+
+ const size_t total_size = make_uint32(0, buf[0], buf[1], buf[2]);
+
+ if(total_size != buf.size() - 3)
+ throw Decoding_Error("Certificate: Message malformed");
+
+ const size_t max_size = policy.maximum_certificate_chain_size();
+ if(max_size > 0 && total_size > max_size)
+ throw Decoding_Error("Certificate chain exceeds policy specified maximum size");
+
+ const uint8_t* certs = buf.data() + 3;
+
+ while(size_t remaining_bytes = buf.data() + buf.size() - certs)
+ {
+ if(remaining_bytes < 3)
+ throw Decoding_Error("Certificate: Message malformed");
+
+ const size_t cert_size = make_uint32(0, certs[0], certs[1], certs[2]);
+
+ if(remaining_bytes < (3 + cert_size))
+ throw Decoding_Error("Certificate: Message malformed");
+
+ DataSource_Memory cert_buf(&certs[3], cert_size);
+ m_certs.push_back(X509_Certificate(cert_buf));
+
+ certs += cert_size + 3;
+ }
+
+ /*
+ * TLS 1.0 through 1.2 all seem to require that the certificate be
+ * precisely a v3 certificate. In fact the strict wording would seem
+ * to require that every certificate in the chain be v3. But often
+ * the intermediates are outside of the control of the server.
+ * But, require that the leaf certificate be v3
+ */
+ if(m_certs.size() > 0 && m_certs[0].x509_version() != 3)
+ {
+ throw TLS_Exception(Alert::BAD_CERTIFICATE,
+ "The leaf certificate must be v3");
+ }
+ }
+
+/**
+* Serialize a Certificate message
+*/
+std::vector<uint8_t> Certificate::serialize() const
+ {
+ std::vector<uint8_t> buf(3);
+
+ for(size_t i = 0; i != m_certs.size(); ++i)
+ {
+ std::vector<uint8_t> raw_cert = m_certs[i].BER_encode();
+ const size_t cert_size = raw_cert.size();
+ for(size_t j = 0; j != 3; ++j)
+ {
+ buf.push_back(get_byte(j+1, static_cast<uint32_t>(cert_size)));
+ }
+ buf += raw_cert;
+ }
+
+ const size_t buf_size = buf.size() - 3;
+ for(size_t i = 0; i != 3; ++i)
+ buf[i] = get_byte(i+1, static_cast<uint32_t>(buf_size));
+
+ return buf;
+ }
+
+}
+
+}
diff --git a/comm/third_party/botan/src/lib/tls/msg_client_hello.cpp b/comm/third_party/botan/src/lib/tls/msg_client_hello.cpp
new file mode 100644
index 0000000000..149f3f0d4b
--- /dev/null
+++ b/comm/third_party/botan/src/lib/tls/msg_client_hello.cpp
@@ -0,0 +1,465 @@
+/*
+* 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 <botan/tls_messages.h>
+#include <botan/tls_alert.h>
+#include <botan/tls_exceptn.h>
+#include <botan/tls_callbacks.h>
+#include <botan/rng.h>
+#include <botan/hash.h>
+
+#include <botan/internal/tls_reader.h>
+#include <botan/internal/tls_session_key.h>
+#include <botan/internal/tls_handshake_io.h>
+#include <botan/internal/tls_handshake_hash.h>
+#include <botan/internal/stl_util.h>
+#include <chrono>
+
+namespace Botan {
+
+namespace TLS {
+
+enum {
+ TLS_EMPTY_RENEGOTIATION_INFO_SCSV = 0x00FF,
+ TLS_FALLBACK_SCSV = 0x5600
+};
+
+std::vector<uint8_t> make_hello_random(RandomNumberGenerator& rng,
+ const Policy& policy)
+ {
+ std::vector<uint8_t> buf(32);
+ rng.randomize(buf.data(), buf.size());
+
+ std::unique_ptr<HashFunction> sha256 = HashFunction::create_or_throw("SHA-256");
+ sha256->update(buf);
+ sha256->final(buf);
+
+ if(policy.include_time_in_hello_random())
+ {
+ const uint32_t time32 = static_cast<uint32_t>(
+ std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()));
+
+ store_be(time32, buf.data());
+ }
+
+ return buf;
+ }
+
+/*
+* 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<uint8_t>& buf)
+ {
+ if(buf.size())
+ throw Decoding_Error("Bad Hello_Request, has non-zero size");
+ }
+
+/*
+* Serialize a Hello Request message
+*/
+std::vector<uint8_t> Hello_Request::serialize() const
+ {
+ return std::vector<uint8_t>();
+ }
+
+/*
+* Create a new Client Hello message
+*/
+Client_Hello::Client_Hello(Handshake_IO& io,
+ Handshake_Hash& hash,
+ const Policy& policy,
+ Callbacks& cb,
+ RandomNumberGenerator& rng,
+ const std::vector<uint8_t>& reneg_info,
+ const Client_Hello::Settings& client_settings,
+ const std::vector<std::string>& next_protocols) :
+ m_version(client_settings.protocol_version()),
+ m_random(make_hello_random(rng, policy)),
+ m_suites(policy.ciphersuite_list(m_version, !client_settings.srp_identifier().empty())),
+ m_comp_methods(1)
+ {
+ if(!policy.acceptable_protocol_version(m_version))
+ throw Internal_Error("Offering " + m_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.
+ */
+ m_extensions.add(new Extended_Master_Secret);
+ m_extensions.add(new Session_Ticket());
+
+ if(policy.negotiate_encrypt_then_mac())
+ m_extensions.add(new Encrypt_then_MAC);
+
+ m_extensions.add(new Renegotiation_Extension(reneg_info));
+
+ m_extensions.add(new Supported_Versions(m_version, policy));
+
+ if(client_settings.hostname() != "")
+ m_extensions.add(new Server_Name_Indicator(client_settings.hostname()));
+
+ if(policy.support_cert_status_message())
+ m_extensions.add(new Certificate_Status_Request({}, {}));
+
+ if(reneg_info.empty() && !next_protocols.empty())
+ m_extensions.add(new Application_Layer_Protocol_Notification(next_protocols));
+
+ if(m_version.supports_negotiable_signature_algorithms())
+ m_extensions.add(new Signature_Algorithms(policy.allowed_signature_schemes()));
+
+ if(m_version.is_datagram_protocol())
+ m_extensions.add(new SRTP_Protection_Profiles(policy.srtp_profiles()));
+
+#if defined(BOTAN_HAS_SRP6)
+ m_extensions.add(new SRP_Identifier(client_settings.srp_identifier()));
+#else
+ if(!client_settings.srp_identifier().empty())
+ {
+ throw Invalid_State("Attempting to initiate SRP session but TLS-SRP support disabled");
+ }
+#endif
+
+ std::unique_ptr<Supported_Groups> supported_groups(new Supported_Groups(policy.key_exchange_groups()));
+
+ if(supported_groups->ec_groups().size() > 0)
+ {
+ m_extensions.add(new Supported_Point_Formats(policy.use_ecc_point_compression()));
+ }
+
+ m_extensions.add(supported_groups.release());
+
+ cb.tls_modify_extensions(m_extensions, CLIENT);
+
+ if(policy.send_fallback_scsv(client_settings.protocol_version()))
+ m_suites.push_back(TLS_FALLBACK_SCSV);
+
+ hash.update(io.send(*this));
+ }
+
+/*
+* Create a new Client Hello message (session resumption case)
+*/
+Client_Hello::Client_Hello(Handshake_IO& io,
+ Handshake_Hash& hash,
+ const Policy& policy,
+ Callbacks& cb,
+ RandomNumberGenerator& rng,
+ const std::vector<uint8_t>& reneg_info,
+ const Session& session,
+ const std::vector<std::string>& next_protocols) :
+ m_version(session.version()),
+ m_session_id(session.session_id()),
+ m_random(make_hello_random(rng, policy)),
+ m_suites(policy.ciphersuite_list(m_version, (session.srp_identifier() != ""))),
+ m_comp_methods(1)
+ {
+ if(!policy.acceptable_protocol_version(m_version))
+ throw Internal_Error("Offering " + m_version.to_string() +
+ " but our own policy does not accept it");
+
+ if(!value_exists(m_suites, session.ciphersuite_code()))
+ m_suites.push_back(session.ciphersuite_code());
+
+ /*
+ We always add the EMS extension, even if not 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_extensions.add(new Extended_Master_Secret);
+
+ m_extensions.add(new Renegotiation_Extension(reneg_info));
+ m_extensions.add(new Server_Name_Indicator(session.server_info().hostname()));
+ m_extensions.add(new Session_Ticket(session.session_ticket()));
+
+ if(policy.support_cert_status_message())
+ m_extensions.add(new Certificate_Status_Request({}, {}));
+
+ std::unique_ptr<Supported_Groups> supported_groups(new Supported_Groups(policy.key_exchange_groups()));
+
+ if(supported_groups->ec_groups().size() > 0)
+ {
+ m_extensions.add(new Supported_Point_Formats(policy.use_ecc_point_compression()));
+ }
+
+ m_extensions.add(supported_groups.release());
+
+ if(session.supports_encrypt_then_mac())
+ m_extensions.add(new Encrypt_then_MAC);
+
+#if defined(BOTAN_HAS_SRP6)
+ m_extensions.add(new SRP_Identifier(session.srp_identifier()));
+#else
+ if(!session.srp_identifier().empty())
+ {
+ throw Invalid_State("Attempting to resume SRP session but TLS-SRP support disabled");
+ }
+#endif
+
+ if(m_version.supports_negotiable_signature_algorithms())
+ m_extensions.add(new Signature_Algorithms(policy.allowed_signature_schemes()));
+
+ if(reneg_info.empty() && !next_protocols.empty())
+ m_extensions.add(new Application_Layer_Protocol_Notification(next_protocols));
+
+ cb.tls_modify_extensions(m_extensions, CLIENT);
+
+ hash.update(io.send(*this));
+ }
+
+void Client_Hello::update_hello_cookie(const Hello_Verify_Request& hello_verify)
+ {
+ if(!m_version.is_datagram_protocol())
+ throw Invalid_State("Cannot use hello cookie with stream protocol");
+
+ m_hello_cookie = hello_verify.cookie();
+ }
+
+/*
+* Serialize a Client Hello message
+*/
+std::vector<uint8_t> Client_Hello::serialize() const
+ {
+ std::vector<uint8_t> buf;
+
+ buf.push_back(m_version.major_version());
+ buf.push_back(m_version.minor_version());
+ buf += m_random;
+
+ append_tls_length_value(buf, m_session_id, 1);
+
+ if(m_version.is_datagram_protocol())
+ append_tls_length_value(buf, m_hello_cookie, 1);
+
+ append_tls_length_value(buf, m_suites, 2);
+ append_tls_length_value(buf, m_comp_methods, 1);
+
+ /*
+ * May not want to send extensions at all in some cases. If so,
+ * should include SCSV value (if reneg info is empty, if not we are
+ * renegotiating with a modern server)
+ */
+
+ buf += m_extensions.serialize(Connection_Side::CLIENT);
+
+ return buf;
+ }
+
+std::vector<uint8_t> Client_Hello::cookie_input_data() const
+ {
+ std::vector<uint8_t> buf;
+
+ buf.push_back(m_version.major_version());
+ buf.push_back(m_version.minor_version());
+ buf += m_random;
+
+ append_tls_length_value(buf, m_session_id, 1);
+
+ append_tls_length_value(buf, m_suites, 2);
+ append_tls_length_value(buf, m_comp_methods, 1);
+
+ // Here we don't serialize the extensions since the client extensions
+ // may contain values we don't know how to serialize back.
+
+ return buf;
+ }
+
+/*
+* Read a counterparty client hello
+*/
+Client_Hello::Client_Hello(const std::vector<uint8_t>& 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_version = Protocol_Version(major_version, minor_version);
+
+ m_random = reader.get_fixed<uint8_t>(32);
+
+ m_session_id = reader.get_range<uint8_t>(1, 0, 32);
+
+ if(m_version.is_datagram_protocol())
+ m_hello_cookie = reader.get_range<uint8_t>(1, 0, 255);
+
+ m_suites = reader.get_range_vector<uint16_t>(2, 1, 32767);
+
+ m_comp_methods = reader.get_range_vector<uint8_t>(1, 1, 255);
+
+ m_extensions.deserialize(reader, Connection_Side::CLIENT);
+
+ if(offered_suite(static_cast<uint16_t>(TLS_EMPTY_RENEGOTIATION_INFO_SCSV)))
+ {
+ if(Renegotiation_Extension* reneg = m_extensions.get<Renegotiation_Extension>())
+ {
+ if(!reneg->renegotiation_info().empty())
+ throw TLS_Exception(Alert::HANDSHAKE_FAILURE,
+ "Client sent renegotiation SCSV and non-empty extension");
+ }
+ else
+ {
+ // add fake extension
+ m_extensions.add(new Renegotiation_Extension());
+ }
+ }
+ }
+
+bool Client_Hello::sent_fallback_scsv() const
+ {
+ return offered_suite(static_cast<uint16_t>(TLS_FALLBACK_SCSV));
+ }
+
+/*
+* Check if we offered this ciphersuite
+*/
+bool Client_Hello::offered_suite(uint16_t ciphersuite) const
+ {
+ for(size_t i = 0; i != m_suites.size(); ++i)
+ if(m_suites[i] == ciphersuite)
+ return true;
+ return false;
+ }
+
+std::vector<Signature_Scheme> Client_Hello::signature_schemes() const
+ {
+ std::vector<Signature_Scheme> schemes;
+
+ if(Signature_Algorithms* sigs = m_extensions.get<Signature_Algorithms>())
+ {
+ schemes = sigs->supported_schemes();
+ }
+
+ return schemes;
+ }
+
+std::vector<Group_Params> Client_Hello::supported_ecc_curves() const
+ {
+ if(Supported_Groups* groups = m_extensions.get<Supported_Groups>())
+ return groups->ec_groups();
+ return std::vector<Group_Params>();
+ }
+
+std::vector<Group_Params> Client_Hello::supported_dh_groups() const
+ {
+ if(Supported_Groups* groups = m_extensions.get<Supported_Groups>())
+ return groups->dh_groups();
+ return std::vector<Group_Params>();
+ }
+
+bool Client_Hello::prefers_compressed_ec_points() const
+ {
+ if(Supported_Point_Formats* ecc_formats = m_extensions.get<Supported_Point_Formats>())
+ {
+ return ecc_formats->prefers_compressed();
+ }
+ return false;
+ }
+
+std::string Client_Hello::sni_hostname() const
+ {
+ if(Server_Name_Indicator* sni = m_extensions.get<Server_Name_Indicator>())
+ return sni->host_name();
+ return "";
+ }
+
+#if defined(BOTAN_HAS_SRP6)
+std::string Client_Hello::srp_identifier() const
+ {
+ if(SRP_Identifier* srp = m_extensions.get<SRP_Identifier>())
+ return srp->identifier();
+ return "";
+ }
+#endif
+
+bool Client_Hello::secure_renegotiation() const
+ {
+ return m_extensions.has<Renegotiation_Extension>();
+ }
+
+std::vector<uint8_t> Client_Hello::renegotiation_info() const
+ {
+ if(Renegotiation_Extension* reneg = m_extensions.get<Renegotiation_Extension>())
+ return reneg->renegotiation_info();
+ return std::vector<uint8_t>();
+ }
+
+std::vector<Protocol_Version> Client_Hello::supported_versions() const
+ {
+ if(Supported_Versions* versions = m_extensions.get<Supported_Versions>())
+ return versions->versions();
+ return {};
+ }
+
+bool Client_Hello::supports_session_ticket() const
+ {
+ return m_extensions.has<Session_Ticket>();
+ }
+
+std::vector<uint8_t> Client_Hello::session_ticket() const
+ {
+ if(Session_Ticket* ticket = m_extensions.get<Session_Ticket>())
+ return ticket->contents();
+ return std::vector<uint8_t>();
+ }
+
+bool Client_Hello::supports_alpn() const
+ {
+ return m_extensions.has<Application_Layer_Protocol_Notification>();
+ }
+
+bool Client_Hello::supports_extended_master_secret() const
+ {
+ return m_extensions.has<Extended_Master_Secret>();
+ }
+
+bool Client_Hello::supports_cert_status_message() const
+ {
+ return m_extensions.has<Certificate_Status_Request>();
+ }
+
+bool Client_Hello::supports_encrypt_then_mac() const
+ {
+ return m_extensions.has<Encrypt_then_MAC>();
+ }
+
+bool Client_Hello::sent_signature_algorithms() const
+ {
+ return m_extensions.has<Signature_Algorithms>();
+ }
+
+std::vector<std::string> Client_Hello::next_protocols() const
+ {
+ if(auto alpn = m_extensions.get<Application_Layer_Protocol_Notification>())
+ return alpn->protocols();
+ return std::vector<std::string>();
+ }
+
+std::vector<uint16_t> Client_Hello::srtp_profiles() const
+ {
+ if(SRTP_Protection_Profiles* srtp = m_extensions.get<SRTP_Protection_Profiles>())
+ return srtp->profiles();
+ return std::vector<uint16_t>();
+ }
+
+
+}
+
+}
diff --git a/comm/third_party/botan/src/lib/tls/msg_client_kex.cpp b/comm/third_party/botan/src/lib/tls/msg_client_kex.cpp
new file mode 100644
index 0000000000..39266962b5
--- /dev/null
+++ b/comm/third_party/botan/src/lib/tls/msg_client_kex.cpp
@@ -0,0 +1,404 @@
+/*
+* Client Key Exchange Message
+* (C) 2004-2010,2016 Jack Lloyd
+* 2017 Harry Reimann, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/tls_messages.h>
+#include <botan/tls_extensions.h>
+#include <botan/rng.h>
+
+#include <botan/internal/tls_reader.h>
+#include <botan/internal/tls_handshake_io.h>
+#include <botan/internal/tls_handshake_state.h>
+#include <botan/internal/tls_handshake_hash.h>
+#include <botan/credentials_manager.h>
+#include <botan/internal/ct_utils.h>
+
+#include <botan/rsa.h>
+
+#if defined(BOTAN_HAS_CECPQ1)
+ #include <botan/cecpq1.h>
+#endif
+
+#if defined(BOTAN_HAS_SRP6)
+ #include <botan/srp6.h>
+#endif
+
+namespace Botan {
+
+namespace TLS {
+
+/*
+* Create a new Client Key Exchange message
+*/
+Client_Key_Exchange::Client_Key_Exchange(Handshake_IO& io,
+ Handshake_State& state,
+ const Policy& policy,
+ Credentials_Manager& creds,
+ const Public_Key* server_public_key,
+ const std::string& hostname,
+ RandomNumberGenerator& rng)
+ {
+ const Kex_Algo kex_algo = state.ciphersuite().kex_method();
+
+ if(kex_algo == Kex_Algo::PSK)
+ {
+ std::string identity_hint = "";
+
+ if(state.server_kex())
+ {
+ TLS_Data_Reader reader("ClientKeyExchange", state.server_kex()->params());
+ identity_hint = reader.get_string(2, 0, 65535);
+ }
+
+ const std::string psk_identity =
+ creds.psk_identity("tls-client", hostname, identity_hint);
+
+ append_tls_length_value(m_key_material, psk_identity, 2);
+
+ SymmetricKey psk = creds.psk("tls-client", hostname, psk_identity);
+
+ std::vector<uint8_t> 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())
+ {
+ TLS_Data_Reader reader("ClientKeyExchange", state.server_kex()->params());
+
+ SymmetricKey psk;
+
+ if(kex_algo == Kex_Algo::DHE_PSK ||
+ kex_algo == Kex_Algo::ECDHE_PSK)
+ {
+ std::string identity_hint = reader.get_string(2, 0, 65535);
+
+ const std::string psk_identity =
+ creds.psk_identity("tls-client", hostname, identity_hint);
+
+ append_tls_length_value(m_key_material, psk_identity, 2);
+
+ psk = creds.psk("tls-client", hostname, psk_identity);
+ }
+
+ if(kex_algo == Kex_Algo::DH ||
+ kex_algo == Kex_Algo::DHE_PSK)
+ {
+ const std::vector<uint8_t> modulus = reader.get_range<uint8_t>(2, 1, 65535);
+ const std::vector<uint8_t> generator = reader.get_range<uint8_t>(2, 1, 65535);
+ const std::vector<uint8_t> peer_public_value = reader.get_range<uint8_t>(2, 1, 65535);
+
+ if(reader.remaining_bytes())
+ throw Decoding_Error("Bad params size for DH key exchange");
+
+ const std::pair<secure_vector<uint8_t>, std::vector<uint8_t>> dh_result =
+ state.callbacks().tls_dh_agree(modulus, generator, peer_public_value, policy, rng);
+
+ if(kex_algo == Kex_Algo::DH)
+ m_pre_master = dh_result.first;
+ else
+ {
+ append_tls_length_value(m_pre_master, dh_result.first, 2);
+ append_tls_length_value(m_pre_master, psk.bits_of(), 2);
+ }
+
+ append_tls_length_value(m_key_material, dh_result.second, 2);
+ }
+ else if(kex_algo == Kex_Algo::ECDH ||
+ kex_algo == Kex_Algo::ECDHE_PSK)
+ {
+ const uint8_t curve_type = reader.get_byte();
+ if(curve_type != 3)
+ throw Decoding_Error("Server sent non-named ECC curve");
+
+ const Group_Params curve_id = static_cast<Group_Params>(reader.get_uint16_t());
+ const std::vector<uint8_t> peer_public_value = reader.get_range<uint8_t>(1, 1, 255);
+
+ if(policy.choose_key_exchange_group({curve_id}) != curve_id)
+ {
+ throw TLS_Exception(Alert::HANDSHAKE_FAILURE,
+ "Server sent ECC curve prohibited by policy");
+ }
+
+ const std::string curve_name = state.callbacks().tls_decode_group_param(curve_id);
+
+ if(curve_name == "")
+ throw Decoding_Error("Server sent unknown named curve " +
+ std::to_string(static_cast<uint16_t>(curve_id)));
+
+ const std::pair<secure_vector<uint8_t>, std::vector<uint8_t>> ecdh_result =
+ state.callbacks().tls_ecdh_agree(curve_name, peer_public_value, policy, rng,
+ state.server_hello()->prefers_compressed_ec_points());
+
+ if(kex_algo == Kex_Algo::ECDH)
+ {
+ m_pre_master = ecdh_result.first;
+ }
+ else
+ {
+ append_tls_length_value(m_pre_master, ecdh_result.first, 2);
+ append_tls_length_value(m_pre_master, psk.bits_of(), 2);
+ }
+
+ append_tls_length_value(m_key_material, ecdh_result.second, 1);
+ }
+#if defined(BOTAN_HAS_SRP6)
+ else if(kex_algo == Kex_Algo::SRP_SHA)
+ {
+ const BigInt N = BigInt::decode(reader.get_range<uint8_t>(2, 1, 65535));
+ const BigInt g = BigInt::decode(reader.get_range<uint8_t>(2, 1, 65535));
+ std::vector<uint8_t> salt = reader.get_range<uint8_t>(1, 1, 255);
+ const BigInt B = BigInt::decode(reader.get_range<uint8_t>(2, 1, 65535));
+
+ const std::string srp_group = srp6_group_identifier(N, g);
+
+ const std::string srp_identifier =
+ creds.srp_identifier("tls-client", hostname);
+
+ const std::string srp_password =
+ creds.srp_password("tls-client", hostname, srp_identifier);
+
+ std::pair<BigInt, SymmetricKey> srp_vals =
+ srp6_client_agree(srp_identifier,
+ srp_password,
+ srp_group,
+ "SHA-1",
+ salt,
+ B,
+ rng);
+
+ append_tls_length_value(m_key_material, BigInt::encode(srp_vals.first), 2);
+ m_pre_master = srp_vals.second.bits_of();
+ }
+#endif
+
+#if defined(BOTAN_HAS_CECPQ1)
+ else if(kex_algo == Kex_Algo::CECPQ1)
+ {
+ const std::vector<uint8_t> cecpq1_offer = reader.get_range<uint8_t>(2, 1, 65535);
+
+ if(cecpq1_offer.size() != CECPQ1_OFFER_BYTES)
+ throw TLS_Exception(Alert::HANDSHAKE_FAILURE, "Invalid CECPQ1 key size");
+
+ std::vector<uint8_t> newhope_accept(CECPQ1_ACCEPT_BYTES);
+ secure_vector<uint8_t> shared_secret(CECPQ1_SHARED_KEY_BYTES);
+ CECPQ1_accept(shared_secret.data(), newhope_accept.data(), cecpq1_offer.data(), rng);
+ append_tls_length_value(m_key_material, newhope_accept, 2);
+ m_pre_master = shared_secret;
+ }
+#endif
+ else
+ {
+ throw Internal_Error("Client_Key_Exchange: Unknown key exchange method was negotiated");
+ }
+
+ reader.assert_done();
+ }
+ else
+ {
+ // No server key exchange msg better mean RSA kex + RSA key in cert
+
+ if(kex_algo != Kex_Algo::STATIC_RSA)
+ throw Unexpected_Message("No server kex message, but negotiated a key exchange that required it");
+
+ if(!server_public_key)
+ throw Internal_Error("No server public key for RSA exchange");
+
+ if(auto rsa_pub = dynamic_cast<const RSA_PublicKey*>(server_public_key))
+ {
+ const Protocol_Version offered_version = state.client_hello()->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 std::vector<uint8_t> encrypted_key = encryptor.encrypt(m_pre_master, rng);
+
+ append_tls_length_value(m_key_material, encrypted_key, 2);
+ }
+ else
+ throw TLS_Exception(Alert::HANDSHAKE_FAILURE,
+ "Expected a RSA key in server cert but got " +
+ server_public_key->algo_name());
+ }
+
+ state.hash().update(io.send(*this));
+ }
+
+/*
+* Read a Client Key Exchange message
+*/
+Client_Key_Exchange::Client_Key_Exchange(const std::vector<uint8_t>& contents,
+ const Handshake_State& state,
+ const Private_Key* server_rsa_kex_key,
+ Credentials_Manager& creds,
+ const Policy& policy,
+ RandomNumberGenerator& rng)
+ {
+ const Kex_Algo kex_algo = state.ciphersuite().kex_method();
+
+ if(kex_algo == Kex_Algo::STATIC_RSA)
+ {
+ 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)
+ throw Internal_Error("Expected RSA kex but no server kex key set");
+
+ if(!dynamic_cast<const RSA_PrivateKey*>(server_rsa_kex_key))
+ throw Internal_Error("Expected RSA key but got " + server_rsa_kex_key->algo_name());
+
+ TLS_Data_Reader reader("ClientKeyExchange", contents);
+ const std::vector<uint8_t> encrypted_pre_master = reader.get_range<uint8_t>(2, 0, 65535);
+ reader.assert_done();
+
+ PK_Decryptor_EME decryptor(*server_rsa_kex_key, rng, "PKCS1v15");
+
+ const uint8_t client_major = state.client_hello()->version().major_version();
+ const uint8_t client_minor = state.client_hello()->version().minor_version();
+
+ /*
+ * PK_Decryptor::decrypt_or_random will return a random value if
+ * either the length does not match the expected value or if the
+ * version number embedded in the PMS does not match the one sent
+ * in the client hello.
+ */
+ const size_t expected_plaintext_size = 48;
+ const size_t expected_content_size = 2;
+ const uint8_t expected_content_bytes[expected_content_size] = { client_major, client_minor };
+ const uint8_t expected_content_pos[expected_content_size] = { 0, 1 };
+
+ m_pre_master =
+ decryptor.decrypt_or_random(encrypted_pre_master.data(),
+ encrypted_pre_master.size(),
+ expected_plaintext_size,
+ rng,
+ expected_content_bytes,
+ expected_content_pos,
+ expected_content_size);
+ }
+ else
+ {
+ TLS_Data_Reader reader("ClientKeyExchange", contents);
+
+ SymmetricKey psk;
+
+ if(key_exchange_is_psk(kex_algo))
+ {
+ const std::string psk_identity = reader.get_string(2, 0, 65535);
+
+ psk = creds.psk("tls-server",
+ state.client_hello()->sni_hostname(),
+ psk_identity);
+
+ if(psk.length() == 0)
+ {
+ if(policy.hide_unknown_users())
+ psk = SymmetricKey(rng, 16);
+ else
+ throw TLS_Exception(Alert::UNKNOWN_PSK_IDENTITY,
+ "No PSK for identifier " + psk_identity);
+ }
+ }
+
+ if(kex_algo == Kex_Algo::PSK)
+ {
+ std::vector<uint8_t> zeros(psk.length());
+ append_tls_length_value(m_pre_master, zeros, 2);
+ append_tls_length_value(m_pre_master, psk.bits_of(), 2);
+ }
+#if defined(BOTAN_HAS_SRP6)
+ else if(kex_algo == Kex_Algo::SRP_SHA)
+ {
+ SRP6_Server_Session& srp = state.server_kex()->server_srp_params();
+
+ m_pre_master = srp.step2(BigInt::decode(reader.get_range<uint8_t>(2, 0, 65535))).bits_of();
+ }
+#endif
+#if defined(BOTAN_HAS_CECPQ1)
+ else if(kex_algo == Kex_Algo::CECPQ1)
+ {
+ const CECPQ1_key& cecpq1_offer = state.server_kex()->cecpq1_key();
+
+ const std::vector<uint8_t> cecpq1_accept = reader.get_range<uint8_t>(2, 0, 65535);
+ if(cecpq1_accept.size() != CECPQ1_ACCEPT_BYTES)
+ throw Decoding_Error("Invalid size for CECPQ1 accept message");
+
+ m_pre_master.resize(CECPQ1_SHARED_KEY_BYTES);
+ CECPQ1_finish(m_pre_master.data(), cecpq1_offer, cecpq1_accept.data());
+ }
+#endif
+ else if(kex_algo == Kex_Algo::DH ||
+ kex_algo == Kex_Algo::DHE_PSK ||
+ kex_algo == Kex_Algo::ECDH ||
+ kex_algo == Kex_Algo::ECDHE_PSK)
+ {
+ const Private_Key& private_key = state.server_kex()->server_kex_key();
+
+ const PK_Key_Agreement_Key* ka_key =
+ dynamic_cast<const PK_Key_Agreement_Key*>(&private_key);
+
+ if(!ka_key)
+ throw Internal_Error("Expected key agreement key type but got " +
+ private_key.algo_name());
+
+ std::vector<uint8_t> client_pubkey;
+
+ if(ka_key->algo_name() == "DH")
+ {
+ client_pubkey = reader.get_range<uint8_t>(2, 0, 65535);
+ }
+ else
+ {
+ client_pubkey = reader.get_range<uint8_t>(1, 1, 255);
+ }
+
+ try
+ {
+ PK_Key_Agreement ka(*ka_key, rng, "Raw");
+
+ secure_vector<uint8_t> shared_secret = ka.derive_key(0, client_pubkey).bits_of();
+
+ if(ka_key->algo_name() == "DH")
+ shared_secret = CT::strip_leading_zeros(shared_secret);
+
+ if(kex_algo == Kex_Algo::DHE_PSK ||
+ kex_algo == Kex_Algo::ECDHE_PSK)
+ {
+ append_tls_length_value(m_pre_master, shared_secret, 2);
+ append_tls_length_value(m_pre_master, psk.bits_of(), 2);
+ }
+ else
+ m_pre_master = shared_secret;
+ }
+ catch(Invalid_Argument& e)
+ {
+ throw TLS_Exception(Alert::ILLEGAL_PARAMETER, e.what());
+ }
+ catch(std::exception&)
+ {
+ /*
+ * Something failed in the DH/ECDH computation. To avoid possible
+ * attacks which are based on triggering and detecting some edge
+ * failure condition, randomize the pre-master output and carry on,
+ * allowing the protocol to fail later in the finished checks.
+ */
+ rng.random_vec(m_pre_master, ka_key->public_value().size());
+ }
+
+ reader.assert_done();
+ }
+ else
+ throw Internal_Error("Client_Key_Exchange: Unknown key exchange negotiated");
+ }
+ }
+
+}
+
+}
diff --git a/comm/third_party/botan/src/lib/tls/msg_finished.cpp b/comm/third_party/botan/src/lib/tls/msg_finished.cpp
new file mode 100644
index 0000000000..47c875a518
--- /dev/null
+++ b/comm/third_party/botan/src/lib/tls/msg_finished.cpp
@@ -0,0 +1,91 @@
+/*
+* Finished Message
+* (C) 2004-2006,2012 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/tls_messages.h>
+#include <botan/kdf.h>
+#include <botan/internal/tls_handshake_io.h>
+#include <botan/internal/tls_handshake_state.h>
+
+namespace Botan {
+
+namespace TLS {
+
+namespace {
+
+/*
+* Compute the verify_data
+*/
+std::vector<uint8_t> finished_compute_verify(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 };
+
+ std::unique_ptr<KDF> prf(state.protocol_specific_prf());
+
+ std::vector<uint8_t> input;
+ std::vector<uint8_t> label;
+ if(side == CLIENT)
+ label += std::make_pair(TLS_CLIENT_LABEL, sizeof(TLS_CLIENT_LABEL));
+ else
+ label += std::make_pair(TLS_SERVER_LABEL, sizeof(TLS_SERVER_LABEL));
+
+ input += state.hash().final(state.version(), state.ciphersuite().prf_algo());
+
+ return unlock(prf->derive_key(12, state.session_keys().master_secret(), input, label));
+ }
+
+}
+
+/*
+* Create a new Finished message
+*/
+Finished::Finished(Handshake_IO& io,
+ Handshake_State& state,
+ Connection_Side side) : m_verification_data(finished_compute_verify( state, side ))
+ {
+ state.hash().update(io.send(*this));
+ }
+
+/*
+* Serialize a Finished message
+*/
+std::vector<uint8_t> Finished::serialize() const
+ {
+ return m_verification_data;
+ }
+
+/*
+* Deserialize a Finished message
+*/
+Finished::Finished(const std::vector<uint8_t>& buf) : m_verification_data(buf)
+ {}
+
+/*
+* Verify a Finished message
+*/
+bool Finished::verify(const Handshake_State& state,
+ Connection_Side side) const
+ {
+ std::vector<byte> computed_verify = finished_compute_verify(state, side);
+
+#if defined(BOTAN_UNSAFE_FUZZER_MODE)
+ return true;
+#else
+ return (m_verification_data.size() == computed_verify.size()) &&
+ constant_time_compare(m_verification_data.data(), computed_verify.data(), computed_verify.size());
+#endif
+ }
+
+}
+
+}
diff --git a/comm/third_party/botan/src/lib/tls/msg_hello_verify.cpp b/comm/third_party/botan/src/lib/tls/msg_hello_verify.cpp
new file mode 100644
index 0000000000..bc93af9d62
--- /dev/null
+++ b/comm/third_party/botan/src/lib/tls/msg_hello_verify.cpp
@@ -0,0 +1,69 @@
+/*
+* DTLS Hello Verify Request
+* (C) 2012 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/tls_messages.h>
+#include <botan/mac.h>
+
+namespace Botan {
+
+namespace TLS {
+
+Hello_Verify_Request::Hello_Verify_Request(const std::vector<uint8_t>& buf)
+ {
+ if(buf.size() < 3)
+ throw Decoding_Error("Hello verify request too small");
+
+ Protocol_Version version(buf[0], buf[1]);
+
+ if(version != Protocol_Version::DTLS_V10 &&
+ version != Protocol_Version::DTLS_V12)
+ {
+ throw Decoding_Error("Unknown version from server in hello verify request");
+ }
+
+ if(static_cast<size_t>(buf[2]) + 3 != buf.size())
+ throw Decoding_Error("Bad length in hello verify request");
+
+ m_cookie.assign(buf.begin() + 3, buf.end());
+ }
+
+Hello_Verify_Request::Hello_Verify_Request(const std::vector<uint8_t>& client_hello_bits,
+ const std::string& client_identity,
+ const SymmetricKey& secret_key)
+ {
+ std::unique_ptr<MessageAuthenticationCode> hmac = MessageAuthenticationCode::create_or_throw("HMAC(SHA-256)");
+ hmac->set_key(secret_key);
+
+ hmac->update_be(static_cast<uint64_t>(client_hello_bits.size()));
+ hmac->update(client_hello_bits);
+ hmac->update_be(static_cast<uint64_t>(client_identity.size()));
+ hmac->update(client_identity);
+
+ m_cookie.resize(hmac->output_length());
+ hmac->final(m_cookie.data());
+ }
+
+std::vector<uint8_t> Hello_Verify_Request::serialize() const
+ {
+ /* DTLS 1.2 server implementations SHOULD use DTLS version 1.0
+ regardless of the version of TLS that is expected to be
+ negotiated (RFC 6347, section 4.2.1)
+ */
+
+ Protocol_Version format_version(Protocol_Version::DTLS_V10);
+
+ std::vector<uint8_t> bits;
+ bits.push_back(format_version.major_version());
+ bits.push_back(format_version.minor_version());
+ bits.push_back(static_cast<uint8_t>(m_cookie.size()));
+ bits += m_cookie;
+ return bits;
+ }
+
+}
+
+}
diff --git a/comm/third_party/botan/src/lib/tls/msg_server_hello.cpp b/comm/third_party/botan/src/lib/tls/msg_server_hello.cpp
new file mode 100644
index 0000000000..651fd14f89
--- /dev/null
+++ b/comm/third_party/botan/src/lib/tls/msg_server_hello.cpp
@@ -0,0 +1,251 @@
+/*
+* 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 <botan/tls_messages.h>
+#include <botan/tls_extensions.h>
+#include <botan/tls_callbacks.h>
+#include <botan/internal/tls_reader.h>
+#include <botan/internal/tls_session_key.h>
+#include <botan/internal/tls_handshake_io.h>
+#include <botan/internal/tls_handshake_hash.h>
+#include <botan/internal/stl_util.h>
+
+namespace Botan {
+
+namespace TLS {
+
+namespace {
+
+const uint64_t DOWNGRADE_TLS11 = 0x444F574E47524400;
+//const uint64_t DOWNGRADE_TLS12 = 0x444F574E47524401;
+
+std::vector<uint8_t>
+make_server_hello_random(RandomNumberGenerator& rng,
+ Protocol_Version offered_version,
+ const Policy& policy)
+ {
+ auto random = make_hello_random(rng, policy);
+
+ if((offered_version == Protocol_Version::TLS_V10 ||
+ offered_version == Protocol_Version::TLS_V11) &&
+ policy.allow_tls12())
+ {
+ store_be(DOWNGRADE_TLS11, &random[24]);
+ }
+
+ if(offered_version == Protocol_Version::DTLS_V10 && policy.allow_dtls12())
+ {
+ store_be(DOWNGRADE_TLS11, &random[24]);
+ }
+
+ return random;
+ }
+
+}
+
+// New session case
+Server_Hello::Server_Hello(Handshake_IO& io,
+ Handshake_Hash& hash,
+ const Policy& policy,
+ Callbacks& cb,
+ RandomNumberGenerator& rng,
+ const std::vector<uint8_t>& reneg_info,
+ const Client_Hello& client_hello,
+ const Server_Hello::Settings& server_settings,
+ const std::string next_protocol) :
+ m_version(server_settings.protocol_version()),
+ m_session_id(server_settings.session_id()),
+ m_random(make_server_hello_random(rng, m_version, policy)),
+ m_ciphersuite(server_settings.ciphersuite()),
+ m_comp_method(0)
+ {
+ if(client_hello.supports_extended_master_secret())
+ m_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_extensions.add(new Certificate_Status_Request);
+
+ Ciphersuite c = Ciphersuite::by_id(m_ciphersuite);
+
+ if(c.cbc_ciphersuite() && client_hello.supports_encrypt_then_mac() && policy.negotiate_encrypt_then_mac())
+ {
+ m_extensions.add(new Encrypt_then_MAC);
+ }
+
+ if(c.ecc_ciphersuite() && client_hello.extension_types().count(TLSEXT_EC_POINT_FORMATS))
+ {
+ m_extensions.add(new Supported_Point_Formats(policy.use_ecc_point_compression()));
+ }
+
+ if(client_hello.secure_renegotiation())
+ m_extensions.add(new Renegotiation_Extension(reneg_info));
+
+ if(client_hello.supports_session_ticket() && server_settings.offer_session_ticket())
+ m_extensions.add(new Session_Ticket());
+
+ if(!next_protocol.empty() && client_hello.supports_alpn())
+ m_extensions.add(new Application_Layer_Protocol_Notification(next_protocol));
+
+ if(m_version.is_datagram_protocol())
+ {
+ const std::vector<uint16_t> server_srtp = policy.srtp_profiles();
+ const std::vector<uint16_t> 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_extensions.add(new SRTP_Protection_Profiles(shared));
+ }
+ }
+
+ cb.tls_modify_extensions(m_extensions, SERVER);
+
+ hash.update(io.send(*this));
+ }
+
+// Resuming
+Server_Hello::Server_Hello(Handshake_IO& io,
+ Handshake_Hash& hash,
+ const Policy& policy,
+ Callbacks& cb,
+ RandomNumberGenerator& rng,
+ const std::vector<uint8_t>& reneg_info,
+ const Client_Hello& client_hello,
+ Session& resumed_session,
+ bool offer_session_ticket,
+ const std::string& next_protocol) :
+ m_version(resumed_session.version()),
+ m_session_id(client_hello.session_id()),
+ m_random(make_hello_random(rng, policy)),
+ m_ciphersuite(resumed_session.ciphersuite_code()),
+ m_comp_method(0)
+ {
+ if(client_hello.supports_extended_master_secret())
+ m_extensions.add(new Extended_Master_Secret);
+
+ if(client_hello.supports_encrypt_then_mac() && policy.negotiate_encrypt_then_mac())
+ {
+ Ciphersuite c = resumed_session.ciphersuite();
+ if(c.cbc_ciphersuite())
+ m_extensions.add(new Encrypt_then_MAC);
+ }
+
+ if(resumed_session.ciphersuite().ecc_ciphersuite() && client_hello.extension_types().count(TLSEXT_EC_POINT_FORMATS))
+ {
+ m_extensions.add(new Supported_Point_Formats(policy.use_ecc_point_compression()));
+ }
+
+ if(client_hello.secure_renegotiation())
+ m_extensions.add(new Renegotiation_Extension(reneg_info));
+
+ if(client_hello.supports_session_ticket() && offer_session_ticket)
+ m_extensions.add(new Session_Ticket());
+
+ if(!next_protocol.empty() && client_hello.supports_alpn())
+ m_extensions.add(new Application_Layer_Protocol_Notification(next_protocol));
+
+ cb.tls_modify_extensions(m_extensions, SERVER);
+
+ hash.update(io.send(*this));
+ }
+
+/*
+* Deserialize a Server Hello message
+*/
+Server_Hello::Server_Hello(const std::vector<uint8_t>& 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_version = Protocol_Version(major_version, minor_version);
+
+ m_random = reader.get_fixed<uint8_t>(32);
+
+ m_session_id = reader.get_range<uint8_t>(1, 0, 32);
+
+ m_ciphersuite = reader.get_uint16_t();
+
+ m_comp_method = reader.get_byte();
+
+ m_extensions.deserialize(reader, Connection_Side::SERVER);
+ }
+
+/*
+* Serialize a Server Hello message
+*/
+std::vector<uint8_t> Server_Hello::serialize() const
+ {
+ std::vector<uint8_t> buf;
+
+ buf.push_back(m_version.major_version());
+ buf.push_back(m_version.minor_version());
+ buf += m_random;
+
+ append_tls_length_value(buf, m_session_id, 1);
+
+ buf.push_back(get_byte(0, m_ciphersuite));
+ buf.push_back(get_byte(1, m_ciphersuite));
+
+ buf.push_back(m_comp_method);
+
+ buf += m_extensions.serialize(Connection_Side::SERVER);
+
+ return buf;
+ }
+
+bool Server_Hello::random_signals_downgrade() const
+ {
+ const uint64_t last8 = load_be<uint64_t>(m_random.data(), 3);
+ return (last8 == DOWNGRADE_TLS11);
+ }
+
+/*
+* 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<uint8_t>& buf)
+ {
+ if(buf.size())
+ throw Decoding_Error("Server_Hello_Done: Must be empty, and is not");
+ }
+
+/*
+* Serialize a Server Hello Done message
+*/
+std::vector<uint8_t> Server_Hello_Done::serialize() const
+ {
+ return std::vector<uint8_t>();
+ }
+
+}
+
+}
diff --git a/comm/third_party/botan/src/lib/tls/msg_server_kex.cpp b/comm/third_party/botan/src/lib/tls/msg_server_kex.cpp
new file mode 100644
index 0000000000..797907ed82
--- /dev/null
+++ b/comm/third_party/botan/src/lib/tls/msg_server_kex.cpp
@@ -0,0 +1,334 @@
+/*
+* Server Key Exchange Message
+* (C) 2004-2010,2012,2015,2016 Jack Lloyd
+* 2017 Harry Reimann, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/tls_messages.h>
+#include <botan/tls_extensions.h>
+#include <botan/internal/tls_reader.h>
+#include <botan/internal/tls_handshake_io.h>
+#include <botan/internal/tls_handshake_state.h>
+#include <botan/credentials_manager.h>
+#include <botan/loadstor.h>
+#include <botan/pubkey.h>
+
+#include <botan/dh.h>
+#include <botan/ecdh.h>
+
+#if defined(BOTAN_HAS_CURVE_25519)
+ #include <botan/curve25519.h>
+#endif
+
+#if defined(BOTAN_HAS_CECPQ1)
+ #include <botan/cecpq1.h>
+#endif
+
+#if defined(BOTAN_HAS_SRP6)
+ #include <botan/srp6.h>
+#endif
+
+namespace Botan {
+
+namespace TLS {
+
+/**
+* Create a new Server Key Exchange message
+*/
+Server_Key_Exchange::Server_Key_Exchange(Handshake_IO& io,
+ Handshake_State& state,
+ const Policy& policy,
+ Credentials_Manager& creds,
+ RandomNumberGenerator& rng,
+ const Private_Key* signing_key)
+ {
+ const std::string hostname = state.client_hello()->sni_hostname();
+ const Kex_Algo kex_algo = state.ciphersuite().kex_method();
+
+ if(kex_algo == Kex_Algo::PSK || kex_algo == Kex_Algo::DHE_PSK || kex_algo == Kex_Algo::ECDHE_PSK)
+ {
+ std::string identity_hint =
+ creds.psk_identity_hint("tls-server", hostname);
+
+ append_tls_length_value(m_params, identity_hint, 2);
+ }
+
+ if(kex_algo == Kex_Algo::DH || kex_algo == Kex_Algo::DHE_PSK)
+ {
+ const std::vector<Group_Params> dh_groups = state.client_hello()->supported_dh_groups();
+
+ Group_Params shared_group = Group_Params::NONE;
+
+ /*
+ If the client does not send any DH groups in the supported groups
+ extension, but does offer DH ciphersuites, we select a group arbitrarily
+ */
+
+ if(dh_groups.empty())
+ {
+ shared_group = policy.default_dh_group();
+ }
+ else
+ {
+ shared_group = policy.choose_key_exchange_group(dh_groups);
+ }
+
+ if(shared_group == Group_Params::NONE)
+ throw TLS_Exception(Alert::HANDSHAKE_FAILURE,
+ "Could not agree on a DH group with the client");
+
+ BOTAN_ASSERT(group_param_is_dh(shared_group), "DH groups for the DH ciphersuites god");
+
+ const std::string group_name = state.callbacks().tls_decode_group_param(shared_group);
+ std::unique_ptr<DH_PrivateKey> dh(new DH_PrivateKey(rng, DL_Group(group_name)));
+
+ append_tls_length_value(m_params, BigInt::encode(dh->get_domain().get_p()), 2);
+ append_tls_length_value(m_params, BigInt::encode(dh->get_domain().get_g()), 2);
+ append_tls_length_value(m_params, dh->public_value(), 2);
+ m_kex_key.reset(dh.release());
+ }
+ else if(kex_algo == Kex_Algo::ECDH || kex_algo == Kex_Algo::ECDHE_PSK)
+ {
+ const std::vector<Group_Params> ec_groups = state.client_hello()->supported_ecc_curves();
+
+ if(ec_groups.empty())
+ throw Internal_Error("Client sent no ECC extension but we negotiated ECDH");
+
+ Group_Params shared_group = policy.choose_key_exchange_group(ec_groups);
+
+ if(shared_group == Group_Params::NONE)
+ throw TLS_Exception(Alert::HANDSHAKE_FAILURE, "No shared ECC group with client");
+
+ std::vector<uint8_t> ecdh_public_val;
+
+ if(shared_group == Group_Params::X25519)
+ {
+#if defined(BOTAN_HAS_CURVE_25519)
+ std::unique_ptr<Curve25519_PrivateKey> x25519(new Curve25519_PrivateKey(rng));
+ ecdh_public_val = x25519->public_value();
+ m_kex_key.reset(x25519.release());
+#else
+ throw Internal_Error("Negotiated X25519 somehow, but it is disabled");
+#endif
+ }
+ else
+ {
+ Group_Params curve = policy.choose_key_exchange_group(ec_groups);
+
+ const std::string curve_name = state.callbacks().tls_decode_group_param(curve);
+
+ EC_Group ec_group(curve_name);
+ std::unique_ptr<ECDH_PrivateKey> ecdh(new ECDH_PrivateKey(rng, ec_group));
+
+ // follow client's preference for point compression
+ ecdh_public_val = ecdh->public_value(
+ state.client_hello()->prefers_compressed_ec_points() ?
+ PointGFp::COMPRESSED : PointGFp::UNCOMPRESSED);
+
+ m_kex_key.reset(ecdh.release());
+ }
+
+ const uint16_t named_curve_id = static_cast<uint16_t>(shared_group);
+ m_params.push_back(3); // named curve
+ 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);
+ }
+#if defined(BOTAN_HAS_SRP6)
+ else if(kex_algo == Kex_Algo::SRP_SHA)
+ {
+ const std::string srp_identifier = state.client_hello()->srp_identifier();
+
+ std::string group_id;
+ BigInt v;
+ std::vector<uint8_t> salt;
+
+ const bool found = creds.srp_verifier("tls-server", hostname,
+ srp_identifier,
+ group_id, v, salt,
+ policy.hide_unknown_users());
+
+ if(!found)
+ throw TLS_Exception(Alert::UNKNOWN_PSK_IDENTITY,
+ "Unknown SRP user " + srp_identifier);
+
+ m_srp_params.reset(new SRP6_Server_Session);
+
+ BigInt B = m_srp_params->step1(v, group_id,
+ "SHA-1", rng);
+
+ DL_Group group(group_id);
+
+ append_tls_length_value(m_params, BigInt::encode(group.get_p()), 2);
+ append_tls_length_value(m_params, BigInt::encode(group.get_g()), 2);
+ append_tls_length_value(m_params, salt, 1);
+ append_tls_length_value(m_params, BigInt::encode(B), 2);
+ }
+#endif
+#if defined(BOTAN_HAS_CECPQ1)
+ else if(kex_algo == Kex_Algo::CECPQ1)
+ {
+ std::vector<uint8_t> cecpq1_offer(CECPQ1_OFFER_BYTES);
+ m_cecpq1_key.reset(new CECPQ1_key);
+ CECPQ1_offer(cecpq1_offer.data(), m_cecpq1_key.get(), rng);
+ append_tls_length_value(m_params, cecpq1_offer, 2);
+ }
+#endif
+ else if(kex_algo != Kex_Algo::PSK)
+ {
+ throw Internal_Error("Server_Key_Exchange: Unknown kex type " +
+ kex_method_to_string(kex_algo));
+ }
+
+ if(state.ciphersuite().signature_used())
+ {
+ BOTAN_ASSERT(signing_key, "Signing key was set");
+
+ std::pair<std::string, Signature_Format> format =
+ state.choose_sig_format(*signing_key, m_scheme, false, policy);
+
+ std::vector<uint8_t> buf = state.client_hello()->random();
+
+ buf += state.server_hello()->random();
+ buf += params();
+
+ m_signature =
+ state.callbacks().tls_sign_message(*signing_key, rng,
+ format.first, format.second, buf);
+ }
+
+ state.hash().update(io.send(*this));
+ }
+
+/**
+* Deserialize a Server Key Exchange message
+*/
+Server_Key_Exchange::Server_Key_Exchange(const std::vector<uint8_t>& buf,
+ const Kex_Algo kex_algo,
+ const Auth_Method auth_method,
+ Protocol_Version version)
+ {
+ TLS_Data_Reader reader("ServerKeyExchange", buf);
+
+ /*
+ * Here we are deserializing enough to find out what offset the
+ * signature is at. All processing is done when the Client Key Exchange
+ * is prepared.
+ */
+
+ if(kex_algo == Kex_Algo::PSK || kex_algo == Kex_Algo::DHE_PSK || kex_algo == Kex_Algo::ECDHE_PSK)
+ {
+ reader.get_string(2, 0, 65535); // identity hint
+ }
+
+ if(kex_algo == Kex_Algo::DH || kex_algo == Kex_Algo::DHE_PSK)
+ {
+ // 3 bigints, DH p, g, Y
+
+ for(size_t i = 0; i != 3; ++i)
+ {
+ reader.get_range<uint8_t>(2, 1, 65535);
+ }
+ }
+ else if(kex_algo == Kex_Algo::ECDH || kex_algo == Kex_Algo::ECDHE_PSK)
+ {
+ reader.get_byte(); // curve type
+ reader.get_uint16_t(); // curve id
+ reader.get_range<uint8_t>(1, 1, 255); // public key
+ }
+ else if(kex_algo == Kex_Algo::SRP_SHA)
+ {
+ // 2 bigints (N,g) then salt, then server B
+
+ reader.get_range<uint8_t>(2, 1, 65535);
+ reader.get_range<uint8_t>(2, 1, 65535);
+ reader.get_range<uint8_t>(1, 1, 255);
+ reader.get_range<uint8_t>(2, 1, 65535);
+ }
+ else if(kex_algo == Kex_Algo::CECPQ1)
+ {
+ // u16 blob
+ reader.get_range<uint8_t>(2, 1, 65535);
+ }
+ else if(kex_algo != Kex_Algo::PSK)
+ throw Decoding_Error("Server_Key_Exchange: Unsupported kex type " +
+ kex_method_to_string(kex_algo));
+
+ m_params.assign(buf.data(), buf.data() + reader.read_so_far());
+
+ if(auth_method != Auth_Method::ANONYMOUS && auth_method != Auth_Method::IMPLICIT)
+ {
+ if(version.supports_negotiable_signature_algorithms())
+ {
+ m_scheme = static_cast<Signature_Scheme>(reader.get_uint16_t());
+ }
+
+ m_signature = reader.get_range<uint8_t>(2, 0, 65535);
+ }
+
+ reader.assert_done();
+ }
+
+/**
+* Serialize a Server Key Exchange message
+*/
+std::vector<uint8_t> Server_Key_Exchange::serialize() const
+ {
+ std::vector<uint8_t> buf = params();
+
+ if(m_signature.size())
+ {
+ if(m_scheme != Signature_Scheme::NONE)
+ {
+ const uint16_t scheme_code = static_cast<uint16_t>(m_scheme);
+ buf.push_back(get_byte(0, scheme_code));
+ buf.push_back(get_byte(1, scheme_code));
+ }
+
+ append_tls_length_value(buf, m_signature, 2);
+ }
+
+ return buf;
+ }
+
+/**
+* Verify a Server Key Exchange message
+*/
+bool Server_Key_Exchange::verify(const Public_Key& server_key,
+ const Handshake_State& state,
+ const Policy& policy) const
+ {
+ policy.check_peer_key_acceptable(server_key);
+
+ std::pair<std::string, Signature_Format> format =
+ state.parse_sig_format(server_key, m_scheme, false, policy);
+
+ std::vector<uint8_t> buf = state.client_hello()->random();
+
+ buf += state.server_hello()->random();
+ buf += params();
+
+ const bool signature_valid =
+ state.callbacks().tls_verify_message(server_key, format.first, format.second,
+ buf, m_signature);
+
+#if defined(BOTAN_UNSAFE_FUZZER_MODE)
+ BOTAN_UNUSED(signature_valid);
+ return true;
+#else
+ return signature_valid;
+#endif
+ }
+
+const Private_Key& Server_Key_Exchange::server_kex_key() const
+ {
+ BOTAN_ASSERT_NONNULL(m_kex_key);
+ return *m_kex_key;
+ }
+
+}
+
+}
diff --git a/comm/third_party/botan/src/lib/tls/msg_session_ticket.cpp b/comm/third_party/botan/src/lib/tls/msg_session_ticket.cpp
new file mode 100644
index 0000000000..bd0d74ceb2
--- /dev/null
+++ b/comm/third_party/botan/src/lib/tls/msg_session_ticket.cpp
@@ -0,0 +1,56 @@
+/*
+* Session Tickets
+* (C) 2012 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/tls_messages.h>
+#include <botan/internal/tls_reader.h>
+#include <botan/internal/tls_handshake_io.h>
+#include <botan/internal/tls_handshake_hash.h>
+#include <botan/loadstor.h>
+
+namespace Botan {
+
+namespace TLS {
+
+New_Session_Ticket::New_Session_Ticket(Handshake_IO& io,
+ Handshake_Hash& hash,
+ const std::vector<uint8_t>& ticket,
+ uint32_t lifetime) :
+ m_ticket_lifetime_hint(lifetime),
+ m_ticket(ticket)
+ {
+ hash.update(io.send(*this));
+ }
+
+New_Session_Ticket::New_Session_Ticket(Handshake_IO& io,
+ Handshake_Hash& hash)
+ {
+ hash.update(io.send(*this));
+ }
+
+New_Session_Ticket::New_Session_Ticket(const std::vector<uint8_t>& 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 = reader.get_range<uint8_t>(2, 0, 65535);
+ reader.assert_done();
+ }
+
+std::vector<uint8_t> New_Session_Ticket::serialize() const
+ {
+ std::vector<uint8_t> buf(4);
+ store_be(m_ticket_lifetime_hint, buf.data());
+ append_tls_length_value(buf, m_ticket, 2);
+ return buf;
+ }
+
+}
+
+}
diff --git a/comm/third_party/botan/src/lib/tls/sessions_sql/info.txt b/comm/third_party/botan/src/lib/tls/sessions_sql/info.txt
new file mode 100644
index 0000000000..58392bd0c4
--- /dev/null
+++ b/comm/third_party/botan/src/lib/tls/sessions_sql/info.txt
@@ -0,0 +1,7 @@
+<defines>
+TLS_SESSION_MANAGER_SQL_DB -> 20141219
+</defines>
+
+<requires>
+pbkdf2
+</requires>
diff --git a/comm/third_party/botan/src/lib/tls/sessions_sql/tls_session_manager_sql.cpp b/comm/third_party/botan/src/lib/tls/sessions_sql/tls_session_manager_sql.cpp
new file mode 100644
index 0000000000..09cfbc4e99
--- /dev/null
+++ b/comm/third_party/botan/src/lib/tls/sessions_sql/tls_session_manager_sql.cpp
@@ -0,0 +1,213 @@
+/*
+* SQL TLS Session Manager
+* (C) 2012,2014 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/tls_session_manager_sql.h>
+#include <botan/database.h>
+#include <botan/pbkdf.h>
+#include <botan/hex.h>
+#include <botan/rng.h>
+#include <botan/loadstor.h>
+#include <chrono>
+
+namespace Botan {
+
+namespace TLS {
+
+Session_Manager_SQL::Session_Manager_SQL(std::shared_ptr<SQL_Database> db,
+ const std::string& passphrase,
+ RandomNumberGenerator& rng,
+ size_t max_sessions,
+ std::chrono::seconds session_lifetime) :
+ m_db(db),
+ m_rng(rng),
+ m_max_sessions(max_sessions),
+ m_session_lifetime(session_lifetime)
+ {
+ m_db->create_table(
+ "create table if not exists tls_sessions "
+ "("
+ "session_id TEXT PRIMARY KEY, "
+ "session_start INTEGER, "
+ "hostname TEXT, "
+ "hostport INTEGER, "
+ "session BLOB"
+ ")");
+
+ m_db->create_table(
+ "create table if not exists tls_sessions_metadata "
+ "("
+ "passphrase_salt BLOB, "
+ "passphrase_iterations INTEGER, "
+ "passphrase_check INTEGER "
+ ")");
+
+ const size_t salts = m_db->row_count("tls_sessions_metadata");
+
+ std::unique_ptr<PBKDF> pbkdf(get_pbkdf("PBKDF2(SHA-512)"));
+
+ if(salts == 1)
+ {
+ // existing db
+ auto stmt = m_db->new_statement("select * from tls_sessions_metadata");
+
+ if(stmt->step())
+ {
+ std::pair<const uint8_t*, size_t> 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);
+
+ secure_vector<uint8_t> x = pbkdf->pbkdf_iterations(32 + 2,
+ passphrase,
+ salt.first, salt.second,
+ iterations);
+
+ const size_t check_val_created = make_uint16(x[0], x[1]);
+ m_session_key.assign(x.begin() + 2, x.end());
+
+ if(check_val_created != check_val_db)
+ throw Invalid_Argument("Session database password not valid");
+ }
+ }
+ else
+ {
+ // maybe just zap the salts + sessions tables in this case?
+ if(salts != 0)
+ throw Internal_Error("Seemingly corrupted TLS session db, multiple salts found");
+
+ // new database case
+
+ std::vector<uint8_t> salt;
+ rng.random_vec(salt, 16);
+ size_t iterations = 0;
+
+ secure_vector<uint8_t> x = pbkdf->pbkdf_timed(32 + 2,
+ passphrase,
+ salt.data(), salt.size(),
+ std::chrono::milliseconds(100),
+ iterations);
+
+ size_t check_val = make_uint16(x[0], x[1]);
+ m_session_key.assign(x.begin() + 2, x.end());
+
+ auto stmt = m_db->new_statement("insert into tls_sessions_metadata values(?1, ?2, ?3)");
+
+ stmt->bind(1, salt);
+ stmt->bind(2, iterations);
+ stmt->bind(3, check_val);
+
+ stmt->spin();
+ }
+ }
+
+bool Session_Manager_SQL::load_from_session_id(const std::vector<uint8_t>& session_id,
+ Session& session)
+ {
+ auto stmt = m_db->new_statement("select session from tls_sessions where session_id = ?1");
+
+ stmt->bind(1, hex_encode(session_id));
+
+ while(stmt->step())
+ {
+ std::pair<const uint8_t*, size_t> blob = stmt->get_blob(0);
+
+ try
+ {
+ session = Session::decrypt(blob.first, blob.second, m_session_key);
+ return true;
+ }
+ catch(...)
+ {
+ }
+ }
+
+ return false;
+ }
+
+bool Session_Manager_SQL::load_from_server_info(const Server_Information& server,
+ Session& session)
+ {
+ auto stmt = m_db->new_statement("select session from tls_sessions"
+ " where hostname = ?1 and hostport = ?2"
+ " order by session_start desc");
+
+ stmt->bind(1, server.hostname());
+ stmt->bind(2, server.port());
+
+ while(stmt->step())
+ {
+ std::pair<const uint8_t*, size_t> blob = stmt->get_blob(0);
+
+ try
+ {
+ session = Session::decrypt(blob.first, blob.second, m_session_key);
+ return true;
+ }
+ catch(...)
+ {
+ }
+ }
+
+ return false;
+ }
+
+void Session_Manager_SQL::remove_entry(const std::vector<uint8_t>& session_id)
+ {
+ auto stmt = m_db->new_statement("delete from tls_sessions where session_id = ?1");
+
+ stmt->bind(1, hex_encode(session_id));
+
+ stmt->spin();
+ }
+
+size_t Session_Manager_SQL::remove_all()
+ {
+ auto stmt = m_db->new_statement("delete from tls_sessions");
+ return stmt->spin();
+ }
+
+void Session_Manager_SQL::save(const Session& session)
+ {
+ if(session.server_info().hostname().empty())
+ return;
+
+ auto stmt = m_db->new_statement("insert or replace into tls_sessions"
+ " values(?1, ?2, ?3, ?4, ?5)");
+
+ stmt->bind(1, hex_encode(session.session_id()));
+ stmt->bind(2, session.start_time());
+ stmt->bind(3, session.server_info().hostname());
+ stmt->bind(4, session.server_info().port());
+ stmt->bind(5, session.encrypt(m_session_key, m_rng));
+
+ stmt->spin();
+
+ prune_session_cache();
+ }
+
+void Session_Manager_SQL::prune_session_cache()
+ {
+ // First expire old sessions
+ auto remove_expired = m_db->new_statement("delete from tls_sessions where session_start <= ?1");
+ remove_expired->bind(1, std::chrono::system_clock::now() - m_session_lifetime);
+ remove_expired->spin();
+
+ const size_t sessions = m_db->row_count("tls_sessions");
+
+ // Then if needed expire some more sessions at random
+ if(sessions > m_max_sessions)
+ {
+ auto remove_some = m_db->new_statement("delete from tls_sessions where session_id in "
+ "(select session_id from tls_sessions limit ?1)");
+
+ remove_some->bind(1, sessions - m_max_sessions);
+ remove_some->spin();
+ }
+ }
+
+}
+
+}
diff --git a/comm/third_party/botan/src/lib/tls/sessions_sql/tls_session_manager_sql.h b/comm/third_party/botan/src/lib/tls/sessions_sql/tls_session_manager_sql.h
new file mode 100644
index 0000000000..d96a8b5bba
--- /dev/null
+++ b/comm/third_party/botan/src/lib/tls/sessions_sql/tls_session_manager_sql.h
@@ -0,0 +1,81 @@
+/*
+* TLS Session Manager storing to encrypted SQL db table
+* (C) 2012,2014 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_TLS_SQL_SESSION_MANAGER_H_
+#define BOTAN_TLS_SQL_SESSION_MANAGER_H_
+
+#include <botan/tls_session_manager.h>
+#include <botan/database.h>
+
+namespace Botan {
+
+class RandomNumberGenerator;
+
+namespace TLS {
+
+/**
+* An implementation of Session_Manager that saves values in a SQL
+* database file, with the session data encrypted using a passphrase.
+*
+* @warning For clients, the hostnames associated with the saved
+* sessions are stored in the database in plaintext. This may be a
+* serious privacy risk in some situations.
+*/
+class BOTAN_PUBLIC_API(2,0) Session_Manager_SQL : public Session_Manager
+ {
+ public:
+ /**
+ * @param db A connection to the database to use
+ The table names botan_tls_sessions and
+ botan_tls_sessions_metadata will be used
+ * @param passphrase used to encrypt the session data
+ * @param rng a random number generator
+ * @param max_sessions a hint on the maximum number of sessions
+ * to keep in memory at any one time. (If zero, don't cap)
+ * @param session_lifetime sessions are expired after this many
+ * seconds have elapsed from initial handshake.
+ */
+ Session_Manager_SQL(std::shared_ptr<SQL_Database> db,
+ const std::string& passphrase,
+ RandomNumberGenerator& rng,
+ size_t max_sessions = 1000,
+ std::chrono::seconds session_lifetime = std::chrono::seconds(7200));
+
+ Session_Manager_SQL(const Session_Manager_SQL&) = delete;
+
+ Session_Manager_SQL& operator=(const Session_Manager_SQL&) = delete;
+
+ bool load_from_session_id(const std::vector<uint8_t>& session_id,
+ Session& session) override;
+
+ bool load_from_server_info(const Server_Information& info,
+ Session& session) override;
+
+ void remove_entry(const std::vector<uint8_t>& session_id) override;
+
+ size_t remove_all() override;
+
+ void save(const Session& session_data) override;
+
+ std::chrono::seconds session_lifetime() const override
+ { return m_session_lifetime; }
+
+ private:
+ void prune_session_cache();
+
+ std::shared_ptr<SQL_Database> m_db;
+ secure_vector<uint8_t> m_session_key;
+ RandomNumberGenerator& m_rng;
+ size_t m_max_sessions;
+ std::chrono::seconds m_session_lifetime;
+ };
+
+}
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/tls/sessions_sqlite3/info.txt b/comm/third_party/botan/src/lib/tls/sessions_sqlite3/info.txt
new file mode 100644
index 0000000000..612142c094
--- /dev/null
+++ b/comm/third_party/botan/src/lib/tls/sessions_sqlite3/info.txt
@@ -0,0 +1,8 @@
+<defines>
+TLS_SQLITE3_SESSION_MANAGER -> 20131128
+</defines>
+
+<requires>
+sessions_sql
+sqlite3
+</requires>
diff --git a/comm/third_party/botan/src/lib/tls/sessions_sqlite3/tls_session_manager_sqlite.cpp b/comm/third_party/botan/src/lib/tls/sessions_sqlite3/tls_session_manager_sqlite.cpp
new file mode 100644
index 0000000000..aa163be8a7
--- /dev/null
+++ b/comm/third_party/botan/src/lib/tls/sessions_sqlite3/tls_session_manager_sqlite.cpp
@@ -0,0 +1,29 @@
+/*
+* SQLite TLS Session Manager
+* (C) 2012 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/tls_session_manager_sqlite.h>
+#include <botan/sqlite3.h>
+
+namespace Botan {
+
+namespace TLS {
+
+Session_Manager_SQLite::Session_Manager_SQLite(const std::string& passphrase,
+ RandomNumberGenerator& rng,
+ const std::string& db_filename,
+ size_t max_sessions,
+ std::chrono::seconds session_lifetime) :
+ Session_Manager_SQL(std::make_shared<Sqlite3_Database>(db_filename),
+ passphrase,
+ rng,
+ max_sessions,
+ session_lifetime)
+ {}
+
+}
+
+}
diff --git a/comm/third_party/botan/src/lib/tls/sessions_sqlite3/tls_session_manager_sqlite.h b/comm/third_party/botan/src/lib/tls/sessions_sqlite3/tls_session_manager_sqlite.h
new file mode 100644
index 0000000000..f906ae585e
--- /dev/null
+++ b/comm/third_party/botan/src/lib/tls/sessions_sqlite3/tls_session_manager_sqlite.h
@@ -0,0 +1,53 @@
+/*
+* SQLite3 TLS Session Manager
+* (C) 2012 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_TLS_SQLITE3_SESSION_MANAGER_H_
+#define BOTAN_TLS_SQLITE3_SESSION_MANAGER_H_
+
+#include <botan/tls_session_manager_sql.h>
+
+namespace Botan {
+
+class RandomNumberGenerator;
+
+namespace TLS {
+
+/**
+* An implementation of Session_Manager that saves values in a SQLite3
+* database file, with the session data encrypted using a passphrase.
+*
+* @warning For clients, the hostnames associated with the saved
+* sessions are stored in the database in plaintext. This may be a
+* serious privacy risk in some situations.
+*/
+class BOTAN_PUBLIC_API(2,0)
+Session_Manager_SQLite final : public Session_Manager_SQL
+ {
+ public:
+ /**
+ * @param passphrase used to encrypt the session data
+ * @param rng a random number generator
+ * @param db_filename filename of the SQLite database file.
+ The table names tls_sessions and tls_sessions_metadata
+ will be used
+ * @param max_sessions a hint on the maximum number of sessions
+ * to keep in memory at any one time. (If zero, don't cap)
+ * @param session_lifetime sessions are expired after this many
+ * seconds have elapsed from initial handshake.
+ */
+ Session_Manager_SQLite(const std::string& passphrase,
+ RandomNumberGenerator& rng,
+ const std::string& db_filename,
+ size_t max_sessions = 1000,
+ std::chrono::seconds session_lifetime = std::chrono::seconds(7200));
+};
+
+}
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/tls/tls_10/info.txt b/comm/third_party/botan/src/lib/tls/tls_10/info.txt
new file mode 100644
index 0000000000..f85a199921
--- /dev/null
+++ b/comm/third_party/botan/src/lib/tls/tls_10/info.txt
@@ -0,0 +1,10 @@
+<defines>
+TLS_V10 -> 20191109
+</defines>
+
+<requires>
+md5
+sha1
+par_hash
+tls_cbc
+</requires>
diff --git a/comm/third_party/botan/src/lib/tls/tls_alert.cpp b/comm/third_party/botan/src/lib/tls/tls_alert.cpp
new file mode 100644
index 0000000000..60c9c4b981
--- /dev/null
+++ b/comm/third_party/botan/src/lib/tls/tls_alert.cpp
@@ -0,0 +1,126 @@
+/*
+* Alert Message
+* (C) 2004-2006,2011 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/tls_alert.h>
+#include <botan/tls_exceptn.h>
+
+namespace Botan {
+
+namespace TLS {
+
+Alert::Alert(const secure_vector<uint8_t>& buf)
+ {
+ if(buf.size() != 2)
+ throw Decoding_Error("Bad size (" + std::to_string(buf.size()) +
+ ") for TLS alert message");
+
+ if(buf[0] == 1) m_fatal = false;
+ else if(buf[0] == 2) m_fatal = true;
+ else
+ throw TLS_Exception(Alert::ILLEGAL_PARAMETER, "Bad code for TLS alert level");
+
+ const uint8_t dc = buf[1];
+
+ m_type_code = static_cast<Type>(dc);
+ }
+
+std::vector<uint8_t> Alert::serialize() const
+ {
+ return std::vector<uint8_t>({
+ static_cast<uint8_t>(is_fatal() ? 2 : 1),
+ static_cast<uint8_t>(type())
+ });
+ }
+
+std::string Alert::type_string() const
+ {
+ switch(type())
+ {
+ case CLOSE_NOTIFY:
+ return "close_notify";
+ case UNEXPECTED_MESSAGE:
+ return "unexpected_message";
+ case BAD_RECORD_MAC:
+ return "bad_record_mac";
+ case DECRYPTION_FAILED:
+ return "decryption_failed";
+ case RECORD_OVERFLOW:
+ return "record_overflow";
+ case DECOMPRESSION_FAILURE:
+ return "decompression_failure";
+ case HANDSHAKE_FAILURE:
+ return "handshake_failure";
+ case NO_CERTIFICATE:
+ return "no_certificate";
+ case BAD_CERTIFICATE:
+ return "bad_certificate";
+ case UNSUPPORTED_CERTIFICATE:
+ return "unsupported_certificate";
+ case CERTIFICATE_REVOKED:
+ return "certificate_revoked";
+ case CERTIFICATE_EXPIRED:
+ return "certificate_expired";
+ case CERTIFICATE_UNKNOWN:
+ return "certificate_unknown";
+ case ILLEGAL_PARAMETER:
+ return "illegal_parameter";
+ case UNKNOWN_CA:
+ return "unknown_ca";
+ case ACCESS_DENIED:
+ return "access_denied";
+ case DECODE_ERROR:
+ return "decode_error";
+ case DECRYPT_ERROR:
+ return "decrypt_error";
+ case EXPORT_RESTRICTION:
+ return "export_restriction";
+ case PROTOCOL_VERSION:
+ return "protocol_version";
+ case INSUFFICIENT_SECURITY:
+ return "insufficient_security";
+ case INTERNAL_ERROR:
+ return "internal_error";
+ case INAPPROPRIATE_FALLBACK:
+ return "inappropriate_fallback";
+ case USER_CANCELED:
+ return "user_canceled";
+ case NO_RENEGOTIATION:
+ return "no_renegotiation";
+
+ case UNSUPPORTED_EXTENSION:
+ return "unsupported_extension";
+ case CERTIFICATE_UNOBTAINABLE:
+ return "certificate_unobtainable";
+ case UNRECOGNIZED_NAME:
+ return "unrecognized_name";
+ case BAD_CERTIFICATE_STATUS_RESPONSE:
+ return "bad_certificate_status_response";
+ case BAD_CERTIFICATE_HASH_VALUE:
+ return "bad_certificate_hash_value";
+ case UNKNOWN_PSK_IDENTITY:
+ return "unknown_psk_identity";
+ case CERTIFICATE_REQUIRED:
+ return "certificate_required";
+ case NO_APPLICATION_PROTOCOL:
+ return "no_application_protocol";
+
+ case NULL_ALERT:
+ return "none";
+ }
+
+ /*
+ * This is effectively the default case for the switch above, but we
+ * leave it out so that when an alert type is added to the enum the
+ * compiler can warn us that it is not included in the switch
+ * statement.
+ */
+ return "unrecognized_alert_" + std::to_string(type());
+ }
+
+}
+
+}
diff --git a/comm/third_party/botan/src/lib/tls/tls_alert.h b/comm/third_party/botan/src/lib/tls/tls_alert.h
new file mode 100644
index 0000000000..d9d3fe3136
--- /dev/null
+++ b/comm/third_party/botan/src/lib/tls/tls_alert.h
@@ -0,0 +1,116 @@
+/*
+* Alert Message
+* (C) 2004-2006,2011,2012 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_TLS_ALERT_H_
+#define BOTAN_TLS_ALERT_H_
+
+#include <botan/secmem.h>
+#include <string>
+
+namespace Botan {
+
+namespace TLS {
+
+/**
+* SSL/TLS Alert Message
+*/
+class BOTAN_PUBLIC_API(2,0) Alert final
+ {
+ public:
+ /**
+ * Type codes for TLS alerts
+ */
+ enum Type {
+ CLOSE_NOTIFY = 0,
+ UNEXPECTED_MESSAGE = 10,
+ BAD_RECORD_MAC = 20,
+ DECRYPTION_FAILED = 21,
+ RECORD_OVERFLOW = 22,
+ DECOMPRESSION_FAILURE = 30,
+ HANDSHAKE_FAILURE = 40,
+ NO_CERTIFICATE = 41, // SSLv3 only
+ BAD_CERTIFICATE = 42,
+ UNSUPPORTED_CERTIFICATE = 43,
+ CERTIFICATE_REVOKED = 44,
+ CERTIFICATE_EXPIRED = 45,
+ CERTIFICATE_UNKNOWN = 46,
+ ILLEGAL_PARAMETER = 47,
+ UNKNOWN_CA = 48,
+ ACCESS_DENIED = 49,
+ DECODE_ERROR = 50,
+ DECRYPT_ERROR = 51,
+ EXPORT_RESTRICTION = 60,
+ PROTOCOL_VERSION = 70,
+ INSUFFICIENT_SECURITY = 71,
+ INTERNAL_ERROR = 80,
+ INAPPROPRIATE_FALLBACK = 86,
+ USER_CANCELED = 90,
+ NO_RENEGOTIATION = 100,
+ UNSUPPORTED_EXTENSION = 110,
+ CERTIFICATE_UNOBTAINABLE = 111,
+ UNRECOGNIZED_NAME = 112,
+ BAD_CERTIFICATE_STATUS_RESPONSE = 113,
+ BAD_CERTIFICATE_HASH_VALUE = 114,
+ UNKNOWN_PSK_IDENTITY = 115,
+ CERTIFICATE_REQUIRED = 116, // RFC 8446
+
+ NO_APPLICATION_PROTOCOL = 120, // RFC 7301
+
+ // pseudo alert values
+ NULL_ALERT = 256
+ };
+
+ /**
+ * @return true iff this alert is non-empty
+ */
+ bool is_valid() const { return (m_type_code != NULL_ALERT); }
+
+ /**
+ * @return if this alert is a fatal one or not
+ */
+ bool is_fatal() const { return m_fatal; }
+
+ /**
+ * @return type of alert
+ */
+ Type type() const { return m_type_code; }
+
+ /**
+ * @return type of alert
+ */
+ std::string type_string() const;
+
+ /**
+ * Serialize an alert
+ */
+ std::vector<uint8_t> serialize() const;
+
+ /**
+ * Deserialize an Alert message
+ * @param buf the serialized alert
+ */
+ explicit Alert(const secure_vector<uint8_t>& buf);
+
+ /**
+ * Create a new Alert
+ * @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() : m_fatal(false), m_type_code(NULL_ALERT) {}
+ private:
+ bool m_fatal;
+ Type m_type_code;
+ };
+
+}
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/tls/tls_algos.cpp b/comm/third_party/botan/src/lib/tls/tls_algos.cpp
new file mode 100644
index 0000000000..9501cfdfb7
--- /dev/null
+++ b/comm/third_party/botan/src/lib/tls/tls_algos.cpp
@@ -0,0 +1,426 @@
+/*
+* (C) 2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/tls_algos.h>
+#include <botan/exceptn.h>
+
+namespace Botan {
+
+namespace TLS {
+
+std::string kdf_algo_to_string(KDF_Algo algo)
+ {
+ switch(algo)
+ {
+ case KDF_Algo::SHA_1:
+ return "SHA-1";
+ case KDF_Algo::SHA_256:
+ return "SHA-256";
+ case KDF_Algo::SHA_384:
+ return "SHA-384";
+ }
+
+ throw Invalid_State("kdf_algo_to_string unknown enum value");
+ }
+
+std::string kex_method_to_string(Kex_Algo method)
+ {
+ switch(method)
+ {
+ case Kex_Algo::STATIC_RSA:
+ return "RSA";
+ case Kex_Algo::DH:
+ return "DH";
+ case Kex_Algo::ECDH:
+ return "ECDH";
+ case Kex_Algo::CECPQ1:
+ return "CECPQ1";
+ case Kex_Algo::SRP_SHA:
+ return "SRP_SHA";
+ case Kex_Algo::PSK:
+ return "PSK";
+ case Kex_Algo::DHE_PSK:
+ return "DHE_PSK";
+ case Kex_Algo::ECDHE_PSK:
+ return "ECDHE_PSK";
+ }
+
+ throw Invalid_State("kex_method_to_string unknown enum value");
+ }
+
+Kex_Algo kex_method_from_string(const std::string& str)
+ {
+ if(str == "RSA")
+ return Kex_Algo::STATIC_RSA;
+
+ if(str == "DH")
+ return Kex_Algo::DH;
+
+ if(str == "ECDH")
+ return Kex_Algo::ECDH;
+
+ if(str == "CECPQ1")
+ return Kex_Algo::CECPQ1;
+
+ if(str == "SRP_SHA")
+ return Kex_Algo::SRP_SHA;
+
+ if(str == "PSK")
+ return Kex_Algo::PSK;
+
+ if(str == "DHE_PSK")
+ return Kex_Algo::DHE_PSK;
+
+ if(str == "ECDHE_PSK")
+ return Kex_Algo::ECDHE_PSK;
+
+ throw Invalid_Argument("Unknown kex method " + str);
+ }
+
+std::string auth_method_to_string(Auth_Method method)
+ {
+ switch(method)
+ {
+ case Auth_Method::RSA:
+ return "RSA";
+ case Auth_Method::DSA:
+ return "DSA";
+ case Auth_Method::ECDSA:
+ return "ECDSA";
+ case Auth_Method::IMPLICIT:
+ return "IMPLICIT";
+ case Auth_Method::ANONYMOUS:
+ return "ANONYMOUS";
+ }
+
+ throw Invalid_State("auth_method_to_string unknown enum value");
+ }
+
+Auth_Method auth_method_from_string(const std::string& str)
+ {
+ if(str == "RSA")
+ return Auth_Method::RSA;
+ if(str == "DSA")
+ return Auth_Method::DSA;
+ if(str == "ECDSA")
+ return Auth_Method::ECDSA;
+ if(str == "IMPLICIT")
+ return Auth_Method::IMPLICIT;
+ if(str == "ANONYMOUS" || str == "")
+ return Auth_Method::ANONYMOUS;
+
+ throw Invalid_Argument("Bad signature method " + str);
+ }
+
+bool group_param_is_dh(Group_Params group)
+ {
+ uint16_t group_id = static_cast<uint16_t>(group);
+ return (group_id >= 256 && group_id < 512);
+ }
+
+Group_Params group_param_from_string(const std::string& group_name)
+ {
+ if(group_name == "secp256r1")
+ return Group_Params::SECP256R1;
+ if(group_name == "secp384r1")
+ return Group_Params::SECP384R1;
+ if(group_name == "secp521r1")
+ return Group_Params::SECP521R1;
+ if(group_name == "brainpool256r1")
+ return Group_Params::BRAINPOOL256R1;
+ if(group_name == "brainpool384r1")
+ return Group_Params::BRAINPOOL384R1;
+ if(group_name == "brainpool512r1")
+ return Group_Params::BRAINPOOL512R1;
+ if(group_name == "x25519")
+ return Group_Params::X25519;
+
+ if(group_name == "ffdhe/ietf/2048")
+ return Group_Params::FFDHE_2048;
+ if(group_name == "ffdhe/ietf/3072")
+ return Group_Params::FFDHE_3072;
+ if(group_name == "ffdhe/ietf/4096")
+ return Group_Params::FFDHE_4096;
+ if(group_name == "ffdhe/ietf/6144")
+ return Group_Params::FFDHE_6144;
+ if(group_name == "ffdhe/ietf/8192")
+ return Group_Params::FFDHE_8192;
+
+ return Group_Params::NONE; // unknown
+ }
+
+std::string group_param_to_string(Group_Params group)
+ {
+ switch(group)
+ {
+ case Group_Params::SECP256R1:
+ return "secp256r1";
+ case Group_Params::SECP384R1:
+ return "secp384r1";
+ case Group_Params::SECP521R1:
+ return "secp521r1";
+ case Group_Params::BRAINPOOL256R1:
+ return "brainpool256r1";
+ case Group_Params::BRAINPOOL384R1:
+ return "brainpool384r1";
+ case Group_Params::BRAINPOOL512R1:
+ return "brainpool512r1";
+ case Group_Params::X25519:
+ return "x25519";
+
+ case Group_Params::FFDHE_2048:
+ return "ffdhe/ietf/2048";
+ case Group_Params::FFDHE_3072:
+ return "ffdhe/ietf/3072";
+ case Group_Params::FFDHE_4096:
+ return "ffdhe/ietf/4096";
+ case Group_Params::FFDHE_6144:
+ return "ffdhe/ietf/6144";
+ case Group_Params::FFDHE_8192:
+ return "ffdhe/ietf/8192";
+
+ default:
+ return "";
+ }
+ }
+
+
+std::string hash_function_of_scheme(Signature_Scheme scheme)
+ {
+ switch(scheme)
+ {
+ case Signature_Scheme::DSA_SHA1:
+ case Signature_Scheme::ECDSA_SHA1:
+ case Signature_Scheme::RSA_PKCS1_SHA1:
+ return "SHA-1";
+
+ case Signature_Scheme::DSA_SHA256:
+ case Signature_Scheme::ECDSA_SHA256:
+ case Signature_Scheme::RSA_PKCS1_SHA256:
+ case Signature_Scheme::RSA_PSS_SHA256:
+ return "SHA-256";
+
+ case Signature_Scheme::DSA_SHA384:
+ case Signature_Scheme::ECDSA_SHA384:
+ case Signature_Scheme::RSA_PKCS1_SHA384:
+ case Signature_Scheme::RSA_PSS_SHA384:
+ return "SHA-384";
+
+ case Signature_Scheme::DSA_SHA512:
+ case Signature_Scheme::ECDSA_SHA512:
+ case Signature_Scheme::RSA_PKCS1_SHA512:
+ case Signature_Scheme::RSA_PSS_SHA512:
+ return "SHA-512";
+
+ case Signature_Scheme::EDDSA_25519:
+ case Signature_Scheme::EDDSA_448:
+ return "Pure";
+
+ case Signature_Scheme::NONE:
+ return "";
+ }
+
+ throw Invalid_State("hash_function_of_scheme: Unknown signature algorithm enum");
+ }
+
+const std::vector<Signature_Scheme>& all_signature_schemes()
+ {
+ /*
+ * This is ordered in some approximate order of preference
+ */
+ static const std::vector<Signature_Scheme> all_schemes = {
+ //Signature_Scheme::EDDSA_448,
+ //Signature_Scheme::EDDSA_25519,
+
+ Signature_Scheme::RSA_PSS_SHA384,
+ Signature_Scheme::RSA_PSS_SHA256,
+ Signature_Scheme::RSA_PSS_SHA512,
+
+ Signature_Scheme::RSA_PKCS1_SHA384,
+ Signature_Scheme::RSA_PKCS1_SHA512,
+ Signature_Scheme::RSA_PKCS1_SHA256,
+
+ Signature_Scheme::ECDSA_SHA384,
+ Signature_Scheme::ECDSA_SHA512,
+ Signature_Scheme::ECDSA_SHA256,
+
+ Signature_Scheme::DSA_SHA384,
+ Signature_Scheme::DSA_SHA512,
+ Signature_Scheme::DSA_SHA256,
+
+ Signature_Scheme::RSA_PKCS1_SHA1,
+ Signature_Scheme::ECDSA_SHA1,
+ Signature_Scheme::DSA_SHA1,
+ };
+
+ return all_schemes;
+ }
+
+bool signature_scheme_is_known(Signature_Scheme scheme)
+ {
+ switch(scheme)
+ {
+ case Signature_Scheme::RSA_PKCS1_SHA1:
+ case Signature_Scheme::RSA_PKCS1_SHA256:
+ case Signature_Scheme::RSA_PKCS1_SHA384:
+ case Signature_Scheme::RSA_PKCS1_SHA512:
+ case Signature_Scheme::RSA_PSS_SHA256:
+ case Signature_Scheme::RSA_PSS_SHA384:
+ case Signature_Scheme::RSA_PSS_SHA512:
+
+ case Signature_Scheme::DSA_SHA1:
+ case Signature_Scheme::DSA_SHA256:
+ case Signature_Scheme::DSA_SHA384:
+ case Signature_Scheme::DSA_SHA512:
+
+ case Signature_Scheme::ECDSA_SHA1:
+ case Signature_Scheme::ECDSA_SHA256:
+ case Signature_Scheme::ECDSA_SHA384:
+ case Signature_Scheme::ECDSA_SHA512:
+ return true;
+
+ default:
+ return false;
+ }
+
+ }
+
+std::string signature_algorithm_of_scheme(Signature_Scheme scheme)
+ {
+ switch(scheme)
+ {
+ case Signature_Scheme::RSA_PKCS1_SHA1:
+ case Signature_Scheme::RSA_PKCS1_SHA256:
+ case Signature_Scheme::RSA_PKCS1_SHA384:
+ case Signature_Scheme::RSA_PKCS1_SHA512:
+ case Signature_Scheme::RSA_PSS_SHA256:
+ case Signature_Scheme::RSA_PSS_SHA384:
+ case Signature_Scheme::RSA_PSS_SHA512:
+ return "RSA";
+
+ case Signature_Scheme::DSA_SHA1:
+ case Signature_Scheme::DSA_SHA256:
+ case Signature_Scheme::DSA_SHA384:
+ case Signature_Scheme::DSA_SHA512:
+ return "DSA";
+
+ case Signature_Scheme::ECDSA_SHA1:
+ case Signature_Scheme::ECDSA_SHA256:
+ case Signature_Scheme::ECDSA_SHA384:
+ case Signature_Scheme::ECDSA_SHA512:
+ return "ECDSA";
+
+ case Signature_Scheme::EDDSA_25519:
+ return "Ed25519";
+
+ case Signature_Scheme::EDDSA_448:
+ return "Ed448";
+
+ case Signature_Scheme::NONE:
+ return "";
+ }
+
+ throw Invalid_State("signature_algorithm_of_scheme: Unknown signature algorithm enum");
+ }
+
+std::string sig_scheme_to_string(Signature_Scheme scheme)
+ {
+ switch(scheme)
+ {
+ case Signature_Scheme::RSA_PKCS1_SHA1:
+ return "RSA_PKCS1_SHA1";
+ case Signature_Scheme::RSA_PKCS1_SHA256:
+ return "RSA_PKCS1_SHA256";
+ case Signature_Scheme::RSA_PKCS1_SHA384:
+ return "RSA_PKCS1_SHA384";
+ case Signature_Scheme::RSA_PKCS1_SHA512:
+ return "RSA_PKCS1_SHA512";
+
+ case Signature_Scheme::DSA_SHA1:
+ return "DSA_SHA1";
+ case Signature_Scheme::DSA_SHA256:
+ return "DSA_SHA256";
+ case Signature_Scheme::DSA_SHA384:
+ return "DSA_SHA384";
+ case Signature_Scheme::DSA_SHA512:
+ return "DSA_SHA512";
+
+ case Signature_Scheme::ECDSA_SHA1:
+ return "ECDSA_SHA1";
+ case Signature_Scheme::ECDSA_SHA256:
+ return "ECDSA_SHA256";
+ case Signature_Scheme::ECDSA_SHA384:
+ return "ECDSA_SHA384";
+ case Signature_Scheme::ECDSA_SHA512:
+ return "ECDSA_SHA512";
+
+ case Signature_Scheme::RSA_PSS_SHA256:
+ return "RSA_PSS_SHA256";
+ case Signature_Scheme::RSA_PSS_SHA384:
+ return "RSA_PSS_SHA384";
+ case Signature_Scheme::RSA_PSS_SHA512:
+ return "RSA_PSS_SHA512";
+
+ case Signature_Scheme::EDDSA_25519:
+ return "EDDSA_25519";
+ case Signature_Scheme::EDDSA_448:
+ return "EDDSA_448";
+
+ case Signature_Scheme::NONE:
+ return "";
+ }
+
+ throw Invalid_State("sig_scheme_to_string: Unknown signature algorithm enum");
+ }
+
+std::string padding_string_for_scheme(Signature_Scheme scheme)
+ {
+ switch(scheme)
+ {
+ case Signature_Scheme::RSA_PKCS1_SHA1:
+ return "EMSA_PKCS1(SHA-1)";
+ case Signature_Scheme::RSA_PKCS1_SHA256:
+ return "EMSA_PKCS1(SHA-256)";
+ case Signature_Scheme::RSA_PKCS1_SHA384:
+ return "EMSA_PKCS1(SHA-384)";
+ case Signature_Scheme::RSA_PKCS1_SHA512:
+ return "EMSA_PKCS1(SHA-512)";
+
+ case Signature_Scheme::DSA_SHA1:
+ case Signature_Scheme::ECDSA_SHA1:
+ return "EMSA1(SHA-1)";
+ case Signature_Scheme::DSA_SHA256:
+ case Signature_Scheme::ECDSA_SHA256:
+ return "EMSA1(SHA-256)";
+ case Signature_Scheme::DSA_SHA384:
+ case Signature_Scheme::ECDSA_SHA384:
+ return "EMSA1(SHA-384)";
+ case Signature_Scheme::DSA_SHA512:
+ case Signature_Scheme::ECDSA_SHA512:
+ return "EMSA1(SHA-512)";
+
+ case Signature_Scheme::RSA_PSS_SHA256:
+ return "PSSR(SHA-256,MGF1,32)";
+ case Signature_Scheme::RSA_PSS_SHA384:
+ return "PSSR(SHA-384,MGF1,48)";
+ case Signature_Scheme::RSA_PSS_SHA512:
+ return "PSSR(SHA-512,MGF1,64)";
+
+ case Signature_Scheme::EDDSA_25519:
+ return "Pure";
+ case Signature_Scheme::EDDSA_448:
+ return "Pure";
+
+ case Signature_Scheme::NONE:
+ return "";
+ }
+
+ throw Invalid_State("padding_string_for_scheme: Unknown signature algorithm enum");
+ }
+
+}
+
+}
diff --git a/comm/third_party/botan/src/lib/tls/tls_algos.h b/comm/third_party/botan/src/lib/tls/tls_algos.h
new file mode 100644
index 0000000000..3b0be81286
--- /dev/null
+++ b/comm/third_party/botan/src/lib/tls/tls_algos.h
@@ -0,0 +1,171 @@
+/*
+* (C) 2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_TLS_ALGO_IDS_H_
+#define BOTAN_TLS_ALGO_IDS_H_
+
+#include <botan/types.h>
+#include <string>
+#include <vector>
+
+//BOTAN_FUTURE_INTERNAL_HEADER(tls_algos.h)
+
+namespace Botan {
+
+namespace TLS {
+
+enum class Cipher_Algo {
+ CHACHA20_POLY1305,
+
+ AES_128_CBC_HMAC_SHA1 = 100,
+ AES_128_CBC_HMAC_SHA256,
+ AES_128_CCM,
+ AES_128_CCM_8,
+ AES_128_GCM,
+ AES_128_OCB,
+
+ AES_256_CBC_HMAC_SHA1 = 200,
+ AES_256_CBC_HMAC_SHA256,
+ AES_256_CBC_HMAC_SHA384,
+ AES_256_CCM,
+ AES_256_CCM_8,
+ AES_256_GCM,
+ AES_256_OCB,
+
+ CAMELLIA_128_CBC_HMAC_SHA1 = 300,
+ CAMELLIA_128_CBC_HMAC_SHA256,
+ CAMELLIA_128_GCM,
+
+ CAMELLIA_256_CBC_HMAC_SHA1 = 400,
+ CAMELLIA_256_CBC_HMAC_SHA256,
+ CAMELLIA_256_CBC_HMAC_SHA384,
+ CAMELLIA_256_GCM,
+
+ ARIA_128_GCM = 500,
+ ARIA_256_GCM,
+
+ DES_EDE_CBC_HMAC_SHA1 = 1000,
+ SEED_CBC_HMAC_SHA1,
+};
+
+enum class KDF_Algo {
+ SHA_1,
+ SHA_256,
+ SHA_384,
+};
+
+std::string BOTAN_DLL kdf_algo_to_string(KDF_Algo algo);
+
+enum class Nonce_Format {
+ CBC_MODE,
+ AEAD_IMPLICIT_4,
+ AEAD_XOR_12,
+};
+
+// TODO encoding should match signature_algorithms extension
+// TODO this should include hash etc as in TLS v1.3
+enum class Auth_Method {
+ RSA,
+ DSA,
+ ECDSA,
+
+ // These are placed outside the encodable range
+ IMPLICIT = 0x10000,
+ ANONYMOUS
+};
+
+std::string BOTAN_TEST_API auth_method_to_string(Auth_Method method);
+Auth_Method BOTAN_TEST_API auth_method_from_string(const std::string& str);
+
+/*
+* This matches the wire encoding
+*/
+enum class Signature_Scheme : uint16_t {
+ NONE = 0x0000,
+
+ RSA_PKCS1_SHA1 = 0x0201,
+ RSA_PKCS1_SHA256 = 0x0401,
+ RSA_PKCS1_SHA384 = 0x0501,
+ RSA_PKCS1_SHA512 = 0x0601,
+
+ DSA_SHA1 = 0x0202,
+ DSA_SHA256 = 0x0402,
+ DSA_SHA384 = 0x0502,
+ DSA_SHA512 = 0x0602,
+
+ ECDSA_SHA1 = 0x0203,
+ ECDSA_SHA256 = 0x0403,
+ ECDSA_SHA384 = 0x0503,
+ ECDSA_SHA512 = 0x0603,
+
+ RSA_PSS_SHA256 = 0x0804,
+ RSA_PSS_SHA384 = 0x0805,
+ RSA_PSS_SHA512 = 0x0806,
+
+ EDDSA_25519 = 0x0807,
+ EDDSA_448 = 0x0808,
+};
+
+BOTAN_UNSTABLE_API const std::vector<Signature_Scheme>& all_signature_schemes();
+
+bool BOTAN_UNSTABLE_API signature_scheme_is_known(Signature_Scheme scheme);
+std::string BOTAN_UNSTABLE_API sig_scheme_to_string(Signature_Scheme scheme);
+std::string BOTAN_UNSTABLE_API hash_function_of_scheme(Signature_Scheme scheme);
+std::string BOTAN_UNSTABLE_API padding_string_for_scheme(Signature_Scheme scheme);
+std::string signature_algorithm_of_scheme(Signature_Scheme scheme);
+
+/*
+* Matches with wire encoding
+*/
+enum class Group_Params : uint16_t {
+ NONE = 0,
+
+ SECP256R1 = 23,
+ SECP384R1 = 24,
+ SECP521R1 = 25,
+ BRAINPOOL256R1 = 26,
+ BRAINPOOL384R1 = 27,
+ BRAINPOOL512R1 = 28,
+
+ X25519 = 29,
+
+ FFDHE_2048 = 256,
+ FFDHE_3072 = 257,
+ FFDHE_4096 = 258,
+ FFDHE_6144 = 259,
+ FFDHE_8192 = 260,
+};
+
+std::string group_param_to_string(Group_Params group);
+Group_Params group_param_from_string(const std::string& group_name);
+bool group_param_is_dh(Group_Params group);
+
+enum class Kex_Algo {
+ STATIC_RSA,
+ DH,
+ ECDH,
+ CECPQ1,
+ SRP_SHA,
+ PSK,
+ DHE_PSK,
+ ECDHE_PSK,
+};
+
+std::string BOTAN_TEST_API kex_method_to_string(Kex_Algo method);
+Kex_Algo BOTAN_TEST_API kex_method_from_string(const std::string& str);
+
+inline bool key_exchange_is_psk(Kex_Algo m)
+ {
+ return (m == Kex_Algo::PSK ||
+ m == Kex_Algo::DHE_PSK ||
+ m == Kex_Algo::ECDHE_PSK);
+ }
+
+}
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/tls/tls_blocking.cpp b/comm/third_party/botan/src/lib/tls/tls_blocking.cpp
new file mode 100644
index 0000000000..b9c699dc03
--- /dev/null
+++ b/comm/third_party/botan/src/lib/tls/tls_blocking.cpp
@@ -0,0 +1,97 @@
+/*
+* TLS Blocking API
+* (C) 2013 Jack Lloyd
+* 2016 Matthias Gierlings
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/tls_blocking.h>
+
+namespace Botan {
+
+namespace TLS {
+
+Blocking_Client::Blocking_Client(read_fn reader,
+ write_fn writer,
+ Session_Manager& session_manager,
+ Credentials_Manager& creds,
+ const Policy& policy,
+ RandomNumberGenerator& rng,
+ const Server_Information& server_info,
+ const Protocol_Version& offer_version,
+ const std::vector<std::string>& next) :
+ m_read(reader),
+ m_callbacks(new TLS::Compat_Callbacks(
+ /*
+ we are ok using deprecated features here because the whole Blocking_Client class
+ is also deprecated, so just silence the warning.
+ */
+ TLS::Compat_Callbacks::SILENCE_DEPRECATION_WARNING::PLEASE,
+ writer,
+ std::bind(&Blocking_Client::data_cb, this, std::placeholders::_1, std::placeholders::_2),
+ std::function<void (Alert)>(std::bind(&Blocking_Client::alert_cb, this, std::placeholders::_1)),
+ std::bind(&Blocking_Client::handshake_cb, this, std::placeholders::_1)
+ )),
+ m_channel(*m_callbacks.get(),
+ session_manager,
+ creds,
+ policy,
+ rng,
+ server_info,
+ offer_version,
+ next)
+ {
+ }
+
+bool Blocking_Client::handshake_cb(const Session& session)
+ {
+ return this->handshake_complete(session);
+ }
+
+void Blocking_Client::alert_cb(const Alert& alert)
+ {
+ this->alert_notification(alert);
+ }
+
+void Blocking_Client::data_cb(const uint8_t data[], size_t data_len)
+ {
+ m_plaintext.insert(m_plaintext.end(), data, data + data_len);
+ }
+
+void Blocking_Client::do_handshake()
+ {
+ std::vector<uint8_t> readbuf(4096);
+
+ while(!m_channel.is_closed() && !m_channel.is_active())
+ {
+ const size_t from_socket = m_read(readbuf.data(), readbuf.size());
+ m_channel.received_data(readbuf.data(), from_socket);
+ }
+ }
+
+size_t Blocking_Client::read(uint8_t buf[], size_t buf_len)
+ {
+ std::vector<uint8_t> readbuf(4096);
+
+ while(m_plaintext.empty() && !m_channel.is_closed())
+ {
+ const size_t from_socket = m_read(readbuf.data(), readbuf.size());
+ m_channel.received_data(readbuf.data(), from_socket);
+ }
+
+ const size_t returned = std::min(buf_len, m_plaintext.size());
+
+ for(size_t i = 0; i != returned; ++i)
+ buf[i] = m_plaintext[i];
+ m_plaintext.erase(m_plaintext.begin(), m_plaintext.begin() + returned);
+
+ BOTAN_ASSERT_IMPLICATION(returned == 0, m_channel.is_closed(),
+ "Only return zero if channel is closed");
+
+ return returned;
+ }
+
+}
+
+}
diff --git a/comm/third_party/botan/src/lib/tls/tls_blocking.h b/comm/third_party/botan/src/lib/tls/tls_blocking.h
new file mode 100644
index 0000000000..01620c652c
--- /dev/null
+++ b/comm/third_party/botan/src/lib/tls/tls_blocking.h
@@ -0,0 +1,103 @@
+/*
+* TLS Blocking API
+* (C) 2013 Jack Lloyd
+* 2016 Matthias Gierlings
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_TLS_BLOCKING_CHANNELS_H_
+#define BOTAN_TLS_BLOCKING_CHANNELS_H_
+
+#include <botan/tls_client.h>
+
+namespace Botan {
+
+namespace TLS {
+
+/**
+* Blocking TLS Client
+* Can be used directly, or subclass to get handshake and alert notifications
+*/
+class BOTAN_PUBLIC_API(2,0) Blocking_Client
+ {
+ public:
+ /*
+ * These functions are expected to block until completing entirely, or
+ * fail by throwing an exception.
+ */
+ typedef std::function<size_t (uint8_t[], size_t)> read_fn;
+ typedef std::function<void (const uint8_t[], size_t)> write_fn;
+
+ BOTAN_DEPRECATED("Use the regular TLS::Client interface")
+ Blocking_Client(read_fn reader,
+ write_fn writer,
+ Session_Manager& session_manager,
+ Credentials_Manager& creds,
+ const Policy& policy,
+ RandomNumberGenerator& rng,
+ const Server_Information& server_info = Server_Information(),
+ const Protocol_Version& offer_version = Protocol_Version::latest_tls_version(),
+ const std::vector<std::string>& next_protos = {});
+
+ /**
+ * Completes full handshake then returns
+ */
+ void do_handshake();
+
+ /**
+ * Number of bytes pending read in the plaintext buffer (bytes
+ * readable without blocking)
+ */
+ size_t pending() const { return m_plaintext.size(); }
+
+ /**
+ * Blocking read, will return at least 1 byte (eventually) or else 0 if the connection
+ * is closed.
+ */
+ size_t read(uint8_t buf[], size_t buf_len);
+
+ void write(const uint8_t buf[], size_t buf_len) { m_channel.send(buf, buf_len); }
+
+ const TLS::Channel& underlying_channel() const { return m_channel; }
+ TLS::Channel& underlying_channel() { return m_channel; }
+
+ void close() { m_channel.close(); }
+
+ bool is_closed() const { return m_channel.is_closed(); }
+
+ std::vector<X509_Certificate> peer_cert_chain() const
+ { return m_channel.peer_cert_chain(); }
+
+ virtual ~Blocking_Client() = default;
+
+ protected:
+ /**
+ * Application can override to get the handshake complete notification
+ */
+ virtual bool handshake_complete(const Session&) { return true; }
+
+ /**
+ * Application can override to get notification of alerts
+ */
+ virtual void alert_notification(const Alert&) {}
+
+ private:
+
+ bool handshake_cb(const Session&);
+
+ void data_cb(const uint8_t data[], size_t data_len);
+
+ void alert_cb(const Alert& alert);
+
+ read_fn m_read;
+ std::unique_ptr<Compat_Callbacks> m_callbacks;
+ TLS::Client m_channel;
+ secure_vector<uint8_t> m_plaintext;
+ };
+
+}
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/tls/tls_callbacks.cpp b/comm/third_party/botan/src/lib/tls/tls_callbacks.cpp
new file mode 100644
index 0000000000..0dd758b757
--- /dev/null
+++ b/comm/third_party/botan/src/lib/tls/tls_callbacks.cpp
@@ -0,0 +1,191 @@
+/*
+* TLS Callbacks
+* (C) 2016 Jack Lloyd
+* 2017 Harry Reimann, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/tls_callbacks.h>
+#include <botan/tls_policy.h>
+#include <botan/tls_algos.h>
+#include <botan/x509path.h>
+#include <botan/ocsp.h>
+#include <botan/dh.h>
+#include <botan/ecdh.h>
+#include <botan/tls_exceptn.h>
+#include <botan/internal/ct_utils.h>
+
+#if defined(BOTAN_HAS_CURVE_25519)
+ #include <botan/curve25519.h>
+#endif
+
+namespace Botan {
+
+void TLS::Callbacks::tls_inspect_handshake_msg(const Handshake_Message&)
+ {
+ // default is no op
+ }
+
+std::string TLS::Callbacks::tls_server_choose_app_protocol(const std::vector<std::string>&)
+ {
+ return "";
+ }
+
+std::string TLS::Callbacks::tls_peer_network_identity()
+ {
+ return "";
+ }
+
+void TLS::Callbacks::tls_modify_extensions(Extensions&, Connection_Side)
+ {
+ }
+
+void TLS::Callbacks::tls_examine_extensions(const Extensions&, Connection_Side)
+ {
+ }
+
+std::string TLS::Callbacks::tls_decode_group_param(Group_Params group_param)
+ {
+ return group_param_to_string(group_param);
+ }
+
+void TLS::Callbacks::tls_verify_cert_chain(
+ const std::vector<X509_Certificate>& cert_chain,
+ const std::vector<std::shared_ptr<const OCSP::Response>>& ocsp_responses,
+ const std::vector<Certificate_Store*>& trusted_roots,
+ Usage_Type usage,
+ const std::string& hostname,
+ const TLS::Policy& policy)
+ {
+ if(cert_chain.empty())
+ throw Invalid_Argument("Certificate chain was empty");
+
+ 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,
+ (usage == Usage_Type::TLS_SERVER_AUTH ? hostname : ""),
+ usage,
+ std::chrono::system_clock::now(),
+ tls_verify_cert_chain_ocsp_timeout(),
+ ocsp_responses);
+
+ if(!result.successful_validation())
+ {
+ throw TLS_Exception(Alert::BAD_CERTIFICATE,
+ "Certificate validation failure: " + result.result_string());
+ }
+ }
+
+std::vector<uint8_t> TLS::Callbacks::tls_sign_message(
+ const Private_Key& key,
+ RandomNumberGenerator& rng,
+ const std::string& emsa,
+ Signature_Format format,
+ const std::vector<uint8_t>& msg)
+ {
+ PK_Signer signer(key, rng, emsa, format);
+
+ return signer.sign_message(msg, rng);
+ }
+
+bool TLS::Callbacks::tls_verify_message(
+ const Public_Key& key,
+ const std::string& emsa,
+ Signature_Format format,
+ const std::vector<uint8_t>& msg,
+ const std::vector<uint8_t>& sig)
+ {
+ PK_Verifier verifier(key, emsa, format);
+
+ return verifier.verify_message(msg, sig);
+ }
+
+std::pair<secure_vector<uint8_t>, std::vector<uint8_t>> TLS::Callbacks::tls_dh_agree(
+ const std::vector<uint8_t>& modulus,
+ const std::vector<uint8_t>& generator,
+ const std::vector<uint8_t>& peer_public_value,
+ const Policy& policy,
+ RandomNumberGenerator& rng)
+ {
+ BigInt p = BigInt::decode(modulus);
+ BigInt g = BigInt::decode(generator);
+ BigInt Y = BigInt::decode(peer_public_value);
+
+ /*
+ * A basic check for key validity. As we do not know q here we
+ * cannot check that Y is in the right subgroup. However since
+ * our key is ephemeral there does not seem to be any
+ * advantage to bogus keys anyway.
+ */
+ if(Y <= 1 || Y >= p - 1)
+ throw TLS_Exception(Alert::ILLEGAL_PARAMETER,
+ "Server sent bad DH key for DHE exchange");
+
+ DL_Group group(p, g);
+
+ if(!group.verify_group(rng, false))
+ throw TLS_Exception(Alert::INSUFFICIENT_SECURITY,
+ "DH group validation failed");
+
+ DH_PublicKey peer_key(group, Y);
+
+ policy.check_peer_key_acceptable(peer_key);
+
+ DH_PrivateKey priv_key(rng, group);
+ PK_Key_Agreement ka(priv_key, rng, "Raw");
+ secure_vector<uint8_t> dh_secret = CT::strip_leading_zeros(
+ ka.derive_key(0, peer_key.public_value()).bits_of());
+
+ return std::make_pair(dh_secret, priv_key.public_value());
+ }
+
+std::pair<secure_vector<uint8_t>, std::vector<uint8_t>> TLS::Callbacks::tls_ecdh_agree(
+ const std::string& curve_name,
+ const std::vector<uint8_t>& peer_public_value,
+ const Policy& policy,
+ RandomNumberGenerator& rng,
+ bool compressed)
+ {
+ secure_vector<uint8_t> ecdh_secret;
+ std::vector<uint8_t> our_public_value;
+
+ if(curve_name == "x25519")
+ {
+#if defined(BOTAN_HAS_CURVE_25519)
+ if(peer_public_value.size() != 32)
+ {
+ throw TLS_Exception(Alert::HANDSHAKE_FAILURE, "Invalid X25519 key size");
+ }
+
+ Curve25519_PublicKey peer_key(peer_public_value);
+ policy.check_peer_key_acceptable(peer_key);
+ Curve25519_PrivateKey priv_key(rng);
+ PK_Key_Agreement ka(priv_key, rng, "Raw");
+ ecdh_secret = ka.derive_key(0, peer_key.public_value()).bits_of();
+
+ // X25519 is always compressed but sent as "uncompressed" in TLS
+ our_public_value = priv_key.public_value();
+#else
+ throw Internal_Error("Negotiated X25519 somehow, but it is disabled");
+#endif
+ }
+ else
+ {
+ EC_Group group(OID::from_string(curve_name));
+ ECDH_PublicKey peer_key(group, group.OS2ECP(peer_public_value));
+ policy.check_peer_key_acceptable(peer_key);
+ ECDH_PrivateKey priv_key(rng, group);
+ PK_Key_Agreement ka(priv_key, rng, "Raw");
+ ecdh_secret = ka.derive_key(0, peer_key.public_value()).bits_of();
+ our_public_value = priv_key.public_value(compressed ? PointGFp::COMPRESSED : PointGFp::UNCOMPRESSED);
+ }
+
+ return std::make_pair(ecdh_secret, our_public_value);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/tls/tls_callbacks.h b/comm/third_party/botan/src/lib/tls/tls_callbacks.h
new file mode 100644
index 0000000000..995c02e2d4
--- /dev/null
+++ b/comm/third_party/botan/src/lib/tls/tls_callbacks.h
@@ -0,0 +1,484 @@
+/*
+* TLS Callbacks
+* (C) 2016 Matthias Gierlings
+* 2016 Jack Lloyd
+* 2017 Harry Reimann, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_TLS_CALLBACKS_H_
+#define BOTAN_TLS_CALLBACKS_H_
+
+#include <botan/tls_session.h>
+#include <botan/tls_alert.h>
+#include <botan/pubkey.h>
+#include <functional>
+
+namespace Botan {
+
+class Certificate_Store;
+class X509_Certificate;
+
+namespace OCSP {
+
+class Response;
+
+}
+
+namespace TLS {
+
+class Handshake_Message;
+class Policy;
+class Extensions;
+class Certificate_Status_Request;
+
+/**
+* Encapsulates the callbacks that a TLS channel will make which are due to
+* channel specific operations.
+*/
+class BOTAN_PUBLIC_API(2,0) Callbacks
+ {
+ public:
+ virtual ~Callbacks() = default;
+
+ /**
+ * Mandatory callback: output function
+ * The channel will call this with data which needs to be sent to the peer
+ * (eg, over a socket or some other form of IPC). The array will be overwritten
+ * when the function returns so a copy must be made if the data cannot be
+ * sent immediately.
+ *
+ * @param data the vector of data to send
+ *
+ * @param size the number of bytes to send
+ */
+ virtual void tls_emit_data(const uint8_t data[], size_t size) = 0;
+
+ /**
+ * Mandatory callback: process application data
+ * Called when application data record is received from the peer.
+ * Again the array is overwritten immediately after the function returns.
+ *
+ * @param seq_no the underlying TLS/DTLS record sequence number
+ *
+ * @param data the vector containing the received record
+ *
+ * @param size the length of the received record, in bytes
+ */
+ virtual void tls_record_received(uint64_t seq_no, const uint8_t data[], size_t size) = 0;
+
+ /**
+ * Mandatory callback: alert received
+ * Called when an alert is received from the peer
+ * If fatal, the connection is closing. If not fatal, the connection may
+ * still be closing (depending on the error and the peer).
+ *
+ * @param alert the source of the alert
+ */
+ virtual void tls_alert(Alert alert) = 0;
+
+ /**
+ * Mandatory callback: session established
+ * Called when a session is established. Throw an exception to abort
+ * the connection.
+ *
+ * @param session the session descriptor
+ *
+ * @return return false to prevent the session from being cached,
+ * return true to cache the session in the configured session manager
+ */
+ virtual bool tls_session_established(const Session& session) = 0;
+
+ /**
+ * Optional callback: session activated
+ * Called when a session is active and can be written to
+ */
+ virtual void tls_session_activated() {}
+
+ /**
+ * Optional callback with default impl: verify cert chain
+ *
+ * Default implementation performs a standard PKIX validation
+ * and initiates network OCSP request for end-entity cert.
+ * Override to provide different behavior.
+ *
+ * Check the certificate chain is valid up to a trusted root, and
+ * optionally (if hostname != "") that the hostname given is
+ * consistent with the leaf certificate.
+ *
+ * This function should throw an exception derived from
+ * std::exception with an informative what() result if the
+ * certificate chain cannot be verified.
+ *
+ * @param cert_chain specifies a certificate chain leading to a
+ * trusted root CA certificate.
+ * @param ocsp_responses the server may have provided some
+ * @param trusted_roots the list of trusted certificates
+ * @param usage what this cert chain is being used for
+ * Usage_Type::TLS_SERVER_AUTH for server chains,
+ * Usage_Type::TLS_CLIENT_AUTH for client chains,
+ * Usage_Type::UNSPECIFIED for other uses
+ * @param hostname when authenticating a server, this is the hostname
+ * the client requested (eg via SNI). When authenticating a client,
+ * this is the server name the client is authenticating *to*.
+ * Empty in other cases or if no hostname was used.
+ * @param policy the TLS policy associated with the session being authenticated
+ * using the certificate chain
+ */
+ virtual void tls_verify_cert_chain(
+ const std::vector<X509_Certificate>& cert_chain,
+ const std::vector<std::shared_ptr<const OCSP::Response>>& ocsp_responses,
+ const std::vector<Certificate_Store*>& trusted_roots,
+ Usage_Type usage,
+ const std::string& hostname,
+ const TLS::Policy& policy);
+
+ /**
+ * Called by default `tls_verify_cert_chain` to get the timeout to use for OCSP
+ * requests. Return 0 to disable online OCSP checks.
+ *
+ * This function should not be "const" since the implementation might need
+ * to perform some side effecting operation to compute the result.
+ */
+ virtual std::chrono::milliseconds tls_verify_cert_chain_ocsp_timeout() const
+ {
+ return std::chrono::milliseconds(0);
+ }
+
+ /**
+ * Called by the TLS server whenever the client included the
+ * status_request extension (see RFC 6066, a.k.a OCSP stapling)
+ * in the ClientHello.
+ *
+ * @return the encoded OCSP response to be sent to the client which
+ * 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.
+ */
+ virtual std::vector<uint8_t> tls_provide_cert_status(const std::vector<X509_Certificate>& chain,
+ const Certificate_Status_Request& csr)
+ {
+ BOTAN_UNUSED(chain);
+ BOTAN_UNUSED(csr);
+ return std::vector<uint8_t>();
+ }
+
+ /**
+ * Optional callback with default impl: sign a message
+ *
+ * Default implementation uses PK_Signer::sign_message().
+ * Override to provide a different approach, e.g. using an external device.
+ *
+ * @param key the private key of the signer
+ * @param rng a random number generator
+ * @param emsa the encoding method to be applied to the message
+ * @param format the signature format
+ * @param msg the input data for the signature
+ *
+ * @return the signature
+ */
+ virtual std::vector<uint8_t> tls_sign_message(
+ const Private_Key& key,
+ RandomNumberGenerator& rng,
+ const std::string& emsa,
+ Signature_Format format,
+ const std::vector<uint8_t>& msg);
+
+ /**
+ * Optional callback with default impl: verify a message signature
+ *
+ * Default implementation uses PK_Verifier::verify_message().
+ * Override to provide a different approach, e.g. using an external device.
+ *
+ * @param key the public key of the signer
+ * @param emsa the encoding method to be applied to the message
+ * @param format the signature format
+ * @param msg the input data for the signature
+ * @param sig the signature to be checked
+ *
+ * @return true if the signature is valid, false otherwise
+ */
+ virtual bool tls_verify_message(
+ const Public_Key& key,
+ const std::string& emsa,
+ Signature_Format format,
+ const std::vector<uint8_t>& msg,
+ const std::vector<uint8_t>& sig);
+
+ /**
+ * Optional callback with default impl: client side DH agreement
+ *
+ * Default implementation uses PK_Key_Agreement::derive_key().
+ * Override to provide a different approach, e.g. using an external device.
+ *
+ * @param modulus the modulus p of the discrete logarithm group
+ * @param generator the generator of the DH subgroup
+ * @param peer_public_value the public value of the peer
+ * @param policy the TLS policy associated with the session being established
+ * @param rng a random number generator
+ *
+ * @return a pair consisting of the agreed raw secret and our public value
+ */
+ virtual std::pair<secure_vector<uint8_t>, std::vector<uint8_t>> tls_dh_agree(
+ const std::vector<uint8_t>& modulus,
+ const std::vector<uint8_t>& generator,
+ const std::vector<uint8_t>& peer_public_value,
+ const Policy& policy,
+ RandomNumberGenerator& rng);
+
+ /**
+ * Optional callback with default impl: client side ECDH agreement
+ *
+ * Default implementation uses PK_Key_Agreement::derive_key().
+ * Override to provide a different approach, e.g. using an external device.
+ *
+ * @param curve_name the name of the elliptic curve
+ * @param peer_public_value the public value of the peer
+ * @param policy the TLS policy associated with the session being established
+ * @param rng a random number generator
+ * @param compressed the compression preference for our public value
+ *
+ * @return a pair consisting of the agreed raw secret and our public value
+ */
+ virtual std::pair<secure_vector<uint8_t>, std::vector<uint8_t>> tls_ecdh_agree(
+ const std::string& curve_name,
+ const std::vector<uint8_t>& peer_public_value,
+ const Policy& policy,
+ RandomNumberGenerator& rng,
+ bool compressed);
+
+ /**
+ * Optional callback: inspect handshake message
+ * Throw an exception to abort the handshake.
+ * Default simply ignores the message.
+ *
+ * @param message the handshake message
+ */
+ virtual void tls_inspect_handshake_msg(const Handshake_Message& message);
+
+ /**
+ * Optional callback for server: choose ALPN protocol
+ * ALPN (RFC 7301) works by the client sending a list of application
+ * protocols it is willing to negotiate. The server then selects which
+ * protocol to use, which is not necessarily even on the list that
+ * the client sent.
+ *
+ * @param client_protos the vector of protocols the client is willing to negotiate
+ *
+ * @return the protocol selected by the server, which need not be on the
+ * list that the client sent; if this is the empty string, the server ignores the
+ * client ALPN extension. Default return value is empty string.
+ */
+ virtual std::string tls_server_choose_app_protocol(const std::vector<std::string>& client_protos);
+
+ /**
+ * Optional callback: examine/modify Extensions before sending.
+ *
+ * Both client and server will call this callback on the Extensions object
+ * before serializing it in the client/server hellos. This allows an
+ * application to modify which extensions are sent during the
+ * handshake.
+ *
+ * Default implementation does nothing.
+ *
+ * @param extn the extensions
+ * @param which_side will be CLIENT or SERVER which is the current
+ * applications role in the exchange.
+ */
+ virtual void tls_modify_extensions(Extensions& extn, Connection_Side which_side);
+
+ /**
+ * Optional callback: examine peer extensions.
+ *
+ * Both client and server will call this callback with the Extensions
+ * object after receiving it from the peer. This allows examining the
+ * Extensions, for example to implement a custom extension. It also allows
+ * an application to require that a particular extension be implemented;
+ * throw an exception from this function to abort the handshake.
+ *
+ * Default implementation does nothing.
+ *
+ * @param extn the extensions
+ * @param which_side will be CLIENT if these are are the clients extensions (ie we are
+ * the server) or SERVER if these are the server extensions (we are the client).
+ */
+ virtual void tls_examine_extensions(const Extensions& extn, Connection_Side which_side);
+
+ /**
+ * Optional callback: decode TLS group ID
+ *
+ * TLS uses a 16-bit field to identify ECC and DH groups. This callback
+ * handles the decoding. You only need to implement this if you are using
+ * a custom ECC or DH group (this is extremely uncommon).
+ *
+ * Default implementation uses the standard (IETF-defined) mappings.
+ */
+ virtual std::string tls_decode_group_param(Group_Params group_param);
+
+ /**
+ * Optional callback: return peer network identity
+ *
+ * There is no expected or specified format. The only expectation is this
+ * function will return a unique value. For example returning the peer
+ * host IP and port.
+ *
+ * This is used to bind the DTLS cookie to a particular network identity.
+ * It is only called if the dtls-cookie-secret PSK is also defined.
+ */
+ virtual std::string tls_peer_network_identity();
+
+ /**
+ * Optional callback: error logging. (not currently called)
+ * @param err An error message related to this connection.
+ */
+ virtual void tls_log_error(const char* err)
+ {
+ BOTAN_UNUSED(err);
+ }
+
+ /**
+ * Optional callback: debug logging. (not currently called)
+ * @param what Some hopefully informative string
+ */
+ virtual void tls_log_debug(const char* what)
+ {
+ BOTAN_UNUSED(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
+ */
+ virtual void tls_log_debug_bin(const char* descr, const uint8_t val[], size_t val_len)
+ {
+ BOTAN_UNUSED(descr, val, val_len);
+ }
+ };
+
+/**
+* TLS::Callbacks using std::function for compatability with the old API signatures.
+* This type is only provided for backward compatibility.
+* New implementations should derive from TLS::Callbacks instead.
+*/
+class BOTAN_PUBLIC_API(2,0) Compat_Callbacks final : public Callbacks
+ {
+ public:
+ typedef std::function<void (const uint8_t[], size_t)> output_fn;
+ typedef std::function<void (const uint8_t[], size_t)> data_cb;
+ typedef std::function<void (Alert, const uint8_t[], size_t)> alert_cb;
+ typedef std::function<bool (const Session&)> handshake_cb;
+ typedef std::function<void (const Handshake_Message&)> handshake_msg_cb;
+ typedef std::function<std::string (std::vector<std::string>)> next_protocol_fn;
+
+ /**
+ * @param data_output_fn is called with data for the outbound socket
+ *
+ * @param app_data_cb is called when new application data is received
+ *
+ * @param recv_alert_cb is called when a TLS alert is received
+ *
+ * @param hs_cb is called when a handshake is completed
+ *
+ * @param hs_msg_cb is called for each handshake message received
+ *
+ * @param next_proto is called with ALPN protocol data sent by the client
+ */
+ BOTAN_DEPRECATED("Use TLS::Callbacks (virtual interface).")
+ Compat_Callbacks(output_fn data_output_fn, data_cb app_data_cb, alert_cb recv_alert_cb,
+ handshake_cb hs_cb, handshake_msg_cb hs_msg_cb = nullptr,
+ next_protocol_fn next_proto = nullptr)
+ : m_output_function(data_output_fn), m_app_data_cb(app_data_cb),
+ m_alert_cb(std::bind(recv_alert_cb, std::placeholders::_1, nullptr, 0)),
+ m_hs_cb(hs_cb), m_hs_msg_cb(hs_msg_cb), m_next_proto(next_proto) {}
+
+ BOTAN_DEPRECATED("Use TLS::Callbacks (virtual interface).")
+ Compat_Callbacks(output_fn data_output_fn, data_cb app_data_cb,
+ std::function<void (Alert)> recv_alert_cb,
+ handshake_cb hs_cb,
+ handshake_msg_cb hs_msg_cb = nullptr,
+ next_protocol_fn next_proto = nullptr)
+ : m_output_function(data_output_fn), m_app_data_cb(app_data_cb),
+ m_alert_cb(recv_alert_cb),
+ m_hs_cb(hs_cb), m_hs_msg_cb(hs_msg_cb), m_next_proto(next_proto) {}
+
+ enum class SILENCE_DEPRECATION_WARNING { PLEASE = 0 };
+ Compat_Callbacks(SILENCE_DEPRECATION_WARNING,
+ output_fn data_output_fn, data_cb app_data_cb,
+ std::function<void (Alert)> recv_alert_cb,
+ handshake_cb hs_cb,
+ handshake_msg_cb hs_msg_cb = nullptr,
+ next_protocol_fn next_proto = nullptr)
+ : m_output_function(data_output_fn),
+ m_app_data_cb(app_data_cb),
+ m_alert_cb(recv_alert_cb),
+ m_hs_cb(hs_cb),
+ m_hs_msg_cb(hs_msg_cb),
+ m_next_proto(next_proto) {}
+
+ Compat_Callbacks(SILENCE_DEPRECATION_WARNING,
+ output_fn data_output_fn, data_cb app_data_cb, alert_cb recv_alert_cb,
+ handshake_cb hs_cb, handshake_msg_cb hs_msg_cb = nullptr,
+ next_protocol_fn next_proto = nullptr)
+ : m_output_function(data_output_fn), m_app_data_cb(app_data_cb),
+ m_alert_cb(std::bind(recv_alert_cb, std::placeholders::_1, nullptr, 0)),
+ m_hs_cb(hs_cb), m_hs_msg_cb(hs_msg_cb), m_next_proto(next_proto) {}
+
+
+ void tls_emit_data(const uint8_t data[], size_t size) override
+ {
+ BOTAN_ASSERT(m_output_function != nullptr,
+ "Invalid TLS output function callback.");
+ m_output_function(data, size);
+ }
+
+ void tls_record_received(uint64_t /*seq_no*/, const uint8_t data[], size_t size) override
+ {
+ BOTAN_ASSERT(m_app_data_cb != nullptr,
+ "Invalid TLS app data callback.");
+ m_app_data_cb(data, size);
+ }
+
+ void tls_alert(Alert alert) override
+ {
+ BOTAN_ASSERT(m_alert_cb != nullptr,
+ "Invalid TLS alert callback.");
+ m_alert_cb(alert);
+ }
+
+ bool tls_session_established(const Session& session) override
+ {
+ BOTAN_ASSERT(m_hs_cb != nullptr,
+ "Invalid TLS handshake callback.");
+ return m_hs_cb(session);
+ }
+
+ std::string tls_server_choose_app_protocol(const std::vector<std::string>& client_protos) override
+ {
+ if(m_next_proto != nullptr) { return m_next_proto(client_protos); }
+ return "";
+ }
+
+ void tls_inspect_handshake_msg(const Handshake_Message& hmsg) override
+ {
+ // The handshake message callback is optional so we can
+ // not assume it has been set.
+ if(m_hs_msg_cb != nullptr) { m_hs_msg_cb(hmsg); }
+ }
+
+ private:
+ const output_fn m_output_function;
+ const data_cb m_app_data_cb;
+ const std::function<void (Alert)> m_alert_cb;
+ const handshake_cb m_hs_cb;
+ const handshake_msg_cb m_hs_msg_cb;
+ const next_protocol_fn m_next_proto;
+ };
+
+}
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/tls/tls_cbc/info.txt b/comm/third_party/botan/src/lib/tls/tls_cbc/info.txt
new file mode 100644
index 0000000000..a1d2b18cce
--- /dev/null
+++ b/comm/third_party/botan/src/lib/tls/tls_cbc/info.txt
@@ -0,0 +1,12 @@
+<defines>
+TLS_CBC -> 20161008
+</defines>
+
+<header:internal>
+tls_cbc.h
+</header:internal>
+
+<requires>
+cbc
+hmac
+</requires>
diff --git a/comm/third_party/botan/src/lib/tls/tls_cbc/tls_cbc.cpp b/comm/third_party/botan/src/lib/tls/tls_cbc/tls_cbc.cpp
new file mode 100644
index 0000000000..3e3e4c2df0
--- /dev/null
+++ b/comm/third_party/botan/src/lib/tls/tls_cbc/tls_cbc.cpp
@@ -0,0 +1,499 @@
+/*
+* TLS CBC Record Handling
+* (C) 2012,2013,2014,2015,2016,2020 Jack Lloyd
+* (C) 2016 Juraj Somorovsky
+* (C) 2016 Matthias Gierlings
+* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/tls_cbc.h>
+#include <botan/cbc.h>
+
+#include <botan/internal/rounding.h>
+#include <botan/internal/ct_utils.h>
+#include <botan/loadstor.h>
+#include <botan/tls_alert.h>
+#include <botan/tls_exceptn.h>
+
+namespace Botan {
+
+namespace TLS {
+
+/*
+* TLS_CBC_HMAC_AEAD_Mode Constructor
+*/
+TLS_CBC_HMAC_AEAD_Mode::TLS_CBC_HMAC_AEAD_Mode(Cipher_Dir dir,
+ std::unique_ptr<BlockCipher> cipher,
+ std::unique_ptr<MessageAuthenticationCode> mac,
+ size_t cipher_keylen,
+ size_t mac_keylen,
+ Protocol_Version version,
+ bool use_encrypt_then_mac) :
+ m_cipher_name(cipher->name()),
+ m_mac_name(mac->name()),
+ m_cipher_keylen(cipher_keylen),
+ 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 = version.supports_explicit_cbc_ivs() ? m_block_size : 0;
+
+ m_is_datagram = version.is_datagram_protocol();
+
+ m_mac = std::move(mac);
+
+ if(dir == ENCRYPTION)
+ m_cbc.reset(new CBC_Encryption(cipher.release(), new Null_Padding));
+ else
+ m_cbc.reset(new CBC_Decryption(cipher.release(), new Null_Padding));
+ }
+
+void TLS_CBC_HMAC_AEAD_Mode::clear()
+ {
+ cbc().clear();
+ mac().clear();
+ reset();
+ }
+
+void TLS_CBC_HMAC_AEAD_Mode::reset()
+ {
+ cbc_state().clear();
+ m_ad.clear();
+ m_msg.clear();
+ }
+
+std::string TLS_CBC_HMAC_AEAD_Mode::name() const
+ {
+ return "TLS_CBC(" + m_cipher_name + "," + m_mac_name + ")";
+ }
+
+size_t TLS_CBC_HMAC_AEAD_Mode::update_granularity() const
+ {
+ return 1; // just buffers anyway
+ }
+
+bool TLS_CBC_HMAC_AEAD_Mode::valid_nonce_length(size_t nl) const
+ {
+ if(m_cbc_state.empty())
+ return nl == block_size();
+ return nl == iv_size();
+ }
+
+Key_Length_Specification TLS_CBC_HMAC_AEAD_Mode::key_spec() const
+ {
+ return Key_Length_Specification(m_cipher_keylen + m_mac_keylen);
+ }
+
+void TLS_CBC_HMAC_AEAD_Mode::key_schedule(const uint8_t key[], size_t keylen)
+ {
+ // Both keys are of fixed length specified by the ciphersuite
+
+ if(keylen != m_cipher_keylen + m_mac_keylen)
+ throw Invalid_Key_Length(name(), keylen);
+
+ mac().set_key(&key[0], m_mac_keylen);
+ cbc().set_key(&key[m_mac_keylen], m_cipher_keylen);
+ }
+
+void TLS_CBC_HMAC_AEAD_Mode::start_msg(const uint8_t nonce[], size_t nonce_len)
+ {
+ if(!valid_nonce_length(nonce_len))
+ {
+ throw Invalid_IV_Length(name(), nonce_len);
+ }
+
+ m_msg.clear();
+
+ if(nonce_len > 0)
+ {
+ m_cbc_state.assign(nonce, nonce + nonce_len);
+ }
+ }
+
+size_t TLS_CBC_HMAC_AEAD_Mode::process(uint8_t buf[], size_t sz)
+ {
+ m_msg.insert(m_msg.end(), buf, buf + sz);
+ return 0;
+ }
+
+std::vector<uint8_t> TLS_CBC_HMAC_AEAD_Mode::assoc_data_with_len(uint16_t len)
+ {
+ std::vector<uint8_t> ad = m_ad;
+ BOTAN_ASSERT(ad.size() == 13, "Expected AAD size");
+ ad[11] = get_byte(0, len);
+ ad[12] = get_byte(1, len);
+ return ad;
+ }
+
+void TLS_CBC_HMAC_AEAD_Mode::set_associated_data(const uint8_t ad[], size_t ad_len)
+ {
+ if(ad_len != 13)
+ throw Invalid_Argument("Invalid TLS AEAD associated data length");
+ m_ad.assign(ad, ad + ad_len);
+ }
+
+void TLS_CBC_HMAC_AEAD_Encryption::set_associated_data(const uint8_t ad[], size_t ad_len)
+ {
+ TLS_CBC_HMAC_AEAD_Mode::set_associated_data(ad, ad_len);
+
+ if(use_encrypt_then_mac())
+ {
+ // AAD hack for EtM
+ // EtM uses ciphertext size instead of plaintext size for AEAD input
+ const uint16_t pt_size = make_uint16(assoc_data()[11], assoc_data()[12]);
+ const uint16_t enc_size = static_cast<uint16_t>(round_up(iv_size() + pt_size + 1, block_size()));
+ assoc_data()[11] = get_byte<uint16_t>(0, enc_size);
+ assoc_data()[12] = get_byte<uint16_t>(1, enc_size);
+ }
+ }
+
+void TLS_CBC_HMAC_AEAD_Encryption::cbc_encrypt_record(
+ secure_vector<uint8_t>& buffer, size_t offset, size_t padding_length)
+ {
+ // We always do short padding:
+ BOTAN_ASSERT_NOMSG(padding_length <= 16);
+
+ buffer.resize(buffer.size() + padding_length);
+
+ const uint8_t padding_val = static_cast<uint8_t>(padding_length - 1);
+
+ CT::poison(&padding_val, 1);
+ CT::poison(&padding_length, 1);
+ CT::poison(buffer.data(), buffer.size());
+
+ const size_t last_block_starts = buffer.size() - block_size();
+ const size_t padding_starts = buffer.size() - padding_length;
+ for(size_t i = last_block_starts; i != buffer.size(); ++i)
+ {
+ auto add_padding = CT::Mask<uint8_t>(CT::Mask<size_t>::is_gte(i, padding_starts));
+ buffer[i] = add_padding.select(padding_val, buffer[i]);
+ }
+
+ CT::unpoison(padding_val);
+ CT::unpoison(padding_length);
+ CT::unpoison(buffer.data(), buffer.size());
+
+ cbc().start(cbc_state());
+ cbc().process(&buffer[offset], buffer.size() - offset);
+
+ cbc_state().assign(buffer.data() + (buffer.size() - block_size()), buffer.data() + buffer.size());
+ }
+
+size_t TLS_CBC_HMAC_AEAD_Encryption::output_length(size_t input_length) const
+ {
+ return round_up(input_length + 1 + (use_encrypt_then_mac() ? 0 : tag_size()), block_size()) +
+ (use_encrypt_then_mac() ? tag_size() : 0);
+ }
+
+void TLS_CBC_HMAC_AEAD_Encryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
+ {
+ update(buffer, offset);
+
+ const size_t msg_size = msg().size();
+
+ const size_t input_size = msg_size + 1 + (use_encrypt_then_mac() ? 0 : tag_size());
+ const size_t enc_size = round_up(input_size, block_size());
+ BOTAN_DEBUG_ASSERT(enc_size % block_size() == 0);
+
+ const uint8_t padding_val = static_cast<uint8_t>(enc_size - input_size);
+ const size_t padding_length = static_cast<size_t>(padding_val) + 1;
+
+ buffer.reserve(offset + msg_size + padding_length + tag_size());
+ buffer.resize(offset + msg_size);
+ copy_mem(&buffer[offset], msg().data(), msg_size);
+
+ mac().update(assoc_data());
+
+ if(use_encrypt_then_mac())
+ {
+ if(iv_size() > 0)
+ {
+ mac().update(cbc_state());
+ }
+
+ cbc_encrypt_record(buffer, offset, padding_length);
+ mac().update(&buffer[offset], enc_size);
+ buffer.resize(buffer.size() + tag_size());
+ mac().final(&buffer[buffer.size() - tag_size()]);
+ }
+ else
+ {
+ mac().update(&buffer[offset], msg_size);
+ buffer.resize(buffer.size() + tag_size());
+ mac().final(&buffer[buffer.size() - tag_size()]);
+ cbc_encrypt_record(buffer, offset, padding_length);
+ }
+ }
+
+/*
+* Checks the TLS padding. Returns 0 if the padding is invalid (we
+* count the padding_length field as part of the padding size so a
+* valid padding will always be at least one byte long), or the length
+* of the padding otherwise. This is actually padding_length + 1
+* because both the padding and padding_length fields are padding from
+* our perspective.
+*
+* Returning 0 in the error case should ensure the MAC check will fail.
+* This approach is suggested in section 6.2.3.2 of RFC 5246.
+*/
+uint16_t check_tls_cbc_padding(const uint8_t record[], size_t record_len)
+ {
+ if(record_len == 0 || record_len > 0xFFFF)
+ return 0;
+
+ const uint16_t rec16 = static_cast<uint16_t>(record_len);
+
+ /*
+ * TLS v1.0 and up require all the padding bytes be the same value
+ * and allows up to 255 bytes.
+ */
+
+ const uint16_t to_check = std::min<uint16_t>(256, static_cast<uint16_t>(record_len));
+ const uint8_t pad_byte = record[record_len-1];
+ const uint16_t pad_bytes = 1 + pad_byte;
+
+ auto pad_invalid = CT::Mask<uint16_t>::is_lt(rec16, pad_bytes);
+
+ for(uint16_t i = rec16 - to_check; i != rec16; ++i)
+ {
+ const uint16_t offset = rec16 - i;
+ const auto in_pad_range = CT::Mask<uint16_t>::is_lte(offset, pad_bytes);
+ const auto pad_correct = CT::Mask<uint16_t>::is_equal(record[i], pad_byte);
+ pad_invalid |= in_pad_range & ~pad_correct;
+ }
+
+ return pad_invalid.if_not_set_return(pad_bytes);
+ }
+
+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");
+
+ cbc().start(cbc_state());
+ cbc_state().assign(record_contents + record_len - block_size(),
+ record_contents + record_len);
+
+ cbc().process(record_contents, record_len);
+ }
+
+size_t TLS_CBC_HMAC_AEAD_Decryption::output_length(size_t) const
+ {
+ /*
+ * We don't know this because the padding is arbitrary
+ */
+ return 0;
+ }
+
+/*
+* This function performs additional compression calls in order
+* to protect from the Lucky 13 attack. It adds new compression
+* function calls over dummy data, by computing additional HMAC updates.
+*
+* The countermeasure was described (in a similar way) in the Lucky 13 paper.
+*
+* Background:
+* - One SHA-1/SHA-256 compression is performed with 64 bytes of data.
+* - HMAC adds 8 byte length field and padding (at least 1 byte) so that we have:
+* - 0 - 55 bytes: 1 compression
+* - 56 - 55+64 bytes: 2 compressions
+* - 56+64 - 55+2*64 bytes: 3 compressions ...
+* - For SHA-384, this works similarly, but we have 128 byte blocks and 16 byte
+* long length field. This results in:
+* - 0 - 111 bytes: 1 compression
+* - 112 - 111+128 bytes: 2 compressions ...
+*
+* The implemented countermeasure works as follows:
+* 1) It computes max_compressions: number of maximum compressions performed on
+* the decrypted data
+* 2) It computes current_compressions: number of compressions performed on the
+* decrypted data, after padding has been removed
+* 3) If current_compressions != max_compressions: It invokes an HMAC update
+* over dummy data so that (max_compressions - current_compressions)
+* compressions are performed. Otherwise, it invokes an HMAC update so that
+* no compressions are performed.
+*
+* Note that the padding validation in Botan is always performed over
+* min(plen,256) bytes, see the function check_tls_cbc_padding. This differs
+* from the countermeasure described in the paper.
+*
+* Note that the padding length padlen does also count the last byte
+* of the decrypted plaintext. This is different from the Lucky 13 paper.
+*
+* This countermeasure leaves a difference of about 100 clock cycles (in
+* comparison to >1000 clock cycles observed without it).
+*
+* plen represents the length of the decrypted plaintext message P
+* padlen represents the padding length
+*
+*/
+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;
+ }
+ // number of maximum MACed bytes
+ const uint16_t L1 = static_cast<uint16_t>(13 + plen - tag_size());
+ // number of current MACed bytes (L1 - padlen)
+ // Here the Lucky 13 paper is different because the padlen length in the paper
+ // does not count the last message byte.
+ const uint16_t L2 = static_cast<uint16_t>(13 + plen - padlen - tag_size());
+ // From the paper, for SHA-256/SHA-1 compute: ceil((L1-55)/64) and ceil((L2-55)/64)
+ // ceil((L1-55)/64) = floor((L1+64-1-55)/64)
+ // Here we compute number of compressions for SHA-* in general
+ const uint16_t max_compresssions = ( (L1 + block_size - 1 - max_bytes_in_first_block) / block_size);
+ const uint16_t current_compressions = ((L2 + block_size - 1 - max_bytes_in_first_block) / block_size);
+ // number of additional compressions we have to perform
+ const uint16_t add_compressions = max_compresssions - current_compressions;
+ const uint16_t equal = CT::Mask<uint16_t>::is_equal(max_compresssions, current_compressions).if_set_return(1);
+ // We compute the data length we need to achieve the number of compressions.
+ // If there are no compressions, we just add 55/111 dummy bytes so that no
+ // compression is performed.
+ const uint16_t data_len = block_size * add_compressions + equal * max_bytes_in_first_block;
+ std::vector<uint8_t> data(data_len);
+ mac().update(data);
+ // we do not need to clear the MAC since the connection is broken anyway
+ }
+
+void TLS_CBC_HMAC_AEAD_Decryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
+ {
+ update(buffer, offset);
+ buffer.resize(offset);
+
+ const size_t record_len = msg().size();
+ uint8_t* record_contents = msg().data();
+
+ // This early exit does not leak info because all the values compared are public
+ if(record_len < tag_size() ||
+ (record_len - (use_encrypt_then_mac() ? tag_size() : 0)) % block_size() != 0)
+ {
+ throw TLS_Exception(Alert::BAD_RECORD_MAC, "Message authentication failure");
+ }
+
+ if(use_encrypt_then_mac())
+ {
+ const size_t enc_size = record_len - tag_size();
+ const size_t enc_iv_size = enc_size + iv_size();
+
+ BOTAN_ASSERT_NOMSG(enc_iv_size <= 0xFFFF);
+
+ mac().update(assoc_data_with_len(static_cast<uint16_t>(enc_iv_size)));
+ if(iv_size() > 0)
+ {
+ mac().update(cbc_state());
+ }
+ mac().update(record_contents, enc_size);
+
+ std::vector<uint8_t> mac_buf(tag_size());
+ mac().final(mac_buf.data());
+
+ const size_t mac_offset = enc_size;
+
+ const bool mac_ok = constant_time_compare(&record_contents[mac_offset], mac_buf.data(), tag_size());
+
+ if(!mac_ok)
+ {
+ throw TLS_Exception(Alert::BAD_RECORD_MAC, "Message authentication failure");
+ }
+
+ cbc_decrypt_record(record_contents, enc_size);
+
+ // 0 if padding was invalid, otherwise 1 + padding_bytes
+ const uint16_t pad_size = check_tls_cbc_padding(record_contents, enc_size);
+
+ // No oracle here, whoever sent us this had the key since MAC check passed
+ if(pad_size == 0)
+ {
+ throw TLS_Exception(Alert::BAD_RECORD_MAC, "Message authentication failure");
+ }
+
+ const uint8_t* plaintext_block = &record_contents[0];
+ const size_t plaintext_length = enc_size - pad_size;
+
+ buffer.insert(buffer.end(), plaintext_block, plaintext_block + plaintext_length);
+ }
+ else
+ {
+ cbc_decrypt_record(record_contents, record_len);
+
+ CT::poison(record_contents, record_len);
+
+ // 0 if padding was invalid, otherwise 1 + padding_bytes
+ uint16_t pad_size = check_tls_cbc_padding(record_contents, record_len);
+
+ /*
+ This mask is zero if there is not enough room in the packet to get a valid MAC.
+
+ We have to accept empty packets, since otherwise we are not compatible
+ with how OpenSSL's countermeasure for fixing BEAST in TLS 1.0 CBC works
+ (sending empty records, instead of 1/(n-1) splitting)
+ */
+
+ // We know the cast cannot overflow as pad_size <= 256 && tag_size <= 32
+ const auto size_ok_mask = CT::Mask<uint16_t>::is_lte(
+ static_cast<uint16_t>(tag_size() + pad_size),
+ static_cast<uint16_t>(record_len));
+
+ pad_size = size_ok_mask.if_set_return(pad_size);
+
+ CT::unpoison(record_contents, record_len);
+
+ /*
+ This is unpoisoned sooner than it should. The pad_size leaks to plaintext_length and
+ then to the timing channel in the MAC computation described in the Lucky 13 paper.
+ */
+ CT::unpoison(pad_size);
+
+ const uint8_t* plaintext_block = &record_contents[0];
+ const uint16_t plaintext_length = static_cast<uint16_t>(record_len - tag_size() - pad_size);
+
+ mac().update(assoc_data_with_len(plaintext_length));
+ mac().update(plaintext_block, plaintext_length);
+
+ std::vector<uint8_t> mac_buf(tag_size());
+ mac().final(mac_buf.data());
+
+ const size_t mac_offset = record_len - (tag_size() + pad_size);
+
+ const bool mac_ok = constant_time_compare(&record_contents[mac_offset], mac_buf.data(), tag_size());
+
+ const auto ok_mask = size_ok_mask & CT::Mask<uint16_t>::expand(mac_ok) & CT::Mask<uint16_t>::expand(pad_size);
+
+ CT::unpoison(ok_mask);
+
+ if(ok_mask.is_set())
+ {
+ buffer.insert(buffer.end(), plaintext_block, plaintext_block + plaintext_length);
+ }
+ else
+ {
+ perform_additional_compressions(record_len, pad_size);
+
+ /*
+ * In DTLS case we have to finish computing the MAC since we require the
+ * MAC state be reset for future packets. This extra timing channel may
+ * be exploitable in a Lucky13 variant.
+ */
+ if(is_datagram_protocol())
+ mac().final(mac_buf);
+ throw TLS_Exception(Alert::BAD_RECORD_MAC, "Message authentication failure");
+ }
+ }
+ }
+
+}
+
+}
diff --git a/comm/third_party/botan/src/lib/tls/tls_cbc/tls_cbc.h b/comm/third_party/botan/src/lib/tls/tls_cbc/tls_cbc.h
new file mode 100644
index 0000000000..9387142184
--- /dev/null
+++ b/comm/third_party/botan/src/lib/tls/tls_cbc/tls_cbc.h
@@ -0,0 +1,186 @@
+/*
+* TLS CBC+HMAC AEAD
+* (C) 2016 Jack Lloyd
+* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_TLS_CBC_HMAC_AEAD_H_
+#define BOTAN_TLS_CBC_HMAC_AEAD_H_
+
+#include <botan/aead.h>
+#include <botan/block_cipher.h>
+#include <botan/mac.h>
+#include <botan/tls_version.h>
+
+namespace Botan {
+
+namespace TLS {
+
+/**
+* TLS CBC+HMAC AEAD base class (GenericBlockCipher in TLS spec)
+* This is the weird TLS-specific mode, not for general consumption.
+*/
+class BOTAN_TEST_API TLS_CBC_HMAC_AEAD_Mode : public AEAD_Mode
+ {
+ public:
+ size_t process(uint8_t buf[], size_t sz) override final;
+
+ std::string name() const override final;
+
+ void set_associated_data(const uint8_t ad[], size_t ad_len) override;
+
+ size_t update_granularity() const override final;
+
+ Key_Length_Specification key_spec() const override final;
+
+ bool valid_nonce_length(size_t nl) const override final;
+
+ size_t tag_size() const override final { return m_tag_size; }
+
+ size_t default_nonce_length() const override final { return m_iv_size; }
+
+ void clear() override final;
+
+ void reset() override final;
+
+ protected:
+ TLS_CBC_HMAC_AEAD_Mode(Cipher_Dir direction,
+ std::unique_ptr<BlockCipher> cipher,
+ std::unique_ptr<MessageAuthenticationCode> mac,
+ size_t cipher_keylen,
+ size_t mac_keylen,
+ Protocol_Version version,
+ bool use_encrypt_then_mac);
+
+ size_t cipher_keylen() const { return m_cipher_keylen; }
+ size_t mac_keylen() const { return m_mac_keylen; }
+ size_t iv_size() const { return m_iv_size; }
+ size_t block_size() const { return m_block_size; }
+
+ bool use_encrypt_then_mac() const { return m_use_encrypt_then_mac; }
+
+ bool is_datagram_protocol() const { return m_is_datagram; }
+
+ Cipher_Mode& cbc() const { return *m_cbc; }
+
+ MessageAuthenticationCode& mac() const
+ {
+ BOTAN_ASSERT_NONNULL(m_mac);
+ return *m_mac;
+ }
+
+ secure_vector<uint8_t>& cbc_state() { return m_cbc_state; }
+ std::vector<uint8_t>& assoc_data() { return m_ad; }
+ secure_vector<uint8_t>& msg() { return m_msg; }
+
+ std::vector<uint8_t> assoc_data_with_len(uint16_t len);
+
+ private:
+ void start_msg(const uint8_t nonce[], size_t nonce_len) override final;
+
+ void key_schedule(const uint8_t key[], size_t length) override final;
+
+ 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_iv_size;
+ size_t m_tag_size;
+ size_t m_block_size;
+ bool m_use_encrypt_then_mac;
+ bool m_is_datagram;
+
+ std::unique_ptr<Cipher_Mode> m_cbc;
+ std::unique_ptr<MessageAuthenticationCode> m_mac;
+
+ secure_vector<uint8_t> m_cbc_state;
+ std::vector<uint8_t> m_ad;
+ secure_vector<uint8_t> m_msg;
+ };
+
+/**
+* TLS_CBC_HMAC_AEAD Encryption
+*/
+class BOTAN_TEST_API TLS_CBC_HMAC_AEAD_Encryption final : public TLS_CBC_HMAC_AEAD_Mode
+ {
+ public:
+ /**
+ */
+ TLS_CBC_HMAC_AEAD_Encryption(
+ std::unique_ptr<BlockCipher> cipher,
+ std::unique_ptr<MessageAuthenticationCode> 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(ENCRYPTION,
+ std::move(cipher),
+ std::move(mac),
+ cipher_keylen,
+ mac_keylen,
+ version,
+ use_encrypt_then_mac)
+ {}
+
+ void set_associated_data(const uint8_t ad[], size_t ad_len) override;
+
+ size_t output_length(size_t input_length) const override;
+
+ size_t minimum_final_size() const override { return 0; }
+
+ void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
+ private:
+ void cbc_encrypt_record(secure_vector<uint8_t>& buffer, size_t offset,
+ size_t padding_length);
+ };
+
+/**
+* TLS_CBC_HMAC_AEAD Decryption
+*/
+class BOTAN_TEST_API TLS_CBC_HMAC_AEAD_Decryption final : public TLS_CBC_HMAC_AEAD_Mode
+ {
+ public:
+ /**
+ */
+ TLS_CBC_HMAC_AEAD_Decryption(std::unique_ptr<BlockCipher> cipher,
+ std::unique_ptr<MessageAuthenticationCode> 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(DECRYPTION,
+ std::move(cipher),
+ std::move(mac),
+ cipher_keylen,
+ mac_keylen,
+ version,
+ use_encrypt_then_mac)
+ {}
+
+ size_t output_length(size_t input_length) const override;
+
+ size_t minimum_final_size() const override { return tag_size(); }
+
+ void finish(secure_vector<uint8_t>& final_block, size_t offset = 0) override;
+
+ private:
+ void cbc_decrypt_record(uint8_t record_contents[], size_t record_len);
+
+ void perform_additional_compressions(size_t plen, size_t padlen);
+ };
+
+/**
+* Check the TLS padding of a record
+* @param record the record bits
+* @param record_len length of record
+* @return 0 if padding is invalid, otherwise padding_bytes + 1
+*/
+BOTAN_TEST_API uint16_t check_tls_cbc_padding(const uint8_t record[], size_t record_len);
+
+}
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/tls/tls_channel.cpp b/comm/third_party/botan/src/lib/tls/tls_channel.cpp
new file mode 100644
index 0000000000..897fed97c6
--- /dev/null
+++ b/comm/third_party/botan/src/lib/tls/tls_channel.cpp
@@ -0,0 +1,795 @@
+/*
+* TLS Channels
+* (C) 2011,2012,2014,2015,2016 Jack Lloyd
+* 2016 Matthias Gierlings
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/tls_channel.h>
+#include <botan/tls_policy.h>
+#include <botan/tls_messages.h>
+#include <botan/kdf.h>
+#include <botan/internal/tls_handshake_state.h>
+#include <botan/internal/tls_record.h>
+#include <botan/internal/tls_seq_numbers.h>
+#include <botan/internal/rounding.h>
+#include <botan/internal/stl_util.h>
+#include <botan/loadstor.h>
+
+namespace Botan {
+
+namespace TLS {
+
+size_t TLS::Channel::IO_BUF_DEFAULT_SIZE = 10*1024;
+
+Channel::Channel(Callbacks& callbacks,
+ Session_Manager& session_manager,
+ RandomNumberGenerator& rng,
+ const Policy& policy,
+ bool is_server,
+ bool is_datagram,
+ size_t reserved_io_buffer_size) :
+ m_is_server(is_server),
+ m_is_datagram(is_datagram),
+ m_callbacks(callbacks),
+ m_session_manager(session_manager),
+ m_policy(policy),
+ m_rng(rng),
+ m_has_been_closed(false)
+ {
+ init(reserved_io_buffer_size);
+ }
+
+Channel::Channel(output_fn out,
+ data_cb app_data_cb,
+ alert_cb recv_alert_cb,
+ handshake_cb hs_cb,
+ handshake_msg_cb hs_msg_cb,
+ Session_Manager& session_manager,
+ RandomNumberGenerator& rng,
+ const Policy& policy,
+ bool is_server,
+ bool is_datagram,
+ size_t io_buf_sz) :
+ m_is_server(is_server),
+ m_is_datagram(is_datagram),
+ m_compat_callbacks(new Compat_Callbacks(
+ /*
+ this Channel constructor is also deprecated so its ok that it
+ relies on a deprecated API
+ */
+ Compat_Callbacks::SILENCE_DEPRECATION_WARNING::PLEASE,
+ out, app_data_cb, recv_alert_cb, hs_cb, hs_msg_cb)),
+ m_callbacks(*m_compat_callbacks.get()),
+ m_session_manager(session_manager),
+ m_policy(policy),
+ m_rng(rng),
+ m_has_been_closed(false)
+ {
+ init(io_buf_sz);
+ }
+
+void Channel::init(size_t io_buf_sz)
+ {
+ /* epoch 0 is plaintext, thus null cipher state */
+ m_write_cipher_states[0] = nullptr;
+ m_read_cipher_states[0] = nullptr;
+
+ m_writebuf.reserve(io_buf_sz);
+ m_readbuf.reserve(io_buf_sz);
+ }
+
+void Channel::reset_state()
+ {
+ m_active_state.reset();
+ m_pending_state.reset();
+ m_readbuf.clear();
+ m_write_cipher_states.clear();
+ m_read_cipher_states.clear();
+ }
+
+void Channel::reset_active_association_state()
+ {
+ // This operation only makes sense for DTLS
+ BOTAN_ASSERT_NOMSG(m_is_datagram);
+ m_active_state.reset();
+ m_read_cipher_states.clear();
+ m_write_cipher_states.clear();
+
+ m_write_cipher_states[0] = nullptr;
+ m_read_cipher_states[0] = nullptr;
+
+ if(m_sequence_numbers)
+ m_sequence_numbers->reset();
+ }
+
+Channel::~Channel()
+ {
+ // So unique_ptr destructors run correctly
+ }
+
+Connection_Sequence_Numbers& Channel::sequence_numbers() const
+ {
+ BOTAN_ASSERT(m_sequence_numbers, "Have a sequence numbers object");
+ return *m_sequence_numbers;
+ }
+
+std::shared_ptr<Connection_Cipher_State> Channel::read_cipher_state_epoch(uint16_t epoch) const
+ {
+ auto i = m_read_cipher_states.find(epoch);
+ if(i == m_read_cipher_states.end())
+ throw Internal_Error("TLS::Channel No read cipherstate for epoch " + std::to_string(epoch));
+ return i->second;
+ }
+
+std::shared_ptr<Connection_Cipher_State> Channel::write_cipher_state_epoch(uint16_t epoch) const
+ {
+ auto i = m_write_cipher_states.find(epoch);
+ if(i == m_write_cipher_states.end())
+ throw Internal_Error("TLS::Channel No write cipherstate for epoch " + std::to_string(epoch));
+ return i->second;
+ }
+
+std::vector<X509_Certificate> Channel::peer_cert_chain() const
+ {
+ if(auto active = active_state())
+ return get_peer_cert_chain(*active);
+ return std::vector<X509_Certificate>();
+ }
+
+bool Channel::save_session(const Session& session)
+ {
+ return callbacks().tls_session_established(session);
+ }
+
+Handshake_State& Channel::create_handshake_state(Protocol_Version version)
+ {
+ if(pending_state())
+ throw Internal_Error("create_handshake_state called during handshake");
+
+ if(auto active = active_state())
+ {
+ Protocol_Version active_version = active->version();
+
+ if(active_version.is_datagram_protocol() != version.is_datagram_protocol())
+ {
+ throw TLS_Exception(Alert::PROTOCOL_VERSION,
+ "Active state using version " + active_version.to_string() +
+ " cannot change to " + version.to_string() + " in pending");
+ }
+ }
+
+ if(!m_sequence_numbers)
+ {
+ if(version.is_datagram_protocol())
+ m_sequence_numbers.reset(new Datagram_Sequence_Numbers);
+ else
+ m_sequence_numbers.reset(new Stream_Sequence_Numbers);
+ }
+
+ using namespace std::placeholders;
+
+ std::unique_ptr<Handshake_IO> io;
+ if(version.is_datagram_protocol())
+ {
+ io.reset(new Datagram_Handshake_IO(
+ std::bind(&Channel::send_record_under_epoch, this, _1, _2, _3),
+ sequence_numbers(),
+ static_cast<uint16_t>(m_policy.dtls_default_mtu()),
+ m_policy.dtls_initial_timeout(),
+ m_policy.dtls_maximum_timeout()));
+ }
+ else
+ {
+ io.reset(new Stream_Handshake_IO(std::bind(&Channel::send_record, this, _1, _2)));
+ }
+
+ m_pending_state.reset(new_handshake_state(io.release()));
+
+ if(auto active = active_state())
+ m_pending_state->set_version(active->version());
+
+ return *m_pending_state.get();
+ }
+
+bool Channel::timeout_check()
+ {
+ if(m_pending_state)
+ return m_pending_state->handshake_io().timeout_check();
+
+ //FIXME: scan cipher suites and remove epochs older than 2*MSL
+ return false;
+ }
+
+void Channel::renegotiate(bool force_full_renegotiation)
+ {
+ if(pending_state()) // currently in handshake?
+ return;
+
+ if(auto active = active_state())
+ {
+ if(force_full_renegotiation == false)
+ force_full_renegotiation = !policy().allow_resumption_for_renegotiation();
+
+ initiate_handshake(create_handshake_state(active->version()),
+ force_full_renegotiation);
+ }
+ else
+ throw Invalid_State("Cannot renegotiate on inactive connection");
+ }
+
+void Channel::change_cipher_spec_reader(Connection_Side side)
+ {
+ auto pending = pending_state();
+
+ BOTAN_ASSERT(pending && pending->server_hello(),
+ "Have received server hello");
+
+ if(pending->server_hello()->compression_method() != 0)
+ throw Internal_Error("Negotiated unknown compression algorithm");
+
+ sequence_numbers().new_read_cipher_state();
+
+ const uint16_t epoch = sequence_numbers().current_read_epoch();
+
+ BOTAN_ASSERT(m_read_cipher_states.count(epoch) == 0,
+ "No read cipher state currently set for next epoch");
+
+ // flip side as we are reading
+ std::shared_ptr<Connection_Cipher_State> read_state(
+ new Connection_Cipher_State(pending->version(),
+ (side == CLIENT) ? SERVER : CLIENT,
+ false,
+ pending->ciphersuite(),
+ pending->session_keys(),
+ pending->server_hello()->supports_encrypt_then_mac()));
+
+ m_read_cipher_states[epoch] = read_state;
+ }
+
+void Channel::change_cipher_spec_writer(Connection_Side side)
+ {
+ auto pending = pending_state();
+
+ BOTAN_ASSERT(pending && pending->server_hello(),
+ "Have received server hello");
+
+ if(pending->server_hello()->compression_method() != 0)
+ throw Internal_Error("Negotiated unknown compression algorithm");
+
+ sequence_numbers().new_write_cipher_state();
+
+ const uint16_t epoch = sequence_numbers().current_write_epoch();
+
+ BOTAN_ASSERT(m_write_cipher_states.count(epoch) == 0,
+ "No write cipher state currently set for next epoch");
+
+ std::shared_ptr<Connection_Cipher_State> write_state(
+ new Connection_Cipher_State(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::is_active() const
+ {
+ if(is_closed())
+ return false;
+ return (active_state() != nullptr);
+ }
+
+bool Channel::is_closed() const
+ {
+ return m_has_been_closed;
+ }
+
+void Channel::activate_session()
+ {
+ std::swap(m_active_state, m_pending_state);
+ m_pending_state.reset();
+
+ if(!m_active_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();
+
+ const auto not_current_epoch =
+ [current_epoch](uint16_t epoch) { return (epoch != current_epoch); };
+
+ map_remove_if(not_current_epoch, m_write_cipher_states);
+ map_remove_if(not_current_epoch, m_read_cipher_states);
+ }
+
+ callbacks().tls_session_activated();
+ }
+
+size_t Channel::received_data(const std::vector<uint8_t>& buf)
+ {
+ return this->received_data(buf.data(), buf.size());
+ }
+
+size_t Channel::received_data(const uint8_t input[], size_t input_size)
+ {
+ const bool allow_epoch0_restart = m_is_datagram && m_is_server && policy().allow_dtls_epoch0_restart();
+
+ try
+ {
+ while(input_size)
+ {
+ size_t consumed = 0;
+
+ auto get_epoch = [this](uint16_t epoch) { return read_cipher_state_epoch(epoch); };
+
+ const Record_Header record =
+ read_record(m_is_datagram,
+ m_readbuf,
+ input,
+ input_size,
+ consumed,
+ m_record_buf,
+ m_sequence_numbers.get(),
+ get_epoch,
+ allow_epoch0_restart);
+
+ const size_t needed = record.needed();
+
+ BOTAN_ASSERT(consumed > 0, "Got to eat something");
+
+ BOTAN_ASSERT(consumed <= input_size,
+ "Record reader consumed sane amount");
+
+ input += consumed;
+ input_size -= consumed;
+
+ BOTAN_ASSERT(input_size == 0 || needed == 0,
+ "Got a full record or consumed all input");
+
+ if(input_size == 0 && needed != 0)
+ return needed; // need more data to complete record
+
+ // Ignore invalid records in DTLS
+ if(m_is_datagram && record.type() == NO_RECORD)
+ {
+ return 0;
+ }
+
+ if(m_record_buf.size() > MAX_PLAINTEXT_SIZE)
+ throw TLS_Exception(Alert::RECORD_OVERFLOW,
+ "TLS plaintext record is larger than allowed maximum");
+
+
+ const bool epoch0_restart = m_is_datagram && record.epoch() == 0 && active_state();
+ BOTAN_ASSERT_IMPLICATION(epoch0_restart, allow_epoch0_restart, "Allowed state");
+
+ const bool initial_record = epoch0_restart || (!pending_state() && !active_state());
+
+ if(record.type() != ALERT)
+ {
+ if(initial_record)
+ {
+ // For initial records just check for basic sanity
+ if(record.version().major_version() != 3 &&
+ record.version().major_version() != 0xFE)
+ {
+ throw TLS_Exception(Alert::PROTOCOL_VERSION,
+ "Received unexpected record version in initial record");
+ }
+ }
+ else if(auto pending = pending_state())
+ {
+ if(pending->server_hello() != nullptr && record.version() != pending->version())
+ {
+ if(record.version() != pending->version())
+ {
+ throw TLS_Exception(Alert::PROTOCOL_VERSION,
+ "Received unexpected record version");
+ }
+ }
+ }
+ else if(auto active = active_state())
+ {
+ if(record.version() != active->version())
+ {
+ throw TLS_Exception(Alert::PROTOCOL_VERSION,
+ "Received unexpected record version");
+ }
+ }
+ }
+
+ if(record.type() == HANDSHAKE || record.type() == CHANGE_CIPHER_SPEC)
+ {
+ if(m_has_been_closed)
+ throw TLS_Exception(Alert::UNEXPECTED_MESSAGE, "Received handshake data after connection closure");
+ process_handshake_ccs(m_record_buf, record.sequence(), record.type(), record.version(), epoch0_restart);
+ }
+ else if(record.type() == APPLICATION_DATA)
+ {
+ if(m_has_been_closed)
+ throw TLS_Exception(Alert::UNEXPECTED_MESSAGE, "Received application data after connection closure");
+ if(pending_state() != nullptr)
+ throw TLS_Exception(Alert::UNEXPECTED_MESSAGE, "Can't interleave application and handshake data");
+ process_application_data(record.sequence(), m_record_buf);
+ }
+ else if(record.type() == ALERT)
+ {
+ process_alert(m_record_buf);
+ }
+ else if(record.type() != NO_RECORD)
+ throw Unexpected_Message("Unexpected record type " +
+ std::to_string(record.type()) +
+ " from counterparty");
+ }
+
+ return 0; // on a record boundary
+ }
+ catch(TLS_Exception& e)
+ {
+ send_fatal_alert(e.type());
+ throw;
+ }
+ catch(Invalid_Authentication_Tag&)
+ {
+ send_fatal_alert(Alert::BAD_RECORD_MAC);
+ throw;
+ }
+ catch(Decoding_Error&)
+ {
+ send_fatal_alert(Alert::DECODE_ERROR);
+ throw;
+ }
+ catch(...)
+ {
+ send_fatal_alert(Alert::INTERNAL_ERROR);
+ throw;
+ }
+ }
+
+void Channel::process_handshake_ccs(const secure_vector<uint8_t>& record,
+ uint64_t record_sequence,
+ Record_Type record_type,
+ Protocol_Version record_version,
+ bool epoch0_restart)
+ {
+ if(!m_pending_state)
+ {
+ // No pending handshake, possibly new:
+ if(record_version.is_datagram_protocol() && !epoch0_restart)
+ {
+ if(m_sequence_numbers)
+ {
+ /*
+ * Might be a peer retransmit under epoch - 1 in which
+ * case we must retransmit last flight
+ */
+ sequence_numbers().read_accept(record_sequence);
+
+ const uint16_t epoch = record_sequence >> 48;
+
+ if(epoch == sequence_numbers().current_read_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
+ {
+ create_handshake_state(record_version);
+ }
+ }
+ else
+ {
+ create_handshake_state(record_version);
+ }
+ }
+
+ // May have been created in above conditional
+ 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();
+
+ if(msg.first == HANDSHAKE_NONE) // no full handshake yet
+ break;
+
+ process_handshake_msg(active_state(), *pending,
+ msg.first, msg.second, epoch0_restart);
+
+ if(!m_pending_state)
+ break;
+ }
+ }
+ }
+
+void Channel::process_application_data(uint64_t seq_no, const secure_vector<uint8_t>& record)
+ {
+ if(!active_state())
+ throw Unexpected_Message("Application data before handshake done");
+
+ callbacks().tls_record_received(seq_no, record.data(), record.size());
+ }
+
+void Channel::process_alert(const secure_vector<uint8_t>& record)
+ {
+ Alert alert_msg(record);
+
+ if(alert_msg.type() == Alert::NO_RENEGOTIATION)
+ m_pending_state.reset();
+
+ callbacks().tls_alert(alert_msg);
+
+ if(alert_msg.is_fatal())
+ {
+ if(auto active = active_state())
+ m_session_manager.remove_entry(active->server_hello()->session_id());
+ }
+
+ if(alert_msg.type() == Alert::CLOSE_NOTIFY)
+ send_warning_alert(Alert::CLOSE_NOTIFY); // reply in kind
+
+ if(alert_msg.type() == Alert::CLOSE_NOTIFY || alert_msg.is_fatal())
+ {
+ m_has_been_closed = true;
+ }
+ }
+
+void Channel::write_record(Connection_Cipher_State* cipher_state, uint16_t epoch,
+ uint8_t record_type, const uint8_t input[], size_t length)
+ {
+ BOTAN_ASSERT(m_pending_state || m_active_state, "Some connection state exists");
+
+ 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);
+
+ if(cipher_state == nullptr)
+ {
+ TLS::write_unencrypted_record(m_writebuf, record_type, record_version, next_seq,
+ input, length);
+ }
+ else
+ {
+ TLS::write_record(m_writebuf, record_type, record_version, next_seq,
+ input, length, *cipher_state, m_rng);
+ }
+
+ callbacks().tls_emit_data(m_writebuf.data(), m_writebuf.size());
+ }
+
+void Channel::send_record_array(uint16_t epoch, uint8_t type, const uint8_t input[], size_t length)
+ {
+ if(length == 0)
+ return;
+
+ /*
+ * In versions without an explicit IV field (only TLS v1.0 now that
+ * SSLv3 has been removed) send a single byte record first to randomize
+ * the following (implicit) IV of the following record.
+ *
+ * This isn't needed in TLS v1.1 or higher.
+ *
+ * An empty record also works but apparently some implementations do
+ * not like this (https://bugzilla.mozilla.org/show_bug.cgi?id=665814)
+ *
+ * See https://www.openssl.org/~bodo/tls-cbc.txt for background.
+ */
+
+ auto cipher_state = write_cipher_state_epoch(epoch);
+
+ if(type == APPLICATION_DATA && m_active_state->version().supports_explicit_cbc_ivs() == false)
+ {
+ while(length)
+ {
+ write_record(cipher_state.get(), epoch, type, input, 1);
+ input += 1;
+ length -= 1;
+
+ const size_t sending = std::min<size_t>(length, MAX_PLAINTEXT_SIZE);
+ write_record(cipher_state.get(), epoch, type, input, sending);
+
+ input += sending;
+ length -= sending;
+ }
+ }
+ else
+ {
+ while(length)
+ {
+ const size_t sending = std::min<size_t>(length, MAX_PLAINTEXT_SIZE);
+ write_record(cipher_state.get(), epoch, type, input, sending);
+
+ input += sending;
+ length -= sending;
+ }
+ }
+ }
+
+void Channel::send_record(uint8_t record_type, const std::vector<uint8_t>& record)
+ {
+ send_record_array(sequence_numbers().current_write_epoch(),
+ record_type, record.data(), record.size());
+ }
+
+void Channel::send_record_under_epoch(uint16_t epoch, uint8_t record_type,
+ const std::vector<uint8_t>& record)
+ {
+ send_record_array(epoch, record_type, record.data(), record.size());
+ }
+
+void Channel::send(const uint8_t buf[], size_t buf_size)
+ {
+ if(!is_active())
+ throw Invalid_State("Data cannot be sent on inactive TLS connection");
+
+ send_record_array(sequence_numbers().current_write_epoch(),
+ APPLICATION_DATA, buf, buf_size);
+ }
+
+void Channel::send(const std::string& string)
+ {
+ this->send(cast_char_ptr_to_uint8(string.data()), string.size());
+ }
+
+void Channel::send_alert(const Alert& alert)
+ {
+ if(alert.is_valid() && !is_closed())
+ {
+ try
+ {
+ send_record(ALERT, alert.serialize());
+ }
+ catch(...) { /* swallow it */ }
+ }
+
+ if(alert.type() == Alert::NO_RENEGOTIATION)
+ m_pending_state.reset();
+
+ if(alert.is_fatal())
+ {
+ if(auto active = active_state())
+ {
+ m_session_manager.remove_entry(active->server_hello()->session_id());
+ }
+ reset_state();
+ }
+
+ if(alert.type() == Alert::CLOSE_NOTIFY || alert.is_fatal())
+ {
+ m_has_been_closed = true;
+ }
+ }
+
+void Channel::secure_renegotiation_check(const Client_Hello* 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::HANDSHAKE_FAILURE,
+ "Client changed its mind about secure renegotiation");
+ }
+
+ if(secure_renegotiation)
+ {
+ const std::vector<uint8_t>& data = client_hello->renegotiation_info();
+
+ if(data != secure_renegotiation_data_for_client_hello())
+ throw TLS_Exception(Alert::HANDSHAKE_FAILURE,
+ "Client sent bad values for secure renegotiation");
+ }
+ }
+
+void Channel::secure_renegotiation_check(const Server_Hello* 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::HANDSHAKE_FAILURE,
+ "Server changed its mind about secure renegotiation");
+ }
+
+ if(secure_renegotiation)
+ {
+ const std::vector<uint8_t>& data = server_hello->renegotiation_info();
+
+ if(data != secure_renegotiation_data_for_server_hello())
+ throw TLS_Exception(Alert::HANDSHAKE_FAILURE,
+ "Server sent bad values for secure renegotiation");
+ }
+ }
+
+std::vector<uint8_t> Channel::secure_renegotiation_data_for_client_hello() const
+ {
+ if(auto active = active_state())
+ return active->client_finished()->verify_data();
+ return std::vector<uint8_t>();
+ }
+
+std::vector<uint8_t> Channel::secure_renegotiation_data_for_server_hello() const
+ {
+ if(auto active = active_state())
+ {
+ std::vector<uint8_t> buf = active->client_finished()->verify_data();
+ buf += active->server_finished()->verify_data();
+ return buf;
+ }
+
+ return std::vector<uint8_t>();
+ }
+
+bool Channel::secure_renegotiation_supported() const
+ {
+ if(auto active = active_state())
+ return active->server_hello()->secure_renegotiation();
+
+ if(auto pending = pending_state())
+ if(auto hello = pending->server_hello())
+ return hello->secure_renegotiation();
+
+ return false;
+ }
+
+SymmetricKey Channel::key_material_export(const std::string& label,
+ const std::string& context,
+ size_t length) const
+ {
+ if(auto active = active_state())
+ {
+ if(pending_state() != nullptr)
+ throw Invalid_State("Channel::key_material_export cannot export during renegotiation");
+
+ std::unique_ptr<KDF> prf(active->protocol_specific_prf());
+
+ const secure_vector<uint8_t>& master_secret =
+ active->session_keys().master_secret();
+
+ std::vector<uint8_t> salt;
+ salt += active->client_hello()->random();
+ salt += active->server_hello()->random();
+
+ if(context != "")
+ {
+ 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<uint16_t>(context_size)));
+ salt.push_back(get_byte(1, static_cast<uint16_t>(context_size)));
+ salt += to_byte_vector(context);
+ }
+
+ return prf->derive_key(length, master_secret, salt, to_byte_vector(label));
+ }
+ else
+ {
+ throw Invalid_State("Channel::key_material_export connection not active");
+ }
+ }
+
+}
+
+}
diff --git a/comm/third_party/botan/src/lib/tls/tls_channel.h b/comm/third_party/botan/src/lib/tls/tls_channel.h
new file mode 100644
index 0000000000..046560e228
--- /dev/null
+++ b/comm/third_party/botan/src/lib/tls/tls_channel.h
@@ -0,0 +1,318 @@
+/*
+* TLS Channel
+* (C) 2011,2012,2014,2015 Jack Lloyd
+* 2016 Matthias Gierlings
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_TLS_CHANNEL_H_
+#define BOTAN_TLS_CHANNEL_H_
+
+#include <botan/tls_session.h>
+#include <botan/tls_alert.h>
+#include <botan/tls_session_manager.h>
+#include <botan/tls_callbacks.h>
+#include <botan/x509cert.h>
+#include <functional>
+#include <vector>
+#include <string>
+#include <map>
+
+namespace Botan {
+
+namespace TLS {
+
+class Connection_Cipher_State;
+class Connection_Sequence_Numbers;
+class Handshake_State;
+class Handshake_Message;
+class Client_Hello;
+class Server_Hello;
+class Policy;
+
+/**
+* Generic interface for TLS endpoint
+*/
+class BOTAN_PUBLIC_API(2,0) Channel
+ {
+ public:
+ typedef std::function<void (const uint8_t[], size_t)> output_fn;
+ typedef std::function<void (const uint8_t[], size_t)> data_cb;
+ typedef std::function<void (Alert, const uint8_t[], size_t)> alert_cb;
+ typedef std::function<bool (const Session&)> handshake_cb;
+ typedef std::function<void (const Handshake_Message&)> handshake_msg_cb;
+ static size_t IO_BUF_DEFAULT_SIZE;
+
+ /**
+ * Set up a new TLS session
+ *
+ * @param callbacks contains a set of callback function references
+ * required by the TLS endpoint.
+ * @param session_manager manages session state
+ * @param rng a random number generator
+ * @param policy specifies other connection policy information
+ * @param is_server whether this is a server session or not
+ * @param is_datagram whether this is a DTLS session
+ * @param io_buf_sz This many bytes of memory will
+ * be preallocated for the read and write buffers. Smaller
+ * values just mean reallocations and copies are more likely.
+ */
+ Channel(Callbacks& callbacks,
+ Session_Manager& session_manager,
+ RandomNumberGenerator& rng,
+ const Policy& policy,
+ bool is_server,
+ bool is_datagram,
+ size_t io_buf_sz = IO_BUF_DEFAULT_SIZE);
+
+ /**
+ * DEPRECATED. This constructor is only provided for backward
+ * compatibility and should not be used in new implementations.
+ * (Not marked deprecated since it is only called internally, by
+ * other deprecated constructors)
+ */
+ Channel(output_fn out,
+ data_cb app_data_cb,
+ alert_cb alert_cb,
+ handshake_cb hs_cb,
+ handshake_msg_cb hs_msg_cb,
+ Session_Manager& session_manager,
+ RandomNumberGenerator& rng,
+ const Policy& policy,
+ bool is_server,
+ bool is_datagram,
+ size_t io_buf_sz = IO_BUF_DEFAULT_SIZE);
+
+ Channel(const Channel&) = delete;
+
+ Channel& operator=(const Channel&) = delete;
+
+ virtual ~Channel();
+
+ /**
+ * Inject TLS traffic received from counterparty
+ * @return a hint as the how many more bytes we need to process the
+ * current record (this may be 0 if on a record boundary)
+ */
+ size_t received_data(const uint8_t buf[], size_t buf_size);
+
+ /**
+ * Inject TLS traffic received from counterparty
+ * @return a hint as the how many more bytes we need to process the
+ * current record (this may be 0 if on a record boundary)
+ */
+ size_t received_data(const std::vector<uint8_t>& buf);
+
+ /**
+ * Inject plaintext intended for counterparty
+ * Throws an exception if is_active() is false
+ */
+ void send(const uint8_t buf[], size_t buf_size);
+
+ /**
+ * Inject plaintext intended for counterparty
+ * Throws an exception if is_active() is false
+ */
+ void send(const std::string& val);
+
+ /**
+ * Inject plaintext intended for counterparty
+ * Throws an exception if is_active() is false
+ */
+ template<typename Alloc>
+ void send(const std::vector<unsigned char, Alloc>& val)
+ {
+ send(val.data(), val.size());
+ }
+
+ /**
+ * Send a TLS alert message. If the alert is fatal, the internal
+ * state (keys, etc) will be reset.
+ * @param alert the Alert to send
+ */
+ void send_alert(const Alert& alert);
+
+ /**
+ * Send a warning alert
+ */
+ void send_warning_alert(Alert::Type type) { send_alert(Alert(type, false)); }
+
+ /**
+ * Send a fatal alert
+ */
+ void send_fatal_alert(Alert::Type type) { send_alert(Alert(type, true)); }
+
+ /**
+ * Send a close notification alert
+ */
+ void close() { send_warning_alert(Alert::CLOSE_NOTIFY); }
+
+ /**
+ * @return true iff the connection is active for sending application data
+ */
+ bool is_active() const;
+
+ /**
+ * @return true iff the connection has been definitely closed
+ */
+ bool is_closed() const;
+
+ /**
+ * @return certificate chain of the peer (may be empty)
+ */
+ std::vector<X509_Certificate> peer_cert_chain() const;
+
+ /**
+ * Key material export (RFC 5705)
+ * @param label a disambiguating label string
+ * @param context a per-association context value
+ * @param length the length of the desired key in bytes
+ * @return key of length bytes
+ */
+ SymmetricKey key_material_export(const std::string& label,
+ const std::string& context,
+ size_t length) const;
+
+ /**
+ * Attempt to renegotiate the session
+ * @param force_full_renegotiation if true, require a full renegotiation,
+ * otherwise allow session resumption
+ */
+ void renegotiate(bool force_full_renegotiation = false);
+
+ /**
+ * @return true iff the counterparty supports the secure
+ * renegotiation extensions.
+ */
+ bool secure_renegotiation_supported() const;
+
+ /**
+ * Perform a handshake timeout check. This does nothing unless
+ * this is a DTLS channel with a pending handshake state, in
+ * which case we check for timeout and potentially retransmit
+ * handshake packets.
+ */
+ bool timeout_check();
+
+ virtual std::string application_protocol() const = 0;
+
+ protected:
+
+ virtual void process_handshake_msg(const Handshake_State* active_state,
+ Handshake_State& pending_state,
+ Handshake_Type type,
+ const std::vector<uint8_t>& contents,
+ bool epoch0_restart) = 0;
+
+ virtual void initiate_handshake(Handshake_State& state,
+ bool force_full_renegotiation) = 0;
+
+ virtual std::vector<X509_Certificate>
+ get_peer_cert_chain(const Handshake_State& state) const = 0;
+
+ virtual Handshake_State* new_handshake_state(class Handshake_IO* io) = 0;
+
+ Handshake_State& create_handshake_state(Protocol_Version version);
+
+ void inspect_handshake_message(const Handshake_Message& msg);
+
+ void activate_session();
+
+ void change_cipher_spec_reader(Connection_Side side);
+
+ void change_cipher_spec_writer(Connection_Side side);
+
+ /* secure renegotiation handling */
+
+ void secure_renegotiation_check(const Client_Hello* client_hello);
+ void secure_renegotiation_check(const Server_Hello* server_hello);
+
+ std::vector<uint8_t> secure_renegotiation_data_for_client_hello() const;
+ std::vector<uint8_t> secure_renegotiation_data_for_server_hello() const;
+
+ RandomNumberGenerator& rng() { return m_rng; }
+
+ Session_Manager& session_manager() { return m_session_manager; }
+
+ const Policy& policy() const { return m_policy; }
+
+ bool save_session(const Session& session);
+
+ Callbacks& callbacks() const { return m_callbacks; }
+
+ void reset_active_association_state();
+
+ private:
+ void init(size_t io_buf_sze);
+
+ void send_record(uint8_t record_type, const std::vector<uint8_t>& record);
+
+ void send_record_under_epoch(uint16_t epoch, uint8_t record_type,
+ const std::vector<uint8_t>& record);
+
+ void send_record_array(uint16_t epoch, uint8_t record_type,
+ const uint8_t input[], size_t length);
+
+ void write_record(Connection_Cipher_State* cipher_state,
+ uint16_t epoch, uint8_t type, const uint8_t input[], size_t length);
+
+ void reset_state();
+
+ Connection_Sequence_Numbers& sequence_numbers() const;
+
+ std::shared_ptr<Connection_Cipher_State> read_cipher_state_epoch(uint16_t epoch) const;
+
+ std::shared_ptr<Connection_Cipher_State> 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::receive_data. */
+ void process_handshake_ccs(const secure_vector<uint8_t>& record,
+ uint64_t record_sequence,
+ Record_Type record_type,
+ Protocol_Version record_version,
+ bool epoch0_restart);
+
+ void process_application_data(uint64_t req_no, const secure_vector<uint8_t>& record);
+
+ void process_alert(const secure_vector<uint8_t>& record);
+
+ const bool m_is_server;
+ const bool m_is_datagram;
+
+ /* callbacks */
+ std::unique_ptr<Compat_Callbacks> m_compat_callbacks;
+ Callbacks& m_callbacks;
+
+ /* external state */
+ Session_Manager& m_session_manager;
+ const Policy& m_policy;
+ RandomNumberGenerator& m_rng;
+
+ /* sequence number state */
+ std::unique_ptr<Connection_Sequence_Numbers> m_sequence_numbers;
+
+ /* pending and active connection states */
+ std::unique_ptr<Handshake_State> m_active_state;
+ std::unique_ptr<Handshake_State> m_pending_state;
+
+ /* cipher states for each epoch */
+ std::map<uint16_t, std::shared_ptr<Connection_Cipher_State>> m_write_cipher_states;
+ std::map<uint16_t, std::shared_ptr<Connection_Cipher_State>> m_read_cipher_states;
+
+ /* I/O buffers */
+ secure_vector<uint8_t> m_writebuf;
+ secure_vector<uint8_t> m_readbuf;
+ secure_vector<uint8_t> m_record_buf;
+
+ bool m_has_been_closed;
+ };
+
+}
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/tls/tls_ciphersuite.cpp b/comm/third_party/botan/src/lib/tls/tls_ciphersuite.cpp
new file mode 100644
index 0000000000..cf284e5650
--- /dev/null
+++ b/comm/third_party/botan/src/lib/tls/tls_ciphersuite.cpp
@@ -0,0 +1,253 @@
+/*
+* TLS Cipher Suite
+* (C) 2004-2010,2012,2013 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/tls_ciphersuite.h>
+#include <botan/exceptn.h>
+#include <botan/parsing.h>
+#include <botan/block_cipher.h>
+#include <botan/stream_cipher.h>
+#include <botan/hash.h>
+#include <algorithm>
+
+namespace Botan {
+
+namespace 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::AEAD_IMPLICIT_4:
+ return 4;
+ case Nonce_Format::AEAD_XOR_12:
+ return 12;
+ }
+
+ throw Invalid_State("In Ciphersuite::nonce_bytes_from_handshake invalid enum value");
+ }
+
+size_t Ciphersuite::nonce_bytes_from_record(Protocol_Version version) const
+ {
+ switch(m_nonce_format)
+ {
+ case Nonce_Format::CBC_MODE:
+ {
+ if(version.supports_explicit_cbc_ivs())
+ {
+ return cipher_algo() == "3DES" ? 8 : 16;
+ }
+ else
+ {
+ return 0;
+ }
+ }
+ case Nonce_Format::AEAD_IMPLICIT_4:
+ return 8;
+ case Nonce_Format::AEAD_XOR_12:
+ return 0;
+ }
+
+ throw Invalid_State("In Ciphersuite::nonce_bytes_from_handshake invalid enum value");
+ }
+
+bool Ciphersuite::is_scsv(uint16_t suite)
+ {
+ // TODO: derive from IANA file in script
+ return (suite == 0x00FF || suite == 0x5600);
+ }
+
+bool Ciphersuite::psk_ciphersuite() const
+ {
+ return kex_method() == Kex_Algo::PSK ||
+ kex_method() == Kex_Algo::DHE_PSK ||
+ kex_method() == Kex_Algo::ECDHE_PSK;
+ }
+
+bool Ciphersuite::ecc_ciphersuite() const
+ {
+ return kex_method() == Kex_Algo::ECDH ||
+ kex_method() == Kex_Algo::ECDHE_PSK ||
+ auth_method() == Auth_Method::ECDSA;
+ }
+
+bool Ciphersuite::usable_in_version(Protocol_Version version) const
+ {
+ if(!version.supports_aead_modes())
+ {
+ // Old versions do not support AEAD, or any MAC but SHA-1
+ if(mac_algo() != "SHA-1")
+ return false;
+ }
+
+ return true;
+ }
+
+bool Ciphersuite::cbc_ciphersuite() const
+ {
+ return (mac_algo() != "AEAD");
+ }
+
+bool Ciphersuite::signature_used() const
+ {
+ return auth_method() != Auth_Method::ANONYMOUS &&
+ auth_method() != Auth_Method::IMPLICIT;
+ }
+
+Ciphersuite Ciphersuite::by_id(uint16_t suite)
+ {
+ const std::vector<Ciphersuite>& all_suites = all_known_ciphersuites();
+ auto s = std::lower_bound(all_suites.begin(), all_suites.end(), suite);
+
+ if(s != all_suites.end() && s->ciphersuite_code() == suite)
+ {
+ return *s;
+ }
+
+ return Ciphersuite(); // some unknown ciphersuite
+ }
+
+Ciphersuite Ciphersuite::from_name(const std::string& name)
+ {
+ const std::vector<Ciphersuite>& all_suites = all_known_ciphersuites();
+
+ for(auto suite : all_suites)
+ {
+ if(suite.to_string() == name)
+ return suite;
+ }
+
+ return Ciphersuite(); // some unknown ciphersuite
+ }
+
+namespace {
+
+bool have_hash(const std::string& prf)
+ {
+ return (HashFunction::providers(prf).size() > 0);
+ }
+
+bool have_cipher(const std::string& cipher)
+ {
+ return (BlockCipher::providers(cipher).size() > 0) ||
+ (StreamCipher::providers(cipher).size() > 0);
+ }
+
+}
+
+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
+ }
+ }
+ 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::SRP_SHA)
+ {
+#if !defined(BOTAN_HAS_SRP6)
+ return false;
+#endif
+ }
+ else 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 || kex_method() == Kex_Algo::DHE_PSK)
+ {
+#if !defined(BOTAN_HAS_DIFFIE_HELLMAN)
+ return false;
+#endif
+ }
+ else if(kex_method() == Kex_Algo::CECPQ1)
+ {
+#if !defined(BOTAN_HAS_CECPQ1)
+ return false;
+#endif
+ }
+
+ if(auth_method() == Auth_Method::DSA)
+ {
+#if !defined(BOTAN_HAS_DSA)
+ return false;
+#endif
+ }
+ else 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;
+ }
+
+}
+
+}
+
diff --git a/comm/third_party/botan/src/lib/tls/tls_ciphersuite.h b/comm/third_party/botan/src/lib/tls/tls_ciphersuite.h
new file mode 100644
index 0000000000..1d23a6c4a6
--- /dev/null
+++ b/comm/third_party/botan/src/lib/tls/tls_ciphersuite.h
@@ -0,0 +1,189 @@
+/*
+* TLS Cipher Suites
+* (C) 2004-2011,2012 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_TLS_CIPHER_SUITES_H_
+#define BOTAN_TLS_CIPHER_SUITES_H_
+
+#include <botan/types.h>
+#include <botan/tls_algos.h>
+#include <botan/tls_version.h>
+#include <string>
+#include <vector>
+
+namespace Botan {
+
+namespace TLS {
+
+/**
+* Ciphersuite Information
+*/
+class BOTAN_PUBLIC_API(2,0) Ciphersuite final
+ {
+ public:
+ /**
+ * Convert an SSL/TLS ciphersuite to algorithm fields
+ * @param suite the ciphersuite code number
+ * @return ciphersuite object
+ */
+ static Ciphersuite by_id(uint16_t suite);
+
+ /**
+ * Convert an SSL/TLS ciphersuite name to algorithm fields
+ * @param name the IANA name for the desired ciphersuite
+ * @return ciphersuite object
+ */
+ static Ciphersuite from_name(const std::string& name);
+
+ /**
+ * Returns true iff this suite is a known SCSV
+ */
+ static bool is_scsv(uint16_t suite);
+
+ /**
+ * Generate a static list of all known ciphersuites and return it.
+ *
+ * @return list of all known ciphersuites
+ */
+ static const std::vector<Ciphersuite>& all_known_ciphersuites();
+
+ /**
+ * Formats the ciphersuite back to an RFC-style ciphersuite string
+ * @return RFC ciphersuite string identifier
+ */
+ std::string to_string() const { return m_iana_id; }
+
+ /**
+ * @return ciphersuite number
+ */
+ uint16_t ciphersuite_code() const { return m_ciphersuite_code; }
+
+ /**
+ * @return true if this is a PSK ciphersuite
+ */
+ bool psk_ciphersuite() const;
+
+ /**
+ * @return true if this is an ECC ciphersuite
+ */
+ bool ecc_ciphersuite() const;
+
+ /**
+ * @return true if this suite uses a CBC cipher
+ */
+ bool cbc_ciphersuite() const;
+
+ bool signature_used() const;
+
+ /**
+ * @return key exchange algorithm used by this ciphersuite
+ */
+ std::string kex_algo() const { return kex_method_to_string(kex_method()); }
+
+ Kex_Algo kex_method() const { return m_kex_algo; }
+
+ /**
+ * @return signature algorithm used by this ciphersuite
+ */
+ std::string sig_algo() const { return auth_method_to_string(auth_method()); }
+
+ Auth_Method auth_method() const { return m_auth_method; }
+
+ /**
+ * @return symmetric cipher algorithm used by this ciphersuite
+ */
+ std::string cipher_algo() const { return m_cipher_algo; }
+
+ /**
+ * @return message authentication algorithm used by this ciphersuite
+ */
+ std::string mac_algo() const { return m_mac_algo; }
+
+ std::string prf_algo() const
+ {
+ return kdf_algo_to_string(m_prf_algo);
+ }
+
+ /**
+ * @return cipher key length used by this ciphersuite
+ */
+ size_t cipher_keylen() const { return m_cipher_keylen; }
+
+ size_t nonce_bytes_from_handshake() const;
+
+ size_t nonce_bytes_from_record(Protocol_Version version) const;
+
+ Nonce_Format nonce_format() const { return m_nonce_format; }
+
+ size_t mac_keylen() const { return m_mac_keylen; }
+
+ /**
+ * @return true if this is a valid/known ciphersuite
+ */
+ bool valid() const { return m_usable; }
+
+ bool usable_in_version(Protocol_Version version) const;
+
+ bool operator<(const Ciphersuite& o) const { return ciphersuite_code() < o.ciphersuite_code(); }
+ bool operator<(const uint16_t c) const { return ciphersuite_code() < c; }
+
+ Ciphersuite() = default;
+
+ private:
+
+ bool is_usable() const;
+
+ Ciphersuite(uint16_t ciphersuite_code,
+ const char* iana_id,
+ Auth_Method auth_method,
+ Kex_Algo kex_algo,
+ const char* cipher_algo,
+ size_t cipher_keylen,
+ const char* mac_algo,
+ size_t mac_keylen,
+ KDF_Algo prf_algo,
+ Nonce_Format nonce_format) :
+ m_ciphersuite_code(ciphersuite_code),
+ m_iana_id(iana_id),
+ m_auth_method(auth_method),
+ m_kex_algo(kex_algo),
+ m_prf_algo(prf_algo),
+ m_nonce_format(nonce_format),
+ m_cipher_algo(cipher_algo),
+ m_mac_algo(mac_algo),
+ m_cipher_keylen(cipher_keylen),
+ m_mac_keylen(mac_keylen)
+ {
+ m_usable = is_usable();
+ }
+
+ uint16_t m_ciphersuite_code = 0;
+
+ /*
+ All of these const char* strings are references to compile time
+ constants in tls_suite_info.cpp
+ */
+ const char* m_iana_id = nullptr;
+
+ Auth_Method m_auth_method = Auth_Method::ANONYMOUS;
+ Kex_Algo m_kex_algo = Kex_Algo::STATIC_RSA;
+ KDF_Algo m_prf_algo = KDF_Algo::SHA_1;
+ Nonce_Format m_nonce_format = Nonce_Format::CBC_MODE;
+
+ const char* m_cipher_algo = nullptr;
+ const char* m_mac_algo = nullptr;
+
+ size_t m_cipher_keylen = 0;
+ size_t m_mac_keylen = 0;
+
+ bool m_usable = false;
+ };
+
+}
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/tls/tls_client.cpp b/comm/third_party/botan/src/lib/tls/tls_client.cpp
new file mode 100644
index 0000000000..e5d90c9503
--- /dev/null
+++ b/comm/third_party/botan/src/lib/tls/tls_client.cpp
@@ -0,0 +1,780 @@
+/*
+* TLS Client
+* (C) 2004-2011,2012,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 <botan/tls_client.h>
+#include <botan/tls_messages.h>
+#include <botan/internal/tls_handshake_state.h>
+#include <botan/internal/stl_util.h>
+#include <iterator>
+#include <sstream>
+
+namespace Botan {
+
+namespace TLS {
+
+namespace {
+
+class Client_Handshake_State final : public Handshake_State
+ {
+ public:
+ Client_Handshake_State(Handshake_IO* io, Callbacks& cb) :
+ Handshake_State(io, cb),
+ m_is_reneg(false)
+ {}
+
+ const Public_Key& get_server_public_key() const
+ {
+ BOTAN_ASSERT(server_public_key, "Server sent us a certificate");
+ return *server_public_key.get();
+ }
+
+ bool is_a_resumption() const { return (resumed_session != nullptr); }
+
+ bool is_a_renegotiation() const { return m_is_reneg; }
+
+ const secure_vector<uint8_t>& resume_master_secret() const
+ {
+ BOTAN_STATE_CHECK(is_a_resumption());
+ return resumed_session->master_secret();
+ }
+
+ const std::vector<X509_Certificate>& resume_peer_certs() const
+ {
+ BOTAN_STATE_CHECK(is_a_resumption());
+ return resumed_session->peer_certs();
+ }
+
+ std::unique_ptr<Public_Key> server_public_key;
+ // Used during session resumption
+ std::unique_ptr<Session> resumed_session;
+ bool m_is_reneg = false;
+ };
+
+}
+
+/*
+* TLS Client Constructor
+*/
+Client::Client(Callbacks& callbacks,
+ Session_Manager& session_manager,
+ Credentials_Manager& creds,
+ const Policy& policy,
+ RandomNumberGenerator& rng,
+ const Server_Information& info,
+ const Protocol_Version& offer_version,
+ const std::vector<std::string>& next_protos,
+ size_t io_buf_sz) :
+ Channel(callbacks, session_manager, rng, policy,
+ false, offer_version.is_datagram_protocol(), io_buf_sz),
+ m_creds(creds),
+ m_info(info)
+ {
+ init(offer_version, next_protos);
+ }
+
+Client::Client(output_fn data_output_fn,
+ data_cb proc_cb,
+ alert_cb recv_alert_cb,
+ handshake_cb hs_cb,
+ Session_Manager& session_manager,
+ Credentials_Manager& creds,
+ const Policy& policy,
+ RandomNumberGenerator& rng,
+ const Server_Information& info,
+ const Protocol_Version& offer_version,
+ const std::vector<std::string>& next_protos,
+ size_t io_buf_sz) :
+ Channel(data_output_fn, proc_cb, recv_alert_cb, hs_cb, Channel::handshake_msg_cb(),
+ session_manager, rng, policy, false, offer_version.is_datagram_protocol(), io_buf_sz),
+ m_creds(creds),
+ m_info(info)
+ {
+ init(offer_version, next_protos);
+ }
+
+Client::Client(output_fn data_output_fn,
+ data_cb proc_cb,
+ alert_cb recv_alert_cb,
+ handshake_cb hs_cb,
+ handshake_msg_cb hs_msg_cb,
+ Session_Manager& session_manager,
+ Credentials_Manager& creds,
+ const Policy& policy,
+ RandomNumberGenerator& rng,
+ const Server_Information& info,
+ const Protocol_Version& offer_version,
+ const std::vector<std::string>& next_protos) :
+ Channel(data_output_fn, proc_cb, recv_alert_cb, hs_cb, hs_msg_cb,
+ session_manager, rng, policy, false, offer_version.is_datagram_protocol()),
+ m_creds(creds),
+ m_info(info)
+ {
+ init(offer_version, next_protos);
+ }
+
+void Client::init(const Protocol_Version& protocol_version,
+ const std::vector<std::string>& next_protocols)
+ {
+ const std::string srp_identifier = m_creds.srp_identifier("tls-client", m_info.hostname());
+
+ Handshake_State& state = create_handshake_state(protocol_version);
+ send_client_hello(state, false, protocol_version,
+ srp_identifier, next_protocols);
+ }
+
+Handshake_State* Client::new_handshake_state(Handshake_IO* io)
+ {
+ return new Client_Handshake_State(io, callbacks());
+ }
+
+std::vector<X509_Certificate>
+Client::get_peer_cert_chain(const Handshake_State& state) const
+ {
+ const Client_Handshake_State& cstate = dynamic_cast<const Client_Handshake_State&>(state);
+
+ if(cstate.is_a_resumption())
+ return cstate.resume_peer_certs();
+
+ if(state.server_certs())
+ return state.server_certs()->cert_chain();
+ return std::vector<X509_Certificate>();
+ }
+
+/*
+* Send a new client hello to renegotiate
+*/
+void Client::initiate_handshake(Handshake_State& state,
+ bool force_full_renegotiation)
+ {
+ send_client_hello(state, force_full_renegotiation,
+ policy().latest_supported_version(state.version().is_datagram_protocol()));
+ }
+
+void Client::send_client_hello(Handshake_State& state_base,
+ bool force_full_renegotiation,
+ Protocol_Version version,
+ const std::string& srp_identifier,
+ const std::vector<std::string>& next_protocols)
+ {
+ Client_Handshake_State& state = dynamic_cast<Client_Handshake_State&>(state_base);
+
+ if(state.version().is_datagram_protocol())
+ state.set_expected_next(HELLO_VERIFY_REQUEST); // optional
+ state.set_expected_next(SERVER_HELLO);
+
+ if(!force_full_renegotiation && !m_info.empty())
+ {
+ std::unique_ptr<Session> session_info(new Session);;
+ if(session_manager().load_from_server_info(m_info, *session_info))
+ {
+ /*
+ Ensure that the session protocol cipher and version are acceptable
+ If not skip the resume and establish a new session
+ */
+ const bool exact_version = session_info->version() == version;
+ const bool ok_version =
+ (session_info->version().is_datagram_protocol() == version.is_datagram_protocol()) &&
+ policy().acceptable_protocol_version(session_info->version());
+
+ 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)
+ {
+ if(srp_identifier == "" || session_info->srp_identifier() == srp_identifier)
+ {
+ state.client_hello(
+ new Client_Hello(state.handshake_io(),
+ state.hash(),
+ policy(),
+ callbacks(),
+ rng(),
+ secure_renegotiation_data_for_client_hello(),
+ *session_info,
+ next_protocols));
+
+ state.resumed_session = std::move(session_info);
+ }
+ }
+ }
+ }
+
+ if(!state.client_hello()) // not resuming
+ {
+ Client_Hello::Settings client_settings(version, m_info.hostname(), srp_identifier);
+ state.client_hello(new Client_Hello(
+ state.handshake_io(),
+ state.hash(),
+ policy(),
+ callbacks(),
+ rng(),
+ secure_renegotiation_data_for_client_hello(),
+ client_settings,
+ next_protocols));
+ }
+
+ secure_renegotiation_check(state.client_hello());
+ }
+
+namespace {
+
+bool key_usage_matches_ciphersuite(Key_Constraints usage,
+ const Ciphersuite& suite)
+ {
+ if(usage == NO_CONSTRAINTS)
+ return true; // anything goes ...
+
+ if(suite.kex_method() == Kex_Algo::STATIC_RSA)
+ {
+ return (usage & KEY_ENCIPHERMENT) | (usage & DATA_ENCIPHERMENT);
+ }
+ else
+ {
+ return (usage & DIGITAL_SIGNATURE) | (usage & NON_REPUDIATION);
+ }
+ }
+
+}
+
+/*
+* Process a handshake message
+*/
+void Client::process_handshake_msg(const Handshake_State* active_state,
+ Handshake_State& state_base,
+ Handshake_Type type,
+ const std::vector<uint8_t>& contents,
+ bool epoch0_restart)
+ {
+ BOTAN_ASSERT_NOMSG(epoch0_restart == false); // only happens on server side
+
+ Client_Handshake_State& state = dynamic_cast<Client_Handshake_State&>(state_base);
+
+ if(type == HELLO_REQUEST && active_state)
+ {
+ Hello_Request hello_request(contents);
+
+ if(state.client_hello())
+ {
+ throw TLS_Exception(Alert::HANDSHAKE_FAILURE, "Cannot renegotiate during a handshake");
+ }
+
+ if(policy().allow_server_initiated_renegotiation())
+ {
+ if(secure_renegotiation_supported() || policy().allow_insecure_renegotiation())
+ {
+ state.m_is_reneg = true;
+ this->initiate_handshake(state, true);
+ }
+ else
+ {
+ throw TLS_Exception(Alert::HANDSHAKE_FAILURE, "Client policy prohibits insecure renegotiation");
+ }
+ }
+ else
+ {
+ if(policy().abort_connection_on_undesired_renegotiation())
+ {
+ throw TLS_Exception(Alert::NO_RENEGOTIATION, "Client policy prohibits renegotiation");
+ }
+ else
+ {
+ // RFC 5746 section 4.2
+ send_warning_alert(Alert::NO_RENEGOTIATION);
+ }
+ }
+
+ return;
+ }
+
+ state.confirm_transition_to(type);
+
+ if(type != HANDSHAKE_CCS && type != FINISHED && type != HELLO_VERIFY_REQUEST)
+ state.hash().update(state.handshake_io().format(contents, type));
+
+ if(type == HELLO_VERIFY_REQUEST)
+ {
+ state.set_expected_next(SERVER_HELLO);
+ state.set_expected_next(HELLO_VERIFY_REQUEST); // might get it again
+
+ Hello_Verify_Request hello_verify_request(contents);
+ state.hello_verify_request(hello_verify_request);
+ }
+ else if(type == SERVER_HELLO)
+ {
+ state.server_hello(new Server_Hello(contents));
+
+ if(!state.client_hello()->offered_suite(state.server_hello()->ciphersuite()))
+ {
+ throw TLS_Exception(Alert::HANDSHAKE_FAILURE,
+ "Server replied with ciphersuite we didn't send");
+ }
+
+ if(!Ciphersuite::by_id(state.server_hello()->ciphersuite()).usable_in_version(state.server_hello()->version()))
+ {
+ throw TLS_Exception(Alert::HANDSHAKE_FAILURE,
+ "Server replied using a ciphersuite not allowed in version it offered");
+ }
+
+ if(Ciphersuite::is_scsv(state.server_hello()->ciphersuite()))
+ {
+ throw TLS_Exception(Alert::HANDSHAKE_FAILURE,
+ "Server replied with a signaling ciphersuite");
+ }
+
+ if(state.server_hello()->compression_method() != 0)
+ {
+ throw TLS_Exception(Alert::ILLEGAL_PARAMETER,
+ "Server replied with non-null compression method");
+ }
+
+ if(state.client_hello()->version() > state.server_hello()->version())
+ {
+ if(state.server_hello()->random_signals_downgrade())
+ throw TLS_Exception(Alert::ILLEGAL_PARAMETER, "Downgrade attack detected");
+ }
+
+ auto client_extn = state.client_hello()->extension_types();
+ auto server_extn = state.server_hello()->extension_types();
+
+ std::vector<Handshake_Extension_Type> diff;
+
+ std::set_difference(server_extn.begin(), server_extn.end(),
+ client_extn.begin(), client_extn.end(),
+ std::back_inserter(diff));
+
+ if(!diff.empty())
+ {
+ // Server sent us back an extension we did not send!
+
+ std::ostringstream msg;
+ msg << "Server replied with unsupported extensions:";
+ for(auto&& d : diff)
+ msg << " " << static_cast<int>(d);
+ throw TLS_Exception(Alert::UNSUPPORTED_EXTENSION, msg.str());
+ }
+
+ if(uint16_t srtp = state.server_hello()->srtp_profile())
+ {
+ if(!value_exists(state.client_hello()->srtp_profiles(), srtp))
+ throw TLS_Exception(Alert::HANDSHAKE_FAILURE,
+ "Server replied with DTLS-SRTP alg we did not send");
+ }
+
+ callbacks().tls_examine_extensions(state.server_hello()->extensions(), SERVER);
+
+ state.set_version(state.server_hello()->version());
+ m_application_protocol = state.server_hello()->next_protocol();
+
+ secure_renegotiation_check(state.server_hello());
+
+ const bool server_returned_same_session_id =
+ !state.server_hello()->session_id().empty() &&
+ (state.server_hello()->session_id() == state.client_hello()->session_id());
+
+ if(server_returned_same_session_id)
+ {
+ // successful resumption
+
+ /*
+ * In this case, we offered the version used in the original
+ * session, and the server must resume with the same version.
+ */
+ if(state.server_hello()->version() != state.client_hello()->version())
+ throw TLS_Exception(Alert::HANDSHAKE_FAILURE,
+ "Server resumed session but with wrong version");
+
+ if(state.server_hello()->supports_extended_master_secret() &&
+ !state.resumed_session->supports_extended_master_secret())
+ {
+ throw TLS_Exception(Alert::HANDSHAKE_FAILURE,
+ "Server resumed session but added extended master secret");
+ }
+
+ if(!state.server_hello()->supports_extended_master_secret() &&
+ state.resumed_session->supports_extended_master_secret())
+ {
+ throw TLS_Exception(Alert::HANDSHAKE_FAILURE,
+ "Server resumed session and removed extended master secret");
+ }
+
+ state.compute_session_keys(state.resume_master_secret());
+
+ if(state.server_hello()->supports_session_ticket())
+ {
+ state.set_expected_next(NEW_SESSION_TICKET);
+ }
+ else
+ {
+ state.set_expected_next(HANDSHAKE_CCS);
+ }
+ }
+ 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
+ // in a resumption scenario.
+
+ if(active_state->version() != state.server_hello()->version())
+ throw TLS_Exception(Alert::PROTOCOL_VERSION,
+ "Server changed version after renegotiation");
+
+ if(state.server_hello()->supports_extended_master_secret() !=
+ active_state->server_hello()->supports_extended_master_secret())
+ {
+ throw TLS_Exception(Alert::HANDSHAKE_FAILURE,
+ "Server changed its mind about extended master secret");
+ }
+ }
+
+ state.resumed_session.reset(); // non-null if we were attempting a resumption
+
+ if(state.client_hello()->version().is_datagram_protocol() !=
+ state.server_hello()->version().is_datagram_protocol())
+ {
+ throw TLS_Exception(Alert::PROTOCOL_VERSION,
+ "Server replied with different protocol type than we offered");
+ }
+
+ if(state.version() > state.client_hello()->version())
+ {
+ throw TLS_Exception(Alert::HANDSHAKE_FAILURE,
+ "Server replied with later version than client offered");
+ }
+
+ if(state.version().major_version() == 3 && state.version().minor_version() == 0)
+ {
+ throw TLS_Exception(Alert::PROTOCOL_VERSION,
+ "Server attempting to negotiate SSLv3 which is not supported");
+ }
+
+ if(!policy().acceptable_protocol_version(state.version()))
+ {
+ throw TLS_Exception(Alert::PROTOCOL_VERSION,
+ "Server version " + state.version().to_string() +
+ " is unacceptable by policy");
+ }
+
+ if(state.ciphersuite().signature_used() || state.ciphersuite().kex_method() == Kex_Algo::STATIC_RSA)
+ {
+ state.set_expected_next(CERTIFICATE);
+ }
+ else if(state.ciphersuite().kex_method() == Kex_Algo::PSK)
+ {
+ /* PSK is anonymous so no certificate/cert req message is
+ ever sent. The server may or may not send a server kex,
+ depending on if it has an identity hint for us.
+
+ (EC)DHE_PSK always sends a server key exchange for the
+ DH exchange portion, and is covered by block below
+ */
+
+ state.set_expected_next(SERVER_KEX);
+ state.set_expected_next(SERVER_HELLO_DONE);
+ }
+ else if(state.ciphersuite().kex_method() != Kex_Algo::STATIC_RSA)
+ {
+ state.set_expected_next(SERVER_KEX);
+ }
+ else
+ {
+ state.set_expected_next(CERTIFICATE_REQUEST); // optional
+ state.set_expected_next(SERVER_HELLO_DONE);
+ }
+ }
+ }
+ else if(type == CERTIFICATE)
+ {
+ state.server_certs(new Certificate(contents, policy()));
+
+ const std::vector<X509_Certificate>& server_certs =
+ state.server_certs()->cert_chain();
+
+ if(server_certs.empty())
+ throw TLS_Exception(Alert::HANDSHAKE_FAILURE,
+ "Client: No certificates sent by server");
+
+ /*
+ If the server supports certificate status messages,
+ certificate verification happens after we receive the server hello done,
+ in case an OCSP response was also available
+ */
+
+ 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(current_cert != server_cert)
+ throw TLS_Exception(Alert::BAD_CERTIFICATE, "Server certificate changed during renegotiation");
+ }
+
+ std::unique_ptr<Public_Key> peer_key(server_cert.subject_public_key());
+
+ const std::string expected_key_type =
+ state.ciphersuite().signature_used() ? state.ciphersuite().sig_algo() : "RSA";
+
+ if(peer_key->algo_name() != expected_key_type)
+ throw TLS_Exception(Alert::ILLEGAL_PARAMETER,
+ "Certificate key type did not match ciphersuite");
+
+ if(!key_usage_matches_ciphersuite(server_cert.constraints(), state.ciphersuite()))
+ throw TLS_Exception(Alert::BAD_CERTIFICATE,
+ "Certificate usage constraints do not allow this ciphersuite");
+
+ state.server_public_key.reset(peer_key.release());
+
+ if(state.ciphersuite().kex_method() != Kex_Algo::STATIC_RSA)
+ {
+ state.set_expected_next(SERVER_KEX);
+ }
+ else
+ {
+ state.set_expected_next(CERTIFICATE_REQUEST); // optional
+ state.set_expected_next(SERVER_HELLO_DONE);
+ }
+
+ if(state.server_hello()->supports_certificate_status_message())
+ {
+ state.set_expected_next(CERTIFICATE_STATUS); // optional
+ }
+ else
+ {
+ try
+ {
+ auto trusted_CAs = m_creds.trusted_certificate_authorities("tls-client", m_info.hostname());
+
+ callbacks().tls_verify_cert_chain(server_certs,
+ {},
+ trusted_CAs,
+ Usage_Type::TLS_SERVER_AUTH,
+ m_info.hostname(),
+ policy());
+ }
+ catch(TLS_Exception&)
+ {
+ throw;
+ }
+ catch(std::exception& e)
+ {
+ throw TLS_Exception(Alert::INTERNAL_ERROR, e.what());
+ }
+ }
+ }
+ else if(type == CERTIFICATE_STATUS)
+ {
+ state.server_cert_status(new Certificate_Status(contents));
+
+ if(state.ciphersuite().kex_method() != Kex_Algo::STATIC_RSA)
+ {
+ state.set_expected_next(SERVER_KEX);
+ }
+ else
+ {
+ state.set_expected_next(CERTIFICATE_REQUEST); // optional
+ state.set_expected_next(SERVER_HELLO_DONE);
+ }
+ }
+ else if(type == SERVER_KEX)
+ {
+ if(state.ciphersuite().psk_ciphersuite() == false)
+ state.set_expected_next(CERTIFICATE_REQUEST); // optional
+ state.set_expected_next(SERVER_HELLO_DONE);
+
+ state.server_kex(
+ new Server_Key_Exchange(contents,
+ state.ciphersuite().kex_method(),
+ state.ciphersuite().auth_method(),
+ state.version())
+ );
+
+ if(state.ciphersuite().signature_used())
+ {
+ const Public_Key& server_key = state.get_server_public_key();
+
+ if(!state.server_kex()->verify(server_key, state, policy()))
+ {
+ throw TLS_Exception(Alert::DECRYPT_ERROR,
+ "Bad signature on server key exchange");
+ }
+ }
+ }
+ else if(type == CERTIFICATE_REQUEST)
+ {
+ state.set_expected_next(SERVER_HELLO_DONE);
+ state.cert_req(new Certificate_Req(contents, state.version()));
+ }
+ else if(type == SERVER_HELLO_DONE)
+ {
+ state.server_hello_done(new Server_Hello_Done(contents));
+
+ if(state.server_certs() != nullptr &&
+ state.server_hello()->supports_certificate_status_message())
+ {
+ try
+ {
+ auto trusted_CAs = m_creds.trusted_certificate_authorities("tls-client", m_info.hostname());
+
+ std::vector<std::shared_ptr<const OCSP::Response>> ocsp;
+ if(state.server_cert_status() != nullptr)
+ {
+ try {
+ ocsp.push_back(std::make_shared<OCSP::Response>(state.server_cert_status()->response()));
+ }
+ catch(Decoding_Error&)
+ {
+ // ignore it here because it might be our fault
+ }
+ }
+
+ callbacks().tls_verify_cert_chain(state.server_certs()->cert_chain(),
+ ocsp,
+ trusted_CAs,
+ Usage_Type::TLS_SERVER_AUTH,
+ m_info.hostname(),
+ policy());
+ }
+ catch(TLS_Exception&)
+ {
+ throw;
+ }
+ catch(std::exception& e)
+ {
+ throw TLS_Exception(Alert::INTERNAL_ERROR, e.what());
+ }
+ }
+
+ if(state.received_handshake_msg(CERTIFICATE_REQUEST))
+ {
+ const auto& types = state.cert_req()->acceptable_cert_types();
+
+ std::vector<X509_Certificate> client_certs =
+ m_creds.find_cert_chain(types,
+ state.cert_req()->acceptable_CAs(),
+ "tls-client",
+ m_info.hostname());
+
+ state.client_certs(new Certificate(state.handshake_io(),
+ state.hash(),
+ client_certs));
+ }
+
+ state.client_kex(
+ new Client_Key_Exchange(state.handshake_io(),
+ state,
+ policy(),
+ m_creds,
+ state.server_public_key.get(),
+ m_info.hostname(),
+ rng())
+ );
+
+ state.compute_session_keys();
+
+ if(state.received_handshake_msg(CERTIFICATE_REQUEST) &&
+ !state.client_certs()->empty())
+ {
+ Private_Key* private_key =
+ m_creds.private_key_for(state.client_certs()->cert_chain()[0],
+ "tls-client",
+ m_info.hostname());
+
+ state.client_verify(
+ new Certificate_Verify(state.handshake_io(),
+ state,
+ policy(),
+ rng(),
+ private_key)
+ );
+ }
+
+ state.handshake_io().send(Change_Cipher_Spec());
+
+ change_cipher_spec_writer(CLIENT);
+
+ state.client_finished(new Finished(state.handshake_io(), state, CLIENT));
+
+ if(state.server_hello()->supports_session_ticket())
+ state.set_expected_next(NEW_SESSION_TICKET);
+ else
+ state.set_expected_next(HANDSHAKE_CCS);
+ }
+ else if(type == NEW_SESSION_TICKET)
+ {
+ state.new_session_ticket(new New_Session_Ticket(contents));
+
+ state.set_expected_next(HANDSHAKE_CCS);
+ }
+ else if(type == HANDSHAKE_CCS)
+ {
+ state.set_expected_next(FINISHED);
+
+ change_cipher_spec_reader(CLIENT);
+ }
+ else if(type == FINISHED)
+ {
+ state.server_finished(new Finished(contents));
+
+ if(!state.server_finished()->verify(state, SERVER))
+ throw TLS_Exception(Alert::DECRYPT_ERROR,
+ "Finished message didn't verify");
+
+ state.hash().update(state.handshake_io().format(contents, type));
+
+ if(!state.client_finished()) // session resume case
+ {
+ state.handshake_io().send(Change_Cipher_Spec());
+ change_cipher_spec_writer(CLIENT);
+ state.client_finished(new Finished(state.handshake_io(), state, CLIENT));
+ }
+
+ std::vector<uint8_t> session_id = state.server_hello()->session_id();
+
+ const std::vector<uint8_t>& session_ticket = state.session_ticket();
+
+ if(session_id.empty() && !session_ticket.empty())
+ session_id = make_hello_random(rng(), policy());
+
+ Session session_info(
+ session_id,
+ state.session_keys().master_secret(),
+ state.server_hello()->version(),
+ state.server_hello()->ciphersuite(),
+ CLIENT,
+ state.server_hello()->supports_extended_master_secret(),
+ state.server_hello()->supports_encrypt_then_mac(),
+ get_peer_cert_chain(state),
+ session_ticket,
+ m_info,
+ "",
+ state.server_hello()->srtp_profile()
+ );
+
+ const bool should_save = save_session(session_info);
+
+ if(session_id.size() > 0 && state.is_a_resumption() == false)
+ {
+ if(should_save)
+ session_manager().save(session_info);
+ else
+ session_manager().remove_entry(session_info.session_id());
+ }
+
+ activate_session();
+ }
+ else
+ throw Unexpected_Message("Unknown handshake message received");
+ }
+
+}
+
+}
diff --git a/comm/third_party/botan/src/lib/tls/tls_client.h b/comm/third_party/botan/src/lib/tls/tls_client.h
new file mode 100644
index 0000000000..0e08b45953
--- /dev/null
+++ b/comm/third_party/botan/src/lib/tls/tls_client.h
@@ -0,0 +1,169 @@
+/*
+* TLS Client
+* (C) 2004-2011 Jack Lloyd
+* 2016 Matthias Gierlings
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_TLS_CLIENT_H_
+#define BOTAN_TLS_CLIENT_H_
+
+#include <botan/tls_channel.h>
+#include <botan/tls_policy.h>
+#include <botan/credentials_manager.h>
+#include <vector>
+
+namespace Botan {
+
+namespace TLS {
+
+/**
+* SSL/TLS Client
+*/
+class BOTAN_PUBLIC_API(2,0) Client final : public Channel
+ {
+ public:
+
+ /**
+ * Set up a new TLS client session
+ *
+ * @param callbacks contains a set of callback function references
+ * required by the TLS client.
+ *
+ * @param session_manager manages session state
+ *
+ * @param creds manages application/user credentials
+ *
+ * @param policy specifies other connection policy information
+ *
+ * @param rng a random number generator
+ *
+ * @param server_info is identifying information about the TLS server
+ *
+ * @param offer_version specifies which version we will offer
+ * to the TLS server.
+ *
+ * @param next_protocols specifies protocols to advertise with ALPN
+ *
+ * @param reserved_io_buffer_size This many bytes of memory will
+ * be preallocated for the read and write buffers. Smaller
+ * values just mean reallocations and copies are more likely.
+ */
+ Client(Callbacks& callbacks,
+ Session_Manager& session_manager,
+ Credentials_Manager& creds,
+ const Policy& policy,
+ RandomNumberGenerator& rng,
+ const Server_Information& server_info = Server_Information(),
+ const Protocol_Version& offer_version = Protocol_Version::latest_tls_version(),
+ const std::vector<std::string>& next_protocols = {},
+ size_t reserved_io_buffer_size = TLS::Client::IO_BUF_DEFAULT_SIZE
+ );
+
+ /**
+ * DEPRECATED. This constructor is only provided for backward
+ * compatibility and should not be used in new code. It will be
+ * removed in a future release.
+ *
+ * Set up a new TLS client session
+ *
+ * @param data_output_fn is called with data for the outbound socket
+ *
+ * @param app_data_cb is called when new application data is received
+ *
+ * @param recv_alert_cb is called when a TLS alert is received
+ *
+ * @param hs_cb is called when a handshake is completed
+ *
+ * @param session_manager manages session state
+ *
+ * @param creds manages application/user credentials
+ *
+ * @param policy specifies other connection policy information
+ *
+ * @param rng a random number generator
+ *
+ * @param server_info is identifying information about the TLS server
+ *
+ * @param offer_version specifies which version we will offer
+ * to the TLS server.
+ *
+ * @param next_protocols specifies protocols to advertise with ALPN
+ *
+ * @param reserved_io_buffer_size This many bytes of memory will
+ * be preallocated for the read and write buffers. Smaller
+ * values just mean reallocations and copies are more likely.
+ */
+ BOTAN_DEPRECATED("Use TLS::Client(TLS::Callbacks ...)")
+ Client(output_fn data_output_fn,
+ data_cb app_data_cb,
+ alert_cb recv_alert_cb,
+ handshake_cb hs_cb,
+ Session_Manager& session_manager,
+ Credentials_Manager& creds,
+ const Policy& policy,
+ RandomNumberGenerator& rng,
+ const Server_Information& server_info = Server_Information(),
+ const Protocol_Version& offer_version = Protocol_Version::latest_tls_version(),
+ const std::vector<std::string>& next_protocols = {},
+ size_t reserved_io_buffer_size = TLS::Client::IO_BUF_DEFAULT_SIZE
+ );
+
+ /**
+ * DEPRECATED. This constructor is only provided for backward
+ * compatibility and should not be used in new implementations.
+ */
+ BOTAN_DEPRECATED("Use TLS::Client(TLS::Callbacks ...)")
+ Client(output_fn out,
+ data_cb app_data_cb,
+ alert_cb alert_cb,
+ handshake_cb hs_cb,
+ handshake_msg_cb hs_msg_cb,
+ Session_Manager& session_manager,
+ Credentials_Manager& creds,
+ const Policy& policy,
+ RandomNumberGenerator& rng,
+ const Server_Information& server_info = Server_Information(),
+ const Protocol_Version& offer_version = Protocol_Version::latest_tls_version(),
+ const std::vector<std::string>& next_protocols = {}
+ );
+
+ /**
+ * @return network protocol as advertised by the TLS server, if server sent the ALPN extension
+ */
+ std::string application_protocol() const override { return m_application_protocol; }
+ private:
+ void init(const Protocol_Version& protocol_version,
+ const std::vector<std::string>& next_protocols);
+
+ std::vector<X509_Certificate>
+ 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,
+ bool force_full_renegotiation,
+ Protocol_Version version,
+ const std::string& srp_identifier = "",
+ const std::vector<std::string>& next_protocols = {});
+
+ void process_handshake_msg(const Handshake_State* active_state,
+ Handshake_State& pending_state,
+ Handshake_Type type,
+ const std::vector<uint8_t>& contents,
+ bool epoch0_restart) override;
+
+ Handshake_State* new_handshake_state(Handshake_IO* io) override;
+
+ Credentials_Manager& m_creds;
+ const Server_Information m_info;
+ std::string m_application_protocol;
+ };
+
+}
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/tls/tls_exceptn.h b/comm/third_party/botan/src/lib/tls/tls_exceptn.h
new file mode 100644
index 0000000000..e7d8c19635
--- /dev/null
+++ b/comm/third_party/botan/src/lib/tls/tls_exceptn.h
@@ -0,0 +1,52 @@
+/*
+* Exceptions
+* (C) 2004-2006 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_TLS_EXCEPTION_H_
+#define BOTAN_TLS_EXCEPTION_H_
+
+#include <botan/exceptn.h>
+#include <botan/tls_alert.h>
+
+namespace Botan {
+
+namespace TLS {
+
+/**
+* TLS Exception Base Class
+*/
+class BOTAN_PUBLIC_API(2,0) TLS_Exception : public Exception
+ {
+ public:
+ Alert::Type type() const { return m_alert_type; }
+
+ TLS_Exception(Alert::Type type,
+ const std::string& err_msg = "Unknown error") :
+ Exception(err_msg), m_alert_type(type) {}
+
+ int error_code() const noexcept override { return static_cast<int>(m_alert_type); }
+
+ ErrorType error_type() const noexcept override { return ErrorType::TLSError; }
+
+ private:
+ Alert::Type m_alert_type;
+ };
+
+/**
+* Unexpected_Message Exception
+*/
+class BOTAN_PUBLIC_API(2,0) Unexpected_Message final : public TLS_Exception
+ {
+ public:
+ explicit Unexpected_Message(const std::string& err) :
+ TLS_Exception(Alert::UNEXPECTED_MESSAGE, err) {}
+ };
+
+}
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/tls/tls_extensions.cpp b/comm/third_party/botan/src/lib/tls/tls_extensions.cpp
new file mode 100644
index 0000000000..631868703f
--- /dev/null
+++ b/comm/third_party/botan/src/lib/tls/tls_extensions.cpp
@@ -0,0 +1,660 @@
+/*
+* TLS Extensions
+* (C) 2011,2012,2015,2016 Jack Lloyd
+* 2016 Juraj Somorovsky
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/tls_extensions.h>
+#include <botan/internal/tls_reader.h>
+#include <botan/tls_exceptn.h>
+#include <botan/tls_policy.h>
+
+namespace Botan {
+
+namespace TLS {
+
+namespace {
+
+Extension* make_extension(TLS_Data_Reader& reader, uint16_t code, uint16_t size, Connection_Side from)
+ {
+ switch(code)
+ {
+ case TLSEXT_SERVER_NAME_INDICATION:
+ return new Server_Name_Indicator(reader, size);
+
+#if defined(BOTAN_HAS_SRP6)
+ case TLSEXT_SRP_IDENTIFIER:
+ return new SRP_Identifier(reader, size);
+#endif
+
+ case TLSEXT_SUPPORTED_GROUPS:
+ return new Supported_Groups(reader, size);
+
+ case TLSEXT_CERT_STATUS_REQUEST:
+ return new Certificate_Status_Request(reader, size, from);
+
+ case TLSEXT_EC_POINT_FORMATS:
+ return new Supported_Point_Formats(reader, size);
+
+ case TLSEXT_SAFE_RENEGOTIATION:
+ return new Renegotiation_Extension(reader, size);
+
+ case TLSEXT_SIGNATURE_ALGORITHMS:
+ return new Signature_Algorithms(reader, size);
+
+ case TLSEXT_USE_SRTP:
+ return new SRTP_Protection_Profiles(reader, size);
+
+ case TLSEXT_ALPN:
+ return new Application_Layer_Protocol_Notification(reader, size);
+
+ case TLSEXT_EXTENDED_MASTER_SECRET:
+ return new Extended_Master_Secret(reader, size);
+
+ case TLSEXT_ENCRYPT_THEN_MAC:
+ return new Encrypt_then_MAC(reader, size);
+
+ case TLSEXT_SESSION_TICKET:
+ return new Session_Ticket(reader, size);
+
+ case TLSEXT_SUPPORTED_VERSIONS:
+ return new Supported_Versions(reader, size, from);
+ }
+
+ return new Unknown_Extension(static_cast<Handshake_Extension_Type>(code),
+ reader, size);
+ }
+
+}
+
+void Extensions::deserialize(TLS_Data_Reader& reader, Connection_Side from)
+ {
+ if(reader.has_remaining())
+ {
+ const uint16_t all_extn_size = reader.get_uint16_t();
+
+ if(reader.remaining_bytes() != all_extn_size)
+ throw Decoding_Error("Bad extension size");
+
+ while(reader.has_remaining())
+ {
+ const uint16_t extension_code = reader.get_uint16_t();
+ const uint16_t extension_size = reader.get_uint16_t();
+
+ const auto type = static_cast<Handshake_Extension_Type>(extension_code);
+
+ if(m_extensions.find(type) != m_extensions.end())
+ throw TLS_Exception(TLS::Alert::DECODE_ERROR,
+ "Peer sent duplicated extensions");
+
+ Extension* extn = make_extension(
+ reader, extension_code, extension_size, from);
+
+ this->add(extn);
+ }
+ }
+ }
+
+std::vector<uint8_t> Extensions::serialize(Connection_Side whoami) const
+ {
+ std::vector<uint8_t> buf(2); // 2 bytes for length field
+
+ for(auto& extn : m_extensions)
+ {
+ if(extn.second->empty())
+ continue;
+
+ const uint16_t extn_code = static_cast<uint16_t>(extn.second->type());
+
+ const std::vector<uint8_t> extn_val = extn.second->serialize(whoami);
+
+ buf.push_back(get_byte(0, extn_code));
+ buf.push_back(get_byte(1, extn_code));
+
+ buf.push_back(get_byte(0, static_cast<uint16_t>(extn_val.size())));
+ buf.push_back(get_byte(1, static_cast<uint16_t>(extn_val.size())));
+
+ buf += extn_val;
+ }
+
+ const uint16_t extn_size = static_cast<uint16_t>(buf.size() - 2);
+
+ buf[0] = get_byte(0, extn_size);
+ buf[1] = get_byte(1, extn_size);
+
+ // avoid sending a completely empty extensions block
+ if(buf.size() == 2)
+ return std::vector<uint8_t>();
+
+ return buf;
+ }
+
+bool Extensions::remove_extension(Handshake_Extension_Type typ)
+ {
+ auto i = m_extensions.find(typ);
+ if(i == m_extensions.end())
+ return false;
+ m_extensions.erase(i);
+ return true;
+ }
+
+std::set<Handshake_Extension_Type> Extensions::extension_types() const
+ {
+ std::set<Handshake_Extension_Type> offers;
+ for(auto i = m_extensions.begin(); i != m_extensions.end(); ++i)
+ offers.insert(i->first);
+ return offers;
+ }
+
+Unknown_Extension::Unknown_Extension(Handshake_Extension_Type type,
+ TLS_Data_Reader& reader,
+ uint16_t extension_size) :
+ m_type(type),
+ m_value(reader.get_fixed<uint8_t>(extension_size))
+ {
+ }
+
+std::vector<uint8_t> Unknown_Extension::serialize(Connection_Side /*whoami*/) const
+ {
+ throw Invalid_State("Cannot encode an unknown TLS extension");
+ }
+
+Server_Name_Indicator::Server_Name_Indicator(TLS_Data_Reader& reader,
+ uint16_t extension_size)
+ {
+ /*
+ * This is used by the server to confirm that it knew the name
+ */
+ if(extension_size == 0)
+ return;
+
+ uint16_t name_bytes = reader.get_uint16_t();
+
+ if(name_bytes + 2 != extension_size)
+ throw Decoding_Error("Bad encoding of SNI extension");
+
+ while(name_bytes)
+ {
+ uint8_t name_type = reader.get_byte();
+ name_bytes--;
+
+ if(name_type == 0) // DNS
+ {
+ m_sni_host_name = reader.get_string(2, 1, 65535);
+ name_bytes -= static_cast<uint16_t>(2 + m_sni_host_name.size());
+ }
+ else // some other unknown name type
+ {
+ reader.discard_next(name_bytes);
+ name_bytes = 0;
+ }
+ }
+ }
+
+std::vector<uint8_t> Server_Name_Indicator::serialize(Connection_Side /*whoami*/) const
+ {
+ std::vector<uint8_t> buf;
+
+ size_t name_len = m_sni_host_name.size();
+
+ buf.push_back(get_byte(0, static_cast<uint16_t>(name_len+3)));
+ buf.push_back(get_byte(1, static_cast<uint16_t>(name_len+3)));
+ buf.push_back(0); // DNS
+
+ buf.push_back(get_byte(0, static_cast<uint16_t>(name_len)));
+ buf.push_back(get_byte(1, static_cast<uint16_t>(name_len)));
+
+ buf += std::make_pair(
+ cast_char_ptr_to_uint8(m_sni_host_name.data()),
+ m_sni_host_name.size());
+
+ return buf;
+ }
+
+#if defined(BOTAN_HAS_SRP6)
+
+SRP_Identifier::SRP_Identifier(TLS_Data_Reader& reader,
+ uint16_t extension_size) : m_srp_identifier(reader.get_string(1, 1, 255))
+ {
+ if(m_srp_identifier.size() + 1 != extension_size)
+ throw Decoding_Error("Bad encoding for SRP identifier extension");
+ }
+
+std::vector<uint8_t> SRP_Identifier::serialize(Connection_Side /*whoami*/) const
+ {
+ std::vector<uint8_t> buf;
+
+ const uint8_t* srp_bytes = cast_char_ptr_to_uint8(m_srp_identifier.data());
+ append_tls_length_value(buf, srp_bytes, m_srp_identifier.size(), 1);
+
+ return buf;
+ }
+
+#endif
+
+Renegotiation_Extension::Renegotiation_Extension(TLS_Data_Reader& reader,
+ uint16_t extension_size) : m_reneg_data(reader.get_range<uint8_t>(1, 0, 255))
+ {
+ if(m_reneg_data.size() + 1 != extension_size)
+ throw Decoding_Error("Bad encoding for secure renegotiation extn");
+ }
+
+std::vector<uint8_t> Renegotiation_Extension::serialize(Connection_Side /*whoami*/) const
+ {
+ std::vector<uint8_t> buf;
+ append_tls_length_value(buf, m_reneg_data, 1);
+ return buf;
+ }
+
+Application_Layer_Protocol_Notification::Application_Layer_Protocol_Notification(TLS_Data_Reader& reader,
+ uint16_t extension_size)
+ {
+ if(extension_size == 0)
+ return; // empty extension
+
+ const uint16_t name_bytes = reader.get_uint16_t();
+
+ size_t bytes_remaining = extension_size - 2;
+
+ if(name_bytes != bytes_remaining)
+ throw Decoding_Error("Bad encoding of ALPN extension, bad length field");
+
+ while(bytes_remaining)
+ {
+ const std::string p = reader.get_string(1, 0, 255);
+
+ if(bytes_remaining < p.size() + 1)
+ throw Decoding_Error("Bad encoding of ALPN, length field too long");
+
+ if(p.empty())
+ throw Decoding_Error("Empty ALPN protocol not allowed");
+
+ bytes_remaining -= (p.size() + 1);
+
+ m_protocols.push_back(p);
+ }
+ }
+
+const std::string& Application_Layer_Protocol_Notification::single_protocol() const
+ {
+ if(m_protocols.size() != 1)
+ throw TLS_Exception(Alert::HANDSHAKE_FAILURE,
+ "Server sent " + std::to_string(m_protocols.size()) +
+ " protocols in ALPN extension response");
+ return m_protocols[0];
+ }
+
+std::vector<uint8_t> Application_Layer_Protocol_Notification::serialize(Connection_Side /*whoami*/) const
+ {
+ std::vector<uint8_t> buf(2);
+
+ for(auto&& p: m_protocols)
+ {
+ if(p.length() >= 256)
+ throw TLS_Exception(Alert::INTERNAL_ERROR, "ALPN name too long");
+ if(p != "")
+ append_tls_length_value(buf,
+ cast_char_ptr_to_uint8(p.data()),
+ p.size(),
+ 1);
+ }
+
+ buf[0] = get_byte(0, static_cast<uint16_t>(buf.size()-2));
+ buf[1] = get_byte(1, static_cast<uint16_t>(buf.size()-2));
+
+ return buf;
+ }
+
+Supported_Groups::Supported_Groups(const std::vector<Group_Params>& groups) : m_groups(groups)
+ {
+ }
+
+std::vector<Group_Params> Supported_Groups::ec_groups() const
+ {
+ std::vector<Group_Params> ec;
+ for(auto g : m_groups)
+ {
+ if(group_param_is_dh(g) == false)
+ ec.push_back(g);
+ }
+ return ec;
+ }
+
+std::vector<Group_Params> Supported_Groups::dh_groups() const
+ {
+ std::vector<Group_Params> dh;
+ for(auto g : m_groups)
+ {
+ if(group_param_is_dh(g) == true)
+ dh.push_back(g);
+ }
+ return dh;
+ }
+
+std::vector<uint8_t> Supported_Groups::serialize(Connection_Side /*whoami*/) const
+ {
+ std::vector<uint8_t> buf(2);
+
+ for(auto g : m_groups)
+ {
+ const uint16_t id = static_cast<uint16_t>(g);
+
+ if(id > 0)
+ {
+ buf.push_back(get_byte(0, id));
+ buf.push_back(get_byte(1, id));
+ }
+ }
+
+ buf[0] = get_byte(0, static_cast<uint16_t>(buf.size()-2));
+ buf[1] = get_byte(1, static_cast<uint16_t>(buf.size()-2));
+
+ return buf;
+ }
+
+Supported_Groups::Supported_Groups(TLS_Data_Reader& reader,
+ uint16_t extension_size)
+ {
+ const uint16_t len = reader.get_uint16_t();
+
+ if(len + 2 != extension_size)
+ throw Decoding_Error("Inconsistent length field in supported groups list");
+
+ if(len % 2 == 1)
+ throw Decoding_Error("Supported groups list of strange size");
+
+ const size_t elems = len / 2;
+
+ for(size_t i = 0; i != elems; ++i)
+ {
+ const uint16_t id = reader.get_uint16_t();
+ m_groups.push_back(static_cast<Group_Params>(id));
+ }
+ }
+
+std::vector<uint8_t> 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<uint8_t>{2, ANSIX962_COMPRESSED_PRIME, UNCOMPRESSED};
+ }
+ else
+ {
+ return std::vector<uint8_t>{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");
+
+ for(size_t i = 0; i != len; ++i)
+ {
+ uint8_t format = reader.get_byte();
+
+ if(static_cast<ECPointFormat>(format) == UNCOMPRESSED)
+ {
+ m_prefers_compressed = false;
+ reader.discard_next(len-i-1);
+ return;
+ }
+ else if(static_cast<ECPointFormat>(format) == ANSIX962_COMPRESSED_PRIME)
+ {
+ m_prefers_compressed = true;
+ reader.discard_next(len-i-1);
+ return;
+ }
+
+ // ignore ANSIX962_COMPRESSED_CHAR2, we don't support these curves
+ }
+ }
+
+std::vector<uint8_t> Signature_Algorithms::serialize(Connection_Side /*whoami*/) const
+ {
+ BOTAN_ASSERT(m_schemes.size() < 256, "Too many signature schemes");
+
+ std::vector<uint8_t> buf;
+
+ const uint16_t len = static_cast<uint16_t>(m_schemes.size() * 2);
+
+ buf.push_back(get_byte(0, len));
+ buf.push_back(get_byte(1, len));
+
+ for(Signature_Scheme scheme : m_schemes)
+ {
+ const uint16_t scheme_code = static_cast<uint16_t>(scheme);
+
+ buf.push_back(get_byte(0, scheme_code));
+ buf.push_back(get_byte(1, scheme_code));
+ }
+
+ return buf;
+ }
+
+Signature_Algorithms::Signature_Algorithms(TLS_Data_Reader& reader,
+ uint16_t extension_size)
+ {
+ uint16_t len = reader.get_uint16_t();
+
+ if(len + 2 != extension_size || len % 2 == 1 || len == 0)
+ {
+ throw Decoding_Error("Bad encoding on signature algorithms extension");
+ }
+
+ while(len)
+ {
+ const uint16_t scheme_code = reader.get_uint16_t();
+ m_schemes.push_back(static_cast<Signature_Scheme>(scheme_code));
+ len -= 2;
+ }
+ }
+
+Session_Ticket::Session_Ticket(TLS_Data_Reader& reader,
+ uint16_t extension_size) : m_ticket(reader.get_elem<uint8_t, std::vector<uint8_t>>(extension_size))
+ {}
+
+SRTP_Protection_Profiles::SRTP_Protection_Profiles(TLS_Data_Reader& reader,
+ uint16_t extension_size) : m_pp(reader.get_range<uint16_t>(2, 0, 65535))
+ {
+ const std::vector<uint8_t> mki = reader.get_range<uint8_t>(1, 0, 255);
+
+ if(m_pp.size() * 2 + mki.size() + 3 != extension_size)
+ throw Decoding_Error("Bad encoding for SRTP protection extension");
+
+ if(!mki.empty())
+ throw Decoding_Error("Unhandled non-empty MKI for SRTP protection extension");
+ }
+
+std::vector<uint8_t> SRTP_Protection_Profiles::serialize(Connection_Side /*whoami*/) const
+ {
+ std::vector<uint8_t> buf;
+
+ const uint16_t pp_len = static_cast<uint16_t>(m_pp.size() * 2);
+ buf.push_back(get_byte(0, pp_len));
+ buf.push_back(get_byte(1, pp_len));
+
+ for(uint16_t pp : m_pp)
+ {
+ buf.push_back(get_byte(0, pp));
+ buf.push_back(get_byte(1, pp));
+ }
+
+ buf.push_back(0); // srtp_mki, always empty here
+
+ return buf;
+ }
+
+Extended_Master_Secret::Extended_Master_Secret(TLS_Data_Reader&,
+ uint16_t extension_size)
+ {
+ if(extension_size != 0)
+ throw Decoding_Error("Invalid extended_master_secret extension");
+ }
+
+std::vector<uint8_t> Extended_Master_Secret::serialize(Connection_Side /*whoami*/) const
+ {
+ return std::vector<uint8_t>();
+ }
+
+Encrypt_then_MAC::Encrypt_then_MAC(TLS_Data_Reader&,
+ uint16_t extension_size)
+ {
+ if(extension_size != 0)
+ throw Decoding_Error("Invalid encrypt_then_mac extension");
+ }
+
+std::vector<uint8_t> Encrypt_then_MAC::serialize(Connection_Side /*whoami*/) const
+ {
+ return std::vector<uint8_t>();
+ }
+
+std::vector<uint8_t> Certificate_Status_Request::serialize(Connection_Side whoami) const
+ {
+ std::vector<uint8_t> buf;
+
+ if(whoami == Connection_Side::SERVER)
+ return buf; // server reply is empty
+
+ /*
+ opaque ResponderID<1..2^16-1>;
+ opaque Extensions<0..2^16-1>;
+
+ CertificateStatusType status_type = ocsp(1)
+ ResponderID responder_id_list<0..2^16-1>
+ Extensions request_extensions;
+ */
+
+ buf.push_back(1); // CertificateStatusType ocsp
+
+ buf.push_back(0);
+ buf.push_back(0);
+ buf.push_back(0);
+ buf.push_back(0);
+
+ return buf;
+ }
+
+Certificate_Status_Request::Certificate_Status_Request(TLS_Data_Reader& reader,
+ uint16_t extension_size,
+ Connection_Side from)
+ {
+ if(from == Connection_Side::SERVER)
+ {
+ if(extension_size != 0)
+ throw Decoding_Error("Server sent non-empty Certificate_Status_Request extension");
+ }
+ else if(extension_size > 0)
+ {
+ const uint8_t type = reader.get_byte();
+ if(type == 1)
+ {
+ const size_t len_resp_id_list = reader.get_uint16_t();
+ m_ocsp_names = reader.get_fixed<uint8_t>(len_resp_id_list);
+ const size_t len_requ_ext = reader.get_uint16_t();
+ m_extension_bytes = reader.get_fixed<uint8_t>(len_requ_ext);
+ }
+ else
+ {
+ reader.discard_next(extension_size - 1);
+ }
+ }
+ }
+
+Certificate_Status_Request::Certificate_Status_Request(const std::vector<uint8_t>& ocsp_responder_ids,
+ const std::vector<std::vector<uint8_t>>& ocsp_key_ids) :
+ m_ocsp_names(ocsp_responder_ids),
+ m_ocsp_keys(ocsp_key_ids)
+ {
+ }
+
+std::vector<uint8_t> Supported_Versions::serialize(Connection_Side whoami) const
+ {
+ std::vector<uint8_t> buf;
+
+ if(whoami == Connection_Side::SERVER)
+ {
+ BOTAN_ASSERT_NOMSG(m_versions.size() == 1);
+ buf.push_back(m_versions[0].major_version());
+ buf.push_back(m_versions[0].minor_version());
+ }
+ else
+ {
+ BOTAN_ASSERT_NOMSG(m_versions.size() >= 1);
+ const uint8_t len = static_cast<uint8_t>(m_versions.size() * 2);
+
+ buf.push_back(len);
+
+ for(Protocol_Version version : m_versions)
+ {
+ buf.push_back(get_byte(0, version.major_version()));
+ buf.push_back(get_byte(1, version.minor_version()));
+ }
+ }
+
+ return buf;
+ }
+
+Supported_Versions::Supported_Versions(Protocol_Version offer, const Policy& policy)
+ {
+ if(offer.is_datagram_protocol())
+ {
+ if(offer >= Protocol_Version::DTLS_V12 && policy.allow_dtls12())
+ m_versions.push_back(Protocol_Version::DTLS_V12);
+#if defined(BOTAN_HAS_TLS_V10)
+ if(offer >= Protocol_Version::DTLS_V10 && policy.allow_dtls10())
+ m_versions.push_back(Protocol_Version::DTLS_V10);
+#endif
+ }
+ else
+ {
+ if(offer >= Protocol_Version::TLS_V12 && policy.allow_tls12())
+ m_versions.push_back(Protocol_Version::TLS_V12);
+#if defined(BOTAN_HAS_TLS_V10)
+ if(offer >= Protocol_Version::TLS_V11 && policy.allow_tls11())
+ m_versions.push_back(Protocol_Version::TLS_V11);
+ if(offer >= Protocol_Version::TLS_V10 && policy.allow_tls10())
+ m_versions.push_back(Protocol_Version::TLS_V10);
+#endif
+ }
+ }
+
+Supported_Versions::Supported_Versions(TLS_Data_Reader& reader,
+ uint16_t extension_size,
+ Connection_Side from)
+ {
+ if(from == Connection_Side::SERVER)
+ {
+ if(extension_size != 2)
+ throw Decoding_Error("Server sent invalid supported_versions extension");
+ m_versions.push_back(Protocol_Version(reader.get_uint16_t()));
+ }
+ else
+ {
+ auto versions = reader.get_range<uint16_t>(1, 1, 127);
+
+ for(auto v : versions)
+ m_versions.push_back(Protocol_Version(v));
+
+ if(extension_size != 1+2*versions.size())
+ throw Decoding_Error("Client sent invalid supported_versions extension");
+ }
+ }
+
+bool Supported_Versions::supports(Protocol_Version version) const
+ {
+ for(auto v : m_versions)
+ if(version == v)
+ return true;
+ return false;
+ }
+
+}
+
+}
diff --git a/comm/third_party/botan/src/lib/tls/tls_extensions.h b/comm/third_party/botan/src/lib/tls/tls_extensions.h
new file mode 100644
index 0000000000..a426c8e56f
--- /dev/null
+++ b/comm/third_party/botan/src/lib/tls/tls_extensions.h
@@ -0,0 +1,551 @@
+/*
+* TLS Extensions
+* (C) 2011,2012,2016,2018,2019 Jack Lloyd
+* (C) 2016 Juraj Somorovsky
+* (C) 2016 Matthias Gierlings
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_TLS_EXTENSIONS_H_
+#define BOTAN_TLS_EXTENSIONS_H_
+
+#include <botan/tls_algos.h>
+#include <botan/tls_magic.h>
+#include <botan/tls_version.h>
+#include <botan/secmem.h>
+#include <botan/pkix_types.h>
+#include <vector>
+#include <string>
+#include <map>
+#include <set>
+
+namespace Botan {
+
+namespace TLS {
+
+class Policy;
+
+class TLS_Data_Reader;
+
+// This will become an enum class in a future major release
+enum Handshake_Extension_Type {
+ TLSEXT_SERVER_NAME_INDICATION = 0,
+ TLSEXT_CERT_STATUS_REQUEST = 5,
+
+ TLSEXT_CERTIFICATE_TYPES = 9,
+ TLSEXT_SUPPORTED_GROUPS = 10,
+ TLSEXT_EC_POINT_FORMATS = 11,
+ TLSEXT_SRP_IDENTIFIER = 12,
+ TLSEXT_SIGNATURE_ALGORITHMS = 13,
+ TLSEXT_USE_SRTP = 14,
+ TLSEXT_ALPN = 16,
+
+ TLSEXT_ENCRYPT_THEN_MAC = 22,
+ TLSEXT_EXTENDED_MASTER_SECRET = 23,
+
+ TLSEXT_SESSION_TICKET = 35,
+
+ TLSEXT_SUPPORTED_VERSIONS = 43,
+
+ TLSEXT_SAFE_RENEGOTIATION = 65281,
+};
+
+/**
+* Base class representing a TLS extension of some kind
+*/
+class BOTAN_UNSTABLE_API Extension
+ {
+ public:
+ /**
+ * @return code number of the extension
+ */
+ virtual Handshake_Extension_Type type() const = 0;
+
+ /**
+ * @return serialized binary for the extension
+ */
+ virtual std::vector<uint8_t> serialize(Connection_Side whoami) const = 0;
+
+ /**
+ * @return if we should encode this extension or not
+ */
+ virtual bool empty() const = 0;
+
+ virtual ~Extension() = default;
+ };
+
+/**
+* Server Name Indicator extension (RFC 3546)
+*/
+class BOTAN_UNSTABLE_API Server_Name_Indicator final : public Extension
+ {
+ public:
+ static Handshake_Extension_Type static_type()
+ { return TLSEXT_SERVER_NAME_INDICATION; }
+
+ Handshake_Extension_Type type() const override { return static_type(); }
+
+ explicit Server_Name_Indicator(const std::string& host_name) :
+ m_sni_host_name(host_name) {}
+
+ Server_Name_Indicator(TLS_Data_Reader& reader,
+ uint16_t extension_size);
+
+ std::string host_name() const { return m_sni_host_name; }
+
+ std::vector<uint8_t> serialize(Connection_Side whoami) const override;
+
+ bool empty() const override { return m_sni_host_name.empty(); }
+ private:
+ std::string m_sni_host_name;
+ };
+
+#if defined(BOTAN_HAS_SRP6)
+/**
+* SRP identifier extension (RFC 5054)
+*/
+class BOTAN_UNSTABLE_API SRP_Identifier final : public Extension
+ {
+ public:
+ static Handshake_Extension_Type static_type()
+ { return TLSEXT_SRP_IDENTIFIER; }
+
+ Handshake_Extension_Type type() const override { return static_type(); }
+
+ explicit SRP_Identifier(const std::string& identifier) :
+ m_srp_identifier(identifier) {}
+
+ SRP_Identifier(TLS_Data_Reader& reader,
+ uint16_t extension_size);
+
+ std::string identifier() const { return m_srp_identifier; }
+
+ std::vector<uint8_t> serialize(Connection_Side whoami) const override;
+
+ bool empty() const override { return m_srp_identifier.empty(); }
+ private:
+ std::string m_srp_identifier;
+ };
+#endif
+
+/**
+* Renegotiation Indication Extension (RFC 5746)
+*/
+class BOTAN_UNSTABLE_API Renegotiation_Extension final : public Extension
+ {
+ public:
+ static Handshake_Extension_Type static_type()
+ { return TLSEXT_SAFE_RENEGOTIATION; }
+
+ Handshake_Extension_Type type() const override { return static_type(); }
+
+ Renegotiation_Extension() = default;
+
+ explicit Renegotiation_Extension(const std::vector<uint8_t>& bits) :
+ m_reneg_data(bits) {}
+
+ Renegotiation_Extension(TLS_Data_Reader& reader,
+ uint16_t extension_size);
+
+ const std::vector<uint8_t>& renegotiation_info() const
+ { return m_reneg_data; }
+
+ std::vector<uint8_t> serialize(Connection_Side whoami) const override;
+
+ bool empty() const override { return false; } // always send this
+ private:
+ std::vector<uint8_t> m_reneg_data;
+ };
+
+/**
+* ALPN (RFC 7301)
+*/
+class BOTAN_UNSTABLE_API Application_Layer_Protocol_Notification final : public Extension
+ {
+ public:
+ static Handshake_Extension_Type static_type() { return TLSEXT_ALPN; }
+
+ Handshake_Extension_Type type() const override { return static_type(); }
+
+ const std::vector<std::string>& protocols() const { return m_protocols; }
+
+ const std::string& single_protocol() const;
+
+ /**
+ * Single protocol, used by server
+ */
+ explicit Application_Layer_Protocol_Notification(const std::string& protocol) :
+ m_protocols(1, protocol) {}
+
+ /**
+ * List of protocols, used by client
+ */
+ explicit Application_Layer_Protocol_Notification(const std::vector<std::string>& protocols) :
+ m_protocols(protocols) {}
+
+ Application_Layer_Protocol_Notification(TLS_Data_Reader& reader,
+ uint16_t extension_size);
+
+ std::vector<uint8_t> serialize(Connection_Side whoami) const override;
+
+ bool empty() const override { return m_protocols.empty(); }
+ private:
+ std::vector<std::string> m_protocols;
+ };
+
+/**
+* Session Ticket Extension (RFC 5077)
+*/
+class BOTAN_UNSTABLE_API Session_Ticket final : public Extension
+ {
+ public:
+ static Handshake_Extension_Type static_type()
+ { return TLSEXT_SESSION_TICKET; }
+
+ Handshake_Extension_Type type() const override { return static_type(); }
+
+ /**
+ * @return contents of the session ticket
+ */
+ const std::vector<uint8_t>& contents() const { return m_ticket; }
+
+ /**
+ * Create empty extension, used by both client and server
+ */
+ Session_Ticket() = default;
+
+ /**
+ * Extension with ticket, used by client
+ */
+ explicit Session_Ticket(const std::vector<uint8_t>& session_ticket) :
+ m_ticket(session_ticket) {}
+
+ /**
+ * Deserialize a session ticket
+ */
+ Session_Ticket(TLS_Data_Reader& reader, uint16_t extension_size);
+
+ std::vector<uint8_t> serialize(Connection_Side) const override { return m_ticket; }
+
+ bool empty() const override { return false; }
+ private:
+ std::vector<uint8_t> m_ticket;
+ };
+
+
+/**
+* Supported Groups Extension (RFC 7919)
+*/
+class BOTAN_UNSTABLE_API Supported_Groups final : public Extension
+ {
+ public:
+ static Handshake_Extension_Type static_type()
+ { return TLSEXT_SUPPORTED_GROUPS; }
+
+ Handshake_Extension_Type type() const override { return static_type(); }
+
+ std::vector<Group_Params> ec_groups() const;
+ std::vector<Group_Params> dh_groups() const;
+
+ std::vector<uint8_t> serialize(Connection_Side whoami) const override;
+
+ explicit Supported_Groups(const std::vector<Group_Params>& groups);
+
+ Supported_Groups(TLS_Data_Reader& reader,
+ uint16_t extension_size);
+
+ bool empty() const override { return m_groups.empty(); }
+ private:
+ std::vector<Group_Params> 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 Handshake_Extension_Type static_type()
+ { return TLSEXT_EC_POINT_FORMATS; }
+
+ Handshake_Extension_Type type() const override { return static_type(); }
+
+ std::vector<uint8_t> 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() { return m_prefers_compressed; }
+
+ private:
+ bool m_prefers_compressed = false;
+ };
+
+/**
+* Signature Algorithms Extension for TLS 1.2 (RFC 5246)
+*/
+class BOTAN_UNSTABLE_API Signature_Algorithms final : public Extension
+ {
+ public:
+ static Handshake_Extension_Type static_type()
+ { return TLSEXT_SIGNATURE_ALGORITHMS; }
+
+ Handshake_Extension_Type type() const override { return static_type(); }
+
+ const std::vector<Signature_Scheme>& supported_schemes() const { return m_schemes; }
+
+ std::vector<uint8_t> serialize(Connection_Side whoami) const override;
+
+ bool empty() const override { return m_schemes.empty(); }
+
+ explicit Signature_Algorithms(const std::vector<Signature_Scheme>& schemes) :
+ m_schemes(schemes) {}
+
+ Signature_Algorithms(TLS_Data_Reader& reader,
+ uint16_t extension_size);
+ private:
+ std::vector<Signature_Scheme> m_schemes;
+ };
+
+/**
+* Used to indicate SRTP algorithms for DTLS (RFC 5764)
+*/
+class BOTAN_UNSTABLE_API SRTP_Protection_Profiles final : public Extension
+ {
+ public:
+ static Handshake_Extension_Type static_type()
+ { return TLSEXT_USE_SRTP; }
+
+ Handshake_Extension_Type type() const override { return static_type(); }
+
+ const std::vector<uint16_t>& profiles() const { return m_pp; }
+
+ std::vector<uint8_t> serialize(Connection_Side whoami) const override;
+
+ bool empty() const override { return m_pp.empty(); }
+
+ explicit SRTP_Protection_Profiles(const std::vector<uint16_t>& pp) : m_pp(pp) {}
+
+ explicit SRTP_Protection_Profiles(uint16_t pp) : m_pp(1, pp) {}
+
+ SRTP_Protection_Profiles(TLS_Data_Reader& reader, uint16_t extension_size);
+ private:
+ std::vector<uint16_t> m_pp;
+ };
+
+/**
+* Extended Master Secret Extension (RFC 7627)
+*/
+class BOTAN_UNSTABLE_API Extended_Master_Secret final : public Extension
+ {
+ public:
+ static Handshake_Extension_Type static_type()
+ { return TLSEXT_EXTENDED_MASTER_SECRET; }
+
+ Handshake_Extension_Type type() const override { return static_type(); }
+
+ std::vector<uint8_t> 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 Handshake_Extension_Type static_type()
+ { return TLSEXT_ENCRYPT_THEN_MAC; }
+
+ Handshake_Extension_Type type() const override { return static_type(); }
+
+ std::vector<uint8_t> 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);
+ };
+
+/**
+* Certificate Status Request (RFC 6066)
+*/
+class BOTAN_UNSTABLE_API Certificate_Status_Request final : public Extension
+ {
+ public:
+ static Handshake_Extension_Type static_type()
+ { return TLSEXT_CERT_STATUS_REQUEST; }
+
+ Handshake_Extension_Type type() const override { return static_type(); }
+
+ std::vector<uint8_t> serialize(Connection_Side whoami) const override;
+
+ bool empty() const override { return false; }
+
+ const std::vector<uint8_t>& get_responder_id_list() const
+ {
+ return m_ocsp_names;
+ }
+
+ const std::vector<uint8_t>& get_request_extensions() const
+ {
+ return m_extension_bytes;
+ }
+
+ // Server generated version: empty
+ Certificate_Status_Request() {}
+
+ // Client version, both lists can be empty
+ Certificate_Status_Request(const std::vector<uint8_t>& ocsp_responder_ids,
+ const std::vector<std::vector<uint8_t>>& ocsp_key_ids);
+
+ Certificate_Status_Request(TLS_Data_Reader& reader,
+ uint16_t extension_size,
+ Connection_Side side);
+ private:
+ std::vector<uint8_t> m_ocsp_names;
+ std::vector<std::vector<uint8_t>> m_ocsp_keys; // is this field really needed
+ std::vector<uint8_t> m_extension_bytes;
+ };
+
+/**
+* Supported Versions from RFC 8446
+*/
+class BOTAN_UNSTABLE_API Supported_Versions final : public Extension
+ {
+ public:
+ static Handshake_Extension_Type static_type()
+ { return TLSEXT_SUPPORTED_VERSIONS; }
+
+ Handshake_Extension_Type type() const override { return static_type(); }
+
+ std::vector<uint8_t> serialize(Connection_Side whoami) const override;
+
+ bool empty() const override { return m_versions.empty(); }
+
+ Supported_Versions(Protocol_Version version, const Policy& policy);
+
+ Supported_Versions(Protocol_Version version)
+ {
+ m_versions.push_back(version);
+ }
+
+ Supported_Versions(TLS_Data_Reader& reader,
+ uint16_t extension_size,
+ Connection_Side from);
+
+ bool supports(Protocol_Version version) const;
+
+ const std::vector<Protocol_Version> versions() const { return m_versions; }
+ private:
+ std::vector<Protocol_Version> m_versions;
+ };
+
+/**
+* Unknown extensions are deserialized as this type
+*/
+class BOTAN_UNSTABLE_API Unknown_Extension final : public Extension
+ {
+ public:
+ Unknown_Extension(Handshake_Extension_Type type,
+ TLS_Data_Reader& reader,
+ uint16_t extension_size);
+
+ std::vector<uint8_t> serialize(Connection_Side whoami) const override; // always fails
+
+ const std::vector<uint8_t>& value() { return m_value; }
+
+ bool empty() const override { return false; }
+
+ Handshake_Extension_Type type() const override { return m_type; }
+
+ private:
+ Handshake_Extension_Type m_type;
+ std::vector<uint8_t> m_value;
+ };
+
+/**
+* Represents a block of extensions in a hello message
+*/
+class BOTAN_UNSTABLE_API Extensions final
+ {
+ public:
+ std::set<Handshake_Extension_Type> extension_types() const;
+
+ template<typename T>
+ T* get() const
+ {
+ return dynamic_cast<T*>(get(T::static_type()));
+ }
+
+ template<typename T>
+ bool has() const
+ {
+ return get<T>() != nullptr;
+ }
+
+ void add(Extension* extn)
+ {
+ m_extensions[extn->type()].reset(extn);
+ }
+
+ Extension* get(Handshake_Extension_Type type) const
+ {
+ auto i = m_extensions.find(type);
+
+ if(i != m_extensions.end())
+ return i->second.get();
+ return nullptr;
+ }
+
+ std::vector<uint8_t> serialize(Connection_Side whoami) const;
+
+ void deserialize(TLS_Data_Reader& reader, Connection_Side from);
+
+ /**
+ * Remvoe 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).
+ */
+ bool remove_extension(Handshake_Extension_Type typ);
+
+ Extensions() = default;
+
+ Extensions(TLS_Data_Reader& reader, Connection_Side side)
+ {
+ deserialize(reader, side);
+ }
+
+ private:
+ Extensions(const Extensions&) = delete;
+ Extensions& operator=(const Extensions&) = delete;
+
+ std::map<Handshake_Extension_Type, std::unique_ptr<Extension>> m_extensions;
+ };
+
+}
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/tls/tls_handshake_hash.cpp b/comm/third_party/botan/src/lib/tls/tls_handshake_hash.cpp
new file mode 100644
index 0000000000..a48251d06d
--- /dev/null
+++ b/comm/third_party/botan/src/lib/tls/tls_handshake_hash.cpp
@@ -0,0 +1,34 @@
+/*
+* TLS Handshake Hash
+* (C) 2004-2006,2011,2012 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/tls_handshake_hash.h>
+#include <botan/hash.h>
+
+namespace Botan {
+
+namespace TLS {
+
+/**
+* Return a TLS Handshake Hash
+*/
+secure_vector<uint8_t> Handshake_Hash::final(Protocol_Version version,
+ const std::string& mac_algo) const
+ {
+ std::string hash_algo = mac_algo;
+ if(!version.supports_ciphersuite_specific_prf())
+ hash_algo = "Parallel(MD5,SHA-160)";
+ else if(mac_algo == "MD5" || mac_algo == "SHA-1")
+ hash_algo = "SHA-256";
+
+ std::unique_ptr<HashFunction> hash(HashFunction::create_or_throw(hash_algo));
+ hash->update(m_data);
+ return hash->final();
+ }
+
+}
+
+}
diff --git a/comm/third_party/botan/src/lib/tls/tls_handshake_hash.h b/comm/third_party/botan/src/lib/tls/tls_handshake_hash.h
new file mode 100644
index 0000000000..bb38015c8c
--- /dev/null
+++ b/comm/third_party/botan/src/lib/tls/tls_handshake_hash.h
@@ -0,0 +1,44 @@
+/*
+* TLS Handshake Hash
+* (C) 2004-2006,2011,2012 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_TLS_HANDSHAKE_HASH_H_
+#define BOTAN_TLS_HANDSHAKE_HASH_H_
+
+#include <botan/secmem.h>
+#include <botan/tls_version.h>
+
+namespace Botan {
+
+namespace TLS {
+
+/**
+* TLS Handshake Hash
+*/
+class Handshake_Hash final
+ {
+ public:
+ void update(const uint8_t in[], size_t length)
+ { m_data += std::make_pair(in, length); }
+
+ void update(const std::vector<uint8_t>& in)
+ { m_data += in; }
+
+ secure_vector<uint8_t> final(Protocol_Version version,
+ const std::string& mac_algo) const;
+
+ const std::vector<uint8_t>& get_contents() const { return m_data; }
+
+ void reset() { m_data.clear(); }
+ private:
+ std::vector<uint8_t> m_data;
+ };
+
+}
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/tls/tls_handshake_io.cpp b/comm/third_party/botan/src/lib/tls/tls_handshake_io.cpp
new file mode 100644
index 0000000000..7f9e2c86c5
--- /dev/null
+++ b/comm/third_party/botan/src/lib/tls/tls_handshake_io.cpp
@@ -0,0 +1,480 @@
+/*
+* TLS Handshake IO
+* (C) 2012,2014,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/tls_handshake_io.h>
+#include <botan/internal/tls_record.h>
+#include <botan/internal/tls_seq_numbers.h>
+#include <botan/tls_messages.h>
+#include <botan/exceptn.h>
+#include <botan/loadstor.h>
+#include <chrono>
+
+namespace Botan {
+
+namespace TLS {
+
+namespace {
+
+inline size_t load_be24(const uint8_t q[3])
+ {
+ return make_uint32(0,
+ q[0],
+ q[1],
+ q[2]);
+ }
+
+void store_be24(uint8_t out[3], size_t val)
+ {
+ out[0] = get_byte(1, static_cast<uint32_t>(val));
+ out[1] = get_byte(2, static_cast<uint32_t>(val));
+ out[2] = get_byte(3, static_cast<uint32_t>(val));
+ }
+
+uint64_t steady_clock_ms()
+ {
+ return std::chrono::duration_cast<std::chrono::milliseconds>(
+ std::chrono::steady_clock::now().time_since_epoch()).count();
+ }
+
+}
+
+Protocol_Version Stream_Handshake_IO::initial_record_version() const
+ {
+ return Protocol_Version::TLS_V10;
+ }
+
+void Stream_Handshake_IO::add_record(const uint8_t record[],
+ size_t record_len,
+ Record_Type record_type, uint64_t)
+ {
+ if(record_type == HANDSHAKE)
+ {
+ m_queue.insert(m_queue.end(), record, record + record_len);
+ }
+ else if(record_type == CHANGE_CIPHER_SPEC)
+ {
+ if(record_len != 1 || record[0] != 1)
+ throw Decoding_Error("Invalid ChangeCipherSpec");
+
+ // Pretend it's a regular handshake message of zero length
+ const uint8_t ccs_hs[] = { HANDSHAKE_CCS, 0, 0, 0 };
+ m_queue.insert(m_queue.end(), ccs_hs, ccs_hs + sizeof(ccs_hs));
+ }
+ else
+ throw Decoding_Error("Unknown message type " + std::to_string(record_type) + " in handshake processing");
+ }
+
+std::pair<Handshake_Type, std::vector<uint8_t>>
+Stream_Handshake_IO::get_next_record(bool)
+ {
+ if(m_queue.size() >= 4)
+ {
+ const size_t length = 4 + make_uint32(0, m_queue[1], m_queue[2], m_queue[3]);
+
+ if(m_queue.size() >= length)
+ {
+ Handshake_Type type = static_cast<Handshake_Type>(m_queue[0]);
+
+ if(type == HANDSHAKE_NONE)
+ throw Decoding_Error("Invalid handshake message type");
+
+ std::vector<uint8_t> contents(m_queue.begin() + 4,
+ m_queue.begin() + length);
+
+ m_queue.erase(m_queue.begin(), m_queue.begin() + length);
+
+ return std::make_pair(type, contents);
+ }
+ }
+
+ return std::make_pair(HANDSHAKE_NONE, std::vector<uint8_t>());
+ }
+
+std::vector<uint8_t>
+Stream_Handshake_IO::format(const std::vector<uint8_t>& msg,
+ Handshake_Type type) const
+ {
+ std::vector<uint8_t> send_buf(4 + msg.size());
+
+ const size_t buf_size = msg.size();
+
+ send_buf[0] = static_cast<uint8_t>(type);
+
+ store_be24(&send_buf[1], buf_size);
+
+ if (msg.size() > 0)
+ {
+ copy_mem(&send_buf[4], msg.data(), msg.size());
+ }
+
+ return send_buf;
+ }
+
+std::vector<uint8_t> Stream_Handshake_IO::send_under_epoch(const Handshake_Message& /*msg*/, uint16_t /*epoch*/)
+ {
+ throw Invalid_State("Not possible to send under arbitrary epoch with stream based TLS");
+ }
+
+std::vector<uint8_t> Stream_Handshake_IO::send(const Handshake_Message& msg)
+ {
+ const std::vector<uint8_t> msg_bits = msg.serialize();
+
+ if(msg.type() == HANDSHAKE_CCS)
+ {
+ m_send_hs(CHANGE_CIPHER_SPEC, msg_bits);
+ return std::vector<uint8_t>(); // not included in handshake hashes
+ }
+
+ const std::vector<uint8_t> buf = format(msg_bits, msg.type());
+ m_send_hs(HANDSHAKE, buf);
+ return buf;
+ }
+
+Protocol_Version Datagram_Handshake_IO::initial_record_version() const
+ {
+ return Protocol_Version::DTLS_V10;
+ }
+
+void Datagram_Handshake_IO::retransmit_last_flight()
+ {
+ const size_t flight_idx = (m_flights.size() == 1) ? 0 : (m_flights.size() - 2);
+ retransmit_flight(flight_idx);
+ }
+
+void Datagram_Handshake_IO::retransmit_flight(size_t flight_idx)
+ {
+ const std::vector<uint16_t>& flight = m_flights.at(flight_idx);
+
+ BOTAN_ASSERT(flight.size() > 0, "Nonempty flight to retransmit");
+
+ uint16_t epoch = m_flight_data[flight[0]].epoch;
+
+ for(auto msg_seq : flight)
+ {
+ auto& msg = m_flight_data[msg_seq];
+
+ if(msg.epoch != epoch)
+ {
+ // Epoch gap: insert the CCS
+ std::vector<uint8_t> ccs(1, 1);
+ m_send_hs(epoch, CHANGE_CIPHER_SPEC, ccs);
+ }
+
+ send_message(msg_seq, msg.epoch, msg.msg_type, msg.msg_bits);
+ epoch = msg.epoch;
+ }
+ }
+
+bool Datagram_Handshake_IO::timeout_check()
+ {
+ if(m_last_write == 0 || (m_flights.size() > 1 && !m_flights.rbegin()->empty()))
+ {
+ /*
+ If we haven't written anything yet obviously no timeout.
+ Also no timeout possible if we are mid-flight,
+ */
+ return false;
+ }
+
+ const uint64_t ms_since_write = steady_clock_ms() - m_last_write;
+
+ if(ms_since_write < m_next_timeout)
+ return false;
+
+ retransmit_last_flight();
+
+ m_next_timeout = std::min(2 * m_next_timeout, m_max_timeout);
+ return true;
+ }
+
+void Datagram_Handshake_IO::add_record(const uint8_t record[],
+ size_t record_len,
+ Record_Type record_type,
+ uint64_t record_sequence)
+ {
+ const uint16_t epoch = static_cast<uint16_t>(record_sequence >> 48);
+
+ if(record_type == CHANGE_CIPHER_SPEC)
+ {
+ if(record_len != 1 || record[0] != 1)
+ throw Decoding_Error("Invalid ChangeCipherSpec");
+
+ // TODO: check this is otherwise empty
+ m_ccs_epochs.insert(epoch);
+ return;
+ }
+
+ const size_t DTLS_HANDSHAKE_HEADER_LEN = 12;
+
+ while(record_len)
+ {
+ if(record_len < DTLS_HANDSHAKE_HEADER_LEN)
+ return; // completely bogus? at least degenerate/weird
+
+ const uint8_t msg_type = record[0];
+ const size_t msg_len = load_be24(&record[1]);
+ const uint16_t message_seq = load_be<uint16_t>(&record[4], 0);
+ const size_t fragment_offset = load_be24(&record[6]);
+ const size_t fragment_length = load_be24(&record[9]);
+
+ const size_t total_size = DTLS_HANDSHAKE_HEADER_LEN + fragment_length;
+
+ if(record_len < total_size)
+ throw Decoding_Error("Bad lengths in DTLS header");
+
+ if(message_seq >= m_in_message_seq)
+ {
+ m_messages[message_seq].add_fragment(&record[DTLS_HANDSHAKE_HEADER_LEN],
+ fragment_length,
+ fragment_offset,
+ epoch,
+ msg_type,
+ msg_len);
+ }
+ else
+ {
+ // TODO: detect retransmitted flight
+ }
+
+ record += total_size;
+ record_len -= total_size;
+ }
+ }
+
+std::pair<Handshake_Type, std::vector<uint8_t>>
+Datagram_Handshake_IO::get_next_record(bool expecting_ccs)
+ {
+ // Expecting a message means the last flight is concluded
+ if(!m_flights.rbegin()->empty())
+ m_flights.push_back(std::vector<uint16_t>());
+
+ if(expecting_ccs)
+ {
+ if(!m_messages.empty())
+ {
+ const uint16_t current_epoch = m_messages.begin()->second.epoch();
+
+ if(m_ccs_epochs.count(current_epoch))
+ return std::make_pair(HANDSHAKE_CCS, std::vector<uint8_t>());
+ }
+ return std::make_pair(HANDSHAKE_NONE, std::vector<uint8_t>());
+ }
+
+ auto i = m_messages.find(m_in_message_seq);
+
+ if(i == m_messages.end() || !i->second.complete())
+ {
+ return std::make_pair(HANDSHAKE_NONE, std::vector<uint8_t>());
+ }
+
+ m_in_message_seq += 1;
+
+ return i->second.message();
+ }
+
+void Datagram_Handshake_IO::Handshake_Reassembly::add_fragment(
+ const uint8_t fragment[],
+ size_t fragment_length,
+ size_t fragment_offset,
+ uint16_t epoch,
+ uint8_t msg_type,
+ size_t msg_length)
+ {
+ if(complete())
+ return; // already have entire message, ignore this
+
+ if(m_msg_type == HANDSHAKE_NONE)
+ {
+ m_epoch = epoch;
+ m_msg_type = msg_type;
+ m_msg_length = msg_length;
+ }
+
+ 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(fragment_offset > m_msg_length)
+ throw Decoding_Error("Fragment offset past end of message");
+
+ if(fragment_offset + fragment_length > m_msg_length)
+ 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];
+
+ 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];
+ m_fragments.clear();
+ }
+ }
+ }
+
+bool Datagram_Handshake_IO::Handshake_Reassembly::complete() const
+ {
+ return (m_msg_type != HANDSHAKE_NONE && m_message.size() == m_msg_length);
+ }
+
+std::pair<Handshake_Type, std::vector<uint8_t>>
+Datagram_Handshake_IO::Handshake_Reassembly::message() const
+ {
+ if(!complete())
+ throw Internal_Error("Datagram_Handshake_IO - message not complete");
+
+ return std::make_pair(static_cast<Handshake_Type>(m_msg_type), m_message);
+ }
+
+std::vector<uint8_t>
+Datagram_Handshake_IO::format_fragment(const uint8_t fragment[],
+ size_t frag_len,
+ uint16_t frag_offset,
+ uint16_t msg_len,
+ Handshake_Type type,
+ uint16_t msg_sequence) const
+ {
+ std::vector<uint8_t> send_buf(12 + frag_len);
+
+ send_buf[0] = static_cast<uint8_t>(type);
+
+ store_be24(&send_buf[1], msg_len);
+
+ store_be(msg_sequence, &send_buf[4]);
+
+ store_be24(&send_buf[6], frag_offset);
+ store_be24(&send_buf[9], frag_len);
+
+ if (frag_len > 0)
+ {
+ copy_mem(&send_buf[12], fragment, frag_len);
+ }
+
+ return send_buf;
+ }
+
+std::vector<uint8_t>
+Datagram_Handshake_IO::format_w_seq(const std::vector<uint8_t>& msg,
+ Handshake_Type type,
+ uint16_t msg_sequence) const
+ {
+ return format_fragment(msg.data(), msg.size(), 0, static_cast<uint16_t>(msg.size()), type, msg_sequence);
+ }
+
+std::vector<uint8_t>
+Datagram_Handshake_IO::format(const std::vector<uint8_t>& msg,
+ Handshake_Type type) const
+ {
+ return format_w_seq(msg, type, m_in_message_seq - 1);
+ }
+
+std::vector<uint8_t> Datagram_Handshake_IO::send(const Handshake_Message& msg)
+ {
+ return this->send_under_epoch(msg, m_seqs.current_write_epoch());
+ }
+
+std::vector<uint8_t>
+Datagram_Handshake_IO::send_under_epoch(const Handshake_Message& msg, uint16_t epoch)
+ {
+ const std::vector<uint8_t> msg_bits = msg.serialize();
+ const Handshake_Type msg_type = msg.type();
+
+ if(msg_type == HANDSHAKE_CCS)
+ {
+ m_send_hs(epoch, CHANGE_CIPHER_SPEC, msg_bits);
+ return std::vector<uint8_t>(); // not included in handshake hashes
+ }
+ else if(msg_type == HELLO_VERIFY_REQUEST)
+ {
+ // This message is not included in the handshake hashes
+ send_message(m_out_message_seq, epoch, msg_type, msg_bits);
+ m_out_message_seq += 1;
+ return std::vector<uint8_t>();
+ }
+
+ // Note: not saving CCS, instead we know it was there due to change in epoch
+ m_flights.rbegin()->push_back(m_out_message_seq);
+ m_flight_data[m_out_message_seq] = Message_Info(epoch, msg_type, msg_bits);
+
+ m_out_message_seq += 1;
+ m_last_write = steady_clock_ms();
+ m_next_timeout = m_initial_timeout;
+
+ return send_message(m_out_message_seq - 1, epoch, msg_type, msg_bits);
+ }
+
+std::vector<uint8_t> Datagram_Handshake_IO::send_message(uint16_t msg_seq,
+ uint16_t epoch,
+ Handshake_Type msg_type,
+ const std::vector<uint8_t>& msg_bits)
+ {
+ const size_t DTLS_HANDSHAKE_HEADER_LEN = 12;
+
+ const std::vector<uint8_t> no_fragment =
+ format_w_seq(msg_bits, msg_type, msg_seq);
+
+ if(no_fragment.size() + DTLS_HEADER_SIZE <= m_mtu)
+ {
+ m_send_hs(epoch, HANDSHAKE, no_fragment);
+ }
+ else
+ {
+ size_t frag_offset = 0;
+
+ /**
+ * Largest possible overhead is for SHA-384 CBC ciphers, with 16 byte IV,
+ * 16+ for padding and 48 bytes for MAC. 128 is probably a strict
+ * over-estimate here. When CBC ciphers are removed this can be reduced
+ * since AEAD modes have no padding, at most 16 byte mac, and smaller
+ * per-record nonce.
+ */
+ const size_t ciphersuite_overhead = (epoch > 0) ? 128 : 0;
+ const size_t header_overhead = DTLS_HEADER_SIZE + DTLS_HANDSHAKE_HEADER_LEN;
+
+ if(m_mtu <= (header_overhead + ciphersuite_overhead))
+ throw Invalid_Argument("DTLS MTU is too small to send headers");
+
+ const size_t max_rec_size = m_mtu - (header_overhead + ciphersuite_overhead);
+
+ while(frag_offset != msg_bits.size())
+ {
+ const size_t frag_len = std::min<size_t>(msg_bits.size() - frag_offset, max_rec_size);
+
+ const std::vector<uint8_t> frag =
+ format_fragment(&msg_bits[frag_offset],
+ frag_len,
+ static_cast<uint16_t>(frag_offset),
+ static_cast<uint16_t>(msg_bits.size()),
+ msg_type,
+ msg_seq);
+
+ m_send_hs(epoch, HANDSHAKE, frag);
+
+ frag_offset += frag_len;
+ }
+ }
+
+ return no_fragment;
+ }
+
+}
+}
diff --git a/comm/third_party/botan/src/lib/tls/tls_handshake_io.h b/comm/third_party/botan/src/lib/tls/tls_handshake_io.h
new file mode 100644
index 0000000000..1c128726d6
--- /dev/null
+++ b/comm/third_party/botan/src/lib/tls/tls_handshake_io.h
@@ -0,0 +1,218 @@
+/*
+* TLS Handshake Serialization
+* (C) 2012,2014 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_TLS_HANDSHAKE_IO_H_
+#define BOTAN_TLS_HANDSHAKE_IO_H_
+
+#include <botan/tls_magic.h>
+#include <botan/tls_version.h>
+#include <functional>
+#include <vector>
+#include <deque>
+#include <map>
+#include <set>
+#include <utility>
+
+namespace Botan {
+
+namespace TLS {
+
+class Handshake_Message;
+
+/**
+* Handshake IO Interface
+*/
+class Handshake_IO
+ {
+ public:
+ virtual Protocol_Version initial_record_version() const = 0;
+
+ virtual std::vector<uint8_t> send(const Handshake_Message& msg) = 0;
+
+ virtual std::vector<uint8_t> send_under_epoch(const Handshake_Message& msg, uint16_t epoch) = 0;
+
+ virtual bool timeout_check() = 0;
+
+ virtual std::vector<uint8_t> format(
+ const std::vector<uint8_t>& handshake_msg,
+ Handshake_Type handshake_type) const = 0;
+
+ virtual void add_record(const uint8_t record[],
+ size_t record_len,
+ Record_Type type,
+ uint64_t sequence_number) = 0;
+
+ /**
+ * Returns (HANDSHAKE_NONE, std::vector<>()) if no message currently available
+ */
+ virtual std::pair<Handshake_Type, std::vector<uint8_t>>
+ get_next_record(bool expecting_ccs) = 0;
+
+ Handshake_IO() = default;
+
+ Handshake_IO(const Handshake_IO&) = delete;
+
+ Handshake_IO& operator=(const Handshake_IO&) = delete;
+
+ virtual ~Handshake_IO() = default;
+ };
+
+/**
+* Handshake IO for stream-based handshakes
+*/
+class Stream_Handshake_IO final : public Handshake_IO
+ {
+ public:
+ typedef std::function<void (uint8_t, const std::vector<uint8_t>&)> writer_fn;
+
+ explicit Stream_Handshake_IO(writer_fn writer) : m_send_hs(writer) {}
+
+ Protocol_Version initial_record_version() const override;
+
+ bool timeout_check() override { return false; }
+
+ std::vector<uint8_t> send(const Handshake_Message& msg) override;
+
+ std::vector<uint8_t> send_under_epoch(const Handshake_Message& msg, uint16_t epoch) override;
+
+ std::vector<uint8_t> format(
+ const std::vector<uint8_t>& handshake_msg,
+ Handshake_Type handshake_type) const override;
+
+ void add_record(const uint8_t record[],
+ size_t record_len,
+ Record_Type type,
+ uint64_t sequence_number) override;
+
+ std::pair<Handshake_Type, std::vector<uint8_t>>
+ get_next_record(bool expecting_ccs) override;
+ private:
+ std::deque<uint8_t> m_queue;
+ writer_fn m_send_hs;
+ };
+
+/**
+* Handshake IO for datagram-based handshakes
+*/
+class Datagram_Handshake_IO final : public Handshake_IO
+ {
+ public:
+ typedef std::function<void (uint16_t, uint8_t, const std::vector<uint8_t>&)> writer_fn;
+
+ Datagram_Handshake_IO(writer_fn writer,
+ class Connection_Sequence_Numbers& seq,
+ uint16_t mtu, uint64_t initial_timeout_ms, uint64_t max_timeout_ms) :
+ 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)
+ {}
+
+ Protocol_Version initial_record_version() const override;
+
+ bool timeout_check() override;
+
+ std::vector<uint8_t> send(const Handshake_Message& msg) override;
+
+ std::vector<uint8_t> send_under_epoch(const Handshake_Message& msg, uint16_t epoch) override;
+
+ std::vector<uint8_t> format(
+ const std::vector<uint8_t>& handshake_msg,
+ Handshake_Type handshake_type) const override;
+
+ void add_record(const uint8_t record[],
+ size_t record_len,
+ Record_Type type,
+ uint64_t sequence_number) override;
+
+ std::pair<Handshake_Type, std::vector<uint8_t>>
+ get_next_record(bool expecting_ccs) override;
+ private:
+ void retransmit_flight(size_t flight);
+ void retransmit_last_flight();
+
+ std::vector<uint8_t> format_fragment(
+ const uint8_t fragment[],
+ size_t fragment_len,
+ uint16_t frag_offset,
+ uint16_t msg_len,
+ Handshake_Type type,
+ uint16_t msg_sequence) const;
+
+ std::vector<uint8_t> format_w_seq(
+ const std::vector<uint8_t>& handshake_msg,
+ Handshake_Type handshake_type,
+ uint16_t msg_sequence) const;
+
+ std::vector<uint8_t> send_message(uint16_t msg_seq, uint16_t epoch,
+ Handshake_Type msg_type,
+ const std::vector<uint8_t>& msg);
+
+ class Handshake_Reassembly final
+ {
+ public:
+ void add_fragment(const uint8_t fragment[],
+ size_t fragment_length,
+ size_t fragment_offset,
+ uint16_t epoch,
+ uint8_t msg_type,
+ size_t msg_length);
+
+ bool complete() const;
+
+ uint16_t epoch() const { return m_epoch; }
+
+ std::pair<Handshake_Type, std::vector<uint8_t>> message() const;
+ private:
+ uint8_t m_msg_type = HANDSHAKE_NONE;
+ size_t m_msg_length = 0;
+ uint16_t m_epoch = 0;
+
+ // vector<bool> m_seen;
+ // vector<uint8_t> m_fragments
+ std::map<size_t, uint8_t> m_fragments;
+ std::vector<uint8_t> m_message;
+ };
+
+ struct Message_Info final
+ {
+ Message_Info(uint16_t e, Handshake_Type mt, const std::vector<uint8_t>& msg) :
+ epoch(e), msg_type(mt), msg_bits(msg) {}
+
+ Message_Info() : epoch(0xFFFF), msg_type(HANDSHAKE_NONE) {}
+
+ uint16_t epoch;
+ Handshake_Type msg_type;
+ std::vector<uint8_t> msg_bits;
+ };
+
+ class Connection_Sequence_Numbers& m_seqs;
+ std::map<uint16_t, Handshake_Reassembly> m_messages;
+ std::set<uint16_t> m_ccs_epochs;
+ std::vector<std::vector<uint16_t>> m_flights;
+ std::map<uint16_t, Message_Info> m_flight_data;
+
+ uint64_t m_initial_timeout = 0;
+ uint64_t m_max_timeout = 0;
+
+ uint64_t m_last_write = 0;
+ uint64_t m_next_timeout = 0;
+
+ uint16_t m_in_message_seq = 0;
+ uint16_t m_out_message_seq = 0;
+
+ writer_fn m_send_hs;
+ uint16_t m_mtu;
+ };
+
+}
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/tls/tls_handshake_msg.h b/comm/third_party/botan/src/lib/tls/tls_handshake_msg.h
new file mode 100644
index 0000000000..a0d9346fba
--- /dev/null
+++ b/comm/third_party/botan/src/lib/tls/tls_handshake_msg.h
@@ -0,0 +1,51 @@
+/*
+* TLS Handshake Message
+* (C) 2012 Jack Lloyd
+* 2016 Matthias Gierlings
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_TLS_HANDSHAKE_MSG_H_
+#define BOTAN_TLS_HANDSHAKE_MSG_H_
+
+#include <botan/tls_magic.h>
+#include <vector>
+#include <string>
+
+namespace Botan {
+
+namespace TLS {
+
+class Handshake_IO;
+class Handshake_Hash;
+
+/**
+* TLS Handshake Message Base Class
+*/
+class BOTAN_PUBLIC_API(2,0) Handshake_Message
+ {
+ public:
+ /**
+ * @return string representation of this message type
+ */
+ std::string type_string() const;
+
+ /**
+ * @return the message type
+ */
+ virtual Handshake_Type type() const = 0;
+
+ /**
+ * @return DER representation of this message
+ */
+ virtual std::vector<uint8_t> serialize() const = 0;
+
+ virtual ~Handshake_Message() = default;
+ };
+
+}
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/tls/tls_handshake_state.cpp b/comm/third_party/botan/src/lib/tls/tls_handshake_state.cpp
new file mode 100644
index 0000000000..9c9390a221
--- /dev/null
+++ b/comm/third_party/botan/src/lib/tls/tls_handshake_state.cpp
@@ -0,0 +1,580 @@
+/*
+* TLS Handshaking
+* (C) 2004-2006,2011,2012,2015,2016 Jack Lloyd
+* 2017 Harry Reimann, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/tls_handshake_state.h>
+#include <botan/internal/tls_record.h>
+#include <botan/tls_messages.h>
+#include <botan/kdf.h>
+#include <sstream>
+
+namespace Botan {
+
+namespace TLS {
+
+std::string Handshake_Message::type_string() const
+ {
+ return handshake_type_to_string(type());
+ }
+
+const char* handshake_type_to_string(Handshake_Type type)
+ {
+ switch(type)
+ {
+ case HELLO_VERIFY_REQUEST:
+ return "hello_verify_request";
+
+ case HELLO_REQUEST:
+ return "hello_request";
+
+ case CLIENT_HELLO:
+ return "client_hello";
+
+ case SERVER_HELLO:
+ return "server_hello";
+
+ case CERTIFICATE:
+ return "certificate";
+
+ case CERTIFICATE_URL:
+ return "certificate_url";
+
+ case CERTIFICATE_STATUS:
+ return "certificate_status";
+
+ case SERVER_KEX:
+ return "server_key_exchange";
+
+ case CERTIFICATE_REQUEST:
+ return "certificate_request";
+
+ case SERVER_HELLO_DONE:
+ return "server_hello_done";
+
+ case CERTIFICATE_VERIFY:
+ return "certificate_verify";
+
+ case CLIENT_KEX:
+ return "client_key_exchange";
+
+ case NEW_SESSION_TICKET:
+ return "new_session_ticket";
+
+ case HANDSHAKE_CCS:
+ return "change_cipher_spec";
+
+ case FINISHED:
+ return "finished";
+
+ case HANDSHAKE_NONE:
+ return "invalid";
+ }
+
+ throw TLS_Exception(Alert::UNEXPECTED_MESSAGE,
+ "Unknown TLS handshake message type " + std::to_string(type));
+ }
+
+namespace {
+
+uint32_t bitmask_for_handshake_type(Handshake_Type type)
+ {
+ switch(type)
+ {
+ case HELLO_VERIFY_REQUEST:
+ return (1 << 0);
+
+ case HELLO_REQUEST:
+ return (1 << 1);
+
+ case CLIENT_HELLO:
+ return (1 << 2);
+
+ case SERVER_HELLO:
+ return (1 << 3);
+
+ case CERTIFICATE:
+ return (1 << 4);
+
+ case CERTIFICATE_URL:
+ return (1 << 5);
+
+ case CERTIFICATE_STATUS:
+ return (1 << 6);
+
+ case SERVER_KEX:
+ return (1 << 7);
+
+ case CERTIFICATE_REQUEST:
+ return (1 << 8);
+
+ case SERVER_HELLO_DONE:
+ return (1 << 9);
+
+ case CERTIFICATE_VERIFY:
+ return (1 << 10);
+
+ case CLIENT_KEX:
+ return (1 << 11);
+
+ case NEW_SESSION_TICKET:
+ return (1 << 12);
+
+ case HANDSHAKE_CCS:
+ return (1 << 13);
+
+ case FINISHED:
+ return (1 << 14);
+
+ // allow explicitly disabling new handshakes
+ case HANDSHAKE_NONE:
+ return 0;
+ }
+
+ throw TLS_Exception(Alert::UNEXPECTED_MESSAGE,
+ "Unknown TLS handshake message type " + std::to_string(type));
+ }
+
+std::string handshake_mask_to_string(uint32_t mask, char combiner)
+ {
+ const Handshake_Type types[] = {
+ HELLO_VERIFY_REQUEST,
+ HELLO_REQUEST,
+ CLIENT_HELLO,
+ SERVER_HELLO,
+ CERTIFICATE,
+ CERTIFICATE_URL,
+ CERTIFICATE_STATUS,
+ SERVER_KEX,
+ CERTIFICATE_REQUEST,
+ SERVER_HELLO_DONE,
+ CERTIFICATE_VERIFY,
+ CLIENT_KEX,
+ NEW_SESSION_TICKET,
+ HANDSHAKE_CCS,
+ FINISHED
+ };
+
+ std::ostringstream o;
+ bool empty = true;
+
+ for(auto&& t : types)
+ {
+ if(mask & bitmask_for_handshake_type(t))
+ {
+ if(!empty)
+ o << combiner;
+ o << handshake_type_to_string(t);
+ empty = false;
+ }
+ }
+
+ return o.str();
+ }
+
+}
+
+/*
+* Initialize the SSL/TLS Handshake State
+*/
+Handshake_State::Handshake_State(Handshake_IO* io, Callbacks& cb) :
+ m_callbacks(cb),
+ m_handshake_io(io),
+ m_version(m_handshake_io->initial_record_version())
+ {
+ }
+
+void Handshake_State::note_message(const Handshake_Message& msg)
+ {
+ m_callbacks.tls_inspect_handshake_msg(msg);
+ }
+
+void Handshake_State::hello_verify_request(const Hello_Verify_Request& hello_verify)
+ {
+ note_message(hello_verify);
+
+ 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* client_hello)
+ {
+ if(client_hello == nullptr)
+ {
+ m_client_hello.reset();
+ hash().reset();
+ }
+ else
+ {
+ m_client_hello.reset(client_hello);
+ note_message(*m_client_hello);
+ }
+ }
+
+void Handshake_State::server_hello(Server_Hello* server_hello)
+ {
+ m_server_hello.reset(server_hello);
+ m_ciphersuite = Ciphersuite::by_id(m_server_hello->ciphersuite());
+ note_message(*m_server_hello);
+ }
+
+void Handshake_State::server_certs(Certificate* server_certs)
+ {
+ m_server_certs.reset(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);
+ note_message(*m_server_cert_status);
+ }
+
+void Handshake_State::server_kex(Server_Key_Exchange* server_kex)
+ {
+ m_server_kex.reset(server_kex);
+ note_message(*m_server_kex);
+ }
+
+void Handshake_State::cert_req(Certificate_Req* cert_req)
+ {
+ m_cert_req.reset(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);
+ note_message(*m_server_hello_done);
+ }
+
+void Handshake_State::client_certs(Certificate* client_certs)
+ {
+ m_client_certs.reset(client_certs);
+ note_message(*m_client_certs);
+ }
+
+void Handshake_State::client_kex(Client_Key_Exchange* client_kex)
+ {
+ m_client_kex.reset(client_kex);
+ note_message(*m_client_kex);
+ }
+
+void Handshake_State::client_verify(Certificate_Verify* client_verify)
+ {
+ m_client_verify.reset(client_verify);
+ note_message(*m_client_verify);
+ }
+
+void Handshake_State::new_session_ticket(New_Session_Ticket* new_session_ticket)
+ {
+ m_new_session_ticket.reset(new_session_ticket);
+ note_message(*m_new_session_ticket);
+ }
+
+void Handshake_State::server_finished(Finished* server_finished)
+ {
+ m_server_finished.reset(server_finished);
+ note_message(*m_server_finished);
+ }
+
+void Handshake_State::client_finished(Finished* client_finished)
+ {
+ m_client_finished.reset(client_finished);
+ note_message(*m_client_finished);
+ }
+
+void Handshake_State::set_version(const Protocol_Version& version)
+ {
+ m_version = version;
+ }
+
+void Handshake_State::compute_session_keys()
+ {
+ m_session_keys = Session_Keys(this, client_kex()->pre_master_secret(), false);
+ }
+
+void Handshake_State::compute_session_keys(const secure_vector<uint8_t>& resume_master_secret)
+ {
+ m_session_keys = Session_Keys(this, resume_master_secret, true);
+ }
+
+void Handshake_State::confirm_transition_to(Handshake_Type handshake_msg)
+ {
+ const uint32_t mask = bitmask_for_handshake_type(handshake_msg);
+
+ m_hand_received_mask |= mask;
+
+ const bool ok = (m_hand_expecting_mask & mask) != 0; // overlap?
+
+ if(!ok)
+ {
+ const uint32_t seen_so_far = m_hand_received_mask & ~mask;
+
+ std::ostringstream msg;
+
+ msg << "Unexpected state transition in handshake got a " << handshake_type_to_string(handshake_msg);
+
+ if(m_hand_expecting_mask == 0)
+ msg << " not expecting messages";
+ else
+ msg << " expected " << handshake_mask_to_string(m_hand_expecting_mask, '|');
+
+ if(seen_so_far != 0)
+ msg << " seen " << handshake_mask_to_string(seen_so_far, '+');
+
+ throw Unexpected_Message(msg.str());
+ }
+
+ /* We don't know what to expect next, so force a call to
+ set_expected_next; if it doesn't happen, the next transition
+ check will always fail which is what we want.
+ */
+ m_hand_expecting_mask = 0;
+ }
+
+void Handshake_State::set_expected_next(Handshake_Type handshake_msg)
+ {
+ m_hand_expecting_mask |= bitmask_for_handshake_type(handshake_msg);
+ }
+
+bool Handshake_State::received_handshake_msg(Handshake_Type handshake_msg) const
+ {
+ const uint32_t mask = bitmask_for_handshake_type(handshake_msg);
+
+ return (m_hand_received_mask & mask) != 0;
+ }
+
+std::pair<Handshake_Type, std::vector<uint8_t>>
+Handshake_State::get_next_handshake_msg()
+ {
+ const bool expecting_ccs =
+ (bitmask_for_handshake_type(HANDSHAKE_CCS) & m_hand_expecting_mask) != 0;
+
+ return m_handshake_io->get_next_record(expecting_ccs);
+ }
+
+std::string Handshake_State::srp_identifier() const
+ {
+#if defined(BOTAN_HAS_SRP6)
+ // Authenticated via the successful key exchange
+ if(ciphersuite().valid() && ciphersuite().kex_method() == Kex_Algo::SRP_SHA)
+ return client_hello()->srp_identifier();
+#endif
+
+ return "";
+ }
+
+
+std::vector<uint8_t> Handshake_State::session_ticket() const
+ {
+ if(new_session_ticket() && !new_session_ticket()->ticket().empty())
+ return new_session_ticket()->ticket();
+
+ return client_hello()->session_ticket();
+ }
+
+KDF* Handshake_State::protocol_specific_prf() const
+ {
+ if(version().supports_ciphersuite_specific_prf())
+ {
+ const std::string prf_algo = ciphersuite().prf_algo();
+
+ if(prf_algo == "MD5" || prf_algo == "SHA-1")
+ return get_kdf("TLS-12-PRF(SHA-256)");
+
+ return get_kdf("TLS-12-PRF(" + prf_algo + ")");
+ }
+
+ // Old PRF used in TLS v1.0, v1.1 and DTLS v1.0
+ return get_kdf("TLS-PRF");
+ }
+
+std::pair<std::string, Signature_Format>
+Handshake_State::choose_sig_format(const Private_Key& key,
+ Signature_Scheme& chosen_scheme,
+ bool for_client_auth,
+ const Policy& policy) const
+ {
+ const std::string sig_algo = key.algo_name();
+
+ if(this->version().supports_negotiable_signature_algorithms())
+ {
+ const std::vector<Signature_Scheme> allowed = policy.allowed_signature_schemes();
+
+ std::vector<Signature_Scheme> requested =
+ (for_client_auth) ? cert_req()->signature_schemes() : client_hello()->signature_schemes();
+
+ if(requested.empty())
+ {
+ // Implicit SHA-1
+ requested.push_back(Signature_Scheme::RSA_PKCS1_SHA1);
+ requested.push_back(Signature_Scheme::ECDSA_SHA1);
+ requested.push_back(Signature_Scheme::DSA_SHA1);
+ }
+
+ for(Signature_Scheme scheme : allowed)
+ {
+ if(signature_scheme_is_known(scheme) == false)
+ {
+ continue;
+ }
+
+ if(signature_algorithm_of_scheme(scheme) == sig_algo)
+ {
+ if(std::find(requested.begin(), requested.end(), scheme) != requested.end())
+ {
+ chosen_scheme = scheme;
+ break;
+ }
+ }
+ }
+
+ const std::string hash = hash_function_of_scheme(chosen_scheme);
+
+ if(!policy.allowed_signature_hash(hash))
+ {
+ throw TLS_Exception(Alert::HANDSHAKE_FAILURE,
+ "Policy refuses to accept signing with any hash supported by peer");
+ }
+
+ if(sig_algo == "RSA")
+ {
+ return std::make_pair(padding_string_for_scheme(chosen_scheme), IEEE_1363);
+ }
+ else if(sig_algo == "DSA" || sig_algo == "ECDSA")
+ {
+ return std::make_pair(padding_string_for_scheme(chosen_scheme), DER_SEQUENCE);
+ }
+ }
+ else
+ {
+ if(sig_algo == "RSA")
+ {
+ const std::string padding = "PKCS1v15(Parallel(MD5,SHA-160))";
+ return std::make_pair(padding, IEEE_1363);
+ }
+ else if(sig_algo == "DSA" || sig_algo == "ECDSA")
+ {
+ const std::string padding = "EMSA1(SHA-1)";
+ return std::make_pair(padding, DER_SEQUENCE);
+ }
+ }
+
+ throw Invalid_Argument(sig_algo + " is invalid/unknown for TLS signatures");
+ }
+
+namespace {
+
+bool supported_algos_include(
+ const std::vector<Signature_Scheme>& schemes,
+ const std::string& key_type,
+ const std::string& hash_type)
+ {
+ for(Signature_Scheme scheme : schemes)
+ {
+ if(signature_scheme_is_known(scheme) &&
+ hash_function_of_scheme(scheme) == hash_type &&
+ signature_algorithm_of_scheme(scheme) == key_type)
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+}
+
+std::pair<std::string, Signature_Format>
+Handshake_State::parse_sig_format(const Public_Key& key,
+ Signature_Scheme scheme,
+ bool for_client_auth,
+ const Policy& policy) const
+ {
+ const std::string key_type = key.algo_name();
+
+ if(!policy.allowed_signature_method(key_type))
+ {
+ throw TLS_Exception(Alert::HANDSHAKE_FAILURE,
+ "Rejecting " + key_type + " signature");
+ }
+
+ if(this->version().supports_negotiable_signature_algorithms() == false)
+ {
+ if(scheme != Signature_Scheme::NONE)
+ throw Decoding_Error("Counterparty sent hash/sig IDs with old version");
+
+ /*
+ There is no check on the acceptability of a v1.0/v1.1 hash type,
+ since it's implicit with use of the protocol
+ */
+
+ if(key_type == "RSA")
+ {
+ const std::string padding = "PKCS1v15(Parallel(MD5,SHA-160))";
+ return std::make_pair(padding, IEEE_1363);
+ }
+ else if(key_type == "DSA" || key_type == "ECDSA")
+ {
+ const std::string padding = "EMSA1(SHA-1)";
+ return std::make_pair(padding, DER_SEQUENCE);
+ }
+ else
+ throw Invalid_Argument(key_type + " is invalid/unknown for TLS signatures");
+ }
+
+ if(scheme == Signature_Scheme::NONE)
+ throw Decoding_Error("Counterparty did not send hash/sig IDS");
+
+ if(key_type != signature_algorithm_of_scheme(scheme))
+ throw Decoding_Error("Counterparty sent inconsistent key and sig types");
+
+ if(for_client_auth && !cert_req())
+ {
+ throw TLS_Exception(Alert::HANDSHAKE_FAILURE,
+ "No certificate verify set");
+ }
+
+ /*
+ Confirm the signature type we just received against the
+ supported_algos list that we sent; it better be there.
+ */
+
+ const std::vector<Signature_Scheme> supported_algos =
+ for_client_auth ? cert_req()->signature_schemes() :
+ client_hello()->signature_schemes();
+
+ if(!signature_scheme_is_known(scheme))
+ throw TLS_Exception(Alert::HANDSHAKE_FAILURE,
+ "Peer sent unknown signature scheme");
+
+ const std::string hash_algo = hash_function_of_scheme(scheme);
+
+ if(!supported_algos_include(supported_algos, key_type, hash_algo))
+ {
+ throw TLS_Exception(Alert::ILLEGAL_PARAMETER,
+ "TLS signature extension did not allow for " +
+ key_type + "/" + hash_algo + " signature");
+ }
+
+ if(key_type == "RSA")
+ {
+ return std::make_pair(padding_string_for_scheme(scheme), IEEE_1363);
+ }
+ else if(key_type == "DSA" || key_type == "ECDSA")
+ {
+ return std::make_pair(padding_string_for_scheme(scheme), DER_SEQUENCE);
+ }
+
+ throw Invalid_Argument(key_type + " is invalid/unknown for TLS signatures");
+ }
+
+}
+
+}
diff --git a/comm/third_party/botan/src/lib/tls/tls_handshake_state.h b/comm/third_party/botan/src/lib/tls/tls_handshake_state.h
new file mode 100644
index 0000000000..3321a6210e
--- /dev/null
+++ b/comm/third_party/botan/src/lib/tls/tls_handshake_state.h
@@ -0,0 +1,206 @@
+/*
+* TLS Handshake State
+* (C) 2004-2006,2011,2012 Jack Lloyd
+* 2017 Harry Reimann, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_TLS_HANDSHAKE_STATE_H_
+#define BOTAN_TLS_HANDSHAKE_STATE_H_
+
+#include <botan/internal/tls_handshake_hash.h>
+#include <botan/internal/tls_handshake_io.h>
+#include <botan/internal/tls_session_key.h>
+#include <botan/tls_ciphersuite.h>
+#include <botan/tls_exceptn.h>
+#include <botan/tls_handshake_msg.h>
+#include <botan/tls_callbacks.h>
+#include <botan/pk_keys.h>
+#include <botan/pubkey.h>
+#include <functional>
+
+namespace Botan {
+
+class KDF;
+
+namespace TLS {
+
+class Callbacks;
+class Policy;
+
+class Hello_Verify_Request;
+class Client_Hello;
+class Server_Hello;
+class Certificate;
+class Certificate_Status;
+class Server_Key_Exchange;
+class Certificate_Req;
+class Server_Hello_Done;
+class Certificate;
+class Client_Key_Exchange;
+class Certificate_Verify;
+class New_Session_Ticket;
+class Finished;
+
+/**
+* SSL/TLS Handshake State
+*/
+class Handshake_State
+ {
+ public:
+ Handshake_State(Handshake_IO* io, Callbacks& callbacks);
+
+ virtual ~Handshake_State() = default;
+
+ Handshake_State(const Handshake_State&) = delete;
+ Handshake_State& operator=(const Handshake_State&) = delete;
+
+ Handshake_IO& handshake_io() { return *m_handshake_io; }
+
+ /**
+ * Return true iff we have received a particular message already
+ * @param msg_type the message type
+ */
+ bool received_handshake_msg(Handshake_Type msg_type) const;
+
+ /**
+ * Confirm that we were expecting this message type
+ * @param msg_type the message type
+ */
+ void confirm_transition_to(Handshake_Type msg_type);
+
+ /**
+ * Record that we are expecting a particular message type next
+ * @param msg_type the message type
+ */
+ void set_expected_next(Handshake_Type msg_type);
+
+ std::pair<Handshake_Type, std::vector<uint8_t>>
+ get_next_handshake_msg();
+
+ std::vector<uint8_t> session_ticket() const;
+
+ std::pair<std::string, Signature_Format>
+ parse_sig_format(const Public_Key& key,
+ Signature_Scheme scheme,
+ bool for_client_auth,
+ const Policy& policy) const;
+
+ std::pair<std::string, Signature_Format>
+ choose_sig_format(const Private_Key& key,
+ Signature_Scheme& scheme,
+ bool for_client_auth,
+ const Policy& policy) const;
+
+ std::string srp_identifier() const;
+
+ KDF* protocol_specific_prf() const;
+
+ Protocol_Version version() const { return m_version; }
+
+ void set_version(const Protocol_Version& version);
+
+ void hello_verify_request(const Hello_Verify_Request& hello_verify);
+
+ void client_hello(Client_Hello* client_hello);
+ void server_hello(Server_Hello* server_hello);
+ void server_certs(Certificate* server_certs);
+ void server_cert_status(Certificate_Status* server_cert_status);
+ void server_kex(Server_Key_Exchange* server_kex);
+ void cert_req(Certificate_Req* cert_req);
+ void server_hello_done(Server_Hello_Done* server_hello_done);
+ void client_certs(Certificate* client_certs);
+ void client_kex(Client_Key_Exchange* client_kex);
+ void client_verify(Certificate_Verify* client_verify);
+ void new_session_ticket(New_Session_Ticket* new_session_ticket);
+ void server_finished(Finished* server_finished);
+ void client_finished(Finished* client_finished);
+
+ const Client_Hello* client_hello() const
+ { return m_client_hello.get(); }
+
+ const Server_Hello* server_hello() const
+ { return m_server_hello.get(); }
+
+ const Certificate* server_certs() const
+ { return m_server_certs.get(); }
+
+ const Server_Key_Exchange* server_kex() const
+ { return m_server_kex.get(); }
+
+ const Certificate_Req* cert_req() const
+ { return m_cert_req.get(); }
+
+ const Server_Hello_Done* server_hello_done() const
+ { return m_server_hello_done.get(); }
+
+ const Certificate* client_certs() const
+ { return m_client_certs.get(); }
+
+ const Client_Key_Exchange* client_kex() const
+ { return m_client_kex.get(); }
+
+ const Certificate_Verify* client_verify() const
+ { return m_client_verify.get(); }
+
+ const Certificate_Status* server_cert_status() const
+ { return m_server_cert_status.get(); }
+
+ const New_Session_Ticket* new_session_ticket() const
+ { return m_new_session_ticket.get(); }
+
+ const Finished* server_finished() const
+ { return m_server_finished.get(); }
+
+ const Finished* client_finished() const
+ { return m_client_finished.get(); }
+
+ const Ciphersuite& ciphersuite() const { return m_ciphersuite; }
+
+ const Session_Keys& session_keys() const { return m_session_keys; }
+
+ Callbacks& callbacks() const { return m_callbacks; }
+
+ void compute_session_keys();
+
+ void compute_session_keys(const secure_vector<uint8_t>& resume_master_secret);
+
+ Handshake_Hash& hash() { return m_handshake_hash; }
+
+ const Handshake_Hash& hash() const { return m_handshake_hash; }
+
+ void note_message(const Handshake_Message& msg);
+ private:
+
+ Callbacks& m_callbacks;
+
+ std::unique_ptr<Handshake_IO> m_handshake_io;
+
+ uint32_t m_hand_expecting_mask = 0;
+ uint32_t m_hand_received_mask = 0;
+ Protocol_Version m_version;
+ Ciphersuite m_ciphersuite;
+ Session_Keys m_session_keys;
+ Handshake_Hash m_handshake_hash;
+
+ std::unique_ptr<Client_Hello> m_client_hello;
+ std::unique_ptr<Server_Hello> m_server_hello;
+ std::unique_ptr<Certificate> m_server_certs;
+ std::unique_ptr<Certificate_Status> m_server_cert_status;
+ std::unique_ptr<Server_Key_Exchange> m_server_kex;
+ std::unique_ptr<Certificate_Req> m_cert_req;
+ std::unique_ptr<Server_Hello_Done> m_server_hello_done;
+ std::unique_ptr<Certificate> m_client_certs;
+ std::unique_ptr<Client_Key_Exchange> m_client_kex;
+ std::unique_ptr<Certificate_Verify> m_client_verify;
+ std::unique_ptr<New_Session_Ticket> m_new_session_ticket;
+ std::unique_ptr<Finished> m_server_finished;
+ std::unique_ptr<Finished> m_client_finished;
+ };
+
+}
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/tls/tls_magic.h b/comm/third_party/botan/src/lib/tls/tls_magic.h
new file mode 100644
index 0000000000..49d69c7d89
--- /dev/null
+++ b/comm/third_party/botan/src/lib/tls/tls_magic.h
@@ -0,0 +1,72 @@
+/*
+* SSL/TLS Protocol Constants
+* (C) 2004-2010 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_TLS_PROTOCOL_MAGIC_H_
+#define BOTAN_TLS_PROTOCOL_MAGIC_H_
+
+#include <botan/types.h>
+
+//BOTAN_FUTURE_INTERNAL_HEADER(tls_magic.h)
+
+namespace Botan {
+
+namespace TLS {
+
+/**
+* Protocol Constants for SSL/TLS
+*/
+enum Size_Limits {
+ TLS_HEADER_SIZE = 5,
+ DTLS_HEADER_SIZE = TLS_HEADER_SIZE + 8,
+
+ MAX_PLAINTEXT_SIZE = 16*1024,
+ MAX_COMPRESSED_SIZE = MAX_PLAINTEXT_SIZE + 1024,
+ MAX_CIPHERTEXT_SIZE = MAX_COMPRESSED_SIZE + 1024,
+};
+
+// This will become an enum class in a future major release
+enum Connection_Side { CLIENT = 1, SERVER = 2 };
+
+// This will become an enum class in a future major release
+enum Record_Type {
+ CHANGE_CIPHER_SPEC = 20,
+ ALERT = 21,
+ HANDSHAKE = 22,
+ APPLICATION_DATA = 23,
+
+ NO_RECORD = 256
+};
+
+// This will become an enum class in a future major release
+enum Handshake_Type {
+ HELLO_REQUEST = 0,
+ CLIENT_HELLO = 1,
+ SERVER_HELLO = 2,
+ HELLO_VERIFY_REQUEST = 3,
+ NEW_SESSION_TICKET = 4, // RFC 5077
+ CERTIFICATE = 11,
+ SERVER_KEX = 12,
+ CERTIFICATE_REQUEST = 13,
+ SERVER_HELLO_DONE = 14,
+ CERTIFICATE_VERIFY = 15,
+ CLIENT_KEX = 16,
+ FINISHED = 20,
+
+ CERTIFICATE_URL = 21,
+ CERTIFICATE_STATUS = 22,
+
+ HANDSHAKE_CCS = 254, // Not a wire value
+ HANDSHAKE_NONE = 255 // Null value
+};
+
+const char* handshake_type_to_string(Handshake_Type t);
+
+}
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/tls/tls_messages.h b/comm/third_party/botan/src/lib/tls/tls_messages.h
new file mode 100644
index 0000000000..fc95a1c020
--- /dev/null
+++ b/comm/third_party/botan/src/lib/tls/tls_messages.h
@@ -0,0 +1,653 @@
+/*
+* 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_H_
+#define BOTAN_TLS_MESSAGES_H_
+
+#include <botan/tls_extensions.h>
+#include <botan/tls_handshake_msg.h>
+#include <botan/tls_session.h>
+#include <botan/tls_policy.h>
+#include <botan/tls_ciphersuite.h>
+#include <botan/pk_keys.h>
+#include <botan/x509cert.h>
+#include <botan/ocsp.h>
+#include <vector>
+#include <string>
+#include <set>
+
+#if defined(BOTAN_HAS_CECPQ1)
+ #include <botan/cecpq1.h>
+#endif
+
+#if defined(BOTAN_HAS_SRP6)
+ #include <botan/srp6.h>
+#endif
+
+namespace Botan {
+
+class Public_Key;
+class Credentials_Manager;
+
+namespace TLS {
+
+class Session;
+class Handshake_IO;
+class Handshake_State;
+class Callbacks;
+
+std::vector<uint8_t> make_hello_random(RandomNumberGenerator& rng,
+ const Policy& policy);
+
+/**
+* DTLS Hello Verify Request
+*/
+class BOTAN_UNSTABLE_API Hello_Verify_Request final : public Handshake_Message
+ {
+ public:
+ std::vector<uint8_t> serialize() const override;
+ Handshake_Type type() const override { return HELLO_VERIFY_REQUEST; }
+
+ const std::vector<uint8_t>& cookie() const { return m_cookie; }
+
+ explicit Hello_Verify_Request(const std::vector<uint8_t>& buf);
+
+ Hello_Verify_Request(const std::vector<uint8_t>& client_hello_bits,
+ const std::string& client_identity,
+ const SymmetricKey& secret_key);
+ private:
+ std::vector<uint8_t> m_cookie;
+ };
+
+/**
+* Client Hello Message
+*/
+class BOTAN_UNSTABLE_API Client_Hello final : public Handshake_Message
+ {
+ public:
+ class Settings final
+ {
+ public:
+ Settings(const Protocol_Version version,
+ const std::string& hostname = "",
+ const std::string& srp_identifier = "") :
+ m_new_session_version(version),
+ m_hostname(hostname),
+ m_srp_identifier(srp_identifier) {}
+
+ const Protocol_Version protocol_version() const { return m_new_session_version; }
+ const std::string& hostname() const { return m_hostname; }
+ const std::string& srp_identifier() const { return m_srp_identifier; }
+
+ private:
+ const Protocol_Version m_new_session_version;
+ const std::string m_hostname;
+ const std::string m_srp_identifier;
+ };
+
+ Handshake_Type type() const override { return CLIENT_HELLO; }
+
+ Protocol_Version version() const { return m_version; }
+
+ std::vector<Protocol_Version> supported_versions() const;
+
+ const std::vector<uint8_t>& random() const { return m_random; }
+
+ const std::vector<uint8_t>& session_id() const { return m_session_id; }
+
+ const std::vector<uint8_t>& compression_methods() const { return m_comp_methods; }
+
+ const std::vector<uint16_t>& ciphersuites() const { return m_suites; }
+
+ bool offered_suite(uint16_t ciphersuite) const;
+
+ bool sent_fallback_scsv() const;
+
+ std::vector<Signature_Scheme> signature_schemes() const;
+
+ std::vector<Group_Params> supported_ecc_curves() const;
+
+ std::vector<Group_Params> supported_dh_groups() const;
+
+ bool prefers_compressed_ec_points() const;
+
+ std::string sni_hostname() const;
+
+#if defined(BOTAN_HAS_SRP6)
+ std::string srp_identifier() const;
+#endif
+
+ bool secure_renegotiation() const;
+
+ std::vector<uint8_t> renegotiation_info() const;
+
+ bool supports_session_ticket() const;
+
+ std::vector<uint8_t> session_ticket() const;
+
+ bool supports_alpn() const;
+
+ bool supports_extended_master_secret() const;
+
+ bool supports_cert_status_message() const;
+
+ bool supports_encrypt_then_mac() const;
+
+ bool sent_signature_algorithms() const;
+
+ std::vector<std::string> next_protocols() const;
+
+ std::vector<uint16_t> srtp_profiles() const;
+
+ void update_hello_cookie(const Hello_Verify_Request& hello_verify);
+
+ const std::vector<uint8_t>& cookie() const { return m_hello_cookie; }
+
+ std::vector<uint8_t> cookie_input_data() const;
+
+ std::set<Handshake_Extension_Type> extension_types() const
+ { return m_extensions.extension_types(); }
+
+ const Extensions& extensions() const { return m_extensions; }
+
+ Client_Hello(Handshake_IO& io,
+ Handshake_Hash& hash,
+ const Policy& policy,
+ Callbacks& cb,
+ RandomNumberGenerator& rng,
+ const std::vector<uint8_t>& reneg_info,
+ const Client_Hello::Settings& client_settings,
+ const std::vector<std::string>& next_protocols);
+
+ Client_Hello(Handshake_IO& io,
+ Handshake_Hash& hash,
+ const Policy& policy,
+ Callbacks& cb,
+ RandomNumberGenerator& rng,
+ const std::vector<uint8_t>& reneg_info,
+ const Session& resumed_session,
+ const std::vector<std::string>& next_protocols);
+
+ explicit Client_Hello(const std::vector<uint8_t>& buf);
+
+ private:
+ std::vector<uint8_t> serialize() const override;
+
+ Protocol_Version m_version;
+ std::vector<uint8_t> m_session_id;
+ std::vector<uint8_t> m_random;
+ std::vector<uint16_t> m_suites;
+ std::vector<uint8_t> m_comp_methods;
+ std::vector<uint8_t> m_hello_cookie; // DTLS only
+
+ Extensions m_extensions;
+ };
+
+/**
+* Server Hello Message
+*/
+class BOTAN_UNSTABLE_API Server_Hello final : public Handshake_Message
+ {
+ public:
+ class Settings final
+ {
+ public:
+ Settings(const std::vector<uint8_t> new_session_id,
+ Protocol_Version new_session_version,
+ uint16_t ciphersuite,
+ bool offer_session_ticket) :
+ m_new_session_id(new_session_id),
+ m_new_session_version(new_session_version),
+ m_ciphersuite(ciphersuite),
+ m_offer_session_ticket(offer_session_ticket) {}
+
+ const std::vector<uint8_t>& 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 std::vector<uint8_t> m_new_session_id;
+ Protocol_Version m_new_session_version;
+ uint16_t m_ciphersuite;
+ bool m_offer_session_ticket;
+ };
+
+
+ Handshake_Type type() const override { return SERVER_HELLO; }
+
+ Protocol_Version version() const { return m_version; }
+
+ const std::vector<uint8_t>& random() const { return m_random; }
+
+ const std::vector<uint8_t>& session_id() const { return m_session_id; }
+
+ uint16_t ciphersuite() const { return m_ciphersuite; }
+
+ uint8_t compression_method() const { return m_comp_method; }
+
+ bool secure_renegotiation() const
+ {
+ return m_extensions.has<Renegotiation_Extension>();
+ }
+
+ std::vector<uint8_t> renegotiation_info() const
+ {
+ if(Renegotiation_Extension* reneg = m_extensions.get<Renegotiation_Extension>())
+ return reneg->renegotiation_info();
+ return std::vector<uint8_t>();
+ }
+
+ bool supports_extended_master_secret() const
+ {
+ return m_extensions.has<Extended_Master_Secret>();
+ }
+
+ bool supports_encrypt_then_mac() const
+ {
+ return m_extensions.has<Encrypt_then_MAC>();
+ }
+
+ bool supports_certificate_status_message() const
+ {
+ return m_extensions.has<Certificate_Status_Request>();
+ }
+
+ bool supports_session_ticket() const
+ {
+ return m_extensions.has<Session_Ticket>();
+ }
+
+ uint16_t srtp_profile() const
+ {
+ if(auto srtp = m_extensions.get<SRTP_Protection_Profiles>())
+ {
+ 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 next_protocol() const
+ {
+ if(auto alpn = m_extensions.get<Application_Layer_Protocol_Notification>())
+ return alpn->single_protocol();
+ return "";
+ }
+
+ std::set<Handshake_Extension_Type> extension_types() const
+ { return m_extensions.extension_types(); }
+
+ const Extensions& extensions() const { return m_extensions; }
+
+ bool prefers_compressed_ec_points() const
+ {
+ if(auto ecc_formats = m_extensions.get<Supported_Point_Formats>())
+ {
+ return ecc_formats->prefers_compressed();
+ }
+ return false;
+ }
+
+ bool random_signals_downgrade() const;
+
+ Server_Hello(Handshake_IO& io,
+ Handshake_Hash& hash,
+ const Policy& policy,
+ Callbacks& cb,
+ RandomNumberGenerator& rng,
+ const std::vector<uint8_t>& secure_reneg_info,
+ const Client_Hello& client_hello,
+ const Server_Hello::Settings& settings,
+ const std::string next_protocol);
+
+ Server_Hello(Handshake_IO& io,
+ Handshake_Hash& hash,
+ const Policy& policy,
+ Callbacks& cb,
+ RandomNumberGenerator& rng,
+ const std::vector<uint8_t>& secure_reneg_info,
+ const Client_Hello& client_hello,
+ Session& resumed_session,
+ bool offer_session_ticket,
+ const std::string& next_protocol);
+
+ explicit Server_Hello(const std::vector<uint8_t>& buf);
+ private:
+ std::vector<uint8_t> serialize() const override;
+
+ Protocol_Version m_version;
+ std::vector<uint8_t> m_session_id, m_random;
+ uint16_t m_ciphersuite;
+ uint8_t m_comp_method;
+
+ Extensions m_extensions;
+ };
+
+/**
+* Client Key Exchange Message
+*/
+class BOTAN_UNSTABLE_API Client_Key_Exchange final : public Handshake_Message
+ {
+ public:
+ Handshake_Type type() const override { return CLIENT_KEX; }
+
+ const secure_vector<uint8_t>& pre_master_secret() const
+ { return m_pre_master; }
+
+ Client_Key_Exchange(Handshake_IO& io,
+ Handshake_State& state,
+ const Policy& policy,
+ Credentials_Manager& creds,
+ const Public_Key* server_public_key,
+ const std::string& hostname,
+ RandomNumberGenerator& rng);
+
+ Client_Key_Exchange(const std::vector<uint8_t>& buf,
+ const Handshake_State& state,
+ const Private_Key* server_rsa_kex_key,
+ Credentials_Manager& creds,
+ const Policy& policy,
+ RandomNumberGenerator& rng);
+
+ private:
+ std::vector<uint8_t> serialize() const override
+ { return m_key_material; }
+
+ std::vector<uint8_t> m_key_material;
+ secure_vector<uint8_t> m_pre_master;
+ };
+
+/**
+* Certificate Message
+*/
+class BOTAN_UNSTABLE_API Certificate final : public Handshake_Message
+ {
+ public:
+ Handshake_Type type() const override { return CERTIFICATE; }
+ const std::vector<X509_Certificate>& cert_chain() const { return m_certs; }
+
+ size_t count() const { return m_certs.size(); }
+ bool empty() const { return m_certs.empty(); }
+
+ Certificate(Handshake_IO& io,
+ Handshake_Hash& hash,
+ const std::vector<X509_Certificate>& certs);
+
+ explicit Certificate(const std::vector<uint8_t>& buf, const Policy &policy);
+ private:
+ std::vector<uint8_t> serialize() const override;
+
+ std::vector<X509_Certificate> m_certs;
+ };
+
+/**
+* Certificate Status (RFC 6066)
+*/
+class BOTAN_UNSTABLE_API Certificate_Status final : public Handshake_Message
+ {
+ public:
+ Handshake_Type type() const override { return CERTIFICATE_STATUS; }
+
+ //std::shared_ptr<const OCSP::Response> response() const { return m_response; }
+
+ const std::vector<uint8_t>& response() const { return m_response; }
+
+ Certificate_Status(const std::vector<uint8_t>& buf);
+
+ Certificate_Status(Handshake_IO& io,
+ Handshake_Hash& hash,
+ std::shared_ptr<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<uint8_t> const& raw_response_bytes );
+
+ private:
+ std::vector<uint8_t> serialize() const override;
+ std::vector<uint8_t> m_response;
+ };
+
+/**
+* Certificate Request Message
+*/
+class BOTAN_UNSTABLE_API Certificate_Req final : public Handshake_Message
+ {
+ public:
+ Handshake_Type type() const override { return CERTIFICATE_REQUEST; }
+
+ const std::vector<std::string>& acceptable_cert_types() const
+ { return m_cert_key_types; }
+
+ const std::vector<X509_DN>& acceptable_CAs() const { return m_names; }
+
+ const std::vector<Signature_Scheme>& signature_schemes() const
+ {
+ return m_schemes;
+ }
+
+ Certificate_Req(Handshake_IO& io,
+ Handshake_Hash& hash,
+ const Policy& policy,
+ const std::vector<X509_DN>& allowed_cas,
+ Protocol_Version version);
+
+ Certificate_Req(const std::vector<uint8_t>& buf,
+ Protocol_Version version);
+ private:
+ std::vector<uint8_t> serialize() const override;
+
+ std::vector<X509_DN> m_names;
+ std::vector<std::string> m_cert_key_types;
+
+ std::vector<Signature_Scheme> m_schemes;
+ };
+
+/**
+* Certificate Verify Message
+*/
+class BOTAN_UNSTABLE_API Certificate_Verify final : public Handshake_Message
+ {
+ public:
+ Handshake_Type type() const override { return CERTIFICATE_VERIFY; }
+
+ /**
+ * 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_Verify(Handshake_IO& io,
+ Handshake_State& state,
+ const Policy& policy,
+ RandomNumberGenerator& rng,
+ const Private_Key* key);
+
+ Certificate_Verify(const std::vector<uint8_t>& buf,
+ Protocol_Version version);
+ private:
+ std::vector<uint8_t> serialize() const override;
+
+ std::vector<uint8_t> m_signature;
+ Signature_Scheme m_scheme = Signature_Scheme::NONE;
+ };
+
+/**
+* Finished Message
+*/
+class BOTAN_UNSTABLE_API Finished final : public Handshake_Message
+ {
+ public:
+ Handshake_Type type() const override { return FINISHED; }
+
+ std::vector<uint8_t> verify_data() const
+ { return m_verification_data; }
+
+ bool verify(const Handshake_State& state,
+ Connection_Side side) const;
+
+ Finished(Handshake_IO& io,
+ Handshake_State& state,
+ Connection_Side side);
+
+ explicit Finished(const std::vector<uint8_t>& buf);
+ private:
+ std::vector<uint8_t> serialize() const override;
+
+ std::vector<uint8_t> m_verification_data;
+ };
+
+/**
+* Hello Request Message
+*/
+class BOTAN_UNSTABLE_API Hello_Request final : public Handshake_Message
+ {
+ public:
+ Handshake_Type type() const override { return HELLO_REQUEST; }
+
+ explicit Hello_Request(Handshake_IO& io);
+ explicit Hello_Request(const std::vector<uint8_t>& buf);
+ private:
+ std::vector<uint8_t> 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 SERVER_KEX; }
+
+ const std::vector<uint8_t>& 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 Private_Key& server_kex_key() const;
+
+#if defined(BOTAN_HAS_SRP6)
+ // Only valid for SRP negotiation
+ SRP6_Server_Session& server_srp_params() const
+ {
+ BOTAN_ASSERT_NONNULL(m_srp_params);
+ return *m_srp_params;
+ }
+#endif
+
+#if defined(BOTAN_HAS_CECPQ1)
+ // Only valid for CECPQ1 negotiation
+ const CECPQ1_key& cecpq1_key() const
+ {
+ BOTAN_ASSERT_NONNULL(m_cecpq1_key);
+ return *m_cecpq1_key;
+ }
+#endif
+
+ 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<uint8_t>& buf,
+ Kex_Algo kex_alg,
+ Auth_Method sig_alg,
+ Protocol_Version version);
+
+ ~Server_Key_Exchange() = default;
+ private:
+ std::vector<uint8_t> serialize() const override;
+
+#if defined(BOTAN_HAS_SRP6)
+ std::unique_ptr<SRP6_Server_Session> m_srp_params;
+#endif
+
+#if defined(BOTAN_HAS_CECPQ1)
+ std::unique_ptr<CECPQ1_key> m_cecpq1_key;
+#endif
+
+ std::unique_ptr<Private_Key> m_kex_key;
+
+ std::vector<uint8_t> m_params;
+
+ std::vector<uint8_t> m_signature;
+ Signature_Scheme m_scheme = Signature_Scheme::NONE;
+ };
+
+/**
+* Server Hello Done Message
+*/
+class BOTAN_UNSTABLE_API Server_Hello_Done final : public Handshake_Message
+ {
+ public:
+ Handshake_Type type() const override { return SERVER_HELLO_DONE; }
+
+ Server_Hello_Done(Handshake_IO& io, Handshake_Hash& hash);
+ explicit Server_Hello_Done(const std::vector<uint8_t>& buf);
+ private:
+ std::vector<uint8_t> serialize() const override;
+ };
+
+/**
+* New Session Ticket Message
+*/
+class BOTAN_UNSTABLE_API New_Session_Ticket final : public Handshake_Message
+ {
+ public:
+ Handshake_Type type() const override { return NEW_SESSION_TICKET; }
+
+ uint32_t ticket_lifetime_hint() const { return m_ticket_lifetime_hint; }
+ const std::vector<uint8_t>& ticket() const { return m_ticket; }
+
+ New_Session_Ticket(Handshake_IO& io,
+ Handshake_Hash& hash,
+ const std::vector<uint8_t>& ticket,
+ uint32_t lifetime);
+
+ New_Session_Ticket(Handshake_IO& io,
+ Handshake_Hash& hash);
+
+ explicit New_Session_Ticket(const std::vector<uint8_t>& buf);
+ private:
+ std::vector<uint8_t> serialize() const override;
+
+ uint32_t m_ticket_lifetime_hint = 0;
+ std::vector<uint8_t> m_ticket;
+ };
+
+/**
+* Change Cipher Spec
+*/
+class BOTAN_UNSTABLE_API Change_Cipher_Spec final : public Handshake_Message
+ {
+ public:
+ Handshake_Type type() const override { return HANDSHAKE_CCS; }
+
+ std::vector<uint8_t> serialize() const override
+ { return std::vector<uint8_t>(1, 1); }
+ };
+
+}
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/tls/tls_policy.cpp b/comm/third_party/botan/src/lib/tls/tls_policy.cpp
new file mode 100644
index 0000000000..17fe288f19
--- /dev/null
+++ b/comm/third_party/botan/src/lib/tls/tls_policy.cpp
@@ -0,0 +1,616 @@
+/*
+* Policies for TLS
+* (C) 2004-2010,2012,2015,2016 Jack Lloyd
+* 2016 Christian Mainka
+* 2017 Harry Reimann, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/tls_policy.h>
+#include <botan/tls_ciphersuite.h>
+#include <botan/tls_algos.h>
+#include <botan/tls_exceptn.h>
+#include <botan/internal/stl_util.h>
+#include <botan/pk_keys.h>
+#include <sstream>
+
+namespace Botan {
+
+namespace TLS {
+
+std::vector<Signature_Scheme> Policy::allowed_signature_schemes() const
+ {
+ std::vector<Signature_Scheme> schemes;
+
+ for(Signature_Scheme scheme : all_signature_schemes())
+ {
+ if(signature_scheme_is_known(scheme) == false)
+ continue;
+ const bool sig_allowed = allowed_signature_method(signature_algorithm_of_scheme(scheme));
+ const bool hash_allowed = allowed_signature_hash(hash_function_of_scheme(scheme));
+
+ if(sig_allowed && hash_allowed)
+ {
+ schemes.push_back(scheme);
+ }
+ }
+
+ return schemes;
+ }
+
+std::vector<std::string> Policy::allowed_ciphers() const
+ {
+ return {
+ //"AES-256/OCB(12)",
+ //"AES-128/OCB(12)",
+ "ChaCha20Poly1305",
+ "AES-256/GCM",
+ "AES-128/GCM",
+ //"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",
+ //"AES-256",
+ //"AES-128",
+ //"Camellia-256",
+ //"Camellia-128",
+ //"SEED",
+ //"3DES",
+ };
+ }
+
+std::vector<std::string> Policy::allowed_signature_hashes() const
+ {
+ return {
+ "SHA-512",
+ "SHA-384",
+ "SHA-256",
+ //"SHA-1",
+ };
+ }
+
+std::vector<std::string> Policy::allowed_macs() const
+ {
+ /*
+ SHA-256 is preferred because the Lucky13 countermeasure works
+ somewhat better for SHA-256 vs SHA-384:
+ https://github.com/randombit/botan/pull/675
+ */
+ return {
+ "AEAD",
+ "SHA-256",
+ "SHA-384",
+ "SHA-1",
+ };
+ }
+
+std::vector<std::string> Policy::allowed_key_exchange_methods() const
+ {
+ return {
+ //"SRP_SHA",
+ //"ECDHE_PSK",
+ //"DHE_PSK",
+ //"PSK",
+ "CECPQ1",
+ "ECDH",
+ "DH",
+ //"RSA",
+ };
+ }
+
+std::vector<std::string> Policy::allowed_signature_methods() const
+ {
+ return {
+ "ECDSA",
+ "RSA",
+ //"DSA",
+ //"IMPLICIT",
+ //"ANONYMOUS" (anon)
+ };
+ }
+
+bool Policy::allowed_signature_method(const std::string& sig_method) const
+ {
+ return value_exists(allowed_signature_methods(), sig_method);
+ }
+
+bool Policy::allowed_signature_hash(const std::string& sig_hash) const
+ {
+ return value_exists(allowed_signature_hashes(), sig_hash);
+ }
+
+bool Policy::use_ecc_point_compression() const
+ {
+ return false;
+ }
+
+Group_Params Policy::choose_key_exchange_group(const std::vector<Group_Params>& peer_groups) const
+ {
+ if(peer_groups.empty())
+ return Group_Params::NONE;
+
+ const std::vector<Group_Params> our_groups = key_exchange_groups();
+
+ for(auto g : our_groups)
+ {
+ if(value_exists(peer_groups, g))
+ return g;
+ }
+
+ return Group_Params::NONE;
+ }
+
+Group_Params Policy::default_dh_group() const
+ {
+ /*
+ * Return the first listed or just default to 2048
+ */
+ for(auto g : key_exchange_groups())
+ {
+ if(group_param_is_dh(g))
+ return g;
+ }
+
+ return Group_Params::FFDHE_2048;
+ }
+
+std::vector<Group_Params> Policy::key_exchange_groups() const
+ {
+ // Default list is ordered by performance
+ return {
+
+#if defined(BOTAN_HAS_CURVE_25519)
+ Group_Params::X25519,
+#endif
+
+ 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,
+ };
+ }
+
+size_t Policy::minimum_dh_group_size() const
+ {
+ return 2048;
+ }
+
+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;
+ }
+
+size_t Policy::minimum_ecdh_group_size() const
+ {
+ // x25519 is smallest curve currently supported for TLS key exchange
+ return 255;
+ }
+
+size_t Policy::minimum_signature_strength() const
+ {
+ return 110;
+ }
+
+bool Policy::require_cert_revocation_info() const
+ {
+ return true;
+ }
+
+size_t Policy::minimum_rsa_bits() const
+ {
+ /* Default assumption is all end-entity certificates should
+ be at least 2048 bits these days.
+
+ If you are connecting to arbitrary servers on the Internet
+ (ie as a web browser or SMTP client) you'll probably have to reduce this
+ to 1024 bits, or perhaps even lower.
+ */
+ return 2048;
+ }
+
+size_t Policy::minimum_dsa_group_size() const
+ {
+ // FIPS 186-3
+ return 2048;
+ }
+
+void Policy::check_peer_key_acceptable(const Public_Key& public_key) const
+ {
+ const std::string algo_name = public_key.algo_name();
+
+ const size_t keylength = public_key.key_length();
+ size_t expected_keylength = 0;
+
+ if(algo_name == "RSA")
+ {
+ expected_keylength = minimum_rsa_bits();
+ }
+ else if(algo_name == "DH")
+ {
+ expected_keylength = minimum_dh_group_size();
+ }
+ else if(algo_name == "DSA")
+ {
+ expected_keylength = minimum_dsa_group_size();
+ }
+ else if(algo_name == "ECDH" || algo_name == "Curve25519")
+ {
+ expected_keylength = minimum_ecdh_group_size();
+ }
+ else if(algo_name == "ECDSA")
+ {
+ expected_keylength = minimum_ecdsa_group_size();
+ }
+ // else some other algo, so leave expected_keylength as zero and the check is a no-op
+
+ if(keylength < expected_keylength)
+ throw TLS_Exception(Alert::INSUFFICIENT_SECURITY,
+ "Peer sent " +
+ std::to_string(keylength) + " bit " + algo_name + " key"
+ ", policy requires at least " +
+ std::to_string(expected_keylength));
+ }
+
+uint32_t Policy::session_ticket_lifetime() const
+ {
+ return 86400; // ~1 day
+ }
+
+bool Policy::send_fallback_scsv(Protocol_Version version) const
+ {
+ return version != latest_supported_version(version.is_datagram_protocol());
+ }
+
+bool Policy::acceptable_protocol_version(Protocol_Version version) const
+ {
+ if(version == Protocol_Version::TLS_V12 && allow_tls12())
+ return true;
+
+ if(version == Protocol_Version::DTLS_V12 && allow_dtls12())
+ return true;
+
+#if defined(BOTAN_HAS_TLS_V10)
+
+ if(version == Protocol_Version::TLS_V11 && allow_tls11())
+ return true;
+ if(version == Protocol_Version::TLS_V10 && allow_tls10())
+ return true;
+ if(version == Protocol_Version::DTLS_V10 && allow_dtls10())
+ return true;
+
+#endif
+
+ return false;
+ }
+
+Protocol_Version Policy::latest_supported_version(bool datagram) const
+ {
+ if(datagram)
+ {
+ if(acceptable_protocol_version(Protocol_Version::DTLS_V12))
+ return Protocol_Version::DTLS_V12;
+#if defined(BOTAN_HAS_TLS_V10)
+ if(acceptable_protocol_version(Protocol_Version::DTLS_V10))
+ return Protocol_Version::DTLS_V10;
+#endif
+ throw Invalid_State("Policy forbids all available DTLS version");
+ }
+ else
+ {
+ if(acceptable_protocol_version(Protocol_Version::TLS_V12))
+ return Protocol_Version::TLS_V12;
+#if defined(BOTAN_HAS_TLS_V10)
+ if(acceptable_protocol_version(Protocol_Version::TLS_V11))
+ return Protocol_Version::TLS_V11;
+ if(acceptable_protocol_version(Protocol_Version::TLS_V10))
+ return Protocol_Version::TLS_V10;
+#endif
+ throw Invalid_State("Policy forbids all available TLS version");
+ }
+ }
+
+bool Policy::acceptable_ciphersuite(const Ciphersuite& ciphersuite) const
+ {
+ return value_exists(allowed_ciphers(), ciphersuite.cipher_algo()) &&
+ value_exists(allowed_macs(), ciphersuite.mac_algo());
+ }
+
+bool Policy::allow_client_initiated_renegotiation() const { return false; }
+bool Policy::allow_server_initiated_renegotiation() const { return false; }
+bool Policy::allow_insecure_renegotiation() const { return false; }
+bool Policy::allow_tls10() const { return false; }
+bool Policy::allow_tls11() const { return false; }
+bool Policy::allow_tls12() const { return true; }
+bool Policy::allow_dtls10() const { return false; }
+bool Policy::allow_dtls12() const { return true; }
+bool Policy::include_time_in_hello_random() const { return true; }
+bool Policy::hide_unknown_users() const { return false; }
+bool Policy::server_uses_own_ciphersuite_preferences() const { return true; }
+bool Policy::negotiate_encrypt_then_mac() const { return true; }
+bool Policy::support_cert_status_message() const { return true; }
+bool Policy::allow_resumption_for_renegotiation() const { return true; }
+bool Policy::only_resume_with_exact_version() const { return true; }
+bool Policy::require_client_certificate_authentication() const { return false; }
+bool Policy::request_client_certificate_authentication() const { return require_client_certificate_authentication(); }
+bool Policy::abort_connection_on_undesired_renegotiation() const { return false; }
+bool Policy::allow_dtls_epoch0_restart() const { return false; }
+
+size_t Policy::maximum_certificate_chain_size() const { return 0; }
+
+// 1 second initial timeout, 60 second max - see RFC 6347 sec 4.2.4.1
+size_t Policy::dtls_initial_timeout() const { return 1*1000; }
+size_t Policy::dtls_maximum_timeout() const { return 60*1000; }
+
+size_t Policy::dtls_default_mtu() const
+ {
+ // default MTU is IPv6 min MTU minus UDP/IP headers
+ return 1280 - 40 - 8;
+ }
+
+std::vector<uint16_t> Policy::srtp_profiles() const
+ {
+ return std::vector<uint16_t>();
+ }
+
+namespace {
+
+class Ciphersuite_Preference_Ordering final
+ {
+ public:
+ Ciphersuite_Preference_Ordering(const std::vector<std::string>& ciphers,
+ const std::vector<std::string>& macs,
+ const std::vector<std::string>& kex,
+ const std::vector<std::string>& sigs) :
+ m_ciphers(ciphers), m_macs(macs), m_kex(kex), m_sigs(sigs) {}
+
+ bool operator()(const Ciphersuite& a, const Ciphersuite& b) const
+ {
+ if(a.kex_method() != b.kex_method())
+ {
+ for(size_t i = 0; i != m_kex.size(); ++i)
+ {
+ if(a.kex_algo() == m_kex[i])
+ return true;
+ if(b.kex_algo() == m_kex[i])
+ return false;
+ }
+ }
+
+ if(a.cipher_algo() != b.cipher_algo())
+ {
+ for(size_t i = 0; i != m_ciphers.size(); ++i)
+ {
+ if(a.cipher_algo() == m_ciphers[i])
+ return true;
+ if(b.cipher_algo() == m_ciphers[i])
+ return false;
+ }
+ }
+
+ if(a.cipher_keylen() != b.cipher_keylen())
+ {
+ if(a.cipher_keylen() < b.cipher_keylen())
+ return false;
+ if(a.cipher_keylen() > b.cipher_keylen())
+ return true;
+ }
+
+ if(a.auth_method() != b.auth_method())
+ {
+ for(size_t i = 0; i != m_sigs.size(); ++i)
+ {
+ if(a.sig_algo() == m_sigs[i])
+ return true;
+ if(b.sig_algo() == m_sigs[i])
+ return false;
+ }
+ }
+
+ if(a.mac_algo() != b.mac_algo())
+ {
+ for(size_t i = 0; i != m_macs.size(); ++i)
+ {
+ if(a.mac_algo() == m_macs[i])
+ return true;
+ if(b.mac_algo() == m_macs[i])
+ return false;
+ }
+ }
+
+ return false; // equal (?!?)
+ }
+ private:
+ std::vector<std::string> m_ciphers, m_macs, m_kex, m_sigs;
+ };
+
+}
+
+std::vector<uint16_t> Policy::ciphersuite_list(Protocol_Version version,
+ bool have_srp) const
+ {
+ const std::vector<std::string> ciphers = allowed_ciphers();
+ const std::vector<std::string> macs = allowed_macs();
+ const std::vector<std::string> kex = allowed_key_exchange_methods();
+ const std::vector<std::string> sigs = allowed_signature_methods();
+
+ std::vector<Ciphersuite> ciphersuites;
+
+ for(auto&& suite : Ciphersuite::all_known_ciphersuites())
+ {
+ // Can we use it?
+ if(!suite.valid())
+ continue;
+
+ // Can we use it in this version?
+ if(!suite.usable_in_version(version))
+ continue;
+
+ // Is it acceptable to the policy?
+ if(!this->acceptable_ciphersuite(suite))
+ continue;
+
+ // Are we doing SRP?
+ if(!have_srp && suite.kex_method() == Kex_Algo::SRP_SHA)
+ continue;
+
+ if(!value_exists(kex, suite.kex_algo()))
+ continue; // unsupported key exchange
+
+ if(!value_exists(ciphers, suite.cipher_algo()))
+ continue; // unsupported cipher
+
+ if(!value_exists(macs, suite.mac_algo()))
+ continue; // unsupported MAC algo
+
+ if(!value_exists(sigs, suite.sig_algo()))
+ {
+ // allow if it's an empty sig algo and we want to use PSK
+ if(suite.auth_method() != Auth_Method::IMPLICIT || !suite.psk_ciphersuite())
+ continue;
+ }
+
+ /*
+ CECPQ1 always uses x25519 for ECDH, so treat the applications
+ removal of x25519 from the ECC curve list as equivalent to
+ saying they do not trust CECPQ1
+ */
+ if(suite.kex_method() == Kex_Algo::CECPQ1)
+ {
+ if(value_exists(key_exchange_groups(), Group_Params::X25519) == false)
+ continue;
+ }
+
+ // OK, consider it
+ ciphersuites.push_back(suite);
+ }
+
+ if(ciphersuites.empty())
+ {
+ throw Invalid_State("Policy does not allow any available cipher suite");
+ }
+
+ Ciphersuite_Preference_Ordering order(ciphers, macs, kex, sigs);
+ std::sort(ciphersuites.begin(), ciphersuites.end(), order);
+
+ std::vector<uint16_t> ciphersuite_codes;
+ for(auto i : ciphersuites)
+ ciphersuite_codes.push_back(i.ciphersuite_code());
+ return ciphersuite_codes;
+ }
+
+namespace {
+
+void print_vec(std::ostream& o,
+ const char* key,
+ const std::vector<std::string>& v)
+ {
+ o << key << " = ";
+ for(size_t i = 0; i != v.size(); ++i)
+ {
+ o << v[i];
+ if(i != v.size() - 1)
+ o << ' ';
+ }
+ o << '\n';
+ }
+
+void print_vec(std::ostream& o,
+ const char* key,
+ const std::vector<Group_Params>& v)
+ {
+ o << key << " = ";
+ for(size_t i = 0; i != v.size(); ++i)
+ {
+ o << group_param_to_string(v[i]);
+ if(i != v.size() - 1)
+ o << ' ';
+ }
+ o << '\n';
+ }
+
+void print_bool(std::ostream& o,
+ const char* key, bool b)
+ {
+ o << key << " = " << (b ? "true" : "false") << '\n';
+ }
+
+}
+
+void Policy::print(std::ostream& o) const
+ {
+ print_bool(o, "allow_tls10", allow_tls10());
+ print_bool(o, "allow_tls11", allow_tls11());
+ print_bool(o, "allow_tls12", allow_tls12());
+ print_bool(o, "allow_dtls10", allow_dtls10());
+ print_bool(o, "allow_dtls12", allow_dtls12());
+ print_vec(o, "ciphers", allowed_ciphers());
+ print_vec(o, "macs", allowed_macs());
+ print_vec(o, "signature_hashes", allowed_signature_hashes());
+ print_vec(o, "signature_methods", allowed_signature_methods());
+ print_vec(o, "key_exchange_methods", allowed_key_exchange_methods());
+ print_vec(o, "key_exchange_groups", key_exchange_groups());
+
+ print_bool(o, "allow_insecure_renegotiation", allow_insecure_renegotiation());
+ print_bool(o, "include_time_in_hello_random", include_time_in_hello_random());
+ print_bool(o, "allow_server_initiated_renegotiation", allow_server_initiated_renegotiation());
+ 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, "support_cert_status_message", support_cert_status_message());
+ o << "session_ticket_lifetime = " << session_ticket_lifetime() << '\n';
+ o << "minimum_dh_group_size = " << minimum_dh_group_size() << '\n';
+ o << "minimum_ecdh_group_size = " << minimum_ecdh_group_size() << '\n';
+ o << "minimum_rsa_bits = " << minimum_rsa_bits() << '\n';
+ o << "minimum_signature_strength = " << minimum_signature_strength() << '\n';
+ }
+
+std::string Policy::to_string() const
+ {
+ std::ostringstream oss;
+ this->print(oss);
+ return oss.str();
+ }
+
+std::vector<std::string> Strict_Policy::allowed_ciphers() const
+ {
+ return { "ChaCha20Poly1305", "AES-256/GCM", "AES-128/GCM" };
+ }
+
+std::vector<std::string> Strict_Policy::allowed_signature_hashes() const
+ {
+ return { "SHA-512", "SHA-384"};
+ }
+
+std::vector<std::string> Strict_Policy::allowed_macs() const
+ {
+ return { "AEAD" };
+ }
+
+std::vector<std::string> Strict_Policy::allowed_key_exchange_methods() const
+ {
+ return { "CECPQ1", "ECDH" };
+ }
+
+bool Strict_Policy::allow_tls10() const { return false; }
+bool Strict_Policy::allow_tls11() const { return false; }
+bool Strict_Policy::allow_tls12() const { return true; }
+bool Strict_Policy::allow_dtls10() const { return false; }
+bool Strict_Policy::allow_dtls12() const { return true; }
+
+}
+
+}
diff --git a/comm/third_party/botan/src/lib/tls/tls_policy.h b/comm/third_party/botan/src/lib/tls/tls_policy.h
new file mode 100644
index 0000000000..4fbbd75453
--- /dev/null
+++ b/comm/third_party/botan/src/lib/tls/tls_policy.h
@@ -0,0 +1,616 @@
+/*
+* Hooks for application level policies on TLS connections
+* (C) 2004-2006,2013 Jack Lloyd
+* 2017 Harry Reimann, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_TLS_POLICY_H_
+#define BOTAN_TLS_POLICY_H_
+
+#include <botan/tls_version.h>
+#include <botan/tls_algos.h>
+#include <botan/tls_ciphersuite.h>
+#include <vector>
+#include <map>
+
+namespace Botan {
+
+class Public_Key;
+
+namespace TLS {
+
+/**
+* TLS Policy Base Class
+* Inherit and overload as desired to suit local policy concerns
+*/
+class BOTAN_PUBLIC_API(2,0) Policy
+ {
+ public:
+
+ /**
+ * Returns a list of ciphers we are willing to negotiate, in
+ * order of preference.
+ */
+ virtual std::vector<std::string> allowed_ciphers() const;
+
+ /**
+ * Returns a list of hash algorithms we are willing to use for
+ * signatures, in order of preference.
+ */
+ virtual std::vector<std::string> allowed_signature_hashes() const;
+
+ /**
+ * Returns a list of MAC algorithms we are willing to use.
+ */
+ virtual std::vector<std::string> allowed_macs() const;
+
+ /**
+ * Returns a list of key exchange algorithms we are willing to
+ * use, in order of preference. Allowed values: DH, empty string
+ * (representing RSA using server certificate key)
+ */
+ virtual std::vector<std::string> allowed_key_exchange_methods() const;
+
+ /**
+ * Returns a list of signature algorithms we are willing to
+ * use, in order of preference. Allowed values RSA and DSA.
+ */
+ virtual std::vector<std::string> allowed_signature_methods() const;
+
+ virtual std::vector<Signature_Scheme> allowed_signature_schemes() const;
+
+ /**
+ * The minimum signature strength we will accept
+ * Returning 80 allows RSA 1024 and SHA-1. Values larger than 80 disable SHA-1 support.
+ * Returning 110 allows RSA 2048.
+ * Return 128 to force ECC (P-256) or large (~3000 bit) RSA keys.
+ * Default is 110
+ */
+ virtual size_t minimum_signature_strength() const;
+
+ /**
+ * Return if cert revocation info (CRL/OCSP) is required
+ * If true, validation will fail unless a valid CRL or OCSP response
+ * was examined.
+ */
+ virtual bool require_cert_revocation_info() const;
+
+ bool allowed_signature_method(const std::string& sig_method) const;
+ bool allowed_signature_hash(const std::string& hash) const;
+
+ /**
+ * Return list of ECC curves and FFDHE groups we are willing to
+ * use in order of preference.
+ */
+ virtual std::vector<Group_Params> key_exchange_groups() const;
+
+ /**
+ * Request that ECC curve points are sent compressed
+ */
+ virtual bool use_ecc_point_compression() const;
+
+ /**
+ * Select a key exchange group to use, from the list of groups sent by the
+ * peer. If none are acceptable, return Group_Params::NONE
+ */
+ virtual Group_Params choose_key_exchange_group(const std::vector<Group_Params>& peer_groups) const;
+
+ /**
+ * Allow renegotiation even if the counterparty doesn't
+ * support the secure renegotiation extension.
+ *
+ * @warning Changing this to true exposes you to injected
+ * plaintext attacks. Read RFC 5746 for background.
+ */
+ virtual bool allow_insecure_renegotiation() const;
+
+ /**
+ * The protocol dictates that the first 32 bits of the random
+ * field are the current time in seconds. However this allows
+ * client fingerprinting attacks. Set to false to disable, in
+ * which case random bytes will be used instead.
+ */
+ virtual bool include_time_in_hello_random() const;
+
+ /**
+ * Consulted by server side. If true, allows clients to initiate a new handshake
+ */
+ virtual bool allow_client_initiated_renegotiation() const;
+
+ /**
+ * Consulted by client side. If true, allows servers to initiate a new handshake
+ */
+ virtual bool allow_server_initiated_renegotiation() const;
+
+ /**
+ * If true, a request to renegotiate will close the connection with
+ * a fatal alert. Otherwise, a warning alert is sent.
+ */
+ virtual bool abort_connection_on_undesired_renegotiation() const;
+
+ virtual bool only_resume_with_exact_version() const;
+
+ /**
+ * Allow TLS v1.0
+ */
+ virtual bool allow_tls10() const;
+
+ /**
+ * Allow TLS v1.1
+ */
+ virtual bool allow_tls11() const;
+
+ /**
+ * Allow TLS v1.2
+ */
+ virtual bool allow_tls12() const;
+
+ /**
+ * Allow DTLS v1.0
+ */
+ virtual bool allow_dtls10() const;
+
+ /**
+ * Allow DTLS v1.2
+ */
+ virtual bool allow_dtls12() const;
+
+ virtual Group_Params default_dh_group() const;
+
+ /**
+ * Return the minimum DH group size we're willing to use
+ * Default is currently 1024 (insecure), should be 2048
+ */
+ virtual size_t minimum_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.
+ */
+ virtual size_t minimum_ecdsa_group_size() const;
+
+ /**
+ * Return the minimum ECDH group size we're willing to use
+ * for key exchange
+ *
+ * Default 255, allowing x25519 and larger
+ * x25519 is the smallest curve we will negotiate
+ * P-521 is the largest
+ */
+ virtual size_t minimum_ecdh_group_size() const;
+
+ /**
+ * Return the minimum bit size we're willing to accept for RSA
+ * key exchange or server signatures.
+ *
+ * It does not place any requirements on the size of any RSA signature(s)
+ * which were used to check the server certificate. This is only
+ * concerned with the server's public key.
+ *
+ * Default is 2048 which is smallest RSA key size still secure
+ * for medium term security.
+ */
+ virtual size_t minimum_rsa_bits() const;
+
+ /**
+ * Minimum DSA group size, default 2048 bits
+ */
+ virtual size_t minimum_dsa_group_size() const;
+
+ /**
+ * Throw an exception if you don't like the peer's key.
+ * Default impl checks the key size against minimum_rsa_bits, minimum_ecdsa_group_size,
+ * or minimum_ecdh_group_size depending on the key's type.
+ * Override if you'd like to perform some other kind of test on
+ * (or logging of) the peer's keys.
+ */
+ virtual void check_peer_key_acceptable(const Public_Key& public_key) const;
+
+ /**
+ * If this function returns false, unknown SRP/PSK identifiers
+ * will be rejected with an unknown_psk_identifier alert as soon
+ * as the non-existence is identified. Otherwise, a false
+ * identifier value will be used and the protocol allowed to
+ * proceed, causing the handshake to eventually fail without
+ * revealing that the username does not exist on this system.
+ */
+ virtual bool hide_unknown_users() const;
+
+ /**
+ * Return the allowed lifetime of a session ticket. If 0, session
+ * tickets do not expire until the session ticket key rolls over.
+ * Expired session tickets cannot be used to resume a session.
+ */
+ virtual uint32_t session_ticket_lifetime() const;
+
+ /**
+ * If this returns a non-empty vector, and DTLS is negotiated,
+ * then we will also attempt to negotiate the SRTP extension from
+ * RFC 5764 using the returned values as the profile ids.
+ */
+ virtual std::vector<uint16_t> srtp_profiles() const;
+
+ /**
+ * @return true if and only if we are willing to accept this version
+ * Default accepts TLS v1.0 and later or DTLS v1.2 or later.
+ */
+ virtual bool acceptable_protocol_version(Protocol_Version version) const;
+
+ /**
+ * Returns the more recent protocol version we are willing to
+ * use, for either TLS or DTLS depending on datagram param.
+ * Shouldn't ever need to override this unless you want to allow
+ * a user to disable use of TLS v1.2 (which is *not recommended*)
+ */
+ virtual Protocol_Version latest_supported_version(bool datagram) const;
+
+ /**
+ * When offering this version, should we send a fallback SCSV?
+ * Default returns true iff version is not the latest version the
+ * policy allows, exists to allow override in case of interop problems.
+ */
+ virtual bool send_fallback_scsv(Protocol_Version version) const;
+
+ /**
+ * Allows policy to reject any ciphersuites which are undesirable
+ * for whatever reason without having to reimplement ciphersuite_list
+ */
+ virtual bool acceptable_ciphersuite(const Ciphersuite& suite) const;
+
+ /**
+ * @return true if servers should choose the ciphersuite matching
+ * their highest preference, rather than the clients.
+ * Has no effect on client side.
+ */
+ virtual bool server_uses_own_ciphersuite_preferences() const;
+
+ /**
+ * Indicates whether the encrypt-then-MAC extension should be negotiated
+ * (RFC 7366)
+ */
+ virtual bool negotiate_encrypt_then_mac() const;
+
+ /**
+ * Indicates whether certificate status messages should be supported
+ */
+ virtual bool support_cert_status_message() const;
+
+ /**
+ * Indicate if client certificate authentication is required.
+ * If true, then a cert will be requested and if the client does
+ * not send a certificate the connection will be closed.
+ */
+ virtual bool require_client_certificate_authentication() const;
+
+ /**
+ * Indicate if client certificate authentication is requested.
+ * If true, then a cert will be requested.
+ */
+ virtual bool request_client_certificate_authentication() const;
+
+ /**
+ * If true, then allow a DTLS client to restart a connection to the
+ * same server association as described in section 4.2.8 of the DTLS RFC
+ */
+ virtual bool allow_dtls_epoch0_restart() const;
+
+ /**
+ * Return allowed ciphersuites, in order of preference
+ */
+ virtual std::vector<uint16_t> ciphersuite_list(Protocol_Version version,
+ bool have_srp) const;
+
+ /**
+ * @return the default MTU for DTLS
+ */
+ virtual size_t dtls_default_mtu() const;
+
+ /**
+ * @return the initial timeout for DTLS
+ */
+ virtual size_t dtls_initial_timeout() const;
+
+ /**
+ * @return the maximum timeout for DTLS
+ */
+ virtual size_t dtls_maximum_timeout() 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;
+
+ virtual bool allow_resumption_for_renegotiation() const;
+
+ /**
+ * Convert this policy to a printable format.
+ * @param o stream to be printed to
+ */
+ virtual void print(std::ostream& o) const;
+
+ /**
+ * Convert this policy to a printable format.
+ * Same as calling `print` on a ostringstream and reading o.str()
+ */
+ std::string to_string() const;
+
+ virtual ~Policy() = default;
+ };
+
+typedef Policy Default_Policy;
+
+/**
+* NSA Suite B 128-bit security level (RFC 6460)
+*
+* @warning As of August 2015 NSA indicated only the 192-bit Suite B
+* should be used for all classification levels.
+*/
+class BOTAN_PUBLIC_API(2,0) NSA_Suite_B_128 : public Policy
+ {
+ public:
+ std::vector<std::string> allowed_ciphers() const override
+ { return std::vector<std::string>({"AES-128/GCM"}); }
+
+ std::vector<std::string> allowed_signature_hashes() const override
+ { return std::vector<std::string>({"SHA-256"}); }
+
+ std::vector<std::string> allowed_macs() const override
+ { return std::vector<std::string>({"AEAD"}); }
+
+ std::vector<std::string> allowed_key_exchange_methods() const override
+ { return std::vector<std::string>({"ECDH"}); }
+
+ std::vector<std::string> allowed_signature_methods() const override
+ { return std::vector<std::string>({"ECDSA"}); }
+
+ std::vector<Group_Params> key_exchange_groups() const override
+ { return {Group_Params::SECP256R1}; }
+
+ size_t minimum_signature_strength() const override { return 128; }
+
+ bool allow_tls10() const override { return false; }
+ bool allow_tls11() const override { return false; }
+ bool allow_tls12() const override { return true; }
+ bool allow_dtls10() const override { return false; }
+ bool allow_dtls12() const override { return false; }
+ };
+
+/**
+* NSA Suite B 192-bit security level (RFC 6460)
+*/
+class BOTAN_PUBLIC_API(2,7) NSA_Suite_B_192 : public Policy
+ {
+ public:
+ std::vector<std::string> allowed_ciphers() const override
+ { return std::vector<std::string>({"AES-256/GCM"}); }
+
+ std::vector<std::string> allowed_signature_hashes() const override
+ { return std::vector<std::string>({"SHA-384"}); }
+
+ std::vector<std::string> allowed_macs() const override
+ { return std::vector<std::string>({"AEAD"}); }
+
+ std::vector<std::string> allowed_key_exchange_methods() const override
+ { return std::vector<std::string>({"ECDH"}); }
+
+ std::vector<std::string> allowed_signature_methods() const override
+ { return std::vector<std::string>({"ECDSA"}); }
+
+ std::vector<Group_Params> key_exchange_groups() const override
+ { return {Group_Params::SECP384R1}; }
+
+ size_t minimum_signature_strength() const override { return 192; }
+
+ bool allow_tls10() const override { return false; }
+ bool allow_tls11() const override { return false; }
+ bool allow_tls12() const override { return true; }
+ bool allow_dtls10() const override { return false; }
+ bool allow_dtls12() const override { return false; }
+ };
+
+/**
+* BSI TR-02102-2 Policy
+*/
+class BOTAN_PUBLIC_API(2,0) BSI_TR_02102_2 : public Policy
+ {
+ public:
+ std::vector<std::string> allowed_ciphers() const override
+ {
+ return std::vector<std::string>({"AES-256/GCM", "AES-128/GCM", "AES-256/CCM", "AES-128/CCM", "AES-256", "AES-128"});
+ }
+
+ std::vector<std::string> allowed_signature_hashes() const override
+ {
+ return std::vector<std::string>({"SHA-512", "SHA-384", "SHA-256"});
+ }
+
+ std::vector<std::string> allowed_macs() const override
+ {
+ return std::vector<std::string>({"AEAD", "SHA-384", "SHA-256"});
+ }
+
+ std::vector<std::string> allowed_key_exchange_methods() const override
+ {
+ return std::vector<std::string>({"ECDH", "DH", "ECDHE_PSK", "DHE_PSK"});
+ }
+
+ std::vector<std::string> allowed_signature_methods() const override
+ {
+ return std::vector<std::string>({"ECDSA", "RSA", "DSA"});
+ }
+
+ std::vector<Group_Params> key_exchange_groups() const override
+ {
+ return std::vector<Group_Params>({
+ Group_Params::BRAINPOOL512R1,
+ Group_Params::BRAINPOOL384R1,
+ Group_Params::BRAINPOOL256R1,
+ Group_Params::SECP384R1,
+ Group_Params::SECP256R1,
+ Group_Params::FFDHE_4096,
+ Group_Params::FFDHE_3072,
+ Group_Params::FFDHE_2048
+ });
+ }
+
+ bool allow_insecure_renegotiation() const override { return false; }
+ bool allow_server_initiated_renegotiation() const override { return true; }
+ bool server_uses_own_ciphersuite_preferences() const override { return true; }
+ bool negotiate_encrypt_then_mac() const override { return true; }
+
+ size_t minimum_rsa_bits() const override { return 2000; }
+ size_t minimum_dh_group_size() const override { return 2000; }
+ size_t minimum_dsa_group_size() const override { return 2000; }
+
+ size_t minimum_ecdh_group_size() const override { return 250; }
+ size_t minimum_ecdsa_group_size() const override { return 250; }
+
+ bool allow_tls10() const override { return false; }
+ bool allow_tls11() const override { return false; }
+ bool allow_tls12() const override { return true; }
+ bool allow_dtls10() const override { return false; }
+ bool allow_dtls12() const override { return false; }
+ };
+
+/**
+* Policy for DTLS. We require DTLS v1.2 and an AEAD mode.
+*/
+class BOTAN_PUBLIC_API(2,0) Datagram_Policy : public Policy
+ {
+ public:
+ std::vector<std::string> allowed_macs() const override
+ { return std::vector<std::string>({"AEAD"}); }
+
+ bool allow_tls10() const override { return false; }
+ bool allow_tls11() const override { return false; }
+ bool allow_tls12() const override { return false; }
+ bool allow_dtls10() const override { return false; }
+ bool allow_dtls12() const override { return true; }
+ };
+
+/*
+* This policy requires a secure version of TLS and disables all insecure
+* algorithms. It is compatible with other botan TLSes (including those using the
+* default policy) and with many other recent implementations. It is a great idea
+* to use if you control both sides of the protocol and don't have to worry
+* about ancient and/or bizarre TLS implementations.
+*/
+class BOTAN_PUBLIC_API(2,0) Strict_Policy : public Policy
+ {
+ public:
+ std::vector<std::string> allowed_ciphers() const override;
+
+ std::vector<std::string> allowed_signature_hashes() const override;
+
+ std::vector<std::string> allowed_macs() const override;
+
+ std::vector<std::string> allowed_key_exchange_methods() const override;
+
+ bool allow_tls10() const override;
+ bool allow_tls11() const override;
+ bool allow_tls12() const override;
+ bool allow_dtls10() const override;
+ bool allow_dtls12() const override;
+ };
+
+class BOTAN_PUBLIC_API(2,0) Text_Policy : public Policy
+ {
+ public:
+
+ std::vector<std::string> allowed_ciphers() const override;
+
+ std::vector<std::string> allowed_signature_hashes() const override;
+
+ std::vector<std::string> allowed_macs() const override;
+
+ std::vector<std::string> allowed_key_exchange_methods() const override;
+
+ std::vector<std::string> allowed_signature_methods() const override;
+
+ std::vector<Group_Params> key_exchange_groups() const override;
+
+ bool use_ecc_point_compression() const override;
+
+ bool allow_tls10() const override;
+
+ bool allow_tls11() const override;
+
+ bool allow_tls12() const override;
+
+ bool allow_dtls10() const override;
+
+ bool allow_dtls12() const override;
+
+ bool allow_insecure_renegotiation() const override;
+
+ bool include_time_in_hello_random() const override;
+
+ bool allow_client_initiated_renegotiation() const override;
+ bool allow_server_initiated_renegotiation() const override;
+
+ bool server_uses_own_ciphersuite_preferences() const override;
+
+ bool negotiate_encrypt_then_mac() const override;
+
+ bool support_cert_status_message() const override;
+
+ bool require_client_certificate_authentication() const override;
+
+ size_t minimum_ecdh_group_size() const override;
+
+ size_t minimum_ecdsa_group_size() const override;
+
+ size_t minimum_dh_group_size() const override;
+
+ size_t minimum_rsa_bits() const override;
+
+ size_t minimum_signature_strength() const override;
+
+ size_t dtls_default_mtu() const override;
+
+ size_t dtls_initial_timeout() const override;
+
+ size_t dtls_maximum_timeout() const override;
+
+ bool require_cert_revocation_info() const override;
+
+ bool hide_unknown_users() const override;
+
+ uint32_t session_ticket_lifetime() const override;
+
+ bool send_fallback_scsv(Protocol_Version version) const override;
+
+ std::vector<uint16_t> srtp_profiles() const override;
+
+ void set(const std::string& k, const std::string& v);
+
+ explicit Text_Policy(const std::string& s);
+
+ explicit Text_Policy(std::istream& in);
+
+ protected:
+
+ std::vector<std::string> get_list(const std::string& key,
+ const std::vector<std::string>& def) const;
+
+ size_t get_len(const std::string& key, size_t def) const;
+
+ bool get_bool(const std::string& key, bool def) const;
+
+ std::string get_str(const std::string& key, const std::string& def = "") const;
+
+ bool set_value(const std::string& key, const std::string& val, bool overwrite);
+
+ private:
+ std::map<std::string, std::string> m_kv;
+ };
+
+}
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/tls/tls_reader.h b/comm/third_party/botan/src/lib/tls/tls_reader.h
new file mode 100644
index 0000000000..c6cffed320
--- /dev/null
+++ b/comm/third_party/botan/src/lib/tls/tls_reader.h
@@ -0,0 +1,231 @@
+/*
+* TLS Data Reader
+* (C) 2010-2011,2014 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_TLS_READER_H_
+#define BOTAN_TLS_READER_H_
+
+#include <botan/exceptn.h>
+#include <botan/secmem.h>
+#include <botan/loadstor.h>
+#include <string>
+#include <vector>
+
+namespace Botan {
+
+namespace TLS {
+
+/**
+* Helper class for decoding TLS protocol messages
+*/
+class TLS_Data_Reader final
+ {
+ public:
+ TLS_Data_Reader(const char* type, const std::vector<uint8_t>& buf_in) :
+ m_typename(type), m_buf(buf_in), m_offset(0) {}
+
+ void assert_done() const
+ {
+ if(has_remaining())
+ throw decode_error("Extra bytes at end of message");
+ }
+
+ size_t read_so_far() const { return m_offset; }
+
+ size_t remaining_bytes() const { return m_buf.size() - m_offset; }
+
+ bool has_remaining() const { return (remaining_bytes() > 0); }
+
+ std::vector<uint8_t> get_remaining()
+ {
+ return std::vector<uint8_t>(m_buf.begin() + m_offset, m_buf.end());
+ }
+
+ void discard_next(size_t bytes)
+ {
+ assert_at_least(bytes);
+ m_offset += bytes;
+ }
+
+ 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]);
+ m_offset += 4;
+ 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]);
+ m_offset += 2;
+ return result;
+ }
+
+ uint8_t get_byte()
+ {
+ assert_at_least(1);
+ uint8_t result = m_buf[m_offset];
+ m_offset += 1;
+ return result;
+ }
+
+ template<typename T, typename Container>
+ Container get_elem(size_t num_elems)
+ {
+ assert_at_least(num_elems * sizeof(T));
+
+ Container result(num_elems);
+
+ for(size_t i = 0; i != num_elems; ++i)
+ result[i] = load_be<T>(&m_buf[m_offset], i);
+
+ m_offset += num_elems * sizeof(T);
+
+ return result;
+ }
+
+ template<typename T>
+ std::vector<T> get_range(size_t len_bytes,
+ size_t min_elems,
+ size_t max_elems)
+ {
+ const size_t num_elems =
+ get_num_elems(len_bytes, sizeof(T), min_elems, max_elems);
+
+ return get_elem<T, std::vector<T>>(num_elems);
+ }
+
+ template<typename T>
+ std::vector<T> get_range_vector(size_t len_bytes,
+ size_t min_elems,
+ size_t max_elems)
+ {
+ const size_t num_elems =
+ get_num_elems(len_bytes, sizeof(T), min_elems, max_elems);
+
+ return get_elem<T, std::vector<T>>(num_elems);
+ }
+
+ std::string get_string(size_t len_bytes,
+ size_t min_bytes,
+ size_t max_bytes)
+ {
+ std::vector<uint8_t> v =
+ get_range_vector<uint8_t>(len_bytes, min_bytes, max_bytes);
+
+ return std::string(cast_uint8_ptr_to_char(v.data()), v.size());
+ }
+
+ template<typename T>
+ std::vector<T> get_fixed(size_t size)
+ {
+ return get_elem<T, std::vector<T>>(size);
+ }
+
+ private:
+ size_t get_length_field(size_t len_bytes)
+ {
+ assert_at_least(len_bytes);
+
+ if(len_bytes == 1)
+ return get_byte();
+ else if(len_bytes == 2)
+ return get_uint16_t();
+
+ throw decode_error("Bad length size");
+ }
+
+ size_t get_num_elems(size_t len_bytes,
+ size_t T_size,
+ size_t min_elems,
+ size_t max_elems)
+ {
+ const size_t byte_length = get_length_field(len_bytes);
+
+ if(byte_length % T_size != 0)
+ throw decode_error("Size isn't multiple of T");
+
+ const size_t num_elems = byte_length / T_size;
+
+ if(num_elems < min_elems || num_elems > max_elems)
+ throw decode_error("Length field outside parameters");
+
+ 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");
+ }
+
+ Decoding_Error decode_error(const std::string& why) const
+ {
+ return Decoding_Error("Invalid " + std::string(m_typename) + ": " + why);
+ }
+
+ const char* m_typename;
+ const std::vector<uint8_t>& m_buf;
+ size_t m_offset;
+ };
+
+/**
+* Helper function for encoding length-tagged vectors
+*/
+template<typename T, typename Alloc>
+void append_tls_length_value(std::vector<uint8_t, Alloc>& 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)
+ throw Invalid_Argument("append_tls_length_value: invalid tag size");
+
+ if((tag_size == 1 && val_bytes > 255) ||
+ (tag_size == 2 && val_bytes > 65535))
+ throw Invalid_Argument("append_tls_length_value: value too large");
+
+ for(size_t i = 0; i != tag_size; ++i)
+ buf.push_back(get_byte(sizeof(val_bytes)-tag_size+i, val_bytes));
+
+ for(size_t i = 0; i != vals_size; ++i)
+ for(size_t j = 0; j != T_size; ++j)
+ buf.push_back(get_byte(j, vals[i]));
+ }
+
+template<typename T, typename Alloc, typename Alloc2>
+void append_tls_length_value(std::vector<uint8_t, Alloc>& buf,
+ const std::vector<T, Alloc2>& vals,
+ size_t tag_size)
+ {
+ append_tls_length_value(buf, vals.data(), vals.size(), tag_size);
+ }
+
+template<typename Alloc>
+void append_tls_length_value(std::vector<uint8_t, Alloc>& buf,
+ const std::string& str,
+ size_t tag_size)
+ {
+ append_tls_length_value(buf,
+ cast_char_ptr_to_uint8(str.data()),
+ str.size(),
+ tag_size);
+ }
+
+}
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/tls/tls_record.cpp b/comm/third_party/botan/src/lib/tls/tls_record.cpp
new file mode 100644
index 0000000000..7a4044e69f
--- /dev/null
+++ b/comm/third_party/botan/src/lib/tls/tls_record.cpp
@@ -0,0 +1,534 @@
+/*
+* TLS Record Handling
+* (C) 2012,2013,2014,2015,2016,2019 Jack Lloyd
+* 2016 Juraj Somorovsky
+* 2016 Matthias Gierlings
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/tls_record.h>
+#include <botan/tls_ciphersuite.h>
+#include <botan/tls_exceptn.h>
+#include <botan/loadstor.h>
+#include <botan/internal/tls_seq_numbers.h>
+#include <botan/internal/tls_session_key.h>
+#include <botan/internal/rounding.h>
+#include <botan/internal/ct_utils.h>
+#include <botan/rng.h>
+
+#if defined(BOTAN_HAS_TLS_CBC)
+ #include <botan/internal/tls_cbc.h>
+#endif
+
+namespace Botan {
+
+namespace TLS {
+
+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) :
+ m_start_time(std::chrono::system_clock::now())
+ {
+ 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<uint8_t>& aead_key = keys.aead_key(side);
+ m_nonce = keys.nonce(side);
+
+ 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 cipher = BlockCipher::create_or_throw(suite.cipher_algo());
+
+ if(our_side)
+ {
+ m_aead.reset(new TLS_CBC_HMAC_AEAD_Encryption(
+ std::move(cipher),
+ std::move(mac),
+ suite.cipher_keylen(),
+ suite.mac_keylen(),
+ version,
+ uses_encrypt_then_mac));
+ }
+ else
+ {
+ m_aead.reset(new TLS_CBC_HMAC_AEAD_Decryption(
+ std::move(cipher),
+ std::move(mac),
+ suite.cipher_keylen(),
+ suite.mac_keylen(),
+ version,
+ uses_encrypt_then_mac));
+ }
+
+#else
+ BOTAN_UNUSED(uses_encrypt_then_mac);
+ throw Internal_Error("Negotiated disabled TLS CBC+HMAC ciphersuite");
+#endif
+ }
+ else
+ {
+ m_aead = AEAD_Mode::create_or_throw(suite.cipher_algo(), our_side ? ENCRYPTION : DECRYPTION);
+ }
+
+ m_aead->set_key(aead_key);
+ }
+
+std::vector<uint8_t> Connection_Cipher_State::aead_nonce(uint64_t seq, RandomNumberGenerator& rng)
+ {
+ switch(m_nonce_format)
+ {
+ case Nonce_Format::CBC_MODE:
+ {
+ if(m_nonce.size())
+ {
+ std::vector<uint8_t> nonce;
+ nonce.swap(m_nonce);
+ return nonce;
+ }
+ std::vector<uint8_t> nonce(nonce_bytes_from_record());
+ rng.randomize(nonce.data(), nonce.size());
+ return nonce;
+ }
+ case Nonce_Format::AEAD_XOR_12:
+ {
+ std::vector<uint8_t> nonce(12);
+ store_be(seq, nonce.data() + 4);
+ xor_buf(nonce, m_nonce.data(), m_nonce.size());
+ return nonce;
+ }
+ case Nonce_Format::AEAD_IMPLICIT_4:
+ {
+ BOTAN_ASSERT_NOMSG(m_nonce.size() == 4);
+ std::vector<uint8_t> nonce(12);
+ copy_mem(&nonce[0], m_nonce.data(), 4);
+ store_be(seq, &nonce[nonce_bytes_from_handshake()]);
+ return nonce;
+ }
+ }
+
+ throw Invalid_State("Unknown nonce format specified");
+ }
+
+std::vector<uint8_t>
+Connection_Cipher_State::aead_nonce(const uint8_t record[], size_t record_len, uint64_t seq)
+ {
+ switch(m_nonce_format)
+ {
+ case Nonce_Format::CBC_MODE:
+ {
+ if(nonce_bytes_from_record() == 0 && m_nonce.size())
+ {
+ std::vector<uint8_t> 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");
+ std::vector<uint8_t> nonce(record, record + nonce_bytes_from_record());
+ return nonce;
+ }
+ case Nonce_Format::AEAD_XOR_12:
+ {
+ std::vector<uint8_t> nonce(12);
+ store_be(seq, nonce.data() + 4);
+ xor_buf(nonce, m_nonce.data(), m_nonce.size());
+ return nonce;
+ }
+ case Nonce_Format::AEAD_IMPLICIT_4:
+ {
+ BOTAN_ASSERT_NOMSG(m_nonce.size() == 4);
+ if(record_len < nonce_bytes_from_record())
+ throw Decoding_Error("Invalid AEAD packet too short to be valid");
+ std::vector<uint8_t> nonce(12);
+ copy_mem(&nonce[0], m_nonce.data(), 4);
+ copy_mem(&nonce[nonce_bytes_from_handshake()], record, nonce_bytes_from_record());
+ return nonce;
+ }
+ }
+
+ throw Invalid_State("Unknown nonce format specified");
+ }
+
+std::vector<uint8_t>
+Connection_Cipher_State::format_ad(uint64_t msg_sequence,
+ uint8_t msg_type,
+ Protocol_Version version,
+ uint16_t msg_length)
+ {
+ std::vector<uint8_t> ad(13);
+
+ store_be(msg_sequence, &ad[0]);
+ ad[8] = msg_type;
+ ad[9] = version.major_version();
+ ad[10] = version.minor_version();
+ ad[11] = get_byte(0, msg_length);
+ ad[12] = get_byte(1, msg_length);
+
+ return ad;
+ }
+
+namespace {
+
+inline void append_u16_len(secure_vector<uint8_t>& output, size_t len_field)
+ {
+ const uint16_t len16 = static_cast<uint16_t>(len_field);
+ BOTAN_ASSERT_EQUAL(len_field, len16, "No truncation");
+ output.push_back(get_byte(0, len16));
+ output.push_back(get_byte(1, len16));
+ }
+
+void write_record_header(secure_vector<uint8_t>& output,
+ uint8_t record_type,
+ Protocol_Version version,
+ uint64_t record_sequence)
+ {
+ output.clear();
+
+ output.push_back(record_type);
+ output.push_back(version.major_version());
+ output.push_back(version.minor_version());
+
+ if(version.is_datagram_protocol())
+ {
+ for(size_t i = 0; i != 8; ++i)
+ output.push_back(get_byte(i, record_sequence));
+ }
+ }
+
+}
+
+void write_unencrypted_record(secure_vector<uint8_t>& output,
+ uint8_t record_type,
+ Protocol_Version version,
+ uint64_t record_sequence,
+ const uint8_t* message,
+ size_t message_len)
+ {
+ if(record_type == APPLICATION_DATA)
+ throw Internal_Error("Writing an unencrypted TLS application data record");
+ write_record_header(output, record_type, version, record_sequence);
+ append_u16_len(output, message_len);
+ output.insert(output.end(), message, message + message_len);
+ }
+
+void write_record(secure_vector<uint8_t>& output,
+ uint8_t record_type,
+ Protocol_Version version,
+ uint64_t record_sequence,
+ const uint8_t* message,
+ size_t message_len,
+ Connection_Cipher_State& cs,
+ RandomNumberGenerator& rng)
+ {
+ write_record_header(output, record_type, version, record_sequence);
+
+ AEAD_Mode& aead = cs.aead();
+ std::vector<uint8_t> aad = cs.format_ad(record_sequence, record_type, version, static_cast<uint16_t>(message_len));
+
+ const size_t ctext_size = aead.output_length(message_len);
+
+ const size_t rec_size = ctext_size + cs.nonce_bytes_from_record();
+
+ aead.set_ad(aad);
+
+ const std::vector<uint8_t> nonce = cs.aead_nonce(record_sequence, rng);
+
+ append_u16_len(output, rec_size);
+
+ if(cs.nonce_bytes_from_record() > 0)
+ {
+ if(cs.nonce_format() == Nonce_Format::CBC_MODE)
+ output += nonce;
+ else
+ output += std::make_pair(&nonce[cs.nonce_bytes_from_handshake()], cs.nonce_bytes_from_record());
+ }
+
+ const size_t header_size = output.size();
+ output += std::make_pair(message, message_len);
+
+ aead.start(nonce);
+ aead.finish(output, header_size);
+
+ BOTAN_ASSERT(output.size() < MAX_CIPHERTEXT_SIZE,
+ "Produced ciphertext larger than protocol allows");
+ }
+
+namespace {
+
+size_t fill_buffer_to(secure_vector<uint8_t>& readbuf,
+ const uint8_t*& input,
+ size_t& input_size,
+ size_t& input_consumed,
+ size_t desired)
+ {
+ if(readbuf.size() >= desired)
+ return 0; // already have it
+
+ const size_t taken = std::min(input_size, desired - readbuf.size());
+
+ readbuf.insert(readbuf.end(), input, input + taken);
+ input_consumed += taken;
+ input_size -= taken;
+ input += taken;
+
+ return (desired - readbuf.size()); // how many bytes do we still need?
+ }
+
+void decrypt_record(secure_vector<uint8_t>& output,
+ uint8_t record_contents[], size_t record_len,
+ uint64_t record_sequence,
+ Protocol_Version record_version,
+ Record_Type record_type,
+ Connection_Cipher_State& cs)
+ {
+ AEAD_Mode& aead = cs.aead();
+
+ const std::vector<uint8_t> nonce = cs.aead_nonce(record_contents, record_len, record_sequence);
+ const uint8_t* msg = &record_contents[cs.nonce_bytes_from_record()];
+ const size_t msg_length = record_len - cs.nonce_bytes_from_record();
+
+ /*
+ * This early rejection is based just on public information (length of the
+ * encrypted packet) and so does not leak any information. We used to use
+ * decode_error here which really is more appropriate, but that confuses some
+ * tools which are attempting automated detection of padding oracles,
+ * including older versions of TLS-Attacker.
+ */
+ if(msg_length < aead.minimum_final_size())
+ throw TLS_Exception(Alert::BAD_RECORD_MAC, "AEAD packet is shorter than the tag");
+
+ const size_t ptext_size = aead.output_length(msg_length);
+
+ aead.set_associated_data_vec(
+ cs.format_ad(record_sequence,
+ static_cast<uint8_t>(record_type),
+ record_version,
+ static_cast<uint16_t>(ptext_size))
+ );
+
+ aead.start(nonce);
+
+ output.assign(msg, msg + msg_length);
+ aead.finish(output, 0);
+ }
+
+Record_Header read_tls_record(secure_vector<uint8_t>& readbuf,
+ const uint8_t input[],
+ size_t input_len,
+ size_t& consumed,
+ secure_vector<uint8_t>& recbuf,
+ Connection_Sequence_Numbers* sequence_numbers,
+ 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))
+ {
+ return Record_Header(needed);
+ }
+
+ BOTAN_ASSERT_EQUAL(readbuf.size(), TLS_HEADER_SIZE, "Have an entire header");
+ }
+
+ if(readbuf[1] != 3)
+ {
+ throw TLS_Exception(Alert::PROTOCOL_VERSION,
+ "Got unexpected TLS record version");
+ }
+
+ const Protocol_Version version(readbuf[1], readbuf[2]);
+
+ const size_t record_size = make_uint16(readbuf[TLS_HEADER_SIZE-2],
+ readbuf[TLS_HEADER_SIZE-1]);
+
+ if(record_size > MAX_CIPHERTEXT_SIZE)
+ throw TLS_Exception(Alert::RECORD_OVERFLOW,
+ "Received a record that exceeds maximum size");
+
+ if(record_size == 0)
+ throw TLS_Exception(Alert::DECODE_ERROR,
+ "Received a completely empty record");
+
+ if(size_t needed = fill_buffer_to(readbuf, input, input_len, consumed, TLS_HEADER_SIZE + record_size))
+ {
+ return Record_Header(needed);
+ }
+
+ BOTAN_ASSERT_EQUAL(static_cast<size_t>(TLS_HEADER_SIZE) + record_size,
+ readbuf.size(),
+ "Have the full record");
+
+ const Record_Type type = static_cast<Record_Type>(readbuf[0]);
+
+ uint16_t epoch = 0;
+
+ uint64_t sequence = 0;
+ if(sequence_numbers)
+ {
+ sequence = sequence_numbers->next_read_sequence();
+ epoch = sequence_numbers->current_read_epoch();
+ }
+ else
+ {
+ // server initial handshake case
+ epoch = 0;
+ }
+
+ if(epoch == 0) // Unencrypted initial handshake
+ {
+ recbuf.assign(readbuf.begin() + TLS_HEADER_SIZE, readbuf.begin() + TLS_HEADER_SIZE + record_size);
+ readbuf.clear();
+ return Record_Header(sequence, version, type);
+ }
+
+ // Otherwise, decrypt, check MAC, return plaintext
+ auto cs = get_cipherstate(epoch);
+
+ BOTAN_ASSERT(cs, "Have cipherstate for this epoch");
+
+ decrypt_record(recbuf,
+ &readbuf[TLS_HEADER_SIZE],
+ record_size,
+ sequence,
+ version,
+ type,
+ *cs);
+
+ if(sequence_numbers)
+ sequence_numbers->read_accept(sequence);
+
+ readbuf.clear();
+ return Record_Header(sequence, version, type);
+ }
+
+Record_Header read_dtls_record(secure_vector<uint8_t>& readbuf,
+ const uint8_t input[],
+ size_t input_len,
+ size_t& consumed,
+ secure_vector<uint8_t>& recbuf,
+ Connection_Sequence_Numbers* sequence_numbers,
+ get_cipherstate_fn get_cipherstate,
+ bool allow_epoch0_restart)
+ {
+ if(readbuf.size() < DTLS_HEADER_SIZE) // header incomplete?
+ {
+ if(fill_buffer_to(readbuf, input, input_len, consumed, DTLS_HEADER_SIZE))
+ {
+ readbuf.clear();
+ return Record_Header(0);
+ }
+
+ BOTAN_ASSERT_EQUAL(readbuf.size(), DTLS_HEADER_SIZE, "Have an entire header");
+ }
+
+ const Protocol_Version version(readbuf[1], readbuf[2]);
+
+ if(version.is_datagram_protocol() == false)
+ {
+ readbuf.clear();
+ return Record_Header(0);
+ }
+
+ const size_t record_size = make_uint16(readbuf[DTLS_HEADER_SIZE-2],
+ readbuf[DTLS_HEADER_SIZE-1]);
+
+ if(record_size > MAX_CIPHERTEXT_SIZE)
+ {
+ // Too large to be valid, ignore it
+ readbuf.clear();
+ return Record_Header(0);
+ }
+
+ if(fill_buffer_to(readbuf, input, input_len, consumed, DTLS_HEADER_SIZE + record_size))
+ {
+ // Truncated packet?
+ readbuf.clear();
+ return Record_Header(0);
+ }
+
+ BOTAN_ASSERT_EQUAL(static_cast<size_t>(DTLS_HEADER_SIZE) + record_size, readbuf.size(),
+ "Have the full record");
+
+ const Record_Type type = static_cast<Record_Type>(readbuf[0]);
+
+ const uint64_t sequence = load_be<uint64_t>(&readbuf[3], 0);
+ const uint16_t epoch = (sequence >> 48);
+
+ const bool already_seen = sequence_numbers && sequence_numbers->already_seen(sequence);
+
+ if(already_seen && !(epoch == 0 && allow_epoch0_restart))
+ {
+ readbuf.clear();
+ return Record_Header(0);
+ }
+
+ if(epoch == 0) // Unencrypted initial handshake
+ {
+ recbuf.assign(readbuf.begin() + DTLS_HEADER_SIZE, readbuf.begin() + DTLS_HEADER_SIZE + record_size);
+ readbuf.clear();
+ if(sequence_numbers)
+ sequence_numbers->read_accept(sequence);
+ return Record_Header(sequence, version, type);
+ }
+
+ try
+ {
+ // Otherwise, decrypt, check MAC, return plaintext
+ auto cs = get_cipherstate(epoch);
+
+ BOTAN_ASSERT(cs, "Have cipherstate for this epoch");
+
+ decrypt_record(recbuf,
+ &readbuf[DTLS_HEADER_SIZE],
+ record_size,
+ sequence,
+ version,
+ type,
+ *cs);
+ }
+ catch(std::exception&)
+ {
+ readbuf.clear();
+ return Record_Header(0);
+ }
+
+ if(sequence_numbers)
+ sequence_numbers->read_accept(sequence);
+
+ readbuf.clear();
+ return Record_Header(sequence, version, type);
+ }
+
+}
+
+Record_Header read_record(bool is_datagram,
+ secure_vector<uint8_t>& readbuf,
+ const uint8_t input[],
+ size_t input_len,
+ size_t& consumed,
+ secure_vector<uint8_t>& recbuf,
+ Connection_Sequence_Numbers* sequence_numbers,
+ get_cipherstate_fn get_cipherstate,
+ bool allow_epoch0_restart)
+ {
+ if(is_datagram)
+ return read_dtls_record(readbuf, input, input_len, consumed,
+ recbuf, sequence_numbers, get_cipherstate, allow_epoch0_restart);
+ else
+ return read_tls_record(readbuf, input, input_len, consumed,
+ recbuf, sequence_numbers, get_cipherstate);
+ }
+
+}
+
+}
diff --git a/comm/third_party/botan/src/lib/tls/tls_record.h b/comm/third_party/botan/src/lib/tls/tls_record.h
new file mode 100644
index 0000000000..8af4e5c050
--- /dev/null
+++ b/comm/third_party/botan/src/lib/tls/tls_record.h
@@ -0,0 +1,188 @@
+/*
+* TLS Record Handling
+* (C) 2004-2012 Jack Lloyd
+* 2016 Matthias Gierlings
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_TLS_RECORDS_H_
+#define BOTAN_TLS_RECORDS_H_
+
+#include <botan/tls_algos.h>
+#include <botan/tls_magic.h>
+#include <botan/tls_version.h>
+#include <botan/aead.h>
+#include <vector>
+#include <chrono>
+#include <functional>
+
+namespace Botan {
+
+namespace TLS {
+
+class Ciphersuite;
+class Session_Keys;
+
+class Connection_Sequence_Numbers;
+
+/**
+* TLS Cipher State
+*/
+class Connection_Cipher_State final
+ {
+ public:
+ /**
+ * Initialize a new cipher state
+ */
+ Connection_Cipher_State(Protocol_Version version,
+ Connection_Side which_side,
+ bool is_our_side,
+ const Ciphersuite& suite,
+ const Session_Keys& keys,
+ bool uses_encrypt_then_mac);
+
+ AEAD_Mode& aead()
+ {
+ BOTAN_ASSERT_NONNULL(m_aead.get());
+ return *m_aead.get();
+ }
+
+ std::vector<uint8_t> aead_nonce(uint64_t seq, RandomNumberGenerator& rng);
+
+ std::vector<uint8_t> aead_nonce(const uint8_t record[], size_t record_len, uint64_t seq);
+
+ std::vector<uint8_t> format_ad(uint64_t seq, uint8_t type,
+ Protocol_Version version,
+ uint16_t ptext_length);
+
+ size_t nonce_bytes_from_handshake() const { return m_nonce_bytes_from_handshake; }
+ size_t nonce_bytes_from_record() const { return m_nonce_bytes_from_record; }
+
+ Nonce_Format nonce_format() const { return m_nonce_format; }
+
+ std::chrono::seconds age() const
+ {
+ return std::chrono::duration_cast<std::chrono::seconds>(
+ std::chrono::system_clock::now() - m_start_time);
+ }
+
+ private:
+ std::chrono::system_clock::time_point m_start_time;
+ std::unique_ptr<AEAD_Mode> m_aead;
+
+ std::vector<uint8_t> m_nonce;
+ Nonce_Format m_nonce_format;
+ size_t m_nonce_bytes_from_handshake;
+ size_t m_nonce_bytes_from_record;
+ };
+
+class Record_Header final
+ {
+ public:
+ 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(NO_RECORD)
+ {}
+
+ size_t needed() const { return m_needed; }
+
+ Protocol_Version version() const
+ {
+ BOTAN_ASSERT_NOMSG(m_needed == 0);
+ return m_version;
+ }
+
+ uint64_t sequence() const
+ {
+ BOTAN_ASSERT_NOMSG(m_needed == 0);
+ return m_sequence;
+ }
+
+ uint16_t epoch() const
+ {
+ return static_cast<uint16_t>(sequence() >> 48);
+ }
+
+ Record_Type type() const
+ {
+ BOTAN_ASSERT_NOMSG(m_needed == 0);
+ return m_type;
+ }
+
+ private:
+ size_t m_needed;
+ uint64_t m_sequence;
+ Protocol_Version m_version;
+ Record_Type m_type;
+ };
+
+/**
+* Create an initial (unencrypted) TLS handshake record
+* @param write_buffer the output record is placed here
+* @param record_type the record layer type
+* @param record_version the record layer version
+* @param record_sequence the record layer sequence number
+* @param message the record contents
+* @param message_len is size of message
+*/
+void write_unencrypted_record(secure_vector<uint8_t>& write_buffer,
+ uint8_t record_type,
+ Protocol_Version record_version,
+ uint64_t record_sequence,
+ const uint8_t* message,
+ size_t message_len);
+
+/**
+* Create a TLS record
+* @param write_buffer the output record is placed here
+* @param record_type the record layer type
+* @param record_version the record layer version
+* @param record_sequence the record layer sequence number
+* @param message the record contents
+* @param message_len is size of message
+* @param cipherstate is the writing cipher state
+* @param rng is a random number generator
+*/
+void write_record(secure_vector<uint8_t>& write_buffer,
+ uint8_t record_type,
+ Protocol_Version record_version,
+ uint64_t record_sequence,
+ const uint8_t* message,
+ size_t message_len,
+ Connection_Cipher_State& cipherstate,
+ RandomNumberGenerator& rng);
+
+// epoch -> cipher state
+typedef std::function<std::shared_ptr<Connection_Cipher_State> (uint16_t)> get_cipherstate_fn;
+
+/**
+* Decode a TLS record
+* @return zero if full message, else number of bytes still needed
+*/
+Record_Header read_record(bool is_datagram,
+ secure_vector<uint8_t>& read_buffer,
+ const uint8_t input[],
+ size_t input_len,
+ size_t& consumed,
+ secure_vector<uint8_t>& record_buf,
+ Connection_Sequence_Numbers* sequence_numbers,
+ get_cipherstate_fn get_cipherstate,
+ bool allow_epoch0_restart);
+
+}
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/tls/tls_seq_numbers.h b/comm/third_party/botan/src/lib/tls/tls_seq_numbers.h
new file mode 100644
index 0000000000..0a0a416f8f
--- /dev/null
+++ b/comm/third_party/botan/src/lib/tls/tls_seq_numbers.h
@@ -0,0 +1,174 @@
+/*
+* TLS Sequence Number Handling
+* (C) 2012 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_TLS_SEQ_NUMBERS_H_
+#define BOTAN_TLS_SEQ_NUMBERS_H_
+
+#include <botan/types.h>
+#include <map>
+
+namespace Botan {
+
+namespace TLS {
+
+class Connection_Sequence_Numbers
+ {
+ public:
+ virtual ~Connection_Sequence_Numbers() = default;
+
+ virtual void new_read_cipher_state() = 0;
+ virtual void new_write_cipher_state() = 0;
+
+ virtual uint16_t current_read_epoch() const = 0;
+ virtual uint16_t current_write_epoch() const = 0;
+
+ virtual uint64_t next_write_sequence(uint16_t) = 0;
+ virtual uint64_t next_read_sequence() = 0;
+
+ virtual bool already_seen(uint64_t seq) const = 0;
+ virtual void read_accept(uint64_t seq) = 0;
+
+ virtual void reset() = 0;
+ };
+
+class Stream_Sequence_Numbers final : public Connection_Sequence_Numbers
+ {
+ public:
+ Stream_Sequence_Numbers() { Stream_Sequence_Numbers::reset(); }
+
+ void reset() override
+ {
+ m_write_seq_no = 0;
+ m_read_seq_no = 0;
+ m_read_epoch = 0;
+ m_write_epoch = 0;
+ }
+
+ void new_read_cipher_state() override { m_read_seq_no = 0; m_read_epoch++; }
+ void new_write_cipher_state() override { m_write_seq_no = 0; m_write_epoch++; }
+
+ uint16_t current_read_epoch() const override { return m_read_epoch; }
+ 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_read_sequence() override { return m_read_seq_no; }
+
+ bool already_seen(uint64_t) const override { return false; }
+ void read_accept(uint64_t) override { m_read_seq_no++; }
+
+ private:
+ uint64_t m_write_seq_no;
+ uint64_t m_read_seq_no;
+ uint16_t m_read_epoch;
+ uint16_t m_write_epoch;
+ };
+
+class Datagram_Sequence_Numbers final : public Connection_Sequence_Numbers
+ {
+ public:
+ Datagram_Sequence_Numbers() { Datagram_Sequence_Numbers::reset(); }
+
+ void reset() override
+ {
+ m_write_seqs.clear();
+ m_write_seqs[0] = 0;
+ m_write_epoch = 0;
+ m_read_epoch = 0;
+ m_window_highest = 0;
+ m_window_bits = 0;
+ }
+
+ void new_read_cipher_state() override { m_read_epoch++; }
+
+ void new_write_cipher_state() override
+ {
+ m_write_epoch++;
+ m_write_seqs[m_write_epoch] = 0;
+ }
+
+ uint16_t current_read_epoch() const override { return m_read_epoch; }
+ uint16_t current_write_epoch() const override { return m_write_epoch; }
+
+ 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");
+ return (static_cast<uint64_t>(epoch) << 48) | i->second++;
+ }
+
+ uint64_t next_read_sequence() override
+ {
+ throw Invalid_State("DTLS uses explicit sequence numbers");
+ }
+
+ bool already_seen(uint64_t sequence) const override
+ {
+ const size_t window_size = sizeof(m_window_bits) * 8;
+
+ if(sequence > m_window_highest)
+ {
+ return false;
+ }
+
+ const uint64_t offset = m_window_highest - sequence;
+
+ if(offset >= window_size)
+ {
+ return true; // really old?
+ }
+
+ return (((m_window_bits >> offset) & 1) == 1);
+ }
+
+ void read_accept(uint64_t sequence) override
+ {
+ const size_t window_size = sizeof(m_window_bits) * 8;
+
+ if(sequence > m_window_highest)
+ {
+ // We've received a later sequence which advances our window
+ const uint64_t offset = sequence - m_window_highest;
+ m_window_highest += offset;
+
+ if(offset >= window_size)
+ m_window_bits = 0;
+ else
+ m_window_bits <<= offset;
+
+ m_window_bits |= 0x01;
+ }
+ else
+ {
+ const uint64_t offset = m_window_highest - sequence;
+
+ if(offset < window_size)
+ {
+ // We've received an old sequence but still within our window
+ m_window_bits |= (static_cast<uint64_t>(1) << offset);
+ }
+ else
+ {
+ // This occurs only if we have reset state (DTLS reconnection case)
+ m_window_highest = sequence;
+ m_window_bits = 0;
+ }
+ }
+ }
+
+ private:
+ std::map<uint16_t, uint64_t> m_write_seqs;
+ uint16_t m_write_epoch = 0;
+ uint16_t m_read_epoch = 0;
+ uint64_t m_window_highest = 0;
+ uint64_t m_window_bits = 0;
+ };
+
+}
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/tls/tls_server.cpp b/comm/third_party/botan/src/lib/tls/tls_server.cpp
new file mode 100644
index 0000000000..e2a0bf2428
--- /dev/null
+++ b/comm/third_party/botan/src/lib/tls/tls_server.cpp
@@ -0,0 +1,1025 @@
+/*
+* TLS Server
+* (C) 2004-2011,2012,2016 Jack Lloyd
+* 2016 Matthias Gierlings
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/tls_server.h>
+#include <botan/tls_messages.h>
+#include <botan/internal/tls_handshake_state.h>
+#include <botan/internal/stl_util.h>
+#include <botan/tls_magic.h>
+
+namespace Botan {
+
+namespace TLS {
+
+class Server_Handshake_State final : public Handshake_State
+ {
+ public:
+ Server_Handshake_State(Handshake_IO* io, Callbacks& cb)
+ : Handshake_State(io, cb) {}
+
+ Private_Key* server_rsa_kex_key() { return m_server_rsa_kex_key; }
+ void set_server_rsa_kex_key(Private_Key* key)
+ { m_server_rsa_kex_key = key; }
+
+ bool allow_session_resumption() const
+ { return m_allow_session_resumption; }
+ void set_allow_session_resumption(bool allow_session_resumption)
+ { m_allow_session_resumption = allow_session_resumption; }
+
+ const std::vector<X509_Certificate>& resume_peer_certs() const
+ { return m_resume_peer_certs; }
+
+ void set_resume_certs(const std::vector<X509_Certificate>& certs)
+ { m_resume_peer_certs = certs; }
+
+ void mark_as_resumption() { m_is_a_resumption = true; }
+
+ bool is_a_resumption() const { return m_is_a_resumption; }
+
+ private:
+ // Used by the server only, in case of RSA key exchange. Not owned
+ Private_Key* m_server_rsa_kex_key = nullptr;
+
+ /*
+ * Used by the server to know if resumption should be allowed on
+ * a server-initiated renegotiation
+ */
+ bool m_allow_session_resumption = true;
+
+ bool m_is_a_resumption = false;
+
+ std::vector<X509_Certificate> m_resume_peer_certs;
+ };
+
+namespace {
+
+bool check_for_resume(Session& session_info,
+ Session_Manager& session_manager,
+ Credentials_Manager& credentials,
+ const Client_Hello* client_hello,
+ std::chrono::seconds session_ticket_lifetime)
+ {
+ const std::vector<uint8_t>& client_session_id = client_hello->session_id();
+ const std::vector<uint8_t>& session_ticket = client_hello->session_ticket();
+
+ if(session_ticket.empty())
+ {
+ if(client_session_id.empty()) // not resuming
+ return false;
+
+ // not found
+ if(!session_manager.load_from_session_id(client_session_id, session_info))
+ return false;
+ }
+ else
+ {
+ // If a session ticket was sent, ignore client session ID
+ try
+ {
+ session_info = Session::decrypt(
+ session_ticket,
+ credentials.psk("tls-server", "session-ticket", ""));
+
+ if(session_ticket_lifetime != std::chrono::seconds(0) &&
+ session_info.session_age() > session_ticket_lifetime)
+ return false; // ticket has expired
+ }
+ catch(...)
+ {
+ return false;
+ }
+ }
+
+ // wrong version
+ if(client_hello->version() != session_info.version())
+ return false;
+
+ // client didn't send original ciphersuite
+ if(!value_exists(client_hello->ciphersuites(),
+ session_info.ciphersuite_code()))
+ return false;
+
+#if defined(BOTAN_HAS_SRP6)
+ // client sent a different SRP identity
+ if(client_hello->srp_identifier() != "")
+ {
+ if(client_hello->srp_identifier() != session_info.srp_identifier())
+ return false;
+ }
+#endif
+
+ // client sent a different SNI hostname
+ if(client_hello->sni_hostname() != "")
+ {
+ if(client_hello->sni_hostname() != session_info.server_info().hostname())
+ return false;
+ }
+
+ // Checking extended_master_secret on resume (RFC 7627 section 5.3)
+ if(client_hello->supports_extended_master_secret() != session_info.supports_extended_master_secret())
+ {
+ if(!session_info.supports_extended_master_secret())
+ {
+ return false; // force new handshake with extended master secret
+ }
+ else
+ {
+ /*
+ Client previously negotiated session with extended master secret,
+ but has now attempted to resume without the extension: abort
+ */
+ throw TLS_Exception(Alert::HANDSHAKE_FAILURE,
+ "Client resumed extended ms session without sending extension");
+ }
+ }
+
+ // Checking encrypt_then_mac on resume (RFC 7366 section 3.1)
+ if(!client_hello->supports_encrypt_then_mac() && session_info.supports_encrypt_then_mac())
+ {
+ /*
+ Client previously negotiated session with Encrypt-then-MAC,
+ but has now attempted to resume without the extension: abort
+ */
+ throw TLS_Exception(Alert::HANDSHAKE_FAILURE,
+ "Client resumed Encrypt-then-MAC session without sending extension");
+ }
+
+ return true;
+ }
+
+/*
+* Choose which ciphersuite to use
+*/
+uint16_t choose_ciphersuite(
+ const Policy& policy,
+ Protocol_Version version,
+ Credentials_Manager& creds,
+ const std::map<std::string, std::vector<X509_Certificate>>& cert_chains,
+ const Client_Hello& client_hello)
+ {
+ const bool our_choice = policy.server_uses_own_ciphersuite_preferences();
+ const bool have_srp = creds.attempt_srp("tls-server", client_hello.sni_hostname());
+ const std::vector<uint16_t> client_suites = client_hello.ciphersuites();
+ const std::vector<uint16_t> server_suites = policy.ciphersuite_list(version, have_srp);
+
+ if(server_suites.empty())
+ throw TLS_Exception(Alert::HANDSHAKE_FAILURE,
+ "Policy forbids us from negotiating any ciphersuite");
+
+ const bool have_shared_ecc_curve =
+ (policy.choose_key_exchange_group(client_hello.supported_ecc_curves()) != Group_Params::NONE);
+
+ /*
+ Walk down one list in preference order
+ */
+ std::vector<uint16_t> pref_list = server_suites;
+ std::vector<uint16_t> other_list = client_suites;
+
+ if(!our_choice)
+ std::swap(pref_list, other_list);
+
+ for(auto suite_id : pref_list)
+ {
+ if(!value_exists(other_list, suite_id))
+ continue;
+
+ const Ciphersuite suite = Ciphersuite::by_id(suite_id);
+
+ if(suite.valid() == false)
+ {
+ continue;
+ }
+
+ if(have_shared_ecc_curve == false && suite.ecc_ciphersuite())
+ {
+ continue;
+ }
+
+ // For non-anon ciphersuites
+ if(suite.signature_used())
+ {
+ const std::string sig_algo = suite.sig_algo();
+
+ // Do we have any certificates for this sig?
+ if(cert_chains.count(sig_algo) == 0)
+ {
+ continue;
+ }
+
+ if(version.supports_negotiable_signature_algorithms())
+ {
+ const std::vector<Signature_Scheme> allowed =
+ policy.allowed_signature_schemes();
+
+ std::vector<Signature_Scheme> client_sig_methods =
+ client_hello.signature_schemes();
+
+ if(client_sig_methods.empty())
+ {
+ // If empty, then implicit SHA-1 (TLS v1.2 rules)
+ client_sig_methods.push_back(Signature_Scheme::RSA_PKCS1_SHA1);
+ client_sig_methods.push_back(Signature_Scheme::ECDSA_SHA1);
+ client_sig_methods.push_back(Signature_Scheme::DSA_SHA1);
+ }
+
+ bool we_support_some_hash_by_client = false;
+
+ for(Signature_Scheme scheme : client_sig_methods)
+ {
+ if(signature_scheme_is_known(scheme) == false)
+ continue;
+
+ if(signature_algorithm_of_scheme(scheme) == suite.sig_algo() &&
+ policy.allowed_signature_hash(hash_function_of_scheme(scheme)))
+ {
+ we_support_some_hash_by_client = true;
+ }
+ }
+
+ if(we_support_some_hash_by_client == false)
+ {
+ throw TLS_Exception(Alert::HANDSHAKE_FAILURE,
+ "Policy does not accept any hash function supported by client");
+ }
+ }
+ }
+
+#if defined(BOTAN_HAS_SRP6)
+ /*
+ The client may offer SRP cipher suites in the hello message but
+ omit the SRP extension. If the server would like to select an
+ SRP cipher suite in this case, the server SHOULD return a fatal
+ "unknown_psk_identity" alert immediately after processing the
+ client hello message.
+ - RFC 5054 section 2.5.1.2
+ */
+ if(suite.kex_method() == Kex_Algo::SRP_SHA && client_hello.srp_identifier() == "")
+ throw TLS_Exception(Alert::UNKNOWN_PSK_IDENTITY,
+ "Client wanted SRP but did not send username");
+#endif
+
+ return suite_id;
+ }
+
+ throw TLS_Exception(Alert::HANDSHAKE_FAILURE,
+ "Can't agree on a ciphersuite with client");
+ }
+
+std::map<std::string, std::vector<X509_Certificate>>
+get_server_certs(const std::string& hostname,
+ Credentials_Manager& creds)
+ {
+ const char* cert_types[] = { "RSA", "ECDSA", "DSA", nullptr };
+
+ std::map<std::string, std::vector<X509_Certificate>> cert_chains;
+
+ for(size_t i = 0; cert_types[i]; ++i)
+ {
+ const std::vector<X509_Certificate> certs =
+ creds.cert_chain_single_type(cert_types[i], "tls-server", hostname);
+
+ if(!certs.empty())
+ cert_chains[cert_types[i]] = certs;
+ }
+
+ return cert_chains;
+ }
+
+}
+
+/*
+* TLS Server Constructor
+*/
+Server::Server(Callbacks& callbacks,
+ Session_Manager& session_manager,
+ Credentials_Manager& creds,
+ const Policy& policy,
+ RandomNumberGenerator& rng,
+ bool is_datagram,
+ size_t io_buf_sz) :
+ Channel(callbacks, session_manager, rng, policy,
+ true, is_datagram, io_buf_sz),
+ m_creds(creds)
+ {
+ }
+
+Server::Server(output_fn output,
+ data_cb got_data_cb,
+ alert_cb recv_alert_cb,
+ handshake_cb hs_cb,
+ Session_Manager& session_manager,
+ Credentials_Manager& creds,
+ const Policy& policy,
+ RandomNumberGenerator& rng,
+ next_protocol_fn next_proto,
+ bool is_datagram,
+ size_t io_buf_sz) :
+ Channel(output, got_data_cb, recv_alert_cb, hs_cb,
+ Channel::handshake_msg_cb(), session_manager,
+ rng, policy, true, is_datagram, io_buf_sz),
+ m_creds(creds),
+ m_choose_next_protocol(next_proto)
+ {
+ }
+
+Server::Server(output_fn output,
+ data_cb got_data_cb,
+ alert_cb recv_alert_cb,
+ handshake_cb hs_cb,
+ handshake_msg_cb hs_msg_cb,
+ Session_Manager& session_manager,
+ Credentials_Manager& creds,
+ const Policy& policy,
+ RandomNumberGenerator& rng,
+ next_protocol_fn next_proto,
+ bool is_datagram) :
+ Channel(output, got_data_cb, recv_alert_cb, hs_cb, hs_msg_cb,
+ session_manager, rng, policy, true, is_datagram),
+ m_creds(creds),
+ m_choose_next_protocol(next_proto)
+ {
+ }
+
+Handshake_State* Server::new_handshake_state(Handshake_IO* io)
+ {
+ std::unique_ptr<Handshake_State> state(new Server_Handshake_State(io, callbacks()));
+
+ state->set_expected_next(CLIENT_HELLO);
+ return state.release();
+ }
+
+std::vector<X509_Certificate>
+Server::get_peer_cert_chain(const Handshake_State& state_base) const
+ {
+ const Server_Handshake_State& state = dynamic_cast<const Server_Handshake_State&>(state_base);
+ if(state.resume_peer_certs().size() > 0)
+ return state.resume_peer_certs();
+
+ if(state.client_certs())
+ return state.client_certs()->cert_chain();
+ return std::vector<X509_Certificate>();
+ }
+
+/*
+* Send a hello request to the client
+*/
+void Server::initiate_handshake(Handshake_State& state,
+ bool force_full_renegotiation)
+ {
+ dynamic_cast<Server_Handshake_State&>(state).
+ set_allow_session_resumption(!force_full_renegotiation);
+
+ Hello_Request hello_req(state.handshake_io());
+ }
+
+namespace {
+
+Protocol_Version select_version(const Botan::TLS::Policy& policy,
+ Protocol_Version client_offer,
+ Protocol_Version active_version,
+ bool is_fallback,
+ const std::vector<Protocol_Version>& supported_versions)
+ {
+ const bool is_datagram = client_offer.is_datagram_protocol();
+ const bool initial_handshake = (active_version.valid() == false);
+
+ const Protocol_Version latest_supported = policy.latest_supported_version(is_datagram);
+
+ if(is_fallback)
+ {
+ if(latest_supported > client_offer)
+ throw TLS_Exception(Alert::INAPPROPRIATE_FALLBACK,
+ "Client signalled fallback SCSV, possible attack");
+ }
+
+ if(supported_versions.size() > 0)
+ {
+ if(is_datagram)
+ {
+ if(policy.allow_dtls12() && value_exists(supported_versions, Protocol_Version(Protocol_Version::DTLS_V12)))
+ return Protocol_Version::DTLS_V12;
+#if defined(BOTAN_HAS_TLS_V10)
+ if(policy.allow_dtls10() && value_exists(supported_versions, Protocol_Version(Protocol_Version::DTLS_V10)))
+ return Protocol_Version::DTLS_V10;
+#endif
+ throw TLS_Exception(Alert::PROTOCOL_VERSION, "No shared DTLS version");
+ }
+ else
+ {
+ if(policy.allow_tls12() && value_exists(supported_versions, Protocol_Version(Protocol_Version::TLS_V12)))
+ return Protocol_Version::TLS_V12;
+#if defined(BOTAN_HAS_TLS_V10)
+ if(policy.allow_tls11() && value_exists(supported_versions, Protocol_Version(Protocol_Version::TLS_V11)))
+ return Protocol_Version::TLS_V11;
+ if(policy.allow_tls10() && value_exists(supported_versions, Protocol_Version(Protocol_Version::TLS_V10)))
+ return Protocol_Version::TLS_V10;
+#endif
+ throw TLS_Exception(Alert::PROTOCOL_VERSION, "No shared TLS version");
+ }
+ }
+
+ const bool client_offer_acceptable =
+ client_offer.known_version() && policy.acceptable_protocol_version(client_offer);
+
+ if(!initial_handshake)
+ {
+ /*
+ * If this is a renegotiation, and the client has offered a
+ * later version than what it initially negotiated, negotiate
+ * the old version. This matches OpenSSL's behavior. If the
+ * client is offering a version earlier than what it initially
+ * negotiated, reject as a probable attack.
+ */
+ if(active_version > client_offer)
+ {
+ throw TLS_Exception(Alert::PROTOCOL_VERSION,
+ "Client negotiated " +
+ active_version.to_string() +
+ " then renegotiated with " +
+ client_offer.to_string());
+ }
+ else
+ {
+ return active_version;
+ }
+ }
+ else if(client_offer_acceptable)
+ {
+ return client_offer;
+ }
+ else if(!client_offer.known_version() || client_offer > latest_supported)
+ {
+ /*
+ The client offered some version newer than the latest we
+ support. Offer them the best we know.
+ */
+ return latest_supported;
+ }
+ else
+ {
+ throw TLS_Exception(Alert::PROTOCOL_VERSION,
+ "Client version " + client_offer.to_string() +
+ " is unacceptable by policy");
+ }
+ }
+
+}
+
+/*
+* Process a CLIENT HELLO Message
+*/
+void Server::process_client_hello_msg(const Handshake_State* active_state,
+ Server_Handshake_State& pending_state,
+ const std::vector<uint8_t>& contents,
+ bool epoch0_restart)
+ {
+ BOTAN_ASSERT_IMPLICATION(epoch0_restart, active_state != nullptr, "Can't restart with a dead connection");
+
+ const bool initial_handshake = epoch0_restart || !active_state;
+
+ if(initial_handshake == false && policy().allow_client_initiated_renegotiation() == false)
+ {
+ if(policy().abort_connection_on_undesired_renegotiation())
+ throw TLS_Exception(Alert::NO_RENEGOTIATION, "Server policy prohibits renegotiation");
+ else
+ send_warning_alert(Alert::NO_RENEGOTIATION);
+ return;
+ }
+
+ if(!policy().allow_insecure_renegotiation() &&
+ !(initial_handshake || secure_renegotiation_supported()))
+ {
+ send_warning_alert(Alert::NO_RENEGOTIATION);
+ return;
+ }
+
+ pending_state.client_hello(new Client_Hello(contents));
+ const Protocol_Version client_offer = pending_state.client_hello()->version();
+ const bool datagram = client_offer.is_datagram_protocol();
+
+ if(datagram)
+ {
+ if(client_offer.major_version() == 0xFF)
+ throw TLS_Exception(Alert::PROTOCOL_VERSION, "Client offered DTLS version with major version 0xFF");
+ }
+ else
+ {
+ if(client_offer.major_version() < 3)
+ throw TLS_Exception(Alert::PROTOCOL_VERSION, "Client offered TLS version with major version under 3");
+ if(client_offer.major_version() == 3 && client_offer.minor_version() == 0)
+ throw TLS_Exception(Alert::PROTOCOL_VERSION, "SSLv3 is not supported");
+ }
+
+ /*
+ * BoGo test suite expects that we will send the hello verify with a record
+ * version matching the version that is eventually negotiated. This is wrong
+ * but harmless, so go with it. Also doing the version negotiation step first
+ * allows to immediately close the connection with an alert if the client has
+ * offered a version that we are not going to negotiate anyway, instead of
+ * making them first do the cookie exchange and then telling them no.
+ *
+ * There is no issue with amplification here, since the alert is just 2 bytes.
+ */
+ const Protocol_Version negotiated_version =
+ select_version(policy(), client_offer,
+ active_state ? active_state->version() : Protocol_Version(),
+ pending_state.client_hello()->sent_fallback_scsv(),
+ pending_state.client_hello()->supported_versions());
+
+ pending_state.set_version(negotiated_version);
+
+ const auto compression_methods = pending_state.client_hello()->compression_methods();
+ if(!value_exists(compression_methods, uint8_t(0)))
+ throw TLS_Exception(Alert::ILLEGAL_PARAMETER, "Client did not offer NULL compression");
+
+ if(initial_handshake && datagram)
+ {
+ SymmetricKey cookie_secret;
+
+ try
+ {
+ cookie_secret = m_creds.psk("tls-server", "dtls-cookie-secret", "");
+ }
+ catch(...) {}
+
+ if(cookie_secret.size() > 0)
+ {
+ 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);
+
+ if(pending_state.client_hello()->cookie() != verify.cookie())
+ {
+ if(epoch0_restart)
+ pending_state.handshake_io().send_under_epoch(verify, 0);
+ else
+ pending_state.handshake_io().send(verify);
+
+ pending_state.client_hello(nullptr);
+ pending_state.set_expected_next(CLIENT_HELLO);
+ return;
+ }
+ }
+ else if(epoch0_restart)
+ {
+ throw TLS_Exception(Alert::HANDSHAKE_FAILURE, "Reuse of DTLS association requires DTLS cookie secret be set");
+ }
+ }
+
+ if(epoch0_restart)
+ {
+ // If we reached here then we were able to verify the cookie
+ reset_active_association_state();
+ }
+
+ secure_renegotiation_check(pending_state.client_hello());
+
+ callbacks().tls_examine_extensions(pending_state.client_hello()->extensions(), CLIENT);
+
+ Session session_info;
+ const bool resuming =
+ pending_state.allow_session_resumption() &&
+ check_for_resume(session_info,
+ session_manager(),
+ m_creds,
+ pending_state.client_hello(),
+ std::chrono::seconds(policy().session_ticket_lifetime()));
+
+ bool have_session_ticket_key = false;
+
+ try
+ {
+ have_session_ticket_key =
+ m_creds.psk("tls-server", "session-ticket", "").length() > 0;
+ }
+ catch(...) {}
+
+ 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());
+
+ // if the callback return was empty, fall back to the (deprecated) std::function
+ if(m_next_protocol.empty() && m_choose_next_protocol)
+ {
+ m_next_protocol = m_choose_next_protocol(pending_state.client_hello()->next_protocols());
+ }
+ }
+
+ if(resuming)
+ {
+ this->session_resume(pending_state, have_session_ticket_key, session_info);
+ }
+ else // new session
+ {
+ this->session_create(pending_state, have_session_ticket_key);
+ }
+ }
+
+void Server::process_certificate_msg(Server_Handshake_State& pending_state,
+ const std::vector<uint8_t>& contents)
+ {
+ pending_state.client_certs(new Certificate(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())
+ throw TLS_Exception(Alert::HANDSHAKE_FAILURE, "Policy requires client send a certificate, but it did not");
+
+ pending_state.set_expected_next(CLIENT_KEX);
+ }
+
+void Server::process_client_key_exchange_msg(Server_Handshake_State& pending_state,
+ const std::vector<uint8_t>& contents)
+ {
+ if(pending_state.received_handshake_msg(CERTIFICATE) && !pending_state.client_certs()->empty())
+ pending_state.set_expected_next(CERTIFICATE_VERIFY);
+ else
+ pending_state.set_expected_next(HANDSHAKE_CCS);
+
+ pending_state.client_kex(new Client_Key_Exchange(contents, pending_state,
+ pending_state.server_rsa_kex_key(),
+ m_creds, policy(), rng()));
+
+ pending_state.compute_session_keys();
+ }
+
+void Server::process_change_cipher_spec_msg(Server_Handshake_State& pending_state)
+ {
+ pending_state.set_expected_next(FINISHED);
+ change_cipher_spec_reader(SERVER);
+ }
+
+void Server::process_certificate_verify_msg(Server_Handshake_State& pending_state,
+ Handshake_Type type,
+ const std::vector<uint8_t>& contents)
+ {
+ pending_state.client_verify(new Certificate_Verify(contents, pending_state.version()));
+
+ const std::vector<X509_Certificate>& client_certs =
+ pending_state.client_certs()->cert_chain();
+
+ const bool sig_valid =
+ pending_state.client_verify()->verify(client_certs[0], pending_state, policy());
+
+ pending_state.hash().update(pending_state.handshake_io().format(contents, type));
+
+ /*
+ * Using DECRYPT_ERROR looks weird here, but per RFC 4346 is for
+ * "A handshake cryptographic operation failed, including being
+ * unable to correctly verify a signature, ..."
+ */
+ if(!sig_valid)
+ throw TLS_Exception(Alert::DECRYPT_ERROR, "Client cert verify failed");
+
+ try
+ {
+ const std::string sni_hostname = pending_state.client_hello()->sni_hostname();
+ auto trusted_CAs = m_creds.trusted_certificate_authorities("tls-server", sni_hostname);
+
+ callbacks().tls_verify_cert_chain(client_certs,
+ {}, // ocsp
+ trusted_CAs,
+ Usage_Type::TLS_CLIENT_AUTH,
+ sni_hostname,
+ policy());
+ }
+ catch(std::exception& e)
+ {
+ throw TLS_Exception(Alert::BAD_CERTIFICATE, e.what());
+ }
+
+ pending_state.set_expected_next(HANDSHAKE_CCS);
+ }
+
+void Server::process_finished_msg(Server_Handshake_State& pending_state,
+ Handshake_Type type,
+ const std::vector<uint8_t>& contents)
+ {
+ pending_state.set_expected_next(HANDSHAKE_NONE);
+
+ pending_state.client_finished(new Finished(contents));
+
+ if(!pending_state.client_finished()->verify(pending_state, CLIENT))
+ throw TLS_Exception(Alert::DECRYPT_ERROR,
+ "Finished message didn't verify");
+
+ if(!pending_state.server_finished())
+ {
+ // already sent finished if resuming, so this is a new session
+
+ pending_state.hash().update(pending_state.handshake_io().format(contents, type));
+
+ Session session_info(
+ pending_state.server_hello()->session_id(),
+ pending_state.session_keys().master_secret(),
+ pending_state.server_hello()->version(),
+ pending_state.server_hello()->ciphersuite(),
+ SERVER,
+ pending_state.server_hello()->supports_extended_master_secret(),
+ pending_state.server_hello()->supports_encrypt_then_mac(),
+ get_peer_cert_chain(pending_state),
+ std::vector<uint8_t>(),
+ Server_Information(pending_state.client_hello()->sni_hostname()),
+ pending_state.srp_identifier(),
+ pending_state.server_hello()->srtp_profile());
+
+ if(save_session(session_info))
+ {
+ if(pending_state.server_hello()->supports_session_ticket())
+ {
+ try
+ {
+ const SymmetricKey ticket_key = m_creds.psk("tls-server", "session-ticket", "");
+
+ pending_state.new_session_ticket(
+ new New_Session_Ticket(pending_state.handshake_io(),
+ pending_state.hash(),
+ session_info.encrypt(ticket_key, rng()),
+ policy().session_ticket_lifetime()));
+ }
+ catch(...) {}
+ }
+ else
+ session_manager().save(session_info);
+ }
+
+ if(!pending_state.new_session_ticket() &&
+ pending_state.server_hello()->supports_session_ticket())
+ {
+ pending_state.new_session_ticket(
+ new New_Session_Ticket(pending_state.handshake_io(), pending_state.hash()));
+ }
+
+ pending_state.handshake_io().send(Change_Cipher_Spec());
+
+ change_cipher_spec_writer(SERVER);
+
+ pending_state.server_finished(new Finished(pending_state.handshake_io(), pending_state, SERVER));
+ }
+
+ activate_session();
+ }
+
+/*
+* Process a handshake message
+*/
+void Server::process_handshake_msg(const Handshake_State* active_state,
+ Handshake_State& state_base,
+ Handshake_Type type,
+ const std::vector<uint8_t>& contents,
+ bool epoch0_restart)
+ {
+ Server_Handshake_State& state = dynamic_cast<Server_Handshake_State&>(state_base);
+ state.confirm_transition_to(type);
+
+ /*
+ * The change cipher spec message isn't technically a handshake
+ * message so it's not included in the hash. The finished and
+ * certificate verify messages are verified based on the current
+ * state of the hash *before* this message so we delay adding them
+ * to the hash computation until we've processed them below.
+ */
+ if(type != HANDSHAKE_CCS && type != FINISHED && type != CERTIFICATE_VERIFY)
+ {
+ state.hash().update(state.handshake_io().format(contents, type));
+ }
+
+ switch(type)
+ {
+ case CLIENT_HELLO:
+ return this->process_client_hello_msg(active_state, state, contents, epoch0_restart);
+
+ case CERTIFICATE:
+ return this->process_certificate_msg(state, contents);
+
+ case CLIENT_KEX:
+ return this->process_client_key_exchange_msg(state, contents);
+
+ case CERTIFICATE_VERIFY:
+ return this->process_certificate_verify_msg(state, type, contents);
+
+ case HANDSHAKE_CCS:
+ return this->process_change_cipher_spec_msg(state);
+
+ case FINISHED:
+ return this->process_finished_msg(state, type, contents);
+
+ default:
+ throw Unexpected_Message("Unknown handshake message received");
+ }
+ }
+
+void Server::session_resume(Server_Handshake_State& pending_state,
+ bool have_session_ticket_key,
+ Session& session_info)
+ {
+ // Only offer a resuming client a new ticket if they didn't send one this time,
+ // ie, resumed via server-side resumption. TODO: also send one if expiring soon?
+
+ const bool offer_new_session_ticket =
+ (pending_state.client_hello()->supports_session_ticket() &&
+ pending_state.client_hello()->session_ticket().empty() &&
+ have_session_ticket_key);
+
+ pending_state.server_hello(new Server_Hello(
+ pending_state.handshake_io(),
+ pending_state.hash(),
+ policy(),
+ callbacks(),
+ rng(),
+ secure_renegotiation_data_for_server_hello(),
+ *pending_state.client_hello(),
+ session_info,
+ offer_new_session_ticket,
+ m_next_protocol));
+
+ secure_renegotiation_check(pending_state.server_hello());
+
+ pending_state.mark_as_resumption();
+ pending_state.compute_session_keys(session_info.master_secret());
+ pending_state.set_resume_certs(session_info.peer_certs());
+
+ if(!save_session(session_info))
+ {
+ session_manager().remove_entry(session_info.session_id());
+
+ if(pending_state.server_hello()->supports_session_ticket()) // send an empty ticket
+ {
+ pending_state.new_session_ticket(
+ new New_Session_Ticket(pending_state.handshake_io(),
+ pending_state.hash()));
+ }
+ }
+
+ if(pending_state.server_hello()->supports_session_ticket() && !pending_state.new_session_ticket())
+ {
+ try
+ {
+ const SymmetricKey ticket_key = m_creds.psk("tls-server", "session-ticket", "");
+
+ pending_state.new_session_ticket(
+ new New_Session_Ticket(pending_state.handshake_io(),
+ pending_state.hash(),
+ session_info.encrypt(ticket_key, rng()),
+ policy().session_ticket_lifetime()));
+ }
+ catch(...) {}
+
+ if(!pending_state.new_session_ticket())
+ {
+ pending_state.new_session_ticket(
+ new New_Session_Ticket(pending_state.handshake_io(), pending_state.hash()));
+ }
+ }
+
+ pending_state.handshake_io().send(Change_Cipher_Spec());
+
+ change_cipher_spec_writer(SERVER);
+
+ pending_state.server_finished(new Finished(pending_state.handshake_io(), pending_state, SERVER));
+ pending_state.set_expected_next(HANDSHAKE_CCS);
+ }
+
+void Server::session_create(Server_Handshake_State& pending_state,
+ bool have_session_ticket_key)
+ {
+ std::map<std::string, std::vector<X509_Certificate>> cert_chains;
+
+ const std::string sni_hostname = pending_state.client_hello()->sni_hostname();
+
+ cert_chains = get_server_certs(sni_hostname, m_creds);
+
+ if(sni_hostname != "" && cert_chains.empty())
+ {
+ cert_chains = get_server_certs("", m_creds);
+
+ /*
+ * Only send the unrecognized_name alert if we couldn't
+ * find any certs for the requested name but did find at
+ * least one cert to use in general. That avoids sending an
+ * unrecognized_name when a server is configured for purely
+ * anonymous/PSK operation.
+ */
+ if(!cert_chains.empty())
+ send_warning_alert(Alert::UNRECOGNIZED_NAME);
+ }
+
+ const uint16_t ciphersuite = choose_ciphersuite(policy(), pending_state.version(),
+ m_creds, cert_chains,
+ *pending_state.client_hello());
+
+ Server_Hello::Settings srv_settings(
+ make_hello_random(rng(), policy()), // new session ID
+ pending_state.version(),
+ ciphersuite,
+ have_session_ticket_key);
+
+ pending_state.server_hello(new Server_Hello(
+ 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());
+
+ const Ciphersuite& pending_suite = pending_state.ciphersuite();
+
+ Private_Key* private_key = nullptr;
+
+ if(pending_suite.signature_used() || pending_suite.kex_method() == Kex_Algo::STATIC_RSA)
+ {
+ 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(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<Certificate_Status_Request>();
+ // csr is non-null if client_hello()->supports_cert_status_message()
+ BOTAN_ASSERT_NOMSG(csr != nullptr);
+ const auto resp_bytes = callbacks().tls_provide_cert_status(cert_chains[algo_used], *csr);
+ if(resp_bytes.size() > 0)
+ {
+ pending_state.server_cert_status(new Certificate_Status(
+ pending_state.handshake_io(),
+ pending_state.hash(),
+ resp_bytes
+ ));
+ }
+ }
+
+ private_key = m_creds.private_key_for(
+ pending_state.server_certs()->cert_chain()[0],
+ "tls-server",
+ sni_hostname);
+
+ if(!private_key)
+ throw Internal_Error("No private key located for associated server cert");
+ }
+
+ 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.handshake_io(),
+ pending_state, policy(),
+ m_creds, rng(), private_key));
+ }
+
+ auto trusted_CAs = m_creds.trusted_certificate_authorities("tls-server", sni_hostname);
+
+ std::vector<X509_DN> client_auth_CAs;
+
+ for(auto store : trusted_CAs)
+ {
+ auto subjects = store->all_subjects();
+ client_auth_CAs.insert(client_auth_CAs.end(), subjects.begin(), subjects.end());
+ }
+
+ 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_Req(pending_state.handshake_io(),
+ pending_state.hash(),
+ policy(),
+ client_auth_CAs,
+ pending_state.version()));
+
+ /*
+ SSLv3 allowed clients to skip the Certificate message entirely
+ if they wanted. In TLS v1.0 and later clients must send a
+ (possibly empty) Certificate message
+ */
+ pending_state.set_expected_next(CERTIFICATE);
+ }
+ else
+ {
+ pending_state.set_expected_next(CLIENT_KEX);
+ }
+
+ pending_state.server_hello_done(new Server_Hello_Done(pending_state.handshake_io(), pending_state.hash()));
+ }
+}
+
+}
diff --git a/comm/third_party/botan/src/lib/tls/tls_server.h b/comm/third_party/botan/src/lib/tls/tls_server.h
new file mode 100644
index 0000000000..c601e8c6e3
--- /dev/null
+++ b/comm/third_party/botan/src/lib/tls/tls_server.h
@@ -0,0 +1,169 @@
+/*
+* TLS Server
+* (C) 2004-2011 Jack Lloyd
+* 2016 Matthias Gierlings
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_TLS_SERVER_H_
+#define BOTAN_TLS_SERVER_H_
+
+#include <botan/tls_channel.h>
+#include <botan/tls_policy.h>
+#include <botan/credentials_manager.h>
+#include <vector>
+
+namespace Botan {
+
+namespace TLS {
+
+class Server_Handshake_State;
+
+/**
+* TLS Server
+*/
+class BOTAN_PUBLIC_API(2,0) Server final : public Channel
+ {
+ public:
+ typedef std::function<std::string (std::vector<std::string>)> next_protocol_fn;
+
+ /**
+ * Server initialization
+ *
+ * @param callbacks contains a set of callback function references
+ * required by the TLS client.
+ *
+ * @param session_manager manages session state
+ *
+ * @param creds manages application/user credentials
+ *
+ * @param policy specifies other connection policy information
+ *
+ * @param rng a random number generator
+ *
+ * @param is_datagram set to true if this server should expect DTLS
+ * connections. Otherwise TLS connections are expected.
+ *
+ * @param reserved_io_buffer_size This many bytes of memory will
+ * be preallocated for the read and write buffers. Smaller
+ * values just mean reallocations and copies are more likely.
+ */
+ Server(Callbacks& callbacks,
+ Session_Manager& session_manager,
+ Credentials_Manager& creds,
+ const Policy& policy,
+ RandomNumberGenerator& rng,
+ bool is_datagram = false,
+ size_t reserved_io_buffer_size = TLS::Server::IO_BUF_DEFAULT_SIZE
+ );
+
+ /**
+ * DEPRECATED. This constructor is only provided for backward
+ * compatibility and should not be used in new implementations.
+ * It will be removed in a future release.
+ */
+ BOTAN_DEPRECATED("Use TLS::Server(TLS::Callbacks ...)")
+ Server(output_fn output,
+ data_cb data_cb,
+ alert_cb recv_alert_cb,
+ handshake_cb hs_cb,
+ Session_Manager& session_manager,
+ Credentials_Manager& creds,
+ const Policy& policy,
+ RandomNumberGenerator& rng,
+ next_protocol_fn next_proto = next_protocol_fn(),
+ bool is_datagram = false,
+ size_t reserved_io_buffer_size = TLS::Server::IO_BUF_DEFAULT_SIZE
+ );
+
+ /**
+ * DEPRECATED. This constructor is only provided for backward
+ * compatibility and should not be used in new implementations.
+ * It will be removed in a future release.
+ */
+ BOTAN_DEPRECATED("Use TLS::Server(TLS::Callbacks ...)")
+ Server(output_fn output,
+ data_cb data_cb,
+ alert_cb recv_alert_cb,
+ handshake_cb hs_cb,
+ handshake_msg_cb hs_msg_cb,
+ Session_Manager& session_manager,
+ Credentials_Manager& creds,
+ const Policy& policy,
+ RandomNumberGenerator& rng,
+ next_protocol_fn next_proto = next_protocol_fn(),
+ bool is_datagram = false
+ );
+
+ /**
+ * Return the protocol notification set by the client (using the
+ * ALPN extension) for this connection, if any. This value is not
+ * tied to the session and a later renegotiation of the same
+ * session can choose a new protocol.
+ */
+ std::string next_protocol() const { return m_next_protocol; }
+
+ /**
+ * Return the protocol notification set by the client (using the
+ * ALPN extension) for this connection, if any. This value is not
+ * tied to the session and a later renegotiation of the same
+ * session can choose a new protocol.
+ */
+ std::string application_protocol() const override { return m_next_protocol; }
+
+ private:
+ std::vector<X509_Certificate>
+ 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,
+ Handshake_Type type,
+ const std::vector<uint8_t>& contents,
+ bool epoch0_restart) override;
+
+ void process_client_hello_msg(const Handshake_State* active_state,
+ Server_Handshake_State& pending_state,
+ const std::vector<uint8_t>& contents,
+ bool epoch0_restart);
+
+ void process_certificate_msg(Server_Handshake_State& pending_state,
+ const std::vector<uint8_t>& contents);
+
+ void process_client_key_exchange_msg(Server_Handshake_State& pending_state,
+ const std::vector<uint8_t>& contents);
+
+ void process_change_cipher_spec_msg(Server_Handshake_State& pending_state);
+
+ void process_certificate_verify_msg(Server_Handshake_State& pending_state,
+ Handshake_Type type,
+ const std::vector<uint8_t>& contents);
+
+ void process_finished_msg(Server_Handshake_State& pending_state,
+ Handshake_Type type,
+ const std::vector<uint8_t>& contents);
+
+ void session_resume(Server_Handshake_State& pending_state,
+ bool have_session_ticket_key,
+ Session& session_info);
+
+ void session_create(Server_Handshake_State& pending_state,
+ bool have_session_ticket_key);
+
+ Handshake_State* new_handshake_state(Handshake_IO* io) override;
+
+ Credentials_Manager& m_creds;
+ std::string m_next_protocol;
+
+ // Set by deprecated constructor, Server calls both this fn and Callbacks version
+ next_protocol_fn m_choose_next_protocol;
+ };
+
+}
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/tls/tls_server_info.h b/comm/third_party/botan/src/lib/tls/tls_server_info.h
new file mode 100644
index 0000000000..d05af6acca
--- /dev/null
+++ b/comm/third_party/botan/src/lib/tls/tls_server_info.h
@@ -0,0 +1,104 @@
+/*
+* TLS Server Information
+* (C) 2012 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_TLS_SERVER_INFO_H_
+#define BOTAN_TLS_SERVER_INFO_H_
+
+#include <botan/types.h>
+#include <string>
+
+namespace Botan {
+
+namespace TLS {
+
+/**
+* Represents information known about a TLS server.
+*/
+class BOTAN_PUBLIC_API(2,0) Server_Information final
+ {
+ public:
+ /**
+ * An empty server info - nothing known
+ */
+ Server_Information() : m_hostname(""), m_service(""), m_port(0) {}
+
+ /**
+ * @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(const std::string& hostname,
+ uint16_t port = 0) :
+ m_hostname(hostname), m_service(""), m_port(port) {}
+
+ /**
+ * @param hostname the host's DNS name, if known
+ * @param service is a text string of the service type
+ * (eg "https", "tor", or "git")
+ * @param port specifies the protocol port of the server (eg for
+ * TCP/UDP). Zero represents unknown.
+ */
+ Server_Information(const std::string& hostname,
+ const std::string& service,
+ uint16_t port = 0) :
+ m_hostname(hostname), m_service(service), m_port(port) {}
+
+ /**
+ * @return the host's DNS name, if known
+ */
+ std::string hostname() const { return m_hostname; }
+
+ /**
+ * @return text string of the service type, e.g.,
+ * "https", "tor", or "git"
+ */
+ std::string service() const { return m_service; }
+
+ /**
+ * @return the protocol port of the server, or zero if unknown
+ */
+ uint16_t port() const { return m_port; }
+
+ /**
+ * @return whether the hostname is known
+ */
+ bool empty() const { return m_hostname.empty(); }
+
+ private:
+ std::string m_hostname, m_service;
+ uint16_t m_port;
+ };
+
+inline bool operator==(const Server_Information& a, const Server_Information& b)
+ {
+ return (a.hostname() == b.hostname()) &&
+ (a.service() == b.service()) &&
+ (a.port() == b.port());
+
+ }
+
+inline bool operator!=(const Server_Information& a, const Server_Information& b)
+ {
+ return !(a == b);
+ }
+
+inline bool operator<(const Server_Information& a, const Server_Information& b)
+ {
+ if(a.hostname() != b.hostname())
+ return (a.hostname() < b.hostname());
+ if(a.service() != b.service())
+ return (a.service() < b.service());
+ if(a.port() != b.port())
+ return (a.port() < b.port());
+ return false; // equal
+ }
+
+}
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/tls/tls_session.cpp b/comm/third_party/botan/src/lib/tls/tls_session.cpp
new file mode 100644
index 0000000000..0fd73c9fc5
--- /dev/null
+++ b/comm/third_party/botan/src/lib/tls/tls_session.cpp
@@ -0,0 +1,299 @@
+/*
+* TLS Session State
+* (C) 2011-2012,2015,2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/tls_session.h>
+#include <botan/loadstor.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
+#include <botan/asn1_obj.h>
+#include <botan/pem.h>
+#include <botan/aead.h>
+#include <botan/mac.h>
+#include <botan/rng.h>
+
+namespace Botan {
+
+namespace TLS {
+
+Session::Session(const std::vector<uint8_t>& session_identifier,
+ const secure_vector<uint8_t>& master_secret,
+ Protocol_Version version,
+ uint16_t ciphersuite,
+ Connection_Side side,
+ bool extended_master_secret,
+ bool encrypt_then_mac,
+ const std::vector<X509_Certificate>& certs,
+ const std::vector<uint8_t>& ticket,
+ const Server_Information& server_info,
+ const std::string& srp_identifier,
+ uint16_t srtp_profile) :
+ m_start_time(std::chrono::system_clock::now()),
+ m_identifier(session_identifier),
+ m_session_ticket(ticket),
+ m_master_secret(master_secret),
+ m_version(version),
+ m_ciphersuite(ciphersuite),
+ m_connection_side(side),
+ m_srtp_profile(srtp_profile),
+ m_extended_master_secret(extended_master_secret),
+ m_encrypt_then_mac(encrypt_then_mac),
+ m_peer_certs(certs),
+ m_server_info(server_info),
+ m_srp_identifier(srp_identifier)
+ {
+ }
+
+Session::Session(const std::string& pem)
+ {
+ secure_vector<uint8_t> der = PEM_Code::decode_check_label(pem, "TLS SESSION");
+
+ *this = Session(der.data(), der.size());
+ }
+
+Session::Session(const uint8_t ber[], size_t ber_len)
+ {
+ uint8_t side_code = 0;
+
+ ASN1_String server_hostname;
+ ASN1_String server_service;
+ size_t server_port;
+
+ ASN1_String srp_identifier_str;
+
+ uint8_t major_version = 0, minor_version = 0;
+ std::vector<uint8_t> peer_cert_bits;
+
+ size_t start_time = 0;
+ size_t srtp_profile = 0;
+ size_t fragment_size = 0;
+ size_t compression_method = 0;
+
+ BER_Decoder(ber, ber_len)
+ .start_cons(SEQUENCE)
+ .decode_and_check(static_cast<size_t>(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)
+ .decode(m_identifier, OCTET_STRING)
+ .decode(m_session_ticket, OCTET_STRING)
+ .decode_integer_type(m_ciphersuite)
+ .decode_integer_type(compression_method)
+ .decode_integer_type(side_code)
+ .decode_integer_type(fragment_size)
+ .decode(m_extended_master_secret)
+ .decode(m_encrypt_then_mac)
+ .decode(m_master_secret, OCTET_STRING)
+ .decode(peer_cert_bits, OCTET_STRING)
+ .decode(server_hostname)
+ .decode(server_service)
+ .decode(server_port)
+ .decode(srp_identifier_str)
+ .decode(srtp_profile)
+ .end_cons()
+ .verify_end();
+
+ /*
+ * Compression is not supported and must be zero
+ */
+ if(compression_method != 0)
+ {
+ throw Decoding_Error("Serialized TLS session contains non-null compression method");
+ }
+
+ /*
+ Fragment size is not supported anymore, but the field is still
+ set in the session object.
+ */
+ if(fragment_size != 0)
+ {
+ throw Decoding_Error("Serialized TLS session used maximum fragment length which is "
+ " no longer supported");
+ }
+
+ m_version = Protocol_Version(major_version, minor_version);
+ m_start_time = std::chrono::system_clock::from_time_t(start_time);
+ m_connection_side = static_cast<Connection_Side>(side_code);
+ m_srtp_profile = static_cast<uint16_t>(srtp_profile);
+
+ m_server_info = Server_Information(server_hostname.value(),
+ server_service.value(),
+ static_cast<uint16_t>(server_port));
+
+ m_srp_identifier = srp_identifier_str.value();
+
+ if(!peer_cert_bits.empty())
+ {
+ DataSource_Memory certs(peer_cert_bits.data(), peer_cert_bits.size());
+
+ while(!certs.end_of_data())
+ m_peer_certs.push_back(X509_Certificate(certs));
+ }
+ }
+
+secure_vector<uint8_t> Session::DER_encode() const
+ {
+ std::vector<uint8_t> peer_cert_bits;
+ for(size_t i = 0; i != m_peer_certs.size(); ++i)
+ peer_cert_bits += m_peer_certs[i].BER_encode();
+
+ return DER_Encoder()
+ .start_cons(SEQUENCE)
+ .encode(static_cast<size_t>(TLS_SESSION_PARAM_STRUCT_VERSION))
+ .encode(static_cast<size_t>(std::chrono::system_clock::to_time_t(m_start_time)))
+ .encode(static_cast<size_t>(m_version.major_version()))
+ .encode(static_cast<size_t>(m_version.minor_version()))
+ .encode(m_identifier, OCTET_STRING)
+ .encode(m_session_ticket, OCTET_STRING)
+ .encode(static_cast<size_t>(m_ciphersuite))
+ .encode(static_cast<size_t>(/*old compression method*/0))
+ .encode(static_cast<size_t>(m_connection_side))
+ .encode(static_cast<size_t>(/*old fragment size*/0))
+ .encode(m_extended_master_secret)
+ .encode(m_encrypt_then_mac)
+ .encode(m_master_secret, OCTET_STRING)
+ .encode(peer_cert_bits, OCTET_STRING)
+ .encode(ASN1_String(m_server_info.hostname(), UTF8_STRING))
+ .encode(ASN1_String(m_server_info.service(), UTF8_STRING))
+ .encode(static_cast<size_t>(m_server_info.port()))
+ .encode(ASN1_String(m_srp_identifier, UTF8_STRING))
+ .encode(static_cast<size_t>(m_srtp_profile))
+ .end_cons()
+ .get_contents();
+ }
+
+std::string Session::PEM_encode() const
+ {
+ return PEM_Code::encode(this->DER_encode(), "TLS SESSION");
+ }
+
+std::chrono::seconds Session::session_age() const
+ {
+ return std::chrono::duration_cast<std::chrono::seconds>(
+ std::chrono::system_clock::now() - m_start_time);
+ }
+
+namespace {
+
+// The output length of the HMAC must be a valid keylength for the AEAD
+const char* TLS_SESSION_CRYPT_HMAC = "HMAC(SHA-512-256)";
+// SIV would be better, but we can't assume it is available
+const char* TLS_SESSION_CRYPT_AEAD = "AES-256/GCM";
+const char* TLS_SESSION_CRYPT_KEY_NAME = "BOTAN TLS SESSION KEY NAME";
+const uint64_t TLS_SESSION_CRYPT_MAGIC = 0x068B5A9D396C0000;
+const size_t TLS_SESSION_CRYPT_MAGIC_LEN = 8;
+const size_t TLS_SESSION_CRYPT_KEY_NAME_LEN = 4;
+const size_t TLS_SESSION_CRYPT_AEAD_NONCE_LEN = 12;
+const size_t TLS_SESSION_CRYPT_AEAD_KEY_SEED_LEN = 16;
+const size_t TLS_SESSION_CRYPT_AEAD_TAG_SIZE = 16;
+
+const size_t TLS_SESSION_CRYPT_HDR_LEN =
+ TLS_SESSION_CRYPT_MAGIC_LEN +
+ TLS_SESSION_CRYPT_KEY_NAME_LEN +
+ TLS_SESSION_CRYPT_AEAD_NONCE_LEN +
+ TLS_SESSION_CRYPT_AEAD_KEY_SEED_LEN;
+
+const size_t TLS_SESSION_CRYPT_OVERHEAD =
+ TLS_SESSION_CRYPT_HDR_LEN + TLS_SESSION_CRYPT_AEAD_TAG_SIZE;
+
+}
+
+std::vector<uint8_t>
+Session::encrypt(const SymmetricKey& key, RandomNumberGenerator& rng) const
+ {
+ auto hmac = MessageAuthenticationCode::create_or_throw(TLS_SESSION_CRYPT_HMAC);
+ hmac->set_key(key);
+
+ // First derive the "key name"
+ std::vector<uint8_t> key_name(hmac->output_length());
+ hmac->update(TLS_SESSION_CRYPT_KEY_NAME);
+ hmac->final(key_name.data());
+ key_name.resize(TLS_SESSION_CRYPT_KEY_NAME_LEN);
+
+ std::vector<uint8_t> aead_nonce;
+ std::vector<uint8_t> key_seed;
+
+ rng.random_vec(aead_nonce, TLS_SESSION_CRYPT_AEAD_NONCE_LEN);
+ rng.random_vec(key_seed, TLS_SESSION_CRYPT_AEAD_KEY_SEED_LEN);
+
+ hmac->update(key_seed);
+ const secure_vector<uint8_t> aead_key = hmac->final();
+
+ secure_vector<uint8_t> bits = this->DER_encode();
+
+ // create the header
+ std::vector<uint8_t> buf;
+ buf.reserve(TLS_SESSION_CRYPT_OVERHEAD + bits.size());
+ buf.resize(TLS_SESSION_CRYPT_MAGIC_LEN);
+ store_be(TLS_SESSION_CRYPT_MAGIC, &buf[0]);
+ buf += key_name;
+ buf += key_seed;
+ buf += aead_nonce;
+
+ std::unique_ptr<AEAD_Mode> aead = AEAD_Mode::create_or_throw(TLS_SESSION_CRYPT_AEAD, ENCRYPTION);
+ BOTAN_ASSERT_NOMSG(aead->valid_nonce_length(TLS_SESSION_CRYPT_AEAD_NONCE_LEN));
+ BOTAN_ASSERT_NOMSG(aead->tag_size() == TLS_SESSION_CRYPT_AEAD_TAG_SIZE);
+ aead->set_key(aead_key);
+ aead->set_associated_data_vec(buf);
+ aead->start(aead_nonce);
+ aead->finish(bits, 0);
+
+ // append the ciphertext
+ buf += bits;
+ return buf;
+ }
+
+Session Session::decrypt(const uint8_t in[], size_t in_len, const SymmetricKey& key)
+ {
+ try
+ {
+ const size_t min_session_size = 48 + 4; // serious under-estimate
+ if(in_len < TLS_SESSION_CRYPT_OVERHEAD + min_session_size)
+ throw Decoding_Error("Encrypted session too short to be valid");
+
+ const uint8_t* magic = &in[0];
+ const uint8_t* key_name = magic + TLS_SESSION_CRYPT_MAGIC_LEN;
+ const uint8_t* key_seed = key_name + TLS_SESSION_CRYPT_KEY_NAME_LEN;
+ const uint8_t* aead_nonce = key_seed + TLS_SESSION_CRYPT_AEAD_KEY_SEED_LEN;
+ const uint8_t* ctext = aead_nonce + TLS_SESSION_CRYPT_AEAD_NONCE_LEN;
+ const size_t ctext_len = in_len - TLS_SESSION_CRYPT_HDR_LEN; // includes the tag
+
+ if(load_be<uint64_t>(magic, 0) != TLS_SESSION_CRYPT_MAGIC)
+ throw Decoding_Error("Missing expected magic numbers");
+
+ auto hmac = MessageAuthenticationCode::create_or_throw(TLS_SESSION_CRYPT_HMAC);
+ hmac->set_key(key);
+
+ // First derive and check the "key name"
+ std::vector<uint8_t> cmp_key_name(hmac->output_length());
+ hmac->update(TLS_SESSION_CRYPT_KEY_NAME);
+ hmac->final(cmp_key_name.data());
+
+ if(same_mem(cmp_key_name.data(), key_name, TLS_SESSION_CRYPT_KEY_NAME_LEN) == false)
+ throw Decoding_Error("Wrong key name for encrypted session");
+
+ hmac->update(key_seed, TLS_SESSION_CRYPT_AEAD_KEY_SEED_LEN);
+ const secure_vector<uint8_t> aead_key = hmac->final();
+
+ auto aead = AEAD_Mode::create_or_throw(TLS_SESSION_CRYPT_AEAD, DECRYPTION);
+ aead->set_key(aead_key);
+ aead->set_associated_data(in, TLS_SESSION_CRYPT_HDR_LEN);
+ aead->start(aead_nonce, TLS_SESSION_CRYPT_AEAD_NONCE_LEN);
+ secure_vector<uint8_t> buf(ctext, ctext + ctext_len);
+ aead->finish(buf, 0);
+ return Session(buf.data(), buf.size());
+ }
+ catch(std::exception& e)
+ {
+ throw Decoding_Error("Failed to decrypt serialized TLS session: " +
+ std::string(e.what()));
+ }
+ }
+
+}
+
+}
diff --git a/comm/third_party/botan/src/lib/tls/tls_session.h b/comm/third_party/botan/src/lib/tls/tls_session.h
new file mode 100644
index 0000000000..5a75e6a32b
--- /dev/null
+++ b/comm/third_party/botan/src/lib/tls/tls_session.h
@@ -0,0 +1,210 @@
+/*
+* TLS Session
+* (C) 2011-2012,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_TLS_SESSION_STATE_H_
+#define BOTAN_TLS_SESSION_STATE_H_
+
+#include <botan/x509cert.h>
+#include <botan/tls_version.h>
+#include <botan/tls_ciphersuite.h>
+#include <botan/tls_magic.h>
+#include <botan/tls_server_info.h>
+#include <botan/secmem.h>
+#include <botan/symkey.h>
+#include <chrono>
+
+namespace Botan {
+
+namespace TLS {
+
+/**
+* Class representing a TLS session state
+*/
+class BOTAN_PUBLIC_API(2,0) Session final
+ {
+ public:
+
+ /**
+ * Uninitialized session
+ */
+ Session() :
+ m_start_time(std::chrono::system_clock::time_point::min()),
+ m_version(),
+ m_ciphersuite(0),
+ m_connection_side(static_cast<Connection_Side>(0)),
+ m_srtp_profile(0),
+ m_extended_master_secret(false),
+ m_encrypt_then_mac(false)
+ {}
+
+ /**
+ * New session (sets session start time)
+ */
+ Session(const std::vector<uint8_t>& session_id,
+ const secure_vector<uint8_t>& master_secret,
+ Protocol_Version version,
+ uint16_t ciphersuite,
+ Connection_Side side,
+ bool supports_extended_master_secret,
+ bool supports_encrypt_then_mac,
+ const std::vector<X509_Certificate>& peer_certs,
+ const std::vector<uint8_t>& session_ticket,
+ const Server_Information& server_info,
+ const std::string& srp_identifier,
+ uint16_t srtp_profile);
+
+ /**
+ * Load a session from DER representation (created by DER_encode)
+ * @param ber DER representation buffer
+ * @param ber_len size of buffer in bytes
+ */
+ Session(const uint8_t ber[], size_t ber_len);
+
+ /**
+ * Load a session from PEM representation (created by PEM_encode)
+ * @param pem PEM representation
+ */
+ explicit Session(const std::string& pem);
+
+ /**
+ * Encode this session data for storage
+ * @warning if the master secret is compromised so is the
+ * session traffic
+ */
+ secure_vector<uint8_t> DER_encode() const;
+
+ /**
+ * Encrypt a session (useful for serialization or session tickets)
+ */
+ std::vector<uint8_t> encrypt(const SymmetricKey& key,
+ RandomNumberGenerator& rng) const;
+
+
+ /**
+ * Decrypt a session created by encrypt
+ * @param ctext the ciphertext returned by encrypt
+ * @param ctext_size the size of ctext in bytes
+ * @param key the same key used by the encrypting side
+ */
+ static Session decrypt(const uint8_t ctext[],
+ size_t ctext_size,
+ const SymmetricKey& key);
+
+ /**
+ * Decrypt a session created by encrypt
+ * @param ctext the ciphertext returned by encrypt
+ * @param key the same key used by the encrypting side
+ */
+ static inline Session decrypt(const std::vector<uint8_t>& ctext,
+ const SymmetricKey& key)
+ {
+ return Session::decrypt(ctext.data(), ctext.size(), key);
+ }
+
+ /**
+ * Encode this session data for storage
+ * @warning if the master secret is compromised so is the
+ * session traffic
+ */
+ std::string PEM_encode() const;
+
+ /**
+ * Get the version of the saved session
+ */
+ Protocol_Version version() const { return m_version; }
+
+ /**
+ * Get the ciphersuite code of the saved session
+ */
+ uint16_t ciphersuite_code() const { return m_ciphersuite; }
+
+ /**
+ * Get the ciphersuite info of the saved session
+ */
+ Ciphersuite ciphersuite() const { return Ciphersuite::by_id(m_ciphersuite); }
+
+ /**
+ * Get which side of the connection the resumed session we are/were
+ * acting as.
+ */
+ Connection_Side side() const { return m_connection_side; }
+
+ /**
+ * Get the SRP identity (if sent by the client in the initial handshake)
+ */
+ const std::string& srp_identifier() const { return m_srp_identifier; }
+
+ /**
+ * Get the saved master secret
+ */
+ const secure_vector<uint8_t>& master_secret() const { return m_master_secret; }
+
+ /**
+ * Get the session identifier
+ */
+ const std::vector<uint8_t>& session_id() const { return m_identifier; }
+
+ /**
+ * Get the negotiated DTLS-SRTP algorithm (RFC 5764)
+ */
+ uint16_t dtls_srtp_profile() const { return m_srtp_profile; }
+
+ bool supports_extended_master_secret() const { return m_extended_master_secret; }
+
+ bool supports_encrypt_then_mac() const { return m_encrypt_then_mac; }
+
+ /**
+ * Return the certificate chain of the peer (possibly empty)
+ */
+ const std::vector<X509_Certificate>& peer_certs() const { return m_peer_certs; }
+
+ /**
+ * Get the wall clock time this session began
+ */
+ std::chrono::system_clock::time_point start_time() const { return m_start_time; }
+
+ /**
+ * Return how long this session has existed (in seconds)
+ */
+ std::chrono::seconds session_age() const;
+
+ /**
+ * Return the session ticket the server gave us
+ */
+ const std::vector<uint8_t>& session_ticket() const { return m_session_ticket; }
+
+ /**
+ * @return information about the TLS server
+ */
+ const Server_Information& server_info() const { return m_server_info; }
+
+ private:
+ enum { TLS_SESSION_PARAM_STRUCT_VERSION = 20160812 };
+
+ std::chrono::system_clock::time_point m_start_time;
+
+ std::vector<uint8_t> m_identifier;
+ std::vector<uint8_t> m_session_ticket; // only used by client side
+ secure_vector<uint8_t> m_master_secret;
+
+ 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<X509_Certificate> m_peer_certs;
+ Server_Information m_server_info; // optional
+ std::string m_srp_identifier; // optional
+ };
+
+}
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/tls/tls_session_key.cpp b/comm/third_party/botan/src/lib/tls/tls_session_key.cpp
new file mode 100644
index 0000000000..2066810413
--- /dev/null
+++ b/comm/third_party/botan/src/lib/tls/tls_session_key.cpp
@@ -0,0 +1,101 @@
+/*
+* TLS Session Key
+* (C) 2004-2006,2011,2016,2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/tls_session_key.h>
+#include <botan/internal/tls_handshake_state.h>
+#include <botan/tls_messages.h>
+#include <botan/kdf.h>
+
+namespace Botan {
+
+namespace TLS {
+
+/**
+* Session_Keys Constructor
+*/
+Session_Keys::Session_Keys(const Handshake_State* state,
+ const secure_vector<uint8_t>& 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();
+
+ const bool extended_master_secret = state->server_hello()->supports_extended_master_secret();
+
+ const size_t prf_gen = 2 * (mac_keylen + cipher_keylen + cipher_nonce_bytes);
+
+ const uint8_t MASTER_SECRET_MAGIC[] = {
+ 0x6D, 0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74 };
+
+ const uint8_t EXT_MASTER_SECRET_MAGIC[] = {
+ 0x65, 0x78, 0x74, 0x65, 0x6E, 0x64, 0x65, 0x64, 0x20,
+ 0x6D, 0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74 };
+
+ const uint8_t KEY_GEN_MAGIC[] = {
+ 0x6B, 0x65, 0x79, 0x20, 0x65, 0x78, 0x70, 0x61, 0x6E, 0x73, 0x69, 0x6F, 0x6E };
+
+ std::unique_ptr<KDF> prf(state->protocol_specific_prf());
+
+ if(resuming)
+ {
+ // This is actually the master secret saved as part of the session
+ m_master_sec = pre_master_secret;
+ }
+ else
+ {
+ std::vector<uint8_t> salt;
+ std::vector<uint8_t> 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->version(),
+ state->ciphersuite().prf_algo());
+ }
+ else
+ {
+ label.assign(MASTER_SECRET_MAGIC, MASTER_SECRET_MAGIC + sizeof(MASTER_SECRET_MAGIC));
+ salt += state->client_hello()->random();
+ salt += state->server_hello()->random();
+ }
+
+ m_master_sec = prf->derive_key(48, pre_master_secret, salt, label);
+ }
+
+ std::vector<uint8_t> salt;
+ std::vector<uint8_t> label;
+ label.assign(KEY_GEN_MAGIC, KEY_GEN_MAGIC + sizeof(KEY_GEN_MAGIC));
+ salt += state->server_hello()->random();
+ salt += state->client_hello()->random();
+
+ const secure_vector<uint8_t> prf_output = prf->derive_key(
+ prf_gen,
+ m_master_sec.data(), m_master_sec.size(),
+ salt.data(), salt.size(),
+ label.data(), label.size());
+
+ const uint8_t* key_data = prf_output.data();
+
+ m_c_aead.resize(mac_keylen + cipher_keylen);
+ m_s_aead.resize(mac_keylen + cipher_keylen);
+
+ copy_mem(&m_c_aead[0], key_data, mac_keylen);
+ copy_mem(&m_s_aead[0], key_data + mac_keylen, mac_keylen);
+
+ 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);
+
+ m_c_nonce.resize(cipher_nonce_bytes);
+ m_s_nonce.resize(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);
+ }
+
+}
+
+}
diff --git a/comm/third_party/botan/src/lib/tls/tls_session_key.h b/comm/third_party/botan/src/lib/tls/tls_session_key.h
new file mode 100644
index 0000000000..0ea6d81cd5
--- /dev/null
+++ b/comm/third_party/botan/src/lib/tls/tls_session_key.h
@@ -0,0 +1,82 @@
+/*
+* TLS Session Key
+* (C) 2004-2006,2011 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_TLS_SESSION_KEYS_H_
+#define BOTAN_TLS_SESSION_KEYS_H_
+
+#include <botan/secmem.h>
+#include <botan/tls_magic.h>
+
+namespace Botan {
+
+namespace TLS {
+
+class Handshake_State;
+
+/**
+* TLS Session Keys
+*/
+class Session_Keys final
+ {
+ public:
+ /**
+ * @return client AEAD key
+ */
+ const secure_vector<uint8_t>& client_aead_key() const { return m_c_aead; }
+
+ /**
+ * @return server AEAD key
+ */
+ const secure_vector<uint8_t>& server_aead_key() const { return m_s_aead; }
+
+ /**
+ * @return client nonce
+ */
+ const std::vector<uint8_t>& client_nonce() const { return m_c_nonce; }
+
+ /**
+ * @return server nonce
+ */
+ const std::vector<uint8_t>& server_nonce() const { return m_s_nonce; }
+
+ /**
+ * @return TLS master secret
+ */
+ const secure_vector<uint8_t>& master_secret() const { return m_master_sec; }
+
+ const secure_vector<uint8_t>& aead_key(Connection_Side side) const
+ {
+ return (side == Connection_Side::CLIENT) ? client_aead_key() : server_aead_key();
+ }
+
+ const std::vector<uint8_t>& nonce(Connection_Side side) const
+ {
+ return (side == Connection_Side::CLIENT) ? client_nonce() : server_nonce();
+ }
+
+ Session_Keys() = default;
+
+ /**
+ * @param state state the handshake state
+ * @param pre_master_secret the pre-master secret
+ * @param resuming whether this TLS session is resumed
+ */
+ Session_Keys(const Handshake_State* state,
+ const secure_vector<uint8_t>& pre_master_secret,
+ bool resuming);
+
+ private:
+ secure_vector<uint8_t> m_master_sec;
+ secure_vector<uint8_t> m_c_aead, m_s_aead;
+ std::vector<uint8_t> m_c_nonce, m_s_nonce;
+ };
+
+}
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/tls/tls_session_manager.h b/comm/third_party/botan/src/lib/tls/tls_session_manager.h
new file mode 100644
index 0000000000..40ca48f719
--- /dev/null
+++ b/comm/third_party/botan/src/lib/tls/tls_session_manager.h
@@ -0,0 +1,160 @@
+/*
+* TLS Session Manager
+* (C) 2011 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_TLS_SESSION_MANAGER_H_
+#define BOTAN_TLS_SESSION_MANAGER_H_
+
+#include <botan/tls_session.h>
+#include <botan/mutex.h>
+#include <chrono>
+#include <map>
+
+namespace Botan {
+
+namespace TLS {
+
+/**
+* Session_Manager is an interface to systems which can save
+* session parameters for supporting session resumption.
+*
+* Saving sessions is done on a best-effort basis; an implementation is
+* allowed to drop sessions due to space constraints.
+*
+* Implementations should strive to be thread safe
+*/
+class BOTAN_PUBLIC_API(2,0) Session_Manager
+ {
+ public:
+ /**
+ * Try to load a saved session (using session ID)
+ * @param session_id the session identifier we are trying to resume
+ * @param session will be set to the saved session data (if found),
+ or not modified if not found
+ * @return true if session was modified
+ */
+ virtual bool load_from_session_id(const std::vector<uint8_t>& session_id,
+ Session& session) = 0;
+
+ /**
+ * Try to load a saved session (using info about server)
+ * @param info the information about the server
+ * @param session will be set to the saved session data (if found),
+ or not modified if not found
+ * @return true if session was modified
+ */
+ virtual bool load_from_server_info(const Server_Information& info,
+ Session& session) = 0;
+
+ /**
+ * Remove this session id from the cache, if it exists
+ */
+ virtual void remove_entry(const std::vector<uint8_t>& session_id) = 0;
+
+ /**
+ * Remove all sessions from the cache, return number of sessions deleted
+ */
+ virtual size_t remove_all() = 0;
+
+ /**
+ * Save a session on a best effort basis; the manager may not in
+ * fact be able to save the session for whatever reason; this is
+ * not an error. Caller cannot assume that calling save followed
+ * immediately by load_from_* will result in a successful lookup.
+ *
+ * @param session to save
+ */
+ virtual void save(const Session& session) = 0;
+
+ /**
+ * Return the allowed lifetime of a session; beyond this time,
+ * sessions are not resumed. Returns 0 if unknown/no explicit
+ * expiration policy.
+ */
+ virtual std::chrono::seconds session_lifetime() const = 0;
+
+ virtual ~Session_Manager() = default;
+ };
+
+/**
+* An implementation of Session_Manager that does not save sessions at
+* all, preventing session resumption.
+*/
+class BOTAN_PUBLIC_API(2,0) Session_Manager_Noop final : public Session_Manager
+ {
+ public:
+ bool load_from_session_id(const std::vector<uint8_t>&, Session&) override
+ { return false; }
+
+ bool load_from_server_info(const Server_Information&, Session&) override
+ { return false; }
+
+ void remove_entry(const std::vector<uint8_t>&) override {}
+
+ size_t remove_all() override { return 0; }
+
+ void save(const Session&) override {}
+
+ std::chrono::seconds session_lifetime() const override
+ { return std::chrono::seconds(0); }
+ };
+
+/**
+* An implementation of Session_Manager that saves values in memory.
+*/
+class BOTAN_PUBLIC_API(2,0) Session_Manager_In_Memory final : public Session_Manager
+ {
+ public:
+ /**
+ * @param rng a RNG used for generating session key and for
+ * session encryption
+ * @param max_sessions a hint on the maximum number of sessions
+ * to keep in memory at any one time. (If zero, don't cap)
+ * @param session_lifetime sessions are expired after this many
+ * seconds have elapsed from initial handshake.
+ */
+ Session_Manager_In_Memory(RandomNumberGenerator& rng,
+ size_t max_sessions = 1000,
+ std::chrono::seconds session_lifetime =
+ std::chrono::seconds(7200));
+
+ bool load_from_session_id(const std::vector<uint8_t>& session_id,
+ Session& session) override;
+
+ bool load_from_server_info(const Server_Information& info,
+ Session& session) override;
+
+ void remove_entry(const std::vector<uint8_t>& session_id) override;
+
+ size_t remove_all() override;
+
+ void save(const Session& session_data) override;
+
+ std::chrono::seconds session_lifetime() const override
+ { return m_session_lifetime; }
+
+ private:
+ bool load_from_session_str(const std::string& session_str,
+ Session& session);
+
+ mutex_type m_mutex;
+
+ size_t m_max_sessions;
+
+ std::chrono::seconds m_session_lifetime;
+
+ RandomNumberGenerator& m_rng;
+ secure_vector<uint8_t> m_session_key;
+
+ std::map<std::string, std::vector<uint8_t>> m_sessions; // hex(session_id) -> session
+ std::map<Server_Information, std::string> m_info_sessions;
+ };
+
+}
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/tls/tls_session_manager_memory.cpp b/comm/third_party/botan/src/lib/tls/tls_session_manager_memory.cpp
new file mode 100644
index 0000000000..600eb440a9
--- /dev/null
+++ b/comm/third_party/botan/src/lib/tls/tls_session_manager_memory.cpp
@@ -0,0 +1,132 @@
+/*
+* TLS Session Management
+* (C) 2011,2012 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/tls_session_manager.h>
+#include <botan/hex.h>
+#include <botan/rng.h>
+#include <chrono>
+
+namespace Botan {
+
+namespace TLS {
+
+Session_Manager_In_Memory::Session_Manager_In_Memory(
+ RandomNumberGenerator& rng,
+ size_t max_sessions,
+ std::chrono::seconds session_lifetime) :
+ m_max_sessions(max_sessions),
+ m_session_lifetime(session_lifetime),
+ m_rng(rng),
+ m_session_key(m_rng.random_vec(32))
+ {}
+
+bool Session_Manager_In_Memory::load_from_session_str(
+ const std::string& session_str, Session& session)
+ {
+ // assert(lock is held)
+
+ auto i = m_sessions.find(session_str);
+
+ if(i == m_sessions.end())
+ return false;
+
+ try
+ {
+ session = Session::decrypt(i->second, m_session_key);
+ }
+ catch(...)
+ {
+ return false;
+ }
+
+ // if session has expired, remove it
+ const auto now = std::chrono::system_clock::now();
+
+ if(session.start_time() + session_lifetime() < now)
+ {
+ m_sessions.erase(i);
+ return false;
+ }
+
+ return true;
+ }
+
+bool Session_Manager_In_Memory::load_from_session_id(
+ const std::vector<uint8_t>& session_id, Session& session)
+ {
+ lock_guard_type<mutex_type> lock(m_mutex);
+
+ return load_from_session_str(hex_encode(session_id), session);
+ }
+
+bool Session_Manager_In_Memory::load_from_server_info(
+ const Server_Information& info, Session& session)
+ {
+ lock_guard_type<mutex_type> lock(m_mutex);
+
+ auto i = m_info_sessions.find(info);
+
+ if(i == m_info_sessions.end())
+ return false;
+
+ if(load_from_session_str(i->second, session))
+ return true;
+
+ /*
+ * It existed at one point but was removed from the sessions map,
+ * remove m_info_sessions entry as well
+ */
+ m_info_sessions.erase(i);
+
+ return false;
+ }
+
+void Session_Manager_In_Memory::remove_entry(
+ const std::vector<uint8_t>& session_id)
+ {
+ lock_guard_type<mutex_type> lock(m_mutex);
+
+ auto i = m_sessions.find(hex_encode(session_id));
+
+ if(i != m_sessions.end())
+ m_sessions.erase(i);
+ }
+
+size_t Session_Manager_In_Memory::remove_all()
+ {
+ const size_t removed = m_sessions.size();
+ m_info_sessions.clear();
+ m_sessions.clear();
+ m_rng.random_vec(m_session_key, 32);
+ return removed;
+ }
+
+void Session_Manager_In_Memory::save(const Session& session)
+ {
+ lock_guard_type<mutex_type> lock(m_mutex);
+
+ if(m_max_sessions != 0)
+ {
+ /*
+ We generate new session IDs with the first 4 bytes being a
+ timestamp, so this actually removes the oldest sessions first.
+ */
+ while(m_sessions.size() >= m_max_sessions)
+ m_sessions.erase(m_sessions.begin());
+ }
+
+ const std::string session_id_str = hex_encode(session.session_id());
+
+ m_sessions[session_id_str] = session.encrypt(m_session_key, m_rng);
+
+ if(session.side() == CLIENT && !session.server_info().empty())
+ m_info_sessions[session.server_info()] = session_id_str;
+ }
+
+}
+
+}
diff --git a/comm/third_party/botan/src/lib/tls/tls_suite_info.cpp b/comm/third_party/botan/src/lib/tls/tls_suite_info.cpp
new file mode 100644
index 0000000000..f55b7cce16
--- /dev/null
+++ b/comm/third_party/botan/src/lib/tls/tls_suite_info.cpp
@@ -0,0 +1,212 @@
+/*
+* TLS cipher suite information
+*
+* This file was automatically generated from the IANA assignments
+* (tls-parameters.txt hash fe1ef8f3492b0708f3b14c9e8f8de55188c1b3c0)
+* by ./src/scripts/tls_suite_info.py on 2019-05-24
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/tls_ciphersuite.h>
+
+namespace Botan {
+
+namespace TLS {
+
+//static
+const std::vector<Ciphersuite>& Ciphersuite::all_known_ciphersuites()
+ {
+ // Note that this list of ciphersuites is ordered by id!
+ static const std::vector<Ciphersuite> 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(0x0013, "DHE_DSS_WITH_3DES_EDE_CBC_SHA", Auth_Method::DSA, Kex_Algo::DH, "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(0x001B, "DH_anon_WITH_3DES_EDE_CBC_SHA", Auth_Method::ANONYMOUS, Kex_Algo::DH, "3DES", 24, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE),
+ 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(0x0032, "DHE_DSS_WITH_AES_128_CBC_SHA", Auth_Method::DSA, Kex_Algo::DH, "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(0x0034, "DH_anon_WITH_AES_128_CBC_SHA", Auth_Method::ANONYMOUS, 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),
+ Ciphersuite(0x0038, "DHE_DSS_WITH_AES_256_CBC_SHA", Auth_Method::DSA, Kex_Algo::DH, "AES-256", 32, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE),
+ Ciphersuite(0x0039, "DHE_RSA_WITH_AES_256_CBC_SHA", Auth_Method::RSA, Kex_Algo::DH, "AES-256", 32, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE),
+ Ciphersuite(0x003A, "DH_anon_WITH_AES_256_CBC_SHA", Auth_Method::ANONYMOUS, Kex_Algo::DH, "AES-256", 32, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE),
+ Ciphersuite(0x003C, "RSA_WITH_AES_128_CBC_SHA256", Auth_Method::IMPLICIT, Kex_Algo::STATIC_RSA, "AES-128", 16, "SHA-256", 32, KDF_Algo::SHA_256, Nonce_Format::CBC_MODE),
+ Ciphersuite(0x003D, "RSA_WITH_AES_256_CBC_SHA256", Auth_Method::IMPLICIT, Kex_Algo::STATIC_RSA, "AES-256", 32, "SHA-256", 32, KDF_Algo::SHA_256, Nonce_Format::CBC_MODE),
+ Ciphersuite(0x0040, "DHE_DSS_WITH_AES_128_CBC_SHA256", Auth_Method::DSA, Kex_Algo::DH, "AES-128", 16, "SHA-256", 32, KDF_Algo::SHA_256, Nonce_Format::CBC_MODE),
+ Ciphersuite(0x0041, "RSA_WITH_CAMELLIA_128_CBC_SHA", Auth_Method::IMPLICIT, Kex_Algo::STATIC_RSA, "Camellia-128", 16, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE),
+ Ciphersuite(0x0044, "DHE_DSS_WITH_CAMELLIA_128_CBC_SHA", Auth_Method::DSA, Kex_Algo::DH, "Camellia-128", 16, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE),
+ Ciphersuite(0x0045, "DHE_RSA_WITH_CAMELLIA_128_CBC_SHA", Auth_Method::RSA, Kex_Algo::DH, "Camellia-128", 16, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE),
+ Ciphersuite(0x0046, "DH_anon_WITH_CAMELLIA_128_CBC_SHA", Auth_Method::ANONYMOUS, Kex_Algo::DH, "Camellia-128", 16, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE),
+ Ciphersuite(0x0067, "DHE_RSA_WITH_AES_128_CBC_SHA256", Auth_Method::RSA, Kex_Algo::DH, "AES-128", 16, "SHA-256", 32, KDF_Algo::SHA_256, Nonce_Format::CBC_MODE),
+ Ciphersuite(0x006A, "DHE_DSS_WITH_AES_256_CBC_SHA256", Auth_Method::DSA, Kex_Algo::DH, "AES-256", 32, "SHA-256", 32, KDF_Algo::SHA_256, Nonce_Format::CBC_MODE),
+ Ciphersuite(0x006B, "DHE_RSA_WITH_AES_256_CBC_SHA256", Auth_Method::RSA, Kex_Algo::DH, "AES-256", 32, "SHA-256", 32, KDF_Algo::SHA_256, Nonce_Format::CBC_MODE),
+ Ciphersuite(0x006C, "DH_anon_WITH_AES_128_CBC_SHA256", Auth_Method::ANONYMOUS, Kex_Algo::DH, "AES-128", 16, "SHA-256", 32, KDF_Algo::SHA_256, Nonce_Format::CBC_MODE),
+ Ciphersuite(0x006D, "DH_anon_WITH_AES_256_CBC_SHA256", Auth_Method::ANONYMOUS, Kex_Algo::DH, "AES-256", 32, "SHA-256", 32, KDF_Algo::SHA_256, Nonce_Format::CBC_MODE),
+ Ciphersuite(0x0084, "RSA_WITH_CAMELLIA_256_CBC_SHA", Auth_Method::IMPLICIT, Kex_Algo::STATIC_RSA, "Camellia-256", 32, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE),
+ Ciphersuite(0x0087, "DHE_DSS_WITH_CAMELLIA_256_CBC_SHA", Auth_Method::DSA, Kex_Algo::DH, "Camellia-256", 32, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE),
+ Ciphersuite(0x0088, "DHE_RSA_WITH_CAMELLIA_256_CBC_SHA", Auth_Method::RSA, Kex_Algo::DH, "Camellia-256", 32, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE),
+ Ciphersuite(0x0089, "DH_anon_WITH_CAMELLIA_256_CBC_SHA", Auth_Method::ANONYMOUS, Kex_Algo::DH, "Camellia-256", 32, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE),
+ Ciphersuite(0x008B, "PSK_WITH_3DES_EDE_CBC_SHA", Auth_Method::IMPLICIT, Kex_Algo::PSK, "3DES", 24, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE),
+ Ciphersuite(0x008C, "PSK_WITH_AES_128_CBC_SHA", Auth_Method::IMPLICIT, Kex_Algo::PSK, "AES-128", 16, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE),
+ Ciphersuite(0x008D, "PSK_WITH_AES_256_CBC_SHA", Auth_Method::IMPLICIT, Kex_Algo::PSK, "AES-256", 32, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE),
+ Ciphersuite(0x008F, "DHE_PSK_WITH_3DES_EDE_CBC_SHA", Auth_Method::IMPLICIT, Kex_Algo::DHE_PSK, "3DES", 24, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE),
+ Ciphersuite(0x0090, "DHE_PSK_WITH_AES_128_CBC_SHA", Auth_Method::IMPLICIT, Kex_Algo::DHE_PSK, "AES-128", 16, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE),
+ Ciphersuite(0x0091, "DHE_PSK_WITH_AES_256_CBC_SHA", Auth_Method::IMPLICIT, Kex_Algo::DHE_PSK, "AES-256", 32, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE),
+ Ciphersuite(0x0096, "RSA_WITH_SEED_CBC_SHA", Auth_Method::IMPLICIT, Kex_Algo::STATIC_RSA, "SEED", 16, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE),
+ Ciphersuite(0x0099, "DHE_DSS_WITH_SEED_CBC_SHA", Auth_Method::DSA, Kex_Algo::DH, "SEED", 16, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE),
+ Ciphersuite(0x009A, "DHE_RSA_WITH_SEED_CBC_SHA", Auth_Method::RSA, Kex_Algo::DH, "SEED", 16, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE),
+ Ciphersuite(0x009B, "DH_anon_WITH_SEED_CBC_SHA", Auth_Method::ANONYMOUS, Kex_Algo::DH, "SEED", 16, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE),
+ Ciphersuite(0x009C, "RSA_WITH_AES_128_GCM_SHA256", Auth_Method::IMPLICIT, Kex_Algo::STATIC_RSA, "AES-128/GCM", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+ Ciphersuite(0x009D, "RSA_WITH_AES_256_GCM_SHA384", Auth_Method::IMPLICIT, Kex_Algo::STATIC_RSA, "AES-256/GCM", 32, "AEAD", 0, KDF_Algo::SHA_384, Nonce_Format::AEAD_IMPLICIT_4),
+ Ciphersuite(0x009E, "DHE_RSA_WITH_AES_128_GCM_SHA256", Auth_Method::RSA, Kex_Algo::DH, "AES-128/GCM", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+ Ciphersuite(0x009F, "DHE_RSA_WITH_AES_256_GCM_SHA384", Auth_Method::RSA, Kex_Algo::DH, "AES-256/GCM", 32, "AEAD", 0, KDF_Algo::SHA_384, Nonce_Format::AEAD_IMPLICIT_4),
+ Ciphersuite(0x00A2, "DHE_DSS_WITH_AES_128_GCM_SHA256", Auth_Method::DSA, Kex_Algo::DH, "AES-128/GCM", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+ Ciphersuite(0x00A3, "DHE_DSS_WITH_AES_256_GCM_SHA384", Auth_Method::DSA, Kex_Algo::DH, "AES-256/GCM", 32, "AEAD", 0, KDF_Algo::SHA_384, Nonce_Format::AEAD_IMPLICIT_4),
+ Ciphersuite(0x00A6, "DH_anon_WITH_AES_128_GCM_SHA256", Auth_Method::ANONYMOUS, Kex_Algo::DH, "AES-128/GCM", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+ Ciphersuite(0x00A7, "DH_anon_WITH_AES_256_GCM_SHA384", Auth_Method::ANONYMOUS, Kex_Algo::DH, "AES-256/GCM", 32, "AEAD", 0, KDF_Algo::SHA_384, Nonce_Format::AEAD_IMPLICIT_4),
+ Ciphersuite(0x00A8, "PSK_WITH_AES_128_GCM_SHA256", Auth_Method::IMPLICIT, Kex_Algo::PSK, "AES-128/GCM", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+ 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(0x00AA, "DHE_PSK_WITH_AES_128_GCM_SHA256", Auth_Method::IMPLICIT, Kex_Algo::DHE_PSK, "AES-128/GCM", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+ Ciphersuite(0x00AB, "DHE_PSK_WITH_AES_256_GCM_SHA384", Auth_Method::IMPLICIT, Kex_Algo::DHE_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(0x00B2, "DHE_PSK_WITH_AES_128_CBC_SHA256", Auth_Method::IMPLICIT, Kex_Algo::DHE_PSK, "AES-128", 16, "SHA-256", 32, KDF_Algo::SHA_256, Nonce_Format::CBC_MODE),
+ Ciphersuite(0x00B3, "DHE_PSK_WITH_AES_256_CBC_SHA384", Auth_Method::IMPLICIT, Kex_Algo::DHE_PSK, "AES-256", 32, "SHA-384", 48, KDF_Algo::SHA_384, Nonce_Format::CBC_MODE),
+ Ciphersuite(0x00BA, "RSA_WITH_CAMELLIA_128_CBC_SHA256", Auth_Method::IMPLICIT, Kex_Algo::STATIC_RSA, "Camellia-128", 16, "SHA-256", 32, KDF_Algo::SHA_256, Nonce_Format::CBC_MODE),
+ Ciphersuite(0x00BD, "DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256", Auth_Method::DSA, Kex_Algo::DH, "Camellia-128", 16, "SHA-256", 32, KDF_Algo::SHA_256, Nonce_Format::CBC_MODE),
+ Ciphersuite(0x00BE, "DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256", Auth_Method::RSA, Kex_Algo::DH, "Camellia-128", 16, "SHA-256", 32, KDF_Algo::SHA_256, Nonce_Format::CBC_MODE),
+ Ciphersuite(0x00BF, "DH_anon_WITH_CAMELLIA_128_CBC_SHA256", Auth_Method::ANONYMOUS, Kex_Algo::DH, "Camellia-128", 16, "SHA-256", 32, KDF_Algo::SHA_256, Nonce_Format::CBC_MODE),
+ Ciphersuite(0x00C0, "RSA_WITH_CAMELLIA_256_CBC_SHA256", Auth_Method::IMPLICIT, Kex_Algo::STATIC_RSA, "Camellia-256", 32, "SHA-256", 32, KDF_Algo::SHA_256, Nonce_Format::CBC_MODE),
+ Ciphersuite(0x00C3, "DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256", Auth_Method::DSA, Kex_Algo::DH, "Camellia-256", 32, "SHA-256", 32, KDF_Algo::SHA_256, Nonce_Format::CBC_MODE),
+ Ciphersuite(0x00C4, "DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256", Auth_Method::RSA, Kex_Algo::DH, "Camellia-256", 32, "SHA-256", 32, KDF_Algo::SHA_256, Nonce_Format::CBC_MODE),
+ Ciphersuite(0x00C5, "DH_anon_WITH_CAMELLIA_256_CBC_SHA256", Auth_Method::ANONYMOUS, Kex_Algo::DH, "Camellia-256", 32, "SHA-256", 32, KDF_Algo::SHA_256, Nonce_Format::CBC_MODE),
+ Ciphersuite(0x16B7, "CECPQ1_RSA_WITH_CHACHA20_POLY1305_SHA256", Auth_Method::RSA, Kex_Algo::CECPQ1, "ChaCha20Poly1305", 32, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_XOR_12),
+ Ciphersuite(0x16B8, "CECPQ1_ECDSA_WITH_CHACHA20_POLY1305_SHA256", Auth_Method::ECDSA, Kex_Algo::CECPQ1, "ChaCha20Poly1305", 32, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_XOR_12),
+ Ciphersuite(0x16B9, "CECPQ1_RSA_WITH_AES_256_GCM_SHA384", Auth_Method::RSA, Kex_Algo::CECPQ1, "AES-256/GCM", 32, "AEAD", 0, KDF_Algo::SHA_384, Nonce_Format::AEAD_IMPLICIT_4),
+ Ciphersuite(0x16BA, "CECPQ1_ECDSA_WITH_AES_256_GCM_SHA384", Auth_Method::ECDSA, Kex_Algo::CECPQ1, "AES-256/GCM", 32, "AEAD", 0, KDF_Algo::SHA_384, Nonce_Format::AEAD_IMPLICIT_4),
+ 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(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),
+ Ciphersuite(0xC017, "ECDH_anon_WITH_3DES_EDE_CBC_SHA", Auth_Method::ANONYMOUS, Kex_Algo::ECDH, "3DES", 24, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE),
+ Ciphersuite(0xC018, "ECDH_anon_WITH_AES_128_CBC_SHA", Auth_Method::ANONYMOUS, Kex_Algo::ECDH, "AES-128", 16, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE),
+ Ciphersuite(0xC019, "ECDH_anon_WITH_AES_256_CBC_SHA", Auth_Method::ANONYMOUS, Kex_Algo::ECDH, "AES-256", 32, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE),
+ Ciphersuite(0xC01A, "SRP_SHA_WITH_3DES_EDE_CBC_SHA", Auth_Method::IMPLICIT, Kex_Algo::SRP_SHA, "3DES", 24, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE),
+ Ciphersuite(0xC01B, "SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA", Auth_Method::RSA, Kex_Algo::SRP_SHA, "3DES", 24, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE),
+ Ciphersuite(0xC01C, "SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA", Auth_Method::DSA, Kex_Algo::SRP_SHA, "3DES", 24, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE),
+ Ciphersuite(0xC01D, "SRP_SHA_WITH_AES_128_CBC_SHA", Auth_Method::IMPLICIT, Kex_Algo::SRP_SHA, "AES-128", 16, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE),
+ Ciphersuite(0xC01E, "SRP_SHA_RSA_WITH_AES_128_CBC_SHA", Auth_Method::RSA, Kex_Algo::SRP_SHA, "AES-128", 16, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE),
+ Ciphersuite(0xC01F, "SRP_SHA_DSS_WITH_AES_128_CBC_SHA", Auth_Method::DSA, Kex_Algo::SRP_SHA, "AES-128", 16, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE),
+ Ciphersuite(0xC020, "SRP_SHA_WITH_AES_256_CBC_SHA", Auth_Method::IMPLICIT, Kex_Algo::SRP_SHA, "AES-256", 32, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE),
+ Ciphersuite(0xC021, "SRP_SHA_RSA_WITH_AES_256_CBC_SHA", Auth_Method::RSA, Kex_Algo::SRP_SHA, "AES-256", 32, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE),
+ Ciphersuite(0xC022, "SRP_SHA_DSS_WITH_AES_256_CBC_SHA", Auth_Method::DSA, Kex_Algo::SRP_SHA, "AES-256", 32, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE),
+ Ciphersuite(0xC023, "ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", Auth_Method::ECDSA, Kex_Algo::ECDH, "AES-128", 16, "SHA-256", 32, KDF_Algo::SHA_256, Nonce_Format::CBC_MODE),
+ Ciphersuite(0xC024, "ECDHE_ECDSA_WITH_AES_256_CBC_SHA384", Auth_Method::ECDSA, Kex_Algo::ECDH, "AES-256", 32, "SHA-384", 48, KDF_Algo::SHA_384, Nonce_Format::CBC_MODE),
+ Ciphersuite(0xC027, "ECDHE_RSA_WITH_AES_128_CBC_SHA256", Auth_Method::RSA, Kex_Algo::ECDH, "AES-128", 16, "SHA-256", 32, KDF_Algo::SHA_256, Nonce_Format::CBC_MODE),
+ Ciphersuite(0xC028, "ECDHE_RSA_WITH_AES_256_CBC_SHA384", Auth_Method::RSA, Kex_Algo::ECDH, "AES-256", 32, "SHA-384", 48, KDF_Algo::SHA_384, Nonce_Format::CBC_MODE),
+ Ciphersuite(0xC02B, "ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", Auth_Method::ECDSA, Kex_Algo::ECDH, "AES-128/GCM", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+ Ciphersuite(0xC02C, "ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", Auth_Method::ECDSA, Kex_Algo::ECDH, "AES-256/GCM", 32, "AEAD", 0, KDF_Algo::SHA_384, Nonce_Format::AEAD_IMPLICIT_4),
+ Ciphersuite(0xC02F, "ECDHE_RSA_WITH_AES_128_GCM_SHA256", Auth_Method::RSA, Kex_Algo::ECDH, "AES-128/GCM", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+ Ciphersuite(0xC030, "ECDHE_RSA_WITH_AES_256_GCM_SHA384", Auth_Method::RSA, Kex_Algo::ECDH, "AES-256/GCM", 32, "AEAD", 0, KDF_Algo::SHA_384, Nonce_Format::AEAD_IMPLICIT_4),
+ Ciphersuite(0xC034, "ECDHE_PSK_WITH_3DES_EDE_CBC_SHA", Auth_Method::IMPLICIT, Kex_Algo::ECDHE_PSK, "3DES", 24, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE),
+ Ciphersuite(0xC035, "ECDHE_PSK_WITH_AES_128_CBC_SHA", Auth_Method::IMPLICIT, Kex_Algo::ECDHE_PSK, "AES-128", 16, "SHA-1", 20, KDF_Algo::SHA_1, Nonce_Format::CBC_MODE),
+ 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(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),
+ Ciphersuite(0xC053, "DHE_RSA_WITH_ARIA_256_GCM_SHA384", Auth_Method::RSA, Kex_Algo::DH, "ARIA-256/GCM", 32, "AEAD", 0, KDF_Algo::SHA_384, Nonce_Format::AEAD_IMPLICIT_4),
+ Ciphersuite(0xC056, "DHE_DSS_WITH_ARIA_128_GCM_SHA256", Auth_Method::DSA, Kex_Algo::DH, "ARIA-128/GCM", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+ Ciphersuite(0xC057, "DHE_DSS_WITH_ARIA_256_GCM_SHA384", Auth_Method::DSA, Kex_Algo::DH, "ARIA-256/GCM", 32, "AEAD", 0, KDF_Algo::SHA_384, Nonce_Format::AEAD_IMPLICIT_4),
+ Ciphersuite(0xC05A, "DH_anon_WITH_ARIA_128_GCM_SHA256", Auth_Method::ANONYMOUS, Kex_Algo::DH, "ARIA-128/GCM", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+ Ciphersuite(0xC05B, "DH_anon_WITH_ARIA_256_GCM_SHA384", Auth_Method::ANONYMOUS, Kex_Algo::DH, "ARIA-256/GCM", 32, "AEAD", 0, KDF_Algo::SHA_384, Nonce_Format::AEAD_IMPLICIT_4),
+ Ciphersuite(0xC05C, "ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256", Auth_Method::ECDSA, Kex_Algo::ECDH, "ARIA-128/GCM", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+ Ciphersuite(0xC05D, "ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384", Auth_Method::ECDSA, Kex_Algo::ECDH, "ARIA-256/GCM", 32, "AEAD", 0, KDF_Algo::SHA_384, Nonce_Format::AEAD_IMPLICIT_4),
+ Ciphersuite(0xC060, "ECDHE_RSA_WITH_ARIA_128_GCM_SHA256", Auth_Method::RSA, Kex_Algo::ECDH, "ARIA-128/GCM", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+ Ciphersuite(0xC061, "ECDHE_RSA_WITH_ARIA_256_GCM_SHA384", Auth_Method::RSA, Kex_Algo::ECDH, "ARIA-256/GCM", 32, "AEAD", 0, KDF_Algo::SHA_384, Nonce_Format::AEAD_IMPLICIT_4),
+ Ciphersuite(0xC06A, "PSK_WITH_ARIA_128_GCM_SHA256", Auth_Method::IMPLICIT, Kex_Algo::PSK, "ARIA-128/GCM", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+ Ciphersuite(0xC06B, "PSK_WITH_ARIA_256_GCM_SHA384", Auth_Method::IMPLICIT, Kex_Algo::PSK, "ARIA-256/GCM", 32, "AEAD", 0, KDF_Algo::SHA_384, Nonce_Format::AEAD_IMPLICIT_4),
+ Ciphersuite(0xC06C, "DHE_PSK_WITH_ARIA_128_GCM_SHA256", Auth_Method::IMPLICIT, Kex_Algo::DHE_PSK, "ARIA-128/GCM", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+ Ciphersuite(0xC06D, "DHE_PSK_WITH_ARIA_256_GCM_SHA384", Auth_Method::IMPLICIT, Kex_Algo::DHE_PSK, "ARIA-256/GCM", 32, "AEAD", 0, KDF_Algo::SHA_384, Nonce_Format::AEAD_IMPLICIT_4),
+ Ciphersuite(0xC072, "ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256", Auth_Method::ECDSA, Kex_Algo::ECDH, "Camellia-128", 16, "SHA-256", 32, KDF_Algo::SHA_256, Nonce_Format::CBC_MODE),
+ Ciphersuite(0xC073, "ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384", Auth_Method::ECDSA, Kex_Algo::ECDH, "Camellia-256", 32, "SHA-384", 48, KDF_Algo::SHA_384, Nonce_Format::CBC_MODE),
+ Ciphersuite(0xC076, "ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256", Auth_Method::RSA, Kex_Algo::ECDH, "Camellia-128", 16, "SHA-256", 32, KDF_Algo::SHA_256, Nonce_Format::CBC_MODE),
+ Ciphersuite(0xC077, "ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384", Auth_Method::RSA, Kex_Algo::ECDH, "Camellia-256", 32, "SHA-384", 48, KDF_Algo::SHA_384, Nonce_Format::CBC_MODE),
+ Ciphersuite(0xC07A, "RSA_WITH_CAMELLIA_128_GCM_SHA256", Auth_Method::IMPLICIT, Kex_Algo::STATIC_RSA, "Camellia-128/GCM", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+ Ciphersuite(0xC07B, "RSA_WITH_CAMELLIA_256_GCM_SHA384", Auth_Method::IMPLICIT, Kex_Algo::STATIC_RSA, "Camellia-256/GCM", 32, "AEAD", 0, KDF_Algo::SHA_384, Nonce_Format::AEAD_IMPLICIT_4),
+ Ciphersuite(0xC07C, "DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256", Auth_Method::RSA, Kex_Algo::DH, "Camellia-128/GCM", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+ Ciphersuite(0xC07D, "DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384", Auth_Method::RSA, Kex_Algo::DH, "Camellia-256/GCM", 32, "AEAD", 0, KDF_Algo::SHA_384, Nonce_Format::AEAD_IMPLICIT_4),
+ Ciphersuite(0xC080, "DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256", Auth_Method::DSA, Kex_Algo::DH, "Camellia-128/GCM", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+ Ciphersuite(0xC081, "DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384", Auth_Method::DSA, Kex_Algo::DH, "Camellia-256/GCM", 32, "AEAD", 0, KDF_Algo::SHA_384, Nonce_Format::AEAD_IMPLICIT_4),
+ Ciphersuite(0xC084, "DH_anon_WITH_CAMELLIA_128_GCM_SHA256", Auth_Method::ANONYMOUS, Kex_Algo::DH, "Camellia-128/GCM", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+ Ciphersuite(0xC085, "DH_anon_WITH_CAMELLIA_256_GCM_SHA384", Auth_Method::ANONYMOUS, Kex_Algo::DH, "Camellia-256/GCM", 32, "AEAD", 0, KDF_Algo::SHA_384, Nonce_Format::AEAD_IMPLICIT_4),
+ Ciphersuite(0xC086, "ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256", Auth_Method::ECDSA, Kex_Algo::ECDH, "Camellia-128/GCM", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+ Ciphersuite(0xC087, "ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384", Auth_Method::ECDSA, Kex_Algo::ECDH, "Camellia-256/GCM", 32, "AEAD", 0, KDF_Algo::SHA_384, Nonce_Format::AEAD_IMPLICIT_4),
+ Ciphersuite(0xC08A, "ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256", Auth_Method::RSA, Kex_Algo::ECDH, "Camellia-128/GCM", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+ Ciphersuite(0xC08B, "ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384", Auth_Method::RSA, Kex_Algo::ECDH, "Camellia-256/GCM", 32, "AEAD", 0, KDF_Algo::SHA_384, Nonce_Format::AEAD_IMPLICIT_4),
+ Ciphersuite(0xC08E, "PSK_WITH_CAMELLIA_128_GCM_SHA256", Auth_Method::IMPLICIT, Kex_Algo::PSK, "Camellia-128/GCM", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+ Ciphersuite(0xC08F, "PSK_WITH_CAMELLIA_256_GCM_SHA384", Auth_Method::IMPLICIT, Kex_Algo::PSK, "Camellia-256/GCM", 32, "AEAD", 0, KDF_Algo::SHA_384, Nonce_Format::AEAD_IMPLICIT_4),
+ Ciphersuite(0xC090, "DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256", Auth_Method::IMPLICIT, Kex_Algo::DHE_PSK, "Camellia-128/GCM", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+ Ciphersuite(0xC091, "DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384", Auth_Method::IMPLICIT, Kex_Algo::DHE_PSK, "Camellia-256/GCM", 32, "AEAD", 0, KDF_Algo::SHA_384, Nonce_Format::AEAD_IMPLICIT_4),
+ Ciphersuite(0xC094, "PSK_WITH_CAMELLIA_128_CBC_SHA256", Auth_Method::IMPLICIT, Kex_Algo::PSK, "Camellia-128", 16, "SHA-256", 32, KDF_Algo::SHA_256, Nonce_Format::CBC_MODE),
+ Ciphersuite(0xC095, "PSK_WITH_CAMELLIA_256_CBC_SHA384", Auth_Method::IMPLICIT, Kex_Algo::PSK, "Camellia-256", 32, "SHA-384", 48, KDF_Algo::SHA_384, Nonce_Format::CBC_MODE),
+ Ciphersuite(0xC096, "DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256", Auth_Method::IMPLICIT, Kex_Algo::DHE_PSK, "Camellia-128", 16, "SHA-256", 32, KDF_Algo::SHA_256, Nonce_Format::CBC_MODE),
+ Ciphersuite(0xC097, "DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384", Auth_Method::IMPLICIT, Kex_Algo::DHE_PSK, "Camellia-256", 32, "SHA-384", 48, KDF_Algo::SHA_384, Nonce_Format::CBC_MODE),
+ Ciphersuite(0xC09A, "ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256", Auth_Method::IMPLICIT, Kex_Algo::ECDHE_PSK, "Camellia-128", 16, "SHA-256", 32, KDF_Algo::SHA_256, Nonce_Format::CBC_MODE),
+ Ciphersuite(0xC09B, "ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384", Auth_Method::IMPLICIT, Kex_Algo::ECDHE_PSK, "Camellia-256", 32, "SHA-384", 48, KDF_Algo::SHA_384, Nonce_Format::CBC_MODE),
+ Ciphersuite(0xC09C, "RSA_WITH_AES_128_CCM", Auth_Method::IMPLICIT, Kex_Algo::STATIC_RSA, "AES-128/CCM", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+ Ciphersuite(0xC09D, "RSA_WITH_AES_256_CCM", Auth_Method::IMPLICIT, Kex_Algo::STATIC_RSA, "AES-256/CCM", 32, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+ Ciphersuite(0xC09E, "DHE_RSA_WITH_AES_128_CCM", Auth_Method::RSA, Kex_Algo::DH, "AES-128/CCM", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+ Ciphersuite(0xC09F, "DHE_RSA_WITH_AES_256_CCM", Auth_Method::RSA, Kex_Algo::DH, "AES-256/CCM", 32, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+ Ciphersuite(0xC0A0, "RSA_WITH_AES_128_CCM_8", Auth_Method::IMPLICIT, Kex_Algo::STATIC_RSA, "AES-128/CCM(8)", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+ Ciphersuite(0xC0A1, "RSA_WITH_AES_256_CCM_8", Auth_Method::IMPLICIT, Kex_Algo::STATIC_RSA, "AES-256/CCM(8)", 32, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+ Ciphersuite(0xC0A2, "DHE_RSA_WITH_AES_128_CCM_8", Auth_Method::RSA, Kex_Algo::DH, "AES-128/CCM(8)", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+ Ciphersuite(0xC0A3, "DHE_RSA_WITH_AES_256_CCM_8", Auth_Method::RSA, Kex_Algo::DH, "AES-256/CCM(8)", 32, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+ Ciphersuite(0xC0A4, "PSK_WITH_AES_128_CCM", Auth_Method::IMPLICIT, Kex_Algo::PSK, "AES-128/CCM", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+ Ciphersuite(0xC0A5, "PSK_WITH_AES_256_CCM", Auth_Method::IMPLICIT, Kex_Algo::PSK, "AES-256/CCM", 32, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+ Ciphersuite(0xC0A6, "DHE_PSK_WITH_AES_128_CCM", Auth_Method::IMPLICIT, Kex_Algo::DHE_PSK, "AES-128/CCM", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+ Ciphersuite(0xC0A7, "DHE_PSK_WITH_AES_256_CCM", Auth_Method::IMPLICIT, Kex_Algo::DHE_PSK, "AES-256/CCM", 32, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+ Ciphersuite(0xC0A8, "PSK_WITH_AES_128_CCM_8", Auth_Method::IMPLICIT, Kex_Algo::PSK, "AES-128/CCM(8)", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+ Ciphersuite(0xC0A9, "PSK_WITH_AES_256_CCM_8", Auth_Method::IMPLICIT, Kex_Algo::PSK, "AES-256/CCM(8)", 32, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+ Ciphersuite(0xC0AA, "PSK_DHE_WITH_AES_128_CCM_8", Auth_Method::IMPLICIT, Kex_Algo::DHE_PSK, "AES-128/CCM(8)", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+ Ciphersuite(0xC0AB, "PSK_DHE_WITH_AES_256_CCM_8", Auth_Method::IMPLICIT, Kex_Algo::DHE_PSK, "AES-256/CCM(8)", 32, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+ Ciphersuite(0xC0AC, "ECDHE_ECDSA_WITH_AES_128_CCM", Auth_Method::ECDSA, Kex_Algo::ECDH, "AES-128/CCM", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+ Ciphersuite(0xC0AD, "ECDHE_ECDSA_WITH_AES_256_CCM", Auth_Method::ECDSA, Kex_Algo::ECDH, "AES-256/CCM", 32, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+ Ciphersuite(0xC0AE, "ECDHE_ECDSA_WITH_AES_128_CCM_8", Auth_Method::ECDSA, Kex_Algo::ECDH, "AES-128/CCM(8)", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+ Ciphersuite(0xC0AF, "ECDHE_ECDSA_WITH_AES_256_CCM_8", Auth_Method::ECDSA, Kex_Algo::ECDH, "AES-256/CCM(8)", 32, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+ Ciphersuite(0xCCA8, "ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256", Auth_Method::RSA, Kex_Algo::ECDH, "ChaCha20Poly1305", 32, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_XOR_12),
+ Ciphersuite(0xCCA9, "ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256", Auth_Method::ECDSA, Kex_Algo::ECDH, "ChaCha20Poly1305", 32, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_XOR_12),
+ Ciphersuite(0xCCAA, "DHE_RSA_WITH_CHACHA20_POLY1305_SHA256", Auth_Method::RSA, Kex_Algo::DH, "ChaCha20Poly1305", 32, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_XOR_12),
+ Ciphersuite(0xCCAB, "PSK_WITH_CHACHA20_POLY1305_SHA256", Auth_Method::IMPLICIT, Kex_Algo::PSK, "ChaCha20Poly1305", 32, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_XOR_12),
+ Ciphersuite(0xCCAC, "ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256", Auth_Method::IMPLICIT, Kex_Algo::ECDHE_PSK, "ChaCha20Poly1305", 32, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_XOR_12),
+ Ciphersuite(0xCCAD, "DHE_PSK_WITH_CHACHA20_POLY1305_SHA256", Auth_Method::IMPLICIT, Kex_Algo::DHE_PSK, "ChaCha20Poly1305", 32, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_XOR_12),
+ Ciphersuite(0xD001, "ECDHE_PSK_WITH_AES_128_GCM_SHA256", Auth_Method::IMPLICIT, Kex_Algo::ECDHE_PSK, "AES-128/GCM", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+ Ciphersuite(0xD002, "ECDHE_PSK_WITH_AES_256_GCM_SHA384", Auth_Method::IMPLICIT, Kex_Algo::ECDHE_PSK, "AES-256/GCM", 32, "AEAD", 0, KDF_Algo::SHA_384, Nonce_Format::AEAD_IMPLICIT_4),
+ Ciphersuite(0xD003, "ECDHE_PSK_WITH_AES_128_CCM_8_SHA256", Auth_Method::IMPLICIT, Kex_Algo::ECDHE_PSK, "AES-128/CCM(8)", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+ Ciphersuite(0xD005, "ECDHE_PSK_WITH_AES_128_CCM_SHA256", Auth_Method::IMPLICIT, Kex_Algo::ECDHE_PSK, "AES-128/CCM", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_IMPLICIT_4),
+ Ciphersuite(0xFFC0, "DHE_RSA_WITH_AES_128_OCB_SHA256", Auth_Method::RSA, Kex_Algo::DH, "AES-128/OCB(12)", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_XOR_12),
+ Ciphersuite(0xFFC1, "DHE_RSA_WITH_AES_256_OCB_SHA256", Auth_Method::RSA, Kex_Algo::DH, "AES-256/OCB(12)", 32, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_XOR_12),
+ Ciphersuite(0xFFC2, "ECDHE_RSA_WITH_AES_128_OCB_SHA256", Auth_Method::RSA, Kex_Algo::ECDH, "AES-128/OCB(12)", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_XOR_12),
+ Ciphersuite(0xFFC3, "ECDHE_RSA_WITH_AES_256_OCB_SHA256", Auth_Method::RSA, Kex_Algo::ECDH, "AES-256/OCB(12)", 32, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_XOR_12),
+ Ciphersuite(0xFFC4, "ECDHE_ECDSA_WITH_AES_128_OCB_SHA256", Auth_Method::ECDSA, Kex_Algo::ECDH, "AES-128/OCB(12)", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_XOR_12),
+ Ciphersuite(0xFFC5, "ECDHE_ECDSA_WITH_AES_256_OCB_SHA256", Auth_Method::ECDSA, Kex_Algo::ECDH, "AES-256/OCB(12)", 32, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_XOR_12),
+ Ciphersuite(0xFFC6, "PSK_WITH_AES_128_OCB_SHA256", Auth_Method::IMPLICIT, Kex_Algo::PSK, "AES-128/OCB(12)", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_XOR_12),
+ Ciphersuite(0xFFC7, "PSK_WITH_AES_256_OCB_SHA256", Auth_Method::IMPLICIT, Kex_Algo::PSK, "AES-256/OCB(12)", 32, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_XOR_12),
+ Ciphersuite(0xFFC8, "DHE_PSK_WITH_AES_128_OCB_SHA256", Auth_Method::IMPLICIT, Kex_Algo::DHE_PSK, "AES-128/OCB(12)", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_XOR_12),
+ Ciphersuite(0xFFC9, "DHE_PSK_WITH_AES_256_OCB_SHA256", Auth_Method::IMPLICIT, Kex_Algo::DHE_PSK, "AES-256/OCB(12)", 32, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_XOR_12),
+ Ciphersuite(0xFFCA, "ECDHE_PSK_WITH_AES_128_OCB_SHA256", Auth_Method::IMPLICIT, Kex_Algo::ECDHE_PSK, "AES-128/OCB(12)", 16, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_XOR_12),
+ Ciphersuite(0xFFCB, "ECDHE_PSK_WITH_AES_256_OCB_SHA256", Auth_Method::IMPLICIT, Kex_Algo::ECDHE_PSK, "AES-256/OCB(12)", 32, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_XOR_12),
+ Ciphersuite(0xFFCC, "CECPQ1_RSA_WITH_AES_256_OCB_SHA256", Auth_Method::RSA, Kex_Algo::CECPQ1, "AES-256/OCB(12)", 32, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_XOR_12),
+ Ciphersuite(0xFFCD, "CECPQ1_ECDSA_WITH_AES_256_OCB_SHA256", Auth_Method::ECDSA, Kex_Algo::CECPQ1, "AES-256/OCB(12)", 32, "AEAD", 0, KDF_Algo::SHA_256, Nonce_Format::AEAD_XOR_12),
+ };
+
+ return g_ciphersuite_list;
+ }
+
+}
+
+}
diff --git a/comm/third_party/botan/src/lib/tls/tls_text_policy.cpp b/comm/third_party/botan/src/lib/tls/tls_text_policy.cpp
new file mode 100644
index 0000000000..1b83f0dbde
--- /dev/null
+++ b/comm/third_party/botan/src/lib/tls/tls_text_policy.cpp
@@ -0,0 +1,319 @@
+/*
+* Text-Based TLS Policy
+* (C) 2016,2017 Jack Lloyd
+* 2017 Harry Reimann, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/tls_policy.h>
+#include <botan/exceptn.h>
+#include <botan/parsing.h>
+#include <sstream>
+
+namespace Botan {
+
+namespace TLS {
+
+std::vector<std::string> Text_Policy::allowed_ciphers() const
+ {
+ return get_list("ciphers", Policy::allowed_ciphers());
+ }
+
+std::vector<std::string> Text_Policy::allowed_signature_hashes() const
+ {
+ return get_list("signature_hashes", Policy::allowed_signature_hashes());
+ }
+
+std::vector<std::string> Text_Policy::allowed_macs() const
+ {
+ return get_list("macs", Policy::allowed_macs());
+ }
+
+std::vector<std::string> Text_Policy::allowed_key_exchange_methods() const
+ {
+ return get_list("key_exchange_methods", Policy::allowed_key_exchange_methods());
+ }
+
+std::vector<std::string> Text_Policy::allowed_signature_methods() const
+ {
+ return get_list("signature_methods", Policy::allowed_signature_methods());
+ }
+
+bool Text_Policy::use_ecc_point_compression() const
+ {
+ return get_bool("use_ecc_point_compression", Policy::use_ecc_point_compression());
+ }
+
+bool Text_Policy::allow_tls10() const
+ {
+ return get_bool("allow_tls10", Policy::allow_tls10());
+ }
+
+bool Text_Policy::allow_tls11() const
+ {
+ return get_bool("allow_tls11", Policy::allow_tls11());
+ }
+
+bool Text_Policy::allow_tls12() const
+ {
+ return get_bool("allow_tls12", Policy::allow_tls12());
+ }
+
+bool Text_Policy::allow_dtls10() const
+ {
+ return get_bool("allow_dtls10", Policy::allow_dtls10());
+ }
+
+bool Text_Policy::allow_dtls12() const
+ {
+ return get_bool("allow_dtls12", Policy::allow_dtls12());
+ }
+
+bool Text_Policy::allow_insecure_renegotiation() const
+ {
+ return get_bool("allow_insecure_renegotiation", Policy::allow_insecure_renegotiation());
+ }
+
+bool Text_Policy::include_time_in_hello_random() const
+ {
+ return get_bool("include_time_in_hello_random", Policy::include_time_in_hello_random());
+ }
+
+bool Text_Policy::require_client_certificate_authentication() const
+ {
+ return get_bool("require_client_certificate_authentication", Policy::require_client_certificate_authentication());
+ }
+
+bool Text_Policy::allow_client_initiated_renegotiation() const
+ {
+ return get_bool("allow_client_initiated_renegotiation", Policy::allow_client_initiated_renegotiation());
+ }
+
+bool Text_Policy::allow_server_initiated_renegotiation() const
+ {
+ return get_bool("allow_server_initiated_renegotiation", Policy::allow_server_initiated_renegotiation());
+ }
+
+bool Text_Policy::server_uses_own_ciphersuite_preferences() const
+ {
+ return get_bool("server_uses_own_ciphersuite_preferences", Policy::server_uses_own_ciphersuite_preferences());
+ }
+
+bool Text_Policy::negotiate_encrypt_then_mac() const
+ {
+ return get_bool("negotiate_encrypt_then_mac", Policy::negotiate_encrypt_then_mac());
+ }
+
+bool Text_Policy::support_cert_status_message() const
+ {
+ return get_bool("support_cert_status_message", Policy::support_cert_status_message());
+ }
+
+std::vector<Group_Params> Text_Policy::key_exchange_groups() const
+ {
+ std::string group_str = get_str("key_exchange_groups");
+
+ if(group_str.empty())
+ {
+ // fall back to previously used name
+ group_str = get_str("groups");
+ }
+
+ if(group_str.empty())
+ {
+ return Policy::key_exchange_groups();
+ }
+
+ std::vector<Group_Params> groups;
+ for(std::string group_name : split_on(group_str, ' '))
+ {
+ Group_Params group_id = group_param_from_string(group_name);
+
+ if(group_id == Group_Params::NONE)
+ {
+ try
+ {
+ size_t consumed = 0;
+ unsigned long ll_id = std::stoul(group_name, &consumed, 0);
+ if(consumed != group_name.size())
+ continue; // some other cruft
+
+ const uint16_t id = static_cast<uint16_t>(ll_id);
+
+ if(id != ll_id)
+ continue; // integer too large
+
+ group_id = static_cast<Group_Params>(id);
+ }
+ catch(...)
+ {
+ continue;
+ }
+ }
+
+ if(group_id != Group_Params::NONE)
+ groups.push_back(group_id);
+ }
+
+ return groups;
+ }
+
+size_t Text_Policy::minimum_ecdh_group_size() const
+ {
+ return get_len("minimum_ecdh_group_size", Policy::minimum_ecdh_group_size());
+ }
+
+size_t Text_Policy::minimum_ecdsa_group_size() const
+ {
+ return get_len("minimum_ecdsa_group_size", Policy::minimum_ecdsa_group_size());
+ }
+
+size_t Text_Policy::minimum_dh_group_size() const
+ {
+ return get_len("minimum_dh_group_size", Policy::minimum_dh_group_size());
+ }
+
+size_t Text_Policy::minimum_rsa_bits() const
+ {
+ return get_len("minimum_rsa_bits", Policy::minimum_rsa_bits());
+ }
+
+size_t Text_Policy::minimum_signature_strength() const
+ {
+ return get_len("minimum_signature_strength", Policy::minimum_signature_strength());
+ }
+
+size_t Text_Policy::dtls_default_mtu() const
+ {
+ return get_len("dtls_default_mtu", Policy::dtls_default_mtu());
+ }
+
+size_t Text_Policy::dtls_initial_timeout() const
+ {
+ return get_len("dtls_initial_timeout", Policy::dtls_initial_timeout());
+ }
+
+size_t Text_Policy::dtls_maximum_timeout() const
+ {
+ return get_len("dtls_maximum_timeout", Policy::dtls_maximum_timeout());
+ }
+
+bool Text_Policy::require_cert_revocation_info() const
+ {
+ return get_bool("require_cert_revocation_info", Policy::require_cert_revocation_info());
+ }
+
+bool Text_Policy::hide_unknown_users() const
+ {
+ return get_bool("hide_unknown_users", Policy::hide_unknown_users());
+ }
+
+uint32_t Text_Policy::session_ticket_lifetime() const
+ {
+ return static_cast<uint32_t>(get_len("session_ticket_lifetime", Policy::session_ticket_lifetime()));
+ }
+
+bool Text_Policy::send_fallback_scsv(Protocol_Version version) const
+ {
+ return get_bool("send_fallback_scsv", false) ? Policy::send_fallback_scsv(version) : false;
+ }
+
+std::vector<uint16_t> Text_Policy::srtp_profiles() const
+ {
+ std::vector<uint16_t> r;
+ for(std::string p : get_list("srtp_profiles", std::vector<std::string>()))
+ {
+ r.push_back(to_uint16(p));
+ }
+ return r;
+ }
+
+void Text_Policy::set(const std::string& k, const std::string& v)
+ {
+ m_kv[k] = v;
+ }
+
+Text_Policy::Text_Policy(const std::string& s)
+ {
+ std::istringstream iss(s);
+ m_kv = read_cfg(iss);
+ }
+
+Text_Policy::Text_Policy(std::istream& in) : m_kv(read_cfg(in))
+ {}
+
+std::vector<std::string>
+Text_Policy::get_list(const std::string& key,
+ const std::vector<std::string>& def) const
+ {
+ const std::string v = get_str(key);
+
+ if(v.empty())
+ {
+ return def;
+ }
+
+ return split_on(v, ' ');
+ }
+
+size_t Text_Policy::get_len(const std::string& key, size_t def) const
+ {
+ const std::string v = get_str(key);
+
+ if(v.empty())
+ {
+ return def;
+ }
+
+ return to_u32bit(v);
+ }
+
+bool Text_Policy::get_bool(const std::string& key, bool def) const
+ {
+ const std::string v = get_str(key);
+
+ if(v.empty())
+ {
+ return def;
+ }
+
+ if(v == "true" || v == "True")
+ {
+ return true;
+ }
+ else if(v == "false" || v == "False")
+ {
+ return false;
+ }
+ else
+ {
+ throw Decoding_Error("Invalid boolean '" + v + "'");
+ }
+ }
+
+std::string Text_Policy::get_str(const std::string& key, const std::string& def) const
+ {
+ auto i = m_kv.find(key);
+ if(i == m_kv.end())
+ {
+ return def;
+ }
+
+ return i->second;
+ }
+
+bool Text_Policy::set_value(const std::string& key, const std::string& val, bool overwrite)
+ {
+ auto i = m_kv.find(key);
+
+ if(overwrite == false && i != m_kv.end())
+ return false;
+
+ m_kv.insert(i, std::make_pair(key, val));
+ return true;
+ }
+
+}
+
+}
diff --git a/comm/third_party/botan/src/lib/tls/tls_version.cpp b/comm/third_party/botan/src/lib/tls/tls_version.cpp
new file mode 100644
index 0000000000..ecbe94897a
--- /dev/null
+++ b/comm/third_party/botan/src/lib/tls/tls_version.cpp
@@ -0,0 +1,88 @@
+/*
+* TLS Protocol Version Management
+* (C) 2012 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/tls_version.h>
+#include <botan/tls_exceptn.h>
+
+namespace Botan {
+
+namespace TLS {
+
+std::string Protocol_Version::to_string() const
+ {
+ const uint8_t maj = major_version();
+ const uint8_t min = minor_version();
+
+ if(maj == 3 && min == 0)
+ return "SSL v3";
+
+ if(maj == 3 && min >= 1) // TLS v1.x
+ return "TLS v1." + std::to_string(min-1);
+
+ if(maj == 254) // DTLS 1.x
+ return "DTLS v1." + std::to_string(255 - min);
+
+ // Some very new or very old protocol (or bogus data)
+ return "Unknown " + std::to_string(maj) + "." + std::to_string(min);
+ }
+
+bool Protocol_Version::is_datagram_protocol() const
+ {
+ return major_version() > 250;
+ }
+
+bool Protocol_Version::operator>(const Protocol_Version& other) const
+ {
+ if(this->is_datagram_protocol() != other.is_datagram_protocol())
+ throw TLS_Exception(Alert::PROTOCOL_VERSION,
+ "Version comparing " + to_string() +
+ " with " + other.to_string());
+
+ if(this->is_datagram_protocol())
+ return m_version < other.m_version; // goes backwards
+
+ return m_version > other.m_version;
+ }
+
+bool Protocol_Version::known_version() const
+ {
+ return (m_version == Protocol_Version::TLS_V10 ||
+ m_version == Protocol_Version::TLS_V11 ||
+ m_version == Protocol_Version::TLS_V12 ||
+ m_version == Protocol_Version::DTLS_V10 ||
+ m_version == Protocol_Version::DTLS_V12);
+ }
+
+bool Protocol_Version::supports_negotiable_signature_algorithms() const
+ {
+ return (m_version != Protocol_Version::TLS_V10 &&
+ m_version != Protocol_Version::TLS_V11 &&
+ m_version != Protocol_Version::DTLS_V10);
+ }
+
+bool Protocol_Version::supports_explicit_cbc_ivs() const
+ {
+ return (m_version != Protocol_Version::TLS_V10);
+ }
+
+bool Protocol_Version::supports_ciphersuite_specific_prf() const
+ {
+ return (m_version != Protocol_Version::TLS_V10 &&
+ m_version != Protocol_Version::TLS_V11 &&
+ m_version != Protocol_Version::DTLS_V10);
+ }
+
+bool Protocol_Version::supports_aead_modes() const
+ {
+ return (m_version != Protocol_Version::TLS_V10 &&
+ m_version != Protocol_Version::TLS_V11 &&
+ m_version != Protocol_Version::DTLS_V10);
+ }
+
+}
+
+}
diff --git a/comm/third_party/botan/src/lib/tls/tls_version.h b/comm/third_party/botan/src/lib/tls/tls_version.h
new file mode 100644
index 0000000000..18e2c0f65c
--- /dev/null
+++ b/comm/third_party/botan/src/lib/tls/tls_version.h
@@ -0,0 +1,156 @@
+/*
+* TLS Protocol Version Management
+* (C) 2012 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_TLS_PROTOCOL_VERSION_H_
+#define BOTAN_TLS_PROTOCOL_VERSION_H_
+
+#include <botan/types.h>
+#include <string>
+
+namespace Botan {
+
+namespace TLS {
+
+/**
+* TLS Protocol Version
+*/
+class BOTAN_PUBLIC_API(2,0) Protocol_Version final
+ {
+ public:
+ enum Version_Code {
+ TLS_V10 = 0x0301,
+ TLS_V11 = 0x0302,
+ TLS_V12 = 0x0303,
+
+ DTLS_V10 = 0xFEFF,
+ DTLS_V12 = 0xFEFD
+ };
+
+ /**
+ * @return latest known TLS version
+ */
+ static Protocol_Version latest_tls_version()
+ {
+ return Protocol_Version(TLS_V12);
+ }
+
+ /**
+ * @return latest known DTLS version
+ */
+ static Protocol_Version latest_dtls_version()
+ {
+ return Protocol_Version(DTLS_V12);
+ }
+
+ Protocol_Version() : m_version(0) {}
+
+ explicit Protocol_Version(uint16_t code) : m_version(code) {}
+
+ /**
+ * @param named_version a specific named version of the protocol
+ */
+ Protocol_Version(Version_Code named_version) :
+ Protocol_Version(static_cast<uint16_t>(named_version)) {}
+
+ /**
+ * @param major the major version
+ * @param minor the minor version
+ */
+ Protocol_Version(uint8_t major, uint8_t minor) :
+ Protocol_Version(static_cast<uint16_t>((static_cast<uint16_t>(major) << 8) | minor)) {}
+
+ /**
+ * @return true if this is a valid protocol version
+ */
+ bool valid() const { return (m_version != 0); }
+
+ /**
+ * @return true if this is a protocol version we know about
+ */
+ bool known_version() const;
+
+ /**
+ * @return major version of the protocol version
+ */
+ uint8_t major_version() const { return static_cast<uint8_t>(m_version >> 8); }
+
+ /**
+ * @return minor version of the protocol version
+ */
+ uint8_t minor_version() const { return static_cast<uint8_t>(m_version & 0xFF); }
+
+ /**
+ * @return the version code
+ */
+ uint16_t version_code() const { return m_version; }
+
+ /**
+ * @return human-readable description of this version
+ */
+ std::string to_string() const;
+
+ /**
+ * @return true iff this is a DTLS version
+ */
+ bool is_datagram_protocol() const;
+
+ /**
+ * @return true if this version supports negotiable signature algorithms
+ */
+ bool supports_negotiable_signature_algorithms() const;
+
+ /**
+ * @return true if this version uses explicit IVs for block ciphers
+ */
+ bool supports_explicit_cbc_ivs() const;
+
+ /**
+ * @return true if this version uses a ciphersuite specific PRF
+ */
+ bool supports_ciphersuite_specific_prf() const;
+
+ bool supports_aead_modes() const;
+
+ /**
+ * @return if this version is equal to other
+ */
+ bool operator==(const Protocol_Version& other) const
+ {
+ return (m_version == other.m_version);
+ }
+
+ /**
+ * @return if this version is not equal to other
+ */
+ bool operator!=(const Protocol_Version& other) const
+ {
+ return (m_version != other.m_version);
+ }
+
+ /**
+ * @return if this version is later than other
+ */
+ bool operator>(const Protocol_Version& other) const;
+
+ /**
+ * @return if this version is later than or equal to other
+ */
+ bool operator>=(const Protocol_Version& other) const
+ {
+ return (*this == other || *this > other);
+ }
+
+ private:
+ uint16_t m_version;
+ };
+
+}
+
+}
+
+#endif
+
diff --git a/comm/third_party/botan/src/lib/utils/assert.cpp b/comm/third_party/botan/src/lib/utils/assert.cpp
new file mode 100644
index 0000000000..b251a469e9
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/assert.cpp
@@ -0,0 +1,54 @@
+/*
+* Runtime assertion checking
+* (C) 2010,2012,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/exceptn.h>
+#include <sstream>
+
+namespace Botan {
+
+void throw_invalid_argument(const char* message,
+ const char* func,
+ const char* file)
+ {
+ std::ostringstream format;
+ format << message << " in " << func << ":" << file;
+ throw Invalid_Argument(format.str());
+ }
+
+void throw_invalid_state(const char* expr,
+ const char* func,
+ const char* file)
+ {
+ std::ostringstream format;
+ format << "Invalid state: " << expr << " was false in " << func << ":" << file;
+ throw Invalid_State(format.str());
+ }
+
+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)
+ format << "'" << assertion_made << "' (expression " << expr_str << ") ";
+ else
+ format << expr_str << " ";
+
+ if(func)
+ format << "in " << func << " ";
+
+ format << "@" << file << ":" << line;
+
+ throw Internal_Error(format.str());
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/utils/assert.h b/comm/third_party/botan/src/lib/utils/assert.h
new file mode 100644
index 0000000000..14cc442609
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/assert.h
@@ -0,0 +1,157 @@
+/*
+* Runtime assertion checking
+* (C) 2010,2018 Jack Lloyd
+* 2017 Simon Warta (Kullo GmbH)
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_ASSERTION_CHECKING_H_
+#define BOTAN_ASSERTION_CHECKING_H_
+
+#include <botan/build.h>
+#include <botan/compiler.h>
+
+namespace Botan {
+
+/**
+* Called when an assertion fails
+* Throws an Exception object
+*/
+BOTAN_NORETURN void BOTAN_PUBLIC_API(2,0)
+ assertion_failure(const char* expr_str,
+ const char* assertion_made,
+ const char* func,
+ const char* file,
+ int line);
+
+/**
+* Called when an invalid argument is used
+* Throws Invalid_Argument
+*/
+BOTAN_NORETURN void BOTAN_UNSTABLE_API throw_invalid_argument(const char* message,
+ const char* func,
+ const char* file);
+
+
+#define BOTAN_ARG_CHECK(expr, msg) \
+ do { if(!(expr)) Botan::throw_invalid_argument(msg, __func__, __FILE__); } while(0)
+
+/**
+* Called when an invalid state is encountered
+* Throws Invalid_State
+*/
+BOTAN_NORETURN void BOTAN_UNSTABLE_API throw_invalid_state(const char* message,
+ const char* func,
+ const char* file);
+
+
+#define BOTAN_STATE_CHECK(expr) \
+ do { if(!(expr)) Botan::throw_invalid_state(#expr, __func__, __FILE__); } while(0)
+
+/**
+* Make an assertion
+*/
+#define BOTAN_ASSERT(expr, assertion_made) \
+ do { \
+ if(!(expr)) \
+ Botan::assertion_failure(#expr, \
+ assertion_made, \
+ __func__, \
+ __FILE__, \
+ __LINE__); \
+ } while(0)
+
+/**
+* Make an assertion
+*/
+#define BOTAN_ASSERT_NOMSG(expr) \
+ do { \
+ if(!(expr)) \
+ Botan::assertion_failure(#expr, \
+ "", \
+ __func__, \
+ __FILE__, \
+ __LINE__); \
+ } while(0)
+
+/**
+* Assert that value1 == value2
+*/
+#define BOTAN_ASSERT_EQUAL(expr1, expr2, assertion_made) \
+ do { \
+ if((expr1) != (expr2)) \
+ 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) \
+ do { \
+ if((expr1) && !(expr2)) \
+ Botan::assertion_failure(#expr1 " implies " #expr2, \
+ msg, \
+ __func__, \
+ __FILE__, \
+ __LINE__); \
+ } while(0)
+
+/**
+* Assert that a pointer is not null
+*/
+#define BOTAN_ASSERT_NONNULL(ptr) \
+ do { \
+ if((ptr) == nullptr) \
+ Botan::assertion_failure(#ptr " is not null", \
+ "", \
+ __func__, \
+ __FILE__, \
+ __LINE__); \
+ } while(0)
+
+#if defined(BOTAN_ENABLE_DEBUG_ASSERTS)
+
+#define BOTAN_DEBUG_ASSERT(expr) BOTAN_ASSERT_NOMSG(expr)
+
+#else
+
+#define BOTAN_DEBUG_ASSERT(expr) do {} while(0)
+
+#endif
+
+/**
+* Mark variable as unused. Takes between 1 and 9 arguments and marks all as unused,
+* e.g. BOTAN_UNUSED(a); or BOTAN_UNUSED(x, y, z);
+*/
+#define _BOTAN_UNUSED_IMPL1(a) static_cast<void>(a)
+#define _BOTAN_UNUSED_IMPL2(a, b) static_cast<void>(a); _BOTAN_UNUSED_IMPL1(b)
+#define _BOTAN_UNUSED_IMPL3(a, b, c) static_cast<void>(a); _BOTAN_UNUSED_IMPL2(b, c)
+#define _BOTAN_UNUSED_IMPL4(a, b, c, d) static_cast<void>(a); _BOTAN_UNUSED_IMPL3(b, c, d)
+#define _BOTAN_UNUSED_IMPL5(a, b, c, d, e) static_cast<void>(a); _BOTAN_UNUSED_IMPL4(b, c, d, e)
+#define _BOTAN_UNUSED_IMPL6(a, b, c, d, e, f) static_cast<void>(a); _BOTAN_UNUSED_IMPL5(b, c, d, e, f)
+#define _BOTAN_UNUSED_IMPL7(a, b, c, d, e, f, g) static_cast<void>(a); _BOTAN_UNUSED_IMPL6(b, c, d, e, f, g)
+#define _BOTAN_UNUSED_IMPL8(a, b, c, d, e, f, g, h) static_cast<void>(a); _BOTAN_UNUSED_IMPL7(b, c, d, e, f, g, h)
+#define _BOTAN_UNUSED_IMPL9(a, b, c, d, e, f, g, h, i) static_cast<void>(a); _BOTAN_UNUSED_IMPL8(b, c, d, e, f, g, h, i)
+#define _BOTAN_UNUSED_GET_IMPL(_1, _2, _3, _4, _5, _6, _7, _8, _9, IMPL_NAME, ...) IMPL_NAME
+
+#define BOTAN_UNUSED(...) _BOTAN_UNUSED_GET_IMPL(__VA_ARGS__, \
+ _BOTAN_UNUSED_IMPL9, \
+ _BOTAN_UNUSED_IMPL8, \
+ _BOTAN_UNUSED_IMPL7, \
+ _BOTAN_UNUSED_IMPL6, \
+ _BOTAN_UNUSED_IMPL5, \
+ _BOTAN_UNUSED_IMPL4, \
+ _BOTAN_UNUSED_IMPL3, \
+ _BOTAN_UNUSED_IMPL2, \
+ _BOTAN_UNUSED_IMPL1, \
+ unused dummy rest value \
+ ) /* we got an one of _BOTAN_UNUSED_IMPL*, now call it */ (__VA_ARGS__)
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/utils/bit_ops.h b/comm/third_party/botan/src/lib/utils/bit_ops.h
new file mode 100644
index 0000000000..79d86f0f76
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/bit_ops.h
@@ -0,0 +1,171 @@
+/*
+* Bit/Word Operations
+* (C) 1999-2008 Jack Lloyd
+* (C) Copyright Projet SECRET, INRIA, Rocquencourt
+* (C) Bhaskar Biswas and Nicolas Sendrier
+* (C) 2014 cryptosource GmbH
+* (C) 2014 Falko Strenzke fstrenzke@cryptosource.de
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_BIT_OPS_H_
+#define BOTAN_BIT_OPS_H_
+
+#include <botan/types.h>
+
+namespace Botan {
+
+/**
+* If top bit of arg is set, return ~0. Otherwise return 0.
+*/
+template<typename T>
+inline T expand_top_bit(T a)
+ {
+ return static_cast<T>(0) - (a >> (sizeof(T)*8-1));
+ }
+
+/**
+* If arg is zero, return ~0. Otherwise return 0
+*/
+template<typename T>
+inline T ct_is_zero(T x)
+ {
+ return expand_top_bit<T>(~x & (x - 1));
+ }
+
+/**
+* Power of 2 test. T should be an unsigned integer type
+* @param arg an integer value
+* @return true iff arg is 2^n for some n > 0
+*/
+template<typename T>
+inline constexpr bool is_power_of_2(T arg)
+ {
+ return (arg != 0) && (arg != 1) && ((arg & static_cast<T>(arg-1)) == 0);
+ }
+
+/**
+* Return the index of the highest set bit
+* T is an unsigned integer type
+* @param n an integer value
+* @return index of the highest set bit in n
+*/
+template<typename T>
+inline 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);
+ hb += z;
+ n >>= z;
+ }
+
+ hb += n;
+
+ return hb;
+ }
+
+/**
+* Return the number of significant bytes in n
+* @param n an integer value
+* @return number of significant bytes in n
+*/
+template<typename T>
+inline 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);
+ b += z/8;
+ n >>= z;
+ }
+
+ b += (n != 0);
+
+ return b;
+ }
+
+/**
+* Count the trailing zero bits in n
+* @param n an integer value
+* @return maximum x st 2^x divides n
+*/
+template<typename T>
+inline 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;
+
+ for(size_t s = 8*sizeof(T) / 2; s > 0; s /= 2)
+ {
+ const T mask = (static_cast<T>(1) << s) - 1;
+ const size_t z = s * (ct_is_zero(n & mask) & 1);
+ lb += z;
+ n >>= z;
+ }
+
+ return lb;
+ }
+
+template<typename T>
+uint8_t ceil_log2(T x)
+ {
+ static_assert(sizeof(T) < 32, "Abnormally large scalar");
+
+ if(x >> (sizeof(T)*8-1))
+ return sizeof(T)*8;
+
+ uint8_t result = 0;
+ T compare = 1;
+
+ while(compare < x)
+ {
+ compare <<= 1;
+ result++;
+ }
+
+ return result;
+ }
+
+// Potentially variable time ctz used for OCB
+inline size_t var_ctz32(uint32_t n)
+ {
+#if defined(BOTAN_BUILD_COMPILER_IS_GCC) || defined(BOTAN_BUILD_COMPILER_IS_CLANG)
+ if(n == 0)
+ return 32;
+ return __builtin_ctz(n);
+#else
+ return ctz<uint32_t>(n);
+#endif
+ }
+
+template<typename T>
+inline 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
+ */
+ const T swap = ((x >> shift) ^ x) & mask;
+ return (x ^ swap) ^ (swap << shift);
+ }
+
+template<typename T>
+inline void swap_bits(T& x, T& y, T mask, size_t shift)
+ {
+ const T swap = ((x >> shift) ^ y) & mask;
+ x ^= swap << shift;
+ y ^= swap;
+ }
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/utils/boost/info.txt b/comm/third_party/botan/src/lib/utils/boost/info.txt
new file mode 100644
index 0000000000..6330cbafdb
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/boost/info.txt
@@ -0,0 +1,9 @@
+<defines>
+BOOST_ASIO -> 20131228
+</defines>
+
+load_on vendor
+
+<libs>
+all -> boost_system
+</libs>
diff --git a/comm/third_party/botan/src/lib/utils/bswap.h b/comm/third_party/botan/src/lib/utils/bswap.h
new file mode 100644
index 0000000000..584fa33234
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/bswap.h
@@ -0,0 +1,108 @@
+/*
+* Byte Swapping Operations
+* (C) 1999-2011,2018 Jack Lloyd
+* (C) 2007 Yves Jerschow
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_BYTE_SWAP_H_
+#define BOTAN_BYTE_SWAP_H_
+
+#include <botan/types.h>
+
+#if defined(BOTAN_BUILD_COMPILER_IS_MSVC)
+ #include <stdlib.h>
+#endif
+
+BOTAN_FUTURE_INTERNAL_HEADER(bswap.h)
+
+namespace Botan {
+
+/**
+* Swap a 16 bit integer
+*/
+inline uint16_t reverse_bytes(uint16_t val)
+ {
+#if defined(BOTAN_BUILD_COMPILER_IS_GCC) || defined(BOTAN_BUILD_COMPILER_IS_CLANG) || defined(BOTAN_BUILD_COMPILER_IS_XLC)
+ return __builtin_bswap16(val);
+#else
+ return static_cast<uint16_t>((val << 8) | (val >> 8));
+#endif
+ }
+
+/**
+* Swap a 32 bit integer
+*/
+inline uint32_t reverse_bytes(uint32_t val)
+ {
+#if defined(BOTAN_BUILD_COMPILER_IS_GCC) || defined(BOTAN_BUILD_COMPILER_IS_CLANG) || defined(BOTAN_BUILD_COMPILER_IS_XLC)
+ return __builtin_bswap32(val);
+
+#elif defined(BOTAN_BUILD_COMPILER_IS_MSVC)
+ return _byteswap_ulong(val);
+
+#elif defined(BOTAN_USE_GCC_INLINE_ASM) && defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
+
+ // GCC-style inline assembly for x86 or x86-64
+ asm("bswapl %0" : "=r" (val) : "0" (val));
+ return val;
+
+#else
+ // Generic implementation
+ uint16_t hi = static_cast<uint16_t>(val >> 16);
+ uint16_t lo = static_cast<uint16_t>(val);
+
+ hi = reverse_bytes(hi);
+ lo = reverse_bytes(lo);
+
+ return (static_cast<uint32_t>(lo) << 16) | hi;
+#endif
+ }
+
+/**
+* Swap a 64 bit integer
+*/
+inline uint64_t reverse_bytes(uint64_t val)
+ {
+#if defined(BOTAN_BUILD_COMPILER_IS_GCC) || defined(BOTAN_BUILD_COMPILER_IS_CLANG) || defined(BOTAN_BUILD_COMPILER_IS_XLC)
+ return __builtin_bswap64(val);
+
+#elif defined(BOTAN_BUILD_COMPILER_IS_MSVC)
+ return _byteswap_uint64(val);
+
+#elif defined(BOTAN_USE_GCC_INLINE_ASM) && defined(BOTAN_TARGET_ARCH_IS_X86_64)
+ // GCC-style inline assembly for x86-64
+ asm("bswapq %0" : "=r" (val) : "0" (val));
+ return val;
+
+#else
+ /* Generic implementation. Defined in terms of 32-bit bswap so any
+ * optimizations in that version can help.
+ */
+
+ uint32_t hi = static_cast<uint32_t>(val >> 32);
+ uint32_t lo = static_cast<uint32_t>(val);
+
+ hi = reverse_bytes(hi);
+ lo = reverse_bytes(lo);
+
+ return (static_cast<uint64_t>(lo) << 32) | hi;
+#endif
+ }
+
+/**
+* Swap 4 Ts in an array
+*/
+template<typename T>
+inline void bswap_4(T x[4])
+ {
+ x[0] = reverse_bytes(x[0]);
+ x[1] = reverse_bytes(x[1]);
+ x[2] = reverse_bytes(x[2]);
+ x[3] = reverse_bytes(x[3]);
+ }
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/utils/calendar.cpp b/comm/third_party/botan/src/lib/utils/calendar.cpp
new file mode 100644
index 0000000000..1c34a71572
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/calendar.cpp
@@ -0,0 +1,124 @@
+/*
+* Calendar Functions
+* (C) 1999-2010,2017 Jack Lloyd
+* (C) 2015 Simon Warta (Kullo GmbH)
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/calendar.h>
+#include <botan/exceptn.h>
+#include <ctime>
+#include <sstream>
+#include <iomanip>
+#include <stdlib.h>
+
+namespace Botan {
+
+namespace {
+
+std::tm do_gmtime(std::time_t time_val)
+ {
+ 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
+#else
+ std::tm* tm_p = std::gmtime(&time_val);
+ if (tm_p == nullptr)
+ throw Encoding_Error("time_t_to_tm could not convert");
+ tm = *tm_p;
+#endif
+
+ return tm;
+ }
+
+/*
+Portable replacement for timegm, _mkgmtime, etc
+
+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
+that the date is at least 1970, which is sufficient for our purposes.
+*/
+size_t days_since_epoch(uint32_t year, uint32_t month, uint32_t day)
+ {
+ if(month <= 2)
+ year -= 1;
+ const uint32_t era = year / 400;
+ const uint32_t yoe = year - era * 400; // [0, 399]
+ const uint32_t doy = (153*(month + (month > 2 ? -3 : 9)) + 2)/5 + day-1; // [0, 365]
+ const uint32_t doe = yoe * 365 + yoe/4 - yoe/100 + doy; // [0, 146096]
+ return era * 146097 + doe - 719468;
+ }
+
+}
+
+std::chrono::system_clock::time_point calendar_point::to_std_timepoint() const
+ {
+ if(get_year() < 1970)
+ throw Invalid_Argument("calendar_point::to_std_timepoint() does not support years before 1970");
+
+ // 32 bit time_t ends at January 19, 2038
+ // https://msdn.microsoft.com/en-us/library/2093ets1.aspx
+ // Throw after 2037 if 32 bit time_t is used
+
+ BOTAN_IF_CONSTEXPR(sizeof(std::time_t) == 4)
+ {
+ if(get_year() > 2037)
+ {
+ throw Invalid_Argument("calendar_point::to_std_timepoint() does not support years after 2037 on this system");
+ }
+ }
+
+ // This upper bound is completely arbitrary
+ if(get_year() >= 2400)
+ {
+ throw Invalid_Argument("calendar_point::to_std_timepoint() does not support years after 2400");
+ }
+
+ const uint64_t seconds_64 = (days_since_epoch(get_year(), get_month(), get_day()) * 86400) +
+ (get_hour() * 60 * 60) + (get_minutes() * 60) + get_seconds();
+
+ const time_t seconds_time_t = static_cast<time_t>(seconds_64);
+
+ if(seconds_64 - seconds_time_t != 0)
+ {
+ throw Invalid_Argument("calendar_point::to_std_timepoint time_t overflow");
+ }
+
+ return std::chrono::system_clock::from_time_t(seconds_time_t);
+ }
+
+std::string calendar_point::to_string() const
+ {
+ // desired format: <YYYY>-<MM>-<dd>T<HH>:<mm>:<ss>
+ std::stringstream output;
+ output << std::setfill('0')
+ << std::setw(4) << get_year() << "-"
+ << std::setw(2) << get_month() << "-"
+ << std::setw(2) << get_day() << "T"
+ << std::setw(2) << get_hour() << ":"
+ << std::setw(2) << get_minutes() << ":"
+ << std::setw(2) << get_seconds();
+ return output.str();
+ }
+
+
+calendar_point calendar_value(
+ const std::chrono::system_clock::time_point& time_point)
+ {
+ std::tm tm = do_gmtime(std::chrono::system_clock::to_time_t(time_point));
+
+ return calendar_point(tm.tm_year + 1900,
+ tm.tm_mon + 1,
+ tm.tm_mday,
+ tm.tm_hour,
+ tm.tm_min,
+ tm.tm_sec);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/utils/calendar.h b/comm/third_party/botan/src/lib/utils/calendar.h
new file mode 100644
index 0000000000..83759070b8
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/calendar.h
@@ -0,0 +1,91 @@
+/*
+* Calendar Functions
+* (C) 1999-2009,2015 Jack Lloyd
+* (C) 2015 Simon Warta (Kullo GmbH)
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_CALENDAR_H_
+#define BOTAN_CALENDAR_H_
+
+#include <botan/types.h>
+#include <chrono>
+#include <string>
+
+namespace Botan {
+
+/**
+* Struct representing a particular date and time
+*/
+class BOTAN_PUBLIC_API(2,0) calendar_point
+ {
+ public:
+
+ /** The year */
+ uint32_t get_year() const { return year; }
+
+ /** The month, 1 through 12 for Jan to Dec */
+ uint32_t get_month() const { return month; }
+
+ /** The day of the month, 1 through 31 (or 28 or 30 based on month */
+ uint32_t get_day() const { return day; }
+
+ /** Hour in 24-hour form, 0 to 23 */
+ uint32_t get_hour() const { return hour; }
+
+ /** Minutes in the hour, 0 to 60 */
+ uint32_t get_minutes() const { return minutes; }
+
+ /** Seconds in the minute, 0 to 60, but might be slightly
+ larger to deal with leap seconds on some systems
+ */
+ uint32_t get_seconds() const { return seconds; }
+
+ /**
+ * Initialize a calendar_point
+ * @param y the year
+ * @param mon the month
+ * @param d the day
+ * @param h the hour
+ * @param min the minute
+ * @param sec the second
+ */
+ calendar_point(uint32_t y, uint32_t mon, uint32_t d, uint32_t h, uint32_t min, uint32_t sec) :
+ year(y), month(mon), day(d), hour(h), minutes(min), seconds(sec) {}
+
+ /**
+ * Returns an STL timepoint object
+ */
+ std::chrono::system_clock::time_point to_std_timepoint() const;
+
+ /**
+ * Returns a human readable string of the struct's components.
+ * Formatting might change over time. Currently it is RFC339 'iso-date-time'.
+ */
+ std::string to_string() const;
+
+ BOTAN_DEPRECATED_PUBLIC_MEMBER_VARIABLES:
+ /*
+ The member variables are public for historical reasons. Use the get_xxx() functions
+ defined above. These members will be made private in a future major release.
+ */
+ uint32_t year;
+ uint32_t month;
+ uint32_t day;
+ uint32_t hour;
+ uint32_t minutes;
+ uint32_t seconds;
+ };
+
+/**
+* Convert a time_point to a calendar_point
+* @param time_point a time point from the system clock
+* @return calendar_point object representing this time point
+*/
+BOTAN_PUBLIC_API(2,0) calendar_point calendar_value(
+ const std::chrono::system_clock::time_point& time_point);
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/utils/charset.cpp b/comm/third_party/botan/src/lib/utils/charset.cpp
new file mode 100644
index 0000000000..ca32c652db
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/charset.cpp
@@ -0,0 +1,283 @@
+/*
+* Character Set Handling
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/charset.h>
+#include <botan/exceptn.h>
+#include <botan/loadstor.h>
+#include <cctype>
+
+namespace Botan {
+
+namespace {
+
+void append_utf8_for(std::string& s, uint32_t c)
+ {
+ if(c >= 0xD800 && c < 0xE000)
+ throw Decoding_Error("Invalid Unicode character");
+
+ if(c <= 0x7F)
+ {
+ const uint8_t b0 = static_cast<uint8_t>(c);
+ s.push_back(static_cast<char>(b0));
+ }
+ else if(c <= 0x7FF)
+ {
+ const uint8_t b0 = 0xC0 | static_cast<uint8_t>(c >> 6);
+ const uint8_t b1 = 0x80 | static_cast<uint8_t>(c & 0x3F);
+ s.push_back(static_cast<char>(b0));
+ s.push_back(static_cast<char>(b1));
+ }
+ else if(c <= 0xFFFF)
+ {
+ const uint8_t b0 = 0xE0 | static_cast<uint8_t>(c >> 12);
+ const uint8_t b1 = 0x80 | static_cast<uint8_t>((c >> 6) & 0x3F);
+ const uint8_t b2 = 0x80 | static_cast<uint8_t>(c & 0x3F);
+ s.push_back(static_cast<char>(b0));
+ s.push_back(static_cast<char>(b1));
+ s.push_back(static_cast<char>(b2));
+ }
+ else if(c <= 0x10FFFF)
+ {
+ const uint8_t b0 = 0xF0 | static_cast<uint8_t>(c >> 18);
+ const uint8_t b1 = 0x80 | static_cast<uint8_t>((c >> 12) & 0x3F);
+ const uint8_t b2 = 0x80 | static_cast<uint8_t>((c >> 6) & 0x3F);
+ const uint8_t b3 = 0x80 | static_cast<uint8_t>(c & 0x3F);
+ s.push_back(static_cast<char>(b0));
+ s.push_back(static_cast<char>(b1));
+ s.push_back(static_cast<char>(b2));
+ s.push_back(static_cast<char>(b3));
+ }
+ else
+ throw Decoding_Error("Invalid Unicode character");
+
+ }
+
+}
+
+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");
+
+ const size_t chars = len / 2;
+
+ std::string s;
+ for(size_t i = 0; i != chars; ++i)
+ {
+ const uint16_t c = load_be<uint16_t>(ucs2, i);
+ append_utf8_for(s, c);
+ }
+
+ return s;
+ }
+
+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");
+
+ const size_t chars = len / 4;
+
+ std::string s;
+ for(size_t i = 0; i != chars; ++i)
+ {
+ const uint32_t c = load_be<uint32_t>(ucs4, i);
+ append_utf8_for(s, c);
+ }
+
+ return s;
+ }
+
+/*
+* Convert from UTF-8 to ISO 8859-1
+*/
+std::string utf8_to_latin1(const std::string& utf8)
+ {
+ std::string iso8859;
+
+ size_t position = 0;
+ while(position != utf8.size())
+ {
+ const uint8_t c1 = static_cast<uint8_t>(utf8[position++]);
+
+ if(c1 <= 0x7F)
+ {
+ iso8859 += static_cast<char>(c1);
+ }
+ else if(c1 >= 0xC0 && c1 <= 0xC7)
+ {
+ if(position == utf8.size())
+ throw Decoding_Error("UTF-8: sequence truncated");
+
+ const uint8_t c2 = static_cast<uint8_t>(utf8[position++]);
+ const uint8_t iso_char = ((c1 & 0x07) << 6) | (c2 & 0x3F);
+
+ if(iso_char <= 0x7F)
+ throw Decoding_Error("UTF-8: sequence longer than needed");
+
+ iso8859 += static_cast<char>(iso_char);
+ }
+ else
+ throw Decoding_Error("UTF-8: Unicode chars not in Latin1 used");
+ }
+
+ return iso8859;
+ }
+
+namespace Charset {
+
+namespace {
+
+/*
+* Convert from UCS-2 to ISO 8859-1
+*/
+std::string ucs2_to_latin1(const std::string& ucs2)
+ {
+ if(ucs2.size() % 2 == 1)
+ throw Decoding_Error("UCS-2 string has an odd number of bytes");
+
+ std::string latin1;
+
+ for(size_t i = 0; i != ucs2.size(); i += 2)
+ {
+ const uint8_t c1 = ucs2[i];
+ const uint8_t c2 = ucs2[i+1];
+
+ if(c1 != 0)
+ throw Decoding_Error("UCS-2 has non-Latin1 characters");
+
+ latin1 += static_cast<char>(c2);
+ }
+
+ return latin1;
+ }
+
+/*
+* Convert from ISO 8859-1 to UTF-8
+*/
+std::string latin1_to_utf8(const std::string& iso8859)
+ {
+ std::string utf8;
+ for(size_t i = 0; i != iso8859.size(); ++i)
+ {
+ const uint8_t c = static_cast<uint8_t>(iso8859[i]);
+
+ if(c <= 0x7F)
+ utf8 += static_cast<char>(c);
+ else
+ {
+ utf8 += static_cast<char>((0xC0 | (c >> 6)));
+ utf8 += static_cast<char>((0x80 | (c & 0x3F)));
+ }
+ }
+ return utf8;
+ }
+
+}
+
+/*
+* Perform character set transcoding
+*/
+std::string transcode(const std::string& str,
+ Character_Set to, Character_Set from)
+ {
+ if(to == LOCAL_CHARSET)
+ to = LATIN1_CHARSET;
+ if(from == LOCAL_CHARSET)
+ from = LATIN1_CHARSET;
+
+ if(to == from)
+ return str;
+
+ if(from == LATIN1_CHARSET && to == UTF8_CHARSET)
+ return latin1_to_utf8(str);
+ if(from == UTF8_CHARSET && to == LATIN1_CHARSET)
+ return utf8_to_latin1(str);
+ if(from == UCS2_CHARSET && to == LATIN1_CHARSET)
+ return ucs2_to_latin1(str);
+
+ throw Invalid_Argument("Unknown transcoding operation from " +
+ std::to_string(from) + " to " + std::to_string(to));
+ }
+
+/*
+* Check if a character represents a digit
+*/
+bool is_digit(char c)
+ {
+ if(c == '0' || c == '1' || c == '2' || c == '3' || c == '4' ||
+ c == '5' || c == '6' || c == '7' || c == '8' || c == '9')
+ return true;
+ return false;
+ }
+
+/*
+* Check if a character represents whitespace
+*/
+bool is_space(char c)
+ {
+ if(c == ' ' || c == '\t' || c == '\n' || c == '\r')
+ return true;
+ return false;
+ }
+
+/*
+* Convert a character to a digit
+*/
+uint8_t char2digit(char c)
+ {
+ switch(c)
+ {
+ case '0': return 0;
+ case '1': return 1;
+ case '2': return 2;
+ case '3': return 3;
+ case '4': return 4;
+ case '5': return 5;
+ case '6': return 6;
+ case '7': return 7;
+ case '8': return 8;
+ case '9': return 9;
+ }
+
+ throw Invalid_Argument("char2digit: Input is not a digit character");
+ }
+
+/*
+* Convert a digit to a character
+*/
+char digit2char(uint8_t b)
+ {
+ switch(b)
+ {
+ case 0: return '0';
+ case 1: return '1';
+ case 2: return '2';
+ case 3: return '3';
+ case 4: return '4';
+ case 5: return '5';
+ case 6: return '6';
+ case 7: return '7';
+ case 8: return '8';
+ case 9: return '9';
+ }
+
+ throw Invalid_Argument("digit2char: Input is not a digit");
+ }
+
+/*
+* Case-insensitive character comparison
+*/
+bool caseless_cmp(char a, char b)
+ {
+ return (std::tolower(static_cast<unsigned char>(a)) ==
+ std::tolower(static_cast<unsigned char>(b)));
+ }
+
+}
+
+}
diff --git a/comm/third_party/botan/src/lib/utils/charset.h b/comm/third_party/botan/src/lib/utils/charset.h
new file mode 100644
index 0000000000..6e7ce30c91
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/charset.h
@@ -0,0 +1,80 @@
+/*
+* Character Set Handling
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_CHARSET_H_
+#define BOTAN_CHARSET_H_
+
+#include <botan/types.h>
+#include <string>
+
+BOTAN_FUTURE_INTERNAL_HEADER(charset.h)
+
+namespace Botan {
+
+/**
+* Convert a sequence of UCS-2 (big endian) characters to a UTF-8 string
+* This is used for ASN.1 BMPString type
+* @param ucs2 the sequence of UCS-2 characters
+* @param len length of ucs2 in bytes, must be a multiple of 2
+*/
+std::string BOTAN_UNSTABLE_API ucs2_to_utf8(const uint8_t ucs2[], size_t len);
+
+/**
+* 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
+* @param len length of ucs4 in bytes, must be a multiple of 4
+*/
+std::string BOTAN_UNSTABLE_API ucs4_to_utf8(const uint8_t ucs4[], size_t len);
+
+/**
+* Convert a UTF-8 string to Latin-1
+* If a character outside the Latin-1 range is encountered, an exception is thrown.
+*/
+std::string BOTAN_UNSTABLE_API utf8_to_latin1(const std::string& utf8);
+
+/**
+* The different charsets (nominally) supported by Botan.
+*/
+enum Character_Set {
+ LOCAL_CHARSET,
+ UCS2_CHARSET,
+ UTF8_CHARSET,
+ LATIN1_CHARSET
+};
+
+namespace Charset {
+
+/*
+* Character set conversion - avoid this.
+* For specific conversions, use the functions above like
+* ucs2_to_utf8 and utf8_to_latin1
+*
+* If you need something more complex than that, use a real library
+* such as iconv, Boost.Locale, or ICU
+*/
+std::string BOTAN_PUBLIC_API(2,0)
+ BOTAN_DEPRECATED("Avoid. See comment in header.")
+ transcode(const std::string& str,
+ Character_Set to,
+ Character_Set from);
+
+/*
+* Simple character classifier functions
+*/
+bool BOTAN_PUBLIC_API(2,0) is_digit(char c);
+bool BOTAN_PUBLIC_API(2,0) is_space(char c);
+bool BOTAN_PUBLIC_API(2,0) caseless_cmp(char x, char y);
+
+uint8_t BOTAN_PUBLIC_API(2,0) char2digit(char c);
+char BOTAN_PUBLIC_API(2,0) digit2char(uint8_t b);
+
+}
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/utils/codec_base.h b/comm/third_party/botan/src/lib/utils/codec_base.h
new file mode 100644
index 0000000000..2338fdfd7e
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/codec_base.h
@@ -0,0 +1,220 @@
+/*
+* Base Encoding and Decoding
+* (C) 2018 Erwan Chaussy
+* (C) 2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_BASE_CODEC_H_
+#define BOTAN_BASE_CODEC_H_
+
+#include <botan/secmem.h>
+#include <botan/exceptn.h>
+#include <vector>
+#include <string>
+
+namespace Botan {
+
+/**
+* Perform encoding using the base provided
+* @param base object giving access to the encodings specifications
+* @param output an array of at least base.encode_max_output bytes
+* @param input is some binary data
+* @param input_length length of input in bytes
+* @param input_consumed is an output parameter which says how many
+* bytes of input were actually consumed. If less than
+* input_length, then the range input[consumed:length]
+* should be passed in later along with more input.
+* @param final_inputs true iff this is the last input, in which case
+ padding chars will be applied if needed
+* @return number of bytes written to output
+*/
+template <class Base>
+size_t base_encode(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();
+
+ size_t input_remaining = input_length;
+ size_t output_produced = 0;
+
+ while(input_remaining >= encoding_bytes_in)
+ {
+ base.encode(output + output_produced, input + input_consumed);
+
+ input_consumed += encoding_bytes_in;
+ output_produced += encoding_bytes_out;
+ input_remaining -= encoding_bytes_in;
+ }
+
+ if(final_inputs && input_remaining)
+ {
+ std::vector<uint8_t> remainder(encoding_bytes_in, 0);
+ for(size_t i = 0; i != input_remaining; ++i)
+ { remainder[i] = input[input_consumed + i]; }
+
+ base.encode(output + output_produced, remainder.data());
+
+ const size_t bits_consumed = base.bits_consumed();
+ const size_t remaining_bits_before_padding = base.remaining_bits_before_padding();
+
+ size_t empty_bits = 8 * (encoding_bytes_in - input_remaining);
+ size_t index = output_produced + encoding_bytes_out - 1;
+ while(empty_bits >= remaining_bits_before_padding)
+ {
+ output[index--] = '=';
+ empty_bits -= bits_consumed;
+ }
+
+ input_consumed += input_remaining;
+ output_produced += encoding_bytes_out;
+ }
+
+ return output_produced;
+ }
+
+
+template <typename Base>
+std::string base_encode_to_string(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);
+
+ size_t consumed = 0;
+ size_t produced = 0;
+
+ if(output_length > 0)
+ {
+ produced = base_encode(base, &output.front(),
+ input, input_length,
+ consumed, true);
+ }
+
+ BOTAN_ASSERT_EQUAL(consumed, input_length, "Consumed the entire input");
+ BOTAN_ASSERT_EQUAL(produced, output.size(), "Produced expected size");
+
+ return output;
+ }
+
+/**
+* Perform decoding using the base provided
+* @param base object giving access to the encodings specifications
+* @param output an array of at least Base::decode_max_output bytes
+* @param input some base input
+* @param input_length length of input in bytes
+* @param input_consumed is an output parameter which says how many
+* bytes of input were actually consumed. If less than
+* input_length, then the range input[consumed:length]
+* should be passed in later along with more input.
+* @param final_inputs true iff this is the last input, in which case
+ padding is allowed
+* @param ignore_ws ignore whitespace on input; if false, throw an
+ exception if whitespace is encountered
+* @return number of bytes written to output
+*/
+template <typename Base>
+size_t base_decode(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();
+
+ uint8_t* out_ptr = output;
+ std::vector<uint8_t> decode_buf(decoding_bytes_in, 0);
+ size_t decode_buf_pos = 0;
+ size_t final_truncate = 0;
+
+ clear_mem(output, base.decode_max_output(input_length));
+
+ for(size_t i = 0; i != input_length; ++i)
+ {
+ const uint8_t bin = base.lookup_binary_value(input[i]);
+
+ if(base.check_bad_char(bin, input[i], ignore_ws)) // May throw Invalid_Argument
+ {
+ decode_buf[decode_buf_pos] = bin;
+ ++decode_buf_pos;
+ }
+
+ /*
+ * If we're at the end of the input, pad with 0s and truncate
+ */
+ if(final_inputs && (i == input_length - 1))
+ {
+ if(decode_buf_pos)
+ {
+ for(size_t j = decode_buf_pos; j < decoding_bytes_in; ++j)
+ { decode_buf[j] = 0; }
+
+ final_truncate = decoding_bytes_in - decode_buf_pos;
+ decode_buf_pos = decoding_bytes_in;
+ }
+ }
+
+ if(decode_buf_pos == decoding_bytes_in)
+ {
+ base.decode(out_ptr, decode_buf.data());
+
+ out_ptr += decoding_bytes_out;
+ decode_buf_pos = 0;
+ input_consumed = i+1;
+ }
+ }
+
+ while(input_consumed < input_length &&
+ base.lookup_binary_value(input[input_consumed]) == 0x80)
+ {
+ ++input_consumed;
+ }
+
+ size_t written = (out_ptr - output) - base.bytes_to_remove(final_truncate);
+
+ return written;
+ }
+
+template<typename Base>
+size_t base_decode_full(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);
+
+ if(consumed != input_length)
+ {
+ throw Invalid_Argument(base.name() + " decoding failed, input did not have full bytes");
+ }
+
+ return written;
+ }
+
+template<typename Vector, typename Base>
+Vector base_decode_to_vec(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);
+
+ const size_t written =
+ base_decode_full(base, bin.data(), input, input_length, ignore_ws);
+
+ bin.resize(written);
+ return bin;
+ }
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/utils/compiler.h b/comm/third_party/botan/src/lib/utils/compiler.h
new file mode 100644
index 0000000000..832ab8f22c
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/compiler.h
@@ -0,0 +1,225 @@
+/*
+* Define useful compiler-specific macros
+* (C) 2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+/* This header is included in both C++ and C (via ffi.h) and should only
+ contain macro definitions. Avoid C++ style // comments in this file.
+*/
+
+#ifndef BOTAN_UTIL_COMPILER_FLAGS_H_
+#define BOTAN_UTIL_COMPILER_FLAGS_H_
+
+/* Should we use GCC-style inline assembler? */
+#if defined(BOTAN_BUILD_COMPILER_IS_GCC) || \
+ defined(BOTAN_BUILD_COMPILER_IS_CLANG) || \
+ defined(BOTAN_BUILD_COMPILER_IS_XLC) || \
+ defined(BOTAN_BUILD_COMPILER_IS_SUN_STUDIO)
+
+ #define BOTAN_USE_GCC_INLINE_ASM
+#endif
+
+/**
+* Used to annotate API exports which are public and supported.
+* These APIs will not be broken/removed unless strictly required for
+* functionality or security, and only in new major versions.
+* @param maj The major version this public API was released in
+* @param min The minor version this public API was released in
+*/
+#define BOTAN_PUBLIC_API(maj,min) BOTAN_DLL
+
+/**
+* Used to annotate API exports which are public, but are now deprecated
+* and which will be removed in a future major release.
+*/
+#define BOTAN_DEPRECATED_API(msg) BOTAN_DLL BOTAN_DEPRECATED(msg)
+
+/**
+* Used to annotate API exports which are public and can be used by
+* applications if needed, but which are intentionally not documented,
+* and which may change incompatibly in a future major version.
+*/
+#define BOTAN_UNSTABLE_API BOTAN_DLL
+
+/**
+* Used to annotate API exports which are exported but only for the
+* purposes of testing. They should not be used by applications and
+* may be removed or changed without notice.
+*/
+#define BOTAN_TEST_API BOTAN_DLL
+
+/*
+* Define BOTAN_GCC_VERSION
+*/
+#if defined(__GNUC__) && !defined(__clang__)
+ #define BOTAN_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__ * 10 + __GNUC_PATCHLEVEL__)
+#else
+ #define BOTAN_GCC_VERSION 0
+#endif
+
+/*
+* Define BOTAN_CLANG_VERSION
+*/
+#if defined(__clang__)
+ #define BOTAN_CLANG_VERSION (__clang_major__ * 10 + __clang_minor__)
+#else
+ #define BOTAN_CLANG_VERSION 0
+#endif
+
+/*
+* Define BOTAN_FUNC_ISA
+*/
+#if (defined(__GNUC__) && !defined(__clang__)) || (BOTAN_CLANG_VERSION > 38)
+ #define BOTAN_FUNC_ISA(isa) __attribute__ ((target(isa)))
+#else
+ #define BOTAN_FUNC_ISA(isa)
+#endif
+
+/*
+* Define BOTAN_WARN_UNUSED_RESULT
+*/
+#if defined(__GNUC__) || defined(__clang__)
+ #define BOTAN_WARN_UNUSED_RESULT __attribute__ ((warn_unused_result))
+#else
+ #define BOTAN_WARN_UNUSED_RESULT
+#endif
+
+/*
+* Define BOTAN_MALLOC_FN
+*/
+#if defined(__ibmxl__)
+ /* XLC pretends to be both Clang and GCC, but is neither */
+ #define BOTAN_MALLOC_FN __attribute__ ((malloc))
+#elif defined(__GNUC__)
+ #define BOTAN_MALLOC_FN __attribute__ ((malloc, alloc_size(1,2)))
+#elif defined(_MSC_VER)
+ #define BOTAN_MALLOC_FN __declspec(restrict)
+#else
+ #define BOTAN_MALLOC_FN
+#endif
+
+/*
+* Define BOTAN_DEPRECATED
+*/
+#if !defined(BOTAN_NO_DEPRECATED_WARNINGS) && !defined(BOTAN_IS_BEING_BUILT) && !defined(BOTAN_AMALGAMATION_H_)
+
+ #if defined(__clang__)
+ #define BOTAN_DEPRECATED(msg) __attribute__ ((deprecated(msg)))
+ #define BOTAN_DEPRECATED_HEADER(hdr) _Pragma("message \"this header is deprecated\"")
+ #define BOTAN_FUTURE_INTERNAL_HEADER(hdr) _Pragma("message \"this header will be made internal in the future\"")
+
+ #elif defined(_MSC_VER)
+ #define BOTAN_DEPRECATED(msg) __declspec(deprecated(msg))
+ #define BOTAN_DEPRECATED_HEADER(hdr) __pragma(message("this header is deprecated"))
+ #define BOTAN_FUTURE_INTERNAL_HEADER(hdr) __pragma(message("this header will be made internal in the future"))
+
+ #elif defined(__GNUC__)
+ /* msg supported since GCC 4.5, earliest we support is 4.8 */
+ #define BOTAN_DEPRECATED(msg) __attribute__ ((deprecated(msg)))
+ #define BOTAN_DEPRECATED_HEADER(hdr) _Pragma("GCC warning \"this header is deprecated\"")
+ #define BOTAN_FUTURE_INTERNAL_HEADER(hdr) _Pragma("GCC warning \"this header will be made internal in the future\"")
+ #endif
+
+#endif
+
+#if !defined(BOTAN_DEPRECATED)
+ #define BOTAN_DEPRECATED(msg)
+#endif
+
+#if !defined(BOTAN_DEPRECATED_HEADER)
+ #define BOTAN_DEPRECATED_HEADER(hdr)
+#endif
+
+#if !defined(BOTAN_FUTURE_INTERNAL_HEADER)
+ #define BOTAN_FUTURE_INTERNAL_HEADER(hdr)
+#endif
+
+/*
+* Define BOTAN_NORETURN
+*/
+#if !defined(BOTAN_NORETURN)
+
+ #if defined (__clang__) || defined (__GNUC__)
+ #define BOTAN_NORETURN __attribute__ ((__noreturn__))
+
+ #elif defined (_MSC_VER)
+ #define BOTAN_NORETURN __declspec(noreturn)
+
+ #else
+ #define BOTAN_NORETURN
+ #endif
+
+#endif
+
+/*
+* Define BOTAN_THREAD_LOCAL
+*/
+#if !defined(BOTAN_THREAD_LOCAL)
+
+ #if defined(BOTAN_TARGET_OS_HAS_THREADS) && defined(BOTAN_TARGET_OS_HAS_THREAD_LOCAL)
+ #define BOTAN_THREAD_LOCAL thread_local
+ #else
+ #define BOTAN_THREAD_LOCAL /**/
+ #endif
+
+#endif
+
+/*
+* Define BOTAN_IF_CONSTEXPR
+*/
+#if !defined(BOTAN_IF_CONSTEXPR)
+ #if __cplusplus >= 201703
+ #define BOTAN_IF_CONSTEXPR if constexpr
+ #else
+ #define BOTAN_IF_CONSTEXPR if
+ #endif
+#endif
+
+/*
+* Define BOTAN_PARALLEL_FOR
+*/
+#if !defined(BOTAN_PARALLEL_FOR)
+
+#if defined(BOTAN_TARGET_HAS_OPENMP)
+ #define BOTAN_PARALLEL_FOR _Pragma("omp parallel for") for
+#else
+ #define BOTAN_PARALLEL_FOR for
+#endif
+
+#endif
+
+/*
+* Define BOTAN_FORCE_INLINE
+*/
+#if !defined(BOTAN_FORCE_INLINE)
+
+ #if defined (__clang__) || defined (__GNUC__)
+ #define BOTAN_FORCE_INLINE __attribute__ ((__always_inline__)) inline
+
+ #elif defined (_MSC_VER)
+ #define BOTAN_FORCE_INLINE __forceinline
+
+ #else
+ #define BOTAN_FORCE_INLINE inline
+ #endif
+
+#endif
+
+/*
+* Define BOTAN_PARALLEL_SIMD_FOR
+*/
+#if !defined(BOTAN_PARALLEL_SIMD_FOR)
+
+#if defined(BOTAN_TARGET_HAS_OPENMP)
+ #define BOTAN_PARALLEL_SIMD_FOR _Pragma("omp simd") for
+#elif defined(BOTAN_BUILD_COMPILER_IS_GCC) && (BOTAN_GCC_VERSION >= 490)
+ #define BOTAN_PARALLEL_SIMD_FOR _Pragma("GCC ivdep") for
+#else
+ #define BOTAN_PARALLEL_SIMD_FOR for
+#endif
+
+#endif
+
+#endif
diff --git a/comm/third_party/botan/src/lib/utils/cpuid/cpuid.cpp b/comm/third_party/botan/src/lib/utils/cpuid/cpuid.cpp
new file mode 100644
index 0000000000..e76e12ea8d
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/cpuid/cpuid.cpp
@@ -0,0 +1,231 @@
+/*
+* Runtime CPU detection
+* (C) 2009,2010,2013,2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/cpuid.h>
+#include <botan/types.h>
+#include <botan/exceptn.h>
+#include <botan/parsing.h>
+#include <ostream>
+
+namespace Botan {
+
+bool CPUID::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 true;
+#endif
+ }
+
+//static
+std::string CPUID::to_string()
+ {
+ std::vector<std::string> flags;
+
+#define CPUID_PRINT(flag) do { if(has_##flag()) { flags.push_back(#flag); } } while(0)
+
+#if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
+ CPUID_PRINT(sse2);
+ CPUID_PRINT(ssse3);
+ CPUID_PRINT(sse41);
+ CPUID_PRINT(sse42);
+ CPUID_PRINT(avx2);
+ CPUID_PRINT(avx512f);
+ CPUID_PRINT(avx512dq);
+ CPUID_PRINT(avx512bw);
+ CPUID_PRINT(avx512_icelake);
+
+ CPUID_PRINT(rdtsc);
+ CPUID_PRINT(bmi1);
+ CPUID_PRINT(bmi2);
+ CPUID_PRINT(adx);
+
+ CPUID_PRINT(aes_ni);
+ CPUID_PRINT(clmul);
+ CPUID_PRINT(rdrand);
+ CPUID_PRINT(rdseed);
+ CPUID_PRINT(intel_sha);
+ CPUID_PRINT(avx512_aes);
+ CPUID_PRINT(avx512_clmul);
+#endif
+
+#if defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY)
+ CPUID_PRINT(altivec);
+ CPUID_PRINT(power_crypto);
+ CPUID_PRINT(darn_rng);
+#endif
+
+#if 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);
+#endif
+
+#undef CPUID_PRINT
+
+ return string_join(flags, ' ');
+ }
+
+//static
+void CPUID::print(std::ostream& o)
+ {
+ o << "CPUID flags: " << CPUID::to_string() << "\n";
+ }
+
+//static
+void CPUID::initialize()
+ {
+ state() = CPUID_Data();
+ }
+
+CPUID::CPUID_Data::CPUID_Data()
+ {
+ m_cache_line_size = 0;
+ m_processor_features = 0;
+
+#if defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY) || \
+ defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY) || \
+ defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
+
+ m_processor_features = detect_cpu_features(&m_cache_line_size);
+
+#endif
+
+ m_processor_features |= CPUID::CPUID_INITIALIZED_BIT;
+
+ if(m_cache_line_size == 0)
+ m_cache_line_size = BOTAN_TARGET_CPU_DEFAULT_CACHE_LINE_SIZE;
+
+ m_endian_status = runtime_check_endian();
+ }
+
+//static
+CPUID::Endian_Status CPUID::CPUID_Data::runtime_check_endian()
+ {
+ // Check runtime endian
+ const uint32_t endian32 = 0x01234567;
+ const uint8_t* e8 = reinterpret_cast<const uint8_t*>(&endian32);
+
+ CPUID::Endian_Status endian = CPUID::Endian_Status::Unknown;
+
+ if(e8[0] == 0x01 && e8[1] == 0x23 && e8[2] == 0x45 && e8[3] == 0x67)
+ {
+ endian = CPUID::Endian_Status::Big;
+ }
+ else if(e8[0] == 0x67 && e8[1] == 0x45 && e8[2] == 0x23 && e8[3] == 0x01)
+ {
+ endian = CPUID::Endian_Status::Little;
+ }
+ 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(endian == CPUID::Endian_Status::Little, "Build and runtime endian match");
+#elif defined(BOTAN_TARGET_CPU_IS_BIG_ENDIAN)
+ BOTAN_ASSERT(endian == CPUID::Endian_Status::Big, "Build and runtime endian match");
+#endif
+
+ return endian;
+ }
+
+std::vector<Botan::CPUID::CPUID_bits>
+CPUID::bit_from_string(const std::string& tok)
+ {
+#if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
+ if(tok == "sse2" || tok == "simd")
+ return {Botan::CPUID::CPUID_SSE2_BIT};
+ if(tok == "ssse3")
+ return {Botan::CPUID::CPUID_SSSE3_BIT};
+ if(tok == "sse41")
+ return {Botan::CPUID::CPUID_SSE41_BIT};
+ if(tok == "sse42")
+ return {Botan::CPUID::CPUID_SSE42_BIT};
+ // aes_ni is the string printed on the console when running "botan cpuid"
+ if(tok == "aesni" || tok == "aes_ni")
+ return {Botan::CPUID::CPUID_AESNI_BIT};
+ if(tok == "clmul")
+ return {Botan::CPUID::CPUID_CLMUL_BIT};
+ if(tok == "avx2")
+ return {Botan::CPUID::CPUID_AVX2_BIT};
+ if(tok == "avx512f")
+ return {Botan::CPUID::CPUID_AVX512F_BIT};
+ if(tok == "avx512_icelake")
+ return {Botan::CPUID::CPUID_AVX512_ICL_BIT};
+ // there were two if statements testing "sha" and "intel_sha" separately; combined
+ if(tok == "sha" || tok=="intel_sha")
+ return {Botan::CPUID::CPUID_SHA_BIT};
+ if(tok == "rdtsc")
+ return {Botan::CPUID::CPUID_RDTSC_BIT};
+ if(tok == "bmi1")
+ return {Botan::CPUID::CPUID_BMI1_BIT};
+ if(tok == "bmi2")
+ return {Botan::CPUID::CPUID_BMI2_BIT};
+ if(tok == "adx")
+ return {Botan::CPUID::CPUID_ADX_BIT};
+ if(tok == "rdrand")
+ return {Botan::CPUID::CPUID_RDRAND_BIT};
+ if(tok == "rdseed")
+ return {Botan::CPUID::CPUID_RDSEED_BIT};
+ if(tok == "avx512_aes")
+ return {Botan::CPUID::CPUID_AVX512_AES_BIT};
+ if(tok == "avx512_clmul")
+ return {Botan::CPUID::CPUID_AVX512_CLMUL_BIT};
+
+#elif defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY)
+ if(tok == "altivec" || tok == "simd")
+ return {Botan::CPUID::CPUID_ALTIVEC_BIT};
+ if(tok == "power_crypto")
+ return {Botan::CPUID::CPUID_POWER_CRYPTO_BIT};
+ if(tok == "darn_rng")
+ return {Botan::CPUID::CPUID_DARN_BIT};
+
+#elif defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY)
+ if(tok == "neon" || tok == "simd")
+ return {Botan::CPUID::CPUID_ARM_NEON_BIT};
+ if(tok == "arm_sve")
+ return {Botan::CPUID::CPUID_ARM_SVE_BIT};
+ if(tok == "armv8sha1" || tok == "arm_sha1")
+ return {Botan::CPUID::CPUID_ARM_SHA1_BIT};
+ if(tok == "armv8sha2" || tok == "arm_sha2")
+ return {Botan::CPUID::CPUID_ARM_SHA2_BIT};
+ if(tok == "armv8aes" || tok == "arm_aes")
+ return {Botan::CPUID::CPUID_ARM_AES_BIT};
+ if(tok == "armv8pmull" || tok == "arm_pmull")
+ return {Botan::CPUID::CPUID_ARM_PMULL_BIT};
+ if(tok == "armv8sha3" || tok == "arm_sha3")
+ return {Botan::CPUID::CPUID_ARM_SHA3_BIT};
+ if(tok == "armv8sha2_512" || tok == "arm_sha2_512")
+ return {Botan::CPUID::CPUID_ARM_SHA2_512_BIT};
+ if(tok == "armv8sm3" || tok == "arm_sm3")
+ return {Botan::CPUID::CPUID_ARM_SM3_BIT};
+ if(tok == "armv8sm4" || tok == "arm_sm4")
+ return {Botan::CPUID::CPUID_ARM_SM4_BIT};
+
+#else
+ BOTAN_UNUSED(tok);
+#endif
+
+ return {};
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/utils/cpuid/cpuid.h b/comm/third_party/botan/src/lib/utils/cpuid/cpuid.h
new file mode 100644
index 0000000000..04d0bbd191
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/cpuid/cpuid.h
@@ -0,0 +1,484 @@
+/*
+* Runtime CPU detection
+* (C) 2009,2010,2013,2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_CPUID_H_
+#define BOTAN_CPUID_H_
+
+#include <botan/types.h>
+#include <vector>
+#include <string>
+#include <iosfwd>
+
+BOTAN_FUTURE_INTERNAL_HEADER(cpuid.h)
+
+namespace Botan {
+
+/**
+* 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_PUBLIC_API(2,1) CPUID final
+ {
+ public:
+ /**
+ * Probe the CPU and see what extensions are supported
+ */
+ static void initialize();
+
+ static bool has_simd_32();
+
+ /**
+ * Deprecated equivalent to
+ * o << "CPUID flags: " << CPUID::to_string() << "\n";
+ */
+ BOTAN_DEPRECATED("Use CPUID::to_string")
+ static void print(std::ostream& o);
+
+ /**
+ * Return a possibly empty string containing list of known CPU
+ * extensions. Each name will be seperated 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).
+ *
+ * Example outputs "sse2 ssse3 rdtsc", "neon arm_aes", "altivec"
+ */
+ static std::string to_string();
+
+ /**
+ * Return a best guess of the cache line size
+ */
+ static size_t cache_line_size()
+ {
+ return state().cache_line_size();
+ }
+
+ 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 state().endian_status() == Endian_Status::Little;
+#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 state().endian_status() == Endian_Status::Big;
+#endif
+ }
+
+ enum CPUID_bits : uint64_t {
+#if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
+ // These values have no relation to cpuid bitfields
+
+ // SIMD instruction sets
+ CPUID_SSE2_BIT = (1ULL << 0),
+ CPUID_SSSE3_BIT = (1ULL << 1),
+ CPUID_SSE41_BIT = (1ULL << 2),
+ CPUID_SSE42_BIT = (1ULL << 3),
+ CPUID_AVX2_BIT = (1ULL << 4),
+ CPUID_AVX512F_BIT = (1ULL << 5),
+
+ CPUID_AVX512DQ_BIT = (1ULL << 6),
+ CPUID_AVX512BW_BIT = (1ULL << 7),
+
+ // Ice Lake profile: AVX-512 F, DQ, BW, IFMA, VBMI, VBMI2, BITALG
+ CPUID_AVX512_ICL_BIT = (1ULL << 11),
+
+ // Crypto-specific ISAs
+ CPUID_AESNI_BIT = (1ULL << 16),
+ CPUID_CLMUL_BIT = (1ULL << 17),
+ CPUID_RDRAND_BIT = (1ULL << 18),
+ CPUID_RDSEED_BIT = (1ULL << 19),
+ CPUID_SHA_BIT = (1ULL << 20),
+ CPUID_AVX512_AES_BIT = (1ULL << 21),
+ CPUID_AVX512_CLMUL_BIT = (1ULL << 22),
+
+ // Misc useful instructions
+ CPUID_RDTSC_BIT = (1ULL << 48),
+ CPUID_ADX_BIT = (1ULL << 49),
+ CPUID_BMI1_BIT = (1ULL << 50),
+ CPUID_BMI2_BIT = (1ULL << 51),
+#endif
+
+#if defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY)
+ CPUID_ALTIVEC_BIT = (1ULL << 0),
+ CPUID_POWER_CRYPTO_BIT = (1ULL << 1),
+ CPUID_DARN_BIT = (1ULL << 2),
+#endif
+
+#if defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY)
+ CPUID_ARM_NEON_BIT = (1ULL << 0),
+ CPUID_ARM_SVE_BIT = (1ULL << 1),
+ CPUID_ARM_AES_BIT = (1ULL << 16),
+ CPUID_ARM_PMULL_BIT = (1ULL << 17),
+ CPUID_ARM_SHA1_BIT = (1ULL << 18),
+ CPUID_ARM_SHA2_BIT = (1ULL << 19),
+ CPUID_ARM_SHA3_BIT = (1ULL << 20),
+ CPUID_ARM_SHA2_512_BIT = (1ULL << 21),
+ CPUID_ARM_SM3_BIT = (1ULL << 22),
+ CPUID_ARM_SM4_BIT = (1ULL << 23),
+#endif
+
+ CPUID_INITIALIZED_BIT = (1ULL << 63)
+ };
+
+#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 SSE4.1
+ */
+ static bool has_sse41()
+ { return has_cpuid_bit(CPUID_SSE41_BIT); }
+
+ /**
+ * Check if the processor supports SSE4.2
+ */
+ static bool has_sse42()
+ { return has_cpuid_bit(CPUID_SSE42_BIT); }
+
+ /**
+ * Check if the processor supports AVX2
+ */
+ static bool has_avx2()
+ { return has_cpuid_bit(CPUID_AVX2_BIT); }
+
+ /**
+ * Check if the processor supports AVX-512F
+ */
+ static bool has_avx512f()
+ { return has_cpuid_bit(CPUID_AVX512F_BIT); }
+
+ /**
+ * Check if the processor supports AVX-512DQ
+ */
+ static bool has_avx512dq()
+ { return has_cpuid_bit(CPUID_AVX512DQ_BIT); }
+
+ /**
+ * Check if the processor supports AVX-512BW
+ */
+ static bool has_avx512bw()
+ { return has_cpuid_bit(CPUID_AVX512BW_BIT); }
+
+ /**
+ * Check if the processor supports AVX-512 Ice Lake profile
+ */
+ static bool has_avx512_icelake()
+ { return has_cpuid_bit(CPUID_AVX512_ICL_BIT); }
+
+ /**
+ * Check if the processor supports AVX-512 AES (VAES)
+ */
+ static bool has_avx512_aes()
+ { return has_cpuid_bit(CPUID_AVX512_AES_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 BMI1
+ */
+ static bool has_bmi1()
+ { return has_cpuid_bit(CPUID_BMI1_BIT); }
+
+ /**
+ * Check if the processor supports BMI2
+ */
+ static bool has_bmi2()
+ { return has_cpuid_bit(CPUID_BMI2_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
+ */
+ static bool has_intel_sha()
+ { return has_cpuid_bit(CPUID_SHA_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
+ }
+
+ /**
+ * Check if the processor supports hardware AES instructions
+ */
+ 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
+ }
+
+ /**
+ * Check if the processor supports carryless multiply
+ * (CLMUL, PMULL)
+ */
+ 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
+ }
+
+ /*
+ * 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<uint64_t>(bit));
+ }
+
+ /*
+ * Don't call this function, use CPUID::has_xxx above
+ * It is only exposed for the tests.
+ */
+ static bool has_cpuid_bit(CPUID_bits elem)
+ {
+ const uint64_t elem64 = static_cast<uint64_t>(elem);
+ return state().has_bit(elem64);
+ }
+
+ static std::vector<CPUID::CPUID_bits> bit_from_string(const std::string& tok);
+ private:
+ enum class Endian_Status : uint32_t {
+ Unknown = 0x00000000,
+ Big = 0x01234567,
+ Little = 0x67452301,
+ };
+
+ struct CPUID_Data
+ {
+ public:
+ CPUID_Data();
+
+ CPUID_Data(const CPUID_Data& other) = default;
+ CPUID_Data& operator=(const CPUID_Data& other) = default;
+
+ void clear_cpuid_bit(uint64_t bit)
+ {
+ m_processor_features &= ~bit;
+ }
+
+ bool has_bit(uint64_t bit) const
+ {
+ return (m_processor_features & bit) == bit;
+ }
+
+ uint64_t processor_features() const { return m_processor_features; }
+ Endian_Status endian_status() const { return m_endian_status; }
+ size_t cache_line_size() const { return m_cache_line_size; }
+
+ private:
+ static Endian_Status runtime_check_endian();
+
+#if defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY) || \
+ defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY) || \
+ defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
+
+ static uint64_t detect_cpu_features(size_t* cache_line_size);
+
+#endif
+ uint64_t m_processor_features;
+ size_t m_cache_line_size;
+ Endian_Status m_endian_status;
+ };
+
+ static CPUID_Data& state()
+ {
+ static CPUID::CPUID_Data g_cpuid;
+ return g_cpuid;
+ }
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/utils/cpuid/cpuid_arm.cpp b/comm/third_party/botan/src/lib/utils/cpuid/cpuid_arm.cpp
new file mode 100644
index 0000000000..0711dfdf39
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/cpuid/cpuid_arm.cpp
@@ -0,0 +1,237 @@
+/*
+* Runtime CPU detection for ARM
+* (C) 2009,2010,2013,2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/cpuid.h>
+
+#if defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY)
+
+#if defined(BOTAN_TARGET_OS_IS_IOS)
+ #include <sys/types.h>
+ #include <sys/sysctl.h>
+
+#else
+ #include <botan/internal/os_utils.h>
+
+#if defined(BOTAN_TARGET_OS_HAS_GETAUXVAL) || defined(BOTAN_TARGET_OS_HAS_ELF_AUX_INFO)
+ #include <sys/auxv.h>
+#endif
+
+#endif
+
+#endif
+
+namespace Botan {
+
+#if defined(BOTAN_TARGET_CPU_IS_ARM_FAMILY)
+
+#if defined(BOTAN_TARGET_OS_IS_IOS)
+
+namespace {
+
+uint64_t flags_by_ios_machine_type(const std::string& machine)
+ {
+ /*
+ * This relies on a map of known machine names to features. This
+ * will quickly grow out of date as new products are introduced, but
+ * is apparently the best we can do for iOS.
+ */
+
+ struct version_info {
+ std::string name;
+ size_t min_version_neon;
+ size_t min_version_armv8;
+ };
+
+ static const version_info min_versions[] = {
+ { "iPhone", 2, 6 },
+ { "iPad", 1, 4 },
+ { "iPod", 4, 7 },
+ { "AppleTV", 2, 5 },
+ };
+
+ if(machine.size() < 3)
+ return 0;
+
+ auto comma = machine.find(',');
+
+ // Simulator, or something we don't know about
+ if(comma == std::string::npos)
+ return 0;
+
+ std::string product = machine.substr(0, comma);
+
+ size_t version = 0;
+ size_t place = 1;
+ while(product.size() > 1 && ::isdigit(product.back()))
+ {
+ const size_t digit = product.back() - '0';
+ version += digit * place;
+ place *= 10;
+ product.pop_back();
+ }
+
+ if(version == 0)
+ return 0;
+
+ for(const version_info& info : min_versions)
+ {
+ if(info.name != product)
+ continue;
+
+ if(version >= info.min_version_armv8)
+ {
+ return CPUID::CPUID_ARM_AES_BIT |
+ CPUID::CPUID_ARM_PMULL_BIT |
+ CPUID::CPUID_ARM_SHA1_BIT |
+ CPUID::CPUID_ARM_SHA2_BIT |
+ CPUID::CPUID_ARM_NEON_BIT;
+ }
+
+ if(version >= info.min_version_neon)
+ return CPUID::CPUID_ARM_NEON_BIT;
+ }
+
+ // Some other product we don't know about
+ return 0;
+ }
+
+}
+
+#endif
+
+uint64_t CPUID::CPUID_Data::detect_cpu_features(size_t* cache_line_size)
+ {
+ BOTAN_UNUSED(cache_line_size);
+
+ uint64_t detected_features = 0;
+
+#if defined(BOTAN_TARGET_OS_HAS_GETAUXVAL) || defined(BOTAN_TARGET_OS_HAS_ELF_AUX_INFO)
+ /*
+ * 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 ARM_hwcap_bit {
+#if defined(BOTAN_TARGET_ARCH_IS_ARM32)
+ NEON_bit = (1 << 12),
+ AES_bit = (1 << 0),
+ PMULL_bit = (1 << 1),
+ SHA1_bit = (1 << 2),
+ SHA2_bit = (1 << 3),
+
+ ARCH_hwcap_neon = 16, // AT_HWCAP
+ ARCH_hwcap_crypto = 26, // AT_HWCAP2
+#elif defined(BOTAN_TARGET_ARCH_IS_ARM64)
+ 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),
+
+ ARCH_hwcap_neon = 16, // AT_HWCAP
+ ARCH_hwcap_crypto = 16, // AT_HWCAP
+#endif
+ };
+
+#if defined(AT_DCACHEBSIZE)
+ // Exists only on Linux
+ const unsigned long dcache_line = OS::get_auxval(AT_DCACHEBSIZE);
+
+ // plausibility check
+ if(dcache_line == 32 || dcache_line == 64 || dcache_line == 128)
+ *cache_line_size = static_cast<size_t>(dcache_line);
+#endif
+
+ const unsigned long hwcap_neon = OS::get_auxval(ARM_hwcap_bit::ARCH_hwcap_neon);
+ if(hwcap_neon & ARM_hwcap_bit::NEON_bit)
+ detected_features |= CPUID::CPUID_ARM_NEON_BIT;
+
+ /*
+ On aarch64 this ends up calling getauxval twice with AT_HWCAP
+ It doesn't seem worth optimizing this out, since getauxval is
+ just reading a field in the ELF header.
+ */
+ const unsigned long hwcap_crypto = OS::get_auxval(ARM_hwcap_bit::ARCH_hwcap_crypto);
+ if(hwcap_crypto & ARM_hwcap_bit::AES_bit)
+ detected_features |= CPUID::CPUID_ARM_AES_BIT;
+ if(hwcap_crypto & ARM_hwcap_bit::PMULL_bit)
+ detected_features |= CPUID::CPUID_ARM_PMULL_BIT;
+ if(hwcap_crypto & ARM_hwcap_bit::SHA1_bit)
+ detected_features |= CPUID::CPUID_ARM_SHA1_BIT;
+ if(hwcap_crypto & ARM_hwcap_bit::SHA2_bit)
+ detected_features |= CPUID::CPUID_ARM_SHA2_BIT;
+
+#if defined(BOTAN_TARGET_ARCH_IS_ARM64)
+ if(hwcap_crypto & ARM_hwcap_bit::SHA3_bit)
+ detected_features |= CPUID::CPUID_ARM_SHA3_BIT;
+ if(hwcap_crypto & ARM_hwcap_bit::SM3_bit)
+ detected_features |= CPUID::CPUID_ARM_SM3_BIT;
+ if(hwcap_crypto & ARM_hwcap_bit::SM4_bit)
+ detected_features |= CPUID::CPUID_ARM_SM4_BIT;
+ if(hwcap_crypto & ARM_hwcap_bit::SHA2_512_bit)
+ detected_features |= CPUID::CPUID_ARM_SHA2_512_BIT;
+ if(hwcap_crypto & ARM_hwcap_bit::SVE_bit)
+ detected_features |= CPUID::CPUID_ARM_SVE_BIT;
+#endif
+
+#elif defined(BOTAN_TARGET_OS_IS_IOS)
+
+ char machine[64] = { 0 };
+ size_t size = sizeof(machine) - 1;
+ ::sysctlbyname("hw.machine", machine, &size, nullptr, 0);
+
+ detected_features = flags_by_ios_machine_type(machine);
+ // No way to detect cache line size on iOS?
+
+#elif defined(BOTAN_USE_GCC_INLINE_ASM) && defined(BOTAN_TARGET_ARCH_IS_ARM64)
+
+ /*
+ No getauxval API available, fall back on probe functions. We only
+ bother with Aarch64 here to simplify the code and because going to
+ extreme contortions to support detect NEON on devices that probably
+ don't support it doesn't seem worthwhile.
+
+ 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; };
+
+ // Only bother running the crypto detection if we found NEON
+
+ if(OS::run_cpu_instruction_probe(neon_probe) == 1)
+ {
+ detected_features |= CPUID::CPUID_ARM_NEON_BIT;
+
+ if(OS::run_cpu_instruction_probe(aes_probe) == 1)
+ detected_features |= CPUID::CPUID_ARM_AES_BIT;
+ if(OS::run_cpu_instruction_probe(pmull_probe) == 1)
+ detected_features |= CPUID::CPUID_ARM_PMULL_BIT;
+ if(OS::run_cpu_instruction_probe(sha1_probe) == 1)
+ detected_features |= CPUID::CPUID_ARM_SHA1_BIT;
+ if(OS::run_cpu_instruction_probe(sha2_probe) == 1)
+ detected_features |= CPUID::CPUID_ARM_SHA2_BIT;
+ }
+
+#endif
+
+ return detected_features;
+ }
+
+#endif
+
+}
diff --git a/comm/third_party/botan/src/lib/utils/cpuid/cpuid_ppc.cpp b/comm/third_party/botan/src/lib/utils/cpuid/cpuid_ppc.cpp
new file mode 100644
index 0000000000..604b97947c
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/cpuid/cpuid_ppc.cpp
@@ -0,0 +1,132 @@
+/*
+* Runtime CPU detection for POWER/PowerPC
+* (C) 2009,2010,2013,2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/cpuid.h>
+#include <botan/internal/os_utils.h>
+
+#if defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY)
+
+/*
+* On macOS and OpenBSD ppc, use sysctl to detect AltiVec
+*/
+#if defined(BOTAN_TARGET_OS_IS_MACOS)
+ #include <sys/sysctl.h>
+#elif defined(BOTAN_TARGET_OS_IS_OPENBSD)
+ #include <sys/param.h>
+ #include <sys/sysctl.h>
+ #include <machine/cpu.h>
+#endif
+
+#endif
+
+namespace Botan {
+
+#if defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY)
+
+/*
+* PowerPC specific block: check for AltiVec using either
+* sysctl or by reading processor version number register.
+*/
+uint64_t CPUID::CPUID_Data::detect_cpu_features(size_t* cache_line_size)
+ {
+ BOTAN_UNUSED(cache_line_size);
+
+#if defined(BOTAN_TARGET_OS_IS_MACOS) || defined(BOTAN_TARGET_OS_IS_OPENBSD)
+ // On macOS and OpenBSD, use sysctl
+
+ int sels[2] = {
+#if defined(BOTAN_TARGET_OS_IS_OPENBSD)
+ CTL_MACHDEP, CPU_ALTIVEC
+#else
+ CTL_HW, HW_VECTORUNIT
+#endif
+ };
+
+ int vector_type = 0;
+ size_t length = sizeof(vector_type);
+ int error = ::sysctl(sels, 2, &vector_type, &length, NULL, 0);
+
+ if(error == 0 && vector_type > 0)
+ return CPUID::CPUID_ALTIVEC_BIT;
+
+#elif (defined(BOTAN_TARGET_OS_HAS_GETAUXVAL) || defined(BOTAN_TARGET_HAS_ELF_AUX_INFO)) && defined(BOTAN_TARGET_ARCH_IS_PPC64)
+
+ enum PPC_hwcap_bit {
+ ALTIVEC_bit = (1 << 28),
+ CRYPTO_bit = (1 << 25),
+ DARN_bit = (1 << 21),
+
+ ARCH_hwcap_altivec = 16, // AT_HWCAP
+ ARCH_hwcap_crypto = 26, // AT_HWCAP2
+ };
+
+ uint64_t detected_features = 0;
+
+ const unsigned long hwcap_altivec = OS::get_auxval(PPC_hwcap_bit::ARCH_hwcap_altivec);
+ if(hwcap_altivec & PPC_hwcap_bit::ALTIVEC_bit)
+ detected_features |= CPUID::CPUID_ALTIVEC_BIT;
+
+ const unsigned long hwcap_crypto = OS::get_auxval(PPC_hwcap_bit::ARCH_hwcap_crypto);
+ if(hwcap_crypto & PPC_hwcap_bit::CRYPTO_bit)
+ detected_features |= CPUID::CPUID_POWER_CRYPTO_BIT;
+ if(hwcap_crypto & PPC_hwcap_bit::DARN_bit)
+ detected_features |= CPUID::CPUID_DARN_BIT;
+
+ return detected_features;
+
+#else
+
+ /*
+ On PowerPC, MSR 287 is PVR, the Processor Version Number
+ Normally it is only accessible to ring 0, but Linux and NetBSD
+ (others, too, maybe?) will trap and emulate it for us.
+ */
+
+ int pvr = OS::run_cpu_instruction_probe([]() noexcept -> int {
+ uint32_t pvr = 0;
+ asm volatile("mfspr %0, 287" : "=r" (pvr));
+ // Top 16 bits suffice to identify the model
+ return static_cast<int>(pvr >> 16);
+ });
+
+ if(pvr > 0)
+ {
+ const uint16_t ALTIVEC_PVR[] = {
+ 0x003E, // IBM POWER6
+ 0x003F, // IBM POWER7
+ 0x004A, // IBM POWER7p
+ 0x004B, // IBM POWER8E
+ 0x004C, // IBM POWER8 NVL
+ 0x004D, // IBM POWER8
+ 0x004E, // IBM POWER9
+ 0x000C, // G4-7400
+ 0x0039, // G5 970
+ 0x003C, // G5 970FX
+ 0x0044, // G5 970MP
+ 0x0070, // Cell PPU
+ 0, // end
+ };
+
+ for(size_t i = 0; ALTIVEC_PVR[i]; ++i)
+ {
+ if(pvr == ALTIVEC_PVR[i])
+ return CPUID::CPUID_ALTIVEC_BIT;
+ }
+
+ return 0;
+ }
+
+ // TODO try direct instruction probing
+
+#endif
+
+ return 0;
+ }
+
+#endif
+
+}
diff --git a/comm/third_party/botan/src/lib/utils/cpuid/cpuid_x86.cpp b/comm/third_party/botan/src/lib/utils/cpuid/cpuid_x86.cpp
new file mode 100644
index 0000000000..0595e1a604
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/cpuid/cpuid_x86.cpp
@@ -0,0 +1,214 @@
+/*
+* Runtime CPU detection for x86
+* (C) 2009,2010,2013,2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/cpuid.h>
+#include <botan/mem_ops.h>
+#include <botan/loadstor.h>
+
+#if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
+
+#if defined(BOTAN_BUILD_COMPILER_IS_MSVC)
+ #include <intrin.h>
+#elif defined(BOTAN_BUILD_COMPILER_IS_INTEL)
+ #include <ia32intrin.h>
+#elif defined(BOTAN_BUILD_COMPILER_IS_GCC) || defined(BOTAN_BUILD_COMPILER_IS_CLANG)
+ #include <cpuid.h>
+#endif
+
+#endif
+
+namespace Botan {
+
+#if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
+
+uint64_t CPUID::CPUID_Data::detect_cpu_features(size_t* cache_line_size)
+ {
+#if defined(BOTAN_BUILD_COMPILER_IS_MSVC)
+ #define X86_CPUID(type, out) do { __cpuid((int*)out, type); } while(0)
+ #define X86_CPUID_SUBLEVEL(type, level, out) do { __cpuidex((int*)out, type, level); } while(0)
+
+#elif defined(BOTAN_BUILD_COMPILER_IS_INTEL)
+ #define X86_CPUID(type, out) do { __cpuid(out, type); } while(0)
+ #define X86_CPUID_SUBLEVEL(type, level, out) do { __cpuidex((int*)out, type, level); } while(0)
+
+#elif defined(BOTAN_TARGET_ARCH_IS_X86_64) && defined(BOTAN_USE_GCC_INLINE_ASM)
+ #define X86_CPUID(type, out) \
+ asm("cpuid\n\t" : "=a" (out[0]), "=b" (out[1]), "=c" (out[2]), "=d" (out[3]) \
+ : "0" (type))
+
+ #define X86_CPUID_SUBLEVEL(type, level, out) \
+ asm("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_GCC) || defined(BOTAN_BUILD_COMPILER_IS_CLANG)
+ #define X86_CPUID(type, out) do { __get_cpuid(type, out, out+1, out+2, out+3); } while(0)
+
+ #define X86_CPUID_SUBLEVEL(type, level, out) \
+ do { __cpuid_count(type, level, out[0], out[1], out[2], out[3]); } while(0)
+#else
+ #warning "No way of calling x86 cpuid instruction for this compiler"
+ #define X86_CPUID(type, out) do { clear_mem(out, 4); } while(0)
+ #define X86_CPUID_SUBLEVEL(type, level, out) do { clear_mem(out, 4); } while(0)
+#endif
+
+ uint64_t features_detected = 0;
+ uint32_t cpuid[4] = { 0 };
+
+ // CPUID 0: vendor identification, max sublevel
+ X86_CPUID(0, cpuid);
+
+ const uint32_t max_supported_sublevel = cpuid[0];
+
+ const uint32_t INTEL_CPUID[3] = { 0x756E6547, 0x6C65746E, 0x49656E69 };
+ const uint32_t AMD_CPUID[3] = { 0x68747541, 0x444D4163, 0x69746E65 };
+ const bool is_intel = same_mem(cpuid + 1, INTEL_CPUID, 3);
+ const bool is_amd = same_mem(cpuid + 1, AMD_CPUID, 3);
+
+ if(max_supported_sublevel >= 1)
+ {
+ // CPUID 1: feature bits
+ X86_CPUID(1, cpuid);
+ const uint64_t flags0 = (static_cast<uint64_t>(cpuid[2]) << 32) | cpuid[3];
+
+ enum x86_CPUID_1_bits : uint64_t {
+ RDTSC = (1ULL << 4),
+ SSE2 = (1ULL << 26),
+ CLMUL = (1ULL << 33),
+ SSSE3 = (1ULL << 41),
+ SSE41 = (1ULL << 51),
+ SSE42 = (1ULL << 52),
+ AESNI = (1ULL << 57),
+ RDRAND = (1ULL << 62)
+ };
+
+ if(flags0 & x86_CPUID_1_bits::RDTSC)
+ features_detected |= CPUID::CPUID_RDTSC_BIT;
+ if(flags0 & x86_CPUID_1_bits::SSE2)
+ features_detected |= CPUID::CPUID_SSE2_BIT;
+ if(flags0 & x86_CPUID_1_bits::CLMUL)
+ features_detected |= CPUID::CPUID_CLMUL_BIT;
+ if(flags0 & x86_CPUID_1_bits::SSSE3)
+ features_detected |= CPUID::CPUID_SSSE3_BIT;
+ if(flags0 & x86_CPUID_1_bits::SSE41)
+ features_detected |= CPUID::CPUID_SSE41_BIT;
+ if(flags0 & x86_CPUID_1_bits::SSE42)
+ features_detected |= CPUID::CPUID_SSE42_BIT;
+ if(flags0 & x86_CPUID_1_bits::AESNI)
+ features_detected |= CPUID::CPUID_AESNI_BIT;
+ if(flags0 & x86_CPUID_1_bits::RDRAND)
+ features_detected |= CPUID::CPUID_RDRAND_BIT;
+ }
+
+ if(is_intel)
+ {
+ // Intel cache line size is in cpuid(1) output
+ *cache_line_size = 8 * get_byte(2, cpuid[1]);
+ }
+ else if(is_amd)
+ {
+ // AMD puts it in vendor zone
+ X86_CPUID(0x80000005, cpuid);
+ *cache_line_size = get_byte(3, cpuid[2]);
+ }
+
+ if(max_supported_sublevel >= 7)
+ {
+ clear_mem(cpuid, 4);
+ X86_CPUID_SUBLEVEL(7, 0, cpuid);
+
+ enum x86_CPUID_7_bits : uint64_t {
+ BMI1 = (1ULL << 3),
+ AVX2 = (1ULL << 5),
+ BMI2 = (1ULL << 8),
+ 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),
+ AVX512_VAES = (1ULL << 41),
+ AVX512_VCLMUL = (1ULL << 42),
+ AVX512_VBITALG = (1ULL << 44),
+ };
+
+ const uint64_t flags7 = (static_cast<uint64_t>(cpuid[2]) << 32) | cpuid[1];
+
+ if(flags7 & x86_CPUID_7_bits::AVX2)
+ features_detected |= CPUID::CPUID_AVX2_BIT;
+ if(flags7 & x86_CPUID_7_bits::BMI1)
+ {
+ features_detected |= CPUID::CPUID_BMI1_BIT;
+ /*
+ We only set the BMI2 bit if BMI1 is also supported, so BMI2
+ code can safely use both extensions. No known processor
+ implements BMI2 but not BMI1.
+ */
+ if(flags7 & x86_CPUID_7_bits::BMI2)
+ features_detected |= CPUID::CPUID_BMI2_BIT;
+ }
+
+ if(flags7 & x86_CPUID_7_bits::AVX512_F)
+ {
+ features_detected |= CPUID::CPUID_AVX512F_BIT;
+
+ if(flags7 & x86_CPUID_7_bits::AVX512_DQ)
+ features_detected |= CPUID::CPUID_AVX512DQ_BIT;
+ if(flags7 & x86_CPUID_7_bits::AVX512_BW)
+ features_detected |= CPUID::CPUID_AVX512BW_BIT;
+
+ const uint64_t ICELAKE_FLAGS =
+ x86_CPUID_7_bits::AVX512_F |
+ x86_CPUID_7_bits::AVX512_DQ |
+ x86_CPUID_7_bits::AVX512_IFMA |
+ x86_CPUID_7_bits::AVX512_BW |
+ x86_CPUID_7_bits::AVX512_VL |
+ x86_CPUID_7_bits::AVX512_VBMI |
+ x86_CPUID_7_bits::AVX512_VBMI2 |
+ x86_CPUID_7_bits::AVX512_VBITALG;
+
+ if((flags7 & ICELAKE_FLAGS) == ICELAKE_FLAGS)
+ features_detected |= CPUID::CPUID_AVX512_ICL_BIT;
+
+ if(flags7 & x86_CPUID_7_bits::AVX512_VAES)
+ features_detected |= CPUID::CPUID_AVX512_AES_BIT;
+ if(flags7 & x86_CPUID_7_bits::AVX512_VCLMUL)
+ features_detected |= CPUID::CPUID_AVX512_CLMUL_BIT;
+ }
+
+ if(flags7 & x86_CPUID_7_bits::RDSEED)
+ features_detected |= CPUID::CPUID_RDSEED_BIT;
+ if(flags7 & x86_CPUID_7_bits::ADX)
+ features_detected |= CPUID::CPUID_ADX_BIT;
+ if(flags7 & x86_CPUID_7_bits::SHA)
+ features_detected |= CPUID::CPUID_SHA_BIT;
+ }
+
+#undef X86_CPUID
+#undef X86_CPUID_SUBLEVEL
+
+ /*
+ * 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(features_detected == 0)
+ {
+ features_detected |= CPUID::CPUID_SSE2_BIT;
+ features_detected |= CPUID::CPUID_RDTSC_BIT;
+ }
+#endif
+
+ return features_detected;
+ }
+
+#endif
+
+}
diff --git a/comm/third_party/botan/src/lib/utils/cpuid/info.txt b/comm/third_party/botan/src/lib/utils/cpuid/info.txt
new file mode 100644
index 0000000000..987d7eae4d
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/cpuid/info.txt
@@ -0,0 +1,7 @@
+<defines>
+CPUID -> 20170917
+</defines>
+
+<header:public>
+cpuid.h
+</header:public>
diff --git a/comm/third_party/botan/src/lib/utils/ct_utils.cpp b/comm/third_party/botan/src/lib/utils/ct_utils.cpp
new file mode 100644
index 0000000000..cbcecfc282
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/ct_utils.cpp
@@ -0,0 +1,83 @@
+/*
+* (C) 2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/ct_utils.h>
+
+namespace Botan {
+
+namespace CT {
+
+secure_vector<uint8_t> copy_output(CT::Mask<uint8_t> bad_input,
+ const uint8_t input[],
+ size_t input_length,
+ size_t offset)
+ {
+ if(input_length == 0)
+ return secure_vector<uint8_t>();
+
+ /*
+ * Ensure at runtime that offset <= input_length. This is an invalid input,
+ * but we can't throw without using the poisoned value. Instead, if it happens,
+ * set offset to be equal to the input length (so output_bytes becomes 0 and
+ * the returned vector is empty)
+ */
+ const auto valid_offset = CT::Mask<size_t>::is_lte(offset, input_length);
+ offset = valid_offset.select(offset, input_length);
+
+ const size_t output_bytes = input_length - offset;
+
+ secure_vector<uint8_t> output(input_length);
+
+ /*
+ Move the desired output bytes to the front using a slow (O^n)
+ but constant time loop that does not leak the value of the offset
+ */
+ for(size_t i = 0; i != input_length; ++i)
+ {
+ /*
+ start index from i rather than 0 since we know j must be >= i + offset
+ to have any effect, and starting from i does not reveal information
+ */
+ for(size_t j = i; j != input_length; ++j)
+ {
+ const uint8_t b = input[j];
+ const auto is_eq = CT::Mask<size_t>::is_equal(j, offset + i);
+ output[i] |= is_eq.if_set_return(b);
+ }
+ }
+
+ bad_input.if_set_zero_out(output.data(), output.size());
+
+ CT::unpoison(output.data(), output.size());
+ CT::unpoison(output_bytes);
+
+ /*
+ This is potentially not const time, depending on how std::vector is
+ implemented. But since we are always reducing length, it should
+ just amount to setting the member var holding the length.
+ */
+ output.resize(output_bytes);
+ return output;
+ }
+
+secure_vector<uint8_t> strip_leading_zeros(const uint8_t in[], size_t length)
+ {
+ size_t leading_zeros = 0;
+
+ auto only_zeros = Mask<uint8_t>::set();
+
+ for(size_t i = 0; i != length; ++i)
+ {
+ only_zeros &= CT::Mask<uint8_t>::is_zero(in[i]);
+ leading_zeros += only_zeros.if_set_return(1);
+ }
+
+ return copy_output(CT::Mask<uint8_t>::cleared(), in, length, leading_zeros);
+ }
+
+}
+
+}
diff --git a/comm/third_party/botan/src/lib/utils/ct_utils.h b/comm/third_party/botan/src/lib/utils/ct_utils.h
new file mode 100644
index 0000000000..f2e7452931
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/ct_utils.h
@@ -0,0 +1,418 @@
+/*
+* Functions for constant time operations on data and testing of
+* constant time annotations using valgrind.
+*
+* For more information about constant time programming see
+* Wagner, Molnar, et al "The Program Counter Security Model"
+*
+* (C) 2010 Falko Strenzke
+* (C) 2015,2016,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_CT_UTILS_H_
+#define BOTAN_CT_UTILS_H_
+
+#include <botan/secmem.h>
+#include <botan/internal/bit_ops.h>
+#include <type_traits>
+#include <vector>
+
+#if defined(BOTAN_HAS_VALGRIND)
+ #include <valgrind/memcheck.h>
+#endif
+
+namespace Botan {
+
+namespace CT {
+
+/**
+* Use valgrind to mark the contents of memory as being undefined.
+* Valgrind will accept operations which manipulate undefined values,
+* but will warn if an undefined value is used to decided a conditional
+* jump or a load/store address. So if we poison all of our inputs we
+* can confirm that the operations in question are truly const time
+* when compiled by whatever compiler is in use.
+*
+* Even better, the VALGRIND_MAKE_MEM_* macros work even when the
+* program is not run under valgrind (though with a few cycles of
+* overhead, which is unfortunate in final binaries as these
+* annotations tend to be used in fairly important loops).
+*
+* This approach was first used in ctgrind (https://github.com/agl/ctgrind)
+* but calling the valgrind mecheck API directly works just as well and
+* doesn't require a custom patched valgrind.
+*/
+template<typename T>
+inline void poison(const T* p, size_t n)
+ {
+#if defined(BOTAN_HAS_VALGRIND)
+ VALGRIND_MAKE_MEM_UNDEFINED(p, n * sizeof(T));
+#else
+ BOTAN_UNUSED(p);
+ BOTAN_UNUSED(n);
+#endif
+ }
+
+template<typename T>
+inline void unpoison(const T* p, size_t n)
+ {
+#if defined(BOTAN_HAS_VALGRIND)
+ VALGRIND_MAKE_MEM_DEFINED(p, n * sizeof(T));
+#else
+ BOTAN_UNUSED(p);
+ BOTAN_UNUSED(n);
+#endif
+ }
+
+template<typename T>
+inline void unpoison(T& p)
+ {
+#if defined(BOTAN_HAS_VALGRIND)
+ VALGRIND_MAKE_MEM_DEFINED(&p, sizeof(T));
+#else
+ BOTAN_UNUSED(p);
+#endif
+ }
+
+/**
+* A Mask type used for constant-time operations. A Mask<T> always has value
+* either 0 (all bits cleared) or ~0 (all bits set). All operations in a Mask<T>
+* are intended to compile to code which does not contain conditional jumps.
+* This must be verified with tooling (eg binary disassembly or using valgrind)
+* since you never know what a compiler might do.
+*/
+template<typename T>
+class Mask
+ {
+ public:
+ static_assert(std::is_unsigned<T>::value, "CT::Mask only defined for unsigned integer types");
+
+ Mask(const Mask<T>& other) = default;
+ Mask<T>& operator=(const Mask<T>& other) = default;
+
+ /**
+ * Derive a Mask from a Mask of a larger type
+ */
+ template<typename U>
+ Mask(Mask<U> o) : m_mask(static_cast<T>(o.value()))
+ {
+ static_assert(sizeof(U) > sizeof(T), "sizes ok");
+ }
+
+ /**
+ * Return a Mask<T> with all bits set
+ */
+ static Mask<T> set()
+ {
+ return Mask<T>(static_cast<T>(~0));
+ }
+
+ /**
+ * Return a Mask<T> with all bits cleared
+ */
+ static Mask<T> cleared()
+ {
+ return Mask<T>(0);
+ }
+
+ /**
+ * Return a Mask<T> which is set if v is != 0
+ */
+ static Mask<T> expand(T v)
+ {
+ return ~Mask<T>::is_zero(v);
+ }
+
+ /**
+ * Return a Mask<T> which is set if m is set
+ */
+ template<typename U>
+ static Mask<T> expand(Mask<U> m)
+ {
+ static_assert(sizeof(U) < sizeof(T), "sizes ok");
+ return ~Mask<T>::is_zero(m.value());
+ }
+
+ /**
+ * Return a Mask<T> which is set if v is == 0 or cleared otherwise
+ */
+ static Mask<T> is_zero(T x)
+ {
+ return Mask<T>(ct_is_zero<T>(x));
+ }
+
+ /**
+ * Return a Mask<T> which is set if x == y
+ */
+ static Mask<T> is_equal(T x, T y)
+ {
+ return Mask<T>::is_zero(static_cast<T>(x ^ y));
+ }
+
+ /**
+ * Return a Mask<T> which is set if x < y
+ */
+ static Mask<T> is_lt(T x, T y)
+ {
+ return Mask<T>(expand_top_bit<T>(x^((x^y) | ((x-y)^x))));
+ }
+
+ /**
+ * Return a Mask<T> which is set if x > y
+ */
+ static Mask<T> is_gt(T x, T y)
+ {
+ return Mask<T>::is_lt(y, x);
+ }
+
+ /**
+ * Return a Mask<T> which is set if x <= y
+ */
+ static Mask<T> is_lte(T x, T y)
+ {
+ return ~Mask<T>::is_gt(x, y);
+ }
+
+ /**
+ * Return a Mask<T> which is set if x >= y
+ */
+ static Mask<T> is_gte(T x, T y)
+ {
+ return ~Mask<T>::is_lt(x, y);
+ }
+
+ static Mask<T> is_within_range(T v, T l, T u)
+ {
+ //return Mask<T>::is_gte(v, l) & Mask<T>::is_lte(v, u);
+
+ const T v_lt_l = v^((v^l) | ((v-l)^v));
+ const T v_gt_u = u^((u^v) | ((u-v)^u));
+ const T either = v_lt_l | v_gt_u;
+ return ~Mask<T>(expand_top_bit(either));
+ }
+
+ static Mask<T> is_any_of(T v, std::initializer_list<T> accepted)
+ {
+ T accept = 0;
+
+ for(auto a: accepted)
+ {
+ const T diff = a ^ v;
+ const T eq_zero = ~diff & (diff - 1);
+ accept |= eq_zero;
+ }
+
+ return Mask<T>(expand_top_bit(accept));
+ }
+
+ /**
+ * AND-combine two masks
+ */
+ Mask<T>& operator&=(Mask<T> o)
+ {
+ m_mask &= o.value();
+ return (*this);
+ }
+
+ /**
+ * XOR-combine two masks
+ */
+ Mask<T>& operator^=(Mask<T> o)
+ {
+ m_mask ^= o.value();
+ return (*this);
+ }
+
+ /**
+ * OR-combine two masks
+ */
+ Mask<T>& operator|=(Mask<T> o)
+ {
+ m_mask |= o.value();
+ return (*this);
+ }
+
+ /**
+ * AND-combine two masks
+ */
+ friend Mask<T> operator&(Mask<T> x, Mask<T> y)
+ {
+ return Mask<T>(x.value() & y.value());
+ }
+
+ /**
+ * XOR-combine two masks
+ */
+ friend Mask<T> operator^(Mask<T> x, Mask<T> y)
+ {
+ return Mask<T>(x.value() ^ y.value());
+ }
+
+ /**
+ * OR-combine two masks
+ */
+ friend Mask<T> operator|(Mask<T> x, Mask<T> y)
+ {
+ return Mask<T>(x.value() | y.value());
+ }
+
+ /**
+ * Negate this mask
+ */
+ Mask<T> operator~() const
+ {
+ return Mask<T>(~value());
+ }
+
+ /**
+ * Return x if the mask is set, or otherwise zero
+ */
+ T if_set_return(T x) const
+ {
+ return m_mask & x;
+ }
+
+ /**
+ * Return x if the mask is cleared, or otherwise zero
+ */
+ T if_not_set_return(T x) const
+ {
+ return ~m_mask & x;
+ }
+
+ /**
+ * If this mask is set, return x, otherwise return y
+ */
+ T select(T x, T y) const
+ {
+ // (x & value()) | (y & ~value())
+ return static_cast<T>(y ^ (value() & (x ^ y)));
+ }
+
+ T select_and_unpoison(T x, T y) const
+ {
+ T r = this->select(x, y);
+ CT::unpoison(r);
+ return r;
+ }
+
+ /**
+ * If this mask is set, return x, otherwise return y
+ */
+ Mask<T> select_mask(Mask<T> x, Mask<T> y) const
+ {
+ return Mask<T>(select(x.value(), y.value()));
+ }
+
+ /**
+ * Conditionally set output to x or y, depending on if mask is set or
+ * cleared (resp)
+ */
+ void select_n(T output[], const T x[], const T y[], size_t len) const
+ {
+ for(size_t i = 0; i != len; ++i)
+ output[i] = this->select(x[i], y[i]);
+ }
+
+ /**
+ * If this mask is set, zero out buf, otherwise do nothing
+ */
+ void if_set_zero_out(T buf[], size_t elems)
+ {
+ for(size_t i = 0; i != elems; ++i)
+ {
+ buf[i] = this->if_not_set_return(buf[i]);
+ }
+ }
+
+ /**
+ * Return the value of the mask, unpoisoned
+ */
+ T unpoisoned_value() const
+ {
+ T r = value();
+ CT::unpoison(r);
+ return r;
+ }
+
+ /**
+ * Return true iff this mask is set
+ */
+ bool is_set() const
+ {
+ return unpoisoned_value() != 0;
+ }
+
+ /**
+ * Return the underlying value of the mask
+ */
+ T value() const
+ {
+ return m_mask;
+ }
+
+ private:
+ Mask(T m) : m_mask(m) {}
+
+ T m_mask;
+ };
+
+template<typename T>
+inline Mask<T> conditional_copy_mem(T cnd,
+ T* to,
+ const T* from0,
+ const T* from1,
+ size_t elems)
+ {
+ const auto mask = CT::Mask<T>::expand(cnd);
+ mask.select_n(to, from0, from1, elems);
+ return mask;
+ }
+
+template<typename T>
+inline void conditional_swap(bool cnd, T& x, T& y)
+ {
+ const auto swap = CT::Mask<T>::expand(cnd);
+
+ T t0 = swap.select(y, x);
+ T t1 = swap.select(x, y);
+ x = t0;
+ y = t1;
+ }
+
+template<typename T>
+inline void conditional_swap_ptr(bool cnd, T& x, T& y)
+ {
+ uintptr_t xp = reinterpret_cast<uintptr_t>(x);
+ uintptr_t yp = reinterpret_cast<uintptr_t>(y);
+
+ conditional_swap<uintptr_t>(cnd, xp, yp);
+
+ x = reinterpret_cast<T>(xp);
+ y = reinterpret_cast<T>(yp);
+ }
+
+/**
+* If bad_mask is unset, return in[delim_idx:input_length] copied to
+* new buffer. If bad_mask is set, return an all zero vector of
+* unspecified length.
+*/
+secure_vector<uint8_t> copy_output(CT::Mask<uint8_t> bad_input,
+ const uint8_t input[],
+ size_t input_length,
+ size_t delim_idx);
+
+secure_vector<uint8_t> strip_leading_zeros(const uint8_t in[], size_t length);
+
+inline secure_vector<uint8_t> strip_leading_zeros(const secure_vector<uint8_t>& in)
+ {
+ return strip_leading_zeros(in.data(), in.size());
+ }
+
+}
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/utils/data_src.cpp b/comm/third_party/botan/src/lib/utils/data_src.cpp
new file mode 100644
index 0000000000..c5689534ff
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/data_src.cpp
@@ -0,0 +1,214 @@
+/*
+* DataSource
+* (C) 1999-2007 Jack Lloyd
+* 2005 Matthew Gregan
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/data_src.h>
+#include <botan/exceptn.h>
+#include <algorithm>
+#include <istream>
+
+#if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
+ #include <fstream>
+#endif
+
+namespace Botan {
+
+/*
+* Read a single byte from the DataSource
+*/
+size_t DataSource::read_byte(uint8_t& out)
+ {
+ return read(&out, 1);
+ }
+
+/*
+* Peek a single byte from the DataSource
+*/
+size_t DataSource::peek_byte(uint8_t& out) const
+ {
+ return peek(&out, 1, 0);
+ }
+
+/*
+* Discard the next N bytes of the data
+*/
+size_t DataSource::discard_next(size_t n)
+ {
+ uint8_t buf[64] = { 0 };
+ size_t discarded = 0;
+
+ while(n)
+ {
+ const size_t got = this->read(buf, std::min(n, sizeof(buf)));
+ discarded += got;
+ n -= got;
+
+ if(got == 0)
+ break;
+ }
+
+ return discarded;
+ }
+
+/*
+* Read from a memory buffer
+*/
+size_t DataSource_Memory::read(uint8_t out[], size_t length)
+ {
+ const size_t got = std::min<size_t>(m_source.size() - m_offset, length);
+ copy_mem(out, m_source.data() + m_offset, got);
+ m_offset += got;
+ return got;
+ }
+
+bool DataSource_Memory::check_available(size_t n)
+ {
+ return (n <= (m_source.size() - m_offset));
+ }
+
+/*
+* Peek into a memory buffer
+*/
+size_t DataSource_Memory::peek(uint8_t out[], size_t length,
+ size_t peek_offset) const
+ {
+ const size_t bytes_left = m_source.size() - m_offset;
+ if(peek_offset >= bytes_left) return 0;
+
+ const size_t got = std::min(bytes_left - peek_offset, length);
+ copy_mem(out, &m_source[m_offset + peek_offset], got);
+ return got;
+ }
+
+/*
+* Check if the memory buffer is empty
+*/
+bool DataSource_Memory::end_of_data() const
+ {
+ return (m_offset == m_source.size());
+ }
+
+/*
+* DataSource_Memory Constructor
+*/
+DataSource_Memory::DataSource_Memory(const std::string& in) :
+ m_source(cast_char_ptr_to_uint8(in.data()),
+ cast_char_ptr_to_uint8(in.data()) + in.length()),
+ m_offset(0)
+ {
+ }
+
+/*
+* Read from a stream
+*/
+size_t DataSource_Stream::read(uint8_t out[], size_t length)
+ {
+ m_source.read(cast_uint8_ptr_to_char(out), length);
+ if(m_source.bad())
+ throw Stream_IO_Error("DataSource_Stream::read: Source failure");
+
+ const size_t got = static_cast<size_t>(m_source.gcount());
+ m_total_read += got;
+ return got;
+ }
+
+bool DataSource_Stream::check_available(size_t n)
+ {
+ const std::streampos orig_pos = m_source.tellg();
+ m_source.seekg(0, std::ios::end);
+ const size_t avail = static_cast<size_t>(m_source.tellg() - orig_pos);
+ m_source.seekg(orig_pos);
+ return (avail >= n);
+ }
+
+/*
+* Peek into a stream
+*/
+size_t DataSource_Stream::peek(uint8_t out[], size_t length, size_t offset) const
+ {
+ if(end_of_data())
+ throw Invalid_State("DataSource_Stream: Cannot peek when out of data");
+
+ size_t got = 0;
+
+ if(offset)
+ {
+ secure_vector<uint8_t> 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");
+ got = static_cast<size_t>(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<size_t>(m_source.gcount());
+ }
+
+ if(m_source.eof())
+ m_source.clear();
+ m_source.seekg(m_total_read, std::ios::beg);
+
+ return got;
+ }
+
+/*
+* Check if the stream is empty or in error
+*/
+bool DataSource_Stream::end_of_data() const
+ {
+ return (!m_source.good());
+ }
+
+/*
+* Return a human-readable ID for this stream
+*/
+std::string DataSource_Stream::id() const
+ {
+ return m_identifier;
+ }
+
+#if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
+
+/*
+* DataSource_Stream Constructor
+*/
+DataSource_Stream::DataSource_Stream(const std::string& path,
+ bool use_binary) :
+ m_identifier(path),
+ m_source_memory(new std::ifstream(path, use_binary ? std::ios::binary : std::ios::in)),
+ m_source(*m_source_memory),
+ m_total_read(0)
+ {
+ if(!m_source.good())
+ {
+ throw Stream_IO_Error("DataSource: Failure opening file " + path);
+ }
+ }
+
+#endif
+
+/*
+* DataSource_Stream Constructor
+*/
+DataSource_Stream::DataSource_Stream(std::istream& in,
+ const std::string& name) :
+ m_identifier(name),
+ m_source(in),
+ m_total_read(0)
+ {
+ }
+
+DataSource_Stream::~DataSource_Stream()
+ {
+ // for ~unique_ptr
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/utils/data_src.h b/comm/third_party/botan/src/lib/utils/data_src.h
new file mode 100644
index 0000000000..09c1bffdf7
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/data_src.h
@@ -0,0 +1,181 @@
+/*
+* DataSource
+* (C) 1999-2007 Jack Lloyd
+* 2012 Markus Wanner
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_DATA_SRC_H_
+#define BOTAN_DATA_SRC_H_
+
+#include <botan/secmem.h>
+#include <string>
+#include <iosfwd>
+
+namespace Botan {
+
+/**
+* This class represents an abstract data source object.
+*/
+class BOTAN_PUBLIC_API(2,0) DataSource
+ {
+ public:
+ /**
+ * Read from the source. Moves the internal offset so that every
+ * call to read will return a new portion of the source.
+ *
+ * @param out the byte array to write the result to
+ * @param length the length of the byte array out
+ * @return length in bytes that was actually read and put
+ * into out
+ */
+ virtual size_t read(uint8_t out[], size_t length) BOTAN_WARN_UNUSED_RESULT = 0;
+
+ virtual bool check_available(size_t n) = 0;
+
+ /**
+ * Read from the source but do not modify the internal
+ * offset. Consecutive calls to peek() will return portions of
+ * the source starting at the same position.
+ *
+ * @param out the byte array to write the output to
+ * @param length the length of the byte array out
+ * @param peek_offset the offset into the stream to read at
+ * @return length in bytes that was actually read and put
+ * into out
+ */
+ virtual size_t peek(uint8_t out[], size_t length, size_t peek_offset) const BOTAN_WARN_UNUSED_RESULT = 0;
+
+ /**
+ * Test whether the source still has data that can be read.
+ * @return true if there is no more data to read, false otherwise
+ */
+ virtual bool end_of_data() const = 0;
+ /**
+ * return the id of this data source
+ * @return std::string representing the id of this data source
+ */
+ virtual std::string id() const { return ""; }
+
+ /**
+ * Read one byte.
+ * @param out the byte to read to
+ * @return length in bytes that was actually read and put
+ * into out
+ */
+ size_t read_byte(uint8_t& out);
+
+ /**
+ * Peek at one byte.
+ * @param out an output byte
+ * @return length in bytes that was actually read and put
+ * into out
+ */
+ size_t peek_byte(uint8_t& out) const;
+
+ /**
+ * Discard the next N bytes of the data
+ * @param N the number of bytes to discard
+ * @return number of bytes actually discarded
+ */
+ size_t discard_next(size_t N);
+
+ /**
+ * @return number of bytes read so far.
+ */
+ virtual size_t get_bytes_read() const = 0;
+
+ DataSource() = default;
+ virtual ~DataSource() = default;
+ DataSource& operator=(const DataSource&) = delete;
+ DataSource(const DataSource&) = delete;
+ };
+
+/**
+* This class represents a Memory-Based DataSource
+*/
+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;
+ bool check_available(size_t n) override;
+ bool end_of_data() const override;
+
+ /**
+ * Construct a memory source that reads from a string
+ * @param in the string to read from
+ */
+ explicit DataSource_Memory(const std::string& in);
+
+ /**
+ * Construct a memory source that reads from a byte array
+ * @param in the byte array to read from
+ * @param length the length of the byte array
+ */
+ DataSource_Memory(const uint8_t in[], size_t length) :
+ m_source(in, in + length), m_offset(0) {}
+
+ /**
+ * Construct a memory source that reads from a secure_vector
+ * @param in the MemoryRegion to read from
+ */
+ explicit DataSource_Memory(const secure_vector<uint8_t>& in) :
+ m_source(in), m_offset(0) {}
+
+ /**
+ * Construct a memory source that reads from a std::vector
+ * @param in the MemoryRegion to read from
+ */
+ explicit DataSource_Memory(const std::vector<uint8_t>& in) :
+ m_source(in.begin(), in.end()), m_offset(0) {}
+
+ size_t get_bytes_read() const override { return m_offset; }
+ private:
+ secure_vector<uint8_t> m_source;
+ size_t m_offset;
+ };
+
+/**
+* This class represents a Stream-Based DataSource.
+*/
+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;
+ bool check_available(size_t n) override;
+ bool end_of_data() const override;
+ std::string id() const override;
+
+ DataSource_Stream(std::istream&,
+ const std::string& id = "<std::istream>");
+
+#if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
+ /**
+ * Construct a Stream-Based DataSource from filesystem path
+ * @param file the path to the file
+ * @param use_binary whether to treat the file as binary or not
+ */
+ DataSource_Stream(const std::string& file, bool use_binary = false);
+#endif
+
+ DataSource_Stream(const DataSource_Stream&) = delete;
+
+ DataSource_Stream& operator=(const DataSource_Stream&) = delete;
+
+ ~DataSource_Stream();
+
+ size_t get_bytes_read() const override { return m_total_read; }
+ private:
+ const std::string m_identifier;
+
+ std::unique_ptr<std::istream> m_source_memory;
+ std::istream& m_source;
+ size_t m_total_read;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/utils/database.h b/comm/third_party/botan/src/lib/utils/database.h
new file mode 100644
index 0000000000..713d4fc59c
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/database.h
@@ -0,0 +1,88 @@
+/*
+* SQL database interface
+* (C) 2014 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_SQL_DATABASE_H_
+#define BOTAN_SQL_DATABASE_H_
+
+#include <botan/types.h>
+#include <botan/exceptn.h>
+#include <string>
+#include <chrono>
+#include <vector>
+
+namespace Botan {
+
+class BOTAN_PUBLIC_API(2,0) SQL_Database
+ {
+ public:
+
+ class BOTAN_PUBLIC_API(2,0) SQL_DB_Error final : public Exception
+ {
+ public:
+ explicit SQL_DB_Error(const std::string& what) :
+ Exception("SQL database", what),
+ m_rc(0)
+ {}
+
+ SQL_DB_Error(const std::string& what, int rc) :
+ Exception("SQL database", what),
+ m_rc(rc)
+ {}
+
+ ErrorType error_type() const noexcept override { return Botan::ErrorType::DatabaseError; }
+
+ int error_code() const noexcept override { return m_rc; }
+ private:
+ int m_rc;
+ };
+
+ class BOTAN_PUBLIC_API(2,0) Statement
+ {
+ public:
+ /* Bind statement parameters */
+ virtual void bind(int column, const std::string& str) = 0;
+
+ virtual void bind(int column, size_t i) = 0;
+
+ virtual void bind(int column, std::chrono::system_clock::time_point time) = 0;
+
+ virtual void bind(int column, const std::vector<uint8_t>& blob) = 0;
+
+ virtual void bind(int column, const uint8_t* data, size_t len) = 0;
+
+ /* Get output */
+ virtual std::pair<const uint8_t*, size_t> get_blob(int column) = 0;
+
+ virtual std::string get_str(int column) = 0;
+
+ virtual size_t get_size_t(int column) = 0;
+
+ /* Run to completion */
+ virtual size_t spin() = 0;
+
+ /* Maybe update */
+ virtual bool step() = 0;
+
+ virtual ~Statement() = default;
+ };
+
+ /*
+ * Create a new statement for execution.
+ * Use ?1, ?2, ?3, etc for parameters to set later with bind
+ */
+ virtual std::shared_ptr<Statement> new_statement(const std::string& base_sql) const = 0;
+
+ virtual size_t row_count(const std::string& table_name) = 0;
+
+ virtual void create_table(const std::string& table_schema) = 0;
+
+ virtual ~SQL_Database() = default;
+};
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/utils/donna128.h b/comm/third_party/botan/src/lib/utils/donna128.h
new file mode 100644
index 0000000000..ff571906d8
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/donna128.h
@@ -0,0 +1,143 @@
+/*
+* A minimal 128-bit integer type for curve25519-donna
+* (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_
+
+#include <botan/mul128.h>
+
+namespace Botan {
+
+class donna128 final
+ {
+ public:
+ donna128(uint64_t ll = 0, uint64_t hh = 0) { l = ll; h = hh; }
+
+ donna128(const donna128&) = default;
+ donna128& operator=(const donna128&) = default;
+
+ friend donna128 operator>>(const donna128& x, size_t shift)
+ {
+ donna128 z = x;
+ if(shift > 0)
+ {
+ const uint64_t carry = z.h << (64 - shift);
+ z.h = (z.h >> shift);
+ z.l = (z.l >> shift) | carry;
+ }
+ return z;
+ }
+
+ friend donna128 operator<<(const donna128& x, size_t shift)
+ {
+ donna128 z = x;
+ if(shift > 0)
+ {
+ const uint64_t carry = z.l >> (64 - shift);
+ z.l = (z.l << shift);
+ z.h = (z.h << shift) | carry;
+ }
+ return z;
+ }
+
+ friend uint64_t operator&(const donna128& x, uint64_t mask)
+ {
+ return x.l & mask;
+ }
+
+ uint64_t operator&=(uint64_t mask)
+ {
+ h = 0;
+ l &= mask;
+ return l;
+ }
+
+ donna128& operator+=(const donna128& x)
+ {
+ l += x.l;
+ h += x.h;
+
+ const uint64_t carry = (l < x.l);
+ h += carry;
+ return *this;
+ }
+
+ donna128& operator+=(uint64_t x)
+ {
+ l += x;
+ const uint64_t carry = (l < x);
+ h += carry;
+ return *this;
+ }
+
+ uint64_t lo() const { return l; }
+ uint64_t hi() const { return h; }
+ private:
+ uint64_t h = 0, l = 0;
+ };
+
+inline donna128 operator*(const donna128& x, uint64_t y)
+ {
+ BOTAN_ARG_CHECK(x.hi() == 0, "High 64 bits of donna128 set to zero during multiply");
+
+ uint64_t lo = 0, hi = 0;
+ mul64x64_128(x.lo(), y, &lo, &hi);
+ return donna128(lo, hi);
+ }
+
+inline donna128 operator*(uint64_t y, const donna128& x)
+ {
+ return x * y;
+ }
+
+inline donna128 operator+(const donna128& x, const donna128& y)
+ {
+ donna128 z = x;
+ z += y;
+ return z;
+ }
+
+inline donna128 operator+(const donna128& x, uint64_t y)
+ {
+ donna128 z = x;
+ z += y;
+ return z;
+ }
+
+inline donna128 operator|(const donna128& x, const donna128& y)
+ {
+ return donna128(x.lo() | y.lo(), x.hi() | y.hi());
+ }
+
+inline uint64_t carry_shift(const donna128& a, size_t shift)
+ {
+ return (a >> shift).lo();
+ }
+
+inline uint64_t combine_lower(const donna128& a, size_t s1,
+ const donna128& b, size_t s2)
+ {
+ donna128 z = (a >> s1) | (b << s2);
+ return z.lo();
+ }
+
+#if defined(BOTAN_TARGET_HAS_NATIVE_UINT128)
+inline uint64_t carry_shift(const uint128_t a, size_t shift)
+ {
+ return static_cast<uint64_t>(a >> shift);
+ }
+
+inline uint64_t combine_lower(const uint128_t a, size_t s1,
+ const uint128_t b, size_t s2)
+ {
+ return static_cast<uint64_t>((a >> s1) | (b << s2));
+ }
+#endif
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/utils/dyn_load/dyn_load.cpp b/comm/third_party/botan/src/lib/utils/dyn_load/dyn_load.cpp
new file mode 100644
index 0000000000..b32fe4b3ab
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/dyn_load/dyn_load.cpp
@@ -0,0 +1,82 @@
+/*
+* Dynamically Loaded Object
+* (C) 2010 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/dyn_load.h>
+#include <botan/exceptn.h>
+
+#if defined(BOTAN_TARGET_OS_HAS_POSIX1)
+ #include <dlfcn.h>
+#elif defined(BOTAN_TARGET_OS_HAS_WIN32)
+ #define NOMINMAX 1
+ #define _WINSOCKAPI_ // stop windows.h including winsock.h
+ #include <windows.h>
+#endif
+
+namespace Botan {
+
+namespace {
+
+void raise_runtime_loader_exception(const std::string& lib_name,
+ const char* msg)
+ {
+ const std::string ex_msg =
+ "Failed to load " + lib_name + ": " +
+ (msg ? msg : "Unknown error");
+
+ throw System_Error(ex_msg, 0);
+ }
+
+}
+
+Dynamically_Loaded_Library::Dynamically_Loaded_Library(
+ const std::string& library) :
+ m_lib_name(library), m_lib(nullptr)
+ {
+#if defined(BOTAN_TARGET_OS_HAS_POSIX1)
+ m_lib = ::dlopen(m_lib_name.c_str(), RTLD_LAZY);
+
+ if(!m_lib)
+ raise_runtime_loader_exception(m_lib_name, ::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
+
+ if(!m_lib)
+ raise_runtime_loader_exception(m_lib_name, "Dynamic load not supported");
+ }
+
+Dynamically_Loaded_Library::~Dynamically_Loaded_Library()
+ {
+#if defined(BOTAN_TARGET_OS_HAS_POSIX1)
+ ::dlclose(m_lib);
+#elif defined(BOTAN_TARGET_OS_HAS_WIN32)
+ ::FreeLibrary((HMODULE)m_lib);
+#endif
+ }
+
+void* Dynamically_Loaded_Library::resolve_symbol(const std::string& symbol)
+ {
+ 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<void*>(::GetProcAddress((HMODULE)m_lib, symbol.c_str()));
+#endif
+
+ if(!addr)
+ throw Invalid_Argument("Failed to resolve symbol " + symbol +
+ " in " + m_lib_name);
+
+ return addr;
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/utils/dyn_load/dyn_load.h b/comm/third_party/botan/src/lib/utils/dyn_load/dyn_load.h
new file mode 100644
index 0000000000..3caf65f277
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/dyn_load/dyn_load.h
@@ -0,0 +1,68 @@
+/*
+* Dynamically Loaded Object
+* (C) 2010 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_DYNAMIC_LOADER_H_
+#define BOTAN_DYNAMIC_LOADER_H_
+
+#include <botan/types.h>
+#include <string>
+
+namespace Botan {
+
+/**
+* Represents a DLL or shared object
+*/
+class BOTAN_PUBLIC_API(2,0) Dynamically_Loaded_Library final
+ {
+ public:
+ /**
+ * Load a DLL (or fail with an exception)
+ * @param lib_name name or path to a library
+ *
+ * If you don't use a full path, the search order will be defined
+ * by whatever the system linker does by default. Always using fully
+ * qualified pathnames can help prevent code injection attacks (eg
+ * via manipulation of LD_LIBRARY_PATH on Linux)
+ */
+ Dynamically_Loaded_Library(const std::string& lib_name);
+
+ /**
+ * Unload the DLL
+ * @warning Any pointers returned by resolve()/resolve_symbol()
+ * should not be used after this destructor runs.
+ */
+ ~Dynamically_Loaded_Library();
+
+ /**
+ * Load a symbol (or fail with an exception)
+ * @param symbol names the symbol to load
+ * @return address of the loaded symbol
+ */
+ void* resolve_symbol(const std::string& symbol);
+
+ /**
+ * Convenience function for casting symbol to the right type
+ * @param symbol names the symbol to load
+ * @return address of the loaded symbol
+ */
+ template<typename T>
+ T resolve(const std::string& symbol)
+ {
+ return reinterpret_cast<T>(resolve_symbol(symbol));
+ }
+
+ private:
+ Dynamically_Loaded_Library(const Dynamically_Loaded_Library&);
+ Dynamically_Loaded_Library& operator=(const Dynamically_Loaded_Library&);
+
+ std::string m_lib_name;
+ void* m_lib;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/utils/dyn_load/info.txt b/comm/third_party/botan/src/lib/utils/dyn_load/info.txt
new file mode 100644
index 0000000000..f20d000246
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/dyn_load/info.txt
@@ -0,0 +1,18 @@
+<defines>
+DYNAMIC_LOADER -> 20160310
+</defines>
+
+load_on dep
+
+<os_features>
+posix1
+win32
+</os_features>
+
+<libs>
+android -> dl
+linux -> dl
+solaris -> dl
+macos -> dl
+hurd -> dl
+</libs>
diff --git a/comm/third_party/botan/src/lib/utils/exceptn.cpp b/comm/third_party/botan/src/lib/utils/exceptn.cpp
new file mode 100644
index 0000000000..e6d067f348
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/exceptn.cpp
@@ -0,0 +1,183 @@
+/*
+* (C) 2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/exceptn.h>
+
+namespace Botan {
+
+std::string to_string(ErrorType type)
+ {
+ switch(type)
+ {
+ case ErrorType::Unknown:
+ return "Unknown";
+ case ErrorType::SystemError:
+ return "SystemError";
+ case ErrorType::NotImplemented:
+ return "NotImplemented";
+ case ErrorType::OutOfMemory:
+ return "OutOfMemory";
+ case ErrorType::InternalError:
+ return "InternalError";
+ case ErrorType::IoError:
+ return "IoError";
+ case ErrorType::InvalidObjectState :
+ return "InvalidObjectState";
+ case ErrorType::KeyNotSet:
+ return "KeyNotSet";
+ case ErrorType::InvalidArgument:
+ return "InvalidArgument";
+ case ErrorType::InvalidKeyLength:
+ return "InvalidKeyLength";
+ case ErrorType::InvalidNonceLength:
+ return "InvalidNonceLength";
+ case ErrorType::LookupError:
+ return "LookupError";
+ case ErrorType::EncodingFailure:
+ return "EncodingFailure";
+ case ErrorType::DecodingFailure:
+ return "DecodingFailure";
+ case ErrorType::TLSError:
+ return "TLSError";
+ case ErrorType::HttpError:
+ return "HttpError";
+ case ErrorType::InvalidTag:
+ return "InvalidTag";
+ case ErrorType::RoughtimeError:
+ return "RoughtimeError";
+ case ErrorType::OpenSSLError :
+ return "OpenSSLError";
+ case ErrorType::CommonCryptoError:
+ return "CommonCryptoError";
+ case ErrorType::Pkcs11Error:
+ return "Pkcs11Error";
+ case ErrorType::TPMError:
+ return "TPMError";
+ case ErrorType::DatabaseError:
+ return "DatabaseError";
+ case ErrorType::ZlibError :
+ return "ZlibError";
+ case ErrorType::Bzip2Error:
+ return "Bzip2Error" ;
+ case ErrorType::LzmaError:
+ return "LzmaError";
+ }
+
+ // No default case in above switch so compiler warns
+ return "Unrecognized Botan error";
+ }
+
+Exception::Exception(const std::string& msg) : m_msg(msg)
+ {}
+
+Exception::Exception(const std::string& msg, const std::exception& e) :
+ m_msg(msg + " failed with " + std::string(e.what()))
+ {}
+
+Exception::Exception(const char* prefix, const std::string& msg) :
+ m_msg(std::string(prefix) + " " + msg)
+ {}
+
+Invalid_Argument::Invalid_Argument(const std::string& msg) :
+ Exception(msg)
+ {}
+
+Invalid_Argument::Invalid_Argument(const std::string& msg, const std::string& where) :
+ Exception(msg + " in " + where)
+ {}
+
+Invalid_Argument::Invalid_Argument(const std::string& msg, const std::exception& e) :
+ Exception(msg, e) {}
+
+Lookup_Error::Lookup_Error(const std::string& type,
+ const std::string& algo,
+ const std::string& provider) :
+ Exception("Unavailable " + type + " " + algo +
+ (provider.empty() ? std::string("") : (" for provider " + provider)))
+ {}
+
+Internal_Error::Internal_Error(const std::string& err) :
+ Exception("Internal error: " + err)
+ {}
+
+Invalid_Key_Length::Invalid_Key_Length(const std::string& name, size_t length) :
+ Invalid_Argument(name + " cannot accept a key of length " +
+ std::to_string(length))
+ {}
+
+Invalid_IV_Length::Invalid_IV_Length(const std::string& mode, size_t bad_len) :
+ Invalid_Argument("IV length " + std::to_string(bad_len) +
+ " is invalid for " + mode)
+ {}
+
+Key_Not_Set::Key_Not_Set(const std::string& algo) :
+ Invalid_State("Key not set in " + algo)
+ {}
+
+Policy_Violation::Policy_Violation(const std::string& err) :
+ Invalid_State("Policy violation: " + err) {}
+
+PRNG_Unseeded::PRNG_Unseeded(const std::string& algo) :
+ Invalid_State("PRNG not seeded: " + algo)
+ {}
+
+Algorithm_Not_Found::Algorithm_Not_Found(const std::string& name) :
+ Lookup_Error("Could not find any algorithm named \"" + name + "\"")
+ {}
+
+No_Provider_Found::No_Provider_Found(const std::string& name) :
+ Exception("Could not find any provider for algorithm named \"" + name + "\"")
+ {}
+
+Provider_Not_Found::Provider_Not_Found(const std::string& algo, const std::string& provider) :
+ Lookup_Error("Could not find provider '" + provider + "' for " + algo)
+ {}
+
+Invalid_Algorithm_Name::Invalid_Algorithm_Name(const std::string& name):
+ Invalid_Argument("Invalid algorithm name: " + name)
+ {}
+
+Encoding_Error::Encoding_Error(const std::string& name) :
+ Invalid_Argument("Encoding error: " + name)
+ {}
+
+Decoding_Error::Decoding_Error(const std::string& name) :
+ Invalid_Argument(name)
+ {}
+
+Decoding_Error::Decoding_Error(const std::string& msg, const std::exception& e) :
+ Invalid_Argument(msg, e)
+ {}
+
+Decoding_Error::Decoding_Error(const std::string& name, const char* exception_message) :
+ Invalid_Argument(name + " failed with exception " + exception_message) {}
+
+Invalid_Authentication_Tag::Invalid_Authentication_Tag(const std::string& msg) :
+ Exception("Invalid authentication tag: " + msg)
+ {}
+
+Invalid_OID::Invalid_OID(const std::string& oid) :
+ Decoding_Error("Invalid ASN.1 OID: " + oid)
+ {}
+
+Stream_IO_Error::Stream_IO_Error(const std::string& err) :
+ Exception("I/O error: " + err)
+ {}
+
+System_Error::System_Error(const std::string& msg, int err_code) :
+ Exception(msg + " error code " + std::to_string(err_code)),
+ m_error_code(err_code)
+ {}
+
+Self_Test_Failure::Self_Test_Failure(const std::string& err) :
+ Internal_Error("Self test failed: " + err)
+ {}
+
+Not_Implemented::Not_Implemented(const std::string& err) :
+ Exception("Not implemented", err)
+ {}
+
+}
diff --git a/comm/third_party/botan/src/lib/utils/exceptn.h b/comm/third_party/botan/src/lib/utils/exceptn.h
new file mode 100644
index 0000000000..442ec91e6e
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/exceptn.h
@@ -0,0 +1,441 @@
+/*
+* Exceptions
+* (C) 1999-2009,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_EXCEPTION_H_
+#define BOTAN_EXCEPTION_H_
+
+#include <botan/types.h>
+#include <exception>
+#include <string>
+
+namespace Botan {
+
+/**
+* Different types of errors that might occur
+*/
+enum class ErrorType {
+ /** Some unknown error */
+ Unknown = 1,
+ /** An error while calling a system interface */
+ SystemError,
+ /** An operation seems valid, but not supported by the current version */
+ NotImplemented,
+ /** Memory allocation failure */
+ OutOfMemory,
+ /** An internal error occurred */
+ InternalError,
+ /** An I/O error occurred */
+ IoError,
+
+ /** Invalid object state */
+ InvalidObjectState = 100,
+ /** A key was not set on an object when this is required */
+ KeyNotSet,
+ /** The application provided an argument which is invalid */
+ InvalidArgument,
+ /** A key with invalid length was provided */
+ InvalidKeyLength,
+ /** A nonce with invalid length was provided */
+ InvalidNonceLength,
+ /** An object type was requested but cannot be found */
+ LookupError,
+ /** Encoding a message or datum failed */
+ EncodingFailure,
+ /** Decoding a message or datum failed */
+ DecodingFailure,
+ /** A TLS error (error_code will be the alert type) */
+ TLSError,
+ /** An error during an HTTP operation */
+ HttpError,
+ /** A message with an invalid authentication tag was detected */
+ InvalidTag,
+ /** An error during Roughtime validation */
+ RoughtimeError,
+
+ /** An error when calling OpenSSL */
+ OpenSSLError = 200,
+ /** An error when interacting with CommonCrypto API */
+ CommonCryptoError,
+ /** An error when interacting with a PKCS11 device */
+ Pkcs11Error,
+ /** An error when interacting with a TPM device */
+ TPMError,
+ /** An error when interacting with a database */
+ DatabaseError,
+
+ /** An error when interacting with zlib */
+ ZlibError = 300,
+ /** An error when interacting with bzip2 */
+ Bzip2Error,
+ /** An error when interacting with lzma */
+ LzmaError,
+
+};
+
+//! \brief Convert an ErrorType to string
+std::string BOTAN_PUBLIC_API(2,11) to_string(ErrorType type);
+
+/**
+* Base class for all exceptions thrown by the library
+*/
+class BOTAN_PUBLIC_API(2,0) Exception : public std::exception
+ {
+ public:
+ /**
+ * Return a descriptive string which is hopefully comprehensible to
+ * a developer. It will likely not be useful for an end user.
+ *
+ * The string has no particular format, and the content of exception
+ * messages may change from release to release. Thus the main use of this
+ * function is for logging or debugging.
+ */
+ const char* what() const noexcept override { return m_msg.c_str(); }
+
+ /**
+ * Return the "type" of error which occurred.
+ */
+ virtual ErrorType error_type() const noexcept { return Botan::ErrorType::Unknown; }
+
+ /**
+ * Return an error code associated with this exception, or otherwise 0.
+ *
+ * The domain of this error varies depending on the source, for example on
+ * POSIX systems it might be errno, while on a Windows system it might be
+ * the result of GetLastError or WSAGetLastError. For error_type() is
+ * OpenSSLError, it will (if nonzero) be an OpenSSL error code from
+ * ERR_get_error.
+ */
+ virtual int error_code() const noexcept { return 0; }
+
+ /**
+ * Avoid throwing base Exception, use a subclass
+ */
+ explicit Exception(const std::string& msg);
+
+ /**
+ * Avoid throwing base Exception, use a subclass
+ */
+ Exception(const char* prefix, const std::string& msg);
+
+ /**
+ * Avoid throwing base Exception, use a subclass
+ */
+ Exception(const std::string& msg, const std::exception& e);
+
+ private:
+ std::string m_msg;
+ };
+
+/**
+* An invalid argument was provided to an API call.
+*/
+class BOTAN_PUBLIC_API(2,0) Invalid_Argument : public Exception
+ {
+ public:
+ explicit Invalid_Argument(const std::string& msg);
+
+ explicit Invalid_Argument(const std::string& msg, const std::string& where);
+
+ Invalid_Argument(const std::string& msg, const std::exception& e);
+
+ ErrorType error_type() const noexcept override { return ErrorType::InvalidArgument; }
+ };
+
+/**
+* An invalid key length was used
+*/
+class BOTAN_PUBLIC_API(2,0) Invalid_Key_Length final : public Invalid_Argument
+ {
+ public:
+ Invalid_Key_Length(const std::string& name, size_t length);
+ ErrorType error_type() const noexcept override { return ErrorType::InvalidKeyLength; }
+ };
+
+/**
+* An invalid nonce length was used
+*/
+class BOTAN_PUBLIC_API(2,0) Invalid_IV_Length final : public Invalid_Argument
+ {
+ public:
+ Invalid_IV_Length(const std::string& mode, size_t bad_len);
+ ErrorType error_type() const noexcept override { return ErrorType::InvalidNonceLength; }
+ };
+
+/**
+* Invalid_Algorithm_Name Exception
+*/
+class BOTAN_PUBLIC_API(2,0) Invalid_Algorithm_Name final : public Invalid_Argument
+ {
+ public:
+ explicit Invalid_Algorithm_Name(const std::string& name);
+ };
+
+/**
+* Encoding_Error Exception
+*
+* This exception derives from Invalid_Argument for historical reasons, and it
+* does not make any real sense for it to do so. In a future major release this
+* exception type will derive directly from Exception instead.
+*/
+class BOTAN_PUBLIC_API(2,0) Encoding_Error final : public Invalid_Argument
+ {
+ public:
+ explicit Encoding_Error(const std::string& name);
+
+ ErrorType error_type() const noexcept override { return ErrorType::EncodingFailure; }
+ };
+
+/**
+* A decoding error occurred.
+*
+* This exception derives from Invalid_Argument for historical reasons, and it
+* does not make any real sense for it to do so. In a future major release this
+* exception type will derive directly from Exception instead.
+*/
+class BOTAN_PUBLIC_API(2,0) Decoding_Error : public Invalid_Argument
+ {
+ public:
+ explicit Decoding_Error(const std::string& name);
+
+ Decoding_Error(const std::string& name, const char* exception_message);
+
+ Decoding_Error(const std::string& msg, const std::exception& e);
+
+ ErrorType error_type() const noexcept override { return ErrorType::DecodingFailure; }
+ };
+
+/**
+* Invalid state was encountered. A request was made on an object while the
+* object was in a state where the operation cannot be performed.
+*/
+class BOTAN_PUBLIC_API(2,0) Invalid_State : public Exception
+ {
+ public:
+ explicit Invalid_State(const std::string& err) : Exception(err) {}
+
+ ErrorType error_type() const noexcept override { return ErrorType::InvalidObjectState; }
+ };
+
+/**
+* A PRNG was called on to produce output while still unseeded
+*/
+class BOTAN_PUBLIC_API(2,0) PRNG_Unseeded final : public Invalid_State
+ {
+ public:
+ explicit PRNG_Unseeded(const std::string& algo);
+ };
+
+/**
+* The key was not set on an object. This occurs with symmetric objects where
+* an operation which requires the key is called prior to set_key being called.
+*/
+class BOTAN_PUBLIC_API(2,4) Key_Not_Set : public Invalid_State
+ {
+ public:
+ explicit Key_Not_Set(const std::string& algo);
+
+ ErrorType error_type() const noexcept override { return ErrorType::KeyNotSet; }
+ };
+
+/**
+* A request was made for some kind of object which could not be located
+*/
+class BOTAN_PUBLIC_API(2,0) Lookup_Error : public Exception
+ {
+ public:
+ explicit Lookup_Error(const std::string& err) : Exception(err) {}
+
+ Lookup_Error(const std::string& type,
+ const std::string& algo,
+ const std::string& provider);
+
+ ErrorType error_type() const noexcept override { return ErrorType::LookupError; }
+ };
+
+/**
+* Algorithm_Not_Found Exception
+*
+* @warning This exception type will be removed in the future. Instead
+* just catch Lookup_Error.
+*/
+class BOTAN_PUBLIC_API(2,0) Algorithm_Not_Found final : public Lookup_Error
+ {
+ public:
+ explicit Algorithm_Not_Found(const std::string& name);
+ };
+
+/**
+* Provider_Not_Found is thrown when a specific provider was requested
+* but that provider is not available.
+*
+* @warning This exception type will be removed in the future. Instead
+* just catch Lookup_Error.
+*/
+class BOTAN_PUBLIC_API(2,0) Provider_Not_Found final : public Lookup_Error
+ {
+ public:
+ Provider_Not_Found(const std::string& algo, const std::string& provider);
+ };
+
+/**
+* An AEAD or MAC check detected a message modification
+*
+* In versions before 2.10, Invalid_Authentication_Tag was named
+* Integrity_Failure, it was renamed to make its usage more clear.
+*/
+class BOTAN_PUBLIC_API(2,0) Invalid_Authentication_Tag final : public Exception
+ {
+ public:
+ explicit Invalid_Authentication_Tag(const std::string& msg);
+
+ ErrorType error_type() const noexcept override { return ErrorType::InvalidTag; }
+ };
+
+/**
+* For compatability with older versions
+*/
+typedef Invalid_Authentication_Tag Integrity_Failure;
+
+/**
+* An error occurred while operating on an IO stream
+*/
+class BOTAN_PUBLIC_API(2,0) Stream_IO_Error final : public Exception
+ {
+ public:
+ explicit Stream_IO_Error(const std::string& err);
+
+ ErrorType error_type() const noexcept override { return ErrorType::IoError; }
+ };
+
+/**
+* System_Error
+*
+* This exception is thrown in the event of an error related to interacting
+* with the operating system.
+*
+* This exception type also (optionally) captures an integer error code eg
+* POSIX errno or Windows GetLastError.
+*/
+class BOTAN_PUBLIC_API(2,9) System_Error : public Exception
+ {
+ public:
+ System_Error(const std::string& msg) : Exception(msg), m_error_code(0) {}
+
+ System_Error(const std::string& msg, int err_code);
+
+ ErrorType error_type() const noexcept override { return ErrorType::SystemError; }
+
+ int error_code() const noexcept override { return m_error_code; }
+
+ private:
+ int m_error_code;
+ };
+
+/**
+* An internal error occurred. If observed, please file a bug.
+*/
+class BOTAN_PUBLIC_API(2,0) Internal_Error : public Exception
+ {
+ public:
+ explicit Internal_Error(const std::string& err);
+
+ ErrorType error_type() const noexcept override { return ErrorType::InternalError; }
+ };
+
+/**
+* Not Implemented Exception
+*
+* This is thrown in the situation where a requested operation is
+* logically valid but is not implemented by this version of the library.
+*/
+class BOTAN_PUBLIC_API(2,0) Not_Implemented final : public Exception
+ {
+ public:
+ explicit Not_Implemented(const std::string& err);
+
+ ErrorType error_type() const noexcept override { return ErrorType::NotImplemented; }
+ };
+
+/*
+ The following exception types are still in use for compatability reasons,
+ but are deprecated and will be removed in a future major release.
+ Instead catch the base class.
+*/
+
+/**
+* An invalid OID string was used.
+*
+* This exception will be removed in a future major release.
+*/
+class BOTAN_PUBLIC_API(2,0) Invalid_OID final : public Decoding_Error
+ {
+ public:
+ explicit Invalid_OID(const std::string& oid);
+ };
+
+/*
+ The following exception types are deprecated, no longer used,
+ and will be removed in a future major release
+*/
+
+/**
+* Self Test Failure Exception
+*
+* This exception is no longer used. It will be removed in a future major release.
+*/
+class BOTAN_PUBLIC_API(2,0) Self_Test_Failure final : public Internal_Error
+ {
+ public:
+ BOTAN_DEPRECATED("no longer used") explicit Self_Test_Failure(const std::string& err);
+ };
+
+/**
+* No_Provider_Found Exception
+*
+* This exception is no longer used. It will be removed in a future major release.
+*/
+class BOTAN_PUBLIC_API(2,0) No_Provider_Found final : public Exception
+ {
+ public:
+ BOTAN_DEPRECATED("no longer used") explicit No_Provider_Found(const std::string& name);
+ };
+
+/**
+* Policy_Violation Exception
+*
+* This exception is no longer used. It will be removed in a future major release.
+*/
+class BOTAN_PUBLIC_API(2,0) Policy_Violation final : public Invalid_State
+ {
+ public:
+ BOTAN_DEPRECATED("no longer used") explicit Policy_Violation(const std::string& err);
+ };
+
+/**
+* Unsupported_Argument Exception
+*
+* An argument that is invalid because it is not supported by Botan.
+* It might or might not be valid in another context like a standard.
+*
+* This exception is no longer used, instead Not_Implemented is thrown.
+* It will be removed in a future major release.
+*/
+class BOTAN_PUBLIC_API(2,0) Unsupported_Argument final : public Invalid_Argument
+ {
+ public:
+ BOTAN_DEPRECATED("no longer used") explicit Unsupported_Argument(const std::string& msg) : Invalid_Argument(msg) {}
+ };
+
+template<typename E, typename... Args>
+inline void do_throw_error(const char* file, int line, const char* func, Args... args)
+ {
+ throw E(file, line, func, args...);
+ }
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/utils/filesystem.cpp b/comm/third_party/botan/src/lib/utils/filesystem.cpp
new file mode 100644
index 0000000000..3072a304f2
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/filesystem.cpp
@@ -0,0 +1,144 @@
+/*
+* (C) 2015,2017,2019 Jack Lloyd
+* (C) 2015 Simon Warta (Kullo GmbH)
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/exceptn.h>
+#include <botan/internal/filesystem.h>
+#include <algorithm>
+#include <deque>
+#include <memory>
+
+#if defined(BOTAN_TARGET_OS_HAS_POSIX1)
+ #include <sys/types.h>
+ #include <sys/stat.h>
+ #include <dirent.h>
+ #include <functional>
+#elif defined(BOTAN_TARGET_OS_HAS_WIN32)
+ #define NOMINMAX 1
+ #define _WINSOCKAPI_ // stop windows.h including winsock.h
+ #include <windows.h>
+#endif
+
+namespace Botan {
+
+namespace {
+
+#if defined(BOTAN_TARGET_OS_HAS_POSIX1)
+
+std::vector<std::string> impl_readdir(const std::string& dir_path)
+ {
+ std::vector<std::string> out;
+ std::deque<std::string> dir_list;
+ dir_list.push_back(dir_path);
+
+ while(!dir_list.empty())
+ {
+ const std::string cur_path = dir_list[0];
+ dir_list.pop_front();
+
+ std::unique_ptr<DIR, std::function<int (DIR*)>> dir(::opendir(cur_path.c_str()), ::closedir);
+
+ if(dir)
+ {
+ while(struct dirent* dirent = ::readdir(dir.get()))
+ {
+ const std::string filename = dirent->d_name;
+ if(filename == "." || filename == "..")
+ continue;
+ const std::string full_path = cur_path + "/" + filename;
+
+ struct stat stat_buf;
+
+ if(::stat(full_path.c_str(), &stat_buf) == -1)
+ continue;
+
+ if(S_ISDIR(stat_buf.st_mode))
+ dir_list.push_back(full_path);
+ else if(S_ISREG(stat_buf.st_mode))
+ out.push_back(full_path);
+ }
+ }
+ }
+
+ return out;
+ }
+
+#elif defined(BOTAN_TARGET_OS_HAS_WIN32)
+
+std::vector<std::string> impl_win32(const std::string& dir_path)
+ {
+ std::vector<std::string> out;
+ std::deque<std::string> dir_list;
+ dir_list.push_back(dir_path);
+
+ while(!dir_list.empty())
+ {
+ const std::string cur_path = dir_list[0];
+ dir_list.pop_front();
+
+ WIN32_FIND_DATAA find_data;
+ HANDLE dir = ::FindFirstFileA((cur_path + "/*").c_str(), &find_data);
+
+ if(dir != INVALID_HANDLE_VALUE)
+ {
+ do
+ {
+ const std::string filename = find_data.cFileName;
+ if(filename == "." || filename == "..")
+ continue;
+ const std::string full_path = cur_path + "/" + filename;
+
+ if(find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+ {
+ dir_list.push_back(full_path);
+ }
+ else
+ {
+ out.push_back(full_path);
+ }
+ }
+ while(::FindNextFileA(dir, &find_data));
+ }
+
+ ::FindClose(dir);
+ }
+
+ return out;
+}
+#endif
+
+}
+
+bool has_filesystem_impl()
+ {
+#if defined(BOTAN_TARGET_OS_HAS_POSIX1)
+ return true;
+#elif defined(BOTAN_TARGET_OS_HAS_WIN32)
+ return true;
+#else
+ return false;
+#endif
+ }
+
+std::vector<std::string> get_files_recursive(const std::string& dir)
+ {
+ std::vector<std::string> files;
+
+#if defined(BOTAN_TARGET_OS_HAS_POSIX1)
+ files = impl_readdir(dir);
+#elif defined(BOTAN_TARGET_OS_HAS_WIN32)
+ files = impl_win32(dir);
+#else
+ BOTAN_UNUSED(dir);
+ throw No_Filesystem_Access();
+#endif
+
+ std::sort(files.begin(), files.end());
+
+ return files;
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/utils/filesystem.h b/comm/third_party/botan/src/lib/utils/filesystem.h
new file mode 100644
index 0000000000..382da7de34
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/filesystem.h
@@ -0,0 +1,33 @@
+/*
+* (C) 2015 Jack Lloyd
+* (C) 2015 Simon Warta (Kullo GmbH)
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_UTIL_FILESYSTEM_H_
+#define BOTAN_UTIL_FILESYSTEM_H_
+
+#include <botan/types.h>
+#include <vector>
+#include <string>
+
+namespace Botan {
+
+/**
+* No_Filesystem_Access Exception
+*/
+class BOTAN_PUBLIC_API(2,0) No_Filesystem_Access final : public Exception
+ {
+ public:
+ No_Filesystem_Access() : Exception("No filesystem access enabled.")
+ {}
+ };
+
+BOTAN_TEST_API bool has_filesystem_impl();
+
+BOTAN_TEST_API std::vector<std::string> get_files_recursive(const std::string& dir);
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/utils/ghash/ghash.cpp b/comm/third_party/botan/src/lib/utils/ghash/ghash.cpp
new file mode 100644
index 0000000000..e24f5e02ca
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/ghash/ghash.cpp
@@ -0,0 +1,236 @@
+/*
+* GCM GHASH
+* (C) 2013,2015,2017 Jack Lloyd
+* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/ghash.h>
+#include <botan/internal/ct_utils.h>
+#include <botan/loadstor.h>
+#include <botan/cpuid.h>
+#include <botan/exceptn.h>
+
+namespace Botan {
+
+std::string GHASH::provider() const
+ {
+#if defined(BOTAN_HAS_GHASH_CLMUL_CPU)
+ if(CPUID::has_carryless_multiply())
+ return "clmul";
+#endif
+
+#if defined(BOTAN_HAS_GHASH_CLMUL_VPERM)
+ if(CPUID::has_vperm())
+ return "vperm";
+#endif
+
+ return "base";
+ }
+
+void GHASH::ghash_multiply(secure_vector<uint8_t>& x,
+ const uint8_t input[],
+ size_t blocks)
+ {
+#if defined(BOTAN_HAS_GHASH_CLMUL_CPU)
+ if(CPUID::has_carryless_multiply())
+ {
+ return ghash_multiply_cpu(x.data(), m_H_pow.data(), input, blocks);
+ }
+#endif
+
+#if defined(BOTAN_HAS_GHASH_CLMUL_VPERM)
+ if(CPUID::has_vperm())
+ {
+ return ghash_multiply_vperm(x.data(), m_HM.data(), input, blocks);
+ }
+#endif
+
+ CT::poison(x.data(), x.size());
+
+ const uint64_t ALL_BITS = 0xFFFFFFFFFFFFFFFF;
+
+ uint64_t X[2] = {
+ load_be<uint64_t>(x.data(), 0),
+ load_be<uint64_t>(x.data(), 1)
+ };
+
+ for(size_t b = 0; b != blocks; ++b)
+ {
+ X[0] ^= load_be<uint64_t>(input, 2*b);
+ X[1] ^= load_be<uint64_t>(input, 2*b+1);
+
+ uint64_t Z[2] = { 0, 0 };
+
+ for(size_t i = 0; i != 64; ++i)
+ {
+ const uint64_t X0MASK = (ALL_BITS + (X[0] >> 63)) ^ ALL_BITS;
+ const uint64_t X1MASK = (ALL_BITS + (X[1] >> 63)) ^ ALL_BITS;
+
+ X[0] <<= 1;
+ X[1] <<= 1;
+
+ Z[0] ^= m_HM[4*i ] & X0MASK;
+ Z[1] ^= m_HM[4*i+1] & X0MASK;
+ Z[0] ^= m_HM[4*i+2] & X1MASK;
+ Z[1] ^= m_HM[4*i+3] & X1MASK;
+ }
+
+ X[0] = Z[0];
+ X[1] = Z[1];
+ }
+
+ store_be<uint64_t>(x.data(), X[0], X[1]);
+ CT::unpoison(x.data(), x.size());
+ }
+
+void GHASH::ghash_update(secure_vector<uint8_t>& ghash,
+ const uint8_t input[], size_t length)
+ {
+ verify_key_set(!m_HM.empty());
+
+ /*
+ This assumes if less than block size input then we're just on the
+ final block and should pad with zeros
+ */
+
+ const size_t full_blocks = length / GCM_BS;
+ const size_t final_bytes = length - (full_blocks * GCM_BS);
+
+ if(full_blocks > 0)
+ {
+ ghash_multiply(ghash, input, full_blocks);
+ }
+
+ if(final_bytes)
+ {
+ uint8_t last_block[GCM_BS] = { 0 };
+ copy_mem(last_block, input + full_blocks * GCM_BS, final_bytes);
+ ghash_multiply(ghash, last_block, 1);
+ secure_scrub_memory(last_block, final_bytes);
+ }
+ }
+
+void GHASH::key_schedule(const uint8_t key[], size_t length)
+ {
+ m_H.assign(key, key+length);
+ m_H_ad.resize(GCM_BS);
+ m_ad_len = 0;
+ m_text_len = 0;
+
+ uint64_t H0 = load_be<uint64_t>(m_H.data(), 0);
+ uint64_t H1 = load_be<uint64_t>(m_H.data(), 1);
+
+ const uint64_t R = 0xE100000000000000;
+
+ m_HM.resize(256);
+
+ // precompute the multiples of H
+ for(size_t i = 0; i != 2; ++i)
+ {
+ for(size_t j = 0; j != 64; ++j)
+ {
+ /*
+ we interleave H^1, H^65, H^2, H^66, H3, H67, H4, H68
+ to make indexing nicer in the multiplication code
+ */
+ m_HM[4*j+2*i] = H0;
+ m_HM[4*j+2*i+1] = H1;
+
+ // GCM's bit ops are reversed so we carry out of the bottom
+ const uint64_t carry = R * (H1 & 1);
+ H1 = (H1 >> 1) | (H0 << 63);
+ H0 = (H0 >> 1) ^ carry;
+ }
+ }
+
+#if defined(BOTAN_HAS_GHASH_CLMUL_CPU)
+ if(CPUID::has_carryless_multiply())
+ {
+ m_H_pow.resize(8);
+ ghash_precompute_cpu(m_H.data(), m_H_pow.data());
+ }
+#endif
+ }
+
+void GHASH::start(const uint8_t nonce[], size_t len)
+ {
+ BOTAN_ARG_CHECK(len == 16, "GHASH requires a 128-bit nonce");
+ m_nonce.assign(nonce, nonce + len);
+ m_ghash = m_H_ad;
+ }
+
+void GHASH::set_associated_data(const uint8_t input[], size_t length)
+ {
+ if(m_ghash.empty() == false)
+ throw Invalid_State("Too late to set AD in GHASH");
+
+ zeroise(m_H_ad);
+
+ ghash_update(m_H_ad, input, length);
+ m_ad_len = length;
+ }
+
+void GHASH::update_associated_data(const uint8_t ad[], size_t length)
+ {
+ verify_key_set(m_ghash.size() == GCM_BS);
+ m_ad_len += length;
+ ghash_update(m_ghash, ad, length);
+ }
+
+void GHASH::update(const uint8_t input[], size_t length)
+ {
+ verify_key_set(m_ghash.size() == GCM_BS);
+ m_text_len += length;
+ ghash_update(m_ghash, input, length);
+ }
+
+void GHASH::add_final_block(secure_vector<uint8_t>& hash,
+ size_t ad_len, size_t text_len)
+ {
+ /*
+ * stack buffer is fine here since the text len is public
+ * and the length of the AD is probably not sensitive either.
+ */
+ uint8_t final_block[GCM_BS];
+ store_be<uint64_t>(final_block, 8*ad_len, 8*text_len);
+ ghash_update(hash, final_block, GCM_BS);
+ }
+
+void GHASH::final(uint8_t mac[], size_t mac_len)
+ {
+ BOTAN_ARG_CHECK(mac_len > 0 && mac_len <= 16, "GHASH output length");
+ add_final_block(m_ghash, m_ad_len, m_text_len);
+
+ for(size_t i = 0; i != mac_len; ++i)
+ mac[i] = m_ghash[i] ^ m_nonce[i];
+
+ m_ghash.clear();
+ m_text_len = 0;
+ }
+
+void GHASH::nonce_hash(secure_vector<uint8_t>& y0, const uint8_t nonce[], size_t nonce_len)
+ {
+ BOTAN_ASSERT(m_ghash.size() == 0, "nonce_hash called during wrong time");
+
+ ghash_update(y0, nonce, nonce_len);
+ add_final_block(y0, 0, nonce_len);
+ }
+
+void GHASH::clear()
+ {
+ zap(m_H);
+ zap(m_HM);
+ reset();
+ }
+
+void GHASH::reset()
+ {
+ zeroise(m_H_ad);
+ m_ghash.clear();
+ m_nonce.clear();
+ m_text_len = m_ad_len = 0;
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/utils/ghash/ghash.h b/comm/third_party/botan/src/lib/utils/ghash/ghash.h
new file mode 100644
index 0000000000..062a04b84b
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/ghash/ghash.h
@@ -0,0 +1,110 @@
+/*
+* (C) 2013 Jack Lloyd
+* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_GCM_GHASH_H_
+#define BOTAN_GCM_GHASH_H_
+
+#include <botan/sym_algo.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(ghash.h)
+
+namespace Botan {
+
+/**
+* GCM's GHASH
+* This is not intended for general use, but is exposed to allow
+* shared code between GCM and GMAC
+*/
+class BOTAN_PUBLIC_API(2,0) GHASH final : public SymmetricAlgorithm
+ {
+ public:
+ void set_associated_data(const uint8_t ad[], size_t ad_len);
+
+ secure_vector<uint8_t> BOTAN_DEPRECATED("Use other impl")
+ nonce_hash(const uint8_t nonce[], size_t nonce_len)
+ {
+ secure_vector<uint8_t> y0(GCM_BS);
+ nonce_hash(y0, nonce, nonce_len);
+ return y0;
+ }
+
+ void nonce_hash(secure_vector<uint8_t>& y0, const uint8_t nonce[], size_t len);
+
+ void start(const uint8_t nonce[], size_t len);
+
+ /*
+ * Assumes input len is multiple of 16
+ */
+ void update(const uint8_t in[], size_t len);
+
+ /*
+ * Incremental update of associated data
+ */
+ void update_associated_data(const uint8_t ad[], size_t len);
+
+ secure_vector<uint8_t> BOTAN_DEPRECATED("Use version taking output params") final()
+ {
+ secure_vector<uint8_t> mac(GCM_BS);
+ final(mac.data(), mac.size());
+ return mac;
+ }
+
+ void final(uint8_t out[], size_t out_len);
+
+ Key_Length_Specification key_spec() const override
+ { return Key_Length_Specification(16); }
+
+ void clear() override;
+
+ void reset();
+
+ std::string name() const override { return "GHASH"; }
+
+ std::string provider() const;
+
+ void ghash_update(secure_vector<uint8_t>& x,
+ const uint8_t input[], size_t input_len);
+
+ void add_final_block(secure_vector<uint8_t>& x,
+ size_t ad_len, size_t pt_len);
+ private:
+
+#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_multiply_cpu(uint8_t x[16],
+ const uint64_t H_pow[4*2],
+ const uint8_t input[], size_t blocks);
+#endif
+
+#if defined(BOTAN_HAS_GHASH_CLMUL_VPERM)
+ static void ghash_multiply_vperm(uint8_t x[16],
+ const uint64_t HM[256],
+ const uint8_t input[], size_t blocks);
+#endif
+
+ void key_schedule(const uint8_t key[], size_t key_len) override;
+
+ void ghash_multiply(secure_vector<uint8_t>& x,
+ const uint8_t input[],
+ size_t blocks);
+
+ static const size_t GCM_BS = 16;
+
+ secure_vector<uint8_t> m_H;
+ secure_vector<uint8_t> m_H_ad;
+ secure_vector<uint8_t> m_ghash;
+ secure_vector<uint8_t> m_nonce;
+ secure_vector<uint64_t> m_HM;
+ secure_vector<uint64_t> m_H_pow;
+ size_t m_ad_len = 0;
+ size_t m_text_len = 0;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/utils/ghash/ghash_cpu/ghash_cpu.cpp b/comm/third_party/botan/src/lib/utils/ghash/ghash_cpu/ghash_cpu.cpp
new file mode 100644
index 0000000000..5d725933d9
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/ghash/ghash_cpu/ghash_cpu.cpp
@@ -0,0 +1,207 @@
+/*
+* Hook for CLMUL/PMULL/VPMSUM
+* (C) 2013,2017,2019,2020 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/ghash.h>
+#include <botan/internal/simd_32.h>
+
+#if defined(BOTAN_SIMD_USE_SSE2)
+ #include <immintrin.h>
+ #include <wmmintrin.h>
+#endif
+
+namespace Botan {
+
+namespace {
+
+BOTAN_FORCE_INLINE SIMD_4x32 BOTAN_FUNC_ISA(BOTAN_VPERM_ISA) 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<int M>
+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);
+ return SIMD_4x32(reinterpret_cast<uint32x4_t>(vmull_p64(a, b)));
+#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 defined(__clang__)
+ auto rv = __builtin_altivec_crypto_vpmsumd(i1v, i2v);
+#else
+ auto rv = __builtin_crypto_vpmsumd(i1v, i2v);
+#endif
+
+ return SIMD_4x32(reinterpret_cast<__vector unsigned int>(rv));
+#endif
+ }
+
+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;
+ }
+
+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>();
+
+ return gcm_reduce(T0, T3);
+ }
+
+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
+ */
+
+ 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));
+
+ SIMD_4x32 T;
+
+ 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;
+
+ return gcm_reduce(hi ^ T.shift_elems_right<2>(),
+ lo ^ T.shift_elems_left<2>());
+ }
+
+}
+
+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);
+ }
+
+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);
+
+ SIMD_4x32 a = reverse_vector(SIMD_4x32::load_le(x));
+
+ 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);
+
+ while(blocks >= 4)
+ {
+ 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));
+
+ a ^= m0;
+ a = gcm_multiply_x4(H1, H2, H3, H4, m3, m2, m1, a);
+
+ input += 4*16;
+ blocks -= 4;
+ }
+ }
+
+ for(size_t i = 0; i != blocks; ++i)
+ {
+ const SIMD_4x32 m = reverse_vector(SIMD_4x32::load_le(input + 16*i));
+
+ a ^= m;
+ a = gcm_multiply(H1, a);
+ }
+
+ a = reverse_vector(a);
+ a.store_le(x);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/utils/ghash/ghash_cpu/info.txt b/comm/third_party/botan/src/lib/utils/ghash/ghash_cpu/info.txt
new file mode 100644
index 0000000000..1f56377a29
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/ghash/ghash_cpu/info.txt
@@ -0,0 +1,34 @@
+<defines>
+GHASH_CLMUL_CPU -> 20201002
+</defines>
+
+endian little
+
+<requires>
+simd
+</requires>
+
+<isa>
+x86_32:sse2
+x86_32:ssse3
+x86_32:aesni
+x86_64:sse2
+x86_64:ssse3
+x86_64:aesni
+arm64:neon
+arm64:armv8crypto
+ppc64:powercrypto
+</isa>
+
+<arch>
+x86_32
+x86_64
+arm64
+ppc64
+</arch>
+
+<cc>
+gcc:4.9
+clang:3.8
+msvc
+</cc>
diff --git a/comm/third_party/botan/src/lib/utils/ghash/ghash_vperm/ghash_vperm.cpp b/comm/third_party/botan/src/lib/utils/ghash/ghash_vperm/ghash_vperm.cpp
new file mode 100644
index 0000000000..f6f342cb9e
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/ghash/ghash_vperm/ghash_vperm.cpp
@@ -0,0 +1,62 @@
+/*
+* (C) 2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/ghash.h>
+#include <immintrin.h>
+
+namespace Botan {
+
+// TODO: extend this to support NEON and AltiVec
+
+BOTAN_FUNC_ISA("ssse3")
+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<const __m128i*>(HM);
+
+ __m128i X = _mm_loadu_si128(reinterpret_cast<__m128i*>(x));
+ X = _mm_shuffle_epi8(X, BSWAP_MASK);
+
+ const __m128i ones = _mm_set1_epi8(-1);
+
+ for(size_t b = 0; b != blocks; ++b)
+ {
+ __m128i M = _mm_loadu_si128(reinterpret_cast<const __m128i*>(input_bytes) + b);
+ M = _mm_shuffle_epi8(M, BSWAP_MASK);
+
+ X = _mm_xor_si128(X, M);
+
+ __m128i Z = _mm_setzero_si128();
+
+ 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));
+ }
+
+ X = _mm_shuffle_epi32(Z, _MM_SHUFFLE(1, 0, 3, 2));
+ }
+
+ X = _mm_shuffle_epi8(X, BSWAP_MASK);
+ _mm_storeu_si128(reinterpret_cast<__m128i*>(x), X);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/utils/ghash/ghash_vperm/info.txt b/comm/third_party/botan/src/lib/utils/ghash/ghash_vperm/info.txt
new file mode 100644
index 0000000000..41d5e1e1b1
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/ghash/ghash_vperm/info.txt
@@ -0,0 +1,8 @@
+<defines>
+GHASH_CLMUL_VPERM -> 20201002
+</defines>
+
+<isa>
+sse2
+ssse3
+</isa>
diff --git a/comm/third_party/botan/src/lib/utils/ghash/info.txt b/comm/third_party/botan/src/lib/utils/ghash/info.txt
new file mode 100644
index 0000000000..4e519eb3b5
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/ghash/info.txt
@@ -0,0 +1,3 @@
+<defines>
+GHASH -> 20201002
+</defines>
diff --git a/comm/third_party/botan/src/lib/utils/http_util/http_util.cpp b/comm/third_party/botan/src/lib/utils/http_util/http_util.cpp
new file mode 100644
index 0000000000..342fe2e843
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/http_util/http_util.cpp
@@ -0,0 +1,267 @@
+/*
+* Sketchy HTTP client
+* (C) 2013,2016 Jack Lloyd
+* 2017 René Korthaus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/http_util.h>
+#include <botan/parsing.h>
+#include <botan/hex.h>
+#include <botan/internal/os_utils.h>
+#include <botan/internal/socket.h>
+#include <botan/internal/stl_util.h>
+#include <sstream>
+
+namespace Botan {
+
+namespace HTTP {
+
+namespace {
+
+/*
+* Connect to a host, write some bytes, then read until the server
+* closes the socket.
+*/
+std::string http_transact(const std::string& hostname,
+ const std::string& service,
+ const std::string& message,
+ std::chrono::milliseconds timeout)
+ {
+ std::unique_ptr<OS::Socket> socket;
+
+ const std::chrono::system_clock::time_point start_time = std::chrono::system_clock::now();
+
+ try
+ {
+ socket = OS::open_socket(hostname, service, timeout);
+ if(!socket)
+ throw Not_Implemented("No socket support enabled in build");
+ }
+ catch(std::exception& e)
+ {
+ throw HTTP_Error("HTTP connection to " + hostname + " failed: " + e.what());
+ }
+
+ // Blocks until entire message has been written
+ socket->write(cast_char_ptr_to_uint8(message.data()),
+ message.size());
+
+ if(std::chrono::system_clock::now() - start_time > timeout)
+ throw HTTP_Error("Timeout during writing message body");
+
+ std::ostringstream oss;
+ std::vector<uint8_t> buf(BOTAN_DEFAULT_BUFFER_SIZE);
+ while(true)
+ {
+ const size_t got = socket->read(buf.data(), buf.size());
+ if(got == 0) // EOF
+ break;
+
+ if(std::chrono::system_clock::now() - start_time > timeout)
+ throw HTTP_Error("Timeout while reading message body");
+
+ oss.write(cast_uint8_ptr_to_char(buf.data()),
+ static_cast<std::streamsize>(got));
+ }
+
+ return oss.str();
+ }
+
+}
+
+std::string url_encode(const std::string& in)
+ {
+ std::ostringstream out;
+
+ for(auto c : in)
+ {
+ if(c >= 'A' && c <= 'Z')
+ out << c;
+ else if(c >= 'a' && c <= 'z')
+ out << c;
+ else if(c >= '0' && c <= '9')
+ out << c;
+ else if(c == '-' || c == '_' || c == '.' || c == '~')
+ out << c;
+ else
+ out << '%' << hex_encode(cast_char_ptr_to_uint8(&c), 1);
+ }
+
+ return out.str();
+ }
+
+std::ostream& operator<<(std::ostream& o, const Response& resp)
+ {
+ o << "HTTP " << resp.status_code() << " " << resp.status_message() << "\n";
+ for(auto h : resp.headers())
+ o << "Header '" << h.first << "' = '" << h.second << "'\n";
+ o << "Body " << std::to_string(resp.body().size()) << " bytes:\n";
+ o.write(cast_uint8_ptr_to_char(resp.body().data()), resp.body().size());
+ return o;
+ }
+
+Response http_sync(http_exch_fn http_transact,
+ const std::string& verb,
+ const std::string& url,
+ const std::string& content_type,
+ const std::vector<uint8_t>& body,
+ size_t allowable_redirects)
+ {
+ if(url.empty())
+ throw HTTP_Error("URL empty");
+
+ const auto protocol_host_sep = url.find("://");
+ if(protocol_host_sep == std::string::npos)
+ throw HTTP_Error("Invalid URL '" + url + "'");
+
+ const auto host_loc_sep = url.find('/', protocol_host_sep + 3);
+
+ std::string hostname, loc, service;
+
+ if(host_loc_sep == std::string::npos)
+ {
+ hostname = url.substr(protocol_host_sep + 3, std::string::npos);
+ 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);
+ }
+
+ const auto port_sep = hostname.find(":");
+ if(port_sep == std::string::npos)
+ {
+ service = "http";
+ // hostname not modified
+ }
+ else
+ {
+ service = hostname.substr(port_sep + 1, std::string::npos);
+ hostname = hostname.substr(0, port_sep);
+ }
+
+ std::ostringstream outbuf;
+
+ outbuf << verb << " " << loc << " HTTP/1.0\r\n";
+ outbuf << "Host: " << hostname << "\r\n";
+
+ if(verb == "GET")
+ {
+ outbuf << "Accept: */*\r\n";
+ outbuf << "Cache-Control: no-cache\r\n";
+ }
+ else if(verb == "POST")
+ outbuf << "Content-Length: " << body.size() << "\r\n";
+
+ if(!content_type.empty())
+ outbuf << "Content-Type: " << content_type << "\r\n";
+ outbuf << "Connection: close\r\n\r\n";
+ outbuf.write(cast_uint8_ptr_to_char(body.data()), body.size());
+
+ std::istringstream io(http_transact(hostname, service, outbuf.str()));
+
+ std::string line1;
+ std::getline(io, line1);
+ if(!io || line1.empty())
+ throw HTTP_Error("No response");
+
+ std::stringstream response_stream(line1);
+ std::string http_version;
+ unsigned int status_code;
+ 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/")
+ throw HTTP_Error("Not an HTTP response");
+
+ std::map<std::string, std::string> headers;
+ std::string header_line;
+ while (std::getline(io, header_line) && header_line != "\r")
+ {
+ auto sep = header_line.find(": ");
+ if(sep == std::string::npos || sep > header_line.size() - 2)
+ throw HTTP_Error("Invalid HTTP header " + header_line);
+ const std::string key = header_line.substr(0, sep);
+
+ if(sep + 2 < header_line.size() - 1)
+ {
+ const std::string val = header_line.substr(sep + 2, (header_line.size() - 1) - (sep + 2));
+ headers[key] = val;
+ }
+ }
+
+ if(status_code == 301 && headers.count("Location"))
+ {
+ if(allowable_redirects == 0)
+ throw HTTP_Error("HTTP redirection count exceeded");
+ return GET_sync(headers["Location"], allowable_redirects - 1);
+ }
+
+ std::vector<uint8_t> resp_body;
+ std::vector<uint8_t> buf(4096);
+ while(io.good())
+ {
+ io.read(cast_uint8_ptr_to_char(buf.data()), buf.size());
+ const size_t got = static_cast<size_t>(io.gcount());
+ resp_body.insert(resp_body.end(), buf.data(), &buf[got]);
+ }
+
+ const std::string header_size = search_map(headers, std::string("Content-Length"));
+
+ if(!header_size.empty())
+ {
+ if(resp_body.size() != to_u32bit(header_size))
+ throw HTTP_Error("Content-Length disagreement, header says " +
+ header_size + " got " + std::to_string(resp_body.size()));
+ }
+
+ return Response(status_code, status_message, resp_body, headers);
+ }
+
+Response http_sync(const std::string& verb,
+ const std::string& url,
+ const std::string& content_type,
+ const std::vector<uint8_t>& body,
+ size_t allowable_redirects,
+ std::chrono::milliseconds timeout)
+ {
+ auto transact_with_timeout =
+ [timeout](const std::string& hostname, const std::string& service, const std::string& message)
+ {
+ return http_transact(hostname, service, message, timeout);
+ };
+
+ return http_sync(
+ transact_with_timeout,
+ verb,
+ url,
+ content_type,
+ body,
+ allowable_redirects);
+ }
+
+Response GET_sync(const std::string& url,
+ size_t allowable_redirects,
+ std::chrono::milliseconds timeout)
+ {
+ return http_sync("GET", url, "", std::vector<uint8_t>(), allowable_redirects, timeout);
+ }
+
+Response POST_sync(const std::string& url,
+ const std::string& content_type,
+ const std::vector<uint8_t>& body,
+ size_t allowable_redirects,
+ std::chrono::milliseconds timeout)
+ {
+ return http_sync("POST", url, content_type, body, allowable_redirects, timeout);
+ }
+
+}
+
+}
diff --git a/comm/third_party/botan/src/lib/utils/http_util/http_util.h b/comm/third_party/botan/src/lib/utils/http_util/http_util.h
new file mode 100644
index 0000000000..7ad7c5828c
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/http_util/http_util.h
@@ -0,0 +1,107 @@
+/*
+* HTTP utilities
+* (C) 2013 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_UTILS_URLGET_H_
+#define BOTAN_UTILS_URLGET_H_
+
+#include <botan/types.h>
+#include <botan/exceptn.h>
+#include <vector>
+#include <map>
+#include <string>
+#include <functional>
+#include <chrono>
+
+BOTAN_FUTURE_INTERNAL_HEADER(http_util.h)
+
+namespace Botan {
+
+namespace HTTP {
+
+/**
+* HTTP_Error Exception
+*/
+class BOTAN_PUBLIC_API(2,0) HTTP_Error final : public Exception
+ {
+ public:
+ explicit HTTP_Error(const std::string& msg) :
+ Exception("HTTP error " + msg)
+ {}
+
+ ErrorType error_type() const noexcept override { return ErrorType::HttpError; }
+
+ };
+
+class Response final
+ {
+ public:
+ Response() : m_status_code(0), m_status_message("Uninitialized") {}
+
+ Response(unsigned int status_code, const std::string& status_message,
+ const std::vector<uint8_t>& body,
+ const std::map<std::string, std::string>& headers) :
+ m_status_code(status_code),
+ m_status_message(status_message),
+ m_body(body),
+ m_headers(headers) {}
+
+ unsigned int status_code() const { return m_status_code; }
+
+ const std::vector<uint8_t>& body() const { return m_body; }
+
+ const std::map<std::string, std::string>& headers() const { return m_headers; }
+
+ std::string status_message() const { return m_status_message; }
+
+ void throw_unless_ok()
+ {
+ if(status_code() != 200)
+ throw HTTP_Error(status_message());
+ }
+
+ private:
+ unsigned int m_status_code;
+ std::string m_status_message;
+ std::vector<uint8_t> m_body;
+ std::map<std::string, std::string> m_headers;
+ };
+
+BOTAN_PUBLIC_API(2,0) std::ostream& operator<<(std::ostream& o, const Response& resp);
+
+typedef std::function<std::string (const std::string&, const std::string&, const std::string&)> http_exch_fn;
+
+BOTAN_PUBLIC_API(2,0) Response http_sync(http_exch_fn fn,
+ const std::string& verb,
+ const std::string& url,
+ const std::string& content_type,
+ const std::vector<uint8_t>& body,
+ size_t allowable_redirects);
+
+BOTAN_PUBLIC_API(2,0) Response http_sync(const std::string& verb,
+ const std::string& url,
+ const std::string& content_type,
+ const std::vector<uint8_t>& body,
+ size_t allowable_redirects,
+ std::chrono::milliseconds timeout = std::chrono::milliseconds(3000));
+
+BOTAN_PUBLIC_API(2,0) Response GET_sync(const std::string& url,
+ size_t allowable_redirects = 1,
+ std::chrono::milliseconds timeout = std::chrono::milliseconds(3000));
+
+BOTAN_PUBLIC_API(2,0) Response POST_sync(const std::string& url,
+ const std::string& content_type,
+ const std::vector<uint8_t>& body,
+ size_t allowable_redirects = 1,
+ std::chrono::milliseconds timeout = std::chrono::milliseconds(3000));
+
+BOTAN_PUBLIC_API(2,0) std::string url_encode(const std::string& url);
+
+}
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/utils/http_util/info.txt b/comm/third_party/botan/src/lib/utils/http_util/info.txt
new file mode 100644
index 0000000000..a3ebc249e2
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/http_util/info.txt
@@ -0,0 +1,11 @@
+<defines>
+HTTP_UTIL -> 20171003
+</defines>
+
+<header:public>
+http_util.h
+</header:public>
+
+<requires>
+socket
+</requires>
diff --git a/comm/third_party/botan/src/lib/utils/info.txt b/comm/third_party/botan/src/lib/utils/info.txt
new file mode 100644
index 0000000000..93ae795ef5
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/info.txt
@@ -0,0 +1,43 @@
+<defines>
+UTIL_FUNCTIONS -> 20180903
+</defines>
+
+load_on always
+
+<header:public>
+assert.h
+bswap.h
+calendar.h
+charset.h
+compiler.h
+data_src.h
+database.h
+exceptn.h
+loadstor.h
+mem_ops.h
+mul128.h
+mutex.h
+parsing.h
+rotate.h
+types.h
+version.h
+stl_compatibility.h
+</header:public>
+
+<header:internal>
+bit_ops.h
+codec_base.h
+ct_utils.h
+donna128.h
+filesystem.h
+os_utils.h
+prefetch.h
+rounding.h
+safeint.h
+stl_util.h
+timer.h
+</header:internal>
+
+<requires>
+cpuid
+</requires>
diff --git a/comm/third_party/botan/src/lib/utils/loadstor.h b/comm/third_party/botan/src/lib/utils/loadstor.h
new file mode 100644
index 0000000000..9a8e9d4bea
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/loadstor.h
@@ -0,0 +1,701 @@
+/*
+* Load/Store Operators
+* (C) 1999-2007,2015,2017 Jack Lloyd
+* 2007 Yves Jerschow
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_LOAD_STORE_H_
+#define BOTAN_LOAD_STORE_H_
+
+#include <botan/types.h>
+#include <botan/bswap.h>
+#include <botan/mem_ops.h>
+#include <vector>
+
+BOTAN_FUTURE_INTERNAL_HEADER(loadstor.h)
+
+#if defined(BOTAN_TARGET_CPU_IS_BIG_ENDIAN)
+ #define BOTAN_ENDIAN_N2L(x) reverse_bytes(x)
+ #define BOTAN_ENDIAN_L2N(x) reverse_bytes(x)
+ #define BOTAN_ENDIAN_N2B(x) (x)
+ #define BOTAN_ENDIAN_B2N(x) (x)
+
+#elif defined(BOTAN_TARGET_CPU_IS_LITTLE_ENDIAN)
+ #define BOTAN_ENDIAN_N2L(x) (x)
+ #define BOTAN_ENDIAN_L2N(x) (x)
+ #define BOTAN_ENDIAN_N2B(x) reverse_bytes(x)
+ #define BOTAN_ENDIAN_B2N(x) reverse_bytes(x)
+
+#endif
+
+namespace Botan {
+
+/**
+* Byte extraction
+* @param byte_num which byte to extract, 0 == highest byte
+* @param input the value to extract from
+* @return byte byte_num of input
+*/
+template<typename T> inline constexpr uint8_t get_byte(size_t byte_num, T input)
+ {
+ return static_cast<uint8_t>(
+ input >> (((~byte_num)&(sizeof(T)-1)) << 3)
+ );
+ }
+
+/**
+* Make a uint16_t from two bytes
+* @param i0 the first byte
+* @param i1 the second byte
+* @return i0 || i1
+*/
+inline constexpr uint16_t make_uint16(uint8_t i0, uint8_t i1)
+ {
+ return static_cast<uint16_t>((static_cast<uint16_t>(i0) << 8) | i1);
+ }
+
+/**
+* Make a uint32_t from four bytes
+* @param i0 the first byte
+* @param i1 the second byte
+* @param i2 the third byte
+* @param i3 the fourth byte
+* @return i0 || i1 || i2 || i3
+*/
+inline constexpr uint32_t make_uint32(uint8_t i0, uint8_t i1, uint8_t i2, uint8_t i3)
+ {
+ return ((static_cast<uint32_t>(i0) << 24) |
+ (static_cast<uint32_t>(i1) << 16) |
+ (static_cast<uint32_t>(i2) << 8) |
+ (static_cast<uint32_t>(i3)));
+ }
+
+/**
+* Make a uint64_t from eight bytes
+* @param i0 the first byte
+* @param i1 the second byte
+* @param i2 the third byte
+* @param i3 the fourth byte
+* @param i4 the fifth byte
+* @param i5 the sixth byte
+* @param i6 the seventh byte
+* @param i7 the eighth byte
+* @return i0 || i1 || i2 || i3 || i4 || i5 || i6 || i7
+*/
+inline constexpr uint64_t make_uint64(uint8_t i0, uint8_t i1, uint8_t i2, uint8_t i3,
+ uint8_t i4, uint8_t i5, uint8_t i6, uint8_t i7)
+ {
+ return ((static_cast<uint64_t>(i0) << 56) |
+ (static_cast<uint64_t>(i1) << 48) |
+ (static_cast<uint64_t>(i2) << 40) |
+ (static_cast<uint64_t>(i3) << 32) |
+ (static_cast<uint64_t>(i4) << 24) |
+ (static_cast<uint64_t>(i5) << 16) |
+ (static_cast<uint64_t>(i6) << 8) |
+ (static_cast<uint64_t>(i7)));
+ }
+
+/**
+* Load a big-endian word
+* @param in a pointer to some bytes
+* @param off an offset into the array
+* @return off'th T of in, as a big-endian value
+*/
+template<typename T>
+inline T load_be(const uint8_t in[], size_t off)
+ {
+ in += off * sizeof(T);
+ T out = 0;
+ for(size_t i = 0; i != sizeof(T); ++i)
+ out = static_cast<T>((out << 8) | in[i]);
+ return out;
+ }
+
+/**
+* Load a little-endian word
+* @param in a pointer to some bytes
+* @param off an offset into the array
+* @return off'th T of in, as a litte-endian value
+*/
+template<typename T>
+inline T load_le(const uint8_t in[], size_t off)
+ {
+ in += off * sizeof(T);
+ T out = 0;
+ for(size_t i = 0; i != sizeof(T); ++i)
+ out = (out << 8) | in[sizeof(T)-1-i];
+ return out;
+ }
+
+/**
+* Load a big-endian uint16_t
+* @param in a pointer to some bytes
+* @param off an offset into the array
+* @return off'th uint16_t of in, as a big-endian value
+*/
+template<>
+inline uint16_t load_be<uint16_t>(const uint8_t in[], size_t off)
+ {
+ in += off * sizeof(uint16_t);
+
+#if defined(BOTAN_ENDIAN_N2B)
+ uint16_t x;
+ typecast_copy(x, in);
+ return BOTAN_ENDIAN_N2B(x);
+#else
+ return make_uint16(in[0], in[1]);
+#endif
+ }
+
+/**
+* Load a little-endian uint16_t
+* @param in a pointer to some bytes
+* @param off an offset into the array
+* @return off'th uint16_t of in, as a little-endian value
+*/
+template<>
+inline uint16_t load_le<uint16_t>(const uint8_t in[], size_t off)
+ {
+ in += off * sizeof(uint16_t);
+
+#if defined(BOTAN_ENDIAN_N2L)
+ uint16_t x;
+ typecast_copy(x, in);
+ return BOTAN_ENDIAN_N2L(x);
+#else
+ return make_uint16(in[1], in[0]);
+#endif
+ }
+
+/**
+* Load a big-endian uint32_t
+* @param in a pointer to some bytes
+* @param off an offset into the array
+* @return off'th uint32_t of in, as a big-endian value
+*/
+template<>
+inline uint32_t load_be<uint32_t>(const uint8_t in[], size_t off)
+ {
+ in += off * sizeof(uint32_t);
+#if defined(BOTAN_ENDIAN_N2B)
+ uint32_t x;
+ typecast_copy(x, in);
+ return BOTAN_ENDIAN_N2B(x);
+#else
+ return make_uint32(in[0], in[1], in[2], in[3]);
+#endif
+ }
+
+/**
+* Load a little-endian uint32_t
+* @param in a pointer to some bytes
+* @param off an offset into the array
+* @return off'th uint32_t of in, as a little-endian value
+*/
+template<>
+inline uint32_t load_le<uint32_t>(const uint8_t in[], size_t off)
+ {
+ in += off * sizeof(uint32_t);
+#if defined(BOTAN_ENDIAN_N2L)
+ uint32_t x;
+ typecast_copy(x, in);
+ return BOTAN_ENDIAN_N2L(x);
+#else
+ return make_uint32(in[3], in[2], in[1], in[0]);
+#endif
+ }
+
+/**
+* Load a big-endian uint64_t
+* @param in a pointer to some bytes
+* @param off an offset into the array
+* @return off'th uint64_t of in, as a big-endian value
+*/
+template<>
+inline uint64_t load_be<uint64_t>(const uint8_t in[], size_t off)
+ {
+ in += off * sizeof(uint64_t);
+#if defined(BOTAN_ENDIAN_N2B)
+ uint64_t x;
+ typecast_copy(x, in);
+ return BOTAN_ENDIAN_N2B(x);
+#else
+ return make_uint64(in[0], in[1], in[2], in[3],
+ in[4], in[5], in[6], in[7]);
+#endif
+ }
+
+/**
+* Load a little-endian uint64_t
+* @param in a pointer to some bytes
+* @param off an offset into the array
+* @return off'th uint64_t of in, as a little-endian value
+*/
+template<>
+inline uint64_t load_le<uint64_t>(const uint8_t in[], size_t off)
+ {
+ in += off * sizeof(uint64_t);
+#if defined(BOTAN_ENDIAN_N2L)
+ uint64_t x;
+ typecast_copy(x, in);
+ return BOTAN_ENDIAN_N2L(x);
+#else
+ return make_uint64(in[7], in[6], in[5], in[4],
+ in[3], in[2], in[1], in[0]);
+#endif
+ }
+
+/**
+* Load two little-endian words
+* @param in a pointer to some bytes
+* @param x0 where the first word will be written
+* @param x1 where the second word will be written
+*/
+template<typename T>
+inline void load_le(const uint8_t in[], T& x0, T& x1)
+ {
+ x0 = load_le<T>(in, 0);
+ x1 = load_le<T>(in, 1);
+ }
+
+/**
+* Load four little-endian words
+* @param in a pointer to some bytes
+* @param x0 where the first word will be written
+* @param x1 where the second word will be written
+* @param x2 where the third word will be written
+* @param x3 where the fourth word will be written
+*/
+template<typename T>
+inline void load_le(const uint8_t in[],
+ T& x0, T& x1, T& x2, T& x3)
+ {
+ x0 = load_le<T>(in, 0);
+ x1 = load_le<T>(in, 1);
+ x2 = load_le<T>(in, 2);
+ x3 = load_le<T>(in, 3);
+ }
+
+/**
+* Load eight little-endian words
+* @param in a pointer to some bytes
+* @param x0 where the first word will be written
+* @param x1 where the second word will be written
+* @param x2 where the third word will be written
+* @param x3 where the fourth word will be written
+* @param x4 where the fifth word will be written
+* @param x5 where the sixth word will be written
+* @param x6 where the seventh word will be written
+* @param x7 where the eighth word will be written
+*/
+template<typename T>
+inline void load_le(const uint8_t in[],
+ T& x0, T& x1, T& x2, T& x3,
+ T& x4, T& x5, T& x6, T& x7)
+ {
+ x0 = load_le<T>(in, 0);
+ x1 = load_le<T>(in, 1);
+ x2 = load_le<T>(in, 2);
+ x3 = load_le<T>(in, 3);
+ x4 = load_le<T>(in, 4);
+ x5 = load_le<T>(in, 5);
+ x6 = load_le<T>(in, 6);
+ x7 = load_le<T>(in, 7);
+ }
+
+/**
+* Load a variable number of little-endian words
+* @param out the output array of words
+* @param in the input array of bytes
+* @param count how many words are in in
+*/
+template<typename T>
+inline void load_le(T out[],
+ const uint8_t in[],
+ size_t count)
+ {
+ if(count > 0)
+ {
+#if defined(BOTAN_TARGET_CPU_IS_LITTLE_ENDIAN)
+ typecast_copy(out, in, count);
+
+#elif defined(BOTAN_TARGET_CPU_IS_BIG_ENDIAN)
+ typecast_copy(out, in, count);
+
+ const size_t blocks = count - (count % 4);
+ const size_t left = count - blocks;
+
+ for(size_t i = 0; i != blocks; i += 4)
+ bswap_4(out + i);
+
+ for(size_t i = 0; i != left; ++i)
+ out[blocks+i] = reverse_bytes(out[blocks+i]);
+#else
+ for(size_t i = 0; i != count; ++i)
+ out[i] = load_le<T>(in, i);
+#endif
+ }
+ }
+
+/**
+* Load two big-endian words
+* @param in a pointer to some bytes
+* @param x0 where the first word will be written
+* @param x1 where the second word will be written
+*/
+template<typename T>
+inline void load_be(const uint8_t in[], T& x0, T& x1)
+ {
+ x0 = load_be<T>(in, 0);
+ x1 = load_be<T>(in, 1);
+ }
+
+/**
+* Load four big-endian words
+* @param in a pointer to some bytes
+* @param x0 where the first word will be written
+* @param x1 where the second word will be written
+* @param x2 where the third word will be written
+* @param x3 where the fourth word will be written
+*/
+template<typename T>
+inline void load_be(const uint8_t in[],
+ T& x0, T& x1, T& x2, T& x3)
+ {
+ x0 = load_be<T>(in, 0);
+ x1 = load_be<T>(in, 1);
+ x2 = load_be<T>(in, 2);
+ x3 = load_be<T>(in, 3);
+ }
+
+/**
+* Load eight big-endian words
+* @param in a pointer to some bytes
+* @param x0 where the first word will be written
+* @param x1 where the second word will be written
+* @param x2 where the third word will be written
+* @param x3 where the fourth word will be written
+* @param x4 where the fifth word will be written
+* @param x5 where the sixth word will be written
+* @param x6 where the seventh word will be written
+* @param x7 where the eighth word will be written
+*/
+template<typename T>
+inline void load_be(const uint8_t in[],
+ T& x0, T& x1, T& x2, T& x3,
+ T& x4, T& x5, T& x6, T& x7)
+ {
+ x0 = load_be<T>(in, 0);
+ x1 = load_be<T>(in, 1);
+ x2 = load_be<T>(in, 2);
+ x3 = load_be<T>(in, 3);
+ x4 = load_be<T>(in, 4);
+ x5 = load_be<T>(in, 5);
+ x6 = load_be<T>(in, 6);
+ x7 = load_be<T>(in, 7);
+ }
+
+/**
+* Load a variable number of big-endian words
+* @param out the output array of words
+* @param in the input array of bytes
+* @param count how many words are in in
+*/
+template<typename T>
+inline void load_be(T out[],
+ const uint8_t in[],
+ size_t count)
+ {
+ if(count > 0)
+ {
+#if defined(BOTAN_TARGET_CPU_IS_BIG_ENDIAN)
+ typecast_copy(out, in, count);
+
+#elif defined(BOTAN_TARGET_CPU_IS_LITTLE_ENDIAN)
+ typecast_copy(out, in, count);
+ const size_t blocks = count - (count % 4);
+ const size_t left = count - blocks;
+
+ for(size_t i = 0; i != blocks; i += 4)
+ bswap_4(out + i);
+
+ for(size_t i = 0; i != left; ++i)
+ out[blocks+i] = reverse_bytes(out[blocks+i]);
+#else
+ for(size_t i = 0; i != count; ++i)
+ out[i] = load_be<T>(in, i);
+#endif
+ }
+ }
+
+/**
+* Store a big-endian uint16_t
+* @param in the input uint16_t
+* @param out the byte array to write to
+*/
+inline void store_be(uint16_t in, uint8_t out[2])
+ {
+#if defined(BOTAN_ENDIAN_N2B)
+ uint16_t o = BOTAN_ENDIAN_N2B(in);
+ typecast_copy(out, o);
+#else
+ out[0] = get_byte(0, in);
+ out[1] = get_byte(1, in);
+#endif
+ }
+
+/**
+* Store a little-endian uint16_t
+* @param in the input uint16_t
+* @param out the byte array to write to
+*/
+inline void store_le(uint16_t in, uint8_t out[2])
+ {
+#if defined(BOTAN_ENDIAN_N2L)
+ uint16_t o = BOTAN_ENDIAN_N2L(in);
+ typecast_copy(out, o);
+#else
+ out[0] = get_byte(1, in);
+ out[1] = get_byte(0, in);
+#endif
+ }
+
+/**
+* Store a big-endian uint32_t
+* @param in the input uint32_t
+* @param out the byte array to write to
+*/
+inline void store_be(uint32_t in, uint8_t out[4])
+ {
+#if defined(BOTAN_ENDIAN_B2N)
+ uint32_t o = BOTAN_ENDIAN_B2N(in);
+ typecast_copy(out, o);
+#else
+ out[0] = get_byte(0, in);
+ out[1] = get_byte(1, in);
+ out[2] = get_byte(2, in);
+ out[3] = get_byte(3, in);
+#endif
+ }
+
+/**
+* Store a little-endian uint32_t
+* @param in the input uint32_t
+* @param out the byte array to write to
+*/
+inline void store_le(uint32_t in, uint8_t out[4])
+ {
+#if defined(BOTAN_ENDIAN_L2N)
+ uint32_t o = BOTAN_ENDIAN_L2N(in);
+ typecast_copy(out, o);
+#else
+ out[0] = get_byte(3, in);
+ out[1] = get_byte(2, in);
+ out[2] = get_byte(1, in);
+ out[3] = get_byte(0, in);
+#endif
+ }
+
+/**
+* Store a big-endian uint64_t
+* @param in the input uint64_t
+* @param out the byte array to write to
+*/
+inline void store_be(uint64_t in, uint8_t out[8])
+ {
+#if defined(BOTAN_ENDIAN_B2N)
+ uint64_t o = BOTAN_ENDIAN_B2N(in);
+ typecast_copy(out, o);
+#else
+ out[0] = get_byte(0, in);
+ out[1] = get_byte(1, in);
+ out[2] = get_byte(2, in);
+ out[3] = get_byte(3, in);
+ out[4] = get_byte(4, in);
+ out[5] = get_byte(5, in);
+ out[6] = get_byte(6, in);
+ out[7] = get_byte(7, in);
+#endif
+ }
+
+/**
+* Store a little-endian uint64_t
+* @param in the input uint64_t
+* @param out the byte array to write to
+*/
+inline void store_le(uint64_t in, uint8_t out[8])
+ {
+#if defined(BOTAN_ENDIAN_L2N)
+ uint64_t o = BOTAN_ENDIAN_L2N(in);
+ typecast_copy(out, o);
+#else
+ out[0] = get_byte(7, in);
+ out[1] = get_byte(6, in);
+ out[2] = get_byte(5, in);
+ out[3] = get_byte(4, in);
+ out[4] = get_byte(3, in);
+ out[5] = get_byte(2, in);
+ out[6] = get_byte(1, in);
+ out[7] = get_byte(0, in);
+#endif
+ }
+
+/**
+* Store two little-endian words
+* @param out the output byte array
+* @param x0 the first word
+* @param x1 the second word
+*/
+template<typename T>
+inline void store_le(uint8_t out[], T x0, T x1)
+ {
+ store_le(x0, out + (0 * sizeof(T)));
+ store_le(x1, out + (1 * sizeof(T)));
+ }
+
+/**
+* Store two big-endian words
+* @param out the output byte array
+* @param x0 the first word
+* @param x1 the second word
+*/
+template<typename T>
+inline void store_be(uint8_t out[], T x0, T x1)
+ {
+ store_be(x0, out + (0 * sizeof(T)));
+ store_be(x1, out + (1 * sizeof(T)));
+ }
+
+/**
+* Store four little-endian words
+* @param out the output byte array
+* @param x0 the first word
+* @param x1 the second word
+* @param x2 the third word
+* @param x3 the fourth word
+*/
+template<typename T>
+inline void store_le(uint8_t out[], T x0, T x1, T x2, T x3)
+ {
+ store_le(x0, out + (0 * sizeof(T)));
+ store_le(x1, out + (1 * sizeof(T)));
+ store_le(x2, out + (2 * sizeof(T)));
+ store_le(x3, out + (3 * sizeof(T)));
+ }
+
+/**
+* Store four big-endian words
+* @param out the output byte array
+* @param x0 the first word
+* @param x1 the second word
+* @param x2 the third word
+* @param x3 the fourth word
+*/
+template<typename T>
+inline void store_be(uint8_t out[], T x0, T x1, T x2, T x3)
+ {
+ store_be(x0, out + (0 * sizeof(T)));
+ store_be(x1, out + (1 * sizeof(T)));
+ store_be(x2, out + (2 * sizeof(T)));
+ store_be(x3, out + (3 * sizeof(T)));
+ }
+
+/**
+* Store eight little-endian words
+* @param out the output byte array
+* @param x0 the first word
+* @param x1 the second word
+* @param x2 the third word
+* @param x3 the fourth word
+* @param x4 the fifth word
+* @param x5 the sixth word
+* @param x6 the seventh word
+* @param x7 the eighth word
+*/
+template<typename T>
+inline void store_le(uint8_t out[], T x0, T x1, T x2, T x3,
+ T x4, T x5, T x6, T x7)
+ {
+ store_le(x0, out + (0 * sizeof(T)));
+ store_le(x1, out + (1 * sizeof(T)));
+ store_le(x2, out + (2 * sizeof(T)));
+ store_le(x3, out + (3 * sizeof(T)));
+ store_le(x4, out + (4 * sizeof(T)));
+ store_le(x5, out + (5 * sizeof(T)));
+ store_le(x6, out + (6 * sizeof(T)));
+ store_le(x7, out + (7 * sizeof(T)));
+ }
+
+/**
+* Store eight big-endian words
+* @param out the output byte array
+* @param x0 the first word
+* @param x1 the second word
+* @param x2 the third word
+* @param x3 the fourth word
+* @param x4 the fifth word
+* @param x5 the sixth word
+* @param x6 the seventh word
+* @param x7 the eighth word
+*/
+template<typename T>
+inline void store_be(uint8_t out[], T x0, T x1, T x2, T x3,
+ T x4, T x5, T x6, T x7)
+ {
+ store_be(x0, out + (0 * sizeof(T)));
+ store_be(x1, out + (1 * sizeof(T)));
+ store_be(x2, out + (2 * sizeof(T)));
+ store_be(x3, out + (3 * sizeof(T)));
+ store_be(x4, out + (4 * sizeof(T)));
+ store_be(x5, out + (5 * sizeof(T)));
+ store_be(x6, out + (6 * sizeof(T)));
+ store_be(x7, out + (7 * sizeof(T)));
+ }
+
+template<typename T>
+void copy_out_be(uint8_t out[], size_t out_bytes, const T in[])
+ {
+ while(out_bytes >= sizeof(T))
+ {
+ store_be(in[0], out);
+ out += sizeof(T);
+ out_bytes -= sizeof(T);
+ in += 1;
+ }
+
+ for(size_t i = 0; i != out_bytes; ++i)
+ out[i] = get_byte(i%8, in[0]);
+ }
+
+template<typename T, typename Alloc>
+void copy_out_vec_be(uint8_t out[], size_t out_bytes, const std::vector<T, Alloc>& in)
+ {
+ copy_out_be(out, out_bytes, in.data());
+ }
+
+template<typename T>
+void copy_out_le(uint8_t out[], size_t out_bytes, const T in[])
+ {
+ while(out_bytes >= sizeof(T))
+ {
+ store_le(in[0], out);
+ out += sizeof(T);
+ out_bytes -= sizeof(T);
+ in += 1;
+ }
+
+ for(size_t i = 0; i != out_bytes; ++i)
+ out[i] = get_byte(sizeof(T) - 1 - (i % 8), in[0]);
+ }
+
+template<typename T, typename Alloc>
+void copy_out_vec_le(uint8_t out[], size_t out_bytes, const std::vector<T, Alloc>& in)
+ {
+ copy_out_le(out, out_bytes, in.data());
+ }
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/utils/locking_allocator/info.txt b/comm/third_party/botan/src/lib/utils/locking_allocator/info.txt
new file mode 100644
index 0000000000..5d7a2ba83e
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/locking_allocator/info.txt
@@ -0,0 +1,12 @@
+<defines>
+LOCKING_ALLOCATOR -> 20131128
+</defines>
+
+<os_features>
+posix1
+virtual_lock
+</os_features>
+
+<requires>
+mem_pool
+</requires>
diff --git a/comm/third_party/botan/src/lib/utils/locking_allocator/locking_allocator.cpp b/comm/third_party/botan/src/lib/utils/locking_allocator/locking_allocator.cpp
new file mode 100644
index 0000000000..1c10c362b6
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/locking_allocator/locking_allocator.cpp
@@ -0,0 +1,75 @@
+/*
+* Mlock Allocator
+* (C) 2012,2014,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/locking_allocator.h>
+#include <botan/internal/os_utils.h>
+#include <botan/internal/mem_pool.h>
+
+namespace Botan {
+
+void* mlock_allocator::allocate(size_t num_elems, size_t elem_size)
+ {
+ if(!m_pool)
+ return nullptr;
+
+ const size_t n = num_elems * elem_size;
+ if(n / elem_size != num_elems)
+ return nullptr; // overflow!
+
+ return m_pool->allocate(n);
+ }
+
+bool mlock_allocator::deallocate(void* p, size_t num_elems, size_t elem_size) noexcept
+ {
+ if(!m_pool)
+ return false;
+
+ size_t n = num_elems * elem_size;
+
+ /*
+ We return nullptr in allocate if there was an overflow, so if an
+ overflow occurs here we know the pointer was not allocated by this pool.
+ */
+ if(n / elem_size != num_elems)
+ return false;
+
+ return m_pool->deallocate(p, n);
+ }
+
+mlock_allocator::mlock_allocator()
+ {
+ const size_t mem_to_lock = OS::get_memory_locking_limit();
+ const size_t page_size = OS::system_page_size();
+
+ if(mem_to_lock > 0 && mem_to_lock % page_size == 0)
+ {
+ m_locked_pages = OS::allocate_locked_pages(mem_to_lock / page_size);
+
+ if(m_locked_pages.size() > 0)
+ {
+ m_pool.reset(new Memory_Pool(m_locked_pages, page_size));
+ }
+ }
+ }
+
+mlock_allocator::~mlock_allocator()
+ {
+ if(m_pool)
+ {
+ m_pool.reset();
+ // OS::free_locked_pages scrubs the memory before free
+ OS::free_locked_pages(m_locked_pages);
+ }
+ }
+
+mlock_allocator& mlock_allocator::instance()
+ {
+ static mlock_allocator mlock;
+ return mlock;
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/utils/locking_allocator/locking_allocator.h b/comm/third_party/botan/src/lib/utils/locking_allocator/locking_allocator.h
new file mode 100644
index 0000000000..8d1d249809
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/locking_allocator/locking_allocator.h
@@ -0,0 +1,45 @@
+/*
+* Mlock Allocator
+* (C) 2012 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_MLOCK_ALLOCATOR_H_
+#define BOTAN_MLOCK_ALLOCATOR_H_
+
+#include <botan/types.h>
+#include <vector>
+#include <memory>
+
+BOTAN_FUTURE_INTERNAL_HEADER(locking_allocator.h)
+
+namespace Botan {
+
+class Memory_Pool;
+
+class BOTAN_PUBLIC_API(2,0) mlock_allocator final
+ {
+ public:
+ static mlock_allocator& instance();
+
+ 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& operator=(const mlock_allocator&) = delete;
+
+ private:
+ mlock_allocator();
+
+ ~mlock_allocator();
+
+ std::unique_ptr<Memory_Pool> m_pool;
+ std::vector<void*> m_locked_pages;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/utils/mem_ops.cpp b/comm/third_party/botan/src/lib/utils/mem_ops.cpp
new file mode 100644
index 0000000000..9ef1d6c4e3
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/mem_ops.cpp
@@ -0,0 +1,68 @@
+/*
+* (C) 2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/mem_ops.h>
+#include <botan/internal/ct_utils.h>
+#include <cstdlib>
+#include <new>
+
+#if defined(BOTAN_HAS_LOCKING_ALLOCATOR)
+ #include <botan/locking_allocator.h>
+#endif
+
+namespace Botan {
+
+BOTAN_MALLOC_FN void* allocate_memory(size_t elems, size_t elem_size)
+ {
+ if(elems == 0 || elem_size == 0)
+ return nullptr;
+
+#if defined(BOTAN_HAS_LOCKING_ALLOCATOR)
+ if(void* p = mlock_allocator::instance().allocate(elems, elem_size))
+ return p;
+#endif
+
+ void* ptr = std::calloc(elems, elem_size);
+ if(!ptr)
+ throw std::bad_alloc();
+ return ptr;
+ }
+
+void deallocate_memory(void* p, size_t elems, size_t elem_size)
+ {
+ if(p == nullptr)
+ return;
+
+ secure_scrub_memory(p, elems * elem_size);
+
+#if defined(BOTAN_HAS_LOCKING_ALLOCATOR)
+ if(mlock_allocator::instance().deallocate(p, elems, elem_size))
+ return;
+#endif
+
+ std::free(p);
+ }
+
+void initialize_allocator()
+ {
+#if defined(BOTAN_HAS_LOCKING_ALLOCATOR)
+ mlock_allocator::instance();
+#endif
+ }
+
+uint8_t ct_compare_u8(const uint8_t x[],
+ const uint8_t y[],
+ size_t len)
+ {
+ volatile uint8_t difference = 0;
+
+ for(size_t i = 0; i != len; ++i)
+ difference |= (x[i] ^ y[i]);
+
+ return CT::Mask<uint8_t>::is_zero(difference).value();
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/utils/mem_ops.h b/comm/third_party/botan/src/lib/utils/mem_ops.h
new file mode 100644
index 0000000000..deb4c01f28
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/mem_ops.h
@@ -0,0 +1,365 @@
+/*
+* Memory Operations
+* (C) 1999-2009,2012,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_MEMORY_OPS_H_
+#define BOTAN_MEMORY_OPS_H_
+
+#include <botan/types.h>
+#include <cstring>
+#include <type_traits>
+#include <vector>
+
+namespace Botan {
+
+/**
+* Allocate a memory buffer by some method. This should only be used for
+* primitive types (uint8_t, uint32_t, etc).
+*
+* @param elems the number of elements
+* @param elem_size the size of each element
+* @return pointer to allocated and zeroed memory, or throw std::bad_alloc on failure
+*/
+BOTAN_PUBLIC_API(2,3) BOTAN_MALLOC_FN void* allocate_memory(size_t elems, size_t elem_size);
+
+/**
+* Free a pointer returned by allocate_memory
+* @param p the pointer returned by allocate_memory
+* @param elems the number of elements, as passed to allocate_memory
+* @param elem_size the size of each element, as passed to allocate_memory
+*/
+BOTAN_PUBLIC_API(2,3) void deallocate_memory(void* p, size_t elems, size_t elem_size);
+
+/**
+* Ensure the allocator is initialized
+*/
+void BOTAN_UNSTABLE_API initialize_allocator();
+
+class Allocator_Initializer
+ {
+ public:
+ Allocator_Initializer() { initialize_allocator(); }
+ };
+
+/**
+* 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 (for example, in some hypothetical
+* implementation it might combine the memory contents with the output
+* of a system PRNG), but if you can detect any difference in behavior
+* at runtime then the clearing is side-effecting and you can just
+* use `clear_mem`.
+*
+* 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_PUBLIC_API(2,0) void secure_scrub_memory(void* ptr, size_t n);
+
+/**
+* Memory comparison, input insensitive
+* @param x a pointer to an array
+* @param y a pointer to another array
+* @param len the number of Ts in x and y
+* @return 0xFF iff x[i] == y[i] forall i in [0...n) or 0x00 otherwise
+*/
+BOTAN_PUBLIC_API(2,9) uint8_t ct_compare_u8(const uint8_t x[],
+ const uint8_t y[],
+ size_t len);
+
+/**
+* Memory comparison, input insensitive
+* @param x a pointer to an array
+* @param y a pointer to another array
+* @param len the number of Ts in x and y
+* @return true iff x[i] == y[i] forall i in [0...n)
+*/
+inline bool constant_time_compare(const uint8_t x[],
+ const uint8_t y[],
+ size_t len)
+ {
+ return ct_compare_u8(x, y, len) == 0xFF;
+ }
+
+/**
+* Zero out some bytes. Warning: use secure_scrub_memory instead if the
+* memory is about to be freed or otherwise the compiler thinks it can
+* elide the writes.
+*
+* @param ptr a pointer to memory to zero
+* @param bytes the number of bytes to zero in ptr
+*/
+inline void clear_bytes(void* ptr, size_t bytes)
+ {
+ if(bytes > 0)
+ {
+ std::memset(ptr, 0, bytes);
+ }
+ }
+
+/**
+* Zero memory before use. This simply calls memset and should not be
+* used in cases where the compiler cannot see the call as a
+* side-effecting operation (for example, if calling clear_mem before
+* deallocating memory, the compiler would be allowed to omit the call
+* to memset entirely under the as-if rule.)
+*
+* @param ptr a pointer to an array of Ts to zero
+* @param n the number of Ts pointed to by ptr
+*/
+template<typename T> inline void clear_mem(T* ptr, size_t n)
+ {
+ clear_bytes(ptr, sizeof(T)*n);
+ }
+
+// is_trivially_copyable is missing in g++ < 5.0
+#if (BOTAN_GCC_VERSION > 0 && BOTAN_GCC_VERSION < 500)
+#define BOTAN_IS_TRIVIALLY_COPYABLE(T) true
+#else
+#define BOTAN_IS_TRIVIALLY_COPYABLE(T) std::is_trivially_copyable<T>::value
+#endif
+
+/**
+* Copy memory
+* @param out the destination array
+* @param in the source array
+* @param n the number of elements of in/out
+*/
+template<typename T> inline void copy_mem(T* out, const T* in, size_t n)
+ {
+ static_assert(std::is_trivial<typename std::decay<T>::type>::value, "");
+ BOTAN_ASSERT_IMPLICATION(n > 0, in != nullptr && out != nullptr,
+ "If n > 0 then args are not null");
+
+ if(in != nullptr && out != nullptr && n > 0)
+ {
+ std::memmove(out, in, sizeof(T)*n);
+ }
+ }
+
+template<typename T> inline void typecast_copy(uint8_t out[], T in[], size_t N)
+ {
+ static_assert(BOTAN_IS_TRIVIALLY_COPYABLE(T), "");
+ std::memcpy(out, in, sizeof(T)*N);
+ }
+
+template<typename T> inline void typecast_copy(T out[], const uint8_t in[], size_t N)
+ {
+ static_assert(std::is_trivial<T>::value, "");
+ std::memcpy(out, in, sizeof(T)*N);
+ }
+
+template<typename T> inline void typecast_copy(uint8_t out[], T in)
+ {
+ typecast_copy(out, &in, 1);
+ }
+
+template<typename T> inline void typecast_copy(T& out, const uint8_t in[])
+ {
+ static_assert(std::is_trivial<typename std::decay<T>::type>::value, "");
+ typecast_copy(&out, in, 1);
+ }
+
+template <class To, class From> inline To typecast_copy(const From *src) noexcept
+ {
+ static_assert(BOTAN_IS_TRIVIALLY_COPYABLE(From) && std::is_trivial<To>::value, "");
+ To dst;
+ std::memcpy(&dst, src, sizeof(To));
+ return dst;
+ }
+
+/**
+* Set memory to a fixed value
+* @param ptr a pointer to an array of bytes
+* @param n the number of Ts pointed to by ptr
+* @param val the value to set each byte to
+*/
+inline void set_mem(uint8_t* ptr, size_t n, uint8_t val)
+ {
+ if(n > 0)
+ {
+ std::memset(ptr, val, n);
+ }
+ }
+
+inline const uint8_t* cast_char_ptr_to_uint8(const char* s)
+ {
+ return reinterpret_cast<const uint8_t*>(s);
+ }
+
+inline const char* cast_uint8_ptr_to_char(const uint8_t* b)
+ {
+ return reinterpret_cast<const char*>(b);
+ }
+
+inline uint8_t* cast_char_ptr_to_uint8(char* s)
+ {
+ return reinterpret_cast<uint8_t*>(s);
+ }
+
+inline char* cast_uint8_ptr_to_char(uint8_t* b)
+ {
+ return reinterpret_cast<char*>(b);
+ }
+
+/**
+* Memory comparison, input insensitive
+* @param p1 a pointer to an array
+* @param p2 a pointer to another array
+* @param n the number of Ts in p1 and p2
+* @return true iff p1[i] == p2[i] forall i in [0...n)
+*/
+template<typename T> inline bool same_mem(const T* p1, const T* p2, size_t n)
+ {
+ volatile T difference = 0;
+
+ for(size_t i = 0; i != n; ++i)
+ difference |= (p1[i] ^ p2[i]);
+
+ return difference == 0;
+ }
+
+template<typename T, typename Alloc>
+size_t buffer_insert(std::vector<T, Alloc>& buf,
+ size_t buf_offset,
+ const T input[],
+ size_t input_length)
+ {
+ BOTAN_ASSERT_NOMSG(buf_offset <= buf.size());
+ const size_t to_copy = std::min(input_length, buf.size() - buf_offset);
+ if(to_copy > 0)
+ {
+ copy_mem(&buf[buf_offset], input, to_copy);
+ }
+ return to_copy;
+ }
+
+template<typename T, typename Alloc, typename Alloc2>
+size_t buffer_insert(std::vector<T, Alloc>& buf,
+ size_t buf_offset,
+ const std::vector<T, Alloc2>& input)
+ {
+ BOTAN_ASSERT_NOMSG(buf_offset <= buf.size());
+ const size_t to_copy = std::min(input.size(), buf.size() - buf_offset);
+ if(to_copy > 0)
+ {
+ copy_mem(&buf[buf_offset], input.data(), to_copy);
+ }
+ return to_copy;
+ }
+
+/**
+* XOR arrays. Postcondition out[i] = in[i] ^ out[i] forall i = 0...length
+* @param out the input/output buffer
+* @param in the read-only input buffer
+* @param length the length of the buffers
+*/
+inline void xor_buf(uint8_t out[],
+ const uint8_t in[],
+ size_t length)
+ {
+ const size_t blocks = length - (length % 32);
+
+ for(size_t i = 0; i != blocks; i += 32)
+ {
+ uint64_t x[4];
+ uint64_t y[4];
+
+ typecast_copy(x, out + i, 4);
+ typecast_copy(y, in + i, 4);
+
+ x[0] ^= y[0];
+ x[1] ^= y[1];
+ x[2] ^= y[2];
+ x[3] ^= y[3];
+
+ typecast_copy(out + i, x, 4);
+ }
+
+ for(size_t i = blocks; i != length; ++i)
+ {
+ out[i] ^= in[i];
+ }
+ }
+
+/**
+* XOR arrays. Postcondition out[i] = in[i] ^ in2[i] forall i = 0...length
+* @param out the output buffer
+* @param in the first input buffer
+* @param in2 the second output buffer
+* @param length the length of the three buffers
+*/
+inline void xor_buf(uint8_t out[],
+ const uint8_t in[],
+ const uint8_t in2[],
+ size_t length)
+ {
+ const size_t blocks = length - (length % 32);
+
+ for(size_t i = 0; i != blocks; i += 32)
+ {
+ uint64_t x[4];
+ uint64_t y[4];
+
+ typecast_copy(x, in + i, 4);
+ typecast_copy(y, in2 + i, 4);
+
+ x[0] ^= y[0];
+ x[1] ^= y[1];
+ x[2] ^= y[2];
+ x[3] ^= y[3];
+
+ typecast_copy(out + i, x, 4);
+ }
+
+ for(size_t i = blocks; i != length; ++i)
+ {
+ out[i] = in[i] ^ in2[i];
+ }
+ }
+
+template<typename Alloc, typename Alloc2>
+void xor_buf(std::vector<uint8_t, Alloc>& out,
+ const std::vector<uint8_t, Alloc2>& in,
+ size_t n)
+ {
+ xor_buf(out.data(), in.data(), n);
+ }
+
+template<typename Alloc>
+void xor_buf(std::vector<uint8_t, Alloc>& out,
+ const uint8_t* in,
+ size_t n)
+ {
+ xor_buf(out.data(), in, n);
+ }
+
+template<typename Alloc, typename Alloc2>
+void xor_buf(std::vector<uint8_t, Alloc>& out,
+ const uint8_t* in,
+ const std::vector<uint8_t, Alloc2>& in2,
+ size_t n)
+ {
+ xor_buf(out.data(), in, in2.data(), n);
+ }
+
+template<typename Alloc, typename Alloc2>
+std::vector<uint8_t, Alloc>&
+operator^=(std::vector<uint8_t, Alloc>& out,
+ const std::vector<uint8_t, Alloc2>& in)
+ {
+ if(out.size() < in.size())
+ out.resize(in.size());
+
+ xor_buf(out.data(), in.data(), in.size());
+ return out;
+ }
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/utils/mem_pool/info.txt b/comm/third_party/botan/src/lib/utils/mem_pool/info.txt
new file mode 100644
index 0000000000..a8a5a53e1b
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/mem_pool/info.txt
@@ -0,0 +1,7 @@
+<defines>
+MEM_POOL -> 20180309
+</defines>
+
+<header:internal>
+mem_pool.h
+</header:internal>
diff --git a/comm/third_party/botan/src/lib/utils/mem_pool/mem_pool.cpp b/comm/third_party/botan/src/lib/utils/mem_pool/mem_pool.cpp
new file mode 100644
index 0000000000..1ff3f0759b
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/mem_pool/mem_pool.cpp
@@ -0,0 +1,435 @@
+/*
+* (C) 2018,2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/mem_pool.h>
+#include <botan/mem_ops.h>
+#include <algorithm>
+
+#if defined(BOTAN_MEM_POOL_USE_MMU_PROTECTIONS)
+ #include <botan/internal/os_utils.h>
+#endif
+
+namespace Botan {
+
+/*
+* Memory pool theory of operation
+*
+* This allocator is not useful for general purpose but works well within the
+* context of allocating cryptographic keys. It makes several assumptions which
+* don't work for implementing malloc but simplify and speed up the implementation:
+*
+* - There is some set of pages, which cannot be expanded later. These are pages
+* which were allocated, mlocked and passed to the Memory_Pool constructor.
+*
+* - The allocator is allowed to return null anytime it feels like not servicing
+* a request, in which case the request will be sent to calloc instead. In
+* particular, requests which are too small or too large are rejected.
+*
+* - Most allocations are powers of 2, the remainder are usually a multiple of 8
+*
+* - Free requests include the size of the allocation, so there is no need to
+* track this within the pool.
+*
+* - Alignment is important to the caller. For this allocator, any allocation of
+* size N is aligned evenly at N bytes.
+*
+* Initially each page is in the free page list. Each page is used for just one
+* size of allocation, with requests bucketed into a small number of common
+* sizes. If the allocation would be too big or too small it is rejected by the pool.
+*
+* The free list is maintained by a bitmap, one per page/Bucket. Since each
+* Bucket only maintains objects of a single size, each bit set or clear
+* indicates the status of one object.
+*
+* An allocation walks the list of buckets and asks each in turn if there is
+* space. If a Bucket does not have any space, it sets a boolean flag m_is_full
+* so that it does not need to rescan when asked again. The flag is cleared on
+* first free from that bucket. If no bucket has space, but there are some free
+* pages left, a free page is claimed as a new Bucket for that size. In this case
+* it is pushed to the front of the list so it is first in line to service new
+* requests.
+*
+* A deallocation also walks the list of buckets for the size and asks each
+* Bucket in turn if it recognizes the pointer. When a Bucket becomes empty as a
+* result of a deallocation, it is recycled back into the free pool. When this
+* happens, the Buckets page goes to the end of the free list. All pages on the
+* free list are marked in the MMU as noaccess, so anything touching them will
+* immediately crash. They are only marked R/W once placed into a new bucket.
+* Making the free list FIFO maximizes the time between the last free of a bucket
+* and that page being writable again, maximizing chances of crashing after a
+* use-after-free.
+*
+* Future work
+* -------------
+*
+* The allocator is protected by a global lock. It would be good to break this
+* up, since almost all of the work can actually be done in parallel especially
+* when allocating objects of different sizes (which can't possibly share a
+* bucket).
+*
+* It may be worthwhile to optimize deallocation by storing the Buckets in order
+* (by pointer value) which would allow binary search to find the owning bucket.
+*
+* A useful addition would be to randomize the allocations. Memory_Pool would be
+* changed to receive also a RandomNumberGenerator& object (presumably the system
+* RNG, or maybe a ChaCha_RNG seeded with system RNG). Then the bucket to use and
+* the offset within the bucket would be chosen randomly, instead of using first fit.
+*
+* Right now we don't make any provision for threading, so if two threads both
+* allocate 32 byte values one after the other, the two allocations will likely
+* share a cache line. Ensuring that distinct threads will (tend to) use distinct
+* buckets would reduce this.
+*
+* Supporting a realloc-style API may be useful.
+*/
+
+namespace {
+
+size_t choose_bucket(size_t n)
+ {
+ const size_t MINIMUM_ALLOCATION = 16;
+ const size_t MAXIMUM_ALLOCATION = 256;
+
+ if(n < MINIMUM_ALLOCATION || n > MAXIMUM_ALLOCATION)
+ return 0;
+
+ // Need to tune these
+
+ const size_t buckets[] = {
+ 16, 24, 32, 48, 64, 80, 96, 112, 128, 160, 192, 256, 0,
+ };
+
+ for(size_t i = 0; buckets[i]; ++i)
+ {
+ if(n <= buckets[i])
+ {
+ return buckets[i];
+ }
+ }
+
+ return 0;
+ }
+
+inline bool ptr_in_pool(const void* pool_ptr, size_t poolsize,
+ const void* buf_ptr, size_t bufsize)
+ {
+ const uintptr_t pool = reinterpret_cast<uintptr_t>(pool_ptr);
+ const uintptr_t buf = reinterpret_cast<uintptr_t>(buf_ptr);
+ return (buf >= pool) && (buf + bufsize <= pool + poolsize);
+ }
+
+// return index of first set bit
+template<typename T>
+size_t find_set_bit(T b)
+ {
+ size_t s = 8*sizeof(T) / 2;
+ size_t bit = 0;
+
+ // In this context we don't need to be const-time
+ while(s > 0)
+ {
+ const T mask = (static_cast<T>(1) << s) - 1;
+ if((b & mask) == 0)
+ {
+ bit += s;
+ b >>= s;
+ }
+ s /= 2;
+ }
+
+ return bit;
+ }
+
+class BitMap final
+ {
+ public:
+ BitMap(size_t bits) : m_len(bits)
+ {
+ m_bits.resize((bits + BITMASK_BITS - 1) / BITMASK_BITS);
+ m_main_mask = static_cast<bitmask_type>(~0);
+ m_last_mask = m_main_mask;
+
+ if(bits % BITMASK_BITS != 0)
+ m_last_mask = (static_cast<bitmask_type>(1) << (bits % BITMASK_BITS)) - 1;
+ }
+
+ bool find_free(size_t* bit);
+
+ void free(size_t bit)
+ {
+ 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<bitmask_type>(1) << (bit % BITMASK_BITS);
+ m_bits[w] = m_bits[w] & (~mask);
+ }
+
+ bool empty() const
+ {
+ for(size_t i = 0; i != m_bits.size(); ++i)
+ {
+ if(m_bits[i] != 0)
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private:
+#if defined(BOTAN_ENABLE_DEBUG_ASSERTS)
+ typedef uint8_t bitmask_type;
+ enum { BITMASK_BITS = 8 };
+#else
+ typedef word bitmask_type;
+ enum { BITMASK_BITS = BOTAN_MP_WORD_BITS };
+#endif
+
+ size_t m_len;
+ bitmask_type m_main_mask;
+ bitmask_type m_last_mask;
+ std::vector<bitmask_type> m_bits;
+ };
+
+bool BitMap::find_free(size_t* bit)
+ {
+ 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)
+ {
+ size_t free_bit = find_set_bit(~m_bits[i]);
+ const bitmask_type bmask = static_cast<bitmask_type>(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 false;
+ }
+
+}
+
+class Bucket final
+ {
+ public:
+ Bucket(uint8_t* mem, size_t mem_size, size_t item_size) :
+ m_item_size(item_size),
+ m_page_size(mem_size),
+ m_range(mem),
+ m_bitmap(mem_size / item_size),
+ m_is_full(false)
+ {
+ }
+
+ uint8_t* alloc()
+ {
+ if(m_is_full)
+ {
+ // I know I am full
+ return nullptr;
+ }
+
+ size_t offset;
+ if(!m_bitmap.find_free(&offset))
+ {
+ // I just found out I am full
+ m_is_full = true;
+ return nullptr;
+ }
+
+ BOTAN_ASSERT(offset * m_item_size < m_page_size, "Offset is in range");
+ return m_range + m_item_size*offset;
+ }
+
+ bool free(void* p)
+ {
+ if(!in_this_bucket(p))
+ return false;
+
+ /*
+ Zero also any trailing bytes, which should not have been written to,
+ but maybe the user was bad and wrote past the end.
+ */
+ std::memset(p, 0, m_item_size);
+
+ const size_t offset = (reinterpret_cast<uintptr_t>(p) - reinterpret_cast<uintptr_t>(m_range)) / m_item_size;
+
+ m_bitmap.free(offset);
+ m_is_full = false;
+
+ return true;
+ }
+
+ bool in_this_bucket(void* p) const
+ {
+ return ptr_in_pool(m_range, m_page_size, p, m_item_size);
+ }
+
+ bool empty() const
+ {
+ return m_bitmap.empty();
+ }
+
+ uint8_t* ptr() const
+ {
+ return m_range;
+ }
+
+ private:
+ size_t m_item_size;
+ size_t m_page_size;
+ uint8_t* m_range;
+ BitMap m_bitmap;
+ bool m_is_full;
+ };
+
+Memory_Pool::Memory_Pool(const std::vector<void*>& pages, size_t page_size) :
+ m_page_size(page_size)
+ {
+ m_min_page_ptr = ~static_cast<uintptr_t>(0);
+ m_max_page_ptr = 0;
+
+ for(size_t i = 0; i != pages.size(); ++i)
+ {
+ const uintptr_t p = reinterpret_cast<uintptr_t>(pages[i]);
+
+ m_min_page_ptr = std::min(p, m_min_page_ptr);
+ m_max_page_ptr = std::max(p, m_max_page_ptr);
+
+ clear_bytes(pages[i], m_page_size);
+#if defined(BOTAN_MEM_POOL_USE_MMU_PROTECTIONS)
+ OS::page_prohibit_access(pages[i]);
+#endif
+ m_free_pages.push_back(static_cast<uint8_t*>(pages[i]));
+ }
+
+ /*
+ Right now this points to the start of the last page, adjust it to instead
+ point to the first byte of the following page
+ */
+ m_max_page_ptr += page_size;
+ }
+
+Memory_Pool::~Memory_Pool()
+ {
+#if defined(BOTAN_MEM_POOL_USE_MMU_PROTECTIONS)
+ for(size_t i = 0; i != m_free_pages.size(); ++i)
+ {
+ OS::page_allow_access(m_free_pages[i]);
+ }
+#endif
+ }
+
+void* Memory_Pool::allocate(size_t n)
+ {
+ if(n > m_page_size)
+ return nullptr;
+
+ const size_t n_bucket = choose_bucket(n);
+
+ if(n_bucket > 0)
+ {
+ lock_guard_type<mutex_type> lock(m_mutex);
+
+ std::deque<Bucket>& buckets = m_buckets_for[n_bucket];
+
+ /*
+ It would be optimal to pick the bucket with the most usage,
+ since a bucket with say 1 item allocated out of it has a high
+ chance of becoming later freed and then the whole page can be
+ recycled.
+ */
+ for(auto& bucket : buckets)
+ {
+ if(uint8_t* p = bucket.alloc())
+ return p;
+
+ // If the bucket is full, maybe move it to the end of the list?
+ // Otoh bucket search should be very fast
+ }
+
+ if(m_free_pages.size() > 0)
+ {
+ uint8_t* ptr = m_free_pages[0];
+ m_free_pages.pop_front();
+#if defined(BOTAN_MEM_POOL_USE_MMU_PROTECTIONS)
+ OS::page_allow_access(ptr);
+#endif
+ buckets.push_front(Bucket(ptr, m_page_size, n_bucket));
+ void* p = buckets[0].alloc();
+ BOTAN_ASSERT_NOMSG(p != nullptr);
+ return p;
+ }
+ }
+
+ // out of room
+ return nullptr;
+ }
+
+bool Memory_Pool::deallocate(void* p, size_t len) noexcept
+ {
+ // Do a fast range check first, before taking the lock
+ const uintptr_t p_val = reinterpret_cast<uintptr_t>(p);
+ if(p_val < m_min_page_ptr || p_val > m_max_page_ptr)
+ return false;
+
+ const size_t n_bucket = choose_bucket(len);
+
+ if(n_bucket != 0)
+ {
+ try
+ {
+ lock_guard_type<mutex_type> lock(m_mutex);
+
+ std::deque<Bucket>& buckets = m_buckets_for[n_bucket];
+
+ for(size_t i = 0; i != buckets.size(); ++i)
+ {
+ Bucket& bucket = buckets[i];
+ if(bucket.free(p))
+ {
+ if(bucket.empty())
+ {
+#if defined(BOTAN_MEM_POOL_USE_MMU_PROTECTIONS)
+ OS::page_prohibit_access(bucket.ptr());
+#endif
+ m_free_pages.push_back(bucket.ptr());
+
+ if(i != buckets.size() - 1)
+ std::swap(buckets.back(), buckets[i]);
+ buckets.pop_back();
+ }
+ return true;
+ }
+ }
+ }
+ catch(...)
+ {
+ /*
+ * The only exception throws that can occur in the above code are from
+ * either the STL or BOTAN_ASSERT failures. In either case, such an
+ * error indicates a logic error or data corruption in the memory
+ * allocator such that it is no longer safe to continue executing.
+ *
+ * Since this function is noexcept, simply letting the exception escape
+ * is sufficient for terminate to be called. However in this scenario
+ * it is implementation defined if any stack unwinding is performed.
+ * Since stack unwinding could cause further memory deallocations this
+ * could result in further corruption in this allocator state. To prevent
+ * this, call terminate directly.
+ */
+ std::terminate();
+ }
+ }
+
+ return false;
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/utils/mem_pool/mem_pool.h b/comm/third_party/botan/src/lib/utils/mem_pool/mem_pool.h
new file mode 100644
index 0000000000..4fcec15eeb
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/mem_pool/mem_pool.h
@@ -0,0 +1,58 @@
+/*
+* (C) 2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_MEM_POOL_H_
+#define BOTAN_MEM_POOL_H_
+
+#include <botan/types.h>
+#include <botan/mutex.h>
+#include <vector>
+#include <deque>
+#include <map>
+
+namespace Botan {
+
+class Bucket;
+
+class BOTAN_TEST_API Memory_Pool final
+ {
+ public:
+ /**
+ * Initialize a memory pool. The memory is not owned by *this,
+ * it must be freed by the caller.
+ * @param pages a list of pages to allocate from
+ * @param page_size the system page size, each page should
+ * point to exactly this much memory.
+ */
+ Memory_Pool(const std::vector<void*>& pages,
+ size_t page_size);
+
+ ~Memory_Pool();
+
+ void* allocate(size_t size);
+
+ bool deallocate(void* p, size_t size) noexcept;
+
+ Memory_Pool(const Memory_Pool&) = delete;
+ Memory_Pool(Memory_Pool&&) = delete;
+
+ Memory_Pool& operator=(const Memory_Pool&) = delete;
+ Memory_Pool& operator=(Memory_Pool&&) = delete;
+
+ private:
+ const size_t m_page_size = 0;
+
+ mutex_type m_mutex;
+
+ std::deque<uint8_t*> m_free_pages;
+ std::map<size_t, std::deque<Bucket>> m_buckets_for;
+ uintptr_t m_min_page_ptr;
+ uintptr_t m_max_page_ptr;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/utils/mul128.h b/comm/third_party/botan/src/lib/utils/mul128.h
new file mode 100644
index 0000000000..8cdaae21e0
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/mul128.h
@@ -0,0 +1,125 @@
+/*
+* 64x64->128 bit multiply operation
+* (C) 2013,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_UTIL_MUL128_H_
+#define BOTAN_UTIL_MUL128_H_
+
+#include <botan/types.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(mul128.h)
+
+namespace Botan {
+
+#if defined(__SIZEOF_INT128__) && defined(BOTAN_TARGET_CPU_HAS_NATIVE_64BIT)
+ #define BOTAN_TARGET_HAS_NATIVE_UINT128
+
+ // Prefer TI mode over __int128 as GCC rejects the latter in pendantic mode
+ #if defined(__GNUG__)
+ typedef unsigned int uint128_t __attribute__((mode(TI)));
+ #else
+ typedef unsigned __int128 uint128_t;
+ #endif
+#endif
+
+}
+
+#if defined(BOTAN_TARGET_HAS_NATIVE_UINT128)
+
+#define BOTAN_FAST_64X64_MUL(a,b,lo,hi) \
+ do { \
+ const uint128_t r = static_cast<uint128_t>(a) * b; \
+ *hi = (r >> 64) & 0xFFFFFFFFFFFFFFFF; \
+ *lo = (r ) & 0xFFFFFFFFFFFFFFFF; \
+ } while(0)
+
+#elif defined(BOTAN_BUILD_COMPILER_IS_MSVC) && defined(BOTAN_TARGET_CPU_HAS_NATIVE_64BIT)
+
+#include <intrin.h>
+#pragma intrinsic(_umul128)
+
+#define BOTAN_FAST_64X64_MUL(a,b,lo,hi) \
+ do { *lo = _umul128(a, b, hi); } while(0)
+
+#elif defined(BOTAN_USE_GCC_INLINE_ASM)
+
+#if defined(BOTAN_TARGET_ARCH_IS_X86_64)
+
+#define BOTAN_FAST_64X64_MUL(a,b,lo,hi) do { \
+ asm("mulq %3" : "=d" (*hi), "=a" (*lo) : "a" (a), "rm" (b) : "cc"); \
+ } while(0)
+
+#elif defined(BOTAN_TARGET_ARCH_IS_ALPHA)
+
+#define BOTAN_FAST_64X64_MUL(a,b,lo,hi) do { \
+ asm("umulh %1,%2,%0" : "=r" (*hi) : "r" (a), "r" (b)); \
+ *lo = a * b; \
+} while(0)
+
+#elif defined(BOTAN_TARGET_ARCH_IS_IA64)
+
+#define BOTAN_FAST_64X64_MUL(a,b,lo,hi) do { \
+ asm("xmpy.hu %0=%1,%2" : "=f" (*hi) : "f" (a), "f" (b)); \
+ *lo = a * b; \
+} while(0)
+
+#elif defined(BOTAN_TARGET_ARCH_IS_PPC64)
+
+#define BOTAN_FAST_64X64_MUL(a,b,lo,hi) do { \
+ asm("mulhdu %0,%1,%2" : "=r" (*hi) : "r" (a), "r" (b) : "cc"); \
+ *lo = a * b; \
+} while(0)
+
+#endif
+
+#endif
+
+namespace Botan {
+
+/**
+* Perform a 64x64->128 bit multiplication
+*/
+inline void mul64x64_128(uint64_t a, uint64_t b, uint64_t* lo, uint64_t* hi)
+ {
+#if defined(BOTAN_FAST_64X64_MUL)
+ BOTAN_FAST_64X64_MUL(a, b, lo, hi);
+#else
+
+ /*
+ * Do a 64x64->128 multiply using four 32x32->64 multiplies plus
+ * some adds and shifts. Last resort for CPUs like UltraSPARC (with
+ * 64-bit registers/ALU, but no 64x64->128 multiply) or 32-bit CPUs.
+ */
+ const size_t HWORD_BITS = 32;
+ const uint32_t HWORD_MASK = 0xFFFFFFFF;
+
+ const uint32_t a_hi = (a >> HWORD_BITS);
+ const uint32_t a_lo = (a & HWORD_MASK);
+ const uint32_t b_hi = (b >> HWORD_BITS);
+ const uint32_t b_lo = (b & HWORD_MASK);
+
+ uint64_t x0 = static_cast<uint64_t>(a_hi) * b_hi;
+ uint64_t x1 = static_cast<uint64_t>(a_lo) * b_hi;
+ uint64_t x2 = static_cast<uint64_t>(a_hi) * b_lo;
+ uint64_t x3 = static_cast<uint64_t>(a_lo) * b_lo;
+
+ // this cannot overflow as (2^32-1)^2 + 2^32-1 < 2^64-1
+ x2 += x3 >> HWORD_BITS;
+
+ // this one can overflow
+ x2 += x1;
+
+ // propagate the carry if any
+ x0 += static_cast<uint64_t>(static_cast<bool>(x2 < x1)) << HWORD_BITS;
+
+ *hi = x0 + (x2 >> HWORD_BITS);
+ *lo = ((x2 & HWORD_MASK) << HWORD_BITS) + (x3 & HWORD_MASK);
+#endif
+ }
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/utils/mutex.h b/comm/third_party/botan/src/lib/utils/mutex.h
new file mode 100644
index 0000000000..a230af9888
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/mutex.h
@@ -0,0 +1,60 @@
+/*
+* (C) 2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_UTIL_MUTEX_H_
+#define BOTAN_UTIL_MUTEX_H_
+
+#include <botan/types.h>
+
+#if defined(BOTAN_TARGET_OS_HAS_THREADS)
+
+#include <mutex>
+
+namespace Botan {
+
+template<typename T> using lock_guard_type = std::lock_guard<T>;
+typedef std::mutex mutex_type;
+typedef std::recursive_mutex recursive_mutex_type;
+
+}
+
+#else
+
+// No threads
+
+namespace Botan {
+
+template<typename Mutex>
+class lock_guard final
+ {
+ public:
+ explicit lock_guard(Mutex& m) : m_mutex(m)
+ { m_mutex.lock(); }
+
+ ~lock_guard() { m_mutex.unlock(); }
+
+ lock_guard(const lock_guard& other) = delete;
+ lock_guard& operator=(const lock_guard& other) = delete;
+ private:
+ Mutex& m_mutex;
+ };
+
+class noop_mutex final
+ {
+ public:
+ void lock() {}
+ void unlock() {}
+ };
+
+typedef noop_mutex mutex_type;
+typedef noop_mutex recursive_mutex_type;
+template<typename T> using lock_guard_type = lock_guard<T>;
+
+}
+
+#endif
+
+#endif
diff --git a/comm/third_party/botan/src/lib/utils/os_utils.cpp b/comm/third_party/botan/src/lib/utils/os_utils.cpp
new file mode 100644
index 0000000000..f5151da42b
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/os_utils.cpp
@@ -0,0 +1,757 @@
+/*
+* OS and machine specific utility functions
+* (C) 2015,2016,2017,2018 Jack Lloyd
+* (C) 2016 Daniel Neus
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/os_utils.h>
+#include <botan/cpuid.h>
+#include <botan/exceptn.h>
+#include <botan/mem_ops.h>
+
+#include <algorithm>
+#include <chrono>
+#include <cstdlib>
+
+#if defined(BOTAN_TARGET_OS_HAS_THREADS)
+ #include <thread>
+#endif
+
+#if defined(BOTAN_TARGET_OS_HAS_EXPLICIT_BZERO)
+ #include <string.h>
+#endif
+
+#if defined(BOTAN_TARGET_OS_HAS_POSIX1)
+ #include <sys/types.h>
+ #include <sys/resource.h>
+ #include <sys/mman.h>
+ #include <signal.h>
+ #include <stdlib.h>
+ #include <setjmp.h>
+ #include <unistd.h>
+ #include <errno.h>
+ #include <termios.h>
+ #undef B0
+#endif
+
+#if defined(BOTAN_TARGET_OS_IS_EMSCRIPTEN)
+ #include <emscripten/emscripten.h>
+#endif
+
+#if defined(BOTAN_TARGET_OS_HAS_GETAUXVAL) || defined(BOTAN_TARGET_OS_IS_ANDROID) || \
+ defined(BOTAN_TARGET_OS_HAS_ELF_AUX_INFO)
+ #include <sys/auxv.h>
+#endif
+
+#if defined(BOTAN_TARGET_OS_HAS_WIN32)
+ #define NOMINMAX 1
+ #define _WINSOCKAPI_ // stop windows.h including winsock.h
+ #include <windows.h>
+#endif
+
+#if defined(BOTAN_TARGET_OS_IS_ANDROID)
+ #include <elf.h>
+ extern "C" char **environ;
+#endif
+
+#if defined(BOTAN_TARGET_OS_IS_IOS) || defined(BOTAN_TARGET_OS_IS_MACOS)
+ #include <mach/vm_statistics.h>
+#endif
+
+namespace Botan {
+
+// Not defined in OS namespace for historical reasons
+void secure_scrub_memory(void* ptr, size_t n)
+ {
+#if defined(BOTAN_TARGET_OS_HAS_RTLSECUREZEROMEMORY)
+ ::RtlSecureZeroMemory(ptr, n);
+
+#elif defined(BOTAN_TARGET_OS_HAS_EXPLICIT_BZERO)
+ ::explicit_bzero(ptr, n);
+
+#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)
+ /*
+ 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.
+ */
+ static void* (*const volatile memset_ptr)(void*, int, size_t) = std::memset;
+ (memset_ptr)(ptr, 0, n);
+#else
+
+ volatile uint8_t* p = reinterpret_cast<volatile uint8_t*>(ptr);
+
+ for(size_t i = 0; i != n; ++i)
+ p[i] = 0;
+#endif
+ }
+
+uint32_t OS::get_process_id()
+ {
+#if defined(BOTAN_TARGET_OS_HAS_POSIX1)
+ return ::getpid();
+#elif defined(BOTAN_TARGET_OS_HAS_WIN32)
+ return ::GetCurrentProcessId();
+#elif defined(BOTAN_TARGET_OS_IS_INCLUDEOS) || defined(BOTAN_TARGET_OS_IS_LLVM) || defined(BOTAN_TARGET_OS_IS_NONE)
+ return 0; // truly no meaningful value
+#else
+ #error "Missing get_process_id"
+#endif
+ }
+
+unsigned long OS::get_auxval(unsigned long id)
+ {
+#if defined(BOTAN_TARGET_OS_HAS_GETAUXVAL)
+ return ::getauxval(id);
+#elif defined(BOTAN_TARGET_OS_IS_ANDROID) && defined(BOTAN_TARGET_ARCH_IS_ARM32)
+
+ if(id == 0)
+ return 0;
+
+ char **p = environ;
+
+ while(*p++ != nullptr)
+ ;
+
+ Elf32_auxv_t *e = reinterpret_cast<Elf32_auxv_t*>(p);
+
+ while(e != nullptr)
+ {
+ if(e->a_type == id)
+ return e->a_un.a_val;
+ e++;
+ }
+
+ return 0;
+#elif defined(BOTAN_TARGET_OS_HAS_ELF_AUX_INFO)
+ unsigned long auxinfo = 0;
+ ::elf_aux_info(id, &auxinfo, sizeof(auxinfo));
+ return auxinfo;
+#else
+ BOTAN_UNUSED(id);
+ return 0;
+#endif
+ }
+
+bool OS::running_in_privileged_state()
+ {
+#if defined(AT_SECURE)
+ return OS::get_auxval(AT_SECURE) != 0;
+#elif defined(BOTAN_TARGET_OS_HAS_POSIX1)
+ return (::getuid() != ::geteuid()) || (::getgid() != ::getegid());
+#else
+ return false;
+#endif
+ }
+
+uint64_t OS::get_cpu_cycle_counter()
+ {
+ uint64_t rtc = 0;
+
+#if defined(BOTAN_TARGET_OS_HAS_WIN32)
+ LARGE_INTEGER tv;
+ ::QueryPerformanceCounter(&tv);
+ rtc = tv.QuadPart;
+
+#elif defined(BOTAN_USE_GCC_INLINE_ASM)
+
+#if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
+
+ if(CPUID::has_rdtsc())
+ {
+ uint32_t rtc_low = 0, rtc_high = 0;
+ asm volatile("rdtsc" : "=d" (rtc_high), "=a" (rtc_low));
+ rtc = (static_cast<uint64_t>(rtc_high) << 32) | rtc_low;
+ }
+
+#elif defined(BOTAN_TARGET_ARCH_IS_PPC64)
+
+ for(;;)
+ {
+ uint32_t rtc_low = 0, rtc_high = 0, rtc_high2 = 0;
+ asm volatile("mftbu %0" : "=r" (rtc_high));
+ asm volatile("mftb %0" : "=r" (rtc_low));
+ asm volatile("mftbu %0" : "=r" (rtc_high2));
+
+ if(rtc_high == rtc_high2)
+ {
+ rtc = (static_cast<uint64_t>(rtc_high) << 32) | rtc_low;
+ break;
+ }
+ }
+
+#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)
+ asm volatile("rd %%tick, %0" : "=r" (rtc));
+
+#elif defined(BOTAN_TARGET_ARCH_IS_IA64)
+ asm volatile("mov %0=ar.itc" : "=r" (rtc));
+
+#elif defined(BOTAN_TARGET_ARCH_IS_S390X)
+ asm volatile("stck 0(%0)" : : "a" (&rtc) : "memory", "cc");
+
+#elif defined(BOTAN_TARGET_ARCH_IS_HPPA)
+ asm volatile("mfctl 16,%0" : "=r" (rtc)); // 64-bit only?
+
+#else
+ //#warning "OS::get_cpu_cycle_counter not implemented"
+#endif
+
+#endif
+
+ return rtc;
+ }
+
+size_t OS::get_cpu_total()
+ {
+#if defined(BOTAN_TARGET_OS_HAS_POSIX1) && defined(_SC_NPROCESSORS_CONF)
+ const long res = ::sysconf(_SC_NPROCESSORS_CONF);
+ if(res > 0)
+ return static_cast<size_t>(res);
+#endif
+
+#if defined(BOTAN_TARGET_OS_HAS_THREADS)
+ return static_cast<size_t>(std::thread::hardware_concurrency());
+#else
+ return 1;
+#endif
+ }
+
+size_t OS::get_cpu_available()
+ {
+#if defined(BOTAN_TARGET_OS_HAS_POSIX1) && defined(_SC_NPROCESSORS_ONLN)
+ const long res = ::sysconf(_SC_NPROCESSORS_ONLN);
+ if(res > 0)
+ return static_cast<size_t>(res);
+#endif
+
+ return OS::get_cpu_total();
+ }
+
+uint64_t OS::get_high_resolution_clock()
+ {
+ if(uint64_t cpu_clock = OS::get_cpu_cycle_counter())
+ return cpu_clock;
+
+#if defined(BOTAN_TARGET_OS_IS_EMSCRIPTEN)
+ return emscripten_get_now();
+#endif
+
+ /*
+ If we got here either we either don't have an asm instruction
+ above, or (for x86) RDTSC is not available at runtime. Try some
+ clock_gettimes and return the first one that works, or otherwise
+ fall back to std::chrono.
+ */
+
+#if defined(BOTAN_TARGET_OS_HAS_CLOCK_GETTIME)
+
+ // The ordering here is somewhat arbitrary...
+ const clockid_t clock_types[] = {
+#if defined(CLOCK_MONOTONIC_HR)
+ CLOCK_MONOTONIC_HR,
+#endif
+#if defined(CLOCK_MONOTONIC_RAW)
+ CLOCK_MONOTONIC_RAW,
+#endif
+#if defined(CLOCK_MONOTONIC)
+ CLOCK_MONOTONIC,
+#endif
+#if defined(CLOCK_PROCESS_CPUTIME_ID)
+ CLOCK_PROCESS_CPUTIME_ID,
+#endif
+#if defined(CLOCK_THREAD_CPUTIME_ID)
+ CLOCK_THREAD_CPUTIME_ID,
+#endif
+ };
+
+ for(clockid_t clock : clock_types)
+ {
+ struct timespec ts;
+ if(::clock_gettime(clock, &ts) == 0)
+ {
+ return (static_cast<uint64_t>(ts.tv_sec) * 1000000000) + static_cast<uint64_t>(ts.tv_nsec);
+ }
+ }
+#endif
+
+ // Plain C++11 fallback
+ auto now = std::chrono::high_resolution_clock::now().time_since_epoch();
+ return std::chrono::duration_cast<std::chrono::nanoseconds>(now).count();
+ }
+
+uint64_t OS::get_system_timestamp_ns()
+ {
+#if defined(BOTAN_TARGET_OS_HAS_CLOCK_GETTIME)
+ struct timespec ts;
+ if(::clock_gettime(CLOCK_REALTIME, &ts) == 0)
+ {
+ return (static_cast<uint64_t>(ts.tv_sec) * 1000000000) + static_cast<uint64_t>(ts.tv_nsec);
+ }
+#endif
+
+ auto now = std::chrono::system_clock::now().time_since_epoch();
+ return std::chrono::duration_cast<std::chrono::nanoseconds>(now).count();
+ }
+
+size_t OS::system_page_size()
+ {
+ const size_t default_page_size = 4096;
+
+#if defined(BOTAN_TARGET_OS_HAS_POSIX1)
+ long p = ::sysconf(_SC_PAGESIZE);
+ if(p > 1)
+ return static_cast<size_t>(p);
+ else
+ return default_page_size;
+#elif defined(BOTAN_TARGET_OS_HAS_VIRTUAL_LOCK)
+ BOTAN_UNUSED(default_page_size);
+ SYSTEM_INFO sys_info;
+ ::GetSystemInfo(&sys_info);
+ return sys_info.dwPageSize;
+#else
+ return default_page_size;
+#endif
+ }
+
+size_t OS::get_memory_locking_limit()
+ {
+#if defined(BOTAN_TARGET_OS_HAS_POSIX1) && defined(BOTAN_TARGET_OS_HAS_POSIX_MLOCK) && defined(RLIMIT_MEMLOCK)
+ /*
+ * If RLIMIT_MEMLOCK is not defined, likely the OS does not support
+ * unprivileged mlock calls.
+ *
+ * Linux defaults to only 64 KiB of mlockable memory per process
+ * (too small) but BSDs offer a small fraction of total RAM (more
+ * than we need). Bound the total mlock size to 512 KiB which is
+ * enough to run the entire test suite without spilling to non-mlock
+ * memory (and thus presumably also enough for many useful
+ * programs), but small enough that we should not cause problems
+ * even if many processes are mlocking on the same machine.
+ */
+ const size_t user_req = read_env_variable_sz("BOTAN_MLOCK_POOL_SIZE", BOTAN_MLOCK_ALLOCATOR_MAX_LOCKED_KB);
+
+ const size_t mlock_requested = std::min<size_t>(user_req, BOTAN_MLOCK_ALLOCATOR_MAX_LOCKED_KB);
+
+ if(mlock_requested > 0)
+ {
+ struct ::rlimit limits;
+
+ ::getrlimit(RLIMIT_MEMLOCK, &limits);
+
+ if(limits.rlim_cur < limits.rlim_max)
+ {
+ limits.rlim_cur = limits.rlim_max;
+ ::setrlimit(RLIMIT_MEMLOCK, &limits);
+ ::getrlimit(RLIMIT_MEMLOCK, &limits);
+ }
+
+ return std::min<size_t>(limits.rlim_cur, mlock_requested * 1024);
+ }
+
+#elif defined(BOTAN_TARGET_OS_HAS_VIRTUAL_LOCK)
+ SIZE_T working_min = 0, working_max = 0;
+ if(!::GetProcessWorkingSetSize(::GetCurrentProcess(), &working_min, &working_max))
+ {
+ return 0;
+ }
+
+ // According to Microsoft MSDN:
+ // The maximum number of pages that a process can lock is equal to the number of pages in its minimum working set minus a small overhead
+ // In the book "Windows Internals Part 2": the maximum lockable pages are minimum working set size - 8 pages
+ // But the information in the book seems to be inaccurate/outdated
+ // I've tested this on Windows 8.1 x64, Windows 10 x64 and Windows 7 x86
+ // On all three OS the value is 11 instead of 8
+ const size_t overhead = OS::system_page_size() * 11;
+ if(working_min > overhead)
+ {
+ const size_t lockable_bytes = working_min - overhead;
+ return std::min<size_t>(lockable_bytes, BOTAN_MLOCK_ALLOCATOR_MAX_LOCKED_KB * 1024);
+ }
+#endif
+
+ // Not supported on this platform
+ return 0;
+ }
+
+bool OS::read_env_variable(std::string& value_out, const std::string& name)
+ {
+ value_out = "";
+
+ if(running_in_privileged_state())
+ return false;
+
+#if defined(BOTAN_TARGET_OS_HAS_WIN32) && defined(BOTAN_BUILD_COMPILER_IS_MSVC)
+ char val[128] = { 0 };
+ size_t req_size = 0;
+ if(getenv_s(&req_size, val, sizeof(val), name.c_str()) == 0)
+ {
+ value_out = std::string(val, req_size);
+ return true;
+ }
+#else
+ if(const char* val = std::getenv(name.c_str()))
+ {
+ value_out = val;
+ return true;
+ }
+#endif
+
+ return false;
+ }
+
+size_t OS::read_env_variable_sz(const std::string& name, size_t def)
+ {
+ std::string value;
+ if(read_env_variable(value, name))
+ {
+ try
+ {
+ const size_t val = std::stoul(value, nullptr);
+ return val;
+ }
+ catch(std::exception&) { /* ignore it */ }
+ }
+
+ return def;
+ }
+
+#if defined(BOTAN_TARGET_OS_HAS_POSIX1) && defined(BOTAN_TARGET_OS_HAS_POSIX_MLOCK)
+
+namespace {
+
+int get_locked_fd()
+ {
+#if defined(BOTAN_TARGET_OS_IS_IOS) || defined(BOTAN_TARGET_OS_IS_MACOS)
+ // On Darwin, tagging anonymous pages allows vmmap to track these.
+ // Allowed from 240 to 255 for userland applications
+ static constexpr int default_locked_fd = 255;
+ int locked_fd = default_locked_fd;
+
+ if(size_t locked_fdl = OS::read_env_variable_sz("BOTAN_LOCKED_FD", default_locked_fd))
+ {
+ if(locked_fdl < 240 || locked_fdl > 255)
+ {
+ locked_fdl = default_locked_fd;
+ }
+ locked_fd = static_cast<int>(locked_fdl);
+ }
+ return VM_MAKE_TAG(locked_fd);
+#else
+ return -1;
+#endif
+ }
+
+}
+
+#endif
+
+std::vector<void*> OS::allocate_locked_pages(size_t count)
+ {
+ std::vector<void*> result;
+
+#if (defined(BOTAN_TARGET_OS_HAS_POSIX1) && defined(BOTAN_TARGET_OS_HAS_POSIX_MLOCK)) || defined(BOTAN_TARGET_OS_HAS_VIRTUAL_LOCK)
+
+ result.reserve(count);
+
+ const size_t page_size = OS::system_page_size();
+
+#if defined(BOTAN_TARGET_OS_HAS_POSIX1) && defined(BOTAN_TARGET_OS_HAS_POSIX_MLOCK)
+ static const int locked_fd = get_locked_fd();
+#endif
+
+ for(size_t i = 0; i != count; ++i)
+ {
+ void* ptr = nullptr;
+
+#if defined(BOTAN_TARGET_OS_HAS_POSIX1) && defined(BOTAN_TARGET_OS_HAS_POSIX_MLOCK)
+
+#if !defined(MAP_ANONYMOUS)
+ #define MAP_ANONYMOUS MAP_ANON
+#endif
+
+#if !defined(MAP_NOCORE)
+#if defined(MAP_CONCEAL)
+ #define MAP_NOCORE MAP_CONCEAL
+#else
+ #define MAP_NOCORE 0
+#endif
+#endif
+
+#if !defined(PROT_MAX)
+ #define PROT_MAX(p) 0
+#endif
+ const int pflags = PROT_READ | PROT_WRITE;
+
+ ptr = ::mmap(nullptr, 3*page_size,
+ pflags | PROT_MAX(pflags),
+ MAP_ANONYMOUS | MAP_PRIVATE | MAP_NOCORE,
+ /*fd=*/locked_fd, /*offset=*/0);
+
+ if(ptr == MAP_FAILED)
+ {
+ continue;
+ }
+
+ // lock the data page
+ if(::mlock(static_cast<uint8_t*>(ptr) + page_size, page_size) != 0)
+ {
+ ::munmap(ptr, 3*page_size);
+ continue;
+ }
+
+#if defined(MADV_DONTDUMP)
+ // we ignore errors here, as DONTDUMP is just a bonus
+ ::madvise(static_cast<uint8_t*>(ptr) + page_size, page_size, MADV_DONTDUMP);
+#endif
+
+#elif defined(BOTAN_TARGET_OS_HAS_VIRTUAL_LOCK)
+ ptr = ::VirtualAlloc(nullptr, 3*page_size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
+
+ if(ptr == nullptr)
+ continue;
+
+ if(::VirtualLock(static_cast<uint8_t*>(ptr) + page_size, page_size) == 0)
+ {
+ ::VirtualFree(ptr, 0, MEM_RELEASE);
+ continue;
+ }
+#endif
+
+ std::memset(ptr, 0, 3*page_size); // zero data page and both guard pages
+
+ // Make guard page preceeding the data page
+ page_prohibit_access(static_cast<uint8_t*>(ptr));
+ // Make guard page following the data page
+ page_prohibit_access(static_cast<uint8_t*>(ptr) + 2*page_size);
+
+ result.push_back(static_cast<uint8_t*>(ptr) + page_size);
+ }
+#else
+ BOTAN_UNUSED(count);
+#endif
+
+ return result;
+ }
+
+void OS::page_allow_access(void* page)
+ {
+#if defined(BOTAN_TARGET_OS_HAS_POSIX1)
+ const size_t page_size = OS::system_page_size();
+ ::mprotect(page, page_size, PROT_READ | PROT_WRITE);
+#elif defined(BOTAN_TARGET_OS_HAS_VIRTUAL_LOCK)
+ const size_t page_size = OS::system_page_size();
+ DWORD old_perms = 0;
+ ::VirtualProtect(page, page_size, PAGE_READWRITE, &old_perms);
+ BOTAN_UNUSED(old_perms);
+#else
+ BOTAN_UNUSED(page);
+#endif
+ }
+
+void OS::page_prohibit_access(void* page)
+ {
+#if defined(BOTAN_TARGET_OS_HAS_POSIX1)
+ const size_t page_size = OS::system_page_size();
+ ::mprotect(page, page_size, PROT_NONE);
+#elif defined(BOTAN_TARGET_OS_HAS_VIRTUAL_LOCK)
+ const size_t page_size = OS::system_page_size();
+ DWORD old_perms = 0;
+ ::VirtualProtect(page, page_size, PAGE_NOACCESS, &old_perms);
+ BOTAN_UNUSED(old_perms);
+#else
+ BOTAN_UNUSED(page);
+#endif
+ }
+
+void OS::free_locked_pages(const std::vector<void*>& pages)
+ {
+ const size_t page_size = OS::system_page_size();
+
+ for(size_t i = 0; i != pages.size(); ++i)
+ {
+ void* ptr = pages[i];
+
+ secure_scrub_memory(ptr, page_size);
+
+ // ptr points to the data page, guard pages are before and after
+ page_allow_access(static_cast<uint8_t*>(ptr) - page_size);
+ page_allow_access(static_cast<uint8_t*>(ptr) + page_size);
+
+#if defined(BOTAN_TARGET_OS_HAS_POSIX1) && defined(BOTAN_TARGET_OS_HAS_POSIX_MLOCK)
+ ::munlock(ptr, page_size);
+ ::munmap(static_cast<uint8_t*>(ptr) - page_size, 3*page_size);
+#elif defined(BOTAN_TARGET_OS_HAS_VIRTUAL_LOCK)
+ ::VirtualUnlock(ptr, page_size);
+ ::VirtualFree(static_cast<uint8_t*>(ptr) - page_size, 0, MEM_RELEASE);
+#endif
+ }
+ }
+
+#if defined(BOTAN_TARGET_OS_HAS_POSIX1) && !defined(BOTAN_TARGET_OS_IS_EMSCRIPTEN)
+
+namespace {
+
+static ::sigjmp_buf g_sigill_jmp_buf;
+
+void botan_sigill_handler(int)
+ {
+ siglongjmp(g_sigill_jmp_buf, /*non-zero return value*/1);
+ }
+
+}
+
+#endif
+
+int OS::run_cpu_instruction_probe(std::function<int ()> probe_fn)
+ {
+ 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;
+
+ sigaction.sa_handler = botan_sigill_handler;
+ sigemptyset(&sigaction.sa_mask);
+ sigaction.sa_flags = 0;
+
+ int rc = ::sigaction(SIGILL, &sigaction, &old_sigaction);
+
+ if(rc != 0)
+ throw System_Error("run_cpu_instruction_probe sigaction failed", errno);
+
+ rc = sigsetjmp(g_sigill_jmp_buf, /*save sigs*/1);
+
+ if(rc == 0)
+ {
+ // first call to sigsetjmp
+ probe_result = probe_fn();
+ }
+ else if(rc == 1)
+ {
+ // non-local return from siglongjmp in signal handler: return error
+ probe_result = -1;
+ }
+
+ // Restore old SIGILL handler, if any
+ rc = ::sigaction(SIGILL, &old_sigaction, nullptr);
+ if(rc != 0)
+ throw System_Error("run_cpu_instruction_probe sigaction restore failed", errno);
+
+#else
+ BOTAN_UNUSED(probe_fn);
+#endif
+
+ return probe_result;
+ }
+
+std::unique_ptr<OS::Echo_Suppression> OS::suppress_echo_on_terminal()
+ {
+#if defined(BOTAN_TARGET_OS_HAS_POSIX1)
+ class POSIX_Echo_Suppression : public Echo_Suppression
+ {
+ public:
+ POSIX_Echo_Suppression()
+ {
+ m_stdin_fd = fileno(stdin);
+ if(::tcgetattr(m_stdin_fd, &m_old_termios) != 0)
+ throw System_Error("Getting terminal status failed", errno);
+
+ struct termios noecho_flags = m_old_termios;
+ noecho_flags.c_lflag &= ~ECHO;
+ noecho_flags.c_lflag |= ECHONL;
+
+ if(::tcsetattr(m_stdin_fd, TCSANOW, &noecho_flags) != 0)
+ throw System_Error("Clearing terminal echo bit failed", errno);
+ }
+
+ void reenable_echo() override
+ {
+ if(m_stdin_fd > 0)
+ {
+ if(::tcsetattr(m_stdin_fd, TCSANOW, &m_old_termios) != 0)
+ throw System_Error("Restoring terminal echo bit failed", errno);
+ m_stdin_fd = -1;
+ }
+ }
+
+ ~POSIX_Echo_Suppression()
+ {
+ try
+ {
+ reenable_echo();
+ }
+ catch(...)
+ {
+ }
+ }
+
+ private:
+ int m_stdin_fd;
+ struct termios m_old_termios;
+ };
+
+ return std::unique_ptr<Echo_Suppression>(new POSIX_Echo_Suppression);
+
+#elif defined(BOTAN_TARGET_OS_HAS_WIN32)
+
+ class Win32_Echo_Suppression : public Echo_Suppression
+ {
+ public:
+ Win32_Echo_Suppression()
+ {
+ m_input_handle = ::GetStdHandle(STD_INPUT_HANDLE);
+ if(::GetConsoleMode(m_input_handle, &m_console_state) == 0)
+ throw System_Error("Getting console mode failed", ::GetLastError());
+
+ DWORD new_mode = ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT;
+ if(::SetConsoleMode(m_input_handle, new_mode) == 0)
+ throw System_Error("Setting console mode failed", ::GetLastError());
+ }
+
+ void reenable_echo() override
+ {
+ if(m_input_handle != INVALID_HANDLE_VALUE)
+ {
+ if(::SetConsoleMode(m_input_handle, m_console_state) == 0)
+ throw System_Error("Setting console mode failed", ::GetLastError());
+ m_input_handle = INVALID_HANDLE_VALUE;
+ }
+ }
+
+ ~Win32_Echo_Suppression()
+ {
+ try
+ {
+ reenable_echo();
+ }
+ catch(...)
+ {
+ }
+ }
+
+ private:
+ HANDLE m_input_handle;
+ DWORD m_console_state;
+ };
+
+ return std::unique_ptr<Echo_Suppression>(new Win32_Echo_Suppression);
+
+#else
+
+ // Not supported on this platform, return null
+ return std::unique_ptr<Echo_Suppression>();
+#endif
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/utils/os_utils.h b/comm/third_party/botan/src/lib/utils/os_utils.h
new file mode 100644
index 0000000000..d31dcb3baf
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/os_utils.h
@@ -0,0 +1,200 @@
+/*
+* OS specific utility functions
+* (C) 2015,2016,2017,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_OS_UTILS_H_
+#define BOTAN_OS_UTILS_H_
+
+#include <botan/types.h>
+#include <functional>
+#include <string>
+#include <vector>
+
+namespace Botan {
+
+namespace OS {
+
+/*
+* This header is internal (not installed) and these functions are not
+* intended to be called by applications. However they are given public
+* visibility (using BOTAN_TEST_API macro) for the tests. This also probably
+* allows them to be overridden by the application on ELF systems, but
+* this hasn't been tested.
+*/
+
+/**
+* @return process ID assigned by the operating system.
+* On Unix and Windows systems, this always returns a result
+* On IncludeOS it returns 0 since there is no process ID to speak of
+* in a unikernel.
+*/
+uint32_t BOTAN_TEST_API get_process_id();
+
+/**
+* Test if we are currently running with elevated permissions
+* eg setuid, setgid, or with POSIX caps set.
+*/
+bool running_in_privileged_state();
+
+/**
+* @return CPU processor clock, if available
+*
+* On Windows, calls QueryPerformanceCounter.
+*
+* Under GCC or Clang on supported platforms the hardware cycle counter is queried.
+* Currently supported processors are x86, PPC, Alpha, SPARC, IA-64, S/390x, and HP-PA.
+* If no CPU cycle counter is available on this system, returns zero.
+*/
+uint64_t BOTAN_TEST_API get_cpu_cycle_counter();
+
+size_t BOTAN_TEST_API get_cpu_total();
+size_t BOTAN_TEST_API get_cpu_available();
+
+/**
+* Return the ELF auxiliary vector cooresponding to the given ID.
+* This only makes sense on Unix-like systems and is currently
+* only supported on Linux, Android, and FreeBSD.
+*
+* Returns zero if not supported on the current system or if
+* the id provided is not known.
+*/
+unsigned long get_auxval(unsigned long id);
+
+/*
+* @return best resolution timestamp available
+*
+* The epoch and update rate of this clock is arbitrary and depending
+* on the hardware it may not tick at a constant rate.
+*
+* Uses hardware cycle counter, if available.
+* On POSIX platforms clock_gettime is used with a monotonic timer
+* As a final fallback std::chrono::high_resolution_clock is used.
+*/
+uint64_t BOTAN_TEST_API get_high_resolution_clock();
+
+/**
+* @return system clock (reflecting wall clock) with best resolution
+* available, normalized to nanoseconds resolution.
+*/
+uint64_t BOTAN_TEST_API get_system_timestamp_ns();
+
+/**
+* @return maximum amount of memory (in bytes) Botan could/should
+* hyptothetically allocate for the memory poool. Reads environment
+* variable "BOTAN_MLOCK_POOL_SIZE", set to "0" to disable pool.
+*/
+size_t get_memory_locking_limit();
+
+/**
+* Return the size of a memory page, if that can be derived on the
+* current system. Otherwise returns some default value (eg 4096)
+*/
+size_t system_page_size();
+
+/**
+* Read the value of an environment variable, setting it to value_out if it
+* exists. Returns false and sets value_out to empty string if no such variable
+* is set. If the process seems to be running in a privileged state (such as
+* setuid) then always returns false and does not examine the environment.
+*/
+bool read_env_variable(std::string& value_out, const std::string& var_name);
+
+/**
+* Read the value of an environment variable and convert it to an
+* 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.
+*/
+size_t read_env_variable_sz(const std::string& var_name, size_t def_value = 0);
+
+/**
+* Request count pages of RAM which are locked into memory using mlock,
+* VirtualLock, or some similar OS specific API. Free it with free_locked_pages.
+*
+* Returns an empty list on failure. This function is allowed to return fewer
+* than count pages.
+*
+* The contents of the allocated pages are undefined.
+*
+* Each page is preceded by and followed by a page which is marked
+* as noaccess, such that accessing it will cause a crash. This turns
+* out of bound reads/writes into crash events.
+*
+* @param count requested number of locked pages
+*/
+std::vector<void*> allocate_locked_pages(size_t count);
+
+/**
+* Free memory allocated by allocate_locked_pages
+* @param pages a list of pages returned by allocate_locked_pages
+*/
+void free_locked_pages(const std::vector<void*>& pages);
+
+/**
+* Set the MMU to prohibit access to this page
+*/
+void page_prohibit_access(void* page);
+
+/**
+* Set the MMU to allow R/W access to this page
+*/
+void page_allow_access(void* page);
+
+
+/**
+* Run a probe instruction to test for support for a CPU instruction.
+* Runs in system-specific env that catches illegal instructions; this
+* function always fails if the OS doesn't provide this.
+* Returns value of probe_fn, if it could run.
+* If error occurs, returns negative number.
+* This allows probe_fn to indicate errors of its own, if it wants.
+* For example the instruction might not only be only available on some
+* CPUs, but also buggy on some subset of these - the probe function
+* can test to make sure the instruction works properly before
+* indicating that the instruction is available.
+*
+* @warning on Unix systems uses signal handling in a way that is not
+* thread safe. It should only be called in a single-threaded context
+* (ie, at static init time).
+*
+* If probe_fn throws an exception the result is undefined.
+*
+* Return codes:
+* -1 illegal instruction detected
+*/
+int BOTAN_TEST_API run_cpu_instruction_probe(std::function<int ()> probe_fn);
+
+/**
+* Represents a terminal state
+*/
+class BOTAN_UNSTABLE_API Echo_Suppression
+ {
+ public:
+ /**
+ * Reenable echo on this terminal. Can be safely called
+ * multiple times. May throw if an error occurs.
+ */
+ virtual void reenable_echo() = 0;
+
+ /**
+ * Implicitly calls reenable_echo, but swallows/ignored all
+ * errors which would leave the terminal in an invalid state.
+ */
+ virtual ~Echo_Suppression() = default;
+ };
+
+/**
+* Suppress echo on the terminal
+* Returns null if this operation is not supported on the current system.
+*/
+std::unique_ptr<Echo_Suppression> BOTAN_UNSTABLE_API suppress_echo_on_terminal();
+
+}
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/utils/parsing.cpp b/comm/third_party/botan/src/lib/utils/parsing.cpp
new file mode 100644
index 0000000000..ff3d4ac0a2
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/parsing.cpp
@@ -0,0 +1,458 @@
+/*
+* Various string utils and parsing functions
+* (C) 1999-2007,2013,2014,2015,2018 Jack Lloyd
+* (C) 2015 Simon Warta (Kullo GmbH)
+* (C) 2017 René Korthaus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/parsing.h>
+#include <botan/exceptn.h>
+#include <botan/charset.h>
+#include <botan/loadstor.h>
+#include <algorithm>
+#include <cctype>
+#include <limits>
+#include <set>
+
+#if defined(BOTAN_HAS_ASN1)
+ #include <botan/asn1_obj.h>
+#endif
+
+namespace Botan {
+
+uint16_t to_uint16(const std::string& str)
+ {
+ const uint32_t x = to_u32bit(str);
+
+ if(x >> 16)
+ throw Invalid_Argument("Integer value exceeds 16 bit range");
+
+ return static_cast<uint16_t>(x);
+ }
+
+uint32_t to_u32bit(const std::string& str)
+ {
+ // std::stoul is not strict enough. Ensure that str is digit only [0-9]*
+ for(const char chr : str)
+ {
+ if(chr < '0' || chr > '9')
+ {
+ std::string chrAsString(1, chr);
+ throw Invalid_Argument("String contains non-digit char: " + chrAsString);
+ }
+ }
+
+ const unsigned long int x = std::stoul(str);
+
+ if(sizeof(unsigned long int) > 4)
+ {
+ // x might be uint64
+ if (x > std::numeric_limits<uint32_t>::max())
+ {
+ throw Invalid_Argument("Integer value of " + str + " exceeds 32 bit range");
+ }
+ }
+
+ return static_cast<uint32_t>(x);
+ }
+
+/*
+* Convert a string into a time duration
+*/
+uint32_t timespec_to_u32bit(const std::string& timespec)
+ {
+ if(timespec.empty())
+ return 0;
+
+ const char suffix = timespec[timespec.size()-1];
+ std::string value = timespec.substr(0, timespec.size()-1);
+
+ uint32_t scale = 1;
+
+ if(Charset::is_digit(suffix))
+ value += suffix;
+ else if(suffix == 's')
+ scale = 1;
+ else if(suffix == 'm')
+ scale = 60;
+ else if(suffix == 'h')
+ scale = 60 * 60;
+ else if(suffix == 'd')
+ scale = 24 * 60 * 60;
+ else if(suffix == 'y')
+ scale = 365 * 24 * 60 * 60;
+ else
+ throw Decoding_Error("timespec_to_u32bit: Bad input " + timespec);
+
+ return scale * to_u32bit(value);
+ }
+
+/*
+* Parse a SCAN-style algorithm name
+*/
+std::vector<std::string> parse_algorithm_name(const std::string& namex)
+ {
+ if(namex.find('(') == std::string::npos &&
+ namex.find(')') == std::string::npos)
+ return std::vector<std::string>(1, namex);
+
+ std::string name = namex, substring;
+ std::vector<std::string> elems;
+ size_t level = 0;
+
+ elems.push_back(name.substr(0, name.find('(')));
+ name = name.substr(name.find('('));
+
+ for(auto i = name.begin(); i != name.end(); ++i)
+ {
+ char c = *i;
+
+ if(c == '(')
+ ++level;
+ if(c == ')')
+ {
+ if(level == 1 && i == name.end() - 1)
+ {
+ if(elems.size() == 1)
+ elems.push_back(substring.substr(1));
+ else
+ elems.push_back(substring);
+ return elems;
+ }
+
+ if(level == 0 || (level == 1 && i != name.end() - 1))
+ throw Invalid_Algorithm_Name(namex);
+ --level;
+ }
+
+ if(c == ',' && level == 1)
+ {
+ if(elems.size() == 1)
+ elems.push_back(substring.substr(1));
+ else
+ elems.push_back(substring);
+ substring.clear();
+ }
+ else
+ substring += c;
+ }
+
+ if(!substring.empty())
+ throw Invalid_Algorithm_Name(namex);
+
+ return elems;
+ }
+
+std::vector<std::string> split_on(const std::string& str, char delim)
+ {
+ return split_on_pred(str, [delim](char c) { return c == delim; });
+ }
+
+std::vector<std::string> split_on_pred(const std::string& str,
+ std::function<bool (char)> pred)
+ {
+ std::vector<std::string> elems;
+ if(str.empty()) return elems;
+
+ std::string substr;
+ for(auto i = str.begin(); i != str.end(); ++i)
+ {
+ if(pred(*i))
+ {
+ if(!substr.empty())
+ elems.push_back(substr);
+ substr.clear();
+ }
+ else
+ substr += *i;
+ }
+
+ if(substr.empty())
+ throw Invalid_Argument("Unable to split string: " + str);
+ elems.push_back(substr);
+
+ return elems;
+ }
+
+/*
+* Join a string
+*/
+std::string string_join(const std::vector<std::string>& strs, char delim)
+ {
+ std::string out = "";
+
+ for(size_t i = 0; i != strs.size(); ++i)
+ {
+ if(i != 0)
+ out += delim;
+ out += strs[i];
+ }
+
+ return out;
+ }
+
+/*
+* Parse an ASN.1 OID string
+*/
+std::vector<uint32_t> parse_asn1_oid(const std::string& oid)
+ {
+#if defined(BOTAN_HAS_ASN1)
+ return OID(oid).get_components();
+#else
+ BOTAN_UNUSED(oid);
+ throw Not_Implemented("ASN1 support not available");
+#endif
+ }
+
+/*
+* X.500 String Comparison
+*/
+bool x500_name_cmp(const std::string& name1, const std::string& name2)
+ {
+ auto p1 = name1.begin();
+ auto p2 = name2.begin();
+
+ while((p1 != name1.end()) && Charset::is_space(*p1)) ++p1;
+ while((p2 != name2.end()) && Charset::is_space(*p2)) ++p2;
+
+ while(p1 != name1.end() && p2 != name2.end())
+ {
+ if(Charset::is_space(*p1))
+ {
+ if(!Charset::is_space(*p2))
+ return false;
+
+ while((p1 != name1.end()) && Charset::is_space(*p1)) ++p1;
+ while((p2 != name2.end()) && Charset::is_space(*p2)) ++p2;
+
+ if(p1 == name1.end() && p2 == name2.end())
+ return true;
+ if(p1 == name1.end() || p2 == name2.end())
+ return false;
+ }
+
+ if(!Charset::caseless_cmp(*p1, *p2))
+ return false;
+ ++p1;
+ ++p2;
+ }
+
+ while((p1 != name1.end()) && Charset::is_space(*p1)) ++p1;
+ while((p2 != name2.end()) && Charset::is_space(*p2)) ++p2;
+
+ if((p1 != name1.end()) || (p2 != name2.end()))
+ return false;
+ return true;
+ }
+
+/*
+* Convert a decimal-dotted string to binary IP
+*/
+uint32_t string_to_ipv4(const std::string& str)
+ {
+ std::vector<std::string> parts = split_on(str, '.');
+
+ if(parts.size() != 4)
+ throw Decoding_Error("Invalid IP string " + str);
+
+ uint32_t ip = 0;
+
+ for(auto part = parts.begin(); part != parts.end(); ++part)
+ {
+ uint32_t octet = to_u32bit(*part);
+
+ if(octet > 255)
+ throw Decoding_Error("Invalid IP string " + str);
+
+ ip = (ip << 8) | (octet & 0xFF);
+ }
+
+ return ip;
+ }
+
+/*
+* Convert an IP address to decimal-dotted string
+*/
+std::string ipv4_to_string(uint32_t ip)
+ {
+ std::string str;
+
+ for(size_t i = 0; i != sizeof(ip); ++i)
+ {
+ if(i)
+ str += ".";
+ str += std::to_string(get_byte(i, ip));
+ }
+
+ return str;
+ }
+
+std::string erase_chars(const std::string& str, const std::set<char>& chars)
+ {
+ std::string out;
+
+ for(auto c: str)
+ if(chars.count(c) == 0)
+ out += c;
+
+ return out;
+ }
+
+std::string replace_chars(const std::string& str,
+ const std::set<char>& chars,
+ char to_char)
+ {
+ std::string out = str;
+
+ for(size_t i = 0; i != out.size(); ++i)
+ if(chars.count(out[i]))
+ out[i] = to_char;
+
+ return out;
+ }
+
+std::string replace_char(const std::string& str, char from_char, char to_char)
+ {
+ std::string out = str;
+
+ for(size_t i = 0; i != out.size(); ++i)
+ if(out[i] == from_char)
+ out[i] = to_char;
+
+ return out;
+ }
+
+std::string tolower_string(const std::string& in)
+ {
+ std::string s = in;
+ for(size_t i = 0; i != s.size(); ++i)
+ {
+ const int cu = static_cast<unsigned char>(s[i]);
+ if(std::isalpha(cu))
+ s[i] = static_cast<char>(std::tolower(cu));
+ }
+ return s;
+ }
+
+bool host_wildcard_match(const std::string& issued_, const std::string& host_)
+ {
+ const std::string issued = tolower_string(issued_);
+ const std::string host = tolower_string(host_);
+
+ if(host.empty() || issued.empty())
+ return false;
+
+ /*
+ If there are embedded nulls in your issued name
+ Well I feel bad for you son
+ */
+ if(std::count(issued.begin(), issued.end(), char(0)) > 0)
+ 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)
+ 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)
+ return false;
+
+ // Similarly a DNS name can't end in .
+ if(host[host.size() - 1] == '.')
+ return false;
+
+ // And a host can't have an empty name component, so reject that
+ if(host.find("..") != std::string::npos)
+ return false;
+
+ // Exact match: accept
+ if(issued == host)
+ {
+ return true;
+ }
+
+ /*
+ Otherwise it might be a wildcard
+
+ 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)
+ {
+ return false;
+ }
+
+ // If no * at all then not a wildcard, and so not a match
+ if(stars != 1)
+ {
+ return false;
+ }
+
+ /*
+ Now walk through the issued string, making sure every character
+ matches. When we come to the (singular) '*', jump forward in the
+ hostname by the corresponding amount. We know exactly how much
+ space the wildcard takes because it must be exactly `len(host) -
+ len(issued) + 1 chars`.
+
+ We also verify that the '*' comes in the leftmost component, and
+ doesn't skip over any '.' in the hostname.
+ */
+ size_t dots_seen = 0;
+ size_t host_idx = 0;
+
+ for(size_t i = 0; i != issued.size(); ++i)
+ {
+ dots_seen += (issued[i] == '.');
+
+ if(issued[i] == '*')
+ {
+ // Fail: wildcard can only come in leftmost component
+ if(dots_seen > 0)
+ {
+ return false;
+ }
+
+ /*
+ Since there is only one * we know the tail of the issued and
+ hostname must be an exact match. In this case advance host_idx
+ to match.
+ */
+ const size_t advance = (host.size() - issued.size() + 1);
+
+ if(host_idx + advance > host.size()) // shouldn't happen
+ return false;
+
+ // 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;
+
+ host_idx += advance;
+ }
+ else
+ {
+ if(issued[i] != host[host_idx])
+ {
+ return false;
+ }
+
+ host_idx += 1;
+ }
+ }
+
+ // Wildcard issued name must have at least 3 components
+ if(dots_seen < 2)
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/utils/parsing.h b/comm/third_party/botan/src/lib/utils/parsing.h
new file mode 100644
index 0000000000..d2c0b5f8c8
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/parsing.h
@@ -0,0 +1,181 @@
+/*
+* Various string utils and parsing functions
+* (C) 1999-2007,2013 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_PARSING_UTILS_H_
+#define BOTAN_PARSING_UTILS_H_
+
+#include <botan/types.h>
+#include <string>
+#include <vector>
+#include <set>
+
+#include <istream>
+#include <functional>
+#include <map>
+
+BOTAN_FUTURE_INTERNAL_HEADER(parsing.h)
+
+namespace Botan {
+
+/**
+* Parse a SCAN-style algorithm name
+* @param scan_name the name
+* @return the name components
+*/
+BOTAN_PUBLIC_API(2,0) std::vector<std::string>
+parse_algorithm_name(const std::string& scan_name);
+
+/**
+* Split a string
+* @param str the input string
+* @param delim the delimitor
+* @return string split by delim
+*/
+BOTAN_PUBLIC_API(2,0) std::vector<std::string> split_on(
+ const std::string& str, char delim);
+
+/**
+* Split a string on a character predicate
+* @param str the input string
+* @param pred the predicate
+*
+* This function will likely be removed in a future release
+*/
+BOTAN_PUBLIC_API(2,0) std::vector<std::string>
+split_on_pred(const std::string& str,
+ std::function<bool (char)> pred);
+
+/**
+* Erase characters from a string
+*/
+BOTAN_PUBLIC_API(2,0)
+BOTAN_DEPRECATED("Unused")
+std::string erase_chars(const std::string& str, const std::set<char>& chars);
+
+/**
+* Replace a character in a string
+* @param str the input string
+* @param from_char the character to replace
+* @param to_char the character to replace it with
+* @return str with all instances of from_char replaced by to_char
+*/
+BOTAN_PUBLIC_API(2,0)
+BOTAN_DEPRECATED("Unused")
+std::string replace_char(const std::string& str,
+ char from_char,
+ char to_char);
+
+/**
+* Replace a character in a string
+* @param str the input string
+* @param from_chars the characters to replace
+* @param to_char the character to replace it with
+* @return str with all instances of from_chars replaced by to_char
+*/
+BOTAN_PUBLIC_API(2,0)
+BOTAN_DEPRECATED("Unused")
+std::string replace_chars(const std::string& str,
+ const std::set<char>& from_chars,
+ char to_char);
+
+/**
+* Join a string
+* @param strs strings to join
+* @param delim the delimitor
+* @return string joined by delim
+*/
+BOTAN_PUBLIC_API(2,0)
+std::string string_join(const std::vector<std::string>& strs,
+ char delim);
+
+/**
+* Parse an ASN.1 OID
+* @param oid the OID in string form
+* @return OID components
+*/
+BOTAN_PUBLIC_API(2,0) std::vector<uint32_t>
+BOTAN_DEPRECATED("Use OID::from_string(oid).get_components()") parse_asn1_oid(const std::string& oid);
+
+/**
+* Compare two names using the X.509 comparison algorithm
+* @param name1 the first name
+* @param name2 the second name
+* @return true if name1 is the same as name2 by the X.509 comparison rules
+*/
+BOTAN_PUBLIC_API(2,0)
+bool x500_name_cmp(const std::string& name1,
+ const std::string& name2);
+
+/**
+* Convert a string to a number
+* @param str the string to convert
+* @return number value of the string
+*/
+BOTAN_PUBLIC_API(2,0) uint32_t to_u32bit(const std::string& str);
+
+/**
+* Convert a string to a number
+* @param str the string to convert
+* @return number value of the string
+*/
+BOTAN_PUBLIC_API(2,3) uint16_t to_uint16(const std::string& str);
+
+/**
+* Convert a time specification to a number
+* @param timespec the time specification
+* @return number of seconds represented by timespec
+*/
+BOTAN_PUBLIC_API(2,0) uint32_t BOTAN_DEPRECATED("Not used anymore")
+timespec_to_u32bit(const std::string& timespec);
+
+/**
+* Convert a string representation of an IPv4 address to a number
+* @param ip_str the string representation
+* @return integer IPv4 address
+*/
+BOTAN_PUBLIC_API(2,0) uint32_t string_to_ipv4(const std::string& ip_str);
+
+/**
+* Convert an IPv4 address to a string
+* @param ip_addr the IPv4 address to convert
+* @return string representation of the IPv4 address
+*/
+BOTAN_PUBLIC_API(2,0) std::string ipv4_to_string(uint32_t ip_addr);
+
+std::map<std::string, std::string> BOTAN_PUBLIC_API(2,0) read_cfg(std::istream& is);
+
+/**
+* Accepts key value pairs deliminated by commas:
+*
+* "" (returns empty map)
+* "K=V" (returns map {'K': 'V'})
+* "K1=V1,K2=V2"
+* "K1=V1,K2=V2,K3=V3"
+* "K1=V1,K2=V2,K3=a_value\,with\,commas_and_\=equals"
+*
+* Values may be empty, keys must be non-empty and unique. Duplicate
+* keys cause an exception.
+*
+* Within both key and value, comma and equals can be escaped with
+* backslash. Backslash can also be escaped.
+*/
+std::map<std::string, std::string> BOTAN_PUBLIC_API(2,8) read_kv(const std::string& kv);
+
+std::string BOTAN_PUBLIC_API(2,0) clean_ws(const std::string& s);
+
+std::string tolower_string(const std::string& s);
+
+/**
+* Check if the given hostname is a match for the specified wildcard
+*/
+bool BOTAN_PUBLIC_API(2,0) host_wildcard_match(const std::string& wildcard,
+ const std::string& host);
+
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/utils/poly_dbl/info.txt b/comm/third_party/botan/src/lib/utils/poly_dbl/info.txt
new file mode 100644
index 0000000000..5aae5b44f4
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/poly_dbl/info.txt
@@ -0,0 +1,7 @@
+<defines>
+POLY_DBL -> 20170927
+</defines>
+
+<header:internal>
+poly_dbl.h
+</header:internal>
diff --git a/comm/third_party/botan/src/lib/utils/poly_dbl/poly_dbl.cpp b/comm/third_party/botan/src/lib/utils/poly_dbl/poly_dbl.cpp
new file mode 100644
index 0000000000..8c728a1480
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/poly_dbl/poly_dbl.cpp
@@ -0,0 +1,115 @@
+/*
+* (C) 2017,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/poly_dbl.h>
+#include <botan/loadstor.h>
+#include <botan/exceptn.h>
+
+namespace Botan {
+
+namespace {
+
+/*
+* The minimum weight irreducible binary polynomial of size n
+*
+* See http://www.hpl.hp.com/techreports/98/HPL-98-135.pdf
+*/
+enum class MinWeightPolynomial : uint64_t {
+ P64 = 0x1B,
+ P128 = 0x87,
+ P192 = 0x87,
+ P256 = 0x425,
+ P512 = 0x125,
+ P1024 = 0x80043,
+};
+
+template<size_t LIMBS, MinWeightPolynomial P>
+void poly_double(uint8_t out[], const uint8_t in[])
+ {
+ uint64_t W[LIMBS];
+ load_be(W, in, LIMBS);
+
+ const uint64_t POLY = static_cast<uint64_t>(P);
+
+ const uint64_t carry = POLY * (W[0] >> 63);
+
+ BOTAN_IF_CONSTEXPR(LIMBS > 0)
+ {
+ for(size_t i = 0; i != LIMBS - 1; ++i)
+ W[i] = (W[i] << 1) ^ (W[i+1] >> 63);
+ }
+
+ W[LIMBS-1] = (W[LIMBS-1] << 1) ^ carry;
+
+ copy_out_be(out, LIMBS*8, W);
+ }
+
+template<size_t LIMBS, MinWeightPolynomial P>
+void poly_double_le(uint8_t out[], const uint8_t in[])
+ {
+ uint64_t W[LIMBS];
+ load_le(W, in, LIMBS);
+
+ const uint64_t POLY = static_cast<uint64_t>(P);
+
+ const uint64_t carry = POLY * (W[LIMBS-1] >> 63);
+
+ BOTAN_IF_CONSTEXPR(LIMBS > 0)
+ {
+ for(size_t i = 0; i != LIMBS - 1; ++i)
+ W[LIMBS-1-i] = (W[LIMBS-1-i] << 1) ^ (W[LIMBS-2-i] >> 63);
+ }
+
+ W[0] = (W[0] << 1) ^ carry;
+
+ copy_out_le(out, LIMBS*8, W);
+ }
+
+}
+
+void poly_double_n(uint8_t out[], const uint8_t in[], size_t n)
+ {
+ switch(n)
+ {
+ case 8:
+ return poly_double<1, MinWeightPolynomial::P64>(out, in);
+ case 16:
+ return poly_double<2, MinWeightPolynomial::P128>(out, in);
+ case 24:
+ return poly_double<3, MinWeightPolynomial::P192>(out, in);
+ case 32:
+ return poly_double<4, MinWeightPolynomial::P256>(out, in);
+ case 64:
+ return poly_double<8, MinWeightPolynomial::P512>(out, in);
+ case 128:
+ return poly_double<16, MinWeightPolynomial::P1024>(out, in);
+ default:
+ throw Invalid_Argument("Unsupported size for poly_double_n");
+ }
+ }
+
+void poly_double_n_le(uint8_t out[], const uint8_t in[], size_t n)
+ {
+ switch(n)
+ {
+ case 8:
+ return poly_double_le<1, MinWeightPolynomial::P64>(out, in);
+ case 16:
+ return poly_double_le<2, MinWeightPolynomial::P128>(out, in);
+ case 24:
+ return poly_double_le<3, MinWeightPolynomial::P192>(out, in);
+ case 32:
+ return poly_double_le<4, MinWeightPolynomial::P256>(out, in);
+ case 64:
+ return poly_double_le<8, MinWeightPolynomial::P512>(out, in);
+ case 128:
+ return poly_double_le<16, MinWeightPolynomial::P1024>(out, in);
+ default:
+ throw Invalid_Argument("Unsupported size for poly_double_n_le");
+ }
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/utils/poly_dbl/poly_dbl.h b/comm/third_party/botan/src/lib/utils/poly_dbl/poly_dbl.h
new file mode 100644
index 0000000000..7d9f523319
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/poly_dbl/poly_dbl.h
@@ -0,0 +1,39 @@
+/*
+* (C) 2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_POLY_DBL_H_
+#define BOTAN_POLY_DBL_H_
+
+#include <botan/types.h>
+
+namespace Botan {
+
+/**
+* Polynomial doubling in GF(2^n)
+*/
+void BOTAN_TEST_API poly_double_n(uint8_t out[], const uint8_t in[], size_t n);
+
+/**
+* Returns true iff poly_double_n is implemented for this size.
+*/
+inline bool poly_double_supported_size(size_t n)
+ {
+ return (n == 8 || n == 16 || n == 24 || n == 32 || n == 64 || n == 128);
+ }
+
+inline void poly_double_n(uint8_t buf[], size_t n)
+ {
+ return poly_double_n(buf, buf, n);
+ }
+
+/*
+* Little endian convention - used for XTS
+*/
+void BOTAN_TEST_API poly_double_n_le(uint8_t out[], const uint8_t in[], size_t n);
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/utils/prefetch.h b/comm/third_party/botan/src/lib/utils/prefetch.h
new file mode 100644
index 0000000000..92c41e5738
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/prefetch.h
@@ -0,0 +1,39 @@
+/*
+* Prefetching Operations
+* (C) 2009 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_PREFETCH_H_
+#define BOTAN_PREFETCH_H_
+
+#include <botan/cpuid.h>
+
+namespace Botan {
+
+template<typename T>
+inline void prefetch_readonly(const T* addr, size_t length)
+ {
+#if defined(__GNUG__)
+ const size_t Ts_per_cache_line = CPUID::cache_line_size() / sizeof(T);
+
+ for(size_t i = 0; i <= length; i += Ts_per_cache_line)
+ __builtin_prefetch(addr + i, 0);
+#endif
+ }
+
+template<typename T>
+inline void prefetch_readwrite(const T* addr, size_t length)
+ {
+#if defined(__GNUG__)
+ const size_t Ts_per_cache_line = CPUID::cache_line_size() / sizeof(T);
+
+ for(size_t i = 0; i <= length; i += Ts_per_cache_line)
+ __builtin_prefetch(addr + i, 1);
+#endif
+ }
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/utils/read_cfg.cpp b/comm/third_party/botan/src/lib/utils/read_cfg.cpp
new file mode 100644
index 0000000000..02f2e0ac81
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/read_cfg.cpp
@@ -0,0 +1,63 @@
+/*
+* Simple config/test file reader
+* (C) 2013,2014,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/parsing.h>
+#include <botan/exceptn.h>
+
+namespace Botan {
+
+std::string clean_ws(const std::string& s)
+ {
+ const char* ws = " \t\n";
+ auto start = s.find_first_not_of(ws);
+ auto end = s.find_last_not_of(ws);
+
+ if(start == std::string::npos)
+ return "";
+
+ if(end == std::string::npos)
+ return s.substr(start, end);
+ else
+ return s.substr(start, start + end + 1);
+ }
+
+std::map<std::string, std::string> read_cfg(std::istream& is)
+ {
+ std::map<std::string, std::string> kv;
+ size_t line = 0;
+
+ while(is.good())
+ {
+ std::string s;
+
+ std::getline(is, s);
+
+ ++line;
+
+ if(s.empty() || s[0] == '#')
+ continue;
+
+ s = clean_ws(s.substr(0, s.find('#')));
+
+ if(s.empty())
+ continue;
+
+ auto eq = s.find("=");
+
+ if(eq == std::string::npos || eq == 0 || eq == s.size() - 1)
+ throw Decoding_Error("Bad read_cfg input '" + s + "' on line " + std::to_string(line));
+
+ const std::string key = clean_ws(s.substr(0, eq));
+ const std::string val = clean_ws(s.substr(eq + 1, std::string::npos));
+
+ kv[key] = val;
+ }
+
+ return kv;
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/utils/read_kv.cpp b/comm/third_party/botan/src/lib/utils/read_kv.cpp
new file mode 100644
index 0000000000..cdc84c6229
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/read_kv.cpp
@@ -0,0 +1,85 @@
+/*
+* (C) 2018 Ribose Inc
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/parsing.h>
+#include <botan/exceptn.h>
+
+namespace Botan {
+
+std::map<std::string, std::string> read_kv(const std::string& kv)
+ {
+ std::map<std::string, std::string> m;
+ if(kv == "")
+ return m;
+
+ std::vector<std::string> parts;
+
+ try
+ {
+ parts = split_on(kv, ',');
+ }
+ catch(std::exception&)
+ {
+ throw Invalid_Argument("Bad KV spec");
+ }
+
+ bool escaped = false;
+ bool reading_key = true;
+ std::string cur_key;
+ std::string cur_val;
+
+ for(char c : kv)
+ {
+ if(c == '\\' && !escaped)
+ {
+ escaped = true;
+ }
+ else if(c == ',' && !escaped)
+ {
+ if(cur_key.empty())
+ throw Invalid_Argument("Bad KV spec empty key");
+
+ if(m.find(cur_key) != m.end())
+ throw Invalid_Argument("Bad KV spec duplicated key");
+ m[cur_key] = cur_val;
+ cur_key = "";
+ cur_val = "";
+ reading_key = true;
+ }
+ else if(c == '=' && !escaped)
+ {
+ if(reading_key == false)
+ throw Invalid_Argument("Bad KV spec unexpected equals sign");
+ reading_key = false;
+ }
+ else
+ {
+ if(reading_key)
+ cur_key += c;
+ else
+ cur_val += c;
+
+ if(escaped)
+ escaped = false;
+ }
+ }
+
+ if(!cur_key.empty())
+ {
+ if(reading_key == false)
+ {
+ if(m.find(cur_key) != m.end())
+ throw Invalid_Argument("Bad KV spec duplicated key");
+ m[cur_key] = cur_val;
+ }
+ else
+ throw Invalid_Argument("Bad KV spec incomplete string");
+ }
+
+ return m;
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/utils/rotate.h b/comm/third_party/botan/src/lib/utils/rotate.h
new file mode 100644
index 0000000000..8599a2695f
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/rotate.h
@@ -0,0 +1,112 @@
+/*
+* Word Rotation Operations
+* (C) 1999-2008,2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_WORD_ROTATE_H_
+#define BOTAN_WORD_ROTATE_H_
+
+#include <botan/types.h>
+
+BOTAN_FUTURE_INTERNAL_HEADER(rotate.h)
+
+namespace Botan {
+
+/**
+* Bit rotation left by a compile-time constant amount
+* @param input the input word
+* @return input rotated left by ROT bits
+*/
+template<size_t ROT, typename T>
+inline constexpr T rotl(T input)
+ {
+ static_assert(ROT > 0 && ROT < 8*sizeof(T), "Invalid rotation constant");
+ return static_cast<T>((input << ROT) | (input >> (8*sizeof(T) - ROT)));
+ }
+
+/**
+* Bit rotation right by a compile-time constant amount
+* @param input the input word
+* @return input rotated right by ROT bits
+*/
+template<size_t ROT, typename T>
+inline constexpr T rotr(T input)
+ {
+ static_assert(ROT > 0 && ROT < 8*sizeof(T), "Invalid rotation constant");
+ return static_cast<T>((input >> ROT) | (input << (8*sizeof(T) - ROT)));
+ }
+
+/**
+* Bit rotation left, variable rotation amount
+* @param input the input word
+* @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<typename T>
+inline T rotl_var(T input, size_t rot)
+ {
+ return rot ? static_cast<T>((input << rot) | (input >> (sizeof(T)*8 - rot))) : input;
+ }
+
+/**
+* Bit rotation right, variable rotation amount
+* @param input the input word
+* @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<typename T>
+inline T rotr_var(T input, size_t rot)
+ {
+ return rot ? static_cast<T>((input >> rot) | (input << (sizeof(T)*8 - rot))) : input;
+ }
+
+#if defined(BOTAN_USE_GCC_INLINE_ASM)
+
+#if defined(BOTAN_TARGET_ARCH_IS_X86_64) || defined(BOTAN_TARGET_ARCH_IS_X86_32)
+
+template<>
+inline uint32_t rotl_var(uint32_t input, size_t rot)
+ {
+ asm("roll %1,%0"
+ : "+r" (input)
+ : "c" (static_cast<uint8_t>(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<uint8_t>(rot))
+ : "cc");
+ return input;
+ }
+
+#endif
+
+#endif
+
+
+template<typename T>
+BOTAN_DEPRECATED("Use rotl<N> or rotl_var")
+inline T rotate_left(T input, size_t rot)
+ {
+ // rotl_var does not reduce
+ return rotl_var(input, rot % (8 * sizeof(T)));
+ }
+
+template<typename T>
+BOTAN_DEPRECATED("Use rotr<N> or rotr_var")
+inline T rotate_right(T input, size_t rot)
+ {
+ // rotr_var does not reduce
+ return rotr_var(input, rot % (8 * sizeof(T)));
+ }
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/utils/rounding.h b/comm/third_party/botan/src/lib/utils/rounding.h
new file mode 100644
index 0000000000..ae0aedf07c
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/rounding.h
@@ -0,0 +1,56 @@
+/*
+* Integer Rounding Functions
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_ROUNDING_H_
+#define BOTAN_ROUNDING_H_
+
+#include <botan/types.h>
+
+namespace Botan {
+
+/**
+* Round up
+* @param n a non-negative integer
+* @param align_to the alignment boundary
+* @return n rounded up to a multiple of align_to
+*/
+inline size_t round_up(size_t n, size_t align_to)
+ {
+ BOTAN_ARG_CHECK(align_to != 0, "align_to must not be 0");
+
+ if(n % align_to)
+ n += align_to - (n % align_to);
+ return n;
+ }
+
+/**
+* Round down
+* @param n an integer
+* @param align_to the alignment boundary
+* @return n rounded down to a multiple of align_to
+*/
+template<typename T>
+inline constexpr T round_down(T n, T align_to)
+ {
+ return (align_to == 0) ? n : (n - (n % align_to));
+ }
+
+/**
+* Clamp
+*/
+inline size_t clamp(size_t n, size_t lower_bound, size_t upper_bound)
+ {
+ if(n < lower_bound)
+ return lower_bound;
+ if(n > upper_bound)
+ return upper_bound;
+ return n;
+ }
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/utils/safeint.h b/comm/third_party/botan/src/lib/utils/safeint.h
new file mode 100644
index 0000000000..5c9ea49553
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/safeint.h
@@ -0,0 +1,41 @@
+/*
+* Safe(r) Integer Handling
+* (C) 2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_UTILS_SAFE_INT_H_
+#define BOTAN_UTILS_SAFE_INT_H_
+
+#include <botan/exceptn.h>
+#include <string>
+
+namespace Botan {
+
+class BOTAN_PUBLIC_API(2,0) Integer_Overflow_Detected final : public Exception
+ {
+ public:
+ Integer_Overflow_Detected(const std::string& file, int line) :
+ Exception("Integer overflow detected at " + file + ":" + std::to_string(line))
+ {}
+
+ ErrorType error_type() const noexcept override { return ErrorType::InternalError; }
+ };
+
+inline size_t checked_add(size_t x, size_t y, const char* file, int line)
+ {
+ // TODO: use __builtin_x_overflow on GCC and Clang
+ size_t z = x + y;
+ if(z < x)
+ {
+ throw Integer_Overflow_Detected(file, line);
+ }
+ return z;
+ }
+
+#define BOTAN_CHECKED_ADD(x,y) checked_add(x,y,__FILE__,__LINE__)
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/utils/simd/info.txt b/comm/third_party/botan/src/lib/utils/simd/info.txt
new file mode 100644
index 0000000000..4a7044afc4
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/simd/info.txt
@@ -0,0 +1,27 @@
+<defines>
+SIMD_32 -> 20131128
+</defines>
+
+<header:internal>
+simd_32.h
+</header:internal>
+
+<isa>
+x86_32:sse2
+x86_64:sse2
+x32:sse2
+arm32:neon
+arm64:neon
+ppc32:altivec
+ppc64:altivec
+</isa>
+
+<arch>
+x86_32
+x86_64
+x32
+arm32
+arm64
+ppc32
+ppc64
+</arch>
diff --git a/comm/third_party/botan/src/lib/utils/simd/simd_32.h b/comm/third_party/botan/src/lib/utils/simd/simd_32.h
new file mode 100644
index 0000000000..5cbc32a187
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/simd/simd_32.h
@@ -0,0 +1,621 @@
+/*
+* 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 <botan/types.h>
+
+#if defined(BOTAN_TARGET_SUPPORTS_SSE2)
+ #include <emmintrin.h>
+ #define BOTAN_SIMD_USE_SSE2
+
+#elif defined(BOTAN_TARGET_SUPPORTS_ALTIVEC)
+ #include <botan/bswap.h>
+ #include <botan/loadstor.h>
+ #include <altivec.h>
+ #undef vector
+ #undef bool
+ #define BOTAN_SIMD_USE_ALTIVEC
+
+#elif defined(BOTAN_TARGET_SUPPORTS_NEON)
+ #include <botan/cpuid.h>
+ #include <arm_neon.h>
+ #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"
+ #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)
+ typedef __m128i native_simd_type;
+#elif defined(BOTAN_SIMD_USE_ALTIVEC)
+ typedef __vector unsigned int native_simd_type;
+#elif defined(BOTAN_SIMD_USE_NEON)
+ typedef uint32x4_t native_simd_type;
+#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;
+
+ /**
+ * Zero initialize SIMD register with 4 32-bit elements
+ */
+ SIMD_4x32() // zero initialized
+ {
+#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])
+ {
+#if defined(BOTAN_SIMD_USE_SSE2)
+ m_simd = _mm_loadu_si128(reinterpret_cast<const __m128i*>(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)
+ {
+#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)
+ {
+#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)
+ {
+#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)
+ {
+#if defined(BOTAN_SIMD_USE_SSE2)
+ return SIMD_4x32(_mm_loadu_si128(reinterpret_cast<const __m128i*>(in)));
+#elif defined(BOTAN_SIMD_USE_ALTIVEC)
+ uint32_t R[4];
+ Botan::load_le(R, static_cast<const uint8_t*>(in), 4);
+ return SIMD_4x32(R);
+#elif defined(BOTAN_SIMD_USE_NEON)
+ SIMD_4x32 l(vld1q_u32(static_cast<const uint32_t*>(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)
+ {
+#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<const uint8_t*>(in), 4);
+ return SIMD_4x32(R);
+
+#elif defined(BOTAN_SIMD_USE_NEON)
+ SIMD_4x32 l(vld1q_u32(static_cast<const uint32_t*>(in)));
+ return CPUID::is_little_endian() ? l.bswap() : l;
+#endif
+ }
+
+ void store_le(uint32_t out[4]) const
+ {
+ this->store_le(reinterpret_cast<uint8_t*>(out));
+ }
+
+ void store_le(uint64_t out[2]) const
+ {
+ this->store_le(reinterpret_cast<uint8_t*>(out));
+ }
+
+ /**
+ * Load a SIMD register with little-endian convention
+ */
+ void store_le(uint8_t out[]) const
+ {
+#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
+ {
+#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
+ }
+
+ /*
+ * This is used for SHA-2/SHACAL2
+ * Return rotr(ROT1) ^ rotr(ROT2) ^ rotr(ROT3)
+ */
+ template<size_t ROT1, size_t ROT2, size_t ROT3>
+ SIMD_4x32 rho() const
+ {
+ const SIMD_4x32 rot1 = this->rotr<ROT1>();
+ const SIMD_4x32 rot2 = this->rotr<ROT2>();
+ const SIMD_4x32 rot3 = this->rotr<ROT3>();
+ return (rot1 ^ rot2 ^ rot3);
+ }
+
+ /**
+ * Left rotation by a compile time constant
+ */
+ template<size_t ROT>
+ SIMD_4x32 rotl() const
+ {
+ static_assert(ROT > 0 && ROT < 32, "Invalid rotation constant");
+
+#if defined(BOTAN_SIMD_USE_SSE2)
+
+ return SIMD_4x32(_mm_or_si128(_mm_slli_epi32(m_simd, static_cast<int>(ROT)),
+ _mm_srli_epi32(m_simd, static_cast<int>(32-ROT))));
+
+#elif defined(BOTAN_SIMD_USE_ALTIVEC)
+
+ const unsigned int r = static_cast<unsigned int>(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)
+
+ BOTAN_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 BOTAN_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<int>(ROT)),
+ vshrq_n_u32(m_simd, static_cast<int>(32-ROT))));
+#endif
+ }
+
+ /**
+ * Right rotation by a compile time constant
+ */
+ template<size_t ROT>
+ SIMD_4x32 rotr() const
+ {
+ return this->rotl<32-ROT>();
+ }
+
+ /**
+ * Add elements of a SIMD vector
+ */
+ SIMD_4x32 operator+(const SIMD_4x32& other) const
+ {
+ SIMD_4x32 retval(*this);
+ retval += other;
+ return retval;
+ }
+
+ /**
+ * Subtract elements of a SIMD vector
+ */
+ SIMD_4x32 operator-(const SIMD_4x32& other) const
+ {
+ SIMD_4x32 retval(*this);
+ retval -= other;
+ return retval;
+ }
+
+ /**
+ * XOR elements of a SIMD vector
+ */
+ SIMD_4x32 operator^(const SIMD_4x32& other) const
+ {
+ SIMD_4x32 retval(*this);
+ retval ^= other;
+ return retval;
+ }
+
+ /**
+ * Binary OR elements of a SIMD vector
+ */
+ SIMD_4x32 operator|(const SIMD_4x32& other) const
+ {
+ SIMD_4x32 retval(*this);
+ retval |= other;
+ return retval;
+ }
+
+ /**
+ * Binary AND elements of a SIMD vector
+ */
+ SIMD_4x32 operator&(const SIMD_4x32& other) const
+ {
+ SIMD_4x32 retval(*this);
+ retval &= other;
+ return retval;
+ }
+
+ void operator+=(const SIMD_4x32& other)
+ {
+#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)
+ {
+#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)
+ {
+#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|=(const SIMD_4x32& other)
+ {
+#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)
+ {
+#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<int SHIFT> SIMD_4x32 shl() const
+ {
+ static_assert(SHIFT > 0 && SHIFT <= 31, "Invalid shift count");
+
+#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<unsigned int>(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<int SHIFT> SIMD_4x32 shr() const
+ {
+#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<unsigned int>(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
+ {
+#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
+ {
+#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
+ {
+#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)
+
+ union {
+ __vector unsigned int V;
+ uint32_t R[4];
+ } vec;
+
+ vec.V = m_simd;
+ bswap_4(vec.R);
+ return SIMD_4x32(vec.R[0], vec.R[1], vec.R[2], vec.R[3]);
+
+#elif defined(BOTAN_SIMD_USE_NEON)
+ return SIMD_4x32(vreinterpretq_u32_u8(vrev32q_u8(vreinterpretq_u8_u32(m_simd))));
+#endif
+ }
+
+ template<size_t I>
+ SIMD_4x32 shift_elems_left() const
+ {
+ static_assert(I <= 3, "Invalid shift count");
+
+#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<size_t I>
+ SIMD_4x32 shift_elems_right() const
+ {
+ static_assert(I <= 3, "Invalid shift count");
+
+#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)
+ {
+#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
+ }
+
+ native_simd_type raw() const BOTAN_FUNC_ISA(BOTAN_SIMD_ISA) { return m_simd; }
+
+ explicit SIMD_4x32(native_simd_type x) : m_simd(x) {}
+ private:
+ native_simd_type m_simd;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/utils/simd/simd_avx2/info.txt b/comm/third_party/botan/src/lib/utils/simd/simd_avx2/info.txt
new file mode 100644
index 0000000000..13a9ae4cbb
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/simd/simd_avx2/info.txt
@@ -0,0 +1,21 @@
+<defines>
+SIMD_AVX2 -> 20180824
+</defines>
+
+<isa>
+avx2
+</isa>
+
+<header:internal>
+simd_avx2.h
+</header:internal>
+
+<cc>
+gcc
+clang
+msvc
+
+# Intel C++ 2020 doesn't support target attribute on overloaded
+# operators, see GH #2260
+#icc
+</cc>
diff --git a/comm/third_party/botan/src/lib/utils/simd/simd_avx2/simd_avx2.h b/comm/third_party/botan/src/lib/utils/simd/simd_avx2/simd_avx2.h
new file mode 100644
index 0000000000..3498c2ad08
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/simd/simd_avx2/simd_avx2.h
@@ -0,0 +1,299 @@
+/*
+* (C) 2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_SIMD_AVX2_H_
+#define BOTAN_SIMD_AVX2_H_
+
+#include <botan/types.h>
+#include <immintrin.h>
+
+namespace Botan {
+
+class SIMD_8x32 final
+ {
+ public:
+
+ SIMD_8x32& operator=(const SIMD_8x32& other) = default;
+ SIMD_8x32(const SIMD_8x32& other) = default;
+
+ SIMD_8x32& operator=(SIMD_8x32&& other) = default;
+ SIMD_8x32(SIMD_8x32&& other) = default;
+
+ BOTAN_FUNC_ISA("avx2")
+ BOTAN_FORCE_INLINE SIMD_8x32()
+ {
+ m_avx2 = _mm256_setzero_si256();
+ }
+
+ BOTAN_FUNC_ISA("avx2")
+ explicit SIMD_8x32(const uint32_t B[8])
+ {
+ m_avx2 = _mm256_loadu_si256(reinterpret_cast<const __m256i*>(B));
+ }
+
+ BOTAN_FUNC_ISA("avx2")
+ explicit SIMD_8x32(uint32_t B0, uint32_t B1, uint32_t B2, uint32_t B3,
+ uint32_t B4, uint32_t B5, uint32_t B6, uint32_t B7)
+ {
+ m_avx2 = _mm256_set_epi32(B7, B6, B5, B4, B3, B2, B1, B0);
+ }
+
+ BOTAN_FUNC_ISA("avx2")
+ static SIMD_8x32 splat(uint32_t B)
+ {
+ return SIMD_8x32(_mm256_set1_epi32(B));
+ }
+
+ BOTAN_FUNC_ISA("avx2")
+ static SIMD_8x32 load_le(const uint8_t* in)
+ {
+ return SIMD_8x32(_mm256_loadu_si256(reinterpret_cast<const __m256i*>(in)));
+ }
+
+ BOTAN_FUNC_ISA("avx2")
+ static SIMD_8x32 load_be(const uint8_t* in)
+ {
+ return load_le(in).bswap();
+ }
+
+ BOTAN_FUNC_ISA("avx2")
+ void store_le(uint8_t out[]) const
+ {
+ _mm256_storeu_si256(reinterpret_cast<__m256i*>(out), m_avx2);
+ }
+
+ BOTAN_FUNC_ISA("avx2")
+ void store_be(uint8_t out[]) const
+ {
+ bswap().store_le(out);
+ }
+
+ template<size_t ROT>
+ BOTAN_FUNC_ISA("avx2")
+ SIMD_8x32 rotl() const
+ {
+ static_assert(ROT > 0 && ROT < 32, "Invalid rotation constant");
+
+#if defined(__AVX512VL__)
+ return SIMD_8x32(_mm256_rol_epi32(m_avx2, ROT));
+#else
+ BOTAN_IF_CONSTEXPR(ROT == 8)
+ {
+ const __m256i shuf_rotl_8 = _mm256_set_epi8(14, 13, 12, 15, 10, 9, 8, 11, 6, 5, 4, 7, 2, 1, 0, 3,
+ 14, 13, 12, 15, 10, 9, 8, 11, 6, 5, 4, 7, 2, 1, 0, 3);
+
+ return SIMD_8x32(_mm256_shuffle_epi8(m_avx2, shuf_rotl_8));
+ }
+ else BOTAN_IF_CONSTEXPR(ROT == 16)
+ {
+ const __m256i shuf_rotl_16 = _mm256_set_epi8(13, 12, 15, 14, 9, 8, 11, 10, 5, 4, 7, 6, 1, 0, 3, 2,
+ 13, 12, 15, 14, 9, 8, 11, 10, 5, 4, 7, 6, 1, 0, 3, 2);
+
+ return SIMD_8x32(_mm256_shuffle_epi8(m_avx2, shuf_rotl_16));
+ }
+ else
+ {
+ return SIMD_8x32(_mm256_or_si256(_mm256_slli_epi32(m_avx2, static_cast<int>(ROT)),
+ _mm256_srli_epi32(m_avx2, static_cast<int>(32-ROT))));
+ }
+#endif
+ }
+
+ template<size_t ROT>
+ BOTAN_FUNC_ISA("avx2")
+ SIMD_8x32 rotr() const
+ {
+ return this->rotl<32-ROT>();
+ }
+
+ template<size_t ROT1, size_t ROT2, size_t ROT3>
+ SIMD_8x32 BOTAN_FUNC_ISA("avx2") rho() const
+ {
+ SIMD_8x32 res;
+
+ const SIMD_8x32 rot1 = this->rotr<ROT1>();
+ const SIMD_8x32 rot2 = this->rotr<ROT2>();
+ const SIMD_8x32 rot3 = this->rotr<ROT3>();
+
+ return rot1 ^ rot2 ^ rot3;
+ }
+
+ BOTAN_FUNC_ISA("avx2")
+ SIMD_8x32 operator+(const SIMD_8x32& other) const
+ {
+ SIMD_8x32 retval(*this);
+ retval += other;
+ return retval;
+ }
+
+ BOTAN_FUNC_ISA("avx2")
+ SIMD_8x32 operator-(const SIMD_8x32& other) const
+ {
+ SIMD_8x32 retval(*this);
+ retval -= other;
+ return retval;
+ }
+
+ BOTAN_FUNC_ISA("avx2")
+ SIMD_8x32 operator^(const SIMD_8x32& other) const
+ {
+ SIMD_8x32 retval(*this);
+ retval ^= other;
+ return retval;
+ }
+
+ BOTAN_FUNC_ISA("avx2")
+ SIMD_8x32 operator|(const SIMD_8x32& other) const
+ {
+ SIMD_8x32 retval(*this);
+ retval |= other;
+ return retval;
+ }
+
+ BOTAN_FUNC_ISA("avx2")
+ SIMD_8x32 operator&(const SIMD_8x32& other) const
+ {
+ SIMD_8x32 retval(*this);
+ retval &= other;
+ return retval;
+ }
+
+ BOTAN_FUNC_ISA("avx2")
+ void operator+=(const SIMD_8x32& other)
+ {
+ m_avx2 = _mm256_add_epi32(m_avx2, other.m_avx2);
+ }
+
+ BOTAN_FUNC_ISA("avx2")
+ void operator-=(const SIMD_8x32& other)
+ {
+ m_avx2 = _mm256_sub_epi32(m_avx2, other.m_avx2);
+ }
+
+ BOTAN_FUNC_ISA("avx2")
+ void operator^=(const SIMD_8x32& other)
+ {
+ m_avx2 = _mm256_xor_si256(m_avx2, other.m_avx2);
+ }
+
+ BOTAN_FUNC_ISA("avx2")
+ void operator|=(const SIMD_8x32& other)
+ {
+ m_avx2 = _mm256_or_si256(m_avx2, other.m_avx2);
+ }
+
+ BOTAN_FUNC_ISA("avx2")
+ void operator&=(const SIMD_8x32& other)
+ {
+ m_avx2 = _mm256_and_si256(m_avx2, other.m_avx2);
+ }
+
+ template<int SHIFT> BOTAN_FUNC_ISA("avx2") SIMD_8x32 shl() const
+ {
+ return SIMD_8x32(_mm256_slli_epi32(m_avx2, SHIFT));
+ }
+
+ template<int SHIFT> BOTAN_FUNC_ISA("avx2") SIMD_8x32 shr() const
+ {
+ return SIMD_8x32(_mm256_srli_epi32(m_avx2, SHIFT));
+ }
+
+ BOTAN_FUNC_ISA("avx2")
+ SIMD_8x32 operator~() const
+ {
+ return SIMD_8x32(_mm256_xor_si256(m_avx2, _mm256_set1_epi32(0xFFFFFFFF)));
+ }
+
+ // (~reg) & other
+ BOTAN_FUNC_ISA("avx2")
+ SIMD_8x32 andc(const SIMD_8x32& other) const
+ {
+ return SIMD_8x32(_mm256_andnot_si256(m_avx2, other.m_avx2));
+ }
+
+ BOTAN_FUNC_ISA("avx2")
+ SIMD_8x32 bswap() const
+ {
+ 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 };
+
+ const __m256i bswap = _mm256_loadu_si256(reinterpret_cast<const __m256i*>(BSWAP_MASK));
+
+ const __m256i output = _mm256_shuffle_epi8(m_avx2, bswap);
+
+ return SIMD_8x32(output);
+ }
+
+ BOTAN_FUNC_ISA("avx2")
+ static void transpose(SIMD_8x32& B0, SIMD_8x32& B1,
+ SIMD_8x32& B2, SIMD_8x32& B3)
+ {
+ const __m256i T0 = _mm256_unpacklo_epi32(B0.m_avx2, B1.m_avx2);
+ const __m256i T1 = _mm256_unpacklo_epi32(B2.m_avx2, B3.m_avx2);
+ const __m256i T2 = _mm256_unpackhi_epi32(B0.m_avx2, B1.m_avx2);
+ const __m256i T3 = _mm256_unpackhi_epi32(B2.m_avx2, B3.m_avx2);
+
+ B0.m_avx2 = _mm256_unpacklo_epi64(T0, T1);
+ B1.m_avx2 = _mm256_unpackhi_epi64(T0, T1);
+ B2.m_avx2 = _mm256_unpacklo_epi64(T2, T3);
+ B3.m_avx2 = _mm256_unpackhi_epi64(T2, T3);
+ }
+
+ BOTAN_FUNC_ISA("avx2")
+ static void transpose(SIMD_8x32& B0, SIMD_8x32& B1,
+ SIMD_8x32& B2, SIMD_8x32& B3,
+ SIMD_8x32& B4, SIMD_8x32& B5,
+ SIMD_8x32& B6, SIMD_8x32& B7)
+ {
+ transpose(B0, B1, B2, B3);
+ transpose(B4, B5, B6, B7);
+
+ swap_tops(B0, B4);
+ swap_tops(B1, B5);
+ swap_tops(B2, B6);
+ swap_tops(B3, B7);
+ }
+
+ BOTAN_FUNC_ISA("avx2")
+ static void reset_registers()
+ {
+ _mm256_zeroupper();
+ }
+
+ BOTAN_FUNC_ISA("avx2")
+ static void zero_registers()
+ {
+ _mm256_zeroall();
+ }
+
+ __m256i BOTAN_FUNC_ISA("avx2") handle() const { return m_avx2; }
+
+ BOTAN_FUNC_ISA("avx2")
+ SIMD_8x32(__m256i x) : m_avx2(x) {}
+
+ private:
+
+ BOTAN_FUNC_ISA("avx2")
+ static void swap_tops(SIMD_8x32& A, SIMD_8x32& B)
+ {
+ SIMD_8x32 T0 = _mm256_permute2x128_si256(A.handle(), B.handle(), 0 + (2 << 4));
+ SIMD_8x32 T1 = _mm256_permute2x128_si256(A.handle(), B.handle(), 1 + (3 << 4));
+ A = T0;
+ B = T1;
+ }
+
+ __m256i m_avx2;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/utils/socket/info.txt b/comm/third_party/botan/src/lib/utils/socket/info.txt
new file mode 100644
index 0000000000..3862f0f74e
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/socket/info.txt
@@ -0,0 +1,18 @@
+<defines>
+SOCKETS -> 20171216
+</defines>
+
+<header:internal>
+uri.h
+socket.h
+socket_udp.h
+</header:internal>
+
+<libs>
+linux -> rt
+mingw -> ws2_32
+windows -> ws2_32
+haiku -> network
+solaris -> socket,nsl
+qnx -> socket
+</libs>
diff --git a/comm/third_party/botan/src/lib/utils/socket/socket.cpp b/comm/third_party/botan/src/lib/utils/socket/socket.cpp
new file mode 100644
index 0000000000..bc632259a6
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/socket/socket.cpp
@@ -0,0 +1,371 @@
+/*
+* (C) 2015,2016,2017 Jack Lloyd
+* (C) 2016 Daniel Neus
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/socket.h>
+#include <botan/exceptn.h>
+#include <botan/mem_ops.h>
+#include <chrono>
+
+#if defined(BOTAN_HAS_BOOST_ASIO)
+ /*
+ * We don't need serial port support anyway, and asking for it causes
+ * macro conflicts with termios.h when this file is included in the
+ * amalgamation.
+ */
+ #define BOOST_ASIO_DISABLE_SERIAL_PORT
+ #include <boost/asio.hpp>
+ #include <boost/asio/system_timer.hpp>
+
+#elif defined(BOTAN_TARGET_OS_HAS_SOCKETS)
+ #include <sys/socket.h>
+ #include <sys/time.h>
+ #include <netinet/in.h>
+ #include <netdb.h>
+ #include <string.h>
+ #include <unistd.h>
+ #include <errno.h>
+ #include <fcntl.h>
+
+#elif defined(BOTAN_TARGET_OS_HAS_WINSOCK2)
+ #include <ws2tcpip.h>
+#endif
+
+namespace Botan {
+
+namespace {
+
+#if defined(BOTAN_HAS_BOOST_ASIO)
+
+class Asio_Socket final : public OS::Socket
+ {
+ public:
+ Asio_Socket(const std::string& hostname,
+ const std::string& service,
+ std::chrono::milliseconds timeout) :
+ m_timeout(timeout), m_timer(m_io), m_tcp(m_io)
+ {
+ m_timer.expires_from_now(m_timeout);
+ check_timeout();
+
+ boost::asio::ip::tcp::resolver resolver(m_io);
+ boost::asio::ip::tcp::resolver::query query(hostname, service);
+ boost::asio::ip::tcp::resolver::iterator dns_iter = resolver.resolve(query);
+
+ boost::system::error_code ec = boost::asio::error::would_block;
+
+ auto connect_cb = [&ec](const boost::system::error_code& e,
+ boost::asio::ip::tcp::resolver::iterator) { ec = e; };
+
+ boost::asio::async_connect(m_tcp, dns_iter, connect_cb);
+
+ while(ec == boost::asio::error::would_block)
+ {
+ m_io.run_one();
+ }
+
+ if(ec)
+ throw boost::system::system_error(ec);
+ if(m_tcp.is_open() == false)
+ throw System_Error("Connection to host " + hostname + " failed");
+ }
+
+ void write(const uint8_t buf[], size_t len) override
+ {
+ m_timer.expires_from_now(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; });
+
+ while(ec == boost::asio::error::would_block) { m_io.run_one(); }
+
+ if(ec)
+ {
+ throw boost::system::system_error(ec);
+ }
+ }
+
+ size_t read(uint8_t buf[], size_t len) override
+ {
+ m_timer.expires_from_now(m_timeout);
+
+ boost::system::error_code ec = boost::asio::error::would_block;
+ size_t got = 0;
+
+ m_tcp.async_read_some(boost::asio::buffer(buf, len),
+ [&](boost::system::error_code cb_ec, size_t cb_got) { ec = cb_ec; got = cb_got; });
+
+ while(ec == boost::asio::error::would_block) { m_io.run_one(); }
+
+ if(ec)
+ {
+ if(ec == boost::asio::error::eof)
+ return 0;
+ throw boost::system::system_error(ec); // Some other error.
+ }
+
+ return got;
+ }
+
+ private:
+ void check_timeout()
+ {
+ if(m_tcp.is_open() && m_timer.expires_at() < std::chrono::system_clock::now())
+ {
+ boost::system::error_code err;
+ m_tcp.close(err);
+ }
+
+ m_timer.async_wait(std::bind(&Asio_Socket::check_timeout, this));
+ }
+
+ const std::chrono::milliseconds m_timeout;
+ boost::asio::io_service m_io;
+ boost::asio::system_timer m_timer;
+ boost::asio::ip::tcp::socket m_tcp;
+ };
+
+#elif defined(BOTAN_TARGET_OS_HAS_SOCKETS) || defined(BOTAN_TARGET_OS_HAS_WINSOCK2)
+
+class BSD_Socket final : public OS::Socket
+ {
+ private:
+#if defined(BOTAN_TARGET_OS_HAS_WINSOCK2)
+ typedef SOCKET socket_type;
+ typedef int socket_op_ret_type;
+ typedef int socklen_type;
+ typedef int sendrecv_len_type;
+ static socket_type invalid_socket() { return INVALID_SOCKET; }
+ static void close_socket(socket_type s) { ::closesocket(s); }
+ static std::string get_last_socket_error() { return std::to_string(::WSAGetLastError()); }
+
+ static bool nonblocking_connect_in_progress()
+ {
+ return (::WSAGetLastError() == WSAEWOULDBLOCK);
+ }
+
+ static void set_nonblocking(socket_type s)
+ {
+ u_long nonblocking = 1;
+ ::ioctlsocket(s, FIONBIO, &nonblocking);
+ }
+
+ static void socket_init()
+ {
+ WSAData wsa_data;
+ WORD wsa_version = MAKEWORD(2, 2);
+
+ if (::WSAStartup(wsa_version, &wsa_data) != 0)
+ {
+ throw System_Error("WSAStartup() failed", WSAGetLastError());
+ }
+
+ if (LOBYTE(wsa_data.wVersion) != 2 || HIBYTE(wsa_data.wVersion) != 2)
+ {
+ ::WSACleanup();
+ throw System_Error("Could not find a usable version of Winsock.dll");
+ }
+ }
+
+ static void socket_fini()
+ {
+ ::WSACleanup();
+ }
+#else
+ typedef int socket_type;
+ typedef ssize_t socket_op_ret_type;
+ typedef socklen_t socklen_type;
+ typedef size_t sendrecv_len_type;
+ static socket_type invalid_socket() { return -1; }
+ static void close_socket(socket_type s) { ::close(s); }
+ static std::string get_last_socket_error() { return ::strerror(errno); }
+ static bool nonblocking_connect_in_progress() { return (errno == EINPROGRESS); }
+ static void set_nonblocking(socket_type s)
+ {
+ if(::fcntl(s, F_SETFL, O_NONBLOCK) < 0)
+ throw System_Error("Setting socket to non-blocking state failed", errno);
+ }
+
+ static void socket_init() {}
+ static void socket_fini() {}
+#endif
+
+ public:
+ BSD_Socket(const std::string& hostname,
+ const std::string& service,
+ std::chrono::microseconds timeout) : m_timeout(timeout)
+ {
+ socket_init();
+
+ m_socket = invalid_socket();
+
+ addrinfo hints;
+ clear_mem(&hints, 1);
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ addrinfo* res;
+
+ int rc = ::getaddrinfo(hostname.c_str(), service.c_str(), &hints, &res);
+
+ if(rc != 0)
+ {
+ throw System_Error("Name resolution failed for " + hostname, rc);
+ }
+
+ for(addrinfo* rp = res; (m_socket == invalid_socket()) && (rp != nullptr); rp = rp->ai_next)
+ {
+ if(rp->ai_family != AF_INET && rp->ai_family != AF_INET6)
+ continue;
+
+ m_socket = ::socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
+
+ if(m_socket == invalid_socket())
+ {
+ // unsupported socket type?
+ continue;
+ }
+
+ set_nonblocking(m_socket);
+
+ int err = ::connect(m_socket, rp->ai_addr, static_cast<socklen_type>(rp->ai_addrlen));
+
+ if(err == -1)
+ {
+ int active = 0;
+ if(nonblocking_connect_in_progress())
+ {
+ 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<int>(m_socket), &write_set);
+
+ active = ::select(static_cast<int>(m_socket + 1), nullptr, &write_set, nullptr, &timeout_tv);
+
+ if(active)
+ {
+ int socket_error = 0;
+ socklen_t len = sizeof(socket_error);
+
+ if(::getsockopt(m_socket, SOL_SOCKET, SO_ERROR, reinterpret_cast<char*>(&socket_error), &len) < 0)
+ throw System_Error("Error calling getsockopt", errno);
+
+ if(socket_error != 0)
+ {
+ active = 0;
+ }
+ }
+ }
+
+ if(active == 0)
+ {
+ close_socket(m_socket);
+ m_socket = invalid_socket();
+ continue;
+ }
+ }
+ }
+
+ ::freeaddrinfo(res);
+
+ if(m_socket == invalid_socket())
+ {
+ throw System_Error("Connecting to " + hostname +
+ " for service " + service + " failed", errno);
+ }
+ }
+
+ ~BSD_Socket()
+ {
+ close_socket(m_socket);
+ m_socket = invalid_socket();
+ socket_fini();
+ }
+
+ 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)
+ {
+ struct timeval timeout = make_timeout_tv();
+ int active = ::select(static_cast<int>(m_socket + 1), nullptr, &write_set, nullptr, &timeout);
+
+ if(active == 0)
+ throw System_Error("Timeout during socket write");
+
+ const size_t left = len - sent_so_far;
+ socket_op_ret_type sent = ::send(m_socket, cast_uint8_ptr_to_char(&buf[sent_so_far]), static_cast<sendrecv_len_type>(left), 0);
+ if(sent < 0)
+ throw System_Error("Socket write failed", errno);
+ else
+ sent_so_far += static_cast<size_t>(sent);
+ }
+ }
+
+ size_t read(uint8_t buf[], size_t len) override
+ {
+ 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<int>(m_socket + 1), &read_set, nullptr, nullptr, &timeout);
+
+ 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<sendrecv_len_type>(len), 0);
+
+ if(got < 0)
+ throw System_Error("Socket read failed", errno);
+
+ return static_cast<size_t>(got);
+ }
+
+ private:
+ struct timeval make_timeout_tv() const
+ {
+ struct timeval tv;
+ tv.tv_sec = static_cast<decltype(timeval::tv_sec)>(m_timeout.count() / 1000000);
+ tv.tv_usec = static_cast<decltype(timeval::tv_usec)>(m_timeout.count() % 1000000);;
+ return tv;
+ }
+
+ const std::chrono::microseconds m_timeout;
+ socket_type m_socket;
+ };
+
+#endif
+
+}
+
+std::unique_ptr<OS::Socket>
+OS::open_socket(const std::string& hostname,
+ const std::string& service,
+ std::chrono::milliseconds timeout)
+ {
+#if defined(BOTAN_HAS_BOOST_ASIO)
+ return std::unique_ptr<OS::Socket>(new Asio_Socket(hostname, service, timeout));
+
+#elif defined(BOTAN_TARGET_OS_HAS_SOCKETS) || defined(BOTAN_TARGET_OS_HAS_WINSOCK2)
+ return std::unique_ptr<OS::Socket>(new BSD_Socket(hostname, service, timeout));
+
+#else
+ BOTAN_UNUSED(hostname);
+ BOTAN_UNUSED(service);
+ BOTAN_UNUSED(timeout);
+ // No sockets for you
+ return std::unique_ptr<Socket>();
+#endif
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/utils/socket/socket.h b/comm/third_party/botan/src/lib/utils/socket/socket.h
new file mode 100644
index 0000000000..03a951478a
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/socket/socket.h
@@ -0,0 +1,64 @@
+/*
+* OS specific utility functions
+* (C) 2015,2016,2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_SOCKET_H_
+#define BOTAN_SOCKET_H_
+
+#include <botan/types.h>
+#include <string>
+#include <chrono>
+
+namespace Botan {
+
+namespace OS {
+
+/*
+* This header is internal (not installed) and these functions are not
+* intended to be called by applications. However they are given public
+* visibility (using BOTAN_TEST_API macro) for the tests. This also probably
+* allows them to be overridden by the application on ELF systems, but
+* this hasn't been tested.
+*/
+
+
+/**
+* A wrapper around a simple blocking TCP socket
+*/
+class BOTAN_TEST_API Socket
+ {
+ public:
+ /**
+ * The socket will be closed upon destruction
+ */
+ virtual ~Socket() = default;
+
+ /**
+ * Write to the socket. Blocks until all bytes sent.
+ * Throws on error.
+ */
+ virtual void write(const uint8_t buf[], size_t len) = 0;
+
+ /**
+ * Reads up to len bytes, returns bytes written to buf.
+ * Returns 0 on EOF. Throws on error.
+ */
+ virtual size_t read(uint8_t buf[], size_t len) = 0;
+ };
+
+/**
+* Open up a socket. Will throw on error. Returns null if sockets are
+* not available on this platform.
+*/
+std::unique_ptr<Socket>
+BOTAN_TEST_API open_socket(const std::string& hostname,
+ const std::string& service,
+ std::chrono::milliseconds timeout);
+
+} // OS
+} // Botan
+
+#endif
diff --git a/comm/third_party/botan/src/lib/utils/socket/socket_udp.cpp b/comm/third_party/botan/src/lib/utils/socket/socket_udp.cpp
new file mode 100644
index 0000000000..fbbdd9abbc
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/socket/socket_udp.cpp
@@ -0,0 +1,344 @@
+/*
+* (C) 2015,2016,2017 Jack Lloyd
+* (C) 2016 Daniel Neus
+* (C) 2019 Nuno Goncalves <nunojpg@gmail.com>
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/socket_udp.h>
+#include <botan/internal/uri.h>
+#include <botan/exceptn.h>
+#include <botan/mem_ops.h>
+#include <chrono>
+
+#if defined(BOTAN_HAS_BOOST_ASIO)
+ /*
+ * We don't need serial port support anyway, and asking for it
+ * causes macro conflicts with Darwin's termios.h when this
+ * file is included in the amalgamation. GH #350
+ */
+ #define BOOST_ASIO_DISABLE_SERIAL_PORT
+ #include <boost/asio.hpp>
+ #include <boost/asio/system_timer.hpp>
+#elif defined(BOTAN_TARGET_OS_HAS_SOCKETS)
+ #include <sys/socket.h>
+ #include <sys/time.h>
+ #include <netinet/in.h>
+ #include <netdb.h>
+ #include <string.h>
+ #include <unistd.h>
+ #include <errno.h>
+ #include <fcntl.h>
+
+#elif defined(BOTAN_TARGET_OS_HAS_WINSOCK2)
+ #include <ws2tcpip.h>
+#endif
+
+namespace Botan {
+
+namespace {
+
+#if defined(BOTAN_HAS_BOOST_ASIO)
+class Asio_SocketUDP final : public OS::SocketUDP
+ {
+ public:
+ Asio_SocketUDP(const std::string& hostname,
+ const std::string& service,
+ std::chrono::microseconds timeout) :
+ m_timeout(timeout), m_timer(m_io), m_udp(m_io)
+ {
+ m_timer.expires_from_now(m_timeout);
+ check_timeout();
+
+ boost::asio::ip::udp::resolver resolver(m_io);
+ boost::asio::ip::udp::resolver::query query(hostname, service);
+ boost::asio::ip::udp::resolver::iterator dns_iter = resolver.resolve(query);
+
+ boost::system::error_code ec = boost::asio::error::would_block;
+
+ auto connect_cb = [&ec](const boost::system::error_code& e,
+ boost::asio::ip::udp::resolver::iterator) { ec = e; };
+
+ boost::asio::async_connect(m_udp, dns_iter, connect_cb);
+
+ while(ec == boost::asio::error::would_block)
+ {
+ m_io.run_one();
+ }
+
+ if(ec)
+ { throw boost::system::system_error(ec); }
+ if(m_udp.is_open() == false)
+ { throw System_Error("Connection to host " + hostname + " failed"); }
+ }
+
+ void write(const uint8_t buf[], size_t len) override
+ {
+ m_timer.expires_from_now(m_timeout);
+
+ boost::system::error_code ec = boost::asio::error::would_block;
+
+ m_udp.async_send(boost::asio::buffer(buf, len),
+ [&ec](boost::system::error_code e, size_t) { ec = e; });
+
+ while(ec == boost::asio::error::would_block)
+ {
+ m_io.run_one();
+ }
+
+ if(ec)
+ {
+ throw boost::system::system_error(ec);
+ }
+ }
+
+ size_t read(uint8_t buf[], size_t len) override
+ {
+ m_timer.expires_from_now(m_timeout);
+
+ boost::system::error_code ec = boost::asio::error::would_block;
+ size_t got = 0;
+
+ m_udp.async_receive(boost::asio::buffer(buf, len),
+ [&](boost::system::error_code cb_ec, size_t cb_got) { ec = cb_ec; got = cb_got; });
+
+ while(ec == boost::asio::error::would_block)
+ {
+ m_io.run_one();
+ }
+
+ if(ec)
+ {
+ if(ec == boost::asio::error::eof)
+ { return 0; }
+ throw boost::system::system_error(ec); // Some other error.
+ }
+
+ return got;
+ }
+
+ private:
+ void check_timeout()
+ {
+ if(m_udp.is_open() && m_timer.expires_at() < std::chrono::system_clock::now())
+ {
+ boost::system::error_code err;
+ m_udp.close(err);
+ }
+
+ m_timer.async_wait(std::bind(&Asio_SocketUDP::check_timeout, this));
+ }
+
+ const std::chrono::microseconds m_timeout;
+ boost::asio::io_service m_io;
+ boost::asio::system_timer m_timer;
+ boost::asio::ip::udp::socket m_udp;
+ };
+#elif defined(BOTAN_TARGET_OS_HAS_SOCKETS) || defined(BOTAN_TARGET_OS_HAS_WINSOCK2)
+class BSD_SocketUDP final : public OS::SocketUDP
+ {
+ public:
+ BSD_SocketUDP(const std::string& hostname,
+ const std::string& service,
+ std::chrono::microseconds timeout) : m_timeout(timeout)
+ {
+ socket_init();
+
+ m_socket = invalid_socket();
+
+ addrinfo* res;
+ addrinfo hints;
+ clear_mem(&hints, 1);
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_DGRAM;
+
+ int rc = ::getaddrinfo(hostname.c_str(), service.c_str(), &hints, &res);
+
+ if(rc != 0)
+ {
+ throw System_Error("Name resolution failed for " + hostname, rc);
+ }
+
+ for(addrinfo* rp = res; (m_socket == invalid_socket()) && (rp != nullptr); rp = rp->ai_next)
+ {
+ if(rp->ai_family != AF_INET && rp->ai_family != AF_INET6)
+ { continue; }
+
+ m_socket = ::socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
+
+ if(m_socket == invalid_socket())
+ {
+ // unsupported socket type?
+ continue;
+ }
+
+ set_nonblocking(m_socket);
+ memcpy(&sa, res->ai_addr, res->ai_addrlen);
+ salen = static_cast<socklen_t>(res->ai_addrlen);
+ }
+
+ ::freeaddrinfo(res);
+
+ if(m_socket == invalid_socket())
+ {
+ throw System_Error("Connecting to " + hostname +
+ " for service " + service + " failed", errno);
+ }
+ }
+
+ ~BSD_SocketUDP()
+ {
+ close_socket(m_socket);
+ m_socket = invalid_socket();
+ socket_fini();
+ }
+
+ 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)
+ {
+ struct timeval timeout = make_timeout_tv();
+ int active = ::select(static_cast<int>(m_socket + 1), nullptr, &write_set, nullptr, &timeout);
+
+ 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<sendrecv_len_type>(left), 0,
+ reinterpret_cast<sockaddr*>(&sa), salen);
+ if(sent < 0)
+ { throw System_Error("Socket write failed", errno); }
+ else
+ { sent_so_far += static_cast<size_t>(sent); }
+ }
+ }
+
+ size_t read(uint8_t buf[], size_t len) override
+ {
+ 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<int>(m_socket + 1), &read_set, nullptr, nullptr, &timeout);
+
+ if(active == 0)
+ { throw System_Error("Timeout during socket read"); }
+
+ socket_op_ret_type got = ::recvfrom(m_socket, cast_uint8_ptr_to_char(buf), static_cast<sendrecv_len_type>(len), 0, nullptr, nullptr);
+
+ if(got < 0)
+ { throw System_Error("Socket read failed", errno); }
+
+ return static_cast<size_t>(got);
+ }
+
+ private:
+#if defined(BOTAN_TARGET_OS_HAS_WINSOCK2)
+ typedef SOCKET socket_type;
+ typedef int socket_op_ret_type;
+ typedef int sendrecv_len_type;
+ static socket_type invalid_socket() { return INVALID_SOCKET; }
+ static void close_socket(socket_type s) { ::closesocket(s); }
+ static std::string get_last_socket_error() { return std::to_string(::WSAGetLastError()); }
+
+ static bool nonblocking_connect_in_progress()
+ {
+ return (::WSAGetLastError() == WSAEWOULDBLOCK);
+ }
+
+ static void set_nonblocking(socket_type s)
+ {
+ u_long nonblocking = 1;
+ ::ioctlsocket(s, FIONBIO, &nonblocking);
+ }
+
+ static void socket_init()
+ {
+ WSAData wsa_data;
+ WORD wsa_version = MAKEWORD(2, 2);
+
+ if(::WSAStartup(wsa_version, &wsa_data) != 0)
+ {
+ throw System_Error("WSAStartup() failed", WSAGetLastError());
+ }
+
+ if(LOBYTE(wsa_data.wVersion) != 2 || HIBYTE(wsa_data.wVersion) != 2)
+ {
+ ::WSACleanup();
+ throw System_Error("Could not find a usable version of Winsock.dll");
+ }
+ }
+
+ static void socket_fini()
+ {
+ ::WSACleanup();
+ }
+#else
+ typedef int socket_type;
+ typedef ssize_t socket_op_ret_type;
+ typedef size_t sendrecv_len_type;
+ static socket_type invalid_socket() { return -1; }
+ static void close_socket(socket_type s) { ::close(s); }
+ static std::string get_last_socket_error() { return ::strerror(errno); }
+ static bool nonblocking_connect_in_progress() { return (errno == EINPROGRESS); }
+ static void set_nonblocking(socket_type s)
+ {
+ if(::fcntl(s, F_SETFL, O_NONBLOCK) < 0)
+ { throw System_Error("Setting socket to non-blocking state failed", errno); }
+ }
+
+ static void socket_init() {}
+ static void socket_fini() {}
+#endif
+ sockaddr_storage sa;
+ socklen_t salen;
+ struct timeval make_timeout_tv() const
+ {
+ struct timeval tv;
+ tv.tv_sec = static_cast<decltype(timeval::tv_sec)>(m_timeout.count() / 1000000);
+ tv.tv_usec = static_cast<decltype(timeval::tv_usec)>(m_timeout.count() % 1000000);;
+ return tv;
+ }
+
+ const std::chrono::microseconds m_timeout;
+ socket_type m_socket;
+ };
+#endif
+}
+
+std::unique_ptr<OS::SocketUDP>
+OS::open_socket_udp(const std::string& hostname,
+ const std::string& service,
+ std::chrono::microseconds timeout)
+ {
+#if defined(BOTAN_HAS_BOOST_ASIO)
+ return std::unique_ptr<OS::SocketUDP>(new Asio_SocketUDP(hostname, service, timeout));
+#elif defined(BOTAN_TARGET_OS_HAS_SOCKETS) || defined(BOTAN_TARGET_OS_HAS_WINSOCK2)
+ return std::unique_ptr<OS::SocketUDP>(new BSD_SocketUDP(hostname, service, timeout));
+#else
+ BOTAN_UNUSED(hostname);
+ BOTAN_UNUSED(service);
+ BOTAN_UNUSED(timeout);
+ return std::unique_ptr<OS::SocketUDP>();
+#endif
+ }
+
+std::unique_ptr<OS::SocketUDP>
+OS::open_socket_udp(const std::string& uri_string,
+ std::chrono::microseconds timeout)
+ {
+ const auto uri = URI::fromAny(uri_string);
+ if(uri.port == 0)
+ { throw Invalid_Argument("UDP port not specified"); }
+ return open_socket_udp(uri.host, std::to_string(uri.port), timeout);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/utils/socket/socket_udp.h b/comm/third_party/botan/src/lib/utils/socket/socket_udp.h
new file mode 100644
index 0000000000..4a9346c2b3
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/socket/socket_udp.h
@@ -0,0 +1,73 @@
+/*
+* (C) 2015,2016,2017 Jack Lloyd
+* (C) 2019 Nuno Goncalves <nunojpg@gmail.com>
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_SOCKET_UDP_H_
+#define BOTAN_SOCKET_UDP_H_
+
+#include <botan/types.h>
+#include <string>
+#include <chrono>
+
+namespace Botan {
+
+namespace OS {
+
+/*
+* This header is internal (not installed) and these functions are not
+* intended to be called by applications. However they are given public
+* visibility (using BOTAN_TEST_API macro) for the tests. This also probably
+* allows them to be overridden by the application on ELF systems, but
+* this hasn't been tested.
+*/
+
+
+/**
+* A wrapper around a simple blocking UDP socket
+*/
+class BOTAN_TEST_API SocketUDP
+ {
+ public:
+ /**
+ * The socket will be closed upon destruction
+ */
+ virtual ~SocketUDP() = default;
+
+ /**
+ * Write to the socket. Returns immediately.
+ * Throws on error.
+ */
+ virtual void write(const uint8_t buf[], size_t len) = 0;
+
+ /**
+ * Reads up to len bytes, returns bytes written to buf.
+ * Returns 0 on EOF. Throws on error.
+ */
+ virtual size_t read(uint8_t buf[], size_t len) = 0;
+ };
+
+/**
+* Open up a socket. Will throw on error. Returns null if sockets are
+* not available on this platform.
+*/
+std::unique_ptr<SocketUDP>
+BOTAN_TEST_API open_socket_udp(const std::string& hostname,
+ const std::string& service,
+ std::chrono::microseconds timeout);
+
+/**
+* Open up a socket. Will throw on error. Returns null if sockets are
+* not available on this platform.
+*/
+std::unique_ptr<SocketUDP>
+BOTAN_TEST_API open_socket_udp(const std::string& uri,
+ std::chrono::microseconds timeout);
+
+
+} // OS
+} // Botan
+
+#endif
diff --git a/comm/third_party/botan/src/lib/utils/socket/uri.cpp b/comm/third_party/botan/src/lib/utils/socket/uri.cpp
new file mode 100644
index 0000000000..5653d97e73
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/socket/uri.cpp
@@ -0,0 +1,188 @@
+/*
+* (C) 2019 Nuno Goncalves <nunojpg@gmail.com>
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/uri.h>
+#include <botan/exceptn.h>
+
+#include <regex>
+
+#if defined(BOTAN_TARGET_OS_HAS_SOCKETS)
+ #include <arpa/inet.h>
+ #include <sys/socket.h>
+ #include <netinet/in.h>
+#elif defined(BOTAN_TARGET_OS_HAS_WINSOCK2)
+ #include <ws2tcpip.h>
+#endif
+
+#if defined(BOTAN_TARGET_OS_HAS_SOCKETS) || defined(BOTAN_TARGET_OS_HAS_WINSOCK2)
+
+namespace {
+
+constexpr bool isdigit(char ch)
+ {
+ return ch >= '0' && ch <= '9';
+ }
+
+bool isDomain(const std::string& domain)
+ {
+#if defined(__GLIBCXX__) && (__GLIBCXX__ < 20160726)
+ // GCC 4.8 does not support regex
+ BOTAN_UNUSED(domain);
+ return true;
+#else
+ std::regex re(
+ R"(^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$)");
+ std::cmatch m;
+ return std::regex_match(domain.c_str(), m, re);
+#endif
+ }
+
+bool isIPv4(const std::string& ip)
+ {
+ sockaddr_storage inaddr;
+ return !!inet_pton(AF_INET, ip.c_str(), &inaddr);
+ }
+
+bool isIPv6(const std::string& ip)
+ {
+ sockaddr_storage in6addr;
+ return !!inet_pton(AF_INET6, ip.c_str(), &in6addr);
+ }
+}
+
+namespace Botan {
+
+URI URI::fromDomain(const std::string& uri)
+ {
+ unsigned port = 0;
+ const auto port_pos = uri.find(':');
+ if(port_pos != std::string::npos)
+ {
+ for(char c : uri.substr(port_pos+1))
+ {
+ if(!isdigit(c))
+ { throw Invalid_Argument("invalid"); }
+ port = port*10 + c - '0';
+ if(port > 65535)
+ { throw Invalid_Argument("invalid"); }
+ }
+ }
+ const auto domain = uri.substr(0, port_pos);
+ if(isIPv4(domain))
+ { throw Invalid_Argument("invalid"); }
+ if(!isDomain(domain))
+ { throw Invalid_Argument("invalid"); }
+ return {Type::Domain, domain, uint16_t(port)};
+ }
+
+URI URI::fromIPv4(const std::string& uri)
+ {
+ unsigned port = 0;
+ const auto port_pos = uri.find(':');
+ if(port_pos != std::string::npos)
+ {
+ for(char c : uri.substr(port_pos+1))
+ {
+ if(!isdigit(c))
+ { throw Invalid_Argument("invalid"); }
+ port = port*10 + c - '0';
+ if(port > 65535)
+ { throw Invalid_Argument("invalid"); }
+ }
+ }
+ const auto ip = uri.substr(0, port_pos);
+ if(!isIPv4(ip))
+ { throw Invalid_Argument("invalid"); }
+ return { Type::IPv4, ip, uint16_t(port) };
+ }
+
+URI URI::fromIPv6(const std::string& uri)
+ {
+ unsigned port = 0;
+ const auto port_pos = uri.find(']');
+ const bool with_braces = (port_pos != std::string::npos);
+ if((uri[0]=='[') != with_braces)
+ { throw Invalid_Argument("invalid"); }
+
+ if(with_braces && (uri.size() > port_pos + 1))
+ {
+ if(uri[port_pos+1]!=':')
+ { throw Invalid_Argument("invalid"); }
+ for(char c : uri.substr(port_pos+2))
+ {
+ if(!isdigit(c))
+ { throw Invalid_Argument("invalid"); }
+ port = port*10 + c - '0';
+ if(port > 65535)
+ { throw Invalid_Argument("invalid"); }
+ }
+ }
+ const auto ip = uri.substr((with_braces ? 1 : 0), port_pos - with_braces);
+ if(!isIPv6(ip))
+ { throw Invalid_Argument("invalid"); }
+ return { Type::IPv6, ip, uint16_t(port) };
+ }
+
+URI URI::fromAny(const std::string& uri)
+ {
+
+ bool colon_seen=false;
+ bool non_number=false;
+ if(uri[0]=='[')
+ { return fromIPv6(uri); }
+ for(auto c : uri)
+ {
+ if(c == ':')
+ {
+ if(colon_seen) //seen two ':'
+ { return fromIPv6(uri); }
+ colon_seen = true;
+ }
+ else if(!isdigit(c) && c != '.')
+ {
+ non_number=true;
+ }
+ }
+ if(!non_number)
+ {
+ if(isIPv4(uri.substr(0, uri.find(':'))))
+ {
+ return fromIPv4(uri);
+ }
+ }
+ return fromDomain(uri);
+ }
+
+std::string URI::to_string() const
+ {
+ if(type == Type::NotSet)
+ {
+ throw Invalid_Argument("not set");
+ }
+
+ if(port != 0)
+ {
+ if(type == Type::IPv6)
+ { return "[" + host + "]:" + std::to_string(port); }
+ return host + ":" + std::to_string(port);
+ }
+ return host;
+ }
+
+}
+
+#else
+
+namespace Botan {
+
+URI URI::fromDomain(const std::string&) {throw Not_Implemented("No socket support enabled in build");}
+URI URI::fromIPv4(const std::string&) {throw Not_Implemented("No socket support enabled in build");}
+URI URI::fromIPv6(const std::string&) {throw Not_Implemented("No socket support enabled in build");}
+URI URI::fromAny(const std::string&) {throw Not_Implemented("No socket support enabled in build");}
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/utils/socket/uri.h b/comm/third_party/botan/src/lib/utils/socket/uri.h
new file mode 100644
index 0000000000..346f9f4f63
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/socket/uri.h
@@ -0,0 +1,49 @@
+/*
+* (C) 2019 Nuno Goncalves <nunojpg@gmail.com>
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_URI_H_
+#define BOTAN_URI_H_
+
+#include <cstdint>
+#include <string>
+
+#include <botan/build.h>
+
+namespace Botan {
+
+struct BOTAN_TEST_API URI
+ {
+ enum class Type : uint8_t
+ {
+ NotSet,
+ IPv4,
+ IPv6,
+ Domain,
+ };
+ static URI fromAny(const std::string& uri);
+ static URI fromIPv4(const std::string& uri);
+ static URI fromIPv6(const std::string& uri);
+ static URI fromDomain(const std::string& uri);
+ URI() = default;
+ URI(Type xtype, const std::string& xhost, unsigned short xport)
+ : type { xtype }
+ , host { xhost }
+ , port { xport }
+ {}
+ bool operator==(const URI& a) const
+ {
+ return type == a.type && host == a.host && port == a.port;
+ }
+ std::string to_string() const;
+
+ const Type type{Type::NotSet};
+ const std::string host{};
+ const uint16_t port{};
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/utils/sqlite3/info.txt b/comm/third_party/botan/src/lib/utils/sqlite3/info.txt
new file mode 100644
index 0000000000..9611944323
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/sqlite3/info.txt
@@ -0,0 +1,13 @@
+<defines>
+SQLITE3 -> 20171118
+</defines>
+
+load_on vendor
+
+<libs>
+all -> sqlite3
+</libs>
+
+<header:public>
+sqlite3.h
+</header:public>
diff --git a/comm/third_party/botan/src/lib/utils/sqlite3/sqlite3.cpp b/comm/third_party/botan/src/lib/utils/sqlite3/sqlite3.cpp
new file mode 100644
index 0000000000..8c71ecd964
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/sqlite3/sqlite3.cpp
@@ -0,0 +1,166 @@
+/*
+* SQLite wrapper
+* (C) 2012 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/sqlite3.h>
+#include <botan/exceptn.h>
+#include <botan/mem_ops.h>
+#include <sqlite3.h>
+
+namespace Botan {
+
+Sqlite3_Database::Sqlite3_Database(const std::string& db_filename)
+ {
+ int rc = ::sqlite3_open(db_filename.c_str(), &m_db);
+
+ if(rc)
+ {
+ const std::string err_msg = ::sqlite3_errmsg(m_db);
+ ::sqlite3_close(m_db);
+ m_db = nullptr;
+ throw SQL_DB_Error("sqlite3_open failed - " + err_msg);
+ }
+ }
+
+Sqlite3_Database::~Sqlite3_Database()
+ {
+ if(m_db)
+ ::sqlite3_close(m_db);
+ m_db = nullptr;
+ }
+
+std::shared_ptr<SQL_Database::Statement> Sqlite3_Database::new_statement(const std::string& base_sql) const
+ {
+ return std::make_shared<Sqlite3_Statement>(m_db, base_sql);
+ }
+
+size_t Sqlite3_Database::row_count(const std::string& table_name)
+ {
+ auto stmt = new_statement("select count(*) from " + table_name);
+
+ if(stmt->step())
+ return stmt->get_size_t(0);
+ else
+ throw SQL_DB_Error("Querying size of table " + table_name + " failed");
+ }
+
+void Sqlite3_Database::create_table(const std::string& table_schema)
+ {
+ char* errmsg = nullptr;
+ int rc = ::sqlite3_exec(m_db, table_schema.c_str(), nullptr, nullptr, &errmsg);
+
+ if(rc != SQLITE_OK)
+ {
+ const std::string err_msg = errmsg;
+ ::sqlite3_free(errmsg);
+ ::sqlite3_close(m_db);
+ m_db = nullptr;
+ throw SQL_DB_Error("sqlite3_exec for table failed - " + err_msg);
+ }
+ }
+
+Sqlite3_Database::Sqlite3_Statement::Sqlite3_Statement(sqlite3* db, const std::string& base_sql)
+ {
+ int rc = ::sqlite3_prepare_v2(db, base_sql.c_str(), -1, &m_stmt, nullptr);
+
+ if(rc != SQLITE_OK)
+ throw SQL_DB_Error("sqlite3_prepare failed on " + base_sql, rc);
+ }
+
+void Sqlite3_Database::Sqlite3_Statement::bind(int column, const std::string& val)
+ {
+ int rc = ::sqlite3_bind_text(m_stmt, column, val.c_str(), -1, SQLITE_TRANSIENT);
+ if(rc != SQLITE_OK)
+ throw SQL_DB_Error("sqlite3_bind_text failed", rc);
+ }
+
+void Sqlite3_Database::Sqlite3_Statement::bind(int column, size_t val)
+ {
+ if(val != static_cast<size_t>(static_cast<int>(val))) // is this cast legit?
+ throw SQL_DB_Error("sqlite3 cannot store " + std::to_string(val) + " without truncation");
+ int rc = ::sqlite3_bind_int(m_stmt, column, val);
+ if(rc != SQLITE_OK)
+ throw SQL_DB_Error("sqlite3_bind_int failed", rc);
+ }
+
+void Sqlite3_Database::Sqlite3_Statement::bind(int column, std::chrono::system_clock::time_point time)
+ {
+ const int timeval = std::chrono::duration_cast<std::chrono::seconds>(time.time_since_epoch()).count();
+ bind(column, timeval);
+ }
+
+void Sqlite3_Database::Sqlite3_Statement::bind(int column, const std::vector<uint8_t>& val)
+ {
+ int rc = ::sqlite3_bind_blob(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_blob(m_stmt, column, p, len, SQLITE_TRANSIENT);
+ if(rc != SQLITE_OK)
+ throw SQL_DB_Error("sqlite3_bind_text failed", rc);
+ }
+
+std::pair<const uint8_t*, size_t> Sqlite3_Database::Sqlite3_Statement::get_blob(int column)
+ {
+ BOTAN_ASSERT(::sqlite3_column_type(m_stmt, column) == SQLITE_BLOB,
+ "Return value is a blob");
+
+ const void* session_blob = ::sqlite3_column_blob(m_stmt, column);
+ const int session_blob_size = ::sqlite3_column_bytes(m_stmt, column);
+
+ BOTAN_ASSERT(session_blob_size >= 0, "Blob size is non-negative");
+
+ return std::make_pair(static_cast<const uint8_t*>(session_blob),
+ static_cast<size_t>(session_blob_size));
+ }
+
+std::string Sqlite3_Database::Sqlite3_Statement::get_str(int column)
+ {
+ BOTAN_ASSERT(::sqlite3_column_type(m_stmt, column) == SQLITE_TEXT,
+ "Return value is text");
+
+ const unsigned char* str = ::sqlite3_column_text(m_stmt, column);
+
+ return std::string(cast_uint8_ptr_to_char(str));
+ }
+
+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 int sessions_int = ::sqlite3_column_int(m_stmt, column);
+
+ BOTAN_ASSERT(sessions_int >= 0, "Expected size_t is non-negative");
+
+ return static_cast<size_t>(sessions_int);
+ }
+
+size_t Sqlite3_Database::Sqlite3_Statement::spin()
+ {
+ size_t steps = 0;
+ while(step())
+ {
+ ++steps;
+ }
+
+ return steps;
+ }
+
+bool Sqlite3_Database::Sqlite3_Statement::step()
+ {
+ return (::sqlite3_step(m_stmt) == SQLITE_ROW);
+ }
+
+Sqlite3_Database::Sqlite3_Statement::~Sqlite3_Statement()
+ {
+ ::sqlite3_finalize(m_stmt);
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/utils/sqlite3/sqlite3.h b/comm/third_party/botan/src/lib/utils/sqlite3/sqlite3.h
new file mode 100644
index 0000000000..08a0f0ae7c
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/sqlite3/sqlite3.h
@@ -0,0 +1,58 @@
+/*
+* SQLite3 wrapper
+* (C) 2012,2014 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_UTILS_SQLITE3_H_
+#define BOTAN_UTILS_SQLITE3_H_
+
+#include <botan/database.h>
+
+class sqlite3;
+class sqlite3_stmt;
+
+namespace Botan {
+
+class BOTAN_PUBLIC_API(2,0) Sqlite3_Database final : public SQL_Database
+ {
+ public:
+ Sqlite3_Database(const std::string& file);
+
+ ~Sqlite3_Database();
+
+ size_t row_count(const std::string& table_name) override;
+
+ void create_table(const std::string& table_schema) override;
+
+ std::shared_ptr<Statement> new_statement(const std::string& sql) const override;
+ private:
+ class Sqlite3_Statement final : public Statement
+ {
+ public:
+ void bind(int column, const std::string& val) override;
+ void bind(int column, size_t val) override;
+ void bind(int column, std::chrono::system_clock::time_point time) override;
+ void bind(int column, const std::vector<uint8_t>& val) override;
+ void bind(int column, const uint8_t* data, size_t len) override;
+
+ std::pair<const uint8_t*, size_t> get_blob(int column) override;
+ std::string get_str(int column) override;
+ size_t get_size_t(int column) override;
+
+ size_t spin() override;
+ bool step() override;
+
+ Sqlite3_Statement(sqlite3* db, const std::string& base_sql);
+ ~Sqlite3_Statement();
+ private:
+ sqlite3_stmt* m_stmt;
+ };
+
+ sqlite3* m_db;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/utils/stl_compatibility.h b/comm/third_party/botan/src/lib/utils/stl_compatibility.h
new file mode 100644
index 0000000000..03bd5c8ac1
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/stl_compatibility.h
@@ -0,0 +1,80 @@
+/*
+* STL standards compatibility functions
+* (C) 2017 Tomasz Frydrych
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_STL_COMPATIBILITY_H_
+#define BOTAN_STL_COMPATIBILITY_H_
+
+#include <botan/types.h>
+#include <memory>
+
+#if __cplusplus < 201402L
+#include <cstddef>
+#include <type_traits>
+#include <utility>
+#endif
+
+BOTAN_FUTURE_INTERNAL_HEADER(stl_compatability.h)
+
+namespace Botan
+{
+/*
+* std::make_unique functionality similar as we have in C++14.
+* C++11 version based on proposal for C++14 implemenatation by Stephan T. Lavavej
+* source: https://isocpp.org/files/papers/N3656.txt
+*/
+#if __cplusplus >= 201402L
+template <typename T, typename ... Args>
+constexpr auto make_unique(Args&&... args)
+ {
+ return std::make_unique<T>(std::forward<Args>(args)...);
+ }
+
+template<class T>
+constexpr auto make_unique(std::size_t size)
+ {
+ return std::make_unique<T>(size);
+ }
+
+#else
+namespace stlCompatibilityDetails
+{
+template<class T> struct _Unique_if
+ {
+ typedef std::unique_ptr<T> _Single_object;
+ };
+
+template<class T> struct _Unique_if<T[]>
+ {
+ typedef std::unique_ptr<T[]> _Unknown_bound;
+ };
+
+template<class T, size_t N> struct _Unique_if<T[N]>
+ {
+ typedef void _Known_bound;
+ };
+} // namespace stlCompatibilityDetails
+
+template<class T, class... Args>
+typename stlCompatibilityDetails::_Unique_if<T>::_Single_object make_unique(Args&&... args)
+ {
+ return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
+ }
+
+template<class T>
+typename stlCompatibilityDetails::_Unique_if<T>::_Unknown_bound make_unique(size_t n)
+ {
+ typedef typename std::remove_extent<T>::type U;
+ return std::unique_ptr<T>(new U[n]());
+ }
+
+template<class T, class... Args>
+typename stlCompatibilityDetails::_Unique_if<T>::_Known_bound make_unique(Args&&...) = delete;
+
+#endif
+
+} // namespace Botan
+#endif
diff --git a/comm/third_party/botan/src/lib/utils/stl_util.h b/comm/third_party/botan/src/lib/utils/stl_util.h
new file mode 100644
index 0000000000..d9167bb7db
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/stl_util.h
@@ -0,0 +1,110 @@
+/*
+* STL Utility Functions
+* (C) 1999-2007 Jack Lloyd
+* (C) 2015 Simon Warta (Kullo GmbH)
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_STL_UTIL_H_
+#define BOTAN_STL_UTIL_H_
+
+#include <vector>
+#include <string>
+#include <map>
+#include <set>
+#include <botan/secmem.h>
+
+namespace Botan {
+
+inline std::vector<uint8_t> to_byte_vector(const std::string& s)
+ {
+ return std::vector<uint8_t>(s.cbegin(), s.cend());
+ }
+
+inline std::string to_string(const secure_vector<uint8_t> &bytes)
+ {
+ return std::string(bytes.cbegin(), bytes.cend());
+ }
+
+/**
+* Return the keys of a map as a std::set
+*/
+template<typename K, typename V>
+std::set<K> map_keys_as_set(const std::map<K, V>& kv)
+ {
+ std::set<K> s;
+ for(auto&& i : kv)
+ {
+ s.insert(i.first);
+ }
+ return s;
+ }
+
+/*
+* Searching through a std::map
+* @param mapping the map to search
+* @param key is what to look for
+* @param null_result is the value to return if key is not in mapping
+* @return mapping[key] or null_result
+*/
+template<typename K, typename V>
+inline V search_map(const std::map<K, V>& mapping,
+ const K& key,
+ const V& null_result = V())
+ {
+ auto i = mapping.find(key);
+ if(i == mapping.end())
+ return null_result;
+ return i->second;
+ }
+
+template<typename K, typename V, typename R>
+inline R search_map(const std::map<K, V>& mapping, const K& key,
+ const R& null_result, const R& found_result)
+ {
+ auto i = mapping.find(key);
+ if(i == mapping.end())
+ return null_result;
+ return found_result;
+ }
+
+/*
+* Insert a key/value pair into a multimap
+*/
+template<typename K, typename V>
+void multimap_insert(std::multimap<K, V>& multimap,
+ const K& key, const V& value)
+ {
+ multimap.insert(std::make_pair(key, value));
+ }
+
+/**
+* Existence check for values
+*/
+template<typename T>
+bool value_exists(const std::vector<T>& vec,
+ const T& val)
+ {
+ for(size_t i = 0; i != vec.size(); ++i)
+ if(vec[i] == val)
+ return true;
+ return false;
+ }
+
+template<typename T, typename Pred>
+void map_remove_if(Pred pred, T& assoc)
+ {
+ auto i = assoc.begin();
+ while(i != assoc.end())
+ {
+ if(pred(i->first))
+ assoc.erase(i++);
+ else
+ i++;
+ }
+ }
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/utils/thread_utils/barrier.cpp b/comm/third_party/botan/src/lib/utils/thread_utils/barrier.cpp
new file mode 100644
index 0000000000..c05c22121e
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/thread_utils/barrier.cpp
@@ -0,0 +1,36 @@
+/*
+* Barrier
+* (C) 2016 Joel Low
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/barrier.h>
+
+namespace Botan {
+
+void Barrier::wait(size_t delta)
+ {
+ std::lock_guard<std::mutex> lock(m_mutex);
+ m_value += delta;
+ }
+
+void Barrier::sync()
+ {
+ std::unique_lock<std::mutex> lock(m_mutex);
+
+ if(m_value > 1)
+ {
+ --m_value;
+ const size_t current_syncs = m_syncs;
+ m_cond.wait(lock, [this, &current_syncs] { return m_syncs != current_syncs; });
+ }
+ else
+ {
+ m_value = 0;
+ ++m_syncs;
+ m_cond.notify_all();
+ }
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/utils/thread_utils/barrier.h b/comm/third_party/botan/src/lib/utils/thread_utils/barrier.h
new file mode 100644
index 0000000000..749da92a77
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/thread_utils/barrier.h
@@ -0,0 +1,42 @@
+/*
+* Barrier
+* (C) 2016 Joel Low
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_UTIL_BARRIER_H_
+#define BOTAN_UTIL_BARRIER_H_
+
+#include <mutex>
+#include <condition_variable>
+
+namespace Botan {
+
+/**
+Barrier implements a barrier synchronization primitive. wait() will
+indicate how many threads to synchronize; each thread needing
+synchronization should call sync(). When sync() returns, the barrier
+is reset to zero, and the m_syncs counter is incremented. m_syncs is a
+counter to ensure that wait() can be called after a sync() even if the
+previously sleeping threads have not awoken.)
+*/
+class Barrier final
+ {
+ public:
+ explicit Barrier(int value = 0) : m_value(value), m_syncs(0) {}
+
+ void wait(size_t delta);
+
+ void sync();
+
+ private:
+ size_t m_value;
+ size_t m_syncs;
+ std::mutex m_mutex;
+ std::condition_variable m_cond;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/utils/thread_utils/info.txt b/comm/third_party/botan/src/lib/utils/thread_utils/info.txt
new file mode 100644
index 0000000000..057f536005
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/thread_utils/info.txt
@@ -0,0 +1,14 @@
+<defines>
+THREAD_UTILS -> 20190922
+</defines>
+
+<header:internal>
+rwlock.h
+barrier.h
+semaphore.h
+thread_pool.h
+</header:internal>
+
+<os_features>
+threads
+</os_features>
diff --git a/comm/third_party/botan/src/lib/utils/thread_utils/rwlock.cpp b/comm/third_party/botan/src/lib/utils/thread_utils/rwlock.cpp
new file mode 100644
index 0000000000..58500a83de
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/thread_utils/rwlock.cpp
@@ -0,0 +1,58 @@
+/*
+* (C) 2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/rwlock.h>
+
+namespace Botan {
+
+RWLock::RWLock() : m_state(0) {}
+
+void RWLock::lock()
+ {
+ std::unique_lock<std::mutex> lock(m_mutex);
+ while(m_state & is_writing)
+ m_gate1.wait(lock);
+ m_state |= is_writing;
+ while(m_state & readers_mask)
+ m_gate2.wait(lock);
+ }
+
+void RWLock::unlock()
+ {
+ std::unique_lock<std::mutex> lock(m_mutex);
+ m_state = 0;
+ m_gate1.notify_all();
+ }
+
+void RWLock::lock_shared()
+ {
+ std::unique_lock<std::mutex> lock(m_mutex);
+ while((m_state & is_writing) || (m_state & readers_mask) == readers_mask)
+ m_gate1.wait(lock);
+ const uint32_t num_readers = (m_state & readers_mask) + 1;
+ m_state &= ~readers_mask;
+ m_state |= num_readers;
+ }
+
+void RWLock::unlock_shared()
+ {
+ std::unique_lock<std::mutex> 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(num_readers == 0)
+ m_gate2.notify_one();
+ }
+ else
+ {
+ if(num_readers == readers_mask - 1)
+ m_gate1.notify_one();
+ }
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/utils/thread_utils/rwlock.h b/comm/third_party/botan/src/lib/utils/thread_utils/rwlock.h
new file mode 100644
index 0000000000..324e667538
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/thread_utils/rwlock.h
@@ -0,0 +1,42 @@
+/*
+* (C) 2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_RWLOCK_H_
+#define BOTAN_RWLOCK_H_
+
+#include <botan/types.h>
+#include <mutex>
+#include <condition_variable>
+
+namespace Botan {
+
+/**
+* A read-write lock. Writers are favored.
+*/
+class BOTAN_TEST_API RWLock final
+ {
+ public:
+ RWLock();
+
+ void lock();
+ void unlock();
+
+ void lock_shared();
+ void unlock_shared();
+ private:
+ std::mutex m_mutex;
+ std::condition_variable m_gate1;
+ std::condition_variable m_gate2;
+ uint32_t m_state;
+
+ // 2**31 concurrent readers should be enough for anyone
+ static const uint32_t is_writing = static_cast<uint32_t>(1) << 31;
+ static const uint32_t readers_mask = ~is_writing;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/utils/thread_utils/semaphore.cpp b/comm/third_party/botan/src/lib/utils/thread_utils/semaphore.cpp
new file mode 100644
index 0000000000..0858a013f2
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/thread_utils/semaphore.cpp
@@ -0,0 +1,38 @@
+/*
+* Semaphore
+* (C) 2013 Joel Low
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/semaphore.h>
+
+// Based on code by Pierre Gaston (http://p9as.blogspot.com/2012/06/c11-semaphores.html)
+
+namespace Botan {
+
+void Semaphore::release(size_t n)
+ {
+ for(size_t i = 0; i != n; ++i)
+ {
+ std::lock_guard<std::mutex> lock(m_mutex);
+
+ if(m_value++ < 0)
+ {
+ ++m_wakeups;
+ m_cond.notify_one();
+ }
+ }
+ }
+
+void Semaphore::acquire()
+ {
+ std::unique_lock<std::mutex> lock(m_mutex);
+ if(m_value-- <= 0)
+ {
+ m_cond.wait(lock, [this] { return m_wakeups > 0; });
+ --m_wakeups;
+ }
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/utils/thread_utils/semaphore.h b/comm/third_party/botan/src/lib/utils/thread_utils/semaphore.h
new file mode 100644
index 0000000000..538d9ecb51
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/thread_utils/semaphore.h
@@ -0,0 +1,34 @@
+/*
+* Semaphore
+* (C) 2013 Joel Low
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_SEMAPHORE_H_
+#define BOTAN_SEMAPHORE_H_
+
+#include <condition_variable>
+#include <mutex>
+
+namespace Botan {
+
+class Semaphore final
+ {
+ public:
+ explicit Semaphore(int value = 0) : m_value(value), m_wakeups(0) {}
+
+ void acquire();
+
+ void release(size_t n = 1);
+
+ private:
+ int m_value;
+ int m_wakeups;
+ std::mutex m_mutex;
+ std::condition_variable m_cond;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/utils/thread_utils/thread_pool.cpp b/comm/third_party/botan/src/lib/utils/thread_utils/thread_pool.cpp
new file mode 100644
index 0000000000..405f79585a
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/thread_utils/thread_pool.cpp
@@ -0,0 +1,103 @@
+/*
+* (C) 2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/thread_pool.h>
+#include <botan/internal/os_utils.h>
+#include <botan/exceptn.h>
+#include <thread>
+
+namespace Botan {
+
+//static
+Thread_Pool& Thread_Pool::global_instance()
+ {
+ static Thread_Pool g_thread_pool(OS::read_env_variable_sz("BOTAN_THREAD_POOL_SIZE"));
+ return g_thread_pool;
+ }
+
+Thread_Pool::Thread_Pool(size_t pool_size)
+ {
+ if(pool_size == 0)
+ {
+ pool_size = OS::get_cpu_available();
+
+ /*
+ * For large machines don't create too many threads, unless
+ * explicitly asked to by the caller.
+ */
+ if(pool_size > 16)
+ pool_size = 16;
+ }
+
+ if(pool_size <= 1)
+ pool_size = 2;
+
+ m_shutdown = false;
+
+ for(size_t i = 0; i != pool_size; ++i)
+ {
+ m_workers.push_back(std::thread(&Thread_Pool::worker_thread, this));
+ }
+ }
+
+void Thread_Pool::shutdown()
+ {
+ {
+ std::unique_lock<std::mutex> lock(m_mutex);
+
+ if(m_shutdown == true)
+ return;
+
+ m_shutdown = true;
+
+ m_more_tasks.notify_all();
+ }
+
+ for(auto&& thread : m_workers)
+ {
+ thread.join();
+ }
+ m_workers.clear();
+ }
+
+void Thread_Pool::queue_thunk(std::function<void ()> fn)
+ {
+ std::unique_lock<std::mutex> lock(m_mutex);
+
+ if(m_shutdown)
+ throw Invalid_State("Cannot add work after thread pool has shut down");
+
+ m_tasks.push_back(fn);
+ m_more_tasks.notify_one();
+ }
+
+void Thread_Pool::worker_thread()
+ {
+ for(;;)
+ {
+ std::function<void()> task;
+
+ {
+ std::unique_lock<std::mutex> lock(m_mutex);
+ m_more_tasks.wait(lock, [this]{ return m_shutdown || !m_tasks.empty(); });
+
+ if(m_tasks.empty())
+ {
+ if(m_shutdown)
+ return;
+ else
+ continue;
+ }
+
+ task = m_tasks.front();
+ m_tasks.pop_front();
+ }
+
+ task();
+ }
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/utils/thread_utils/thread_pool.h b/comm/third_party/botan/src/lib/utils/thread_utils/thread_pool.h
new file mode 100644
index 0000000000..34f4f26dbc
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/thread_utils/thread_pool.h
@@ -0,0 +1,82 @@
+/*
+* (C) 2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_THREAD_POOL_H_
+#define BOTAN_THREAD_POOL_H_
+
+#include <botan/types.h>
+#include <functional>
+#include <deque>
+#include <vector>
+#include <memory>
+#include <utility>
+#include <type_traits>
+#include <mutex>
+#include <thread>
+#include <future>
+#include <condition_variable>
+
+namespace Botan {
+
+class BOTAN_TEST_API Thread_Pool
+ {
+ public:
+ /**
+ * Return an instance to a shared thread pool
+ */
+ static Thread_Pool& global_instance();
+
+ /**
+ * 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() { shutdown(); }
+
+ void shutdown();
+
+ size_t worker_count() const { return m_workers.size(); }
+
+ Thread_Pool(const Thread_Pool&) = delete;
+ Thread_Pool& operator=(const Thread_Pool&) = delete;
+
+ Thread_Pool(Thread_Pool&&) = delete;
+ Thread_Pool& operator=(Thread_Pool&&) = delete;
+
+ /*
+ * Enqueue some work
+ */
+ void queue_thunk(std::function<void ()>);
+
+ template<class F, class... Args>
+ auto run(F&& f, Args&&... args) -> std::future<typename std::result_of<F(Args...)>::type>
+ {
+ typedef typename std::result_of<F(Args...)>::type return_type;
+
+ auto future_work = std::bind(std::forward<F>(f), std::forward<Args>(args)...);
+ auto task = std::make_shared<std::packaged_task<return_type ()>>(future_work);
+ auto future_result = task->get_future();
+ queue_thunk([task]() { (*task)(); });
+ return future_result;
+ }
+
+ private:
+ void worker_thread();
+
+ // Only touched in constructor and destructor
+ std::vector<std::thread> m_workers;
+
+ std::mutex m_mutex;
+ std::condition_variable m_more_tasks;
+ std::deque<std::function<void ()>> m_tasks;
+ bool m_shutdown;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/utils/timer.cpp b/comm/third_party/botan/src/lib/utils/timer.cpp
new file mode 100644
index 0000000000..404da5ce8b
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/timer.cpp
@@ -0,0 +1,150 @@
+/*
+* (C) 2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/timer.h>
+#include <botan/internal/os_utils.h>
+#include <algorithm>
+#include <sstream>
+#include <iomanip>
+
+namespace Botan {
+
+void Timer::start()
+ {
+ stop();
+ m_timer_start = OS::get_system_timestamp_ns();
+ m_cpu_cycles_start = OS::get_cpu_cycle_counter();
+ }
+
+void Timer::stop()
+ {
+ if(m_timer_start)
+ {
+ if(m_cpu_cycles_start != 0)
+ {
+ const uint64_t cycles_taken = OS::get_cpu_cycle_counter() - m_cpu_cycles_start;
+ if(cycles_taken > 0)
+ {
+ m_cpu_cycles_used += static_cast<size_t>(cycles_taken * m_clock_cycle_ratio);
+ }
+ }
+
+ const uint64_t now = OS::get_system_timestamp_ns();
+
+ if(now > m_timer_start)
+ {
+ const uint64_t dur = now - m_timer_start;
+
+ m_time_used += dur;
+
+ if(m_event_count == 0)
+ {
+ m_min_time = m_max_time = dur;
+ }
+ else
+ {
+ m_max_time = std::max(m_max_time, dur);
+ m_min_time = std::min(m_min_time, dur);
+ }
+ }
+
+ m_timer_start = 0;
+ ++m_event_count;
+ }
+ }
+
+bool Timer::operator<(const Timer& other) const
+ {
+ if(this->doing() != other.doing())
+ return (this->doing() < other.doing());
+
+ return (this->get_name() < other.get_name());
+ }
+
+std::string Timer::to_string() const
+ {
+ if(m_custom_msg.size() > 0)
+ {
+ return m_custom_msg;
+ }
+ else if(this->buf_size() == 0)
+ {
+ return result_string_ops();
+ }
+ else
+ {
+ return result_string_bps();
+ }
+ }
+
+std::string Timer::result_string_bps() const
+ {
+ const size_t MiB = 1024 * 1024;
+
+ const double MiB_total = static_cast<double>(events()) / MiB;
+ const double MiB_per_sec = MiB_total / seconds();
+
+ std::ostringstream oss;
+ oss << get_name();
+
+ if(!doing().empty())
+ {
+ oss << " " << doing();
+ }
+
+ if(buf_size() > 0)
+ {
+ oss << " buffer size " << buf_size() << " bytes:";
+ }
+
+ if(events() == 0)
+ oss << " " << "N/A";
+ else
+ oss << " " << std::fixed << std::setprecision(3) << MiB_per_sec << " MiB/sec";
+
+ if(cycles_consumed() != 0)
+ {
+ const double cycles_per_byte = static_cast<double>(cycles_consumed()) / events();
+ oss << " " << std::fixed << std::setprecision(2) << cycles_per_byte << " cycles/byte";
+ }
+
+ oss << " (" << MiB_total << " MiB in " << milliseconds() << " ms)\n";
+
+ return oss.str();
+ }
+
+std::string Timer::result_string_ops() const
+ {
+ std::ostringstream oss;
+
+ oss << get_name() << " ";
+
+ if(events() == 0)
+ {
+ oss << "no events\n";
+ }
+ else
+ {
+ oss << static_cast<uint64_t>(events_per_second())
+ << ' ' << doing() << "/sec; "
+ << std::setprecision(2) << std::fixed
+ << ms_per_event() << " ms/op";
+
+ if(cycles_consumed() != 0)
+ {
+ const double cycles_per_op = static_cast<double>(cycles_consumed()) / events();
+ const int precision = (cycles_per_op < 10000) ? 2 : 0;
+ oss << " " << std::fixed << std::setprecision(precision) << cycles_per_op << " cycles/op";
+ }
+
+ oss << " (" << events() << " " << (events() == 1 ? "op" : "ops")
+ << " in " << milliseconds() << " ms)\n";
+ }
+
+ return oss.str();
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/utils/timer.h b/comm/third_party/botan/src/lib/utils/timer.h
new file mode 100644
index 0000000000..f54c81fb62
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/timer.h
@@ -0,0 +1,185 @@
+/*
+* (C) 2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_TIMER_H_
+#define BOTAN_TIMER_H_
+
+#include <botan/types.h>
+#include <string>
+#include <chrono>
+
+namespace Botan {
+
+class BOTAN_TEST_API Timer final
+ {
+ public:
+ Timer(const std::string& name,
+ const std::string& provider,
+ const std::string& doing,
+ uint64_t event_mult,
+ size_t buf_size,
+ double clock_cycle_ratio,
+ uint64_t clock_speed)
+ : m_name(name + ((provider.empty() || provider == "base") ? "" : " [" + provider + "]"))
+ , m_doing(doing)
+ , m_buf_size(buf_size)
+ , m_event_mult(event_mult)
+ , m_clock_cycle_ratio(clock_cycle_ratio)
+ , m_clock_speed(clock_speed)
+ {}
+
+ Timer(const std::string& name) :
+ Timer(name, "", "", 1, 0, 0.0, 0)
+ {}
+
+ Timer(const std::string& 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)
+ {
+ return (milliseconds() < msec.count());
+ }
+
+ class Timer_Scope final
+ {
+ public:
+ explicit Timer_Scope(Timer& timer)
+ : m_timer(timer)
+ {
+ m_timer.start();
+ }
+ ~Timer_Scope()
+ {
+ try
+ {
+ m_timer.stop();
+ }
+ catch(...) {}
+ }
+ private:
+ Timer& m_timer;
+ };
+
+ template<typename F>
+ auto run(F f) -> decltype(f())
+ {
+ Timer_Scope timer(*this);
+ return f();
+ }
+
+ template<typename F>
+ void run_until_elapsed(std::chrono::milliseconds msec, F f)
+ {
+ while(this->under(msec))
+ {
+ run(f);
+ }
+ }
+
+ uint64_t value() const
+ {
+ return m_time_used;
+ }
+
+ double seconds() const
+ {
+ return milliseconds() / 1000.0;
+ }
+
+ double milliseconds() const
+ {
+ return value() / 1000000.0;
+ }
+
+ double ms_per_event() const
+ {
+ return milliseconds() / events();
+ }
+
+ uint64_t cycles_consumed() const
+ {
+ if(m_clock_speed != 0)
+ {
+ return static_cast<uint64_t>((m_clock_speed * value()) / 1000.0);
+ }
+ return m_cpu_cycles_used;
+ }
+
+ uint64_t events() const
+ {
+ return m_event_count * m_event_mult;
+ }
+
+ const std::string& get_name() const
+ {
+ return m_name;
+ }
+
+ const std::string& doing() const
+ {
+ return m_doing;
+ }
+
+ size_t buf_size() const
+ {
+ return m_buf_size;
+ }
+
+ double bytes_per_second() const
+ {
+ return seconds() > 0.0 ? events() / seconds() : 0.0;
+ }
+
+ double events_per_second() const
+ {
+ return seconds() > 0.0 ? events() / seconds() : 0.0;
+ }
+
+ double seconds_per_event() const
+ {
+ return events() > 0 ? seconds() / events() : 0.0;
+ }
+
+ void set_custom_msg(const std::string& s)
+ {
+ m_custom_msg = s;
+ }
+
+ bool operator<(const Timer& other) const;
+
+ std::string to_string() const;
+
+ private:
+ std::string result_string_bps() const;
+ std::string result_string_ops() const;
+
+ // const data
+ std::string m_name, m_doing;
+ size_t m_buf_size;
+ uint64_t m_event_mult;
+ double m_clock_cycle_ratio;
+ uint64_t m_clock_speed;
+
+ // set at runtime
+ std::string m_custom_msg;
+ uint64_t m_time_used = 0, m_timer_start = 0;
+ uint64_t m_event_count = 0;
+
+ uint64_t m_max_time = 0, m_min_time = 0;
+ uint64_t m_cpu_cycles_start = 0, m_cpu_cycles_used = 0;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/utils/types.h b/comm/third_party/botan/src/lib/utils/types.h
new file mode 100644
index 0000000000..549163aa8c
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/types.h
@@ -0,0 +1,112 @@
+/*
+* Low Level Types
+* (C) 1999-2007 Jack Lloyd
+* (C) 2015 Simon Warta (Kullo GmbH)
+* (C) 2016 René Korthaus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_TYPES_H_
+#define BOTAN_TYPES_H_
+
+#include <botan/build.h> // IWYU pragma: export
+#include <botan/assert.h> // IWYU pragma: export
+#include <cstddef> // IWYU pragma: export
+#include <cstdint> // IWYU pragma: export
+#include <memory> // IWYU pragma: export
+
+namespace Botan {
+
+/**
+* @mainpage Botan Crypto Library API Reference
+*
+* <dl>
+* <dt>Abstract Base Classes<dd>
+* BlockCipher, HashFunction, KDF, MessageAuthenticationCode, RandomNumberGenerator,
+* StreamCipher, SymmetricAlgorithm, AEAD_Mode, Cipher_Mode
+* <dt>Public Key Interface Classes<dd>
+* PK_Key_Agreement, PK_Signer, PK_Verifier, PK_Encryptor, PK_Decryptor
+* <dt>Authenticated Encryption Modes<dd>
+* @ref CCM_Mode "CCM", @ref ChaCha20Poly1305_Mode "ChaCha20Poly1305", @ref EAX_Mode "EAX",
+* @ref GCM_Mode "GCM", @ref OCB_Mode "OCB", @ref SIV_Mode "SIV"
+* <dt>Block Ciphers<dd>
+* @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 "CAST-256", DES, @ref DESX "DES-X", @ref TripleDES "3DES",
+* @ref GOST_28147_89 "GOST 28147-89", IDEA, KASUMI, Lion, MISTY1, Noekeon, SEED, Serpent, SHACAL2, SM4,
+* @ref Threefish_512 "Threefish", Twofish, XTEA
+* <dt>Stream Ciphers<dd>
+* ChaCha, @ref CTR_BE "CTR", OFB, RC4, Salsa20
+* <dt>Hash Functions<dd>
+* BLAKE2b, @ref GOST_34_11 "GOST 34.11", @ref Keccak_1600 "Keccak", MD4, MD5, @ref RIPEMD_160 "RIPEMD-160",
+* @ref SHA_160 "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, Tiger, Whirlpool
+* <dt>Non-Cryptographic Checksums<dd>
+* Adler32, CRC24, CRC32
+* <dt>Message Authentication Codes<dd>
+* @ref CBC_MAC "CBC-MAC", CMAC, HMAC, Poly1305, SipHash, ANSI_X919_MAC
+* <dt>Random Number Generators<dd>
+* AutoSeeded_RNG, HMAC_DRBG, Processor_RNG, System_RNG
+* <dt>Key Derivation<dd>
+* HKDF, @ref KDF1 "KDF1 (IEEE 1363)", @ref KDF1_18033 "KDF1 (ISO 18033-2)", @ref KDF2 "KDF2 (IEEE 1363)",
+* @ref sp800_108.h "SP800-108", @ref SP800_56C "SP800-56C", @ref PKCS5_PBKDF1 "PBKDF1 (PKCS#5),
+* @ref PKCS5_PBKDF2 "PBKDF2 (PKCS#5)"
+* <dt>Password Hashing<dd>
+* @ref argon2.h "Argon2", @ref scrypt.h "scrypt", @ref bcrypt.h "bcrypt", @ref passhash9.h "passhash9"
+* <dt>Public Key Cryptosystems<dd>
+* @ref dlies.h "DLIES", @ref ecies.h "ECIES", @ref elgamal.h "ElGamal"
+* @ref rsa.h "RSA", @ref newhope.h "NewHope", @ref mceliece.h "McEliece" and @ref mceies.h "MCEIES",
+* @ref sm2.h "SM2"
+* <dt>Public Key Signature Schemes<dd>
+* @ref dsa.h "DSA", @ref ecdsa.h "ECDSA", @ref ecgdsa.h "ECGDSA", @ref eckcdsa.h "ECKCDSA",
+* @ref gost_3410.h "GOST 34.10-2001", @ref sm2.h "SM2", @ref xmss.h "XMSS"
+* <dt>Key Agreement<dd>
+* @ref dh.h "DH", @ref ecdh.h "ECDH"
+* <dt>Compression<dd>
+* @ref bzip2.h "bzip2", @ref lzma.h "lzma", @ref zlib.h "zlib"
+* <dt>TLS<dd>
+* TLS::Client, TLS::Server, TLS::Policy, TLS::Protocol_Version, TLS::Callbacks, TLS::Ciphersuite,
+* TLS::Session, TLS::Session_Manager, Credentials_Manager
+* <dt>X.509<dd>
+* X509_Certificate, X509_CRL, X509_CA, Certificate_Extension, PKCS10_Request, X509_Cert_Options,
+* Certificate_Store, Certificate_Store_In_SQL, Certificate_Store_In_SQLite
+* </dl>
+*/
+
+using std::uint8_t;
+using std::uint16_t;
+using std::uint32_t;
+using std::uint64_t;
+using std::int32_t;
+using std::int64_t;
+using std::size_t;
+
+/*
+* These typedefs are no longer used within the library headers
+* or code. They are kept only for compatability with software
+* written against older versions.
+*/
+using byte = std::uint8_t;
+using u16bit = std::uint16_t;
+using u32bit = std::uint32_t;
+using u64bit = std::uint64_t;
+using s32bit = std::int32_t;
+
+#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
+
+/*
+* Should this assert fail on your system please contact the developers
+* for assistance in porting.
+*/
+static_assert(sizeof(std::size_t) == 8 || sizeof(std::size_t) == 4,
+ "This platform has an unexpected size for size_t");
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/utils/uuid/info.txt b/comm/third_party/botan/src/lib/utils/uuid/info.txt
new file mode 100644
index 0000000000..b078146e8a
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/uuid/info.txt
@@ -0,0 +1,8 @@
+<defines>
+UUID -> 20180930
+</defines>
+
+<requires>
+rng
+hex
+</requires>
diff --git a/comm/third_party/botan/src/lib/utils/uuid/uuid.cpp b/comm/third_party/botan/src/lib/utils/uuid/uuid.cpp
new file mode 100644
index 0000000000..c81cae6657
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/uuid/uuid.cpp
@@ -0,0 +1,82 @@
+/*
+* UUID type
+* (C) 2015,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/uuid.h>
+#include <botan/rng.h>
+#include <botan/hex.h>
+#include <sstream>
+
+namespace Botan {
+
+UUID::UUID(RandomNumberGenerator& rng)
+ {
+ m_uuid.resize(16);
+ rng.randomize(m_uuid.data(), m_uuid.size());
+
+ // Mark as a random v4 UUID (RFC 4122 sec 4.4)
+ m_uuid[6] = 0x40 | (m_uuid[6] & 0x0F);
+
+ // Set reserved bits
+ m_uuid[8] = 0x80 | (m_uuid[8] & 0x3F);
+ }
+
+UUID::UUID(const std::vector<uint8_t>& blob)
+ {
+ if(blob.size() != 16)
+ {
+ throw Invalid_Argument("Bad UUID blob " + hex_encode(blob));
+ }
+
+ m_uuid = blob;
+ }
+
+UUID::UUID(const std::string& uuid_str)
+ {
+ if(uuid_str.size() != 36 ||
+ uuid_str[8] != '-' ||
+ uuid_str[13] != '-' ||
+ uuid_str[18] != '-' ||
+ uuid_str[23] != '-')
+ {
+ throw Invalid_Argument("Bad UUID '" + uuid_str + "'");
+ }
+
+ std::string just_hex;
+ for(size_t i = 0; i != uuid_str.size(); ++i)
+ {
+ char c = uuid_str[i];
+
+ if(c == '-')
+ continue;
+
+ just_hex += c;
+ }
+
+ m_uuid = hex_decode(just_hex);
+
+ if(m_uuid.size() != 16)
+ {
+ throw Invalid_Argument("Bad UUID '" + uuid_str + "'");
+ }
+ }
+
+std::string UUID::to_string() const
+ {
+ if(is_valid() == false)
+ throw Invalid_State("UUID object is empty cannot convert to string");
+
+ std::string h = hex_encode(m_uuid);
+
+ h.insert(8, "-");
+ h.insert(13, "-");
+ h.insert(18, "-");
+ h.insert(23, "-");
+
+ return h;
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/utils/uuid/uuid.h b/comm/third_party/botan/src/lib/utils/uuid/uuid.h
new file mode 100644
index 0000000000..8f95f4d672
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/uuid/uuid.h
@@ -0,0 +1,69 @@
+/*
+* UUID type
+* (C) 2015,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_UUID_H_
+#define BOTAN_UUID_H_
+
+#include <botan/types.h>
+#include <vector>
+#include <string>
+
+BOTAN_FUTURE_INTERNAL_HEADER(uuid.h)
+
+namespace Botan {
+
+class RandomNumberGenerator;
+
+class BOTAN_UNSTABLE_API UUID final
+ {
+ public:
+ /**
+ * Create an uninitialized UUID object
+ */
+ UUID() : m_uuid() {}
+
+ /**
+ * Create a random UUID
+ */
+ UUID(RandomNumberGenerator& rng);
+
+ /**
+ * Load a UUID from a 16 byte vector
+ */
+ UUID(const std::vector<uint8_t>& blob);
+
+ UUID& operator=(const UUID& other) = default;
+ UUID(const UUID& other) = default;
+
+ /**
+ * Decode a UUID string
+ */
+ UUID(const std::string& uuid_str);
+
+ /**
+ * Convert the UUID to a string
+ */
+ std::string to_string() const;
+
+ const std::vector<uint8_t>& binary_value() const { return m_uuid; }
+
+ bool operator==(const UUID& other) const
+ {
+ return m_uuid == other.m_uuid;
+ }
+
+ bool operator!=(const UUID& other) const { return !(*this == other); }
+
+ bool is_valid() const { return m_uuid.size() == 16; }
+
+ private:
+ std::vector<uint8_t> m_uuid;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/utils/version.cpp b/comm/third_party/botan/src/lib/utils/version.cpp
new file mode 100644
index 0000000000..aa82d7cc95
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/version.cpp
@@ -0,0 +1,100 @@
+/*
+* Version Information
+* (C) 1999-2013,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/version.h>
+#include <sstream>
+
+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.
+*/
+
+#define QUOTE(name) #name
+#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
+ ;
+ }
+
+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)
+ "UNSAFE FUZZER MODE 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 ")";
+ }
+
+#undef STR
+#undef QUOTE
+
+/*
+* Return the version as a string
+*/
+std::string version_string()
+ {
+ return std::string(version_cstr());
+ }
+
+std::string short_version_string()
+ {
+ return std::string(short_version_cstr());
+ }
+
+uint32_t version_datestamp() { return BOTAN_VERSION_DATESTAMP; }
+
+/*
+* Return parts of the version as integers
+*/
+uint32_t version_major() { return BOTAN_VERSION_MAJOR; }
+uint32_t version_minor() { return BOTAN_VERSION_MINOR; }
+uint32_t version_patch() { return BOTAN_VERSION_PATCH; }
+
+std::string runtime_version_check(uint32_t major,
+ uint32_t minor,
+ uint32_t patch)
+ {
+ if(major != version_major() || minor != version_minor() || patch != version_patch())
+ {
+ std::ostringstream oss;
+ oss << "Warning: linked version (" << short_version_string() << ")"
+ << " does not match version built against "
+ << "(" << major << '.' << minor << '.' << patch << ")\n";
+ return oss.str();
+ }
+
+ return "";
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/utils/version.h b/comm/third_party/botan/src/lib/utils/version.h
new file mode 100644
index 0000000000..fe59de625c
--- /dev/null
+++ b/comm/third_party/botan/src/lib/utils/version.h
@@ -0,0 +1,101 @@
+/*
+* Version Information
+* (C) 1999-2011,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_VERSION_H_
+#define BOTAN_VERSION_H_
+
+#include <botan/types.h>
+#include <string>
+
+namespace Botan {
+
+/*
+* Get information describing the version
+*/
+
+/**
+* Get a human-readable string identifying the version of Botan.
+* No particular format should be assumed.
+* @return version string
+*/
+BOTAN_PUBLIC_API(2,0) std::string version_string();
+
+/**
+* Same as version_string() except returning a pointer to a statically
+* allocated string.
+* @return version string
+*/
+BOTAN_PUBLIC_API(2,0) const char* version_cstr();
+
+/**
+* Return a version string of the form "MAJOR.MINOR.PATCH" where
+* each of the values is an integer.
+*/
+BOTAN_PUBLIC_API(2,4) std::string short_version_string();
+
+/**
+* Same as version_short_string except returning a pointer to the string.
+*/
+BOTAN_PUBLIC_API(2,4) const char* short_version_cstr();
+
+/**
+* Return the date this version of botan was released, in an integer of
+* the form YYYYMMDD. For instance a version released on May 21, 2013
+* would return the integer 20130521. If the currently running version
+* is not an official release, this function will return 0 instead.
+*
+* @return release date, or zero if unreleased
+*/
+BOTAN_PUBLIC_API(2,0) uint32_t version_datestamp();
+
+/**
+* Get the major version number.
+* @return major version number
+*/
+BOTAN_PUBLIC_API(2,0) uint32_t version_major();
+
+/**
+* Get the minor version number.
+* @return minor version number
+*/
+BOTAN_PUBLIC_API(2,0) uint32_t version_minor();
+
+/**
+* Get the patch number.
+* @return patch number
+*/
+BOTAN_PUBLIC_API(2,0) uint32_t version_patch();
+
+/**
+* Usable for checking that the DLL version loaded at runtime exactly
+* matches the compile-time version. Call using BOTAN_VERSION_* macro
+* values. Returns the empty string if an exact match, otherwise an
+* appropriate message. Added with 1.11.26.
+*/
+BOTAN_PUBLIC_API(2,0) std::string
+runtime_version_check(uint32_t major,
+ uint32_t minor,
+ uint32_t patch);
+
+/*
+* Macros for compile-time version checks
+*/
+#define BOTAN_VERSION_CODE_FOR(a,b,c) ((a << 16) | (b << 8) | (c))
+
+/**
+* Compare using BOTAN_VERSION_CODE_FOR, as in
+* # if BOTAN_VERSION_CODE < BOTAN_VERSION_CODE_FOR(1,8,0)
+* # error "Botan version too old"
+* # endif
+*/
+#define BOTAN_VERSION_CODE BOTAN_VERSION_CODE_FOR(BOTAN_VERSION_MAJOR, \
+ BOTAN_VERSION_MINOR, \
+ BOTAN_VERSION_PATCH)
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/x509/asn1_alt_name.cpp b/comm/third_party/botan/src/lib/x509/asn1_alt_name.cpp
new file mode 100644
index 0000000000..a347a3114c
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/asn1_alt_name.cpp
@@ -0,0 +1,265 @@
+/*
+* AlternativeName
+* (C) 1999-2007 Jack Lloyd
+* 2007 Yves Jerschow
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/pkix_types.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
+#include <botan/oids.h>
+#include <botan/internal/stl_util.h>
+#include <botan/parsing.h>
+#include <botan/loadstor.h>
+
+#include <sstream>
+
+namespace Botan {
+
+/*
+* Create an AlternativeName
+*/
+AlternativeName::AlternativeName(const std::string& email_addr,
+ const std::string& uri,
+ const std::string& dns,
+ const std::string& ip)
+ {
+ add_attribute("RFC822", email_addr);
+ add_attribute("DNS", dns);
+ add_attribute("URI", uri);
+ add_attribute("IP", ip);
+ }
+
+/*
+* Add an attribute to an alternative name
+*/
+void AlternativeName::add_attribute(const std::string& type,
+ const std::string& value)
+ {
+ if(type.empty() || value.empty())
+ return;
+
+ auto range = m_alt_info.equal_range(type);
+ for(auto j = range.first; j != range.second; ++j)
+ if(j->second == value)
+ return;
+
+ multimap_insert(m_alt_info, type, value);
+ }
+
+/*
+* Add an OtherName field
+*/
+void AlternativeName::add_othername(const OID& oid, const std::string& value,
+ ASN1_Tag type)
+ {
+ if(value.empty())
+ return;
+ multimap_insert(m_othernames, oid, ASN1_String(value, type));
+ }
+
+/*
+* Return all of the alternative names
+*/
+std::multimap<std::string, std::string> AlternativeName::contents() const
+ {
+ std::multimap<std::string, std::string> names;
+
+ for(auto i = m_alt_info.begin(); i != m_alt_info.end(); ++i)
+ {
+ multimap_insert(names, i->first, i->second);
+ }
+
+ for(auto i = m_othernames.begin(); i != m_othernames.end(); ++i)
+ {
+ multimap_insert(names, i->first.to_formatted_string(), i->second.value());
+ }
+
+ return names;
+ }
+
+bool AlternativeName::has_field(const std::string& attr) const
+ {
+ auto range = m_alt_info.equal_range(attr);
+ return (range.first != range.second);
+ }
+
+std::string AlternativeName::get_first_attribute(const std::string& attr) const
+ {
+ auto i = m_alt_info.lower_bound(attr);
+ if(i != m_alt_info.end() && i->first == attr)
+ return i->second;
+
+ return "";
+ }
+
+std::vector<std::string> AlternativeName::get_attribute(const std::string& attr) const
+ {
+ std::vector<std::string> results;
+ auto range = m_alt_info.equal_range(attr);
+ for(auto i = range.first; i != range.second; ++i)
+ results.push_back(i->second);
+ return results;
+ }
+
+X509_DN AlternativeName::dn() const
+ {
+ X509_DN dn;
+ auto range = m_alt_info.equal_range("DN");
+
+ for(auto i = range.first; i != range.second; ++i)
+ {
+ std::istringstream strm(i->second);
+ strm >> dn;
+ }
+
+ return dn;
+ }
+
+/*
+* Return if this object has anything useful
+*/
+bool AlternativeName::has_items() const
+ {
+ return (m_alt_info.size() > 0 || m_othernames.size() > 0);
+ }
+
+namespace {
+
+/*
+* DER encode an AlternativeName entry
+*/
+void encode_entries(DER_Encoder& encoder,
+ const std::multimap<std::string, std::string>& attr,
+ const std::string& type, ASN1_Tag tagging)
+ {
+ auto range = attr.equal_range(type);
+
+ for(auto i = range.first; i != range.second; ++i)
+ {
+ if(type == "RFC822" || type == "DNS" || type == "URI")
+ {
+ ASN1_String asn1_string(i->second, IA5_STRING);
+ encoder.add_object(tagging, CONTEXT_SPECIFIC, asn1_string.value());
+ }
+ else if(type == "IP")
+ {
+ const uint32_t ip = string_to_ipv4(i->second);
+ uint8_t ip_buf[4] = { 0 };
+ store_be(ip, ip_buf);
+ encoder.add_object(tagging, CONTEXT_SPECIFIC, ip_buf, 4);
+ }
+ else if (type == "DN")
+ {
+ std::stringstream ss(i->second);
+ X509_DN dn;
+ ss >> dn;
+ encoder.encode(dn);
+ }
+ }
+ }
+
+}
+
+/*
+* DER encode an AlternativeName extension
+*/
+void AlternativeName::encode_into(DER_Encoder& der) const
+ {
+ der.start_cons(SEQUENCE);
+
+ encode_entries(der, m_alt_info, "RFC822", ASN1_Tag(1));
+ encode_entries(der, m_alt_info, "DNS", ASN1_Tag(2));
+ encode_entries(der, m_alt_info, "DN", ASN1_Tag(4));
+ encode_entries(der, m_alt_info, "URI", ASN1_Tag(6));
+ encode_entries(der, m_alt_info, "IP", ASN1_Tag(7));
+
+ for(auto i = m_othernames.begin(); i != m_othernames.end(); ++i)
+ {
+ der.start_explicit(0)
+ .encode(i->first)
+ .start_explicit(0)
+ .encode(i->second)
+ .end_explicit()
+ .end_explicit();
+ }
+
+ der.end_cons();
+ }
+
+/*
+* Decode a BER encoded AlternativeName
+*/
+void AlternativeName::decode_from(BER_Decoder& source)
+ {
+ BER_Decoder names = source.start_cons(SEQUENCE);
+
+ // FIXME this is largely a duplication of GeneralName::decode_from
+
+ while(names.more_items())
+ {
+ BER_Object obj = names.get_next_object();
+
+ if(obj.is_a(0, CONTEXT_SPECIFIC))
+ {
+ BER_Decoder othername(obj);
+
+ OID oid;
+ othername.decode(oid);
+ if(othername.more_items())
+ {
+ BER_Object othername_value_outer = othername.get_next_object();
+ othername.verify_end();
+
+ if(othername_value_outer.is_a(0, ASN1_Tag(CONTEXT_SPECIFIC | CONSTRUCTED)) == false)
+ throw Decoding_Error("Invalid tags on otherName value");
+
+ BER_Decoder othername_value_inner(othername_value_outer);
+
+ 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() == UNIVERSAL)
+ {
+ add_othername(oid, ASN1::to_string(value), value.type());
+ }
+ }
+ }
+ if(obj.is_a(1, CONTEXT_SPECIFIC))
+ {
+ add_attribute("RFC822", ASN1::to_string(obj));
+ }
+ else if(obj.is_a(2, CONTEXT_SPECIFIC))
+ {
+ add_attribute("DNS", ASN1::to_string(obj));
+ }
+ else if(obj.is_a(6, CONTEXT_SPECIFIC))
+ {
+ add_attribute("URI", ASN1::to_string(obj));
+ }
+ else if(obj.is_a(4, ASN1_Tag(CONTEXT_SPECIFIC | CONSTRUCTED)))
+ {
+ BER_Decoder dec(obj);
+ X509_DN dn;
+ std::stringstream ss;
+
+ dec.decode(dn);
+ ss << dn;
+
+ add_attribute("DN", ss.str());
+ }
+ else if(obj.is_a(7, CONTEXT_SPECIFIC))
+ {
+ if(obj.length() == 4)
+ {
+ const uint32_t ip = load_be<uint32_t>(obj.bits(), 0);
+ add_attribute("IP", ipv4_to_string(ip));
+ }
+ }
+
+ }
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/x509/asn1_alt_name.h b/comm/third_party/botan/src/lib/x509/asn1_alt_name.h
new file mode 100644
index 0000000000..b0ca1d87e2
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/asn1_alt_name.h
@@ -0,0 +1,11 @@
+/*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_X509_ALT_NAME_H_
+#define BOTAN_X509_ALT_NAME_H_
+
+#include <botan/pkix_types.h>
+BOTAN_DEPRECATED_HEADER(asn1_alt_name.h)
+
+#endif
diff --git a/comm/third_party/botan/src/lib/x509/asn1_attribute.h b/comm/third_party/botan/src/lib/x509/asn1_attribute.h
new file mode 100644
index 0000000000..0139c18c70
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/asn1_attribute.h
@@ -0,0 +1,11 @@
+/*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_ASN1_ATTRIBUTE_H_
+#define BOTAN_ASN1_ATTRIBUTE_H_
+
+#include <botan/pkix_types.h>
+BOTAN_DEPRECATED_HEADER(asn1_attribute.h)
+
+#endif
diff --git a/comm/third_party/botan/src/lib/x509/cert_status.cpp b/comm/third_party/botan/src/lib/x509/cert_status.cpp
new file mode 100644
index 0000000000..e22d6e1be1
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/cert_status.cpp
@@ -0,0 +1,125 @@
+/*
+* (C) 2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/pkix_enums.h>
+
+namespace Botan {
+
+const char* to_string(Certificate_Status_Code code)
+ {
+ switch(code)
+ {
+ case Certificate_Status_Code::VERIFIED:
+ return "Verified";
+ case Certificate_Status_Code::OCSP_RESPONSE_GOOD:
+ return "OCSP response accepted as affirming unrevoked status for certificate";
+ case Certificate_Status_Code::OCSP_SIGNATURE_OK:
+ return "Signature on OCSP response was found valid";
+ case Certificate_Status_Code::VALID_CRL_CHECKED:
+ return "Valid CRL examined";
+
+ case Certificate_Status_Code::CERT_SERIAL_NEGATIVE:
+ return "Certificate serial number is negative";
+ case Certificate_Status_Code::DN_TOO_LONG:
+ return "Distinguished name too long";
+ case Certificate_Status_Code::OCSP_NO_REVOCATION_URL:
+ return "OCSP URL not available";
+ case Certificate_Status_Code::OCSP_SERVER_NOT_AVAILABLE:
+ return "OCSP server not available";
+
+ case Certificate_Status_Code::NO_REVOCATION_DATA:
+ return "No revocation data";
+ case Certificate_Status_Code::SIGNATURE_METHOD_TOO_WEAK:
+ return "Signature method too weak";
+ case Certificate_Status_Code::UNTRUSTED_HASH:
+ return "Hash function used is considered too weak for security";
+
+ case Certificate_Status_Code::CERT_NOT_YET_VALID:
+ return "Certificate is not yet valid";
+ case Certificate_Status_Code::CERT_HAS_EXPIRED:
+ return "Certificate has expired";
+ case Certificate_Status_Code::OCSP_NOT_YET_VALID:
+ return "OCSP is not yet valid";
+ case Certificate_Status_Code::OCSP_HAS_EXPIRED:
+ return "OCSP response has expired";
+ case Certificate_Status_Code::OCSP_IS_TOO_OLD:
+ return "OCSP response is too old";
+ case Certificate_Status_Code::CRL_NOT_YET_VALID:
+ return "CRL response is not yet valid";
+ case Certificate_Status_Code::CRL_HAS_EXPIRED:
+ return "CRL has expired";
+
+ case Certificate_Status_Code::CERT_ISSUER_NOT_FOUND:
+ return "Certificate issuer not found";
+ case Certificate_Status_Code::CANNOT_ESTABLISH_TRUST:
+ return "Cannot establish trust";
+ case Certificate_Status_Code::CERT_CHAIN_LOOP:
+ return "Loop in certificate chain";
+ case Certificate_Status_Code::CHAIN_LACKS_TRUST_ROOT:
+ return "Certificate chain does not end in a CA certificate";
+ case Certificate_Status_Code::CHAIN_NAME_MISMATCH:
+ return "Certificate issuer does not match subject of issuing cert";
+
+ case Certificate_Status_Code::POLICY_ERROR:
+ return "Certificate policy error";
+ case Certificate_Status_Code::DUPLICATE_CERT_POLICY:
+ return "Certificate contains duplicate policy";
+ case Certificate_Status_Code::INVALID_USAGE:
+ return "Certificate does not allow the requested usage";
+ case Certificate_Status_Code::CERT_CHAIN_TOO_LONG:
+ return "Certificate chain too long";
+ case Certificate_Status_Code::CA_CERT_NOT_FOR_CERT_ISSUER:
+ return "CA certificate not allowed to issue certs";
+ case Certificate_Status_Code::CA_CERT_NOT_FOR_CRL_ISSUER:
+ return "CA certificate not allowed to issue CRLs";
+ case Certificate_Status_Code::NO_MATCHING_CRLDP:
+ return "No CRL with matching distribution point for certificate";
+ case Certificate_Status_Code::OCSP_CERT_NOT_LISTED:
+ return "OCSP cert not listed";
+ case Certificate_Status_Code::OCSP_BAD_STATUS:
+ return "OCSP bad status";
+ case Certificate_Status_Code::CERT_NAME_NOMATCH:
+ 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::UNKNOWN_CRITICAL_EXTENSION:
+ return "Unknown critical extension encountered";
+ case Certificate_Status_Code::DUPLICATE_CERT_EXTENSION:
+ return "Duplicate certificate extension encountered";
+ case Certificate_Status_Code::EXT_IN_V1_V2_CERT:
+ return "Encountered extension in certificate with version that does not allow it";
+ case Certificate_Status_Code::V2_IDENTIFIERS_IN_V1_CERT:
+ return "Encountered v2 identifiers in v1 certificate";
+ 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";
+ 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";
+ case Certificate_Status_Code::OCSP_NO_HTTP:
+ return "OCSP requests not available, no HTTP support compiled in";
+ case Certificate_Status_Code::CERT_IS_REVOKED:
+ return "Certificate is revoked";
+ case Certificate_Status_Code::CRL_BAD_SIGNATURE:
+ return "CRL bad signature";
+ case Certificate_Status_Code::SIGNATURE_ERROR:
+ return "Signature error";
+ case Certificate_Status_Code::CERT_PUBKEY_INVALID:
+ return "Certificate public key invalid";
+ case Certificate_Status_Code::SIGNATURE_ALGO_UNKNOWN:
+ return "Certificate signed with unknown/unavailable algorithm";
+ case Certificate_Status_Code::SIGNATURE_ALGO_BAD_PARAMS:
+ return "Certificate signature has invalid parameters";
+
+ // intentionally no default so we are warned if new enum values are added
+ }
+
+ return nullptr;
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/x509/cert_status.h b/comm/third_party/botan/src/lib/x509/cert_status.h
new file mode 100644
index 0000000000..f330ff348f
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/cert_status.h
@@ -0,0 +1,11 @@
+/*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_X509_PATH_RESULT_H_
+#define BOTAN_X509_PATH_RESULT_H_
+
+#include <botan/pkix_enums.h>
+BOTAN_DEPRECATED_HEADER(cert_status.h)
+
+#endif
diff --git a/comm/third_party/botan/src/lib/x509/certstor.cpp b/comm/third_party/botan/src/lib/x509/certstor.cpp
new file mode 100644
index 0000000000..a066f208d4
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/certstor.cpp
@@ -0,0 +1,233 @@
+/*
+* Certificate Store
+* (C) 1999-2010,2013 Jack Lloyd
+* (C) 2017 Fabian Weissberg, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/certstor.h>
+#include <botan/pkix_types.h>
+#include <botan/internal/filesystem.h>
+#include <botan/hash.h>
+#include <botan/data_src.h>
+
+namespace Botan {
+
+Certificate_Store::~Certificate_Store() {}
+
+std::shared_ptr<const X509_Certificate>
+Certificate_Store::find_cert(const X509_DN& subject_dn, const std::vector<uint8_t>& key_id) const
+ {
+ const auto certs = find_all_certs(subject_dn, key_id);
+
+ if(certs.empty())
+ {
+ return nullptr; // certificate not found
+ }
+
+ // `count` might be greater than 1, but we'll just select the first match
+ return certs.front();
+ }
+
+std::shared_ptr<const X509_CRL> Certificate_Store::find_crl_for(const X509_Certificate&) const
+ {
+ return {};
+ }
+
+void Certificate_Store_In_Memory::add_certificate(const X509_Certificate& cert)
+ {
+ for(const auto& c : m_certs)
+ if(*c == cert)
+ return;
+
+ m_certs.push_back(std::make_shared<const X509_Certificate>(cert));
+ }
+
+void Certificate_Store_In_Memory::add_certificate(std::shared_ptr<const X509_Certificate> cert)
+ {
+ for(const auto& c : m_certs)
+ if(*c == *cert)
+ return;
+
+ m_certs.push_back(cert);
+ }
+
+std::vector<X509_DN> Certificate_Store_In_Memory::all_subjects() const
+ {
+ std::vector<X509_DN> subjects;
+ for(const auto& cert : m_certs)
+ subjects.push_back(cert->subject_dn());
+ return subjects;
+ }
+
+std::shared_ptr<const X509_Certificate>
+Certificate_Store_In_Memory::find_cert(const X509_DN& subject_dn,
+ const std::vector<uint8_t>& key_id) const
+ {
+ for(const auto& cert : m_certs)
+ {
+ // Only compare key ids if set in both call and in the cert
+ if(key_id.size())
+ {
+ std::vector<uint8_t> skid = cert->subject_key_id();
+
+ if(skid.size() && skid != key_id) // no match
+ continue;
+ }
+
+ if(cert->subject_dn() == subject_dn)
+ return cert;
+ }
+
+ return nullptr;
+ }
+
+std::vector<std::shared_ptr<const X509_Certificate>> Certificate_Store_In_Memory::find_all_certs(
+ const X509_DN& subject_dn,
+ const std::vector<uint8_t>& key_id) const
+ {
+ std::vector<std::shared_ptr<const X509_Certificate>> matches;
+
+ for(const auto& cert : m_certs)
+ {
+ if(key_id.size())
+ {
+ std::vector<uint8_t> skid = cert->subject_key_id();
+
+ if(skid.size() && skid != key_id) // no match
+ continue;
+ }
+
+ if(cert->subject_dn() == subject_dn)
+ matches.push_back(cert);
+ }
+
+ return matches;
+ }
+
+std::shared_ptr<const X509_Certificate>
+Certificate_Store_In_Memory::find_cert_by_pubkey_sha1(const std::vector<uint8_t>& key_hash) const
+ {
+ if(key_hash.size() != 20)
+ throw Invalid_Argument("Certificate_Store_In_Memory::find_cert_by_pubkey_sha1 invalid hash");
+
+ std::unique_ptr<HashFunction> 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
+ return cert;
+ }
+
+ return nullptr;
+ }
+
+std::shared_ptr<const X509_Certificate>
+Certificate_Store_In_Memory::find_cert_by_raw_subject_dn_sha256(const std::vector<uint8_t>& subject_hash) const
+ {
+ if(subject_hash.size() != 32)
+ throw Invalid_Argument("Certificate_Store_In_Memory::find_cert_by_raw_subject_dn_sha256 invalid hash");
+
+ std::unique_ptr<HashFunction> hash(HashFunction::create("SHA-256"));
+
+ 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
+ return cert;
+ }
+
+ return nullptr;
+ }
+
+void Certificate_Store_In_Memory::add_crl(const X509_CRL& crl)
+ {
+ std::shared_ptr<const X509_CRL> crl_s = std::make_shared<const X509_CRL>(crl);
+ return add_crl(crl_s);
+ }
+
+void Certificate_Store_In_Memory::add_crl(std::shared_ptr<const X509_CRL> crl)
+ {
+ X509_DN crl_issuer = crl->issuer_dn();
+
+ for(auto& c : m_crls)
+ {
+ // 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;
+ }
+ }
+
+ // Totally new CRL, add to the list
+ m_crls.push_back(crl);
+ }
+
+std::shared_ptr<const X509_CRL> Certificate_Store_In_Memory::find_crl_for(const X509_Certificate& subject) const
+ {
+ const std::vector<uint8_t>& 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.size())
+ {
+ std::vector<uint8_t> akid = c->authority_key_id();
+
+ if(akid.size() && akid != key_id) // no match
+ continue;
+ }
+
+ if(c->issuer_dn() == subject.issuer_dn())
+ return c;
+ }
+
+ return {};
+ }
+
+Certificate_Store_In_Memory::Certificate_Store_In_Memory(const X509_Certificate& cert)
+ {
+ add_certificate(cert);
+ }
+
+#if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
+Certificate_Store_In_Memory::Certificate_Store_In_Memory(const std::string& dir)
+ {
+ if(dir.empty())
+ return;
+
+ std::vector<std::string> maybe_certs = get_files_recursive(dir);
+
+ if(maybe_certs.empty())
+ {
+ maybe_certs.push_back(dir);
+ }
+
+ for(auto&& cert_file : maybe_certs)
+ {
+ try
+ {
+ DataSource_Stream src(cert_file, true);
+ while(!src.end_of_data())
+ {
+ try
+ {
+ m_certs.push_back(std::make_shared<X509_Certificate>(src));
+ }
+ catch(std::exception&)
+ {
+ // stop searching for other certificate at first exception
+ break;
+ }
+ }
+ }
+ catch(std::exception&)
+ {
+ }
+ }
+ }
+#endif
+
+}
diff --git a/comm/third_party/botan/src/lib/x509/certstor.h b/comm/third_party/botan/src/lib/x509/certstor.h
new file mode 100644
index 0000000000..6901589d26
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/certstor.h
@@ -0,0 +1,165 @@
+/*
+* Certificate Store
+* (C) 1999-2010,2013 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_CERT_STORE_H_
+#define BOTAN_CERT_STORE_H_
+
+#include <botan/x509cert.h>
+#include <botan/x509_crl.h>
+
+namespace Botan {
+
+/**
+* Certificate Store Interface
+*/
+class BOTAN_PUBLIC_API(2,0) Certificate_Store
+ {
+ public:
+ virtual ~Certificate_Store();
+
+ /**
+ * Find a certificate by Subject DN and (optionally) key identifier
+ * @param subject_dn the subject's distinguished name
+ * @param key_id an optional key id
+ * @return a matching certificate or nullptr otherwise
+ * If more than one certificate in the certificate store matches, then
+ * a single value is selected arbitrarily.
+ */
+ virtual std::shared_ptr<const X509_Certificate>
+ find_cert(const X509_DN& subject_dn, const std::vector<uint8_t>& key_id) const;
+
+ /**
+ * Find all certificates with a given Subject DN.
+ * Subject DN and even the key identifier might not be unique.
+ */
+ virtual std::vector<std::shared_ptr<const X509_Certificate>> find_all_certs(
+ const X509_DN& subject_dn, const std::vector<uint8_t>& key_id) const = 0;
+
+
+ /**
+ * Find a certificate by searching for one with a matching SHA-1 hash of
+ * public key. Used for OCSP.
+ * @param key_hash SHA-1 hash of the subject's public key
+ * @return a matching certificate or nullptr otherwise
+ */
+ virtual std::shared_ptr<const X509_Certificate>
+ find_cert_by_pubkey_sha1(const std::vector<uint8_t>& key_hash) const = 0;
+
+ /**
+ * Find a certificate by searching for one with a matching SHA-256 hash of
+ * raw subject name. Used for OCSP.
+ * @param subject_hash SHA-256 hash of the subject's raw name
+ * @return a matching certificate or nullptr otherwise
+ */
+ virtual std::shared_ptr<const X509_Certificate>
+ find_cert_by_raw_subject_dn_sha256(const std::vector<uint8_t>& subject_hash) const = 0;
+
+ /**
+ * Finds a CRL for the given certificate
+ * @param subject the subject certificate
+ * @return the CRL for subject or nullptr otherwise
+ */
+ virtual std::shared_ptr<const X509_CRL> find_crl_for(const X509_Certificate& subject) const;
+
+ /**
+ * @return whether the certificate is known
+ * @param cert certififcate to be searched
+ */
+ bool certificate_known(const X509_Certificate& cert) const
+ {
+ return find_cert(cert.subject_dn(), cert.subject_key_id()) != nullptr;
+ }
+
+ // remove this (used by TLS::Server)
+ virtual std::vector<X509_DN> all_subjects() const = 0;
+ };
+
+/**
+* In Memory Certificate Store
+*/
+class BOTAN_PUBLIC_API(2,0) Certificate_Store_In_Memory final : public Certificate_Store
+ {
+ public:
+ /**
+ * Attempt to parse all files in dir (including subdirectories)
+ * as certificates. Ignores errors.
+ */
+ explicit Certificate_Store_In_Memory(const std::string& dir);
+
+ /**
+ * Adds given certificate to the store.
+ */
+ explicit Certificate_Store_In_Memory(const X509_Certificate& cert);
+
+ /**
+ * Create an empty store.
+ */
+ Certificate_Store_In_Memory() = default;
+
+ /**
+ * Add a certificate to the store.
+ * @param cert certificate to be added
+ */
+ void add_certificate(const X509_Certificate& cert);
+
+ /**
+ * Add a certificate already in a shared_ptr to the store.
+ * @param cert certificate to be added
+ */
+ void add_certificate(std::shared_ptr<const X509_Certificate> cert);
+
+ /**
+ * Add a certificate revocation list (CRL) to the store.
+ * @param crl CRL to be added
+ */
+ void add_crl(const X509_CRL& crl);
+
+ /**
+ * Add a certificate revocation list (CRL) to the store as a shared_ptr
+ * @param crl CRL to be added
+ */
+ void add_crl(std::shared_ptr<const X509_CRL> crl);
+
+ /**
+ * @return DNs for all certificates managed by the store
+ */
+ std::vector<X509_DN> all_subjects() const override;
+
+ /*
+ * Find a certificate by Subject DN and (optionally) key identifier
+ * @return the first certificate that matches
+ */
+ std::shared_ptr<const X509_Certificate> find_cert(
+ const X509_DN& subject_dn,
+ const std::vector<uint8_t>& key_id) const override;
+
+ /*
+ * Find all certificates with a given Subject DN.
+ * Subject DN and even the key identifier might not be unique.
+ */
+ std::vector<std::shared_ptr<const X509_Certificate>> find_all_certs(
+ const X509_DN& subject_dn, const std::vector<uint8_t>& key_id) const override;
+
+ std::shared_ptr<const X509_Certificate>
+ find_cert_by_pubkey_sha1(const std::vector<uint8_t>& key_hash) const override;
+
+ std::shared_ptr<const X509_Certificate>
+ find_cert_by_raw_subject_dn_sha256(const std::vector<uint8_t>& subject_hash) const override;
+
+ /**
+ * Finds a CRL for the given certificate
+ */
+ std::shared_ptr<const X509_CRL> find_crl_for(const X509_Certificate& subject) const override;
+ private:
+ // TODO: Add indexing on the DN and key id to avoid linear search
+ std::vector<std::shared_ptr<const X509_Certificate>> m_certs;
+ std::vector<std::shared_ptr<const X509_CRL>> m_crls;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/x509/certstor_flatfile/certstor_flatfile.cpp b/comm/third_party/botan/src/lib/x509/certstor_flatfile/certstor_flatfile.cpp
new file mode 100644
index 0000000000..9804759b96
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/certstor_flatfile/certstor_flatfile.cpp
@@ -0,0 +1,148 @@
+/*
+* Certificate Store
+* (C) 1999-2019 Jack Lloyd
+* (C) 2019 Patrick Schmidt
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/certstor_flatfile.h>
+#include <botan/pkix_types.h>
+#include <botan/data_src.h>
+#include <botan/pem.h>
+#include <stdexcept>
+
+namespace Botan {
+namespace {
+std::vector<std::vector<uint8_t>> decode_all_certificates(DataSource& source)
+ {
+ std::vector<std::vector<uint8_t>> pems;
+
+ while(!source.end_of_data())
+ {
+ std::string label;
+ std::vector<uint8_t> cert;
+ try
+ {
+ cert = unlock(PEM_Code::decode(source, label));
+
+ if(label == "CERTIFICATE" || label == "X509 CERTIFICATE" || label == "TRUSTED CERTIFICATE")
+ {
+ pems.push_back(cert);
+ }
+ }
+ catch(const Decoding_Error&) {}
+ }
+
+ return pems;
+ }
+}
+
+Flatfile_Certificate_Store::Flatfile_Certificate_Store(const std::string& file, bool ignore_non_ca)
+ {
+ if(file.empty())
+ {
+ throw Invalid_Argument("Flatfile_Certificate_Store::Flatfile_Certificate_Store invalid file path");
+ }
+
+ DataSource_Stream file_stream(file);
+
+ for(const std::vector<uint8_t>& der : decode_all_certificates(file_stream))
+ {
+ std::shared_ptr<const X509_Certificate> cert = std::make_shared<const X509_Certificate>(der.data(), der.size());
+
+ /*
+ * Various weird or misconfigured system roots include intermediate certificates,
+ * or even stranger certificates which are not valid for cert issuance at all.
+ * Previously this code would error on such cases as an obvious misconfiguration,
+ * 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);
+ }
+ else if(!ignore_non_ca)
+ {
+ throw Invalid_Argument("Flatfile_Certificate_Store received non CA cert " + cert->subject_dn().to_string());
+ }
+ }
+
+ if(m_all_subjects.empty())
+ {
+ throw Invalid_Argument("Flatfile_Certificate_Store::Flatfile_Certificate_Store cert file is empty");
+ }
+ }
+
+std::vector<X509_DN> Flatfile_Certificate_Store::all_subjects() const
+ {
+ return m_all_subjects;
+ }
+
+std::vector<std::shared_ptr<const X509_Certificate>> Flatfile_Certificate_Store::find_all_certs(
+ const X509_DN& subject_dn,
+ const std::vector<uint8_t>& key_id) const
+ {
+ std::vector<std::shared_ptr<const X509_Certificate>> found_certs;
+ try
+ {
+ const auto certs = m_dn_to_cert.at(subject_dn);
+
+ for(auto cert : certs)
+ {
+ if(key_id.empty() || key_id == cert->subject_key_id())
+ {
+ found_certs.push_back(cert);
+ }
+ }
+ }
+ catch(const std::out_of_range&)
+ {
+ return {};
+ }
+
+ return found_certs;
+ }
+
+std::shared_ptr<const X509_Certificate>
+Flatfile_Certificate_Store::find_cert_by_pubkey_sha1(const std::vector<uint8_t>& key_hash) const
+ {
+ if(key_hash.size() != 20)
+ {
+ throw Invalid_Argument("Flatfile_Certificate_Store::find_cert_by_pubkey_sha1 invalid hash");
+ }
+
+ auto found_cert = m_pubkey_sha1_to_cert.find(key_hash);
+
+ if(found_cert != m_pubkey_sha1_to_cert.end())
+ {
+ return found_cert->second;
+ }
+
+ return nullptr;
+ }
+
+std::shared_ptr<const X509_Certificate>
+Flatfile_Certificate_Store::find_cert_by_raw_subject_dn_sha256(const std::vector<uint8_t>& subject_hash) const
+ {
+ if(subject_hash.size() != 32)
+ { throw Invalid_Argument("Flatfile_Certificate_Store::find_cert_by_raw_subject_dn_sha256 invalid hash"); }
+
+ auto found_cert = m_subject_dn_sha256_to_cert.find(subject_hash);
+
+ if(found_cert != m_subject_dn_sha256_to_cert.end())
+ {
+ return found_cert->second;
+ }
+
+ return nullptr;
+ }
+
+std::shared_ptr<const X509_CRL> Flatfile_Certificate_Store::find_crl_for(const X509_Certificate& subject) const
+ {
+ BOTAN_UNUSED(subject);
+ return {};
+ }
+}
diff --git a/comm/third_party/botan/src/lib/x509/certstor_flatfile/certstor_flatfile.h b/comm/third_party/botan/src/lib/x509/certstor_flatfile/certstor_flatfile.h
new file mode 100644
index 0000000000..1608aaa118
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/certstor_flatfile/certstor_flatfile.h
@@ -0,0 +1,77 @@
+/*
+* Certificate Store
+* (C) 1999-2019 Jack Lloyd
+* (C) 2019 Patrick Schmidt
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_CERT_STORE_FLATFILE_H_
+#define BOTAN_CERT_STORE_FLATFILE_H_
+
+#include <botan/certstor.h>
+
+#include <vector>
+#include <memory>
+#include <map>
+
+namespace Botan {
+/**
+* Certificate Store that is backed by a file of PEMs of trusted CAs.
+*/
+class BOTAN_PUBLIC_API(2, 11) Flatfile_Certificate_Store final : public Certificate_Store
+ {
+ public:
+ /**
+ * Construct a new Certificate_Store given a file path to a file including
+ * PEMs of trusted self-signed CAs.
+ *
+ * @param file the name of the file to read certificates from
+ * @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(const std::string& 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;
+
+ /**
+ * @return DNs for all certificates managed by the store
+ */
+ std::vector<X509_DN> all_subjects() const override;
+
+ /**
+ * Find all certificates with a given Subject DN.
+ * Subject DN and even the key identifier might not be unique.
+ */
+ std::vector<std::shared_ptr<const X509_Certificate>> find_all_certs(
+ const X509_DN& subject_dn, const std::vector<uint8_t>& key_id) const override;
+
+ /**
+ * Find a certificate by searching for one with a matching SHA-1 hash of
+ * public key.
+ * @return a matching certificate or nullptr otherwise
+ */
+ std::shared_ptr<const X509_Certificate>
+ find_cert_by_pubkey_sha1(const std::vector<uint8_t>& key_hash) const override;
+
+ std::shared_ptr<const X509_Certificate>
+ find_cert_by_raw_subject_dn_sha256(const std::vector<uint8_t>& subject_hash) const override;
+
+ /**
+ * Fetching CRLs is not supported by this certificate store. This will
+ * always return an empty list.
+ */
+ std::shared_ptr<const X509_CRL> find_crl_for(const X509_Certificate& subject) const override;
+
+ private:
+ std::vector<X509_DN> m_all_subjects;
+ std::map<X509_DN, std::vector<std::shared_ptr<const X509_Certificate>>> m_dn_to_cert;
+ std::map<std::vector<uint8_t>, std::shared_ptr<const X509_Certificate>> m_pubkey_sha1_to_cert;
+ std::map<std::vector<uint8_t>, std::shared_ptr<const X509_Certificate>> m_subject_dn_sha256_to_cert;
+ };
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/x509/certstor_flatfile/info.txt b/comm/third_party/botan/src/lib/x509/certstor_flatfile/info.txt
new file mode 100644
index 0000000000..902997be73
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/certstor_flatfile/info.txt
@@ -0,0 +1,12 @@
+<defines>
+CERTSTOR_FLATFILE -> 20190410
+</defines>
+
+<os_features>
+filesystem
+</os_features>
+
+<header:public>
+certstor_flatfile.h
+</header:public>
+
diff --git a/comm/third_party/botan/src/lib/x509/certstor_sql/certstor_sql.cpp b/comm/third_party/botan/src/lib/x509/certstor_sql/certstor_sql.cpp
new file mode 100644
index 0000000000..ab4b8e64a7
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/certstor_sql/certstor_sql.cpp
@@ -0,0 +1,335 @@
+/*
+* Certificate Store in SQL
+* (C) 2016 Kai Michaelis, Rohde & Schwarz Cybersecurity
+* (C) 2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/certstor_sql.h>
+#include <botan/pk_keys.h>
+#include <botan/ber_dec.h>
+#include <botan/pkcs8.h>
+#include <botan/data_src.h>
+#include <botan/pkix_types.h>
+
+namespace Botan {
+
+Certificate_Store_In_SQL::Certificate_Store_In_SQL(std::shared_ptr<SQL_Database> db,
+ const std::string& passwd,
+ RandomNumberGenerator& rng,
+ const std::string& table_prefix) :
+ m_rng(rng),
+ m_database(db),
+ m_prefix(table_prefix),
+ m_password(passwd)
+ {
+ m_database->create_table("CREATE TABLE IF NOT EXISTS " +
+ m_prefix + "certificates ( \
+ fingerprint BLOB PRIMARY KEY, \
+ subject_dn BLOB, \
+ key_id BLOB, \
+ priv_fingerprint BLOB, \
+ certificate BLOB UNIQUE NOT NULL\
+ )");
+ m_database->create_table("CREATE TABLE IF NOT EXISTS " + m_prefix + "keys (\
+ fingerprint BLOB PRIMARY KEY, \
+ key BLOB UNIQUE NOT NULL \
+ )");
+ m_database->create_table("CREATE TABLE IF NOT EXISTS " + m_prefix + "revoked (\
+ fingerprint BLOB PRIMARY KEY, \
+ reason BLOB NOT NULL, \
+ time BLOB NOT NULL \
+ )");
+ }
+
+// Certificate handling
+std::shared_ptr<const X509_Certificate>
+Certificate_Store_In_SQL::find_cert(const X509_DN& subject_dn, const std::vector<uint8_t>& key_id) const
+ {
+ std::shared_ptr<SQL_Database::Statement> stmt;
+
+ const std::vector<uint8_t> dn_encoding = subject_dn.BER_encode();
+
+ if(key_id.empty())
+ {
+ stmt = m_database->new_statement("SELECT certificate FROM " + m_prefix + "certificates WHERE subject_dn == ?1 LIMIT 1");
+ stmt->bind(1, dn_encoding);
+ }
+ 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");
+ stmt->bind(1, dn_encoding);
+ stmt->bind(2,key_id);
+ }
+
+ while(stmt->step())
+ {
+ auto blob = stmt->get_blob(0);
+ return std::make_shared<X509_Certificate>(std::vector<uint8_t>(blob.first, blob.first + blob.second));
+ }
+
+ return std::shared_ptr<const X509_Certificate>();
+ }
+
+std::vector<std::shared_ptr<const X509_Certificate>>
+Certificate_Store_In_SQL::find_all_certs(const X509_DN& subject_dn, const std::vector<uint8_t>& key_id) const
+ {
+ std::vector<std::shared_ptr<const X509_Certificate>> certs;
+
+ std::shared_ptr<SQL_Database::Statement> stmt;
+
+ const std::vector<uint8_t> dn_encoding = subject_dn.BER_encode();
+
+ if(key_id.empty())
+ {
+ stmt = m_database->new_statement("SELECT certificate FROM " + m_prefix + "certificates WHERE subject_dn == ?1");
+ stmt->bind(1, dn_encoding);
+ }
+ else
+ {
+ stmt = m_database->new_statement("SELECT certificate FROM " + m_prefix + "certificates WHERE\
+ subject_dn == ?1 AND (key_id == NULL OR key_id == ?2)");
+ stmt->bind(1, dn_encoding);
+ stmt->bind(2, key_id);
+ }
+
+ std::shared_ptr<const X509_Certificate> cert;
+ while(stmt->step())
+ {
+ auto blob = stmt->get_blob(0);
+ certs.push_back(std::make_shared<X509_Certificate>(
+ std::vector<uint8_t>(blob.first,blob.first + blob.second)));
+ }
+
+ return certs;
+ }
+
+std::shared_ptr<const X509_Certificate>
+Certificate_Store_In_SQL::find_cert_by_pubkey_sha1(const std::vector<uint8_t>& /*key_hash*/) const
+ {
+ throw Not_Implemented("Certificate_Store_In_SQL::find_cert_by_pubkey_sha1");
+ }
+
+std::shared_ptr<const X509_Certificate>
+Certificate_Store_In_SQL::find_cert_by_raw_subject_dn_sha256(const std::vector<uint8_t>& /*subject_hash*/) const
+ {
+ throw Not_Implemented("Certificate_Store_In_SQL::find_cert_by_raw_subject_dn_sha256");
+ }
+
+std::shared_ptr<const X509_CRL>
+Certificate_Store_In_SQL::find_crl_for(const X509_Certificate& subject) const
+ {
+ auto all_crls = generate_crls();
+
+ for(auto crl: all_crls)
+ {
+ if(!crl.get_revoked().empty() && crl.issuer_dn() == subject.issuer_dn())
+ return std::shared_ptr<X509_CRL>(new X509_CRL(crl));
+ }
+
+ return std::shared_ptr<X509_CRL>();
+ }
+
+std::vector<X509_DN> Certificate_Store_In_SQL::all_subjects() const
+ {
+ std::vector<X509_DN> ret;
+ auto stmt = m_database->new_statement("SELECT subject_dn FROM " + m_prefix + "certificates");
+
+ while(stmt->step())
+ {
+ auto blob = stmt->get_blob(0);
+ BER_Decoder dec(blob.first,blob.second);
+ X509_DN dn;
+
+ dn.decode_from(dec);
+
+ ret.push_back(dn);
+ }
+
+ return ret;
+ }
+
+bool Certificate_Store_In_SQL::insert_cert(const X509_Certificate& cert)
+ {
+ const std::vector<uint8_t> dn_encoding = cert.subject_dn().BER_encode();
+ const std::vector<uint8_t> cert_encoding = cert.BER_encode();
+
+ auto stmt = m_database->new_statement("INSERT OR REPLACE INTO " +
+ m_prefix + "certificates (\
+ fingerprint, \
+ subject_dn, \
+ key_id, \
+ priv_fingerprint, \
+ certificate \
+ ) VALUES ( ?1, ?2, ?3, ?4, ?5 )");
+
+ stmt->bind(1,cert.fingerprint("SHA-256"));
+ stmt->bind(2,dn_encoding);
+ stmt->bind(3,cert.subject_key_id());
+ stmt->bind(4,std::vector<uint8_t>());
+ stmt->bind(5,cert_encoding);
+ stmt->spin();
+
+ return true;
+ }
+
+
+bool Certificate_Store_In_SQL::remove_cert(const X509_Certificate& cert)
+ {
+ if(!find_cert(cert.subject_dn(),cert.subject_key_id()))
+ return false;
+
+ auto stmt = m_database->new_statement("DELETE FROM " + m_prefix + "certificates WHERE fingerprint == ?1");
+
+ stmt->bind(1,cert.fingerprint("SHA-256"));
+ stmt->spin();
+
+ return true;
+ }
+
+// Private key handling
+std::shared_ptr<const Private_Key> Certificate_Store_In_SQL::find_key(const X509_Certificate& cert) const
+ {
+ auto stmt = m_database->new_statement("SELECT key FROM " + m_prefix + "keys "
+ "JOIN " + m_prefix + "certificates ON " +
+ m_prefix + "keys.fingerprint == " + m_prefix + "certificates.priv_fingerprint "
+ "WHERE " + m_prefix + "certificates.fingerprint == ?1");
+ stmt->bind(1,cert.fingerprint("SHA-256"));
+
+ std::shared_ptr<const Private_Key> key;
+ while(stmt->step())
+ {
+ auto blob = stmt->get_blob(0);
+ DataSource_Memory src(blob.first,blob.second);
+ key.reset(PKCS8::load_key(src, m_rng, m_password));
+ }
+
+ return key;
+ }
+
+std::vector<std::shared_ptr<const X509_Certificate>>
+Certificate_Store_In_SQL::find_certs_for_key(const Private_Key& key) const
+ {
+ auto fpr = 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);
+
+ std::vector<std::shared_ptr<const X509_Certificate>> certs;
+ while(stmt->step())
+ {
+ auto blob = stmt->get_blob(0);
+ certs.push_back(std::make_shared<X509_Certificate>(
+ std::vector<uint8_t>(blob.first,blob.first + blob.second)));
+ }
+
+ return certs;
+ }
+
+bool Certificate_Store_In_SQL::insert_key(const X509_Certificate& cert, const Private_Key& key) {
+ insert_cert(cert);
+
+ if(find_key(cert))
+ return false;
+
+ auto pkcs8 = PKCS8::BER_encode(key, m_rng, m_password);
+ auto fpr = 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(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(2,cert.fingerprint("SHA-256"));
+ stmt2->spin();
+
+ return true;
+ }
+
+void Certificate_Store_In_SQL::remove_key(const Private_Key& key)
+ {
+ auto fpr = key.fingerprint_private("SHA-256");
+ auto stmt = m_database->new_statement("DELETE FROM " + m_prefix + "keys WHERE fingerprint == ?1");
+
+ stmt->bind(1,fpr);
+ stmt->spin();
+ }
+
+// Revocation
+void Certificate_Store_In_SQL::revoke_cert(const X509_Certificate& cert, CRL_Code code, const X509_Time& time)
+ {
+ 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,code);
+
+ if(time.time_is_set())
+ {
+ stmt1->bind(3, time.BER_encode());
+ }
+ else
+ {
+ stmt1->bind(3, static_cast<size_t>(-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");
+
+ stmt->bind(1,cert.fingerprint("SHA-256"));
+ stmt->spin();
+ }
+
+std::vector<X509_CRL> Certificate_Store_In_SQL::generate_crls() const
+ {
+ auto stmt = m_database->new_statement(
+ "SELECT certificate,reason,time FROM " + m_prefix + "revoked "
+ "JOIN " + m_prefix + "certificates ON " +
+ m_prefix + "certificates.fingerprint == " + m_prefix + "revoked.fingerprint");
+
+ std::map<X509_DN,std::vector<CRL_Entry>> crls;
+ while(stmt->step())
+ {
+ auto blob = stmt->get_blob(0);
+ auto cert = X509_Certificate(
+ std::vector<uint8_t>(blob.first,blob.first + blob.second));
+ auto code = static_cast<CRL_Code>(stmt->get_size_t(1));
+ auto ent = CRL_Entry(cert,code);
+
+ auto i = crls.find(cert.issuer_dn());
+ if(i == crls.end())
+ {
+ crls.insert(std::make_pair(cert.issuer_dn(),std::vector<CRL_Entry>({ent})));
+ }
+ else
+ {
+ i->second.push_back(ent);
+ }
+ }
+
+ std::vector<X509_CRL> ret;
+ X509_Time t(std::chrono::system_clock::now());
+
+ for(auto p: crls)
+ {
+ ret.push_back(X509_CRL(p.first,t,t,p.second));
+ }
+
+ return ret;
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/x509/certstor_sql/certstor_sql.h b/comm/third_party/botan/src/lib/x509/certstor_sql/certstor_sql.h
new file mode 100644
index 0000000000..fd80eb1919
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/certstor_sql/certstor_sql.h
@@ -0,0 +1,119 @@
+/*
+* Certificate Store in SQL
+* (C) 2016 Kai Michaelis, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_CERT_STORE_SQL_H_
+#define BOTAN_CERT_STORE_SQL_H_
+
+#include <botan/certstor.h>
+#include <botan/x509cert.h>
+#include <botan/x509_crl.h>
+#include <botan/database.h>
+#include <botan/mutex.h>
+
+namespace Botan {
+
+class Private_Key;
+class RandomNumberGenerator;
+
+/**
+ * Certificate and private key store backed by an SQL database.
+ */
+class BOTAN_PUBLIC_API(2,0) Certificate_Store_In_SQL : public Certificate_Store
+ {
+ public:
+ /**
+ * Create/open a certificate store.
+ * @param db underlying database storage
+ * @param passwd password to encrypt private keys in the database
+ * @param rng used for encrypting keys
+ * @param table_prefix optional prefix for db table names
+ */
+ explicit Certificate_Store_In_SQL(const std::shared_ptr<SQL_Database> db,
+ const std::string& passwd,
+ RandomNumberGenerator& rng,
+ const std::string& table_prefix = "");
+
+ /**
+ * Returns the first certificate with matching subject DN and optional key ID.
+ */
+ std::shared_ptr<const X509_Certificate>
+ find_cert(const X509_DN& subject_dn, const std::vector<uint8_t>& key_id) const override;
+
+ /*
+ * Find all certificates with a given Subject DN.
+ * Subject DN and even the key identifier might not be unique.
+ */
+ std::vector<std::shared_ptr<const X509_Certificate>> find_all_certs(
+ const X509_DN& subject_dn, const std::vector<uint8_t>& key_id) const override;
+
+ std::shared_ptr<const X509_Certificate>
+ find_cert_by_pubkey_sha1(const std::vector<uint8_t>& key_hash) const override;
+
+ std::shared_ptr<const X509_Certificate>
+ find_cert_by_raw_subject_dn_sha256(const std::vector<uint8_t>& subject_hash) const override;
+
+ /**
+ * Returns all subject DNs known to the store instance.
+ */
+ std::vector<X509_DN> all_subjects() const override;
+
+ /**
+ * Inserts "cert" into the store, returns false if the certificate is
+ * already known and true if insertion was successful.
+ */
+ bool insert_cert(const X509_Certificate& cert);
+
+ /**
+ * Removes "cert" from the store. Returns false if the certificate could not
+ * be found and true if removal was successful.
+ */
+ 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<const Private_Key> find_key(const X509_Certificate&) const;
+
+ /// Returns all certificates for private key "key".
+ std::vector<std::shared_ptr<const X509_Certificate>>
+ find_certs_for_key(const Private_Key& key) const;
+
+ /**
+ * Inserts "key" for "cert" into the store, returns false if the key is
+ * already known and true if insertion was successful.
+ */
+ bool insert_key(const X509_Certificate& cert, const Private_Key& key);
+
+ /// Removes "key" from the store.
+ 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());
+
+ /// Reverses the revokation for "cert".
+ void affirm_cert(const X509_Certificate&);
+
+ /**
+ * Generates Certificate Revocation Lists for all certificates marked as revoked.
+ * A CRL is returned for each unique issuer DN.
+ */
+ std::vector<X509_CRL> generate_crls() const;
+
+ /**
+ * Generates a CRL for all certificates issued by the given issuer.
+ */
+ std::shared_ptr<const X509_CRL>
+ find_crl_for(const X509_Certificate& issuer) const override;
+
+ private:
+ RandomNumberGenerator& m_rng;
+ std::shared_ptr<SQL_Database> m_database;
+ std::string m_prefix;
+ std::string m_password;
+ mutex_type m_mutex;
+ };
+
+}
+#endif
diff --git a/comm/third_party/botan/src/lib/x509/certstor_sql/info.txt b/comm/third_party/botan/src/lib/x509/certstor_sql/info.txt
new file mode 100644
index 0000000000..a6046e5d73
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/certstor_sql/info.txt
@@ -0,0 +1,3 @@
+<defines>
+CERTSTOR_SQL -> 20160818
+</defines>
diff --git a/comm/third_party/botan/src/lib/x509/certstor_sqlite3/certstor_sqlite.cpp b/comm/third_party/botan/src/lib/x509/certstor_sqlite3/certstor_sqlite.cpp
new file mode 100644
index 0000000000..b7c066483d
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/certstor_sqlite3/certstor_sqlite.cpp
@@ -0,0 +1,19 @@
+/*
+* Certificate Store in SQL
+* (C) 2016 Kai Michaelis, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/certstor_sqlite.h>
+#include <botan/sqlite3.h>
+
+namespace Botan {
+
+Certificate_Store_In_SQLite::Certificate_Store_In_SQLite(const std::string& db_path,
+ const std::string& passwd,
+ RandomNumberGenerator& rng,
+ const std::string& table_prefix) :
+ Certificate_Store_In_SQL(std::make_shared<Sqlite3_Database>(db_path), passwd, rng, table_prefix)
+ {}
+}
diff --git a/comm/third_party/botan/src/lib/x509/certstor_sqlite3/certstor_sqlite.h b/comm/third_party/botan/src/lib/x509/certstor_sqlite3/certstor_sqlite.h
new file mode 100644
index 0000000000..6d4187e148
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/certstor_sqlite3/certstor_sqlite.h
@@ -0,0 +1,34 @@
+/*
+* Certificate Store in SQL
+* (C) 2016 Kai Michaelis, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_CERT_STORE_SQLITE_H_
+#define BOTAN_CERT_STORE_SQLITE_H_
+
+#include <botan/certstor_sql.h>
+
+namespace Botan {
+
+/**
+* Certificate and private key store backed by an sqlite (https://sqlite.org) database.
+*/
+class BOTAN_PUBLIC_API(2,0) Certificate_Store_In_SQLite final : public Certificate_Store_In_SQL
+ {
+ public:
+ /**
+ * Create/open a certificate store.
+ * @param db_path path to the database file
+ * @param passwd password to encrypt private keys in the database
+ * @param rng used for encrypting keys
+ * @param table_prefix optional prefix for db table names
+ */
+ Certificate_Store_In_SQLite(const std::string& db_path,
+ const std::string& passwd,
+ RandomNumberGenerator& rng,
+ const std::string& table_prefix = "");
+ };
+}
+#endif
diff --git a/comm/third_party/botan/src/lib/x509/certstor_sqlite3/info.txt b/comm/third_party/botan/src/lib/x509/certstor_sqlite3/info.txt
new file mode 100644
index 0000000000..39b8c579b8
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/certstor_sqlite3/info.txt
@@ -0,0 +1,8 @@
+<defines>
+CERTSTOR_SQLITE3 -> 20160818
+</defines>
+
+<requires>
+certstor_sql
+sqlite3
+</requires>
diff --git a/comm/third_party/botan/src/lib/x509/certstor_system/certstor_system.cpp b/comm/third_party/botan/src/lib/x509/certstor_system/certstor_system.cpp
new file mode 100644
index 0000000000..0fca1a9757
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/certstor_system/certstor_system.cpp
@@ -0,0 +1,70 @@
+/*
+* (C) 2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/certstor_system.h>
+#include <botan/pkix_types.h>
+#include <botan/x509cert.h>
+
+#if defined(BOTAN_HAS_CERTSTOR_MACOS)
+ #include <botan/certstor_macos.h>
+#elif defined(BOTAN_HAS_CERTSTOR_WINDOWS)
+ #include <botan/certstor_windows.h>
+#elif defined(BOTAN_HAS_CERTSTOR_FLATFILE) && defined(BOTAN_SYSTEM_CERT_BUNDLE)
+ #include <botan/certstor_flatfile.h>
+#endif
+
+namespace Botan {
+
+System_Certificate_Store::System_Certificate_Store()
+ {
+#if defined(BOTAN_HAS_CERTSTOR_MACOS)
+ m_system_store = std::make_shared<Certificate_Store_MacOS>();
+#elif defined(BOTAN_HAS_CERTSTOR_WINDOWS)
+ m_system_store = std::make_shared<Certificate_Store_Windows>();
+#elif defined(BOTAN_HAS_CERTSTOR_FLATFILE) && defined(BOTAN_SYSTEM_CERT_BUNDLE)
+ m_system_store = std::make_shared<Flatfile_Certificate_Store>(BOTAN_SYSTEM_CERT_BUNDLE, true);
+#else
+ throw Not_Implemented("No system certificate store available in this build");
+#endif
+ }
+
+std::shared_ptr<const X509_Certificate>
+System_Certificate_Store::find_cert(const X509_DN& subject_dn, const std::vector<uint8_t>& key_id) const
+ {
+ return m_system_store->find_cert(subject_dn, key_id);
+ }
+
+std::vector<std::shared_ptr<const X509_Certificate>>
+System_Certificate_Store::find_all_certs(const X509_DN& subject_dn,
+ const std::vector<uint8_t>& key_id) const
+ {
+ return m_system_store->find_all_certs(subject_dn, key_id);
+ }
+
+std::shared_ptr<const X509_Certificate>
+System_Certificate_Store::find_cert_by_pubkey_sha1(const std::vector<uint8_t>& key_hash) const
+ {
+ return m_system_store->find_cert_by_pubkey_sha1(key_hash);
+ }
+
+std::shared_ptr<const X509_Certificate>
+System_Certificate_Store::find_cert_by_raw_subject_dn_sha256(const std::vector<uint8_t>& subject_hash) const
+ {
+ return m_system_store->find_cert_by_raw_subject_dn_sha256(subject_hash);
+ }
+
+std::shared_ptr<const X509_CRL>
+System_Certificate_Store::find_crl_for(const X509_Certificate& subject) const
+ {
+ return m_system_store->find_crl_for(subject);
+ }
+
+std::vector<X509_DN> System_Certificate_Store::all_subjects() const
+ {
+ return m_system_store->all_subjects();
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/x509/certstor_system/certstor_system.h b/comm/third_party/botan/src/lib/x509/certstor_system/certstor_system.h
new file mode 100644
index 0000000000..3a0fc6153a
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/certstor_system/certstor_system.h
@@ -0,0 +1,42 @@
+/*
+* (C) 2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_SYSTEM_CERT_STORE_H_
+#define BOTAN_SYSTEM_CERT_STORE_H_
+
+#include <botan/certstor.h>
+
+namespace Botan {
+
+class BOTAN_PUBLIC_API(2,11) System_Certificate_Store final : public Certificate_Store
+ {
+ public:
+
+ System_Certificate_Store();
+
+ std::shared_ptr<const X509_Certificate>
+ find_cert(const X509_DN& subject_dn, const std::vector<uint8_t>& key_id) const override;
+
+ std::vector<std::shared_ptr<const X509_Certificate>>
+ find_all_certs(const X509_DN& subject_dn, const std::vector<uint8_t>& key_id) const override;
+
+ std::shared_ptr<const X509_Certificate>
+ find_cert_by_pubkey_sha1(const std::vector<uint8_t>& key_hash) const override;
+
+ std::shared_ptr<const X509_Certificate>
+ find_cert_by_raw_subject_dn_sha256(const std::vector<uint8_t>& subject_hash) const override;
+
+ std::shared_ptr<const X509_CRL> find_crl_for(const X509_Certificate& subject) const override;
+
+ std::vector<X509_DN> all_subjects() const override;
+
+ private:
+ std::shared_ptr<Certificate_Store> m_system_store;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/x509/certstor_system/info.txt b/comm/third_party/botan/src/lib/x509/certstor_system/info.txt
new file mode 100644
index 0000000000..c9b3ca73ef
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/certstor_system/info.txt
@@ -0,0 +1,8 @@
+<defines>
+CERTSTOR_SYSTEM -> 20190411
+</defines>
+
+<os_features>
+apple_keychain
+filesystem
+</os_features>
diff --git a/comm/third_party/botan/src/lib/x509/certstor_system_macos/certstor_macos.cpp b/comm/third_party/botan/src/lib/x509/certstor_system_macos/certstor_macos.cpp
new file mode 100644
index 0000000000..1836f3da4b
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/certstor_system_macos/certstor_macos.cpp
@@ -0,0 +1,470 @@
+/*
+* Certificate Store
+* (C) 1999-2019 Jack Lloyd
+* (C) 2019-2020 René Meusel
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <algorithm>
+#include <array>
+
+#include <botan/ber_dec.h>
+#include <botan/certstor_macos.h>
+#include <botan/data_src.h>
+#include <botan/der_enc.h>
+#include <botan/exceptn.h>
+#include <botan/pkix_types.h>
+
+#define __ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORES 0
+#include <CoreFoundation/CoreFoundation.h>
+#include <CoreServices/CoreServices.h>
+
+namespace Botan {
+
+namespace {
+
+/**
+ * Abstract RAII wrapper for CFTypeRef-style object handles
+ * All of those xxxRef types are eventually typedefs to void*
+ */
+template<typename T>
+class scoped_CFType
+ {
+ public:
+ explicit scoped_CFType(T value)
+ : m_value(value)
+ {
+ }
+
+ scoped_CFType(const scoped_CFType<T>& rhs) = delete;
+ scoped_CFType(scoped_CFType<T>&& rhs) :
+ m_value(std::move(rhs.m_value))
+ {
+ rhs.m_value = nullptr;
+ }
+
+ ~scoped_CFType()
+ {
+ if(m_value)
+ {
+ CFRelease(m_value);
+ }
+ }
+
+ operator bool() const { return m_value != nullptr; }
+
+ void assign(T value)
+ {
+ BOTAN_ASSERT(m_value == nullptr, "scoped_CFType was not set yet");
+ m_value = value;
+ }
+
+ T& get() { return m_value; }
+ const T& get() const { return m_value; }
+
+ private:
+ T m_value;
+ };
+
+/**
+ * Apple's DN parser "normalizes" ASN1 'PrintableString' into upper-case values
+ * and strips leading, trailing as well as multiple white spaces.
+ * 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<ASN1_String>(), resp. std::get<OID>()
+ const auto oid = rdn.first;
+ auto str = rdn.second;
+
+ if(str.tagging() == ASN1_Tag::PRINTABLE_STRING)
+ {
+ 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));
+ }
+ else if(!normalized.empty() && normalized.back() != ' ')
+ {
+ // remove leading and squash multiple white spaces
+ normalized.push_back(c);
+ }
+ }
+
+ if(normalized.back() == ' ')
+ {
+ // remove potential remaining single trailing white space char
+ normalized.erase(normalized.end() - 1);
+ }
+
+ str = ASN1_String(normalized, str.tagging());
+ }
+
+ result.add_attribute(oid, str);
+ }
+
+ return result;
+ }
+
+std::vector<uint8_t> normalizeAndSerialize(const X509_DN& dn)
+ {
+ std::vector<uint8_t> result_dn;
+ DER_Encoder encoder(result_dn);
+ normalize(dn).encode_into(encoder);
+ return result_dn;
+ }
+
+std::string to_string(const CFStringRef cfstring)
+ {
+ const char* ccstr = CFStringGetCStringPtr(cfstring, kCFStringEncodingUTF8);
+
+ if(ccstr != nullptr)
+ {
+ return std::string(ccstr);
+ }
+
+ auto utf16_pairs = CFStringGetLength(cfstring);
+ auto max_utf8_bytes = CFStringGetMaximumSizeForEncoding(utf16_pairs, kCFStringEncodingUTF8);
+
+ std::vector<char> cstr(max_utf8_bytes, '\0');
+ auto result = CFStringGetCString(cfstring,
+ cstr.data(), cstr.size(),
+ kCFStringEncodingUTF8);
+
+ return (result) ? std::string(cstr.data()) : std::string();
+ }
+
+std::string to_string(const OSStatus status)
+ {
+ scoped_CFType<CFStringRef> eCFString(
+ SecCopyErrorMessageString(status, nullptr));
+ return to_string(eCFString.get());
+ }
+
+void check_success(const OSStatus status, const std::string context)
+ {
+ if(errSecSuccess == status)
+ {
+ return;
+ }
+
+ throw Internal_Error(
+ std::string("failed to " + context + ": " + to_string(status)));
+ }
+
+template <typename T>
+void check_notnull(const T& value, const std::string context)
+ {
+ if(value)
+ {
+ return;
+ }
+
+ throw Internal_Error(std::string("failed to ") + context);
+ }
+
+} // namespace
+
+/**
+ * Internal class implementation (i.e. Pimpl) to keep the required platform-
+ * dependent members of Certificate_Store_MacOS contained in this compilation
+ * unit.
+ */
+class Certificate_Store_MacOS_Impl
+ {
+ private:
+ static constexpr const char* system_roots =
+ "/System/Library/Keychains/SystemRootCertificates.keychain";
+ static constexpr const char* system_keychain =
+ "/Library/Keychains/System.keychain";
+
+ 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.
+ */
+ class Query
+ {
+ public:
+ Query() = default;
+ ~Query() = default;
+ Query(Query&& other) = default;
+ Query& operator=(Query&& other) = default;
+
+ Query(const Query& other) = delete;
+ Query& operator=(const Query& other) = delete;
+
+ public:
+ void addParameter(CFStringRef key, CFTypeRef value)
+ {
+ m_keys.emplace_back(key);
+ m_values.emplace_back(value);
+ }
+
+ void addParameter(CFStringRef key, std::vector<uint8_t> value)
+ {
+ // TODO C++17: std::vector::emplace_back will return the reference
+ // to the inserted object straight away.
+ m_data_store.emplace_back(std::move(value));
+ const auto& data = m_data_store.back();
+
+ m_data_refs.emplace_back(CFDataCreateWithBytesNoCopy(kCFAllocatorDefault,
+ data.data(),
+ data.size(),
+ kCFAllocatorNull));
+ const auto& data_ref = m_data_refs.back();
+ check_notnull(data_ref, "create CFDataRef of search object failed");
+
+ addParameter(key, data_ref.get());
+ }
+
+ /**
+ * Amends the user-provided search query with generic filter rules
+ * for the associated system keychains and transforms it into a
+ * representation that can be passed to the Apple keychain API.
+ */
+ scoped_CFType<CFDictionaryRef> prepare(const CFArrayRef& keychains,
+ const SecPolicyRef& policy)
+ {
+ addParameter(kSecClass, kSecClassCertificate);
+ addParameter(kSecReturnRef, kCFBooleanTrue);
+ addParameter(kSecMatchLimit, kSecMatchLimitAll);
+ addParameter(kSecMatchTrustedOnly, kCFBooleanTrue);
+ addParameter(kSecMatchSearchList, keychains);
+ addParameter(kSecMatchPolicy, policy);
+
+ BOTAN_ASSERT_EQUAL(m_keys.size(), m_values.size(), "valid key-value pairs");
+
+ auto query = scoped_CFType<CFDictionaryRef>(CFDictionaryCreate(
+ kCFAllocatorDefault, (const void**)m_keys.data(),
+ (const void**)m_values.data(), m_keys.size(),
+ &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
+ check_notnull(query, "create search query");
+
+ return query;
+ }
+
+ private:
+ using Data = std::vector<std::vector<uint8_t>>;
+ using DataRefs = std::vector<scoped_CFType<CFDataRef>>;
+ using Keys = std::vector<CFStringRef>;
+ using Values = std::vector<CFTypeRef>;
+
+ 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
+ Keys m_keys; //! ordered list of search parameter keys
+ Values m_values; //! ordered list of search parameter values
+ };
+
+ public:
+ Certificate_Store_MacOS_Impl() :
+ m_policy(SecPolicyCreateBasicX509()),
+ m_system_roots(nullptr),
+ m_system_chain(nullptr),
+ m_keychains(nullptr)
+ {
+ check_success(SecKeychainOpen(system_roots, &m_system_roots.get()),
+ "open system root certificates");
+ check_success(SecKeychainOpen(system_keychain, &m_system_chain.get()),
+ "open system keychain");
+ check_notnull(m_system_roots, "open system root certificate chain");
+ check_notnull(m_system_chain, "open system certificate chain");
+
+ // m_keychains is merely a convenience list view into all open keychain
+ // objects. This list is required in prepareQuery().
+ std::array<const void*, 2> keychains{{
+ m_system_roots.get(),
+ m_system_chain.get()
+ }};
+
+ m_keychains.assign(
+ CFArrayCreate(kCFAllocatorDefault,
+ keychains.data(),
+ keychains.size(),
+ &kCFTypeArrayCallBacks));
+ check_notnull(m_keychains, "initialize keychain array");
+ }
+
+ std::shared_ptr<const X509_Certificate> findOne(Query query) const
+ {
+ query.addParameter(kSecMatchLimit, kSecMatchLimitOne);
+
+ scoped_CFType<CFTypeRef> result(nullptr);
+ search(std::move(query), &result.get());
+
+ return (result) ? readCertificate(result.get()) : nullptr;
+ }
+
+ std::vector<std::shared_ptr<const X509_Certificate>> findAll(Query query) const
+ {
+ query.addParameter(kSecMatchLimit, kSecMatchLimitAll);
+
+ scoped_CFType<CFArrayRef> result(nullptr);
+ search(std::move(query), (CFTypeRef*)&result.get());
+
+ std::vector<std::shared_ptr<const X509_Certificate>> output;
+
+ if(result)
+ {
+ const auto count = CFArrayGetCount(result.get());
+ BOTAN_ASSERT(count > 0, "certificate result list contains data");
+
+ for(unsigned int i = 0; i < count; ++i)
+ {
+ auto cert = CFArrayGetValueAtIndex(result.get(), i);
+ output.emplace_back(readCertificate(cert));
+ }
+ }
+
+ return output;
+ }
+
+ protected:
+ void search(Query query, CFTypeRef* result) const
+ {
+ scoped_CFType<CFDictionaryRef> fullQuery(query.prepare(keychains(), policy()));
+
+ auto status = SecItemCopyMatching(fullQuery.get(), result);
+
+ if(errSecItemNotFound == status)
+ {
+ return; // no matches
+ }
+
+ check_success(status, "look up certificate");
+ check_notnull(result, "look up certificate (invalid result value)");
+ }
+
+ /**
+ * Convert a CFTypeRef object into a Botan::X509_Certificate
+ */
+ std::shared_ptr<const X509_Certificate> readCertificate(CFTypeRef object) const
+ {
+ if(!object || CFGetTypeID(object) != SecCertificateGetTypeID())
+ {
+ throw Internal_Error("cannot convert CFTypeRef to SecCertificateRef");
+ }
+
+ auto cert = static_cast<SecCertificateRef>(const_cast<void*>(object));
+
+ scoped_CFType<CFDataRef> derData(SecCertificateCopyData(cert));
+ check_notnull(derData, "read extracted certificate");
+
+ const auto data = CFDataGetBytePtr(derData.get());
+ const auto length = CFDataGetLength(derData.get());
+
+ DataSource_Memory ds(data, length);
+ return std::make_shared<Botan::X509_Certificate>(ds);
+ }
+
+ CFArrayRef keychains() const { return m_keychains.get(); }
+ SecPolicyRef policy() const { return m_policy.get(); }
+
+ private:
+ scoped_CFType<SecPolicyRef> m_policy;
+ scoped_CFType<SecKeychainRef> m_system_roots;
+ scoped_CFType<SecKeychainRef> m_system_chain;
+ scoped_CFType<CFArrayRef> m_keychains;
+ };
+
+//
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+//
+// Implementation of Botan::Certificate_Store interface ...
+//
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+//
+
+Certificate_Store_MacOS::Certificate_Store_MacOS() :
+ m_impl(std::make_shared<Certificate_Store_MacOS_Impl>())
+ {
+ }
+
+std::vector<X509_DN> Certificate_Store_MacOS::all_subjects() const
+ {
+ // Note: This fetches and parses all certificates in the trust store.
+ // Apple's API provides SecCertificateCopyNormalizedSubjectSequence
+ // which facilitates reading the certificate DN without parsing the
+ // entire certificate via Botan::X509_Certificate. However, this
+ // function applies the same DN "normalization" as stated above.
+ const auto certificates = m_impl->findAll({});
+
+ std::vector<X509_DN> output;
+ std::transform(certificates.cbegin(), certificates.cend(),
+ std::back_inserter(output),
+ [](const std::shared_ptr<const X509_Certificate> cert)
+ {
+ return cert->subject_dn();
+ });
+
+ return output;
+ }
+
+std::shared_ptr<const X509_Certificate>
+Certificate_Store_MacOS::find_cert(const X509_DN& subject_dn,
+ const std::vector<uint8_t>& key_id) const
+ {
+ Certificate_Store_MacOS_Impl::Query query;
+ query.addParameter(kSecAttrSubject, normalizeAndSerialize(subject_dn));
+
+ if(!key_id.empty())
+ {
+ query.addParameter(kSecAttrSubjectKeyID, key_id);
+ }
+
+ return m_impl->findOne(std::move(query));
+ }
+
+std::vector<std::shared_ptr<const X509_Certificate>> Certificate_Store_MacOS::find_all_certs(
+ const X509_DN& subject_dn,
+ const std::vector<uint8_t>& key_id) const
+ {
+ Certificate_Store_MacOS_Impl::Query query;
+ query.addParameter(kSecAttrSubject, normalizeAndSerialize(subject_dn));
+
+ if(!key_id.empty())
+ {
+ query.addParameter(kSecAttrSubjectKeyID, key_id);
+ }
+
+ return m_impl->findAll(std::move(query));
+ }
+
+std::shared_ptr<const X509_Certificate>
+Certificate_Store_MacOS::find_cert_by_pubkey_sha1(const std::vector<uint8_t>& key_hash) const
+ {
+ if(key_hash.size() != 20)
+ {
+ throw Invalid_Argument("Certificate_Store_MacOS::find_cert_by_pubkey_sha1 invalid hash");
+ }
+
+ Certificate_Store_MacOS_Impl::Query query;
+ query.addParameter(kSecAttrPublicKeyHash, key_hash);
+
+ return m_impl->findOne(std::move(query));
+ }
+
+std::shared_ptr<const X509_Certificate>
+Certificate_Store_MacOS::find_cert_by_raw_subject_dn_sha256(const std::vector<uint8_t>& subject_hash) const
+ {
+ BOTAN_UNUSED(subject_hash);
+ throw Not_Implemented("Certificate_Store_MacOS::find_cert_by_raw_subject_dn_sha256");
+ }
+
+std::shared_ptr<const X509_CRL> Certificate_Store_MacOS::find_crl_for(const X509_Certificate& subject) const
+ {
+ BOTAN_UNUSED(subject);
+ return {};
+ }
+
+} // namespace Botan
diff --git a/comm/third_party/botan/src/lib/x509/certstor_system_macos/certstor_macos.h b/comm/third_party/botan/src/lib/x509/certstor_system_macos/certstor_macos.h
new file mode 100644
index 0000000000..e7416e631c
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/certstor_system_macos/certstor_macos.h
@@ -0,0 +1,81 @@
+/*
+* Certificate Store
+* (C) 1999-2019 Jack Lloyd
+* (C) 2019 René Meusel
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_CERT_STORE_SYSTEM_MACOS_H_
+#define BOTAN_CERT_STORE_SYSTEM_MACOS_H_
+
+#include <memory>
+
+#include <botan/certstor.h>
+
+namespace Botan {
+
+class Certificate_Store_MacOS_Impl;
+
+/**
+* Certificate Store that is backed by the system trust store on macOS. This
+* opens a handle to the macOS keychain and serves certificate queries directly
+* from there.
+*/
+class BOTAN_PUBLIC_API(2, 10) Certificate_Store_MacOS final : public Certificate_Store
+ {
+ public:
+ Certificate_Store_MacOS();
+
+ Certificate_Store_MacOS(const Certificate_Store_MacOS&) = default;
+ Certificate_Store_MacOS(Certificate_Store_MacOS&&) = default;
+ Certificate_Store_MacOS& operator=(const Certificate_Store_MacOS&) = default;
+ Certificate_Store_MacOS& operator=(Certificate_Store_MacOS&&) = default;
+
+ /**
+ * @return DNs for all certificates managed by the store
+ */
+ std::vector<X509_DN> all_subjects() const override;
+
+ /**
+ * Find a certificate by Subject DN and (optionally) key identifier
+ * @return the first certificate that matches
+ */
+ std::shared_ptr<const X509_Certificate> find_cert(
+ const X509_DN& subject_dn,
+ const std::vector<uint8_t>& key_id) const override;
+
+ /**
+ * Find all certificates with a given Subject DN.
+ * Subject DN and even the key identifier might not be unique.
+ */
+ std::vector<std::shared_ptr<const X509_Certificate>> find_all_certs(
+ const X509_DN& subject_dn, const std::vector<uint8_t>& key_id) const override;
+
+ /**
+ * Find a certificate by searching for one with a matching SHA-1 hash of
+ * public key.
+ * @return a matching certificate or nullptr otherwise
+ */
+ std::shared_ptr<const X509_Certificate>
+ find_cert_by_pubkey_sha1(const std::vector<uint8_t>& key_hash) const override;
+
+ /**
+ * @throws Botan::Not_Implemented
+ */
+ std::shared_ptr<const X509_Certificate>
+ find_cert_by_raw_subject_dn_sha256(const std::vector<uint8_t>& subject_hash) const override;
+
+ /**
+ * Fetching CRLs is not supported by the keychain on macOS. This will
+ * always return an empty list.
+ */
+ std::shared_ptr<const X509_CRL> find_crl_for(const X509_Certificate& subject) const override;
+
+ private:
+ std::shared_ptr<Certificate_Store_MacOS_Impl> m_impl;
+ };
+
+}
+
+#endif \ No newline at end of file
diff --git a/comm/third_party/botan/src/lib/x509/certstor_system_macos/info.txt b/comm/third_party/botan/src/lib/x509/certstor_system_macos/info.txt
new file mode 100644
index 0000000000..723ec61bd9
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/certstor_system_macos/info.txt
@@ -0,0 +1,15 @@
+<defines>
+CERTSTOR_MACOS -> 20190207
+</defines>
+
+<os_features>
+apple_keychain
+</os_features>
+
+<header:public>
+certstor_macos.h
+</header:public>
+
+<frameworks>
+macos -> CoreFoundation,Security
+</frameworks>
diff --git a/comm/third_party/botan/src/lib/x509/certstor_system_windows/certstor_windows.cpp b/comm/third_party/botan/src/lib/x509/certstor_system_windows/certstor_windows.cpp
new file mode 100644
index 0000000000..51530954ac
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/certstor_system_windows/certstor_windows.cpp
@@ -0,0 +1,258 @@
+/*
+* Certificate Store
+* (C) 1999-2019 Jack Lloyd
+* (C) 2018-2019 Patrik Fiedler, Tim Oesterreich
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/certstor_windows.h>
+#include <botan/pkix_types.h>
+#include <botan/der_enc.h>
+
+#include <array>
+#include <vector>
+
+#define NOMINMAX 1
+#define _WINSOCKAPI_ // stop windows.h including winsock.h
+#include <windows.h>
+#include <wincrypt.h>
+
+#define WINCRYPT_UNUSED_PARAM 0 // for avoiding warnings when passing NULL to unused params in win32 api that accept integer types
+
+namespace Botan {
+namespace {
+
+using Cert_Pointer = std::shared_ptr<const Botan::X509_Certificate>;
+using Cert_Vector = std::vector<Cert_Pointer>;
+const std::array<const char*, 2> 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.
+ */
+template<class T>
+class Handle_Guard
+ {
+ public:
+ Handle_Guard(T context)
+ : m_context(context)
+ {
+ }
+
+ Handle_Guard(const Handle_Guard<T>& rhs) = delete;
+ Handle_Guard(Handle_Guard<T>&& rhs) :
+ m_context(std::move(rhs.m_context))
+ {
+ rhs.m_context = nullptr;
+ }
+
+ ~Handle_Guard()
+ {
+ close<T>();
+ }
+
+ operator bool() const
+ {
+ return m_context != nullptr;
+ }
+
+ bool assign(T context)
+ {
+ m_context = context;
+ return m_context != nullptr;
+ }
+
+ T& get()
+ {
+ return m_context;
+ }
+
+ const T& get() const
+ {
+ return m_context;
+ }
+
+ T operator->()
+ {
+ return m_context;
+ }
+
+ private:
+ template<class T2 = T>
+ typename std::enable_if<std::is_same<T2, PCCERT_CONTEXT>::value>::type close()
+ {
+ if(m_context)
+ {
+ CertFreeCertificateContext(m_context);
+ }
+ }
+
+ template<class T2 = T>
+ typename std::enable_if<std::is_same<T2, HCERTSTORE>::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);
+ }
+ }
+
+ T m_context;
+ };
+
+HCERTSTORE open_cert_store(const char* cert_store_name)
+ {
+ auto store = CertOpenSystemStoreA(WINCRYPT_UNUSED_PARAM, cert_store_name);
+ if(!store)
+ {
+ throw Botan::Internal_Error(
+ "failed to open windows certificate store '" + std::string(cert_store_name) +
+ "' (Error Code: " +
+ std::to_string(::GetLastError()) + ")");
+ }
+ return store;
+ }
+
+Cert_Vector search_cert_stores(const _CRYPTOAPI_BLOB& blob, const DWORD& find_type,
+ std::function<bool(const Cert_Vector& certs, Cert_Pointer cert)> filter,
+ bool return_on_first_found)
+ {
+ Cert_Vector certs;
+ for(const auto store_name : cert_store_names)
+ {
+ Handle_Guard<HCERTSTORE> windows_cert_store = open_cert_store(store_name);
+ Handle_Guard<PCCERT_CONTEXT> 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())))
+ {
+ auto cert = std::make_shared<X509_Certificate>(cert_context->pbCertEncoded, cert_context->cbCertEncoded);
+ if(filter(certs, cert))
+ {
+ if(return_on_first_found)
+ {
+ return {cert};
+ }
+ certs.push_back(cert);
+ }
+ }
+ }
+
+ return certs;
+ }
+
+bool already_contains_certificate(const Cert_Vector& certs, Cert_Pointer cert)
+ {
+ return std::any_of(certs.begin(), certs.end(), [&](std::shared_ptr<const Botan::X509_Certificate> c)
+ {
+ return *c == *cert;
+ });
+ }
+
+Cert_Vector find_cert_by_dn_and_key_id(const Botan::X509_DN& subject_dn,
+ const std::vector<uint8_t>& key_id,
+ bool return_on_first_found)
+ {
+ _CRYPTOAPI_BLOB blob;
+ DWORD find_type;
+ std::vector<uint8_t> dn_data;
+
+ // 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;
+ DER_Encoder encoder(dn_data);
+ subject_dn.encode_into(encoder);
+ blob.cbData = static_cast<DWORD>(dn_data.size());
+ blob.pbData = reinterpret_cast<BYTE*>(dn_data.data());
+ }
+ else
+ {
+ find_type = CERT_FIND_KEY_IDENTIFIER;
+ blob.cbData = static_cast<DWORD>(key_id.size());
+ blob.pbData = const_cast<BYTE*>(key_id.data());
+ }
+
+ auto filter = [&](const Cert_Vector& certs, Cert_Pointer cert)
+ {
+ return !already_contains_certificate(certs, cert) && (key_id.empty() || cert->subject_dn() == subject_dn);
+ };
+
+ return search_cert_stores(blob, find_type, filter, return_on_first_found);
+ }
+} // namespace
+
+Certificate_Store_Windows::Certificate_Store_Windows() {}
+
+std::vector<X509_DN> Certificate_Store_Windows::all_subjects() const
+ {
+ std::vector<X509_DN> subject_dns;
+ for(const auto store_name : cert_store_names)
+ {
+ Handle_Guard<HCERTSTORE> windows_cert_store = open_cert_store(store_name);
+ Handle_Guard<PCCERT_CONTEXT> 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())))
+ {
+ X509_Certificate cert(cert_context->pbCertEncoded, cert_context->cbCertEncoded);
+ subject_dns.push_back(cert.subject_dn());
+ }
+ }
+
+ return subject_dns;
+ }
+
+Cert_Pointer Certificate_Store_Windows::find_cert(const Botan::X509_DN& subject_dn,
+ const std::vector<uint8_t>& key_id) const
+ {
+ const auto certs = find_cert_by_dn_and_key_id(subject_dn, key_id, true);
+ return certs.empty() ? nullptr : certs.front();
+ }
+
+Cert_Vector Certificate_Store_Windows::find_all_certs(
+ const X509_DN& subject_dn,
+ const std::vector<uint8_t>& key_id) const
+ {
+ return find_cert_by_dn_and_key_id(subject_dn, key_id, false);
+ }
+
+Cert_Pointer Certificate_Store_Windows::find_cert_by_pubkey_sha1(const std::vector<uint8_t>& 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<DWORD>(key_hash.size());
+ blob.pbData = const_cast<BYTE*>(key_hash.data());
+
+ auto filter = [](const Cert_Vector&, Cert_Pointer) { return true; };
+
+ const auto certs = search_cert_stores(blob, CERT_FIND_KEY_IDENTIFIER, filter, true);
+ return certs.empty() ? nullptr : certs.front();
+ }
+
+Cert_Pointer Certificate_Store_Windows::find_cert_by_raw_subject_dn_sha256(
+ const std::vector<uint8_t>& subject_hash) const
+ {
+ BOTAN_UNUSED(subject_hash);
+ throw Not_Implemented("Certificate_Store_Windows::find_cert_by_raw_subject_dn_sha256");
+ }
+
+std::shared_ptr<const X509_CRL> 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 {};
+ }
+}
diff --git a/comm/third_party/botan/src/lib/x509/certstor_system_windows/certstor_windows.h b/comm/third_party/botan/src/lib/x509/certstor_system_windows/certstor_windows.h
new file mode 100644
index 0000000000..f47e718c85
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/certstor_system_windows/certstor_windows.h
@@ -0,0 +1,70 @@
+/*
+* Certificate Store
+* (C) 1999-2019 Jack Lloyd
+* (C) 2019 Patrick Schmidt
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_CERT_STORE_SYSTEM_WINDOWS_H_
+#define BOTAN_CERT_STORE_SYSTEM_WINDOWS_H_
+
+#include <botan/certstor.h>
+
+namespace Botan {
+/**
+* Certificate Store that is backed by the system trust store on Windows.
+*/
+class BOTAN_PUBLIC_API(2, 11) Certificate_Store_Windows final : public Certificate_Store
+ {
+ public:
+ Certificate_Store_Windows();
+
+ Certificate_Store_Windows(const Certificate_Store_Windows&) = default;
+ Certificate_Store_Windows(Certificate_Store_Windows&&) = default;
+ Certificate_Store_Windows& operator=(const Certificate_Store_Windows&) = default;
+ Certificate_Store_Windows& operator=(Certificate_Store_Windows&&) = default;
+
+ /**
+ * @return DNs for all certificates managed by the store
+ */
+ std::vector<X509_DN> all_subjects() const override;
+
+ /**
+ * Find a certificate by Subject DN and (optionally) key identifier
+ * @return the first certificate that matches
+ */
+ std::shared_ptr<const X509_Certificate> find_cert(
+ const X509_DN& subject_dn,
+ const std::vector<uint8_t>& key_id) const override;
+
+ /**
+ * Find all certificates with a given Subject DN.
+ * Subject DN and even the key identifier might not be unique.
+ */
+ std::vector<std::shared_ptr<const X509_Certificate>> find_all_certs(
+ const X509_DN& subject_dn, const std::vector<uint8_t>& key_id) const override;
+
+ /**
+ * Find a certificate by searching for one with a matching SHA-1 hash of
+ * public key.
+ * @return a matching certificate or nullptr otherwise
+ */
+ std::shared_ptr<const X509_Certificate>
+ find_cert_by_pubkey_sha1(const std::vector<uint8_t>& key_hash) const override;
+
+ /**
+ * @throws Botan::Not_Implemented
+ */
+ std::shared_ptr<const X509_Certificate>
+ find_cert_by_raw_subject_dn_sha256(const std::vector<uint8_t>& subject_hash) const override;
+
+ /**
+ * Not Yet Implemented
+ * @return nullptr;
+ */
+ std::shared_ptr<const X509_CRL> find_crl_for(const X509_Certificate& subject) const override;
+ };
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/x509/certstor_system_windows/info.txt b/comm/third_party/botan/src/lib/x509/certstor_system_windows/info.txt
new file mode 100644
index 0000000000..6d70de8bf2
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/certstor_system_windows/info.txt
@@ -0,0 +1,16 @@
+<defines>
+CERTSTOR_WINDOWS -> 20190430
+</defines>
+
+<os_features>
+win32,certificate_store
+</os_features>
+
+<header:public>
+certstor_windows.h
+</header:public>
+
+<libs>
+windows -> crypt32
+mingw -> crypt32
+</libs>
diff --git a/comm/third_party/botan/src/lib/x509/crl_ent.cpp b/comm/third_party/botan/src/lib/x509/crl_ent.cpp
new file mode 100644
index 0000000000..d83d43d73c
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/crl_ent.cpp
@@ -0,0 +1,140 @@
+/*
+* CRL Entry
+* (C) 1999-2010 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/x509_crl.h>
+#include <botan/x509cert.h>
+#include <botan/x509_ext.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
+#include <botan/bigint.h>
+
+namespace Botan {
+
+struct CRL_Entry_Data
+ {
+ std::vector<uint8_t> m_serial;
+ X509_Time m_time;
+ CRL_Code m_reason = UNSPECIFIED;
+ Extensions m_extensions;
+ };
+
+/*
+* Create a CRL_Entry
+*/
+CRL_Entry::CRL_Entry(const X509_Certificate& cert, CRL_Code why)
+ {
+ m_data.reset(new CRL_Entry_Data);
+ 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 != UNSPECIFIED)
+ {
+ m_data->m_extensions.add(new Cert_Extension::CRL_ReasonCode(why));
+ }
+ }
+
+/*
+* Compare two CRL_Entrys for equality
+*/
+bool operator==(const CRL_Entry& a1, const CRL_Entry& a2)
+ {
+ if(a1.serial_number() != a2.serial_number())
+ return false;
+ if(a1.expire_time() != a2.expire_time())
+ return false;
+ if(a1.reason_code() != a2.reason_code())
+ return false;
+ return true;
+ }
+
+/*
+* Compare two CRL_Entrys for inequality
+*/
+bool operator!=(const CRL_Entry& a1, const CRL_Entry& a2)
+ {
+ return !(a1 == a2);
+ }
+
+/*
+* DER encode a CRL_Entry
+*/
+void CRL_Entry::encode_into(DER_Encoder& der) const
+ {
+ der.start_cons(SEQUENCE)
+ .encode(BigInt::decode(serial_number()))
+ .encode(expire_time())
+ .start_cons(SEQUENCE)
+ .encode(extensions())
+ .end_cons()
+ .end_cons();
+ }
+
+/*
+* Decode a BER encoded CRL_Entry
+*/
+void CRL_Entry::decode_from(BER_Decoder& source)
+ {
+ BigInt serial_number_bn;
+
+ std::unique_ptr<CRL_Entry_Data> data(new CRL_Entry_Data);
+
+ BER_Decoder entry = source.start_cons(SEQUENCE);
+
+ entry.decode(serial_number_bn).decode(data->m_time);
+ data->m_serial = BigInt::encode(serial_number_bn);
+
+ if(entry.more_items())
+ {
+ entry.decode(data->m_extensions);
+ if(auto ext = data->m_extensions.get_extension_object_as<Cert_Extension::CRL_ReasonCode>())
+ {
+ data->m_reason = ext->get_reason();
+ }
+ else
+ {
+ data->m_reason = UNSPECIFIED;
+ }
+ }
+
+ entry.end_cons();
+
+ m_data.reset(data.release());
+ }
+
+const CRL_Entry_Data& CRL_Entry::data() const
+ {
+ if(!m_data)
+ {
+ throw Invalid_State("CRL_Entry_Data uninitialized");
+ }
+
+ return *m_data.get();
+ }
+
+const std::vector<uint8_t>& CRL_Entry::serial_number() const
+ {
+ return data().m_serial;
+ }
+
+const X509_Time& CRL_Entry::expire_time() const
+ {
+ return data().m_time;
+ }
+
+CRL_Code CRL_Entry::reason_code() const
+ {
+ return data().m_reason;
+ }
+
+const Extensions& CRL_Entry::extensions() const
+ {
+ return data().m_extensions;
+ }
+
+
+}
diff --git a/comm/third_party/botan/src/lib/x509/crl_ent.h b/comm/third_party/botan/src/lib/x509/crl_ent.h
new file mode 100644
index 0000000000..aa60d4172b
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/crl_ent.h
@@ -0,0 +1,11 @@
+/*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_CRL_ENTRY_H_
+#define BOTAN_CRL_ENTRY_H_
+
+#include <botan/x509_crl.h>
+BOTAN_DEPRECATED_HEADER(crl_ent.h)
+
+#endif
diff --git a/comm/third_party/botan/src/lib/x509/datastor.cpp b/comm/third_party/botan/src/lib/x509/datastor.cpp
new file mode 100644
index 0000000000..2cdd3458ca
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/datastor.cpp
@@ -0,0 +1,205 @@
+/*
+* Data Store
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/datastor.h>
+#include <botan/exceptn.h>
+#include <botan/parsing.h>
+#include <botan/hex.h>
+#include <botan/internal/stl_util.h>
+
+namespace Botan {
+
+/*
+* Data_Store Equality Comparison
+*/
+bool Data_Store::operator==(const Data_Store& other) const
+ {
+ return (m_contents == other.m_contents);
+ }
+
+/*
+* Check if this key has at least one value
+*/
+bool Data_Store::has_value(const std::string& key) const
+ {
+ return (m_contents.lower_bound(key) != m_contents.end());
+ }
+
+/*
+* Search based on an arbitrary predicate
+*/
+std::multimap<std::string, std::string> Data_Store::search_for(
+ std::function<bool (std::string, std::string)> predicate) const
+ {
+ std::multimap<std::string, std::string> out;
+
+ for(auto i = m_contents.begin(); i != m_contents.end(); ++i)
+ if(predicate(i->first, i->second))
+ out.insert(std::make_pair(i->first, i->second));
+
+ return out;
+ }
+
+/*
+* Search based on key equality
+*/
+std::vector<std::string> Data_Store::get(const std::string& looking_for) const
+ {
+ std::vector<std::string> out;
+ auto range = m_contents.equal_range(looking_for);
+ for(auto i = range.first; i != range.second; ++i)
+ out.push_back(i->second);
+ return out;
+ }
+
+/*
+* Get a single atom
+*/
+std::string Data_Store::get1(const std::string& key) const
+ {
+ std::vector<std::string> vals = get(key);
+
+ if(vals.empty())
+ throw Invalid_State("Data_Store::get1: No values set for " + key);
+ if(vals.size() > 1)
+ throw Invalid_State("Data_Store::get1: More than one value for " + key);
+
+ return vals[0];
+ }
+
+std::string Data_Store::get1(const std::string& key,
+ const std::string& default_value) const
+ {
+ std::vector<std::string> vals = get(key);
+
+ if(vals.size() > 1)
+ throw Invalid_State("Data_Store::get1: More than one value for " + key);
+
+ if(vals.empty())
+ return default_value;
+
+ return vals[0];
+ }
+
+/*
+* Get a single std::vector atom
+*/
+std::vector<uint8_t>
+Data_Store::get1_memvec(const std::string& key) const
+ {
+ std::vector<std::string> vals = get(key);
+
+ if(vals.empty())
+ return std::vector<uint8_t>();
+
+ if(vals.size() > 1)
+ throw Invalid_State("Data_Store::get1_memvec: Multiple values for " +
+ key);
+
+ return hex_decode(vals[0]);
+ }
+
+/*
+* Get a single uint32_t atom
+*/
+uint32_t Data_Store::get1_uint32(const std::string& key,
+ uint32_t default_val) const
+ {
+ std::vector<std::string> vals = get(key);
+
+ if(vals.empty())
+ return default_val;
+ else if(vals.size() > 1)
+ throw Invalid_State("Data_Store::get1_uint32: Multiple values for " + key);
+
+ return to_u32bit(vals[0]);
+ }
+
+/*
+* Insert a single key and value
+*/
+void Data_Store::add(const std::string& key, const std::string& val)
+ {
+ multimap_insert(m_contents, key, val);
+ }
+
+/*
+* Insert a single key and value
+*/
+void Data_Store::add(const std::string& key, uint32_t val)
+ {
+ add(key, std::to_string(val));
+ }
+
+/*
+* Insert a single key and value
+*/
+void Data_Store::add(const std::string& key, const secure_vector<uint8_t>& val)
+ {
+ add(key, hex_encode(val.data(), val.size()));
+ }
+
+void Data_Store::add(const std::string& key, const std::vector<uint8_t>& val)
+ {
+ add(key, hex_encode(val.data(), val.size()));
+ }
+
+/*
+* Insert a mapping of key/value pairs
+*/
+void Data_Store::add(const std::multimap<std::string, std::string>& in)
+ {
+ std::multimap<std::string, std::string>::const_iterator i = in.begin();
+ while(i != in.end())
+ {
+ m_contents.insert(*i);
+ ++i;
+ }
+ }
+
+/*
+* Create and populate a X509_DN
+*/
+X509_DN create_dn(const Data_Store& info)
+ {
+ auto names = info.search_for(
+ [](const std::string& key, const std::string&)
+ {
+ return (key.find("X520.") != std::string::npos);
+ });
+
+ X509_DN dn;
+
+ for(auto i = names.begin(); i != names.end(); ++i)
+ dn.add_attribute(i->first, i->second);
+
+ return dn;
+ }
+
+/*
+* Create and populate an AlternativeName
+*/
+AlternativeName create_alt_name(const Data_Store& info)
+ {
+ auto names = info.search_for(
+ [](const std::string& key, const std::string&)
+ {
+ return (key == "RFC822" ||
+ key == "DNS" ||
+ key == "URI" ||
+ key == "IP");
+ });
+
+ AlternativeName alt_name;
+
+ for(auto i = names.begin(); i != names.end(); ++i)
+ alt_name.add_attribute(i->first, i->second);
+
+ return alt_name;
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/x509/datastor.h b/comm/third_party/botan/src/lib/x509/datastor.h
new file mode 100644
index 0000000000..1ff85c22cf
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/datastor.h
@@ -0,0 +1,85 @@
+/*
+* Data Store
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_DATA_STORE_H_
+#define BOTAN_DATA_STORE_H_
+
+#include <botan/pkix_types.h>
+#include <functional>
+#include <string>
+#include <vector>
+#include <map>
+
+BOTAN_FUTURE_INTERNAL_HEADER(datastor.h)
+
+namespace Botan {
+
+/**
+* Data Store
+*
+* This class is used internally by the library, and exposed for ABI
+* reasons. There is no reason for applications to use this type directly.
+* It will be removed in a future major release.
+*/
+class BOTAN_UNSTABLE_API Data_Store final
+ {
+ public:
+ /**
+ * A search function
+ */
+ bool operator==(const Data_Store&) const;
+
+ std::multimap<std::string, std::string> search_for(
+ std::function<bool (std::string, std::string)> predicate) const;
+
+ std::vector<std::string> get(const std::string&) const;
+
+ std::string get1(const std::string& key) const;
+
+ std::string get1(const std::string& key,
+ const std::string& default_value) const;
+
+ std::vector<uint8_t> get1_memvec(const std::string&) const;
+ uint32_t get1_uint32(const std::string&, uint32_t = 0) const;
+
+ bool has_value(const std::string&) const;
+
+ void add(const std::multimap<std::string, std::string>&);
+ void add(const std::string&, const std::string&);
+ void add(const std::string&, uint32_t);
+ void add(const std::string&, const secure_vector<uint8_t>&);
+ void add(const std::string&, const std::vector<uint8_t>&);
+ private:
+ std::multimap<std::string, std::string> m_contents;
+ };
+
+/*
+* Data Store Extraction Operations
+*/
+
+/*
+* Create and populate a X509_DN
+* @param info data store containing DN information
+* @return DN containing attributes from data store
+*/
+BOTAN_PUBLIC_API(2,0) X509_DN
+BOTAN_DEPRECATED("Avoid roundtripping names through Data_Store")
+create_dn(const Data_Store& info);
+
+/*
+* Create and populate an AlternativeName
+* @param info data store containing AlternativeName information
+* @return AlternativeName containing attributes from data store
+*/
+BOTAN_PUBLIC_API(2,0) AlternativeName
+BOTAN_DEPRECATED("Avoid roundtripping names through Data_Store")
+create_alt_name(const Data_Store& info);
+
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/x509/info.txt b/comm/third_party/botan/src/lib/x509/info.txt
new file mode 100644
index 0000000000..20a1aa2b08
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/info.txt
@@ -0,0 +1,12 @@
+<defines>
+X509_CERTIFICATES -> 20151023
+X509 -> 20180911
+OCSP -> 20161118
+</defines>
+
+<requires>
+asn1
+pubkey
+sha1
+sha2_32
+</requires>
diff --git a/comm/third_party/botan/src/lib/x509/key_constraint.cpp b/comm/third_party/botan/src/lib/x509/key_constraint.cpp
new file mode 100644
index 0000000000..09a4c059a3
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/key_constraint.cpp
@@ -0,0 +1,106 @@
+/*
+* KeyUsage
+* (C) 1999-2007,2016 Jack Lloyd
+* (C) 2016 René Korthaus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/pkix_types.h>
+#include <botan/pk_keys.h>
+#include <vector>
+
+namespace Botan {
+
+std::string key_constraints_to_string(Key_Constraints constraints)
+ {
+ std::vector<std::string> str;
+
+ if(constraints == NO_CONSTRAINTS)
+ return "no_constraints";
+
+ if(constraints & DIGITAL_SIGNATURE)
+ str.push_back("digital_signature");
+
+ if(constraints & NON_REPUDIATION)
+ str.push_back("non_repudiation");
+
+ if(constraints & KEY_ENCIPHERMENT)
+ str.push_back("key_encipherment");
+
+ if(constraints & DATA_ENCIPHERMENT)
+ str.push_back("data_encipherment");
+
+ if(constraints & KEY_AGREEMENT)
+ str.push_back("key_agreement");
+
+ if(constraints & KEY_CERT_SIGN)
+ str.push_back("key_cert_sign");
+
+ if(constraints & CRL_SIGN)
+ str.push_back("crl_sign");
+
+ if(constraints & ENCIPHER_ONLY)
+ str.push_back("encipher_only");
+
+ if(constraints & DECIPHER_ONLY)
+ str.push_back("decipher_only");
+
+ // Not 0 (checked at start) but nothing matched above!
+ if(str.empty())
+ return "other_unknown_constraints";
+
+ if(str.size() == 1)
+ return str[0];
+
+ std::string out;
+ for(size_t i = 0; i < str.size() - 1; ++i)
+ {
+ out += str[i];
+ out += ',';
+ }
+ out += str[str.size() - 1];
+
+ return out;
+ }
+
+/*
+* Make sure the given key constraints are permitted for the given key type
+*/
+void verify_cert_constraints_valid_for_key_type(const Public_Key& pub_key,
+ Key_Constraints constraints)
+ {
+ const std::string name = pub_key.algo_name();
+
+ size_t permitted = 0;
+
+ const bool can_agree = (name == "DH" || name == "ECDH");
+ const bool can_encrypt = (name == "RSA" || name == "ElGamal");
+
+ const bool can_sign =
+ (name == "RSA" || name == "DSA" ||
+ name == "ECDSA" || name == "ECGDSA" || name == "ECKCDSA" || name == "Ed25519" ||
+ name == "GOST-34.10" || name == "GOST-34.10-2012-256" || name == "GOST-34.10-2012-512");
+
+ if(can_agree)
+ {
+ permitted |= KEY_AGREEMENT | ENCIPHER_ONLY | DECIPHER_ONLY;
+ }
+
+ if(can_encrypt)
+ {
+ permitted |= KEY_ENCIPHERMENT | DATA_ENCIPHERMENT;
+ }
+
+ if(can_sign)
+ {
+ permitted |= DIGITAL_SIGNATURE | NON_REPUDIATION | KEY_CERT_SIGN | CRL_SIGN;
+ }
+
+ if(Key_Constraints(constraints & permitted) != constraints)
+ {
+ throw Invalid_Argument("Invalid " + name + " constraints " + key_constraints_to_string(constraints));
+ }
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/x509/key_constraint.h b/comm/third_party/botan/src/lib/x509/key_constraint.h
new file mode 100644
index 0000000000..5d5c7b0832
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/key_constraint.h
@@ -0,0 +1,11 @@
+/*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_KEY_CONSTRAINT_H_
+#define BOTAN_KEY_CONSTRAINT_H_
+
+#include <botan/pkix_enums.h>
+BOTAN_DEPRECATED_HEADER(key_constraint.h)
+
+#endif
diff --git a/comm/third_party/botan/src/lib/x509/name_constraint.cpp b/comm/third_party/botan/src/lib/x509/name_constraint.cpp
new file mode 100644
index 0000000000..c9045729de
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/name_constraint.cpp
@@ -0,0 +1,273 @@
+/*
+* X.509 Name Constraint
+* (C) 2015 Kai Michaelis
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/pkix_types.h>
+#include <botan/ber_dec.h>
+#include <botan/loadstor.h>
+#include <botan/x509cert.h>
+#include <botan/parsing.h>
+#include <sstream>
+
+namespace Botan {
+
+class DER_Encoder;
+
+GeneralName::GeneralName(const std::string& str) : GeneralName()
+ {
+ size_t p = str.find(':');
+
+ if(p != std::string::npos)
+ {
+ m_type = str.substr(0, p);
+ m_name = str.substr(p + 1, std::string::npos);
+ }
+ else
+ {
+ throw Invalid_Argument("Failed to decode Name Constraint");
+ }
+ }
+
+void GeneralName::encode_into(DER_Encoder&) const
+ {
+ throw Not_Implemented("GeneralName encoding");
+ }
+
+void GeneralName::decode_from(class BER_Decoder& ber)
+ {
+ BER_Object obj = ber.get_next_object();
+
+ if(obj.is_a(1, CONTEXT_SPECIFIC))
+ {
+ m_type = "RFC822";
+ m_name = ASN1::to_string(obj);
+ }
+ else if(obj.is_a(2, CONTEXT_SPECIFIC))
+ {
+ m_type = "DNS";
+ m_name = ASN1::to_string(obj);
+ }
+ else if(obj.is_a(6, CONTEXT_SPECIFIC))
+ {
+ m_type = "URI";
+ m_name = ASN1::to_string(obj);
+ }
+ else if(obj.is_a(4, ASN1_Tag(CONTEXT_SPECIFIC | CONSTRUCTED)))
+ {
+ m_type = "DN";
+ X509_DN dn;
+ BER_Decoder dec(obj);
+ std::stringstream ss;
+
+ dn.decode_from(dec);
+ ss << dn;
+
+ m_name = ss.str();
+ }
+ else if(obj.is_a(7, CONTEXT_SPECIFIC))
+ {
+ if(obj.length() == 8)
+ {
+ m_type = "IP";
+ m_name = ipv4_to_string(load_be<uint32_t>(obj.bits(), 0)) + "/" +
+ ipv4_to_string(load_be<uint32_t>(obj.bits(), 1));
+ }
+ else if(obj.length() == 32)
+ {
+ throw Decoding_Error("Unsupported IPv6 name constraint");
+ }
+ else
+ {
+ throw Decoding_Error("Invalid IP name constraint size " + std::to_string(obj.length()));
+ }
+ }
+ else
+ {
+ throw Decoding_Error("Found unknown GeneralName type");
+ }
+ }
+
+GeneralName::MatchResult GeneralName::matches(const X509_Certificate& cert) const
+ {
+ std::vector<std::string> nam;
+ std::function<bool(const GeneralName*, const std::string&)> match_fn;
+
+ const X509_DN& dn = cert.subject_dn();
+ const AlternativeName& alt_name = cert.subject_alt_name();
+
+ if(type() == "DNS")
+ {
+ match_fn = std::mem_fn(&GeneralName::matches_dns);
+
+ nam = alt_name.get_attribute("DNS");
+
+ if(nam.empty())
+ {
+ nam = dn.get_attribute("CN");
+ }
+ }
+ else if(type() == "DN")
+ {
+ match_fn = std::mem_fn(&GeneralName::matches_dn);
+
+ nam.push_back(dn.to_string());
+
+ const auto alt_dn = alt_name.dn();
+ if(alt_dn.empty() == false)
+ {
+ nam.push_back(alt_dn.to_string());
+ }
+ }
+ else if(type() == "IP")
+ {
+ match_fn = std::mem_fn(&GeneralName::matches_ip);
+ nam = alt_name.get_attribute("IP");
+ }
+ else
+ {
+ return MatchResult::UnknownType;
+ }
+
+ if(nam.empty())
+ {
+ return MatchResult::NotFound;
+ }
+
+ bool some = false;
+ bool all = true;
+
+ for(const std::string& n: nam)
+ {
+ bool m = match_fn(this, n);
+
+ some |= m;
+ all &= m;
+ }
+
+ if(all)
+ {
+ return MatchResult::All;
+ }
+ else if(some)
+ {
+ return MatchResult::Some;
+ }
+ else
+ {
+ return MatchResult::None;
+ }
+ }
+
+bool GeneralName::matches_dns(const std::string& nam) const
+ {
+ if(nam.size() == name().size())
+ {
+ return tolower_string(nam) == tolower_string(name());
+ }
+ else if(name().size() > nam.size())
+ {
+ // The constraint is longer than the issued name: not possibly a match
+ return false;
+ }
+ else // name.size() < nam.size()
+ {
+ // constr is suffix of nam
+ const std::string constr = name().front() == '.' ? name() : "." + name();
+ const std::string substr = nam.substr(nam.size() - constr.size(), constr.size());
+ return tolower_string(constr) == tolower_string(substr);
+ }
+ }
+
+bool GeneralName::matches_dn(const std::string& nam) const
+ {
+ std::stringstream ss(nam);
+ std::stringstream tt(name());
+ X509_DN nam_dn, my_dn;
+
+ ss >> nam_dn;
+ tt >> my_dn;
+
+ auto attr = nam_dn.get_attributes();
+ bool ret = true;
+ size_t trys = 0;
+
+ for(const auto& c: my_dn.dn_info())
+ {
+ auto i = attr.equal_range(c.first);
+
+ if(i.first != i.second)
+ {
+ trys += 1;
+ ret = ret && (i.first->second == c.second.value());
+ }
+ }
+
+ return trys > 0 && ret;
+ }
+
+bool GeneralName::matches_ip(const std::string& nam) const
+ {
+ uint32_t ip = string_to_ipv4(nam);
+ std::vector<std::string> p = split_on(name(), '/');
+
+ if(p.size() != 2)
+ throw Decoding_Error("failed to parse IPv4 address");
+
+ uint32_t net = string_to_ipv4(p.at(0));
+ uint32_t mask = string_to_ipv4(p.at(1));
+
+ return (ip & mask) == net;
+ }
+
+std::ostream& operator<<(std::ostream& os, const GeneralName& gn)
+ {
+ os << gn.type() << ":" << gn.name();
+ return os;
+ }
+
+GeneralSubtree::GeneralSubtree(const std::string& str) : GeneralSubtree()
+ {
+ size_t p0, p1;
+ const auto min = std::stoull(str, &p0, 10);
+ const auto max = std::stoull(str.substr(p0 + 1), &p1, 10);
+ GeneralName gn(str.substr(p0 + p1 + 2));
+
+ if(p0 > 0 && p1 > 0)
+ {
+ m_minimum = static_cast<size_t>(min);
+ m_maximum = static_cast<size_t>(max);
+ m_base = gn;
+ }
+ else
+ {
+ throw Invalid_Argument("Failed to decode Name Constraint");
+ }
+ }
+
+void GeneralSubtree::encode_into(DER_Encoder&) const
+ {
+ throw Not_Implemented("General Subtree encoding");
+ }
+
+void GeneralSubtree::decode_from(class BER_Decoder& ber)
+ {
+ ber.start_cons(SEQUENCE)
+ .decode(m_base)
+ .decode_optional(m_minimum,ASN1_Tag(0), CONTEXT_SPECIFIC,size_t(0))
+ .end_cons();
+
+ if(m_minimum != 0)
+ throw Decoding_Error("GeneralSubtree minimum must be 0");
+
+ m_maximum = std::numeric_limits<std::size_t>::max();
+ }
+
+std::ostream& operator<<(std::ostream& os, const GeneralSubtree& gs)
+ {
+ os << gs.minimum() << "," << gs.maximum() << "," << gs.base();
+ return os;
+ }
+}
diff --git a/comm/third_party/botan/src/lib/x509/name_constraint.h b/comm/third_party/botan/src/lib/x509/name_constraint.h
new file mode 100644
index 0000000000..4f56f89eda
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/name_constraint.h
@@ -0,0 +1,11 @@
+/*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_NAME_CONSTRAINT_H_
+#define BOTAN_NAME_CONSTRAINT_H_
+
+#include <botan/pkix_types.h>
+BOTAN_DEPRECATED_HEADER(name_constraint.h)
+
+#endif
diff --git a/comm/third_party/botan/src/lib/x509/ocsp.cpp b/comm/third_party/botan/src/lib/x509/ocsp.cpp
new file mode 100644
index 0000000000..1ca8232634
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/ocsp.cpp
@@ -0,0 +1,363 @@
+/*
+* OCSP
+* (C) 2012,2013 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/ocsp.h>
+#include <botan/certstor.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
+#include <botan/x509_ext.h>
+#include <botan/oids.h>
+#include <botan/base64.h>
+#include <botan/pubkey.h>
+#include <botan/parsing.h>
+
+#if defined(BOTAN_HAS_HTTP_UTIL)
+ #include <botan/http_util.h>
+#endif
+
+namespace Botan {
+
+namespace OCSP {
+
+namespace {
+
+// TODO: should this be in a header somewhere?
+void decode_optional_list(BER_Decoder& ber,
+ ASN1_Tag tag,
+ std::vector<X509_Certificate>& output)
+ {
+ BER_Object obj = ber.get_next_object();
+
+ if(obj.is_a(tag, ASN1_Tag(CONTEXT_SPECIFIC | CONSTRUCTED)) == false)
+ {
+ ber.push_back(obj);
+ return;
+ }
+
+ BER_Decoder list(obj);
+
+ while(list.more_items())
+ {
+ BER_Object certbits = list.get_next_object();
+ X509_Certificate cert(certbits.bits(), certbits.length());
+ output.push_back(std::move(cert));
+ }
+ }
+
+}
+
+Request::Request(const X509_Certificate& issuer_cert,
+ const X509_Certificate& subject_cert) :
+ m_issuer(issuer_cert),
+ m_certid(m_issuer, BigInt::decode(subject_cert.serial_number()))
+ {
+ if(subject_cert.issuer_dn() != issuer_cert.subject_dn())
+ throw Invalid_Argument("Invalid cert pair to OCSP::Request (mismatched issuer,subject args?)");
+ }
+
+Request::Request(const X509_Certificate& issuer_cert,
+ const BigInt& subject_serial) :
+ m_issuer(issuer_cert),
+ m_certid(m_issuer, subject_serial)
+ {
+ }
+
+std::vector<uint8_t> Request::BER_encode() const
+ {
+ std::vector<uint8_t> output;
+ DER_Encoder(output).start_cons(SEQUENCE)
+ .start_cons(SEQUENCE)
+ .start_explicit(0)
+ .encode(static_cast<size_t>(0)) // version #
+ .end_explicit()
+ .start_cons(SEQUENCE)
+ .start_cons(SEQUENCE)
+ .encode(m_certid)
+ .end_cons()
+ .end_cons()
+ .end_cons()
+ .end_cons();
+
+ return output;
+ }
+
+std::string Request::base64_encode() const
+ {
+ return Botan::base64_encode(BER_encode());
+ }
+
+Response::Response(Certificate_Status_Code status)
+ {
+ m_status = Response_Status_Code::Successful;
+ m_dummy_response_status = status;
+ }
+
+Response::Response(const uint8_t response_bits[], size_t response_bits_len) :
+ m_response_bits(response_bits, response_bits + response_bits_len)
+ {
+ m_dummy_response_status = Certificate_Status_Code::OCSP_RESPONSE_INVALID;
+
+ BER_Decoder response_outer = BER_Decoder(m_response_bits).start_cons(SEQUENCE);
+
+ size_t resp_status = 0;
+
+ response_outer.decode(resp_status, ENUMERATED, UNIVERSAL);
+
+ m_status = static_cast<Response_Status_Code>(resp_status);
+
+ if(m_status != Response_Status_Code::Successful)
+ { return; }
+
+ if(response_outer.more_items())
+ {
+ BER_Decoder response_bytes =
+ response_outer.start_cons(ASN1_Tag(0), CONTEXT_SPECIFIC).start_cons(SEQUENCE);
+
+ 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_cons(SEQUENCE);
+
+ basicresponse.start_cons(SEQUENCE)
+ .raw_bytes(m_tbs_bits)
+ .end_cons()
+ .decode(m_sig_algo)
+ .decode(m_signature, BIT_STRING);
+ decode_optional_list(basicresponse, ASN1_Tag(0), m_certs);
+
+ size_t responsedata_version = 0;
+ Extensions extensions;
+
+ BER_Decoder(m_tbs_bits)
+ .decode_optional(responsedata_version, ASN1_Tag(0),
+ ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC))
+
+ .decode_optional(m_signer_name, ASN1_Tag(1),
+ ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC))
+
+ .decode_optional_string(m_key_hash, OCTET_STRING, 2,
+ ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC))
+
+ .decode(m_produced_at)
+
+ .decode_list(m_responses)
+
+ .decode_optional(extensions, ASN1_Tag(1),
+ ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC));
+ }
+
+ response_outer.end_cons();
+ }
+
+Certificate_Status_Code Response::verify_signature(const X509_Certificate& issuer) const
+ {
+ if (m_responses.empty())
+ return m_dummy_response_status;
+
+ try
+ {
+ std::unique_ptr<Public_Key> pub_key(issuer.subject_public_key());
+
+ const std::vector<std::string> sig_info =
+ split_on(m_sig_algo.get_oid().to_formatted_string(), '/');
+
+ if(sig_info.size() != 2 || sig_info[0] != pub_key->algo_name())
+ return Certificate_Status_Code::OCSP_RESPONSE_INVALID;
+
+ std::string padding = sig_info[1];
+ const Signature_Format format = pub_key->default_x509_signature_format();
+
+ PK_Verifier verifier(*pub_key, padding, format);
+
+ if(verifier.verify_message(ASN1::put_in_sequence(m_tbs_bits), m_signature))
+ return Certificate_Status_Code::OCSP_SIGNATURE_OK;
+ else
+ return Certificate_Status_Code::OCSP_SIGNATURE_ERROR;
+ }
+ catch(Exception&)
+ {
+ return Certificate_Status_Code::OCSP_SIGNATURE_ERROR;
+ }
+ }
+
+Certificate_Status_Code Response::check_signature(const std::vector<Certificate_Store*>& trusted_roots,
+ const std::vector<std::shared_ptr<const X509_Certificate>>& ee_cert_path) const
+ {
+ if (m_responses.empty())
+ return m_dummy_response_status;
+
+ std::shared_ptr<const X509_Certificate> signing_cert;
+
+ for(size_t i = 0; i != trusted_roots.size(); ++i)
+ {
+ if(m_signer_name.empty() && m_key_hash.empty())
+ return Certificate_Status_Code::OCSP_RESPONSE_INVALID;
+
+ if(!m_signer_name.empty())
+ {
+ signing_cert = trusted_roots[i]->find_cert(m_signer_name, std::vector<uint8_t>());
+ if(signing_cert)
+ {
+ break;
+ }
+ }
+
+ if(m_key_hash.size() > 0)
+ {
+ signing_cert = trusted_roots[i]->find_cert_by_pubkey_sha1(m_key_hash);
+ if(signing_cert)
+ {
+ break;
+ }
+ }
+ }
+
+ if(!signing_cert && ee_cert_path.size() > 1)
+ {
+ // End entity cert is not allowed to sign their own OCSP request :)
+ for(size_t i = 1; i < ee_cert_path.size(); ++i)
+ {
+ // Check all CA certificates in the (assumed validated) EE cert path
+ if(!m_signer_name.empty() && ee_cert_path[i]->subject_dn() == m_signer_name)
+ {
+ signing_cert = ee_cert_path[i];
+ break;
+ }
+
+ if(m_key_hash.size() > 0 && ee_cert_path[i]->subject_public_key_bitstring_sha1() == m_key_hash)
+ {
+ signing_cert = ee_cert_path[i];
+ break;
+ }
+ }
+ }
+
+ if(!signing_cert && m_certs.size() > 0)
+ {
+ for(size_t i = 0; i < m_certs.size(); ++i)
+ {
+ // Check all CA certificates in the (assumed validated) EE cert path
+ if(!m_signer_name.empty() && m_certs[i].subject_dn() == m_signer_name)
+ {
+ signing_cert = std::make_shared<const X509_Certificate>(m_certs[i]);
+ break;
+ }
+
+ if(m_key_hash.size() > 0 && m_certs[i].subject_public_key_bitstring_sha1() == m_key_hash)
+ {
+ signing_cert = std::make_shared<const X509_Certificate>(m_certs[i]);
+ break;
+ }
+ }
+ }
+
+ if(!signing_cert)
+ return Certificate_Status_Code::OCSP_ISSUER_NOT_FOUND;
+
+ if(!signing_cert->allowed_usage(CRL_SIGN) &&
+ !signing_cert->allowed_extended_usage("PKIX.OCSPSigning"))
+ {
+ return Certificate_Status_Code::OCSP_RESPONSE_MISSING_KEYUSAGE;
+ }
+
+ return this->verify_signature(*signing_cert);
+ }
+
+Certificate_Status_Code Response::status_for(const X509_Certificate& issuer,
+ const X509_Certificate& subject,
+ std::chrono::system_clock::time_point ref_time,
+ std::chrono::seconds max_age) const
+ {
+ if(m_responses.empty())
+ { return m_dummy_response_status; }
+
+ for(const auto& response : m_responses)
+ {
+ if(response.certid().is_id_for(issuer, subject))
+ {
+ X509_Time x509_ref_time(ref_time);
+
+ 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; }
+
+ 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; }
+
+ if(response.cert_status() == 0)
+ { return Certificate_Status_Code::OCSP_RESPONSE_GOOD; }
+ else
+ { return Certificate_Status_Code::OCSP_BAD_STATUS; }
+ }
+ }
+
+ return Certificate_Status_Code::OCSP_CERT_NOT_LISTED;
+ }
+
+#if defined(BOTAN_HAS_HTTP_UTIL)
+
+Response online_check(const X509_Certificate& issuer,
+ const BigInt& subject_serial,
+ const std::string& ocsp_responder,
+ Certificate_Store* trusted_roots,
+ std::chrono::milliseconds timeout)
+ {
+ if(ocsp_responder.empty())
+ throw Invalid_Argument("No OCSP responder specified");
+
+ OCSP::Request req(issuer, subject_serial);
+
+ auto http = HTTP::POST_sync(ocsp_responder,
+ "application/ocsp-request",
+ req.BER_encode(),
+ 1,
+ timeout);
+
+ http.throw_unless_ok();
+
+ // Check the MIME type?
+
+ OCSP::Response response(http.body());
+
+ std::vector<Certificate_Store*> trusted_roots_vec;
+ trusted_roots_vec.push_back(trusted_roots);
+
+ if(trusted_roots)
+ response.check_signature(trusted_roots_vec);
+
+ return response;
+ }
+
+
+Response online_check(const X509_Certificate& issuer,
+ const X509_Certificate& subject,
+ Certificate_Store* trusted_roots,
+ std::chrono::milliseconds timeout)
+ {
+ if(subject.issuer_dn() != issuer.subject_dn())
+ throw Invalid_Argument("Invalid cert pair to OCSP::online_check (mismatched issuer,subject args?)");
+
+ return online_check(issuer,
+ BigInt::decode(subject.serial_number()),
+ subject.ocsp_responder(),
+ trusted_roots,
+ timeout);
+ }
+
+#endif
+
+}
+
+}
diff --git a/comm/third_party/botan/src/lib/x509/ocsp.h b/comm/third_party/botan/src/lib/x509/ocsp.h
new file mode 100644
index 0000000000..5522456442
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/ocsp.h
@@ -0,0 +1,282 @@
+/*
+* OCSP
+* (C) 2012 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_OCSP_H_
+#define BOTAN_OCSP_H_
+
+#include <botan/asn1_obj.h>
+#include <botan/pkix_types.h>
+#include <botan/x509cert.h>
+#include <botan/bigint.h>
+#include <chrono>
+
+namespace Botan {
+
+class Certificate_Store;
+
+namespace OCSP {
+
+class BOTAN_PUBLIC_API(2,0) CertID final : public ASN1_Object
+ {
+ public:
+ CertID() = default;
+
+ CertID(const X509_Certificate& issuer,
+ const BigInt& subject_serial);
+
+ bool is_id_for(const X509_Certificate& issuer,
+ const X509_Certificate& subject) const;
+
+ void encode_into(class DER_Encoder& to) const override;
+
+ void decode_from(class BER_Decoder& from) override;
+
+ const std::vector<uint8_t>& issuer_key_hash() const { return m_issuer_key_hash; }
+
+ private:
+ AlgorithmIdentifier m_hash_id;
+ std::vector<uint8_t> m_issuer_dn_hash;
+ std::vector<uint8_t> m_issuer_key_hash;
+ BigInt m_subject_serial;
+ };
+
+class BOTAN_PUBLIC_API(2,0) SingleResponse final : public ASN1_Object
+ {
+ public:
+ const CertID& certid() const { return m_certid; }
+
+ size_t cert_status() const { return m_cert_status; }
+
+ X509_Time this_update() const { return m_thisupdate; }
+
+ X509_Time next_update() const { return m_nextupdate; }
+
+ void encode_into(class DER_Encoder& to) const override;
+
+ void decode_from(class BER_Decoder& from) override;
+ private:
+ CertID m_certid;
+ size_t m_cert_status = 2; // unknown
+ X509_Time m_thisupdate;
+ X509_Time m_nextupdate;
+ };
+
+/**
+* An OCSP request.
+*/
+class BOTAN_PUBLIC_API(2,0) Request final
+ {
+ public:
+ /**
+ * Create an OCSP request.
+ * @param issuer_cert issuer certificate
+ * @param subject_cert subject certificate
+ */
+ Request(const X509_Certificate& issuer_cert,
+ const X509_Certificate& subject_cert);
+
+ Request(const X509_Certificate& issuer_cert,
+ const BigInt& subject_serial);
+
+ /**
+ * @return BER-encoded OCSP request
+ */
+ std::vector<uint8_t> BER_encode() const;
+
+ /**
+ * @return Base64-encoded OCSP request
+ */
+ std::string base64_encode() const;
+
+ /**
+ * @return issuer certificate
+ */
+ const X509_Certificate& issuer() const { return m_issuer; }
+
+ /**
+ * @return subject certificate
+ */
+ const X509_Certificate& subject() const { throw Not_Implemented("Method have been deprecated"); }
+
+ const std::vector<uint8_t>& issuer_key_hash() const
+ { return m_certid.issuer_key_hash(); }
+ private:
+ X509_Certificate m_issuer;
+ CertID m_certid;
+ };
+
+/**
+* OCSP response status.
+*
+* see https://tools.ietf.org/html/rfc6960#section-4.2.1
+*/
+enum class Response_Status_Code {
+ Successful = 0,
+ Malformed_Request = 1,
+ Internal_Error = 2,
+ Try_Later = 3,
+ Sig_Required = 5,
+ Unauthorized = 6
+};
+
+/**
+* OCSP response.
+*
+* Note this class is only usable as an OCSP client
+*/
+class BOTAN_PUBLIC_API(2,0) Response final
+ {
+ public:
+ /**
+ * Creates an empty OCSP response.
+ */
+ Response() = default;
+
+ /**
+ * 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);
+
+ /**
+ * Parses an OCSP response.
+ * @param response_bits response bits received
+ */
+ Response(const std::vector<uint8_t>& response_bits) :
+ Response(response_bits.data(), response_bits.size())
+ {}
+
+ /**
+ * Parses an OCSP response.
+ * @param response_bits response bits received
+ * @param response_bits_len length of response in bytes
+ */
+ Response(const uint8_t response_bits[],
+ size_t response_bits_len);
+
+ /**
+ * Check signature and return status
+ * The optional cert_path is the (already validated!) certificate path of
+ * the end entity which is being inquired about
+ * @param trust_roots list of certstores containing trusted roots
+ * @param cert_path optionally, the (already verified!) certificate path for the certificate
+ * this is an OCSP response for. This is necessary to find the correct intermediate CA in
+ * some cases.
+ */
+ Certificate_Status_Code check_signature(const std::vector<Certificate_Store*>& trust_roots,
+ const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path = {}) const;
+
+ /**
+ * Verify that issuer's key signed this response
+ * @param issuer certificate of issuer
+ * @return if signature valid OCSP_SIGNATURE_OK else an error code
+ */
+ Certificate_Status_Code verify_signature(const X509_Certificate& issuer) const;
+
+ /**
+ * @return the status of the response
+ */
+ Response_Status_Code status() const { return m_status; }
+
+ /**
+ * @return the time this OCSP response was supposedly produced at
+ */
+ const X509_Time& produced_at() const { return m_produced_at; }
+
+ /**
+ * @return DN of signer, if provided in response (may be empty)
+ */
+ const X509_DN& signer_name() const { return m_signer_name; }
+
+ /**
+ * @return key hash, if provided in response (may be empty)
+ */
+ const std::vector<uint8_t>& signer_key_hash() const { return m_key_hash; }
+
+ const std::vector<uint8_t>& raw_bits() const { return m_response_bits; }
+
+ /**
+ * Searches the OCSP response for issuer and subject certificate.
+ * @param issuer issuer certificate
+ * @param subject subject certificate
+ * @param ref_time the reference time
+ * @param max_age the maximum age the response should be considered valid
+ * if next_update is not set
+ * @return OCSP status code, possible values:
+ * CERT_IS_REVOKED,
+ * OCSP_NOT_YET_VALID,
+ * OCSP_HAS_EXPIRED,
+ * OCSP_IS_TOO_OLD,
+ * OCSP_RESPONSE_GOOD,
+ * OCSP_BAD_STATUS,
+ * OCSP_CERT_NOT_LISTED
+ */
+ Certificate_Status_Code status_for(const X509_Certificate& issuer,
+ const X509_Certificate& subject,
+ std::chrono::system_clock::time_point ref_time = std::chrono::system_clock::now(),
+ std::chrono::seconds max_age = std::chrono::seconds::zero()) const;
+
+ /**
+ * @return the certificate chain, if provided in response
+ */
+ const std::vector<X509_Certificate> &certificates() const { return m_certs; }
+
+ private:
+ Response_Status_Code m_status;
+ std::vector<uint8_t> m_response_bits;
+ X509_Time m_produced_at;
+ X509_DN m_signer_name;
+ std::vector<uint8_t> m_key_hash;
+ std::vector<uint8_t> m_tbs_bits;
+ AlgorithmIdentifier m_sig_algo;
+ std::vector<uint8_t> m_signature;
+ std::vector<X509_Certificate> m_certs;
+
+ std::vector<SingleResponse> m_responses;
+
+ Certificate_Status_Code m_dummy_response_status;
+ };
+
+#if defined(BOTAN_HAS_HTTP_UTIL)
+
+/**
+* Makes an online OCSP request via HTTP and returns the OCSP response.
+* @param issuer issuer certificate
+* @param subject_serial the subject's serial number
+* @param ocsp_responder the OCSP responder to query
+* @param trusted_roots trusted roots for the OCSP response
+* @param timeout a timeout on the HTTP request
+* @return OCSP response
+*/
+BOTAN_PUBLIC_API(2,1)
+Response online_check(const X509_Certificate& issuer,
+ const BigInt& subject_serial,
+ const std::string& ocsp_responder,
+ Certificate_Store* trusted_roots,
+ std::chrono::milliseconds timeout = std::chrono::milliseconds(3000));
+
+/**
+* Makes an online OCSP request via HTTP and returns the OCSP response.
+* @param issuer issuer certificate
+* @param subject subject certificate
+* @param trusted_roots trusted roots for the OCSP response
+* @param timeout a timeout on the HTTP request
+* @return OCSP response
+*/
+BOTAN_PUBLIC_API(2,0)
+Response online_check(const X509_Certificate& issuer,
+ const X509_Certificate& subject,
+ Certificate_Store* trusted_roots,
+ std::chrono::milliseconds timeout = std::chrono::milliseconds(3000));
+
+#endif
+
+}
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/x509/ocsp_types.cpp b/comm/third_party/botan/src/lib/x509/ocsp_types.cpp
new file mode 100644
index 0000000000..c4b78112eb
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/ocsp_types.cpp
@@ -0,0 +1,105 @@
+/*
+* OCSP subtypes
+* (C) 2012 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/ocsp.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
+#include <botan/x509_ext.h>
+#include <botan/hash.h>
+
+namespace Botan {
+
+namespace 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
+ */
+ std::unique_ptr<HashFunction> hash(HashFunction::create_or_throw("SHA-160"));
+
+ 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::decode(subject.serial_number()) != m_subject_serial)
+ return false;
+
+ const std::string hash_algo = m_hash_id.get_oid().to_formatted_string();
+ std::unique_ptr<HashFunction> 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(class DER_Encoder& to) const
+ {
+ to.start_cons(SEQUENCE)
+ .encode(m_hash_id)
+ .encode(m_issuer_dn_hash, OCTET_STRING)
+ .encode(m_issuer_key_hash, OCTET_STRING)
+ .encode(m_subject_serial)
+ .end_cons();
+ }
+
+void CertID::decode_from(class BER_Decoder& from)
+ {
+ from.start_cons(SEQUENCE)
+ .decode(m_hash_id)
+ .decode(m_issuer_dn_hash, OCTET_STRING)
+ .decode(m_issuer_key_hash, OCTET_STRING)
+ .decode(m_subject_serial)
+ .end_cons();
+
+ }
+
+void SingleResponse::encode_into(class DER_Encoder&) const
+ {
+ throw Not_Implemented("SingleResponse::encode_into");
+ }
+
+void SingleResponse::decode_from(class BER_Decoder& from)
+ {
+ BER_Object cert_status;
+ Extensions extensions;
+
+ from.start_cons(SEQUENCE)
+ .decode(m_certid)
+ .get_next(cert_status)
+ .decode(m_thisupdate)
+ .decode_optional(m_nextupdate, ASN1_Tag(0),
+ ASN1_Tag(CONTEXT_SPECIFIC | CONSTRUCTED))
+ .decode_optional(extensions,
+ ASN1_Tag(1),
+ ASN1_Tag(CONTEXT_SPECIFIC | CONSTRUCTED))
+ .end_cons();
+
+ m_cert_status = cert_status.type();
+ }
+
+}
+
+}
diff --git a/comm/third_party/botan/src/lib/x509/ocsp_types.h b/comm/third_party/botan/src/lib/x509/ocsp_types.h
new file mode 100644
index 0000000000..aa628d8d60
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/ocsp_types.h
@@ -0,0 +1,11 @@
+/*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_OCSP_TYPES_H_
+#define BOTAN_OCSP_TYPES_H_
+
+#include <botan/ocsp.h>
+BOTAN_DEPRECATED_HEADER(ocsp_types.h)
+
+#endif
diff --git a/comm/third_party/botan/src/lib/x509/pkcs10.cpp b/comm/third_party/botan/src/lib/x509/pkcs10.cpp
new file mode 100644
index 0000000000..1da5ecca48
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/pkcs10.cpp
@@ -0,0 +1,304 @@
+/*
+* PKCS #10
+* (C) 1999-2007,2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/pkcs10.h>
+#include <botan/x509_key.h>
+#include <botan/x509_ext.h>
+#include <botan/x509cert.h>
+#include <botan/ber_dec.h>
+#include <botan/der_enc.h>
+#include <botan/pubkey.h>
+#include <botan/oids.h>
+#include <botan/pem.h>
+
+namespace Botan {
+
+struct PKCS10_Data
+ {
+ X509_DN m_subject_dn;
+ std::vector<uint8_t> m_public_key_bits;
+ AlternativeName m_alt_name;
+ std::string m_challenge;
+ Extensions m_extensions;
+ };
+
+std::string PKCS10_Request::PEM_label() const
+ {
+ return "CERTIFICATE REQUEST";
+ }
+
+std::vector<std::string> PKCS10_Request::alternate_PEM_labels() const
+ {
+ return { "NEW CERTIFICATE REQUEST" };
+ }
+
+PKCS10_Request::PKCS10_Request(DataSource& src)
+ {
+ load_data(src);
+ }
+
+PKCS10_Request::PKCS10_Request(const std::vector<uint8_t>& vec)
+ {
+ DataSource_Memory src(vec.data(), vec.size());
+ load_data(src);
+ }
+
+#if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
+PKCS10_Request::PKCS10_Request(const std::string& fsname)
+ {
+ DataSource_Stream src(fsname, true);
+ load_data(src);
+ }
+#endif
+
+//static
+PKCS10_Request PKCS10_Request::create(const Private_Key& key,
+ const X509_DN& subject_dn,
+ const Extensions& extensions,
+ const std::string& hash_fn,
+ RandomNumberGenerator& rng,
+ const std::string& padding_scheme,
+ const std::string& challenge)
+ {
+ AlgorithmIdentifier sig_algo;
+ std::unique_ptr<PK_Signer> signer = choose_sig_format(sig_algo, key, rng, hash_fn, padding_scheme);
+
+ const size_t PKCS10_VERSION = 0;
+
+ DER_Encoder tbs_req;
+
+ tbs_req.start_cons(SEQUENCE)
+ .encode(PKCS10_VERSION)
+ .encode(subject_dn)
+ .raw_bytes(key.subject_public_key())
+ .start_explicit(0);
+
+ if(challenge.empty() == false)
+ {
+ std::vector<uint8_t> value;
+ DER_Encoder(value).encode(ASN1_String(challenge, DIRECTORY_STRING));
+ tbs_req.encode(Attribute("PKCS9.ChallengePassword", value));
+ }
+
+ std::vector<uint8_t> extension_req;
+ DER_Encoder(extension_req).start_cons(SEQUENCE).encode(extensions).end_cons();
+ tbs_req.encode(Attribute("PKCS9.ExtensionRequest", extension_req));
+
+ // end the start_explicit above
+ tbs_req.end_explicit().end_cons();
+
+ const std::vector<uint8_t> req =
+ X509_Object::make_signed(signer.get(), rng, sig_algo,
+ tbs_req.get_contents());
+
+ return PKCS10_Request(req);
+ }
+
+/*
+* Decode the CertificateRequestInfo
+*/
+namespace {
+
+std::unique_ptr<PKCS10_Data> decode_pkcs10(const std::vector<uint8_t>& body)
+ {
+ std::unique_ptr<PKCS10_Data> data(new PKCS10_Data);
+
+ BER_Decoder cert_req_info(body);
+
+ size_t version;
+ cert_req_info.decode(version);
+ if(version != 0)
+ throw Decoding_Error("Unknown version code in PKCS #10 request: " +
+ std::to_string(version));
+
+ cert_req_info.decode(data->m_subject_dn);
+
+ BER_Object public_key = cert_req_info.get_next_object();
+ if(public_key.is_a(SEQUENCE, CONSTRUCTED) == false)
+ 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();
+
+ std::set<std::string> pkcs9_email;
+
+ if(attr_bits.is_a(0, ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC)))
+ {
+ BER_Decoder attributes(attr_bits);
+ while(attributes.more_items())
+ {
+ Attribute attr;
+ attributes.decode(attr);
+
+ const OID& oid = attr.get_oid();
+ BER_Decoder value(attr.get_parameters());
+
+ if(oid == OID::from_string("PKCS9.EmailAddress"))
+ {
+ ASN1_String email;
+ value.decode(email);
+ pkcs9_email.insert(email.value());
+ }
+ else if(oid == OID::from_string("PKCS9.ChallengePassword"))
+ {
+ ASN1_String challenge_password;
+ value.decode(challenge_password);
+ data->m_challenge = challenge_password.value();
+ }
+ else if(oid == OID::from_string("PKCS9.ExtensionRequest"))
+ {
+ value.decode(data->m_extensions).verify_end();
+ }
+ }
+ attributes.verify_end();
+ }
+ else if(attr_bits.is_set())
+ throw BER_Bad_Tag("PKCS10_Request: Unexpected tag for attributes", attr_bits.tagging());
+
+ cert_req_info.verify_end();
+
+ if(auto ext = data->m_extensions.get_extension_object_as<Cert_Extension::Subject_Alternative_Name>())
+ {
+ data->m_alt_name = ext->get_alt_name();
+ }
+
+ for(std::string email : pkcs9_email)
+ {
+ data->m_alt_name.add_attribute("RFC882", email);
+ }
+
+ return data;
+ }
+
+}
+
+void PKCS10_Request::force_decode()
+ {
+ m_data.reset();
+
+ std::unique_ptr<PKCS10_Data> data = decode_pkcs10(signed_body());
+
+ m_data.reset(data.release());
+
+ if(!this->check_signature(subject_public_key()))
+ throw Decoding_Error("PKCS #10 request: Bad signature detected");
+ }
+
+const PKCS10_Data& PKCS10_Request::data() const
+ {
+ if(m_data == nullptr)
+ throw Decoding_Error("PKCS10_Request decoding failed");
+ return *m_data.get();
+ }
+
+/*
+* Return the challenge password (if any)
+*/
+std::string PKCS10_Request::challenge_password() const
+ {
+ return data().m_challenge;
+ }
+
+/*
+* Return the name of the requestor
+*/
+const X509_DN& PKCS10_Request::subject_dn() const
+ {
+ return data().m_subject_dn;
+ }
+
+/*
+* Return the public key of the requestor
+*/
+const std::vector<uint8_t>& PKCS10_Request::raw_public_key() const
+ {
+ return data().m_public_key_bits;
+ }
+
+/*
+* Return the public key of the requestor
+*/
+Public_Key* PKCS10_Request::subject_public_key() const
+ {
+ DataSource_Memory source(raw_public_key());
+ return X509::load_key(source);
+ }
+
+/*
+* Return the alternative names of the requestor
+*/
+const AlternativeName& PKCS10_Request::subject_alt_name() const
+ {
+ return data().m_alt_name;
+ }
+
+/*
+* Return the X509v3 extensions
+*/
+const Extensions& PKCS10_Request::extensions() const
+ {
+ return data().m_extensions;
+ }
+
+/*
+* 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<Cert_Extension::Key_Usage&>(*ext).get_constraints();
+ }
+
+ return NO_CONSTRAINTS;
+ }
+
+/*
+* Return the extendend key constraints (if any)
+*/
+std::vector<OID> PKCS10_Request::ex_constraints() const
+ {
+ if(auto ext = extensions().get(OID::from_string("X509v3.ExtendedKeyUsage")))
+ {
+ return dynamic_cast<Cert_Extension::Extended_Key_Usage&>(*ext).get_oids();
+ }
+
+ 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<Cert_Extension::Basic_Constraints&>(*ext).get_is_ca();
+ }
+
+ return false;
+ }
+
+/*
+* Return the desired path limit (if any)
+*/
+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<Cert_Extension::Basic_Constraints&>(*ext);
+ if(basic_constraints.get_is_ca())
+ {
+ return basic_constraints.get_path_limit();
+ }
+ }
+
+ return 0;
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/x509/pkcs10.h b/comm/third_party/botan/src/lib/x509/pkcs10.h
new file mode 100644
index 0000000000..3f3f87357d
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/pkcs10.h
@@ -0,0 +1,148 @@
+/*
+* PKCS #10
+* (C) 1999-2007 Jack Lloyd
+* (C) 2016 René Korthaus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_PKCS10_H_
+#define BOTAN_PKCS10_H_
+
+#include <botan/x509_obj.h>
+#include <botan/pkix_enums.h>
+#include <vector>
+
+namespace Botan {
+
+struct PKCS10_Data;
+
+class Private_Key;
+class Extensions;
+class X509_DN;
+class AlternativeName;
+
+/**
+* PKCS #10 Certificate Request.
+*/
+class BOTAN_PUBLIC_API(2,0) PKCS10_Request final : public X509_Object
+ {
+ public:
+ /**
+ * Get the subject public key.
+ * @return subject public key
+ */
+ Public_Key* subject_public_key() const;
+
+ /**
+ * Get the raw DER encoded public key.
+ * @return raw DER encoded public key
+ */
+ const std::vector<uint8_t>& raw_public_key() const;
+
+ /**
+ * Get the subject DN.
+ * @return subject DN
+ */
+ const X509_DN& subject_dn() const;
+
+ /**
+ * Get the subject alternative name.
+ * @return subject alternative name.
+ */
+ const AlternativeName& subject_alt_name() const;
+
+ /**
+ * Get the key constraints for the key associated with this
+ * PKCS#10 object.
+ * @return key constraints
+ */
+ Key_Constraints constraints() const;
+
+ /**
+ * Get the extendend key constraints (if any).
+ * @return extended key constraints
+ */
+ std::vector<OID> ex_constraints() const;
+
+ /**
+ * Find out whether this is a CA request.
+ * @result true if it is a CA request, false otherwise.
+ */
+ bool is_CA() const;
+
+ /**
+ * Return the constraint on the path length defined
+ * in the BasicConstraints extension.
+ * @return path limit
+ */
+ size_t path_limit() const;
+
+ /**
+ * Get the challenge password for this request
+ * @return challenge password for this request
+ */
+ std::string challenge_password() const;
+
+ /**
+ * Get the X509v3 extensions.
+ * @return X509v3 extensions
+ */
+ const Extensions& extensions() const;
+
+ /**
+ * Create a PKCS#10 Request from a data source.
+ * @param source the data source providing the DER encoded request
+ */
+ explicit PKCS10_Request(DataSource& source);
+
+#if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
+ /**
+ * Create a PKCS#10 Request from a file.
+ * @param filename the name of the file containing the DER or PEM
+ * encoded request file
+ */
+ explicit PKCS10_Request(const std::string& filename);
+#endif
+
+ /**
+ * Create a PKCS#10 Request from binary data.
+ * @param vec a std::vector containing the DER value
+ */
+ explicit PKCS10_Request(const std::vector<uint8_t>& vec);
+
+ /**
+ * Create a new PKCS10 certificate request
+ * @param key the key that will be included in the certificate request
+ * @param subject_dn the DN to be placed in the request
+ * @param extensions extensions to include in the request
+ * @param hash_fn the hash function to use to create the signature
+ * @param rng a random number generator
+ * @param padding_scheme if set specifies the padding scheme, otherwise an
+ * algorithm-specific default is used.
+ * @param challenge a challenge string to be included in the PKCS10 request,
+ * sometimes used for revocation purposes.
+ */
+ static PKCS10_Request create(const Private_Key& key,
+ const X509_DN& subject_dn,
+ const Extensions& extensions,
+ const std::string& hash_fn,
+ RandomNumberGenerator& rng,
+ const std::string& padding_scheme = "",
+ const std::string& challenge = "");
+
+ private:
+ std::string PEM_label() const override;
+
+ std::vector<std::string> alternate_PEM_labels() const override;
+
+ void force_decode() override;
+
+ const PKCS10_Data& data() const;
+
+ std::shared_ptr<PKCS10_Data> m_data;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/x509/pkix_enums.h b/comm/third_party/botan/src/lib/x509/pkix_enums.h
new file mode 100644
index 0000000000..a1c85293b8
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/pkix_enums.h
@@ -0,0 +1,143 @@
+/*
+* (C) 2013 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_X509_PKIX_ENUMS_H_
+#define BOTAN_X509_PKIX_ENUMS_H_
+
+#include <botan/types.h>
+
+namespace Botan {
+
+/**
+* Certificate validation status code
+*/
+enum class Certificate_Status_Code {
+ OK = 0,
+ VERIFIED = 0,
+
+ // Revocation status
+ OCSP_RESPONSE_GOOD = 1,
+ OCSP_SIGNATURE_OK = 2,
+ VALID_CRL_CHECKED = 3,
+ OCSP_NO_HTTP = 4,
+
+ // Warnings
+ FIRST_WARNING_STATUS = 500,
+ CERT_SERIAL_NEGATIVE = 500,
+ DN_TOO_LONG = 501,
+ OCSP_NO_REVOCATION_URL = 502,
+ OCSP_SERVER_NOT_AVAILABLE = 503,
+
+ // Typo versions of above - will be removed in future major release
+ OSCP_NO_REVOCATION_URL = 502,
+ OSCP_SERVER_NOT_AVAILABLE = 503,
+
+ // Errors
+ FIRST_ERROR_STATUS = 1000,
+
+ SIGNATURE_METHOD_TOO_WEAK = 1000,
+ UNTRUSTED_HASH = 1001,
+ NO_REVOCATION_DATA = 1002,
+ NO_MATCHING_CRLDP = 1003,
+
+ // Time problems
+ CERT_NOT_YET_VALID = 2000,
+ CERT_HAS_EXPIRED = 2001,
+ OCSP_NOT_YET_VALID = 2002,
+ OCSP_HAS_EXPIRED = 2003,
+ CRL_NOT_YET_VALID = 2004,
+ CRL_HAS_EXPIRED = 2005,
+ OCSP_IS_TOO_OLD = 2006,
+
+ // Chain generation problems
+ CERT_ISSUER_NOT_FOUND = 3000,
+ CANNOT_ESTABLISH_TRUST = 3001,
+ CERT_CHAIN_LOOP = 3002,
+ CHAIN_LACKS_TRUST_ROOT = 3003,
+ CHAIN_NAME_MISMATCH = 3004,
+
+ // Validation errors
+ POLICY_ERROR = 4000,
+ INVALID_USAGE = 4001,
+ CERT_CHAIN_TOO_LONG = 4002,
+ CA_CERT_NOT_FOR_CERT_ISSUER = 4003,
+ NAME_CONSTRAINT_ERROR = 4004,
+
+ // Revocation errors
+ CA_CERT_NOT_FOR_CRL_ISSUER = 4005,
+ OCSP_CERT_NOT_LISTED = 4006,
+ OCSP_BAD_STATUS = 4007,
+
+ // Other problems
+ CERT_NAME_NOMATCH = 4008,
+ UNKNOWN_CRITICAL_EXTENSION = 4009,
+ DUPLICATE_CERT_EXTENSION = 4010,
+ OCSP_SIGNATURE_ERROR = 4501,
+ OCSP_ISSUER_NOT_FOUND = 4502,
+ OCSP_RESPONSE_MISSING_KEYUSAGE = 4503,
+ OCSP_RESPONSE_INVALID = 4504,
+ EXT_IN_V1_V2_CERT = 4505,
+ DUPLICATE_CERT_POLICY = 4506,
+ V2_IDENTIFIERS_IN_V1_CERT = 4507,
+
+ // Hard failures
+ CERT_IS_REVOKED = 5000,
+ CRL_BAD_SIGNATURE = 5001,
+ SIGNATURE_ERROR = 5002,
+ CERT_PUBKEY_INVALID = 5003,
+ SIGNATURE_ALGO_UNKNOWN = 5004,
+ SIGNATURE_ALGO_BAD_PARAMS = 5005
+};
+
+/**
+* Convert a status code to a human readable diagnostic message
+* @param code the certifcate status
+* @return string literal constant, or nullptr if code unknown
+*/
+BOTAN_PUBLIC_API(2,0) const char* to_string(Certificate_Status_Code code);
+
+/**
+* X.509v3 Key Constraints.
+* If updating update copy in ffi.h
+*/
+enum Key_Constraints {
+ NO_CONSTRAINTS = 0,
+ DIGITAL_SIGNATURE = 1 << 15,
+ NON_REPUDIATION = 1 << 14,
+ KEY_ENCIPHERMENT = 1 << 13,
+ DATA_ENCIPHERMENT = 1 << 12,
+ KEY_AGREEMENT = 1 << 11,
+ KEY_CERT_SIGN = 1 << 10,
+ CRL_SIGN = 1 << 9,
+ ENCIPHER_ONLY = 1 << 8,
+ DECIPHER_ONLY = 1 << 7
+};
+
+/**
+* X.509v2 CRL Reason Code.
+* This will become an enum class in a future major release
+*/
+enum CRL_Code : uint32_t {
+ UNSPECIFIED = 0,
+ KEY_COMPROMISE = 1,
+ CA_COMPROMISE = 2,
+ AFFILIATION_CHANGED = 3,
+ SUPERSEDED = 4,
+ CESSATION_OF_OPERATION = 5,
+ CERTIFICATE_HOLD = 6,
+ REMOVE_FROM_CRL = 8,
+ PRIVLEDGE_WITHDRAWN = 9,
+ PRIVILEGE_WITHDRAWN = 9,
+ AA_COMPROMISE = 10,
+
+ DELETE_CRL_ENTRY = 0xFF00,
+ OCSP_GOOD = 0xFF01,
+ OCSP_UNKNOWN = 0xFF02
+};
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/x509/pkix_types.h b/comm/third_party/botan/src/lib/x509/pkix_types.h
new file mode 100644
index 0000000000..983121955f
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/pkix_types.h
@@ -0,0 +1,613 @@
+/*
+* (C) 1999-2010,2012,2018,2020 Jack Lloyd
+* (C) 2007 Yves Jerschow
+* (C) 2015 Kai Michaelis
+* (C) 2016 René Korthaus, Rohde & Schwarz Cybersecurity
+* (C) 2017 Fabian Weissberg, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_PKIX_TYPES_H_
+#define BOTAN_PKIX_TYPES_H_
+
+#include <botan/asn1_obj.h>
+#include <botan/pkix_enums.h>
+#include <vector>
+#include <string>
+#include <iosfwd>
+#include <map>
+#include <set>
+
+namespace Botan {
+
+class X509_Certificate;
+class Data_Store;
+class Public_Key;
+
+/**
+* Check that key constraints are permitted for a specific public key.
+* @param pub_key the public key on which the constraints shall be enforced on
+* @param constraints the constraints that shall be enforced on the key
+* @throw Invalid_Argument if the given constraints are not permitted for this key
+*/
+BOTAN_PUBLIC_API(2,0) void verify_cert_constraints_valid_for_key_type(const Public_Key& pub_key,
+ Key_Constraints constraints);
+
+std::string BOTAN_PUBLIC_API(2,0) key_constraints_to_string(Key_Constraints);
+
+/**
+* Distinguished Name
+*/
+class BOTAN_PUBLIC_API(2,0) X509_DN final : public ASN1_Object
+ {
+ public:
+ X509_DN() = default;
+
+ explicit X509_DN(const std::multimap<OID, std::string>& args)
+ {
+ for(auto i : args)
+ add_attribute(i.first, i.second);
+ }
+
+ explicit X509_DN(const std::multimap<std::string, std::string>& args)
+ {
+ for(auto i : args)
+ add_attribute(i.first, i.second);
+ }
+
+ void encode_into(DER_Encoder&) const override;
+ void decode_from(BER_Decoder&) override;
+
+ bool has_field(const OID& oid) const;
+ ASN1_String get_first_attribute(const OID& oid) const;
+
+ /*
+ * Return the BER encoded data, if any
+ */
+ const std::vector<uint8_t>& get_bits() const { return m_dn_bits; }
+
+ bool empty() const { return m_rdn.empty(); }
+
+ std::string to_string() const;
+
+ const std::vector<std::pair<OID,ASN1_String>>& dn_info() const { return m_rdn; }
+
+ std::multimap<OID, std::string> get_attributes() const;
+ std::multimap<std::string, std::string> contents() const;
+
+ bool has_field(const std::string& attr) const;
+ std::vector<std::string> get_attribute(const std::string& attr) const;
+ std::string get_first_attribute(const std::string& attr) const;
+
+ void add_attribute(const std::string& key, const std::string& val);
+
+ void add_attribute(const OID& oid, const std::string& val)
+ {
+ add_attribute(oid, ASN1_String(val));
+ }
+
+ void add_attribute(const OID& oid, const ASN1_String& val);
+
+ static std::string deref_info_field(const std::string& key);
+
+ /**
+ * Lookup upper bounds in characters for the length of distinguished name fields
+ * as given in RFC 5280, Appendix A.
+ *
+ * @param oid the oid of the DN to lookup
+ * @return the upper bound, or zero if no ub is known to Botan
+ */
+ static size_t lookup_ub(const OID& oid);
+
+ private:
+ std::vector<std::pair<OID,ASN1_String>> m_rdn;
+ std::vector<uint8_t> m_dn_bits;
+ };
+
+bool BOTAN_PUBLIC_API(2,0) operator==(const X509_DN& dn1, const X509_DN& dn2);
+bool BOTAN_PUBLIC_API(2,0) operator!=(const X509_DN& dn1, const X509_DN& dn2);
+
+/*
+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
+*/
+bool BOTAN_PUBLIC_API(2,0) operator<(const X509_DN& dn1, const X509_DN& dn2);
+
+BOTAN_PUBLIC_API(2,0) std::ostream& operator<<(std::ostream& out, const X509_DN& dn);
+BOTAN_PUBLIC_API(2,0) std::istream& operator>>(std::istream& in, X509_DN& dn);
+
+/**
+* Alternative Name
+*/
+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;
+
+ std::multimap<std::string, std::string> contents() const;
+
+ bool has_field(const std::string& attr) const;
+ std::vector<std::string> get_attribute(const std::string& attr) const;
+
+ std::string get_first_attribute(const std::string& attr) const;
+
+ void add_attribute(const std::string& type, const std::string& value);
+ void add_othername(const OID& oid, const std::string& value, ASN1_Tag type);
+
+ const std::multimap<std::string, std::string>& get_attributes() const
+ {
+ return m_alt_info;
+ }
+
+ const std::multimap<OID, ASN1_String>& get_othernames() const
+ {
+ return m_othernames;
+ }
+
+ X509_DN dn() const;
+
+ bool has_items() const;
+
+ AlternativeName(const std::string& email_addr = "",
+ const std::string& uri = "",
+ const std::string& dns = "",
+ const std::string& ip_address = "");
+ private:
+ std::multimap<std::string, std::string> m_alt_info;
+ std::multimap<OID, ASN1_String> m_othernames;
+ };
+
+/**
+* Attribute
+*/
+class BOTAN_PUBLIC_API(2,0) Attribute final : public ASN1_Object
+ {
+ public:
+ void encode_into(DER_Encoder& to) const override;
+ void decode_from(BER_Decoder& from) override;
+
+ Attribute() = default;
+ Attribute(const OID&, const std::vector<uint8_t>&);
+ Attribute(const std::string&, const std::vector<uint8_t>&);
+
+ const OID& get_oid() const { return oid; }
+
+ const std::vector<uint8_t>& get_parameters() const { return parameters; }
+
+ BOTAN_DEPRECATED_PUBLIC_MEMBER_VARIABLES:
+ /*
+ * These values are public for historical reasons, but in a future release
+ * they will be made private. Do not access them.
+ */
+ OID oid;
+ std::vector<uint8_t> parameters;
+ };
+
+/**
+* @brief X.509 GeneralName Type
+*
+* 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).
+*/
+class BOTAN_PUBLIC_API(2,0) GeneralName final : public ASN1_Object
+ {
+ public:
+ enum MatchResult : int
+ {
+ All,
+ Some,
+ None,
+ NotFound,
+ UnknownType,
+ };
+
+ /**
+ * Creates an empty GeneralName.
+ */
+ GeneralName() = default;
+
+ /**
+ * Creates a new GeneralName for its string format.
+ * @param str type and name, colon-separated, e.g., "DNS:google.com"
+ */
+ GeneralName(const std::string& str);
+
+ void encode_into(DER_Encoder&) const override;
+
+ void decode_from(BER_Decoder&) override;
+
+ /**
+ * @return Type of the name. Can be DN, DNS, IP, RFC822 or URI.
+ */
+ const std::string& type() const { return m_type; }
+
+ /**
+ * @return The name as string. Format depends on type.
+ */
+ const std::string& name() const { return m_name; }
+
+ /**
+ * Checks whether a given certificate (partially) matches this name.
+ * @param cert certificate to be matched
+ * @return the match result
+ */
+ MatchResult matches(const X509_Certificate& cert) const;
+
+ private:
+ std::string m_type;
+ std::string m_name;
+
+ bool matches_dns(const std::string&) const;
+ bool matches_dn(const std::string&) const;
+ bool matches_ip(const std::string&) const;
+ };
+
+std::ostream& operator<<(std::ostream& os, const GeneralName& gn);
+
+/**
+* @brief A single Name Constraint
+*
+* The Name Constraint extension adds a minimum and maximum path
+* length to a GeneralName to form a constraint. The length limits
+* are currently unused.
+*/
+class BOTAN_PUBLIC_API(2,0) GeneralSubtree final : public ASN1_Object
+ {
+ public:
+ /**
+ * Creates an empty name constraint.
+ */
+ GeneralSubtree() : m_base(), m_minimum(0), m_maximum(std::numeric_limits<std::size_t>::max())
+ {}
+
+ /***
+ * Creates a new name constraint.
+ * @param base name
+ * @param min minimum path length
+ * @param max maximum path length
+ */
+ GeneralSubtree(const GeneralName& base, size_t min, size_t max)
+ : m_base(base), m_minimum(min), m_maximum(max)
+ {}
+
+ /**
+ * Creates a new name constraint for its string format.
+ * @param str name constraint
+ */
+ GeneralSubtree(const std::string& str);
+
+ void encode_into(DER_Encoder&) const override;
+
+ void decode_from(BER_Decoder&) override;
+
+ /**
+ * @return name
+ */
+ const GeneralName& base() const { return m_base; }
+
+ /**
+ * @return minimum path length
+ */
+ size_t minimum() const { return m_minimum; }
+
+ /**
+ * @return maximum path length
+ */
+ size_t maximum() const { return m_maximum; }
+
+ private:
+ GeneralName m_base;
+ size_t m_minimum;
+ size_t m_maximum;
+ };
+
+std::ostream& operator<<(std::ostream& os, const GeneralSubtree& gs);
+
+/**
+* @brief Name Constraints
+*
+* Wraps the Name Constraints associated with a certificate.
+*/
+class BOTAN_PUBLIC_API(2,0) NameConstraints final
+ {
+ public:
+ /**
+ * Creates an empty name NameConstraints.
+ */
+ NameConstraints() : m_permitted_subtrees(), m_excluded_subtrees() {}
+
+ /**
+ * Creates NameConstraints from a list of permitted and excluded subtrees.
+ * @param permitted_subtrees names for which the certificate is permitted
+ * @param excluded_subtrees names for which the certificate is not permitted
+ */
+ NameConstraints(std::vector<GeneralSubtree>&& permitted_subtrees,
+ std::vector<GeneralSubtree>&& excluded_subtrees)
+ : m_permitted_subtrees(permitted_subtrees), m_excluded_subtrees(excluded_subtrees)
+ {}
+
+ /**
+ * @return permitted names
+ */
+ const std::vector<GeneralSubtree>& permitted() const { return m_permitted_subtrees; }
+
+ /**
+ * @return excluded names
+ */
+ const std::vector<GeneralSubtree>& excluded() const { return m_excluded_subtrees; }
+
+ private:
+ std::vector<GeneralSubtree> m_permitted_subtrees;
+ std::vector<GeneralSubtree> m_excluded_subtrees;
+ };
+
+/**
+* X.509 Certificate Extension
+*/
+class BOTAN_PUBLIC_API(2,0) Certificate_Extension
+ {
+ public:
+ /**
+ * @return OID representing this extension
+ */
+ virtual OID oid_of() const = 0;
+
+ /*
+ * @return specific OID name
+ * If possible OIDS table should match oid_name to OIDS, ie
+ * OID::from_string(ext->oid_name()) == ext->oid_of()
+ * Should return empty string if OID is not known
+ */
+ virtual std::string oid_name() const = 0;
+
+ /**
+ * Make a copy of this extension
+ * @return copy of this
+ */
+ virtual Certificate_Extension* copy() const = 0;
+
+ /*
+ * Add the contents of this extension into the information
+ * for the subject and/or issuer, as necessary.
+ * @param subject the subject info
+ * @param issuer the issuer info
+ */
+ virtual void contents_to(Data_Store& subject,
+ Data_Store& issuer) const = 0;
+
+ /*
+ * Callback visited during path validation.
+ *
+ * An extension can implement this callback to inspect
+ * the path during path validation.
+ *
+ * If an error occurs during validation of this extension,
+ * 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 cert_path Certificate path which is currently validated
+ * @param pos Position of subject certificate in cert_path
+ */
+ virtual void validate(const X509_Certificate& subject, const X509_Certificate& issuer,
+ const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path,
+ std::vector<std::set<Certificate_Status_Code>>& cert_status,
+ size_t pos);
+
+ virtual ~Certificate_Extension() = default;
+ protected:
+ friend class Extensions;
+ virtual bool should_encode() const { return true; }
+ virtual std::vector<uint8_t> encode_inner() const = 0;
+ virtual void decode_inner(const std::vector<uint8_t>&) = 0;
+ };
+
+/**
+* X.509 Certificate Extension List
+*/
+class BOTAN_PUBLIC_API(2,0) Extensions final : public ASN1_Object
+ {
+ public:
+ /**
+ * Look up an object in the extensions, based on OID Returns
+ * nullptr if not set, if the extension was either absent or not
+ * handled. The pointer returned is owned by the Extensions
+ * object.
+ * This would be better with an optional<T> return value
+ */
+ const Certificate_Extension* get_extension_object(const OID& oid) const;
+
+ template<typename T>
+ const T* get_extension_object_as(const OID& oid = T::static_oid()) const
+ {
+ if(const Certificate_Extension* extn = get_extension_object(oid))
+ {
+ // Unknown_Extension oid_name is empty
+ if(extn->oid_name().empty())
+ {
+ return nullptr;
+ }
+ else if(const T* extn_as_T = dynamic_cast<const T*>(extn))
+ {
+ return extn_as_T;
+ }
+ else
+ {
+ throw Decoding_Error("Exception::get_extension_object_as dynamic_cast failed");
+ }
+ }
+
+ return nullptr;
+ }
+
+ /**
+ * Return the set of extensions in the order they appeared in the certificate
+ * (or as they were added, if constructed)
+ */
+ const std::vector<OID>& get_extension_oids() const
+ {
+ return m_extension_oids;
+ }
+
+ /**
+ * Return true if an extension was set
+ */
+ bool extension_set(const OID& oid) const;
+
+ /**
+ * Return true if an extesion was set and marked critical
+ */
+ bool critical_extension_set(const OID& oid) const;
+
+ /**
+ * Return the raw bytes of the extension
+ * Will throw if OID was not set as an extension.
+ */
+ std::vector<uint8_t> get_extension_bits(const OID& oid) const;
+
+ void encode_into(class DER_Encoder&) const override;
+ void decode_from(class BER_Decoder&) override;
+ void contents_to(Data_Store&, Data_Store&) const;
+
+ /**
+ * Adds a new extension to the list.
+ * @param extn pointer to the certificate extension (Extensions takes ownership)
+ * @param critical whether this extension should be marked as critical
+ * @throw Invalid_Argument if the extension is already present in the list
+ */
+ void add(Certificate_Extension* extn, bool critical = false);
+
+ /**
+ * Adds a new extension to the list unless it already exists. If the extension
+ * already exists within the Extensions object, the extn pointer will be deleted.
+ *
+ * @param extn pointer to the certificate extension (Extensions takes ownership)
+ * @param critical whether this extension should be marked as critical
+ * @return true if the object was added false if the extension was already used
+ */
+ bool add_new(Certificate_Extension* extn, bool critical = false);
+
+ /**
+ * Adds an extension to the list or replaces it.
+ * @param extn the certificate extension
+ * @param critical whether this extension should be marked as critical
+ */
+ void replace(Certificate_Extension* extn, bool critical = false);
+
+ /**
+ * Remove an extension from the list. Returns true if the
+ * extension had been set, false otherwise.
+ */
+ bool remove(const OID& oid);
+
+ /**
+ * Searches for an extension by OID and returns the result.
+ * Only the known extensions types declared in this header
+ * are searched for by this function.
+ * @return Copy of extension with oid, nullptr if not found.
+ * Can avoid creating a copy by using get_extension_object function
+ */
+ std::unique_ptr<Certificate_Extension> get(const OID& oid) const;
+
+ /**
+ * Searches for an extension by OID and returns the result decoding
+ * it to some arbitrary extension type chosen by the application.
+ *
+ * Only the unknown extensions, that is, extensions types that
+ * are not declared in this header, are searched for by this
+ * function.
+ *
+ * @return Pointer to new extension with oid, nullptr if not found.
+ */
+ template<typename T>
+ std::unique_ptr<T> get_raw(const OID& oid) const
+ {
+ auto extn_info = m_extension_info.find(oid);
+
+ if(extn_info != m_extension_info.end())
+ {
+ // Unknown_Extension oid_name is empty
+ if(extn_info->second.obj().oid_name() == "")
+ {
+ std::unique_ptr<T> ext(new T);
+ ext->decode_inner(extn_info->second.bits());
+ return ext;
+ }
+ }
+ return nullptr;
+ }
+
+ /**
+ * Returns a copy of the list of extensions together with the corresponding
+ * criticality flag. All extensions are encoded as some object, falling back
+ * to Unknown_Extension class which simply allows reading the bytes as well
+ * as the criticality flag.
+ */
+ std::vector<std::pair<std::unique_ptr<Certificate_Extension>, bool>> extensions() const;
+
+ /**
+ * Returns the list of extensions as raw, encoded bytes
+ * together with the corresponding criticality flag.
+ * Contains all extensions, including any extensions encoded as Unknown_Extension
+ */
+ std::map<OID, std::pair<std::vector<uint8_t>, bool>> extensions_raw() const;
+
+ Extensions() {}
+
+ Extensions(const Extensions&) = default;
+ Extensions& operator=(const Extensions&) = default;
+
+ Extensions(Extensions&&) = default;
+ Extensions& operator=(Extensions&&) = default;
+
+ private:
+ static std::unique_ptr<Certificate_Extension>
+ create_extn_obj(const OID& oid,
+ bool critical,
+ const std::vector<uint8_t>& body);
+
+ class Extensions_Info
+ {
+ public:
+ Extensions_Info(bool critical,
+ Certificate_Extension* ext) :
+ m_obj(ext),
+ m_bits(m_obj->encode_inner()),
+ m_critical(critical)
+ {
+ }
+
+ Extensions_Info(bool critical,
+ const std::vector<uint8_t>& encoding,
+ Certificate_Extension* ext) :
+ m_obj(ext),
+ m_bits(encoding),
+ m_critical(critical)
+ {
+ }
+
+ bool is_critical() const { return m_critical; }
+ const std::vector<uint8_t>& bits() const { return m_bits; }
+ const Certificate_Extension& obj() const
+ {
+ BOTAN_ASSERT_NONNULL(m_obj.get());
+ return *m_obj.get();
+ }
+
+ private:
+ std::shared_ptr<Certificate_Extension> m_obj;
+ std::vector<uint8_t> m_bits;
+ bool m_critical = false;
+ };
+
+ std::vector<OID> m_extension_oids;
+ std::map<OID, Extensions_Info> m_extension_info;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/x509/x509_attribute.cpp b/comm/third_party/botan/src/lib/x509/x509_attribute.cpp
new file mode 100644
index 0000000000..035f254c88
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/x509_attribute.cpp
@@ -0,0 +1,58 @@
+/*
+* Attribute
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/pkix_types.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
+#include <botan/oids.h>
+
+namespace Botan {
+
+/*
+* Create an Attribute
+*/
+Attribute::Attribute(const OID& attr_oid, const std::vector<uint8_t>& attr_value) :
+ oid(attr_oid),
+ parameters(attr_value)
+ {}
+
+/*
+* Create an Attribute
+*/
+Attribute::Attribute(const std::string& attr_oid,
+ const std::vector<uint8_t>& attr_value) :
+ oid(OID::from_string(attr_oid)),
+ parameters(attr_value)
+ {}
+
+/*
+* DER encode a Attribute
+*/
+void Attribute::encode_into(DER_Encoder& codec) const
+ {
+ codec.start_cons(SEQUENCE)
+ .encode(oid)
+ .start_cons(SET)
+ .raw_bytes(parameters)
+ .end_cons()
+ .end_cons();
+ }
+
+/*
+* Decode a BER encoded Attribute
+*/
+void Attribute::decode_from(BER_Decoder& codec)
+ {
+ codec.start_cons(SEQUENCE)
+ .decode(oid)
+ .start_cons(SET)
+ .raw_bytes(parameters)
+ .end_cons()
+ .end_cons();
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/x509/x509_ca.cpp b/comm/third_party/botan/src/lib/x509/x509_ca.cpp
new file mode 100644
index 0000000000..542a3a9191
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/x509_ca.cpp
@@ -0,0 +1,338 @@
+/*
+* X.509 Certificate Authority
+* (C) 1999-2010 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/x509_ca.h>
+#include <botan/x509_key.h>
+#include <botan/x509self.h>
+#include <botan/x509_ext.h>
+#include <botan/pkix_types.h>
+#include <botan/pubkey.h>
+#include <botan/der_enc.h>
+#include <botan/bigint.h>
+#include <botan/parsing.h>
+#include <botan/oids.h>
+#include <botan/hash.h>
+#include <botan/emsa.h>
+#include <botan/scan_name.h>
+#include <algorithm>
+#include <iterator>
+
+namespace Botan {
+
+/*
+* Load the certificate and private key
+*/
+X509_CA::X509_CA(const X509_Certificate& c,
+ const Private_Key& key,
+ const std::string& hash_fn,
+ RandomNumberGenerator& rng) :
+ m_ca_cert(c),
+ m_hash_fn(hash_fn)
+ {
+ if(!m_ca_cert.is_CA_cert())
+ throw Invalid_Argument("X509_CA: This certificate is not for a CA");
+
+ std::map<std::string,std::string> opts;
+ // constructor without additional options: use the padding used in the CA certificate
+ // sig_oid_str = <sig_alg>/<padding>, so padding with all its options will look
+ // like a cipher mode to the scanner
+ std::string sig_oid_str = OIDS::oid2str_or_throw(c.signature_algorithm().get_oid());
+ SCAN_Name scanner(sig_oid_str);
+ std::string pad = scanner.cipher_mode();
+ if(!pad.empty())
+ opts.insert({"padding",pad});
+
+ m_signer.reset(choose_sig_format(key, opts, rng, hash_fn, m_ca_sig_algo));
+ }
+
+/*
+* Load the certificate and private key, and additional options
+*/
+X509_CA::X509_CA(const X509_Certificate& ca_certificate,
+ const Private_Key& key,
+ const std::map<std::string,std::string>& opts,
+ const std::string& hash_fn,
+ RandomNumberGenerator& rng) : m_ca_cert(ca_certificate), m_hash_fn(hash_fn)
+ {
+ if(!m_ca_cert.is_CA_cert())
+ throw Invalid_Argument("X509_CA: This certificate is not for a CA");
+
+ m_signer.reset(choose_sig_format(key, opts, rng, hash_fn, m_ca_sig_algo));
+ }
+
+/*
+* X509_CA Destructor
+*/
+X509_CA::~X509_CA()
+ {
+ /* for unique_ptr */
+ }
+
+namespace {
+
+Extensions choose_extensions(const PKCS10_Request& req,
+ const X509_Certificate& ca_cert,
+ const std::string& hash_fn)
+ {
+ Key_Constraints constraints;
+ if(req.is_CA())
+ {
+ constraints = Key_Constraints(KEY_CERT_SIGN | CRL_SIGN);
+ }
+ else
+ {
+ std::unique_ptr<Public_Key> key(req.subject_public_key());
+ verify_cert_constraints_valid_for_key_type(*key, req.constraints());
+ constraints = req.constraints();
+ }
+
+ Extensions extensions = req.extensions();
+
+ extensions.replace(
+ new Cert_Extension::Basic_Constraints(req.is_CA(), req.path_limit()),
+ true);
+
+ if(constraints != NO_CONSTRAINTS)
+ {
+ extensions.replace(new Cert_Extension::Key_Usage(constraints), true);
+ }
+
+ extensions.replace(new Cert_Extension::Authority_Key_ID(ca_cert.subject_key_id()));
+ extensions.replace(new Cert_Extension::Subject_Key_ID(req.raw_public_key(), hash_fn));
+
+ extensions.replace(
+ new Cert_Extension::Subject_Alternative_Name(req.subject_alt_name()));
+
+ extensions.replace(
+ new Cert_Extension::Extended_Key_Usage(req.ex_constraints()));
+
+ return extensions;
+ }
+
+}
+
+X509_Certificate X509_CA::sign_request(const PKCS10_Request& req,
+ RandomNumberGenerator& rng,
+ const BigInt& serial_number,
+ const X509_Time& not_before,
+ const X509_Time& not_after) const
+ {
+ auto extensions = choose_extensions(req, m_ca_cert, m_hash_fn);
+
+ return make_cert(m_signer.get(), rng, serial_number,
+ m_ca_sig_algo, req.raw_public_key(),
+ not_before, not_after,
+ m_ca_cert.subject_dn(), req.subject_dn(),
+ extensions);
+ }
+
+/*
+* Sign a PKCS #10 certificate request
+*/
+X509_Certificate X509_CA::sign_request(const PKCS10_Request& req,
+ RandomNumberGenerator& rng,
+ const X509_Time& not_before,
+ const X509_Time& not_after) const
+ {
+ auto extensions = choose_extensions(req, m_ca_cert, m_hash_fn);
+
+ return make_cert(m_signer.get(), rng, m_ca_sig_algo,
+ req.raw_public_key(),
+ not_before, not_after,
+ m_ca_cert.subject_dn(), req.subject_dn(),
+ extensions);
+ }
+
+X509_Certificate X509_CA::make_cert(PK_Signer* signer,
+ RandomNumberGenerator& rng,
+ const AlgorithmIdentifier& sig_algo,
+ const std::vector<uint8_t>& pub_key,
+ const X509_Time& not_before,
+ const X509_Time& not_after,
+ const X509_DN& issuer_dn,
+ const X509_DN& subject_dn,
+ const Extensions& extensions)
+ {
+ const size_t SERIAL_BITS = 128;
+ 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);
+ }
+
+/*
+* Create a new certificate
+*/
+X509_Certificate X509_CA::make_cert(PK_Signer* signer,
+ RandomNumberGenerator& rng,
+ const BigInt& serial_no,
+ const AlgorithmIdentifier& sig_algo,
+ const std::vector<uint8_t>& pub_key,
+ const X509_Time& not_before,
+ const X509_Time& not_after,
+ const X509_DN& issuer_dn,
+ const X509_DN& subject_dn,
+ const Extensions& extensions)
+ {
+ const size_t X509_CERT_VERSION = 3;
+
+ // clang-format off
+ return X509_Certificate(X509_Object::make_signed(
+ signer, rng, sig_algo,
+ DER_Encoder().start_cons(SEQUENCE)
+ .start_explicit(0)
+ .encode(X509_CERT_VERSION-1)
+ .end_explicit()
+
+ .encode(serial_no)
+
+ .encode(sig_algo)
+ .encode(issuer_dn)
+
+ .start_cons(SEQUENCE)
+ .encode(not_before)
+ .encode(not_after)
+ .end_cons()
+
+ .encode(subject_dn)
+ .raw_bytes(pub_key)
+
+ .start_explicit(3)
+ .start_cons(SEQUENCE)
+ .encode(extensions)
+ .end_cons()
+ .end_explicit()
+ .end_cons()
+ .get_contents()
+ ));
+ // clang-format on
+ }
+
+/*
+* Create a new, empty CRL
+*/
+X509_CRL X509_CA::new_crl(RandomNumberGenerator& rng,
+ uint32_t next_update) const
+ {
+ return new_crl(rng,
+ std::chrono::system_clock::now(),
+ std::chrono::seconds(next_update));
+ }
+
+/*
+* Update a CRL with new entries
+*/
+X509_CRL X509_CA::update_crl(const X509_CRL& crl,
+ const std::vector<CRL_Entry>& new_revoked,
+ RandomNumberGenerator& rng,
+ uint32_t next_update) const
+ {
+ return update_crl(crl, new_revoked, rng,
+ std::chrono::system_clock::now(),
+ std::chrono::seconds(next_update));
+ }
+
+
+X509_CRL X509_CA::new_crl(RandomNumberGenerator& rng,
+ std::chrono::system_clock::time_point issue_time,
+ std::chrono::seconds next_update) const
+ {
+ std::vector<CRL_Entry> empty;
+ return make_crl(empty, 1, rng, issue_time, next_update);
+ }
+
+X509_CRL X509_CA::update_crl(const X509_CRL& last_crl,
+ const std::vector<CRL_Entry>& new_revoked,
+ RandomNumberGenerator& rng,
+ std::chrono::system_clock::time_point issue_time,
+ std::chrono::seconds next_update) const
+ {
+ std::vector<CRL_Entry> revoked = last_crl.get_revoked();
+
+ std::copy(new_revoked.begin(), new_revoked.end(),
+ std::back_inserter(revoked));
+
+ return make_crl(revoked, last_crl.crl_number() + 1, rng, issue_time, next_update);
+ }
+
+/*
+* Create a CRL
+*/
+X509_CRL X509_CA::make_crl(const std::vector<CRL_Entry>& revoked,
+ uint32_t crl_number,
+ RandomNumberGenerator& rng,
+ std::chrono::system_clock::time_point issue_time,
+ std::chrono::seconds next_update) const
+ {
+ const size_t X509_CRL_VERSION = 2;
+
+ auto expire_time = issue_time + next_update;
+
+ Extensions extensions;
+ extensions.add(new Cert_Extension::Authority_Key_ID(m_ca_cert.subject_key_id()));
+ extensions.add(new Cert_Extension::CRL_Number(crl_number));
+
+ // clang-format off
+ const std::vector<uint8_t> crl = X509_Object::make_signed(
+ m_signer.get(), rng, m_ca_sig_algo,
+ DER_Encoder().start_cons(SEQUENCE)
+ .encode(X509_CRL_VERSION-1)
+ .encode(m_ca_sig_algo)
+ .encode(m_ca_cert.subject_dn())
+ .encode(X509_Time(issue_time))
+ .encode(X509_Time(expire_time))
+ .encode_if(revoked.size() > 0,
+ DER_Encoder()
+ .start_cons(SEQUENCE)
+ .encode_list(revoked)
+ .end_cons()
+ )
+ .start_explicit(0)
+ .start_cons(SEQUENCE)
+ .encode(extensions)
+ .end_cons()
+ .end_explicit()
+ .end_cons()
+ .get_contents());
+ // clang-format on
+
+ return X509_CRL(crl);
+ }
+
+/*
+* Return the CA's certificate
+*/
+X509_Certificate X509_CA::ca_certificate() const
+ {
+ return m_ca_cert;
+ }
+
+/*
+* Choose a signing format for the key
+*/
+
+PK_Signer* choose_sig_format(const Private_Key& key,
+ RandomNumberGenerator& rng,
+ const std::string& hash_fn,
+ AlgorithmIdentifier& sig_algo)
+ {
+ return X509_Object::choose_sig_format(sig_algo, key, rng, hash_fn, "").release();
+ }
+
+PK_Signer* choose_sig_format(const Private_Key& key,
+ const std::map<std::string,std::string>& opts,
+ RandomNumberGenerator& rng,
+ const std::string& hash_fn,
+ AlgorithmIdentifier& sig_algo)
+ {
+ std::string padding;
+ if(opts.count("padding"))
+ padding = opts.at("padding");
+ return X509_Object::choose_sig_format(sig_algo, key, rng, hash_fn, padding).release();
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/x509/x509_ca.h b/comm/third_party/botan/src/lib/x509/x509_ca.h
new file mode 100644
index 0000000000..20e9b1bccf
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/x509_ca.h
@@ -0,0 +1,261 @@
+/*
+* X.509 Certificate Authority
+* (C) 1999-2008 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_X509_CA_H_
+#define BOTAN_X509_CA_H_
+
+#include <botan/x509cert.h>
+#include <botan/x509_crl.h>
+#include <chrono>
+#include <map>
+
+#if defined(BOTAN_HAS_SYSTEM_RNG)
+ #include <botan/system_rng.h>
+#endif
+
+namespace Botan {
+
+class BigInt;
+class Private_Key;
+class PKCS10_Request;
+class PK_Signer;
+
+/**
+* This class represents X.509 Certificate Authorities (CAs).
+*/
+class BOTAN_PUBLIC_API(2,0) X509_CA final
+ {
+ public:
+ /**
+ * Sign a PKCS#10 Request.
+ * @param req the request to sign
+ * @param rng the rng to use
+ * @param not_before the starting time for the certificate
+ * @param not_after the expiration time for the certificate
+ * @return resulting certificate
+ */
+ X509_Certificate sign_request(const PKCS10_Request& req,
+ RandomNumberGenerator& rng,
+ const X509_Time& not_before,
+ const X509_Time& not_after) const;
+
+ /**
+ * Sign a PKCS#10 Request.
+ * @param req the request to sign
+ * @param rng the rng to use
+ * @param serial_number the serial number the cert will be assigned.
+ * @param not_before the starting time for the certificate
+ * @param not_after the expiration time for the certificate
+ * @return resulting certificate
+ */
+ X509_Certificate sign_request(const PKCS10_Request& req,
+ RandomNumberGenerator& rng,
+ const BigInt& serial_number,
+ const X509_Time& not_before,
+ const X509_Time& not_after) const;
+
+ /**
+ * Get the certificate of this CA.
+ * @return CA certificate
+ */
+ X509_Certificate ca_certificate() const;
+
+ /**
+ * Create a new and empty CRL for this CA.
+ * @param rng the random number generator to use
+ * @param issue_time the issue time (typically system_clock::now)
+ * @param next_update the time interval after issue_data within which
+ * a new CRL will be produced.
+ * @return new CRL
+ */
+ X509_CRL new_crl(RandomNumberGenerator& rng,
+ std::chrono::system_clock::time_point issue_time,
+ std::chrono::seconds next_update) const;
+
+ /**
+ * Create a new CRL by with additional entries.
+ * @param last_crl the last CRL of this CA to add the new entries to
+ * @param new_entries contains the new CRL entries to be added to the CRL
+ * @param rng the random number generator to use
+ * @param issue_time the issue time (typically system_clock::now)
+ * @param next_update the time interval after issue_data within which
+ * a new CRL will be produced.
+ */
+ X509_CRL update_crl(const X509_CRL& last_crl,
+ const std::vector<CRL_Entry>& new_entries,
+ RandomNumberGenerator& rng,
+ std::chrono::system_clock::time_point issue_time,
+ std::chrono::seconds next_update) const;
+
+ /**
+ * Create a new and empty CRL for this CA.
+ * @param rng the random number generator to use
+ * @param next_update the time to set in next update in seconds
+ * as the offset from the current time
+ * @return new CRL
+ */
+ X509_CRL new_crl(RandomNumberGenerator& rng,
+ uint32_t next_update = 604800) const;
+
+ /**
+ * Create a new CRL by with additional entries.
+ * @param last_crl the last CRL of this CA to add the new entries to
+ * @param new_entries contains the new CRL entries to be added to the CRL
+ * @param rng the random number generator to use
+ * @param next_update the time to set in next update in seconds
+ * as the offset from the current time
+ */
+ X509_CRL update_crl(const X509_CRL& last_crl,
+ const std::vector<CRL_Entry>& new_entries,
+ RandomNumberGenerator& rng,
+ uint32_t next_update = 604800) const;
+
+ /**
+ * Interface for creating new certificates
+ * @param signer a signing object
+ * @param rng a random number generator
+ * @param sig_algo the signature algorithm identifier
+ * @param pub_key the serialized public key
+ * @param not_before the start time of the certificate
+ * @param not_after the end time of the certificate
+ * @param issuer_dn the DN of the issuer
+ * @param subject_dn the DN of the subject
+ * @param extensions an optional list of certificate extensions
+ * @returns newly minted certificate
+ */
+ static X509_Certificate make_cert(PK_Signer* signer,
+ RandomNumberGenerator& rng,
+ const AlgorithmIdentifier& sig_algo,
+ const std::vector<uint8_t>& pub_key,
+ const X509_Time& not_before,
+ const X509_Time& not_after,
+ const X509_DN& issuer_dn,
+ const X509_DN& subject_dn,
+ const Extensions& extensions);
+
+ /**
+ * Interface for creating new certificates
+ * @param signer a signing object
+ * @param rng a random number generator
+ * @param serial_number the serial number the cert will be assigned
+ * @param sig_algo the signature algorithm identifier
+ * @param pub_key the serialized public key
+ * @param not_before the start time of the certificate
+ * @param not_after the end time of the certificate
+ * @param issuer_dn the DN of the issuer
+ * @param subject_dn the DN of the subject
+ * @param extensions an optional list of certificate extensions
+ * @returns newly minted certificate
+ */
+ static X509_Certificate make_cert(PK_Signer* signer,
+ RandomNumberGenerator& rng,
+ const BigInt& serial_number,
+ const AlgorithmIdentifier& sig_algo,
+ const std::vector<uint8_t>& pub_key,
+ const X509_Time& not_before,
+ const X509_Time& not_after,
+ const X509_DN& issuer_dn,
+ const X509_DN& subject_dn,
+ const Extensions& extensions);
+
+ /**
+ * Create a new CA object.
+ * @param ca_certificate the certificate of the CA
+ * @param key the private key of the CA
+ * @param hash_fn name of a hash function to use for signing
+ * @param rng the random generator to use
+ */
+ X509_CA(const X509_Certificate& ca_certificate,
+ const Private_Key& key,
+ const std::string& hash_fn,
+ RandomNumberGenerator& rng);
+
+ /**
+ * Create a new CA object.
+ * @param ca_certificate the certificate of the CA
+ * @param key the private key of the CA
+ * @param opts additional options, e.g. padding, as key value pairs
+ * @param hash_fn name of a hash function to use for signing
+ * @param rng the random generator to use
+ */
+ X509_CA(const X509_Certificate& ca_certificate,
+ const Private_Key& key,
+ const std::map<std::string,std::string>& opts,
+ const std::string& hash_fn,
+ RandomNumberGenerator& rng);
+
+#if defined(BOTAN_HAS_SYSTEM_RNG)
+ BOTAN_DEPRECATED("Use version taking RNG object")
+ X509_CA(const X509_Certificate& ca_certificate,
+ const Private_Key& key,
+ const std::string& hash_fn) :
+ X509_CA(ca_certificate, key, hash_fn, system_rng())
+ {}
+#endif
+
+ 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();
+
+ private:
+ X509_CRL make_crl(const std::vector<CRL_Entry>& entries,
+ uint32_t crl_number,
+ RandomNumberGenerator& rng,
+ std::chrono::system_clock::time_point issue_time,
+ std::chrono::seconds next_update) const;
+
+ AlgorithmIdentifier m_ca_sig_algo;
+ X509_Certificate m_ca_cert;
+ std::string m_hash_fn;
+ std::unique_ptr<PK_Signer> m_signer;
+ };
+
+/**
+* Choose the default signature format for a certain public key signature
+* scheme.
+* @param key will be the key to choose a padding scheme for
+* @param rng the random generator to use
+* @param hash_fn is the desired hash function
+* @param alg_id will be set to the chosen scheme
+* @return A PK_Signer object for generating signatures
+*/
+BOTAN_PUBLIC_API(2,0) PK_Signer* choose_sig_format(const Private_Key& key,
+ RandomNumberGenerator& rng,
+ const std::string& hash_fn,
+ AlgorithmIdentifier& alg_id);
+
+/**
+* @verbatim
+* Choose the default signature format for a certain public key signature
+* scheme.
+*
+* The only option recognized by opts at this moment is "padding"
+* Find an entry from src/build-data/oids.txt under [signature] of the form
+* <sig_algo>/<padding>[(<hash_algo>)] and add {"padding",<padding>}
+* to opts.
+* @endverbatim
+*
+* @param key will be the key to choose a padding scheme for
+* @param opts contains additional options for building the certificate
+* @param rng the random generator to use
+* @param hash_fn is the desired hash function
+* @param alg_id will be set to the chosen scheme
+* @return A PK_Signer object for generating signatures
+*/
+PK_Signer* choose_sig_format(const Private_Key& key,
+ const std::map<std::string,std::string>& opts,
+ RandomNumberGenerator& rng,
+ const std::string& hash_fn,
+ AlgorithmIdentifier& alg_id);
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/x509/x509_crl.cpp b/comm/third_party/botan/src/lib/x509/x509_crl.cpp
new file mode 100644
index 0000000000..1b5df1a083
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/x509_crl.cpp
@@ -0,0 +1,268 @@
+/*
+* X.509 CRL
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/x509_crl.h>
+#include <botan/x509_ext.h>
+#include <botan/x509cert.h>
+#include <botan/ber_dec.h>
+
+#include <sstream>
+
+namespace Botan {
+
+struct CRL_Data
+ {
+ X509_DN m_issuer;
+ X509_Time m_this_update;
+ X509_Time m_next_update;
+ std::vector<CRL_Entry> m_entries;
+ Extensions m_extensions;
+
+ // cached values from extensions
+ size_t m_crl_number = 0;
+ std::vector<uint8_t> m_auth_key_id;
+ std::string m_issuing_distribution_point;
+ };
+
+std::string X509_CRL::PEM_label() const
+ {
+ return "X509 CRL";
+ }
+
+std::vector<std::string> X509_CRL::alternate_PEM_labels() const
+ {
+ return { "CRL" };
+ }
+
+X509_CRL::X509_CRL(DataSource& src)
+ {
+ load_data(src);
+ }
+
+X509_CRL::X509_CRL(const std::vector<uint8_t>& vec)
+ {
+ DataSource_Memory src(vec.data(), vec.size());
+ load_data(src);
+ }
+
+#if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
+X509_CRL::X509_CRL(const std::string& fsname)
+ {
+ DataSource_Stream src(fsname, true);
+ load_data(src);
+ }
+#endif
+
+X509_CRL::X509_CRL(const X509_DN& issuer,
+ const X509_Time& this_update,
+ const X509_Time& next_update,
+ const std::vector<CRL_Entry>& revoked) :
+ X509_Object()
+ {
+ m_data.reset(new CRL_Data);
+ m_data->m_issuer = issuer;
+ m_data->m_this_update = this_update;
+ m_data->m_next_update = next_update;
+ m_data->m_entries = revoked;
+ }
+
+/**
+* Check if this particular certificate is listed in the CRL
+*/
+bool X509_CRL::is_revoked(const X509_Certificate& cert) const
+ {
+ /*
+ If the cert wasn't issued by the CRL issuer, it's possible the cert
+ is revoked, but not by this CRL. Maybe throw an exception instead?
+ */
+ if(cert.issuer_dn() != issuer_dn())
+ return false;
+
+ std::vector<uint8_t> crl_akid = authority_key_id();
+ std::vector<uint8_t> cert_akid = cert.authority_key_id();
+
+ if(!crl_akid.empty() && !cert_akid.empty())
+ {
+ if(crl_akid != cert_akid)
+ return false;
+ }
+
+ std::vector<uint8_t> 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() == REMOVE_FROM_CRL)
+ is_revoked = false;
+ else
+ is_revoked = true;
+ }
+ }
+
+ return is_revoked;
+ }
+
+/*
+* Decode the TBSCertList data
+*/
+namespace {
+
+std::unique_ptr<CRL_Data> decode_crl_body(const std::vector<uint8_t>& body,
+ const AlgorithmIdentifier& sig_algo)
+ {
+ std::unique_ptr<CRL_Data> data(new CRL_Data);
+
+ BER_Decoder tbs_crl(body);
+
+ size_t version;
+ tbs_crl.decode_optional(version, INTEGER, UNIVERSAL);
+
+ if(version != 0 && version != 1)
+ throw X509_CRL::X509_CRL_Error("Unknown X.509 CRL version " +
+ std::to_string(version+1));
+
+ AlgorithmIdentifier sig_algo_inner;
+ tbs_crl.decode(sig_algo_inner);
+
+ if(sig_algo != sig_algo_inner)
+ throw X509_CRL::X509_CRL_Error("Algorithm identifier mismatch");
+
+ tbs_crl.decode(data->m_issuer)
+ .decode(data->m_this_update)
+ .decode(data->m_next_update);
+
+ BER_Object next = tbs_crl.get_next_object();
+
+ if(next.is_a(SEQUENCE, CONSTRUCTED))
+ {
+ BER_Decoder cert_list(std::move(next));
+
+ while(cert_list.more_items())
+ {
+ CRL_Entry entry;
+ cert_list.decode(entry);
+ data->m_entries.push_back(entry);
+ }
+ next = tbs_crl.get_next_object();
+ }
+
+ if(next.is_a(0, ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC)))
+ {
+ BER_Decoder crl_options(std::move(next));
+ crl_options.decode(data->m_extensions).verify_end();
+ next = tbs_crl.get_next_object();
+ }
+
+ if(next.is_set())
+ throw X509_CRL::X509_CRL_Error("Unknown tag in CRL");
+
+ tbs_crl.verify_end();
+
+ // Now cache some fields from the extensions
+ if(auto ext = data->m_extensions.get_extension_object_as<Cert_Extension::CRL_Number>())
+ {
+ data->m_crl_number = ext->get_crl_number();
+ }
+ if(auto ext = data->m_extensions.get_extension_object_as<Cert_Extension::Authority_Key_ID>())
+ {
+ data->m_auth_key_id = ext->get_key_id();
+ }
+ if(auto ext = data->m_extensions.get_extension_object_as<Cert_Extension::CRL_Issuing_Distribution_Point>())
+ {
+ std::stringstream ss;
+
+ for(const auto& pair : ext->get_point().contents())
+ {
+ ss << pair.first << ": " << pair.second << " ";
+ }
+ data->m_issuing_distribution_point = ss.str();
+ }
+
+ return data;
+ }
+
+}
+
+void X509_CRL::force_decode()
+ {
+ m_data.reset(decode_crl_body(signed_body(), signature_algorithm()).release());
+ }
+
+const CRL_Data& X509_CRL::data() const
+ {
+ if(!m_data)
+ {
+ throw Invalid_State("X509_CRL uninitialized");
+ }
+ return *m_data.get();
+ }
+
+const Extensions& X509_CRL::extensions() const
+ {
+ return data().m_extensions;
+ }
+
+/*
+* Return the list of revoked certificates
+*/
+const std::vector<CRL_Entry>& X509_CRL::get_revoked() const
+ {
+ return data().m_entries;
+ }
+
+/*
+* Return the distinguished name of the issuer
+*/
+const X509_DN& X509_CRL::issuer_dn() const
+ {
+ return data().m_issuer;
+ }
+
+/*
+* Return the key identifier of the issuer
+*/
+const std::vector<uint8_t>& X509_CRL::authority_key_id() const
+ {
+ return data().m_auth_key_id;
+ }
+
+/*
+* Return the CRL number of this CRL
+*/
+uint32_t X509_CRL::crl_number() const
+ {
+ return static_cast<uint32_t>(data().m_crl_number);
+ }
+
+/*
+* Return the issue data of the CRL
+*/
+const X509_Time& X509_CRL::this_update() const
+ {
+ return data().m_this_update;
+ }
+
+/*
+* Return the date when a new CRL will be issued
+*/
+const X509_Time& X509_CRL::next_update() const
+ {
+ return data().m_next_update;
+ }
+
+/*
+* Return the CRL's distribution point
+*/
+std::string X509_CRL::crl_issuing_distribution_point() const
+ {
+ return data().m_issuing_distribution_point;
+ }
+}
diff --git a/comm/third_party/botan/src/lib/x509/x509_crl.h b/comm/third_party/botan/src/lib/x509/x509_crl.h
new file mode 100644
index 0000000000..6d4a301abd
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/x509_crl.h
@@ -0,0 +1,209 @@
+/*
+* X.509 CRL
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_X509_CRL_H_
+#define BOTAN_X509_CRL_H_
+
+#include <botan/x509_obj.h>
+#include <botan/asn1_obj.h>
+#include <botan/pkix_enums.h>
+#include <vector>
+
+namespace Botan {
+
+class Extensions;
+class X509_Certificate;
+class X509_DN;
+
+struct CRL_Entry_Data;
+struct CRL_Data;
+
+/**
+* This class represents CRL entries
+*/
+class BOTAN_PUBLIC_API(2,0) CRL_Entry final : public ASN1_Object
+ {
+ public:
+ void encode_into(class DER_Encoder&) const override;
+ void decode_from(class BER_Decoder&) override;
+
+ /**
+ * Get the serial number of the certificate associated with this entry.
+ * @return certificate's serial number
+ */
+ const std::vector<uint8_t>& serial_number() const;
+
+ /**
+ * Get the revocation date of the certificate associated with this entry
+ * @return certificate's revocation date
+ */
+ const X509_Time& expire_time() const;
+
+ /**
+ * Get the entries reason code
+ * @return reason code
+ */
+ CRL_Code reason_code() const;
+
+ /**
+ * Get the extensions on this CRL entry
+ */
+ const Extensions& extensions() const;
+
+ /**
+ * Create uninitialized CRL_Entry object
+ */
+ CRL_Entry() = default;
+
+ /**
+ * Construct an CRL entry.
+ * @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 = UNSPECIFIED);
+
+ private:
+ friend class X509_CRL;
+
+ const CRL_Entry_Data& data() const;
+
+ std::shared_ptr<CRL_Entry_Data> m_data;
+ };
+
+/**
+* Test two CRL entries for equality in all fields.
+*/
+BOTAN_PUBLIC_API(2,0) bool operator==(const CRL_Entry&, const CRL_Entry&);
+
+/**
+* Test two CRL entries for inequality in at least one field.
+*/
+BOTAN_PUBLIC_API(2,0) bool operator!=(const CRL_Entry&, const CRL_Entry&);
+
+/**
+* This class represents X.509 Certificate Revocation Lists (CRLs).
+*/
+class BOTAN_PUBLIC_API(2,0) X509_CRL final : public X509_Object
+ {
+ public:
+ /**
+ * This class represents CRL related errors.
+ *
+ * In a future major release this exception type will be removed and
+ * replaced with Decoding_Error
+ */
+ class BOTAN_PUBLIC_API(2,0) X509_CRL_Error final : public Decoding_Error
+ {
+ public:
+ explicit X509_CRL_Error(const std::string& error) :
+ Decoding_Error("X509_CRL: " + error) {}
+ };
+
+ /**
+ * Check if this particular certificate is listed in the CRL
+ */
+ bool is_revoked(const X509_Certificate& cert) const;
+
+ /**
+ * Get the entries of this CRL in the form of a vector.
+ * @return vector containing the entries of this CRL.
+ */
+ const std::vector<CRL_Entry>& get_revoked() const;
+
+ /**
+ * Get the issuer DN of this CRL.
+ * @return CRLs issuer DN
+ */
+ const X509_DN& issuer_dn() const;
+
+ /**
+ * @return extension data for this CRL
+ */
+ const Extensions& extensions() const;
+
+ /**
+ * Get the AuthorityKeyIdentifier of this CRL.
+ * @return this CRLs AuthorityKeyIdentifier
+ */
+ const std::vector<uint8_t>& authority_key_id() const;
+
+ /**
+ * Get the serial number of this CRL.
+ * @return CRLs serial number
+ */
+ uint32_t crl_number() const;
+
+ /**
+ * Get the CRL's thisUpdate value.
+ * @return CRLs thisUpdate
+ */
+ const X509_Time& this_update() const;
+
+ /**
+ * Get the CRL's nextUpdate value.
+ * @return CRLs nextdUpdate
+ */
+ const X509_Time& next_update() const;
+
+ /**
+ * Get the CRL's distribution point
+ * @return CRL.IssuingDistributionPoint from the CRL's Data_Store
+ */
+ std::string crl_issuing_distribution_point() const;
+
+ /**
+ * Create an uninitialized CRL object. Any attempts to access
+ * this object will throw an exception.
+ */
+ X509_CRL() = default;
+
+ /**
+ * Construct a CRL from a data source.
+ * @param source the data source providing the DER or PEM encoded CRL.
+ */
+ 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(const std::string& filename);
+#endif
+
+ /**
+ * Construct a CRL from a binary vector
+ * @param vec the binary (DER) representation of the CRL
+ */
+ X509_CRL(const std::vector<uint8_t>& vec);
+
+ /**
+ * Construct a CRL
+ * @param issuer issuer of this CRL
+ * @param thisUpdate valid from
+ * @param nextUpdate valid until
+ * @param revoked entries to be included in the CRL
+ */
+ X509_CRL(const X509_DN& issuer, const X509_Time& thisUpdate,
+ const X509_Time& nextUpdate, const std::vector<CRL_Entry>& revoked);
+
+ private:
+ std::string PEM_label() const override;
+
+ std::vector<std::string> alternate_PEM_labels() const override;
+
+ void force_decode() override;
+
+ const CRL_Data& data() const;
+
+ std::shared_ptr<CRL_Data> m_data;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/x509/x509_dn.cpp b/comm/third_party/botan/src/lib/x509/x509_dn.cpp
new file mode 100644
index 0000000000..99aad475a9
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/x509_dn.cpp
@@ -0,0 +1,428 @@
+/*
+* X509_DN
+* (C) 1999-2007,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/pkix_types.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
+#include <botan/parsing.h>
+#include <botan/internal/stl_util.h>
+#include <botan/oids.h>
+#include <ostream>
+#include <sstream>
+#include <cctype>
+
+namespace Botan {
+
+/*
+* Add an attribute to a X509_DN
+*/
+void X509_DN::add_attribute(const std::string& type,
+ const std::string& str)
+ {
+ add_attribute(OID::from_string(type), str);
+ }
+
+/*
+* Add an attribute to a X509_DN
+*/
+void X509_DN::add_attribute(const OID& oid, const ASN1_String& str)
+ {
+ if(str.empty())
+ return;
+
+ m_rdn.push_back(std::make_pair(oid, str));
+ m_dn_bits.clear();
+ }
+
+/*
+* Get the attributes of this X509_DN
+*/
+std::multimap<OID, std::string> X509_DN::get_attributes() const
+ {
+ std::multimap<OID, std::string> retval;
+
+ for(auto& i : m_rdn)
+ multimap_insert(retval, i.first, i.second.value());
+ return retval;
+ }
+
+/*
+* Get the contents of this X.500 Name
+*/
+std::multimap<std::string, std::string> X509_DN::contents() const
+ {
+ std::multimap<std::string, std::string> retval;
+
+ for(auto& i : m_rdn)
+ {
+ multimap_insert(retval, i.first.to_formatted_string(), i.second.value());
+ }
+ return retval;
+ }
+
+bool X509_DN::has_field(const std::string& attr) const
+ {
+ const OID o = OIDS::str2oid_or_empty(deref_info_field(attr));
+ if(o.has_value())
+ return has_field(o);
+ else
+ return false;
+ }
+
+bool X509_DN::has_field(const OID& oid) const
+ {
+ for(auto& i : m_rdn)
+ {
+ if(i.first == oid)
+ return true;
+ }
+
+ return false;
+ }
+
+std::string X509_DN::get_first_attribute(const std::string& attr) const
+ {
+ const OID oid = OID::from_string(deref_info_field(attr));
+ return get_first_attribute(oid).value();
+ }
+
+ASN1_String X509_DN::get_first_attribute(const OID& oid) const
+ {
+ for(auto& i : m_rdn)
+ {
+ if(i.first == oid)
+ {
+ return i.second;
+ }
+ }
+
+ return ASN1_String();
+ }
+
+/*
+* Get a single attribute type
+*/
+std::vector<std::string> X509_DN::get_attribute(const std::string& attr) const
+ {
+ const OID oid = OID::from_string(deref_info_field(attr));
+
+ std::vector<std::string> values;
+
+ for(auto& i : m_rdn)
+ {
+ if(i.first == oid)
+ {
+ values.push_back(i.second.value());
+ }
+ }
+
+ return values;
+ }
+
+/*
+* Deref aliases in a subject/issuer info request
+*/
+std::string X509_DN::deref_info_field(const std::string& info)
+ {
+ if(info == "Name" || info == "CommonName" || info == "CN") return "X520.CommonName";
+ if(info == "SerialNumber" || info == "SN") return "X520.SerialNumber";
+ if(info == "Country" || info == "C") return "X520.Country";
+ if(info == "Organization" || info == "O") return "X520.Organization";
+ if(info == "Organizational Unit" || info == "OrgUnit" || info == "OU")
+ return "X520.OrganizationalUnit";
+ if(info == "Locality" || info == "L") return "X520.Locality";
+ if(info == "State" || info == "Province" || info == "ST") return "X520.State";
+ if(info == "Email") return "RFC822";
+ return info;
+ }
+
+/*
+* Compare two X509_DNs for equality
+*/
+bool operator==(const X509_DN& dn1, const X509_DN& dn2)
+ {
+ auto attr1 = dn1.get_attributes();
+ auto attr2 = dn2.get_attributes();
+
+ if(attr1.size() != attr2.size()) return false;
+
+ auto p1 = attr1.begin();
+ auto p2 = attr2.begin();
+
+ while(true)
+ {
+ if(p1 == attr1.end() && p2 == attr2.end())
+ break;
+ if(p1 == attr1.end()) return false;
+ if(p2 == attr2.end()) return false;
+ if(p1->first != p2->first) return false;
+ if(!x500_name_cmp(p1->second, p2->second))
+ return false;
+ ++p1;
+ ++p2;
+ }
+ return true;
+ }
+
+/*
+* Compare two X509_DNs for inequality
+*/
+bool operator!=(const X509_DN& dn1, const X509_DN& dn2)
+ {
+ return !(dn1 == dn2);
+ }
+
+/*
+* Induce an arbitrary ordering on DNs
+*/
+bool operator<(const X509_DN& dn1, const X509_DN& dn2)
+ {
+ auto attr1 = dn1.get_attributes();
+ 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;
+
+ // We know they are the same # of elements, now compare the OIDs:
+ auto p1 = attr1.begin();
+ auto p2 = attr2.begin();
+
+ while(p1 != attr1.end() && p2 != attr2.end())
+ {
+ if(p1->first != p2->first)
+ {
+ 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);
+ }
+
+ ++p1;
+ ++p2;
+ }
+
+ // if we reach here, then the DNs should be identical
+ BOTAN_DEBUG_ASSERT(dn1 == dn2);
+ return false;
+ }
+
+/*
+* DER encode a DistinguishedName
+*/
+void X509_DN::encode_into(DER_Encoder& der) const
+ {
+ der.start_cons(SEQUENCE);
+
+ if(!m_dn_bits.empty())
+ {
+ /*
+ If we decoded this from somewhere, encode it back exactly as
+ we received it
+ */
+ der.raw_bytes(m_dn_bits);
+ }
+ else
+ {
+ for(const auto& dn : m_rdn)
+ {
+ der.start_cons(SET)
+ .start_cons(SEQUENCE)
+ .encode(dn.first)
+ .encode(dn.second)
+ .end_cons()
+ .end_cons();
+ }
+ }
+
+ der.end_cons();
+ }
+
+/*
+* Decode a BER encoded DistinguishedName
+*/
+void X509_DN::decode_from(BER_Decoder& source)
+ {
+ std::vector<uint8_t> bits;
+
+ source.start_cons(SEQUENCE)
+ .raw_bytes(bits)
+ .end_cons();
+
+ BER_Decoder sequence(bits);
+
+ while(sequence.more_items())
+ {
+ BER_Decoder rdn = sequence.start_cons(SET);
+
+ while(rdn.more_items())
+ {
+ OID oid;
+ ASN1_String str;
+
+ rdn.start_cons(SEQUENCE)
+ .decode(oid)
+ .decode(str) // TODO support Any
+ .end_cons();
+
+ add_attribute(oid, str);
+ }
+ }
+
+ m_dn_bits = bits;
+ }
+
+namespace {
+
+std::string to_short_form(const OID& oid)
+ {
+ const std::string long_id = oid.to_formatted_string();
+
+ if(long_id == "X520.CommonName")
+ return "CN";
+
+ if(long_id == "X520.Country")
+ return "C";
+
+ if(long_id == "X520.Organization")
+ return "O";
+
+ if(long_id == "X520.OrganizationalUnit")
+ return "OU";
+
+ return long_id;
+ }
+
+}
+
+std::string X509_DN::to_string() const
+ {
+ std::ostringstream out;
+ out << *this;
+ return out.str();
+ }
+
+std::ostream& operator<<(std::ostream& out, const X509_DN& dn)
+ {
+ auto info = dn.dn_info();
+
+ for(size_t i = 0; i != info.size(); ++i)
+ {
+ out << to_short_form(info[i].first) << "=\"";
+ for(char c : info[i].second.value())
+ {
+ if(c == '\\' || c == '\"')
+ {
+ out << "\\";
+ }
+ out << c;
+ }
+ out << "\"";
+
+ if(i + 1 < info.size())
+ {
+ out << ",";
+ }
+ }
+ return out;
+ }
+
+std::istream& operator>>(std::istream& in, X509_DN& dn)
+ {
+ in >> std::noskipws;
+ do
+ {
+ std::string key;
+ std::string val;
+ char c;
+
+ while(in.good())
+ {
+ in >> c;
+
+ if(std::isspace(c) && key.empty())
+ continue;
+ else if(!std::isspace(c))
+ {
+ key.push_back(c);
+ break;
+ }
+ else
+ break;
+ }
+
+ while(in.good())
+ {
+ in >> c;
+
+ if(!std::isspace(c) && c != '=')
+ key.push_back(c);
+ else if(c == '=')
+ break;
+ else
+ throw Invalid_Argument("Ill-formed X.509 DN");
+ }
+
+ bool in_quotes = false;
+ while(in.good())
+ {
+ in >> c;
+
+ if(std::isspace(c))
+ {
+ if(!in_quotes && !val.empty())
+ break;
+ else if(in_quotes)
+ val.push_back(' ');
+ }
+ else if(c == '"')
+ in_quotes = !in_quotes;
+ else if(c == '\\')
+ {
+ if(in.good())
+ in >> c;
+ val.push_back(c);
+ }
+ else if(c == ',' && !in_quotes)
+ break;
+ else
+ val.push_back(c);
+ }
+
+ if(!key.empty() && !val.empty())
+ dn.add_attribute(X509_DN::deref_info_field(key),val);
+ else
+ break;
+ }
+ while(in.good());
+ return in;
+ }
+}
diff --git a/comm/third_party/botan/src/lib/x509/x509_dn.h b/comm/third_party/botan/src/lib/x509/x509_dn.h
new file mode 100644
index 0000000000..cd7beebe9c
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/x509_dn.h
@@ -0,0 +1,11 @@
+/*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_X509_DN_H_
+#define BOTAN_X509_DN_H_
+
+#include <botan/pkix_types.h>
+BOTAN_DEPRECATED_HEADER(x509_dn.h)
+
+#endif
diff --git a/comm/third_party/botan/src/lib/x509/x509_dn_ub.cpp b/comm/third_party/botan/src/lib/x509/x509_dn_ub.cpp
new file mode 100644
index 0000000000..b5c7645a13
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/x509_dn_ub.cpp
@@ -0,0 +1,60 @@
+/*
+* DN_UB maps: Upper bounds on the length of DN strings
+*
+* This file was automatically generated by ./src/scripts/oids.py on 2019-10-21
+*
+* 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 <botan/pkix_types.h>
+#include <botan/asn1_obj.h>
+#include <map>
+
+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.
+ */
+static const std::map<Botan::OID, size_t> DN_UB =
+ {
+ { Botan::OID({2,5,4,10}), 64 }, // X520.Organization
+ { Botan::OID({2,5,4,11}), 64 }, // X520.OrganizationalUnit
+ { Botan::OID({2,5,4,12}), 64 }, // X520.Title
+ { Botan::OID({2,5,4,3}), 64 }, // X520.CommonName
+ { Botan::OID({2,5,4,4}), 40 }, // X520.Surname
+ { Botan::OID({2,5,4,42}), 32768 }, // X520.GivenName
+ { Botan::OID({2,5,4,43}), 32768 }, // X520.Initials
+ { Botan::OID({2,5,4,44}), 32768 }, // X520.GenerationalQualifier
+ { Botan::OID({2,5,4,46}), 64 }, // X520.DNQualifier
+ { Botan::OID({2,5,4,5}), 64 }, // X520.SerialNumber
+ { Botan::OID({2,5,4,6}), 3 }, // X520.Country
+ { Botan::OID({2,5,4,65}), 128 }, // X520.Pseudonym
+ { Botan::OID({2,5,4,7}), 128 }, // X520.Locality
+ { Botan::OID({2,5,4,8}), 128 }, // X520.State
+ { Botan::OID({2,5,4,9}), 128 } // X520.StreetAddress
+ };
+}
+
+namespace Botan {
+
+//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;
+ }
+ }
+}
+
diff --git a/comm/third_party/botan/src/lib/x509/x509_ext.cpp b/comm/third_party/botan/src/lib/x509/x509_ext.cpp
new file mode 100644
index 0000000000..e81e15c187
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/x509_ext.cpp
@@ -0,0 +1,1023 @@
+/*
+* X.509 Certificate Extensions
+* (C) 1999-2010,2012 Jack Lloyd
+* (C) 2016 René Korthaus, Rohde & Schwarz Cybersecurity
+* (C) 2017 Fabian Weissberg, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/x509_ext.h>
+#include <botan/x509cert.h>
+#include <botan/datastor.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
+#include <botan/hash.h>
+#include <botan/loadstor.h>
+#include <botan/internal/bit_ops.h>
+#include <algorithm>
+#include <set>
+#include <sstream>
+
+namespace Botan {
+
+/*
+* Create a Certificate_Extension object of some kind to handle
+*/
+std::unique_ptr<Certificate_Extension>
+Extensions::create_extn_obj(const OID& oid,
+ bool critical,
+ const std::vector<uint8_t>& body)
+ {
+ const std::string oid_str = oid.to_string();
+
+ std::unique_ptr<Certificate_Extension> extn;
+
+ if(oid == Cert_Extension::Subject_Key_ID::static_oid())
+ {
+ extn.reset(new Cert_Extension::Subject_Key_ID);
+ }
+ else if(oid == Cert_Extension::Key_Usage::static_oid())
+ {
+ extn.reset(new Cert_Extension::Key_Usage);
+ }
+ else if(oid == Cert_Extension::Subject_Alternative_Name::static_oid())
+ {
+ extn.reset(new Cert_Extension::Subject_Alternative_Name);
+ }
+ else if(oid == Cert_Extension::Issuer_Alternative_Name::static_oid())
+ {
+ extn.reset(new Cert_Extension::Issuer_Alternative_Name);
+ }
+ else if(oid == Cert_Extension::Basic_Constraints::static_oid())
+ {
+ extn.reset(new Cert_Extension::Basic_Constraints);
+ }
+ else if(oid == Cert_Extension::CRL_Number::static_oid())
+ {
+ extn.reset(new Cert_Extension::CRL_Number);
+ }
+ else if(oid == Cert_Extension::CRL_ReasonCode::static_oid())
+ {
+ extn.reset(new Cert_Extension::CRL_ReasonCode);
+ }
+ else if(oid == Cert_Extension::Authority_Key_ID::static_oid())
+ {
+ extn.reset(new Cert_Extension::Authority_Key_ID);
+ }
+ else if(oid == Cert_Extension::Name_Constraints::static_oid())
+ {
+ extn.reset(new Cert_Extension::Name_Constraints);
+ }
+ else if(oid == Cert_Extension::CRL_Distribution_Points::static_oid())
+ {
+ extn.reset(new Cert_Extension::CRL_Distribution_Points);
+ }
+ else if(oid == Cert_Extension::CRL_Issuing_Distribution_Point::static_oid())
+ {
+ extn.reset(new Cert_Extension::CRL_Issuing_Distribution_Point);
+ }
+ else if(oid == Cert_Extension::Certificate_Policies::static_oid())
+ {
+ extn.reset(new Cert_Extension::Certificate_Policies);
+ }
+ else if(oid == Cert_Extension::Extended_Key_Usage::static_oid())
+ {
+ extn.reset(new Cert_Extension::Extended_Key_Usage);
+ }
+ else if(oid == Cert_Extension::Authority_Information_Access::static_oid())
+ {
+ extn.reset(new Cert_Extension::Authority_Information_Access);
+ }
+ else
+ {
+ // some other unknown extension type
+ extn.reset(new Cert_Extension::Unknown_Extension(oid, critical));
+ }
+
+ try
+ {
+ extn->decode_inner(body);
+ }
+ catch(Decoding_Error&)
+ {
+ extn.reset(new Cert_Extension::Unknown_Extension(oid, critical));
+ extn->decode_inner(body);
+ }
+ return extn;
+ }
+
+/*
+* Validate the extension (the default implementation is a NOP)
+*/
+void Certificate_Extension::validate(const X509_Certificate&, const X509_Certificate&,
+ const std::vector<std::shared_ptr<const X509_Certificate>>&,
+ std::vector<std::set<Certificate_Status_Code>>&,
+ size_t)
+ {
+ }
+
+/*
+* Add a new cert
+*/
+void Extensions::add(Certificate_Extension* extn, bool critical)
+ {
+ // sanity check: we don't want to have the same extension more than once
+ if(m_extension_info.count(extn->oid_of()) > 0)
+ {
+ const std::string name = extn->oid_name();
+ delete extn;
+ throw Invalid_Argument("Extension " + name + " already present in Extensions::add");
+ }
+
+ const OID oid = extn->oid_of();
+ Extensions_Info info(critical, extn);
+ m_extension_oids.push_back(oid);
+ m_extension_info.emplace(oid, info);
+ }
+
+bool Extensions::add_new(Certificate_Extension* extn, bool critical)
+ {
+ if(m_extension_info.count(extn->oid_of()) > 0)
+ {
+ delete extn;
+ return false; // already exists
+ }
+
+ const OID oid = extn->oid_of();
+ Extensions_Info info(critical, extn);
+ m_extension_oids.push_back(oid);
+ m_extension_info.emplace(oid, info);
+ return true;
+ }
+
+bool Extensions::remove(const OID& oid)
+ {
+ const bool erased = m_extension_info.erase(oid) > 0;
+
+ if(erased)
+ {
+ m_extension_oids.erase(std::find(m_extension_oids.begin(), m_extension_oids.end(), oid));
+ }
+
+ return erased;
+ }
+
+void Extensions::replace(Certificate_Extension* extn, bool critical)
+ {
+ // Remove it if it existed
+ remove(extn->oid_of());
+
+ const OID oid = extn->oid_of();
+ Extensions_Info info(critical, extn);
+ m_extension_oids.push_back(oid);
+ m_extension_info.emplace(oid, info);
+ }
+
+bool Extensions::extension_set(const OID& oid) const
+ {
+ return (m_extension_info.find(oid) != m_extension_info.end());
+ }
+
+bool Extensions::critical_extension_set(const OID& oid) const
+ {
+ auto i = m_extension_info.find(oid);
+ if(i != m_extension_info.end())
+ return i->second.is_critical();
+ return false;
+ }
+
+std::vector<uint8_t> Extensions::get_extension_bits(const OID& oid) const
+ {
+ auto i = m_extension_info.find(oid);
+ if(i == m_extension_info.end())
+ throw Invalid_Argument("Extensions::get_extension_bits no such extension set");
+
+ return i->second.bits();
+ }
+
+const Certificate_Extension* Extensions::get_extension_object(const OID& oid) const
+ {
+ auto extn = m_extension_info.find(oid);
+ if(extn == m_extension_info.end())
+ return nullptr;
+
+ return &extn->second.obj();
+ }
+
+std::unique_ptr<Certificate_Extension> Extensions::get(const OID& oid) const
+ {
+ if(const Certificate_Extension* ext = this->get_extension_object(oid))
+ {
+ return std::unique_ptr<Certificate_Extension>(ext->copy());
+ }
+ return nullptr;
+ }
+
+std::vector<std::pair<std::unique_ptr<Certificate_Extension>, bool>> Extensions::extensions() const
+ {
+ std::vector<std::pair<std::unique_ptr<Certificate_Extension>, bool>> exts;
+ for(auto&& ext : m_extension_info)
+ {
+ exts.push_back(
+ std::make_pair(
+ std::unique_ptr<Certificate_Extension>(ext.second.obj().copy()),
+ ext.second.is_critical())
+ );
+ }
+ return exts;
+ }
+
+std::map<OID, std::pair<std::vector<uint8_t>, bool>> Extensions::extensions_raw() const
+ {
+ std::map<OID, std::pair<std::vector<uint8_t>, bool>> out;
+ for(auto&& ext : m_extension_info)
+ {
+ out.emplace(ext.first,
+ std::make_pair(ext.second.bits(),
+ ext.second.is_critical()));
+ }
+ return out;
+ }
+
+/*
+* Encode an Extensions list
+*/
+void Extensions::encode_into(DER_Encoder& to_object) const
+ {
+ for(auto ext_info : m_extension_info)
+ {
+ const OID& oid = ext_info.first;
+ const bool should_encode = ext_info.second.obj().should_encode();
+
+ if(should_encode)
+ {
+ const bool is_critical = ext_info.second.is_critical();
+ const std::vector<uint8_t>& ext_value = ext_info.second.bits();
+
+ to_object.start_cons(SEQUENCE)
+ .encode(oid)
+ .encode_optional(is_critical, false)
+ .encode(ext_value, OCTET_STRING)
+ .end_cons();
+ }
+ }
+ }
+
+/*
+* Decode a list of Extensions
+*/
+void Extensions::decode_from(BER_Decoder& from_source)
+ {
+ m_extension_oids.clear();
+ m_extension_info.clear();
+
+ BER_Decoder sequence = from_source.start_cons(SEQUENCE);
+
+ while(sequence.more_items())
+ {
+ OID oid;
+ bool critical;
+ std::vector<uint8_t> bits;
+
+ sequence.start_cons(SEQUENCE)
+ .decode(oid)
+ .decode_optional(critical, BOOLEAN, UNIVERSAL, false)
+ .decode(bits, OCTET_STRING)
+ .end_cons();
+
+ std::unique_ptr<Certificate_Extension> obj = create_extn_obj(oid, critical, bits);
+ Extensions_Info info(critical, bits, obj.release());
+
+ m_extension_oids.push_back(oid);
+ m_extension_info.emplace(oid, info);
+ }
+ sequence.verify_end();
+ }
+
+/*
+* Write the extensions to an info store
+*/
+void Extensions::contents_to(Data_Store& subject_info,
+ Data_Store& issuer_info) const
+ {
+ for(auto&& m_extn_info : m_extension_info)
+ {
+ m_extn_info.second.obj().contents_to(subject_info, issuer_info);
+ subject_info.add(m_extn_info.second.obj().oid_name() + ".is_critical",
+ m_extn_info.second.is_critical());
+ }
+ }
+
+namespace Cert_Extension {
+
+/*
+* Checked accessor for the path_limit member
+*/
+size_t Basic_Constraints::get_path_limit() const
+ {
+ if(!m_is_ca)
+ throw Invalid_State("Basic_Constraints::get_path_limit: Not a CA");
+ return m_path_limit;
+ }
+
+/*
+* Encode the extension
+*/
+std::vector<uint8_t> Basic_Constraints::encode_inner() const
+ {
+ std::vector<uint8_t> output;
+ DER_Encoder(output)
+ .start_cons(SEQUENCE)
+ .encode_if(m_is_ca,
+ DER_Encoder()
+ .encode(m_is_ca)
+ .encode_optional(m_path_limit, NO_CERT_PATH_LIMIT)
+ )
+ .end_cons();
+ return output;
+ }
+
+/*
+* Decode the extension
+*/
+void Basic_Constraints::decode_inner(const std::vector<uint8_t>& in)
+ {
+ BER_Decoder(in)
+ .start_cons(SEQUENCE)
+ .decode_optional(m_is_ca, BOOLEAN, UNIVERSAL, false)
+ .decode_optional(m_path_limit, INTEGER, UNIVERSAL, NO_CERT_PATH_LIMIT)
+ .end_cons();
+
+ if(m_is_ca == false)
+ m_path_limit = 0;
+ }
+
+/*
+* Return a textual representation
+*/
+void Basic_Constraints::contents_to(Data_Store& subject, Data_Store&) const
+ {
+ subject.add("X509v3.BasicConstraints.is_ca", (m_is_ca ? 1 : 0));
+ subject.add("X509v3.BasicConstraints.path_constraint", static_cast<uint32_t>(m_path_limit));
+ }
+
+/*
+* Encode the extension
+*/
+std::vector<uint8_t> Key_Usage::encode_inner() const
+ {
+ if(m_constraints == NO_CONSTRAINTS)
+ throw Encoding_Error("Cannot encode zero usage constraints");
+
+ const size_t unused_bits = ctz(static_cast<uint32_t>(m_constraints));
+
+ std::vector<uint8_t> der;
+ der.push_back(BIT_STRING);
+ der.push_back(2 + ((unused_bits < 8) ? 1 : 0));
+ der.push_back(unused_bits % 8);
+ der.push_back((m_constraints >> 8) & 0xFF);
+ if(m_constraints & 0xFF)
+ der.push_back(m_constraints & 0xFF);
+
+ return der;
+ }
+
+/*
+* Decode the extension
+*/
+void Key_Usage::decode_inner(const std::vector<uint8_t>& in)
+ {
+ BER_Decoder ber(in);
+
+ BER_Object obj = ber.get_next_object();
+
+ obj.assert_is_a(BIT_STRING, UNIVERSAL, "usage constraint");
+
+ if(obj.length() != 2 && obj.length() != 3)
+ throw BER_Decoding_Error("Bad size for BITSTRING in usage constraint");
+
+ uint16_t usage = 0;
+
+ const uint8_t* bits = obj.bits();
+
+ if(bits[0] >= 8)
+ throw BER_Decoding_Error("Invalid unused bits in usage constraint");
+
+ const uint8_t mask = static_cast<uint8_t>(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);
+ }
+
+/*
+* Return a textual representation
+*/
+void Key_Usage::contents_to(Data_Store& subject, Data_Store&) const
+ {
+ subject.add("X509v3.KeyUsage", m_constraints);
+ }
+
+/*
+* Encode the extension
+*/
+std::vector<uint8_t> Subject_Key_ID::encode_inner() const
+ {
+ std::vector<uint8_t> output;
+ DER_Encoder(output).encode(m_key_id, OCTET_STRING);
+ return output;
+ }
+
+/*
+* Decode the extension
+*/
+void Subject_Key_ID::decode_inner(const std::vector<uint8_t>& in)
+ {
+ BER_Decoder(in).decode(m_key_id, OCTET_STRING).verify_end();
+ }
+
+/*
+* Return a textual representation
+*/
+void Subject_Key_ID::contents_to(Data_Store& subject, Data_Store&) const
+ {
+ subject.add("X509v3.SubjectKeyIdentifier", m_key_id);
+ }
+
+/*
+* Subject_Key_ID Constructor
+*/
+Subject_Key_ID::Subject_Key_ID(const std::vector<uint8_t>& pub_key, const std::string& hash_name)
+ {
+ std::unique_ptr<HashFunction> hash(HashFunction::create_or_throw(hash_name));
+
+ m_key_id.resize(hash->output_length());
+
+ hash->update(pub_key);
+ hash->final(m_key_id.data());
+
+ // Truncate longer hashes, 192 bits here seems plenty
+ const size_t max_skid_len = (192 / 8);
+ if(m_key_id.size() > max_skid_len)
+ m_key_id.resize(max_skid_len);
+ }
+
+/*
+* Encode the extension
+*/
+std::vector<uint8_t> Authority_Key_ID::encode_inner() const
+ {
+ std::vector<uint8_t> output;
+ DER_Encoder(output)
+ .start_cons(SEQUENCE)
+ .encode(m_key_id, OCTET_STRING, ASN1_Tag(0), CONTEXT_SPECIFIC)
+ .end_cons();
+ return output;
+ }
+
+/*
+* Decode the extension
+*/
+void Authority_Key_ID::decode_inner(const std::vector<uint8_t>& in)
+ {
+ BER_Decoder(in)
+ .start_cons(SEQUENCE)
+ .decode_optional_string(m_key_id, OCTET_STRING, 0);
+ }
+
+/*
+* Return a textual representation
+*/
+void Authority_Key_ID::contents_to(Data_Store&, Data_Store& issuer) const
+ {
+ if(m_key_id.size())
+ issuer.add("X509v3.AuthorityKeyIdentifier", m_key_id);
+ }
+
+/*
+* Encode the extension
+*/
+std::vector<uint8_t> Subject_Alternative_Name::encode_inner() const
+ {
+ std::vector<uint8_t> output;
+ DER_Encoder(output).encode(m_alt_name);
+ return output;
+ }
+
+/*
+* Encode the extension
+*/
+std::vector<uint8_t> Issuer_Alternative_Name::encode_inner() const
+ {
+ std::vector<uint8_t> output;
+ DER_Encoder(output).encode(m_alt_name);
+ return output;
+ }
+
+/*
+* Decode the extension
+*/
+void Subject_Alternative_Name::decode_inner(const std::vector<uint8_t>& in)
+ {
+ BER_Decoder(in).decode(m_alt_name);
+ }
+
+/*
+* Decode the extension
+*/
+void Issuer_Alternative_Name::decode_inner(const std::vector<uint8_t>& in)
+ {
+ BER_Decoder(in).decode(m_alt_name);
+ }
+
+/*
+* Return a textual representation
+*/
+void Subject_Alternative_Name::contents_to(Data_Store& subject_info,
+ Data_Store&) const
+ {
+ subject_info.add(get_alt_name().contents());
+ }
+
+/*
+* Return a textual representation
+*/
+void Issuer_Alternative_Name::contents_to(Data_Store&, Data_Store& issuer_info) const
+ {
+ issuer_info.add(get_alt_name().contents());
+ }
+
+/*
+* Encode the extension
+*/
+std::vector<uint8_t> Extended_Key_Usage::encode_inner() const
+ {
+ std::vector<uint8_t> output;
+ DER_Encoder(output)
+ .start_cons(SEQUENCE)
+ .encode_list(m_oids)
+ .end_cons();
+ return output;
+ }
+
+/*
+* Decode the extension
+*/
+void Extended_Key_Usage::decode_inner(const std::vector<uint8_t>& in)
+ {
+ BER_Decoder(in).decode_list(m_oids);
+ }
+
+/*
+* Return a textual representation
+*/
+void Extended_Key_Usage::contents_to(Data_Store& subject, Data_Store&) const
+ {
+ for(size_t i = 0; i != m_oids.size(); ++i)
+ subject.add("X509v3.ExtendedKeyUsage", m_oids[i].to_string());
+ }
+
+/*
+* Encode the extension
+*/
+std::vector<uint8_t> Name_Constraints::encode_inner() const
+ {
+ throw Not_Implemented("Name_Constraints encoding");
+ }
+
+
+/*
+* Decode the extension
+*/
+void Name_Constraints::decode_inner(const std::vector<uint8_t>& in)
+ {
+ std::vector<GeneralSubtree> permit, exclude;
+ BER_Decoder ber(in);
+ BER_Decoder ext = ber.start_cons(SEQUENCE);
+ BER_Object per = ext.get_next_object();
+
+ ext.push_back(per);
+ if(per.is_a(0, ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC)))
+ {
+ ext.decode_list(permit,ASN1_Tag(0),ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC));
+ if(permit.empty())
+ throw Encoding_Error("Empty Name Contraint list");
+ }
+
+ BER_Object exc = ext.get_next_object();
+ ext.push_back(exc);
+ if(per.is_a(1, ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC)))
+ {
+ ext.decode_list(exclude,ASN1_Tag(1),ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC));
+ if(exclude.empty())
+ throw Encoding_Error("Empty Name Contraint list");
+ }
+
+ ext.end_cons();
+
+ if(permit.empty() && exclude.empty())
+ throw Encoding_Error("Empty Name Contraint extension");
+
+ m_name_constraints = NameConstraints(std::move(permit),std::move(exclude));
+ }
+
+/*
+* Return a textual representation
+*/
+void Name_Constraints::contents_to(Data_Store& subject, Data_Store&) const
+ {
+ std::stringstream ss;
+
+ for(const GeneralSubtree& gs: m_name_constraints.permitted())
+ {
+ ss << gs;
+ subject.add("X509v3.NameConstraints.permitted", ss.str());
+ ss.str(std::string());
+ }
+ for(const GeneralSubtree& gs: m_name_constraints.excluded())
+ {
+ ss << gs;
+ subject.add("X509v3.NameConstraints.excluded", ss.str());
+ ss.str(std::string());
+ }
+ }
+
+void Name_Constraints::validate(const X509_Certificate& subject, const X509_Certificate& issuer,
+ const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path,
+ std::vector<std::set<Certificate_Status_Code>>& cert_status,
+ size_t pos)
+ {
+ if(!m_name_constraints.permitted().empty() || !m_name_constraints.excluded().empty())
+ {
+ if(!subject.is_CA_cert())
+ {
+ cert_status.at(pos).insert(Certificate_Status_Code::NAME_CONSTRAINT_ERROR);
+ }
+
+ const bool issuer_name_constraint_critical =
+ issuer.is_critical("X509v3.NameConstraints");
+
+ // Check that all subordinate certs pass the name constraint
+ for(size_t j = 0; j < pos; ++j)
+ {
+ bool permitted = m_name_constraints.permitted().empty();
+ bool failed = false;
+
+ for(auto c: m_name_constraints.permitted())
+ {
+ switch(c.base().matches(*cert_path.at(j)))
+ {
+ case GeneralName::MatchResult::NotFound:
+ case GeneralName::MatchResult::All:
+ permitted = true;
+ break;
+ case GeneralName::MatchResult::UnknownType:
+ failed = issuer_name_constraint_critical;
+ permitted = true;
+ break;
+ default:
+ break;
+ }
+ }
+
+ for(auto c: m_name_constraints.excluded())
+ {
+ switch(c.base().matches(*cert_path.at(j)))
+ {
+ case GeneralName::MatchResult::All:
+ case GeneralName::MatchResult::Some:
+ failed = true;
+ break;
+ case GeneralName::MatchResult::UnknownType:
+ failed = issuer_name_constraint_critical;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if(failed || !permitted)
+ {
+ cert_status.at(j).insert(Certificate_Status_Code::NAME_CONSTRAINT_ERROR);
+ }
+ }
+ }
+ }
+
+namespace {
+
+/*
+* A policy specifier
+*/
+class Policy_Information final : public ASN1_Object
+ {
+ public:
+ Policy_Information() = default;
+ explicit Policy_Information(const OID& oid) : m_oid(oid) {}
+
+ const OID& oid() const { return m_oid; }
+
+ void encode_into(DER_Encoder& codec) const override
+ {
+ codec.start_cons(SEQUENCE)
+ .encode(m_oid)
+ .end_cons();
+ }
+
+ void decode_from(BER_Decoder& codec) override
+ {
+ codec.start_cons(SEQUENCE)
+ .decode(m_oid)
+ .discard_remaining()
+ .end_cons();
+ }
+
+ private:
+ OID m_oid;
+ };
+
+}
+
+/*
+* Encode the extension
+*/
+std::vector<uint8_t> Certificate_Policies::encode_inner() const
+ {
+ std::vector<Policy_Information> policies;
+
+ for(size_t i = 0; i != m_oids.size(); ++i)
+ policies.push_back(Policy_Information(m_oids[i]));
+
+ std::vector<uint8_t> output;
+ DER_Encoder(output)
+ .start_cons(SEQUENCE)
+ .encode_list(policies)
+ .end_cons();
+ return output;
+ }
+
+/*
+* Decode the extension
+*/
+void Certificate_Policies::decode_inner(const std::vector<uint8_t>& in)
+ {
+ std::vector<Policy_Information> policies;
+
+ BER_Decoder(in).decode_list(policies);
+ m_oids.clear();
+ for(size_t i = 0; i != policies.size(); ++i)
+ m_oids.push_back(policies[i].oid());
+ }
+
+/*
+* Return a textual representation
+*/
+void Certificate_Policies::contents_to(Data_Store& info, Data_Store&) const
+ {
+ for(size_t i = 0; i != m_oids.size(); ++i)
+ info.add("X509v3.CertificatePolicies", m_oids[i].to_string());
+ }
+
+void Certificate_Policies::validate(
+ const X509_Certificate& /*subject*/,
+ const X509_Certificate& /*issuer*/,
+ const std::vector<std::shared_ptr<const X509_Certificate>>& /*cert_path*/,
+ std::vector<std::set<Certificate_Status_Code>>& cert_status,
+ size_t pos)
+ {
+ std::set<OID> 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);
+ }
+ }
+
+std::vector<uint8_t> Authority_Information_Access::encode_inner() const
+ {
+ ASN1_String url(m_ocsp_responder, IA5_STRING);
+
+ std::vector<uint8_t> output;
+ DER_Encoder(output)
+ .start_cons(SEQUENCE)
+ .start_cons(SEQUENCE)
+ .encode(OID::from_string("PKIX.OCSP"))
+ .add_object(ASN1_Tag(6), CONTEXT_SPECIFIC, url.value())
+ .end_cons()
+ .end_cons();
+ return output;
+ }
+
+void Authority_Information_Access::decode_inner(const std::vector<uint8_t>& in)
+ {
+ BER_Decoder ber = BER_Decoder(in).start_cons(SEQUENCE);
+
+ while(ber.more_items())
+ {
+ OID oid;
+
+ BER_Decoder info = ber.start_cons(SEQUENCE);
+
+ info.decode(oid);
+
+ if(oid == OID::from_string("PKIX.OCSP"))
+ {
+ BER_Object name = info.get_next_object();
+
+ if(name.is_a(6, CONTEXT_SPECIFIC))
+ {
+ m_ocsp_responder = ASN1::to_string(name);
+ }
+
+ }
+ if(oid == OID::from_string("PKIX.CertificateAuthorityIssuers"))
+ {
+ BER_Object name = info.get_next_object();
+
+ if(name.is_a(6, CONTEXT_SPECIFIC))
+ {
+ m_ca_issuers.push_back(ASN1::to_string(name));
+ }
+ }
+ }
+ }
+
+void Authority_Information_Access::contents_to(Data_Store& subject, Data_Store&) const
+ {
+ if(!m_ocsp_responder.empty())
+ subject.add("OCSP.responder", m_ocsp_responder);
+ for(const std::string& ca_issuer : m_ca_issuers)
+ subject.add("PKIX.CertificateAuthorityIssuers", ca_issuer);
+ }
+
+/*
+* Checked accessor for the crl_number member
+*/
+size_t CRL_Number::get_crl_number() const
+ {
+ if(!m_has_value)
+ throw Invalid_State("CRL_Number::get_crl_number: Not set");
+ return m_crl_number;
+ }
+
+/*
+* Copy a CRL_Number extension
+*/
+CRL_Number* CRL_Number::copy() const
+ {
+ if(!m_has_value)
+ throw Invalid_State("CRL_Number::copy: Not set");
+ return new CRL_Number(m_crl_number);
+ }
+
+/*
+* Encode the extension
+*/
+std::vector<uint8_t> CRL_Number::encode_inner() const
+ {
+ std::vector<uint8_t> output;
+ DER_Encoder(output).encode(m_crl_number);
+ return output;
+ }
+
+/*
+* Decode the extension
+*/
+void CRL_Number::decode_inner(const std::vector<uint8_t>& in)
+ {
+ BER_Decoder(in).decode(m_crl_number);
+ m_has_value = true;
+ }
+
+/*
+* Return a textual representation
+*/
+void CRL_Number::contents_to(Data_Store& info, Data_Store&) const
+ {
+ info.add("X509v3.CRLNumber", static_cast<uint32_t>(m_crl_number));
+ }
+
+/*
+* Encode the extension
+*/
+std::vector<uint8_t> CRL_ReasonCode::encode_inner() const
+ {
+ std::vector<uint8_t> output;
+ DER_Encoder(output).encode(static_cast<size_t>(m_reason), ENUMERATED, UNIVERSAL);
+ return output;
+ }
+
+/*
+* Decode the extension
+*/
+void CRL_ReasonCode::decode_inner(const std::vector<uint8_t>& in)
+ {
+ size_t reason_code = 0;
+ BER_Decoder(in).decode(reason_code, ENUMERATED, UNIVERSAL);
+ m_reason = static_cast<CRL_Code>(reason_code);
+ }
+
+/*
+* Return a textual representation
+*/
+void CRL_ReasonCode::contents_to(Data_Store& info, Data_Store&) const
+ {
+ info.add("X509v3.CRLReasonCode", m_reason);
+ }
+
+std::vector<uint8_t> CRL_Distribution_Points::encode_inner() const
+ {
+ throw Not_Implemented("CRL_Distribution_Points encoding");
+ }
+
+void CRL_Distribution_Points::decode_inner(const std::vector<uint8_t>& buf)
+ {
+ BER_Decoder(buf)
+ .decode_list(m_distribution_points)
+ .verify_end();
+
+ std::stringstream ss;
+
+ for(size_t i = 0; i != m_distribution_points.size(); ++i)
+ {
+ auto contents = m_distribution_points[i].point().contents();
+
+ for(const auto& pair : contents)
+ {
+ ss << pair.first << ": " << pair.second << " ";
+ }
+ }
+
+ m_crl_distribution_urls.push_back(ss.str());
+ }
+
+void CRL_Distribution_Points::contents_to(Data_Store& subject, Data_Store&) const
+ {
+ for(const std::string& crl_url : m_crl_distribution_urls)
+ subject.add("CRL.DistributionPoint", crl_url);
+ }
+
+void CRL_Distribution_Points::Distribution_Point::encode_into(class DER_Encoder&) const
+ {
+ throw Not_Implemented("CRL_Distribution_Points encoding");
+ }
+
+void CRL_Distribution_Points::Distribution_Point::decode_from(class BER_Decoder& ber)
+ {
+ ber.start_cons(SEQUENCE)
+ .start_cons(ASN1_Tag(0), CONTEXT_SPECIFIC)
+ .decode_optional_implicit(m_point, ASN1_Tag(0),
+ ASN1_Tag(CONTEXT_SPECIFIC | CONSTRUCTED),
+ SEQUENCE, CONSTRUCTED)
+ .end_cons().end_cons();
+ }
+
+std::vector<uint8_t> CRL_Issuing_Distribution_Point::encode_inner() const
+ {
+ throw Not_Implemented("CRL_Issuing_Distribution_Point encoding");
+ }
+
+void CRL_Issuing_Distribution_Point::decode_inner(const std::vector<uint8_t>& buf)
+ {
+ BER_Decoder(buf).decode(m_distribution_point).verify_end();
+ }
+
+void CRL_Issuing_Distribution_Point::contents_to(Data_Store& info, Data_Store&) const
+ {
+ auto contents = m_distribution_point.point().contents();
+ std::stringstream ss;
+
+ for(const auto& pair : contents)
+ {
+ ss << pair.first << ": " << pair.second << " ";
+ }
+
+ info.add("X509v3.CRLIssuingDistributionPoint", ss.str());
+ }
+
+std::vector<uint8_t> Unknown_Extension::encode_inner() const
+ {
+ return m_bytes;
+ }
+
+void Unknown_Extension::decode_inner(const std::vector<uint8_t>& bytes)
+ {
+ // Just treat as an opaque blob at this level
+ m_bytes = bytes;
+ }
+
+void Unknown_Extension::contents_to(Data_Store&, Data_Store&) const
+ {
+ // No information store
+ }
+
+}
+
+}
diff --git a/comm/third_party/botan/src/lib/x509/x509_ext.h b/comm/third_party/botan/src/lib/x509/x509_ext.h
new file mode 100644
index 0000000000..cb6e064c9d
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/x509_ext.h
@@ -0,0 +1,529 @@
+/*
+* X.509 Certificate Extensions
+* (C) 1999-2007,2012 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_X509_EXTENSIONS_H_
+#define BOTAN_X509_EXTENSIONS_H_
+
+#include <botan/pkix_types.h>
+#include <set>
+
+namespace Botan {
+
+class Data_Store;
+class X509_Certificate;
+
+namespace Cert_Extension {
+
+static const size_t NO_CERT_PATH_LIMIT = 0xFFFFFFF0;
+
+/**
+* Basic Constraints Extension
+*/
+class BOTAN_PUBLIC_API(2,0) Basic_Constraints final : public Certificate_Extension
+ {
+ public:
+ Basic_Constraints* copy() const override
+ { return new Basic_Constraints(m_is_ca, m_path_limit); }
+
+ Basic_Constraints(bool ca = false, size_t limit = 0) :
+ m_is_ca(ca), m_path_limit(limit) {}
+
+ bool get_is_ca() const { return m_is_ca; }
+ size_t get_path_limit() const;
+
+ static OID static_oid() { return OID("2.5.29.19"); }
+ OID oid_of() const override { return static_oid(); }
+
+ private:
+ std::string oid_name() const override
+ { return "X509v3.BasicConstraints"; }
+
+ std::vector<uint8_t> encode_inner() const override;
+ void decode_inner(const std::vector<uint8_t>&) override;
+ void contents_to(Data_Store&, Data_Store&) const override;
+
+ bool m_is_ca;
+ size_t m_path_limit;
+ };
+
+/**
+* Key Usage Constraints Extension
+*/
+class BOTAN_PUBLIC_API(2,0) Key_Usage final : public Certificate_Extension
+ {
+ public:
+ Key_Usage* copy() const override { return new Key_Usage(m_constraints); }
+
+ explicit Key_Usage(Key_Constraints c = NO_CONSTRAINTS) : m_constraints(c) {}
+
+ Key_Constraints get_constraints() const { return m_constraints; }
+
+ static OID static_oid() { return OID("2.5.29.15"); }
+ OID oid_of() const override { return static_oid(); }
+
+ private:
+ std::string oid_name() const override { return "X509v3.KeyUsage"; }
+
+ bool should_encode() const override
+ { return (m_constraints != NO_CONSTRAINTS); }
+ std::vector<uint8_t> encode_inner() const override;
+ void decode_inner(const std::vector<uint8_t>&) override;
+ void contents_to(Data_Store&, Data_Store&) const override;
+
+ Key_Constraints m_constraints;
+ };
+
+/**
+* Subject Key Identifier Extension
+*/
+class BOTAN_PUBLIC_API(2,0) Subject_Key_ID final : public Certificate_Extension
+ {
+ public:
+ Subject_Key_ID() = default;
+
+ explicit Subject_Key_ID(const std::vector<uint8_t>& k) : m_key_id(k) {}
+
+ Subject_Key_ID(const std::vector<uint8_t>& public_key,
+ const std::string& hash_fn);
+
+ Subject_Key_ID* copy() const override
+ { return new Subject_Key_ID(m_key_id); }
+
+ const std::vector<uint8_t>& get_key_id() const { return m_key_id; }
+
+ static OID static_oid() { return OID("2.5.29.14"); }
+ OID oid_of() const override { return static_oid(); }
+
+ private:
+
+ std::string oid_name() const override
+ { return "X509v3.SubjectKeyIdentifier"; }
+
+ bool should_encode() const override { return (m_key_id.size() > 0); }
+ std::vector<uint8_t> encode_inner() const override;
+ void decode_inner(const std::vector<uint8_t>&) override;
+ void contents_to(Data_Store&, Data_Store&) const override;
+
+ std::vector<uint8_t> m_key_id;
+ };
+
+/**
+* Authority Key Identifier Extension
+*/
+class BOTAN_PUBLIC_API(2,0) Authority_Key_ID final : public Certificate_Extension
+ {
+ public:
+ Authority_Key_ID* copy() const override
+ { return new Authority_Key_ID(m_key_id); }
+
+ Authority_Key_ID() = default;
+ explicit Authority_Key_ID(const std::vector<uint8_t>& k) : m_key_id(k) {}
+
+ const std::vector<uint8_t>& get_key_id() const { return m_key_id; }
+
+ static OID static_oid() { return OID("2.5.29.35"); }
+ OID oid_of() const override { return static_oid(); }
+
+ private:
+ std::string oid_name() const override
+ { return "X509v3.AuthorityKeyIdentifier"; }
+
+ bool should_encode() const override { return (m_key_id.size() > 0); }
+ std::vector<uint8_t> encode_inner() const override;
+ void decode_inner(const std::vector<uint8_t>&) override;
+ void contents_to(Data_Store&, Data_Store&) const override;
+
+ std::vector<uint8_t> m_key_id;
+ };
+
+/**
+* Subject Alternative Name Extension
+*/
+class BOTAN_PUBLIC_API(2,4) Subject_Alternative_Name final : public Certificate_Extension
+ {
+ public:
+ const AlternativeName& get_alt_name() const { return m_alt_name; }
+
+ static OID static_oid() { return OID("2.5.29.17"); }
+ OID oid_of() const override { return static_oid(); }
+
+ Subject_Alternative_Name* copy() const override
+ { return new Subject_Alternative_Name(get_alt_name()); }
+
+ explicit Subject_Alternative_Name(const AlternativeName& name = AlternativeName()) :
+ m_alt_name(name) {}
+
+ private:
+ std::string oid_name() const override { return "X509v3.SubjectAlternativeName"; }
+
+ bool should_encode() const override { return m_alt_name.has_items(); }
+ std::vector<uint8_t> encode_inner() const override;
+ void decode_inner(const std::vector<uint8_t>&) override;
+ void contents_to(Data_Store&, Data_Store&) const override;
+
+ AlternativeName m_alt_name;
+ };
+
+/**
+* Issuer Alternative Name Extension
+*/
+class BOTAN_PUBLIC_API(2,0) Issuer_Alternative_Name final : public Certificate_Extension
+ {
+ public:
+ const AlternativeName& get_alt_name() const { return m_alt_name; }
+
+ static OID static_oid() { return OID("2.5.29.18"); }
+ OID oid_of() const override { return static_oid(); }
+
+ Issuer_Alternative_Name* copy() const override
+ { return new Issuer_Alternative_Name(get_alt_name()); }
+
+ explicit Issuer_Alternative_Name(const AlternativeName& name = AlternativeName()) :
+ m_alt_name(name) {}
+
+ private:
+ std::string oid_name() const override { return "X509v3.IssuerAlternativeName"; }
+
+ bool should_encode() const override { return m_alt_name.has_items(); }
+ std::vector<uint8_t> encode_inner() const override;
+ void decode_inner(const std::vector<uint8_t>&) override;
+ void contents_to(Data_Store&, Data_Store&) const override;
+
+ AlternativeName m_alt_name;
+ };
+
+/**
+* Extended Key Usage Extension
+*/
+class BOTAN_PUBLIC_API(2,0) Extended_Key_Usage final : public Certificate_Extension
+ {
+ public:
+ Extended_Key_Usage* copy() const override
+ { return new Extended_Key_Usage(m_oids); }
+
+ Extended_Key_Usage() = default;
+ explicit Extended_Key_Usage(const std::vector<OID>& o) : m_oids(o) {}
+
+ const std::vector<OID>& get_oids() const { return m_oids; }
+
+ static OID static_oid() { return OID("2.5.29.37"); }
+ OID oid_of() const override { return static_oid(); }
+
+ private:
+ std::string oid_name() const override { return "X509v3.ExtendedKeyUsage"; }
+
+ bool should_encode() const override { return (m_oids.size() > 0); }
+ std::vector<uint8_t> encode_inner() const override;
+ void decode_inner(const std::vector<uint8_t>&) override;
+ void contents_to(Data_Store&, Data_Store&) const override;
+
+ std::vector<OID> m_oids;
+ };
+
+/**
+* Name Constraints
+*/
+class BOTAN_PUBLIC_API(2,0) Name_Constraints final : public Certificate_Extension
+ {
+ public:
+ Name_Constraints* copy() const override
+ { return new Name_Constraints(m_name_constraints); }
+
+ Name_Constraints() = default;
+ Name_Constraints(const NameConstraints &nc) : m_name_constraints(nc) {}
+
+ void validate(const X509_Certificate& subject, const X509_Certificate& issuer,
+ const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path,
+ std::vector<std::set<Certificate_Status_Code>>& 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"); }
+ OID oid_of() const override { return static_oid(); }
+
+ private:
+ std::string oid_name() const override
+ { return "X509v3.NameConstraints"; }
+
+ bool should_encode() const override { return true; }
+ std::vector<uint8_t> encode_inner() const override;
+ void decode_inner(const std::vector<uint8_t>&) override;
+ void contents_to(Data_Store&, Data_Store&) const override;
+
+ NameConstraints m_name_constraints;
+ };
+
+/**
+* Certificate Policies Extension
+*/
+class BOTAN_PUBLIC_API(2,0) Certificate_Policies final : public Certificate_Extension
+ {
+ public:
+ Certificate_Policies* copy() const override
+ { return new Certificate_Policies(m_oids); }
+
+ Certificate_Policies() = default;
+ explicit Certificate_Policies(const std::vector<OID>& o) : m_oids(o) {}
+
+ BOTAN_DEPRECATED("Use get_policy_oids")
+ std::vector<OID> get_oids() const { return m_oids; }
+
+ const std::vector<OID>& get_policy_oids() const { return m_oids; }
+
+ 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::vector<std::shared_ptr<const X509_Certificate>>& cert_path,
+ std::vector<std::set<Certificate_Status_Code>>& cert_status,
+ size_t pos) override;
+ private:
+ std::string oid_name() const override
+ { return "X509v3.CertificatePolicies"; }
+
+ bool should_encode() const override { return (m_oids.size() > 0); }
+ std::vector<uint8_t> encode_inner() const override;
+ void decode_inner(const std::vector<uint8_t>&) override;
+ void contents_to(Data_Store&, Data_Store&) const override;
+
+ std::vector<OID> m_oids;
+ };
+
+/**
+* Authority Information Access Extension
+*/
+class BOTAN_PUBLIC_API(2,0) Authority_Information_Access final : public Certificate_Extension
+ {
+ public:
+ Authority_Information_Access* copy() const override
+ { return new Authority_Information_Access(m_ocsp_responder, m_ca_issuers); }
+
+ Authority_Information_Access() = default;
+
+ explicit Authority_Information_Access(const std::string& ocsp, const std::vector<std::string>& ca_issuers = std::vector<std::string>()) :
+ m_ocsp_responder(ocsp), m_ca_issuers(ca_issuers) {}
+
+ std::string ocsp_responder() const { return m_ocsp_responder; }
+
+ static OID static_oid() { return OID("1.3.6.1.5.5.7.1.1"); }
+ OID oid_of() const override { return static_oid(); }
+ const std::vector<std::string> ca_issuers() const { return m_ca_issuers; }
+
+ private:
+ std::string oid_name() const override
+ { return "PKIX.AuthorityInformationAccess"; }
+
+ bool should_encode() const override { return (!m_ocsp_responder.empty()); }
+
+ std::vector<uint8_t> encode_inner() const override;
+ void decode_inner(const std::vector<uint8_t>&) override;
+
+ void contents_to(Data_Store&, Data_Store&) const override;
+
+ std::string m_ocsp_responder;
+ std::vector<std::string> m_ca_issuers;
+ };
+
+/**
+* CRL Number Extension
+*/
+class BOTAN_PUBLIC_API(2,0) CRL_Number final : public Certificate_Extension
+ {
+ public:
+ CRL_Number* copy() const override;
+
+ CRL_Number() : m_has_value(false), m_crl_number(0) {}
+ 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"); }
+ OID oid_of() const override { return static_oid(); }
+
+ private:
+ std::string oid_name() const override { return "X509v3.CRLNumber"; }
+
+ bool should_encode() const override { return m_has_value; }
+ std::vector<uint8_t> encode_inner() const override;
+ void decode_inner(const std::vector<uint8_t>&) override;
+ void contents_to(Data_Store&, Data_Store&) const override;
+
+ bool m_has_value;
+ size_t m_crl_number;
+ };
+
+/**
+* CRL Entry Reason Code Extension
+*/
+class BOTAN_PUBLIC_API(2,0) CRL_ReasonCode final : public Certificate_Extension
+ {
+ public:
+ CRL_ReasonCode* copy() const override
+ { return new CRL_ReasonCode(m_reason); }
+
+ explicit CRL_ReasonCode(CRL_Code r = UNSPECIFIED) : m_reason(r) {}
+
+ CRL_Code get_reason() const { return m_reason; }
+
+ static OID static_oid() { return OID("2.5.29.21"); }
+ OID oid_of() const override { return static_oid(); }
+
+ private:
+ std::string oid_name() const override { return "X509v3.ReasonCode"; }
+
+ bool should_encode() const override { return (m_reason != UNSPECIFIED); }
+ std::vector<uint8_t> encode_inner() const override;
+ void decode_inner(const std::vector<uint8_t>&) override;
+ void contents_to(Data_Store&, Data_Store&) const override;
+
+ CRL_Code m_reason;
+ };
+
+/**
+* CRL Distribution Points Extension
+* todo enforce restrictions from RFC 5280 4.2.1.13
+*/
+class BOTAN_PUBLIC_API(2,0) CRL_Distribution_Points final : public Certificate_Extension
+ {
+ public:
+ class BOTAN_PUBLIC_API(2,0) Distribution_Point final : public ASN1_Object
+ {
+ public:
+ void encode_into(class DER_Encoder&) const override;
+ void decode_from(class BER_Decoder&) override;
+
+ const AlternativeName& point() const { return m_point; }
+ private:
+ AlternativeName m_point;
+ };
+
+ CRL_Distribution_Points* copy() const override
+ { return new CRL_Distribution_Points(m_distribution_points); }
+
+ CRL_Distribution_Points() = default;
+
+ explicit CRL_Distribution_Points(const std::vector<Distribution_Point>& points) :
+ m_distribution_points(points) {}
+
+ const std::vector<Distribution_Point>& distribution_points() const
+ { return m_distribution_points; }
+
+ const std::vector<std::string>& crl_distribution_urls() const
+ { return m_crl_distribution_urls; }
+
+ static OID static_oid() { return OID("2.5.29.31"); }
+ OID oid_of() const override { return static_oid(); }
+
+ private:
+ std::string oid_name() const override
+ { return "X509v3.CRLDistributionPoints"; }
+
+ bool should_encode() const override
+ { return !m_distribution_points.empty(); }
+
+ std::vector<uint8_t> encode_inner() const override;
+ void decode_inner(const std::vector<uint8_t>&) override;
+ void contents_to(Data_Store&, Data_Store&) const override;
+
+ std::vector<Distribution_Point> m_distribution_points;
+ std::vector<std::string> m_crl_distribution_urls;
+ };
+
+/**
+* CRL Issuing Distribution Point Extension
+* todo enforce restrictions from RFC 5280 5.2.5
+*/
+class CRL_Issuing_Distribution_Point final : public Certificate_Extension
+ {
+ public:
+ CRL_Issuing_Distribution_Point() = default;
+
+ explicit CRL_Issuing_Distribution_Point(const CRL_Distribution_Points::Distribution_Point& distribution_point) :
+ m_distribution_point(distribution_point) {}
+
+ CRL_Issuing_Distribution_Point* copy() const override
+ { return new CRL_Issuing_Distribution_Point(m_distribution_point); }
+
+ const AlternativeName& get_point() const
+ { return m_distribution_point.point(); }
+
+ static OID static_oid() { return OID("2.5.29.28"); }
+ OID oid_of() const override { return static_oid(); }
+
+ private:
+ std::string oid_name() const override
+ { return "X509v3.CRLIssuingDistributionPoint"; }
+
+ bool should_encode() const override { return true; }
+ std::vector<uint8_t> encode_inner() const override;
+ void decode_inner(const std::vector<uint8_t>&) override;
+ void contents_to(Data_Store&, Data_Store&) const override;
+
+ CRL_Distribution_Points::Distribution_Point m_distribution_point;
+ };
+
+/**
+* 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* copy() const override
+ { return new Unknown_Extension(m_oid, m_critical); }
+
+ /**
+ * Return the OID of this unknown extension
+ */
+ OID oid_of() const override
+ { return m_oid; }
+
+ //static_oid not defined for Unknown_Extension
+
+ /**
+ * Return the extension contents
+ */
+ const std::vector<uint8_t>& extension_contents() const { return m_bytes; }
+
+ /**
+ * Return if this extension was marked critical
+ */
+ bool is_critical_extension() const { return m_critical; }
+
+ void validate(const X509_Certificate&, const X509_Certificate&,
+ const std::vector<std::shared_ptr<const X509_Certificate>>&,
+ std::vector<std::set<Certificate_Status_Code>>& cert_status,
+ size_t pos) override
+ {
+ if(m_critical)
+ {
+ cert_status.at(pos).insert(Certificate_Status_Code::UNKNOWN_CRITICAL_EXTENSION);
+ }
+ }
+
+ private:
+ std::string oid_name() const override { return ""; }
+
+ bool should_encode() const override { return true; }
+ std::vector<uint8_t> encode_inner() const override;
+ void decode_inner(const std::vector<uint8_t>&) override;
+ void contents_to(Data_Store&, Data_Store&) const override;
+
+ OID m_oid;
+ bool m_critical;
+ std::vector<uint8_t> m_bytes;
+ };
+
+ }
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/x509/x509_obj.cpp b/comm/third_party/botan/src/lib/x509/x509_obj.cpp
new file mode 100644
index 0000000000..2bb67629a6
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/x509_obj.cpp
@@ -0,0 +1,424 @@
+/*
+* X.509 SIGNED Object
+* (C) 1999-2007,2020 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/x509_obj.h>
+#include <botan/pubkey.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
+#include <botan/parsing.h>
+#include <botan/pem.h>
+#include <botan/emsa.h>
+#include <algorithm>
+
+namespace Botan {
+
+namespace {
+struct Pss_params
+ {
+ AlgorithmIdentifier hash_algo;
+ AlgorithmIdentifier mask_gen_algo;
+ AlgorithmIdentifier mask_gen_hash; // redundant: decoded mask_gen_algo.parameters
+ size_t salt_len;
+ size_t trailer_field;
+ };
+
+Pss_params decode_pss_params(const std::vector<uint8_t>& encoded_pss_params)
+ {
+ const AlgorithmIdentifier default_hash("SHA-160", AlgorithmIdentifier::USE_NULL_PARAM);
+ const AlgorithmIdentifier default_mgf("MGF1", default_hash.BER_encode());
+
+ Pss_params pss_parameter;
+ BER_Decoder(encoded_pss_params)
+ .start_cons(SEQUENCE)
+ .decode_optional(pss_parameter.hash_algo, ASN1_Tag(0), PRIVATE, default_hash)
+ .decode_optional(pss_parameter.mask_gen_algo, ASN1_Tag(1), PRIVATE, default_mgf)
+ .decode_optional(pss_parameter.salt_len, ASN1_Tag(2), PRIVATE, size_t(20))
+ .decode_optional(pss_parameter.trailer_field, ASN1_Tag(3), PRIVATE, size_t(1))
+ .end_cons();
+
+ BER_Decoder(pss_parameter.mask_gen_algo.get_parameters()).decode(pss_parameter.mask_gen_hash);
+
+ return pss_parameter;
+ }
+}
+
+/*
+* Read a PEM or BER X.509 object
+*/
+void X509_Object::load_data(DataSource& in)
+ {
+ try {
+ if(ASN1::maybe_BER(in) && !PEM_Code::matches(in))
+ {
+ BER_Decoder dec(in);
+ decode_from(dec);
+ }
+ 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 alt_label : alternate_PEM_labels())
+ {
+ if(got_label == alt_label)
+ {
+ is_alternate = true;
+ break;
+ }
+ }
+
+ if(!is_alternate)
+ throw Decoding_Error("Unexpected PEM label for " + PEM_label() + " of " + got_label);
+ }
+
+ BER_Decoder dec(ber);
+ decode_from(dec);
+ }
+ }
+ catch(Decoding_Error& e)
+ {
+ throw Decoding_Error(PEM_label() + " decoding", e);
+ }
+ }
+
+
+void X509_Object::encode_into(DER_Encoder& to) const
+ {
+ to.start_cons(SEQUENCE)
+ .start_cons(SEQUENCE)
+ .raw_bytes(signed_body())
+ .end_cons()
+ .encode(signature_algorithm())
+ .encode(signature(), BIT_STRING)
+ .end_cons();
+ }
+
+/*
+* Read a BER encoded X.509 object
+*/
+void X509_Object::decode_from(BER_Decoder& from)
+ {
+ from.start_cons(SEQUENCE)
+ .start_cons(SEQUENCE)
+ .raw_bytes(m_tbs_bits)
+ .end_cons()
+ .decode(m_sig_algo)
+ .decode(m_sig, BIT_STRING)
+ .end_cons();
+
+ force_decode();
+ }
+
+/*
+* Return a PEM encoded X.509 object
+*/
+std::string X509_Object::PEM_encode() const
+ {
+ return PEM_Code::encode(BER_encode(), PEM_label());
+ }
+
+/*
+* Return the TBS data
+*/
+std::vector<uint8_t> X509_Object::tbs_data() const
+ {
+ return ASN1::put_in_sequence(m_tbs_bits);
+ }
+
+/*
+* Return the hash used in generating the signature
+*/
+std::string X509_Object::hash_used_for_signature() const
+ {
+ const OID& oid = m_sig_algo.get_oid();
+ const std::vector<std::string> sig_info = split_on(oid.to_formatted_string(), '/');
+
+ if(sig_info.size() == 1 && sig_info[0] == "Ed25519")
+ return "SHA-512";
+ else if(sig_info.size() != 2)
+ throw Internal_Error("Invalid name format found for " + oid.to_string());
+
+ if(sig_info[1] == "EMSA4")
+ {
+ const OID hash_oid = decode_pss_params(signature_algorithm().get_parameters()).hash_algo.get_oid();
+ return hash_oid.to_formatted_string();
+ }
+ else
+ {
+ const std::vector<std::string> pad_and_hash =
+ parse_algorithm_name(sig_info[1]);
+
+ if(pad_and_hash.size() != 2)
+ {
+ throw Internal_Error("Invalid name format " + sig_info[1]);
+ }
+
+ return pad_and_hash[1];
+ }
+ }
+
+/*
+* Check the signature on an object
+*/
+bool X509_Object::check_signature(const Public_Key* pub_key) const
+ {
+ if(!pub_key)
+ throw Invalid_Argument("No key provided for " + PEM_label() + " signature check");
+ std::unique_ptr<const Public_Key> key(pub_key);
+ return check_signature(*key);
+ }
+
+bool X509_Object::check_signature(const Public_Key& pub_key) const
+ {
+ const Certificate_Status_Code code = verify_signature(pub_key);
+ return (code == Certificate_Status_Code::VERIFIED);
+ }
+
+Certificate_Status_Code X509_Object::verify_signature(const Public_Key& pub_key) const
+ {
+ const std::vector<std::string> sig_info =
+ split_on(m_sig_algo.get_oid().to_formatted_string(), '/');
+
+ if(sig_info.size() < 1 || sig_info.size() > 2 || sig_info[0] != pub_key.algo_name())
+ return Certificate_Status_Code::SIGNATURE_ALGO_BAD_PARAMS;
+
+ const std::string pub_key_algo = sig_info[0];
+ std::string padding;
+ if(sig_info.size() == 2)
+ padding = sig_info[1];
+ else if(pub_key_algo == "Ed25519" || pub_key_algo == "XMSS")
+ padding = "Pure";
+ else
+ return Certificate_Status_Code::SIGNATURE_ALGO_BAD_PARAMS;
+
+ const Signature_Format format = pub_key.default_x509_signature_format();
+
+ if(padding == "EMSA4")
+ {
+ // "MUST contain RSASSA-PSS-params"
+ if(signature_algorithm().get_parameters().empty())
+ {
+ return Certificate_Status_Code::SIGNATURE_ALGO_BAD_PARAMS;
+ }
+
+ Pss_params pss_parameter = decode_pss_params(signature_algorithm().get_parameters());
+
+ // hash_algo must be SHA1, SHA2-224, SHA2-256, SHA2-384 or SHA2-512
+ const std::string hash_algo = pss_parameter.hash_algo.get_oid().to_formatted_string();
+ if(hash_algo != "SHA-160" &&
+ hash_algo != "SHA-224" &&
+ hash_algo != "SHA-256" &&
+ hash_algo != "SHA-384" &&
+ hash_algo != "SHA-512")
+ {
+ return Certificate_Status_Code::UNTRUSTED_HASH;
+ }
+
+ const std::string mgf_algo = pss_parameter.mask_gen_algo.get_oid().to_formatted_string();
+ if(mgf_algo != "MGF1")
+ {
+ return Certificate_Status_Code::SIGNATURE_ALGO_BAD_PARAMS;
+ }
+
+ // For MGF1, it is strongly RECOMMENDED that the underlying hash function be the same as the one identified by hashAlgorithm
+ // Must be SHA1, SHA2-224, SHA2-256, SHA2-384 or SHA2-512
+ if(pss_parameter.mask_gen_hash.get_oid() != pss_parameter.hash_algo.get_oid())
+ {
+ return Certificate_Status_Code::SIGNATURE_ALGO_BAD_PARAMS;
+ }
+
+ if(pss_parameter.trailer_field != 1)
+ {
+ return Certificate_Status_Code::SIGNATURE_ALGO_BAD_PARAMS;
+ }
+
+ padding += "(" + hash_algo + "," + mgf_algo + "," + std::to_string(pss_parameter.salt_len) + ")";
+ }
+ else
+ {
+ /*
+ * For all other signature types the signature parameters should
+ * be either NULL or empty. In theory there is some distinction between
+ * these but in practice they seem to be used somewhat interchangeably.
+ *
+ * The various RFCs all have prescriptions of what is allowed:
+ * RSA - NULL (RFC 3279)
+ * DSA - empty (RFC 3279)
+ * ECDSA - empty (RFC 3279)
+ * GOST - empty (RFC 4491)
+ * Ed25519 - empty (RFC 8410)
+ * XMSS - empty (draft-vangeest-x509-hash-sigs)
+ *
+ * But in practice we find RSA with empty and ECDSA will NULL all
+ * over the place so it's not really possible to enforce. For Ed25519
+ * and XMSS because they are new we attempt to enforce.
+ */
+ if(pub_key_algo == "Ed25519" || pub_key_algo == "XMSS")
+ {
+ if(!signature_algorithm().parameters_are_empty())
+ {
+ return Certificate_Status_Code::SIGNATURE_ALGO_BAD_PARAMS;
+ }
+ }
+ else
+ {
+ if(!signature_algorithm().parameters_are_null_or_empty())
+ {
+ return Certificate_Status_Code::SIGNATURE_ALGO_BAD_PARAMS;
+ }
+ }
+ }
+
+ try
+ {
+ PK_Verifier verifier(pub_key, padding, format);
+ const bool valid = verifier.verify_message(tbs_data(), signature());
+
+ if(valid)
+ return Certificate_Status_Code::VERIFIED;
+ else
+ return Certificate_Status_Code::SIGNATURE_ERROR;
+ }
+ catch(Algorithm_Not_Found&)
+ {
+ return Certificate_Status_Code::SIGNATURE_ALGO_UNKNOWN;
+ }
+ catch(...)
+ {
+ // This shouldn't happen, fallback to generic signature error
+ return Certificate_Status_Code::SIGNATURE_ERROR;
+ }
+ }
+
+/*
+* Apply the X.509 SIGNED macro
+*/
+std::vector<uint8_t> X509_Object::make_signed(PK_Signer* signer,
+ RandomNumberGenerator& rng,
+ const AlgorithmIdentifier& algo,
+ const secure_vector<uint8_t>& tbs_bits)
+ {
+ const std::vector<uint8_t> signature = signer->sign_message(tbs_bits, rng);
+
+ std::vector<uint8_t> output;
+ DER_Encoder(output)
+ .start_cons(SEQUENCE)
+ .raw_bytes(tbs_bits)
+ .encode(algo)
+ .encode(signature, BIT_STRING)
+ .end_cons();
+
+ return output;
+ }
+
+namespace {
+
+std::string choose_sig_algo(AlgorithmIdentifier& sig_algo,
+ const Private_Key& key,
+ const std::string& hash_fn,
+ const std::string& user_specified)
+ {
+ const std::string algo_name = key.algo_name();
+ std::string padding;
+
+ // check algo_name and set default
+ if(algo_name == "RSA")
+ {
+ // set to EMSA3 for compatibility reasons, originally it was the only option
+ padding = "EMSA3(" + hash_fn + ")";
+ }
+ else if(algo_name == "DSA" ||
+ algo_name == "ECDSA" ||
+ algo_name == "ECGDSA" ||
+ algo_name == "ECKCDSA" ||
+ algo_name == "GOST-34.10" ||
+ algo_name == "GOST-34.10-2012-256" ||
+ algo_name == "GOST-34.10-2012-512")
+ {
+ padding = "EMSA1(" + hash_fn + ")";
+ }
+ else if(algo_name == "Ed25519")
+ {
+ padding = "Pure";
+ }
+ else if(algo_name == "XMSS")
+ {
+ if(user_specified.empty() == true)
+ {
+ throw Invalid_Argument("XMSS requires padding scheme");
+ }
+ padding = user_specified;
+ sig_algo = AlgorithmIdentifier(OID::from_string("XMSS"), AlgorithmIdentifier::USE_EMPTY_PARAM);
+ return padding;
+ }
+ else
+ {
+ throw Invalid_Argument("Unknown X.509 signing key type: " + algo_name);
+ }
+
+ if(user_specified.empty() == false)
+ {
+ padding = user_specified;
+ }
+
+ if(padding != "Pure")
+ {
+ // try to construct an EMSA object from the padding options or default
+ std::unique_ptr<EMSA> emsa;
+ try
+ {
+ emsa.reset(get_emsa(padding));
+ }
+ /*
+ * get_emsa will throw if opts contains {"padding",<valid_padding>} but
+ * <valid_padding> does not specify a hash function.
+ * Omitting it is valid since it needs to be identical to hash_fn.
+ * If it still throws, something happened that we cannot repair here,
+ * e.g. the algorithm/padding combination is not supported.
+ */
+ catch(...)
+ {
+ emsa.reset(get_emsa(padding + "(" + hash_fn + ")"));
+ }
+
+ if(!emsa)
+ {
+ throw Invalid_Argument("Could not parse padding scheme " + padding);
+ }
+
+ sig_algo = emsa->config_for_x509(key, hash_fn);
+ return emsa->name();
+ }
+ else
+ {
+ sig_algo = AlgorithmIdentifier(OID::from_string("Ed25519"), AlgorithmIdentifier::USE_EMPTY_PARAM);
+ return "Pure";
+ }
+ }
+
+}
+
+/*
+* Choose a signing format for the key
+*/
+std::unique_ptr<PK_Signer> X509_Object::choose_sig_format(AlgorithmIdentifier& sig_algo,
+ const Private_Key& key,
+ RandomNumberGenerator& rng,
+ const std::string& hash_fn,
+ const std::string& padding_algo)
+ {
+ const Signature_Format format = key.default_x509_signature_format();
+
+ const std::string emsa = choose_sig_algo(sig_algo, key, hash_fn, padding_algo);
+
+ return std::unique_ptr<PK_Signer>(new PK_Signer(key, rng, emsa, format));
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/x509/x509_obj.h b/comm/third_party/botan/src/lib/x509/x509_obj.h
new file mode 100644
index 0000000000..fd972aed9d
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/x509_obj.h
@@ -0,0 +1,144 @@
+/*
+* X.509 SIGNED Object
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_X509_OBJECT_H_
+#define BOTAN_X509_OBJECT_H_
+
+#include <botan/asn1_obj.h>
+#include <botan/pkix_enums.h>
+#include <vector>
+
+namespace Botan {
+
+class Public_Key;
+class Private_Key;
+class RandomNumberGenerator;
+
+/**
+* This class represents abstract X.509 signed objects as in the X.500
+* SIGNED macro
+*/
+class BOTAN_PUBLIC_API(2,0) X509_Object : public ASN1_Object
+ {
+ public:
+ /**
+ * The underlying data that is to be or was signed
+ * @return data that is or was signed
+ */
+ std::vector<uint8_t> tbs_data() const;
+
+ /**
+ * @return signature on tbs_data()
+ */
+ const std::vector<uint8_t>& signature() const { return m_sig; }
+
+ /**
+ * @return signed body
+ */
+ const std::vector<uint8_t>& signed_body() const { return m_tbs_bits; }
+
+ /**
+ * @return signature algorithm that was used to generate signature
+ */
+ const AlgorithmIdentifier& signature_algorithm() const { return m_sig_algo; }
+
+ /**
+ * @return hash algorithm that was used to generate signature
+ */
+ std::string hash_used_for_signature() const;
+
+ /**
+ * Create a signed X509 object.
+ * @param signer the signer used to sign the object
+ * @param rng the random number generator to use
+ * @param alg_id the algorithm identifier of the signature scheme
+ * @param tbs the tbs bits to be signed
+ * @return signed X509 object
+ */
+ static std::vector<uint8_t> make_signed(class PK_Signer* signer,
+ RandomNumberGenerator& rng,
+ const AlgorithmIdentifier& alg_id,
+ const secure_vector<uint8_t>& tbs);
+
+ /**
+ * Check the signature on this data
+ * @param key the public key purportedly used to sign this data
+ * @return status of the signature - OK if verified or otherwise an indicator of
+ * the problem preventing verification.
+ */
+ Certificate_Status_Code verify_signature(const Public_Key& key) const;
+
+ /**
+ * Check the signature on this data
+ * @param key the public key purportedly used to sign this data
+ * @return true if the signature is valid, otherwise false
+ */
+ bool check_signature(const Public_Key& key) const;
+
+ /**
+ * Check the signature on this data
+ * @param key the public key purportedly used to sign this data
+ * the object will be deleted after use (this should have
+ * been a std::unique_ptr<Public_Key>)
+ * @return true if the signature is valid, otherwise false
+ */
+ bool check_signature(const Public_Key* key) const;
+
+ /**
+ * DER encode an X509_Object
+ * See @ref ASN1_Object::encode_into()
+ */
+ void encode_into(class DER_Encoder& to) const override;
+
+ /**
+ * Decode a BER encoded X509_Object
+ * See @ref ASN1_Object::decode_from()
+ */
+ void decode_from(class BER_Decoder& from) override;
+
+ /**
+ * @return PEM encoding of this
+ */
+ 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<std::string> alternate_PEM_labels() const
+ { return std::vector<std::string>(); }
+
+ virtual ~X509_Object() = default;
+
+ static std::unique_ptr<PK_Signer>
+ choose_sig_format(AlgorithmIdentifier& sig_algo,
+ const Private_Key& key,
+ RandomNumberGenerator& rng,
+ const std::string& hash_fn,
+ const std::string& padding_algo);
+
+ protected:
+
+ X509_Object() = default;
+
+ /**
+ * Decodes from src as either DER or PEM data, then calls force_decode()
+ */
+ void load_data(DataSource& src);
+
+ private:
+ virtual void force_decode() = 0;
+
+ AlgorithmIdentifier m_sig_algo;
+ std::vector<uint8_t> m_tbs_bits;
+ std::vector<uint8_t> m_sig;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/x509/x509cert.cpp b/comm/third_party/botan/src/lib/x509/x509cert.cpp
new file mode 100644
index 0000000000..55f279c58c
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/x509cert.cpp
@@ -0,0 +1,956 @@
+/*
+* X.509 Certificates
+* (C) 1999-2010,2015,2017 Jack Lloyd
+* (C) 2016 René Korthaus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/x509cert.h>
+#include <botan/x509_key.h>
+#include <botan/datastor.h>
+#include <botan/pk_keys.h>
+#include <botan/x509_ext.h>
+#include <botan/ber_dec.h>
+#include <botan/parsing.h>
+#include <botan/bigint.h>
+#include <botan/oids.h>
+#include <botan/hash.h>
+#include <botan/hex.h>
+#include <algorithm>
+#include <sstream>
+
+namespace Botan {
+
+struct X509_Certificate_Data
+ {
+ std::vector<uint8_t> m_serial;
+ AlgorithmIdentifier m_sig_algo_inner;
+ X509_DN m_issuer_dn;
+ X509_DN m_subject_dn;
+ std::vector<uint8_t> m_issuer_dn_bits;
+ std::vector<uint8_t> m_subject_dn_bits;
+ X509_Time m_not_before;
+ X509_Time m_not_after;
+ std::vector<uint8_t> m_subject_public_key_bits;
+ std::vector<uint8_t> m_subject_public_key_bits_seq;
+ std::vector<uint8_t> m_subject_public_key_bitstring;
+ std::vector<uint8_t> m_subject_public_key_bitstring_sha1;
+ AlgorithmIdentifier m_subject_public_key_algid;
+
+ std::vector<uint8_t> m_v2_issuer_key_id;
+ std::vector<uint8_t> m_v2_subject_key_id;
+ Extensions m_v3_extensions;
+
+ std::vector<OID> m_extended_key_usage;
+ std::vector<uint8_t> m_authority_key_id;
+ std::vector<uint8_t> m_subject_key_id;
+ std::vector<OID> m_cert_policies;
+
+ std::vector<std::string> m_crl_distribution_points;
+ std::string m_ocsp_responder;
+ std::vector<std::string> m_ca_issuers;
+
+ std::vector<uint8_t> m_issuer_dn_bits_sha256;
+ std::vector<uint8_t> m_subject_dn_bits_sha256;
+
+ std::string m_fingerprint_sha1;
+ std::string m_fingerprint_sha256;
+
+ AlternativeName m_subject_alt_name;
+ AlternativeName m_issuer_alt_name;
+ NameConstraints m_name_constraints;
+
+ Data_Store m_subject_ds;
+ Data_Store m_issuer_ds;
+
+ size_t m_version = 0;
+ size_t m_path_len_constraint = 0;
+ Key_Constraints m_key_constraints = NO_CONSTRAINTS;
+ bool m_self_signed = false;
+ bool m_is_ca_certificate = false;
+ bool m_serial_negative = false;
+ };
+
+std::string X509_Certificate::PEM_label() const
+ {
+ return "CERTIFICATE";
+ }
+
+std::vector<std::string> X509_Certificate::alternate_PEM_labels() const
+ {
+ return { "X509 CERTIFICATE" };
+ }
+
+X509_Certificate::X509_Certificate(DataSource& src)
+ {
+ load_data(src);
+ }
+
+X509_Certificate::X509_Certificate(const std::vector<uint8_t>& 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);
+ load_data(src);
+ }
+
+#if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
+X509_Certificate::X509_Certificate(const std::string& fsname)
+ {
+ DataSource_Stream src(fsname, true);
+ load_data(src);
+ }
+#endif
+
+namespace {
+
+std::unique_ptr<X509_Certificate_Data> parse_x509_cert_body(const X509_Object& obj)
+ {
+ std::unique_ptr<X509_Certificate_Data> data(new X509_Certificate_Data);
+
+ BigInt serial_bn;
+ BER_Object public_key;
+ BER_Object v3_exts_data;
+
+ BER_Decoder(obj.signed_body())
+ .decode_optional(data->m_version, ASN1_Tag(0), ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC))
+ .decode(serial_bn)
+ .decode(data->m_sig_algo_inner)
+ .decode(data->m_issuer_dn)
+ .start_cons(SEQUENCE)
+ .decode(data->m_not_before)
+ .decode(data->m_not_after)
+ .end_cons()
+ .decode(data->m_subject_dn)
+ .get_next(public_key)
+ .decode_optional_string(data->m_v2_issuer_key_id, BIT_STRING, 1)
+ .decode_optional_string(data->m_v2_subject_key_id, BIT_STRING, 2)
+ .get_next(v3_exts_data)
+ .verify_end("TBSCertificate has extra data after extensions block");
+
+ if(data->m_version > 2)
+ 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");
+
+ public_key.assert_is_a(SEQUENCE, CONSTRUCTED, "X.509 certificate public key");
+
+ // crude method to save the serial's sign; will get lost during decoding, otherwise
+ data->m_serial_negative = serial_bn.is_negative();
+
+ // for general sanity convert wire version (0 based) to standards version (v1 .. v3)
+ data->m_version += 1;
+
+ data->m_serial = BigInt::encode(serial_bn);
+ 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());
+
+ // validate_public_key_params(public_key.value);
+ AlgorithmIdentifier public_key_alg_id;
+ BER_Decoder(public_key).decode(public_key_alg_id).discard_remaining();
+
+ const std::vector<std::string> public_key_info =
+ split_on(OIDS::oid2str_or_empty(public_key_alg_id.get_oid()), '/');
+
+ if(!public_key_info.empty() && public_key_info[0] == "RSA")
+ {
+ // RFC4055: If PublicKeyAlgo = PSS or OAEP: limit the use of the public key exclusively to either RSASSA - PSS or RSAES - OAEP
+ if(public_key_info.size() >= 2)
+ {
+ if(public_key_info[1] == "EMSA4")
+ {
+ /*
+ When the RSA private key owner wishes to limit the use of the public
+ key exclusively to RSASSA-PSS, then the id-RSASSA-PSS object
+ identifier MUST be used in the algorithm field within the subject
+ public key information, and, if present, the parameters field MUST
+ contain RSASSA-PSS-params.
+
+ All parameters in the signature structure algorithm identifier MUST
+ match the parameters in the key structure algorithm identifier
+ except the saltLength field. The saltLength field in the signature parameters
+ MUST be greater or equal to that in the key parameters field.
+
+ ToDo: Allow salt length to be greater
+ */
+ if(public_key_alg_id != obj.signature_algorithm())
+ {
+ throw Decoding_Error("Algorithm identifier mismatch");
+ }
+ }
+ }
+ else
+ {
+ // oid = rsaEncryption -> parameters field MUST contain NULL
+ if(public_key_alg_id != AlgorithmIdentifier(public_key_alg_id.get_oid(), AlgorithmIdentifier::USE_NULL_PARAM))
+ {
+ throw Decoding_Error("RSA algorithm parameters field MUST contain NULL");
+ }
+ }
+ }
+
+ data->m_subject_public_key_bits.assign(public_key.bits(), public_key.bits() + public_key.length());
+
+ 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)
+ .decode(data->m_subject_public_key_algid)
+ .decode(data->m_subject_public_key_bitstring, BIT_STRING);
+
+ if(v3_exts_data.is_a(3, ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC)))
+ {
+ // Path validation will reject a v1/v2 cert with v3 extensions
+ BER_Decoder(v3_exts_data).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<Cert_Extension::Key_Usage>())
+ {
+ data->m_key_constraints = ext->get_constraints();
+ /*
+ RFC 5280: When the keyUsage extension appears in a certificate,
+ at least one of the bits MUST be set to 1.
+ */
+ if(data->m_key_constraints == NO_CONSTRAINTS)
+ {
+ throw Decoding_Error("Certificate has invalid encoding for KeyUsage");
+ }
+ }
+ else
+ {
+ data->m_key_constraints = NO_CONSTRAINTS;
+ }
+
+ if(auto ext = data->m_v3_extensions.get_extension_object_as<Cert_Extension::Subject_Key_ID>())
+ {
+ data->m_subject_key_id = ext->get_key_id();
+ }
+
+ if(auto ext = data->m_v3_extensions.get_extension_object_as<Cert_Extension::Authority_Key_ID>())
+ {
+ data->m_authority_key_id = ext->get_key_id();
+ }
+
+ if(auto ext = data->m_v3_extensions.get_extension_object_as<Cert_Extension::Name_Constraints>())
+ {
+ data->m_name_constraints = ext->get_name_constraints();
+ }
+
+ if(auto ext = data->m_v3_extensions.get_extension_object_as<Cert_Extension::Basic_Constraints>())
+ {
+ if(ext->get_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
+ * missing, as do most other implementations. But it may be worth
+ * removing this entirely, or alternately adding a warning level
+ * validation failure for it.
+ */
+ if(data->m_key_constraints == NO_CONSTRAINTS ||
+ (data->m_key_constraints & KEY_CERT_SIGN))
+ {
+ data->m_is_ca_certificate = true;
+ data->m_path_len_constraint = ext->get_path_limit();
+ }
+ }
+ }
+
+ if(auto ext = data->m_v3_extensions.get_extension_object_as<Cert_Extension::Issuer_Alternative_Name>())
+ {
+ data->m_issuer_alt_name = ext->get_alt_name();
+ }
+
+ if(auto ext = data->m_v3_extensions.get_extension_object_as<Cert_Extension::Subject_Alternative_Name>())
+ {
+ data->m_subject_alt_name = ext->get_alt_name();
+ }
+
+ if(auto ext = data->m_v3_extensions.get_extension_object_as<Cert_Extension::Extended_Key_Usage>())
+ {
+ data->m_extended_key_usage = ext->get_oids();
+ }
+
+ if(auto ext = data->m_v3_extensions.get_extension_object_as<Cert_Extension::Certificate_Policies>())
+ {
+ data->m_cert_policies = ext->get_policy_oids();
+ }
+
+ if(auto ext = data->m_v3_extensions.get_extension_object_as<Cert_Extension::Authority_Information_Access>())
+ {
+ data->m_ocsp_responder = ext->ocsp_responder();
+ data->m_ca_issuers = ext->ca_issuers();
+ }
+
+ if(auto ext = data->m_v3_extensions.get_extension_object_as<Cert_Extension::CRL_Distribution_Points>())
+ {
+ data->m_crl_distribution_points = ext->crl_distribution_urls();
+ }
+
+ // Check for self-signed vs self-issued certificates
+ if(data->m_subject_dn == data->m_issuer_dn)
+ {
+ if(data->m_subject_key_id.empty() == false && data->m_authority_key_id.empty() == false)
+ {
+ 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).
+ */
+ data->m_self_signed = true;
+
+ try
+ {
+ std::unique_ptr<Public_Key> pub_key(X509::load_key(data->m_subject_public_key_bits_seq));
+
+ Certificate_Status_Code sig_status = obj.verify_signature(*pub_key);
+
+ if(sig_status == Certificate_Status_Code::OK ||
+ sig_status == 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
+ }
+ }
+ }
+
+ const std::vector<uint8_t> full_encoding = obj.BER_encode();
+
+ std::unique_ptr<HashFunction> sha1(HashFunction::create("SHA-1"));
+ if(sha1)
+ {
+ sha1->update(data->m_subject_public_key_bitstring);
+ 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");
+ }
+
+ std::unique_ptr<HashFunction> sha256(HashFunction::create("SHA-256"));
+ if(sha256)
+ {
+ 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");
+ }
+
+ data->m_subject_ds.add(data->m_subject_dn.contents());
+ data->m_issuer_ds.add(data->m_issuer_dn.contents());
+ data->m_v3_extensions.contents_to(data->m_subject_ds, data->m_issuer_ds);
+
+ return data;
+ }
+
+}
+
+/*
+* Decode the TBSCertificate data
+*/
+void X509_Certificate::force_decode()
+ {
+ m_data.reset();
+
+ std::unique_ptr<X509_Certificate_Data> data = parse_x509_cert_body(*this);
+
+ m_data.reset(data.release());
+ }
+
+const X509_Certificate_Data& X509_Certificate::data() const
+ {
+ if(m_data == nullptr)
+ {
+ throw Invalid_State("X509_Certificate uninitialized");
+ }
+ return *m_data.get();
+ }
+
+uint32_t X509_Certificate::x509_version() const
+ {
+ return static_cast<uint32_t>(data().m_version);
+ }
+
+bool X509_Certificate::is_self_signed() const
+ {
+ return data().m_self_signed;
+ }
+
+const X509_Time& X509_Certificate::not_before() const
+ {
+ return data().m_not_before;
+ }
+
+const X509_Time& X509_Certificate::not_after() const
+ {
+ return data().m_not_after;
+ }
+
+const AlgorithmIdentifier& X509_Certificate::subject_public_key_algo() const
+ {
+ return data().m_subject_public_key_algid;
+ }
+
+const std::vector<uint8_t>& X509_Certificate::v2_issuer_key_id() const
+ {
+ return data().m_v2_issuer_key_id;
+ }
+
+const std::vector<uint8_t>& X509_Certificate::v2_subject_key_id() const
+ {
+ return data().m_v2_subject_key_id;
+ }
+
+const std::vector<uint8_t>& X509_Certificate::subject_public_key_bits() const
+ {
+ return data().m_subject_public_key_bits;
+ }
+
+const std::vector<uint8_t>& X509_Certificate::subject_public_key_info() const
+ {
+ return data().m_subject_public_key_bits_seq;
+ }
+
+const std::vector<uint8_t>& X509_Certificate::subject_public_key_bitstring() const
+ {
+ return data().m_subject_public_key_bitstring;
+ }
+
+const std::vector<uint8_t>& X509_Certificate::subject_public_key_bitstring_sha1() const
+ {
+ if(data().m_subject_public_key_bitstring_sha1.empty())
+ throw Encoding_Error("X509_Certificate::subject_public_key_bitstring_sha1 called but SHA-1 disabled in build");
+
+ return data().m_subject_public_key_bitstring_sha1;
+ }
+
+const std::vector<uint8_t>& X509_Certificate::authority_key_id() const
+ {
+ return data().m_authority_key_id;
+ }
+
+const std::vector<uint8_t>& X509_Certificate::subject_key_id() const
+ {
+ return data().m_subject_key_id;
+ }
+
+const std::vector<uint8_t>& X509_Certificate::serial_number() const
+ {
+ return data().m_serial;
+ }
+
+bool X509_Certificate::is_serial_negative() const
+ {
+ return data().m_serial_negative;
+ }
+
+
+const X509_DN& X509_Certificate::issuer_dn() const
+ {
+ return data().m_issuer_dn;
+ }
+
+const X509_DN& X509_Certificate::subject_dn() const
+ {
+ return data().m_subject_dn;
+ }
+
+const std::vector<uint8_t>& X509_Certificate::raw_issuer_dn() const
+ {
+ return data().m_issuer_dn_bits;
+ }
+
+const std::vector<uint8_t>& X509_Certificate::raw_subject_dn() const
+ {
+ return data().m_subject_dn_bits;
+ }
+
+bool X509_Certificate::is_CA_cert() const
+ {
+ if(data().m_version < 3 && data().m_self_signed)
+ return true;
+
+ return data().m_is_ca_certificate;
+ }
+
+uint32_t X509_Certificate::path_limit() const
+ {
+ if(data().m_version < 3 && data().m_self_signed)
+ return 32; // in theory infinite, but this is more than enough
+
+ return static_cast<uint32_t>(data().m_path_len_constraint);
+ }
+
+Key_Constraints X509_Certificate::constraints() const
+ {
+ return data().m_key_constraints;
+ }
+
+const std::vector<OID>& X509_Certificate::extended_key_usage() const
+ {
+ return data().m_extended_key_usage;
+ }
+
+const std::vector<OID>& X509_Certificate::certificate_policy_oids() const
+ {
+ return data().m_cert_policies;
+ }
+
+const NameConstraints& X509_Certificate::name_constraints() const
+ {
+ return data().m_name_constraints;
+ }
+
+const Extensions& X509_Certificate::v3_extensions() const
+ {
+ return data().m_v3_extensions;
+ }
+
+bool X509_Certificate::allowed_usage(Key_Constraints usage) const
+ {
+ if(constraints() == NO_CONSTRAINTS)
+ return true;
+ return ((constraints() & usage) == usage);
+ }
+
+bool X509_Certificate::allowed_extended_usage(const std::string& usage) const
+ {
+ return allowed_extended_usage(OID::from_string(usage));
+ }
+
+bool X509_Certificate::allowed_extended_usage(const OID& usage) const
+ {
+ const std::vector<OID>& ex = extended_key_usage();
+ if(ex.empty())
+ return true;
+
+ if(std::find(ex.begin(), ex.end(), usage) != ex.end())
+ return true;
+
+ return false;
+ }
+
+bool X509_Certificate::allowed_usage(Usage_Type usage) const
+ {
+ // These follow suggestions in RFC 5280 4.2.1.12
+
+ switch(usage)
+ {
+ case Usage_Type::UNSPECIFIED:
+ return true;
+
+ case Usage_Type::TLS_SERVER_AUTH:
+ return (allowed_usage(KEY_AGREEMENT) || allowed_usage(KEY_ENCIPHERMENT) || allowed_usage(DIGITAL_SIGNATURE)) && allowed_extended_usage("PKIX.ServerAuth");
+
+ case Usage_Type::TLS_CLIENT_AUTH:
+ return (allowed_usage(DIGITAL_SIGNATURE) || allowed_usage(KEY_AGREEMENT)) && allowed_extended_usage("PKIX.ClientAuth");
+
+ case Usage_Type::OCSP_RESPONDER:
+ return (allowed_usage(DIGITAL_SIGNATURE) || allowed_usage(NON_REPUDIATION)) && allowed_extended_usage("PKIX.OCSPSigning");
+
+ case Usage_Type::CERTIFICATE_AUTHORITY:
+ return is_CA_cert();
+
+ case Usage_Type::ENCRYPTION:
+ return (allowed_usage(KEY_ENCIPHERMENT) || allowed_usage(DATA_ENCIPHERMENT));
+ }
+
+ return false;
+ }
+
+bool X509_Certificate::has_constraints(Key_Constraints constraints) const
+ {
+ if(this->constraints() == NO_CONSTRAINTS)
+ {
+ return false;
+ }
+
+ return ((this->constraints() & constraints) != 0);
+ }
+
+bool X509_Certificate::has_ex_constraint(const std::string& ex_constraint) const
+ {
+ return has_ex_constraint(OID::from_string(ex_constraint));
+ }
+
+bool X509_Certificate::has_ex_constraint(const OID& usage) const
+ {
+ const std::vector<OID>& ex = extended_key_usage();
+ return (std::find(ex.begin(), ex.end(), usage) != ex.end());
+ }
+
+/*
+* Return if a certificate extension is marked critical
+*/
+bool X509_Certificate::is_critical(const std::string& ex_name) const
+ {
+ return v3_extensions().critical_extension_set(OID::from_string(ex_name));
+ }
+
+std::string X509_Certificate::ocsp_responder() const
+ {
+ return data().m_ocsp_responder;
+ }
+
+std::vector<std::string> X509_Certificate::ca_issuers() const
+ {
+ return data().m_ca_issuers;
+ }
+
+std::string X509_Certificate::crl_distribution_point() const
+ {
+ // just returns the first (arbitrarily)
+ if(data().m_crl_distribution_points.size() > 0)
+ return data().m_crl_distribution_points[0];
+ return "";
+ }
+
+const AlternativeName& X509_Certificate::subject_alt_name() const
+ {
+ return data().m_subject_alt_name;
+ }
+
+const AlternativeName& X509_Certificate::issuer_alt_name() const
+ {
+ return data().m_issuer_alt_name;
+ }
+
+/*
+* Return information about the subject
+*/
+std::vector<std::string>
+X509_Certificate::subject_info(const std::string& req) const
+ {
+ if(req == "Email")
+ return this->subject_info("RFC822");
+
+ if(subject_dn().has_field(req))
+ return subject_dn().get_attribute(req);
+
+ if(subject_alt_name().has_field(req))
+ return subject_alt_name().get_attribute(req);
+
+ // These will be removed later:
+ if(req == "X509.Certificate.v2.key_id")
+ return {hex_encode(this->v2_subject_key_id())};
+ if(req == "X509v3.SubjectKeyIdentifier")
+ return {hex_encode(this->subject_key_id())};
+ if(req == "X509.Certificate.dn_bits")
+ return {hex_encode(this->raw_subject_dn())};
+ if(req == "X509.Certificate.start")
+ return {not_before().to_string()};
+ if(req == "X509.Certificate.end")
+ return {not_after().to_string()};
+
+ if(req == "X509.Certificate.version")
+ return {std::to_string(x509_version())};
+ if(req == "X509.Certificate.serial")
+ return {hex_encode(serial_number())};
+
+ return data().m_subject_ds.get(req);
+ }
+
+/*
+* Return information about the issuer
+*/
+std::vector<std::string>
+X509_Certificate::issuer_info(const std::string& req) const
+ {
+ if(issuer_dn().has_field(req))
+ return issuer_dn().get_attribute(req);
+
+ if(issuer_alt_name().has_field(req))
+ return issuer_alt_name().get_attribute(req);
+
+ // These will be removed later:
+ if(req == "X509.Certificate.v2.key_id")
+ return {hex_encode(this->v2_issuer_key_id())};
+ if(req == "X509v3.AuthorityKeyIdentifier")
+ return {hex_encode(this->authority_key_id())};
+ if(req == "X509.Certificate.dn_bits")
+ return {hex_encode(this->raw_issuer_dn())};
+
+ return data().m_issuer_ds.get(req);
+ }
+
+/*
+* Return the public key in this certificate
+*/
+std::unique_ptr<Public_Key> X509_Certificate::load_subject_public_key() const
+ {
+ try
+ {
+ return std::unique_ptr<Public_Key>(X509::load_key(subject_public_key_info()));
+ }
+ catch(std::exception& e)
+ {
+ throw Decoding_Error("X509_Certificate::load_subject_public_key", e);
+ }
+ }
+
+Public_Key* X509_Certificate::subject_public_key() const
+ {
+ return load_subject_public_key().release();
+ }
+
+std::vector<uint8_t> 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<uint8_t> 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");
+ return data().m_subject_dn_bits_sha256;
+ }
+
+namespace {
+
+/*
+* Lookup each OID in the vector
+*/
+std::vector<std::string> lookup_oids(const std::vector<OID>& oids)
+ {
+ std::vector<std::string> out;
+
+ for(const OID& oid : oids)
+ {
+ out.push_back(oid.to_formatted_string());
+ }
+ return out;
+ }
+
+}
+
+/*
+* Return the list of extended key usage OIDs
+*/
+std::vector<std::string> X509_Certificate::ex_constraints() const
+ {
+ return lookup_oids(extended_key_usage());
+ }
+
+/*
+* Return the list of certificate policies
+*/
+std::vector<std::string> X509_Certificate::policies() const
+ {
+ return lookup_oids(certificate_policy_oids());
+ }
+
+std::string X509_Certificate::fingerprint(const std::string& hash_name) const
+ {
+ /*
+ * The SHA-1 and SHA-256 fingerprints are precomputed since these
+ * are the most commonly used. Especially, SHA-256 fingerprints are
+ * used for cycle detection during path construction.
+ *
+ * If SHA-1 or SHA-256 was missing at parsing time the vectors are
+ * left empty in which case we fall back to create_hex_fingerprint
+ * which will throw if the hash is unavailable.
+ */
+ if(hash_name == "SHA-256" && data().m_fingerprint_sha256.size() > 0)
+ return data().m_fingerprint_sha256;
+ else if(hash_name == "SHA-1" && data().m_fingerprint_sha1.size() > 0)
+ return data().m_fingerprint_sha1;
+ else
+ return create_hex_fingerprint(this->BER_encode(), hash_name);
+ }
+
+bool X509_Certificate::matches_dns_name(const std::string& name) const
+ {
+ if(name.empty())
+ return false;
+
+ std::vector<std::string> issued_names = subject_info("DNS");
+
+ // Fall back to CN only if no DNS names are set (RFC 6125 sec 6.4.4)
+ if(issued_names.empty())
+ issued_names = subject_info("Name");
+
+ for(size_t i = 0; i != issued_names.size(); ++i)
+ {
+ if(host_wildcard_match(issued_names[i], name))
+ return true;
+ }
+
+ return false;
+ }
+
+/*
+* Compare two certificates for equality
+*/
+bool X509_Certificate::operator==(const X509_Certificate& other) const
+ {
+ return (this->signature() == other.signature() &&
+ this->signature_algorithm() == other.signature_algorithm() &&
+ this->signed_body() == other.signed_body());
+ }
+
+bool X509_Certificate::operator<(const X509_Certificate& other) const
+ {
+ /* If signature values are not equal, sort by lexicographic ordering of that */
+ if(this->signature() != other.signature())
+ {
+ return (this->signature() < other.signature());
+ }
+
+ // Then compare the signed contents
+ return this->signed_body() < other.signed_body();
+ }
+
+/*
+* X.509 Certificate Comparison
+*/
+bool operator!=(const X509_Certificate& cert1, const X509_Certificate& cert2)
+ {
+ return !(cert1 == cert2);
+ }
+
+std::string X509_Certificate::to_string() const
+ {
+ std::ostringstream out;
+
+ out << "Version: " << this->x509_version() << "\n";
+ out << "Subject: " << subject_dn() << "\n";
+ out << "Issuer: " << issuer_dn() << "\n";
+ out << "Issued: " << this->not_before().readable_string() << "\n";
+ out << "Expires: " << this->not_after().readable_string() << "\n";
+
+ out << "Constraints:\n";
+ Key_Constraints constraints = this->constraints();
+ if(constraints == NO_CONSTRAINTS)
+ out << " None\n";
+ else
+ {
+ if(constraints & DIGITAL_SIGNATURE)
+ out << " Digital Signature\n";
+ if(constraints & NON_REPUDIATION)
+ out << " Non-Repudiation\n";
+ if(constraints & KEY_ENCIPHERMENT)
+ out << " Key Encipherment\n";
+ if(constraints & DATA_ENCIPHERMENT)
+ out << " Data Encipherment\n";
+ if(constraints & KEY_AGREEMENT)
+ out << " Key Agreement\n";
+ if(constraints & KEY_CERT_SIGN)
+ out << " Cert Sign\n";
+ if(constraints & CRL_SIGN)
+ out << " CRL Sign\n";
+ if(constraints & ENCIPHER_ONLY)
+ out << " Encipher Only\n";
+ if(constraints & DECIPHER_ONLY)
+ out << " Decipher Only\n";
+ }
+
+ const std::vector<OID>& policies = this->certificate_policy_oids();
+ if(!policies.empty())
+ {
+ out << "Policies: " << "\n";
+ for(auto oid : policies)
+ out << " " << oid.to_string() << "\n";
+ }
+
+ const std::vector<OID>& ex_constraints = this->extended_key_usage();
+ if(!ex_constraints.empty())
+ {
+ out << "Extended Constraints:\n";
+ for(auto&& oid : ex_constraints)
+ {
+ out << " " << oid.to_formatted_string() << "\n";
+ }
+ }
+
+ const NameConstraints& name_constraints = this->name_constraints();
+
+ if(!name_constraints.permitted().empty() || !name_constraints.excluded().empty())
+ {
+ out << "Name Constraints:\n";
+
+ if(!name_constraints.permitted().empty())
+ {
+ out << " Permit";
+ for(auto st: name_constraints.permitted())
+ {
+ out << " " << st.base();
+ }
+ out << "\n";
+ }
+
+ if(!name_constraints.excluded().empty())
+ {
+ out << " Exclude";
+ for(auto st: name_constraints.excluded())
+ {
+ out << " " << st.base();
+ }
+ out << "\n";
+ }
+ }
+
+ if(!ocsp_responder().empty())
+ out << "OCSP responder " << ocsp_responder() << "\n";
+
+ const std::vector<std::string> ca_issuers = this->ca_issuers();
+ if(!ca_issuers.empty())
+ {
+ out << "CA Issuers:\n";
+ for(size_t i = 0; i != ca_issuers.size(); i++)
+ out << " URI: " << ca_issuers[i] << "\n";
+ }
+
+ if(!crl_distribution_point().empty())
+ out << "CRL " << crl_distribution_point() << "\n";
+
+ out << "Signature algorithm: " << this->signature_algorithm().get_oid().to_formatted_string() << "\n";
+
+ out << "Serial number: " << hex_encode(this->serial_number()) << "\n";
+
+ if(this->authority_key_id().size())
+ out << "Authority keyid: " << hex_encode(this->authority_key_id()) << "\n";
+
+ if(this->subject_key_id().size())
+ out << "Subject keyid: " << hex_encode(this->subject_key_id()) << "\n";
+
+ try
+ {
+ std::unique_ptr<Public_Key> pubkey(this->subject_public_key());
+ out << "Public Key [" << pubkey->algo_name() << "-" << pubkey->key_length() << "]\n\n";
+ out << X509::PEM_encode(*pubkey);
+ }
+ catch(Decoding_Error&)
+ {
+ const AlgorithmIdentifier& alg_id = this->subject_public_key_algo();
+ out << "Failed to decode key with oid " << alg_id.get_oid().to_string() << "\n";
+ }
+
+ return out.str();
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/x509/x509cert.h b/comm/third_party/botan/src/lib/x509/x509cert.h
new file mode 100644
index 0000000000..0355bbb58e
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/x509cert.h
@@ -0,0 +1,461 @@
+/*
+* X.509 Certificates
+* (C) 1999-2007,2015,2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_X509_CERTS_H_
+#define BOTAN_X509_CERTS_H_
+
+#include <botan/x509_obj.h>
+#include <memory>
+
+namespace Botan {
+
+class Public_Key;
+class X509_DN;
+class Extensions;
+class AlternativeName;
+class NameConstraints;
+
+enum class Usage_Type
+ {
+ UNSPECIFIED, // no restrictions
+ TLS_SERVER_AUTH,
+ TLS_CLIENT_AUTH,
+ CERTIFICATE_AUTHORITY,
+ OCSP_RESPONDER,
+ ENCRYPTION
+ };
+
+struct X509_Certificate_Data;
+
+/**
+* This class represents an X.509 Certificate
+*/
+class BOTAN_PUBLIC_API(2,0) X509_Certificate : public X509_Object
+ {
+ public:
+ /**
+ * Return a newly allocated copy of the public key associated
+ * with the subject of this certificate. This object is owned
+ * by the caller.
+ *
+ * Prefer load_subject_public_key in new code
+ *
+ * @return public key
+ */
+ Public_Key* subject_public_key() const;
+
+ /**
+ * Create a public key object associated with the public key bits in this
+ * certificate. If the public key bits was valid for X.509 encoding
+ * purposes but invalid algorithmically (for example, RSA with an even
+ * modulus) that will be detected at this point, and an exception will be
+ * thrown.
+ *
+ * @return subject public key of this certificate
+ */
+ std::unique_ptr<Public_Key> load_subject_public_key() const;
+
+ /**
+ * Get the public key associated with this certificate. This includes the
+ * outer AlgorithmIdentifier
+ * @return subject public key of this certificate
+ */
+ const std::vector<uint8_t>& subject_public_key_bits() const;
+
+ /**
+ * Get the SubjectPublicKeyInfo associated with this certificate.
+ * @return subject public key info of this certificate
+ */
+ const std::vector<uint8_t>& subject_public_key_info() const;
+
+ /**
+ * Return the algorithm identifier of the public key
+ */
+ const AlgorithmIdentifier& subject_public_key_algo() const;
+
+ /**
+ * Get the bit string of the public key associated with this certificate
+ * @return public key bits
+ */
+ const std::vector<uint8_t>& subject_public_key_bitstring() const;
+
+ /**
+ * Get the SHA-1 bit string of the public key associated with this certificate.
+ * This is used for OCSP among other protocols.
+ * This function will throw if SHA-1 is not available.
+ * @return hash of subject public key of this certificate
+ */
+ const std::vector<uint8_t>& subject_public_key_bitstring_sha1() const;
+
+ /**
+ * Get the certificate's issuer distinguished name (DN).
+ * @return issuer DN of this certificate
+ */
+ const X509_DN& issuer_dn() const;
+
+ /**
+ * Get the certificate's subject distinguished name (DN).
+ * @return subject DN of this certificate
+ */
+ const X509_DN& subject_dn() const;
+
+ /**
+ * Get a value for a specific subject_info parameter name.
+ * @param name the name of the parameter to look up. Possible names include
+ * "X509.Certificate.version", "X509.Certificate.serial",
+ * "X509.Certificate.start", "X509.Certificate.end",
+ * "X509.Certificate.v2.key_id", "X509.Certificate.public_key",
+ * "X509v3.BasicConstraints.path_constraint",
+ * "X509v3.BasicConstraints.is_ca", "X509v3.NameConstraints",
+ * "X509v3.ExtendedKeyUsage", "X509v3.CertificatePolicies",
+ * "X509v3.SubjectKeyIdentifier", "X509.Certificate.serial",
+ * "X520.CommonName", "X520.Organization", "X520.Country",
+ * "RFC822" (Email in SAN) or "PKCS9.EmailAddress" (Email in DN).
+ * @return value(s) of the specified parameter
+ */
+ std::vector<std::string> subject_info(const std::string& name) const;
+
+ /**
+ * Get a value for a specific subject_info parameter name.
+ * @param name the name of the parameter to look up. Possible names are
+ * "X509.Certificate.v2.key_id" or "X509v3.AuthorityKeyIdentifier".
+ * @return value(s) of the specified parameter
+ */
+ std::vector<std::string> issuer_info(const std::string& name) const;
+
+ /**
+ * Raw issuer DN bits
+ */
+ const std::vector<uint8_t>& raw_issuer_dn() const;
+
+ /**
+ * SHA-256 of Raw issuer DN
+ */
+ std::vector<uint8_t> raw_issuer_dn_sha256() const;
+
+ /**
+ * Raw subject DN
+ */
+ const std::vector<uint8_t>& raw_subject_dn() const;
+
+ /**
+ * SHA-256 of Raw subject DN
+ */
+ std::vector<uint8_t> raw_subject_dn_sha256() const;
+
+ /**
+ * Get the notBefore of the certificate as a string
+ * @return notBefore of the certificate
+ */
+ std::string BOTAN_DEPRECATED("Use not_before().to_string()") start_time() const
+ {
+ return not_before().to_string();
+ }
+
+ /**
+ * Get the notAfter of the certificate as a string
+ * @return notAfter of the certificate
+ */
+ std::string BOTAN_DEPRECATED("Use not_after().to_string()") end_time() const
+ {
+ return not_after().to_string();
+ }
+
+ /**
+ * Get the notBefore of the certificate as X509_Time
+ * @return notBefore of the certificate
+ */
+ const X509_Time& not_before() const;
+
+ /**
+ * Get the notAfter of the certificate as X509_Time
+ * @return notAfter of the certificate
+ */
+ const X509_Time& not_after() const;
+
+ /**
+ * Get the X509 version of this certificate object.
+ * @return X509 version
+ */
+ uint32_t x509_version() const;
+
+ /**
+ * Get the serial number of this certificate.
+ * @return certificates serial number
+ */
+ const std::vector<uint8_t>& serial_number() const;
+
+ /**
+ * Get the serial number's sign
+ * @return 1 iff the serial is negative.
+ */
+ bool is_serial_negative() const;
+
+ /**
+ * Get the DER encoded AuthorityKeyIdentifier of this certificate.
+ * @return DER encoded AuthorityKeyIdentifier
+ */
+ const std::vector<uint8_t>& authority_key_id() const;
+
+ /**
+ * Get the DER encoded SubjectKeyIdentifier of this certificate.
+ * @return DER encoded SubjectKeyIdentifier
+ */
+ const std::vector<uint8_t>& subject_key_id() const;
+
+ /**
+ * Check whether this certificate is self signed.
+ * If the DN issuer and subject agree,
+ * @return true if this certificate is self signed
+ */
+ bool is_self_signed() const;
+
+ /**
+ * Check whether this certificate is a CA certificate.
+ * @return true if this certificate is a CA certificate
+ */
+ bool is_CA_cert() const;
+
+ /**
+ * Returns true if the specified @param usage is set in the key usage extension
+ * or if no key usage constraints are set at all.
+ * To check if a certain key constraint is set in the certificate
+ * use @see X509_Certificate#has_constraints.
+ */
+ bool allowed_usage(Key_Constraints usage) const;
+
+ /**
+ * Returns true if the specified @param usage is set in the extended key usage extension
+ * or if no extended key usage constraints are set at all.
+ * To check if a certain extended key constraint is set in the certificate
+ * use @see X509_Certificate#has_ex_constraint.
+ */
+ bool allowed_extended_usage(const std::string& usage) const;
+
+ /**
+ * Returns true if the specified usage is set in the extended key usage extension,
+ * or if no extended key usage constraints are set at all.
+ * To check if a certain extended key constraint is set in the certificate
+ * use @see X509_Certificate#has_ex_constraint.
+ */
+ bool allowed_extended_usage(const OID& usage) const;
+
+ /**
+ * Returns true if the required key and extended key constraints are set in the certificate
+ * for the specified @param usage or if no key constraints are set in both the key usage
+ * and extended key usage extension.
+ */
+ bool allowed_usage(Usage_Type usage) const;
+
+ /**
+ * Returns true if the specified @param constraints are included in the key
+ * usage extension.
+ */
+ bool has_constraints(Key_Constraints constraints) const;
+
+ /**
+ * Returns true if and only if @param ex_constraint (referring to an
+ * extended key constraint, eg "PKIX.ServerAuth") is included in the
+ * extended key extension.
+ */
+ bool BOTAN_DEPRECATED("Use version taking an OID")
+ has_ex_constraint(const std::string& ex_constraint) const;
+
+ /**
+ * Returns true if and only if OID @param ex_constraint is
+ * included in the extended key extension.
+ */
+ bool has_ex_constraint(const OID& ex_constraint) const;
+
+ /**
+ * Get the path limit as defined in the BasicConstraints extension of
+ * this certificate.
+ * @return path limit
+ */
+ uint32_t path_limit() const;
+
+ /**
+ * Check whenever a given X509 Extension is marked critical in this
+ * certificate.
+ */
+ bool is_critical(const std::string& ex_name) const;
+
+ /**
+ * Get the key constraints as defined in the KeyUsage extension of this
+ * certificate.
+ * @return key constraints
+ */
+ Key_Constraints constraints() const;
+
+ /**
+ * Get the key constraints as defined in the ExtendedKeyUsage
+ * extension of this certificate.
+ * @return key constraints
+ */
+ std::vector<std::string>
+ BOTAN_DEPRECATED("Use extended_key_usage") ex_constraints() const;
+
+ /**
+ * Get the key usage as defined in the ExtendedKeyUsage extension
+ * of this certificate, or else an empty vector.
+ * @return key usage
+ */
+ const std::vector<OID>& extended_key_usage() const;
+
+ /**
+ * Get the name constraints as defined in the NameConstraints
+ * extension of this certificate.
+ * @return name constraints
+ */
+ const NameConstraints& name_constraints() const;
+
+ /**
+ * Get the policies as defined in the CertificatePolicies extension
+ * of this certificate.
+ * @return certificate policies
+ */
+ std::vector<std::string> BOTAN_DEPRECATED("Use certificate_policy_oids") policies() const;
+
+ const std::vector<OID>& certificate_policy_oids() const;
+
+ /**
+ * Get all extensions of this certificate.
+ * @return certificate extensions
+ */
+ const Extensions& v3_extensions() const;
+
+ /**
+ * Return the v2 issuer key ID. v2 key IDs are almost never used,
+ * instead see v3_subject_key_id.
+ */
+ const std::vector<uint8_t>& v2_issuer_key_id() const;
+
+ /**
+ * Return the v2 subject key ID. v2 key IDs are almost never used,
+ * instead see v3_subject_key_id.
+ */
+ const std::vector<uint8_t>& v2_subject_key_id() const;
+
+ /**
+ * Return the subject alternative names (DNS, IP, ...)
+ */
+ const AlternativeName& subject_alt_name() const;
+
+ /**
+ * Return the issuer alternative names (DNS, IP, ...)
+ */
+ const AlternativeName& issuer_alt_name() const;
+
+ /**
+ * Return the listed address of an OCSP responder, or empty if not set
+ */
+ std::string ocsp_responder() const;
+
+ /**
+ * Return the listed addresses of ca issuers, or empty if not set
+ */
+ std::vector<std::string> ca_issuers() const;
+
+ /**
+ * Return the CRL distribution point, or empty if not set
+ */
+ std::string crl_distribution_point() const;
+
+ /**
+ * @return a free-form string describing the certificate
+ */
+ std::string to_string() const;
+
+ /**
+ * @return a fingerprint of the certificate
+ * @param hash_name hash function used to calculate the fingerprint
+ */
+ std::string fingerprint(const std::string& hash_name = "SHA-1") const;
+
+ /**
+ * Check if a certain DNS name matches up with the information in
+ * the cert
+ * @param name DNS name to match
+ */
+ bool matches_dns_name(const std::string& name) const;
+
+ /**
+ * Check to certificates for equality.
+ * @return true both certificates are (binary) equal
+ */
+ bool operator==(const X509_Certificate& other) const;
+
+ /**
+ * Impose an arbitrary (but consistent) ordering, eg to allow sorting
+ * a container of certificate objects.
+ * @return true if this is less than other by some unspecified criteria
+ */
+ bool operator<(const X509_Certificate& other) const;
+
+ /**
+ * Create a certificate from a data source providing the DER or
+ * PEM encoded certificate.
+ * @param source the data source
+ */
+ explicit X509_Certificate(DataSource& source);
+
+#if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
+ /**
+ * Create a certificate from a file containing the DER or PEM
+ * encoded certificate.
+ * @param filename the name of the certificate file
+ */
+ explicit X509_Certificate(const std::string& filename);
+#endif
+
+ /**
+ * Create a certificate from a buffer
+ * @param in the buffer containing the DER-encoded certificate
+ */
+ explicit X509_Certificate(const std::vector<uint8_t>& 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);
+
+ /**
+ * Create an uninitialized certificate object. Any attempts to
+ * access this object will throw an exception.
+ */
+ X509_Certificate() = default;
+
+ X509_Certificate(const X509_Certificate& other) = default;
+
+ X509_Certificate& operator=(const X509_Certificate& other) = default;
+
+ private:
+ std::string PEM_label() const override;
+
+ std::vector<std::string> alternate_PEM_labels() const override;
+
+ void force_decode() override;
+
+ const X509_Certificate_Data& data() const;
+
+ std::shared_ptr<X509_Certificate_Data> m_data;
+ };
+
+/**
+* Check two certificates for inequality
+* @param cert1 The first certificate
+* @param cert2 The second certificate
+* @return true if the arguments represent different certificates,
+* false if they are binary identical
+*/
+BOTAN_PUBLIC_API(2,0) bool operator!=(const X509_Certificate& cert1, const X509_Certificate& cert2);
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/x509/x509opt.cpp b/comm/third_party/botan/src/lib/x509/x509opt.cpp
new file mode 100644
index 0000000000..f762acd7b7
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/x509opt.cpp
@@ -0,0 +1,100 @@
+/*
+* X.509 Certificate Options
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/x509self.h>
+#include <botan/parsing.h>
+#include <chrono>
+
+namespace Botan {
+
+/*
+* Set when the certificate should become valid
+*/
+void X509_Cert_Options::not_before(const std::string& time_string)
+ {
+ start = X509_Time(time_string, ASN1_Tag::UTC_OR_GENERALIZED_TIME);
+ }
+
+/*
+* Set when the certificate should expire
+*/
+void X509_Cert_Options::not_after(const std::string& time_string)
+ {
+ end = X509_Time(time_string, ASN1_Tag::UTC_OR_GENERALIZED_TIME);
+ }
+
+/*
+* Set key constraint information
+*/
+void X509_Cert_Options::add_constraints(Key_Constraints usage)
+ {
+ constraints = usage;
+ }
+
+/*
+* Set key constraint information
+*/
+void X509_Cert_Options::add_ex_constraint(const OID& oid)
+ {
+ ex_constraints.push_back(oid);
+ }
+
+/*
+* Set key constraint information
+*/
+void X509_Cert_Options::add_ex_constraint(const std::string& oid_str)
+ {
+ ex_constraints.push_back(OID::from_string(oid_str));
+ }
+
+/*
+* Mark this certificate for CA usage
+*/
+void X509_Cert_Options::CA_key(size_t limit)
+ {
+ is_CA = true;
+ path_limit = limit;
+ }
+
+void X509_Cert_Options::set_padding_scheme(const std::string& scheme)
+ {
+ padding_scheme = scheme;
+ }
+
+/*
+* Initialize the certificate options
+*/
+X509_Cert_Options::X509_Cert_Options(const std::string& initial_opts,
+ uint32_t expiration_time)
+ {
+ is_CA = false;
+ path_limit = 0;
+ constraints = NO_CONSTRAINTS;
+ // use default for chosen algorithm
+ padding_scheme = "";
+
+ auto now = std::chrono::system_clock::now();
+
+ start = X509_Time(now);
+ end = X509_Time(now + std::chrono::seconds(expiration_time));
+
+ if(initial_opts.empty())
+ return;
+
+ std::vector<std::string> parsed = split_on(initial_opts, '/');
+
+ if(parsed.size() > 4)
+ throw Invalid_Argument("X.509 cert options: Too many names: "
+ + initial_opts);
+
+ if(parsed.size() >= 1) common_name = parsed[0];
+ if(parsed.size() >= 2) country = parsed[1];
+ if(parsed.size() >= 3) organization = parsed[2];
+ if(parsed.size() == 4) org_unit = parsed[3];
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/x509/x509path.cpp b/comm/third_party/botan/src/lib/x509/x509path.cpp
new file mode 100644
index 0000000000..b5cdc27c2d
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/x509path.cpp
@@ -0,0 +1,1088 @@
+/*
+* X.509 Certificate Path Validation
+* (C) 2010,2011,2012,2014,2016 Jack Lloyd
+* (C) 2017 Fabian Weissberg, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/x509path.h>
+#include <botan/x509_ext.h>
+#include <botan/pk_keys.h>
+#include <botan/ocsp.h>
+#include <botan/oids.h>
+#include <algorithm>
+#include <chrono>
+#include <vector>
+#include <set>
+#include <string>
+#include <sstream>
+
+#if defined(BOTAN_HAS_ONLINE_REVOCATION_CHECKS)
+ #include <future>
+ #include <botan/http_util.h>
+#endif
+
+namespace Botan {
+
+/*
+* PKIX path validation
+*/
+CertificatePathStatusCodes
+PKIX::check_chain(const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path,
+ std::chrono::system_clock::time_point ref_time,
+ const std::string& hostname,
+ Usage_Type usage,
+ size_t min_signature_algo_strength,
+ const std::set<std::string>& trusted_hashes)
+ {
+ if(cert_path.empty())
+ throw Invalid_Argument("PKIX::check_chain cert_path empty");
+
+ const bool self_signed_ee_cert = (cert_path.size() == 1);
+
+ X509_Time validation_time(ref_time);
+
+ CertificatePathStatusCodes cert_status(cert_path.size());
+
+ if(!hostname.empty() && !cert_path[0]->matches_dns_name(hostname))
+ cert_status[0].insert(Certificate_Status_Code::CERT_NAME_NOMATCH);
+
+ if(!cert_path[0]->allowed_usage(usage))
+ cert_status[0].insert(Certificate_Status_Code::INVALID_USAGE);
+
+ if(cert_path[0]->is_CA_cert() == false &&
+ cert_path[0]->has_constraints(KEY_CERT_SIGN))
+ {
+ /*
+ "If the keyCertSign bit is asserted, then the cA bit in the
+ basic constraints extension (Section 4.2.1.9) MUST also be
+ asserted." - RFC 5280
+
+ We don't bother doing this check on the rest of the path since they
+ must have the cA bit asserted or the validation will fail anyway.
+ */
+ cert_status[0].insert(Certificate_Status_Code::INVALID_USAGE);
+ }
+
+ for(size_t i = 0; i != cert_path.size(); ++i)
+ {
+ std::set<Certificate_Status_Code>& status = cert_status.at(i);
+
+ const bool at_self_signed_root = (i == cert_path.size() - 1);
+
+ const std::shared_ptr<const X509_Certificate>& subject = cert_path[i];
+
+ const std::shared_ptr<const X509_Certificate>& issuer = cert_path[at_self_signed_root ? (i) : (i + 1)];
+
+ if(at_self_signed_root && (issuer->is_self_signed() == false))
+ {
+ status.insert(Certificate_Status_Code::CHAIN_LACKS_TRUST_ROOT);
+ }
+
+ if(subject->issuer_dn() != issuer->subject_dn())
+ {
+ status.insert(Certificate_Status_Code::CHAIN_NAME_MISMATCH);
+ }
+
+ // Check the serial number
+ if(subject->is_serial_negative())
+ {
+ status.insert(Certificate_Status_Code::CERT_SERIAL_NEGATIVE);
+ }
+
+ // Check the subject's DN components' length
+
+ for(const auto& dn_pair : subject->subject_dn().dn_info())
+ {
+ const size_t dn_ub = X509_DN::lookup_ub(dn_pair.first);
+ // dn_pair = <OID,str>
+ if(dn_ub > 0 && dn_pair.second.size() > dn_ub)
+ {
+ status.insert(Certificate_Status_Code::DN_TOO_LONG);
+ }
+ }
+
+ // Check all certs for valid time range
+ if(validation_time < subject->not_before())
+ status.insert(Certificate_Status_Code::CERT_NOT_YET_VALID);
+
+ if(validation_time > subject->not_after())
+ status.insert(Certificate_Status_Code::CERT_HAS_EXPIRED);
+
+ // Check issuer constraints
+ if(!issuer->is_CA_cert() && !self_signed_ee_cert)
+ status.insert(Certificate_Status_Code::CA_CERT_NOT_FOR_CERT_ISSUER);
+
+ std::unique_ptr<Public_Key> issuer_key(issuer->subject_public_key());
+
+ // Check the signature algorithm is known
+ if(OIDS::oid2str_or_empty(subject->signature_algorithm().get_oid()).empty())
+ {
+ status.insert(Certificate_Status_Code::SIGNATURE_ALGO_UNKNOWN);
+ }
+ else
+ {
+ // only perform the following checks if the signature algorithm is known
+ if(!issuer_key)
+ {
+ status.insert(Certificate_Status_Code::CERT_PUBKEY_INVALID);
+ }
+ else
+ {
+ const Certificate_Status_Code sig_status = subject->verify_signature(*issuer_key);
+
+ if(sig_status != Certificate_Status_Code::VERIFIED)
+ status.insert(sig_status);
+
+ if(issuer_key->estimated_strength() < min_signature_algo_strength)
+ status.insert(Certificate_Status_Code::SIGNATURE_METHOD_TOO_WEAK);
+ }
+
+ // Ignore untrusted hashes on self-signed roots
+ if(trusted_hashes.size() > 0 && !at_self_signed_root)
+ {
+ if(trusted_hashes.count(subject->hash_used_for_signature()) == 0)
+ status.insert(Certificate_Status_Code::UNTRUSTED_HASH);
+ }
+ }
+
+ // Check cert extensions
+
+ if(subject->x509_version() == 1)
+ {
+ if(subject->v2_issuer_key_id().empty() == false ||
+ subject->v2_subject_key_id().empty() == false)
+ {
+ status.insert(Certificate_Status_Code::V2_IDENTIFIERS_IN_V1_CERT);
+ }
+ }
+
+ Extensions extensions = subject->v3_extensions();
+ const auto& extensions_vec = extensions.extensions();
+ if(subject->x509_version() < 3 && !extensions_vec.empty())
+ {
+ status.insert(Certificate_Status_Code::EXT_IN_V1_V2_CERT);
+ }
+ for(auto& extension : extensions_vec)
+ {
+ extension.first->validate(*subject, *issuer, cert_path, cert_status, i);
+ }
+ if(extensions.extensions().size() != extensions.get_extension_oids().size())
+ {
+ status.insert(Certificate_Status_Code::DUPLICATE_CERT_EXTENSION);
+ }
+ }
+
+ // path len check
+ size_t max_path_length = cert_path.size();
+ for(size_t i = cert_path.size() - 1; i > 0 ; --i)
+ {
+ std::set<Certificate_Status_Code>& status = cert_status.at(i);
+ const std::shared_ptr<const X509_Certificate>& subject = cert_path[i];
+
+ /*
+ * If the certificate was not self-issued, verify that max_path_length is
+ * greater than zero and decrement max_path_length by 1.
+ */
+ if(subject->subject_dn() != subject->issuer_dn())
+ {
+ if(max_path_length > 0)
+ {
+ --max_path_length;
+ }
+ else
+ {
+ status.insert(Certificate_Status_Code::CERT_CHAIN_TOO_LONG);
+ }
+ }
+
+ /*
+ * 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();
+ }
+ }
+
+ return cert_status;
+ }
+
+CertificatePathStatusCodes
+PKIX::check_ocsp(const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path,
+ const std::vector<std::shared_ptr<const OCSP::Response>>& ocsp_responses,
+ const std::vector<Certificate_Store*>& trusted_certstores,
+ std::chrono::system_clock::time_point ref_time,
+ std::chrono::seconds max_ocsp_age)
+ {
+ if(cert_path.empty())
+ throw Invalid_Argument("PKIX::check_ocsp cert_path empty");
+
+ CertificatePathStatusCodes cert_status(cert_path.size() - 1);
+
+ for(size_t i = 0; i != cert_path.size() - 1; ++i)
+ {
+ std::set<Certificate_Status_Code>& status = cert_status.at(i);
+
+ std::shared_ptr<const X509_Certificate> subject = cert_path.at(i);
+ std::shared_ptr<const X509_Certificate> ca = cert_path.at(i+1);
+
+ if(i < ocsp_responses.size() && (ocsp_responses.at(i) != nullptr)
+ && (ocsp_responses.at(i)->status() == OCSP::Response_Status_Code::Successful))
+ {
+ try
+ {
+ Certificate_Status_Code ocsp_signature_status = ocsp_responses.at(i)->check_signature(trusted_certstores, cert_path);
+
+ if(ocsp_signature_status == Certificate_Status_Code::OCSP_SIGNATURE_OK)
+ {
+ // Signature ok, so check the claimed status
+ Certificate_Status_Code ocsp_status = ocsp_responses.at(i)->status_for(*ca, *subject, ref_time, max_ocsp_age);
+ status.insert(ocsp_status);
+ }
+ else
+ {
+ // Some signature problem
+ status.insert(ocsp_signature_status);
+ }
+ }
+ catch(Exception&)
+ {
+ status.insert(Certificate_Status_Code::OCSP_RESPONSE_INVALID);
+ }
+ }
+ }
+
+ while(cert_status.size() > 0 && cert_status.back().empty())
+ cert_status.pop_back();
+
+ return cert_status;
+ }
+
+CertificatePathStatusCodes
+PKIX::check_crl(const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path,
+ const std::vector<std::shared_ptr<const X509_CRL>>& crls,
+ std::chrono::system_clock::time_point ref_time)
+ {
+ if(cert_path.empty())
+ throw Invalid_Argument("PKIX::check_crl cert_path empty");
+
+ CertificatePathStatusCodes cert_status(cert_path.size());
+ const X509_Time validation_time(ref_time);
+
+ for(size_t i = 0; i != cert_path.size() - 1; ++i)
+ {
+ std::set<Certificate_Status_Code>& status = cert_status.at(i);
+
+ if(i < crls.size() && crls.at(i))
+ {
+ std::shared_ptr<const X509_Certificate> subject = cert_path.at(i);
+ std::shared_ptr<const X509_Certificate> ca = cert_path.at(i+1);
+
+ if(!ca->allowed_usage(CRL_SIGN))
+ status.insert(Certificate_Status_Code::CA_CERT_NOT_FOR_CRL_ISSUER);
+
+ if(validation_time < crls[i]->this_update())
+ status.insert(Certificate_Status_Code::CRL_NOT_YET_VALID);
+
+ if(validation_time > crls[i]->next_update())
+ status.insert(Certificate_Status_Code::CRL_HAS_EXPIRED);
+
+ if(crls[i]->check_signature(ca->subject_public_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);
+
+ std::string dp = subject->crl_distribution_point();
+ if(!dp.empty())
+ {
+ if(dp != crls[i]->crl_issuing_distribution_point())
+ {
+ 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 && OIDS::oid2str_or_empty(extension.first->oid_of()) == "")
+ {
+ /* 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);
+ }
+ }
+
+ }
+ }
+
+ while(cert_status.size() > 0 && cert_status.back().empty())
+ cert_status.pop_back();
+
+ return cert_status;
+ }
+
+CertificatePathStatusCodes
+PKIX::check_crl(const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path,
+ const std::vector<Certificate_Store*>& certstores,
+ std::chrono::system_clock::time_point ref_time)
+ {
+ if(cert_path.empty())
+ throw Invalid_Argument("PKIX::check_crl cert_path empty");
+
+ if(certstores.empty())
+ throw Invalid_Argument("PKIX::check_crl certstores empty");
+
+ std::vector<std::shared_ptr<const X509_CRL>> crls(cert_path.size());
+
+ for(size_t i = 0; i != cert_path.size(); ++i)
+ {
+ BOTAN_ASSERT_NONNULL(cert_path[i]);
+ for(size_t c = 0; c != certstores.size(); ++c)
+ {
+ crls[i] = certstores[c]->find_crl_for(*cert_path[i]);
+ if(crls[i])
+ break;
+ }
+ }
+
+ return PKIX::check_crl(cert_path, crls, ref_time);
+ }
+
+#if defined(BOTAN_HAS_ONLINE_REVOCATION_CHECKS)
+
+CertificatePathStatusCodes
+PKIX::check_ocsp_online(const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path,
+ const std::vector<Certificate_Store*>& trusted_certstores,
+ std::chrono::system_clock::time_point ref_time,
+ std::chrono::milliseconds timeout,
+ bool ocsp_check_intermediate_CAs,
+ std::chrono::seconds max_ocsp_age)
+ {
+ if(cert_path.empty())
+ throw Invalid_Argument("PKIX::check_ocsp_online cert_path empty");
+
+ std::vector<std::future<std::shared_ptr<const OCSP::Response>>> ocsp_response_futures;
+
+ size_t to_ocsp = 1;
+
+ if(ocsp_check_intermediate_CAs)
+ to_ocsp = cert_path.size() - 1;
+ if(cert_path.size() == 1)
+ to_ocsp = 0;
+
+ for(size_t i = 0; i < to_ocsp; ++i)
+ {
+ const std::shared_ptr<const X509_Certificate>& subject = cert_path.at(i);
+ const std::shared_ptr<const X509_Certificate>& issuer = cert_path.at(i+1);
+
+ if(subject->ocsp_responder() == "")
+ {
+ ocsp_response_futures.emplace_back(std::async(std::launch::deferred, [&]() -> std::shared_ptr<const OCSP::Response> {
+ return std::make_shared<const OCSP::Response>(Certificate_Status_Code::OCSP_NO_REVOCATION_URL);
+ }));
+ }
+ else
+ {
+ ocsp_response_futures.emplace_back(std::async(std::launch::async, [&]() -> std::shared_ptr<const OCSP::Response> {
+ OCSP::Request req(*issuer, BigInt::decode(subject->serial_number()));
+
+ 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 std::make_shared<const OCSP::Response>(Certificate_Status_Code::OCSP_SERVER_NOT_AVAILABLE);
+ // Check the MIME type?
+
+ return std::make_shared<const OCSP::Response>(http.body());
+ }));
+ }
+ }
+
+ std::vector<std::shared_ptr<const OCSP::Response>> ocsp_responses;
+
+ for(size_t i = 0; i < ocsp_response_futures.size(); ++i)
+ {
+ ocsp_responses.push_back(ocsp_response_futures[i].get());
+ }
+
+ return PKIX::check_ocsp(cert_path, ocsp_responses, trusted_certstores, ref_time, max_ocsp_age);
+ }
+
+CertificatePathStatusCodes
+PKIX::check_crl_online(const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path,
+ const std::vector<Certificate_Store*>& certstores,
+ Certificate_Store_In_Memory* crl_store,
+ std::chrono::system_clock::time_point ref_time,
+ std::chrono::milliseconds timeout)
+ {
+ if(cert_path.empty())
+ throw Invalid_Argument("PKIX::check_crl_online cert_path empty");
+ if(certstores.empty())
+ throw Invalid_Argument("PKIX::check_crl_online certstores empty");
+
+ std::vector<std::future<std::shared_ptr<const X509_CRL>>> future_crls;
+ std::vector<std::shared_ptr<const X509_CRL>> crls(cert_path.size());
+
+ for(size_t i = 0; i != cert_path.size(); ++i)
+ {
+ const std::shared_ptr<const X509_Certificate>& cert = cert_path.at(i);
+ for(size_t c = 0; c != certstores.size(); ++c)
+ {
+ crls[i] = certstores[c]->find_crl_for(*cert);
+ if(crls[i])
+ break;
+ }
+
+ // TODO: check if CRL is expired and re-request?
+
+ // Only request if we don't already have a CRL
+ if(crls[i])
+ {
+ /*
+ We already have a CRL, so just insert this empty one to hold a place in the vector
+ so that indexes match up
+ */
+ future_crls.emplace_back(std::future<std::shared_ptr<const X509_CRL>>());
+ }
+ else if(cert->crl_distribution_point() == "")
+ {
+ // Avoid creating a thread for this case
+ future_crls.emplace_back(std::async(std::launch::deferred, [&]() -> std::shared_ptr<const X509_CRL> {
+ throw Not_Implemented("No CRL distribution point for this certificate");
+ }));
+ }
+ else
+ {
+ future_crls.emplace_back(std::async(std::launch::async, [&]() -> std::shared_ptr<const X509_CRL> {
+ auto http = HTTP::GET_sync(cert->crl_distribution_point(),
+ /*redirects*/ 1, timeout);
+
+ http.throw_unless_ok();
+ // check the mime type?
+ return std::make_shared<const X509_CRL>(http.body());
+ }));
+ }
+ }
+
+ for(size_t i = 0; i != future_crls.size(); ++i)
+ {
+ if(future_crls[i].valid())
+ {
+ try
+ {
+ crls[i] = future_crls[i].get();
+ }
+ catch(std::exception&)
+ {
+ // crls[i] left null
+ // todo: log exception e.what() ?
+ }
+ }
+ }
+
+ const CertificatePathStatusCodes crl_status = PKIX::check_crl(cert_path, crls, ref_time);
+
+ if(crl_store)
+ {
+ for(size_t i = 0; i != crl_status.size(); ++i)
+ {
+ if(crl_status[i].count(Certificate_Status_Code::VALID_CRL_CHECKED))
+ {
+ // better be non-null, we supposedly validated it
+ BOTAN_ASSERT_NONNULL(crls[i]);
+ crl_store->add_crl(crls[i]);
+ }
+ }
+ }
+
+ return crl_status;
+ }
+
+#endif
+
+Certificate_Status_Code
+PKIX::build_certificate_path(std::vector<std::shared_ptr<const X509_Certificate>>& cert_path,
+ const std::vector<Certificate_Store*>& trusted_certstores,
+ const std::shared_ptr<const X509_Certificate>& end_entity,
+ const std::vector<std::shared_ptr<const X509_Certificate>>& end_entity_extra)
+ {
+ if(end_entity->is_self_signed())
+ {
+ return Certificate_Status_Code::CANNOT_ESTABLISH_TRUST;
+ }
+
+ /*
+ * 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<std::string> certs_seen;
+
+ cert_path.push_back(end_entity);
+ certs_seen.insert(end_entity->fingerprint("SHA-256"));
+
+ Certificate_Store_In_Memory ee_extras;
+ for(size_t i = 0; i != end_entity_extra.size(); ++i)
+ ee_extras.add_certificate(end_entity_extra[i]);
+
+ // 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<uint8_t> auth_key_id = last.authority_key_id();
+
+ std::shared_ptr<const X509_Certificate> 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;
+ }
+ }
+
+ if(!issuer)
+ {
+ // fall back to searching supplemental certs
+ issuer = ee_extras.find_cert(issuer_dn, auth_key_id);
+ }
+
+ if(!issuer)
+ return Certificate_Status_Code::CERT_ISSUER_NOT_FOUND;
+
+ const std::string fprint = issuer->fingerprint("SHA-256");
+
+ if(certs_seen.count(fprint) > 0) // already seen?
+ {
+ return Certificate_Status_Code::CERT_CHAIN_LOOP;
+ }
+
+ 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;
+ }
+ }
+ }
+ }
+
+/**
+ * utilities for PKIX::build_all_certificate_paths
+ */
+namespace
+{
+// <certificate, trusted?>
+using cert_maybe_trusted = std::pair<std::shared_ptr<const X509_Certificate>,bool>;
+}
+
+/**
+ * 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 <IssuerCert, trusted?> on the stack for each of them.
+ *
+ */
+Certificate_Status_Code
+PKIX::build_all_certificate_paths(std::vector<std::vector<std::shared_ptr<const X509_Certificate>>>& cert_paths_out,
+ const std::vector<Certificate_Store*>& trusted_certstores,
+ const std::shared_ptr<const X509_Certificate>& end_entity,
+ const std::vector<std::shared_ptr<const X509_Certificate>>& end_entity_extra)
+ {
+ if(!cert_paths_out.empty())
+ {
+ throw Invalid_Argument("PKIX::build_all_certificate_paths: cert_paths_out must be empty");
+ }
+
+ if(end_entity->is_self_signed())
+ {
+ return Certificate_Status_Code::CANNOT_ESTABLISH_TRUST;
+ }
+
+ /*
+ * Pile up error messages
+ */
+ std::vector<Certificate_Status_Code> stats;
+
+ Certificate_Store_In_Memory ee_extras;
+ for(size_t i = 0; i != end_entity_extra.size(); ++i)
+ {
+ ee_extras.add_certificate(end_entity_extra[i]);
+ }
+
+ /*
+ * 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<std::string> 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<std::shared_ptr<const X509_Certificate>> path_so_far;
+
+ // todo can we assume that the end certificate is not trusted?
+ std::vector<cert_maybe_trusted> stack = { {end_entity, false} };
+
+ while(!stack.empty())
+ {
+ // found a deletion marker that guides the DFS, backtracing
+ if(stack.back().first == nullptr)
+ {
+ 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
+ {
+ std::shared_ptr<const X509_Certificate> last = stack.back().first;
+ 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<uint8_t> auth_key_id = last->authority_key_id();
+
+ // search for trusted issuers
+ std::vector<std::shared_ptr<const X509_Certificate>> 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<std::shared_ptr<const X509_Certificate>> 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.size() + misc_issuers.size() == 0)
+ {
+ 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::shared_ptr<const X509_Certificate>(nullptr),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
+ {
+ return Certificate_Status_Code::OK;
+ }
+ }
+
+
+void PKIX::merge_revocation_status(CertificatePathStatusCodes& chain_status,
+ const CertificatePathStatusCodes& crl,
+ const CertificatePathStatusCodes& ocsp,
+ bool require_rev_on_end_entity,
+ bool require_rev_on_intermediates)
+ {
+ 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;
+
+ if(i < crl.size() && crl[i].size() > 0)
+ {
+ for(auto&& code : crl[i])
+ {
+ if(code == Certificate_Status_Code::VALID_CRL_CHECKED)
+ {
+ had_crl = true;
+ }
+ chain_status[i].insert(code);
+ }
+ }
+
+ if(i < ocsp.size() && ocsp[i].size() > 0)
+ {
+ for(auto&& code : ocsp[i])
+ {
+ if(code == Certificate_Status_Code::OCSP_RESPONSE_GOOD ||
+ code == Certificate_Status_Code::OCSP_NO_REVOCATION_URL || // softfail
+ code == Certificate_Status_Code::OCSP_SERVER_NOT_AVAILABLE) // softfail
+ {
+ had_ocsp = true;
+ }
+
+ chain_status[i].insert(code);
+ }
+ }
+
+ if(had_crl == false && had_ocsp == false)
+ {
+ if((require_rev_on_end_entity && i == 0) ||
+ (require_rev_on_intermediates && i > 0))
+ {
+ chain_status[i].insert(Certificate_Status_Code::NO_REVOCATION_DATA);
+ }
+ }
+ }
+ }
+
+Certificate_Status_Code PKIX::overall_status(const CertificatePathStatusCodes& cert_status)
+ {
+ if(cert_status.empty())
+ throw Invalid_Argument("PKIX::overall_status empty cert status");
+
+ Certificate_Status_Code overall_status = Certificate_Status_Code::OK;
+
+ // take the "worst" error as overall
+ for(const std::set<Certificate_Status_Code>& s : cert_status)
+ {
+ if(!s.empty())
+ {
+ auto worst = *s.rbegin();
+ // Leave informative OCSP/CRL confirmations on cert-level status only
+ if(worst >= Certificate_Status_Code::FIRST_ERROR_STATUS && worst > overall_status)
+ {
+ overall_status = worst;
+ }
+ }
+ }
+ return overall_status;
+ }
+
+Path_Validation_Result x509_path_validate(
+ const std::vector<X509_Certificate>& end_certs,
+ const Path_Validation_Restrictions& restrictions,
+ const std::vector<Certificate_Store*>& trusted_roots,
+ const std::string& hostname,
+ Usage_Type usage,
+ std::chrono::system_clock::time_point ref_time,
+ std::chrono::milliseconds ocsp_timeout,
+ const std::vector<std::shared_ptr<const OCSP::Response>>& ocsp_resp)
+ {
+ if(end_certs.empty())
+ {
+ throw Invalid_Argument("x509_path_validate called with no subjects");
+ }
+
+ std::shared_ptr<const X509_Certificate> end_entity(std::make_shared<const X509_Certificate>(end_certs[0]));
+ std::vector<std::shared_ptr<const X509_Certificate>> end_entity_extra;
+ for(size_t i = 1; i < end_certs.size(); ++i)
+ {
+ end_entity_extra.push_back(std::make_shared<const X509_Certificate>(end_certs[i]));
+ }
+
+ std::vector<std::vector<std::shared_ptr<const X509_Certificate>>> cert_paths;
+ Certificate_Status_Code path_building_result = PKIX::build_all_certificate_paths(cert_paths, trusted_roots, end_entity, end_entity_extra);
+
+ // 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);
+ }
+
+ std::vector<Path_Validation_Result> 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.minimum_key_strength(),
+ restrictions.trusted_hashes());
+
+ CertificatePathStatusCodes crl_status =
+ PKIX::check_crl(cert_path, trusted_roots, ref_time);
+
+ CertificatePathStatusCodes ocsp_status;
+
+ if(ocsp_resp.size() > 0)
+ {
+ ocsp_status = PKIX::check_ocsp(cert_path, ocsp_resp, trusted_roots, ref_time, restrictions.max_ocsp_age());
+ }
+
+ if(ocsp_status.empty() && ocsp_timeout != std::chrono::milliseconds(0))
+ {
+#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.ocsp_all_intermediates());
+#else
+ ocsp_status.resize(1);
+ ocsp_status[0].insert(Certificate_Status_Code::OCSP_NO_HTTP);
+#endif
+ }
+
+ PKIX::merge_revocation_status(status, crl_status, ocsp_status,
+ restrictions.require_revocation_information(),
+ restrictions.ocsp_all_intermediates());
+
+ Path_Validation_Result pvd(status, std::move(cert_path));
+ if(pvd.successful_validation())
+ {
+ return pvd;
+ }
+ else
+ {
+ error_results.push_back(std::move(pvd));
+ }
+ }
+ return error_results[0];
+ }
+
+Path_Validation_Result x509_path_validate(
+ const X509_Certificate& end_cert,
+ const Path_Validation_Restrictions& restrictions,
+ const std::vector<Certificate_Store*>& trusted_roots,
+ const std::string& hostname,
+ Usage_Type usage,
+ std::chrono::system_clock::time_point when,
+ std::chrono::milliseconds ocsp_timeout,
+ const std::vector<std::shared_ptr<const OCSP::Response>>& ocsp_resp)
+ {
+ std::vector<X509_Certificate> certs;
+ certs.push_back(end_cert);
+ return x509_path_validate(certs, restrictions, trusted_roots, hostname, usage, when, ocsp_timeout, ocsp_resp);
+ }
+
+Path_Validation_Result x509_path_validate(
+ const std::vector<X509_Certificate>& end_certs,
+ const Path_Validation_Restrictions& restrictions,
+ const Certificate_Store& store,
+ const std::string& hostname,
+ Usage_Type usage,
+ std::chrono::system_clock::time_point when,
+ std::chrono::milliseconds ocsp_timeout,
+ const std::vector<std::shared_ptr<const OCSP::Response>>& ocsp_resp)
+ {
+ std::vector<Certificate_Store*> trusted_roots;
+ trusted_roots.push_back(const_cast<Certificate_Store*>(&store));
+
+ return x509_path_validate(end_certs, restrictions, trusted_roots, hostname, usage, when, ocsp_timeout, ocsp_resp);
+ }
+
+Path_Validation_Result x509_path_validate(
+ const X509_Certificate& end_cert,
+ const Path_Validation_Restrictions& restrictions,
+ const Certificate_Store& store,
+ const std::string& hostname,
+ Usage_Type usage,
+ std::chrono::system_clock::time_point when,
+ std::chrono::milliseconds ocsp_timeout,
+ const std::vector<std::shared_ptr<const OCSP::Response>>& ocsp_resp)
+ {
+ std::vector<X509_Certificate> certs;
+ certs.push_back(end_cert);
+
+ std::vector<Certificate_Store*> trusted_roots;
+ trusted_roots.push_back(const_cast<Certificate_Store*>(&store));
+
+ return x509_path_validate(certs, restrictions, trusted_roots, hostname, usage, when, ocsp_timeout, ocsp_resp);
+ }
+
+Path_Validation_Restrictions::Path_Validation_Restrictions(bool require_rev,
+ size_t key_strength,
+ bool ocsp_intermediates,
+ std::chrono::seconds max_ocsp_age) :
+ 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)
+ {
+ if(key_strength <= 80)
+ { m_trusted_hashes.insert("SHA-160"); }
+
+ m_trusted_hashes.insert("SHA-224");
+ m_trusted_hashes.insert("SHA-256");
+ m_trusted_hashes.insert("SHA-384");
+ m_trusted_hashes.insert("SHA-512");
+ }
+
+namespace {
+CertificatePathStatusCodes find_warnings(const CertificatePathStatusCodes& all_statuses)
+ {
+ CertificatePathStatusCodes warnings;
+ for(const auto& status_set_i : all_statuses)
+ {
+ std::set<Certificate_Status_Code> warning_set_i;
+ for(const auto& code : status_set_i)
+ {
+ if(code >= Certificate_Status_Code::FIRST_WARNING_STATUS &&
+ code < Certificate_Status_Code::FIRST_ERROR_STATUS)
+ {
+ warning_set_i.insert(code);
+ }
+ }
+ warnings.push_back(warning_set_i);
+ }
+ return warnings;
+ }
+}
+
+Path_Validation_Result::Path_Validation_Result(CertificatePathStatusCodes status,
+ std::vector<std::shared_ptr<const X509_Certificate>>&& cert_chain) :
+ m_all_status(status),
+ m_warnings(find_warnings(m_all_status)),
+ m_cert_path(cert_chain),
+ m_overall(PKIX::overall_status(m_all_status))
+ {
+ }
+
+const X509_Certificate& Path_Validation_Result::trust_root() const
+ {
+ if(m_cert_path.empty())
+ throw Invalid_State("Path_Validation_Result::trust_root no path set");
+ if(result() != Certificate_Status_Code::VERIFIED)
+ throw Invalid_State("Path_Validation_Result::trust_root meaningless with invalid status");
+
+ return *m_cert_path[m_cert_path.size()-1];
+ }
+
+std::set<std::string> Path_Validation_Result::trusted_hashes() const
+ {
+ std::set<std::string> hashes;
+ for(size_t i = 0; i != m_cert_path.size(); ++i)
+ hashes.insert(m_cert_path[i]->hash_used_for_signature());
+ return hashes;
+ }
+
+bool Path_Validation_Result::successful_validation() const
+ {
+ return (result() == Certificate_Status_Code::VERIFIED ||
+ result() == Certificate_Status_Code::OCSP_RESPONSE_GOOD ||
+ result() == Certificate_Status_Code::VALID_CRL_CHECKED);
+ }
+
+bool Path_Validation_Result::no_warnings() const
+ {
+ for(auto status_set_i : m_warnings)
+ if(!status_set_i.empty())
+ return false;
+ return true;
+ }
+
+CertificatePathStatusCodes Path_Validation_Result::warnings() const
+ {
+ return m_warnings;
+ }
+
+std::string Path_Validation_Result::result_string() const
+ {
+ return status_string(result());
+ }
+
+const char* Path_Validation_Result::status_string(Certificate_Status_Code code)
+ {
+ if(const char* s = to_string(code))
+ return s;
+
+ return "Unknown error";
+ }
+
+std::string Path_Validation_Result::warnings_string() const
+ {
+ const std::string sep(", ");
+ std::string res;
+ for(size_t i = 0; i < m_warnings.size(); i++)
+ {
+ for(auto code : m_warnings[i])
+ res += "[" + std::to_string(i) + "] " + status_string(code) + sep;
+ }
+ // remove last sep
+ if(res.size() >= sep.size())
+ res = res.substr(0, res.size() - sep.size());
+ return res;
+ }
+}
diff --git a/comm/third_party/botan/src/lib/x509/x509path.h b/comm/third_party/botan/src/lib/x509/x509path.h
new file mode 100644
index 0000000000..c8575fd32b
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/x509path.h
@@ -0,0 +1,475 @@
+/*
+* X.509 Cert Path Validation
+* (C) 2010-2011 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_X509_CERT_PATH_VALIDATION_H_
+#define BOTAN_X509_CERT_PATH_VALIDATION_H_
+
+#include <botan/pkix_enums.h>
+#include <botan/x509cert.h>
+#include <botan/certstor.h>
+#include <botan/ocsp.h>
+#include <functional>
+#include <set>
+#include <chrono>
+
+#if defined(BOTAN_TARGET_OS_HAS_THREADS) && defined(BOTAN_HAS_HTTP_UTIL)
+ #define BOTAN_HAS_ONLINE_REVOCATION_CHECKS
+#endif
+
+namespace Botan {
+
+/**
+* This type represents the validation status of an entire certificate path.
+* There is one set of status codes for each certificate in the path.
+*/
+typedef std::vector<std::set<Certificate_Status_Code>> CertificatePathStatusCodes;
+
+/**
+* Specifies restrictions on the PKIX path validation
+*/
+class BOTAN_PUBLIC_API(2,0) Path_Validation_Restrictions final
+ {
+ public:
+ /**
+ * @param require_rev if true, revocation information is required
+
+ * @param minimum_key_strength is the minimum strength (in terms of
+ * operations, eg 80 means 2^80) of a signature. Signatures weaker than
+ * this are rejected. If more than 80, SHA-1 signatures are also
+ * rejected. If possible use at least setting 110.
+ *
+ * 80 bit strength requires 1024 bit RSA
+ * 110 bit strength requires 2k bit RSA
+ * 128 bit strength requires ~3k bit RSA or P-256
+ * @param ocsp_all_intermediates Make OCSP requests for all CAs as
+ * well as end entity (if OCSP enabled in path validation request)
+ * @param max_ocsp_age maximum age of OCSP responses w/o next_update.
+ * If zero, there is no maximum age
+ */
+ 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());
+
+ /**
+ * @param require_rev if true, revocation information is required
+ * @param minimum_key_strength is the minimum strength (in terms of
+ * operations, eg 80 means 2^80) of a signature. Signatures
+ * weaker than this are rejected.
+ * @param ocsp_all_intermediates Make OCSP requests for all CAs as
+ * well as end entity (if OCSP enabled in path validation request)
+ * @param trusted_hashes a set of trusted hashes. Any signatures
+ * created using a hash other than one of these will be
+ * rejected.
+ * @param max_ocsp_age maximum age of OCSP responses w/o next_update.
+ * If zero, there is no maximum age
+ */
+ Path_Validation_Restrictions(bool require_rev,
+ size_t minimum_key_strength,
+ bool ocsp_all_intermediates,
+ const std::set<std::string>& trusted_hashes,
+ std::chrono::seconds max_ocsp_age = std::chrono::seconds::zero()) :
+ 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) {}
+
+ /**
+ * @return whether revocation information is required
+ */
+ bool require_revocation_information() const
+ { return m_require_revocation_information; }
+
+ /**
+ * @return whether all intermediate CAs should also be OCSPed. If false
+ * then only end entity OCSP is required/requested.
+ */
+ bool ocsp_all_intermediates() const
+ { return m_ocsp_all_intermediates; }
+
+ /**
+ * @return trusted signature hash functions
+ */
+ const std::set<std::string>& trusted_hashes() const
+ { return m_trusted_hashes; }
+
+ /**
+ * @return minimum required key strength
+ */
+ size_t minimum_key_strength() const
+ { return m_minimum_key_strength; }
+
+ /**
+ * @return maximum age of OCSP responses w/o next_update.
+ * If zero, there is no maximum age
+ */
+ std::chrono::seconds max_ocsp_age() const
+ { return m_max_ocsp_age; }
+
+ private:
+ bool m_require_revocation_information;
+ bool m_ocsp_all_intermediates;
+ std::set<std::string> m_trusted_hashes;
+ size_t m_minimum_key_strength;
+ std::chrono::seconds m_max_ocsp_age;
+ };
+
+/**
+* Represents the result of a PKIX path validation
+*/
+class BOTAN_PUBLIC_API(2,0) Path_Validation_Result final
+ {
+ public:
+ typedef Certificate_Status_Code Code;
+
+ /**
+ * @return the set of hash functions you are implicitly
+ * trusting by trusting this result.
+ */
+ std::set<std::string> trusted_hashes() const;
+
+ /**
+ * @return the trust root of the validation if successful
+ * throws an exception if the validation failed
+ */
+ const X509_Certificate& trust_root() const;
+
+ /**
+ * @return the full path from subject to trust root
+ * This path may be empty
+ */
+ const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path() const { return m_cert_path; }
+
+ /**
+ * @return true iff the validation was successful
+ */
+ bool successful_validation() const;
+
+ /**
+ * @return true iff no warnings occured during validation
+ */
+ bool no_warnings() const;
+
+ /**
+ * @return overall validation result code
+ */
+ Certificate_Status_Code result() const { return m_overall; }
+
+ /**
+ * @return a set of status codes for each certificate in the chain
+ */
+ const CertificatePathStatusCodes& all_statuses() const
+ { return m_all_status; }
+
+ /**
+ * @return the subset of status codes that are warnings
+ */
+ CertificatePathStatusCodes warnings() const;
+
+ /**
+ * @return string representation of the validation result
+ */
+ std::string result_string() const;
+
+ /**
+ * @return string representation of the warnings
+ */
+ std::string warnings_string() const;
+
+ /**
+ * @param code validation status code
+ * @return corresponding validation status message
+ */
+ static const char* status_string(Certificate_Status_Code code);
+
+ /**
+ * Create a Path_Validation_Result
+ * @param status list of validation status codes
+ * @param cert_chain the certificate chain that was validated
+ */
+ Path_Validation_Result(CertificatePathStatusCodes status,
+ std::vector<std::shared_ptr<const X509_Certificate>>&& cert_chain);
+
+ /**
+ * Create a Path_Validation_Result
+ * @param status validation status code
+ */
+ explicit Path_Validation_Result(Certificate_Status_Code status) : m_overall(status) {}
+
+ private:
+ CertificatePathStatusCodes m_all_status;
+ CertificatePathStatusCodes m_warnings;
+ std::vector<std::shared_ptr<const X509_Certificate>> m_cert_path;
+ Certificate_Status_Code m_overall;
+ };
+
+/**
+* PKIX Path Validation
+* @param end_certs certificate chain to validate (with end entity certificate in end_certs[0])
+* @param restrictions path validation restrictions
+* @param trusted_roots list of certificate stores that contain trusted certificates
+* @param hostname if not empty, compared against the DNS name in end_certs[0]
+* @param usage if not set to UNSPECIFIED, compared against the key usage in end_certs[0]
+* @param validation_time what reference time to use for validation
+* @param ocsp_timeout timeout for OCSP operations, 0 disables OCSP check
+* @param ocsp_resp additional OCSP responses to consider (eg from peer)
+* @return result of the path validation
+* note: when enabled, OCSP check is softfail by default: if the OCSP server is not
+* reachable, Path_Validation_Result::successful_validation() will return true.
+* Hardfail OCSP check can be achieve by also calling Path_Validation_Result::no_warnings().
+*/
+Path_Validation_Result BOTAN_PUBLIC_API(2,0) x509_path_validate(
+ const std::vector<X509_Certificate>& end_certs,
+ const Path_Validation_Restrictions& restrictions,
+ const std::vector<Certificate_Store*>& trusted_roots,
+ const std::string& hostname = "",
+ Usage_Type usage = Usage_Type::UNSPECIFIED,
+ std::chrono::system_clock::time_point validation_time = std::chrono::system_clock::now(),
+ std::chrono::milliseconds ocsp_timeout = std::chrono::milliseconds(0),
+ const std::vector<std::shared_ptr<const OCSP::Response>>& ocsp_resp = {});
+
+/**
+* PKIX Path Validation
+* @param end_cert certificate to validate
+* @param restrictions path validation restrictions
+* @param trusted_roots list of stores that contain trusted certificates
+* @param hostname if not empty, compared against the DNS name in end_cert
+* @param usage if not set to UNSPECIFIED, compared against the key usage in end_cert
+* @param validation_time what reference time to use for validation
+* @param ocsp_timeout timeout for OCSP operations, 0 disables OCSP check
+* @param ocsp_resp additional OCSP responses to consider (eg from peer)
+* @return result of the path validation
+*/
+Path_Validation_Result BOTAN_PUBLIC_API(2,0) x509_path_validate(
+ const X509_Certificate& end_cert,
+ const Path_Validation_Restrictions& restrictions,
+ const std::vector<Certificate_Store*>& trusted_roots,
+ const std::string& hostname = "",
+ Usage_Type usage = Usage_Type::UNSPECIFIED,
+ std::chrono::system_clock::time_point validation_time = std::chrono::system_clock::now(),
+ std::chrono::milliseconds ocsp_timeout = std::chrono::milliseconds(0),
+ const std::vector<std::shared_ptr<const OCSP::Response>>& ocsp_resp = {});
+
+/**
+* PKIX Path Validation
+* @param end_cert certificate to validate
+* @param restrictions path validation restrictions
+* @param store store that contains trusted certificates
+* @param hostname if not empty, compared against the DNS name in end_cert
+* @param usage if not set to UNSPECIFIED, compared against the key usage in end_cert
+* @param validation_time what reference time to use for validation
+* @param ocsp_timeout timeout for OCSP operations, 0 disables OCSP check
+* @param ocsp_resp additional OCSP responses to consider (eg from peer)
+* @return result of the path validation
+*/
+Path_Validation_Result BOTAN_PUBLIC_API(2,0) x509_path_validate(
+ const X509_Certificate& end_cert,
+ const Path_Validation_Restrictions& restrictions,
+ const Certificate_Store& store,
+ const std::string& hostname = "",
+ Usage_Type usage = Usage_Type::UNSPECIFIED,
+ std::chrono::system_clock::time_point validation_time = std::chrono::system_clock::now(),
+ std::chrono::milliseconds ocsp_timeout = std::chrono::milliseconds(0),
+ const std::vector<std::shared_ptr<const OCSP::Response>>& ocsp_resp = {});
+
+/**
+* PKIX Path Validation
+* @param end_certs certificate chain to validate
+* @param restrictions path validation restrictions
+* @param store store that contains trusted certificates
+* @param hostname if not empty, compared against the DNS name in end_certs[0]
+* @param usage if not set to UNSPECIFIED, compared against the key usage in end_certs[0]
+* @param validation_time what reference time to use for validation
+* @param ocsp_timeout timeout for OCSP operations, 0 disables OCSP check
+* @param ocsp_resp additional OCSP responses to consider (eg from peer)
+* @return result of the path validation
+*/
+Path_Validation_Result BOTAN_PUBLIC_API(2,0) x509_path_validate(
+ const std::vector<X509_Certificate>& end_certs,
+ const Path_Validation_Restrictions& restrictions,
+ const Certificate_Store& store,
+ const std::string& hostname = "",
+ Usage_Type usage = Usage_Type::UNSPECIFIED,
+ std::chrono::system_clock::time_point validation_time = std::chrono::system_clock::now(),
+ std::chrono::milliseconds ocsp_timeout = std::chrono::milliseconds(0),
+ const std::vector<std::shared_ptr<const OCSP::Response>>& ocsp_resp = {});
+
+
+/**
+* namespace PKIX holds the building blocks that are called by x509_path_validate.
+* This allows custom validation logic to be written by applications and makes
+* for easier testing, but unless you're positive you know what you're doing you
+* probably want to just call x509_path_validate instead.
+*/
+namespace PKIX {
+
+Certificate_Status_Code
+build_all_certificate_paths(std::vector<std::vector<std::shared_ptr<const X509_Certificate>>>& cert_paths,
+ const std::vector<Certificate_Store*>& trusted_certstores,
+ const std::shared_ptr<const X509_Certificate>& end_entity,
+ const std::vector<std::shared_ptr<const X509_Certificate>>& end_entity_extra);
+
+
+/**
+* Build certificate path
+* @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)
+*/
+Certificate_Status_Code
+BOTAN_PUBLIC_API(2,0) build_certificate_path(std::vector<std::shared_ptr<const X509_Certificate>>& cert_path_out,
+ const std::vector<Certificate_Store*>& trusted_certstores,
+ const std::shared_ptr<const X509_Certificate>& end_entity,
+ const std::vector<std::shared_ptr<const X509_Certificate>>& end_entity_extra);
+
+/**
+* Check the certificate chain, but not any revocation data
+*
+* @param cert_path path built by build_certificate_path with OK result
+* @param ref_time whatever time you want to perform the validation
+* against (normally current system clock)
+* @param hostname the hostname
+* @param usage end entity usage checks
+* @param min_signature_algo_strength 80 or 110 typically
+* Note 80 allows 1024 bit RSA and SHA-1. 110 allows 2048 bit RSA and SHA-2.
+* Using 128 requires ECC (P-256) or ~3000 bit RSA keys.
+* @param trusted_hashes set of trusted hash functions, empty means accept any
+* hash we have an OID for
+* @return vector of results on per certificate in the path, each containing a set of
+* results. If all codes in the set are < Certificate_Status_Code::FIRST_ERROR_STATUS,
+* then the result for that certificate is successful. If all results are
+*/
+CertificatePathStatusCodes
+BOTAN_PUBLIC_API(2,0) check_chain(const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path,
+ std::chrono::system_clock::time_point ref_time,
+ const std::string& hostname,
+ Usage_Type usage,
+ size_t min_signature_algo_strength,
+ const std::set<std::string>& trusted_hashes);
+
+/**
+* Check OCSP responses for revocation information
+* @param cert_path path already validated by check_chain
+* @param ocsp_responses the OCSP responses to consider
+* @param certstores trusted roots
+* @param ref_time whatever time you want to perform the validation against
+* (normally current system clock)
+* @param max_ocsp_age maximum age of OCSP responses w/o next_update. If zero,
+* there is no maximum age
+* @return revocation status
+*/
+CertificatePathStatusCodes
+BOTAN_PUBLIC_API(2, 0) check_ocsp(const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path,
+ const std::vector<std::shared_ptr<const OCSP::Response>>& ocsp_responses,
+ const std::vector<Certificate_Store*>& certstores,
+ std::chrono::system_clock::time_point ref_time,
+ std::chrono::seconds max_ocsp_age = std::chrono::seconds::zero());
+
+/**
+* Check CRLs for revocation information
+* @param cert_path path already validated by check_chain
+* @param crls the list of CRLs to check, it is assumed that crls[i] (if not null)
+* is the associated CRL for the subject in cert_path[i].
+* @param ref_time whatever time you want to perform the validation against
+* (normally current system clock)
+* @return revocation status
+*/
+CertificatePathStatusCodes
+BOTAN_PUBLIC_API(2,0) check_crl(const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path,
+ const std::vector<std::shared_ptr<const X509_CRL>>& crls,
+ std::chrono::system_clock::time_point ref_time);
+
+/**
+* Check CRLs for revocation information
+* @param cert_path path already validated by check_chain
+* @param certstores a list of certificate stores to query for the CRL
+* @param ref_time whatever time you want to perform the validation against
+* (normally current system clock)
+* @return revocation status
+*/
+CertificatePathStatusCodes
+BOTAN_PUBLIC_API(2,0) check_crl(const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path,
+ const std::vector<Certificate_Store*>& certstores,
+ std::chrono::system_clock::time_point ref_time);
+
+#if defined(BOTAN_HAS_ONLINE_REVOCATION_CHECKS)
+
+/**
+* Check OCSP using online (HTTP) access. Current version creates a thread and
+* network connection per OCSP request made.
+*
+* @param cert_path path already validated by check_chain
+* @param trusted_certstores a list of certstores with trusted certs
+* @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
+* may block for up to timeout*cert_path.size()*C for some small C.
+* @param ocsp_check_intermediate_CAs if true also performs OCSP on any intermediate
+* CA certificates. If false, only does OCSP on the end entity cert.
+* @param max_ocsp_age maximum age of OCSP responses w/o next_update. If zero,
+* there is no maximum age
+* @return revocation status
+*/
+CertificatePathStatusCodes
+BOTAN_PUBLIC_API(2, 0) check_ocsp_online(const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path,
+ const std::vector<Certificate_Store*>& trusted_certstores,
+ std::chrono::system_clock::time_point ref_time,
+ std::chrono::milliseconds timeout,
+ bool ocsp_check_intermediate_CAs,
+ std::chrono::seconds max_ocsp_age = std::chrono::seconds::zero());
+
+/**
+* Check CRL using online (HTTP) access. Current version creates a thread and
+* network connection per CRL access.
+
+* @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.
+* @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
+* may block for up to timeout*cert_path.size()*C for some small C.
+* @return revocation status
+*/
+CertificatePathStatusCodes
+BOTAN_PUBLIC_API(2,0) check_crl_online(const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path,
+ const std::vector<Certificate_Store*>& trusted_certstores,
+ Certificate_Store_In_Memory* certstore_to_recv_crls,
+ std::chrono::system_clock::time_point ref_time,
+ std::chrono::milliseconds timeout);
+
+#endif
+
+/**
+* Find overall status (OK, error) of a validation
+* @param cert_status result of merge_revocation_status or check_chain
+*/
+Certificate_Status_Code BOTAN_PUBLIC_API(2,0) overall_status(const CertificatePathStatusCodes& cert_status);
+
+/**
+* Merge the results from CRL and/or OCSP checks into chain_status
+* @param chain_status the certificate status
+* @param crl_status results from check_crl
+* @param ocsp_status results from check_ocsp
+* @param require_rev_on_end_entity require valid CRL or OCSP on end-entity cert
+* @param require_rev_on_intermediates require valid CRL or OCSP on all intermediate certificates
+*/
+void BOTAN_PUBLIC_API(2,0) merge_revocation_status(CertificatePathStatusCodes& chain_status,
+ const CertificatePathStatusCodes& crl_status,
+ const CertificatePathStatusCodes& ocsp_status,
+ bool require_rev_on_end_entity,
+ bool require_rev_on_intermediates);
+
+}
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/x509/x509self.cpp b/comm/third_party/botan/src/lib/x509/x509self.cpp
new file mode 100644
index 0000000000..f75827fd83
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/x509self.cpp
@@ -0,0 +1,152 @@
+/*
+* PKCS #10/Self Signed Cert Creation
+* (C) 1999-2008,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/x509self.h>
+#include <botan/x509_key.h>
+#include <botan/x509_ext.h>
+#include <botan/x509_ca.h>
+#include <botan/der_enc.h>
+#include <botan/pubkey.h>
+#include <botan/hash.h>
+
+namespace Botan {
+
+namespace {
+
+/*
+* Load information from the X509_Cert_Options
+*/
+void load_info(const X509_Cert_Options& opts, X509_DN& subject_dn,
+ AlternativeName& subject_alt)
+ {
+ subject_dn.add_attribute("X520.CommonName", opts.common_name);
+ subject_dn.add_attribute("X520.Country", opts.country);
+ subject_dn.add_attribute("X520.State", opts.state);
+ subject_dn.add_attribute("X520.Locality", opts.locality);
+ subject_dn.add_attribute("X520.Organization", opts.organization);
+ subject_dn.add_attribute("X520.OrganizationalUnit", opts.org_unit);
+ for(auto extra_ou : opts.more_org_units) {
+ subject_dn.add_attribute("X520.OrganizationalUnit", extra_ou);
+ }
+
+ subject_dn.add_attribute("X520.SerialNumber", opts.serial_number);
+ subject_alt = AlternativeName(opts.email, opts.uri, opts.dns, opts.ip);
+ subject_alt.add_othername(OID::from_string("PKIX.XMPPAddr"),
+ opts.xmpp, UTF8_STRING);
+
+ for(auto dns : opts.more_dns)
+ subject_alt.add_attribute("DNS", dns);
+ }
+}
+
+namespace X509 {
+
+/*
+* Create a new self-signed X.509 certificate
+*/
+X509_Certificate create_self_signed_cert(const X509_Cert_Options& opts,
+ const Private_Key& key,
+ const std::string& hash_fn,
+ RandomNumberGenerator& rng)
+ {
+ AlgorithmIdentifier sig_algo;
+ X509_DN subject_dn;
+ AlternativeName subject_alt;
+
+ // for now, only the padding option is used
+ std::map<std::string,std::string> sig_opts = { {"padding",opts.padding_scheme} };
+
+ const std::vector<uint8_t> pub_key = X509::BER_encode(key);
+ std::unique_ptr<PK_Signer> signer(choose_sig_format(key, sig_opts, rng, hash_fn, sig_algo));
+ BOTAN_ASSERT_NOMSG(sig_algo.get_oid().has_value());
+ load_info(opts, subject_dn, subject_alt);
+
+ Extensions extensions = opts.extensions;
+
+ Key_Constraints constraints;
+ if(opts.is_CA)
+ {
+ constraints = Key_Constraints(KEY_CERT_SIGN | CRL_SIGN);
+ }
+ else
+ {
+ verify_cert_constraints_valid_for_key_type(key, opts.constraints);
+ constraints = opts.constraints;
+ }
+
+ extensions.add_new(
+ new Cert_Extension::Basic_Constraints(opts.is_CA, opts.path_limit),
+ true);
+
+ if(constraints != NO_CONSTRAINTS)
+ {
+ extensions.add_new(new Cert_Extension::Key_Usage(constraints), true);
+ }
+
+ std::unique_ptr<Cert_Extension::Subject_Key_ID> skid(new Cert_Extension::Subject_Key_ID(pub_key, hash_fn));
+
+ extensions.add_new(new Cert_Extension::Authority_Key_ID(skid->get_key_id()));
+ extensions.add_new(skid.release());
+
+ extensions.add_new(
+ new Cert_Extension::Subject_Alternative_Name(subject_alt));
+
+ extensions.add_new(
+ new Cert_Extension::Extended_Key_Usage(opts.ex_constraints));
+
+ return X509_CA::make_cert(signer.get(), rng, sig_algo, pub_key,
+ opts.start, opts.end,
+ subject_dn, subject_dn,
+ extensions);
+ }
+
+/*
+* Create a PKCS #10 certificate request
+*/
+PKCS10_Request create_cert_req(const X509_Cert_Options& opts,
+ const Private_Key& key,
+ const std::string& hash_fn,
+ RandomNumberGenerator& rng)
+ {
+ X509_DN subject_dn;
+ AlternativeName subject_alt;
+ load_info(opts, subject_dn, subject_alt);
+
+ Key_Constraints constraints;
+ if(opts.is_CA)
+ {
+ constraints = Key_Constraints(KEY_CERT_SIGN | CRL_SIGN);
+ }
+ else
+ {
+ verify_cert_constraints_valid_for_key_type(key, opts.constraints);
+ constraints = opts.constraints;
+ }
+
+ Extensions extensions = opts.extensions;
+
+ extensions.add_new(new Cert_Extension::Basic_Constraints(opts.is_CA, opts.path_limit));
+
+ if(constraints != NO_CONSTRAINTS)
+ {
+ extensions.add_new(new Cert_Extension::Key_Usage(constraints));
+ }
+ extensions.add_new(new Cert_Extension::Extended_Key_Usage(opts.ex_constraints));
+ extensions.add_new(new Cert_Extension::Subject_Alternative_Name(subject_alt));
+
+ return PKCS10_Request::create(key,
+ subject_dn,
+ extensions,
+ hash_fn,
+ rng,
+ opts.padding_scheme,
+ opts.challenge);
+ }
+
+}
+
+}
diff --git a/comm/third_party/botan/src/lib/x509/x509self.h b/comm/third_party/botan/src/lib/x509/x509self.h
new file mode 100644
index 0000000000..27c30b12dc
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/x509self.h
@@ -0,0 +1,222 @@
+/*
+* X.509 Self-Signed Certificate
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_X509_SELF_H_
+#define BOTAN_X509_SELF_H_
+
+#include <botan/x509cert.h>
+#include <botan/pkcs10.h>
+#include <botan/pkix_types.h>
+
+namespace Botan {
+
+class RandomNumberGenerator;
+class Private_Key;
+
+/**
+* Options for X.509 certificates.
+*/
+class BOTAN_PUBLIC_API(2,0) X509_Cert_Options final
+ {
+ public:
+ /**
+ * the subject common name
+ */
+ std::string common_name;
+
+ /**
+ * the subject counry
+ */
+ std::string country;
+
+ /**
+ * the subject organization
+ */
+ std::string organization;
+
+ /**
+ * the subject organizational unit
+ */
+ std::string org_unit;
+
+ /**
+ * additional subject organizational units.
+ */
+ std::vector<std::string> more_org_units;
+
+ /**
+ * the subject locality
+ */
+ std::string locality;
+
+ /**
+ * the subject state
+ */
+ std::string state;
+
+ /**
+ * the subject serial number
+ */
+ std::string serial_number;
+
+ /**
+ * the subject email adress
+ */
+ std::string email;
+
+ /**
+ * the subject URI
+ */
+ std::string uri;
+
+ /**
+ * the subject IPv4 address
+ */
+ std::string ip;
+
+ /**
+ * the subject DNS
+ */
+ std::string dns;
+
+ /**
+ * additional subject DNS entries.
+ */
+ std::vector<std::string> more_dns;
+
+ /**
+ * the subject XMPP
+ */
+ std::string xmpp;
+
+ /**
+ * the subject challenge password
+ */
+ std::string challenge;
+
+ /**
+ * the subject notBefore
+ */
+ X509_Time start;
+ /**
+ * the subject notAfter
+ */
+ X509_Time end;
+
+ /**
+ * Indicates whether the certificate request
+ */
+ bool is_CA;
+
+ /**
+ * Indicates the BasicConstraints path limit
+ */
+ size_t path_limit;
+
+ std::string padding_scheme;
+
+ /**
+ * The key constraints for the subject public key
+ */
+ Key_Constraints constraints;
+
+ /**
+ * The key extended constraints for the subject public key
+ */
+ std::vector<OID> ex_constraints;
+
+ /**
+ * Additional X.509 extensions
+ */
+ Extensions extensions;
+
+ /**
+ * Mark the certificate as a CA certificate and set the path limit.
+ * @param limit the path limit to be set in the BasicConstraints extension.
+ */
+ void CA_key(size_t limit = 1);
+
+ /**
+ * Choose a padding scheme different from the default for the key used.
+ */
+ void set_padding_scheme(const std::string& scheme);
+
+ /**
+ * Set the notBefore of the certificate.
+ * @param time the notBefore value of the certificate
+ */
+ void not_before(const std::string& time);
+
+ /**
+ * Set the notAfter of the certificate.
+ * @param time the notAfter value of the certificate
+ */
+ void not_after(const std::string& time);
+
+ /**
+ * Add the key constraints of the KeyUsage extension.
+ * @param constr the constraints to set
+ */
+ void add_constraints(Key_Constraints constr);
+
+ /**
+ * Add constraints to the ExtendedKeyUsage extension.
+ * @param oid the oid to add
+ */
+ void add_ex_constraint(const OID& oid);
+
+ /**
+ * Add constraints to the ExtendedKeyUsage extension.
+ * @param name the name to look up the oid to add
+ */
+ void add_ex_constraint(const std::string& name);
+
+ /**
+ * Construct a new options object
+ * @param opts define the common name of this object. An example for this
+ * parameter would be "common_name/country/organization/organizational_unit".
+ * @param expire_time the expiration time (from the current clock in seconds)
+ */
+ X509_Cert_Options(const std::string& opts = "",
+ uint32_t expire_time = 365 * 24 * 60 * 60);
+ };
+
+namespace X509 {
+
+/**
+* Create a self-signed X.509 certificate.
+* @param opts the options defining the certificate to create
+* @param key the private key used for signing, i.e. the key
+* associated with this self-signed certificate
+* @param hash_fn the hash function to use
+* @param rng the rng to use
+* @return newly created self-signed certificate
+*/
+BOTAN_PUBLIC_API(2,0) X509_Certificate
+create_self_signed_cert(const X509_Cert_Options& opts,
+ const Private_Key& key,
+ const std::string& hash_fn,
+ RandomNumberGenerator& rng);
+
+/**
+* Create a PKCS#10 certificate request.
+* @param opts the options defining the request to create
+* @param key the key used to sign this request
+* @param rng the rng to use
+* @param hash_fn the hash function to use
+* @return newly created PKCS#10 request
+*/
+BOTAN_PUBLIC_API(2,0) PKCS10_Request create_cert_req(const X509_Cert_Options& opts,
+ const Private_Key& key,
+ const std::string& hash_fn,
+ RandomNumberGenerator& rng);
+
+}
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/python/botan2.py b/comm/third_party/botan/src/python/botan2.py
new file mode 100755
index 0000000000..6ae1bd6da3
--- /dev/null
+++ b/comm/third_party/botan/src/python/botan2.py
@@ -0,0 +1,1787 @@
+#!/usr/bin/python
+
+"""
+Python wrapper of the botan crypto library
+https://botan.randombit.net
+
+(C) 2015,2017,2018,2019 Jack Lloyd
+(C) 2015 Uri Blumenthal (extensions and patches)
+
+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 2.7, CPython 3.x, and PyPy
+
+It uses botan's ffi module, which exposes a C API. This version of the
+module requires FFI API version 20180713, which was introduced in
+Botan 2.8
+
+"""
+
+from ctypes import CDLL, POINTER, byref, create_string_buffer, \
+ c_void_p, c_size_t, c_uint8, c_uint32, c_uint64, c_int, c_uint, c_char_p
+
+from sys import version_info, platform
+from time import strptime, mktime, time as system_time
+from binascii import hexlify
+from datetime import datetime
+
+BOTAN_FFI_VERSION = 20191214
+
+#
+# Base exception for all exceptions raised from this module
+#
+class BotanException(Exception):
+
+ def __init__(self, message, rc=0):
+
+ self.__rc = rc
+
+ if rc == 0:
+ super(BotanException, self).__init__(message)
+ else:
+ descr = _DLL.botan_error_description(rc).decode('ascii')
+ super(BotanException, self).__init__("%s: %d (%s)" % (message, rc, descr))
+
+ def error_code(self):
+ return self.__rc
+
+#
+# Module initialization
+#
+
+def _load_botan_dll(expected_version):
+
+ possible_dll_names = []
+
+ if platform in ['win32', 'cygwin', 'msys']:
+ possible_dll_names.append('botan.dll')
+ elif platform in ['darwin', 'macos']:
+ possible_dll_names.append('libbotan-2.dylib')
+ else:
+ # assumed to be some Unix/Linux system
+ possible_dll_names.append('libbotan-2.so')
+ possible_dll_names += ['libbotan-2.so.%d' % (v) for v in reversed(range(13, 20))]
+
+ for dll_name in possible_dll_names:
+ try:
+ dll = CDLL(dll_name)
+ dll.botan_ffi_supports_api.argtypes = [c_uint32]
+ dll.botan_ffi_supports_api.restype = c_int
+ if dll.botan_ffi_supports_api(expected_version) == 0:
+ return dll
+ except OSError:
+ pass
+
+ raise BotanException("Could not find a usable Botan shared object library")
+
+def _errcheck(rc, fn, _args):
+ # This errcheck should only be used for int-returning functions
+ assert isinstance(rc, int)
+
+ if rc >= 0 or rc in fn.allowed_errors:
+ return rc
+ raise BotanException('%s failed' % (fn.__name__), rc)
+
+def _set_prototypes(dll):
+ # pylint: disable=too-many-statements,line-too-long
+ def ffi_api(fn, args, allowed_errors=None):
+ if allowed_errors is None:
+ allowed_errors = [-10]
+ fn.argtypes = args
+ fn.restype = c_int
+ fn.errcheck = _errcheck
+ fn.allowed_errors = allowed_errors
+
+ dll.botan_version_string.argtypes = []
+ dll.botan_version_string.restype = c_char_p
+
+ dll.botan_version_string.argtypes = []
+ dll.botan_version_string.restype = c_char_p
+
+ dll.botan_version_major.argtypes = []
+ dll.botan_version_major.restype = c_uint32
+
+ dll.botan_version_minor.argtypes = []
+ dll.botan_version_minor.restype = c_uint32
+
+ dll.botan_version_patch.argtypes = []
+ dll.botan_version_patch.restype = c_uint32
+
+ dll.botan_ffi_api_version.argtypes = []
+ dll.botan_ffi_api_version.restype = c_uint32
+
+ dll.botan_error_description.argtypes = [c_int]
+ dll.botan_error_description.restype = c_char_p
+
+ # These are generated using src/scripts/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])
+
+ ffi_api(dll.botan_hex_encode, [c_char_p, c_size_t, c_char_p, c_uint32])
+ ffi_api(dll.botan_hex_decode, [c_char_p, c_size_t, c_char_p, POINTER(c_size_t)])
+
+ ffi_api(dll.botan_base64_encode, [c_char_p, c_size_t, c_char_p, POINTER(c_size_t)])
+ ffi_api(dll.botan_base64_decode, [c_char_p, c_size_t, c_char_p, POINTER(c_size_t)])
+
+ # RNG
+ ffi_api(dll.botan_rng_init, [c_void_p, c_char_p])
+ 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_destroy, [c_void_p])
+
+ # HASH
+ ffi_api(dll.botan_hash_init, [c_void_p, c_char_p, c_uint32])
+ ffi_api(dll.botan_hash_copy_state, [c_void_p, c_void_p])
+ ffi_api(dll.botan_hash_output_length, [c_void_p, POINTER(c_size_t)])
+ ffi_api(dll.botan_hash_block_size, [c_void_p, POINTER(c_size_t)])
+ ffi_api(dll.botan_hash_update, [c_void_p, c_char_p, c_size_t])
+ ffi_api(dll.botan_hash_final, [c_void_p, c_char_p])
+ ffi_api(dll.botan_hash_clear, [c_void_p])
+ 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)])
+
+ # 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)])
+ ffi_api(dll.botan_mac_set_key, [c_void_p, c_char_p, c_size_t])
+ ffi_api(dll.botan_mac_update, [c_void_p, c_char_p, c_size_t])
+ ffi_api(dll.botan_mac_final, [c_void_p, c_char_p])
+ ffi_api(dll.botan_mac_clear, [c_void_p])
+ ffi_api(dll.botan_mac_name, [c_void_p, c_char_p, POINTER(c_size_t)])
+ ffi_api(dll.botan_mac_get_keyspec, [c_void_p, POINTER(c_size_t), POINTER(c_size_t), POINTER(c_size_t)])
+ ffi_api(dll.botan_mac_destroy, [c_void_p])
+
+ # CIPHER
+ ffi_api(dll.botan_cipher_init, [c_void_p, c_char_p, c_uint32])
+ ffi_api(dll.botan_cipher_name, [c_void_p, c_char_p, POINTER(c_size_t)])
+ ffi_api(dll.botan_cipher_output_length, [c_void_p, c_size_t, POINTER(c_size_t)])
+ ffi_api(dll.botan_cipher_valid_nonce_length, [c_void_p, c_size_t])
+ ffi_api(dll.botan_cipher_get_tag_length, [c_void_p, POINTER(c_size_t)])
+ ffi_api(dll.botan_cipher_get_default_nonce_length, [c_void_p, POINTER(c_size_t)])
+ ffi_api(dll.botan_cipher_get_update_granularity, [c_void_p, POINTER(c_size_t)])
+ ffi_api(dll.botan_cipher_query_keylen, [c_void_p, POINTER(c_size_t), POINTER(c_size_t)])
+ ffi_api(dll.botan_cipher_get_keyspec, [c_void_p, POINTER(c_size_t), POINTER(c_size_t), POINTER(c_size_t)])
+ ffi_api(dll.botan_cipher_set_key, [c_void_p, c_char_p, c_size_t])
+ ffi_api(dll.botan_cipher_reset, [c_void_p])
+ ffi_api(dll.botan_cipher_set_associated_data, [c_void_p, c_char_p, c_size_t])
+ ffi_api(dll.botan_cipher_start, [c_void_p, c_char_p, c_size_t])
+ ffi_api(dll.botan_cipher_update,
+ [c_void_p, c_uint32, c_char_p, c_size_t, POINTER(c_size_t), c_char_p, c_size_t, POINTER(c_size_t)])
+ ffi_api(dll.botan_cipher_clear, [c_void_p])
+ ffi_api(dll.botan_cipher_destroy, [c_void_p])
+
+ ffi_api(dll.botan_pbkdf,
+ [c_char_p, c_char_p, c_size_t, c_char_p, c_char_p, c_size_t, c_size_t])
+ ffi_api(dll.botan_pbkdf_timed,
+ [c_char_p, c_char_p, c_size_t, c_char_p, c_char_p, c_size_t, c_size_t, POINTER(c_size_t)])
+
+ ffi_api(dll.botan_pwdhash,
+ [c_char_p, c_size_t, c_size_t, c_size_t, c_char_p, c_size_t, c_char_p, c_size_t, c_char_p, c_size_t])
+ ffi_api(dll.botan_pwdhash_timed,
+ [c_char_p, c_uint32, POINTER(c_size_t), POINTER(c_size_t), POINTER(c_size_t), c_char_p, c_size_t, c_char_p, c_size_t, c_char_p, c_size_t])
+
+ ffi_api(dll.botan_scrypt,
+ [c_char_p, c_size_t, c_char_p, c_char_p, c_size_t, c_size_t, c_size_t, c_size_t])
+
+ ffi_api(dll.botan_kdf,
+ [c_char_p, c_char_p, c_size_t, c_char_p, c_size_t, c_char_p, c_size_t, c_char_p, c_size_t])
+
+ # BLOCK
+ ffi_api(dll.botan_block_cipher_init, [c_void_p, c_char_p])
+ ffi_api(dll.botan_block_cipher_destroy, [c_void_p])
+ ffi_api(dll.botan_block_cipher_clear, [c_void_p])
+ ffi_api(dll.botan_block_cipher_set_key, [c_void_p, c_char_p, c_size_t])
+ ffi_api(dll.botan_block_cipher_block_size, [c_void_p])
+ ffi_api(dll.botan_block_cipher_encrypt_blocks, [c_void_p, c_char_p, c_char_p, c_size_t])
+ ffi_api(dll.botan_block_cipher_decrypt_blocks, [c_void_p, c_char_p, c_char_p, c_size_t])
+ ffi_api(dll.botan_block_cipher_name, [c_void_p, c_char_p, POINTER(c_size_t)])
+ ffi_api(dll.botan_block_cipher_get_keyspec, [c_void_p, POINTER(c_size_t), POINTER(c_size_t), POINTER(c_size_t)])
+
+ # MP
+ 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_to_str, [c_void_p, c_uint8, c_char_p, POINTER(c_size_t)])
+ 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])
+ ffi_api(dll.botan_mp_set_from_str, [c_void_p, c_char_p])
+ ffi_api(dll.botan_mp_set_from_radix_str, [c_void_p, c_char_p, c_size_t])
+ ffi_api(dll.botan_mp_num_bits, [c_void_p, POINTER(c_size_t)])
+ ffi_api(dll.botan_mp_num_bytes, [c_void_p, POINTER(c_size_t)])
+ ffi_api(dll.botan_mp_to_bin, [c_void_p, c_char_p])
+ ffi_api(dll.botan_mp_from_bin, [c_void_p, c_char_p, c_size_t])
+ ffi_api(dll.botan_mp_to_uint32, [c_void_p, POINTER(c_uint32)])
+ ffi_api(dll.botan_mp_is_positive, [c_void_p])
+ ffi_api(dll.botan_mp_is_negative, [c_void_p])
+ ffi_api(dll.botan_mp_flip_sign, [c_void_p])
+ ffi_api(dll.botan_mp_is_zero, [c_void_p])
+ ffi_api(dll.botan_mp_is_odd, [c_void_p])
+ ffi_api(dll.botan_mp_is_even, [c_void_p])
+ ffi_api(dll.botan_mp_add_u32, [c_void_p, c_void_p, c_uint32])
+ ffi_api(dll.botan_mp_sub_u32, [c_void_p, c_void_p, c_uint32])
+ ffi_api(dll.botan_mp_add, [c_void_p, c_void_p, c_void_p])
+ ffi_api(dll.botan_mp_sub, [c_void_p, c_void_p, c_void_p])
+ ffi_api(dll.botan_mp_mul, [c_void_p, c_void_p, c_void_p])
+ ffi_api(dll.botan_mp_div, [c_void_p, c_void_p, c_void_p, c_void_p])
+ ffi_api(dll.botan_mp_mod_mul, [c_void_p, c_void_p, c_void_p, c_void_p])
+ ffi_api(dll.botan_mp_equal, [c_void_p, c_void_p])
+ ffi_api(dll.botan_mp_cmp, [POINTER(c_int), c_void_p, c_void_p])
+ ffi_api(dll.botan_mp_swap, [c_void_p, c_void_p])
+ ffi_api(dll.botan_mp_powmod, [c_void_p, c_void_p, c_void_p, c_void_p])
+ ffi_api(dll.botan_mp_lshift, [c_void_p, c_void_p, c_size_t])
+ ffi_api(dll.botan_mp_rshift, [c_void_p, c_void_p, c_size_t])
+ ffi_api(dll.botan_mp_mod_inverse, [c_void_p, c_void_p, c_void_p])
+ ffi_api(dll.botan_mp_rand_bits, [c_void_p, c_void_p, c_size_t])
+ ffi_api(dll.botan_mp_rand_range, [c_void_p, c_void_p, c_void_p, c_void_p])
+ ffi_api(dll.botan_mp_gcd, [c_void_p, c_void_p, c_void_p])
+ ffi_api(dll.botan_mp_is_prime, [c_void_p, c_void_p, c_size_t])
+ ffi_api(dll.botan_mp_get_bit, [c_void_p, c_size_t])
+ ffi_api(dll.botan_mp_set_bit, [c_void_p, c_size_t])
+ ffi_api(dll.botan_mp_clear_bit, [c_void_p, c_size_t])
+
+ ffi_api(dll.botan_bcrypt_generate,
+ [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])
+
+ # PUBKEY
+ ffi_api(dll.botan_privkey_create, [c_void_p, c_char_p, c_char_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])
+ ffi_api(dll.botan_privkey_create_ecdh, [c_void_p, c_void_p, c_char_p])
+ ffi_api(dll.botan_privkey_create_mceliece, [c_void_p, c_void_p, c_size_t, c_size_t])
+ ffi_api(dll.botan_privkey_create_dh, [c_void_p, c_void_p, c_char_p])
+ ffi_api(dll.botan_privkey_create_dsa, [c_void_p, c_void_p, c_size_t, c_size_t])
+ ffi_api(dll.botan_privkey_create_elgamal, [c_void_p, c_void_p, c_size_t, c_size_t])
+ ffi_api(dll.botan_privkey_load,
+ [c_void_p, c_void_p, c_char_p, c_size_t, c_char_p])
+ ffi_api(dll.botan_privkey_destroy, [c_void_p])
+ ffi_api(dll.botan_privkey_export, [c_void_p, c_char_p, POINTER(c_size_t), c_uint32])
+ ffi_api(dll.botan_privkey_algo_name, [c_void_p, c_char_p, POINTER(c_size_t)])
+ ffi_api(dll.botan_privkey_export_encrypted,
+ [c_void_p, c_char_p, POINTER(c_size_t), c_void_p, c_char_p, c_char_p, c_uint32])
+ ffi_api(dll.botan_privkey_export_encrypted_pbkdf_msec,
+ [c_void_p, c_char_p, POINTER(c_size_t), c_void_p, c_char_p, c_uint32, POINTER(c_size_t), c_char_p, c_char_p, c_uint32])
+ ffi_api(dll.botan_privkey_export_encrypted_pbkdf_iter,
+ [c_void_p, c_char_p, POINTER(c_size_t), c_void_p, c_char_p, c_size_t, c_char_p, c_char_p, c_uint32])
+ ffi_api(dll.botan_privkey_export_pubkey, [c_void_p, c_void_p])
+ ffi_api(dll.botan_pubkey_load, [c_void_p, c_char_p, c_size_t])
+ ffi_api(dll.botan_pubkey_export, [c_void_p, c_char_p, POINTER(c_size_t), c_uint32])
+ ffi_api(dll.botan_pubkey_algo_name, [c_void_p, c_char_p, POINTER(c_size_t)])
+ ffi_api(dll.botan_pubkey_check_key, [c_void_p, c_void_p, c_uint32], [-1])
+ ffi_api(dll.botan_pubkey_estimated_strength, [c_void_p, POINTER(c_size_t)])
+ ffi_api(dll.botan_pubkey_fingerprint, [c_void_p, c_char_p, c_char_p, POINTER(c_size_t)])
+ 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_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])
+ ffi_api(dll.botan_privkey_rsa_get_q, [c_void_p, c_void_p])
+ ffi_api(dll.botan_privkey_rsa_get_d, [c_void_p, c_void_p])
+ ffi_api(dll.botan_privkey_rsa_get_n, [c_void_p, c_void_p])
+ 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_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,
+ [c_void_p, c_void_p, c_void_p, c_void_p, c_void_p])
+ ffi_api(dll.botan_pubkey_load_dsa,
+ [c_void_p, c_void_p, c_void_p, c_void_p, c_void_p])
+ ffi_api(dll.botan_privkey_dsa_get_x, [c_void_p, c_void_p])
+ ffi_api(dll.botan_pubkey_dsa_get_p, [c_void_p, c_void_p])
+ ffi_api(dll.botan_pubkey_dsa_get_q, [c_void_p, c_void_p])
+ ffi_api(dll.botan_pubkey_dsa_get_g, [c_void_p, c_void_p])
+ ffi_api(dll.botan_pubkey_dsa_get_y, [c_void_p, c_void_p])
+ ffi_api(dll.botan_privkey_load_dh, [c_void_p, c_void_p, c_void_p, c_void_p])
+ 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_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])
+ ffi_api(dll.botan_pubkey_ed25519_get_pubkey, [c_void_p, c_char_p])
+ ffi_api(dll.botan_privkey_load_x25519, [c_void_p, c_char_p])
+ ffi_api(dll.botan_pubkey_load_x25519, [c_void_p, c_char_p])
+ ffi_api(dll.botan_privkey_x25519_get_privkey, [c_void_p, c_char_p])
+ ffi_api(dll.botan_pubkey_x25519_get_pubkey, [c_void_p, 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_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_sm2, [c_void_p, c_void_p, c_void_p, 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])
+ ffi_api(dll.botan_pubkey_sm2_compute_za,
+ [c_char_p, POINTER(c_size_t), c_char_p, c_char_p, c_void_p])
+
+ # PK
+ ffi_api(dll.botan_pk_op_encrypt_create, [c_void_p, c_void_p, c_char_p, c_uint32])
+ ffi_api(dll.botan_pk_op_encrypt_destroy, [c_void_p])
+ ffi_api(dll.botan_pk_op_encrypt_output_length, [c_void_p, c_size_t, POINTER(c_size_t)])
+ ffi_api(dll.botan_pk_op_encrypt,
+ [c_void_p, c_void_p, c_char_p, POINTER(c_size_t), c_char_p, c_size_t])
+ ffi_api(dll.botan_pk_op_decrypt_create, [c_void_p, c_void_p, c_char_p, c_uint32])
+ ffi_api(dll.botan_pk_op_decrypt_destroy, [c_void_p])
+ ffi_api(dll.botan_pk_op_decrypt_output_length, [c_void_p, c_size_t, POINTER(c_size_t)])
+ ffi_api(dll.botan_pk_op_decrypt,
+ [c_void_p, c_char_p, POINTER(c_size_t), c_char_p, c_size_t])
+ ffi_api(dll.botan_pk_op_sign_create, [c_void_p, c_void_p, c_char_p, c_uint32])
+ ffi_api(dll.botan_pk_op_sign_destroy, [c_void_p])
+ ffi_api(dll.botan_pk_op_sign_output_length, [c_void_p, POINTER(c_size_t)])
+ ffi_api(dll.botan_pk_op_sign_update, [c_void_p, c_char_p, c_size_t])
+ ffi_api(dll.botan_pk_op_sign_finish, [c_void_p, c_void_p, c_char_p, POINTER(c_size_t)])
+ ffi_api(dll.botan_pk_op_verify_create, [c_void_p, c_void_p, c_char_p, c_uint32])
+ ffi_api(dll.botan_pk_op_verify_destroy, [c_void_p])
+ ffi_api(dll.botan_pk_op_verify_update, [c_void_p, c_char_p, c_size_t])
+ ffi_api(dll.botan_pk_op_verify_finish, [c_void_p, c_char_p, c_size_t])
+ ffi_api(dll.botan_pk_op_key_agreement_create, [c_void_p, c_void_p, c_char_p, c_uint32])
+ ffi_api(dll.botan_pk_op_key_agreement_destroy, [c_void_p])
+ ffi_api(dll.botan_pk_op_key_agreement_export_public, [c_void_p, c_char_p, POINTER(c_size_t)])
+ ffi_api(dll.botan_pk_op_key_agreement_size, [c_void_p, POINTER(c_size_t)])
+ ffi_api(dll.botan_pk_op_key_agreement,
+ [c_void_p, c_char_p, POINTER(c_size_t), c_char_p, c_size_t, c_char_p, c_size_t])
+
+ ffi_api(dll.botan_pkcs_hash_id, [c_char_p, c_char_p, POINTER(c_size_t)])
+
+ ffi_api(dll.botan_mceies_encrypt,
+ [c_void_p, c_void_p, c_char_p, c_char_p, c_size_t, c_char_p, c_size_t, c_char_p, POINTER(c_size_t)])
+ ffi_api(dll.botan_mceies_decrypt,
+ [c_void_p, c_char_p, c_char_p, c_size_t, c_char_p, c_size_t, c_char_p, POINTER(c_size_t)])
+
+ # X509
+ ffi_api(dll.botan_x509_cert_load, [c_void_p, c_char_p, c_size_t])
+ ffi_api(dll.botan_x509_cert_load_file, [c_void_p, c_char_p])
+ ffi_api(dll.botan_x509_cert_destroy, [c_void_p])
+ ffi_api(dll.botan_x509_cert_dup, [c_void_p, c_void_p])
+ ffi_api(dll.botan_x509_cert_get_time_starts, [c_void_p, c_char_p, POINTER(c_size_t)])
+ ffi_api(dll.botan_x509_cert_get_time_expires, [c_void_p, c_char_p, POINTER(c_size_t)])
+ ffi_api(dll.botan_x509_cert_not_before, [c_void_p, POINTER(c_uint64)])
+ ffi_api(dll.botan_x509_cert_not_after, [c_void_p, POINTER(c_uint64)])
+ ffi_api(dll.botan_x509_cert_get_fingerprint, [c_void_p, c_char_p, c_char_p, POINTER(c_size_t)])
+ ffi_api(dll.botan_x509_cert_get_serial_number, [c_void_p, c_char_p, POINTER(c_size_t)])
+ ffi_api(dll.botan_x509_cert_get_authority_key_id, [c_void_p, c_char_p, POINTER(c_size_t)])
+ ffi_api(dll.botan_x509_cert_get_subject_key_id, [c_void_p, c_char_p, POINTER(c_size_t)])
+ ffi_api(dll.botan_x509_cert_get_public_key_bits, [c_void_p, c_char_p, POINTER(c_size_t)])
+ ffi_api(dll.botan_x509_cert_get_public_key, [c_void_p, c_void_p])
+ ffi_api(dll.botan_x509_cert_get_issuer_dn,
+ [c_void_p, c_char_p, c_size_t, c_char_p, POINTER(c_size_t)])
+ ffi_api(dll.botan_x509_cert_get_subject_dn,
+ [c_void_p, c_char_p, c_size_t, c_char_p, POINTER(c_size_t)])
+ ffi_api(dll.botan_x509_cert_to_string, [c_void_p, c_char_p, POINTER(c_size_t)])
+ ffi_api(dll.botan_x509_cert_allowed_usage, [c_void_p, c_uint])
+ ffi_api(dll.botan_x509_cert_hostname_match, [c_void_p, c_char_p], [-1])
+ ffi_api(dll.botan_x509_cert_verify,
+ [POINTER(c_int), c_void_p, c_void_p, c_size_t, c_void_p, c_size_t, c_char_p, c_size_t, c_char_p, c_uint64])
+
+ dll.botan_x509_cert_validation_status.argtypes = [c_int]
+ dll.botan_x509_cert_validation_status.restype = c_char_p
+
+ # 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_destroy, [c_void_p])
+ ffi_api(dll.botan_x509_is_revoked, [c_void_p, c_void_p], [-1])
+ 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])
+
+ ffi_api(dll.botan_key_wrap3394,
+ [c_char_p, c_size_t, c_char_p, c_size_t, c_char_p, POINTER(c_size_t)])
+ ffi_api(dll.botan_key_unwrap3394,
+ [c_char_p, c_size_t, c_char_p, c_size_t, c_char_p, POINTER(c_size_t)])
+
+ # HOTP
+ ffi_api(dll.botan_hotp_init,
+ [c_void_p, c_char_p, c_size_t, c_char_p, c_size_t])
+ ffi_api(dll.botan_hotp_destroy, [c_void_p])
+ ffi_api(dll.botan_hotp_generate, [c_void_p, POINTER(c_uint32), c_uint64])
+ ffi_api(dll.botan_hotp_check,
+ [c_void_p, POINTER(c_uint64), c_uint32, c_uint64, c_size_t])
+
+ # TOTP
+ ffi_api(dll.botan_totp_init,
+ [c_void_p, c_char_p, c_size_t, c_char_p, c_size_t, c_size_t])
+ ffi_api(dll.botan_totp_destroy, [c_void_p])
+ ffi_api(dll.botan_totp_generate, [c_void_p, POINTER(c_uint32), c_uint64])
+ ffi_api(dll.botan_totp_check, [c_void_p, c_uint32, c_uint64, c_size_t])
+
+ # FPE
+ ffi_api(dll.botan_fpe_fe1_init,
+ [c_void_p, c_void_p, c_char_p, c_size_t, c_size_t, c_uint32])
+ ffi_api(dll.botan_fpe_destroy, [c_void_p])
+ ffi_api(dll.botan_fpe_encrypt, [c_void_p, c_void_p, c_char_p, c_size_t])
+ ffi_api(dll.botan_fpe_decrypt, [c_void_p, c_void_p, c_char_p, c_size_t])
+
+ return dll
+
+#
+# Load the DLL and set prototypes on it
+#
+_DLL = _set_prototypes(_load_botan_dll(BOTAN_FFI_VERSION))
+
+#
+# Internal utilities
+#
+def _call_fn_returning_sz(fn):
+ sz = c_size_t(0)
+ fn(byref(sz))
+ return int(sz.value)
+
+def _call_fn_returning_vec(guess, fn):
+
+ buf = create_string_buffer(guess)
+ buf_len = c_size_t(len(buf))
+
+ rc = fn(buf, byref(buf_len))
+ if rc == -10 and buf_len.value > len(buf):
+ return _call_fn_returning_vec(buf_len.value, fn)
+
+ assert buf_len.value <= len(buf)
+ return buf.raw[0:int(buf_len.value)]
+
+def _call_fn_returning_str(guess, fn):
+ # Assumes that anything called with this is returning plain ASCII strings
+ # (base64 data, algorithm names, etc)
+ v = _call_fn_returning_vec(guess, fn)
+ return v.decode('ascii')[:-1]
+
+def _ctype_str(s):
+ if s is None:
+ return None
+ assert isinstance(s, str)
+ if version_info[0] < 3:
+ return s
+ else:
+ return s.encode('utf-8')
+
+def _ctype_to_str(s):
+ if version_info[0] < 3:
+ return s.encode('utf-8')
+ else:
+ return s.decode('utf-8')
+
+def _ctype_bits(s):
+ if version_info[0] < 3:
+ if isinstance(s, str):
+ return s
+ elif isinstance(s, unicode): # pylint: disable=undefined-variable
+ return s.decode('utf-8')
+ else:
+ raise Exception("Internal error - unexpected type %s provided to _ctype_bits" % (type(s).__name__))
+ else:
+ if isinstance(s, bytes):
+ return s
+ elif isinstance(s, str):
+ return s.encode('utf-8')
+ else:
+ raise Exception("Internal error - unexpected type %s provided to _ctype_bits" % (type(s).__name__))
+
+def _ctype_bufout(buf):
+ if version_info[0] < 3:
+ return str(buf.raw)
+ else:
+ return buf.raw
+
+def _hex_encode(buf):
+ return hexlify(buf).decode('ascii')
+
+#
+# Versioning
+#
+def version_major():
+ return int(_DLL.botan_version_major())
+
+def version_minor():
+ return int(_DLL.botan_version_minor())
+
+def version_patch():
+ return int(_DLL.botan_version_patch())
+
+def ffi_api_version():
+ return int(_DLL.botan_ffi_api_version())
+
+def version_string():
+ return _DLL.botan_version_string().decode('ascii')
+
+#
+# Utilities
+#
+def const_time_compare(x, y):
+ len_x = len(x)
+ len_y = len(y)
+ if len_x != len_y:
+ return False
+ rc = _DLL.botan_constant_time_compare(_ctype_bits(x), _ctype_bits(y), c_size_t(len_x))
+ return rc == 0
+
+#
+# RNG
+#
+class RandomNumberGenerator(object):
+ # Can also use type "system"
+ def __init__(self, rng_type='system'):
+ self.__obj = c_void_p(0)
+ _DLL.botan_rng_init(byref(self.__obj), _ctype_str(rng_type))
+
+ def __del__(self):
+ _DLL.botan_rng_destroy(self.__obj)
+
+ def handle_(self):
+ return self.__obj
+
+ def reseed(self, bits=256):
+ _DLL.botan_rng_reseed(self.__obj, bits)
+
+ def reseed_from_rng(self, source_rng, bits=256):
+ _DLL.botan_rng_reseed_from_rng(self.__obj, source_rng.handle_(), bits)
+
+ def add_entropy(self, seed):
+ _DLL.botan_rng_add_entropy(self.__obj, _ctype_bits(seed), len(seed))
+
+ def get(self, length):
+ out = create_string_buffer(length)
+ l = c_size_t(length)
+ _DLL.botan_rng_get(self.__obj, out, l)
+ return _ctype_bufout(out)
+
+#
+# Block cipher
+#
+class BlockCipher(object):
+ def __init__(self, algo):
+
+ 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_block_cipher_init(byref(self.__obj), _ctype_str(algo), flags)
+
+ min_keylen = c_size_t(0)
+ max_keylen = c_size_t(0)
+ mod_keylen = c_size_t(0)
+ _DLL.botan_block_cipher_get_keyspec(self.__obj, byref(min_keylen), byref(max_keylen), byref(mod_keylen))
+
+ self.__min_keylen = min_keylen.value
+ self.__max_keylen = max_keylen.value
+ self.__mod_keylen = mod_keylen.value
+
+ self.__block_size = _DLL.botan_block_cipher_block_size(self.__obj)
+
+ def __del__(self):
+ _DLL.botan_block_cipher_destroy(self.__obj)
+
+ def set_key(self, key):
+ _DLL.botan_block_cipher_set_key(self.__obj, key, len(key))
+
+ def encrypt(self, pt):
+ if len(pt) % self.block_size() != 0:
+ raise Exception("Invalid input must be multiple of block size")
+
+ blocks = c_size_t(len(pt) // self.block_size())
+ output = create_string_buffer(len(pt))
+ _DLL.botan_block_cipher_encrypt_blocks(self.__obj, pt, output, blocks)
+ return output
+
+ def decrypt(self, ct):
+ if len(ct) % self.block_size() != 0:
+ raise Exception("Invalid input must be multiple of block size")
+
+ blocks = c_size_t(len(ct) // self.block_size())
+ output = create_string_buffer(len(ct))
+ _DLL.botan_block_cipher_decrypt_blocks(self.__obj, ct, output, blocks)
+ return output
+
+ def algo_name(self):
+ 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):
+ return self.__block_size
+
+ def minimum_keylength(self):
+ return self.__min_keylen
+
+ def maximum_keylength(self):
+ return self.__max_keylen
+
+
+#
+# Hash function
+#
+class HashFunction(object):
+ def __init__(self, algo):
+
+ 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_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))
+
+ def __del__(self):
+ _DLL.botan_hash_destroy(self.__obj)
+
+ def copy_state(self):
+ copy = c_void_p(0)
+ _DLL.botan_hash_copy_state(byref(copy), self.__obj)
+ return HashFunction(copy)
+
+ def algo_name(self):
+ return _call_fn_returning_str(32, lambda b, bl: _DLL.botan_hash_name(self.__obj, b, bl))
+
+ def clear(self):
+ _DLL.botan_hash_clear(self.__obj)
+
+ def output_length(self):
+ return self.__output_length
+
+ def block_size(self):
+ return self.__block_size
+
+ def update(self, x):
+ _DLL.botan_hash_update(self.__obj, _ctype_bits(x), len(x))
+
+ def final(self):
+ out = create_string_buffer(self.output_length())
+ _DLL.botan_hash_final(self.__obj, out)
+ return _ctype_bufout(out)
+
+#
+# Message authentication codes
+#
+class MsgAuthCode(object):
+ def __init__(self, algo):
+ 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)
+
+ min_keylen = c_size_t(0)
+ max_keylen = c_size_t(0)
+ mod_keylen = c_size_t(0)
+ _DLL.botan_mac_get_keyspec(self.__obj, byref(min_keylen), byref(max_keylen), byref(mod_keylen))
+
+ self.__min_keylen = min_keylen.value
+ self.__max_keylen = max_keylen.value
+ self.__mod_keylen = mod_keylen.value
+
+ output_length = c_size_t(0)
+ _DLL.botan_mac_output_length(self.__obj, byref(output_length))
+ self.__output_length = output_length.value
+
+ def __del__(self):
+ _DLL.botan_mac_destroy(self.__obj)
+
+ def clear(self):
+ _DLL.botan_mac_clear(self.__obj)
+
+ def algo_name(self):
+ return _call_fn_returning_str(32, lambda b, bl: _DLL.botan_mac_name(self.__obj, b, bl))
+
+ def output_length(self):
+ return self.__output_length
+
+ def minimum_keylength(self):
+ return self.__min_keylen
+
+ def maximum_keylength(self):
+ return self.__max_keylen
+
+ def set_key(self, key):
+ _DLL.botan_mac_set_key(self.__obj, key, len(key))
+
+ def update(self, x):
+ _DLL.botan_mac_update(self.__obj, x, len(x))
+
+ def final(self):
+ out = create_string_buffer(self.output_length())
+ _DLL.botan_mac_final(self.__obj, out)
+ return _ctype_bufout(out)
+
+class SymmetricCipher(object):
+ def __init__(self, algo, encrypt=True):
+ flags = 0 if encrypt else 1
+ self.__obj = c_void_p(0)
+ _DLL.botan_cipher_init(byref(self.__obj), _ctype_str(algo), flags)
+
+ def __del__(self):
+ _DLL.botan_cipher_destroy(self.__obj)
+
+ def algo_name(self):
+ 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 key_length(self):
+ 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 is_authenticated(self):
+ return self.tag_length() > 0
+
+ def valid_nonce_length(self, nonce_len):
+ rc = _DLL.botan_cipher_valid_nonce_length(self.__obj, nonce_len)
+ return rc == 1
+
+ def reset(self):
+ _DLL.botan_cipher_reset(self.__obj)
+
+ def clear(self):
+ _DLL.botan_cipher_clear(self.__obj)
+
+ def set_key(self, key):
+ _DLL.botan_cipher_set_key(self.__obj, key, len(key))
+
+ def set_assoc_data(self, ad):
+ _DLL.botan_cipher_set_associated_data(self.__obj, ad, len(ad))
+
+ def start(self, nonce):
+ _DLL.botan_cipher_start(self.__obj, nonce, len(nonce))
+
+ def _update(self, txt, final):
+
+ inp = txt if txt else ''
+ inp_sz = c_size_t(len(inp))
+ inp_consumed = c_size_t(0)
+ out = create_string_buffer(inp_sz.value + (self.tag_length() if final else 0))
+ out_sz = c_size_t(len(out))
+ out_written = c_size_t(0)
+ flags = c_uint32(1 if final else 0)
+
+ _DLL.botan_cipher_update(self.__obj, flags,
+ out, out_sz, byref(out_written),
+ _ctype_bits(inp), inp_sz, byref(inp_consumed))
+
+ # buffering not supported yet
+ assert inp_consumed.value == inp_sz.value
+ return out.raw[0:int(out_written.value)]
+
+ def update(self, txt):
+ return self._update(txt, False)
+
+ def finish(self, txt=None):
+ return self._update(txt, True)
+
+def bcrypt(passwd, rng_obj, work_factor=10):
+ """
+ Bcrypt password hashing
+ """
+ out_len = c_size_t(64)
+ out = create_string_buffer(out_len.value)
+ flags = c_uint32(0)
+ _DLL.botan_bcrypt_generate(out, byref(out_len), _ctype_str(passwd),
+ rng_obj.handle_(), c_size_t(work_factor), flags)
+ b = out.raw[0:int(out_len.value)-1]
+ if b[-1] == '\x00':
+ b = b[:-1]
+ return _ctype_to_str(b)
+
+def check_bcrypt(passwd, passwd_hash):
+ rc = _DLL.botan_bcrypt_is_valid(_ctype_str(passwd), _ctype_str(passwd_hash))
+ return rc == 0
+
+#
+# PBKDF
+#
+def pbkdf(algo, password, out_len, iterations=10000, salt=None):
+ if salt is None:
+ salt = RandomNumberGenerator().get(12)
+
+ out_buf = create_string_buffer(out_len)
+
+ _DLL.botan_pwdhash(_ctype_str(algo), iterations, 0, 0,
+ out_buf, out_len,
+ _ctype_str(password), len(password),
+ salt, len(salt))
+ return (salt, iterations, out_buf.raw)
+
+def pbkdf_timed(algo, password, out_len, ms_to_run=300, salt=None):
+ if salt is None:
+ salt = RandomNumberGenerator().get(12)
+
+ out_buf = create_string_buffer(out_len)
+ iterations = c_size_t(0)
+
+ _DLL.botan_pwdhash_timed(_ctype_str(algo), c_uint32(ms_to_run),
+ byref(iterations), None, None,
+ out_buf, out_len,
+ _ctype_str(password), len(password),
+ salt, len(salt))
+ return (salt, iterations.value, out_buf.raw)
+
+#
+# Scrypt
+#
+def scrypt(out_len, password, salt, n=1024, r=8, p=8):
+ out_buf = create_string_buffer(out_len)
+ _DLL.botan_pwdhash(_ctype_str("Scrypt"), n, r, p,
+ out_buf, out_len,
+ _ctype_str(password), len(password),
+ _ctype_bits(salt), len(salt))
+
+ return out_buf.raw
+
+#
+# KDF
+#
+def kdf(algo, secret, out_len, salt, label):
+ out_buf = create_string_buffer(out_len)
+ out_sz = c_size_t(out_len)
+ _DLL.botan_kdf(_ctype_str(algo), out_buf, out_sz,
+ secret, len(secret),
+ salt, len(salt),
+ label, len(label))
+ return out_buf.raw[0:int(out_sz.value)]
+
+#
+# Public key
+#
+class PublicKey(object): # pylint: disable=invalid-name
+
+ def __init__(self, obj=c_void_p(0)):
+ self.__obj = obj
+
+ @classmethod
+ def load(cls, val):
+ obj = c_void_p(0)
+ _DLL.botan_pubkey_load(byref(obj), _ctype_bits(val), len(val))
+ return PublicKey(obj)
+
+ @classmethod
+ def load_rsa(cls, n, e):
+ obj = c_void_p(0)
+ n = MPI(n)
+ e = MPI(e)
+ _DLL.botan_pubkey_load_rsa(byref(obj), n.handle_(), e.handle_())
+ return PublicKey(obj)
+
+ @classmethod
+ def load_dsa(cls, p, q, g, y):
+ obj = c_void_p(0)
+ 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)
+
+ @classmethod
+ def load_dh(cls, p, g, y):
+ obj = c_void_p(0)
+ 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)
+
+ @classmethod
+ def load_elgamal(cls, p, q, g, y):
+ obj = c_void_p(0)
+ 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)
+
+ @classmethod
+ def load_ecdsa(cls, curve, pub_x, pub_y):
+ obj = c_void_p(0)
+ 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)
+
+ @classmethod
+ def load_ecdh(cls, curve, pub_x, pub_y):
+ obj = c_void_p(0)
+ 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)
+
+ @classmethod
+ def load_sm2(cls, curve, pub_x, pub_y):
+ obj = c_void_p(0)
+ 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)
+
+ def __del__(self):
+ _DLL.botan_pubkey_destroy(self.__obj)
+
+ def handle_(self):
+ return self.__obj
+
+ def check_key(self, rng_obj, strong=True):
+ 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):
+ r = c_size_t(0)
+ _DLL.botan_pubkey_estimated_strength(self.__obj, byref(r))
+ return r.value
+
+ def algo_name(self):
+ return _call_fn_returning_str(32, lambda b, bl: _DLL.botan_pubkey_algo_name(self.__obj, b, bl))
+
+ def export(self, pem=False):
+ if pem:
+ return _call_fn_returning_str(4096, lambda b, bl: _DLL.botan_pubkey_export(self.__obj, b, bl, 1))
+ else:
+ return _call_fn_returning_vec(4096, lambda b, bl: _DLL.botan_pubkey_export(self.__obj, b, bl, 0))
+
+ def encoding(self, pem=False):
+ return self.export(pem)
+
+ def to_der(self):
+ return self.export(False)
+
+ def to_pem(self):
+ return self.export(True)
+
+ def fingerprint(self, hash_algorithm='SHA-256'):
+ n = HashFunction(hash_algorithm).output_length()
+ buf = create_string_buffer(n)
+ buf_len = c_size_t(n)
+
+ _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):
+ v = MPI()
+ _DLL.botan_pubkey_get_field(v.handle_(), self.__obj, _ctype_str(field_name))
+ return int(v)
+
+#
+# Private Key
+#
+class PrivateKey(object):
+
+ def __init__(self, obj=c_void_p(0)):
+ self.__obj = obj
+
+ @classmethod
+ def load(cls, val, passphrase=""):
+ obj = c_void_p(0)
+ rng_obj = c_void_p(0) # unused in recent versions
+ _DLL.botan_privkey_load(byref(obj), rng_obj, _ctype_bits(val), len(val), _ctype_str(passphrase))
+ return PrivateKey(obj)
+
+ @classmethod
+ def create(cls, algo, params, rng_obj):
+ if algo == 'rsa':
+ algo = 'RSA'
+ params = "%d" % (params)
+ elif algo == 'ecdsa':
+ algo = 'ECDSA'
+ elif algo in ['ecdh', 'ECDH']:
+ if params == 'curve25519':
+ algo = 'Curve25519'
+ params = ''
+ else:
+ algo = 'ECDH'
+ elif algo in ['mce', 'mceliece']:
+ algo = 'McEliece'
+ params = "%d,%d" % (params[0], params[1])
+
+ obj = c_void_p(0)
+ _DLL.botan_privkey_create(byref(obj), _ctype_str(algo), _ctype_str(params), rng_obj.handle_())
+ return PrivateKey(obj)
+
+ @classmethod
+ def load_rsa(cls, p, q, e):
+ obj = c_void_p(0)
+ 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)
+
+ @classmethod
+ def load_dsa(cls, p, q, g, x):
+ obj = c_void_p(0)
+ 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)
+
+ @classmethod
+ def load_dh(cls, p, g, x):
+ obj = c_void_p(0)
+ 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)
+
+ @classmethod
+ def load_elgamal(cls, p, q, g, x):
+ obj = c_void_p(0)
+ 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)
+
+ @classmethod
+ def load_ecdsa(cls, curve, x):
+ obj = c_void_p(0)
+ x = MPI(x)
+ _DLL.botan_privkey_load_ecdsa(byref(obj), x.handle_(), _ctype_str(curve))
+ return PrivateKey(obj)
+
+ @classmethod
+ def load_ecdh(cls, curve, x):
+ obj = c_void_p(0)
+ x = MPI(x)
+ _DLL.botan_privkey_load_ecdh(byref(obj), x.handle_(), _ctype_str(curve))
+ return PrivateKey(obj)
+
+ @classmethod
+ def load_sm2(cls, curve, x):
+ obj = c_void_p(0)
+ x = MPI(x)
+ _DLL.botan_privkey_load_sm2(byref(obj), x.handle_(), _ctype_str(curve))
+ return PrivateKey(obj)
+
+ def __del__(self):
+ _DLL.botan_privkey_destroy(self.__obj)
+
+ def handle_(self):
+ return self.__obj
+
+ def check_key(self, rng_obj, strong=True):
+ 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):
+ 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 to_der(self):
+ return self.export(False)
+
+ def to_pem(self):
+ return self.export(True)
+
+ def export(self, pem=False):
+ if pem:
+ return _call_fn_returning_str(4096, lambda b, bl: _DLL.botan_privkey_export(self.__obj, b, bl, 1))
+ else:
+ return _call_fn_returning_vec(4096, lambda b, bl: _DLL.botan_privkey_export(self.__obj, b, bl, 0))
+
+ def export_encrypted(self, passphrase, rng_obj, pem=False, msec=300, cipher=None, pbkdf=None): # pylint: disable=redefined-outer-name
+ flags = 1 if pem else 0
+ msec = c_uint32(msec)
+ _iters = c_size_t(0)
+
+ cb = lambda b, bl: _DLL.botan_privkey_export_encrypted_pbkdf_msec(
+ self.__obj, b, bl, rng_obj.handle_(), _ctype_str(passphrase),
+ msec, byref(_iters), _ctype_str(cipher), _ctype_str(pbkdf), flags)
+
+ if pem:
+ return _call_fn_returning_str(8192, cb)
+ else:
+ return _call_fn_returning_vec(4096, cb)
+
+ def get_field(self, field_name):
+ v = MPI()
+ _DLL.botan_privkey_get_field(v.handle_(), self.__obj, _ctype_str(field_name))
+ return int(v)
+
+class PKEncrypt(object):
+ def __init__(self, key, padding):
+ 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)
+
+ def __del__(self):
+ _DLL.botan_pk_op_encrypt_destroy(self.__obj)
+
+ def encrypt(self, msg, rng_obj):
+ 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)
+ _DLL.botan_pk_op_encrypt(self.__obj, rng_obj.handle_(), outbuf, byref(outbuf_sz), msg, len(msg))
+ return outbuf.raw[0:int(outbuf_sz.value)]
+
+
+class PKDecrypt(object):
+ def __init__(self, key, padding):
+ 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)
+
+ def __del__(self):
+ _DLL.botan_pk_op_decrypt_destroy(self.__obj)
+
+ def decrypt(self, msg):
+ 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)
+ _DLL.botan_pk_op_decrypt(self.__obj, outbuf, byref(outbuf_sz), _ctype_bits(msg), len(msg))
+ return outbuf.raw[0:int(outbuf_sz.value)]
+
+class PKSign(object): # pylint: disable=invalid-name
+ def __init__(self, key, padding, der=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)
+
+ 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 finish(self, rng_obj):
+ 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)
+ _DLL.botan_pk_op_sign_finish(self.__obj, rng_obj.handle_(), outbuf, byref(outbuf_sz))
+ return outbuf.raw[0:int(outbuf_sz.value)]
+
+class PKVerify(object):
+ def __init__(self, key, padding, der=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)
+
+ def __del__(self):
+ _DLL.botan_pk_op_verify_destroy(self.__obj)
+
+ def update(self, msg):
+ _DLL.botan_pk_op_verify_update(self.__obj, _ctype_bits(msg), len(msg))
+
+ def check_signature(self, signature):
+ rc = _DLL.botan_pk_op_verify_finish(self.__obj, _ctype_bits(signature), len(signature))
+ if rc == 0:
+ return True
+ return False
+
+class PKKeyAgreement(object):
+ def __init__(self, key, kdf_name):
+ 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)
+
+ self.m_public_value = _call_fn_returning_vec(
+ 0, lambda b, bl: _DLL.botan_pk_op_key_agreement_export_public(key.handle_(), b, bl))
+
+ def __del__(self):
+ _DLL.botan_pk_op_key_agreement_destroy(self.__obj)
+
+ def public_value(self):
+ return self.m_public_value
+
+ def underlying_output_length(self):
+ 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):
+ if key_len == 0:
+ key_len = self.underlying_output_length()
+ return _call_fn_returning_vec(key_len, lambda b, bl:
+ _DLL.botan_pk_op_key_agreement(self.__obj, b, bl,
+ other, len(other),
+ salt, len(salt)))
+
+#
+# MCEIES encryption
+# Must be used with McEliece keys
+#
+def mceies_encrypt(mce, rng_obj, aead, pt, ad):
+ return _call_fn_returning_vec(len(pt) + 1024, lambda b, bl:
+ _DLL.botan_mceies_encrypt(mce.handle_(),
+ rng_obj.handle_(),
+ _ctype_str(aead),
+ _ctype_bits(pt),
+ len(pt),
+ _ctype_bits(ad),
+ len(ad),
+ b, bl))
+
+def mceies_decrypt(mce, aead, ct, ad):
+
+ #msg = cast(msg, c_char_p)
+ #ll = c_size_t(ll)
+
+ return _call_fn_returning_vec(len(ct), lambda b, bl:
+ _DLL.botan_mceies_decrypt(mce.handle_(),
+ _ctype_str(aead),
+ _ctype_bits(ct),
+ len(ct),
+ _ctype_bits(ad),
+ len(ad),
+ b, bl))
+
+
+def _load_buf_or_file(filename, buf, file_fn, buf_fn):
+ if filename is None and buf is None:
+ raise BotanException("No filename or buf given")
+ if filename is not None and buf is not None:
+ raise BotanException("Both filename and buf given")
+
+ obj = c_void_p(0)
+
+ if filename is not None:
+ file_fn(byref(obj), _ctype_str(filename))
+ elif buf is not None:
+ buf_fn(byref(obj), _ctype_bits(buf), len(buf))
+
+ return obj
+
+
+#
+# X.509 certificates
+#
+class X509Cert(object): # pylint: disable=invalid-name
+ def __init__(self, filename=None, buf=None):
+ 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):
+ starts = _call_fn_returning_str(
+ 16, lambda b, bl: _DLL.botan_x509_cert_get_time_starts(self.__obj, b, bl))
+ if len(starts) == 13:
+ # UTC time
+ struct_time = strptime(starts, "%y%m%d%H%M%SZ")
+ elif len(starts) == 15:
+ # Generalized time
+ struct_time = strptime(starts, "%Y%m%d%H%M%SZ")
+ else:
+ raise BotanException("Unexpected date/time format for x509 start time")
+
+ return datetime.fromtimestamp(mktime(struct_time))
+
+ def time_expires(self):
+ expires = _call_fn_returning_str(
+ 16, lambda b, bl: _DLL.botan_x509_cert_get_time_expires(self.__obj, b, bl))
+ if len(expires) == 13:
+ # UTC time
+ struct_time = strptime(expires, "%y%m%d%H%M%SZ")
+ elif len(expires) == 15:
+ # Generalized time
+ struct_time = strptime(expires, "%Y%m%d%H%M%SZ")
+ else:
+ raise BotanException("Unexpected date/time format for x509 expire time")
+
+ return datetime.fromtimestamp(mktime(struct_time))
+
+ def to_string(self):
+ return _call_fn_returning_str(
+ 4096, lambda b, bl: _DLL.botan_x509_cert_to_string(self.__obj, b, bl))
+
+ def fingerprint(self, hash_algo='SHA-256'):
+ 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):
+ 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):
+ 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):
+ 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):
+ return _call_fn_returning_vec(
+ 512, lambda b, bl: _DLL.botan_x509_cert_get_public_key_bits(self.__obj, b, bl))
+
+ def subject_public_key(self):
+ 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):
+ 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):
+ 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):
+ rc = _DLL.botan_x509_cert_hostname_match(self.__obj, _ctype_str(hostname))
+ return rc == 0
+
+ def not_before(self):
+ time = c_uint64(0)
+ _DLL.botan_x509_cert_not_before(self.__obj, byref(time))
+ return time.value
+
+ def not_after(self):
+ time = c_uint64(0)
+ _DLL.botan_x509_cert_not_after(self.__obj, byref(time))
+ return time.value
+
+ def allowed_usage(self, usage_list):
+ usage_values = {"NO_CONSTRAINTS": 0,
+ "DIGITAL_SIGNATURE": 32768,
+ "NON_REPUDIATION": 16384,
+ "KEY_ENCIPHERMENT": 8192,
+ "DATA_ENCIPHERMENT": 4096,
+ "KEY_AGREEMENT": 2048,
+ "KEY_CERT_SIGN": 1024,
+ "CRL_SIGN": 512,
+ "ENCIPHER_ONLY": 256,
+ "DECIPHER_ONLY": 128}
+ usage = 0
+ for u in usage_list:
+ if u not in usage_values:
+ return False
+ usage += usage_values[u]
+
+ rc = _DLL.botan_x509_cert_allowed_usage(self.__obj, c_uint(usage))
+ return rc == 0
+
+ def handle_(self):
+ return self.__obj
+
+ def verify(self,
+ intermediates=None,
+ trusted=None,
+ trusted_path=None,
+ required_strength=0,
+ hostname=None,
+ reference_time=0,
+ crls=None):
+ #pylint: disable=too-many-locals
+
+ if intermediates is not None:
+ c_intermediates = len(intermediates) * c_void_p
+ arr_intermediates = c_intermediates()
+ for i, ca in enumerate(intermediates):
+ arr_intermediates[i] = ca.handle_()
+ len_intermediates = c_size_t(len(intermediates))
+ else:
+ arr_intermediates = c_void_p(0)
+ len_intermediates = c_size_t(0)
+
+ if trusted is not None:
+ c_trusted = len(trusted) * c_void_p
+ arr_trusted = c_trusted()
+ for i, ca in enumerate(trusted):
+ arr_trusted[i] = ca.handle_()
+ len_trusted = c_size_t(len(trusted))
+ else:
+ arr_trusted = c_void_p(0)
+ len_trusted = c_size_t(0)
+
+ if crls is not None:
+ c_crls = len(crls) * c_void_p
+ arr_crls = c_crls()
+ for i, crl in enumerate(crls):
+ arr_crls[i] = crl.handle_()
+ len_crls = c_size_t(len(crls))
+ else:
+ arr_crls = c_void_p(0)
+ len_crls = c_size_t(0)
+
+ error_code = c_int(0)
+
+ _DLL.botan_x509_cert_verify_with_crl(byref(error_code),
+ self.__obj,
+ byref(arr_intermediates),
+ len_intermediates,
+ byref(arr_trusted),
+ len_trusted,
+ byref(arr_crls),
+ len_crls,
+ _ctype_str(trusted_path),
+ c_size_t(required_strength),
+ _ctype_str(hostname),
+ c_uint64(reference_time))
+
+ return error_code.value
+
+ @classmethod
+ def validation_status(cls, error_code):
+ return _ctype_to_str(_DLL.botan_x509_cert_validation_status(c_int(error_code)))
+
+ def is_revoked(self, crl):
+ rc = _DLL.botan_x509_is_revoked(crl.handle_(), self.__obj)
+ return rc == 0
+
+
+#
+# X.509 Certificate revocation lists
+#
+class X509CRL(object):
+ def __init__(self, filename=None, buf=None):
+ 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)
+
+ def handle_(self):
+ return self.__obj
+
+
+class MPI(object): # pylint: disable=too-many-public-methods
+
+ def __init__(self, initial_value=None, radix=None):
+
+ self.__obj = c_void_p(0)
+ _DLL.botan_mp_init(byref(self.__obj))
+
+ if initial_value is None:
+ pass # left as zero
+ elif isinstance(initial_value, MPI):
+ _DLL.botan_mp_set_from_mp(self.__obj, initial_value.handle_())
+ elif radix is not None:
+ _DLL.botan_mp_set_from_radix_str(self.__obj, _ctype_str(initial_value), c_size_t(radix))
+ elif isinstance(initial_value, str):
+ _DLL.botan_mp_set_from_str(self.__obj, _ctype_str(initial_value))
+ else:
+ # For int or long (or whatever else), try converting to string:
+ _DLL.botan_mp_set_from_str(self.__obj, _ctype_str(str(initial_value)))
+
+ @classmethod
+ def random(cls, rng_obj, bits):
+ 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):
+ bn = MPI()
+ _DLL.botan_mp_rand_range(bn.handle_(), rng_obj.handle_(), lower.handle_(), upper.handle_())
+ return bn
+
+ def __del__(self):
+ _DLL.botan_mp_destroy(self.__obj)
+
+ def handle_(self):
+ return self.__obj
+
+ def __int__(self):
+ out = create_string_buffer(2*self.byte_count() + 1)
+ _DLL.botan_mp_to_hex(self.__obj, out)
+ val = int(out.value, 16)
+ if self.is_negative():
+ return -val
+ else:
+ return val
+
+ 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
+
+ def to_bytes(self):
+ byte_count = self.byte_count()
+ out_len = c_size_t(byte_count)
+ out = create_string_buffer(out_len.value)
+ _DLL.botan_mp_to_bin(self.__obj, out, byref(out_len))
+ assert out_len.value == byte_count
+ return out
+
+ def is_negative(self):
+ rc = _DLL.botan_mp_is_negative(self.__obj)
+ return rc == 1
+
+ def is_positive(self):
+ rc = _DLL.botan_mp_is_positive(self.__obj)
+ return rc == 1
+
+ def is_zero(self):
+ rc = _DLL.botan_mp_is_zero(self.__obj)
+ return rc == 1
+
+ def is_odd(self):
+ return self.get_bit(0) == 1
+
+ def is_even(self):
+ return self.get_bit(0) == 0
+
+ def flip_sign(self):
+ _DLL.botan_mp_flip_sign(self.__obj)
+
+ def cmp(self, other):
+ r = c_int(0)
+ _DLL.botan_mp_cmp(byref(r), self.__obj, other.handle_())
+ return r.value
+
+ def __hash__(self):
+ return hash(self.to_bytes())
+
+ def __eq__(self, other):
+ return self.cmp(other) == 0
+
+ def __ne__(self, other):
+ return self.cmp(other) != 0
+
+ def __lt__(self, other):
+ return self.cmp(other) < 0
+
+ def __le__(self, other):
+ return self.cmp(other) <= 0
+
+ def __gt__(self, other):
+ return self.cmp(other) > 0
+
+ def __ge__(self, other):
+ return self.cmp(other) >= 0
+
+ def __add__(self, other):
+ r = MPI()
+ _DLL.botan_mp_add(r.handle_(), self.__obj, other.handle_())
+ return r
+
+ def __iadd__(self, other):
+ _DLL.botan_mp_add(self.__obj, self.__obj, other.handle_())
+ return self
+
+ def __sub__(self, other):
+ r = MPI()
+ _DLL.botan_mp_sub(r.handle_(), self.__obj, other.handle_())
+ return r
+
+ def __isub__(self, other):
+ _DLL.botan_mp_sub(self.__obj, self.__obj, other.handle_())
+ return self
+
+ def __mul__(self, other):
+ r = MPI()
+ _DLL.botan_mp_mul(r.handle_(), self.__obj, other.handle_())
+ return r
+
+ def __imul__(self, other):
+ _DLL.botan_mp_mul(self.__obj, self.__obj, other.handle_())
+ return self
+
+ def __divmod__(self, other):
+ d = MPI()
+ q = MPI()
+ _DLL.botan_mp_div(d.handle_(), q.handle_(), self.__obj, other.handle_())
+ return (d, q)
+
+ def __mod__(self, other):
+ 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)
+ r = MPI()
+ _DLL.botan_mp_lshift(r.handle_(), self.__obj, shift)
+ return r
+
+ def __ilshift__(self, shift):
+ shift = c_size_t(shift)
+ _DLL.botan_mp_lshift(self.__obj, self.__obj, shift)
+ return self
+
+ def __rshift__(self, shift):
+ shift = c_size_t(shift)
+ r = MPI()
+ _DLL.botan_mp_rshift(r.handle_(), self.__obj, shift)
+ return r
+
+ def __irshift__(self, shift):
+ shift = c_size_t(shift)
+ _DLL.botan_mp_rshift(self.__obj, self.__obj, shift)
+ return self
+
+ def mod_mul(self, other, modulus):
+ r = MPI()
+ _DLL.botan_mp_mod_mul(r.handle_(), self.__obj, other.handle_(), modulus.handle_())
+ return r
+
+ def gcd(self, other):
+ r = MPI()
+ _DLL.botan_mp_gcd(r.handle_(), self.__obj, other.handle_())
+ return r
+
+ def pow_mod(self, exponent, 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):
+ return _DLL.botan_mp_is_prime(self.__obj, rng_obj.handle_(), c_size_t(prob)) == 1
+
+ def inverse_mod(self, modulus):
+ r = MPI()
+ _DLL.botan_mp_mod_inverse(r.handle_(), self.__obj, modulus.handle_())
+ return r
+
+ def bit_count(self):
+ b = c_size_t(0)
+ _DLL.botan_mp_num_bits(self.__obj, byref(b))
+ return b.value
+
+ def byte_count(self):
+ b = c_size_t(0)
+ _DLL.botan_mp_num_bytes(self.__obj, byref(b))
+ return b.value
+
+ def get_bit(self, bit):
+ return _DLL.botan_mp_get_bit(self.__obj, c_size_t(bit)) == 1
+
+ def clear_bit(self, bit):
+ _DLL.botan_mp_clear_bit(self.__obj, c_size_t(bit))
+
+ def set_bit(self, bit):
+ _DLL.botan_mp_set_bit(self.__obj, c_size_t(bit))
+
+class FormatPreservingEncryptionFE1(object):
+
+ def __init__(self, modulus, key, rounds=5, compat_mode=False):
+ 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)
+
+ def __del__(self):
+ _DLL.botan_fpe_destroy(self.__obj)
+
+ def encrypt(self, msg, tweak):
+ r = MPI(msg)
+ _DLL.botan_fpe_encrypt(self.__obj, r.handle_(), _ctype_bits(tweak), len(tweak))
+ return r
+
+ def decrypt(self, msg, tweak):
+ r = MPI(msg)
+ _DLL.botan_fpe_decrypt(self.__obj, r.handle_(), _ctype_bits(tweak), len(tweak))
+ return r
+
+class HOTP(object):
+ def __init__(self, key, digest="SHA-1", digits=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):
+ code = c_uint32(0)
+ _DLL.botan_hotp_generate(self.__obj, byref(code), counter)
+ return code.value
+
+ def check(self, code, counter, resync_range=0):
+ next_ctr = c_uint64(0)
+ rc = _DLL.botan_hotp_check(self.__obj, byref(next_ctr), code, counter, resync_range)
+ if rc == 0:
+ return (True, next_ctr.value)
+ else:
+ return (False, counter)
+
+class TOTP(object):
+ def __init__(self, key, digest="SHA-1", digits=6, timestep=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):
+ 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):
+ if timestamp is None:
+ timestamp = int(system_time())
+ rc = _DLL.botan_totp_check(self.__obj, code, timestamp, acceptable_drift)
+ if rc == 0:
+ return True
+ return False
+
+def nist_key_wrap(kek, key):
+ output = create_string_buffer(len(key) + 8)
+ out_len = c_size_t(len(output))
+ _DLL.botan_key_wrap3394(key, len(key), kek, len(kek), output, byref(out_len))
+ return output[0:int(out_len.value)]
+
+def nist_key_unwrap(kek, wrapped):
+ output = create_string_buffer(len(wrapped))
+ out_len = c_size_t(len(output))
+ _DLL.botan_key_unwrap3394(wrapped, len(wrapped), kek, len(kek), output, byref(out_len))
+ return output[0:int(out_len.value)]
+
+# Typedefs for compat with older versions
+# Will be removed in a future major release
+cipher = SymmetricCipher # pylint: disable=invalid-name
+rng = RandomNumberGenerator # pylint: disable=invalid-name
+hash_function = HashFunction # pylint: disable=invalid-name
+message_authentication_code = MsgAuthCode # pylint: disable=invalid-name
+
+x509_cert = X509Cert # pylint: disable=invalid-name
+public_key = PublicKey # pylint: disable=invalid-name
+private_key = PrivateKey # pylint: disable=invalid-name
+
+pk_op_encrypt = PKEncrypt # pylint: disable=invalid-name
+pk_op_decrypt = PKDecrypt # pylint: disable=invalid-name
+pk_op_sign = PKSign # pylint: disable=invalid-name
+pk_op_verify = PKVerify # pylint: disable=invalid-name
+pk_op_key_agreement = PKKeyAgreement # pylint: disable=invalid-name
diff --git a/comm/third_party/botan/src/scripts/Dockerfile.android b/comm/third_party/botan/src/scripts/Dockerfile.android
new file mode 100644
index 0000000000..124d5d4f6d
--- /dev/null
+++ b/comm/third_party/botan/src/scripts/Dockerfile.android
@@ -0,0 +1,17 @@
+FROM devnexen/android-ndk:r20 AS android-ndk
+ARG ANDROID_ARCH
+ARG ANDROID_TOOLCHAIN_SUF
+ARG ANDROID_ARCH_SUF
+ARG ANDROID_SDK_VER
+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
+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 --git a/comm/third_party/botan/src/scripts/bench.py b/comm/third_party/botan/src/scripts/bench.py
new file mode 100755
index 0000000000..1cc626366f
--- /dev/null
+++ b/comm/third_party/botan/src/scripts/bench.py
@@ -0,0 +1,216 @@
+#!/usr/bin/python
+
+"""
+Compare Botan with OpenSSL using their respective benchmark utils
+
+(C) 2017 Jack Lloyd
+
+Botan is released under the Simplified BSD License (see license.txt)
+
+TODO
+ - Also compare RSA, ECDSA, ECDH
+ - Output pretty graphs with matplotlib
+"""
+
+import logging
+import os
+import sys
+import optparse # pylint: disable=deprecated-module
+import subprocess
+import re
+import json
+
+def setup_logging(options):
+ if options.verbose:
+ log_level = logging.DEBUG
+ elif options.quiet:
+ log_level = logging.WARNING
+ else:
+ log_level = logging.INFO
+
+ class LogOnErrorHandler(logging.StreamHandler, object):
+ def emit(self, record):
+ super(LogOnErrorHandler, self).emit(record)
+ if record.levelno >= logging.ERROR:
+ sys.exit(1)
+
+ lh = LogOnErrorHandler(sys.stdout)
+ lh.setFormatter(logging.Formatter('%(levelname) 7s: %(message)s'))
+ logging.getLogger().addHandler(lh)
+ logging.getLogger().setLevel(log_level)
+
+def run_command(cmd):
+ logging.debug("Running '%s'", ' '.join(cmd))
+
+ proc = subprocess.Popen(cmd,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ universal_newlines=True)
+ stdout, stderr = proc.communicate()
+
+ if proc.returncode != 0:
+ logging.error("Running command %s failed ret %d", ' '.join(cmd), proc.returncode)
+
+ return stdout + stderr
+
+def get_openssl_version(openssl):
+ output = run_command([openssl, 'version'])
+
+ openssl_version_re = re.compile(r'OpenSSL ([0-9a-z\.]+) .*')
+
+ match = openssl_version_re.match(output)
+
+ if match:
+ return match.group(1)
+ else:
+ logging.warning("Unable to parse OpenSSL version output %s", output)
+ return output
+
+def get_botan_version(botan):
+ return run_command([botan, 'version']).strip()
+
+EVP_MAP = {
+ 'Blowfish': 'bf-ecb',
+ 'AES-128/GCM': 'aes-128-gcm',
+ 'AES-256/GCM': 'aes-256-gcm',
+ 'ChaCha20': 'chacha20',
+ 'MD5': 'md5',
+ 'SHA-1': 'sha1',
+ 'RIPEMD-160': 'ripemd160',
+ 'SHA-256': 'sha256',
+ 'SHA-384': 'sha384',
+ 'SHA-512': 'sha512'
+ }
+
+def run_openssl_bench(openssl, algo):
+
+ logging.info('Running OpenSSL benchmark for %s', algo)
+
+ cmd = [openssl, 'speed', '-mr']
+
+ if algo in EVP_MAP:
+ cmd += ['-evp', EVP_MAP[algo]]
+ else:
+ cmd += [algo]
+
+ output = run_command(cmd)
+
+ buf_header = re.compile(r'\+DT:([a-z0-9-]+):([0-9]+):([0-9]+)$')
+ res_header = re.compile(r'\+R:([0-9]+):[a-z0-9-]+:([0-9]+\.[0-9]+)$')
+ ignored = re.compile(r'\+(H|F):.*')
+
+ results = []
+
+ result = None
+
+ for l in output.splitlines():
+ if ignored.match(l):
+ continue
+
+ if result is None:
+ match = buf_header.match(l)
+ if match is None:
+ logging.error("Unexpected output from OpenSSL %s", l)
+
+ result = {'algo': algo, 'buf_size': int(match.group(3))}
+ else:
+ match = res_header.match(l)
+
+ result['bytes'] = int(match.group(1)) * result['buf_size']
+ result['runtime'] = float(match.group(2))
+ result['bps'] = int(result['bytes'] / result['runtime'])
+ results.append(result)
+ result = None
+
+ return results
+
+def run_botan_bench(botan, runtime, buf_sizes, algo):
+
+ runtime = .05
+
+ cmd = [botan, 'speed', '--format=json', '--msec=%d' % int(runtime * 1000),
+ '--buf-size=%s' % (','.join([str(i) for i in buf_sizes])), algo]
+ output = run_command(cmd)
+ output = json.loads(output)
+
+ return output
+
+class BenchmarkResult(object):
+ def __init__(self, algo, buf_sizes, openssl_results, botan_results):
+ self.algo = algo
+ self.results = {}
+
+ def find_result(results, sz):
+ for r in results:
+ if 'buf_size' in r and r['buf_size'] == sz:
+ return r['bps']
+ raise Exception("Could not find expected result in data")
+
+ for buf_size in buf_sizes:
+ self.results[buf_size] = {
+ 'openssl': find_result(openssl_results, buf_size),
+ 'botan': find_result(botan_results, buf_size)
+ }
+
+ def result_string(self):
+
+ out = ""
+ for (k, v) in self.results.items():
+ out += "algo %s buf_size % 6d botan % 12d bps openssl % 12d bps adv %.02f\n" % (
+ self.algo, k, v['botan'], v['openssl'], float(v['botan']) / v['openssl'])
+ return out
+
+def bench_algo(openssl, botan, algo):
+ openssl_results = run_openssl_bench(openssl, algo)
+
+ buf_sizes = sorted([x['buf_size'] for x in openssl_results])
+ runtime = sum(x['runtime'] for x in openssl_results) / len(openssl_results)
+
+ botan_results = run_botan_bench(botan, runtime, buf_sizes, algo)
+
+ return BenchmarkResult(algo, buf_sizes, openssl_results, botan_results)
+
+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('--quiet', action='store_true', default=False, help="be very quiet")
+
+ parser.add_option('--openssl-cli', metavar='PATH',
+ default='/usr/bin/openssl',
+ help='Path to openssl binary (default %default)')
+
+ parser.add_option('--botan-cli', metavar='PATH',
+ default='/usr/bin/botan',
+ help='Path to botan binary (default %default)')
+
+ (options, args) = parser.parse_args(args)
+
+ setup_logging(options)
+
+ openssl = options.openssl_cli
+ botan = options.botan_cli
+
+ if os.access(openssl, os.X_OK) is False:
+ logging.error("Unable to access openssl binary at %s", openssl)
+
+ if os.access(botan, os.X_OK) is False:
+ logging.error("Unable to access botan binary at %s", botan)
+
+ openssl_version = get_openssl_version(openssl)
+ botan_version = get_botan_version(botan)
+
+ logging.info("Comparing Botan %s with OpenSSL %s", botan_version, openssl_version)
+
+ for algo in sorted(EVP_MAP.keys()):
+ result = bench_algo(openssl, botan, algo)
+ print(result.result_string())
+
+
+ return 0
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/comm/third_party/botan/src/scripts/build_docs.py b/comm/third_party/botan/src/scripts/build_docs.py
new file mode 100755
index 0000000000..6eb9b656c9
--- /dev/null
+++ b/comm/third_party/botan/src/scripts/build_docs.py
@@ -0,0 +1,242 @@
+#!/usr/bin/env python
+
+"""
+Botan doc generation script
+
+(C) 2014,2015,2017 Jack Lloyd
+
+Botan is released under the Simplified BSD License (see license.txt)
+"""
+
+import sys
+import optparse # pylint: disable=deprecated-module
+import subprocess
+import shutil
+import logging
+import json
+import tempfile
+import os
+import stat
+
+def get_concurrency():
+ """
+ Get default concurrency level of build
+ """
+ def_concurrency = 2
+
+ try:
+ import multiprocessing
+ return max(def_concurrency, multiprocessing.cpu_count())
+ except ImportError:
+ return def_concurrency
+
+def have_prog(prog):
+ """
+ Check if some named program exists in the path
+ """
+ for path in os.environ['PATH'].split(os.pathsep):
+ exe_file = os.path.join(path, prog)
+ if os.path.exists(exe_file) and os.access(exe_file, os.X_OK):
+ return True
+ return False
+
+def find_rst2man():
+ possible_names = ['rst2man', 'rst2man.py']
+
+ for name in possible_names:
+ if have_prog(name):
+ return name
+ raise Exception("Was configured with rst2man but could not be located in PATH")
+
+def touch(fname):
+ try:
+ os.utime(fname, None)
+ except OSError:
+ open(fname, 'a').close()
+
+def copy_files(src_path, dest_dir):
+
+ logging.debug("Copying %s to %s", src_path, dest_dir)
+
+ file_mode = os.stat(src_path).st_mode
+
+ try:
+ os.mkdir(dest_dir)
+ except OSError:
+ pass
+
+ if stat.S_ISREG(file_mode):
+ logging.debug("Copying file %s to %s", src_path, dest_dir)
+ shutil.copy(src_path, dest_dir)
+ else:
+ for f in os.listdir(src_path):
+ src_file = os.path.join(src_path, f)
+ file_mode = os.stat(src_file).st_mode
+ if stat.S_ISREG(file_mode):
+ dest_file = os.path.join(dest_dir, f)
+ shutil.copyfile(src_file, dest_file)
+ elif stat.S_ISDIR(file_mode):
+ copy_files(os.path.join(src_path, f), os.path.join(dest_dir, f))
+
+def run_and_check(cmd_line, cwd=None):
+
+ logging.info("Starting %s", ' '.join(cmd_line))
+
+ try:
+ proc = subprocess.Popen(cmd_line, cwd=cwd)
+
+ proc.communicate()
+ except OSError as e:
+ logging.error("Executing %s failed (%s)", ' '.join(cmd_line), e)
+
+ if proc.returncode != 0:
+ logging.error("Error running %s", ' '.join(cmd_line))
+ sys.exit(1)
+
+
+def parse_options(args):
+ parser = optparse.OptionParser()
+
+ parser.add_option('--verbose', action='store_true', default=False,
+ help='Show debug messages')
+ parser.add_option('--quiet', action='store_true', default=False,
+ help='Show only warnings and errors')
+
+ parser.add_option('--build-dir', metavar='DIR', default='build',
+ help='Location of build output (default \'%default\')')
+ parser.add_option('--dry-run', default=False, action='store_true',
+ help='Just display what would be done')
+
+ (options, args) = parser.parse_args(args)
+
+ if len(args) > 1:
+ logging.error("Unknown arguments")
+ return None
+
+ def log_level():
+ if options.verbose:
+ return logging.DEBUG
+ if options.quiet:
+ return logging.WARNING
+ return logging.INFO
+
+ logging.getLogger().setLevel(log_level())
+
+ return options
+
+def sphinx_supports_concurrency():
+ import re
+ from distutils.version import StrictVersion
+
+ proc = subprocess.Popen(['sphinx-build', '--version'],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT)
+ output, _ = proc.communicate()
+ if isinstance(output, bytes):
+ output = output.decode('ascii')
+ output = output.strip()
+
+ # Sphinx v1.1.3
+ # sphinx-build 1.7.4
+ match = re.match(r'^(?:[a-zA-Z_-]+) v?(([0-9]+)\.([0-9]+))', output)
+
+ if match is None:
+ # If regex doesn't match, disable by default
+ logging.warning("Did not recognize sphinx version from '%s'", output)
+ return False
+
+ version = StrictVersion(match.group(1))
+
+ if version < StrictVersion('1.4'):
+ # not supported
+ return False
+ if version == StrictVersion('3.0'):
+ # Bug in Sphinx 3.0 https://github.com/sphinx-doc/sphinx/issues/7438
+ return False
+ return True
+
+def read_config(config):
+ try:
+ f = open(config)
+ cfg = json.load(f)
+ f.close()
+ except OSError:
+ raise Exception('Failed to load build config %s - is build dir correct?' % (config))
+
+ return cfg
+
+def main(args=None):
+ # pylint: disable=too-many-branches
+
+ if args is None:
+ args = sys.argv
+
+ logging.basicConfig(stream=sys.stdout,
+ format='%(levelname) 7s: %(message)s')
+
+ options = parse_options(args)
+
+ if options is None:
+ return 1
+
+ cfg = read_config(os.path.join(options.build_dir, 'build_config.json'))
+
+ with_docs = bool(cfg['with_documentation'])
+ with_sphinx = bool(cfg['with_sphinx'])
+ with_pdf = bool(cfg['with_pdf'])
+ with_rst2man = bool(cfg['with_rst2man'])
+ with_doxygen = bool(cfg['with_doxygen'])
+
+ doc_stamp_file = cfg['doc_stamp_file']
+
+ handbook_src = cfg['doc_dir']
+ handbook_output = cfg['handbook_output_dir']
+
+ if with_docs is False:
+ logging.debug('Documentation build disabled')
+ return 0
+
+ cmds = []
+
+ if with_doxygen:
+ cmds.append(['doxygen', os.path.join(cfg['build_dir'], 'botan.doxy')])
+
+ if with_sphinx:
+ sphinx_build = ['sphinx-build', '-q', '-c', cfg['sphinx_config_dir']]
+ if sphinx_supports_concurrency():
+ sphinx_build += ['-j', str(get_concurrency())]
+
+ cmds.append(sphinx_build + ['-b', 'html', handbook_src, handbook_output])
+
+ if with_pdf:
+ latex_output = tempfile.mkdtemp(prefix='botan_latex_')
+ 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])
+ else:
+ # otherwise just copy it
+ cmds.append(['cp', handbook_src, handbook_output])
+
+ if with_rst2man:
+ cmds.append([find_rst2man(),
+ os.path.join(cfg['build_dir'], 'botan.rst'),
+ os.path.join(cfg['build_dir'], 'botan.1')])
+
+ cmds.append(['touch', doc_stamp_file])
+
+ for cmd in cmds:
+ if options.dry_run:
+ print(' '.join(cmd))
+ else:
+ if cmd[0] == 'cp':
+ assert len(cmd) == 3
+ copy_files(cmd[1], cmd[2])
+ elif cmd[0] == 'touch':
+ assert len(cmd) == 2
+ touch(cmd[1])
+ else:
+ run_and_check(cmd)
+ return 0
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/comm/third_party/botan/src/scripts/check.py b/comm/third_party/botan/src/scripts/check.py
new file mode 100644
index 0000000000..ea80d552d2
--- /dev/null
+++ b/comm/third_party/botan/src/scripts/check.py
@@ -0,0 +1,89 @@
+#!/usr/bin/env python
+
+"""
+Implements the "make check" target
+
+(C) 2020 Jack Lloyd, Rene Meusel
+
+Botan is released under the Simplified BSD License (see license.txt)
+"""
+
+import json
+import logging
+import optparse # pylint: disable=deprecated-module
+import os
+import subprocess
+import sys
+
+def run_and_check(cmd_line, env=None, cwd=None):
+
+ logging.info("Starting %s", ' '.join(cmd_line))
+
+ try:
+ proc = subprocess.Popen(cmd_line, cwd=cwd, env=env)
+ proc.communicate()
+ except OSError as e:
+ logging.error("Executing %s failed (%s)", ' '.join(cmd_line), e)
+
+ if proc.returncode != 0:
+ logging.error("Error running %s", ' '.join(cmd_line))
+ sys.exit(1)
+
+
+def make_environment(build_shared_lib):
+ if not build_shared_lib:
+ return None
+
+ env = os.environ.copy()
+
+ def extend_colon_list(k, n):
+ env[k] = n if k not in env else ":".join([env[k], n])
+
+ extend_colon_list("DYLD_LIBRARY_PATH", os.path.abspath("."))
+ extend_colon_list("LD_LIBRARY_PATH", os.path.abspath("."))
+
+ return env
+
+
+def parse_options(args):
+ parser = optparse.OptionParser()
+ parser.add_option('--build-dir', default='build', metavar='DIR',
+ help='specify the botan build directory (default %default)')
+
+ (options, args) = parser.parse_args(args)
+
+ if len(args) > 1:
+ raise Exception("Unknown arguments")
+
+ return options
+
+
+def read_config(config):
+ try:
+ with open(config) as f:
+ return json.load(f)
+ except OSError:
+ raise Exception('Failed to load build config %s - is build dir correct?' % (config))
+
+
+def main(args=None):
+ if args is None:
+ args = sys.argv
+
+ options = parse_options(args)
+
+ cfg = read_config(os.path.join(options.build_dir, 'build_config.json'))
+
+ test_exe = cfg.get('test_exe')
+ build_shared_lib = cfg.get('build_shared_lib')
+
+ if not os.path.isfile(test_exe) or not os.access(test_exe, os.X_OK):
+ raise Exception("Test binary not built")
+
+ run_and_check([test_exe, "--data-dir=%s" % cfg.get('test_data_dir')],
+ make_environment(build_shared_lib))
+
+ return 0
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/comm/third_party/botan/src/scripts/ci/appveyor.yml b/comm/third_party/botan/src/scripts/ci/appveyor.yml
new file mode 100644
index 0000000000..35345841a4
--- /dev/null
+++ b/comm/third_party/botan/src/scripts/ci/appveyor.yml
@@ -0,0 +1,90 @@
+
+clone_depth: 5
+
+environment:
+ SCCACHE_CACHE_SIZE: 160M
+ SCCACHE_VERSION: 0.2.12
+ APPVEYOR_SAVE_CACHE_ON_ERROR: true
+
+ matrix:
+
+ # MSVC 2015 DLL x86-32
+ - CC: VC2015
+ PLATFORM: x86
+ TARGET: shared
+ APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
+ BOOST_ROOT: "C:\\Libraries\\boost_1_69_0"
+ BOOST_LIBRARYDIR: "C:\\Libraries\\boost_1_69_0\\lib32-msvc-14.0"
+ BOOST_SYSTEM_LIBRARY: "libboost_system-vc140-mt-x32-1_69"
+ MAKE_TOOL: nmake
+ TARGET_CC: msvc
+ EXTRA_FLAGS: "--disable-werror"
+ DISABLED_TESTS: "certstor_system" # requires 'ISRG Root X1' / not in this AppVeyor image
+
+ # MSVC 2017 DLL x86-32
+ - CC: VC2017
+ PLATFORM: x86
+ TARGET: shared
+ APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
+ BOOST_ROOT: "C:\\Libraries\\boost_1_69_0"
+ BOOST_LIBRARYDIR: "C:\\Libraries\\boost_1_69_0\\lib32-msvc-14.1"
+ BOOST_SYSTEM_LIBRARY: "libboost_system-vc141-mt-x32-1_69"
+ MAKE_TOOL: jom
+ TARGET_CC: msvc
+ DISABLED_TESTS: "certstor_system" # requires 'ISRG Root X1' / not in this AppVeyor image
+
+ # MSVC 2017 DLL x86-64
+ - CC: VC2017
+ PLATFORM: x86_amd64
+ TARGET: shared
+ APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
+ BOOST_ROOT: "C:\\Libraries\\boost_1_69_0"
+ BOOST_LIBRARYDIR: "C:\\Libraries\\boost_1_69_0\\lib64-msvc-14.1"
+ BOOST_SYSTEM_LIBRARY: "libboost_system-vc141-mt-x64-1_69"
+ MAKE_TOOL: jom
+ TARGET_CC: msvc
+ DISABLED_TESTS: "certstor_system" # requires 'ISRG Root X1' / not in this AppVeyor image
+
+ # MSVC 2017 static x86-64
+ - CC: VC2017
+ PLATFORM: x86_amd64
+ TARGET: static
+ APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
+ BOOST_ROOT: "C:\\Libraries\\boost_1_69_0"
+ BOOST_LIBRARYDIR: "C:\\Libraries\\boost_1_69_0\\lib64-msvc-14.1"
+ BOOST_SYSTEM_LIBRARY: "libboost_system-vc141-mt-x64-1_69"
+ MAKE_TOOL: jom
+ TARGET_CC: msvc
+ EXTRA_FLAGS: "--extra-cxxflags=/DUNICODE --extra-cxxflags=/D_UNICODE"
+ DISABLED_TESTS: "certstor_system" # requires 'ISRG Root X1' / not in this AppVeyor image
+
+ # MSVC 2019 static x86-64 w/debug iterators
+ - CC: VC2019
+ PLATFORM: x86_amd64
+ TARGET: sanitizer
+ APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
+ MAKE_TOOL: jom
+ TARGET_CC: msvc
+
+ # MinGW GCC
+ - CC: MinGW
+ PLATFORM: x86_amd64
+ TARGET: static
+ APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
+ MAKE_TOOL: mingw32-make
+ TARGET_CC: gcc
+
+install:
+ - call src\scripts\ci\setup_appveyor.bat
+
+build_script:
+ - python src\scripts\ci_build.py --os=windows --cc=%TARGET_CC% --without-python3 --compiler-cache=sccache --make-tool=%MAKE_TOOL% --cpu=%PLATFORM% --disabled-tests=%DISABLED_TESTS% %EXTRA_FLAGS% %TARGET%
+
+# whitelist branches to avoid testing feature branches twice (as branch and as pull request)
+branches:
+ only:
+ - master
+ - release-2
+
+cache:
+ - C:\Users\appveyor\AppData\Local\Mozilla\sccache\cache
diff --git a/comm/third_party/botan/src/scripts/ci/codecov.yml b/comm/third_party/botan/src/scripts/ci/codecov.yml
new file mode 100644
index 0000000000..db26a6bd55
--- /dev/null
+++ b/comm/third_party/botan/src/scripts/ci/codecov.yml
@@ -0,0 +1,15 @@
+---
+
+# Documentation
+# https://github.com/codecov/support/wiki/Codecov-Yaml#full-yaml
+#
+# Validate this file
+# curl --data-binary @codecov.yml https://codecov.io/validate
+
+coverage:
+ status:
+ project:
+ default:
+ # Random seeds in tests lead to a +/-0.05% coverage span even for PRs
+ # that do not change source code
+ threshold: 0.05
diff --git a/comm/third_party/botan/src/scripts/ci/lgtm.yml b/comm/third_party/botan/src/scripts/ci/lgtm.yml
new file mode 100644
index 0000000000..fa2423858b
--- /dev/null
+++ b/comm/third_party/botan/src/scripts/ci/lgtm.yml
@@ -0,0 +1,31 @@
+
+queries:
+ - include: cpp/inconsistent-null-check
+ - include: cpp/overrunning-write
+ - include: cpp/unbounded-write
+ - include: cpp/offset-use-before-range-check
+ - include: cpp/incomplete-parity-check
+ - include: cpp/mistyped-function-arguments
+ - include: cpp/comparison-with-wider-type
+ - include: cpp/inconsistent-call-on-result
+ - include: cpp/incorrect-not-operator-usage
+ - include: cpp/stack-address-escape
+ - include: cpp/nested-loops-with-same-variable
+ - include: cpp/suspicious-allocation-size
+ - include: cpp/allocation-too-small
+ - include: cpp/uninitialized-local
+ - include: cpp/static-buffer-overflow
+ - include: cpp/suspicious-sizeof
+ - include: cpp/suspicious-pointer-scaling-void
+ - include: cpp/declaration-hides-variable
+ - include: cpp/empty-if
+ - include: cpp/unused-local-variable
+ - include: cpp/unused-static-function
+ - include: cpp/unused-static-variable
+ - exclude: cpp/fixme-comment
+
+extraction:
+ cpp:
+ configure:
+ command:
+ - ./configure.py --build-targets="static,shared,cli,tests,bogo_shim" --build-fuzzers=test --with-zlib --with-bzip2 --with-lzma --with-openssl --with-sqlite3 --no-store-vc-rev
diff --git a/comm/third_party/botan/src/scripts/ci/setup_appveyor.bat b/comm/third_party/botan/src/scripts/ci/setup_appveyor.bat
new file mode 100644
index 0000000000..5b48e78d8f
--- /dev/null
+++ b/comm/third_party/botan/src/scripts/ci/setup_appveyor.bat
@@ -0,0 +1,19 @@
+
+echo Current build setup CC="%CC%" PLATFORM="%PLATFORM%" TARGET="%TARGET%"
+
+if %CC% == VC2015 call "%ProgramFiles(x86)%\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" %PLATFORM%
+if %CC% == VC2017 call "%ProgramFiles(x86)%\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsall.bat" %PLATFORM%
+if %CC% == VC2019 call "%ProgramFiles(x86)%\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvarsall.bat" %PLATFORM%
+if %CC% == MinGW set PATH=%PATH%;C:\msys64\mingw64\bin
+
+rem check compiler version
+if %CC% == MinGW g++ -v
+if not %CC% == MinGW cl
+
+appveyor DownloadFile https://github.com/mozilla/sccache/releases/download/%SCCACHE_VERSION%/sccache-%SCCACHE_VERSION%-x86_64-pc-windows-msvc.tar.gz
+tar -xf sccache-%SCCACHE_VERSION%-x86_64-pc-windows-msvc.tar.gz
+
+appveyor DownloadFile http://download.qt.io/official_releases/jom/jom.zip -FileName jom.zip
+7z e jom.zip
+
+set PATH=%PATH%;sccache-%SCCACHE_VERSION%-x86_64-pc-windows-msvc
diff --git a/comm/third_party/botan/src/scripts/ci/setup_gh_actions.sh b/comm/third_party/botan/src/scripts/ci/setup_gh_actions.sh
new file mode 100755
index 0000000000..33737ffe37
--- /dev/null
+++ b/comm/third_party/botan/src/scripts/ci/setup_gh_actions.sh
@@ -0,0 +1,69 @@
+#!/bin/bash
+
+# GitHub Actions setup script for Botan build
+#
+# (C) 2015,2017 Simon Warta
+# (C) 2016,2017,2018,2020 Jack Lloyd
+#
+# Botan is released under the Simplified BSD License (see license.txt)
+
+command -v shellcheck > /dev/null && shellcheck "$0" # Run shellcheck on this if available
+
+set -ex
+
+TARGET=$1
+
+if type -p "apt-get"; then
+ sudo apt-get -qq update
+ sudo apt-get -qq install ccache
+
+ if [ "$TARGET" = "valgrind" ]; then
+ sudo apt-get -qq install valgrind
+
+ elif [ "$TARGET" = "clang" ]; then
+ sudo apt-get -qq install clang
+
+ elif [ "$TARGET" = "cross-i386" ]; then
+ sudo 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-arm64" ]; then
+ sudo apt-get -qq install qemu-user g++-aarch64-linux-gnu
+
+ elif [ "$TARGET" = "cross-ppc64" ]; then
+ sudo apt-get -qq install qemu-user g++-powerpc64le-linux-gnu
+
+ elif [ "$TARGET" = "cross-android-arm32" ] || [ "$TARGET" = "cross-android-arm64" ]; then
+ wget -nv https://dl.google.com/android/repository/"$ANDROID_NDK"-linux-x86_64.zip
+ unzip -qq "$ANDROID_NDK"-linux-x86_64.zip
+
+ elif [ "$TARGET" = "baremetal" ]; then
+ sudo apt-get -qq install gcc-arm-none-eabi libstdc++-arm-none-eabi-newlib
+
+ echo 'extern "C" void __sync_synchronize() {}' >> src/tests/main.cpp
+ echo 'extern "C" void __sync_synchronize() {}' >> src/cli/main.cpp
+
+ elif [ "$TARGET" = "lint" ]; then
+ sudo apt-get -qq install pylint
+
+ elif [ "$TARGET" = "coverage" ]; then
+ sudo apt-get -qq install g++-8 softhsm2 libtspi-dev lcov python-coverage libboost-all-dev gdb
+ pip install --user codecov
+ echo "$HOME/.local/bin" >> "$GITHUB_PATH"
+
+ git clone --depth 1 --branch runner-changes https://github.com/randombit/boringssl.git
+
+ 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 python-docutils python3-sphinx
+ fi
+else
+ HOMEBREW_NO_AUTO_UPDATE=1 brew install ccache
+fi
diff --git a/comm/third_party/botan/src/scripts/ci/setup_travis.sh b/comm/third_party/botan/src/scripts/ci/setup_travis.sh
new file mode 100755
index 0000000000..f039e574dd
--- /dev/null
+++ b/comm/third_party/botan/src/scripts/ci/setup_travis.sh
@@ -0,0 +1,89 @@
+#!/bin/bash
+
+# Travis CI setup script for Botan build
+#
+# (C) 2015,2017 Simon Warta
+# (C) 2016,2017,2018 Jack Lloyd
+
+command -v shellcheck > /dev/null && shellcheck "$0" # Run shellcheck on this if available
+
+set -ev
+
+if [ "$TRAVIS_OS_NAME" = "linux" ]; then
+
+ if [ "$TARGET" = "valgrind" ]; then
+ sudo apt-get -qq update
+ sudo apt-get install valgrind
+
+ elif [ "$TARGET" = "gcc4.8" ]; then
+ sudo apt-get -qq update
+ sudo apt-get install g++-4.8
+
+ elif [ "$TARGET" = "clang8" ]; then
+ sudo apt-get -qq update
+ sudo apt-get install clang-8
+
+ elif [ "$TARGET" = "cross-i386" ]; then
+ sudo apt-get -qq update
+ sudo apt-get install g++-multilib linux-libc-dev libc6-dev-i386
+
+ elif [ "$TARGET" = "cross-win64" ]; then
+ sudo apt-get -qq update
+ sudo apt-get install wine-development g++-mingw-w64-x86-64
+
+ elif [ "$TARGET" = "cross-arm32" ]; then
+ sudo dpkg --add-architecture armhf
+ sudo apt-get -qq update
+ sudo apt-get install g++-arm-linux-gnueabihf
+ sudo apt-get install -o APT::Immediate-Configure=0 libc6:armhf libstdc++6:armhf
+
+ elif [ "$TARGET" = "cross-arm64" ]; then
+ sudo apt-get -qq update
+ sudo apt-get install qemu-user g++-aarch64-linux-gnu
+
+ elif [ "$TARGET" = "cross-ppc32" ]; then
+ sudo apt-get -qq update
+ sudo apt-get install qemu-user g++-powerpc-linux-gnu
+
+ elif [ "$TARGET" = "cross-ppc64" ]; then
+ sudo apt-get -qq update
+ sudo apt-get install qemu-user g++-powerpc64le-linux-gnu
+
+ elif [ "$TARGET" = "cross-mips64" ]; then
+ sudo apt-get -qq update
+ sudo apt-get install qemu-user g++-mips64-linux-gnuabi64
+
+ elif [ "$TARGET" = "cross-android-arm32" ] || [ "$TARGET" = "cross-android-arm64" ]; then
+ wget -nv https://dl.google.com/android/repository/"$ANDROID_NDK"-linux-x86_64.zip
+ unzip -qq "$ANDROID_NDK"-linux-x86_64.zip
+
+ elif [ "$TARGET" = "baremetal" ]; then
+ sudo apt-get -qq update
+ sudo apt-get install gcc-arm-none-eabi libstdc++-arm-none-eabi-newlib
+
+ echo 'extern "C" void __sync_synchronize() {}' >> src/tests/main.cpp
+ echo 'extern "C" void __sync_synchronize() {}' >> src/cli/main.cpp
+
+ elif [ "$TARGET" = "lint" ]; then
+ sudo apt-get -qq update
+ sudo apt-get install pylint
+
+ elif [ "$TARGET" = "coverage" ]; then
+ sudo apt-get -qq update
+ sudo apt-get install g++-8 softhsm2 libtspi-dev lcov python-coverage libboost-all-dev gdb
+ pip install --user codecov
+ git clone --depth 1 --branch runner-changes-golang1.10 https://github.com/randombit/boringssl.git
+
+ 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
+
+ elif [ "$TARGET" = "docs" ]; then
+ sudo apt-get -qq update
+ sudo apt-get install doxygen python-docutils python3-sphinx
+ fi
+
+elif [ "$TRAVIS_OS_NAME" = "osx" ]; then
+ HOMEBREW_NO_AUTO_UPDATE=1 brew install ccache
+fi
diff --git a/comm/third_party/botan/src/scripts/ci/travis.yml b/comm/third_party/botan/src/scripts/ci/travis.yml
new file mode 100644
index 0000000000..15609864c4
--- /dev/null
+++ b/comm/third_party/botan/src/scripts/ci/travis.yml
@@ -0,0 +1,50 @@
+language: cpp
+os: linux
+dist: focal
+compiler: gcc
+
+jobs:
+ include:
+ - name: Linux ppc64le (GCC)
+ arch: ppc64le
+ env:
+ - TARGET="shared"
+
+ - name: Linux arm64 (GCC)
+ arch: arm64
+ env:
+ - TARGET="shared"
+
+ - name: Linux GCC 4.8
+ dist: bionic
+ env:
+ - TARGET="gcc4.8"
+ - EXTRA_FLAGS="--disable-werror"
+
+install:
+ - ./src/scripts/ci/setup_travis.sh
+
+script:
+ - ./src/scripts/ci_build.py --os=$TRAVIS_OS_NAME --cc=$CC --cc-bin=$CXX --without-pylint3 --pkcs11-lib=$PKCS11_LIB $EXTRA_FLAGS $TARGET
+
+# whitelist branches to avoid testing feature branches twice (as branch and as pull request)
+branches:
+ only:
+ - master
+ - release-2
+ - coverity_scan
+
+git:
+ depth: 10
+
+cache:
+ ccache: true
+
+addons:
+ coverity_scan:
+ project:
+ name: "randombit/botan"
+ notification_email: jack@randombit.net
+ build_command_prepend: "./configure.py --no-optimizations --with-zlib --with-openssl"
+ build_command: "make -j2"
+ branch_pattern: coverity_scan
diff --git a/comm/third_party/botan/src/scripts/ci_build.py b/comm/third_party/botan/src/scripts/ci_build.py
new file mode 100755
index 0000000000..93a5ec3626
--- /dev/null
+++ b/comm/third_party/botan/src/scripts/ci_build.py
@@ -0,0 +1,620 @@
+#!/usr/bin/env python
+
+"""
+CI build script
+(C) 2017,2020 Jack Lloyd
+
+Botan is released under the Simplified BSD License (see license.txt)
+"""
+
+import os
+import platform
+import subprocess
+import sys
+import time
+import tempfile
+import optparse # pylint: disable=deprecated-module
+
+def get_concurrency():
+ def_concurrency = 2
+
+ try:
+ import multiprocessing
+ return multiprocessing.cpu_count()
+ except ImportError:
+ return def_concurrency
+
+def build_targets(target, target_os):
+ if target in ['shared', 'minimized', 'bsi', 'nist']:
+ yield 'shared'
+ elif target in ['static', 'fuzzers', 'baremetal']:
+ yield 'static'
+ elif target_os in ['windows']:
+ yield 'shared'
+ elif target_os in ['ios', 'mingw']:
+ yield 'static'
+ else:
+ yield 'shared'
+ yield 'static'
+
+ yield 'cli'
+ yield 'tests'
+
+ if target in ['coverage']:
+ yield 'bogo_shim'
+
+def determine_flags(target, target_os, target_cpu, target_cc, cc_bin,
+ ccache, root_dir, pkcs11_lib, use_gdb, disable_werror, extra_cxxflags,
+ disabled_tests):
+ # pylint: disable=too-many-branches,too-many-statements,too-many-arguments,too-many-locals
+
+ """
+ Return the configure.py flags as well as make/test running prefixes
+ """
+ is_cross_target = target.startswith('cross-')
+
+ if target_os not in ['linux', 'osx', 'windows', 'freebsd']:
+ print('Error unknown OS %s' % (target_os))
+ return (None, None, None)
+
+ if is_cross_target:
+ if target_os == 'osx':
+ target_os = 'ios'
+ elif target == 'cross-win64':
+ target_os = 'mingw'
+ elif target in ['cross-android-arm32', 'cross-android-arm64']:
+ target_os = 'android'
+
+ if target_os == 'windows' and target_cc == 'gcc':
+ target_os = 'mingw'
+
+ if target == 'baremetal':
+ target_os = 'none'
+
+ make_prefix = []
+ test_prefix = []
+ test_cmd = [os.path.join(root_dir, 'botan-test')]
+
+ install_prefix = tempfile.mkdtemp(prefix='botan-install-')
+
+ flags = ['--prefix=%s' % (install_prefix),
+ '--cc=%s' % (target_cc),
+ '--os=%s' % (target_os),
+ '--build-targets=%s' % ','.join(build_targets(target, target_os))]
+
+ if ccache is not None:
+ flags += ['--no-store-vc-rev', '--compiler-cache=%s' % (ccache)]
+
+ if target_os != 'osx' and not disable_werror:
+ flags += ['--werror-mode']
+
+ if target_cpu is not None:
+ flags += ['--cpu=%s' % (target_cpu)]
+
+ for flag in extra_cxxflags:
+ flags += ['--extra-cxxflags=%s' % (flag)]
+
+ if target in ['minimized']:
+ flags += ['--minimized-build', '--enable-modules=system_rng,sha2_32,sha2_64,aes']
+
+ 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=tls']
+
+ if target == 'docs':
+ flags += ['--with-doxygen', '--with-sphinx', '--with-rst2man']
+ test_cmd = None
+
+ if target == 'cross-win64':
+ # this test compiles under MinGW but fails when run under Wine
+ disabled_tests.append('certstor_system')
+
+ if target == 'coverage':
+ flags += ['--with-coverage-info', '--with-debug-info', '--test-mode']
+
+ if target == 'valgrind':
+ flags += ['--with-valgrind']
+ test_prefix = ['valgrind', '--error-exitcode=9', '-v', '--leak-check=full', '--show-reachable=yes']
+ # valgrind is single threaded anyway
+ test_cmd += ['--test-threads=1']
+ # valgrind is slow
+ slow_tests = [
+ 'cryptobox', 'dh_invalid', 'dh_kat', 'dh_keygen',
+ 'dl_group_gen', 'dlies', 'dsa_param', 'ecc_basemul',
+ 'ecdsa_verify_wycheproof', 'mce_keygen', 'passhash9',
+ 'rsa_encrypt', 'rsa_pss', 'rsa_pss_raw', 'scrypt',
+ 'srp6_kat', 'x509_path_bsi', 'xmss_keygen', 'xmss_sign',
+ 'pbkdf', 'argon2', 'bcrypt', 'bcrypt_pbkdf', 'compression',
+ 'ed25519_sign', 'elgamal_keygen', 'x509_path_rsa_pss']
+
+ disabled_tests += slow_tests
+
+ if target == 'fuzzers':
+ flags += ['--unsafe-fuzzer-mode']
+
+ if target in ['fuzzers', 'coverage']:
+ flags += ['--build-fuzzers=test']
+
+ if target in ['fuzzers', 'sanitizer']:
+ flags += ['--with-debug-asserts']
+
+ if target_cc in ['clang', 'gcc']:
+ flags += ['--enable-sanitizers=address,undefined']
+ else:
+ flags += ['--with-sanitizers']
+
+ if target in ['valgrind', 'sanitizer', 'fuzzers']:
+ flags += ['--disable-modules=locking_allocator']
+
+ if target == 'baremetal':
+ cc_bin = 'arm-none-eabi-c++'
+ flags += ['--cpu=arm32', '--disable-neon', '--without-stack-protector', '--ldflags=-specs=nosys.specs']
+ test_cmd = None
+
+ if is_cross_target:
+ if target_os == 'ios':
+ make_prefix = ['xcrun', '--sdk', 'iphoneos']
+ test_cmd = None
+ if target == 'cross-ios-arm64':
+ flags += ['--cpu=arm64', '--cc-abi-flags=-arch arm64 -stdlib=libc++']
+ else:
+ raise Exception("Unknown cross target '%s' for iOS" % (target))
+ elif target_os == 'android':
+
+ ndk = os.getenv('ANDROID_NDK')
+ if ndk is None:
+ raise Exception('Android CI build requires ANDROID_NDK env variable be set')
+
+ api_lvl = int(os.getenv('ANDROID_API_LEVEL', '0'))
+ if api_lvl == 0:
+ # If not set arbitrarily choose API 16 (Android 4.1) for ARMv7 and 28 (Android 9) for AArch64
+ api_lvl = 16 if target == 'cross-android-arm32' else 28
+
+ toolchain_dir = os.path.join(ndk, 'toolchains/llvm/prebuilt/linux-x86_64/bin')
+ test_cmd = None
+
+ if target == 'cross-android-arm32':
+ cc_bin = os.path.join(toolchain_dir, 'armv7a-linux-androideabi%d-clang++' % (api_lvl))
+ flags += ['--cpu=armv7',
+ '--ar-command=%s' % (os.path.join(toolchain_dir, 'arm-linux-androideabi-ar'))]
+ elif target == 'cross-android-arm64':
+ cc_bin = os.path.join(toolchain_dir, 'aarch64-linux-android%d-clang++' % (api_lvl))
+ flags += ['--cpu=arm64',
+ '--ar-command=%s' % (os.path.join(toolchain_dir, 'aarch64-linux-android-ar'))]
+
+ if api_lvl < 18:
+ flags += ['--without-os-features=getauxval']
+ if api_lvl >= 28:
+ flags += ['--with-os-features=getentropy']
+
+ elif target == 'cross-i386':
+ flags += ['--cpu=x86_32']
+
+ elif target == 'cross-win64':
+ # MinGW in 16.04 is lacking std::mutex for unknown reason
+ cc_bin = 'x86_64-w64-mingw32-g++'
+ flags += ['--cpu=x86_64', '--cc-abi-flags=-static',
+ '--ar-command=x86_64-w64-mingw32-ar', '--without-os-feature=threads']
+ test_cmd = [os.path.join(root_dir, 'botan-test.exe')] + test_cmd[1:]
+ test_prefix = ['wine']
+ else:
+ if target == 'cross-arm32':
+ flags += ['--cpu=armv7']
+ cc_bin = 'arm-linux-gnueabihf-g++'
+ # Currently arm32 CI only runs on native AArch64
+ #test_prefix = ['qemu-arm', '-L', '/usr/arm-linux-gnueabihf/']
+ elif target == 'cross-arm64':
+ flags += ['--cpu=aarch64']
+ cc_bin = 'aarch64-linux-gnu-g++'
+ test_prefix = ['qemu-aarch64', '-L', '/usr/aarch64-linux-gnu/']
+ elif target == 'cross-ppc32':
+ flags += ['--cpu=ppc32']
+ cc_bin = 'powerpc-linux-gnu-g++'
+ test_prefix = ['qemu-ppc', '-L', '/usr/powerpc-linux-gnu/']
+ elif target == 'cross-ppc64':
+ flags += ['--cpu=ppc64', '--with-endian=little']
+ cc_bin = 'powerpc64le-linux-gnu-g++'
+ test_prefix = ['qemu-ppc64le', '-cpu', 'POWER8', '-L', '/usr/powerpc64le-linux-gnu/']
+ elif target == 'cross-mips64':
+ flags += ['--cpu=mips64', '--with-endian=big']
+ cc_bin = 'mips64-linux-gnuabi64-g++'
+ test_prefix = ['qemu-mips64', '-L', '/usr/mips64-linux-gnuabi64/']
+ test_cmd.remove('simd_32') # no SIMD on MIPS
+ else:
+ raise Exception("Unknown cross target '%s' for Linux" % (target))
+ else:
+ # Flags specific to native targets
+
+ if target_os in ['osx', 'linux']:
+ flags += ['--with-bzip2', '--with-sqlite', '--with-zlib']
+
+ if target_os in ['osx', 'ios']:
+ flags += ['--with-commoncrypto']
+
+ if target == 'coverage':
+ flags += ['--with-boost']
+
+ if target_os == 'windows' and target in ['shared', 'static']:
+ # ./configure.py needs extra hand-holding for boost on windows
+ boost_root = os.environ.get('BOOST_ROOT')
+ boost_libs = os.environ.get('BOOST_LIBRARYDIR')
+ boost_system = os.environ.get('BOOST_SYSTEM_LIBRARY')
+
+ if boost_root and boost_libs and boost_system:
+ flags += ['--with-boost',
+ '--with-external-includedir', boost_root,
+ '--with-external-libdir', boost_libs,
+ '--boost-library-name', boost_system]
+
+ if target_os == 'linux':
+ flags += ['--with-lzma']
+
+ if target_os == 'linux':
+ if target not in ['sanitizer', 'valgrind', 'minimized']:
+ # Avoid OpenSSL when using dynamic checkers, or on OS X where it sporadically
+ # is not installed on the CI image
+ flags += ['--with-openssl']
+
+ if target in ['coverage']:
+ flags += ['--with-tpm']
+ test_cmd += ['--run-online-tests']
+ if pkcs11_lib and os.access(pkcs11_lib, os.R_OK):
+ test_cmd += ['--pkcs11-lib=%s' % (pkcs11_lib)]
+
+ if target in ['coverage', 'sanitizer']:
+ test_cmd += ['--run-long-tests']
+
+ flags += ['--cc-bin=%s' % (cc_bin)]
+
+ if test_cmd is None:
+ run_test_command = None
+ else:
+ if use_gdb:
+ disabled_tests.append("os_utils")
+
+ # render 'disabled_tests' array into test_cmd
+ if disabled_tests:
+ test_cmd += ['--skip-tests=%s' % (','.join(disabled_tests))]
+
+ if use_gdb:
+ (cmd, args) = test_cmd[0], test_cmd[1:]
+ run_test_command = test_prefix + ['gdb', cmd,
+ '-ex', 'run %s' % (' '.join(args)),
+ '-ex', 'bt',
+ '-ex', 'quit']
+ else:
+ run_test_command = test_prefix + test_cmd
+
+ return flags, run_test_command, make_prefix
+
+def run_cmd(cmd, root_dir):
+ """
+ Execute a command, die if it failed
+ """
+ print("Running '%s' ..." % (' '.join(cmd)))
+ sys.stdout.flush()
+
+ 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(root_dir)
+ sub_env['DYLD_LIBRARY_PATH'] = os.path.abspath(root_dir)
+ sub_env['PYTHONPATH'] = os.path.abspath(os.path.join(root_dir, 'src/python'))
+ cwd = None
+
+ redirect_stdout = None
+ if len(cmd) >= 3 and cmd[-2] == '>':
+ redirect_stdout = open(cmd[-1], 'w')
+ cmd = cmd[:-2]
+ if len(cmd) > 1 and cmd[0].startswith('indir:'):
+ cwd = cmd[0][6:]
+ cmd = cmd[1:]
+ while len(cmd) > 1 and cmd[0].startswith('env:') and cmd[0].find('=') > 0:
+ env_key, env_val = cmd[0][4:].split('=')
+ sub_env[env_key] = env_val
+ cmd = cmd[1:]
+
+ proc = subprocess.Popen(cmd, cwd=cwd, close_fds=True, env=sub_env, stdout=redirect_stdout)
+ 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))
+
+ if cmd[0] not in ['lcov']:
+ sys.exit(proc.returncode)
+
+def default_os():
+ platform_os = platform.system().lower()
+ if platform_os == 'darwin':
+ return 'osx'
+ return platform_os
+
+def parse_args(args):
+ """
+ Parse arguments
+ """
+ parser = optparse.OptionParser()
+
+ parser.add_option('--os', default=default_os(),
+ help='Set the target os (default %default)')
+ parser.add_option('--cc', default='gcc',
+ help='Set the target compiler type (default %default)')
+ parser.add_option('--cc-bin', default=None,
+ help='Set path to compiler')
+ parser.add_option('--root-dir', metavar='D', default='.',
+ help='Set directory to execute from (default %default)')
+
+ parser.add_option('--make-tool', metavar='TOOL', default='make',
+ help='Specify tool to run to build source (default %default)')
+
+ parser.add_option('--extra-cxxflags', metavar='FLAGS', default=[], action='append',
+ help='Specify extra build flags')
+
+ parser.add_option('--cpu', default=None,
+ help='Specify a target CPU platform')
+
+ parser.add_option('--with-debug', action='store_true', default=False,
+ help='Include debug information')
+ parser.add_option('--amalgamation', action='store_true', default=False,
+ help='Build via amalgamation')
+ parser.add_option('--disable-shared', action='store_true', default=False,
+ help='Disable building shared libraries')
+ parser.add_option('--disabled-tests', metavar='DISABLED_TESTS', default=[], action='append',
+ help='Comma separated list of tests that should not be run')
+
+ parser.add_option('--branch', metavar='B', default=None,
+ help='Specify branch being built')
+
+ parser.add_option('--dry-run', action='store_true', default=False,
+ help='Just show commands to be executed')
+ parser.add_option('--build-jobs', metavar='J', default=get_concurrency(),
+ help='Set number of jobs to run in parallel (default %default)')
+
+ parser.add_option('--compiler-cache', default=None, metavar='CC',
+ help='Set a compiler cache to use (ccache, sccache)')
+
+ parser.add_option('--pkcs11-lib', default=os.getenv('PKCS11_LIB'), metavar='LIB',
+ help='Set PKCS11 lib to use for testing')
+
+ parser.add_option('--with-python3', dest='use_python3', action='store_true', default=None,
+ help='Enable using python3')
+ parser.add_option('--without-python3', dest='use_python3', action='store_false',
+ help='Disable using python3')
+
+ parser.add_option('--with-pylint3', dest='use_pylint3', action='store_true', default=True,
+ help='Enable using python3 pylint')
+ parser.add_option('--without-pylint3', dest='use_pylint3', action='store_false',
+ help='Disable using python3 pylint')
+
+ parser.add_option('--disable-werror', action='store_true', default=False,
+ help='Allow warnings to compile')
+
+ parser.add_option('--run-under-gdb', dest='use_gdb', action='store_true', default=False,
+ help='Run test suite under gdb and capture backtrace')
+
+ return parser.parse_args(args)
+
+def have_prog(prog):
+ """
+ Check if some named program exists in the path
+ """
+ for path in os.environ['PATH'].split(os.pathsep):
+ exe_file = os.path.join(path, prog)
+ if os.path.exists(exe_file) and os.access(exe_file, os.X_OK):
+ return True
+ return False
+
+def main(args=None):
+ # pylint: disable=too-many-branches,too-many-statements,too-many-locals,too-many-return-statements,too-many-locals
+ """
+ Parse options, do the things
+ """
+
+ if os.getenv('COVERITY_SCAN_BRANCH') == '1':
+ print('Skipping build COVERITY_SCAN_BRANCH set in environment')
+ return 0
+
+ if args is None:
+ args = sys.argv
+ print("Invoked as '%s'" % (' '.join(args)))
+ (options, args) = parse_args(args)
+
+ if len(args) != 2:
+ print('Usage: %s [options] target' % (args[0]))
+ return 1
+
+ target = args[1]
+
+ if options.use_python3 is None:
+ use_python3 = have_prog('python3')
+ else:
+ use_python3 = options.use_python3
+
+ py_interp = 'python'
+ if use_python3:
+ py_interp = 'python3'
+
+ if options.cc_bin is None:
+ if options.cc == 'gcc':
+ options.cc_bin = 'g++'
+ elif options.cc == 'clang':
+ options.cc_bin = 'clang++'
+ elif options.cc == 'msvc':
+ options.cc_bin = 'cl'
+ else:
+ print('Error unknown compiler %s' % (options.cc))
+ return 1
+
+ if options.compiler_cache is None and options.cc != 'msvc':
+ # Autodetect ccache
+ if have_prog('ccache'):
+ options.compiler_cache = 'ccache'
+
+ if options.compiler_cache not in [None, 'ccache', 'sccache']:
+ raise Exception("Don't know about %s as a compiler cache" % (options.compiler_cache))
+
+ root_dir = options.root_dir
+
+ if not os.access(root_dir, os.R_OK):
+ raise Exception('Bad root dir setting, dir %s not readable' % (root_dir))
+
+ cmds = []
+
+ if target == 'lint':
+
+ pylint_rc = '--rcfile=%s' % (os.path.join(root_dir, 'src/configs/pylint.rc'))
+ pylint_flags = [pylint_rc, '--reports=no']
+
+ # Some disabled rules specific to Python3
+ # useless-object-inheritance: complains about code still useful in Python2
+ py3_flags = '--disable=useless-object-inheritance'
+
+ py_scripts = [
+ 'configure.py',
+ 'src/python/botan2.py',
+ 'src/scripts/ci_build.py',
+ 'src/scripts/install.py',
+ 'src/scripts/ci_check_install.py',
+ 'src/scripts/dist.py',
+ 'src/scripts/cleanup.py',
+ 'src/scripts/check.py',
+ 'src/scripts/build_docs.py',
+ 'src/scripts/website.py',
+ 'src/scripts/bench.py',
+ 'src/scripts/test_python.py',
+ 'src/scripts/test_fuzzers.py',
+ 'src/scripts/test_cli.py',
+ 'src/scripts/python_unittests.py',
+ 'src/scripts/python_unittests_unix.py']
+
+ full_paths = [os.path.join(root_dir, s) for s in py_scripts]
+
+ if use_python3 and options.use_pylint3:
+ cmds.append(['python3', '-m', 'pylint'] + pylint_flags + [py3_flags] + full_paths)
+
+ else:
+ config_flags, run_test_command, make_prefix = determine_flags(
+ target, options.os, options.cpu, options.cc,
+ options.cc_bin, options.compiler_cache, root_dir,
+ options.pkcs11_lib, options.use_gdb, options.disable_werror,
+ options.extra_cxxflags, options.disabled_tests)
+
+ cmds.append([py_interp, os.path.join(root_dir, 'configure.py')] + config_flags)
+
+ make_cmd = [options.make_tool]
+ if root_dir != '.':
+ make_cmd += ['-C', root_dir]
+ if options.build_jobs > 1 and options.make_tool != 'nmake':
+ make_cmd += ['-j%d' % (options.build_jobs)]
+ make_cmd += ['-k']
+
+ if target == 'docs':
+ cmds.append(make_cmd + ['docs'])
+ else:
+ 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 ['coverage']:
+ make_targets += ['bogo_shim']
+
+ cmds.append(make_prefix + make_cmd + make_targets)
+
+ if options.compiler_cache is not None:
+ cmds.append([options.compiler_cache, '--show-stats'])
+
+ if run_test_command is not None:
+ cmds.append(run_test_command)
+
+ if target == 'coverage':
+ runner_dir = os.path.abspath(os.path.join(root_dir, 'boringssl', 'ssl', 'test', 'runner'))
+
+ cmds.append(['indir:%s' % (runner_dir),
+ 'go', 'test', '-pipe',
+ '-num-workers', str(4*get_concurrency()),
+ '-shim-path', os.path.abspath(os.path.join(root_dir, 'botan_bogo_shim')),
+ '-shim-config', os.path.abspath(os.path.join(root_dir, 'src', 'bogo_shim', 'config.json'))])
+
+ if target in ['coverage', 'fuzzers']:
+ cmds.append([py_interp, os.path.join(root_dir, 'src/scripts/test_fuzzers.py'),
+ os.path.join(root_dir, 'fuzzer_corpus'),
+ os.path.join(root_dir, 'build/fuzzer')])
+
+ if target in ['shared', 'coverage'] and options.os != 'windows':
+ botan_exe = os.path.join(root_dir, 'botan-cli.exe' if options.os == 'windows' else 'botan')
+
+ args = ['--threads=%d' % (options.build_jobs)]
+ test_scripts = ['test_cli.py', 'test_cli_crypt.py']
+ for script in test_scripts:
+ cmds.append([py_interp, os.path.join(root_dir, 'src/scripts', script)] +
+ args + [botan_exe])
+
+ python_tests = os.path.join(root_dir, 'src/scripts/test_python.py')
+
+ if target in ['shared', 'coverage']:
+
+ if options.os == 'windows':
+ if options.cpu == 'x86':
+ # Python on AppVeyor is a 32-bit binary so only test for 32-bit
+ cmds.append([py_interp, '-b', python_tests])
+ else:
+ if use_python3:
+ cmds.append(['python3', '-b', python_tests])
+
+ if target in ['shared', 'static', 'bsi', 'nist']:
+ cmds.append(make_cmd + ['install'])
+ build_config = os.path.join(root_dir, 'build', 'build_config.json')
+ cmds.append([py_interp, os.path.join(root_dir, 'src/scripts/ci_check_install.py'), build_config])
+
+ if target in ['coverage']:
+ if not have_prog('lcov'):
+ print('Error: lcov not found in PATH (%s)' % (os.getenv('PATH')))
+ return 1
+
+ if not have_prog('gcov'):
+ print('Error: gcov not found in PATH (%s)' % (os.getenv('PATH')))
+ return 1
+
+ cov_file = 'coverage.info'
+ raw_cov_file = 'coverage.info.raw'
+
+ cmds.append(['lcov', '--capture', '--directory', options.root_dir,
+ '--output-file', raw_cov_file])
+ cmds.append(['lcov', '--remove', raw_cov_file, '/usr/*', '--output-file', cov_file])
+ cmds.append(['lcov', '--list', cov_file])
+
+ if have_prog('coverage'):
+ cmds.append(['coverage', 'run', '--branch',
+ '--rcfile', os.path.join(root_dir, 'src/configs/coverage.rc'),
+ python_tests])
+
+ if have_prog('codecov'):
+ # If codecov exists assume we are in CI and report to codecov.io
+ cmds.append(['codecov', '>', 'codecov_stdout.log'])
+ else:
+ # Otherwise generate a local HTML report
+ cmds.append(['genhtml', cov_file, '--output-directory', 'lcov-out'])
+
+ cmds.append(make_cmd + ['clean'])
+ cmds.append(make_cmd + ['distclean'])
+
+ for cmd in cmds:
+ if options.dry_run:
+ print('$ ' + ' '.join(cmd))
+ else:
+ run_cmd(cmd, root_dir)
+
+ return 0
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/comm/third_party/botan/src/scripts/ci_check_install.py b/comm/third_party/botan/src/scripts/ci_check_install.py
new file mode 100755
index 0000000000..c0a3727629
--- /dev/null
+++ b/comm/third_party/botan/src/scripts/ci_check_install.py
@@ -0,0 +1,104 @@
+#!/usr/bin/env python
+# coding=utf8
+
+"""
+Botan CI check installation script
+This script is used to validate the results of `make install`
+
+(C) 2020 Jack Lloyd, René Meusel, Hannes Rantzsch
+
+Botan is released under the Simplified BSD License (see license.txt)
+"""
+
+import os
+import sys
+import json
+import re
+
+def verify_library(build_config):
+ lib_dir = build_config['libdir']
+ if not os.path.isdir(lib_dir):
+ print('Error: libdir "%s" is not a directory' % lib_dir)
+ return False
+
+ found_libs = set([])
+
+ major_version = int(build_config["version_major"])
+
+ if build_config['compiler'] == 'msvc':
+ expected_lib_format = r'^botan\.(dll|lib)$'
+ elif build_config['os'] == 'macos':
+ expected_lib_format = r'^libbotan-%d\.(a|dylib)$' % (major_version)
+ else:
+ expected_lib_format = r'^libbotan-%d\.(a|so)$' % (major_version)
+
+ lib_re = re.compile(expected_lib_format)
+
+ # Unlike the include dir this may have other random libs in it
+ for (_, _, filenames) in os.walk(lib_dir):
+ for filename in filenames:
+ if lib_re.match(filename) is not None:
+ found_libs.add(filename)
+
+ if len(found_libs) == 0:
+ print("Could not find any libraries from us")
+ return False
+
+ # This should match up the count and names of libraries installed
+ # vs the build configuration (eg static lib installed or not)
+
+ return True
+
+def verify_includes(build_config):
+ include_dir = build_config['installed_include_dir']
+ if not os.path.isdir(include_dir):
+ print('Error: installed_include_dir "%s" is not a directory' % include_dir)
+ return False
+
+ expected_headers = set(build_config['public_headers'] + build_config['external_headers'])
+ found_headers = set([])
+
+ for (_, _, filenames) in os.walk(include_dir):
+ for filename in filenames:
+ found_headers.add(filename)
+
+ if found_headers != expected_headers:
+ missing = expected_headers - found_headers
+ extra = found_headers - expected_headers
+
+ if len(missing) > 0:
+ print("Missing expected headers: %s" % (" ".join(sorted(missing))))
+
+ if len(extra) > 0:
+ print("Have unexpected headers: %s" % (" ".join(sorted(extra))))
+ return False
+
+ return True
+
+def main(args=None):
+ if args is None:
+ args = sys.argv
+
+ if len(args) < 2:
+ print("Usage: %s <build_config.json>" % args[0])
+ return 1
+
+ with open(os.path.join(args[1])) as f:
+ build_config = json.load(f)
+
+ install_prefix = build_config['prefix']
+
+ if not os.path.isdir(install_prefix):
+ print('Error: install_prefix "%s" is not a directory' % install_prefix)
+ return 1
+
+ if not verify_includes(build_config):
+ return 1
+
+ if not verify_library(build_config):
+ return 1
+
+ return 0
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/comm/third_party/botan/src/scripts/cleanup.py b/comm/third_party/botan/src/scripts/cleanup.py
new file mode 100755
index 0000000000..3e8142bab0
--- /dev/null
+++ b/comm/third_party/botan/src/scripts/cleanup.py
@@ -0,0 +1,133 @@
+#!/usr/bin/env python
+
+"""
+Implements the "make clean" target
+
+(C) 2017 Jack Lloyd
+
+Botan is released under the Simplified BSD License (see license.txt)
+"""
+
+import os
+import sys
+import stat
+import re
+import optparse # pylint: disable=deprecated-module
+import logging
+import json
+import shutil
+import errno
+
+def remove_dir(d):
+ try:
+ if os.access(d, os.X_OK):
+ logging.debug('Removing directory "%s"', d)
+ shutil.rmtree(d)
+ else:
+ logging.debug('Directory %s was missing', d)
+ except Exception as e: # pylint: disable=broad-except
+ logging.error('Failed removing directory "%s": %s', d, e)
+
+def remove_file(f):
+ try:
+ logging.debug('Removing file "%s"', f)
+ os.unlink(f)
+ except OSError as e:
+ if e.errno != errno.ENOENT:
+ logging.error('Failed removing file "%s": %s', f, e)
+
+def remove_all_in_dir(d):
+ if os.access(d, os.X_OK):
+ logging.debug('Removing all files in directory "%s"', d)
+
+ for f in os.listdir(d):
+ full_path = os.path.join(d, f)
+ mode = os.lstat(full_path).st_mode
+
+ if stat.S_ISDIR(mode):
+ remove_dir(full_path)
+ else:
+ remove_file(full_path)
+
+def parse_options(args):
+ parser = optparse.OptionParser()
+ parser.add_option('--build-dir', default='build', metavar='DIR',
+ help='specify build dir to clean (default %default)')
+
+ parser.add_option('--distclean', action='store_true', default=False,
+ help='clean everything')
+ parser.add_option('--verbose', action='store_true', default=False,
+ help='noisy logging')
+
+ (options, args) = parser.parse_args(args)
+
+ if len(args) > 1:
+ raise Exception("Unknown arguments")
+
+ return options
+
+def main(args=None):
+ if args is None:
+ args = sys.argv
+
+ options = parse_options(args)
+
+ logging.basicConfig(stream=sys.stderr,
+ format='%(levelname) 7s: %(message)s',
+ level=logging.DEBUG if options.verbose else logging.INFO)
+
+ build_dir = options.build_dir
+
+ if not os.access(build_dir, os.X_OK):
+ logging.debug('No build directory found')
+ # No build dir: clean enough!
+ return 0
+
+ build_config_path = os.path.join(build_dir, 'build_config.json')
+ build_config_str = None
+
+ try:
+ build_config_file = open(build_config_path)
+ build_config_str = build_config_file.read()
+ build_config_file.close()
+ except Exception: # pylint: disable=broad-except
+ # Ugh have to do generic catch as different exception type thrown in Python2
+ logging.error("Unable to access build_config.json in build dir")
+ return 1
+
+ build_config = json.loads(build_config_str)
+
+ if options.distclean:
+ build_dir = build_config['build_dir']
+ remove_file(build_config['makefile_path'])
+ remove_dir(build_dir)
+ else:
+ for dir_type in ['libobj_dir', 'cliobj_dir', 'testobj_dir', 'handbook_output_dir', 'doc_output_dir_doxygen']:
+ dir_path = build_config[dir_type]
+ if dir_path:
+ remove_all_in_dir(dir_path)
+
+ remove_file(build_config['doc_stamp_file'])
+
+ remove_file(build_config['cli_exe'])
+ remove_file(build_config['test_exe'])
+
+ lib_basename = build_config['lib_prefix'] + build_config['libname']
+ matches_libname = re.compile('^' + lib_basename + '.([a-z]+)((\\.[0-9\\.]+)|$)')
+
+ known_suffix = ['a', 'so', 'dll', 'manifest', 'exp']
+
+ for f in os.listdir(build_config['out_dir']):
+ match = matches_libname.match(f)
+ if match and match.group(1) in known_suffix:
+ remove_file(os.path.join(build_config['out_dir'], f))
+
+ if options.distclean:
+ if 'generated_files' in build_config:
+ for f in build_config['generated_files'].split(' '):
+ remove_file(f)
+
+ return 0
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/comm/third_party/botan/src/scripts/comba.py b/comm/third_party/botan/src/scripts/comba.py
new file mode 100755
index 0000000000..309dca0821
--- /dev/null
+++ b/comm/third_party/botan/src/scripts/comba.py
@@ -0,0 +1,126 @@
+#!/usr/bin/python3
+
+import sys
+import datetime
+
+# (C) 2011,2014,2015,2016 Jack Lloyd
+# Botan is released under the Simplified BSD License (see license.txt)
+
+# Used to generate src/lib/math/mp/mp_comba.cpp
+
+def comba_indexes(N):
+
+ indexes = []
+
+ for i in range(0, 2*N):
+ x = []
+
+ for j in range(max(0, i-N+1), min(N, i+1)):
+ x += [(j,i-j)]
+ indexes += [sorted(x)]
+
+ return indexes
+
+def comba_sqr_indexes(N):
+
+ indexes = []
+
+ for i in range(0, 2*N):
+ x = []
+
+ for j in range(max(0, i-N+1), min(N, i+1)):
+ if j < i-j:
+ x += [(j,i-j)]
+ else:
+ x += [(i-j,j)]
+ indexes += [sorted(x)]
+
+ return indexes
+
+def comba_multiply_code(N):
+ indexes = comba_indexes(N)
+
+ w2 = 'w2'
+ w1 = 'w1'
+ w0 = 'w0'
+
+ for (i,idx) in zip(range(0, len(indexes)), indexes):
+ for pair in idx:
+ print(" word3_muladd(&%s, &%s, &%s, x[%2d], y[%2d]);" % (w2, w1, w0, pair[0], pair[1]))
+
+ if i < 2*N-2:
+ print(" z[%2d] = %s; %s = 0;\n" % (i, w0, w0))
+ else:
+ print(" z[%2d] = %s;" % (i, w0))
+ (w0,w1,w2) = (w1,w2,w0)
+ #print("z[%2d] = w0; w0 = w1; w1 = w2; w2 = 0;" % (i))
+
+def comba_square_code(N):
+ indexes = comba_sqr_indexes(N)
+
+ w2 = 'w2'
+ w1 = 'w1'
+ w0 = 'w0'
+
+ for (rnd,idx) in zip(range(0, len(indexes)), indexes):
+ for (i,pair) in zip(range(0, len(idx)), idx):
+ if pair[0] == pair[1]:
+ print(" word3_muladd (&%s, &%s, &%s, x[%2d], x[%2d]);" % (w2, w1, w0, pair[0], pair[1]))
+ elif i % 2 == 0:
+ print(" word3_muladd_2(&%s, &%s, &%s, x[%2d], x[%2d]);" % (w2, w1, w0, pair[0], pair[1]))
+
+ if rnd < 2*N-2:
+ print(" z[%2d] = %s; %s = 0;\n" % (rnd, w0, w0))
+ else:
+ print(" z[%2d] = %s;" % (rnd, w0))
+
+ (w0,w1,w2) = (w1,w2,w0)
+
+def main(args = None):
+ if args is None:
+ args = sys.argv
+
+ if len(args) <= 1:
+ sizes = [4, 6, 8, 9, 16, 24]
+ else:
+ sizes = map(int, args[1:])
+
+ print("""/*
+* Comba Multiplication and Squaring
+*
+* This file was automatically generated by %s on %s
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/internal/mp_core.h>
+#include <botan/internal/mp_asmi.h>
+
+namespace Botan {
+""" % (sys.argv[0], datetime.date.today().strftime("%Y-%m-%d")))
+
+ for n in sizes:
+ print("/*\n* Comba %dx%d Squaring\n*/" % (n, n))
+ print("void bigint_comba_sqr%d(word z[%d], const word x[%d])" % (n, 2*n, n))
+ print(" {")
+ print(" word w2 = 0, w1 = 0, w0 = 0;\n")
+
+ comba_square_code(n)
+
+ print(" }\n")
+
+ print("/*\n* Comba %dx%d Multiplication\n*/" % (n, n))
+ print("void bigint_comba_mul%d(word z[%d], const word x[%d], const word y[%d])" % (n, 2*n, n, n))
+ print(" {")
+ print(" word w2 = 0, w1 = 0, w0 = 0;\n")
+
+ comba_multiply_code(n)
+
+ print(" }\n")
+
+ print("}")
+
+ return 0
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/comm/third_party/botan/src/scripts/create_corpus_zip.py b/comm/third_party/botan/src/scripts/create_corpus_zip.py
new file mode 100755
index 0000000000..5faee3b526
--- /dev/null
+++ b/comm/third_party/botan/src/scripts/create_corpus_zip.py
@@ -0,0 +1,48 @@
+#!/usr/bin/python
+
+# These is used to create fuzzer corpus zip files
+
+# This is primarily used by OSS-Fuzz but might be useful if you were
+# deploying the binaries in a custom fuzzer deployment system.
+
+import sys
+import os
+import zipfile
+import stat
+
+def main(args=None):
+ if args is None:
+ args = sys.argv
+
+ if len(args) != 2 and len(args) != 3:
+ print("Usage: %s corpus_dir <output_dir>" % (args[0]))
+ return 1
+
+ root_dir = args[1]
+
+ if len(args) == 3:
+ output_dir = args[2]
+ else:
+ output_dir = ''
+
+ if not os.access(root_dir, os.R_OK):
+ print("Error could not access directory '%s'" % (root_dir))
+ return 1
+
+ for corpus_dir in os.listdir(root_dir):
+ if corpus_dir == '.git':
+ continue
+ subdir = os.path.join(root_dir, corpus_dir)
+ if not stat.S_ISDIR(os.stat(subdir).st_mode):
+ continue
+
+ zipfile_path = os.path.join(output_dir, '%s.zip' % (corpus_dir))
+ zf = zipfile.ZipFile(zipfile_path, 'w', zipfile.ZIP_DEFLATED)
+ for f in os.listdir(subdir):
+ zf.write(os.path.join(subdir, f), f)
+ zf.close()
+
+ return 0
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/comm/third_party/botan/src/scripts/dist.py b/comm/third_party/botan/src/scripts/dist.py
new file mode 100755
index 0000000000..ce072ec105
--- /dev/null
+++ b/comm/third_party/botan/src/scripts/dist.py
@@ -0,0 +1,466 @@
+#!/usr/bin/env python
+
+"""
+Release script for botan (https://botan.randombit.net/)
+
+This script requires Python 2.7 or 3.6
+
+(C) 2011,2012,2013,2015,2016,2017 Jack Lloyd
+
+Botan is released under the Simplified BSD License (see license.txt)
+"""
+
+import datetime
+import errno
+import hashlib
+import io
+import logging
+import optparse # pylint: disable=deprecated-module
+import os
+import re
+import shutil
+import subprocess
+import sys
+import tarfile
+import time
+import traceback
+
+# This is horrible, but there is no way to override tarfile's use of time.time
+# in setting the gzip header timestamp, which breaks deterministic archives
+
+GZIP_HEADER_TIME = 0
+
+def fake_time():
+ return GZIP_HEADER_TIME
+time.time = fake_time
+
+
+def check_subprocess_results(subproc, name):
+ (raw_stdout, raw_stderr) = subproc.communicate()
+
+ stderr = raw_stderr.decode('utf-8')
+
+ if subproc.returncode != 0:
+ stdout = raw_stdout.decode('utf-8')
+ if stdout != '':
+ logging.error(stdout)
+ if stderr != '':
+ logging.error(stderr)
+ raise Exception('Running %s failed' % (name))
+
+ if stderr != '':
+ logging.warning(stderr)
+
+ return raw_stdout
+
+def run_git(args):
+ cmd = ['git'] + args
+ logging.debug('Running %s' % (' '.join(cmd)))
+ proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ return check_subprocess_results(proc, 'git')
+
+def maybe_gpg(val):
+ val = val.decode('ascii')
+ if 'BEGIN PGP SIGNATURE' in val:
+ return val.split('\n')[-2]
+ else:
+ return val.strip()
+
+def rel_time_to_epoch(year, month, day, hour, minute, second):
+ dt = datetime.datetime(year, month, day, hour, minute, second)
+ return (dt - datetime.datetime(1970, 1, 1)).total_seconds()
+
+def datestamp(tag):
+ ts = maybe_gpg(run_git(['show', '--no-patch', '--format=%ai', tag]))
+
+ ts_matcher = re.compile(r'^(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2}) .*')
+
+ logging.debug('Git returned timestamp of %s for tag %s' % (ts, tag))
+ match = ts_matcher.match(ts)
+
+ if match is None:
+ logging.error('Failed parsing timestamp "%s" of tag %s' % (ts, tag))
+ return 0
+
+ rel_date = int(match.group(1) + match.group(2) + match.group(3))
+ rel_epoch = rel_time_to_epoch(*[int(match.group(i)) for i in range(1, 7)])
+
+ return rel_date, rel_epoch
+
+def revision_of(tag):
+ return maybe_gpg(run_git(['show', '--no-patch', '--format=%H', tag]))
+
+def extract_revision(revision, to):
+ tar_val = run_git(['archive', '--format=tar', '--prefix=%s/' % (to), revision])
+ tar_f = tarfile.open(fileobj=io.BytesIO(tar_val))
+ tar_f.extractall()
+
+def gpg_sign(keyid, passphrase_file, files, detached=True):
+
+ options = ['--armor', '--detach-sign'] if detached else ['--clearsign']
+
+ gpg_cmd = ['gpg', '--batch'] + options + ['--local-user', keyid]
+ if passphrase_file is not None:
+ gpg_cmd[1:1] = ['--passphrase-file', passphrase_file]
+
+ for filename in files:
+ logging.info('Signing %s using PGP id %s' % (filename, keyid))
+
+ cmd = gpg_cmd + [filename]
+
+ logging.debug('Running %s' % (' '.join(cmd)))
+
+ gpg = subprocess.Popen(cmd,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+
+ check_subprocess_results(gpg, 'gpg')
+
+ return [filename + '.asc' for filename in files]
+
+def parse_args(args):
+ parser = optparse.OptionParser(
+ "usage: %prog [options] <version tag>\n" +
+ " %prog [options] snapshot <branch>"
+ )
+
+ parser.add_option('--verbose', action='store_true',
+ default=False, help='Extra debug output')
+
+ parser.add_option('--quiet', action='store_true',
+ default=False, help='Only show errors')
+
+ parser.add_option('--output-dir', metavar='DIR', default='.',
+ help='Where to place output (default %default)')
+
+ parser.add_option('--print-output-names', action='store_true',
+ help='Print output archive filenames to stdout')
+
+ parser.add_option('--archive-types', metavar='LIST', default='txz',
+ help='Set archive types to generate (default %default)')
+
+ parser.add_option('--pgp-key-id', metavar='KEYID',
+ default='EFBADFBC',
+ help='PGP signing key (default %default, "none" to disable)')
+
+ parser.add_option('--pgp-passphrase-file', metavar='FILE',
+ default=None,
+ help='PGP signing key passphrase file')
+
+ parser.add_option('--write-hash-file', metavar='FILE', default=None,
+ help='Write a file with checksums')
+
+ return parser.parse_args(args)
+
+def remove_file_if_exists(fspath):
+ try:
+ os.unlink(fspath)
+ except OSError as e:
+ if e.errno != errno.ENOENT:
+ raise
+
+def rewrite_version_file(version_file, target_version, snapshot_branch, rev_id, rel_date):
+
+ if snapshot_branch:
+ assert target_version == snapshot_branch
+
+ contents = open(version_file).readlines()
+
+ version_re = re.compile('release_(major|minor|patch) = ([0-9]+)')
+ version_suffix_re = re.compile('release_suffix = \'(-(alpha|beta|rc)[0-9]+)\'')
+
+ def content_rewriter(target_version):
+ version_info = {}
+
+ release_type = 'release'
+
+ # Not included in old version files so set a default
+ version_info["suffix"] = ""
+
+ for line in contents:
+
+ if not snapshot_branch:
+ match = version_re.match(line)
+ if match:
+ version_info[match.group(1)] = int(match.group(2))
+
+ match = version_suffix_re.match(line)
+ if match:
+ suffix = match.group(1)
+ version_info['suffix'] = suffix
+ if suffix.find('alpha') >= 0:
+ release_type = 'alpha'
+ elif suffix.find('beta') >= 0:
+ release_type = 'beta'
+ elif suffix.find('rc') >= 0:
+ release_type = 'release candidate'
+
+ if line == 'release_vc_rev = None\n':
+ yield 'release_vc_rev = \'git:%s\'\n' % (rev_id)
+ elif line == 'release_datestamp = 0\n':
+ yield 'release_datestamp = %d\n' % (rel_date)
+ elif line == "release_type = \'unreleased\'\n":
+ if target_version == snapshot_branch:
+ yield "release_type = 'snapshot:%s'\n" % (snapshot_branch)
+ else:
+ yield "release_type = '%s'\n" % (release_type)
+ else:
+ yield line
+
+ if not snapshot_branch:
+ for req_var in ["major", "minor", "patch", "suffix"]:
+ if req_var not in version_info.keys():
+ raise Exception('Missing version field for %s in version file' % (req_var))
+
+ marked_version = "%d.%d.%d%s" % (version_info["major"],
+ version_info["minor"],
+ version_info["patch"],
+ version_info["suffix"])
+
+ if marked_version != target_version:
+ raise Exception('Release version file %s does not match tagged version %s' % (
+ marked_version, target_version))
+
+ new_contents = ''.join(list(content_rewriter(target_version)))
+ open(version_file, 'w').write(new_contents)
+
+def write_archive(version, output_basename, archive_type, rel_epoch, all_files, hash_file):
+ # pylint: disable=too-many-locals
+ def archive_suffix(archive_type):
+ if archive_type == 'tgz':
+ return 'tgz'
+ elif archive_type == 'tbz':
+ return 'tar.bz2'
+ elif archive_type == 'txz':
+ return 'tar.xz'
+ elif archive_type == 'tar':
+ return 'tar'
+ else:
+ raise Exception("Unknown archive type '%s'" % (archive_type))
+
+ output_archive = output_basename + '.' + archive_suffix(archive_type)
+ logging.info('Writing archive "%s"' % (output_archive))
+
+ remove_file_if_exists(output_archive)
+ remove_file_if_exists(output_archive + '.asc')
+
+ def write_mode(archive_type):
+ if archive_type == 'tgz':
+ return 'w:gz'
+ elif archive_type == 'tbz':
+ return 'w:bz2'
+ elif archive_type == 'txz':
+ return 'w:xz'
+ elif archive_type == 'tar':
+ return 'w'
+ else:
+ raise Exception("Unknown archive type '%s'" % (archive_type))
+
+ # gzip format embeds the original filename, tarfile.py does the wrong
+ # thing unless the output name ends in .gz. So pass an explicit
+ # fileobj in that case, and supply a name in the form tarfile expects.
+ archive_suffix = '.tar.gz' if archive_type == 'tgz' else '.tar'
+
+ def archive_format(version):
+ # A change in Python meant that 2.14 and 2.15 were released with a
+ # tarfile using POSIX pax format (the new default for tarfile module)
+ # instead of the previously used GNU format.
+ if version in ['2.14.0', '2.15.0']:
+ return tarfile.PAX_FORMAT
+ else:
+ return tarfile.GNU_FORMAT
+
+ archive = tarfile.open(output_basename + archive_suffix,
+ write_mode(archive_type),
+ format=archive_format(version),
+ fileobj=open(output_archive, 'wb'))
+
+ for f in all_files:
+ tarinfo = archive.gettarinfo(f)
+ tarinfo.uid = 500
+ tarinfo.gid = 500
+ tarinfo.uname = "botan"
+ tarinfo.gname = "botan"
+ tarinfo.mtime = rel_epoch
+ archive.addfile(tarinfo, open(f, 'rb'))
+ archive.close()
+
+ archive_contents = open(output_archive, 'rb').read()
+
+ sha256 = hashlib.new('sha256')
+ sha256.update(archive_contents)
+ archive_hash = sha256.hexdigest().upper()
+
+ logging.info('%s is %.2f MiB' % (output_archive, len(archive_contents) / (1024.0*1024.0)))
+ logging.info('SHA-256(%s) = %s' % (output_archive, archive_hash))
+ if hash_file is not None:
+ hash_file.write("%s %s\n" % (archive_hash, output_archive))
+
+ return output_archive
+
+def configure_logging(options):
+ class ExitOnErrorLogHandler(logging.StreamHandler, object):
+ def emit(self, record):
+ super(ExitOnErrorLogHandler, self).emit(record)
+ # Exit script if and ERROR or worse occurred
+ if record.levelno >= logging.ERROR:
+ if sys.exc_info()[2] is not None:
+ logging.info(traceback.format_exc())
+ sys.exit(1)
+
+ def log_level():
+ if options.verbose:
+ return logging.DEBUG
+ if options.quiet:
+ return logging.ERROR
+ return logging.INFO
+
+ lh = ExitOnErrorLogHandler(sys.stderr)
+ lh.setFormatter(logging.Formatter('%(levelname) 7s: %(message)s'))
+ logging.getLogger().addHandler(lh)
+ logging.getLogger().setLevel(log_level())
+
+def main(args=None):
+ # pylint: disable=too-many-branches,too-many-locals,too-many-statements
+ if args is None:
+ args = sys.argv[1:]
+
+ (options, args) = parse_args(args)
+
+ configure_logging(options)
+
+ if len(args) != 1 and len(args) != 2:
+ logging.error('Usage: %s [options] <version tag>' % (sys.argv[0]))
+
+ snapshot_branch = None
+ target_version = None
+
+ archives = options.archive_types.split(',') if options.archive_types != '' else []
+ for archive_type in archives:
+ if archive_type not in ['tar', 'tgz', 'tbz', 'txz']:
+ logging.error('Unknown archive type "%s"' % (archive_type))
+
+ if args[0] == 'snapshot':
+ if len(args) != 2:
+ logging.error('Missing branch name for snapshot command')
+ snapshot_branch = args[1]
+ else:
+ if len(args) != 1:
+ logging.error('Usage error, try --help')
+ target_version = args[0]
+
+ if snapshot_branch:
+ logging.info('Creating snapshot release from branch %s', snapshot_branch)
+ target_version = snapshot_branch
+ elif len(args) == 1:
+ try:
+ logging.info('Creating release for version %s' % (target_version))
+ except ValueError as e:
+ logging.error('Invalid version number %s' % (target_version))
+
+ rev_id = revision_of(target_version)
+ if rev_id == '':
+ logging.error('No tag matching %s found' % (target_version))
+
+ rel_date, rel_epoch = datestamp(target_version)
+ if rel_date == 0 or rel_epoch == 0:
+ logging.error('No date found for version, git error?')
+
+ logging.info('Found %s at revision id %s released %d' % (target_version, rev_id, rel_date))
+
+ global GZIP_HEADER_TIME # pylint: disable=global-statement
+ GZIP_HEADER_TIME = rel_epoch
+
+ def output_name():
+ if snapshot_branch:
+ if snapshot_branch == 'master':
+ return 'Botan-snapshot-%s' % (rel_date)
+ else:
+ return 'Botan-snapshot-%s-%s' % (snapshot_branch, rel_date)
+ else:
+ return 'Botan-' + target_version
+
+ output_basename = output_name()
+
+ logging.debug('Output basename %s' % (output_basename))
+
+ if os.access(output_basename, os.X_OK):
+ logging.info('Removing existing output dir %s' % (output_basename))
+ shutil.rmtree(output_basename)
+
+ extract_revision(rev_id, output_basename)
+
+ all_files = []
+ for (curdir, _, files) in os.walk(output_basename):
+ all_files += [os.path.join(curdir, f) for f in files]
+ all_files.sort(key=lambda f: (os.path.dirname(f), os.path.basename(f)))
+
+ def find_version_file():
+
+ # location of file with version information has moved over time
+ for possible_version_file in ['src/build-data/version.txt', 'version.txt', 'botan_version.py']:
+ full_path = os.path.join(output_basename, possible_version_file)
+ if os.access(full_path, os.R_OK):
+ return full_path
+
+ logging.error('Cannot locate version file')
+ return None
+
+ version_file = find_version_file()
+
+ if not os.access(version_file, os.R_OK):
+ logging.error('Cannot read %s' % (version_file))
+
+ rewrite_version_file(version_file, target_version, snapshot_branch, rev_id, rel_date)
+
+ try:
+ os.makedirs(options.output_dir)
+ except OSError as e:
+ if e.errno != errno.EEXIST:
+ logging.error('Creating dir %s failed %s' % (options.output_dir, e))
+
+ output_files = []
+
+ hash_file = None
+ if options.write_hash_file is not None:
+ hash_file = open(options.write_hash_file, 'w')
+
+ for archive_type in archives:
+ output_files.append(write_archive(target_version,
+ output_basename,
+ archive_type,
+ rel_epoch,
+ all_files,
+ hash_file))
+
+ if hash_file is not None:
+ hash_file.close()
+
+ shutil.rmtree(output_basename)
+
+ if options.pgp_key_id != 'none':
+ if options.write_hash_file is not None:
+ output_files += gpg_sign(options.pgp_key_id, options.pgp_passphrase_file,
+ [options.write_hash_file], False)
+ else:
+ output_files += gpg_sign(options.pgp_key_id, options.pgp_passphrase_file,
+ output_files, True)
+
+ if options.output_dir != '.':
+ for output_file in output_files:
+ logging.debug('Moving %s to %s' % (output_file, options.output_dir))
+ shutil.move(output_file, os.path.join(options.output_dir, output_file))
+
+ if options.print_output_names:
+ for output_file in output_files:
+ print(output_file)
+
+ return 0
+
+if __name__ == '__main__':
+ try:
+ sys.exit(main())
+ except Exception as e: # pylint: disable=broad-except
+ logging.info(traceback.format_exc())
+ logging.error(e)
+ sys.exit(1)
diff --git a/comm/third_party/botan/src/scripts/docker-android.sh b/comm/third_party/botan/src/scripts/docker-android.sh
new file mode 100755
index 0000000000..22bbc41f43
--- /dev/null
+++ b/comm/third_party/botan/src/scripts/docker-android.sh
@@ -0,0 +1,11 @@
+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 --git a/comm/third_party/botan/src/scripts/ffi_decls.py b/comm/third_party/botan/src/scripts/ffi_decls.py
new file mode 100755
index 0000000000..b336e61f53
--- /dev/null
+++ b/comm/third_party/botan/src/scripts/ffi_decls.py
@@ -0,0 +1,113 @@
+#!/usr/bin/python
+
+"""
+Automatically generate declarations for the FFI layer
+
+(C) 2019 Jack Lloyd
+
+Botan is released under the Simplified BSD License (see license.txt)
+"""
+
+from pycparser import c_ast, parse_file
+
+ffi_header = 'src/lib/ffi/ffi.h'
+
+def to_ctype(typ, is_ptr):
+
+ if typ.startswith('botan_') and typ.endswith('_t'):
+ return 'c_void_p'
+
+ if is_ptr is False:
+ if typ == 'uint32':
+ return 'c_uint32'
+ elif typ == 'size_t':
+ return 'c_size_t'
+ elif typ == 'uint8_t':
+ return 'c_uint8'
+ elif typ == 'uint32_t':
+ return 'c_uint32'
+ elif typ == 'uint64_t':
+ return 'c_uint64'
+ elif typ == 'int':
+ return 'c_int'
+ elif typ == 'unsigned':
+ return 'c_uint'
+ else:
+ if typ == 'void':
+ return 'c_void_p'
+ elif typ in ['char', 'uint8_t']: # hack
+ return 'c_char_p'
+ elif typ == 'size_t':
+ return 'POINTER(c_size_t)'
+ #elif typ == 'uint8_t':
+ # return 'POINTER(c_uint8)'
+ elif typ == 'uint32_t':
+ return 'POINTER(c_uint32)'
+ elif typ == 'uint64_t':
+ return 'POINTER(c_uint64)'
+ elif typ == 'int':
+ return 'POINTER(c_int)'
+
+ raise Exception("Unknown type %s/%d" % (typ, is_ptr))
+
+GROUP = None
+
+class FuncDefVisitor(c_ast.NodeVisitor):
+ def visit_FuncDecl(self, node):
+
+ if not isinstance(node.type, c_ast.TypeDecl):
+ #print("ignoring", node.type)
+ return
+
+ if node.type.type.names != ['int']:
+ #print("ignoring", node.type)
+ return
+
+ # all functions returning ints:
+ fn_name = node.type.declname
+
+ fn_group = fn_name.split('_')[1]
+ if fn_group == 'privkey':
+ fn_group = 'pubkey' # hack
+
+ global GROUP
+
+ if fn_group != GROUP:
+ if fn_group in ['rng', 'hash', 'mac', 'cipher', 'block', 'mp', 'pubkey', 'pk', 'x509', 'hotp', 'totp', 'fpe']:
+ print("\n # ", fn_group.upper())
+ else:
+ print("")
+ GROUP = fn_group
+
+
+ fn_args = []
+
+ for param in node.args.params:
+
+ is_ptr = False
+ typ = None
+ if isinstance(param.type, c_ast.PtrDecl):
+ is_ptr = True
+ typ = param.type.type.type.names[0]
+ elif isinstance(param.type, c_ast.ArrayDecl):
+ is_ptr = True
+ typ = param.type.type.type.names[0]
+ else:
+ typ = param.type.type.names[0]
+
+ ctype = to_ctype(typ, is_ptr)
+ fn_args.append(ctype)
+
+ decl = " ffi_api(dll.%s," % (fn_name)
+ if len(fn_args) > 4:
+ decl += "\n "
+ else:
+ decl += ' '
+
+ decl += '[' + ', '.join(fn_args) + '])'
+
+ print(decl)
+
+ast = parse_file(ffi_header, use_cpp=True, cpp_args=['-Ibuild/include', '-std=c89', '-DBOTAN_DLL='])
+v = FuncDefVisitor()
+v.visit(ast)
diff --git a/comm/third_party/botan/src/scripts/fuzzer.xml b/comm/third_party/botan/src/scripts/fuzzer.xml
new file mode 100644
index 0000000000..686059f8c8
--- /dev/null
+++ b/comm/third_party/botan/src/scripts/fuzzer.xml
@@ -0,0 +1,17 @@
+<!-- This is a template for a config file for fuzzing with TLS-Attacker -->
+
+<startupCommandsHolder>
+ <serverCommand>$botan_cli </serverCommand>
+ <serverPort>$tls_port</serverPort>
+ <workflowFolder>$workflow_dir</workflowFolder>
+ <modifiedVariableTypes>TLS_CONSTANT,LENGTH,COUNT,PUBLIC_KEY,PADDING,SIGNATURE,PLAIN_PROTOCOL_MESSAGE</modifiedVariableTypes>
+ <outputFolder>/tmp/</outputFolder>
+ <startupCommandsList>
+ <startupCommands>
+ <fuzzerCommand>simple_fuzzer -connect localhost:$PORT</fuzzerCommand>
+ <serverCommandParameters>tls_server $rsa_cert $rsa_key --port=$PORT --policy=$fuzz_policy --dump-traces=/tmp/tls/ --output=/tmp/botan_output.log --error-output=/tmp/botan_error_output.log </serverCommandParameters>
+ <shortName>botan-rsa</shortName>
+ </startupCommands>
+ <!-- TODO ECDSA -->
+ </startupCommandsList>
+</startupCommandsHolder>
diff --git a/comm/third_party/botan/src/scripts/gen_os_features.py b/comm/third_party/botan/src/scripts/gen_os_features.py
new file mode 100755
index 0000000000..bfcfac7d19
--- /dev/null
+++ b/comm/third_party/botan/src/scripts/gen_os_features.py
@@ -0,0 +1,95 @@
+#!/usr/bin/env python3
+
+"""
+A script to automatically write docs to /docs. Currently it generates
+os.rst, a feature table of OS features.
+
+Requires Python 3.
+
+(C) 2015 Simon Warta (Kullo GmbH)
+
+Botan is released under the Simplified BSD License (see license.txt)
+"""
+
+# global
+import argparse
+import glob
+import os
+import sys
+
+# Assume this script is in botan/src/scripts
+botan_root = os.path.join(os.path.dirname(sys.argv[0]), "..", "..")
+
+# locale
+sys.path.append(botan_root)
+from configure import OsInfo
+
+parser = argparse.ArgumentParser(description="")
+parser.add_argument('--verbose', dest='verbose', action='store_const',
+ const=True, default=False,
+ help='Verbose output (default: false)')
+args = parser.parse_args()
+
+def update_os():
+ TABLE_TITLE="OS Features"
+
+ files = []
+ files += glob.glob(botan_root + '/src/build-data/os/*.txt')
+ files.sort()
+
+ if len(files) == 0:
+ print("No info.txt files found.")
+ sys.exit(1)
+
+ f1 = open(os.path.join(botan_root, 'doc', 'dev_ref', 'os.rst'), 'w+')
+
+ all_features = set()
+ oss = {}
+
+ for filename in files:
+ o = OsInfo(filename)
+ oss[o.basename] = o
+ all_features |= set(o.target_features)
+ if args.verbose:
+ print(o.basename)
+ print(o.target_features)
+
+ featurelist = list(all_features)
+ featurelist.sort()
+ oslist = list(oss.keys())
+ oslist.sort()
+
+ if args.verbose:
+ print(featurelist)
+
+ print(TABLE_TITLE, file=f1)
+ print("========================================", file=f1)
+ print("", file=f1)
+
+ print("A summary of OS features as defined in ``src/build-data/os``.", file=f1)
+ print("", file=f1)
+
+ print("::", file=f1)
+ print("", file=f1)
+ for o in oslist:
+ print(" %s: %s" % (o[0:1], o), file=f1)
+ print("", file=f1)
+
+ print('.. csv-table::', file=f1)
+ print(' :header: "Feature", "' + '", "'.join([o[0:1] for o in oslist]) + '"', file=f1)
+ print('', file=f1)
+
+ for f in featurelist:
+ line = ' "' + f + '"'
+ for o in oslist:
+ line += ', "'
+ 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 --git a/comm/third_party/botan/src/scripts/install.py b/comm/third_party/botan/src/scripts/install.py
new file mode 100755
index 0000000000..32a7a7e9e8
--- /dev/null
+++ b/comm/third_party/botan/src/scripts/install.py
@@ -0,0 +1,261 @@
+#!/usr/bin/env python
+
+"""
+Botan install script
+
+(C) 2014,2015,2017 Jack Lloyd
+
+Botan is released under the Simplified BSD License (see license.txt)
+"""
+
+import errno
+import json
+import logging
+import optparse # pylint: disable=deprecated-module
+import os
+import shutil
+import sys
+import traceback
+
+def parse_command_line(args):
+
+ parser = optparse.OptionParser()
+
+ parser.add_option('--verbose', action='store_true', default=False,
+ help='Show debug messages')
+ parser.add_option('--quiet', action='store_true', default=False,
+ help='Show only warnings and errors')
+
+ build_group = optparse.OptionGroup(parser, 'Source options')
+ build_group.add_option('--build-dir', metavar='DIR', default='build',
+ help='Location of build output (default \'%default\')')
+ parser.add_option_group(build_group)
+
+ install_group = optparse.OptionGroup(parser, 'Installation options')
+ install_group.add_option('--prefix', default='/usr/local',
+ help='Set output directory (default %default)')
+ install_group.add_option('--bindir', default='bin', metavar='DIR',
+ help='Set binary subdir (default %default)')
+ install_group.add_option('--libdir', default='lib', metavar='DIR',
+ help='Set library subdir (default %default)')
+ install_group.add_option('--includedir', default='include', metavar='DIR',
+ help='Set include subdir (default %default)')
+ install_group.add_option('--docdir', default='share/doc', metavar='DIR',
+ help='Set documentation subdir (default %default)')
+ install_group.add_option('--pkgconfigdir', default='pkgconfig', metavar='DIR',
+ help='Set pkgconfig subdir (default %default)')
+
+ install_group.add_option('--umask', metavar='MASK', default='022',
+ help='Umask to set (default %default)')
+ parser.add_option_group(install_group)
+
+ (options, args) = parser.parse_args(args)
+
+ def log_level():
+ if options.verbose:
+ return logging.DEBUG
+ if options.quiet:
+ return logging.WARNING
+ return logging.INFO
+
+ logging.getLogger().setLevel(log_level())
+
+ return (options, args)
+
+
+class PrependDestdirError(Exception):
+ pass
+
+
+def is_subdir(path, subpath):
+ return os.path.relpath(path, start=subpath).startswith("..")
+
+
+def prepend_destdir(path):
+ """
+ Needed because os.path.join() discards the first path if the
+ second one is absolute, which is usually the case here. Still, we
+ want relative paths to work and leverage the os awareness of
+ os.path.join().
+ """
+ destdir = os.environ.get('DESTDIR', "")
+
+ if destdir:
+ # DESTDIR is non-empty, but we only join absolute paths on UNIX-like file systems
+ if os.path.sep != "/":
+ raise PrependDestdirError("Only UNIX-like file systems using forward slash " \
+ "separator supported when DESTDIR is set.")
+ if not os.path.isabs(path):
+ 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()
+ path = path.lstrip(os.path.sep)
+ path = os.path.join(destdir, path)
+
+ if not is_subdir(destdir, path):
+ raise PrependDestdirError("path escapes DESTDIR (path='%s', destdir='%s')" % (path, destdir))
+
+ return path
+
+
+def makedirs(dirname, exist_ok=True):
+ try:
+ logging.debug('Creating directory %s' % (dirname))
+ os.makedirs(dirname)
+ except OSError as e:
+ if e.errno != errno.EEXIST or not exist_ok:
+ raise e
+
+# Clear link and create new one
+def force_symlink(target, linkname):
+ try:
+ os.unlink(linkname)
+ except OSError as e:
+ if e.errno != errno.ENOENT:
+ raise e
+ os.symlink(target, linkname)
+
+def calculate_exec_mode(options):
+ out = 0o777
+ if 'umask' in os.__dict__:
+ umask = int(options.umask, 8)
+ logging.debug('Setting umask to %s' % oct(umask))
+ os.umask(int(options.umask, 8))
+ out &= (umask ^ 0o777)
+ return out
+
+def main(args):
+ # pylint: disable=too-many-locals,too-many-branches,too-many-statements
+
+ logging.basicConfig(stream=sys.stdout,
+ format='%(levelname) 7s: %(message)s')
+
+ (options, args) = parse_command_line(args)
+
+ exe_mode = calculate_exec_mode(options)
+
+ def copy_file(src, dst):
+ logging.debug('Copying %s to %s' % (src, dst))
+ shutil.copyfile(src, dst)
+
+ def copy_executable(src, dst):
+ copy_file(src, dst)
+ logging.debug('Make %s executable' % dst)
+ os.chmod(dst, exe_mode)
+
+ with open(os.path.join(options.build_dir, 'build_config.json')) as f:
+ cfg = json.load(f)
+
+ ver_major = int(cfg['version_major'])
+ ver_minor = int(cfg['version_minor'])
+ ver_patch = int(cfg['version_patch'])
+ target_os = cfg['os']
+ build_shared_lib = bool(cfg['build_shared_lib'])
+ build_static_lib = bool(cfg['build_static_lib'])
+ build_cli = bool(cfg['build_cli_exe'])
+ out_dir = cfg['out_dir']
+
+ bin_dir = options.bindir
+ lib_dir = options.libdir
+ target_include_dir = os.path.join(options.prefix,
+ options.includedir,
+ 'botan-%d' % (ver_major),
+ 'botan')
+
+ for d in [options.prefix, lib_dir, bin_dir, target_include_dir]:
+ makedirs(prepend_destdir(d))
+
+ build_include_dir = os.path.join(options.build_dir, 'include', 'botan')
+
+ for include in sorted(os.listdir(build_include_dir)):
+ if include == 'internal':
+ continue
+ copy_file(os.path.join(build_include_dir, include),
+ prepend_destdir(os.path.join(target_include_dir, include)))
+
+ build_external_include_dir = os.path.join(options.build_dir, 'include', 'external')
+
+ for include in sorted(os.listdir(build_external_include_dir)):
+ copy_file(os.path.join(build_external_include_dir, include),
+ prepend_destdir(os.path.join(target_include_dir, include)))
+
+ if build_static_lib or target_os == 'windows':
+ static_lib = cfg['static_lib_name']
+ copy_file(os.path.join(out_dir, static_lib),
+ prepend_destdir(os.path.join(lib_dir, os.path.basename(static_lib))))
+
+ if build_shared_lib:
+ if target_os == "windows":
+ libname = cfg['libname']
+ soname_base = libname + '.dll'
+ copy_executable(os.path.join(out_dir, soname_base),
+ prepend_destdir(os.path.join(bin_dir, soname_base)))
+ else:
+ soname_patch = cfg['soname_patch']
+ soname_abi = cfg['soname_abi']
+ soname_base = cfg['soname_base']
+
+ copy_executable(os.path.join(out_dir, soname_patch),
+ prepend_destdir(os.path.join(lib_dir, soname_patch)))
+
+ if target_os != "openbsd":
+ prev_cwd = os.getcwd()
+ try:
+ os.chdir(prepend_destdir(lib_dir))
+ force_symlink(soname_patch, soname_abi)
+ force_symlink(soname_patch, soname_base)
+ finally:
+ os.chdir(prev_cwd)
+
+ if build_cli:
+ copy_executable(cfg['cli_exe'], prepend_destdir(os.path.join(bin_dir, cfg['cli_exe_name'])))
+
+ if 'botan_pkgconfig' in cfg:
+ pkgconfig_dir = os.path.join(options.prefix, options.libdir, options.pkgconfigdir)
+ makedirs(prepend_destdir(pkgconfig_dir))
+ copy_file(cfg['botan_pkgconfig'],
+ prepend_destdir(os.path.join(pkgconfig_dir, os.path.basename(cfg['botan_pkgconfig']))))
+
+ if 'ffi' in cfg['mod_list'] and cfg['build_shared_lib'] is True and cfg['install_python_module'] is True:
+ for ver in cfg['python_version'].split(','):
+ py_lib_path = os.path.join(lib_dir, 'python%s' % (ver), 'site-packages')
+ logging.debug('Installing python module to %s' % (py_lib_path))
+ makedirs(prepend_destdir(py_lib_path))
+
+ py_dir = cfg['python_dir']
+
+ copy_file(os.path.join(py_dir, 'botan2.py'),
+ prepend_destdir(os.path.join(py_lib_path, 'botan2.py')))
+
+ if cfg['with_documentation']:
+ target_doc_dir = os.path.join(options.prefix, options.docdir,
+ 'botan-%d.%d.%d' % (ver_major, ver_minor, ver_patch))
+
+ shutil.rmtree(prepend_destdir(target_doc_dir), True)
+ shutil.copytree(cfg['doc_output_dir'], prepend_destdir(target_doc_dir))
+
+ copy_file(os.path.join(cfg['base_dir'], 'license.txt'),
+ prepend_destdir(os.path.join(target_doc_dir, 'license.txt')))
+ copy_file(os.path.join(cfg['base_dir'], 'news.rst'),
+ prepend_destdir(os.path.join(target_doc_dir, 'news.txt')))
+ for f in [f for f in os.listdir(cfg['doc_dir']) if f.endswith('.txt')]:
+ copy_file(os.path.join(cfg['doc_dir'], f), prepend_destdir(os.path.join(target_doc_dir, f)))
+
+ if cfg['with_rst2man']:
+ man1_dir = prepend_destdir(os.path.join(options.prefix, os.path.join(cfg['mandir'], 'man1')))
+ makedirs(man1_dir)
+
+ copy_file(os.path.join(cfg['build_dir'], 'botan.1'),
+ os.path.join(man1_dir, 'botan.1'))
+
+ logging.info('Botan %s installation complete', cfg['version'])
+ return 0
+
+if __name__ == '__main__':
+ try:
+ sys.exit(main(sys.argv))
+ except Exception as e: # pylint: disable=broad-except
+ logging.error('Failure: %s' % (e))
+ logging.info(traceback.format_exc())
+ sys.exit(1)
diff --git a/comm/third_party/botan/src/scripts/macro_checks.py b/comm/third_party/botan/src/scripts/macro_checks.py
new file mode 100755
index 0000000000..df1a503c6c
--- /dev/null
+++ b/comm/third_party/botan/src/scripts/macro_checks.py
@@ -0,0 +1,42 @@
+#!/usr/bin/python
+
+# (C) 2018 Jack Lloyd
+# Botan is released under the Simplified BSD License (see license.txt)
+
+# Scans all source and test files and makes sure we are not using a
+# BOTAN_HAS_xxx macro which is not actually defined anywhere.
+
+from configure import ModuleInfo, load_info_files
+import os
+import re
+import logging
+
+src_dir = 'src'
+lib_dir = os.path.join(src_dir, 'lib')
+
+info_modules = load_info_files(lib_dir, 'Modules', "info.txt", ModuleInfo)
+
+all_defines = set()
+
+for module in info_modules.values():
+ for define in module._defines:
+ all_defines.add(define)
+
+extras = ['MP_DWORD', 'VALGRIND', 'SANITIZER_UNDEFINED',
+ 'ONLINE_REVOCATION_CHECKS', 'NIST_PRIME_REDUCERS_W32']
+
+for extra in extras:
+ all_defines.add(extra)
+
+macro = re.compile('BOTAN_HAS_([A-Z0-9_]+)')
+
+for dirname, subdirs, files in os.walk(src_dir):
+ for fname in files:
+ if fname.endswith('.h') or fname.endswith('.cpp'):
+ contents = open(os.path.join(dirname, fname)).read()
+
+ for m in re.finditer(macro, contents):
+
+ if m.group(1) not in all_defines:
+ logging.error('In %s found unknown feature macro %s' % (fname, m.group(1)))
+
diff --git a/comm/third_party/botan/src/scripts/monty.py b/comm/third_party/botan/src/scripts/monty.py
new file mode 100755
index 0000000000..f253da3f67
--- /dev/null
+++ b/comm/third_party/botan/src/scripts/monty.py
@@ -0,0 +1,98 @@
+#!/usr/bin/python3
+
+import sys
+import datetime
+
+# (C) 2018 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):
+
+ lines = []
+
+ lines.append("word w2 = 0, w1 = 0, w0 = 0;")
+ lines.append("w0 = z[0];")
+ lines.append("ws[0] = w0 * p_dash;")
+
+ lines.append("word3_muladd(&w2, &w1, &w0, ws[0], p[0]);")
+ lines.append("w0 = w1; w1 = w2; w2 = 0;")
+
+ for i in range(1, n):
+ for j in range(0, i):
+ lines.append("word3_muladd(&w2, &w1, &w0, ws[%d], p[%d]);" % (j, i-j))
+
+ lines.append("word3_add(&w2, &w1, &w0, z[%d]);" % (i))
+ lines.append("ws[%d] = w0 * p_dash;" % (i))
+
+ lines.append("word3_muladd(&w2, &w1, &w0, ws[%d], p[0]);" % (i))
+ lines.append("w0 = w1; w1 = w2; w2 = 0;")
+
+ for i in range(0, n):
+ for j in range(i + 1, n):
+ lines.append("word3_muladd(&w2, &w1, &w0, ws[%d], p[%d]);" % (j, n + i-j))
+
+ lines.append("word3_add(&w2, &w1, &w0, z[%d]);" % (n+i))
+ lines.append("ws[%d] = w0;" % (i))
+ lines.append("w0 = w1; w1 = w2; w2 = 0;")
+
+ lines.append("word3_add(&w2, &w1, &w0, z[%d]);" % (2*(n+1) - 1))
+
+ lines.append("ws[%d] = w0;" % (n))
+ lines.append("ws[%d] = w1;" % (n+1))
+
+ if n < 16:
+ lines.append("word borrow = 0;")
+ for i in range(n):
+ lines.append("ws[%d] = word_sub(ws[%d], p[%d], &borrow);" % (n + 1 + i, i, i))
+ lines.append("ws[%d] = word_sub(ws[%d], 0, &borrow);" % (2*n+1, n))
+ else:
+ lines.append("word borrow = bigint_sub3(ws + %d + 1, ws, %d + 1, p, %d);" % (n, n, n))
+
+ lines.append("CT::conditional_copy_mem(borrow, z, ws, ws + %d, %d);" % (n + 1, n + 1))
+ lines.append("clear_mem(z + %d, 2*(%d+1) - %d);" % (n, n, n))
+
+ for line in lines:
+ print(" %s" % (line))
+
+def main(args = None):
+ if args is None:
+ args = sys.argv
+
+ if len(args) <= 1:
+ sizes = [4, 6, 8, 16, 24, 32]
+ else:
+ sizes = map(int, args[1:])
+
+ print("""/*
+* 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)
+*/
+
+#include <botan/internal/mp_monty.h>
+#include <botan/internal/mp_core.h>
+#include <botan/internal/mp_asmi.h>
+#include <botan/internal/ct_utils.h>
+
+namespace Botan {
+""" % (sys.argv[0], datetime.date.today().strftime("%Y-%m-%d")))
+
+ for n in sizes:
+ print("void bigint_monty_redc_%d(word z[], const word p[%d], word p_dash, word ws[])" % (n, n))
+ print(" {")
+
+ monty_redc_code(n)
+
+ print(" }\n")
+
+ print("}")
+
+ return 0
+
+if __name__ == '__main__':
+ sys.exit(main())
+
+
diff --git a/comm/third_party/botan/src/scripts/oids.py b/comm/third_party/botan/src/scripts/oids.py
new file mode 100755
index 0000000000..323b6efb50
--- /dev/null
+++ b/comm/third_party/botan/src/scripts/oids.py
@@ -0,0 +1,337 @@
+#!/usr/bin/python
+
+"""
+(C) 2016 Jack Lloyd
+(C) 2017 Fabian Weissberg, Rohde & Schwarz Cybersecurity
+
+Botan is released under the Simplified BSD License (see license.txt)
+"""
+
+import sys
+import datetime
+import re
+from collections import defaultdict
+
+def format_oid(oid):
+ #return '"' + oid + '"'
+ 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 += '{ "%s", "%s" },\n' % (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 <botan/oids.h>
+#include <unordered_map>
+
+namespace Botan {
+
+std::unordered_map<std::string, std::string> OIDS::load_oid2str_map()
+ {
+ return std::unordered_map<std::string,std::string>{
+ %s
+ };
+ }
+
+std::unordered_map<std::string, OID> OIDS::load_str2oid_map()
+ {
+ return std::unordered_map<std::string,OID>{
+ %s
+ };
+ }
+
+}
+""" % (sys.argv[0], datetime.date.today().strftime("%Y-%m-%d"),
+ format_map(oid2str), format_map(str2oid, True))
+
+
+def format_if(m, nm,t=False):
+ s = ''
+ for k in sorted(m.keys()):
+ v = m[k]
+
+ if t:
+ s += ' if(%s == "%s") return OID(%s);\n' % (nm,k, format_oid(v))
+ else:
+ s += ' if(%s == "%s") return "%s";\n' % (nm,k, v)
+
+ s = s[:-1]
+
+ return s
+
+def format_as_ifs(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 <botan/oids.h>
+
+namespace Botan {
+
+namespace OIDS {
+
+std::string lookup(const OID& oid)
+ {
+ const std::string oid_str = oid.to_string();
+%s
+
+ return std::string();
+ }
+
+OID lookup(const std::string& name)
+ {
+%s
+
+ return OID();
+ }
+
+}
+
+}
+""" % (sys.argv[0], datetime.date.today().strftime("%Y-%m-%d"),
+ format_if(oid2str,"oid_str"), format_if(str2oid, "name", True))
+
+
+def format_dn_ub_map(dn_ub, oid2str):
+ s = ''
+ for k in sorted(dn_ub.keys()):
+ v = dn_ub[k]
+
+ s += ' { Botan::OID({%s}), %s }, // %s\n' % (k.replace('.',','),v,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 <botan/x509_dn.h>
+#include <botan/asn1_oid.h>
+#include <map>
+
+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.
+ */
+static const std::map<Botan::OID, size_t> DN_UB =
+ {
+%s
+ };
+}
+
+namespace Botan {
+
+//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;
+ }
+ }
+}
+""" % (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) is not 0:
+ s = s[:-2]
+ s += '} },\n'
+ s = s[:-1]
+ return s
+
+
+def format_pads_as_map(sig_dict):
+ return """/*
+* Sets of allowed padding schemes for public key types
+*
+* 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 <botan/internal/padding.h>
+#include <unordered_map>
+#include <vector>
+#include <string>
+#include <algorithm>
+
+namespace Botan {
+
+namespace {
+
+const std::unordered_map<const std::string, std::vector<std::string>> allowed_signature_paddings =
+ {
+ %s
+ };
+
+}
+
+const std::vector<std::string> get_sig_paddings(const std::string algo)
+ {
+ auto i = allowed_signature_paddings.find(algo);
+ if(i != allowed_signature_paddings.end())
+ return i->second;
+ return {};
+ }
+
+bool sig_algo_and_pad_ok(const std::string algo, std::string padding)
+ {
+ const std::vector<std::string> pads = get_sig_paddings(algo);
+ return std::find(pads.begin(), pads.end(), padding) != pads.end();
+ }
+
+}
+""" % (sys.argv[0], datetime.date.today().strftime("%Y-%m-%d"),
+ format_set_map(sig_dict))
+
+
+def main(args = None):
+ """ Print header files (oids.cpp, dn_ub.cpp) depending on the first argument and on src/build-data/oids.txt
+
+ 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
+ Choose 'pads' to print padding.cpp, needs to be written to src/lib/pk_pad/padding.cpp
+ """
+ if args is None:
+ args = sys.argv
+ if len(args) < 2:
+ raise Exception("Use either 'oids', 'dn_ub', 'pads' as first argument")
+
+ oid_lines = open('./src/build-data/oids.txt').readlines()
+
+ oid_re = re.compile("^([0-9][0-9.]+) = ([A-Za-z0-9_\./\(\), -]+)(?: = )?([0-9]+)?$")
+ hdr_re = re.compile("^\[([a-z0-9_]+)\]$")
+ pad_re = re.compile("^([A-Za-z0-9_\., -]+)/([A-Za-z0-9_-]+)[A-Za-z0-9_\.\(\), -]*$")
+
+ oid2str = {}
+ str2oid = {}
+ dn_ub = {}
+ sig2pads = defaultdict(set)
+ cur_hdr = None
+
+ for line in oid_lines:
+ line = line.strip()
+ if len(line) == 0:
+ continue
+
+ if line[0] == '#':
+ continue
+
+ match = hdr_re.match(line)
+ if match is not None:
+ cur_hdr = match.group(1)
+ continue
+
+ match = oid_re.match(line)
+ if match is None:
+ raise Exception(line)
+
+ oid = match.group(1)
+ nam = 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)
+ # parse signature paddings
+ elif cur_hdr == "signature":
+ pad_match = pad_re.search(nam)
+ if pad_match is not None:
+ sig2pads[pad_match.group(1)].add(pad_match.group(2))
+
+ 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))
+ elif args[1] == "pads":
+ print(format_pads_as_map(sig2pads))
+ else:
+ print("Unknown command: try oids, dn_ub, or pads")
+
+ return 0
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/comm/third_party/botan/src/scripts/python_unittests.py b/comm/third_party/botan/src/scripts/python_unittests.py
new file mode 100755
index 0000000000..a6a22f3f0b
--- /dev/null
+++ b/comm/third_party/botan/src/scripts/python_unittests.py
@@ -0,0 +1,224 @@
+#!/usr/bin/env python3
+
+"""
+Unittests for Botan Python scripts.
+
+Requires Python 3.
+
+(C) 2017 Simon Warta (Kullo GmbH)
+
+Botan is released under the Simplified BSD License (see license.txt)
+"""
+
+import sys
+import unittest
+
+sys.path.append("../..") # Botan repo root
+from configure import AmalgamationHelper # pylint: disable=wrong-import-position
+from configure import ModulesChooser # pylint: disable=wrong-import-position
+
+class AmalgamationHelperTests(unittest.TestCase):
+ def test_matcher_std_includes(self):
+ self.assertEqual(AmalgamationHelper.is_unconditional_std_include("#include <string>"), "string")
+ self.assertEqual(AmalgamationHelper.is_unconditional_std_include("#include <string> // comment"), "string")
+
+ self.assertEqual(AmalgamationHelper.is_unconditional_std_include("#include <myfile.h>"), None)
+ self.assertEqual(AmalgamationHelper.is_unconditional_std_include("#include <unistd.h>"), None)
+ self.assertEqual(AmalgamationHelper.is_unconditional_std_include(" #include <string>"), None)
+
+ def test_matcher_botan_include(self):
+ self.assertEqual(AmalgamationHelper.is_botan_include("#include <botan/oids.h>"),
+ "oids.h")
+ self.assertEqual(AmalgamationHelper.is_botan_include("#include <botan/internal/socket.h>"),
+ "internal/socket.h")
+ self.assertEqual(AmalgamationHelper.is_botan_include("#include <botan/oids.h> // comment"),
+ "oids.h")
+ self.assertEqual(AmalgamationHelper.is_botan_include("#include <botan/internal/socket.h> // comment"),
+ "internal/socket.h")
+ self.assertEqual(AmalgamationHelper.is_botan_include(" #include <botan/oids.h>"),
+ "oids.h")
+ self.assertEqual(AmalgamationHelper.is_botan_include(" #include <botan/internal/socket.h>"),
+ "internal/socket.h")
+
+ self.assertEqual(AmalgamationHelper.is_botan_include("#include <string>"), None)
+ self.assertEqual(AmalgamationHelper.is_botan_include("#include <myfile.h>"), None)
+ self.assertEqual(AmalgamationHelper.is_botan_include("#include <unistd.h>"), None)
+
+ def test_matcher_any_includes(self):
+ self.assertEqual(AmalgamationHelper.is_any_include("#include <string>"), "string")
+ self.assertEqual(AmalgamationHelper.is_any_include("#include <myfile.h>"), "myfile.h")
+ self.assertEqual(AmalgamationHelper.is_any_include("#include <unistd.h>"), "unistd.h")
+ self.assertEqual(AmalgamationHelper.is_any_include("#include <botan/oids.h>"),
+ "botan/oids.h")
+ self.assertEqual(AmalgamationHelper.is_any_include(" #include <string>"), "string")
+ self.assertEqual(AmalgamationHelper.is_any_include(" #include <myfile.h>"), "myfile.h")
+ self.assertEqual(AmalgamationHelper.is_any_include(" #include <unistd.h>"), "unistd.h")
+ self.assertEqual(AmalgamationHelper.is_any_include(" #include <botan/oids.h>"),
+ "botan/oids.h")
+ self.assertEqual(AmalgamationHelper.is_any_include("#include <string> // comment"), "string")
+ self.assertEqual(AmalgamationHelper.is_any_include("#include <myfile.h> // comment"), "myfile.h")
+ self.assertEqual(AmalgamationHelper.is_any_include("#include <unistd.h> // comment"), "unistd.h")
+ self.assertEqual(AmalgamationHelper.is_any_include("#include <botan/oids.h> // comment"),
+ "botan/oids.h")
+
+class ModulesChooserResolveDependencies(unittest.TestCase):
+ def test_base(self):
+ available_modules = set(["A", "B"])
+ table = {
+ "A": [],
+ }
+ ok, modules = ModulesChooser.resolve_dependencies(available_modules, table, "A")
+ self.assertTrue(ok)
+ self.assertEqual(modules, set(["A"]))
+
+ def test_no_dependencies_defined(self):
+ available_modules = set(["A", "B"])
+ table = {
+ "A": [],
+ }
+ with self.assertRaises(KeyError):
+ ModulesChooser.resolve_dependencies(available_modules, table, "B")
+
+ available_modules = set(["A", "B"])
+ table = {
+ "A": ["B"],
+ }
+ with self.assertRaises(KeyError):
+ ModulesChooser.resolve_dependencies(available_modules, table, "A")
+
+ def test_add_dependency(self):
+ available_modules = set(["A", "B"])
+ table = {
+ "A": ["B"],
+ "B": []
+ }
+ ok, modules = ModulesChooser.resolve_dependencies(available_modules, table, "A")
+ self.assertTrue(ok)
+ self.assertEqual(modules, set(["A", "B"]))
+
+ def test_add_dependencies_two_levels(self):
+ available_modules = set(["A", "B", "C"])
+ table = {
+ "A": ["B"],
+ "B": ["C"],
+ "C": []
+ }
+ ok, modules = ModulesChooser.resolve_dependencies(available_modules, table, "A")
+ self.assertTrue(ok)
+ self.assertEqual(modules, set(["A", "B", "C"]))
+
+ def test_circular(self):
+ available_modules = set(["A", "B", "C"])
+ table = {
+ "A": ["B"],
+ "B": ["C"],
+ "C": ["A"]
+ }
+ ok, modules = ModulesChooser.resolve_dependencies(available_modules, table, "A")
+ self.assertTrue(ok)
+ self.assertEqual(modules, set(["A", "B", "C"]))
+
+ def test_not_available(self):
+ available_modules = set(["A", "C"])
+ table = {
+ "A": ["B"],
+ "B": ["C"],
+ "C": ["A"]
+ }
+ ok, _ = ModulesChooser.resolve_dependencies(available_modules, table, "B")
+ self.assertFalse(ok)
+
+ def test_dependency_not_available(self):
+ available_modules = set(["A", "C"])
+ table = {
+ "A": ["B"],
+ "B": ["C"],
+ "C": ["A"]
+ }
+ ok, _ = ModulesChooser.resolve_dependencies(available_modules, table, "A")
+ self.assertFalse(ok)
+
+ def test_dependency2_not_available(self):
+ available_modules = set(["A", "B"])
+ table = {
+ "A": ["B"],
+ "B": ["C"],
+ "C": ["A"]
+ }
+ ok, _ = ModulesChooser.resolve_dependencies(available_modules, table, "A")
+ self.assertFalse(ok)
+
+ def test_dependency_choices(self):
+ available_modules = set(["A", "B", "C"])
+ table = {
+ "A": ["B|C"],
+ "B": [],
+ "C": []
+ }
+ ok, modules = ModulesChooser.resolve_dependencies(available_modules, table, "A")
+ self.assertTrue(ok)
+ self.assertTrue(modules == set(["A", "B"]) or modules == set(["A", "C"]))
+
+ def test_dependency_prefer_existing(self):
+ available_modules = set(["A", "B", "C"])
+ table = {
+ "A": ["C", "B|C"],
+ "B": [],
+ "C": []
+ }
+ ok, modules = ModulesChooser.resolve_dependencies(available_modules, table, "A")
+ self.assertTrue(ok)
+ self.assertEqual(modules, set(["A", "C"]))
+
+ def test_dependency_prefer_existing2(self):
+ available_modules = set(["A", "B", "C"])
+ table = {
+ "A": ["B", "B|C"],
+ "B": [],
+ "C": []
+ }
+ ok, modules = ModulesChooser.resolve_dependencies(available_modules, table, "A")
+ self.assertTrue(ok)
+ self.assertEqual(modules, set(["A", "B"]))
+
+ def test_dependency_choices_impossible(self):
+ available_modules = set(["A", "C"])
+ table = {
+ "A": ["B|C"],
+ "B": [],
+ "C": []
+ }
+ ok, modules = ModulesChooser.resolve_dependencies(available_modules, table, "A")
+ self.assertTrue(ok)
+ self.assertEqual(modules, set(["A", "C"]))
+
+ def test_dependency_choices_impossible2(self):
+ available_modules = set(["A", "B"])
+ table = {
+ "A": ["B|C"],
+ "B": [],
+ "C": []
+ }
+ ok, modules = ModulesChooser.resolve_dependencies(available_modules, table, "A")
+ self.assertTrue(ok)
+ self.assertEqual(modules, set(["A", "B"]))
+
+ def test_deep(self):
+ available_modules = set(["A", "B", "C", "E", "G"])
+ table = {
+ "A": ["B|C"],
+ "B": ["D"],
+ "C": ["E"],
+ "D": [],
+ "E": ["F|G"],
+ "F": ["A", "B"],
+ "G": ["A", "G"]
+ }
+ ok, modules = ModulesChooser.resolve_dependencies(available_modules, table, "G")
+ self.assertTrue(ok)
+ self.assertEqual(modules, set(["G", "A", "C", "E"]))
+
+
+if __name__ == '__main__':
+ unittest.TestCase.longMessage = True
+ unittest.main()
diff --git a/comm/third_party/botan/src/scripts/python_unittests_unix.py b/comm/third_party/botan/src/scripts/python_unittests_unix.py
new file mode 100755
index 0000000000..fe9f06a62a
--- /dev/null
+++ b/comm/third_party/botan/src/scripts/python_unittests_unix.py
@@ -0,0 +1,67 @@
+#!/usr/bin/env python3
+
+"""
+Unittests for Botan Python scripts. Those tests only need to pass un UNIX-like
+operating systems.
+
+Requires Python 3.
+
+(C) 2017 Simon Warta (Kullo GmbH)
+
+Botan is released under the Simplified BSD License (see license.txt)
+"""
+
+import os
+import sys
+import unittest
+
+sys.path.append("../..") # Botan repo root
+from install import prepend_destdir # pylint: disable=wrong-import-position
+from install import PrependDestdirError # pylint: disable=wrong-import-position
+
+
+class PrependDestdir(unittest.TestCase):
+ def test_absolute_destdir(self):
+ os.environ["DESTDIR"] = "/"
+ self.assertEqual(prepend_destdir("/home/me"), "/home/me")
+ self.assertEqual(prepend_destdir("/home/me/"), "/home/me")
+ self.assertEqual(prepend_destdir("/home/me/../me2"), "/home/me2")
+
+ os.environ["DESTDIR"] = "/opt"
+ self.assertEqual(prepend_destdir("/home/me"), "/opt/home/me")
+ self.assertEqual(prepend_destdir("/home/me/"), "/opt/home/me")
+ self.assertEqual(prepend_destdir("/home/me/../me2"), "/opt/home/me2")
+
+ def test_relative_destdir(self):
+ os.environ["DESTDIR"] = "."
+ self.assertEqual(prepend_destdir("/home/me"), "./home/me")
+ self.assertEqual(prepend_destdir("/home/me/"), "./home/me")
+ self.assertEqual(prepend_destdir("/home/me/../me2"), "./home/me2")
+
+ os.environ["DESTDIR"] = "bar"
+ self.assertEqual(prepend_destdir("/home/me"), "bar/home/me")
+ self.assertEqual(prepend_destdir("/home/me/"), "bar/home/me")
+ self.assertEqual(prepend_destdir("/home/me/../me2"), "bar/home/me2")
+
+ def test_relative(self):
+ # No destdir set
+ os.environ["DESTDIR"] = ""
+ self.assertEqual(prepend_destdir("foo"), "foo")
+ self.assertEqual(prepend_destdir("../foo"), "../foo")
+
+ # Destdir set
+ os.environ["DESTDIR"] = "/opt"
+ with self.assertRaises(PrependDestdirError):
+ prepend_destdir("foo")
+ with self.assertRaises(PrependDestdirError):
+ prepend_destdir("../foo")
+
+ def test_escaping(self):
+ os.environ["DESTDIR"] = "/opt"
+ with self.assertRaises(PrependDestdirError):
+ prepend_destdir("/foo/../..")
+
+
+if __name__ == '__main__':
+ unittest.TestCase.longMessage = True
+ unittest.main()
diff --git a/comm/third_party/botan/src/scripts/run_tls_attacker.py b/comm/third_party/botan/src/scripts/run_tls_attacker.py
new file mode 100755
index 0000000000..a773646334
--- /dev/null
+++ b/comm/third_party/botan/src/scripts/run_tls_attacker.py
@@ -0,0 +1,138 @@
+#!/usr/bin/python
+
+import os
+import sys
+import subprocess
+import tempfile
+import time
+import random
+import optparse
+import string
+
+def run_subprocess(cmd):
+ print("Running '%s'" % (' '.join(cmd)))
+
+ proc = subprocess.Popen(cmd, bufsize=-1)
+ proc.communicate()
+
+ if proc.returncode != 0:
+ print('Running "%s" failed rc %d' % (' '.join(cmd), proc.returncode))
+ sys.exit(proc.returncode)
+
+def spawn_server(cmd):
+ print("Spawning '%s'" % (' '.join(cmd)))
+ return subprocess.Popen(cmd, bufsize=-1)#,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
+
+def main(args=None):
+ if args is None:
+ args = sys.argv
+
+ parser = optparse.OptionParser()
+
+ parser.add_option('--type', default='tests',
+ help='Which TLS-Attacker tests to run (tests, policy, fuzzer)')
+ parser.add_option('--src-dir', metavar='DIR', default='./src',
+ help='Specify path to botan sources (default "%default")')
+ parser.add_option('--verbose', action='store_true',
+ help='Be noisy')
+
+ (options, args) = parser.parse_args(args)
+
+ if len(args) != 3:
+ print("Usage: %s botan_cli_exe botan_ci_tools" % (args[0]))
+ return 1
+
+ cli_exe = args[1]
+ ci_tools = args[2]
+ test_type = options.type
+ src_dir = options.src_dir
+
+ if test_type not in ['tests', 'policy', 'fuzzer']:
+ print("Unknown --type %s" % (options.test_type))
+ return 1
+
+ if os.access(cli_exe, os.X_OK) != True:
+ print("Unable to find CLI tool at %s" % (cli_exe))
+ return 1
+
+ if os.access(src_dir, os.X_OK) != True:
+ print("Unable to find src dir at %s" % (src_dir))
+ return 1
+
+ test_data_dir = os.path.join(src_dir, 'tests/data')
+
+ lax_policy_txt = os.path.join(test_data_dir, 'tls-policy/compat.txt')
+ bsi_policy_txt = os.path.join(test_data_dir, 'tls-policy/bsi.txt')
+
+ tls_attacker_dir = os.path.join(ci_tools, 'TLS-Attacker')
+ tls_attacker_jar = os.path.join(tls_attacker_dir, 'TLS-Attacker-1.2.jar')
+ 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:
+ print("Unable to find TLS-Attacker jar at %s" % (tls_attacker_jar))
+ return 1
+
+ rsa_key = tempfile.NamedTemporaryFile(prefix='rsa_key_')
+ rsa_crt = tempfile.NamedTemporaryFile(prefix='rsa_crt_')
+
+ run_subprocess([cli_exe, 'keygen', '--algo=RSA', '--params=2048', '--output=%s' % (rsa_key.name)])
+ run_subprocess([cli_exe, 'gen_self_signed', rsa_key.name, 'localhost', '--output=%s' % (rsa_crt.name)])
+
+ server_log = 'botan_log.txt'
+ server_err_log = 'botan_err_log.txt'
+
+ tls_port = random.randint(50000, 60000)
+
+ botan_server_cmd = [cli_exe, 'tls_server', rsa_crt.name, rsa_key.name,
+ '--port=%d' % (tls_port),
+ '--output='+server_log,
+ '--error-output='+server_err_log]
+
+ java_tls_attacker = ['java', '-jar', tls_attacker_jar,
+ '-loglevel', 'DEBUG' if options.verbose else 'ERROR']
+ tls_attacker_opts = ['-tls_timeout', '300', '-connect', 'localhost:%d' % (tls_port)]
+
+ if test_type == 'tests':
+ try:
+ server_process = spawn_server(botan_server_cmd +
+ ['--policy=%s' % (lax_policy_txt)])
+ time.sleep(1)
+ run_subprocess(java_tls_attacker + ['testsuite_server'] + tls_attacker_opts +
+ ['-folder', tls_attacker_testsuites])
+ finally:
+ server_process.terminate()
+ elif test_type == 'policy':
+ try:
+ server_process = spawn_server(botan_server_cmd +
+ ['--policy=%s' % (bsi_policy_txt)])
+ time.sleep(1)
+ run_subprocess(java_tls_attacker + ['testtls_server'] + tls_attacker_opts +
+ ['-policy', bsi_policy_txt])
+ finally:
+ server_process.terminate()
+ elif test_type == 'fuzzer':
+
+ template_mapping = {
+ 'rsa_key': rsa_key.name,
+ 'rsa_cert': rsa_crt.name,
+ 'botan_cli': cli_exe,
+ 'workflow_dir': tls_fuzzer_workflows,
+ 'fuzz_policy': lax_policy_txt,
+ 'tls_port': str(tls_port),
+ 'PORT': '$PORT' # this is a var for TLS-Attacker don't touch it
+ }
+
+ template_txt = open(os.path.join(src_dir, 'scripts/fuzzer.xml')).read()
+
+ config = string.Template(template_txt).substitute(template_mapping)
+
+ fuzzer_config = tempfile.NamedTemporaryFile(prefix='fuzzer_cfg_', delete=False)
+ fuzzer_config.write(config.encode('ascii'))
+ fuzzer_config.close()
+
+ run_subprocess(java_tls_attacker + ['multi_fuzzer'] +
+ ['-startup_command_file', fuzzer_config.name])
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/comm/third_party/botan/src/scripts/run_tls_fuzzer.py b/comm/third_party/botan/src/scripts/run_tls_fuzzer.py
new file mode 100755
index 0000000000..b4ee91d247
--- /dev/null
+++ b/comm/third_party/botan/src/scripts/run_tls_fuzzer.py
@@ -0,0 +1,98 @@
+#!/usr/bin/python
+
+import argparse
+import subprocess
+import logging
+import sys
+import os
+import time
+
+def script_is_disabled(script_name):
+ if script_name.find('tls13') >= 0:
+ return True
+ if script_name.find('sslv2') >= 0:
+ return True
+
+ disabled = {
+ 'test-SSLv3-padding.py',
+ 'test-serverhello-random.py', # assumes support for SSLv2 hello
+ 'test-x25519.py', # assumes support for X448 (!)
+ }
+
+ if script_name in disabled:
+ return True
+
+ slow = {
+ 'test-bleichenbacher-workaround.py',
+ 'test-client-compatibility.py',
+ 'test-dhe-key-share-random.py',
+ 'test-dhe-no-shared-secret-padding.py',
+ 'test-ecdhe-padded-shared-secret.py',
+ 'test-ecdhe-rsa-key-share-random.py',
+ 'test-fuzzed-plaintext.py',
+ 'test-invalid-client-hello-w-record-overflow.py',
+ 'test-invalid-client-hello.py',
+ 'test-large-hello.py',
+ }
+ if script_name in slow:
+ return True
+
+ return False
+
+def main(args = None):
+ if args is None:
+ args = sys.argv[1:]
+
+ parser = argparse.ArgumentParser()
+
+ # TODO generate key and spawn the server on some random port in tmp dir
+ # TODO support running tls_server binary under valgrind
+
+ parser.add_argument('--verbose', action='store_true', default=False)
+ parser.add_argument('tls-fuzzer-dir')
+
+ args = vars(parser.parse_args(args))
+
+ tlsfuzzer_dir = args['tls-fuzzer-dir']
+
+ if not os.access(tlsfuzzer_dir, os.X_OK):
+ raise Exception("Unable to read TLS fuzzer dir")
+
+ tls_scripts_dir = os.path.join(tlsfuzzer_dir, 'scripts')
+ if not os.access(tlsfuzzer_dir, os.X_OK):
+ raise Exception("Unable to read TLS fuzzer scripts dir")
+
+ scripts = sorted(os.listdir(tls_scripts_dir))
+
+ procs = {}
+
+ for script in scripts:
+ if script_is_disabled(script):
+ logging.debug('Skipping %s' % (script))
+ continue
+
+ procs[script] = subprocess.Popen([sys.executable, os.path.join(tls_scripts_dir, script)],
+ cwd=tlsfuzzer_dir,
+ stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+
+ results = {}
+
+ while len(results) != len(procs):
+ time.sleep(.5)
+ for (script, proc) in procs.items():
+
+ if script in results:
+ continue
+
+ if proc.poll() != None:
+ rv = proc.returncode
+ results[script] = rv
+ if rv == 0:
+ print("PASS %s" % (script))
+ else:
+ print("FAIL %s" % (script))
+ sys.stdout.flush()
+ return 0
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/comm/third_party/botan/src/scripts/show_dependencies.py b/comm/third_party/botan/src/scripts/show_dependencies.py
new file mode 100755
index 0000000000..edf2d91e01
--- /dev/null
+++ b/comm/third_party/botan/src/scripts/show_dependencies.py
@@ -0,0 +1,213 @@
+#!/usr/bin/env python
+
+"""
+Show Botan module dependencies as a list or graph.
+
+Requires graphviz from pip when graphical output is selected:
+https://pypi.python.org/pypi/graphviz
+
+(C) 2015,2018 Simon Warta (Kullo GmbH)
+
+Botan is released under the Simplified BSD License (see license.txt)
+"""
+
+# global
+import argparse
+import copy
+import sys
+import subprocess
+from collections import OrderedDict
+import glob
+import os
+
+# Assume this script is in botan/src/scripts
+botan_root = os.path.join(os.path.dirname(sys.argv[0]), "..", "..")
+
+# locale
+sys.path.append(botan_root)
+from configure import ModuleInfo
+
+parser = argparse.ArgumentParser(description=
+ 'Show Botan module dependencies. '
+ 'The output is reduced by indirect dependencies, '
+ 'i.e. you must look at the result recursively to get all dependencies.')
+
+parser.add_argument('mode',
+ choices=["list", "draw"],
+ help='The output mode')
+parser.add_argument('--format',
+ nargs='?',
+ choices=["pdf", "png"],
+ default="pdf",
+ help='The file format (drawing mode only)')
+parser.add_argument('--engine',
+ nargs='?',
+ choices=["fdp", "dot"],
+ default="dot",
+ help='The graph engine (drawing mode only)')
+parser.add_argument('--all', dest='all', action='store_const',
+ const=True, default=False,
+ help='Show all dependencies. Default: direct dependencies only. (list mode only)')
+parser.add_argument('--verbose', dest='verbose', action='store_const',
+ const=True, default=False,
+ help='Verbose output (default: false)')
+args = parser.parse_args()
+
+files = []
+files += glob.glob(botan_root + '/src/lib/*/*/*/*/*/*/info.txt')
+files += glob.glob(botan_root + '/src/lib/*/*/*/*/*/info.txt')
+files += glob.glob(botan_root + '/src/lib/*/*/*/*/info.txt')
+files += glob.glob(botan_root + '/src/lib/*/*/*/info.txt')
+files += glob.glob(botan_root + '/src/lib/*/*/info.txt')
+files += glob.glob(botan_root + '/src/lib/*/info.txt')
+files += glob.glob(botan_root + '/src/lib/info.txt')
+files.sort()
+
+if len(files) == 0:
+ print("No info.txt files found.")
+ sys.exit(1)
+
+modules = []
+
+def dicts(t): return {k: dicts(t[k]) for k in t}
+
+def paths(t, path = [], level=0):
+ ret = []
+ for key in t:
+ ret.append(path + [key])
+ ret += paths(t[key], path + [key], level+1)
+ return ret
+
+if args.verbose:
+ print("Getting dependencies from into.txt files ...")
+
+for filename in files:
+ (rest, info_txt) = os.path.split(filename)
+ (rest, modname) = os.path.split(rest)
+ module = ModuleInfo(filename)
+ modules.append(module)
+ if args.verbose:
+ print(module.basename)
+ print("\t" + str(set(module.dependencies(None))))
+
+if args.verbose:
+ print(str(len(modules)) + " modules:")
+ names=[m.basename for m in modules]
+ names.sort()
+ print(names)
+ print("")
+
+if args.verbose:
+ print("resolving dependencies ...")
+
+def cartinality(depdict):
+ return sum([len(depdict[k]) for k in depdict])
+
+registered_dependencies = dict()
+all_dependencies = dict()
+direct_dependencies = dict()
+
+for module in modules:
+ lst = module.dependencies(None)
+ registered_dependencies[module.basename] = set(lst) - set([module.basename])
+
+# Get all_dependencies from registered_dependencies
+def add_dependency():
+ for key in all_dependencies:
+ potentially_new_modules_for_key = None
+ new_modules_for_key = None
+ for currently_in in all_dependencies[key]:
+ if currently_in in all_dependencies:
+ potentially_new_modules_for_key = all_dependencies[currently_in] - set([key])
+ if not potentially_new_modules_for_key <= all_dependencies[key]:
+ new_modules_for_key = potentially_new_modules_for_key.copy()
+ break
+ if new_modules_for_key:
+ all_dependencies[key] |= new_modules_for_key
+ return
+
+
+all_dependencies = copy.deepcopy(registered_dependencies)
+direct_dependencies = copy.deepcopy(registered_dependencies)
+
+# Sort
+all_dependencies = OrderedDict(sorted(all_dependencies.items()))
+direct_dependencies = OrderedDict(sorted(direct_dependencies.items()))
+
+#print(direct_dependencies)
+
+last_card = -1
+while True:
+ card = cartinality(all_dependencies)
+ # print(card)
+ if card == last_card:
+ break
+ last_card = card
+ add_dependency()
+
+# 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:
+ return False
+ else:
+ return b in direct_dependencies[a]
+
+def remove_indirect_dependencies():
+ for mod in direct_dependencies:
+ for one in direct_dependencies[mod]:
+ others = direct_dependencies[mod] - set([one])
+ for other in others:
+ if depends_on(other, one):
+ direct_dependencies[mod].remove(one)
+ return
+ # Go to next mod
+
+last_card = -1
+while True:
+ card = cartinality(direct_dependencies)
+ # print(card)
+ if card == last_card:
+ break
+ last_card = card
+ remove_indirect_dependencies()
+
+def openfile(f):
+ # pylint: disable=no-member
+ # os.startfile is available on Windows only
+ if sys.platform.startswith('linux'):
+ subprocess.call(["xdg-open", f])
+ else:
+ os.startfile(f)
+
+if args.verbose:
+ print("Done resolving dependencies.")
+
+if args.mode == "list":
+ if args.all:
+ for key in all_dependencies:
+ print(key.ljust(17) + " : " + ", ".join(sorted(all_dependencies[key])))
+ else:
+ for key in direct_dependencies:
+ print(key.ljust(17) + " : " + ", ".join(sorted(direct_dependencies[key])))
+
+if args.mode == "draw":
+ import graphviz as gv
+ import tempfile
+
+ tmpdir = tempfile.mkdtemp(prefix="botan-")
+
+ g2 = gv.Digraph(format=args.format, engine=args.engine)
+ g2.attr('graph', rankdir='RL') # draw horizontally
+ for key in direct_dependencies:
+ g2.node(key)
+ for dep in direct_dependencies[key]:
+ g2.edge(key, dep)
+
+ if args.verbose:
+ print("Rendering graph ...")
+ filename = g2.render(filename='graph', directory=tmpdir)
+
+ if args.verbose:
+ print("Opening " + filename + " ...")
+ openfile(filename)
diff --git a/comm/third_party/botan/src/scripts/test_all_configs.py b/comm/third_party/botan/src/scripts/test_all_configs.py
new file mode 100755
index 0000000000..227abd952f
--- /dev/null
+++ b/comm/third_party/botan/src/scripts/test_all_configs.py
@@ -0,0 +1,136 @@
+#!/usr/bin/python
+
+"""
+This configures and builds with many different sub-configurations
+in an attempt to flush out missing feature macro checks, etc.
+
+There is probably no reason for you to run this. Unless you want to.
+
+(C) 2017 Jack Lloyd
+
+Botan is released under the Simplified BSD License (see license.txt)
+"""
+
+import optparse # pylint: disable=deprecated-module
+import sys
+import subprocess
+
+def get_module_list(configure_py):
+ configure = subprocess.Popen([configure_py, '--list-modules'], stdout=subprocess.PIPE)
+
+ (stdout, _) = configure.communicate()
+
+ if configure.returncode != 0:
+ raise Exception("Running configure.py --list-modules failed")
+
+ modules = [s.decode('ascii') for s in stdout.split()]
+ modules.remove('tpm') # can't test
+ modules.remove('base') # can't remove
+ return modules
+
+def get_concurrency():
+ def_concurrency = 2
+
+ try:
+ import multiprocessing
+ return max(def_concurrency, multiprocessing.cpu_count())
+ except ImportError:
+ return def_concurrency
+
+def try_to_run(cmdline):
+ print("Running %s ... " % (' '.join(cmdline)))
+ sys.stdout.flush()
+
+ cmd = subprocess.Popen(cmdline, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ (stdout, stderr) = cmd.communicate()
+
+ failed = (cmd.returncode != 0)
+
+ if failed:
+ print("FAILURE")
+ print(stdout)
+ print(stderr)
+ sys.stdout.flush()
+
+ return not failed
+
+def run_test_build(configure_py, modules, include, jobs, run_tests):
+ config = [configure_py, '--without-documentation']
+
+ if include:
+ config.append('--minimized')
+ if modules:
+ config.append('--enable-modules=' + ','.join(modules))
+ else:
+ config.append('--disable-modules=' + ','.join(modules))
+
+ if try_to_run(config) is False:
+ return False
+
+ if try_to_run(['make', '-j', str(jobs)]) is False:
+ return False
+
+ if run_tests is False:
+ return True
+
+ # Flaky test causing errors when running tests
+ tests_to_skip = []
+
+ cmdline = ['./botan-test', '--test-threads=%d' % (jobs)]
+
+ if len(tests_to_skip) > 0:
+ cmdline.append('--skip-tests=%s' % (','.join(tests_to_skip)))
+
+ return try_to_run(cmdline)
+
+def main(args):
+
+ # TODO take configure.py and botan-test paths via options
+
+ parser = optparse.OptionParser()
+
+ parser.add_option('--run-tests', default=False, action='store_true')
+ parser.add_option('--jobs', default=get_concurrency(),
+ help="jobs to run (default %default)")
+
+ (options, args) = parser.parse_args(args)
+
+ run_tests = options.run_tests
+ jobs = int(options.jobs)
+
+ configure_py = './configure.py'
+ modules = get_module_list(configure_py)
+
+ cant_disable = ['block', 'hash', 'hex', 'mac', 'modes', 'rng', 'stream', 'utils', 'cpuid', 'entropy']
+ always_include = ['thread_utils', 'sha2_64']#, 'sha2_64', 'aes']
+
+ fails = 0
+ failed = []
+
+ for module in sorted(modules):
+ if (module in always_include) or (module in cant_disable):
+ continue # already testing it
+
+ extra = []
+ if module == 'auto_rng':
+ extra.append('dev_random')
+ if run_test_build(configure_py, [module] + always_include + extra, True, jobs, run_tests) is False:
+ failed.append(module)
+ fails += 1
+
+ for module in sorted(modules):
+ if module in cant_disable or module in always_include:
+ continue
+ if run_test_build(configure_py, [module], False, jobs, run_tests) is False:
+ failed.append(module)
+ fails += 1
+
+ if len(failed) > 0:
+ print("Failed building with %s" % (' '.join(failed)))
+ else:
+ print("All configurations ok")
+
+ return fails
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv))
diff --git a/comm/third_party/botan/src/scripts/test_cli.py b/comm/third_party/botan/src/scripts/test_cli.py
new file mode 100755
index 0000000000..4e0f8ab830
--- /dev/null
+++ b/comm/third_party/botan/src/scripts/test_cli.py
@@ -0,0 +1,1429 @@
+#!/usr/bin/python
+
+"""
+(C) 2018,2019 Jack Lloyd
+
+Botan is released under the Simplified BSD License (see license.txt)
+"""
+
+import subprocess
+import sys
+import os
+import logging
+import optparse # pylint: disable=deprecated-module
+import time
+import shutil
+import tempfile
+import re
+import random
+import json
+import binascii
+import multiprocessing
+from multiprocessing.pool import ThreadPool
+
+# pylint: disable=global-statement,unused-argument
+
+CLI_PATH = None
+TESTS_RUN = 0
+TESTS_FAILED = 0
+
+class TestLogHandler(logging.StreamHandler, object):
+ def emit(self, record):
+ # Do the default stuff first
+ super(TestLogHandler, self).emit(record)
+ if record.levelno >= logging.ERROR:
+ global TESTS_FAILED
+ TESTS_FAILED += 1
+
+def setup_logging(options):
+ if options.verbose:
+ log_level = logging.DEBUG
+ elif options.quiet:
+ log_level = logging.WARNING
+ else:
+ log_level = logging.INFO
+
+ lh = TestLogHandler(sys.stdout)
+ lh.setFormatter(logging.Formatter('%(levelname) 7s: %(message)s'))
+ logging.getLogger().addHandler(lh)
+ logging.getLogger().setLevel(log_level)
+
+def random_port_number():
+ return random.randint(1024, 65535)
+
+def test_cli(cmd, cmd_options, expected_output=None, cmd_input=None, expected_stderr=None, use_drbg=True):
+ global TESTS_RUN
+
+ TESTS_RUN += 1
+
+ opt_list = []
+
+ if isinstance(cmd_options, str):
+ opt_list = cmd_options.split(' ')
+ elif isinstance(cmd_options, list):
+ opt_list = cmd_options
+
+ if use_drbg:
+ fixed_drbg_seed = "802" * 32
+ drbg_options = ['--rng-type=drbg', '--drbg-seed=' + fixed_drbg_seed]
+ else:
+ drbg_options = []
+
+ cmdline = [CLI_PATH, cmd] + drbg_options + opt_list
+
+ logging.debug("Executing '%s'" % (' '.join([CLI_PATH, cmd] + opt_list)))
+
+ stdout = None
+ stderr = None
+
+ if cmd_input is None:
+ proc = subprocess.Popen(cmdline, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ (stdout, stderr) = proc.communicate()
+ else:
+ proc = subprocess.Popen(cmdline, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ (stdout, stderr) = proc.communicate(cmd_input.encode())
+
+ if stderr:
+ if expected_stderr is None:
+ logging.error("Got output on stderr %s (stdout was %s)", stderr, stdout)
+ else:
+ if stderr != expected_stderr:
+ logging.error("Got output on stderr %s which did not match expected value %s", stderr, expected_stderr)
+ else:
+ if expected_stderr is not None:
+ logging.error('Expected output on stderr but got nothing')
+
+ output = stdout.decode('ascii').strip()
+
+ if expected_output is not None:
+ if output != expected_output:
+ logging.error("Got unexpected output running cmd %s %s", cmd, cmd_options)
+ logging.info("Output lengths %d vs expected %d", len(output), len(expected_output))
+ logging.info("Got %s", output)
+ logging.info("Exp %s", expected_output)
+
+ return output
+
+def check_for_command(cmd):
+ cmdline = [CLI_PATH, 'has_command', cmd]
+ proc = subprocess.Popen(cmdline)
+ proc.communicate()
+
+ return proc.returncode == 0
+
+def cli_config_tests(_tmp_dir):
+ prefix = test_cli("config", "prefix")
+ cflags = test_cli("config", "cflags")
+ ldflags = test_cli("config", "ldflags")
+ libs = test_cli("config", "libs")
+
+ if len(prefix) < 4 or prefix[0] != '/':
+ logging.error("Bad prefix %s" % (prefix))
+ if ("-I%s/include/botan-2" % (prefix)) not in cflags:
+ logging.error("Bad cflags %s" % (cflags))
+ if not ldflags.endswith(("-L%s/lib" % (prefix))):
+ logging.error("Bad ldflags %s" % (ldflags))
+ if "-lbotan-2" not in libs:
+ logging.error("Bad libs %s" % (libs))
+
+def cli_help_tests(_tmp_dir):
+ output = test_cli("help", None, None)
+
+ # Maybe test format somehow??
+ if len(output) < 500:
+ logging.error("Help output seems very short")
+
+def cli_version_tests(_tmp_dir):
+ output = test_cli("version", None, None)
+
+ version_re = re.compile(r'[0-9]\.[0-9]+\.[0-9]')
+ if not version_re.match(output):
+ logging.error("Unexpected version output %s" % (output))
+
+ output = test_cli("version", ["--full"], None, None)
+ version_full_re = re.compile(r'Botan [0-9]\.[0-9]+\.[0-9] \(.* revision .*, distribution .*\)$')
+ if not version_full_re.match(output):
+ logging.error("Unexpected version output %s" % (output))
+
+def cli_is_prime_tests(_tmp_dir):
+ test_cli("is_prime", "5", "5 is probably prime")
+ test_cli("is_prime", "9", "9 is composite")
+ test_cli("is_prime", "548950623407687320763", "548950623407687320763 is probably prime")
+
+def cli_gen_prime_tests(_tmp_dir):
+ test_cli("gen_prime", "64", "15568813029901363163")
+ test_cli("gen_prime", "128", "287193909494025008847286845478788766073")
+
+def cli_cycle_counter(_tmp_dir):
+ output = test_cli("cpu_clock", None, None)
+
+ if output.startswith('No CPU cycle counter on this machine'):
+ return
+
+ have_clock_re = re.compile(r'Estimated CPU clock [0-9\.]+ (M|G)Hz')
+
+ if have_clock_re.match(output):
+ return
+
+ logging.error('Unexpected output from cpu_clock: %s', output)
+
+def cli_entropy_tests(_tmp_dir):
+ output = test_cli("entropy", ["all"], None)
+
+ status_re = re.compile('Polling [a-z0-9_]+ gathered [0-9]+ bytes in [0-9]+ outputs with estimated entropy [0-9]+')
+ unavail_re = re.compile('Source [a-z0-9_]+ is unavailable')
+ comp_re = re.compile('Sample from [a-z0-9_]+ was .* compressed from [0-9]+ bytes to [0-9]+ bytes')
+ output_re = re.compile(r'[A-F0-9]+(...)?')
+
+ status_next = True
+
+ for line in output.split('\n'):
+ if comp_re.match(line):
+ continue
+
+ if status_next:
+ if status_re.match(line) is not None:
+ status_next = False
+ elif unavail_re.match(line) is not None:
+ pass
+ else:
+ logging.error('Unexpected status line %s', line)
+ status_next = False
+ else:
+ if output_re.match(line) is None:
+ logging.error('Unexpected sample line %s', line)
+ status_next = True
+
+def cli_factor_tests(_tmp_dir):
+ test_cli("factor", "97", "97: 97")
+ test_cli("factor", "9753893489562389", "9753893489562389: 21433 455087644733")
+ test_cli("factor", "12019502040659149507", "12019502040659149507: 3298628633 3643787579")
+
+def cli_mod_inverse_tests(_tmp_dir):
+ test_cli("mod_inverse", "97 802", "339")
+ test_cli("mod_inverse", "98 802", "0")
+
+def cli_base64_tests(_tmp_dir):
+ test_cli("base64_enc", "-", "YmVlcyE=", "bees!")
+ test_cli("base64_dec", "-", "bees!", "YmVlcyE=")
+
+def cli_base32_tests(_tmp_dir):
+ test_cli("base32_enc", "-", "MJSWK4ZB", "bees!")
+ test_cli("base32_dec", "-", "bees!", "MJSWK4ZB")
+
+def cli_base58_tests(_tmp_dir):
+ test_cli("base58_enc", "-", "C6sRAr4", "bees!")
+ test_cli("base58_dec", "-", "bees!", "C6sRAr4")
+
+ test_cli("base58_enc", ["--check", "-"], "Cjv15cdjaBc", "F00F")
+ test_cli("base58_dec", ["--check", "-"], "F00F", "Cjv15cdjaBc")
+
+def cli_hex_tests(_tmp_dir):
+ test_cli("hex_enc", "-", "6265657321", "bees!")
+ test_cli("hex_dec", "-", "bees!", "6265657321")
+
+def cli_hash_tests(_tmp_dir):
+ test_cli("hash", "--algo=SHA-256",
+ "E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855 -", "")
+
+ test_cli("hash", "--algo=SHA-256",
+ "BA7816BF8F01CFEA414140DE5DAE2223B00361A396177A9CB410FF61F20015AD -", "abc")
+
+ test_cli("hash", ["--algo=SHA-256", "--format=base64"],
+ "ungWv48Bz+pBQUDeXa4iI7ADYaOWF3qctBD/YfIAFa0= -", "abc")
+
+ test_cli("hash", ["--algo=SHA-224", "--format=base58", "--no-fsname"],
+ "MuGc8HkSVyJjfMjPM5UQikPToBTzNucEghcGLe", "abc")
+
+ test_cli("hash", ["--algo=SHA-224", "--format=base58check", "--no-fsname"],
+ "3MmfMqgrhemdVa9bDAGfooukbviWtKMBx2xauL2RsyAe", "abc")
+
+def cli_hmac_tests(tmp_dir):
+ key_file = os.path.join(tmp_dir, 'hmac.key')
+
+ test_cli("rng", ["64", "--output=%s" % (key_file)], "")
+
+ test_cli("hmac", ["--no-fsname", "--hash=SHA-384", key_file, key_file],
+ "E3A8529377030B28A7DBDFC50DDEC8E4ECEFB6EA850D95EB785938CD3E3AFEF9EF8B08AF219C1496633193468AB755CB")
+
+def cli_bcrypt_tests(_tmp_dir):
+ test_cli("gen_bcrypt", "--work-factor=4 s3kr1t",
+ "$2a$04$0.8G7o08XYwvBBWA3l0WUujtwoGZgGDzVSN8fNkNqXikcK4A3lHPS")
+
+ test_cli("check_bcrypt", "s3kr1t $2a$04$gHX4Qg7pDSJuXiPXnmt8leyb.FFzX1Bv4rXwIj2cPSakJ8zNnhIka",
+ "Password is valid")
+
+ test_cli("check_bcrypt", "santa $2a$04$gHX4Qg7pDSJuXiPXnmt8leyb.FFzX1Bv4rXwIj2cPSakJ8zNnhIka",
+ "Password is NOT valid")
+
+def cli_argon2_tests(_tmp_dir):
+ password = "s3kr1t"
+ expected = "$argon2id$v=19$m=8,t=1,p=1$2A+I9q2+ZayxDDYC5n2YWw$/Lhx+Jbtlpw+Kxpskfv7+AKhBL/5ebalTJkVC1O5+1E"
+ test_cli("gen_argon2", ['--mem=8', password], expected)
+ test_cli("gen_argon2", ['--mem=8', '--t=1', password], expected)
+ test_cli("gen_argon2", ['--mem=8', '--t=1', '--p=1', password], expected)
+
+ test_cli("check_argon2", [password, expected], "Password is valid")
+ test_cli("check_argon2", ["guessing", expected], "Password is NOT valid")
+
+def cli_gen_dl_group_tests(_tmp_dir):
+
+ pem = """-----BEGIN X9.42 DH PARAMETERS-----
+MIIBJAKBgwTw7LQiLkXJsrgMVQxTPlWaQlYz/raZ+5RtIZe4YluQgRQGPFADLZ/t
+TOYzuIzZJFOcdKtEtrVkxZRGSkjZwKFKLUD6fzSjoC2M2EHktK/y5HsvxBxL4tKr
+q1ffbyPQi+iBLYTZAXygvxj2vWyrvA+/w4nbt1fStCHTDhWjLWqFpV9nAoGDAKzA
+HUu/IRl7OiUtW/dz36gzEJnaYtz4ZtJl0FG8RJiOe02lD8myqW2sVzYqMvKD0LGx
+x9fdSKC1G+aZ/NWtqrQjb66Daf7b0ddDx+bfWTWJ2dOtZd8IL2rmQQJm+JogDi9i
+huVYFicDNQGzi+nEKAzrZ1L/VxtiSiw/qw0IyOuVtz8CFjgPiPatvmWssQw2AuZ9
+mFvAZ/8wal0=
+-----END X9.42 DH PARAMETERS-----"""
+
+ test_cli("gen_dl_group", "--pbits=1043", pem)
+
+ dsa_grp = """-----BEGIN X9.42 DH PARAMETERS-----
+MIIBHgKBgQCyP1vosC/axliM2hmJ9EOSdd1zBkuzMP25CYD8PFkRVrPLr1ClSUtn
+eXTIsHToJ7d7sRwtidQGW9BrvUEyiAWE06W/wnLPxB3/g2/l/P2EhbNmNHAO7rV7
+ZVz/uKR4Xcvzxg9uk5MpT1VsxA8H6VEwzefNF1Rya92rqGgBTNT3/wKBgC7HLL8A
+Gu3tqJxTk1iNgojjOiSreLn6ihA8R8kQnRXDTNtDKz996KHGInfMBurUI1zPM3xq
+bHc0CvU1Nf87enhPIretzJcFgiCWrNFUIC25zPEjp0s3/ERHT4Bi1TABZ3j6YUEQ
+fnnj+9XriKKHf2WtX0T4FXorvnKq30m934rzAhUAvwhWDK3yZEmphc7dwl4/J3Zp
++MU=
+-----END X9.42 DH PARAMETERS-----"""
+
+ test_cli("gen_dl_group", ["--type=dsa", "--pbits=1024"], dsa_grp)
+
+
+def cli_key_tests(tmp_dir):
+
+ pem = """-----BEGIN PRIVATE KEY-----
+MIGEAgEAMBAGByqGSM49AgEGBSuBBAAKBG0wawIBAQQg2A+I9q2+ZayxDDYC5n2Y
+W8Bn/zBm4D3mwS5qMwADRDehRANCAATwnDFqsjXL9SD/Rr1Vy4pb79PswXdQNZBN
+mlLtJ5JvZ0/p6zP3x+Y9yPIrAR8L/acG5ItSrAKXzzuqQQZMv4aN
+-----END PRIVATE KEY-----"""
+
+ priv_key = os.path.join(tmp_dir, 'priv.pem')
+ pub_key = os.path.join(tmp_dir, 'pub.pem')
+ pub_der_key = os.path.join(tmp_dir, 'pub.der')
+ enc_pem = os.path.join(tmp_dir, 'priv_enc.pem')
+ enc_der = os.path.join(tmp_dir, 'priv_enc.der')
+ ca_cert = os.path.join(tmp_dir, 'ca.crt')
+ crt_req = os.path.join(tmp_dir, 'crt.req')
+ user_cert = os.path.join(tmp_dir, 'user.crt')
+
+ test_cli("keygen", ["--algo=ECDSA", "--params=secp256k1"], pem)
+
+ test_cli("keygen", ["--algo=ECDSA", "--params=secp256r1", "--output=" + priv_key], "")
+
+ test_cli("pkcs8", "--pub-out --output=%s %s" % (pub_key, priv_key), "")
+ test_cli("pkcs8", "--pub-out --der-out --output=%s %s" % (pub_der_key, priv_key), "")
+
+ test_cli("pkcs8", "--pass-out=foof --der-out --output=%s %s" % (enc_der, priv_key), "")
+ test_cli("pkcs8", "--pass-out=foof --output=%s %s" % (enc_pem, priv_key), "")
+
+ dec_pem = test_cli("pkcs8", ["--pass-in=foof", enc_pem], None)
+ dec_der = test_cli("pkcs8", ["--pass-in=foof", enc_der], None)
+
+ if dec_pem != dec_der:
+ logging.error("Problem decrypting PKCS8 key")
+
+ test_cli("fingerprint", ['--no-fsname', pub_key],
+ "83:FC:67:87:30:C7:0C:9C:54:9A:E7:A1:FA:25:83:4C:77:A4:43:16:33:6D:47:3C:CE:4B:91:62:30:97:62:D4")
+
+ test_cli("fingerprint", ['--no-fsname', pub_der_key],
+ "83:FC:67:87:30:C7:0C:9C:54:9A:E7:A1:FA:25:83:4C:77:A4:43:16:33:6D:47:3C:CE:4B:91:62:30:97:62:D4")
+
+ test_cli("fingerprint", ['--no-fsname', pub_key, pub_der_key],
+ "83:FC:67:87:30:C7:0C:9C:54:9A:E7:A1:FA:25:83:4C:77:A4:43:16:33:6D:47:3C:CE:4B:91:62:30:97:62:D4\n"
+ "83:FC:67:87:30:C7:0C:9C:54:9A:E7:A1:FA:25:83:4C:77:A4:43:16:33:6D:47:3C:CE:4B:91:62:30:97:62:D4")
+
+ test_cli("fingerprint", [pub_der_key],
+ pub_der_key +
+ ": 83:FC:67:87:30:C7:0C:9C:54:9A:E7:A1:FA:25:83:4C:77:A4:43:16:33:6D:47:3C:CE:4B:91:62:30:97:62:D4")
+
+ test_cli("fingerprint", ['-'],
+ "83:FC:67:87:30:C7:0C:9C:54:9A:E7:A1:FA:25:83:4C:77:A4:43:16:33:6D:47:3C:CE:4B:91:62:30:97:62:D4",
+ open(pub_key, 'rb').read().decode())
+
+ valid_sig = "nI4mI1ec14Y7nYUWs2edysAVvkob0TWpmGh5rrYWDA+/W9Fj0ZM21qJw8qa3/avAOIVBO6hoMEVmfJYXlS+ReA=="
+
+ test_cli("sign", "--provider=base %s %s" % (priv_key, pub_key), valid_sig)
+
+ test_cli("verify", [pub_key, pub_key, '-'],
+ "Signature is valid", valid_sig)
+
+ test_cli("verify", [pub_key, pub_key, '-'],
+ "Signature is invalid",
+ valid_sig.replace("G", "H"))
+
+ test_cli("gen_self_signed",
+ [priv_key, "CA", "--ca", "--country=VT",
+ "--dns=ca.example", "--hash=SHA-384", "--output="+ca_cert],
+ "")
+
+ test_cli("cert_verify", ca_cert, "Certificate did not validate - Cannot establish trust")
+
+ cert_info = test_cli("cert_info", ['--fingerprint', ca_cert], None)
+
+ if cert_info.find('Subject: CN="CA",C="VT"') < 0:
+ logging.error('Unexpected output for cert_info command %s', cert_info)
+ if cert_info.find('Subject keyid: 69DD911C9EEE3400C67CBC3F3056CBE711BD56AF9495013F') < 0:
+ logging.error('Unexpected output for cert_info command %s', cert_info)
+
+ test_cli("gen_pkcs10", "%s User --output=%s" % (priv_key, crt_req))
+
+ test_cli("sign_cert", "%s %s %s --output=%s" % (ca_cert, priv_key, crt_req, user_cert))
+
+ test_cli("cert_verify", [user_cert, ca_cert],
+ "Certificate passes validation checks")
+
+ test_cli("cert_verify", user_cert,
+ "Certificate did not validate - Certificate issuer not found")
+
+def cli_xmss_sign_tests(tmp_dir):
+ 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')
+ msg = os.path.join(tmp_dir, 'input')
+ sig1 = os.path.join(tmp_dir, 'sig1')
+ sig2 = os.path.join(tmp_dir, 'sig2')
+
+ 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], "5B38F737BA41BE7F40433DB30EAEF7C41ABB0F7D9E7A09DEB5FDCE7B6811693F")
+
+ test_cli("pkcs8", "--pub-out --output=%s %s" % (pub_key, priv_key), "")
+ test_cli("fingerprint", ['--no-fsname', pub_key],
+ "B0:F4:98:6E:D8:4E:05:63:A1:D8:4B:37:61:5A:A0:41:78:7E:DE:0E:72:46:E0:A8:D6:CF:09:54:08:DA:A4:22")
+
+ # 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], "04AF45451C7A9AF2D828E1AD6EC262E012436F4087C5DA6F32C689D781E597D0")
+ test_cli("hash", ["--no-fsname", priv_key], "67929FAEC636E43DE828C1CD7E2D11CE7C3388CE90DD0A0F687C6627FFA850CD")
+
+ 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], "0785A6AD54CC7D01F2BE2BC6463A3EAA1159792E52210ED754992C5068E8F24F")
+ test_cli("hash", ["--no-fsname", priv_key], "1940945D68B1CF54D79E05DD7913A4D0B4959183F1E12B81A4E43EF4E63FBD20")
+
+ # 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],
+ "B0:F4:98:6E:D8:4E:05:63:A1:D8:4B:37:61:5A:A0:41:78:7E:DE:0E:72:46:E0:A8:D6:CF:09:54:08:DA:A4:22")
+
+def cli_pbkdf_tune_tests(_tmp_dir):
+ if not check_for_command("pbkdf_tune"):
+ return
+
+ expected = re.compile(r'For (default|[1-9][0-9]*) ms selected Scrypt\([0-9]+,[0-9]+,[0-9]+\) using [0-9]+ MiB')
+
+ output = test_cli("pbkdf_tune", ["--check", "1", "10", "50", "default"], None).split('\n')
+
+ for line in output:
+ if expected.match(line) is None:
+ logging.error("Unexpected line '%s'" % (line))
+
+ expected_pbkdf2 = re.compile(r'For (default|[1-9][0-9]*) ms selected PBKDF2\(HMAC\(SHA-256\),[0-9]+\)')
+
+ output = test_cli("pbkdf_tune", ["--algo=PBKDF2(SHA-256)", "--check", "1", "10", "50", "default"], None).split('\n')
+
+ for line in output:
+ if expected_pbkdf2.match(line) is None:
+ logging.error("Unexpected line '%s'" % (line))
+
+ expected_argon2 = re.compile(r'For (default|[1-9][0-9]*) ms selected Argon2id\([0-9]+,[0-9]+,[0-9]+\)')
+
+ output = test_cli("pbkdf_tune", ["--algo=Argon2id", "--check", "1", "10", "50", "default"], None).split('\n')
+
+ for line in output:
+ if expected_argon2.match(line) is None:
+ logging.error("Unexpected line '%s'" % (line))
+
+def cli_psk_db_tests(tmp_dir):
+ if not check_for_command("psk_get"):
+ return
+
+ psk_db = os.path.join(tmp_dir, 'psk.db')
+ db_key1 = "909"*32
+ db_key2 = "451"*32
+
+ test_cli("psk_set", [psk_db, db_key1, "name", "F00FEE"], "")
+ test_cli("psk_set", [psk_db, db_key2, "name", "C00FEE11"], "")
+ test_cli("psk_set", [psk_db, db_key1, "name2", "50051029"], "")
+
+ test_cli("psk_get", [psk_db, db_key1, "name"], "F00FEE")
+ test_cli("psk_get", [psk_db, db_key2, "name"], "C00FEE11")
+
+ test_cli("psk_list", [psk_db, db_key1], "name\nname2")
+ test_cli("psk_list", [psk_db, db_key2], "name")
+
+def cli_compress_tests(tmp_dir):
+
+ if not check_for_command("compress"):
+ return
+
+ input_file = os.path.join(tmp_dir, 'input.txt')
+ output_file = os.path.join(tmp_dir, 'input.txt.gz')
+
+ with open(input_file, 'w') as f:
+ f.write("hi there")
+ f.close()
+
+ test_cli("compress", input_file)
+
+ if not os.access(output_file, os.R_OK):
+ logging.error("Compression did not created expected output file")
+
+ is_py3 = sys.version_info[0] == 3
+
+ output_hdr = open(output_file, 'rb').read(2)
+
+ if is_py3:
+ if output_hdr[0] != 0x1F or output_hdr[1] != 0x8B:
+ logging.error("Did not see expected gzip header")
+ else:
+ if ord(output_hdr[0]) != 0x1F or ord(output_hdr[1]) != 0x8B:
+ logging.error("Did not see expected gzip header")
+
+ os.unlink(input_file)
+
+ test_cli("decompress", output_file)
+
+ if not os.access(input_file, os.R_OK):
+ logging.error("Decompression did not created expected output file")
+
+ recovered = open(input_file).read()
+ if recovered != "hi there":
+ logging.error("Decompression did not recover original input")
+
+def cli_rng_tests(_tmp_dir):
+ test_cli("rng", "10", "D80F88F6ADBE65ACB10C")
+ test_cli("rng", "16", "D80F88F6ADBE65ACB10C3602E67D985B")
+ test_cli("rng", "10 6", "D80F88F6ADBE65ACB10C\n1B119CC068AF")
+
+ test_cli("rng", ['--format=base64', '10'], "2A+I9q2+ZayxDA==")
+ test_cli("rng", ['--format=base58', '10'], "D93XRyVfxqs7oR")
+ test_cli("rng", ['--format=base58check', '10'], "2NS1jYUq92TyGFVnhVLa")
+
+ hex_10 = re.compile('[A-F0-9]{20}')
+
+ for rng in ['system', 'auto', 'entropy']:
+ output = test_cli("rng", ["10", '--%s' % (rng)], use_drbg=False)
+ if output == "D80F88F6ADBE65ACB10C":
+ logging.error('RNG produced DRBG output')
+ if hex_10.match(output) is None:
+ logging.error('Unexpected RNG output %s' % (output))
+
+ has_rdrand = test_cli("cpuid", []).find(' rdrand ') > 0
+
+ if has_rdrand:
+ output = test_cli("rng", ["10", '--rdrand'], use_drbg=False)
+
+ if output == "D80F88F6ADBE65ACB10C":
+ logging.error('RDRAND produced DRBG output')
+ if hex_10.match(output) is None:
+ logging.error('Unexpected RNG output %s' % (output))
+
+def cli_roughtime_check_tests(tmp_dir):
+ # pylint: disable=line-too-long
+ if not check_for_command("roughtime_check"):
+ return
+ chain = os.path.join(tmp_dir, 'roughtime-chain')
+
+ with open(chain, 'w') as f:
+ f.write("""\
+ed25519 bbT+RPS7zKX6w71ssPibzmwWqU9ffRV5oj2OresSmhE= eu9yhsJfVfguVSqGZdE8WKIxaBBM0ZG3Vmuc+IyZmG2YVmrIktUByDdwIFw6F4rZqmSFsBO85ljoVPz5bVPCOw== BQAAAEAAAABAAAAApAAAADwBAABTSUcAUEFUSFNSRVBDRVJUSU5EWBnGOEajOwPA6G7oL47seBP4C7eEpr57H43C2/fK/kMA0UGZVUdf4KNX8oxOK6JIcsbVk8qhghTwA70qtwpYmQkDAAAABAAAAAwAAABSQURJTUlEUFJPT1RAQg8AJrA8tEqPBQAqisiuAxgy2Pj7UJAiWbCdzGz1xcCnja3T+AqhC8fwpeIwW4GPy/vEb/awXW2DgSLKJfzWIAz+2lsR7t4UjNPvAgAAAEAAAABTSUcAREVMRes9Ch4X0HIw5KdOTB8xK4VDFSJBD/G9t7Et/CU7UW61OiTBXYYQTG2JekWZmGa0OHX1JPGG+APkpbsNw0BKUgYDAAAAIAAAACgAAABQVUJLTUlOVE1BWFR/9BWjpsWTQ1f6iUJea3EfZ1MkX3ftJiV3ABqNLpncFwAAAAAAAAAA//////////8AAAAA
+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
+ed25519 etPaaIxcBMY1oUeGpwvPMCJMwlRVNxv51KK/tktoJTQ= U53wX99JzZwy4BXa9C6R04bPu4yqFB5w5/wTgG8Mw5wm+VLrY70ECxJ9ZHnpdHVHaLEU3aeLnQFZyZPRAEOCyw== BQAAAEAAAABAAAAApAAAADwBAABTSUcAUEFUSFNSRVBDRVJUSU5EWMh3mPWCCbOlX8xDWbU9qdfKoReJX/XLsivom8bJJYmcC7T03tyXrtWUheEJweHtg4qMgSyifQS1MjHJSy1jPAsDAAAABAAAAAwAAABSQURJTUlEUFJPT1RAQg8Akxw/tEqPBQBfOsOuciR7jiAW5itQ39y8yVr/ZJmgMwvTjqaU4/wA05ZqG4RqoLdvDXh5bCNySL6LrrnBNSAHwn5COt0CItNuAgAAAEAAAABTSUcAREVMRVP3BIOzsZmuxqMi+ScIBPyKtzFfK7ZlPFNP0JrNwln2QYtAcQFIKywDdNAAL+n8i3dz1p99K50FJjCkCl2J6AMDAAAAIAAAACgAAABQVUJLTUlOVE1BWFQKC/kZVdjiNT2NCSGfnpot4eqipyMFsyMjiIQmqqqXqQCAa245jwUAAGCgA56PBQAAAAAA
+ed25519 AW5uAoTSTDfG5NfY1bTh08GUnOqlRb+HVhbJ3ODJvsE= IcZcXFuaLKYYhWcK3sT/6PrVeXMmabCRbf9hvVfkMkqEW1PFL++ZnHJ1/m+G8azITxvktwsfP1YAOOxWdbf9XQ== BQAAAEAAAABAAAAApAAAADwBAABTSUcAUEFUSFNSRVBDRVJUSU5EWL5DAl8GPNUQ/mSXl0tI4N9yZAO+PiXTodJOTDL+WU/x26iqgyyQRikSSocRMzAEVLDGasdyW19mVC6H/6vfXggDAAAABAAAAAwAAABSQURJTUlEUFJPT1RAQg8Av/JAtEqPBQBIP346SHhCdDfughzeH+uYSbxngDYxqHzBDtZt0obUKrzxfRWzD1oR61B1reLvoPVCKSfzEngi/g1NSQjTrzNMAgAAAEAAAABTSUcAREVMRTQLLplQv0rN4p77Bo59qT8bbquV6MKSwILI/Tw2LLGo9noaZegUFmM+rNu1d1AVOEVQ01j6/2xDmBvp0d6MZgEDAAAAIAAAACgAAABQVUJLTUlOVE1BWFS4a1dYoIB5u/zkbR3sIteuhVrQkszzj+Gng9ywo6O9VgAAAAAAAAAA//////////8AAAAA
+ed25519 cj8GsiNlRkqiDElAeNMSBBMwrAl15hYPgX50+GWX/lA= Tsy82BBU2xxVqNe1ip11OyEGoKWhKoSggWjBmDTSBmKbTs7bPPCEidYc5TQ23sQUWe62G35fQOVU28q+Eq5uhQ== BQAAAEAAAABAAAAApAAAADwBAABTSUcAUEFUSFNSRVBDRVJUSU5EWDAmi7zgXAqLgQXVfbjeqnUZRiXCZI64QIoAKFL83CQHbyXgB4cNwHfQ9mSg0hYxTp1M8QxOuzusnUpk05DIRwwDAAAABAAAAAwAAABSQURJTUlEUFJPT1RAQg8AcOBCtEqPBQBhsr1mKOxxCf4VDFzAtYB4Nhs332AN1LrJU/8+VqktzfPd2R7awJHEVEWugvSvOrr+9d332mQObAkYfKfDtbSFAgAAAEAAAABTSUcAREVMRUjnhDvkIjFzTEYtgHOfMpRHtnNZj4P31RFtapkwzGjOtc93pYDd7zqQCw2AVcfbSnPqa8k26z96Q9fVRzq0pw8DAAAAIAAAACgAAABQVUJLTUlOVE1BWFR7qp2oerjpbN8Y23nUGARIlsgkodW4owH29ZKhxDMn8AAAAAAAAAAA//////////8AAAAA
+""")
+
+ test_cli("roughtime_check", chain, """\
+1: UTC 2019-08-04T13:38:17 (+-1000000us)
+ 2: UTC 2019-08-04T13:38:17 (+-1000000us)
+ 3: UTC 2019-08-04T13:38:17 (+-1000000us)
+ 4: UTC 2019-08-04T13:38:18 (+-1000000us)
+ 5: UTC 2019-08-04T13:38:18 (+-1000000us)""")
+
+ with open(chain, 'w') as f:
+ f.write("ed25519 bbT+RPS7zKX6w71ssPibzmwWqU9ffRV5oj2OresSmhE= eu9yhsJfVfguVSqGZdE8WKIxaBBM0ZG3Vmuc+IyZmG2YVmrIktUByDdwIFw6F4rZqmSFsBO85ljoVPz5bVPCOw== BQAAAEAAAABAAAAApAAAADwBAABTSUcAUEFUSFNSRVBDRVJUSU5EWBnGOEajOwPA6G7oL47seBP4C7eEpr57H43C2/fK/kMA0UGZVUdf4KNX8oxOK6JIcsbVk8qhghTwA70qtwpYmQkDAAAABAAAAAwAAABSQURJTUlEUFJPT1RAQg8AJrA8tEqPBQAqisiuAxgy2Pj7UJAiWbCdzGz1xcCnja3T+AqhC8fwpeIwW4GPy/vEb/awXW2DgSLKJfzWIAz+2lsR7t4UjNPvAgAAAEAAAABTSUcAREVMRes9Ch4X0HIw5KdOTB8xK4VDFSJBD/G9t7Et/CU7UW61OiTBXYYQTG2JekWZmGa0OHX1JPGG+APkpbsNw0BKUgYDAAAAIAAAACgAAABQVUJLTUlOVE1BWFR/9BWjpsWTQ1f6iUJea3EfZ1MkX3ftJiV3ABqNLpncFwAAAAAAAAAA//////////8AAAAA")
+ test_cli("roughtime_check", [chain, "--raw-time"], "1: UTC 1564925897781286 (+-1000000us)")
+
+ with open(chain, 'w') as f:
+ f.write("ed25519 cbT+RPS7zKX6w71ssPibzmwWqU9ffRV5oj2OresSmhE= eu9yhsJfVfguVSqGZdE8WKIxaBBM0ZG3Vmuc+IyZmG2YVmrIktUByDdwIFw6F4rZqmSFsBO85ljoVPz5bVPCOw== BQAAAEAAAABAAAAApAAAADwBAABTSUcAUEFUSFNSRVBDRVJUSU5EWBnGOEajOwPA6G7oL47seBP4C7eEpr57H43C2/fK/kMA0UGZVUdf4KNX8oxOK6JIcsbVk8qhghTwA70qtwpYmQkDAAAABAAAAAwAAABSQURJTUlEUFJPT1RAQg8AJrA8tEqPBQAqisiuAxgy2Pj7UJAiWbCdzGz1xcCnja3T+AqhC8fwpeIwW4GPy/vEb/awXW2DgSLKJfzWIAz+2lsR7t4UjNPvAgAAAEAAAABTSUcAREVMRes9Ch4X0HIw5KdOTB8xK4VDFSJBD/G9t7Et/CU7UW61OiTBXYYQTG2JekWZmGa0OHX1JPGG+APkpbsNw0BKUgYDAAAAIAAAACgAAABQVUJLTUlOVE1BWFR/9BWjpsWTQ1f6iUJea3EfZ1MkX3ftJiV3ABqNLpncFwAAAAAAAAAA//////////8AAAAA")
+ test_cli("roughtime_check", chain, expected_stderr=b'Error: Roughtime Invalid signature or public key\n')
+
+def cli_roughtime_tests(tmp_dir):
+ # pylint: disable=line-too-long
+ # pylint: disable=too-many-locals
+ import socket
+ import base64
+ import threading
+
+ if not check_for_command("roughtime"):
+ return
+
+ server_port = random_port_number()
+ chain_file = os.path.join(tmp_dir, 'roughtime-chain')
+ ecosystem = os.path.join(tmp_dir, 'ecosystem')
+
+ def run_udp_server():
+ sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ server_address = ('127.0.0.1', server_port)
+ sock.bind(server_address)
+
+ while True:
+ data, address = sock.recvfrom(4096)
+
+ if data:
+ if data != base64.b64decode(server_request):
+ logging.error("unexpected request")
+
+ sock.sendto(base64.b64decode(server_response), address)
+
+ udp_thread = threading.Thread(target=run_udp_server)
+ udp_thread.daemon = True
+ udp_thread.start()
+
+ chain = [
+ """\
+ed25519 gD63hSj3ScS+wuOeGrubXlq35N1c5Lby/S+T7MNTjxo= 2A+I9q2+ZayxDDYC5n2YW8Bn/zBm4D3mwS5qMwADRDcbFpBcf3yPOyeZiqpLBTkxo8GT8zMQFeApv4ScffjC8A== BQAAAEAAAABAAAAApAAAADwBAABTSUcAUEFUSFNSRVBDRVJUSU5EWDwlo/AkUnTrecAW4Ci5Tkh3KOqs6R7KLTsFtq16RXN5F7G5ckGv11UtzHoZTbKbEk03a6ogAOK54Q2CI/7XGA8DAAAABAAAAAwAAABSQURJTUlEUFJPT1RAQg8AWDLihlaSBQAoq/5gEjRCrhfH16X2GYjQJSG/CgSuGhYeCsrw7XkphLI3cxw2unJRDW8DAJrYqEGaW0NPKZk7bbpPjU/Q6Es1AgAAAEAAAABTSUcAREVMRUJbs67Sb5Wx/jzWyT1PhWR0c4kg59tjSGofo8R3eHzcA9CGwavuRdxOArhVWWODG99gYgfmjcRLgt9/jH+99w4DAAAAIAAAACgAAABQVUJLTUlOVE1BWFRXRfQ1RHLWGOgqABUTYfVBDZrv3OL2nPLYve9ldfNVLOjdPVFFkgUA6D0Vb1mSBQAAAAAA
+""",
+ """\
+ed25519 gD63hSj3ScS+wuOeGrubXlq35N1c5Lby/S+T7MNTjxo= 2A+I9q2+ZayxDDYC5n2YW8Bn/zBm4D3mwS5qMwADRDcbFpBcf3yPOyeZiqpLBTkxo8GT8zMQFeApv4ScffjC8A== BQAAAEAAAABAAAAApAAAADwBAABTSUcAUEFUSFNSRVBDRVJUSU5EWDwlo/AkUnTrecAW4Ci5Tkh3KOqs6R7KLTsFtq16RXN5F7G5ckGv11UtzHoZTbKbEk03a6ogAOK54Q2CI/7XGA8DAAAABAAAAAwAAABSQURJTUlEUFJPT1RAQg8AWDLihlaSBQAoq/5gEjRCrhfH16X2GYjQJSG/CgSuGhYeCsrw7XkphLI3cxw2unJRDW8DAJrYqEGaW0NPKZk7bbpPjU/Q6Es1AgAAAEAAAABTSUcAREVMRUJbs67Sb5Wx/jzWyT1PhWR0c4kg59tjSGofo8R3eHzcA9CGwavuRdxOArhVWWODG99gYgfmjcRLgt9/jH+99w4DAAAAIAAAACgAAABQVUJLTUlOVE1BWFRXRfQ1RHLWGOgqABUTYfVBDZrv3OL2nPLYve9ldfNVLOjdPVFFkgUA6D0Vb1mSBQAAAAAA
+ed25519 gD63hSj3ScS+wuOeGrubXlq35N1c5Lby/S+T7MNTjxo= 2A+I9q2+ZayxDDYC5n2YW8Bn/zBm4D3mwS5qMwADRDcbFpBcf3yPOyeZiqpLBTkxo8GT8zMQFeApv4ScffjC8A== BQAAAEAAAABAAAAApAAAADwBAABTSUcAUEFUSFNSRVBDRVJUSU5EWHH5Ofs4HciIFXjE9egjDbistJptoMXIC7ugCgHhI4NPJqfYY256NpULXKc9c30ul7oHXQyKLfGd84mIAxC3UwQDAAAABAAAAAwAAABSQURJTUlEUFJPT1RAQg8AuOoUh1aSBQANeC4gGGG3a23PpmF+y6CrUS9VWjyj0Ydpl2tMVDLaK2vd5QtYKKJ3UOyprGKk0D/aPn4E3Bk2rE3BKBZRXM1AAgAAAEAAAABTSUcAREVMRci9uvioJssgd8txxFlqz9RqPx+YLVMkHmm24fMUtYGWF/nhkoEYVGT7O+tXSfHHY/KHcUZjVaZpEt/tmXlXBAUDAAAAIAAAACgAAABQVUJLTUlOVE1BWFSxhKhavdriTvCAtNVcK5yr0cAbsWp2MsrwUV5YTc+7V0CsaLZSkgUAQAxA1GaSBQAAAAAA
+""",
+ """\
+ed25519 gD63hSj3ScS+wuOeGrubXlq35N1c5Lby/S+T7MNTjxo= SbWKPilWYrt+1vgFU3jlxGNOH6I/1npX8wl+KoraN3S6VDsyM6EfCV+JPEK8BsNoM2VIpMcSdjcVna/GwXwZkg== BQAAAEAAAABAAAAApAAAADwBAABTSUcAUEFUSFNSRVBDRVJUSU5EWHH5Ofs4HciIFXjE9egjDbistJptoMXIC7ugCgHhI4NPJqfYY256NpULXKc9c30ul7oHXQyKLfGd84mIAxC3UwQDAAAABAAAAAwAAABSQURJTUlEUFJPT1RAQg8AuOoUh1aSBQANeC4gGGG3a23PpmF+y6CrUS9VWjyj0Ydpl2tMVDLaK2vd5QtYKKJ3UOyprGKk0D/aPn4E3Bk2rE3BKBZRXM1AAgAAAEAAAABTSUcAREVMRci9uvioJssgd8txxFlqz9RqPx+YLVMkHmm24fMUtYGWF/nhkoEYVGT7O+tXSfHHY/KHcUZjVaZpEt/tmXlXBAUDAAAAIAAAACgAAABQVUJLTUlOVE1BWFSxhKhavdriTvCAtNVcK5yr0cAbsWp2MsrwUV5YTc+7V0CsaLZSkgUAQAxA1GaSBQAAAAAA
+ed25519 gD63hSj3ScS+wuOeGrubXlq35N1c5Lby/S+T7MNTjxo= 2A+I9q2+ZayxDDYC5n2YW8Bn/zBm4D3mwS5qMwADRDcbFpBcf3yPOyeZiqpLBTkxo8GT8zMQFeApv4ScffjC8A== BQAAAEAAAABAAAAApAAAADwBAABTSUcAUEFUSFNSRVBDRVJUSU5EWN5Y0b2irPS1JgqJFQMciPg4aWd9qj1ZqcJc5bGXe1m4ZdAXa5OIhXa0+680MgpyhEHhqYJDIwH1XRa1OZx5YAUDAAAABAAAAAwAAABSQURJTUlEUFJPT1RAQg8AgBW3iFaSBQD9WI+Qr6NOZsDmP0PsnCo66mstM3ac5ZON+I+ZeEK8lZWBASvsD2JIfq3v4d1QH5g4STs3wOazQPc25Puy659ZAgAAAEAAAABTSUcAREVMRUJbs67Sb5Wx/jzWyT1PhWR0c4kg59tjSGofo8R3eHzcA9CGwavuRdxOArhVWWODG99gYgfmjcRLgt9/jH+99w4DAAAAIAAAACgAAABQVUJLTUlOVE1BWFRXRfQ1RHLWGOgqABUTYfVBDZrv3OL2nPLYve9ldfNVLOjdPVFFkgUA6D0Vb1mSBQAAAAAA
+""",
+ ]
+ request = [
+ "AgAAAEAAAABOT05DUEFE/9gPiPatvmWssQw2AuZ9mFvAZ/8wZuA95sEuajMAA0Q3GxaQXH98jzsnmYqqSwU5MaPBk/MzEBXgKb+EnH34wvAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
+ "AgAAAEAAAABOT05DUEFE/0m1ij4pVmK7ftb4BVN45cRjTh+iP9Z6V/MJfiqK2jd0ulQ7MjOhHwlfiTxCvAbDaDNlSKTHEnY3FZ2vxsF8GZIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
+ "AgAAAEAAAABOT05DUEFE/0AcDP0F/L7NTiOCQlHovyMlovVtG4lBRqAgydNYk9WOoanOwclZuV8z2b/SCHj5thxbSNxuLNZoDQ2b6TWgPfsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
+ ]
+ response = [
+ "BQAAAEAAAABAAAAApAAAADwBAABTSUcAUEFUSFNSRVBDRVJUSU5EWDwlo/AkUnTrecAW4Ci5Tkh3KOqs6R7KLTsFtq16RXN5F7G5ckGv11UtzHoZTbKbEk03a6ogAOK54Q2CI/7XGA8DAAAABAAAAAwAAABSQURJTUlEUFJPT1RAQg8AWDLihlaSBQAoq/5gEjRCrhfH16X2GYjQJSG/CgSuGhYeCsrw7XkphLI3cxw2unJRDW8DAJrYqEGaW0NPKZk7bbpPjU/Q6Es1AgAAAEAAAABTSUcAREVMRUJbs67Sb5Wx/jzWyT1PhWR0c4kg59tjSGofo8R3eHzcA9CGwavuRdxOArhVWWODG99gYgfmjcRLgt9/jH+99w4DAAAAIAAAACgAAABQVUJLTUlOVE1BWFRXRfQ1RHLWGOgqABUTYfVBDZrv3OL2nPLYve9ldfNVLOjdPVFFkgUA6D0Vb1mSBQAAAAAA",
+ "BQAAAEAAAABAAAAApAAAADwBAABTSUcAUEFUSFNSRVBDRVJUSU5EWHH5Ofs4HciIFXjE9egjDbistJptoMXIC7ugCgHhI4NPJqfYY256NpULXKc9c30ul7oHXQyKLfGd84mIAxC3UwQDAAAABAAAAAwAAABSQURJTUlEUFJPT1RAQg8AuOoUh1aSBQANeC4gGGG3a23PpmF+y6CrUS9VWjyj0Ydpl2tMVDLaK2vd5QtYKKJ3UOyprGKk0D/aPn4E3Bk2rE3BKBZRXM1AAgAAAEAAAABTSUcAREVMRci9uvioJssgd8txxFlqz9RqPx+YLVMkHmm24fMUtYGWF/nhkoEYVGT7O+tXSfHHY/KHcUZjVaZpEt/tmXlXBAUDAAAAIAAAACgAAABQVUJLTUlOVE1BWFSxhKhavdriTvCAtNVcK5yr0cAbsWp2MsrwUV5YTc+7V0CsaLZSkgUAQAxA1GaSBQAAAAAA",
+ "BQAAAEAAAABAAAAApAAAADwBAABTSUcAUEFUSFNSRVBDRVJUSU5EWN5Y0b2irPS1JgqJFQMciPg4aWd9qj1ZqcJc5bGXe1m4ZdAXa5OIhXa0+680MgpyhEHhqYJDIwH1XRa1OZx5YAUDAAAABAAAAAwAAABSQURJTUlEUFJPT1RAQg8AgBW3iFaSBQD9WI+Qr6NOZsDmP0PsnCo66mstM3ac5ZON+I+ZeEK8lZWBASvsD2JIfq3v4d1QH5g4STs3wOazQPc25Puy659ZAgAAAEAAAABTSUcAREVMRUJbs67Sb5Wx/jzWyT1PhWR0c4kg59tjSGofo8R3eHzcA9CGwavuRdxOArhVWWODG99gYgfmjcRLgt9/jH+99w4DAAAAIAAAACgAAABQVUJLTUlOVE1BWFRXRfQ1RHLWGOgqABUTYfVBDZrv3OL2nPLYve9ldfNVLOjdPVFFkgUA6D0Vb1mSBQAAAAAA",
+ ]
+
+ server_request = request[0]
+ server_response = response[0]
+ test_cli("roughtime", [], expected_stderr=b'Please specify either --servers-file or --host and --pubkey\n')
+
+ with open(ecosystem, 'w') as f:
+ f.write("Cloudflare-Roughtime ed25519 gD63hSj4ScS+wuOeGrubXlq35N1c5Lby/S+T7MNTjxo= udp 127.0.0.1:" + str(server_port))
+
+ test_cli("roughtime", [
+ "--check-local-clock=0",
+ "--chain-file=",
+ "--servers-file=" + ecosystem]
+ , expected_stderr=b'ERROR: Public key does not match!\n')
+
+ with open(ecosystem, 'w') as f:
+ f.write("Cloudflare-Roughtime ed25519 gD63hSj3ScS+wuOeGrubXlq35N1c5Lby/S+T7MNTjxo= udp 127.0.0.1:" + str(server_port))
+
+ test_cli("roughtime", [
+ "--chain-file=",
+ "--servers-file=" + ecosystem]
+ , expected_stderr=b'ERROR: Local clock mismatch\n')
+
+ test_cli("roughtime", [
+ "--check-local-clock=0",
+ "--chain-file=" + chain_file,
+ "--servers-file=" + ecosystem]
+ , "Cloudflare-Roughtime : UTC 2019-09-12T08:00:11 (+-1000000us)")
+
+ with open(chain_file, 'r') as f:
+ read_data = f.read()
+ if read_data != chain[0]:
+ logging.error("unexpected chain")
+
+ server_request = request[1]
+ server_response = response[1]
+ test_cli("roughtime", [
+ "--check-local-clock=0",
+ "--chain-file=" + chain_file,
+ "--host=127.0.0.1:" + str(server_port),
+ "--pubkey=gD63hSj3ScS+wuOeGrubXlq35N1c5Lby/S+T7MNTjxo=",
+ "--raw-time"]
+ , "UTC 1568275214691000 (+-1000000us)")
+
+ with open(chain_file, 'r') as f:
+ read_data = f.read()
+ if read_data != chain[1]:
+ logging.error("unexpected chain")
+
+ server_request = request[2]
+ server_response = response[2]
+ test_cli("roughtime", [
+ "--check-local-clock=0",
+ "--chain-file=" + chain_file,
+ "--host=127.0.0.1:" + str(server_port),
+ "--pubkey=gD63hSj3ScS+wuOeGrubXlq35N1c5Lby/S+T7MNTjxo=",
+ "--max-chain-size=2"]
+ , "UTC 2019-09-12T08:00:42 (+-1000000us)")
+
+ with open(chain_file, 'r') as f:
+ read_data = f.read()
+ if read_data != chain[2]:
+ logging.error("unexpected chain")
+
+def cli_pk_workfactor_tests(_tmp_dir):
+ test_cli("pk_workfactor", "1024", "80")
+ test_cli("pk_workfactor", "2048", "111")
+ test_cli("pk_workfactor", ["--type=rsa", "512"], "58")
+ test_cli("pk_workfactor", ["--type=dl", "512"], "58")
+ test_cli("pk_workfactor", ["--type=dl_exp", "512"], "128")
+
+def cli_dl_group_info_tests(_tmp_dir):
+
+ dl_output = re.compile('(P|G) = [A-F0-9]+')
+
+ for bits in [1024, 1536, 2048, 3072, 4096, 6144, 8192]:
+ output = test_cli("dl_group_info", "modp/ietf/%d" % (bits))
+ lines = output.split('\n')
+
+ if len(lines) != 2:
+ logging.error('Unexpected output from dl_group_info')
+
+ for l in lines:
+ if not dl_output.match(l):
+ logging.error('Unexpected output from dl_group_info')
+
+
+
+def cli_ec_group_info_tests(_tmp_dir):
+
+ # pylint: disable=line-too-long
+ secp256r1_info = """P = FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF
+A = FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC
+B = 5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B
+N = FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551
+G = 6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296,4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5"""
+
+ secp256r1_pem = """-----BEGIN EC PARAMETERS-----
+MIHgAgEBMCwGByqGSM49AQECIQD/////AAAAAQAAAAAAAAAAAAAAAP//////////
+/////zBEBCD/////AAAAAQAAAAAAAAAAAAAAAP///////////////AQgWsY12Ko6
+k+ez671VdpiGvGUdBrDMU7D2O848PifSYEsEQQRrF9Hy4SxCR/i85uVjpEDydwN9
+gS3rM6D0oTlF2JjClk/jQuL+Gn+bjufrSnwPnhYrzjNXazFezsu2QGg3v1H1AiEA
+/////wAAAAD//////////7zm+q2nF56E87nKwvxjJVECAQE=
+-----END EC PARAMETERS-----"""
+
+ test_cli("ec_group_info", "secp256r1", secp256r1_info)
+ test_cli("ec_group_info", "--pem secp256r1", secp256r1_pem)
+
+def cli_cpuid_tests(_tmp_dir):
+ cpuid_output = test_cli("cpuid", [])
+
+ if not cpuid_output.startswith('CPUID flags:'):
+ logging.error('Unexpected cpuid output "%s"' % (cpuid_output))
+
+ flag_re = re.compile('[a-z0-9_]+')
+ flags = cpuid_output[13:].split(' ')
+ for flag in flags:
+ if flag != '' and flag_re.match(flag) is None:
+ logging.error('Unexpected CPUID flag name "%s"' % (flag))
+
+def cli_cc_enc_tests(_tmp_dir):
+ test_cli("cc_encrypt", ["8028028028028029", "pass"], "4308989841607208")
+ test_cli("cc_decrypt", ["4308989841607208", "pass"], "8028028028028027")
+
+def cli_cert_issuance_tests(tmp_dir):
+ root_key = os.path.join(tmp_dir, 'root.key')
+ root_crt = os.path.join(tmp_dir, 'root.crt')
+ int_key = os.path.join(tmp_dir, 'int.key')
+ int_crt = os.path.join(tmp_dir, 'int.crt')
+ int_csr = os.path.join(tmp_dir, 'int.csr')
+ leaf_key = os.path.join(tmp_dir, 'leaf.key')
+ leaf_crt = os.path.join(tmp_dir, 'leaf.crt')
+ leaf_csr = os.path.join(tmp_dir, 'leaf.csr')
+
+ test_cli("keygen", ["--params=2048", "--output=" + root_key], "")
+ test_cli("keygen", ["--params=2048", "--output=" + int_key], "")
+ test_cli("keygen", ["--params=2048", "--output=" + leaf_key], "")
+
+ test_cli("gen_self_signed",
+ [root_key, "Root", "--ca", "--path-limit=2", "--output="+root_crt], "")
+
+ test_cli("gen_pkcs10", "%s Intermediate --ca --output=%s" % (int_key, int_csr))
+ test_cli("sign_cert", "%s %s %s --output=%s" % (root_crt, root_key, int_csr, int_crt))
+
+ test_cli("gen_pkcs10", "%s Leaf --output=%s" % (leaf_key, leaf_csr))
+ test_cli("sign_cert", "%s %s %s --output=%s" % (int_crt, int_key, leaf_csr, leaf_crt))
+
+ test_cli("cert_verify" "%s %s %s" % (leaf_crt, int_crt, root_crt), "Certificate passes validation checks")
+
+def cli_timing_test_tests(_tmp_dir):
+
+ timing_tests = ["bleichenbacher", "manger",
+ "ecdsa", "ecc_mul", "inverse_mod", "pow_mod",
+ "lucky13sec3", "lucky13sec4sha1",
+ "lucky13sec4sha256", "lucky13sec4sha384"]
+
+ output_re = re.compile('[0-9]+;[0-9];[0-9]+')
+
+ for suite in timing_tests:
+ output = test_cli("timing_test", [suite, "--measurement-runs=16", "--warmup-runs=3"], None).split('\n')
+
+ for line in output:
+ if output_re.match(line) is None:
+ logging.error("Unexpected output in timing_test %s: %s", suite, line)
+
+def cli_tls_ciphersuite_tests(_tmp_dir):
+ policies = ['default', 'suiteb_128', 'suiteb_192', 'strict', 'all']
+
+ versions = ['tls1.0', 'tls1.1', 'tls1.2']
+
+ ciphersuite_re = re.compile('^[A-Z0-9_]+$')
+
+ for policy in policies:
+ for version in versions:
+
+ if version != 'tls1.2' and policy != 'all':
+ continue
+
+ output = test_cli("tls_ciphers", ["--version=" + version, "--policy=" + policy], None).split('\n')
+
+ for line in output:
+ if ciphersuite_re.match(line) is None:
+ logging.error("Unexpected ciphersuite line %s", line)
+
+def cli_asn1_tests(_tmp_dir):
+ input_pem = """-----BEGIN BLOB-----
+MCACAQUTBnN0cmluZzEGAQH/AgFjBAUAAAAAAAMEAP///w==
+-----END BLOB------
+"""
+
+ expected = """d= 0, l= 32: SEQUENCE
+ d= 1, l= 1: INTEGER 05
+ d= 1, l= 6: PRINTABLE STRING string
+ d= 1, l= 6: SET
+ d= 2, l= 1: BOOLEAN true
+ d= 2, l= 1: INTEGER 63
+ d= 1, l= 5: OCTET STRING 0000000000
+ d= 1, l= 4: BIT STRING FFFFFF"""
+
+ test_cli("asn1print", "--pem -", expected, input_pem)
+
+def cli_tls_socket_tests(tmp_dir):
+ client_msg = b'Client message %d\n' % (random.randint(0, 2**128))
+ server_port = random_port_number()
+
+ priv_key = os.path.join(tmp_dir, 'priv.pem')
+ ca_cert = os.path.join(tmp_dir, 'ca.crt')
+ crt_req = os.path.join(tmp_dir, 'crt.req')
+ server_cert = os.path.join(tmp_dir, 'server.crt')
+
+ test_cli("keygen", ["--algo=ECDSA", "--params=secp256r1", "--output=" + priv_key], "")
+
+ test_cli("gen_self_signed",
+ [priv_key, "CA", "--ca", "--country=VT",
+ "--dns=ca.example", "--hash=SHA-384", "--output="+ca_cert],
+ "")
+
+ test_cli("cert_verify", ca_cert, "Certificate did not validate - Cannot establish trust")
+
+ test_cli("gen_pkcs10", "%s localhost --output=%s" % (priv_key, crt_req))
+
+ test_cli("sign_cert", "%s %s %s --output=%s" % (ca_cert, priv_key, crt_req, server_cert))
+
+ tls_server = subprocess.Popen([CLI_PATH, 'tls_server', '--max-clients=1',
+ '--port=%d' % (server_port), server_cert, priv_key],
+ stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+
+ wait_time = 1.0
+
+ time.sleep(wait_time)
+
+ tls_client = subprocess.Popen([CLI_PATH, 'tls_client', 'localhost',
+ '--port=%d' % (server_port), '--trusted-cas=%s' % (ca_cert)],
+ stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+
+ time.sleep(wait_time)
+
+ tls_client.stdin.write(client_msg)
+ tls_client.stdin.flush()
+
+ time.sleep(wait_time)
+
+ (stdout, stderr) = tls_client.communicate()
+
+ if stderr:
+ logging.error("Got unexpected stderr output %s" % (stderr))
+
+ if b'Handshake complete' not in stdout:
+ logging.error('Failed to complete handshake: %s' % (stdout))
+
+ if client_msg not in stdout:
+ logging.error("Missing client message from stdout %s" % (stdout))
+
+ tls_server.communicate()
+
+def cli_tls_http_server_tests(tmp_dir):
+ if not check_for_command("tls_http_server"):
+ return
+
+ try:
+ from http.client import HTTPSConnection
+ except ImportError:
+ try:
+ from httplib import HTTPSConnection
+ except ImportError:
+ return
+ import ssl
+
+ server_port = random_port_number()
+
+ priv_key = os.path.join(tmp_dir, 'priv.pem')
+ ca_cert = os.path.join(tmp_dir, 'ca.crt')
+ crt_req = os.path.join(tmp_dir, 'crt.req')
+ server_cert = os.path.join(tmp_dir, 'server.crt')
+
+ test_cli("keygen", ["--algo=ECDSA", "--params=secp384r1", "--output=" + priv_key], "")
+
+ test_cli("gen_self_signed",
+ [priv_key, "CA", "--ca", "--country=VT",
+ "--dns=ca.example", "--hash=SHA-384", "--output="+ca_cert],
+ "")
+
+ test_cli("gen_pkcs10", "%s localhost --output=%s" % (priv_key, crt_req))
+
+ test_cli("sign_cert", "%s %s %s --output=%s" % (ca_cert, priv_key, crt_req, server_cert))
+
+ tls_server = subprocess.Popen([CLI_PATH, 'tls_http_server', '--max-clients=2',
+ '--port=%d' % (server_port), server_cert, priv_key],
+ stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+
+ wait_time = 1.0
+ time.sleep(wait_time)
+
+ context = ssl.create_default_context(cafile=ca_cert)
+ conn = HTTPSConnection('localhost', port=server_port, context=context)
+ conn.request("GET", "/")
+ resp = conn.getresponse()
+
+ if resp.status != 200:
+ logging.error('Unexpected response status %d' % (resp.status))
+
+ body = str(resp.read())
+
+ if body.find('TLS negotiation with Botan 2.') < 0:
+ logging.error('Unexpected response body')
+
+ conn.request("POST", "/logout")
+ resp = conn.getresponse()
+
+ if resp.status != 405:
+ logging.error('Unexpected response status %d' % (resp.status))
+
+ if sys.version_info.major >= 3:
+ rc = tls_server.wait(5) # pylint: disable=too-many-function-args
+ else:
+ rc = tls_server.wait()
+
+ if rc != 0:
+ logging.error("Unexpected return code from https_server %d", rc)
+
+def cli_tls_proxy_tests(tmp_dir):
+ # pylint: disable=too-many-locals,too-many-statements
+ if not check_for_command("tls_proxy"):
+ return
+
+ try:
+ from http.client import HTTPSConnection
+ except ImportError:
+ try:
+ from httplib import HTTPSConnection
+ except ImportError:
+ return
+
+ try:
+ from http.server import HTTPServer, BaseHTTPRequestHandler
+ except ImportError:
+ try:
+ from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler
+ except ImportError:
+ return
+
+ import ssl
+ import threading
+
+ server_port = random_port_number()
+ proxy_port = random_port_number()
+
+ while server_port == proxy_port:
+ proxy_port = random_port_number()
+
+ priv_key = os.path.join(tmp_dir, 'priv.pem')
+ ca_cert = os.path.join(tmp_dir, 'ca.crt')
+ crt_req = os.path.join(tmp_dir, 'crt.req')
+ server_cert = os.path.join(tmp_dir, 'server.crt')
+
+ test_cli("keygen", ["--algo=ECDSA", "--params=secp384r1", "--output=" + priv_key], "")
+
+ test_cli("gen_self_signed",
+ [priv_key, "CA", "--ca", "--country=VT",
+ "--dns=ca.example", "--hash=SHA-384", "--output="+ca_cert],
+ "")
+
+ test_cli("gen_pkcs10", "%s localhost --output=%s" % (priv_key, crt_req))
+
+ test_cli("sign_cert", "%s %s %s --output=%s" % (ca_cert, priv_key, crt_req, server_cert))
+
+ tls_proxy = subprocess.Popen([CLI_PATH, 'tls_proxy', str(proxy_port), '127.0.0.1', str(server_port),
+ server_cert, priv_key, '--output=/tmp/proxy.err', '--max-clients=2'],
+ stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+
+ wait_time = 1.0
+
+ time.sleep(wait_time)
+
+ server_response = binascii.hexlify(os.urandom(32))
+
+ def run_http_server():
+ class Handler(BaseHTTPRequestHandler):
+
+ def do_GET(self): # pylint: disable=invalid-name
+ self.send_response(200)
+ self.end_headers()
+ self.wfile.write(server_response)
+
+ httpd = HTTPServer(('', server_port), Handler)
+ httpd.serve_forever()
+
+ http_thread = threading.Thread(target=run_http_server)
+ http_thread.daemon = True
+ http_thread.start()
+
+ time.sleep(wait_time)
+
+ context = ssl.create_default_context(cafile=ca_cert)
+
+ for _i in range(2):
+ conn = HTTPSConnection('localhost', port=proxy_port, context=context)
+ conn.request("GET", "/")
+ resp = conn.getresponse()
+
+ if resp.status != 200:
+ logging.error('Unexpected response status %d' % (resp.status))
+
+ body = resp.read()
+
+ if body != server_response:
+ logging.error('Unexpected response from server %s' % (body))
+
+ if sys.version_info.major >= 3:
+ rc = tls_proxy.wait(5) # pylint: disable=too-many-function-args
+ else:
+ rc = tls_proxy.wait()
+
+ if rc != 0:
+ logging.error('Unexpected return code %d', rc)
+
+def cli_trust_root_tests(tmp_dir):
+ pem_file = os.path.join(tmp_dir, 'pems')
+ dn_file = os.path.join(tmp_dir, 'dns')
+
+ test_cli("trust_roots", ['--dn-only', '--output=%s' % (dn_file)], "")
+
+ dn_re = re.compile('(.+=\".+\")(,.+=\".+\")')
+ for line in open(dn_file):
+ if dn_re.match(line) is None:
+ logging.error("Unexpected DN line %s", line)
+
+ test_cli("trust_roots", ['--output=%s' % (pem_file)], "")
+
+def cli_tss_tests(tmp_dir):
+ data_file = os.path.join(tmp_dir, 'data')
+
+ exp_hash = "53B3C59276AE30EA7FD882268E80FD96AD80CC9FEB15F9FB940E7C4B5CF80B9E"
+
+ test_cli("rng", ["32", "--output=%s" % (data_file)], "")
+ test_cli("hash", ["--no-fsname", data_file], exp_hash)
+
+ m = 3
+ n = 5
+
+ test_cli("tss_split", [str(m), str(n), data_file, "--share-prefix=%s/split" % (tmp_dir)], "")
+
+ share_files = []
+
+ for i in range(1, n+1):
+ share = os.path.join(tmp_dir, "split%d.tss" % (i))
+ if not os.access(share, os.R_OK):
+ logging.error("Failed to create expected split file %s", share)
+ share_files.append(share)
+
+ rec5 = os.path.join(tmp_dir, "recovered_5")
+ test_cli("tss_recover", share_files + ["--output=%s" % (rec5)], "")
+ test_cli("hash", ["--no-fsname", rec5], exp_hash)
+
+ rec4 = os.path.join(tmp_dir, "recovered_4")
+ test_cli("tss_recover", share_files[1:] + ["--output=%s" % (rec4)], "")
+ test_cli("hash", ["--no-fsname", rec4], exp_hash)
+
+ rec3 = os.path.join(tmp_dir, "recovered_3")
+ test_cli("tss_recover", share_files[2:] + ["--output=%s" % (rec3)], "")
+ test_cli("hash", ["--no-fsname", rec3], exp_hash)
+
+ rec2 = os.path.join(tmp_dir, "recovered_2")
+ test_cli("tss_recover", share_files[3:] + ["--output=%s" % (rec2)], "", None,
+ b'Error: Insufficient shares to do TSS reconstruction\n')
+
+
+def cli_pk_encrypt_tests(tmp_dir):
+ input_file = os.path.join(tmp_dir, 'input')
+ ctext_file = os.path.join(tmp_dir, 'ctext')
+ recovered_file = os.path.join(tmp_dir, 'recovered')
+ rsa_priv_key = os.path.join(tmp_dir, 'rsa.priv')
+ rsa_pub_key = os.path.join(tmp_dir, 'rsa.pub')
+
+ test_cli("keygen", ["--algo=RSA", "--provider=base", "--params=2048", "--output=%s" % (rsa_priv_key)], "")
+
+ key_hash = "72AF3227EF57A728E894D54623EB8E2C0CD11A4A98BF2DF32DB052BF60897873"
+ test_cli("hash", ["--no-fsname", "--algo=SHA-256", rsa_priv_key], key_hash)
+
+ test_cli("pkcs8", ["--pub-out", "%s/rsa.priv" % (tmp_dir), "--output=%s" % (rsa_pub_key)], "")
+
+ # Generate a random input file
+ test_cli("rng", ["10", "16", "32", "--output=%s" % (input_file)], "")
+
+ # Because we used a fixed DRBG for each invocation the same ctext is generated each time
+ rng_output_hash = "32F5E7B61357DE8397EFDA1E598379DFD5EE21767BDF4E2A435F05117B836AC6"
+ ctext_hash = "FF1F0EEC2C42DD61D78505C5DF624A19AE6FE2BAB0B8F7D878C7655D54C68FE0"
+
+ test_cli("hash", ["--no-fsname", "--algo=SHA-256", input_file], rng_output_hash)
+
+ # Encrypt and verify ciphertext is the expected value
+ test_cli("pk_encrypt", [rsa_pub_key, input_file, "--output=%s" % (ctext_file)], "")
+ test_cli("hash", ["--no-fsname", "--algo=SHA-256", ctext_file], ctext_hash)
+
+ # Decrypt and verify plaintext is recovered
+ test_cli("pk_decrypt", [rsa_priv_key, ctext_file, "--output=%s" % (recovered_file)], "")
+ test_cli("hash", ["--no-fsname", "--algo=SHA-256", recovered_file], rng_output_hash)
+
+def cli_uuid_tests(_tmp_dir):
+ test_cli("uuid", [], "D80F88F6-ADBE-45AC-B10C-3602E67D985B")
+
+ uuid_re = re.compile(r'[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}')
+
+ output = test_cli("uuid", [])
+
+ if uuid_re.match(output) is None:
+ logging.error('Bad uuid output %s' % (output))
+
+def cli_tls_client_hello_tests(_tmp_dir):
+
+ # pylint: disable=line-too-long
+ chello = "16030100cf010000cb03035b3cf2457b864d7bef2a4b1f84fc3ced2b68d9551f3455ffdd305af277a91bb200003a16b816b716ba16b9cca9cca8c02cc030c02bc02fc0adc0acc024c00ac028c014c023c009c027c013ccaa009f009ec09fc09e006b003900670033010000680000000e000c000009676d61696c2e636f6d000500050100000000000a001a0018001d0017001a0018001b0019001c01000101010201030104000b00020100000d00140012080508040806050106010401050306030403001600000017000000230000ff01000100"
+
+ output = test_cli("tls_client_hello", ["--hex", "-"], None, chello)
+
+ output_hash = "8EBFC3205ACFA98461128FE5D081D19254237AF84F7DAF000A3C992C3CF6DE44"
+ test_cli("hash", ["--no-fsname", "--algo=SHA-256", "-"], output_hash, output)
+
+def cli_speed_pk_tests(_tmp_dir):
+ msec = 1
+
+ pk_algos = ["ECDSA", "ECDH", "SM2", "ECKCDSA", "ECGDSA", "GOST-34.10",
+ "DH", "DSA", "ElGamal", "Ed25519", "Curve25519", "NEWHOPE", "McEliece",
+ "RSA", "RSA_keygen", "XMSS"]
+
+ 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-z ]+)/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_pbkdf_tests(_tmp_dir):
+ msec = 1
+ pbkdf_ops = ['bcrypt', 'passhash9', 'argon2']
+
+ 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:
+ output = test_cli("speed", ["--msec=%d" % (msec), op], None).split('\n')
+ for line in output:
+ if format_re.match(line) is None:
+ logging.error("Unexpected line %s", line)
+
+def cli_speed_table_tests(_tmp_dir):
+ msec = 1
+
+ version_re = re.compile(r'^Botan 2\.[0-9]+\.[0-9] \(.*, revision .*, distribution .*\)')
+ cpuid_re = re.compile(r'^CPUID: [a-z_0-9 ]*$')
+ format_re = re.compile(r'^AES-128 .* 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$')
+ tbl_val_re = re.compile(r'^AES-128 +(encrypt|decrypt) +[0-9]+(\.[0-9]{2})$')
+
+ output = test_cli("speed", ["--format=table", "--provider=base", "--msec=%d" % (msec), "AES-128"], None).split('\n')
+
+ if len(output) != 11:
+ logging.error('Unexpected number of lines from table output')
+
+ if version_re.match(output[0]) is None:
+ logging.error("Unexpected version line %s", output[0])
+
+ if output[1] != '':
+ if cpuid_re.match(output[1]) is None:
+ logging.error("Unexpected cpuid line %s", output[1])
+ elif output[2] != '':
+ logging.error("Expected newline got %s", output[2])
+
+ if format_re.match(output[3]) is None:
+ logging.error("Unexpected line %s", output[3])
+ if format_re.match(output[4]) is None:
+ logging.error("Unexpected line %s", output[4])
+ if output[5] != '':
+ logging.error("Expected newline got %s", output[5])
+
+ if tbl_hdr_re.match(output[6]) is None:
+ logging.error("Unexpected table header %s", output[6])
+ if tbl_val_re.match(output[7]) is None:
+ logging.error("Unexpected table header %s", output[7])
+ if tbl_val_re.match(output[8]) is None:
+ logging.error("Unexpected table header %s", output[8])
+ if output[9] != '':
+ logging.error("Expected newline got %s", output[9])
+ if output[10].find('results are the number of 1000s bytes processed per second') < 0:
+ logging.error("Unexpected trailing message got %s", output[10])
+
+def cli_speed_invalid_option_tests(_tmp_dir):
+ speed_usage = b"Usage: speed --msec=500 --format=default --ecc-groups= --provider= --buf-size=1024 --clear-cpuid= --cpu-clock-speed=0 --cpu-clock-ratio=1.0 *algos\n"
+
+ test_cli("speed", ["--buf-size=0", "--msec=1", "AES-128"],
+ expected_stderr=b"Usage error: Cannot have a zero-sized buffer\n%s" % (speed_usage))
+
+ test_cli("speed", ["--buf-size=F00F", "--msec=1", "AES-128"],
+ expected_stderr=b"Usage error: Invalid integer value 'F00F' for option buf-size\n%s" % (speed_usage))
+
+ test_cli("speed", ["--buf-size=90000000", "--msec=1", "AES-128"],
+ expected_stderr=b"Usage error: Specified buffer size is too large\n%s" % (speed_usage))
+
+ test_cli("speed", ["--clear-cpuid=goku", "--msec=1", "AES-128"],
+ expected_stderr=b"Warning don't know CPUID flag 'goku'\n")
+
+def cli_speed_math_tests(_tmp_dir):
+ 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_init', 'poly_dbl',
+ 'bn_redc', 'nistp_redc', 'ecc_mult', 'ecc_ops', 'os2ecp', 'primality_test']
+
+ 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:
+ output = test_cli("speed", ["--msec=%d" % (msec), op], None).split('\n')
+ for line in output:
+ if format_re.match(line) is None:
+ logging.error("Unexpected line %s", line)
+
+def cli_speed_tests(_tmp_dir):
+ # pylint: disable=too-many-branches
+
+ msec = 1
+
+ output = test_cli("speed", ["--msec=%d" % (msec), "--buf-size=64,512", "AES-128"], None).split('\n')
+
+ if len(output) % 4 != 0:
+ logging.error("Unexpected number of lines for AES-128 speed test")
+
+ # pylint: disable=line-too-long
+ format_re = re.compile(r'^AES-128 .* buffer size [0-9]+ bytes: [0-9]+\.[0-9]+ MiB\/sec .*\([0-9]+\.[0-9]+ MiB in [0-9]+\.[0-9]+ ms\)')
+ for line in output:
+ if format_re.match(line) is None:
+ logging.error("Unexpected line %s", line)
+
+ output = test_cli("speed", ["--msec=%d" % (msec), "ChaCha20", "SHA-256", "HMAC(SHA-256)"], None).split('\n')
+
+ # pylint: disable=line-too-long
+ 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\)')
+ for line in output:
+ if format_re.match(line) is None:
+ logging.error("Unexpected line %s", line)
+
+ output = test_cli("speed", ["--msec=%d" % (msec), "AES-128/GCM"], None).split('\n')
+ format_re_ks = re.compile(r'^AES-128/GCM\(16\).* [0-9]+ key schedule/sec; [0-9]+\.[0-9]+ ms/op .*\([0-9]+ (op|ops) in [0-9\.]+ ms\)')
+ format_re_cipher = re.compile(r'^AES-128/GCM\(16\) .* buffer size [0-9]+ bytes: [0-9]+\.[0-9]+ MiB\/sec .*\([0-9]+\.[0-9]+ MiB in [0-9]+\.[0-9]+ ms\)')
+ for line in output:
+ if format_re_ks.match(line) is None:
+ if format_re_cipher.match(line) is None:
+ logging.error('Unexpected line %s', line)
+
+ output = test_cli("speed", ["--msec=%d" % (msec), "scrypt"], None).split('\n')
+
+ format_re = re.compile(r'^scrypt-[0-9]+-[0-9]+-[0-9]+ \([0-9]+ MiB\) [0-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)
+
+ output = test_cli("speed", ["--msec=%d" % (msec), "RNG"], None).split('\n')
+
+ # ChaCha_RNG generate buffer size 1024 bytes: 954.431 MiB/sec 4.01 cycles/byte (477.22 MiB in 500.00 ms)
+ format_re = re.compile(r'^.* generate buffer size [0-9]+ bytes: [0-9]+\.[0-9]+ MiB/sec .*\([0-9]+\.[0-9]+ MiB in [0-9]+\.[0-9]+ ms')
+ for line in output:
+ if format_re.match(line) is None:
+ logging.error("Unexpected line %s", line)
+
+ # Entropy source rdseed output 128 bytes estimated entropy 0 in 0.02168 ms total samples 32
+ output = test_cli("speed", ["--msec=%d" % (msec), "entropy"], None).split('\n')
+ format_re = re.compile(r'^Entropy source [_a-z0-9]+ output [0-9]+ bytes estimated entropy [0-9]+ in [0-9]+\.[0-9]+ ms .*total samples [0-9]+')
+ for line in output:
+ if format_re.match(line) is None:
+ logging.error("Unexpected line %s", line)
+
+ output = test_cli("speed", ["--msec=%d" % (msec), "--format=json", "AES-128"], None)
+
+ json_blob = json.loads(output)
+ if len(json_blob) < 2:
+ logging.error("Unexpected size for JSON output")
+
+ for b in json_blob:
+ 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))
+
+def run_test(fn_name, fn):
+ start = time.time()
+ tmp_dir = tempfile.mkdtemp(prefix='botan_cli_')
+ try:
+ fn(tmp_dir)
+ except Exception as e: # pylint: disable=broad-except
+ logging.error("Test %s threw exception: %s", fn_name, e)
+
+ shutil.rmtree(tmp_dir)
+ end = time.time()
+ logging.info("Ran %s in %.02f sec", fn_name, end-start)
+
+def main(args=None):
+ # pylint: disable=too-many-branches,too-many-locals
+ if args is None:
+ args = sys.argv
+
+ parser = optparse.OptionParser(
+ formatter=optparse.IndentedHelpFormatter(max_help_position=50))
+
+ parser.add_option('--verbose', action='store_true', default=False)
+ parser.add_option('--quiet', action='store_true', default=False)
+ parser.add_option('--threads', action='store', type='int', default=0)
+
+ (options, args) = parser.parse_args(args)
+
+ setup_logging(options)
+
+ if len(args) < 2:
+ logging.error("Usage: %s path_to_botan_cli [test_regex]", args[0])
+ return 1
+
+ if not os.access(args[1], os.X_OK):
+ logging.error("Could not access/execute %s", args[1])
+ return 2
+
+ threads = options.threads
+ if threads == 0:
+ threads = multiprocessing.cpu_count()
+
+ global CLI_PATH
+ CLI_PATH = args[1]
+
+ test_regex = None
+ if len(args) == 3:
+ try:
+ test_regex = re.compile(args[2])
+ except re.error as e:
+ logging.error("Invalid regex: %s", str(e))
+ return 1
+
+ # some of the slowest tests are grouped up front
+ test_fns = [
+ cli_speed_tests,
+ cli_speed_pk_tests,
+ cli_speed_math_tests,
+ cli_speed_pbkdf_tests,
+ cli_speed_table_tests,
+ cli_speed_invalid_option_tests,
+ cli_xmss_sign_tests,
+
+ cli_argon2_tests,
+ cli_asn1_tests,
+ cli_base32_tests,
+ cli_base58_tests,
+ cli_base64_tests,
+ cli_bcrypt_tests,
+ cli_cc_enc_tests,
+ cli_cycle_counter,
+ cli_cert_issuance_tests,
+ cli_compress_tests,
+ cli_config_tests,
+ cli_cpuid_tests,
+ cli_dl_group_info_tests,
+ cli_ec_group_info_tests,
+ cli_entropy_tests,
+ cli_factor_tests,
+ cli_gen_dl_group_tests,
+ cli_gen_prime_tests,
+ cli_hash_tests,
+ cli_help_tests,
+ cli_hex_tests,
+ cli_hmac_tests,
+ cli_is_prime_tests,
+ cli_key_tests,
+ cli_mod_inverse_tests,
+ cli_pbkdf_tune_tests,
+ cli_pk_encrypt_tests,
+ cli_pk_workfactor_tests,
+ cli_psk_db_tests,
+ cli_rng_tests,
+ cli_roughtime_check_tests,
+ cli_roughtime_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_trust_root_tests,
+ cli_tss_tests,
+ cli_uuid_tests,
+ cli_version_tests,
+ ]
+
+ tests_to_run = []
+ for fn in test_fns:
+ fn_name = fn.__name__
+
+ if test_regex is None or test_regex.search(fn_name) is not None:
+ tests_to_run.append((fn_name, fn))
+
+ start_time = time.time()
+
+ if threads > 1:
+ pool = ThreadPool(processes=threads)
+ results = []
+ for test in tests_to_run:
+ results.append(pool.apply_async(run_test, test))
+
+ for result in results:
+ result.get()
+ else:
+ for test in tests_to_run:
+ run_test(test[0], test[1])
+
+ end_time = time.time()
+
+ print("Ran %d tests with %d failures in %.02f seconds" % (
+ TESTS_RUN, TESTS_FAILED, end_time - start_time))
+
+ if TESTS_FAILED > 0:
+ return 1
+ return 0
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/comm/third_party/botan/src/scripts/test_cli_crypt.py b/comm/third_party/botan/src/scripts/test_cli_crypt.py
new file mode 100755
index 0000000000..6160d03690
--- /dev/null
+++ b/comm/third_party/botan/src/scripts/test_cli_crypt.py
@@ -0,0 +1,220 @@
+#!/usr/bin/env python
+
+import binascii
+import argparse
+import re
+import subprocess
+import sys
+import os.path
+import logging
+import time
+from collections import OrderedDict
+import multiprocessing
+from multiprocessing.pool import ThreadPool
+
+SUPPORTED_ALGORITHMS = {
+ "AES-128/CFB": "aes-128-cfb",
+ "AES-192/CFB": "aes-192-cfb",
+ "AES-256/CFB": "aes-256-cfb",
+ "AES-128/GCM": "aes-128-gcm",
+ "AES-192/GCM": "aes-192-gcm",
+ "AES-256/GCM": "aes-256-gcm",
+ "AES-128/OCB": "aes-128-ocb",
+ "AES-128/XTS": "aes-128-xts",
+ "AES-256/XTS": "aes-256-xts",
+ "ChaCha20Poly1305": "chacha20poly1305",
+}
+
+class VecDocument:
+ def __init__(self, filepath):
+ self.data = OrderedDict()
+ last_testcase_number = 1
+ current_testcase_number = 1
+ current_group_name = ""
+ last_group_name = ""
+ current_testcase = {}
+
+ PATTERN_GROUPHEADER = "^\[(.+)\]$"
+ PATTERN_KEYVALUE = "^\s*([a-zA-Z]+)\s*=(.*)$"
+
+ with open(filepath, 'r') as f:
+ # Append one empty line to simplify parsing
+ lines = f.read().splitlines() + ["\n"]
+
+ for line in lines:
+ line = line.strip()
+ if line.startswith("#"):
+ pass # Skip
+ elif line == "":
+ current_testcase_number += 1
+ elif re.match(PATTERN_GROUPHEADER, line):
+ match = re.match(PATTERN_GROUPHEADER, line)
+ current_group_name = match.group(1)
+ elif re.match(PATTERN_KEYVALUE, line):
+ match = re.match(PATTERN_KEYVALUE, line)
+ key = match.group(1)
+ value = match.group(2).strip()
+ current_testcase[key] = value
+
+ if current_testcase_number != last_testcase_number:
+ if not current_group_name in self.data:
+ self.data[current_group_name] = []
+ if len(current_testcase) != 0:
+ self.data[current_group_name].append(current_testcase)
+ current_testcase = {}
+ last_testcase_number = current_testcase_number
+
+ if current_group_name != last_group_name:
+ last_group_name = current_group_name
+ # Reset testcase number
+ last_testcase_number = 1
+ current_testcase_number = 1
+
+ def get_data(self):
+ return self.data
+
+TESTS_RUN = 0
+TESTS_FAILED = 0
+
+class TestLogHandler(logging.StreamHandler, object):
+ def emit(self, record):
+ # Do the default stuff first
+ super(TestLogHandler, self).emit(record)
+ if record.levelno >= logging.ERROR:
+ global TESTS_FAILED
+ TESTS_FAILED += 1
+
+def setup_logging(options):
+ if options.verbose:
+ log_level = logging.DEBUG
+ elif options.quiet:
+ log_level = logging.WARNING
+ else:
+ log_level = logging.INFO
+
+ lh = TestLogHandler(sys.stdout)
+ lh.setFormatter(logging.Formatter('%(levelname) 7s: %(message)s'))
+ logging.getLogger().addHandler(lh)
+ logging.getLogger().setLevel(log_level)
+
+def test_cipher_kat(cli_binary, data):
+ iv = data['Nonce']
+ key = data['Key']
+ ad = data['AD'] if 'AD' in data else ""
+ plaintext = data['In'].lower()
+ ciphertext = data['Out'].lower()
+ algorithm = data['Algorithm']
+ direction = data['Direction']
+
+ mode = SUPPORTED_ALGORITHMS.get(algorithm)
+ if mode is None:
+ raise Exception("Unknown algorithm: '" + algorithm + "'")
+
+ cmd = [
+ cli_binary,
+ "encryption",
+ "--mode=%s" % mode,
+ "--iv=%s" % iv,
+ "--ad=%s" % ad,
+ "--key=%s" % key]
+ if direction == "decrypt":
+ cmd += ['--decrypt']
+
+ if direction == "decrypt":
+ invalue = ciphertext
+ else:
+ invalue = plaintext
+
+ p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE)
+ out_raw = p.communicate(input=binascii.unhexlify(invalue))[0]
+ output = binascii.hexlify(out_raw).decode("UTF-8").lower()
+
+ expected = plaintext if direction == "decrypt" else ciphertext
+ if expected != output:
+ logging.error("For test %s got %s expected %s" % (data['testname'], output, expected))
+
+def get_testdata(document, max_tests):
+ out = []
+ for algorithm in document:
+ if algorithm in SUPPORTED_ALGORITHMS:
+ testcase_number = 0
+ for testcase in document[algorithm]:
+ testcase_number += 1
+ for direction in ['encrypt', 'decrypt']:
+ testname = "{} no {:0>3} ({})".format(
+ algorithm.lower(), testcase_number, direction)
+ testname = re.sub("[^a-z0-9-]", "_", testname)
+ testname = re.sub("_+", "_", testname)
+ testname = testname.strip("_")
+ test = {'testname': testname}
+ for key in testcase:
+ value = testcase[key]
+ test[key] = value
+ test['Algorithm'] = algorithm
+ test['Direction'] = direction
+
+ out.append(test)
+
+ if max_tests > 0 and testcase_number > max_tests:
+ break
+ return out
+
+def main(args=None):
+ if args is None:
+ args = sys.argv
+
+ parser = argparse.ArgumentParser(description="")
+ parser.add_argument('cli_binary', help='path to the botan cli binary')
+ parser.add_argument('--max-tests', type=int, default=50, metavar="M")
+ parser.add_argument('--threads', type=int, default=0, metavar="T")
+ parser.add_argument('--verbose', action='store_true', default=False)
+ parser.add_argument('--quiet', action='store_true', default=False)
+ args = parser.parse_args()
+
+ setup_logging(args)
+
+ cli_binary = args.cli_binary
+ max_tests = args.max_tests
+ threads = args.threads
+
+ if threads == 0:
+ threads = multiprocessing.cpu_count()
+
+ test_data_dir = os.path.join('src', 'tests', 'data')
+
+ mode_test_data = [os.path.join(test_data_dir, 'modes', 'cfb.vec'),
+ os.path.join(test_data_dir, 'aead', 'gcm.vec'),
+ os.path.join(test_data_dir, 'aead', 'ocb.vec'),
+ os.path.join(test_data_dir, 'modes', 'xts.vec'),
+ os.path.join(test_data_dir, 'aead', 'chacha20poly1305.vec')]
+
+ kats = []
+ for f in mode_test_data:
+ vecfile = VecDocument(f)
+ kats += get_testdata(vecfile.get_data(), max_tests)
+
+ start_time = time.time()
+
+ if threads > 1:
+ pool = ThreadPool(processes=threads)
+ results = []
+ for test in kats:
+ results.append(pool.apply_async(test_cipher_kat, (cli_binary, test)))
+
+ for result in results:
+ result.get()
+ else:
+ for test in kats:
+ test_cipher_kat(test)
+
+ end_time = time.time()
+
+ print("Ran %d tests with %d failures in %.02f seconds" % (
+ len(kats), TESTS_FAILED, end_time - start_time))
+
+ if TESTS_FAILED > 0:
+ return 1
+ return 0
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/comm/third_party/botan/src/scripts/test_fuzzers.py b/comm/third_party/botan/src/scripts/test_fuzzers.py
new file mode 100755
index 0000000000..01c202f236
--- /dev/null
+++ b/comm/third_party/botan/src/scripts/test_fuzzers.py
@@ -0,0 +1,187 @@
+#!/usr/bin/python
+
+# (C) 2017,2018 Jack Lloyd
+
+import sys
+import os
+import subprocess
+import optparse # pylint: disable=deprecated-module
+import stat
+import multiprocessing
+import time
+
+def run_fuzzer_gdb(args):
+ (fuzzer_bin, corpus_file) = args
+
+ gdb_proc = subprocess.Popen(['gdb', '--quiet', '--return-child-result', fuzzer_bin],
+ stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ close_fds=True)
+
+ gdb_commands = ('run < %s\nbt\nquit\n' % (corpus_file)).encode('ascii')
+
+ (stdout, stderr) = gdb_proc.communicate(gdb_commands)
+
+ if gdb_proc.returncode == 0:
+ return (0, '', '')
+
+ return (corpus_file, gdb_proc.returncode, stdout.decode('ascii'), stderr.decode('ascii'))
+
+def run_fuzzer(args):
+ (fuzzer_bin, corpus_file) = args
+ corpus_fd = open(corpus_file, 'r')
+ fuzzer_proc = subprocess.Popen([fuzzer_bin], stdin=corpus_fd,
+ stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True)
+ (stdout, stderr) = fuzzer_proc.communicate()
+ corpus_fd.close()
+ return (corpus_file, fuzzer_proc.returncode, stdout.decode('ascii'), stderr.decode('ascii'))
+
+def run_fuzzer_many_files(fuzzer_bin, corpus_files):
+ fuzzer_proc = subprocess.Popen([fuzzer_bin] + corpus_files, stdin=None,
+ stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True)
+ (stdout, stderr) = fuzzer_proc.communicate()
+ return (fuzzer_proc.returncode, stdout.decode('ascii'), stderr.decode('ascii'))
+
+def main(args=None):
+ #pylint: disable=too-many-branches
+ #pylint: disable=too-many-statements
+ #pylint: disable=too-many-locals
+
+ if args is None:
+ args = sys.argv
+
+ parser = optparse.OptionParser(
+ usage='Usage: %prog [options] corpus_dir fuzzers_dir',
+ )
+
+ parser.add_option('--gdb', action='store_true',
+ help='Run under GDB and capture backtraces')
+
+ parser.add_option('--one-at-a-time', action='store_true', default=False,
+ help='Test one corpus input at a time')
+
+ (options, args) = parser.parse_args(args)
+
+ if len(args) != 3:
+ parser.print_usage()
+ return 1
+
+ if options.gdb and not options.one_at_a_time:
+ print("Option --gdb requires --one-at-a-time")
+ return 1
+
+ corpus_dir = args[1]
+ fuzzer_dir = args[2]
+
+ if not os.access(corpus_dir, os.R_OK):
+ print("Error could not access corpus directory '%s'" % (corpus_dir))
+ return 1
+
+ if not os.access(fuzzer_dir, os.R_OK):
+ print("Error could not access fuzzers directory '%s'" % (fuzzer_dir))
+ return 1
+
+ fuzzers = set([])
+ for fuzzer in os.listdir(fuzzer_dir):
+ if fuzzer.endswith('.zip'):
+ continue
+ fuzzers.add(fuzzer)
+
+ corpii = set([])
+ for corpus in os.listdir(corpus_dir):
+ # Ignore regular files in toplevel dir
+ if not stat.S_ISDIR(os.stat(os.path.join(corpus_dir, corpus)).st_mode):
+ continue
+
+ if corpus == '.git':
+ continue
+
+ corpii.add(corpus)
+
+ fuzzers_without_corpus = fuzzers - corpii
+ corpus_without_fuzzers = corpii - fuzzers
+
+ for f in sorted(list(fuzzers_without_corpus)):
+ print("Warning: Fuzzer %s has no corpus" % (f))
+ for c in sorted(list(corpus_without_fuzzers)):
+ print("Warning: Corpus %s has no fuzzer" % (c))
+
+ fuzzers_with_corpus = fuzzers & corpii
+
+ crash_count = 0
+ stderr_count = 0
+ stdout_count = 0
+
+ if options.one_at_a_time:
+ pool = multiprocessing.Pool(multiprocessing.cpu_count() * 2)
+ chunk_size = 32 # arbitrary
+
+ run_fuzzer_func = run_fuzzer_gdb if options.gdb else run_fuzzer
+
+ 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)))]
+
+ # We have to do this hack because multiprocessing's Pool.map doesn't support
+ # passing any initial arguments, just the single iteratable
+ map_args = [(fuzzer_bin, f) for f in corpus_files]
+
+ start = time.time()
+
+ for result in pool.map(run_fuzzer_func, map_args, chunk_size):
+ (corpus_file, retcode, stdout, stderr) = result
+
+ if retcode != 0:
+ print("Fuzzer %s crashed with input %s returncode %d" % (fuzzer, corpus_file, retcode))
+ crash_count += 1
+
+ if stdout:
+ print("Fuzzer %s produced stdout on input %s:\n%s" % (fuzzer, corpus_file, stdout))
+ stdout_count += 1
+
+ if stderr:
+ print("Fuzzer %s produced stderr on input %s:\n%s" % (fuzzer, corpus_file, stderr))
+ stderr_count += 1
+
+ duration = time.time() - start
+ print("Tested fuzzer %s with %d test cases, %d crashes in %.02f seconds" % (
+ fuzzer, len(corpus_files), crash_count, duration))
+ crash_count = 0
+ sys.stdout.flush()
+ else:
+ 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)))]
+
+ start = time.time()
+
+ (retcode, stdout, stderr) = run_fuzzer_many_files(fuzzer_bin, corpus_files)
+
+ if retcode != 0:
+ print("Fuzzer %s crashed returncode %d" % (fuzzer, retcode))
+ crash_count += 1
+
+ if stdout:
+ print("Fuzzer %s produced stdout:\n%s" % (fuzzer, stdout))
+ stdout_count += 1
+
+ if stderr:
+ print("Fuzzer %s produced stderr:\n%s" % (fuzzer, stderr))
+ stderr_count += 1
+
+ duration = time.time() - start
+
+ print("Tested fuzzer %s with %d test cases, %d crashes in %.02f seconds" % (
+ fuzzer, len(corpus_files), crash_count, duration))
+ crash_count = 0
+
+ if crash_count > 0 or stderr_count > 0 or stdout_count > 0:
+ print("Ran fuzzer tests, %d crashes %d stdout %d stderr" % (crash_count, stdout_count, stderr_count))
+ return 2
+ return 0
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/comm/third_party/botan/src/scripts/test_python.py b/comm/third_party/botan/src/scripts/test_python.py
new file mode 100644
index 0000000000..2202c0e4bc
--- /dev/null
+++ b/comm/third_party/botan/src/scripts/test_python.py
@@ -0,0 +1,695 @@
+#!/usr/bin/env python
+
+"""
+(C) 2015,2017,2018,2019 Jack Lloyd
+
+Botan is released under the Simplified BSD License (see license.txt)
+"""
+
+import unittest
+import binascii
+import botan2
+
+def hex_encode(buf):
+ return binascii.hexlify(buf).decode('ascii')
+
+def hex_decode(buf):
+ return binascii.unhexlify(buf.encode('ascii'))
+
+class BotanPythonTests(unittest.TestCase):
+ # pylint: disable=too-many-public-methods,too-many-locals
+
+ def test_version(self):
+ version_str = botan2.version_string()
+ self.assertTrue(version_str.startswith('Botan '))
+
+ self.assertEqual(botan2.version_major(), 2)
+ self.assertGreaterEqual(botan2.version_minor(), 8)
+
+ self.assertGreaterEqual(botan2.ffi_api_version(), 20180713)
+
+ def test_compare(self):
+
+ x = "1234"
+ y = "1234"
+ z = "1233"
+ self.assertTrue(botan2.const_time_compare(x, y))
+ self.assertFalse(botan2.const_time_compare(x, z))
+ self.assertFalse(botan2.const_time_compare(x, x + z))
+
+ def test_block_cipher(self):
+ aes = botan2.BlockCipher("AES-128")
+ self.assertEqual(aes.algo_name(), "AES-128")
+ self.assertEqual(aes.block_size(), 16)
+ self.assertEqual(aes.minimum_keylength(), 16)
+ self.assertEqual(aes.maximum_keylength(), 16)
+
+ aes.set_key(hex_decode("000102030405060708090a0b0c0d0e0f"))
+ ct = aes.encrypt(hex_decode("00112233445566778899aabbccddeeff"))
+
+ self.assertEqual(hex_encode(ct), "69c4e0d86a7b0430d8cdb78070b4c55a")
+
+ pt = aes.decrypt(ct)
+
+ self.assertEqual(hex_encode(pt), "00112233445566778899aabbccddeeff")
+
+ def test_kdf(self):
+
+ secret = hex_decode('6FD4C3C0F38E5C7A6F83E99CD9BD')
+ salt = hex_decode('DBB986')
+ label = hex_decode('')
+ expected = hex_decode('02AEB40A3D4B66FBA540F9D4B20006F2046E0F3A029DEAB201FC692B79EB27CEF7E16069046A')
+
+ produced = botan2.kdf('KDF2(SHA-1)', secret, 38, salt, label)
+
+ self.assertEqual(hex_encode(produced), hex_encode(expected))
+
+ def test_pbkdf(self):
+
+ (salt, iterations, pbkdf) = botan2.pbkdf('PBKDF2(SHA-1)', '', 32, 10000, hex_decode('0001020304050607'))
+
+ self.assertEqual(iterations, 10000)
+ self.assertEqual(hex_encode(pbkdf),
+ '59b2b1143b4cb1059ec58d9722fb1c72471e0d85c6f7543ba5228526375b0127')
+
+ (salt, iterations, pbkdf) = botan2.pbkdf_timed('PBKDF2(SHA-256)', 'xyz', 32, 200)
+
+ cmp_pbkdf = botan2.pbkdf('PBKDF2(SHA-256)', 'xyz', 32, iterations, salt)[2]
+
+ self.assertEqual(pbkdf, cmp_pbkdf)
+
+ def test_scrypt(self):
+ scrypt = botan2.scrypt(10, '', '', 16, 1, 1)
+ self.assertEqual(hex_encode(scrypt), "77d6576238657b203b19")
+
+ scrypt = botan2.scrypt(32, 'password', 'NaCl', 1024, 8, 16)
+ self.assertEqual(hex_encode(scrypt), "fdbabe1c9d3472007856e7190d01e9fe7c6ad7cbc8237830e77376634b373162")
+
+ def test_bcrypt(self):
+ r = botan2.RandomNumberGenerator()
+ phash = botan2.bcrypt('testing', r)
+ self.assertTrue(isinstance(phash, str))
+ self.assertTrue(phash.startswith("$2a$"))
+
+ self.assertTrue(botan2.check_bcrypt('testing', phash))
+ self.assertFalse(botan2.check_bcrypt('live fire', phash))
+
+ self.assertTrue(botan2.check_bcrypt('test', '$2a$04$wjen1fAA.UW6UxthpKK.huyOoxvCR7ATRCVC4CBIEGVDOCtr8Oj1C'))
+
+ def test_mac(self):
+
+ hmac = botan2.MsgAuthCode('HMAC(SHA-256)')
+ self.assertEqual(hmac.algo_name(), 'HMAC(SHA-256)')
+ self.assertEqual(hmac.minimum_keylength(), 0)
+ self.assertEqual(hmac.maximum_keylength(), 4096)
+ hmac.set_key(hex_decode('0102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F20'))
+ hmac.update(hex_decode('616263'))
+
+ expected = hex_decode('A21B1F5D4CF4F73A4DD939750F7A066A7F98CC131CB16A6692759021CFAB8181')
+ produced = hmac.final()
+
+ self.assertEqual(hex_encode(expected), hex_encode(produced))
+
+ def test_rng(self):
+ user_rng = botan2.RandomNumberGenerator("user")
+
+ output1 = user_rng.get(32)
+ output2 = user_rng.get(32)
+
+ self.assertEqual(len(output1), 32)
+ self.assertEqual(len(output2), 32)
+ self.assertNotEqual(output1, output2)
+
+ output3 = user_rng.get(1021)
+ self.assertEqual(len(output3), 1021)
+
+ system_rng = botan2.RandomNumberGenerator('system')
+
+ user_rng.reseed_from_rng(system_rng, 256)
+
+ user_rng.add_entropy('seed material...')
+
+ def test_hash(self):
+
+ try:
+ _h = botan2.HashFunction('NoSuchHash')
+ except botan2.BotanException as e:
+ self.assertEqual(str(e), "botan_hash_init failed: -40 (Not implemented)")
+
+ sha256 = botan2.HashFunction('SHA-256')
+ self.assertEqual(sha256.algo_name(), 'SHA-256')
+ self.assertEqual(sha256.output_length(), 32)
+ self.assertEqual(sha256.block_size(), 64)
+ sha256.update('ignore this please')
+ sha256.clear()
+ sha256.update('a')
+ hash1 = sha256.final()
+
+ self.assertEqual(hex_encode(hash1), "ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb")
+
+ sha256.update(hex_decode('61'))
+ sha256_2 = sha256.copy_state()
+ sha256.update(hex_decode('6263'))
+ h2 = sha256.final()
+ self.assertEqual(hex_encode(h2), "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad")
+
+ self.assertEqual(hex_encode(sha256_2.final()), hex_encode(hash1))
+
+ def test_cipher(self):
+ for mode in ['AES-128/CTR-BE', 'Serpent/GCM', 'ChaCha20Poly1305']:
+ enc = botan2.SymmetricCipher(mode, encrypt=True)
+
+ if mode == 'AES-128/CTR-BE':
+ self.assertEqual(enc.algo_name(), 'CTR-BE(AES-128)')
+ elif mode == 'Serpent/GCM':
+ self.assertEqual(enc.algo_name(), 'Serpent/GCM(16)')
+ else:
+ self.assertEqual(enc.algo_name(), mode)
+
+ (kmin, kmax) = enc.key_length()
+
+ self.assertLessEqual(kmin, kmax)
+
+ rng = botan2.RandomNumberGenerator()
+ iv = rng.get(enc.default_nonce_length())
+ key = rng.get(kmax)
+ pt = rng.get(21)
+
+ enc.set_key(key)
+ enc.start(iv)
+
+ update_result = enc.update('')
+ assert not update_result
+
+ ct = enc.finish(pt)
+
+ dec = botan2.SymmetricCipher(mode, encrypt=False)
+ dec.set_key(key)
+ dec.start(iv)
+ decrypted = dec.finish(ct)
+
+ self.assertEqual(decrypted, pt)
+
+
+ def test_mceliece(self):
+ rng = botan2.RandomNumberGenerator()
+ mce_priv = botan2.PrivateKey.create('McEliece', '2960,57', rng)
+ mce_pub = mce_priv.get_public_key()
+ self.assertEqual(mce_pub.estimated_strength(), 128)
+
+ mce_plaintext = rng.get(16)
+ mce_ad = rng.get(48)
+ mce_ciphertext = botan2.mceies_encrypt(mce_pub, rng, 'ChaCha20Poly1305', mce_plaintext, mce_ad)
+
+ mce_decrypt = botan2.mceies_decrypt(mce_priv, 'ChaCha20Poly1305', mce_ciphertext, mce_ad)
+
+ self.assertEqual(mce_plaintext, mce_decrypt)
+
+ def test_rsa_load_store(self):
+
+ rsa_priv_pem = """-----BEGIN PRIVATE KEY-----
+MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBALWtiBjcofJW/4+r
+CIjQZn2V3yCYsNIBpMdVkNPr36FZ3ZHGSv2ggmCe+IWy0fTcBVyP+fo3HC8zmOC2
+EsYDFRExyB2zIsjRXlPrVrTfcyXwUEaInLJQId5CguFrmyj1y7K43ezg+OTop39n
+TyaukrciCSCh++Q/UQOanHnR8ctrAgMBAAECgYBPfKySgBmk31ZyA7k4rsFgye01
+JEkcoNZ41iGG7ujJffl4maLew9a3MmZ2jI3azVbVMDMFPA5rQm5tRowBMYEJ5oBc
+LP4AP41Lujfa+vua6l3t94bAV+CufZiY0297FcPbGqNu+xSQ2Bol2uHh9mrcgQUs
+fevA50KOLR9hv4zH6QJBAPCOKiExONtVhJn8qVPCBlJ8Vjjnt9Uno5EzMBAKMbZi
+OySkGwo9/9LUWO03r7tjrGSy5jJk+iOrcLeDl6zETfkCQQDBV6PpD/3ccQ1IfWcw
+jG8yik0bIuXgrD0uW4g8Cvj+05wrv7RYPHuFtj3Rtb94YjtgYn7QvjH7y88XmTC4
+2k2DAkEA4E9Ae7kBUoz42/odDswyxwHICMIRyoJu5Ht9yscmufH5Ql6AFFnhzf9S
+eMjfZfY4j6G+Q6mjElXQAl+DtIdMSQJBAJzdMkuBggI8Zv6NYA9voThsJSsDIWcr
+12epM9sjO+nkXizQmM2OJNnThkyDHRna+Tm2MBXEemFEdn06+ODBnWkCQQChAbG4
+255RiCuYdrfiTPF/WLtvRyGd1LRwHcYIW4mJFPzxYAMTwQKbppLAnxw73vyef/zC
+2BgXEW02tjRBtgZ+
+-----END PRIVATE KEY-----
+"""
+
+ rsa_pub_pem = """-----BEGIN PUBLIC KEY-----
+MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC1rYgY3KHyVv+PqwiI0GZ9ld8g
+mLDSAaTHVZDT69+hWd2Rxkr9oIJgnviFstH03AVcj/n6NxwvM5jgthLGAxURMcgd
+syLI0V5T61a033Ml8FBGiJyyUCHeQoLha5so9cuyuN3s4Pjk6Kd/Z08mrpK3Igkg
+ofvkP1EDmpx50fHLawIDAQAB
+-----END PUBLIC KEY-----
+"""
+
+ rsapriv = botan2.PrivateKey.load(rsa_priv_pem)
+
+ self.assertEqual(rsapriv.to_pem(), rsa_priv_pem)
+
+ rsapub = rsapriv.get_public_key()
+ self.assertEqual(rsapub.to_pem(), rsa_pub_pem)
+
+ rsapub = botan2.PublicKey.load(rsa_pub_pem)
+ self.assertEqual(rsapub.to_pem(), rsa_pub_pem)
+
+ n = 0xB5AD8818DCA1F256FF8FAB0888D0667D95DF2098B0D201A4C75590D3EBDFA159DD91C64AFDA082609EF885B2D1F4DC055C8FF9FA371C2F3398E0B612C603151131C81DB322C8D15E53EB56B4DF7325F05046889CB25021DE4282E16B9B28F5CBB2B8DDECE0F8E4E8A77F674F26AE92B7220920A1FBE43F51039A9C79D1F1CB6B # pylint: disable=line-too-long
+ e = 0x10001
+
+ rsapub2 = botan2.PublicKey.load_rsa(n, e)
+ self.assertEqual(rsapub2.to_pem(), rsa_pub_pem)
+
+ self.assertEqual(rsapub2.get_field("n"), n)
+ self.assertEqual(rsapub2.get_field("e"), e)
+
+ def test_key_crypto(self):
+ rng = botan2.RandomNumberGenerator()
+ priv = botan2.PrivateKey.create('RSA', '1024', rng)
+ passphrase = "super secret tell noone"
+
+ for is_pem in [True, False]:
+ ref_val = priv.export(is_pem)
+
+ enc1 = priv.export_encrypted(passphrase, rng, True, msec=10)
+ dec1 = botan2.PrivateKey.load(enc1, passphrase)
+ self.assertEqual(dec1.export(is_pem), ref_val)
+
+ pem2 = priv.export_encrypted(passphrase, rng, True, msec=10, cipher="AES-128/SIV")
+ dec2 = botan2.PrivateKey.load(pem2, passphrase)
+ self.assertEqual(dec2.export(is_pem), ref_val)
+
+ pem3 = priv.export_encrypted(passphrase, rng, True, msec=10, cipher="AES-128/GCM", pbkdf="Scrypt")
+ dec3 = botan2.PrivateKey.load(pem3, passphrase)
+ self.assertEqual(dec3.export(is_pem), ref_val)
+
+ def test_check_key(self):
+ # valid (if rather small) RSA key
+ n = 273279220906618527352827457840955116141
+ e = 0x10001
+
+ rng = botan2.RandomNumberGenerator()
+
+ rsapub = botan2.PublicKey.load_rsa(n, e)
+ self.assertTrue(rsapub.check_key(rng))
+
+ # invalid
+ try:
+ rsapub = botan2.PublicKey.load_rsa(n - 1, e)
+ except botan2.BotanException as e:
+ self.assertEqual(str(e), "botan_pubkey_load_rsa failed: -1 (Invalid input)")
+
+ def test_rsa(self):
+ # pylint: disable=too-many-locals
+ rng = botan2.RandomNumberGenerator()
+ rsapriv = botan2.PrivateKey.create('RSA', '1024', rng)
+ self.assertEqual(rsapriv.algo_name(), 'RSA')
+
+ priv_pem = rsapriv.to_pem()
+ priv_der = rsapriv.to_der()
+
+ self.assertEqual(priv_pem[0:28], "-----BEGIN PRIVATE KEY-----\n")
+ self.assertGreater(len(priv_pem), len(priv_der))
+
+ rsapub = rsapriv.get_public_key()
+ self.assertEqual(rsapub.algo_name(), 'RSA')
+ self.assertEqual(rsapub.estimated_strength(), 80)
+
+ pub_pem = rsapub.to_pem()
+ pub_der = rsapub.to_der()
+
+ self.assertEqual(pub_pem[0:27], "-----BEGIN PUBLIC KEY-----\n")
+ self.assertGreater(len(pub_pem), len(pub_der))
+
+ enc = botan2.PKEncrypt(rsapub, "OAEP(SHA-256)")
+ dec = botan2.PKDecrypt(rsapriv, "OAEP(SHA-256)")
+
+ symkey = rng.get(32)
+ ctext = enc.encrypt(symkey, rng)
+
+ ptext = dec.decrypt(ctext)
+
+ self.assertEqual(ptext, symkey)
+
+ signer = botan2.PKSign(rsapriv, 'EMSA4(SHA-384)')
+
+ signer.update('messa')
+ signer.update('ge')
+ sig = signer.finish(botan2.RandomNumberGenerator())
+
+ verify = botan2.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))
+
+ def test_ecdsa(self):
+ rng = botan2.RandomNumberGenerator()
+
+ hash_fn = 'EMSA1(SHA-256)'
+ group = 'secp256r1'
+ msg = 'test message'
+
+ priv = botan2.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'))
+
+ signer = botan2.PKSign(priv, hash_fn, True)
+ signer.update(msg)
+ signature = signer.finish(rng)
+
+ verifier = botan2.PKVerify(pub, hash_fn)
+ verifier.update(msg)
+ #fails because DER/not-DER mismatch
+ self.assertFalse(verifier.check_signature(signature))
+
+ verifier = botan2.PKVerify(pub, hash_fn, True)
+ verifier.update(msg)
+ self.assertTrue(verifier.check_signature(signature))
+
+ pub_x = pub.get_field('public_x')
+ pub_y = priv.get_field('public_y')
+ pub2 = botan2.PublicKey.load_ecdsa(group, pub_x, pub_y)
+ verifier = botan2.PKVerify(pub2, hash_fn, True)
+ verifier.update(msg)
+ self.assertTrue(verifier.check_signature(signature))
+
+ priv2 = botan2.PrivateKey.load_ecdsa(group, priv.get_field('x'))
+ signer = botan2.PKSign(priv2, hash_fn, True)
+ # sign empty message
+ signature = signer.finish(rng)
+
+ # verify empty message
+ self.assertTrue(verifier.check_signature(signature))
+
+ def test_sm2(self):
+ rng = botan2.RandomNumberGenerator()
+
+ hash_fn = 'EMSA1(SM3)'
+ group = 'sm2p256v1'
+ msg = 'test message'
+
+ priv = botan2.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 = botan2.PKSign(priv, hash_fn)
+ signer.update(msg)
+ signature = signer.finish(rng)
+
+ verifier = botan2.PKVerify(pub, hash_fn)
+ verifier.update(msg)
+ self.assertTrue(verifier.check_signature(signature))
+
+ pub_x = pub.get_field('public_x')
+ pub_y = priv.get_field('public_y')
+ pub2 = botan2.PublicKey.load_sm2(group, pub_x, pub_y)
+ verifier = botan2.PKVerify(pub2, hash_fn)
+ verifier.update(msg)
+ self.assertTrue(verifier.check_signature(signature))
+
+ priv2 = botan2.PrivateKey.load_sm2(group, priv.get_field('x'))
+ signer = botan2.PKSign(priv2, hash_fn)
+ # sign empty message
+ signature = signer.finish(rng)
+
+ # verify empty message
+ self.assertTrue(verifier.check_signature(signature))
+
+ def test_ecdh(self):
+ # pylint: disable=too-many-locals
+ a_rng = botan2.RandomNumberGenerator('user')
+ b_rng = botan2.RandomNumberGenerator('user')
+
+ kdf = 'KDF2(SHA-384)'
+
+ for grp in ['secp256r1', 'secp384r1', 'brainpool256r1']:
+ a_priv = botan2.PrivateKey.create('ECDH', grp, a_rng)
+ b_priv = botan2.PrivateKey.create('ECDH', grp, b_rng)
+
+ a_op = botan2.PKKeyAgreement(a_priv, kdf)
+ b_op = botan2.PKKeyAgreement(b_priv, kdf)
+
+ a_pub = a_op.public_value()
+ b_pub = b_op.public_value()
+
+ salt = a_rng.get(8) + b_rng.get(8)
+
+ a_key = a_op.agree(b_pub, 32, salt)
+ b_key = b_op.agree(a_pub, 32, salt)
+
+ self.assertEqual(a_key, b_key)
+
+ a_pem = a_priv.to_pem()
+
+ a_priv_x = a_priv.get_field('x')
+
+ new_a = botan2.PrivateKey.load_ecdh(grp, a_priv_x)
+
+ self.assertEqual(a_pem, new_a.to_pem())
+
+ def test_certs(self):
+ # pylint: disable=too-many-statements
+ cert = botan2.X509Cert(filename="src/tests/data/x509/ecc/CSCA.CSCA.csca-germany.1.crt")
+ pubkey = cert.subject_public_key()
+
+ self.assertEqual(pubkey.algo_name(), 'ECDSA')
+ self.assertEqual(pubkey.estimated_strength(), 112)
+
+ self.assertEqual(cert.fingerprint("SHA-1"),
+ "32:42:1C:C3:EC:54:D7:E9:43:EC:51:F0:19:23:BD:85:1D:F2:1B:B9")
+
+ self.assertEqual(hex_encode(cert.serial_number()), "01")
+ self.assertEqual(hex_encode(cert.authority_key_id()),
+ "0096452de588f966c4ccdf161dd1f3f5341b71e7")
+
+ self.assertEqual(cert.subject_dn('Name', 0), 'csca-germany')
+ self.assertEqual(cert.subject_dn('Email', 0), 'csca-germany@bsi.bund.de')
+ self.assertEqual(cert.subject_dn('Organization', 0), 'bund')
+ self.assertEqual(cert.subject_dn('Organizational Unit', 0), 'bsi')
+ self.assertEqual(cert.subject_dn('Country', 0), 'DE')
+
+ self.assertTrue(cert.to_string().startswith("Version: 3"))
+
+ self.assertEqual(cert.issuer_dn('Name', 0), 'csca-germany')
+ self.assertEqual(cert.issuer_dn('Organization', 0), 'bund')
+ self.assertEqual(cert.issuer_dn('Organizational Unit', 0), 'bsi')
+ self.assertEqual(cert.issuer_dn('Country', 0), 'DE')
+
+ self.assertTrue(cert.hostname_match('csca-germany'))
+ self.assertFalse(cert.hostname_match('csca-slovakia'))
+
+ self.assertEqual(cert.not_before(), 1184858838)
+ self.assertEqual(cert.not_after(), 1831907880)
+
+ self.assertTrue(cert.allowed_usage(["CRL_SIGN", "KEY_CERT_SIGN"]))
+ self.assertTrue(cert.allowed_usage(["KEY_CERT_SIGN"]))
+ self.assertFalse(cert.allowed_usage(["DIGITAL_SIGNATURE"]))
+ self.assertFalse(cert.allowed_usage(["DIGITAL_SIGNATURE", "CRL_SIGN"]))
+
+ root = botan2.X509Cert("src/tests/data/x509/nist/root.crt")
+
+ int09 = botan2.X509Cert("src/tests/data/x509/nist/test09/int.crt")
+ end09 = botan2.X509Cert("src/tests/data/x509/nist/test09/end.crt")
+ self.assertEqual(end09.verify([int09], [root]), 2001)
+
+ end04 = botan2.X509Cert("src/tests/data/x509/nist/test04/end.crt")
+ int04_1 = botan2.X509Cert("src/tests/data/x509/nist/test04/int1.crt")
+ int04_2 = botan2.X509Cert("src/tests/data/x509/nist/test04/int2.crt")
+ self.assertEqual(end04.verify([int04_1, int04_2], [], "src/tests/data/x509/nist/", required_strength=80), 0)
+ self.assertEqual(end04.verify([int04_1, int04_2], [], required_strength=80), 3000)
+ self.assertEqual(end04.verify([int04_1, int04_2], [root], required_strength=80, hostname="User1-CP.02.01"), 0)
+ self.assertEqual(end04.verify([int04_1, int04_2], [root], required_strength=80, hostname="invalid"), 4008)
+ self.assertEqual(end04.verify([int04_1, int04_2], [root], required_strength=80, reference_time=1), 2000)
+
+ self.assertEqual(botan2.X509Cert.validation_status(0), 'Verified')
+ self.assertEqual(botan2.X509Cert.validation_status(3000), 'Certificate issuer not found')
+ self.assertEqual(botan2.X509Cert.validation_status(4008), 'Certificate does not match provided name')
+
+ rootcrl = botan2.X509CRL("src/tests/data/x509/nist/root.crl")
+
+ end01 = botan2.X509Cert("src/tests/data/x509/nist/test01/end.crt")
+ self.assertEqual(end01.verify([], [root], required_strength=80, crls=[rootcrl]), 0)
+
+ int20 = botan2.X509Cert("src/tests/data/x509/nist/test20/int.crt")
+ end20 = botan2.X509Cert("src/tests/data/x509/nist/test20/end.crt")
+ int20crl = botan2.X509CRL("src/tests/data/x509/nist/test20/int.crl")
+
+ self.assertEqual(end20.verify([int20], [root], required_strength=80, crls=[int20crl, rootcrl]), 5000)
+ self.assertEqual(botan2.X509Cert.validation_status(5000), 'Certificate is revoked')
+
+ int21 = botan2.X509Cert("src/tests/data/x509/nist/test21/int.crt")
+ end21 = botan2.X509Cert("src/tests/data/x509/nist/test21/end.crt")
+ int21crl = botan2.X509CRL("src/tests/data/x509/nist/test21/int.crl")
+ self.assertEqual(end21.verify([int21], [root], required_strength=80, crls=[int21crl, rootcrl]), 5000)
+
+ self.assertTrue(int20.is_revoked(rootcrl))
+ self.assertFalse(int04_1.is_revoked(rootcrl))
+ self.assertTrue(end21.is_revoked(int21crl))
+
+
+ def test_mpi(self):
+ # pylint: disable=too-many-statements,too-many-locals
+ z = botan2.MPI()
+ self.assertEqual(z.bit_count(), 0)
+ five = botan2.MPI('5')
+ self.assertEqual(five.bit_count(), 3)
+ big = botan2.MPI('0x85839682368923476892367235')
+ self.assertEqual(big.bit_count(), 104)
+ small = botan2.MPI(0xDEADBEEF)
+ radix = botan2.MPI("DEADBEEF", 16)
+
+ self.assertEqual(hex_encode(small.to_bytes()), "deadbeef")
+ self.assertEqual(hex_encode(big.to_bytes()), "85839682368923476892367235")
+
+ self.assertEqual(int(small), 0xDEADBEEF)
+ self.assertEqual(int(radix), int(small))
+
+ self.assertEqual(int(small >> 16), 0xDEAD)
+
+ small >>= 15
+
+ self.assertEqual(int(small), 0x1BD5B)
+
+ small <<= 15
+
+ self.assertEqual(int(small), 0xDEAD8000)
+
+ ten = botan2.MPI(10)
+
+ self.assertEqual(ten, five + five)
+ self.assertNotEqual(ten, five)
+ self.assertLess(five, ten)
+ self.assertLessEqual(five, ten)
+
+ x = botan2.MPI(five)
+
+ self.assertEqual(x, five)
+
+ x += botan2.MPI(1)
+ self.assertNotEqual(x, five)
+
+ self.assertEqual(int(x * five), 30)
+
+ x *= five
+ x *= five
+ self.assertEqual(int(x), 150)
+
+ self.assertTrue(not x.is_negative())
+
+ x.flip_sign()
+ self.assertTrue(x.is_negative())
+ self.assertEqual(int(x), -150)
+
+ x.flip_sign()
+
+ x.set_bit(0)
+ self.assertTrue(int(x), 151)
+ self.assertTrue(x.get_bit(0))
+ self.assertTrue(x.get_bit(4))
+ self.assertFalse(x.get_bit(6))
+
+ x.clear_bit(4)
+ self.assertEqual(int(x), 135)
+
+ rng = botan2.RandomNumberGenerator()
+ self.assertFalse(x.is_prime(rng))
+
+ two = botan2.MPI(2)
+
+ x += two
+ self.assertTrue(x.is_prime(rng))
+
+ mod = x + two
+
+ inv = x.inverse_mod(mod)
+ self.assertEqual(int(inv), 69)
+ self.assertEqual(int((inv * x) % mod), 1)
+
+ p = inv.pow_mod(botan2.MPI(46), mod)
+ self.assertEqual(int(p), 42)
+
+ one = botan2.MPI(1)
+ twelve = botan2.MPI("C", 16)
+ eight = botan2.MPI(8)
+
+ mul = twelve.mod_mul(eight, inv)
+ self.assertEqual(int(mul), 27)
+
+ gcd = one.gcd(one)
+ self.assertEqual(one, gcd)
+ gcd = one.gcd(twelve)
+ self.assertEqual(one, gcd)
+ gcd = twelve.gcd(eight)
+ self.assertEqual(4, int(gcd))
+
+ def test_mpi_random(self):
+ rng = botan2.RandomNumberGenerator()
+
+ u = botan2.MPI.random(rng, 512)
+ self.assertEqual(u.bit_count(), 512)
+
+ l = u >> 32
+ self.assertEqual(l.bit_count(), 512-32)
+
+ for _i in range(10):
+ x = botan2.MPI.random_range(rng, l, u)
+ self.assertLess(x, u)
+ self.assertGreater(x, l)
+
+ def test_fpe(self):
+
+ modulus = botan2.MPI('1000000000')
+ key = b'001122334455'
+
+ fpe = botan2.FormatPreservingEncryptionFE1(modulus, key)
+
+ value = botan2.MPI('392910392')
+ tweak = 'tweak value'
+
+ ctext = fpe.encrypt(value, tweak)
+
+ ptext = fpe.decrypt(ctext, tweak)
+
+ self.assertEqual(value, ptext)
+
+ def test_keywrap(self):
+ key = hex_decode('00112233445566778899aabbccddeeff')
+ kek = hex_decode('000102030405060708090a0b0c0d0e0f')
+
+ wrapped = botan2.nist_key_wrap(kek, key)
+ self.assertEqual(hex_encode(wrapped), '1fa68b0a8112b447aef34bd8fb5a7b829d3e862371d2cfe5')
+
+ self.assertEqual(len(wrapped), 16+8)
+ unwrapped = botan2.nist_key_unwrap(kek, wrapped)
+ self.assertEqual(hex_encode(unwrapped), '00112233445566778899aabbccddeeff')
+
+ def test_hotp(self):
+
+ hotp = botan2.HOTP(b'12345678901234567890')
+
+ self.assertEqual(hotp.generate(0), 755224)
+ self.assertEqual(hotp.generate(1), 287082)
+ self.assertEqual(hotp.generate(9), 520489)
+
+ self.assertEqual(hotp.check(520489, 8), (False, 8))
+ self.assertEqual(hotp.check(520489, 8, 1), (True, 10))
+ self.assertEqual(hotp.check(520489, 7, 2), (True, 10))
+ self.assertEqual(hotp.check(520489, 0, 9), (True, 10))
+
+ def test_totp(self):
+
+ totp = botan2.TOTP(b'12345678901234567890', digest="SHA-1", digits=8)
+
+ self.assertEqual(totp.generate(59), 94287082)
+ self.assertEqual(totp.generate(1111111109), 7081804)
+ self.assertEqual(totp.generate(1111111111), 14050471)
+ self.assertEqual(totp.generate(1234567890), 89005924)
+ self.assertEqual(totp.generate(1234567890), 89005924)
+ self.assertEqual(totp.generate(2000000000), 69279037)
+
+ self.assertTrue(totp.check(7081804, 1111111109))
+ self.assertTrue(totp.check(7081804, 1111111109 - 29))
+ self.assertFalse(totp.check(7081804, 1111111109 + 1))
+ self.assertTrue(totp.check(7081804, 1111111109 + 30, 1))
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/comm/third_party/botan/src/scripts/tls_scanner/boa.txt b/comm/third_party/botan/src/scripts/tls_scanner/boa.txt
new file mode 100644
index 0000000000..436b785728
--- /dev/null
+++ b/comm/third_party/botan/src/scripts/tls_scanner/boa.txt
@@ -0,0 +1 @@
+bankofamerica.com
diff --git a/comm/third_party/botan/src/scripts/tls_scanner/policy.txt b/comm/third_party/botan/src/scripts/tls_scanner/policy.txt
new file mode 100644
index 0000000000..ddd7a7c57d
--- /dev/null
+++ b/comm/third_party/botan/src/scripts/tls_scanner/policy.txt
@@ -0,0 +1,19 @@
+allow_tls10=true
+allow_tls11=true
+allow_tls12=true
+allow_dtls10=false
+allow_dtls12=false
+
+# Camellia first just to see if there is anyone out there who will negotiate it with us
+ciphers=Camellia-128 Camellia-256 Camellia-128/GCM Camellia-256/GCM ChaCha20Poly1305 AES-256/GCM AES-128/GCM AES-256 AES-128
+signature_hashes=SHA-384 SHA-256 SHA-1
+macs=AEAD SHA-384 SHA-256 SHA-1
+key_exchange_methods=CECPQ1 ECDH DH RSA
+signature_methods=ECDSA RSA DSA IMPLICIT
+ecc_curves=x25519 secp256r1 secp384r1
+minimum_dh_group_size=1024
+minimum_ecdh_group_size=255
+minimum_rsa_bits=2048
+
+allow_insecure_renegotiation=false
+allow_server_initiated_renegotiation=false
diff --git a/comm/third_party/botan/src/scripts/tls_scanner/readme.txt b/comm/third_party/botan/src/scripts/tls_scanner/readme.txt
new file mode 100644
index 0000000000..a4754b02df
--- /dev/null
+++ b/comm/third_party/botan/src/scripts/tls_scanner/readme.txt
@@ -0,0 +1,5 @@
+
+Simple script to scan hosts to check basic TLS client compatability.
+
+URL list chosen mostly from large tech/software vendors, feel free to
+send suggestions.
diff --git a/comm/third_party/botan/src/scripts/tls_scanner/tls_scanner.py b/comm/third_party/botan/src/scripts/tls_scanner/tls_scanner.py
new file mode 100755
index 0000000000..8fdf046ca7
--- /dev/null
+++ b/comm/third_party/botan/src/scripts/tls_scanner/tls_scanner.py
@@ -0,0 +1,60 @@
+#!/usr/bin/python2
+
+import sys
+import time
+import subprocess
+import re
+
+def format_report(client_output):
+ version_re = re.compile('TLS (v1\.[0-2]) using ([A-Z0-9_]+)')
+
+ version_match = version_re.search(client_output)
+
+ #print client_output
+
+ if version_match:
+ return "Established %s %s" % (version_match.group(1), version_match.group(2))
+ else:
+ return client_output
+
+def scanner(args = None):
+ if args is None:
+ args = sys.argv
+
+ if len(args) != 2:
+ print "Error: Usage tls_scanner.py host_file"
+ return 2
+
+ scanners = {}
+
+ for url in [s.strip() for s in open(args[1]).readlines()]:
+ scanners[url] = subprocess.Popen(['../../../botan', 'tls_client', '--policy=policy.txt', url],
+ stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE)
+
+ for url in scanners.keys():
+ scanners[url].stdin.close()
+
+ report = {}
+ timeout = 10
+
+ for url in scanners.keys():
+ print "waiting for", url
+
+ for i in range(timeout):
+ scanners[url].poll()
+ if scanners[url].returncode != None:
+ break
+ #print "Waiting %d more seconds for %s" % (timeout-i, url)
+ time.sleep(1)
+
+ if scanners[url].returncode != None:
+ output = scanners[url].stdout.read() + scanners[url].stderr.read()
+ report[url] = format_report(output)
+
+ for url in report.keys():
+ print url, ":", report[url]
+
+ return 0
+
+if __name__ == '__main__':
+ sys.exit(scanner())
diff --git a/comm/third_party/botan/src/scripts/tls_scanner/urls.txt b/comm/third_party/botan/src/scripts/tls_scanner/urls.txt
new file mode 100644
index 0000000000..3be7276b32
--- /dev/null
+++ b/comm/third_party/botan/src/scripts/tls_scanner/urls.txt
@@ -0,0 +1,58 @@
+adobe.com
+adp.com
+airbnb.com
+akamai.com
+amazon.com
+apache.org
+apple.com
+bbc.co.uk
+bing.com
+ca.com
+cisco.com
+citrix.com
+cloudflare.com
+craigslist.org
+dell.com
+ebay.com
+facebook.com
+github.com
+gmail.com
+google.com
+hp.com
+huawei.com
+ibm.com
+ietf.org
+intuit.com
+linkedin.com
+medium.com
+microsoft.com
+mikestoolbox.org
+netflix.com
+openssl.org
+oracle.com
+chase.com
+bankofamerica.com
+citibank.com
+wellsfargo.com
+ebay.com
+paypal.com
+randombit.net
+reddit.com
+redhat.com
+salesforce.com
+sas.com
+siemens.com
+sony.com
+stripe.com
+symantec.com
+tls.mbed.org
+twitter.com
+uber.com
+vmware.com
+whatsapp.com
+wikipedia.org
+www.iso.org
+www.lg.com
+yahoo.com
+yandex.ru
+youtube.com
diff --git a/comm/third_party/botan/src/scripts/tls_suite_info.py b/comm/third_party/botan/src/scripts/tls_suite_info.py
new file mode 100755
index 0000000000..21dcd7fcdb
--- /dev/null
+++ b/comm/third_party/botan/src/scripts/tls_suite_info.py
@@ -0,0 +1,342 @@
+#!/usr/bin/env python2
+
+"""
+Used to generate lib/tls/tls_suite_info.cpp from IANA params
+
+(C) 2011, 2012, 2013, 2014, 2015, 2016, 2017 Jack Lloyd
+
+Botan is released under the Simplified BSD License (see license.txt)
+"""
+
+import sys
+import re
+import datetime
+import hashlib
+import optparse
+
+def to_ciphersuite_info(code, name):
+
+ (sig_and_kex,cipher_and_mac) = name.split('_WITH_')
+
+ if sig_and_kex == 'RSA':
+ sig_algo = 'IMPLICIT'
+ kex_algo = 'RSA'
+ elif 'PSK' in sig_and_kex:
+ sig_algo = 'IMPLICIT'
+ kex_algo = sig_and_kex
+ elif 'SRP' in sig_and_kex:
+ srp_info = sig_and_kex.split('_')
+ if len(srp_info) == 2: # 'SRP_' + hash
+ kex_algo = sig_and_kex
+ sig_algo = 'IMPLICIT'
+ else:
+ kex_algo = '_'.join(srp_info[0:-1])
+ sig_algo = srp_info[-1]
+ else:
+ (kex_algo, sig_algo) = sig_and_kex.split('_')
+
+ cipher_and_mac = cipher_and_mac.split('_')
+
+ mac_algo = cipher_and_mac[-1]
+
+ cipher = cipher_and_mac[:-1]
+
+ if mac_algo == '8' and cipher[-1] == 'CCM':
+ cipher = cipher[:-1]
+ mac_algo = 'CCM_8'
+ elif cipher[-2] == 'CCM' and cipher[-1] == '8':
+ cipher = cipher[:-1]
+ mac_algo = 'CCM_8'
+
+ if mac_algo == 'CCM':
+ cipher += ['CCM']
+ mac_algo = 'SHA256'
+ elif mac_algo == 'CCM_8':
+ cipher += ['CCM(8)']
+ mac_algo = 'SHA256'
+
+ cipher_info = {
+ 'CHACHA20': ('ChaCha',32),
+ 'IDEA': ('IDEA',16),
+ 'DES': ('DES',8),
+ '3DES': ('3DES',24),
+ 'CAMELLIA': ('Camellia',None),
+ 'AES': ('AES',None),
+ 'SEED': ('SEED',16),
+ 'ARIA': ('ARIA',None),
+ }
+
+ tls_to_botan_names = {
+ 'IMPLICIT': 'IMPLICIT',
+
+ 'anon': 'ANONYMOUS',
+ 'MD5': 'MD5',
+ 'SHA': 'SHA-1',
+ 'SHA256': 'SHA-256',
+ 'SHA384': 'SHA-384',
+ 'SHA512': 'SHA-512',
+
+ 'CHACHA': 'ChaCha',
+ '3DES': 'TripleDES',
+
+ 'DSS': 'DSA',
+ 'ECDSA': 'ECDSA',
+ 'RSA': 'RSA',
+ 'SRP_SHA': 'SRP_SHA',
+ 'DHE': 'DH',
+ 'DH': 'DH',
+ 'ECDHE': 'ECDH',
+ 'ECDH': 'ECDH',
+ '': '',
+ 'PSK': 'PSK',
+ 'DHE_PSK': 'DHE_PSK',
+ 'PSK_DHE': 'DHE_PSK',
+ 'ECDHE_PSK': 'ECDHE_PSK',
+ 'CECPQ1': 'CECPQ1',
+ 'CECPQ1_PSK': 'CECPQ1_PSK',
+ }
+
+ mac_keylen = {
+ 'MD5': 16,
+ 'SHA-1': 20,
+ 'SHA-256': 32,
+ 'SHA-384': 48,
+ 'SHA-512': 64,
+ }
+
+ mac_algo = tls_to_botan_names[mac_algo]
+ sig_algo = tls_to_botan_names[sig_algo]
+ kex_algo = tls_to_botan_names[kex_algo]
+ if kex_algo == 'RSA':
+ kex_algo = 'STATIC_RSA'
+
+ (cipher_algo, cipher_keylen) = cipher_info[cipher[0]]
+
+ if cipher_keylen is None:
+ cipher_keylen = int(cipher[1]) / 8
+
+ if cipher_algo in ['AES', 'Camellia', 'ARIA']:
+ cipher_algo += '-%d' % (cipher_keylen*8)
+
+ mode = ''
+
+ if cipher[0] == 'CHACHA20' and cipher[1] == 'POLY1305':
+ return (name, code, sig_algo, kex_algo, "ChaCha20Poly1305", cipher_keylen, "AEAD", 0, mac_algo, 'AEAD_XOR_12')
+
+ mode = cipher[-1]
+ if mode not in ['CBC', 'GCM', 'CCM(8)', 'CCM', 'OCB']:
+ print "#warning Unknown mode '%s' for ciphersuite %s (0x%d)" % (' '.join(cipher), name, code)
+
+ if mode != 'CBC':
+ if mode == 'OCB':
+ cipher_algo += '/OCB(12)'
+ else:
+ cipher_algo += '/' + mode
+
+ if mode == 'CBC':
+ return (name, code, sig_algo, kex_algo, cipher_algo, cipher_keylen, mac_algo, mac_keylen[mac_algo], mac_algo, 'CBC_MODE')
+ elif mode == 'OCB':
+ return (name, code, sig_algo, kex_algo, cipher_algo, cipher_keylen, "AEAD", 0, mac_algo, 'AEAD_XOR_12')
+ else:
+ return (name, code, sig_algo, kex_algo, cipher_algo, cipher_keylen, "AEAD", 0, mac_algo, 'AEAD_IMPLICIT_4')
+
+def open_input(args):
+ iana_url = 'https://www.iana.org/assignments/tls-parameters/tls-parameters.txt'
+
+ if len(args) == 1:
+ try:
+ return open('tls-parameters.txt')
+ except OSError:
+ pass
+
+ import urllib2
+ return urllib2.urlopen(iana_url)
+ else:
+ return open(args[1])
+
+"""
+Handle command line options
+"""
+def process_command_line(args):
+
+ parser = optparse.OptionParser()
+
+ parser.add_option('--with-ocb', action='store_true', default=True,
+ help='enable OCB AEAD suites')
+ parser.add_option('--without-ocb', action='store_false', dest='with_ocb',
+ help='disable OCB AEAD suites')
+
+ parser.add_option('--with-aria-cbc', action='store_true', default=False,
+ help='enable ARIA CBC suites')
+ parser.add_option('--without-aria-cbc', action='store_false', dest='with_aria_cbc',
+ help='disable ARIA CBC suites')
+
+ parser.add_option('--with-cecpq1', action='store_true', default=True,
+ help='enable CECPQ1 suites')
+ parser.add_option('--without-cecpq1', action='store_false', dest='with_cecpq1',
+ help='disable CECPQ1 suites')
+
+ parser.add_option('--with-srp-aead', action='store_true', default=False,
+ help='add SRP AEAD suites')
+ parser.add_option('--without-srp-aead', action='store_false', dest='with_srp_aead',
+ help='disable SRP AEAD suites')
+
+ parser.add_option('--save-download', action='store_true', default=False,
+ help='save downloaded tls-parameters.txt to cwd')
+
+ parser.add_option('--output', '-o',
+ help='file to write output to (default %default)',
+ default='src/lib/tls/tls_suite_info.cpp')
+
+ return parser.parse_args(args)
+
+def main(args = None):
+ if args is None:
+ args = sys.argv
+
+ weak_crypto = ['EXPORT', 'RC2', 'IDEA', 'RC4', '_DES_', 'WITH_NULL', 'GOST']
+ static_dh = ['ECDH_ECDSA', 'ECDH_RSA', 'DH_DSS', 'DH_RSA'] # not supported
+ protocol_goop = ['SCSV', 'KRB5']
+ maybe_someday = ['RSA_PSK', 'ECCPWD']
+ not_supported = weak_crypto + static_dh + protocol_goop + maybe_someday
+
+ (options, args) = process_command_line(args)
+
+ if not options.with_aria_cbc:
+ not_supported += ['ARIA_128_CBC', 'ARIA_256_CBC']
+
+ ciphersuite_re = re.compile(' +0x([0-9a-fA-F][0-9a-fA-F]),0x([0-9a-fA-F][0-9a-fA-F]) + TLS_([A-Za-z_0-9]+) ')
+
+ suites = {}
+
+ contents = ''
+
+ for line in open_input(args):
+ contents += line
+ match = ciphersuite_re.match(line)
+ if match:
+ code = match.group(1) + match.group(2)
+ name = match.group(3)
+
+ should_use = True
+ for ns in not_supported:
+ if ns in name:
+ should_use = False
+
+ if should_use and name.find('_WITH_') > 0:
+ suites[code] = to_ciphersuite_info(code, name)
+
+ sha1 = hashlib.sha1()
+ sha1.update(contents)
+ contents_hash = sha1.hexdigest()
+
+ if options.save_download:
+ out = open('tls-parameters.txt', 'w')
+ out.write(contents)
+ out.close()
+
+ def define_custom_ciphersuite(name, code):
+ suites[code] = to_ciphersuite_info(code, name)
+
+ if options.with_cecpq1:
+ # CECPQ1 key exchange
+ define_custom_ciphersuite('CECPQ1_RSA_WITH_CHACHA20_POLY1305_SHA256', '16B7')
+ define_custom_ciphersuite('CECPQ1_ECDSA_WITH_CHACHA20_POLY1305_SHA256', '16B8')
+ define_custom_ciphersuite('CECPQ1_RSA_WITH_AES_256_GCM_SHA384', '16B9')
+ define_custom_ciphersuite('CECPQ1_ECDSA_WITH_AES_256_GCM_SHA384', '16BA')
+
+ if options.with_ocb:
+ # OCB ciphersuites draft-zauner-tls-aes-ocb-04
+ define_custom_ciphersuite('DHE_RSA_WITH_AES_128_OCB_SHA256', 'FFC0')
+ define_custom_ciphersuite('DHE_RSA_WITH_AES_256_OCB_SHA256', 'FFC1')
+ define_custom_ciphersuite('ECDHE_RSA_WITH_AES_128_OCB_SHA256', 'FFC2')
+ define_custom_ciphersuite('ECDHE_RSA_WITH_AES_256_OCB_SHA256', 'FFC3')
+ define_custom_ciphersuite('ECDHE_ECDSA_WITH_AES_128_OCB_SHA256', 'FFC4')
+ define_custom_ciphersuite('ECDHE_ECDSA_WITH_AES_256_OCB_SHA256', 'FFC5')
+
+ define_custom_ciphersuite('PSK_WITH_AES_128_OCB_SHA256', 'FFC6')
+ define_custom_ciphersuite('PSK_WITH_AES_256_OCB_SHA256', 'FFC7')
+ define_custom_ciphersuite('DHE_PSK_WITH_AES_128_OCB_SHA256', 'FFC8')
+ define_custom_ciphersuite('DHE_PSK_WITH_AES_256_OCB_SHA256', 'FFC9')
+ define_custom_ciphersuite('ECDHE_PSK_WITH_AES_128_OCB_SHA256', 'FFCA')
+ define_custom_ciphersuite('ECDHE_PSK_WITH_AES_256_OCB_SHA256', 'FFCB')
+
+ if options.with_cecpq1 and options.with_ocb:
+ # CECPQ1 OCB ciphersuites - Botan extension
+ define_custom_ciphersuite('CECPQ1_RSA_WITH_AES_256_OCB_SHA256', 'FFCC')
+ define_custom_ciphersuite('CECPQ1_ECDSA_WITH_AES_256_OCB_SHA256', 'FFCD')
+ #define_custom_ciphersuite('CECPQ1_PSK_WITH_AES_256_OCB_SHA256', 'FFCE')
+
+ if options.with_srp_aead:
+ # SRP using GCM or OCB - Botan extension
+ define_custom_ciphersuite('SRP_SHA_WITH_AES_256_GCM_SHA384', 'FFA0')
+ define_custom_ciphersuite('SRP_SHA_RSA_WITH_AES_256_GCM_SHA384', 'FFA1')
+ define_custom_ciphersuite('SRP_SHA_DSS_WITH_AES_256_GCM_SHA384', 'FFA2')
+ define_custom_ciphersuite('SRP_SHA_ECDSA_WITH_AES_256_GCM_SHA384', 'FFA3')
+
+ if options.with_ocb:
+ define_custom_ciphersuite('SRP_SHA_WITH_AES_256_OCB_SHA256', 'FFA4')
+ define_custom_ciphersuite('SRP_SHA_RSA_WITH_AES_256_OCB_SHA256', 'FFA5')
+ define_custom_ciphersuite('SRP_SHA_DSS_WITH_AES_256_OCB_SHA256', 'FFA6')
+ define_custom_ciphersuite('SRP_SHA_ECDSA_WITH_AES_256_OCB_SHA256', 'FFA7')
+
+ suite_info = ''
+
+ def header():
+ return """/*
+* TLS cipher suite information
+*
+* This file was automatically generated from the IANA assignments
+* (tls-parameters.txt hash %s)
+* by %s on %s
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+""" % (contents_hash, sys.argv[0], datetime.date.today().strftime("%Y-%m-%d"))
+
+ suite_info += header()
+
+ suite_info += """#include <botan/tls_ciphersuite.h>
+
+namespace Botan {
+
+namespace TLS {
+
+//static
+const std::vector<Ciphersuite>& Ciphersuite::all_known_ciphersuites()
+ {
+ // Note that this list of ciphersuites is ordered by id!
+ static const std::vector<Ciphersuite> g_ciphersuite_list = {
+"""
+
+ 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 += """ };
+
+ return g_ciphersuite_list;
+ }
+
+}
+
+}
+"""
+
+ if options.output == '-':
+ print suite_info,
+ else:
+ out = open(options.output, 'w')
+ out.write(suite_info)
+ out.close()
+
+ return 0
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/comm/third_party/botan/src/scripts/website.py b/comm/third_party/botan/src/scripts/website.py
new file mode 100755
index 0000000000..e28909531f
--- /dev/null
+++ b/comm/third_party/botan/src/scripts/website.py
@@ -0,0 +1,166 @@
+#!/usr/bin/python
+
+"""
+Generate the Botan website
+
+(C) 2017 Jack Lloyd
+"""
+
+import optparse # pylint: disable=deprecated-module
+import subprocess
+import sys
+import errno
+import shutil
+import tempfile
+import os
+
+def run_and_check(cmd_line, cwd=None):
+ print("Executing %s ..." % (' '.join(cmd_line)))
+
+ proc = subprocess.Popen(cmd_line,
+ cwd=cwd,
+ close_fds=True,
+ stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+
+ (stdout, stderr) = proc.communicate()
+
+ if proc.returncode != 0:
+ print("Error running %s" % (' '.join(cmd_line)))
+ print(stdout)
+ print(stderr)
+ sys.exit(1)
+
+def rmtree_ignore_missing(path):
+ try:
+ shutil.rmtree(path)
+ except OSError:
+ # check errno?
+ pass
+
+def configure_build(botan_dir, build_dir):
+
+ run_and_check([os.path.join(botan_dir, 'configure.py'),
+ '--with-doxygen', '--with-sphinx',
+ '--with-build-dir=%s' % (build_dir)])
+
+def run_doxygen(tmp_dir, output_dir):
+ run_and_check(['doxygen', os.path.join(tmp_dir, 'build/botan.doxy')])
+ shutil.move(os.path.join(tmp_dir, 'build/docs/doxygen'), output_dir)
+
+def run_sphinx(botan_dir, tmp_dir, output_dir):
+
+ sphinx_config = os.path.join(botan_dir, 'src/configs/sphinx')
+ sphinx_dir = os.path.join(tmp_dir, 'sphinx')
+ os.mkdir(sphinx_dir)
+
+ shutil.copyfile(os.path.join(botan_dir, 'readme.rst'),
+ os.path.join(sphinx_dir, 'index.rst'))
+
+ for f in ['news.rst', os.path.join('doc', 'security.rst')]:
+ shutil.copy(os.path.join(botan_dir, f), sphinx_dir)
+
+ toc = """.. toctree::
+
+ index
+ news
+ security
+ User Guide <https://botan.randombit.net/handbook>
+ API Reference <https://botan.randombit.net/doxygen>
+"""
+
+ contents_rst = open(os.path.join(sphinx_dir, 'contents.rst'), 'w')
+ contents_rst.write(toc)
+ contents_rst.close()
+
+ sphinx_invoke = ['sphinx-build', '-t', 'website', '-c', sphinx_config, '-b', 'html']
+
+ handbook_dir = os.path.join(botan_dir, 'doc')
+
+ run_and_check(sphinx_invoke + [sphinx_dir, output_dir])
+ run_and_check(sphinx_invoke + [handbook_dir, os.path.join(output_dir, 'handbook')])
+
+ rmtree_ignore_missing(os.path.join(output_dir, '.doctrees'))
+ rmtree_ignore_missing(os.path.join(output_dir, 'handbook', '.doctrees'))
+ os.remove(os.path.join(output_dir, '.buildinfo'))
+ os.remove(os.path.join(output_dir, 'handbook', '.buildinfo'))
+
+ # share _static subdirs
+ shutil.rmtree(os.path.join(output_dir, 'handbook', '_static'))
+ os.symlink('../_static', os.path.join(output_dir, 'handbook', '_static'))
+
+ # Build PDF
+ latex_output = os.path.join(tmp_dir, 'latex')
+ run_and_check(['sphinx-build', '-c', sphinx_config, '-b', 'latex', handbook_dir, latex_output])
+
+ # Have to run twice because TeX
+ run_and_check(['pdflatex', 'botan.tex'], cwd=latex_output)
+ run_and_check(['pdflatex', 'botan.tex'], cwd=latex_output)
+
+ shutil.copy(os.path.join(latex_output, 'botan.pdf'),
+ os.path.join(output_dir, 'handbook'))
+
+
+def main(args):
+ parser = optparse.OptionParser()
+
+ parser.add_option('-o', '--output-dir', default=None,
+ help="Where to write output")
+
+ (options, args) = parser.parse_args(args)
+
+ output_dir = options.output_dir
+ tmp_dir = tempfile.mkdtemp(prefix='botan_website_')
+
+ # assumes we live in src/scripts
+ botan_dir = os.path.normpath(os.path.join(os.path.dirname(__file__),
+ "..", ".."))
+
+ if os.access(os.path.join(botan_dir, 'configure.py'), os.X_OK) is False:
+ print("Can't find configure.py in %s", botan_dir)
+ return 1
+
+ if output_dir is None:
+ cwd = os.getcwd()
+
+ if os.path.basename(cwd) == 'botan-website':
+ output_dir = '.'
+ else:
+ output_dir = os.path.join(cwd, 'botan-website')
+
+ try:
+ os.mkdir(output_dir)
+ except OSError as e:
+ if e.errno == errno.EEXIST:
+ pass
+ else:
+ raise e
+
+ for subdir in ['_static', '_sources', 'doxygen', 'handbook']:
+ try:
+ shutil.rmtree(os.path.join(output_dir, subdir))
+ except OSError as e:
+ if e.errno == errno.ENOENT:
+ pass
+ else:
+ print("Error removing dir", e)
+ return 1
+
+ configure_build(botan_dir, tmp_dir)
+ run_doxygen(tmp_dir, output_dir)
+ run_sphinx(botan_dir, tmp_dir, output_dir)
+
+ for f in ['doc/pgpkey.txt', 'license.txt']:
+ shutil.copy(os.path.join(botan_dir, f), output_dir)
+
+ favicon = open(os.path.join(output_dir, 'favicon.ico'), 'w')
+ # Create an empty favicon.ico file so it gets cached by browsers
+ favicon.close()
+
+ shutil.rmtree(tmp_dir)
+
+ return 0
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv))