blob: f151c5e880f626f64d509320bd7cacaffa6b9992 [file] [log] [blame]
Harald Welte781bd5d2011-12-06 22:23:52 +01001/* GSM/GPRS/3G authentication core infrastructure */
2
3/* (C) 2011 by Harald Welte <laforge@gnumonks.org>
4 *
5 * All Rights Reserved
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 */
22
23#include <osmocom/crypt/auth.h>
Maxaead05d2016-04-21 15:57:52 +020024#include <osmocom/core/bits.h>
Harald Welte781bd5d2011-12-06 22:23:52 +010025#include "milenage/common.h"
26#include "milenage/milenage.h"
27
Harald Welte781bd5d2011-12-06 22:23:52 +010028static int milenage_gen_vec(struct osmo_auth_vector *vec,
29 struct osmo_sub_auth_data *aud,
30 const uint8_t *_rand)
31{
32 size_t res_len = sizeof(vec->res);
Neels Hofmeyr82c9a0e2017-03-13 17:36:17 +010033 uint64_t next_sqn;
Harald Welte781bd5d2011-12-06 22:23:52 +010034 uint8_t sqn[6];
Neels Hofmeyrbb6f7b72017-03-13 17:27:17 +010035 uint64_t ind_mask;
36 uint64_t seq_1;
Harald Welte781bd5d2011-12-06 22:23:52 +010037 int rc;
38
Neels Hofmeyrbb6f7b72017-03-13 17:27:17 +010039 /* Determine next SQN, according to 3GPP TS 33.102:
40 * SQN consists of SEQ and a lower significant part of IND bits:
41 *
42 * |----------SEQ------------|
43 * |------------------------SQN-----------|
44 * |-----IND----|
45 *
46 * The IND part is used as "slots": e.g. a given HLR client will always
47 * get the same IND part, called ind here, with incrementing SEQ. In
48 * the USIM, each IND slot enforces that its SEQ are used in ascending
49 * order -- as long as that constraint is satisfied, the SQN may jump
50 * forwards and backwards. For example, for ind_bitlen == 5, asking the
51 * USIM for SQN = 32, 64, 33 is allowed, because 32 and 64 are
52 * SEQ || (ind == 0), and though 33 is below 64, it is ind == 1 and
53 * allowed. Not allowed would be 32, 96, 64, because 64 would go
54 * backwards after 96, both being ind == 0.
55 *
56 * From the last used SQN, we want to increment SEQ + 1, and then pick
57 * the matching IND part.
58 *
59 * IND size is suggested in TS 33.102 as 5 bits. SQN is 48 bits long.
60 * If ind_bitlen is passed too large here, the algorithms will break
61 * down. But at which point should we return an error? A sane limit
62 * seems to be ind_bitlen == 10, but to protect against failure,
63 * limiting ind_bitlen to 28 is enough, 28 being the number of bits
64 * suggested for the delta in 33.102, which is discussed to still
65 * require 2^15 > 32000 authentications to wrap the SQN back to the
66 * start.
67 *
68 * Note that if a caller with ind == 1 generates N vectors, the SQN
69 * stored after this will reflect SEQ + N. If then another caller with
70 * ind == 2 generates another N vectors, this will then use SEQ + N
71 * onwards and end up with SEQ + N + N. In other words, most of each
72 * SEQ's IND slots will remain unused. When looking at SQN being 48
73 * bits wide, after dropping ind_bitlen (say 5) from it, we will still
74 * have a sequence range of 2^43 = 8.8e12, eight trillion sequences,
75 * which is large enough to not bother further. With the maximum
76 * ind_bitlen of 28 enforced below, we still get more than 1 million
77 * sequences, which is also sufficiently large.
78 *
79 * An ind_bitlen of zero may be passed from legacy callers that are not
80 * aware of the IND extension. For these, below algorithm works out as
81 * before, simply incrementing SQN by 1.
82 *
83 * This is also a mechanism for tools like the osmo-auc-gen to directly
84 * request a given SQN to be used. With ind_bitlen == 0 the caller can
85 * be sure that this code will increment SQN by exactly one before
86 * generating a tuple, thus a caller would simply pass
87 * { .ind_bitlen = 0, .ind = 0, .sqn = (desired_sqn - 1) }
88 */
89
90 if (aud->u.umts.ind_bitlen > 28)
91 return -2;
92
93 seq_1 = 1LL << aud->u.umts.ind_bitlen;
94 ind_mask = ~(seq_1 - 1);
95
96 /* the ind index must not affect the SEQ part */
97 if (aud->u.umts.ind > seq_1)
98 return -3;
99
Neels Hofmeyr82c9a0e2017-03-13 17:36:17 +0100100 /* keep the incremented SQN local until gsm_milenage() succeeded. */
Neels Hofmeyrbb6f7b72017-03-13 17:27:17 +0100101 next_sqn = ((aud->u.umts.sqn + seq_1) & ind_mask) + aud->u.umts.ind;
Neels Hofmeyr82c9a0e2017-03-13 17:36:17 +0100102
103 osmo_store64be_ext(next_sqn, sqn, 6);
Harald Welteaae23622011-12-07 11:35:02 +0100104 milenage_generate(aud->u.umts.opc, aud->u.umts.amf, aud->u.umts.k,
Harald Welte781bd5d2011-12-06 22:23:52 +0100105 sqn, _rand,
106 vec->autn, vec->ik, vec->ck, vec->res, &res_len);
107 vec->res_len = res_len;
Harald Welteaae23622011-12-07 11:35:02 +0100108 rc = gsm_milenage(aud->u.umts.opc, aud->u.umts.k, _rand, vec->sres, vec->kc);
Harald Welte781bd5d2011-12-06 22:23:52 +0100109 if (rc < 0)
110 return rc;
111
112 vec->auth_types = OSMO_AUTH_TYPE_UMTS | OSMO_AUTH_TYPE_GSM;
Neels Hofmeyr82c9a0e2017-03-13 17:36:17 +0100113
114 /* for storage in the caller's AUC database */
115 aud->u.umts.sqn = next_sqn;
Harald Welte781bd5d2011-12-06 22:23:52 +0100116
117 return 0;
118}
119
120static int milenage_gen_vec_auts(struct osmo_auth_vector *vec,
121 struct osmo_sub_auth_data *aud,
122 const uint8_t *auts, const uint8_t *rand_auts,
123 const uint8_t *_rand)
124{
125 uint8_t sqn_out[6];
Harald Weltea72e47b2012-03-21 09:03:16 +0100126 uint8_t gen_opc[16];
127 uint8_t *opc;
Harald Welte781bd5d2011-12-06 22:23:52 +0100128 int rc;
129
Harald Weltea72e47b2012-03-21 09:03:16 +0100130 /* Check if we only know OP and compute OPC if required */
131 if (aud->type == OSMO_AUTH_TYPE_UMTS && aud->u.umts.opc_is_op) {
132 rc = milenage_opc_gen(gen_opc, aud->u.umts.k,
133 aud->u.umts.opc);
134 if (rc < 0)
135 return rc;
136 opc = gen_opc;
137 } else
138 opc = aud->u.umts.opc;
139
140 rc = milenage_auts(opc, aud->u.umts.k, rand_auts, auts, sqn_out);
Harald Welte781bd5d2011-12-06 22:23:52 +0100141 if (rc < 0)
142 return rc;
143
Neels Hofmeyrbb6f7b72017-03-13 17:27:17 +0100144 /* Update our "largest used SQN" from the USIM -- milenage_gen_vec()
145 * below will increment SQN. */
Neels Hofmeyr82c9a0e2017-03-13 17:36:17 +0100146 aud->u.umts.sqn = osmo_load64be_ext(sqn_out, 6) >> 16;
Harald Welte781bd5d2011-12-06 22:23:52 +0100147
148 return milenage_gen_vec(vec, aud, _rand);
149}
150
151static struct osmo_auth_impl milenage_alg = {
152 .algo = OSMO_AUTH_ALG_MILENAGE,
153 .name = "MILENAGE (libosmogsm built-in)",
154 .priority = 1000,
155 .gen_vec = &milenage_gen_vec,
156 .gen_vec_auts = &milenage_gen_vec_auts,
157};
158
159static __attribute__((constructor)) void on_dso_load_milenage(void)
160{
161 osmo_auth_register(&milenage_alg);
162}