From 91f0cc481305893557f69330ec5234946508ce05 Mon Sep 17 00:00:00 2001 From: Tim Liu Date: Fri, 18 Oct 2024 08:13:38 +0000 Subject: [PATCH] refactor milenage APIs --- milenage/milenage.go | 847 +++++++++++++++--------------------- milenage/milenage_test.go | 879 +++++++++++++++++++++++++------------- 2 files changed, 923 insertions(+), 803 deletions(-) diff --git a/milenage/milenage.go b/milenage/milenage.go index 5b40c36..224df59 100644 --- a/milenage/milenage.go +++ b/milenage/milenage.go @@ -5,7 +5,6 @@ import ( "encoding/hex" "fmt" "reflect" - "strconv" "github.com/pkg/errors" ) @@ -29,187 +28,138 @@ const ( CIPHER_BLOCK_LEN = 16 ) -func GenerateOPc(k, op []uint8) (opc []uint8, err error) { - err = validateArg(k, "K", K_LEN) - if err != nil { - return nil, err - } - err = validateArg(op, "OP", OP_LEN) - if err != nil { - return nil, err - } - - return generateOPc(k, op) -} - -func GenerateOPcFromHex(k, op string) (opc string, err error) { - K, err := validateHexArg(k, "K", K_LEN) - if err != nil { - return "", err - } - OP, err := validateHexArg(op, "OP", OP_LEN) - if err != nil { - return "", err - } - - OPc, err := generateOPc(K, OP) - return hex.EncodeToString(OPc), err -} - -func generateOPc(k, op []uint8) ([]uint8, error) { - block, err := aes.NewCipher(k) - if err != nil { - return nil, err - } - - opc := make([]byte, block.BlockSize()) - - block.Encrypt(opc, op) - - for i := 0; i < OPC_LEN; i++ { - opc[i] ^= op[i] - } - - return opc, nil -} - -type ParameterLengthError struct { - Name string - Exact int - Expected int -} - -func (e *ParameterLengthError) Error() string { - return fmt.Sprintf("parameter[%s] length should be %d byte(s), not %d byte(s)", - e.Name, e.Expected, e.Exact) -} - -// validateArg that validate the args is a valid length -func validateArg(arg []byte, argName string, expectedLen int) error { - if len(arg) != expectedLen { - return &ParameterLengthError{Name: argName, Exact: len(arg), Expected: expectedLen} - } - return nil -} +// rotate parameter +const ( + r1 = 8 + r3 = 4 + r4 = 8 + r5 = 12 +) -func validateHexArg(hexArg string, argName string, expectedLen int) ([]byte, error) { - arg, err := hex.DecodeString(hexArg) - if err != nil { - return nil, errors.Wrapf(err, "decode arg[%s]=[%s] fail", argName, hexArg) - } - if len(arg) != expectedLen { - return nil, &ParameterLengthError{Name: argName, Exact: len(arg), Expected: expectedLen} - } - return arg, nil -} +// NOTE: The block cipher selected is Rijndael Algorithm with 128-bit block size (AES) in MILENAGE algorithm. /** - * milenage_f1 - Milenage f1 and f1* algorithms + * 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: 0 on success, -1 on failure + * Returns: + * @mac_a: MAC-A = 64-bit network authentication code + * @mac_s: MAC-S = 64-bit resync authentication code */ -func milenageF1(opc, k, _rand, sqn, amf, mac_a, mac_s []uint8) error { - tmp2, tmp3 := make([]uint8, 16), make([]uint8, 16) - // var tmp1, tmp2, tmp3 [16]uint8 - - rijndaelInput := make([]uint8, 16) +func f1(opc, k, _rand, sqn, amf []byte) (mac_a, mac_s []byte, err error) { + mac_a, mac_s = make([]byte, MAC_LEN), make([]byte, MAC_LEN) + rijndaelInput := make([]uint8, CIPHER_BLOCK_LEN) /* tmp1 = TEMP = E_K(RAND XOR OP_C) */ - for i := 0; i < 16; i++ { + for i := 0; i < CIPHER_BLOCK_LEN; i++ { rijndaelInput[i] = _rand[i] ^ opc[i] } - // RijndaelEncrypt( OP, op_c ); block, err := aes.NewCipher(k) if err != nil { - return err + return nil, nil, err } tmp1 := make([]byte, block.BlockSize()) block.Encrypt(tmp1, rijndaelInput) - // fmt.Printf("tmp1: %x\n", tmp1) - - /* tmp2 = IN1 = SQN || AMF || SQN || AMF */ + /* tmp2 = IN1 = SQN || AMF || SQN */ + tmp2 := make([]uint8, CIPHER_BLOCK_LEN) copy(tmp2[0:], sqn[0:6]) copy(tmp2[6:], amf[0:2]) copy(tmp2[8:], tmp2[0:8]) - /* - os_memcpy(tmp2, sqn, 6); - os_memcpy(tmp2 + 6, amf, 2); - os_memcpy(tmp2 + 8, tmp2, 8); - */ /* OUT1 = E_K(TEMP XOR rot(IN1 XOR OP_C, r1) XOR c1) XOR OP_C */ + tmp3 := make([]uint8, CIPHER_BLOCK_LEN) /* rotate (tmp2 XOR OP_C) by r1 (= 0x40 = 8 bytes) */ - for i := 0; i < 16; i++ { - tmp3[(i+8)%16] = tmp2[i] ^ opc[i] + for i := 0; i < CIPHER_BLOCK_LEN; i++ { + tmp3[(i+(CIPHER_BLOCK_LEN-r1))%CIPHER_BLOCK_LEN] = tmp2[i] ^ opc[i] } - // fmt.Printf("tmp3: %x\n", tmp3) - /* XOR with TEMP = E_K(RAND XOR OP_C) */ - for i := 0; i < 16; i++ { + for i := 0; i < CIPHER_BLOCK_LEN; i++ { tmp3[i] ^= tmp1[i] } - // fmt.Printf("tmp3 XOR with TEMP: %x\n", tmp3) /* XOR with c1 (= ..00, i.e., NOP) */ /* f1 || f1* = E_K(tmp3) XOR OP_c */ - tmp1 = make([]byte, block.BlockSize()) + tmp1 = make([]byte, CIPHER_BLOCK_LEN) block.Encrypt(tmp1, tmp3) - // fmt.Printf("XOR with c1 (: %x\n", tmp1) - - for i := 0; i < 16; i++ { + for i := 0; i < CIPHER_BLOCK_LEN; i++ { tmp1[i] ^= opc[i] } - // fmt.Printf("tmp1[i] ^= opc[i] %x\n", tmp1) - if mac_a != nil { - copy(mac_a[0:], tmp1[0:8]) + + copy(mac_a[0:], tmp1[0:8]) + copy(mac_s[0:], tmp1[8:16]) + + return mac_a, mac_s, nil +} + +/* AUTS = (SQNms ^ AK-S) || MAC-S */ +func CutAUTS(auts []byte) ([]byte, []byte) { + if len(auts) != AUTS_LEN { + return nil, nil } + concSQNms := make([]byte, SQN_LEN) + macS := make([]byte, MAC_LEN) - if mac_s != nil { - copy(mac_s[0:], tmp1[8:16]) + copy(concSQNms, auts[0:SQN_LEN]) + copy(macS, auts[SQN_LEN:SQN_LEN+MAC_LEN]) + + return concSQNms, macS +} + +/* AUTN = (SQNms ^ AK) || AMF || MAC-A */ +func CutAUTN(autn []byte) ([]byte, []byte, []byte) { + if len(autn) != AUTN_LEN { + return nil, nil, nil } + SQN := make([]byte, SQN_LEN) + AMF := make([]byte, AMF_LEN) + MAC := make([]byte, MAC_LEN) - return nil + copy(SQN, autn[0:SQN_LEN]) + copy(AMF, autn[SQN_LEN:SQN_LEN+AMF_LEN]) + copy(MAC, autn[SQN_LEN+AMF_LEN:SQN_LEN+AMF_LEN+MAC_LEN]) + + return SQN, AMF, MAC } /** - * milenage_f2345 - Milenage f2, f3, f4, f5, f5* algorithms + * 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: 0 on success, -1 on failure + * Returns: + * @res: RES = 64-bit signed response (f2) + * @ck: CK = 128-bit confidentiality key (f3) + * @ik: IK = 128-bit integrity key (f4) + * @ak: AK = 48-bit anonymity key (f5) + * @akstar: AK = 48-bit anonymity key (f5*) */ -func milenageF2345(opc, k, _rand, res, ck, ik, ak, akstar []uint8) error { - tmp1 := make([]uint8, 16) +func f2345(opc, k, _rand []uint8) (res, ck, ik, ak, akstar []byte, err error) { + res = make([]byte, RES_LEN) + ck, ik = make([]byte, CK_LEN), make([]byte, IK_LEN) + ak, akstar = make([]byte, AK_LEN), make([]byte, AK_LEN) /* tmp2 = TEMP = E_K(RAND XOR OP_C) */ - for i := 0; i < 16; i++ { + tmp1 := make([]uint8, CIPHER_BLOCK_LEN) + for i := 0; i < CIPHER_BLOCK_LEN; i++ { tmp1[i] = _rand[i] ^ opc[i] } block, err := aes.NewCipher(k) if err != nil { - return err + return nil, nil, nil, nil, nil, err } - tmp2 := make([]byte, block.BlockSize()) + tmp2 := make([]byte, CIPHER_BLOCK_LEN) block.Encrypt(tmp2, tmp1) /* OUT2 = E_K(rot(TEMP XOR OP_C, r2) XOR c2) XOR OP_C */ @@ -219,203 +169,65 @@ func milenageF2345(opc, k, _rand, res, ck, ik, ak, akstar []uint8) error { /* f2 and f5 */ /* rotate by r2 (= 0, i.e., NOP) */ - for i := 0; i < 16; i++ { + for i := 0; i < CIPHER_BLOCK_LEN; i++ { tmp1[i] = tmp2[i] ^ opc[i] } tmp1[15] ^= 1 // XOR c2 (= ..01) - /* - 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 */ tmp3 := make([]byte, block.BlockSize()) block.Encrypt(tmp3, tmp1) - for i := 0; i < 16; i++ { + for i := 0; i < CIPHER_BLOCK_LEN; i++ { tmp3[i] ^= opc[i] } - if res != nil { - copy(res[0:], tmp3[8:16]) // f2 - } + /* f2 */ + copy(res[0:], tmp3[8:16]) - if ak != nil { - copy(ak[0:], tmp3[0:6]) // f5 - } - /* - if (aes_128_encrypt_block(k, tmp1, tmp3)) - return -1; - for (i = 0; i < 16; i++) - tmp3[i] ^= opc[i]; - if (res) - os_memcpy(res, tmp3 + 8, 8); // f2 - if (ak) - os_memcpy(ak, tmp3, 6); // f5 - */ + /* f5 */ + copy(ak[0:], tmp3[0:6]) /* f3 */ - if ck != nil { - // 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) - - block.Encrypt(ck, tmp1) - - for i := 0; i < 16; i++ { - ck[i] ^= opc[i] - } - } - /* - 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) - if (aes_128_encrypt_block(k, tmp1, ck)) - return -1; - for (i = 0; i < 16; i++) - ck[i] ^= opc[i]; - } - */ - - /* f4 */ - if ik != nil { - // 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) - - block.Encrypt(ik, tmp1) - - for i := 0; i < 16; i++ { - ik[i] ^= opc[i] - } - } - /* - 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) - if (aes_128_encrypt_block(k, tmp1, ik)) - return -1; - for (i = 0; i < 16; i++) - ik[i] ^= opc[i]; - } - */ + // rotate by r3 = 0x20 = 4 bytes + for i := 0; i < CIPHER_BLOCK_LEN; i++ { + tmp1[(i+(CIPHER_BLOCK_LEN-r3))%CIPHER_BLOCK_LEN] = tmp2[i] ^ opc[i] + } + tmp1[15] ^= 2 // XOR c3 (= ..02) - /* f5* */ - if akstar != nil { - // 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) - - block.Encrypt(tmp1, tmp1) - - for i := 0; i < 6; i++ { - akstar[i] = tmp1[i] ^ opc[i] - } - } - /* - 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) - if (aes_128_encrypt_block(k, tmp1, tmp1)) - return -1; - for (i = 0; i < 6; i++) - akstar[i] = tmp1[i] ^ opc[i]; - } - */ + block.Encrypt(ck, tmp1) - return nil -} + for i := 0; i < CIPHER_BLOCK_LEN; i++ { + ck[i] ^= opc[i] + } -func MilenageGenerate(opc, amf, k, sqn, _rand, autn, ik, ck, ak, res []uint8, res_len *uint) { - // var i int - mac_a := make([]uint8, 8) + /* f4 */ + // rotate by r4 = 0x40 = 8 bytes + for i := 0; i < IK_LEN; i++ { + tmp1[(i+(CIPHER_BLOCK_LEN-r4))%CIPHER_BLOCK_LEN] = tmp2[i] ^ opc[i] + } + tmp1[15] ^= 4 // XOR c4 (= ..04) - // fmt.Println(i) - // fmt.Println(mac_a) + block.Encrypt(ik, tmp1) - if (*res_len) < 8 { - *res_len = 0 - return + for i := 0; i < IK_LEN; i++ { + ik[i] ^= opc[i] } - if milenageF1(opc, k, _rand, sqn, amf, mac_a, nil) != nil || - milenageF2345(opc, k, _rand, res, ck, ik, ak, nil) != nil { - *res_len = 0 - return + /* f5* */ + // rotate by r5 = 0x60 = 12 bytes + for i := 0; i < CIPHER_BLOCK_LEN; i++ { + tmp1[(i+(CIPHER_BLOCK_LEN-r5))%CIPHER_BLOCK_LEN] = tmp2[i] ^ opc[i] } + tmp1[15] ^= 8 // XOR c5 (= ..08) - *res_len = 8 + block.Encrypt(tmp1, tmp1) - /* AUTN = (SQN ^ AK) || AMF || MAC */ - for i := 0; i < 6; i++ { - autn[i] = sqn[i] ^ ak[i] - copy(autn[6:], amf[0:2]) - copy(autn[8:], mac_a[0:8]) + for i := 0; i < AK_LEN; i++ { + akstar[i] = tmp1[i] ^ opc[i] } - /* - for (i = 0; i < 6; i++) - autn[i] = sqn[i] ^ ak[i]; - os_memcpy(autn + 6, amf, 2); - os_memcpy(autn + 8, mac_a, 8); - */ -} -/** - * 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 - */ -//int milenage_auts(const c_uint8_t *opc, const c_uint8_t *k, const c_uint8_t *_rand, -// const c_uint8_t *auts, c_uint8_t *sqn) -func Milenage_auts(opc, k, _rand, auts, sqn []uint8) int { - amf := []uint8{0x00, 0x00} // TS 33.102 v7.0.0, 6.3.3 - ak := make([]uint8, 6) - mac_s := make([]uint8, 8) - /* - c_uint8_t amf[2] = { 0x00, 0x00 }; // TS 33.102 v7.0.0, 6.3.3 - c_uint8_t ak[6], mac_s[8]; - int i; - */ - - if milenageF2345(opc, k, _rand, nil, nil, nil, nil, ak) != nil { - return -1 - } - - for i := 0; i < 6; i++ { - sqn[i] = auts[i] ^ ak[i] - } - - if milenageF1(opc, k, _rand, sqn, amf, nil, mac_s) != nil || !reflect.DeepEqual(mac_s, auts[6:14]) { - return -1 - } - - /* - if (milenage_f2345(opc, k, _rand, NULL, NULL, NULL, NULL, ak)) - return -1; - for (i = 0; i < 6; i++) - sqn[i] = auts[i] ^ ak[i]; - if (milenage_f1(opc, k, _rand, sqn, amf, NULL, mac_s) || - os_memcmp_const(mac_s, auts + 6, 8) != 0) - return -1; - */ - return 0 + return res, ck, ik, ak, akstar, nil } /** @@ -427,276 +239,309 @@ func Milenage_auts(opc, k, _rand, auts, sqn []uint8) int { * @kc: Buffer for Kc = 64-bit Kc * Returns: 0 on success, -1 on failure */ -func Gsm_milenage(opc, k, _rand, sres, kc []uint8) int { - res, ck, ik := make([]uint8, 8), make([]uint8, 16), make([]uint8, 16) - - if milenageF2345(opc, k, _rand, res, ck, ik, nil, nil) != nil { - return -1 - } - /* - if (milenage_f2345(opc, k, _rand, res, ck, ik, NULL, NULL)) - return -1; - */ - - for i := 0; i < 8; i++ { - kc[i] = ck[i] ^ ck[i+8] ^ ik[i] ^ ik[i+8] - } - /* - for (i = 0; i < 8; i++) - kc[i] = ck[i] ^ ck[i + 8] ^ ik[i] ^ ik[i + 8]; - */ - - // if GSM_MILENAGE_ALT_SRES - // copy(sres, res[0:4]) - - // if not GSM_MILENAGE_ALT_SRES - for i := 0; i < 4; i++ { - sres[i] = res[i] ^ res[i+4] - } - /* - #ifdef GSM_MILENAGE_ALT_SRES - os_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 0 -} /** - * 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 +* generateAKAParameters - Generate AKA AUTN,IK,CK,RES +* @opc: OPc = 128-bit operator variant algorithm configuration field (encr.) +* @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 +* Returns: +* @ik: IK = 128-bit integrity key (f4) +* @ck: CK = 128-bit confidentiality key (f3) +* @res: RES = 64-bit signed response (f2) +* @autn: AUTN = 128-bit authentication token +* @err: errors */ -func Milenage_check(opc, k, sqn, _rand, autn, ik, ck, res []uint8, res_len *uint, auts []uint8) int { - mac_a, ak, rx_sqn := make([]uint8, 8), make([]uint8, 6), make([]uint8, 6) - var amf []uint8 - - // fmt.Println(mac_a, amf) - - /* TODO - d_trace(1, "Milenage: AUTN\n"); d_trace_hex(1, autn, 16); - d_trace(1, "Milenage: RAND\n"); d_trace_hex(1, _rand, 16); - */ +func generateAKAParameters(opc, k, rand, sqn, amf []byte) (ik, ck, xres, autn []byte, err error) { + mac, _, err := f1(opc, k, rand, sqn, amf) + if err != nil { + err = errors.Wrap(err, "calculate F1 failed") + return nil, nil, nil, nil, err + } - if milenageF2345(opc, k, _rand, res, ck, ik, ak, nil) != nil { - return -1 + xres, ck, ik, ak, _, err := f2345(opc, k, rand) + if err != nil { + err = errors.Wrap(err, "calculate F2345 failed") + return nil, nil, nil, nil, err } - /* - if (milenage_f2345(opc, k, _rand, res, ck, ik, ak, NULL)) - return -1; - */ - *res_len = 8 - /* TODO - d_trace(1, "Milenage: RES\n"); d_trace_hex(1, res, *res_len); - d_trace(1, "Milenage: CK\n"); d_trace_hex(1, ck, 16); - d_trace(1, "Milenage: IK\n"); d_trace_hex(1, ik, 16); - d_trace(1, "Milenage: AK\n"); d_trace_hex(1, ak, 6); - */ + consSQNhe := xor(sqn, ak) + autn = append(consSQNhe, append(amf, mac...)...) - /* AUTN = (SQN ^ AK) || AMF || MAC */ - for i := 0; i < 6; i++ { - rx_sqn[i] = autn[i] ^ ak[i] - } - /* - for (i = 0; i < 6; i++) - rx_sqn[i] = autn[i] ^ ak[i]; - */ + return ik, ck, xres, autn, nil +} - // TODO d_trace(1, "Milenage: SQN\n"); d_trace_hex(1, rx_sqn, 6); +func GenerateAKAParameters(opc, k, rand, sqn, amf []byte) (ik, ck, xres, autn []byte, err error) { + err = validateArg(opc, "OPc", OPC_LEN) + if err != nil { + return nil, nil, nil, nil, err + } + err = validateArg(k, "K", K_LEN) + if err != nil { + return nil, nil, nil, nil, err + } + err = validateArg(rand, "RAND", RAND_LEN) + if err != nil { + return nil, nil, nil, nil, err + } + err = validateArg(sqn, "SQN", SQN_LEN) + if err != nil { + return nil, nil, nil, nil, err + } + err = validateArg(amf, "AMF", AMF_LEN) + if err != nil { + return nil, nil, nil, nil, err + } + return generateAKAParameters(opc, k, rand, sqn, amf) +} - if os_memcmp(rx_sqn, sqn, 6) <= 0 { - auts_amf := []uint8{0x00, 0x00} // TS 33.102 v7.0.0, 6.3.3 +/** +* generateKeysWithAUTN - Generate AKA AK,IK,CK,RES from AUTN +* @opc: OPc = 128-bit operator variant algorithm configuration field (encr.) +* @k: K = 128-bit subscriber key +* @rand: RAND = 128-bit random challenge +* @autn: AUTN = 128-bit authentication token +* Returns: +* @sqnhe: SQN = 48-bit sequence number from HM +* @ak: AK = 48-bit anonymity key (f5) +* @ik: IK = 128-bit integrity key (f4) +* @ck: CK = 128-bit confidentiality key (f3) +* @res: RES = 64-bit signed response (f2) +* @err: errors + */ +func generateKeysWithAUTN(opc, k, rand, autn []byte) (sqnhe, ak, ik, ck, res []byte, err error) { + res, ck, ik, ak, _, err = f2345(opc, k, rand) + if err != nil { + err = errors.Wrap(err, "calculate F2345 failed") + return nil, nil, nil, nil, nil, err + } - if milenageF2345(opc, k, _rand, nil, nil, nil, nil, ak) != nil { - return -1 - } + concSQNhe, amf, xmac := CutAUTN(autn) - // TODO d_trace(1, "Milenage: AK*\n"); d_trace_hex(1, ak, 6); + sqnhe = xor(concSQNhe, ak) - for i := 0; i < 6; i++ { - auts[i] = sqn[i] ^ ak[i] - } + mac, _, err := f1(opc, k, rand, sqnhe, amf) + if err != nil { + err = errors.Wrap(err, "calculate F1 failed") + return nil, nil, nil, nil, nil, err + } - if milenageF1(opc, k, _rand, sqn, auts_amf, nil, auts[6:]) != nil { - return -1 - } + if !reflect.DeepEqual(xmac, mac) { + return nil, nil, nil, nil, nil, + &MACFailureError{MACName: "MAC-A", ExpectedMAC: xmac, ExactMAC: mac} + } - // TODO d_trace(1, "Milenage: AUTS*\n"); d_trace_hex(1, auts, 14); + return sqnhe, ak, ik, ck, res, nil +} - return -2 +func GenerateKeysWithAUTN(opc, k, rand, autn []byte) (sqnhe, ak, ik, ck, res []byte, err error) { + err = validateArg(opc, "OPc", OPC_LEN) + if err != nil { + return nil, nil, nil, nil, nil, err + } + err = validateArg(k, "K", K_LEN) + if err != nil { + return nil, nil, nil, nil, nil, err + } + err = validateArg(rand, "RAND", RAND_LEN) + if err != nil { + return nil, nil, nil, nil, nil, err + } + err = validateArg(autn, "AUTN", AUTN_LEN) + if err != nil { + return nil, nil, nil, nil, nil, err } - /* - if (os_memcmp(rx_sqn, sqn, 6) <= 0) { - c_uint8_t 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; - d_trace(1, "Milenage: AK*\n"); d_trace_hex(1, ak, 6); + return generateKeysWithAUTN(opc, k, rand, autn) +} +var _ error = &MACFailureError{} - 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; - d_trace(1, "Milenage: AUTS*\n"); d_trace_hex(1, auts, 14); - return -2; - } - */ +type MACFailureError struct { + MACName string + ExpectedMAC []byte + ExactMAC []byte +} - amf = autn[6:] - // TODO d_trace(1, "Milenage: AMF\n"); d_trace_hex(1, amf, 2); +func (m *MACFailureError) Error() string { + return fmt.Sprintf("X%s[%x] not match %s[%x]", m.MACName, m.ExpectedMAC, m.MACName, m.ExactMAC) +} + +// The AMF used to calculate MAC-S assumes a dummy value of all zeros +var resynchAMF = []byte{0x00, 0x00} - if milenageF1(opc, k, _rand, rx_sqn, amf, mac_a, nil) != nil { - return -1 +func validateAUTS(opc, k, rand, auts []byte) (sqnms []byte, err error) { + ConcSQNms, MACS := CutAUTS(auts) + // nolint:dogsled + _, _, _, _, AKstar, err := f2345(opc, k, rand) + if err != nil { + return nil, errors.Wrap(err, "calculate F2345 Fail") } - // TODO d_trace(1, "Milenage: MAC_A\n"); d_trace_hex(1, mac_a, 8); + SQNms := xor(ConcSQNms, AKstar) - if os_memcmp(mac_a, autn[8:], 8) != 0 { - // TODO d_trace(1, "Milenage: MAC mismatch\n"); - // TODO d_trace(1, "Milenage: Received MAC_A\n"); d_trace_hex(1, autn + 8, 8); + _, XMACS, err := f1(opc, k, rand, SQNms, resynchAMF) + if err != nil { + return nil, errors.Wrap(err, "calculate F1 Fail") + } - return -1 + if !reflect.DeepEqual(XMACS, MACS) { + return nil, &MACFailureError{MACName: "MAC-S", ExpectedMAC: XMACS, ExactMAC: MACS} } - /* - amf = autn + 6; - d_trace(1, "Milenage: AMF\n"); d_trace_hex(1, amf, 2); - if (milenage_f1(opc, k, _rand, rx_sqn, amf, mac_a, NULL)) - return -1; - d_trace(1, "Milenage: MAC_A\n"); d_trace_hex(1, mac_a, 8); + return SQNms, nil +} - if (os_memcmp_const(mac_a, autn + 8, 8) != 0) { - d_trace(1, "Milenage: MAC mismatch\n"); - d_trace(1, "Milenage: Received MAC_A\n"); d_trace_hex(1, autn + 8, 8); - return -1; - } - */ +func ValidateAUTS(opc, k, rand, auts []byte) (sqnms []byte, err error) { + err = validateArg(opc, "OPc", OPC_LEN) + if err != nil { + return nil, err + } + err = validateArg(k, "K", K_LEN) + if err != nil { + return nil, err + } + err = validateArg(rand, "RAND", RAND_LEN) + if err != nil { + return nil, err + } + err = validateArg(auts, "AUTS", AUTS_LEN) + if err != nil { + return nil, err + } - return 0 + return validateAUTS(opc, k, rand, auts) } -// implementation of os_memcmp -func os_memcmp(a, b []uint8, num int) int { - for i := 0; i < num; i++ { - if a[i] < b[i] { - return -i - } - - if a[i] > b[i] { - return i - } +func generateAUTS(opc, k, rand, sqnms []byte) (auts []byte, err error) { + var AKstar []byte + // nolint:dogsled + _, _, _, _, AKstar, err = f2345(opc, k, rand) + if err != nil { + return nil, errors.Wrap(err, "calculate f2345 Fail") } - return 0 -} + ConcSQNms := xor(sqnms, AKstar) -func F1(opc, k, _rand, sqn, amf, mac_a, mac_s []uint8) error { - return milenageF1(opc, k, _rand, sqn, amf, mac_a, mac_s) -} + _, MACS, err := f1(opc, k, rand, sqnms, resynchAMF) + if err != nil { + return nil, errors.Wrap(err, "calculate F1 Fail") + } -func F2345(opc, k, _rand, res, ck, ik, ak, akstar []uint8) error { - return milenageF2345(opc, k, _rand, res, ck, ik, ak, akstar) + AUTS := append(ConcSQNms, MACS...) + + return AUTS, nil } -func GenerateOPC(k, op []uint8) ([]uint8, error) { - block, err := aes.NewCipher(k) +func GenerateAUTS(opc, k, rand, sqnms []byte) (auts []byte, err error) { + err = validateArg(opc, "OPc", OPC_LEN) + if err != nil { + return nil, err + } + err = validateArg(k, "K", K_LEN) + if err != nil { + return nil, err + } + err = validateArg(rand, "RAND", RAND_LEN) + if err != nil { + return nil, err + } + err = validateArg(sqnms, "SQNms", SQN_LEN) if err != nil { return nil, err } - opc := make([]byte, block.BlockSize()) + return generateAUTS(opc, k, rand, sqnms) +} - block.Encrypt(opc, op) +// xor return (a xor b) (copy) +func xor(a, b []byte) []byte { + var outLen int + if len(a) > len(b) { + outLen = len(b) + } else { + outLen = len(a) + } - for i := 0; i < 16; i++ { - opc[i] ^= op[i] + out := make([]byte, outLen) + for i := 0; i < outLen; i++ { + out[i] = a[i] ^ b[i] } - return opc, nil + return out } -func InsertData(op, k, _rand, sqn, amf []uint8, OP, K, RAND, SQN, AMF string) { - var res uint64 - var err error +type ParameterLengthError struct { + Name string + Exact int + Expected int +} - // load op - // fmt.Print("OP: ") - for i := 0; i < 16; i++ { - res, err = strconv.ParseUint(OP[i*2:i*2+2], 16, 8) +func (e *ParameterLengthError) Error() string { + return fmt.Sprintf("parameter[%s] length should be %d byte(s), not %d byte(s)", + e.Name, e.Expected, e.Exact) +} - if err == nil { - op[i] = uint8(res) - // fmt.Printf("%02x ", op[i]) - } +// validateArg that validate the args is a valid length +func validateArg(arg []byte, argName string, expectedLen int) error { + if len(arg) != expectedLen { + return &ParameterLengthError{Name: argName, Exact: len(arg), Expected: expectedLen} } + return nil +} - // fmt.Println() - // fmt.Printf("OP: %x\n", op) - - // load k - // fmt.Print("K: ") - for i := 0; i < 16; i++ { - res, err = strconv.ParseUint(K[i*2:i*2+2], 16, 8) - - if err == nil { - k[i] = uint8(res) - // fmt.Printf("%02x ", k[i]) - } +func validateHexArg(hexArg string, argName string, expectedLen int) ([]byte, error) { + arg, err := hex.DecodeString(hexArg) + if err != nil { + return nil, errors.Wrapf(err, "decode arg[%s]=[%s] fail", argName, hexArg) } + if len(arg) != expectedLen { + return nil, &ParameterLengthError{Name: argName, Exact: len(arg), Expected: expectedLen} + } + return arg, nil +} - // fmt.Println() - // fmt.Printf("K: %x\n", k) +func GenerateOPc(k, op []uint8) (opc []uint8, err error) { + err = validateArg(k, "K", K_LEN) + if err != nil { + return nil, err + } + err = validateArg(op, "OP", OP_LEN) + if err != nil { + return nil, err + } - // load _rand - // fmt.Print("RAND: ") - for i := 0; i < 16; i++ { - res, err = strconv.ParseUint(RAND[i*2:i*2+2], 16, 8) + return generateOPc(k, op) +} - if err == nil { - _rand[i] = uint8(res) - // fmt.Printf("%02x ", _rand[i]) - } +func GenerateOPcFromHex(k, op string) (opc string, err error) { + K, err := validateHexArg(k, "K", K_LEN) + if err != nil { + return "", err + } + OP, err := validateHexArg(op, "OP", OP_LEN) + if err != nil { + return "", err } - // fmt.Println() - // load sqn - // fmt.Print("SQN: ") - for i := 0; i < 6; i++ { - res, err = strconv.ParseUint(SQN[i*2:i*2+2], 16, 8) + OPc, err := generateOPc(K, OP) + return hex.EncodeToString(OPc), err +} - if err == nil { - sqn[i] = uint8(res) - // fmt.Printf("%02x ", sqn[i]) - } +func generateOPc(k, op []uint8) ([]uint8, error) { + block, err := aes.NewCipher(k) + if err != nil { + return nil, err } - // fmt.Println() - // fmt.Printf("SQN: %x\n", sqn) + opc := make([]byte, block.BlockSize()) - // load amf - // fmt.Print("AMF: ") - for i := 0; i < 2; i++ { - res, err = strconv.ParseUint(AMF[i*2:i*2+2], 16, 8) + block.Encrypt(opc, op) - if err == nil { - amf[i] = uint8(res) - // fmt.Printf("%02x ", amf[i]) - } + for i := 0; i < OPC_LEN; i++ { + opc[i] ^= op[i] } - // fmt.Println() - // fmt.Printf("AMF: %x\n", amf) - // fmt.Println() + return opc, nil } diff --git a/milenage/milenage_test.go b/milenage/milenage_test.go index da7bf34..a1f6c34 100644 --- a/milenage/milenage_test.go +++ b/milenage/milenage_test.go @@ -3,12 +3,12 @@ package milenage import ( "encoding/hex" "fmt" - "reflect" - "strings" "testing" + + "github.com/stretchr/testify/require" ) -type f1Test struct { +type f1TestSet struct { K string RAND string SQN string @@ -16,10 +16,10 @@ type f1Test struct { OP string ExpectedOPc string f1 string - f1Start string + f1Star string } -type f2f5f3Test struct { +type f2f5f3TestSet struct { K string RAND string OP string @@ -29,7 +29,7 @@ type f2f5f3Test struct { ExpectedCK string } -type f4f5StarTest struct { +type f4f5StarTestSet struct { K string RAND string OP string @@ -38,8 +38,25 @@ type f4f5StarTest struct { ExpectedAKStar string } -func TestF1Test35207(t *testing.T) { - Testf1TestTable := []f1Test{ +type ConformanceTestSet struct { + K string + RAND string + SQN string + AMF string + OP string + ExpectedOPc string + ExpectedMACA string // f1 + ExpectedMACS string // f1* + ExpectedRES string // f2 + ExpectedCK string // f3 + ExpectedIK string // f4 + ExpectedAK string // f5 + ExpectedAKStar string // f5* +} + +// TestF1ImplementorsDataSet according TS35.207 Test Set +func TestF1ImplementorsDataSet(t *testing.T) { + f1TestCases := []f1TestSet{ { K: "465b5ce8b199b49faa5f0a2ee238a6bc", RAND: "23553cbe9637a89d218ae64dae47bf35", @@ -48,7 +65,7 @@ func TestF1Test35207(t *testing.T) { OP: "cdc202d5123e20f62b6d676ac72cb318", ExpectedOPc: "cd63cb71954a9f4e48a5994e37a02baf", f1: "4a9ffac354dfafb3", - f1Start: "01cfaf9ec4e871e9", + f1Star: "01cfaf9ec4e871e9", }, { K: "0396eb317b6d1c36f19c1c84cd6ffd16", @@ -58,7 +75,7 @@ func TestF1Test35207(t *testing.T) { OP: "ff53bade17df5d4e793073ce9d7579fa", ExpectedOPc: "53c15671c60a4b731c55b4a441c0bde2", f1: "5df5b31807e258b0", - f1Start: "a8c016e51ef4a343", + f1Star: "a8c016e51ef4a343", }, { K: "fec86ba6eb707ed08905757b1bb44b8f", @@ -68,7 +85,7 @@ func TestF1Test35207(t *testing.T) { OP: "dbc59adcb6f9a0ef735477b7fadf8374", ExpectedOPc: "1006020f0a478bf6b699f15c062e42b3", f1: "9cabc3e99baf7281", - f1Start: "95814ba2b3044324", + f1Star: "95814ba2b3044324", }, { K: "9e5944aea94b81165c82fbf9f32db751", @@ -78,7 +95,7 @@ func TestF1Test35207(t *testing.T) { OP: "223014c5806694c007ca1eeef57f004f", ExpectedOPc: "a64a507ae1a2a98bb88eb4210135dc87", f1: "74a58220cba84c49", - f1Start: "ac2cc74a96871837", + f1Star: "ac2cc74a96871837", }, { K: "4ab1deb05ca6ceb051fc98e77d026a84", @@ -88,7 +105,7 @@ func TestF1Test35207(t *testing.T) { OP: "2d16c5cd1fdf6b22383584e3bef2a8d8", ExpectedOPc: "dcf07cbd51855290b92a07a9891e523e", f1: "49e785dd12626ef2", - f1Start: "9e85790336bb3fa2", + f1Star: "9e85790336bb3fa2", }, { K: "6c38a116ac280c454f59332ee35c8c4f", @@ -98,70 +115,37 @@ func TestF1Test35207(t *testing.T) { OP: "1ba00a1a7c6700ac8c3ff3e96ad08725", ExpectedOPc: "3803ef5363b947c6aaa225e58fae3934", f1: "078adfb488241a57", - f1Start: "80246b8d0186bcf1", + f1Star: "80246b8d0186bcf1", }, } - for i, testTable := range Testf1TestTable { - K, err := hex.DecodeString(strings.Repeat(testTable.K, 1)) - if err != nil { - t.Errorf("err: %+v\n", err) - } - RAND, err := hex.DecodeString(strings.Repeat(testTable.RAND, 1)) - if err != nil { - t.Errorf("err: %+v\n", err) - } - SQN, err := hex.DecodeString(strings.Repeat(testTable.SQN, 1)) - if err != nil { - t.Errorf("err: %+v\n", err) - } - AMF, err := hex.DecodeString(strings.Repeat(testTable.AMF, 1)) - if err != nil { - t.Errorf("err: %+v\n", err) - } - OP, err := hex.DecodeString(strings.Repeat(testTable.OP, 1)) - if err != nil { - t.Errorf("err: %+v\n", err) - } - ExpectedOPc, err := hex.DecodeString(strings.Repeat(testTable.ExpectedOPc, 1)) - if err != nil { - t.Errorf("err: %+v\n", err) - } - f1, err := hex.DecodeString(strings.Repeat(testTable.f1, 1)) - if err != nil { - t.Errorf("err: %+v\n", err) - } - f1Start, err := hex.DecodeString(strings.Repeat(testTable.f1Start, 1)) - if err != nil { - t.Errorf("err: %+v\n", err) - } - - OPC, err := GenerateOPC(K, OP) - if err != nil { - t.Errorf("err: %+v\n", err) - } - - fmt.Printf("K=%x\nSQN=%x\nOP=%x\nOPC=%x\n", K, SQN, OP, OPC) - if !reflect.DeepEqual(OPC, ExpectedOPc) { - t.Errorf("Testf1Test35207[%d] \t OPC[0x%x] \t ExpectedOPc[0x%x]\n", i, OPC, ExpectedOPc) - } - MAC_A, MAC_S := make([]byte, 8), make([]byte, 8) - err = F1(OPC, K, RAND, SQN, AMF, MAC_A, MAC_S) - if err != nil { - t.Errorf("err: %+v\n", err) - } - - if !reflect.DeepEqual(MAC_A, f1) { - t.Errorf("Testf1Test35207[%d] \t MAC_A[0x%x] \t f1[0x%x]\n", i, MAC_A, f1) - } - if !reflect.DeepEqual(MAC_S, f1Start) { - t.Errorf("Testf1Test35207[%d] \t MAC_S[0x%x] \t f1Start[0x%x]\n", i, MAC_S, f1Start) - } + for i, tc := range f1TestCases { + t.Run(fmt.Sprintf("Set_%d", i+1), func(t *testing.T) { + K, err := hex.DecodeString(tc.K) + require.NoError(t, err, "decode K fail") + RAND, err := hex.DecodeString(tc.RAND) + require.NoError(t, err, "decode RAND fail") + SQN, err := hex.DecodeString(tc.SQN) + require.NoError(t, err, "decode SQN fail") + AMF, err := hex.DecodeString(tc.AMF) + require.NoError(t, err, "decode AMF fail") + OP, err := hex.DecodeString(tc.OP) + require.NoError(t, err, "decode OP fail") + + OPC, err := GenerateOPc(K, OP) + require.NoError(t, err, "calculate OPc fail") + require.Equal(t, tc.ExpectedOPc, hex.EncodeToString(OPC)) + MAC_A, MAC_S, err := f1(OPC, K, RAND, SQN, AMF) + require.NoError(t, err, "calculate F1 fail") + + require.Equal(t, tc.f1, hex.EncodeToString(MAC_A)) + require.Equal(t, tc.f1Star, hex.EncodeToString(MAC_S)) + }) } } -func TestF2F5F3Test35207(t *testing.T) { - Testf2f5f3TestTable := []f2f5f3Test{ +func TestF2F5F3ImplementorsDataSet(t *testing.T) { + f2f5f3TestCases := []f2f5f3TestSet{ { K: "465b5ce8b199b49faa5f0a2ee238a6bc", RAND: "23553cbe9637a89d218ae64dae47bf35", @@ -207,67 +191,41 @@ func TestF2F5F3Test35207(t *testing.T) { ExpectedAK: "31e11a609118", ExpectedCK: "7657766b373d1c2138f307e3de9242f9", }, + { + K: "6c38a116ac280c454f59332ee35c8c4f", + RAND: "ee6466bc96202c5a557abbeff8babf63", + OP: "1ba00a1a7c6700ac8c3ff3e96ad08725", + ExpectedOPc: "3803ef5363b947c6aaa225e58fae3934", + ExpectedRES: "16c8233f05a0ac28", + ExpectedAK: "45b0f69ab06c", + ExpectedCK: "3f8c7587fe8e4b233af676aede30ba3b", + }, } - for i, testTable := range Testf2f5f3TestTable { - K, err := hex.DecodeString(strings.Repeat(testTable.K, 1)) - if err != nil { - t.Errorf("err: %+v\n", err) - } - OP, err := hex.DecodeString(strings.Repeat(testTable.OP, 1)) - if err != nil { - t.Errorf("err: %+v\n", err) - } - ExpectedOPc, err := hex.DecodeString(strings.Repeat(testTable.ExpectedOPc, 1)) - if err != nil { - t.Errorf("err: %+v\n", err) - } - ExpectedRES, err := hex.DecodeString(strings.Repeat(testTable.ExpectedRES, 1)) - if err != nil { - t.Errorf("err: %+v\n", err) - } - ExpectedAK, err := hex.DecodeString(strings.Repeat(testTable.ExpectedAK, 1)) - if err != nil { - t.Errorf("err: %+v\n", err) - } - ExpectedCK, err := hex.DecodeString(strings.Repeat(testTable.ExpectedCK, 1)) - if err != nil { - t.Errorf("err: %+v\n", err) - } - RAND, err := hex.DecodeString(strings.Repeat(testTable.RAND, 1)) - if err != nil { - t.Errorf("err: %+v\n", err) - } - - OPC, err := GenerateOPC(K, OP) - if err != nil { - t.Errorf("err: %+v\n", err) - } - - if !reflect.DeepEqual(OPC, ExpectedOPc) { - t.Errorf("TestF2F5F3Test35207[%d] \t OPC[0x%x] \t ExpectedOPc[0x%x]\n", i, OPC, ExpectedOPc) - } - CK, IK := make([]byte, 16), make([]byte, 16) - RES := make([]byte, 8) - AK, AKstar := make([]byte, 6), make([]byte, 6) - err = F2345(OPC, K, RAND, RES, CK, IK, AK, AKstar) - if err != nil { - t.Errorf("err: %+v\n", err) - } - if !reflect.DeepEqual(RES, ExpectedRES) { - t.Errorf("TestF2F5F3Test35207[%d] \t RES[0x%x] \t ExpectedRES[0x%x]\n", i, RES, ExpectedRES) - } - if !reflect.DeepEqual(AK, ExpectedAK) { - t.Errorf("TestF2F5F3Test35207[%d] \t AK[0x%x] \t ExpectedAK[0x%x]\n", i, AK, ExpectedAK) - } - if !reflect.DeepEqual(CK, ExpectedCK) { - t.Errorf("TestF2F5F3Test35207[%d] \t CK[0x%x] \t ExpectedCK[0x%x]\n", i, CK, ExpectedCK) - } + for i, tc := range f2f5f3TestCases { + t.Run(fmt.Sprintf("Set_%d", i+1), func(t *testing.T) { + K, err := hex.DecodeString(tc.K) + require.NoError(t, err, "decode K fail") + RAND, err := hex.DecodeString(tc.RAND) + require.NoError(t, err, "decode RAND fail") + OP, err := hex.DecodeString(tc.OP) + require.NoError(t, err, "decode OP fail") + + OPc, err := GenerateOPc(K, OP) + require.NoError(t, err, "calculate OPc fail") + require.Equal(t, tc.ExpectedOPc, hex.EncodeToString(OPc)) + + RES, CK, _, AK, _, err := f2345(OPc, K, RAND) + require.NoError(t, err, "calculate F2345 fail") + require.Equal(t, tc.ExpectedAK, hex.EncodeToString(AK)) + require.Equal(t, tc.ExpectedCK, hex.EncodeToString(CK)) + require.Equal(t, tc.ExpectedRES, hex.EncodeToString(RES)) + }) } } -func TestF4F5StarTest35207(t *testing.T) { - Testf4f5StarTestTable := []f4f5StarTest{ +func TestF4F5StarImplementorsDataSet(t *testing.T) { + f4f5StarTestCases := []f4f5StarTestSet{ { K: "465b5ce8b199b49faa5f0a2ee238a6bc", RAND: "23553cbe9637a89d218ae64dae47bf35", @@ -318,203 +276,520 @@ func TestF4F5StarTest35207(t *testing.T) { }, } - for i, testTable := range Testf4f5StarTestTable { - K, err := hex.DecodeString(strings.Repeat(testTable.K, 1)) - if err != nil { - t.Errorf("err: %+v\n", err) - } - OP, err := hex.DecodeString(strings.Repeat(testTable.OP, 1)) - if err != nil { - t.Errorf("err: %+v\n", err) - } - ExpectedOPc, err := hex.DecodeString(strings.Repeat(testTable.ExpectedOPc, 1)) - if err != nil { - t.Errorf("err: %+v\n", err) - } - - ExpectedAKStar, err := hex.DecodeString(strings.Repeat(testTable.ExpectedAKStar, 1)) - if err != nil { - t.Errorf("err: %+v\n", err) - } - ExpectedIK, err := hex.DecodeString(strings.Repeat(testTable.ExpectedIK, 1)) - if err != nil { - t.Errorf("err: %+v\n", err) - } - RAND, err := hex.DecodeString(strings.Repeat(testTable.RAND, 1)) - if err != nil { - t.Errorf("err: %+v\n", err) - } - - OPC, err := GenerateOPC(K, OP) - if err != nil { - t.Errorf("err: %+v\n", err) - } - - if !reflect.DeepEqual(OPC, ExpectedOPc) { - t.Errorf("TestF4F5StarTest35207[%d] \t OPC[0x%x] \t ExpectedOPc[0x%x]\n", i, OPC, ExpectedOPc) - } - CK, IK := make([]byte, 16), make([]byte, 16) - RES := make([]byte, 8) - AK, AKstar := make([]byte, 6), make([]byte, 6) - err = F2345(OPC, K, RAND, RES, CK, IK, AK, AKstar) - if err != nil { - t.Errorf("err: %+v\n", err) - } - if !reflect.DeepEqual(AKstar, ExpectedAKStar) { - t.Errorf("TestF4F5StarTest35207[%d] \t AKstar[0x%x] \t ExpectedAKStar[0x%x]\n", i, AKstar, ExpectedAKStar) - } - if !reflect.DeepEqual(IK, ExpectedIK) { - t.Errorf("TestF4F5StarTest35207[%d] \t IK[0x%x] \t ExpectedIK[0x%x]\n", i, IK, ExpectedIK) - } + for i, tc := range f4f5StarTestCases { + t.Run(fmt.Sprintf("Set_%d", i+1), func(t *testing.T) { + K, err := hex.DecodeString(tc.K) + require.NoError(t, err, "decode K fail") + RAND, err := hex.DecodeString(tc.RAND) + require.NoError(t, err, "decode RAND fail") + OP, err := hex.DecodeString(tc.OP) + require.NoError(t, err, "decode OP fail") + + OPc, err := GenerateOPc(K, OP) + require.NoError(t, err, "calculate OPc fail") + require.Equal(t, tc.ExpectedOPc, hex.EncodeToString(OPc)) + + _, _, IK, _, AKstar, err := f2345(OPc, K, RAND) + require.NoError(t, err, "calculate F2345 fail") + require.Equal(t, tc.ExpectedAKStar, hex.EncodeToString(AKstar)) + require.Equal(t, tc.ExpectedIK, hex.EncodeToString(IK)) + }) } } func TestGenerateOPC(t *testing.T) { - // K_str := "3016ebeae2c45bd0060923dbbb402be6" - K_str := "000102030405060708090a0b0c0d0e0f" // CHT - K, err := hex.DecodeString(K_str) - if err != nil { - t.Errorf("err: %+v\n", err) + testCases := []struct { + inOP string + inK string + expectedOPc string + }{ + { // CHT case + inOP: "00112233445566778899aabbccddeeff", + inK: "000102030405060708090a0b0c0d0e0f", + expectedOPc: "69d5c2eb2e2e624750541d3bbc692ba5", + }, + { // Landslide Re-synch + inOP: "63bfa50ee6523365ff14c1f45f88737d", + inK: "00000000000000000000000000000000", + expectedOPc: "c8ffd2aa7a43c926bf2b2826205b9030", + }, } - // OP_str := "00000000000000000000000000000000" - OP_str := "00112233445566778899aabbccddeeff" // CHT - OP, err := hex.DecodeString(OP_str) - if err != nil { - t.Errorf("err: %+v\n", err) - } - fmt.Println("K:", K) + for tcIdx, tc := range testCases { + t.Run(fmt.Sprintf("Case_%d", tcIdx), func(t *testing.T) { + K, err := hex.DecodeString(tc.inK) + require.NoError(t, err, "decode inK fail") - fmt.Println("OP:", OP) + OP, err := hex.DecodeString(tc.inOP) + require.NoError(t, err, "decode inOP fail") - OPCbyGo, err := GenerateOPC(K, OP) - if err != nil { - t.Errorf("err: %+v\n", err) - } + OPc, err := GenerateOPc(K, OP) + require.NoError(t, err, "GenerateOPC fail") - fmt.Println("OPCbyGo:", OPCbyGo) + require.Equal(t, tc.expectedOPc, hex.EncodeToString(OPc)) + }) + } } -func TestRAND(t *testing.T) { - /* - K, RAND, CK, IK: 128 bits (16 bytes) (hex len = 32) - SQN, AK: 48 bits (6 bytes) (hex len = 12) TS33.102 - 6.3.2 - AMF: 16 bits (2 bytes) (hex len = 4) TS33.102 - Annex H - */ - - K_str := "5122250214c33e723a5dd523fc145fc0" - OP_str := "c9e8763286b5b9ffbdf56e1297d0887b" - SQN_str := "16f3b3f70fc2" - - K, err := hex.DecodeString(K_str) - if err != nil { - t.Errorf("err: %+v\n", err) - } - OP, err := hex.DecodeString(OP_str) - if err != nil { - t.Errorf("err: %+v\n", err) - } - SQN, err := hex.DecodeString(SQN_str) - if err != nil { - t.Errorf("err: %+v\n", err) +// TestConformanceTestSet from TS 35.208 +func TestConformanceTestSet(t *testing.T) { + conformanceTestCases := []ConformanceTestSet{ + { // Test Set 1 + K: "465b5ce8b199b49faa5f0a2ee238a6bc", + RAND: "23553cbe9637a89d218ae64dae47bf35", + SQN: "ff9bb4d0b607", + AMF: "b9b9", + OP: "cdc202d5123e20f62b6d676ac72cb318", + ExpectedOPc: "cd63cb71954a9f4e48a5994e37a02baf", + ExpectedMACA: "4a9ffac354dfafb3", + ExpectedMACS: "01cfaf9ec4e871e9", + ExpectedRES: "a54211d5e3ba50bf", + ExpectedCK: "b40ba9a3c58b2a05bbf0d987b21bf8cb", + ExpectedIK: "f769bcd751044604127672711c6d3441", + ExpectedAK: "aa689c648370", + ExpectedAKStar: "451e8beca43b", + }, + { // Test Set 3 + K: "fec86ba6eb707ed08905757b1bb44b8f", + RAND: "9f7c8d021accf4db213ccff0c7f71a6a", + SQN: "9d0277595ffc", + AMF: "725c", + OP: "dbc59adcb6f9a0ef735477b7fadf8374", + ExpectedOPc: "1006020f0a478bf6b699f15c062e42b3", + ExpectedMACA: "9cabc3e99baf7281", + ExpectedMACS: "95814ba2b3044324", + ExpectedRES: "8011c48c0c214ed2", + ExpectedCK: "5dbdbb2954e8f3cde665b046179a5098", + ExpectedIK: "59a92d3b476a0443487055cf88b2307b", + ExpectedAK: "33484dc2136b", + ExpectedAKStar: "deacdd848cc6", + }, + { // Test Set 4 + K: "9e5944aea94b81165c82fbf9f32db751", + RAND: "ce83dbc54ac0274a157c17f80d017bd6", + SQN: "0b604a81eca8", + AMF: "9e09", + OP: "223014c5806694c007ca1eeef57f004f", + ExpectedOPc: "a64a507ae1a2a98bb88eb4210135dc87", + ExpectedMACA: "74a58220cba84c49", + ExpectedMACS: "ac2cc74a96871837", + ExpectedRES: "f365cd683cd92e96", + ExpectedCK: "e203edb3971574f5a94b0d61b816345d", + ExpectedIK: "0c4524adeac041c4dd830d20854fc46b", + ExpectedAK: "f0b9c08ad02e", + ExpectedAKStar: "6085a86c6f63", + }, + { // Test Set 5 + K: "4ab1deb05ca6ceb051fc98e77d026a84", + RAND: "74b0cd6031a1c8339b2b6ce2b8c4a186", + SQN: "e880a1b580b6", + AMF: "9f07", + OP: "2d16c5cd1fdf6b22383584e3bef2a8d8", + ExpectedOPc: "dcf07cbd51855290b92a07a9891e523e", + ExpectedMACA: "49e785dd12626ef2", + ExpectedMACS: "9e85790336bb3fa2", + ExpectedRES: "5860fc1bce351e7e", + ExpectedCK: "7657766b373d1c2138f307e3de9242f9", + ExpectedIK: "1c42e960d89b8fa99f2744e0708ccb53", + ExpectedAK: "31e11a609118", + ExpectedAKStar: "fe2555e54aa9", + }, + { // Test Set 6 + K: "6c38a116ac280c454f59332ee35c8c4f", + RAND: "ee6466bc96202c5a557abbeff8babf63", + SQN: "414b98222181", + AMF: "4464", + OP: "1ba00a1a7c6700ac8c3ff3e96ad08725", + ExpectedOPc: "3803ef5363b947c6aaa225e58fae3934", + ExpectedMACA: "078adfb488241a57", + ExpectedMACS: "80246b8d0186bcf1", + ExpectedRES: "16c8233f05a0ac28", + ExpectedCK: "3f8c7587fe8e4b233af676aede30ba3b", + ExpectedIK: "a7466cc1e6b2a1337d49d3b66e95d7b4", + ExpectedAK: "45b0f69ab06c", + ExpectedAKStar: "1f53cd2b1113", + }, + { // Test Set 7 + K: "2d609d4db0ac5bf0d2c0de267014de0d", + RAND: "194aa756013896b74b4a2a3b0af4539e", + SQN: "6bf69438c2e4", + AMF: "5f67", + OP: "460a48385427aa39264aac8efc9e73e8", + ExpectedOPc: "c35a0ab0bcbfc9252caff15f24efbde0", + ExpectedMACA: "bd07d3003b9e5cc3", + ExpectedMACS: "bcb6c2fcad152250", + ExpectedRES: "8c25a16cd918a1df", + ExpectedCK: "4cd0846020f8fa0731dd47cbdc6be411", + ExpectedIK: "88ab80a415f15c73711254a1d388f696", + ExpectedAK: "7e6455f34cf3", + ExpectedAKStar: "dc6dd01e8f15", + }, + { // Test Set 8 + K: "a530a7fe428fad1082c45eddfce13884", + RAND: "3a4c2b3245c50eb5c71d08639395764d", + SQN: "f63f5d768784", + AMF: "b90e", + OP: "511c6c4e83e38c89b1c5d8dde62426fa", + ExpectedOPc: "27953e49bc8af6dcc6e730eb80286be3", + ExpectedMACA: "53761fbd679b0bad", + ExpectedMACS: "21adfd334a10e7ce", + ExpectedRES: "a63241e1ffc3e5ab", + ExpectedCK: "10f05bab75a99a5fbb98a9c287679c3b", + ExpectedIK: "f9ec0865eb32f22369cade40c59c3a44", + ExpectedAK: "88196c47986f", + ExpectedAKStar: "c987a3d23115", + }, + { // Test Set 9 + K: "d9151cf04896e25830bf2e08267b8360", + RAND: "f761e5e93d603feb730e27556cb8a2ca", + SQN: "47ee0199820a", + AMF: "9113", + OP: "75fc2233a44294ee8e6de25c4353d26b", + ExpectedOPc: "c4c93effe8a08138c203d4c27ce4e3d9", + ExpectedMACA: "66cc4be44862af1f", + ExpectedMACS: "7a4b8d7a8753f246", + ExpectedRES: "4a90b2171ac83a76", + ExpectedCK: "71236b7129f9b22ab77ea7a54c96da22", + ExpectedIK: "90527ebaa5588968db41727325a04d9e", + ExpectedAK: "82a0f5287a71", + ExpectedAKStar: "527dbf41f35f", + }, + { // Test Set 10 + K: "a0e2971b6822e8d354a18cc235624ecb", + RAND: "08eff828b13fdb562722c65c7f30a9b2", + SQN: "db5c066481e0", + AMF: "716b", + OP: "323792faca21fb4d5d6f13c145a9d2c1", + ExpectedOPc: "82a26f22bba9e9488f949a10d98e9cc4", + ExpectedMACA: "9485fe24621cb9f6", + ExpectedMACS: "bce325ce03e2e9b9", + ExpectedRES: "4bc2212d8624910a", + ExpectedCK: "08cef6d004ec61471a3c3cda048137fa", + ExpectedIK: "ed0318ca5deb9206272f6e8fa64ba411", + ExpectedAK: "a2f858aa9e5d", + ExpectedAKStar: "74e76fbbec38", + }, + { // Test Set 11 + K: "0da6f7ba86d5eac8a19cf563ac58642d", + RAND: "679ac4dbacd7d233ff9d6806f4149ce3", + SQN: "6e2331d692ad", + AMF: "224a", + OP: "4b9a26fa459e3acbff36f4015de3bdc1", + ExpectedOPc: "0db1071f8767562ca43a0a64c41e8d08", + ExpectedMACA: "2831d7ae9088e492", + ExpectedMACS: "9b2e16951135d523", + ExpectedRES: "6fc30fee6d123523", + ExpectedCK: "69b1cae7c7429d975e245cacb05a517c", + ExpectedIK: "74f24e8c26df58e1b38d7dcd4f1b7fbd", + ExpectedAK: "4c539a26e1fa", + ExpectedAKStar: "07861e126928", + }, + { // Test Set 12 + K: "77b45843c88e58c10d202684515ed430", + RAND: "4c47eb3076dc55fe5106cb2034b8cd78", + SQN: "fe1a8731005d", + AMF: "ad25", + OP: "bf3286c7a51409ce95724d503bfe6e70", + ExpectedOPc: "d483afae562409a326b5bb0b20c4d762", + ExpectedMACA: "08332d7e9f484570", + ExpectedMACS: "ed41b734489d5207", + ExpectedRES: "aefa357beac2a87a", + ExpectedCK: "908c43f0569cb8f74bc971e706c36c5f", + ExpectedIK: "c251df0d888dd9329bcf46655b226e40", + ExpectedAK: "30ff25cdadf6", + ExpectedAKStar: "e84ed0d4677e", + }, + { // Test Set 13 + K: "729b17729270dd87ccdf1bfe29b4e9bb", + RAND: "311c4c929744d675b720f3b7e9b1cbd0", + SQN: "c85c4cf65916", + AMF: "5bb2", + OP: "d04c9c35bd2262fa810d2924d036fd13", + ExpectedOPc: "228c2f2f06ac3268a9e616ee16db4ba1", + ExpectedMACA: "ff794fe2f827ebf8", + ExpectedMACS: "24fe4dc61e874b52", + ExpectedRES: "98dbbd099b3b408d", + ExpectedCK: "44c0f23c5493cfd241e48f197e1d1012", + ExpectedIK: "0c9fb81613884c2535dd0eabf3b440d8", + ExpectedAK: "5380d158cfe3", + ExpectedAKStar: "87ac3b559fb6", + }, + { // Test Set 14 + K: "d32dd23e89dc662354ca12eb79dd32fa", + RAND: "cf7d0ab1d94306950bf12018fbd46887", + SQN: "484107e56a43", + AMF: "b5e6", + OP: "fe75905b9da47d356236d0314e09c32e", + ExpectedOPc: "d22a4b4180a5325708a5ff70d9f67ec7", + ExpectedMACA: "cf19d62b6a809866", + ExpectedMACS: "5d269537e45e2ce6", + ExpectedRES: "af4a411e1139f2c2", + ExpectedCK: "5af86b80edb70df5292cc1121cbad50c", + ExpectedIK: "7f4d6ae7440e18789a8b75ad3f42f03a", + ExpectedAK: "217af49272ad", + ExpectedAKStar: "900e101c677e", + }, + { // Test Set 15 + K: "af7c65e1927221de591187a2c5987a53", + RAND: "1f0f8578464fd59b64bed2d09436b57a", + SQN: "3d627b01418d", + AMF: "84f6", + OP: "0c7acb8d95b7d4a31c5aca6d26345a88", + ExpectedOPc: "a4cf5c8155c08a7eff418e5443b98e55", + ExpectedMACA: "c37cae7805642032", + ExpectedMACS: "68cd09a452d8db7c", + ExpectedRES: "7bffa5c2f41fbc05", + ExpectedCK: "3f8c3f3ccf7625bf77fc94bcfd22fd26", + ExpectedIK: "abcbae8fd46115e9961a55d0da5f2078", + ExpectedAK: "837fd7b74419", + ExpectedAKStar: "56e97a6090b1", + }, + { // Test Set 16 + K: "5bd7ecd3d3127a41d12539bed4e7cf71", + RAND: "59b75f14251c75031d0bcbac1c2c04c7", + SQN: "a298ae8929dc", + AMF: "d056", + OP: "f967f76038b920a9cd25e10c08b49924", + ExpectedOPc: "76089d3c0ff3efdc6e36721d4fceb747", + ExpectedMACA: "c3f25cd94309107e", + ExpectedMACS: "b0c8ba343665afcc", + ExpectedRES: "7e3f44c7591f6f45", + ExpectedCK: "d42b2d615e49a03ac275a5aef97af892", + ExpectedIK: "0b3f8d024fe6bfafaa982b8f82e319c2", + ExpectedAK: "5be11495525d", + ExpectedAKStar: "4d6a34a1e4eb", + }, + { // Test Set 17 + K: "6cd1c6ceb1e01e14f1b82316a90b7f3d", + RAND: "f69b78f300a0568bce9f0cb93c4be4c9", + SQN: "b4fce5feb059", + AMF: "e4bb", + OP: "078bfca9564659ecd8851e84e6c59b48", + ExpectedOPc: "a219dc37f1dc7d66738b5843c799f206", + ExpectedMACA: "69a90869c268cb7b", + ExpectedMACS: "2e0fdcf9fd1cfa6a", + ExpectedRES: "70f6bdb9ad21525f", + ExpectedCK: "6edaf99e5bd9f85d5f36d91c1272fb4b", + ExpectedIK: "d61c853c280dd9c46f297baec386de17", + ExpectedAK: "1c408a858b3e", + ExpectedAKStar: "aa4ae52daa30", + }, + { // Test Set 18 + K: "b73a90cbcf3afb622dba83c58a8415df", + RAND: "b120f1c1a0102a2f507dd543de68281f", + SQN: "f1e8a523a36d", + AMF: "471b", + OP: "b672047e003bb952dca6cb8af0e5b779", + ExpectedOPc: "df0c67868fa25f748b7044c6e7c245b8", + ExpectedMACA: "ebd70341bcd415b0", + ExpectedMACS: "12359f5d82220c14", + ExpectedRES: "479dd25c20792d63", + ExpectedCK: "66195dbed0313274c5ca7766615fa25e", + ExpectedIK: "66bec707eb2afc476d7408a8f2927b36", + ExpectedAK: "aefdaa5ddd99", + ExpectedAKStar: "12ec2b87fbb1", + }, + { // Test Set 19 + K: "5122250214c33e723a5dd523fc145fc0", + RAND: "81e92b6c0ee0e12ebceba8d92a99dfa5", + SQN: "16f3b3f70fc2", + AMF: "c3ab", + OP: "c9e8763286b5b9ffbdf56e1297d0887b", + ExpectedOPc: "981d464c7c52eb6e5036234984ad0bcf", + ExpectedMACA: "2a5c23d15ee351d5", + ExpectedMACS: "62dae3853f3af9d2", + ExpectedRES: "28d7b0f2a2ec3de5", + ExpectedCK: "5349fbe098649f948f5d2e973a81c00f", + ExpectedIK: "9744871ad32bf9bbd1dd5ce54e3e2e5a", + ExpectedAK: "ada15aeb7bb8", + ExpectedAKStar: "d461bc15475d", + }, + { // Test Set 20 + K: "90dca4eda45b53cf0f12d7c9c3bc6a89", + RAND: "9fddc72092c6ad036b6e464789315b78", + SQN: "20f813bd4141", + AMF: "61df", + OP: "3ffcfe5b7b1111589920d3528e84e655", + ExpectedOPc: "cb9cccc4b9258e6dca4760379fb82581", + ExpectedMACA: "09db94eab4f8149e", + ExpectedMACS: "a29468aa9775b527", + ExpectedRES: "a95100e2760952cd", + ExpectedCK: "b5f2da03883b69f96bf52e029ed9ac45", + ExpectedIK: "b4721368bc16ea67875c5598688bb0ef", + ExpectedAK: "83cfd54db913", + ExpectedAKStar: "4f2039392ddc", + }, } - - OPC, err := GenerateOPC(K, OP) - if err != nil { - t.Errorf("err: %+v\n", err) + for i, tc := range conformanceTestCases { + t.Run(fmt.Sprintf("Set_%d", i+1), func(t *testing.T) { + K, err := hex.DecodeString(tc.K) + require.NoError(t, err, "decode K fail") + RAND, err := hex.DecodeString(tc.RAND) + require.NoError(t, err, "decode RAND fail") + SQN, err := hex.DecodeString(tc.SQN) + require.NoError(t, err, "decode SQN fail") + AMF, err := hex.DecodeString(tc.AMF) + require.NoError(t, err, "decode AMF fail") + OP, err := hex.DecodeString(tc.OP) + require.NoError(t, err, "decode OP fail") + + OPC, err := GenerateOPc(K, OP) + require.NoError(t, err, "calculate OPc fail") + require.Equal(t, tc.ExpectedOPc, hex.EncodeToString(OPC)) + MAC_A, MAC_S, err := f1(OPC, K, RAND, SQN, AMF) + require.NoError(t, err, "calculate F1 fail") + require.Equal(t, tc.ExpectedMACA, hex.EncodeToString(MAC_A)) + require.Equal(t, tc.ExpectedMACS, hex.EncodeToString(MAC_S)) + + RES, CK, IK, AK, AKstar, err := f2345(OPC, K, RAND) + require.NoError(t, err, "calculate F2345 fail") + require.Equal(t, tc.ExpectedRES, hex.EncodeToString(RES)) + require.Equal(t, tc.ExpectedCK, hex.EncodeToString(CK)) + require.Equal(t, tc.ExpectedIK, hex.EncodeToString(IK)) + require.Equal(t, tc.ExpectedAK, hex.EncodeToString(AK)) + require.Equal(t, tc.ExpectedAKStar, hex.EncodeToString(AKstar)) + }) } +} - fmt.Printf("K=%x\nSQN=%x\nOP=%x\nOPC=%x\n", K, SQN, OP, OPC) - RAND, err := hex.DecodeString("81e92b6c0ee0e12ebceba8d92a99dfa5") - if err != nil { - t.Errorf("err: %+v\n", err) +func TestGenerateAKAParameters(t *testing.T) { + testCases := []struct { + K string + OPc string + RAND string + AMF string + SQN string + expectedRES string + expectedCK string + expectedIK string + expectedAUTN string + }{ + { + K: "5122250214c33e723a5dd523fc145fc0", + OPc: "981d464c7c52eb6e5036234984ad0bcf", + RAND: "81e92b6c0ee0e12ebceba8d92a99dfa5", + AMF: "c3ab", + SQN: "16f3b3f70fc2", + expectedRES: "28d7b0f2a2ec3de5", + expectedCK: "5349fbe098649f948f5d2e973a81c00f", + expectedIK: "9744871ad32bf9bbd1dd5ce54e3e2e5a", + expectedAUTN: "bb52e91c747ac3ab2a5c23d15ee351d5", + }, } - AMF, err := hex.DecodeString("c3ab") - if err != nil { - t.Errorf("err: %+v\n", err) + for i, tc := range testCases { + t.Run(fmt.Sprintf("Set_%d", i), func(t *testing.T) { + K, err := hex.DecodeString(tc.K) + require.NoError(t, err, "decode K fail") + OPc, err := hex.DecodeString(tc.OPc) + require.NoError(t, err, "decode OPc fail") + AMF, err := hex.DecodeString(tc.AMF) + require.NoError(t, err, "decode AMF fail") + SQN, err := hex.DecodeString(tc.SQN) + require.NoError(t, err, "decode SQN fail") + RAND, err := hex.DecodeString(tc.RAND) + require.NoError(t, err, "decode RAND fail") + + IK, CK, RES, AUTN, err := GenerateAKAParameters(OPc, K, RAND, SQN, AMF) + require.NoError(t, err, "calculate AKA parameters fail") + require.Equal(t, tc.expectedIK, hex.EncodeToString(IK)) + require.Equal(t, tc.expectedCK, hex.EncodeToString(CK)) + require.Equal(t, tc.expectedRES, hex.EncodeToString(RES)) + require.Equal(t, tc.expectedAUTN, hex.EncodeToString(AUTN)) + }) } - fmt.Printf("RAND=%x\nAMF=%x\n", RAND, AMF) - - // for test - // RAND, _ = hex.DecodeString(TestGenAuthData.MilenageTestSet19.RAND) - // AMF, _ = hex.DecodeString(TestGenAuthData.MilenageTestSet19.AMF) - fmt.Printf("For test: RAND=%x, AMF=%x\n", RAND, AMF) - - // Run milenage - MAC_A, MAC_S := make([]byte, 8), make([]byte, 8) - CK, IK := make([]byte, 16), make([]byte, 16) - RES := make([]byte, 8) - AK, AKstar := make([]byte, 6), make([]byte, 6) - - // Generate MAC_A, MAC_S +} - err = F1(OPC, K, RAND, SQN, AMF, MAC_A, MAC_S) - if err != nil { - t.Errorf("err: %+v\n", err) +func TestGenerateKeysWithAUTN(t *testing.T) { + testCases := []struct { + K string + OPc string + RAND string + AUTN string + expectedSQN string + expectedAK string + expectedCK string + expectedIK string + + expectedRES string + }{ + { + K: "5122250214c33e723a5dd523fc145fc0", + OPc: "981d464c7c52eb6e5036234984ad0bcf", + RAND: "81e92b6c0ee0e12ebceba8d92a99dfa5", + AUTN: "bb52e91c747ac3ab2a5c23d15ee351d5", + expectedRES: "28d7b0f2a2ec3de5", + expectedAK: "ada15aeb7bb8", + expectedCK: "5349fbe098649f948f5d2e973a81c00f", + expectedIK: "9744871ad32bf9bbd1dd5ce54e3e2e5a", + expectedSQN: "16f3b3f70fc2", + }, } - // Generate RES, CK, IK, AK, AKstar - // RES == XRES (expected RES) for server - err = F2345(OPC, K, RAND, RES, CK, IK, AK, AKstar) - if err != nil { - t.Errorf("err: %+v\n", err) - } - fmt.Printf("milenage RES = %s\n", hex.EncodeToString(RES)) - // - fmt.Printf("RES=%x\n", RES) - expRES, err := hex.DecodeString("28d7b0f2a2ec3de5") - if err != nil { - t.Errorf("err: %+v\n", err) - } - if !reflect.DeepEqual(RES, RES) { - t.Errorf("RES[0x%x] \t expected[0x%x]\n", RES, expRES) - } - // Generate AUTN - fmt.Printf("CK=%x\n", CK) - expCK, err := hex.DecodeString("5349fbe098649f948f5d2e973a81c00f") - if err != nil { - t.Errorf("err: %+v\n", err) - } - if !reflect.DeepEqual(CK, expCK) { - t.Errorf("CK[0x%x] \t expected[0x%x]\n", CK, expCK) - } - fmt.Printf("IK=%x\n", IK) - expIK, err := hex.DecodeString("9744871ad32bf9bbd1dd5ce54e3e2e5a") - if err != nil { - t.Errorf("err: %+v\n", err) + for i, tc := range testCases { + t.Run(fmt.Sprintf("Set_%d", i), func(t *testing.T) { + K, err := hex.DecodeString(tc.K) + require.NoError(t, err, "decode K fail") + OPc, err := hex.DecodeString(tc.OPc) + require.NoError(t, err, "decode OPc fail") + RAND, err := hex.DecodeString(tc.RAND) + require.NoError(t, err, "decode RAND fail") + AUTN, err := hex.DecodeString(tc.AUTN) + require.NoError(t, err, "decode AUTN fail") + + SQN, AK, IK, CK, RES, err := GenerateKeysWithAUTN(OPc, K, RAND, AUTN) + require.NoError(t, err, "calculate AKA parameters fail") + require.Equal(t, tc.expectedAK, hex.EncodeToString(AK)) + require.Equal(t, tc.expectedIK, hex.EncodeToString(IK)) + require.Equal(t, tc.expectedCK, hex.EncodeToString(CK)) + require.Equal(t, tc.expectedRES, hex.EncodeToString(RES)) + require.Equal(t, tc.expectedSQN, hex.EncodeToString(SQN)) + }) } - if !reflect.DeepEqual(IK, expIK) { - t.Errorf("IK[0x%x] \t expected[0x%x]\n", IK, expIK) - } - // fmt.Printf("SQN=%x\nAK =%x\n", SQN, AK) - // fmt.Printf("AK=%x\n", AK) - expAK, err := hex.DecodeString("ada15aeb7bb8") - if err != nil { - t.Errorf("err: %+v\n", err) - } - if !reflect.DeepEqual(AK, expAK) { - t.Errorf("AK[0x%x] \t expected[0x%x]\n", AK, expAK) - } - // fmt.Printf("AMF=%x, MAC_A=%x\n", AMF, MAC_A) - SQNxorAK := make([]byte, 6) - for i := 0; i < len(SQN); i++ { - SQNxorAK[i] = SQN[i] ^ AK[i] - } - - fmt.Printf("SQN xor AK = %x\n", SQNxorAK) - AUTN := append(append(SQNxorAK, AMF...), MAC_A...) - - // fmt.Printf("MAC_A = %x\n", MAC_A) - // fmt.Printf("MAC_S = %x\n", MAC_S) - - // fmt.Printf("AUTN = %x\n", AUTN) +} - expAUTN, err := hex.DecodeString("bb52e91c747ac3ab2a5c23d15ee351d5") - if err != nil { - t.Errorf("err: %+v\n", err) +func TestValidateAUTS(t *testing.T) { + ValidateAUTSTestSetTestCases := []struct { + K string + OPc string + AUTS string + RAND string + ExpectedSQNms string + }{ + { + K: "00000000000000000000000000000000", + OPc: "c8ffd2aa7a43c926bf2b2826205b9030", + AUTS: "797d7a19ca27f99f4363d3ca24be", + RAND: "01000000000000002e6f0eb33b7ffde7", + ExpectedSQNms: "00052c8c338e", + }, + { + K: "00000000000000000012340000000000", + OPc: "c8ffd2aa7a43c926bf2b2826205b9030", + AUTS: "797d7a19ca27f99f4363d3ca24be", + RAND: "01000000000000002e6f0eb33b7ffde7", + ExpectedSQNms: "", + }, } - if !reflect.DeepEqual(AUTN, expAUTN) { - t.Errorf("AUTN[0x%x] \t expected[0x%x]\n", AUTN, expAUTN) + for i, tc := range ValidateAUTSTestSetTestCases { + t.Run(fmt.Sprintf("Set_%d", i+1), func(t *testing.T) { + OPc, err := hex.DecodeString(tc.OPc) + require.NoError(t, err, "decode OPc fail") + K, err := hex.DecodeString(tc.K) + require.NoError(t, err, "decode K fail") + AUTS, err := hex.DecodeString(tc.AUTS) + require.NoError(t, err, "decode AUTS fail") + RAND, err := hex.DecodeString(tc.RAND) + require.NoError(t, err, "decode RAND fail") + + SQNms, err := ValidateAUTS(OPc, K, RAND, AUTS) + if tc.ExpectedSQNms != "" { + require.NoError(t, err, "validate AUTS fail") + require.Equal(t, tc.ExpectedSQNms, hex.EncodeToString(SQNms), "SQNms not eqaul") + } else { // expected fail + macFail := &MACFailureError{} + require.ErrorAs(t, err, &macFail, "not return \"MACFailure\"") + } + }) } }