diff options
Diffstat (limited to 'src/gsm/auth_core.c')
-rw-r--r-- | src/gsm/auth_core.c | 172 |
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); + } +} + /*! @} */ |