diff --git a/lib/lfrfid/protocols/lfrfid_protocols.c b/lib/lfrfid/protocols/lfrfid_protocols.c index 175d64c2b27..f0c28f6753a 100644 --- a/lib/lfrfid/protocols/lfrfid_protocols.c +++ b/lib/lfrfid/protocols/lfrfid_protocols.c @@ -18,6 +18,7 @@ #include "protocol_keri.h" #include "protocol_gallagher.h" #include "protocol_nexwatch.h" +#include "protocol_securakey.h" const ProtocolBase* lfrfid_protocols[] = { [LFRFIDProtocolEM4100] = &protocol_em4100, @@ -41,4 +42,5 @@ const ProtocolBase* lfrfid_protocols[] = { [LFRFIDProtocolKeri] = &protocol_keri, [LFRFIDProtocolGallagher] = &protocol_gallagher, [LFRFIDProtocolNexwatch] = &protocol_nexwatch, + [LFRFIDProtocolSecurakey] = &protocol_securakey, }; diff --git a/lib/lfrfid/protocols/lfrfid_protocols.h b/lib/lfrfid/protocols/lfrfid_protocols.h index 89aa51c2518..c90e842af15 100644 --- a/lib/lfrfid/protocols/lfrfid_protocols.h +++ b/lib/lfrfid/protocols/lfrfid_protocols.h @@ -29,6 +29,7 @@ typedef enum { LFRFIDProtocolKeri, LFRFIDProtocolGallagher, LFRFIDProtocolNexwatch, + LFRFIDProtocolSecurakey, LFRFIDProtocolMax, } LFRFIDProtocol; diff --git a/lib/lfrfid/protocols/protocol_securakey.c b/lib/lfrfid/protocols/protocol_securakey.c new file mode 100644 index 00000000000..55048a592f1 --- /dev/null +++ b/lib/lfrfid/protocols/protocol_securakey.c @@ -0,0 +1,294 @@ +// The timing parameters and data structure used in this file +// are based on the knowledge found in Proxmark3's firmware: +// https://github.com/RfidResearchGroup/proxmark3/blob/1c52152d30f7744c0336633317ea6640dbcdc796/client/src/cmdlfsecurakey.c +// PM3's repo has mentioned the existence of non-26-or-32-bit formats. +// Those are not supported here for preventing false positives. +#include +#include +#include +#include +#include "lfrfid_protocols.h" +#include + +#define TAG "SECURAKEY" +#define SECURAKEY_ENCODED_SIZE_BITS (84) +#define SECURAKEY_PREAMBLE_SIZE_BITS (12) +#define SECURAKEY_ENCODED_FULL_SIZE_BITS \ + (SECURAKEY_ENCODED_SIZE_BITS + SECURAKEY_PREAMBLE_SIZE_BITS) +#define SECURAKEY_ENCODED_FULL_SIZE_BYTE (SECURAKEY_ENCODED_FULL_SIZE_BITS / 8) +#define SECURAKEY_DECODED_DATA_SIZE_BITS \ + (48) // 16-bit for facility code/number, 16-bit for card number, 16-bit for two checksum +#define SECURAKEY_DECODED_DATA_SIZE_BYTES (SECURAKEY_DECODED_DATA_SIZE_BITS / 8) +#define LFRFID_FREQUENCY (125000) +#define SECURAKEY_CLOCK_PER_BIT (40) // RF/40 +#define SECURAKEY_READ_LONG_TIME \ + (1000000 / (LFRFID_FREQUENCY / SECURAKEY_CLOCK_PER_BIT)) // 1000000 micro sec / sec +#define SECURAKEY_READ_SHORT_TIME (SECURAKEY_READ_LONG_TIME / 2) +#define SECURAKEY_READ_JITTER_TIME (SECURAKEY_READ_SHORT_TIME * 40 / 100) // 40% jitter tolerance +#define SECURAKEY_READ_SHORT_TIME_LOW \ + (SECURAKEY_READ_SHORT_TIME - \ + SECURAKEY_READ_JITTER_TIME) // these are used for manchester decoding +#define SECURAKEY_READ_SHORT_TIME_HIGH (SECURAKEY_READ_SHORT_TIME + SECURAKEY_READ_JITTER_TIME) +#define SECURAKEY_READ_LONG_TIME_LOW (SECURAKEY_READ_LONG_TIME - SECURAKEY_READ_JITTER_TIME) +#define SECURAKEY_READ_LONG_TIME_HIGH (SECURAKEY_READ_LONG_TIME + SECURAKEY_READ_JITTER_TIME) + +typedef struct { + uint8_t data[SECURAKEY_DECODED_DATA_SIZE_BYTES]; + uint8_t encoded_data[SECURAKEY_ENCODED_FULL_SIZE_BYTE]; + uint8_t encoded_data_index; + bool encoded_polarity; + ManchesterState decoder_manchester_state; + uint8_t bit_format; +} ProtocolSecurakey; + +ProtocolSecurakey* protocol_securakey_alloc(void) { + ProtocolSecurakey* protocol = malloc(sizeof(ProtocolSecurakey)); + return (void*)protocol; +}; + +void protocol_securakey_free(ProtocolSecurakey* protocol) { + free(protocol); +}; + +uint8_t* protocol_securakey_get_data(ProtocolSecurakey* protocol) { + return protocol->data; +}; + +static bool protocol_securakey_can_be_decoded(ProtocolSecurakey* protocol) { + // check 11 bits preamble + if(bit_lib_get_bits_16(protocol->encoded_data, 0, SECURAKEY_PREAMBLE_SIZE_BITS) == + 0b011111111100) { + if(bit_lib_get_bits(protocol->encoded_data, 13, 6) == 26 || + bit_lib_get_bits(protocol->encoded_data, 13, 6) == 32) { + return true; + } else { + return false; + } + } else { + return false; + } +}; + +static void protocol_securakey_decode(ProtocolSecurakey* protocol) { + memset(protocol->data, 0, SECURAKEY_DECODED_DATA_SIZE_BYTES); + // encoded_data looks like this (citation: pm3 repo): + // 26-bit format (1-bit even parity bit, 8-bit facility number, 16-bit card number, 1-bit odd parity bit) + // preamble ??bitlen reserved EPf fffffffc cccccccc cccccccOP CS? CS2? + // 0111111111 0 01011010 0 00000000 0 00000010 0 00110110 0 00111110 0 01100010 0 00001111 0 01100000 0 00000000 0 0000 + + // 32-bit format (1-bit even parity bit, 14-bit facility number, 16-bit card number, 1-bit odd parity bit) + // preamble ??bitlen reserved EPfffffff fffffffc cccccccc cccccccOP CS? CS2? + // 0111111111 0 01100000 0 00000000 0 10000100 0 11001010 0 01011011 0 01010110 0 00010110 0 11100000 0 00000000 0 0000 + + // left two 0 paddings in the beginning for easier parsing (00011010 = 011010) + // get facility number (f) + if(bit_lib_get_bits(protocol->encoded_data, 13, 6) == 26) { + FURI_LOG_D(TAG, "26-bit Securakey detected"); + protocol->bit_format = 26; + bit_lib_copy_bits(protocol->data, 8, 1, protocol->encoded_data, 36); + // have to skip one spacer + bit_lib_copy_bits(protocol->data, 9, 7, protocol->encoded_data, 38); + } else if(bit_lib_get_bits(protocol->encoded_data, 13, 6) == 32) { + FURI_LOG_D(TAG, "32-bit Securakey detected"); + protocol->bit_format = 32; + // same two 0 paddings here, otherwise should be bit_lib_copy_bits(protocol->data, 8, 7, protocol->encoded_data, 30); + bit_lib_copy_bits(protocol->data, 2, 7, protocol->encoded_data, 30); + // have to skip one spacer + bit_lib_copy_bits(protocol->data, 9, 7, protocol->encoded_data, 38); + } + + // get card number (c) + bit_lib_copy_bits(protocol->data, 16, 1, protocol->encoded_data, 45); + // same skips here + bit_lib_copy_bits(protocol->data, 17, 8, protocol->encoded_data, 47); + bit_lib_copy_bits(protocol->data, 25, 7, protocol->encoded_data, 56); + + // unsure about CS yet, might as well just save it + // CS1 + bit_lib_copy_bits(protocol->data, 32, 8, protocol->encoded_data, 65); + // CS2 + bit_lib_copy_bits(protocol->data, 40, 8, protocol->encoded_data, 74); + + // (decoded) data looks like this (pp are zero paddings): + // 26-bit format (1-bit EP, 8-bit facility number, 16-bit card number, 1-bit OP) + // pppppppp ffffffff cccccccc cccccccc CS1 CS2 + // 00000000 00011011 00011111 00110001 00001111 01100000 + + // 32-bit format (1-bit EP, 14-bit facility number, 16-bit card number, 1-bit OP) + // ppffffff ffffffff cccccccc cccccccc CS1 CS2 + // 00000010 01100101 00101101 10101011 00010110 11100000 +}; + +void protocol_securakey_decoder_start(ProtocolSecurakey* protocol) { + memset(protocol->encoded_data, 0, SECURAKEY_ENCODED_FULL_SIZE_BYTE); + manchester_advance( + protocol->decoder_manchester_state, + ManchesterEventReset, + &protocol->decoder_manchester_state, + NULL); +}; + +bool protocol_securakey_decoder_feed(ProtocolSecurakey* protocol, bool level, uint32_t duration) { + bool result = false; + // this is where we do manchester demodulation on already ASK-demoded data + ManchesterEvent event = ManchesterEventReset; + if(duration > SECURAKEY_READ_SHORT_TIME_LOW && duration < SECURAKEY_READ_SHORT_TIME_HIGH) { + if(!level) { + event = ManchesterEventShortHigh; + } else { + event = ManchesterEventShortLow; + } + } else if(duration > SECURAKEY_READ_LONG_TIME_LOW && duration < SECURAKEY_READ_LONG_TIME_HIGH) { + if(!level) { + event = ManchesterEventLongHigh; + } else { + event = ManchesterEventLongLow; + } + } + // append a new bit to the encoded bit stream + if(event != ManchesterEventReset) { + bool data; + bool data_ok = manchester_advance( + protocol->decoder_manchester_state, event, &protocol->decoder_manchester_state, &data); + if(data_ok) { + bit_lib_push_bit(protocol->encoded_data, SECURAKEY_ENCODED_FULL_SIZE_BYTE, data); + if(protocol_securakey_can_be_decoded(protocol)) { + protocol_securakey_decode(protocol); + result = true; + } + } + } + return result; +}; + +void protocol_securakey_render_data(ProtocolSecurakey* protocol, FuriString* result) { + if(bit_lib_get_bits(protocol->data, 0, 8) == 0) { + protocol->bit_format = 26; + } else { + protocol->bit_format = 32; + } + furi_string_printf( + result, + "%u-bit format\nFacility code: %u\nCard number: %u", + protocol->bit_format, + bit_lib_get_bits_16(protocol->data, 0, 16), + bit_lib_get_bits_16(protocol->data, 16, 16)); +}; + +bool protocol_securakey_encoder_start(ProtocolSecurakey* protocol) { + // set all of our encoded_data bits to zeros. + memset(protocol->encoded_data, 0, SECURAKEY_ENCODED_FULL_SIZE_BYTE); + + // write the preamble to the beginning of the encoded_data + bit_lib_set_bits(protocol->encoded_data, 0, 0b01111111, 8); + bit_lib_set_bits(protocol->encoded_data, 8, 0b11001, 5); + + if(bit_lib_get_bits(protocol->data, 0, 8) == 0) { + protocol->bit_format = 26; + // set bit length + bit_lib_set_bits(protocol->encoded_data, 13, protocol->bit_format, 6); + // set even parity & odd parity + if(!bit_lib_test_parity(protocol->data, 8, 12, BitLibParityOdd, 12)) { + bit_lib_set_bit(protocol->encoded_data, 35, 1); + } + if(bit_lib_test_parity(protocol->data, 20, 12, BitLibParityOdd, 12)) { + bit_lib_set_bit(protocol->encoded_data, 63, 1); + } + // write facility number (f) + bit_lib_copy_bits(protocol->encoded_data, 36, 1, protocol->data, 8); + // have to skip one spacer + bit_lib_copy_bits(protocol->encoded_data, 38, 7, protocol->data, 9); + + } else { + protocol->bit_format = 32; + // set bit length + bit_lib_set_bits(protocol->encoded_data, 13, protocol->bit_format, 6); + // set EP & OP + if(!bit_lib_test_parity(protocol->data, 2, 15, BitLibParityOdd, 15)) { + bit_lib_set_bit(protocol->encoded_data, 29, 1); + } + if(bit_lib_test_parity(protocol->data, 17, 15, BitLibParityOdd, 15)) { + bit_lib_set_bit(protocol->encoded_data, 63, 1); + } + // write facility number (f) + bit_lib_copy_bits(protocol->encoded_data, 30, 7, protocol->data, 2); + // have to skip one spacer + bit_lib_copy_bits(protocol->encoded_data, 38, 7, protocol->data, 3); + } + + // write card number (c) + bit_lib_copy_bits(protocol->encoded_data, 45, 1, protocol->data, 16); + // same skips here + bit_lib_copy_bits(protocol->encoded_data, 47, 8, protocol->data, 17); + bit_lib_copy_bits(protocol->encoded_data, 56, 7, protocol->data, 25); + + // unsure about CS yet might as well just copy it from saved + // CS1 + bit_lib_copy_bits(protocol->encoded_data, 65, 8, protocol->data, 32); + // CS2 + bit_lib_copy_bits(protocol->encoded_data, 74, 8, protocol->data, 40); + + // for sending we start at bit 0. + protocol->encoded_data_index = 0; + protocol->encoded_polarity = true; + return true; +}; + +LevelDuration protocol_securakey_encoder_yield(ProtocolSecurakey* protocol) { + bool level = bit_lib_get_bit(protocol->encoded_data, protocol->encoded_data_index); + uint32_t duration = SECURAKEY_CLOCK_PER_BIT / 2; + if(protocol->encoded_polarity) { + protocol->encoded_polarity = false; + } else { + level = !level; + protocol->encoded_polarity = true; + bit_lib_increment_index(protocol->encoded_data_index, SECURAKEY_ENCODED_FULL_SIZE_BITS); + } + return level_duration_make(level, duration); +}; + +bool protocol_securakey_write_data(ProtocolSecurakey* protocol, void* data) { + LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data; + bool result = false; + // Correct protocol data by redecoding + protocol_securakey_encoder_start(protocol); + protocol_securakey_decode(protocol); + protocol_securakey_encoder_start(protocol); + // Write T5577 + if(request->write_type == LFRFIDWriteTypeT5577) { + request->t5577.block[0] = + (LFRFID_T5577_MODULATION_MANCHESTER | LFRFID_T5577_BITRATE_RF_40 | + (3 + << LFRFID_T5577_MAXBLOCK_SHIFT)); // we only need 3 32-bit blocks for our 96-bit encoded data + request->t5577.block[1] = bit_lib_get_bits_32(protocol->encoded_data, 0, 32); + request->t5577.block[2] = bit_lib_get_bits_32(protocol->encoded_data, 32, 32); + request->t5577.block[3] = bit_lib_get_bits_32(protocol->encoded_data, 64, 32); + request->t5577.blocks_to_write = 4; + result = true; + } + return result; +}; + +const ProtocolBase protocol_securakey = { + .name = "Radio Key", + .manufacturer = "Securakey", + .data_size = SECURAKEY_DECODED_DATA_SIZE_BYTES, + .features = LFRFIDFeatureASK, + .validate_count = 3, + .alloc = (ProtocolAlloc)protocol_securakey_alloc, + .free = (ProtocolFree)protocol_securakey_free, + .get_data = (ProtocolGetData)protocol_securakey_get_data, + .decoder = + { + .start = (ProtocolDecoderStart)protocol_securakey_decoder_start, + .feed = (ProtocolDecoderFeed)protocol_securakey_decoder_feed, + }, + .encoder = + { + .start = (ProtocolEncoderStart)protocol_securakey_encoder_start, + .yield = (ProtocolEncoderYield)protocol_securakey_encoder_yield, + }, + .render_data = (ProtocolRenderData)protocol_securakey_render_data, + .render_brief_data = (ProtocolRenderData)protocol_securakey_render_data, + .write_data = (ProtocolWriteData)protocol_securakey_write_data, +}; \ No newline at end of file diff --git a/lib/lfrfid/protocols/protocol_securakey.h b/lib/lfrfid/protocols/protocol_securakey.h new file mode 100644 index 00000000000..ad990f119c1 --- /dev/null +++ b/lib/lfrfid/protocols/protocol_securakey.h @@ -0,0 +1,4 @@ +#pragma once +#include + +extern const ProtocolBase protocol_securakey; \ No newline at end of file