The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
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 
44  { L("AKA"), AKA_SIM_METHOD_HINT_AKA },
45  { L("AKA'"), AKA_SIM_METHOD_HINT_AKA_PRIME },
46  { L("SIM"), AKA_SIM_METHOD_HINT_SIM },
47 };
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  */
57 size_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  */
75 char 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  */
167 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)
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 
238 bad_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  */
261  case ID_TAG_SIM_FASTAUTH:
262  if (hint) *hint = AKA_SIM_METHOD_HINT_SIM;
264  return 0;
265 
266  case ID_TAG_AKA_FASTAUTH:
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  */
306 eap_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)");
316  return FR_EAP_METHOD_INVALID;
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 
340  return FR_EAP_METHOD_INVALID;
341 }
342 
348  [AKA_SIM_ID_TYPE_UNKNOWN] = '\0',
349  },
354  [AKA_SIM_ID_TYPE_UNKNOWN] = '\0',
355  },
360  [AKA_SIM_ID_TYPE_UNKNOWN] = '\0',
361  },
363  '\0' /* Should set for all elements */
364  }
365 };
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  */
376 {
377  return hint_byte_matrix[method][type];
378 }
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 %i bytes, got %zu bytes",
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  */
551 {
552  return fr_base64_alphabet_decode[us(encr_id[0])];
553 }
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 %i 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  }
664  out[AKA_SIM_IMSI_MAX_LEN] = '\0';
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 
677 void 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 
702 void 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 
727 void 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 
771 TEST_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:207
#define unlikely(_x)
Definition: build.h:378
#define NUM_ELEMENTS(_t)
Definition: build.h:335
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
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
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
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
Definition: merged_model.c:31
unsigned int uint32_t
Definition: merged_model.c:33
long int ssize_t
Definition: merged_model.c:24
unsigned char uint8_t
Definition: merged_model.c:30
RADIUS bio handlers for tracking 8-bit IDs.
uint32_t fr_rand(void)
Return a 32-bit random number.
Definition: rand.c:106
fr_aka_sim_id_type_t type
An element in a lexicographically sorted array of name to num mappings.
Definition: table.h:45
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:279
static size_t char ** out
Definition: value.h:984