diff --git a/.gitignore b/.gitignore index a7cc8803..9c3a607c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ # .gitignore # don't push these files to the repository +*.DS_Store .history *.log *.eml @@ -11,6 +12,7 @@ *.s19 *.map *.bin +!client/loclass/iclass_dump.bin !client/hardnested/*.bin *.dll *.moc.cpp diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index a8273e5e..0b556269 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -292,12 +292,12 @@ static void UartInit(uint8_t *data, uint8_t *parity) // use parameter non_real_time to provide a timestamp. Set to 0 if the decoder should measure real time static RAMFUNC bool MillerDecoding(uint8_t bit, uint32_t non_real_time) { - + //Dbprintf("Miller decoding now!"); Uart.fourBits = (Uart.fourBits << 8) | bit; - if (Uart.state == STATE_UNSYNCD) { // not yet synced + if (Uart.state == STATE_UNSYNCD) { // not yet synced - Uart.syncBit = 9999; // not set + Uart.syncBit = 9999; // not set // The start bit is one ore more Sequence Y followed by a Sequence Z (... 11111111 00x11111). We need to distinguish from // Sequence X followed by Sequence Y followed by Sequence Z (111100x1 11111111 00x11111) // we therefore look for a ...xx11111111111100x11111xxxxxx... pattern @@ -463,6 +463,7 @@ static void DemodInit(uint8_t *data, uint8_t *parity) } // use parameter non_real_time to provide a timestamp. Set to 0 if the decoder should measure real time +// Input *bit* to the Manchester decoding is one byte (8 bits) which are read from the RHR. static RAMFUNC int ManchesterDecoding(uint8_t bit, uint16_t offset, uint32_t non_real_time) { @@ -470,7 +471,7 @@ static RAMFUNC int ManchesterDecoding(uint8_t bit, uint16_t offset, uint32_t non if (Demod.state == DEMOD_UNSYNCD) { - if (Demod.highCnt < 2) { // wait for a stable unmodulated signal + if (Demod.highCnt < 2) { // wait for a stable unmodulated signal if (Demod.twoBits == 0x0000) { Demod.highCnt++; } else { @@ -494,14 +495,16 @@ static RAMFUNC int ManchesterDecoding(uint8_t bit, uint16_t offset, uint32_t non } } - } else { - - if (IsManchesterModulationNibble1(Demod.twoBits >> Demod.syncBit)) { // modulation in first half - if (IsManchesterModulationNibble2(Demod.twoBits >> Demod.syncBit)) { // ... and in second half = collision + } else { //Demod.state == DEMOD_MANCHESTER_DATA + //Determine the modulation details for each nibble (4 bits) separately + if (IsManchesterModulationNibble1(Demod.twoBits >> Demod.syncBit)) { // if modulation in first half + if (IsManchesterModulationNibble2(Demod.twoBits >> Demod.syncBit)) { // ... and in second half, that specifies collision + //Save the collision position and treat as Sequence D if (!Demod.collisionPos) { Demod.collisionPos = (Demod.len << 3) + Demod.bitCount; } - } // modulation in first half only - Sequence D = 1 + } + // modulation in first half only - Sequence D = 1. Demod.bitCount++; Demod.shiftReg = (Demod.shiftReg >> 1) | 0x100; // in both cases, add a 1 to the shiftreg if(Demod.bitCount == 9) { // if we decoded a full byte (including parity) @@ -516,7 +519,7 @@ static RAMFUNC int ManchesterDecoding(uint8_t bit, uint16_t offset, uint32_t non } } Demod.endTime = Demod.startTime + 8*(9*Demod.len + Demod.bitCount + 1) - 4; - } else { // no modulation in first half + } else { // if no modulation in first half if (IsManchesterModulationNibble2(Demod.twoBits >> Demod.syncBit)) { // and modulation in second half = Sequence E = 0 Demod.bitCount++; Demod.shiftReg = (Demod.shiftReg >> 1); // add a 0 to the shiftreg @@ -532,7 +535,7 @@ static RAMFUNC int ManchesterDecoding(uint8_t bit, uint16_t offset, uint32_t non } } Demod.endTime = Demod.startTime + 8*(9*Demod.len + Demod.bitCount + 1); - } else { // no modulation in both halves - End of communication + } else { // no modulation in both halves - End of communication if(Demod.bitCount > 0) { // there are some remaining data bits Demod.shiftReg >>= (9 - Demod.bitCount); // right align the decoded bits Demod.output[Demod.len++] = Demod.shiftReg & 0xff; // and add them to the output @@ -1289,6 +1292,8 @@ static void TransmitFor14443a(const uint8_t *cmd, uint16_t len, uint32_t *timing // clear TXRDY AT91C_BASE_SSC->SSC_THR = SEC_Y; + //Dbprintf("Sending bytes to the PICC."); + uint16_t c = 0; for(;;) { if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { @@ -1586,22 +1591,32 @@ static int GetIso14443aAnswerFromTag(uint8_t *receivedResponse, uint8_t *receive LED_D_ON(); FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_READER_LISTEN); - // Now get the answer from the card + /* Now, get the answer from the card. + Registers used on the AT91: + SSC_RHR = Receive Holding Register (8 bits) + SSC_SR = Status Register (contains RXRDY, which is one bit. 0 is RHR is empty, or 1 if RHR has 8 bits of data in it) + */ + DemodInit(receivedResponse, receivedResponsePar); - // clear RXRDY: + + // clear RXRDY by reading the contents of RHR. uint8_t b = (uint8_t)AT91C_BASE_SSC->SSC_RHR; c = 0; for(;;) { - WDT_HIT(); + WDT_HIT(); //Watchdog Timer - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - b = (uint8_t)AT91C_BASE_SSC->SSC_RHR; + if(AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY) { + b = (uint8_t)AT91C_BASE_SSC->SSC_RHR; //read in 1 byte of data from the RHR + + //Perform the manchester decoding on the 1 byte just received. if(ManchesterDecoding(b, offset, 0)) { NextTransferTime = MAX(NextTransferTime, Demod.endTime - (DELAY_AIR2ARM_AS_READER + DELAY_ARM2AIR_AS_READER)/16 + FRAME_DELAY_TIME_PICC_TO_PCD); + //Dbprintf("Finished decoding (Manchester). Value of c=%d. Cycle count (for one bit) = %d", c, cycle_count); return true; } else if (c++ > iso14a_timeout && Demod.state == DEMOD_UNSYNCD) { + //we reach here only if we time out (i.e. receiving the data from the PICC takes too long) return false; } } diff --git a/client/lualibs/read14a.lua b/client/lualibs/read14a.lua index 60fc0e68..c1d10956 100644 --- a/client/lualibs/read14a.lua +++ b/client/lualibs/read14a.lua @@ -91,10 +91,10 @@ local function sendToDevice(command, ignoreresponse) return response,nil end --- This function does a connect and retrieves som einfo +-- This function does a connect and retrieves some info -- @param dont_disconnect - if true, does not disable the field -- @param no_rats - if true, skips ISO14443-4 select (RATS) --- @return if successfull: an table containing card info +-- @return if successfull: a table containing card info -- @return if unsuccessfull : nil, error local function read14443a(dont_disconnect, no_rats) local command, result, info, err, data diff --git a/client/scripts/mifarePlus.lua b/client/scripts/mifarePlus.lua new file mode 100644 index 00000000..85815093 --- /dev/null +++ b/client/scripts/mifarePlus.lua @@ -0,0 +1,310 @@ +local cmds = require('commands') +local lib14a = require('read14a') + +SIXTEEN_BYTES_ZEROS = "00000000000000000000000000000000" + +GETVERS_INIT = "0360" -- Begins the GetVersion command +GETVERS_CONT = "03AF" -- Continues the GetVersion command +POWEROFF = "OFF" +WRITEPERSO = "03A8" +COMMITPERSO = "03AA" +AUTH_FIRST = "0370" +AUTH_CONT = "0372" +AUTH_NONFIRST = "0376" +PREPAREPC = "03F0" +PROXIMITYCHECK = "03F2" +VERIFYPC = "03FD" +READPLAINNOMACUNMACED = "0336" + +--- +-- This is only meant to be used when errors occur +function oops(err) + print("ERROR: ",err) +end + +--- +-- Used to send raw data to the firmware to subsequently forward the data to the card. +function sendRaw(rawdata, crc, power) + print((": %s"):format(rawdata)) + + local flags = lib14a.ISO14A_COMMAND.ISO14A_RAW + if crc then + flags = flags + lib14a.ISO14A_COMMAND.ISO14A_APPEND_CRC + end + if power then + flags = flags + lib14a.ISO14A_COMMAND.ISO14A_NO_DISCONNECT + end + + local command = Command:new{cmd = cmds.CMD_READER_ISO_14443a, + arg1 = flags, -- Send raw + arg2 = string.len(rawdata) / 2, -- arg2 contains the length, which is half the length of the ASCII-string rawdata + data = rawdata} + local ignore_response = false + local result, err = lib14a.sendToDevice(command, ignore_response) + if result then + --unpack the first 4 parts of the result as longs, and the last as an extremely long string to later be cut down based on arg1, the number of bytes returned + local count,cmd,arg1,arg2,arg3,data = bin.unpack('LLLLH512',result) + returned_bytes = string.sub(data, 1, arg1 * 2) + print((": %s"):format(returned_bytes)) -- need to multiply by 2 because the hex digits are actually two bytes when they are strings + return returned_bytes + else + err = "Error sending the card raw data." + oops(err) + end +end + +function writePerso() + -- Used to write any data, including the keys (Key A and Key B), for all the sectors. + -- writePerso() command parameters: + -- 1 byte - 0xA8 - Command Code + -- 2 bytes - Address of the first block or key to be written to (40 blocks are numbered from 0x0000 to 0x00FF) + -- X bytes - The data bytes to be written, starting from the first block. Amount of data sent can be from 16 to 240 bytes in 16 byte increments. This allows + -- up to 15 blocks to be written at once. + -- response from PICC: + -- 0x90 - OK + -- 0x09 - targeted block is invalid for writes, i.e. block 0, which contains manufacturer data + -- 0x0B - command invalid + -- 0x0C - unexpected command length + + + -- First, set all the data in the card to zeros. The keys, stored in the sector trailer block, are also set to zeros. + -- The only block which cannot be explicitly set is block 0x0000, the manufacturer block. + print("Setting values of normal blocks") + cardsize = 4 --need to set to 4 for 4k or 2 for 2k + if(cardsize == 4) then + numblocks = 255 + elseif(cardsize == 2) then + numblocks = 127 + else + oops("Invalid card size") + end + + for i=1,numblocks,1 do --skip block 0 + --convert the number to hex with leading zeros, then use it as the block number in writeBlock() + blocknum = string.format("%04x", i) + writeBlock(blocknum, SIXTEEN_BYTES_ZEROS) + end + print("Finished setting values of normal blocks") + + print("Setting AES Sector keys") + -- Next, write to the AES sector keys + for i=0,39 do --for each sector number + local keyA_block = "40" .. string.format("%02x", i * 2) + local keyB_block = "40" .. string.format("%02x", (i * 2) + 1) + --Can also calculate the keys fancily to make them unique, if desired + keyA = SIXTEEN_BYTES_ZEROS + keyB = SIXTEEN_BYTES_ZEROS + writeBlock(keyA_block, keyA) + writeBlock(keyB_block, keyB) + end + print("Finished setting AES Sector keys") + + print("Setting misc keys which haven't been set yet.") + --CardMasterKey + blocknum = "9000" + writeBlock(blocknum, SIXTEEN_BYTES_ZEROS) + --CardConfigurationKey + blocknum = "9001" + writeBlock(blocknum, SIXTEEN_BYTES_ZEROS) + --L3SwitchKey + blocknum = "9003" + writeBlock(blocknum, SIXTEEN_BYTES_ZEROS) + --SL1CardAuthKey + blocknum = "9004" + writeBlock(blocknum, SIXTEEN_BYTES_ZEROS) + --L3SectorSwitchKey + blocknum = "9006" + writeBlock(blocknum, SIXTEEN_BYTES_ZEROS) + --L1L3MixSectorSwitchKey + blocknum = "9007" + writeBlock(blocknum, SIXTEEN_BYTES_ZEROS) + --VC Keys + --VCProximityKey + blocknum = "A001" + writeBlock(blocknum, SIXTEEN_BYTES_ZEROS) + --VCSelectENCKey + blocknum = "A080" + writeBlock(blocknum, SIXTEEN_BYTES_ZEROS) + --VCSelectMACKey + blocknum = "A081" + writeBlock(blocknum, SIXTEEN_BYTES_ZEROS) + --TransactionMACKey1 + blocknum = "C000" + writeBlock(blocknum, SIXTEEN_BYTES_ZEROS) + --TransactionMACConfKey1 + blocknum = "C001" + writeBlock(blocknum, SIXTEEN_BYTES_ZEROS) + print("Finished setting misc keys.") + + print("WritePerso finished! Card is ready to move into new security level.") +end + +function writeBlock(blocknum, data) + -- Method writes 16 bytes of the string sent (data) to the specified block number + -- The block numbers sent to the card need to be in little endian format (i.e. block 0x0001 is sent as 0x1000) + blocknum_little_endian = string.sub(blocknum, 3, 4) .. string.sub(blocknum, 1, 2) + commandString = WRITEPERSO .. blocknum_little_endian .. data --Write 16 bytes (32 hex chars). + response = sendRaw(commandString, true, true) --0x90 is returned upon success + if string.sub(response, 3, 4) ~= "90" then + oops(("error occurred while trying to write to block %s"):format(blocknum)) + end +end + +function authenticateAES() + -- Used to try to authenticate with the AES keys we programmed into the card, to ensure the authentication works correctly. + commandString = AUTH_FIRST + commandString = commandString .. "" +end + +function getVersion() + sendRaw(GETVERS_INIT, true, true) + sendRaw(GETVERS_CONT, true, true) + sendRaw(GETVERS_CONT, true, true) +end + +function commitPerso(SL) + --pass SL as "01" to move to SL1 or "03" to move to SL3. + commandString = COMMITPERSO .. SL + response = sendRaw(commandString, true, true) --0x90 is returned upon success + if string.sub(response, 3, 4) ~= "90" then + oops("error occurred while trying to switch security level") + end +end + +function calculateMAC(MAC_input) + -- Pad the input if it is not a multiple of 16 bytes (32 nibbles). + if(string.len(MAC_input) % 32 ~= 0) then + MAC_input = MAC_input .. "80" + end + while(string.len(MAC_input) % 32 ~= 0) do + MAC_input = MAC_input .. "0" + end + print("Padded MAC Input = " .. MAC_input .. ", length (bytes) = " .. string.len(MAC_input) / 2) + + --The MAC would actually be calculated here, and the output stored in raw_output + raw_output = "00010203040506070001020304050607" -- Dummy filler for now of 16-byte output. To be filled with actual MAC for testing purposes. + + -- The final 8-byte MAC output is a concatenation of every 2nd byte starting from the second MSB. + final_output = "" + j = 3 + for i = 1,8 do + final_output = final_output .. string.sub(RndR, j, j + 1) .. string.sub(RndC, j, j + 1) + j = j + 4 + end + return final_output +end + +function proximityCheck() + --PreparePC-- + commandString = PREPAREPC + response = sendRaw(commandString, true, true) + OPT = string.sub(response, 5, 6) + if(tonumber(OPT) == 1) then + pps_present = true + else + pps_present = false + end + pubRespTime = string.sub(response, 7, 10) + if(pps_present == true) then + pps = string.sub(response, 11, 12) + else + pps = nil + end + print("OPT = " .. OPT .. " pubRespTime = " .. pubRespTime .. " pps = " .. pps) + + --PC-- + RndC = "0001020304050607" --Random Challenge + num_rounds = 8 --Needs to be 1, 2, 4, or 8 + part_len = 8 / num_rounds + j = 1 + RndR = "" + for i = 1,num_rounds do + pRndC = "" + for q = 1,(part_len*2) do + pRndC = pRndC .. string.sub(RndC,j,j) + j = j + 1 + end + commandString = PROXIMITYCHECK .. "0" .. tostring(part_len) .. pRndC + pRndR = string.sub(sendRaw(commandString, true, true), 3, 3+part_len) + RndR = RndR .. pRndR + end + print("RndC = " .. RndC .. " RndR = " .. RndR) + + --VerifyPC-- + MAC_input = "FD" .. OPT .. pubRespTime + if(pps_present == true) then + MAC_input = MAC_input .. pps + end + rnum_concat = "" + rnum_concat = RndR .. RndC --temporary (only works for when a single random challenge (8 bytes) is sent) + -- j = 1 + -- for i = 1,8 do + -- rnum_concat = rnum_concat .. string.sub(RndR, j, j + 1) .. string.sub(RndC, j, j + 1) + -- j = j + 2 + -- end + MAC_input = MAC_input .. rnum_concat + print("Concatenation of random numbers = " .. rnum_concat) + print("Final PCD concatenation before input into MAC function = " .. MAC_input) + MAC_tag = calculateMAC(MAC_input) + print("8-byte PCD MAC_tag (placeholder - currently incorrect) = " .. MAC_tag) + commandString = VERIFYPC .. MAC_tag + response = sendRaw(commandString, true, true) + print(response) + PICC_MAC = string.sub(response, 5, 20) + print("8-byte MAC returned by PICC = " .. PICC_MAC) + MAC_input = "90" .. string.sub(MAC_input, 3) + print("Final PICC concatenation before input into MAC function = " .. MAC_input) + MAC_tag = calculateMAC(MAC_input) + print("8-byte PICC MAC_tag (placeholder - currently incorrect) = " .. MAC_tag) + +end + +--- +-- The main entry point +function main(args) + -- Initialize the card using the already-present read14a library + info,err = lib14a.read14443a(true, false) + --Perform PPS (Protocol and Parameter Selection) check to finish the ISO 14443-4 protocol. + response = sendRaw("e050", true, true) + if(response == nil) then + err = "No response from RATS" + end + response = sendRaw("D01100", true, true) + if(response == nil) then + err = "No response from PPS check" + end + if err then + oops(err) + else + print(("Connected to card with a UID of %s."):format(info.uid)) + end + + + -- Now, the card is initialized and we can do more interesting things. + + --writePerso() + --commitPerso("03") --move to SL3 + --getVersion() + proximityCheck() + + --commandString = VERIFYPC .. "186EFDE8DDC7D30B" + -- MAC = f5180d6e 40fdeae8 e9dd6ac7 bcd3350b + -- response = sendRaw(commandString, true, true) + + -- attempt to read VCProximityKey at block A001 + -- commandString = READPLAINNOMACUNMACED .. "01A0" .. "01" + -- response = sendRaw(commandString, true, true) + + -- authenticate with CardConfigurationKey + -- commandString = AUTH_FIRST .. "0190" .. "00" + -- response = sendRaw(commandString, true, true) + + -- Power off the Proxmark + sendRaw(POWEROFF, false, false) + + + +end + + +main(args) -- Call the main function