From 08cd96c5a905e9df658a96d2fb8a8da557bf3269 Mon Sep 17 00:00:00 2001 From: CinderSocket Date: Tue, 10 Mar 2026 12:25:31 -0700 Subject: [PATCH] Improve Wiegand PACS encode/decode and verbose output --- CHANGELOG.md | 1 + client/src/cmdwiegand.c | 195 +++++++++++++++++++++++++++-------- client/src/wiegand_formats.c | 53 +++++++--- tools/pm3_tests.sh | 16 +++ 4 files changed, 210 insertions(+), 55 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index edae94449..7c427b4d9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ All notable changes to this project will be documented in this file. This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log... ## [unreleased][unreleased] +- Changed `wiegand encode` / `wiegand decode` - added support for encoding and decoding the new 96-bit ASN.1 encoded format, `--bin` encoding, verbose PACS encoding output, and explicit rejection of raw/binary decodes above 96 bits (@cindersocket) - Added `hf felica discnodes` command (@kormax) - Added `hf mfp dump` command (@apply-science) - Added `hf felica seacinfo` command (@kormax) diff --git a/client/src/cmdwiegand.c b/client/src/cmdwiegand.c index 125735bf1..191a31d38 100644 --- a/client/src/cmdwiegand.c +++ b/client/src/cmdwiegand.c @@ -27,6 +27,7 @@ #include "protocols.h" #include "parity.h" // oddparity #include "cmdhflist.h" // annotations +#include "commonutil.h" // ARRAYLEN #include "wiegand_formats.h" #include "wiegand_formatutils.h" #include "util.h" @@ -34,45 +35,104 @@ static int CmdHelp(const char *Cmd); #define PACS_EXTRA_LONG_FORMAT 18 // 144 bits -#define PACS_LONG_FORMAT 12 // 96 bits +#define PACS_LONG_FORMAT 13 // 96 bits + 1 byte pad #define PACS_FORMAT 6 // 44 bits -static int wiegand_new_pacs(uint8_t *padded_pacs, uint8_t plen) { +#define PACS_MAX_WIEGAND_BITS 96 +#define WIEGAND_MAX_ENCODED_BITS (PACS_MAX_WIEGAND_BITS + 8) - uint8_t d[PACS_EXTRA_LONG_FORMAT] = {0}; - memcpy(d, padded_pacs, plen); - - uint8_t pad = d[0]; - - char *binstr = (char *)calloc((PACS_EXTRA_LONG_FORMAT * 8) + 1, sizeof(uint8_t)); - if (binstr == NULL) { - PrintAndLogEx(WARNING, "Failed to allocate memory"); - return PM3_EMALLOC; +static void wiegand_packed_to_binstr(const wiegand_message_t *packed, char *binstr) { + for (uint8_t i = 0; i < packed->Length; i++) { + binstr[i] = get_bit_by_position((wiegand_message_t *)packed, i) ? '1' : '0'; } + binstr[packed->Length] = '\0'; +} - uint8_t n = plen - 1; +static int wiegand_print_new_pacs_verbose(const wiegand_message_t *packed, const uint8_t *pacs, size_t pacs_len) { + char binstr[PACS_MAX_WIEGAND_BITS + 1] = {0}; + char rawbin[WIEGAND_MAX_ENCODED_BITS + 1] = {0}; + uint8_t raw[(WIEGAND_MAX_ENCODED_BITS + 7) / 8] = {0}; + size_t raw_len = 0; - bytes_2_binstr(binstr, d + 1, n); + wiegand_packed_to_binstr(packed, binstr); + rawbin[0] = '1'; + memcpy(rawbin + 1, binstr, packed->Length); + binstr_2_bytes(raw, &raw_len, rawbin); + bytes_2_binstr(rawbin, raw, raw_len); - binstr[strlen(binstr) - pad] = '\0'; + PrintAndLogEx(INFO, "----------------------- " _CYAN_("PACS Encoding") " ------------------------"); + PrintAndLogEx(SUCCESS, "New PACS......... " _GREEN_("0x %s"), sprint_hex_inrow(pacs, pacs_len)); + PrintAndLogEx(INFO, "With Sentinel.... " _GREEN_("0b %s") " (%zu-bit)", rawbin, strlen(rawbin)); + PrintAndLogEx(SUCCESS, "Wiegand --raw.... " _YELLOW_("0x %s"), sprint_hex_inrow(raw, raw_len)); + PrintAndLogEx(INFO, "Without Sentinel. " _GREEN_("0b %s") " (%zu-bit)", binstr, strlen(binstr)); + return PM3_SUCCESS; +} - size_t tlen = 0; - uint8_t tmp[16] = {0}; - binstr_2_bytes(tmp, &tlen, binstr); - PrintAndLogEx(SUCCESS, "Wiegand raw.... " _YELLOW_("%s"), sprint_hex_inrow(tmp, tlen)); +static int wiegand_encode_new_pacs(const wiegand_message_t *packed, bool verbose) { - uint32_t top = 0, mid = 0, bot = 0; - if (binstring_to_u96(&top, &mid, &bot, binstr) != strlen(binstr)) { - PrintAndLogEx(ERR, "Binary string contains none <0|1> chars"); - free(binstr); + if (packed->Length == 0) { + PrintAndLogEx(ERR, "Empty Wiegand input"); return PM3_EINVARG; } - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, "------------------------- " _CYAN_("SIO - Wiegand") " ---------------------------"); - decode_wiegand(top, mid, bot, strlen(binstr)); - free(binstr); + if (packed->Length > PACS_MAX_WIEGAND_BITS) { + PrintAndLogEx(ERR, "New PACS encoding supports up to %u Wiegand bits", PACS_MAX_WIEGAND_BITS); + return PM3_EINVARG; + } + + uint8_t padded_bits = (uint8_t)(((packed->Length + 7) / 8) * 8); + uint8_t pad = padded_bits - packed->Length; + + char binstr[PACS_MAX_WIEGAND_BITS + 1] = {0}; + wiegand_packed_to_binstr(packed, binstr); + memset(binstr + packed->Length, '0', pad); + binstr[padded_bits] = '\0'; + + size_t pacs_len = 0; + uint8_t pacs[PACS_LONG_FORMAT] = {0}; + binstr_2_bytes(pacs + 1, &pacs_len, binstr); + pacs[0] = pad; + + PrintAndLogEx(SUCCESS, "New PACS......... " _GREEN_("0x %s"), sprint_hex_inrow(pacs, pacs_len + 1)); + if (verbose) { + PrintAndLogEx(NORMAL, ""); + return wiegand_print_new_pacs_verbose(packed, pacs, pacs_len + 1); + } return PM3_SUCCESS; } + +static int wiegand_new_pacs(const uint8_t *padded_pacs, uint8_t plen) { + return HIDDumpPACSBits(padded_pacs, plen, false); +} + +static int wiegand_print_raw_from_bin(const uint8_t *binarr, int blen) { + uint8_t out[(WIEGAND_MAX_ENCODED_BITS + 7) / 8] = {0}; + char binstr[WIEGAND_MAX_ENCODED_BITS + 1] = {0}; + + binstr[0] = '1'; + for (int i = 0; i < blen; i++) { + binstr[i + 1] = binarr[i] ? '1' : '0'; + } + + size_t out_len = 0; + binstr_2_bytes(out, &out_len, binstr); + PrintAndLogEx(SUCCESS, "Wiegand raw.... " _YELLOW_("%s"), sprint_hex_inrow(out, out_len)); + return PM3_SUCCESS; +} + +static int wiegand_encode_new_pacs_from_bin(const uint8_t *binarr, int blen, bool verbose) { + wiegand_message_t packed; + memset(&packed, 0, sizeof(packed)); + packed.Length = blen; + + for (int i = 0; i < blen; i++) { + if (set_bit_by_position(&packed, binarr[i], i) == false) { + PrintAndLogEx(ERR, "Binary string must be less than or equal to %u bits", PACS_MAX_WIEGAND_BITS); + return PM3_EINVARG; + } + } + + return wiegand_encode_new_pacs(&packed, verbose); +} int CmdWiegandList(const char *Cmd) { CLIParserContext *ctx; @@ -98,17 +158,22 @@ int CmdWiegandEncode(const char *Cmd) { CLIParserInit(&ctx, "wiegand encode", "Encode wiegand formatted number to raw hex", "wiegand encode --fc 101 --cn 1337 -> show all formats\n" - "wiegand encode -w H10301 --fc 101 --cn 1337 -> H10301 format " + "wiegand encode -w H10301 --fc 101 --cn 1337 -> H10301 format\n" + "wiegand encode --bin 1 -> raw wiegand hex with sentinel\n" + "wiegand encode -w H10301 --fc 123 --cn 4567 --new -> new ASN.1 encoded format" ); void *argtable[] = { arg_param_begin, + arg_str0("b", "bin", "", "binary string to be encoded"), arg_u64_0(NULL, "fc", "", "facility number"), - arg_u64_1(NULL, "cn", "", "card number"), + arg_u64_0(NULL, "cn", "", "card number"), arg_u64_0(NULL, "issue", "", "issue level"), arg_u64_0(NULL, "oem", "", "OEM code"), arg_str0("w", "wiegand", "", "see `wiegand list` for available formats"), + arg_lit0("n", "new", "encode to new ASN.1 encoded format"), arg_lit0(NULL, "pre", "add HID ProxII preamble to wiegand output"), + arg_lit0("v", "verbose", "verbose output"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -116,17 +181,44 @@ int CmdWiegandEncode(const char *Cmd) { wiegand_card_t data; memset(&data, 0, sizeof(wiegand_card_t)); - data.FacilityCode = arg_get_u32_def(ctx, 1, 0); - data.CardNumber = arg_get_u64_def(ctx, 2, 0); - data.IssueLevel = arg_get_u32_def(ctx, 3, 0); - data.OEM = arg_get_u32_def(ctx, 4, 0); + uint8_t binarr[PACS_MAX_WIEGAND_BITS] = {0}; + int blen = 0; + int res = CLIParamBinToBuf(arg_get_str(ctx, 1), binarr, ARRAYLEN(binarr), &blen); + + data.FacilityCode = arg_get_u32_def(ctx, 2, 0); + data.CardNumber = arg_get_u64_def(ctx, 3, 0); + data.IssueLevel = arg_get_u32_def(ctx, 4, 0); + data.OEM = arg_get_u32_def(ctx, 5, 0); int len = 0; char format[16] = {0}; - CLIParamStrToBuf(arg_get_str(ctx, 5), (uint8_t *)format, sizeof(format), &len); - bool preamble = arg_get_lit(ctx, 6); + CLIParamStrToBuf(arg_get_str(ctx, 6), (uint8_t *)format, sizeof(format), &len); + + bool new_pacs = arg_get_lit(ctx, 7); + bool preamble = arg_get_lit(ctx, 8); + bool verbose = arg_get_lit(ctx, 9); CLIParserFree(ctx); + if (res) { + PrintAndLogEx(FAILED, "Error parsing binary string"); + return PM3_EINVARG; + } + + if (new_pacs && preamble) { + PrintAndLogEx(ERR, "`--new` and `--pre` can't be combined"); + return PM3_EINVARG; + } + + if (blen && (len || data.FacilityCode || data.CardNumber || data.IssueLevel || data.OEM || preamble)) { + PrintAndLogEx(ERR, "`--bin` can't be combined with format, field, or preamble options"); + return PM3_EINVARG; + } + + if (blen == 0 && len == 0 && data.CardNumber == 0 && data.FacilityCode == 0 && data.IssueLevel == 0 && data.OEM == 0) { + PrintAndLogEx(ERR, "Must provide either card data, a specific format, or `--bin`"); + return PM3_EINVARG; + } + int idx = -1; if (len) { idx = HIDFindCardFormat(format); @@ -136,13 +228,26 @@ int CmdWiegandEncode(const char *Cmd) { } } - if (idx != -1) { + if (new_pacs && idx == -1 && blen == 0) { + PrintAndLogEx(ERR, "`--new` requires either `--bin` or a specific wiegand format"); + return PM3_EINVARG; + } + + if (blen) { + if (new_pacs) { + return wiegand_encode_new_pacs_from_bin(binarr, blen, verbose); + } + return wiegand_print_raw_from_bin(binarr, blen); + } else if (idx != -1) { wiegand_message_t packed; memset(&packed, 0, sizeof(wiegand_message_t)); if (HIDPack(idx, &data, &packed, preamble) == false) { PrintAndLogEx(WARNING, "The card data could not be encoded in the selected format."); return PM3_ESOFT; } + if (new_pacs) { + return wiegand_encode_new_pacs(&packed, verbose); + } print_wiegand_code(&packed); } else { // try all formats and print only the ones that work. @@ -157,14 +262,14 @@ int CmdWiegandDecode(const char *Cmd) { CLIParserInit(&ctx, "wiegand decode", "Decode raw hex or binary to wiegand format", "wiegand decode --raw 2006F623AE\n" - "wiegand decode --new 06BD88EB80 -> 4..8 bytes, new padded format " + "wiegand decode --new 06BD88EB80 -> 4..13 bytes, new ASN.1 encoded format " ); void *argtable[] = { arg_param_begin, arg_str0("r", "raw", "", "raw hex to be decoded"), arg_str0("b", "bin", "", "binary string to be decoded"), - arg_str0("n", "new", "", "new padded pacs as raw hex to be decoded"), + arg_str0("n", "new", "", "new ASN.1 encoded data as raw hex to be decoded"), arg_lit0("f", "force", "skip preabmle checking, brute force all possible lengths for raw hex input"), arg_param_end }; @@ -174,11 +279,11 @@ int CmdWiegandDecode(const char *Cmd) { CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)hex, sizeof(hex), &hlen); int blen = 0; - uint8_t binarr[100] = {0x00}; + uint8_t binarr[WIEGAND_MAX_ENCODED_BITS] = {0x00}; int res = CLIParamBinToBuf(arg_get_str(ctx, 2), binarr, sizeof(binarr), &blen); int plen = 0; - uint8_t phex[8] = {0}; + uint8_t phex[PACS_LONG_FORMAT] = {0}; res = CLIParamHexToBuf(arg_get_str(ctx, 3), phex, sizeof(phex), &plen); bool no_preamble = arg_get_lit(ctx, 4); @@ -193,6 +298,10 @@ int CmdWiegandDecode(const char *Cmd) { uint32_t top = 0, mid = 0, bot = 0; if (hlen) { + if ((hlen * 4) > PACS_MAX_WIEGAND_BITS) { + PrintAndLogEx(ERR, "Raw hex decode supports up to %u Wiegand bits", PACS_MAX_WIEGAND_BITS); + return PM3_EINVARG; + } res = hexstring_to_u96(&top, &mid, &bot, hex); if (res != hlen) { PrintAndLogEx(ERR, "Hex string contains none hex chars"); @@ -200,11 +309,15 @@ int CmdWiegandDecode(const char *Cmd) { } if (no_preamble) { - // pass hex input length as is and brute force all possible lengths + // Pass the input hex length through so decode_wiegand() brute-forces + // the possible bit lengths instead of assuming a preamble-encoded value. blen = -hlen; } - } else if (blen) { + if (blen > PACS_MAX_WIEGAND_BITS) { + PrintAndLogEx(ERR, "Binary decode supports up to %u Wiegand bits", PACS_MAX_WIEGAND_BITS); + return PM3_EINVARG; + } int n = binarray_to_u96(&top, &mid, &bot, binarr, blen); if (n != blen) { PrintAndLogEx(ERR, "Binary string contains none <0|1> chars"); diff --git a/client/src/wiegand_formats.c b/client/src/wiegand_formats.c index 7cecb4825..88f52aaa1 100644 --- a/client/src/wiegand_formats.c +++ b/client/src/wiegand_formats.c @@ -1796,6 +1796,14 @@ bool decode_wiegand(uint32_t top, uint32_t mid, uint32_t bot, int n) { } int HIDDumpPACSBits(const uint8_t *const data, const uint8_t length, bool verbose) { + // PACS is encoded as a 1-byte pad count followed by at least 1 byte of payload. + // Reject malformed inputs here so trimming the pad bits below cannot index before + // the start of the binary buffer. + if (length < 2 || data[0] > 0x07) { + PrintAndLogEx(ERR, "Invalid PACS value"); + return PM3_EINVARG; + } + uint8_t n = length - 1; uint8_t pad = data[0]; char *binstr = (char *)calloc((length * 8) + 1, sizeof(uint8_t)); @@ -1815,13 +1823,19 @@ int HIDDumpPACSBits(const uint8_t *const data, const uint8_t length, bool verbos PrintAndLogEx(DEBUG, "bin.............. " _GREEN_("%s") " ( %zu )", binstr, strlen(binstr)); size_t hexlen = 0; - uint8_t hex[16] = {0}; + uint8_t *hex = calloc(n ? n : 1, sizeof(uint8_t)); + if (hex == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + free(binstr); + return PM3_EMALLOC; + } binstr_2_bytes(hex, &hexlen, binstr); PrintAndLogEx(SUCCESS, "hex.............. " _GREEN_("%s"), sprint_hex_inrow(hex, hexlen)); uint32_t top = 0, mid = 0, bot = 0; if (binstring_to_u96(&top, &mid, &bot, binstr) != strlen(binstr)) { PrintAndLogEx(ERR, "Binary string contains none <0|1> chars"); + free(hex); free(binstr); return PM3_EINVARG; } @@ -1850,25 +1864,36 @@ int HIDDumpPACSBits(const uint8_t *const data, const uint8_t length, bool verbos // MIFARE DESFire // MIFARE Classic - char mfcbin[28] = {0}; - mfcbin[0] = '1'; - memcpy(mfcbin + 1, binstr, strlen(binstr)); - binstr_2_bytes(hex, &hexlen, mfcbin); + size_t binstrlen = strlen(binstr); + // Match hf mf encodehid: prepend the sentinel bit, pack to bytes, then right-align + // the result in the 15-byte payload area after the leading 0x02 marker in block 5. + // Fixed-size local buffers are enough for the current 96-bit Wiegand limit: + // 96 data bits + 1 sentinel + NUL = 98 chars, packed into at most 13 bytes. + if (binstrlen <= 96) { + char mfcbin[98] = {0}; + uint8_t mfcpayload[15] = {0}; + size_t mfchexlen = 0; - PrintAndLogEx(INFO, "Downgrade to " _YELLOW_("MIFARE Classic") " (Pm3 simulation)"); - PrintAndLogEx(SUCCESS, " hf mf eclr;"); - PrintAndLogEx(SUCCESS, " hf mf esetblk --blk 0 -d 049DBA42A23E80884400C82000000000;"); - PrintAndLogEx(SUCCESS, " hf mf esetblk --blk 1 -d 1B014D48000000000000000000000000;"); - PrintAndLogEx(SUCCESS, " hf mf esetblk --blk 3 -d A0A1A2A3A4A5787788C189ECA97F8C2A;"); - PrintAndLogEx(SUCCESS, " hf mf esetblk --blk 5 -d 020000000000000000000000%s;", sprint_hex_inrow(hex, hexlen)); - PrintAndLogEx(SUCCESS, " hf mf esetblk --blk 7 -d 484944204953787788AA204752454154;"); - PrintAndLogEx(SUCCESS, " hf mf sim --1k -i;"); - PrintAndLogEx(NORMAL, ""); + mfcbin[0] = '1'; + memcpy(mfcbin + 1, binstr, binstrlen); + binstr_2_bytes(mfcpayload + (sizeof(mfcpayload) - ((binstrlen + 1 + 7) / 8)), &mfchexlen, mfcbin); + + PrintAndLogEx(INFO, "Downgrade to " _YELLOW_("MIFARE Classic") " (Pm3 simulation)"); + PrintAndLogEx(SUCCESS, " hf mf eclr;"); + PrintAndLogEx(SUCCESS, " hf mf esetblk --blk 0 -d 049DBA42A23E80884400C82000000000;"); + PrintAndLogEx(SUCCESS, " hf mf esetblk --blk 1 -d 1B014D48000000000000000000000000;"); + PrintAndLogEx(SUCCESS, " hf mf esetblk --blk 3 -d A0A1A2A3A4A5787788C189ECA97F8C2A;"); + PrintAndLogEx(SUCCESS, " hf mf esetblk --blk 5 -d 02%s;", sprint_hex_inrow(mfcpayload, sizeof(mfcpayload))); + PrintAndLogEx(SUCCESS, " hf mf esetblk --blk 7 -d 484944204953787788AA204752454154;"); + PrintAndLogEx(SUCCESS, " hf mf sim --1k -i;"); + PrintAndLogEx(NORMAL, ""); + } PrintAndLogEx(INFO, "Downgrade to " _YELLOW_("MIFARE Classic 1K")); PrintAndLogEx(SUCCESS, " hf mf encodehid --bin %s", binstr); PrintAndLogEx(NORMAL, ""); } + free(hex); free(binstr); return PM3_SUCCESS; } diff --git a/tools/pm3_tests.sh b/tools/pm3_tests.sh index 2f27a6389..e94d5096e 100755 --- a/tools/pm3_tests.sh +++ b/tools/pm3_tests.sh @@ -474,8 +474,24 @@ while true; do if ! CheckExecute "nfc decode test - signature" "$CLIENTBIN -c 'nfc decode -d 03FF010194113870696C65742E65653A656B616172743A3266195F26063132303832325904202020205F28033233335F2701316E1B5A13333038363439303039303030323636343030355304EBF2CE704103000000AC536967010200803A2448FCA7D354A654A81BD021150D1A152D1DF4D7A55D2B771F12F094EAB6E5E10F2617A2F8DAD4FD38AFF8EA39B71C19BD42618CDA86EE7E144636C8E0E7CFC4096E19C3680E09C78A0CDBC05DA2D698E551D5D709717655E56FE3676880B897D2C70DF5F06ECE07C71435255144F8EE41AF110E7B180DA0E6C22FB8FDEF61800025687474703A2F2F70696C65742E65652F6372742F33303836343930302D303030312E637274FE'" "30864900-0001.crt"; then break; fi if ! CheckExecute "nfc decode test - openprinter tag" "$CLIENTBIN -c 'nfc decode -d 03FF012F91013A55046E756D616B6572732E636F6D2F70726F64756374732F6162732D66696C616D656E743F76617269616E743D3436393434323937333836323932521CD26170706C69636174696F6E2F766E642E6F70656E7072696E74746167A10218AFBF041B000007D0FCAB45F9080009020A70414253204C656D6F6E2059656C6C6F770B684E756D616B6572730E1A69094200101903E81119041A1218F01343F9A800181DF93C29182218F01823190104182418AA1825185A18261864FF00'" "application/vnd.openprinttag"; then break; fi + if ! CheckExecute "wiegand encode test - new" "$CLIENTBIN -c 'wiegand encode -w H10301 --fc 123 --cn 4567 --new'" "New PACS\\.{9} 0x 06BD88EB80"; then break; fi + if ! CheckExecute "wiegand encode test - bin" "$CLIENTBIN -c 'wiegand encode --bin 1'" "Wiegand raw\\.{4} 03"; then break; fi + if ! CheckExecute "wiegand encode test - new bin" "$CLIENTBIN -c 'wiegand encode --bin 1 --new'" "New PACS\\.{9} 0x 0780"; then break; fi + if ! CheckExecute "wiegand encode test - new bin verbose hdr" "$CLIENTBIN -c 'wiegand encode --bin 1 --new --verbose'" "New PACS"; then break; fi + if ! CheckExecute "wiegand encode test - new bin verbose pad" "$CLIENTBIN -c 'wiegand encode --bin 1 --new --verbose'" "With Sentinel\\.{4} 0b 00000011 \\(8-bit\\)"; then break; fi + if ! CheckExecute "wiegand encode test - new bin verbose raw" "$CLIENTBIN -c 'wiegand encode --bin 1 --new --verbose'" "Wiegand --raw\\.{4} 0x 03"; then break; fi + if ! CheckExecute "wiegand encode test - new bin verbose bin" "$CLIENTBIN -c 'wiegand encode --bin 1 --new --verbose'" "Without Sentinel\\. 0b 1 \\(1-bit\\)"; then break; fi + if ! CheckExecute "wiegand encode test - bin 96-bit" "PAT=\$(printf '01%.0s' {1..48}); $CLIENTBIN -c \"wiegand encode --bin \$PAT\"" "Wiegand raw\\.{4} 01555555555555555555555555"; then break; fi + if ! CheckExecute "wiegand encode test - new bin 96-bit" "PAT=\$(printf '01%.0s' {1..48}); $CLIENTBIN -c \"wiegand encode --bin \$PAT --new\"" "New PACS\\.{9} 0x 00555555555555555555555555"; then break; fi + if ! CheckExecute "wiegand encode test - new bin 96-bit verbose pad" "PAT=\$(printf '01%.0s' {1..48}); $CLIENTBIN -c \"wiegand encode --bin \$PAT --new --verbose\"" "With Sentinel\\.{4} 0b 00000001010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101 \\(104-bit\\)"; then break; fi + if ! CheckExecute "wiegand encode test - new bin 96-bit verbose raw" "PAT=\$(printf '01%.0s' {1..48}); $CLIENTBIN -c \"wiegand encode --bin \$PAT --new --verbose\"" "Wiegand --raw\\.{4} 0x 01555555555555555555555555"; then break; fi + if ! CheckExecute "wiegand encode test - new 48-bit" "$CLIENTBIN -c 'wiegand encode -w C1k48s --fc 42069 --cn 42069 --new'" "New PACS\\.{9} 0x 0000A4550148AB"; then break; fi + if ! CheckExecute "wiegand decode test - raw over 96-bit" "$CLIENTBIN -c 'wiegand decode --raw 01555555555555555555555555' 2>&1" "Raw hex decode supports up to 96 Wiegand bits"; then break; fi if ! CheckExecute "wiegand decode test - raw" "$CLIENTBIN -c 'wiegand decode --raw 2006F623AE'" "FC: 123 CN: 4567 parity \( ok \)"; then break; fi + if ! CheckExecute "wiegand decode test - bin over 96-bit" "PAT=\$(printf '01%.0s' {1..49}); $CLIENTBIN -c \"wiegand decode --bin \$PAT\" 2>&1" "Binary decode supports up to 96 Wiegand bits"; then break; fi if ! CheckExecute "wiegand decode test - new" "$CLIENTBIN -c 'wiegand decode --new 06BD88EB80'" "FC: 123 CN: 4567 parity \( ok \)"; then break; fi + if ! CheckExecute "wiegand decode test - new 96-bit" "$CLIENTBIN -c 'wiegand decode --new 00555555555555555555555555'" "hex\\.{14} 555555555555555555555555"; then break; fi + if ! CheckExecute "wiegand decode test - new 48-bit" "$CLIENTBIN -c 'wiegand decode --new 0000A4550148AB'" "C1k48s.*FC: 42069 CN: 42069 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