From: Kirill Isakov Date: Thu, 17 Mar 2022 14:49:29 +0000 (+0600) Subject: Add support for OpenSSL 3.0+ X-Git-Url: https://tinc-vpn.org/git/browse?p=tinc;a=commitdiff_plain;h=dcf9e6c3e444fd39318f8d5b261bdc22e5031f67 Add support for OpenSSL 3.0+ Also use centralized logging for OpenSSL errors. https://github.com/gsliepen/tinc/issues/347 --- diff --git a/src/Makefile.am b/src/Makefile.am index 7d6a31dd..dc124b14 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -223,24 +223,28 @@ tincd_SOURCES += \ openssl/crypto.c \ openssl/digest.c openssl/digest.h \ openssl/prf.c \ - openssl/rsa.c + openssl/rsa.c \ + openssl/log.c openssl/log.h tinc_SOURCES += \ openssl/cipher.c openssl/cipher.h \ openssl/crypto.c \ openssl/digest.c openssl/digest.h \ openssl/prf.c \ openssl/rsa.c \ - openssl/rsagen.c + openssl/rsagen.c \ + openssl/log.c openssl/log.h sptps_test_SOURCES += \ openssl/crypto.c \ openssl/digest.c openssl/digest.h \ - openssl/prf.c + openssl/prf.c \ + openssl/log.c openssl/log.h sptps_keypair_SOURCES += \ openssl/crypto.c sptps_speed_SOURCES += \ openssl/crypto.c \ openssl/digest.c openssl/digest.h \ - openssl/prf.c + openssl/prf.c \ + openssl/log.c openssl/log.h else if GCRYPT tincd_SOURCES += \ diff --git a/src/openssl/cipher.c b/src/openssl/cipher.c index 6b2affc0..77747f11 100644 --- a/src/openssl/cipher.c +++ b/src/openssl/cipher.c @@ -19,10 +19,10 @@ #include "../system.h" -#include #include #include +#include "log.h" #include "cipher.h" #include "../cipher.h" #include "../logger.h" @@ -120,7 +120,7 @@ bool cipher_set_key(cipher_t *cipher, void *key, bool encrypt) { return true; } - logger(DEBUG_ALWAYS, LOG_ERR, "Error while setting key: %s", ERR_error_string(ERR_get_error(), NULL)); + openssl_err("set key"); return false; } @@ -137,7 +137,7 @@ bool cipher_set_key_from_rsa(cipher_t *cipher, void *key, size_t len, bool encry return true; } - logger(DEBUG_ALWAYS, LOG_ERR, "Error while setting key: %s", ERR_error_string(ERR_get_error(), NULL)); + openssl_err("set key"); return false; } @@ -166,7 +166,7 @@ bool cipher_encrypt(cipher_t *cipher, const void *indata, size_t inlen, void *ou } } - logger(DEBUG_ALWAYS, LOG_ERR, "Error while encrypting: %s", ERR_error_string(ERR_get_error(), NULL)); + openssl_err("encrypt data"); return false; } @@ -195,7 +195,7 @@ bool cipher_decrypt(cipher_t *cipher, const void *indata, size_t inlen, void *ou } } - logger(DEBUG_ALWAYS, LOG_ERR, "Error while decrypting: %s", ERR_error_string(ERR_get_error(), NULL)); + openssl_err("decrypt data"); return false; } diff --git a/src/openssl/crypto.c b/src/openssl/crypto.c index 34009d69..c2df0af3 100644 --- a/src/openssl/crypto.c +++ b/src/openssl/crypto.c @@ -93,7 +93,9 @@ void randomize(void *out, size_t outlen) { void crypto_init(void) { random_init(); +#if OPENSSL_VERSION_MAJOR < 3 ENGINE_load_builtin_engines(); +#endif if(!RAND_status()) { fprintf(stderr, "Not enough entropy for the PRNG!\n"); diff --git a/src/openssl/digest.c b/src/openssl/digest.c index 71eaec7c..6e27b153 100644 --- a/src/openssl/digest.c +++ b/src/openssl/digest.c @@ -22,9 +22,14 @@ #include #include +#if OPENSSL_VERSION_MAJOR >= 3 +#include +#endif + #include "digest.h" #include "../digest.h" #include "../logger.h" +#include "log.h" static void digest_open(digest_t *digest, const EVP_MD *evp_md, size_t maclength) { digest->digest = evp_md; @@ -63,13 +68,51 @@ bool digest_open_by_nid(digest_t *digest, int nid, size_t maclength) { } bool digest_set_key(digest_t *digest, const void *key, size_t len) { +#if OPENSSL_VERSION_MAJOR < 3 digest->hmac_ctx = HMAC_CTX_new(); - HMAC_Init_ex(digest->hmac_ctx, key, (int)len, digest->digest, NULL); if(!digest->hmac_ctx) { abort(); } + HMAC_Init_ex(digest->hmac_ctx, key, (int)len, digest->digest, NULL); +#else + EVP_MAC *mac = EVP_MAC_fetch(NULL, OSSL_MAC_NAME_HMAC, NULL); + + if(!mac) { + openssl_err("fetch MAC"); + return false; + } + + digest->hmac_ctx = EVP_MAC_CTX_new(mac); + EVP_MAC_free(mac); + + if(!digest->hmac_ctx) { + openssl_err("create MAC context"); + return false; + } + + const char *hmac_algo = EVP_MD_get0_name(digest->digest); + + if(!hmac_algo) { + openssl_err("get HMAC algorithm name"); + return false; + } + + // The casts are okay, the parameters are not going to change. For example, see: + // https://github.com/openssl/openssl/blob/31b7f23d2f958491d46c8a8e61c2b77b1b546f3e/crypto/ec/ecdh_kdf.c#L37-L38 + const OSSL_PARAM params[] = { + OSSL_PARAM_octet_string(OSSL_MAC_PARAM_KEY, (void *)key, len), + OSSL_PARAM_utf8_string(OSSL_MAC_PARAM_DIGEST, (void *)hmac_algo, 0), + OSSL_PARAM_END, + }; + + if(!EVP_MAC_init(digest->hmac_ctx, NULL, 0, params)) { + openssl_err("set MAC context params"); + return false; + } + +#endif return true; } @@ -83,7 +126,11 @@ void digest_close(digest_t *digest) { } if(digest->hmac_ctx) { +#if OPENSSL_VERSION_MAJOR < 3 HMAC_CTX_free(digest->hmac_ctx); +#else + EVP_MAC_CTX_free(digest->hmac_ctx); +#endif } memset(digest, 0, sizeof(*digest)); @@ -94,10 +141,24 @@ bool digest_create(digest_t *digest, const void *indata, size_t inlen, void *out unsigned char tmpdata[len]; if(digest->hmac_ctx) { - if(!HMAC_Init_ex(digest->hmac_ctx, NULL, 0, NULL, NULL) - || !HMAC_Update(digest->hmac_ctx, indata, inlen) - || !HMAC_Final(digest->hmac_ctx, tmpdata, NULL)) { - logger(DEBUG_ALWAYS, LOG_DEBUG, "Error creating digest: %s", ERR_error_string(ERR_get_error(), NULL)); + bool ok; + +#if OPENSSL_VERSION_MAJOR < 3 + ok = HMAC_Init_ex(digest->hmac_ctx, NULL, 0, NULL, NULL) + && HMAC_Update(digest->hmac_ctx, indata, inlen) + && HMAC_Final(digest->hmac_ctx, tmpdata, NULL); +#else + EVP_MAC_CTX *mac_ctx = EVP_MAC_CTX_dup(digest->hmac_ctx); + + ok = mac_ctx + && EVP_MAC_update(mac_ctx, indata, inlen) + && EVP_MAC_final(mac_ctx, tmpdata, NULL, sizeof(tmpdata)); + + EVP_MAC_CTX_free(mac_ctx); +#endif + + if(!ok) { + openssl_err("create HMAC"); return false; } } else { @@ -112,7 +173,7 @@ bool digest_create(digest_t *digest, const void *indata, size_t inlen, void *out if(!EVP_DigestInit(digest->md_ctx, digest->digest) || !EVP_DigestUpdate(digest->md_ctx, indata, inlen) || !EVP_DigestFinal(digest->md_ctx, tmpdata, NULL)) { - logger(DEBUG_ALWAYS, LOG_DEBUG, "Error creating digest: %s", ERR_error_string(ERR_get_error(), NULL)); + openssl_err("create digest"); return false; } } diff --git a/src/openssl/digest.h b/src/openssl/digest.h index 66d54164..d6efacfa 100644 --- a/src/openssl/digest.h +++ b/src/openssl/digest.h @@ -25,7 +25,11 @@ struct digest { const EVP_MD *digest; +#if OPENSSL_VERSION_MAJOR < 3 HMAC_CTX *hmac_ctx; +#else + EVP_MAC_CTX *hmac_ctx; +#endif EVP_MD_CTX *md_ctx; size_t maclength; }; diff --git a/src/openssl/log.c b/src/openssl/log.c new file mode 100644 index 00000000..b29bd914 --- /dev/null +++ b/src/openssl/log.c @@ -0,0 +1,8 @@ +#include +#include "log.h" +#include "../logger.h" + +void openssl_err(const char *msg) { + const char *err = ERR_error_string(ERR_peek_last_error(), NULL); + logger(DEBUG_ALWAYS, LOG_ERR, "OpenSSL error: unable to %s: %s", msg, err); +} diff --git a/src/openssl/log.h b/src/openssl/log.h new file mode 100644 index 00000000..e30697d3 --- /dev/null +++ b/src/openssl/log.h @@ -0,0 +1,6 @@ +#ifndef OPENSSL_LOG_H +#define OPENSSL_LOG_H + +extern void openssl_err(const char *msg); + +#endif // OPENSSL_LOG_H diff --git a/src/openssl/rsa.c b/src/openssl/rsa.c index d50ba1d7..bf6938b9 100644 --- a/src/openssl/rsa.c +++ b/src/openssl/rsa.c @@ -20,112 +20,253 @@ #include "../system.h" #include -#include #include #define TINC_RSA_INTERNAL + +#if OPENSSL_VERSION_MAJOR < 3 typedef RSA rsa_t; +#else +#include +#include +#include +#include +#include + +typedef EVP_PKEY rsa_t; +#endif +#include "log.h" #include "../logger.h" #include "../rsa.h" // Set RSA keys -rsa_t *rsa_set_hex_public_key(const char *n, const char *e) { - BIGNUM *bn_n = NULL; - BIGNUM *bn_e = NULL; +#if OPENSSL_VERSION_MAJOR >= 3 +static EVP_PKEY *build_rsa_key(int selection, const BIGNUM *bn_n, const BIGNUM *bn_e, const BIGNUM *bn_d) { + assert(bn_n); + assert(bn_e); - if((size_t)BN_hex2bn(&bn_n, n) != strlen(n) || (size_t)BN_hex2bn(&bn_e, e) != strlen(e)) { - BN_free(bn_e); - BN_free(bn_n); - return false; + EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_from_name(NULL, "RSA", NULL); + + if(!ctx) { + openssl_err("initialize key context"); + return NULL; } - rsa_t *rsa = RSA_new(); + OSSL_PARAM_BLD *bld = OSSL_PARAM_BLD_new(); + OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_N, bn_n); + OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_E, bn_e); - if(!rsa) { - return NULL; + if(bn_d) { + OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_D, bn_d); } - RSA_set0_key(rsa, bn_n, bn_e, NULL); + OSSL_PARAM *params = OSSL_PARAM_BLD_to_param(bld); + EVP_PKEY *key = NULL; - return rsa; + bool ok = EVP_PKEY_fromdata_init(ctx) > 0 + && EVP_PKEY_fromdata(ctx, &key, selection, params) > 0; + + OSSL_PARAM_free(params); + OSSL_PARAM_BLD_free(bld); + EVP_PKEY_CTX_free(ctx); + + if(ok) { + return key; + } + + openssl_err("build key"); + return NULL; } +#endif -rsa_t *rsa_set_hex_private_key(const char *n, const char *e, const char *d) { +static bool hex_to_bn(BIGNUM **bn, const char *hex) { + return (size_t)BN_hex2bn(bn, hex) == strlen(hex); +} + +static rsa_t *rsa_set_hex_key(const char *n, const char *e, const char *d) { + rsa_t *rsa = NULL; BIGNUM *bn_n = NULL; BIGNUM *bn_e = NULL; BIGNUM *bn_d = NULL; - if((size_t)BN_hex2bn(&bn_n, n) != strlen(n) || (size_t)BN_hex2bn(&bn_e, e) != strlen(e) || (size_t)BN_hex2bn(&bn_d, d) != strlen(d)) { + if(!hex_to_bn(&bn_n, n) || !hex_to_bn(&bn_e, e) || (d && !hex_to_bn(&bn_d, d))) { + goto exit; + } + +#if OPENSSL_VERSION_MAJOR < 3 + rsa = RSA_new(); + + if(rsa) { + RSA_set0_key(rsa, bn_n, bn_e, bn_d); + } + +#else + int selection = bn_d ? EVP_PKEY_KEYPAIR : EVP_PKEY_PUBLIC_KEY; + rsa = build_rsa_key(selection, bn_n, bn_e, bn_d); +#endif + +exit: +#if OPENSSL_VERSION_MAJOR < 3 + + if(!rsa) +#endif + { BN_free(bn_d); BN_free(bn_e); BN_free(bn_n); - return false; } - rsa_t *rsa = RSA_new(); + return rsa; +} - if(!rsa) { +rsa_t *rsa_set_hex_public_key(const char *n, const char *e) { + return rsa_set_hex_key(n, e, NULL); +} + +rsa_t *rsa_set_hex_private_key(const char *n, const char *e, const char *d) { + return rsa_set_hex_key(n, e, d); +} + +// Read PEM RSA keys + +#if OPENSSL_VERSION_MAJOR >= 3 +static rsa_t *read_key_from_pem(FILE *fp, int selection) { + rsa_t *rsa = NULL; + OSSL_DECODER_CTX *ctx = OSSL_DECODER_CTX_new_for_pkey(&rsa, "PEM", NULL, "RSA", selection, NULL, NULL); + + if(!ctx) { + openssl_err("initialize decoder"); return NULL; } - RSA_set0_key(rsa, bn_n, bn_e, bn_d); + bool ok = OSSL_DECODER_from_fp(ctx, fp); + OSSL_DECODER_CTX_free(ctx); + + if(!ok) { + rsa = NULL; + openssl_err("read RSA key from file"); + } return rsa; } - -// Read PEM RSA keys +#endif rsa_t *rsa_read_pem_public_key(FILE *fp) { - rsa_t *rsa = PEM_read_RSAPublicKey(fp, NULL, NULL, NULL); + rsa_t *rsa; + +#if OPENSSL_VERSION_MAJOR < 3 + rsa = PEM_read_RSAPublicKey(fp, NULL, NULL, NULL); if(!rsa) { rewind(fp); rsa = PEM_read_RSA_PUBKEY(fp, NULL, NULL, NULL); } +#else + rsa = read_key_from_pem(fp, OSSL_KEYMGMT_SELECT_PUBLIC_KEY); +#endif + if(!rsa) { - logger(DEBUG_ALWAYS, LOG_ERR, "Unable to read RSA public key: %s", ERR_error_string(ERR_get_error(), NULL)); + openssl_err("read RSA public key"); } return rsa; } rsa_t *rsa_read_pem_private_key(FILE *fp) { - rsa_t *rsa = PEM_read_RSAPrivateKey(fp, NULL, NULL, NULL); + rsa_t *rsa; + +#if OPENSSL_VERSION_MAJOR < 3 + rsa = PEM_read_RSAPrivateKey(fp, NULL, NULL, NULL); +#else + rsa = read_key_from_pem(fp, OSSL_KEYMGMT_SELECT_PRIVATE_KEY); +#endif if(!rsa) { - logger(DEBUG_ALWAYS, LOG_ERR, "Unable to read RSA private key: %s", ERR_error_string(ERR_get_error(), NULL)); + openssl_err("read RSA private key"); } return rsa; } size_t rsa_size(const rsa_t *rsa) { +#if OPENSSL_VERSION_MAJOR < 3 return RSA_size(rsa); +#else + return EVP_PKEY_get_size(rsa); +#endif } bool rsa_public_encrypt(rsa_t *rsa, const void *in, size_t len, void *out) { +#if OPENSSL_VERSION_MAJOR < 3 + if((size_t)RSA_public_encrypt((int) len, in, out, rsa, RSA_NO_PADDING) == len) { return true; } - logger(DEBUG_ALWAYS, LOG_ERR, "Unable to perform RSA encryption: %s", ERR_error_string(ERR_get_error(), NULL)); +#else + EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(rsa, NULL); + + if(ctx) { + size_t outlen = len; + + bool ok = EVP_PKEY_encrypt_init(ctx) > 0 + && EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_NO_PADDING) > 0 + && EVP_PKEY_encrypt(ctx, out, &outlen, in, len) > 0 + && outlen == len; + + EVP_PKEY_CTX_free(ctx); + + if(ok) { + return true; + } + } + +#endif + + openssl_err("perform RSA encryption"); return false; } bool rsa_private_decrypt(rsa_t *rsa, const void *in, size_t len, void *out) { +#if OPENSSL_VERSION_MAJOR < 3 + if((size_t)RSA_private_decrypt((int) len, in, out, rsa, RSA_NO_PADDING) == len) { return true; } - logger(DEBUG_ALWAYS, LOG_ERR, "Unable to perform RSA decryption: %s", ERR_error_string(ERR_get_error(), NULL)); +#else + EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(rsa, NULL); + + if(ctx) { + size_t outlen = len; + + bool ok = EVP_PKEY_decrypt_init(ctx) > 0 + && EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_NO_PADDING) > 0 + && EVP_PKEY_decrypt(ctx, out, &outlen, in, len) > 0 + && outlen == len; + + EVP_PKEY_CTX_free(ctx); + + if(ok) { + return true; + } + } + +#endif + + openssl_err("perform RSA decryption"); return false; } void rsa_free(rsa_t *rsa) { if(rsa) { +#if OPENSSL_VERSION_MAJOR < 3 RSA_free(rsa); +#else + EVP_PKEY_free(rsa); +#endif } } diff --git a/src/openssl/rsagen.c b/src/openssl/rsagen.c index 3674057c..e825bf3b 100644 --- a/src/openssl/rsagen.c +++ b/src/openssl/rsagen.c @@ -23,11 +23,20 @@ #include #define TINC_RSA_INTERNAL + +#if OPENSSL_VERSION_MAJOR < 3 typedef RSA rsa_t; +#else +typedef EVP_PKEY rsa_t; +#include +#include +#endif #include "../logger.h" #include "../rsagen.h" +#include "log.h" +#if OPENSSL_VERSION_MAJOR < 3 /* This function prettyprints the key generation process */ static int indicator(int a, int b, BN_GENCB *cb) { @@ -68,41 +77,99 @@ static int indicator(int a, int b, BN_GENCB *cb) { return 1; } +#endif // Generate RSA key rsa_t *rsa_generate(size_t bits, unsigned long exponent) { BIGNUM *bn_e = BN_new(); - rsa_t *rsa = RSA_new(); - BN_GENCB *cb = BN_GENCB_new(); + rsa_t *rsa = NULL; - if(!bn_e || !rsa || !cb) { + if(!bn_e) { abort(); } BN_set_word(bn_e, exponent); + +#if OPENSSL_VERSION_MAJOR < 3 + rsa = RSA_new(); + BN_GENCB *cb = BN_GENCB_new(); + + if(!rsa || !cb) { + abort(); + } + BN_GENCB_set(cb, indicator, NULL); int result = RSA_generate_key_ex(rsa, (int) bits, bn_e, cb); BN_GENCB_free(cb); - BN_free(bn_e); if(!result) { fprintf(stderr, "Error during key generation!\n"); RSA_free(rsa); - return NULL; + rsa = NULL; + } + +#else + EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL); + + bool ok = ctx + && EVP_PKEY_keygen_init(ctx) > 0 + && EVP_PKEY_CTX_set1_rsa_keygen_pubexp(ctx, bn_e) > 0 + && EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, (int)bits) > 0 + && EVP_PKEY_keygen(ctx, &rsa) > 0; + + if(ctx) { + EVP_PKEY_CTX_free(ctx); } + if(!ok) { + openssl_err("generate key"); + rsa = NULL; + } + +#endif + + BN_free(bn_e); + return rsa; } // Write PEM RSA keys +#if OPENSSL_VERSION_MAJOR >= 3 +static bool write_key_to_pem(const rsa_t *rsa, FILE *fp, int selection) { + OSSL_ENCODER_CTX *enc = OSSL_ENCODER_CTX_new_for_pkey(rsa, selection, "PEM", NULL, NULL); + + if(!enc) { + openssl_err("create encoder context"); + return false; + } + + bool ok = OSSL_ENCODER_to_fp(enc, fp); + OSSL_ENCODER_CTX_free(enc); + + if(!ok) { + openssl_err("write key to file"); + } + + return ok; +} +#endif + bool rsa_write_pem_public_key(rsa_t *rsa, FILE *fp) { +#if OPENSSL_VERSION_MAJOR < 3 return PEM_write_RSAPublicKey(fp, rsa); +#else + return write_key_to_pem(rsa, fp, OSSL_KEYMGMT_SELECT_PUBLIC_KEY); +#endif } bool rsa_write_pem_private_key(rsa_t *rsa, FILE *fp) { +#if OPENSSL_VERSION_MAJOR < 3 return PEM_write_RSAPrivateKey(fp, rsa, NULL, NULL, 0, NULL, NULL); +#else + return write_key_to_pem(rsa, fp, OSSL_KEYMGMT_SELECT_PRIVATE_KEY); +#endif }