blob: 2bd05a6d6d41c0289148783b99f8a5486f19e13c [file] [log] [blame]
Neels Hofmeyr17518fe2017-06-20 04:35:06 +02001/*! \file auth_milenage.c
2 * GSM/GPRS/3G authentication core infrastructure */
3/*
4 * (C) 2011 by Harald Welte <laforge@gnumonks.org>
Harald Welte781bd5d2011-12-06 22:23:52 +01005 *
6 * All Rights Reserved
7 *
Harald Weltee08da972017-11-13 01:00:26 +09008 * SPDX-License-Identifier: GPL-2.0+
9 *
Harald Welte781bd5d2011-12-06 22:23:52 +010010 * 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 *
Harald Welte781bd5d2011-12-06 22:23:52 +010020 */
21
Harald Weltea9c91cc2023-05-30 11:58:28 +020022#include <errno.h>
Harald Welte781bd5d2011-12-06 22:23:52 +010023#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 Welte96e2a002017-06-12 21:44:18 +020028/*! \addtogroup auth
29 * @{
30 */
31
Harald Welte08450c92023-05-30 10:55:37 +020032static const uint8_t *gen_opc_if_needed(const struct osmo_sub_auth_data2 *aud, uint8_t *gen_opc)
Harald Welte113392a2017-08-16 23:37:25 +020033{
34 int rc;
35
36 /* Check if we only know OP and compute OPC if required */
37 if (aud->type == OSMO_AUTH_TYPE_UMTS && aud->u.umts.opc_is_op) {
38 rc = milenage_opc_gen(gen_opc, aud->u.umts.k, aud->u.umts.opc);
39 if (rc < 0)
40 return NULL;
41 return gen_opc;
42 } else
43 return aud->u.umts.opc;
44}
45
Harald Welte781bd5d2011-12-06 22:23:52 +010046static int milenage_gen_vec(struct osmo_auth_vector *vec,
Harald Welte08450c92023-05-30 10:55:37 +020047 struct osmo_sub_auth_data2 *aud,
Harald Welte781bd5d2011-12-06 22:23:52 +010048 const uint8_t *_rand)
49{
50 size_t res_len = sizeof(vec->res);
Neels Hofmeyr82c9a0e2017-03-13 17:36:17 +010051 uint64_t next_sqn;
Harald Welte113392a2017-08-16 23:37:25 +020052 uint8_t gen_opc[16];
53 const uint8_t *opc;
Harald Welte781bd5d2011-12-06 22:23:52 +010054 uint8_t sqn[6];
Neels Hofmeyrbb6f7b72017-03-13 17:27:17 +010055 uint64_t ind_mask;
56 uint64_t seq_1;
Harald Welte781bd5d2011-12-06 22:23:52 +010057
Harald Welte5248c472023-05-30 11:09:17 +020058 OSMO_ASSERT(aud->algo == OSMO_AUTH_ALG_MILENAGE);
59
Harald Weltea9c91cc2023-05-30 11:58:28 +020060 if (aud->u.umts.k_len != 16)
61 return -EINVAL;
62 if (aud->u.umts.opc_len != 16)
63 return -EINVAL;
Harald Welted8e53092023-05-30 15:01:38 +020064 if (vec->res_len != 4 && vec->res_len != 8)
65 return -EINVAL;
Harald Weltea9c91cc2023-05-30 11:58:28 +020066
Harald Welte113392a2017-08-16 23:37:25 +020067 opc = gen_opc_if_needed(aud, gen_opc);
68 if (!opc)
69 return -1;
70
Neels Hofmeyrbb6f7b72017-03-13 17:27:17 +010071 /* Determine next SQN, according to 3GPP TS 33.102:
72 * SQN consists of SEQ and a lower significant part of IND bits:
73 *
74 * |----------SEQ------------|
75 * |------------------------SQN-----------|
76 * |-----IND----|
77 *
78 * The IND part is used as "slots": e.g. a given HLR client will always
79 * get the same IND part, called ind here, with incrementing SEQ. In
80 * the USIM, each IND slot enforces that its SEQ are used in ascending
81 * order -- as long as that constraint is satisfied, the SQN may jump
82 * forwards and backwards. For example, for ind_bitlen == 5, asking the
83 * USIM for SQN = 32, 64, 33 is allowed, because 32 and 64 are
84 * SEQ || (ind == 0), and though 33 is below 64, it is ind == 1 and
85 * allowed. Not allowed would be 32, 96, 64, because 64 would go
86 * backwards after 96, both being ind == 0.
87 *
88 * From the last used SQN, we want to increment SEQ + 1, and then pick
89 * the matching IND part.
90 *
91 * IND size is suggested in TS 33.102 as 5 bits. SQN is 48 bits long.
92 * If ind_bitlen is passed too large here, the algorithms will break
93 * down. But at which point should we return an error? A sane limit
94 * seems to be ind_bitlen == 10, but to protect against failure,
95 * limiting ind_bitlen to 28 is enough, 28 being the number of bits
96 * suggested for the delta in 33.102, which is discussed to still
97 * require 2^15 > 32000 authentications to wrap the SQN back to the
98 * start.
99 *
100 * Note that if a caller with ind == 1 generates N vectors, the SQN
101 * stored after this will reflect SEQ + N. If then another caller with
102 * ind == 2 generates another N vectors, this will then use SEQ + N
103 * onwards and end up with SEQ + N + N. In other words, most of each
104 * SEQ's IND slots will remain unused. When looking at SQN being 48
105 * bits wide, after dropping ind_bitlen (say 5) from it, we will still
106 * have a sequence range of 2^43 = 8.8e12, eight trillion sequences,
107 * which is large enough to not bother further. With the maximum
108 * ind_bitlen of 28 enforced below, we still get more than 1 million
109 * sequences, which is also sufficiently large.
110 *
111 * An ind_bitlen of zero may be passed from legacy callers that are not
112 * aware of the IND extension. For these, below algorithm works out as
113 * before, simply incrementing SQN by 1.
114 *
115 * This is also a mechanism for tools like the osmo-auc-gen to directly
116 * request a given SQN to be used. With ind_bitlen == 0 the caller can
117 * be sure that this code will increment SQN by exactly one before
118 * generating a tuple, thus a caller would simply pass
119 * { .ind_bitlen = 0, .ind = 0, .sqn = (desired_sqn - 1) }
120 */
121
Neels Hofmeyr1a02e362017-10-07 04:44:08 +0200122 if (aud->u.umts.ind_bitlen > OSMO_MILENAGE_IND_BITLEN_MAX)
Neels Hofmeyrbb6f7b72017-03-13 17:27:17 +0100123 return -2;
124
125 seq_1 = 1LL << aud->u.umts.ind_bitlen;
126 ind_mask = ~(seq_1 - 1);
127
128 /* the ind index must not affect the SEQ part */
Neels Hofmeyre0cb0ee2017-03-16 05:30:11 +0100129 if (aud->u.umts.ind >= seq_1)
Neels Hofmeyrbb6f7b72017-03-13 17:27:17 +0100130 return -3;
131
Neels Hofmeyr82c9a0e2017-03-13 17:36:17 +0100132 /* keep the incremented SQN local until gsm_milenage() succeeded. */
Neels Hofmeyrbb6f7b72017-03-13 17:27:17 +0100133 next_sqn = ((aud->u.umts.sqn + seq_1) & ind_mask) + aud->u.umts.ind;
Neels Hofmeyr82c9a0e2017-03-13 17:36:17 +0100134
135 osmo_store64be_ext(next_sqn, sqn, 6);
Harald Welte113392a2017-08-16 23:37:25 +0200136 milenage_generate(opc, aud->u.umts.amf, aud->u.umts.k,
Harald Welte781bd5d2011-12-06 22:23:52 +0100137 sqn, _rand,
138 vec->autn, vec->ik, vec->ck, vec->res, &res_len);
Harald Welted8e53092023-05-30 15:01:38 +0200139
Harald Welte9d5d3552023-05-30 16:01:45 +0200140 osmo_auth_c3(vec->kc, vec->ck, vec->ik);
141 osmo_auth_c2(vec->sres, vec->res, vec->res_len, 1);
Harald Welte781bd5d2011-12-06 22:23:52 +0100142
143 vec->auth_types = OSMO_AUTH_TYPE_UMTS | OSMO_AUTH_TYPE_GSM;
Neels Hofmeyr82c9a0e2017-03-13 17:36:17 +0100144
145 /* for storage in the caller's AUC database */
146 aud->u.umts.sqn = next_sqn;
Harald Welte781bd5d2011-12-06 22:23:52 +0100147
148 return 0;
149}
150
151static int milenage_gen_vec_auts(struct osmo_auth_vector *vec,
Harald Welte08450c92023-05-30 10:55:37 +0200152 struct osmo_sub_auth_data2 *aud,
Harald Welte781bd5d2011-12-06 22:23:52 +0100153 const uint8_t *auts, const uint8_t *rand_auts,
154 const uint8_t *_rand)
155{
156 uint8_t sqn_out[6];
Harald Weltea72e47b2012-03-21 09:03:16 +0100157 uint8_t gen_opc[16];
Harald Welte113392a2017-08-16 23:37:25 +0200158 const uint8_t *opc;
Harald Welte781bd5d2011-12-06 22:23:52 +0100159 int rc;
160
Harald Welte5248c472023-05-30 11:09:17 +0200161 OSMO_ASSERT(aud->algo == OSMO_AUTH_ALG_MILENAGE);
162
Harald Weltea9c91cc2023-05-30 11:58:28 +0200163 if (aud->u.umts.k_len != 16)
164 return -EINVAL;
165 if (aud->u.umts.opc_len != 16)
166 return -EINVAL;
167
Harald Welte113392a2017-08-16 23:37:25 +0200168 opc = gen_opc_if_needed(aud, gen_opc);
Harald Weltea72e47b2012-03-21 09:03:16 +0100169
170 rc = milenage_auts(opc, aud->u.umts.k, rand_auts, auts, sqn_out);
Harald Welte781bd5d2011-12-06 22:23:52 +0100171 if (rc < 0)
172 return rc;
173
Neels Hofmeyr2066a422017-08-26 22:43:50 +0200174 aud->u.umts.sqn_ms = osmo_load64be_ext(sqn_out, 6) >> 16;
Neels Hofmeyrbb6f7b72017-03-13 17:27:17 +0100175 /* Update our "largest used SQN" from the USIM -- milenage_gen_vec()
176 * below will increment SQN. */
Neels Hofmeyr2066a422017-08-26 22:43:50 +0200177 aud->u.umts.sqn = aud->u.umts.sqn_ms;
Harald Welte781bd5d2011-12-06 22:23:52 +0100178
179 return milenage_gen_vec(vec, aud, _rand);
180}
181
182static struct osmo_auth_impl milenage_alg = {
183 .algo = OSMO_AUTH_ALG_MILENAGE,
184 .name = "MILENAGE (libosmogsm built-in)",
185 .priority = 1000,
186 .gen_vec = &milenage_gen_vec,
187 .gen_vec_auts = &milenage_gen_vec_auts,
188};
189
190static __attribute__((constructor)) void on_dso_load_milenage(void)
191{
192 osmo_auth_register(&milenage_alg);
193}
Harald Welte96e2a002017-06-12 21:44:18 +0200194
195/*! @} */