| /* (C) 2016 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> |
| * All Rights Reserved |
| * |
| * Author: Neels Hofmeyr <nhofmeyr@sysmocom.de> |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU Affero General Public License as published by |
| * the Free Software Foundation; either version 3 of the License, or |
| * (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU Affero General Public License |
| * along with this program. If not, see <http://www.gnu.org/licenses/>. |
| * |
| */ |
| |
| #include <stdio.h> |
| #include <string.h> |
| #include <inttypes.h> |
| |
| #include <osmocom/core/application.h> |
| #include <osmocom/core/utils.h> |
| #include <osmocom/core/logging.h> |
| |
| #include <osmocom/crypt/auth.h> |
| |
| #include "logging.h" |
| #include "auc.h" |
| |
| #define comment_start() fprintf(stderr, "\n===== %s\n", __func__); |
| #define comment_end() fprintf(stderr, "===== %s: SUCCESS\n\n", __func__); |
| |
| #define VERBOSE_ASSERT(val, expect_op, fmt) \ |
| do { \ |
| fprintf(stderr, #val " == " fmt "\n", (val)); \ |
| OSMO_ASSERT((val) expect_op); \ |
| } while (0); |
| |
| char *vec_str(const struct osmo_auth_vector *vec) |
| { |
| static char buf[1024]; |
| char *pos = buf; |
| char *end = buf + sizeof(buf); |
| |
| #define append(what) \ |
| if (pos >= end) \ |
| return buf; \ |
| pos += snprintf(pos, sizeof(buf) - (pos - buf), \ |
| " " #what ": %s\n", \ |
| osmo_hexdump_nospc((void*)&vec->what, sizeof(vec->what))) |
| |
| append(rand); |
| append(autn); |
| append(ck); |
| append(ik); |
| append(res); |
| append(res_len); |
| append(kc); |
| append(sres); |
| append(auth_types); |
| #undef append |
| |
| return buf; |
| } |
| |
| #define VEC_IS(vec, expect) do { \ |
| char *_is = vec_str(vec); \ |
| fprintf(stderr, "auth vector ==\n%s\n", _is); \ |
| if (strcmp(_is, expect)) { \ |
| fprintf(stderr, "MISMATCH! expected ==\n%s\n", \ |
| expect); \ |
| char *a = _is; \ |
| char *b = expect; \ |
| for (; *a && *b; a++, b++) { \ |
| if (*a != *b) { \ |
| while (a > _is && *(a-1) != '\n') a--; \ |
| fprintf(stderr, "mismatch at %d:\n" \ |
| "%s", (int)(a - _is), a); \ |
| break; \ |
| } \ |
| } \ |
| OSMO_ASSERT(false); \ |
| } \ |
| } while (0) |
| |
| uint8_t fake_rand[16] = { 0 }; |
| |
| int rand_get(uint8_t *rand, unsigned int len) |
| { |
| OSMO_ASSERT(len <= sizeof(fake_rand)); |
| memcpy(rand, fake_rand, len); |
| return len; |
| } |
| |
| static void test_gen_vectors_2g_only(void) |
| { |
| struct osmo_sub_auth_data aud2g; |
| struct osmo_sub_auth_data aud3g; |
| struct osmo_auth_vector vec; |
| int rc; |
| |
| comment_start(); |
| |
| aud2g = (struct osmo_sub_auth_data){ |
| .type = OSMO_AUTH_TYPE_GSM, |
| .algo = OSMO_AUTH_ALG_COMP128v1, |
| }; |
| |
| osmo_hexparse("EB215756028D60E3275E613320AEC880", |
| aud2g.u.gsm.ki, sizeof(aud2g.u.gsm.ki)); |
| |
| aud3g = (struct osmo_sub_auth_data){ 0 }; |
| |
| osmo_hexparse("39fa2f4e3d523d8619a73b4f65c3e14d", |
| fake_rand, sizeof(fake_rand)); |
| |
| vec = (struct osmo_auth_vector){ {0} }; |
| VERBOSE_ASSERT(aud3g.u.umts.sqn, == 0, "%"PRIu64); |
| rc = auc_compute_vectors(&vec, 1, &aud2g, &aud3g, NULL, NULL); |
| VERBOSE_ASSERT(rc, == 1, "%d"); |
| |
| VEC_IS(&vec, |
| " rand: 39fa2f4e3d523d8619a73b4f65c3e14d\n" |
| " autn: 00000000000000000000000000000000\n" |
| " ck: 00000000000000000000000000000000\n" |
| " ik: 00000000000000000000000000000000\n" |
| " res: 00000000000000000000000000000000\n" |
| " res_len: 00\n" |
| " kc: 241a5b16aeb8e400\n" |
| " sres: 429d5b27\n" |
| " auth_types: 01000000\n" |
| ); |
| |
| VERBOSE_ASSERT(aud3g.u.umts.sqn, == 0, "%"PRIu64); |
| |
| /* even though vec is not zero-initialized, it should produce the same |
| * result (regardless of the umts sequence nr) */ |
| aud3g.u.umts.sqn = 123; |
| rc = auc_compute_vectors(&vec, 1, &aud2g, &aud3g, NULL, NULL); |
| VERBOSE_ASSERT(rc, == 1, "%d"); |
| |
| VEC_IS(&vec, |
| " rand: 39fa2f4e3d523d8619a73b4f65c3e14d\n" |
| " autn: 00000000000000000000000000000000\n" |
| " ck: 00000000000000000000000000000000\n" |
| " ik: 00000000000000000000000000000000\n" |
| " res: 00000000000000000000000000000000\n" |
| " res_len: 00\n" |
| " kc: 241a5b16aeb8e400\n" |
| " sres: 429d5b27\n" |
| " auth_types: 01000000\n" |
| ); |
| |
| comment_end(); |
| } |
| |
| static void test_gen_vectors_2g_plus_3g(void) |
| { |
| struct osmo_sub_auth_data aud2g; |
| struct osmo_sub_auth_data aud3g; |
| struct osmo_auth_vector vec; |
| int rc; |
| |
| comment_start(); |
| |
| aud2g = (struct osmo_sub_auth_data){ |
| .type = OSMO_AUTH_TYPE_GSM, |
| .algo = OSMO_AUTH_ALG_COMP128v1, |
| }; |
| |
| osmo_hexparse("EB215756028D60E3275E613320AEC880", |
| aud2g.u.gsm.ki, sizeof(aud2g.u.gsm.ki)); |
| |
| aud3g = (struct osmo_sub_auth_data){ |
| .type = OSMO_AUTH_TYPE_UMTS, |
| .algo = OSMO_AUTH_ALG_MILENAGE, |
| }; |
| |
| osmo_hexparse("EB215756028D60E3275E613320AEC880", |
| aud3g.u.umts.k, sizeof(aud3g.u.umts.k)); |
| osmo_hexparse("FB2A3D1B360F599ABAB99DB8669F8308", |
| aud3g.u.umts.opc, sizeof(aud3g.u.umts.opc)); |
| |
| osmo_hexparse("39fa2f4e3d523d8619a73b4f65c3e14d", |
| fake_rand, sizeof(fake_rand)); |
| |
| vec = (struct osmo_auth_vector){ {0} }; |
| VERBOSE_ASSERT(aud3g.u.umts.sqn, == 0, "%"PRIu64); |
| rc = auc_compute_vectors(&vec, 1, &aud2g, &aud3g, NULL, NULL); |
| VERBOSE_ASSERT(rc, == 1, "%d"); |
| |
| VEC_IS(&vec, |
| " rand: 39fa2f4e3d523d8619a73b4f65c3e14d\n" |
| " autn: 8704f5ba55f30000d2ee44b22c8ea919\n" |
| " ck: f64735036e5871319c679f4742a75ea1\n" |
| " ik: 27497388b6cb044648f396aa155b95ef\n" |
| " res: e229c19e791f2e410000000000000000\n" |
| " res_len: 08\n" |
| " kc: 241a5b16aeb8e400\n" |
| " sres: 429d5b27\n" |
| " auth_types: 03000000\n" |
| ); |
| |
| VERBOSE_ASSERT(aud3g.u.umts.sqn, == 1, "%"PRIu64); |
| |
| /* even though vec is not zero-initialized, it should produce the same |
| * result with the same sequence nr */ |
| aud3g.u.umts.sqn = 0; |
| VERBOSE_ASSERT(aud3g.u.umts.sqn, == 0, "%"PRIu64); |
| rc = auc_compute_vectors(&vec, 1, &aud2g, &aud3g, NULL, NULL); |
| VERBOSE_ASSERT(rc, == 1, "%d"); |
| VERBOSE_ASSERT(aud3g.u.umts.sqn, == 1, "%"PRIu64); |
| |
| VEC_IS(&vec, |
| " rand: 39fa2f4e3d523d8619a73b4f65c3e14d\n" |
| " autn: 8704f5ba55f30000d2ee44b22c8ea919\n" |
| " ck: f64735036e5871319c679f4742a75ea1\n" |
| " ik: 27497388b6cb044648f396aa155b95ef\n" |
| " res: e229c19e791f2e410000000000000000\n" |
| " res_len: 08\n" |
| " kc: 241a5b16aeb8e400\n" |
| " sres: 429d5b27\n" |
| " auth_types: 03000000\n" |
| ); |
| |
| comment_end(); |
| } |
| |
| static void test_gen_vectors_3g_only(void) |
| { |
| struct osmo_sub_auth_data aud2g; |
| struct osmo_sub_auth_data aud3g; |
| struct osmo_auth_vector vec; |
| uint8_t auts[14]; |
| uint8_t rand_auts[16]; |
| int rc; |
| |
| comment_start(); |
| |
| aud2g = (struct osmo_sub_auth_data){ 0 }; |
| |
| aud3g = (struct osmo_sub_auth_data){ |
| .type = OSMO_AUTH_TYPE_UMTS, |
| .algo = OSMO_AUTH_ALG_MILENAGE, |
| }; |
| |
| osmo_hexparse("EB215756028D60E3275E613320AEC880", |
| aud3g.u.umts.k, sizeof(aud3g.u.umts.k)); |
| osmo_hexparse("FB2A3D1B360F599ABAB99DB8669F8308", |
| aud3g.u.umts.opc, sizeof(aud3g.u.umts.opc)); |
| |
| osmo_hexparse("39fa2f4e3d523d8619a73b4f65c3e14d", |
| fake_rand, sizeof(fake_rand)); |
| |
| vec = (struct osmo_auth_vector){ {0} }; |
| VERBOSE_ASSERT(aud3g.u.umts.sqn, == 0, "%"PRIu64); |
| rc = auc_compute_vectors(&vec, 1, &aud2g, &aud3g, NULL, NULL); |
| VERBOSE_ASSERT(rc, == 1, "%d"); |
| |
| VEC_IS(&vec, |
| " rand: 39fa2f4e3d523d8619a73b4f65c3e14d\n" |
| " autn: 8704f5ba55f30000d2ee44b22c8ea919\n" |
| " ck: f64735036e5871319c679f4742a75ea1\n" |
| " ik: 27497388b6cb044648f396aa155b95ef\n" |
| " res: e229c19e791f2e410000000000000000\n" |
| " res_len: 08\n" |
| " kc: 059a4f668f6fbe39\n" |
| " sres: 9b36efdf\n" |
| " auth_types: 03000000\n" |
| ); |
| |
| /* Note: 3GPP TS 33.102 6.8.1.2: c3 function to get GSM auth is |
| * KC[0..7] == CK[0..7] ^ CK[8..15] ^ IK[0..7] ^ IK[8..15] |
| * In [16]: hex( 0xf64735036e587131 |
| * ^ 0x9c679f4742a75ea1 |
| * ^ 0x27497388b6cb0446 |
| * ^ 0x48f396aa155b95ef) |
| * Out[16]: '0x59a4f668f6fbe39L' |
| * hence expecting kc: 059a4f668f6fbe39 |
| */ |
| |
| VERBOSE_ASSERT(aud3g.u.umts.sqn, == 1, "%"PRIu64); |
| |
| /* even though vec is not zero-initialized, it should produce the same |
| * result with the same sequence nr */ |
| aud3g.u.umts.sqn = 0; |
| VERBOSE_ASSERT(aud3g.u.umts.sqn, == 0, "%"PRIu64); |
| rc = auc_compute_vectors(&vec, 1, &aud2g, &aud3g, NULL, NULL); |
| VERBOSE_ASSERT(rc, == 1, "%d"); |
| VERBOSE_ASSERT(aud3g.u.umts.sqn, == 1, "%"PRIu64); |
| |
| VEC_IS(&vec, |
| " rand: 39fa2f4e3d523d8619a73b4f65c3e14d\n" |
| " autn: 8704f5ba55f30000d2ee44b22c8ea919\n" |
| " ck: f64735036e5871319c679f4742a75ea1\n" |
| " ik: 27497388b6cb044648f396aa155b95ef\n" |
| " res: e229c19e791f2e410000000000000000\n" |
| " res_len: 08\n" |
| " kc: 059a4f668f6fbe39\n" |
| " sres: 9b36efdf\n" |
| " auth_types: 03000000\n" |
| ); |
| |
| |
| fprintf(stderr, "- test AUTS resync\n"); |
| vec = (struct osmo_auth_vector){}; |
| aud3g.u.umts.sqn = 0; |
| VERBOSE_ASSERT(aud3g.u.umts.sqn, == 0, "%"PRIu64); |
| |
| /* The AUTN sent was 8704f5ba55f30000d2ee44b22c8ea919 |
| * with the first 6 bytes being SQN ^ AK. |
| * K = EB215756028D60E3275E613320AEC880 |
| * OPC = FB2A3D1B360F599ABAB99DB8669F8308 |
| * RAND = 39fa2f4e3d523d8619a73b4f65c3e14d |
| * --milenage-f5--> |
| * AK = 8704f5ba55f3 |
| * |
| * The first six bytes are 8704f5ba55f3, |
| * and 8704f5ba55f3 ^ AK = 0. |
| * --> SQN = 0. |
| * |
| * Say the USIM doesn't like that, let's say it is at SQN 23. |
| * SQN_MS = 000000000017 |
| * |
| * AUTS = Conc(SQN_MS) || MAC-S |
| * Conc(SQN_MS) = SQN_MS ⊕ f5*[K](RAND) |
| * MAC-S = f1*[K] (SQN MS || RAND || AMF) |
| * |
| * f5*--> Conc(SQN_MS) = 000000000017 ^ 979498b1f73a |
| * = 979498b1f72d |
| * AMF = 0000 (TS 33.102 v7.0.0, 6.3.3) |
| * |
| * MAC-S = f1*[K] (000000000017 || 39fa2f4e3d523d8619a73b4f65c3e14d || 0000) |
| * = 3e28c59fa2e72f9c |
| * |
| * AUTS = 979498b1f72d || 3e28c59fa2e72f9c |
| * |
| * verify valid AUTS resulting in SQN 23 with: |
| * osmo-auc-gen -3 -a milenage -k EB215756028D60E3275E613320AEC880 \ |
| * -o FB2A3D1B360F599ABAB99DB8669F8308 \ |
| * -r 39fa2f4e3d523d8619a73b4f65c3e14d \ |
| * -A 979498b1f72d3e28c59fa2e72f9c |
| */ |
| |
| /* AUTS response by USIM */ |
| osmo_hexparse("979498b1f72d3e28c59fa2e72f9c", |
| auts, sizeof(auts)); |
| /* RAND sent to USIM, which AUTS was generated from */ |
| osmo_hexparse("39fa2f4e3d523d8619a73b4f65c3e14d", |
| rand_auts, sizeof(rand_auts)); |
| /* new RAND token for the next key */ |
| osmo_hexparse("897210a0f7de278f0b8213098e098a3f", |
| fake_rand, sizeof(fake_rand)); |
| rc = auc_compute_vectors(&vec, 1, &aud2g, &aud3g, rand_auts, auts); |
| VERBOSE_ASSERT(rc, == 1, "%d"); |
| /* The USIM's last sqn was 23, the calculated vector was 24, hence the |
| * next one stored should be 25. */ |
| VERBOSE_ASSERT(aud3g.u.umts.sqn, == 25, "%"PRIu64); |
| |
| VEC_IS(&vec, |
| " rand: 897210a0f7de278f0b8213098e098a3f\n" |
| " autn: c6b9790dad4b00000cf322869ea6a481\n" |
| " ck: e9922bd036718ed9e40bd1d02c3b81a5\n" |
| " ik: f19c20ca863137f8892326d959ec5e01\n" |
| " res: 9af5a557902d2db80000000000000000\n" |
| " res_len: 08\n" |
| " kc: 7526fc13c5976685\n" |
| " sres: 0ad888ef\n" |
| " auth_types: 03000000\n" |
| ); |
| |
| comment_end(); |
| } |
| |
| int main() |
| { |
| printf("auc_3g_test.c\n"); |
| osmo_init_logging(&hlr_log_info); |
| log_set_print_filename(osmo_stderr_target, 0); |
| log_set_print_timestamp(osmo_stderr_target, 0); |
| log_set_use_color(osmo_stderr_target, 0); |
| log_set_print_category(osmo_stderr_target, 1); |
| |
| test_gen_vectors_2g_only(); |
| test_gen_vectors_2g_plus_3g(); |
| test_gen_vectors_3g_only(); |
| |
| printf("Done\n"); |
| return 0; |
| } |