X-Git-Url: https://git.zerfleddert.de/cgi-bin/gitweb.cgi/proxmark3-svn/blobdiff_plain/0de8e3874d6524379550a50a4d9c29faf2d239ea..6063655a3ca719d224c61ece09645cedd5b4818c:/client/mifarehost.c diff --git a/client/mifarehost.c b/client/mifarehost.c index d3f4a4cd..4e9ac539 100644 --- a/client/mifarehost.c +++ b/client/mifarehost.c @@ -14,8 +14,8 @@ #include #include "mifarehost.h" #include "proxmark3.h" - -#define llx PRIx64 +//#include "radixsort.h" +#include // MIFARE int compar_int(const void * a, const void * b) { @@ -23,16 +23,25 @@ int compar_int(const void * a, const void * b) { //return (*(uint64_t*)b - *(uint64_t*)a); // better: - if (*(uint64_t*)b == *(uint64_t*)a) return 0; - else if (*(uint64_t*)b > *(uint64_t*)a) return 1; - else return -1; + if (*(uint64_t*)b > *(uint64_t*)a) return 1; + if (*(uint64_t*)b < *(uint64_t*)a) return -1; + return 0; + + //return (*(uint64_t*)b > *(uint64_t*)a) - (*(uint64_t*)b < *(uint64_t*)a); } // Compare 16 Bits out of cryptostate int Compare16Bits(const void * a, const void * b) { - if ((*(uint64_t*)b & 0x00ff000000ff0000) == (*(uint64_t*)a & 0x00ff000000ff0000)) return 0; - else if ((*(uint64_t*)b & 0x00ff000000ff0000) > (*(uint64_t*)a & 0x00ff000000ff0000)) return 1; - else return -1; + if ((*(uint64_t*)b & 0x00ff000000ff0000) > (*(uint64_t*)a & 0x00ff000000ff0000)) return 1; + if ((*(uint64_t*)b & 0x00ff000000ff0000) < (*(uint64_t*)a & 0x00ff000000ff0000)) return -1; + return 0; + +/* return + ((*(uint64_t*)b & 0x00ff000000ff0000) > (*(uint64_t*)a & 0x00ff000000ff0000)) + - + ((*(uint64_t*)b & 0x00ff000000ff0000) < (*(uint64_t*)a & 0x00ff000000ff0000)) + ; +*/ } typedef @@ -59,13 +68,13 @@ void* nested_worker_thread(void *arg) { struct Crypto1State *p1; StateList_t *statelist = arg; - - statelist->head.slhead = lfsr_recovery32(statelist->ks1, statelist->nt ^ statelist->uid); + statelist->head.slhead = lfsr_recovery32(statelist->ks1, statelist->nt ^ statelist->uid); + for (p1 = statelist->head.slhead; *(uint64_t *)p1 != 0; p1++); + statelist->len = p1 - statelist->head.slhead; statelist->tail.sltail = --p1; qsort(statelist->head.slhead, statelist->len, sizeof(uint64_t), Compare16Bits); - return statelist->head.slhead; } @@ -74,28 +83,20 @@ int mfnested(uint8_t blockNo, uint8_t keyType, uint8_t * key, uint8_t trgBlockNo uint16_t i; uint32_t uid; UsbCommand resp; - StateList_t statelists[2]; struct Crypto1State *p1, *p2, *p3, *p4; - // flush queue - WaitForResponseTimeout(CMD_ACK,NULL,100); - UsbCommand c = {CMD_MIFARE_NESTED, {blockNo + keyType * 0x100, trgBlockNo + trgKeyType * 0x100, calibrate}}; memcpy(c.d.asBytes, key, 6); + clearCommandBuffer(); SendCommand(&c); + if (!WaitForResponseTimeout(CMD_ACK, &resp, 1500)) return -1; - if (!WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { - return -1; - } - - if (resp.arg[0]) { - return resp.arg[0]; // error during nested - } - + // error during nested + if (resp.arg[0]) return resp.arg[0]; + memcpy(&uid, resp.d.asBytes, 4); - PrintAndLog("uid:%08x trgbl=%d trgkey=%x", uid, (uint16_t)resp.arg[2] & 0xff, (uint16_t)resp.arg[2] >> 8); - + for (i = 0; i < 2; i++) { statelists[i].blockNo = resp.arg[2] & 0xff; statelists[i].keyType = (resp.arg[2] >> 8) & 0xff; @@ -104,28 +105,26 @@ int mfnested(uint8_t blockNo, uint8_t keyType, uint8_t * key, uint8_t trgBlockNo memcpy(&statelists[i].ks1, (void *)(resp.d.asBytes + 4 + i * 8 + 4), 4); } - // calc keys - + // calc keys pthread_t thread_id[2]; // create and run worker threads - for (i = 0; i < 2; i++) { + for (i = 0; i < 2; i++) pthread_create(thread_id + i, NULL, nested_worker_thread, &statelists[i]); - } - + // wait for threads to terminate: - for (i = 0; i < 2; i++) { + for (i = 0; i < 2; i++) pthread_join(thread_id[i], (void*)&statelists[i].head.slhead); - } - // the first 16 Bits of the cryptostate already contain part of our key. // Create the intersection of the two lists based on these 16 Bits and // roll back the cryptostate p1 = p3 = statelists[0].head.slhead; p2 = p4 = statelists[1].head.slhead; + while (p1 <= statelists[0].tail.sltail && p2 <= statelists[1].tail.sltail) { if (Compare16Bits(p1, p2) == 0) { + struct Crypto1State savestate, *savep = &savestate; savestate = *p1; while(Compare16Bits(p1, savep) == 0 && p1 <= statelists[0].tail.sltail) { @@ -147,6 +146,7 @@ int mfnested(uint8_t blockNo, uint8_t keyType, uint8_t * key, uint8_t trgBlockNo while (Compare16Bits(p1, p2) == 1) p2++; } } + p3->even = 0; p3->odd = 0; p4->even = 0; p4->odd = 0; statelists[0].len = p3 - statelists[0].head.slhead; @@ -158,10 +158,11 @@ int mfnested(uint8_t blockNo, uint8_t keyType, uint8_t * key, uint8_t trgBlockNo // intersection of both lists. Create the intersection: qsort(statelists[0].head.keyhead, statelists[0].len, sizeof(uint64_t), compar_int); qsort(statelists[1].head.keyhead, statelists[1].len, sizeof(uint64_t), compar_int); - + uint64_t *p5, *p6, *p7; p5 = p7 = statelists[0].head.keyhead; p6 = statelists[1].head.keyhead; + while (p5 <= statelists[0].tail.keytail && p6 <= statelists[1].tail.keytail) { if (compar_int(p5, p6) == 0) { *p7++ = *p5++; @@ -173,38 +174,58 @@ int mfnested(uint8_t blockNo, uint8_t keyType, uint8_t * key, uint8_t trgBlockNo } } statelists[0].len = p7 - statelists[0].head.keyhead; - statelists[0].tail.keytail=--p7; + statelists[0].tail.keytail = --p7; + uint32_t numOfCandidates = statelists[0].len; + if ( numOfCandidates == 0 ) goto out; + memset(resultKey, 0, 6); + uint64_t key64 = 0; + // The list may still contain several key candidates. Test each of them with mfCheckKeys - for (i = 0; i < statelists[0].len; i++) { - uint8_t keyBlock[6]; - uint64_t key64; + // uint32_t max_keys = keycnt > (USB_CMD_DATA_SIZE/6) ? (USB_CMD_DATA_SIZE/6) : keycnt; + uint8_t keyBlock[USB_CMD_DATA_SIZE] = {0x00}; + + for (i = 0; i < numOfCandidates; ++i){ crypto1_get_lfsr(statelists[0].head.slhead + i, &key64); - num_to_bytes(key64, 6, keyBlock); - key64 = 0; - if (!mfCheckKeys(statelists[0].blockNo, statelists[0].keyType, false, 1, keyBlock, &key64)) { - num_to_bytes(key64, 6, resultKey); - break; - } + num_to_bytes(key64, 6, keyBlock + i * 6); + } + + if (!mfCheckKeys(statelists[0].blockNo, statelists[0].keyType, false, numOfCandidates, keyBlock, &key64)) { + free(statelists[0].head.slhead); + free(statelists[1].head.slhead); + num_to_bytes(key64, 6, resultKey); + + PrintAndLog("UID: %08x target block:%3u key type: %c -- Found key [%012"llx"]", + uid, + (uint16_t)resp.arg[2] & 0xff, + (resp.arg[2] >> 8) ? 'B' : 'A', + key64 + ); + return -5; } +out: + PrintAndLog("UID: %08x target block:%3u key type: %c", + uid, + (uint16_t)resp.arg[2] & 0xff, + (resp.arg[2] >> 8) ? 'B' : 'A' + ); + free(statelists[0].head.slhead); - free(statelists[1].head.slhead); - return 0; + free(statelists[1].head.slhead); + return -4; } int mfCheckKeys (uint8_t blockNo, uint8_t keyType, bool clear_trace, uint8_t keycnt, uint8_t * keyBlock, uint64_t * key){ *key = 0; - - UsbCommand c = {CMD_MIFARE_CHKKEYS, {((blockNo & 0xff) | ((keyType&0xff)<<8)), clear_trace, keycnt}}; + UsbCommand c = {CMD_MIFARE_CHKKEYS, { (blockNo | (keyType<<8)), clear_trace, keycnt}}; memcpy(c.d.asBytes, keyBlock, 6 * keycnt); - clearCommandBuffer(); SendCommand(&c); UsbCommand resp; - if (!WaitForResponseTimeout(CMD_ACK,&resp,3000)) return 1; + if (!WaitForResponseTimeout(CMD_ACK,&resp, 2500)) return 1; if ((resp.arg[0] & 0xff) != 0x01) return 2; *key = bytes_to_num(resp.d.asBytes, 6); return 0; @@ -237,17 +258,17 @@ int mfEmlSetMem_xt(uint8_t *data, int blockNum, int blocksCount, int blockBtWidt // "MAGIC" CARD -int mfCSetUID(uint8_t *uid, uint8_t *atqa, uint8_t *sak, uint8_t *oldUID, bool wantWipe) { - uint8_t oldblock0[16] = {0x00}; - uint8_t block0[16] = {0x00}; +int mfCSetUID(uint8_t *uid, uint8_t *atqa, uint8_t *sak, uint8_t *oldUID, uint8_t wipecard) { - int old = mfCGetBlock(0, oldblock0, CSETBLOCK_SINGLE_OPER); - if (old == 0) { - memcpy(block0, oldblock0, 16); - PrintAndLog("old block 0: %s", sprint_hex(block0,16)); - } else { - PrintAndLog("Couldn't get old data. Will write over the last bytes of Block 0."); - } + uint8_t params = MAGIC_SINGLE; + uint8_t block0[16]; + memset(block0, 0x00, sizeof(block0)); + + int old = mfCGetBlock(0, block0, params); + if (old == 0) + PrintAndLog("old block 0: %s", sprint_hex(block0, sizeof(block0))); + else + PrintAndLog("Couldn't get old data. Will write over the last bytes of Block 0."); // fill in the new values // UID @@ -255,26 +276,30 @@ int mfCSetUID(uint8_t *uid, uint8_t *atqa, uint8_t *sak, uint8_t *oldUID, bool w // Mifare UID BCC block0[4] = block0[0]^block0[1]^block0[2]^block0[3]; // mifare classic SAK(byte 5) and ATQA(byte 6 and 7, reversed) - if (sak!=NULL) + if ( sak != NULL ) block0[5]=sak[0]; - if (atqa!=NULL) { + + if ( atqa != NULL ) { block0[6]=atqa[1]; block0[7]=atqa[0]; } PrintAndLog("new block 0: %s", sprint_hex(block0,16)); - return mfCSetBlock(0, block0, oldUID, wantWipe, CSETBLOCK_SINGLE_OPER); + + if ( wipecard ) params |= MAGIC_WIPE; + if ( oldUID == NULL) params |= MAGIC_UID; + + return mfCSetBlock(0, block0, oldUID, params); } -int mfCSetBlock(uint8_t blockNo, uint8_t *data, uint8_t *uid, bool wantWipe, uint8_t params) { +int mfCSetBlock(uint8_t blockNo, uint8_t *data, uint8_t *uid, uint8_t params) { uint8_t isOK = 0; - UsbCommand c = {CMD_MIFARE_CSETBLOCK, {wantWipe, params & (0xFE | (uid == NULL ? 0:1)), blockNo}}; + UsbCommand c = {CMD_MIFARE_CSETBLOCK, {params, blockNo, 0}}; memcpy(c.d.asBytes, data, 16); - clearCommandBuffer(); SendCommand(&c); UsbCommand resp; - if (WaitForResponseTimeout(CMD_ACK,&resp,1500)) { + if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { isOK = resp.arg[0] & 0xff; if (uid != NULL) memcpy(uid, resp.d.asBytes, 4); @@ -289,9 +314,7 @@ int mfCSetBlock(uint8_t blockNo, uint8_t *data, uint8_t *uid, bool wantWipe, uin int mfCGetBlock(uint8_t blockNo, uint8_t *data, uint8_t params) { uint8_t isOK = 0; - - UsbCommand c = {CMD_MIFARE_CGETBLOCK, {params, 0, blockNo}}; - + UsbCommand c = {CMD_MIFARE_CGETBLOCK, {params, blockNo, 0}}; clearCommandBuffer(); SendCommand(&c); UsbCommand resp; @@ -345,7 +368,7 @@ int isBlockEmpty(int blockN) { } int isBlockTrailer(int blockN) { - return ((blockN & 0x03) == 0x03); + return ((blockN & 0x03) == 0x03); } int loadTraceCard(uint8_t *tuid) { @@ -440,20 +463,22 @@ void mf_crypto1_decrypt(struct Crypto1State *pcs, uint8_t *data, int len, bool i for (i = 0; i < len; i++) data[i] = crypto1_byte(pcs, 0x00, isEncrypted) ^ data[i]; } else { - bt = 0; - for (i = 0; i < 4; i++) - bt |= (crypto1_bit(pcs, 0, isEncrypted) ^ BIT(data[0], i)) << i; - + bt = 0; + bt |= (crypto1_bit(pcs, 0, isEncrypted) ^ BIT(data[0], 0)) << 0; + bt |= (crypto1_bit(pcs, 0, isEncrypted) ^ BIT(data[0], 1)) << 1; + bt |= (crypto1_bit(pcs, 0, isEncrypted) ^ BIT(data[0], 2)) << 2; + bt |= (crypto1_bit(pcs, 0, isEncrypted) ^ BIT(data[0], 3)) << 3; data[0] = bt; } return; } - int mfTraceDecode(uint8_t *data_src, int len, bool wantSaveToEmlFile) { + uint8_t data[64]; if (traceState == TRACE_ERROR) return 1; + if (len > 64) { traceState = TRACE_ERROR; return 1; @@ -630,3 +655,28 @@ int mfTraceDecode(uint8_t *data_src, int len, bool wantSaveToEmlFile) { return 0; } + +int tryDecryptWord(uint32_t nt, uint32_t ar_enc, uint32_t at_enc, uint8_t *data, int len){ + /* + uint32_t nt; // tag challenge + uint32_t nr_enc; // encrypted reader challenge + uint32_t ar_enc; // encrypted reader response + uint32_t at_enc; // encrypted tag response + */ + struct Crypto1State *pcs = NULL; + + ks2 = ar_enc ^ prng_successor(nt, 64); + ks3 = at_enc ^ prng_successor(nt, 96); + + PrintAndLog("Decrypting data with:"); + PrintAndLog(" nt: %08x",nt); + PrintAndLog(" ar_enc: %08x",ar_enc); + PrintAndLog(" at_enc: %08x",at_enc); + PrintAndLog("\nEncrypted data: [%s]", sprint_hex(data,len) ); + + pcs = lfsr_recovery64(ks2, ks3); + mf_crypto1_decrypt(pcs, data, len, FALSE); + PrintAndLog("Decrypted data: [%s]", sprint_hex(data,len) ); + crypto1_destroy(pcs); + return 0; +}