mirror of
https://github.com/DarkFlippers/unleashed-firmware.git
synced 2026-11-14 12:27:33 +00:00
Add Manually now asks whether to enter a card as FC/ID or as hex. FC/ID takes the numbers printed on the card, one number input per field, and lays them out in the protocol data itself: EM4100, HID H10301, ioProx XSF, AWID and Pyramid 26-bit, and Gallagher. Parity and checksums are added by the firmware encoders as for hex-entered data. Formats whose scrambling and checksums are not exported by the protocol library go straight to hex as before. HID H10302, H10304, H10306, AMAG S10401 and Corporate 1000 35-bit are read on top of the firmware's Generic HIDProx protocol, whose data is the raw 44-bit field. The read and saved-card screens add a line for every format the frame fits by length and parity; a 37-bit frame fits more than one, so the second and later readings are prefixed "or". Layouts follow the Proxmark3 wiegand_formats.c reference except S10401, whose even parity covers bits 1-17 rather than 18: of 42 badges exported from a live access system, all 42 pass that and only the 13 with bit 18 clear pass Proxmark3's. Casi-Rusco C10106 badges transmit a plain EM4100 frame at RF/32, so the app reads the badge id out of those 40 bits - a 19-bit credential beginning with 15, then a 19-bit card field, less 66606 when its top bit is set. The frame carries no parity, so the line is shown as the alternative reading and the credential range keeps it off almost all other EM4100 cards. The Generic HIDProx render now gives the frame length alone. Its "Data:" line repeated the hex every caller already prints, and read the frame one nibble at a time from offsets that assumed a full first nibble, so a frame whose length is not a multiple of four lost bits after its first digit. A unit test covers every size header. A number input whose range excludes 0 opened on an empty field that could not be confirmed, because 0 is drawn as empty and the starting value was only clamped when it was not 0. It is now clamped unconditionally, so the field is only ever empty when 0 is in range, and an empty field reads back as 0 and is range-checked like any other value. Co-authored-by: matthewb-worthe <135917560+matthewb-worthe@users.noreply.github.com> Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
258 lines
8.6 KiB
C
258 lines
8.6 KiB
C
#include <furi.h>
|
|
#include <toolbox/protocols/protocol.h>
|
|
#include <lfrfid/tools/fsk_demod.h>
|
|
#include <lfrfid/tools/fsk_osc.h>
|
|
#include "lfrfid_protocols.h"
|
|
#include <bit_lib/bit_lib.h>
|
|
|
|
#define JITTER_TIME (20)
|
|
#define MIN_TIME (64 - JITTER_TIME)
|
|
#define MAX_TIME (80 + JITTER_TIME)
|
|
|
|
#define HID_DATA_SIZE 11
|
|
#define HID_PREAMBLE_SIZE 1
|
|
#define HID_PROTOCOL_SIZE_UNKNOWN 0
|
|
|
|
#define HID_ENCODED_DATA_SIZE (HID_PREAMBLE_SIZE + HID_DATA_SIZE + HID_PREAMBLE_SIZE)
|
|
#define HID_ENCODED_BIT_SIZE ((HID_PREAMBLE_SIZE + HID_DATA_SIZE) * 8)
|
|
#define HID_DECODED_DATA_SIZE (6)
|
|
#define HID_DECODED_BIT_SIZE ((HID_ENCODED_BIT_SIZE - HID_PREAMBLE_SIZE * 8) / 2)
|
|
|
|
#define HID_PREAMBLE 0x1D
|
|
|
|
typedef struct {
|
|
FSKDemod* fsk_demod;
|
|
} ProtocolHIDDecoder;
|
|
|
|
typedef struct {
|
|
FSKOsc* fsk_osc;
|
|
uint8_t encoded_index;
|
|
uint32_t pulse;
|
|
} ProtocolHIDEncoder;
|
|
|
|
typedef struct {
|
|
ProtocolHIDDecoder decoder;
|
|
ProtocolHIDEncoder encoder;
|
|
uint8_t encoded_data[HID_ENCODED_DATA_SIZE];
|
|
uint8_t data[HID_DECODED_DATA_SIZE];
|
|
} ProtocolHID;
|
|
|
|
ProtocolHID* protocol_hid_generic_alloc(void) {
|
|
ProtocolHID* protocol = malloc(sizeof(ProtocolHID));
|
|
protocol->decoder.fsk_demod = fsk_demod_alloc(MIN_TIME, 6, MAX_TIME, 5);
|
|
protocol->encoder.fsk_osc = fsk_osc_alloc(8, 10, 50);
|
|
|
|
return protocol;
|
|
}
|
|
|
|
void protocol_hid_generic_free(ProtocolHID* protocol) {
|
|
fsk_demod_free(protocol->decoder.fsk_demod);
|
|
fsk_osc_free(protocol->encoder.fsk_osc);
|
|
free(protocol);
|
|
}
|
|
|
|
uint8_t* protocol_hid_generic_get_data(ProtocolHID* protocol) {
|
|
return protocol->data;
|
|
}
|
|
|
|
void protocol_hid_generic_decoder_start(ProtocolHID* protocol) {
|
|
memset(protocol->encoded_data, 0, HID_ENCODED_DATA_SIZE);
|
|
}
|
|
|
|
static bool protocol_hid_generic_can_be_decoded(const uint8_t* data) {
|
|
// check preamble
|
|
if(data[0] != HID_PREAMBLE || data[HID_PREAMBLE_SIZE + HID_DATA_SIZE] != HID_PREAMBLE) {
|
|
return false;
|
|
}
|
|
|
|
// check for manchester encoding
|
|
for(size_t i = HID_PREAMBLE_SIZE; i < (HID_PREAMBLE_SIZE + HID_DATA_SIZE); i++) {
|
|
for(size_t n = 0; n < 4; n++) {
|
|
uint8_t bit_pair = (data[i] >> (n * 2)) & 0b11;
|
|
if(bit_pair == 0b11 || bit_pair == 0b00) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static void protocol_hid_generic_decode(const uint8_t* from, uint8_t* to) {
|
|
size_t bit_index = 0;
|
|
for(size_t i = HID_PREAMBLE_SIZE; i < (HID_PREAMBLE_SIZE + HID_DATA_SIZE); i++) {
|
|
for(size_t n = 0; n < 4; n++) {
|
|
uint8_t bit_pair = (from[i] >> (6 - (n * 2))) & 0b11;
|
|
if(bit_pair == 0b01) {
|
|
bit_lib_set_bit(to, bit_index, 0);
|
|
} else if(bit_pair == 0b10) {
|
|
bit_lib_set_bit(to, bit_index, 1);
|
|
}
|
|
bit_index++;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Decodes size from the HID Proximity header:
|
|
* - If any of the first six bits is 1, the key is composed of the bits
|
|
* following the first 1
|
|
* - Otherwise, if the first six bits are 0:
|
|
* - If the seventh bit is 0, the key is composed of the remaining 37 bits.
|
|
* - If the seventh bit is 1, the size header continues until the next 1 bit,
|
|
* and the key is composed of however many bits remain.
|
|
*
|
|
* HID Proximity keys are 26 bits at minimum. If the header implies a key size
|
|
* under 26 bits, this function returns HID_PROTOCOL_SIZE_UNKNOWN.
|
|
*/
|
|
static uint8_t protocol_hid_generic_decode_protocol_size(ProtocolHID* protocol) {
|
|
for(size_t bit_index = 0; bit_index < 6; bit_index++) {
|
|
if(bit_lib_get_bit(protocol->data, bit_index)) {
|
|
return HID_DECODED_BIT_SIZE - bit_index - 1;
|
|
}
|
|
}
|
|
|
|
if(!bit_lib_get_bit(protocol->data, 6)) {
|
|
return 37;
|
|
}
|
|
|
|
size_t bit_index = 7;
|
|
uint8_t size = 36;
|
|
while(!bit_lib_get_bit(protocol->data, bit_index) && size >= 26) {
|
|
size--;
|
|
bit_index++;
|
|
}
|
|
return size < 26 ? HID_PROTOCOL_SIZE_UNKNOWN : size;
|
|
}
|
|
|
|
bool protocol_hid_generic_decoder_feed(ProtocolHID* protocol, bool level, uint32_t duration) {
|
|
bool value;
|
|
uint32_t count;
|
|
bool result = false;
|
|
|
|
fsk_demod_feed(protocol->decoder.fsk_demod, level, duration, &value, &count);
|
|
if(count > 0) {
|
|
for(size_t i = 0; i < count; i++) {
|
|
bit_lib_push_bit(protocol->encoded_data, HID_ENCODED_DATA_SIZE, value);
|
|
if(protocol_hid_generic_can_be_decoded(protocol->encoded_data)) {
|
|
protocol_hid_generic_decode(protocol->encoded_data, protocol->data);
|
|
result = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static void protocol_hid_generic_encode(ProtocolHID* protocol) {
|
|
protocol->encoded_data[0] = HID_PREAMBLE;
|
|
|
|
size_t bit_index = 0;
|
|
for(size_t i = 0; i < HID_DECODED_BIT_SIZE; i++) {
|
|
bool bit = bit_lib_get_bit(protocol->data, i);
|
|
if(bit) {
|
|
bit_lib_set_bit(protocol->encoded_data, 8 + bit_index, 1);
|
|
bit_lib_set_bit(protocol->encoded_data, 8 + bit_index + 1, 0);
|
|
} else {
|
|
bit_lib_set_bit(protocol->encoded_data, 8 + bit_index, 0);
|
|
bit_lib_set_bit(protocol->encoded_data, 8 + bit_index + 1, 1);
|
|
}
|
|
bit_index += 2;
|
|
}
|
|
}
|
|
|
|
bool protocol_hid_generic_encoder_start(ProtocolHID* protocol) {
|
|
protocol->encoder.encoded_index = 0;
|
|
protocol->encoder.pulse = 0;
|
|
protocol_hid_generic_encode(protocol);
|
|
|
|
return true;
|
|
}
|
|
|
|
LevelDuration protocol_hid_generic_encoder_yield(ProtocolHID* protocol) {
|
|
bool level = 0;
|
|
uint32_t duration = 0;
|
|
|
|
// if pulse is zero, we need to output high, otherwise we need to output low
|
|
if(protocol->encoder.pulse == 0) {
|
|
// get bit
|
|
uint8_t bit = bit_lib_get_bit(protocol->encoded_data, protocol->encoder.encoded_index);
|
|
|
|
// get pulse from oscillator
|
|
bool advance = fsk_osc_next(protocol->encoder.fsk_osc, bit, &duration);
|
|
|
|
if(advance) {
|
|
bit_lib_increment_index(protocol->encoder.encoded_index, HID_ENCODED_BIT_SIZE);
|
|
}
|
|
|
|
// duration diveded by 2 because we need to output high and low
|
|
duration = duration / 2;
|
|
protocol->encoder.pulse = duration;
|
|
level = true;
|
|
} else {
|
|
// output low half and reset pulse
|
|
duration = protocol->encoder.pulse;
|
|
protocol->encoder.pulse = 0;
|
|
level = false;
|
|
}
|
|
|
|
return level_duration_make(level, duration);
|
|
}
|
|
|
|
bool protocol_hid_generic_write_data(ProtocolHID* protocol, void* data) {
|
|
LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data;
|
|
bool result = false;
|
|
|
|
// Correct protocol data by redecoding
|
|
protocol_hid_generic_encoder_start(protocol);
|
|
protocol_hid_generic_decode(protocol->encoded_data, protocol->data);
|
|
|
|
protocol_hid_generic_encoder_start(protocol);
|
|
|
|
if(request->write_type == LFRFIDWriteTypeT5577) {
|
|
request->t5577.block[0] = LFRFID_T5577_MODULATION_FSK2a | LFRFID_T5577_BITRATE_RF_50 |
|
|
(3 << LFRFID_T5577_MAXBLOCK_SHIFT);
|
|
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;
|
|
}
|
|
|
|
void protocol_hid_generic_render_data(ProtocolHID* protocol, FuriString* result) {
|
|
// the data itself is shown in hex by every caller, so only the frame length is added
|
|
const uint8_t protocol_size = protocol_hid_generic_decode_protocol_size(protocol);
|
|
|
|
if(protocol_size == HID_PROTOCOL_SIZE_UNKNOWN) {
|
|
furi_string_set(result, "Generic HID Proximity");
|
|
} else {
|
|
furi_string_printf(result, "%hhu-bit HID Proximity", protocol_size);
|
|
}
|
|
}
|
|
|
|
const ProtocolBase protocol_hid_generic = {
|
|
.name = "HIDProx",
|
|
.manufacturer = "Generic",
|
|
.data_size = HID_DECODED_DATA_SIZE,
|
|
.features = LFRFIDFeatureASK,
|
|
.validate_count = 6,
|
|
.alloc = (ProtocolAlloc)protocol_hid_generic_alloc,
|
|
.free = (ProtocolFree)protocol_hid_generic_free,
|
|
.get_data = (ProtocolGetData)protocol_hid_generic_get_data,
|
|
.decoder =
|
|
{
|
|
.start = (ProtocolDecoderStart)protocol_hid_generic_decoder_start,
|
|
.feed = (ProtocolDecoderFeed)protocol_hid_generic_decoder_feed,
|
|
},
|
|
.encoder =
|
|
{
|
|
.start = (ProtocolEncoderStart)protocol_hid_generic_encoder_start,
|
|
.yield = (ProtocolEncoderYield)protocol_hid_generic_encoder_yield,
|
|
},
|
|
.render_data = (ProtocolRenderData)protocol_hid_generic_render_data,
|
|
.render_brief_data = (ProtocolRenderData)protocol_hid_generic_render_data,
|
|
.write_data = (ProtocolWriteData)protocol_hid_generic_write_data,
|
|
};
|