23#include <freeradius-devel/tls/strerror.h>
24#include <freeradius-devel/util/base64.h>
25#include <freeradius-devel/util/rand.h>
26#include <openssl/evp.h>
31#define us(x) (uint8_t) x
61 p = (
char *)memchr((
uint8_t const *)nai,
'@', nai_len);
62 if (!p)
return nai_len;
79 p = (
char *)memchr((
uint8_t const *)nai,
'@', nai_len);
98 char const *domain,
size_t domain_len)
100 char const *p = domain, *end = p + domain_len;
104 if (((p + 8) < end) || (CRYPTO_memcmp(p,
"wlan.mnc", 8) != 0))
return -1;
107 if (((p + 3) < end)) {
111 num = strtoul(p, &q, 10);
119 if (((p + 3) < end) || (CRYPTO_memcmp(p,
"mcc", 3) != 0)) {
123 num = strtoul(p, &q, 10);
131 if (((p + 15) < end) || (CRYPTO_memcmp(p,
"3gppnetwork.org", 15) != 0)) {
204 for (i = 1; i < id_len; i++) {
205 if (!isdigit(
id[i])) {
309 char const *end =
id + len;
315 fr_strerror_const(
"ID missing 3gpp domain (wlan.mnc<MNC>.mcc<MCC>.3gppnetwork.org)");
398 char const *imsi,
size_t imsi_len,
403 size_t encr_len, len = 0;
407 char const *p = imsi, *end = p + imsi_len;
410 uint8_t *compressed = padded +
sizeof(rand);
412 EVP_CIPHER_CTX *evp_ctx;
415 fr_strerror_printf(
"Invalid key indicator value, expected value between 0-15, got %u", key_ind);
436 memset(padded, 0,
sizeof(padded));
441 *compressed++ = (0xf0 | (*p++ -
'0'));
449 if (
unlikely(!isdigit((
char)p[0]) || !isdigit((
char)p[1]))) {
454 *compressed++ = ((p[0] -
'0') << 4) | (p[1] -
'0');
466 memcpy(padded, (
uint8_t *)rand,
sizeof(rand));
472 if (
unlikely(EVP_EncryptInit_ex(evp_ctx, EVP_aes_128_ecb(), NULL, key, NULL) != 1)) {
473 fr_tls_strerror_printf(
"Failed initialising AES-128-ECB context");
488 EVP_CIPHER_CTX_set_padding(evp_ctx, 0);
489 if (
unlikely(EVP_EncryptUpdate(evp_ctx, encr, (
int *)&len, padded,
sizeof(padded)) != 1)) {
490 fr_tls_strerror_printf(
"Failed encrypting padded IMSI");
495 if (
unlikely(EVP_EncryptFinal_ex(evp_ctx, encr + len, (
int *)&len) != 1)) {
496 fr_tls_strerror_printf(
"Failed finalising encrypted IMSI");
504 if (
unlikely(encr_len !=
sizeof(padded))) {
505 fr_strerror_printf(
"Invalid ciphertext length, expected %zu, got %zu",
sizeof(padded), encr_len);
513 u_end = u_p + encr_len;
526 while (u_p < u_end) {
579 EVP_CIPHER_CTX *evp_ctx;
587 uint8_t *compressed = decr + 8;
615 if (
unlikely(EVP_DecryptInit_ex(evp_ctx, EVP_aes_128_ecb(), NULL, key, NULL) != 1)) {
616 fr_tls_strerror_printf(
"Failed initialising AES-128-ECB context");
630 EVP_CIPHER_CTX_set_padding(evp_ctx, 0);
631 if (
unlikely(EVP_DecryptUpdate(evp_ctx, decr, (
int *)&len, dec,
sizeof(dec)) != 1)) {
632 fr_tls_strerror_printf(
"Failed decypting IMSI");
637 if (
unlikely(EVP_DecryptFinal_ex(evp_ctx, decr + len, (
int *)&len) != 1)) {
638 fr_tls_strerror_printf(
"Failed finalising decypted IMSI");
659 *out_p++ = (compressed[0] & 0x0f) +
'0';
660 for (i = 1; i < 8; i++) {
661 *out_p++ = ((compressed[i] & 0xf0) >> 4) +
'0';
662 *out_p++ = (compressed[i] & 0x0f) +
'0';
675#include <freeradius-devel/util/acutest.h>
677void test_encrypt_decypt_key0(
void)
679 char const id[] =
"001234554321001";
680 char const key[] =
"1234567812345678";
686 char decrypted_id[
sizeof(id)];
699 TEST_CHECK(memcmp(
id, decrypted_id, 15) == 0);
702void test_encrypt_decypt_key1(
void)
704 char const id[] =
"001234554321001";
705 char const key[] =
"1234567812345678";
711 char decrypted_id[
sizeof(id)];
724 TEST_CHECK(memcmp(
id, decrypted_id, 15) == 0);
727void test_encrypt_decypt_key16(
void)
729 char const id[] =
"001234554321001";
730 char const key0[] =
"1234567812345678";
731 char const key1[] =
"2222222288888888";
732 char const key2[] =
"2222222299999999";
733 char const key3[] =
"2222222200000000";
734 char const key4[] =
"2222222211111111";
735 char const key5[] =
"2222222222222222";
736 char const key6[] =
"2222222233333333";
737 char const key7[] =
"2222222244444444";
738 char const key8[] =
"2222222255555555";
739 char const key9[] =
"2222222266666666";
740 char const key10[] =
"2222222277777777";
741 char const key11[] =
"1111111188888888";
742 char const key12[] =
"2222222288888888";
743 char const key13[] =
"3333333388888888";
744 char const key14[] =
"4444444488888888";
745 char const key15[] =
"5555555588888888";
746 char const *keys[] = { key0, key1, key2, key3, key4, key5,
747 key6, key7, key8, key9, key10, key11,
748 key12, key13, key14, key15 };
754 char decrypted_id[
sizeof(id)];
757 9, 15, (
uint8_t const *)keys[15]) == 0);
768 TEST_CHECK(memcmp(
id, decrypted_id, 15) == 0);
775 {
L(
"encrypt_decrypt key0"), test_encrypt_decypt_key0 },
776 {
L(
"encrypt_decrypt key1"), test_encrypt_decypt_key1 },
777 {
L(
"encrypt_decrypt key16"), test_encrypt_decypt_key16 },
char const fr_base64_alphabet_encode[UINT8_MAX]
uint8_t const fr_base64_alphabet_decode[UINT8_MAX]
#define L(_str)
Helper for initialising arrays of string literals.
EAP-SIM/EAP-AKA Private crypto functions.
@ FR_EAP_METHOD_AKA_PRIME
EVP_CIPHER_CTX * aka_sim_crypto_cipher_ctx(void)
Allocate and reset a resumable EVP_CIPHER_CTX for each thread.
int fr_aka_sim_id_3gpp_pseudonym_decrypt(char out[AKA_SIM_IMSI_MAX_LEN+1], char const encr_id[AKA_SIM_3GPP_PSEUDONYM_LEN], uint8_t const key[16])
Decrypt the 3GPP pseudonym.
ssize_t fr_aka_sim_3gpp_root_nai_domain_mcc_mnc(uint16_t *mnc, uint16_t *mcc, char const *domain, size_t domain_len)
Extract the MCC and MCN from the 3GPP domain.
fr_table_num_sorted_t const fr_aka_sim_id_method_table[]
uint8_t fr_aka_sim_id_3gpp_pseudonym_key_index(char const encr_id[AKA_SIM_3GPP_PSEUDONYM_LEN])
Return the key index from a 3gpp pseudonym.
size_t fr_aka_sim_id_method_table_len
size_t fr_aka_sim_id_user_len(char const *nai, size_t nai_len)
Find where the identity ends.
eap_type_t fr_aka_sim_id_to_eap_type(char const *id, size_t len)
Determine if a given identity is a 3gpp identity, and return the EAP method hinted.
char fr_aka_sim_hint_byte(fr_aka_sim_id_type_t type, fr_aka_sim_method_hint_t method)
Return the expected identity hint for a given type/method combination.
char const * fr_aka_sim_domain(char const *nai, size_t nai_len)
Find where in the NAI string the domain starts.
static char hint_byte_matrix[AKA_SIM_METHOD_HINT_MAX][AKA_SIM_ID_TYPE_MAX]
fr_table_num_sorted_t const fr_aka_sim_id_request_table[]
uint8_t fr_aka_sim_id_3gpp_pseudonym_tag(char const encr_id[AKA_SIM_3GPP_PSEUDONYM_LEN])
Return the tag from a 3gpp pseudonym.
size_t fr_aka_sim_id_request_table_len
int fr_aka_sim_id_3gpp_pseudonym_encrypt(char out[AKA_SIM_3GPP_PSEUDONYM_LEN+1], char const *imsi, size_t imsi_len, uint8_t tag, uint8_t key_ind, uint8_t const key[16])
Create a 3gpp pseudonym from a permanent ID.
int fr_aka_sim_id_type(fr_aka_sim_id_type_t *type, fr_aka_sim_method_hint_t *hint, char const *id, size_t id_len)
Determine what type of ID was provided in the initial identity response.
#define AKA_SIM_IMSI_MAX_LEN
Length of an IMSI number in ASCII.
@ AKA_SIM_INIT_ID_REQ
We've requested no ID. This is used for last_id_req.
@ AKA_SIM_NO_ID_REQ
We're not requesting any ID.
@ AKA_SIM_ANY_ID_REQ
Request IMSI, Pseudonym or Fast-reauth.
@ AKA_SIM_FULLAUTH_ID_REQ
Request IMSI or Pseudonym.
@ AKA_SIM_PERMANENT_ID_REQ
Request IMSI.
@ ID_TAG_AKA_PERMANENT
IMSI, and hint that client wants to do EAP-AKA.
@ ID_TAG_SIM_PERMANENT
IMSI, and hint that client wants to do EAP-SIM.
@ ID_TAG_AKA_PRIME_FASTAUTH
Fastuath, continue EAP-AKA-Prime.
@ ID_TAG_AKA_FASTAUTH
Fastauth, continue EAP-AKA.
@ ID_TAG_SIM_PSEUDONYM
Pseudonym, continue EAP-SIM.
@ ID_TAG_AKA_PRIME_PSEUDONYM
Pseudonym, continue EAP-AKA-Prime.
@ ID_TAG_AKA_PSEUDONYM
Pseudonym, continue EAP-AKA.
@ ID_TAG_SIM_FASTAUTH
Fastauth, continue EAP-SIM.
@ ID_TAG_AKA_PRIME_PERMANENT
IMSI, and hint that client wants to do EAP-AKA-Prime.
fr_aka_sim_method_hint_t
SIM/AKA method hints.
@ AKA_SIM_METHOD_HINT_AKA
The identity hints the supplicant wants to use EAP-AKA.
@ AKA_SIM_METHOD_HINT_SIM
The identity hints the supplicant wants to use EAP-SIM.
@ AKA_SIM_METHOD_HINT_AKA_PRIME
@ AKA_SIM_METHOD_HINT_MAX
@ AKA_SIM_METHOD_HINT_UNKNOWN
We don't know what method the identity hints at.
#define AKA_SIM_3GPP_PSEUDONYM_LEN
Length of a base64 encoded 3gpp pseudonym.
fr_aka_sim_id_type_t
SIM/AKA identity type hints.
@ AKA_SIM_ID_TYPE_UNKNOWN
We don't know what type of identity this is.
@ AKA_SIM_ID_TYPE_PSEUDONYM
This is a custom pseudonym.
@ AKA_SIM_ID_TYPE_PERMANENT
This is a permanent identity (the IMSI of the SIM).
@ AKA_SIM_ID_TYPE_FASTAUTH
This is a fastauth (session-resumption) id.
RADIUS bio handlers for tracking 8-bit IDs.
uint32_t fr_rand(void)
Return a 32-bit random number.
fr_aka_sim_id_type_t type
An element in a lexicographically sorted array of name to num mappings.
Master include file to access all functions and structures in the library.
void fr_strerror_clear(void)
Clears all pending messages from the talloc pools.
char const * fr_strerror_pop(void)
Pop the last library error.
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
#define fr_strerror_printf_push(_fmt,...)
Add a message to an existing stack of messages at the tail.
#define fr_strerror_const_push(_msg)
#define fr_strerror_const(_msg)
#define fr_box_strvalue_len(_val, _len)
static size_t char ** out