| 1 | //----------------------------------------------------------------------------- |
| 2 | // Copyright (C) 2010 iZsh <izsh at fail0verflow.com>, Hagen Fritsch |
| 3 | // Copyright (C) 2011 Gerhard de Koning Gans |
| 4 | // Copyright (C) 2014 Midnitesnake & Andy Davies & Martin Holst Swende |
| 5 | // |
| 6 | // This code is licensed to you under the terms of the GNU GPL, version 2 or, |
| 7 | // at your option, any later version. See the LICENSE.txt file for the text of |
| 8 | // the license. |
| 9 | //----------------------------------------------------------------------------- |
| 10 | // High frequency iClass commands |
| 11 | //----------------------------------------------------------------------------- |
| 12 | |
| 13 | #include <stdio.h> |
| 14 | #include <stdlib.h> |
| 15 | #include <string.h> |
| 16 | #include <sys/stat.h> |
| 17 | #include "iso14443crc.h" // Can also be used for iClass, using 0xE012 as CRC-type |
| 18 | #include "data.h" |
| 19 | #include "proxmark3.h" |
| 20 | #include "ui.h" |
| 21 | #include "cmdparser.h" |
| 22 | #include "cmdhficlass.h" |
| 23 | #include "common.h" |
| 24 | #include "util.h" |
| 25 | #include "cmdmain.h" |
| 26 | #include "loclass/des.h" |
| 27 | #include "loclass/cipherutils.h" |
| 28 | #include "loclass/cipher.h" |
| 29 | #include "loclass/ikeys.h" |
| 30 | #include "loclass/elite_crack.h" |
| 31 | #include "loclass/fileutils.h" |
| 32 | #include "protocols.h" |
| 33 | #include "usb_cmd.h" |
| 34 | #include "cmdhfmfu.h" |
| 35 | |
| 36 | static int CmdHelp(const char *Cmd); |
| 37 | |
| 38 | #define ICLASS_KEYS_MAX 8 |
| 39 | static uint8_t iClass_Key_Table[ICLASS_KEYS_MAX][8] = { |
| 40 | { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, |
| 41 | { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, |
| 42 | { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, |
| 43 | { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, |
| 44 | { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, |
| 45 | { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, |
| 46 | { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, |
| 47 | { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 } |
| 48 | }; |
| 49 | |
| 50 | typedef struct iclass_block { |
| 51 | uint8_t d[8]; |
| 52 | } iclass_block_t; |
| 53 | |
| 54 | int xorbits_8(uint8_t val) { |
| 55 | uint8_t res = val ^ (val >> 1); //1st pass |
| 56 | res = res ^ (res >> 1); // 2nd pass |
| 57 | res = res ^ (res >> 2); // 3rd pass |
| 58 | res = res ^ (res >> 4); // 4th pass |
| 59 | return res & 1; |
| 60 | } |
| 61 | |
| 62 | int CmdHFiClassList(const char *Cmd) { |
| 63 | PrintAndLog("Deprecated command, use 'hf list iclass' instead"); |
| 64 | return 0; |
| 65 | } |
| 66 | |
| 67 | int CmdHFiClassSnoop(const char *Cmd) { |
| 68 | UsbCommand c = {CMD_SNOOP_ICLASS}; |
| 69 | SendCommand(&c); |
| 70 | return 0; |
| 71 | } |
| 72 | |
| 73 | int usage_hf_iclass_sim(void) { |
| 74 | PrintAndLog("Usage: hf iclass sim <option> [CSN]"); |
| 75 | PrintAndLog(" options"); |
| 76 | PrintAndLog(" 0 <CSN> simulate the given CSN"); |
| 77 | PrintAndLog(" 1 simulate default CSN"); |
| 78 | PrintAndLog(" 2 Reader-attack, gather reader responses to extract elite key"); |
| 79 | PrintAndLog(" 3 Full simulation using emulator memory (see 'hf iclass eload')"); |
| 80 | PrintAndLog(" example: hf iclass sim 0 031FEC8AF7FF12E0"); |
| 81 | PrintAndLog(" example: hf iclass sim 2"); |
| 82 | PrintAndLog(" example: hf iclass eload 'tagdump.bin'"); |
| 83 | PrintAndLog(" hf iclass sim 3"); |
| 84 | return 0; |
| 85 | } |
| 86 | |
| 87 | #define NUM_CSNS 15 |
| 88 | int CmdHFiClassSim(const char *Cmd) { |
| 89 | uint8_t simType = 0; |
| 90 | uint8_t CSN[8] = {0, 0, 0, 0, 0, 0, 0, 0}; |
| 91 | |
| 92 | if (strlen(Cmd)<1) { |
| 93 | return usage_hf_iclass_sim(); |
| 94 | } |
| 95 | simType = param_get8ex(Cmd, 0, 0, 10); |
| 96 | |
| 97 | if(simType == 0) |
| 98 | { |
| 99 | if (param_gethex(Cmd, 1, CSN, 16)) { |
| 100 | PrintAndLog("A CSN should consist of 16 HEX symbols"); |
| 101 | return usage_hf_iclass_sim(); |
| 102 | } |
| 103 | |
| 104 | PrintAndLog("--simtype:%02x csn:%s", simType, sprint_hex(CSN, 8)); |
| 105 | } |
| 106 | if(simType > 3) |
| 107 | { |
| 108 | PrintAndLog("Undefined simptype %d", simType); |
| 109 | return usage_hf_iclass_sim(); |
| 110 | } |
| 111 | |
| 112 | uint8_t numberOfCSNs=0; |
| 113 | if(simType == 2) |
| 114 | { |
| 115 | UsbCommand c = {CMD_SIMULATE_TAG_ICLASS, {simType,NUM_CSNS}}; |
| 116 | UsbCommand resp = {0}; |
| 117 | |
| 118 | uint8_t csns[8*NUM_CSNS] = { |
| 119 | 0x00, 0x0B, 0x0F, 0xFF, 0xF7, 0xFF, 0x12, 0xE0, |
| 120 | 0x00, 0x04, 0x0E, 0x08, 0xF7, 0xFF, 0x12, 0xE0, |
| 121 | 0x00, 0x09, 0x0D, 0x05, 0xF7, 0xFF, 0x12, 0xE0, |
| 122 | 0x00, 0x0A, 0x0C, 0x06, 0xF7, 0xFF, 0x12, 0xE0, |
| 123 | 0x00, 0x0F, 0x0B, 0x03, 0xF7, 0xFF, 0x12, 0xE0, |
| 124 | 0x00, 0x08, 0x0A, 0x0C, 0xF7, 0xFF, 0x12, 0xE0, |
| 125 | 0x00, 0x0D, 0x09, 0x09, 0xF7, 0xFF, 0x12, 0xE0, |
| 126 | 0x00, 0x0E, 0x08, 0x0A, 0xF7, 0xFF, 0x12, 0xE0, |
| 127 | 0x00, 0x03, 0x07, 0x17, 0xF7, 0xFF, 0x12, 0xE0, |
| 128 | 0x00, 0x3C, 0x06, 0xE0, 0xF7, 0xFF, 0x12, 0xE0, |
| 129 | 0x00, 0x01, 0x05, 0x1D, 0xF7, 0xFF, 0x12, 0xE0, |
| 130 | 0x00, 0x02, 0x04, 0x1E, 0xF7, 0xFF, 0x12, 0xE0, |
| 131 | 0x00, 0x07, 0x03, 0x1B, 0xF7, 0xFF, 0x12, 0xE0, |
| 132 | 0x00, 0x00, 0x02, 0x24, 0xF7, 0xFF, 0x12, 0xE0, |
| 133 | 0x00, 0x05, 0x01, 0x21, 0xF7, 0xFF, 0x12, 0xE0 }; |
| 134 | |
| 135 | memcpy(c.d.asBytes, csns, 8*NUM_CSNS); |
| 136 | |
| 137 | SendCommand(&c); |
| 138 | if (!WaitForResponseTimeout(CMD_ACK, &resp, -1)) { |
| 139 | PrintAndLog("Command timed out"); |
| 140 | return 0; |
| 141 | } |
| 142 | |
| 143 | uint8_t num_mac_responses = resp.arg[1]; |
| 144 | PrintAndLog("Mac responses: %d MACs obtained (should be %d)", num_mac_responses,NUM_CSNS); |
| 145 | |
| 146 | size_t datalen = NUM_CSNS*24; |
| 147 | /* |
| 148 | * Now, time to dump to file. We'll use this format: |
| 149 | * <8-byte CSN><8-byte CC><4 byte NR><4 byte MAC>.... |
| 150 | * So, it should wind up as |
| 151 | * 8 * 24 bytes. |
| 152 | * |
| 153 | * The returndata from the pm3 is on the following format |
| 154 | * <4 byte NR><4 byte MAC> |
| 155 | * CC are all zeroes, CSN is the same as was sent in |
| 156 | **/ |
| 157 | void* dump = malloc(datalen); |
| 158 | memset(dump,0,datalen);//<-- Need zeroes for the CC-field |
| 159 | uint8_t i = 0; |
| 160 | for(i = 0 ; i < NUM_CSNS ; i++) |
| 161 | { |
| 162 | memcpy(dump+i*24, csns+i*8,8); //CSN |
| 163 | //8 zero bytes here... |
| 164 | //Then comes NR_MAC (eight bytes from the response) |
| 165 | memcpy(dump+i*24+16,resp.d.asBytes+i*8,8); |
| 166 | |
| 167 | } |
| 168 | /** Now, save to dumpfile **/ |
| 169 | saveFile("iclass_mac_attack", "bin", dump,datalen); |
| 170 | free(dump); |
| 171 | }else |
| 172 | { |
| 173 | UsbCommand c = {CMD_SIMULATE_TAG_ICLASS, {simType,numberOfCSNs}}; |
| 174 | memcpy(c.d.asBytes, CSN, 8); |
| 175 | SendCommand(&c); |
| 176 | } |
| 177 | |
| 178 | return 0; |
| 179 | } |
| 180 | |
| 181 | int HFiClassReader(const char *Cmd, bool loop, bool verbose) { |
| 182 | bool tagFound = false; |
| 183 | UsbCommand c = {CMD_READER_ICLASS, {FLAG_ICLASS_READER_CSN| |
| 184 | FLAG_ICLASS_READER_CONF|FLAG_ICLASS_READER_AA}}; |
| 185 | // loop in client not device - else on windows have a communication error |
| 186 | c.arg[0] |= FLAG_ICLASS_READER_ONLY_ONCE | FLAG_ICLASS_READER_ONE_TRY; |
| 187 | UsbCommand resp; |
| 188 | while(!ukbhit()){ |
| 189 | SendCommand(&c); |
| 190 | if (WaitForResponseTimeout(CMD_ACK,&resp, 4500)) { |
| 191 | uint8_t readStatus = resp.arg[0] & 0xff; |
| 192 | uint8_t *data = resp.d.asBytes; |
| 193 | |
| 194 | if (verbose) |
| 195 | PrintAndLog("Readstatus:%02x", readStatus); |
| 196 | if( readStatus == 0){ |
| 197 | //Aborted |
| 198 | if (verbose) PrintAndLog("Quitting..."); |
| 199 | return 0; |
| 200 | } |
| 201 | if( readStatus & FLAG_ICLASS_READER_CSN){ |
| 202 | PrintAndLog("CSN: %s",sprint_hex(data,8)); |
| 203 | tagFound = true; |
| 204 | } |
| 205 | if( readStatus & FLAG_ICLASS_READER_CC) PrintAndLog("CC: %s",sprint_hex(data+16,8)); |
| 206 | if( readStatus & FLAG_ICLASS_READER_CONF){ |
| 207 | printIclassDumpInfo(data); |
| 208 | } |
| 209 | if (tagFound && !loop) return 1; |
| 210 | } else { |
| 211 | if (verbose) PrintAndLog("Command execute timeout"); |
| 212 | } |
| 213 | if (!loop) break; |
| 214 | } |
| 215 | return 0; |
| 216 | } |
| 217 | |
| 218 | int CmdHFiClassReader(const char *Cmd) { |
| 219 | return HFiClassReader(Cmd, true, true); |
| 220 | } |
| 221 | |
| 222 | int CmdHFiClassReader_Replay(const char *Cmd) { |
| 223 | uint8_t readerType = 0; |
| 224 | uint8_t MAC[4]={0x00, 0x00, 0x00, 0x00}; |
| 225 | |
| 226 | if (strlen(Cmd)<1) { |
| 227 | PrintAndLog("Usage: hf iclass replay <MAC>"); |
| 228 | PrintAndLog(" sample: hf iclass replay 00112233"); |
| 229 | return 0; |
| 230 | } |
| 231 | |
| 232 | if (param_gethex(Cmd, 0, MAC, 8)) { |
| 233 | PrintAndLog("MAC must include 8 HEX symbols"); |
| 234 | return 1; |
| 235 | } |
| 236 | |
| 237 | UsbCommand c = {CMD_READER_ICLASS_REPLAY, {readerType}}; |
| 238 | memcpy(c.d.asBytes, MAC, 4); |
| 239 | SendCommand(&c); |
| 240 | |
| 241 | return 0; |
| 242 | } |
| 243 | |
| 244 | int iclassEmlSetMem(uint8_t *data, int blockNum, int blocksCount) { |
| 245 | UsbCommand c = {CMD_MIFARE_EML_MEMSET, {blockNum, blocksCount, 0}}; |
| 246 | memcpy(c.d.asBytes, data, blocksCount * 16); |
| 247 | SendCommand(&c); |
| 248 | return 0; |
| 249 | } |
| 250 | |
| 251 | int hf_iclass_eload_usage(void) { |
| 252 | PrintAndLog("Loads iclass tag-dump into emulator memory on device"); |
| 253 | PrintAndLog("Usage: hf iclass eload f <filename>"); |
| 254 | PrintAndLog(""); |
| 255 | PrintAndLog("Example: hf iclass eload f iclass_tagdump-aa162d30f8ff12f1.bin"); |
| 256 | return 0; |
| 257 | } |
| 258 | |
| 259 | int CmdHFiClassELoad(const char *Cmd) { |
| 260 | |
| 261 | char opt = param_getchar(Cmd, 0); |
| 262 | if (strlen(Cmd)<1 || opt == 'h') |
| 263 | return hf_iclass_eload_usage(); |
| 264 | |
| 265 | //File handling and reading |
| 266 | FILE *f; |
| 267 | char filename[FILE_PATH_SIZE]; |
| 268 | if(opt == 'f' && param_getstr(Cmd, 1, filename) > 0) |
| 269 | { |
| 270 | f = fopen(filename, "rb"); |
| 271 | }else{ |
| 272 | return hf_iclass_eload_usage(); |
| 273 | } |
| 274 | |
| 275 | if(!f) { |
| 276 | PrintAndLog("Failed to read from file '%s'", filename); |
| 277 | return 1; |
| 278 | } |
| 279 | |
| 280 | fseek(f, 0, SEEK_END); |
| 281 | long fsize = ftell(f); |
| 282 | fseek(f, 0, SEEK_SET); |
| 283 | |
| 284 | if (fsize < 0) { |
| 285 | PrintAndLog("Error, when getting filesize"); |
| 286 | fclose(f); |
| 287 | return 1; |
| 288 | } |
| 289 | |
| 290 | uint8_t *dump = malloc(fsize); |
| 291 | |
| 292 | size_t bytes_read = fread(dump, 1, fsize, f); |
| 293 | fclose(f); |
| 294 | |
| 295 | printIclassDumpInfo(dump); |
| 296 | //Validate |
| 297 | |
| 298 | if (bytes_read < fsize) |
| 299 | { |
| 300 | prnlog("Error, could only read %d bytes (should be %d)",bytes_read, fsize ); |
| 301 | free(dump); |
| 302 | return 1; |
| 303 | } |
| 304 | //Send to device |
| 305 | uint32_t bytes_sent = 0; |
| 306 | uint32_t bytes_remaining = bytes_read; |
| 307 | |
| 308 | while(bytes_remaining > 0){ |
| 309 | uint32_t bytes_in_packet = MIN(USB_CMD_DATA_SIZE, bytes_remaining); |
| 310 | UsbCommand c = {CMD_ICLASS_EML_MEMSET, {bytes_sent,bytes_in_packet,0}}; |
| 311 | memcpy(c.d.asBytes, dump, bytes_in_packet); |
| 312 | SendCommand(&c); |
| 313 | bytes_remaining -= bytes_in_packet; |
| 314 | bytes_sent += bytes_in_packet; |
| 315 | } |
| 316 | free(dump); |
| 317 | PrintAndLog("Sent %d bytes of data to device emulator memory", bytes_sent); |
| 318 | return 0; |
| 319 | } |
| 320 | |
| 321 | static int readKeyfile(const char *filename, size_t len, uint8_t* buffer) { |
| 322 | FILE *f = fopen(filename, "rb"); |
| 323 | if(!f) { |
| 324 | PrintAndLog("Failed to read from file '%s'", filename); |
| 325 | return 1; |
| 326 | } |
| 327 | fseek(f, 0, SEEK_END); |
| 328 | long fsize = ftell(f); |
| 329 | fseek(f, 0, SEEK_SET); |
| 330 | size_t bytes_read = fread(buffer, 1, len, f); |
| 331 | fclose(f); |
| 332 | if(fsize != len) |
| 333 | { |
| 334 | PrintAndLog("Warning, file size is %d, expected %d", fsize, len); |
| 335 | return 1; |
| 336 | } |
| 337 | if(bytes_read != len) |
| 338 | { |
| 339 | PrintAndLog("Warning, could only read %d bytes, expected %d" ,bytes_read, len); |
| 340 | return 1; |
| 341 | } |
| 342 | return 0; |
| 343 | } |
| 344 | |
| 345 | int usage_hf_iclass_decrypt(void) { |
| 346 | PrintAndLog("Usage: hf iclass decrypt f <tagdump>"); |
| 347 | PrintAndLog(""); |
| 348 | PrintAndLog("OBS! In order to use this function, the file 'iclass_decryptionkey.bin' must reside"); |
| 349 | PrintAndLog("in the working directory. The file should be 16 bytes binary data"); |
| 350 | PrintAndLog(""); |
| 351 | PrintAndLog("example: hf iclass decrypt f tagdump_12312342343.bin"); |
| 352 | PrintAndLog(""); |
| 353 | PrintAndLog("OBS! This is pretty stupid implementation, it tries to decrypt every block after block 6. "); |
| 354 | PrintAndLog("Correct behaviour would be to decrypt only the application areas where the key is valid,"); |
| 355 | PrintAndLog("which is defined by the configuration block."); |
| 356 | return 1; |
| 357 | } |
| 358 | |
| 359 | int CmdHFiClassDecrypt(const char *Cmd) { |
| 360 | uint8_t key[16] = { 0 }; |
| 361 | if(readKeyfile("iclass_decryptionkey.bin", 16, key)) |
| 362 | { |
| 363 | usage_hf_iclass_decrypt(); |
| 364 | return 1; |
| 365 | } |
| 366 | PrintAndLog("Decryption file found... "); |
| 367 | char opt = param_getchar(Cmd, 0); |
| 368 | if (strlen(Cmd)<1 || opt == 'h') |
| 369 | return usage_hf_iclass_decrypt(); |
| 370 | |
| 371 | //Open the tagdump-file |
| 372 | FILE *f; |
| 373 | char filename[FILE_PATH_SIZE]; |
| 374 | if(opt == 'f' && param_getstr(Cmd, 1, filename) > 0) { |
| 375 | f = fopen(filename, "rb"); |
| 376 | if ( f == NULL ) { |
| 377 | PrintAndLog("Could not find file %s", filename); |
| 378 | return 1; |
| 379 | } |
| 380 | } else { |
| 381 | return usage_hf_iclass_decrypt(); |
| 382 | } |
| 383 | |
| 384 | fseek(f, 0, SEEK_END); |
| 385 | long fsize = ftell(f); |
| 386 | fseek(f, 0, SEEK_SET); |
| 387 | uint8_t enc_dump[8] = {0}; |
| 388 | uint8_t *decrypted = malloc(fsize); |
| 389 | des3_context ctx = { DES_DECRYPT ,{ 0 } }; |
| 390 | des3_set2key_dec( &ctx, key); |
| 391 | size_t bytes_read = fread(enc_dump, 1, 8, f); |
| 392 | |
| 393 | //Use the first block (CSN) for filename |
| 394 | char outfilename[FILE_PATH_SIZE] = { 0 }; |
| 395 | snprintf(outfilename,FILE_PATH_SIZE,"iclass_tagdump-%02x%02x%02x%02x%02x%02x%02x%02x-decrypted", |
| 396 | enc_dump[0],enc_dump[1],enc_dump[2],enc_dump[3], |
| 397 | enc_dump[4],enc_dump[5],enc_dump[6],enc_dump[7]); |
| 398 | |
| 399 | size_t blocknum =0; |
| 400 | while(bytes_read == 8) |
| 401 | { |
| 402 | if(blocknum < 7) |
| 403 | { |
| 404 | memcpy(decrypted+(blocknum*8), enc_dump, 8); |
| 405 | }else{ |
| 406 | des3_crypt_ecb(&ctx, enc_dump,decrypted +(blocknum*8) ); |
| 407 | } |
| 408 | printvar("decrypted block", decrypted +(blocknum*8), 8); |
| 409 | bytes_read = fread(enc_dump, 1, 8, f); |
| 410 | blocknum++; |
| 411 | } |
| 412 | fclose(f); |
| 413 | |
| 414 | saveFile(outfilename,"bin", decrypted, blocknum*8); |
| 415 | free(decrypted); |
| 416 | return 0; |
| 417 | } |
| 418 | |
| 419 | int usage_hf_iclass_encrypt(void) { |
| 420 | PrintAndLog("Usage: hf iclass encrypt <BlockData>"); |
| 421 | PrintAndLog(""); |
| 422 | PrintAndLog("OBS! In order to use this function, the file 'iclass_decryptionkey.bin' must reside"); |
| 423 | PrintAndLog("in the working directory. The file should be 16 bytes binary data"); |
| 424 | PrintAndLog(""); |
| 425 | PrintAndLog("example: hf iclass encrypt 0102030405060708"); |
| 426 | PrintAndLog(""); |
| 427 | return 0; |
| 428 | } |
| 429 | |
| 430 | static int iClassEncryptBlkData(uint8_t *blkData) { |
| 431 | uint8_t key[16] = { 0 }; |
| 432 | if(readKeyfile("iclass_decryptionkey.bin", 16, key)) |
| 433 | { |
| 434 | usage_hf_iclass_encrypt(); |
| 435 | return 1; |
| 436 | } |
| 437 | PrintAndLog("Decryption file found... "); |
| 438 | |
| 439 | uint8_t encryptedData[16]; |
| 440 | uint8_t *encrypted = encryptedData; |
| 441 | des3_context ctx = { DES_DECRYPT ,{ 0 } }; |
| 442 | des3_set2key_enc( &ctx, key); |
| 443 | |
| 444 | des3_crypt_ecb(&ctx, blkData,encrypted); |
| 445 | //printvar("decrypted block", decrypted, 8); |
| 446 | memcpy(blkData,encrypted,8); |
| 447 | |
| 448 | return 1; |
| 449 | } |
| 450 | |
| 451 | int CmdHFiClassEncryptBlk(const char *Cmd) { |
| 452 | uint8_t blkData[8] = {0}; |
| 453 | char opt = param_getchar(Cmd, 0); |
| 454 | if (strlen(Cmd)<1 || opt == 'h') |
| 455 | return usage_hf_iclass_encrypt(); |
| 456 | |
| 457 | //get the bytes to encrypt |
| 458 | if (param_gethex(Cmd, 0, blkData, 16)) |
| 459 | { |
| 460 | PrintAndLog("BlockData must include 16 HEX symbols"); |
| 461 | return 0; |
| 462 | } |
| 463 | if (!iClassEncryptBlkData(blkData)) return 0; |
| 464 | |
| 465 | printvar("encrypted block", blkData, 8); |
| 466 | return 1; |
| 467 | } |
| 468 | |
| 469 | void Calc_wb_mac(uint8_t blockno, uint8_t *data, uint8_t *div_key, uint8_t MAC[4]) { |
| 470 | uint8_t WB[9]; |
| 471 | WB[0] = blockno; |
| 472 | memcpy(WB + 1,data,8); |
| 473 | doMAC_N(WB,sizeof(WB),div_key,MAC); |
| 474 | //printf("Cal wb mac block [%02x][%02x%02x%02x%02x%02x%02x%02x%02x] : MAC [%02x%02x%02x%02x]",WB[0],WB[1],WB[2],WB[3],WB[4],WB[5],WB[6],WB[7],WB[8],MAC[0],MAC[1],MAC[2],MAC[3]); |
| 475 | } |
| 476 | |
| 477 | static bool select_only(uint8_t *CSN, uint8_t *CCNR, bool use_credit_key, bool verbose) { |
| 478 | UsbCommand resp; |
| 479 | |
| 480 | UsbCommand c = {CMD_READER_ICLASS, {0}}; |
| 481 | c.arg[0] = FLAG_ICLASS_READER_ONLY_ONCE | FLAG_ICLASS_READER_CC | FLAG_ICLASS_READER_ONE_TRY; |
| 482 | if (use_credit_key) |
| 483 | c.arg[0] |= FLAG_ICLASS_READER_CEDITKEY; |
| 484 | |
| 485 | clearCommandBuffer(); |
| 486 | SendCommand(&c); |
| 487 | if (!WaitForResponseTimeout(CMD_ACK,&resp,4500)) |
| 488 | { |
| 489 | PrintAndLog("Command execute timeout"); |
| 490 | return false; |
| 491 | } |
| 492 | |
| 493 | uint8_t isOK = resp.arg[0] & 0xff; |
| 494 | uint8_t *data = resp.d.asBytes; |
| 495 | |
| 496 | memcpy(CSN,data,8); |
| 497 | if (CCNR!=NULL)memcpy(CCNR,data+16,8); |
| 498 | if(isOK > 0) |
| 499 | { |
| 500 | if (verbose) PrintAndLog("CSN: %s",sprint_hex(CSN,8)); |
| 501 | } |
| 502 | if(isOK <= 1){ |
| 503 | PrintAndLog("Failed to obtain CC! Aborting"); |
| 504 | return false; |
| 505 | } |
| 506 | return true; |
| 507 | } |
| 508 | |
| 509 | static bool select_and_auth(uint8_t *KEY, uint8_t *MAC, uint8_t *div_key, bool use_credit_key, bool elite, bool rawkey, bool verbose) { |
| 510 | uint8_t CSN[8]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; |
| 511 | uint8_t CCNR[12]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; |
| 512 | |
| 513 | if (!select_only(CSN, CCNR, use_credit_key, verbose)) |
| 514 | return false; |
| 515 | |
| 516 | //get div_key |
| 517 | if(rawkey) |
| 518 | memcpy(div_key, KEY, 8); |
| 519 | else |
| 520 | HFiClassCalcDivKey(CSN, KEY, div_key, elite); |
| 521 | PrintAndLog("Authing with %s: %02x%02x%02x%02x%02x%02x%02x%02x", rawkey ? "raw key" : "diversified key", div_key[0],div_key[1],div_key[2],div_key[3],div_key[4],div_key[5],div_key[6],div_key[7]); |
| 522 | |
| 523 | doMAC(CCNR, div_key, MAC); |
| 524 | UsbCommand resp; |
| 525 | UsbCommand d = {CMD_ICLASS_AUTHENTICATION, {0}}; |
| 526 | memcpy(d.d.asBytes, MAC, 4); |
| 527 | clearCommandBuffer(); |
| 528 | SendCommand(&d); |
| 529 | if (!WaitForResponseTimeout(CMD_ACK,&resp,4500)) |
| 530 | { |
| 531 | PrintAndLog("Auth Command execute timeout"); |
| 532 | return false; |
| 533 | } |
| 534 | uint8_t isOK = resp.arg[0] & 0xff; |
| 535 | if (!isOK) { |
| 536 | PrintAndLog("Authentication error"); |
| 537 | return false; |
| 538 | } |
| 539 | return true; |
| 540 | } |
| 541 | |
| 542 | int usage_hf_iclass_dump(void) { |
| 543 | PrintAndLog("Usage: hf iclass dump f <fileName> k <Key> c <CreditKey> e|r\n"); |
| 544 | PrintAndLog("Options:"); |
| 545 | PrintAndLog(" f <filename> : specify a filename to save dump to"); |
| 546 | PrintAndLog(" k <Key> : *Access Key as 16 hex symbols or 1 hex to select key from memory"); |
| 547 | PrintAndLog(" c <CreditKey>: Credit Key as 16 hex symbols or 1 hex to select key from memory"); |
| 548 | PrintAndLog(" e : If 'e' is specified, the key is interpreted as the 16 byte"); |
| 549 | PrintAndLog(" Custom Key (KCus), which can be obtained via reader-attack"); |
| 550 | PrintAndLog(" See 'hf iclass sim 2'. This key should be on iclass-format"); |
| 551 | PrintAndLog(" r : If 'r' is specified, the key is interpreted as raw block 3/4"); |
| 552 | PrintAndLog(" NOTE: * = required"); |
| 553 | PrintAndLog("Samples:"); |
| 554 | PrintAndLog(" hf iclass dump k 001122334455667B"); |
| 555 | PrintAndLog(" hf iclass dump k AAAAAAAAAAAAAAAA c 001122334455667B"); |
| 556 | PrintAndLog(" hf iclass dump k AAAAAAAAAAAAAAAA e"); |
| 557 | return 0; |
| 558 | } |
| 559 | |
| 560 | int CmdHFiClassReader_Dump(const char *Cmd) { |
| 561 | |
| 562 | uint8_t MAC[4] = {0x00,0x00,0x00,0x00}; |
| 563 | uint8_t div_key[8] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; |
| 564 | uint8_t c_div_key[8] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; |
| 565 | uint8_t blockno = 0; |
| 566 | uint8_t numblks = 0; |
| 567 | uint8_t maxBlk = 31; |
| 568 | uint8_t app_areas = 1; |
| 569 | uint8_t kb = 2; |
| 570 | uint8_t KEY[8] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; |
| 571 | uint8_t CreditKEY[8] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; |
| 572 | uint8_t keyNbr = 0; |
| 573 | uint8_t dataLen = 0; |
| 574 | uint8_t fileNameLen = 0; |
| 575 | char filename[FILE_PATH_SIZE]={0}; |
| 576 | char tempStr[50] = {0}; |
| 577 | bool have_debit_key = false; |
| 578 | bool have_credit_key = false; |
| 579 | bool use_credit_key = false; |
| 580 | bool elite = false; |
| 581 | bool rawkey = false; |
| 582 | bool errors = false; |
| 583 | uint8_t cmdp = 0; |
| 584 | |
| 585 | while(param_getchar(Cmd, cmdp) != 0x00) |
| 586 | { |
| 587 | switch(param_getchar(Cmd, cmdp)) |
| 588 | { |
| 589 | case 'h': |
| 590 | case 'H': |
| 591 | return usage_hf_iclass_dump(); |
| 592 | case 'c': |
| 593 | case 'C': |
| 594 | have_credit_key = true; |
| 595 | dataLen = param_getstr(Cmd, cmdp+1, tempStr); |
| 596 | if (dataLen == 16) { |
| 597 | errors = param_gethex(tempStr, 0, CreditKEY, dataLen); |
| 598 | } else if (dataLen == 1) { |
| 599 | keyNbr = param_get8(Cmd, cmdp+1); |
| 600 | if (keyNbr < ICLASS_KEYS_MAX) { |
| 601 | memcpy(CreditKEY, iClass_Key_Table[keyNbr], 8); |
| 602 | } else { |
| 603 | PrintAndLog("\nERROR: Credit KeyNbr is invalid\n"); |
| 604 | errors = true; |
| 605 | } |
| 606 | } else { |
| 607 | PrintAndLog("\nERROR: Credit Key is incorrect length\n"); |
| 608 | errors = true; |
| 609 | } |
| 610 | cmdp += 2; |
| 611 | break; |
| 612 | case 'e': |
| 613 | case 'E': |
| 614 | elite = true; |
| 615 | cmdp++; |
| 616 | break; |
| 617 | case 'f': |
| 618 | case 'F': |
| 619 | fileNameLen = param_getstr(Cmd, cmdp+1, filename); |
| 620 | if (fileNameLen < 1) { |
| 621 | PrintAndLog("No filename found after f"); |
| 622 | errors = true; |
| 623 | } |
| 624 | cmdp += 2; |
| 625 | break; |
| 626 | case 'k': |
| 627 | case 'K': |
| 628 | have_debit_key = true; |
| 629 | dataLen = param_getstr(Cmd, cmdp+1, tempStr); |
| 630 | if (dataLen == 16) { |
| 631 | errors = param_gethex(tempStr, 0, KEY, dataLen); |
| 632 | } else if (dataLen == 1) { |
| 633 | keyNbr = param_get8(Cmd, cmdp+1); |
| 634 | if (keyNbr < ICLASS_KEYS_MAX) { |
| 635 | memcpy(KEY, iClass_Key_Table[keyNbr], 8); |
| 636 | } else { |
| 637 | PrintAndLog("\nERROR: Credit KeyNbr is invalid\n"); |
| 638 | errors = true; |
| 639 | } |
| 640 | } else { |
| 641 | PrintAndLog("\nERROR: Credit Key is incorrect length\n"); |
| 642 | errors = true; |
| 643 | } |
| 644 | cmdp += 2; |
| 645 | break; |
| 646 | case 'r': |
| 647 | case 'R': |
| 648 | rawkey = true; |
| 649 | cmdp++; |
| 650 | break; |
| 651 | default: |
| 652 | PrintAndLog("Unknown parameter '%c'\n", param_getchar(Cmd, cmdp)); |
| 653 | errors = true; |
| 654 | break; |
| 655 | } |
| 656 | if(errors) return usage_hf_iclass_dump(); |
| 657 | } |
| 658 | |
| 659 | if (cmdp < 2) return usage_hf_iclass_dump(); |
| 660 | // if no debit key given try credit key on AA1 (not for iclass but for some picopass this will work) |
| 661 | if (!have_debit_key && have_credit_key) use_credit_key = true; |
| 662 | |
| 663 | //get config and first 3 blocks |
| 664 | UsbCommand c = {CMD_READER_ICLASS, {FLAG_ICLASS_READER_CSN | |
| 665 | FLAG_ICLASS_READER_CONF | FLAG_ICLASS_READER_ONLY_ONCE | FLAG_ICLASS_READER_ONE_TRY}}; |
| 666 | UsbCommand resp; |
| 667 | uint8_t tag_data[255*8]; |
| 668 | |
| 669 | clearCommandBuffer(); |
| 670 | SendCommand(&c); |
| 671 | if (!WaitForResponseTimeout(CMD_ACK, &resp, 4500)) { |
| 672 | PrintAndLog("Command execute timeout"); |
| 673 | ul_switch_off_field(); |
| 674 | return 0; |
| 675 | } |
| 676 | uint8_t readStatus = resp.arg[0] & 0xff; |
| 677 | uint8_t *data = resp.d.asBytes; |
| 678 | |
| 679 | if(readStatus == 0){ |
| 680 | PrintAndLog("No tag found..."); |
| 681 | ul_switch_off_field(); |
| 682 | return 0; |
| 683 | } |
| 684 | if( readStatus & (FLAG_ICLASS_READER_CSN|FLAG_ICLASS_READER_CONF|FLAG_ICLASS_READER_CC)){ |
| 685 | memcpy(tag_data, data, 8*3); |
| 686 | blockno+=2; // 2 to force re-read of block 2 later. (seems to respond differently..) |
| 687 | numblks = data[8]; |
| 688 | getMemConfig(data[13], data[12], &maxBlk, &app_areas, &kb); |
| 689 | // large memory - not able to dump pages currently |
| 690 | if (numblks > maxBlk) numblks = maxBlk; |
| 691 | } |
| 692 | ul_switch_off_field(); |
| 693 | // authenticate debit key and get div_key - later store in dump block 3 |
| 694 | if (!select_and_auth(KEY, MAC, div_key, use_credit_key, elite, rawkey, false)){ |
| 695 | //try twice - for some reason it sometimes fails the first time... |
| 696 | if (!select_and_auth(KEY, MAC, div_key, use_credit_key, elite, rawkey, false)){ |
| 697 | ul_switch_off_field(); |
| 698 | return 0; |
| 699 | } |
| 700 | } |
| 701 | |
| 702 | // begin dump |
| 703 | UsbCommand w = {CMD_ICLASS_DUMP, {blockno, numblks-blockno+1}}; |
| 704 | clearCommandBuffer(); |
| 705 | SendCommand(&w); |
| 706 | if (!WaitForResponseTimeout(CMD_ACK, &resp, 4500)) { |
| 707 | PrintAndLog("Command execute time-out 1"); |
| 708 | ul_switch_off_field(); |
| 709 | return 1; |
| 710 | } |
| 711 | uint32_t blocksRead = resp.arg[1]; |
| 712 | uint8_t isOK = resp.arg[0] & 0xff; |
| 713 | if (!isOK && !blocksRead) { |
| 714 | PrintAndLog("Read Block Failed"); |
| 715 | ul_switch_off_field(); |
| 716 | return 0; |
| 717 | } |
| 718 | uint32_t startindex = resp.arg[2]; |
| 719 | if (blocksRead*8 > sizeof(tag_data)-(blockno*8)) { |
| 720 | PrintAndLog("Data exceeded Buffer size!"); |
| 721 | blocksRead = (sizeof(tag_data)/8) - blockno; |
| 722 | } |
| 723 | // response ok - now get bigbuf content of the dump |
| 724 | GetFromBigBuf(tag_data+(blockno*8), blocksRead*8, startindex); |
| 725 | WaitForResponse(CMD_ACK,NULL); |
| 726 | size_t gotBytes = blocksRead*8 + blockno*8; |
| 727 | |
| 728 | // try AA2 |
| 729 | if (have_credit_key) { |
| 730 | //turn off hf field before authenticating with different key |
| 731 | ul_switch_off_field(); |
| 732 | memset(MAC,0,4); |
| 733 | // AA2 authenticate credit key and git c_div_key - later store in dump block 4 |
| 734 | if (!select_and_auth(CreditKEY, MAC, c_div_key, true, false, false, false)){ |
| 735 | //try twice - for some reason it sometimes fails the first time... |
| 736 | if (!select_and_auth(CreditKEY, MAC, c_div_key, true, false, false, false)){ |
| 737 | ul_switch_off_field(); |
| 738 | return 0; |
| 739 | } |
| 740 | } |
| 741 | // do we still need to read more block? (aa2 enabled?) |
| 742 | if (maxBlk > blockno+numblks+1) { |
| 743 | // setup dump and start |
| 744 | w.arg[0] = blockno + blocksRead; |
| 745 | w.arg[1] = maxBlk - (blockno + blocksRead); |
| 746 | clearCommandBuffer(); |
| 747 | SendCommand(&w); |
| 748 | if (!WaitForResponseTimeout(CMD_ACK, &resp, 4500)) { |
| 749 | PrintAndLog("Command execute timeout 2"); |
| 750 | ul_switch_off_field(); |
| 751 | return 0; |
| 752 | } |
| 753 | uint8_t isOK = resp.arg[0] & 0xff; |
| 754 | blocksRead = resp.arg[1]; |
| 755 | if (!isOK && !blocksRead) { |
| 756 | PrintAndLog("Read Block Failed 2"); |
| 757 | ul_switch_off_field(); |
| 758 | return 0; |
| 759 | } |
| 760 | |
| 761 | startindex = resp.arg[2]; |
| 762 | if (blocksRead*8 > sizeof(tag_data)-gotBytes) { |
| 763 | PrintAndLog("Data exceeded Buffer size!"); |
| 764 | blocksRead = (sizeof(tag_data) - gotBytes)/8; |
| 765 | } |
| 766 | // get dumped data from bigbuf |
| 767 | GetFromBigBuf(tag_data+gotBytes, blocksRead*8, startindex); |
| 768 | WaitForResponse(CMD_ACK,NULL); |
| 769 | |
| 770 | gotBytes += blocksRead*8; |
| 771 | } else { //field is still on - turn it off... |
| 772 | ul_switch_off_field(); |
| 773 | } |
| 774 | } |
| 775 | |
| 776 | // add diversified keys to dump |
| 777 | if (have_debit_key) memcpy(tag_data+(3*8),div_key,8); |
| 778 | if (have_credit_key) memcpy(tag_data+(4*8),c_div_key,8); |
| 779 | // print the dump |
| 780 | printf("------+--+-------------------------+\n"); |
| 781 | printf("CSN |00| %s|\n",sprint_hex(tag_data, 8)); |
| 782 | printIclassDumpContents(tag_data, 1, (gotBytes/8), gotBytes); |
| 783 | |
| 784 | if (filename[0] == 0){ |
| 785 | snprintf(filename, FILE_PATH_SIZE,"iclass_tagdump-%02x%02x%02x%02x%02x%02x%02x%02x", |
| 786 | tag_data[0],tag_data[1],tag_data[2],tag_data[3], |
| 787 | tag_data[4],tag_data[5],tag_data[6],tag_data[7]); |
| 788 | } |
| 789 | |
| 790 | // save the dump to .bin file |
| 791 | PrintAndLog("Saving dump file - %d blocks read", gotBytes/8); |
| 792 | saveFile(filename, "bin", tag_data, gotBytes); |
| 793 | return 1; |
| 794 | } |
| 795 | |
| 796 | static int WriteBlock(uint8_t blockno, uint8_t *bldata, uint8_t *KEY, bool use_credit_key, bool elite, bool rawkey, bool verbose) { |
| 797 | uint8_t MAC[4]={0x00,0x00,0x00,0x00}; |
| 798 | uint8_t div_key[8]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; |
| 799 | if (!select_and_auth(KEY, MAC, div_key, use_credit_key, elite, rawkey, verbose)) |
| 800 | return 0; |
| 801 | |
| 802 | UsbCommand resp; |
| 803 | |
| 804 | Calc_wb_mac(blockno,bldata,div_key,MAC); |
| 805 | UsbCommand w = {CMD_ICLASS_WRITEBLOCK, {blockno}}; |
| 806 | memcpy(w.d.asBytes, bldata, 8); |
| 807 | memcpy(w.d.asBytes + 8, MAC, 4); |
| 808 | |
| 809 | clearCommandBuffer(); |
| 810 | SendCommand(&w); |
| 811 | if (!WaitForResponseTimeout(CMD_ACK,&resp,4500)) |
| 812 | { |
| 813 | PrintAndLog("Write Command execute timeout"); |
| 814 | return 0; |
| 815 | } |
| 816 | uint8_t isOK = resp.arg[0] & 0xff; |
| 817 | if (!isOK) { |
| 818 | PrintAndLog("Write Block Failed"); |
| 819 | return 0; |
| 820 | } |
| 821 | PrintAndLog("Write Block Successful"); |
| 822 | return 1; |
| 823 | } |
| 824 | |
| 825 | int usage_hf_iclass_writeblock(void) { |
| 826 | PrintAndLog("Options:"); |
| 827 | PrintAndLog(" b <Block> : The block number as 2 hex symbols"); |
| 828 | PrintAndLog(" d <data> : Set the Data to write as 16 hex symbols"); |
| 829 | PrintAndLog(" k <Key> : Access Key as 16 hex symbols or 1 hex to select key from memory"); |
| 830 | PrintAndLog(" c : If 'c' is specified, the key set is assumed to be the credit key\n"); |
| 831 | PrintAndLog(" e : If 'e' is specified, elite computations applied to key"); |
| 832 | PrintAndLog(" r : If 'r' is specified, no computations applied to key"); |
| 833 | PrintAndLog("Samples:"); |
| 834 | PrintAndLog(" hf iclass writeblk b 0A d AAAAAAAAAAAAAAAA k 001122334455667B"); |
| 835 | PrintAndLog(" hf iclass writeblk b 1B d AAAAAAAAAAAAAAAA k 001122334455667B c"); |
| 836 | PrintAndLog(" hf iclass writeblk b 0A d AAAAAAAAAAAAAAAA n 0"); |
| 837 | return 0; |
| 838 | } |
| 839 | |
| 840 | int CmdHFiClass_WriteBlock(const char *Cmd) { |
| 841 | uint8_t blockno=0; |
| 842 | uint8_t bldata[8]={0,0,0,0,0,0,0,0}; |
| 843 | uint8_t KEY[8]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; |
| 844 | uint8_t keyNbr = 0; |
| 845 | uint8_t dataLen = 0; |
| 846 | char tempStr[50] = {0}; |
| 847 | bool use_credit_key = false; |
| 848 | bool elite = false; |
| 849 | bool rawkey= false; |
| 850 | bool errors = false; |
| 851 | uint8_t cmdp = 0; |
| 852 | while(param_getchar(Cmd, cmdp) != 0x00) |
| 853 | { |
| 854 | switch(param_getchar(Cmd, cmdp)) |
| 855 | { |
| 856 | case 'h': |
| 857 | case 'H': |
| 858 | return usage_hf_iclass_writeblock(); |
| 859 | case 'b': |
| 860 | case 'B': |
| 861 | if (param_gethex(Cmd, cmdp+1, &blockno, 2)) { |
| 862 | PrintAndLog("Block No must include 2 HEX symbols\n"); |
| 863 | errors = true; |
| 864 | } |
| 865 | cmdp += 2; |
| 866 | break; |
| 867 | case 'c': |
| 868 | case 'C': |
| 869 | use_credit_key = true; |
| 870 | cmdp++; |
| 871 | break; |
| 872 | case 'd': |
| 873 | case 'D': |
| 874 | if (param_gethex(Cmd, cmdp+1, bldata, 16)) |
| 875 | { |
| 876 | PrintAndLog("KEY must include 16 HEX symbols\n"); |
| 877 | errors = true; |
| 878 | } |
| 879 | cmdp += 2; |
| 880 | break; |
| 881 | case 'e': |
| 882 | case 'E': |
| 883 | elite = true; |
| 884 | cmdp++; |
| 885 | break; |
| 886 | case 'k': |
| 887 | case 'K': |
| 888 | dataLen = param_getstr(Cmd, cmdp+1, tempStr); |
| 889 | if (dataLen == 16) { |
| 890 | errors = param_gethex(tempStr, 0, KEY, dataLen); |
| 891 | } else if (dataLen == 1) { |
| 892 | keyNbr = param_get8(Cmd, cmdp+1); |
| 893 | if (keyNbr < ICLASS_KEYS_MAX) { |
| 894 | memcpy(KEY, iClass_Key_Table[keyNbr], 8); |
| 895 | } else { |
| 896 | PrintAndLog("\nERROR: Credit KeyNbr is invalid\n"); |
| 897 | errors = true; |
| 898 | } |
| 899 | } else { |
| 900 | PrintAndLog("\nERROR: Credit Key is incorrect length\n"); |
| 901 | errors = true; |
| 902 | } |
| 903 | cmdp += 2; |
| 904 | break; |
| 905 | case 'r': |
| 906 | case 'R': |
| 907 | rawkey = true; |
| 908 | cmdp++; |
| 909 | break; |
| 910 | default: |
| 911 | PrintAndLog("Unknown parameter '%c'\n", param_getchar(Cmd, cmdp)); |
| 912 | errors = true; |
| 913 | break; |
| 914 | } |
| 915 | if(errors) return usage_hf_iclass_writeblock(); |
| 916 | } |
| 917 | |
| 918 | if (cmdp < 6) return usage_hf_iclass_writeblock(); |
| 919 | int ans = WriteBlock(blockno, bldata, KEY, use_credit_key, elite, rawkey, true); |
| 920 | ul_switch_off_field(); |
| 921 | return ans; |
| 922 | } |
| 923 | |
| 924 | int usage_hf_iclass_clone(void) { |
| 925 | PrintAndLog("Usage: hf iclass clone f <tagfile.bin> b <first block> l <last block> k <KEY> c e|r"); |
| 926 | PrintAndLog("Options:"); |
| 927 | PrintAndLog(" f <filename>: specify a filename to clone from"); |
| 928 | PrintAndLog(" b <Block> : The first block to clone as 2 hex symbols"); |
| 929 | PrintAndLog(" l <Last Blk>: Set the Data to write as 16 hex symbols"); |
| 930 | PrintAndLog(" k <Key> : Access Key as 16 hex symbols or 1 hex to select key from memory"); |
| 931 | PrintAndLog(" c : If 'c' is specified, the key set is assumed to be the credit key\n"); |
| 932 | PrintAndLog(" e : If 'e' is specified, elite computations applied to key"); |
| 933 | PrintAndLog(" r : If 'r' is specified, no computations applied to key"); |
| 934 | PrintAndLog("Samples:"); |
| 935 | PrintAndLog(" hf iclass clone f iclass_tagdump-121345.bin b 06 l 1A k 1122334455667788 e"); |
| 936 | PrintAndLog(" hf iclass clone f iclass_tagdump-121345.bin b 05 l 19 k 0"); |
| 937 | PrintAndLog(" hf iclass clone f iclass_tagdump-121345.bin b 06 l 19 k 0 e"); |
| 938 | return -1; |
| 939 | } |
| 940 | |
| 941 | int CmdHFiClassCloneTag(const char *Cmd) { |
| 942 | char filename[FILE_PATH_SIZE] = {0}; |
| 943 | char tempStr[50]={0}; |
| 944 | uint8_t KEY[8]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; |
| 945 | uint8_t keyNbr = 0; |
| 946 | uint8_t fileNameLen = 0; |
| 947 | uint8_t startblock = 0; |
| 948 | uint8_t endblock = 0; |
| 949 | uint8_t dataLen = 0; |
| 950 | bool use_credit_key = false; |
| 951 | bool elite = false; |
| 952 | bool rawkey = false; |
| 953 | bool errors = false; |
| 954 | uint8_t cmdp = 0; |
| 955 | while(param_getchar(Cmd, cmdp) != 0x00) |
| 956 | { |
| 957 | switch(param_getchar(Cmd, cmdp)) |
| 958 | { |
| 959 | case 'h': |
| 960 | case 'H': |
| 961 | return usage_hf_iclass_clone(); |
| 962 | case 'b': |
| 963 | case 'B': |
| 964 | if (param_gethex(Cmd, cmdp+1, &startblock, 2)) { |
| 965 | PrintAndLog("Start Block No must include 2 HEX symbols\n"); |
| 966 | errors = true; |
| 967 | } |
| 968 | cmdp += 2; |
| 969 | break; |
| 970 | case 'c': |
| 971 | case 'C': |
| 972 | use_credit_key = true; |
| 973 | cmdp++; |
| 974 | break; |
| 975 | case 'e': |
| 976 | case 'E': |
| 977 | elite = true; |
| 978 | cmdp++; |
| 979 | break; |
| 980 | case 'f': |
| 981 | case 'F': |
| 982 | fileNameLen = param_getstr(Cmd, cmdp+1, filename); |
| 983 | if (fileNameLen < 1) { |
| 984 | PrintAndLog("No filename found after f"); |
| 985 | errors = true; |
| 986 | } |
| 987 | cmdp += 2; |
| 988 | break; |
| 989 | case 'k': |
| 990 | case 'K': |
| 991 | dataLen = param_getstr(Cmd, cmdp+1, tempStr); |
| 992 | if (dataLen == 16) { |
| 993 | errors = param_gethex(tempStr, 0, KEY, dataLen); |
| 994 | } else if (dataLen == 1) { |
| 995 | keyNbr = param_get8(Cmd, cmdp+1); |
| 996 | if (keyNbr < ICLASS_KEYS_MAX) { |
| 997 | memcpy(KEY, iClass_Key_Table[keyNbr], 8); |
| 998 | } else { |
| 999 | PrintAndLog("\nERROR: Credit KeyNbr is invalid\n"); |
| 1000 | errors = true; |
| 1001 | } |
| 1002 | } else { |
| 1003 | PrintAndLog("\nERROR: Credit Key is incorrect length\n"); |
| 1004 | errors = true; |
| 1005 | } |
| 1006 | cmdp += 2; |
| 1007 | break; |
| 1008 | case 'l': |
| 1009 | case 'L': |
| 1010 | if (param_gethex(Cmd, cmdp+1, &endblock, 2)) { |
| 1011 | PrintAndLog("Start Block No must include 2 HEX symbols\n"); |
| 1012 | errors = true; |
| 1013 | } |
| 1014 | cmdp += 2; |
| 1015 | break; |
| 1016 | case 'r': |
| 1017 | case 'R': |
| 1018 | rawkey = true; |
| 1019 | cmdp++; |
| 1020 | break; |
| 1021 | default: |
| 1022 | PrintAndLog("Unknown parameter '%c'\n", param_getchar(Cmd, cmdp)); |
| 1023 | errors = true; |
| 1024 | break; |
| 1025 | } |
| 1026 | if(errors) return usage_hf_iclass_clone(); |
| 1027 | } |
| 1028 | |
| 1029 | if (cmdp < 8) return usage_hf_iclass_clone(); |
| 1030 | |
| 1031 | FILE *f; |
| 1032 | |
| 1033 | iclass_block_t tag_data[USB_CMD_DATA_SIZE/12]; |
| 1034 | |
| 1035 | if ((endblock-startblock+1)*12 > USB_CMD_DATA_SIZE) { |
| 1036 | PrintAndLog("Trying to write too many blocks at once. Max: %d", USB_CMD_DATA_SIZE/8); |
| 1037 | } |
| 1038 | // file handling and reading |
| 1039 | f = fopen(filename,"rb"); |
| 1040 | if(!f) { |
| 1041 | PrintAndLog("Failed to read from file '%s'", filename); |
| 1042 | return 1; |
| 1043 | } |
| 1044 | |
| 1045 | if (startblock<5) { |
| 1046 | PrintAndLog("You cannot write key blocks this way. yet... make your start block > 4"); |
| 1047 | fclose(f); |
| 1048 | return 0; |
| 1049 | } |
| 1050 | // now read data from the file from block 6 --- 19 |
| 1051 | // ok we will use this struct [data 8 bytes][MAC 4 bytes] for each block calculate all mac number for each data |
| 1052 | // then copy to usbcommand->asbytes; the max is 32 - 6 = 24 block 12 bytes each block 288 bytes then we can only accept to clone 21 blocks at the time, |
| 1053 | // else we have to create a share memory |
| 1054 | int i; |
| 1055 | fseek(f,startblock*8,SEEK_SET); |
| 1056 | if ( fread(tag_data,sizeof(iclass_block_t),endblock - startblock + 1,f) == 0 ) { |
| 1057 | PrintAndLog("File reading error."); |
| 1058 | fclose(f); |
| 1059 | return 2; |
| 1060 | } |
| 1061 | |
| 1062 | uint8_t MAC[4]={0x00,0x00,0x00,0x00}; |
| 1063 | uint8_t div_key[8]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; |
| 1064 | |
| 1065 | if (!select_and_auth(KEY, MAC, div_key, use_credit_key, elite, rawkey, true)) |
| 1066 | return 0; |
| 1067 | |
| 1068 | UsbCommand w = {CMD_ICLASS_CLONE,{startblock,endblock}}; |
| 1069 | uint8_t *ptr; |
| 1070 | // calculate all mac for every the block we will write |
| 1071 | for (i = startblock; i <= endblock; i++){ |
| 1072 | Calc_wb_mac(i,tag_data[i - startblock].d,div_key,MAC); |
| 1073 | // usb command d start pointer = d + (i - 6) * 12 |
| 1074 | // memcpy(pointer,tag_data[i - 6],8) 8 bytes |
| 1075 | // memcpy(pointer + 8,mac,sizoof(mac) 4 bytes; |
| 1076 | // next one |
| 1077 | ptr = w.d.asBytes + (i - startblock) * 12; |
| 1078 | memcpy(ptr, &(tag_data[i - startblock].d[0]), 8); |
| 1079 | memcpy(ptr + 8,MAC, 4); |
| 1080 | } |
| 1081 | uint8_t p[12]; |
| 1082 | for (i = 0; i <= endblock - startblock;i++){ |
| 1083 | memcpy(p,w.d.asBytes + (i * 12),12); |
| 1084 | printf("Block |%02x|",i + startblock); |
| 1085 | printf(" %02x%02x%02x%02x%02x%02x%02x%02x |",p[0],p[1],p[2],p[3],p[4],p[5],p[6],p[7]); |
| 1086 | printf(" MAC |%02x%02x%02x%02x|\n",p[8],p[9],p[10],p[11]); |
| 1087 | } |
| 1088 | UsbCommand resp; |
| 1089 | SendCommand(&w); |
| 1090 | if (!WaitForResponseTimeout(CMD_ACK,&resp,4500)) |
| 1091 | { |
| 1092 | PrintAndLog("Command execute timeout"); |
| 1093 | return 0; |
| 1094 | } |
| 1095 | return 1; |
| 1096 | } |
| 1097 | |
| 1098 | static int ReadBlock(uint8_t *KEY, uint8_t blockno, uint8_t keyType, bool elite, bool rawkey, bool verbose) { |
| 1099 | uint8_t MAC[4]={0x00,0x00,0x00,0x00}; |
| 1100 | uint8_t div_key[8]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; |
| 1101 | |
| 1102 | if (!select_and_auth(KEY, MAC, div_key, (keyType==0x18), elite, rawkey, verbose)) |
| 1103 | return 0; |
| 1104 | |
| 1105 | UsbCommand resp; |
| 1106 | UsbCommand w = {CMD_ICLASS_READBLOCK, {blockno}}; |
| 1107 | clearCommandBuffer(); |
| 1108 | SendCommand(&w); |
| 1109 | if (!WaitForResponseTimeout(CMD_ACK,&resp,4500)) |
| 1110 | { |
| 1111 | PrintAndLog("Command execute timeout"); |
| 1112 | return 0; |
| 1113 | } |
| 1114 | uint8_t isOK = resp.arg[0] & 0xff; |
| 1115 | if (!isOK) { |
| 1116 | PrintAndLog("Read Block Failed"); |
| 1117 | return 0; |
| 1118 | } |
| 1119 | //data read is stored in: resp.d.asBytes[0-15] |
| 1120 | if (verbose) PrintAndLog("Block %02X: %s\n",blockno, sprint_hex(resp.d.asBytes,8)); |
| 1121 | return 1; |
| 1122 | } |
| 1123 | |
| 1124 | int usage_hf_iclass_readblock(void) { |
| 1125 | PrintAndLog("Usage: hf iclass readblk b <Block> k <Key> c e|r\n"); |
| 1126 | PrintAndLog("Options:"); |
| 1127 | PrintAndLog(" b <Block> : The block number as 2 hex symbols"); |
| 1128 | PrintAndLog(" k <Key> : Access Key as 16 hex symbols or 1 hex to select key from memory"); |
| 1129 | PrintAndLog(" c : If 'c' is specified, the key set is assumed to be the credit key\n"); |
| 1130 | PrintAndLog(" e : If 'e' is specified, elite computations applied to key"); |
| 1131 | PrintAndLog(" r : If 'r' is specified, no computations applied to key"); |
| 1132 | PrintAndLog("Samples:"); |
| 1133 | PrintAndLog(" hf iclass readblk b 06 k 0011223344556677"); |
| 1134 | PrintAndLog(" hf iclass readblk b 1B k 0011223344556677 c"); |
| 1135 | PrintAndLog(" hf iclass readblk b 0A k 0"); |
| 1136 | return 0; |
| 1137 | } |
| 1138 | |
| 1139 | int CmdHFiClass_ReadBlock(const char *Cmd) { |
| 1140 | uint8_t blockno=0; |
| 1141 | uint8_t keyType = 0x88; //debit key |
| 1142 | uint8_t KEY[8]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; |
| 1143 | uint8_t keyNbr = 0; |
| 1144 | uint8_t dataLen = 0; |
| 1145 | char tempStr[50] = {0}; |
| 1146 | bool elite = false; |
| 1147 | bool rawkey = false; |
| 1148 | bool errors = false; |
| 1149 | uint8_t cmdp = 0; |
| 1150 | while(param_getchar(Cmd, cmdp) != 0x00) |
| 1151 | { |
| 1152 | switch(param_getchar(Cmd, cmdp)) |
| 1153 | { |
| 1154 | case 'h': |
| 1155 | case 'H': |
| 1156 | return usage_hf_iclass_readblock(); |
| 1157 | case 'b': |
| 1158 | case 'B': |
| 1159 | if (param_gethex(Cmd, cmdp+1, &blockno, 2)) { |
| 1160 | PrintAndLog("Block No must include 2 HEX symbols\n"); |
| 1161 | errors = true; |
| 1162 | } |
| 1163 | cmdp += 2; |
| 1164 | break; |
| 1165 | case 'c': |
| 1166 | case 'C': |
| 1167 | keyType = 0x18; |
| 1168 | cmdp++; |
| 1169 | break; |
| 1170 | case 'e': |
| 1171 | case 'E': |
| 1172 | elite = true; |
| 1173 | cmdp++; |
| 1174 | break; |
| 1175 | case 'k': |
| 1176 | case 'K': |
| 1177 | dataLen = param_getstr(Cmd, cmdp+1, tempStr); |
| 1178 | if (dataLen == 16) { |
| 1179 | errors = param_gethex(tempStr, 0, KEY, dataLen); |
| 1180 | } else if (dataLen == 1) { |
| 1181 | keyNbr = param_get8(Cmd, cmdp+1); |
| 1182 | if (keyNbr < ICLASS_KEYS_MAX) { |
| 1183 | memcpy(KEY, iClass_Key_Table[keyNbr], 8); |
| 1184 | } else { |
| 1185 | PrintAndLog("\nERROR: Credit KeyNbr is invalid\n"); |
| 1186 | errors = true; |
| 1187 | } |
| 1188 | } else { |
| 1189 | PrintAndLog("\nERROR: Credit Key is incorrect length\n"); |
| 1190 | errors = true; |
| 1191 | } |
| 1192 | cmdp += 2; |
| 1193 | break; |
| 1194 | case 'r': |
| 1195 | case 'R': |
| 1196 | rawkey = true; |
| 1197 | cmdp++; |
| 1198 | break; |
| 1199 | default: |
| 1200 | PrintAndLog("Unknown parameter '%c'\n", param_getchar(Cmd, cmdp)); |
| 1201 | errors = true; |
| 1202 | break; |
| 1203 | } |
| 1204 | if(errors) return usage_hf_iclass_readblock(); |
| 1205 | } |
| 1206 | |
| 1207 | if (cmdp < 4) return usage_hf_iclass_readblock(); |
| 1208 | |
| 1209 | return ReadBlock(KEY, blockno, keyType, elite, rawkey, true); |
| 1210 | } |
| 1211 | |
| 1212 | int CmdHFiClass_loclass(const char *Cmd) { |
| 1213 | char opt = param_getchar(Cmd, 0); |
| 1214 | |
| 1215 | if (strlen(Cmd)<1 || opt == 'h') { |
| 1216 | PrintAndLog("Usage: hf iclass loclass [options]"); |
| 1217 | PrintAndLog("Options:"); |
| 1218 | PrintAndLog("h Show this help"); |
| 1219 | PrintAndLog("t Perform self-test"); |
| 1220 | PrintAndLog("f <filename> Bruteforce iclass dumpfile"); |
| 1221 | PrintAndLog(" An iclass dumpfile is assumed to consist of an arbitrary number of"); |
| 1222 | PrintAndLog(" malicious CSNs, and their protocol responses"); |
| 1223 | PrintAndLog(" The binary format of the file is expected to be as follows: "); |
| 1224 | PrintAndLog(" <8 byte CSN><8 byte CC><4 byte NR><4 byte MAC>"); |
| 1225 | PrintAndLog(" <8 byte CSN><8 byte CC><4 byte NR><4 byte MAC>"); |
| 1226 | PrintAndLog(" <8 byte CSN><8 byte CC><4 byte NR><4 byte MAC>"); |
| 1227 | PrintAndLog(" ... totalling N*24 bytes"); |
| 1228 | return 0; |
| 1229 | } |
| 1230 | char fileName[255] = {0}; |
| 1231 | if(opt == 'f') |
| 1232 | { |
| 1233 | if(param_getstr(Cmd, 1, fileName) > 0) |
| 1234 | { |
| 1235 | return bruteforceFileNoKeys(fileName); |
| 1236 | }else |
| 1237 | { |
| 1238 | PrintAndLog("You must specify a filename"); |
| 1239 | } |
| 1240 | } |
| 1241 | else if(opt == 't') |
| 1242 | { |
| 1243 | int errors = testCipherUtils(); |
| 1244 | errors += testMAC(); |
| 1245 | errors += doKeyTests(0); |
| 1246 | errors += testElite(); |
| 1247 | if(errors) |
| 1248 | { |
| 1249 | prnlog("OBS! There were errors!!!"); |
| 1250 | } |
| 1251 | return errors; |
| 1252 | } |
| 1253 | |
| 1254 | return 0; |
| 1255 | } |
| 1256 | |
| 1257 | void printIclassDumpContents(uint8_t *iclass_dump, uint8_t startblock, uint8_t endblock, size_t filesize) { |
| 1258 | uint8_t mem_config; |
| 1259 | memcpy(&mem_config, iclass_dump + 13,1); |
| 1260 | uint8_t maxmemcount; |
| 1261 | uint8_t filemaxblock = filesize / 8; |
| 1262 | if (mem_config & 0x80) |
| 1263 | maxmemcount = 255; |
| 1264 | else |
| 1265 | maxmemcount = 31; |
| 1266 | //PrintAndLog ("endblock: %d, filesize: %d, maxmemcount: %d, filemaxblock: %d", endblock,filesize, maxmemcount, filemaxblock); |
| 1267 | |
| 1268 | if (startblock == 0) |
| 1269 | startblock = 6; |
| 1270 | if ((endblock > maxmemcount) || (endblock == 0)) |
| 1271 | endblock = maxmemcount; |
| 1272 | |
| 1273 | // remember endblock need to relate to zero-index arrays. |
| 1274 | if (endblock > filemaxblock-1) |
| 1275 | endblock = filemaxblock; |
| 1276 | |
| 1277 | int i = startblock; |
| 1278 | printf("------+--+-------------------------+\n"); |
| 1279 | while (i <= endblock) { |
| 1280 | uint8_t *blk = iclass_dump + (i * 8); |
| 1281 | printf("Block |%02X| %s|\n", i, sprint_hex(blk, 8) ); |
| 1282 | i++; |
| 1283 | } |
| 1284 | printf("------+--+-------------------------+\n"); |
| 1285 | } |
| 1286 | |
| 1287 | int usage_hf_iclass_readtagfile() { |
| 1288 | PrintAndLog("Usage: hf iclass readtagfile <filename> [startblock] [endblock]"); |
| 1289 | return 1; |
| 1290 | } |
| 1291 | |
| 1292 | int CmdHFiClassReadTagFile(const char *Cmd) { |
| 1293 | int startblock = 0; |
| 1294 | int endblock = 0; |
| 1295 | char tempnum[5]; |
| 1296 | FILE *f; |
| 1297 | char filename[FILE_PATH_SIZE]; |
| 1298 | if (param_getstr(Cmd, 0, filename) < 1) |
| 1299 | return usage_hf_iclass_readtagfile(); |
| 1300 | if (param_getstr(Cmd,1,(char *)&tempnum) < 1) |
| 1301 | startblock = 0; |
| 1302 | else |
| 1303 | sscanf(tempnum,"%d",&startblock); |
| 1304 | |
| 1305 | if (param_getstr(Cmd,2,(char *)&tempnum) < 1) |
| 1306 | endblock = 0; |
| 1307 | else |
| 1308 | sscanf(tempnum,"%d",&endblock); |
| 1309 | // file handling and reading |
| 1310 | f = fopen(filename,"rb"); |
| 1311 | if(!f) { |
| 1312 | PrintAndLog("Failed to read from file '%s'", filename); |
| 1313 | return 1; |
| 1314 | } |
| 1315 | fseek(f, 0, SEEK_END); |
| 1316 | long fsize = ftell(f); |
| 1317 | fseek(f, 0, SEEK_SET); |
| 1318 | |
| 1319 | if ( fsize < 0 ) { |
| 1320 | PrintAndLog("Error, when getting filesize"); |
| 1321 | fclose(f); |
| 1322 | return 1; |
| 1323 | } |
| 1324 | |
| 1325 | uint8_t *dump = malloc(fsize); |
| 1326 | |
| 1327 | size_t bytes_read = fread(dump, 1, fsize, f); |
| 1328 | fclose(f); |
| 1329 | uint8_t *csn = dump; |
| 1330 | printf("------+--+-------------------------+\n"); |
| 1331 | printf("CSN |00| %s|\n", sprint_hex(csn, 8) ); |
| 1332 | // printIclassDumpInfo(dump); |
| 1333 | printIclassDumpContents(dump,startblock,endblock,bytes_read); |
| 1334 | free(dump); |
| 1335 | return 0; |
| 1336 | } |
| 1337 | |
| 1338 | /* |
| 1339 | uint64_t xorcheck(uint64_t sdiv,uint64_t hdiv) { |
| 1340 | uint64_t new_div = 0x00; |
| 1341 | new_div ^= sdiv; |
| 1342 | new_div ^= hdiv; |
| 1343 | return new_div; |
| 1344 | } |
| 1345 | |
| 1346 | uint64_t hexarray_to_uint64(uint8_t *key) { |
| 1347 | char temp[17]; |
| 1348 | uint64_t uint_key; |
| 1349 | for (int i = 0;i < 8;i++) |
| 1350 | sprintf(&temp[(i *2)],"%02X",key[i]); |
| 1351 | temp[16] = '\0'; |
| 1352 | if (sscanf(temp,"%016" SCNx64,&uint_key) < 1) |
| 1353 | return 0; |
| 1354 | return uint_key; |
| 1355 | } |
| 1356 | */ |
| 1357 | void HFiClassCalcDivKey(uint8_t *CSN, uint8_t *KEY, uint8_t *div_key, bool elite){ |
| 1358 | uint8_t keytable[128] = {0}; |
| 1359 | uint8_t key_index[8] = {0}; |
| 1360 | if (elite) { |
| 1361 | uint8_t key_sel[8] = { 0 }; |
| 1362 | uint8_t key_sel_p[8] = { 0 }; |
| 1363 | hash2(KEY, keytable); |
| 1364 | hash1(CSN, key_index); |
| 1365 | for(uint8_t i = 0; i < 8 ; i++) |
| 1366 | key_sel[i] = keytable[key_index[i]] & 0xFF; |
| 1367 | |
| 1368 | //Permute from iclass format to standard format |
| 1369 | permutekey_rev(key_sel, key_sel_p); |
| 1370 | diversifyKey(CSN, key_sel_p, div_key); |
| 1371 | } else { |
| 1372 | diversifyKey(CSN, KEY, div_key); |
| 1373 | } |
| 1374 | } |
| 1375 | |
| 1376 | //when told CSN, oldkey, newkey, if new key is elite (elite), and if old key was elite (oldElite) |
| 1377 | //calculate and return xor_div_key (ready for a key write command) |
| 1378 | //print all div_keys if verbose |
| 1379 | static void HFiClassCalcNewKey(uint8_t *CSN, uint8_t *OLDKEY, uint8_t *NEWKEY, uint8_t *xor_div_key, bool elite, bool oldElite, bool verbose){ |
| 1380 | uint8_t old_div_key[8] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; |
| 1381 | uint8_t new_div_key[8] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; |
| 1382 | //get old div key |
| 1383 | HFiClassCalcDivKey(CSN, OLDKEY, old_div_key, oldElite); |
| 1384 | //get new div key |
| 1385 | HFiClassCalcDivKey(CSN, NEWKEY, new_div_key, elite); |
| 1386 | |
| 1387 | for (uint8_t i = 0; i < sizeof(old_div_key); i++){ |
| 1388 | xor_div_key[i] = old_div_key[i] ^ new_div_key[i]; |
| 1389 | } |
| 1390 | if (verbose) { |
| 1391 | printf("Old Div Key : %s\n",sprint_hex(old_div_key,8)); |
| 1392 | printf("New Div Key : %s\n",sprint_hex(new_div_key,8)); |
| 1393 | printf("Xor Div Key : %s\n",sprint_hex(xor_div_key,8)); |
| 1394 | } |
| 1395 | } |
| 1396 | |
| 1397 | int usage_hf_iclass_calc_newkey(void) { |
| 1398 | PrintAndLog("HELP : Manage iClass Keys in client memory:\n"); |
| 1399 | PrintAndLog("Usage: hf iclass calc_newkey o <Old key> n <New key> s [csn] e"); |
| 1400 | PrintAndLog(" Options:"); |
| 1401 | PrintAndLog(" o <oldkey> : *specify a key as 16 hex symbols or a key number as 1 symbol"); |
| 1402 | PrintAndLog(" n <newkey> : *specify a key as 16 hex symbols or a key number as 1 symbol"); |
| 1403 | PrintAndLog(" s <csn> : specify a card Serial number to diversify the key (if omitted will attempt to read a csn)"); |
| 1404 | PrintAndLog(" e : specify new key as elite calc"); |
| 1405 | PrintAndLog(" ee : specify old and new key as elite calc"); |
| 1406 | PrintAndLog("Samples:"); |
| 1407 | PrintAndLog(" e key to e key given csn : hf iclass calcnewkey o 1122334455667788 n 2233445566778899 s deadbeafdeadbeaf ee"); |
| 1408 | PrintAndLog(" std key to e key read csn: hf iclass calcnewkey o 1122334455667788 n 2233445566778899 e"); |
| 1409 | PrintAndLog(" std to std read csn : hf iclass calcnewkey o 1122334455667788 n 2233445566778899"); |
| 1410 | PrintAndLog("NOTE: * = required\n"); |
| 1411 | |
| 1412 | return 1; |
| 1413 | } |
| 1414 | |
| 1415 | int CmdHFiClassCalcNewKey(const char *Cmd) { |
| 1416 | uint8_t OLDKEY[8] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; |
| 1417 | uint8_t NEWKEY[8] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; |
| 1418 | uint8_t xor_div_key[8] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; |
| 1419 | uint8_t CSN[8] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; |
| 1420 | uint8_t CCNR[12] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; |
| 1421 | uint8_t keyNbr = 0; |
| 1422 | uint8_t dataLen = 0; |
| 1423 | char tempStr[50] = {0}; |
| 1424 | bool givenCSN = false; |
| 1425 | bool oldElite = false; |
| 1426 | bool elite = false; |
| 1427 | bool errors = false; |
| 1428 | uint8_t cmdp = 0; |
| 1429 | while(param_getchar(Cmd, cmdp) != 0x00) |
| 1430 | { |
| 1431 | switch(param_getchar(Cmd, cmdp)) |
| 1432 | { |
| 1433 | case 'h': |
| 1434 | case 'H': |
| 1435 | return usage_hf_iclass_calc_newkey(); |
| 1436 | case 'e': |
| 1437 | case 'E': |
| 1438 | dataLen = param_getstr(Cmd, cmdp, tempStr); |
| 1439 | if (dataLen==2) |
| 1440 | oldElite = true; |
| 1441 | elite = true; |
| 1442 | cmdp++; |
| 1443 | break; |
| 1444 | case 'n': |
| 1445 | case 'N': |
| 1446 | dataLen = param_getstr(Cmd, cmdp+1, tempStr); |
| 1447 | if (dataLen == 16) { |
| 1448 | errors = param_gethex(tempStr, 0, NEWKEY, dataLen); |
| 1449 | } else if (dataLen == 1) { |
| 1450 | keyNbr = param_get8(Cmd, cmdp+1); |
| 1451 | if (keyNbr < ICLASS_KEYS_MAX) { |
| 1452 | memcpy(NEWKEY, iClass_Key_Table[keyNbr], 8); |
| 1453 | } else { |
| 1454 | PrintAndLog("\nERROR: NewKey Nbr is invalid\n"); |
| 1455 | errors = true; |
| 1456 | } |
| 1457 | } else { |
| 1458 | PrintAndLog("\nERROR: NewKey is incorrect length\n"); |
| 1459 | errors = true; |
| 1460 | } |
| 1461 | cmdp += 2; |
| 1462 | break; |
| 1463 | case 'o': |
| 1464 | case 'O': |
| 1465 | dataLen = param_getstr(Cmd, cmdp+1, tempStr); |
| 1466 | if (dataLen == 16) { |
| 1467 | errors = param_gethex(tempStr, 0, OLDKEY, dataLen); |
| 1468 | } else if (dataLen == 1) { |
| 1469 | keyNbr = param_get8(Cmd, cmdp+1); |
| 1470 | if (keyNbr < ICLASS_KEYS_MAX) { |
| 1471 | memcpy(OLDKEY, iClass_Key_Table[keyNbr], 8); |
| 1472 | } else { |
| 1473 | PrintAndLog("\nERROR: Credit KeyNbr is invalid\n"); |
| 1474 | errors = true; |
| 1475 | } |
| 1476 | } else { |
| 1477 | PrintAndLog("\nERROR: Credit Key is incorrect length\n"); |
| 1478 | errors = true; |
| 1479 | } |
| 1480 | cmdp += 2; |
| 1481 | break; |
| 1482 | case 's': |
| 1483 | case 'S': |
| 1484 | givenCSN = true; |
| 1485 | if (param_gethex(Cmd, cmdp+1, CSN, 16)) |
| 1486 | return usage_hf_iclass_calc_newkey(); |
| 1487 | cmdp += 2; |
| 1488 | break; |
| 1489 | default: |
| 1490 | PrintAndLog("Unknown parameter '%c'\n", param_getchar(Cmd, cmdp)); |
| 1491 | errors = true; |
| 1492 | break; |
| 1493 | } |
| 1494 | if(errors) return usage_hf_iclass_calc_newkey(); |
| 1495 | } |
| 1496 | |
| 1497 | if (cmdp < 4) return usage_hf_iclass_calc_newkey(); |
| 1498 | |
| 1499 | if (!givenCSN) |
| 1500 | if (!select_only(CSN, CCNR, false, true)) |
| 1501 | return 0; |
| 1502 | |
| 1503 | HFiClassCalcNewKey(CSN, OLDKEY, NEWKEY, xor_div_key, elite, oldElite, true); |
| 1504 | return 0; |
| 1505 | } |
| 1506 | |
| 1507 | static int loadKeys(char *filename) { |
| 1508 | FILE *f; |
| 1509 | f = fopen(filename,"rb"); |
| 1510 | if(!f) { |
| 1511 | PrintAndLog("Failed to read from file '%s'", filename); |
| 1512 | return 0; |
| 1513 | } |
| 1514 | fseek(f, 0, SEEK_END); |
| 1515 | long fsize = ftell(f); |
| 1516 | fseek(f, 0, SEEK_SET); |
| 1517 | |
| 1518 | if ( fsize < 0 ) { |
| 1519 | PrintAndLog("Error, when getting filesize"); |
| 1520 | fclose(f); |
| 1521 | return 1; |
| 1522 | } |
| 1523 | |
| 1524 | uint8_t *dump = malloc(fsize); |
| 1525 | |
| 1526 | size_t bytes_read = fread(dump, 1, fsize, f); |
| 1527 | fclose(f); |
| 1528 | if (bytes_read > ICLASS_KEYS_MAX * 8){ |
| 1529 | PrintAndLog("File is too long to load - bytes: %u", bytes_read); |
| 1530 | free(dump); |
| 1531 | return 0; |
| 1532 | } |
| 1533 | uint8_t i = 0; |
| 1534 | for (; i < bytes_read/8; i++){ |
| 1535 | memcpy(iClass_Key_Table[i],dump+(i*8),8); |
| 1536 | } |
| 1537 | free(dump); |
| 1538 | PrintAndLog("%u keys loaded", i); |
| 1539 | return 1; |
| 1540 | } |
| 1541 | |
| 1542 | static int saveKeys(char *filename) { |
| 1543 | FILE *f; |
| 1544 | f = fopen(filename,"wb"); |
| 1545 | if (f == NULL) { |
| 1546 | printf("error opening file %s\n",filename); |
| 1547 | return 0; |
| 1548 | } |
| 1549 | for (uint8_t i = 0; i < ICLASS_KEYS_MAX; i++){ |
| 1550 | if (fwrite(iClass_Key_Table[i],8,1,f) != 1){ |
| 1551 | PrintAndLog("save key failed to write to file: %s", filename); |
| 1552 | break; |
| 1553 | } |
| 1554 | } |
| 1555 | fclose(f); |
| 1556 | return 0; |
| 1557 | } |
| 1558 | |
| 1559 | static int printKeys(void) { |
| 1560 | PrintAndLog(""); |
| 1561 | for (uint8_t i = 0; i < ICLASS_KEYS_MAX; i++){ |
| 1562 | PrintAndLog("%u: %s",i,sprint_hex(iClass_Key_Table[i],8)); |
| 1563 | } |
| 1564 | PrintAndLog(""); |
| 1565 | return 0; |
| 1566 | } |
| 1567 | |
| 1568 | int usage_hf_iclass_managekeys(void) { |
| 1569 | PrintAndLog("HELP : Manage iClass Keys in client memory:\n"); |
| 1570 | PrintAndLog("Usage: hf iclass managekeys n [keynbr] k [key] f [filename] s l p\n"); |
| 1571 | PrintAndLog(" Options:"); |
| 1572 | PrintAndLog(" n <keynbr> : specify the keyNbr to set in memory"); |
| 1573 | PrintAndLog(" k <key> : set a key in memory"); |
| 1574 | PrintAndLog(" f <filename>: specify a filename to use with load or save operations"); |
| 1575 | PrintAndLog(" s : save keys in memory to file specified by filename"); |
| 1576 | PrintAndLog(" l : load keys to memory from file specified by filename"); |
| 1577 | PrintAndLog(" p : print keys loaded into memory\n"); |
| 1578 | PrintAndLog("Samples:"); |
| 1579 | PrintAndLog(" set key : hf iclass managekeys n 0 k 1122334455667788"); |
| 1580 | PrintAndLog(" save key file: hf iclass managekeys f mykeys.bin s"); |
| 1581 | PrintAndLog(" load key file: hf iclass managekeys f mykeys.bin l"); |
| 1582 | PrintAndLog(" print keys : hf iclass managekeys p\n"); |
| 1583 | return 0; |
| 1584 | } |
| 1585 | |
| 1586 | int CmdHFiClassManageKeys(const char *Cmd) { |
| 1587 | uint8_t keyNbr = 0; |
| 1588 | uint8_t dataLen = 0; |
| 1589 | uint8_t KEY[8] = {0}; |
| 1590 | char filename[FILE_PATH_SIZE]; |
| 1591 | uint8_t fileNameLen = 0; |
| 1592 | bool errors = false; |
| 1593 | uint8_t operation = 0; |
| 1594 | char tempStr[20]; |
| 1595 | uint8_t cmdp = 0; |
| 1596 | |
| 1597 | while(param_getchar(Cmd, cmdp) != 0x00) |
| 1598 | { |
| 1599 | switch(param_getchar(Cmd, cmdp)) |
| 1600 | { |
| 1601 | case 'h': |
| 1602 | case 'H': |
| 1603 | return usage_hf_iclass_managekeys(); |
| 1604 | case 'f': |
| 1605 | case 'F': |
| 1606 | fileNameLen = param_getstr(Cmd, cmdp+1, filename); |
| 1607 | if (fileNameLen < 1) { |
| 1608 | PrintAndLog("No filename found after f"); |
| 1609 | errors = true; |
| 1610 | } |
| 1611 | cmdp += 2; |
| 1612 | break; |
| 1613 | case 'n': |
| 1614 | case 'N': |
| 1615 | keyNbr = param_get8(Cmd, cmdp+1); |
| 1616 | if (keyNbr >= ICLASS_KEYS_MAX) { |
| 1617 | PrintAndLog("Invalid block number"); |
| 1618 | errors = true; |
| 1619 | } |
| 1620 | cmdp += 2; |
| 1621 | break; |
| 1622 | case 'k': |
| 1623 | case 'K': |
| 1624 | operation += 3; //set key |
| 1625 | dataLen = param_getstr(Cmd, cmdp+1, tempStr); |
| 1626 | if (dataLen == 16) { //ul-c or ev1/ntag key length |
| 1627 | errors = param_gethex(tempStr, 0, KEY, dataLen); |
| 1628 | } else { |
| 1629 | PrintAndLog("\nERROR: Key is incorrect length\n"); |
| 1630 | errors = true; |
| 1631 | } |
| 1632 | cmdp += 2; |
| 1633 | break; |
| 1634 | case 'p': |
| 1635 | case 'P': |
| 1636 | operation += 4; //print keys in memory |
| 1637 | cmdp++; |
| 1638 | break; |
| 1639 | case 'l': |
| 1640 | case 'L': |
| 1641 | operation += 5; //load keys from file |
| 1642 | cmdp++; |
| 1643 | break; |
| 1644 | case 's': |
| 1645 | case 'S': |
| 1646 | operation += 6; //save keys to file |
| 1647 | cmdp++; |
| 1648 | break; |
| 1649 | default: |
| 1650 | PrintAndLog("Unknown parameter '%c'\n", param_getchar(Cmd, cmdp)); |
| 1651 | errors = true; |
| 1652 | break; |
| 1653 | } |
| 1654 | if(errors) return usage_hf_iclass_managekeys(); |
| 1655 | } |
| 1656 | if (operation == 0){ |
| 1657 | PrintAndLog("no operation specified (load, save, or print)\n"); |
| 1658 | return usage_hf_iclass_managekeys(); |
| 1659 | } |
| 1660 | if (operation > 6){ |
| 1661 | PrintAndLog("Too many operations specified\n"); |
| 1662 | return usage_hf_iclass_managekeys(); |
| 1663 | } |
| 1664 | if (operation > 4 && fileNameLen == 0){ |
| 1665 | PrintAndLog("You must enter a filename when loading or saving\n"); |
| 1666 | return usage_hf_iclass_managekeys(); |
| 1667 | } |
| 1668 | |
| 1669 | switch (operation){ |
| 1670 | case 3: memcpy(iClass_Key_Table[keyNbr], KEY, 8); return 1; |
| 1671 | case 4: return printKeys(); |
| 1672 | case 5: return loadKeys(filename); |
| 1673 | case 6: return saveKeys(filename); |
| 1674 | break; |
| 1675 | } |
| 1676 | return 0; |
| 1677 | } |
| 1678 | |
| 1679 | static command_t CommandTable[] = |
| 1680 | { |
| 1681 | {"help", CmdHelp, 1, "This help"}, |
| 1682 | {"calcnewkey", CmdHFiClassCalcNewKey, 1, "[options..] Calc Diversified keys (blocks 3 & 4) to write new keys"}, |
| 1683 | {"clone", CmdHFiClassCloneTag, 0, "[options..] Authenticate and Clone from iClass bin file"}, |
| 1684 | {"decrypt", CmdHFiClassDecrypt, 1, "[f <fname>] Decrypt tagdump" }, |
| 1685 | {"dump", CmdHFiClassReader_Dump, 0, "[options..] Authenticate and Dump iClass tag's AA1"}, |
| 1686 | {"eload", CmdHFiClassELoad, 0, "[f <fname>] (experimental) Load data into iClass emulator memory"}, |
| 1687 | {"encryptblk", CmdHFiClassEncryptBlk, 1, "<BlockData> Encrypt given block data"}, |
| 1688 | {"list", CmdHFiClassList, 0, " (Deprecated) List iClass history"}, |
| 1689 | {"loclass", CmdHFiClass_loclass, 1, "[options..] Use loclass to perform bruteforce of reader attack dump"}, |
| 1690 | {"managekeys", CmdHFiClassManageKeys, 1, "[options..] Manage the keys to use with iClass"}, |
| 1691 | {"readblk", CmdHFiClass_ReadBlock, 0, "[options..] Authenticate and Read iClass block"}, |
| 1692 | {"reader", CmdHFiClassReader, 0, " Read an iClass tag"}, |
| 1693 | {"readtagfile", CmdHFiClassReadTagFile, 1, "[options..] Display Content from tagfile"}, |
| 1694 | {"replay", CmdHFiClassReader_Replay, 0, "<mac> Read an iClass tag via Reply Attack"}, |
| 1695 | {"sim", CmdHFiClassSim, 0, "[options..] Simulate iClass tag"}, |
| 1696 | {"snoop", CmdHFiClassSnoop, 0, " Eavesdrop iClass communication"}, |
| 1697 | {"writeblk", CmdHFiClass_WriteBlock, 0, "[options..] Authenticate and Write iClass block"}, |
| 1698 | {NULL, NULL, 0, NULL} |
| 1699 | }; |
| 1700 | |
| 1701 | int CmdHFiClass(const char *Cmd) |
| 1702 | { |
| 1703 | CmdsParse(CommandTable, Cmd); |
| 1704 | return 0; |
| 1705 | } |
| 1706 | |
| 1707 | int CmdHelp(const char *Cmd) |
| 1708 | { |
| 1709 | CmdsHelp(CommandTable); |
| 1710 | return 0; |
| 1711 | } |