diff options
69 files changed, 2877 insertions, 1467 deletions
diff --git a/.travis.yml b/.travis.yml index e71e76d2a..a2ccb3a64 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,113 +1,86 @@ -dist: trusty - -sudo: required - language: cpp -# BEGIN BUILD MATRIX -# -# Build matrix must be smaller on branch coverity_scan because quota allows -# only 5 Coverity Scan jobs at once. Thus we have different versions of this -# file on branch master and coverity_scan. -# -# Please keep this file as much in sync as possible to allow easy merging -# from master into coverity_scan and only have differences in the build -# matrix block. -# -# Check the difference as follows: -# $ git checkout master -# $ git checkout coverity_scan -# $ git difftool master coverity_scan .travis.yml os: - linux - osx +dist: trusty +sudo: required osx_image: xcode7 compiler: - clang - gcc -git: - depth: 5 - env: global: + - CCACHE_SIZE=50M + - CCACHE_COMPRESS=1 + - BUILD_JOBS=2 # SONAR_TOKEN for accessing the SonarQube server - secure: "VezRbHFg6kllV5WG06M3tG3aHJaC3xrMylJ6RCVbL+uz2JeralVCqV7eIk4fVb9cu83Li+weEa0AJj0wkxpIUJ+vUh5F65L6gSWSbgHP7muOSVsmnEc6KvX4n3av/ZGe4geSmsxqh2pd/2xI1h7KioGRhKeqaZIdjVgWgGJW2iQ=" # GITHUB_TOKEN for posting found issues as comments in the PR that's beeing analyzed by SonarQube - secure: "Th0mBSkUCDqu+EA6F7zA6DCSDZBNunfndANyq06BwaFlj71daWWjthwYFsfg3T5N2ZmI+PsULQQpOirCnJt1lbNHhMVJwZPkW0JnjoxbSNpSI2+nHv7+GO9X9WjK0LRFawiQu8WxmLMQDA+0oR0BERSFKc3gmbuav9fDfla0dXg=" matrix: - - TARGETOS="native" MODULES="all" BOOST="n" BUILD_MODE="shared" - - TARGETOS="native" MODULES="all" BOOST="n" BUILD_MODE="static" - - TARGETOS="native" MODULES="all" BOOST="n" BUILD_MODE="coverage" - - TARGETOS="native" MODULES="all" BOOST="n" BUILD_MODE="sanitizer" - - TARGETOS="native" MODULES="all" BOOST="n" BUILD_MODE="sonarqube" - - TARGETOS="native" MODULES="all" BOOST="y" BUILD_MODE="shared" - - TARGETOS="native" MODULES="all" BOOST="y" BUILD_MODE="static" - - TARGETOS="native" MODULES="all" BOOST="y" BUILD_MODE="coverage" - - TARGETOS="native" MODULES="all" BOOST="y" BUILD_MODE="sanitizer" - - - TARGETOS="native" MODULES="min" BOOST="n" BUILD_MODE="shared" - - TARGETOS="native" MODULES="min" BOOST="n" BUILD_MODE="static" - - TARGETOS="native" MODULES="min" BOOST="y" BUILD_MODE="shared" - - TARGETOS="native" MODULES="min" BOOST="y" BUILD_MODE="static" - - - TARGETOS="ios32" MODULES="all" BOOST="n" BUILD_MODE="static" - - TARGETOS="ios64" MODULES="all" BOOST="n" BUILD_MODE="static" + - BUILD_MODE="shared" + - BUILD_MODE="static" + - BUILD_MODE="mini-shared" + - BUILD_MODE="mini-static" + - BUILD_MODE="cross-arm32" + - BUILD_MODE="cross-arm64" + - BUILD_MODE="cross-ppc32" + - BUILD_MODE="cross-ppc64" + - BUILD_MODE="cross-win32" + - BUILD_MODE="coverage" + - BUILD_MODE="sanitizer" + - BUILD_MODE="valgrind" + - BUILD_MODE="sonarqube" matrix: - exclude: - - os: osx - compiler: gcc - - # No boost on Linux because installing Boost is easier on OS X - - os: linux - env: TARGETOS="native" MODULES="all" BOOST="y" BUILD_MODE="shared" + # Ignore some problem builds for now + allow_failures: - os: linux - env: TARGETOS="native" MODULES="all" BOOST="y" BUILD_MODE="static" + env: BUILD_MODE="cross-ppc32" - os: linux - env: TARGETOS="native" MODULES="all" BOOST="y" BUILD_MODE="coverage" + env: BUILD_MODE="cross-ppc64" - os: linux - env: TARGETOS="native" MODULES="all" BOOST="y" BUILD_MODE="sanitizer" - - os: linux - env: TARGETOS="native" MODULES="min" BOOST="y" BUILD_MODE="shared" - - os: linux - env: TARGETOS="native" MODULES="min" BOOST="y" BUILD_MODE="static" + env: BUILD_MODE="cross-win32" - # No coverage, sanitizer and sonarqube on clang + exclude: + # Skip GCC on OS X entirely + - os: osx + compiler: gcc + + # Run coverage, valgrind, sonarqube, sanitizer, minimized and + # non-ARM cross builds on Linux/gcc only. The sanitizer builds + # under Clang run the tests very slowly and cause CI timeouts. - compiler: clang - env: TARGETOS="native" MODULES="all" BOOST="n" BUILD_MODE="coverage" + env: BUILD_MODE="coverage" - compiler: clang - env: TARGETOS="native" MODULES="all" BOOST="n" BUILD_MODE="sanitizer" + env: BUILD_MODE="sonarqube" - compiler: clang - env: TARGETOS="native" MODULES="all" BOOST="n" BUILD_MODE="sonarqube" + env: BUILD_MODE="sanitizer" - compiler: clang - env: TARGETOS="native" MODULES="all" BOOST="y" BUILD_MODE="coverage" + env: BUILD_MODE="valgrind" - compiler: clang - env: TARGETOS="native" MODULES="all" BOOST="y" BUILD_MODE="sanitizer" - - # No minimal builds on clang + env: BUILD_MODE="mini-shared" - compiler: clang - env: TARGETOS="native" MODULES="min" BOOST="n" BUILD_MODE="shared" + env: BUILD_MODE="mini-static" - compiler: clang - env: TARGETOS="native" MODULES="min" BOOST="n" BUILD_MODE="static" + env: BUILD_MODE="cross-win32" - compiler: clang - env: TARGETOS="native" MODULES="min" BOOST="y" BUILD_MODE="shared" + env: BUILD_MODE="cross-ppc32" - compiler: clang - env: TARGETOS="native" MODULES="min" BOOST="y" BUILD_MODE="static" + env: BUILD_MODE="cross-ppc64" + # No ARM Clang compiler on Linux - os: linux - env: TARGETOS="ios32" MODULES="all" BOOST="n" BUILD_MODE="static" + compiler: clang + env: BUILD_MODE="cross-arm32" - os: linux - env: TARGETOS="ios64" MODULES="all" BOOST="n" BUILD_MODE="static" -# END BUILD MATRIX - -cache: - ccache: true - directories: - - $HOME/.sonar/cache + compiler: clang + env: BUILD_MODE="cross-arm64" install: - ./src/scripts/ci/travis/install.sh @@ -121,6 +94,14 @@ after_success: notifications: email: [email protected] +git: + depth: 5 + +cache: + ccache: true + directories: + - $HOME/.sonar/cache + addons: sonarqube: true @@ -131,3 +112,4 @@ addons: build_command_prepend: "./configure.py --cc-bin=/usr/bin/g++-4.8" build_command: "make -j2" branch_pattern: coverity_scan + diff --git a/botan_version.py b/botan_version.py index c737e21a0..06fc6cf5c 100644 --- a/botan_version.py +++ b/botan_version.py @@ -1,7 +1,7 @@ release_major = 1 release_minor = 11 -release_patch = 31 +release_patch = 32 release_so_abi_rev = release_patch # These are set by the distribution script diff --git a/configure.py b/configure.py index 9d30d08a0..5ab812fad 100755 --- a/configure.py +++ b/configure.py @@ -478,6 +478,8 @@ Generic lexer function for info.txt and src/build-data files """ def lex_me_harder(infofile, to_obj, allowed_groups, name_val_pairs): + to_obj.infofile = infofile + # Format as a nameable Python variable def py_var(group): return group.replace(':', '_') @@ -733,6 +735,19 @@ class ModulePolicyInfo(object): lex_me_harder(infofile, self, ['required', 'if_available', '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(object): def __init__(self, infofile): lex_me_harder(infofile, self, @@ -838,7 +853,6 @@ class CompilerInfo(object): self.mach_abi_linking = force_to_dict(self.mach_abi_linking) self.isa_flags = force_to_dict(self.isa_flags) - self.infofile = infofile self.mach_opt_flags = {} while self.mach_opt != []: @@ -1841,7 +1855,7 @@ def main(argv = None): logging.getLogger().setLevel(log_level()) - logging.debug('%s invoked with options "%s"' % ( + logging.info('%s invoked with options "%s"' % ( argv[0], ' '.join(argv[1:]))) logging.info('Platform: OS="%s" machine="%s" proc="%s"' % ( @@ -1887,6 +1901,9 @@ def main(argv = None): module_policies = load_build_data('module policy', 'policy', ModulePolicyInfo) + for policy in module_policies.values(): + policy.cross_check(modules) + if options.list_modules: for k in sorted(modules.keys()): print(k) diff --git a/doc/license.txt b/doc/license.txt index b097646e8..88b16651c 100644 --- a/doc/license.txt +++ b/doc/license.txt @@ -31,6 +31,8 @@ Copyright (C) 1999-2013,2014,2015,2016 Jack Lloyd 2015,2016 Kai Michaelis 2016 Simon Cogliani 2015,2016 Rohde & Schwarz Cybersecurity + 2016 Juraj Somorovsky + 2016 Christian Mainka All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/doc/manual/tls.rst b/doc/manual/tls.rst index a10a4280c..6c1ca42f2 100644 --- a/doc/manual/tls.rst +++ b/doc/manual/tls.rst @@ -22,44 +22,62 @@ 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 a user provided callback will be invoked. If the reader is -familiar with OpenSSL's BIO layer, it might be analagous to saying the -only way of interacting with Botan's TLS is via a `BIO_mem` I/O +side, then the appropriate user provided callback will be invoked. + +If the reader is familiar with OpenSSL's BIO layer, it might be analagous +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, etc. +message queue, lwIP on RTOS, some carrier pidgeons, etc. -The callbacks for TLS have the signatures +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 output_fn(const byte data[], size_t data_len) + .. cpp:function:: void tls_emit_data(const byte data[], size_t data_len) - TLS requests that all bytes of *data* be queued up to send to the - counterparty. After this function returns, *data* will be - overwritten, so a copy of the input must be made if the callback + 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. - .. cpp:function:: void data_cb(const byte data[], size_t data_len) + 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 asyncronous 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 byte 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. - Called whenever application data is received from the other side - of the connection, in which case *data* and *data_len* specify - the data received. This array will be overwritten sometime after - the callback returns, so again a copy should be made if need be. + For TLS the record number will always increase. - .. cpp:function:: void alert_cb(Alert alert, const byte data[], size_t data_len) + For DTLS, it is possible to receive records with the `rec_no` field + field out of order or repeated. It is even possible (from a malicious or + faulty peer) to receive multiple copies of a single record with differing plaintexts. - Called when an alert is received. Normally, data is null and - data_len is 0, as most alerts have no associated data. However, - if TLS heartbeats (see :rfc:`6520`) were negotiated, and we - initiated a heartbeat, then if/when the other party responds, - ``alert_cb`` will be called with whatever data was included in - the heartbeat response (if any) along with a psuedo-alert value - of ``HEARTBEAT_PAYLOAD``. + .. cpp:function:: void tls_alert(Alert alert) - .. cpp:function:: bool handshake_cb(const TLS::Session& session) + 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) - Called whenever a negotiation completes. This can happen more - than once on any connection. The *session* parameter provides - information about the session which was established. + 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. @@ -68,8 +86,29 @@ The callbacks for TLS have the signatures exception which will send a close message to the counterparty and reset the connection state. -You can of course use tools like ``std::bind`` to bind additional -parameters to your callback functions. + .. 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_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_log_debug(const char*) + + This callback is for exerimental purposes and currently unused. It may be + removed or modified in a future release. + +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 ---------------------------------------- @@ -80,16 +119,6 @@ available: .. cpp:class:: TLS::Channel - .. cpp:type:: std::function<void (const byte[], size_t)> output_fn - - .. cpp:type:: std::function<void (const byte[], size_t)> data_cb - - .. cpp:type:: std::function<void (Alert, const byte[], size_t)> alert_cb - - .. cpp:type:: std::function<bool (const Session&)> handshake_cb - - Typedefs used in the code for the functions described above - .. cpp:function:: size_t received_data(const byte buf[], size_t buf_size) .. cpp:function:: size_t received_data(const std::vector<byte>& buf) @@ -194,10 +223,7 @@ TLS Clients .. cpp:class:: TLS::Client .. cpp:function:: Client( \ - output_fn out, \ - data_cb app_data_cb, \ - alert_cb alert_cb, \ - handshake_cb hs_cb, \ + Callbacks& callbacks, Session_Manager& session_manager, \ Credentials_Manager& creds, \ const Policy& policy, \ @@ -211,29 +237,8 @@ TLS Clients Initialize a new TLS client. The constructor will immediately initiate a new session. - The *output_fn* callback will be called with output that - should be sent to the counterparty. For instance this will be - called immediately from the constructor after the client hello - message is constructed. An implementation of *output_fn* is - allowed to defer the write (for instance if writing when the - callback occurs would block), but should eventually write the data - to the counterparty *in order*. - - The *data_cb* will be called with data sent by the counterparty - after it has been processed. The byte array and size_t represent - the plaintext value and size. - - The *alert_cb* will be called when a protocol alert is received, - commonly with a close alert during connection teardown. - - The *handshake_cb* function is called when a handshake - (either initial or renegotiation) is completed. The return value of - the callback specifies if the session should be cached for later - resumption. If the function for some reason desires to prevent the - connection from completing, it should throw an exception - (preferably a TLS::Exception, which can provide more specific alert - information to the counterparty). The :cpp:class:`TLS::Session` - provides information about the session that was just established. + 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. @@ -285,34 +290,30 @@ TLS Servers .. cpp:class:: TLS::Server .. cpp:function:: Server( \ - output_fn output, \ - data_cb data_cb, \ - alert_cb alert_cb, \ - handshake_cb handshake_cb, \ + Callbacks& callbacks, 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 = 16*1024 \ ) -The first 8 arguments as well as the final argument +The first 5 arguments as well as the final argument *reserved_io_buffer_size*, are treated similiarly to the :ref:`client <tls_client>`. -The (optional) argument, *proto_chooser*, is a function called if the -client sent the ALPN extension to negotiate an application -protocol. In that case, the function should choose a protocol to use -and return it. Alternately it can throw an exception to abort the -exchange; the ALPN specification says that if this occurs the alert -should be of type `NO_APPLICATION_PROTOCOL`. +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 hello without this parameter. +would not until they actually received a client hello. Code for a TLS server using asio is in `src/cli/tls_proxy.cpp`. @@ -516,7 +517,7 @@ policy settings from a file. Values without an explicit mode use old-style CBC with HMAC encryption. Default value: "AES-256/GCM", "AES-128/GCM", "ChaCha20Poly1305", - "AES-256/CCM", "AES-128/CCM", "AES-256/CCM-8", "AES-128/CCM-8", + "AES-256/CCM", "AES-128/CCM", "AES-256/CCM(8)", "AES-128/CCM(8)", "AES-256", "AES-128" Also allowed: "Camellia-256/GCM", "Camellia-128/GCM", @@ -529,15 +530,18 @@ policy settings from a file. .. note:: - The current ChaCha20Poly1305 ciphersuites are non-standard but - as of 2015 were implemented and deployed by Google and - elsewhere. Support will be changed to using IETF standard - ChaCha20Poly1305 ciphersuites when those are defined. + 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 @@ -577,6 +581,10 @@ policy settings from a file. Also allowed (disabled by default): "" (meaning anonymous) + .. note:: + + DSA authentication is deprecated and will be removed in a future release. + .. cpp:function:: std::vector<std::string> allowed_ecc_curves() const Return a list of ECC curves we are willing to use, in order of preference. diff --git a/doc/news.rst b/doc/news.rst index b5cfa86ab..e27ae6593 100644 --- a/doc/news.rst +++ b/doc/news.rst @@ -1,7 +1,19 @@ Release Notes ======================================== -Version 1.11.31, Not Yet Released +Version 1.11.32, Not Yet Released +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +* Add support for TLS Encrypt-then-MAC extension (GH #492 and #578) + which fixes the known issues in the TLS CBC-HMAC construction. + +* Add a new TLS Callbacks interface. Compatability with previous + versions is maintained. GH #457 and #567 + +* The HMAC_RNG constructor added in 1.11.31 that took both an RNG and an + entropy source list ignored the entropy sources. + +Version 1.11.31, 2016-08-30 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ * Fix undefined behavior in Curve25519 on platforms without a native 128-bit @@ -23,22 +35,37 @@ Version 1.11.31, Not Yet Released 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. + 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 ECKCDSA signature algorithm (#504) +* 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) -* RNG changes: NIST SP900-80's HMAC_DRBG is now the default generator - for userspace RNG (in AutoSeeded_RNG). HMAC_DRBG now attempts to detect - use of fork via pid checks and perform automatic reseeding. - GH #520 +* 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) * Add RDRAND_RNG which directly exposes the CPU RNG (GH #543) @@ -58,6 +85,8 @@ Version 1.11.31, Not Yet Released * 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) @@ -74,11 +103,13 @@ Version 1.11.31, Not Yet Released * 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) + BigInt (GH #558), HMAC_DRBG (GH #598 #600) * New deprecations. See the full list in doc/deprecated.txt diff --git a/doc/security.rst b/doc/security.rst index 23b46f30d..6223943e0 100644 --- a/doc/security.rst +++ b/doc/security.rst @@ -19,6 +19,24 @@ Advisories 2016 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +* 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 diff --git a/doc/todo.rst b/doc/todo.rst index b6c55a898..f4720f3ba 100644 --- a/doc/todo.rst +++ b/doc/todo.rst @@ -22,7 +22,6 @@ CLI for an example * `encrypt` / `decrypt` tools providing password and/or public key based file encryption -* `bcrypt` cmdlet * Make help output more helpful * More microbenchmarks in `speed`: modular exponentiation, ECC point multiplication, other BigInt operations @@ -33,7 +32,9 @@ TLS * Make DTLS support optional at build time * Make TLS v1.0 and v1.1 optional at build time +* Make finite field DH optional at build time * Curve25519 key exchange +* NEWHOPE (CECPQ1) key exchange (GH #613) * TLS OCSP stapling (RFC 6066) * Encrypt-then-MAC extension (RFC 7366) * Authentication using TOFU (sqlite3 storage) @@ -47,6 +48,7 @@ TLS PKIX ---------------------------------------- +* Test suite for validation of 'real world' cert chains (GH #611) * Support multiple DNS names in certificates * X.509 policy constraints * OCSP responder logic @@ -109,7 +111,6 @@ Public Key Crypto, Math * XMSS (draft-irtf-cfrg-xmss-hash-based-signatures) * SPHINCS-256 -* NEWHOPE RLWE scheme + BoringSSL compatible TLS cipher suites * EdDSA (GH #283) * Ed448-Goldilocks * FHMQV diff --git a/readme.rst b/readme.rst index 2f6134ba8..35f58d159 100644 --- a/readme.rst +++ b/readme.rst @@ -100,9 +100,9 @@ Versions 1.11 and later require a working C++11 compiler; GCC 4.8 and later, Clang 3.4 and later, and MSVC 2013 are regularly tested. The latest development release is -`1.11.30 <http://botan.randombit.net/releases/Botan-1.11.30.tgz>`_ -`(sig) <http://botan.randombit.net/releases/Botan-1.11.30.tgz.asc>`_ -released on 2016-06-19 +`1.11.31 <http://botan.randombit.net/releases/Botan-1.11.31.tgz>`_ +`(sig) <http://botan.randombit.net/releases/Botan-1.11.31.tgz.asc>`_ +released on 2016-08-30 Old Stable Series (1.10) ---------------------------------------- diff --git a/src/build-data/cc/clang.txt b/src/build-data/cc/clang.txt index 0e2963665..8c5baf7ca 100644 --- a/src/build-data/cc/clang.txt +++ b/src/build-data/cc/clang.txt @@ -11,12 +11,13 @@ add_framework_option "-framework " lang_flags "-std=c++11 -D_REENTRANT -fstack-protector" warning_flags "-Wall -Wextra -Wpedantic -Wshadow -Wstrict-aliasing -Wstrict-overflow=5 -Wcast-align -Wmissing-declarations -Wpointer-arith -Wcast-qual -Wunreachable-code" -maintainer_warning_flags "-Qunused-arguments -Werror -Wno-error=unused-parameter -Wno-error=unused-variable -Wno-error=unreachable-code" +maintainer_warning_flags "-Qunused-arguments -Werror -Wno-error=unused-parameter -Wno-error=unused-variable -Wno-error=unreachable-code -Wno-error=deprecated-declarations" compile_flags "-c" debug_info_flags "-g" optimization_flags "-O3" -sanitizer_flags "-fsanitize=address,undefined -fsanitize-coverage=edge,indirect-calls,8bit-counters -fno-sanitize-recover=undefined" +#sanitizer_flags "-fsanitize=address,undefined -fsanitize-coverage=edge,indirect-calls,8bit-counters -fno-sanitize-recover=undefined" +sanitizer_flags "-fsanitize=address,undefined" shared_flags "-fPIC" coverage_flags "--coverage" diff --git a/src/build-data/cc/gcc.txt b/src/build-data/cc/gcc.txt index 0687a6dd0..7d795d4e1 100644 --- a/src/build-data/cc/gcc.txt +++ b/src/build-data/cc/gcc.txt @@ -8,8 +8,11 @@ add_lib_dir_option -L add_lib_option -l lang_flags "-std=c++11 -D_REENTRANT" -maintainer_warning_flags "-Wold-style-cast -Werror -Wno-error=old-style-cast -Wno-error=zero-as-null-pointer-constant -Wno-error=unused-parameter -Wno-error=unused-variable -Wno-error=strict-overflow -Wsuggest-override" -warning_flags "-Wall -Wextra -Wpedantic -Wshadow -Wstrict-aliasing -Wstrict-overflow=5 -Wcast-align -Wmissing-declarations -Wpointer-arith -Wcast-qual -Wzero-as-null-pointer-constant -Wnon-virtual-dtor" + +# This should only contain flags which are included in GCC 4.8 +warning_flags "-Wall -Wextra -Wpedantic -Wstrict-aliasing -Wstrict-overflow=5 -Wcast-align -Wmissing-declarations -Wpointer-arith -Wcast-qual -Wzero-as-null-pointer-constant -Wnon-virtual-dtor" + +maintainer_warning_flags "-Wold-style-cast -Wsuggest-override -Wshadow -Werror -Wno-error=old-style-cast -Wno-error=zero-as-null-pointer-constant -Wno-error=unused-parameter -Wno-error=unused-variable -Wno-error=strict-overflow -Wno-error=deprecated-declarations" compile_flags "-c" debug_info_flags "-g" diff --git a/src/build-data/policy/bsi.txt b/src/build-data/policy/bsi.txt index 7f2b09610..c4ed27648 100644 --- a/src/build-data/policy/bsi.txt +++ b/src/build-data/policy/bsi.txt @@ -42,7 +42,6 @@ ecdh # rng auto_rng -hmac_rng hmac_drbg </required> @@ -59,7 +58,6 @@ beos_stats cryptoapi_rng darwin_secrandom dev_random -hres_timer proc_walk rdrand rdseed @@ -71,9 +69,7 @@ system_rng # utils locking_allocator -simd_altivec -simd_scalar -simd_sse2 +simd </if_available> <prohibited> @@ -141,7 +137,6 @@ eme_raw #emsa_pkcs1 // needed for tls emsa_raw emsa_x931 -emsa1_bsi # hash blake2 diff --git a/src/build-data/policy/modern.txt b/src/build-data/policy/modern.txt index a98ec5077..fa4b45d74 100644 --- a/src/build-data/policy/modern.txt +++ b/src/build-data/policy/modern.txt @@ -56,9 +56,7 @@ aes_ssse3 serpent_simd threefish_avx2 -simd_scalar -simd_sse2 -simd_altivec +simd rdrand_rng system_rng @@ -68,7 +66,6 @@ beos_stats cryptoapi_rng darwin_secrandom dev_random -hres_timer proc_walk rdrand rdseed diff --git a/src/cli/credentials.h b/src/cli/credentials.h index 11bfd3de1..95bbd5aa4 100644 --- a/src/cli/credentials.h +++ b/src/cli/credentials.h @@ -62,7 +62,7 @@ class Basic_Credentials_Manager : public Botan::Credentials_Manager try { // TODO: make path configurable - const std::vector<std::string> paths = { "/usr/share/ca-certificates" }; + const std::vector<std::string> paths = { "/etc/ssl/certs", "/usr/share/ca-certificates" }; for(auto&& path : paths) { diff --git a/src/cli/tls_client.cpp b/src/cli/tls_client.cpp index 6af2f56f8..caf7d4a1f 100644 --- a/src/cli/tls_client.cpp +++ b/src/cli/tls_client.cpp @@ -1,5 +1,6 @@ /* * (C) 2014,2015 Jack Lloyd +* 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -35,7 +36,7 @@ namespace Botan_CLI { -class TLS_Client final : public Command +class TLS_Client final : public Command, public Botan::TLS::Callbacks { public: TLS_Client() : Command("tls_client host --port=443 --print-certs --policy= " @@ -98,15 +99,10 @@ class TLS_Client final : public Command const std::vector<std::string> protocols_to_offer = Botan::split_on("next-protocols", ','); - int sockfd = connect_to_host(host, port, use_tcp); + m_sockfd = connect_to_host(host, port, use_tcp); using namespace std::placeholders; - auto socket_write = - use_tcp ? - std::bind(stream_socket_write, sockfd, _1, _2) : - std::bind(dgram_socket_write, sockfd, _1, _2); - auto version = policy->latest_supported_version(!use_tcp); if(flag_set("tls1.0")) @@ -118,10 +114,7 @@ class TLS_Client final : public Command version = Botan::TLS::Protocol_Version::TLS_V11; } - Botan::TLS::Client client(socket_write, - std::bind(&TLS_Client::process_data, this, _1, _2), - std::bind(&TLS_Client::alert_received, this, _1, _2, _3), - std::bind(&TLS_Client::handshake_complete, this, _1), + Botan::TLS::Client client(*this, *session_mgr, creds, *policy, @@ -136,7 +129,7 @@ class TLS_Client final : public Command { fd_set readfds; FD_ZERO(&readfds); - FD_SET(sockfd, &readfds); + FD_SET(m_sockfd, &readfds); if(client.is_active()) { @@ -152,13 +145,13 @@ class TLS_Client final : public Command struct timeval timeout = { 1, 0 }; - ::select(sockfd + 1, &readfds, nullptr, nullptr, &timeout); + ::select(m_sockfd + 1, &readfds, nullptr, nullptr, &timeout); - if(FD_ISSET(sockfd, &readfds)) + if(FD_ISSET(m_sockfd, &readfds)) { uint8_t buf[4*1024] = { 0 }; - ssize_t got = ::read(sockfd, buf, sizeof(buf)); + ssize_t got = ::read(m_sockfd, buf, sizeof(buf)); if(got == 0) { @@ -216,7 +209,7 @@ class TLS_Client final : public Command } } - ::close(sockfd); + ::close(m_sockfd); } private: @@ -256,7 +249,7 @@ class TLS_Client final : public Command return fd; } - bool handshake_complete(const Botan::TLS::Session& session) + bool tls_session_established(const Botan::TLS::Session& session) override { output() << "Handshake complete, " << session.version().to_string() << " using " << session.ciphersuite().to_string() << "\n"; @@ -290,13 +283,13 @@ class TLS_Client final : public Command throw CLI_Error("Socket write failed errno=" + std::to_string(errno)); } - static void stream_socket_write(int sockfd, const uint8_t buf[], size_t length) + void tls_emit_data(const uint8_t buf[], size_t length) override { size_t offset = 0; while(length) { - ssize_t sent = ::send(sockfd, (const char*)buf + offset, + ssize_t sent = ::send(m_sockfd, (const char*)buf + offset, length, MSG_NOSIGNAL); if(sent == -1) @@ -312,16 +305,19 @@ class TLS_Client final : public Command } } - void alert_received(Botan::TLS::Alert alert, const uint8_t [], size_t ) + void tls_alert(Botan::TLS::Alert alert) override { output() << "Alert: " << alert.type_string() << "\n"; } - void process_data(const uint8_t buf[], size_t buf_size) + 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]; } + + private: + int m_sockfd; }; BOTAN_REGISTER_COMMAND("tls_client", TLS_Client); diff --git a/src/cli/tls_proxy.cpp b/src/cli/tls_proxy.cpp index 2929e473d..5140654de 100644 --- a/src/cli/tls_proxy.cpp +++ b/src/cli/tls_proxy.cpp @@ -1,6 +1,7 @@ /* * TLS Server Proxy * (C) 2014,2015 Jack Lloyd +* (C) 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -59,7 +60,7 @@ void log_text_message(const char* where, const uint8_t buf[], size_t buf_len) //std::cout << where << ' ' << std::string(c, c + buf_len) << std::endl; } -class tls_proxy_session : public boost::enable_shared_from_this<tls_proxy_session> +class tls_proxy_session : public boost::enable_shared_from_this<tls_proxy_session>, public Botan::TLS::Callbacks { public: enum { readbuf_size = 4 * 1024 }; @@ -111,10 +112,7 @@ class tls_proxy_session : public boost::enable_shared_from_this<tls_proxy_sessio m_server_endpoints(endpoints), m_client_socket(io), m_server_socket(io), - m_tls(boost::bind(&tls_proxy_session::tls_proxy_write_to_client, this, _1, _2), - boost::bind(&tls_proxy_session::tls_client_write_to_proxy, this, _1, _2), - boost::bind(&tls_proxy_session::tls_alert_cb, this, _1, _2, _3), - boost::bind(&tls_proxy_session::tls_handshake_complete, this, _1), + m_tls(*this, session_manager, credentials, policy, @@ -167,7 +165,7 @@ class tls_proxy_session : public boost::enable_shared_from_this<tls_proxy_sessio { m_client_socket.close(); } - tls_proxy_write_to_client(nullptr, 0); // initiate another write if needed + tls_emit_data(nullptr, 0); // initiate another write if needed } void handle_server_write_completion(const boost::system::error_code& error) @@ -183,13 +181,13 @@ class tls_proxy_session : public boost::enable_shared_from_this<tls_proxy_sessio proxy_write_to_server(nullptr, 0); // initiate another write if needed } - void tls_client_write_to_proxy(const uint8_t buf[], size_t buf_len) + 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_proxy_write_to_client(const uint8_t buf[], size_t 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); @@ -268,7 +266,7 @@ class tls_proxy_session : public boost::enable_shared_from_this<tls_proxy_sessio boost::asio::placeholders::bytes_transferred))); } - bool tls_handshake_complete(const Botan::TLS::Session& session) + bool tls_session_established(const Botan::TLS::Session& session) override { //std::cout << "Handshake from client complete" << std::endl; @@ -292,7 +290,7 @@ class tls_proxy_session : public boost::enable_shared_from_this<tls_proxy_sessio return true; } - void tls_alert_cb(Botan::TLS::Alert alert, const uint8_t[], size_t) + void tls_alert(Botan::TLS::Alert alert) override { if(alert.type() == Botan::TLS::Alert::CLOSE_NOTIFY) { diff --git a/src/cli/tls_server.cpp b/src/cli/tls_server.cpp index 2496f5508..7fc38cf31 100644 --- a/src/cli/tls_server.cpp +++ b/src/cli/tls_server.cpp @@ -33,7 +33,7 @@ namespace Botan_CLI { class TLS_Server final : public Command { public: - TLS_Server() : Command("tls_server cert key --port=443 --type=tcp") {} + TLS_Server() : Command("tls_server cert key --port=443 --type=tcp --policy=") {} void go() override { @@ -47,7 +47,24 @@ class TLS_Server final : public Command const bool is_tcp = (transport == "tcp"); - Botan::TLS::Policy policy; // TODO read policy from file + std::unique_ptr<Botan::TLS::Policy> policy; + const std::string policy_file = get_arg("policy"); + std::filebuf fb; + if(policy_file.size() > 0) + { + std::ifstream policy_stream(policy_file); + if(!policy_stream.good()) + { + error_output() << "Failed reading policy file\n"; + return; + } + policy.reset(new Botan::TLS::Text_Policy(policy_stream)); + } + + if(!policy) + { + policy.reset(new Botan::TLS::Policy); + } Botan::TLS::Session_Manager_In_Memory session_manager(rng()); // TODO sqlite3 @@ -112,7 +129,7 @@ class TLS_Server final : public Command std::bind(&TLS_Server::handshake_complete, this, _1), session_manager, creds, - policy, + *policy, rng(), protocol_chooser, !is_tcp); diff --git a/src/cli/utils.cpp b/src/cli/utils.cpp index 610a14dc1..5ec8f295e 100644 --- a/src/cli/utils.cpp +++ b/src/cli/utils.cpp @@ -266,7 +266,7 @@ class Generate_Bcrypt final : public Command void go() override { const std::string password = get_arg("password"); - const size_t wf = get_arg_sz("work_factor"); + const size_t wf = get_arg_sz("work-factor"); output() << Botan::generate_bcrypt(password, rng(), wf) << "\n"; } diff --git a/src/lib/cert/x509/x509_ca.h b/src/lib/cert/x509/x509_ca.h index 6ea51cd06..ba3724f5e 100644 --- a/src/lib/cert/x509/x509_ca.h +++ b/src/lib/cert/x509/x509_ca.h @@ -22,7 +22,6 @@ namespace Botan { class BOTAN_DLL X509_CA { public: - /** * Sign a PKCS#10 Request. * @param req the request to sign diff --git a/src/lib/cert/x509/x509cert.cpp b/src/lib/cert/x509/x509cert.cpp index 20a4bca25..ffedf43f0 100644 --- a/src/lib/cert/x509/x509cert.cpp +++ b/src/lib/cert/x509/x509cert.cpp @@ -276,19 +276,21 @@ bool X509_Certificate::allowed_extended_usage(const std::string& usage) const 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_Constraints(DATA_ENCIPHERMENT | KEY_ENCIPHERMENT | DIGITAL_SIGNATURE)) && allowed_extended_usage("PKIX.ServerAuth"); + 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(Key_Constraints(DIGITAL_SIGNATURE | NON_REPUDIATION)) && allowed_extended_usage("PKIX.ClientAuth"); + return (allowed_usage(DIGITAL_SIGNATURE) || allowed_usage(KEY_AGREEMENT)) && allowed_extended_usage("PKIX.ClientAuth"); case Usage_Type::OCSP_RESPONDER: - return allowed_usage(Key_Constraints(DIGITAL_SIGNATURE | NON_REPUDIATION)) && allowed_extended_usage("PKIX.OCSPSigning"); + return (allowed_usage(DIGITAL_SIGNATURE) || allowed_usage(NON_REPUDIATION)) && allowed_extended_usage("PKIX.OCSPSigning"); case Usage_Type::CERTIFICATE_AUTHORITY: return is_CA_cert(); diff --git a/src/lib/math/bigint/big_ops2.cpp b/src/lib/math/bigint/big_ops2.cpp index 9a3408247..6e234f036 100644 --- a/src/lib/math/bigint/big_ops2.cpp +++ b/src/lib/math/bigint/big_ops2.cpp @@ -1,6 +1,7 @@ /* * BigInt Assignment Operators * (C) 1999-2007 Jack Lloyd +* 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -118,10 +119,7 @@ BigInt& BigInt::operator*=(const BigInt& y) secure_vector<word> z(data(), data() + x_sw); secure_vector<word> workspace(size()); - - bigint_mul(mutable_data(), size(), workspace.data(), - z.data(), z.size(), x_sw, - y.data(), y.size(), y_sw); + bigint_mul(*this, BigInt(*this), y, workspace.data()); } return (*this); diff --git a/src/lib/math/bigint/big_ops3.cpp b/src/lib/math/bigint/big_ops3.cpp index 6cf837020..24927b4fc 100644 --- a/src/lib/math/bigint/big_ops3.cpp +++ b/src/lib/math/bigint/big_ops3.cpp @@ -1,6 +1,7 @@ /* * BigInt Binary Operators * (C) 1999-2007 Jack Lloyd +* 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -93,9 +94,7 @@ BigInt operator*(const BigInt& x, const BigInt& y) else if(x_sw && y_sw) { secure_vector<word> workspace(z.size()); - bigint_mul(z.mutable_data(), z.size(), workspace.data(), - x.data(), x.size(), x_sw, - y.data(), y.size(), y_sw); + bigint_mul(z, x, y, workspace.data()); } if(x_sw && y_sw && x.sign() != y.sign()) diff --git a/src/lib/math/ec_gfp/curve_gfp.cpp b/src/lib/math/ec_gfp/curve_gfp.cpp index 9bf2191c6..96593e601 100644 --- a/src/lib/math/ec_gfp/curve_gfp.cpp +++ b/src/lib/math/ec_gfp/curve_gfp.cpp @@ -1,6 +1,7 @@ /* * Elliptic curves over GF(p) Montgomery Representation * (C) 2014,2015 Jack Lloyd +* 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -80,20 +81,14 @@ void CurveGFp_Montgomery::curve_mul(BigInt& z, const BigInt& x, const BigInt& y, return; } - const size_t x_sw = x.sig_words(); - const size_t y_sw = y.sig_words(); - const size_t output_size = 2*m_p_words + 1; ws.resize(2*(m_p_words+2)); z.grow_to(output_size); z.clear(); - bigint_monty_mul(z.mutable_data(), output_size, - x.data(), x.size(), x_sw, - y.data(), y.size(), y_sw, - m_p.data(), m_p_words, m_p_dash, - ws.data()); + bigint_monty_mul(z, x, y, m_p.data(), m_p_words, m_p_dash, ws.data()); + } void CurveGFp_Montgomery::curve_sqr(BigInt& z, const BigInt& x, @@ -115,9 +110,7 @@ void CurveGFp_Montgomery::curve_sqr(BigInt& z, const BigInt& x, z.grow_to(output_size); z.clear(); - bigint_monty_sqr(z.mutable_data(), output_size, - x.data(), x.size(), x_sw, - m_p.data(), m_p_words, m_p_dash, + bigint_monty_sqr(z, x, m_p.data(), m_p_words, m_p_dash, ws.data()); } @@ -174,9 +167,7 @@ void CurveGFp_NIST::curve_mul(BigInt& z, const BigInt& x, const BigInt& y, z.grow_to(output_size); z.clear(); - bigint_mul(z.mutable_data(), output_size, ws.data(), - x.data(), x.size(), x.sig_words(), - y.data(), y.size(), y.sig_words()); + bigint_mul(z, x, y, ws.data()); this->redc(z, ws); } diff --git a/src/lib/math/mp/mp_core.h b/src/lib/math/mp/mp_core.h index 73f13742c..c4ce005ba 100644 --- a/src/lib/math/mp/mp_core.h +++ b/src/lib/math/mp/mp_core.h @@ -2,6 +2,7 @@ * MPI Algorithms * (C) 1999-2010 Jack Lloyd * 2006 Luca Piccarreta +* 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -9,6 +10,7 @@ #ifndef BOTAN_MP_CORE_OPS_H__ #define BOTAN_MP_CORE_OPS_H__ +#include <botan/bigint.h> #include <botan/mp_types.h> namespace Botan { @@ -134,17 +136,14 @@ void bigint_monty_redc(word z[], /* * Montgomery Multiplication */ -void bigint_monty_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, +void bigint_monty_mul(BigInt& z, const BigInt& x, const BigInt& y, const word p[], size_t p_size, word p_dash, word workspace[]); /* * Montgomery Squaring */ -void bigint_monty_sqr(word z[], size_t z_size, - const word x[], size_t x_size, size_t x_sw, +void bigint_monty_sqr(BigInt& z, const BigInt& x, const word p[], size_t p_size, word p_dash, word workspace[]); @@ -182,9 +181,7 @@ void bigint_comba_sqr16(word out[32], const word in[16]); /* * High Level Multiplication/Squaring Interfaces */ -void bigint_mul(word z[], size_t z_size, word workspace[], - const word x[], size_t x_size, size_t x_sw, - const word y[], size_t y_size, size_t y_sw); +void bigint_mul(BigInt& z, const BigInt& x, const BigInt& y, word workspace[]); void bigint_sqr(word z[], size_t z_size, word workspace[], const word x[], size_t x_size, size_t x_sw); diff --git a/src/lib/math/mp/mp_karat.cpp b/src/lib/math/mp/mp_karat.cpp index 9135fdd6a..7a763e2a9 100644 --- a/src/lib/math/mp/mp_karat.cpp +++ b/src/lib/math/mp/mp_karat.cpp @@ -1,6 +1,7 @@ /* * Multiplication and Squaring * (C) 1999-2010 Jack Lloyd +* 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -252,60 +253,55 @@ size_t karatsuba_size(size_t z_size, size_t x_size, size_t x_sw) /* * Multiplication Algorithm Dispatcher */ -void bigint_mul(word z[], size_t z_size, word workspace[], - const word x[], size_t x_size, size_t x_sw, - const word y[], size_t y_size, size_t y_sw) +void bigint_mul(BigInt& z, const BigInt& x, const BigInt& y, word workspace[]) { - // checking that z_size >= x_sw + y_sw without overflow - BOTAN_ASSERT(z_size > x_sw && z_size > y_sw && z_size-x_sw >= y_sw, "Output size is sufficient"); - - if(x_sw == 1) + if(x.sig_words() == 1) { - bigint_linmul3(z, y, y_sw, x[0]); + bigint_linmul3(z.mutable_data(), y.data(), y.sig_words(), x.data()[0]); } - else if(y_sw == 1) + else if(y.sig_words() == 1) { - bigint_linmul3(z, x, x_sw, y[0]); + bigint_linmul3(z.mutable_data(), x.data(), x.sig_words(), y.data()[0]); } - else if(x_sw <= 4 && x_size >= 4 && - y_sw <= 4 && y_size >= 4 && z_size >= 8) + else if(x.sig_words() <= 4 && x.size() >= 4 && + y.sig_words() <= 4 && y.size() >= 4 && z.size() >= 8) { - bigint_comba_mul4(z, x, y); + bigint_comba_mul4(z.mutable_data(), x.data(), y.data()); } - else if(x_sw <= 6 && x_size >= 6 && - y_sw <= 6 && y_size >= 6 && z_size >= 12) + else if(x.sig_words() <= 6 && x.size() >= 6 && + y.sig_words() <= 6 && y.size() >= 6 && z.size() >= 12) { - bigint_comba_mul6(z, x, y); + bigint_comba_mul6(z.mutable_data(), x.data(), y.data()); } - else if(x_sw <= 8 && x_size >= 8 && - y_sw <= 8 && y_size >= 8 && z_size >= 16) + else if(x.sig_words() <= 8 && x.size() >= 8 && + y.sig_words() <= 8 && y.size() >= 8 && z.size() >= 16) { - bigint_comba_mul8(z, x, y); + bigint_comba_mul8(z.mutable_data(), x.data(), y.data()); } - else if(x_sw <= 9 && x_size >= 9 && - y_sw <= 9 && y_size >= 9 && z_size >= 18) + else if(x.sig_words() <= 9 && x.size() >= 9 && + y.sig_words() <= 9 && y.size() >= 9 && z.size() >= 18) { - bigint_comba_mul9(z, x, y); + bigint_comba_mul9(z.mutable_data(), x.data(), y.data()); } - else if(x_sw <= 16 && x_size >= 16 && - y_sw <= 16 && y_size >= 16 && z_size >= 32) + else if(x.sig_words() <= 16 && x.size() >= 16 && + y.sig_words() <= 16 && y.size() >= 16 && z.size() >= 32) { - bigint_comba_mul16(z, x, y); + bigint_comba_mul16(z.mutable_data(), x.data(), y.data()); } - else if(x_sw < KARATSUBA_MULTIPLY_THRESHOLD || - y_sw < KARATSUBA_MULTIPLY_THRESHOLD || + else if(x.sig_words() < KARATSUBA_MULTIPLY_THRESHOLD || + y.sig_words() < KARATSUBA_MULTIPLY_THRESHOLD || !workspace) { - basecase_mul(z, x, x_sw, y, y_sw); + basecase_mul(z.mutable_data(), x.data(), x.sig_words(), y.data(), y.sig_words()); } else { - const size_t N = karatsuba_size(z_size, x_size, x_sw, y_size, y_sw); + const size_t N = karatsuba_size(z.size(), x.size(), x.sig_words(), y.size(), y.sig_words()); if(N) - karatsuba_mul(z, x, y, N, workspace); + karatsuba_mul(z.mutable_data(), x.data(), y.data(), N, workspace); else - basecase_mul(z, x, x_sw, y, y_sw); + basecase_mul(z.mutable_data(), x.data(), x.sig_words(), y.data(), y.sig_words()); } } diff --git a/src/lib/math/mp/mp_monty.cpp b/src/lib/math/mp/mp_monty.cpp index 7e427b540..88b5de715 100644 --- a/src/lib/math/mp/mp_monty.cpp +++ b/src/lib/math/mp/mp_monty.cpp @@ -2,10 +2,12 @@ * 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/bigint.h> #include <botan/internal/mp_core.h> #include <botan/internal/mp_madd.h> #include <botan/internal/mp_asmi.h> @@ -92,30 +94,25 @@ void bigint_monty_redc(word z[], BOTAN_ASSERT(borrow == 0 || borrow == 1, "Expected borrow"); } -void bigint_monty_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, +void bigint_monty_mul(BigInt& z, const BigInt& x, const BigInt& y, const word p[], size_t p_size, word p_dash, word ws[]) { - bigint_mul(&z[0], z_size, &ws[0], - &x[0], x_size, x_sw, - &y[0], y_size, y_sw); + bigint_mul(z, x, y, &ws[0]); - bigint_monty_redc(&z[0], + bigint_monty_redc(z.mutable_data(), &p[0], p_size, p_dash, &ws[0]); + } -void bigint_monty_sqr(word z[], size_t z_size, - const word x[], size_t x_size, size_t x_sw, - const word p[], size_t p_size, word p_dash, - word ws[]) +void bigint_monty_sqr(BigInt& z, const BigInt& x, const word p[], + size_t p_size, word p_dash, word ws[]) { - bigint_sqr(&z[0], z_size, &ws[0], - &x[0], x_size, x_sw); + bigint_sqr(z.mutable_data(), z.size(), &ws[0], + x.data(), x.size(), x.sig_words()); - bigint_monty_redc(&z[0], + bigint_monty_redc(z.mutable_data(), &p[0], p_size, p_dash, &ws[0]); } diff --git a/src/lib/math/numbertheory/mp_numth.cpp b/src/lib/math/numbertheory/mp_numth.cpp index 3373b9ee7..d78d21128 100644 --- a/src/lib/math/numbertheory/mp_numth.cpp +++ b/src/lib/math/numbertheory/mp_numth.cpp @@ -1,6 +1,7 @@ /* * Fused and Important MP Algorithms * (C) 1999-2007 Jack Lloyd +* 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -40,20 +41,13 @@ BigInt mul_add(const BigInt& a, const BigInt& b, const BigInt& c) 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.size() + b.size(), c_sw) + 1); + BigInt r(sign, std::max(a.size() + b.size(), c.sig_words()) + 1); secure_vector<word> workspace(r.size()); - bigint_mul(r.mutable_data(), r.size(), - workspace.data(), - a.data(), a.size(), a_sw, - b.data(), b.size(), b_sw); + bigint_mul(r, a, b, workspace.data()); - const size_t r_size = std::max(r.sig_words(), c_sw); - bigint_add2(r.mutable_data(), r_size, c.data(), c_sw); + const size_t r_size = std::max(r.sig_words(), c.sig_words()); + bigint_add2(r.mutable_data(), r_size, c.data(), c.sig_words()); return r; } diff --git a/src/lib/math/numbertheory/powm_mnt.cpp b/src/lib/math/numbertheory/powm_mnt.cpp index 5c441db3a..572f0de98 100644 --- a/src/lib/math/numbertheory/powm_mnt.cpp +++ b/src/lib/math/numbertheory/powm_mnt.cpp @@ -1,6 +1,7 @@ /* * Montgomery Exponentiation * (C) 1999-2010,2012 Jack Lloyd +* 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -8,6 +9,7 @@ #include <botan/internal/def_powm.h> #include <botan/numthry.h> #include <botan/internal/mp_core.h> +#include <iostream> namespace Botan { @@ -34,36 +36,26 @@ void Montgomery_Exponentiator::set_base(const BigInt& base) m_g[0] = 1; - bigint_monty_mul(z.mutable_data(), z.size(), - m_g[0].data(), m_g[0].size(), m_g[0].sig_words(), - m_R2_mod.data(), m_R2_mod.size(), m_R2_mod.sig_words(), + bigint_monty_mul(z, m_g[0], m_R2_mod, m_modulus.data(), m_mod_words, m_mod_prime, workspace.data()); - m_g[0] = z; m_g[1] = (base >= m_modulus) ? (base % m_modulus) : base; - bigint_monty_mul(z.mutable_data(), z.size(), - m_g[1].data(), m_g[1].size(), m_g[1].sig_words(), - m_R2_mod.data(), m_R2_mod.size(), m_R2_mod.sig_words(), + bigint_monty_mul(z, m_g[1], m_R2_mod, m_modulus.data(), m_mod_words, m_mod_prime, workspace.data()); m_g[1] = z; const BigInt& x = m_g[1]; - const size_t x_sig = x.sig_words(); for(size_t i = 2; i != m_g.size(); ++i) { const BigInt& y = m_g[i-1]; - const size_t y_sig = y.sig_words(); - bigint_monty_mul(z.mutable_data(), z.size(), - x.data(), x.size(), x_sig, - y.data(), y.size(), y_sig, - m_modulus.data(), m_mod_words, m_mod_prime, + bigint_monty_mul(z, x, y, m_modulus.data(), m_mod_words, m_mod_prime, workspace.data()); m_g[i] = z; @@ -82,15 +74,13 @@ BigInt Montgomery_Exponentiator::execute() const const size_t z_size = 2*(m_mod_words + 1); BigInt z(BigInt::Positive, z_size); - secure_vector<word> workspace(z_size); + secure_vector<word> workspace(z.size()); for(size_t i = exp_nibbles; i > 0; --i) { for(size_t k = 0; k != m_window_bits; ++k) { - bigint_monty_sqr(z.mutable_data(), z_size, - x.data(), x.size(), x.sig_words(), - m_modulus.data(), m_mod_words, m_mod_prime, + bigint_monty_sqr(z, x, m_modulus.data(), m_mod_words, m_mod_prime, workspace.data()); x = z; @@ -100,9 +90,7 @@ BigInt Montgomery_Exponentiator::execute() const const BigInt& y = m_g[nibble]; - bigint_monty_mul(z.mutable_data(), z_size, - x.data(), x.size(), x.sig_words(), - y.data(), y.size(), y.sig_words(), + bigint_monty_mul(z, x, y, m_modulus.data(), m_mod_words, m_mod_prime, workspace.data()); diff --git a/src/lib/pubkey/curve25519/donna.cpp b/src/lib/pubkey/curve25519/donna.cpp index 9b28e412c..a0e4d249f 100644 --- a/src/lib/pubkey/curve25519/donna.cpp +++ b/src/lib/pubkey/curve25519/donna.cpp @@ -39,6 +39,26 @@ typedef byte u8; typedef u64bit limb; typedef limb felem[5]; +typedef struct + { + limb* x; + limb* z; + } fmonty_pair_t; + +typedef struct + { + fmonty_pair_t q; + fmonty_pair_t q_dash; + const limb* q_minus_q_dash; + } fmonty_in_t; + +typedef struct + { + fmonty_pair_t two_q; + fmonty_pair_t q_plus_q_dash; + } fmonty_out_t; + + #if !defined(BOTAN_TARGET_HAS_NATIVE_UINT128) typedef donna128 uint128_t; #endif @@ -273,44 +293,41 @@ fcontract(u8 *output, const felem input) { /* Input: Q, Q', Q-Q' * Output: 2Q, Q+Q' * - * x2 z3: long form - * x3 z3: long form - * x z: short form, destroyed - * xprime zprime: short form, destroyed - * qmqp: short form, preserved + * 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 */ static void -fmonty(limb *x2, limb *z2, /* output 2Q */ - limb *x3, limb *z3, /* output Q + Q' */ - limb *x, limb *z, /* input Q */ - limb *xprime, limb *zprime, /* input Q' */ - const limb *qmqp /* input Q - Q' */) { +fmonty(fmonty_out_t& result, fmonty_in_t& in) +{ limb origx[5], origxprime[5], zzz[5], xx[5], zz[5], xxprime[5], - zzprime[5], zzzprime[5]; + zzprime[5], zzzprime[5]; - copy_mem(origx, x, 5); - fsum(x, z); - fdifference_backwards(z, origx); // does x - z + copy_mem(origx, in.q.x, 5); + fsum(in.q.x, in.q.z); + fdifference_backwards(in.q.z, origx); // does x - z - copy_mem(origxprime, xprime, 5); - fsum(xprime, zprime); - fdifference_backwards(zprime, origxprime); - fmul(xxprime, xprime, z); - fmul(zzprime, x, zprime); + copy_mem(origxprime, in.q_dash.x, 5); + fsum(in.q_dash.x, in.q_dash.z); + fdifference_backwards(in.q_dash.z, origxprime); + fmul(xxprime, in.q_dash.x, in.q.z); + fmul(zzprime, in.q.x, in.q_dash.z); copy_mem(origxprime, xxprime, 5); fsum(xxprime, zzprime); fdifference_backwards(zzprime, origxprime); - fsquare_times(x3, xxprime, 1); + fsquare_times(result.q_plus_q_dash.x, xxprime, 1); fsquare_times(zzzprime, zzprime, 1); - fmul(z3, zzzprime, qmqp); + fmul(result.q_plus_q_dash.z, zzzprime, in.q_minus_q_dash); - fsquare_times(xx, x, 1); - fsquare_times(zz, z, 1); - fmul(x2, xx, zz); + fsquare_times(xx, in.q.x, 1); + fsquare_times(zz, in.q.z, 1); + fmul(result.two_q.x, xx, zz); fdifference_backwards(zz, xx); // does zz = xx - zz fscalar_product(zzz, zz, 121665); fsum(zzz, xx); - fmul(z2, zz, zzz); + fmul(result.two_q.z, zz, zzz); } // ----------------------------------------------------------------------------- @@ -356,11 +373,10 @@ cmult(limb *resultx, limb *resultz, const u8 *n, const limb *q) { swap_conditional(nqx, nqpqx, bit); swap_conditional(nqz, nqpqz, bit); - fmonty(nqx2, nqz2, - nqpqx2, nqpqz2, - nqx, nqz, - nqpqx, nqpqz, - q); + + fmonty_out_t result { nqx2, nqz2, nqpqx2, nqpqz2 }; + fmonty_in_t in { nqx, nqz, nqpqx, nqpqz, q }; + fmonty(result, in); swap_conditional(nqx2, nqpqx2, bit); swap_conditional(nqz2, nqpqz2, bit); diff --git a/src/lib/pubkey/pubkey.cpp b/src/lib/pubkey/pubkey.cpp index c0485fec8..8b24ee983 100644 --- a/src/lib/pubkey/pubkey.cpp +++ b/src/lib/pubkey/pubkey.cpp @@ -54,7 +54,7 @@ PK_Decryptor::decrypt_or_random(const byte in[], { const secure_vector<byte> fake_pms = rng.random_vec(expected_pt_len); - CT::poison(in, length); + //CT::poison(in, length); byte valid_mask = 0; secure_vector<byte> decoded = do_decrypt(valid_mask, in, length); @@ -90,8 +90,8 @@ PK_Decryptor::decrypt_or_random(const byte in[], /*from1*/fake_pms.data(), expected_pt_len); - CT::unpoison(in, length); - CT::unpoison(decoded.data(), decoded.size()); + //CT::unpoison(in, length); + //CT::unpoison(decoded.data(), decoded.size()); return decoded; } diff --git a/src/lib/rng/hmac_rng/hmac_rng.cpp b/src/lib/rng/hmac_rng/hmac_rng.cpp index d66c538ab..081d8b38a 100644 --- a/src/lib/rng/hmac_rng/hmac_rng.cpp +++ b/src/lib/rng/hmac_rng/hmac_rng.cpp @@ -16,7 +16,7 @@ HMAC_RNG::HMAC_RNG(std::unique_ptr<MessageAuthenticationCode> prf, RandomNumberGenerator& underlying_rng, Entropy_Sources& entropy_sources, size_t reseed_interval) : - Stateful_RNG(underlying_rng, reseed_interval), + Stateful_RNG(underlying_rng, entropy_sources, reseed_interval), m_prf(std::move(prf)) { BOTAN_ASSERT_NONNULL(m_prf); diff --git a/src/lib/tls/info.txt b/src/lib/tls/info.txt index b26179226..667726318 100644 --- a/src/lib/tls/info.txt +++ b/src/lib/tls/info.txt @@ -6,6 +6,7 @@ load_on auto credentials_manager.h tls_alert.h tls_blocking.h +tls_callbacks.h tls_channel.h tls_ciphersuite.h tls_client.h diff --git a/src/lib/tls/msg_cert_verify.cpp b/src/lib/tls/msg_cert_verify.cpp index 2598255eb..6b59e703f 100644 --- a/src/lib/tls/msg_cert_verify.cpp +++ b/src/lib/tls/msg_cert_verify.cpp @@ -82,6 +82,8 @@ bool Certificate_Verify::verify(const X509_Certificate& cert, { 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_hash_algo, m_sig_algo, true, policy); diff --git a/src/lib/tls/msg_certificate.cpp b/src/lib/tls/msg_certificate.cpp index 32e3e17f0..10ee7c95f 100644 --- a/src/lib/tls/msg_certificate.cpp +++ b/src/lib/tls/msg_certificate.cpp @@ -31,7 +31,7 @@ Certificate::Certificate(Handshake_IO& io, /** * Deserialize a Certificate message */ -Certificate::Certificate(const std::vector<byte>& buf) +Certificate::Certificate(const std::vector<byte>& buf, const Policy& /*policy_currently_unused*/) { if(buf.size() < 3) throw Decoding_Error("Certificate: Message malformed"); diff --git a/src/lib/tls/msg_client_hello.cpp b/src/lib/tls/msg_client_hello.cpp index 23807215f..69f9a5e11 100644 --- a/src/lib/tls/msg_client_hello.cpp +++ b/src/lib/tls/msg_client_hello.cpp @@ -1,6 +1,7 @@ /* * TLS Hello Request and Client Hello Messages * (C) 2004-2011,2015,2016 Jack Lloyd +* 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -68,22 +69,20 @@ std::vector<byte> Hello_Request::serialize() const */ Client_Hello::Client_Hello(Handshake_IO& io, Handshake_Hash& hash, - Protocol_Version version, const Policy& policy, RandomNumberGenerator& rng, const std::vector<byte>& reneg_info, - const std::vector<std::string>& next_protocols, - const std::string& hostname, - const std::string& srp_identifier) : - m_version(version), + 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, (srp_identifier != ""))), + m_suites(policy.ciphersuite_list(m_version, + client_settings.srp_identifier() != "")), m_comp_methods(policy.compression()) { m_extensions.add(new Extended_Master_Secret); m_extensions.add(new Renegotiation_Extension(reneg_info)); - - m_extensions.add(new Server_Name_Indicator(hostname)); + m_extensions.add(new Server_Name_Indicator(client_settings.hostname())); m_extensions.add(new Session_Ticket()); m_extensions.add(new Supported_Elliptic_Curves(policy.allowed_ecc_curves())); @@ -96,20 +95,23 @@ Client_Hello::Client_Hello(Handshake_IO& io, if(reneg_info.empty() && !next_protocols.empty()) m_extensions.add(new Application_Layer_Protocol_Notification(next_protocols)); + + if(policy.negotiate_encrypt_then_mac()) + m_extensions.add(new Encrypt_then_MAC); #if defined(BOTAN_HAS_SRP6) - m_extensions.add(new SRP_Identifier(srp_identifier)); + m_extensions.add(new SRP_Identifier(client_settings.srp_identifier())); #else - if(!srp_identifier.empty()) + if(!client_settings.srp_identifier().empty()) { throw Invalid_State("Attempting to initiate SRP session but TLS-SRP support disabled"); } #endif - BOTAN_ASSERT(policy.acceptable_protocol_version(version), + BOTAN_ASSERT(policy.acceptable_protocol_version(client_settings.protocol_version()), "Our policy accepts the version we are offering"); - if(policy.send_fallback_scsv(version)) + if(policy.send_fallback_scsv(client_settings.protocol_version())) m_suites.push_back(TLS_FALLBACK_SCSV); hash.update(io.send(*this)); @@ -155,6 +157,9 @@ Client_Hello::Client_Hello(Handshake_IO& io, if(reneg_info.empty() && !next_protocols.empty()) m_extensions.add(new Application_Layer_Protocol_Notification(next_protocols)); + + if(policy.negotiate_encrypt_then_mac()) + m_extensions.add(new Encrypt_then_MAC); #if defined(BOTAN_HAS_SRP6) m_extensions.add(new SRP_Identifier(session.srp_identifier())); diff --git a/src/lib/tls/msg_client_kex.cpp b/src/lib/tls/msg_client_kex.cpp index 77e9795f4..bc4d33d52 100644 --- a/src/lib/tls/msg_client_kex.cpp +++ b/src/lib/tls/msg_client_kex.cpp @@ -92,13 +92,6 @@ Client_Key_Exchange::Client_Key_Exchange(Handshake_IO& io, if(reader.remaining_bytes()) throw Decoding_Error("Bad params size for DH key exchange"); - if(p.bits() < policy.minimum_dh_group_size()) - throw TLS_Exception(Alert::INSUFFICIENT_SECURITY, - "Server sent DH group of " + - std::to_string(p.bits()) + - " bits, policy requires at least " + - std::to_string(policy.minimum_dh_group_size())); - /* * 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 @@ -117,6 +110,8 @@ Client_Key_Exchange::Client_Key_Exchange(Handshake_IO& io, DH_PublicKey counterparty_key(group, Y); + policy.check_peer_key_acceptable(counterparty_key); + DH_PrivateKey priv_key(rng, group); PK_Key_Agreement ka(priv_key, "Raw"); @@ -160,6 +155,8 @@ Client_Key_Exchange::Client_Key_Exchange(Handshake_IO& io, ECDH_PublicKey counterparty_key(group, OS2ECP(ecdh_key, group.get_curve())); + policy.check_peer_key_acceptable(counterparty_key); + ECDH_PrivateKey priv_key(rng, group); PK_Key_Agreement ka(priv_key, "Raw"); diff --git a/src/lib/tls/msg_server_hello.cpp b/src/lib/tls/msg_server_hello.cpp index f8d0c63c7..ebe8fb085 100644 --- a/src/lib/tls/msg_server_hello.cpp +++ b/src/lib/tls/msg_server_hello.cpp @@ -1,6 +1,7 @@ /* * TLS Server Hello and Server Hello Done * (C) 2004-2011,2015,2016 Jack Lloyd +* 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -23,25 +24,28 @@ Server_Hello::Server_Hello(Handshake_IO& io, RandomNumberGenerator& rng, const std::vector<byte>& reneg_info, const Client_Hello& client_hello, - const std::vector<byte>& new_session_id, - Protocol_Version new_session_version, - u16bit ciphersuite, - byte compression, - bool offer_session_ticket, - const std::string& next_protocol) : - m_version(new_session_version), - m_session_id(new_session_id), + 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_hello_random(rng, policy)), - m_ciphersuite(ciphersuite), - m_comp_method(compression) + m_ciphersuite(server_settings.ciphersuite()), + m_comp_method(server_settings.compression()) { 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 = Ciphersuite::by_id(m_ciphersuite); + if(c.cbc_ciphersuite()) + m_extensions.add(new Encrypt_then_MAC); + } + if(client_hello.secure_renegotiation()) m_extensions.add(new Renegotiation_Extension(reneg_info)); - if(client_hello.supports_session_ticket() && offer_session_ticket) + 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()) @@ -90,6 +94,13 @@ Server_Hello::Server_Hello(Handshake_IO& io, 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(client_hello.secure_renegotiation()) m_extensions.add(new Renegotiation_Extension(reneg_info)); diff --git a/src/lib/tls/msg_server_kex.cpp b/src/lib/tls/msg_server_kex.cpp index 98e3ad1f0..10581fe45 100644 --- a/src/lib/tls/msg_server_kex.cpp +++ b/src/lib/tls/msg_server_kex.cpp @@ -236,6 +236,8 @@ 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_hash_algo, m_sig_algo, false, policy); diff --git a/src/lib/tls/tls_blocking.cpp b/src/lib/tls/tls_blocking.cpp index a1867b6b5..9408972fd 100644 --- a/src/lib/tls/tls_blocking.cpp +++ b/src/lib/tls/tls_blocking.cpp @@ -1,6 +1,7 @@ /* * TLS Blocking API * (C) 2013 Jack Lloyd +* 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -23,10 +24,13 @@ Blocking_Client::Blocking_Client(read_fn reader, const Protocol_Version& offer_version, const std::vector<std::string>& next) : m_read(reader), - m_channel(writer, - std::bind(&Blocking_Client::data_cb, this, _1, _2), - std::bind(&Blocking_Client::alert_cb, this, _1, _2, _3), - std::bind(&Blocking_Client::handshake_cb, this, _1), + m_callbacks(new TLS::Compat_Callbacks( + writer, + std::bind(&Blocking_Client::data_cb, this, _1, _2), + std::function<void (Alert)>(std::bind(&Blocking_Client::alert_cb, this, _1)), + std::bind(&Blocking_Client::handshake_cb, this, _1) + )), + m_channel(*m_callbacks.get(), session_manager, creds, policy, @@ -42,7 +46,7 @@ bool Blocking_Client::handshake_cb(const Session& session) return this->handshake_complete(session); } -void Blocking_Client::alert_cb(const Alert& alert, const byte[], size_t) +void Blocking_Client::alert_cb(const Alert& alert) { this->alert_notification(alert); } diff --git a/src/lib/tls/tls_blocking.h b/src/lib/tls/tls_blocking.h index 00e65cbaf..0f2986710 100644 --- a/src/lib/tls/tls_blocking.h +++ b/src/lib/tls/tls_blocking.h @@ -1,6 +1,7 @@ /* * TLS Blocking API * (C) 2013 Jack Lloyd +* 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -32,6 +33,7 @@ class BOTAN_DLL Blocking_Client typedef std::function<size_t (byte[], size_t)> read_fn; typedef std::function<void (const byte[], size_t)> write_fn; + BOTAN_DEPRECATED("Use the regular TLS::Client interface") Blocking_Client(read_fn reader, write_fn writer, Session_Manager& session_manager, @@ -89,9 +91,10 @@ class BOTAN_DLL Blocking_Client void data_cb(const byte data[], size_t data_len); - void alert_cb(const Alert& alert, const byte 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<byte> m_plaintext; }; diff --git a/src/lib/tls/tls_callbacks.h b/src/lib/tls/tls_callbacks.h new file mode 100644 index 000000000..75887c23f --- /dev/null +++ b/src/lib/tls/tls_callbacks.h @@ -0,0 +1,203 @@ +/* +* TLS Callbacks +* (C) 2016 Matthias Gierlings +* 2016 Jack Lloyd +* +* 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> +namespace Botan { + +namespace TLS { + +class Handshake_Message; + +/** +* Encapsulates the callbacks that a TLS channel will make which are due to +* channel specific operations. +*/ +class BOTAN_DLL Callbacks + { + public: + virtual ~Callbacks(); + + /** + * 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(u64bit seq_no, const uint8_t data[], size_t size) = 0; + + /** + * Mandary 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: 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: debug logging. (not currently used) + */ + virtual bool tls_log_debug(const char*) { return false; } + }; + +/** +* 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_DLL Compat_Callbacks final : public Callbacks + { + public: + typedef std::function<void (const byte[], size_t)> output_fn; + typedef std::function<void (const byte[], size_t)> data_cb; + typedef std::function<void (Alert, const byte[], 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 output_fn is called with data for the outbound socket + * + * @param app_data_cb is called when new application data is received + * + * @param alert_cb is called when a TLS alert is received + * + * @param handshake_cb is called when a handshake is completed + */ + BOTAN_DEPRECATED("Use TLS::Callbacks (virtual interface).") + Compat_Callbacks(output_fn out, data_cb app_data_cb, alert_cb alert_cb, + handshake_cb hs_cb, handshake_msg_cb hs_msg_cb = nullptr, + next_protocol_fn next_proto = nullptr) + : m_output_function(out), m_app_data_cb(app_data_cb), + m_alert_cb(std::bind(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 out, data_cb app_data_cb, + std::function<void (Alert)> alert_cb, + handshake_cb hs_cb, + handshake_msg_cb hs_msg_cb = nullptr, + next_protocol_fn next_proto = nullptr) + : m_output_function(out), m_app_data_cb(app_data_cb), + m_alert_cb(alert_cb), + m_hs_cb(hs_cb), m_hs_msg_cb(hs_msg_cb), m_next_proto(next_proto) {} + + void tls_emit_data(const byte 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(u64bit /*seq_no*/, const byte 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/src/lib/tls/tls_channel.cpp b/src/lib/tls/tls_channel.cpp index f445eef99..5e9207da7 100644 --- a/src/lib/tls/tls_channel.cpp +++ b/src/lib/tls/tls_channel.cpp @@ -1,6 +1,7 @@ /* * TLS Channels -* (C) 2011,2012,2014,2015 Jack Lloyd +* (C) 2011,2012,2014,2015,2016 Jack Lloyd +* 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -18,32 +19,63 @@ namespace Botan { namespace TLS { -Channel::Channel(output_fn output_fn, - data_cb data_cb, - alert_cb alert_cb, - handshake_cb handshake_cb, - handshake_msg_cb handshake_msg_cb, +Callbacks::~Callbacks() {} + +void Callbacks::tls_inspect_handshake_msg(const Handshake_Message&) + { + // default is no op + } + +std::string Callbacks::tls_server_choose_app_protocol(const std::vector<std::string>&) + { + return ""; + } + +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_datagram, size_t reserved_io_buffer_size) : m_is_datagram(is_datagram), - m_data_cb(data_cb), - m_alert_cb(alert_cb), - m_output_fn(output_fn), - m_handshake_cb(handshake_cb), - m_handshake_msg_cb(handshake_msg_cb), + m_callbacks(callbacks), m_session_manager(session_manager), m_policy(policy), m_rng(rng) { + init(reserved_io_buffer_size); + } + +Channel::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_datagram, + size_t io_buf_sz) : + m_is_datagram(is_datagram), + m_compat_callbacks(new Compat_Callbacks(out, app_data_cb, alert_cb, hs_cb, hs_msg_cb)), + m_callbacks(*m_compat_callbacks.get()), + m_session_manager(session_manager), + m_policy(policy), + m_rng(rng) + { + 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(reserved_io_buffer_size); - m_readbuf.reserve(reserved_io_buffer_size); + m_writebuf.reserve(io_buf_sz); + m_readbuf.reserve(io_buf_sz); } void Channel::reset_state() @@ -183,7 +215,8 @@ void Channel::change_cipher_spec_reader(Connection_Side side) (side == CLIENT) ? SERVER : CLIENT, false, pending->ciphersuite(), - pending->session_keys())); + pending->session_keys(), + pending->server_hello()->supports_encrypt_then_mac())); m_read_cipher_states[epoch] = read_state; } @@ -210,7 +243,8 @@ void Channel::change_cipher_spec_writer(Connection_Side side) side, true, pending->ciphersuite(), - pending->session_keys())); + pending->session_keys(), + pending->server_hello()->supports_encrypt_then_mac())); m_write_cipher_states[epoch] = write_state; } @@ -263,23 +297,19 @@ size_t Channel::received_data(const byte input[], size_t input_size) { while(!is_closed() && input_size) { - secure_vector<byte> record; + secure_vector<byte> record_data; u64bit record_sequence = 0; Record_Type record_type = NO_RECORD; Protocol_Version record_version; size_t consumed = 0; + Record_Raw_Input raw_input(input, input_size, consumed, m_is_datagram); + Record record(record_data, &record_sequence, &record_version, &record_type); const size_t needed = read_record(m_readbuf, - input, - input_size, - m_is_datagram, - consumed, + raw_input, record, - &record_sequence, - &record_version, - &record_type, m_sequence_numbers.get(), std::bind(&TLS::Channel::read_cipher_state_epoch, this, std::placeholders::_1)); @@ -298,105 +328,21 @@ size_t Channel::received_data(const byte input[], size_t input_size) if(input_size == 0 && needed != 0) return needed; // need more data to complete record - if(record.size() > MAX_PLAINTEXT_SIZE) + if(record_data.size() > MAX_PLAINTEXT_SIZE) throw TLS_Exception(Alert::RECORD_OVERFLOW, "TLS plaintext record is larger than allowed maximum"); if(record_type == HANDSHAKE || record_type == CHANGE_CIPHER_SPEC) { - if(!m_pending_state) - { - // No pending handshake, possibly new: - if(record_version.is_datagram_protocol()) - { - 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 u16bit 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(unlock(record), - record_type, - record_sequence); - } - } - else if(record_sequence == 0) - { - 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(unlock(record), - 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); - } - } + process_handshake_ccs(record_data, record_sequence, record_type, record_version); } else if(record_type == APPLICATION_DATA) { - if(!active_state()) - throw Unexpected_Message("Application data before handshake done"); - - /* - * OpenSSL among others sends empty records in versions - * before TLS v1.1 in order to randomize the IV of the - * following record. Avoid spurious callbacks. - */ - if(record.size() > 0) - m_data_cb(record.data(), record.size()); + process_application_data(record_sequence, record_data); } else if(record_type == ALERT) { - Alert alert_msg(record); - - if(alert_msg.type() == Alert::NO_RENEGOTIATION) - m_pending_state.reset(); - - m_alert_cb(alert_msg, nullptr, 0); - - 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()) - { - reset_state(); - return 0; - } + process_alert(record_data); } else if(record_type != NO_RECORD) throw Unexpected_Message("Unexpected record type " + @@ -428,6 +374,108 @@ size_t Channel::received_data(const byte input[], size_t input_size) } } +void Channel::process_handshake_ccs(const secure_vector<byte>& record, + u64bit record_sequence, + Record_Type record_type, + Protocol_Version record_version) + { + if(!m_pending_state) + { + // No pending handshake, possibly new: + if(record_version.is_datagram_protocol()) + { + 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 u16bit 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(unlock(record), + record_type, + record_sequence); + } + } + else if(record_sequence == 0) + { + 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(unlock(record), + 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); + } + } + } + +void Channel::process_application_data(u64bit seq_no, const secure_vector<byte>& record) + { + if(!active_state()) + throw Unexpected_Message("Application data before handshake done"); + + /* + * OpenSSL among others sends empty records in versions + * before TLS v1.1 in order to randomize the IV of the + * following record. Avoid spurious callbacks. + */ + if(record.size() > 0) + callbacks().tls_record_received(seq_no, record.data(), record.size()); + } + +void Channel::process_alert(const secure_vector<byte>& 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()) + { + reset_state(); + } + } + + void Channel::write_record(Connection_Cipher_State* cipher_state, u16bit epoch, byte record_type, const byte input[], size_t length) { @@ -436,16 +484,16 @@ void Channel::write_record(Connection_Cipher_State* cipher_state, u16bit epoch, Protocol_Version record_version = (m_pending_state) ? (m_pending_state->version()) : (m_active_state->version()); + Record_Message record_message(record_type, 0, input, length); + TLS::write_record(m_writebuf, - record_type, - input, - length, + record_message, record_version, sequence_numbers().next_write_sequence(epoch), cipher_state, m_rng); - m_output_fn(m_writebuf.data(), m_writebuf.size()); + callbacks().tls_emit_data(m_writebuf.data(), m_writebuf.size()); } void Channel::send_record_array(u16bit epoch, byte type, const byte input[], size_t length) diff --git a/src/lib/tls/tls_channel.h b/src/lib/tls/tls_channel.h index e0219c242..073af760f 100644 --- a/src/lib/tls/tls_channel.h +++ b/src/lib/tls/tls_channel.h @@ -1,6 +1,7 @@ /* * TLS Channel * (C) 2011,2012,2014,2015 Jack Lloyd +* 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -12,6 +13,7 @@ #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 <vector> #include <string> @@ -37,7 +39,20 @@ class BOTAN_DLL Channel typedef std::function<void (Alert, const byte[], 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; + Channel(Callbacks& callbacks, + Session_Manager& session_manager, + RandomNumberGenerator& rng, + const Policy& policy, + 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. + */ + BOTAN_DEPRECATED("Use TLS::Channel(TLS::Callbacks ...)") Channel(output_fn out, data_cb app_data_cb, alert_cb alert_cb, @@ -47,7 +62,7 @@ class BOTAN_DLL Channel RandomNumberGenerator& rng, const Policy& policy, bool is_datagram, - size_t io_buf_sz = 16*1024); + size_t io_buf_sz = IO_BUF_DEFAULT_SIZE); Channel(const Channel&) = delete; @@ -200,10 +215,12 @@ class BOTAN_DLL Channel const Policy& policy() const { return m_policy; } - bool save_session(const Session& session) const { return m_handshake_cb(session); } + bool save_session(const Session& session) const { return callbacks().tls_session_established(session); } - handshake_msg_cb get_handshake_msg_cb() const { return m_handshake_msg_cb; } + Callbacks& callbacks() const { return m_callbacks; } private: + void init(size_t io_buf_sze); + void send_record(byte record_type, const std::vector<byte>& record); void send_record_under_epoch(u16bit epoch, byte record_type, @@ -227,14 +244,21 @@ class BOTAN_DLL Channel 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<byte>& record, + u64bit record_sequence, + Record_Type record_type, + Protocol_Version record_version); + + void process_application_data(u64bit req_no, const secure_vector<byte>& record); + + void process_alert(const secure_vector<byte>& record); + bool m_is_datagram; /* callbacks */ - data_cb m_data_cb; - alert_cb m_alert_cb; - output_fn m_output_fn; - handshake_cb m_handshake_cb; - handshake_msg_cb m_handshake_msg_cb; + std::unique_ptr<Compat_Callbacks> m_compat_callbacks; + Callbacks& m_callbacks; /* external state */ Session_Manager& m_session_manager; diff --git a/src/lib/tls/tls_ciphersuite.cpp b/src/lib/tls/tls_ciphersuite.cpp index dc0c71278..9a52e0e0e 100644 --- a/src/lib/tls/tls_ciphersuite.cpp +++ b/src/lib/tls/tls_ciphersuite.cpp @@ -35,6 +35,13 @@ bool Ciphersuite::ecc_ciphersuite() const return (sig_algo() == "ECDSA" || kex_algo() == "ECDH" || kex_algo() == "ECDHE_PSK"); } +bool Ciphersuite::cbc_ciphersuite() const + { + return (cipher_algo() == "3DES" || cipher_algo() == "SEED" || + cipher_algo() == "AES-128" || cipher_algo() == "AES-256" || + cipher_algo() == "Camellia-128" || cipher_algo() == "Camellia-256"); + } + Ciphersuite Ciphersuite::by_id(u16bit suite) { const std::vector<Ciphersuite>& all_suites = all_known_ciphersuites(); diff --git a/src/lib/tls/tls_ciphersuite.h b/src/lib/tls/tls_ciphersuite.h index 199e126b1..6708e3ca6 100644 --- a/src/lib/tls/tls_ciphersuite.h +++ b/src/lib/tls/tls_ciphersuite.h @@ -63,6 +63,11 @@ class BOTAN_DLL Ciphersuite bool ecc_ciphersuite() const; /** + * @return true if this suite uses a CBC cipher + */ + bool cbc_ciphersuite() const; + + /** * @return key exchange algorithm used by this ciphersuite */ std::string kex_algo() const { return m_kex_algo; } diff --git a/src/lib/tls/tls_client.cpp b/src/lib/tls/tls_client.cpp index 301c77c6b..0e72b9a28 100644 --- a/src/lib/tls/tls_client.cpp +++ b/src/lib/tls/tls_client.cpp @@ -1,6 +1,7 @@ /* * TLS Client * (C) 2004-2011,2012,2015,2016 Jack Lloyd +* 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -23,7 +24,7 @@ class Client_Handshake_State : public Handshake_State public: // using Handshake_State::Handshake_State; - Client_Handshake_State(Handshake_IO* io, handshake_msg_cb cb) : Handshake_State(io, cb) {} + Client_Handshake_State(Handshake_IO* io, Callbacks& cb) : Handshake_State(io, cb) {} const Public_Key& get_server_public_Key() const { @@ -42,6 +43,23 @@ class Client_Handshake_State : public Handshake_State /* * 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, offer_version.is_datagram_protocol(), + io_buf_sz), + m_creds(creds), + m_info(info) + { + init(offer_version, next_protos); + } + Client::Client(output_fn output_fn, data_cb proc_cb, alert_cb alert_cb, @@ -59,10 +77,7 @@ Client::Client(output_fn output_fn, m_creds(creds), m_info(info) { - const std::string srp_identifier = m_creds.srp_identifier("tls-client", m_info.hostname()); - - Handshake_State& state = create_handshake_state(offer_version); - send_client_hello(state, false, offer_version, srp_identifier, next_protos); + init(offer_version, next_protos); } Client::Client(output_fn output_fn, @@ -82,15 +97,22 @@ Client::Client(output_fn output_fn, 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(offer_version); - send_client_hello(state, false, offer_version, srp_identifier, next_protos); + 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, get_handshake_msg_cb()); + return new Client_Handshake_State(io, callbacks()); } std::vector<X509_Certificate> @@ -145,16 +167,15 @@ void Client::send_client_hello(Handshake_State& state_base, 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(), - version, policy(), rng(), secure_renegotiation_data_for_client_hello(), - next_protocols, - m_info.hostname(), - srp_identifier)); + client_settings, + next_protocols)); } secure_renegotiation_check(state.client_hello()); @@ -352,7 +373,7 @@ void Client::process_handshake_msg(const Handshake_State* active_state, state.set_expected_next(SERVER_HELLO_DONE); } - state.server_certs(new Certificate(contents)); + state.server_certs(new Certificate(contents, policy())); const std::vector<X509_Certificate>& server_certs = state.server_certs()->cert_chain(); @@ -419,11 +440,9 @@ void Client::process_handshake_msg(const Handshake_State* active_state, "tls-client", m_info.hostname()); - state.client_certs( - new Certificate(state.handshake_io(), - state.hash(), - client_certs) - ); + state.client_certs(new Certificate(state.handshake_io(), + state.hash(), + client_certs)); } state.client_kex( @@ -510,6 +529,7 @@ void Client::process_handshake_msg(const Handshake_State* active_state, state.server_hello()->compression_method(), CLIENT, state.server_hello()->supports_extended_master_secret(), + state.server_hello()->supports_encrypt_then_mac(), get_peer_cert_chain(state), session_ticket, m_info, diff --git a/src/lib/tls/tls_client.h b/src/lib/tls/tls_client.h index 45a741878..09af053af 100644 --- a/src/lib/tls/tls_client.h +++ b/src/lib/tls/tls_client.h @@ -1,6 +1,7 @@ /* * TLS Client * (C) 2004-2011 Jack Lloyd +* 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -22,9 +23,49 @@ namespace TLS { class BOTAN_DLL 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. + * + * Set up a new TLS client session + * * @param output_fn is called with data for the outbound socket * * @param app_data_cb is called when new application data is received @@ -52,7 +93,7 @@ class BOTAN_DLL Client final : public Channel * 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 out, data_cb app_data_cb, alert_cb alert_cb, @@ -64,9 +105,14 @@ class BOTAN_DLL Client final : public Channel 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 = 16*1024 + 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, @@ -83,6 +129,9 @@ class BOTAN_DLL Client final : public Channel const std::string& application_protocol() const { 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; diff --git a/src/lib/tls/tls_extensions.cpp b/src/lib/tls/tls_extensions.cpp index 3ea97203c..3dceb505a 100644 --- a/src/lib/tls/tls_extensions.cpp +++ b/src/lib/tls/tls_extensions.cpp @@ -1,6 +1,7 @@ /* * TLS Extensions * (C) 2011,2012,2015,2016 Jack Lloyd +* 2016 Juraj Somorovsky * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -47,6 +48,9 @@ Extension* make_extension(TLS_Data_Reader& reader, 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); @@ -519,6 +523,18 @@ std::vector<byte> Extended_Master_Secret::serialize() const return std::vector<byte>(); } +Encrypt_then_MAC::Encrypt_then_MAC(TLS_Data_Reader&, + u16bit extension_size) + { + if(extension_size != 0) + throw Decoding_Error("Invalid encrypt_then_mac extension"); + } + +std::vector<byte> Encrypt_then_MAC::serialize() const + { + return std::vector<byte>(); + } + } } diff --git a/src/lib/tls/tls_extensions.h b/src/lib/tls/tls_extensions.h index a5aac0020..dc69eec36 100644 --- a/src/lib/tls/tls_extensions.h +++ b/src/lib/tls/tls_extensions.h @@ -1,6 +1,8 @@ /* * TLS Extensions * (C) 2011,2012,2016 Jack Lloyd +* 2016 Juraj Somorovsky +* 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -34,9 +36,9 @@ enum Handshake_Extension_Type { TLSEXT_SRP_IDENTIFIER = 12, TLSEXT_SIGNATURE_ALGORITHMS = 13, TLSEXT_USE_SRTP = 14, - TLSEXT_HEARTBEAT_SUPPORT = 15, TLSEXT_ALPN = 16, + TLSEXT_ENCRYPT_THEN_MAC = 22, TLSEXT_EXTENDED_MASTER_SECRET = 23, TLSEXT_SESSION_TICKET = 35, @@ -341,6 +343,26 @@ class Extended_Master_Secret final : public Extension }; /** +* Encrypt-then-MAC Extension (RFC 7366) +*/ +class 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<byte> serialize() const override; + + bool empty() const override { return false; } + + Encrypt_then_MAC() {} + + Encrypt_then_MAC(TLS_Data_Reader& reader, u16bit extension_size); + }; + +/** * Represents a block of extensions in a hello message */ class Extensions diff --git a/src/lib/tls/tls_handshake_msg.h b/src/lib/tls/tls_handshake_msg.h index 7e527abf4..618ae8d76 100644 --- a/src/lib/tls/tls_handshake_msg.h +++ b/src/lib/tls/tls_handshake_msg.h @@ -1,6 +1,7 @@ /* * TLS Handshake Message * (C) 2012 Jack Lloyd +* 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -16,6 +17,9 @@ namespace Botan { namespace TLS { +class Handshake_IO; +class Handshake_Hash; + /** * TLS Handshake Message Base Class */ diff --git a/src/lib/tls/tls_handshake_state.cpp b/src/lib/tls/tls_handshake_state.cpp index afc32ba87..71cacdabd 100644 --- a/src/lib/tls/tls_handshake_state.cpp +++ b/src/lib/tls/tls_handshake_state.cpp @@ -8,6 +8,7 @@ #include <botan/internal/tls_handshake_state.h> #include <botan/internal/tls_messages.h> #include <botan/internal/tls_record.h> +#include <botan/tls_callbacks.h> namespace Botan { @@ -174,8 +175,8 @@ std::string handshake_mask_to_string(u32bit mask) /* * Initialize the SSL/TLS Handshake State */ -Handshake_State::Handshake_State(Handshake_IO* io, handshake_msg_cb cb) : - m_msg_callback(cb), +Handshake_State::Handshake_State(Handshake_IO* io, Callbacks& cb) : + m_callbacks(cb), m_handshake_io(io), m_version(m_handshake_io->initial_record_version()) { @@ -183,6 +184,11 @@ Handshake_State::Handshake_State(Handshake_IO* io, handshake_msg_cb cb) : Handshake_State::~Handshake_State() {} +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); diff --git a/src/lib/tls/tls_handshake_state.h b/src/lib/tls/tls_handshake_state.h index 2943a8637..bdec10d14 100644 --- a/src/lib/tls/tls_handshake_state.h +++ b/src/lib/tls/tls_handshake_state.h @@ -24,6 +24,7 @@ class KDF; namespace TLS { +class Callbacks; class Policy; class Hello_Verify_Request; @@ -45,9 +46,7 @@ class Finished; class Handshake_State { public: - typedef std::function<void (const Handshake_Message&)> handshake_msg_cb; - - Handshake_State(Handshake_IO* io, handshake_msg_cb cb); + Handshake_State(Handshake_IO* io, Callbacks& callbacks); virtual ~Handshake_State(); @@ -164,15 +163,10 @@ class Handshake_State const Handshake_Hash& hash() const { return m_handshake_hash; } - void note_message(const Handshake_Message& msg) - { - if(m_msg_callback) - m_msg_callback(msg); - } - + void note_message(const Handshake_Message& msg); private: - handshake_msg_cb m_msg_callback; + Callbacks& m_callbacks; std::unique_ptr<Handshake_IO> m_handshake_io; diff --git a/src/lib/tls/tls_messages.h b/src/lib/tls/tls_messages.h index 3bee89e13..8ccb2fbff 100644 --- a/src/lib/tls/tls_messages.h +++ b/src/lib/tls/tls_messages.h @@ -1,6 +1,7 @@ /* * TLS Messages * (C) 2004-2011,2015 Jack Lloyd +* 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -61,6 +62,26 @@ class Hello_Verify_Request final : public Handshake_Message class Client_Hello final : public Handshake_Message { public: + class Settings + { + 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; } @@ -141,6 +162,11 @@ class Client_Hello final : public Handshake_Message return m_extensions.has<Extended_Master_Secret>(); } + bool supports_encrypt_then_mac() const + { + return m_extensions.has<Encrypt_then_MAC>(); + } + std::vector<std::string> next_protocols() const { if(auto alpn = m_extensions.get<Application_Layer_Protocol_Notification>()) @@ -162,13 +188,11 @@ class Client_Hello final : public Handshake_Message Client_Hello(Handshake_IO& io, Handshake_Hash& hash, - Protocol_Version version, const Policy& policy, RandomNumberGenerator& rng, const std::vector<byte>& reneg_info, - const std::vector<std::string>& next_protocols, - const std::string& hostname = "", - const std::string& srp_identifier = ""); + const Client_Hello::Settings& client_settings, + const std::vector<std::string>& next_protocols); Client_Hello(Handshake_IO& io, Handshake_Hash& hash, @@ -199,6 +223,35 @@ class Client_Hello final : public Handshake_Message class Server_Hello final : public Handshake_Message { public: + class Settings + { + public: + Settings(const std::vector<byte> new_session_id, + Protocol_Version new_session_version, + u16bit ciphersuite, + byte compression, + bool offer_session_ticket) + : m_new_session_id(new_session_id), + m_new_session_version(new_session_version), + m_ciphersuite(ciphersuite), + m_compression(compression), + m_offer_session_ticket(offer_session_ticket) {}; + + const std::vector<byte>& session_id() const { return m_new_session_id; }; + Protocol_Version protocol_version() const { return m_new_session_version; }; + u16bit ciphersuite() const { return m_ciphersuite; }; + byte compression() const { return m_compression; } + bool offer_session_ticket() const { return m_offer_session_ticket; } + + private: + const std::vector<byte> m_new_session_id; + Protocol_Version m_new_session_version; + u16bit m_ciphersuite; + byte m_compression; + bool m_offer_session_ticket; + }; + + Handshake_Type type() const override { return SERVER_HELLO; } Protocol_Version version() const { return m_version; } @@ -228,6 +281,11 @@ class Server_Hello final : public Handshake_Message return m_extensions.has<Extended_Master_Secret>(); } + bool supports_encrypt_then_mac() const + { + return m_extensions.has<Encrypt_then_MAC>(); + } + bool supports_session_ticket() const { return m_extensions.has<Session_Ticket>(); @@ -262,12 +320,8 @@ class Server_Hello final : public Handshake_Message RandomNumberGenerator& rng, const std::vector<byte>& secure_reneg_info, const Client_Hello& client_hello, - const std::vector<byte>& new_session_id, - Protocol_Version new_session_version, - u16bit ciphersuite, - byte compression, - bool offer_session_ticket, - const std::string& next_protocol); + const Server_Hello::Settings& settings, + const std::string next_protocol); Server_Hello(Handshake_IO& io, Handshake_Hash& hash, @@ -341,7 +395,7 @@ class Certificate final : public Handshake_Message Handshake_Hash& hash, const std::vector<X509_Certificate>& certs); - explicit Certificate(const std::vector<byte>& buf); + explicit Certificate(const std::vector<byte>& buf, const Policy &policy); private: std::vector<byte> serialize() const override; diff --git a/src/lib/tls/tls_policy.cpp b/src/lib/tls/tls_policy.cpp index 10b193215..592d4f572 100644 --- a/src/lib/tls/tls_policy.cpp +++ b/src/lib/tls/tls_policy.cpp @@ -1,6 +1,7 @@ /* * Policies for TLS * (C) 2004-2010,2012,2015,2016 Jack Lloyd +* 2016 Christian Mainka * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -119,14 +120,75 @@ std::string Policy::choose_curve(const std::vector<std::string>& curve_names) co std::string Policy::dh_group() const { + // We offer 2048 bit DH because we can return "modp/ietf/2048"; } size_t Policy::minimum_dh_group_size() const { + // Many servers still send 1024 bit return 1024; } +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 + { + // P-256 is smallest curve currently supplrted for TLS key exchange (after 1.11.29) + return 256; + } + +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; + } + +void Policy::check_peer_key_acceptable(const Public_Key& public_key) const + { + const std::string algo_name = public_key.algo_name(); + + // FIXME this is not really the right way to do this + size_t keylength = public_key.max_input_bits(); + size_t expected_keylength = 0; + + if(algo_name == "RSA") + { + expected_keylength = minimum_rsa_bits(); + keylength += 1; // fixup for use of max_input_bits above + } + else if(algo_name == "DH") + { + expected_keylength = minimum_dh_group_size(); + } + else if(algo_name == "ECDH") + { + 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)); + } + /* * Return allowed compression algorithms */ @@ -147,10 +209,17 @@ bool Policy::send_fallback_scsv(Protocol_Version version) const bool Policy::acceptable_protocol_version(Protocol_Version version) const { - if(version.is_datagram_protocol()) - return (version >= Protocol_Version::DTLS_V12); - else - return (version >= Protocol_Version::TLS_V10); + // Uses boolean optimization: + // First check the current version (left part), then if it is allowed + // (right part) + // checks are ordered according to their probability + return ( + ( ( version == Protocol_Version::TLS_V12) && allow_tls12() ) || + ( ( version == Protocol_Version::TLS_V10) && allow_tls10() ) || + ( ( version == Protocol_Version::TLS_V11) && allow_tls11() ) || + ( ( version == Protocol_Version::DTLS_V12) && allow_dtls12() ) || + ( ( version == Protocol_Version::DTLS_V10) && allow_dtls10() ) + ); } Protocol_Version Policy::latest_supported_version(bool datagram) const @@ -168,9 +237,15 @@ bool Policy::acceptable_ciphersuite(const Ciphersuite&) const bool Policy::allow_server_initiated_renegotiation() const { return false; } bool Policy::allow_insecure_renegotiation() const { return false; } +bool Policy::allow_tls10() const { return true; } +bool Policy::allow_tls11() const { return true; } +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; } // 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; } @@ -339,6 +414,11 @@ void print_bool(std::ostream& o, 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()); @@ -351,9 +431,12 @@ void Policy::print(std::ostream& o) const 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()); o << "session_ticket_lifetime = " << session_ticket_lifetime() << '\n'; o << "dh_group = " << dh_group() << '\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'; } std::vector<std::string> Strict_Policy::allowed_ciphers() const @@ -376,13 +459,11 @@ std::vector<std::string> Strict_Policy::allowed_key_exchange_methods() const return { "ECDH" }; } -bool Strict_Policy::acceptable_protocol_version(Protocol_Version version) const - { - if(version.is_datagram_protocol()) - return (version >= Protocol_Version::DTLS_V12); - else - return (version >= Protocol_Version::TLS_V12); - } +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/src/lib/tls/tls_policy.h b/src/lib/tls/tls_policy.h index 999ba2887..76e80ddde 100644 --- a/src/lib/tls/tls_policy.h +++ b/src/lib/tls/tls_policy.h @@ -101,15 +101,79 @@ class BOTAN_DLL Policy * Allow servers to initiate a new handshake */ virtual bool allow_server_initiated_renegotiation() 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 std::string 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 256, allowing P-256 and larger + * P-256 is the smallest curve we will negotiate + */ + 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; /** + * 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 @@ -168,6 +232,12 @@ class BOTAN_DLL Policy 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; + + /** * Return allowed ciphersuites, in order of preference */ virtual std::vector<u16bit> ciphersuite_list(Protocol_Version version, @@ -207,9 +277,12 @@ class BOTAN_DLL NSA_Suite_B_128 : public Policy std::vector<std::string> allowed_ecc_curves() const override { return std::vector<std::string>({"secp256r1"}); } - - bool acceptable_protocol_version(Protocol_Version version) const override - { return version == Protocol_Version::TLS_V12; } + + 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; } }; /** @@ -220,9 +293,12 @@ class BOTAN_DLL Datagram_Policy : public Policy public: std::vector<std::string> allowed_macs() const override { return std::vector<std::string>({"AEAD"}); } - - bool acceptable_protocol_version(Protocol_Version version) const override - { return version == Protocol_Version::DTLS_V12; } + + 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; } }; /* @@ -243,7 +319,11 @@ class BOTAN_DLL Strict_Policy : public Policy std::vector<std::string> allowed_key_exchange_methods() const override; - bool acceptable_protocol_version(Protocol_Version version) 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_DLL Text_Policy : public Policy @@ -267,6 +347,21 @@ class BOTAN_DLL Text_Policy : public Policy std::vector<std::string> allowed_ecc_curves() const override { return get_list("ecc_curves", Policy::allowed_ecc_curves()); } + + bool allow_tls10() const override + { return get_bool("allow_tls10", Policy::allow_tls10()); } + + bool allow_tls11() const override + { return get_bool("allow_tls11", Policy::allow_tls11()); } + + bool allow_tls12() const override + { return get_bool("allow_tls12", Policy::allow_tls12()); } + + bool allow_dtls10() const override + { return get_bool("allow_dtls10", Policy::allow_dtls10()); } + + bool allow_dtls12() const override + { return get_bool("allow_dtls12", Policy::allow_dtls12()); } bool allow_insecure_renegotiation() const override { return get_bool("allow_insecure_renegotiation", Policy::allow_insecure_renegotiation()); } @@ -280,12 +375,24 @@ class BOTAN_DLL Text_Policy : public Policy bool server_uses_own_ciphersuite_preferences() const override { return get_bool("server_uses_own_ciphersuite_preferences", Policy::server_uses_own_ciphersuite_preferences()); } + bool negotiate_encrypt_then_mac() const override + { return get_bool("negotiate_encrypt_then_mac", Policy::negotiate_encrypt_then_mac()); } + std::string dh_group() const override { return get_str("dh_group", Policy::dh_group()); } + size_t minimum_ecdh_group_size() const override + { return get_len("minimum_ecdh_group_size", Policy::minimum_ecdh_group_size()); } + + size_t minimum_ecdsa_group_size() const override + { return get_len("minimum_ecdsa_group_size", Policy::minimum_ecdsa_group_size()); } + size_t minimum_dh_group_size() const override { return get_len("minimum_dh_group_size", Policy::minimum_dh_group_size()); } + size_t minimum_rsa_bits() const override + { return get_len("minimum_rsa_bits", Policy::minimum_rsa_bits()); } + bool hide_unknown_users() const override { return get_bool("hide_unknown_users", Policy::hide_unknown_users()); } diff --git a/src/lib/tls/tls_record.cpp b/src/lib/tls/tls_record.cpp index eacf313a8..877b81b41 100644 --- a/src/lib/tls/tls_record.cpp +++ b/src/lib/tls/tls_record.cpp @@ -1,6 +1,8 @@ /* * TLS Record Handling -* (C) 2012,2013,2014,2015 Jack Lloyd +* (C) 2012,2013,2014,2015,2016 Jack Lloyd +* 2016 Juraj Somorovsky +* 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -23,10 +25,12 @@ Connection_Cipher_State::Connection_Cipher_State(Protocol_Version version, Connection_Side side, bool our_side, const Ciphersuite& suite, - const Session_Keys& keys) : + const Session_Keys& keys, + bool uses_encrypt_then_mac) : m_start_time(std::chrono::system_clock::now()), m_nonce_bytes_from_handshake(suite.nonce_bytes_from_handshake()), - m_nonce_bytes_from_record(suite.nonce_bytes_from_record()) + m_nonce_bytes_from_record(suite.nonce_bytes_from_record()), + m_uses_encrypt_then_mac(uses_encrypt_then_mac) { SymmetricKey mac_key, cipher_key; InitializationVector iv; @@ -152,7 +156,7 @@ Connection_Cipher_State::format_ad(u64bit msg_sequence, } void write_record(secure_vector<byte>& output, - byte msg_type, const byte msg[], size_t msg_length, + Record_Message msg, Protocol_Version version, u64bit seq, Connection_Cipher_State* cs, @@ -160,7 +164,7 @@ void write_record(secure_vector<byte>& output, { output.clear(); - output.push_back(msg_type); + output.push_back(msg.get_type()); output.push_back(version.major_version()); output.push_back(version.minor_version()); @@ -172,17 +176,17 @@ void write_record(secure_vector<byte>& output, if(!cs) // initial unencrypted handshake records { - output.push_back(get_byte(0, static_cast<u16bit>(msg_length))); - output.push_back(get_byte(1, static_cast<u16bit>(msg_length))); + output.push_back(get_byte<u16bit>(0, static_cast<u16bit>(msg.get_size()))); + output.push_back(get_byte<u16bit>(1, static_cast<u16bit>(msg.get_size()))); - output.insert(output.end(), msg, msg + msg_length); + output.insert(output.end(), msg.get_data(), msg.get_data() + msg.get_size()); return; } if(AEAD_Mode* aead = cs->aead()) { - const size_t ctext_size = aead->output_length(msg_length); + const size_t ctext_size = aead->output_length(msg.get_size()); const std::vector<byte> nonce = cs->aead_nonce(seq); @@ -193,17 +197,16 @@ void write_record(secure_vector<byte>& output, output.push_back(get_byte(0, static_cast<u16bit>(rec_size))); output.push_back(get_byte(1, static_cast<u16bit>(rec_size))); - aead->set_ad(cs->format_ad(seq, msg_type, version, static_cast<u16bit>(msg_length))); + aead->set_ad(cs->format_ad(seq, msg.get_type(), version, static_cast<u16bit>(msg.get_size()))); if(cs->nonce_bytes_from_record() > 0) { output += std::make_pair(&nonce[cs->nonce_bytes_from_handshake()], cs->nonce_bytes_from_record()); } - BOTAN_ASSERT(aead->start(nonce).empty(), "AEAD doesn't return anything from start"); const size_t offset = output.size(); - output += std::make_pair(msg, msg_length); + output += std::make_pair(msg.get_data(), msg.get_size()); aead->finish(output, offset); BOTAN_ASSERT(output.size() == offset + ctext_size, "Expected size"); @@ -213,77 +216,151 @@ void write_record(secure_vector<byte>& output, return; } - cs->mac()->update(cs->format_ad(seq, msg_type, version, static_cast<u16bit>(msg_length))); - - cs->mac()->update(msg, msg_length); - const size_t block_size = cs->block_size(); const size_t iv_size = cs->iv_size(); const size_t mac_size = cs->mac_size(); - const size_t buf_size = round_up( - iv_size + msg_length + mac_size + (block_size ? 1 : 0), - block_size); + if(!cs->uses_encrypt_then_mac()) + { + cs->mac()->update(cs->format_ad(seq, msg.get_type(), version, static_cast<u16bit>(msg.get_size()))); + cs->mac()->update(msg.get_data(), msg.get_size()); - if(buf_size > MAX_CIPHERTEXT_SIZE) - throw Internal_Error("Output record is larger than allowed by protocol"); + const size_t buf_size = round_up( + iv_size + msg.get_size() + mac_size + (block_size ? 1 : 0), + block_size); - output.push_back(get_byte(0, static_cast<u16bit>(buf_size))); - output.push_back(get_byte(1, static_cast<u16bit>(buf_size))); + if(buf_size > MAX_CIPHERTEXT_SIZE) + throw Internal_Error("Output record is larger than allowed by protocol"); - const size_t header_size = output.size(); + output.push_back(get_byte(0, static_cast<u16bit>(buf_size))); + output.push_back(get_byte(1, static_cast<u16bit>(buf_size))); - if(iv_size) - { - output.resize(output.size() + iv_size); - rng.randomize(&output[output.size() - iv_size], iv_size); - } + const size_t header_size = output.size(); - output.insert(output.end(), msg, msg + msg_length); + if(iv_size) + { + output.resize(output.size() + iv_size); + rng.randomize(&output[output.size() - iv_size], iv_size); + } - output.resize(output.size() + mac_size); - cs->mac()->final(&output[output.size() - mac_size]); + output.insert(output.end(), msg.get_data(), msg.get_data() + msg.get_size()); - if(block_size) - { - const size_t pad_val = - buf_size - (iv_size + msg_length + mac_size + 1); + output.resize(output.size() + mac_size); + cs->mac()->final(&output[output.size() - mac_size]); - for(size_t i = 0; i != pad_val + 1; ++i) - output.push_back(static_cast<byte>(pad_val)); - } + if(block_size) + { + const size_t pad_val = + buf_size - (iv_size + msg.get_size() + mac_size + 1); - if(buf_size > MAX_CIPHERTEXT_SIZE) - throw Internal_Error("Produced ciphertext larger than protocol allows"); + for(size_t i = 0; i != pad_val + 1; ++i) + output.push_back(static_cast<byte>(pad_val)); + } + + if(buf_size > MAX_CIPHERTEXT_SIZE) + throw Internal_Error("Produced ciphertext larger than protocol allows"); - BOTAN_ASSERT_EQUAL(buf_size + header_size, output.size(), + BOTAN_ASSERT_EQUAL(buf_size + header_size, output.size(), "Output buffer is sized properly"); - if(BlockCipher* bc = cs->block_cipher()) - { - secure_vector<byte>& cbc_state = cs->cbc_state(); + if(BlockCipher* bc = cs->block_cipher()) + { + secure_vector<byte>& cbc_state = cs->cbc_state(); - BOTAN_ASSERT(buf_size % block_size == 0, + BOTAN_ASSERT(buf_size % block_size == 0, "Buffer is an even multiple of block size"); - byte* buf = &output[header_size]; + byte* buf = &output[header_size]; + + const size_t blocks = buf_size / block_size; - const size_t blocks = buf_size / block_size; + xor_buf(buf, cbc_state.data(), block_size); + bc->encrypt(buf); - xor_buf(buf, cbc_state.data(), block_size); - bc->encrypt(buf); + for(size_t i = 1; i < blocks; ++i) + { + xor_buf(&buf[block_size*i], &buf[block_size*(i-1)], block_size); + bc->encrypt(&buf[block_size*i]); + } - for(size_t i = 1; i < blocks; ++i) + cbc_state.assign(&buf[block_size*(blocks-1)], + &buf[block_size*blocks]); + } + else { - xor_buf(&buf[block_size*i], &buf[block_size*(i-1)], block_size); - bc->encrypt(&buf[block_size*i]); + throw Internal_Error("NULL cipher not supported"); } + } + else + { + const size_t enc_size = round_up( + iv_size + msg.get_size() + (block_size ? 1 : 0), + block_size); + + const size_t buf_size = enc_size + mac_size; + + if(buf_size > MAX_CIPHERTEXT_SIZE) + throw Internal_Error("Output record is larger than allowed by protocol"); + + output.push_back(get_byte<u16bit>(0, buf_size)); + output.push_back(get_byte<u16bit>(1, buf_size)); + + const size_t header_size = output.size(); - cbc_state.assign(&buf[block_size*(blocks-1)], + if(iv_size) + { + output.resize(output.size() + iv_size); + rng.randomize(&output[output.size() - iv_size], iv_size); + } + + output.insert(output.end(), msg.get_data(), msg.get_data() + msg.get_size()); + + if(block_size) + { + const size_t pad_val = + enc_size - (iv_size + msg.get_size() + 1); + + for(size_t i = 0; i != pad_val + 1; ++i) + output.push_back(pad_val); + } + + if(BlockCipher* bc = cs->block_cipher()) + { + secure_vector<byte>& cbc_state = cs->cbc_state(); + + BOTAN_ASSERT( enc_size % block_size == 0, + "Buffer is an even multiple of block size"); + + byte* buf = &output[header_size]; + + const size_t blocks = enc_size / block_size; + + xor_buf(buf, cbc_state.data(), block_size); + bc->encrypt(buf); + + for(size_t i = 1; i < blocks; ++i) + { + xor_buf(&buf[block_size*i], &buf[block_size*(i-1)], block_size); + bc->encrypt(&buf[block_size*i]); + } + + cbc_state.assign(&buf[block_size*(blocks-1)], &buf[block_size*blocks]); + + cs->mac()->update(cs->format_ad(seq, msg.get_type(), version, enc_size)); + cs->mac()->update(buf, enc_size); + + output.resize(output.size() + mac_size); + cs->mac()->final(&output[output.size() - mac_size]); + + BOTAN_ASSERT_EQUAL(buf_size + header_size, output.size(), + "Output buffer is sized properly"); + } + else + { + throw Internal_Error("NULL cipher not supported"); + } } - else - throw Internal_Error("NULL cipher not supported"); } namespace { @@ -409,117 +486,147 @@ void decrypt_record(secure_vector<byte>& output, const size_t mac_size = cs.mac_size(); const size_t iv_size = cs.iv_size(); - // This early exit does not leak info because all the values are public - if((record_len < mac_size + iv_size) || (record_len % cs.block_size() != 0)) - throw Decoding_Error("Record sent with invalid length"); + if(!cs.uses_encrypt_then_mac()) + { + // This early exit does not leak info because all the values are public + if((record_len < mac_size + iv_size) || (record_len % cs.block_size() != 0)) + throw TLS_Exception(Alert::BAD_RECORD_MAC, "Message authentication failure"); - CT::poison(record_contents, record_len); + CT::poison(record_contents, record_len); - cbc_decrypt_record(record_contents, record_len, cs, *bc); + cbc_decrypt_record(record_contents, record_len, cs, *bc); - // 0 if padding was invalid, otherwise 1 + padding_bytes - u16bit pad_size = tls_padding_check(record_contents, record_len); + // 0 if padding was invalid, otherwise 1 + padding_bytes + u16bit pad_size = tls_padding_check(record_contents, record_len); - // This mask is zero if there is not enough room in the packet - const u16bit size_ok_mask = CT::is_lte<u16bit>(static_cast<u16bit>(mac_size + pad_size + iv_size), static_cast<u16bit>(record_len)); - pad_size &= size_ok_mask; + // 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 the BEAST countermeasure (thus record_len+1). + const u16bit size_ok_mask = CT::is_lte<u16bit>(static_cast<u16bit>(mac_size + pad_size + iv_size), static_cast<u16bit>(record_len + 1)); + pad_size &= size_ok_mask; - CT::unpoison(record_contents, record_len); + 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); + /* + 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 byte* plaintext_block = &record_contents[iv_size]; - const u16bit plaintext_length = static_cast<u16bit>(record_len - mac_size - iv_size - pad_size); + const byte* plaintext_block = &record_contents[iv_size]; + const u16bit plaintext_length = static_cast<u16bit>(record_len - mac_size - iv_size - pad_size); - cs.mac()->update(cs.format_ad(record_sequence, record_type, record_version, plaintext_length)); - cs.mac()->update(plaintext_block, plaintext_length); + cs.mac()->update(cs.format_ad(record_sequence, record_type, record_version, plaintext_length)); + cs.mac()->update(plaintext_block, plaintext_length); - std::vector<byte> mac_buf(mac_size); - cs.mac()->final(mac_buf.data()); + std::vector<byte> mac_buf(mac_size); + cs.mac()->final(mac_buf.data()); - const size_t mac_offset = record_len - (mac_size + pad_size); + const size_t mac_offset = record_len - (mac_size + pad_size); - const bool mac_ok = same_mem(&record_contents[mac_offset], mac_buf.data(), mac_size); + const bool mac_ok = same_mem(&record_contents[mac_offset], mac_buf.data(), mac_size); - const u16bit ok_mask = size_ok_mask & CT::expand_mask<u16bit>(mac_ok) & CT::expand_mask<u16bit>(pad_size); + const u16bit ok_mask = size_ok_mask & CT::expand_mask<u16bit>(mac_ok) & CT::expand_mask<u16bit>(pad_size); - CT::unpoison(ok_mask); + CT::unpoison(ok_mask); - if(ok_mask) - { - output.assign(plaintext_block, plaintext_block + plaintext_length); + if(ok_mask) + { + output.assign(plaintext_block, plaintext_block + plaintext_length); + } + else + { + throw TLS_Exception(Alert::BAD_RECORD_MAC, "Message authentication failure"); + } } else { - throw TLS_Exception(Alert::BAD_RECORD_MAC, "Message authentication failure"); + const size_t enc_size = record_len - mac_size; + // This early exit does not leak info because all the values are public + if((record_len < mac_size + iv_size) || ( enc_size % cs.block_size() != 0)) + throw TLS_Exception(Alert::BAD_RECORD_MAC, "Message authentication failure"); + + cs.mac()->update(cs.format_ad(record_sequence, record_type, record_version, enc_size)); + cs.mac()->update(record_contents, enc_size); + + std::vector<byte> mac_buf(mac_size); + cs.mac()->final(mac_buf.data()); + + const size_t mac_offset = enc_size; + + const bool mac_ok = same_mem(&record_contents[mac_offset], mac_buf.data(), mac_size); + + if(!mac_ok) + { + throw TLS_Exception(Alert::BAD_RECORD_MAC, "Message authentication failure"); + } + + cbc_decrypt_record(record_contents, enc_size, cs, *bc); + + // 0 if padding was invalid, otherwise 1 + padding_bytes + u16bit pad_size = tls_padding_check(record_contents, enc_size); + + const byte* plaintext_block = &record_contents[iv_size]; + const u16bit plaintext_length = enc_size - iv_size - pad_size; + + output.assign(plaintext_block, plaintext_block + plaintext_length); } } } size_t read_tls_record(secure_vector<byte>& readbuf, - const byte input[], - size_t input_sz, - size_t& consumed, - secure_vector<byte>& record, - u64bit* record_sequence, - Protocol_Version* record_version, - Record_Type* record_type, + Record_Raw_Input& raw_input, + Record& rec, Connection_Sequence_Numbers* sequence_numbers, get_cipherstate_fn get_cipherstate) { - consumed = 0; - if(readbuf.size() < TLS_HEADER_SIZE) // header incomplete? { if(size_t needed = fill_buffer_to(readbuf, - input, input_sz, consumed, + raw_input.get_data(), raw_input.get_size(), raw_input.get_consumed(), TLS_HEADER_SIZE)) return needed; BOTAN_ASSERT_EQUAL(readbuf.size(), TLS_HEADER_SIZE, "Have an entire header"); } - *record_version = Protocol_Version(readbuf[1], readbuf[2]); + *rec.get_protocol_version() = Protocol_Version(readbuf[1], readbuf[2]); - BOTAN_ASSERT(!record_version->is_datagram_protocol(), "Expected TLS"); + BOTAN_ASSERT(!rec.get_protocol_version()->is_datagram_protocol(), "Expected TLS"); - const size_t record_len = make_u16bit(readbuf[TLS_HEADER_SIZE-2], + const size_t record_size = make_u16bit(readbuf[TLS_HEADER_SIZE-2], readbuf[TLS_HEADER_SIZE-1]); - if(record_len > MAX_CIPHERTEXT_SIZE) + if(record_size > MAX_CIPHERTEXT_SIZE) throw TLS_Exception(Alert::RECORD_OVERFLOW, "Received a record that exceeds maximum size"); - if(record_len == 0) + 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_sz, consumed, - TLS_HEADER_SIZE + record_len)) + raw_input.get_data(), raw_input.get_size(), raw_input.get_consumed(), + TLS_HEADER_SIZE + record_size)) return needed; - BOTAN_ASSERT_EQUAL(static_cast<size_t>(TLS_HEADER_SIZE) + record_len, + BOTAN_ASSERT_EQUAL(static_cast<size_t>(TLS_HEADER_SIZE) + record_size, readbuf.size(), "Have the full record"); - *record_type = static_cast<Record_Type>(readbuf[0]); + *rec.get_type() = static_cast<Record_Type>(readbuf[0]); u16bit epoch = 0; if(sequence_numbers) { - *record_sequence = sequence_numbers->next_read_sequence(); + *rec.get_sequence() = sequence_numbers->next_read_sequence(); epoch = sequence_numbers->current_read_epoch(); } else { // server initial handshake case - *record_sequence = 0; + *rec.get_sequence() = 0; epoch = 0; } @@ -527,7 +634,7 @@ size_t read_tls_record(secure_vector<byte>& readbuf, if(epoch == 0) // Unencrypted initial handshake { - record.assign(readbuf.begin() + TLS_HEADER_SIZE, readbuf.begin() + TLS_HEADER_SIZE + record_len); + rec.get_data().assign(readbuf.begin() + TLS_HEADER_SIZE, readbuf.begin() + TLS_HEADER_SIZE + record_size); readbuf.clear(); return 0; // got a full record } @@ -537,37 +644,30 @@ size_t read_tls_record(secure_vector<byte>& readbuf, BOTAN_ASSERT(cs, "Have cipherstate for this epoch"); - decrypt_record(record, + decrypt_record(rec.get_data(), record_contents, - record_len, - *record_sequence, - *record_version, - *record_type, + record_size, + *rec.get_sequence(), + *rec.get_protocol_version(), + *rec.get_type(), *cs); if(sequence_numbers) - sequence_numbers->read_accept(*record_sequence); + sequence_numbers->read_accept(*rec.get_sequence()); readbuf.clear(); return 0; } size_t read_dtls_record(secure_vector<byte>& readbuf, - const byte input[], - size_t input_sz, - size_t& consumed, - secure_vector<byte>& record, - u64bit* record_sequence, - Protocol_Version* record_version, - Record_Type* record_type, + Record_Raw_Input& raw_input, + Record& rec, Connection_Sequence_Numbers* sequence_numbers, get_cipherstate_fn get_cipherstate) { - consumed = 0; - if(readbuf.size() < DTLS_HEADER_SIZE) // header incomplete? { - if(fill_buffer_to(readbuf, input, input_sz, consumed, DTLS_HEADER_SIZE)) + if(fill_buffer_to(readbuf, raw_input.get_data(), raw_input.get_size(), raw_input.get_consumed(), DTLS_HEADER_SIZE)) { readbuf.clear(); return 0; @@ -576,38 +676,35 @@ size_t read_dtls_record(secure_vector<byte>& readbuf, BOTAN_ASSERT_EQUAL(readbuf.size(), DTLS_HEADER_SIZE, "Have an entire header"); } - *record_version = Protocol_Version(readbuf[1], readbuf[2]); + *rec.get_protocol_version() = Protocol_Version(readbuf[1], readbuf[2]); - BOTAN_ASSERT(record_version->is_datagram_protocol(), "Expected DTLS"); + BOTAN_ASSERT(rec.get_protocol_version()->is_datagram_protocol(), "Expected DTLS"); - const size_t record_len = make_u16bit(readbuf[DTLS_HEADER_SIZE-2], - readbuf[DTLS_HEADER_SIZE-1]); + const size_t record_size = make_u16bit(readbuf[DTLS_HEADER_SIZE-2], + readbuf[DTLS_HEADER_SIZE-1]); - // Invalid packet: - if(record_len == 0 || record_len > MAX_CIPHERTEXT_SIZE) - { - readbuf.clear(); - return 0; - } + if(record_size > MAX_CIPHERTEXT_SIZE) + throw TLS_Exception(Alert::RECORD_OVERFLOW, + "Got message that exceeds maximum size"); - if(fill_buffer_to(readbuf, input, input_sz, consumed, DTLS_HEADER_SIZE + record_len)) + if(fill_buffer_to(readbuf, raw_input.get_data(), raw_input.get_size(), raw_input.get_consumed(), DTLS_HEADER_SIZE + record_size)) { // Truncated packet? readbuf.clear(); return 0; } - BOTAN_ASSERT_EQUAL(static_cast<size_t>(DTLS_HEADER_SIZE) + record_len, readbuf.size(), + BOTAN_ASSERT_EQUAL(static_cast<size_t>(DTLS_HEADER_SIZE) + record_size, readbuf.size(), "Have the full record"); - *record_type = static_cast<Record_Type>(readbuf[0]); + *rec.get_type() = static_cast<Record_Type>(readbuf[0]); u16bit epoch = 0; - *record_sequence = load_be<u64bit>(&readbuf[3], 0); - epoch = (*record_sequence >> 48); + *rec.get_sequence() = load_be<u64bit>(&readbuf[3], 0); + epoch = (*rec.get_sequence() >> 48); - if(sequence_numbers && sequence_numbers->already_seen(*record_sequence)) + if(sequence_numbers && sequence_numbers->already_seen(*rec.get_sequence())) { readbuf.clear(); return 0; @@ -617,7 +714,7 @@ size_t read_dtls_record(secure_vector<byte>& readbuf, if(epoch == 0) // Unencrypted initial handshake { - record.assign(readbuf.begin() + DTLS_HEADER_SIZE, readbuf.begin() + DTLS_HEADER_SIZE + record_len); + rec.get_data().assign(readbuf.begin() + DTLS_HEADER_SIZE, readbuf.begin() + DTLS_HEADER_SIZE + record_size); readbuf.clear(); return 0; // got a full record } @@ -629,23 +726,23 @@ size_t read_dtls_record(secure_vector<byte>& readbuf, BOTAN_ASSERT(cs, "Have cipherstate for this epoch"); - decrypt_record(record, + decrypt_record(rec.get_data(), record_contents, - record_len, - *record_sequence, - *record_version, - *record_type, + record_size, + *rec.get_sequence(), + *rec.get_protocol_version(), + *rec.get_type(), *cs); } catch(std::exception) { readbuf.clear(); - *record_type = NO_RECORD; + *rec.get_type() = NO_RECORD; return 0; } if(sequence_numbers) - sequence_numbers->read_accept(*record_sequence); + sequence_numbers->read_accept(*rec.get_sequence()); readbuf.clear(); return 0; @@ -654,24 +751,16 @@ size_t read_dtls_record(secure_vector<byte>& readbuf, } size_t read_record(secure_vector<byte>& readbuf, - const byte input[], - size_t input_sz, - bool is_datagram, - size_t& consumed, - secure_vector<byte>& record, - u64bit* record_sequence, - Protocol_Version* record_version, - Record_Type* record_type, + Record_Raw_Input& raw_input, + Record& rec, Connection_Sequence_Numbers* sequence_numbers, get_cipherstate_fn get_cipherstate) { - if(is_datagram) - return read_dtls_record(readbuf, input, input_sz, consumed, - record, record_sequence, record_version, record_type, + if(raw_input.is_datagram()) + return read_dtls_record(readbuf, raw_input, rec, sequence_numbers, get_cipherstate); else - return read_tls_record(readbuf, input, input_sz, consumed, - record, record_sequence, record_version, record_type, + return read_tls_record(readbuf, raw_input, rec, sequence_numbers, get_cipherstate); } diff --git a/src/lib/tls/tls_record.h b/src/lib/tls/tls_record.h index e3b0b9b58..4420a9c66 100644 --- a/src/lib/tls/tls_record.h +++ b/src/lib/tls/tls_record.h @@ -1,6 +1,7 @@ /* * TLS Record Handling * (C) 2004-2012 Jack Lloyd +* 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -38,7 +39,8 @@ class Connection_Cipher_State Connection_Side which_side, bool is_our_side, const Ciphersuite& suite, - const Session_Keys& keys); + const Session_Keys& keys, + bool uses_encrypt_then_mac); AEAD_Mode* aead() { return m_aead.get(); } @@ -66,6 +68,8 @@ class Connection_Cipher_State size_t nonce_bytes_from_handshake() const { return m_nonce_bytes_from_handshake; } + bool uses_encrypt_then_mac() const { return m_uses_encrypt_then_mac; } + bool cbc_without_explicit_iv() const { return (m_block_size > 0) && (m_iv_size == 0); } @@ -88,8 +92,84 @@ class Connection_Cipher_State size_t m_nonce_bytes_from_handshake; size_t m_nonce_bytes_from_record; size_t m_iv_size = 0; + + bool m_uses_encrypt_then_mac; }; +class Record + { + public: + Record(secure_vector<byte>& data, + u64bit* sequence, + Protocol_Version* protocol_version, + Record_Type* type) + : m_data(data), m_sequence(sequence), m_protocol_version(protocol_version), + m_type(type), m_size(data.size()) {}; + + secure_vector<byte>& get_data() { return m_data; } + + Protocol_Version* get_protocol_version() { return m_protocol_version; } + + u64bit* get_sequence() { return m_sequence; } + + Record_Type* get_type() { return m_type; } + + size_t& get_size() { return m_size; } + + private: + secure_vector<byte>& m_data; + u64bit* m_sequence; + Protocol_Version* m_protocol_version; + Record_Type* m_type; + size_t m_size; + }; + +class Record_Message + { + public: + Record_Message(const byte* data, size_t size) + : m_type(0), m_sequence(0), m_data(data), m_size(size) {}; + Record_Message(byte type, u64bit sequence, const byte* data, size_t size) + : m_type(type), m_sequence(sequence), m_data(data), + m_size(size) {}; + + byte& get_type() { return m_type; }; + u64bit& get_sequence() { return m_sequence; }; + const byte* get_data() { return m_data; }; + size_t& get_size() { return m_size; }; + + private: + byte m_type; + u64bit m_sequence; + const byte* m_data; + size_t m_size; +}; + +class Record_Raw_Input + { + public: + Record_Raw_Input(const byte* data, size_t size, size_t& consumed, + bool is_datagram) + : m_data(data), m_size(size), m_consumed(consumed), + m_is_datagram(is_datagram) {}; + + const byte*& get_data() { return m_data; }; + + size_t& get_size() { return m_size; }; + + size_t& get_consumed() { return m_consumed; }; + void set_consumed(size_t consumed) { m_consumed = consumed; } + + bool is_datagram() { return m_is_datagram; }; + + private: + const byte* m_data; + size_t m_size; + size_t& m_consumed; + bool m_is_datagram; + }; + + /** * Create a TLS record * @param write_buffer the output record is placed here @@ -103,7 +183,7 @@ class Connection_Cipher_State * @return number of bytes written to write_buffer */ void write_record(secure_vector<byte>& write_buffer, - byte msg_type, const byte msg[], size_t msg_length, + Record_Message rec_msg, Protocol_Version version, u64bit msg_sequence, Connection_Cipher_State* cipherstate, @@ -117,14 +197,8 @@ typedef std::function<std::shared_ptr<Connection_Cipher_State> (u16bit)> get_cip * @return zero if full message, else number of bytes still needed */ size_t read_record(secure_vector<byte>& read_buffer, - const byte input[], - size_t input_length, - bool is_datagram, - size_t& input_consumed, - secure_vector<byte>& record, - u64bit* record_sequence, - Protocol_Version* record_version, - Record_Type* record_type, + Record_Raw_Input& raw_input, + Record& rec, Connection_Sequence_Numbers* sequence_numbers, get_cipherstate_fn get_cipherstate); diff --git a/src/lib/tls/tls_server.cpp b/src/lib/tls/tls_server.cpp index 41b14ae08..40aa18d27 100644 --- a/src/lib/tls/tls_server.cpp +++ b/src/lib/tls/tls_server.cpp @@ -1,6 +1,7 @@ /* * TLS Server * (C) 2004-2011,2012,2016 Jack Lloyd +* 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -9,30 +10,41 @@ #include <botan/internal/tls_handshake_state.h> #include <botan/internal/tls_messages.h> #include <botan/internal/stl_util.h> +#include <botan/tls_magic.h> namespace Botan { namespace TLS { -namespace { - class Server_Handshake_State : public Handshake_State { public: - // using Handshake_State::Handshake_State; + 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; } - Server_Handshake_State(Handshake_IO* io, handshake_msg_cb cb) : Handshake_State(io, cb) {} + private: // Used by the server only, in case of RSA key exchange. Not owned - Private_Key* server_rsa_kex_key = nullptr; + 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 allow_session_resumption = true; + bool m_allow_session_resumption = true; }; +namespace { + bool check_for_resume(Session& session_info, Session_Manager& session_manager, Credentials_Manager& credentials, @@ -118,6 +130,19 @@ bool check_for_resume(Session& session_info, } } + // 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; } @@ -225,6 +250,19 @@ get_server_certs(const std::string& hostname, /* * 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, + is_datagram, io_buf_sz), + m_creds(creds) + { + } + Server::Server(output_fn output, data_cb data_cb, alert_cb alert_cb, @@ -236,13 +274,15 @@ Server::Server(output_fn output, next_protocol_fn next_proto, bool is_datagram, size_t io_buf_sz) : - Channel(output, data_cb, alert_cb, handshake_cb, Channel::handshake_msg_cb(), - session_manager, rng, policy, is_datagram, io_buf_sz), + Channel(output, data_cb, alert_cb, handshake_cb, + Channel::handshake_msg_cb(), session_manager, + rng, policy, is_datagram, io_buf_sz), m_creds(creds), m_choose_next_protocol(next_proto) { } + Server::Server(output_fn output, data_cb data_cb, alert_cb alert_cb, @@ -263,8 +303,7 @@ Server::Server(output_fn output, Handshake_State* Server::new_handshake_state(Handshake_IO* io) { - std::unique_ptr<Handshake_State> state( - new Server_Handshake_State(io, get_handshake_msg_cb())); + std::unique_ptr<Handshake_State> state(new Server_Handshake_State(io, callbacks())); state->set_expected_next(CLIENT_HELLO); return state.release(); @@ -284,441 +323,516 @@ Server::get_peer_cert_chain(const Handshake_State& state) const void Server::initiate_handshake(Handshake_State& state, bool force_full_renegotiation) { - dynamic_cast<Server_Handshake_State&>(state).allow_session_resumption = - !force_full_renegotiation; + dynamic_cast<Server_Handshake_State&>(state). + set_allow_session_resumption(!force_full_renegotiation); Hello_Request hello_req(state.handshake_io()); } /* -* Process a handshake message +* Process a CLIENT HELLO Message */ -void Server::process_handshake_msg(const Handshake_State* active_state, - Handshake_State& state_base, - Handshake_Type type, - const std::vector<byte>& contents) - { - 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) +void Server::process_client_hello_msg(const Handshake_State* active_state, + Server_Handshake_State& pending_state, + const std::vector<byte>& contents) +{ + const bool initial_handshake = !active_state; + + if(!policy().allow_insecure_renegotiation() && + !(initial_handshake || secure_renegotiation_supported())) { - state.hash().update(state.handshake_io().format(contents, type)); + send_warning_alert(Alert::NO_RENEGOTIATION); + return; } - if(type == CLIENT_HELLO) + pending_state.client_hello(new Client_Hello(contents)); + const Protocol_Version client_version = pending_state.client_hello()->version(); + + Protocol_Version negotiated_version; + + const Protocol_Version latest_supported = + policy().latest_supported_version(client_version.is_datagram_protocol()); + + if((initial_handshake && client_version.known_version()) || + (!initial_handshake && client_version == active_state->version())) { - const bool initial_handshake = !active_state; + /* + Common cases: new client hello with some known version, or a + renegotiation using the same version as previously + negotiated. + */ - if(!policy().allow_insecure_renegotiation() && - !(initial_handshake || secure_renegotiation_supported())) + negotiated_version = client_version; + } + else if(!initial_handshake && (client_version != active_state->version())) + { + /* + * 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_state->version() > client_version) { - send_warning_alert(Alert::NO_RENEGOTIATION); - return; + throw TLS_Exception(Alert::PROTOCOL_VERSION, + "Client negotiated " + + active_state->version().to_string() + + " then renegotiated with " + + client_version.to_string()); } + else + negotiated_version = active_state->version(); + } + else + { + /* + New negotiation using a version we don't know. Offer them the + best we currently know and support + */ + negotiated_version = latest_supported; + } - state.client_hello(new Client_Hello(contents)); + if(!policy().acceptable_protocol_version(negotiated_version)) + { + throw TLS_Exception(Alert::PROTOCOL_VERSION, + "Client version " + negotiated_version.to_string() + + " is unacceptable by policy"); + } - const Protocol_Version client_version = state.client_hello()->version(); + if(pending_state.client_hello()->sent_fallback_scsv()) + { + if(latest_supported > client_version) + throw TLS_Exception(Alert::INAPPROPRIATE_FALLBACK, + "Client signalled fallback SCSV, possible attack"); + } - Protocol_Version negotiated_version; + secure_renegotiation_check(pending_state.client_hello()); - const Protocol_Version latest_supported = - policy().latest_supported_version(client_version.is_datagram_protocol()); + pending_state.set_version(negotiated_version); - if((initial_handshake && client_version.known_version()) || - (!initial_handshake && client_version == active_state->version())) - { - /* - Common cases: new client hello with some known version, or a - renegotiation using the same version as previously - negotiated. - */ + 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())); - negotiated_version = client_version; - } - else if(!initial_handshake && (client_version != active_state->version())) - { - /* - * 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_state->version() > client_version) - { - throw TLS_Exception(Alert::PROTOCOL_VERSION, - "Client negotiated " + - active_state->version().to_string() + - " then renegotiated with " + - client_version.to_string()); - } - else - negotiated_version = active_state->version(); - } - else - { - /* - New negotiation using a version we don't know. Offer them the - best we currently know and support - */ - negotiated_version = latest_supported; - } + bool have_session_ticket_key = false; - if(!policy().acceptable_protocol_version(negotiated_version)) - { - throw TLS_Exception(Alert::PROTOCOL_VERSION, - "Client version " + negotiated_version.to_string() + - " is unacceptable by policy"); - } + 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(state.client_hello()->sent_fallback_scsv()) + // if the callback return was empty, fall back to the (deprecated) std::function + if(m_next_protocol.empty() && m_choose_next_protocol) { - if(latest_supported > client_version) - throw TLS_Exception(Alert::INAPPROPRIATE_FALLBACK, - "Client signalled fallback SCSV, possible attack"); + m_next_protocol = m_choose_next_protocol(pending_state.client_hello()->next_protocols()); } + } - secure_renegotiation_check(state.client_hello()); + 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); + } +} - state.set_version(negotiated_version); +void Server::process_certificate_msg(Server_Handshake_State& pending_state, + const std::vector<byte>& contents) +{ + pending_state.client_certs(new Certificate(contents, policy())); + pending_state.set_expected_next(CLIENT_KEX); +} - Session session_info; - const bool resuming = - state.allow_session_resumption && - check_for_resume(session_info, - session_manager(), - m_creds, - state.client_hello(), - std::chrono::seconds(policy().session_ticket_lifetime())); +void Server::process_client_key_exchange_msg(Server_Handshake_State& pending_state, + const std::vector<byte>& 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); - bool have_session_ticket_key = false; + pending_state.client_kex( + new Client_Key_Exchange(contents, pending_state, + pending_state.server_rsa_kex_key(), + m_creds, policy(), rng()) + ); - try - { - have_session_ticket_key = - m_creds.psk("tls-server", "session-ticket", "").length() > 0; - } - catch(...) {} + pending_state.compute_session_keys(); +} - m_next_protocol = ""; - if(m_choose_next_protocol && state.client_hello()->supports_alpn()) - m_next_protocol = m_choose_next_protocol(state.client_hello()->next_protocols()); +void Server::process_change_cipher_spec_msg(Server_Handshake_State& pending_state) +{ + pending_state.set_expected_next(FINISHED); + change_cipher_spec_reader(SERVER); +} - if(resuming) - { - // 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 = - (state.client_hello()->supports_session_ticket() && - state.client_hello()->session_ticket().empty() && - have_session_ticket_key); - - state.server_hello(new Server_Hello( - state.handshake_io(), - state.hash(), - policy(), - rng(), - secure_renegotiation_data_for_server_hello(), - *state.client_hello(), - session_info, - offer_new_session_ticket, - m_next_protocol - )); - - secure_renegotiation_check(state.server_hello()); - - state.compute_session_keys(session_info.master_secret()); - - if(!save_session(session_info)) - { - session_manager().remove_entry(session_info.session_id()); - - if(state.server_hello()->supports_session_ticket()) // send an empty ticket - { - state.new_session_ticket( - new New_Session_Ticket(state.handshake_io(), - state.hash()) - ); - } - } +void Server::process_certificate_verify_msg(Server_Handshake_State& pending_state, + Handshake_Type type, + const std::vector<byte>& 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 + { + m_creds.verify_certificate_chain ( "tls-server", "", client_certs ); + } + catch ( std::exception& e ) + { + throw TLS_Exception ( Alert::BAD_CERTIFICATE, e.what() ); + } + + pending_state.set_expected_next ( HANDSHAKE_CCS ); +} - if(state.server_hello()->supports_session_ticket() && !state.new_session_ticket()) - { - try - { - const SymmetricKey ticket_key = m_creds.psk("tls-server", "session-ticket", ""); - - state.new_session_ticket( - new New_Session_Ticket(state.handshake_io(), - state.hash(), - session_info.encrypt(ticket_key, rng()), - policy().session_ticket_lifetime()) - ); - } - catch(...) {} - - if(!state.new_session_ticket()) - { - state.new_session_ticket( - new New_Session_Ticket(state.handshake_io(), state.hash()) - ); - } - } +void Server::process_finished_msg(Server_Handshake_State& pending_state, + Handshake_Type type, + const std::vector<byte>& contents) +{ + pending_state.set_expected_next ( HANDSHAKE_NONE ); - state.handshake_io().send(Change_Cipher_Spec()); + pending_state.client_finished ( new Finished ( contents ) ); - change_cipher_spec_writer(SERVER); + if ( !pending_state.client_finished()->verify ( pending_state, CLIENT ) ) + throw TLS_Exception ( Alert::DECRYPT_ERROR, + "Finished message didn't verify" ); - state.server_finished(new Finished(state.handshake_io(), state, SERVER)); - state.set_expected_next(HANDSHAKE_CCS); - } - else // new session - { - std::map<std::string, std::vector<X509_Certificate> > cert_chains; + if ( !pending_state.server_finished() ) + { + // already sent finished if resuming, so this is a new session - const std::string sni_hostname = state.client_hello()->sni_hostname(); + pending_state.hash().update ( pending_state.handshake_io().format ( contents, type ) ); - cert_chains = get_server_certs(sni_hostname, m_creds); + 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(), + pending_state.server_hello()->compression_method(), + 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<byte>(), + Server_Information(pending_state.client_hello()->sni_hostname()), + pending_state.srp_identifier(), + pending_state.server_hello()->srtp_profile() + ); - if(sni_hostname != "" && cert_chains.empty()) + if ( save_session ( session_info ) ) { - 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 operation. - */ - if(!cert_chains.empty()) - send_alert(Alert(Alert::UNRECOGNIZED_NAME)); + 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 ); } - state.server_hello(new Server_Hello( - state.handshake_io(), - state.hash(), - policy(), - rng(), - secure_renegotiation_data_for_server_hello(), - *state.client_hello(), - make_hello_random(rng(), policy()), // new session ID - state.version(), - choose_ciphersuite(policy(), state.version(), m_creds, cert_chains, state.client_hello()), - choose_compression(policy(), state.client_hello()->compression_methods()), - have_session_ticket_key, - m_next_protocol) + 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() ) ); + } - secure_renegotiation_check(state.server_hello()); + pending_state.handshake_io().send ( Change_Cipher_Spec() ); - const std::string sig_algo = state.ciphersuite().sig_algo(); - const std::string kex_algo = state.ciphersuite().kex_algo(); + change_cipher_spec_writer ( SERVER ); - if(sig_algo != "") - { - BOTAN_ASSERT(!cert_chains[sig_algo].empty(), - "Attempting to send empty certificate chain"); + pending_state.server_finished ( new Finished ( pending_state.handshake_io(), pending_state, SERVER ) ); + } - state.server_certs(new Certificate(state.handshake_io(), - state.hash(), - cert_chains[sig_algo])); - } + activate_session(); - Private_Key* private_key = nullptr; +} - if(kex_algo == "RSA" || sig_algo != "") - { - private_key = m_creds.private_key_for( - state.server_certs()->cert_chain()[0], - "tls-server", - sni_hostname); +/* +* Process a handshake message +*/ +void Server::process_handshake_msg(const Handshake_State* active_state, + Handshake_State& state_base, + Handshake_Type type, + const std::vector<byte>& contents) + { + Server_Handshake_State& state = dynamic_cast<Server_Handshake_State&>(state_base); + state.confirm_transition_to(type); - if(!private_key) - throw Internal_Error("No private key located for associated server cert"); - } + /* + * 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)); + } - if(kex_algo == "RSA") - { - state.server_rsa_kex_key = private_key; - } - else - { - state.server_kex(new Server_Key_Exchange(state.handshake_io(), - state, policy(), - m_creds, rng(), private_key)); - } + switch(type) + { + case CLIENT_HELLO: + this->process_client_hello_msg(active_state, state, contents); + break; + + case CERTIFICATE: + this->process_certificate_msg(state, contents); + break; + + case CLIENT_KEX: + this->process_client_key_exchange_msg(state, contents); + break; + + case CERTIFICATE_VERIFY: + this->process_certificate_verify_msg(state, type, contents); + break; + + case HANDSHAKE_CCS: + this->process_change_cipher_spec_msg(state); + break; - auto trusted_CAs = m_creds.trusted_certificate_authorities("tls-server", sni_hostname); + case FINISHED: + this->process_finished_msg(state, type, contents); + break; - std::vector<X509_DN> client_auth_CAs; + default: + throw Unexpected_Message("Unknown handshake message received"); + break; + } + } - for(auto store : trusted_CAs) +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(), + 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.compute_session_keys(session_info.master_secret()); + + 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 { - auto subjects = store->all_subjects(); - client_auth_CAs.insert(client_auth_CAs.end(), subjects.begin(), subjects.end()); + pending_state.new_session_ticket( + new New_Session_Ticket(pending_state.handshake_io(), + pending_state.hash()) + ); } + } - if(!client_auth_CAs.empty() && state.ciphersuite().sig_algo() != "") + if(pending_state.server_hello()->supports_session_ticket() && !pending_state.new_session_ticket()) + { + try { - state.cert_req( - new Certificate_Req(state.handshake_io(), state.hash(), - policy(), client_auth_CAs, state.version())); + const SymmetricKey ticket_key = m_creds.psk("tls-server", "session-ticket", ""); - state.set_expected_next(CERTIFICATE); + 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 the client doesn't have a cert they want to use they are - * allowed to send either an empty cert message or proceed - * directly to the client key exchange, so allow either case. - */ - state.set_expected_next(CLIENT_KEX); - - state.server_hello_done(new Server_Hello_Done(state.handshake_io(), state.hash())); + if(!pending_state.new_session_ticket()) + { + pending_state.new_session_ticket( + new New_Session_Ticket(pending_state.handshake_io(), pending_state.hash()) + ); + } } - } - else if(type == CERTIFICATE) - { - state.client_certs(new Certificate(contents)); - state.set_expected_next(CLIENT_KEX); - } - else if(type == CLIENT_KEX) - { - if(state.received_handshake_msg(CERTIFICATE) && !state.client_certs()->empty()) - state.set_expected_next(CERTIFICATE_VERIFY); - else - state.set_expected_next(HANDSHAKE_CCS); + pending_state.handshake_io().send(Change_Cipher_Spec()); - state.client_kex( - new Client_Key_Exchange(contents, state, - state.server_rsa_kex_key, - m_creds, policy(), rng()) - ); + change_cipher_spec_writer(SERVER); - state.compute_session_keys(); - } - else if(type == CERTIFICATE_VERIFY) - { - state.client_verify(new Certificate_Verify(contents, state.version())); + pending_state.server_finished(new Finished(pending_state.handshake_io(), pending_state, SERVER)); + pending_state.set_expected_next(HANDSHAKE_CCS); + } - const std::vector<X509_Certificate>& client_certs = - state.client_certs()->cert_chain(); +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 bool sig_valid = - state.client_verify()->verify(client_certs[0], state, policy()); + const std::string sni_hostname = pending_state.client_hello()->sni_hostname(); - state.hash().update(state.handshake_io().format(contents, type)); + cert_chains = get_server_certs(sni_hostname, m_creds); + + if(sni_hostname != "" && cert_chains.empty()) + { + cert_chains = get_server_certs("", m_creds); /* - * 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, ..." + * 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 operation. */ - if(!sig_valid) - throw TLS_Exception(Alert::DECRYPT_ERROR, "Client cert verify failed"); - - try - { - m_creds.verify_certificate_chain("tls-server", "", client_certs); - } - catch(std::exception& e) - { - throw TLS_Exception(Alert::BAD_CERTIFICATE, e.what()); - } - - state.set_expected_next(HANDSHAKE_CCS); - } - else if(type == HANDSHAKE_CCS) - { - state.set_expected_next(FINISHED); - change_cipher_spec_reader(SERVER); + if(!cert_chains.empty()) + send_alert(Alert(Alert::UNRECOGNIZED_NAME)); } - else if(type == FINISHED) + + Server_Hello::Settings srv_settings( + make_hello_random(rng(), policy()), // new session ID + pending_state.version(), + choose_ciphersuite(policy(), + pending_state.version(), + m_creds, + cert_chains, + pending_state.client_hello()), + choose_compression(policy(), + pending_state.client_hello()->compression_methods()), + have_session_ticket_key); + + pending_state.server_hello(new Server_Hello( + pending_state.handshake_io(), + pending_state.hash(), + policy(), + rng(), + secure_renegotiation_data_for_server_hello(), + *pending_state.client_hello(), + srv_settings, + m_next_protocol) + ); + + secure_renegotiation_check(pending_state.server_hello()); + + const std::string sig_algo = pending_state.ciphersuite().sig_algo(); + const std::string kex_algo = pending_state.ciphersuite().kex_algo(); + + if(sig_algo != "") { - state.set_expected_next(HANDSHAKE_NONE); + BOTAN_ASSERT(!cert_chains[sig_algo].empty(), + "Attempting to send empty certificate chain"); - state.client_finished(new Finished(contents)); + pending_state.server_certs(new Certificate(pending_state.handshake_io(), + pending_state.hash(), + cert_chains[sig_algo])); + } - if(!state.client_finished()->verify(state, CLIENT)) - throw TLS_Exception(Alert::DECRYPT_ERROR, - "Finished message didn't verify"); + Private_Key* private_key = nullptr; - if(!state.server_finished()) - { - // already sent finished if resuming, so this is a new session + if(kex_algo == "RSA" || sig_algo != "") + { + private_key = m_creds.private_key_for( + pending_state.server_certs()->cert_chain()[0], + "tls-server", + sni_hostname); - state.hash().update(state.handshake_io().format(contents, type)); + if(!private_key) + throw Internal_Error("No private key located for associated server cert"); + } - Session session_info( - state.server_hello()->session_id(), - state.session_keys().master_secret(), - state.server_hello()->version(), - state.server_hello()->ciphersuite(), - state.server_hello()->compression_method(), - SERVER, - state.server_hello()->supports_extended_master_secret(), - get_peer_cert_chain(state), - std::vector<byte>(), - Server_Information(state.client_hello()->sni_hostname()), - state.srp_identifier(), - state.server_hello()->srtp_profile() - ); + if(kex_algo == "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)); + } - if(save_session(session_info)) - { - if(state.server_hello()->supports_session_ticket()) - { - try - { - const SymmetricKey ticket_key = m_creds.psk("tls-server", "session-ticket", ""); - - state.new_session_ticket( - new New_Session_Ticket(state.handshake_io(), - state.hash(), - session_info.encrypt(ticket_key, rng()), - policy().session_ticket_lifetime()) - ); - } - catch(...) {} - } - else - session_manager().save(session_info); - } + auto trusted_CAs = m_creds.trusted_certificate_authorities("tls-server", sni_hostname); - if(!state.new_session_ticket() && - state.server_hello()->supports_session_ticket()) - { - state.new_session_ticket( - new New_Session_Ticket(state.handshake_io(), state.hash()) - ); - } + std::vector<X509_DN> client_auth_CAs; - state.handshake_io().send(Change_Cipher_Spec()); + for(auto store : trusted_CAs) + { + auto subjects = store->all_subjects(); + client_auth_CAs.insert(client_auth_CAs.end(), subjects.begin(), subjects.end()); + } - change_cipher_spec_writer(SERVER); + if(!client_auth_CAs.empty() && pending_state.ciphersuite().sig_algo() != "") + { + pending_state.cert_req( + new Certificate_Req(pending_state.handshake_io(), + pending_state.hash(), + policy(), + client_auth_CAs, + pending_state.version())); + + pending_state.set_expected_next(CERTIFICATE); + } - state.server_finished(new Finished(state.handshake_io(), state, SERVER)); - } + /* + * If the client doesn't have a cert they want to use they are + * allowed to send either an empty cert message or proceed + * directly to the client key exchange, so allow either case. + */ + pending_state.set_expected_next(CLIENT_KEX); - activate_session(); - } - else - throw Unexpected_Message("Unknown handshake message received"); + pending_state.server_hello_done(new Server_Hello_Done(pending_state.handshake_io(), pending_state.hash())); } - } } diff --git a/src/lib/tls/tls_server.h b/src/lib/tls/tls_server.h index 5ea2a1318..051eda445 100644 --- a/src/lib/tls/tls_server.h +++ b/src/lib/tls/tls_server.h @@ -1,6 +1,7 @@ /* * TLS Server * (C) 2004-2011 Jack Lloyd +* 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -12,10 +13,13 @@ #include <botan/credentials_manager.h> #include <vector> + namespace Botan { namespace TLS { +class Server_Handshake_State; + /** * TLS Server */ @@ -26,7 +30,39 @@ class BOTAN_DLL Server final : public Channel /** * 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. + */ + BOTAN_DEPRECATED("Use TLS::Server(TLS::Callbacks ...)") Server(output_fn output, data_cb data_cb, alert_cb alert_cb, @@ -37,9 +73,14 @@ class BOTAN_DLL Server final : public Channel RandomNumberGenerator& rng, next_protocol_fn next_proto = next_protocol_fn(), bool is_datagram = false, - size_t reserved_io_buffer_size = 16*1024 + 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. + */ + BOTAN_DEPRECATED("Use TLS::Server(TLS::Callbacks ...)") Server(output_fn output, data_cb data_cb, alert_cb alert_cb, @@ -73,12 +114,40 @@ class BOTAN_DLL Server final : public Channel Handshake_Type type, const std::vector<byte>& contents) override; + void process_client_hello_msg(const Handshake_State* active_state, + Server_Handshake_State& pending_state, + const std::vector<byte>& contents); + + void process_certificate_msg(Server_Handshake_State& pending_state, + const std::vector<byte>& contents); + + void process_client_key_exchange_msg(Server_Handshake_State& pending_state, + const std::vector<byte>& 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<byte>& contents); + + void process_finished_msg(Server_Handshake_State& pending_state, + Handshake_Type type, + const std::vector<byte>& 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; - std::string m_next_protocol; }; } diff --git a/src/lib/tls/tls_session.cpp b/src/lib/tls/tls_session.cpp index 18c9b357c..d6b52846f 100644 --- a/src/lib/tls/tls_session.cpp +++ b/src/lib/tls/tls_session.cpp @@ -24,6 +24,7 @@ Session::Session(const std::vector<byte>& session_identifier, byte compression_method, Connection_Side side, bool extended_master_secret, + bool encrypt_then_mac, const std::vector<X509_Certificate>& certs, const std::vector<byte>& ticket, const Server_Information& server_info, @@ -39,6 +40,7 @@ Session::Session(const std::vector<byte>& session_identifier, 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) @@ -83,6 +85,7 @@ Session::Session(const byte ber[], size_t ber_len) .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) @@ -142,6 +145,7 @@ secure_vector<byte> Session::DER_encode() const .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)) diff --git a/src/lib/tls/tls_session.h b/src/lib/tls/tls_session.h index 8ca646cf2..643b79ac6 100644 --- a/src/lib/tls/tls_session.h +++ b/src/lib/tls/tls_session.h @@ -38,7 +38,8 @@ class BOTAN_DLL Session m_compression_method(0), m_connection_side(static_cast<Connection_Side>(0)), m_srtp_profile(0), - m_extended_master_secret(false) + m_extended_master_secret(false), + m_encrypt_then_mac(false) {} /** @@ -51,6 +52,7 @@ class BOTAN_DLL Session byte compression_method, Connection_Side side, bool supports_extended_master_secret, + bool supports_encrypt_then_mac, const std::vector<X509_Certificate>& peer_certs, const std::vector<byte>& session_ticket, const Server_Information& server_info, @@ -157,6 +159,8 @@ class BOTAN_DLL Session 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) */ @@ -180,7 +184,7 @@ class BOTAN_DLL Session const Server_Information& server_info() const { return m_server_info; } private: - enum { TLS_SESSION_PARAM_STRUCT_VERSION = 20160103 }; + enum { TLS_SESSION_PARAM_STRUCT_VERSION = 20160812}; std::chrono::system_clock::time_point m_start_time; @@ -194,6 +198,7 @@ class BOTAN_DLL Session Connection_Side m_connection_side; u16bit 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 diff --git a/src/lib/utils/calendar.cpp b/src/lib/utils/calendar.cpp index 73602d634..2ed90486a 100644 --- a/src/lib/utils/calendar.cpp +++ b/src/lib/utils/calendar.cpp @@ -12,6 +12,7 @@ #include <sstream> #include <iomanip> #include <mutex> +#include <stdlib.h> #if defined(BOTAN_HAS_BOOST_DATETIME) #include <boost/date_time/posix_time/posix_time_types.hpp> @@ -39,7 +40,7 @@ std::tm do_gmtime(std::time_t time_val) return tm; } -#if !defined(BOTAN_TARGET_OS_HAS_TIMEGM) && !defined(BOTAN_TARGET_OS_HAS_MKGMTIME) +#if !defined(BOTAN_TARGET_OS_HAS_TIMEGM) && !(defined(BOTAN_TARGET_OS_HAS_MKGMTIME) && defined(BOTAN_BUILD_COMPILER_IS_MSVC)) #if defined(BOTAN_HAS_BOOST_DATETIME) @@ -67,7 +68,7 @@ std::time_t boost_timegm(std::tm *tm) return out; } -#else +#elif defined(BOTAN_OS_TYPE_IS_UNIX) #pragma message "Caution! A fallback version of timegm() is used which is not thread-safe" @@ -138,13 +139,15 @@ std::chrono::system_clock::time_point calendar_point::to_std_timepoint() const // Define a function alias `botan_timegm` #if defined(BOTAN_TARGET_OS_HAS_TIMEGM) std::time_t (&botan_timegm)(std::tm *tm) = timegm; - #elif defined(BOTAN_TARGET_OS_HAS_MKGMTIME) + #elif defined(BOTAN_TARGET_OS_HAS_MKGMTIME) && defined(BOTAN_BUILD_COMPILER_IS_MSVC) // http://stackoverflow.com/questions/16647819/timegm-cross-platform std::time_t (&botan_timegm)(std::tm *tm) = _mkgmtime; #elif defined(BOTAN_HAS_BOOST_DATETIME) std::time_t (&botan_timegm)(std::tm *tm) = boost_timegm; - #else + #elif defined(BOTAN_OS_TYPE_IS_UNIX) std::time_t (&botan_timegm)(std::tm *tm) = fallback_timegm; + #else + std::time_t (&botan_timegm)(std::tm *tm) = mktime; // localtime instead... #endif // Convert std::tm to std::time_t diff --git a/src/lib/utils/os_utils.cpp b/src/lib/utils/os_utils.cpp index c00c898a3..2f17da952 100644 --- a/src/lib/utils/os_utils.cpp +++ b/src/lib/utils/os_utils.cpp @@ -19,7 +19,7 @@ #include <unistd.h> #endif -#if defined(BOTAN_TARGET_OS_IS_WINDOWS) +#if defined(BOTAN_TARGET_OS_IS_WINDOWS) || defined(BOTAN_TARGET_OS_IS_MINGW) #include <windows.h> #endif @@ -31,7 +31,7 @@ uint32_t get_process_id() { #if defined(BOTAN_TARGET_OS_TYPE_IS_UNIX) return ::getpid(); -#elif defined(BOTAN_TARGET_OS_IS_WINDOWS) +#elif defined(BOTAN_TARGET_OS_IS_WINDOWS) || defined(BOTAN_TARGET_OS_IS_MINGW) return ::GetCurrentProcessId(); #else throw Exception("get_process_id not supported"); @@ -40,49 +40,92 @@ uint32_t get_process_id() uint64_t get_processor_timestamp() { - uint64_t rtc = 0; - #if defined(BOTAN_TARGET_OS_HAS_QUERY_PERF_COUNTER) LARGE_INTEGER tv; ::QueryPerformanceCounter(&tv); - rtc = tv.QuadPart; -#endif - -#if defined(BOTAN_USE_GCC_INLINE_ASM) + return tv.QuadPart; -#if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY) +#elif defined(BOTAN_USE_GCC_INLINE_ASM) && defined(BOTAN_TARGET_CPU_IS_X86_FAMILY) if(CPUID::has_rdtsc()) // not available on all x86 CPUs { uint32_t rtc_low = 0, rtc_high = 0; asm volatile("rdtsc" : "=d" (rtc_high), "=a" (rtc_low)); - rtc = (static_cast<u64bit>(rtc_high) << 32) | rtc_low; + return (static_cast<u64bit>(rtc_high) << 32) | rtc_low; } -#elif defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY) +#elif defined(BOTAN_USE_GCC_INLINE_ASM) && defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY) uint32_t rtc_low = 0, rtc_high = 0; asm volatile("mftbu %0; mftb %1" : "=r" (rtc_high), "=r" (rtc_low)); - rtc = (static_cast<u64bit>(rtc_high) << 32) | rtc_low; + return (static_cast<u64bit>(rtc_high) << 32) | rtc_low; -#elif defined(BOTAN_TARGET_ARCH_IS_ALPHA) +#elif defined(BOTAN_USE_GCC_INLINE_ASM) && defined(BOTAN_TARGET_ARCH_IS_ALPHA) + uint64_t rtc = 0; asm volatile("rpcc %0" : "=r" (rtc)); + return rtc; -#elif defined(BOTAN_TARGET_ARCH_IS_SPARC64) && !defined(BOTAN_TARGET_OS_IS_OPENBSD) // OpenBSD does not trap access to the %tick register +#elif defined(BOTAN_USE_GCC_INLINE_ASM) && defined(BOTAN_TARGET_ARCH_IS_SPARC64) && !defined(BOTAN_TARGET_OS_IS_OPENBSD) + uint64_t rtc = 0; asm volatile("rd %%tick, %0" : "=r" (rtc)); + return rtc; -#elif defined(BOTAN_TARGET_ARCH_IS_IA64) +#elif defined(BOTAN_USE_GCC_INLINE_ASM) && defined(BOTAN_TARGET_ARCH_IS_IA64) + uint64_t rtc = 0; asm volatile("mov %0=ar.itc" : "=r" (rtc)); + return rtc; -#elif defined(BOTAN_TARGET_ARCH_IS_S390X) +#elif defined(BOTAN_USE_GCC_INLINE_ASM) && defined(BOTAN_TARGET_ARCH_IS_S390X) + uint64_t rtc = 0; asm volatile("stck 0(%0)" : : "a" (&rtc) : "memory", "cc"); + return rtc; -#elif defined(BOTAN_TARGET_ARCH_IS_HPPA) +#elif defined(BOTAN_USE_GCC_INLINE_ASM) && defined(BOTAN_TARGET_ARCH_IS_HPPA) + uint64_t rtc = 0; asm volatile("mfctl 16,%0" : "=r" (rtc)); // 64-bit only? + return rtc; +#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 - return rtc; + // 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 get_system_timestamp_ns() @@ -141,7 +184,7 @@ size_t get_memory_locking_limit() return std::min<size_t>(limits.rlim_cur, mlock_requested * 1024); } -#elif defined BOTAN_TARGET_OS_HAS_VIRTUAL_LOCK +#elif defined(BOTAN_TARGET_OS_HAS_VIRTUAL_LOCK) && defined(BOTAN_BUILD_COMPILER_IS_MSVC) SIZE_T working_min = 0, working_max = 0; DWORD working_flags = 0; if(!::GetProcessWorkingSetSizeEx(::GetCurrentProcess(), &working_min, &working_max, &working_flags)) diff --git a/src/lib/utils/simd/simd_32.h b/src/lib/utils/simd/simd_32.h index 0b4ca8f03..c29c55c7a 100644 --- a/src/lib/utils/simd/simd_32.h +++ b/src/lib/utils/simd/simd_32.h @@ -100,7 +100,9 @@ class SIMD_4x32 __vector unsigned char perm = vec_lvsl(0, in_32); - perm = vec_xor(perm, vec_splat_u8(3)); +#if defined(BOTAN_TARGET_CPU_IS_BIG_ENDIAN) + perm = vec_xor(perm, vec_splat_u8(3)); // bswap vector +#endif R0 = vec_perm(R0, R1, perm); @@ -124,6 +126,10 @@ class SIMD_4x32 __vector unsigned char perm = vec_lvsl(0, in_32); +#if defined(BOTAN_TARGET_CPU_IS_LITTLE_ENDIAN) + perm = vec_xor(perm, vec_splat_u8(3)); // bswap vector +#endif + R0 = vec_perm(R0, R1, perm); return SIMD_4x32(R0); @@ -142,7 +148,9 @@ class SIMD_4x32 #elif defined(BOTAN_SIMD_USE_ALTIVEC) __vector unsigned char perm = vec_lvsl(0, static_cast<u32bit*>(nullptr)); +#if defined(BOTAN_TARGET_CPU_IS_BIG_ENDIAN) perm = vec_xor(perm, vec_splat_u8(3)); // bswap vector +#endif union { __vector unsigned int V; diff --git a/src/scripts/ci/travis/build.sh b/src/scripts/ci/travis/build.sh index 8cdb6ccc9..f2b4f2b67 100755 --- a/src/scripts/ci/travis/build.sh +++ b/src/scripts/ci/travis/build.sh @@ -2,97 +2,171 @@ set -ev which shellcheck > /dev/null && shellcheck "$0" # Run shellcheck on this if available -if [ "$BUILD_MODE" = "static" ]; then - CFG_FLAGS=(--disable-shared --via-amalgamation) -elif [ "$BUILD_MODE" = "shared" ] || [ "$BUILD_MODE" = "sonarqube" ]; then - CFG_FLAGS=() +MAKE_PREFIX="" +TEST_PREFIX="" +TEST_EXE=./botan-test +CFG_FLAGS=(--prefix=/tmp/botan-installation --cc=$CC --os=$TRAVIS_OS_NAME) + +CFG_FLAGS+=(--with-pkcs11) + +CC_BIN=$CXX + +if [ "$BUILD_MODE" = "static" ] || [ "$BUILD_MODE" = "mini-static" ]; then + CFG_FLAGS+=(--disable-shared --via-amalgamation) +elif [ "$BUILD_MODE" = "shared" ] || [ "$BUILD_MODE" = "mini-shared" ]; then + # No special flags required for shared lib build + CFG_FLAGS+=() +elif [ "$BUILD_MODE" = "sonarqube" ]; then + # No special flags required + CFG_FLAGS+=() +elif [ "$BUILD_MODE" = "parallel" ]; then + + if [ "$CC" = "gcc" ]; then + CFG_FLAGS+=(--with-cilkplus) + else + CFG_FLAGS+=(--with-openmp) + fi + elif [ "$BUILD_MODE" = "coverage" ]; then - CFG_FLAGS=(--with-coverage) + CFG_FLAGS+=(--with-coverage) elif [ "$BUILD_MODE" = "sanitizer" ]; then - CFG_FLAGS=(--with-sanitizers) + export ASAN_OPTIONS=detect_leaks=0 + CFG_FLAGS+=(--with-sanitizers) +elif [ "$BUILD_MODE" = "valgrind" ]; then + CFG_FLAGS+=(--with-valgrind --with-debug-info) + TEST_PREFIX="valgrind --track-origins=yes --error-exitcode=9 -v" fi -if [ "$MODULES" = "min" ]; then - CFG_FLAGS+=(--minimized-build --enable-modules=base) +if [ "$BUILD_MODE" = "mini-static" ] || [ "$BUILD_MODE" = "mini-shared" ]; then + CFG_FLAGS+=(--minimized-build --enable-modules="base,dev_random,system_rng,sha2_32,sha2_64,aes") +elif [ "$BUILD_MODE" = "valgrind" ]; then + # Valgrind on Travis on full build takes too long and the job is killed + # Prune to the most important stuff + CFG_FLAGS+=(--module-policy=modern --enable-modules=tls) + +elif [ "${BUILD_MODE:0:5}" != "cross" ]; then + # Only use external libraries when compiling natively + CFG_FLAGS+=(--with-bzip2 --with-lzma --with-sqlite --with-zlib) + + # Avoid OpenSSL when using dynamic checkers... + if [ "$BUILD_MODE" != "sanitizer" ] && [ "$BUILD_MODE" != "valgrind" ]; then + CFG_LFAGS+=(--with-openssl) + fi fi -if [ "$BOOST" = "y" ]; then +if [ "$TRAVIS_OS_NAME" = "osx" ] && [ "${BUILD_MODE:0:5}" != "cross" ]; then + # The Boost-specific codepaths are tested via the OS X CI CFG_FLAGS+=(--with-boost) fi -CFG_FLAGS+=(--with-pkcs11 --prefix=/tmp/botan-installation) +if [ "${BUILD_MODE:0:6}" = "cross-" ]; then + CFG_FLAGS+=(--disable-shared) -# enable ccache -if [ "$BUILD_MODE" != "sonarqube" ]; then - ccache --max-size=100M - ccache --show-stats - export CXX="ccache $CXX" -fi + if [ "$TRAVIS_OS_NAME" = "osx" ]; then + MAKE_PREFIX="xcrun --sdk iphoneos" + if [ "$BUILD_MODE" = "cross-arm32" ]; then + CFG_FLAGS+=(--cpu=armv7 --cc-abi-flags="-arch armv7 -arch armv7s -stdlib=libc++") + elif [ "$BUILD_MODE" = "cross-arm64" ]; then + CFG_FLAGS+=(--cpu=armv8-a --cc-abi-flags="-arch arm64 -stdlib=libc++") + fi + elif [ "$TRAVIS_OS_NAME" = "linux" ]; then + CFG_FLAGS+=(--cc-abi-flags="-static-libstdc++") -# configure -if [ "$TARGETOS" = "ios32" ]; then - ./configure.py "${CFG_FLAGS[@]}" --cpu=armv7 --cc=clang --cc-bin="$CXX" \ - --cc-abi-flags="-arch armv7 -arch armv7s -stdlib=libc++" + if [ "$BUILD_MODE" = "cross-arm32" ]; then + CC_BIN=arm-linux-gnueabihf-g++-4.8 + TEST_PREFIX="qemu-arm -L /usr/arm-linux-gnueabihf/" + CFG_FLAGS+=(--cpu=armv7) + CFG_FLAGS+=(--module-policy=modern --enable-modules=tls) + elif [ "$BUILD_MODE" = "cross-arm64" ]; then + CC_BIN=aarch64-linux-gnu-g++-4.8 + TEST_PREFIX="qemu-aarch64 -L /usr/aarch64-linux-gnu/" + CFG_FLAGS+=(--cpu=armv8-a) + CFG_FLAGS+=(--module-policy=modern --enable-modules=tls) + elif [ "$BUILD_MODE" = "cross-ppc32" ]; then + CC_BIN=powerpc-linux-gnu-g++-4.8 + TEST_PREFIX="qemu-ppc -L /usr/powerpc-linux-gnu/" + CFG_FLAGS+=(--cpu=ppc32) + CFG_FLAGS+=(--module-policy=modern --enable-modules=tls) + elif [ "$BUILD_MODE" = "cross-ppc64" ]; then + CC_BIN=powerpc64le-linux-gnu-g++-4.8 + TEST_PREFIX="qemu-ppc64 -L /usr/powerpc64le-linux-gnu/" + CFG_FLAGS+=(--cpu=ppc64) + CFG_FLAGS+=(--module-policy=modern --enable-modules=tls) + elif [ "$BUILD_MODE" = "cross-win32" ]; then + CC_BIN=i686-w64-mingw32-g++ + # No test prefix needed, PE executes as usual with Wine installed + CFG_FLAGS+=(--cpu=x86_32 --os=mingw --cc-abi-flags="-static") + TEST_EXE=./botan-test.exe + fi + fi +fi -elif [ "$TARGETOS" = "ios64" ]; then - ./configure.py "${CFG_FLAGS[@]}" --cpu=armv8-a --cc=clang --cc-bin="$CXX" \ - --cc-abi-flags="-arch arm64 -stdlib=libc++" +CFG_FLAGS+=(--cc-bin="ccache $CC_BIN") -else - ./configure.py "${CFG_FLAGS[@]}" --cc="$CC" --cc-bin="$CXX" \ - --with-bzip2 --with-lzma --with-openssl --with-sqlite --with-zlib +if [ "$BUILD_MODE" = "sonarqube" ]; then + MAKE_PREFIX="./build-wrapper-linux-x86/build-wrapper-linux-x86-64 --out-dir bw-outputs" fi -# build -if [ "${TARGETOS:0:3}" = "ios" ]; then - xcrun --sdk iphoneos make -j 2 -elif [ "$BUILD_MODE" = "sonarqube" ]; then - ./build-wrapper-linux-x86/build-wrapper-linux-x86-64 --out-dir bw-outputs make -j 2 -else - make -j 2 -fi +# configure +./configure.py "${CFG_FLAGS[@]}" -# Show post-build ccache stats -if [ "$BUILD_MODE" != "sonarqube" ]; then - ccache --show-stats -fi +# pre-build ccache stats +ccache --show-stats + +# build! +echo $MAKE_PREFIX make -j $BUILD_JOBS +time $MAKE_PREFIX make -j $BUILD_JOBS + +# post-build ccache stats +ccache --show-stats # Run SonarQube analysis -if [ "$TRAVIS_BRANCH" = "master" ] && [ "$TRAVIS_PULL_REQUEST" = "false" ] && [ "$BUILD_MODE" = "sonarqube" ]; then - # => This will run a full analysis of the project and push results to the SonarQube server. - # - # Analysis is done only on master so that build of branches don't push analyses to the same project and therefore "pollute" the results - echo "Starting analysis by SonarQube..." - sonar-scanner -Dsonar.login=$SONAR_TOKEN -fi -# PR analysis deactivated at least until custom quality profiles can be created -#elif [ "$TRAVIS_PULL_REQUEST" != "false" ] && [ -n "${GITHUB_TOKEN-}" ] && [ "$BUILD_MODE" = "sonarqube" ]; then - # => This will analyse the PR and display found issues as comments in the PR, but it won't push results to the SonarQube server - # - # For security reasons environment variables are not available on the pull requests - # coming from outside repositories - # http://docs.travis-ci.com/user/pull-requests/#Security-Restrictions-when-testing-Pull-Requests - # That's why the analysis does not need to be executed if the variable GITHUB_TOKEN is not defined. -# echo "Starting Pull Request analysis by SonarQube..." -# sonar-scanner -Dsonar.login=$SONAR_TOKEN \ -# -Dsonar.analysis.mode=preview \ -# -Dsonar.github.oauth=$GITHUB_TOKEN \ -# -Dsonar.github.repository=$TRAVIS_REPO_SLUG \ -# -Dsonar.github.pullRequest=$TRAVIS_PULL_REQUEST -#fi -# When neither on master branch nor on a non-external pull request => nothing to do - -if [ "$MODULES" != "min" ] && [ "${TARGETOS:0:3}" != "ios" ] && [ "$BUILD_MODE" != "sonarqube" ]; then - ./botan-test +if [ "$BUILD_MODE" = "sonarqube" ]; then + + if [ "$TRAVIS_BRANCH" = "master" ] && [ "$TRAVIS_PULL_REQUEST" = "false" ]; then + # => This will run a full analysis of the project and push results to the SonarQube server. + # + # Analysis is done only on master so that build of branches don't push analyses to the same project and therefore "pollute" the results + echo "Starting analysis by SonarQube..." + sonar-scanner "-Dsonar.login=$SONAR_TOKEN" + + # PR analysis deactivated at least until custom quality profiles can be created + elif false && [ "$TRAVIS_PULL_REQUEST" != "false" ] && [ -n "${GITHUB_TOKEN-}" ]; then + # => This will analyse the PR and display found issues as comments in the PR, but it won't push results to the SonarQube server + # + # For security reasons environment variables are not available on the pull requests + # coming from outside repositories + # http://docs.travis-ci.com/user/pull-requests/#Security-Restrictions-when-testing-Pull-Requests + # That's why the analysis does not need to be executed if the variable GITHUB_TOKEN is not defined. + echo "Starting Pull Request analysis by SonarQube..." + sonar-scanner -Dsonar.login=$SONAR_TOKEN \ + -Dsonar.analysis.mode=preview \ + -Dsonar.github.oauth=$GITHUB_TOKEN \ + -Dsonar.github.repository=$TRAVIS_REPO_SLUG \ + -Dsonar.github.pullRequest=$TRAVIS_PULL_REQUEST + fi + # When neither on master branch nor on a non-external pull request => nothing to do + fi + +if [ "$BUILD_MODE" == "sonarqube" ] || \ + ( [ "${BUILD_MODE:0:5}" == "cross" ] && [ "$TRAVIS_OS_NAME" == "osx" ] ); then + echo "Running tests disabled on this build type" +else + echo Running $TEST_PREFIX $TEST_EXE + time $TEST_PREFIX $TEST_EXE fi -if [ "$MODULES" != "min" ] && [ "$BUILD_MODE" = "shared" ] && [ "$TARGETOS" = "native" ] +# Run Python tests (need shared libs) +if [ "$BUILD_MODE" = "shared" ] then - python2 --version - python3 --version - LD_LIBRARY_PATH=. python2 src/python/botan.py - LD_LIBRARY_PATH=. python3 src/python/botan.py + # TODO: find all things in PATH that begin with python- and execute them :) + for py in python2 python3 + do + $py --version + LD_LIBRARY_PATH=. $py src/python/botan.py + done fi +# Test make install make install diff --git a/src/scripts/ci/travis/install.sh b/src/scripts/ci/travis/install.sh index a9d38ed80..987438127 100755 --- a/src/scripts/ci/travis/install.sh +++ b/src/scripts/ci/travis/install.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash set -ev which shellcheck > /dev/null && shellcheck "$0" # Run shellcheck on this if available @@ -18,13 +18,38 @@ if [ "$BUILD_MODE" = "sonarqube" ]; then unzip build-wrapper-linux-x86.zip fi +if [ "$TRAVIS_OS_NAME" = "linux" ]; then + if [ "$BUILD_MODE" = "valgrind" ] || [ "${BUILD_MODE:0:5}" = "cross" ]; then + sudo apt-get -qq update + + if [ "$BUILD_MODE" = "valgrind" ]; then + sudo apt-get install valgrind + elif [ "$BUILD_MODE" = "cross-arm32" ]; then + sudo apt-get install g++-4.8-arm-linux-gnueabihf libc6-dev-armhf-cross qemu-user + elif [ "$BUILD_MODE" = "cross-arm64" ]; then + sudo apt-get install g++-4.8-aarch64-linux-gnu libc6-dev-arm64-cross qemu-user + elif [ "$BUILD_MODE" = "cross-ppc32" ]; then + sudo apt-get install g++-4.8-powerpc-linux-gnu libc6-dev-powerpc-cross qemu-user + elif [ "$BUILD_MODE" = "cross-ppc64" ]; then + sudo apt-get install g++-4.8-powerpc64le-linux-gnu libc6-dev-ppc64el-cross qemu-user + elif [ "$BUILD_MODE" = "cross-win32" ]; then + sudo apt-get install g++-mingw-w64-i686 mingw-w64-i686-dev + + # See https://github.com/travis-ci/travis-ci/issues/6460 + sudo dpkg --add-architecture i386 + sudo apt-get -qq update # have to update again due to adding i386 above + sudo apt-get install wine + fi + fi +fi + if [ "$TRAVIS_OS_NAME" = "osx" ]; then # Workaround for https://github.com/Homebrew/homebrew/issues/42553 brew update || brew update brew install ccache - if [ "$TARGETOS" = "native" ]; then + if [ "$BUILD_MODE" != "cross-arm32" ] && [ "$BUILD_MODE" != "cross-arm64" ]; then brew install xz brew install python # python2 brew install python3 diff --git a/src/tests/test_ffi.cpp b/src/tests/test_ffi.cpp index 7f1182fe0..d48111683 100644 --- a/src/tests/test_ffi.cpp +++ b/src/tests/test_ffi.cpp @@ -406,13 +406,38 @@ class FFI_Unit_Tests : public Test TEST_FFI_OK(botan_pubkey_export, (pub, pubkey.data(), &pubkey_len, BOTAN_PRIVKEY_EXPORT_FLAG_PEM)); // export private key + std::vector<uint8_t> privkey; size_t privkey_len = 0; + + /* + * botan_privkey_export is bogus for several reasons. first it hardcodes a 300 msec + * pbkdf, instead of taking that as an argument. secondly, calling it twice not only + * returns different results (due to the encryption) but they may have different sizes, + * if the number of PBKDF iterations that is used in the two runs differs greatly, and + * ends up encoding as fewer bytes in the variable length ASN.1 encoding used in PKCS #8 + * private key encryption. + * + * here request the size but then add 10 bytes. this is an attempt to avoid occasional + * cases on CI where the above case occurs, and the build fails because on the second + * call, more space was required than the first call had returned. + */ + const size_t privkey_size_slop = 10; + + // call with nullptr to query the length TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE, botan_privkey_export, (priv, nullptr, &privkey_len, BOTAN_PRIVKEY_EXPORT_FLAG_DER)); - std::vector<uint8_t> privkey(privkey_len); + privkey.resize(privkey_len + privkey_size_slop); + privkey_len = privkey.size(); // set buffer size + TEST_FFI_OK(botan_privkey_export, (priv, privkey.data(), &privkey_len, BOTAN_PRIVKEY_EXPORT_FLAG_DER)); + privkey.resize(privkey_len); + + result.test_lt("Reasonable size", 64, privkey.size()); + + // Now again for PEM privkey_len = 0; + TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE, botan_privkey_export, (priv, nullptr, &privkey_len, BOTAN_PRIVKEY_EXPORT_FLAG_PEM)); privkey.resize(privkey_len); @@ -422,9 +447,10 @@ class FFI_Unit_Tests : public Test privkey_len = 0; TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE, botan_privkey_export_encrypted, (priv, nullptr, &privkey_len, rng, "password", "", BOTAN_PRIVKEY_EXPORT_FLAG_DER)); - privkey.resize(privkey_len); - TEST_FFI_OK(botan_privkey_export_encrypted, (priv, privkey.data(), &privkey_len, rng, "password", "", BOTAN_PRIVKEY_EXPORT_FLAG_DER)); + privkey.resize(privkey_len + privkey_size_slop); + privkey_len = privkey.size(); + TEST_FFI_OK(botan_privkey_export_encrypted, (priv, privkey.data(), &privkey_len, rng, "password", "", BOTAN_PRIVKEY_EXPORT_FLAG_DER)); privkey_len = 0; TEST_FFI_RC(BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE, botan_privkey_export_encrypted, (priv, nullptr, &privkey_len, rng, "password", "", BOTAN_PRIVKEY_EXPORT_FLAG_PEM)); @@ -642,6 +668,7 @@ class FFI_Unit_Tests : public Test Test::Result result("FFI"); botan_privkey_t priv; +#if defined(BOTAN_HAS_MCELIECE) if (TEST_FFI_OK(botan_privkey_create_mceliece, (&priv, rng, 2048, 50))) { botan_pubkey_t pub; @@ -683,6 +710,10 @@ class FFI_Unit_Tests : public Test TEST_FFI_OK(botan_pubkey_destroy, (pub)); TEST_FFI_OK(botan_privkey_destroy, (priv)); } +#else + // Not included, test that calling the FFI function work (and returns an error) + TEST_FFI_RC(BOTAN_FFI_ERROR_NOT_IMPLEMENTED, botan_privkey_create_mceliece, (&priv, rng, 2048, 50)); +#endif return result; } diff --git a/src/tests/unit_tls.cpp b/src/tests/unit_tls.cpp index f125bfcb5..33c6c245e 100644 --- a/src/tests/unit_tls.cpp +++ b/src/tests/unit_tls.cpp @@ -1,5 +1,6 @@ /* * (C) 2014,2015 Jack Lloyd +* 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -161,13 +162,18 @@ std::function<void (const byte[], size_t)> queue_inserter(std::vector<byte>& q) return [&](const byte buf[], size_t sz) { q.insert(q.end(), buf, buf + sz); }; } -void print_alert(Botan::TLS::Alert, const byte[], size_t) +void print_alert(Botan::TLS::Alert) + { + } + +void alert_cb_with_data(Botan::TLS::Alert, const byte[], size_t) { } Test::Result test_tls_handshake(Botan::TLS::Protocol_Version offer_version, Botan::Credentials_Manager& creds, - Botan::TLS::Policy& policy) + Botan::TLS::Policy& client_policy, + Botan::TLS::Policy& server_policy ) { Botan::RandomNumberGenerator& rng = Test::rng(); @@ -218,164 +224,216 @@ Test::Result test_tls_handshake(Botan::TLS::Protocol_Version offer_version, { std::vector<byte> c2s_traffic, s2c_traffic, client_recv, server_recv, client_sent, server_sent; - Botan::TLS::Server server(queue_inserter(s2c_traffic), - queue_inserter(server_recv), - print_alert, - handshake_complete, + std::unique_ptr<Botan::TLS::Callbacks> server_cb(new Botan::TLS::Compat_Callbacks( + queue_inserter(s2c_traffic), + queue_inserter(server_recv), + std::function<void (Botan::TLS::Alert, const byte[], size_t)>(alert_cb_with_data), + handshake_complete, + nullptr, + next_protocol_chooser)); + + // TLS::Server object constructed by new constructor using virtual callback interface. + std::unique_ptr<Botan::TLS::Server> server( + new Botan::TLS::Server(*server_cb, server_sessions, creds, - policy, + server_policy, rng, - next_protocol_chooser, - false); + false)); + + std::unique_ptr<Botan::TLS::Callbacks> client_cb(new Botan::TLS::Compat_Callbacks( + queue_inserter(c2s_traffic), + queue_inserter(client_recv), + std::function<void (Botan::TLS::Alert, const byte[], size_t)>(alert_cb_with_data), + handshake_complete)); - Botan::TLS::Client client(queue_inserter(c2s_traffic), - queue_inserter(client_recv), - print_alert, - handshake_complete, + // TLS::Client object constructed by new constructor using virtual callback interface. + std::unique_ptr<Botan::TLS::Client> client( + new Botan::TLS::Client(*client_cb, client_sessions, creds, - policy, + client_policy, rng, Botan::TLS::Server_Information("server.example.com"), offer_version, - protocols_offered); + protocols_offered)); size_t rounds = 0; - while(true) + // Test TLS using both new and legacy constructors. + for(size_t ctor_sel = 0; ctor_sel < 2; ctor_sel++) { - ++rounds; - - if(rounds > 25) + if(ctor_sel == 1) { - if(r <= 2) - result.test_failure("Still here after many rounds, deadlock?"); - break; + c2s_traffic.clear(); + s2c_traffic.clear(); + server_recv.clear(); + client_recv.clear(); + client_sent.clear(); + server_sent.clear(); + + // TLS::Server object constructed by legacy constructor. + server.reset( + new Botan::TLS::Server(queue_inserter(s2c_traffic), + queue_inserter(server_recv), + alert_cb_with_data, + handshake_complete, + server_sessions, + creds, + server_policy, + rng, + next_protocol_chooser, + false)); + + // TLS::Client object constructed by legacy constructor. + client.reset( + new Botan::TLS::Client(queue_inserter(c2s_traffic), + queue_inserter(client_recv), + alert_cb_with_data, + handshake_complete, + client_sessions, + creds, + server_policy, + rng, + Botan::TLS::Server_Information("server.example.com"), + offer_version, + protocols_offered)); } - if(handshake_done && (client.is_closed() || server.is_closed())) - break; - - if(client.is_active() && client_sent.empty()) + while(true) { - // Choose a len between 1 and 511 - const size_t c_len = 1 + rng.next_byte() + rng.next_byte(); - client_sent = unlock(rng.random_vec(c_len)); + ++rounds; - // TODO send in several records - client.send(client_sent); - } + if(rounds > 25) + { + if(r <= 2) + result.test_failure("Still here after many rounds, deadlock?"); + break; + } - if(server.is_active() && server_sent.empty()) - { - result.test_eq("server protocol", server.next_protocol(), "test/3"); + if(handshake_done && (client->is_closed() || server->is_closed())) + break; - const size_t s_len = 1 + rng.next_byte() + rng.next_byte(); - server_sent = unlock(rng.random_vec(s_len)); - server.send(server_sent); - } + if(client->is_active() && client_sent.empty()) + { + // Choose a len between 1 and 511 + const size_t c_len = 1 + rng.next_byte() + rng.next_byte(); + client_sent = unlock(rng.random_vec(c_len)); - const bool corrupt_client_data = (r == 3); - const bool corrupt_server_data = (r == 4); + // TODO send in several records + client->send(client_sent); + } - if(c2s_traffic.size() > 0) - { - /* - * Use this as a temp value to hold the queues as otherwise they - * might end up appending more in response to messages during the - * handshake. - */ - std::vector<byte> input; - std::swap(c2s_traffic, input); - - if(corrupt_server_data) + if(server->is_active() && server_sent.empty()) { - input = Test::mutate_vec(input, true); - size_t needed = server.received_data(input.data(), input.size()); - - size_t total_consumed = needed; + result.test_eq("server->protocol", server->next_protocol(), "test/3"); - while(needed > 0 && - result.test_lt("Never requesting more than max protocol len", needed, 18*1024) && - result.test_lt("Total requested is readonable", total_consumed, 128*1024)) - { - input.resize(needed); - Test::rng().randomize(input.data(), input.size()); - needed = server.received_data(input.data(), input.size()); - total_consumed += needed; - } + const size_t s_len = 1 + rng.next_byte() + rng.next_byte(); + server_sent = unlock(rng.random_vec(s_len)); + server->send(server_sent); } - else + + const bool corrupt_client_data = (r == 3); + const bool corrupt_server_data = (r == 4); + + if(c2s_traffic.size() > 0) { - size_t needed = server.received_data(input.data(), input.size()); - result.test_eq("full packet received", needed, 0); - } + /* + * Use this as a temp value to hold the queues as otherwise they + * might end up appending more in response to messages during the + * handshake. + */ + std::vector<byte> input; + std::swap(c2s_traffic, input); + + if(corrupt_server_data) + { + input = Test::mutate_vec(input, true); + size_t needed = server->received_data(input.data(), input.size()); - continue; - } + size_t total_consumed = needed; - if(s2c_traffic.size() > 0) - { - std::vector<byte> input; - std::swap(s2c_traffic, input); + while(needed > 0 && + result.test_lt("Never requesting more than max protocol len", needed, 18*1024) && + result.test_lt("Total requested is readonable", total_consumed, 128*1024)) + { + input.resize(needed); + Test::rng().randomize(input.data(), input.size()); + needed = server->received_data(input.data(), input.size()); + total_consumed += needed; + } + } + else + { + size_t needed = server->received_data(input.data(), input.size()); + result.test_eq("full packet received", needed, 0); + } + + continue; + } - if(corrupt_client_data) + if(s2c_traffic.size() > 0) { - input = Test::mutate_vec(input, true); - size_t needed = client.received_data(input.data(), input.size()); + std::vector<byte> input; + std::swap(s2c_traffic, input); + + if(corrupt_client_data) + { + input = Test::mutate_vec(input, true); + size_t needed = client->received_data(input.data(), input.size()); - size_t total_consumed = 0; + size_t total_consumed = 0; - while(needed > 0 && result.test_lt("Never requesting more than max protocol len", needed, 18*1024)) + while(needed > 0 && result.test_lt("Never requesting more than max protocol len", needed, 18*1024)) + { + input.resize(needed); + Test::rng().randomize(input.data(), input.size()); + needed = client->received_data(input.data(), input.size()); + total_consumed += needed; + } + } + else { - input.resize(needed); - Test::rng().randomize(input.data(), input.size()); - needed = client.received_data(input.data(), input.size()); - total_consumed += needed; + size_t needed = client->received_data(input.data(), input.size()); + result.test_eq("full packet received", needed, 0); } + + continue; } - else + + if(client_recv.size()) { - size_t needed = client.received_data(input.data(), input.size()); - result.test_eq("full packet received", needed, 0); + result.test_eq("client recv", client_recv, server_sent); } - continue; - } - - if(client_recv.size()) - { - result.test_eq("client recv", client_recv, server_sent); - } - - if(server_recv.size()) - { - result.test_eq("server recv", server_recv, client_sent); - } + if(server_recv.size()) + { + result.test_eq("server->recv", server_recv, client_sent); + } - if(r > 2) - { - if(client_recv.size() && server_recv.size()) + if(r > 2) { - result.test_failure("Negotiated in the face of data corruption " + std::to_string(r)); + if(client_recv.size() && server_recv.size()) + { + result.test_failure("Negotiated in the face of data corruption " + std::to_string(r)); + } } - } - if(client.is_closed() && server.is_closed()) - break; + if(client->is_closed() && server->is_closed()) + break; - if(server_recv.size() && client_recv.size()) - { - Botan::SymmetricKey client_key = client.key_material_export("label", "context", 32); - Botan::SymmetricKey server_key = server.key_material_export("label", "context", 32); + if(server_recv.size() && client_recv.size()) + { + Botan::SymmetricKey client_key = client->key_material_export("label", "context", 32); + Botan::SymmetricKey server_key = server->key_material_export("label", "context", 32); - result.test_eq("TLS key material export", client_key.bits_of(), server_key.bits_of()); + result.test_eq("TLS key material export", client_key.bits_of(), server_key.bits_of()); - if(r % 2 == 0) - client.close(); - else - server.close(); + if(r % 2 == 0) + client->close(); + else + server->close(); + } } } } @@ -397,9 +455,17 @@ Test::Result test_tls_handshake(Botan::TLS::Protocol_Version offer_version, return result; } +Test::Result test_tls_handshake(Botan::TLS::Protocol_Version offer_version, + Botan::Credentials_Manager& creds, + Botan::TLS::Policy& policy ) + { + return test_tls_handshake(offer_version, creds, policy, policy); + } + Test::Result test_dtls_handshake(Botan::TLS::Protocol_Version offer_version, Botan::Credentials_Manager& creds, - Botan::TLS::Policy& policy) + Botan::TLS::Policy& client_policy, + Botan::TLS::Policy& server_policy ) { BOTAN_ASSERT(offer_version.is_datagram_protocol(), "Test is for datagram version"); @@ -444,183 +510,232 @@ Test::Result test_dtls_handshake(Botan::TLS::Protocol_Version offer_version, { std::vector<byte> c2s_traffic, s2c_traffic, client_recv, server_recv, client_sent, server_sent; - Botan::TLS::Server server(queue_inserter(s2c_traffic), - queue_inserter(server_recv), - print_alert, - handshake_complete, + std::unique_ptr<Botan::TLS::Callbacks> server_cb(new Botan::TLS::Compat_Callbacks( + queue_inserter(s2c_traffic), + queue_inserter(server_recv), + std::function<void (Botan::TLS::Alert)>(print_alert), + handshake_complete, + nullptr, + next_protocol_chooser)); + + std::unique_ptr<Botan::TLS::Callbacks> client_cb(new Botan::TLS::Compat_Callbacks( + queue_inserter(c2s_traffic), + queue_inserter(client_recv), + std::function<void (Botan::TLS::Alert)>(print_alert), + handshake_complete)); + + // TLS::Server object constructed by new constructor using virtual callback interface. + std::unique_ptr<Botan::TLS::Server> server( + new Botan::TLS::Server(*server_cb, server_sessions, creds, - policy, + server_policy, rng, - next_protocol_chooser, - true); + true)); - Botan::TLS::Client client(queue_inserter(c2s_traffic), - queue_inserter(client_recv), - print_alert, - handshake_complete, + // TLS::Client object constructed by new constructor using virtual callback interface. + std::unique_ptr<Botan::TLS::Client> client( + new Botan::TLS::Client(*client_cb, client_sessions, creds, - policy, + client_policy, rng, Botan::TLS::Server_Information("server.example.com"), offer_version, - protocols_offered); + protocols_offered)); size_t rounds = 0; - while(true) + // Test DTLS using both new and legacy constructors. + for(size_t ctor_sel = 0; ctor_sel < 2; ctor_sel++) { - // TODO: client and server should be in different threads - std::this_thread::sleep_for(std::chrono::milliseconds(rng.next_byte() % 2)); - ++rounds; - - if(rounds > 100) + if(ctor_sel == 1) { - result.test_failure("Still here after many rounds"); - break; + c2s_traffic.clear(); + s2c_traffic.clear(); + server_recv.clear(); + client_recv.clear(); + client_sent.clear(); + server_sent.clear(); + // TLS::Server object constructed by legacy constructor. + server.reset( + new Botan::TLS::Server(queue_inserter(s2c_traffic), + queue_inserter(server_recv), + alert_cb_with_data, + handshake_complete, + server_sessions, + creds, + server_policy, + rng, + next_protocol_chooser, + true)); + + // TLS::Client object constructed by legacy constructor. + client.reset( + new Botan::TLS::Client(queue_inserter(c2s_traffic), + queue_inserter(client_recv), + alert_cb_with_data, + handshake_complete, + client_sessions, + creds, + client_policy, + rng, + Botan::TLS::Server_Information("server.example.com"), + offer_version, + protocols_offered)); } - if(handshake_done && (client.is_closed() || server.is_closed())) - break; - - if(client.is_active() && client_sent.empty()) + while(true) { - // Choose a len between 1 and 511 and send random chunks: - const size_t c_len = 1 + rng.next_byte() + rng.next_byte(); - client_sent = unlock(rng.random_vec(c_len)); - - // TODO send multiple parts - client.send(client_sent); - } + // TODO: client and server should be in different threads + std::this_thread::sleep_for(std::chrono::milliseconds(rng.next_byte() % 2)); + ++rounds; - if(server.is_active() && server_sent.empty()) - { - result.test_eq("server ALPN", server.next_protocol(), "test/3"); - - const size_t s_len = 1 + rng.next_byte() + rng.next_byte(); - server_sent = unlock(rng.random_vec(s_len)); - server.send(server_sent); - } + if(rounds > 100) + { + result.test_failure("Still here after many rounds"); + break; + } - const bool corrupt_client_data = (r == 3 && rng.next_byte() % 3 <= 1 && rounds < 10); - const bool corrupt_server_data = (r == 4 && rng.next_byte() % 3 <= 1 && rounds < 10); + if(handshake_done && (client->is_closed() || server->is_closed())) + break; - if(c2s_traffic.size() > 0) - { - /* - * Use this as a temp value to hold the queues as otherwise they - * might end up appending more in response to messages during the - * handshake. - */ - std::vector<byte> input; - std::swap(c2s_traffic, input); - - if(corrupt_server_data) + if(client->is_active() && client_sent.empty()) { - try - { - input = Test::mutate_vec(input, true); - size_t needed = server.received_data(input.data(), input.size()); - - if(needed > 0 && result.test_lt("Never requesting more than max protocol len", needed, 18*1024)) - { - input.resize(needed); - Test::rng().randomize(input.data(), input.size()); - client.received_data(input.data(), input.size()); - } - } - catch(std::exception&) - { - result.test_note("corruption caused server exception"); - } + // Choose a len between 1 and 511, todo use random chunks + const size_t c_len = 1 + rng.next_byte() + rng.next_byte(); + client_sent = unlock(rng.random_vec(c_len)); + client->send(client_sent); } - else + + if(server->is_active() && server_sent.empty()) { - try - { - size_t needed = server.received_data(input.data(), input.size()); - result.test_eq("full packet received", needed, 0); - } - catch(std::exception& e) - { - result.test_failure("server error", e.what()); - } - } + result.test_eq("server ALPN", server->next_protocol(), "test/3"); - continue; - } + const size_t s_len = 1 + rng.next_byte() + rng.next_byte(); + server_sent = unlock(rng.random_vec(s_len)); + server->send(server_sent); + } - if(s2c_traffic.size() > 0) - { - std::vector<byte> input; - std::swap(s2c_traffic, input); + const bool corrupt_client_data = (r == 3 && rng.next_byte() % 3 <= 1 && rounds < 10); + const bool corrupt_server_data = (r == 4 && rng.next_byte() % 3 <= 1 && rounds < 10); - if(corrupt_client_data) + if(c2s_traffic.size() > 0) { - try + /* + * Use this as a temp value to hold the queues as otherwise they + * might end up appending more in response to messages during the + * handshake. + */ + std::vector<byte> input; + std::swap(c2s_traffic, input); + + if(corrupt_server_data) { - input = Test::mutate_vec(input, true); - size_t needed = client.received_data(input.data(), input.size()); - - if(needed > 0 && result.test_lt("Never requesting more than max protocol len", needed, 18*1024)) + try { - input.resize(needed); - Test::rng().randomize(input.data(), input.size()); - client.received_data(input.data(), input.size()); + input = Test::mutate_vec(input, true); + size_t needed = server->received_data(input.data(), input.size()); + + if(needed > 0 && result.test_lt("Never requesting more than max protocol len", needed, 18*1024)) + { + input.resize(needed); + Test::rng().randomize(input.data(), input.size()); + client->received_data(input.data(), input.size()); + } + } + catch(std::exception&) + { + result.test_note("corruption caused server exception"); } } - catch(std::exception&) + else { - result.test_note("corruption caused client exception"); + try + { + size_t needed = server->received_data(input.data(), input.size()); + result.test_eq("full packet received", needed, 0); + } + catch(std::exception& e) + { + result.test_failure("server error", e.what()); + } } + + continue; } - else + + if(s2c_traffic.size() > 0) { - try + std::vector<byte> input; + std::swap(s2c_traffic, input); + + if(corrupt_client_data) { - size_t needed = client.received_data(input.data(), input.size()); - result.test_eq("full packet received", needed, 0); + try + { + input = Test::mutate_vec(input, true); + size_t needed = client->received_data(input.data(), input.size()); + + if(needed > 0 && result.test_lt("Never requesting more than max protocol len", needed, 18*1024)) + { + input.resize(needed); + Test::rng().randomize(input.data(), input.size()); + client->received_data(input.data(), input.size()); + } + } + catch(std::exception&) + { + result.test_note("corruption caused client exception"); + } } - catch(std::exception& e) + else { - result.test_failure("client error", e.what()); + try + { + size_t needed = client->received_data(input.data(), input.size()); + result.test_eq("full packet received", needed, 0); + } + catch(std::exception& e) + { + result.test_failure("client error", e.what()); + } } - } - continue; - } + continue; + } - // If we corrupted a DTLS application message, resend it: - if(client.is_active() && corrupt_client_data && server_recv.empty()) - client.send(client_sent); - if(server.is_active() && corrupt_server_data && client_recv.empty()) - server.send(server_sent); + // If we corrupted a DTLS application message, resend it: + if(client->is_active() && corrupt_client_data && server_recv.empty()) + client->send(client_sent); + if(server->is_active() && corrupt_server_data && client_recv.empty()) + server->send(server_sent); - if(client_recv.size()) - { - result.test_eq("client recv", client_recv, server_sent); - } + if(client_recv.size()) + { + result.test_eq("client recv", client_recv, server_sent); + } - if(server_recv.size()) - { - result.test_eq("server recv", server_recv, client_sent); - } + if(server_recv.size()) + { + result.test_eq("server recv", server_recv, client_sent); + } - if(client.is_closed() && server.is_closed()) - break; + if(client->is_closed() && server->is_closed()) + break; - if(server_recv.size() && client_recv.size()) - { - Botan::SymmetricKey client_key = client.key_material_export("label", "context", 32); - Botan::SymmetricKey server_key = server.key_material_export("label", "context", 32); + if(server_recv.size() && client_recv.size()) + { + Botan::SymmetricKey client_key = client->key_material_export("label", "context", 32); + Botan::SymmetricKey server_key = server->key_material_export("label", "context", 32); - result.test_eq("key material export", client_key.bits_of(), server_key.bits_of()); + result.test_eq("key material export", client_key.bits_of(), server_key.bits_of()); - if(r % 2 == 0) - client.close(); - else - server.close(); + if(r % 2 == 0) + client->close(); + else + server->close(); + } } } } @@ -641,6 +756,13 @@ Test::Result test_dtls_handshake(Botan::TLS::Protocol_Version offer_version, return result; } +Test::Result test_dtls_handshake(Botan::TLS::Protocol_Version offer_version, + Botan::Credentials_Manager& creds, + Botan::TLS::Policy& policy) + { + return test_dtls_handshake(offer_version, creds, policy, policy); + } + class Test_Policy : public Botan::TLS::Text_Policy { public: @@ -650,6 +772,8 @@ class Test_Policy : public Botan::TLS::Text_Policy size_t dtls_initial_timeout() const override { return 1; } size_t dtls_maximum_timeout() const override { return 8; } + + size_t minimum_rsa_bits() const { return 1024; } }; @@ -719,6 +843,25 @@ class TLS_Unit_Tests : public Test results.push_back(test_tls_handshake(Botan::TLS::Protocol_Version::TLS_V12, *basic_creds, policy)); results.push_back(test_dtls_handshake(Botan::TLS::Protocol_Version::DTLS_V12, *basic_creds, policy)); + policy.set("negotiate_encrypt_then_mac", "false"); + policy.set("key_exchange_methods", "ECDH"); + policy.set("ciphers", "AES-128"); + Test_Policy server_policy; + server_policy.set("key_exchange_methods", "ECDH"); + server_policy.set("ciphers", "AES-128"); + server_policy.set("negotiate_encrypt_then_mac", "true"); + results.push_back(test_tls_handshake(Botan::TLS::Protocol_Version::TLS_V10, *basic_creds, policy, server_policy)); + results.push_back(test_tls_handshake(Botan::TLS::Protocol_Version::TLS_V11, *basic_creds, policy, server_policy)); + results.push_back(test_tls_handshake(Botan::TLS::Protocol_Version::TLS_V12, *basic_creds, policy, server_policy)); + results.push_back(test_dtls_handshake(Botan::TLS::Protocol_Version::DTLS_V10, *basic_creds, policy, server_policy)); + results.push_back(test_dtls_handshake(Botan::TLS::Protocol_Version::DTLS_V12, *basic_creds, policy, server_policy)); + + policy.set("negotiate_encrypt_then_mac", "true"); + policy.set("ciphers", "AES-128/GCM"); + server_policy.set("ciphers", "AES-128/GCM"); + results.push_back(test_tls_handshake(Botan::TLS::Protocol_Version::TLS_V12, *basic_creds, policy, server_policy)); + results.push_back(test_dtls_handshake(Botan::TLS::Protocol_Version::DTLS_V12, *basic_creds, policy, server_policy)); + return results; } diff --git a/tls-policy/BSI_TR-02102-2.txt b/tls-policy/BSI_TR-02102-2.txt new file mode 100644 index 000000000..bdc844b46 --- /dev/null +++ b/tls-policy/BSI_TR-02102-2.txt @@ -0,0 +1,20 @@ +allow_tls10=false +allow_tls11=false +allow_tls12=true +allow_dtls10=false +allow_dtls12=false + +ciphers=AES-256/GCM AES-128/GCM AES-256 AES-128 +signature_hashes=SHA-384 SHA-256 +macs=AEAD SHA-384 SHA-256 +key_exchange_methods=ECDH DH ECDHE_PSK DHE_PSK ECDH_PSK +signature_methods=ECDSA RSA DSA +ecc_curves=brainpool512r1 brainpool384r1 brainpool256r1 secp384r1 secp256r1 +minimum_dh_group_size=2000 +minimum_ecdh_group_size=250 +minimum_rsa_bits=2000 +ecc_curves=brainpool512r1 brainpool384r1 brainpool256r1 + +allow_insecure_renegotiation=false +allow_server_initiated_renegotiation=true +server_uses_own_ciphersuite_preferences=true |