From: René Korthaus Date: Wed, 25 Jul 2018 11:01:19 +0000 (+0200) Subject: botan: Add Botan plugin to libstrongswan X-Git-Tag: 5.7.0rc1~4^2~22 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=af26cc4d8542fb6aaea9bfc5aaae1b139faee94a;p=thirdparty%2Fstrongswan.git botan: Add Botan plugin to libstrongswan --- diff --git a/configure.ac b/configure.ac index 26fd707b7b..ae05100bb3 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 66539a8797..e6d7ce74b8 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 0000000000..95df441308 --- /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 0000000000..af8fe4c437 --- /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 0000000000..246904a5f1 --- /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 0000000000..2ca14f35a3 --- /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 0000000000..88f221d343 --- /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 0000000000..48b231f88b --- /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 0000000000..f4751de01a --- /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 0000000000..abb4597ad3 --- /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 0000000000..0fcfda25e6 --- /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 0000000000..abc513b709 --- /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 0000000000..eb49ec3cbe --- /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 0000000000..7fc59bdc17 --- /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 0000000000..600e60b0e4 --- /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 0000000000..f3caec3876 --- /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 0000000000..2cb0bc1174 --- /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 0000000000..a1e3514d8e --- /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 0000000000..1deeea9616 --- /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 0000000000..3bc3883218 --- /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 0000000000..fdb08a90e2 --- /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 0000000000..f398f9fb7c --- /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 0000000000..087288863e --- /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 0000000000..c35e0c2b66 --- /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 0000000000..546b88dacd --- /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 0000000000..b2445b5f92 --- /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 0000000000..4f6be83336 --- /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 0000000000..d69e29e625 --- /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 0000000000..d19658740a --- /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_ @}*/