]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
Avoid allocating a new EVP_CIPHER_CTX for every operation
authorArran Cudbard-Bell <a.cudbardb@freeradius.org>
Mon, 29 Mar 2021 14:43:42 +0000 (15:43 +0100)
committerArran Cudbard-Bell <a.cudbardb@freeradius.org>
Wed, 31 Mar 2021 15:12:10 +0000 (16:12 +0100)
src/lib/eap_aka_sim/crypto.c
src/lib/eap_aka_sim/crypto_priv.h [new file with mode: 0644]
src/lib/eap_aka_sim/decode.c
src/lib/eap_aka_sim/encode.c
src/lib/eap_aka_sim/id.c

index 438ec229009f5c32ca3938cb6cc28b8900cc0cc6..f36dd3be385333bd3888664b30969ea381482794 100644 (file)
@@ -42,10 +42,43 @@ RCSID("$Id$")
 #include <freeradius-devel/util/proto.h>
 #include <freeradius-devel/util/rand.h>
 #include <freeradius-devel/util/sha1.h>
+#include <freeradius-devel/util/thread_local.h>
 #include <openssl/evp.h>
 
 #include "base.h"
 #include "attrs.h"
+#include "crypto_priv.h"
+
+/** Used for every non-persistent EVP_CIPHER operation in this library
+ *
+ * Avoids memory churn allocating and freeing the ctx for each operation.
+ */
+static _Thread_local EVP_CIPHER_CTX *evp_chipher_ctx;
+
+static void _evp_cipher_ctx_free_on_exit(void *arg)
+{
+       EVP_CIPHER_CTX_free(arg);
+}
+
+/** Allocate and reset a resumable EVP_CIPHER_CTX for each thread
+ *
+ * No two crypto operations ever occur simultaneously in the same thread,
+ * so it's fine to use a single context that persists for all operations.
+ */
+EVP_CIPHER_CTX *aka_sim_crypto_cipher_ctx(void)
+{
+       EVP_CIPHER_CTX *ctx;
+
+       if (unlikely(!evp_chipher_ctx)) {
+               MEM(ctx = EVP_CIPHER_CTX_new());
+               fr_thread_local_set_destructor(evp_chipher_ctx, _evp_cipher_ctx_free_on_exit, ctx);
+       } else {
+               ctx = evp_chipher_ctx;
+               EVP_CIPHER_CTX_reset(ctx);
+       }
+
+       return ctx;
+}
 
 /** Free OpenSSL memory associated with our checkcode ctx
  *
diff --git a/src/lib/eap_aka_sim/crypto_priv.h b/src/lib/eap_aka_sim/crypto_priv.h
new file mode 100644 (file)
index 0000000..488fba5
--- /dev/null
@@ -0,0 +1,35 @@
+#pragma once
+/*
+ *   This program is 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.
+ *
+ *   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.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/**
+ * @file src/lib/eap_aka_sim/crypto_priv.h
+ * @brief EAP-SIM/EAP-AKA Private crypto functions
+ *
+ * @copyright 2021 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
+ */
+#include <sys/types.h>
+#include <freeradius-devel/util/token.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+EVP_CIPHER_CTX *aka_sim_crypto_cipher_ctx(void);
+
+#ifdef __cplusplus
+}
+#endif
index 7d7e81e1366ca2d5606e7564a4c1988762b429ec..f02d6b5e347f3f1447949e37a1aa1dbdd58d3991 100644 (file)
@@ -35,8 +35,9 @@ RCSID("$Id$")
 #include <freeradius-devel/io/test_point.h>
 
 #include <freeradius-devel/eap/types.h>
-#include "base.h"
 #include "attrs.h"
+#include "base.h"
+#include "crypto_priv.h"
 
 /*
  *  EAP-SIM/AKA/AKA' PACKET FORMAT
@@ -173,17 +174,16 @@ static ssize_t sim_value_decrypt(TALLOC_CTX *ctx, uint8_t **out,
                }
        }
 
-       evp_ctx = EVP_CIPHER_CTX_new();
-       if (!evp_ctx) {
-               tls_strerror_printf("%s: Failed initialising EVP ctx", __FUNCTION__);
-               return -1;
+       if (unlikely(!packet_ctx->k_encr)) {
+               fr_strerror_printf("%s: No k_encr set, cannot decrypt attributes", __FUNCTION__);
+               return PAIR_ENCODE_FATAL_ERROR;
        }
 
-       if (!EVP_DecryptInit_ex(evp_ctx, evp_cipher, NULL, packet_ctx->keys->k_encr, packet_ctx->iv)) {
+       evp_ctx = aka_sim_crypto_cipher_ctx();
+       if (!EVP_DecryptInit_ex(evp_ctx, evp_cipher, NULL, packet_ctx->k_encr, packet_ctx->iv)) {
                tls_strerror_printf("%s: Failed setting decryption parameters", __FUNCTION__);
        error:
                talloc_free(decr);
-               EVP_CIPHER_CTX_free(evp_ctx);
                return -1;
        }
 
@@ -211,8 +211,6 @@ static ssize_t sim_value_decrypt(TALLOC_CTX *ctx, uint8_t **out,
        }
        decr_len += len;
 
-       EVP_CIPHER_CTX_free(evp_ctx);
-
        /*
         *      Note: packet_ctx implicitly validates the length of the padding
         *      attribute (if present), so we don't have to do it later.
index bdcccdb871783c5086ca4619c2d961cf2b267f61..ff2850cbc2cb68347dd13972b5cad697410bfcfc 100644 (file)
@@ -34,6 +34,7 @@ RCSID("$Id$")
 #include <freeradius-devel/eap/types.h>
 #include "base.h"
 #include "attrs.h"
+#include "crypto_priv.h"
 
 #define SIM_MAX_ATTRIBUTE_VALUE_LEN    ((255 * 4) - 2)         /* max length field value less Type + Length fields */
 
@@ -191,18 +192,17 @@ static ssize_t encode_encrypted_value(fr_dbuff_t *dbuff,
                FR_PROTO_HEX_DUMP(fr_dbuff_start(&work_dbuff), pad_len, "Done padding attribute");
        }
 
-       evp_ctx = EVP_CIPHER_CTX_new();
-       if (!evp_ctx) {
-               tls_strerror_printf("Failed allocating EVP context");
+       if (unlikely(!packet_ctx->k_encr)) {
+               fr_strerror_printf("%s: No k_encr set, cannot encrypt attributes", __FUNCTION__);
                return PAIR_ENCODE_FATAL_ERROR;
        }
 
+       evp_ctx = aka_sim_crypto_cipher_ctx();
        if (unlikely(EVP_EncryptInit_ex(evp_ctx, evp_cipher, NULL,
-                                       packet_ctx->keys->k_encr, packet_ctx->iv) != 1)) {
+                                       packet_ctx->k_encr, packet_ctx->iv) != 1)) {
                tls_strerror_printf("Failed initialising AES-128-ECB context");
        error:
                talloc_free(encr);
-               EVP_CIPHER_CTX_free(evp_ctx);
                return PAIR_ENCODE_FATAL_ERROR;
        }
 
@@ -251,10 +251,10 @@ static ssize_t encode_encrypted_value(fr_dbuff_t *dbuff,
         *      Overwrite the plaintext with our encrypted blob
         */
        fr_dbuff_set_to_start(&work_dbuff);
-       FR_DBUFF_IN_MEMCPY_RETURN(&work_dbuff, encr, encr_len);
 
+       slen = fr_dbuff_in_memcpy(&work_dbuff, encr, encr_len);
        talloc_free(encr);
-       EVP_CIPHER_CTX_free(evp_ctx);
+       if (slen <= 0) return slen;
 
        return fr_dbuff_set(dbuff, &work_dbuff);
 }
index ff426c1c8e24d92b75d8038091e95879d0e4eefd..bbeb3d5a94a1436f030169ffee6d1298c8fafc74 100644 (file)
@@ -26,6 +26,7 @@
 #include <openssl/evp.h>
 #include "base.h"
 #include "id.h"
+#include "crypto_priv.h"
 
 #define us(x) (uint8_t) x
 
@@ -413,16 +414,10 @@ int fr_aka_sim_id_3gpp_pseudonym_encrypt(char out[AKA_SIM_3GPP_PSEUDONYM_LEN + 1
        /*
         *      Now we have to encrypt the padded IMSI with AES-ECB
         */
-       evp_ctx = EVP_CIPHER_CTX_new();
-       if (!evp_ctx) {
-               tls_strerror_printf("Failed allocating EVP context");
-               return -1;
-       }
-
+       evp_ctx = aka_sim_crypto_cipher_ctx();
        if (unlikely(EVP_EncryptInit_ex(evp_ctx, EVP_aes_128_ecb(), NULL, key, NULL) != 1)) {
                tls_strerror_printf("Failed initialising AES-128-ECB context");
        error:
-               EVP_CIPHER_CTX_free(evp_ctx);
                return -1;
        }
 
@@ -457,8 +452,6 @@ int fr_aka_sim_id_3gpp_pseudonym_encrypt(char out[AKA_SIM_3GPP_PSEUDONYM_LEN + 1
                goto error;
        }
 
-       EVP_CIPHER_CTX_free(evp_ctx);
-
        /*
         *      Now encode the entire output as base64.
         */
@@ -564,16 +557,10 @@ int fr_aka_sim_id_3gpp_pseudonym_decrypt(char out[AKA_SIM_IMSI_MAX_LEN + 1],
                p += 4; /* 32bit input -> 24bit output */
        }
 
-       evp_ctx = EVP_CIPHER_CTX_new();
-       if (!evp_ctx) {
-               tls_strerror_printf("Failed allocating EVP context");
-               return -1;
-       }
-
+       evp_ctx = aka_sim_crypto_cipher_ctx();
        if (unlikely(EVP_DecryptInit_ex(evp_ctx, EVP_aes_128_ecb(), NULL, key, NULL) != 1)) {
                tls_strerror_printf("Failed initialising AES-128-ECB context");
        error:
-               EVP_CIPHER_CTX_free(evp_ctx);
                return -1;
        }
 
@@ -620,9 +607,6 @@ int fr_aka_sim_id_3gpp_pseudonym_decrypt(char out[AKA_SIM_IMSI_MAX_LEN + 1],
                *out_p++ = ((compressed[i] & 0xf0) >> 4) + '0';
                *out_p++ = (compressed[i] & 0x0f) + '0';
        }
-
-       EVP_CIPHER_CTX_free(evp_ctx);
-
        out[AKA_SIM_IMSI_MAX_LEN] = '\0';
 
        return 0;