Add support for OpenSSL 3.0+
authorKirill Isakov <bootctl@gmail.com>
Thu, 17 Mar 2022 14:49:29 +0000 (20:49 +0600)
committerKirill Isakov <bootctl@gmail.com>
Thu, 17 Mar 2022 16:15:37 +0000 (22:15 +0600)
Also use centralized logging for OpenSSL errors.

https://github.com/gsliepen/tinc/issues/347

src/Makefile.am
src/openssl/cipher.c
src/openssl/crypto.c
src/openssl/digest.c
src/openssl/digest.h
src/openssl/log.c [new file with mode: 0644]
src/openssl/log.h [new file with mode: 0644]
src/openssl/rsa.c
src/openssl/rsagen.c

index 7d6a31d..dc124b1 100644 (file)
@@ -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 += \
index 6b2affc..77747f1 100644 (file)
 
 #include "../system.h"
 
-#include <openssl/rand.h>
 #include <openssl/err.h>
 #include <openssl/evp.h>
 
+#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;
 }
 
index 34009d6..c2df0af 100644 (file)
@@ -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");
index 71eaec7..6e27b15 100644 (file)
 #include <openssl/err.h>
 #include <openssl/hmac.h>
 
+#if OPENSSL_VERSION_MAJOR >= 3
+#include <openssl/core_names.h>
+#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;
                }
        }
index 66d5416..d6efacf 100644 (file)
 
 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 (file)
index 0000000..b29bd91
--- /dev/null
@@ -0,0 +1,8 @@
+#include <openssl/err.h>
+#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 (file)
index 0000000..e30697d
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef OPENSSL_LOG_H
+#define OPENSSL_LOG_H
+
+extern void openssl_err(const char *msg);
+
+#endif // OPENSSL_LOG_H
index d50ba1d..bf6938b 100644 (file)
 #include "../system.h"
 
 #include <openssl/pem.h>
-#include <openssl/err.h>
 #include <openssl/rsa.h>
 
 #define TINC_RSA_INTERNAL
+
+#if OPENSSL_VERSION_MAJOR < 3
 typedef RSA rsa_t;
+#else
+#include <openssl/encoder.h>
+#include <openssl/decoder.h>
+#include <openssl/core_names.h>
+#include <openssl/param_build.h>
+#include <assert.h>
+
+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
        }
 }
index 3674057..e825bf3 100644 (file)
 #include <openssl/err.h>
 
 #define TINC_RSA_INTERNAL
+
+#if OPENSSL_VERSION_MAJOR < 3
 typedef RSA rsa_t;
+#else
+typedef EVP_PKEY rsa_t;
+#include <openssl/encoder.h>
+#include <openssl/evp.h>
+#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
 }