blob: a506a03dfab1c669e20fa053c5471dbc04d55561 [file] [log] [blame]
Daniel Willmanna981f9d2018-03-16 01:59:42 +07001/*! \file auth_xor.c
2 * GSM/GPRS/3G authentication core infrastructure */
3/*
4 * (C) 2018 by Harald Welte <laforge@gnumonks.org>
5 * (C) 2017 by sysmocom s.f.m.c. GmbH
6 *
7 * All Rights Reserved
8 *
9 * Author: Daniel Willmann <dwillmann@sysmocom.de>
10 *
11 * All Rights Reserved
12 *
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2 of the License, or
16 * (at your option) any later version.
17 *
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
22 *
Daniel Willmanna981f9d2018-03-16 01:59:42 +070023 */
24
25#include <string.h>
26#include <stdint.h>
27#include <errno.h>
28
29#include <osmocom/core/bit64gen.h>
30#include <osmocom/crypt/auth.h>
31
32/*! \addtogroup auth
33 * @{
34 */
35
36static void xor(uint8_t *out, const uint8_t *a, const uint8_t *b, size_t len)
37{
38 size_t i;
39
40 for (i = 0; i < len; i++)
41 out[i] = a[i] ^ b[i];
42}
43
44/* 3GPP TS 34.108, section 8.1.2.1 */
45static int xor_gen_vec(struct osmo_auth_vector *vec,
Harald Welte08450c92023-05-30 10:55:37 +020046 struct osmo_sub_auth_data2 *aud,
Daniel Willmanna981f9d2018-03-16 01:59:42 +070047 const uint8_t *_rand)
48{
49 uint8_t xdout[16], cdout[8];
50 uint8_t ak[6], xmac[8];
51 int i;
52
Harald Welte5248c472023-05-30 11:09:17 +020053 OSMO_ASSERT(aud->algo == OSMO_AUTH_ALG_XOR_3G);
54
Daniel Willmanna981f9d2018-03-16 01:59:42 +070055 /* Step 1: xdout = (ki or k) ^ rand */
56 if (aud->type == OSMO_AUTH_TYPE_GSM)
57 xor(xdout, aud->u.gsm.ki, _rand, sizeof(xdout));
Harald Weltea9c91cc2023-05-30 11:58:28 +020058 else if (aud->type == OSMO_AUTH_TYPE_UMTS) {
59 if (aud->u.umts.k_len != 16)
60 return -EINVAL;
Daniel Willmanna981f9d2018-03-16 01:59:42 +070061 xor(xdout, aud->u.umts.k, _rand, sizeof(xdout));
Harald Weltea9c91cc2023-05-30 11:58:28 +020062 } else
Daniel Willmanna981f9d2018-03-16 01:59:42 +070063 return -ENOTSUP;
64
65 /**
66 * Step 2: res = xdout
67 *
68 * Suggested length for res is 128 bits, i.e. 16 bytes,
69 * but also can be in range: 30 < n < 128 bits.
70 */
71 memcpy(vec->res, xdout, sizeof(xdout));
72 vec->res_len = sizeof(xdout);
73
74 /* ck = xdout[1-15,0] */
75 memcpy(vec->ck, xdout + 1, sizeof(xdout) - 1);
76 vec->ck[15] = xdout[0];
77
78 /* ik = xdout[2-15,0-1] */
79 memcpy(vec->ik, xdout + 2, sizeof(xdout) - 2);
80 memcpy(vec->ik + sizeof(xdout) - 2, xdout, 2);
81
82 /* ak = xdout[3-8] */
83 memcpy(ak, xdout + 3, sizeof(ak));
84
85 /**
86 * 3GPP TS 33.102, clause 6.8.1.2, b
87 * sres = c2(res) = res[0-3] ^ res[4-7] ^ res[8-11] ^ res[12-15]
88 */
89 for (i = 0; i < 4; i++) {
90 vec->sres[i] = vec->res[i] ^ vec->res[i + 4];
91 vec->sres[i] ^= vec->res[i + 8] ^ vec->res[i + 12];
92 }
93
94 /**
95 * 3GPP TS 33.102, clause 6.8.1.2, c
96 * kc = c3(ck, ik) = ck[0-7] ^ ck[8-15] ^ ik[0-7] ^ ik[8-15]
97 * FIXME: do we really have CK/IK for GSM?
98 */
99 osmo_auth_c3(vec->kc, vec->ck, vec->ik);
100
101 /* The further part is UMTS specific */
102 if (aud->type != OSMO_AUTH_TYPE_UMTS) {
103 vec->auth_types = OSMO_AUTH_TYPE_GSM;
104 return 0;
105 }
106
107 /**
108 * Step 3: cdout = sqn[0-5] || amf[0-1]
109 * NOTE (for USIM): sqn[0-5] = autn[0-5] ^ ak[0-5]
110 */
111 osmo_store64be_ext(aud->u.umts.sqn, cdout, 6);
112 memcpy(cdout + 6, aud->u.umts.amf, 2);
113
114 /* Step 4: xmac = xdout[0-8] ^ cdout[0-8] */
115 xor(xmac, xdout, cdout, sizeof(xmac));
116
117 /**
118 * Step 5: autn = sqn ^ ak || amf || mac
119 * NOTE: cdout still contains SQN from step 3
120 */
121 xor(vec->autn, cdout, ak, sizeof(ak));
122 memcpy(vec->autn + 6, aud->u.umts.amf, 2);
123 memcpy(vec->autn + 8, xmac, sizeof(xmac));
124
125 vec->auth_types = OSMO_AUTH_TYPE_UMTS | OSMO_AUTH_TYPE_GSM;
126
127 return 0;
128}
129
130/* 3GPP TS 34.108, section 8.1.2.2 */
131static int xor_gen_vec_auts(struct osmo_auth_vector *vec,
Harald Welte08450c92023-05-30 10:55:37 +0200132 struct osmo_sub_auth_data2 *aud,
Daniel Willmanna981f9d2018-03-16 01:59:42 +0700133 const uint8_t *auts,
134 const uint8_t *rand_auts,
135 const uint8_t *_rand)
136{
137 uint8_t xdout[16], cdout[8];
138 uint8_t ak[6], xmac[8];
139 uint8_t sqnms[6];
140
Harald Welte5248c472023-05-30 11:09:17 +0200141 OSMO_ASSERT(aud->algo == OSMO_AUTH_ALG_XOR_3G);
142
Daniel Willmanna981f9d2018-03-16 01:59:42 +0700143 /* Step 1: xdout = (ki or k) ^ rand */
144 if (aud->type == OSMO_AUTH_TYPE_GSM)
145 xor(xdout, aud->u.gsm.ki, _rand, sizeof(xdout));
Harald Weltea9c91cc2023-05-30 11:58:28 +0200146 else if (aud->type == OSMO_AUTH_TYPE_UMTS) {
147 if (aud->u.umts.k_len != 16)
148 return -EINVAL;
Daniel Willmanna981f9d2018-03-16 01:59:42 +0700149 xor(xdout, aud->u.umts.k, _rand, sizeof(xdout));
Harald Weltea9c91cc2023-05-30 11:58:28 +0200150 } else
Daniel Willmanna981f9d2018-03-16 01:59:42 +0700151 return -ENOTSUP;
152
153 /* Step 2: ak = xdout[2-8] */
154 memcpy(ak, xdout + 3, 6);
155
156 /* sqnms = auts[0-5] ^ ak[0-5] */
157 xor(sqnms, auts, ak, sizeof(ak));
158
159 /* cdout = sqnms || amf* (dummy) */
160 memcpy(cdout, sqnms, 6);
161 memset(cdout + 6, 0x00, 2);
162
163 /* xmac = xdout[0-7] ^ cdout[0-7] */
164 xor(xmac, xdout, cdout, 8);
165
166 /* Compare the last 64 bits of received AUTS with the locally-generated MAC-S */
167 if (memcmp(auts + 6, xmac, 8))
168 return -1;
169
170 /* Update the "largest used SQN" from the USIM,
171 * milenage_gen_vec() will increment it. */
172 aud->u.umts.sqn_ms = osmo_load64be_ext(sqnms, 6) >> 16;
173 aud->u.umts.sqn = aud->u.umts.sqn_ms;
174
175 return xor_gen_vec(vec, aud, _rand);
176}
177
178static struct osmo_auth_impl xor_alg = {
Harald Welte9b7c9ae2023-02-21 22:24:15 +0100179 .algo = OSMO_AUTH_ALG_XOR_3G,
180 .name = "XOR-3G (libosmogsm built-in)",
Daniel Willmanna981f9d2018-03-16 01:59:42 +0700181 .priority = 1000,
182 .gen_vec = &xor_gen_vec,
183 .gen_vec_auts = &xor_gen_vec_auts,
184};
185
186static __attribute__((constructor)) void on_dso_load_xor(void)
187{
188 osmo_auth_register(&xor_alg);
189}
190
191/*! @} */