milenage.h 10.1 KB
Newer Older
Laurent's avatar
Laurent committed
1
/*
2
   Adaptatipn of SW from hereafter license
Laurent's avatar
Laurent committed
3
   Author: laurent.thomas@open-cells.com
4 5
   The OpenAirInterface project uses this copy under the terms of BSD
   license.
Laurent's avatar
Laurent committed
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
*/
/*
   3GPP AKA - Milenage algorithm (3GPP TS 35.205, .206, .207, .208)
   Copyright (c) 2006-2007 <j@w1.fi>

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License version 2 as
   published by the Free Software Foundation.

   Alternatively, this software may be distributed under the terms of BSD
   license.

   See README and COPYING for more details.

   This file implements an example authentication algorithm defined for 3GPP
   AKA. This can be used to implement a simple HLR/AuC into hlr_auc_gw to allow
   EAP-AKA to be tested properly with real USIM cards.

   This implementations assumes that the r1..r5 and c1..c5 constants defined in
   TS 35.206 are used, i.e., r1=64, r2=0, r3=32, r4=64, r5=96, c1=00..00,
   c2=00..01, c3=00..02, c4=00..04, c5=00..08. The block cipher is assumed to
   be AES (Rijndael).
*/

#ifndef MILENAGE_H
#define MILENAGE_H
32 33
#include <openssl/aes.h>

Laurent's avatar
Laurent committed
34 35 36 37 38 39 40 41 42 43 44 45 46

#define u8 uint8_t
/**
   milenage_f1 - Milenage f1 and f1* algorithms
   @opc: OPc = 128-bit value derived from OP and K
   @k: K = 128-bit subscriber key
   @_rand: RAND = 128-bit random challenge
   @sqn: SQN = 48-bit sequence number
   @amf: AMF = 16-bit authentication management field
   @mac_a: Buffer for MAC-A = 64-bit network authentication code, or %NULL
   @mac_s: Buffer for MAC-S = 64-bit resync authentication code, or %NULL
   Returns: true on success, false on failure
*/
47 48 49 50 51 52 53 54

void aes_128_encrypt_block(const u8* key, const u8* in, u8* out) {
   AES_KEY k;
  u8 tmp[16];
  AES_set_encrypt_key(key, 128, &k);
  AES_encrypt(in, tmp, &k); 
  memcpy(out, tmp, sizeof(tmp));
}
55
static bool milenage_f1(const u8 *opc, const u8 *k, const u8 *_rand,
Laurent's avatar
Laurent committed
56 57 58 59 60 61 62 63 64
                 const u8 *sqn, const u8 *amf, u8 *mac_a, u8 *mac_s) {
  u8 tmp1[16], tmp2[16], tmp3[16];
  int i;

  /* tmp1 = TEMP = E_K(RAND XOR OP_C) */
  for (i = 0; i < 16; i++)
    tmp1[i] = _rand[i] ^ opc[i];

  aes_128_encrypt_block(k, tmp1, tmp1);
65 66 67 68
  AES_KEY kTab;
  uint8_t cyphered[16];
  AES_set_encrypt_key(k, 128, &kTab);
  AES_encrypt(tmp1, cyphered, &kTab);
Laurent's avatar
Laurent committed
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
  /* tmp2 = IN1 = SQN || AMF || SQN || AMF */
  memcpy(tmp2, sqn, 6);
  memcpy(tmp2 + 6, amf, 2);
  memcpy(tmp2 + 8, tmp2, 8);

  /* OUT1 = E_K(TEMP XOR rot(IN1 XOR OP_C, r1) XOR c1) XOR OP_C */

  /* rotate (tmp2 XOR OP_C) by r1 (= 0x40 = 8 bytes) */
  for (i = 0; i < 16; i++)
    tmp3[(i + 8) % 16] = tmp2[i] ^ opc[i];

  /* XOR with TEMP = E_K(RAND XOR OP_C) */
  for (i = 0; i < 16; i++)
    tmp3[i] ^= tmp1[i];

  /* XOR with c1 (= ..00, i.e., NOP) */
  /* f1 || f1* = E_K(tmp3) XOR OP_c */
  aes_128_encrypt_block(k, tmp3, tmp1);

  for (i = 0; i < 16; i++)
    tmp1[i] ^= opc[i];

  if (mac_a)
    memcpy(mac_a, tmp1, 8); /* f1 */

  if (mac_s)
    memcpy(mac_s, tmp1 + 8, 8); /* f1* */

  return true;
}


/**
   milenage_f2345 - Milenage f2, f3, f4, f5, f5* algorithms
   @opc: OPc = 128-bit value derived from OP and K
   @k: K = 128-bit subscriber key
   @_rand: RAND = 128-bit random challenge
   @res: Buffer for RES = 64-bit signed response (f2), or %NULL
   @ck: Buffer for CK = 128-bit confidentiality key (f3), or %NULL
   @ik: Buffer for IK = 128-bit integrity key (f4), or %NULL
   @ak: Buffer for AK = 48-bit anonymity key (f5), or %NULL
   @akstar: Buffer for AK = 48-bit anonymity key (f5*), or %NULL
   Returns: true on success, false on failure
*/
113
static bool milenage_f2345(const u8 *opc, const u8 *k, const u8 *_rand,
Laurent's avatar
Laurent committed
114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202
                    u8 *res, u8 *ck, u8 *ik, u8 *ak, u8 *akstar) {
  u8 tmp1[16], tmp2[16], tmp3[16];
  int i;

  /* tmp2 = TEMP = E_K(RAND XOR OP_C) */
  for (i = 0; i < 16; i++)
    tmp1[i] = _rand[i] ^ opc[i];

  aes_128_encrypt_block(k, tmp1, tmp2);

  /* OUT2 = E_K(rot(TEMP XOR OP_C, r2) XOR c2) XOR OP_C */
  /* OUT3 = E_K(rot(TEMP XOR OP_C, r3) XOR c3) XOR OP_C */
  /* OUT4 = E_K(rot(TEMP XOR OP_C, r4) XOR c4) XOR OP_C */
  /* OUT5 = E_K(rot(TEMP XOR OP_C, r5) XOR c5) XOR OP_C */

  /* f2 and f5 */
  /* rotate by r2 (= 0, i.e., NOP) */
  for (i = 0; i < 16; i++)
    tmp1[i] = tmp2[i] ^ opc[i];

  tmp1[15] ^= 1; /* XOR c2 (= ..01) */
  /* f5 || f2 = E_K(tmp1) XOR OP_c */
  aes_128_encrypt_block(k, tmp1, tmp3);

  for (i = 0; i < 16; i++)
    tmp3[i] ^= opc[i];

  if (res)
    memcpy(res, tmp3 + 8, 8); /* f2 */

  if (ak)
    memcpy(ak, tmp3, 6); /* f5 */

  /* f3 */
  if (ck) {
    /* rotate by r3 = 0x20 = 4 bytes */
    for (i = 0; i < 16; i++)
      tmp1[(i + 12) % 16] = tmp2[i] ^ opc[i];

    tmp1[15] ^= 2; /* XOR c3 (= ..02) */
    aes_128_encrypt_block(k, tmp1, ck);

    for (i = 0; i < 16; i++)
      ck[i] ^= opc[i];
  }

  /* f4 */
  if (ik) {
    /* rotate by r4 = 0x40 = 8 bytes */
    for (i = 0; i < 16; i++)
      tmp1[(i + 8) % 16] = tmp2[i] ^ opc[i];

    tmp1[15] ^= 4; /* XOR c4 (= ..04) */
    aes_128_encrypt_block(k, tmp1, ik);

    for (i = 0; i < 16; i++)
      ik[i] ^= opc[i];
  }

  /* f5* */
  if (akstar) {
    /* rotate by r5 = 0x60 = 12 bytes */
    for (i = 0; i < 16; i++)
      tmp1[(i + 4) % 16] = tmp2[i] ^ opc[i];

    tmp1[15] ^= 8; /* XOR c5 (= ..08) */
    aes_128_encrypt_block(k, tmp1, tmp1);

    for (i = 0; i < 6; i++)
      akstar[i] = tmp1[i] ^ opc[i];
  }

  return true;
}


/**
   milenage_generate - Generate AKA AUTN,IK,CK,RES
   @opc: OPc = 128-bit operator variant algorithm configuration field (encr.)
   @amf: AMF = 16-bit authentication management field
   @k: K = 128-bit subscriber key
   @sqn: SQN = 48-bit sequence number
   @_rand: RAND = 128-bit random challenge
   @autn: Buffer for AUTN = 128-bit authentication token
   @ik: Buffer for IK = 128-bit integrity key (f4), or %NULL
   @ck: Buffer for CK = 128-bit confidentiality key (f3), or %NULL
   @res: Buffer for RES = 64-bit signed response (f2), or %NULL
   @res_len: Max length for res; set to used length or 0 on failure
*/
203
static bool milenage_generate(const u8 *opc, const u8 *amf, const u8 *k,
Laurent's avatar
Laurent committed
204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232
                       const u8 *sqn, const u8 *_rand, u8 *autn, u8 *ik,
                       u8 *ck, u8 *res) {
  int i;
  u8 mac_a[8], ak[6];

  if (!milenage_f1(opc, k, _rand, sqn, amf, mac_a, NULL) ||
      !milenage_f2345(opc, k, _rand, res, ck, ik, ak, NULL))
    return false;

  /* AUTN = (SQN ^ AK) || AMF || MAC */
  for (i = 0; i < 6; i++)
    autn[i] = sqn[i] ^ ak[i];

  memcpy(autn + 6, amf, 2);
  memcpy(autn + 8, mac_a, 8);
  return true;
}


/**
   milenage_auts - Milenage AUTS validation
   @opc: OPc = 128-bit operator variant algorithm configuration field (encr.)
   @k: K = 128-bit subscriber key
   @_rand: RAND = 128-bit random challenge
   @auts: AUTS = 112-bit authentication token from client
   @sqn: Buffer for SQN = 48-bit sequence number
   Returns: 0 = success (sqn filled), -1 on failure
*/
#define p(a) printf("%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx\n", (int)((a)[0]), (int)((a)[1]), (int)((a)[2]), (int)((a)[3]),(int)((a)[4]), (int)((a)[5]));
Laurent's avatar
Laurent committed
233 234
// valid code but put in comment as it is not used right now
#if 0
235
static bool milenage_auts(const u8 *opc, const u8 *k, const u8 *_rand, const u8 *auts,
Laurent's avatar
Laurent committed
236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264
                   u8 *sqn) {
  u8 amf[2] = { 0x00, 0x00 }; /* TS 33.102 v7.0.0, 6.3.3 */
  u8 ak[6], mac_s[8];
  int i;

  if (!milenage_f2345(opc, k, _rand, NULL, NULL, NULL, NULL, ak))
    return false;

  for (i = 0; i < 6; i++)
    sqn[i] = auts[i] ^ ak[i];

  //p(sqn);
  if (!milenage_f1(opc, k, _rand, sqn, amf, NULL, mac_s) ||
      memcmp(mac_s, auts + 6, 8) != 0)
    return false;

  return true;
}


/**
   gsm_milenage - Generate GSM-Milenage (3GPP TS 55.205) authentication triplet
   @opc: OPc = 128-bit operator variant algorithm configuration field (encr.)
   @k: K = 128-bit subscriber key
   @_rand: RAND = 128-bit random challenge
   @sres: Buffer for SRES = 32-bit SRES
   @kc: Buffer for Kc = 64-bit Kc
   Returns: 0 on success, -1 on failure
*/
265
static bool gsm_milenage(const u8 *opc, const u8 *k, const u8 *_rand, u8 *sres, u8 *kc) {
Laurent's avatar
Laurent committed
266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300
  u8 res[8], ck[16], ik[16];
  int i;

  if (milenage_f2345(opc, k, _rand, res, ck, ik, NULL, NULL))
    return false;

  for (i = 0; i < 8; i++)
    kc[i] = ck[i] ^ ck[i + 8] ^ ik[i] ^ ik[i + 8];

#ifdef GSM_MILENAGE_ALT_SRES
  memcpy(sres, res, 4);
#else /* GSM_MILENAGE_ALT_SRES */

  for (i = 0; i < 4; i++)
    sres[i] = res[i] ^ res[i + 4];

#endif /* GSM_MILENAGE_ALT_SRES */
  return true;
}


/**
   milenage_generate - Generate AKA AUTN,IK,CK,RES
   @opc: OPc = 128-bit operator variant algorithm configuration field (encr.)
   @k: K = 128-bit subscriber key
   @sqn: SQN = 48-bit sequence number
   @_rand: RAND = 128-bit random challenge
   @autn: AUTN = 128-bit authentication token
   @ik: Buffer for IK = 128-bit integrity key (f4), or %NULL
   @ck: Buffer for CK = 128-bit confidentiality key (f3), or %NULL
   @res: Buffer for RES = 64-bit signed response (f2), or %NULL
   @res_len: Variable that will be set to RES length
   @auts: 112-bit buffer for AUTS
   Returns: 0 on success, -1 on failure, or -2 on synchronization failure
*/
301
static int milenage_check(const u8 *opc, const u8 *k, const u8 *sqn, const u8 *_rand,
Laurent's avatar
Laurent committed
302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344
                   const u8 *autn, u8 *ik, u8 *ck, u8 *res, size_t *res_len,
                   u8 *auts) {
  int i;
  u8 mac_a[8], ak[6], rx_sqn[6];
  const u8 *amf;

  if (milenage_f2345(opc, k, _rand, res, ck, ik, ak, NULL))
    return -1;

  *res_len = 8;

  /* AUTN = (SQN ^ AK) || AMF || MAC */
  for (i = 0; i < 6; i++)
    rx_sqn[i] = autn[i] ^ ak[i];

  if (memcmp(rx_sqn, sqn, 6) <= 0) {
    u8 auts_amf[2] = { 0x00, 0x00 }; /* TS 33.102 v7.0.0, 6.3.3 */

    if (milenage_f2345(opc, k, _rand, NULL, NULL, NULL, NULL, ak))
      return -1;

    for (i = 0; i < 6; i++)
      auts[i] = sqn[i] ^ ak[i];

    if (milenage_f1(opc, k, _rand, sqn, auts_amf, NULL, auts + 6))
      return -1;

    return -2;
  }

  amf = autn + 6;

  if (milenage_f1(opc, k, _rand, rx_sqn, amf, mac_a, NULL))
    return -1;

  if (memcmp(mac_a, autn + 8, 8) != 0) {
    printf( "Milenage: MAC mismatch\n");
    return -1;
  }

  return 0;
}

345
static void milenage_opc_gen(const u8 *k, const u8 *op, u8 *opc) {
Laurent's avatar
Laurent committed
346 347 348 349 350 351 352 353 354
  int i;
  /* Encrypt OP using K */
  aes_128_encrypt_block(k, op, opc);

  /* XOR the resulting Ek(OP) with OP */
  for (i = 0; i < 16; i++)
    opc[i] = opc[i] ^ op[i];
}
#endif
Laurent's avatar
Laurent committed
355
#endif