diff options
author | Yves-Alexis Perez <corsac@debian.org> | 2019-01-02 10:45:36 +0100 |
---|---|---|
committer | Yves-Alexis Perez <corsac@debian.org> | 2019-01-02 11:07:05 +0100 |
commit | 918094fde55fa0dbfd59a5f88d576efb513a88db (patch) | |
tree | 61e31656c60a6cc928c50cd633568043673e2cbd /src/libstrongswan/plugins | |
parent | 69bc96f6b0b388d35e983f8d27224fa49d92918c (diff) | |
download | vyos-strongswan-918094fde55fa0dbfd59a5f88d576efb513a88db.tar.gz vyos-strongswan-918094fde55fa0dbfd59a5f88d576efb513a88db.zip |
New upstream version 5.7.2
Diffstat (limited to 'src/libstrongswan/plugins')
48 files changed, 2480 insertions, 285 deletions
diff --git a/src/libstrongswan/plugins/agent/agent_private_key.c b/src/libstrongswan/plugins/agent/agent_private_key.c index 77c29916c..db87affc9 100644 --- a/src/libstrongswan/plugins/agent/agent_private_key.c +++ b/src/libstrongswan/plugins/agent/agent_private_key.c @@ -82,6 +82,14 @@ enum agent_msg_type_t { }; /** + * Flags for signatures + */ +enum agent_signature_flags_t { + SSH_AGENT_FLAG_SHA2_256 = 2, + SSH_AGENT_FLAG_SHA2_512 = 4, +}; + +/** * read a byte from a blob */ static u_char read_byte(chunk_t *blob) @@ -217,12 +225,35 @@ static bool read_key(private_agent_private_key_t *this, public_key_t *pubkey) } static bool scheme_supported(private_agent_private_key_t *this, - signature_scheme_t scheme) + signature_scheme_t scheme, uint32_t *flags, + char **prefix) { switch (this->pubkey->get_type(this->pubkey)) { case KEY_RSA: - return scheme == SIGN_RSA_EMSA_PKCS1_SHA1; + switch (scheme) + { + case SIGN_RSA_EMSA_PKCS1_SHA1: + *prefix = "ssh-rsa"; + return TRUE; + case SIGN_RSA_EMSA_PKCS1_SHA2_256: + *flags |= SSH_AGENT_FLAG_SHA2_256; + *prefix = "rsa-sha2-256"; + return TRUE; + case SIGN_RSA_EMSA_PKCS1_SHA2_512: + *flags |= SSH_AGENT_FLAG_SHA2_512; + *prefix = "rsa-sha2-512"; + return TRUE; + default: + break; + } + return FALSE; + case KEY_ED25519: + *prefix = "ssh-ed25519"; + return scheme == SIGN_ED25519; + case KEY_ED448: + *prefix = "ssh-ed448"; + return scheme == SIGN_ED448; case KEY_ECDSA: return scheme == SIGN_ECDSA_256 || scheme == SIGN_ECDSA_384 || @@ -236,11 +267,12 @@ METHOD(private_key_t, sign, bool, private_agent_private_key_t *this, signature_scheme_t scheme, void *params, chunk_t data, chunk_t *signature) { - uint32_t len, flags; - char buf[2048]; + key_type_t type; + uint32_t len, flags = 0; + char buf[2048], *prefix = NULL; chunk_t blob; - if (!scheme_supported(this, scheme)) + if (!scheme_supported(this, scheme, &flags, &prefix)) { DBG1(DBG_LIB, "signature scheme %N not supported by ssh-agent", signature_scheme_names, scheme); @@ -272,7 +304,7 @@ METHOD(private_key_t, sign, bool, return FALSE; } - flags = htonl(0); + flags = htonl(flags); if (write(this->socket, &flags, sizeof(flags)) != sizeof(flags)) { DBG1(DBG_LIB, "writing to ssh-agent failed"); @@ -290,9 +322,15 @@ METHOD(private_key_t, sign, bool, } /* parse length */ blob = read_string(&blob); - /* check sig type */ - if (chunk_equals(read_string(&blob), chunk_from_str("ssh-rsa"))) - { /* for RSA the signature has no special encoding */ + /* verify type */ + if (prefix && !chunk_equals(read_string(&blob), chunk_from_str(prefix))) + { + DBG1(DBG_LIB, "ssh-agent didn't return requested %s signature", prefix); + return FALSE; + } + type = this->pubkey->get_type(this->pubkey); + if (type == KEY_RSA || type == KEY_ED25519 || type == KEY_ED448) + { /* for RSA/EdDSA, the signature has no special encoding */ blob = read_string(&blob); if (blob.len) { @@ -301,7 +339,7 @@ METHOD(private_key_t, sign, bool, } } else - { /* anything else is treated as ECSDA for now */ + { /* parse ECDSA signatures */ blob = read_string(&blob); if (blob.len) { @@ -340,6 +378,80 @@ METHOD(private_key_t, get_keysize, int, return this->pubkey->get_keysize(this->pubkey); } +/** + * Private data for RSA scheme enumerator + */ +typedef struct { + enumerator_t public; + int index; + bool reverse; +} scheme_enumerator_t; + +static signature_params_t rsa_schemes[] = { + { .scheme = SIGN_RSA_EMSA_PKCS1_SHA2_256 }, + { .scheme = SIGN_RSA_EMSA_PKCS1_SHA2_512 }, +}; + +METHOD(enumerator_t, enumerate_rsa_scheme, bool, + scheme_enumerator_t *this, va_list args) +{ + signature_params_t **params; + + VA_ARGS_VGET(args, params); + + if ((this->reverse && --this->index >= 0) || + (!this->reverse && ++this->index < countof(rsa_schemes))) + { + *params = &rsa_schemes[this->index]; + return TRUE; + } + return FALSE; +} + +/** + * Create an enumerator for the supported RSA signature schemes + */ +static enumerator_t *create_rsa_enumerator(private_agent_private_key_t *this) +{ + scheme_enumerator_t *enumerator; + + INIT(enumerator, + .public = { + .enumerate = enumerator_enumerate_default, + .venumerate = _enumerate_rsa_scheme, + .destroy = (void*)free, + }, + .index = -1, + .reverse = FALSE, + ); + /* propose SHA-512 first for larger keys */ + if (get_keysize(this) > 3072) + { + enumerator->index = countof(rsa_schemes); + enumerator->reverse = TRUE; + } + return &enumerator->public; +} + +METHOD(private_key_t, supported_signature_schemes, enumerator_t*, + private_agent_private_key_t *this) +{ + key_type_t type = get_type(this); + + switch (type) + { + case KEY_RSA: + return create_rsa_enumerator(this); + case KEY_ED25519: + case KEY_ED448: + case KEY_ECDSA: + return signature_schemes_for_key(type, get_keysize(this)); + default: + break; + } + return enumerator_create_empty(); +} + METHOD(private_key_t, get_public_key, public_key_t*, private_agent_private_key_t *this) { @@ -413,6 +525,7 @@ agent_private_key_t *agent_private_key_open(key_type_t type, va_list args) .public = { .key = { .get_type = _get_type, + .supported_signature_schemes = _supported_signature_schemes, .sign = _sign, .decrypt = _decrypt, .get_keysize = _get_keysize, diff --git a/src/libstrongswan/plugins/botan/Makefile.am b/src/libstrongswan/plugins/botan/Makefile.am index c1160145a..30d3e601c 100644 --- a/src/libstrongswan/plugins/botan/Makefile.am +++ b/src/libstrongswan/plugins/botan/Makefile.am @@ -23,9 +23,11 @@ libstrongswan_botan_la_SOURCES = \ botan_ec_diffie_hellman.h botan_ec_diffie_hellman.c \ botan_ec_public_key.h botan_ec_public_key.c \ botan_ec_private_key.h botan_ec_private_key.c \ + botan_ed_public_key.h botan_ed_public_key.c \ + botan_ed_private_key.h botan_ed_private_key.c \ botan_util.h botan_util.c \ botan_util_keys.h botan_util_keys.c \ - botan_gcm.h botan_gcm.c \ + botan_aead.h botan_aead.c \ botan_x25519.h botan_x25519.c libstrongswan_botan_la_LDFLAGS = -module -avoid-version diff --git a/src/libstrongswan/plugins/botan/Makefile.in b/src/libstrongswan/plugins/botan/Makefile.in index ef9f88610..3bb3e22f4 100644 --- a/src/libstrongswan/plugins/botan/Makefile.in +++ b/src/libstrongswan/plugins/botan/Makefile.in @@ -142,8 +142,9 @@ am_libstrongswan_botan_la_OBJECTS = botan_plugin.lo botan_rng.lo \ botan_hasher.lo botan_hmac.lo botan_crypter.lo \ botan_rsa_public_key.lo botan_rsa_private_key.lo \ botan_diffie_hellman.lo botan_ec_diffie_hellman.lo \ - botan_ec_public_key.lo botan_ec_private_key.lo botan_util.lo \ - botan_util_keys.lo botan_gcm.lo botan_x25519.lo + botan_ec_public_key.lo botan_ec_private_key.lo \ + botan_ed_public_key.lo botan_ed_private_key.lo botan_util.lo \ + botan_util_keys.lo botan_aead.lo botan_x25519.lo libstrongswan_botan_la_OBJECTS = $(am_libstrongswan_botan_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) @@ -478,9 +479,11 @@ libstrongswan_botan_la_SOURCES = \ botan_ec_diffie_hellman.h botan_ec_diffie_hellman.c \ botan_ec_public_key.h botan_ec_public_key.c \ botan_ec_private_key.h botan_ec_private_key.c \ + botan_ed_public_key.h botan_ed_public_key.c \ + botan_ed_private_key.h botan_ed_private_key.c \ botan_util.h botan_util.c \ botan_util_keys.h botan_util_keys.c \ - botan_gcm.h botan_gcm.c \ + botan_aead.h botan_aead.c \ botan_x25519.h botan_x25519.c libstrongswan_botan_la_LDFLAGS = -module -avoid-version @@ -574,12 +577,14 @@ mostlyclean-compile: distclean-compile: -rm -f *.tab.c +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/botan_aead.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/botan_crypter.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/botan_diffie_hellman.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/botan_ec_diffie_hellman.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/botan_ec_private_key.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/botan_ec_public_key.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/botan_gcm.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/botan_ed_private_key.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/botan_ed_public_key.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/botan_hasher.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/botan_hmac.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/botan_plugin.Plo@am__quote@ diff --git a/src/libstrongswan/plugins/botan/botan_gcm.c b/src/libstrongswan/plugins/botan/botan_aead.c index 7e0fc1468..40006ae77 100644 --- a/src/libstrongswan/plugins/botan/botan_gcm.c +++ b/src/libstrongswan/plugins/botan/botan_aead.c @@ -1,4 +1,7 @@ /* + * Copyright (C) 2018 Tobias Brunner + * HSR Hochschule fuer Technik Rapperswil + * * Copyright (C) 2018 Atanas Filyanov * Rohde & Schwarz Cybersecurity GmbH * @@ -21,23 +24,28 @@ * THE SOFTWARE. */ -#include "botan_gcm.h" +#include "botan_aead.h" #include <botan/build.h> -#ifdef BOTAN_HAS_AES -#ifdef BOTAN_HAS_AEAD_GCM +#if (defined(BOTAN_HAS_AES) && \ + (defined(BOTAN_HAS_AEAD_GCM) || defined(BOTAN_HAS_AEAD_CCM))) || \ + defined(BOTAN_HAS_AEAD_CHACHA20_POLY1305) #include <crypto/iv/iv_gen_seq.h> #include <botan/ffi.h> /** - * as defined in RFC 4106 + * As defined in RFC 4106 (GCM) and RFC 7634 (ChaPoly) */ -#define IV_LEN 8 -#define SALT_LEN 4 -#define NONCE_LEN (IV_LEN + SALT_LEN) +#define IV_LEN 8 +#define SALT_LEN 4 +#define CHAPOLY_KEY_LEN 32 +/** + * As defined in RFC 4309 + */ +#define CCM_SALT_LEN 3 typedef struct private_aead_t private_aead_t; @@ -56,7 +64,7 @@ struct private_aead_t { /** * Salt value */ - char salt[SALT_LEN]; + chunk_t salt; /** * Size of the integrity check value @@ -77,15 +85,12 @@ struct private_aead_t { /** * 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) +static bool do_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, input_consumed = 0; - - memcpy(nonce, this->salt, SALT_LEN); - memcpy(nonce + SALT_LEN, iv.ptr, IV_LEN); + chunk_t nonce; if (botan_cipher_init(&cipher, this->cipher_name, init_flag)) { @@ -105,7 +110,9 @@ static bool crypt(private_aead_t *this, chunk_t data, chunk_t assoc, chunk_t iv, return FALSE; } - if (botan_cipher_start(cipher, nonce, NONCE_LEN)) + nonce = chunk_cata("cc", this->salt, iv); + + if (botan_cipher_start(cipher, nonce.ptr, nonce.len)) { botan_cipher_destroy(cipher); return FALSE; @@ -149,7 +156,8 @@ METHOD(aead_t, encrypt, bool, *encrypted = chunk_alloc(plain.len + this->icv_size); out = encrypted->ptr; } - return crypt(this, plain, assoc, iv, out, BOTAN_CIPHER_INIT_FLAG_ENCRYPT); + return do_crypt(this, plain, assoc, iv, out, + BOTAN_CIPHER_INIT_FLAG_ENCRYPT); } METHOD(aead_t, decrypt, bool, @@ -170,8 +178,8 @@ METHOD(aead_t, decrypt, bool, *plain = chunk_alloc(encrypted.len); out = plain->ptr; } - return crypt(this, encrypted, assoc, iv, out, - BOTAN_CIPHER_INIT_FLAG_DECRYPT); + return do_crypt(this, encrypted, assoc, iv, out, + BOTAN_CIPHER_INIT_FLAG_DECRYPT); } METHOD(aead_t, get_block_size, size_t, @@ -201,7 +209,7 @@ METHOD(aead_t, get_iv_gen, iv_gen_t*, METHOD(aead_t, get_key_size, size_t, private_aead_t *this) { - return this->key.len + SALT_LEN; + return this->key.len + this->salt.len; } METHOD(aead_t, set_key, bool, @@ -211,7 +219,7 @@ METHOD(aead_t, set_key, bool, { return FALSE; } - memcpy(this->salt, key.ptr + key.len - SALT_LEN, SALT_LEN); + memcpy(this->salt.ptr, key.ptr + key.len - this->salt.len, this->salt.len); memcpy(this->key.ptr, key.ptr, this->key.len); return TRUE; } @@ -220,15 +228,82 @@ METHOD(aead_t, destroy, void, private_aead_t *this) { chunk_clear(&this->key); + chunk_clear(&this->salt); this->iv_gen->destroy(this->iv_gen); free(this); } +#ifdef BOTAN_HAS_AES +#if defined(BOTAN_HAS_AEAD_GCM) || defined(BOTAN_HAS_AEAD_GCM) + +static struct { + encryption_algorithm_t algo; + size_t key_size; + char *name; + size_t icv_size; +} aes_modes[] = { + { ENCR_AES_GCM_ICV8, 16, "AES-128/GCM(8)", 8 }, + { ENCR_AES_GCM_ICV8, 24, "AES-192/GCM(8)", 8 }, + { ENCR_AES_GCM_ICV8, 32, "AES-256/GCM(8)", 8 }, + { ENCR_AES_GCM_ICV12, 16, "AES-128/GCM(12)", 12 }, + { ENCR_AES_GCM_ICV12, 24, "AES-192/GCM(12)", 12 }, + { ENCR_AES_GCM_ICV12, 32, "AES-256/GCM(12)", 12 }, + { ENCR_AES_GCM_ICV16, 16, "AES-128/GCM(16)", 16 }, + { ENCR_AES_GCM_ICV16, 24, "AES-192/GCM(16)", 16 }, + { ENCR_AES_GCM_ICV16, 32, "AES-256/GCM(16)", 16 }, + { ENCR_AES_CCM_ICV8, 16, "AES-128/CCM(8,4)", 8 }, + { ENCR_AES_CCM_ICV8, 24, "AES-192/CCM(8,4)", 8 }, + { ENCR_AES_CCM_ICV8, 32, "AES-256/CCM(8,4)", 8 }, + { ENCR_AES_CCM_ICV12, 16, "AES-128/CCM(12,4)", 12 }, + { ENCR_AES_CCM_ICV12, 24, "AES-192/CCM(12,4)", 12 }, + { ENCR_AES_CCM_ICV12, 32, "AES-256/CCM(12,4)", 12 }, + { ENCR_AES_CCM_ICV16, 16, "AES-128/CCM(16,4)", 16 }, + { ENCR_AES_CCM_ICV16, 24, "AES-192/CCM(16,4)", 16 }, + { ENCR_AES_CCM_ICV16, 32, "AES-256/CCM(16,4)", 16 }, +}; + +/** + * Determine the cipher name and ICV size for the given algorithm and key size + */ +static bool determine_aes_params(private_aead_t *this, + encryption_algorithm_t algo, size_t key_size) +{ + int i; + + for (i = 0; i < countof(aes_modes); i++) + { + if (aes_modes[i].algo == algo && + aes_modes[i].key_size == key_size) + { + this->cipher_name = aes_modes[i].name; + this->icv_size = aes_modes[i].icv_size; + return TRUE; + } + } + return FALSE; +} + +#endif +#endif + +/** + * Check the given salt size, set it if not set + */ +static bool check_salt_size(size_t expected, size_t *salt_size) +{ + if (*salt_size) + { + return *salt_size == expected; + } + *salt_size = expected; + return TRUE; +} + /* * Described in header */ -aead_t *botan_gcm_create(encryption_algorithm_t algo, size_t key_size, - size_t salt_size) +aead_t *botan_aead_create(encryption_algorithm_t algo, size_t key_size, + size_t salt_size) { private_aead_t *this; @@ -246,88 +321,68 @@ aead_t *botan_gcm_create(encryption_algorithm_t algo, size_t key_size, }, ); - if (salt_size && salt_size != SALT_LEN) - { - /* currently not supported */ - free(this); - return NULL; - } - switch (algo) { +#ifdef BOTAN_HAS_AES +#ifdef BOTAN_HAS_AEAD_GCM case ENCR_AES_GCM_ICV8: - switch (key_size) + case ENCR_AES_GCM_ICV12: + case ENCR_AES_GCM_ICV16: + if (!key_size) { - case 0: - key_size = 16; - /* FALL */ - 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; + key_size = 16; + } + if (!check_salt_size(SALT_LEN, &salt_size) || + !determine_aes_params(this, algo, key_size)) + { + free(this); + return NULL; } - this->icv_size = 8; break; - case ENCR_AES_GCM_ICV12: - switch (key_size) +#endif +#ifdef BOTAN_HAS_AEAD_CCM + case ENCR_AES_CCM_ICV8: + case ENCR_AES_CCM_ICV12: + case ENCR_AES_CCM_ICV16: + if (!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; + key_size = 16; + } + if (!check_salt_size(CCM_SALT_LEN, &salt_size) || + !determine_aes_params(this, algo, key_size)) + { + free(this); + return NULL; } - this->icv_size = 12; break; - case ENCR_AES_GCM_ICV16: - switch (key_size) +#endif +#endif +#ifdef BOTAN_HAS_AEAD_CHACHA20_POLY1305 + case ENCR_CHACHA20_POLY1305: + if (!key_size) + { + key_size = CHAPOLY_KEY_LEN; + } + if (key_size != CHAPOLY_KEY_LEN || + !check_salt_size(SALT_LEN, &salt_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; + free(this); + return NULL; } + this->cipher_name = "ChaCha20Poly1305"; this->icv_size = 16; break; +#endif default: free(this); return NULL; } this->key = chunk_alloc(key_size); + this->salt = chunk_alloc(salt_size); this->iv_gen = iv_gen_seq_create(); return &this->public; } #endif -#endif diff --git a/src/libstrongswan/plugins/botan/botan_gcm.h b/src/libstrongswan/plugins/botan/botan_aead.h index b2053cb4d..00a2ba4bc 100644 --- a/src/libstrongswan/plugins/botan/botan_gcm.h +++ b/src/libstrongswan/plugins/botan/botan_aead.h @@ -1,4 +1,7 @@ /* + * Copyright (C) 2018 Tobias Brunner + * HSR Hochschule fuer Technik Rapperswil + * * Copyright (C) 2018 Atanas Filyanov * Rohde & Schwarz Cybersecurity GmbH * @@ -22,14 +25,14 @@ */ /** - * Implements the aead_t interface using Botan in GCM mode. + * Implements the aead_t interface using Botan. * - * @defgroup botan_gcm botan_gcm + * @defgroup botan_aead botan_aead * @{ @ingroup botan_p */ -#ifndef BOTAN_GCM_H_ -#define BOTAN_GCM_H_ +#ifndef BOTAN_AEAD_H_ +#define BOTAN_AEAD_H_ #include <crypto/aead.h> @@ -41,7 +44,7 @@ * @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); +aead_t *botan_aead_create(encryption_algorithm_t algo, size_t key_size, + size_t salt_size); -#endif /** BOTAN_GCM_H_ @}*/ +#endif /** BOTAN_AEAD_H_ @}*/ diff --git a/src/libstrongswan/plugins/botan/botan_crypter.c b/src/libstrongswan/plugins/botan/botan_crypter.c index 002be6ea8..3ec5c4d5e 100644 --- a/src/libstrongswan/plugins/botan/botan_crypter.c +++ b/src/libstrongswan/plugins/botan/botan_crypter.c @@ -25,6 +25,10 @@ #include "botan_crypter.h" +#include <botan/build.h> + +#if defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_MODE_CBC) + #include <botan/ffi.h> typedef struct private_botan_crypter_t private_botan_crypter_t; @@ -189,3 +193,5 @@ botan_crypter_t *botan_crypter_create(encryption_algorithm_t algo, this->key = chunk_alloc(key_size); return &this->public; } + +#endif diff --git a/src/libstrongswan/plugins/botan/botan_ec_public_key.c b/src/libstrongswan/plugins/botan/botan_ec_public_key.c index 4c85dbcec..095ae3f20 100644 --- a/src/libstrongswan/plugins/botan/botan_ec_public_key.c +++ b/src/libstrongswan/plugins/botan/botan_ec_public_key.c @@ -69,9 +69,7 @@ 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) { - botan_pk_op_verify_t verify_op; chunk_t sig = signature; - bool valid = FALSE; if (signature_format == SIG_FORMAT_DER_SEQUENCE) { @@ -104,22 +102,7 @@ static bool verify_signature(private_botan_ec_public_key_t *this, memcpy(sig.ptr + (keylen - r.len), r.ptr, r.len); memcpy(sig.ptr + keylen + (keylen - s.len), s.ptr, s.len); } - - if (botan_pk_op_verify_create(&verify_op, this->key, hash_and_padding, 0)) - { - return FALSE; - } - - if (botan_pk_op_verify_update(verify_op, data.ptr, data.len)) - { - botan_pk_op_verify_destroy(verify_op); - return FALSE; - } - - valid = !(botan_pk_op_verify_finish(verify_op, sig.ptr, sig.len)); - - botan_pk_op_verify_destroy(verify_op); - return valid; + return botan_verify_signature(this->key, hash_and_padding, data, sig); } METHOD(public_key_t, get_type, key_type_t, diff --git a/src/libstrongswan/plugins/botan/botan_ed_private_key.c b/src/libstrongswan/plugins/botan/botan_ed_private_key.c new file mode 100644 index 000000000..3f0f54222 --- /dev/null +++ b/src/libstrongswan/plugins/botan/botan_ed_private_key.c @@ -0,0 +1,279 @@ +/* + * Copyright (C) 2018 Tobias Brunner + * HSR Hochschule fuer Technik Rapperswil + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "botan_ed_private_key.h" +#include "botan_ed_public_key.h" +#include "botan_util.h" + +#include <botan/build.h> + +#ifdef BOTAN_HAS_ED25519 + +#include <asn1/asn1.h> +#include <utils/debug.h> + +typedef struct private_private_key_t private_private_key_t; + +#define ED25519_KEY_LEN 32 + +/** + * Private data + */ +struct private_private_key_t { + + /** + * Public interface + */ + private_key_t public; + + /** + * Botan private key object + */ + botan_privkey_t key; + + /** + * Reference count + */ + refcount_t ref; +}; + +METHOD(private_key_t, sign, bool, + private_private_key_t *this, signature_scheme_t scheme, + void *params, chunk_t data, chunk_t *signature) +{ + switch (scheme) + { + case SIGN_ED25519: + return botan_get_signature(this->key, "Pure", 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_private_key_t *this, encryption_scheme_t scheme, + chunk_t crypto, chunk_t *plain) +{ + DBG1(DBG_LIB, "EdDSA private key decryption not implemented"); + return FALSE; +} + +METHOD(private_key_t, get_keysize, int, + private_private_key_t *this) +{ + return ED25519_KEY_LEN * 8; +} + +METHOD(private_key_t, get_type, key_type_t, + private_private_key_t *this) +{ + return KEY_ED25519; +} + +METHOD(private_key_t, get_public_key, public_key_t*, + private_private_key_t *this) +{ + botan_pubkey_t pubkey; + + if (botan_privkey_export_pubkey(&pubkey, this->key)) + { + return NULL; + } + return botan_ed_public_key_adopt(pubkey); +} + +METHOD(private_key_t, get_fingerprint, bool, + private_private_key_t *this, cred_encoding_type_t type, + chunk_t *fingerprint) +{ + botan_pubkey_t pubkey; + bool success = FALSE; + + /* check the cache before doing the export */ + if (lib->encoding->get_cache(lib->encoding, type, this, fingerprint)) + { + return TRUE; + } + + if (botan_privkey_export_pubkey(&pubkey, this->key)) + { + return FALSE; + } + success = botan_get_fingerprint(pubkey, this, type, fingerprint); + botan_pubkey_destroy(pubkey); + return success; +} + +METHOD(private_key_t, get_encoding, bool, + private_private_key_t *this, cred_encoding_type_t type, + chunk_t *encoding) +{ + return botan_get_privkey_encoding(this->key, type, encoding); +} + +METHOD(private_key_t, get_ref, private_key_t*, + private_private_key_t *this) +{ + ref_get(&this->ref); + return &this->public; +} + +METHOD(private_key_t, destroy, void, + private_private_key_t *this) +{ + if (ref_put(&this->ref)) + { + lib->encoding->clear_cache(lib->encoding, this); + botan_privkey_destroy(this->key); + free(this); + } +} + +/** + * Internal generic constructor + */ +static private_private_key_t *create_empty() +{ + private_private_key_t *this; + + INIT(this, + .public = { + .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; +} + +/* + * Described in header + */ +private_key_t *botan_ed_private_key_adopt(botan_privkey_t key) +{ + private_private_key_t *this; + + this = create_empty(); + this->key = key; + + return &this->public; +} + +/* + * Described in header + */ +private_key_t *botan_ed_private_key_gen(key_type_t type, va_list args) +{ + private_private_key_t *this; + botan_rng_t rng; + + while (TRUE) + { + switch (va_arg(args, builder_part_t)) + { + case BUILD_KEY_SIZE: + /* just ignore the key size */ + va_arg(args, u_int); + continue; + case BUILD_END: + break; + default: + return NULL; + } + break; + } + + if (botan_rng_init(&rng, "system")) + { + return NULL; + } + + this = create_empty(); + + if (botan_privkey_create(&this->key, "Ed25519", NULL, rng)) + { + DBG1(DBG_LIB, "EdDSA private key generation failed"); + botan_rng_destroy(rng); + free(this); + return NULL; + } + + botan_rng_destroy(rng); + return &this->public; +} + +/* + * Described in header + */ +private_key_t *botan_ed_private_key_load(key_type_t type, va_list args) +{ + private_private_key_t *this; + chunk_t key = chunk_empty; + + while (TRUE) + { + switch (va_arg(args, builder_part_t)) + { + case BUILD_EDDSA_PRIV_ASN1_DER: + key = va_arg(args, chunk_t); + continue; + case BUILD_END: + break; + default: + return NULL; + } + break; + } + + /* PKCS#8-encoded keys are handled generically, so we only handle the + * explicit case */ + if (asn1_unwrap(&key, &key) != ASN1_OCTET_STRING || + key.len != ED25519_KEY_LEN) + { + return NULL; + } + + this = create_empty(); + + if (botan_privkey_load_ed25519(&this->key, key.ptr)) + { + free(this); + return NULL; + } + return &this->public; +} + +#endif diff --git a/src/libstrongswan/plugins/botan/botan_ed_private_key.h b/src/libstrongswan/plugins/botan/botan_ed_private_key.h new file mode 100644 index 000000000..f7f32e8f3 --- /dev/null +++ b/src/libstrongswan/plugins/botan/botan_ed_private_key.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2018 Tobias Brunner + * HSR Hochschule fuer Technik Rapperswil + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/** + * @defgroup botan_ed_private_key botan_ed_private_key + * @{ @ingroup botan_p + */ + +#ifndef BOTAN_ED_PRIVATE_KEY_H_ +#define BOTAN_ED_PRIVATE_KEY_H_ + +#include <botan/ffi.h> + +#include <credentials/builder.h> +#include <credentials/keys/private_key.h> + +/** + * Generate an EdDSA private key using Botan. + * + * @param type type of the key, must be KEY_ED25519 + * @param args builder_part_t argument list + * @return generated key, NULL on failure + */ +private_key_t *botan_ed_private_key_gen(key_type_t type, va_list args); + +/** + * Load an EdDSA private key using Botan. + * + * @param type type of the key, must be KEY_ED25519 + * @param args builder_part_t argument list + * @return loaded key, NULL on failure + */ +private_key_t *botan_ed_private_key_load(key_type_t type, va_list args); + +/** + * Load an EdDSA private key by adopting a botan_privkey_t object. + * + * @param key private key object (adopted) + * @return loaded key, NULL on failure + */ +private_key_t *botan_ed_private_key_adopt(botan_privkey_t key); + +#endif /** BOTAN_ED_PRIVATE_KEY_H_ @}*/ diff --git a/src/libstrongswan/plugins/botan/botan_ed_public_key.c b/src/libstrongswan/plugins/botan/botan_ed_public_key.c new file mode 100644 index 000000000..41d2baae8 --- /dev/null +++ b/src/libstrongswan/plugins/botan/botan_ed_public_key.c @@ -0,0 +1,202 @@ +/* + * Copyright (C) 2018 Tobias Brunner + * HSR Hochschule fuer Technik Rapperswil + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "botan_ed_public_key.h" +#include "botan_util.h" + +#include <botan/build.h> + +#ifdef BOTAN_HAS_ED25519 + +#include <utils/debug.h> + +typedef struct private_public_key_t private_public_key_t; + +/** + * Private data + */ +struct private_public_key_t { + + /** + * Public interface + */ + public_key_t public; + + /** + * Botan public key object + */ + botan_pubkey_t key; + + /** + * Reference counter + */ + refcount_t ref; +}; + +METHOD(public_key_t, get_type, key_type_t, + private_public_key_t *this) +{ + return KEY_ED25519; +} + +METHOD(public_key_t, get_keysize, int, + private_public_key_t *this) +{ + return ED25519_KEY_LEN * 8; +} + +METHOD(public_key_t, verify, bool, + private_public_key_t *this, signature_scheme_t scheme, + void *params, chunk_t data, chunk_t signature) +{ + switch (scheme) + { + case SIGN_ED25519: + return botan_verify_signature(this->key, "Pure", 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_public_key_t *this, encryption_scheme_t scheme, + chunk_t crypto, chunk_t *plain) +{ + DBG1(DBG_LIB, "EdDSA public key encryption not implemented"); + return FALSE; +} + +METHOD(public_key_t, get_fingerprint, bool, + private_public_key_t *this, cred_encoding_type_t type, + chunk_t *fingerprint) +{ + return botan_get_fingerprint(this->key, this, type, fingerprint); +} + +METHOD(public_key_t, get_encoding, bool, + private_public_key_t *this, cred_encoding_type_t type, + chunk_t *encoding) +{ + return botan_get_encoding(this->key, type, encoding); +} + +METHOD(public_key_t, get_ref, public_key_t*, + private_public_key_t *this) +{ + ref_get(&this->ref); + return &this->public; +} + +METHOD(public_key_t, destroy, void, + private_public_key_t *this) +{ + if (ref_put(&this->ref)) + { + lib->encoding->clear_cache(lib->encoding, this); + botan_pubkey_destroy(this->key); + free(this); + } +} + +/** + * Internal generic constructor + */ +static private_public_key_t *create_empty() +{ + private_public_key_t *this; + + INIT(this, + .public = { + .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, + ); + + return this; +} + +/* + * Described in header + */ +public_key_t *botan_ed_public_key_adopt(botan_pubkey_t key) +{ + private_public_key_t *this; + + this = create_empty(); + this->key = key; + + return &this->public; +} + +/* + * Described in header + */ +public_key_t *botan_ed_public_key_load(key_type_t type, va_list args) +{ + private_public_key_t *this; + chunk_t key = chunk_empty; + + while (TRUE) + { + switch (va_arg(args, builder_part_t)) + { + case BUILD_EDDSA_PUB: + key = va_arg(args, chunk_t); + continue; + case BUILD_END: + break; + default: + return NULL; + } + break; + } + + /* ASN.1-encoded keys are handled generically, so we only handle the + * explicit case */ + if (key.len != ED25519_KEY_LEN) + { + return NULL; + } + + this = create_empty(); + + if (botan_pubkey_load_ed25519(&this->key, key.ptr)) + { + free(this); + return NULL; + } + return &this->public; +} + +#endif diff --git a/src/libstrongswan/plugins/botan/botan_ed_public_key.h b/src/libstrongswan/plugins/botan/botan_ed_public_key.h new file mode 100644 index 000000000..0f44b1afb --- /dev/null +++ b/src/libstrongswan/plugins/botan/botan_ed_public_key.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2018 Tobias Brunner + * HSR Hochschule fuer Technik Rapperswil + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef BOTAN_ED_PUBLIC_KEY_H_ +#define BOTAN_ED_PUBLIC_KEY_H_ + +#include <botan/ffi.h> + +#include <credentials/builder.h> +#include <credentials/keys/public_key.h> + +#define ED25519_KEY_LEN 32 + +/** + * Load an EdDSA public key by adopting a botan_pubkey_t object. + * + * @param key public key object (adopted) + * @return loaded key, NULL on failure + */ +public_key_t *botan_ed_public_key_adopt(botan_pubkey_t key); + +/** + * Load an EdDSA public key using Botan. + * + * @param type type of the key, must be KEY_ED25519 + * @param args builder_part_t argument list + * @return loaded key, NULL on failure + */ +public_key_t *botan_ed_public_key_load(key_type_t type, va_list args); + +#endif /** BOTAN_ED_PUBLIC_KEY_H_ @}*/ diff --git a/src/libstrongswan/plugins/botan/botan_plugin.c b/src/libstrongswan/plugins/botan/botan_plugin.c index fd8e5f5a6..f045ba074 100644 --- a/src/libstrongswan/plugins/botan/botan_plugin.c +++ b/src/libstrongswan/plugins/botan/botan_plugin.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2018 Tobias Brunner + * Copyright (C) 2018 Andreas Steffen * HSR Hochschule fuer Technik Rapperswil * * Copyright (C) 2018 René Korthaus @@ -36,7 +37,9 @@ #include "botan_ec_diffie_hellman.h" #include "botan_ec_public_key.h" #include "botan_ec_private_key.h" -#include "botan_gcm.h" +#include "botan_ed_public_key.h" +#include "botan_ed_private_key.h" +#include "botan_aead.h" #include "botan_util_keys.h" #include "botan_x25519.h" @@ -101,6 +104,7 @@ METHOD(plugin_t, get_features, int, #endif /* crypters */ +#if defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_MODE_CBC) PLUGIN_REGISTER(CRYPTER, botan_crypter_create), #ifdef BOTAN_HAS_AES #ifdef BOTAN_HAS_MODE_CBC @@ -108,17 +112,43 @@ METHOD(plugin_t, get_features, int, PLUGIN_PROVIDE(CRYPTER, ENCR_AES_CBC, 24), PLUGIN_PROVIDE(CRYPTER, ENCR_AES_CBC, 32), #endif +#endif +#endif + + /* AEAD */ +#if (defined(BOTAN_HAS_AES) && \ + (defined(BOTAN_HAS_AEAD_GCM) || defined(BOTAN_HAS_AEAD_CCM))) || \ + defined(BOTAN_HAS_AEAD_CHACHA20_POLY1305) + PLUGIN_REGISTER(AEAD, botan_aead_create), +#ifdef BOTAN_HAS_AES #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), + PLUGIN_PROVIDE(AEAD, ENCR_AES_GCM_ICV8, 16), + PLUGIN_PROVIDE(AEAD, ENCR_AES_GCM_ICV8, 24), + PLUGIN_PROVIDE(AEAD, ENCR_AES_GCM_ICV8, 32), #endif + #ifdef BOTAN_HAS_AEAD_CCM + PLUGIN_PROVIDE(AEAD, ENCR_AES_CCM_ICV16, 16), + PLUGIN_PROVIDE(AEAD, ENCR_AES_CCM_ICV16, 24), + PLUGIN_PROVIDE(AEAD, ENCR_AES_CCM_ICV16, 32), + PLUGIN_PROVIDE(AEAD, ENCR_AES_CCM_ICV12, 16), + PLUGIN_PROVIDE(AEAD, ENCR_AES_CCM_ICV12, 24), + PLUGIN_PROVIDE(AEAD, ENCR_AES_CCM_ICV12, 32), + PLUGIN_PROVIDE(AEAD, ENCR_AES_CCM_ICV8, 16), + PLUGIN_PROVIDE(AEAD, ENCR_AES_CCM_ICV8, 24), + PLUGIN_PROVIDE(AEAD, ENCR_AES_CCM_ICV8, 32), + #endif +#endif +#ifdef BOTAN_HAS_AEAD_CHACHA20_POLY1305 + PLUGIN_PROVIDE(AEAD, ENCR_CHACHA20_POLY1305, 32), #endif +#endif + /* hashers */ PLUGIN_REGISTER(HASHER, botan_hasher_create), #ifdef BOTAN_HAS_MD5 @@ -135,6 +165,13 @@ METHOD(plugin_t, get_features, int, PLUGIN_PROVIDE(HASHER, HASH_SHA384), PLUGIN_PROVIDE(HASHER, HASH_SHA512), #endif +#ifdef BOTAN_HAS_SHA3 + PLUGIN_PROVIDE(HASHER, HASH_SHA3_224), + PLUGIN_PROVIDE(HASHER, HASH_SHA3_256), + PLUGIN_PROVIDE(HASHER, HASH_SHA3_384), + PLUGIN_PROVIDE(HASHER, HASH_SHA3_512), +#endif + /* prfs */ #ifdef BOTAN_HAS_HMAC PLUGIN_REGISTER(PRF, botan_hmac_prf_create), @@ -168,7 +205,8 @@ METHOD(plugin_t, get_features, int, #endif /* BOTAN_HAS_HMAC */ /* generic key loaders */ -#if defined (BOTAN_HAS_RSA) || defined(BOTAN_HAS_ECDSA) +#if defined (BOTAN_HAS_RSA) || defined(BOTAN_HAS_ECDSA) || \ + defined(BOTAN_HAS_ED25519) PLUGIN_REGISTER(PUBKEY, botan_public_key_load, TRUE), PLUGIN_PROVIDE(PUBKEY, KEY_ANY), #ifdef BOTAN_HAS_RSA @@ -177,6 +215,9 @@ METHOD(plugin_t, get_features, int, #ifdef BOTAN_HAS_ECDSA PLUGIN_PROVIDE(PUBKEY, KEY_ECDSA), #endif +#ifdef BOTAN_HAS_ED25519 + PLUGIN_PROVIDE(PUBKEY, KEY_ED25519), +#endif PLUGIN_REGISTER(PRIVKEY, botan_private_key_load, TRUE), PLUGIN_PROVIDE(PRIVKEY, KEY_ANY), #ifdef BOTAN_HAS_RSA @@ -185,6 +226,9 @@ METHOD(plugin_t, get_features, int, #ifdef BOTAN_HAS_ECDSA PLUGIN_PROVIDE(PRIVKEY, KEY_ECDSA), #endif +#ifdef BOTAN_HAS_ED25519 + PLUGIN_PROVIDE(PRIVKEY, KEY_ED25519), +#endif #endif /* RSA */ #ifdef BOTAN_HAS_RSA @@ -218,6 +262,16 @@ METHOD(plugin_t, get_features, int, PLUGIN_PROVIDE(PUBKEY_VERIFY, SIGN_RSA_EMSA_PKCS1_SHA2_384), PLUGIN_PROVIDE(PUBKEY_VERIFY, SIGN_RSA_EMSA_PKCS1_SHA2_512), #endif +#ifdef BOTAN_HAS_SHA3 + PLUGIN_PROVIDE(PRIVKEY_SIGN, SIGN_RSA_EMSA_PKCS1_SHA3_224), + PLUGIN_PROVIDE(PRIVKEY_SIGN, SIGN_RSA_EMSA_PKCS1_SHA3_256), + PLUGIN_PROVIDE(PRIVKEY_SIGN, SIGN_RSA_EMSA_PKCS1_SHA3_384), + PLUGIN_PROVIDE(PRIVKEY_SIGN, SIGN_RSA_EMSA_PKCS1_SHA3_512), + PLUGIN_PROVIDE(PUBKEY_VERIFY, SIGN_RSA_EMSA_PKCS1_SHA3_224), + PLUGIN_PROVIDE(PUBKEY_VERIFY, SIGN_RSA_EMSA_PKCS1_SHA3_256), + PLUGIN_PROVIDE(PUBKEY_VERIFY, SIGN_RSA_EMSA_PKCS1_SHA3_384), + PLUGIN_PROVIDE(PUBKEY_VERIFY, SIGN_RSA_EMSA_PKCS1_SHA3_512), +#endif #endif #ifdef BOTAN_HAS_EMSA_PSSR PLUGIN_PROVIDE(PRIVKEY_SIGN, SIGN_RSA_EMSA_PSS), @@ -272,6 +326,21 @@ METHOD(plugin_t, get_features, int, #endif /* BOTAN_HAS_EMSA1 */ #endif /* BOTAN_HAS_ECDSA */ +#ifdef BOTAN_HAS_ED25519 + /* EdDSA private/public key loading */ + PLUGIN_REGISTER(PUBKEY, botan_ed_public_key_load, TRUE), + PLUGIN_PROVIDE(PUBKEY, KEY_ED25519), + PLUGIN_REGISTER(PRIVKEY, botan_ed_private_key_load, TRUE), + PLUGIN_PROVIDE(PRIVKEY, KEY_ED25519), + PLUGIN_REGISTER(PRIVKEY_GEN, botan_ed_private_key_gen, FALSE), + PLUGIN_PROVIDE(PRIVKEY_GEN, KEY_ED25519), + PLUGIN_PROVIDE(PRIVKEY_SIGN, SIGN_ED25519), + PLUGIN_PROVIDE(PUBKEY_VERIFY, SIGN_ED25519), + /* register a pro forma identity hasher, never instantiated */ + PLUGIN_REGISTER(HASHER, return_null), + PLUGIN_PROVIDE(HASHER, HASH_IDENTITY), +#endif + /* random numbers */ #if BOTAN_HAS_SYSTEM_RNG #if BOTAN_HAS_HMAC_DRBG diff --git a/src/libstrongswan/plugins/botan/botan_rsa_private_key.c b/src/libstrongswan/plugins/botan/botan_rsa_private_key.c index bb723ff95..02820b297 100644 --- a/src/libstrongswan/plugins/botan/botan_rsa_private_key.c +++ b/src/libstrongswan/plugins/botan/botan_rsa_private_key.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2018 Tobias Brunner + * Copyright (C) 2018 Andreas Steffen * HSR Hochschule fuer Technik Rapperswil * * Copyright (C) 2018 René Korthaus @@ -84,13 +85,8 @@ bool botan_emsa_pss_identifier(rsa_pss_params_t *params, char *id, size_t len) { return FALSE; } - - if (params->salt_len > RSA_PSS_SALT_LEN_DEFAULT) - { - return snprintf(id, len, "EMSA-PSS(%s,MGF1,%zd)", hash, - params->salt_len) < len; - } - return snprintf(id, len, "EMSA-PSS(%s,MGF1)", hash) < len; + return snprintf(id, len, "EMSA-PSS(%s,MGF1,%zd)", hash, + params->salt_len) < len; } /** @@ -140,6 +136,18 @@ METHOD(private_key_t, sign, bool, case SIGN_RSA_EMSA_PKCS1_SHA2_512: return botan_get_signature(this->key, "EMSA_PKCS1(SHA-512)", data, signature); + case SIGN_RSA_EMSA_PKCS1_SHA3_224: + return botan_get_signature(this->key, "EMSA_PKCS1(SHA-3(224))", data, + signature); + case SIGN_RSA_EMSA_PKCS1_SHA3_256: + return botan_get_signature(this->key, "EMSA_PKCS1(SHA-3(256))", data, + signature); + case SIGN_RSA_EMSA_PKCS1_SHA3_384: + return botan_get_signature(this->key, "EMSA_PKCS1(SHA-3(384))", data, + signature); + case SIGN_RSA_EMSA_PKCS1_SHA3_512: + return botan_get_signature(this->key, "EMSA_PKCS1(SHA-3(512))", data, + signature); case SIGN_RSA_EMSA_PSS: return build_emsa_pss_signature(this, params, data, signature); default: @@ -617,7 +625,7 @@ botan_rsa_private_key_t *botan_rsa_private_key_load(key_type_t type, if (n.ptr && e.ptr && d.ptr) { - botan_mp_t n_mp, e_mp, d_mp, p_mp, q_mp; + botan_mp_t n_mp, e_mp, d_mp, p_mp = NULL, q_mp = NULL; if (!chunk_to_botan_mp(n, &n_mp)) { diff --git a/src/libstrongswan/plugins/botan/botan_rsa_public_key.c b/src/libstrongswan/plugins/botan/botan_rsa_public_key.c index c6e2e8861..244caa585 100644 --- a/src/libstrongswan/plugins/botan/botan_rsa_public_key.c +++ b/src/libstrongswan/plugins/botan/botan_rsa_public_key.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2018 Tobias Brunner + * Copyright (C) 2018 Andreas Steffen * HSR Hochschule fuer Technik Rapperswil * * Copyright (C) 2018 René Korthaus @@ -69,33 +70,6 @@ struct private_botan_rsa_public_key_t { bool botan_emsa_pss_identifier(rsa_pss_params_t *params, char *id, size_t len); /** - * 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 PSS signature described in PKCS#1 */ static bool verify_emsa_pss_signature(private_botan_rsa_public_key_t *this, @@ -109,7 +83,7 @@ static bool verify_emsa_pss_signature(private_botan_rsa_public_key_t *this, { return FALSE; } - return verify_rsa_signature(this, hash_and_padding, data, signature); + return botan_verify_signature(this->key, hash_and_padding, data, signature); } METHOD(public_key_t, get_type, key_type_t, @@ -125,23 +99,35 @@ METHOD(public_key_t, verify, bool, switch (scheme) { case SIGN_RSA_EMSA_PKCS1_NULL: - return verify_rsa_signature(this, "EMSA_PKCS1(Raw)", data, - signature); + return botan_verify_signature(this->key, "EMSA_PKCS1(Raw)", data, + signature); case SIGN_RSA_EMSA_PKCS1_SHA1: - return verify_rsa_signature(this, "EMSA_PKCS1(SHA-1)", data, - signature); + return botan_verify_signature(this->key, "EMSA_PKCS1(SHA-1)", data, + signature); case SIGN_RSA_EMSA_PKCS1_SHA2_224: - return verify_rsa_signature(this, "EMSA_PKCS1(SHA-224)", - data, signature); + return botan_verify_signature(this->key, "EMSA_PKCS1(SHA-224)", + data, signature); case SIGN_RSA_EMSA_PKCS1_SHA2_256: - return verify_rsa_signature(this, "EMSA_PKCS1(SHA-256)", - data, signature); + return botan_verify_signature(this->key, "EMSA_PKCS1(SHA-256)", + data, signature); case SIGN_RSA_EMSA_PKCS1_SHA2_384: - return verify_rsa_signature(this, "EMSA_PKCS1(SHA-384)", - data, signature); + return botan_verify_signature(this->key, "EMSA_PKCS1(SHA-384)", + data, signature); case SIGN_RSA_EMSA_PKCS1_SHA2_512: - return verify_rsa_signature(this, "EMSA_PKCS1(SHA-512)", - data, signature); + return botan_verify_signature(this->key, "EMSA_PKCS1(SHA-512)", + data, signature); + case SIGN_RSA_EMSA_PKCS1_SHA3_224: + return botan_verify_signature(this->key, "EMSA_PKCS1(SHA-3(224)", + data, signature); + case SIGN_RSA_EMSA_PKCS1_SHA3_256: + return botan_verify_signature(this->key, "EMSA_PKCS1(SHA-3(256))", + data, signature); + case SIGN_RSA_EMSA_PKCS1_SHA3_384: + return botan_verify_signature(this->key, "EMSA_PKCS1(SHA-3(384))", + data, signature); + case SIGN_RSA_EMSA_PKCS1_SHA3_512: + return botan_verify_signature(this->key, "EMSA_PKCS1(SHA-3(512))", + data, signature); case SIGN_RSA_EMSA_PSS: return verify_emsa_pss_signature(this, params, data, signature); default: diff --git a/src/libstrongswan/plugins/botan/botan_util.c b/src/libstrongswan/plugins/botan/botan_util.c index 5e18405d7..f5728e43e 100644 --- a/src/libstrongswan/plugins/botan/botan_util.c +++ b/src/libstrongswan/plugins/botan/botan_util.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2018 Tobias Brunner + * Copyright (C) 2018 Andreas Steffen * HSR Hochschule fuer Technik Rapperswil * * Copyright (C) 2018 René Korthaus @@ -67,6 +68,14 @@ const char *botan_get_hash(hash_algorithm_t hash) return "SHA-384"; case HASH_SHA512: return "SHA-512"; + case HASH_SHA3_224: + return "SHA-3(224)"; + case HASH_SHA3_256: + return "SHA-3(256)"; + case HASH_SHA3_384: + return "SHA-3(384)"; + case HASH_SHA3_512: + return "SHA-3(512)"; default: return NULL; } @@ -252,6 +261,32 @@ bool botan_get_signature(botan_privkey_t key, const char *scheme, /* * Described in header */ +bool botan_verify_signature(botan_pubkey_t key, const char *scheme, + chunk_t data, chunk_t signature) +{ + botan_pk_op_verify_t verify_op; + bool valid = FALSE; + + if (botan_pk_op_verify_create(&verify_op, key, scheme, 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; +} + +/* + * Described in header + */ bool botan_dh_key_derivation(botan_privkey_t key, chunk_t pub, chunk_t *secret) { botan_pk_op_ka_t ka; diff --git a/src/libstrongswan/plugins/botan/botan_util.h b/src/libstrongswan/plugins/botan/botan_util.h index 08830356e..7fb74ec5d 100644 --- a/src/libstrongswan/plugins/botan/botan_util.h +++ b/src/libstrongswan/plugins/botan/botan_util.h @@ -101,6 +101,18 @@ bool botan_get_signature(botan_privkey_t key, const char *scheme, chunk_t data, chunk_t *signature); /** + * Verify the given signature using the provided data and key with the specified + * signature scheme (hash/padding). + * + * @param key private key object + * @param scheme hash/padding algorithm + * @param data signed data + * @param signature signature to verify + */ +bool botan_verify_signature(botan_pubkey_t key, const char* scheme, + chunk_t data, chunk_t signature); + +/** * Do the Diffie-Hellman key derivation using the given private key and public * value. * diff --git a/src/libstrongswan/plugins/botan/botan_util_keys.c b/src/libstrongswan/plugins/botan/botan_util_keys.c index 176c2caf9..dc4031491 100644 --- a/src/libstrongswan/plugins/botan/botan_util_keys.c +++ b/src/libstrongswan/plugins/botan/botan_util_keys.c @@ -24,6 +24,8 @@ #include "botan_util_keys.h" #include "botan_ec_public_key.h" #include "botan_ec_private_key.h" +#include "botan_ed_public_key.h" +#include "botan_ed_private_key.h" #include "botan_rsa_public_key.h" #include "botan_rsa_private_key.h" @@ -104,15 +106,27 @@ public_key_t *botan_public_key_load(key_type_t type, va_list args) return NULL; } +#ifdef BOTAN_HAS_RSA if (streq(name, "RSA") && (type == KEY_ANY || type == KEY_RSA)) { this = (public_key_t*)botan_rsa_public_key_adopt(pubkey); } - else if (streq(name, "ECDSA") && (type == KEY_ANY || type == KEY_ECDSA)) + else +#endif +#ifdef BOTAN_HAS_ECDSA + if (streq(name, "ECDSA") && (type == KEY_ANY || type == KEY_ECDSA)) { this = (public_key_t*)botan_ec_public_key_adopt(pubkey); } else +#endif +#ifdef BOTAN_HAS_ED25519 + if (streq(name, "Ed25519") && (type == KEY_ANY || type == KEY_ED25519)) + { + this = botan_ed_public_key_adopt(pubkey); + } + else +#endif { botan_pubkey_destroy(pubkey); } @@ -120,6 +134,7 @@ public_key_t *botan_public_key_load(key_type_t type, va_list args) return this; } +#ifdef BOTAN_HAS_ECDSA /** * Determine the curve OID from a PKCS#8 structure */ @@ -139,6 +154,7 @@ static int determine_ec_oid(chunk_t pkcs8) } return oid; } +#endif /* * Described in header @@ -151,7 +167,6 @@ private_key_t *botan_private_key_load(key_type_t type, va_list args) chunk_t blob = chunk_empty; botan_rng_t rng; char *name; - int oid; while (TRUE) { @@ -188,20 +203,35 @@ private_key_t *botan_private_key_load(key_type_t type, va_list args) botan_pubkey_destroy(pubkey); if (!name) { + botan_privkey_destroy(key); return NULL; } + +#ifdef BOTAN_HAS_RSA if (streq(name, "RSA") && (type == KEY_ANY || type == KEY_RSA)) { this = (private_key_t*)botan_rsa_private_key_adopt(key); } - else if (streq(name, "ECDSA") && (type == KEY_ANY || type == KEY_ECDSA)) + else +#endif +#ifdef BOTAN_HAS_ECDSA + if (streq(name, "ECDSA") && (type == KEY_ANY || type == KEY_ECDSA)) { - oid = determine_ec_oid(blob); + int oid = determine_ec_oid(blob); if (oid != OID_UNKNOWN) { this = (private_key_t*)botan_ec_private_key_adopt(key, oid); } } + else +#endif +#ifdef BOTAN_HAS_ED25519 + if (streq(name, "Ed25519") && (type == KEY_ANY || type == KEY_ED25519)) + { + this = botan_ed_private_key_adopt(key); + } +#endif + if (!this) { botan_privkey_destroy(key); diff --git a/src/libstrongswan/plugins/curve25519/curve25519_public_key.c b/src/libstrongswan/plugins/curve25519/curve25519_public_key.c index 1d4dec565..dfc1df4d0 100644 --- a/src/libstrongswan/plugins/curve25519/curve25519_public_key.c +++ b/src/libstrongswan/plugins/curve25519/curve25519_public_key.c @@ -1,4 +1,5 @@ /* + * Copyright (C) 2018 Tobias Brunner * Copyright (C) 2016 Andreas Steffen * HSR Hochschule fuer Technik Rapperswil * @@ -48,6 +49,13 @@ METHOD(public_key_t, get_type, key_type_t, return KEY_ED25519; } +/* L = 2^252+27742317777372353535851937790883648493 in little-endian form */ +static chunk_t curve25519_order = chunk_from_chars( + 0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, + 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10); + METHOD(public_key_t, verify, bool, private_curve25519_public_key_t *this, signature_scheme_t scheme, void *params, chunk_t data, chunk_t signature) @@ -93,6 +101,20 @@ METHOD(public_key_t, verify, bool, { return FALSE; } + /* make sure 0 <= s < L, as per RFC 8032, section 5.1.7 to prevent signature + * malleability. Due to the three-bit check above (forces s < 2^253) there + * is not that much room, but adding L once works with most signatures */ + for (i = 31; ; i--) + { + if (sig[i+32] < curve25519_order.ptr[i]) + { + break; + } + else if (sig[i+32] > curve25519_order.ptr[i] || i == 0) + { + return FALSE; + } + } hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA512); if (!hasher) @@ -200,22 +222,68 @@ static const asn1Object_t pubkeyObjects[] = { #define ED25519_SUBJECT_PUBLIC_KEY 2 /** + * Parse the ASN.1-encoded subjectPublicKeyInfo + */ +static bool parse_public_key_info(private_curve25519_public_key_t *this, + chunk_t blob) +{ + asn1_parser_t *parser; + chunk_t object; + bool success = FALSE; + int objectID, oid; + + parser = asn1_parser_create(pubkeyObjects, blob); + + while (parser->iterate(parser, &objectID, &object)) + { + switch (objectID) + { + case ED25519_SUBJECT_PUBLIC_KEY_ALGORITHM: + { + oid = asn1_parse_algorithmIdentifier(object, + parser->get_level(parser) + 1, NULL); + if (oid != OID_ED25519) + { + goto end; + } + break; + } + case ED25519_SUBJECT_PUBLIC_KEY: + { + /* encoded as an ASN1 BIT STRING */ + if (object.len != 1 + ED25519_KEY_LEN) + { + goto end; + } + this->pubkey = chunk_clone(chunk_skip(object, 1)); + break; + } + } + } + success = parser->success(parser); + +end: + parser->destroy(parser); + return success; +} + +/** * See header. */ curve25519_public_key_t *curve25519_public_key_load(key_type_t type, va_list args) { private_curve25519_public_key_t *this; - chunk_t blob = chunk_empty, object; - asn1_parser_t *parser; - bool success = FALSE; - int objectID, oid; + chunk_t asn1 = chunk_empty, blob = chunk_empty; while (TRUE) { switch (va_arg(args, builder_part_t)) { case BUILD_BLOB_ASN1_DER: + asn1 = va_arg(args, chunk_t); + continue; + case BUILD_EDDSA_PUB: blob = va_arg(args, chunk_t); continue; case BUILD_END: @@ -244,39 +312,11 @@ curve25519_public_key_t *curve25519_public_key_load(key_type_t type, .ref = 1, ); - parser = asn1_parser_create(pubkeyObjects, blob); - - while (parser->iterate(parser, &objectID, &object)) + if (blob.len == ED25519_KEY_LEN) { - switch (objectID) - { - case ED25519_SUBJECT_PUBLIC_KEY_ALGORITHM: - { - oid = asn1_parse_algorithmIdentifier(object, - parser->get_level(parser) + 1, NULL); - if (oid != OID_ED25519) - { - goto end; - } - break; - } - case ED25519_SUBJECT_PUBLIC_KEY: - { - /* encoded as an ASN1 BIT STRING */ - if (object.len != 1 + ED25519_KEY_LEN) - { - goto end; - } - this->pubkey = chunk_clone(chunk_skip(object, 1)); - break; - } - } + this->pubkey = chunk_clone(blob); } - success = parser->success(parser); - -end: - parser->destroy(parser); - if (!success) + else if (!asn1.len || !parse_public_key_info(this, asn1)) { destroy(this); return NULL; diff --git a/src/libstrongswan/plugins/gcrypt/gcrypt_plugin.c b/src/libstrongswan/plugins/gcrypt/gcrypt_plugin.c index 45fba242b..6946e4576 100644 --- a/src/libstrongswan/plugins/gcrypt/gcrypt_plugin.c +++ b/src/libstrongswan/plugins/gcrypt/gcrypt_plugin.c @@ -43,10 +43,12 @@ struct private_gcrypt_plugin_t { gcrypt_plugin_t public; }; +#if GCRYPT_VERSION_NUMBER < 0x010600 /** * Define gcrypt multi-threading callbacks as gcry_threads_pthread */ GCRY_THREAD_OPTION_PTHREAD_IMPL; +#endif METHOD(plugin_t, get_name, char*, private_gcrypt_plugin_t *this) @@ -163,7 +165,9 @@ plugin_t *gcrypt_plugin_create() { private_gcrypt_plugin_t *this; +#if GCRYPT_VERSION_NUMBER < 0x010600 gcry_control(GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread); +#endif if (!gcry_check_version(GCRYPT_VERSION)) { diff --git a/src/libstrongswan/plugins/gcrypt/gcrypt_rsa_private_key.c b/src/libstrongswan/plugins/gcrypt/gcrypt_rsa_private_key.c index c06f43348..394b87c27 100644 --- a/src/libstrongswan/plugins/gcrypt/gcrypt_rsa_private_key.c +++ b/src/libstrongswan/plugins/gcrypt/gcrypt_rsa_private_key.c @@ -187,11 +187,7 @@ static bool sign_pkcs1(private_gcrypt_rsa_private_key_t *this, } else { - u_int slen = hasher_hash_size(hash_algorithm); - if (pss->salt_len > RSA_PSS_SALT_LEN_DEFAULT) - { - slen = pss->salt_len; - } + u_int slen = pss->salt_len; err = gcry_sexp_build(&in, NULL, "(data(flags pss)(salt-length %u)(hash %s %b))", slen, hash_name, hash.len, hash.ptr); diff --git a/src/libstrongswan/plugins/gcrypt/gcrypt_rsa_public_key.c b/src/libstrongswan/plugins/gcrypt/gcrypt_rsa_public_key.c index 9e2ac1287..bbfa5e298 100644 --- a/src/libstrongswan/plugins/gcrypt/gcrypt_rsa_public_key.c +++ b/src/libstrongswan/plugins/gcrypt/gcrypt_rsa_public_key.c @@ -139,11 +139,7 @@ static bool verify_pkcs1(private_gcrypt_rsa_public_key_t *this, if (pss) { - u_int slen = hasher_hash_size(algorithm); - if (pss->salt_len > RSA_PSS_SALT_LEN_DEFAULT) - { - slen = pss->salt_len; - } + u_int slen = pss->salt_len; err = gcry_sexp_build(&in, NULL, "(data(flags pss)(salt-length %u)(hash %s %b))", slen, hash_name, hash.len, hash.ptr); diff --git a/src/libstrongswan/plugins/gmp/gmp_rsa_private_key.c b/src/libstrongswan/plugins/gmp/gmp_rsa_private_key.c index a255a40ab..2d2d5c6fb 100644 --- a/src/libstrongswan/plugins/gmp/gmp_rsa_private_key.c +++ b/src/libstrongswan/plugins/gmp/gmp_rsa_private_key.c @@ -393,15 +393,11 @@ static bool build_emsa_pss_signature(private_gmp_rsa_private_key_t *this, goto error; } - salt.len = hash.len; + salt.len = params->salt_len; if (params->salt.len) { salt = params->salt; } - else if (params->salt_len > RSA_PSS_SALT_LEN_DEFAULT) - { - salt.len = params->salt_len; - } if (emlen < (hash.len + salt.len + 2)) { /* too long */ goto error; diff --git a/src/libstrongswan/plugins/gmp/gmp_rsa_public_key.c b/src/libstrongswan/plugins/gmp/gmp_rsa_public_key.c index 9b5ee67fa..f9bd1d314 100644 --- a/src/libstrongswan/plugins/gmp/gmp_rsa_public_key.c +++ b/src/libstrongswan/plugins/gmp/gmp_rsa_public_key.c @@ -205,12 +205,7 @@ static bool verify_emsa_pss_signature(private_gmp_rsa_public_key_t *this, { goto error; } - /* determine salt length */ - salt.len = hash.len; - if (params->salt_len > RSA_PSS_SALT_LEN_DEFAULT) - { - salt.len = params->salt_len; - } + salt.len = params->salt_len; /* verify general structure of EM */ maskbits = (8 * em.len) - embits; if (em.len < (hash.len + salt.len + 2) || em.ptr[em.len-1] != 0xbc || diff --git a/src/libstrongswan/plugins/mysql/mysql_database.c b/src/libstrongswan/plugins/mysql/mysql_database.c index d7e35d9fd..90f8185b0 100644 --- a/src/libstrongswan/plugins/mysql/mysql_database.c +++ b/src/libstrongswan/plugins/mysql/mysql_database.c @@ -131,9 +131,13 @@ typedef struct { */ static void conn_release(private_mysql_database_t *this, conn_t *conn) { - this->mutex->lock(this->mutex); - conn->in_use = FALSE; - this->mutex->unlock(this->mutex); + /* do not release the connection while transactions are using it */ + if (!this->transaction->get(this->transaction)) + { + this->mutex->lock(this->mutex); + conn->in_use = FALSE; + this->mutex->unlock(this->mutex); + } } /** diff --git a/src/libstrongswan/plugins/openssl/Makefile.am b/src/libstrongswan/plugins/openssl/Makefile.am index 9287f788a..d484092e7 100644 --- a/src/libstrongswan/plugins/openssl/Makefile.am +++ b/src/libstrongswan/plugins/openssl/Makefile.am @@ -29,7 +29,10 @@ libstrongswan_openssl_la_SOURCES = \ openssl_pkcs12.c openssl_pkcs12.h \ openssl_rng.c openssl_rng.h \ openssl_hmac.c openssl_hmac.h \ - openssl_gcm.c openssl_gcm.h + openssl_gcm.c openssl_gcm.h \ + openssl_x_diffie_hellman.c openssl_x_diffie_hellman.h \ + openssl_ed_private_key.c openssl_ed_private_key.h \ + openssl_ed_public_key.c openssl_ed_public_key.h libstrongswan_openssl_la_LDFLAGS = -module -avoid-version libstrongswan_openssl_la_LIBADD = $(OPENSSL_LIB) diff --git a/src/libstrongswan/plugins/openssl/Makefile.in b/src/libstrongswan/plugins/openssl/Makefile.in index 79be2e670..da04d17cf 100644 --- a/src/libstrongswan/plugins/openssl/Makefile.in +++ b/src/libstrongswan/plugins/openssl/Makefile.in @@ -145,7 +145,8 @@ am_libstrongswan_openssl_la_OBJECTS = openssl_plugin.lo \ openssl_ec_diffie_hellman.lo openssl_ec_private_key.lo \ openssl_ec_public_key.lo openssl_x509.lo openssl_crl.lo \ openssl_pkcs7.lo openssl_pkcs12.lo openssl_rng.lo \ - openssl_hmac.lo openssl_gcm.lo + openssl_hmac.lo openssl_gcm.lo openssl_x_diffie_hellman.lo \ + openssl_ed_private_key.lo openssl_ed_public_key.lo libstrongswan_openssl_la_OBJECTS = \ $(am_libstrongswan_openssl_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) @@ -487,7 +488,10 @@ libstrongswan_openssl_la_SOURCES = \ openssl_pkcs12.c openssl_pkcs12.h \ openssl_rng.c openssl_rng.h \ openssl_hmac.c openssl_hmac.h \ - openssl_gcm.c openssl_gcm.h + openssl_gcm.c openssl_gcm.h \ + openssl_x_diffie_hellman.c openssl_x_diffie_hellman.h \ + openssl_ed_private_key.c openssl_ed_private_key.h \ + openssl_ed_public_key.c openssl_ed_public_key.h libstrongswan_openssl_la_LDFLAGS = -module -avoid-version libstrongswan_openssl_la_LIBADD = $(OPENSSL_LIB) @@ -586,6 +590,8 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/openssl_ec_diffie_hellman.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/openssl_ec_private_key.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/openssl_ec_public_key.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/openssl_ed_private_key.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/openssl_ed_public_key.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/openssl_gcm.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/openssl_hasher.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/openssl_hmac.Plo@am__quote@ @@ -598,6 +604,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/openssl_sha1_prf.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/openssl_util.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/openssl_x509.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/openssl_x_diffie_hellman.Plo@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ diff --git a/src/libstrongswan/plugins/openssl/openssl_crl.c b/src/libstrongswan/plugins/openssl/openssl_crl.c index bb5f20dcf..3e7490dc6 100644 --- a/src/libstrongswan/plugins/openssl/openssl_crl.c +++ b/src/libstrongswan/plugins/openssl/openssl_crl.c @@ -57,6 +57,9 @@ static inline void X509_CRL_get0_signature(const X509_CRL *crl, ASN1_BIT_STRING #define X509_REVOKED_get0_serialNumber(r) ({ (r)->serialNumber; }) #define X509_REVOKED_get0_revocationDate(r) ({ (r)->revocationDate; }) #define X509_CRL_get0_extensions(c) ({ (c)->crl->extensions; }) +#define ASN1_STRING_get0_data(a) ASN1_STRING_data(a) +#define X509_CRL_get0_lastUpdate(c) X509_CRL_get_lastUpdate(c) +#define X509_CRL_get0_nextUpdate(c) X509_CRL_get_nextUpdate(c) #endif typedef struct private_openssl_crl_t private_openssl_crl_t; @@ -193,7 +196,7 @@ METHOD(enumerator_t, crl_enumerate, bool, if (ASN1_STRING_type(crlrsn) == V_ASN1_ENUMERATED && ASN1_STRING_length(crlrsn) == 1) { - *reason = *ASN1_STRING_data(crlrsn); + *reason = *ASN1_STRING_get0_data(crlrsn); } ASN1_STRING_free(crlrsn); } @@ -288,7 +291,11 @@ METHOD(certificate_t, issued_by, bool, chunk_t fingerprint, tbs; public_key_t *key; x509_t *x509; +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + const ASN1_BIT_STRING *sig; +#else ASN1_BIT_STRING *sig; +#endif bool valid; if (issuer->get_type(issuer) != CERT_X509) @@ -509,7 +516,7 @@ static bool parse_extensions(private_openssl_crl_t *this) bool ok; int i, num; X509_EXTENSION *ext; - STACK_OF(X509_EXTENSION) *extensions; + const STACK_OF(X509_EXTENSION) *extensions; extensions = X509_CRL_get0_extensions(this->crl); if (extensions) @@ -564,7 +571,11 @@ static bool parse_crl(private_openssl_crl_t *this) { const unsigned char *ptr = this->encoding.ptr; chunk_t sig_scheme; +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + const X509_ALGOR *alg; +#else X509_ALGOR *alg; +#endif this->crl = d2i_X509_CRL(NULL, &ptr, this->encoding.len); if (!this->crl) @@ -573,7 +584,7 @@ static bool parse_crl(private_openssl_crl_t *this) } X509_CRL_get0_signature(this->crl, NULL, &alg); - sig_scheme = openssl_i2chunk(X509_ALGOR, alg); + sig_scheme = openssl_i2chunk(X509_ALGOR, (X509_ALGOR*)alg); INIT(this->scheme); if (!signature_params_parse(sig_scheme, 0, this->scheme)) { @@ -588,8 +599,8 @@ static bool parse_crl(private_openssl_crl_t *this) { return FALSE; } - this->thisUpdate = openssl_asn1_to_time(X509_CRL_get_lastUpdate(this->crl)); - this->nextUpdate = openssl_asn1_to_time(X509_CRL_get_nextUpdate(this->crl)); + this->thisUpdate = openssl_asn1_to_time(X509_CRL_get0_lastUpdate(this->crl)); + this->nextUpdate = openssl_asn1_to_time(X509_CRL_get0_nextUpdate(this->crl)); return parse_extensions(this); } diff --git a/src/libstrongswan/plugins/openssl/openssl_ed_private_key.c b/src/libstrongswan/plugins/openssl/openssl_ed_private_key.c new file mode 100644 index 000000000..b5bc9b868 --- /dev/null +++ b/src/libstrongswan/plugins/openssl/openssl_ed_private_key.c @@ -0,0 +1,356 @@ +/* + * Copyright (C) 2018 Tobias Brunner + * HSR Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include <openssl/evp.h> + +#if OPENSSL_VERSION_NUMBER >= 0x1010100fL && !defined(OPENSSL_NO_EC) + +#include "openssl_ed_private_key.h" + +#include <utils/debug.h> + +typedef struct private_private_key_t private_private_key_t; + +/** + * Private data + */ +struct private_private_key_t { + + /** + * Public interface + */ + private_key_t public; + + /** + * Key object + */ + EVP_PKEY *key; + + /** + * Key type + */ + key_type_t type; + + /** + * TRUE if the key is from an OpenSSL ENGINE and might not be readable + */ + bool engine; + + /** + * reference count + */ + refcount_t ref; +}; + +/** + * We can't include asn1.h, declare function prototype directly + */ +int asn1_unwrap(chunk_t*, chunk_t*); + +/* from ed public key */ +int openssl_ed_key_type(key_type_t type); +int openssl_ed_keysize(key_type_t type); +bool openssl_ed_fingerprint(EVP_PKEY *key, cred_encoding_type_t type, chunk_t *fp); + +METHOD(private_key_t, sign, bool, + private_private_key_t *this, signature_scheme_t scheme, + void *params, chunk_t data, chunk_t *signature) +{ + EVP_MD_CTX *ctx; + bool success = FALSE; + + if ((this->type == KEY_ED25519 && scheme != SIGN_ED25519) || + (this->type == KEY_ED448 && scheme != SIGN_ED448)) + { + DBG1(DBG_LIB, "signature scheme %N not supported by %N key", + signature_scheme_names, scheme, key_type_names, this->type); + return FALSE; + } + + ctx = EVP_MD_CTX_new(); + if (!ctx || + EVP_DigestSignInit(ctx, NULL, NULL, NULL, this->key) <= 0) + { + goto error; + } + + if (EVP_DigestSign(ctx, NULL, &signature->len, data.ptr, data.len) <= 0) + { + goto error; + } + + *signature = chunk_alloc(signature->len); + + if (EVP_DigestSign(ctx, signature->ptr, &signature->len, + data.ptr, data.len) <= 0) + { + goto error; + } + + success = TRUE; + +error: + EVP_MD_CTX_free(ctx); + return success; +} + +METHOD(private_key_t, decrypt, bool, + private_private_key_t *this, encryption_scheme_t scheme, + chunk_t crypto, chunk_t *plain) +{ + DBG1(DBG_LIB, "EdDSA private key decryption not implemented"); + return FALSE; +} + +METHOD(private_key_t, get_keysize, int, + private_private_key_t *this) +{ + return openssl_ed_keysize(this->type); +} + +METHOD(private_key_t, get_type, key_type_t, + private_private_key_t *this) +{ + return this->type; +} + +METHOD(private_key_t, get_public_key, public_key_t*, + private_private_key_t *this) +{ + public_key_t *public; + chunk_t key; + + if (!EVP_PKEY_get_raw_public_key(this->key, NULL, &key.len)) + { + return FALSE; + } + key = chunk_alloca(key.len); + if (!EVP_PKEY_get_raw_public_key(this->key, key.ptr, &key.len)) + { + return FALSE; + } + public = lib->creds->create(lib->creds, CRED_PUBLIC_KEY, this->type, + BUILD_EDDSA_PUB, key, BUILD_END); + return public; +} + +METHOD(private_key_t, get_fingerprint, bool, + private_private_key_t *this, cred_encoding_type_t type, + chunk_t *fingerprint) +{ + return openssl_ed_fingerprint(this->key, type, fingerprint); +} + +METHOD(private_key_t, get_encoding, bool, + private_private_key_t *this, cred_encoding_type_t type, chunk_t *encoding) +{ + u_char *p; + + if (this->engine) + { + return FALSE; + } + + switch (type) + { + case PRIVKEY_ASN1_DER: + case PRIVKEY_PEM: + { + bool success = TRUE; + + *encoding = chunk_alloc(i2d_PrivateKey(this->key, NULL)); + p = encoding->ptr; + i2d_PrivateKey(this->key, &p); + + if (type == PRIVKEY_PEM) + { + chunk_t asn1_encoding = *encoding; + + success = lib->encoding->encode(lib->encoding, PRIVKEY_PEM, + NULL, encoding, CRED_PART_EDDSA_PRIV_ASN1_DER, + asn1_encoding, CRED_PART_END); + chunk_clear(&asn1_encoding); + } + return success; + } + default: + return FALSE; + } +} + +METHOD(private_key_t, get_ref, private_key_t*, + private_private_key_t *this) +{ + ref_get(&this->ref); + return &this->public; +} + +METHOD(private_key_t, destroy, void, + private_private_key_t *this) +{ + if (ref_put(&this->ref)) + { + lib->encoding->clear_cache(lib->encoding, this->key); + EVP_PKEY_free(this->key); + free(this); + } +} + +/** + * Internal generic constructor + */ +static private_private_key_t *create_internal(key_type_t type, EVP_PKEY *key) +{ + private_private_key_t *this; + + INIT(this, + .public = { + .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, + }, + .type = type, + .key = key, + .ref = 1, + ); + + return this; +} + +/* + * Described in header + */ +private_key_t *openssl_ed_private_key_create(EVP_PKEY *key, bool engine) +{ + private_private_key_t *this; + key_type_t type; + + switch (EVP_PKEY_base_id(key)) + { + case EVP_PKEY_X25519: + type = KEY_ED25519; + break; + case EVP_PKEY_X448: + type = KEY_ED448; + break; + default: + EVP_PKEY_free(key); + return NULL; + } + + this = create_internal(type, key); + this->engine = engine; + return &this->public; +} + +/* + * Described in header + */ +private_key_t *openssl_ed_private_key_gen(key_type_t type, va_list args) +{ + private_private_key_t *this; + EVP_PKEY_CTX *ctx; + EVP_PKEY *key = NULL; + + while (TRUE) + { + switch (va_arg(args, builder_part_t)) + { + case BUILD_KEY_SIZE: + /* just ignore the key size */ + va_arg(args, u_int); + continue; + case BUILD_END: + break; + default: + return NULL; + } + break; + } + + ctx = EVP_PKEY_CTX_new_id(openssl_ed_key_type(type), NULL); + if (!ctx || + EVP_PKEY_keygen_init(ctx) <= 0 || + EVP_PKEY_keygen(ctx, &key) <= 0) + { + DBG1(DBG_LIB, "generating %N key failed", key_type_names, type); + EVP_PKEY_CTX_free(ctx); + return NULL; + } + EVP_PKEY_CTX_free(ctx); + + this = create_internal(type, key); + return &this->public; +} + +/* + * Described in header + */ +private_key_t *openssl_ed_private_key_load(key_type_t type, va_list args) +{ + private_private_key_t *this; + chunk_t blob = chunk_empty, priv = chunk_empty; + EVP_PKEY *key = NULL; + + while (TRUE) + { + switch (va_arg(args, builder_part_t)) + { + case BUILD_BLOB_ASN1_DER: + blob = va_arg(args, chunk_t); + continue; + case BUILD_EDDSA_PRIV_ASN1_DER: + priv = va_arg(args, chunk_t); + continue; + case BUILD_END: + break; + default: + return NULL; + } + break; + } + + if (priv.len) + { + /* unwrap octet string */ + if (asn1_unwrap(&priv, &priv) == 0x04 && priv.len) + { + key = EVP_PKEY_new_raw_private_key(openssl_ed_key_type(type), NULL, + priv.ptr, priv.len); + } + } + else if (blob.len) + { + key = d2i_PrivateKey(openssl_ed_key_type(type), NULL, + (const u_char**)&blob.ptr, blob.len); + } + if (!key) + { + return NULL; + } + this = create_internal(type, key); + return &this->public; +} + +#endif /* OPENSSL_NO_ECDSA */ diff --git a/src/libstrongswan/plugins/openssl/openssl_ed_private_key.h b/src/libstrongswan/plugins/openssl/openssl_ed_private_key.h new file mode 100644 index 000000000..ce9071348 --- /dev/null +++ b/src/libstrongswan/plugins/openssl/openssl_ed_private_key.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2018 Tobias Brunner + * HSR Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup openssl_ed_private_key openssl_ed_private_key + * @{ @ingroup openssl_p + */ + +#ifndef OPENSSL_ED_PRIVATE_KEY_H_ +#define OPENSSL_ED_PRIVATE_KEY_H_ + +#include <openssl/evp.h> + +#include <credentials/builder.h> +#include <credentials/keys/private_key.h> + +/** + * Generate an EdDSA private key using OpenSSL. + * + * @param type type of the key, must be KEY_ED25519 or KEY_ED448 + * @param args builder_part_t argument list + * @return generated key, NULL on failure + */ +private_key_t *openssl_ed_private_key_gen(key_type_t type, va_list args); + +/** + * Load an EdDSA private key using OpenSSL. + * + * Accepts a BUILD_BLOB_ASN1_DER argument. + * + * @param type type of the key, must be KEY_ED25519 or KEY_ED448 + * @param args builder_part_t argument list + * @return loaded key, NULL on failure + */ +private_key_t *openssl_ed_private_key_load(key_type_t type, va_list args); + +/** + * Wrap an EVP_PKEY object of type EVP_PKEY_ED25519/448 + * + * @param key EVP_PKEY object (adopted) + * @param engine whether the key was loaded via an engine + * @return loaded key, NULL on failure + */ +private_key_t *openssl_ed_private_key_create(EVP_PKEY *key, bool engine); + +#endif /** OPENSSL_ED_PRIVATE_KEY_H_ @}*/ diff --git a/src/libstrongswan/plugins/openssl/openssl_ed_public_key.c b/src/libstrongswan/plugins/openssl/openssl_ed_public_key.c new file mode 100644 index 000000000..2daddc57e --- /dev/null +++ b/src/libstrongswan/plugins/openssl/openssl_ed_public_key.c @@ -0,0 +1,304 @@ +/* + * Copyright (C) 2018 Tobias Brunner + * HSR Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include <openssl/evp.h> + +#if OPENSSL_VERSION_NUMBER >= 0x1010100fL && !defined(OPENSSL_NO_EC) + +#include <openssl/x509.h> + +#include "openssl_ed_public_key.h" + +#include <utils/debug.h> + +typedef struct private_public_key_t private_public_key_t; + +/** + * Private data + */ +struct private_public_key_t { + + /** + * Public interface + */ + public_key_t public; + + /** + * Key object + */ + EVP_PKEY *key; + + /** + * Key type + */ + key_type_t type; + + /** + * Reference counter + */ + refcount_t ref; +}; + +/** + * Map a key type to an EVP key type + */ +int openssl_ed_key_type(key_type_t type) +{ + switch (type) + { + case KEY_ED25519: + return EVP_PKEY_ED25519; + case KEY_ED448: + return EVP_PKEY_ED448; + default: + return 0; + } +} + +/** + * Map a key type to a key size + */ +int openssl_ed_keysize(key_type_t type) +{ + switch (type) + { + case KEY_ED25519: + return 32 * 8; + case KEY_ED448: + return 57 * 8; + default: + return 0; + } +} + +METHOD(public_key_t, get_type, key_type_t, + private_public_key_t *this) +{ + return this->type; +} + +METHOD(public_key_t, verify, bool, + private_public_key_t *this, signature_scheme_t scheme, + void *params, chunk_t data, chunk_t signature) +{ + EVP_MD_CTX *ctx; + + if ((this->type == KEY_ED25519 && scheme != SIGN_ED25519) || + (this->type == KEY_ED448 && scheme != SIGN_ED448)) + { + DBG1(DBG_LIB, "signature scheme %N not supported by %N key", + signature_scheme_names, scheme, key_type_names, this->type); + return FALSE; + } + + ctx = EVP_MD_CTX_new(); + if (!ctx || + EVP_DigestVerifyInit(ctx, NULL, NULL, NULL, this->key) <= 0 || + EVP_DigestVerify(ctx, signature.ptr, signature.len, + data.ptr, data.len) <= 0) + { + EVP_MD_CTX_free(ctx); + return FALSE; + } + EVP_MD_CTX_free(ctx); + return TRUE; +} + +METHOD(public_key_t, encrypt, bool, + private_public_key_t *this, encryption_scheme_t scheme, + chunk_t crypto, chunk_t *plain) +{ + DBG1(DBG_LIB, "encryption scheme %N not supported", encryption_scheme_names, + scheme); + return FALSE; +} + +METHOD(public_key_t, get_keysize, int, + private_public_key_t *this) +{ + return openssl_ed_keysize(this->type); +} + +/** + * Calculate fingerprint from an EdDSA key, also used in ed private key. + */ +bool openssl_ed_fingerprint(EVP_PKEY *key, cred_encoding_type_t type, + chunk_t *fp) +{ + hasher_t *hasher; + chunk_t blob; + u_char *p; + + if (lib->encoding->get_cache(lib->encoding, type, key, fp)) + { + return TRUE; + } + switch (type) + { + case KEYID_PUBKEY_SHA1: + if (!EVP_PKEY_get_raw_public_key(key, NULL, &blob.len)) + { + return FALSE; + } + blob = chunk_alloca(blob.len); + if (!EVP_PKEY_get_raw_public_key(key, blob.ptr, &blob.len)) + { + return FALSE; + } + break; + case KEYID_PUBKEY_INFO_SHA1: + blob = chunk_alloca(i2d_PUBKEY(key, NULL)); + p = blob.ptr; + i2d_PUBKEY(key, &p); + break; + default: + return FALSE; + } + hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1); + if (!hasher || !hasher->allocate_hash(hasher, blob, fp)) + { + DBG1(DBG_LIB, "SHA1 not supported, fingerprinting failed"); + DESTROY_IF(hasher); + return FALSE; + } + hasher->destroy(hasher); + lib->encoding->cache(lib->encoding, type, key, *fp); + return TRUE; +} + +METHOD(public_key_t, get_fingerprint, bool, + private_public_key_t *this, cred_encoding_type_t type, chunk_t *fingerprint) +{ + return openssl_ed_fingerprint(this->key, type, fingerprint); +} + +METHOD(public_key_t, get_encoding, bool, + private_public_key_t *this, cred_encoding_type_t type, chunk_t *encoding) +{ + bool success = TRUE; + u_char *p; + + *encoding = chunk_alloc(i2d_PUBKEY(this->key, NULL)); + p = encoding->ptr; + i2d_PUBKEY(this->key, &p); + + if (type != PUBKEY_SPKI_ASN1_DER) + { + chunk_t asn1_encoding = *encoding; + + success = lib->encoding->encode(lib->encoding, type, + NULL, encoding, CRED_PART_EDDSA_PUB_ASN1_DER, + asn1_encoding, CRED_PART_END); + chunk_clear(&asn1_encoding); + } + return success; +} + +METHOD(public_key_t, get_ref, public_key_t*, + private_public_key_t *this) +{ + ref_get(&this->ref); + return &this->public; +} + +METHOD(public_key_t, destroy, void, + private_public_key_t *this) +{ + if (ref_put(&this->ref)) + { + lib->encoding->clear_cache(lib->encoding, this->key); + EVP_PKEY_free(this->key); + free(this); + } +} + +/** + * Generic private constructor + */ +static private_public_key_t *create_empty(key_type_t type) +{ + private_public_key_t *this; + + INIT(this, + .public = { + .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, + }, + .type = type, + .ref = 1, + ); + + return this; +} + +/* + * Described in header + */ +public_key_t *openssl_ed_public_key_load(key_type_t type, va_list args) +{ + private_public_key_t *this; + chunk_t blob = chunk_empty, pub = chunk_empty; + EVP_PKEY *key = NULL; + + while (TRUE) + { + switch (va_arg(args, builder_part_t)) + { + case BUILD_BLOB_ASN1_DER: + blob = va_arg(args, chunk_t); + continue; + case BUILD_EDDSA_PUB: + pub = va_arg(args, chunk_t); + continue; + case BUILD_END: + break; + default: + return NULL; + } + break; + } + + if (pub.len) + { + key = EVP_PKEY_new_raw_public_key(openssl_ed_key_type(type), NULL, + pub.ptr, pub.len); + } + else if (blob.len) + { + key = d2i_PUBKEY(NULL, (const u_char**)&blob.ptr, blob.len); + if (key && EVP_PKEY_base_id(key) != openssl_ed_key_type(type)) + { + EVP_PKEY_free(key); + return NULL; + } + } + if (!key) + { + return NULL; + } + this = create_empty(type); + this->key = key; + return &this->public; +} + +#endif /* OPENSSL_VERSION_NUMBER */ diff --git a/src/libstrongswan/plugins/openssl/openssl_ed_public_key.h b/src/libstrongswan/plugins/openssl/openssl_ed_public_key.h new file mode 100644 index 000000000..c4e1ba3ed --- /dev/null +++ b/src/libstrongswan/plugins/openssl/openssl_ed_public_key.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2018 Tobias Brunner + * HSR Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup openssl_ed_public_key openssl_ed_public_key + * @{ @ingroup openssl_p + */ + +#ifndef OPENSSL_ED_PUBLIC_KEY_H_ +#define OPENSSL_ED_PUBLIC_KEY_H_ + +#include <credentials/builder.h> +#include <credentials/keys/public_key.h> + +/** + * Load an EdDSA public key using OpenSSL. + * + * Accepts a BUILD_BLOB_ASN1_DER argument. + * + * @param type type of the key, must be KEY_ED25519 or KEY_ED448 + * @param args builder_part_t argument list + * @return loaded key, NULL on failure + */ +public_key_t *openssl_ed_public_key_load(key_type_t type, va_list args); + +#endif /** OPENSSL_ED_PUBLIC_KEY_H_ @}*/ diff --git a/src/libstrongswan/plugins/openssl/openssl_plugin.c b/src/libstrongswan/plugins/openssl/openssl_plugin.c index 8b0a7c5c7..cbeb6c3b7 100644 --- a/src/libstrongswan/plugins/openssl/openssl_plugin.c +++ b/src/libstrongswan/plugins/openssl/openssl_plugin.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008-2016 Tobias Brunner + * Copyright (C) 2008-2018 Tobias Brunner * Copyright (C) 2008 Martin Willi * HSR Hochschule fuer Technik Rapperswil * @@ -47,6 +47,9 @@ #include "openssl_rng.h" #include "openssl_hmac.h" #include "openssl_gcm.h" +#include "openssl_x_diffie_hellman.h" +#include "openssl_ed_public_key.h" +#include "openssl_ed_private_key.h" #ifndef FIPS_MODE #define FIPS_MODE 0 @@ -307,6 +310,11 @@ static private_key_t *openssl_private_key_load(key_type_t type, va_list args) case EVP_PKEY_EC: return openssl_ec_private_key_create(key, FALSE); #endif +#if OPENSSL_VERSION_NUMBER >= 0x1010100fL && !defined(OPENSSL_NO_EC) + case EVP_PKEY_ED25519: + case EVP_PKEY_ED448: + return openssl_ed_private_key_create(key, FALSE); +#endif /* OPENSSL_VERSION_NUMBER */ default: EVP_PKEY_free(key); break; @@ -370,7 +378,7 @@ static private_key_t *openssl_private_key_connect(key_type_t type, #ifndef OPENSSL_NO_ENGINE char *engine_id = NULL; char keyname[BUF_LEN]; - chunk_t keyid = chunk_empty;; + chunk_t keyid = chunk_empty; EVP_PKEY *key; ENGINE *engine; int slot = -1; @@ -395,7 +403,7 @@ static private_key_t *openssl_private_key_connect(key_type_t type, } break; } - if (!keyid.len || keyid.len > 40) + if (!keyid.len) { return NULL; } @@ -405,7 +413,7 @@ static private_key_t *openssl_private_key_connect(key_type_t type, { snprintf(keyname, sizeof(keyname), "%d:", slot); } - if (sizeof(keyname) - strlen(keyname) <= keyid.len * 4 / 3 + 1) + if (sizeof(keyname) - strlen(keyname) <= keyid.len * 2 + 1) { return NULL; } @@ -428,21 +436,21 @@ static private_key_t *openssl_private_key_connect(key_type_t type, ENGINE_free(engine); return NULL; } + ENGINE_free(engine); if (!login(engine, keyid)) { DBG1(DBG_LIB, "login to engine '%s' failed", engine_id); - ENGINE_free(engine); + ENGINE_finish(engine); return NULL; } key = ENGINE_load_private_key(engine, keyname, NULL, NULL); + ENGINE_finish(engine); if (!key) { DBG1(DBG_LIB, "failed to load private key with ID '%s' from " "engine '%s'", keyname, engine_id); - ENGINE_free(engine); return NULL; } - ENGINE_free(engine); switch (EVP_PKEY_base_id(key)) { @@ -454,6 +462,11 @@ static private_key_t *openssl_private_key_connect(key_type_t type, case EVP_PKEY_EC: return openssl_ec_private_key_create(key, TRUE); #endif +#if OPENSSL_VERSION_NUMBER >= 0x1010100fL && !defined(OPENSSL_NO_EC) + case EVP_PKEY_ED25519: + case EVP_PKEY_ED448: + return openssl_ed_private_key_create(key, TRUE); +#endif /* OPENSSL_VERSION_NUMBER */ default: EVP_PKEY_free(key); break; @@ -594,7 +607,7 @@ METHOD(plugin_t, get_features, int, PLUGIN_PROVIDE(DH, ECP_384_BP), PLUGIN_PROVIDE(DH, ECP_512_BP), PLUGIN_PROVIDE(DH, ECP_224_BP), -#endif +#endif /* OPENSSL_NO_ECDH */ #ifndef OPENSSL_NO_DH /* MODP DH groups */ PLUGIN_REGISTER(DH, openssl_diffie_hellman_create), @@ -699,6 +712,30 @@ METHOD(plugin_t, get_features, int, PLUGIN_PROVIDE(PUBKEY_VERIFY, SIGN_ECDSA_521), #endif #endif /* OPENSSL_NO_ECDSA */ +#if OPENSSL_VERSION_NUMBER >= 0x1010100fL && !defined(OPENSSL_NO_EC) + PLUGIN_REGISTER(DH, openssl_x_diffie_hellman_create), + /* available since 1.1.0a, but we require 1.1.1 features */ + PLUGIN_PROVIDE(DH, CURVE_25519), + /* available since 1.1.1 */ + PLUGIN_PROVIDE(DH, CURVE_448), + /* EdDSA private/public key loading */ + PLUGIN_REGISTER(PUBKEY, openssl_ed_public_key_load, TRUE), + PLUGIN_PROVIDE(PUBKEY, KEY_ED25519), + PLUGIN_PROVIDE(PUBKEY, KEY_ED448), + PLUGIN_REGISTER(PRIVKEY, openssl_ed_private_key_load, TRUE), + PLUGIN_PROVIDE(PRIVKEY, KEY_ED25519), + PLUGIN_PROVIDE(PRIVKEY, KEY_ED448), + PLUGIN_REGISTER(PRIVKEY_GEN, openssl_ed_private_key_gen, FALSE), + PLUGIN_PROVIDE(PRIVKEY_GEN, KEY_ED25519), + PLUGIN_PROVIDE(PRIVKEY_GEN, KEY_ED448), + PLUGIN_PROVIDE(PRIVKEY_SIGN, SIGN_ED25519), + PLUGIN_PROVIDE(PRIVKEY_SIGN, SIGN_ED448), + PLUGIN_PROVIDE(PUBKEY_VERIFY, SIGN_ED25519), + PLUGIN_PROVIDE(PUBKEY_VERIFY, SIGN_ED448), + /* register a pro forma identity hasher, never instantiated */ + PLUGIN_REGISTER(HASHER, return_null), + PLUGIN_PROVIDE(HASHER, HASH_IDENTITY), +#endif /* OPENSSL_VERSION_NUMBER && !OPENSSL_NO_EC */ /* generic key loader */ PLUGIN_REGISTER(PRIVKEY, openssl_private_key_load, TRUE), PLUGIN_PROVIDE(PRIVKEY, KEY_ANY), diff --git a/src/libstrongswan/plugins/openssl/openssl_rng.c b/src/libstrongswan/plugins/openssl/openssl_rng.c index a25b6b4b6..d3993749f 100644 --- a/src/libstrongswan/plugins/openssl/openssl_rng.c +++ b/src/libstrongswan/plugins/openssl/openssl_rng.c @@ -1,4 +1,7 @@ /* + * Copyright (C) 2012-2018 Tobias Brunner + * HSR Hochschule fuer Technik Rapperswil + * * Copyright (C) 2012 Aleksandr Grinberg * * Permission is hereby granted, free of charge, to any person obtaining a copy @@ -24,7 +27,6 @@ #include <utils/debug.h> #include <openssl/rand.h> -#include <openssl/err.h> #include "openssl_rng.h" @@ -49,6 +51,13 @@ struct private_openssl_rng_t { METHOD(rng_t, get_bytes, bool, private_openssl_rng_t *this, size_t bytes, uint8_t *buffer) { +#if OPENSSL_VERSION_NUMBER >= 0x1010100fL + if (this->quality > RNG_WEAK) + { /* use a separate DRBG for data we want to keep private, compared + * to e.g. nonces */ + return RAND_priv_bytes((char*)buffer, bytes) == 1; + } +#endif return RAND_bytes((char*)buffer, bytes) == 1; } diff --git a/src/libstrongswan/plugins/openssl/openssl_rsa_private_key.c b/src/libstrongswan/plugins/openssl/openssl_rsa_private_key.c index 401a51a0b..8a9fdfe25 100644 --- a/src/libstrongswan/plugins/openssl/openssl_rsa_private_key.c +++ b/src/libstrongswan/plugins/openssl/openssl_rsa_private_key.c @@ -103,13 +103,8 @@ static bool build_signature(private_openssl_rsa_private_key_t *this, if (pss) { const EVP_MD *mgf1md = openssl_get_md(pss->mgf1_hash); - int slen = EVP_MD_size(md); - if (pss->salt_len > RSA_PSS_SALT_LEN_DEFAULT) - { - slen = pss->salt_len; - } if (EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PSS_PADDING) <= 0 || - EVP_PKEY_CTX_set_rsa_pss_saltlen(pctx, slen) <= 0 || + EVP_PKEY_CTX_set_rsa_pss_saltlen(pctx, pss->salt_len) <= 0 || EVP_PKEY_CTX_set_rsa_mgf1_md(pctx, mgf1md) <= 0) { goto error; diff --git a/src/libstrongswan/plugins/openssl/openssl_rsa_public_key.c b/src/libstrongswan/plugins/openssl/openssl_rsa_public_key.c index 20bf30ae9..38b4eda35 100644 --- a/src/libstrongswan/plugins/openssl/openssl_rsa_public_key.c +++ b/src/libstrongswan/plugins/openssl/openssl_rsa_public_key.c @@ -95,13 +95,8 @@ static bool verify_signature(private_openssl_rsa_public_key_t *this, if (pss) { const EVP_MD *mgf1md = openssl_get_md(pss->mgf1_hash); - int slen = EVP_MD_size(md); - if (pss->salt_len > RSA_PSS_SALT_LEN_DEFAULT) - { - slen = pss->salt_len; - } if (EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PSS_PADDING) <= 0 || - EVP_PKEY_CTX_set_rsa_pss_saltlen(pctx, slen) <= 0 || + EVP_PKEY_CTX_set_rsa_pss_saltlen(pctx, pss->salt_len) <= 0 || EVP_PKEY_CTX_set_rsa_mgf1_md(pctx, mgf1md) <= 0) { goto error; diff --git a/src/libstrongswan/plugins/openssl/openssl_util.c b/src/libstrongswan/plugins/openssl/openssl_util.c index b7f969f73..f99dcd6b1 100644 --- a/src/libstrongswan/plugins/openssl/openssl_util.c +++ b/src/libstrongswan/plugins/openssl/openssl_util.c @@ -26,6 +26,7 @@ #if OPENSSL_VERSION_NUMBER < 0x10100000L #define OBJ_get0_data(o) ((o)->data) #define OBJ_length(o) ((o)->length) +#define ASN1_STRING_get0_data(a) ASN1_STRING_data((ASN1_STRING*)a) #endif /** @@ -164,11 +165,12 @@ chunk_t openssl_asn1_obj2chunk(ASN1_OBJECT *asn1) /** * Described in header. */ -chunk_t openssl_asn1_str2chunk(ASN1_STRING *asn1) +chunk_t openssl_asn1_str2chunk(const ASN1_STRING *asn1) { if (asn1) { - return chunk_create(ASN1_STRING_data(asn1), ASN1_STRING_length(asn1)); + return chunk_create((u_char*)ASN1_STRING_get0_data(asn1), + ASN1_STRING_length(asn1)); } return chunk_empty; } @@ -212,7 +214,7 @@ int openssl_asn1_known_oid(ASN1_OBJECT *obj) /** * Described in header. */ -time_t openssl_asn1_to_time(ASN1_TIME *time) +time_t openssl_asn1_to_time(const ASN1_TIME *time) { chunk_t chunk; diff --git a/src/libstrongswan/plugins/openssl/openssl_util.h b/src/libstrongswan/plugins/openssl/openssl_util.h index 80e557fa8..4afe76bf2 100644 --- a/src/libstrongswan/plugins/openssl/openssl_util.h +++ b/src/libstrongswan/plugins/openssl/openssl_util.h @@ -109,7 +109,7 @@ chunk_t openssl_asn1_obj2chunk(ASN1_OBJECT *asn1); * @param asn1 asn1 string to convert * @return chunk, pointing into asn1 string */ -chunk_t openssl_asn1_str2chunk(ASN1_STRING *asn1); +chunk_t openssl_asn1_str2chunk(const ASN1_STRING *asn1); /** * Convert an openssl X509_NAME to a identification_t of type ID_DER_ASN1_DN. @@ -133,7 +133,7 @@ int openssl_asn1_known_oid(ASN1_OBJECT *obj); * @param time openssl ASN1_TIME * @returns time_t, 0 on error */ -time_t openssl_asn1_to_time(ASN1_TIME *time); +time_t openssl_asn1_to_time(const ASN1_TIME *time); /** * Compatibility macros diff --git a/src/libstrongswan/plugins/openssl/openssl_x509.c b/src/libstrongswan/plugins/openssl/openssl_x509.c index fae2d678f..fe21b0221 100644 --- a/src/libstrongswan/plugins/openssl/openssl_x509.c +++ b/src/libstrongswan/plugins/openssl/openssl_x509.c @@ -389,7 +389,11 @@ METHOD(certificate_t, issued_by, bool, public_key_t *key; bool valid; x509_t *x509 = (x509_t*)issuer; +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + const ASN1_BIT_STRING *sig; +#else ASN1_BIT_STRING *sig; +#endif chunk_t tbs; if (&this->public.x509.interface == issuer) @@ -993,7 +997,7 @@ static bool parse_subjectKeyIdentifier_ext(private_openssl_x509_t *this, */ static bool parse_extensions(private_openssl_x509_t *this) { - STACK_OF(X509_EXTENSION) *extensions; + const STACK_OF(X509_EXTENSION) *extensions; int i, num; /* unless we see a keyUsage extension we are compliant with RFC 4945 */ @@ -1077,7 +1081,11 @@ static bool parse_certificate(private_openssl_x509_t *this) hasher_t *hasher; chunk_t chunk, sig_scheme, sig_scheme_tbs; ASN1_OBJECT *oid; +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + const X509_ALGOR *alg; +#else X509_ALGOR *alg; +#endif this->x509 = d2i_X509(NULL, &ptr, this->encoding.len); if (!this->x509) @@ -1135,9 +1143,9 @@ static bool parse_certificate(private_openssl_x509_t *this) /* while X509_ALGOR_cmp() is declared in the headers of older OpenSSL * versions, at least on Ubuntu 14.04 it is not actually defined */ X509_get0_signature(NULL, &alg, this->x509); - sig_scheme = openssl_i2chunk(X509_ALGOR, alg); + sig_scheme = openssl_i2chunk(X509_ALGOR, (X509_ALGOR*)alg); alg = X509_get0_tbs_sigalg(this->x509); - sig_scheme_tbs = openssl_i2chunk(X509_ALGOR, alg); + sig_scheme_tbs = openssl_i2chunk(X509_ALGOR, (X509_ALGOR*)alg); if (!chunk_equals(sig_scheme, sig_scheme_tbs)) { free(sig_scheme_tbs.ptr); diff --git a/src/libstrongswan/plugins/openssl/openssl_x_diffie_hellman.c b/src/libstrongswan/plugins/openssl/openssl_x_diffie_hellman.c new file mode 100644 index 000000000..37943f5bf --- /dev/null +++ b/src/libstrongswan/plugins/openssl/openssl_x_diffie_hellman.c @@ -0,0 +1,256 @@ +/* + * Copyright (C) 2018 Tobias Brunner + * HSR Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include <openssl/evp.h> + +/* basic support for X25519 was added with 1.1.0a, but we require features (e.g. + * to load the keys) that were only added with 1.1.1 */ +#if OPENSSL_VERSION_NUMBER >= 0x1010100fL && !defined(OPENSSL_NO_ECDH) + +#include "openssl_x_diffie_hellman.h" + +#include <utils/debug.h> + +typedef struct private_diffie_hellman_t private_diffie_hellman_t; + +/** + * Private data + */ +struct private_diffie_hellman_t { + /** + * Public interface. + */ + diffie_hellman_t public; + + /** + * Diffie Hellman group number. + */ + diffie_hellman_group_t group; + + /** + * Private (public) key + */ + EVP_PKEY *key; + + /** + * Shared secret + */ + chunk_t shared_secret; + + /** + * True if shared secret is computed + */ + bool computed; +}; + +/** + * Map a DH group to a key type + */ +static int map_key_type(diffie_hellman_group_t group) +{ + switch (group) + { + case CURVE_25519: + return EVP_PKEY_X25519; + case CURVE_448: + return EVP_PKEY_X448; + default: + return 0; + } +} + +/** + * Compute the shared secret + */ +static bool compute_shared_key(private_diffie_hellman_t *this, EVP_PKEY *pub, + chunk_t *shared_secret) +{ + EVP_PKEY_CTX *ctx; + bool success = FALSE; + + ctx = EVP_PKEY_CTX_new(this->key, NULL); + if (!ctx) + { + return FALSE; + } + + if (EVP_PKEY_derive_init(ctx) <= 0) + { + goto error; + } + + if (EVP_PKEY_derive_set_peer(ctx, pub) <= 0) + { + goto error; + } + + if (EVP_PKEY_derive(ctx, NULL, &shared_secret->len) <= 0) + { + goto error; + } + + *shared_secret = chunk_alloc(shared_secret->len); + + if (EVP_PKEY_derive(ctx, shared_secret->ptr, &shared_secret->len) <= 0) + { + goto error; + } + + success = TRUE; + +error: + EVP_PKEY_CTX_free(ctx); + return success; +} + +METHOD(diffie_hellman_t, set_other_public_value, bool, + private_diffie_hellman_t *this, chunk_t value) +{ + EVP_PKEY *pub; + + if (!diffie_hellman_verify_value(this->group, value)) + { + return FALSE; + } + + pub = EVP_PKEY_new_raw_public_key(map_key_type(this->group), NULL, + value.ptr, value.len); + if (!pub) + { + DBG1(DBG_LIB, "%N public value is malformed", + diffie_hellman_group_names, this->group); + return FALSE; + } + + chunk_clear(&this->shared_secret); + + if (!compute_shared_key(this, pub, &this->shared_secret)) + { + DBG1(DBG_LIB, "%N shared secret computation failed", + diffie_hellman_group_names, this->group); + EVP_PKEY_free(pub); + return FALSE; + } + this->computed = TRUE; + EVP_PKEY_free(pub); + return TRUE; +} + +METHOD(diffie_hellman_t, get_my_public_value, bool, + private_diffie_hellman_t *this, chunk_t *value) +{ + size_t len; + + if (!EVP_PKEY_get_raw_public_key(this->key, NULL, &len)) + { + return FALSE; + } + + *value = chunk_alloc(len); + + if (!EVP_PKEY_get_raw_public_key(this->key, value->ptr, &value->len)) + { + chunk_free(value); + return FALSE; + } + return TRUE; +} + +METHOD(diffie_hellman_t, set_private_value, bool, + private_diffie_hellman_t *this, chunk_t value) +{ + EVP_PKEY_free(this->key); + this->key = EVP_PKEY_new_raw_private_key(map_key_type(this->group), NULL, + value.ptr, value.len); + if (!this->key) + { + return FALSE; + } + return TRUE; +} + +METHOD(diffie_hellman_t, get_shared_secret, bool, + private_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_diffie_hellman_t *this) +{ + return this->group; +} + +METHOD(diffie_hellman_t, destroy, void, + private_diffie_hellman_t *this) +{ + EVP_PKEY_free(this->key); + chunk_clear(&this->shared_secret); + free(this); +} + +/* + * Described in header + */ +diffie_hellman_t *openssl_x_diffie_hellman_create(diffie_hellman_group_t group) +{ + private_diffie_hellman_t *this; + EVP_PKEY_CTX *ctx = NULL; + EVP_PKEY *key = NULL; + + switch (group) + { + case CURVE_25519: + ctx = EVP_PKEY_CTX_new_id(NID_X25519, NULL); + break; + case CURVE_448: + ctx = EVP_PKEY_CTX_new_id(NID_X448, NULL); + break; + default: + break; + } + + if (!ctx || + EVP_PKEY_keygen_init(ctx) <= 0 || + EVP_PKEY_keygen(ctx, &key) <= 0) + { + DBG1(DBG_LIB, "generating key for %N failed", + diffie_hellman_group_names, group); + EVP_PKEY_CTX_free(ctx); + return NULL; + } + EVP_PKEY_CTX_free(ctx); + + INIT(this, + .public = { + .get_shared_secret = _get_shared_secret, + .set_other_public_value = _set_other_public_value, + .get_my_public_value = _get_my_public_value, + .set_private_value = _set_private_value, + .get_dh_group = _get_dh_group, + .destroy = _destroy, + }, + .group = group, + .key = key, + ); + return &this->public; +} + +#endif /* OPENSSL_NO_ECDH */ diff --git a/src/libstrongswan/plugins/openssl/openssl_x_diffie_hellman.h b/src/libstrongswan/plugins/openssl/openssl_x_diffie_hellman.h new file mode 100644 index 000000000..e28f38d15 --- /dev/null +++ b/src/libstrongswan/plugins/openssl/openssl_x_diffie_hellman.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2018 Tobias Brunner + * HSR Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * Implementation of the X25519/X448 Diffie-Hellman algorithm using OpenSSL. + * + * @defgroup openssl_x_diffie_hellman openssl_x_diffie_hellman + * @{ @ingroup openssl_p + */ + +#ifndef OPENSSL_X_DIFFIE_HELLMAN_H_ +#define OPENSSL_X_DIFFIE_HELLMAN_H_ + +#include <library.h> + +/** + * Creates a new diffie_hellman_t object. + * + * @param group Diffie Hellman group number to use + * @return object, NULL if not supported + */ +diffie_hellman_t *openssl_x_diffie_hellman_create(diffie_hellman_group_t group); + +#endif /** OPENSSL_X_DIFFIE_HELLMAN_H_ @}*/ + diff --git a/src/libstrongswan/plugins/sshkey/sshkey_builder.c b/src/libstrongswan/plugins/sshkey/sshkey_builder.c index eab6559b3..934514249 100644 --- a/src/libstrongswan/plugins/sshkey/sshkey_builder.c +++ b/src/libstrongswan/plugins/sshkey/sshkey_builder.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2014 Tobias Brunner + * Copyright (C) 2013-2018 Tobias Brunner * HSR Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it @@ -89,6 +89,34 @@ static sshkey_public_key_t *parse_public_key(chunk_t blob) return lib->creds->create(lib->creds, CRED_PUBLIC_KEY, KEY_RSA, BUILD_RSA_MODULUS, n, BUILD_RSA_PUB_EXP, e, BUILD_END); } + else if (chunk_equals(format, chunk_from_str("ssh-ed25519"))) + { + chunk_t blob; + + if (!reader->read_data32(reader, &blob)) + { + DBG1(DBG_LIB, "invalid Ed25519 key in SSH key"); + reader->destroy(reader); + return NULL; + } + reader->destroy(reader); + return lib->creds->create(lib->creds, CRED_PUBLIC_KEY, KEY_ED25519, + BUILD_EDDSA_PUB, blob, BUILD_END); + } + else if (chunk_equals(format, chunk_from_str("ssh-ed448"))) + { + chunk_t blob; + + if (!reader->read_data32(reader, &blob)) + { + DBG1(DBG_LIB, "invalid Ed448 key in SSH key"); + reader->destroy(reader); + return NULL; + } + reader->destroy(reader); + return lib->creds->create(lib->creds, CRED_PUBLIC_KEY, KEY_ED448, + BUILD_EDDSA_PUB, blob, BUILD_END); + } else if (format.len > strlen(ECDSA_PREFIX) && strpfx(format.ptr, ECDSA_PREFIX)) { @@ -140,8 +168,9 @@ static sshkey_public_key_t *load_from_stream(FILE *file) char line[1024], *token; while (!public && fgets(line, sizeof(line), file)) - { /* the format is: ssh-rsa|ecdsa-... <key(base64)> <identifier> */ - if (!strpfx(line, "ssh-rsa") && !strpfx(line, ECDSA_PREFIX)) + { /* the format is: ssh-<key-type> <key(base64)> <identifier> */ + if (!strpfx(line, "ssh-rsa") && !strpfx(line, ECDSA_PREFIX) && + !strpfx(line, "ssh-ed25519") && !strpfx(line, "ssh-ed448")) { continue; } diff --git a/src/libstrongswan/plugins/sshkey/sshkey_encoder.c b/src/libstrongswan/plugins/sshkey/sshkey_encoder.c index 9f5f8bd1f..ed35fc010 100644 --- a/src/libstrongswan/plugins/sshkey/sshkey_encoder.c +++ b/src/libstrongswan/plugins/sshkey/sshkey_encoder.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 Tobias Brunner + * Copyright (C) 2013-2018 Tobias Brunner * HSR Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it @@ -72,6 +72,42 @@ static bool build_public_key(chunk_t *encoding, va_list args) writer->destroy(writer); return TRUE; } + else if (cred_encoding_args(args, CRED_PART_EDDSA_PUB_ASN1_DER, &n, + CRED_PART_END)) + { + chunk_t alg; + char *prefix; + int oid; + + /* parse subjectPublicKeyInfo */ + if (asn1_unwrap(&n, &n) != ASN1_SEQUENCE) + { + return FALSE; + } + oid = asn1_parse_algorithmIdentifier(n, 1, NULL); + switch (oid) + { + case OID_ED25519: + prefix = "ssh-ed25519"; + break; + case OID_ED448: + prefix = "ssh-ed448"; + break; + default: + return FALSE; + } + if (asn1_unwrap(&n, &alg) != ASN1_SEQUENCE || + asn1_unwrap(&n, &n) != ASN1_BIT_STRING || !n.len) + { + return FALSE; + } + writer = bio_writer_create(0); + writer->write_data32(writer, chunk_from_str(prefix)); + writer->write_data32(writer, chunk_skip(n, 1)); + *encoding = chunk_to_base64(writer->get_buf(writer), NULL); + writer->destroy(writer); + return TRUE; + } else if (cred_encoding_args(args, CRED_PART_ECDSA_PUB_ASN1_DER, &n, CRED_PART_END)) { diff --git a/src/libstrongswan/plugins/test_vectors/Makefile.am b/src/libstrongswan/plugins/test_vectors/Makefile.am index c4d9f2fc5..3d34cf7c9 100644 --- a/src/libstrongswan/plugins/test_vectors/Makefile.am +++ b/src/libstrongswan/plugins/test_vectors/Makefile.am @@ -49,6 +49,7 @@ libstrongswan_test_vectors_la_SOURCES = \ test_vectors/ecp.c \ test_vectors/ecpbp.c \ test_vectors/curve25519.c \ + test_vectors/curve448.c \ test_vectors/rng.c libstrongswan_test_vectors_la_LDFLAGS = -module -avoid-version diff --git a/src/libstrongswan/plugins/test_vectors/Makefile.in b/src/libstrongswan/plugins/test_vectors/Makefile.in index 7f6c319c6..ed3ae0f40 100644 --- a/src/libstrongswan/plugins/test_vectors/Makefile.in +++ b/src/libstrongswan/plugins/test_vectors/Makefile.in @@ -156,7 +156,8 @@ am_libstrongswan_test_vectors_la_OBJECTS = test_vectors_plugin.lo \ test_vectors/sha3_shake.lo test_vectors/fips_prf.lo \ test_vectors/modp.lo test_vectors/modpsub.lo \ test_vectors/ecp.lo test_vectors/ecpbp.lo \ - test_vectors/curve25519.lo test_vectors/rng.lo + test_vectors/curve25519.lo test_vectors/curve448.lo \ + test_vectors/rng.lo libstrongswan_test_vectors_la_OBJECTS = \ $(am_libstrongswan_test_vectors_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) @@ -518,6 +519,7 @@ libstrongswan_test_vectors_la_SOURCES = \ test_vectors/ecp.c \ test_vectors/ecpbp.c \ test_vectors/curve25519.c \ + test_vectors/curve448.c \ test_vectors/rng.c libstrongswan_test_vectors_la_LDFLAGS = -module -avoid-version @@ -680,6 +682,8 @@ test_vectors/ecpbp.lo: test_vectors/$(am__dirstamp) \ test_vectors/$(DEPDIR)/$(am__dirstamp) test_vectors/curve25519.lo: test_vectors/$(am__dirstamp) \ test_vectors/$(DEPDIR)/$(am__dirstamp) +test_vectors/curve448.lo: test_vectors/$(am__dirstamp) \ + test_vectors/$(DEPDIR)/$(am__dirstamp) test_vectors/rng.lo: test_vectors/$(am__dirstamp) \ test_vectors/$(DEPDIR)/$(am__dirstamp) @@ -710,6 +714,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@test_vectors/$(DEPDIR)/chacha20_xof.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@test_vectors/$(DEPDIR)/chacha20poly1305.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@test_vectors/$(DEPDIR)/curve25519.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@test_vectors/$(DEPDIR)/curve448.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@test_vectors/$(DEPDIR)/des.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@test_vectors/$(DEPDIR)/ecp.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@test_vectors/$(DEPDIR)/ecpbp.Plo@am__quote@ diff --git a/src/libstrongswan/plugins/test_vectors/test_vectors.h b/src/libstrongswan/plugins/test_vectors/test_vectors.h index 7ab965a82..7c8ac0c6e 100644 --- a/src/libstrongswan/plugins/test_vectors/test_vectors.h +++ b/src/libstrongswan/plugins/test_vectors/test_vectors.h @@ -116,6 +116,7 @@ TEST_VECTOR_AEAD(aes_gcm23) TEST_VECTOR_AEAD(chacha20poly1305_1) TEST_VECTOR_AEAD(chacha20poly1305_2) TEST_VECTOR_AEAD(chacha20poly1305_3) +TEST_VECTOR_AEAD(chacha20poly1305_4) TEST_VECTOR_SIGNER(aes_xcbc_s1) TEST_VECTOR_SIGNER(aes_xcbc_s2) @@ -305,3 +306,4 @@ TEST_VECTOR_DH(ecp384bp) TEST_VECTOR_DH(ecp512bp) TEST_VECTOR_DH(curve25519_1) TEST_VECTOR_DH(curve25519_2) +TEST_VECTOR_DH(curve448_1) diff --git a/src/libstrongswan/plugins/test_vectors/test_vectors/chacha20poly1305.c b/src/libstrongswan/plugins/test_vectors/test_vectors/chacha20poly1305.c index 21726cbbb..dcbfe5ca3 100644 --- a/src/libstrongswan/plugins/test_vectors/test_vectors/chacha20poly1305.c +++ b/src/libstrongswan/plugins/test_vectors/test_vectors/chacha20poly1305.c @@ -16,10 +16,40 @@ #include <crypto/crypto_tester.h> /** - * From draft-irtf-cfrg-chacha20-poly1305 + * From RFC 7539 */ aead_test_vector_t chacha20poly1305_1 = { .alg = ENCR_CHACHA20_POLY1305, .key_size = 32, .salt_size = 4, + .len = 114, .alen = 12, + .key = "\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f" + "\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f" + "\x07\x00\x00\x00", + .iv = "\x40\x41\x42\x43\x44\x45\x46\x47", + .adata = "\x50\x51\x52\x53\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7", + .plain = "\x4c\x61\x64\x69\x65\x73\x20\x61\x6e\x64\x20\x47\x65\x6e\x74\x6c" + "\x65\x6d\x65\x6e\x20\x6f\x66\x20\x74\x68\x65\x20\x63\x6c\x61\x73" + "\x73\x20\x6f\x66\x20\x27\x39\x39\x3a\x20\x49\x66\x20\x49\x20\x63" + "\x6f\x75\x6c\x64\x20\x6f\x66\x66\x65\x72\x20\x79\x6f\x75\x20\x6f" + "\x6e\x6c\x79\x20\x6f\x6e\x65\x20\x74\x69\x70\x20\x66\x6f\x72\x20" + "\x74\x68\x65\x20\x66\x75\x74\x75\x72\x65\x2c\x20\x73\x75\x6e\x73" + "\x63\x72\x65\x65\x6e\x20\x77\x6f\x75\x6c\x64\x20\x62\x65\x20\x69" + "\x74\x2e", + .cipher = "\xd3\x1a\x8d\x34\x64\x8e\x60\xdb\x7b\x86\xaf\xbc\x53\xef\x7e\xc2" + "\xa4\xad\xed\x51\x29\x6e\x08\xfe\xa9\xe2\xb5\xa7\x36\xee\x62\xd6" + "\x3d\xbe\xa4\x5e\x8c\xa9\x67\x12\x82\xfa\xfb\x69\xda\x92\x72\x8b" + "\x1a\x71\xde\x0a\x9e\x06\x0b\x29\x05\xd6\xa5\xb6\x7e\xcd\x3b\x36" + "\x92\xdd\xbd\x7f\x2d\x77\x8b\x8c\x98\x03\xae\xe3\x28\x09\x1b\x58" + "\xfa\xb3\x24\xe4\xfa\xd6\x75\x94\x55\x85\x80\x8b\x48\x31\xd7\xbc" + "\x3f\xf4\xde\xf0\x8e\x4b\x7a\x9d\xe5\x76\xd2\x65\x86\xce\xc6\x4b" + "\x61\x16\x1a\xe1\x0b\x59\x4f\x09\xe2\x6a\x7e\x90\x2e\xcb\xd0\x60" + "\x06\x91", +}; + +/** + * Additional test vector from RFC 7539 + */ +aead_test_vector_t chacha20poly1305_2 = { + .alg = ENCR_CHACHA20_POLY1305, .key_size = 32, .salt_size = 4, .len = 265, .alen = 12, .key = "\x1c\x92\x40\xa5\xeb\x55\xd3\x8a\xf3\x33\x88\x86\x04\xf6\xb5\xf0" "\x47\x39\x17\xc1\x40\x2b\x80\x09\x9d\xca\x5c\xbc\x20\x70\x75\xc0" @@ -64,9 +94,9 @@ aead_test_vector_t chacha20poly1305_1 = { }; /** - * ESP example from draft-ietf-ipsecme-chacha20-poly1305-06 + * ESP example from RFC 7634 */ -aead_test_vector_t chacha20poly1305_2 = { +aead_test_vector_t chacha20poly1305_3 = { .alg = ENCR_CHACHA20_POLY1305, .key_size = 32, .salt_size = 4, .len = 88, .alen = 8, .key = "\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f" @@ -90,9 +120,9 @@ aead_test_vector_t chacha20poly1305_2 = { }; /** - * IKEv2 example from draft-ietf-ipsecme-chacha20-poly1305-06 + * IKEv2 example from RFC 7634 */ -aead_test_vector_t chacha20poly1305_3 = { +aead_test_vector_t chacha20poly1305_4 = { .alg = ENCR_CHACHA20_POLY1305, .key_size = 32, .salt_size = 4, .len = 13, .alen = 32, .key = "\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f" diff --git a/src/libstrongswan/plugins/test_vectors/test_vectors/curve25519.c b/src/libstrongswan/plugins/test_vectors/test_vectors/curve25519.c index 676fcfc5a..23c024a37 100644 --- a/src/libstrongswan/plugins/test_vectors/test_vectors/curve25519.c +++ b/src/libstrongswan/plugins/test_vectors/test_vectors/curve25519.c @@ -16,7 +16,7 @@ #include <crypto/crypto_tester.h> /** - * From RFC 8037 + * From RFC 7748 */ dh_test_vector_t curve25519_1 = { .group = CURVE_25519, .priv_len = 32, .pub_len = 32, .shared_len = 32, diff --git a/src/libstrongswan/plugins/test_vectors/test_vectors/curve448.c b/src/libstrongswan/plugins/test_vectors/test_vectors/curve448.c new file mode 100644 index 000000000..fccbb808a --- /dev/null +++ b/src/libstrongswan/plugins/test_vectors/test_vectors/curve448.c @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2018 Tobias Brunner + * HSR Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the Licenseor (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be usefulbut + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include <crypto/crypto_tester.h> + +/** + * From RFC 7748 + */ +dh_test_vector_t curve448_1 = { + .group = CURVE_448, .priv_len = 56, .pub_len = 56, .shared_len = 56, + .priv_a = "\x9a\x8f\x49\x25\xd1\x51\x9f\x57\x75\xcf\x46\xb0\x4b\x58\x00\xd4" + "\xee\x9e\xe8\xba\xe8\xbc\x55\x65\xd4\x98\xc2\x8d\xd9\xc9\xba\xf5" + "\x74\xa9\x41\x97\x44\x89\x73\x91\x00\x63\x82\xa6\xf1\x27\xab\x1d" + "\x9a\xc2\xd8\xc0\xa5\x98\x72\x6b", + .priv_b = "\x1c\x30\x6a\x7a\xc2\xa0\xe2\xe0\x99\x0b\x29\x44\x70\xcb\xa3\x39" + "\xe6\x45\x37\x72\xb0\x75\x81\x1d\x8f\xad\x0d\x1d\x69\x27\xc1\x20" + "\xbb\x5e\xe8\x97\x2b\x0d\x3e\x21\x37\x4c\x9c\x92\x1b\x09\xd1\xb0" + "\x36\x6f\x10\xb6\x51\x73\x99\x2d", + .pub_a = "\x9b\x08\xf7\xcc\x31\xb7\xe3\xe6\x7d\x22\xd5\xae\xa1\x21\x07\x4a" + "\x27\x3b\xd2\xb8\x3d\xe0\x9c\x63\xfa\xa7\x3d\x2c\x22\xc5\xd9\xbb" + "\xc8\x36\x64\x72\x41\xd9\x53\xd4\x0c\x5b\x12\xda\x88\x12\x0d\x53" + "\x17\x7f\x80\xe5\x32\xc4\x1f\xa0", + .pub_b = "\x3e\xb7\xa8\x29\xb0\xcd\x20\xf5\xbc\xfc\x0b\x59\x9b\x6f\xec\xcf" + "\x6d\xa4\x62\x71\x07\xbd\xb0\xd4\xf3\x45\xb4\x30\x27\xd8\xb9\x72" + "\xfc\x3e\x34\xfb\x42\x32\xa1\x3c\xa7\x06\xdc\xb5\x7a\xec\x3d\xae" + "\x07\xbd\xc1\xc6\x7b\xf3\x36\x09", + .shared = "\x07\xff\xf4\x18\x1a\xc6\xcc\x95\xec\x1c\x16\xa9\x4a\x0f\x74\xd1" + "\x2d\xa2\x32\xce\x40\xa7\x75\x52\x28\x1d\x28\x2b\xb6\x0c\x0b\x56" + "\xfd\x24\x64\xc3\x35\x54\x39\x36\x52\x1c\x24\x40\x30\x85\xd5\x9a" + "\x44\x9a\x50\x37\x51\x4a\x87\x9d", +}; |