The FreeRADIUS server $Id: a4e7f8c94e8038b7627bcf52356b7ed15955c1d7 $
Loading...
Searching...
No Matches
id.c
Go to the documentation of this file.
1/*
2 * This program 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 = memchr(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 = memchr(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 */
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;
404 int len = 0;
405
406 char *out_p = out;
407
408 char const *p = imsi, *end = p + imsi_len;
409 uint8_t *u_p, *u_end;
410 uint32_t rand[2];
411 uint8_t *compressed = padded + sizeof(rand); /* Part of padded which contains the compressed IMSI */
412
413 EVP_CIPHER_CTX *evp_ctx;
414
415 if (unlikely(key_ind > 15)) { /* 4 bits */
416 fr_strerror_printf("Invalid key indicator value, expected value between 0-15, got %u", key_ind);
417 return -1;
418 }
419 if (unlikely(tag > 63)) { /* 6 bits */
420 fr_strerror_printf("Invalid tag value, expected value between 0-63, got %u", tag);
421 return -1;
422 }
423
424 /*
425 * Technically the IMSI number is between 14-15, but this
426 * encryption scheme only works for 15 char IMSIs.
427 */
428 if (unlikely(imsi_len != AKA_SIM_IMSI_MAX_LEN)) {
429 fr_strerror_printf("Invalid ID len, expected length of 15, got %zu", imsi_len);
430 return -1;
431 }
432 if (unlikely(!key)) {
433 fr_strerror_const("Provided key was NULL");
434 return -1;
435 }
436
437 memset(padded, 0, sizeof(padded)); /* So we don't output garbage if imsi_len < 15 */
438
439 /*
440 * ID is an odd length (15).
441 */
442 *compressed++ = (0xf0 | (*p++ - '0'));
443
444 /*
445 * Because we know the remaining IMSI length
446 * is a multiple of two, we can consume it
447 * 2 bytes at a time (omnomnom).
448 */
449 while (p < end) {
450 if (unlikely(!isdigit((char)p[0]) || !isdigit((char)p[1]))) {
451 fr_strerror_const("IMSI contains invalid character");
452 return -1;
453 }
454
455 *compressed++ = ((p[0] - '0') << 4) | (p[1] - '0');
456 p += 2;
457 }
458
459 /*
460 * Add 8 bytes of random data to pad out the
461 * compressed IMSI to a multiple of 16 (needed
462 * to help secure AES-ECB).
463 */
464 rand[0] = fr_rand();
465 rand[1] = fr_rand();
466
467 memcpy(padded, (uint8_t *)rand, sizeof(rand));
468
469 /*
470 * Now we have to encrypt the padded IMSI with AES-ECB
471 */
472 evp_ctx = aka_sim_crypto_cipher_ctx();
473 if (unlikely(EVP_EncryptInit_ex(evp_ctx, EVP_aes_128_ecb(), NULL, key, NULL) != 1)) {
474 fr_tls_strerror_printf("Failed initialising AES-128-ECB context");
475 error:
476 return -1;
477 }
478
479 /*
480 * By default OpenSSL will try and pad out a 16 byte
481 * plaintext to 32 bytes so that it's detectable that
482 * there was padding.
483 *
484 * In this case we know the length of the plaintext
485 * we're trying to recover, so we explicitly tell
486 * OpenSSL not to pad here, and not to expected padding
487 * when decrypting.
488 */
489 EVP_CIPHER_CTX_set_padding(evp_ctx, 0);
490 if (unlikely(EVP_EncryptUpdate(evp_ctx, encr, &len, padded, sizeof(padded)) != 1)) {
491 fr_tls_strerror_printf("Failed encrypting padded IMSI");
492 goto error;
493 }
494 encr_len = len;
495
496 if (unlikely(EVP_EncryptFinal_ex(evp_ctx, encr + len, &len) != 1)) {
497 fr_tls_strerror_printf("Failed finalising encrypted IMSI");
498 goto error;
499 }
500 encr_len += len;
501
502 /*
503 * Ciphertext should be same length as plaintext.
504 */
505 if (unlikely(encr_len != sizeof(padded))) {
506 fr_strerror_printf("Invalid ciphertext length, expected %zu, got %zu", sizeof(padded), encr_len);
507 goto error;
508 }
509
510 /*
511 * Now encode the entire output as base64.
512 */
513 u_p = encr;
514 u_end = u_p + encr_len;
515
516 /*
517 * Consume tag (6 bits) + key_ind (4 bits) + encr[0] (8 bits) = 18 bits (or 3 bytes of b64)
518 */
519 *out_p++ = fr_base64_alphabet_encode[tag & 0x3f]; /* 6 bits tag */
520 *out_p++ = fr_base64_alphabet_encode[((key_ind & 0x0f) << 2) | ((u_p[0] & 0xc0) >> 6)]; /* 4 bits key_ind + 2 high bits encr[0] */
521 *out_p++ = fr_base64_alphabet_encode[u_p[0] & 0x3f]; /* 6 low bits of encr[0] */
522 u_p++;
523
524 /*
525 * Consume 3 bytes of input for 4 bytes of b64 (5 iterations)
526 */
527 while (u_p < u_end) {
528 *out_p++ = fr_base64_alphabet_encode[(u_p[0] & 0xfc) >> 2]; /* 6 high bits of p[0] */
529 *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] */
530 *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] */
531 *out_p++ = fr_base64_alphabet_encode[u_p[2] & 0x3f]; /* 6 low bits of p[2] */
532 u_p += 3;
533 }
534 if ((out_p - out) != AKA_SIM_3GPP_PSEUDONYM_LEN) {
535 fr_strerror_printf("Base64 output length invalid, expected %u bytes, got %zu bytes",
536 AKA_SIM_3GPP_PSEUDONYM_LEN, (size_t) (out_p - out));
537 return -1;
538 }
539
541
542 return 0;
543}
544
545/** Return the tag from a 3gpp pseudonym
546 *
547 * @param[in] encr_id The 3gpp pseudonym.
548 *
549 * @return the tag associated with the pseudonym.
550 */
555
556/** Return the key index from a 3gpp pseudonym
557 *
558 * @param[in] encr_id The 3gpp pseudonym.
559 *
560 * @return the key index associated with the pseudonym.
561 */
563{
564 return ((fr_base64_alphabet_decode[us(encr_id[1])] & 0x3c) >> 2);
565}
566
567/** Decrypt the 3GPP pseudonym
568 *
569 * @param[out] out Where to write the decypted, uncompressed IMSI.
570 * @param[in] encr_id to decypt. Will read exactly 23 bytes from the buffer.
571 * @param[in] key to use to decrypt the encrypted and compressed IMSI.
572 * Must be 128 bits (16 bytes).
573 * @return
574 * - 0 on success.
575 * - -1 if any of the parameters were invalid.
576 */
578 char const encr_id[AKA_SIM_3GPP_PSEUDONYM_LEN], uint8_t const key[16])
579{
580 EVP_CIPHER_CTX *evp_ctx;
581
582 char *out_p = out;
583
584 uint8_t dec[16];
585 uint8_t *dec_p = dec;
586
587 uint8_t decr[16];
588 uint8_t *compressed = decr + 8; /* Pointer into plaintext after the random component */
589 size_t decr_len;
590
591 char const *p = encr_id, *end = p + AKA_SIM_3GPP_PSEUDONYM_LEN;
592
593 int len = 0;
594 unsigned int i;
595
596 for (i = 0; i < AKA_SIM_3GPP_PSEUDONYM_LEN; i++) {
597 if (!fr_is_base64(encr_id[i])) {
598 fr_strerror_printf("Encrypted IMSI contains non-base64 char '%pV'",
599 fr_box_strvalue_len(&encr_id[i], 1));
600 return -1;
601 }
602 }
603
604 *dec_p++ = (((fr_base64_alphabet_decode[us(p[1])] & 0x03) << 6) | fr_base64_alphabet_decode[us(p[2])]);
605 p += 3;
606
607 while (p < end) {
608 *dec_p++ = ((fr_base64_alphabet_decode[us(p[0])] << 2) | (fr_base64_alphabet_decode[us(p[1])] >> 4));
609 *dec_p++ = ((fr_base64_alphabet_decode[us(p[1])] << 4) & 0xf0) | (fr_base64_alphabet_decode[us(p[2])] >> 2);
610 *dec_p++ = ((fr_base64_alphabet_decode[us(p[2])] << 6) & 0xc0) | fr_base64_alphabet_decode[us(p[3])];
611
612 p += 4; /* 32bit input -> 24bit output */
613 }
614
615 evp_ctx = aka_sim_crypto_cipher_ctx();
616 if (unlikely(EVP_DecryptInit_ex(evp_ctx, EVP_aes_128_ecb(), NULL, key, NULL) != 1)) {
617 fr_tls_strerror_printf("Failed initialising AES-128-ECB context");
618 error:
619 return -1;
620 }
621
622 /*
623 * By default OpenSSL expects 16 bytes of plaintext
624 * to produce 32 bytes of ciphertext, due to padding
625 * being added if the plaintext is a multiple of 16.
626 *
627 * There's no way for OpenSSL to determine if a
628 * 16 byte ciphertext was padded or not, so we need to
629 * inform OpenSSL explicitly that there's no padding.
630 */
631 EVP_CIPHER_CTX_set_padding(evp_ctx, 0);
632 if (unlikely(EVP_DecryptUpdate(evp_ctx, decr, &len, dec, sizeof(dec)) != 1)) {
633 fr_tls_strerror_printf("Failed decypting IMSI");
634 goto error;
635 }
636 decr_len = len;
637
638 if (unlikely(EVP_DecryptFinal_ex(evp_ctx, decr + len, &len) != 1)) {
639 fr_tls_strerror_printf("Failed finalising decypted IMSI");
640 goto error;
641 }
642 decr_len += len;
643
644 /*
645 * This should never happen, and probably means that
646 * some sort of memory corruption has occurred.
647 */
648 if (unlikely(decr_len > (AKA_SIM_IMSI_MAX_LEN + 1))) {
649 fr_strerror_printf("Decrypted data len invalid. Expected %u bytes, got %zu bytes",
650 (AKA_SIM_IMSI_MAX_LEN + 1), decr_len);
651 goto error;
652 }
653
654 /*
655 * Decompress the IMSI
656 *
657 * The first 8 octets are a junk random value which
658 * we ignore.
659 */
660 *out_p++ = (compressed[0] & 0x0f) + '0';
661 for (i = 1; i < 8; i++) {
662 *out_p++ = ((compressed[i] & 0xf0) >> 4) + '0';
663 *out_p++ = (compressed[i] & 0x0f) + '0';
664 }
666
667 return 0;
668}
669
670#ifdef TESTING_SIM_ID
671/*
672 * 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
673 */
674#include <freeradius-devel/util/test/acutest.h>
675
676void test_encrypt_decypt_key0(void)
677{
678 char const id[] = "001234554321001";
679 char const key[] = "1234567812345678";
680 uint8_t tag;
681 uint8_t key_ind;
682 char const *log;
683
684 char encrypted_id[AKA_SIM_3GPP_PSEUDONYM_LEN + 1];
685 char decrypted_id[sizeof(id)];
686
687 TEST_CHECK(fr_aka_sim_id_3gpp_pseudonym_encrypt(encrypted_id, id, sizeof(id) - 1, 6, 0, (uint8_t const *)key) == 0);
688 while ((log = fr_strerror_pop())) printf("%s\n", log);
689
690 tag = fr_aka_sim_id_3gpp_pseudonym_tag(encrypted_id);
691 TEST_CHECK(tag == 6);
692 key_ind = fr_aka_sim_id_3gpp_pseudonym_key_index(encrypted_id);
693 TEST_CHECK(key_ind == 0);
694
695 TEST_CHECK(fr_aka_sim_id_3gpp_pseudonym_decrypt(decrypted_id, encrypted_id, (uint8_t const *)key) == 0);
696 while ((log = fr_strerror_pop())) printf("%s\n", log);
697
698 TEST_CHECK(memcmp(id, decrypted_id, 15) == 0);
699}
700
701void test_encrypt_decypt_key1(void)
702{
703 char const id[] = "001234554321001";
704 char const key[] = "1234567812345678";
705 uint8_t tag;
706 uint8_t key_ind;
707 char const *log;
708
709 char encrypted_id[AKA_SIM_3GPP_PSEUDONYM_LEN + 1];
710 char decrypted_id[sizeof(id)];
711
712 TEST_CHECK(fr_aka_sim_id_3gpp_pseudonym_encrypt(encrypted_id, id, sizeof(id) - 1, 11, 1, (uint8_t const *)key) == 0);
713 while ((log = fr_strerror_pop())) printf("%s\n", log);
714
715 tag = fr_aka_sim_id_3gpp_pseudonym_tag(encrypted_id);
716 TEST_CHECK(tag == 11);
717 key_ind = fr_aka_sim_id_3gpp_pseudonym_key_index(encrypted_id);
718 TEST_CHECK(key_ind == 1);
719
720 TEST_CHECK(fr_aka_sim_id_3gpp_pseudonym_decrypt(decrypted_id, encrypted_id, (uint8_t const *)key) == 0);
721 while ((log = fr_strerror_pop())) printf("%s\n", log);
722
723 TEST_CHECK(memcmp(id, decrypted_id, 15) == 0);
724}
725
726void test_encrypt_decypt_key16(void)
727{
728 char const id[] = "001234554321001";
729 char const key0[] = "1234567812345678";
730 char const key1[] = "2222222288888888";
731 char const key2[] = "2222222299999999";
732 char const key3[] = "2222222200000000";
733 char const key4[] = "2222222211111111";
734 char const key5[] = "2222222222222222";
735 char const key6[] = "2222222233333333";
736 char const key7[] = "2222222244444444";
737 char const key8[] = "2222222255555555";
738 char const key9[] = "2222222266666666";
739 char const key10[] = "2222222277777777";
740 char const key11[] = "1111111188888888";
741 char const key12[] = "2222222288888888";
742 char const key13[] = "3333333388888888";
743 char const key14[] = "4444444488888888";
744 char const key15[] = "5555555588888888";
745 char const *keys[] = { key0, key1, key2, key3, key4, key5,
746 key6, key7, key8, key9, key10, key11,
747 key12, key13, key14, key15 };
748 uint8_t tag;
749 uint8_t key_ind;
750 char const *log;
751
752 char encrypted_id[AKA_SIM_3GPP_PSEUDONYM_LEN + 1];
753 char decrypted_id[sizeof(id)];
754
755 TEST_CHECK(fr_aka_sim_id_3gpp_pseudonym_encrypt(encrypted_id, id, sizeof(id) - 1,
756 9, 15, (uint8_t const *)keys[15]) == 0);
757 while ((log = fr_strerror_pop())) printf("%s\n", log);
758
759 tag = fr_aka_sim_id_3gpp_pseudonym_tag(encrypted_id);
760 TEST_CHECK(tag == 9);
761 key_ind = fr_aka_sim_id_3gpp_pseudonym_key_index(encrypted_id);
762 TEST_CHECK(key_ind == 15);
763
764 TEST_CHECK(fr_aka_sim_id_3gpp_pseudonym_decrypt(decrypted_id, encrypted_id, (uint8_t const *)keys[key_ind]) == 0);
765 while ((log = fr_strerror_pop())) printf("%s\n", log);
766
767 TEST_CHECK(memcmp(id, decrypted_id, 15) == 0);
768}
769
770TEST_LIST = {
771 /*
772 * Initialisation
773 */
774 { L("encrypt_decrypt key0"), test_encrypt_decypt_key0 },
775 { L("encrypt_decrypt key1"), test_encrypt_decypt_key1 },
776 { L("encrypt_decrypt key16"), test_encrypt_decypt_key16 },
777
778 { NULL }
779};
780#endif
#define TEST_CHECK(cond)
Definition acutest.h:101
#define TEST_LIST
Definition acutest.h:75
char const fr_base64_alphabet_encode[SBUFF_CHAR_CLASS]
Definition base64.c:32
uint8_t const fr_base64_alphabet_decode[SBUFF_CHAR_CLASS]
Definition base64.c:99
#define fr_is_base64(_c)
Definition base64.h:64
#define L(_str)
Helper for initialising arrays of string literals.
Definition build.h:245
#define unlikely(_x)
Definition build.h:486
#define NUM_ELEMENTS(_t)
Definition build.h:437
EAP-SIM/EAP-AKA Private crypto functions.
@ 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
enum eap_type eap_type_t
EVP_CIPHER_CTX * aka_sim_crypto_cipher_ctx(void)
Allocate and reset a resumable EVP_CIPHER_CTX for each thread.
Definition crypto.c:69
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:577
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:562
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:551
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
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
@ 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
#define AKA_SIM_3GPP_PSEUDONYM_LEN
Length of a base64 encoded 3gpp pseudonym.
Definition id.h:31
@ 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_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:128
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:581
char const * fr_strerror_pop(void)
Pop the last library error.
Definition strerror.c:685
#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:340
static fr_sbuff_err_t char ** out
Definition value.h:1061
static fr_sbuff_err_t char size_t * len
Definition value.h:1061