aboutsummaryrefslogtreecommitdiffstats
path: root/src/gsm/auth_core.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/gsm/auth_core.c')
-rw-r--r--src/gsm/auth_core.c172
1 files changed, 158 insertions, 14 deletions
diff --git a/src/gsm/auth_core.c b/src/gsm/auth_core.c
index 9e750a01..6972cb77 100644
--- a/src/gsm/auth_core.c
+++ b/src/gsm/auth_core.c
@@ -1,4 +1,4 @@
-/* (C) 2010-2012 by Harald Welte <laforge@gnumonks.org>
+/* (C) 2010-2023 by Harald Welte <laforge@gnumonks.org>
*
* All Rights Reserved
*
@@ -14,10 +14,6 @@
* 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 General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include "config.h"
@@ -40,6 +36,35 @@
static LLIST_HEAD(osmo_auths);
+/* generate auth_data2 from auth_data (for legacy API/ABI compatibility */
+static int auth_data2auth_data2(struct osmo_sub_auth_data2 *out, const struct osmo_sub_auth_data *in)
+{
+ out->type = in->type;
+ out->algo = in->algo;
+ switch (in->type) {
+ case OSMO_AUTH_TYPE_NONE:
+ return 0;
+ case OSMO_AUTH_TYPE_GSM:
+ memcpy(out->u.gsm.ki, in->u.gsm.ki, sizeof(out->u.gsm.ki));
+ break;
+ case OSMO_AUTH_TYPE_UMTS:
+ memcpy(out->u.umts.opc, in->u.umts.opc, sizeof(in->u.umts.opc));
+ out->u.umts.opc_len = sizeof(in->u.umts.opc);
+ memcpy(out->u.umts.k, in->u.umts.k, sizeof(in->u.umts.k));
+ out->u.umts.k_len = sizeof(in->u.umts.k);
+ memcpy(out->u.umts.amf, in->u.umts.amf, sizeof(out->u.umts.amf));
+ out->u.umts.sqn = in->u.umts.sqn;
+ out->u.umts.opc_is_op = in->u.umts.opc_is_op;
+ out->u.umts.ind_bitlen = in->u.umts.ind_bitlen;
+ out->u.umts.ind = in->u.umts.ind;
+ out->u.umts.sqn_ms = in->u.umts.sqn_ms;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
static struct osmo_auth_impl *selected_auths[_OSMO_AUTH_ALG_NUM];
/*! Register an authentication algorithm implementation with the core
@@ -142,6 +167,42 @@ int osmo_auth_3g_from_2g(struct osmo_auth_vector *vec)
}
/*! Generate authentication vector
+ * \param[out] vec Generated authentication vector. See below!
+ * \param[in] aud Subscriber-specific key material
+ * \param[in] _rand Random challenge to be used
+ * \returns 0 on success, negative error on failure
+ *
+ * This function performs the core cryptographic function of the AUC,
+ * computing authentication triples/quintuples based on the permanent
+ * subscriber data and a random value. The result is what is forwarded
+ * by the AUC via HLR and VLR to the MSC which will then be able to
+ * invoke authentication with the MS.
+ *
+ * Contrary to the older osmo_auth_gen_vec(), the caller must specify
+ * the desired RES length in the vec->res_len field prior to calling
+ * this function. The requested length must match the capabilities of
+ * the chosen algorithm (e.g. 4/8 for MILENAGE).
+ */
+int osmo_auth_gen_vec2(struct osmo_auth_vector *vec,
+ struct osmo_sub_auth_data2 *aud,
+ const uint8_t *_rand)
+{
+ struct osmo_auth_impl *impl = selected_auths[aud->algo];
+ int rc;
+
+ if (!impl)
+ return -ENOENT;
+
+ rc = impl->gen_vec(vec, aud, _rand);
+ if (rc < 0)
+ return rc;
+
+ memcpy(vec->rand, _rand, sizeof(vec->rand));
+
+ return 0;
+}
+
+/*! Generate authentication vector
* \param[out] vec Generated authentication vector
* \param[in] aud Subscriber-specific key material
* \param[in] _rand Random challenge to be used
@@ -157,13 +218,58 @@ int osmo_auth_gen_vec(struct osmo_auth_vector *vec,
struct osmo_sub_auth_data *aud,
const uint8_t *_rand)
{
+ struct osmo_sub_auth_data2 aud2;
+ int rc;
+
+ if (aud->type == OSMO_AUTH_TYPE_UMTS) {
+ /* old API callers are not expected to initialize this struct field,
+ * and always expect an 8-byte RES value */
+ vec->res_len = 8;
+ }
+
+ rc = auth_data2auth_data2(&aud2, aud);
+ if (rc < 0)
+ return rc;
+
+ rc = osmo_auth_gen_vec2(vec, &aud2, _rand);
+ if (aud->type == OSMO_AUTH_TYPE_UMTS)
+ aud->u.umts.sqn = aud2.u.umts.sqn;
+
+ return rc;
+}
+
+/*! Generate authentication vector and re-sync sequence
+ * \param[out] vec Generated authentication vector. See below!
+ * \param[in] aud Subscriber-specific key material
+ * \param[in] auts AUTS value sent by the SIM/MS
+ * \param[in] rand_auts RAND value sent by the SIM/MS
+ * \param[in] _rand Random challenge to be used to generate vector
+ * \returns 0 on success, negative error on failure
+ *
+ * This function performs a special variant of the core cryptographic
+ * function of the AUC: computing authentication triples/quintuples
+ * based on the permanent subscriber data, a random value as well as the
+ * AUTS and RAND values returned by the SIM/MS. This special variant is
+ * needed if the sequence numbers between MS and AUC have for some
+ * reason become different.
+ *
+ * Contrary to the older osmo_auth_gen_vec_auts(), the caller must specify
+ * the desired RES length in the vec->res_len field prior to calling
+ * this function. The requested length must match the capabilities of
+ * the chosen algorithm (e.g. 4/8 for MILENAGE).
+ */
+int osmo_auth_gen_vec_auts2(struct osmo_auth_vector *vec,
+ struct osmo_sub_auth_data2 *aud,
+ const uint8_t *auts, const uint8_t *rand_auts,
+ const uint8_t *_rand)
+{
struct osmo_auth_impl *impl = selected_auths[aud->algo];
int rc;
- if (!impl)
+ if (!impl || !impl->gen_vec_auts)
return -ENOENT;
- rc = impl->gen_vec(vec, aud, _rand);
+ rc = impl->gen_vec_auts(vec, aud, auts, rand_auts, _rand);
if (rc < 0)
return rc;
@@ -192,19 +298,26 @@ int osmo_auth_gen_vec_auts(struct osmo_auth_vector *vec,
const uint8_t *auts, const uint8_t *rand_auts,
const uint8_t *_rand)
{
- struct osmo_auth_impl *impl = selected_auths[aud->algo];
+ struct osmo_sub_auth_data2 aud2;
int rc;
- if (!impl || !impl->gen_vec_auts)
- return -ENOENT;
+ if (aud->type == OSMO_AUTH_TYPE_UMTS) {
+ /* old API callers are not expected to initialize this struct field,
+ * and always expect an 8-byte RES value */
+ vec->res_len = 8;
+ }
- rc = impl->gen_vec_auts(vec, aud, auts, rand_auts, _rand);
+ rc = auth_data2auth_data2(&aud2, aud);
if (rc < 0)
return rc;
- memcpy(vec->rand, _rand, sizeof(vec->rand));
+ rc = osmo_auth_gen_vec_auts2(vec, &aud2, auts, rand_auts, _rand);
+ if (aud->type == OSMO_AUTH_TYPE_UMTS) {
+ aud->u.umts.sqn = aud2.u.umts.sqn;
+ aud->u.umts.sqn_ms = aud2.u.umts.sqn_ms;
+ }
- return 0;
+ return rc;
}
static const struct value_string auth_alg_vals[] = {
@@ -212,8 +325,10 @@ static const struct value_string auth_alg_vals[] = {
{ OSMO_AUTH_ALG_COMP128v1, "COMP128v1" },
{ OSMO_AUTH_ALG_COMP128v2, "COMP128v2" },
{ OSMO_AUTH_ALG_COMP128v3, "COMP128v3" },
- { OSMO_AUTH_ALG_XOR, "XOR" },
+ { OSMO_AUTH_ALG_XOR_3G, "XOR-3G" },
{ OSMO_AUTH_ALG_MILENAGE, "MILENAGE" },
+ { OSMO_AUTH_ALG_XOR_2G, "XOR-2G" },
+ { OSMO_AUTH_ALG_TUAK, "TUAK" },
{ 0, NULL }
};
@@ -249,4 +364,33 @@ void osmo_auth_c3(uint8_t kc[], const uint8_t ck[], const uint8_t ik[])
kc[i] = ck[i] ^ ck[i + 8] ^ ik[i] ^ ik[i + 8];
}
+/*! Derive GSM SRES from UMTS [X]RES (auth function c2 from 3GPP TS 33.103 Section 6.8.1.2
+ * \param[out] sres GSM SRES value, 4 byte target buffer
+ * \param[in] res UMTS XRES, 4..16 bytes input buffer
+ * \param[in] res_len length of res parameter (in bytes)
+ * \param[in] sres_deriv_func SRES derivation function (1 or 2, see 3GPP TS 55.205 Section 4
+ */
+void osmo_auth_c2(uint8_t sres[4], const uint8_t *res, size_t res_len, uint8_t sres_deriv_func)
+{
+ uint8_t xres[16];
+
+ OSMO_ASSERT(sres_deriv_func == 1 || sres_deriv_func == 2);
+ OSMO_ASSERT(res_len <= sizeof(xres));
+
+ memcpy(xres, res, res_len);
+
+ /* zero-pad the end, if XRES is < 16 bytes */
+ if (res_len < sizeof(xres))
+ memset(xres+res_len, 0, sizeof(xres)-res_len);
+
+ if (sres_deriv_func == 1) {
+ /* SRES derivation function #1 */
+ for (unsigned int i = 0; i < 4; i++)
+ sres[i] = xres[i] ^ xres[4+i] ^ xres[8+i] ^ xres[12+i];
+ } else {
+ /* SRES derivation function #2 */
+ memcpy(sres, xres, 4);
+ }
+}
+
/*! @} */