├── .gitignore ├── C_alg ├── Kasumi.c ├── Kasumi.h ├── KeccakP-1600-3gpp.c ├── KeccakP-1600-3gpp.h ├── Makefile ├── SNOW_3G.c ├── SNOW_3G.h ├── ZUC.c ├── ZUC.h ├── comp128.c └── comp128.h ├── C_py ├── pycomp128.c ├── pykasumi.c ├── pykeccakp1600.c ├── pysnow.c └── pyzuc.c ├── CryptoMobile ├── AES.py ├── CM.py ├── CMAC.py ├── EC.py ├── ECIES.py ├── Milenage.py ├── TUAK.py ├── __init__.py ├── conv.py └── utils.py ├── README.md ├── _ctypes └── CM_ctypes.py ├── setup.py └── test ├── __init__.py ├── test_CM.py ├── test_CryptoMobile.py ├── test_ECIES.py ├── test_Milenage.py └── test_TUAK.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | 27 | # Winmerge / gedit backup file 28 | *.py~ 29 | *.bak -------------------------------------------------------------------------------- /C_alg/Kasumi.c: -------------------------------------------------------------------------------- 1 | /* ----------------------------------------------------------------------- 2 | * code extracted from 3GPP TS 35.202, annex 2, for core functions 3 | * https://portal.3gpp.org/desktopmodules/Specifications/SpecificationDetails.aspx?specificationId=2387 4 | * code extracted from 3GPP TS 35.201, annex 2, for F8 and F9 functions 5 | * https://portal.3gpp.org/desktopmodules/Specifications/SpecificationDetails.aspx?specificationId=2386 6 | *-----------------------------------------------------------------------*/ 7 | 8 | /*----------------------------------------------------------------------- 9 | * Kasumi.c 10 | *----------------------------------------------------------------------- 11 | * 12 | * A sample implementation of KASUMI, the core algorithm for the 13 | * 3GPP Confidentiality and Integrity algorithms. 14 | * 15 | * This has been coded for clarity, not necessarily for efficiency. 16 | * 17 | * This will compile and run correctly on both Intel (little endian) 18 | * and Sparc (big endian) machines. (Compilers used supported 32-bit ints). 19 | * 20 | * Version 1.1 08 May 2000 21 | * 22 | *-----------------------------------------------------------------------*/ 23 | 24 | #include "Kasumi.h" 25 | 26 | /*--------- 16 bit rotate left ------------------------------------------*/ 27 | 28 | #define ROL16(a,b) (u16)((a<>(16-b))) 29 | 30 | /*-------- globals: The subkey arrays -----------------------------------*/ 31 | 32 | static u16 KLi1[8], KLi2[8]; 33 | static u16 KOi1[8], KOi2[8], KOi3[8]; 34 | static u16 KIi1[8], KIi2[8], KIi3[8]; 35 | 36 | 37 | /*--------------------------------------------------------------------- 38 | * FI() 39 | * The FI function (fig 3). It includes the S7 and S9 tables. 40 | * Transforms a 16-bit value. 41 | *---------------------------------------------------------------------*/ 42 | static u16 FI( u16 in, u16 subkey ) 43 | { 44 | u16 nine, seven; 45 | static u16 S7[] = { 46 | 54, 50, 62, 56, 22, 34, 94, 96, 38, 6, 63, 93, 2, 18,123, 33, 47 | 55,113, 39,114, 21, 67, 65, 12, 47, 73, 46, 27, 25,111,124, 81, 48 | 53, 9,121, 79, 52, 60, 58, 48,101,127, 40,120,104, 70, 71, 43, 49 | 20,122, 72, 61, 23,109, 13,100, 77, 1, 16, 7, 82, 10,105, 98, 50 | 117,116, 76, 11, 89,106, 0,125,118, 99, 86, 69, 30, 57,126, 87, 51 | 112, 51, 17, 5, 95, 14, 90, 84, 91, 8, 35,103, 32, 97, 28, 66, 52 | 102, 31, 26, 45, 75, 4, 85, 92, 37, 74, 80, 49, 68, 29,115, 44, 53 | 64,107,108, 24,110, 83, 36, 78, 42, 19, 15, 41, 88,119, 59, 3}; 54 | static u16 S9[] = { 55 | 167,239,161,379,391,334, 9,338, 38,226, 48,358,452,385, 90,397, 56 | 183,253,147,331,415,340, 51,362,306,500,262, 82,216,159,356,177, 57 | 175,241,489, 37,206, 17, 0,333, 44,254,378, 58,143,220, 81,400, 58 | 95, 3,315,245, 54,235,218,405,472,264,172,494,371,290,399, 76, 59 | 165,197,395,121,257,480,423,212,240, 28,462,176,406,507,288,223, 60 | 501,407,249,265, 89,186,221,428,164, 74,440,196,458,421,350,163, 61 | 232,158,134,354, 13,250,491,142,191, 69,193,425,152,227,366,135, 62 | 344,300,276,242,437,320,113,278, 11,243, 87,317, 36, 93,496, 27, 63 | 487,446,482, 41, 68,156,457,131,326,403,339, 20, 39,115,442,124, 64 | 475,384,508, 53,112,170,479,151,126,169, 73,268,279,321,168,364, 65 | 363,292, 46,499,393,327,324, 24,456,267,157,460,488,426,309,229, 66 | 439,506,208,271,349,401,434,236, 16,209,359, 52, 56,120,199,277, 67 | 465,416,252,287,246, 6, 83,305,420,345,153,502, 65, 61,244,282, 68 | 173,222,418, 67,386,368,261,101,476,291,195,430, 49, 79,166,330, 69 | 280,383,373,128,382,408,155,495,367,388,274,107,459,417, 62,454, 70 | 132,225,203,316,234, 14,301, 91,503,286,424,211,347,307,140,374, 71 | 35,103,125,427, 19,214,453,146,498,314,444,230,256,329,198,285, 72 | 50,116, 78,410, 10,205,510,171,231, 45,139,467, 29, 86,505, 32, 73 | 72, 26,342,150,313,490,431,238,411,325,149,473, 40,119,174,355, 74 | 185,233,389, 71,448,273,372, 55,110,178,322, 12,469,392,369,190, 75 | 1,109,375,137,181, 88, 75,308,260,484, 98,272,370,275,412,111, 76 | 336,318, 4,504,492,259,304, 77,337,435, 21,357,303,332,483, 18, 77 | 47, 85, 25,497,474,289,100,269,296,478,270,106, 31,104,433, 84, 78 | 414,486,394, 96, 99,154,511,148,413,361,409,255,162,215,302,201, 79 | 266,351,343,144,441,365,108,298,251, 34,182,509,138,210,335,133, 80 | 311,352,328,141,396,346,123,319,450,281,429,228,443,481, 92,404, 81 | 485,422,248,297, 23,213,130,466, 22,217,283, 70,294,360,419,127, 82 | 312,377, 7,468,194, 2,117,295,463,258,224,447,247,187, 80,398, 83 | 284,353,105,390,299,471,470,184, 57,200,348, 63,204,188, 33,451, 84 | 97, 30,310,219, 94,160,129,493, 64,179,263,102,189,207,114,402, 85 | 438,477,387,122,192, 42,381, 5,145,118,180,449,293,323,136,380, 86 | 43, 66, 60,455,341,445,202,432, 8,237, 15,376,436,464, 59,461}; 87 | 88 | /* The sixteen bit input is split into two unequal halves, * 89 | * nine bits and seven bits - as is the subkey */ 90 | 91 | nine = (u16)(in>>7); 92 | seven = (u16)(in&0x7F); 93 | 94 | /* Now run the various operations */ 95 | 96 | nine = (u16)(S9[nine] ^ seven); 97 | seven = (u16)(S7[seven] ^ (nine & 0x7F)); 98 | 99 | seven ^= (subkey>>9); 100 | nine ^= (subkey&0x1FF); 101 | 102 | nine = (u16)(S9[nine] ^ seven); 103 | seven = (u16)(S7[seven] ^ (nine & 0x7F)); 104 | 105 | in = (u16)((seven<<9) + nine); 106 | 107 | return( in ); 108 | } 109 | 110 | 111 | /*--------------------------------------------------------------------- 112 | * FO() 113 | * The FO() function. 114 | * Transforms a 32-bit value. Uses to identify the 115 | * appropriate subkeys to use. 116 | *---------------------------------------------------------------------*/ 117 | static u32 FO( u32 in, int index ) 118 | { 119 | u16 left, right; 120 | 121 | /* Split the input into two 16-bit words */ 122 | 123 | left = (u16)(in>>16); 124 | right = (u16) in; 125 | 126 | /* Now apply the same basic transformation three times */ 127 | 128 | left ^= KOi1[index]; 129 | left = FI( left, KIi1[index] ); 130 | left ^= right; 131 | 132 | right ^= KOi2[index]; 133 | right = FI( right, KIi2[index] ); 134 | right ^= left; 135 | 136 | left ^= KOi3[index]; 137 | left = FI( left, KIi3[index] ); 138 | left ^= right; 139 | 140 | in = (((u32)right)<<16)+left; 141 | 142 | return( in ); 143 | } 144 | 145 | /*--------------------------------------------------------------------- 146 | * FL() 147 | * The FL() function. 148 | * Transforms a 32-bit value. Uses to identify the 149 | * appropriate subkeys to use. 150 | *---------------------------------------------------------------------*/ 151 | static u32 FL( u32 in, int index ) 152 | { 153 | u16 l, r, a, b; 154 | 155 | /* split out the left and right halves */ 156 | 157 | l = (u16)(in>>16); 158 | r = (u16)(in); 159 | 160 | /* do the FL() operations */ 161 | 162 | a = (u16) (l & KLi1[index]); 163 | r ^= ROL16(a,1); 164 | 165 | b = (u16)(r | KLi2[index]); 166 | l ^= ROL16(b,1); 167 | 168 | /* put the two halves back together */ 169 | 170 | in = (((u32)l)<<16) + r; 171 | 172 | return( in ); 173 | } 174 | 175 | 176 | /*--------------------------------------------------------------------- 177 | * Kasumi() 178 | * the Main algorithm (fig 1). Apply the same pair of operations 179 | * four times. Transforms the 64-bit input. 180 | *---------------------------------------------------------------------*/ 181 | EXPORTIT void Kasumi( u8 *data ) 182 | { 183 | u32 left, right, temp; 184 | REGISTER32 *d; 185 | int n; 186 | 187 | /* Start by getting the data into two 32-bit words (endian corect) */ 188 | 189 | d = (REGISTER32*)data; 190 | left = (((u32)d[0].b8[0])<<24)+(((u32)d[0].b8[1])<<16) 191 | +(d[0].b8[2]<<8)+(d[0].b8[3]); 192 | right = (((u32)d[1].b8[0])<<24)+(((u32)d[1].b8[1])<<16) 193 | +(d[1].b8[2]<<8)+(d[1].b8[3]); 194 | n = 0; 195 | do { 196 | temp = FL( left, n ); 197 | temp = FO( temp, n++ ); 198 | right ^= temp; 199 | temp = FO( right, n ); 200 | temp = FL( temp, n++ ); 201 | left ^= temp; 202 | } while( n<=7 ); 203 | 204 | /* return the correct endian result */ 205 | d[0].b8[0] = (u8)(left>>24); d[1].b8[0] = (u8)(right>>24); 206 | d[0].b8[1] = (u8)(left>>16); d[1].b8[1] = (u8)(right>>16); 207 | d[0].b8[2] = (u8)(left>>8); d[1].b8[2] = (u8)(right>>8); 208 | d[0].b8[3] = (u8)(left); d[1].b8[3] = (u8)(right); 209 | 210 | /* strange issue with gcc, where data is not updated 211 | with left and right values... give a try like this: 212 | data = d; 213 | actually not working... */ 214 | } 215 | 216 | /*--------------------------------------------------------------------- 217 | * KeySchedule() 218 | * Build the key schedule. Most "key" operations use 16-bit 219 | * subkeys so we build u16-sized arrays that are "endian" correct. 220 | *---------------------------------------------------------------------*/ 221 | EXPORTIT void KeySchedule( u8 *k ) 222 | { 223 | static u16 C[] = { 224 | 0x0123,0x4567,0x89AB,0xCDEF, 0xFEDC,0xBA98,0x7654,0x3210 }; 225 | u16 key[8], Kprime[8]; 226 | REGISTER16 *k16; 227 | int n; 228 | 229 | /* Start by ensuring the subkeys are endian correct on a 16-bit basis */ 230 | 231 | k16 = (REGISTER16 *)k; 232 | for( n=0; n<8; ++n ) 233 | key[n] = (u16)((k16[n].b8[0]<<8) + (k16[n].b8[1])); 234 | 235 | /* Now build the K'[] keys */ 236 | 237 | for( n=0; n<8; ++n ) 238 | Kprime[n] = (u16)(key[n] ^ C[n]); 239 | 240 | /* Finally construct the various sub keys */ 241 | 242 | for( n=0; n<8; ++n ) 243 | { 244 | KLi1[n] = ROL16(key[n],1); 245 | KLi2[n] = Kprime[(n+2)&0x7]; 246 | KOi1[n] = ROL16(key[(n+1)&0x7],5); 247 | KOi2[n] = ROL16(key[(n+5)&0x7],8); 248 | KOi3[n] = ROL16(key[(n+6)&0x7],13); 249 | KIi1[n] = Kprime[(n+4)&0x7]; 250 | KIi2[n] = Kprime[(n+3)&0x7]; 251 | KIi3[n] = Kprime[(n+7)&0x7]; 252 | } 253 | } 254 | /*--------------------------------------------------------------------- 255 | * e n d o f k a s u m i . c 256 | *---------------------------------------------------------------------*/ 257 | 258 | /*------------------------------------------------------------------- 259 | * F8 - Confidentiality Algorithm 260 | *------------------------------------------------------------------- 261 | * 262 | * A sample implementation of f8, the 3GPP Confidentiality algorithm. 263 | * 264 | * This has been coded for clarity, not necessarily for efficiency. 265 | * 266 | * This will compile and run correctly on both Intel (little endian) 267 | * and Sparc (big endian) machines. (Compilers used supported 32-bit ints) 268 | * 269 | * Version 1.0 05 November 1999 270 | * 271 | *-------------------------------------------------------------------*/ 272 | 273 | /*--------------------------------------------------------- 274 | * f8() 275 | * Given key, count, bearer, direction, data, 276 | * and bit length encrypt the bit stream 277 | *---------------------------------------------------------*/ 278 | EXPORTIT void f8(u8 *key, u32 count, u32 bearer, u32 dir, u8 *data, int length) 279 | { 280 | REGISTER64 A; /* the modifier */ 281 | REGISTER64 temp; /* The working register */ 282 | int i, n; 283 | int lastbits = (8-(length%8)) % 8; 284 | u8 ModKey[16]; /* Modified key */ 285 | u16 blkcnt; /* The block counter */ 286 | 287 | /* Start by building our global modifier */ 288 | temp.b32[0] = temp.b32[1] = 0; 289 | A.b32[0] = A.b32[1] = 0; 290 | 291 | /* initialise register in an endian correct manner*/ 292 | A.b8[0] = (u8) (count>>24); 293 | A.b8[1] = (u8) (count>>16); 294 | A.b8[2] = (u8) (count>>8); 295 | A.b8[3] = (u8) (count); 296 | A.b8[4] = (u8) (bearer<<3); 297 | A.b8[4] |= (u8) (dir<<2); 298 | 299 | /* Construct the modified key and then "kasumi" A */ 300 | for( n=0; n<16; ++n ) 301 | ModKey[n] = (u8)(key[n] ^ 0x55); 302 | KeySchedule( ModKey ); 303 | 304 | Kasumi( A.b8 ); /* First encryption to create modifier */ 305 | 306 | /* Final initialisation steps */ 307 | blkcnt = 0; 308 | KeySchedule( key ); 309 | 310 | /* Now run the block cipher */ 311 | while( length > 0 ) 312 | { 313 | /* First we calculate the next 64-bits of keystream */ 314 | 315 | /* XOR in A and BLKCNT to last value */ 316 | temp.b32[0] ^= A.b32[0]; 317 | temp.b32[1] ^= A.b32[1]; 318 | temp.b8[7] ^= (u8) blkcnt; 319 | temp.b8[6] ^= (u8) (blkcnt>>8); 320 | 321 | /* KASUMI it to produce the next block of keystream */ 322 | Kasumi( temp.b8 ); 323 | 324 | /* Set to the number of bytes of input data * 325 | * we have to modify. (=8 if length <= 64) */ 326 | if( length >= 64 ) 327 | n = 8; 328 | else 329 | n = (length+7)/8; 330 | 331 | /* XOR the keystream with the input data stream */ 332 | for( i=0; i holds our chaining value... * 388 | * is the running XOR of all KASUMI o/ps */ 389 | for( n=0; n<4; ++n ) 390 | { 391 | A.b8[n] = (u8)(count>>(24-(n*8))); 392 | A.b8[n+4] = (u8)(fresh>>(24-(n*8))); 393 | } 394 | Kasumi( A.b8 ); 395 | B.b32[0] = A.b32[0]; 396 | B.b32[1] = A.b32[1]; 397 | 398 | /* Now run the blocks until we reach the last block */ 399 | while( length >= 64 ) 400 | { 401 | for( n=0; n<8; ++n ) 402 | A.b8[n] ^= *data++; 403 | Kasumi( A.b8 ); 404 | length -= 64; 405 | B.b32[0] ^= A.b32[0]; /* running XOR across */ 406 | B.b32[1] ^= A.b32[1]; /* the block outputs */ 407 | } 408 | 409 | /* Process whole bytes in the last block */ 410 | n = 0; 411 | while( length >=8 ) 412 | { 413 | A.b8[n++] ^= *data++; 414 | length -= 8; 415 | } 416 | 417 | /* Now add the direction bit to the input bit stream * 418 | * If length (which holds the # of data bits in the * 419 | * last byte) is non-zero we add it in, otherwise * 420 | * it has to start a new byte. */ 421 | if( length ) 422 | { 423 | i = *data; 424 | if( dir ) 425 | i |= FinalBit[length]; 426 | } 427 | else 428 | i = dir ? 0x80 : 0; 429 | 430 | A.b8[n++] ^= (u8)i; 431 | 432 | /* Now add in the final '1' bit. The problem here * 433 | * is if the message length happens to be n*64-1. * 434 | * If so we need to process this block and then * 435 | * create a new input block of 0x8000000000000000. */ 436 | if( (length==7) && (n==8) ) /* then we've filled the block */ 437 | { 438 | Kasumi( A.b8 ); 439 | B.b32[0] ^= A.b32[0]; /* running XOR across */ 440 | B.b32[1] ^= A.b32[1]; /* the block outputs */ 441 | 442 | A.b8[0] ^= 0x80; /* toggle first bit */ 443 | i = 0x80; 444 | n = 1; 445 | } 446 | else 447 | { 448 | if( length == 7 ) /* we finished off the last byte */ 449 | A.b8[n] ^= 0x80; /* so start a new one..... */ 450 | else 451 | A.b8[n-1] ^= FinalBit[length+1]; 452 | } 453 | 454 | Kasumi( A.b8 ); 455 | B.b32[0] ^= A.b32[0]; /* running XOR across */ 456 | B.b32[1] ^= A.b32[1]; /* the block outputs */ 457 | 458 | /* Final step is to KASUMI what we have using the * 459 | * key XORd with 0xAAAA..... */ 460 | for( n=0; n<16; ++n ) 461 | ModKey[n] = (u8)*key++ ^ 0xAA; 462 | KeySchedule( ModKey ); 463 | Kasumi( B.b8 ); 464 | 465 | /* We return the left-most 32-bits of the result */ 466 | 467 | for( n=0; n<4; ++n ) 468 | mac_i[n] = B.b8[n]; 469 | 470 | return( mac_i ); 471 | } 472 | 473 | /*----------------------------------------------------------- 474 | * e n d o f f 9 . c 475 | *-----------------------------------------------------------*/ 476 | -------------------------------------------------------------------------------- /C_alg/Kasumi.h: -------------------------------------------------------------------------------- 1 | /* ----------------------------------------------------------------------- 2 | * code extracted from 3GPP TS 35.202, annex 2, for core functions 3 | * https://portal.3gpp.org/desktopmodules/Specifications/SpecificationDetails.aspx?specificationId=2387 4 | * code extracted from 3GPP TS 35.201, annex 2, for F8 and F9 functions 5 | * https://portal.3gpp.org/desktopmodules/Specifications/SpecificationDetails.aspx?specificationId=2386 6 | *-----------------------------------------------------------------------*/ 7 | 8 | /* this is the trick to make the code cross-platform 9 | * at least, Win32 / Linux */ 10 | 11 | #if defined(_WIN32) || defined(__WIN32__) 12 | # include 13 | # define EXPORTIT __declspec(dllexport) 14 | #else 15 | # define EXPORTIT 16 | #endif 17 | 18 | /*--------------------------------------------------------- 19 | * Kasumi.h 20 | *---------------------------------------------------------*/ 21 | 22 | #include 23 | 24 | typedef unsigned char u8; 25 | typedef unsigned short u16; 26 | /* original reference is using long, which is 64 bits on 64 bits system 27 | I changed to int to make it work on x86 32 / 64 bits system 28 | typedef unsigned long u32; 29 | */ 30 | typedef unsigned int u32; 31 | 32 | 33 | /*------- unions: used to remove "endian" issues ------------------------*/ 34 | 35 | typedef union { 36 | u32 b32; 37 | u16 b16[2]; 38 | u8 b8[4]; 39 | } REGISTER32; /* is redefining DWORD */ 40 | 41 | typedef union { 42 | u16 b16; 43 | u8 b8[2]; 44 | } REGISTER16; /* is redefining WORD */ 45 | 46 | /*----- a 64-bit structure to help with endian issues -----*/ 47 | 48 | typedef union { 49 | u32 b32[2]; 50 | u16 b16[4]; 51 | u8 b8[8]; 52 | } REGISTER64; 53 | 54 | /*------------- prototypes -------------------------------- 55 | * take care: length (in f8 and f9) is always in bits 56 | *---------------------------------------------------------*/ 57 | 58 | /* initialize the 128 bits key into the cipher */ 59 | EXPORTIT void KeySchedule( u8 *key ); 60 | 61 | /* cipher a block of 64 bits */ 62 | EXPORTIT void Kasumi( u8 *data ); 63 | 64 | /* cipher a whole message in 3GPP -counter- mode */ 65 | EXPORTIT void f8( u8 *key, u32 count, u32 bearer, u32 dir, \ 66 | u8 *data, int length ); 67 | 68 | /* compute a 3GPP MAC on a message */ 69 | EXPORTIT u8 * f9( u8 *key, u32 count, u32 fresh, u32 dir, \ 70 | u8 *data, int length ); 71 | -------------------------------------------------------------------------------- /C_alg/KeccakP-1600-3gpp.c: -------------------------------------------------------------------------------- 1 | /* ----------------------------------------------------------------------- 2 | * code extracted from 3GPP TS 35.231, annex E for Keccak core functions 3 | * https://portal.3gpp.org/desktopmodules/Specifications/SpecificationDetails.aspx?specificationId=2402 4 | *-----------------------------------------------------------------------*/ 5 | 6 | /* This code may be freely used or adapted. 7 | */ 8 | 9 | #include "KeccakP-1600-3gpp.h" 10 | 11 | 12 | const uint8_t Rho[25] = {0,1,62,28,27,36,44,6,55,20,3,10,43,25,39,41,45, 13 | 15,21,8,18,2,61,56,14}; 14 | 15 | const uint8_t Pi[25] = {0,6,12,18,24,3,9,10,16,22,1,7,13,19,20,4,5,11,17, 16 | 23,2,8,14,15,21}; 17 | 18 | const uint8_t Iota[24] = {1,146,218,112,155,33,241,89,138,136,57,42,187,203, 19 | 217,83,82,192,26,106,241,208,33,120}; 20 | 21 | #define ROTATE64(value, n) \ 22 | ((((uint64_t)(value))<<(n)) | (((uint64_t)(value))>>(64-(n)))) 23 | 24 | /* --------------------------------------------------------------------- 25 | 64-bit version of Keccak_f(1600) 26 | --------------------------------------------------------------------- 27 | */ 28 | void Keccak_f_64(uint64_t s[25]) 29 | { uint64_t t[5]; 30 | uint8_t i, j, round; 31 | 32 | for(round=0; round<24; ++round) 33 | { /* Theta function */ 34 | for(i=0; i<5; ++i) 35 | t[i] = s[i] ^ s[5+i] ^ s[10+i] ^ s[15+i] ^ s[20+i]; 36 | for(i=0; i<5; ++i, s+=5) 37 | { s[0] ^= t[4] ^ ROTATE64(t[1], 1); 38 | s[1] ^= t[0] ^ ROTATE64(t[2], 1); 39 | s[2] ^= t[1] ^ ROTATE64(t[3], 1); 40 | s[3] ^= t[2] ^ ROTATE64(t[4], 1); 41 | s[4] ^= t[3] ^ ROTATE64(t[0], 1); 42 | } 43 | s -= 25; 44 | 45 | /* Rho function */ 46 | for(i=1; i<25; ++i) 47 | s[i] = ROTATE64(s[i], Rho[i]); 48 | 49 | /* Pi function */ 50 | for(t[1] = s[i=1]; (j=Pi[i]) > 1; s[i]=s[j], i=j); 51 | s[i] = t[1]; 52 | 53 | /* Chi function */ 54 | for(i=0; i<5; ++i, s += 5) 55 | { t[0] = (~s[1]) & s[2]; 56 | t[1] = (~s[2]) & s[3]; 57 | t[2] = (~s[3]) & s[4]; 58 | t[3] = (~s[4]) & s[0]; 59 | t[4] = (~s[0]) & s[1]; 60 | for(j=0; j<5; ++j) s[j] ^= t[j]; 61 | } 62 | s -= 25; 63 | 64 | /* Iota function */ 65 | t[0] = Iota[round]; 66 | *s ^= (t[0] | (t[0]<<11) | (t[0]<<26) | (t[0]<<57)) 67 | & 0x800000008000808BULL; /* set & mask bits 0,1,3,7,15,31,63 */ 68 | } 69 | } 70 | 71 | 72 | /* --------------------------------------------------------------------- 73 | 8-bit version of Keccak_f(1600) 74 | --------------------------------------------------------------------- 75 | */ 76 | void Keccak_f_8(uint8_t s[200]) 77 | { uint8_t t[40], i, j, k, round; 78 | 79 | for(round=0; round<24; ++round) 80 | { /* Theta function */ 81 | for(i=0; i<40; ++i) 82 | t[i]=s[i]^s[40+i]^s[80+i]^s[120+i]^s[160+i]; 83 | for(i=0; i<200; i+=8) 84 | for(j = (i+32)%40, k=0; k<8; ++k) 85 | s[i+k] ^= t[j+k]; 86 | for(i=0; i<40; t[i] = (t[i]<<1)|j, i+=8) 87 | for(j = t[i+7]>>7, k=7; k; --k) 88 | t[i+k] = (t[i+k]<<1)|(t[i+k-1]>>7); 89 | for(i=0; i<200; i+=8) 90 | for(j = (i+8)%40, k=0; k<8; ++k) 91 | s[i+k] ^= t[j+k]; 92 | 93 | /* Rho function */ 94 | for(i=8; i<200; i+=8) 95 | { for(j = Rho[i>>3]>>3, k=0; k<8; ++k) /* j:=bytes to shift, s->t */ 96 | t[(k+j)&7] = s[i+k]; 97 | for(j = Rho[i>>3]&7, k=7; k; --k) /* j:=bits to shift, t->s */ 98 | s[i+k] = (t[k]<>(8-j)); 99 | s[i] = (t[0]<>(8-j)); 100 | } 101 | 102 | /* Pi function */ 103 | for(k=8; k<16; ++k) t[k] = s[k]; /* =memcpy(t+8, s+8, 8) */ 104 | for(i=1; (j=Pi[i])>1; i=j) 105 | for(k=0; k<8; ++k) /* =memcpy(s+(i<<3), s+(j<<3), 8) */ 106 | s[(i<<3)|k] = s[(j<<3)|k]; 107 | for(k=0; k<8; ++k) /* =memcpy(s+(i<<3), t+8, 8) */ 108 | s[(i<<3)|k] = t[k+8]; 109 | 110 | /* Chi function */ 111 | for(i=0; i<200; i+=40) 112 | { for(j=0; j<40; ++j) 113 | t[j]=(~s[i+(j+8)%40]) & s[i+(j+16)%40]; 114 | for(j=0; j<40; ++j) s[i+j]^=t[j]; 115 | } 116 | 117 | /* Iota function */ 118 | k = Iota[round]; 119 | s[0] ^= k & 0x8B; /* bits 0, 1, 3, 7 */ 120 | s[1] ^= (k<<3)&0x80; /* bit 15 */ 121 | s[3] ^= (k<<2)&0x80; /* bit 31 */ 122 | s[7] ^= (k<<1)&0x80; /* bit 63 */ 123 | 124 | } 125 | } 126 | 127 | /* --------------------------------------------------------------------- 128 | 32-bit version of Keccak_f(1600) 129 | --------------------------------------------------------------------- 130 | */ 131 | void Keccak_f_32(uint32_t s[50]) 132 | { uint32_t t[10]; 133 | uint8_t i, j, round, k; 134 | 135 | for(round=0; round<24; ++round) 136 | { /* Theta function */ 137 | for(i=0; i<10; ++i) 138 | t[i] = s[i] ^ s[10+i] ^ s[20+i] ^ s[30+i] ^ s[40+i]; 139 | for(i=0; i<5; ++i) 140 | for(j=8, k=2; ; j%=10, k=(k+2)%10) 141 | { *s++ ^= t[j++] ^ ((t[k]<<1)|(t[k+1]>>31)); 142 | *s++ ^= t[j++] ^ ((t[k+1]<<1)|(t[k]>>31)); 143 | if(j==8) break; 144 | } 145 | s -= 50; 146 | 147 | /* Rho function */ 148 | for(i=2; i<50; i+=2) 149 | { k = Rho[i>>1] & 0x1f; 150 | t[0] = (s[i+1] << k) | (s[i] >> (32-k)); 151 | t[1] = (s[i] << k) | (s[i+1] >> (32-k)); 152 | k = Rho[i>>1] >> 5; 153 | s[i] = t[1-k], s[i+1] = t[k]; 154 | } 155 | 156 | /* Pi function */ 157 | for(i=2, t[0]=s[2], t[1]=s[3]; (j=(Pi[i>>1]<<1))>2; i=j) 158 | s[i]=s[j], s[i+1]=s[j+1]; 159 | s[i]=t[0], s[i+1]=t[1]; 160 | 161 | /* Chi function */ 162 | for(i=0; i<5; ++i, s+=10) 163 | { for(j=0; j<10; ++j) 164 | t[j] = (~s[(j+2)%10]) & s[(j+4)%10]; 165 | for(j=0; j<10; ++j) 166 | s[j] ^= t[j]; 167 | } 168 | s -= 50; 169 | 170 | /* Iota function */ 171 | t[0] = Iota[round]; 172 | s[0] ^= (t[0] | (t[0]<<11) | (t[0]<<26)) & 0x8000808B; 173 | s[1] ^= (t[0]<<25) & 0x80000000; 174 | } 175 | } 176 | 177 | -------------------------------------------------------------------------------- /C_alg/KeccakP-1600-3gpp.h: -------------------------------------------------------------------------------- 1 | /* ----------------------------------------------------------------------- 2 | * code extracted from 3GPP TS 35.231, annex E for Keccak core functions 3 | * https://portal.3gpp.org/desktopmodules/Specifications/SpecificationDetails.aspx?specificationId=2402 4 | *-----------------------------------------------------------------------*/ 5 | 6 | /* this is the trick to make the code cross-platform 7 | * at least, Win32 / Linux */ 8 | 9 | #if defined(_WIN32) || defined(__WIN32__) 10 | # include 11 | # define EXPORTIT __declspec(dllexport) 12 | #else 13 | # define EXPORTIT 14 | #endif 15 | 16 | #include 17 | 18 | /*------------------------------------------------------------------------ 19 | * KeccakP-1600-3gpp.h 20 | *------------------------------------------------------------------------*/ 21 | 22 | EXPORTIT void Keccak_f_8 (uint8_t s[200]); 23 | EXPORTIT void Keccak_f_32(uint32_t s[50]); 24 | EXPORTIT void Keccak_f_64(uint64_t s[25]); 25 | 26 | -------------------------------------------------------------------------------- /C_alg/Makefile: -------------------------------------------------------------------------------- 1 | CC?=gcc 2 | OPTS=-c -O2 -Wall -Wno-unused-function -fPIC $(CFLAGS) $(CPPFLAGS) 3 | SHARED_OPTS=-shared -fPIC 4 | SOURCES=Kasumi.c SNOW_3G.c ZUC.c KeccakP-1600-3gpp.c comp128.c 5 | OBJECTS=$(SOURCES:.c=.o) 6 | 7 | LIBS=Kasumi SNOW_3G ZUC KeccakP-1600-3gpp comp128 8 | 9 | .PHONY: all 10 | all: $(OBJECTS) 11 | 12 | $(OBJECTS): %.o: %.c 13 | $(CC) $(OPTS) $< -o $@ 14 | $(CC) $(SHARED_OPTS) -o $*.so $< 15 | 16 | clean: 17 | rm *.so 18 | rm *.o 19 | -------------------------------------------------------------------------------- /C_alg/SNOW_3G.h: -------------------------------------------------------------------------------- 1 | /* ----------------------------------------------------------------------- 2 | * code extracted from ETSI / SAGE specification of the 3GPP Confidentiality and Integrity Algorithms UEA2 & UIA2. 3 | * Document 2: SNOW 3G Specification. Version 1.1 from the 6th September 2006, annex 4. 4 | * https://www.gsma.com/security/wp-content/uploads/2019/05/snow3gspec.pdf 5 | * code extracted from ETSI / SAGE specification of the 3GPP Confidentiality and Integrity Algorithms UEA2 & UIA2. 6 | * Document 1: UEA2 and UIA2 Specification. Version 2.1 from the 16th March 2009, annex 4. 7 | * https://www.gsma.com/security/wp-content/uploads/2019/05/uea2uia2d1v21.pdf 8 | * 9 | * All updated SNOW 3G specifications maybe found on the GSMA website: 10 | * https://www.gsma.com/security/security-algorithms/ 11 | *-----------------------------------------------------------------------*/ 12 | 13 | /* this is the trick to make the code cross-platform 14 | * at least, Win32 / Linux */ 15 | 16 | #if defined(_WIN32) || defined(__WIN32__) 17 | # include 18 | # define EXPORTIT __declspec(dllexport) 19 | #else 20 | # define EXPORTIT 21 | #endif 22 | 23 | /*--------------------------------------------------------- 24 | * SNOW_3G.h 25 | *---------------------------------------------------------*/ 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | typedef unsigned char u8; 33 | typedef unsigned int u32; 34 | typedef unsigned long long u64; 35 | 36 | /* Initialization. 37 | * Input k[4]: Four 32-bit words making up 128-bit key. 38 | * Input IV[4]: Four 32-bit words making 128-bit initialization variable. 39 | * Output: All the LFSRs and FSM are initialized for key generation. 40 | * See Section 4.1. 41 | */ 42 | 43 | EXPORTIT void Initialize(u32 k[4], u32 IV[4]); 44 | 45 | /* Generation of Keystream. 46 | * input n: number of 32-bit words of keystream. 47 | * input z: space for the generated keystream, assumes 48 | * memory is allocated already. 49 | * output: generated keystream which is filled in z 50 | * See section 4.2. 51 | */ 52 | 53 | EXPORTIT void GenerateKeystream(u32 n, u32 *z); 54 | 55 | /* f8. 56 | * Input key: 128 bit Confidentiality Key. 57 | * Input count:32-bit Count, Frame dependent input. 58 | * Input bearer: 5-bit Bearer identity (in the LSB side). 59 | * Input dir:1 bit, direction of transmission. 60 | * Input data: length number of bits, input bit stream. 61 | * Input length: 32 bit Length, i.e., the number of bits to be encrypted or 62 | * decrypted. 63 | * Output data: Output bit stream. Assumes data is suitably memory 64 | * allocated. 65 | * Encrypts/decrypts blocks of data between 1 and 2^32 bits in length as 66 | * defined in Section 3. 67 | */ 68 | 69 | EXPORTIT void f8( u8 *key, u32 count, u32 bearer, u32 dir, \ 70 | u8 *data, u32 length ); 71 | 72 | /* f9. 73 | * Input key: 128 bit Integrity Key. 74 | * Input count:32-bit Count, Frame dependent input. 75 | * Input fresh: 32-bit Random number. 76 | * Input dir:1 bit, direction of transmission (in the LSB). 77 | * Input data: length number of bits, input bit stream. 78 | * Input length: 64 bit Length, i.e., the number of bits to be MAC'd. 79 | * Output : 32 bit block used as MAC 80 | * Generates 32-bit MAC using UIA2 algorithm as defined in Section 4. 81 | */ 82 | 83 | EXPORTIT u8* f9( u8* key, u32 count, u32 fresh, u32 dir, \ 84 | u8 *data, u64 length); 85 | -------------------------------------------------------------------------------- /C_alg/ZUC.c: -------------------------------------------------------------------------------- 1 | /* ----------------------------------------------------------------------- 2 | * code extracted from ETSI / SAGE specification of the 3GPP Confidentiality and Integrity Algorithms 128-EEA3 & 128-EIA3. 3 | * Document 2: ZUC Specification. Version 1.6 from 28th June 2011, appendix A. 4 | * https://www.gsma.com/security/wp-content/uploads/2019/05/eea3eia3zucv16.pdf 5 | * code extracted from ETSI / SAGE specification of the 3GPP Confidentiality and Integrity Algorithms 128-EEA3 & 128-EIA3. 6 | * Document 1: 128-EEA3 and 128-EIA3 Specification. Version 1.7 from the 30th December 2011, annex 1. 7 | * https://www.gsma.com/security/wp-content/uploads/2019/05/EEA3_EIA3_specification_v1_8.pdf 8 | * (warning: only link to version 1.9 exists) 9 | * 10 | * All updated ZUC specifications maybe found on the GSMA website: 11 | * https://www.gsma.com/security/security-algorithms/ 12 | *-----------------------------------------------------------------------*/ 13 | 14 | /*--------------------------------------------- 15 | * ZUC / EEA3 / EIA3 : LTE security algorithm 16 | *--------------------------------------------*/ 17 | 18 | #include "ZUC.h" 19 | 20 | /*-------------------------------------------- 21 | * ZUC keystream generator algorithm 22 | *------------------------------------------*/ 23 | 24 | /* the state registers of LFSR */ 25 | u32 LFSR_S0; 26 | u32 LFSR_S1; 27 | u32 LFSR_S2; 28 | u32 LFSR_S3; 29 | u32 LFSR_S4; 30 | u32 LFSR_S5; 31 | u32 LFSR_S6; 32 | u32 LFSR_S7; 33 | u32 LFSR_S8; 34 | u32 LFSR_S9; 35 | u32 LFSR_S10; 36 | u32 LFSR_S11; 37 | u32 LFSR_S12; 38 | u32 LFSR_S13; 39 | u32 LFSR_S14; 40 | u32 LFSR_S15; 41 | 42 | /* the registers of F */ 43 | u32 F_R1; 44 | u32 F_R2; 45 | 46 | /* the outputs of BitReorganization */ 47 | u32 BRC_X0; 48 | u32 BRC_X1; 49 | u32 BRC_X2; 50 | u32 BRC_X3; 51 | 52 | /* the s-boxes */ 53 | u8 S0[256] = { 54 | 0x3e,0x72,0x5b,0x47,0xca,0xe0,0x00,0x33,0x04,0xd1,0x54,0x98,0x09,0xb9,0x6d,0xcb, 55 | 0x7b,0x1b,0xf9,0x32,0xaf,0x9d,0x6a,0xa5,0xb8,0x2d,0xfc,0x1d,0x08,0x53,0x03,0x90, 56 | 0x4d,0x4e,0x84,0x99,0xe4,0xce,0xd9,0x91,0xdd,0xb6,0x85,0x48,0x8b,0x29,0x6e,0xac, 57 | 0xcd,0xc1,0xf8,0x1e,0x73,0x43,0x69,0xc6,0xb5,0xbd,0xfd,0x39,0x63,0x20,0xd4,0x38, 58 | 0x76,0x7d,0xb2,0xa7,0xcf,0xed,0x57,0xc5,0xf3,0x2c,0xbb,0x14,0x21,0x06,0x55,0x9b, 59 | 0xe3,0xef,0x5e,0x31,0x4f,0x7f,0x5a,0xa4,0x0d,0x82,0x51,0x49,0x5f,0xba,0x58,0x1c, 60 | 0x4a,0x16,0xd5,0x17,0xa8,0x92,0x24,0x1f,0x8c,0xff,0xd8,0xae,0x2e,0x01,0xd3,0xad, 61 | 0x3b,0x4b,0xda,0x46,0xeb,0xc9,0xde,0x9a,0x8f,0x87,0xd7,0x3a,0x80,0x6f,0x2f,0xc8, 62 | 0xb1,0xb4,0x37,0xf7,0x0a,0x22,0x13,0x28,0x7c,0xcc,0x3c,0x89,0xc7,0xc3,0x96,0x56, 63 | 0x07,0xbf,0x7e,0xf0,0x0b,0x2b,0x97,0x52,0x35,0x41,0x79,0x61,0xa6,0x4c,0x10,0xfe, 64 | 0xbc,0x26,0x95,0x88,0x8a,0xb0,0xa3,0xfb,0xc0,0x18,0x94,0xf2,0xe1,0xe5,0xe9,0x5d, 65 | 0xd0,0xdc,0x11,0x66,0x64,0x5c,0xec,0x59,0x42,0x75,0x12,0xf5,0x74,0x9c,0xaa,0x23, 66 | 0x0e,0x86,0xab,0xbe,0x2a,0x02,0xe7,0x67,0xe6,0x44,0xa2,0x6c,0xc2,0x93,0x9f,0xf1, 67 | 0xf6,0xfa,0x36,0xd2,0x50,0x68,0x9e,0x62,0x71,0x15,0x3d,0xd6,0x40,0xc4,0xe2,0x0f, 68 | 0x8e,0x83,0x77,0x6b,0x25,0x05,0x3f,0x0c,0x30,0xea,0x70,0xb7,0xa1,0xe8,0xa9,0x65, 69 | 0x8d,0x27,0x1a,0xdb,0x81,0xb3,0xa0,0xf4,0x45,0x7a,0x19,0xdf,0xee,0x78,0x34,0x60 70 | }; 71 | 72 | u8 S1[256] = { 73 | 0x55,0xc2,0x63,0x71,0x3b,0xc8,0x47,0x86,0x9f,0x3c,0xda,0x5b,0x29,0xaa,0xfd,0x77, 74 | 0x8c,0xc5,0x94,0x0c,0xa6,0x1a,0x13,0x00,0xe3,0xa8,0x16,0x72,0x40,0xf9,0xf8,0x42, 75 | 0x44,0x26,0x68,0x96,0x81,0xd9,0x45,0x3e,0x10,0x76,0xc6,0xa7,0x8b,0x39,0x43,0xe1, 76 | 0x3a,0xb5,0x56,0x2a,0xc0,0x6d,0xb3,0x05,0x22,0x66,0xbf,0xdc,0x0b,0xfa,0x62,0x48, 77 | 0xdd,0x20,0x11,0x06,0x36,0xc9,0xc1,0xcf,0xf6,0x27,0x52,0xbb,0x69,0xf5,0xd4,0x87, 78 | 0x7f,0x84,0x4c,0xd2,0x9c,0x57,0xa4,0xbc,0x4f,0x9a,0xdf,0xfe,0xd6,0x8d,0x7a,0xeb, 79 | 0x2b,0x53,0xd8,0x5c,0xa1,0x14,0x17,0xfb,0x23,0xd5,0x7d,0x30,0x67,0x73,0x08,0x09, 80 | 0xee,0xb7,0x70,0x3f,0x61,0xb2,0x19,0x8e,0x4e,0xe5,0x4b,0x93,0x8f,0x5d,0xdb,0xa9, 81 | 0xad,0xf1,0xae,0x2e,0xcb,0x0d,0xfc,0xf4,0x2d,0x46,0x6e,0x1d,0x97,0xe8,0xd1,0xe9, 82 | 0x4d,0x37,0xa5,0x75,0x5e,0x83,0x9e,0xab,0x82,0x9d,0xb9,0x1c,0xe0,0xcd,0x49,0x89, 83 | 0x01,0xb6,0xbd,0x58,0x24,0xa2,0x5f,0x38,0x78,0x99,0x15,0x90,0x50,0xb8,0x95,0xe4, 84 | 0xd0,0x91,0xc7,0xce,0xed,0x0f,0xb4,0x6f,0xa0,0xcc,0xf0,0x02,0x4a,0x79,0xc3,0xde, 85 | 0xa3,0xef,0xea,0x51,0xe6,0x6b,0x18,0xec,0x1b,0x2c,0x80,0xf7,0x74,0xe7,0xff,0x21, 86 | 0x5a,0x6a,0x54,0x1e,0x41,0x31,0x92,0x35,0xc4,0x33,0x07,0x0a,0xba,0x7e,0x0e,0x34, 87 | 0x88,0xb1,0x98,0x7c,0xf3,0x3d,0x60,0x6c,0x7b,0xca,0xd3,0x1f,0x32,0x65,0x04,0x28, 88 | 0x64,0xbe,0x85,0x9b,0x2f,0x59,0x8a,0xd7,0xb0,0x25,0xac,0xaf,0x12,0x03,0xe2,0xf2 89 | }; 90 | 91 | /* the constants D */ 92 | u32 EK_d[16] = { 93 | 0x44D7, 0x26BC, 0x626B, 0x135E, 0x5789, 0x35E2, 0x7135, 0x09AF, 94 | 0x4D78, 0x2F13, 0x6BC4, 0x1AF1, 0x5E26, 0x3C4D, 0x789A, 0x47AC 95 | }; 96 | 97 | /* c = a + b mod (2^31 - E1) */ 98 | u32 AddM(u32 a, u32 b) 99 | { 100 | u32 c = a + b; 101 | return (c & 0x7FFFFFFF) + (c >> 31); 102 | } 103 | 104 | /* LFSR with initialization mode */ 105 | 106 | #define MulByPow2(x, k) ((((x) << k) | ((x) >> (31 - k))) & 0x7FFFFFFF) 107 | 108 | void LFSRWithInitialisationMode(u32 u) 109 | { 110 | u32 f, v; 111 | f = LFSR_S0; 112 | 113 | v = MulByPow2(LFSR_S0, 8); 114 | f = AddM(f, v); 115 | v = MulByPow2(LFSR_S4, 20); 116 | f = AddM(f, v); 117 | v = MulByPow2(LFSR_S10, 21); 118 | f = AddM(f, v); 119 | v = MulByPow2(LFSR_S13, 17); 120 | f = AddM(f, v); 121 | v = MulByPow2(LFSR_S15, 15); 122 | f = AddM(f, v); 123 | 124 | f = AddM(f, u); 125 | 126 | /* update the state */ 127 | LFSR_S0 = LFSR_S1; 128 | LFSR_S1 = LFSR_S2; 129 | LFSR_S2 = LFSR_S3; 130 | LFSR_S3 = LFSR_S4; 131 | LFSR_S4 = LFSR_S5; 132 | LFSR_S5 = LFSR_S6; 133 | LFSR_S6 = LFSR_S7; 134 | LFSR_S7 = LFSR_S8; 135 | LFSR_S8 = LFSR_S9; 136 | LFSR_S9 = LFSR_S10; 137 | LFSR_S10 = LFSR_S11; 138 | LFSR_S11 = LFSR_S12; 139 | LFSR_S12 = LFSR_S13; 140 | LFSR_S13 = LFSR_S14; 141 | LFSR_S14 = LFSR_S15; 142 | LFSR_S15 = f; 143 | } 144 | 145 | /* LFSR with work mode */ 146 | void LFSRWithWorkMode(void) 147 | { 148 | u32 f, v; 149 | f = LFSR_S0; 150 | 151 | v = MulByPow2(LFSR_S0, 8); 152 | f = AddM(f, v); 153 | v = MulByPow2(LFSR_S4, 20); 154 | f = AddM(f, v); 155 | v = MulByPow2(LFSR_S10, 21); 156 | f = AddM(f, v); 157 | v = MulByPow2(LFSR_S13, 17); 158 | f = AddM(f, v); 159 | v = MulByPow2(LFSR_S15, 15); 160 | f = AddM(f, v); 161 | 162 | /* update the state */ 163 | LFSR_S0 = LFSR_S1; 164 | LFSR_S1 = LFSR_S2; 165 | LFSR_S2 = LFSR_S3; 166 | LFSR_S3 = LFSR_S4; 167 | LFSR_S4 = LFSR_S5; 168 | LFSR_S5 = LFSR_S6; 169 | LFSR_S6 = LFSR_S7; 170 | LFSR_S7 = LFSR_S8; 171 | LFSR_S8 = LFSR_S9; 172 | LFSR_S9 = LFSR_S10; 173 | LFSR_S10 = LFSR_S11; 174 | LFSR_S11 = LFSR_S12; 175 | LFSR_S12 = LFSR_S13; 176 | LFSR_S13 = LFSR_S14; 177 | LFSR_S14 = LFSR_S15; 178 | LFSR_S15 = f; 179 | } 180 | 181 | /* BitReorganization */ 182 | void BitReorganization(void) 183 | { 184 | BRC_X0 = ((LFSR_S15 & 0x7FFF8000) << 1) | (LFSR_S14 & 0xFFFF); 185 | BRC_X1 = ((LFSR_S11 & 0xFFFF) << 16) | (LFSR_S9 >> 15); 186 | BRC_X2 = ((LFSR_S7 & 0xFFFF) << 16) | (LFSR_S5 >> 15); 187 | BRC_X3 = ((LFSR_S2 & 0xFFFF) << 16) | (LFSR_S0 >> 15); 188 | } 189 | 190 | #define ROT(a, k) (((a) << k) | ((a) >> (32 - k))) 191 | 192 | /* L1 */ 193 | u32 L1(u32 X) 194 | { 195 | return (X ^ ROT(X, 2) ^ ROT(X, 10) ^ ROT(X, 18) ^ ROT(X, 24)); 196 | } 197 | 198 | /* L2 */ 199 | u32 L2(u32 X) 200 | { 201 | return (X ^ ROT(X, 8) ^ ROT(X, 14) ^ ROT(X, 22) ^ ROT(X, 30)); 202 | } 203 | 204 | #define MAKEU32(a, b, c, d) (((u32)(a) << 24) | ((u32)(b) << 16) | ((u32)(c) << 8) | ((u32)(d))) 205 | 206 | /* F */ 207 | u32 F(void) 208 | { 209 | u32 W, W1, W2, u, v; 210 | 211 | W = (BRC_X0 ^ F_R1) + F_R2; 212 | W1 = F_R1 + BRC_X1; 213 | W2 = F_R2 ^ BRC_X2; 214 | 215 | u = L1((W1 << 16) | (W2 >> 16)); 216 | v = L2((W2 << 16) | (W1 >> 16)); 217 | 218 | F_R1 = MAKEU32(S0[u >> 24], S1[(u >> 16) & 0xFF], 219 | S0[(u >> 8) & 0xFF], S1[u & 0xFF]); 220 | F_R2 = MAKEU32(S0[v >> 24], S1[(v >> 16) & 0xFF], 221 | S0[(v >> 8) & 0xFF], S1[v & 0xFF]); 222 | 223 | return W; 224 | } 225 | 226 | #define MAKEU31(a, b, c) (((u32)(a) << 23) | ((u32)(b) << 8) | (u32)(c)) 227 | 228 | /* initialize */ 229 | EXPORTIT void Initialization(u8* k, u8* iv) 230 | { 231 | u32 w, nCount; 232 | 233 | /* expand key */ 234 | LFSR_S0 = MAKEU31(k[0], EK_d[0], iv[0]); 235 | LFSR_S1 = MAKEU31(k[1], EK_d[1], iv[1]); 236 | LFSR_S2 = MAKEU31(k[2], EK_d[2], iv[2]); 237 | LFSR_S3 = MAKEU31(k[3], EK_d[3], iv[3]); 238 | LFSR_S4 = MAKEU31(k[4], EK_d[4], iv[4]); 239 | LFSR_S5 = MAKEU31(k[5], EK_d[5], iv[5]); 240 | LFSR_S6 = MAKEU31(k[6], EK_d[6], iv[6]); 241 | LFSR_S7 = MAKEU31(k[7], EK_d[7], iv[7]); 242 | LFSR_S8 = MAKEU31(k[8], EK_d[8], iv[8]); 243 | LFSR_S9 = MAKEU31(k[9], EK_d[9], iv[9]); 244 | LFSR_S10 = MAKEU31(k[10], EK_d[10], iv[10]); 245 | LFSR_S11 = MAKEU31(k[11], EK_d[11], iv[11]); 246 | LFSR_S12 = MAKEU31(k[12], EK_d[12], iv[12]); 247 | LFSR_S13 = MAKEU31(k[13], EK_d[13], iv[13]); 248 | LFSR_S14 = MAKEU31(k[14], EK_d[14], iv[14]); 249 | LFSR_S15 = MAKEU31(k[15], EK_d[15], iv[15]); 250 | 251 | /* set F_R1 and F_R2 to zero */ 252 | F_R1 = 0; 253 | F_R2 = 0; 254 | nCount = 32; 255 | while (nCount > 0) 256 | { 257 | BitReorganization(); 258 | w = F(); 259 | LFSRWithInitialisationMode(w >> 1); 260 | nCount --; 261 | } 262 | } 263 | 264 | EXPORTIT void GenerateKeystream(u32* pKeystream, u32 KeystreamLen) 265 | { 266 | u32 i; 267 | BitReorganization(); 268 | F(); /* discard the output of F */ 269 | LFSRWithWorkMode(); 270 | 271 | for (i = 0; i < KeystreamLen; i ++) 272 | { 273 | BitReorganization(); 274 | pKeystream[i] = F() ^ BRC_X3; 275 | LFSRWithWorkMode(); 276 | } 277 | } 278 | 279 | /* The ZUC algorithm, see ref. [3]*/ 280 | void ZUC(u8* k, u8* iv, u32* ks, u32 len) 281 | { 282 | /* The initialization of ZUC, see page 17 of ref. [3]*/ 283 | Initialization(k, iv); 284 | /* The procedure of generating keystream of ZUC, see page 18 of ref. [3]*/ 285 | GenerateKeystream(ks, len); 286 | } 287 | /* end of ZUC.c */ 288 | 289 | /*----------------------------------------------------- 290 | * EEA3 291 | *---------------------------------------------------*/ 292 | 293 | /* 294 | * EEA3: LTE Encryption Algorithm 3 295 | * EEA3.c 296 | */ 297 | EXPORTIT void EEA3(u8* CK, u32 COUNT, u32 BEARER, u32 DIRECTION, 298 | u32 LENGTH, u32* M, u32* C) 299 | { 300 | u32 *z, L, i; 301 | u8 IV[16]; 302 | u32 lastbits = (32-(LENGTH%32))%32; 303 | 304 | L = (LENGTH+31)/32; 305 | z = (u32 *) malloc(L*sizeof(u32)); 306 | 307 | IV[0] = (COUNT>>24) & 0xFF; 308 | IV[1] = (COUNT>>16) & 0xFF; 309 | IV[2] = (COUNT>>8) & 0xFF; 310 | IV[3] = COUNT & 0xFF; 311 | 312 | IV[4] = ((BEARER << 3) | ((DIRECTION&1)<<2)) & 0xFC; 313 | IV[5] = 0; 314 | IV[6] = 0; 315 | IV[7] = 0; 316 | 317 | IV[8] = IV[0]; 318 | IV[9] = IV[1]; 319 | IV[10] = IV[2]; 320 | IV[11] = IV[3]; 321 | 322 | IV[12] = IV[4]; 323 | IV[13] = IV[5]; 324 | IV[14] = IV[6]; 325 | IV[15] = IV[7]; 326 | 327 | ZUC(CK, IV, z, L); 328 | 329 | for (i=0; i>(32-ti)); 358 | return WORD; 359 | } 360 | 361 | u8 GET_BIT(u32 * DATA, u32 i) 362 | { 363 | return (DATA[i/32] & (1<<(31-(i%32)))) ? 1 : 0; 364 | } 365 | 366 | EXPORTIT void EIA3(u8* IK, u32 COUNT, u32 BEARER, u32 DIRECTION, 367 | u32 LENGTH, u32* M, u32* MAC) 368 | { 369 | u32 *z, N, L, T, i; 370 | u8 IV[16]; 371 | 372 | IV[0] = (COUNT>>24) & 0xFF; 373 | IV[1] = (COUNT>>16) & 0xFF; 374 | IV[2] = (COUNT>>8) & 0xFF; 375 | IV[3] = COUNT & 0xFF; 376 | 377 | IV[4] = (BEARER << 3) & 0xF8; 378 | IV[5] = IV[6] = IV[7] = 0; 379 | 380 | IV[8] = ((COUNT>>24) & 0xFF) ^ ((DIRECTION&1)<<7); 381 | IV[9] = (COUNT>>16) & 0xFF; 382 | IV[10] = (COUNT>>8) & 0xFF; 383 | IV[11] = COUNT & 0xFF; 384 | 385 | IV[12] = IV[4]; 386 | IV[13] = IV[5]; 387 | IV[14] = IV[6] ^ ((DIRECTION&1)<<7); 388 | IV[15] = IV[7]; 389 | 390 | N = LENGTH + 64; 391 | L = (N + 31) / 32; 392 | z = (u32 *) malloc(L*sizeof(u32)); 393 | ZUC(IK, IV, z, L); 394 | 395 | T = 0; 396 | for (i=0; i 19 | # define EXPORTIT __declspec(dllexport) 20 | #else 21 | # define EXPORTIT 22 | #endif 23 | 24 | #include 25 | 26 | /*------------------------------------------------------------------------ 27 | * ZUC.h 28 | * Code taken from the ZUC specification 29 | * available on the GSMA website 30 | *------------------------------------------------------------------------*/ 31 | 32 | /* type definition from */ 33 | typedef unsigned char u8; 34 | typedef unsigned int u32; 35 | 36 | /* 37 | * ZUC keystream generator 38 | * k: secret key (input, 16 bytes) 39 | * iv: initialization vector (input, 16 bytes) 40 | * Keystream: produced keystream (output, variable length) 41 | * KeystreamLen: length in 32-bit words requested for the keystream (input) 42 | */ 43 | EXPORTIT void Initialization(u8* k, u8* iv); 44 | EXPORTIT void GenerateKeystream(u32* pKeystream, u32 KeystreamLen); 45 | 46 | /* 47 | * CK: ciphering key 48 | * COUNT: frame counter 49 | * BEARER: radio bearer 50 | * DIRECTION 51 | * LENGTH: length of the frame in bits 52 | * M: original message (input) 53 | * C: processed message (output) 54 | */ 55 | EXPORTIT void EEA3(u8* CK, u32 COUNT, u32 BEARER, u32 DIRECTION, 56 | u32 LENGTH, u32* M, u32* C); 57 | 58 | /* 59 | * IK: integrity key 60 | * COUNT: frame counter 61 | * BEARER: radio bearer 62 | * DIRECTION 63 | * LENGTH: length of the frame in bits 64 | * M: original message (input) 65 | * MAC: processed message MAC (output) 66 | */ 67 | EXPORTIT void EIA3(u8* IK, u32 COUNT, u32 BEARER, u32 DIRECTION, 68 | u32 LENGTH, u32* M, u32* MAC); 69 | -------------------------------------------------------------------------------- /C_alg/comp128.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This program is is free software; you can redistribute it and/or modify 3 | * it under the terms of the GNU General Public License, version 2 if the 4 | * License as published by the Free Software Foundation. 5 | * 6 | * This program is distributed in the hope that it will be useful, 7 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | * GNU General Public License for more details. 10 | * 11 | * You should have received a copy of the GNU General Public License 12 | * along with this program; if not, write to the Free Software 13 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 14 | */ 15 | 16 | /** 17 | * $Id$ 18 | * @file comp128.c 19 | * @brief Implementations of comp128v1, comp128v2, comp128v3 algorithms 20 | * 21 | * Comp128v1 was inspired by code from: 22 | * Marc Briceno , Ian Goldberg , 23 | * and David Wagner 24 | * 25 | * But it has been fully rewritten (Sylvain Munaut ) from various PDFs found online 26 | * describing the algorithm because the licence of the code referenced above was unclear. 27 | * A comment snippet from the original code is included below, it describes where the doc came 28 | * from and how the algorithm was reverse engineered. 29 | * 30 | * Comp128v2 & v3 is a port of the python code from: 31 | * http://www.hackingprojects.net/ 32 | * 33 | * @note The above GPL license only applies to comp128v1, the license for comp128v2 and comp128v3 is unknown. 34 | * 35 | * @copyright 2013 The FreeRADIUS server project 36 | * @copyright 2013 Hacking projects [http://www.hackingprojects.net/] 37 | * @copyright 2009 Sylvain Munaut 38 | */ 39 | 40 | #include "comp128.h" 41 | #include 42 | /* 512 bytes */ 43 | static uint8_t const comp128v1_t0[] = { 44 | 102, 177, 186, 162, 2, 156, 112, 75, 55, 25, 8, 12, 251, 193, 246, 188, 45 | 109, 213, 151, 53, 42, 79, 191, 115, 233, 242, 164, 223, 209, 148, 108, 161, 46 | 252, 37, 244, 47, 64, 211, 6, 237, 185, 160, 139, 113, 76, 138, 59, 70, 47 | 67, 26, 13, 157, 63, 179, 221, 30, 214, 36, 166, 69, 152, 124, 207, 116, 48 | 247, 194, 41, 84, 71, 1, 49, 14, 95, 35, 169, 21, 96, 78, 215, 225, 49 | 182, 243, 28, 92, 201, 118, 4, 74, 248, 128, 17, 11, 146, 132, 245, 48, 50 | 149, 90, 120, 39, 87, 230, 106, 232, 175, 19, 126, 190, 202, 141, 137, 176, 51 | 250, 27, 101, 40, 219, 227, 58, 20, 51, 178, 98, 216, 140, 22, 32, 121, 52 | 61, 103, 203, 72, 29, 110, 85, 212, 180, 204, 150, 183, 15, 66, 172, 196, 53 | 56, 197, 158, 0, 100, 45, 153, 7, 144, 222, 163, 167, 60, 135, 210, 231, 54 | 174, 165, 38, 249, 224, 34, 220, 229, 217, 208, 241, 68, 206, 189, 125, 255, 55 | 239, 54, 168, 89, 123, 122, 73, 145, 117, 234, 143, 99, 129, 200, 192, 82, 56 | 104, 170, 136, 235, 93, 81, 205, 173, 236, 94, 105, 52, 46, 228, 198, 5, 57 | 57, 254, 97, 155, 142, 133, 199, 171, 187, 50, 65, 181, 127, 107, 147, 226, 58 | 184, 218, 131, 33, 77, 86, 31, 44, 88, 62, 238, 18, 24, 43, 154, 23, 59 | 80, 159, 134, 111, 9, 114, 3, 91, 16, 130, 83, 10, 195, 240, 253, 119, 60 | 177, 102, 162, 186, 156, 2, 75, 112, 25, 55, 12, 8, 193, 251, 188, 246, 61 | 213, 109, 53, 151, 79, 42, 115, 191, 242, 233, 223, 164, 148, 209, 161, 108, 62 | 37, 252, 47, 244, 211, 64, 237, 6, 160, 185, 113, 139, 138, 76, 70, 59, 63 | 26, 67, 157, 13, 179, 63, 30, 221, 36, 214, 69, 166, 124, 152, 116, 207, 64 | 194, 247, 84, 41, 1, 71, 14, 49, 35, 95, 21, 169, 78, 96, 225, 215, 65 | 243, 182, 92, 28, 118, 201, 74, 4, 128, 248, 11, 17, 132, 146, 48, 245, 66 | 90, 149, 39, 120, 230, 87, 232, 106, 19, 175, 190, 126, 141, 202, 176, 137, 67 | 27, 250, 40, 101, 227, 219, 20, 58, 178, 51, 216, 98, 22, 140, 121, 32, 68 | 103, 61, 72, 203, 110, 29, 212, 85, 204, 180, 183, 150, 66, 15, 196, 172, 69 | 197, 56, 0, 158, 45, 100, 7, 153, 222, 144, 167, 163, 135, 60, 231, 210, 70 | 165, 174, 249, 38, 34, 224, 229, 220, 208, 217, 68, 241, 189, 206, 255, 125, 71 | 54, 239, 89, 168, 122, 123, 145, 73, 234, 117, 99, 143, 200, 129, 82, 192, 72 | 170, 104, 235, 136, 81, 93, 173, 205, 94, 236, 52, 105, 228, 46, 5, 198, 73 | 254, 57, 155, 97, 133, 142, 171, 199, 50, 187, 181, 65, 107, 127, 226, 147, 74 | 218, 184, 33, 131, 86, 77, 44, 31, 62, 88, 18, 238, 43, 24, 23, 154, 75 | 159, 80, 111, 134, 114, 9, 91, 3, 130, 16, 10, 83, 240, 195, 119, 253}; 76 | 77 | /* 256 bytes */ 78 | static uint8_t const comp128v1_t1[] = { 79 | 19, 11, 80, 114, 43, 1, 69, 94, 39, 18, 127, 117, 97, 3, 85, 43, 80 | 27, 124, 70, 83, 47, 71, 63, 10, 47, 89, 79, 4, 14, 59, 11, 5, 81 | 35, 107, 103, 68, 21, 86, 36, 91, 85, 126, 32, 50, 109, 94, 120, 6, 82 | 53, 79, 28, 45, 99, 95, 41, 34, 88, 68, 93, 55, 110, 125, 105, 20, 83 | 90, 80, 76, 96, 23, 60, 89, 64, 121, 56, 14, 74, 101, 8, 19, 78, 84 | 76, 66, 104, 46, 111, 50, 32, 3, 39, 0, 58, 25, 92, 22, 18, 51, 85 | 57, 65, 119, 116, 22, 109, 7, 86, 59, 93, 62, 110, 78, 99, 77, 67, 86 | 12, 113, 87, 98, 102, 5, 88, 33, 38, 56, 23, 8, 75, 45, 13, 75, 87 | 95, 63, 28, 49, 123, 120, 20, 112, 44, 30, 15, 98, 106, 2, 103, 29, 88 | 82, 107, 42, 124, 24, 30, 41, 16, 108, 100, 117, 40, 73, 40, 7, 114, 89 | 82, 115, 36, 112, 12, 102, 100, 84, 92, 48, 72, 97, 9, 54, 55, 74, 90 | 113, 123, 17, 26, 53, 58, 4, 9, 69, 122, 21, 118, 42, 60, 27, 73, 91 | 118, 125, 34, 15, 65, 115, 84, 64, 62, 81, 70, 1, 24, 111, 121, 83, 92 | 104, 81, 49, 127, 48, 105, 31, 10, 6, 91, 87, 37, 16, 54, 116, 126, 93 | 31, 38, 13, 0, 72, 106, 77, 61, 26, 67, 46, 29, 96, 37, 61, 52, 94 | 101, 17, 44, 108, 71, 52, 66, 57, 33, 51, 25, 90, 2, 119, 122, 35}; 95 | 96 | /* 128 bytes */ 97 | static uint8_t const comp128v1_t2[] = { 98 | 52, 50, 44, 6, 21, 49, 41, 59, 39, 51, 25, 32, 51, 47, 52, 43, 99 | 37, 4, 40, 34, 61, 12, 28, 4, 58, 23, 8, 15, 12, 22, 9, 18, 100 | 55, 10, 33, 35, 50, 1, 43, 3, 57, 13, 62, 14, 7, 42, 44, 59, 101 | 62, 57, 27, 6, 8, 31, 26, 54, 41, 22, 45, 20, 39, 3, 16, 56, 102 | 48, 2, 21, 28, 36, 42, 60, 33, 34, 18, 0, 11, 24, 10, 17, 61, 103 | 29, 14, 45, 26, 55, 46, 11, 17, 54, 46, 9, 24, 30, 60, 32, 0, 104 | 20, 38, 2, 30, 58, 35, 1, 16, 56, 40, 23, 48, 13, 19, 19, 27, 105 | 31, 53, 47, 38, 63, 15, 49, 5, 37, 53, 25, 36, 63, 29, 5, 7}; 106 | 107 | /* 64 bytes */ 108 | static uint8_t const comp128v1_t3[] = { 109 | 1, 5, 29, 6, 25, 1, 18, 23, 17, 19, 0, 9, 24, 25, 6, 31, 110 | 28, 20, 24, 30, 4, 27, 3, 13, 15, 16, 14, 18, 4, 3, 8, 9, 111 | 20, 0, 12, 26, 21, 8, 28, 2, 29, 2, 15, 7, 11, 22, 14, 10, 112 | 17, 21, 12, 30, 26, 27, 16, 31, 11, 7, 13, 23, 10, 5, 22, 19}; 113 | 114 | /* 32 bytes */ 115 | static uint8_t const comp128v1_t4[] = { 116 | 15, 12, 10, 4, 1, 14, 11, 7, 5, 0, 14, 7, 1, 2, 13, 8, 117 | 10, 3, 4, 9, 6, 0, 3, 2, 5, 6, 8, 9, 11, 13, 15, 12}; 118 | 119 | static uint8_t const *_comp128_table[] = { comp128v1_t0, comp128v1_t1, comp128v1_t2, comp128v1_t3, comp128v1_t4 }; 120 | 121 | /* 256 bytes */ 122 | static uint8_t const comp128v23_t0[] = { 123 | 197, 235, 60, 151, 98, 96, 3, 100, 248, 118, 42, 117, 172, 211, 181, 203, 124 | 61, 126, 156, 87, 149, 224, 55, 132, 186, 63, 238, 255, 85, 83, 152, 33, 125 | 160, 184, 210, 219, 159, 11, 180, 194, 130, 212, 147, 5, 215, 92, 27, 46, 126 | 113, 187, 52, 25, 185, 79, 221, 48, 70, 31, 101, 15, 195, 201, 50, 222, 127 | 137, 233, 229, 106, 122, 183, 178, 177, 144, 207, 234, 182, 37, 254, 227, 231, 128 | 54, 209, 133, 65, 202, 69, 237, 220, 189, 146, 120, 68, 21, 125, 38, 30, 129 | 2, 155, 53, 196, 174, 176, 51, 246, 167, 76, 110, 20, 82, 121, 103, 112, 130 | 56, 173, 49, 217, 252, 0, 114, 228, 123, 12, 93, 161, 253, 232, 240, 175, 131 | 67, 128, 22, 158, 89, 18, 77, 109, 190, 17, 62, 4, 153, 163, 59, 145, 132 | 138, 7, 74, 205, 10, 162, 80, 45, 104, 111, 150, 214, 154, 28, 191, 169, 133 | 213, 88, 193, 198, 200, 245, 39, 164, 124, 84, 78, 1, 188, 170, 23, 86, 134 | 226, 141, 32, 6, 131, 127, 199, 40, 135, 16, 57, 71, 91, 225, 168, 242, 135 | 206, 97, 166, 44, 14, 90, 236, 239, 230, 244, 223, 108, 102, 119, 148, 251, 136 | 29, 216, 8, 9, 249, 208, 24, 105, 94, 34, 64, 95, 115, 72, 134, 204, 137 | 43, 247, 243, 218, 47, 58, 73, 107, 241, 179, 116, 66, 36, 143, 81, 250, 138 | 139, 19, 13, 142, 140, 129, 192, 99, 171, 157, 136, 41, 75, 35, 165, 26}; 139 | 140 | /* 256 bytes */ 141 | static uint8_t const comp128v23_t1[] = { 142 | 170, 42, 95, 141, 109, 30, 71, 89, 26, 147, 231, 205, 239, 212, 124, 129, 143 | 216, 79, 15, 185, 153, 14, 251, 162, 0, 241, 172, 197, 43, 10, 194, 235, 144 | 6, 20, 72, 45, 143, 104, 161, 119, 41, 136, 38, 189, 135, 25, 93, 18, 145 | 224, 171, 252, 195, 63, 19, 58, 165, 23, 55, 133, 254, 214, 144, 220, 178, 146 | 156, 52, 110, 225, 97, 183, 140, 39, 53, 88, 219, 167, 16, 198, 62, 222, 147 | 76, 139, 175, 94, 51, 134, 115, 22, 67, 1, 249, 217, 3, 5, 232, 138, 148 | 31, 56, 116, 163, 70, 128, 234, 132, 229, 184, 244, 13, 34, 73, 233, 154, 149 | 179, 131, 215, 236, 142, 223, 27, 57, 246, 108, 211, 8, 253, 85, 66, 245, 150 | 193, 78, 190, 4, 17, 7, 150, 127, 152, 213, 37, 186, 2, 243, 46, 169, 151 | 68, 101, 60, 174, 208, 158, 176, 69, 238, 191, 90, 83, 166, 125, 77, 59, 152 | 21, 92, 49, 151, 168, 99, 9, 50, 146, 113, 117, 228, 65, 230, 40, 82, 153 | 54, 237, 227, 102, 28, 36, 107, 24, 44, 126, 206, 201, 61, 114, 164, 207, 154 | 181, 29, 91, 64, 221, 255, 48, 155, 192, 111, 180, 210, 182, 247, 203, 148, 155 | 209, 98, 173, 11, 75, 123, 250, 118, 32, 47, 240, 202, 74, 177, 100, 80, 156 | 196, 33, 248, 86, 157, 137, 120, 130, 84, 204, 122, 81, 242, 188, 200, 149, 157 | 226, 218, 160, 187, 106, 35, 87, 105, 96, 145, 199, 159, 12, 121, 103, 112}; 158 | 159 | static inline void _comp128_compression_round(uint8_t *x, int n, const uint8_t *tbl) 160 | { 161 | int i, j, m, a, b, y, z; 162 | m = 4 - n; 163 | for (i = 0; i < (1 << n); i++) { 164 | for (j = 0; j < (1 << m); j++) { 165 | a = j + i * (2 << m); 166 | b = a + (1 << m); 167 | y = (x[a] + (x[b] << 1)) & ((32 << m) - 1); 168 | z = ((x[a] << 1) + x[b]) & ((32 << m) - 1); 169 | x[a] = tbl[y]; 170 | x[b] = tbl[z]; 171 | } 172 | } 173 | } 174 | 175 | static inline void _comp128_compression(uint8_t *x) 176 | { 177 | int n; 178 | for (n = 0; n < 5; n++) { 179 | _comp128_compression_round(x, n, _comp128_table[n]); 180 | } 181 | } 182 | 183 | static inline void _comp128_bitsfrombytes(uint8_t *x, uint8_t *bits) 184 | { 185 | int i; 186 | 187 | memset(bits, 0x00, 128); 188 | for (i = 0; i < 128; i++) { 189 | if (x[i >> 2] & (1 << (3 - (i & 3)))) { 190 | bits[i] = 1; 191 | } 192 | } 193 | } 194 | 195 | static inline void _comp128_permutation(uint8_t *x, uint8_t *bits) 196 | { 197 | int i; 198 | memset(&x[16], 0x00, 16); 199 | for (i = 0; i < 128; i++) { 200 | x[(i >> 3) + 16] |= bits[(i * 17) & 127] << (7 - (i & 7)); 201 | } 202 | } 203 | 204 | /** Calculate comp128v1 sres and kc from ki and rand 205 | * 206 | * This code derived from a leaked document from the GSM standards. 207 | * Some missing pieces were filled in by reverse-engineering a working SIM. 208 | * We have verified that this is the correct COMP128 algorithm. 209 | * 210 | * The first page of the document identifies it as 211 | * _Technical Information: GSM System Security Study_. 212 | * 10-1617-01, 10th June 1988. 213 | * The bottom of the title page is marked 214 | * Racal Research Ltd. 215 | * Worton Drive, Worton Grange Industrial Estate, 216 | * Reading, Berks. RG2 0SB, England. 217 | * Telephone: Reading (0734) 868601 Telex: 847152 218 | * The relevant bits are in Part I, Section 20 (pages 66--67). Enjoy! 219 | * 220 | * Note: There are three typos in the spec (discovered by reverse-engineering). 221 | * First, "z = (2 * x[n] + x[n]) mod 2^(9-j)" should clearly read 222 | * "z = (2 * x[m] + x[n]) mod 2^(9-j)". 223 | * Second, the "k" loop in the "Form bits from bytes" section is severely 224 | * botched: the k index should run only from 0 to 3, and clearly the range 225 | * on "the (8-k)th bit of byte j" is also off (should be 0..7, not 1..8, 226 | * to be consistent with the subsequent section). 227 | * Third, SRES is taken from the first 8 nibbles of x[], not the last 8 as 228 | * claimed in the document. (And the document doesn't specify how Kc is 229 | * derived, but that was also easily discovered with reverse engineering.) 230 | * All of these typos have been corrected in the following code. 231 | * 232 | * @param[out] sres 4 byte value derived from ki and rand. 233 | * @param[out] kc 12 byte value derived from ki and rand. 234 | * @param[in] ki known only by the SIM and AuC (us in this case). 235 | * @param[in] rand 16 bytes of randomness. 236 | */ 237 | EXPORTIT void comp128v1(uint8_t *sres, uint8_t *kc, uint8_t const *ki, uint8_t const *rand) 238 | { 239 | int i; 240 | uint8_t x[32], bits[128]; 241 | 242 | /* x[16-31] = RAND */ 243 | memcpy(&x[16], rand, 16); 244 | 245 | /* 246 | * Round 1-7 247 | */ 248 | for (i=0; i < 7; i++) { 249 | /* x[0-15] = Ki */ 250 | memcpy(x, ki, 16); 251 | 252 | /* Compression */ 253 | _comp128_compression(x); 254 | 255 | /* FormBitFromBytes */ 256 | _comp128_bitsfrombytes(x, bits); 257 | 258 | /* Permutation */ 259 | _comp128_permutation(x, bits); 260 | } 261 | 262 | /* 263 | * Round 8 (final) 264 | * x[0-15] = Ki 265 | */ 266 | memcpy(x, ki, 16); 267 | 268 | /* Compression */ 269 | _comp128_compression(x); 270 | 271 | /* Output stage */ 272 | for (i = 0; i < 8; i += 2) { 273 | sres[i >> 1] = x[i] << 4 | x[i + 1]; 274 | } 275 | 276 | for (i = 0; i < 12; i += 2) { 277 | kc[i>>1] = (x[i + 18] << 6) | 278 | (x[i + 19] << 2) | 279 | (x[i + 20] >> 2); 280 | } 281 | 282 | kc[6] = (x[30] << 6) | (x[31] << 2); 283 | kc[7] = 0; 284 | } 285 | 286 | static void _comp128v23(uint8_t *rand, uint8_t const *kxor) 287 | { 288 | uint8_t temp[16]; 289 | uint8_t km_rm[32]; 290 | 291 | int i, j, k, z; 292 | 293 | memset(&temp, 0, sizeof(temp)); 294 | memcpy(km_rm, rand, 16); 295 | memcpy(km_rm + 16, kxor, 16); 296 | memset(rand, 0, 16); 297 | 298 | for (i = 0; i < 5; i++) { 299 | j = 0; 300 | 301 | for (z = 0; z < 16; z++) { 302 | temp[z] = comp128v23_t0[comp128v23_t1[km_rm[16 + z]] ^ km_rm[z]]; 303 | } 304 | 305 | while ((1 << i) > j) { 306 | k = 0; 307 | 308 | while ((1 << (4 - i)) > k) { 309 | km_rm[(((2 * k) + 1) << i) + j] = 310 | comp128v23_t0[comp128v23_t1[temp[(k << i) + j]] ^ (km_rm[(k << i) + 16 + j])]; 311 | km_rm[(k << (i + 1)) + j] = temp[(k << i) + j]; 312 | k++; 313 | } 314 | j++; 315 | } 316 | } 317 | 318 | for (i = 0; i < 16; i++) { 319 | for (j = 0; j < 8; j++) { 320 | rand[i] = rand[i] ^ (((km_rm[(19 * (j + 8 * i) + 19) % 256 / 8] >> (3 * j + 3) % 8) & 1) << j); 321 | } 322 | } 323 | } 324 | 325 | /** Calculate comp128v2 or comp128v3 sres and kc from ki and rand 326 | * 327 | * @param[out] sres 4 byte value derived from ki and rand. 328 | * @param[out] kc 8 byte value derived from ki and rand. 329 | * @param[in] ki known only by the SIM and AuC (us in this case). 330 | * @param[in] rand 16 bytes of randomness. 331 | * @param[in] v2 if true we use version comp128-2 else we use comp128-3. 332 | */ 333 | EXPORTIT void comp128v23(uint8_t *sres, uint8_t *kc, uint8_t const *ki, uint8_t const *rand, bool v2) 334 | { 335 | uint8_t k_mix[16]; 336 | uint8_t rand_mix[16]; 337 | uint8_t katyvasz[16]; 338 | uint8_t buffer[16]; 339 | 340 | /* Every day IM suffling... */ 341 | int i; 342 | 343 | for (i = 0; i < 8; i++) { 344 | k_mix[i] = ki[15 - i]; 345 | k_mix[15 - i] = ki[i]; 346 | } 347 | 348 | for (i = 0; i < 8; i++) { 349 | rand_mix[i] = rand[15 - i]; 350 | rand_mix[15 - i] = rand[i]; 351 | } 352 | 353 | for (i = 0; i < 16; i++) { 354 | katyvasz[i] = k_mix[i] ^ rand_mix[i]; 355 | } 356 | 357 | for (i = 0; i < 8; i++) { 358 | _comp128v23(rand_mix, katyvasz); 359 | } 360 | 361 | for (i = 0; i < 16; i++) { 362 | buffer[i] = rand_mix[15 - i]; 363 | } 364 | 365 | if (v2) { 366 | buffer[15] = 0x00; 367 | buffer[14] = 4 * (buffer[14] >> 2); 368 | } 369 | 370 | for (i = 0; i < 4; i++) { 371 | buffer[8 + i - 4] = buffer[8 + i]; 372 | buffer[8 + i] = buffer[8 + i + 4]; 373 | } 374 | 375 | /* 376 | * The algorithm uses 16 bytes until this point, but only 12 bytes are effective 377 | * also 12 bytes coming out from the SIM card. 378 | */ 379 | memcpy(sres, buffer, 4); 380 | memcpy(kc, buffer + 4, 8); 381 | } 382 | 383 | #if 0 384 | #include 385 | #include 386 | static int hextoint(char x) 387 | { 388 | x = toupper(x); 389 | if (x >= 'A' && x <= 'F') { 390 | return x-'A' + 10; 391 | } else if (x >= '0' && x <= '9') { 392 | return x-'0'; 393 | } 394 | 395 | fprintf(stderr, "Bad input.\n"); 396 | 397 | exit(1); 398 | } 399 | 400 | int main(int argc, char **argv) 401 | { 402 | uint8_t rand[16], key[16], sres[4], kc[8]; 403 | int version; 404 | int i; 405 | 406 | if ((argc != 4) || 407 | (strlen(argv[1]) != 34) || (strlen(argv[2]) != 34) || 408 | (strncmp(argv[1], "0x", 2) != 0) || (strncmp(argv[2], "0x", 2) != 0) || 409 | !(version = atoi(argv[3]))) { 410 | error: 411 | fprintf(stderr, "Usage: %s 0x 0x [1|2|3]\n", argv[0]); 412 | exit(1); 413 | } 414 | 415 | for (i = 0; i < 16; i++) { 416 | key[i] = (hextoint(argv[1][(2 * i) + 2]) << 4) | hextoint(argv[1][(2 * i) + 3]); 417 | } 418 | 419 | for (i = 0; i < 16; i++) { 420 | rand[i] = (hextoint(argv[2][(2 * i) + 2]) << 4) | hextoint(argv[2][(2 * i) + 3]); 421 | } 422 | 423 | switch (version) { 424 | case 3: 425 | comp128v23(sres, kc, key, rand, false); 426 | break; 427 | 428 | case 2: 429 | comp128v23(sres, kc, key, rand, true); 430 | break; 431 | 432 | case 1: 433 | comp128v1(sres, kc, key, rand); 434 | break; 435 | 436 | default: 437 | fprintf(stderr, "Invalid version, must be 1, 2 or 3"); 438 | goto error; 439 | } 440 | 441 | /* Output in vector format ,, */ 442 | for (i = 0; i < 16; i++) { 443 | printf("%02X", key[i]); 444 | } 445 | printf(","); 446 | for (i = 0; i < 16; i++) { 447 | printf("%02X", rand[i]); 448 | } 449 | printf(","); 450 | for (i = 0; i < 4; i++) { 451 | printf("%02X", sres[i]); 452 | } 453 | for (i = 0; i < 8; i++) { 454 | printf("%02X", kc[i]); 455 | } 456 | printf("\n"); 457 | 458 | return 0; 459 | } 460 | #endif 461 | 462 | -------------------------------------------------------------------------------- /C_alg/comp128.h: -------------------------------------------------------------------------------- 1 | /* this is the trick to make the code cross-platform 2 | * at least, Win32 / Linux */ 3 | 4 | #if defined(_WIN32) || defined(__WIN32__) 5 | # include 6 | # include 7 | # define EXPORTIT __declspec(dllexport) 8 | typedef unsigned char uint8_t; 9 | 10 | #else 11 | # define EXPORTIT 12 | # include 13 | # include 14 | # include 15 | #endif 16 | 17 | #ifndef _COMP128_H 18 | #define _COMP128_H 19 | 20 | EXPORTIT void comp128v1(uint8_t *sres, uint8_t *kc, const uint8_t *ki, const uint8_t *rand); 21 | EXPORTIT void comp128v23(uint8_t *sres, uint8_t *kc, uint8_t const *ki, uint8_t const *rand, bool v2); 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /C_py/pycomp128.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Software Name : CryptoMobile 3 | * Version : 0.2.0 4 | * 5 | * Copyright © 2017. Benoit Michau. ANSSI. 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License version 2 as published 9 | * by the Free Software Foundation. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You will find a copy of the terms and conditions of the GNU General Public 17 | * License version 2 in the "license.txt" file or 18 | * see http://www.gnu.org/licenses/ or write to the Free Software Foundation, 19 | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 20 | * 21 | *-------------------------------------------------------- 22 | * File Name : CryptoMobile/pycomp128.c 23 | * Created : 2017-07-27 24 | *-------------------------------------------------------- 25 | */ 26 | 27 | #include 28 | #include "../C_alg/comp128.h" 29 | 30 | 31 | /* Python 2 and 3 initialization mess */ 32 | 33 | 34 | struct module_state { 35 | PyObject *error; 36 | }; 37 | 38 | #if PY_MAJOR_VERSION >= 3 39 | 40 | #define GETSTATE(m) ((struct module_state*)PyModule_GetState(m)) 41 | 42 | #else 43 | 44 | #define GETSTATE(m) (&_state) 45 | static struct module_state _state; 46 | 47 | #endif 48 | 49 | static PyObject * error_out(PyObject *m) { 50 | struct module_state *st = GETSTATE(m); 51 | PyErr_SetString(st->error, "something bad happened"); 52 | return NULL; 53 | } 54 | 55 | static PyObject* pycomp128v1(PyObject* dummy, PyObject* args); 56 | static PyObject* pycomp128v2(PyObject* dummy, PyObject* args); 57 | static PyObject* pycomp128v3(PyObject* dummy, PyObject* args); 58 | 59 | static char pycomp128v1_doc[] = 60 | "comp128v1(ki [16 bytes], rand [16 bytes]) -> (sres [4 bytes], kc [8 bytes])"; 61 | static char pycomp128v2_doc[] = 62 | "comp128v2(ki [16 bytes], rand [16 bytes]) -> (sres [4 bytes], kc [8 bytes])"; 63 | static char pycomp128v3_doc[] = 64 | "comp128v3(ki [16 bytes], rand [16 bytes]) -> (sres [4 bytes], kc [8 bytes])"; 65 | 66 | static PyMethodDef pycomp128_methods[] = 67 | { 68 | //{exported name, function, args handling, doc string} 69 | {"error_out", (PyCFunction)error_out, METH_NOARGS, NULL}, 70 | {"comp128v1", pycomp128v1, METH_VARARGS, pycomp128v1_doc}, 71 | {"comp128v2", pycomp128v2, METH_VARARGS, pycomp128v2_doc}, 72 | {"comp128v3", pycomp128v3, METH_VARARGS, pycomp128v3_doc}, 73 | { NULL, NULL, 0, NULL } 74 | }; 75 | 76 | #if PY_MAJOR_VERSION >= 3 77 | 78 | static int pycomp128_traverse(PyObject *m, visitproc visit, void *arg) { 79 | Py_VISIT(GETSTATE(m)->error); 80 | return 0; 81 | } 82 | 83 | static int pycomp128_clear(PyObject *m) { 84 | Py_CLEAR(GETSTATE(m)->error); 85 | return 0; 86 | } 87 | 88 | static struct PyModuleDef moduledef = { 89 | PyModuleDef_HEAD_INIT, 90 | "pycomp128", 91 | "bindings for Comp128 GSM authentication algorithms", 92 | sizeof(struct module_state), 93 | pycomp128_methods, 94 | NULL, 95 | pycomp128_traverse, 96 | pycomp128_clear, 97 | NULL 98 | }; 99 | 100 | #define INITERROR return NULL 101 | 102 | PyObject * PyInit_pycomp128(void) 103 | 104 | #else 105 | 106 | #define INITERROR return 107 | 108 | void initpycomp128(void) 109 | 110 | #endif 111 | 112 | { 113 | #if PY_MAJOR_VERSION >= 3 114 | 115 | PyObject *module = PyModule_Create(&moduledef); 116 | 117 | #else 118 | 119 | PyObject *module = Py_InitModule4( 120 | "pycomp128", 121 | pycomp128_methods, 122 | "bindings for Comp128 GSM authentication algorithms", 123 | 0, 124 | PYTHON_API_VERSION); 125 | 126 | #endif 127 | 128 | if (module == NULL) 129 | INITERROR; 130 | struct module_state *st = GETSTATE(module); 131 | 132 | st->error = PyErr_NewException("pycomp128.Error", NULL, NULL); 133 | if (st->error == NULL) { 134 | Py_DECREF(module); 135 | INITERROR; 136 | } 137 | 138 | #if PY_MAJOR_VERSION >= 3 139 | 140 | return module; 141 | 142 | #endif 143 | } 144 | 145 | 146 | /* pycomp128 binding to comp128.h */ 147 | 148 | 149 | static PyObject* pycomp128v1(PyObject* dummy, PyObject* args) 150 | { 151 | PyObject* ret = 0; 152 | 153 | // input: ki, rand (bytes buffer -> const uint8_t*) 154 | Py_buffer ki; 155 | Py_buffer rand; 156 | // output: sres, kc (uint8_t* -> bytes buffer) 157 | uint8_t sres[4]; 158 | uint8_t kc[8]; 159 | 160 | if (! PyArg_ParseTuple(args, "z*z*", &ki, &rand)) 161 | return NULL; 162 | 163 | if ( (ki.len != 16) || (rand.len != 16) ) { 164 | PyErr_SetString(PyExc_ValueError, "invalid args"); 165 | return NULL; 166 | } 167 | 168 | //void comp128v1(uint8_t *sres, uint8_t *kc, const uint8_t *ki, const uint8_t *rand); 169 | comp128v1(sres, kc, (const uint8_t *)ki.buf, (const uint8_t *)rand.buf); 170 | 171 | ret = PyTuple_New(2); 172 | PyTuple_SetItem(ret, 0, PyBytes_FromStringAndSize((char *)sres, 4)); 173 | PyTuple_SetItem(ret, 1, PyBytes_FromStringAndSize((char *)kc, 8)); 174 | return ret; 175 | }; 176 | 177 | 178 | static PyObject* pycomp128v2(PyObject* dummy, PyObject* args) 179 | { 180 | PyObject* ret = 0; 181 | 182 | // input: ki, rand (bytes buffer -> const uint8_t*) 183 | Py_buffer ki; 184 | Py_buffer rand; 185 | // output: sres, kc (uint8_t* -> bytes buffer) 186 | uint8_t sres[4]; 187 | uint8_t kc[8]; 188 | 189 | if (! PyArg_ParseTuple(args, "z*z*", &ki, &rand)) 190 | return NULL; 191 | 192 | if ( (ki.len != 16) || (rand.len != 16) ) { 193 | PyErr_SetString(PyExc_ValueError, "invalid args"); 194 | return NULL; 195 | } 196 | 197 | //void comp128v23(uint8_t *sres, uint8_t *kc, uint8_t const *ki, uint8_t const *rand, bool v2); 198 | comp128v23(sres, kc, (const uint8_t *)ki.buf, (const uint8_t *)rand.buf, true); 199 | 200 | ret = PyTuple_New(2); 201 | PyTuple_SetItem(ret, 0, PyBytes_FromStringAndSize((char *)sres, 4)); 202 | PyTuple_SetItem(ret, 1, PyBytes_FromStringAndSize((char *)kc, 8)); 203 | return ret; 204 | }; 205 | 206 | 207 | static PyObject* pycomp128v3(PyObject* dummy, PyObject* args) 208 | { 209 | PyObject* ret = 0; 210 | 211 | // input: ki, rand (bytes buffer -> const uint8_t*) 212 | Py_buffer ki; 213 | Py_buffer rand; 214 | // output: sres, kc (uint8_t* -> bytes buffer) 215 | uint8_t sres[4]; 216 | uint8_t kc[8]; 217 | 218 | if (! PyArg_ParseTuple(args, "z*z*", &ki, &rand)) 219 | return NULL; 220 | 221 | if ( (ki.len != 16) || (rand.len != 16) ) { 222 | PyErr_SetString(PyExc_ValueError, "invalid args"); 223 | return NULL; 224 | } 225 | 226 | //void comp128v23(uint8_t *sres, uint8_t *kc, uint8_t const *ki, uint8_t const *rand, bool v2); 227 | comp128v23(sres, kc, (const uint8_t *)ki.buf, (const uint8_t *)rand.buf, false); 228 | 229 | ret = PyTuple_New(2); 230 | PyTuple_SetItem(ret, 0, PyBytes_FromStringAndSize((char *)sres, 4)); 231 | PyTuple_SetItem(ret, 1, PyBytes_FromStringAndSize((char *)kc, 8)); 232 | return ret; 233 | }; 234 | -------------------------------------------------------------------------------- /C_py/pykasumi.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Software Name : CryptoMobile 3 | * Version : 0.2.0 4 | * 5 | * Copyright © 2017. Benoit Michau. ANSSI. 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License version 2 as published 9 | * by the Free Software Foundation. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You will find a copy of the terms and conditions of the GNU General Public 17 | * License version 2 in the "license.txt" file or 18 | * see http://www.gnu.org/licenses/ or write to the Free Software Foundation, 19 | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 20 | * 21 | *-------------------------------------------------------- 22 | * File Name : CryptoMobile/pykasumi.c 23 | * Created : 2017-07-27 24 | *-------------------------------------------------------- 25 | */ 26 | 27 | #include 28 | #include "../C_alg/Kasumi.h" 29 | 30 | 31 | /* Python 2 and 3 initialization mess */ 32 | 33 | 34 | struct module_state { 35 | PyObject *error; 36 | }; 37 | 38 | #if PY_MAJOR_VERSION >= 3 39 | 40 | #define GETSTATE(m) ((struct module_state*)PyModule_GetState(m)) 41 | 42 | #else 43 | 44 | #define GETSTATE(m) (&_state) 45 | static struct module_state _state; 46 | 47 | #endif 48 | 49 | static PyObject * error_out(PyObject *m) { 50 | struct module_state *st = GETSTATE(m); 51 | PyErr_SetString(st->error, "something bad happened"); 52 | return NULL; 53 | } 54 | 55 | static PyObject* pykasumi_keyschedule(PyObject* dummy, PyObject* args); 56 | static PyObject* pykasumi_kasumi(PyObject* dummy, PyObject* args); 57 | static PyObject* pykasumi_f8(PyObject* dummy, PyObject* args); 58 | static PyObject* pykasumi_f9(PyObject* dummy, PyObject* args); 59 | 60 | static char pykasumi_keyschedule_doc[] = 61 | "kasumi_keyschedule(key [16 bytes]) -> None"; 62 | static char pykasumi_kasumi_doc[] = 63 | "kasumi_kasumi(clear_block [8 bytes]) -> ciphered_block [8 bytes]"; 64 | static char pykasumi_f8_doc[] = 65 | "kasumi_f8(ck [16 bytes], count [uint32], bearer [uint32], dir [0 or 1], "\ 66 | "data_in [bytes], length [int, length in bits]) -> data_out [bytes]"; 67 | static char pykasumi_f9_doc[] = 68 | "kasumi_f9(ik [16 bytes], count [uint32], bearer [uint32], dir [0 or 1], "\ 69 | "data_in [bytes], length [int, length in bits]) -> mac [4 bytes]"; 70 | 71 | static PyMethodDef pykasumi_methods[] = 72 | { 73 | //{exported name, function, args handling, doc string} 74 | {"error_out", (PyCFunction)error_out, METH_NOARGS, NULL}, 75 | {"kasumi_keyschedule", pykasumi_keyschedule, METH_VARARGS, pykasumi_keyschedule_doc}, 76 | {"kasumi_kasumi", pykasumi_kasumi, METH_VARARGS, pykasumi_kasumi_doc}, 77 | {"kasumi_f8", pykasumi_f8, METH_VARARGS, pykasumi_f8_doc}, 78 | {"kasumi_f9", pykasumi_f9, METH_VARARGS, pykasumi_f9_doc}, 79 | { NULL, NULL, 0, NULL } 80 | }; 81 | 82 | #if PY_MAJOR_VERSION >= 3 83 | 84 | static int pykasumi_traverse(PyObject *m, visitproc visit, void *arg) { 85 | Py_VISIT(GETSTATE(m)->error); 86 | return 0; 87 | } 88 | 89 | static int pykasumi_clear(PyObject *m) { 90 | Py_CLEAR(GETSTATE(m)->error); 91 | return 0; 92 | } 93 | 94 | static struct PyModuleDef moduledef = { 95 | PyModuleDef_HEAD_INIT, 96 | "pykasumi", 97 | "bindings for Kasumi F8 and F9 UMTS cryptographic functions", 98 | sizeof(struct module_state), 99 | pykasumi_methods, 100 | NULL, 101 | pykasumi_traverse, 102 | pykasumi_clear, 103 | NULL 104 | }; 105 | 106 | #define INITERROR return NULL 107 | 108 | PyObject * PyInit_pykasumi(void) 109 | 110 | #else 111 | 112 | #define INITERROR return 113 | 114 | void initpykasumi(void) 115 | 116 | #endif 117 | 118 | { 119 | #if PY_MAJOR_VERSION >= 3 120 | 121 | PyObject *module = PyModule_Create(&moduledef); 122 | 123 | #else 124 | 125 | PyObject *module = Py_InitModule4( 126 | "pykasumi", 127 | pykasumi_methods, 128 | "bindings for Kasumi F8 and F9 UMTS cryptographic functions", 129 | 0, 130 | PYTHON_API_VERSION); 131 | 132 | #endif 133 | 134 | if (module == NULL) 135 | INITERROR; 136 | struct module_state *st = GETSTATE(module); 137 | 138 | st->error = PyErr_NewException("pykasumi.Error", NULL, NULL); 139 | if (st->error == NULL) { 140 | Py_DECREF(module); 141 | INITERROR; 142 | } 143 | 144 | #if PY_MAJOR_VERSION >= 3 145 | 146 | return module; 147 | 148 | #endif 149 | } 150 | 151 | 152 | /* pykasumi binding to Kasumi.h */ 153 | 154 | 155 | static PyObject* pykasumi_keyschedule(PyObject* dummy, PyObject* args) 156 | { 157 | // input: key (bytes buffer -> u8 *) 158 | Py_buffer key; 159 | 160 | if (! PyArg_ParseTuple(args, "z*", &key)) 161 | return NULL; 162 | 163 | if (key.len != 16) 164 | { 165 | PyErr_SetString(PyExc_ValueError, "invalid args"); 166 | return NULL; 167 | }; 168 | 169 | //void KeySchedule( u8 *key ); 170 | KeySchedule((u8 *)key.buf); 171 | 172 | Py_RETURN_NONE; 173 | }; 174 | 175 | 176 | static PyObject* pykasumi_kasumi(PyObject* dummy, PyObject* args) 177 | { 178 | PyObject* ret = 0; 179 | 180 | // input: data (bytes buffer -> u8 *) 181 | Py_buffer data_py; 182 | // output 183 | u8 data[8]; 184 | 185 | if (! PyArg_ParseTuple(args, "z*", &data_py)) 186 | return NULL; 187 | 188 | if (data_py.len != 8) 189 | { 190 | PyErr_SetString(PyExc_ValueError, "invalid args"); 191 | return NULL; 192 | }; 193 | 194 | // duplicate the input buffer in order to not mutate it 195 | memcpy(data, data_py.buf, 8); 196 | 197 | //void Kasumi( u8 *data ); 198 | Kasumi(data); 199 | 200 | ret = PyBytes_FromStringAndSize((char *)data, 8); 201 | return ret; 202 | }; 203 | 204 | 205 | static PyObject* pykasumi_f8(PyObject* dummy, PyObject* args) 206 | { 207 | PyObject* ret = 0; 208 | 209 | // input: key, data (bytes buffer -> u8 *), count, bearer, dir (u32), length (int, in bits) 210 | Py_buffer key; 211 | Py_buffer data_py; 212 | u32 count, bearer, dir; 213 | int length, out_sz; 214 | // output: data (u8 * -> bytes buffer of size length in bits 215 | u8 * data; 216 | 217 | if (! PyArg_ParseTuple(args, "z*IIIz*i", &key, &count, &bearer, &dir, &data_py, &length)) 218 | return NULL; 219 | 220 | // transform length in bits to length in bytes 221 | out_sz = length >> 3; 222 | if (length % 8) 223 | out_sz++; 224 | 225 | if ((key.len != 16) || (dir > 1) || (out_sz > data_py.len)) 226 | { 227 | PyErr_SetString(PyExc_ValueError, "invalid args"); 228 | return NULL; 229 | }; 230 | 231 | // duplicate the input buffer in order to not mutate it 232 | data = (u8 *)malloc(out_sz); 233 | if (data == NULL) 234 | { 235 | PyErr_SetString(PyExc_RuntimeError, "malloc failed"); 236 | return NULL; 237 | }; 238 | memcpy(data, data_py.buf, out_sz); 239 | 240 | //void f8( u8 *key, u32 count, u32 bearer, u32 dir, u8 *data, int length ); 241 | f8((u8 *)key.buf, count, bearer, dir, data, length); 242 | 243 | ret = PyBytes_FromStringAndSize((char *)data, out_sz); 244 | free(data); 245 | data = NULL; 246 | 247 | return ret; 248 | }; 249 | 250 | 251 | static PyObject* pykasumi_f9(PyObject* dummy, PyObject* args) 252 | { 253 | PyObject* ret = 0; 254 | 255 | // input: key, data (bytes buffer -> u8 *), count, fresh, dir (u32), length (int, in bits) 256 | Py_buffer key; 257 | Py_buffer data; 258 | u32 count, fresh, dir; 259 | int length, out_sz; 260 | // output: mac (u8 * -> bytes buffer of size 4) 261 | u8 * mac; 262 | 263 | if (! PyArg_ParseTuple(args, "z*IIIz*i", &key, &count, &fresh, &dir, &data, &length)) 264 | return NULL; 265 | 266 | // transform length in bits to length in bytes 267 | out_sz = length >> 3; 268 | if (length % 8) 269 | out_sz++; 270 | 271 | if ((key.len != 16) || (dir > 1) || (out_sz > data.len)) 272 | { 273 | PyErr_SetString(PyExc_ValueError, "invalid args"); 274 | return NULL; 275 | }; 276 | 277 | //u8 * f9( u8 *key, u32 count, u32 fresh, u32 dir, u8 *data, int length ); 278 | mac = f9((u8 *)key.buf, count, fresh, dir, (u8 *)data.buf, length); 279 | 280 | ret = PyBytes_FromStringAndSize((char *)mac, 4); 281 | return ret; 282 | }; 283 | -------------------------------------------------------------------------------- /C_py/pykeccakp1600.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Software Name : CryptoMobile 3 | * Version : 0.2.0 4 | * 5 | * Copyright 2018. Benoit Michau. P1Sec. 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License version 2 as published 9 | * by the Free Software Foundation. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You will find a copy of the terms and conditions of the GNU General Public 17 | * License version 2 in the "license.txt" file or 18 | * see http://www.gnu.org/licenses/ or write to the Free Software Foundation, 19 | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 20 | * 21 | *-------------------------------------------------------- 22 | * File Name : CryptoMobile/pykeccakp1600.c 23 | * Created : 2018-12-18 24 | *-------------------------------------------------------- 25 | */ 26 | 27 | #include 28 | #include "../C_alg/KeccakP-1600-3gpp.h" 29 | 30 | 31 | /* Python 2 and 3 initialization mess */ 32 | 33 | 34 | struct module_state { 35 | PyObject *error; 36 | }; 37 | 38 | #if PY_MAJOR_VERSION >= 3 39 | 40 | #define GETSTATE(m) ((struct module_state*)PyModule_GetState(m)) 41 | 42 | #else 43 | 44 | #define GETSTATE(m) (&_state) 45 | static struct module_state _state; 46 | 47 | #endif 48 | 49 | static PyObject * error_out(PyObject *m) { 50 | struct module_state *st = GETSTATE(m); 51 | PyErr_SetString(st->error, "something bad happened"); 52 | return NULL; 53 | } 54 | 55 | static PyObject* pykeccakp1600(PyObject* dummy, PyObject* args); 56 | //static PyObject* push_data(PyObject* dummy, PyObject* args); 57 | 58 | static char pykeccakp1600_doc[] = 59 | " pykeccakp1600(data_in [200 bytes]) -> data_out [200 bytes]"; 60 | 61 | static PyMethodDef pykeccakp1600_methods[] = 62 | { 63 | {"error_out", (PyCFunction)error_out, METH_NOARGS, NULL}, 64 | {"pykeccakp1600", pykeccakp1600, METH_VARARGS, pykeccakp1600_doc}, 65 | // {"push_data", push_data, METH_VARARGS, NULL}, 66 | { NULL, NULL, 0, NULL } 67 | }; 68 | 69 | #if PY_MAJOR_VERSION >= 3 70 | 71 | static int pykeccakp1600_traverse(PyObject *m, visitproc visit, void *arg) { 72 | Py_VISIT(GETSTATE(m)->error); 73 | return 0; 74 | } 75 | 76 | static int pykeccakp1600_clear(PyObject *m) { 77 | Py_CLEAR(GETSTATE(m)->error); 78 | return 0; 79 | } 80 | 81 | static struct PyModuleDef moduledef = { 82 | PyModuleDef_HEAD_INIT, 83 | "pykeccakp1600", 84 | "bindings for the Keccak P-1600 permutation cryptographic 64-bit functions", 85 | sizeof(struct module_state), 86 | pykeccakp1600_methods, 87 | NULL, 88 | pykeccakp1600_traverse, 89 | pykeccakp1600_clear, 90 | NULL 91 | }; 92 | 93 | #define INITERROR return NULL 94 | 95 | PyObject * PyInit_pykeccakp1600(void) 96 | 97 | #else 98 | 99 | #define INITERROR return 100 | 101 | void initpykeccakp1600(void) 102 | 103 | #endif 104 | 105 | { 106 | #if PY_MAJOR_VERSION >= 3 107 | 108 | PyObject *module = PyModule_Create(&moduledef); 109 | 110 | #else 111 | 112 | PyObject *module = Py_InitModule4( 113 | "pykeccakp1600", 114 | pykeccakp1600_methods, 115 | "bindings for the Keccak P-1600 permutation cryptographic 64-bit functions", 116 | 0, 117 | PYTHON_API_VERSION); 118 | 119 | #endif 120 | 121 | if (module == NULL) 122 | INITERROR; 123 | struct module_state *st = GETSTATE(module); 124 | 125 | st->error = PyErr_NewException("pykeccakp1600.Error", NULL, NULL); 126 | if (st->error == NULL) { 127 | Py_DECREF(module); 128 | INITERROR; 129 | } 130 | 131 | #if PY_MAJOR_VERSION >= 3 132 | 133 | return module; 134 | 135 | #endif 136 | } 137 | 138 | 139 | // utiliy function required for handling (char *) to (uint64 *) conversion 140 | 141 | uint64_t swap_uint64( uint64_t val ) 142 | { 143 | val = ((val << 8) & 0xFF00FF00FF00FF00ULL ) | ((val >> 8) & 0x00FF00FF00FF00FFULL ); 144 | val = ((val << 16) & 0xFFFF0000FFFF0000ULL ) | ((val >> 16) & 0x0000FFFF0000FFFFULL ); 145 | return (val << 32) | (val >> 32); 146 | } 147 | 148 | /* 149 | pykeccakp1600 binding to the Keccak_f_64() function 150 | as defined in KeccakP-1600-3gpp.h 151 | */ 152 | 153 | static PyObject* pykeccakp1600(PyObject* dummy, PyObject* args) 154 | { 155 | PyObject* ret = 0; 156 | 157 | // input: state (200 bytes buffer -> void *) 158 | Py_buffer data_in; 159 | uint64_t state[25]; 160 | //uint8_t i; 161 | 162 | if (! PyArg_ParseTuple(args, "z*", &data_in)) 163 | return NULL; 164 | 165 | if (data_in.len != 200) 166 | { 167 | PyErr_SetString(PyExc_ValueError, "invalid arg, must be 200 bytes"); 168 | return NULL; 169 | }; 170 | 171 | memcpy(state, data_in.buf, 200); 172 | /* no need to swap bytes actually... who knows ! 173 | for (i=0; i < 25; i++) { 174 | state[i] = swap_uint64(state[i]); 175 | } 176 | */ 177 | 178 | //void Keccak_f_64(uint64 *s) 179 | Keccak_f_64(state); 180 | 181 | /* 182 | for (i=0; i < 25; i++) { 183 | state[i] = swap_uint64(state[i]); 184 | } 185 | */ 186 | ret = PyBytes_FromStringAndSize((char *)state, 200); 187 | 188 | return ret; 189 | }; 190 | 191 | 192 | /* 193 | void PUSH_DATA_64(uint64_t * INOUT, uint8_t * data, uint8_t n, uint8_t location) 194 | { 195 | while(n--) 196 | INOUT[location>>3] |= ((uint64_t)data[n]) << ((location++ & 7)<<3); 197 | }; 198 | 199 | void PUSH_DATA_32(uint32_t * INOUT, uint8_t * data, uint8_t n, uint8_t location) 200 | { 201 | while(n--) 202 | INOUT[location>>2] |= ((uint32_t)data[n]) << ((location++ & 3)<<3); 203 | }; 204 | 205 | void PUSH_DATA_8(uint8_t * INOUT, uint8_t * data, uint8_t n, uint8_t location) 206 | { 207 | while(n--) 208 | INOUT[location++] = data[n]; 209 | }; 210 | 211 | 212 | static PyObject* push_data(PyObject* dummy, PyObject* args) 213 | { 214 | PyObject* ret = 0; 215 | 216 | // input: state (200 bytes buffer -> void *) 217 | Py_buffer data; 218 | uint8_t location; 219 | //uint64_t INOUT[25]; 220 | //uint32_t INOUT[50]; 221 | uint8_t INOUT[200]; 222 | 223 | if (! PyArg_ParseTuple(args, "z*b", &data, &location)) 224 | return NULL; 225 | 226 | if (data.len > 255 || location > 199) 227 | { 228 | PyErr_SetString(PyExc_ValueError, "invalid args"); 229 | return NULL; 230 | }; 231 | 232 | memset(INOUT, 0, 200); 233 | 234 | //PUSH_DATA_64(INOUT, (uint8_t *)data.buf, (uint8_t)(data.len<<3), location); 235 | //PUSH_DATA_32(INOUT, (uint8_t *)data.buf, (uint8_t)(data.len<<3), location); 236 | PUSH_DATA_8 (INOUT, (uint8_t *)data.buf, (uint8_t)(data.len<<3), location); 237 | 238 | ret = PyBytes_FromStringAndSize((char *)INOUT, 200); 239 | return ret; 240 | }; 241 | */ 242 | -------------------------------------------------------------------------------- /C_py/pysnow.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Software Name : CryptoMobile 3 | * Version : 0.2.0 4 | * 5 | * Copyright © 2017. Benoit Michau. ANSSI. 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License version 2 as published 9 | * by the Free Software Foundation. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You will find a copy of the terms and conditions of the GNU General Public 17 | * License version 2 in the "license.txt" file or 18 | * see http://www.gnu.org/licenses/ or write to the Free Software Foundation, 19 | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 20 | * 21 | *-------------------------------------------------------- 22 | * File Name : CryptoMobile/pysnow.c 23 | * Created : 2017-07-27 24 | *-------------------------------------------------------- 25 | */ 26 | 27 | #include 28 | #include "../C_alg/SNOW_3G.h" 29 | 30 | 31 | /* Python 2 and 3 initialization mess */ 32 | 33 | 34 | struct module_state { 35 | PyObject *error; 36 | }; 37 | 38 | #if PY_MAJOR_VERSION >= 3 39 | 40 | #define GETSTATE(m) ((struct module_state*)PyModule_GetState(m)) 41 | 42 | #else 43 | 44 | #define GETSTATE(m) (&_state) 45 | static struct module_state _state; 46 | 47 | #endif 48 | 49 | static PyObject * error_out(PyObject *m) { 50 | struct module_state *st = GETSTATE(m); 51 | PyErr_SetString(st->error, "something bad happened"); 52 | return NULL; 53 | } 54 | 55 | static PyObject* pysnow_initialize(PyObject* dummy, PyObject* args); 56 | static PyObject* pysnow_generatekeystream(PyObject* dummy, PyObject* args); 57 | static PyObject* pysnow_f8(PyObject* dummy, PyObject* args); 58 | static PyObject* pysnow_f9(PyObject* dummy, PyObject* args); 59 | 60 | static char pysnow_initialize_doc[] = 61 | "snow_initialize(key [16 bytes], iv [16 bytes]) -> None"; 62 | static char pysnow_generatekeystream_doc[] = 63 | "snow_generatekeystream(n [uint32, number of 32-bit words]) -> keystream [bytes]"; 64 | static char pysnow_f8_doc[] = 65 | "snow_f8(ck [16 bytes], count [uint32], bearer [uint32], dir [0 or 1], "\ 66 | "data_in [bytes], length [uint32, length in bits]) -> data_out [bytes]"; 67 | static char pysnow_f9_doc[] = 68 | "snow_f9(ik [16 bytes], count [uint32], bearer [uint32], dir [0 or 1], "\ 69 | "data_in [bytes], length [uint32, length in bits]) -> mac [4 bytes]"; 70 | 71 | static PyMethodDef pysnow_methods[] = 72 | { 73 | //{exported name, function, args handling, doc string} 74 | {"error_out", (PyCFunction)error_out, METH_NOARGS, NULL}, 75 | {"snow_initialize", pysnow_initialize, METH_VARARGS, pysnow_initialize_doc}, 76 | {"snow_generatekeystream", pysnow_generatekeystream, METH_VARARGS, pysnow_generatekeystream_doc}, 77 | {"snow_f8", pysnow_f8, METH_VARARGS, pysnow_f8_doc}, 78 | {"snow_f9", pysnow_f9, METH_VARARGS, pysnow_f9_doc}, 79 | { NULL, NULL, 0, NULL } 80 | }; 81 | 82 | #if PY_MAJOR_VERSION >= 3 83 | 84 | static int pysnow_traverse(PyObject *m, visitproc visit, void *arg) { 85 | Py_VISIT(GETSTATE(m)->error); 86 | return 0; 87 | } 88 | 89 | static int pysnow_clear(PyObject *m) { 90 | Py_CLEAR(GETSTATE(m)->error); 91 | return 0; 92 | } 93 | 94 | static struct PyModuleDef moduledef = { 95 | PyModuleDef_HEAD_INIT, 96 | "pysnow", 97 | "bindings for SNOW-3G F8 and F9 UMTS cryptographic functions", 98 | sizeof(struct module_state), 99 | pysnow_methods, 100 | NULL, 101 | pysnow_traverse, 102 | pysnow_clear, 103 | NULL 104 | }; 105 | 106 | #define INITERROR return NULL 107 | 108 | PyObject * PyInit_pysnow(void) 109 | 110 | #else 111 | 112 | #define INITERROR return 113 | 114 | void initpysnow(void) 115 | 116 | #endif 117 | 118 | { 119 | #if PY_MAJOR_VERSION >= 3 120 | 121 | PyObject *module = PyModule_Create(&moduledef); 122 | 123 | #else 124 | 125 | PyObject *module = Py_InitModule4( 126 | "pysnow", 127 | pysnow_methods, 128 | "bindings for SNOW-3G F8 and F9 UMTS cryptographic functions", 129 | 0, 130 | PYTHON_API_VERSION); 131 | 132 | #endif 133 | 134 | if (module == NULL) 135 | INITERROR; 136 | struct module_state *st = GETSTATE(module); 137 | 138 | st->error = PyErr_NewException("pysnow.Error", NULL, NULL); 139 | if (st->error == NULL) { 140 | Py_DECREF(module); 141 | INITERROR; 142 | } 143 | 144 | #if PY_MAJOR_VERSION >= 3 145 | 146 | return module; 147 | 148 | #endif 149 | } 150 | 151 | 152 | /* pysnow binding to SNOW_3G.h */ 153 | 154 | 155 | // utiliy macro and function required for handling (char *) to (u32 *) conversion 156 | 157 | 158 | #define SWAP_BYTES(X) \ 159 | ((((X) & 0xff000000) >> 24) | (((X) & 0x00ff0000) >> 8) | \ 160 | (((X) & 0x0000ff00) << 8) | (((X) & 0x000000ff) << 24)) 161 | 162 | void memcpy_bswap(u32* bufout, char* bufin, u32 n) 163 | { 164 | u32 i; 165 | 166 | // copy bufin into bufout 167 | memcpy(bufout, bufin, 4*n); 168 | 169 | // swap bytes of uint32_t values within bufout 170 | for (i=0; i u8 *) 178 | Py_buffer k_py; 179 | Py_buffer IV_py; 180 | u32 k[4]; 181 | u32 IV[4]; 182 | 183 | if (! PyArg_ParseTuple(args, "z*z*", &k_py, &IV_py)) 184 | return NULL; 185 | 186 | if ((k_py.len != 16) || (IV_py.len != 16)) 187 | { 188 | PyErr_SetString(PyExc_ValueError, "invalid args"); 189 | return NULL; 190 | }; 191 | 192 | // swap u32 bytes from Python buffer into new array 193 | memcpy_bswap(k, (char *)k_py.buf, 4); 194 | memcpy_bswap(IV, (char *)IV_py.buf, 4); 195 | 196 | //void Initialize(u32 k[4], u32 IV[4]); 197 | Initialize(k, IV); 198 | 199 | Py_RETURN_NONE; 200 | }; 201 | 202 | 203 | static PyObject* pysnow_generatekeystream(PyObject* dummy, PyObject* args) 204 | { 205 | PyObject* ret = 0; 206 | u32 i; 207 | 208 | // input: n (int -> u32, number of 32-bits words of keystream) 209 | u32 n; 210 | // output: z (u32 * -> bytes buffer, keystream) 211 | u32 * z; 212 | 213 | if (! PyArg_ParseTuple(args, "I", &n)) 214 | return NULL; 215 | 216 | z = (u32 *)malloc(4*n); 217 | if (z == NULL) 218 | { 219 | PyErr_SetString(PyExc_RuntimeError, "malloc failed"); 220 | return NULL; 221 | }; 222 | 223 | //void GenerateKeystream(u32 n, u32 *z); 224 | GenerateKeystream(n, z); 225 | 226 | // swap u32 bytes on place for z 227 | for (i=0; i u8 *), count, bearer, dir, length (u32) 245 | Py_buffer key; 246 | Py_buffer data_py; 247 | u32 count, bearer, dir, length; 248 | int out_sz; 249 | // output: data (u8 * -> bytes buffer of size length in bits) 250 | u8 * data; 251 | 252 | if (! PyArg_ParseTuple(args, "z*IIIz*I", &key, &count, &bearer, &dir, &data_py, &length)) 253 | return NULL; 254 | 255 | // transform length in bits to length in bytes 256 | out_sz = length >> 3; 257 | if (length % 8) 258 | out_sz++; 259 | 260 | if ((key.len != 16) || (dir > 1) || (out_sz > data_py.len)) 261 | { 262 | PyErr_SetString(PyExc_ValueError, "invalid args"); 263 | return NULL; 264 | }; 265 | 266 | // duplicate the input buffer in order to not mutate it 267 | data = (u8 *)malloc(out_sz); 268 | if (data == NULL) 269 | { 270 | PyErr_SetString(PyExc_RuntimeError, "malloc failed"); 271 | return NULL; 272 | }; 273 | memcpy(data, data_py.buf, out_sz); 274 | 275 | //void f8( u8 *key, u32 count, u32 bearer, u32 dir, u8 *data, u32 length ); 276 | f8((u8 *)key.buf, count, bearer, dir, data, length); 277 | 278 | ret = PyBytes_FromStringAndSize((char *)data, out_sz); 279 | free(data); 280 | data = NULL; 281 | 282 | return ret; 283 | }; 284 | 285 | 286 | static PyObject* pysnow_f9(PyObject* dummy, PyObject* args) 287 | { 288 | PyObject* ret = 0; 289 | 290 | // input: key, data (bytes buffer -> u8 *), count, fresh, dir, length (u32) 291 | Py_buffer key; 292 | Py_buffer data; 293 | u32 count, fresh, dir, length; 294 | int out_sz; 295 | // output: mac (u8 * -> bytes buffer of size 4) 296 | u8 * mac; 297 | 298 | if (! PyArg_ParseTuple(args, "z*IIIz*I", &key, &count, &fresh, &dir, &data, &length)) 299 | return NULL; 300 | 301 | // transform length in bits to length in bytes 302 | out_sz = length >> 3; 303 | if (length % 8) 304 | out_sz++; 305 | 306 | if ((key.len != 16) || (dir > 1) || (out_sz > data.len)) 307 | { 308 | PyErr_SetString(PyExc_ValueError, "invalid args"); 309 | return NULL; 310 | }; 311 | 312 | //u8 * f9( u8* key, u32 count, u32 fresh, u32 dir, u8 *data, u64 length); 313 | mac = f9((u8 *)key.buf, count, fresh, dir, (u8 *)data.buf, length); 314 | 315 | ret = PyBytes_FromStringAndSize((char *)mac, 4); 316 | return ret; 317 | }; 318 | -------------------------------------------------------------------------------- /C_py/pyzuc.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Software Name : CryptoMobile 3 | * Version : 0.2.0 4 | * 5 | * Copyright © 2017. Benoit Michau. ANSSI. 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License version 2 as published 9 | * by the Free Software Foundation. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You will find a copy of the terms and conditions of the GNU General Public 17 | * License version 2 in the "license.txt" file or 18 | * see http://www.gnu.org/licenses/ or write to the Free Software Foundation, 19 | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 20 | * 21 | *-------------------------------------------------------- 22 | * File Name : CryptoMobile/pyzuc.c 23 | * Created : 2017-07-27 24 | *-------------------------------------------------------- 25 | */ 26 | 27 | #include 28 | #include "../C_alg/ZUC.h" 29 | 30 | 31 | /* Python 2 and 3 initialization mess */ 32 | 33 | 34 | struct module_state { 35 | PyObject *error; 36 | }; 37 | 38 | #if PY_MAJOR_VERSION >= 3 39 | 40 | #define GETSTATE(m) ((struct module_state*)PyModule_GetState(m)) 41 | 42 | #else 43 | 44 | #define GETSTATE(m) (&_state) 45 | static struct module_state _state; 46 | 47 | #endif 48 | 49 | static PyObject * error_out(PyObject *m) { 50 | struct module_state *st = GETSTATE(m); 51 | PyErr_SetString(st->error, "something bad happened"); 52 | return NULL; 53 | } 54 | 55 | static PyObject* pyzuc_initialization(PyObject* dummy, PyObject* args); 56 | static PyObject* pyzuc_generatekeystream(PyObject* dummy, PyObject* args); 57 | static PyObject* pyzuc_eea3(PyObject* dummy, PyObject* args); 58 | static PyObject* pyzuc_eia3(PyObject* dummy, PyObject* args); 59 | 60 | static char pyzuc_initialization_doc[] = 61 | "zuc_initialization(key [16 bytes], iv [16 bytes]) -> None"; 62 | static char pyzuc_generatekeystream_doc[] = 63 | "zuc_generatekeystream(n [uint32, number of 32-bit words]) -> keystream [bytes]"; 64 | static char pyzuc_eea3_doc[] = 65 | "zuc_eea3(ck [16 bytes], count [uint32], bearer [uint32], dir [0 or 1], "\ 66 | "length [uint32, length in bits], data_in [bytes]) -> data_out [bytes]"; 67 | static char pyzuc_eia3_doc[] = 68 | "zuc_eia3(ik [16 bytes], count [uint32], bearer [uint32], dir [0 or 1], "\ 69 | "length [uint32, length in bits], data_in [bytes]) -> mac [4 bytes]"; 70 | 71 | static PyMethodDef pyzuc_methods[] = 72 | { 73 | {"error_out", (PyCFunction)error_out, METH_NOARGS, NULL}, 74 | {"zuc_initialization", pyzuc_initialization, METH_VARARGS, pyzuc_initialization_doc}, 75 | {"zuc_generatekeystream", pyzuc_generatekeystream, METH_VARARGS, pyzuc_generatekeystream_doc}, 76 | {"zuc_eea3", pyzuc_eea3, METH_VARARGS, pyzuc_eea3_doc}, 77 | {"zuc_eia3", pyzuc_eia3, METH_VARARGS, pyzuc_eia3_doc}, 78 | { NULL, NULL, 0, NULL } 79 | }; 80 | 81 | #if PY_MAJOR_VERSION >= 3 82 | 83 | static int pyzuc_traverse(PyObject *m, visitproc visit, void *arg) { 84 | Py_VISIT(GETSTATE(m)->error); 85 | return 0; 86 | } 87 | 88 | static int pyzuc_clear(PyObject *m) { 89 | Py_CLEAR(GETSTATE(m)->error); 90 | return 0; 91 | } 92 | 93 | static struct PyModuleDef moduledef = { 94 | PyModuleDef_HEAD_INIT, 95 | "pyzuc", 96 | "bindings for ZUC EEA3 and EIA3 LTE cryptographic functions", 97 | sizeof(struct module_state), 98 | pyzuc_methods, 99 | NULL, 100 | pyzuc_traverse, 101 | pyzuc_clear, 102 | NULL 103 | }; 104 | 105 | #define INITERROR return NULL 106 | 107 | PyObject * PyInit_pyzuc(void) 108 | 109 | #else 110 | 111 | #define INITERROR return 112 | 113 | void initpyzuc(void) 114 | 115 | #endif 116 | 117 | { 118 | #if PY_MAJOR_VERSION >= 3 119 | 120 | PyObject *module = PyModule_Create(&moduledef); 121 | 122 | #else 123 | 124 | PyObject *module = Py_InitModule4( 125 | "pyzuc", 126 | pyzuc_methods, 127 | "bindings for ZUC EEA3 and EIA3 LTE cryptographic functions", 128 | 0, 129 | PYTHON_API_VERSION); 130 | 131 | #endif 132 | 133 | if (module == NULL) 134 | INITERROR; 135 | struct module_state *st = GETSTATE(module); 136 | 137 | st->error = PyErr_NewException("pyzuc.Error", NULL, NULL); 138 | if (st->error == NULL) { 139 | Py_DECREF(module); 140 | INITERROR; 141 | } 142 | 143 | #if PY_MAJOR_VERSION >= 3 144 | 145 | return module; 146 | 147 | #endif 148 | } 149 | 150 | 151 | /* pyzuc binding to ZUC.h */ 152 | 153 | 154 | // utiliy macro and function required for handling (char *) to (u32 *) conversion 155 | 156 | #define SWAP_BYTES(X) \ 157 | ((((X) & 0xff000000) >> 24) | (((X) & 0x00ff0000) >> 8) | \ 158 | (((X) & 0x0000ff00) << 8) | (((X) & 0x000000ff) << 24)) 159 | 160 | 161 | void memcpy_bswap(u32* bufout, char* bufin, u32 n) 162 | { 163 | u32 i; 164 | 165 | // copy bufin into bufout 166 | memcpy(bufout, bufin, 4*n); 167 | 168 | // swap bytes of uint32_t values within bufout 169 | for (i=0; i u8 *) 177 | Py_buffer k; 178 | Py_buffer iv; 179 | 180 | if (! PyArg_ParseTuple(args, "z*z*", &k, &iv)) 181 | return NULL; 182 | 183 | if ((k.len != 16) || (iv.len != 16)) 184 | { 185 | PyErr_SetString(PyExc_ValueError, "invalid args"); 186 | return NULL; 187 | }; 188 | 189 | //void Initialization(u8* k, u8* iv); 190 | Initialization((u8 *)k.buf, (u8 *)iv.buf); 191 | 192 | Py_RETURN_NONE; 193 | }; 194 | 195 | 196 | static PyObject* pyzuc_generatekeystream(PyObject* dummy, PyObject* args) 197 | { 198 | PyObject* ret = 0; 199 | u32 i; 200 | 201 | // input: n (int -> u32, number of bits of keystream) 202 | u32 KeystreamLen; 203 | u32 * pKeystream; 204 | 205 | if (! PyArg_ParseTuple(args, "I", &KeystreamLen)) 206 | return NULL; 207 | 208 | // output: pKeystream (u32 * -> bytes buffer) 209 | pKeystream = (u32 *)malloc(4*KeystreamLen); 210 | if (pKeystream == NULL) 211 | { 212 | PyErr_SetString(PyExc_RuntimeError, "malloc failed"); 213 | return NULL; 214 | }; 215 | 216 | //GenerateKeystream(u32* pKeystream, u32 KeystreamLen); 217 | GenerateKeystream(pKeystream, KeystreamLen); 218 | 219 | // swap u32 bytes on place for pKeystream 220 | for (i=0; i u8 *), COUNT, BEARER, DIRECTION, LENGTH (int -> u32), 236 | // M (bytes buffer -> u32 *) 237 | Py_buffer CK; 238 | Py_buffer M_py; 239 | u32 COUNT, BEARER, DIRECTION, LENGTH, out_wsz, i; 240 | int out_sz; 241 | u32 * M; 242 | u32 * C; 243 | 244 | if (! PyArg_ParseTuple(args, "z*IIIIz*", &CK, &COUNT, &BEARER, &DIRECTION, &LENGTH, &M_py)) 245 | return NULL; 246 | 247 | // transform length in bits to length in bytes 248 | out_sz = LENGTH >> 3; 249 | if (LENGTH % 8) 250 | out_sz++; 251 | 252 | // transform length in bits to length in 32-bits words 253 | out_wsz = LENGTH >> 5; 254 | if (LENGTH % 32) 255 | out_wsz++; 256 | 257 | if ((CK.len != 16) || (DIRECTION > 1) || (out_sz > M_py.len)) 258 | { 259 | PyErr_SetString(PyExc_ValueError, "invalid args"); 260 | return NULL; 261 | }; 262 | 263 | // swap u32 bytes from Python buffer M_py into new array M 264 | M = (u32 *)malloc(4*out_wsz); 265 | if (M == NULL) 266 | { 267 | PyErr_SetString(PyExc_RuntimeError, "malloc failed"); 268 | return NULL; 269 | }; 270 | memcpy_bswap(M, (char *)M_py.buf, out_wsz); 271 | 272 | // output: C (u32 * -> bytes buffer of size length in bits) 273 | C = (u32 *)malloc(out_wsz<<2); 274 | if (C == NULL) 275 | { 276 | PyErr_SetString(PyExc_RuntimeError, "malloc failed"); 277 | return NULL; 278 | }; 279 | 280 | //void void EEA3(u8* CK, u32 COUNT, u32 BEARER, u32 DIRECTION, u32 LENGTH, u32* M, u32* C); 281 | EEA3((u8 *)CK.buf, COUNT, BEARER, DIRECTION, LENGTH, M, C); 282 | free(M); 283 | M = NULL; 284 | 285 | // swap u32 bytes on place for C 286 | for (i=0; i u8 *), COUNT, BEARER, DIRECTION, LENGTH (int -> u32), 302 | // M (bytes buffer -> u32 *) 303 | Py_buffer IK; 304 | Py_buffer M_py; 305 | u32 COUNT, BEARER, DIRECTION, LENGTH, m_wsz; 306 | int m_sz; 307 | u32 * M; 308 | // output: MAC (u32 * -> bytes buffer of size 4) 309 | u32 MAC[1]; 310 | 311 | if (! PyArg_ParseTuple(args, "z*IIIIz*", &IK, &COUNT, &BEARER, &DIRECTION, &LENGTH, &M_py)) 312 | return NULL; 313 | 314 | // transform length in bits to length in bytes 315 | m_sz = LENGTH >> 3; 316 | if (LENGTH % 8) 317 | m_sz++; 318 | 319 | // transform length in bits to length in 32-bits words 320 | m_wsz = LENGTH >> 5; 321 | if (LENGTH % 32) 322 | m_wsz++; 323 | 324 | if ((IK.len != 16) || (DIRECTION > 1) || (m_sz > M_py.len)) 325 | { 326 | PyErr_SetString(PyExc_ValueError, "invalid args"); 327 | return NULL; 328 | }; 329 | 330 | // swap u32 bytes from Python buffer M_py into new array M 331 | M = (u32 *)malloc(4*m_wsz); 332 | if (M == NULL) 333 | { 334 | PyErr_SetString(PyExc_RuntimeError, "malloc failed"); 335 | return NULL; 336 | }; 337 | memcpy_bswap(M, (char *)M_py.buf, m_wsz); 338 | 339 | //void EIA3(u8* IK, u32 COUNT, u32 BEARER, u32 DIRECTION, u32 LENGTH, u32* M, u32* MAC); 340 | EIA3((u8 *)IK.buf, COUNT, BEARER, DIRECTION, LENGTH, M, MAC); 341 | free(M); 342 | M = NULL; 343 | 344 | // swap bytes of the MAC on place 345 | *MAC = SWAP_BYTES(*MAC); 346 | 347 | ret = PyBytes_FromStringAndSize((char *)MAC, 4); 348 | return ret; 349 | }; 350 | -------------------------------------------------------------------------------- /CryptoMobile/AES.py: -------------------------------------------------------------------------------- 1 | # −*− coding: UTF−8 −*− 2 | #/** 3 | # * Software Name : CryptoMobile 4 | # * Version : 0.3 5 | # * 6 | # * Copyright 2020. Benoit Michau. P1Sec. 7 | # * 8 | # * This program is free software: you can redistribute it and/or modify 9 | # * it under the terms of the GNU General Public License version 2 as published 10 | # * by the Free Software Foundation. 11 | # * 12 | # * This program is distributed in the hope that it will be useful, 13 | # * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # * GNU General Public License for more details. 16 | # * 17 | # * You will find a copy of the terms and conditions of the GNU General Public 18 | # * License version 2 in the "license.txt" file or 19 | # * see http://www.gnu.org/licenses/ or write to the Free Software Foundation, 20 | # * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 21 | # * 22 | # *-------------------------------------------------------- 23 | # * File Name : CryptoMobile/AES.py 24 | # * Created : 2020-01-20 25 | # * Authors : Benoit Michau 26 | # *-------------------------------------------------------- 27 | #*/ 28 | 29 | __all__ = ['AES_CTR', 'AES_ECB'] 30 | 31 | from struct import pack, unpack 32 | 33 | from .utils import * 34 | 35 | # this is a wrapper around few Python cryptographic libraries that support AES 36 | # pycrypto (which seems unmaintained since 2014 / 2015) 37 | # pycryptodome, which is a fork of pycrypto 38 | # cryptography, which is a wrapper around openssl 39 | 40 | 41 | # try to load pycrypto 42 | try: 43 | from Crypto.Cipher import AES as AES_pycrypto 44 | from Crypto.Util import Counter as Counter_pycrypto 45 | except ImportError: 46 | _with_pycrypto = False 47 | else: 48 | _with_pycrypto = True 49 | 50 | 51 | # try to load pycryptodome 52 | try: 53 | from Cryptodome.Cipher import AES as AES_pycryptodome 54 | except ImportError: 55 | _with_pycryptodome = False 56 | else: 57 | _with_pycryptodome = True 58 | 59 | 60 | # try to load cryptography 61 | try: 62 | from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes 63 | from cryptography.hazmat.backends import default_backend 64 | except ImportError: 65 | _with_cryptography = False 66 | else: 67 | _with_cryptography = True 68 | _backend = default_backend() 69 | 70 | 71 | # backend disablement 72 | #_with_pycrypto = False 73 | #_with_pycryptodome = False 74 | #_with_cryptography = False 75 | 76 | 77 | #------------------------------------------------------------------------------# 78 | # AES ECB mode (for Milenage and CMAC mode) 79 | #------------------------------------------------------------------------------# 80 | 81 | class AES_ECB_pycrypto(object): 82 | """AES in ECB mode""" 83 | 84 | block_size = 16 85 | 86 | def __init__(self, key): 87 | """initialize AES in ECB mode with the given key""" 88 | self.aes = AES_pycrypto.new(key, AES_pycrypto.MODE_ECB) 89 | 90 | def encrypt(self, data): 91 | """encrypt data with the key set at initialization""" 92 | return self.aes.encrypt(data) 93 | 94 | 95 | class AES_ECB_pycryptodome(object): 96 | """AES in ECB mode""" 97 | 98 | block_size = 16 99 | 100 | def __init__(self, key): 101 | """initialize AES in ECB mode with the given key""" 102 | self.aes = AES_pycryptodome.new(key, AES_pycryptodome.MODE_ECB) 103 | 104 | def encrypt(self, data): 105 | """encrypt data with the key set at initialization""" 106 | return self.aes.encrypt(data) 107 | 108 | 109 | class AES_ECB_cryptography(object): 110 | """AES in ECB mode""" 111 | 112 | block_size = 16 113 | 114 | def __init__(self, key): 115 | """initialize AES in ECB mode with the given key""" 116 | self.aes = Cipher(algorithms.AES(key), modes.ECB(), backend=_backend).encryptor() 117 | 118 | def encrypt(self, data): 119 | """encrypt data with the key set at initialization""" 120 | return self.aes.update(data) 121 | 122 | 123 | #------------------------------------------------------------------------------# 124 | # AES CTR mode (for EEA2) 125 | #------------------------------------------------------------------------------# 126 | 127 | class AES_CTR_pycrypto(object): 128 | """AES in CTR mode""" 129 | 130 | block_size = 16 131 | 132 | def __init__(self, key, nonce, cnt=0): 133 | """initialize AES in ECB mode with the given key and nonce buffer 134 | 135 | key : 16 bytes buffer 136 | nonce: 8 most significant bytes buffer of the counter initial value 137 | counter will be incremented starting at 0 138 | cnt : uint64, 8 least significant bytes value of the counter 139 | default is 0 140 | """ 141 | self.aes = AES_pycrypto.new( 142 | key, 143 | AES_pycrypto.MODE_CTR, 144 | counter=Counter_pycrypto.new(64, prefix=nonce, initial_value=cnt)) 145 | 146 | def encrypt(self, data): 147 | """encrypt / decrypt data with the key and IV set at initialization""" 148 | return self.aes.encrypt(data) 149 | 150 | decrypt = encrypt 151 | 152 | 153 | class AES_CTR_pycryptodome(object): 154 | """AES in CTR mode""" 155 | 156 | block_size = 16 157 | 158 | def __init__(self, key, nonce, cnt=0): 159 | """initialize AES in ECB mode with the given key and nonce buffer 160 | 161 | key : 16 bytes buffer 162 | nonce: 8 most significant bytes buffer of the counter initial value 163 | counter will be incremented starting at 0 164 | cnt : uint64, 8 least significant bytes value of the counter 165 | default is 0 166 | """ 167 | self.aes = AES_pycryptodome.new( 168 | key, 169 | AES_pycryptodome.MODE_CTR, 170 | nonce=nonce, 171 | initial_value=cnt) 172 | 173 | def encrypt(self, data): 174 | """encrypt / decrypt data with the key and IV set at initialization""" 175 | return self.aes.encrypt(data) 176 | 177 | decrypt = encrypt 178 | 179 | 180 | class AES_CTR_cryptography(object): 181 | """AES in CTR mode""" 182 | 183 | block_size = 16 184 | 185 | def __init__(self, key, nonce, cnt=0): 186 | """initialize AES in ECB mode with the given key and nonce buffer 187 | 188 | key : 16 bytes buffer 189 | nonce: 8 most significant bytes buffer of the counter initial value 190 | counter will be incremented starting at 0 191 | cnt : uint64, 8 least significant bytes value of the counter 192 | default is 0 193 | """ 194 | self.aes = Cipher( 195 | algorithms.AES(key), 196 | modes.CTR(nonce + pack('>Q', cnt)), 197 | backend=_backend).encryptor() 198 | 199 | def encrypt(self, data): 200 | """encrypt / decrypt data with the key and IV set at initialization""" 201 | return self.aes.update(data) 202 | 203 | decrypt = encrypt 204 | 205 | 206 | #------------------------------------------------------------------------------# 207 | # AES backend selection 208 | #------------------------------------------------------------------------------# 209 | 210 | if _with_pycrypto: 211 | AES_ECB = AES_ECB_pycrypto 212 | AES_CTR = AES_CTR_pycrypto 213 | 214 | elif _with_pycryptodome: 215 | AES_CTR = AES_CTR_pycryptodome 216 | AES_ECB = AES_ECB_pycryptodome 217 | 218 | elif _with_cryptography: 219 | AES_CTR = AES_CTR_cryptography 220 | AES_ECB = AES_ECB_cryptography 221 | 222 | else: 223 | raise(ImportError('missing AES backend: requires cryptography, pycryptodome or pycrypto')) 224 | 225 | #print('AES backend: %s, %s' % (AES_ECB.__name__, AES_CTR.__name__)) 226 | -------------------------------------------------------------------------------- /CryptoMobile/CM.py: -------------------------------------------------------------------------------- 1 | # −*− coding: UTF−8 −*− 2 | #/** 3 | # * Software Name : CryptoMobile 4 | # * Version : 0.3 5 | # * 6 | # * Copyright 2017. Benoit Michau. ANSSI. 7 | # * 8 | # * This program is free software: you can redistribute it and/or modify 9 | # * it under the terms of the GNU General Public License version 2 as published 10 | # * by the Free Software Foundation. 11 | # * 12 | # * This program is distributed in the hope that it will be useful, 13 | # * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # * GNU General Public License for more details. 16 | # * 17 | # * You will find a copy of the terms and conditions of the GNU General Public 18 | # * License version 2 in the "license.txt" file or 19 | # * see http://www.gnu.org/licenses/ or write to the Free Software Foundation, 20 | # * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 21 | # * 22 | # *-------------------------------------------------------- 23 | # * File Name : CryptoMobile/CM.py 24 | # * Created : 2017-07-28 25 | # * Authors : Benoit Michau 26 | # *-------------------------------------------------------- 27 | #*/ 28 | 29 | from struct import pack, unpack 30 | # 31 | from pykasumi import * 32 | from pysnow import * 33 | from pyzuc import * 34 | from .utils import * 35 | from .CMAC import CMAC 36 | 37 | try: 38 | from .AES import AES_CTR, AES_ECB 39 | # filter * export 40 | __all__ = ['KASUMI', 'SNOW3G', 'ZUC', 'AES_3GPP', 41 | 'UEA1', 'UIA1', 'UEA2', 'UIA2', 42 | 'EEA1', 'EIA1', 'EEA2', 'EIA2', 'EEA3', 'EIA3'] 43 | _with_aes = True 44 | except ImportError as err: 45 | print(err) 46 | print('EEA2 / EIA2 not available') 47 | # filter * export 48 | __all__ = ['KASUMI', 'SNOW3G', 'ZUC', 49 | 'UEA1', 'UIA1', 'UEA2', 'UIA2', 50 | 'EEA1', 'EIA1', 'EEA3', 'EIA3'] 51 | _with_aes = False 52 | 53 | 54 | class KASUMI(object): 55 | """UMTS primary encryption / integrity protection algorithm 56 | It is a block cipher, working with: 57 | - 128 bits key 58 | - 64 bits block 59 | 60 | 61 | Key scheduling and ECB-mode single block cipher primitives are defined with 62 | methods: 63 | 64 | _initialize(key [16 bytes]) -> None 65 | 66 | _cipher_block(input [8 bytes]) -> output [8 bytes] 67 | 68 | 69 | For securing radio frames at UMTS RLC or MAC layer, UMTS modes of operation 70 | are defined in F8 and F9 methods: 71 | 72 | F8(key [16 bytes], count [uint32], bearer [uint32], dir [0 or 1], data_in [bytes], bitlen [uint32]) 73 | -> data_out [bytes] 74 | 75 | an UMTS bearer is usually coded on 5 bits 76 | optional bitlen argument represents the length of data_in in bits 77 | 78 | F9(key [16 bytes], count [uint32], fresh [uint32], dir [0 or 1], data_in [bytes], bitlen [uint32]) 79 | -> mac [4 bytes] 80 | 81 | optional bitlen argument represents the length of data_in in bits 82 | 83 | 84 | GSM / GPRS compatibility modes (A5/3, A5/4, GEA3, GEA4, GIA4) are not implemented 85 | """ 86 | block_size = 8 87 | key_size = 16 88 | 89 | def _keyschedule(self, key): 90 | try: 91 | return kasumi_keyschedule(key) 92 | except ValueError as err: 93 | raise(CMException(err)) 94 | 95 | _initialize = _keyschedule 96 | 97 | def _kasumi(self, data_in): 98 | try: 99 | return kasumi_kasumi(data_in) 100 | except ValueError as err: 101 | raise(CMException(err)) 102 | 103 | _cipher_block = _kasumi 104 | 105 | def F8(self, key, count, bearer, dir, data_in, bitlen=None): 106 | # avoid uint32 under/overflow 107 | if not 0 <= count < MAX_UINT32 or \ 108 | not 0 <= bearer < MAX_UINT32: 109 | raise(CMException('invalid args')) 110 | # 111 | if bitlen is None: 112 | bitlen = 8*len(data_in) 113 | # 114 | try: 115 | return kasumi_f8(key, count, bearer, dir, data_in, bitlen) 116 | except ValueError as err: 117 | raise(CMException(err)) 118 | 119 | def F9(self, key, count, fresh, dir, data_in, bitlen=None): 120 | # avoid uint32 under/overflow 121 | if not 0 <= count < MAX_UINT32 or \ 122 | not 0 <= fresh < MAX_UINT32: 123 | raise(CMException('invalid args')) 124 | # 125 | if bitlen is None: 126 | bitlen = 8*len(data_in) 127 | # 128 | try: 129 | return kasumi_f9(key, count, fresh, dir, data_in, bitlen) 130 | except ValueError as err: 131 | raise(CMException(err)) 132 | 133 | 134 | class SNOW3G(object): 135 | """UMTS secondary encryption / integrity protection algorithm 136 | It is a pseudo-random generator, working with: 137 | - 128 bits key and 128 bits initialization vector 138 | - delivering a stream of 32 bits words 139 | 140 | 141 | Generator initialization and keystream generation primitives are defined 142 | with methods: 143 | 144 | _initialize(key [16 bytes], iv [16 bytes]) -> None 145 | 146 | _generate_keystream(length [uint32]) -> keystream 147 | 148 | 149 | For securing radio frames at UMTS RLC or MAC layer, UMTS modes of operation 150 | are defined in F8 and F9 methods: 151 | 152 | F8(key [16 bytes], count [uint32], bearer [uint32], dir [0 or 1], data_in [bytes], bitlen [uint32]) 153 | -> data_out [bytes] 154 | 155 | an UMTS bearer is usually coded on 5 bits 156 | optional bitlen argument represents the length of data_in in bits 157 | 158 | F9(key [16 bytes], count [uint32], fresh [uint32], dir [0 or 1], data_in [bytes], bitlen [uint32]) 159 | -> mac [4 bytes] 160 | 161 | optional bitlen argument represents the length of data_in in bits 162 | 163 | 164 | LTE modes of operation (EEA1, EIA1) is supported as well: the only difference 165 | is for EIA1, `bearer' is replacing `fresh' and has a max value of 31. 166 | EEA1 and EIA1 methods are defined: 167 | 168 | EEA1 aliases F8 169 | 170 | EIA1(key [16 bytes], count [uint32], bearer [uint5], dir [0 or 1], data_in [bytes], bitlen [uint32]) 171 | -> mac [4 bytes] 172 | """ 173 | iv_size = 16 174 | key_size = 16 175 | 176 | def _initialize(self, key, iv): 177 | try: 178 | return snow_initialize(key, iv) 179 | except ValueError as err: 180 | raise(CMException(err)) 181 | 182 | def _generate_keystream(self, length): 183 | if not 0 <= length < MAX_UINT32: 184 | raise(CMException('invalid args')) 185 | # 186 | lw = length >> 2 187 | if length % 4: 188 | lastbytes = True 189 | lw += 1 190 | else: 191 | lastbytes = False 192 | # 193 | try: 194 | if lastbytes: 195 | return snow_generatekeystream(lw)[:length] 196 | else: 197 | return snow_generatekeystream(lw) 198 | except ValueError as err: 199 | raise(CMException(err)) 200 | 201 | def F8(self, key, count, bearer, dir, data_in, bitlen=None): 202 | # avoid uint32 under/overflow 203 | if not 0 <= count < MAX_UINT32 or \ 204 | not 0 <= bearer < MAX_UINT32: 205 | raise(CMException('invalid args')) 206 | # 207 | if bitlen is None: 208 | bitlen = 8*len(data_in) 209 | # 210 | try: 211 | return snow_f8(key, count, bearer, dir, data_in, bitlen) 212 | except ValueError as err: 213 | raise(CMException(err)) 214 | 215 | def F9(self, key, count, fresh, dir, data_in, bitlen=None): 216 | # avoid uint32 under/overflow 217 | if not 0 <= count < MAX_UINT32 or \ 218 | not 0 <= fresh < MAX_UINT32: 219 | raise(CMException('invalid args')) 220 | # 221 | if bitlen is None: 222 | bitlen = 8*len(data_in) 223 | # 224 | try: 225 | return snow_f9(key, count, fresh, dir, data_in, bitlen) 226 | except ValueError as err: 227 | raise(CMException(err)) 228 | 229 | EEA1 = F8 230 | 231 | def EIA1(self, key, count, bearer, dir, data_in, bitlen=None): 232 | if not 0 <= bearer < 32: 233 | raise(CMException('invalid args')) 234 | # 235 | try: 236 | return self.F9(key, count, bearer<<27, dir, data_in, bitlen) 237 | except (ValueError, CMException) as err: 238 | raise(CMException(err)) 239 | 240 | 241 | class ZUC(object): 242 | """LTE 3rd encryption / integrity protection algorithm 243 | It is a pseudo-random generator, working with: 244 | - 128 bits key and 128 bits initialization vector 245 | - delivering a stream of 32 bits words 246 | 247 | 248 | Generator initialization and keystream generation primitives are defined 249 | with methods: 250 | 251 | _initialize(key [16 bytes], iv [16 bytes]) -> None 252 | 253 | _generate_keystream(length [uint32]) -> keystream [bytes] 254 | 255 | 256 | For securing packets at the LTE PDCP and NAS layers, LTE modes of operation 257 | are defined in EEA3 and EIA3 methods: 258 | 259 | EEA3(key [16 bytes], count [uint32], bearer [uint32], dir [0 or 1], data_in [bytes], bitlen [uint32]) 260 | -> data_out [bytes] 261 | 262 | an LTE bearer is usually coded on 5 bits 263 | optional bitlen argument represents the length of data_in in bits 264 | 265 | EIA3(key [16 bytes], count [uint32], bearer [uint32], dir [0 or 1], data_in [bytes], bitlen [uint32]) 266 | -> mac [4 bytes] 267 | 268 | optional bitlen argument represents the length of data_in in bits 269 | """ 270 | iv_size = 16 271 | key_size = 16 272 | 273 | def _initialize(self, key, iv): 274 | try: 275 | zuc_initialization(key, iv) 276 | except ValueError as err: 277 | raise(CMException(err)) 278 | 279 | def _generate_keystream(self, length): 280 | if not 0 <= length < MAX_UINT32: 281 | raise(CMException('invalid args')) 282 | # 283 | lw = length >> 2 284 | if length % 4: 285 | lastbytes = True 286 | lw += 1 287 | else: 288 | lastbytes = False 289 | # 290 | try: 291 | if lastbytes: 292 | return zuc_generatekeystream(lw)[:length] 293 | else: 294 | return zuc_generatekeystream(lw) 295 | except ValueError as err: 296 | raise(CMException(err)) 297 | 298 | def EEA3(self, key, count, bearer, dir, data_in, bitlen=None): 299 | # avoid uint32 under/overflow 300 | if not 0 <= count < MAX_UINT32 or \ 301 | not 0 <= bearer < 32: 302 | raise(CMException('invalid args')) 303 | # 304 | if bitlen is None: 305 | bitlen = 8*len(data_in) 306 | # 307 | try: 308 | return zuc_eea3(key, count, bearer, dir, bitlen, data_in) 309 | except ValueError as err: 310 | raise(CMException(err)) 311 | 312 | def EIA3(self, key, count, bearer, dir, data_in, bitlen=None): 313 | # avoid uint32 under/overflow 314 | if not 0 <= count < MAX_UINT32 or \ 315 | not 0 <= bearer < 32: 316 | raise(CMException('invalid args')) 317 | # 318 | if bitlen is None: 319 | bitlen = 8*len(data_in) 320 | # 321 | try: 322 | return zuc_eia3(key, count, bearer, dir, bitlen, data_in) 323 | except ValueError as err: 324 | raise(CMException(err)) 325 | 326 | 327 | class AES_3GPP(object): 328 | """LTE 2nd encryption / integrity protection algorithm 329 | It is using AES with 128 bit key in CTR encryption mode and CBC-MAC integrity 330 | protection mode. 331 | 332 | For securing packets at the LTE PDCP and NAS layers, LTE modes of operation 333 | are defined in EEA2 and EIA2 methods: 334 | 335 | EEA2(key [16 bytes], count [uint32], bearer [uint32], dir [0 or 1], data_in [bytes], bitlen [uint32]) 336 | -> data_out [bytes] 337 | 338 | an LTE bearer is usually coded on 5 bits 339 | optional bitlen argument represents the length of data_in in bits 340 | 341 | EIA2(key [16 bytes], count [uint32], bearer [uint32], dir [0 or 1], data_in [bytes], bitlen [uint32]) 342 | -> mac [4 bytes] 343 | 344 | optional bitlen argument represents the length of data_in in bits 345 | """ 346 | 347 | def EEA2(self, key, count, bearer, dir, data_in, bitlen=None): 348 | # avoid uint32 under/overflow 349 | if not 0 <= count < MAX_UINT32 or \ 350 | not 0 <= bearer <= 32: 351 | raise(CMException('invalid args')) 352 | # 353 | if bitlen is None: 354 | bitlen = 8*len(data_in) 355 | lastbits = None 356 | else: 357 | lastbits = (8-(bitlen%8))%8 358 | blen = bitlen >> 3 359 | if lastbits: 360 | blen += 1 361 | if blen < len(data_in): 362 | data_in = data_in[:blen] 363 | # 364 | nonce = pack('>II', count, (bearer<<27)+(dir<<26)) 365 | enc = AES_CTR(key, nonce).encrypt(data_in) 366 | # 367 | if lastbits: 368 | # zero last bits 369 | if py_vers < 3: 370 | return enc[:-1] + chr(ord(enc[-1]) & (0x100 - (1<> 3 387 | if lastbits: 388 | blen += 1 389 | if blen < len(data_in): 390 | data_in = data_in[:blen] 391 | # 392 | M = pack('>II', count, (bearer<<27)+(dir<<26)) + data_in 393 | cmac = CMAC(key, AES_ECB, Tlen=32) 394 | return cmac.cmac(M, 64+bitlen) 395 | 396 | 397 | ################### 398 | # DEFINE 3GPP ALG # 399 | # convinient for # 400 | # python export # 401 | ################### 402 | # 403 | _K = KASUMI() 404 | _S = SNOW3G() 405 | _Z = ZUC() 406 | if _with_aes: 407 | _A = AES_3GPP() 408 | # For 3G 409 | UEA1 = _K.F8 410 | UIA1 = _K.F9 411 | UEA2 = _S.F8 412 | UIA2 = _S.F9 413 | # For LTE 414 | EEA1 = _S.F8 415 | EIA1 = _S.EIA1 416 | EEA3 = _Z.EEA3 417 | EIA3 = _Z.EIA3 418 | if _with_aes: 419 | EEA2 = _A.EEA2 420 | EIA2 = _A.EIA2 421 | -------------------------------------------------------------------------------- /CryptoMobile/CMAC.py: -------------------------------------------------------------------------------- 1 | # −*− coding: UTF−8 −*− 2 | #/** 3 | # * Software Name : CryptoMobile 4 | # * Version : 0.3 5 | # * 6 | # * Copyright 2017. Benoit Michau. ANSSI. 7 | # * 8 | # * This program is free software: you can redistribute it and/or modify 9 | # * it under the terms of the GNU General Public License version 2 as published 10 | # * by the Free Software Foundation. 11 | # * 12 | # * This program is distributed in the hope that it will be useful, 13 | # * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # * GNU General Public License for more details. 16 | # * 17 | # * You will find a copy of the terms and conditions of the GNU General Public 18 | # * License version 2 in the "license.txt" file or 19 | # * see http://www.gnu.org/licenses/ or write to the Free Software Foundation, 20 | # * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 21 | # * 22 | # *-------------------------------------------------------- 23 | # * File Name : CryptoMobile/CMAC.py 24 | # * Created : 2017-08-22 25 | # * Authors : Benoit Michau 26 | # *-------------------------------------------------------- 27 | #*/ 28 | 29 | from struct import pack, unpack 30 | # 31 | from .utils import * 32 | 33 | 34 | class CMAC(object): 35 | """CMAC mode of operation as defined by NIST 36 | to be used with a block cipher 37 | 38 | Initialize with the key, block-cipher function and optionally MAC length. 39 | Run it with cmac() on the data to process. 40 | It returns the MAC of the expected length. 41 | 42 | e.g. 43 | >>> cmac = CMAC(16*b'A', AES, Tlen=64) 44 | >>> cmac.cmac(200*b'testing ') 45 | b'\xe0*\xf5x\x14\xbc\x13\x96' 46 | """ 47 | 48 | def __init__(self, key, ciphermod, Tlen=None): 49 | """ 50 | key [X bytes]: key used by the cipher algorithm set in `ciphermod' 51 | length X must correspond to the given ciphermod key length 52 | ciphermod [encryption module]: block-cipher algorithm 53 | must have `block_size' attribute and `__init__(key)' method, 54 | which returns an instance with an `encrypt(data_in)' method 55 | Tlen [int, optional]: requested MAC length (in bits) 56 | """ 57 | # set the key 58 | self.key = key 59 | # init block cipher 60 | try: 61 | self.__init_cipher(ciphermod) 62 | except Exception as err: 63 | raise(CMException('invalid ciphermod arg, ', err)) 64 | # schedule it (defines self.K1 and self.K2 [16 bytes]) 65 | self.__keyschedule() 66 | # set MAC length 67 | if Tlen is None: 68 | self.Tlen = 8*self._blocksize 69 | elif not 0 < Tlen <= 8*self._blocksize: 70 | raise(CMException('invalid args')) 71 | else: 72 | self.Tlen = Tlen 73 | 74 | def __init_cipher(self, ciphermod): 75 | # set block-cipher and block size (in bits) 76 | self._ciphermod = ciphermod 77 | self._blocksize = ciphermod.block_size 78 | # init ECB-mode block cipher 79 | self._cipher = ciphermod(self.key) 80 | # link to its encrypt() method 81 | self._encrypt = self._cipher.encrypt 82 | 83 | def __keyschedule(self): 84 | # schedule the key for potential padding 85 | # encrypt a zero input block 86 | L = self._encrypt(b'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0') 87 | # schedule depending of the MSB of L 88 | # python-fu: unpack the 128 bits register as 2 BE uint64 89 | Lh, Ll = unpack('>QQ', L) 90 | # sum both uint64 as an uint128, left-shift and filter 91 | K1 = (((Lh<<64)+Ll) << 1) & 0xffffffffffffffffffffffffffffffff 92 | # XOR K1 depending of the MSB of L 93 | if Lh & 0x8000000000000000: 94 | K1 ^= 0x87 95 | # re-shift K1 to make K2 96 | K2 = (K1 << 1) & 0xffffffffffffffffffffffffffffffff 97 | # XOR K2 depending of the MSB of K1 98 | if K1 & 0x80000000000000000000000000000000: 99 | K2 ^= 0x87 100 | # set 2 corresponding 16-bytes strings K1, K2 101 | self.K1 = pack('>QQ', K1>>64, K1%MAX_UINT64) 102 | self.K2 = pack('>QQ', K2>>64, K2%MAX_UINT64) 103 | 104 | def cmac(self, data_in, data_len=None): 105 | """Computes the CBC-MAC over data_in, according to initialization 106 | information 107 | 108 | data_in [bytes] 109 | data_len [int, optional]: length in bits of data_in, over wich the mac 110 | is computed 111 | """ 112 | # prepare the input data according to the requested length (in bits) 113 | # of input data to be processed 114 | len_data_in = 8 * len(data_in) 115 | if data_len is None: 116 | data_len = len_data_in 117 | lastbits = 0 118 | elif not 0 < data_len <= len_data_in: 119 | raise(CMException('invalid args')) 120 | elif data_len < len_data_in: 121 | # truncate data_in according to data_len 122 | olen = data_len>>3 123 | lastbits = (8-(data_len%8))%8 124 | if lastbits: 125 | # zero last bits after data_len 126 | if py_vers < 3: 127 | data_in = data_in[:olen] + chr( ord(data_in[olen]) & (0x100-(1<>3)) * b'\0' 153 | # xor Mn with K2 154 | Mn = xor_buf(Mn, self.K2) 155 | else: 156 | # M is blocksize-aligned 157 | # xor Mn with K1 158 | Mn = xor_buf(Mn, self.K1) 159 | else: 160 | # empty data_in... 161 | Mn = xor_buf(b'\x80\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0', self.K2) 162 | M.append(Mn) 163 | # loop over the blocks to MAC all of them 164 | C = self._blocksize * b'\0' 165 | for Mi in M: 166 | C = self._encrypt(xor_buf(C, Mi)) 167 | if self.Tlen == self._blocksize: 168 | return C 169 | else: 170 | # truncate C 171 | olen = self.Tlen>>3 172 | T = C[:olen] 173 | if self.Tlen % 8: 174 | # zero last bits of T 175 | lastbits = (8-(self.Tlen%8))%8 176 | if py_vers < 3: 177 | return T + chr(ord(C[olen]) & (0x100 - (1<Q', self.SK[24:32])[0], 89 | self.SK[32:64] 90 | ) 91 | # encryption 92 | aes = AES_CTR(aes_key, aes_nonce, aes_cnt) 93 | ciphertext = aes.encrypt(plaintext) 94 | # MAC 95 | mac = hmac.new(mac_key, ciphertext, hashlib.sha256).digest() 96 | # 97 | return self.EK, ciphertext, mac[0:8] 98 | 99 | 100 | class ECIES_HN(object): 101 | """ECIES_HN handles the ECIES computation required on the Home Network side 102 | to unprotect a subscriber's SUCI into a fixed identity SUPI 103 | """ 104 | 105 | def __init__(self, hn_priv_key, profile='A'): 106 | if profile == 'A': 107 | self.EC = X25519(loc_privkey=hn_priv_key) 108 | elif profile == 'B': 109 | self.EC = ECDH_SECP256R1(loc_privkey=hn_priv_key) 110 | else: 111 | raise(CMException('unknown ECIES profile %s' % profile)) 112 | 113 | def unprotect(self, ue_pubkey, ciphertext, mac): 114 | """unprotects the given ciphertext using associated MAC and UE ephemeral 115 | public key 116 | 117 | returns the decrypted cleartext bytes buffer or None if MAC verification 118 | failed 119 | """ 120 | SK = KDF(ue_pubkey, self.EC.generate_sharedkey(ue_pubkey)) 121 | aes_key, aes_nonce, aes_cnt, mac_key = ( 122 | SK[:16], 123 | SK[16:24], 124 | unpack('>Q', SK[24:32])[0], 125 | SK[32:64] 126 | ) 127 | # 128 | # verify MAC 129 | mac_hn = hmac.new(mac_key, ciphertext, hashlib.sha256).digest() 130 | mac_verif = hmac.compare_digest(mac_hn[0:8], mac) 131 | # decrypt 132 | aes = AES_CTR(aes_key, aes_nonce, aes_cnt) 133 | cleartext = aes.decrypt(ciphertext) 134 | # 135 | if mac_verif: 136 | return cleartext 137 | else: 138 | return None 139 | 140 | -------------------------------------------------------------------------------- /CryptoMobile/Milenage.py: -------------------------------------------------------------------------------- 1 | # −*− coding: UTF−8 −*− 2 | #/** 3 | # * Software Name : CryptoMobile 4 | # * Version : 0.3 5 | # * 6 | # * Copyright 2013. Benoit Michau. ANSSI. 7 | # * 8 | # * This program is free software: you can redistribute it and/or modify 9 | # * it under the terms of the GNU General Public License version 2 as published 10 | # * by the Free Software Foundation. 11 | # * 12 | # * This program is distributed in the hope that it will be useful, 13 | # * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # * GNU General Public License for more details. 16 | # * 17 | # * You will find a copy of the terms and conditions of the GNU General Public 18 | # * License version 2 in the "license.txt" file or 19 | # * see http://www.gnu.org/licenses/ or write to the Free Software Foundation, 20 | # * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 21 | # * 22 | # *-------------------------------------------------------- 23 | # * File Name : CryptoMobile/Milenage.py 24 | # * Created : 2013-07-13 25 | # * Authors : Benoit Michau 26 | # *-------------------------------------------------------- 27 | #*/ 28 | 29 | ######################################################## 30 | # CryptoMobile python toolkit 31 | # 32 | # Milenage authentication algorithm 33 | # as proposed by ETSI SAGE for 3G authentication (AES-based) 34 | # see 3GPP TS 35.205, 206 and 207 35 | ####################################################### 36 | 37 | import sys 38 | import hmac 39 | from struct import pack, unpack 40 | from hashlib import sha256 41 | # 42 | from .utils import * 43 | from .AES import AES_ECB 44 | 45 | 46 | __all__ = ['Milenage', 'make_OPc'] 47 | 48 | 49 | if sys.version_info[0] > 2: 50 | # python 3 51 | def rot_buf(b, r): 52 | """rotate buffer b by r bits 53 | """ 54 | ro, rb = r>>3, r%8 55 | # byte-aligned rotation 56 | br = b[ro:] + b[:ro] 57 | if rb: 58 | # unaligned bit-rotation 59 | c = [ ((br[i]<>(8-rb)) for i in range(len(br)-1)] 60 | c.append( ((br[-1]<>(8-rb)) ) 61 | br = bytes(c) 62 | return br 63 | 64 | else: 65 | # python 2 66 | def rot_buf(b, r): 67 | """rotate buffer b by r bits 68 | """ 69 | ro, rb = r>>3, r%8 70 | # byte-aligned rotation 71 | br = b[ro:] + b[:ro] 72 | if rb: 73 | # unaligned bit-rotation 74 | br = bytearray(br) 75 | c = [ ((br[i]<>(8-rb)) for i in range(len(br)-1)] 76 | c.append( ((br[-1]<>(8-rb)) ) 77 | br = bytes(bytearray(c)) 78 | return br 79 | 80 | 81 | def rot_buf16(b, r): 82 | """rotate 16-bytes buffer b by r bits 83 | """ 84 | ro, rb = r>>3, r%8 85 | # byte 86 | br = b[ro:] + b[:ro] 87 | if rb: 88 | b0, b1 = unpack('>QQ', br) 89 | br = pack('>QQ', ((b0<>(64-rb)), 90 | ((b1<>(64-rb))) 91 | return br 92 | 93 | 94 | def make_OPc( K, OP ): 95 | """derive OP with K to produce OPc""" 96 | return xor_buf( AES_ECB(K).encrypt(OP), OP ) 97 | 98 | 99 | ### 100 | # 3GPP authentication algorithm 101 | ### 102 | 103 | class Milenage: 104 | """Milenage cryptographic functions, based on AES 105 | 106 | see 3GPP TS 35.205 107 | """ 108 | 109 | ###################### 110 | # OPERATOR CONSTANTS # 111 | ###################### 112 | # defined operator constants for the Milenage framework 113 | # not recommended to change those 114 | # better playing with OP 115 | 116 | c1 = 15 * b'\x00' + b'\x00' # 128 bits 117 | c2 = 15 * b'\x00' + b'\x01' # 128 bits 118 | c3 = 15 * b'\x00' + b'\x02' # 128 bits 119 | c4 = 15 * b'\x00' + b'\x04' # 128 bits 120 | c5 = 15 * b'\x00' + b'\x08' # 128 bits 121 | 122 | r1 = 0x40 # uint8 123 | r2 = 0x00 # uint8 124 | r3 = 0x20 # uint8 125 | r4 = 0x40 # uint8 126 | r5 = 0x60 # uint8 127 | 128 | def __init__(self, OP): 129 | self.OP = OP 130 | self.OPc = None 131 | 132 | def set_opc(self, OPc): 133 | """This sets OPc and saves some AES rounds in f1, f1star, f2345, f5star 134 | when producing several vectors for a single subscriber 135 | """ 136 | self.OPc = OPc 137 | 138 | def unset_opc(self): 139 | self.OPc = None 140 | 141 | ###################### 142 | # MILENAGE FUNCTIONS # 143 | ###################### 144 | 145 | def f1(self, K, RAND, SQN, AMF, OP=None): 146 | """return MAC_A [8 bytes buffer] or None on error 147 | """ 148 | if len(K) != 16 or len(RAND) != 16 or len(SQN) != 6 or len(AMF) != 2: 149 | log('ERR', 'Milenage.f1: invalid args') 150 | return None 151 | # 152 | if self.OPc is not None: 153 | OPc = self.OPc 154 | elif OP is not None: 155 | OPc = make_OPc(K, OP) 156 | else: 157 | OPc = make_OPc(K, self.OP) 158 | # 159 | inp = SQN + AMF + SQN + AMF 160 | cipher = AES_ECB(K) 161 | K_OPc_RAND = cipher.encrypt(xor_buf(RAND, OPc)) 162 | # 163 | out1 = xor_buf(cipher.encrypt( 164 | xor_buf(xor_buf(rot_buf16(xor_buf(inp, OPc), 165 | self.r1), 166 | self.c1), 167 | K_OPc_RAND)), 168 | OPc) 169 | 170 | return out1[0:8] 171 | 172 | def f1star(self, K, RAND, SQN, AMF, OP=None): 173 | """return MAC_S [8 bytes buffer] or None on error 174 | """ 175 | if len(K) != 16 or len(RAND) != 16 or len(SQN) != 6 or len(AMF) != 2: 176 | log('ERR', 'Milenage.f1star: invalid args') 177 | return None 178 | # 179 | if self.OPc is not None: 180 | OPc = self.OPc 181 | elif OP is not None: 182 | OPc = make_OPc(K, OP) 183 | else: 184 | OPc = make_OPc(K, self.OP) 185 | # 186 | inp = SQN + AMF + SQN + AMF 187 | cipher = AES_ECB(K) 188 | K_OPc_RAND = cipher.encrypt(xor_buf(RAND, OPc)) 189 | # 190 | out1 = xor_buf(cipher.encrypt( 191 | xor_buf(xor_buf(rot_buf16(xor_buf(inp, OPc), 192 | self.r1), 193 | self.c1), 194 | K_OPc_RAND)), 195 | OPc) 196 | return out1[8:16] 197 | 198 | def f2345(self, K, RAND, OP=None): 199 | """return RES [8], CK [16], IK [16] and AK [6] bytes buffers or None on error 200 | """ 201 | if len(K) != 16 or len(RAND) != 16: 202 | log('ERR', 'Milenage.f2345: invalid args') 203 | return None 204 | # 205 | if self.OPc is not None: 206 | OPc = self.OPc 207 | elif OP is not None: 208 | OPc = make_OPc(K, OP) 209 | else: 210 | OPc = make_OPc(K, self.OP) 211 | # 212 | cipher = AES_ECB(K) 213 | K_OPc_RAND_OPc = xor_buf(cipher.encrypt( 214 | xor_buf(OPc, RAND)), 215 | OPc) 216 | # 217 | out2 = xor_buf(OPc, 218 | cipher.encrypt( 219 | xor_buf(rot_buf16(K_OPc_RAND_OPc, 220 | self.r2), 221 | self.c2))) 222 | # 223 | out3 = xor_buf(OPc, 224 | cipher.encrypt( 225 | xor_buf(rot_buf16(K_OPc_RAND_OPc, 226 | self.r3), 227 | self.c3))) 228 | # 229 | out4 = xor_buf(OPc, 230 | cipher.encrypt( 231 | xor_buf(rot_buf16(K_OPc_RAND_OPc, 232 | self.r4), 233 | self.c4))) 234 | # 235 | return out2[8:16], out3, out4, out2[:6] 236 | 237 | def f5star(self, K, RAND, OP=None): 238 | """return AK [6 bytes buffer] or None on error 239 | """ 240 | if len(K) != 16 or len(RAND) != 16: 241 | log('ERR', 'Milenage.f5star: invalid args') 242 | return None 243 | # 244 | if self.OPc is not None: 245 | OPc = self.OPc 246 | elif OP is not None: 247 | OPc = make_OPc(K, OP) 248 | else: 249 | OPc = make_OPc(K, self.OP) 250 | # 251 | cipher = AES_ECB(K) 252 | K_OPc_RAND_OPc = xor_buf(cipher.encrypt( 253 | xor_buf(OPc, RAND)), 254 | OPc) 255 | # 256 | out5 = xor_buf(OPc, 257 | cipher.encrypt( 258 | xor_buf(rot_buf16(K_OPc_RAND_OPc, 259 | self.r5), 260 | self.c5))) 261 | # 262 | return out5[:6] 263 | 264 | -------------------------------------------------------------------------------- /CryptoMobile/TUAK.py: -------------------------------------------------------------------------------- 1 | # −*− coding: UTF−8 −*− 2 | #/** 3 | # * Software Name : CryptoMobile 4 | # * Version : 0.3 5 | # * 6 | # * Copyright 2018. Benoit Michau. P1Sec. 7 | # * 8 | # * This program is free software: you can redistribute it and/or modify 9 | # * it under the terms of the GNU General Public License version 2 as published 10 | # * by the Free Software Foundation. 11 | # * 12 | # * This program is distributed in the hope that it will be useful, 13 | # * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # * GNU General Public License for more details. 16 | # * 17 | # * You will find a copy of the terms and conditions of the GNU General Public 18 | # * License version 2 in the "license.txt" file or 19 | # * see http://www.gnu.org/licenses/ or write to the Free Software Foundation, 20 | # * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 21 | # * 22 | # *-------------------------------------------------------- 23 | # * File Name : CryptoMobile/TUAK.py 24 | # * Created : 2018-12-20 25 | # * Authors : Benoit Michau 26 | # *-------------------------------------------------------- 27 | #*/ 28 | 29 | ######################################################## 30 | # CryptoMobile python toolkit 31 | # 32 | # TUAK authentication algorithm 33 | # as proposed by 3GPP as an alternative to Milenage 34 | # algorithm based on SHA-3 (more exactly its KeccakP-1600 permutation) 35 | # see 3GPP TS 35.231, 232 and 233 36 | ####################################################### 37 | 38 | from binascii import * 39 | 40 | import sys 41 | python_version = sys.version_info[0] 42 | 43 | import pykeccakp1600 as kec 44 | keccakp1600 = kec.pykeccakp1600 45 | 46 | from .utils import * 47 | 48 | 49 | __all__ = ['TUAK', 'make_TOPc'] 50 | 51 | 52 | def make_TOPc( K, TOP, ALGONAME, KeccakIterations ): 53 | """derives TOP with K to produce TOPc 54 | requires the TUAK global parameters ALGONAME and KeccakIterations""" 55 | if len(K) == 16: 56 | INSTANCE = b'\x00' 57 | else: 58 | #len(K) == 32 59 | INSTANCE = b'\x01' 60 | 61 | INOUT = [] 62 | INOUT.append( TOP[::-1] ) 63 | INOUT.append( INSTANCE[::-1] ) 64 | INOUT.append( ALGONAME[::-1] ) 65 | INOUT.append( b'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0' ) 66 | INOUT.append( K[::-1] ) 67 | if len(K) == 16: 68 | INOUT.append( b'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0' ) 69 | # add padding 70 | INOUT.append( b'\x1f\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x80' ) 71 | INOUT.append( b'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'\ 72 | b'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0' ) 73 | INOUT = b''.join(INOUT) 74 | 75 | for i in range(KeccakIterations): 76 | INOUT = keccakp1600(INOUT) 77 | return INOUT[:32][::-1] 78 | 79 | 80 | ### 81 | # 3GPP TUAK authentication algorithm 82 | ### 83 | 84 | class TUAK: 85 | """TUAK cryptographic functions, based on KeccakP-1600 86 | 87 | see 3GPP TS 35.231 88 | """ 89 | 90 | ALGONAME = b'TUAK1.0' 91 | 92 | KeccakIterations = 1 93 | 94 | #################### 95 | # OPERATOR SETTING # 96 | #################### 97 | # defined operator settings for the length of the differents outputs 98 | LEN_MAC = 64 # can be 64, 128 or 256 bit 99 | LEN_RES = 64 # can be 32, 64, 128 or 256 bit 100 | LEN_CK = 128 # can be 128 or 256 bit 101 | LEN_IK = 128 # can be 128 or 256 bit 102 | # input length for K: 128 or 256 bit 103 | # input length for TOP: 256 bit 104 | 105 | 106 | def __init__(self, TOP): 107 | self.TOP = TOP 108 | self.TOPc = None 109 | 110 | def make_topc(self, K, TOP=None): 111 | """return the TOPc value derived from K, TOP and the TUAK configuration 112 | if TOP is None, relies on the instance self.TOP value 113 | """ 114 | if TOP is None: 115 | return make_TOPc(K, self.TOP, self.ALGONAME, self.KeccakIterations) 116 | else: 117 | return make_TOPc(K, TOP, self.ALGONAME, self.KeccakIterations) 118 | 119 | def set_topc(self, TOPc): 120 | """set TOPc and save some Keccak rounds in f1, f1star, f2345, f5star 121 | when producing several vectors for a single subscriber 122 | """ 123 | self.TOPc = TOPc 124 | 125 | def unset_opc(self): 126 | self.TOPc = None 127 | 128 | ################## 129 | # TUAK FUNCTIONS # 130 | ################## 131 | 132 | def f1(self, K, RAND, SQN, AMF, TOP=None): 133 | """return MAC_A [8, 16 or 32 bytes buffer] or None on error 134 | """ 135 | if len(K) not in (16, 32) or len(RAND) != 16 or len(SQN) != 6 or len(AMF) != 2: 136 | log('ERR', 'TUAK.f1: invalid args') 137 | return None 138 | 139 | if self.LEN_MAC == 64: 140 | INSTANCE = 0x08 141 | off = 8 142 | elif self.LEN_MAC == 128: 143 | INSTANCE = 0x10 144 | off = 16 145 | else: 146 | #self.LEN_MAC == 256 147 | INSTANCE = 0x20 148 | off = 32 149 | if len(K) == 32: 150 | INSTANCE += 1 151 | if python_version > 2: 152 | INSTANCE = bytes([INSTANCE]) 153 | else: 154 | INSTANCE = chr(INSTANCE) 155 | 156 | if self.TOPc is not None: 157 | TOPc = self.TOPc 158 | else: 159 | TOPc = self.make_topc(K, TOP) 160 | 161 | INOUT = [] 162 | INOUT.append( TOPc[::-1] ) 163 | INOUT.append( INSTANCE[::-1] ) 164 | INOUT.append( self.ALGONAME[::-1] ) 165 | INOUT.append( RAND[::-1] ) 166 | INOUT.append( AMF[::-1] ) 167 | INOUT.append( SQN[::-1] ) 168 | INOUT.append( K[::-1] ) 169 | if len(K) == 16: 170 | INOUT.append( b'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0' ) 171 | # add padding 172 | INOUT.append( b'\x1f\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x80' ) 173 | INOUT.append( b'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'\ 174 | b'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0' ) 175 | INOUT = b''.join(INOUT) 176 | 177 | for i in range(self.KeccakIterations): 178 | INOUT = keccakp1600(INOUT) 179 | return INOUT[:off][::-1] 180 | 181 | def f1star(self, K, RAND, SQN, AMF, TOP=None): 182 | """return MAC_S [8, 16 or 32 bytes buffer] or None on error 183 | """ 184 | if len(K) not in (16, 32) or len(RAND) != 16 or len(SQN) != 6 or len(AMF) != 2: 185 | log('ERR', 'TUAK.f1star: invalid args') 186 | return None 187 | 188 | if self.LEN_MAC == 64: 189 | INSTANCE = 0x88 190 | off = 8 191 | elif self.LEN_MAC == 128: 192 | INSTANCE = 0x90 193 | off = 16 194 | else: 195 | #self.LEN_MAC == 256 196 | INSTANCE = 0xa0 197 | off = 32 198 | if len(K) == 32: 199 | INSTANCE += 1 200 | if python_version > 2: 201 | INSTANCE = bytes([INSTANCE]) 202 | else: 203 | INSTANCE = chr(INSTANCE) 204 | 205 | if self.TOPc is not None: 206 | TOPc = self.TOPc 207 | else: 208 | TOPc = self.make_topc(K, TOP) 209 | 210 | INOUT = [] 211 | INOUT.append( TOPc[::-1] ) 212 | INOUT.append( INSTANCE[::-1] ) 213 | INOUT.append( self.ALGONAME[::-1] ) 214 | INOUT.append( RAND[::-1] ) 215 | INOUT.append( AMF[::-1] ) 216 | INOUT.append( SQN[::-1] ) 217 | INOUT.append( K[::-1] ) 218 | if len(K) == 16: 219 | INOUT.append( b'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0' ) 220 | # add padding 221 | INOUT.append( b'\x1f\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x80' ) 222 | INOUT.append( b'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'\ 223 | b'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0' ) 224 | INOUT = b''.join(INOUT) 225 | 226 | for i in range(self.KeccakIterations): 227 | INOUT = keccakp1600(INOUT) 228 | return INOUT[:off][::-1] 229 | 230 | def f2345(self, K, RAND, TOP=None): 231 | """return RES [4, 8, 16 or 32], CK [16 or 32], IK [16 or 32] and AK [6] bytes buffers or None on error 232 | """ 233 | if len(K) not in (16, 32) or len(RAND) != 16: 234 | log('ERR', 'TUAK.f234: invalid args') 235 | return None 236 | 237 | if self.LEN_RES == 32: 238 | INSTANCE = 0x40 239 | offres = 4 240 | elif self.LEN_RES == 64: 241 | INSTANCE = 0x48 242 | offres = 8 243 | elif self.LEN_RES == 128: 244 | INSTANCE = 0x50 245 | offres = 16 246 | else: 247 | #self.LEN_RES == 256 248 | INSTANCE = 0x60 249 | offres = 32 250 | if self.LEN_CK == 256: 251 | INSTANCE += 4 252 | offck = 32 253 | else: 254 | offck = 16 255 | if self.LEN_IK == 256: 256 | INSTANCE += 2 257 | offik = 32 258 | else: 259 | offik = 16 260 | if len(K) == 32: 261 | INSTANCE += 1 262 | 263 | if python_version > 2: 264 | INSTANCE = bytes([INSTANCE]) 265 | else: 266 | INSTANCE = chr(INSTANCE) 267 | 268 | if self.TOPc is not None: 269 | TOPc = self.TOPc 270 | else: 271 | TOPc = self.make_topc(K, TOP) 272 | 273 | INOUT = [] 274 | INOUT.append( TOPc[::-1] ) 275 | INOUT.append( INSTANCE[::-1] ) 276 | INOUT.append( self.ALGONAME[::-1] ) 277 | INOUT.append( RAND[::-1] ) 278 | INOUT.append( b'\0\0\0\0\0\0\0\0' ) 279 | INOUT.append( K[::-1] ) 280 | if len(K) == 16: 281 | INOUT.append( b'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0' ) 282 | # add padding 283 | INOUT.append( b'\x1f\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x80' ) 284 | INOUT.append( b'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'\ 285 | b'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0' ) 286 | INOUT = b''.join(INOUT) 287 | 288 | for i in range(self.KeccakIterations): 289 | INOUT = keccakp1600(INOUT) 290 | return INOUT[:offres][::-1], INOUT[32:32+offck][::-1], INOUT[64:64+offik][::-1], INOUT[96:102][::-1] 291 | 292 | def f5star(self, K, RAND, TOP=None): 293 | """return AK [6 bytes buffer] or None on error 294 | """ 295 | if len(K) not in (16, 32) or len(RAND) != 16: 296 | log('ERR', 'TUAK.f5star: invalid args') 297 | return None 298 | 299 | INSTANCE = 0xc0 300 | if len(K) == 32: 301 | INSTANCE += 1 302 | 303 | if python_version > 2: 304 | INSTANCE = bytes([INSTANCE]) 305 | else: 306 | INSTANCE = chr(INSTANCE) 307 | 308 | if self.TOPc is not None: 309 | TOPc = self.TOPc 310 | else: 311 | TOPc = self.make_topc(K, TOP) 312 | 313 | INOUT = [] 314 | INOUT.append( TOPc[::-1] ) 315 | INOUT.append( INSTANCE[::-1] ) 316 | INOUT.append( self.ALGONAME[::-1] ) 317 | INOUT.append( RAND[::-1] ) 318 | INOUT.append( b'\0\0\0\0\0\0\0\0' ) 319 | INOUT.append( K[::-1] ) 320 | if len(K) == 16: 321 | INOUT.append( b'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0' ) 322 | # add padding 323 | INOUT.append( b'\x1f\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x80' ) 324 | INOUT.append( b'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'\ 325 | b'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0' ) 326 | INOUT = b''.join(INOUT) 327 | 328 | for i in range(self.KeccakIterations): 329 | INOUT = keccakp1600(INOUT) 330 | return INOUT[96:102][::-1] 331 | 332 | -------------------------------------------------------------------------------- /CryptoMobile/__init__.py: -------------------------------------------------------------------------------- 1 | __all__ = ['utils', 'AES', 'CMAC', 'CM', 'Milenage', 'TUAK', 'conv'] 2 | __version__ = '0.3' 3 | -------------------------------------------------------------------------------- /CryptoMobile/utils.py: -------------------------------------------------------------------------------- 1 | # −*− coding: UTF−8 −*− 2 | #/** 3 | # * Software Name : CryptoMobile 4 | # * Version : 0.3 5 | # * 6 | # * Copyright 2017. Benoit Michau. ANSSI. 7 | # * 8 | # * This program is free software: you can redistribute it and/or modify 9 | # * it under the terms of the GNU General Public License version 2 as published 10 | # * by the Free Software Foundation. 11 | # * 12 | # * This program is distributed in the hope that it will be useful, 13 | # * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # * GNU General Public License for more details. 16 | # * 17 | # * You will find a copy of the terms and conditions of the GNU General Public 18 | # * License version 2 in the "license.txt" file or 19 | # * see http://www.gnu.org/licenses/ or write to the Free Software Foundation, 20 | # * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 21 | # * 22 | # *-------------------------------------------------------- 23 | # * File Name : CryptoMobile/utils.py 24 | # * Created : 2017-08-22 25 | # * Authors : Benoit Michau 26 | # *-------------------------------------------------------- 27 | #*/ 28 | 29 | import sys 30 | if sys.version_info[0] < 3: 31 | py_vers = 2 32 | int_types = (int, long) 33 | else: 34 | py_vers = 3 35 | int_types = (int, ) 36 | 37 | 38 | MAX_UINT32 = 1<<32 39 | MAX_UINT64 = 1<<64 40 | 41 | 42 | if py_vers > 2: 43 | 44 | def xor_buf(b1, b2): 45 | return bytes([b1[i]^b2[i] for i in range(0, min(len(b1), len(b2)))]) 46 | 47 | def int_from_bytes(b): 48 | return int.from_bytes(b, 'big') 49 | 50 | def bytes_from_int(i, length): 51 | return i.to_bytes(length, 'big') 52 | 53 | else: 54 | 55 | def xor_buf(b1, b2): 56 | b1, b2 = bytearray(b1), bytearray(b2) 57 | return b''.join([chr(b1[i]^b2[i]) for i in range(0, min(len(b1), len(b2)))]) 58 | 59 | def int_from_bytes(b): 60 | return reduce(lambda x, y: (x<<8) + y, map(ord, b)) 61 | 62 | def bytes_from_int(i, length): 63 | return bytes(bytearray([(i>>o) & 0xff for o in range(8*(length-1), -1, -8)])) 64 | 65 | 66 | # CryptoMobile-wide Exception handler 67 | class CMException(Exception): 68 | """CryptoMobile specific exception 69 | """ 70 | pass 71 | 72 | 73 | # convinience function: change the content if required 74 | def log(level='DBG', msg=''): 75 | # log wrapper 76 | print('[%s] %s' % (level, msg)) 77 | 78 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CryptoMobile toolkit 2 | 3 | ## Update 2019 4 | The fork at [P1sec](https://github.com/P1sec/CryptoMobile) is going to be more actively maintained and extended than this project. 5 | Do not hesitate to clone it instead of this one. 6 | 7 | 8 | ## About 9 | This toolkit implements python wrappers around 3G and LTE encryption and 10 | integrity protection algorithms, COMP128, Milenage and TUAK authentication 11 | algorithms, and ECIES identity protection scheme. 12 | 13 | 14 | ## Disclaimer 15 | This is delivered for study only: beware that cryptographic material, 16 | especially ciphering algorithms are always subject to national regulation. 17 | Moreover, use in real networks and equipments of some of the algorithms provided 18 | are subect to agreement / licensing by the GSMA and / or the ETSI: 19 | see [GSMA](https://www.gsma.com/aboutus/leadership/committees-and-groups/working-groups/fraud-security-group/security-algorithms) 20 | and [ETSI](https://www.etsi.org/security-algorithms-and-codes/security-algorithms). 21 | 22 | 23 | ## Installation 24 | The standard installation process is to use the CPython build environment to compile 25 | C files and install them together with the Python wrappers. The Milenage and EEA2/EIA2 26 | algorithms moreover require one of the following Python cryptographic library to support 27 | AES: 28 | - [pycryptodome](https://www.pycryptodome.org/) or 29 | - [cryptography](https://cryptography.io/en/latest/) 30 | 31 | The ECIES module requires the last one (cryptography) to work, as no support for ECIES 32 | with pycryptodome as been developped yet. 33 | 34 | 35 | This library supports both Python 2.7 and 3.X versions. 36 | An installation script is available: it installs the library within your Python 37 | package directory: 38 | 39 | ``` 40 | python setup.py install 41 | ``` 42 | or to make a system-wide install 43 | ``` 44 | sudo python setup.py install 45 | ``` 46 | 47 | It is also possible to test the library before installing it: 48 | 49 | ``` 50 | python setup.py test 51 | ``` 52 | 53 | Or to simply build the library without installing it in the system: 54 | 55 | ``` 56 | python setup.py build 57 | ``` 58 | 59 | For generic info on building C extensions on Windows, see the 60 | [Python wiki](https://wiki.python.org/moin/WindowsCompilers). 61 | When building on a Windows system using the MSVC compiler, the .c files will be automatically 62 | renamed to .cc by the install script in order to get them compiled correctly by the MSVC compiler. 63 | 64 | To be noted also that the library builds and runs fine with pypy3. 65 | 66 | 67 | ### Installing the ctypes version instead of the CPython wrappers 68 | There is still the possibility to install manually the historical version (before 2019) 69 | of the library which uses Python-only _ctypes_ source files. A *CM_ctypes.py* is available 70 | in the \_ctypes directory for this purpose. 71 | Please note that this part is not supported anymore, no more tested, and may not work correctly 72 | or even at all. 73 | 74 | 75 | ## Usage 76 | Most of the classes and methods have docstrings. Just read them to get information 77 | on how to use and call them. 78 | 79 | 80 | Warning: most of the C reference implementations are using global or static variables, 81 | which are making them not thread-safe. Using them through Python is however OK thanks 82 | to the GIL, but beware in case you want to use them directly from C. 83 | 84 | 85 | ### CMAC mode of operation 86 | This is the CBC-MAC mode as defined by NIST. It works with any block cipher primitive, 87 | and returns MAC of any length in bits. This is written in pure Python. 88 | 89 | Here is an example on how to use it with AES: 90 | ``` 91 | >>> from CryptoMobile.CMAC import CMAC 92 | >>> help(CMAC) 93 | [...] 94 | >>> from CryptoMobile.AES import AES_ECB 95 | >>> key = 16*b'A' 96 | >>> cmac = CMAC(key, AES_ECB, Tlen=48) 97 | >>> cmac.cmac(200*b'test') 98 | b'\xf7\xad\x89-j\n' 99 | >>> cmac.cmac(200*b'test', (200*8)-2) # this is to not compute the MAC over the last 2 bits of the input 100 | b'\xa7\x7f\xc4\xbf\xfc\xf4' 101 | ``` 102 | 103 | ### COMP128 104 | This is the Python wrapper over the COMP128 v1, v2 and v3 algorithms. The C code 105 | has been taken from the FreeRADIUS project. 106 | 107 | Here is an example on how to use it: 108 | ``` 109 | >>> from pycomp128 import * 110 | >>> help(comp128v1) 111 | [...] 112 | >>> key, rand = 16*b'A', 16*b'B' 113 | >>> comp128v1(key, rand) 114 | (b'#9\x0b^', b"\x08\xb6'\xf36\x80\xec\x00") 115 | >>> comp128v2(key, rand) 116 | (b'\x8a\x9b\xaaI', b']\xdcPs\xa6:\x04\x00') 117 | >>> comp128v3(key, rand) 118 | (b'\x8a\x9b\xaaI', b']\xdcPs\xa6:\x07\xf9') 119 | ``` 120 | 121 | ### Milenage 122 | This is Python wrapper over the Milenage algorithm. The mode of operation is written 123 | in Python, and makes use of the AES function from one of the AES Python backend found. 124 | 125 | c1 to c5 and r1 to r5 constants are implemented as class attribute. 126 | The class must be instantiated with the OP parameter. 127 | 128 | Here is an example on how to use it: 129 | ``` 130 | >>> from CryptoMobile.Milenage import Milenage 131 | >>> help(Milenage) 132 | [...] 133 | >>> Milenage.c1 134 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' 135 | >>> Milenage.c2 136 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01' 137 | >>> Milenage.r3 138 | 32 139 | >>> OP = 16*b'F' 140 | >>> Mil = Milenage(OP) 141 | >>> key, rand = 16*b'A', 16*b'B' 142 | >>> help(Mil.f1) 143 | [...] 144 | >>> Mil.f1(key, rand, SQN=b'\0\0\0\0\x12\x34', AMF=b'\0\0') 145 | b'\x18\x92\x97\xa2\xbb\x08i\xf0' 146 | >>> Mil.f1(key, rand, SQN=b'\0\0\0\0\x12\x34', AMF=b'\0\0', OP=16*b'G') # it is possible the use a different OP parameter 147 | b'E\xf0\xb4\xef\x0c\xa6\x95\xe1' 148 | >>> help(Mil.f2345) 149 | [...] 150 | >>> Mil.f2345(key, rand) 151 | (b'\xdd\x0b\x0f\x95\x92\x06\x1e\xb9', b'~\x8d\xf5&\xe37\xc2\xaf\xe4\x83\xc5\x802\xf7\x1fV', b'\x82;\xcfM\xc5\xfc{\x06BM\xd1\xd6UZJ\xa2', b'g\xe8\x85\r\x0b\xd9') 152 | ``` 153 | 154 | The defaut behaviour is to recompute the OPc at each method call. In order to save 155 | some AES rounds in case you want to compute several authentication vectors for a given 156 | subscriber, it is possible to set the OPc before calling the f methods. 157 | ``` 158 | >>> help(Mil.make_opc) 159 | [...] 160 | >>> from CryptoMobile.Milenage import make_OPc 161 | >>> Mil.set_opc(make_OPc(key, OP)) 162 | >>> Mil.f1(key, rand, SQN=b'\0\0\0\0\x12\x35', AMF=b'\0\0') 163 | b'\xf7~|\x95\x9e\xbf\xfb?' 164 | >>> Mil.f2345(key, rand) 165 | (b'\xdd\x0b\x0f\x95\x92\x06\x1e\xb9', b'~\x8d\xf5&\xe37\xc2\xaf\xe4\x83\xc5\x802\xf7\x1fV', b'\x82;\xcfM\xc5\xfc{\x06BM\xd1\xd6UZJ\xa2', b'g\xe8\x85\r\x0b\xd9') 166 | >>> Mil.unset_opc() 167 | ``` 168 | 169 | Some conversion functions are also provided in the Milenage module: 170 | - conv\_C2, conv\_C3, conv\_C4 and conv\_C5 for 2G / 3G authentication vectors conversion 171 | - conv\_A2, conv\_A3, conv\_A4 and conv\_A7 for LTE key derivation and 3G / LTE authentication 172 | vectors conversion 173 | 174 | 175 | ### TUAK 176 | This is the Python wrapper over the TUAK algorithm. The mode of operation is written 177 | in Python, and makes use of the KeccakP-1600 permutation function. The C code for this 178 | permutation function has been taken from the 3GPP TS 35.231 specification. 179 | 180 | TUAK algorithm is to be used similarly as Milenage. TOP (TUAK-OP) is replacing OP 181 | and TOPc is replacing OPc. TOP, TOPc are 32 bytes, secret keys K can be 16 or 32 bytes. 182 | Length of outputs produced (MAC, RES, CK and IK) can be configured through the following 183 | class attributes too: LEN\_CK, LEN\_IK, LEN\_MAC, LEN\_RES. 184 | Moreover, the algorithm can be personalized with 2 parameters, implemented as class 185 | attributes: ALGONAME and KeccakIterations. On the other side, there is no such constants 186 | as c1..c5 and r1..r5, as in Milenage. 187 | 188 | Here is an example on how to use it: 189 | ``` 190 | >>> from CryptoMobile.TUAK import TUAK 191 | >>> help(TUAK) 192 | [...] 193 | >>> TUAK.ALGONAME 194 | b'TUAK1.0' 195 | >>> TUAK.KeccakIterations 196 | 1 197 | >>> TOP = 32*b'F' 198 | >>> Tuak = TUAK(TOP) 199 | >>> key, rand = 32*b'A', 16*b'B' 200 | >>> help(Tuak.f1) 201 | [...] 202 | >>> Tuak.f1(key, rand, SQN=b'\0\0\0\0\x12\x34', AMF=b'\x80\0') 203 | b'\xdd\xf1\xc7w\x11x\xce\xdb' 204 | >>> Tuak.f2345(key, rand) 205 | (b'}/\xdc\xd4\xcb(qG', b'\xa8\x1dF\x84\x80\xac\t\xab\xe4\xa3\xf6\xe1\x8b\x9b7\xfe', b'g~=\xaf1\xfcy\x9b\x92\xc6\xd2M\xfa\xd0\xed\t', b'\x83\x1e\xcbp\xa6"') 206 | ``` 207 | 208 | TOPc handling is similar as in Milenage and can be set explicitly through the set\_topc() method 209 | before calling f1() and f2345() methods several times, then finally unset with unset\_topc() method. 210 | 211 | 212 | ### Kasumi-based encryption and integrity protection algorithms 213 | This is a Python wrapper around the reference C code of Kasumi and its mode of operation 214 | for 3G networks. Kasumi is a block cipher working with 64 bit blocks. 215 | 216 | Here is an example on how to use the Kasumi primitive: 217 | ``` 218 | >>> from pykasumi import * 219 | >>> help(kasumi_keyschedule) 220 | [...] 221 | >>> help(kasumi_kasumi) 222 | [...] 223 | >>> key, block_in = 16*b'A', 8*b'B' 224 | >>> kasumi_keyschedule(key) 225 | >>> kasumi_kasumi(block_in) 226 | b"S\xf6']\x1c\x1e\xfd\x00" 227 | ``` 228 | 229 | And the Kasumi in F8 and F9 modes of operation: 230 | ``` 231 | >>> help(kasumi_f8) 232 | [...] 233 | >>> help(kasumi_f9) 234 | [...] 235 | >>> key, count, bearer, dir = 16*b'A', 107, 3, 0 236 | >>> kasumi_f8(key, count, bearer, dir, 10*b'test', 10*4*8) 237 | b'q\xe9\x86\xdd\xde\xc1\x14\xb0=pv2|\xe8\\Ib\x84\xa1\xf9\xc0\x01=)\xac!mV\xe4\xc15L\t\xf0\x1f\x1b\x02\xb8\xf9l' 238 | >>> kasumi_f9(key, count, bearer, dir, 10*b'test', 10*4*8) 239 | b'\x1c!j\x0e' 240 | ``` 241 | 242 | ### SNOW-3G-based encryption and integrity protection algorithms 243 | This is a Python wrapper around the reference C code of SNOW-3G and its mode of operation 244 | for 3G and LTE networks. SNOW-3G is a stream cipher working with 32 bit words. 245 | 246 | Here is an example on how to use the SNOW-3G primitive: 247 | ``` 248 | >>> from pysnow import * 249 | >>> help(snow_initialize) 250 | [...] 251 | >>> help(snow_generatekeystream) 252 | [...] 253 | >>> key, iv = 16*b'A', 16*b'B' 254 | >>> snow_initialize(key, iv) 255 | >>> snow_generatekeystream(6) 256 | b'\\^\xff\x98\xad\xa6\x17\xb8\xa4e\x03S\x93T\xbew\xc7\xd1gpr\xf3\x99\xd9' 257 | ``` 258 | 259 | And the SNOW-3G in F8 and F9 modes of operation: 260 | ``` 261 | >>> help(snow_f8) 262 | [...] 263 | >>> help(snow_f9) 264 | [...] 265 | >>> key, count, bearer, dir = 16*b'A', 107, 3, 0 266 | >>> snow_f8(key, count, bearer, dir, 10*b'test', 10*4*8) 267 | b'{\x98\xa1\x90\x0c\x9f\xe9zNp3\xba\xdc\xa6|-\xfe\x91\xffk\x99\x9d\xbc^\xc3\xe1n\xbd\x06U\x98\xfa\x82 \x1a\xf2\xf6\x08\xbb\xe7' 268 | >>> snow_f9(key, count, bearer, dir, 10*b'test', 10*4*8) 269 | b'\xe0\x8e\xde\x85' 270 | ``` 271 | 272 | The EEA1-128 and EIA1-128 modes of operation for LTE are similar to F8 and F9 for 3G 273 | networks. 274 | 275 | 276 | ### ZUC-based encryption and integrity protection algorithms 277 | This is a Python wrapper around the reference C code of ZUC and its mode of operation 278 | for LTE networks. ZUC is a stream cipher working with 32 bit words. 279 | 280 | Here is an example on how to use the ZUC primitive: 281 | ``` 282 | >>> from pyzuc import * 283 | >>> help(zuc_initialization) 284 | [...] 285 | >>> help(zuc_generatekeystream) 286 | [...] 287 | >>> key, iv = 16*b'A', 16*b'B' 288 | >>> zuc_initialization(key, iv) 289 | >>> zuc_generatekeystream(4) 290 | b'\xcf{\x10P\x1e\xf3c\x13\x1c}\x0c\xc2\x8c\xd8\x1a\xae' 291 | ``` 292 | 293 | And the ZUC in EEA3 and EIA3 modes of operation: 294 | ``` 295 | >>> help(zuc_eea3) 296 | [...] 297 | >>> help(zuc_eia3) 298 | [...] 299 | >>> key, count, bearer, dir = 16*b'A', 107, 3, 0 300 | >>> zuc_eea3(key, count, bearer, dir, 10*4*8, 10*b'test') 301 | b'\xda\x9as,\x97:\x86)]\xde\x8b\x14Qq\x85\x15cME$\xc4)\xe7\x7f@\xfe\x10\x1f\xcd\xb05G\xa0\x1d9\x92\x85L2 ' 302 | >>> zuc_eia3(key, count, bearer, dir, 10*4*8, 10*b'test') 303 | b'X\xcb\xa1\x9c' 304 | ``` 305 | 306 | ### The CM module, gathering all 3G and LTE encryption and integrity protection algorithms in one place 307 | The CM module implements each algorithm as a class, with its primitives and 3G and / or LTE 308 | modes of operation as specific methods. 309 | Finally, UEA and UIA are aliases for the given UMTS encryption and integrity protection 310 | algorithms, and EEA and EIA are aliases for the given LTE encryption and integrity 311 | protection algorithms. 312 | 313 | Here is an example with the 2nd UMTS algorithm (SNOW-3G based) and the 2nd and 3rd 314 | LTE algorithms (AES-based and ZUC-based): 315 | ``` 316 | >>> from CryptoMobile.CM import * 317 | >>> dir() 318 | ['AES_3GPP', 'EEA1', 'EEA2', 'EEA3', 'EIA1', 'EIA2', 'EIA3', 'KASUMI', 'SNOW3G', 'UEA1', 'UEA2', 'UIA1', 'UIA2', 'ZUC', '__builtins__', '__doc__', '__name__', '__package__'] 319 | >>> help(UIA2) 320 | [...] 321 | >>> UIA2(key=16*b'\xab', count=0x1234, fresh=0x986532ab, dir=0, data=100*b'nepascourirauborddelapiscine') 322 | b':\xe5t:' 323 | >>> help(UEA2) 324 | [...] 325 | >>> UEA2(key=16*b'\xab', count=0x1234, bearer=0x8, dir=0, data=100*b'nepascourirauborddelapiscine') 326 | b'\x03Z\xa0\x83\x14\x198l\x1b\x91\\\x94\x18\xfc\xbd\xecb-\xdfs1\xd6\xbb1\x88y\xf0\xc9\xf5\xec\xc5\x1b\x7f\xcc...' 327 | >>> UEA2(key=16*b'\xab', count=0x1234, bearer=0x8, dir=0, data=_) 328 | b'nepascourirauborddelapiscinenepascourirauborddelapiscinenepascourirauborddelapiscinenepascourirauborddelapi...' 329 | 330 | >>> help(EEA2) 331 | [...] 332 | >>> EEA2(key=16*b'\xc1', count=0x9955ab, bearer=0x16, dir=1, data=50*b'MonPantalonS\'EstDecousu', bitlen=1149) 333 | b'-y\xf1\xee\xb7\xe4\x0c\xf2\xdfz`\xb04"\x8c\xda\xc8B!n\x863V"\xaei\x91\x1b\xc5\xfc\x1dx\xb9l\xe8\x99q\\q\x88\x91\xc8f\r\x05\xdf\x94S\x97\xc0\x96\xb75\x00@\...' 334 | >>> EEA2(key=16*b'\xc1', count=0x9955ab, bearer=0x16, dir=1, data=_, bitlen=1149) 335 | b"MonPantalonS'EstDecousuMonPantalonS'EstDecousuMonPantalonS'EstDecousuMonPantalonS'EstDecousuMonPantalonS'EstDecousuMonPantalonS'EstDecousuMonPah" 336 | >>> help(EIA3) 337 | [...] 338 | >>> EIA3(key=16*b'\xc1', count=0x9955ab, bearer=0x16, dir=1, data=50*'MonPantalonS\'EstDecousu', bitlen=1149) 339 | b'\xa9\xc5h\x9e' 340 | ``` 341 | 342 | 343 | ### ECIES module to support 5G SUPI / SUCI protection scheme 344 | The ECIES module, which relies on the python cryptography library, supports both 345 | ECIES profiles A and B, as described in 3GPP TS 33.501, annex C. 346 | 347 | At first a fixed Home-Network public / private keypair needs to be established. For this, 348 | the module EC can be used: 349 | ``` 350 | >>> from CryptoMobile.EC import * 351 | >>> ec = X25519() # using Curve25519 elliptic curve, i.e. profile A 352 | >>> ec.generate_keypair() 353 | >>> hn_pubkey = ec.get_pubkey() 354 | >>> hn_pubkey 355 | b"\xd9-\x98\xc5\x08\xa7M\x18\x80bi\x0b\xfa-\xd6[D\xe9'\xe4G|\x1d\xe1sRjXM[\xc7;" 356 | >>> hn_privkey = ec.get_privkey() 357 | >>> hn_privkey 358 | b'`y\x06o\xcf\x9c\xe0\xa4\x18\xb1ks\xe6\x97\xafB)\xeftt2\xcfX\xe4\x82\xaf/\x83[\xcc\xa7O' 359 | >>> ec = ECDH_SECP256R1() # using secp256r1 elliptic curve domain, i.e. profile B 360 | >>> ec.generate_keypair() 361 | >>> hn_pubkey = ec.get_pubkey() 362 | >>> hn_pubkey 363 | b'\x03u\xe82C\xa3.\x0e)\xaf\xd6\xad\n\x01\xafZ2\xca\xc9\x95G\\xG\x9d\xdczU\x91n\x1d%m' 364 | >>> hn_privkey = ec.get_privkey() 365 | >>> hn_privkey # the private key for secp256r1 is longer as it is actually packed into a DER-encoded PKCS8 structure 366 | b"0\x81\x87\x02\x01\x000\x13\x06\x07*\x86H\xce=[...]\x86'\x17" 367 | ``` 368 | 369 | In the principle, the public key of the home network needs to be setup in subscribers' SIM card, whereas 370 | the private key needs to be securely stored within the home network. Take care as the current version 371 | of the EC module does not provide options to manage those generated private keys password-protected when 372 | exported / imported. 373 | 374 | Then, when a subscriber wants to encrypt its fixed identity (e.g. the MSIN part of its IMSI), 375 | to be then decrypted within the home network: 376 | ``` 377 | >>> ue_msin = b'\x102Tv\x98' # BCD-encoded value of the digit-string 0123456789 378 | >>> from CryptoMobile.ECIES import * 379 | >>> ue = ECIES_UE(profile='A') 380 | >>> ue.generate_sharedkey(hn_pubkey) 381 | >>> ue_pubkey, ue_ciphertext, ue_mac = ue.protect(ue_msin) 382 | >>> ue_pubkey, ue_ciphertext, ue_mac 383 | (b'\xe1\x1dBR\x8e\xcbd\x05\x94J\xf2ka\xee^\xaa\x96`\x87X\xe3\x96R\xd8w\xcb\xda\x0e}\xab\x9f\x01', 384 | b'\x93I\x95?8', 385 | b'\xbc\x91\xe1\x0cy\xe2\xf5\xa6') 386 | >>> hn = ECIES_HN(hn_privkey, profile='A') 387 | >>> hn_msin = hn.unprotect(ue_pubkey, ue_ciphertext, ue_mac) 388 | >>> hn_msin == ue_msin 389 | True 390 | ``` 391 | 392 | 393 | ### running Milenage, TUAK, ECIES, UMTS and LTE algorithms test vectors 394 | By running the setup test (see installation), test vectors will all be run. 395 | You can also run some performance test by hand: 396 | 397 | ``` 398 | $ python test/test_CM.py 399 | 1000 full testsets in 7.393 seconds 400 | $ python test/test_Milenage.py 401 | 1000 full testsets in 1.494 seconds 402 | $ python test/test_TUAK.py 403 | 10000 full testsets in 2.215 seconds 404 | $ python test/test_ECIES.py 405 | 1000 full testsets in 2.202 seconds 406 | ``` 407 | 408 | 409 | ## Content 410 | The library is structured into 3 main parts: 411 | - C\_alg: provides C source codes for comp128, KeccakP-1600, Kasumi, SNOW 3G and ZUC 412 | - C\_py: provides C source files wrapping those algorithms with CPython (for both 413 | Python2 and Python3) 414 | - CryptoMobile: provides Python source files. 415 | 416 | And two additional folders: 417 | - test: provides files with test vectors. 418 | - \_ctypes: provides the old CM module which uses ctypes binding to the C files 419 | compiled as shared object. 420 | 421 | Within the CryptoMobile directory, we have the following modules: 422 | - utils.py: provides common routine (eg log() and exception) for the library 423 | - AES.py: provides support for several AES Python backend 424 | - CMAC.py: provides a CMAC class which implement the CMAC mode of operation 425 | - CM.py: the main module providing classes KASUMI, SNOW3G, ZUC (making use of the 426 | wrappers in C\_py) and AES\_3GPP (making use of the AES backend), 427 | and functions UEA1, UIA1, UEA2, UIA2, EEA1, EIA1, EEA2, EIA2, EEA3 and EIA3. 428 | - Milenage.py: provides the Milenage algorithm and conversion functions to be used 429 | for keys and authentication vectors conversion. 430 | - TUAK.py: provides the TUAK algorithm. 431 | - EC.py: provides both Curve25519 and secp256r1 elliptic curve modules for key exchange 432 | - ECIES.py: provides ECIES processing for 5G SUPI / SUCI protection scheme 433 | 434 | 435 | ## Credits 436 | - ETSI / SAGE for providing public cryptographic specifications, together with 437 | reference C source code 438 | - FreeRADIUS, Hacking projects, Sylvain Munaut, for the comp128.c source code 439 | - Developers and maintainers of pycrypto, pycryptodome and cryptography Python libraries 440 | 441 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # −*− coding: UTF−8 −*− 2 | 3 | import os 4 | import distutils.ccompiler as dist_ccomp 5 | import distutils.command.build as dist_build 6 | # 7 | from setuptools import setup, Extension 8 | from setuptools.command.install import install 9 | 10 | 11 | def rename_files(dirpath, fromsuf, tosuf): 12 | for fn in os.listdir(dirpath): 13 | if fn.endswith(fromsuf): 14 | os.rename(dirpath + fn, dirpath + fn[:-len(fromsuf)] + tosuf) 15 | 16 | if dist_ccomp.get_default_compiler() == 'msvc': 17 | # MSVC requires C files to be actually C++ in order to compile them with 18 | # support for "modern" C features 19 | print('compiling C extensions with MSVC: renaming .c to .cc') 20 | rename_files('./C_alg/', '.c', '.cc') 21 | rename_files('./C_py/', '.c', '.cc') 22 | pycomp128 = Extension('pycomp128', sources=['C_py/pycomp128.cc', 'C_alg/comp128.cc']) 23 | pykasumi = Extension('pykasumi', sources=['C_py/pykasumi.cc', 'C_alg/Kasumi.cc']) 24 | pysnow = Extension('pysnow', sources=['C_py/pysnow.cc', 'C_alg/SNOW_3G.cc']) 25 | pyzuc = Extension('pyzuc', sources=['C_py/pyzuc.cc', 'C_alg/ZUC.cc']) 26 | pykeccakp1600 = Extension('pykeccakp1600', sources=['C_py/pykeccakp1600.cc', 'C_alg/KeccakP-1600-3gpp.cc']) 27 | else: 28 | pycomp128 = Extension('pycomp128', sources=['C_py/pycomp128.c', 'C_alg/comp128.c']) 29 | pykasumi = Extension('pykasumi', sources=['C_py/pykasumi.c', 'C_alg/Kasumi.c']) 30 | pysnow = Extension('pysnow', sources=['C_py/pysnow.c', 'C_alg/SNOW_3G.c']) 31 | pyzuc = Extension('pyzuc', sources=['C_py/pyzuc.c', 'C_alg/ZUC.c']) 32 | pykeccakp1600 = Extension('pykeccakp1600', sources=['C_py/pykeccakp1600.c', 'C_alg/KeccakP-1600-3gpp.c']) 33 | 34 | def postop(): 35 | if dist_ccomp.get_default_compiler() == 'msvc': 36 | # reverting the renaming 37 | rename_files('./C_alg/', '.cc', '.c') 38 | rename_files('./C_py/', '.cc', '.c') 39 | 40 | class build_wrapper(dist_build.build): 41 | def run(self): 42 | # on windows: rename *.c to *.cc 43 | # on linux: should run OK 44 | dist_build.build.run(self) 45 | postop() 46 | 47 | class install_wrapper(install): 48 | def run(self): 49 | # on windows: rename *.c to *.cc 50 | # on linux: should run OK 51 | install.run(self) 52 | postop() 53 | 54 | setup( 55 | name='CryptoMobile', 56 | version='0.3', 57 | cmdclass={'install': install_wrapper, 58 | 'build' : build_wrapper}, 59 | packages=['CryptoMobile'], 60 | ext_modules=[pycomp128, pykasumi, pysnow, pyzuc, pykeccakp1600], 61 | 62 | test_suite="test.test_CryptoMobile", 63 | 64 | author='Benoit Michau', 65 | author_email='michau.benoit@gmail.com', 66 | description='CryptoMobile provides (C)Python bindings to reference implementations '\ 67 | 'in C of mobile cryptographic algorithms: Comp128, Milenage, TUAK, Kasumi, SNOW-3G, ZUC', 68 | long_description=open('README.md', 'r').read(), 69 | url='https://github.com/P1Sec/CryptoMobile/', 70 | keywords='cryptography mobile network Kasumi SNOW ZUC Milenage Comp128 TUAK', 71 | license='GPLv2+', 72 | ) 73 | -------------------------------------------------------------------------------- /test/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | #/** 3 | # * Software Name : CryptoMobile 4 | # * Version : 0.3 5 | # * 6 | # * Copyright 2018. Benoit Michau. P1Sec. 7 | # * 8 | # * This program is free software: you can redistribute it and/or modify 9 | # * it under the terms of the GNU General Public License version 2 as published 10 | # * by the Free Software Foundation. 11 | # * 12 | # * This program is distributed in the hope that it will be useful, 13 | # * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # * GNU General Public License for more details. 16 | # * 17 | # * You will find a copy of the terms and conditions of the GNU General Public 18 | # * License version 2 in the "license.txt" file or 19 | # * see http://www.gnu.org/licenses/ or write to the Free Software Foundation, 20 | # * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 21 | # * 22 | # *-------------------------------------------------------- 23 | # * File Name : test/__init__.py 24 | # * Created : 2018-02-09 25 | # * Authors : Benoit Michau 26 | # *-------------------------------------------------------- 27 | #*/ 28 | # 29 | __all__ = ['test_CM', 'test_Milenage', 'test_TUAK', 'test_ECIES', 'test_CryptoMobile'] 30 | __version__ = '0.3' 31 | 32 | -------------------------------------------------------------------------------- /test/test_CryptoMobile.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | #/** 3 | # * Software Name : CryptoMobile 4 | # * Version : 0.3 5 | # * 6 | # * Copyright 2018. Benoit Michau. ANSSI. 7 | # * Copyright 2018. Benoit Michau. P1Sec. 8 | # * 9 | # * This program is free software: you can redistribute it and/or modify 10 | # * it under the terms of the GNU General Public License version 2 as published 11 | # * by the Free Software Foundation. 12 | # * 13 | # * This program is distributed in the hope that it will be useful, 14 | # * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # * GNU General Public License for more details. 17 | # * 18 | # * You will find a copy of the terms and conditions of the GNU General Public 19 | # * License version 2 in the "license.txt" file or 20 | # * see http://www.gnu.org/licenses/ or write to the Free Software Foundation, 21 | # * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 22 | # * 23 | # *-------------------------------------------------------- 24 | # * File Name : test/test_CryptoMobile.py 25 | # * Created : 2018-02-09 26 | # * Authors : Benoit Michau 27 | # *-------------------------------------------------------- 28 | #*/ 29 | 30 | import unittest 31 | 32 | from test.test_CM import ( 33 | test_CM, 34 | testperf as testperf_CM 35 | ) 36 | from test.test_TUAK import ( 37 | test_TUAK, 38 | testperf as testperf_TUAK 39 | ) 40 | try: 41 | from test.test_Milenage import ( 42 | test_Milenage, 43 | testperf as testperf_Milenage 44 | ) 45 | except ImportError: 46 | _with_aes = False 47 | else: 48 | _with_aes = True 49 | try: 50 | from test.test_ECIES import ( 51 | test_ECIES, 52 | testperf as testperf_ECIES 53 | ) 54 | except ImportError: 55 | _with_ec = False 56 | else: 57 | _with_ec = True 58 | 59 | 60 | class TestCryptoMobile(unittest.TestCase): 61 | 62 | # core objects 63 | def test_core(self): 64 | print('[<>] testing CryptoMobile.CM') 65 | test_CM() 66 | 67 | def test_tuak(self): 68 | print('[<>] testing CryptoMobile.TUAK') 69 | test_TUAK() 70 | 71 | if _with_aes: 72 | 73 | def test_milenage(self): 74 | print('[<>] testing CryptoMobile.Milenage') 75 | test_Milenage() 76 | 77 | if _with_ec: 78 | 79 | def test_ecies(self): 80 | print('[<>] testing CryptoMobile.ECIES') 81 | test_ECIES() 82 | 83 | 84 | if __name__ == '__main__': 85 | testperf_CM() 86 | testperf_TUAK() 87 | if _with_aes: 88 | testperf_Milenage() 89 | if _with_ec: 90 | testperf_ECIES() 91 | -------------------------------------------------------------------------------- /test/test_ECIES.py: -------------------------------------------------------------------------------- 1 | # −*− coding: UTF−8 −*− 2 | #/** 3 | # * Software Name : CryptoMobile 4 | # * Version : 0.4 5 | # * 6 | # * Copyright 2020. Benoit Michau. P1Sec. 7 | # * 8 | # * This program is free software: you can redistribute it and/or modify 9 | # * it under the terms of the GNU General Public License version 2 as published 10 | # * by the Free Software Foundation. 11 | # * 12 | # * This program is distributed in the hope that it will be useful, 13 | # * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # * GNU General Public License for more details. 16 | # * 17 | # * You will find a copy of the terms and conditions of the GNU General Public 18 | # * License version 2 in the "license.txt" file or 19 | # * see http://www.gnu.org/licenses/ or write to the Free Software Foundation, 20 | # * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 21 | # * 22 | # *-------------------------------------------------------- 23 | # * File Name : test/test_ECIES.py 24 | # * Created : 2020-01-22 25 | # * Authors : Benoit Michau 26 | # *-------------------------------------------------------- 27 | #*/ 28 | 29 | ######################################################## 30 | # CryptoMobile python toolkit 31 | # 32 | # ECIES computation 33 | # as defined in 3GPP TS 33.501, annex C 34 | ####################################################### 35 | 36 | from time import time 37 | from binascii import unhexlify 38 | 39 | from cryptography.hazmat.primitives.asymmetric.x25519 import X25519PrivateKey 40 | from CryptoMobile.ECIES import * 41 | from CryptoMobile.EC import ( 42 | X25519, 43 | ECDH_SECP256R1, 44 | int_from_bytes, 45 | ec, 46 | _backend 47 | ) 48 | 49 | 50 | # annex C.4.3, ECIES Profile A test data 51 | def test_profileA(): 52 | hn_privkey = unhexlify('c53c22208b61860b06c62e5406a7b330c2b577aa5558981510d128247d38bd1d') 53 | hn_pubkey = unhexlify('5a8d38864820197c3394b92613b20b91633cbd897119273bf8e4a6f4eec0a650') 54 | eph_privkey = unhexlify('c80949f13ebe61af4ebdbd293ea4f942696b9e815d7e8f0096bbf6ed7de62256') 55 | eph_pubkey = unhexlify('b2e92f836055a255837debf850b528997ce0201cb82adfe4be1f587d07d8457d') 56 | shared_key = unhexlify('028ddf890ec83cdf163947ce45f6ec1a0e3070ea5fe57e2b1f05139f3e82422a') 57 | # 58 | plaintext = unhexlify('00012080f6') 59 | ciphertext = unhexlify('cb02352410') 60 | mactag = unhexlify('cddd9e730ef3fa87') 61 | 62 | x1 = X25519(eph_privkey) 63 | x2 = X25519(hn_privkey) 64 | 65 | ue = ECIES_UE(profile='A') 66 | ue.EC.PrivKey = X25519PrivateKey.from_private_bytes(eph_privkey) 67 | hn = ECIES_HN(profile='A', hn_priv_key=hn_privkey) 68 | ue.generate_sharedkey(hn_pubkey, fresh=False) 69 | ue_pk, ue_ct, ue_mac = ue.protect(plaintext) 70 | hn_ct = hn.unprotect(ue_pk, ue_ct, ue_mac) 71 | 72 | return x1.get_pubkey() == eph_pubkey and \ 73 | x1.get_privkey() == eph_privkey and \ 74 | x1.generate_sharedkey(hn_pubkey) == shared_key and \ 75 | x2.get_pubkey() == hn_pubkey and \ 76 | x2.get_privkey() == hn_privkey and \ 77 | x2.generate_sharedkey(eph_pubkey) == shared_key and \ 78 | ue_ct == ciphertext and ue_mac == mactag and hn_ct == plaintext 79 | 80 | 81 | # annex C.4.4, ECIES Profile A test data 82 | def test_profileB(): 83 | hn_pubkey = unhexlify('0272DA71976234CE833A6907425867B82E074D44EF907DFB4B3E21C1C2256EBCD1') # compressed 84 | hn_privkey = unhexlify('F1AB1074477EBCC7F554EA1C5FC368B1616730155E0041AC447D6301975FECDA') 85 | eph_pubkey = unhexlify('039AAB8376597021E855679A9778EA0B67396E68C66DF32C0F41E9ACCA2DA9B9D1') # compressed 86 | eph_privkey = unhexlify('99798858A1DC6A2C68637149A4B1DBFD1FDFF5ADDD62A2142F06699ED7602529') 87 | shared_key = unhexlify('6C7E6518980025B982FBB2FF746E3C2E85A196D252099A7AD23EA7B4C0959CAE') 88 | # 89 | plaintext = unhexlify('00012080F6') 90 | ciphertext = unhexlify('46A33FC271') 91 | mactag = unhexlify('6AC7DAE96AA30A4D') 92 | 93 | x1 = ECDH_SECP256R1(eph_privkey) 94 | x2 = ECDH_SECP256R1(hn_privkey) 95 | 96 | ue = ECIES_UE(profile='B') 97 | ue.EC.PrivKey = ec.derive_private_key(int_from_bytes(eph_privkey), ec.SECP256R1(), backend=_backend) 98 | hn = ECIES_HN(profile='B', hn_priv_key=hn_privkey) 99 | ue.generate_sharedkey(hn_pubkey, fresh=False) 100 | ue_pk, ue_ct, ue_mac = ue.protect(plaintext) 101 | hn_ct = hn.unprotect(ue_pk, ue_ct, ue_mac) 102 | 103 | return x1.get_pubkey() == eph_pubkey and \ 104 | x1.get_privkey() == eph_privkey and \ 105 | x1.generate_sharedkey(hn_pubkey) == shared_key and \ 106 | x2.get_pubkey() == hn_pubkey and \ 107 | x2.get_privkey() == hn_privkey and \ 108 | x2.generate_sharedkey(eph_pubkey) == shared_key and \ 109 | ue_ct == ciphertext and ue_mac == mactag and hn_ct == plaintext 110 | 111 | 112 | def testall(): 113 | return test_profileA() & test_profileB() 114 | 115 | 116 | def testperf(): 117 | T0 = time() 118 | for i in range(1000): 119 | if not testall(): 120 | print('testset failing... exiting') 121 | return 122 | print('1000 full ECIES testsets in %.3f seconds' % (time()-T0, )) 123 | 124 | 125 | def test_ECIES(): 126 | assert( testall() ) 127 | 128 | 129 | if __name__ == '__main__': 130 | testperf() 131 | -------------------------------------------------------------------------------- /test/test_Milenage.py: -------------------------------------------------------------------------------- 1 | # −*− coding: UTF−8 −*− 2 | #/** 3 | # * Software Name : CryptoMobile 4 | # * Version : 0.3 5 | # * 6 | # * Copyright 2020. Benoit Michau. P1Sec. 7 | # * 8 | # * This program is free software: you can redistribute it and/or modify 9 | # * it under the terms of the GNU General Public License version 2 as published 10 | # * by the Free Software Foundation. 11 | # * 12 | # * This program is distributed in the hope that it will be useful, 13 | # * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # * GNU General Public License for more details. 16 | # * 17 | # * You will find a copy of the terms and conditions of the GNU General Public 18 | # * License version 2 in the "license.txt" file or 19 | # * see http://www.gnu.org/licenses/ or write to the Free Software Foundation, 20 | # * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 21 | # * 22 | # *-------------------------------------------------------- 23 | # * File Name : test/test_Milenage.py 24 | # * Created : 2020-01-20 25 | # * Authors : Benoit Michau 26 | # *-------------------------------------------------------- 27 | #*/ 28 | 29 | ######################################################## 30 | # CryptoMobile python toolkit 31 | # 32 | # Milenage authentication algorithm 33 | # as proposed by ETSI SAGE for 3G authentication (AES-based) 34 | # see 3GPP TS 35.205, 206 and 207 35 | ####################################################### 36 | 37 | from time import time 38 | 39 | from CryptoMobile.Milenage import Milenage, make_OPc 40 | 41 | 42 | OPnull = b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' 43 | 44 | 45 | ### 46 | # Milenage f1, f1*, f2, f3, f4, f5 and f5*: testsets from 3GPP TS 35.207, section 4, 5 and 6 47 | ### 48 | 49 | def milenage_testset_1(): 50 | K = b'F[\\\xe8\xb1\x99\xb4\x9f\xaa_\n.\xe28\xa6\xbc' 51 | RAND = b'#U<\xbe\x967\xa8\x9d!\x8a\xe6M\xaeG\xbf5' 52 | SQN = b'\xff\x9b\xb4\xd0\xb6\x07' 53 | AMF = b'\xb9\xb9' 54 | OP = b'\xcd\xc2\x02\xd5\x12> \xf6+mgj\xc7,\xb3\x18' 55 | # 56 | return make_OPc(K, OP) == b'\xcdc\xcbq\x95J\x9fNH\xa5\x99N7\xa0+\xaf' and \ 57 | Milenage(OP).f1(K, RAND, SQN, AMF) == Milenage(OPnull).f1(K, RAND, SQN, AMF, OP) == b'J\x9f\xfa\xc3T\xdf\xaf\xb3' and \ 58 | Milenage(OP).f1star(K, RAND, SQN, AMF) == Milenage(OPnull).f1star(K, RAND, SQN, AMF, OP) == b'\x01\xcf\xaf\x9e\xc4\xe8q\xe9' and \ 59 | Milenage(OP).f2345(K, RAND) == Milenage(OPnull).f2345(K, RAND, OP) == (b'\xa5B\x11\xd5\xe3\xbaP\xbf', 60 | b'\xb4\x0b\xa9\xa3\xc5\x8b*\x05\xbb\xf0\xd9\x87\xb2\x1b\xf8\xcb', b'\xf7i\xbc\xd7Q\x04F\x04\x12vrq\x1cm4A', b'\xaah\x9cd\x83p') and \ 61 | Milenage(OP).f5star(K, RAND) == Milenage(OPnull).f5star(K, RAND, OP) == b'E\x1e\x8b\xec\xa4;' 62 | 63 | def milenage_testset_2(): 64 | K = b'\x03\x96\xeb1{m\x1c6\xf1\x9c\x1c\x84\xcdo\xfd\x16' 65 | RAND = b'\xc0\r`1\x03\xdc\xeeR\xc4G\x81\x19IB\x02\xe8' 66 | SQN = b'\xfd\x8e\xef@\xdf}' 67 | AMF = b'\xaf\x17' 68 | OP = b'\xffS\xba\xde\x17\xdf]Ny0s\xce\x9duy\xfa' 69 | return make_OPc(K, OP) == b'S\xc1Vq\xc6\nKs\x1cU\xb4\xa4A\xc0\xbd\xe2' and \ 70 | Milenage(OP).f1(K, RAND, SQN, AMF) == Milenage(OPnull).f1(K, RAND, SQN, AMF, OP) == b']\xf5\xb3\x18\x07\xe2X\xb0' and \ 71 | Milenage(OP).f1star(K, RAND, SQN, AMF) == Milenage(OPnull).f1star(K, RAND, SQN, AMF, OP) == b'\xa8\xc0\x16\xe5\x1e\xf4\xa3C' and \ 72 | Milenage(OP).f2345(K, RAND) == Milenage(OPnull).f2345(K, RAND, OP) == (b'\xd3\xa6(\xed\x98\x86 \xf0', 73 | b'X\xc43\xffzp\x82\xac\xd4$"\x0f+g\xc5V', b'!\xa8\xc1\xf9)p*\xdb>s\x84\x88\xb9\xf5\xc5\xda', b'\xc4w\x83\x99_r') and \ 74 | Milenage(OP).f5star(K, RAND) == Milenage(OPnull).f5star(K, RAND, OP) == b'0\xf1\x19pa\xc1' 75 | 76 | def milenage_testset_3(): 77 | K = b'\xfe\xc8k\xa6\xebp~\xd0\x89\x05u{\x1b\xb4K\x8f' 78 | RAND = b'\x9f|\x8d\x02\x1a\xcc\xf4\xdb!<\xcf\xf0\xc7\xf7\x1aj' 79 | SQN = b'\x9d\x02wY_\xfc' 80 | AMF = b'r\\' 81 | OP = b'\xdb\xc5\x9a\xdc\xb6\xf9\xa0\xefsTw\xb7\xfa\xdf\x83t' 82 | return make_OPc(K, OP) == b'\x10\x06\x02\x0f\nG\x8b\xf6\xb6\x99\xf1\\\x06.B\xb3' and \ 83 | Milenage(OP).f1(K, RAND, SQN, AMF) == Milenage(OPnull).f1(K, RAND, SQN, AMF, OP) == b'\x9c\xab\xc3\xe9\x9b\xafr\x81' and \ 84 | Milenage(OP).f1star(K, RAND, SQN, AMF) == Milenage(OPnull).f1star(K, RAND, SQN, AMF, OP) == b'\x95\x81K\xa2\xb3\x04C$' and \ 85 | Milenage(OP).f2345(K, RAND) == Milenage(OPnull).f2345(K, RAND, OP) == (b'\x80\x11\xc4\x8c\x0c!N\xd2', 86 | b']\xbd\xbb)T\xe8\xf3\xcd\xe6e\xb0F\x17\x9aP\x98', b'Y\xa9-;Gj\x04CHpU\xcf\x88\xb20{', b'3HM\xc2\x13k') and \ 87 | Milenage(OP).f5star(K, RAND) == Milenage(OPnull).f5star(K, RAND, OP) == b'\xde\xac\xdd\x84\x8c\xc6' 88 | 89 | def milenage_testset_4(): 90 | K = b'\x9eYD\xae\xa9K\x81\x16\\\x82\xfb\xf9\xf3-\xb7Q' 91 | RAND = b"\xce\x83\xdb\xc5J\xc0'J\x15|\x17\xf8\r\x01{\xd6" 92 | SQN = b'\x0b`J\x81\xec\xa8' 93 | AMF = b'\x9e\t' 94 | OP = b'"0\x14\xc5\x80f\x94\xc0\x07\xca\x1e\xee\xf5\x7f\x00O' 95 | return make_OPc(K, OP) == b'\xa6JPz\xe1\xa2\xa9\x8b\xb8\x8e\xb4!\x015\xdc\x87' and \ 96 | Milenage(OP).f1(K, RAND, SQN, AMF) == Milenage(OPnull).f1(K, RAND, SQN, AMF, OP) == b't\xa5\x82 \xcb\xa8LI' and \ 97 | Milenage(OP).f1star(K, RAND, SQN, AMF) == Milenage(OPnull).f1star(K, RAND, SQN, AMF, OP) == b'\xac,\xc7J\x96\x87\x187' and \ 98 | Milenage(OP).f2345(K, RAND) == Milenage(OPnull).f2345(K, RAND, OP) == (b'\xf3e\xcdh<\xd9.\x96', 99 | b'\xe2\x03\xed\xb3\x97\x15t\xf5\xa9K\ra\xb8\x164]', b'\x0cE$\xad\xea\xc0A\xc4\xdd\x83\r \x85O\xc4k', b'\xf0\xb9\xc0\x8a\xd0.') and \ 100 | Milenage(OP).f5star(K, RAND) == Milenage(OPnull).f5star(K, RAND, OP) == b'`\x85\xa8loc' 101 | 102 | def milenage_testset_5(): 103 | K = b'J\xb1\xde\xb0\\\xa6\xce\xb0Q\xfc\x98\xe7}\x02j\x84' 104 | RAND = b't\xb0\xcd`1\xa1\xc83\x9b+l\xe2\xb8\xc4\xa1\x86' 105 | SQN = b'\xe8\x80\xa1\xb5\x80\xb6' 106 | AMF = b'\x9f\x07' 107 | OP = b'-\x16\xc5\xcd\x1f\xdfk"85\x84\xe3\xbe\xf2\xa8\xd8' 108 | return make_OPc(K, OP) == b'\xdc\xf0|\xbdQ\x85R\x90\xb9*\x07\xa9\x89\x1eR>' and \ 109 | Milenage(OP).f1(K, RAND, SQN, AMF) == Milenage(OPnull).f1(K, RAND, SQN, AMF, OP) == b'I\xe7\x85\xdd\x12bn\xf2' and \ 110 | Milenage(OP).f1star(K, RAND, SQN, AMF) == Milenage(OPnull).f1star(K, RAND, SQN, AMF, OP) == b'\x9e\x85y\x036\xbb?\xa2' and \ 111 | Milenage(OP).f2345(K, RAND) == Milenage(OPnull).f2345(K, RAND, OP) == (b'X`\xfc\x1b\xce5\x1e~', 112 | b'vWvk7=\x1c!8\xf3\x07\xe3\xde\x92B\xf9', b"\x1cB\xe9`\xd8\x9b\x8f\xa9\x9f'D\xe0p\x8c\xcbS", b'1\xe1\x1a`\x91\x18') and \ 113 | Milenage(OP).f5star(K, RAND) == Milenage(OPnull).f5star(K, RAND, OP) == b'\xfe%U\xe5J\xa9' 114 | 115 | def milenage_testset_6(): 116 | K = b'l8\xa1\x16\xac(\x0cEOY3.\xe3\\\x8cO' 117 | RAND = b'\xeedf\xbc\x96 ,ZUz\xbb\xef\xf8\xba\xbfc' 118 | SQN = b'AK\x98"!\x81' 119 | AMF = b'Dd' 120 | OP = b'\x1b\xa0\n\x1a|g\x00\xac\x8c?\xf3\xe9j\xd0\x87%' 121 | return make_OPc(K, OP) == b'8\x03\xefSc\xb9G\xc6\xaa\xa2%\xe5\x8f\xae94' and \ 122 | Milenage(OP).f1(K, RAND, SQN, AMF) == Milenage(OPnull).f1(K, RAND, SQN, AMF, OP) == b'\x07\x8a\xdf\xb4\x88$\x1aW' and \ 123 | Milenage(OP).f1star(K, RAND, SQN, AMF) == Milenage(OPnull).f1star(K, RAND, SQN, AMF, OP) == b'\x80$k\x8d\x01\x86\xbc\xf1' and \ 124 | Milenage(OP).f2345(K, RAND) == Milenage(OPnull).f2345(K, RAND, OP) == (b'\x16\xc8#?\x05\xa0\xac(', 125 | b'?\x8cu\x87\xfe\x8eK#:\xf6v\xae\xde0\xba;', b'\xa7Fl\xc1\xe6\xb2\xa13}I\xd3\xb6n\x95\xd7\xb4', b'E\xb0\xf6\x9a\xb0l') and \ 126 | Milenage(OP).f5star(K, RAND) == Milenage(OPnull).f5star(K, RAND, OP) == b'\x1fS\xcd+\x11\x13' 127 | 128 | 129 | def milenage_testsets(): 130 | return milenage_testset_1() and milenage_testset_2() and milenage_testset_3() and\ 131 | milenage_testset_4() and milenage_testset_5() and milenage_testset_6() 132 | 133 | 134 | def testall(): 135 | return milenage_testsets() 136 | 137 | 138 | def testperf(): 139 | T0 = time() 140 | for i in range(1000): 141 | if not testall(): 142 | print('testset failing... exiting') 143 | return 144 | print('1000 full Milenage testsets in %.3f seconds' % (time()-T0, )) 145 | 146 | 147 | def test_Milenage(): 148 | assert( testall() ) 149 | 150 | 151 | if __name__ == '__main__': 152 | testperf() 153 | --------------------------------------------------------------------------------