aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.travis.yml126
-rw-r--r--botan_version.py2
-rwxr-xr-xconfigure.py21
-rw-r--r--doc/license.txt2
-rw-r--r--doc/manual/tls.rst174
-rw-r--r--doc/news.rst47
-rw-r--r--doc/security.rst18
-rw-r--r--doc/todo.rst5
-rw-r--r--readme.rst6
-rw-r--r--src/build-data/cc/clang.txt5
-rw-r--r--src/build-data/cc/gcc.txt7
-rw-r--r--src/build-data/policy/bsi.txt7
-rw-r--r--src/build-data/policy/modern.txt5
-rw-r--r--src/cli/credentials.h2
-rw-r--r--src/cli/tls_client.cpp38
-rw-r--r--src/cli/tls_proxy.cpp18
-rw-r--r--src/cli/tls_server.cpp23
-rw-r--r--src/cli/utils.cpp2
-rw-r--r--src/lib/cert/x509/x509_ca.h1
-rw-r--r--src/lib/cert/x509/x509cert.cpp8
-rw-r--r--src/lib/math/bigint/big_ops2.cpp6
-rw-r--r--src/lib/math/bigint/big_ops3.cpp5
-rw-r--r--src/lib/math/ec_gfp/curve_gfp.cpp19
-rw-r--r--src/lib/math/mp/mp_core.h13
-rw-r--r--src/lib/math/mp/mp_karat.cpp58
-rw-r--r--src/lib/math/mp/mp_monty.cpp25
-rw-r--r--src/lib/math/numbertheory/mp_numth.cpp16
-rw-r--r--src/lib/math/numbertheory/powm_mnt.cpp28
-rw-r--r--src/lib/pubkey/curve25519/donna.cpp76
-rw-r--r--src/lib/pubkey/pubkey.cpp6
-rw-r--r--src/lib/rng/hmac_rng/hmac_rng.cpp2
-rw-r--r--src/lib/tls/info.txt1
-rw-r--r--src/lib/tls/msg_cert_verify.cpp2
-rw-r--r--src/lib/tls/msg_certificate.cpp2
-rw-r--r--src/lib/tls/msg_client_hello.cpp29
-rw-r--r--src/lib/tls/msg_client_kex.cpp11
-rw-r--r--src/lib/tls/msg_server_hello.cpp33
-rw-r--r--src/lib/tls/msg_server_kex.cpp2
-rw-r--r--src/lib/tls/tls_blocking.cpp14
-rw-r--r--src/lib/tls/tls_blocking.h5
-rw-r--r--src/lib/tls/tls_callbacks.h203
-rw-r--r--src/lib/tls/tls_channel.cpp278
-rw-r--r--src/lib/tls/tls_channel.h40
-rw-r--r--src/lib/tls/tls_ciphersuite.cpp7
-rw-r--r--src/lib/tls/tls_ciphersuite.h5
-rw-r--r--src/lib/tls/tls_client.cpp56
-rw-r--r--src/lib/tls/tls_client.h53
-rw-r--r--src/lib/tls/tls_extensions.cpp16
-rw-r--r--src/lib/tls/tls_extensions.h24
-rw-r--r--src/lib/tls/tls_handshake_msg.h4
-rw-r--r--src/lib/tls/tls_handshake_state.cpp10
-rw-r--r--src/lib/tls/tls_handshake_state.h14
-rw-r--r--src/lib/tls/tls_messages.h76
-rw-r--r--src/lib/tls/tls_policy.cpp103
-rw-r--r--src/lib/tls/tls_policy.h121
-rw-r--r--src/lib/tls/tls_record.cpp411
-rw-r--r--src/lib/tls/tls_record.h94
-rw-r--r--src/lib/tls/tls_server.cpp838
-rw-r--r--src/lib/tls/tls_server.h73
-rw-r--r--src/lib/tls/tls_session.cpp4
-rw-r--r--src/lib/tls/tls_session.h9
-rw-r--r--src/lib/utils/calendar.cpp11
-rw-r--r--src/lib/utils/os_utils.cpp81
-rw-r--r--src/lib/utils/simd/simd_32.h10
-rwxr-xr-xsrc/scripts/ci/travis/build.sh212
-rwxr-xr-xsrc/scripts/ci/travis/install.sh29
-rw-r--r--src/tests/test_ffi.cpp37
-rw-r--r--src/tests/unit_tls.cpp635
-rw-r--r--tls-policy/BSI_TR-02102-2.txt20
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:
+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