#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
*
--- /dev/null
+#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
#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
}
}
- 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;
}
}
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.
#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 */
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;
}
* 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);
}
#include <openssl/evp.h>
#include "base.h"
#include "id.h"
+#include "crypto_priv.h"
#define us(x) (uint8_t) x
/*
* 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;
}
goto error;
}
- EVP_CIPHER_CTX_free(evp_ctx);
-
/*
* Now encode the entire output as base64.
*/
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;
}
*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;