diff --git a/CHANGELOG.md b/CHANGELOG.md index 50c9ba007..55e11e25a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac ## [unreleased][unreleased] - Added `hf mfdes getversion` command (@kormax) +- Added DES transport mode support for iCLASS credential encode/decode helpers. (@cindersocket) - Changed `doc/magic_cards_notes.md` - updated the ID82xx / Hitag ยต clone section with current Proxmark3 support, chip variations, default passwords and detection notes (@mishamyte) - Added two Mifare Classic keys into extensive dictionary which are hardcoded into Mifare Plus SE (@team-orangeBlue) - Fixed `hf mfp rdbl` when using "read multiple" blocks by decrypting the entire buffer instead of one block only (@team-orangeBlue) diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index a4597951b..a08e4ff03 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -140,6 +140,22 @@ typedef enum { TRIPLEDES } BLOCK79ENCRYPTION; +static void iclass_set_transport_mode(uint8_t *data, BLOCK79ENCRYPTION mode); +static int iclass_apply_transport_mode_to_block(uint8_t *blk_data, const uint8_t *key, BLOCK79ENCRYPTION mode, bool encrypt); + +static const CLIParserOption IClassEncodeEncryptionOpts[] = { + {None, "none"}, + {DES, "des"}, + {TRIPLEDES, "2k3des"}, + {0, NULL}, +}; + +static const CLIParserOption IClassTransportEncryptionOpts[] = { + {DES, "des"}, + {TRIPLEDES, "2k3des"}, + {0, NULL}, +}; + // 16 bytes key static int iclass_load_transport(uint8_t *key, uint8_t n) { size_t keylen = 0; @@ -169,41 +185,100 @@ static int iclass_load_transport(uint8_t *key, uint8_t n) { static void iclass_decrypt_transport(uint8_t *key, uint8_t limit, uint8_t *enc_data, uint8_t *dec_data, BLOCK79ENCRYPTION aa1_encryption) { - // tripledes - mbedtls_des3_context ctx; - mbedtls_des3_set2key_dec(&ctx, key); + bool should_decrypt = false; + switch (aa1_encryption) { + case DES: + case TRIPLEDES: + should_decrypt = true; + break; + case RFU: + case None: + default: + break; + } bool decrypted_block789 = false; for (uint8_t i = 0; i < limit; ++i) { uint16_t idx = i * PICOPASS_BLOCK_SIZE; - - switch (aa1_encryption) { - // Right now, only 3DES is supported - case TRIPLEDES: - // Decrypt block 7,8,9 if configured. - if (i > 6 && i <= 9 && memcmp(enc_data + idx, empty, PICOPASS_BLOCK_SIZE) != 0) { - mbedtls_des3_crypt_ecb(&ctx, enc_data + idx, dec_data + idx); - decrypted_block789 = true; - } - break; - case DES: - case RFU: - case None: - // Nothing to do for None anyway... - default: - continue; + if (should_decrypt == false || i <= 6 || i > 9) { + continue; } + if (memcmp(enc_data + idx, empty, PICOPASS_BLOCK_SIZE) == 0 || memcmp(enc_data + idx, zeros, PICOPASS_BLOCK_SIZE) == 0) { + continue; + } + + memcpy(dec_data + idx, enc_data + idx, PICOPASS_BLOCK_SIZE); + if (iclass_apply_transport_mode_to_block(dec_data + idx, key, aa1_encryption, false) != PM3_SUCCESS) { + break; + } + decrypted_block789 = true; + if (decrypted_block789) { // Set the 2 last bits of block6 to 0 to mark the data as decrypted dec_data[(6 * PICOPASS_BLOCK_SIZE) + 7] &= 0xFC; } } +} +static void iclass_set_transport_mode(uint8_t *data, BLOCK79ENCRYPTION mode) { + data[7] &= 0xFC; + data[7] |= (mode & 0x03); +} + +static void iclass_des_block_transform(uint8_t *blk_data, const uint8_t *key, bool encrypt) { + mbedtls_des_context ctx; + if (encrypt) { + mbedtls_des_setkey_enc(&ctx, key); + } else { + mbedtls_des_setkey_dec(&ctx, key); + } + mbedtls_des_crypt_ecb(&ctx, blk_data, blk_data); + mbedtls_des_free(&ctx); +} + +static void iclass_2k3des_block_transform(uint8_t *blk_data, const uint8_t *key, bool encrypt) { + mbedtls_des3_context ctx; + if (encrypt) { + mbedtls_des3_set2key_enc(&ctx, key); + } else { + mbedtls_des3_set2key_dec(&ctx, key); + } + mbedtls_des3_crypt_ecb(&ctx, blk_data, blk_data); mbedtls_des3_free(&ctx); } +static int iclass_apply_transport_mode_to_block(uint8_t *blk_data, const uint8_t *key, BLOCK79ENCRYPTION mode, bool encrypt) { + if (blk_data == NULL) { + return PM3_EINVARG; + } + + switch (mode) { + case None: + case RFU: + return PM3_SUCCESS; + case DES: + iclass_des_block_transform(blk_data, key, encrypt); + return PM3_SUCCESS; + case TRIPLEDES: + iclass_2k3des_block_transform(blk_data, key, encrypt); + return PM3_SUCCESS; + default: + return PM3_EINVARG; + } +} + +static int iclass_apply_transport_mode_to_credential(uint8_t *credential, const uint8_t *key, BLOCK79ENCRYPTION mode, bool encrypt) { + for (uint8_t blockno = 0; blockno < 3; blockno++) { + int res = iclass_apply_transport_mode_to_block(credential + (blockno * PICOPASS_BLOCK_SIZE), key, mode, encrypt); + if (res != PM3_SUCCESS) { + return res; + } + } + return PM3_SUCCESS; +} + static inline uint32_t leadingzeros(uint64_t a) { #if defined __GNUC__ return __builtin_clzll(a); @@ -1200,6 +1275,8 @@ static int CmdHFiClassTagSim(const char *Cmd) { "the tool tries to load " ICLASS_DECRYPTION_BIN ".", "hf iclass tagsim --fc 101 --cn 1337\n" "hf iclass tagsim -w H10301 --fc 101 --cn 1337 --ki 0\n" + "hf iclass tagsim -w H10301 --fc 101 --cn 1337 --enc none\n" + "hf iclass tagsim -w H10301 --fc 101 --cn 1337 --enc des\n" "hf iclass tagsim -w H10301 --fc 101 --cn 1337 --kd 0102030405060708 --elite\n" "hf iclass tagsim --bin 10001111100000001010100011 --ki 0\n" "hf iclass tagsim -w H10301 --fc 101 --cn 1337 --ki 0 --enckey 00000000000000000000000000000000\n" @@ -1220,6 +1297,7 @@ static int CmdHFiClassTagSim(const char *Cmd) { arg_lit0(NULL, "raw", "Keys are already diversified, skip diversification"), arg_str0(NULL, "csn", "", "Custom CSN, 8 hex bytes (auto-generated if omitted)"), arg_str0(NULL, "enckey", "", "3DES transport key, 16 hex bytes"), + arg_str0(NULL, "enc", "", "credential transport mode (default: 2k3des)"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -1272,9 +1350,15 @@ static int CmdHFiClassTagSim(const char *Cmd) { uint8_t *enckeyptr = NULL; bool have_enc_key = false; CLIGetHexWithReturn(ctx, 13, enc_key, &enc_key_len); + BLOCK79ENCRYPTION enc_mode = TRIPLEDES; + int enc_mode_res = CLIGetOptionList(arg_get_str(ctx, 14), IClassEncodeEncryptionOpts, (int *)&enc_mode); CLIParserFree(ctx); + if (enc_mode_res != 0) { + return PM3_EINVARG; + } + // --- validation if ((rawkey + elite) > 1) { PrintAndLogEx(ERR, "Cannot combine --elite and --raw"); @@ -1341,9 +1425,16 @@ static int CmdHFiClassTagSim(const char *Cmd) { have_enc_key = true; } - if (have_enc_key == false) { - // try smart-card helper first, then fall back to file - bool use_sc = IsCardHelperPresent(false); + bool use_sc = false; + if (enc_mode == None && have_enc_key) { + PrintAndLogEx(WARNING, "Transport mode marker is none; --enckey will be ignored."); + } + + if (enc_mode != None && have_enc_key == false) { + // Try smart-card helper for 2K3DES only, then fall back to file. + if (enc_mode == TRIPLEDES) { + use_sc = IsCardHelperPresent(false); + } if (use_sc == false) { size_t keylen = 0; int res = loadFile_safe(ICLASS_DECRYPTION_BIN, "", (void **)&enckeyptr, &keylen); @@ -1361,6 +1452,10 @@ static int CmdHFiClassTagSim(const char *Cmd) { } } + if (enc_mode != None && have_enc_key == false) { + enc_mode = None; + } + // --------------------------------------------------------------- // Build the 256-byte (32-block) dump in memory // --------------------------------------------------------------- @@ -1461,19 +1556,15 @@ static int CmdHFiClassTagSim(const char *Cmd) { memcpy(credential + 12, &packed.Bot, sizeof(packed.Bot)); } - // Capture smart-card helper state before starting simulation (can't query mid-sim) - bool use_sc = have_enc_key ? IsCardHelperPresent(false) : false; - - // Encrypt credential blocks 7, 8, 9 - if (have_enc_key) { + iclass_set_transport_mode(credential, enc_mode); + if (enc_mode != None) { if (use_sc) { Encrypt(credential + 8, credential + 8); Encrypt(credential + 16, credential + 16); Encrypt(credential + 24, credential + 24); - } else { - iclass_encrypt_block_data(credential + 8, enc_key); - iclass_encrypt_block_data(credential + 16, enc_key); - iclass_encrypt_block_data(credential + 24, enc_key); + } else if (iclass_apply_transport_mode_to_credential(credential + 8, enc_key, enc_mode, true) != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Failed to encode credential transport blocks"); + return PM3_EINVARG; } } @@ -1599,15 +1690,16 @@ static int CmdHFiClassTagSim(const char *Cmd) { } } - if (have_enc_key) { + iclass_set_transport_mode(new_cred, enc_mode); + if (enc_mode != None) { if (use_sc) { Encrypt(new_cred + 8, new_cred + 8); Encrypt(new_cred + 16, new_cred + 16); Encrypt(new_cred + 24, new_cred + 24); - } else { - iclass_encrypt_block_data(new_cred + 8, enc_key); - iclass_encrypt_block_data(new_cred + 16, enc_key); - iclass_encrypt_block_data(new_cred + 24, enc_key); + } else if (iclass_apply_transport_mode_to_credential(new_cred + 8, enc_key, enc_mode, true) != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Failed to encode credential transport blocks"); + running = false; + break; } } @@ -2119,7 +2211,7 @@ static void iclass_decode_credentials(uint8_t *data) { static int CmdHFiClassDecrypt(const char *Cmd) { CLIParserContext *clictx; CLIParserInit(&clictx, "hf iclass decrypt", - "3DES decrypt data\n" + "DES/3DES decrypt data\n" "This is a naive implementation, it tries to decrypt every block after block 6.\n" "Correct behaviour would be to decrypt only the application areas where the key is valid,\n" "which is defined by the configuration block.\n" @@ -2135,8 +2227,9 @@ static int CmdHFiClassDecrypt(const char *Cmd) { void *argtable[] = { arg_param_begin, arg_str0("f", "file", "", "Specify a filename for dump file"), - arg_str0("d", "data", "", "3DES encrypted data"), - arg_str0("k", "key", "", "3DES transport key"), + arg_str0("d", "data", "", "DES/3DES encrypted data"), + arg_str0("k", "key", "", "DES/3DES transport key"), + arg_str0(NULL, "enc", "[des|2k3des]", "transport encryption mode"), arg_lit0("v", "verbose", "verbose output"), arg_lit0(NULL, "d6", "decode as block 6"), arg_lit0("z", "dense", "dense dump output style"), @@ -2162,10 +2255,16 @@ static int CmdHFiClassDecrypt(const char *Cmd) { CLIGetHexWithReturn(clictx, 3, key, &key_len); - bool verbose = arg_get_lit(clictx, 4); - bool use_decode6 = arg_get_lit(clictx, 5); - bool dense_output = g_session.dense_output || arg_get_lit(clictx, 6); - bool nosave = arg_get_lit(clictx, 7); + int transport_mode = TRIPLEDES; + if (CLIGetOptionList(arg_get_str(clictx, 4), IClassTransportEncryptionOpts, &transport_mode)) { + CLIParserFree(clictx); + return PM3_EINVARG; + } + + bool verbose = arg_get_lit(clictx, 5); + bool use_decode6 = arg_get_lit(clictx, 6); + bool dense_output = g_session.dense_output || arg_get_lit(clictx, 7); + bool nosave = arg_get_lit(clictx, 8); CLIParserFree(clictx); // sanity checks @@ -2206,15 +2305,9 @@ static int CmdHFiClassDecrypt(const char *Cmd) { bool use_sc = false; if (have_key == false) { use_sc = IsCardHelperPresent(verbose); - if (use_sc == false) { - size_t keylen = 0; - res = loadFile_safe(ICLASS_DECRYPTION_BIN, "", (void **)&keyptr, &keylen); - if (res != PM3_SUCCESS) { - PrintAndLogEx(INFO, "Couldn't find any decryption methods"); - free(decrypted); - return PM3_EINVARG; - } - + size_t keylen = 0; + res = loadFile_safe(ICLASS_DECRYPTION_BIN, "", (void **)&keyptr, &keylen); + if (res == PM3_SUCCESS) { if (keylen != 16) { PrintAndLogEx(ERR, "Failed to load transport key from file"); free(keyptr); @@ -2223,21 +2316,30 @@ static int CmdHFiClassDecrypt(const char *Cmd) { } memcpy(key, keyptr, sizeof(key)); free(keyptr); + have_key = true; + } else if (use_sc == false) { + PrintAndLogEx(INFO, "Couldn't find any decryption methods"); + free(decrypted); + return PM3_EINVARG; } } - // tripledes - mbedtls_des3_context ctx; - mbedtls_des3_set2key_dec(&ctx, key); - // decrypt user supplied data if (have_data) { uint8_t dec_data[PICOPASS_BLOCK_SIZE] = {0}; - if (use_sc) { + memcpy(dec_data, enc_data, sizeof(dec_data)); + if (use_sc && transport_mode == TRIPLEDES) { Decrypt(enc_data, dec_data); + } else if (have_key) { + if (iclass_apply_transport_mode_to_block(dec_data, key, (BLOCK79ENCRYPTION)transport_mode, false) != PM3_SUCCESS) { + free(decrypted); + return PM3_EINVARG; + } } else { - mbedtls_des3_crypt_ecb(&ctx, enc_data, dec_data); + PrintAndLogEx(INFO, "Couldn't find any decryption methods"); + free(decrypted); + return PM3_EINVARG; } PrintAndLogEx(SUCCESS, "encrypted... %s", sprint_hex_inrow(enc_data, sizeof(enc_data))); @@ -2280,19 +2382,26 @@ static int CmdHFiClassDecrypt(const char *Cmd) { memcpy(enc_data, decrypted + idx, PICOPASS_BLOCK_SIZE); switch (aa1_encryption) { - // Right now, only 3DES is supported + case DES: case TRIPLEDES: // Decrypt block 7,8,9 if configured. - if (blocknum > 6 && blocknum <= 9 && memcmp(enc_data, empty, PICOPASS_BLOCK_SIZE) != 0) { - if (use_sc) { + if (blocknum > 6 && blocknum <= 9 && memcmp(enc_data, empty, PICOPASS_BLOCK_SIZE) != 0 && memcmp(enc_data, zeros, PICOPASS_BLOCK_SIZE) != 0) { + if (use_sc && aa1_encryption == TRIPLEDES) { Decrypt(enc_data, decrypted + idx); + } else if (have_key) { + memcpy(decrypted + idx, enc_data, PICOPASS_BLOCK_SIZE); + if (iclass_apply_transport_mode_to_block(decrypted + idx, key, aa1_encryption, false) != PM3_SUCCESS) { + free(decrypted); + return PM3_EINVARG; + } } else { - mbedtls_des3_crypt_ecb(&ctx, enc_data, decrypted + idx); + PrintAndLogEx(INFO, "Couldn't find any decryption methods"); + free(decrypted); + return PM3_EINVARG; } decrypted_block789 = true; } break; - case DES: case RFU: case None: // Nothing to do for None anyway... @@ -2362,14 +2471,13 @@ static int CmdHFiClassDecrypt(const char *Cmd) { free(decrypted); } - mbedtls_des3_free(&ctx); return PM3_SUCCESS; } static int CmdHFiClassEncryptBlk(const char *Cmd) { CLIParserContext *clictx; CLIParserInit(&clictx, "hf iclass encrypt", - "3DES encrypt data\n" + "DES/3DES encrypt data\n" "OBS! In order to use this function, the file 'iclass_decryptionkey.bin' must reside\n" "in the resources directory. The file should be 16 hex bytes of binary data", "hf iclass encrypt -d 0102030405060708\n" @@ -2378,7 +2486,8 @@ static int CmdHFiClassEncryptBlk(const char *Cmd) { void *argtable[] = { arg_param_begin, arg_str1("d", "data", "", "data to encrypt"), - arg_str0("k", "key", "", "3DES transport key"), + arg_str0("k", "key", "", "DES/3DES transport key"), + arg_str0(NULL, "enc", "[des|2k3des]", "transport encryption mode"), arg_lit0("v", "verbose", "verbose output"), arg_param_end }; @@ -2402,6 +2511,12 @@ static int CmdHFiClassEncryptBlk(const char *Cmd) { CLIGetHexWithReturn(clictx, 2, key, &key_len); + int transport_mode = TRIPLEDES; + if (CLIGetOptionList(arg_get_str(clictx, 3), IClassTransportEncryptionOpts, &transport_mode)) { + CLIParserFree(clictx); + return PM3_EINVARG; + } + if (key_len > 0) { if (key_len != 16) { PrintAndLogEx(ERR, "Transport key must be 16 hex bytes (32 HEX characters)"); @@ -2411,13 +2526,15 @@ static int CmdHFiClassEncryptBlk(const char *Cmd) { have_key = true; } - bool verbose = arg_get_lit(clictx, 3); + bool verbose = arg_get_lit(clictx, 4); CLIParserFree(clictx); bool use_sc = false; if (have_key == false) { - use_sc = IsCardHelperPresent(verbose); + if (transport_mode == TRIPLEDES) { + use_sc = IsCardHelperPresent(verbose); + } if (use_sc == false) { size_t keylen = 0; int res = loadFile_safe(ICLASS_DECRYPTION_BIN, "", (void **)&keyptr, &keylen); @@ -2439,10 +2556,10 @@ static int CmdHFiClassEncryptBlk(const char *Cmd) { PrintAndLogEx(SUCCESS, "plain....... %s", sprint_hex_inrow(blk_data, sizeof(blk_data))); - if (use_sc) { + if (use_sc && transport_mode == TRIPLEDES) { Encrypt(blk_data, blk_data); - } else { - iclass_encrypt_block_data(blk_data, key); + } else if (iclass_apply_transport_mode_to_block(blk_data, key, (BLOCK79ENCRYPTION)transport_mode, true) != PM3_SUCCESS) { + return PM3_EINVARG; } PrintAndLogEx(SUCCESS, "encrypted... " _YELLOW_("%s"), sprint_hex_inrow(blk_data, sizeof(blk_data))); @@ -7371,6 +7488,7 @@ static int CmdHFiClassEncode(const char *Cmd) { arg_lit0(NULL, "emu", "Write to emulation memory instead of card"), arg_lit0(NULL, "shallow", "use shallow (ASK) reader modulation instead of OOK"), arg_lit0("v", NULL, "verbose (print encoded blocks)"), + arg_str0(NULL, "enc", "[none|des|2k3des]", "transport encryption mode"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -7454,6 +7572,11 @@ static int CmdHFiClassEncode(const char *Cmd) { bool shallow_mod = arg_get_lit(ctx, 13); bool verbose = arg_get_lit(ctx, 14); + int transport_mode = TRIPLEDES; + if (CLIGetOptionList(arg_get_str(ctx, 15), IClassEncodeEncryptionOpts, &transport_mode)) { + CLIParserFree(ctx); + return PM3_EINVARG; + } CLIParserFree(ctx); @@ -7470,6 +7593,10 @@ static int CmdHFiClassEncode(const char *Cmd) { have_enc_key = true; } + if (transport_mode == None && enc_key_len != 0) { + PrintAndLogEx(WARNING, "Transport mode marker is none; --enckey will be ignored."); + } + if (bin_len > 64) { PrintAndLogEx(ERR, "Binary wiegand string must be less than 64 bits"); return PM3_EINVARG; @@ -7480,9 +7607,9 @@ static int CmdHFiClassEncode(const char *Cmd) { return PM3_EINVARG; } - if (have_enc_key == false) { + if (transport_mode != None && have_enc_key == false) { // The IsCardHelperPresent function clears the emulator memory - if (use_emulator_memory) { + if (use_emulator_memory || transport_mode != TRIPLEDES) { use_sc = false; } else { use_sc = IsCardHelperPresent(false); @@ -7567,18 +7694,20 @@ static int CmdHFiClassEncode(const char *Cmd) { memcpy(credential + 12, &packed.Bot, sizeof(packed.Bot)); } - // encrypt with transport key - if (use_sc) { - Encrypt(credential + 8, credential + 8); - Encrypt(credential + 16, credential + 16); - Encrypt(credential + 24, credential + 24); - } else { - iclass_encrypt_block_data(credential + 8, enc_key); - iclass_encrypt_block_data(credential + 16, enc_key); - iclass_encrypt_block_data(credential + 24, enc_key); + iclass_set_transport_mode(credential, (BLOCK79ENCRYPTION)transport_mode); + if (transport_mode != None) { + if (use_sc) { + Encrypt(credential + 8, credential + 8); + Encrypt(credential + 16, credential + 16); + Encrypt(credential + 24, credential + 24); + } else if (iclass_apply_transport_mode_to_credential(credential + 8, enc_key, (BLOCK79ENCRYPTION)transport_mode, true) != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Failed to apply transport mode"); + return PM3_EINVARG; + } } if (verbose) { + PrintAndLogEx(INFO, "Mode: %s", CLIGetOptionListStr(IClassEncodeEncryptionOpts, transport_mode)); for (uint8_t i = 0; i < 4; i++) { PrintAndLogEx(INFO, "Block %d/0x0%x -> " _YELLOW_("%s"), 6 + i, 6 + i, sprint_hex_inrow(credential + (i * 8), 8)); } diff --git a/tools/pm3_online_tests.sh b/tools/pm3_online_tests.sh index e56b03003..b5963414e 100755 --- a/tools/pm3_online_tests.sh +++ b/tools/pm3_online_tests.sh @@ -11,6 +11,7 @@ TESTALL=false TESTDESFIREVALUE=false TESTHIDWIEGAND=false TESTMFHIDENCODE=false +TESTICLASSREADER=false NEED_MF_HID_ENCODE_WIPE=false TESTMANUAL=false @@ -20,12 +21,14 @@ while (( "$#" )); do case "$1" in -h|--help) echo """ -Usage: $0 [--pm3bin /path/to/pm3] [desfire_value|hid_wiegand|mf_hid_encode] +Usage: $0 [--pm3bin /path/to/pm3] [--pm3port /dev/tty...] [desfire_value|hid_wiegand|mf_hid_encode|iclass_reader] --pm3bin ...: Specify path to pm3 binary to test + --pm3port ...: Specify serial port for client/proxmark3 --manual ...: Pause after successful online LF HID clone/read checks for external reader verification desfire_value: Test DESFire value operations with card hid_wiegand: Test LF HID T55xx clone and PM3 readback flows mf_hid_encode: Test MIFARE Classic HID encoding flows + iclass_reader: Load iCLASS HID credentials into emulator memory for external reader verification You must specify a test target - no default 'all' for online tests """ exit 0 @@ -39,6 +42,15 @@ Usage: $0 [--pm3bin /path/to/pm3] [desfire_value|hid_wiegand|mf_hid_encode] exit 1 fi ;; + --pm3port) + if [ -n "$2" ] && [ ${2:0:1} != "-" ]; then + PM3PORT=$2 + shift 2 + else + echo "Error: Argument for $1 is missing" >&2 + exit 1 + fi + ;; --manual) TESTMANUAL=true shift @@ -58,6 +70,11 @@ Usage: $0 [--pm3bin /path/to/pm3] [desfire_value|hid_wiegand|mf_hid_encode] TESTMFHIDENCODE=true shift ;; + iclass_reader) + TESTALL=false + TESTICLASSREADER=true + shift + ;; -*|--*=) # unsupported flags echo "Error: Unsupported flag $1" >&2 exit 1 @@ -341,7 +358,7 @@ if command -v git >/dev/null && git rev-parse --is-inside-work-tree >/dev/null 2 fi # Check that user specified a test -if [ "$TESTDESFIREVALUE" = false ] && [ "$TESTHIDWIEGAND" = false ] && [ "$TESTMFHIDENCODE" = false ]; then +if [ "$TESTDESFIREVALUE" = false ] && [ "$TESTHIDWIEGAND" = false ] && [ "$TESTMFHIDENCODE" = false ] && [ "$TESTICLASSREADER" = false ]; then echo "Error: You must specify a test target. Use -h for help." exit 1 fi @@ -399,6 +416,28 @@ while true; do if ! RestoreMfHidEncodeCard; then break; fi if ! CheckMfHidEncodeCleanup "hf mf encodehid cleanup verify"; then break; fi fi + + if $TESTICLASSREADER; then + echo -e "\n${C_BLUE}Testing iCLASS reader verification${C_NC} ${PM3BIN:=./pm3}" + if ! CheckFileExist "pm3 exists" "$PM3BIN"; then break; fi + if [ -z "${PM3PORT:-}" ]; then + PM3PORT="$(ls /dev/cu.usbmodem* 2>/dev/null | head -n 1 || true)" + fi + if [ -z "${PM3PORT:-}" ]; then + echo "Error: No Proxmark3 serial port found. Pass --pm3port /dev/tty..." + break + fi + echo "Using PM3 port: $PM3PORT" + PM3CMD="$PM3BIN" + PM3CMD="$PM3CMD $PM3PORT" + + WaitForEnter "PRESS ENTER TO START ICLASS PLAIN SIM, PRESENT THE PM3 TO ANOTHER READER, CONFIRM: iCLASS H10301 FC 31 CN 337, THEN PRESS THE PM3 BUTTON TO STOP SIM" + if ! CheckExecute "hf iclass emu reader plain" "$PM3CMD -c 'hf iclass tagsim -w H10301 --fc 31 --cn 337 --enc none' 2>&1" "Uploaded .* bytes to emulator memory"; then break; fi + WaitForEnter "PRESS ENTER TO START ICLASS DES SIM, PRESENT THE PM3 TO ANOTHER READER, CONFIRM: iCLASS H10301 FC 31 CN 337, THEN PRESS THE PM3 BUTTON TO STOP SIM" + if ! CheckExecute "hf iclass emu reader des" "$PM3CMD -c 'hf iclass tagsim -w H10301 --fc 31 --cn 337 --enc des' 2>&1" "Uploaded .* bytes to emulator memory"; then break; fi + WaitForEnter "PRESS ENTER TO START ICLASS 2K3DES SIM, PRESENT THE PM3 TO ANOTHER READER, CONFIRM: iCLASS H10301 FC 31 CN 337, THEN PRESS THE PM3 BUTTON TO STOP SIM" + if ! CheckExecute "hf iclass emu reader 2k3des" "$PM3CMD -c 'hf iclass tagsim -w H10301 --fc 31 --cn 337 --enc 2k3des' 2>&1" "Uploaded .* bytes to emulator memory"; then break; fi + fi echo -e "\n------------------------------------------------------------" echo -e "Tests [ ${C_GREEN}OK${C_NC} ] ${C_OK}\n" diff --git a/tools/pm3_tests.sh b/tools/pm3_tests.sh index 94b904f4c..1d5e89ef9 100755 --- a/tools/pm3_tests.sh +++ b/tools/pm3_tests.sh @@ -500,6 +500,15 @@ while true; do if ! CheckExecute "wiegand decode new no padded bin" "if ! $CLIENTBIN -c 'wiegand decode --new 06BD88EB80' 2>&1 | grep -q 'padded bin'; then echo OK; fi" "OK"; then break; fi if ! CheckExecute "wiegand decode new 96-bit" "$CLIENTBIN -c 'wiegand decode --new 00555555555555555555555555'" "hex\\.{14} 555555555555555555555555"; then break; fi if ! CheckExecute "wiegand decode new 48-bit" "$CLIENTBIN -c 'wiegand decode --new 0000A4550148AB'" "C1k48s.*FC: 42069 CN: 42069 parity \( ok \)"; then break; fi + + if ! CheckFileExist "iCLASS transport key exists" "$RESOURCEPATH/iclass_decryptionkey.bin"; then break; fi + ICLASS_TRANSPORT_KEY="$(od -An -tx1 -v "$RESOURCEPATH/iclass_decryptionkey.bin" | tr -d ' \n')" + if ! CheckExecute "hf iclass encrypt default 2k3des" "$CLIENTBIN -c 'hf iclass encrypt -d 00000000063E02A3 -k $ICLASS_TRANSPORT_KEY'" "encrypted\\.\\.\\. 10A145919ED16F50"; then break; fi + if ! CheckExecute "hf iclass decrypt default 2k3des" "$CLIENTBIN -c 'hf iclass decrypt -d 10A145919ED16F50 -k $ICLASS_TRANSPORT_KEY'" "plain\\.\\.\\.\\.\\.\\.\\. 00000000063E02A3"; then break; fi + if ! CheckExecute "hf iclass encrypt des" "$CLIENTBIN -c 'hf iclass encrypt -d 00000000063E02A3 --enc des -k $ICLASS_TRANSPORT_KEY'" "encrypted\\.\\.\\. D50D3FC66AF7E0F3"; then break; fi + if ! CheckExecute "hf iclass decrypt des" "$CLIENTBIN -c 'hf iclass decrypt -d D50D3FC66AF7E0F3 --enc des -k $ICLASS_TRANSPORT_KEY'" "plain\\.\\.\\.\\.\\.\\.\\. 00000000063E02A3"; then break; fi + if ! CheckExecute "hf iclass view dump" "$CLIENTBIN -c 'hf iclass view -f traces/iclass/hf-iclass-dump.json'" "7/0x07 \\| 78 36 02 A2 28 30 10 E8"; then break; fi + if ! CheckExecute "hf iclass decrypt dump" "$CLIENTBIN -c 'hf iclass decrypt -f traces/iclass/hf-iclass-dump.json --ns -k $ICLASS_TRANSPORT_KEY'" "C1k48s.*FC: 69 CN: 69420 parity \\( ok \\)"; then break; fi if ! CheckExecute "wiegand Verkada40 encode test 1" "$CLIENTBIN -c 'wiegand encode -w Verkada40 --fc 50 --cn 1001'" "86400007D3"; then break; fi if ! CheckExecute "wiegand Verkada40 decode test 1" "$CLIENTBIN -c 'wiegand decode --raw 86400007D3'" "Verkada40.*FC: 50 CN: 1001 parity \( ok \)"; then break; fi if ! CheckExecute "wiegand Verkada40 encode test 2" "$CLIENTBIN -c 'wiegand encode -w Verkada40 --fc 50 --cn 1004'" "86400007D9"; then break; fi