blob: 05fbf691c058798e50b2cfa3d0af4dd2e325f24a [file] [log] [blame]
Harald Welte8bd9d5d2023-05-28 09:36:50 +02001/*! \file auth_tuak.c
2 * GSM/GPRS/3G authentication core infrastructure */
3/*
4 * (C) 2023 by Harald Welte <laforge@osmocom.org>
5 *
6 * All Rights Reserved
7 *
8 * SPDX-License-Identifier: GPL-2.0+
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 */
21
22/* NOTE: TUAK offers a lot of size variability in terms of size of length of MAC_A, MAC_S,
23 * but this is not used within 3GPP. The different sizes of Kc and RES are handled via
24 * osmo_sub_auth_data2. */
25
26#include <errno.h>
27#include <osmocom/crypt/auth.h>
28#include <osmocom/core/bits.h>
29#include "tuak/tuak.h"
30
31/*! \addtogroup auth
32 * @{
33 */
34
35static const uint8_t *gen_opc_if_needed(const struct osmo_sub_auth_data2 *aud, uint8_t *gen_opc)
36{
37 int rc;
38
39 /* Check if we only know OP and compute OPC if required */
40 if (aud->type == OSMO_AUTH_TYPE_UMTS && aud->u.umts.opc_is_op) {
41 rc = tuak_opc_gen(gen_opc, aud->u.umts.k, aud->u.umts.k_len, aud->u.umts.opc);
42 if (rc < 0)
43 return NULL;
44 return gen_opc;
45 }
46
47 return aud->u.umts.opc;
48}
49
50static int tuak_gen_vec(struct osmo_auth_vector *vec,
51 struct osmo_sub_auth_data2 *aud,
52 const uint8_t *_rand)
53{
54 size_t res_len = vec->res_len;
55 uint64_t next_sqn;
56 uint8_t gen_opc[32];
57 const uint8_t *opc;
58 uint8_t sqn[6];
59 uint64_t ind_mask;
60 uint64_t seq_1;
61
62 switch (vec->res_len) {
63 case 4:
64 case 8:
65 case 16:
66 break;
67 default:
68 return -EINVAL;
69 }
70
71 OSMO_ASSERT(aud->algo == OSMO_AUTH_ALG_TUAK);
72
73 if (aud->u.umts.k_len != 16 && aud->u.umts.k_len != 32)
74 return -EINVAL;
75 if (aud->u.umts.opc_len != 32)
76 return -EINVAL;
77
78 opc = gen_opc_if_needed(aud, gen_opc);
79 if (!opc)
80 return -1;
81
82 /* Determine next SQN, according to 3GPP TS 33.102:
83 * SQN consists of SEQ and a lower significant part of IND bits:
84 *
85 * |----------SEQ------------|
86 * |------------------------SQN-----------|
87 * |-----IND----|
88 *
89 * The IND part is used as "slots": e.g. a given HLR client will always
90 * get the same IND part, called ind here, with incrementing SEQ. In
91 * the USIM, each IND slot enforces that its SEQ are used in ascending
92 * order -- as long as that constraint is satisfied, the SQN may jump
93 * forwards and backwards. For example, for ind_bitlen == 5, asking the
94 * USIM for SQN = 32, 64, 33 is allowed, because 32 and 64 are
95 * SEQ || (ind == 0), and though 33 is below 64, it is ind == 1 and
96 * allowed. Not allowed would be 32, 96, 64, because 64 would go
97 * backwards after 96, both being ind == 0.
98 *
99 * From the last used SQN, we want to increment SEQ + 1, and then pick
100 * the matching IND part.
101 *
102 * IND size is suggested in TS 33.102 as 5 bits. SQN is 48 bits long.
103 * If ind_bitlen is passed too large here, the algorithms will break
104 * down. But at which point should we return an error? A sane limit
105 * seems to be ind_bitlen == 10, but to protect against failure,
106 * limiting ind_bitlen to 28 is enough, 28 being the number of bits
107 * suggested for the delta in 33.102, which is discussed to still
108 * require 2^15 > 32000 authentications to wrap the SQN back to the
109 * start.
110 *
111 * Note that if a caller with ind == 1 generates N vectors, the SQN
112 * stored after this will reflect SEQ + N. If then another caller with
113 * ind == 2 generates another N vectors, this will then use SEQ + N
114 * onwards and end up with SEQ + N + N. In other words, most of each
115 * SEQ's IND slots will remain unused. When looking at SQN being 48
116 * bits wide, after dropping ind_bitlen (say 5) from it, we will still
117 * have a sequence range of 2^43 = 8.8e12, eight trillion sequences,
118 * which is large enough to not bother further. With the maximum
119 * ind_bitlen of 28 enforced below, we still get more than 1 million
120 * sequences, which is also sufficiently large.
121 *
122 * An ind_bitlen of zero may be passed from legacy callers that are not
123 * aware of the IND extension. For these, below algorithm works out as
124 * before, simply incrementing SQN by 1.
125 *
126 * This is also a mechanism for tools like the osmo-auc-gen to directly
127 * request a given SQN to be used. With ind_bitlen == 0 the caller can
128 * be sure that this code will increment SQN by exactly one before
129 * generating a tuple, thus a caller would simply pass
130 * { .ind_bitlen = 0, .ind = 0, .sqn = (desired_sqn - 1) }
131 */
132
133 if (aud->u.umts.ind_bitlen > OSMO_MILENAGE_IND_BITLEN_MAX)
134 return -2;
135
136 seq_1 = 1LL << aud->u.umts.ind_bitlen;
137 ind_mask = ~(seq_1 - 1);
138
139 /* the ind index must not affect the SEQ part */
140 if (aud->u.umts.ind >= seq_1)
141 return -3;
142
143 /* keep the incremented SQN local until gsm_milenage() succeeded. */
144 next_sqn = ((aud->u.umts.sqn + seq_1) & ind_mask) + aud->u.umts.ind;
145
146 osmo_store64be_ext(next_sqn, sqn, 6);
147
148 tuak_generate(opc, aud->u.umts.amf, aud->u.umts.k, aud->u.umts.k_len,
149 sqn, _rand, vec->autn, vec->ik, vec->ck, vec->res, &res_len);
150
151 /* generate the GSM Kc + SRES values using C2 + C3 functions */
152 osmo_auth_c3(vec->kc, vec->ck, vec->ik);
153 osmo_auth_c2(vec->sres, vec->res, vec->res_len, 1);
154
155 vec->auth_types = OSMO_AUTH_TYPE_UMTS | OSMO_AUTH_TYPE_GSM;
156
157 /* for storage in the caller's AUC database */
158 aud->u.umts.sqn = next_sqn;
159
160 return 0;
161}
162
163static int tuak_gen_vec_auts(struct osmo_auth_vector *vec,
164 struct osmo_sub_auth_data2 *aud,
165 const uint8_t *auts, const uint8_t *rand_auts,
166 const uint8_t *_rand)
167{
168 uint8_t sqn_out[6];
169 uint8_t gen_opc[32];
170 const uint8_t *opc;
171 int rc;
172
173 OSMO_ASSERT(aud->algo == OSMO_AUTH_ALG_TUAK);
174
175 if (aud->u.umts.k_len != 16 && aud->u.umts.k_len != 32)
176 return -EINVAL;
177 if (aud->u.umts.opc_len != 32)
178 return -EINVAL;
179
180 opc = gen_opc_if_needed(aud, gen_opc);
181
182 rc = tuak_auts(opc, aud->u.umts.k, sizeof(aud->u.umts.k), rand_auts, auts, sqn_out);
183 if (rc < 0)
184 return rc;
185
186 aud->u.umts.sqn_ms = osmo_load64be_ext(sqn_out, 6) >> 16;
187 /* Update our "largest used SQN" from the USIM -- milenage_gen_vec()
188 * below will increment SQN. */
189 aud->u.umts.sqn = aud->u.umts.sqn_ms;
190
191 return tuak_gen_vec(vec, aud, _rand);
192}
193
194static struct osmo_auth_impl tuak_alg = {
195 .algo = OSMO_AUTH_ALG_TUAK,
196 .name = "TUAK (libosmogsm built-in)",
197 .priority = 1000,
198 .gen_vec = &tuak_gen_vec,
199 .gen_vec_auts = &tuak_gen_vec_auts,
200};
201
202static __attribute__((constructor)) void on_dso_load_tuak(void)
203{
204 osmo_auth_register(&tuak_alg);
205}
206
207/*! @} */