From af26cc4d8542fb6aaea9bfc5aaae1b139faee94a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Korthaus?= Date: Wed, 25 Jul 2018 13:01:19 +0200 Subject: [PATCH 01/23] botan: Add Botan plugin to libstrongswan --- configure.ac | 10 + src/libstrongswan/Makefile.am | 7 + src/libstrongswan/plugins/botan/Makefile.am | 30 + .../plugins/botan/botan_crypter.c | 195 ++++ .../plugins/botan/botan_crypter.h | 58 ++ .../plugins/botan/botan_diffie_hellman.c | 261 +++++ .../plugins/botan/botan_diffie_hellman.h | 72 ++ .../plugins/botan/botan_ec_diffie_hellman.c | 248 +++++ .../plugins/botan/botan_ec_diffie_hellman.h | 57 ++ .../plugins/botan/botan_ec_private_key.c | 581 +++++++++++ .../plugins/botan/botan_ec_private_key.h | 78 ++ .../plugins/botan/botan_ec_public_key.c | 462 +++++++++ .../plugins/botan/botan_ec_public_key.h | 55 + src/libstrongswan/plugins/botan/botan_gcm.c | 349 +++++++ src/libstrongswan/plugins/botan/botan_gcm.h | 53 + .../plugins/botan/botan_hasher.c | 152 +++ .../plugins/botan/botan_hasher.h | 54 + src/libstrongswan/plugins/botan/botan_hmac.c | 178 ++++ src/libstrongswan/plugins/botan/botan_hmac.h | 53 + .../plugins/botan/botan_plugin.c | 282 +++++ .../plugins/botan/botan_plugin.h | 50 + src/libstrongswan/plugins/botan/botan_rng.c | 128 +++ src/libstrongswan/plugins/botan/botan_rng.h | 57 ++ .../plugins/botan/botan_rsa_private_key.c | 964 ++++++++++++++++++ .../plugins/botan/botan_rsa_private_key.h | 72 ++ .../plugins/botan/botan_rsa_public_key.c | 601 +++++++++++ .../plugins/botan/botan_rsa_public_key.h | 59 ++ src/libstrongswan/plugins/botan/botan_util.c | 43 + src/libstrongswan/plugins/botan/botan_util.h | 41 + 29 files changed, 5250 insertions(+) create mode 100644 src/libstrongswan/plugins/botan/Makefile.am create mode 100644 src/libstrongswan/plugins/botan/botan_crypter.c create mode 100644 src/libstrongswan/plugins/botan/botan_crypter.h create mode 100644 src/libstrongswan/plugins/botan/botan_diffie_hellman.c create mode 100644 src/libstrongswan/plugins/botan/botan_diffie_hellman.h create mode 100644 src/libstrongswan/plugins/botan/botan_ec_diffie_hellman.c create mode 100644 src/libstrongswan/plugins/botan/botan_ec_diffie_hellman.h create mode 100644 src/libstrongswan/plugins/botan/botan_ec_private_key.c create mode 100644 src/libstrongswan/plugins/botan/botan_ec_private_key.h create mode 100644 src/libstrongswan/plugins/botan/botan_ec_public_key.c create mode 100644 src/libstrongswan/plugins/botan/botan_ec_public_key.h create mode 100644 src/libstrongswan/plugins/botan/botan_gcm.c create mode 100644 src/libstrongswan/plugins/botan/botan_gcm.h create mode 100644 src/libstrongswan/plugins/botan/botan_hasher.c create mode 100644 src/libstrongswan/plugins/botan/botan_hasher.h create mode 100644 src/libstrongswan/plugins/botan/botan_hmac.c create mode 100644 src/libstrongswan/plugins/botan/botan_hmac.h create mode 100644 src/libstrongswan/plugins/botan/botan_plugin.c create mode 100644 src/libstrongswan/plugins/botan/botan_plugin.h create mode 100644 src/libstrongswan/plugins/botan/botan_rng.c create mode 100644 src/libstrongswan/plugins/botan/botan_rng.h create mode 100644 src/libstrongswan/plugins/botan/botan_rsa_private_key.c create mode 100644 src/libstrongswan/plugins/botan/botan_rsa_private_key.h create mode 100644 src/libstrongswan/plugins/botan/botan_rsa_public_key.c create mode 100644 src/libstrongswan/plugins/botan/botan_rsa_public_key.h create mode 100644 src/libstrongswan/plugins/botan/botan_util.c create mode 100644 src/libstrongswan/plugins/botan/botan_util.h diff --git a/configure.ac b/configure.ac index 26fd707b7..ae05100bb 100644 --- a/configure.ac +++ b/configure.ac @@ -126,6 +126,7 @@ ARG_DISBL_SET([aes], [disable AES software implementation plugin.]) ARG_ENABL_SET([af-alg], [enable AF_ALG crypto interface to Linux Crypto API.]) ARG_ENABL_SET([bliss], [enable BLISS software implementation plugin.]) ARG_ENABL_SET([blowfish], [enable Blowfish software implementation plugin.]) +ARG_ENABL_SET([botan], [enables the Botan crypto plugin.]) ARG_ENABL_SET([ccm], [enables the CCM AEAD wrapper crypto plugin.]) ARG_ENABL_SET([chapoly], [enables the ChaCha20/Poly1305 AEAD plugin.]) ARG_DISBL_SET([cmac], [disable CMAC crypto implementation plugin.]) @@ -1149,6 +1150,12 @@ if test x$gcrypt = xtrue; then ) fi +if test x$botan = xtrue; then + PKG_CHECK_MODULES(botan, [botan-2]) + AC_SUBST(botan_CFLAGS) + AC_SUBST(botan_LIBS) +fi + if test x$uci = xtrue; then AC_CHECK_LIB([uci],[uci_alloc_context],[LIBS="$LIBS"],[AC_MSG_ERROR([UCI library libuci not found])],[]) AC_CHECK_HEADER([uci.h],,[AC_MSG_ERROR([UCI header uci.h not found!])]) @@ -1399,6 +1406,7 @@ ADD_PLUGIN([pem], [s charon scepclient pki scripts manager meds ADD_PLUGIN([padlock], [s charon]) ADD_PLUGIN([openssl], [s charon scepclient pki scripts manager medsrv attest nm cmd aikgen]) ADD_PLUGIN([gcrypt], [s charon scepclient pki scripts manager medsrv attest nm cmd aikgen]) +ADD_PLUGIN([botan], [s charon scepclient pki scripts manager medsrv attest nm cmd aikgen]) ADD_PLUGIN([af-alg], [s charon scepclient pki scripts medsrv attest nm cmd aikgen]) ADD_PLUGIN([fips-prf], [s charon nm cmd]) ADD_PLUGIN([gmp], [s charon scepclient pki scripts manager medsrv attest nm cmd aikgen fuzz]) @@ -1568,6 +1576,7 @@ AM_CONDITIONAL(USE_SQLITE, test x$sqlite = xtrue) AM_CONDITIONAL(USE_PADLOCK, test x$padlock = xtrue) AM_CONDITIONAL(USE_OPENSSL, test x$openssl = xtrue) AM_CONDITIONAL(USE_GCRYPT, test x$gcrypt = xtrue) +AM_CONDITIONAL(USE_BOTAN, test x$botan = xtrue) AM_CONDITIONAL(USE_AGENT, test x$agent = xtrue) AM_CONDITIONAL(USE_KEYCHAIN, test x$keychain = xtrue) AM_CONDITIONAL(USE_PKCS11, test x$pkcs11 = xtrue) @@ -1846,6 +1855,7 @@ AC_CONFIG_FILES([ src/libstrongswan/plugins/padlock/Makefile src/libstrongswan/plugins/openssl/Makefile src/libstrongswan/plugins/gcrypt/Makefile + src/libstrongswan/plugins/botan/Makefile src/libstrongswan/plugins/agent/Makefile src/libstrongswan/plugins/keychain/Makefile src/libstrongswan/plugins/pkcs11/Makefile diff --git a/src/libstrongswan/Makefile.am b/src/libstrongswan/Makefile.am index 66539a879..e6d7ce74b 100644 --- a/src/libstrongswan/Makefile.am +++ b/src/libstrongswan/Makefile.am @@ -565,6 +565,13 @@ if MONOLITHIC endif endif +if USE_BOTAN + SUBDIRS += plugins/botan +if MONOLITHIC + libstrongswan_la_LIBADD += plugins/botan/libstrongswan-botan.la +endif +endif + if USE_FIPS_PRF SUBDIRS += plugins/fips_prf if MONOLITHIC diff --git a/src/libstrongswan/plugins/botan/Makefile.am b/src/libstrongswan/plugins/botan/Makefile.am new file mode 100644 index 000000000..95df44130 --- /dev/null +++ b/src/libstrongswan/plugins/botan/Makefile.am @@ -0,0 +1,30 @@ +AM_CPPFLAGS = \ + -I$(top_srcdir)/src/libstrongswan + +AM_CFLAGS = \ + $(PLUGIN_CFLAGS) \ + $(botan_CFLAGS) + +if MONOLITHIC +noinst_LTLIBRARIES = libstrongswan-botan.la +else +plugin_LTLIBRARIES = libstrongswan-botan.la +endif + +libstrongswan_botan_la_SOURCES = \ + botan_plugin.h botan_plugin.c \ + botan_rng.h botan_rng.c \ + botan_hasher.h botan_hasher.c \ + botan_hmac.h botan_hmac.c \ + botan_crypter.h botan_crypter.c \ + botan_rsa_public_key.h botan_rsa_public_key.c \ + botan_rsa_private_key.h botan_rsa_private_key.c \ + botan_diffie_hellman.h botan_diffie_hellman.c \ + botan_ec_diffie_hellman.h botan_ec_diffie_hellman.c \ + botan_ec_public_key.h botan_ec_public_key.c \ + botan_ec_private_key.h botan_ec_private_key.c \ + botan_util.c \ + botan_gcm.h botan_gcm.c + +libstrongswan_botan_la_LDFLAGS = -module -avoid-version +libstrongswan_botan_la_LIBADD = $(botan_LIBS) diff --git a/src/libstrongswan/plugins/botan/botan_crypter.c b/src/libstrongswan/plugins/botan/botan_crypter.c new file mode 100644 index 000000000..af8fe4c43 --- /dev/null +++ b/src/libstrongswan/plugins/botan/botan_crypter.c @@ -0,0 +1,195 @@ +/* + * Copyright (C) 2018 René Korthaus + * Copyright (C) 2018 Konstantinos Kolelis + * Copyright (C) 2018 Tobias Hommel + * Rohde & Schwarz Cybersecurity GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "botan_crypter.h" + +#include + +typedef struct private_botan_crypter_t private_botan_crypter_t; + +/** + * Private data of botan_crypter_t + */ +struct private_botan_crypter_t { + + /** + * Public part of this class + */ + botan_crypter_t public; + + /** + * The key + */ + chunk_t key; + + /** + * The cipher name + */ + const char* cipher_name; +}; + +/** + * Do the actual en/decryption + */ +static bool crypt(private_botan_crypter_t *this, chunk_t data, chunk_t iv, + chunk_t *dst, uint32_t init_flag) +{ + botan_cipher_t cipher; + size_t output_written = 0; + size_t input_consumed = 0; + uint8_t *in, *out; + bool success = FALSE; + + in = data.ptr; + if (dst) + { + *dst= chunk_alloc(data.len); + out = dst->ptr; + } + else + { + out = data.ptr; + } + + if (botan_cipher_init(&cipher, this->cipher_name, init_flag)) + { + return FALSE; + } + + if (!botan_cipher_set_key(cipher, this->key.ptr, this->key.len) + && !botan_cipher_start(cipher, iv.ptr, iv.len) + && !botan_cipher_update(cipher, BOTAN_CIPHER_UPDATE_FLAG_FINAL, out, + data.len, &output_written, in, data.len, + &input_consumed) + && (output_written == input_consumed)) + { + success = TRUE; + } + + botan_cipher_destroy(cipher); + return success; +} + +METHOD(crypter_t, decrypt, bool, + private_botan_crypter_t *this, chunk_t data, chunk_t iv, chunk_t *dst) +{ + return crypt(this, data, iv, dst, BOTAN_CIPHER_INIT_FLAG_DECRYPT); +} + + +METHOD(crypter_t, encrypt, bool, + private_botan_crypter_t *this, chunk_t data, chunk_t iv, chunk_t *dst) +{ + return crypt(this, data, iv, dst, BOTAN_CIPHER_INIT_FLAG_ENCRYPT); +} + +METHOD(crypter_t, get_block_size, size_t, + private_botan_crypter_t *this) +{ + return AES_BLOCK_SIZE; +} + +METHOD(crypter_t, get_iv_size, size_t, + private_botan_crypter_t *this) +{ + return AES_BLOCK_SIZE; +} + +METHOD(crypter_t, get_key_size, size_t, + private_botan_crypter_t *this) +{ + return this->key.len; +} + +METHOD(crypter_t, set_key, bool, + private_botan_crypter_t *this, chunk_t key) +{ + memcpy(this->key.ptr, key.ptr, min(key.len, this->key.len)); + return TRUE; +} + +METHOD(crypter_t, destroy, void, + private_botan_crypter_t *this) +{ + chunk_clear(&this->key); + free(this); +} + +/* + * Described in header + */ +botan_crypter_t *botan_crypter_create(encryption_algorithm_t algo, + size_t key_size) +{ + private_botan_crypter_t *this; + + INIT(this, + .public = { + .crypter = { + .encrypt = _encrypt, + .decrypt = _decrypt, + .get_block_size = _get_block_size, + .get_iv_size = _get_iv_size, + .get_key_size = _get_key_size, + .set_key = _set_key, + .destroy = _destroy, + }, + }, + ); + + + switch (algo) + { + case ENCR_AES_CBC: + switch (key_size) + { + case 16: + /* AES 128 */ + this->cipher_name = "AES-128/CBC/NoPadding"; + break; + case 24: + /* AES-192 */ + this->cipher_name = "AES-192/CBC/NoPadding"; + break; + case 32: + /* AES-256 */ + this->cipher_name = "AES-256/CBC/NoPadding"; + break; + default: + free(this); + return NULL; + } + break; + default: + { + free(this); + return NULL; + + } + } + + this->key = chunk_alloc(key_size); + return &this->public; +} diff --git a/src/libstrongswan/plugins/botan/botan_crypter.h b/src/libstrongswan/plugins/botan/botan_crypter.h new file mode 100644 index 000000000..246904a5f --- /dev/null +++ b/src/libstrongswan/plugins/botan/botan_crypter.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2018 René Korthaus + * Copyright (C) 2018 Konstantinos Kolelis + * Rohde & Schwarz Cybersecurity GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/** + * @defgroup botan_crypter botan_crypter + * @{ @ingroup botan_p + */ + +#ifndef BOTAN_CRYPTER_H_ +#define BOTAN_CRYPTER_H_ + +typedef struct botan_crypter_t botan_crypter_t; + +#include + +/** + * Implementation of crypters using Botan. + */ +struct botan_crypter_t { + + /** + * Implements crypter_t interface. + */ + crypter_t crypter; +}; + +/** + * Constructor to create botan_crypter_t. + * + * @param algo algorithm to implement + * @param key_size key size in bytes + * @return botan_crypter_t, NULL if not supported + */ +botan_crypter_t *botan_crypter_create(encryption_algorithm_t algo, + size_t key_size); + +#endif /** BOTAN_CRYPTER_H_ @}*/ diff --git a/src/libstrongswan/plugins/botan/botan_diffie_hellman.c b/src/libstrongswan/plugins/botan/botan_diffie_hellman.c new file mode 100644 index 000000000..2ca14f35a --- /dev/null +++ b/src/libstrongswan/plugins/botan/botan_diffie_hellman.c @@ -0,0 +1,261 @@ +/* + * Copyright (C) 2018 René Korthaus + * Copyright (C) 2018 Konstantinos Kolelis + * Rohde & Schwarz Cybersecurity GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "botan_diffie_hellman.h" + +#include + +#ifdef BOTAN_HAS_DIFFIE_HELLMAN + +#include "botan_util.h" + +#include + +#include + +typedef struct private_botan_diffie_hellman_t private_botan_diffie_hellman_t; + +/** + * Private data of an botan_diffie_hellman_t object. + */ +struct private_botan_diffie_hellman_t { + + /** + * Public botan_diffie_hellman_t interface + */ + botan_diffie_hellman_t public; + + /** + * Diffie Hellman group number + */ + diffie_hellman_group_t group; + + /** + * Private key + */ + botan_privkey_t dh_key; + + /** + * Diffie hellman shared secret + */ + chunk_t shared_secret; + + /** + * Generator value + */ + botan_mp_t g; + + /** + * Modulus + */ + botan_mp_t p; + +}; + +bool load_private_key(private_botan_diffie_hellman_t *this, chunk_t value) +{ + botan_mp_t xa; + if (chunk_to_botan_mp(value, &xa)) + { + return FALSE; + } + + if (botan_privkey_destroy(this->dh_key) || + botan_privkey_load_dh (&this->dh_key, this->p, this->g, xa)) + { + return FALSE; + } + return TRUE; +} + +METHOD(diffie_hellman_t, set_other_public_value, bool, + private_botan_diffie_hellman_t *this, chunk_t value) +{ + botan_pk_op_ka_t op; + + if (!diffie_hellman_verify_value(this->group, value)) + { + return FALSE; + } + + chunk_clear(&this->shared_secret); + botan_pk_op_key_agreement_create(&op, this->dh_key, "Raw", 0); + + /* get shared secret key size */ + if (botan_pk_op_key_agreement(op, NULL, &this->shared_secret.len, value.ptr, + value.len, NULL, 0) + != BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE) + { + return FALSE; + } + + this->shared_secret = chunk_alloc(this->shared_secret.len); + if (botan_pk_op_key_agreement(op, this->shared_secret.ptr, + &this->shared_secret.len, value.ptr, + value.len, NULL, 0)) + { + return FALSE; + } + + return TRUE; +} + +METHOD(diffie_hellman_t, get_my_public_value, bool, + private_botan_diffie_hellman_t *this, chunk_t *value) +{ + *value = chunk_empty; + + /* get key size of public key first */ + if (botan_pk_op_key_agreement_export_public(this->dh_key, NULL, &value->len) + != BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE) + { + return FALSE; + } + + *value = chunk_alloc(value->len); + if (botan_pk_op_key_agreement_export_public(this->dh_key, value->ptr, + &value->len)) + { + chunk_clear(value); + return FALSE; + } + + return TRUE; +} + +METHOD(diffie_hellman_t, set_private_value, bool, + private_botan_diffie_hellman_t *this, chunk_t value) +{ + return load_private_key(this, value); +} + +METHOD(diffie_hellman_t, get_shared_secret, bool, + private_botan_diffie_hellman_t *this, chunk_t *secret) +{ + if (this->shared_secret.len == 0) + { + return FALSE; + } + + *secret = chunk_clone(this->shared_secret); + return TRUE; +} + +METHOD(diffie_hellman_t, get_dh_group, diffie_hellman_group_t, + private_botan_diffie_hellman_t *this) +{ + return this->group; +} + +METHOD(diffie_hellman_t, destroy, void, + private_botan_diffie_hellman_t *this) +{ + botan_mp_destroy(this->p); + botan_mp_destroy(this->g); + botan_privkey_destroy(this->dh_key); + chunk_clear(&this->shared_secret); + free(this); +} + +/* + * Generic internal constructor + */ +botan_diffie_hellman_t *create_generic(diffie_hellman_group_t group, + chunk_t g, chunk_t p) +{ + private_botan_diffie_hellman_t *this; + + INIT(this, + .public = { + .dh = { + .get_shared_secret = _get_shared_secret, + .set_other_public_value = _set_other_public_value, + .get_my_public_value = _get_my_public_value, + .set_private_value = _set_private_value, + .get_dh_group = _get_dh_group, + .destroy = _destroy, + }, + }, + .group = group, + ); + + if (chunk_to_botan_mp(p, &this->p)) + { + destroy(this); + return NULL; + } + + if (chunk_to_botan_mp(g, &this->g)) + { + destroy(this); + return NULL; + } + + chunk_t random; + rng_t *rng; + rng = lib->crypto->create_rng(lib->crypto, RNG_STRONG); + if (rng && rng->allocate_bytes(rng, p.len, &random)) + { + rng->destroy(rng); + if (!load_private_key(this, random)) + { + destroy(this); + chunk_clear(&random); + return NULL; + } + } + + return &this->public; +} + +/* + * Described in header. + */ +botan_diffie_hellman_t * +botan_diffie_hellman_create(diffie_hellman_group_t group) +{ + diffie_hellman_params_t *params; + params = diffie_hellman_get_params(group); + if (!params) + { + return NULL; + } + return create_generic(group, params->generator, params->prime); +} + +/* + * Described in header. + */ +botan_diffie_hellman_t * +botan_diffie_hellman_create_custom(diffie_hellman_group_t group, chunk_t g, + chunk_t p) +{ + if (group == MODP_CUSTOM) + { + return create_generic(group, g, p); + } + return NULL; +} + +#endif diff --git a/src/libstrongswan/plugins/botan/botan_diffie_hellman.h b/src/libstrongswan/plugins/botan/botan_diffie_hellman.h new file mode 100644 index 000000000..88f221d34 --- /dev/null +++ b/src/libstrongswan/plugins/botan/botan_diffie_hellman.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2018 René Korthaus + * Copyright (C) 2018 Konstantinos Kolelis + * Rohde & Schwarz Cybersecurity GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/** + * @defgroup botan_diffie_hellman botan_diffie_hellman + * @{ @ingroup botan_p + */ + +#ifndef BOTAN_DIFFIE_HELLMAN_H_ +#define BOTAN_DIFFIE_HELLMAN_H_ + +typedef struct botan_diffie_hellman_t botan_diffie_hellman_t; + +#include + +/** + * Implementation of the Diffie-Hellman algorithm using Botan. + */ +struct botan_diffie_hellman_t { + + /** + * Implements diffie_hellman_t interface. + */ + diffie_hellman_t dh; +}; + +/** + * Creates a new botan_diffie_hellman_t object. + * + * @param group Diffie Hellman group number to use + * @return botan_diffie_hellman_t object, + * NULL if not supported + */ +botan_diffie_hellman_t * +botan_diffie_hellman_create(diffie_hellman_group_t group); + +/** + * Creates a new botan_diffie_hellman_t object for MODP_CUSTOM. + * + * @param group MODP_CUSTOM + * @param g generator + * @param p prime + * @return botan_diffie_hellman_t object, + * NULL if not supported + */ +botan_diffie_hellman_t * +botan_diffie_hellman_create_custom(diffie_hellman_group_t group, chunk_t g, + chunk_t p); + +#endif /** BOTAN_DIFFIE_HELLMAN_H_ @}*/ + diff --git a/src/libstrongswan/plugins/botan/botan_ec_diffie_hellman.c b/src/libstrongswan/plugins/botan/botan_ec_diffie_hellman.c new file mode 100644 index 000000000..48b231f88 --- /dev/null +++ b/src/libstrongswan/plugins/botan/botan_ec_diffie_hellman.c @@ -0,0 +1,248 @@ +/* + * Copyright (C) 2018 René Korthaus + * Rohde & Schwarz Cybersecurity GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "botan_ec_diffie_hellman.h" + +#include + +#ifdef BOTAN_HAS_ECDH + +#include + +#include + +typedef struct private_botan_ec_diffie_hellman_t + private_botan_ec_diffie_hellman_t; + +/** + * Private data of a botan_ec_diffie_hellman_t object. + */ +struct private_botan_ec_diffie_hellman_t { + /** + * Public botan_ec_diffie_hellman_t interface + */ + botan_ec_diffie_hellman_t public; + + /** + * Diffie Hellman group + */ + diffie_hellman_group_t group; + + /** + * EC curve name + */ + const char* curve_name; + + /** + * EC private key + */ + botan_privkey_t key; + + /** + * Shared secret + */ + chunk_t shared_secret; + + /** + * True if shared secret is computed + */ + bool computed; +}; + +METHOD(diffie_hellman_t, set_other_public_value, bool, + private_botan_ec_diffie_hellman_t *this, chunk_t value) +{ + if (!diffie_hellman_verify_value(this->group, value)) + { + return FALSE; + } + + botan_pk_op_ka_t ka; + if (botan_pk_op_key_agreement_create(&ka, this->key, "Raw", 0)) + { + return FALSE; + } + + /* prepend 0x04 to indicate uncompressed point format */ + uint8_t indic = 0x04; + value = chunk_cata("cc", chunk_from_thing(indic), value); + size_t out_len = 0; + if (botan_pk_op_key_agreement(ka, NULL, &out_len, value.ptr, value.len, + NULL, 0) + != BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE) + { + botan_pk_op_key_agreement_destroy(ka); + return FALSE; + } + + if (out_len == 0) + { + botan_pk_op_key_agreement_destroy(ka); + return FALSE; + } + + chunk_clear(&this->shared_secret); + this->shared_secret = chunk_alloc(out_len); + if (botan_pk_op_key_agreement(ka, this->shared_secret.ptr, + &this->shared_secret.len, value.ptr, + value.len, NULL, 0)) + { + chunk_clear(&this->shared_secret); + botan_pk_op_key_agreement_destroy(ka); + return FALSE; + } + + botan_pk_op_key_agreement_destroy(ka); + this->computed = TRUE; + return TRUE; +} + +METHOD(diffie_hellman_t, get_my_public_value, bool, + private_botan_ec_diffie_hellman_t *this, chunk_t *value) +{ + chunk_t pkey = chunk_empty; + if (botan_pk_op_key_agreement_export_public(this->key, NULL, &pkey.len) + != BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE) + { + return FALSE; + } + + pkey = chunk_alloca(pkey.len); + if (botan_pk_op_key_agreement_export_public(this->key, pkey.ptr, &pkey.len)) + { + return FALSE; + } + + /* skip 0x04 byte prepended by botan */ + *value = chunk_clone(chunk_skip(pkey, 1)); + return TRUE; +} + +METHOD(diffie_hellman_t, set_private_value, bool, + private_botan_ec_diffie_hellman_t *this, chunk_t value) +{ + botan_mp_t scalar; + if (botan_mp_init(&scalar)) + { + return FALSE; + } + + if (botan_mp_from_bin(scalar, value.ptr, value.len)) + { + botan_mp_destroy(scalar); + return FALSE; + } + + if (botan_privkey_destroy(this->key)) + { + botan_mp_destroy(scalar); + return FALSE; + } + + if (botan_privkey_load_ecdh(&this->key, scalar, this->curve_name)) + { + botan_mp_destroy(scalar); + return FALSE; + } + + botan_mp_destroy(scalar); + this->computed = FALSE; + return TRUE; +} + +METHOD(diffie_hellman_t, get_shared_secret, bool, + private_botan_ec_diffie_hellman_t *this, chunk_t *secret) +{ + if (!this->computed) + { + return FALSE; + } + *secret = chunk_clone(this->shared_secret); + return TRUE; +} + +METHOD(diffie_hellman_t, get_dh_group, diffie_hellman_group_t, + private_botan_ec_diffie_hellman_t *this) +{ + return this->group; +} + +METHOD(diffie_hellman_t, destroy, void, + private_botan_ec_diffie_hellman_t *this) +{ + botan_privkey_destroy(this->key); + chunk_clear(&this->shared_secret); + free(this); +} + +/* + * Described in header. + */ +botan_ec_diffie_hellman_t * +botan_ec_diffie_hellman_create(diffie_hellman_group_t group) +{ + private_botan_ec_diffie_hellman_t *this; + + INIT(this, + .public = { + .dh = { + .get_shared_secret = _get_shared_secret, + .set_other_public_value = _set_other_public_value, + .get_my_public_value = _get_my_public_value, + .set_private_value = _set_private_value, + .get_dh_group = _get_dh_group, + .destroy = _destroy, + }, + }, + .group = group, + ); + + switch (group) + { + case ECP_256_BIT: + this->curve_name = "secp256r1"; + break; + case ECP_384_BIT: + this->curve_name = "secp384r1"; + break; + case ECP_521_BIT: + this->curve_name = "secp521r1"; + break; + case ECP_256_BP: + this->curve_name = "brainpool256r1"; + break; + case ECP_384_BP: + this->curve_name = "brainpool384r1"; + break; + case ECP_512_BP: + this->curve_name = "brainpool512r1"; + break; + default: + free(this); + return NULL; + } + + return &this->public; +} + +#endif diff --git a/src/libstrongswan/plugins/botan/botan_ec_diffie_hellman.h b/src/libstrongswan/plugins/botan/botan_ec_diffie_hellman.h new file mode 100644 index 000000000..f4751de01 --- /dev/null +++ b/src/libstrongswan/plugins/botan/botan_ec_diffie_hellman.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2018 René Korthaus + * Rohde & Schwarz Cybersecurity GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/** + * @defgroup botan_ec_diffie_hellman botan_ec_diffie_hellman + * @{ @ingroup botan_p + */ + +#ifndef BOTAN_EC_DIFFIE_HELLMAN_H_ +#define BOTAN_EC_DIFFIE_HELLMAN_H_ + +typedef struct botan_ec_diffie_hellman_t botan_ec_diffie_hellman_t; + +#include + +/** + * Implementation of the EC Diffie-Hellman algorithm using Botan. + */ +struct botan_ec_diffie_hellman_t { + + /** + * Implements diffie_hellman_t interface. + */ + diffie_hellman_t dh; +}; + +/** + * Creates a new botan_ec_diffie_hellman_t object. + * + * @param group EC Diffie Hellman group number to use + * @return botan_ec_diffie_hellman_t object, NULL if not supported + */ +botan_ec_diffie_hellman_t * +botan_ec_diffie_hellman_create(diffie_hellman_group_t group); + +#endif /** BOTAN_EC_DIFFIE_HELLMAN_H_ @}*/ + diff --git a/src/libstrongswan/plugins/botan/botan_ec_private_key.c b/src/libstrongswan/plugins/botan/botan_ec_private_key.c new file mode 100644 index 000000000..abb4597ad --- /dev/null +++ b/src/libstrongswan/plugins/botan/botan_ec_private_key.c @@ -0,0 +1,581 @@ +/* + * Copyright (C) 2018 René Korthaus + * Copyright (C) 2018 Konstantinos Kolelis + * Rohde & Schwarz Cybersecurity GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + +#include "botan_ec_private_key.h" + +#include + +#ifdef BOTAN_HAS_ECDSA + +#include +#include +#include + +#include + +#include + +typedef struct private_botan_ec_private_key_t private_botan_ec_private_key_t; + +/** + * Private data of a botan_ec_private_key_t object. + */ +struct private_botan_ec_private_key_t { + /** + * Public interface for this signer. + */ + botan_ec_private_key_t public; + + /** + * Botan ec private key + */ + botan_privkey_t key; + + /** + * Reference count + */ + refcount_t ref; +}; + +#define SIG_FORMAT_IEEE_1363 0 +#define SIG_FORMAT_DER_SEQUENCE 1 + +/* implemented in ec public key */ +bool botan_ec_fingerprint(botan_pubkey_t *ec, cred_encoding_type_t type, + chunk_t *fp); + +/** + * Build a DER encoded signature as in RFC 3279 or as in RFC 4754 + */ +static bool build_signature(botan_privkey_t key, const char *hash_and_padding, + int signature_format, chunk_t data, + chunk_t *signature) +{ + if (!hash_and_padding || !signature) + { + return FALSE; + } + + botan_pk_op_sign_t sign_op; + + if (botan_pk_op_sign_create(&sign_op, key, hash_and_padding, 0)) + { + return FALSE; + } + + botan_rng_t rng; + if (botan_rng_init(&rng, "user")) + { + return FALSE; + } + + /* get size of signature first */ + if (botan_pk_op_sign_update(sign_op, data.ptr, data.len)) + { + botan_rng_destroy(rng); + botan_pk_op_sign_destroy(sign_op); + return FALSE; + } + + signature->len = 0; + if (botan_pk_op_sign_finish(sign_op, rng, NULL, &signature->len) + != BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE) + { + botan_rng_destroy(rng); + botan_pk_op_sign_destroy(sign_op); + return FALSE; + } + + /* now get the signature */ + *signature = chunk_alloc(signature->len); + if (botan_pk_op_sign_update(sign_op, data.ptr, data.len)) + { + botan_rng_destroy(rng); + botan_pk_op_sign_destroy(sign_op); + return FALSE; + } + + if (botan_pk_op_sign_finish(sign_op, rng, signature->ptr, &signature->len)) + { + botan_rng_destroy(rng); + botan_pk_op_sign_destroy(sign_op); + return FALSE; + } + + botan_rng_destroy(rng); + botan_pk_op_sign_destroy(sign_op); + + if (signature_format == SIG_FORMAT_DER_SEQUENCE) + { + /* format as ASN.1 sequence of two integers r,s */ + chunk_t r, s = chunk_empty; + chunk_split(*signature, "aa", signature->len / 2, &r, + signature->len / 2, &s); + + chunk_free(signature); + *signature = asn1_wrap(ASN1_SEQUENCE, "mm", asn1_integer("c", r), + asn1_integer("c", s)); + } + + return TRUE; +} + +METHOD(private_key_t, sign, bool, + private_botan_ec_private_key_t *this, signature_scheme_t scheme, + void *params, chunk_t data, chunk_t *signature) +{ + switch (scheme) + { + case SIGN_ECDSA_WITH_NULL: + /* r||s -> Botan::IEEE_1363, data is the hash already */ + return build_signature(this->key, "Raw", + SIG_FORMAT_IEEE_1363, data, signature); + case SIGN_ECDSA_WITH_SHA1_DER: + /* DER SEQUENCE of two INTEGERS r,s -> Botan::DER_SEQUENCE */ + return build_signature(this->key, "EMSA1(SHA-1)", + SIG_FORMAT_DER_SEQUENCE, data, signature); + case SIGN_ECDSA_WITH_SHA256_DER: + return build_signature(this->key, "EMSA1(SHA-256)", + SIG_FORMAT_DER_SEQUENCE, data, signature); + case SIGN_ECDSA_WITH_SHA384_DER: + return build_signature(this->key, "EMSA1(SHA-384)", + SIG_FORMAT_DER_SEQUENCE, data, signature); + case SIGN_ECDSA_WITH_SHA512_DER: + return build_signature(this->key, "EMSA1(SHA-512)", + SIG_FORMAT_DER_SEQUENCE, data, signature); + case SIGN_ECDSA_256: + /* r||s -> Botan::IEEE_1363 */ + return build_signature(this->key, "EMSA1(SHA-256)", + SIG_FORMAT_IEEE_1363, data, signature); + case SIGN_ECDSA_384: + /* r||s -> Botan::IEEE_1363 */ + return build_signature(this->key, "EMSA1(SHA-384)", + SIG_FORMAT_IEEE_1363, data, signature); + case SIGN_ECDSA_521: + /* r||s -> Botan::IEEE_1363 */ + return build_signature(this->key, "EMSA1(SHA-512)", + SIG_FORMAT_IEEE_1363, data, signature); + default: + DBG1(DBG_LIB, "signature scheme %N not supported via botan", + signature_scheme_names, scheme); + return FALSE; + } +} + +METHOD(private_key_t, decrypt, bool, + private_botan_ec_private_key_t *this, encryption_scheme_t scheme, + chunk_t crypto, chunk_t *plain) +{ + DBG1(DBG_LIB, "EC private key decryption not implemented"); + return FALSE; +} + +METHOD(private_key_t, get_keysize, int, + private_botan_ec_private_key_t *this) +{ + botan_mp_t p; + if(botan_mp_init(&p)) + { + return 0; + } + + if(botan_privkey_get_field(p, this->key, "p")) + { + botan_mp_destroy(p); + return 0; + } + + size_t bits = 0; + if(botan_mp_num_bits(p, &bits)) + { + botan_mp_destroy(p); + return 0; + } + + botan_mp_destroy(p); + return bits; +} + +METHOD(private_key_t, get_type, key_type_t, + private_botan_ec_private_key_t *this) +{ + return KEY_ECDSA; +} + +METHOD(private_key_t, get_public_key, public_key_t*, + private_botan_ec_private_key_t *this) +{ + public_key_t *public; + chunk_t key = chunk_empty; + + botan_pubkey_t pubkey; + if (botan_privkey_export_pubkey(&pubkey, this->key)) + { + return FALSE; + } + + if (botan_pubkey_export(pubkey, NULL, &key.len, + BOTAN_PRIVKEY_EXPORT_FLAG_DER) + != BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE) + { + botan_pubkey_destroy(pubkey); + return FALSE; + } + + key = chunk_alloc(key.len); + + if (botan_pubkey_export(pubkey, key.ptr, &key.len, + BOTAN_PRIVKEY_EXPORT_FLAG_DER)) + { + chunk_free(&key); + botan_pubkey_destroy(pubkey); + return FALSE; + } + + public = lib->creds->create(lib->creds, CRED_PUBLIC_KEY, KEY_ECDSA, + BUILD_BLOB_ASN1_DER, key, BUILD_END); + + chunk_free(&key); + botan_pubkey_destroy(pubkey); + return public; +} + +METHOD(private_key_t, get_fingerprint, bool, + private_botan_ec_private_key_t *this, cred_encoding_type_t type, + chunk_t *fingerprint) +{ + bool success = FALSE; + + botan_pubkey_t pubkey; + if (botan_privkey_export_pubkey(&pubkey, this->key)) + { + return FALSE; + } + + success = botan_ec_fingerprint(&pubkey, type, fingerprint); + botan_pubkey_destroy(pubkey); + return success; +} + +METHOD(private_key_t, get_encoding, bool, + private_botan_ec_private_key_t *this, cred_encoding_type_t type, + chunk_t *encoding) +{ + switch (type) + { + case PRIVKEY_ASN1_DER: + case PRIVKEY_PEM: + { + bool success = TRUE; + + botan_mp_t x; + if (botan_mp_init(&x)) + { + return FALSE; + } + + if (botan_privkey_get_field(x, this->key, "x")) + { + botan_mp_destroy(x); + return FALSE; + } + + chunk_t pval = chunk_empty; + if (botan_mp_num_bytes(x, &pval.len)) + { + botan_mp_destroy(x); + return FALSE; + } + + pval = chunk_alloc(pval.len); + + if (botan_mp_to_bin(x, pval.ptr)) + { + botan_mp_destroy(x); + return FALSE; + } + + chunk_t version = chunk_from_chars( 0x01 ); + *encoding = + asn1_wrap(ASN1_SEQUENCE, "ms", asn1_integer("c", version), + asn1_wrap(ASN1_OCTET_STRING, "s", pval)); + + if (type == PRIVKEY_PEM) + { + chunk_t asn1_encoding = *encoding; + + success = lib->encoding->encode(lib->encoding, PRIVKEY_PEM, + NULL, encoding, + CRED_PART_ECDSA_PRIV_ASN1_DER, + asn1_encoding, CRED_PART_END); + chunk_clear(&asn1_encoding); + } + + botan_mp_destroy(x); + return success; + } + default: + return FALSE; + } +} + +METHOD(private_key_t, get_ref, private_key_t*, + private_botan_ec_private_key_t *this) +{ + ref_get(&this->ref); + return &this->public.key; +} + +METHOD(private_key_t, destroy, void, + private_botan_ec_private_key_t *this) +{ + if (ref_put(&this->ref)) + { + if (&this->key) + { + lib->encoding->clear_cache(lib->encoding, &this->key); + botan_privkey_destroy(this->key); + } + free(this); + } +} + +/** + * Internal generic constructor + */ +static private_botan_ec_private_key_t *create_empty() +{ + private_botan_ec_private_key_t *this; + + INIT(this, + .public = { + .key = { + .get_type = _get_type, + .sign = _sign, + .decrypt = _decrypt, + .get_keysize = _get_keysize, + .get_public_key = _get_public_key, + .equals = private_key_equals, + .belongs_to = private_key_belongs_to, + .get_fingerprint = _get_fingerprint, + .has_fingerprint = private_key_has_fingerprint, + .get_encoding = _get_encoding, + .get_ref = _get_ref, + .destroy = _destroy, + }, + }, + .ref = 1, + ); + + return this; +} + +/* + * See header. + */ +botan_ec_private_key_t *botan_ec_private_key_gen(key_type_t type, va_list args) +{ + private_botan_ec_private_key_t *this; + u_int key_size = 0; + const char* curve; + + while (TRUE) + { + switch (va_arg(args, builder_part_t)) + { + case BUILD_KEY_SIZE: + key_size = va_arg(args, u_int); + continue; + case BUILD_END: + break; + default: + return NULL; + } + break; + } + + if (!key_size) + { + return NULL; + } + + switch (key_size) + { + case 256: + curve = "secp256r1"; + break; + case 384: + curve = "secp384r1"; + break; + case 521: + curve = "secp521r1"; + break; + default: + DBG1(DBG_LIB, "EC private key size %d not supported via botan", + key_size); + return NULL; + } + + botan_rng_t rng; + if (botan_rng_init(&rng, "user")) + { + return NULL; + } + + this = create_empty(); + + if(botan_privkey_create_ecdsa(&this->key, rng, curve)) + { + DBG1(DBG_LIB, "EC private key generation failed", key_size); + botan_rng_destroy(rng); + destroy(this); + return NULL; + } + + botan_rng_destroy(rng); + return &this->public; +} + +/** + * ASN.1 definition of a ECPrivateKey structure (RFC 5915) + */ +static const asn1Object_t ecPrivateKeyObjects[] = { + { 0, "ECPrivateKey", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */ + { 1, "version", ASN1_INTEGER, ASN1_BODY }, /* 1 */ + { 1, "privateKey", ASN1_OCTET_STRING, ASN1_BODY }, /* 2 */ + { 1, "parameters", ASN1_EOC, ASN1_RAW }, /* 3 */ + { 1, "publicKey", ASN1_BIT_STRING, ASN1_OPT }, /* 4 */ + { 0, "exit", ASN1_EOC, ASN1_EXIT } +}; + +#define ECPK_PRIVATE_KEY 2 +#define ECPK_PRIVATE_KEY_PARAMS 3 + +/** + * See header. + */ +botan_ec_private_key_t *botan_ec_private_key_load(key_type_t type, va_list args) +{ + private_botan_ec_private_key_t *this; + chunk_t params = chunk_empty, key = chunk_empty; + chunk_t object, pkcs8 = chunk_empty; + + while (TRUE) + { + switch (va_arg(args, builder_part_t)) + { + case BUILD_BLOB_ALGID_PARAMS: + params = va_arg(args, chunk_t); + continue; + case BUILD_BLOB_ASN1_DER: + key = va_arg(args, chunk_t); + continue; + case BUILD_END: + break; + default: + return NULL; + } + break; + } + + this = create_empty(); + + /* + * botan expects a PKCS#8 private key, so we build one + * RFC 5282 mandates ECParameters as part of the algorithmIdentifier + */ + chunk_t alg_id = chunk_empty; + if(params.len != 0) + { + /* if ECDomainParameters is passed, just append it */ + alg_id = asn1_wrap(ASN1_SEQUENCE, "mc", + asn1_build_known_oid(OID_EC_PUBLICKEY), params); + } + else + { + /* + * no explicit ECParameters passed, so we extract them from the + * ECPrivateKey structure and append it to the algorithmIdentifier + */ + asn1_parser_t *parser; + int objectID; + + parser = asn1_parser_create(ecPrivateKeyObjects, key); + parser->set_flags(parser, FALSE, TRUE); + + while (parser->iterate(parser, &objectID, &object)) + { + if (objectID == ECPK_PRIVATE_KEY_PARAMS) + { + if (!asn1_parse_simple_object(&object, ASN1_CONTEXT_C_0, 0, + "parameters")) + { + parser->destroy(parser); + return NULL; + } + + if (asn1_unwrap(&object, ¶ms) != ASN1_OID) + { + parser->destroy(parser); + return NULL; + } + + break; + } + } + + parser->destroy(parser); + alg_id = asn1_wrap(ASN1_SEQUENCE, "mc", + asn1_build_known_oid(OID_EC_PUBLICKEY), + asn1_simple_object(ASN1_OID, params)); + } + + pkcs8 = asn1_wrap(ASN1_SEQUENCE, "cms", + asn1_integer("c", chunk_from_chars(0x00)), + alg_id, + asn1_wrap(ASN1_OCTET_STRING, "c", key)); + + botan_rng_t rng; + if (botan_rng_init(&rng, "user")) + { + chunk_clear(&pkcs8); + destroy(this); + return NULL; + } + + if (botan_privkey_load(&this->key, rng, pkcs8.ptr, pkcs8.len, NULL)) + { + chunk_clear(&pkcs8); + botan_rng_destroy(rng); + destroy(this); + return NULL; + } + + chunk_clear(&pkcs8); + botan_rng_destroy(rng); + return &this->public; +} + +#endif \ No newline at end of file diff --git a/src/libstrongswan/plugins/botan/botan_ec_private_key.h b/src/libstrongswan/plugins/botan/botan_ec_private_key.h new file mode 100644 index 000000000..0fcfda25e --- /dev/null +++ b/src/libstrongswan/plugins/botan/botan_ec_private_key.h @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2018 René Korthaus + * Copyright (C) 2018 Konstantinos Kolelis + * Rohde & Schwarz Cybersecurity GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/** + * @defgroup botan_ec_private_key botan_ec_private_key + * @{ @ingroup botan_p + */ + +#ifndef BOTAN_EC_PRIVATE_KEY_H_ +#define BOTAN_EC_PRIVATE_KEY_H_ + +#include + +#ifdef BOTAN_HAS_ECDSA + +#include +#include + +typedef struct botan_ec_private_key_t botan_ec_private_key_t; + +/** + * private_key_t implementation of ECDSA using Botan. + */ +struct botan_ec_private_key_t { + + /** + * Implements private_key_t interface + */ + private_key_t key; +}; + +/** + * Generate a ECDSA private key using Botan. + * + * Accepts the BUILD_KEY_SIZE argument. + * + * @param type type of the key, must be KEY_ECDSA + * @param args builder_part_t argument list + * @return generated key, NULL on failure + */ +botan_ec_private_key_t *botan_ec_private_key_gen(key_type_t type, va_list args); + +/** + * Load a ECDSA private key using Botan. + * + * Accepts a BUILD_BLOB_ASN1_DER argument. + * + * @param type type of the key, must be KEY_ECDSA + * @param args builder_part_t argument list + * @return loaded key, NULL on failure + */ +botan_ec_private_key_t *botan_ec_private_key_load(key_type_t type, + va_list args); + +#endif + +#endif /** BOTAN_EC_PRIVATE_KEY_H_ @}*/ diff --git a/src/libstrongswan/plugins/botan/botan_ec_public_key.c b/src/libstrongswan/plugins/botan/botan_ec_public_key.c new file mode 100644 index 000000000..abc513b70 --- /dev/null +++ b/src/libstrongswan/plugins/botan/botan_ec_public_key.c @@ -0,0 +1,462 @@ +/* + * Copyright (C) 2018 René Korthaus + * Copyright (C) 2018 Konstantinos Kolelis + * Rohde & Schwarz Cybersecurity GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "botan_ec_public_key.h" + +#include + +#ifdef BOTAN_HAS_ECDSA + +#include +#include + +#include + +#include + +typedef struct private_botan_ec_public_key_t private_botan_ec_public_key_t; + +/** + * Private data structure with signing context. + */ +struct private_botan_ec_public_key_t { + /** + * Public interface for this signer + */ + botan_ec_public_key_t public; + + /** + * Botan ec public key + */ + botan_pubkey_t key; + + /** + * Reference counter + */ + refcount_t ref; +}; + +#define SIG_FORMAT_IEEE_1363 0 +#define SIG_FORMAT_DER_SEQUENCE 1 + +/** + * Verification of a DER encoded signature as in RFC 3279 or as in RFC 4754 + */ +static bool verify_signature(private_botan_ec_public_key_t *this, + const char* hash_and_padding, int signature_format, size_t keylen, + chunk_t data, chunk_t signature) +{ + chunk_t sig; + + if (signature_format == SIG_FORMAT_DER_SEQUENCE) + { + /* + * botan requires a signature in IEEE 1363 format (r||s) + * re-encode from ASN.1 sequence of two integers r,s + */ + chunk_t parse, r, s; + parse = signature; + + if (asn1_unwrap(&parse, &parse) != ASN1_SEQUENCE + || asn1_unwrap(&parse, &r) != ASN1_INTEGER + || asn1_unwrap(&parse, &s) != ASN1_INTEGER) + { + return FALSE; + } + + r = chunk_skip_zero(r); + s = chunk_skip_zero(s); + + /* + * r and s must be of size m_order.bytes()/2 each + */ + if (r.len > keylen || s.len > keylen) + { + return FALSE; + } + + sig = chunk_alloca(2 * keylen); + memset(sig.ptr, 0, sig.len); + memcpy(sig.ptr + (keylen - r.len), r.ptr, r.len); + memcpy(sig.ptr + keylen + (keylen - s.len), s.ptr, s.len); + } + else + { + sig.ptr = signature.ptr; + sig.len = signature.len; + } + + { + botan_pk_op_verify_t verify_op; + bool valid = FALSE; + + if (botan_pk_op_verify_create(&verify_op, this->key, hash_and_padding, + 0)) + { + return FALSE; + } + + if (botan_pk_op_verify_update(verify_op, data.ptr, data.len)) + { + botan_pk_op_verify_destroy(verify_op); + return FALSE; + } + + valid = !(botan_pk_op_verify_finish(verify_op, sig.ptr, sig.len)); + + botan_pk_op_verify_destroy(verify_op); + + return valid; + } +} + +METHOD(public_key_t, get_type, key_type_t, + private_botan_ec_public_key_t *this) +{ + return KEY_ECDSA; +} + +METHOD(public_key_t, get_keysize, int, + private_botan_ec_public_key_t *this) +{ + botan_mp_t p; + if(botan_mp_init(&p)) + { + return 0; + } + + if(botan_pubkey_get_field(p, this->key, "p")) + { + botan_mp_destroy(p); + return 0; + } + + size_t bits = 0; + if(botan_mp_num_bits(p, &bits)) + { + botan_mp_destroy(p); + return 0; + } + + botan_mp_destroy(p); + return bits; +} + +METHOD(public_key_t, verify, bool, + private_botan_ec_public_key_t *this, signature_scheme_t scheme, + void *params, chunk_t data, chunk_t signature) +{ + size_t keylen = (get_keysize(this) + 7) / 8; + const char *hash_and_padding; + int sig_format; + + switch (scheme) + { + case SIGN_ECDSA_WITH_NULL: + /* r||s -> Botan::IEEE_1363, data is the hash already */ + hash_and_padding = "Raw"; + sig_format = SIG_FORMAT_IEEE_1363; + break; + case SIGN_ECDSA_WITH_SHA1_DER: + /* DER SEQUENCE of two INTEGERS r,s -> Botan::DER_SEQUENCE */ + hash_and_padding = "EMSA1(SHA-1)"; + sig_format = SIG_FORMAT_DER_SEQUENCE; + break; + case SIGN_ECDSA_WITH_SHA256_DER: + hash_and_padding = "EMSA1(SHA-256)"; + sig_format = SIG_FORMAT_DER_SEQUENCE; + break; + case SIGN_ECDSA_WITH_SHA384_DER: + hash_and_padding = "EMSA1(SHA-384)"; + sig_format = SIG_FORMAT_DER_SEQUENCE; + break; + case SIGN_ECDSA_WITH_SHA512_DER: + hash_and_padding = "EMSA1(SHA-512)"; + sig_format = SIG_FORMAT_DER_SEQUENCE; + break; + case SIGN_ECDSA_256: + /* r||s -> Botan::IEEE_1363 */ + hash_and_padding = "EMSA1(SHA-256)"; + sig_format = SIG_FORMAT_IEEE_1363; + break; + case SIGN_ECDSA_384: + /* r||s -> Botan::IEEE_1363 */ + hash_and_padding = "EMSA1(SHA-384)"; + sig_format = SIG_FORMAT_IEEE_1363; + break; + case SIGN_ECDSA_521: + /* r||s -> Botan::IEEE_1363 */ + hash_and_padding = "EMSA1(SHA-512)"; + sig_format = SIG_FORMAT_IEEE_1363; + break; + default: + DBG1(DBG_LIB, "signature scheme %N not supported via botan", + signature_scheme_names, scheme); + return FALSE; + } + + return verify_signature(this, hash_and_padding, + sig_format, keylen, data, signature); +} + +METHOD(public_key_t, encrypt, bool, + private_botan_ec_public_key_t *this, encryption_scheme_t scheme, + chunk_t crypto, chunk_t *plain) +{ + DBG1(DBG_LIB, "EC public key encryption not implemented"); + return FALSE; +} + +/** + * Calculate fingerprint from a botan_pubkey_t, also used in ec private key. + */ +bool botan_ec_fingerprint(botan_pubkey_t *ec, cred_encoding_type_t type, + chunk_t *fp) +{ + hasher_t *hasher; + chunk_t key; + + if (lib->encoding->get_cache(lib->encoding, type, ec, fp)) + { + return TRUE; + } + + switch (type) + { + case KEYID_PUBKEY_SHA1: + /* subjectPublicKey -> use botan_pubkey_fingerprint() */ + { + if (botan_pubkey_fingerprint(*ec, "SHA-1", NULL, &fp->len)) + { + return FALSE; + } + + *fp = chunk_alloc(fp->len); + + if (botan_pubkey_fingerprint(*ec, "SHA-1", fp->ptr, &fp->len)) + { + chunk_free(fp); + return FALSE; + } + + break; + } + case KEYID_PUBKEY_INFO_SHA1: + /* subjectPublicKeyInfo -> use botan_pubkey_export(), then hash */ + { + if (botan_pubkey_export(*ec, NULL, &key.len, + BOTAN_PRIVKEY_EXPORT_FLAG_DER)) + { + return FALSE; + } + + key = chunk_alloc(key.len); + + if (botan_pubkey_export(*ec, key.ptr, &key.len, + BOTAN_PRIVKEY_EXPORT_FLAG_DER)) + { + chunk_free(&key); + return FALSE; + } + + hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1); + if (!hasher || !hasher->allocate_hash(hasher, key, fp)) + { + DBG1(DBG_LIB, "SHA1 hash algorithm not supported," + " fingerprinting failed"); + DESTROY_IF(hasher); + chunk_free(&key); + return FALSE; + } + + hasher->destroy(hasher); + chunk_free(&key); + break; + } + default: + return FALSE; + } + + lib->encoding->cache(lib->encoding, type, ec, *fp); + return TRUE; +} + +METHOD(public_key_t, get_fingerprint, bool, + private_botan_ec_public_key_t *this, cred_encoding_type_t type, + chunk_t *fingerprint) +{ + return botan_ec_fingerprint(&this->key, type, fingerprint); +} + +METHOD(public_key_t, get_encoding, bool, + private_botan_ec_public_key_t *this, cred_encoding_type_t type, + chunk_t *encoding) +{ + bool success = TRUE; + + if (botan_pubkey_export(this->key, NULL, &encoding->len, + BOTAN_PRIVKEY_EXPORT_FLAG_DER)) + { + return FALSE; + } + + *encoding = chunk_alloc(encoding->len); + + if (botan_pubkey_export(this->key, encoding->ptr, &encoding->len, + BOTAN_PRIVKEY_EXPORT_FLAG_DER)) + { + chunk_free(encoding); + return FALSE; + } + + if (type != PUBKEY_SPKI_ASN1_DER) + { + chunk_t asn1_encoding = *encoding; + + success = lib->encoding->encode(lib->encoding, type, NULL, encoding, + CRED_PART_ECDSA_PUB_ASN1_DER, + asn1_encoding, CRED_PART_END); + chunk_free(&asn1_encoding); + } + + return success; +} + +METHOD(public_key_t, get_ref, public_key_t*, + private_botan_ec_public_key_t *this) +{ + ref_get(&this->ref); + return &this->public.key; +} + +METHOD(public_key_t, destroy, void, + private_botan_ec_public_key_t *this) +{ + if (ref_put(&this->ref)) + { + botan_pubkey_destroy(this->key); + free(this); + } +} + +/** + * See header. + */ +botan_ec_public_key_t *botan_ec_public_key_load(key_type_t type, va_list args) +{ + private_botan_ec_public_key_t *this; + chunk_t blob = chunk_empty; + + if (type != KEY_ECDSA) + { + return NULL; + } + + while (TRUE) + { + switch (va_arg(args, builder_part_t)) + { + case BUILD_BLOB_ASN1_DER: + blob = va_arg(args, chunk_t); + continue; + case BUILD_END: + break; + default: + return NULL; + } + break; + } + + INIT(this, + .public = { + .key = { + .get_type = _get_type, + .verify = _verify, + .encrypt = _encrypt, + .get_keysize = _get_keysize, + .equals = public_key_equals, + .get_fingerprint = _get_fingerprint, + .has_fingerprint = public_key_has_fingerprint, + .get_encoding = _get_encoding, + .get_ref = _get_ref, + .destroy = _destroy, + }, + }, + .ref = 1, + ); + + if (botan_pubkey_load(&this->key, blob.ptr, blob.len)) + { + destroy(this); + return NULL; + } + + size_t namesize = 0; + if (botan_pubkey_algo_name(this->key, NULL, &namesize) != + BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE) + { + botan_pubkey_destroy(this->key); + destroy(this); + return NULL; + } + + char* namebuf = malloc(namesize); + if (botan_pubkey_algo_name(this->key, namebuf, &namesize)) + { + free(namebuf); + botan_pubkey_destroy(this->key); + destroy(this); + return NULL; + } + + const char* algo_name = "ECDSA"; + if (!strneq(namebuf, algo_name, sizeof(algo_name))) + { + free(namebuf); + botan_pubkey_destroy(this->key); + destroy(this); + return NULL; + } + free(namebuf); + + botan_rng_t rng; + if (botan_rng_init(&rng, "user")) + { + return FALSE; + } + + if (botan_pubkey_check_key(this->key, rng, BOTAN_CHECK_KEY_EXPENSIVE_TESTS)) + { + DBG1(DBG_LIB, "public key failed key checks"); + botan_rng_destroy(rng); + botan_pubkey_destroy(this->key); + destroy(this); + return NULL; + } + + botan_rng_destroy(rng); + return &this->public; +} + +#endif diff --git a/src/libstrongswan/plugins/botan/botan_ec_public_key.h b/src/libstrongswan/plugins/botan/botan_ec_public_key.h new file mode 100644 index 000000000..eb49ec3cb --- /dev/null +++ b/src/libstrongswan/plugins/botan/botan_ec_public_key.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2018 René Korthaus + * Copyright (C) 2018 Konstantinos Kolelis + * Rohde & Schwarz Cybersecurity GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef BOTAN_EC_PUBLIC_KEY_H_ +#define BOTAN_EC_PUBLIC_KEY_H_ + +typedef struct botan_ec_public_key_t botan_ec_public_key_t; + +#include +#include + +/** + * public_key_t implementation of ECDSA using botan. + */ +struct botan_ec_public_key_t { + + /** + * Implements the public_key_t interface + */ + public_key_t key; +}; + +/** + * Load a ECDSA public key using botan. + * + * Accepts a BUILD_BLOB_ASN1_DER argument. + * + * @param type type of the key, must be KEY_ECDSA + * @param args builder_part_t argument list + * @return loaded key, NULL on failure + */ +botan_ec_public_key_t *botan_ec_public_key_load(key_type_t type, va_list args); + +#endif /** BOTAN_EC_PUBLIC_KEY_H_ @}*/ diff --git a/src/libstrongswan/plugins/botan/botan_gcm.c b/src/libstrongswan/plugins/botan/botan_gcm.c new file mode 100644 index 000000000..7fc59bdc1 --- /dev/null +++ b/src/libstrongswan/plugins/botan/botan_gcm.c @@ -0,0 +1,349 @@ +/* + * Copyright (C) 2018 Atanas Filyanov + * Rohde & Schwarz Cybersecurity GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "botan_gcm.h" + +#include + +#ifdef BOTAN_HAS_AES +#ifdef BOTAN_HAS_AEAD_GCM + +#include + +#include + +/** + * as defined in RFC 4106 + */ +#define IV_LEN 8 +#define SALT_LEN 4 +#define NONCE_LEN (IV_LEN + SALT_LEN) + +typedef struct private_aead_t private_aead_t; + +struct private_aead_t { + + /** + * Public interface + */ + aead_t public; + + /** + * The encryption key + */ + chunk_t key; + + /** + * Salt value + */ + char salt[SALT_LEN]; + + /** + * Size of the integrity check value + */ + size_t icv_size; + + /** + * IV generator + */ + iv_gen_t *iv_gen; + + /** + * The cipher to use + */ + const char* cipher_name; +}; + +/** + * Do the actual en/decryption + */ +static bool crypt(private_aead_t *this, chunk_t data, chunk_t assoc, chunk_t iv, + u_char *out, uint32_t init_flag) +{ + botan_cipher_t cipher; + uint8_t nonce[NONCE_LEN]; + size_t output_written = 0; + size_t input_consumed = 0; + + memcpy(nonce, this->salt, SALT_LEN); + memcpy(nonce + SALT_LEN, iv.ptr, IV_LEN); + + if (botan_cipher_init(&cipher, this->cipher_name, init_flag)) + { + return FALSE; + } + + if (botan_cipher_set_key(cipher, this->key.ptr, this->key.len)) + { + botan_cipher_destroy(cipher); + return FALSE; + } + + if (assoc.len + && botan_cipher_set_associated_data(cipher, assoc.ptr, assoc.len)) + { + botan_cipher_destroy(cipher); + return FALSE; + } + + if (botan_cipher_start(cipher, nonce, NONCE_LEN)) + { + botan_cipher_destroy(cipher); + return FALSE; + } + + if (init_flag == BOTAN_CIPHER_INIT_FLAG_ENCRYPT) + { + if (botan_cipher_update(cipher, BOTAN_CIPHER_UPDATE_FLAG_FINAL, + out, data.len + this->icv_size, &output_written, + data.ptr, data.len, &input_consumed)) + { + botan_cipher_destroy(cipher); + return FALSE; + } + } + else if (init_flag == BOTAN_CIPHER_INIT_FLAG_DECRYPT) + { + if (botan_cipher_update(cipher, BOTAN_CIPHER_UPDATE_FLAG_FINAL, + out, data.len, &output_written, + data.ptr, data.len + this->icv_size, &input_consumed)) + { + botan_cipher_destroy(cipher); + return FALSE; + } + } + + botan_cipher_destroy(cipher); + + return TRUE; +} + +METHOD(aead_t, encrypt, bool, + private_aead_t *this, chunk_t plain, chunk_t assoc, chunk_t iv, + chunk_t *encrypted) +{ + u_char *out; + + out = plain.ptr; + if (encrypted) + { + *encrypted = chunk_alloc(plain.len + this->icv_size); + out = encrypted->ptr; + } + return crypt(this, plain, assoc, iv, out, BOTAN_CIPHER_INIT_FLAG_ENCRYPT); +} + +METHOD(aead_t, decrypt, bool, + private_aead_t *this, chunk_t encrypted, chunk_t assoc, chunk_t iv, + chunk_t *plain) +{ + u_char *out; + + if (encrypted.len < this->icv_size) + { + return FALSE; + } + encrypted.len -= this->icv_size; + + out = encrypted.ptr; + if (plain) + { + *plain = chunk_alloc(encrypted.len); + out = plain->ptr; + } + return crypt(this, encrypted, assoc, iv, out, + BOTAN_CIPHER_INIT_FLAG_DECRYPT); +} + +METHOD(aead_t, get_block_size, size_t, + private_aead_t *this) +{ + return 1; +} + +METHOD(aead_t, get_icv_size, size_t, + private_aead_t *this) +{ + return this->icv_size; +} + +METHOD(aead_t, get_iv_size, size_t, + private_aead_t *this) +{ + return IV_LEN; +} + +METHOD(aead_t, get_iv_gen, iv_gen_t*, + private_aead_t *this) +{ + return this->iv_gen; +} + +METHOD(aead_t, get_key_size, size_t, + private_aead_t *this) +{ + return this->key.len + SALT_LEN; +} + +METHOD(aead_t, set_key, bool, + private_aead_t *this, chunk_t key) +{ + if (key.len != get_key_size(this)) + { + return FALSE; + } + memcpy(this->salt, key.ptr + key.len - SALT_LEN, SALT_LEN); + memcpy(this->key.ptr, key.ptr, this->key.len); + return TRUE; +} + +METHOD(aead_t, destroy, void, + private_aead_t *this) +{ + chunk_clear(&this->key); + this->iv_gen->destroy(this->iv_gen); + free(this); +} + +aead_t *botan_gcm_create(encryption_algorithm_t algo, + size_t key_size, size_t salt_size) +{ + private_aead_t *this; + + INIT(this, + .public = { + .encrypt = _encrypt, + .decrypt = _decrypt, + .get_block_size = _get_block_size, + .get_icv_size = _get_icv_size, + .get_iv_size = _get_iv_size, + .get_iv_gen = _get_iv_gen, + .get_key_size = _get_key_size, + .set_key = _set_key, + .destroy = _destroy, + }, + ); + + switch (algo) + { + case ENCR_AES_GCM_ICV8: + this->icv_size = 8; + break; + case ENCR_AES_GCM_ICV12: + this->icv_size = 12; + break; + case ENCR_AES_GCM_ICV16: + this->icv_size = 16; + break; + default: + free(this); + return NULL; + } + + if (salt_size && salt_size != SALT_LEN) + { + /* currently not supported */ + free(this); + return NULL; + } + + switch (algo) + { + case ENCR_AES_GCM_ICV8: + switch (key_size) + { + case 0: + key_size = 16; + case 16: + this->cipher_name = "AES-128/GCM(8)"; + break; + case 24: + this->cipher_name = "AES-192/GCM(8)"; + break; + case 32: + this->cipher_name = "AES-256/GCM(8)"; + break; + default: + free(this); + return NULL; + } + break; + case ENCR_AES_GCM_ICV12: + switch (key_size) + { + case 0: + key_size = 16; + /* FALL */ + case 16: + this->cipher_name = "AES-128/GCM(12)"; + break; + case 24: + this->cipher_name = "AES-192/GCM(12)"; + break; + case 32: + this->cipher_name = "AES-256/GCM(12)"; + break; + default: + free(this); + return NULL; + } + break; + case ENCR_AES_GCM_ICV16: + switch (key_size) + { + case 0: + key_size = 16; + /* FALL */ + case 16: + this->cipher_name = "AES-128/GCM"; + break; + case 24: + this->cipher_name = "AES-192/GCM"; + break; + case 32: + this->cipher_name = "AES-256/GCM"; + break; + default: + free(this); + return NULL; + } + break; + default: + free(this); + return NULL; + } + + if (!this->cipher_name) + { + free(this); + return NULL; + } + + this->key = chunk_alloc(key_size); + this->iv_gen = iv_gen_seq_create(); + + return &this->public; +} + +#endif +#endif \ No newline at end of file diff --git a/src/libstrongswan/plugins/botan/botan_gcm.h b/src/libstrongswan/plugins/botan/botan_gcm.h new file mode 100644 index 000000000..600e60b0e --- /dev/null +++ b/src/libstrongswan/plugins/botan/botan_gcm.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2018 Atanas Filyanov + * Rohde & Schwarz Cybersecurity GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/** + * Implements the aead_t interface using Botan in GCM mode. + * + * @defgroup botan_gcm botan_gcm + * @{ @ingroup botan_p + */ + +#ifndef BOTAN_GCM_H_ +#define BOTAN_GCM_H_ + +#include + +#ifdef BOTAN_HAS_AEAD_GCM + +#include + +/** + * Constructor to create aead_t implementation. + * + * @param algo algorithm to implement + * @param key_size key size in bytes + * @param salt_size size of implicit salt length + * @return aead_t object, NULL if not supported + */ +aead_t *botan_gcm_create(encryption_algorithm_t algo, size_t key_size, + size_t salt_size); + +#endif + +#endif /** BOTAN_GCM_H_ @}*/ diff --git a/src/libstrongswan/plugins/botan/botan_hasher.c b/src/libstrongswan/plugins/botan/botan_hasher.c new file mode 100644 index 000000000..f3caec387 --- /dev/null +++ b/src/libstrongswan/plugins/botan/botan_hasher.c @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2018 René Korthaus + * Rohde & Schwarz Cybersecurity GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "botan_hasher.h" + +#include + +#include + +typedef struct private_botan_hasher_t private_botan_hasher_t; + +/** + * Private data of botan_hasher_t + */ +struct private_botan_hasher_t { + + /** + * Public part of this class. + */ + botan_hasher_t public; + + /** + * botan hash instance + */ + botan_hash_t hash; +}; + +METHOD(hasher_t, get_hash_size, size_t, + private_botan_hasher_t *this) +{ + size_t len = 0; + if (botan_hash_output_length(this->hash, &len)) + { + return 0; + } + return len; +} + +METHOD(hasher_t, reset, bool, + private_botan_hasher_t *this) +{ + if (botan_hash_clear(this->hash)) + { + return FALSE; + } + return TRUE; +} + +METHOD(hasher_t, get_hash, bool, + private_botan_hasher_t *this, chunk_t chunk, uint8_t *hash) +{ + if (botan_hash_update(this->hash, chunk.ptr, chunk.len)) + { + return FALSE; + } + + if (hash) + { + if (botan_hash_final(this->hash, hash)) + { + return FALSE; + } + } + return TRUE; +} + +METHOD(hasher_t, allocate_hash, bool, + private_botan_hasher_t *this, chunk_t chunk, chunk_t *hash) +{ + if (hash) + { + *hash = chunk_alloc(get_hash_size(this)); + return get_hash(this, chunk, hash->ptr); + } + return get_hash(this, chunk, NULL); +} + +METHOD(hasher_t, destroy, void, + private_botan_hasher_t *this) +{ + botan_hash_destroy(this->hash); + free(this); +} + +/* + * Described in header + */ +botan_hasher_t *botan_hasher_create(hash_algorithm_t algo) +{ + private_botan_hasher_t *this; + const char* hash_name; + + switch (algo) + { + case HASH_SHA1: + hash_name = "SHA-1"; + break; + case HASH_SHA224: + hash_name = "SHA-224"; + break; + case HASH_SHA256: + hash_name = "SHA-256"; + break; + case HASH_SHA384: + hash_name = "SHA-384"; + break; + case HASH_SHA512: + hash_name = "SHA-512"; + break; + default: + return NULL; + } + + INIT(this, + .public = { + .hasher = { + .get_hash = _get_hash, + .allocate_hash = _allocate_hash, + .get_hash_size = _get_hash_size, + .reset = _reset, + .destroy = _destroy, + }, + }, + ); + + if (botan_hash_init(&this->hash, hash_name, 0)) + { + return NULL; + } + + return &this->public; +} diff --git a/src/libstrongswan/plugins/botan/botan_hasher.h b/src/libstrongswan/plugins/botan/botan_hasher.h new file mode 100644 index 000000000..2cb0bc117 --- /dev/null +++ b/src/libstrongswan/plugins/botan/botan_hasher.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2017 René Korthaus, Rohde & Schwarz Cybersecurity + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/** + * @defgroup botan_hasher botan_hasher + * @{ @ingroup botan_p + */ + +#ifndef BOTAN_HASHER_H_ +#define BOTAN_HASHER_H_ + +typedef struct botan_hasher_t botan_hasher_t; + +#include + +/** + * Implementation of hashers using botan. + */ +struct botan_hasher_t { + + /** + * The hasher_t interface. + */ + hasher_t hasher; +}; + +/** + * Constructor to create botan_hasher_t. + * + * @param algo algorithm + * @return botan_hasher_t, NULL if not supported + */ +botan_hasher_t *botan_hasher_create(hash_algorithm_t algo); + +#endif /** BOTAN_HASHER_H_ @}*/ diff --git a/src/libstrongswan/plugins/botan/botan_hmac.c b/src/libstrongswan/plugins/botan/botan_hmac.c new file mode 100644 index 000000000..a1e3514d8 --- /dev/null +++ b/src/libstrongswan/plugins/botan/botan_hmac.c @@ -0,0 +1,178 @@ +/* + * Copyright (C) 2018 René Korthaus + * Rohde & Schwarz Cybersecurity GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "botan_hmac.h" + +#include + +#ifdef BOTAN_HAS_HMAC + +#include +#include +#include + +#include + +typedef struct private_botan_mac_t private_botan_mac_t; + +/** + * Private data of a mac_t object. + */ +struct private_botan_mac_t { + + /** + * Public interface + */ + mac_t public; + + /** + * HMAC + */ + botan_mac_t hmac; +}; + +METHOD(mac_t, set_key, bool, + private_botan_mac_t *this, chunk_t key) +{ + if (botan_mac_set_key(this->hmac, key.ptr, key.len)) + { + return FALSE; + } + return TRUE; +} + +METHOD(mac_t, get_mac, bool, + private_botan_mac_t *this, chunk_t data, uint8_t *out) +{ + if (botan_mac_update(this->hmac, data.ptr, data.len)) + { + return FALSE; + } + + if ( out == NULL) + { + return TRUE; + } + + if (botan_mac_final(this->hmac, out)) + { + return FALSE; + } + + return TRUE; +} + +METHOD(mac_t, get_mac_size, size_t, + private_botan_mac_t *this) +{ + size_t len = 0; + if (botan_mac_output_length(this->hmac, &len)) + { + return 0; + } + return len; +} + +METHOD(mac_t, destroy, void, + private_botan_mac_t *this) +{ + botan_mac_destroy(this->hmac); + free(this); +} + +/* + * Create a Botan-backed implementation of the mac_t interface + */ +static mac_t *hmac_create(hash_algorithm_t algo) +{ + private_botan_mac_t *this; + const char* hmac_name; + + switch (algo) + { + case HASH_SHA1: + hmac_name = "HMAC(SHA-1)"; + break; + case HASH_SHA256: + hmac_name = "HMAC(SHA-256)"; + break; + case HASH_SHA384: + hmac_name = "HMAC(SHA-384)"; + break; + case HASH_SHA512: + hmac_name = "HMAC(SHA-512)"; + break; + default: + return NULL; + } + + INIT(this, + .public = { + .get_mac = _get_mac, + .get_mac_size = _get_mac_size, + .set_key = _set_key, + .destroy = _destroy, + } + ); + + if (botan_mac_init(&this->hmac, hmac_name, 0)) + { + free(this); + return NULL; + } + + return &this->public; +} + +/* + * Described in header + */ +prf_t *botan_hmac_prf_create(pseudo_random_function_t algo) +{ + mac_t *hmac; + + hmac = hmac_create(hasher_algorithm_from_prf(algo)); + if (hmac) + { + return mac_prf_create(hmac); + } + return NULL; +} + +/* + * Described in header + */ +signer_t *botan_hmac_signer_create(integrity_algorithm_t algo) +{ + mac_t *hmac; + size_t trunc; + + hmac = hmac_create(hasher_algorithm_from_integrity(algo, &trunc)); + if (hmac) + { + return mac_signer_create(hmac, trunc); + } + return NULL; +} + +#endif diff --git a/src/libstrongswan/plugins/botan/botan_hmac.h b/src/libstrongswan/plugins/botan/botan_hmac.h new file mode 100644 index 000000000..1deeea961 --- /dev/null +++ b/src/libstrongswan/plugins/botan/botan_hmac.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2018 René Korthaus + * Rohde & Schwarz Cybersecurity GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/** + * Implements HMAC based PRF and signer using Botan's HMAC functions. + * + * @defgroup botan_hmac botan_hmac + * @{ @ingroup botan_p + */ + +#ifndef BOTAN_HMAC_H_ +#define BOTAN_HMAC_H_ + +#include +#include + +/** + * Creates a new prf_t object based on an HMAC. + * + * @param algo algorithm to implement + * @return prf_t object, NULL if not supported + */ +prf_t *botan_hmac_prf_create(pseudo_random_function_t algo); + +/** + * Creates a new signer_t object based on an HMAC. + * + * @param algo algorithm to implement + * @return signer_t, NULL if not supported + */ +signer_t *botan_hmac_signer_create(integrity_algorithm_t algo); + +#endif /** BOTAN_HMAC_H_ @}*/ diff --git a/src/libstrongswan/plugins/botan/botan_plugin.c b/src/libstrongswan/plugins/botan/botan_plugin.c new file mode 100644 index 000000000..3bc388321 --- /dev/null +++ b/src/libstrongswan/plugins/botan/botan_plugin.c @@ -0,0 +1,282 @@ +/* + * Copyright (C) 2018 René Korthaus + * Copyright (C) 2018 Konstantinos Kolelis + * Rohde & Schwarz Cybersecurity GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "botan_plugin.h" +#include "botan_rng.h" +#include "botan_hasher.h" +#include "botan_crypter.h" +#include "botan_diffie_hellman.h" +#include "botan_hmac.h" +#include "botan_rsa_public_key.h" +#include "botan_rsa_private_key.h" +#include "botan_ec_diffie_hellman.h" +#include "botan_ec_public_key.h" +#include "botan_ec_private_key.h" +#include "botan_gcm.h" + +#include + +#include +#include + +typedef struct private_botan_plugin_t private_botan_plugin_t; + +/** + * private data of botan_plugin + */ +struct private_botan_plugin_t { + + /** + * public functions + */ + botan_plugin_t public; +}; + +METHOD(plugin_t, get_name, char*, + private_botan_plugin_t *this) +{ + return "botan"; +} + +METHOD(plugin_t, get_features, int, + private_botan_plugin_t *this, plugin_feature_t *features[]) +{ + static plugin_feature_t f[] = { + +#ifdef BOTAN_HAS_DIFFIE_HELLMAN + /* MODP DH groups */ + PLUGIN_REGISTER(DH, botan_diffie_hellman_create), + PLUGIN_PROVIDE(DH, MODP_3072_BIT), + PLUGIN_PROVIDE(DH, MODP_4096_BIT), + PLUGIN_PROVIDE(DH, MODP_6144_BIT), + PLUGIN_PROVIDE(DH, MODP_8192_BIT), + PLUGIN_PROVIDE(DH, MODP_2048_BIT), + PLUGIN_PROVIDE(DH, MODP_2048_224), + PLUGIN_PROVIDE(DH, MODP_2048_256), + PLUGIN_PROVIDE(DH, MODP_1536_BIT), + PLUGIN_PROVIDE(DH, MODP_1024_BIT), + PLUGIN_PROVIDE(DH, MODP_1024_160), + PLUGIN_PROVIDE(DH, MODP_768_BIT), + PLUGIN_REGISTER(DH, botan_diffie_hellman_create_custom), + PLUGIN_PROVIDE(DH, MODP_CUSTOM), +#endif + /* crypters */ + PLUGIN_REGISTER(CRYPTER, botan_crypter_create), +#ifdef BOTAN_HAS_AES + #ifdef BOTAN_HAS_MODE_CBC + PLUGIN_PROVIDE(CRYPTER, ENCR_AES_CBC, 16), + PLUGIN_PROVIDE(CRYPTER, ENCR_AES_CBC, 24), + PLUGIN_PROVIDE(CRYPTER, ENCR_AES_CBC, 32), + #endif + #ifdef BOTAN_HAS_AEAD_GCM + /* AES GCM */ + PLUGIN_REGISTER(AEAD, botan_gcm_create), + PLUGIN_PROVIDE(AEAD, ENCR_AES_GCM_ICV16, 16), + PLUGIN_PROVIDE(AEAD, ENCR_AES_GCM_ICV16, 24), + PLUGIN_PROVIDE(AEAD, ENCR_AES_GCM_ICV16, 32), + PLUGIN_PROVIDE(AEAD, ENCR_AES_GCM_ICV12, 16), + PLUGIN_PROVIDE(AEAD, ENCR_AES_GCM_ICV12, 24), + PLUGIN_PROVIDE(AEAD, ENCR_AES_GCM_ICV12, 32), + #endif +#endif + /* hashers */ + PLUGIN_REGISTER(HASHER, botan_hasher_create), +#ifdef BOTAN_HAS_SHA1 + PLUGIN_PROVIDE(HASHER, HASH_SHA1), +#endif +#ifdef BOTAN_HAS_SHA2_32 + PLUGIN_PROVIDE(HASHER, HASH_SHA224), + PLUGIN_PROVIDE(HASHER, HASH_SHA256), +#endif +#ifdef BOTAN_HAS_SHA2_64 + PLUGIN_PROVIDE(HASHER, HASH_SHA384), + PLUGIN_PROVIDE(HASHER, HASH_SHA512), +#endif + /* prfs */ +#ifdef BOTAN_HAS_HMAC + PLUGIN_REGISTER(PRF, botan_hmac_prf_create), +#ifdef BOTAN_HAS_SHA1 + PLUGIN_PROVIDE(PRF, PRF_HMAC_SHA1), +#endif +#ifdef BOTAN_HAS_SHA2_32 + PLUGIN_PROVIDE(PRF, PRF_HMAC_SHA2_256), +#endif +#ifdef BOTAN_HAS_SHA2_64 + PLUGIN_PROVIDE(PRF, PRF_HMAC_SHA2_384), + PLUGIN_PROVIDE(PRF, PRF_HMAC_SHA2_512), +#endif + /* signer */ + PLUGIN_REGISTER(SIGNER, botan_hmac_signer_create), +#ifdef BOTAN_HAS_SHA1 + PLUGIN_PROVIDE(SIGNER, AUTH_HMAC_SHA1_96), + PLUGIN_PROVIDE(SIGNER, AUTH_HMAC_SHA1_128), + PLUGIN_PROVIDE(SIGNER, AUTH_HMAC_SHA1_160), +#endif +#ifdef BOTAN_HAS_SHA2_32 + PLUGIN_PROVIDE(SIGNER, AUTH_HMAC_SHA2_256_128), + PLUGIN_PROVIDE(SIGNER, AUTH_HMAC_SHA2_256_256), +#endif +#ifdef BOTAN_HAS_SHA2_64 + PLUGIN_PROVIDE(SIGNER, AUTH_HMAC_SHA2_384_192), + PLUGIN_PROVIDE(SIGNER, AUTH_HMAC_SHA2_384_384), + PLUGIN_PROVIDE(SIGNER, AUTH_HMAC_SHA2_512_256), + PLUGIN_PROVIDE(SIGNER, AUTH_HMAC_SHA2_512_512), +#endif + +#ifdef BOTAN_HAS_ECDH + /* EC DH groups */ + PLUGIN_REGISTER(DH, botan_ec_diffie_hellman_create), + PLUGIN_PROVIDE(DH, ECP_256_BIT), + PLUGIN_PROVIDE(DH, ECP_384_BIT), + PLUGIN_PROVIDE(DH, ECP_521_BIT), + PLUGIN_PROVIDE(DH, ECP_256_BP), + PLUGIN_PROVIDE(DH, ECP_384_BP), + PLUGIN_PROVIDE(DH, ECP_512_BP), +#endif +#endif + /* RSA */ +#ifdef BOTAN_HAS_RSA + /* public/private key loading/generation */ + PLUGIN_REGISTER(PUBKEY, botan_rsa_public_key_load, TRUE), + PLUGIN_PROVIDE(PUBKEY, KEY_RSA), + PLUGIN_REGISTER(PRIVKEY, botan_rsa_private_key_load, TRUE), + PLUGIN_PROVIDE(PRIVKEY, KEY_RSA), + PLUGIN_REGISTER(PRIVKEY_GEN, botan_rsa_private_key_gen, FALSE), + PLUGIN_PROVIDE(PRIVKEY_GEN, KEY_RSA), + /* encryption/signature schemes */ +#ifdef BOTAN_HAS_EMSA_PKCS1 + PLUGIN_PROVIDE(PRIVKEY_SIGN, SIGN_RSA_EMSA_PKCS1_NULL), + PLUGIN_PROVIDE(PUBKEY_VERIFY, SIGN_RSA_EMSA_PKCS1_NULL), +#ifdef BOTAN_HAS_SHA1 + PLUGIN_PROVIDE(PRIVKEY_SIGN, SIGN_RSA_EMSA_PKCS1_SHA1), + PLUGIN_PROVIDE(PRIVKEY_SIGN, SIGN_RSA_EMSA_PKCS1_SHA1), + PLUGIN_PROVIDE(PUBKEY_VERIFY, SIGN_RSA_EMSA_PKCS1_SHA1), + PLUGIN_PROVIDE(PUBKEY_VERIFY, SIGN_RSA_EMSA_PKCS1_SHA1), +#endif +#ifdef BOTAN_HAS_SHA2_32 + PLUGIN_PROVIDE(PRIVKEY_SIGN, SIGN_RSA_EMSA_PKCS1_SHA2_224), + PLUGIN_PROVIDE(PRIVKEY_SIGN, SIGN_RSA_EMSA_PKCS1_SHA2_256), + PLUGIN_PROVIDE(PUBKEY_VERIFY, SIGN_RSA_EMSA_PKCS1_SHA2_224), + PLUGIN_PROVIDE(PUBKEY_VERIFY, SIGN_RSA_EMSA_PKCS1_SHA2_256), +#endif +#ifdef BOTAN_HAS_SHA2_64 + PLUGIN_PROVIDE(PRIVKEY_SIGN, SIGN_RSA_EMSA_PKCS1_SHA2_384), + PLUGIN_PROVIDE(PRIVKEY_SIGN, SIGN_RSA_EMSA_PKCS1_SHA2_512), + PLUGIN_PROVIDE(PUBKEY_VERIFY, SIGN_RSA_EMSA_PKCS1_SHA2_384), + PLUGIN_PROVIDE(PUBKEY_VERIFY, SIGN_RSA_EMSA_PKCS1_SHA2_512), +#endif +#endif +#ifdef BOTAN_HAS_EMSA_PSSR + PLUGIN_PROVIDE(PRIVKEY_SIGN, SIGN_RSA_EMSA_PSS), + PLUGIN_PROVIDE(PUBKEY_VERIFY, SIGN_RSA_EMSA_PSS), +#endif + PLUGIN_PROVIDE(PRIVKEY_DECRYPT, ENCRYPT_RSA_PKCS1), + PLUGIN_PROVIDE(PUBKEY_ENCRYPT, ENCRYPT_RSA_PKCS1), +#ifdef BOTAN_HAS_EME_OAEP +#ifdef BOTAN_HAS_SHA2_32 + PLUGIN_PROVIDE(PUBKEY_ENCRYPT, ENCRYPT_RSA_OAEP_SHA224), + PLUGIN_PROVIDE(PUBKEY_ENCRYPT, ENCRYPT_RSA_OAEP_SHA256), +#endif +#ifdef BOTAN_HAS_SHA2_64 + PLUGIN_PROVIDE(PUBKEY_ENCRYPT, ENCRYPT_RSA_OAEP_SHA384), + PLUGIN_PROVIDE(PUBKEY_ENCRYPT, ENCRYPT_RSA_OAEP_SHA512), +#endif +#endif +#endif /* BOTAN_HAS_RSA */ + +#ifdef BOTAN_HAS_ECDSA + /* EC private/public key loading */ + PLUGIN_REGISTER(PRIVKEY, botan_ec_private_key_load, TRUE), + PLUGIN_PROVIDE(PRIVKEY, KEY_ECDSA), + PLUGIN_REGISTER(PRIVKEY_GEN, botan_ec_private_key_gen, FALSE), + PLUGIN_PROVIDE(PRIVKEY_GEN, KEY_ECDSA), + PLUGIN_REGISTER(PUBKEY, botan_ec_public_key_load, TRUE), + PLUGIN_PROVIDE(PUBKEY, KEY_ECDSA), +#ifdef BOTAN_HAS_EMSA_RAW + PLUGIN_PROVIDE(PRIVKEY_SIGN, SIGN_ECDSA_WITH_NULL), + PLUGIN_PROVIDE(PUBKEY_VERIFY, SIGN_ECDSA_WITH_NULL), +#endif +#ifdef BOTAN_HAS_EMSA1 +#ifdef BOTAN_HAS_SHA1 + PLUGIN_PROVIDE(PRIVKEY_SIGN, SIGN_ECDSA_WITH_SHA1_DER), + PLUGIN_PROVIDE(PUBKEY_VERIFY, SIGN_ECDSA_WITH_SHA1_DER), +#endif +#ifdef BOTAN_HAS_SHA2_32 + PLUGIN_PROVIDE(PRIVKEY_SIGN, SIGN_ECDSA_WITH_SHA256_DER), + PLUGIN_PROVIDE(PUBKEY_VERIFY, SIGN_ECDSA_WITH_SHA256_DER), + PLUGIN_PROVIDE(PRIVKEY_SIGN, SIGN_ECDSA_256), + PLUGIN_PROVIDE(PUBKEY_VERIFY, SIGN_ECDSA_256), +#endif +#ifndef BOTAN_HAS_SHA2_64 + PLUGIN_PROVIDE(PRIVKEY_SIGN, SIGN_ECDSA_WITH_SHA384_DER), + PLUGIN_PROVIDE(PRIVKEY_SIGN, SIGN_ECDSA_WITH_SHA512_DER), + PLUGIN_PROVIDE(PUBKEY_VERIFY, SIGN_ECDSA_WITH_SHA384_DER), + PLUGIN_PROVIDE(PUBKEY_VERIFY, SIGN_ECDSA_WITH_SHA512_DER), + PLUGIN_PROVIDE(PRIVKEY_SIGN, SIGN_ECDSA_384), + PLUGIN_PROVIDE(PRIVKEY_SIGN, SIGN_ECDSA_521), + PLUGIN_PROVIDE(PUBKEY_VERIFY, SIGN_ECDSA_384), + PLUGIN_PROVIDE(PUBKEY_VERIFY, SIGN_ECDSA_521), +#endif +#endif /* BOTAN_HAS_EMSA1 */ +#endif /* BOTAN_HAS_ECDSA */ + + /* random numbers */ +#if BOTAN_HAS_SYSTEM_RNG +#if BOTAN_HAS_HMAC_DRBG + PLUGIN_REGISTER(RNG, botan_rng_create), + PLUGIN_PROVIDE(RNG, RNG_WEAK), + PLUGIN_PROVIDE(RNG, RNG_STRONG), + PLUGIN_PROVIDE(RNG, RNG_TRUE) +#endif +#endif + }; + *features = f; + return countof(f); +} + +METHOD(plugin_t, destroy, void, + private_botan_plugin_t *this) +{ + free(this); +} + +/* + * see header file + */ +plugin_t *botan_plugin_create() +{ + private_botan_plugin_t *this; + + INIT(this, + .public = { + .plugin = { + .get_name = _get_name, + .get_features = _get_features, + .destroy = _destroy, + }, + }, + ); + + return &this->public.plugin; +} diff --git a/src/libstrongswan/plugins/botan/botan_plugin.h b/src/libstrongswan/plugins/botan/botan_plugin.h new file mode 100644 index 000000000..fdb08a90e --- /dev/null +++ b/src/libstrongswan/plugins/botan/botan_plugin.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2018 René Korthaus + * Rohde & Schwarz Cybersecurity GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/** + * @defgroup botan_p botan + * @ingroup plugins + * + * @defgroup botan_plugin botan_plugin + * @{ @ingroup botan_p + */ + +#ifndef BOTAN_PLUGIN_H_ +#define BOTAN_PLUGIN_H_ + +#include + +typedef struct botan_plugin_t botan_plugin_t; + +/** + * Plugin implementing crypto functions using Botan. + */ +struct botan_plugin_t { + + /** + * implements plugin interface + */ + plugin_t plugin; +}; + +#endif /** BOTAN_PLUGIN_H_ @}*/ diff --git a/src/libstrongswan/plugins/botan/botan_rng.c b/src/libstrongswan/plugins/botan/botan_rng.c new file mode 100644 index 000000000..f398f9fb7 --- /dev/null +++ b/src/libstrongswan/plugins/botan/botan_rng.c @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2018 René Korthaus + * Rohde & Schwarz Cybersecurity GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "botan_rng.h" + +#include + +#ifdef BOTAN_HAS_HMAC_DRBG + +#include + +typedef struct private_botan_random_t private_botan_random_t; + +/** + * Private data of an botan_rng_t object. + */ +struct private_botan_random_t { + + /** + * Public botan_rnd_t interface. + */ + botan_random_t public; + + /** + * RNG quality of this instance + */ + rng_quality_t quality; + + /** + * RNG type + */ + const char* rng_name; +}; + +METHOD(rng_t, get_bytes, bool, + private_botan_random_t *this, size_t bytes, uint8_t *buffer) +{ + botan_rng_t rng; + if (botan_rng_init(&rng, this->rng_name)) + { + return FALSE; + } + + if (botan_rng_get(rng, buffer, bytes)) + { + botan_rng_destroy(rng); + return FALSE; + } + + botan_rng_destroy(rng); + return TRUE; +} + +METHOD(rng_t, allocate_bytes, bool, + private_botan_random_t *this, size_t bytes, chunk_t *chunk) +{ + *chunk = chunk_alloc(bytes); + if (!get_bytes(this, chunk->len, chunk->ptr)) + { + chunk_free(chunk); + return FALSE; + } + return TRUE; +} + +METHOD(rng_t, destroy, void, + private_botan_random_t *this) +{ + free(this); +} + +/* + * Described in header. + */ +botan_random_t *botan_rng_create(rng_quality_t quality) +{ + private_botan_random_t *this; + const char* rng_name; + + switch (quality) + { + case RNG_WEAK: + case RNG_STRONG: + rng_name = "user"; + break; + case RNG_TRUE: + rng_name = "system"; + break; + default: + return NULL; + } + + INIT(this, + .public = { + .rng = { + .get_bytes = _get_bytes, + .allocate_bytes = _allocate_bytes, + .destroy = _destroy, + }, + }, + .quality = quality, + .rng_name = rng_name, + ); + + return &this->public; +} + +#endif diff --git a/src/libstrongswan/plugins/botan/botan_rng.h b/src/libstrongswan/plugins/botan/botan_rng.h new file mode 100644 index 000000000..087288863 --- /dev/null +++ b/src/libstrongswan/plugins/botan/botan_rng.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2018 René Korthaus + * Rohde & Schwarz Cybersecurity GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/** + * @defgroup botan_rng botan_rng + * @{ @ingroup botan_p + */ + +#ifndef BOTAN_RNG_H_ +#define BOTAN_RNG_H_ + +typedef struct botan_random_t botan_random_t; + +#include + +/** + * rng_t implementation using botan. + * + * @note botan_rng_t is a botan reserved type. + */ +struct botan_random_t { + + /** + * Implements rng_t. + */ + rng_t rng; +}; + +/** + * Creates a botan_random_t instance. + * + * @param quality required quality of randomness + * @return botan_random_t instance + */ +botan_random_t *botan_rng_create(rng_quality_t quality); + +#endif /** BOTAN_RNG_H_ @} */ diff --git a/src/libstrongswan/plugins/botan/botan_rsa_private_key.c b/src/libstrongswan/plugins/botan/botan_rsa_private_key.c new file mode 100644 index 000000000..c35e0c2b6 --- /dev/null +++ b/src/libstrongswan/plugins/botan/botan_rsa_private_key.c @@ -0,0 +1,964 @@ +/* + * Copyright (C) 2018 René Korthaus + * Rohde & Schwarz Cybersecurity GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "botan_rsa_private_key.h" + +#include + +#ifdef BOTAN_HAS_RSA + +#include "botan_util.h" + +#include + +#include + +typedef struct private_botan_rsa_private_key_t private_botan_rsa_private_key_t; + +/** + * Private data of a botan_rsa_private_key_t object. + */ +struct private_botan_rsa_private_key_t { + /** + * Public interface for this signer. + */ + botan_rsa_private_key_t public; + + /** + * Botan private key + */ + botan_privkey_t key; + + + /** + * reference count + */ + refcount_t ref; +}; + +/** + * Get the binary representation of a named RSA parameter + */ +static int botan_rsa_get_field(botan_privkey_t *key, const char *field_name, + chunk_t *value) +{ + botan_mp_t field; + if (botan_mp_init(&field)) + { + return -1; + } + + if (botan_privkey_get_field(field, *key, field_name)) + { + botan_mp_destroy(field); + return -1; + } + + size_t field_size = 0; + if (botan_mp_num_bytes(field, &field_size)) + { + botan_mp_destroy(field); + return -1; + } + + if (field_size == 0) + { + botan_mp_destroy(field); + return -1; + } + + *value = chunk_alloc(field_size); + if (botan_mp_to_bin(field, value->ptr)) + { + botan_mp_destroy(field); + chunk_clear(value); + return -1; + } + + botan_mp_destroy(field); + return 0; +} + +/** + * Build RSA signature + */ +static bool build_rsa_signature(private_botan_rsa_private_key_t *this, + const char* hash_and_padding, chunk_t data, chunk_t* signature) +{ + botan_pk_op_sign_t sign_op; + + if (botan_pk_op_sign_create(&sign_op, this->key, hash_and_padding, 0)) + { + return FALSE; + } + + botan_rng_t rng; + if (botan_rng_init(&rng, "user")) + { + botan_pk_op_sign_destroy(sign_op); + return FALSE; + } + + /* get size of signature first */ + if (botan_pk_op_sign_update(sign_op, data.ptr, data.len)) + { + botan_rng_destroy(rng); + botan_pk_op_sign_destroy(sign_op); + return FALSE; + } + + signature->len = 0; + if (botan_pk_op_sign_finish(sign_op, rng, NULL, &signature->len) + != BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE) + { + botan_rng_destroy(rng); + botan_pk_op_sign_destroy(sign_op); + return FALSE; + } + + /* now get the signature */ + *signature = chunk_alloc(signature->len); + if (botan_pk_op_sign_update(sign_op, data.ptr, data.len)) + { + chunk_free(signature); + botan_rng_destroy(rng); + botan_pk_op_sign_destroy(sign_op); + return FALSE; + } + + if (botan_pk_op_sign_finish(sign_op, rng, signature->ptr, &signature->len)) + { + chunk_free(signature); + botan_rng_destroy(rng); + botan_pk_op_sign_destroy(sign_op); + return FALSE; + } + + botan_rng_destroy(rng); + botan_pk_op_sign_destroy(sign_op); + return TRUE; +} + +/** + * Build an EMSA PKCS1 signature described in PKCS#1 + */ +static bool build_emsa_pkcs1_signature(private_botan_rsa_private_key_t *this, + const char* hash_and_padding, chunk_t data, chunk_t* signature) +{ + return build_rsa_signature(this, hash_and_padding, data, signature); +} + +static bool botan_get_hash(hash_algorithm_t hash, char* hash_str) +{ + switch (hash) + { + case HASH_SHA1: + sprintf(hash_str, "SHA-1"); + break; + case HASH_SHA224: + sprintf(hash_str, "SHA-224"); + break; + case HASH_SHA256: + sprintf(hash_str, "SHA-256"); + break; + case HASH_SHA384: + sprintf(hash_str, "SHA-384"); + break; + case HASH_SHA512: + sprintf(hash_str, "SHA-512"); + break; + default: + return FALSE; + } + + return TRUE; +} + +/** + * Build an EMSA PSS signature described in PKCS#1 + */ +static bool build_emsa_pss_signature(private_botan_rsa_private_key_t *this, + rsa_pss_params_t *params, chunk_t data, + chunk_t *sig) +{ + char* hash_and_padding, *hash, *mgf1_hash; + char* salt_len = NULL; + size_t len; + bool success = FALSE; + + if (!params) + { + return FALSE; + } + + /* botan currently does not support passing the mgf1 hash */ + if (params->hash != params->mgf1_hash) + { + DBG1(DBG_LIB, "passing mgf1 hash not supported via botan"); + return FALSE; + } + + hash = malloc(8); + if (!botan_get_hash(params->hash, hash)) + { + free(hash); + return FALSE; + } + + mgf1_hash = malloc(8); + if (!botan_get_hash(params->mgf1_hash, mgf1_hash)) + { + free(hash); + free(mgf1_hash); + return FALSE; + } + + if (params->salt_len > RSA_PSS_SALT_LEN_DEFAULT) + { + salt_len = malloc(6); + snprintf(salt_len, 5, "%d", params->salt_len); + } + + len = 24 + strlen(hash) + strlen(mgf1_hash); + hash_and_padding = malloc(len+1); + + if (salt_len) + { + snprintf(hash_and_padding, len, "EMSA-PSS(%s,MGF1,%s)", hash, salt_len); + } + else + { + snprintf(hash_and_padding, len, "EMSA-PSS(%s,MGF1)", hash); + } + + if (build_rsa_signature(this, hash_and_padding, data, sig)) + { + success = TRUE; + } + + if (salt_len) + free(salt_len); + free(hash); + free(mgf1_hash); + free(hash_and_padding); + return success; +} + +METHOD(private_key_t, get_type, key_type_t, + private_botan_rsa_private_key_t *this) +{ + return KEY_RSA; +} + +METHOD(private_key_t, sign, bool, + private_botan_rsa_private_key_t *this, signature_scheme_t scheme, + void *params, chunk_t data, chunk_t *signature) +{ + switch (scheme) + { + case SIGN_RSA_EMSA_PKCS1_NULL: + return build_emsa_pkcs1_signature(this, "EMSA_PKCS1(Raw)", data, + signature); + case SIGN_RSA_EMSA_PKCS1_SHA1: + return build_emsa_pkcs1_signature(this, "EMSA_PKCS1(SHA-1)", data, + signature); + case SIGN_RSA_EMSA_PKCS1_SHA2_224: + return build_emsa_pkcs1_signature(this, "EMSA_PKCS1(SHA-224)", data, + signature); + case SIGN_RSA_EMSA_PKCS1_SHA2_256: + return build_emsa_pkcs1_signature(this, "EMSA_PKCS1(SHA-256)", data, + signature); + case SIGN_RSA_EMSA_PKCS1_SHA2_384: + return build_emsa_pkcs1_signature(this, "EMSA_PKCS1(SHA-384)", data, + signature); + case SIGN_RSA_EMSA_PKCS1_SHA2_512: + return build_emsa_pkcs1_signature(this, "EMSA_PKCS1(SHA-512)", data, + signature); + case SIGN_RSA_EMSA_PSS: + return build_emsa_pss_signature(this, params, data, signature); + default: + DBG1(DBG_LIB, "signature scheme %N not supported via botan", + signature_scheme_names, scheme); + return FALSE; + } +} + +METHOD(private_key_t, decrypt, bool, private_botan_rsa_private_key_t *this, + encryption_scheme_t scheme, chunk_t crypto, chunk_t *plain) +{ + const char *padding; + + switch (scheme) + { + case ENCRYPT_RSA_PKCS1: + padding = "PKCS1v15"; + break; + case ENCRYPT_RSA_OAEP_SHA1: + padding = "OAEP(SHA-1)"; + break; + case ENCRYPT_RSA_OAEP_SHA224: + padding = "OAEP(SHA-224)"; + break; + case ENCRYPT_RSA_OAEP_SHA256: + padding = "OAEP(SHA-256)"; + break; + case ENCRYPT_RSA_OAEP_SHA384: + padding = "OAEP(SHA-384)"; + break; + case ENCRYPT_RSA_OAEP_SHA512: + padding = "OAEP(SHA-512)"; + break; + default: + DBG1(DBG_LIB, "encryption scheme %N not supported via botan", + encryption_scheme_names, scheme); + return FALSE; + } + + botan_pk_op_decrypt_t decrypt_op; + if (botan_pk_op_decrypt_create(&decrypt_op, this->key, padding, 0)) + { + return FALSE; + } + + /* + * get size of plaintext first + */ + if (botan_pk_op_decrypt(decrypt_op, NULL, &plain->len, crypto.ptr, + crypto.len) + != BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE) + { + botan_pk_op_decrypt_destroy(decrypt_op); + return FALSE; + } + + /* + * now get the plaintext + */ + *plain = chunk_alloc(plain->len); + if (botan_pk_op_decrypt(decrypt_op, plain->ptr, &plain->len, crypto.ptr, + crypto.len)) + { + chunk_free(plain); + botan_pk_op_decrypt_destroy(decrypt_op); + return FALSE; + } + + botan_pk_op_decrypt_destroy(decrypt_op); + return TRUE; +} + +METHOD(private_key_t, get_keysize, int, + private_botan_rsa_private_key_t *this) +{ + botan_mp_t n; + if (botan_mp_init(&n)) + { + return -1; + } + + if (botan_privkey_rsa_get_n(n, this->key)) + { + return -1; + } + + size_t bits = 0; + if (botan_mp_num_bits(n, &bits)) + { + botan_mp_destroy(n); + return -1; + } + + botan_mp_destroy(n); + return bits; +} + +METHOD(private_key_t, get_public_key, public_key_t*, + private_botan_rsa_private_key_t *this) +{ + chunk_t n, e; + + if (botan_rsa_get_field(&this->key, "n", &n)) + { + return NULL; + } + + if (botan_rsa_get_field(&this->key, "e", &e)) + { + chunk_clear(&n); + return NULL; + } + + public_key_t *pub_key = lib->creds->create(lib->creds, CRED_PUBLIC_KEY, + KEY_RSA, BUILD_RSA_MODULUS, n, + BUILD_RSA_PUB_EXP, e, BUILD_END); + + chunk_free(&n); + chunk_free(&e); + return pub_key; +} + +METHOD(private_key_t, get_fingerprint, bool, + private_botan_rsa_private_key_t *this, cred_encoding_type_t type, + chunk_t *fingerprint) +{ + chunk_t n, e; + bool success; + + if (lib->encoding->get_cache(lib->encoding, type, &this->key, fingerprint)) + { + return TRUE; + } + + if (botan_rsa_get_field(&this->key, "n", &n)) + { + return FALSE; + } + + if (botan_rsa_get_field(&this->key, "e", &e)) + { + chunk_clear(&n); + return FALSE; + } + + success = lib->encoding->encode(lib->encoding, type, &this->key, + fingerprint, CRED_PART_RSA_MODULUS, n, + CRED_PART_RSA_PUB_EXP, e, CRED_PART_END); + chunk_free(&n); + chunk_free(&e); + return success; + +} + +METHOD(private_key_t, get_encoding, bool, + private_botan_rsa_private_key_t *this, cred_encoding_type_t type, + chunk_t *encoding) +{ + switch (type) + { + case PRIVKEY_ASN1_DER: + case PRIVKEY_PEM: + { + bool success = TRUE; + + uint32_t format = BOTAN_PRIVKEY_EXPORT_FLAG_DER; + if (type == PRIVKEY_PEM) + { + format = BOTAN_PRIVKEY_EXPORT_FLAG_PEM; + } + + size_t bits = 0; + if(botan_privkey_rsa_get_privkey(this->key, NULL, &bits, format)) + { + return FALSE; + } + + *encoding = chunk_alloc(bits); + if(botan_privkey_rsa_get_privkey(this->key, encoding->ptr, &bits, format)) + { + chunk_clear(encoding); + return FALSE; + } + + return success; + } + default: + return FALSE; + } +} + +METHOD(private_key_t, get_ref, private_key_t*, + private_botan_rsa_private_key_t *this) +{ + ref_get(&this->ref); + return &this->public.key; +} + +METHOD(private_key_t, destroy, void, + private_botan_rsa_private_key_t *this) +{ + if (ref_put(&this->ref)) + { + if (&this->key) + { + lib->encoding->clear_cache(lib->encoding, &this->key); + botan_privkey_destroy(this->key); + } + free(this); + } +} + +/** + * Internal generic constructor + */ +static private_botan_rsa_private_key_t *create_empty() +{ + private_botan_rsa_private_key_t *this; + + INIT(this, + .public = { + .key = { + .get_type = _get_type, + .sign = _sign, + .decrypt = _decrypt, + .get_keysize = _get_keysize, + .get_public_key = _get_public_key, + .equals = private_key_equals, + .belongs_to = private_key_belongs_to, + .get_fingerprint = _get_fingerprint, + .has_fingerprint = private_key_has_fingerprint, + .get_encoding = _get_encoding, + .get_ref = _get_ref, + .destroy = _destroy, + }, + }, + .ref = 1, + ); + + return this; +} + +/* + * See header. + */ +botan_rsa_private_key_t *botan_rsa_private_key_gen(key_type_t type, + va_list args) +{ + private_botan_rsa_private_key_t *this; + + u_int key_size = 0; + + while (TRUE) + { + switch (va_arg(args, builder_part_t)) + { + case BUILD_KEY_SIZE: + key_size = va_arg(args, u_int); + continue; + case BUILD_END: + break; + default: + return NULL; + } + break; + } + + if (!key_size) + { + return NULL; + } + + botan_rng_t rng; + if (botan_rng_init(&rng, "user")) + { + return NULL; + } + + this = create_empty(); + + if(botan_privkey_create_rsa(&this->key, rng, key_size)) + { + botan_rng_destroy(rng); + destroy(this); + return NULL; + } + + botan_rng_destroy(rng); + return &this->public; +} + +/** + * Recover the primes from n, e and d using the algorithm described in + * Appendix C of NIST SP 800-56B. + */ +static bool calculate_pq(botan_mp_t *n, botan_mp_t *e, botan_mp_t *d, + botan_mp_t *p, botan_mp_t *q) +{ + botan_mp_t k, one, r, zero, two, n1, x, y, g, rem; + int i, t, j; + bool success = TRUE; + + if (botan_mp_init(&k)) + { + success = FALSE; + goto error; + } + + if (botan_mp_init(&one)) + { + success = FALSE; + goto error; + } + + if (botan_mp_set_from_int(one, 1)) + { + success = FALSE; + goto error; + } + + /* 1. k = de - 1 */ + if (botan_mp_mul(k, *d, *e) || botan_mp_sub(k, k, one)) + { + success = FALSE; + goto error; + } + + /* k must be even */ + if (!botan_mp_is_even(k)) + { + success = FALSE; + goto error; + } + + /* 2. k = 2^t * r, where r is the largest odd integer dividing k, and t >= 1 */ + if (botan_mp_init(&r)) + { + success = FALSE; + goto error; + } + + if (botan_mp_set_from_mp(r, k)) + { + success = FALSE; + goto error; + } + + for (t = 0; !botan_mp_is_odd(r); t++) + { + if (botan_mp_rshift(r, r, 1)) + { + success = FALSE; + goto error; + } + } + + /* need 0, 2, n-1 below */ + if (botan_mp_init(&zero)) + { + success = FALSE; + goto error; + } + + if (botan_mp_set_from_int(zero, 0)) + { + success = FALSE; + goto error; + } + + if (botan_mp_init(&n1)) + { + success = FALSE; + goto error; + } + + if (botan_mp_sub(n1, *n, one)) + { + success = FALSE; + goto error; + } + + if (botan_mp_init(&g)) + { + success = FALSE; + goto error; + } + + botan_rng_t rng; + if (botan_rng_init(&rng, "user")) + { + success = FALSE; + goto error; + } + + if (botan_mp_init(&two)) + { + success = FALSE; + goto error; + } + + if (botan_mp_set_from_int(two, 2)) + { + success = FALSE; + goto error; + } + + for (i = 0; i < 100; i++) + { + /* 3a. generate a random integer g in the range [0, n-1] */ + if (botan_mp_rand_range(g, rng, zero, n1)) + { + success = FALSE; + goto error; + } + + /* 3b. y = g^r mod n */ + if (botan_mp_init(&y)) + { + success = FALSE; + goto error; + } + + if (botan_mp_powmod(y, g, r, *n)) + { + success = FALSE; + goto error; + } + + /* 3c. If y = 1 or y = n – 1, try again */ + if (botan_mp_equal(y, one) || botan_mp_equal(y, n1)) + { + continue; + } + + if (botan_mp_init(&x)) + { + success = FALSE; + goto error; + } + + for (j = 0; j < t; j++) + { + /* x = y^2 mod n */ + if (botan_mp_powmod(x, y, two, *n)) + { + success = FALSE; + goto error; + } + + /* stop if x == 1 */ + if (botan_mp_equal(x, one)) + { + goto done; + } + + /* retry with new g if x = n-1 */ + if (botan_mp_equal(x, n1)) + { + break; + } + + /* let y = x */ + if(botan_mp_set_from_mp(y, x)) + { + success = FALSE; + goto error; + } + } + } + +done: + /* 5. p = GCD(y – 1, n) and q = n/p */ + if (botan_mp_sub(y, y, one)) + { + success = FALSE; + goto error; + } + + if (botan_mp_init(p)) + { + success = FALSE; + goto error; + } + + if (botan_mp_gcd(*p, y, *n)) + { + success = FALSE; + goto error; + } + + if (botan_mp_init(q)) + { + success = FALSE; + goto error; + } + + if (botan_mp_init(&rem)) + { + success = FALSE; + goto error; + } + + if (botan_mp_div(*q, rem, *n, *p)) + { + success = FALSE; + goto error; + } + + if (!botan_mp_is_zero(rem)) + { + success = FALSE; + goto error; + } + +error: + if (!success) + { + botan_mp_destroy(*p); + botan_mp_destroy(*q); + } + + botan_mp_destroy(k); + botan_mp_destroy(one); + botan_mp_destroy(r); + botan_mp_destroy(zero); + botan_mp_destroy(two); + botan_mp_destroy(n1); + botan_mp_destroy(x); + botan_mp_destroy(y); + botan_mp_destroy(rem); + return success; +} + +/* + * See header + */ +botan_rsa_private_key_t *botan_rsa_private_key_load(key_type_t type, + va_list args) +{ + private_botan_rsa_private_key_t *this; + chunk_t n, e, d, p, q, blob; + + n = e = d = p = q = blob = chunk_empty; + while (TRUE) + { + switch (va_arg(args, builder_part_t)) + { + case BUILD_BLOB_ASN1_DER: + blob = va_arg(args, chunk_t); + continue; + case BUILD_RSA_MODULUS: + n = va_arg(args, chunk_t); + continue; + case BUILD_RSA_PUB_EXP: + e = va_arg(args, chunk_t); + continue; + case BUILD_RSA_PRIV_EXP: + d = va_arg(args, chunk_t); + continue; + case BUILD_RSA_PRIME1: + p = va_arg(args, chunk_t); + continue; + case BUILD_RSA_PRIME2: + q = va_arg(args, chunk_t); + continue; + case BUILD_RSA_EXP1: + case BUILD_RSA_EXP2: + case BUILD_RSA_COEFF: + /* not required for botan */ + va_arg(args, chunk_t); + continue; + case BUILD_END: + break; + default: + return NULL; + } + break; + } + + if (blob.ptr) + { + this = create_empty(); + + if (botan_privkey_load_rsa_pkcs1(&this->key, blob.ptr, blob.len)) + { + destroy(this); + return NULL; + } + + return &this->public; + } + + if (n.ptr && e.ptr && d.ptr) + { + botan_mp_t n_mp, e_mp, d_mp; + if (chunk_to_botan_mp(n, &n_mp)) + { + return NULL; + } + + if (chunk_to_botan_mp(e, &e_mp)) + { + botan_mp_destroy(n_mp); + return NULL; + } + + if (chunk_to_botan_mp(d, &d_mp)) + { + botan_mp_destroy(n_mp); + botan_mp_destroy(e_mp); + return NULL; + } + + botan_mp_t p_mp, q_mp; + if (p.ptr && q.ptr) + { + if (chunk_to_botan_mp(p, &p_mp)) + { + botan_mp_destroy(n_mp); + botan_mp_destroy(e_mp); + botan_mp_destroy(d_mp); + return NULL; + } + + if (chunk_to_botan_mp(q, &q_mp)) + { + botan_mp_destroy(n_mp); + botan_mp_destroy(e_mp); + botan_mp_destroy(d_mp); + botan_mp_destroy(p_mp); + return NULL; + } + } + else + { + // calculate p,q from n, e, d + if (!calculate_pq(&n_mp, &e_mp, &d_mp, &p_mp, &q_mp)) + { + botan_mp_destroy(n_mp); + botan_mp_destroy(e_mp); + botan_mp_destroy(d_mp); + return NULL; + } + } + + this = create_empty(); + + if (botan_privkey_load_rsa(&this->key, p_mp, q_mp, e_mp)) + { + botan_mp_destroy(e_mp); + botan_mp_destroy(p_mp); + botan_mp_destroy(q_mp); + destroy(this); + return NULL; + } + + botan_mp_destroy(e_mp); + botan_mp_destroy(p_mp); + botan_mp_destroy(q_mp); + + return &this->public; + } + + return NULL; +} + +#endif diff --git a/src/libstrongswan/plugins/botan/botan_rsa_private_key.h b/src/libstrongswan/plugins/botan/botan_rsa_private_key.h new file mode 100644 index 000000000..546b88dac --- /dev/null +++ b/src/libstrongswan/plugins/botan/botan_rsa_private_key.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2018 René Korthaus + * Rohde & Schwarz Cybersecurity GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/** + * @defgroup botan_rsa_private_key botan_rsa_private_key + * @{ @ingroup botan_p + */ + +#ifndef BOTAN_RSA_PRIVATE_KEY_H_ +#define BOTAN_RSA_PRIVATE_KEY_H_ + +#include +#include + +typedef struct botan_rsa_private_key_t botan_rsa_private_key_t; + +/** + * private_key_t implementation of RSA algorithm using Botan. + */ +struct botan_rsa_private_key_t { + + /** + * Implements private_key_t interface + */ + private_key_t key; +}; + +/** + * Generate a RSA private key using Botan. + * + * Accepts the BUILD_KEY_SIZE argument. + * + * @param type type of the key, must be KEY_RSA + * @param args builder_part_t argument list + * @return generated key, NULL on failure + */ +botan_rsa_private_key_t *botan_rsa_private_key_gen(key_type_t type, + va_list args); + +/** + * Load a RSA private key using Botan. + * + * Accepts a BUILD_BLOB_ASN1_DER argument. + * + * @param type type of the key, must be KEY_RSA + * @param args builder_part_t argument list + * @return loaded key, NULL on failure + */ +botan_rsa_private_key_t *botan_rsa_private_key_load(key_type_t type, + va_list args); + +#endif /** BOTAN_RSA_PRIVATE_KEY_H_ @}*/ diff --git a/src/libstrongswan/plugins/botan/botan_rsa_public_key.c b/src/libstrongswan/plugins/botan/botan_rsa_public_key.c new file mode 100644 index 000000000..b2445b5f9 --- /dev/null +++ b/src/libstrongswan/plugins/botan/botan_rsa_public_key.c @@ -0,0 +1,601 @@ +/* + * Copyright (C) 2018 René Korthaus + * Rohde & Schwarz Cybersecurity GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "botan_rsa_public_key.h" + +#include + +#ifdef BOTAN_HAS_RSA + +#include "botan_util.h" + +#include +#include +#include + +#include + +#include + +typedef struct private_botan_rsa_public_key_t private_botan_rsa_public_key_t; + +/** + * Private data structure with signing context. + */ +struct private_botan_rsa_public_key_t { + /** + * Public interface for this signer + */ + botan_rsa_public_key_t public; + + /** + * Botan public key + */ + botan_pubkey_t key; + + /** + * Reference counter + */ + refcount_t ref; +}; + +/** + * Get the binary representation of a named RSA parameter + */ +static int botan_rsa_get_field(botan_pubkey_t *key, const char *field_name, + chunk_t *value) +{ + botan_mp_t field; + size_t field_size = 0; + + if (botan_mp_init(&field)) + { + return -1; + } + + if (botan_pubkey_get_field(field, *key, field_name)) + { + return -1; + } + + if (botan_mp_num_bytes(field, &field_size)) + { + botan_mp_destroy(field); + return -1; + } + + if (field_size == 0) + { + botan_mp_destroy(field); + return -1; + } + + *value = chunk_empty; + *value = chunk_alloc(field_size); + if (botan_mp_to_bin(field, value->ptr)) + { + botan_mp_destroy(field); + chunk_clear(value); + return -1; + } + + return 0; +} + +/** + * Verify RSA signature + */ +static bool verify_rsa_signature(private_botan_rsa_public_key_t *this, + const char* hash_and_padding, chunk_t data, chunk_t signature) +{ + botan_pk_op_verify_t verify_op; + bool valid = FALSE; + + if (botan_pk_op_verify_create(&verify_op, this->key, hash_and_padding, 0)) + { + return FALSE; + } + + if (botan_pk_op_verify_update(verify_op, data.ptr, data.len)) + { + botan_pk_op_verify_destroy(verify_op); + return FALSE; + } + + valid = + !(botan_pk_op_verify_finish(verify_op, signature.ptr, signature.len)); + + botan_pk_op_verify_destroy(verify_op); + return valid; +} + +/** + * Verification of an EMSA PKCS1 signature described in PKCS#1 + */ +static bool verify_emsa_pkcs1_signature(private_botan_rsa_public_key_t *this, + const char* hash_and_padding, chunk_t data, chunk_t signature) +{ + return verify_rsa_signature(this, hash_and_padding, data, signature); +} + +static bool botan_get_hash(hash_algorithm_t hash, char* hash_str) +{ + switch (hash) + { + case HASH_SHA1: + sprintf(hash_str, "SHA-1"); + break; + case HASH_SHA224: + sprintf(hash_str, "SHA-224"); + break; + case HASH_SHA256: + sprintf(hash_str, "SHA-256"); + break; + case HASH_SHA384: + sprintf(hash_str, "SHA-384"); + break; + case HASH_SHA512: + sprintf(hash_str, "SHA-512"); + break; + default: + return FALSE; + } + + return TRUE; +} + +/** + * Verification of an EMSA PSS signature described in PKCS#1 + */ +static bool verify_emsa_pss_signature(private_botan_rsa_public_key_t *this, + rsa_pss_params_t *params, chunk_t data, + chunk_t signature) +{ + char* hash_and_padding, *hash, *mgf1_hash; + char* salt_len = NULL; + size_t len; + bool success = FALSE; + + if (!params) + { + return FALSE; + } + + // botan currently does not support passing the mgf1 hash + if (params->hash != params->mgf1_hash) + { + DBG1(DBG_LIB, "passing mgf1 hash not supported via botan"); + return FALSE; + } + + hash = malloc(8); + if(!botan_get_hash(params->hash, hash)) + { + free(hash); + return FALSE; + } + + mgf1_hash = malloc(8); + if(!botan_get_hash(params->mgf1_hash, mgf1_hash)) + { + free(hash); + free(mgf1_hash); + return FALSE; + } + + if(params->salt_len > RSA_PSS_SALT_LEN_DEFAULT) + { + salt_len = malloc(6); + snprintf(salt_len, 5, "%d", params->salt_len); + } + + len = 24 + strlen(hash) + strlen(mgf1_hash); + hash_and_padding = malloc(len+1); + + if(salt_len) + { + snprintf(hash_and_padding, len, "EMSA-PSS(%s,MGF1,%s)", hash, salt_len); + } + else + { + snprintf(hash_and_padding, len, "EMSA-PSS(%s,MGF1)", hash); + } + + if (verify_rsa_signature(this, hash_and_padding, data, signature)) + { + success = TRUE; + } + + if(salt_len) + free(salt_len); + free(hash); + free(mgf1_hash); + free(hash_and_padding); + return success; +} + +METHOD(public_key_t, get_type, key_type_t, + private_botan_rsa_public_key_t *this) +{ + return KEY_RSA; +} + +METHOD(public_key_t, verify, bool, + private_botan_rsa_public_key_t *this, signature_scheme_t scheme, + void *params, chunk_t data, chunk_t signature) +{ + switch (scheme) + { + case SIGN_RSA_EMSA_PKCS1_NULL: + return verify_emsa_pkcs1_signature(this, "EMSA_PKCS1(Raw)", data, + signature); + case SIGN_RSA_EMSA_PKCS1_SHA1: + return verify_emsa_pkcs1_signature(this, "EMSA_PKCS1(SHA-1)", data, + signature); + case SIGN_RSA_EMSA_PKCS1_SHA2_224: + return verify_emsa_pkcs1_signature(this, "EMSA_PKCS1(SHA-224)", + data, signature); + case SIGN_RSA_EMSA_PKCS1_SHA2_256: + return verify_emsa_pkcs1_signature(this, "EMSA_PKCS1(SHA-256)", + data, signature); + case SIGN_RSA_EMSA_PKCS1_SHA2_384: + return verify_emsa_pkcs1_signature(this, "EMSA_PKCS1(SHA-384)", + data, signature); + case SIGN_RSA_EMSA_PKCS1_SHA2_512: + return verify_emsa_pkcs1_signature(this, "EMSA_PKCS1(SHA-512)", + data, signature); + case SIGN_RSA_EMSA_PSS: + return verify_emsa_pss_signature(this, params, data, signature); + default: + DBG1(DBG_LIB, "signature scheme %N not supported via botan", + signature_scheme_names, scheme); + return FALSE; + } +} + +METHOD(public_key_t, encrypt, bool, + private_botan_rsa_public_key_t *this, encryption_scheme_t scheme, + chunk_t plain, chunk_t *crypto) +{ + const char* padding; + + switch (scheme) + { + case ENCRYPT_RSA_PKCS1: + padding = "PKCS1v15"; + break; + case ENCRYPT_RSA_OAEP_SHA1: + padding = "OAEP(SHA-1)"; + break; + case ENCRYPT_RSA_OAEP_SHA224: + padding = "OAEP(SHA-224)"; + break; + case ENCRYPT_RSA_OAEP_SHA256: + padding = "OAEP(SHA-256)"; + break; + case ENCRYPT_RSA_OAEP_SHA384: + padding = "OAEP(SHA-384)"; + break; + case ENCRYPT_RSA_OAEP_SHA512: + padding = "OAEP(SHA-512)"; + break; + default: + DBG1(DBG_LIB, "encryption scheme %N not supported via botan", + encryption_scheme_names, scheme); + return FALSE; + } + + botan_rng_t rng; + if (botan_rng_init(&rng, "user")) + { + return FALSE; + } + + botan_pk_op_encrypt_t encrypt_op; + if (botan_pk_op_encrypt_create(&encrypt_op, this->key, padding, 0)) + { + botan_rng_destroy(rng); + return FALSE; + } + + /* + * get size of ciphertext first + */ + if (botan_pk_op_encrypt(encrypt_op, rng, NULL, &crypto->len, plain.ptr, + plain.len) + != BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE) + { + botan_rng_destroy(rng); + botan_pk_op_encrypt_destroy(encrypt_op); + return FALSE; + } + + /* + * now get the ciphertext + */ + *crypto = chunk_alloc(crypto->len); + if (botan_pk_op_encrypt(encrypt_op, rng, crypto->ptr, &crypto->len, + plain.ptr, plain.len)) + { + chunk_free(crypto); + botan_rng_destroy(rng); + botan_pk_op_encrypt_destroy(encrypt_op); + return FALSE; + } + + botan_rng_destroy(rng); + botan_pk_op_encrypt_destroy(encrypt_op); + return TRUE; +} + +METHOD(public_key_t, get_keysize, int, + private_botan_rsa_public_key_t *this) +{ + botan_mp_t n; + size_t bits = 0; + + if (botan_mp_init(&n)) + { + return -1; + } + + if (botan_pubkey_rsa_get_n(n, this->key)) + { + return -1; + } + + if (botan_mp_num_bits(n, &bits)) + { + botan_mp_destroy(n); + return -1; + } + + botan_mp_destroy(n); + return bits; +} + +METHOD(public_key_t, get_fingerprint, bool, + private_botan_rsa_public_key_t *this, cred_encoding_type_t type, + chunk_t *fp) +{ + chunk_t n, e; + bool success = FALSE; + + if (lib->encoding->get_cache(lib->encoding, type, &this->key, fp)) + { + return TRUE; + } + + if (botan_rsa_get_field(&this->key, "n", &n)) + { + return FALSE; + } + + if (botan_rsa_get_field(&this->key, "e", &e)) + { + chunk_free(&n); + return FALSE; + } + + success = lib->encoding->encode(lib->encoding, type, &this->key, fp, + CRED_PART_RSA_MODULUS, n, + CRED_PART_RSA_PUB_EXP, e, CRED_PART_END); + + chunk_free(&n); + chunk_free(&e); + return success; +} + +METHOD(public_key_t, get_encoding, bool, + private_botan_rsa_public_key_t *this, cred_encoding_type_t type, + chunk_t *encoding) +{ + chunk_t n, e; + bool success = FALSE; + + if (botan_rsa_get_field(&this->key, "n", &n)) + { + return FALSE; + } + + if (botan_rsa_get_field(&this->key, "e", &e)) + { + chunk_free(&n); + return FALSE; + } + + success = lib->encoding->encode(lib->encoding, type, NULL, encoding, + CRED_PART_RSA_MODULUS, n, + CRED_PART_RSA_PUB_EXP, e, CRED_PART_END); + + chunk_free(&n); + chunk_free(&e); + return success; +} + +METHOD(public_key_t, get_ref, public_key_t*, + private_botan_rsa_public_key_t *this) +{ + ref_get(&this->ref); + return &this->public.key; +} + +METHOD(public_key_t, destroy, void, + private_botan_rsa_public_key_t *this) +{ + if (ref_put(&this->ref)) + { + if (&this->key) + { + lib->encoding->clear_cache(lib->encoding, &this->key); + botan_pubkey_destroy(this->key); + } + free(this); + } +} + +/** + * Internal generic constructor + */ +static private_botan_rsa_public_key_t *create_empty() +{ + private_botan_rsa_public_key_t *this; + + INIT(this, + .public = { + .key = { + .get_type = _get_type, + .verify = _verify, + .encrypt = _encrypt, + .equals = public_key_equals, + .get_keysize = _get_keysize, + .get_fingerprint = _get_fingerprint, + .has_fingerprint = public_key_has_fingerprint, + .get_encoding = _get_encoding, + .get_ref = _get_ref, + .destroy = _destroy, + }, + }, + .ref = 1, + ); + + return this; +} + +/** + * See header. + */ +botan_rsa_public_key_t *botan_rsa_public_key_load(key_type_t type, + va_list args) +{ + private_botan_rsa_public_key_t *this = NULL; + + chunk_t blob, n, e; + + n = e = blob = chunk_empty; + while (TRUE) + { + switch (va_arg(args, builder_part_t)) + { + case BUILD_BLOB_ASN1_DER: + blob = va_arg(args, chunk_t); + continue; + case BUILD_RSA_MODULUS: + n = va_arg(args, chunk_t); + continue; + case BUILD_RSA_PUB_EXP: + e = va_arg(args, chunk_t); + continue; + case BUILD_END: + break; + default: + return NULL; + } + break; + } + + if (blob.ptr) + { + switch (type) + { + /* SubjectPublicKeyInfo */ + case KEY_ANY: + { + this = create_empty(); + + if (botan_pubkey_load(&this->key, blob.ptr, blob.len)) + { + destroy(this); + return NULL; + } + + size_t namesize = 0; + if (botan_pubkey_algo_name(this->key, NULL, &namesize) + != BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE) + { + botan_pubkey_destroy(this->key); + destroy(this); + return NULL; + } + + char* namebuf = malloc(namesize); + if (botan_pubkey_algo_name(this->key, namebuf, &namesize)) + { + free(namebuf); + botan_pubkey_destroy(this->key); + destroy(this); + return NULL; + } + + const char* algo_name = "RSA"; + if (!strneq(namebuf, algo_name, sizeof(algo_name))) + { + free(namebuf); + botan_pubkey_destroy(this->key); + destroy(this); + return NULL; + } + + free(namebuf); + break; + } + default: + return NULL; + } + } + else if(n.ptr && e.ptr && type == KEY_RSA) + { + + botan_mp_t mp_n, mp_e; + if (chunk_to_botan_mp(n, &mp_n)) + { + return NULL; + } + + if (chunk_to_botan_mp(e, &mp_e)) + { + botan_mp_destroy(mp_n); + return NULL; + } + + this = create_empty(); + + if (botan_pubkey_load_rsa(&this->key, mp_n, mp_e)) + { + botan_mp_destroy(mp_n); + botan_mp_destroy(mp_e); + destroy(this); + return NULL; + } + + botan_mp_destroy(mp_n); + botan_mp_destroy(mp_e); + } + if (this != NULL) + { + return &this->public; + } + return NULL; +} + +#endif \ No newline at end of file diff --git a/src/libstrongswan/plugins/botan/botan_rsa_public_key.h b/src/libstrongswan/plugins/botan/botan_rsa_public_key.h new file mode 100644 index 000000000..4f6be8333 --- /dev/null +++ b/src/libstrongswan/plugins/botan/botan_rsa_public_key.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2018 René Korthaus + * Rohde & Schwarz Cybersecurity GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/** + * @defgroup botan_rsa_public_key botan_rsa_public_key + * @{ @ingroup botan_p + */ + +#ifndef BOTAN_RSA_PUBLIC_KEY_H_ +#define BOTAN_RSA_PUBLIC_KEY_H_ + +typedef struct botan_rsa_public_key_t botan_rsa_public_key_t; + +#include + +/** + * public_key_t implementation of RSA algorithm using Botan. + */ +struct botan_rsa_public_key_t { + + /** + * Implements the public_key_t interface + */ + public_key_t key; +}; + +/** + * Load a RSA public key using Botan. + * + * Accepts a BUILD_BLOB_ASN1_DER argument. + * + * @param type type of the key, must be KEY_RSA + * @param args builder_part_t argument list + * @return loaded key, NULL on failure + */ +botan_rsa_public_key_t *botan_rsa_public_key_load(key_type_t type, + va_list args); + +#endif /** BOTAN_RSA_PUBLIC_KEY_H_ @}*/ diff --git a/src/libstrongswan/plugins/botan/botan_util.c b/src/libstrongswan/plugins/botan/botan_util.c new file mode 100644 index 000000000..d69e29e62 --- /dev/null +++ b/src/libstrongswan/plugins/botan/botan_util.c @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2018 René Korthaus + * Rohde & Schwarz Cybersecurity GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "botan_util.h" + +#include + +#include + +int chunk_to_botan_mp(chunk_t value, botan_mp_t *mp) +{ + if (botan_mp_init(mp)) + { + return -1; + } + + if (botan_mp_from_bin(*mp, value.ptr, value.len)) + { + botan_mp_destroy(*mp); + return -1; + } + return 0; +} diff --git a/src/libstrongswan/plugins/botan/botan_util.h b/src/libstrongswan/plugins/botan/botan_util.h new file mode 100644 index 000000000..d19658740 --- /dev/null +++ b/src/libstrongswan/plugins/botan/botan_util.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2018 René Korthaus + * Rohde & Schwarz Cybersecurity GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/** + * @defgroup botan_util botan_util + * @{ @ingroup botan_p + */ + +#ifndef BOTAN_UTIL_H_ +#define BOTAN_UTIL_H_ + +#include + +#include + +/** + * Converts chunk_t to botan_mp_t + */ +int chunk_to_botan_mp(chunk_t value, botan_mp_t *mp); + +#endif /** BOTAN_UTIL_H_ @}*/ From 04ecaff6a97b09c3f495f3b5652ae17d45fa756e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Korthaus?= Date: Thu, 26 Jul 2018 11:17:07 +0200 Subject: [PATCH 02/23] unit-tests: Remove 768 bits RSA gen test Botan only allows RSA generating keys >= 1,024 bits, which makes the RSA test suite fail. It is questionable whether it makes sense to test 768 bit RSA keys anymore. They are too weak from today's perspective anyway. --- src/libstrongswan/tests/suites/test_rsa.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libstrongswan/tests/suites/test_rsa.c b/src/libstrongswan/tests/suites/test_rsa.c index 3f6353404..e6dc7744a 100644 --- a/src/libstrongswan/tests/suites/test_rsa.c +++ b/src/libstrongswan/tests/suites/test_rsa.c @@ -146,7 +146,7 @@ static void test_bad_sigs(public_key_t *pubkey) * RSA key sizes to test */ static int key_sizes[] = { - 768, 1024, 1536, 2048, 3072, 4096, + 1024, 1536, 2048, 3072, 4096, }; START_TEST(test_gen) From 13f113f7a939494392e1d1ec121c83b6536ef66c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Korthaus?= Date: Fri, 27 Jul 2018 09:33:39 +0200 Subject: [PATCH 03/23] botan: Add MD5 support to Botan hasher Support MD5 in the Botan plugin if supported by Botan. MD5 is required for RADIUS and obviously EAP-MD5, and also for non-PKCS#8 encoded, encrypted private keys. --- src/libstrongswan/plugins/botan/botan_hasher.c | 3 +++ src/libstrongswan/plugins/botan/botan_hasher.h | 3 ++- src/libstrongswan/plugins/botan/botan_plugin.c | 3 +++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/libstrongswan/plugins/botan/botan_hasher.c b/src/libstrongswan/plugins/botan/botan_hasher.c index f3caec387..06b2ad9b2 100644 --- a/src/libstrongswan/plugins/botan/botan_hasher.c +++ b/src/libstrongswan/plugins/botan/botan_hasher.c @@ -112,6 +112,9 @@ botan_hasher_t *botan_hasher_create(hash_algorithm_t algo) switch (algo) { + case HASH_MD5: + hash_name = "MD5"; + break; case HASH_SHA1: hash_name = "SHA-1"; break; diff --git a/src/libstrongswan/plugins/botan/botan_hasher.h b/src/libstrongswan/plugins/botan/botan_hasher.h index 2cb0bc117..164f63711 100644 --- a/src/libstrongswan/plugins/botan/botan_hasher.h +++ b/src/libstrongswan/plugins/botan/botan_hasher.h @@ -1,5 +1,6 @@ /* - * Copyright (C) 2017 René Korthaus, Rohde & Schwarz Cybersecurity + * Copyright (C) 2018 René Korthaus + * Rohde & Schwarz Cybersecurity GmbH * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/src/libstrongswan/plugins/botan/botan_plugin.c b/src/libstrongswan/plugins/botan/botan_plugin.c index 3bc388321..0fb9159c1 100644 --- a/src/libstrongswan/plugins/botan/botan_plugin.c +++ b/src/libstrongswan/plugins/botan/botan_plugin.c @@ -102,6 +102,9 @@ METHOD(plugin_t, get_features, int, #endif /* hashers */ PLUGIN_REGISTER(HASHER, botan_hasher_create), +#ifdef BOTAN_HAS_MD5 + PLUGIN_PROVIDE(HASHER, HASH_MD5), +#endif #ifdef BOTAN_HAS_SHA1 PLUGIN_PROVIDE(HASHER, HASH_SHA1), #endif From de2a24310c1b86f6652560f927b77d5e724730cb Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Mon, 6 Aug 2018 17:46:54 +0200 Subject: [PATCH 04/23] botan: Fixes, code style changes plus some refactorings Some changes rely on newly added FFI functions in Botan's master branch. --- src/libstrongswan/plugins/botan/Makefile.am | 2 +- .../plugins/botan/botan_crypter.c | 32 +- .../plugins/botan/botan_diffie_hellman.c | 102 ++-- .../plugins/botan/botan_diffie_hellman.h | 19 +- .../plugins/botan/botan_ec_diffie_hellman.c | 81 ++-- .../plugins/botan/botan_ec_diffie_hellman.h | 5 +- .../plugins/botan/botan_ec_private_key.c | 194 +++----- .../plugins/botan/botan_ec_private_key.h | 6 - .../plugins/botan/botan_ec_public_key.c | 207 ++------ src/libstrongswan/plugins/botan/botan_gcm.c | 50 +- src/libstrongswan/plugins/botan/botan_gcm.h | 14 +- .../plugins/botan/botan_hasher.c | 34 +- src/libstrongswan/plugins/botan/botan_hmac.c | 10 +- .../plugins/botan/botan_plugin.c | 5 +- src/libstrongswan/plugins/botan/botan_rng.c | 38 +- .../plugins/botan/botan_rsa_private_key.c | 453 +++++------------- .../plugins/botan/botan_rsa_public_key.c | 286 +++-------- src/libstrongswan/plugins/botan/botan_util.c | 193 +++++++- src/libstrongswan/plugins/botan/botan_util.h | 55 ++- 19 files changed, 694 insertions(+), 1092 deletions(-) diff --git a/src/libstrongswan/plugins/botan/Makefile.am b/src/libstrongswan/plugins/botan/Makefile.am index 95df44130..bbad71541 100644 --- a/src/libstrongswan/plugins/botan/Makefile.am +++ b/src/libstrongswan/plugins/botan/Makefile.am @@ -23,7 +23,7 @@ libstrongswan_botan_la_SOURCES = \ botan_ec_diffie_hellman.h botan_ec_diffie_hellman.c \ botan_ec_public_key.h botan_ec_public_key.c \ botan_ec_private_key.h botan_ec_private_key.c \ - botan_util.c \ + botan_util.h botan_util.c \ botan_gcm.h botan_gcm.c libstrongswan_botan_la_LDFLAGS = -module -avoid-version diff --git a/src/libstrongswan/plugins/botan/botan_crypter.c b/src/libstrongswan/plugins/botan/botan_crypter.c index af8fe4c43..002be6ea8 100644 --- a/src/libstrongswan/plugins/botan/botan_crypter.c +++ b/src/libstrongswan/plugins/botan/botan_crypter.c @@ -65,7 +65,7 @@ static bool crypt(private_botan_crypter_t *this, chunk_t data, chunk_t iv, in = data.ptr; if (dst) { - *dst= chunk_alloc(data.len); + *dst = chunk_alloc(data.len); out = dst->ptr; } else @@ -77,13 +77,13 @@ static bool crypt(private_botan_crypter_t *this, chunk_t data, chunk_t iv, { return FALSE; } - - if (!botan_cipher_set_key(cipher, this->key.ptr, this->key.len) - && !botan_cipher_start(cipher, iv.ptr, iv.len) - && !botan_cipher_update(cipher, BOTAN_CIPHER_UPDATE_FLAG_FINAL, out, - data.len, &output_written, in, data.len, - &input_consumed) - && (output_written == input_consumed)) + + if (!botan_cipher_set_key(cipher, this->key.ptr, this->key.len) && + !botan_cipher_start(cipher, iv.ptr, iv.len) && + !botan_cipher_update(cipher, BOTAN_CIPHER_UPDATE_FLAG_FINAL, out, + data.len, &output_written, in, data.len, + &input_consumed) && + (output_written == input_consumed)) { success = TRUE; } @@ -93,38 +93,38 @@ static bool crypt(private_botan_crypter_t *this, chunk_t data, chunk_t iv, } METHOD(crypter_t, decrypt, bool, - private_botan_crypter_t *this, chunk_t data, chunk_t iv, chunk_t *dst) + private_botan_crypter_t *this, chunk_t data, chunk_t iv, chunk_t *dst) { return crypt(this, data, iv, dst, BOTAN_CIPHER_INIT_FLAG_DECRYPT); } METHOD(crypter_t, encrypt, bool, - private_botan_crypter_t *this, chunk_t data, chunk_t iv, chunk_t *dst) + private_botan_crypter_t *this, chunk_t data, chunk_t iv, chunk_t *dst) { return crypt(this, data, iv, dst, BOTAN_CIPHER_INIT_FLAG_ENCRYPT); } METHOD(crypter_t, get_block_size, size_t, - private_botan_crypter_t *this) + private_botan_crypter_t *this) { return AES_BLOCK_SIZE; } METHOD(crypter_t, get_iv_size, size_t, - private_botan_crypter_t *this) + private_botan_crypter_t *this) { return AES_BLOCK_SIZE; } METHOD(crypter_t, get_key_size, size_t, - private_botan_crypter_t *this) + private_botan_crypter_t *this) { return this->key.len; } METHOD(crypter_t, set_key, bool, - private_botan_crypter_t *this, chunk_t key) + private_botan_crypter_t *this, chunk_t key) { memcpy(this->key.ptr, key.ptr, min(key.len, this->key.len)); return TRUE; @@ -159,7 +159,6 @@ botan_crypter_t *botan_crypter_create(encryption_algorithm_t algo, }, ); - switch (algo) { case ENCR_AES_CBC: @@ -183,11 +182,8 @@ botan_crypter_t *botan_crypter_create(encryption_algorithm_t algo, } break; default: - { free(this); return NULL; - - } } this->key = chunk_alloc(key_size); diff --git a/src/libstrongswan/plugins/botan/botan_diffie_hellman.c b/src/libstrongswan/plugins/botan/botan_diffie_hellman.c index 2ca14f35a..422b4aea1 100644 --- a/src/libstrongswan/plugins/botan/botan_diffie_hellman.c +++ b/src/libstrongswan/plugins/botan/botan_diffie_hellman.c @@ -70,22 +70,27 @@ struct private_botan_diffie_hellman_t { * Modulus */ botan_mp_t p; - }; +/** + * Load a DH private key + */ bool load_private_key(private_botan_diffie_hellman_t *this, chunk_t value) { botan_mp_t xa; - if (chunk_to_botan_mp(value, &xa)) + + if (!chunk_to_botan_mp(value, &xa)) { return FALSE; } if (botan_privkey_destroy(this->dh_key) || - botan_privkey_load_dh (&this->dh_key, this->p, this->g, xa)) + botan_privkey_load_dh(&this->dh_key, this->p, this->g, xa)) { + botan_mp_destroy(xa); return FALSE; } + botan_mp_destroy(xa); return TRUE; } @@ -99,25 +104,29 @@ METHOD(diffie_hellman_t, set_other_public_value, bool, return FALSE; } - chunk_clear(&this->shared_secret); - botan_pk_op_key_agreement_create(&op, this->dh_key, "Raw", 0); - - /* get shared secret key size */ - if (botan_pk_op_key_agreement(op, NULL, &this->shared_secret.len, value.ptr, - value.len, NULL, 0) - != BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE) + if (botan_pk_op_key_agreement_create(&op, this->dh_key, "Raw", 0)) { return FALSE; } + chunk_clear(&this->shared_secret); + + if (botan_pk_op_key_agreement_size(op, &this->shared_secret.len)) + { + botan_pk_op_key_agreement_destroy(op); + return FALSE; + } + this->shared_secret = chunk_alloc(this->shared_secret.len); if (botan_pk_op_key_agreement(op, this->shared_secret.ptr, - &this->shared_secret.len, value.ptr, - value.len, NULL, 0)) + &this->shared_secret.len, value.ptr, + value.len, NULL, 0)) { + chunk_clear(&this->shared_secret); + botan_pk_op_key_agreement_destroy(op); return FALSE; } - + botan_pk_op_key_agreement_destroy(op); return TRUE; } @@ -128,36 +137,35 @@ METHOD(diffie_hellman_t, get_my_public_value, bool, /* get key size of public key first */ if (botan_pk_op_key_agreement_export_public(this->dh_key, NULL, &value->len) - != BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE) + != BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE) { return FALSE; } *value = chunk_alloc(value->len); if (botan_pk_op_key_agreement_export_public(this->dh_key, value->ptr, - &value->len)) + &value->len)) { chunk_clear(value); return FALSE; } - return TRUE; } METHOD(diffie_hellman_t, set_private_value, bool, private_botan_diffie_hellman_t *this, chunk_t value) { + chunk_clear(&this->shared_secret); return load_private_key(this, value); } METHOD(diffie_hellman_t, get_shared_secret, bool, private_botan_diffie_hellman_t *this, chunk_t *secret) { - if (this->shared_secret.len == 0) + if (!this->shared_secret.len) { return FALSE; } - *secret = chunk_clone(this->shared_secret); return TRUE; } @@ -181,10 +189,12 @@ METHOD(diffie_hellman_t, destroy, void, /* * Generic internal constructor */ -botan_diffie_hellman_t *create_generic(diffie_hellman_group_t group, - chunk_t g, chunk_t p) +static botan_diffie_hellman_t *create_generic(diffie_hellman_group_t group, + chunk_t g, chunk_t p, size_t exp_len) { private_botan_diffie_hellman_t *this; + chunk_t random; + rng_t *rng; INIT(this, .public = { @@ -200,42 +210,52 @@ botan_diffie_hellman_t *create_generic(diffie_hellman_group_t group, .group = group, ); - if (chunk_to_botan_mp(p, &this->p)) + if (!chunk_to_botan_mp(p, &this->p)) { destroy(this); return NULL; } - if (chunk_to_botan_mp(g, &this->g)) + if (!chunk_to_botan_mp(g, &this->g)) { destroy(this); return NULL; } - chunk_t random; - rng_t *rng; rng = lib->crypto->create_rng(lib->crypto, RNG_STRONG); - if (rng && rng->allocate_bytes(rng, p.len, &random)) + if (!rng || !rng->allocate_bytes(rng, p.len, &random)) { - rng->destroy(rng); - if (!load_private_key(this, random)) - { - destroy(this); - chunk_clear(&random); - return NULL; - } + DESTROY_IF(rng); + destroy(this); + return NULL; } + rng->destroy(rng); + if (!load_private_key(this, random)) + { + chunk_clear(&random); + destroy(this); + return NULL; + } + chunk_clear(&random); return &this->public; } /* * Described in header. */ -botan_diffie_hellman_t * -botan_diffie_hellman_create(diffie_hellman_group_t group) +botan_diffie_hellman_t *botan_diffie_hellman_create( + diffie_hellman_group_t group, ...) { diffie_hellman_params_t *params; + chunk_t g, p; + + if (group == MODP_CUSTOM) + { + VA_ARGS_GET(group, g, p); + return create_generic(group, g, p); + } + params = diffie_hellman_get_params(group); if (!params) { @@ -244,18 +264,4 @@ botan_diffie_hellman_create(diffie_hellman_group_t group) return create_generic(group, params->generator, params->prime); } -/* - * Described in header. - */ -botan_diffie_hellman_t * -botan_diffie_hellman_create_custom(diffie_hellman_group_t group, chunk_t g, - chunk_t p) -{ - if (group == MODP_CUSTOM) - { - return create_generic(group, g, p); - } - return NULL; -} - #endif diff --git a/src/libstrongswan/plugins/botan/botan_diffie_hellman.h b/src/libstrongswan/plugins/botan/botan_diffie_hellman.h index 88f221d34..84408229f 100644 --- a/src/libstrongswan/plugins/botan/botan_diffie_hellman.h +++ b/src/libstrongswan/plugins/botan/botan_diffie_hellman.h @@ -49,24 +49,11 @@ struct botan_diffie_hellman_t { * Creates a new botan_diffie_hellman_t object. * * @param group Diffie Hellman group number to use + * @param ... expects generator and prime as chunk_t if MODP_CUSTOM * @return botan_diffie_hellman_t object, * NULL if not supported */ -botan_diffie_hellman_t * -botan_diffie_hellman_create(diffie_hellman_group_t group); - -/** - * Creates a new botan_diffie_hellman_t object for MODP_CUSTOM. - * - * @param group MODP_CUSTOM - * @param g generator - * @param p prime - * @return botan_diffie_hellman_t object, - * NULL if not supported - */ -botan_diffie_hellman_t * -botan_diffie_hellman_create_custom(diffie_hellman_group_t group, chunk_t g, - chunk_t p); +botan_diffie_hellman_t *botan_diffie_hellman_create( + diffie_hellman_group_t group, ...); #endif /** BOTAN_DIFFIE_HELLMAN_H_ @}*/ - diff --git a/src/libstrongswan/plugins/botan/botan_ec_diffie_hellman.c b/src/libstrongswan/plugins/botan/botan_ec_diffie_hellman.c index 48b231f88..a482bc028 100644 --- a/src/libstrongswan/plugins/botan/botan_ec_diffie_hellman.c +++ b/src/libstrongswan/plugins/botan/botan_ec_diffie_hellman.c @@ -27,19 +27,21 @@ #ifdef BOTAN_HAS_ECDH +#include "botan_util.h" + #include #include -typedef struct private_botan_ec_diffie_hellman_t - private_botan_ec_diffie_hellman_t; +typedef struct private_botan_ec_diffie_hellman_t private_botan_ec_diffie_hellman_t; /** * Private data of a botan_ec_diffie_hellman_t object. */ struct private_botan_ec_diffie_hellman_t { + /** - * Public botan_ec_diffie_hellman_t interface + * Public interface */ botan_ec_diffie_hellman_t public; @@ -62,47 +64,35 @@ struct private_botan_ec_diffie_hellman_t { * Shared secret */ chunk_t shared_secret; - - /** - * True if shared secret is computed - */ - bool computed; }; METHOD(diffie_hellman_t, set_other_public_value, bool, private_botan_ec_diffie_hellman_t *this, chunk_t value) { + botan_pk_op_ka_t ka; + if (!diffie_hellman_verify_value(this->group, value)) { return FALSE; } - botan_pk_op_ka_t ka; if (botan_pk_op_key_agreement_create(&ka, this->key, "Raw", 0)) { return FALSE; } - /* prepend 0x04 to indicate uncompressed point format */ - uint8_t indic = 0x04; - value = chunk_cata("cc", chunk_from_thing(indic), value); - size_t out_len = 0; - if (botan_pk_op_key_agreement(ka, NULL, &out_len, value.ptr, value.len, - NULL, 0) - != BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE) - { - botan_pk_op_key_agreement_destroy(ka); - return FALSE; - } - - if (out_len == 0) - { - botan_pk_op_key_agreement_destroy(ka); - return FALSE; - } - chunk_clear(&this->shared_secret); - this->shared_secret = chunk_alloc(out_len); + + if (botan_pk_op_key_agreement_size(ka, &this->shared_secret.len)) + { + botan_pk_op_key_agreement_destroy(ka); + return FALSE; + } + + /* prepend 0x04 to indicate uncompressed point format */ + value = chunk_cata("cc", chunk_from_chars(0x04), value); + + this->shared_secret = chunk_alloc(this->shared_secret.len); if (botan_pk_op_key_agreement(ka, this->shared_secret.ptr, &this->shared_secret.len, value.ptr, value.len, NULL, 0)) @@ -111,9 +101,7 @@ METHOD(diffie_hellman_t, set_other_public_value, bool, botan_pk_op_key_agreement_destroy(ka); return FALSE; } - botan_pk_op_key_agreement_destroy(ka); - this->computed = TRUE; return TRUE; } @@ -121,6 +109,7 @@ METHOD(diffie_hellman_t, get_my_public_value, bool, private_botan_ec_diffie_hellman_t *this, chunk_t *value) { chunk_t pkey = chunk_empty; + if (botan_pk_op_key_agreement_export_public(this->key, NULL, &pkey.len) != BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE) { @@ -142,14 +131,11 @@ METHOD(diffie_hellman_t, set_private_value, bool, private_botan_ec_diffie_hellman_t *this, chunk_t value) { botan_mp_t scalar; - if (botan_mp_init(&scalar)) - { - return FALSE; - } - if (botan_mp_from_bin(scalar, value.ptr, value.len)) + chunk_clear(&this->shared_secret); + + if (!chunk_to_botan_mp(value, &scalar)) { - botan_mp_destroy(scalar); return FALSE; } @@ -166,14 +152,13 @@ METHOD(diffie_hellman_t, set_private_value, bool, } botan_mp_destroy(scalar); - this->computed = FALSE; return TRUE; } METHOD(diffie_hellman_t, get_shared_secret, bool, private_botan_ec_diffie_hellman_t *this, chunk_t *secret) { - if (!this->computed) + if (!this->shared_secret.len) { return FALSE; } @@ -198,10 +183,11 @@ METHOD(diffie_hellman_t, destroy, void, /* * Described in header. */ -botan_ec_diffie_hellman_t * -botan_ec_diffie_hellman_create(diffie_hellman_group_t group) +botan_ec_diffie_hellman_t *botan_ec_diffie_hellman_create( + diffie_hellman_group_t group) { private_botan_ec_diffie_hellman_t *this; + botan_rng_t rng; INIT(this, .public = { @@ -242,6 +228,21 @@ botan_ec_diffie_hellman_create(diffie_hellman_group_t group) return NULL; } + if (botan_rng_init(&rng, "user")) + { + free(this); + return NULL; + } + + if (botan_privkey_create_ecdh(&this->key, rng, this->curve_name)) + { + DBG1(DBG_LIB, "ECDH private key generation failed"); + botan_rng_destroy(rng); + free(this); + return NULL; + } + + botan_rng_destroy(rng); return &this->public; } diff --git a/src/libstrongswan/plugins/botan/botan_ec_diffie_hellman.h b/src/libstrongswan/plugins/botan/botan_ec_diffie_hellman.h index f4751de01..0ba832ed3 100644 --- a/src/libstrongswan/plugins/botan/botan_ec_diffie_hellman.h +++ b/src/libstrongswan/plugins/botan/botan_ec_diffie_hellman.h @@ -50,8 +50,7 @@ struct botan_ec_diffie_hellman_t { * @param group EC Diffie Hellman group number to use * @return botan_ec_diffie_hellman_t object, NULL if not supported */ -botan_ec_diffie_hellman_t * -botan_ec_diffie_hellman_create(diffie_hellman_group_t group); +botan_ec_diffie_hellman_t *botan_ec_diffie_hellman_create( + diffie_hellman_group_t group); #endif /** BOTAN_EC_DIFFIE_HELLMAN_H_ @}*/ - diff --git a/src/libstrongswan/plugins/botan/botan_ec_private_key.c b/src/libstrongswan/plugins/botan/botan_ec_private_key.c index abb4597ad..e5a6c7c4d 100644 --- a/src/libstrongswan/plugins/botan/botan_ec_private_key.c +++ b/src/libstrongswan/plugins/botan/botan_ec_private_key.c @@ -24,6 +24,7 @@ #include "botan_ec_private_key.h" +#include "botan_util.h" #include @@ -43,8 +44,9 @@ typedef struct private_botan_ec_private_key_t private_botan_ec_private_key_t; * Private data of a botan_ec_private_key_t object. */ struct private_botan_ec_private_key_t { + /** - * Public interface for this signer. + * Public interface */ botan_ec_private_key_t public; @@ -62,10 +64,6 @@ struct private_botan_ec_private_key_t { #define SIG_FORMAT_IEEE_1363 0 #define SIG_FORMAT_DER_SEQUENCE 1 -/* implemented in ec public key */ -bool botan_ec_fingerprint(botan_pubkey_t *ec, cred_encoding_type_t type, - chunk_t *fp); - /** * Build a DER encoded signature as in RFC 3279 or as in RFC 4754 */ @@ -73,72 +71,23 @@ static bool build_signature(botan_privkey_t key, const char *hash_and_padding, int signature_format, chunk_t data, chunk_t *signature) { - if (!hash_and_padding || !signature) + if (!botan_get_signature(key, hash_and_padding, data, signature)) { return FALSE; } - botan_pk_op_sign_t sign_op; - - if (botan_pk_op_sign_create(&sign_op, key, hash_and_padding, 0)) - { - return FALSE; - } - - botan_rng_t rng; - if (botan_rng_init(&rng, "user")) - { - return FALSE; - } - - /* get size of signature first */ - if (botan_pk_op_sign_update(sign_op, data.ptr, data.len)) - { - botan_rng_destroy(rng); - botan_pk_op_sign_destroy(sign_op); - return FALSE; - } - - signature->len = 0; - if (botan_pk_op_sign_finish(sign_op, rng, NULL, &signature->len) - != BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE) - { - botan_rng_destroy(rng); - botan_pk_op_sign_destroy(sign_op); - return FALSE; - } - - /* now get the signature */ - *signature = chunk_alloc(signature->len); - if (botan_pk_op_sign_update(sign_op, data.ptr, data.len)) - { - botan_rng_destroy(rng); - botan_pk_op_sign_destroy(sign_op); - return FALSE; - } - - if (botan_pk_op_sign_finish(sign_op, rng, signature->ptr, &signature->len)) - { - botan_rng_destroy(rng); - botan_pk_op_sign_destroy(sign_op); - return FALSE; - } - - botan_rng_destroy(rng); - botan_pk_op_sign_destroy(sign_op); - if (signature_format == SIG_FORMAT_DER_SEQUENCE) { /* format as ASN.1 sequence of two integers r,s */ - chunk_t r, s = chunk_empty; + chunk_t r = chunk_empty, s = chunk_empty; + chunk_split(*signature, "aa", signature->len / 2, &r, - signature->len / 2, &s); + signature->len / 2, &s); chunk_free(signature); - *signature = asn1_wrap(ASN1_SEQUENCE, "mm", asn1_integer("c", r), - asn1_integer("c", s)); + *signature = asn1_wrap(ASN1_SEQUENCE, "mm", asn1_integer("m", r), + asn1_integer("m", s)); } - return TRUE; } @@ -148,12 +97,12 @@ METHOD(private_key_t, sign, bool, { switch (scheme) { + /* r||s -> Botan::IEEE_1363, data is the hash already */ case SIGN_ECDSA_WITH_NULL: - /* r||s -> Botan::IEEE_1363, data is the hash already */ return build_signature(this->key, "Raw", - SIG_FORMAT_IEEE_1363, data, signature); + SIG_FORMAT_IEEE_1363, data, signature); + /* DER SEQUENCE of two INTEGERS r,s -> Botan::DER_SEQUENCE */ case SIGN_ECDSA_WITH_SHA1_DER: - /* DER SEQUENCE of two INTEGERS r,s -> Botan::DER_SEQUENCE */ return build_signature(this->key, "EMSA1(SHA-1)", SIG_FORMAT_DER_SEQUENCE, data, signature); case SIGN_ECDSA_WITH_SHA256_DER: @@ -165,16 +114,14 @@ METHOD(private_key_t, sign, bool, case SIGN_ECDSA_WITH_SHA512_DER: return build_signature(this->key, "EMSA1(SHA-512)", SIG_FORMAT_DER_SEQUENCE, data, signature); + /* r||s -> Botan::IEEE_1363 */ case SIGN_ECDSA_256: - /* r||s -> Botan::IEEE_1363 */ return build_signature(this->key, "EMSA1(SHA-256)", SIG_FORMAT_IEEE_1363, data, signature); case SIGN_ECDSA_384: - /* r||s -> Botan::IEEE_1363 */ return build_signature(this->key, "EMSA1(SHA-384)", SIG_FORMAT_IEEE_1363, data, signature); case SIGN_ECDSA_521: - /* r||s -> Botan::IEEE_1363 */ return build_signature(this->key, "EMSA1(SHA-512)", SIG_FORMAT_IEEE_1363, data, signature); default: @@ -196,19 +143,15 @@ METHOD(private_key_t, get_keysize, int, private_botan_ec_private_key_t *this) { botan_mp_t p; - if(botan_mp_init(&p)) - { - return 0; - } - - if(botan_privkey_get_field(p, this->key, "p")) - { - botan_mp_destroy(p); - return 0; - } - size_t bits = 0; - if(botan_mp_num_bits(p, &bits)) + + if (botan_mp_init(&p)) + { + return 0; + } + + if (botan_privkey_get_field(p, this->key, "p") || + botan_mp_num_bits(p, &bits)) { botan_mp_destroy(p); return 0; @@ -228,9 +171,9 @@ METHOD(private_key_t, get_public_key, public_key_t*, private_botan_ec_private_key_t *this) { public_key_t *public; + botan_pubkey_t pubkey; chunk_t key = chunk_empty; - botan_pubkey_t pubkey; if (botan_privkey_export_pubkey(&pubkey, this->key)) { return FALSE; @@ -255,7 +198,7 @@ METHOD(private_key_t, get_public_key, public_key_t*, } public = lib->creds->create(lib->creds, CRED_PUBLIC_KEY, KEY_ECDSA, - BUILD_BLOB_ASN1_DER, key, BUILD_END); + BUILD_BLOB_ASN1_DER, key, BUILD_END); chunk_free(&key); botan_pubkey_destroy(pubkey); @@ -266,15 +209,20 @@ METHOD(private_key_t, get_fingerprint, bool, private_botan_ec_private_key_t *this, cred_encoding_type_t type, chunk_t *fingerprint) { + botan_pubkey_t pubkey; bool success = FALSE; - botan_pubkey_t pubkey; + /* check the cache before doing the export */ + if (lib->encoding->get_cache(lib->encoding, type, this, fingerprint)) + { + return TRUE; + } + if (botan_privkey_export_pubkey(&pubkey, this->key)) { return FALSE; } - - success = botan_ec_fingerprint(&pubkey, type, fingerprint); + success = botan_get_fingerprint(pubkey, this, type, fingerprint); botan_pubkey_destroy(pubkey); return success; } @@ -288,9 +236,10 @@ METHOD(private_key_t, get_encoding, bool, case PRIVKEY_ASN1_DER: case PRIVKEY_PEM: { + botan_mp_t x; + chunk_t pval = chunk_empty; bool success = TRUE; - botan_mp_t x; if (botan_mp_init(&x)) { return FALSE; @@ -302,7 +251,6 @@ METHOD(private_key_t, get_encoding, bool, return FALSE; } - chunk_t pval = chunk_empty; if (botan_mp_num_bytes(x, &pval.len)) { botan_mp_destroy(x); @@ -317,10 +265,12 @@ METHOD(private_key_t, get_encoding, bool, return FALSE; } - chunk_t version = chunk_from_chars( 0x01 ); - *encoding = - asn1_wrap(ASN1_SEQUENCE, "ms", asn1_integer("c", version), - asn1_wrap(ASN1_OCTET_STRING, "s", pval)); + /* FIXME: this does not include the params, which the parser/loader + * below actually requires (and is mandated by RFC 5915). we might + * have to store/parse the OID so we can add it here. */ + *encoding = asn1_wrap(ASN1_SEQUENCE, "ms", + asn1_integer("c", chunk_from_chars(0x01)), + asn1_wrap(ASN1_OCTET_STRING, "s", pval)); if (type == PRIVKEY_PEM) { @@ -353,11 +303,8 @@ METHOD(private_key_t, destroy, void, { if (ref_put(&this->ref)) { - if (&this->key) - { - lib->encoding->clear_cache(lib->encoding, &this->key); - botan_privkey_destroy(this->key); - } + lib->encoding->clear_cache(lib->encoding, this); + botan_privkey_destroy(this->key); free(this); } } @@ -393,13 +340,14 @@ static private_botan_ec_private_key_t *create_empty() } /* - * See header. + * Described in header */ botan_ec_private_key_t *botan_ec_private_key_gen(key_type_t type, va_list args) { private_botan_ec_private_key_t *this; + botan_rng_t rng; u_int key_size = 0; - const char* curve; + const char *curve; while (TRUE) { @@ -438,19 +386,18 @@ botan_ec_private_key_t *botan_ec_private_key_gen(key_type_t type, va_list args) return NULL; } - botan_rng_t rng; - if (botan_rng_init(&rng, "user")) + if (botan_rng_init(&rng, "system")) { return NULL; } this = create_empty(); - if(botan_privkey_create_ecdsa(&this->key, rng, curve)) + if (botan_privkey_create_ecdsa(&this->key, rng, curve)) { - DBG1(DBG_LIB, "EC private key generation failed", key_size); + DBG1(DBG_LIB, "EC private key generation failed"); botan_rng_destroy(rng); - destroy(this); + free(this); return NULL; } @@ -462,25 +409,26 @@ botan_ec_private_key_t *botan_ec_private_key_gen(key_type_t type, va_list args) * ASN.1 definition of a ECPrivateKey structure (RFC 5915) */ static const asn1Object_t ecPrivateKeyObjects[] = { - { 0, "ECPrivateKey", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */ - { 1, "version", ASN1_INTEGER, ASN1_BODY }, /* 1 */ - { 1, "privateKey", ASN1_OCTET_STRING, ASN1_BODY }, /* 2 */ - { 1, "parameters", ASN1_EOC, ASN1_RAW }, /* 3 */ - { 1, "publicKey", ASN1_BIT_STRING, ASN1_OPT }, /* 4 */ - { 0, "exit", ASN1_EOC, ASN1_EXIT } + { 0, "ECPrivateKey", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */ + { 1, "version", ASN1_INTEGER, ASN1_BODY }, /* 1 */ + { 1, "privateKey", ASN1_OCTET_STRING, ASN1_BODY }, /* 2 */ + { 1, "parameters", ASN1_EOC, ASN1_RAW }, /* 3 */ + { 1, "publicKey", ASN1_BIT_STRING, ASN1_OPT }, /* 4 */ + { 0, "exit", ASN1_EOC, ASN1_EXIT } }; #define ECPK_PRIVATE_KEY 2 #define ECPK_PRIVATE_KEY_PARAMS 3 -/** - * See header. +/* + * Described in header */ botan_ec_private_key_t *botan_ec_private_key_load(key_type_t type, va_list args) { private_botan_ec_private_key_t *this; chunk_t params = chunk_empty, key = chunk_empty; - chunk_t object, pkcs8 = chunk_empty; + chunk_t object, alg_id = chunk_empty, pkcs8 = chunk_empty; + botan_rng_t rng; while (TRUE) { @@ -500,18 +448,15 @@ botan_ec_private_key_t *botan_ec_private_key_load(key_type_t type, va_list args) break; } - this = create_empty(); - /* * botan expects a PKCS#8 private key, so we build one * RFC 5282 mandates ECParameters as part of the algorithmIdentifier */ - chunk_t alg_id = chunk_empty; - if(params.len != 0) + if (params.len != 0) { /* if ECDomainParameters is passed, just append it */ - alg_id = asn1_wrap(ASN1_SEQUENCE, "mc", - asn1_build_known_oid(OID_EC_PUBLICKEY), params); + alg_id = asn1_algorithmIdentifier_params(OID_EC_PUBLICKEY, + chunk_clone(params)); } else { @@ -541,27 +486,26 @@ botan_ec_private_key_t *botan_ec_private_key_load(key_type_t type, va_list args) parser->destroy(parser); return NULL; } - break; } } parser->destroy(parser); - alg_id = asn1_wrap(ASN1_SEQUENCE, "mc", - asn1_build_known_oid(OID_EC_PUBLICKEY), - asn1_simple_object(ASN1_OID, params)); + alg_id = asn1_algorithmIdentifier_params(OID_EC_PUBLICKEY, + asn1_simple_object(ASN1_OID, params)); } - pkcs8 = asn1_wrap(ASN1_SEQUENCE, "cms", + pkcs8 = asn1_wrap(ASN1_SEQUENCE, "mms", asn1_integer("c", chunk_from_chars(0x00)), alg_id, asn1_wrap(ASN1_OCTET_STRING, "c", key)); - botan_rng_t rng; + this = create_empty(); + if (botan_rng_init(&rng, "user")) { chunk_clear(&pkcs8); - destroy(this); + free(this); return NULL; } @@ -569,7 +513,7 @@ botan_ec_private_key_t *botan_ec_private_key_load(key_type_t type, va_list args) { chunk_clear(&pkcs8); botan_rng_destroy(rng); - destroy(this); + free(this); return NULL; } @@ -578,4 +522,4 @@ botan_ec_private_key_t *botan_ec_private_key_load(key_type_t type, va_list args) return &this->public; } -#endif \ No newline at end of file +#endif diff --git a/src/libstrongswan/plugins/botan/botan_ec_private_key.h b/src/libstrongswan/plugins/botan/botan_ec_private_key.h index 0fcfda25e..9450331a4 100644 --- a/src/libstrongswan/plugins/botan/botan_ec_private_key.h +++ b/src/libstrongswan/plugins/botan/botan_ec_private_key.h @@ -30,10 +30,6 @@ #ifndef BOTAN_EC_PRIVATE_KEY_H_ #define BOTAN_EC_PRIVATE_KEY_H_ -#include - -#ifdef BOTAN_HAS_ECDSA - #include #include @@ -73,6 +69,4 @@ botan_ec_private_key_t *botan_ec_private_key_gen(key_type_t type, va_list args); botan_ec_private_key_t *botan_ec_private_key_load(key_type_t type, va_list args); -#endif - #endif /** BOTAN_EC_PRIVATE_KEY_H_ @}*/ diff --git a/src/libstrongswan/plugins/botan/botan_ec_public_key.c b/src/libstrongswan/plugins/botan/botan_ec_public_key.c index abc513b70..54782bcbf 100644 --- a/src/libstrongswan/plugins/botan/botan_ec_public_key.c +++ b/src/libstrongswan/plugins/botan/botan_ec_public_key.c @@ -23,6 +23,7 @@ */ #include "botan_ec_public_key.h" +#include "botan_util.h" #include @@ -41,6 +42,7 @@ typedef struct private_botan_ec_public_key_t private_botan_ec_public_key_t; * Private data structure with signing context. */ struct private_botan_ec_public_key_t { + /** * Public interface for this signer */ @@ -67,7 +69,9 @@ static bool verify_signature(private_botan_ec_public_key_t *this, const char* hash_and_padding, int signature_format, size_t keylen, chunk_t data, chunk_t signature) { - chunk_t sig; + botan_pk_op_verify_t verify_op; + chunk_t sig = signature; + bool valid = FALSE; if (signature_format == SIG_FORMAT_DER_SEQUENCE) { @@ -75,12 +79,11 @@ static bool verify_signature(private_botan_ec_public_key_t *this, * botan requires a signature in IEEE 1363 format (r||s) * re-encode from ASN.1 sequence of two integers r,s */ - chunk_t parse, r, s; - parse = signature; + chunk_t parse = signature, r, s; - if (asn1_unwrap(&parse, &parse) != ASN1_SEQUENCE - || asn1_unwrap(&parse, &r) != ASN1_INTEGER - || asn1_unwrap(&parse, &s) != ASN1_INTEGER) + if (asn1_unwrap(&parse, &parse) != ASN1_SEQUENCE || + asn1_unwrap(&parse, &r) != ASN1_INTEGER || + asn1_unwrap(&parse, &s) != ASN1_INTEGER) { return FALSE; } @@ -101,34 +104,22 @@ static bool verify_signature(private_botan_ec_public_key_t *this, memcpy(sig.ptr + (keylen - r.len), r.ptr, r.len); memcpy(sig.ptr + keylen + (keylen - s.len), s.ptr, s.len); } - else + + if (botan_pk_op_verify_create(&verify_op, this->key, hash_and_padding, 0)) { - sig.ptr = signature.ptr; - sig.len = signature.len; + return FALSE; } + if (botan_pk_op_verify_update(verify_op, data.ptr, data.len)) { - botan_pk_op_verify_t verify_op; - bool valid = FALSE; - - if (botan_pk_op_verify_create(&verify_op, this->key, hash_and_padding, - 0)) - { - return FALSE; - } - - if (botan_pk_op_verify_update(verify_op, data.ptr, data.len)) - { - botan_pk_op_verify_destroy(verify_op); - return FALSE; - } - - valid = !(botan_pk_op_verify_finish(verify_op, sig.ptr, sig.len)); - botan_pk_op_verify_destroy(verify_op); - - return valid; + return FALSE; } + + valid = !(botan_pk_op_verify_finish(verify_op, sig.ptr, sig.len)); + + botan_pk_op_verify_destroy(verify_op); + return valid; } METHOD(public_key_t, get_type, key_type_t, @@ -141,19 +132,15 @@ METHOD(public_key_t, get_keysize, int, private_botan_ec_public_key_t *this) { botan_mp_t p; - if(botan_mp_init(&p)) - { - return 0; - } - - if(botan_pubkey_get_field(p, this->key, "p")) - { - botan_mp_destroy(p); - return 0; - } - size_t bits = 0; - if(botan_mp_num_bits(p, &bits)) + + if (botan_mp_init(&p)) + { + return 0; + } + + if (botan_pubkey_get_field(p, this->key, "p") || + botan_mp_num_bits(p, &bits)) { botan_mp_destroy(p); return 0; @@ -173,13 +160,13 @@ METHOD(public_key_t, verify, bool, switch (scheme) { + /* r||s -> Botan::IEEE_1363, data is the hash already */ case SIGN_ECDSA_WITH_NULL: - /* r||s -> Botan::IEEE_1363, data is the hash already */ hash_and_padding = "Raw"; sig_format = SIG_FORMAT_IEEE_1363; break; + /* DER SEQUENCE of two INTEGERS r,s -> Botan::DER_SEQUENCE */ case SIGN_ECDSA_WITH_SHA1_DER: - /* DER SEQUENCE of two INTEGERS r,s -> Botan::DER_SEQUENCE */ hash_and_padding = "EMSA1(SHA-1)"; sig_format = SIG_FORMAT_DER_SEQUENCE; break; @@ -195,18 +182,16 @@ METHOD(public_key_t, verify, bool, hash_and_padding = "EMSA1(SHA-512)"; sig_format = SIG_FORMAT_DER_SEQUENCE; break; + /* r||s -> Botan::IEEE_1363 */ case SIGN_ECDSA_256: - /* r||s -> Botan::IEEE_1363 */ hash_and_padding = "EMSA1(SHA-256)"; sig_format = SIG_FORMAT_IEEE_1363; break; case SIGN_ECDSA_384: - /* r||s -> Botan::IEEE_1363 */ hash_and_padding = "EMSA1(SHA-384)"; sig_format = SIG_FORMAT_IEEE_1363; break; case SIGN_ECDSA_521: - /* r||s -> Botan::IEEE_1363 */ hash_and_padding = "EMSA1(SHA-512)"; sig_format = SIG_FORMAT_IEEE_1363; break; @@ -228,119 +213,18 @@ METHOD(public_key_t, encrypt, bool, return FALSE; } -/** - * Calculate fingerprint from a botan_pubkey_t, also used in ec private key. - */ -bool botan_ec_fingerprint(botan_pubkey_t *ec, cred_encoding_type_t type, - chunk_t *fp) -{ - hasher_t *hasher; - chunk_t key; - - if (lib->encoding->get_cache(lib->encoding, type, ec, fp)) - { - return TRUE; - } - - switch (type) - { - case KEYID_PUBKEY_SHA1: - /* subjectPublicKey -> use botan_pubkey_fingerprint() */ - { - if (botan_pubkey_fingerprint(*ec, "SHA-1", NULL, &fp->len)) - { - return FALSE; - } - - *fp = chunk_alloc(fp->len); - - if (botan_pubkey_fingerprint(*ec, "SHA-1", fp->ptr, &fp->len)) - { - chunk_free(fp); - return FALSE; - } - - break; - } - case KEYID_PUBKEY_INFO_SHA1: - /* subjectPublicKeyInfo -> use botan_pubkey_export(), then hash */ - { - if (botan_pubkey_export(*ec, NULL, &key.len, - BOTAN_PRIVKEY_EXPORT_FLAG_DER)) - { - return FALSE; - } - - key = chunk_alloc(key.len); - - if (botan_pubkey_export(*ec, key.ptr, &key.len, - BOTAN_PRIVKEY_EXPORT_FLAG_DER)) - { - chunk_free(&key); - return FALSE; - } - - hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1); - if (!hasher || !hasher->allocate_hash(hasher, key, fp)) - { - DBG1(DBG_LIB, "SHA1 hash algorithm not supported," - " fingerprinting failed"); - DESTROY_IF(hasher); - chunk_free(&key); - return FALSE; - } - - hasher->destroy(hasher); - chunk_free(&key); - break; - } - default: - return FALSE; - } - - lib->encoding->cache(lib->encoding, type, ec, *fp); - return TRUE; -} - METHOD(public_key_t, get_fingerprint, bool, private_botan_ec_public_key_t *this, cred_encoding_type_t type, chunk_t *fingerprint) { - return botan_ec_fingerprint(&this->key, type, fingerprint); + return botan_get_fingerprint(this->key, this, type, fingerprint); } METHOD(public_key_t, get_encoding, bool, private_botan_ec_public_key_t *this, cred_encoding_type_t type, chunk_t *encoding) { - bool success = TRUE; - - if (botan_pubkey_export(this->key, NULL, &encoding->len, - BOTAN_PRIVKEY_EXPORT_FLAG_DER)) - { - return FALSE; - } - - *encoding = chunk_alloc(encoding->len); - - if (botan_pubkey_export(this->key, encoding->ptr, &encoding->len, - BOTAN_PRIVKEY_EXPORT_FLAG_DER)) - { - chunk_free(encoding); - return FALSE; - } - - if (type != PUBKEY_SPKI_ASN1_DER) - { - chunk_t asn1_encoding = *encoding; - - success = lib->encoding->encode(lib->encoding, type, NULL, encoding, - CRED_PART_ECDSA_PUB_ASN1_DER, - asn1_encoding, CRED_PART_END); - chunk_free(&asn1_encoding); - } - - return success; + return botan_get_encoding(this->key, type, encoding); } METHOD(public_key_t, get_ref, public_key_t*, @@ -355,18 +239,22 @@ METHOD(public_key_t, destroy, void, { if (ref_put(&this->ref)) { + lib->encoding->clear_cache(lib->encoding, this); botan_pubkey_destroy(this->key); free(this); } } -/** - * See header. +/* + * Described in header */ botan_ec_public_key_t *botan_ec_public_key_load(key_type_t type, va_list args) { private_botan_ec_public_key_t *this; chunk_t blob = chunk_empty; + botan_rng_t rng; + size_t namesize = 0; + char *namebuf; if (type != KEY_ECDSA) { @@ -408,49 +296,42 @@ botan_ec_public_key_t *botan_ec_public_key_load(key_type_t type, va_list args) if (botan_pubkey_load(&this->key, blob.ptr, blob.len)) { - destroy(this); + free(this); return NULL; } - size_t namesize = 0; - if (botan_pubkey_algo_name(this->key, NULL, &namesize) != - BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE) + if (botan_pubkey_algo_name(this->key, NULL, &namesize) + != BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE) { - botan_pubkey_destroy(this->key); destroy(this); return NULL; } - char* namebuf = malloc(namesize); + namebuf = malloc(namesize); if (botan_pubkey_algo_name(this->key, namebuf, &namesize)) { free(namebuf); - botan_pubkey_destroy(this->key); destroy(this); return NULL; } - const char* algo_name = "ECDSA"; - if (!strneq(namebuf, algo_name, sizeof(algo_name))) + if (!strneq(namebuf, "ECDSA", namesize)) { free(namebuf); - botan_pubkey_destroy(this->key); destroy(this); return NULL; } free(namebuf); - botan_rng_t rng; if (botan_rng_init(&rng, "user")) { - return FALSE; + return NULL; } if (botan_pubkey_check_key(this->key, rng, BOTAN_CHECK_KEY_EXPENSIVE_TESTS)) { DBG1(DBG_LIB, "public key failed key checks"); botan_rng_destroy(rng); - botan_pubkey_destroy(this->key); destroy(this); return NULL; } diff --git a/src/libstrongswan/plugins/botan/botan_gcm.c b/src/libstrongswan/plugins/botan/botan_gcm.c index 7fc59bdc1..7e0fc1468 100644 --- a/src/libstrongswan/plugins/botan/botan_gcm.c +++ b/src/libstrongswan/plugins/botan/botan_gcm.c @@ -82,8 +82,7 @@ static bool crypt(private_aead_t *this, chunk_t data, chunk_t assoc, chunk_t iv, { botan_cipher_t cipher; uint8_t nonce[NONCE_LEN]; - size_t output_written = 0; - size_t input_consumed = 0; + size_t output_written = 0, input_consumed = 0; memcpy(nonce, this->salt, SALT_LEN); memcpy(nonce + SALT_LEN, iv.ptr, IV_LEN); @@ -99,8 +98,8 @@ static bool crypt(private_aead_t *this, chunk_t data, chunk_t assoc, chunk_t iv, return FALSE; } - if (assoc.len - && botan_cipher_set_associated_data(cipher, assoc.ptr, assoc.len)) + if (assoc.len && + botan_cipher_set_associated_data(cipher, assoc.ptr, assoc.len)) { botan_cipher_destroy(cipher); return FALSE; @@ -115,8 +114,8 @@ static bool crypt(private_aead_t *this, chunk_t data, chunk_t assoc, chunk_t iv, if (init_flag == BOTAN_CIPHER_INIT_FLAG_ENCRYPT) { if (botan_cipher_update(cipher, BOTAN_CIPHER_UPDATE_FLAG_FINAL, - out, data.len + this->icv_size, &output_written, - data.ptr, data.len, &input_consumed)) + out, data.len + this->icv_size, &output_written, + data.ptr, data.len, &input_consumed)) { botan_cipher_destroy(cipher); return FALSE; @@ -125,8 +124,8 @@ static bool crypt(private_aead_t *this, chunk_t data, chunk_t assoc, chunk_t iv, else if (init_flag == BOTAN_CIPHER_INIT_FLAG_DECRYPT) { if (botan_cipher_update(cipher, BOTAN_CIPHER_UPDATE_FLAG_FINAL, - out, data.len, &output_written, - data.ptr, data.len + this->icv_size, &input_consumed)) + out, data.len, &output_written, data.ptr, + data.len + this->icv_size, &input_consumed)) { botan_cipher_destroy(cipher); return FALSE; @@ -225,8 +224,11 @@ METHOD(aead_t, destroy, void, free(this); } -aead_t *botan_gcm_create(encryption_algorithm_t algo, - size_t key_size, size_t salt_size) +/* + * Described in header + */ +aead_t *botan_gcm_create(encryption_algorithm_t algo, size_t key_size, + size_t salt_size) { private_aead_t *this; @@ -244,22 +246,6 @@ aead_t *botan_gcm_create(encryption_algorithm_t algo, }, ); - switch (algo) - { - case ENCR_AES_GCM_ICV8: - this->icv_size = 8; - break; - case ENCR_AES_GCM_ICV12: - this->icv_size = 12; - break; - case ENCR_AES_GCM_ICV16: - this->icv_size = 16; - break; - default: - free(this); - return NULL; - } - if (salt_size && salt_size != SALT_LEN) { /* currently not supported */ @@ -274,6 +260,7 @@ aead_t *botan_gcm_create(encryption_algorithm_t algo, { case 0: key_size = 16; + /* FALL */ case 16: this->cipher_name = "AES-128/GCM(8)"; break; @@ -287,6 +274,7 @@ aead_t *botan_gcm_create(encryption_algorithm_t algo, free(this); return NULL; } + this->icv_size = 8; break; case ENCR_AES_GCM_ICV12: switch (key_size) @@ -307,6 +295,7 @@ aead_t *botan_gcm_create(encryption_algorithm_t algo, free(this); return NULL; } + this->icv_size = 12; break; case ENCR_AES_GCM_ICV16: switch (key_size) @@ -327,18 +316,13 @@ aead_t *botan_gcm_create(encryption_algorithm_t algo, free(this); return NULL; } + this->icv_size = 16; break; default: free(this); return NULL; } - if (!this->cipher_name) - { - free(this); - return NULL; - } - this->key = chunk_alloc(key_size); this->iv_gen = iv_gen_seq_create(); @@ -346,4 +330,4 @@ aead_t *botan_gcm_create(encryption_algorithm_t algo, } #endif -#endif \ No newline at end of file +#endif diff --git a/src/libstrongswan/plugins/botan/botan_gcm.h b/src/libstrongswan/plugins/botan/botan_gcm.h index 600e60b0e..b2053cb4d 100644 --- a/src/libstrongswan/plugins/botan/botan_gcm.h +++ b/src/libstrongswan/plugins/botan/botan_gcm.h @@ -31,23 +31,17 @@ #ifndef BOTAN_GCM_H_ #define BOTAN_GCM_H_ -#include - -#ifdef BOTAN_HAS_AEAD_GCM - #include /** * Constructor to create aead_t implementation. * - * @param algo algorithm to implement - * @param key_size key size in bytes - * @param salt_size size of implicit salt length - * @return aead_t object, NULL if not supported + * @param algo algorithm to implement + * @param key_size key size in bytes + * @param salt_size size of implicit salt length + * @return aead_t object, NULL if not supported */ aead_t *botan_gcm_create(encryption_algorithm_t algo, size_t key_size, size_t salt_size); -#endif - #endif /** BOTAN_GCM_H_ @}*/ diff --git a/src/libstrongswan/plugins/botan/botan_hasher.c b/src/libstrongswan/plugins/botan/botan_hasher.c index 06b2ad9b2..3a8fcd802 100644 --- a/src/libstrongswan/plugins/botan/botan_hasher.c +++ b/src/libstrongswan/plugins/botan/botan_hasher.c @@ -22,6 +22,7 @@ */ #include "botan_hasher.h" +#include "botan_util.h" #include @@ -49,6 +50,7 @@ METHOD(hasher_t, get_hash_size, size_t, private_botan_hasher_t *this) { size_t len = 0; + if (botan_hash_output_length(this->hash, &len)) { return 0; @@ -74,12 +76,9 @@ METHOD(hasher_t, get_hash, bool, return FALSE; } - if (hash) + if (hash && botan_hash_final(this->hash, hash)) { - if (botan_hash_final(this->hash, hash)) - { - return FALSE; - } + return FALSE; } return TRUE; } @@ -110,28 +109,10 @@ botan_hasher_t *botan_hasher_create(hash_algorithm_t algo) private_botan_hasher_t *this; const char* hash_name; - switch (algo) + hash_name = botan_get_hash(algo); + if (!hash_name) { - case HASH_MD5: - hash_name = "MD5"; - break; - case HASH_SHA1: - hash_name = "SHA-1"; - break; - case HASH_SHA224: - hash_name = "SHA-224"; - break; - case HASH_SHA256: - hash_name = "SHA-256"; - break; - case HASH_SHA384: - hash_name = "SHA-384"; - break; - case HASH_SHA512: - hash_name = "SHA-512"; - break; - default: - return NULL; + return FALSE; } INIT(this, @@ -150,6 +131,5 @@ botan_hasher_t *botan_hasher_create(hash_algorithm_t algo) { return NULL; } - return &this->public; } diff --git a/src/libstrongswan/plugins/botan/botan_hmac.c b/src/libstrongswan/plugins/botan/botan_hmac.c index a1e3514d8..367d27f24 100644 --- a/src/libstrongswan/plugins/botan/botan_hmac.c +++ b/src/libstrongswan/plugins/botan/botan_hmac.c @@ -69,16 +69,10 @@ METHOD(mac_t, get_mac, bool, return FALSE; } - if ( out == NULL) - { - return TRUE; - } - - if (botan_mac_final(this->hmac, out)) + if (out && botan_mac_final(this->hmac, out)) { return FALSE; } - return TRUE; } @@ -86,6 +80,7 @@ METHOD(mac_t, get_mac_size, size_t, private_botan_mac_t *this) { size_t len = 0; + if (botan_mac_output_length(this->hmac, &len)) { return 0; @@ -140,7 +135,6 @@ static mac_t *hmac_create(hash_algorithm_t algo) free(this); return NULL; } - return &this->public; } diff --git a/src/libstrongswan/plugins/botan/botan_plugin.c b/src/libstrongswan/plugins/botan/botan_plugin.c index 0fb9159c1..bce6b8fee 100644 --- a/src/libstrongswan/plugins/botan/botan_plugin.c +++ b/src/libstrongswan/plugins/botan/botan_plugin.c @@ -78,7 +78,6 @@ METHOD(plugin_t, get_features, int, PLUGIN_PROVIDE(DH, MODP_1024_BIT), PLUGIN_PROVIDE(DH, MODP_1024_160), PLUGIN_PROVIDE(DH, MODP_768_BIT), - PLUGIN_REGISTER(DH, botan_diffie_hellman_create_custom), PLUGIN_PROVIDE(DH, MODP_CUSTOM), #endif /* crypters */ @@ -146,6 +145,7 @@ METHOD(plugin_t, get_features, int, PLUGIN_PROVIDE(SIGNER, AUTH_HMAC_SHA2_512_256), PLUGIN_PROVIDE(SIGNER, AUTH_HMAC_SHA2_512_512), #endif +#endif /* BOTAN_HAS_HMAC */ #ifdef BOTAN_HAS_ECDH /* EC DH groups */ @@ -156,7 +156,6 @@ METHOD(plugin_t, get_features, int, PLUGIN_PROVIDE(DH, ECP_256_BP), PLUGIN_PROVIDE(DH, ECP_384_BP), PLUGIN_PROVIDE(DH, ECP_512_BP), -#endif #endif /* RSA */ #ifdef BOTAN_HAS_RSA @@ -265,7 +264,7 @@ METHOD(plugin_t, destroy, void, } /* - * see header file + * Described in header */ plugin_t *botan_plugin_create() { diff --git a/src/libstrongswan/plugins/botan/botan_rng.c b/src/libstrongswan/plugins/botan/botan_rng.c index f398f9fb7..c49225c3c 100644 --- a/src/libstrongswan/plugins/botan/botan_rng.c +++ b/src/libstrongswan/plugins/botan/botan_rng.c @@ -47,28 +47,15 @@ struct private_botan_random_t { rng_quality_t quality; /** - * RNG type + * RNG instance */ - const char* rng_name; + botan_rng_t rng; }; METHOD(rng_t, get_bytes, bool, private_botan_random_t *this, size_t bytes, uint8_t *buffer) { - botan_rng_t rng; - if (botan_rng_init(&rng, this->rng_name)) - { - return FALSE; - } - - if (botan_rng_get(rng, buffer, bytes)) - { - botan_rng_destroy(rng); - return FALSE; - } - - botan_rng_destroy(rng); - return TRUE; + return botan_rng_get(this->rng, buffer, bytes) == 0; } METHOD(rng_t, allocate_bytes, bool, @@ -86,11 +73,12 @@ METHOD(rng_t, allocate_bytes, bool, METHOD(rng_t, destroy, void, private_botan_random_t *this) { + botan_rng_destroy(this->rng); free(this); } /* - * Described in header. + * Described in header */ botan_random_t *botan_rng_create(rng_quality_t quality) { @@ -101,7 +89,17 @@ botan_random_t *botan_rng_create(rng_quality_t quality) { case RNG_WEAK: case RNG_STRONG: + /* some rng_t instances of this class (e.g. in the ike-sa-manager) + * may be called concurrently by different threads. the Botan RNGs + * are not reentrant, by default, so use the threadsafe version. + * because we build without threading support when running tests + * with leak-detective (lots of reports of frees of unknown memory) + * there is a fallback to the default */ +#ifdef BOTAN_TARGET_OS_HAS_THREADS + rng_name = "user-threadsafe"; +#else rng_name = "user"; +#endif break; case RNG_TRUE: rng_name = "system"; @@ -119,9 +117,13 @@ botan_random_t *botan_rng_create(rng_quality_t quality) }, }, .quality = quality, - .rng_name = rng_name, ); + if (botan_rng_init(&this->rng, rng_name)) + { + free(this); + return NULL; + } return &this->public; } diff --git a/src/libstrongswan/plugins/botan/botan_rsa_private_key.c b/src/libstrongswan/plugins/botan/botan_rsa_private_key.c index c35e0c2b6..29f692333 100644 --- a/src/libstrongswan/plugins/botan/botan_rsa_private_key.c +++ b/src/libstrongswan/plugins/botan/botan_rsa_private_key.c @@ -39,6 +39,7 @@ typedef struct private_botan_rsa_private_key_t private_botan_rsa_private_key_t; * Private data of a botan_rsa_private_key_t object. */ struct private_botan_rsa_private_key_t { + /** * Public interface for this signer. */ @@ -49,7 +50,6 @@ struct private_botan_rsa_private_key_t { */ botan_privkey_t key; - /** * reference count */ @@ -59,32 +59,23 @@ struct private_botan_rsa_private_key_t { /** * Get the binary representation of a named RSA parameter */ -static int botan_rsa_get_field(botan_privkey_t *key, const char *field_name, - chunk_t *value) +static bool get_rsa_field(botan_privkey_t *key, const char *field_name, + chunk_t *value) { botan_mp_t field; + size_t field_size = 0; + if (botan_mp_init(&field)) { - return -1; + return FALSE; } - if (botan_privkey_get_field(field, *key, field_name)) + if (botan_privkey_get_field(field, *key, field_name) || + botan_mp_num_bytes(field, &field_size) || + !field_size) { botan_mp_destroy(field); - return -1; - } - - size_t field_size = 0; - if (botan_mp_num_bytes(field, &field_size)) - { - botan_mp_destroy(field); - return -1; - } - - if (field_size == 0) - { - botan_mp_destroy(field); - return -1; + return FALSE; } *value = chunk_alloc(field_size); @@ -92,105 +83,9 @@ static int botan_rsa_get_field(botan_privkey_t *key, const char *field_name, { botan_mp_destroy(field); chunk_clear(value); - return -1; + return FALSE; } - botan_mp_destroy(field); - return 0; -} - -/** - * Build RSA signature - */ -static bool build_rsa_signature(private_botan_rsa_private_key_t *this, - const char* hash_and_padding, chunk_t data, chunk_t* signature) -{ - botan_pk_op_sign_t sign_op; - - if (botan_pk_op_sign_create(&sign_op, this->key, hash_and_padding, 0)) - { - return FALSE; - } - - botan_rng_t rng; - if (botan_rng_init(&rng, "user")) - { - botan_pk_op_sign_destroy(sign_op); - return FALSE; - } - - /* get size of signature first */ - if (botan_pk_op_sign_update(sign_op, data.ptr, data.len)) - { - botan_rng_destroy(rng); - botan_pk_op_sign_destroy(sign_op); - return FALSE; - } - - signature->len = 0; - if (botan_pk_op_sign_finish(sign_op, rng, NULL, &signature->len) - != BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE) - { - botan_rng_destroy(rng); - botan_pk_op_sign_destroy(sign_op); - return FALSE; - } - - /* now get the signature */ - *signature = chunk_alloc(signature->len); - if (botan_pk_op_sign_update(sign_op, data.ptr, data.len)) - { - chunk_free(signature); - botan_rng_destroy(rng); - botan_pk_op_sign_destroy(sign_op); - return FALSE; - } - - if (botan_pk_op_sign_finish(sign_op, rng, signature->ptr, &signature->len)) - { - chunk_free(signature); - botan_rng_destroy(rng); - botan_pk_op_sign_destroy(sign_op); - return FALSE; - } - - botan_rng_destroy(rng); - botan_pk_op_sign_destroy(sign_op); - return TRUE; -} - -/** - * Build an EMSA PKCS1 signature described in PKCS#1 - */ -static bool build_emsa_pkcs1_signature(private_botan_rsa_private_key_t *this, - const char* hash_and_padding, chunk_t data, chunk_t* signature) -{ - return build_rsa_signature(this, hash_and_padding, data, signature); -} - -static bool botan_get_hash(hash_algorithm_t hash, char* hash_str) -{ - switch (hash) - { - case HASH_SHA1: - sprintf(hash_str, "SHA-1"); - break; - case HASH_SHA224: - sprintf(hash_str, "SHA-224"); - break; - case HASH_SHA256: - sprintf(hash_str, "SHA-256"); - break; - case HASH_SHA384: - sprintf(hash_str, "SHA-384"); - break; - case HASH_SHA512: - sprintf(hash_str, "SHA-512"); - break; - default: - return FALSE; - } - return TRUE; } @@ -201,10 +96,8 @@ static bool build_emsa_pss_signature(private_botan_rsa_private_key_t *this, rsa_pss_params_t *params, chunk_t data, chunk_t *sig) { - char* hash_and_padding, *hash, *mgf1_hash; - char* salt_len = NULL; - size_t len; - bool success = FALSE; + const char *hash; + char hash_and_padding[BUF_LEN]; if (!params) { @@ -218,50 +111,23 @@ static bool build_emsa_pss_signature(private_botan_rsa_private_key_t *this, return FALSE; } - hash = malloc(8); - if (!botan_get_hash(params->hash, hash)) + hash = botan_get_hash(params->hash); + if (!hash) { - free(hash); - return FALSE; - } - - mgf1_hash = malloc(8); - if (!botan_get_hash(params->mgf1_hash, mgf1_hash)) - { - free(hash); - free(mgf1_hash); return FALSE; } if (params->salt_len > RSA_PSS_SALT_LEN_DEFAULT) { - salt_len = malloc(6); - snprintf(salt_len, 5, "%d", params->salt_len); - } - - len = 24 + strlen(hash) + strlen(mgf1_hash); - hash_and_padding = malloc(len+1); - - if (salt_len) - { - snprintf(hash_and_padding, len, "EMSA-PSS(%s,MGF1,%s)", hash, salt_len); + snprintf(hash_and_padding, sizeof(hash_and_padding), + "EMSA-PSS(%s,MGF1,%u)", hash, params->salt_len); } else { - snprintf(hash_and_padding, len, "EMSA-PSS(%s,MGF1)", hash); + snprintf(hash_and_padding, sizeof(hash_and_padding), + "EMSA-PSS(%s,MGF1)", hash); } - - if (build_rsa_signature(this, hash_and_padding, data, sig)) - { - success = TRUE; - } - - if (salt_len) - free(salt_len); - free(hash); - free(mgf1_hash); - free(hash_and_padding); - return success; + return botan_get_signature(this->key, hash_and_padding, data, sig); } METHOD(private_key_t, get_type, key_type_t, @@ -277,23 +143,23 @@ METHOD(private_key_t, sign, bool, switch (scheme) { case SIGN_RSA_EMSA_PKCS1_NULL: - return build_emsa_pkcs1_signature(this, "EMSA_PKCS1(Raw)", data, - signature); + return botan_get_signature(this->key, "EMSA_PKCS1(Raw)", data, + signature); case SIGN_RSA_EMSA_PKCS1_SHA1: - return build_emsa_pkcs1_signature(this, "EMSA_PKCS1(SHA-1)", data, - signature); + return botan_get_signature(this->key, "EMSA_PKCS1(SHA-1)", data, + signature); case SIGN_RSA_EMSA_PKCS1_SHA2_224: - return build_emsa_pkcs1_signature(this, "EMSA_PKCS1(SHA-224)", data, - signature); + return botan_get_signature(this->key, "EMSA_PKCS1(SHA-224)", data, + signature); case SIGN_RSA_EMSA_PKCS1_SHA2_256: - return build_emsa_pkcs1_signature(this, "EMSA_PKCS1(SHA-256)", data, - signature); + return botan_get_signature(this->key, "EMSA_PKCS1(SHA-256)", data, + signature); case SIGN_RSA_EMSA_PKCS1_SHA2_384: - return build_emsa_pkcs1_signature(this, "EMSA_PKCS1(SHA-384)", data, - signature); + return botan_get_signature(this->key, "EMSA_PKCS1(SHA-384)", data, + signature); case SIGN_RSA_EMSA_PKCS1_SHA2_512: - return build_emsa_pkcs1_signature(this, "EMSA_PKCS1(SHA-512)", data, - signature); + return botan_get_signature(this->key, "EMSA_PKCS1(SHA-512)", data, + signature); case SIGN_RSA_EMSA_PSS: return build_emsa_pss_signature(this, params, data, signature); default: @@ -303,9 +169,11 @@ METHOD(private_key_t, sign, bool, } } -METHOD(private_key_t, decrypt, bool, private_botan_rsa_private_key_t *this, - encryption_scheme_t scheme, chunk_t crypto, chunk_t *plain) +METHOD(private_key_t, decrypt, bool, + private_botan_rsa_private_key_t *this, encryption_scheme_t scheme, + chunk_t crypto, chunk_t *plain) { + botan_pk_op_decrypt_t decrypt_op; const char *padding; switch (scheme) @@ -334,26 +202,18 @@ METHOD(private_key_t, decrypt, bool, private_botan_rsa_private_key_t *this, return FALSE; } - botan_pk_op_decrypt_t decrypt_op; if (botan_pk_op_decrypt_create(&decrypt_op, this->key, padding, 0)) { return FALSE; } - /* - * get size of plaintext first - */ - if (botan_pk_op_decrypt(decrypt_op, NULL, &plain->len, crypto.ptr, - crypto.len) - != BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE) + plain->len = 0; + if (botan_pk_op_decrypt_output_length(decrypt_op, crypto.len, &plain->len)) { botan_pk_op_decrypt_destroy(decrypt_op); return FALSE; } - /* - * now get the plaintext - */ *plain = chunk_alloc(plain->len); if (botan_pk_op_decrypt(decrypt_op, plain->ptr, &plain->len, crypto.ptr, crypto.len)) @@ -362,7 +222,6 @@ METHOD(private_key_t, decrypt, bool, private_botan_rsa_private_key_t *this, botan_pk_op_decrypt_destroy(decrypt_op); return FALSE; } - botan_pk_op_decrypt_destroy(decrypt_op); return TRUE; } @@ -371,21 +230,18 @@ METHOD(private_key_t, get_keysize, int, private_botan_rsa_private_key_t *this) { botan_mp_t n; + size_t bits = 0; + if (botan_mp_init(&n)) { - return -1; + return 0; } - if (botan_privkey_rsa_get_n(n, this->key)) - { - return -1; - } - - size_t bits = 0; - if (botan_mp_num_bits(n, &bits)) + if (botan_privkey_rsa_get_n(n, this->key) || + botan_mp_num_bits(n, &bits)) { botan_mp_destroy(n); - return -1; + return 0; } botan_mp_destroy(n); @@ -395,22 +251,23 @@ METHOD(private_key_t, get_keysize, int, METHOD(private_key_t, get_public_key, public_key_t*, private_botan_rsa_private_key_t *this) { + public_key_t *pub_key; chunk_t n, e; - if (botan_rsa_get_field(&this->key, "n", &n)) + if (!get_rsa_field(&this->key, "n", &n)) { return NULL; } - if (botan_rsa_get_field(&this->key, "e", &e)) + if (!get_rsa_field(&this->key, "e", &e)) { - chunk_clear(&n); + chunk_free(&n); return NULL; } - public_key_t *pub_key = lib->creds->create(lib->creds, CRED_PUBLIC_KEY, - KEY_RSA, BUILD_RSA_MODULUS, n, - BUILD_RSA_PUB_EXP, e, BUILD_END); + pub_key = lib->creds->create(lib->creds, CRED_PUBLIC_KEY, KEY_RSA, + BUILD_RSA_MODULUS, n, BUILD_RSA_PUB_EXP, e, + BUILD_END); chunk_free(&n); chunk_free(&e); @@ -421,32 +278,22 @@ METHOD(private_key_t, get_fingerprint, bool, private_botan_rsa_private_key_t *this, cred_encoding_type_t type, chunk_t *fingerprint) { - chunk_t n, e; - bool success; + botan_pubkey_t pubkey; + bool success = FALSE; - if (lib->encoding->get_cache(lib->encoding, type, &this->key, fingerprint)) + /* check the cache before doing the export */ + if (lib->encoding->get_cache(lib->encoding, type, this, fingerprint)) { return TRUE; } - if (botan_rsa_get_field(&this->key, "n", &n)) + if (botan_privkey_export_pubkey(&pubkey, this->key)) { return FALSE; } - - if (botan_rsa_get_field(&this->key, "e", &e)) - { - chunk_clear(&n); - return FALSE; - } - - success = lib->encoding->encode(lib->encoding, type, &this->key, - fingerprint, CRED_PART_RSA_MODULUS, n, - CRED_PART_RSA_PUB_EXP, e, CRED_PART_END); - chunk_free(&n); - chunk_free(&e); + success = botan_get_fingerprint(pubkey, this, type, fingerprint); + botan_pubkey_destroy(pubkey); return success; - } METHOD(private_key_t, get_encoding, bool, @@ -458,27 +305,28 @@ METHOD(private_key_t, get_encoding, bool, case PRIVKEY_ASN1_DER: case PRIVKEY_PEM: { + uint32_t format = BOTAN_PRIVKEY_EXPORT_FLAG_DER; + size_t len = 0; bool success = TRUE; - uint32_t format = BOTAN_PRIVKEY_EXPORT_FLAG_DER; if (type == PRIVKEY_PEM) { format = BOTAN_PRIVKEY_EXPORT_FLAG_PEM; } - size_t bits = 0; - if(botan_privkey_rsa_get_privkey(this->key, NULL, &bits, format)) + if (botan_privkey_rsa_get_privkey(this->key, NULL, &len, format) + != BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE) { return FALSE; } - *encoding = chunk_alloc(bits); - if(botan_privkey_rsa_get_privkey(this->key, encoding->ptr, &bits, format)) + *encoding = chunk_alloc(len); + if (botan_privkey_rsa_get_privkey(this->key, encoding->ptr, &len, + format)) { chunk_clear(encoding); return FALSE; } - return success; } default: @@ -498,11 +346,8 @@ METHOD(private_key_t, destroy, void, { if (ref_put(&this->ref)) { - if (&this->key) - { - lib->encoding->clear_cache(lib->encoding, &this->key); - botan_privkey_destroy(this->key); - } + lib->encoding->clear_cache(lib->encoding, this); + botan_privkey_destroy(this->key); free(this); } } @@ -538,13 +383,13 @@ static private_botan_rsa_private_key_t *create_empty() } /* - * See header. + * Described in header */ botan_rsa_private_key_t *botan_rsa_private_key_gen(key_type_t type, va_list args) { private_botan_rsa_private_key_t *this; - + botan_rng_t rng; u_int key_size = 0; while (TRUE) @@ -567,21 +412,19 @@ botan_rsa_private_key_t *botan_rsa_private_key_gen(key_type_t type, return NULL; } - botan_rng_t rng; - if (botan_rng_init(&rng, "user")) + if (botan_rng_init(&rng, "system")) { return NULL; } this = create_empty(); - if(botan_privkey_create_rsa(&this->key, rng, key_size)) + if (botan_privkey_create_rsa(&this->key, rng, key_size)) { botan_rng_destroy(rng); - destroy(this); + free(this); return NULL; } - botan_rng_destroy(rng); return &this->public; } @@ -593,52 +436,35 @@ botan_rsa_private_key_t *botan_rsa_private_key_gen(key_type_t type, static bool calculate_pq(botan_mp_t *n, botan_mp_t *e, botan_mp_t *d, botan_mp_t *p, botan_mp_t *q) { - botan_mp_t k, one, r, zero, two, n1, x, y, g, rem; + botan_mp_t k = NULL, one = NULL, r = NULL, zero = NULL, two = NULL; + botan_mp_t n1 = NULL, x = NULL, y = NULL, g = NULL, rem = NULL; + botan_rng_t rng = NULL; int i, t, j; - bool success = TRUE; + bool success = FALSE; - if (botan_mp_init(&k)) + if (botan_mp_init(&k) || + botan_mp_init(&one) || + botan_mp_set_from_int(one, 1)) { - success = FALSE; goto error; } - if (botan_mp_init(&one)) - { - success = FALSE; - goto error; - } - - if (botan_mp_set_from_int(one, 1)) - { - success = FALSE; - goto error; - } - - /* 1. k = de - 1 */ + /* 1. k = d * e - 1 */ if (botan_mp_mul(k, *d, *e) || botan_mp_sub(k, k, one)) { - success = FALSE; goto error; } /* k must be even */ if (!botan_mp_is_even(k)) { - success = FALSE; goto error; } /* 2. k = 2^t * r, where r is the largest odd integer dividing k, and t >= 1 */ - if (botan_mp_init(&r)) + if (botan_mp_init(&r) || + botan_mp_set_from_mp(r, k)) { - success = FALSE; - goto error; - } - - if (botan_mp_set_from_mp(r, k)) - { - success = FALSE; goto error; } @@ -646,58 +472,41 @@ static bool calculate_pq(botan_mp_t *n, botan_mp_t *e, botan_mp_t *d, { if (botan_mp_rshift(r, r, 1)) { - success = FALSE; goto error; } } - /* need 0, 2, n-1 below */ - if (botan_mp_init(&zero)) + /* need 0 and n-1 below */ + if (botan_mp_init(&zero) || + botan_mp_init(&n1) || + botan_mp_sub(n1, *n, one)) { - success = FALSE; - goto error; - } - - if (botan_mp_set_from_int(zero, 0)) - { - success = FALSE; - goto error; - } - - if (botan_mp_init(&n1)) - { - success = FALSE; - goto error; - } - - if (botan_mp_sub(n1, *n, one)) - { - success = FALSE; goto error; } if (botan_mp_init(&g)) { - success = FALSE; goto error; } - botan_rng_t rng; if (botan_rng_init(&rng, "user")) { - success = FALSE; goto error; } if (botan_mp_init(&two)) { - success = FALSE; goto error; } if (botan_mp_set_from_int(two, 2)) { - success = FALSE; + goto error; + } + + if (botan_mp_init(&y) || + botan_mp_init(&x)) + { goto error; } @@ -706,20 +515,11 @@ static bool calculate_pq(botan_mp_t *n, botan_mp_t *e, botan_mp_t *d, /* 3a. generate a random integer g in the range [0, n-1] */ if (botan_mp_rand_range(g, rng, zero, n1)) { - success = FALSE; goto error; } - /* 3b. y = g^r mod n */ - if (botan_mp_init(&y)) - { - success = FALSE; - goto error; - } - if (botan_mp_powmod(y, g, r, *n)) { - success = FALSE; goto error; } @@ -729,18 +529,11 @@ static bool calculate_pq(botan_mp_t *n, botan_mp_t *e, botan_mp_t *d, continue; } - if (botan_mp_init(&x)) - { - success = FALSE; - goto error; - } - for (j = 0; j < t; j++) { /* x = y^2 mod n */ if (botan_mp_powmod(x, y, two, *n)) { - success = FALSE; goto error; } @@ -757,9 +550,8 @@ static bool calculate_pq(botan_mp_t *n, botan_mp_t *e, botan_mp_t *d, } /* let y = x */ - if(botan_mp_set_from_mp(y, x)) + if (botan_mp_set_from_mp(y, x)) { - success = FALSE; goto error; } } @@ -769,53 +561,36 @@ done: /* 5. p = GCD(y – 1, n) and q = n/p */ if (botan_mp_sub(y, y, one)) { - success = FALSE; goto error; } - if (botan_mp_init(p)) + if (botan_mp_init(p) || + botan_mp_gcd(*p, y, *n)) { - success = FALSE; goto error; } - if (botan_mp_gcd(*p, y, *n)) + if (botan_mp_init(q) || + botan_mp_init(&rem) || + botan_mp_div(*q, rem, *n, *p)) { - success = FALSE; - goto error; - } - - if (botan_mp_init(q)) - { - success = FALSE; - goto error; - } - - if (botan_mp_init(&rem)) - { - success = FALSE; - goto error; - } - - if (botan_mp_div(*q, rem, *n, *p)) - { - success = FALSE; goto error; } if (!botan_mp_is_zero(rem)) { - success = FALSE; goto error; } + success = TRUE; + error: if (!success) { botan_mp_destroy(*p); botan_mp_destroy(*q); } - + botan_rng_destroy(rng); botan_mp_destroy(k); botan_mp_destroy(one); botan_mp_destroy(r); @@ -824,12 +599,13 @@ error: botan_mp_destroy(n1); botan_mp_destroy(x); botan_mp_destroy(y); + botan_mp_destroy(g); botan_mp_destroy(rem); return success; } /* - * See header + * Described in header */ botan_rsa_private_key_t *botan_rsa_private_key_load(key_type_t type, va_list args) @@ -880,38 +656,37 @@ botan_rsa_private_key_t *botan_rsa_private_key_load(key_type_t type, if (botan_privkey_load_rsa_pkcs1(&this->key, blob.ptr, blob.len)) { - destroy(this); + free(this); return NULL; } - return &this->public; } if (n.ptr && e.ptr && d.ptr) { - botan_mp_t n_mp, e_mp, d_mp; - if (chunk_to_botan_mp(n, &n_mp)) + botan_mp_t n_mp, e_mp, d_mp, p_mp, q_mp; + + if (!chunk_to_botan_mp(n, &n_mp)) { return NULL; } - if (chunk_to_botan_mp(e, &e_mp)) + if (!chunk_to_botan_mp(e, &e_mp)) { botan_mp_destroy(n_mp); return NULL; } - if (chunk_to_botan_mp(d, &d_mp)) + if (!chunk_to_botan_mp(d, &d_mp)) { botan_mp_destroy(n_mp); botan_mp_destroy(e_mp); return NULL; } - botan_mp_t p_mp, q_mp; if (p.ptr && q.ptr) { - if (chunk_to_botan_mp(p, &p_mp)) + if (!chunk_to_botan_mp(p, &p_mp)) { botan_mp_destroy(n_mp); botan_mp_destroy(e_mp); @@ -919,7 +694,7 @@ botan_rsa_private_key_t *botan_rsa_private_key_load(key_type_t type, return NULL; } - if (chunk_to_botan_mp(q, &q_mp)) + if (!chunk_to_botan_mp(q, &q_mp)) { botan_mp_destroy(n_mp); botan_mp_destroy(e_mp); @@ -930,7 +705,7 @@ botan_rsa_private_key_t *botan_rsa_private_key_load(key_type_t type, } else { - // calculate p,q from n, e, d + /* calculate p,q from n, e, d */ if (!calculate_pq(&n_mp, &e_mp, &d_mp, &p_mp, &q_mp)) { botan_mp_destroy(n_mp); @@ -939,6 +714,8 @@ botan_rsa_private_key_t *botan_rsa_private_key_load(key_type_t type, return NULL; } } + botan_mp_destroy(n_mp); + botan_mp_destroy(d_mp); this = create_empty(); @@ -947,7 +724,7 @@ botan_rsa_private_key_t *botan_rsa_private_key_load(key_type_t type, botan_mp_destroy(e_mp); botan_mp_destroy(p_mp); botan_mp_destroy(q_mp); - destroy(this); + free(this); return NULL; } diff --git a/src/libstrongswan/plugins/botan/botan_rsa_public_key.c b/src/libstrongswan/plugins/botan/botan_rsa_public_key.c index b2445b5f9..617fad5d1 100644 --- a/src/libstrongswan/plugins/botan/botan_rsa_public_key.c +++ b/src/libstrongswan/plugins/botan/botan_rsa_public_key.c @@ -43,6 +43,7 @@ typedef struct private_botan_rsa_public_key_t private_botan_rsa_public_key_t; * Private data structure with signing context. */ struct private_botan_rsa_public_key_t { + /** * Public interface for this signer */ @@ -59,54 +60,12 @@ struct private_botan_rsa_public_key_t { refcount_t ref; }; -/** - * Get the binary representation of a named RSA parameter - */ -static int botan_rsa_get_field(botan_pubkey_t *key, const char *field_name, - chunk_t *value) -{ - botan_mp_t field; - size_t field_size = 0; - - if (botan_mp_init(&field)) - { - return -1; - } - - if (botan_pubkey_get_field(field, *key, field_name)) - { - return -1; - } - - if (botan_mp_num_bytes(field, &field_size)) - { - botan_mp_destroy(field); - return -1; - } - - if (field_size == 0) - { - botan_mp_destroy(field); - return -1; - } - - *value = chunk_empty; - *value = chunk_alloc(field_size); - if (botan_mp_to_bin(field, value->ptr)) - { - botan_mp_destroy(field); - chunk_clear(value); - return -1; - } - - return 0; -} - /** * Verify RSA signature */ static bool verify_rsa_signature(private_botan_rsa_public_key_t *this, - const char* hash_and_padding, chunk_t data, chunk_t signature) + const char* hash_and_padding, chunk_t data, + chunk_t signature) { botan_pk_op_verify_t verify_op; bool valid = FALSE; @@ -122,48 +81,12 @@ static bool verify_rsa_signature(private_botan_rsa_public_key_t *this, return FALSE; } - valid = - !(botan_pk_op_verify_finish(verify_op, signature.ptr, signature.len)); + valid = !botan_pk_op_verify_finish(verify_op, signature.ptr, signature.len); botan_pk_op_verify_destroy(verify_op); return valid; } -/** - * Verification of an EMSA PKCS1 signature described in PKCS#1 - */ -static bool verify_emsa_pkcs1_signature(private_botan_rsa_public_key_t *this, - const char* hash_and_padding, chunk_t data, chunk_t signature) -{ - return verify_rsa_signature(this, hash_and_padding, data, signature); -} - -static bool botan_get_hash(hash_algorithm_t hash, char* hash_str) -{ - switch (hash) - { - case HASH_SHA1: - sprintf(hash_str, "SHA-1"); - break; - case HASH_SHA224: - sprintf(hash_str, "SHA-224"); - break; - case HASH_SHA256: - sprintf(hash_str, "SHA-256"); - break; - case HASH_SHA384: - sprintf(hash_str, "SHA-384"); - break; - case HASH_SHA512: - sprintf(hash_str, "SHA-512"); - break; - default: - return FALSE; - } - - return TRUE; -} - /** * Verification of an EMSA PSS signature described in PKCS#1 */ @@ -171,67 +94,38 @@ static bool verify_emsa_pss_signature(private_botan_rsa_public_key_t *this, rsa_pss_params_t *params, chunk_t data, chunk_t signature) { - char* hash_and_padding, *hash, *mgf1_hash; - char* salt_len = NULL; - size_t len; - bool success = FALSE; + const char *hash; + char hash_and_padding[BUF_LEN]; if (!params) { return FALSE; } - // botan currently does not support passing the mgf1 hash + /* botan currently does not support passing the mgf1 hash */ if (params->hash != params->mgf1_hash) { DBG1(DBG_LIB, "passing mgf1 hash not supported via botan"); return FALSE; } - hash = malloc(8); - if(!botan_get_hash(params->hash, hash)) + hash = botan_get_hash(params->hash); + if (!hash) { - free(hash); return FALSE; } - mgf1_hash = malloc(8); - if(!botan_get_hash(params->mgf1_hash, mgf1_hash)) + if (params->salt_len > RSA_PSS_SALT_LEN_DEFAULT) { - free(hash); - free(mgf1_hash); - return FALSE; - } - - if(params->salt_len > RSA_PSS_SALT_LEN_DEFAULT) - { - salt_len = malloc(6); - snprintf(salt_len, 5, "%d", params->salt_len); - } - - len = 24 + strlen(hash) + strlen(mgf1_hash); - hash_and_padding = malloc(len+1); - - if(salt_len) - { - snprintf(hash_and_padding, len, "EMSA-PSS(%s,MGF1,%s)", hash, salt_len); + snprintf(hash_and_padding, sizeof(hash_and_padding), + "EMSA-PSS(%s,MGF1,%u)", hash, params->salt_len); } else { - snprintf(hash_and_padding, len, "EMSA-PSS(%s,MGF1)", hash); + snprintf(hash_and_padding, sizeof(hash_and_padding), + "EMSA-PSS(%s,MGF1)", hash); } - - if (verify_rsa_signature(this, hash_and_padding, data, signature)) - { - success = TRUE; - } - - if(salt_len) - free(salt_len); - free(hash); - free(mgf1_hash); - free(hash_and_padding); - return success; + return verify_rsa_signature(this, hash_and_padding, data, signature); } METHOD(public_key_t, get_type, key_type_t, @@ -247,23 +141,23 @@ METHOD(public_key_t, verify, bool, switch (scheme) { case SIGN_RSA_EMSA_PKCS1_NULL: - return verify_emsa_pkcs1_signature(this, "EMSA_PKCS1(Raw)", data, - signature); + return verify_rsa_signature(this, "EMSA_PKCS1(Raw)", data, + signature); case SIGN_RSA_EMSA_PKCS1_SHA1: - return verify_emsa_pkcs1_signature(this, "EMSA_PKCS1(SHA-1)", data, - signature); + return verify_rsa_signature(this, "EMSA_PKCS1(SHA-1)", data, + signature); case SIGN_RSA_EMSA_PKCS1_SHA2_224: - return verify_emsa_pkcs1_signature(this, "EMSA_PKCS1(SHA-224)", - data, signature); + return verify_rsa_signature(this, "EMSA_PKCS1(SHA-224)", + data, signature); case SIGN_RSA_EMSA_PKCS1_SHA2_256: - return verify_emsa_pkcs1_signature(this, "EMSA_PKCS1(SHA-256)", - data, signature); + return verify_rsa_signature(this, "EMSA_PKCS1(SHA-256)", + data, signature); case SIGN_RSA_EMSA_PKCS1_SHA2_384: - return verify_emsa_pkcs1_signature(this, "EMSA_PKCS1(SHA-384)", - data, signature); + return verify_rsa_signature(this, "EMSA_PKCS1(SHA-384)", + data, signature); case SIGN_RSA_EMSA_PKCS1_SHA2_512: - return verify_emsa_pkcs1_signature(this, "EMSA_PKCS1(SHA-512)", - data, signature); + return verify_rsa_signature(this, "EMSA_PKCS1(SHA-512)", + data, signature); case SIGN_RSA_EMSA_PSS: return verify_emsa_pss_signature(this, params, data, signature); default: @@ -277,6 +171,8 @@ METHOD(public_key_t, encrypt, bool, private_botan_rsa_public_key_t *this, encryption_scheme_t scheme, chunk_t plain, chunk_t *crypto) { + botan_pk_op_encrypt_t encrypt_op; + botan_rng_t rng; const char* padding; switch (scheme) @@ -305,34 +201,25 @@ METHOD(public_key_t, encrypt, bool, return FALSE; } - botan_rng_t rng; if (botan_rng_init(&rng, "user")) { return FALSE; } - botan_pk_op_encrypt_t encrypt_op; if (botan_pk_op_encrypt_create(&encrypt_op, this->key, padding, 0)) { botan_rng_destroy(rng); return FALSE; } - /* - * get size of ciphertext first - */ - if (botan_pk_op_encrypt(encrypt_op, rng, NULL, &crypto->len, plain.ptr, - plain.len) - != BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE) + crypto->len = 0; + if (botan_pk_op_encrypt_output_length(encrypt_op, plain.len, &crypto->len)) { botan_rng_destroy(rng); botan_pk_op_encrypt_destroy(encrypt_op); return FALSE; } - /* - * now get the ciphertext - */ *crypto = chunk_alloc(crypto->len); if (botan_pk_op_encrypt(encrypt_op, rng, crypto->ptr, &crypto->len, plain.ptr, plain.len)) @@ -342,7 +229,6 @@ METHOD(public_key_t, encrypt, bool, botan_pk_op_encrypt_destroy(encrypt_op); return FALSE; } - botan_rng_destroy(rng); botan_pk_op_encrypt_destroy(encrypt_op); return TRUE; @@ -356,18 +242,14 @@ METHOD(public_key_t, get_keysize, int, if (botan_mp_init(&n)) { - return -1; + return 0; } - if (botan_pubkey_rsa_get_n(n, this->key)) - { - return -1; - } - - if (botan_mp_num_bits(n, &bits)) + if (botan_pubkey_rsa_get_n(n, this->key) || + botan_mp_num_bits(n, &bits)) { botan_mp_destroy(n); - return -1; + return 0; } botan_mp_destroy(n); @@ -378,59 +260,14 @@ METHOD(public_key_t, get_fingerprint, bool, private_botan_rsa_public_key_t *this, cred_encoding_type_t type, chunk_t *fp) { - chunk_t n, e; - bool success = FALSE; - - if (lib->encoding->get_cache(lib->encoding, type, &this->key, fp)) - { - return TRUE; - } - - if (botan_rsa_get_field(&this->key, "n", &n)) - { - return FALSE; - } - - if (botan_rsa_get_field(&this->key, "e", &e)) - { - chunk_free(&n); - return FALSE; - } - - success = lib->encoding->encode(lib->encoding, type, &this->key, fp, - CRED_PART_RSA_MODULUS, n, - CRED_PART_RSA_PUB_EXP, e, CRED_PART_END); - - chunk_free(&n); - chunk_free(&e); - return success; + return botan_get_fingerprint(this->key, this, type, fp); } METHOD(public_key_t, get_encoding, bool, private_botan_rsa_public_key_t *this, cred_encoding_type_t type, chunk_t *encoding) { - chunk_t n, e; - bool success = FALSE; - - if (botan_rsa_get_field(&this->key, "n", &n)) - { - return FALSE; - } - - if (botan_rsa_get_field(&this->key, "e", &e)) - { - chunk_free(&n); - return FALSE; - } - - success = lib->encoding->encode(lib->encoding, type, NULL, encoding, - CRED_PART_RSA_MODULUS, n, - CRED_PART_RSA_PUB_EXP, e, CRED_PART_END); - - chunk_free(&n); - chunk_free(&e); - return success; + return botan_get_encoding(this->key, type, encoding); } METHOD(public_key_t, get_ref, public_key_t*, @@ -445,11 +282,8 @@ METHOD(public_key_t, destroy, void, { if (ref_put(&this->ref)) { - if (&this->key) - { - lib->encoding->clear_cache(lib->encoding, &this->key); - botan_pubkey_destroy(this->key); - } + lib->encoding->clear_cache(lib->encoding, this); + botan_pubkey_destroy(this->key); free(this); } } @@ -482,14 +316,13 @@ static private_botan_rsa_public_key_t *create_empty() return this; } -/** - * See header. +/* + * Described in header */ botan_rsa_public_key_t *botan_rsa_public_key_load(key_type_t type, - va_list args) + va_list args) { private_botan_rsa_public_key_t *this = NULL; - chunk_t blob, n, e; n = e = blob = chunk_empty; @@ -519,43 +352,41 @@ botan_rsa_public_key_t *botan_rsa_public_key_load(key_type_t type, switch (type) { /* SubjectPublicKeyInfo */ + case KEY_RSA: case KEY_ANY: { + size_t namesize = 0; + char *namebuf; + this = create_empty(); if (botan_pubkey_load(&this->key, blob.ptr, blob.len)) { - destroy(this); + free(this); return NULL; } - size_t namesize = 0; if (botan_pubkey_algo_name(this->key, NULL, &namesize) != BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE) { - botan_pubkey_destroy(this->key); destroy(this); return NULL; } - char* namebuf = malloc(namesize); + namebuf = malloc(namesize); if (botan_pubkey_algo_name(this->key, namebuf, &namesize)) { free(namebuf); - botan_pubkey_destroy(this->key); destroy(this); return NULL; } - const char* algo_name = "RSA"; - if (!strneq(namebuf, algo_name, sizeof(algo_name))) + if (!strneq(namebuf, "RSA", namesize)) { free(namebuf); - botan_pubkey_destroy(this->key); destroy(this); return NULL; } - free(namebuf); break; } @@ -563,16 +394,16 @@ botan_rsa_public_key_t *botan_rsa_public_key_load(key_type_t type, return NULL; } } - else if(n.ptr && e.ptr && type == KEY_RSA) + else if (n.ptr && e.ptr && type == KEY_RSA) { - botan_mp_t mp_n, mp_e; - if (chunk_to_botan_mp(n, &mp_n)) + + if (!chunk_to_botan_mp(n, &mp_n)) { return NULL; } - if (chunk_to_botan_mp(e, &mp_e)) + if (!chunk_to_botan_mp(e, &mp_e)) { botan_mp_destroy(mp_n); return NULL; @@ -584,18 +415,15 @@ botan_rsa_public_key_t *botan_rsa_public_key_load(key_type_t type, { botan_mp_destroy(mp_n); botan_mp_destroy(mp_e); - destroy(this); + free(this); return NULL; } botan_mp_destroy(mp_n); botan_mp_destroy(mp_e); } - if (this != NULL) - { - return &this->public; - } - return NULL; + + return &this->public; } -#endif \ No newline at end of file +#endif diff --git a/src/libstrongswan/plugins/botan/botan_util.c b/src/libstrongswan/plugins/botan/botan_util.c index d69e29e62..9dcf592c5 100644 --- a/src/libstrongswan/plugins/botan/botan_util.c +++ b/src/libstrongswan/plugins/botan/botan_util.c @@ -1,4 +1,7 @@ /* + * Copyright (C) 2018 Tobias Brunner + * HSR Hochschule fuer Technik Rapperswil + * * Copyright (C) 2018 René Korthaus * Rohde & Schwarz Cybersecurity GmbH * @@ -27,17 +30,199 @@ #include -int chunk_to_botan_mp(chunk_t value, botan_mp_t *mp) +/* + * Described in header + */ +bool chunk_to_botan_mp(chunk_t value, botan_mp_t *mp) { if (botan_mp_init(mp)) { - return -1; + return FALSE; } if (botan_mp_from_bin(*mp, value.ptr, value.len)) { botan_mp_destroy(*mp); - return -1; + return FALSE; } - return 0; + return TRUE; +} + +/* + * Described in header + */ +const char *botan_get_hash(hash_algorithm_t hash) +{ + switch (hash) + { + case HASH_MD5: + return "MD5"; + case HASH_SHA1: + return "SHA-1"; + case HASH_SHA224: + return "SHA-224"; + case HASH_SHA256: + return "SHA-256"; + case HASH_SHA384: + return "SHA-384"; + case HASH_SHA512: + return "SHA-512"; + default: + return NULL; + } +} + +/* + * Described in header + */ +bool botan_get_encoding(botan_pubkey_t pubkey, cred_encoding_type_t type, + chunk_t *encoding) +{ + bool success = TRUE; + + encoding->len = 0; + if (botan_pubkey_export(pubkey, NULL, &encoding->len, + BOTAN_PRIVKEY_EXPORT_FLAG_DER) + != BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE) + { + return FALSE; + } + + *encoding = chunk_alloc(encoding->len); + if (botan_pubkey_export(pubkey, encoding->ptr, &encoding->len, + BOTAN_PRIVKEY_EXPORT_FLAG_DER)) + { + chunk_free(encoding); + return FALSE; + } + + if (type != PUBKEY_SPKI_ASN1_DER) + { + chunk_t asn1_encoding = *encoding; + + success = lib->encoding->encode(lib->encoding, type, NULL, encoding, + CRED_PART_ECDSA_PUB_ASN1_DER, + asn1_encoding, CRED_PART_END); + chunk_free(&asn1_encoding); + } + return success; +} + +/* + * Described in header + */ +bool botan_get_fingerprint(botan_pubkey_t pubkey, void *cache, + cred_encoding_type_t type, chunk_t *fp) +{ + hasher_t *hasher; + chunk_t key; + + if (cache && + lib->encoding->get_cache(lib->encoding, type, cache, fp)) + { + return TRUE; + } + + switch (type) + { + case KEYID_PUBKEY_SHA1: + { + /* subjectPublicKey -> use botan_pubkey_fingerprint() */ + fp->len = 0; + if (botan_pubkey_fingerprint(pubkey, "SHA-1", NULL, &fp->len) + != BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE) + { + return FALSE; + } + + *fp = chunk_alloc(fp->len); + if (botan_pubkey_fingerprint(pubkey, "SHA-1", fp->ptr, &fp->len)) + { + chunk_free(fp); + return FALSE; + } + break; + } + case KEYID_PUBKEY_INFO_SHA1: + { + /* subjectPublicKeyInfo -> use botan_pubkey_export(), then hash */ + if (!botan_get_encoding(pubkey, PUBKEY_SPKI_ASN1_DER, &key)) + { + return FALSE; + } + + hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1); + if (!hasher || !hasher->allocate_hash(hasher, key, fp)) + { + DBG1(DBG_LIB, "SHA1 hash algorithm not supported, " + "fingerprinting failed"); + DESTROY_IF(hasher); + chunk_free(&key); + return FALSE; + } + hasher->destroy(hasher); + chunk_free(&key); + break; + } + default: + return FALSE; + } + + if (cache) + { + lib->encoding->cache(lib->encoding, type, cache, *fp); + } + return TRUE; +} + +/* + * Described in header + */ +bool botan_get_signature(botan_privkey_t key, const char *scheme, + chunk_t data, chunk_t *signature) +{ + botan_pk_op_sign_t sign_op; + botan_rng_t rng; + + if (!scheme || !signature) + { + return FALSE; + } + + if (botan_pk_op_sign_create(&sign_op, key, scheme, 0)) + { + return FALSE; + } + + if (botan_pk_op_sign_update(sign_op, data.ptr, data.len)) + { + botan_pk_op_sign_destroy(sign_op); + return FALSE; + } + + signature->len = 0; + if (botan_pk_op_sign_output_length(sign_op, &signature->len)) + { + botan_pk_op_sign_destroy(sign_op); + return FALSE; + } + + if (botan_rng_init(&rng, "user")) + { + botan_pk_op_sign_destroy(sign_op); + return FALSE; + } + + *signature = chunk_alloc(signature->len); + if (botan_pk_op_sign_finish(sign_op, rng, signature->ptr, &signature->len)) + { + chunk_free(signature); + botan_rng_destroy(rng); + botan_pk_op_sign_destroy(sign_op); + return FALSE; + } + + botan_rng_destroy(rng); + botan_pk_op_sign_destroy(sign_op); + return TRUE; } diff --git a/src/libstrongswan/plugins/botan/botan_util.h b/src/libstrongswan/plugins/botan/botan_util.h index d19658740..1f4b96f6e 100644 --- a/src/libstrongswan/plugins/botan/botan_util.h +++ b/src/libstrongswan/plugins/botan/botan_util.h @@ -1,4 +1,7 @@ /* + * Copyright (C) 2018 Tobias Brunner + * HSR Hochschule fuer Technik Rapperswil + * * Copyright (C) 2018 René Korthaus * Rohde & Schwarz Cybersecurity GmbH * @@ -34,8 +37,56 @@ #include /** - * Converts chunk_t to botan_mp_t + * Converts chunk_t to botan_mp_t. + * + * @param value chunk to convert + * @param mp allocated botan_mp_t + * @return TRUE if conversion successful */ -int chunk_to_botan_mp(chunk_t value, botan_mp_t *mp); +bool chunk_to_botan_mp(chunk_t value, botan_mp_t *mp); + +/** + * Get the Botan string identifier for the given hash algorithm. + * + * @param hash hash algorithm + * @return Botan string identifier, NULL if not found + */ +const char *botan_get_hash(hash_algorithm_t hash); + +/** + * Get the encoding of a botan_pubkey_t. + * + * @param pubkey public key object + * @param type encoding type + * @param encoding allocated encoding + * @return TRUE if encoding successful + */ +bool botan_get_encoding(botan_pubkey_t pubkey, cred_encoding_type_t type, + chunk_t *encoding); + +/** + * Get the fingerprint of a botan_pubkey_t. + * + * @param pubkey public key object + * @param cache key to use for caching, NULL to not cache + * @param type fingerprint type + * @param fp allocated fingerprint + * @return TRUE if fingerprinting successful + */ +bool botan_get_fingerprint(botan_pubkey_t pubkey, void *cache, + cred_encoding_type_t type, chunk_t *fp); + +/** + * Sign the given data using the provided key with the specified signature + * scheme (hash/padding). + * + * @param key private key object + * @param scheme hash/padding algorithm + * @param data data to sign + * @param signature allocated signature + * @return TRUE if signature successfully created + */ +bool botan_get_signature(botan_privkey_t key, const char *scheme, + chunk_t data, chunk_t *signature); #endif /** BOTAN_UTIL_H_ @}*/ From ba7e74291e1650714b724e37cd43640f9cb65610 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Thu, 9 Aug 2018 08:45:48 +0200 Subject: [PATCH 05/23] pkcs1: Accept EC private keys without public key but make sure of an OID --- src/libstrongswan/plugins/pkcs1/pkcs1_builder.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/libstrongswan/plugins/pkcs1/pkcs1_builder.c b/src/libstrongswan/plugins/pkcs1/pkcs1_builder.c index 967e501d1..c934f0b1d 100644 --- a/src/libstrongswan/plugins/pkcs1/pkcs1_builder.c +++ b/src/libstrongswan/plugins/pkcs1/pkcs1_builder.c @@ -271,7 +271,8 @@ end: * } * * While the parameters and publicKey fields are OPTIONAL, RFC 5915 says that - * parameters MUST be included and publicKey SHOULD be. + * parameters MUST be included (an errata clarifies this, so this is only the + * case for plain private keys, not encoded in PKCS#8) and publicKey SHOULD be. */ static bool is_ec_private_key(chunk_t blob) { @@ -281,7 +282,8 @@ static bool is_ec_private_key(chunk_t blob) asn1_parse_integer_uint64(data) == 1 && asn1_unwrap(&blob, &data) == ASN1_OCTET_STRING && asn1_unwrap(&blob, &data) == ASN1_CONTEXT_C_0 && - asn1_unwrap(&blob, &data) == ASN1_CONTEXT_C_1; + asn1_unwrap(&data, &data) == ASN1_OID && + (!blob.len || (asn1_unwrap(&blob, &data) == ASN1_CONTEXT_C_1)); } /** From 72491b7843bf29ac350de0db6729807e5321b7b0 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 8 Aug 2018 18:23:11 +0200 Subject: [PATCH 06/23] botan: Encode curve OID and public key in EC private key Without OID we can't generate an algorithmIdentifier when loading the key again. And older versions of OpenSSL insist on a public key when e.g. converting a key to PKCS#8. Simply unwrapping the ECPrivateKey structure avoids log messages when parsing other keys in the KEY_ANY case. --- .../plugins/botan/botan_ec_private_key.c | 203 +++++++++++------- .../plugins/botan/botan_plugin.c | 1 + 2 files changed, 128 insertions(+), 76 deletions(-) diff --git a/src/libstrongswan/plugins/botan/botan_ec_private_key.c b/src/libstrongswan/plugins/botan/botan_ec_private_key.c index e5a6c7c4d..bfe8f3833 100644 --- a/src/libstrongswan/plugins/botan/botan_ec_private_key.c +++ b/src/libstrongswan/plugins/botan/botan_ec_private_key.c @@ -1,4 +1,7 @@ /* + * Copyright (C) 2018 Tobias Brunner + * HSR Hochschule fuer Technik Rapperswil + * * Copyright (C) 2018 René Korthaus * Copyright (C) 2018 Konstantinos Kolelis * Rohde & Schwarz Cybersecurity GmbH @@ -32,7 +35,6 @@ #include #include -#include #include @@ -55,6 +57,11 @@ struct private_botan_ec_private_key_t { */ botan_privkey_t key; + /** + * OID of the curve + */ + int oid; + /** * Reference count */ @@ -227,6 +234,79 @@ METHOD(private_key_t, get_fingerprint, bool, return success; } +/** + * Get a field from the private key as chunk + */ +static bool get_field_chunk(botan_privkey_t key, char *field, chunk_t *out) +{ + botan_mp_t val; + + *out = chunk_empty; + + if (botan_mp_init(&val)) + { + return FALSE; + } + + if (botan_privkey_get_field(val, key, field) || + botan_mp_num_bytes(val, &out->len)) + { + botan_mp_destroy(val); + return FALSE; + } + + *out = chunk_alloc(out->len); + + if (botan_mp_to_bin(val, out->ptr)) + { + botan_mp_destroy(val); + chunk_free(out); + return FALSE; + } + botan_mp_destroy(val); + return TRUE; +} + +/** + * Encodes the public key as ASN.1 BIT STRING (0x04 || x || y) + */ +static bool get_pubkey_bitstring(botan_privkey_t key, chunk_t *out) +{ + chunk_t p, x, y, pub; + size_t len; + + if (!get_field_chunk(key, "p", &p)) + { + return FALSE; + } + len = p.len; + chunk_free(&p); + + if (!get_field_chunk(key, "public_x", &x)) + { + return FALSE; + } + if (!get_field_chunk(key, "public_y", &y)) + { + chunk_free(&x); + return FALSE; + } + + pub = chunk_alloca(2 * len); + memset(pub.ptr, 0, pub.len); + memcpy(pub.ptr + (len - x.len), x.ptr, x.len); + memcpy(pub.ptr + len + (len - y.len), y.ptr, y.len); + chunk_free(&x); + chunk_free(&y); + + *out = asn1_wrap(ASN1_BIT_STRING, "ccc", + /* unused bits in the bit string */ + chunk_from_chars(0x00), + /* uncompressed format */ + chunk_from_chars(0x04), pub); + return TRUE; +} + METHOD(private_key_t, get_encoding, bool, private_botan_ec_private_key_t *this, cred_encoding_type_t type, chunk_t *encoding) @@ -236,41 +316,25 @@ METHOD(private_key_t, get_encoding, bool, case PRIVKEY_ASN1_DER: case PRIVKEY_PEM: { - botan_mp_t x; - chunk_t pval = chunk_empty; + chunk_t priv, pub; bool success = TRUE; - if (botan_mp_init(&x)) + if (!get_field_chunk(this->key, "x", &priv)) { return FALSE; } - - if (botan_privkey_get_field(x, this->key, "x")) + if (!get_pubkey_bitstring(this->key, &pub)) { - botan_mp_destroy(x); + chunk_clear(&priv); return FALSE; } - if (botan_mp_num_bytes(x, &pval.len)) - { - botan_mp_destroy(x); - return FALSE; - } - - pval = chunk_alloc(pval.len); - - if (botan_mp_to_bin(x, pval.ptr)) - { - botan_mp_destroy(x); - return FALSE; - } - - /* FIXME: this does not include the params, which the parser/loader - * below actually requires (and is mandated by RFC 5915). we might - * have to store/parse the OID so we can add it here. */ - *encoding = asn1_wrap(ASN1_SEQUENCE, "ms", + *encoding = asn1_wrap(ASN1_SEQUENCE, "msmm", asn1_integer("c", chunk_from_chars(0x01)), - asn1_wrap(ASN1_OCTET_STRING, "s", pval)); + asn1_wrap(ASN1_OCTET_STRING, "s", priv), + asn1_wrap(ASN1_CONTEXT_C_0, "m", + asn1_build_known_oid(this->oid)), + asn1_wrap(ASN1_CONTEXT_C_1, "m", pub)); if (type == PRIVKEY_PEM) { @@ -282,8 +346,6 @@ METHOD(private_key_t, get_encoding, bool, asn1_encoding, CRED_PART_END); chunk_clear(&asn1_encoding); } - - botan_mp_destroy(x); return success; } default: @@ -312,7 +374,7 @@ METHOD(private_key_t, destroy, void, /** * Internal generic constructor */ -static private_botan_ec_private_key_t *create_empty() +static private_botan_ec_private_key_t *create_empty(int oid) { private_botan_ec_private_key_t *this; @@ -333,6 +395,7 @@ static private_botan_ec_private_key_t *create_empty() .destroy = _destroy, }, }, + .oid = oid, .ref = 1, ); @@ -347,6 +410,7 @@ botan_ec_private_key_t *botan_ec_private_key_gen(key_type_t type, va_list args) private_botan_ec_private_key_t *this; botan_rng_t rng; u_int key_size = 0; + int oid; const char *curve; while (TRUE) @@ -373,12 +437,15 @@ botan_ec_private_key_t *botan_ec_private_key_gen(key_type_t type, va_list args) { case 256: curve = "secp256r1"; + oid = OID_PRIME256V1; break; case 384: curve = "secp384r1"; + oid = OID_SECT384R1; break; case 521: curve = "secp521r1"; + oid = OID_SECT521R1; break; default: DBG1(DBG_LIB, "EC private key size %d not supported via botan", @@ -391,7 +458,7 @@ botan_ec_private_key_t *botan_ec_private_key_gen(key_type_t type, va_list args) return NULL; } - this = create_empty(); + this = create_empty(oid); if (botan_privkey_create_ecdsa(&this->key, rng, curve)) { @@ -405,21 +472,6 @@ botan_ec_private_key_t *botan_ec_private_key_gen(key_type_t type, va_list args) return &this->public; } -/** - * ASN.1 definition of a ECPrivateKey structure (RFC 5915) - */ -static const asn1Object_t ecPrivateKeyObjects[] = { - { 0, "ECPrivateKey", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */ - { 1, "version", ASN1_INTEGER, ASN1_BODY }, /* 1 */ - { 1, "privateKey", ASN1_OCTET_STRING, ASN1_BODY }, /* 2 */ - { 1, "parameters", ASN1_EOC, ASN1_RAW }, /* 3 */ - { 1, "publicKey", ASN1_BIT_STRING, ASN1_OPT }, /* 4 */ - { 0, "exit", ASN1_EOC, ASN1_EXIT } -}; - -#define ECPK_PRIVATE_KEY 2 -#define ECPK_PRIVATE_KEY_PARAMS 3 - /* * Described in header */ @@ -427,8 +479,9 @@ botan_ec_private_key_t *botan_ec_private_key_load(key_type_t type, va_list args) { private_botan_ec_private_key_t *this; chunk_t params = chunk_empty, key = chunk_empty; - chunk_t object, alg_id = chunk_empty, pkcs8 = chunk_empty; + chunk_t alg_id = chunk_empty, pkcs8 = chunk_empty; botan_rng_t rng; + int oid = OID_UNKNOWN; while (TRUE) { @@ -449,50 +502,48 @@ botan_ec_private_key_t *botan_ec_private_key_load(key_type_t type, va_list args) } /* - * botan expects a PKCS#8 private key, so we build one - * RFC 5282 mandates ECParameters as part of the algorithmIdentifier + * Botan expects a PKCS#8 private key, so we build one, if necessary. + * RFC 5480 mandates ECParameters as part of the algorithmIdentifier, which + * we should get from e.g. the pkcs8 plugin. */ - if (params.len != 0) + if (params.len != 0 && type == KEY_ECDSA) { - /* if ECDomainParameters is passed, just append it */ + /* if ECParameters is passed, just use it */ alg_id = asn1_algorithmIdentifier_params(OID_EC_PUBLICKEY, chunk_clone(params)); + if (asn1_unwrap(¶ms, ¶ms) == ASN1_OID) + { + oid = asn1_known_oid(params); + } } else { /* - * no explicit ECParameters passed, so we extract them from the - * ECPrivateKey structure and append it to the algorithmIdentifier + * no explicit ECParameters passed, try to extract them from the + * ECPrivateKey structure and create an algorithmIdentifier */ - asn1_parser_t *parser; - int objectID; + chunk_t unwrap = key, inner; - parser = asn1_parser_create(ecPrivateKeyObjects, key); - parser->set_flags(parser, FALSE, TRUE); - - while (parser->iterate(parser, &objectID, &object)) + if (asn1_unwrap(&unwrap, &unwrap) == ASN1_SEQUENCE && + asn1_unwrap(&unwrap, &inner) == ASN1_INTEGER && + asn1_parse_integer_uint64(inner) == 1 && + asn1_unwrap(&unwrap, &inner) == ASN1_OCTET_STRING && + asn1_unwrap(&unwrap, &inner) == ASN1_CONTEXT_C_0 && + asn1_unwrap(&inner, &inner) == ASN1_OID) { - if (objectID == ECPK_PRIVATE_KEY_PARAMS) + oid = asn1_known_oid(inner); + if (oid != OID_UNKNOWN) { - if (!asn1_parse_simple_object(&object, ASN1_CONTEXT_C_0, 0, - "parameters")) - { - parser->destroy(parser); - return NULL; - } - - if (asn1_unwrap(&object, ¶ms) != ASN1_OID) - { - parser->destroy(parser); - return NULL; - } - break; + alg_id = asn1_algorithmIdentifier_params(OID_EC_PUBLICKEY, + asn1_simple_object(ASN1_OID, inner)); } } + } - parser->destroy(parser); - alg_id = asn1_algorithmIdentifier_params(OID_EC_PUBLICKEY, - asn1_simple_object(ASN1_OID, params)); + if (oid == OID_UNKNOWN) + { + chunk_free(&alg_id); + return NULL; } pkcs8 = asn1_wrap(ASN1_SEQUENCE, "mms", @@ -500,7 +551,7 @@ botan_ec_private_key_t *botan_ec_private_key_load(key_type_t type, va_list args) alg_id, asn1_wrap(ASN1_OCTET_STRING, "c", key)); - this = create_empty(); + this = create_empty(oid); if (botan_rng_init(&rng, "user")) { diff --git a/src/libstrongswan/plugins/botan/botan_plugin.c b/src/libstrongswan/plugins/botan/botan_plugin.c index bce6b8fee..468d50530 100644 --- a/src/libstrongswan/plugins/botan/botan_plugin.c +++ b/src/libstrongswan/plugins/botan/botan_plugin.c @@ -211,6 +211,7 @@ METHOD(plugin_t, get_features, int, /* EC private/public key loading */ PLUGIN_REGISTER(PRIVKEY, botan_ec_private_key_load, TRUE), PLUGIN_PROVIDE(PRIVKEY, KEY_ECDSA), + PLUGIN_PROVIDE(PRIVKEY, KEY_ANY), PLUGIN_REGISTER(PRIVKEY_GEN, botan_ec_private_key_gen, FALSE), PLUGIN_PROVIDE(PRIVKEY_GEN, KEY_ECDSA), PLUGIN_REGISTER(PUBKEY, botan_ec_public_key_load, TRUE), From 607f10dca409d6c278cd6fb69e332bbed22e5268 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Thu, 9 Aug 2018 13:00:50 +0200 Subject: [PATCH 07/23] botan: Load public/private keys generically Simplifies public key loading and this way unencrypted PKCS#8-encoded keys can be loaded directly without pkcs8 plugin (code for encrypted keys could probably later be added, if necessary). It also simplifies the implementation of private_key_t::get_public_key() a lot. --- src/libstrongswan/plugins/botan/Makefile.am | 1 + .../plugins/botan/botan_ec_private_key.c | 44 ++-- .../plugins/botan/botan_ec_private_key.h | 15 ++ .../plugins/botan/botan_ec_public_key.c | 70 +----- .../plugins/botan/botan_ec_public_key.h | 15 +- .../plugins/botan/botan_plugin.c | 44 +++- .../plugins/botan/botan_rsa_private_key.c | 75 +++---- .../plugins/botan/botan_rsa_private_key.h | 10 + .../plugins/botan/botan_rsa_public_key.c | 72 ++---- .../plugins/botan/botan_rsa_public_key.h | 17 +- .../plugins/botan/botan_util_keys.c | 211 ++++++++++++++++++ .../plugins/botan/botan_util_keys.h | 61 +++++ 12 files changed, 415 insertions(+), 220 deletions(-) create mode 100644 src/libstrongswan/plugins/botan/botan_util_keys.c create mode 100644 src/libstrongswan/plugins/botan/botan_util_keys.h diff --git a/src/libstrongswan/plugins/botan/Makefile.am b/src/libstrongswan/plugins/botan/Makefile.am index bbad71541..1188841d3 100644 --- a/src/libstrongswan/plugins/botan/Makefile.am +++ b/src/libstrongswan/plugins/botan/Makefile.am @@ -24,6 +24,7 @@ libstrongswan_botan_la_SOURCES = \ botan_ec_public_key.h botan_ec_public_key.c \ botan_ec_private_key.h botan_ec_private_key.c \ botan_util.h botan_util.c \ + botan_util_keys.h botan_util_keys.c \ botan_gcm.h botan_gcm.c libstrongswan_botan_la_LDFLAGS = -module -avoid-version diff --git a/src/libstrongswan/plugins/botan/botan_ec_private_key.c b/src/libstrongswan/plugins/botan/botan_ec_private_key.c index bfe8f3833..78ba43f81 100644 --- a/src/libstrongswan/plugins/botan/botan_ec_private_key.c +++ b/src/libstrongswan/plugins/botan/botan_ec_private_key.c @@ -27,6 +27,7 @@ #include "botan_ec_private_key.h" +#include "botan_ec_public_key.h" #include "botan_util.h" #include @@ -177,39 +178,13 @@ METHOD(private_key_t, get_type, key_type_t, METHOD(private_key_t, get_public_key, public_key_t*, private_botan_ec_private_key_t *this) { - public_key_t *public; botan_pubkey_t pubkey; - chunk_t key = chunk_empty; if (botan_privkey_export_pubkey(&pubkey, this->key)) { - return FALSE; + return NULL; } - - if (botan_pubkey_export(pubkey, NULL, &key.len, - BOTAN_PRIVKEY_EXPORT_FLAG_DER) - != BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE) - { - botan_pubkey_destroy(pubkey); - return FALSE; - } - - key = chunk_alloc(key.len); - - if (botan_pubkey_export(pubkey, key.ptr, &key.len, - BOTAN_PRIVKEY_EXPORT_FLAG_DER)) - { - chunk_free(&key); - botan_pubkey_destroy(pubkey); - return FALSE; - } - - public = lib->creds->create(lib->creds, CRED_PUBLIC_KEY, KEY_ECDSA, - BUILD_BLOB_ASN1_DER, key, BUILD_END); - - chunk_free(&key); - botan_pubkey_destroy(pubkey); - return public; + return (public_key_t*)botan_ec_public_key_adopt(pubkey); } METHOD(private_key_t, get_fingerprint, bool, @@ -402,6 +377,19 @@ static private_botan_ec_private_key_t *create_empty(int oid) return this; } +/* + * Described in header + */ +botan_ec_private_key_t *botan_ec_private_key_adopt(botan_privkey_t key, int oid) +{ + private_botan_ec_private_key_t *this; + + this = create_empty(oid); + this->key = key; + + return &this->public; +} + /* * Described in header */ diff --git a/src/libstrongswan/plugins/botan/botan_ec_private_key.h b/src/libstrongswan/plugins/botan/botan_ec_private_key.h index 9450331a4..2b9686ceb 100644 --- a/src/libstrongswan/plugins/botan/botan_ec_private_key.h +++ b/src/libstrongswan/plugins/botan/botan_ec_private_key.h @@ -1,4 +1,7 @@ /* + * Copyright (C) 2018 Tobias Brunner + * HSR Hochschule fuer Technik Rapperswil + * * Copyright (C) 2018 René Korthaus * Copyright (C) 2018 Konstantinos Kolelis * Rohde & Schwarz Cybersecurity GmbH @@ -30,6 +33,8 @@ #ifndef BOTAN_EC_PRIVATE_KEY_H_ #define BOTAN_EC_PRIVATE_KEY_H_ +#include + #include #include @@ -69,4 +74,14 @@ botan_ec_private_key_t *botan_ec_private_key_gen(key_type_t type, va_list args); botan_ec_private_key_t *botan_ec_private_key_load(key_type_t type, va_list args); +/** + * Load a ECDSA private key by adopting a botan_privkey_t object. + * + * @param key private key object (adopted) + * @param oid EC curve OID + * @return loaded key, NULL on failure + */ +botan_ec_private_key_t *botan_ec_private_key_adopt(botan_privkey_t key, + int oid); + #endif /** BOTAN_EC_PRIVATE_KEY_H_ @}*/ diff --git a/src/libstrongswan/plugins/botan/botan_ec_public_key.c b/src/libstrongswan/plugins/botan/botan_ec_public_key.c index 54782bcbf..4c85dbcec 100644 --- a/src/libstrongswan/plugins/botan/botan_ec_public_key.c +++ b/src/libstrongswan/plugins/botan/botan_ec_public_key.c @@ -248,33 +248,9 @@ METHOD(public_key_t, destroy, void, /* * Described in header */ -botan_ec_public_key_t *botan_ec_public_key_load(key_type_t type, va_list args) +botan_ec_public_key_t *botan_ec_public_key_adopt(botan_pubkey_t key) { private_botan_ec_public_key_t *this; - chunk_t blob = chunk_empty; - botan_rng_t rng; - size_t namesize = 0; - char *namebuf; - - if (type != KEY_ECDSA) - { - return NULL; - } - - while (TRUE) - { - switch (va_arg(args, builder_part_t)) - { - case BUILD_BLOB_ASN1_DER: - blob = va_arg(args, chunk_t); - continue; - case BUILD_END: - break; - default: - return NULL; - } - break; - } INIT(this, .public = { @@ -291,52 +267,10 @@ botan_ec_public_key_t *botan_ec_public_key_load(key_type_t type, va_list args) .destroy = _destroy, }, }, + .key = key, .ref = 1, ); - if (botan_pubkey_load(&this->key, blob.ptr, blob.len)) - { - free(this); - return NULL; - } - - if (botan_pubkey_algo_name(this->key, NULL, &namesize) - != BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE) - { - destroy(this); - return NULL; - } - - namebuf = malloc(namesize); - if (botan_pubkey_algo_name(this->key, namebuf, &namesize)) - { - free(namebuf); - destroy(this); - return NULL; - } - - if (!strneq(namebuf, "ECDSA", namesize)) - { - free(namebuf); - destroy(this); - return NULL; - } - free(namebuf); - - if (botan_rng_init(&rng, "user")) - { - return NULL; - } - - if (botan_pubkey_check_key(this->key, rng, BOTAN_CHECK_KEY_EXPENSIVE_TESTS)) - { - DBG1(DBG_LIB, "public key failed key checks"); - botan_rng_destroy(rng); - destroy(this); - return NULL; - } - - botan_rng_destroy(rng); return &this->public; } diff --git a/src/libstrongswan/plugins/botan/botan_ec_public_key.h b/src/libstrongswan/plugins/botan/botan_ec_public_key.h index eb49ec3cb..ddb3d5b04 100644 --- a/src/libstrongswan/plugins/botan/botan_ec_public_key.h +++ b/src/libstrongswan/plugins/botan/botan_ec_public_key.h @@ -25,11 +25,13 @@ #ifndef BOTAN_EC_PUBLIC_KEY_H_ #define BOTAN_EC_PUBLIC_KEY_H_ -typedef struct botan_ec_public_key_t botan_ec_public_key_t; +#include #include #include +typedef struct botan_ec_public_key_t botan_ec_public_key_t; + /** * public_key_t implementation of ECDSA using botan. */ @@ -42,14 +44,11 @@ struct botan_ec_public_key_t { }; /** - * Load a ECDSA public key using botan. + * Load a ECDSA public key by adopting a botan_pubkey_t object. * - * Accepts a BUILD_BLOB_ASN1_DER argument. - * - * @param type type of the key, must be KEY_ECDSA - * @param args builder_part_t argument list - * @return loaded key, NULL on failure + * @param key public key object (adopted) + * @return loaded key, NULL on failure */ -botan_ec_public_key_t *botan_ec_public_key_load(key_type_t type, va_list args); +botan_ec_public_key_t *botan_ec_public_key_adopt(botan_pubkey_t key); #endif /** BOTAN_EC_PUBLIC_KEY_H_ @}*/ diff --git a/src/libstrongswan/plugins/botan/botan_plugin.c b/src/libstrongswan/plugins/botan/botan_plugin.c index 468d50530..b387a2bd1 100644 --- a/src/libstrongswan/plugins/botan/botan_plugin.c +++ b/src/libstrongswan/plugins/botan/botan_plugin.c @@ -1,4 +1,7 @@ /* + * Copyright (C) 2018 Tobias Brunner + * HSR Hochschule fuer Technik Rapperswil + * * Copyright (C) 2018 René Korthaus * Copyright (C) 2018 Konstantinos Kolelis * Rohde & Schwarz Cybersecurity GmbH @@ -34,6 +37,7 @@ #include "botan_ec_public_key.h" #include "botan_ec_private_key.h" #include "botan_gcm.h" +#include "botan_util_keys.h" #include @@ -79,6 +83,16 @@ METHOD(plugin_t, get_features, int, PLUGIN_PROVIDE(DH, MODP_1024_160), PLUGIN_PROVIDE(DH, MODP_768_BIT), PLUGIN_PROVIDE(DH, MODP_CUSTOM), +#endif +#ifdef BOTAN_HAS_ECDH + /* EC DH groups */ + PLUGIN_REGISTER(DH, botan_ec_diffie_hellman_create), + PLUGIN_PROVIDE(DH, ECP_256_BIT), + PLUGIN_PROVIDE(DH, ECP_384_BIT), + PLUGIN_PROVIDE(DH, ECP_521_BIT), + PLUGIN_PROVIDE(DH, ECP_256_BP), + PLUGIN_PROVIDE(DH, ECP_384_BP), + PLUGIN_PROVIDE(DH, ECP_512_BP), #endif /* crypters */ PLUGIN_REGISTER(CRYPTER, botan_crypter_create), @@ -147,15 +161,24 @@ METHOD(plugin_t, get_features, int, #endif #endif /* BOTAN_HAS_HMAC */ -#ifdef BOTAN_HAS_ECDH - /* EC DH groups */ - PLUGIN_REGISTER(DH, botan_ec_diffie_hellman_create), - PLUGIN_PROVIDE(DH, ECP_256_BIT), - PLUGIN_PROVIDE(DH, ECP_384_BIT), - PLUGIN_PROVIDE(DH, ECP_521_BIT), - PLUGIN_PROVIDE(DH, ECP_256_BP), - PLUGIN_PROVIDE(DH, ECP_384_BP), - PLUGIN_PROVIDE(DH, ECP_512_BP), + /* generic key loaders */ +#if defined (BOTAN_HAS_RSA) || defined(BOTAN_HAS_ECDSA) + PLUGIN_REGISTER(PUBKEY, botan_public_key_load, TRUE), + PLUGIN_PROVIDE(PUBKEY, KEY_ANY), +#ifdef BOTAN_HAS_RSA + PLUGIN_PROVIDE(PUBKEY, KEY_RSA), +#endif +#ifdef BOTAN_HAS_ECDSA + PLUGIN_PROVIDE(PUBKEY, KEY_ECDSA), +#endif + PLUGIN_REGISTER(PRIVKEY, botan_private_key_load, TRUE), + PLUGIN_PROVIDE(PRIVKEY, KEY_ANY), +#ifdef BOTAN_HAS_RSA + PLUGIN_PROVIDE(PRIVKEY, KEY_RSA), +#endif +#ifdef BOTAN_HAS_ECDSA + PLUGIN_PROVIDE(PRIVKEY, KEY_ECDSA), +#endif #endif /* RSA */ #ifdef BOTAN_HAS_RSA @@ -164,6 +187,7 @@ METHOD(plugin_t, get_features, int, PLUGIN_PROVIDE(PUBKEY, KEY_RSA), PLUGIN_REGISTER(PRIVKEY, botan_rsa_private_key_load, TRUE), PLUGIN_PROVIDE(PRIVKEY, KEY_RSA), + PLUGIN_PROVIDE(PRIVKEY, KEY_ANY), PLUGIN_REGISTER(PRIVKEY_GEN, botan_rsa_private_key_gen, FALSE), PLUGIN_PROVIDE(PRIVKEY_GEN, KEY_RSA), /* encryption/signature schemes */ @@ -214,8 +238,6 @@ METHOD(plugin_t, get_features, int, PLUGIN_PROVIDE(PRIVKEY, KEY_ANY), PLUGIN_REGISTER(PRIVKEY_GEN, botan_ec_private_key_gen, FALSE), PLUGIN_PROVIDE(PRIVKEY_GEN, KEY_ECDSA), - PLUGIN_REGISTER(PUBKEY, botan_ec_public_key_load, TRUE), - PLUGIN_PROVIDE(PUBKEY, KEY_ECDSA), #ifdef BOTAN_HAS_EMSA_RAW PLUGIN_PROVIDE(PRIVKEY_SIGN, SIGN_ECDSA_WITH_NULL), PLUGIN_PROVIDE(PUBKEY_VERIFY, SIGN_ECDSA_WITH_NULL), diff --git a/src/libstrongswan/plugins/botan/botan_rsa_private_key.c b/src/libstrongswan/plugins/botan/botan_rsa_private_key.c index 29f692333..8b2583b96 100644 --- a/src/libstrongswan/plugins/botan/botan_rsa_private_key.c +++ b/src/libstrongswan/plugins/botan/botan_rsa_private_key.c @@ -1,4 +1,7 @@ /* + * Copyright (C) 2018 Tobias Brunner + * HSR Hochschule fuer Technik Rapperswil + * * Copyright (C) 2018 René Korthaus * Rohde & Schwarz Cybersecurity GmbH * @@ -22,6 +25,7 @@ */ #include "botan_rsa_private_key.h" +#include "botan_rsa_public_key.h" #include @@ -56,39 +60,6 @@ struct private_botan_rsa_private_key_t { refcount_t ref; }; -/** - * Get the binary representation of a named RSA parameter - */ -static bool get_rsa_field(botan_privkey_t *key, const char *field_name, - chunk_t *value) -{ - botan_mp_t field; - size_t field_size = 0; - - if (botan_mp_init(&field)) - { - return FALSE; - } - - if (botan_privkey_get_field(field, *key, field_name) || - botan_mp_num_bytes(field, &field_size) || - !field_size) - { - botan_mp_destroy(field); - return FALSE; - } - - *value = chunk_alloc(field_size); - if (botan_mp_to_bin(field, value->ptr)) - { - botan_mp_destroy(field); - chunk_clear(value); - return FALSE; - } - botan_mp_destroy(field); - return TRUE; -} - /** * Build an EMSA PSS signature described in PKCS#1 */ @@ -251,27 +222,13 @@ METHOD(private_key_t, get_keysize, int, METHOD(private_key_t, get_public_key, public_key_t*, private_botan_rsa_private_key_t *this) { - public_key_t *pub_key; - chunk_t n, e; + botan_pubkey_t pubkey; - if (!get_rsa_field(&this->key, "n", &n)) + if (botan_privkey_export_pubkey(&pubkey, this->key)) { return NULL; } - - if (!get_rsa_field(&this->key, "e", &e)) - { - chunk_free(&n); - return NULL; - } - - pub_key = lib->creds->create(lib->creds, CRED_PUBLIC_KEY, KEY_RSA, - BUILD_RSA_MODULUS, n, BUILD_RSA_PUB_EXP, e, - BUILD_END); - - chunk_free(&n); - chunk_free(&e); - return pub_key; + return (public_key_t*)botan_rsa_public_key_adopt(pubkey); } METHOD(private_key_t, get_fingerprint, bool, @@ -382,6 +339,19 @@ static private_botan_rsa_private_key_t *create_empty() return this; } +/* + * Described in header + */ +botan_rsa_private_key_t *botan_rsa_private_key_adopt(botan_privkey_t key) +{ + private_botan_rsa_private_key_t *this; + + this = create_empty(); + this->key = key; + + return &this->public; +} + /* * Described in header */ @@ -650,6 +620,11 @@ botan_rsa_private_key_t *botan_rsa_private_key_load(key_type_t type, break; } + if (type == KEY_ANY && !blob.ptr) + { + return NULL; + } + if (blob.ptr) { this = create_empty(); diff --git a/src/libstrongswan/plugins/botan/botan_rsa_private_key.h b/src/libstrongswan/plugins/botan/botan_rsa_private_key.h index 546b88dac..f0f419c7f 100644 --- a/src/libstrongswan/plugins/botan/botan_rsa_private_key.h +++ b/src/libstrongswan/plugins/botan/botan_rsa_private_key.h @@ -29,6 +29,8 @@ #ifndef BOTAN_RSA_PRIVATE_KEY_H_ #define BOTAN_RSA_PRIVATE_KEY_H_ +#include + #include #include @@ -69,4 +71,12 @@ botan_rsa_private_key_t *botan_rsa_private_key_gen(key_type_t type, botan_rsa_private_key_t *botan_rsa_private_key_load(key_type_t type, va_list args); +/** + * Load a RSA private key by adopting a botan_privkey_t object. + * + * @param key private key object (adopted) + * @return loaded key, NULL on failure + */ +botan_rsa_private_key_t *botan_rsa_private_key_adopt(botan_privkey_t key); + #endif /** BOTAN_RSA_PRIVATE_KEY_H_ @}*/ diff --git a/src/libstrongswan/plugins/botan/botan_rsa_public_key.c b/src/libstrongswan/plugins/botan/botan_rsa_public_key.c index 617fad5d1..6adb1c4b7 100644 --- a/src/libstrongswan/plugins/botan/botan_rsa_public_key.c +++ b/src/libstrongswan/plugins/botan/botan_rsa_public_key.c @@ -1,4 +1,7 @@ /* + * Copyright (C) 2018 Tobias Brunner + * HSR Hochschule fuer Technik Rapperswil + * * Copyright (C) 2018 René Korthaus * Rohde & Schwarz Cybersecurity GmbH * @@ -316,6 +319,19 @@ static private_botan_rsa_public_key_t *create_empty() return this; } +/* + * Described in header + */ +botan_rsa_public_key_t *botan_rsa_public_key_adopt(botan_pubkey_t key) +{ + private_botan_rsa_public_key_t *this; + + this = create_empty(); + this->key = key; + + return &this->public; +} + /* * Described in header */ @@ -323,16 +339,13 @@ botan_rsa_public_key_t *botan_rsa_public_key_load(key_type_t type, va_list args) { private_botan_rsa_public_key_t *this = NULL; - chunk_t blob, n, e; + chunk_t n, e; - n = e = blob = chunk_empty; + n = e = chunk_empty; while (TRUE) { switch (va_arg(args, builder_part_t)) { - case BUILD_BLOB_ASN1_DER: - blob = va_arg(args, chunk_t); - continue; case BUILD_RSA_MODULUS: n = va_arg(args, chunk_t); continue; @@ -347,54 +360,7 @@ botan_rsa_public_key_t *botan_rsa_public_key_load(key_type_t type, break; } - if (blob.ptr) - { - switch (type) - { - /* SubjectPublicKeyInfo */ - case KEY_RSA: - case KEY_ANY: - { - size_t namesize = 0; - char *namebuf; - - this = create_empty(); - - if (botan_pubkey_load(&this->key, blob.ptr, blob.len)) - { - free(this); - return NULL; - } - - if (botan_pubkey_algo_name(this->key, NULL, &namesize) - != BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE) - { - destroy(this); - return NULL; - } - - namebuf = malloc(namesize); - if (botan_pubkey_algo_name(this->key, namebuf, &namesize)) - { - free(namebuf); - destroy(this); - return NULL; - } - - if (!strneq(namebuf, "RSA", namesize)) - { - free(namebuf); - destroy(this); - return NULL; - } - free(namebuf); - break; - } - default: - return NULL; - } - } - else if (n.ptr && e.ptr && type == KEY_RSA) + if (n.ptr && e.ptr && type == KEY_RSA) { botan_mp_t mp_n, mp_e; diff --git a/src/libstrongswan/plugins/botan/botan_rsa_public_key.h b/src/libstrongswan/plugins/botan/botan_rsa_public_key.h index 4f6be8333..1d80df9ff 100644 --- a/src/libstrongswan/plugins/botan/botan_rsa_public_key.h +++ b/src/libstrongswan/plugins/botan/botan_rsa_public_key.h @@ -1,4 +1,7 @@ /* + * Copyright (C) 2018 Tobias Brunner + * HSR Hochschule fuer Technik Rapperswil + * * Copyright (C) 2018 René Korthaus * Rohde & Schwarz Cybersecurity GmbH * @@ -29,10 +32,12 @@ #ifndef BOTAN_RSA_PUBLIC_KEY_H_ #define BOTAN_RSA_PUBLIC_KEY_H_ -typedef struct botan_rsa_public_key_t botan_rsa_public_key_t; +#include #include +typedef struct botan_rsa_public_key_t botan_rsa_public_key_t; + /** * public_key_t implementation of RSA algorithm using Botan. */ @@ -47,7 +52,7 @@ struct botan_rsa_public_key_t { /** * Load a RSA public key using Botan. * - * Accepts a BUILD_BLOB_ASN1_DER argument. + * Accepts a BUILD_RSA_MODULUS/BUILD_RSA_PUB_EXP arguments. * * @param type type of the key, must be KEY_RSA * @param args builder_part_t argument list @@ -56,4 +61,12 @@ struct botan_rsa_public_key_t { botan_rsa_public_key_t *botan_rsa_public_key_load(key_type_t type, va_list args); +/** + * Load a RSA public key by adopting a botan_pubkey_t object. + * + * @param key public key object (adopted) + * @return loaded key, NULL on failure + */ +botan_rsa_public_key_t *botan_rsa_public_key_adopt(botan_pubkey_t key); + #endif /** BOTAN_RSA_PUBLIC_KEY_H_ @}*/ diff --git a/src/libstrongswan/plugins/botan/botan_util_keys.c b/src/libstrongswan/plugins/botan/botan_util_keys.c new file mode 100644 index 000000000..176c2caf9 --- /dev/null +++ b/src/libstrongswan/plugins/botan/botan_util_keys.c @@ -0,0 +1,211 @@ +/* + * Copyright (C) 2018 Tobias Brunner + * HSR Hochschule fuer Technik Rapperswil + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "botan_util_keys.h" +#include "botan_ec_public_key.h" +#include "botan_ec_private_key.h" +#include "botan_rsa_public_key.h" +#include "botan_rsa_private_key.h" + +#include +#include + +/** + * Get the algorithm name of a public key + */ +static char *get_algo_name(botan_pubkey_t pubkey) +{ + char *name; + size_t len = 0; + + if (botan_pubkey_algo_name(pubkey, NULL, &len) + != BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE) + { + return NULL; + } + + name = malloc(len); + if (botan_pubkey_algo_name(pubkey, name, &len)) + { + free(name); + return NULL; + } + return name; +} + +/* + * Described in header + */ +public_key_t *botan_public_key_load(key_type_t type, va_list args) +{ + public_key_t *this = NULL; + botan_pubkey_t pubkey; + chunk_t blob = chunk_empty; + botan_rng_t rng; + char *name; + + while (TRUE) + { + switch (va_arg(args, builder_part_t)) + { + case BUILD_BLOB_ASN1_DER: + blob = va_arg(args, chunk_t); + continue; + case BUILD_END: + break; + default: + return NULL; + } + break; + } + + if (botan_rng_init(&rng, "user")) + { + return NULL; + } + if (botan_pubkey_load(&pubkey, blob.ptr, blob.len)) + { + botan_rng_destroy(rng); + return NULL; + } + if (botan_pubkey_check_key(pubkey, rng, BOTAN_CHECK_KEY_EXPENSIVE_TESTS)) + { + DBG1(DBG_LIB, "public key failed key checks"); + botan_pubkey_destroy(pubkey); + botan_rng_destroy(rng); + return NULL; + } + botan_rng_destroy(rng); + + name = get_algo_name(pubkey); + if (!name) + { + botan_pubkey_destroy(pubkey); + return NULL; + } + + if (streq(name, "RSA") && (type == KEY_ANY || type == KEY_RSA)) + { + this = (public_key_t*)botan_rsa_public_key_adopt(pubkey); + } + else if (streq(name, "ECDSA") && (type == KEY_ANY || type == KEY_ECDSA)) + { + this = (public_key_t*)botan_ec_public_key_adopt(pubkey); + } + else + { + botan_pubkey_destroy(pubkey); + } + free(name); + return this; +} + +/** + * Determine the curve OID from a PKCS#8 structure + */ +static int determine_ec_oid(chunk_t pkcs8) +{ + int oid = OID_UNKNOWN; + chunk_t inner, params = chunk_empty; + + if (asn1_unwrap(&pkcs8, &pkcs8) == ASN1_SEQUENCE && + asn1_unwrap(&pkcs8, &inner) == ASN1_INTEGER && + asn1_parse_integer_uint64(inner) == 0 && + asn1_parse_algorithmIdentifier(pkcs8, 0, ¶ms) == OID_EC_PUBLICKEY && + params.len && + asn1_unwrap(¶ms, ¶ms) == ASN1_OID) + { + oid = asn1_known_oid(params); + } + return oid; +} + +/* + * Described in header + */ +private_key_t *botan_private_key_load(key_type_t type, va_list args) +{ + private_key_t *this = NULL; + botan_privkey_t key; + botan_pubkey_t pubkey; + chunk_t blob = chunk_empty; + botan_rng_t rng; + char *name; + int oid; + + while (TRUE) + { + switch (va_arg(args, builder_part_t)) + { + case BUILD_BLOB_ASN1_DER: + blob = va_arg(args, chunk_t); + continue; + case BUILD_END: + break; + default: + return NULL; + } + break; + } + + if (botan_rng_init(&rng, "user")) + { + return NULL; + } + if (botan_privkey_load(&key, rng, blob.ptr, blob.len, NULL)) + { + botan_rng_destroy(rng); + return NULL; + } + botan_rng_destroy(rng); + + if (botan_privkey_export_pubkey(&pubkey, key)) + { + botan_privkey_destroy(key); + return NULL; + } + name = get_algo_name(pubkey); + botan_pubkey_destroy(pubkey); + if (!name) + { + return NULL; + } + if (streq(name, "RSA") && (type == KEY_ANY || type == KEY_RSA)) + { + this = (private_key_t*)botan_rsa_private_key_adopt(key); + } + else if (streq(name, "ECDSA") && (type == KEY_ANY || type == KEY_ECDSA)) + { + oid = determine_ec_oid(blob); + if (oid != OID_UNKNOWN) + { + this = (private_key_t*)botan_ec_private_key_adopt(key, oid); + } + } + if (!this) + { + botan_privkey_destroy(key); + } + free(name); + return this; +} diff --git a/src/libstrongswan/plugins/botan/botan_util_keys.h b/src/libstrongswan/plugins/botan/botan_util_keys.h new file mode 100644 index 000000000..f05f7ce5e --- /dev/null +++ b/src/libstrongswan/plugins/botan/botan_util_keys.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2018 Tobias Brunner + * HSR Hochschule fuer Technik Rapperswil + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/** + * Helper functions to load public and private keys in a generic way + * + * @defgroup botan_util_keys botan_util_keys + * @{ @ingroup botan_p + */ + +#ifndef BOTAN_UTIL_KEYS_H_ +#define BOTAN_UTIL_KEYS_H_ + +#include + +#include +#include + +/** + * Load a public key in subjectPublicKeyInfo encoding + * + * Accepts a BUILD_BLOB_ASN1_DER argument. + * + * @param type type of the key + * @param args builder_part_t argument list + * @return loaded key, NULL on failure + */ +public_key_t *botan_public_key_load(key_type_t type, va_list args); + +/** + * Load a private key in PKCS#8 encoding + * + * Accepts a BUILD_BLOB_ASN1_DER argument. + * + * @param type type of the key + * @param args builder_part_t argument list + * @return loaded key, NULL on failure + */ +private_key_t *botan_private_key_load(key_type_t type, va_list args); + +#endif /** BOTAN_UTIL_KEYS_H_ @}*/ From bd267c863f3a23f79694d309568e72569ff8a5be Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 10 Aug 2018 09:02:26 +0200 Subject: [PATCH 08/23] botan: Encode private keys as PKCS#8 Since we can now parse that encoding directly we can simplify the private key export and stick to PKCS#8. --- .../plugins/botan/botan_ec_private_key.c | 114 +----------------- .../plugins/botan/botan_rsa_private_key.c | 33 +---- src/libstrongswan/plugins/botan/botan_util.c | 33 +++++ src/libstrongswan/plugins/botan/botan_util.h | 11 ++ 4 files changed, 46 insertions(+), 145 deletions(-) diff --git a/src/libstrongswan/plugins/botan/botan_ec_private_key.c b/src/libstrongswan/plugins/botan/botan_ec_private_key.c index 78ba43f81..f8dbb66d7 100644 --- a/src/libstrongswan/plugins/botan/botan_ec_private_key.c +++ b/src/libstrongswan/plugins/botan/botan_ec_private_key.c @@ -209,123 +209,11 @@ METHOD(private_key_t, get_fingerprint, bool, return success; } -/** - * Get a field from the private key as chunk - */ -static bool get_field_chunk(botan_privkey_t key, char *field, chunk_t *out) -{ - botan_mp_t val; - - *out = chunk_empty; - - if (botan_mp_init(&val)) - { - return FALSE; - } - - if (botan_privkey_get_field(val, key, field) || - botan_mp_num_bytes(val, &out->len)) - { - botan_mp_destroy(val); - return FALSE; - } - - *out = chunk_alloc(out->len); - - if (botan_mp_to_bin(val, out->ptr)) - { - botan_mp_destroy(val); - chunk_free(out); - return FALSE; - } - botan_mp_destroy(val); - return TRUE; -} - -/** - * Encodes the public key as ASN.1 BIT STRING (0x04 || x || y) - */ -static bool get_pubkey_bitstring(botan_privkey_t key, chunk_t *out) -{ - chunk_t p, x, y, pub; - size_t len; - - if (!get_field_chunk(key, "p", &p)) - { - return FALSE; - } - len = p.len; - chunk_free(&p); - - if (!get_field_chunk(key, "public_x", &x)) - { - return FALSE; - } - if (!get_field_chunk(key, "public_y", &y)) - { - chunk_free(&x); - return FALSE; - } - - pub = chunk_alloca(2 * len); - memset(pub.ptr, 0, pub.len); - memcpy(pub.ptr + (len - x.len), x.ptr, x.len); - memcpy(pub.ptr + len + (len - y.len), y.ptr, y.len); - chunk_free(&x); - chunk_free(&y); - - *out = asn1_wrap(ASN1_BIT_STRING, "ccc", - /* unused bits in the bit string */ - chunk_from_chars(0x00), - /* uncompressed format */ - chunk_from_chars(0x04), pub); - return TRUE; -} - METHOD(private_key_t, get_encoding, bool, private_botan_ec_private_key_t *this, cred_encoding_type_t type, chunk_t *encoding) { - switch (type) - { - case PRIVKEY_ASN1_DER: - case PRIVKEY_PEM: - { - chunk_t priv, pub; - bool success = TRUE; - - if (!get_field_chunk(this->key, "x", &priv)) - { - return FALSE; - } - if (!get_pubkey_bitstring(this->key, &pub)) - { - chunk_clear(&priv); - return FALSE; - } - - *encoding = asn1_wrap(ASN1_SEQUENCE, "msmm", - asn1_integer("c", chunk_from_chars(0x01)), - asn1_wrap(ASN1_OCTET_STRING, "s", priv), - asn1_wrap(ASN1_CONTEXT_C_0, "m", - asn1_build_known_oid(this->oid)), - asn1_wrap(ASN1_CONTEXT_C_1, "m", pub)); - - if (type == PRIVKEY_PEM) - { - chunk_t asn1_encoding = *encoding; - - success = lib->encoding->encode(lib->encoding, PRIVKEY_PEM, - NULL, encoding, - CRED_PART_ECDSA_PRIV_ASN1_DER, - asn1_encoding, CRED_PART_END); - chunk_clear(&asn1_encoding); - } - return success; - } - default: - return FALSE; - } + return botan_get_privkey_encoding(this->key, type, encoding); } METHOD(private_key_t, get_ref, private_key_t*, diff --git a/src/libstrongswan/plugins/botan/botan_rsa_private_key.c b/src/libstrongswan/plugins/botan/botan_rsa_private_key.c index 8b2583b96..558bb70f7 100644 --- a/src/libstrongswan/plugins/botan/botan_rsa_private_key.c +++ b/src/libstrongswan/plugins/botan/botan_rsa_private_key.c @@ -257,38 +257,7 @@ METHOD(private_key_t, get_encoding, bool, private_botan_rsa_private_key_t *this, cred_encoding_type_t type, chunk_t *encoding) { - switch (type) - { - case PRIVKEY_ASN1_DER: - case PRIVKEY_PEM: - { - uint32_t format = BOTAN_PRIVKEY_EXPORT_FLAG_DER; - size_t len = 0; - bool success = TRUE; - - if (type == PRIVKEY_PEM) - { - format = BOTAN_PRIVKEY_EXPORT_FLAG_PEM; - } - - if (botan_privkey_rsa_get_privkey(this->key, NULL, &len, format) - != BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE) - { - return FALSE; - } - - *encoding = chunk_alloc(len); - if (botan_privkey_rsa_get_privkey(this->key, encoding->ptr, &len, - format)) - { - chunk_clear(encoding); - return FALSE; - } - return success; - } - default: - return FALSE; - } + return botan_get_privkey_encoding(this->key, type, encoding); } METHOD(private_key_t, get_ref, private_key_t*, diff --git a/src/libstrongswan/plugins/botan/botan_util.c b/src/libstrongswan/plugins/botan/botan_util.c index 9dcf592c5..860d376c3 100644 --- a/src/libstrongswan/plugins/botan/botan_util.c +++ b/src/libstrongswan/plugins/botan/botan_util.c @@ -108,6 +108,39 @@ bool botan_get_encoding(botan_pubkey_t pubkey, cred_encoding_type_t type, return success; } +/* + * Described in header + */ +bool botan_get_privkey_encoding(botan_privkey_t key, cred_encoding_type_t type, + chunk_t *encoding) +{ + uint32_t format = BOTAN_PRIVKEY_EXPORT_FLAG_DER; + + switch (type) + { + case PRIVKEY_PEM: + format = BOTAN_PRIVKEY_EXPORT_FLAG_PEM; + /* fall-through */ + case PRIVKEY_ASN1_DER: + encoding->len = 0; + if (botan_privkey_export(key, NULL, &encoding->len, format) + != BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE) + { + return FALSE; + } + *encoding = chunk_alloc(encoding->len); + if (botan_privkey_export(key, encoding->ptr, &encoding->len, + format)) + { + chunk_free(encoding); + return FALSE; + } + return TRUE; + default: + return FALSE; + } +} + /* * Described in header */ diff --git a/src/libstrongswan/plugins/botan/botan_util.h b/src/libstrongswan/plugins/botan/botan_util.h index 1f4b96f6e..2c6b1f816 100644 --- a/src/libstrongswan/plugins/botan/botan_util.h +++ b/src/libstrongswan/plugins/botan/botan_util.h @@ -64,6 +64,17 @@ const char *botan_get_hash(hash_algorithm_t hash); bool botan_get_encoding(botan_pubkey_t pubkey, cred_encoding_type_t type, chunk_t *encoding); +/** + * Get the encoding of a botan_privkey_t. + * + * @param key private key object + * @param type encoding type + * @param encoding allocated encoding + * @return TRUE if encoding successful + */ +bool botan_get_privkey_encoding(botan_privkey_t key, cred_encoding_type_t type, + chunk_t *encoding); + /** * Get the fingerprint of a botan_pubkey_t. * From 304d4ca57a4189b8cd21aec53da1abe4ee2e8a59 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 10 Aug 2018 17:04:09 +0200 Subject: [PATCH 09/23] botan: Adhere to configured DH exponent length --- src/libstrongswan/plugins/botan/botan_diffie_hellman.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/libstrongswan/plugins/botan/botan_diffie_hellman.c b/src/libstrongswan/plugins/botan/botan_diffie_hellman.c index 422b4aea1..008e15fbd 100644 --- a/src/libstrongswan/plugins/botan/botan_diffie_hellman.c +++ b/src/libstrongswan/plugins/botan/botan_diffie_hellman.c @@ -223,7 +223,7 @@ static botan_diffie_hellman_t *create_generic(diffie_hellman_group_t group, } rng = lib->crypto->create_rng(lib->crypto, RNG_STRONG); - if (!rng || !rng->allocate_bytes(rng, p.len, &random)) + if (!rng || !rng->allocate_bytes(rng, exp_len, &random)) { DESTROY_IF(rng); destroy(this); @@ -253,7 +253,7 @@ botan_diffie_hellman_t *botan_diffie_hellman_create( if (group == MODP_CUSTOM) { VA_ARGS_GET(group, g, p); - return create_generic(group, g, p); + return create_generic(group, g, p, p.len); } params = diffie_hellman_get_params(group); @@ -261,7 +261,8 @@ botan_diffie_hellman_t *botan_diffie_hellman_create( { return NULL; } - return create_generic(group, params->generator, params->prime); + return create_generic(group, params->generator, params->prime, + params->exp_len); } #endif From c064a5288ece8fb4f3780ac3a1810ac00871e4a6 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 8 Aug 2018 11:41:36 +0200 Subject: [PATCH 10/23] leak-detective: Whitelist some Botan functions Due to the mangled C++ function names it's tricky to be more specific. The "leaked" allocations are from a static hashtable containing EC groups. There is another leak caused by the locking allocator singleton (triggered by the first function that uses it, usually initialization of a cipher, but could be a hasher in other test runners), but we can avoid that with a Botan config option. --- src/libstrongswan/utils/leak_detective.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/libstrongswan/utils/leak_detective.c b/src/libstrongswan/utils/leak_detective.c index b873e12a8..08fcc0ef0 100644 --- a/src/libstrongswan/utils/leak_detective.c +++ b/src/libstrongswan/utils/leak_detective.c @@ -609,6 +609,11 @@ static char *whitelist[] = { /* FHH IMCs and IMVs */ "TNC_IMC_NotifyConnectionChange", "TNC_IMV_NotifyConnectionChange", + /* Botan */ + "botan_public_key_load", + "botan_privkey_create_ecdsa", + "botan_privkey_create_ecdh", + "botan_privkey_load_ecdh", }; /** From 9ee23d5efa099e05f4f98d71e7fab677f08dd88c Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 8 Aug 2018 11:35:46 +0200 Subject: [PATCH 11/23] travis: Add Botan build We build Botan directly from the master branch until 2.8.0 is released. --- .travis.yml | 2 ++ scripts/test.sh | 28 ++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/.travis.yml b/.travis.yml index 8e36f4ebe..d220950c7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -38,6 +38,8 @@ env: - TEST=default - TEST=default MONOLITHIC=yes - TEST=default LEAK_DETECTIVE=yes + - TEST=botan + - TEST=botan LEAK_DETECTIVE=yes - TEST=openssl - TEST=openssl LEAK_DETECTIVE=yes - TEST=gcrypt diff --git a/scripts/test.sh b/scripts/test.sh index 58dc7307e..a2ec74a4b 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -26,6 +26,32 @@ gcrypt) CONFIG="--disable-defaults --enable-pki --enable-gcrypt --enable-pkcs1" DEPS="libgcrypt11-dev" ;; +botan) + CONFIG="--disable-defaults --enable-pki --enable-botan" + # we can't use the old package that comes with Ubuntu so we build from + # the current master until 2.8.0 is released and then probably switch to + # that unless we need newer features (at least 2.7.0 plus PKCS#1 patch is + # currently required) + DEPS="" + if test "$1" = "deps"; then + # if the leak detective is enabled we have to disable threading support + # (used for std::async) as that causes invalid frees somehow, the + # locking allocator causes a static leak via the first function that + # references it (e.g. crypter or hasher), so we disable that too + if test "$LEAK_DETECTIVE" = "yes"; then + BOTAN_CONFIG="--without-os-features=threads + --disable-modules=locking_allocator" + fi + # disable some larger modules we don't need for the tests + BOTAN_CONFIG="$BOTAN_CONFIG --disable-modules=pkcs11,tls,x509,xmss" + git clone --depth 1 https://github.com/randombit/botan.git botan && + cd botan && + python ./configure.py $BOTAN_CONFIG && + make -j4 libs >/dev/null && + sudo make install >/dev/null && + sudo ldconfig || exit $? + fi + ;; printf-builtin) CONFIG="--with-printf-hooks=builtin" ;; @@ -43,6 +69,8 @@ all|coverage|sonarcloud) CONFIG="$CONFIG --disable-nm" # not enabled on the build server CONFIG="$CONFIG --disable-af-alg" + # separate test case with external dependency + CONFIG="$CONFIG --disable-botan" if test "$TEST" != "coverage"; then CONFIG="$CONFIG --disable-coverage" else From 472efd38095dbcba063e45c2be7791c879bb9ca5 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 8 Aug 2018 17:06:15 +0200 Subject: [PATCH 12/23] leak-detective: Add an option to ignore frees of unknown memory blocks This also changes how unknown/corrupted memory is handled in the free() and realloc() hooks in general. Incorporates changes provided by Thomas Egerer who ran into a similar issue. --- src/libstrongswan/utils/leak_detective.c | 127 ++++++++++++++++++----- 1 file changed, 101 insertions(+), 26 deletions(-) diff --git a/src/libstrongswan/utils/leak_detective.c b/src/libstrongswan/utils/leak_detective.c index 08fcc0ef0..efeb0f478 100644 --- a/src/libstrongswan/utils/leak_detective.c +++ b/src/libstrongswan/utils/leak_detective.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2014 Tobias Brunner + * Copyright (C) 2013-2018 Tobias Brunner * Copyright (C) 2006-2013 Martin Willi * HSR Hochschule fuer Technik Rapperswil * @@ -162,7 +162,12 @@ static spinlock_t *lock; /** * Is leak detection currently enabled? */ -static bool enabled = FALSE; +static bool enabled; + +/** + * Whether to report calls to free() with memory not allocated by us + */ +static bool ignore_unknown; /** * Is leak detection disabled for the current thread? @@ -888,7 +893,7 @@ HOOK(void, free, void *ptr) return; } /* allow freeing of NULL */ - if (ptr == NULL) + if (!ptr) { return; } @@ -899,21 +904,47 @@ HOOK(void, free, void *ptr) if (hdr->magic != MEMORY_HEADER_MAGIC || tail->magic != MEMORY_TAIL_MAGIC) { + bool bt = TRUE; + + /* check if memory appears to be allocated by our hooks */ if (has_hdr(hdr)) { - /* memory was allocated by our hooks but is corrupted */ fprintf(stderr, "freeing corrupted memory (%p): " - "header magic 0x%x, tail magic 0x%x:\n", - ptr, hdr->magic, tail->magic); + "%u bytes, header magic 0x%x, tail magic 0x%x:\n", + ptr, hdr->bytes, hdr->magic, tail->magic); + remove_hdr(hdr); + + if (hdr->magic == MEMORY_HEADER_MAGIC) + { /* only access the old backtrace if header magic is valid */ + hdr->backtrace->log(hdr->backtrace, stderr, TRUE); + hdr->backtrace->destroy(hdr->backtrace); + } + else + { + fprintf(stderr, " header magic invalid, ignore backtrace of " + "allocation\n"); + } } else { - /* memory was not allocated by our hooks */ - fprintf(stderr, "freeing invalid memory (%p)\n", ptr); + /* just free this block of unknown memory */ + hdr = ptr; + + if (ignore_unknown) + { + bt = FALSE; + } + else + { + fprintf(stderr, "freeing unknown memory (%p):\n", ptr); + } + } + if (bt) + { + backtrace = backtrace_create(2); + backtrace->log(backtrace, stderr, TRUE); + backtrace->destroy(backtrace); } - backtrace = backtrace_create(2); - backtrace->log(backtrace, stderr, TRUE); - backtrace->destroy(backtrace); } else { @@ -921,12 +952,11 @@ HOOK(void, free, void *ptr) hdr->backtrace->destroy(hdr->backtrace); - /* clear MAGIC, set mem to something remarkable */ + /* set mem to something remarkable */ memset(hdr, MEMORY_FREE_PATTERN, sizeof(memory_header_t) + hdr->bytes + sizeof(memory_tail_t)); - - real_free(hdr); } + real_free(hdr); enable_thread(before); } @@ -938,19 +968,19 @@ HOOK(void*, realloc, void *old, size_t bytes) memory_header_t *hdr; memory_tail_t *tail; backtrace_t *backtrace; - bool before; + bool before, have_backtrace = TRUE; if (!enabled || thread_disabled->get(thread_disabled)) { return real_realloc(old, bytes); } /* allow reallocation of NULL */ - if (old == NULL) + if (!old) { return malloc(bytes); } /* handle zero size as a free() */ - if (bytes == 0) + if (!bytes) { free(old); return NULL; @@ -959,22 +989,64 @@ HOOK(void*, realloc, void *old, size_t bytes) hdr = old - sizeof(memory_header_t); tail = old + hdr->bytes; - remove_hdr(hdr); - + before = enable_thread(FALSE); if (hdr->magic != MEMORY_HEADER_MAGIC || tail->magic != MEMORY_TAIL_MAGIC) { - fprintf(stderr, "reallocating invalid memory (%p):\n" - "header magic 0x%x:\n", old, hdr->magic); - backtrace = backtrace_create(2); - backtrace->log(backtrace, stderr, TRUE); - backtrace->destroy(backtrace); + bool bt = TRUE; + + /* check if memory appears to be allocated by our hooks */ + if (has_hdr(hdr)) + { + fprintf(stderr, "reallocating corrupted memory (%p, %u bytes): " + "%zu bytes, header magic 0x%x, tail magic 0x%x:\n", + old, hdr->bytes, bytes, hdr->magic, tail->magic); + remove_hdr(hdr); + + if (hdr->magic == MEMORY_HEADER_MAGIC) + { /* only access header fields (backtrace, bytes) if header magic + * is still valid */ + hdr->backtrace->log(hdr->backtrace, stderr, TRUE); + memset(&tail->magic, MEMORY_ALLOC_PATTERN, sizeof(tail->magic)); + } + else + { + fprintf(stderr, " header magic invalid, ignore backtrace of " + "allocation\n"); + have_backtrace = FALSE; + hdr->magic = MEMORY_HEADER_MAGIC; + } + } + else + { + /* adopt this block of unknown memory */ + hdr = old; + have_backtrace = FALSE; + + if (ignore_unknown) + { + bt = FALSE; + } + else + { + fprintf(stderr, "reallocating unknown memory (%p): %zu bytes:\n", + old, bytes); + } + } + if (bt) + { + backtrace = backtrace_create(2); + backtrace->log(backtrace, stderr, TRUE); + backtrace->destroy(backtrace); + } } else { + remove_hdr(hdr); /* clear tail magic, allocate, set tail magic */ memset(&tail->magic, MEMORY_ALLOC_PATTERN, sizeof(tail->magic)); } + hdr = real_realloc(hdr, sizeof(memory_header_t) + bytes + sizeof(memory_tail_t)); tail = ((void*)hdr) + bytes + sizeof(memory_header_t); @@ -983,8 +1055,10 @@ HOOK(void*, realloc, void *old, size_t bytes) /* update statistics */ hdr->bytes = bytes; - before = enable_thread(FALSE); - hdr->backtrace->destroy(hdr->backtrace); + if (have_backtrace) + { + hdr->backtrace->destroy(hdr->backtrace); + } hdr->backtrace = backtrace_create(2); enable_thread(before); @@ -1027,6 +1101,7 @@ leak_detective_t *leak_detective_create() free(this); return NULL; } + ignore_unknown = getenv("LEAK_DETECTIVE_IGNORE_UNKNOWN") != NULL; lock = spinlock_create(); thread_disabled = thread_value_create(NULL); From e5e500c07effe05916abdd333f45b62005300cc9 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Thu, 30 Aug 2018 14:48:34 +0200 Subject: [PATCH 13/23] ike-init: Fix leak if KE payload creation fails --- src/libcharon/sa/ikev2/tasks/ike_init.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libcharon/sa/ikev2/tasks/ike_init.c b/src/libcharon/sa/ikev2/tasks/ike_init.c index 28e28e410..307d99264 100644 --- a/src/libcharon/sa/ikev2/tasks/ike_init.c +++ b/src/libcharon/sa/ikev2/tasks/ike_init.c @@ -362,8 +362,6 @@ static bool build_payloads(private_ike_init_t *this, message_t *message) } message->add_payload(message, (payload_t*)sa_payload); - nonce_payload = nonce_payload_create(PLV2_NONCE); - nonce_payload->set_nonce(nonce_payload, this->my_nonce); ke_payload = ke_payload_create_from_diffie_hellman(PLV2_KEY_EXCHANGE, this->dh); if (!ke_payload) @@ -371,6 +369,8 @@ static bool build_payloads(private_ike_init_t *this, message_t *message) DBG1(DBG_IKE, "creating KE payload failed"); return FALSE; } + nonce_payload = nonce_payload_create(PLV2_NONCE); + nonce_payload->set_nonce(nonce_payload, this->my_nonce); if (this->old_sa) { /* payload order differs if we are rekeying */ From 125222dca76318a49d76685928701e01c8e36ac7 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Tue, 11 Sep 2018 10:32:50 +0200 Subject: [PATCH 14/23] test-vectors: Add the actual test vector from RFC 8031 for x25519 The existing test vector is from RFC 8037. --- .../plugins/test_vectors/test_vectors.h | 3 ++- .../test_vectors/test_vectors/curve25519.c | 22 ++++++++++++++++--- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/src/libstrongswan/plugins/test_vectors/test_vectors.h b/src/libstrongswan/plugins/test_vectors/test_vectors.h index 9bbe701ee..7ab965a82 100644 --- a/src/libstrongswan/plugins/test_vectors/test_vectors.h +++ b/src/libstrongswan/plugins/test_vectors/test_vectors.h @@ -303,4 +303,5 @@ TEST_VECTOR_DH(ecp224bp) TEST_VECTOR_DH(ecp256bp) TEST_VECTOR_DH(ecp384bp) TEST_VECTOR_DH(ecp512bp) -TEST_VECTOR_DH(curve25519) +TEST_VECTOR_DH(curve25519_1) +TEST_VECTOR_DH(curve25519_2) diff --git a/src/libstrongswan/plugins/test_vectors/test_vectors/curve25519.c b/src/libstrongswan/plugins/test_vectors/test_vectors/curve25519.c index f46d81c16..676fcfc5a 100644 --- a/src/libstrongswan/plugins/test_vectors/test_vectors/curve25519.c +++ b/src/libstrongswan/plugins/test_vectors/test_vectors/curve25519.c @@ -16,10 +16,9 @@ #include /** - * From RFC 8031 + * From RFC 8037 */ - -dh_test_vector_t curve25519 = { +dh_test_vector_t curve25519_1 = { .group = CURVE_25519, .priv_len = 32, .pub_len = 32, .shared_len = 32, .priv_a = "\x77\x07\x6d\x0a\x73\x18\xa5\x7d\x3c\x16\xc1\x72\x51\xb2\x66\x45" "\xdf\x4c\x2f\x87\xeb\xc0\x99\x2a\xb1\x77\xfb\xa5\x1d\xb9\x2c\x2a", @@ -32,3 +31,20 @@ dh_test_vector_t curve25519 = { .shared = "\x4a\x5d\x9d\x5b\xa4\xce\x2d\xe1\x72\x8e\x3b\xf4\x80\x35\x0f\x25" "\xe0\x7e\x21\xc9\x47\xd1\x9e\x33\x76\xf0\x9b\x3c\x1e\x16\x17\x42", }; + +/** + * From RFC 8031 + */ +dh_test_vector_t curve25519_2 = { + .group = CURVE_25519, .priv_len = 32, .pub_len = 32, .shared_len = 32, + .priv_a = "\x75\x1f\xb4\x30\x86\x55\xb4\x76\xb6\x78\x9b\x73\x25\xf9\xea\x8c" + "\xdd\xd1\x6a\x58\x53\x3f\xf6\xd9\xe6\x00\x09\x46\x4a\x5f\x9d\x94", + .priv_b = "\x0a\x54\x64\x52\x53\x29\x0d\x60\xdd\xad\xd0\xe0\x30\xba\xcd\x9e" + "\x55\x01\xef\xdc\x22\x07\x55\xa1\xe9\x78\xf1\xb8\x39\xa0\x56\x88", + .pub_a = "\x48\xd5\xdd\xd4\x06\x12\x57\xba\x16\x6f\xa3\xf9\xbb\xdb\x74\xf1" + "\xa4\xe8\x1c\x08\x93\x84\xfa\x77\xf7\x90\x70\x9f\x0d\xfb\xc7\x66", + .pub_b = "\x0b\xe7\xc1\xf5\xaa\xd8\x7d\x7e\x44\x86\x62\x67\x32\x98\xa4\x43" + "\x47\x8b\x85\x97\x45\x17\x9e\xaf\x56\x4c\x79\xc0\xef\x6e\xee\x25", + .shared = "\xc7\x49\x50\x60\x7a\x12\x32\x7f\x32\x04\xd9\x4b\x68\x25\xbf\xb0" + "\x68\xb7\xf8\x31\x9a\x9e\x37\x08\xed\x3d\x43\xce\x81\x30\xc9\x50", +}; From 40b3bf6ba72d18a73926f3d5266aee6c1195af7a Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Tue, 11 Sep 2018 10:58:42 +0200 Subject: [PATCH 15/23] botan: Simplify DH/ECDH key derivation --- .../plugins/botan/botan_diffie_hellman.c | 25 +--------------- .../plugins/botan/botan_ec_diffie_hellman.c | 25 +--------------- src/libstrongswan/plugins/botan/botan_util.c | 30 +++++++++++++++++++ src/libstrongswan/plugins/botan/botan_util.h | 13 ++++++++ 4 files changed, 45 insertions(+), 48 deletions(-) diff --git a/src/libstrongswan/plugins/botan/botan_diffie_hellman.c b/src/libstrongswan/plugins/botan/botan_diffie_hellman.c index 008e15fbd..a55711d1b 100644 --- a/src/libstrongswan/plugins/botan/botan_diffie_hellman.c +++ b/src/libstrongswan/plugins/botan/botan_diffie_hellman.c @@ -97,37 +97,14 @@ bool load_private_key(private_botan_diffie_hellman_t *this, chunk_t value) METHOD(diffie_hellman_t, set_other_public_value, bool, private_botan_diffie_hellman_t *this, chunk_t value) { - botan_pk_op_ka_t op; - if (!diffie_hellman_verify_value(this->group, value)) { return FALSE; } - if (botan_pk_op_key_agreement_create(&op, this->dh_key, "Raw", 0)) - { - return FALSE; - } - chunk_clear(&this->shared_secret); - if (botan_pk_op_key_agreement_size(op, &this->shared_secret.len)) - { - botan_pk_op_key_agreement_destroy(op); - return FALSE; - } - - this->shared_secret = chunk_alloc(this->shared_secret.len); - if (botan_pk_op_key_agreement(op, this->shared_secret.ptr, - &this->shared_secret.len, value.ptr, - value.len, NULL, 0)) - { - chunk_clear(&this->shared_secret); - botan_pk_op_key_agreement_destroy(op); - return FALSE; - } - botan_pk_op_key_agreement_destroy(op); - return TRUE; + return botan_dh_key_derivation(this->dh_key, value, &this->shared_secret); } METHOD(diffie_hellman_t, get_my_public_value, bool, diff --git a/src/libstrongswan/plugins/botan/botan_ec_diffie_hellman.c b/src/libstrongswan/plugins/botan/botan_ec_diffie_hellman.c index a482bc028..ed28b4639 100644 --- a/src/libstrongswan/plugins/botan/botan_ec_diffie_hellman.c +++ b/src/libstrongswan/plugins/botan/botan_ec_diffie_hellman.c @@ -69,40 +69,17 @@ struct private_botan_ec_diffie_hellman_t { METHOD(diffie_hellman_t, set_other_public_value, bool, private_botan_ec_diffie_hellman_t *this, chunk_t value) { - botan_pk_op_ka_t ka; - if (!diffie_hellman_verify_value(this->group, value)) { return FALSE; } - if (botan_pk_op_key_agreement_create(&ka, this->key, "Raw", 0)) - { - return FALSE; - } - chunk_clear(&this->shared_secret); - if (botan_pk_op_key_agreement_size(ka, &this->shared_secret.len)) - { - botan_pk_op_key_agreement_destroy(ka); - return FALSE; - } - /* prepend 0x04 to indicate uncompressed point format */ value = chunk_cata("cc", chunk_from_chars(0x04), value); - this->shared_secret = chunk_alloc(this->shared_secret.len); - if (botan_pk_op_key_agreement(ka, this->shared_secret.ptr, - &this->shared_secret.len, value.ptr, - value.len, NULL, 0)) - { - chunk_clear(&this->shared_secret); - botan_pk_op_key_agreement_destroy(ka); - return FALSE; - } - botan_pk_op_key_agreement_destroy(ka); - return TRUE; + return botan_dh_key_derivation(this->key, value, &this->shared_secret); } METHOD(diffie_hellman_t, get_my_public_value, bool, diff --git a/src/libstrongswan/plugins/botan/botan_util.c b/src/libstrongswan/plugins/botan/botan_util.c index 860d376c3..a1d352950 100644 --- a/src/libstrongswan/plugins/botan/botan_util.c +++ b/src/libstrongswan/plugins/botan/botan_util.c @@ -259,3 +259,33 @@ bool botan_get_signature(botan_privkey_t key, const char *scheme, botan_pk_op_sign_destroy(sign_op); return TRUE; } + +/* + * Described in header + */ +bool botan_dh_key_derivation(botan_privkey_t key, chunk_t pub, chunk_t *secret) +{ + botan_pk_op_ka_t ka; + + if (botan_pk_op_key_agreement_create(&ka, key, "Raw", 0)) + { + return FALSE; + } + + if (botan_pk_op_key_agreement_size(ka, &secret->len)) + { + botan_pk_op_key_agreement_destroy(ka); + return FALSE; + } + + *secret = chunk_alloc(secret->len); + if (botan_pk_op_key_agreement(ka, secret->ptr, &secret->len, pub.ptr, + pub.len, NULL, 0)) + { + chunk_clear(secret); + botan_pk_op_key_agreement_destroy(ka); + return FALSE; + } + botan_pk_op_key_agreement_destroy(ka); + return TRUE; +} diff --git a/src/libstrongswan/plugins/botan/botan_util.h b/src/libstrongswan/plugins/botan/botan_util.h index 2c6b1f816..08830356e 100644 --- a/src/libstrongswan/plugins/botan/botan_util.h +++ b/src/libstrongswan/plugins/botan/botan_util.h @@ -100,4 +100,17 @@ bool botan_get_fingerprint(botan_pubkey_t pubkey, void *cache, bool botan_get_signature(botan_privkey_t key, const char *scheme, chunk_t data, chunk_t *signature); +/** + * Do the Diffie-Hellman key derivation using the given private key and public + * value. + * + * Note that the public value is not verified in this function. + * + * @param key DH private key + * @param pub other's public value + * @param secret the derived secret (allocated on success) + * @return TRUE if derivation was successful + */ +bool botan_dh_key_derivation(botan_privkey_t key, chunk_t pub, chunk_t *secret); + #endif /** BOTAN_UTIL_H_ @}*/ From b4062c4bb7a6835838f5212a75c8e98699517098 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Tue, 11 Sep 2018 11:05:21 +0200 Subject: [PATCH 16/23] botan: Add support for X25519 --- src/libstrongswan/plugins/botan/Makefile.am | 3 +- .../plugins/botan/botan_plugin.c | 6 + .../plugins/botan/botan_x25519.c | 176 ++++++++++++++++++ .../plugins/botan/botan_x25519.h | 42 +++++ 4 files changed, 226 insertions(+), 1 deletion(-) create mode 100644 src/libstrongswan/plugins/botan/botan_x25519.c create mode 100644 src/libstrongswan/plugins/botan/botan_x25519.h diff --git a/src/libstrongswan/plugins/botan/Makefile.am b/src/libstrongswan/plugins/botan/Makefile.am index 1188841d3..c1160145a 100644 --- a/src/libstrongswan/plugins/botan/Makefile.am +++ b/src/libstrongswan/plugins/botan/Makefile.am @@ -25,7 +25,8 @@ libstrongswan_botan_la_SOURCES = \ botan_ec_private_key.h botan_ec_private_key.c \ botan_util.h botan_util.c \ botan_util_keys.h botan_util_keys.c \ - botan_gcm.h botan_gcm.c + botan_gcm.h botan_gcm.c \ + botan_x25519.h botan_x25519.c libstrongswan_botan_la_LDFLAGS = -module -avoid-version libstrongswan_botan_la_LIBADD = $(botan_LIBS) diff --git a/src/libstrongswan/plugins/botan/botan_plugin.c b/src/libstrongswan/plugins/botan/botan_plugin.c index b387a2bd1..fd8e5f5a6 100644 --- a/src/libstrongswan/plugins/botan/botan_plugin.c +++ b/src/libstrongswan/plugins/botan/botan_plugin.c @@ -38,6 +38,7 @@ #include "botan_ec_private_key.h" #include "botan_gcm.h" #include "botan_util_keys.h" +#include "botan_x25519.h" #include @@ -94,6 +95,11 @@ METHOD(plugin_t, get_features, int, PLUGIN_PROVIDE(DH, ECP_384_BP), PLUGIN_PROVIDE(DH, ECP_512_BP), #endif +#ifdef BOTAN_HAS_X25519 + PLUGIN_REGISTER(DH, botan_x25519_create), + PLUGIN_PROVIDE(DH, CURVE_25519), +#endif + /* crypters */ PLUGIN_REGISTER(CRYPTER, botan_crypter_create), #ifdef BOTAN_HAS_AES diff --git a/src/libstrongswan/plugins/botan/botan_x25519.c b/src/libstrongswan/plugins/botan/botan_x25519.c new file mode 100644 index 000000000..519f29f55 --- /dev/null +++ b/src/libstrongswan/plugins/botan/botan_x25519.c @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2018 Tobias Brunner + * HSR Hochschule fuer Technik Rapperswil + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "botan_x25519.h" + +#include + +#ifdef BOTAN_HAS_X25519 + +#include "botan_util.h" + +#include + +#include + +typedef struct private_diffie_hellman_t private_diffie_hellman_t; + +/** + * Private data + */ +struct private_diffie_hellman_t { + + /** + * Public interface + */ + diffie_hellman_t public; + + /** + * Private key + */ + botan_privkey_t key; + + /** + * Shared secret + */ + chunk_t shared_secret; +}; + +METHOD(diffie_hellman_t, set_other_public_value, bool, + private_diffie_hellman_t *this, chunk_t value) +{ + if (!diffie_hellman_verify_value(CURVE_25519, value)) + { + return FALSE; + } + + chunk_clear(&this->shared_secret); + + return botan_dh_key_derivation(this->key, value, &this->shared_secret); +} + +METHOD(diffie_hellman_t, get_my_public_value, bool, + private_diffie_hellman_t *this, chunk_t *value) +{ + value->len = 0; + if (botan_pk_op_key_agreement_export_public(this->key, NULL, &value->len) + != BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE) + { + return FALSE; + } + + *value = chunk_alloc(value->len); + if (botan_pk_op_key_agreement_export_public(this->key, value->ptr, + &value->len)) + { + chunk_free(value); + return FALSE; + } + return TRUE; +} + +METHOD(diffie_hellman_t, set_private_value, bool, + private_diffie_hellman_t *this, chunk_t value) +{ + if (value.len != 32) + { + return FALSE; + } + + chunk_clear(&this->shared_secret); + + if (botan_privkey_destroy(this->key)) + { + return FALSE; + } + + if (botan_privkey_load_x25519(&this->key, value.ptr)) + { + return FALSE; + } + return TRUE; +} + +METHOD(diffie_hellman_t, get_shared_secret, bool, + private_diffie_hellman_t *this, chunk_t *secret) +{ + if (!this->shared_secret.len) + { + return FALSE; + } + *secret = chunk_clone(this->shared_secret); + return TRUE; +} + +METHOD(diffie_hellman_t, get_dh_group, diffie_hellman_group_t, + private_diffie_hellman_t *this) +{ + return CURVE_25519; +} + +METHOD(diffie_hellman_t, destroy, void, + private_diffie_hellman_t *this) +{ + botan_privkey_destroy(this->key); + chunk_clear(&this->shared_secret); + free(this); +} + +/* + * Described in header + */ +diffie_hellman_t *botan_x25519_create(diffie_hellman_group_t group) +{ + private_diffie_hellman_t *this; + botan_rng_t rng; + + INIT(this, + .public = { + .get_shared_secret = _get_shared_secret, + .set_other_public_value = _set_other_public_value, + .get_my_public_value = _get_my_public_value, + .set_private_value = _set_private_value, + .get_dh_group = _get_dh_group, + .destroy = _destroy, + }, + ); + + if (botan_rng_init(&rng, "user")) + { + free(this); + return NULL; + } + + if (botan_privkey_create_ecdh(&this->key, rng, "curve25519")) + { + DBG1(DBG_LIB, "x25519 private key generation failed"); + botan_rng_destroy(rng); + free(this); + return NULL; + } + + botan_rng_destroy(rng); + return &this->public; +} + +#endif diff --git a/src/libstrongswan/plugins/botan/botan_x25519.h b/src/libstrongswan/plugins/botan/botan_x25519.h new file mode 100644 index 000000000..e95d6cde4 --- /dev/null +++ b/src/libstrongswan/plugins/botan/botan_x25519.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2018 Tobias Brunner + * HSR Hochschule fuer Technik Rapperswil + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/** + * @defgroup botan_x25519 botan_x25519 + * @{ @ingroup botan_p + */ + +#ifndef BOTAN_X25519_H_ +#define BOTAN_X25519_H_ + +#include + +/** + * Creates a new X25519 implementation using Botan. + * + * @param group DH group, must be CURVE_25519 + * @return object, NULL if not supported + */ +diffie_hellman_t *botan_x25519_create(diffie_hellman_group_t group); + +#endif /** BOTAN_X25519_H_ @}*/ From a5c682e87da80bc6d32e22d650326b71363d581c Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Thu, 30 Aug 2018 17:47:43 +0200 Subject: [PATCH 17/23] testing: Enable Botan and the plugin ldconfig is required, otherwise the library won't be found by strongSwan in the same session. Should later be changed to 2.8.0 or a newer stable release. --- testing/scripts/recipes/011_botan.mk | 30 +++++++++++++++++++++++ testing/scripts/recipes/013_strongswan.mk | 1 + 2 files changed, 31 insertions(+) create mode 100644 testing/scripts/recipes/011_botan.mk diff --git a/testing/scripts/recipes/011_botan.mk b/testing/scripts/recipes/011_botan.mk new file mode 100644 index 000000000..ef0f6d066 --- /dev/null +++ b/testing/scripts/recipes/011_botan.mk @@ -0,0 +1,30 @@ +#!/usr/bin/make + +PKG = botan +SRC = https://github.com/randombit/$(PKG).git +# will have to be changed to the 2.8.0 tag later +REV = 1872f899716854927ecc68022fac318735be8824 + +NUM_CPUS := $(shell getconf _NPROCESSORS_ONLN) + +# the first two are necessary due to LD, the others to reduce the build time +CONFIG_OPTS = \ + --without-os-features=threads \ + --disable-modules=locking_allocator \ + --disable-modules=pkcs11,tls,x509,xmss \ + +all: install + +$(PKG): + git clone $(SRC) $(PKG) + +.$(PKG)-cloned-$(REV): $(PKG) + cd $(PKG) && git fetch && git checkout $(REV) + @touch $@ + +.$(PKG)-built-$(REV): .$(PKG)-cloned-$(REV) + cd $(PKG) && python ./configure.py $(CONFIG_OPTS) && make -j $(NUM_CPUS) + @touch $@ + +install: .$(PKG)-built-$(REV) + cd $(PKG) && make install && ldconfig diff --git a/testing/scripts/recipes/013_strongswan.mk b/testing/scripts/recipes/013_strongswan.mk index ff6a2db6a..13a1b23c8 100644 --- a/testing/scripts/recipes/013_strongswan.mk +++ b/testing/scripts/recipes/013_strongswan.mk @@ -62,6 +62,7 @@ CONFIG_OPTS = \ --enable-sqlite \ --enable-attr-sql \ --enable-mediation \ + --enable-botan \ --enable-openssl \ --enable-blowfish \ --enable-kernel-pfkey \ From 72a6831e7c2b5d3bf3b8a7d2501bf854e303fe73 Mon Sep 17 00:00:00 2001 From: Andreas Steffen Date: Wed, 5 Sep 2018 08:07:06 +0200 Subject: [PATCH 18/23] testing: Added botan/rw-cert scenario --- testing/tests/botan/rw-cert/description.txt | 9 +++++ testing/tests/botan/rw-cert/evaltest.dat | 10 ++++++ .../rw-cert/hosts/carol/etc/strongswan.conf | 11 ++++++ .../hosts/carol/etc/swanctl/rsa/carolKey.pem | 30 ++++++++++++++++ .../hosts/carol/etc/swanctl/swanctl.conf | 35 +++++++++++++++++++ .../rw-cert/hosts/dave/etc/strongswan.conf | 11 ++++++ .../hosts/dave/etc/swanctl/swanctl.conf | 27 ++++++++++++++ .../rw-cert/hosts/moon/etc/strongswan.conf | 15 ++++++++ .../hosts/moon/etc/swanctl/swanctl.conf | 25 +++++++++++++ testing/tests/botan/rw-cert/posttest.dat | 8 +++++ testing/tests/botan/rw-cert/pretest.dat | 11 ++++++ testing/tests/botan/rw-cert/test.conf | 25 +++++++++++++ 12 files changed, 217 insertions(+) create mode 100755 testing/tests/botan/rw-cert/description.txt create mode 100755 testing/tests/botan/rw-cert/evaltest.dat create mode 100755 testing/tests/botan/rw-cert/hosts/carol/etc/strongswan.conf create mode 100644 testing/tests/botan/rw-cert/hosts/carol/etc/swanctl/rsa/carolKey.pem create mode 100755 testing/tests/botan/rw-cert/hosts/carol/etc/swanctl/swanctl.conf create mode 100755 testing/tests/botan/rw-cert/hosts/dave/etc/strongswan.conf create mode 100755 testing/tests/botan/rw-cert/hosts/dave/etc/swanctl/swanctl.conf create mode 100755 testing/tests/botan/rw-cert/hosts/moon/etc/strongswan.conf create mode 100755 testing/tests/botan/rw-cert/hosts/moon/etc/swanctl/swanctl.conf create mode 100755 testing/tests/botan/rw-cert/posttest.dat create mode 100755 testing/tests/botan/rw-cert/pretest.dat create mode 100755 testing/tests/botan/rw-cert/test.conf diff --git a/testing/tests/botan/rw-cert/description.txt b/testing/tests/botan/rw-cert/description.txt new file mode 100755 index 000000000..1b92ee3cd --- /dev/null +++ b/testing/tests/botan/rw-cert/description.txt @@ -0,0 +1,9 @@ +The roadwarrior carol and the gateway moon use the botan +plugin based on the Botan library for all cryptographical functions whereas +roadwarrior dave uses the default strongSwan cryptographical +plugins. The authentication is based on X.509 certificates. +

+Upon the successful establishment of the IPsec tunnels, the updown script +automatically inserts iptables-based firewall rules that let pass the tunneled traffic. +In order to test both tunnel and firewall, both carol and dave ping +the client alice behind the gateway moon. diff --git a/testing/tests/botan/rw-cert/evaltest.dat b/testing/tests/botan/rw-cert/evaltest.dat new file mode 100755 index 000000000..51bf8c1ba --- /dev/null +++ b/testing/tests/botan/rw-cert/evaltest.dat @@ -0,0 +1,10 @@ +carol::swanctl --list-sas --raw 2> /dev/null::home.*version=2 state=ESTABLISHED local-host=192.168.0.100 local-port=4500 local-id=carol@strongswan.org remote-host=192.168.0.1 remote-port=4500 remote-id=moon.strongswan.org initiator=yes.*encr-alg=AES_CBC encr-keysize=128 integ-alg=HMAC_SHA2_256_128 prf-alg=PRF_HMAC_SHA2_256 dh-group=MODP_3072.*child-sas.*home.*state=INSTALLED mode=TUNNEL.*ESP.*encr-alg=AES_GCM_16 encr-keysize=128.*local-ts=\[192.168.0.100/32] remote-ts=\[10.1.0.0/16]::YES +dave:: swanctl --list-sas --raw 2> /dev/null::home.*version=2 state=ESTABLISHED local-host=192.168.0.200 local-port=4500 local-id=dave@strongswan.org remote-host=192.168.0.1 remote-port=4500 remote-id=moon.strongswan.org initiator=yes.*encr-alg=AES_CBC encr-keysize=128 integ-alg=HMAC_SHA2_256_128 prf-alg=PRF_HMAC_SHA2_256 dh-group=MODP_3072.*child-sas.*home.*state=INSTALLED mode=TUNNEL.*ESP.*encr-alg=AES_GCM_16 encr-keysize=128.*local-ts=\[192.168.0.200/32] remote-ts=\[10.1.0.0/16]::YES +moon:: swanctl --list-sas --ike-id 1 --raw 2> /dev/null::rw.*version=2 state=ESTABLISHED local-host=192.168.0.1 local-port=4500 local-id=moon.strongswan.org remote-host=192.168.0.100 remote-port=4500 remote-id=carol@strongswan.org.*encr-alg=AES_CBC encr-keysize=128 integ-alg=HMAC_SHA2_256_128 prf-alg=PRF_HMAC_SHA2_256 dh-group=MODP_3072.*child-sas.*net.*reqid=1 state=INSTALLED mode=TUNNEL.*ESP.*encr-alg=AES_GCM_16 encr-keysize=128.*local-ts=\[10.1.0.0/16] remote-ts=\[192.168.0.100/32]::YES +moon:: swanctl --list-sas --ike-id 2 --raw 2> /dev/null::rw.*version=2 state=ESTABLISHED local-host=192.168.0.1 local-port=4500 local-id=moon.strongswan.org remote-host=192.168.0.200 remote-port=4500 remote-id=dave@strongswan.org.*encr-alg=AES_CBC encr-keysize=128 integ-alg=HMAC_SHA2_256_128 prf-alg=PRF_HMAC_SHA2_256 dh-group=MODP_3072.*child-sas.*net.*reqid=2 state=INSTALLED mode=TUNNEL.*ESP.*encr-alg=AES_GCM_16 encr-keysize=128.*local-ts=\[10.1.0.0/16] remote-ts=\[192.168.0.200/32]::YES +alice::ping -c 1 192.168.0.100::64 bytes from 192.168.0.100: icmp_.eq=1::YES +alice::ping -c 1 192.168.0.200::64 bytes from 192.168.0.200: icmp_.eq=1::YES +moon::tcpdump::IP moon.strongswan.org > carol.strongswan.org: ESP::YES +moon::tcpdump::IP carol.strongswan.org > moon.strongswan.org: ESP::YES +moon::tcpdump::IP moon.strongswan.org > dave.strongswan.org: ESP::YES +moon::tcpdump::IP dave.strongswan.org > moon.strongswan.org: ESP::YES diff --git a/testing/tests/botan/rw-cert/hosts/carol/etc/strongswan.conf b/testing/tests/botan/rw-cert/hosts/carol/etc/strongswan.conf new file mode 100755 index 000000000..e5c6d88b3 --- /dev/null +++ b/testing/tests/botan/rw-cert/hosts/carol/etc/strongswan.conf @@ -0,0 +1,11 @@ +# /etc/strongswan.conf - strongSwan configuration file + +swanctl { + load = botan pem x509 revocation constraints pubkey +} + +charon-systemd { + load = nonce botan pem x509 revocation constraints pubkey curl kernel-netlink socket-default updown vici + + rsa_pss = yes +} diff --git a/testing/tests/botan/rw-cert/hosts/carol/etc/swanctl/rsa/carolKey.pem b/testing/tests/botan/rw-cert/hosts/carol/etc/swanctl/rsa/carolKey.pem new file mode 100644 index 000000000..1454ec54c --- /dev/null +++ b/testing/tests/botan/rw-cert/hosts/carol/etc/swanctl/rsa/carolKey.pem @@ -0,0 +1,30 @@ +-----BEGIN RSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: AES-128-CBC,7E1D40A7901772BA4D22AF58AA2DC76F + +1jt4EsxtHvgpSLN8PA/kSVKgoAsBEBQb8RK6VGnZywMCnpJdLKdPisGGYKNPg53b +/0AFBmQVE60M8icbSAIUrAtyKxaBkoc9A7ibNCjobi0UzXTm3GcZZ1EC4/lE9PQZ +/2FbcPgQWN3kZraZDkeP9XBXl6PorES8xvQUxJ9pd4hL7/c28fIApGhEimkIZO8o +Qb7bR2cNCLYQAR6PeDoqhV39gvWoh77wp1WB3tQVbkS6MI/xl3wY2QVdq3Sbszh+ +f6lDU/SZS8BU0f44FRoInPp0GasgJ7MCiuEIshjuNPa50QkMcnNJsSgVEuw2hjN6 +LvAXx7vPt9pKpQfnu7YSJUsXDYN6PyXt7sZ8hDqraYIcI6eMpEBaTpItPSV2eckv +06KC24Oa66E1yufNFAY49S2OY+pJA0W5zmcCqCjdrfJ+wNQYKZpbrfGz4VRzlFJC +e3VkmAFwA5rcZdlp/mU2XREy+TaWsHMnpL0NcMHGmsfkTgaJIkRWalrdxlNTeitr +3boNHWk0ESyMcBYRpM3eNXsGpiYy93u0bhrPbnqJsV6miKqpbs1aBNjlJ9s1Y2fC +sko5/v7uMjb5tLF3lWQZfTu+bYtpGxFrqHJjhd8yd4gL1cFi30JcjczhwRY3Dily +c0BFekMGmPc1djn6tfIFu13X9xTxyidCpVaT9UGnOaQs9OF1u8XAnZDaQgPwjLiy +UlOE8xQ60LrhWLD582FsFnZz56bZ+QOQRWDMsB8nJeqnFXKfcRlnr0qlG6lTfA8h +XkK/qGpdVvivS+CpbhVP6ixdEfa91Rx4NjLj53LGqOYwFEkM/OAIuMJetBfx3v9T +iQfv594KE32nv9besnKlmJr2cGQWBYg1pUOtFj/aZ00yuXacv8qwzbrt4xGGDYGO +Aj5Yf93UEcVkTySO1xJ1yiC6GJv1lLm0i5StwykHypxFijKe/zOpgtHVa5v5igjO +v6cfhfJGGgIPTYrtt+EDKXcayvy2e2U/3HYVCHYiiMPX8AvP/R6m7MGrzYxm/WyO +t68EWXSDLfuR3qcIlpP4aSBxuSpKhY/dIkS/beKZ7Njx1s4jSuYDMbKuuCRFSU2H +8ISHS0kh3FetiS8IyIYzxab+KQZwnVtiGj4oaAhgFTIIoH26Fv5+xka74JdzOSUA +jR9puKuxaegVWQVBx4cCyg6hAdewRm64PAcbApZWrPvMPBfTZFnXeifmaurcdK8p +p/1eLrrPnNM6+Fh6lcKdX74yHPz3eWP3K1njZegzWnChhEWElPhJr6qYNQjd+lAS +7650RJ3CJLUxBffnRR9nTArxFNI5jGWg/plLJTaRT5x5qg1dGNMqntpoeiY++Ttk +GFDGVIOICBze6SOvzkZBbuXLJSWmWj5g9J2cYsLoOvlwsDT7FzKl8p6VY4V+SQb+ +4PN8qZWmOeczaLEhZ1QLmTKFpz9+wUZsXeBd1s78bWJR0zhraMPa0UJ9GBGq6uQ0 +yZ4Xm5KHKcgoewCUQMekU9ECsmR5NuC7VFDaa1OdPEVnEYR1xtaWUY0lYKOiixnd ++85fSq/yAXI/r0O4ISA55o9y1kDqVibTwJacb6xXGg8dHSH+TtigwD8fK9mekkDC +-----END RSA PRIVATE KEY----- diff --git a/testing/tests/botan/rw-cert/hosts/carol/etc/swanctl/swanctl.conf b/testing/tests/botan/rw-cert/hosts/carol/etc/swanctl/swanctl.conf new file mode 100755 index 000000000..8485bc3fb --- /dev/null +++ b/testing/tests/botan/rw-cert/hosts/carol/etc/swanctl/swanctl.conf @@ -0,0 +1,35 @@ +connections { + + home { + local_addrs = 192.168.0.100 + remote_addrs = 192.168.0.1 + + local { + auth = pubkey + certs = carolCert.pem + id = carol@strongswan.org + } + remote { + auth = pubkey + id = moon.strongswan.org + } + children { + home { + remote_ts = 10.1.0.0/16 + + updown = /usr/local/libexec/ipsec/_updown iptables + esp_proposals = aes128gcm128-modp3072 + } + } + version = 2 + proposals = aes128-sha256-modp3072 + } +} + +secrets { + + rsa-carol { + file = carolKey.pem + secret = "nH5ZQEWtku0RJEZ6" + } +} diff --git a/testing/tests/botan/rw-cert/hosts/dave/etc/strongswan.conf b/testing/tests/botan/rw-cert/hosts/dave/etc/strongswan.conf new file mode 100755 index 000000000..b682bc054 --- /dev/null +++ b/testing/tests/botan/rw-cert/hosts/dave/etc/strongswan.conf @@ -0,0 +1,11 @@ +# /etc/strongswan.conf - strongSwan configuration file + +swanctl { + load = pem pkcs1 x509 revocation constraints pubkey openssl random +} + +charon-systemd { + load = random nonce sha1 sha2 aes hmac mgf1 pem pkcs1 x509 revocation constraints pubkey gmp curl kernel-netlink socket-default updown vici + + rsa_pss = yes +} diff --git a/testing/tests/botan/rw-cert/hosts/dave/etc/swanctl/swanctl.conf b/testing/tests/botan/rw-cert/hosts/dave/etc/swanctl/swanctl.conf new file mode 100755 index 000000000..27c6f12ba --- /dev/null +++ b/testing/tests/botan/rw-cert/hosts/dave/etc/swanctl/swanctl.conf @@ -0,0 +1,27 @@ +connections { + + home { + local_addrs = 192.168.0.200 + remote_addrs = 192.168.0.1 + + local { + auth = pubkey + certs = daveCert.pem + id = dave@strongswan.org + } + remote { + auth = pubkey + id = moon.strongswan.org + } + children { + home { + remote_ts = 10.1.0.0/16 + + updown = /usr/local/libexec/ipsec/_updown iptables + esp_proposals = aes128gcm128-modp3072 + } + } + version = 2 + proposals = aes128-sha256-modp3072 + } +} diff --git a/testing/tests/botan/rw-cert/hosts/moon/etc/strongswan.conf b/testing/tests/botan/rw-cert/hosts/moon/etc/strongswan.conf new file mode 100755 index 000000000..6a33d582b --- /dev/null +++ b/testing/tests/botan/rw-cert/hosts/moon/etc/strongswan.conf @@ -0,0 +1,15 @@ +# /etc/strongswan.conf - strongSwan configuration file + +swanctl { + load = pem botan x509 revocation constraints pubkey +} + +charon-systemd { + load = nonce test-vectors botan pem x509 revocation constraints pubkey curl kernel-netlink socket-default updown vici + + rsa_pss = yes + integrity_test = yes + crypto_test { + on_add = yes + } +} diff --git a/testing/tests/botan/rw-cert/hosts/moon/etc/swanctl/swanctl.conf b/testing/tests/botan/rw-cert/hosts/moon/etc/swanctl/swanctl.conf new file mode 100755 index 000000000..75c21c663 --- /dev/null +++ b/testing/tests/botan/rw-cert/hosts/moon/etc/swanctl/swanctl.conf @@ -0,0 +1,25 @@ +connections { + + rw { + local_addrs = 192.168.0.1 + + local { + auth = pubkey + certs = moonCert.pem + id = moon.strongswan.org + } + remote { + auth = pubkey + } + children { + net { + local_ts = 10.1.0.0/16 + + updown = /usr/local/libexec/ipsec/_updown iptables + esp_proposals = aes128gcm128-modp3072 + } + } + version = 2 + proposals = aes128-sha256-modp3072 + } +} diff --git a/testing/tests/botan/rw-cert/posttest.dat b/testing/tests/botan/rw-cert/posttest.dat new file mode 100755 index 000000000..b909ac76c --- /dev/null +++ b/testing/tests/botan/rw-cert/posttest.dat @@ -0,0 +1,8 @@ +carol::swanctl --terminate --ike home +dave::swanctl --terminate --ike home +carol::systemctl stop strongswan-swanctl +dave::systemctl stop strongswan-swanctl +moon::systemctl stop strongswan-swanctl +moon::iptables-restore < /etc/iptables.flush +carol::iptables-restore < /etc/iptables.flush +dave::iptables-restore < /etc/iptables.flush diff --git a/testing/tests/botan/rw-cert/pretest.dat b/testing/tests/botan/rw-cert/pretest.dat new file mode 100755 index 000000000..dd1a17ccb --- /dev/null +++ b/testing/tests/botan/rw-cert/pretest.dat @@ -0,0 +1,11 @@ +moon::iptables-restore < /etc/iptables.rules +carol::iptables-restore < /etc/iptables.rules +dave::iptables-restore < /etc/iptables.rules +moon::systemctl start strongswan-swanctl +carol::systemctl start strongswan-swanctl +dave::systemctl start strongswan-swanctl +moon::expect-connection rw +carol::expect-connection home +carol::swanctl --initiate --child home 2> /dev/null +dave::expect-connection home +dave::swanctl --initiate --child home 2> /dev/null diff --git a/testing/tests/botan/rw-cert/test.conf b/testing/tests/botan/rw-cert/test.conf new file mode 100755 index 000000000..1227b9d1c --- /dev/null +++ b/testing/tests/botan/rw-cert/test.conf @@ -0,0 +1,25 @@ +#!/bin/bash +# +# This configuration file provides information on the +# guest instances used for this test + +# All guest instances that are required for this test +# +VIRTHOSTS="alice moon carol winnetou dave" + +# Corresponding block diagram +# +DIAGRAM="a-m-c-w-d.png" + +# Guest instances on which tcpdump is to be started +# +TCPDUMPHOSTS="moon" + +# Guest instances on which IPsec is started +# Used for IPsec logging purposes +# +IPSECHOSTS="moon carol dave" + +# charon controlled by swanctl +# +SWANCTL=1 From 9c6bcb21f061c0d7cdf7c12fe30f66587eb25fbc Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 12 Sep 2018 12:02:41 +0200 Subject: [PATCH 19/23] travis: Only add the sonarcloud addon for that build --- .travis.yml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index d220950c7..99a01d209 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,10 +7,6 @@ compiler: - gcc - clang -addons: - sonarcloud: - organization: "strongswan" - before_install: - travis_retry ./scripts/test.sh deps - travis_retry ./scripts/test.sh pydeps @@ -63,6 +59,9 @@ matrix: env: TEST=sonarcloud git: depth: false + addons: + sonarcloud: + organization: "strongswan" - compiler: gcc env: TEST=win64 MONOLITHIC=yes # this does not work on 14.04 due to a missing @4 decoration for From e5d52774677b7217d2eaa7c327433e3d5e994af1 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 12 Sep 2018 12:05:14 +0200 Subject: [PATCH 20/23] travis: Build botan plugin also in the tests that build everything --- scripts/test.sh | 42 ++++++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/scripts/test.sh b/scripts/test.sh index a2ec74a4b..70994ed1b 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -1,6 +1,26 @@ #!/bin/sh # Build script for Travis CI +build_botan() +{ + # if the leak detective is enabled we have to disable threading support + # (used for std::async) as that causes invalid frees somehow, the + # locking allocator causes a static leak via the first function that + # references it (e.g. crypter or hasher), so we disable that too + if test "$LEAK_DETECTIVE" = "yes"; then + BOTAN_CONFIG="--without-os-features=threads + --disable-modules=locking_allocator" + fi + # disable some larger modules we don't need for the tests + BOTAN_CONFIG="$BOTAN_CONFIG --disable-modules=pkcs11,tls,x509,xmss" + git clone --depth 1 https://github.com/randombit/botan.git botan && + cd botan && + python ./configure.py $BOTAN_CONFIG && + make -j4 libs >/dev/null && + sudo make install >/dev/null && + sudo ldconfig || exit $? +} + if test -z $TRAVIS_BUILD_DIR; then TRAVIS_BUILD_DIR=$PWD fi @@ -34,22 +54,7 @@ botan) # currently required) DEPS="" if test "$1" = "deps"; then - # if the leak detective is enabled we have to disable threading support - # (used for std::async) as that causes invalid frees somehow, the - # locking allocator causes a static leak via the first function that - # references it (e.g. crypter or hasher), so we disable that too - if test "$LEAK_DETECTIVE" = "yes"; then - BOTAN_CONFIG="--without-os-features=threads - --disable-modules=locking_allocator" - fi - # disable some larger modules we don't need for the tests - BOTAN_CONFIG="$BOTAN_CONFIG --disable-modules=pkcs11,tls,x509,xmss" - git clone --depth 1 https://github.com/randombit/botan.git botan && - cd botan && - python ./configure.py $BOTAN_CONFIG && - make -j4 libs >/dev/null && - sudo make install >/dev/null && - sudo ldconfig || exit $? + build_botan fi ;; printf-builtin) @@ -69,8 +74,6 @@ all|coverage|sonarcloud) CONFIG="$CONFIG --disable-nm" # not enabled on the build server CONFIG="$CONFIG --disable-af-alg" - # separate test case with external dependency - CONFIG="$CONFIG --disable-botan" if test "$TEST" != "coverage"; then CONFIG="$CONFIG --disable-coverage" else @@ -82,6 +85,9 @@ all|coverage|sonarcloud) libpcsclite-dev libpam0g-dev binutils-dev libunwind8-dev libjson0-dev iptables-dev python-pip libtspi-dev" PYDEPS="pytest" + if test "$1" = "deps"; then + build_botan + fi ;; win*) CONFIG="--disable-defaults --enable-svc --enable-ikev2 From a80069e7bff4afacad1527494e81f573ca596a03 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 12 Sep 2018 12:56:11 +0200 Subject: [PATCH 21/23] gcrypt: Make generic DH constructor static --- src/libstrongswan/plugins/gcrypt/gcrypt_dh.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libstrongswan/plugins/gcrypt/gcrypt_dh.c b/src/libstrongswan/plugins/gcrypt/gcrypt_dh.c index f59144a86..b57f05e3a 100644 --- a/src/libstrongswan/plugins/gcrypt/gcrypt_dh.c +++ b/src/libstrongswan/plugins/gcrypt/gcrypt_dh.c @@ -195,8 +195,8 @@ METHOD(diffie_hellman_t, destroy, void, /* * Generic internal constructor */ -gcrypt_dh_t *create_generic(diffie_hellman_group_t group, size_t exp_len, - chunk_t g, chunk_t p) +static gcrypt_dh_t *create_generic(diffie_hellman_group_t group, size_t exp_len, + chunk_t g, chunk_t p) { private_gcrypt_dh_t *this; gcry_error_t err; From 24af02b00d49376db47b37896987e81e153b05ac Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 12 Sep 2018 13:12:44 +0200 Subject: [PATCH 22/23] travis: Use amalgamation build for Botan and build outside our source tree This merges all source files into botan_all.cpp, which reduces the build time by almost 50%. Building outside the strongSwan tree avoids analyzing Botan with sonarqube. --- scripts/test.sh | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/scripts/test.sh b/scripts/test.sh index 70994ed1b..0cff48842 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -3,6 +3,8 @@ build_botan() { + BOTAN_DIR=$TRAVIS_BUILD_DIR/../botan + # if the leak detective is enabled we have to disable threading support # (used for std::async) as that causes invalid frees somehow, the # locking allocator causes a static leak via the first function that @@ -13,12 +15,13 @@ build_botan() fi # disable some larger modules we don't need for the tests BOTAN_CONFIG="$BOTAN_CONFIG --disable-modules=pkcs11,tls,x509,xmss" - git clone --depth 1 https://github.com/randombit/botan.git botan && - cd botan && - python ./configure.py $BOTAN_CONFIG && + git clone --depth 1 https://github.com/randombit/botan.git $BOTAN_DIR && + cd $BOTAN_DIR && + python ./configure.py --amalgamation $BOTAN_CONFIG && make -j4 libs >/dev/null && sudo make install >/dev/null && sudo ldconfig || exit $? + cd - } if test -z $TRAVIS_BUILD_DIR; then From 1bbb736edc9fc46c5f50f060608d94c952934735 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 12 Sep 2018 15:51:08 +0200 Subject: [PATCH 23/23] travis: Use a fix revision for Botan and speed up subsequent builds via ccache --- .travis.yml | 2 ++ scripts/test.sh | 6 +++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 99a01d209..504e118d9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,6 +7,8 @@ compiler: - gcc - clang +cache: ccache + before_install: - travis_retry ./scripts/test.sh deps - travis_retry ./scripts/test.sh pydeps diff --git a/scripts/test.sh b/scripts/test.sh index 0cff48842..301c3148e 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -3,6 +3,8 @@ build_botan() { + # same revision used in the build recipe of the testing environment + BOTAN_REV=1872f899716854927ecc68022fac318735be8824 BOTAN_DIR=$TRAVIS_BUILD_DIR/../botan # if the leak detective is enabled we have to disable threading support @@ -15,8 +17,10 @@ build_botan() fi # disable some larger modules we don't need for the tests BOTAN_CONFIG="$BOTAN_CONFIG --disable-modules=pkcs11,tls,x509,xmss" - git clone --depth 1 https://github.com/randombit/botan.git $BOTAN_DIR && + + git clone https://github.com/randombit/botan.git $BOTAN_DIR && cd $BOTAN_DIR && + git checkout $BOTAN_REV && python ./configure.py --amalgamation $BOTAN_CONFIG && make -j4 libs >/dev/null && sudo make install >/dev/null &&