Redesign of lf hid card format handler as discussed with @marshmellow42

The new handler accepts multiple formats of the same length.
Because of this, the existing pack/unpack commands are unsupported
and have been removed and replaced with 'lf hid encode' and 'lf hid decode'.
The decode command will test a packed Prox ID against all programmed
formats and return results for all matching formats.
The encode command takes the parameter of format name instead of
bit length (as per the old pack command). Additionally, an 'lf hid write'
command has been added as a single-command combination of encode and clone.

To support easier addition of new formats, a library for handling card
fields has been added. This will allow direct access to the card bits,
to linear fields, and to non-linear (jumping) fields in a single line
of code without having to resort to managing bit positions or masks
on the underlying data. A number of new formats have been added as working
examples of the new support functions.
This commit is contained in:
grauerfuchs 2018-08-27 14:03:46 -04:00
parent 18a3bf6119
commit b5a5fc4d9f
7 changed files with 874 additions and 304 deletions

172
client/hidcardformatutils.c Normal file
View file

@ -0,0 +1,172 @@
//-----------------------------------------------------------------------------
// 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 support functions
//-----------------------------------------------------------------------------
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include "hidcardformatutils.h"
#include "ui.h"
bool get_bit_by_position(/* in */hidproxmessage_t* data, /* in */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(/* inout */hidproxmessage_t* data, /* in */bool value, /* in */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 hid_proxmessage struct changes, this function must also
* be updated to match.
*/
void proxmessage_datacopy(/*in*/hidproxmessage_t* src, /*out*/hidproxmessage_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(/* in */hidproxmessage_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(/* inout */hidproxmessage_t* data, uint64_t value, uint8_t firstBit, uint8_t length){
hidproxmessage_t tmpdata;
proxmessage_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) proxmessage_datacopy(&tmpdata, data);
return result;
}
uint64_t get_nonlinear_field(/* in */hidproxmessage_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(/* inout */hidproxmessage_t* data, uint64_t value, uint8_t numBits, uint8_t* bits){
hidproxmessage_t tmpdata;
proxmessage_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) proxmessage_datacopy(&tmpdata, data);
return result;
}
uint8_t get_length_from_header(/* inout */hidproxmessage_t* data) {
uint8_t len = 0;
uint32_t hFmt; // 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 & 0x00000020) == 0) { // 37 bits
hFmt = 0;
len = 37;
} else if ((data->mid & 0x0000001F) > 0){ // 36-32 bits
hFmt = data->mid & 0x0000001F;
len = 32;
} else { // <32 bits
hFmt = data->bot;
len = 0;
}
while (hFmt > 1) {
hFmt >>= 1;
len++;
}
return len;
}
hidproxmessage_t initialize_proxmessage_object(uint32_t top, uint32_t mid, uint32_t bot){
struct hidproxmessage_s result;
memset(&result, 0, sizeof(hidproxmessage_t));
result.top = top;
result.mid = mid;
result.bot = bot;
result.Length = get_length_from_header(&result);
return result;
}
bool add_HID_header(/* inout */hidproxmessage_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;
}