diff --git a/client/cmdmain.c b/client/cmdmain.c index eba34b2ca..4b6013d80 100644 --- a/client/cmdmain.c +++ b/client/cmdmain.c @@ -32,6 +32,7 @@ #include "cmdflashmem.h" // rdv40 flashmem commands #include "cmdsmartcard.h" // rdv40 smart card ISO7816 commands #include "cmdusart.h" // rdv40 FPC USART commands +#include "cmdwiegand.h" // wiegand commands #include "ui.h" #include "util_posix.h" @@ -98,11 +99,12 @@ static command_t CommandTable[] = { {"mem", CmdFlashMem, IfPm3Flash, "{ Flash Memory manipulation... }"}, {"msleep", CmdMsleep, AlwaysAvailable, "Add a pause in milliseconds"}, {"rem", CmdRem, AlwaysAvailable, "Add text to row in log file"}, - {"reveng", CmdRev, AlwaysAvailable, "{ Crc calculations from the RevEng software }"}, + {"reveng", CmdRev, AlwaysAvailable, "{ CRC calculations from RevEng software }"}, {"sc", CmdSmartcard, IfPm3Smartcard, "{ Smart card ISO7816 commands... }"}, {"script", CmdScript, AlwaysAvailable, "{ Scripting commands }"}, {"trace", CmdTrace, AlwaysAvailable, "{ Trace manipulation... }"}, {"usart", CmdUsart, IfPm3FpcUsartFromUsb, "{ USART commands... }"}, + {"wiegand", CmdWiegand, AlwaysAvailable, "{ Wiegand format manipulation... }"}, {"quit", CmdQuit, AlwaysAvailable, ""}, {"exit", CmdQuit, AlwaysAvailable, "Exit program"}, {NULL, NULL, NULL, NULL} diff --git a/client/cmdwiegand.c b/client/cmdwiegand.c new file mode 100644 index 000000000..0b567d514 --- /dev/null +++ b/client/cmdwiegand.c @@ -0,0 +1,205 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2018 iceman +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// Trace commands +//----------------------------------------------------------------------------- +#include "cmdwiegand.h" + +#include +#include +#include +#include "cmdparser.h" // command_t +#include "comms.h" +#include "pm3_cmd.h" +#include "protocols.h" +#include "parity.h" // oddparity +#include "cmdhflist.h" // annotations +#include "wiegand_formats.h" +#include "wiegand_formatutils.h" +#include "util.h" + +static int CmdHelp(const char *Cmd); + +static int usage_wiegand_list() { + PrintAndLogEx(NORMAL, "List available wiegand formats"); + return PM3_SUCCESS; +} +static int usage_wiegand_encode() { + PrintAndLogEx(NORMAL, "Encode wiegand formatted number to raw hex"); + PrintAndLogEx(NORMAL, "Usage: wiegand encode [w ] [ ] {...}"); + PrintAndLogEx(NORMAL, "Options:"); + PrintAndLogEx(NORMAL, " w see `wiegand list` for available formats"); + PrintAndLogEx(NORMAL, " c card number"); + PrintAndLogEx(NORMAL, " f facility code"); + PrintAndLogEx(NORMAL, " i issue Level"); + PrintAndLogEx(NORMAL, " o OEM code"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "samples:"); + PrintAndLogEx(NORMAL, " wiegand encode w H10301 f 101 c 1337"); + return PM3_SUCCESS; +} +static int usage_wiegand_decode() { + PrintAndLogEx(NORMAL, "Decode raw hex to wiegand format"); + PrintAndLogEx(NORMAL, "Usage: wiegand decode [id]

"); + PrintAndLogEx(NORMAL, " p ignore invalid parity"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Samples:"); + PrintAndLogEx(NORMAL, " wiegand decode 2006f623ae"); + return PM3_SUCCESS; +} + +void PrintTagId(wiegand_message_t *packed){ + if (packed->Top != 0) { + PrintAndLogEx(SUCCESS, "Card ID: %X%08X%08X", + (uint32_t)packed->Top, + (uint32_t)packed->Mid, + (uint32_t)packed->Bot) + ; + } else { + PrintAndLogEx(SUCCESS, "Card ID: %X%08X", + (uint32_t)packed->Mid, + (uint32_t)packed->Bot) + ; + } +} + +int CmdWiegandList(const char *Cmd) { + bool errors = false; + char cmdp = 0; + while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { + switch (tolower(param_getchar(Cmd, cmdp))) { + case 'h': + return usage_wiegand_list(); + default: + PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); + errors = true; + break; + } + } + HIDListFormats(); + return PM3_SUCCESS; +} + +int CmdWiegandEncode(const char *Cmd) { + + int format_idx = -1; + char format[16] = {0}; + + wiegand_card_t data; + memset(&data, 0, sizeof(wiegand_card_t)); + + bool errors = false; + char cmdp = 0; + while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { + switch (tolower(param_getchar(Cmd, cmdp))) { + case 'h': + return usage_wiegand_encode(); + case 'w': + param_getstr(Cmd, cmdp + 1, format, sizeof(format)); + format_idx = HIDFindCardFormat(format); + if (format_idx == -1) { + PrintAndLogEx(WARNING, "Unknown format: %s", format); + errors = true; + } + cmdp += 2; + break; + case 'i': + data.IssueLevel = param_get32ex(Cmd, cmdp + 1, 0, 10); + cmdp += 2; + break; + case 'f': + data.FacilityCode = param_get32ex(Cmd, cmdp + 1, 0, 10); + cmdp += 2; + break; + case 'c': + data.CardNumber = param_get64ex(Cmd, cmdp + 1, 0, 10); + cmdp += 2; + break; + case 'o': + data.OEM = param_get32ex(Cmd, cmdp + 1, 0, 10); + cmdp += 2; + break; + default: + PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); + errors = true; + break; + } + } + if (errors || cmdp == 0) return usage_wiegand_encode(); + + wiegand_message_t packed; + memset(&packed, 0, sizeof(wiegand_message_t)); + + if (HIDPack(format_idx, &data, &packed) == false) { + PrintAndLogEx(WARNING, "The card data could not be encoded in the selected format."); + return PM3_ESOFT; + } + + PrintTagId(&packed); + return PM3_SUCCESS; +} + +int CmdWiegandDecode(const char *Cmd) { + + uint32_t top = 0, mid = 0, bot = 0; + bool ignore_parity = false, gothex = false; + bool errors = false; + char cmdp = 0; + while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { + uint32_t strlen = param_getlength(Cmd, cmdp); + strlen++; // null termin + if ( strlen > 2 ) { + char *s = calloc(strlen, sizeof(uint8_t)); + param_getstr(Cmd, cmdp, s, strlen); + hexstring_to_u96(&top, &mid, &bot, s); + free(s); + gothex = true; + cmdp++; + continue; + } + switch (tolower(param_getchar(Cmd, cmdp))) { + case 'h': + return usage_wiegand_decode(); + case 'p': + ignore_parity = true; + cmdp++; + break; + default: + PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); + errors = true; + break; + } + } + if (gothex == false) + errors = true; + + if (errors || cmdp < 1) return usage_wiegand_decode(); + + wiegand_message_t packed = initialize_message_object(top, mid, bot); + + HIDTryUnpack(&packed, ignore_parity); + return PM3_SUCCESS; +} + +static command_t CommandTable[] = { + {"help", CmdHelp, AlwaysAvailable, "This help"}, + {"list", CmdWiegandList, AlwaysAvailable, "List available wiegand formats"}, + {"encode", CmdWiegandEncode, AlwaysAvailable, "Convert "}, + {"decode", CmdWiegandDecode, AlwaysAvailable, "Convert raw hex to wiegand format"}, + {NULL, NULL, NULL, NULL} +}; + +static int CmdHelp(const char *Cmd) { + (void)Cmd; // Cmd is not used so far + CmdsHelp(CommandTable); + return PM3_SUCCESS; +} + +int CmdWiegand(const char *Cmd) { + clearCommandBuffer(); + return CmdsParse(CommandTable, Cmd); +} diff --git a/client/cmdwiegand.h b/client/cmdwiegand.h new file mode 100644 index 000000000..2c6e8fb40 --- /dev/null +++ b/client/cmdwiegand.h @@ -0,0 +1,20 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2019 iceman +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// Trace commands +//----------------------------------------------------------------------------- + +#ifndef CMDWIEGAND_H__ +#define CMDWIEGAND_H__ + +#include "common.h" + +int CmdWiegand(const char *Cmd); +int CmdWiegandList(const char *Cmd); +int CmdWiegandEncode(const char *Cmd); +int CmdWiegandDecode(const char *Cmd); +#endif diff --git a/client/util.c b/client/util.c index 55b114fdb..f74605562 100644 --- a/client/util.c +++ b/client/util.c @@ -878,3 +878,20 @@ char *strmcopy(const char *buf) { } return str; } + +/** + * Converts a hex string to component "hi2", "hi" and "lo" 32-bit integers, one nibble + * at a time. + * + * Returns the number of nibbles (4 bits) entered. + */ +int hexstring_to_u96(uint32_t* hi2, uint32_t* hi, uint32_t* lo, const char* str) { + int n = 0, i = 0; + + while (sscanf(&str[i++], "%1x", &n ) == 1) { + *hi2 = (*hi2 << 4) | (*hi >> 28); + *hi = (*hi << 4) | (*lo >> 28); + *lo = (*lo << 4) | (n & 0xf); + } + return i - 1; +} diff --git a/client/util.h b/client/util.h index 7cfea0f8b..df69493d2 100644 --- a/client/util.h +++ b/client/util.h @@ -98,4 +98,5 @@ void clean_ascii(unsigned char *buf, size_t len); void strcleanrn(char *buf, size_t len); void strcreplace(char *buf, size_t len, char from, char to); char *strmcopy(const char *buf); +int hexstring_to_u96(uint32_t* hi2, uint32_t* hi, uint32_t* lo, const char* str); #endif diff --git a/client/wiegand_formats.c b/client/wiegand_formats.c new file mode 100644 index 000000000..7228abd82 --- /dev/null +++ b/client/wiegand_formats.c @@ -0,0 +1,715 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2018 grauerfuchs +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// HID card format packing/unpacking routines +//----------------------------------------------------------------------------- +#include "wiegand_formats.h" + +bool Pack_H10301(wiegand_card_t* card, wiegand_message_t* packed){ + memset(packed, 0, sizeof(wiegand_message_t)); + + if (card->FacilityCode > 0xFF) return false; // Can't encode FC. + if (card->CardNumber > 0xFFFF) return false; // Can't encode CN. + if (card->IssueLevel > 0) return false; // Not used in this format + if (card->OEM > 0) return false; // Not used in this format + + packed->Length = 26; // Set number of bits + packed->Bot |= (card->CardNumber & 0xFFFF) << 1; + packed->Bot |= (card->FacilityCode & 0xFF) << 17; + packed->Bot |= oddparity32((packed->Bot >> 1) & 0xFFF) & 1; + packed->Bot |= (evenparity32((packed->Bot >> 13) & 0xFFF) & 1) << 25; + return add_HID_header(packed); +} + +bool Unpack_H10301(wiegand_message_t* packed, wiegand_card_t* card){ + memset(card, 0, sizeof(wiegand_card_t)); + if (packed->Length != 26) return false; // Wrong length? Stop here. + + card->CardNumber = (packed->Bot >> 1) & 0xFFFF; + card->FacilityCode = (packed->Bot >> 17) & 0xFF; + card->ParityValid = + (oddparity32((packed->Bot >> 1) & 0xFFF) == (packed->Bot & 1)) && + ((evenparity32((packed->Bot >> 13) & 0xFFF) & 1) == ((packed->Bot >> 25) & 1)); + return true; +} + +bool Pack_Tecom27(wiegand_card_t* card, wiegand_message_t* packed){ + memset(packed, 0, sizeof(wiegand_message_t)); + + if (card->FacilityCode > 0x7FF) return false; // Can't encode FC. + if (card->CardNumber > 0xFFFF) return false; // Can't encode CN. + if (card->IssueLevel > 0) return false; // Not used in this format + if (card->OEM > 0) return false; // Not used in this format + + packed->Length = 27; + set_nonlinear_field(packed, card->FacilityCode, 10, (uint8_t[]){15, 19, 24, 23, 22, 18, 6, 10, 14, 3, 2}); + set_nonlinear_field(packed, card->CardNumber, 16, (uint8_t[]){0, 1, 13, 12, 9, 26, 20, 16, 17, 21, 25, 7, 8, 11, 4, 5}); + return add_HID_header(packed); +} + +bool Unpack_Tecom27(wiegand_message_t* packed, wiegand_card_t* card){ + memset(card, 0, sizeof(wiegand_card_t)); + + if (packed->Length != 27) return false; // Wrong length? Stop here. + + card->CardNumber = get_nonlinear_field(packed, 16, (uint8_t[]){0, 1, 13, 12, 9, 26, 20, 16, 17, 21, 25, 7, 8, 11, 4, 5}); + card->FacilityCode = get_nonlinear_field(packed, 10, (uint8_t[]){15, 19, 24, 23, 22, 18, 6, 10, 14, 3, 2}); + return true; +} + +bool Pack_2804W(wiegand_card_t* card, wiegand_message_t* packed){ + memset(packed, 0, sizeof(wiegand_message_t)); + + if (card->FacilityCode > 0x0FF) return false; // Can't encode FC. + if (card->CardNumber > 0x7FFF) return false; // Can't encode CN. + if (card->IssueLevel > 0) return false; // Not used in this format + if (card->OEM > 0) return false; // Not used in this format + + packed->Length = 28; + set_linear_field(packed, card->FacilityCode, 4, 8); + set_linear_field(packed, card->CardNumber, 12, 15); + set_bit_by_position(packed, + oddparity32(get_nonlinear_field(packed, 16, (uint8_t[]){4, 5, 7, 8, 10, 11, 13, 14, 16, 17, 19, 20, 22, 23, 25, 26})) + , 2); + set_bit_by_position(packed, + evenparity32(get_linear_field(packed, 1, 13)) + , 0); + set_bit_by_position(packed, + oddparity32(get_linear_field(packed, 0, 27)) + , 27); + return add_HID_header(packed); +} + +bool Unpack_2804W(wiegand_message_t* packed, wiegand_card_t* card){ + memset(card, 0, sizeof(wiegand_card_t)); + + if (packed->Length != 28) return false; // Wrong length? Stop here. + + card->FacilityCode = get_linear_field(packed, 4, 8); + card->CardNumber = get_linear_field(packed, 12, 15); + card->ParityValid = + (get_bit_by_position(packed, 0) == evenparity32(get_linear_field(packed, 1, 13))) && + (get_bit_by_position(packed, 2) == oddparity32(get_nonlinear_field(packed, 16, (uint8_t[]){4, 5, 7, 8, 10, 11, 13, 14, 16, 17, 19, 20, 22, 23, 25, 26}))) && + (get_bit_by_position(packed, 27) == oddparity32(get_linear_field(packed, 0, 27))); + return true; +} + +bool Pack_ATSW30(wiegand_card_t* card, wiegand_message_t* packed){ + memset(packed, 0, sizeof(wiegand_message_t)); + + if (card->FacilityCode > 0xFFF) return false; // Can't encode FC. + if (card->CardNumber > 0xFFFF) return false; // Can't encode CN. + if (card->IssueLevel > 0) return false; // Not used in this format + if (card->OEM > 0) return false; // Not used in this format + + packed->Length = 30; + set_linear_field(packed, card->FacilityCode, 1, 12); + set_linear_field(packed, card->CardNumber, 13, 16); + set_bit_by_position(packed, + evenparity32(get_linear_field(packed, 1, 12)) + , 0); + set_bit_by_position(packed, + oddparity32(get_linear_field(packed, 13, 16)) + , 29); + return add_HID_header(packed); +} + +bool Unpack_ATSW30(wiegand_message_t* packed, wiegand_card_t* card){ + memset(card, 0, sizeof(wiegand_card_t)); + + if (packed->Length != 30) return false; // Wrong length? Stop here. + + card->FacilityCode = get_linear_field(packed, 1, 12); + card->CardNumber = get_linear_field(packed, 13, 16); + card->ParityValid = + (get_bit_by_position(packed, 0) == evenparity32(get_linear_field(packed, 1, 12))) && + (get_bit_by_position(packed, 29) == oddparity32(get_linear_field(packed, 13, 16))); + return true; +} + +bool Pack_ADT31(wiegand_card_t* card, wiegand_message_t* packed){ + memset(packed, 0, sizeof(wiegand_message_t)); + + if (card->FacilityCode > 0x0F) return false; // Can't encode FC. + if (card->CardNumber > 0x7FFFFF) return false; // Can't encode CN. + if (card->IssueLevel > 0) return false; // Not used in this format + if (card->OEM > 0) return false; // Not used in this format + + packed->Length = 31; + set_linear_field(packed, card->FacilityCode, 1, 4); + set_linear_field(packed, card->CardNumber, 5, 23); + // Parity not known, but 4 bits are unused. + return add_HID_header(packed); +} + +bool Unpack_ADT31(wiegand_message_t* packed, wiegand_card_t* card){ + memset(card, 0, sizeof(wiegand_card_t)); + + if (packed->Length != 31) return false; // Wrong length? Stop here. + card->FacilityCode = get_linear_field(packed, 1, 4); + card->CardNumber = get_linear_field(packed, 5, 23); + return true; +} + +bool Pack_Kastle(wiegand_card_t* card, wiegand_message_t* packed){ + memset(packed, 0, sizeof(wiegand_message_t)); + + if (card->FacilityCode > 0x00FF) return false; // Can't encode FC. + if (card->CardNumber > 0x0000FFFF) return false; // Can't encode CN. + if (card->IssueLevel > 0x001F) return false; // IL is only 5 bits. + if (card->OEM > 0) return false; // Not used in this format + + packed->Length = 32; // Set number of bits + set_bit_by_position(packed, 1, 1); // Always 1 + set_linear_field(packed, card->IssueLevel, 2, 5); + set_linear_field(packed, card->FacilityCode, 7, 8); + set_linear_field(packed, card->CardNumber, 15, 16); + set_bit_by_position(packed, evenparity32(get_linear_field(packed, 1, 16)), 0); + set_bit_by_position(packed, oddparity32(get_linear_field(packed, 14, 17)), 31); + return add_HID_header(packed); +} + +bool Unpack_Kastle(wiegand_message_t* packed, wiegand_card_t* card){ + memset(card, 0, sizeof(wiegand_card_t)); + + if (packed->Length != 32) return false; // Wrong length? Stop here. + if (get_bit_by_position(packed, 1) != 1) return false; // Always 1 in this format + + card->IssueLevel = get_linear_field(packed, 2, 5); + card->FacilityCode = get_linear_field(packed, 7, 8); + card->CardNumber = get_linear_field(packed, 15, 16); + card->ParityValid = + (get_bit_by_position(packed, 0) == evenparity32(get_linear_field(packed, 1, 16))) && + (get_bit_by_position(packed, 31) == oddparity32(get_linear_field(packed, 14, 17))); + return true; +} + +bool Pack_D10202(wiegand_card_t* card, wiegand_message_t* packed){ + memset(packed, 0, sizeof(wiegand_message_t)); + + if (card->FacilityCode > 0x007F) return false; // Can't encode FC. + if (card->CardNumber > 0x00FFFFFF) return false; // Can't encode CN. + if (card->IssueLevel > 0) return false; // Not used in this format + if (card->OEM > 0) return false; // Not used in this format + + packed->Length = 33; // Set number of bits + set_linear_field(packed, card->FacilityCode, 1, 7); + set_linear_field(packed, card->CardNumber, 8, 24); + set_bit_by_position(packed, evenparity32(get_linear_field(packed, 1, 16)), 0); + set_bit_by_position(packed, oddparity32(get_linear_field(packed, 16, 16)), 32); + return add_HID_header(packed); +} + +bool Unpack_D10202(wiegand_message_t* packed, wiegand_card_t* card){ + memset(card, 0, sizeof(wiegand_card_t)); + + if (packed->Length != 33) return false; // Wrong length? Stop here. + + card->CardNumber = get_linear_field(packed, 8, 24); + card->FacilityCode = get_linear_field(packed, 1, 7); + card->ParityValid = + (get_bit_by_position(packed, 0) == evenparity32(get_linear_field(packed, 1, 16))) && + (get_bit_by_position(packed, 32) == oddparity32(get_linear_field(packed, 16, 16))); + return true; +} + +bool Pack_H10306(wiegand_card_t* card, wiegand_message_t* packed){ + memset(packed, 0, sizeof(wiegand_message_t)); + + if (card->FacilityCode > 0xFFFF) return false; // Can't encode FC. + if (card->CardNumber > 0xFFFF) return false; // Can't encode CN. + if (card->IssueLevel > 0) return false; // Not used in this format + if (card->OEM > 0) return false; // Not used in this format + + packed->Length = 34; // Set number of bits + packed->Bot |= (card->CardNumber & 0xFFFF) << 1; + packed->Bot |= (card->FacilityCode & 0x7FFF) << 17; + packed->Mid |= (card->FacilityCode & 0x8000) >> 15; + packed->Mid |= (evenparity32((packed->Mid & 0x00000001) ^ (packed->Bot & 0xFFFE0000)) & 1) << 1; + packed->Bot |= ( oddparity32(packed->Bot & 0x0001FFFE) & 1); + return add_HID_header(packed); +} + +bool Unpack_H10306(wiegand_message_t* packed, wiegand_card_t* card){ + memset(card, 0, sizeof(wiegand_card_t)); + + if (packed->Length != 34) return false; // Wrong length? Stop here. + + card->CardNumber = (packed->Bot >> 1) & 0xFFFF; + card->FacilityCode = ((packed->Mid & 1) << 15) | ((packed->Bot >> 17) & 0xFF); + card->ParityValid = + ((evenparity32((packed->Mid & 0x00000001) ^ (packed->Bot & 0xFFFE0000)) & 1) == ((packed->Mid >> 1) & 1)) && + ((oddparity32(packed->Bot & 0x0001FFFE) & 1) == ((packed->Bot & 1))); + return true; +} + +bool Pack_N10002(wiegand_card_t* card, wiegand_message_t* packed){ + memset(packed, 0, sizeof(wiegand_message_t)); + + if (card->FacilityCode > 0xFF) return false; // Can't encode FC. + if (card->CardNumber > 0xFFFF) return false; // Can't encode CN. + if (card->IssueLevel > 0) return false; // Not used in this format + if (card->OEM > 0) return false; // Not used in this format + + packed->Length = 34; // Set number of bits + set_linear_field(packed, card->FacilityCode, 9, 8); + set_linear_field(packed, card->CardNumber, 17, 16); + return add_HID_header(packed); +} + +bool Unpack_N10002(wiegand_message_t* packed, wiegand_card_t* card){ + memset(card, 0, sizeof(wiegand_card_t)); + + if (packed->Length != 34) return false; // Wrong length? Stop here. + + card->CardNumber = get_linear_field(packed, 17, 16); + card->FacilityCode = get_linear_field(packed, 9, 8); + return true; +} + +bool Pack_C1k35s(wiegand_card_t* card, wiegand_message_t* packed){ + memset(packed, 0, sizeof(wiegand_message_t)); + + if (card->FacilityCode > 0xFFF) return false; // Can't encode FC. + if (card->CardNumber > 0xFFFFF) return false; // Can't encode CN. + if (card->IssueLevel > 0) return false; // Not used in this format + if (card->OEM > 0) return false; // Not used in this format + + packed->Length = 35; // Set number of bits + packed->Bot |= (card->CardNumber & 0x000FFFFF) << 1; + packed->Bot |= (card->FacilityCode & 0x000007FF) << 21; + packed->Mid |= (card->FacilityCode & 0x00000800) >> 11; + packed->Mid |= (evenparity32((packed->Mid & 0x00000001) ^ (packed->Bot & 0xB6DB6DB6)) & 1) << 1; + packed->Bot |= ( oddparity32((packed->Mid & 0x00000003) ^ (packed->Bot & 0x6DB6DB6C)) & 1); + packed->Mid |= ( oddparity32((packed->Mid & 0x00000003) ^ (packed->Bot & 0xFFFFFFFF)) & 1) << 2; + return add_HID_header(packed); +} + +bool Unpack_C1k35s(wiegand_message_t* packed, wiegand_card_t* card){ + memset(card, 0, sizeof(wiegand_card_t)); + + if (packed->Length != 35) return false; // Wrong length? Stop here. + + card->CardNumber = (packed->Bot >> 1) & 0x000FFFFF; + card->FacilityCode = ((packed->Mid & 1) << 11) | ((packed->Bot >> 21)); + card->ParityValid = + (evenparity32((packed->Mid & 0x00000001) ^ (packed->Bot & 0xB6DB6DB6)) == ((packed->Mid >> 1) & 1)) && + ( oddparity32((packed->Mid & 0x00000003) ^ (packed->Bot & 0x6DB6DB6C)) == ((packed->Bot >> 0) & 1)) && + ( oddparity32((packed->Mid & 0x00000003) ^ (packed->Bot & 0xFFFFFFFF)) == ((packed->Mid >> 2) & 1)); + return true; +} + +bool Pack_H10320(wiegand_card_t* card, wiegand_message_t* packed){ + memset(packed, 0, sizeof(wiegand_message_t)); + + if (card->FacilityCode > 0) return false; // Can't encode FC. (none in this format) + if (card->CardNumber > 99999999) return false; // Can't encode CN. + if (card->IssueLevel > 0) return false; // Not used in this format + if (card->OEM > 0) return false; // Not used in this format + + packed->Length = 36; // Set number of bits + // This card is BCD-encoded rather than binary. Set the 4-bit groups independently. + for (uint32_t idx = 0; idx < 8; idx++){ + set_linear_field(packed, (uint64_t)(card->CardNumber / pow(10, 7-idx)) % 10, idx * 4, 4); + } + set_bit_by_position(packed, evenparity32( + get_nonlinear_field(packed, 8, (uint8_t[]){0, 4, 8, 12, 16, 20, 24, 28}) + ), 32); + set_bit_by_position(packed, oddparity32( + get_nonlinear_field(packed, 8, (uint8_t[]){1, 5, 9, 13, 17, 21, 25, 29}) + ), 33); + set_bit_by_position(packed, evenparity32( + get_nonlinear_field(packed, 8, (uint8_t[]){2, 6, 10, 14, 18, 22, 28, 30}) + ), 34); + set_bit_by_position(packed, evenparity32( + get_nonlinear_field(packed, 8, (uint8_t[]){3, 7, 11, 15, 19, 23, 29, 31}) + ), 35); + return add_HID_header(packed); +} + +bool Unpack_H10320(wiegand_message_t* packed, wiegand_card_t* card){ + memset(card, 0, sizeof(wiegand_card_t)); + + if (packed->Length != 36) return false; // Wrong length? Stop here. + + // This card is BCD-encoded rather than binary. Get the 4-bit groups independently. + for (uint32_t idx = 0; idx < 8; idx++){ + uint64_t val = get_linear_field(packed, idx * 4, 4); + if (val > 9){ + // Violation of BCD; Zero and exit. + card->CardNumber = 0; + return false; + } else { + card->CardNumber += val * pow(10, 7-idx); + } + } + card->ParityValid = + (get_bit_by_position(packed, 32) == evenparity32(get_nonlinear_field(packed, 8, (uint8_t[]){0, 4, 8, 12, 16, 20, 24, 28}))) && + (get_bit_by_position(packed, 33) == oddparity32(get_nonlinear_field(packed, 8, (uint8_t[]){1, 5, 9, 13, 17, 21, 25, 29}))) && + (get_bit_by_position(packed, 34) == evenparity32(get_nonlinear_field(packed, 8, (uint8_t[]){2, 6, 10, 14, 18, 22, 28, 30}))) && + (get_bit_by_position(packed, 35) == evenparity32(get_nonlinear_field(packed, 8, (uint8_t[]){3, 7, 11, 15, 19, 23, 29, 31}))); + return true; +} + +bool Pack_S12906(wiegand_card_t* card, wiegand_message_t* packed){ + memset(packed, 0, sizeof(wiegand_message_t)); + + if (card->FacilityCode > 0xFF) return false; // Can't encode FC. + if (card->IssueLevel > 0x03) return false; // Can't encode IL. + if (card->CardNumber > 0x00FFFFFF) return false; // Can't encode CN. + if (card->OEM > 0) return false; // Not used in this format + + packed->Length = 36; // Set number of bits + set_linear_field(packed, card->FacilityCode, 1, 8); + set_linear_field(packed, card->IssueLevel, 9, 2); + set_linear_field(packed, card->CardNumber, 11, 24); + set_bit_by_position(packed, oddparity32(get_linear_field(packed, 1, 17)), 0); + set_bit_by_position(packed, oddparity32(get_linear_field(packed, 17, 18)), 35); + return add_HID_header(packed); +} + +bool Unpack_S12906(wiegand_message_t* packed, wiegand_card_t* card){ + memset(card, 0, sizeof(wiegand_card_t)); + + if (packed->Length != 36) return false; // Wrong length? Stop here. + + card->FacilityCode = get_linear_field(packed, 1, 8); + card->IssueLevel = get_linear_field(packed, 9, 2); + card->CardNumber = get_linear_field(packed, 11, 24); + card->ParityValid = + (get_bit_by_position(packed, 0) == oddparity32(get_linear_field(packed, 1, 17))) && + (get_bit_by_position(packed, 35) == oddparity32(get_linear_field(packed, 17, 18))); + return true; +} + +bool Pack_Sie36(wiegand_card_t* card, wiegand_message_t* packed){ + memset(packed, 0, sizeof(wiegand_message_t)); + + if (card->FacilityCode > 0x0003FFFF) return false; // Can't encode FC. + if (card->CardNumber > 0x0000FFFF) return false; // Can't encode CN. + if (card->IssueLevel > 0) return false; // Not used in this format + if (card->OEM > 0) return false; // Not used in this format + + packed->Length = 36; // Set number of bits + set_linear_field(packed, card->FacilityCode, 1, 18); + set_linear_field(packed, card->CardNumber, 19, 16); + set_bit_by_position(packed, + oddparity32(get_nonlinear_field(packed, 23, (uint8_t[]){1, 3, 4, 6, 7, 9, 10, 12, 13, 15, 16, 18, 19, 21, 22, 24, 25, 27, 28, 30, 31, 33, 34})) + , 0); + set_bit_by_position(packed, + evenparity32(get_nonlinear_field(packed, 23, (uint8_t[]){1, 2, 4, 5, 7, 8, 10, 11, 13, 14, 16, 17, 19, 20, 22, 23, 25, 26, 28, 29, 31, 32, 34})) + , 35); + return add_HID_header(packed); +} + +bool Unpack_Sie36(wiegand_message_t* packed, wiegand_card_t* card){ + memset(card, 0, sizeof(wiegand_card_t)); + + if (packed->Length != 36) return false; // Wrong length? Stop here. + + card->FacilityCode = get_linear_field(packed, 1, 18); + card->CardNumber = get_linear_field(packed, 19, 16); + card->ParityValid = + (get_bit_by_position(packed, 0) == oddparity32(get_nonlinear_field(packed, 23, (uint8_t[]){1, 3, 4, 6, 7, 9, 10, 12, 13, 15, 16, 18, 19, 21, 22, 24, 25, 27, 28, 30, 31, 33, 34}))) && + (get_bit_by_position(packed, 35) == oddparity32(get_nonlinear_field(packed, 23, (uint8_t[]){1, 2, 4, 5, 7, 8, 10, 11, 13, 14, 16, 17, 19, 20, 22, 23, 25, 26, 28, 29, 31, 32, 34}))); + return true; +} + +bool Pack_C15001(wiegand_card_t* card, wiegand_message_t* packed){ + memset(packed, 0, sizeof(wiegand_message_t)); + + if (card->FacilityCode > 0x000000FF) return false; // Can't encode FC. + if (card->CardNumber > 0x0000FFFF) return false; // Can't encode CN. + if (card->IssueLevel > 0) return false; // Not used in this format + if (card->OEM > 0x000003FF) return false; // Can't encode OEM. + + packed->Length = 36; // Set number of bits + set_linear_field(packed, card->OEM, 1, 10); + set_linear_field(packed, card->FacilityCode, 11, 8); + set_linear_field(packed, card->CardNumber, 19, 16); + set_bit_by_position(packed, evenparity32(get_linear_field(packed, 1, 17)), 0); + set_bit_by_position(packed, oddparity32(get_linear_field(packed, 18, 17)), 35); + return add_HID_header(packed); +} + +bool Unpack_C15001(wiegand_message_t* packed, wiegand_card_t* card){ + memset(card, 0, sizeof(wiegand_card_t)); + + if (packed->Length != 36) return false; // Wrong length? Stop here. + + card->OEM = get_linear_field(packed, 1, 10); + card->FacilityCode = get_linear_field(packed, 11, 8); + card->CardNumber = get_linear_field(packed, 19, 16); + card->ParityValid = + (get_bit_by_position(packed, 0) == evenparity32(get_linear_field(packed, 1, 17))) && + (get_bit_by_position(packed, 35) == oddparity32(get_linear_field(packed, 18, 17))); + return true; +} + +bool Pack_H10302(wiegand_card_t* card, wiegand_message_t* packed){ + memset(packed, 0, sizeof(wiegand_message_t)); + + if (card->FacilityCode > 0) return false; // Can't encode FC. (none in this format) + if (card->CardNumber > 0x00000007FFFFFFFF) return false; // Can't encode CN. + if (card->IssueLevel > 0) return false; // Not used in this format + if (card->OEM > 0) return false; // Not used in this format + + packed->Length = 37; // Set number of bits + set_linear_field(packed, card->CardNumber, 1, 35); + set_bit_by_position(packed, evenparity32(get_linear_field(packed, 1, 18)), 0); + set_bit_by_position(packed, oddparity32(get_linear_field(packed, 18, 18)), 36); + return add_HID_header(packed); +} + +bool Unpack_H10302(wiegand_message_t* packed, wiegand_card_t* card){ + memset(card, 0, sizeof(wiegand_card_t)); + + if (packed->Length != 37) return false; // Wrong length? Stop here. + + card->CardNumber = get_linear_field(packed, 1, 35); + card->ParityValid = + (get_bit_by_position(packed, 0) == evenparity32(get_linear_field(packed, 1, 18))) && + (get_bit_by_position(packed, 36) == oddparity32(get_linear_field(packed, 18, 18))); + return true; +} + +bool Pack_H10304(wiegand_card_t* card, wiegand_message_t* packed){ + memset(packed, 0, sizeof(wiegand_message_t)); + + if (card->FacilityCode > 0x0000FFFF) return false; // Can't encode FC. + if (card->CardNumber > 0x0007FFFF) return false; // Can't encode CN. + if (card->IssueLevel > 0) return false; // Not used in this format + if (card->OEM > 0) return false; // Not used in this format + + packed->Length = 37; // Set number of bits + packed->Bot |= (card->CardNumber & 0x0007FFFF) << 1; + packed->Bot |= (card->FacilityCode & 0x00000FFF) << 20; + packed->Mid |= (card->FacilityCode & 0x0000F000) >> 12; + packed->Mid |= (evenparity32((packed->Mid & 0x0000000F) ^ (packed->Bot & 0xFFFC0000)) & 1) << 4; + packed->Bot |= ( oddparity32(packed->Bot & 0x0007FFFE) & 1); + return add_HID_header(packed); +} + +bool Unpack_H10304(wiegand_message_t* packed, wiegand_card_t* card){ + memset(card, 0, sizeof(wiegand_card_t)); + + if (packed->Length != 37) return false; // Wrong length? Stop here. + + card->CardNumber = (packed->Bot >> 1) & 0x0007FFFF; + card->FacilityCode = ((packed->Mid & 0xF) << 12) | ((packed->Bot >> 20)); + card->ParityValid = + (evenparity32((packed->Mid & 0x0000000F) ^ (packed->Bot & 0xFFFC0000)) == ((packed->Mid >> 4) & 1)) && + (oddparity32( packed->Bot & 0x0007FFFE) == (packed->Bot & 1)); + return true; +} + +bool Pack_P10001(wiegand_card_t* card, wiegand_message_t* packed){ + + memset(packed, 0, sizeof(wiegand_message_t)); + + if (card->FacilityCode > 0xFFF) return false; // Can't encode FC. + if (card->CardNumber > 0xFFFF) return false; // Can't encode CN. + if (card->IssueLevel > 0) return false; // Not used in this format + if (card->OEM > 0) return false; // Not used in this format + + packed->Length = 40; // Set number of bits + set_linear_field(packed, 0xF, 0, 4); + set_linear_field(packed, card->FacilityCode, 4, 12); + set_linear_field(packed, card->CardNumber, 16, 16); + set_linear_field(packed, + get_linear_field(packed, 0, 8) ^ + get_linear_field(packed, 8, 8) ^ + get_linear_field(packed, 16, 8) ^ + get_linear_field(packed, 24, 8) + , 32, 8); + return add_HID_header(packed); +} + +bool Unpack_P10001(wiegand_message_t* packed, wiegand_card_t* card){ + + memset(card, 0, sizeof(wiegand_card_t)); + + if (packed->Length != 40) return false; // Wrong length? Stop here. + + card->CardNumber = get_linear_field(packed, 16, 16); + card->FacilityCode = get_linear_field(packed, 4, 12); + card->ParityValid = ( + get_linear_field(packed, 0, 8) ^ + get_linear_field(packed, 8, 8) ^ + get_linear_field(packed, 16, 8) ^ + get_linear_field(packed, 24, 8) + ) == get_linear_field(packed, 32, 8); + return true; +} + +bool Pack_C1k48s(wiegand_card_t* card, wiegand_message_t* packed){ + + memset(packed, 0, sizeof(wiegand_message_t)); + + if (card->FacilityCode > 0x003FFFFF) return false; // Can't encode FC. + if (card->CardNumber > 0x007FFFFF) return false; // Can't encode CN. + if (card->IssueLevel > 0) return false; // Not used in this format + if (card->OEM > 0) return false; // Not used in this format + + packed->Length = 48; // Set number of bits + packed->Bot |= (card->CardNumber & 0x007FFFFF) << 1; + packed->Bot |= (card->FacilityCode & 0x000000FF) << 24; + packed->Mid |= (card->FacilityCode & 0x003FFF00) >> 8; + packed->Mid |= (evenparity32((packed->Mid & 0x00001B6D) ^ (packed->Bot & 0xB6DB6DB6)) & 1) << 14; + packed->Bot |= ( oddparity32((packed->Mid & 0x000036DB) ^ (packed->Bot & 0x6DB6DB6C)) & 1); + packed->Mid |= ( oddparity32((packed->Mid & 0x00007FFF) ^ (packed->Bot & 0xFFFFFFFF)) & 1) << 15; + return add_HID_header(packed); +} + +bool Unpack_C1k48s(wiegand_message_t* packed, wiegand_card_t* card){ + memset(card, 0, sizeof(wiegand_card_t)); + + if (packed->Length != 48) return false; // Wrong length? Stop here. + + card->CardNumber = (packed->Bot >> 1) & 0x007FFFFF; + card->FacilityCode = ((packed->Mid & 0x00003FFF) << 8) | ((packed->Bot >> 24)); + card->ParityValid = + (evenparity32((packed->Mid & 0x00001B6D) ^ (packed->Bot & 0xB6DB6DB6)) == ((packed->Mid >> 14) & 1)) && + ( oddparity32((packed->Mid & 0x000036DB) ^ (packed->Bot & 0x6DB6DB6C)) == ((packed->Bot >> 0) & 1)) && + ( oddparity32((packed->Mid & 0x00007FFF) ^ (packed->Bot & 0xFFFFFFFF)) == ((packed->Mid >> 15) & 1)); + return true; +} + +static const cardformat_t FormatTable[] = { + {"H10301", Pack_H10301, Unpack_H10301, "HID H10301 26-bit", {1, 1, 0, 0, 1}}, // imported from old pack/unpack + {"Tecom27", Pack_Tecom27, Unpack_Tecom27, "Tecom 27-bit", {1, 1, 0, 0, 1}}, // from cardinfo.barkweb.com.au + {"2804W", Pack_2804W, Unpack_2804W, "2804 Wiegand", {1, 1, 0, 0, 1}}, // from cardinfo.barkweb.com.au + {"ATSW30", Pack_ATSW30, Unpack_ATSW30, "ATS Wiegand 30-bit", {1, 1, 0, 0, 1}}, // from cardinfo.barkweb.com.au + {"ADT31", Pack_ADT31, Unpack_ADT31, "HID ADT 31-bit", {1, 1, 0, 0, 1}}, // from cardinfo.barkweb.com.au + {"Kastle", Pack_Kastle, Unpack_Kastle, "Kastle 32-bit", {1, 1, 1, 0, 1}}, // from @xilni; PR #23 on RfidResearchGroup/proxmark3 + {"D10202", Pack_D10202, Unpack_D10202, "HID D10202 33-bit", {1, 1, 0, 0, 1}}, // from cardinfo.barkweb.com.au + {"H10306", Pack_H10306, Unpack_H10306, "HID H10306 34-bit", {1, 1, 0, 0, 1}}, // imported from old pack/unpack + {"N10002", Pack_N10002, Unpack_N10002, "HID N10002 34-bit", {1, 1, 0, 0, 1}}, // from cardinfo.barkweb.com.au + {"C1k35s", Pack_C1k35s, Unpack_C1k35s, "HID Corporate 1000 35-bit standard layout", {1, 1, 0, 0, 1}}, // imported from old pack/unpack + {"C15001", Pack_C15001, Unpack_C15001, "HID KeySpan 36-bit", {1, 1, 0, 1, 1}}, // from Proxmark forums + {"S12906", Pack_S12906, Unpack_S12906, "HID Simplex 36-bit", {1, 1, 1, 0, 1}}, // from cardinfo.barkweb.com.au + {"Sie36", Pack_Sie36, Unpack_Sie36, "HID 36-bit Siemens", {1, 1, 0, 0, 1}}, // from cardinfo.barkweb.com.au + {"H10320", Pack_H10320, Unpack_H10320, "HID H10320 36-bit BCD", {1, 0, 0, 0, 1}}, // from Proxmark forums + {"H10302", Pack_H10302, Unpack_H10302, "HID H10302 37-bit huge ID", {1, 0, 0, 0, 1}}, // from Proxmark forums + {"H10304", Pack_H10304, Unpack_H10304, "HID H10304 37-bit", {1, 1, 0, 0, 1}}, // imported from old pack/unpack + {"P10001", Pack_P10001, Unpack_P10001, "HID P10001 Honeywell 40-bit" }, // from cardinfo.barkweb.com.au + {"C1k48s", Pack_C1k48s, Unpack_C1k48s, "HID Corporate 1000 48-bit standard layout", {1, 1, 0, 0, 1}}, // imported from old pack/unpack + {NULL, NULL, NULL, NULL, {0, 0, 0, 0, 0}} // Must null terminate array +}; + +void HIDListFormats(){ + if (FormatTable[0].Name == NULL) + return; + + PrintAndLogEx(NORMAL, "%-10s %s", "Name", "Description"); + PrintAndLogEx(NORMAL, "------------------------------------------------------------"); + + int i = 0; + while (FormatTable[i].Name) { + PrintAndLogEx(NORMAL, _YELLOW_("%-10s")" %-30s", FormatTable[i].Name, FormatTable[i].Descrp); + ++i; + } + PrintAndLogEx(NORMAL, ""); + return; +} + +cardformat_t HIDGetCardFormat(int idx){ + return FormatTable[idx]; +} + +int HIDFindCardFormat(const char *format) { + + if (FormatTable[0].Name == NULL) + return -1; + + int i = 0; + +// str_lower + + while (FormatTable[i].Name && strcmp(FormatTable[i].Name, format)) { + ++i; + } + + if (FormatTable[i].Name) + return i; + + return -1; +} + +bool HIDPack(int format_idx, wiegand_card_t* card, wiegand_message_t* packed){ + memset(packed, 0, sizeof(wiegand_message_t)); + + if (format_idx < 0 || format_idx >= (sizeof(FormatTable) / sizeof(FormatTable[0]))) + return false; + + return FormatTable[format_idx].Pack(card, packed); +} + +void HIDDisplayUnpackedCard(wiegand_card_t* card, const cardformat_t format){ + +/* + PrintAndLogEx(SUCCESS, " Format: %s (%s)", format.Name, format.Descrp); + + if (format.Fields.hasFacilityCode) + PrintAndLogEx(SUCCESS, "Facility Code: %d",card->FacilityCode); + + if (format.Fields.hasCardNumber) + PrintAndLogEx(SUCCESS, " Card Number: %d",card->CardNumber); + + if (format.Fields.hasIssueLevel) + PrintAndLogEx(SUCCESS, " Issue Level: %d",card->IssueLevel); + + if (format.Fields.hasOEMCode) + PrintAndLogEx(SUCCESS, " OEM Code: %d",card->OEM); + + if (format.Fields.hasParity) + PrintAndLogEx(SUCCESS, " Parity: %s",card->ParityValid ? "Valid" : "Invalid"); +*/ + + char s[80] = {0}; + if (format.Fields.hasFacilityCode) + snprintf(s, sizeof(s), "FC: %d", card->FacilityCode); + + if (format.Fields.hasCardNumber) + snprintf(s + strlen(s), sizeof(s) - strlen(s), " CN: %" PRIu64, card->CardNumber); + + if (format.Fields.hasIssueLevel) + snprintf(s + strlen(s), sizeof(s) - strlen(s), " Issue %d", card->IssueLevel); + + if (format.Fields.hasOEMCode) + snprintf(s + strlen(s), sizeof(s) - strlen(s), " OEM: %d",card->OEM); + + if (format.Fields.hasParity) + snprintf(s + strlen(s), sizeof(s) - strlen(s), " parity: %s", card->ParityValid ? "valid" : "invalid"); + + PrintAndLogEx(SUCCESS, "%s [%s - %s]", s, format.Name, format.Descrp); +} + +bool HIDTryUnpack(wiegand_message_t* packed, bool ignore_parity){ + if (FormatTable[0].Name == NULL) + return false; + + bool result = false; + int i = 0; + wiegand_card_t card; + memset(&card, 0, sizeof(wiegand_card_t)); + + while (FormatTable[i].Name) { + if (FormatTable[i].Unpack(packed, &card)) { + if (ignore_parity || !FormatTable[i].Fields.hasParity || card.ParityValid) { + result = true; + HIDDisplayUnpackedCard(&card, FormatTable[i]); + } + } + ++i; + } + if ( result == false ) { + PrintAndLogEx(SUCCESS, "Unknown. Bit len %d", packed->Length); + } + + return result; +} diff --git a/client/wiegand_formats.h b/client/wiegand_formats.h new file mode 100644 index 000000000..c904f789a --- /dev/null +++ b/client/wiegand_formats.h @@ -0,0 +1,48 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2018 grauerfuchs +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// Wiegand format packing/unpacking routines +//----------------------------------------------------------------------------- + +#ifndef WIEGAND_FORMATS_H__ +#define WIEGAND_FORMATS_H__ + +#include // memset +#include +#include +#include +#include +#include +#include "cmddata.h" +#include "wiegand_formatutils.h" +#include "parity.h" // for parity +#include "ui.h" + +typedef struct { + bool hasCardNumber; + bool hasFacilityCode; + bool hasIssueLevel; + bool hasOEMCode; + bool hasParity; +} cardformatdescriptor_t; + +// Structure for defined Wiegand card formats available for packing/unpacking +typedef struct { + const char* Name; + bool (*Pack)(wiegand_card_t* card, wiegand_message_t* packed); + bool (*Unpack)(wiegand_message_t* packed, wiegand_card_t* card); + const char* Descrp; + cardformatdescriptor_t Fields; +} cardformat_t; + +void HIDListFormats(); +int HIDFindCardFormat(const char *format); +cardformat_t HIDGetCardFormat(int idx); +bool HIDPack(int FormatIndex, wiegand_card_t* card, wiegand_message_t* packed); +bool HIDTryUnpack(wiegand_message_t* packed, bool ignoreParity); + +#endif diff --git a/client/wiegand_formatutils.c b/client/wiegand_formatutils.c new file mode 100644 index 000000000..dd3678daf --- /dev/null +++ b/client/wiegand_formatutils.c @@ -0,0 +1,185 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2018 grauerfuchs +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// Wiegand card format packing/unpacking support functions +//----------------------------------------------------------------------------- + +#include +#include +#include +#include +#include "wiegand_formatutils.h" +#include "ui.h" + +bool get_bit_by_position(wiegand_message_t* data, uint8_t pos){ + if (pos >= data->Length) return false; + pos = (data->Length - pos) - 1; // invert ordering; Indexing goes from 0 to 1. Subtract 1 for weight of bit. + bool result = false; + if (pos > 95) + result = false; + else if (pos > 63) + result = (data->Top >> (pos - 64)) & 1; + else if (pos > 31) + result = (data->Mid >> (pos - 32)) & 1; + else + result = (data->Bot >> pos) & 1; + return result; +} +bool set_bit_by_position(wiegand_message_t* data, bool value, uint8_t pos){ + if (pos >= data->Length) return false; + pos = (data->Length - pos) - 1; // invert ordering; Indexing goes from 0 to 1. Subtract 1 for weight of bit. + if (pos > 95) { + return false; + } else if (pos > 63) { + if (value) + data->Top |= (1 << (pos - 64)); + else + data->Top &= ~(1 << (pos - 64)); + return true; + } else if (pos > 31) { + if (value) + data->Mid |= (1 << (pos - 32)); + else + data->Mid &= ~(1 << (pos - 32)); + return true; + } else { + if (value) + data->Bot |= (1 << pos); + else + data->Bot &= ~(1 << pos); + return true; + } +} +/** + * Safeguard the data by doing a manual deep copy + * + * At the time of the initial writing, the struct does not contain pointers. That doesn't + * mean it won't eventually contain one, however. To prevent memory leaks and erroneous + * aliasing, perform the copy function manually instead. Hence, this function. + * + * If the definition of the wiegand_message struct changes, this function must also + * be updated to match. + */ +void message_datacopy(wiegand_message_t* src, wiegand_message_t* dest){ + dest->Bot = src->Bot; + dest->Mid = src->Mid; + dest->Top = src->Top; + dest->Length = src->Length; +} +/** + * + * Yes, this is horribly inefficient for linear data. + * The current code is a temporary measure to have a working function in place + * until all the bugs shaken from the block/chunk version of the code. + * + */ +uint64_t get_linear_field(wiegand_message_t* data, uint8_t firstBit, uint8_t length){ + uint64_t result = 0; + for (uint8_t i = 0; i < length; i++ ) { + result = (result << 1) | get_bit_by_position(data, firstBit + i); + } + return result; +} +bool set_linear_field(wiegand_message_t* data, uint64_t value, uint8_t firstBit, uint8_t length){ + wiegand_message_t tmpdata; + message_datacopy(data, &tmpdata); + bool result = true; + for (int i = 0; i < length; i++){ + result &= set_bit_by_position(&tmpdata, (value >> ((length - i) - 1)) & 1, firstBit + i); + } + if (result) + message_datacopy(&tmpdata, data); + + return result; +} + +uint64_t get_nonlinear_field(wiegand_message_t* data, uint8_t numBits, uint8_t* bits){ + uint64_t result = 0; + for (int i = 0; i < numBits; i++){ + result = (result << 1) | (get_bit_by_position(data, *(bits+i)) & 1); + } + return result; +} +bool set_nonlinear_field(wiegand_message_t* data, uint64_t value, uint8_t numBits, uint8_t* bits){ + + wiegand_message_t tmpdata; + message_datacopy(data, &tmpdata); + + bool result = true; + for (int i = 0; i < numBits; i++){ + result &= set_bit_by_position(&tmpdata, (value >> ((numBits - i) - 1)) & 1, *(bits + i)); + } + + if (result) + message_datacopy(&tmpdata, data); + + return result; +} + +uint8_t get_length_from_header(wiegand_message_t* data) { + uint8_t len = 0; + uint32_t hfmt = 0; // for calculating card length + + if ((data->Top & 0x000FFFFF) > 0) { // > 64 bits + hfmt = data->Top & 0x000FFFFF; + len = 64; + } else if ((data->Mid & 0xFFFFFFC0) > 0) { // < 63-38 bits + hfmt = data->Mid & 0xFFFFFFC0; + len = 32; + } else if (data->Mid && (data->Mid & 0x00000020) == 0) { // 37 bits; + hfmt = 0; + len = 37; + } else if ((data->Mid & 0x0000001F) > 0){ // 36-32 bits + printf("a\n"); + hfmt = data->Mid & 0x0000001F; + len = 32; + } else if (data->Top == 0 && data->Mid == 0) { //< 32 bits + hfmt = data->Bot; + len = 0; + } else { + hfmt = data->Bot; + len = 0; + } + + while (hfmt > 1) { + hfmt >>= 1; + len++; + } + return len; +} + +wiegand_message_t initialize_message_object(uint32_t top, uint32_t mid, uint32_t bot){ + wiegand_message_t result; + memset(&result, 0, sizeof(wiegand_message_t)); + + result.Top = top; + result.Mid = mid; + result.Bot = bot; + result.Length = get_length_from_header(&result); + return result; +} + +bool add_HID_header(wiegand_message_t* data){ + if (data->Length > 84 || data->Length == 0) return false; // Invalid value + + if (data->Length >= 64){ + data->Top |= 1 << (data->Length - 64); // leading 1: start bit + data->Top |= 0x09e00000; // Extended-length header + } else if (data->Length > 37){ + data->Mid |= 1 << (data->Length - 32); // leading 1: start bit + data->Top |= 0x09e00000; // Extended-length header + } else if (data->Length == 37){ + // No header bits added to 37-bit cards + } else if (data->Length >= 32){ + data->Mid |= 0x20; // Bit 37; standard header + data->Mid |= 1 << (data->Length - 32); // leading 1: start bit + } else { + data->Mid |= 0x20; // Bit 37; standard header + data->Bot |= 1 << data->Length; // leading 1: start bit + } + return true; +} diff --git a/client/wiegand_formatutils.h b/client/wiegand_formatutils.h new file mode 100644 index 000000000..d9e74bcb2 --- /dev/null +++ b/client/wiegand_formatutils.h @@ -0,0 +1,49 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2018 grauerfuchs +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// Weigand card format packing/unpacking support functions +//----------------------------------------------------------------------------- + +#ifndef WIEGAND_FORMATUTILS_H__ +#define WIEGAND_FORMATUTILS_H__ + +#include +#include +#include + +// Structure for packed wiegand messages +// Always align lowest value (last transmitted) bit to ordinal position 0 (lowest valued bit bottom) +typedef struct { + uint8_t Length; // Number of encoded bits in wiegand message (excluding headers and preamble) + uint32_t Top; // Bits in x<<64 positions + uint32_t Mid; // Bits in x<<32 positions + uint32_t Bot; // Lowest ordinal positions +} wiegand_message_t; + +// Structure for unpacked wiegand card, like HID prox +typedef struct { + uint32_t FacilityCode; + uint64_t CardNumber; + uint32_t IssueLevel; + uint32_t OEM; + bool ParityValid; // Only valid for responses +} wiegand_card_t; + +bool get_bit_by_position(wiegand_message_t* data, uint8_t pos); +bool set_bit_by_position(wiegand_message_t* data, bool value, uint8_t pos); + +uint64_t get_linear_field(wiegand_message_t* data, uint8_t firstBit, uint8_t length); +bool set_linear_field(wiegand_message_t* data, uint64_t value, uint8_t firstBit, uint8_t length); + +uint64_t get_nonlinear_field(wiegand_message_t* data, uint8_t numBits, uint8_t* bits); +bool set_nonlinear_field(wiegand_message_t* data, uint64_t value, uint8_t numBits, uint8_t* bits); + +wiegand_message_t initialize_message_object(uint32_t top, uint32_t mid, uint32_t bot); + +bool add_HID_header(wiegand_message_t* data); + +#endif