The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
id.c
Go to the documentation of this file.
1/*
2 * This program is is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or (at
5 * your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15 */
16
17/**
18 * @file src/lib/eap_aka_sim/id.c
19 * @brief EAP-SIM/EAP-AKA identity detection, creation, and decyption.
20 *
21 * @copyright 2017 The FreeRADIUS server project
22 */
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>
27#include "base.h"
28#include "id.h"
29#include "crypto_priv.h"
30
31#define us(x) (uint8_t) x
32
34 { L("Any-Id-Req"), AKA_SIM_ANY_ID_REQ },
35 { L("FullAuth-Id-Req"), AKA_SIM_FULLAUTH_ID_REQ },
36 { L("Init"), AKA_SIM_INIT_ID_REQ },
37 { L("Permanent-Id-Req"), AKA_SIM_PERMANENT_ID_REQ },
38 { L("no"), AKA_SIM_NO_ID_REQ }, /* Used for config parsing */
39 { L("none"), AKA_SIM_NO_ID_REQ },
40};
42
49
50/** Find where the identity ends
51 *
52 * @param[in] nai we're attempting to split.
53 * @param[in] nai_len The length of the NAI string.
54 * @return
55 * - How long the identity portion of the NAI is.
56 */
57size_t fr_aka_sim_id_user_len(char const *nai, size_t nai_len)
58{
59 char const *p;
60
61 p = (char *)memchr((uint8_t const *)nai, '@', nai_len);
62 if (!p) return nai_len;
63
64 return p - nai;
65}
66
67/** Find where in the NAI string the domain starts
68 *
69 * @param[in] nai we're attempting to split.
70 * @param[in] nai_len The length of the NAI string.
71 * @return
72 * - A pointer to where the domain portion of the domain starts.
73 * - NULL if there was no @ in the identity.
74 */
75char const *fr_aka_sim_domain(char const *nai, size_t nai_len)
76{
77 char const *p;
78
79 p = (char *)memchr((uint8_t const *)nai, '@', nai_len);
80 if (!p) return NULL;
81
82 return p + 1;
83}
84
85/** Extract the MCC and MCN from the 3GPP domain
86 *
87 * 3GPP Root NAI domain format wlan.mnc<MNC>.mcc<MCC>.3gppnetwork.org.
88 *
89 * @param[out] mnc Mobile network code.
90 * @param[out] mcc Mobile country code.
91 * @param[in] domain to parse.
92 * @param[in] domain_len Length of the domain component.
93 * @return
94 * - number of bytes parsed.
95 * - <= 0 on error - The negative offset of where parsing failed.
96 */
98 char const *domain, size_t domain_len)
99{
100 char const *p = domain, *end = p + domain_len;
101 char *q;
102 unsigned long num;
103
104 if (((p + 8) < end) || (CRYPTO_memcmp(p, "wlan.mnc", 8) != 0)) return -1;
105 p += 8;
106
107 if (((p + 3) < end)) {
108 fr_strerror_const("Missing MNC component");
109 return (domain - p);
110 }
111 num = strtoul(p, &q, 10);
112 if (*q != '.') {
113 fr_strerror_const("Invalid MCN component");
114 return (domain - q);
115 }
116 if (mnc) *mnc = (uint16_t)num;
117 p = q + 1;
118
119 if (((p + 3) < end) || (CRYPTO_memcmp(p, "mcc", 3) != 0)) {
120 fr_strerror_const("Missing MCC component");
121 return (domain - p);
122 }
123 num = strtoul(p, &q, 10);
124 if (*q != '.') {
125 fr_strerror_const("Invalid MCC component");
126 return (domain - q);
127 }
128 if (mcc) *mcc = (uint16_t)num;
129
130 p = q + 1;
131 if (((p + 15) < end) || (CRYPTO_memcmp(p, "3gppnetwork.org", 15) != 0)) {
132 fr_strerror_const("Missing 3gppnetwork.org suffix");
133 return (domain - p);
134 }
135 p += 15;
136
137 if (p != end) {
138 fr_strerror_const("Trailing garbage");
139 return (domain - p);
140 }
141
142 return p - domain;
143}
144
145/** Determine what type of ID was provided in the initial identity response
146 *
147 * @param[out] hint Whether this is a hint to do EAP-SIM or EAP-AKA[']:
148 * - AKA_SIM_METHOD_HINT_AKA_PRIME this ID was generated during an EAP-AKA' exchange
149 * or the supplicant hints it wants to perform EAP-AKA'.
150 * - AKA_SIM_METHOD_HINT_AKA this ID was generated during an EAP-AKA exchange
151 * or the supplicant hints it wants to perform EAP-AKA.
152 * - AKA_SIM_METHOD_HINT_SIM this IS was generated during an EAP-SIM exchange
153 * or the supplicant hints it wants to perform EAP-SIM.
154 * - AKA_SIM_METHOD_HINT_UNKNOWN we don't know what type of authentication generated
155 * this ID or which one to start.
156 * @param[out] type What type of identity this is:
157 * - AKA_SIM_ID_TYPE_PERMANENT if the ID is an IMSI.
158 * - AKA_SIM_ID_TYPE_PSEUDONYM if the ID is a freeform pseudonym.
159 * - AKA_SIM_ID_TYPE_FASTAUTH if the ID is a fastauth identity.
160 * - AKA_SIM_ID_TYPE_UNKNOWN if we can't determine what sort of ID this is.
161 * @param[in] id the NAI string provided.
162 * @param[in] id_len the length of the NAI string.
163 * @return
164 * - 0 on success.
165 * - -1 on failure.
166 */
167int 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)
168{
169 size_t i;
170
171 /*
172 * May not fail in the first part of this function
173 * so we need to clear any pending messages.
174 */
176
177 if (id_len < 1) {
178 if (hint) *hint = AKA_SIM_METHOD_HINT_UNKNOWN;
180 fr_strerror_const("ID length too short");
181 return -1;
182 }
183
184 /*
185 * Just get the length of the ID between the start
186 * and the first '@'. Returns the length of the
187 * entire string if no '@' found, so works with
188 * full NAI strings and Stripped-User-Name.
189 */
190 id_len = fr_aka_sim_id_user_len(id, id_len);
191
192 /*
193 * Permanent ID format check
194 */
195 switch (id[0]) {
199 if (id_len > 16) {
200 fr_strerror_printf("IMSI too long, expected <= 16 bytes got %zu bytes", id_len);
201 goto bad_format;
202 }
203
204 for (i = 1; i < id_len; i++) {
205 if (!isdigit(id[i])) {
206 fr_strerror_printf("Invalid digit '%pV' in IMSI \"%pV\"",
207 fr_box_strvalue_len(&id[i], 1),
208 fr_box_strvalue_len(id, id_len));
209 goto bad_format;
210 }
211 }
212
213 switch (id[0]) {
215 if (hint) *hint = AKA_SIM_METHOD_HINT_SIM;
216 if (type) *type = AKA_SIM_ID_TYPE_PERMANENT; /* All digits */
217 return 0;
218
220 if (hint) *hint = AKA_SIM_METHOD_HINT_AKA;
221 if (type) *type = AKA_SIM_ID_TYPE_PERMANENT; /* All digits */
222 return 0;
223
225 if (hint) *hint = AKA_SIM_METHOD_HINT_AKA_PRIME;
226 if (type) *type = AKA_SIM_ID_TYPE_PERMANENT; /* All Digits */
227 return 0;
228
229 default:
230 break;
231 }
232 break;
233
234 default:
235 break;
236 }
237
238bad_format:
239 /*
240 * Pseudonym
241 */
242 switch (id[0]) {
244 if (hint) *hint = AKA_SIM_METHOD_HINT_SIM;
246 return 0;
247
249 if (hint) *hint = AKA_SIM_METHOD_HINT_AKA;
251 return 0;
252
254 if (hint) *hint = AKA_SIM_METHOD_HINT_AKA_PRIME;
256 return 0;
257
258 /*
259 * Fast reauth identity
260 */
262 if (hint) *hint = AKA_SIM_METHOD_HINT_SIM;
264 return 0;
265
267 if (hint) *hint = AKA_SIM_METHOD_HINT_AKA;
269 return 0;
270
272 if (hint) *hint = AKA_SIM_METHOD_HINT_AKA_PRIME;
274 return 0;
275
277 if (hint) *hint = AKA_SIM_METHOD_HINT_UNKNOWN;
279 fr_strerror_const_push("Got SIM-Permanent-ID tag, but identity is not a permanent ID");
280 return -1;
281
283 if (hint) *hint = AKA_SIM_METHOD_HINT_UNKNOWN;
285 fr_strerror_const_push("Got AKA-Permanent-ID tag, but identity is not a permanent ID");
286 return -1;
287
288 default:
289 if (hint) *hint = AKA_SIM_METHOD_HINT_UNKNOWN;
291 fr_strerror_printf_push("Unrecognised tag '%pV'", fr_box_strvalue_len(id, 1));
292 return -1;
293 }
294}
295
296/** Determine if a given identity is a 3gpp identity, and return the EAP method hinted
297 *
298 * @param[in] id to check.
299 * @param[in] len Length of the id.
300 * @return
301 * - FR_EAP_METHOD_INVALID if this is not a 3gpp identity.
302 * - FR_EAP_METHOD_AKA_PRIME if this is an AKA-Prime identity.
303 * - FR_EAP_METHOD_AKA if this is an AKA identity.
304 * - FR_EAP_METHOD_SIM if this is a SIM identity.
305 */
306eap_type_t fr_aka_sim_id_to_eap_type(char const *id, size_t len)
307{
308 char const *domain;
309 char const *end = id + len;
311
312 domain = fr_aka_sim_domain(id, len);
313 if (!domain) {
314 bad_domain:
315 fr_strerror_const("ID missing 3gpp domain (wlan.mnc<MNC>.mcc<MCC>.3gppnetwork.org)");
317 }
318
319 /*
320 * Check the domain has our recognised format
321 */
322 if (fr_aka_sim_3gpp_root_nai_domain_mcc_mnc(NULL, NULL, domain, end - domain) < 0) goto bad_domain;
323
324 if (fr_aka_sim_id_type(NULL, &method, id, domain - id) < 0) return FR_EAP_METHOD_INVALID;
325
326 switch (method) {
329
331 return FR_EAP_METHOD_AKA;
332
334 return FR_EAP_METHOD_SIM;
335
336 default:
337 break;
338 }
339
341}
342
366
367/** Return the expected identity hint for a given type/method combination
368 *
369 * @param[in] type Whether this is a permanent, pseudonym or fastauth ID
370 * @param[in] method What EAP-Method the identity hints at.
371 * @return
372 * - An IMSI tag byte [0-9] (ASCII)
373 * - '\0' if either the method or type values are unknown.
374 */
379
380/** Create a 3gpp pseudonym from a permanent ID
381 *
382 * @param[out] out Where to write the resulting pseudonym, must be a buffer of
383 * exactly AKA_SIM_3GPP_PSEUDONYM_LEN + 1 bytes.
384 * @param[in] imsi Permanent ID to derive pseudonym from. Note: If the IMSI is less than
385 * 15 digits it will be rpadded with zeros.
386 * @param[in] imsi_len Length of the IMSI. Must be between 1-15.
387 * @param[in] tag Tag value to prepend to the pseudonym. This field is 6 bits (0-63).
388 * @param[in] key_ind Key indicator (or key index), the key number used to produce
389 * the encr ID. There may be up to 16 keys in use at any one
390 * time. This field is 4 bits (0-15).
391 * @param[in] key as described by the 'Security aspects of non-3GPP accesses' document.
392 * Must be 128 bits (16 bytes).
393 * @return
394 * - 0 on success.
395 * - -1 if any of the parameters were invalid.
396 */
398 char const *imsi, size_t imsi_len,
399 uint8_t tag, uint8_t key_ind, uint8_t const key[16])
400{
401 uint8_t padded[16]; /* Random (8 bytes) + Compressed (8 bytes) */
402 uint8_t encr[16]; /* aes_ecb(padded) */
403 size_t encr_len, len = 0;
404
405 char *out_p = out;
406
407 char const *p = imsi, *end = p + imsi_len;
408 uint8_t *u_p, *u_end;
409 uint32_t rand[2];
410 uint8_t *compressed = padded + sizeof(rand); /* Part of padded which contains the compressed IMSI */
411
412 EVP_CIPHER_CTX *evp_ctx;
413
414 if (unlikely(key_ind > 15)) { /* 4 bits */
415 fr_strerror_printf("Invalid key indicator value, expected value between 0-15, got %u", key_ind);
416 return -1;
417 }
418 if (unlikely(tag > 63)) { /* 6 bits */
419 fr_strerror_printf("Invalid tag value, expected value between 0-63, got %u", tag);
420 return -1;
421 }
422
423 /*
424 * Technically the IMSI number is between 14-15, but this
425 * encryption scheme only works for 15 char IMSIs.
426 */
427 if (unlikely(imsi_len != AKA_SIM_IMSI_MAX_LEN)) {
428 fr_strerror_printf("Invalid ID len, expected length of 15, got %zu", imsi_len);
429 return -1;
430 }
431 if (unlikely(!key)) {
432 fr_strerror_const("Provided key was NULL");
433 return -1;
434 }
435
436 memset(padded, 0, sizeof(padded)); /* So we don't output garbage if imsi_len < 15 */
437
438 /*
439 * ID is an odd length (15).
440 */
441 *compressed++ = (0xf0 | (*p++ - '0'));
442
443 /*
444 * Because we know the remaining IMSI length
445 * is a multiple of two, we can consume it
446 * 2 bytes at a time (omnomnom).
447 */
448 while (p < end) {
449 if (unlikely(!isdigit((char)p[0]) || !isdigit((char)p[1]))) {
450 fr_strerror_const("IMSI contains invalid character");
451 return -1;
452 }
453
454 *compressed++ = ((p[0] - '0') << 4) | (p[1] - '0');
455 p += 2;
456 }
457
458 /*
459 * Add 8 bytes of random data to pad out the
460 * compressed IMSI to a multiple of 16 (needed
461 * to help secure AES-ECB).
462 */
463 rand[0] = fr_rand();
464 rand[1] = fr_rand();
465
466 memcpy(padded, (uint8_t *)rand, sizeof(rand));
467
468 /*
469 * Now we have to encrypt the padded IMSI with AES-ECB
470 */
471 evp_ctx = aka_sim_crypto_cipher_ctx();
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");
474 error:
475 return -1;
476 }
477
478 /*
479 * By default OpenSSL will try and pad out a 16 byte
480 * plaintext to 32 bytes so that it's detectable that
481 * there was padding.
482 *
483 * In this case we know the length of the plaintext
484 * we're trying to recover, so we explicitly tell
485 * OpenSSL not to pad here, and not to expected padding
486 * when decrypting.
487 */
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");
491 goto error;
492 }
493 encr_len = len;
494
495 if (unlikely(EVP_EncryptFinal_ex(evp_ctx, encr + len, (int *)&len) != 1)) {
496 fr_tls_strerror_printf("Failed finalising encrypted IMSI");
497 goto error;
498 }
499 encr_len += len;
500
501 /*
502 * Ciphertext should be same length as plaintext.
503 */
504 if (unlikely(encr_len != sizeof(padded))) {
505 fr_strerror_printf("Invalid ciphertext length, expected %zu, got %zu", sizeof(padded), encr_len);
506 goto error;
507 }
508
509 /*
510 * Now encode the entire output as base64.
511 */
512 u_p = encr;
513 u_end = u_p + encr_len;
514
515 /*
516 * Consume tag (6 bits) + key_ind (4 bits) + encr[0] (8 bits) = 18 bits (or 3 bytes of b64)
517 */
518 *out_p++ = fr_base64_alphabet_encode[tag & 0x3f]; /* 6 bits tag */
519 *out_p++ = fr_base64_alphabet_encode[((key_ind & 0x0f) << 2) | ((u_p[0] & 0xc0) >> 6)]; /* 4 bits key_ind + 2 high bits encr[0] */
520 *out_p++ = fr_base64_alphabet_encode[u_p[0] & 0x3f]; /* 6 low bits of encr[0] */
521 u_p++;
522
523 /*
524 * Consume 3 bytes of input for 4 bytes of b64 (5 iterations)
525 */
526 while (u_p < u_end) {
527 *out_p++ = fr_base64_alphabet_encode[(u_p[0] & 0xfc) >> 2]; /* 6 high bits of p[0] */
528 *out_p++ = fr_base64_alphabet_encode[((u_p[0] & 0x03) << 4) | ((u_p[1] & 0xf0) >> 4)]; /* 2 low bits of p[0] + 4 high bits of p[1] */
529 *out_p++ = fr_base64_alphabet_encode[((u_p[1] & 0x0f) << 2) | ((u_p[2] & 0xc0) >> 6)]; /* 4 low bits of p[1] + 2 high bits of p[2] */
530 *out_p++ = fr_base64_alphabet_encode[u_p[2] & 0x3f]; /* 6 low bits of p[2] */
531 u_p += 3;
532 }
533 if ((out_p - out) != AKA_SIM_3GPP_PSEUDONYM_LEN) {
534 fr_strerror_printf("Base64 output length invalid, expected %u bytes, got %zu bytes",
535 AKA_SIM_3GPP_PSEUDONYM_LEN, (size_t) (out_p - out));
536 return -1;
537 }
538
540
541 return 0;
542}
543
544/** Return the tag from a 3gpp pseudonym
545 *
546 * @param[in] encr_id The 3gpp pseudonym.
547 *
548 * @return the tag associated with the pseudonym.
549 */
554
555/** Return the key index from a 3gpp pseudonym
556 *
557 * @param[in] encr_id The 3gpp pseudonym.
558 *
559 * @return the key index associated with the pseudonym.
560 */
562{
563 return ((fr_base64_alphabet_decode[us(encr_id[1])] & 0x3c) >> 2);
564}
565
566/** Decrypt the 3GPP pseudonym
567 *
568 * @param[out] out Where to write the decypted, uncompressed IMSI.
569 * @param[in] encr_id to decypt. Will read exactly 23 bytes from the buffer.
570 * @param[in] key to use to decrypt the encrypted and compressed IMSI.
571 * Must be 128 bits (16 bytes).
572 * @return
573 * - 0 on success.
574 * - -1 if any of the parameters were invalid.
575 */
577 char const encr_id[AKA_SIM_3GPP_PSEUDONYM_LEN], uint8_t const key[16])
578{
579 EVP_CIPHER_CTX *evp_ctx;
580
581 char *out_p = out;
582
583 uint8_t dec[16];
584 uint8_t *dec_p = dec;
585
586 uint8_t decr[16];
587 uint8_t *compressed = decr + 8; /* Pointer into plaintext after the random component */
588 size_t decr_len;
589
590 char const *p = encr_id, *end = p + AKA_SIM_3GPP_PSEUDONYM_LEN;
591
592 size_t len = 0;
593 unsigned int i;
594
595 for (i = 0; i < AKA_SIM_3GPP_PSEUDONYM_LEN; i++) {
596 if (!fr_is_base64(encr_id[i])) {
597 fr_strerror_printf("Encrypted IMSI contains non-base64 char '%pV'",
598 fr_box_strvalue_len(&encr_id[i], 1));
599 return -1;
600 }
601 }
602
603 *dec_p++ = (((fr_base64_alphabet_decode[us(p[1])] & 0x03) << 6) | fr_base64_alphabet_decode[us(p[2])]);
604 p += 3;
605
606 while (p < end) {
607 *dec_p++ = ((fr_base64_alphabet_decode[us(p[0])] << 2) | (fr_base64_alphabet_decode[us(p[1])] >> 4));
608 *dec_p++ = ((fr_base64_alphabet_decode[us(p[1])] << 4) & 0xf0) | (fr_base64_alphabet_decode[us(p[2])] >> 2);
609 *dec_p++ = ((fr_base64_alphabet_decode[us(p[2])] << 6) & 0xc0) | fr_base64_alphabet_decode[us(p[3])];
610
611 p += 4; /* 32bit input -> 24bit output */
612 }
613
614 evp_ctx = aka_sim_crypto_cipher_ctx();
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");
617 error:
618 return -1;
619 }
620
621 /*
622 * By default OpenSSL expects 16 bytes of plaintext
623 * to produce 32 bytes of ciphertext, due to padding
624 * being added if the plaintext is a multiple of 16.
625 *
626 * There's no way for OpenSSL to determine if a
627 * 16 byte ciphertext was padded or not, so we need to
628 * inform OpenSSL explicitly that there's no padding.
629 */
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");
633 goto error;
634 }
635 decr_len = len;
636
637 if (unlikely(EVP_DecryptFinal_ex(evp_ctx, decr + len, (int *)&len) != 1)) {
638 fr_tls_strerror_printf("Failed finalising decypted IMSI");
639 goto error;
640 }
641 decr_len += len;
642
643 /*
644 * This should never happen, and probably means that
645 * some sort of memory corruption has occurred.
646 */
647 if (unlikely(decr_len > (AKA_SIM_IMSI_MAX_LEN + 1))) {
648 fr_strerror_printf("Decrypted data len invalid. Expected %u bytes, got %zu bytes",
649 (AKA_SIM_IMSI_MAX_LEN + 1), decr_len);
650 goto error;
651 }
652
653 /*
654 * Decompress the IMSI
655 *
656 * The first 8 octets are a junk random value which
657 * we ignore.
658 */
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';
663 }
665
666 return 0;
667}
668
669#ifdef TESTING_SIM_ID
670/*
671 * cc id.c -g3 -Wall -DHAVE_DLFCN_H -DTESTING_SIM_ID -DWITH_TLS -I../../../../ -I../../../ -I ../base/ -I /usr/local/opt/openssl/include/ -include ../include/build.h -L /usr/local/opt/openssl/lib/ -l ssl -l crypto -l talloc -L ../../../../../build/lib/local/.libs/ -lfreeradius-server -lfreeradius-tls -lfreeradius-util -o test_sim_id && ./test_sim_id
672 */
673#include <stddef.h>
674#include <stdbool.h>
675#include <freeradius-devel/util/acutest.h>
676
677void test_encrypt_decypt_key0(void)
678{
679 char const id[] = "001234554321001";
680 char const key[] = "1234567812345678";
681 uint8_t tag;
682 uint8_t key_ind;
683 char const *log;
684
685 char encrypted_id[AKA_SIM_3GPP_PSEUDONYM_LEN + 1];
686 char decrypted_id[sizeof(id)];
687
688 TEST_CHECK(fr_aka_sim_id_3gpp_pseudonym_encrypt(encrypted_id, id, sizeof(id) - 1, 6, 0, (uint8_t const *)key) == 0);
689 while ((log = fr_strerror_pop())) printf("%s\n", log);
690
691 tag = fr_aka_sim_id_3gpp_pseudonym_tag(encrypted_id);
692 TEST_CHECK(tag == 6);
693 key_ind = fr_aka_sim_id_3gpp_pseudonym_key_index(encrypted_id);
694 TEST_CHECK(key_ind == 0);
695
696 TEST_CHECK(fr_aka_sim_id_3gpp_pseudonym_decrypt(decrypted_id, encrypted_id, (uint8_t const *)key) == 0);
697 while ((log = fr_strerror_pop())) printf("%s\n", log);
698
699 TEST_CHECK(memcmp(id, decrypted_id, 15) == 0);
700}
701
702void test_encrypt_decypt_key1(void)
703{
704 char const id[] = "001234554321001";
705 char const key[] = "1234567812345678";
706 uint8_t tag;
707 uint8_t key_ind;
708 char const *log;
709
710 char encrypted_id[AKA_SIM_3GPP_PSEUDONYM_LEN + 1];
711 char decrypted_id[sizeof(id)];
712
713 TEST_CHECK(fr_aka_sim_id_3gpp_pseudonym_encrypt(encrypted_id, id, sizeof(id) - 1, 11, 1, (uint8_t const *)key) == 0);
714 while ((log = fr_strerror_pop())) printf("%s\n", log);
715
716 tag = fr_aka_sim_id_3gpp_pseudonym_tag(encrypted_id);
717 TEST_CHECK(tag == 11);
718 key_ind = fr_aka_sim_id_3gpp_pseudonym_key_index(encrypted_id);
719 TEST_CHECK(key_ind == 1);
720
721 TEST_CHECK(fr_aka_sim_id_3gpp_pseudonym_decrypt(decrypted_id, encrypted_id, (uint8_t const *)key) == 0);
722 while ((log = fr_strerror_pop())) printf("%s\n", log);
723
724 TEST_CHECK(memcmp(id, decrypted_id, 15) == 0);
725}
726
727void test_encrypt_decypt_key16(void)
728{
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 };
749 uint8_t tag;
750 uint8_t key_ind;
751 char const *log;
752
753 char encrypted_id[AKA_SIM_3GPP_PSEUDONYM_LEN + 1];
754 char decrypted_id[sizeof(id)];
755
756 TEST_CHECK(fr_aka_sim_id_3gpp_pseudonym_encrypt(encrypted_id, id, sizeof(id) - 1,
757 9, 15, (uint8_t const *)keys[15]) == 0);
758 while ((log = fr_strerror_pop())) printf("%s\n", log);
759
760 tag = fr_aka_sim_id_3gpp_pseudonym_tag(encrypted_id);
761 TEST_CHECK(tag == 9);
762 key_ind = fr_aka_sim_id_3gpp_pseudonym_key_index(encrypted_id);
763 TEST_CHECK(key_ind == 15);
764
765 TEST_CHECK(fr_aka_sim_id_3gpp_pseudonym_decrypt(decrypted_id, encrypted_id, (uint8_t const *)keys[key_ind]) == 0);
766 while ((log = fr_strerror_pop())) printf("%s\n", log);
767
768 TEST_CHECK(memcmp(id, decrypted_id, 15) == 0);
769}
770
771TEST_LIST = {
772 /*
773 * Initialisation
774 */
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 },
778
779 { NULL }
780};
781#endif
#define TEST_CHECK(cond)
Definition acutest.h:85
#define TEST_LIST
Definition acutest.h:62
char const fr_base64_alphabet_encode[UINT8_MAX]
Definition base64.c:32
uint8_t const fr_base64_alphabet_decode[UINT8_MAX]
Definition base64.c:99
#define fr_is_base64(_c)
Definition base64.h:67
#define L(_str)
Helper for initialising arrays of string literals.
Definition build.h:209
#define unlikely(_x)
Definition build.h:381
#define NUM_ELEMENTS(_t)
Definition build.h:337
EAP-SIM/EAP-AKA Private crypto functions.
enum eap_type eap_type_t
@ FR_EAP_METHOD_SIM
Definition types.h:63
@ FR_EAP_METHOD_AKA
Definition types.h:68
@ FR_EAP_METHOD_AKA_PRIME
Definition types.h:96
@ FR_EAP_METHOD_INVALID
Definition types.h:45
EVP_CIPHER_CTX * aka_sim_crypto_cipher_ctx(void)
Allocate and reset a resumable EVP_CIPHER_CTX for each thread.
Definition crypto.c:70
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.
Definition id.c:576
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.
Definition id.c:97
fr_table_num_sorted_t const fr_aka_sim_id_method_table[]
Definition id.c:43
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.
Definition id.c:561
size_t fr_aka_sim_id_method_table_len
Definition id.c:48
size_t fr_aka_sim_id_user_len(char const *nai, size_t nai_len)
Find where the identity ends.
Definition id.c:57
#define us(x)
Definition id.c:31
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.
Definition id.c:306
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.
Definition id.c:375
char const * fr_aka_sim_domain(char const *nai, size_t nai_len)
Find where in the NAI string the domain starts.
Definition id.c:75
static char hint_byte_matrix[AKA_SIM_METHOD_HINT_MAX][AKA_SIM_ID_TYPE_MAX]
Definition id.c:343
fr_table_num_sorted_t const fr_aka_sim_id_request_table[]
Definition id.c:33
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.
Definition id.c:550
size_t fr_aka_sim_id_request_table_len
Definition id.c:41
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.
Definition id.c:397
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.
Definition id.c:167
#define AKA_SIM_IMSI_MAX_LEN
Length of an IMSI number in ASCII.
Definition id.h:32
@ AKA_SIM_INIT_ID_REQ
We've requested no ID. This is used for last_id_req.
Definition id.h:78
@ AKA_SIM_NO_ID_REQ
We're not requesting any ID.
Definition id.h:79
@ AKA_SIM_ANY_ID_REQ
Request IMSI, Pseudonym or Fast-reauth.
Definition id.h:80
@ AKA_SIM_FULLAUTH_ID_REQ
Request IMSI or Pseudonym.
Definition id.h:81
@ AKA_SIM_PERMANENT_ID_REQ
Request IMSI.
Definition id.h:82
@ ID_TAG_AKA_PERMANENT
IMSI, and hint that client wants to do EAP-AKA.
Definition id.h:66
@ ID_TAG_SIM_PERMANENT
IMSI, and hint that client wants to do EAP-SIM.
Definition id.h:62
@ ID_TAG_AKA_PRIME_FASTAUTH
Fastuath, continue EAP-AKA-Prime.
Definition id.h:72
@ ID_TAG_AKA_FASTAUTH
Fastauth, continue EAP-AKA.
Definition id.h:68
@ ID_TAG_SIM_PSEUDONYM
Pseudonym, continue EAP-SIM.
Definition id.h:63
@ ID_TAG_AKA_PRIME_PSEUDONYM
Pseudonym, continue EAP-AKA-Prime.
Definition id.h:71
@ ID_TAG_AKA_PSEUDONYM
Pseudonym, continue EAP-AKA.
Definition id.h:67
@ ID_TAG_SIM_FASTAUTH
Fastauth, continue EAP-SIM.
Definition id.h:64
@ ID_TAG_AKA_PRIME_PERMANENT
IMSI, and hint that client wants to do EAP-AKA-Prime.
Definition id.h:70
fr_aka_sim_method_hint_t
SIM/AKA method hints.
Definition id.h:39
@ AKA_SIM_METHOD_HINT_AKA
The identity hints the supplicant wants to use EAP-AKA.
Definition id.h:43
@ AKA_SIM_METHOD_HINT_SIM
The identity hints the supplicant wants to use EAP-SIM.
Definition id.h:41
@ AKA_SIM_METHOD_HINT_AKA_PRIME
Definition id.h:45
@ AKA_SIM_METHOD_HINT_MAX
Definition id.h:46
@ AKA_SIM_METHOD_HINT_UNKNOWN
We don't know what method the identity hints at.
Definition id.h:40
#define AKA_SIM_3GPP_PSEUDONYM_LEN
Length of a base64 encoded 3gpp pseudonym.
Definition id.h:31
fr_aka_sim_id_type_t
SIM/AKA identity type hints.
Definition id.h:53
@ AKA_SIM_ID_TYPE_UNKNOWN
We don't know what type of identity this is.
Definition id.h:54
@ AKA_SIM_ID_TYPE_PSEUDONYM
This is a custom pseudonym.
Definition id.h:56
@ AKA_SIM_ID_TYPE_MAX
Definition id.h:58
@ AKA_SIM_ID_TYPE_PERMANENT
This is a permanent identity (the IMSI of the SIM).
Definition id.h:55
@ AKA_SIM_ID_TYPE_FASTAUTH
This is a fastauth (session-resumption) id.
Definition id.h:57
unsigned short uint16_t
unsigned int uint32_t
long int ssize_t
unsigned char uint8_t
RADIUS bio handlers for tracking 8-bit IDs.
uint32_t fr_rand(void)
Return a 32-bit random number.
Definition rand.c:105
fr_aka_sim_id_type_t type
An element in a lexicographically sorted array of name to num mappings.
Definition table.h:49
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.
Definition strerror.c:577
char const * fr_strerror_pop(void)
Pop the last library error.
Definition strerror.c:681
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
Definition strerror.h:64
#define fr_strerror_printf_push(_fmt,...)
Add a message to an existing stack of messages at the tail.
Definition strerror.h:84
#define fr_strerror_const_push(_msg)
Definition strerror.h:227
#define fr_strerror_const(_msg)
Definition strerror.h:223
#define fr_box_strvalue_len(_val, _len)
Definition value.h:286
static size_t char ** out
Definition value.h:997