214 lines
7.2 KiB
C
214 lines
7.2 KiB
C
#include "rolling_flaws_keeloq.h"
|
|
|
|
#include "rolling_flaws_utils.h"
|
|
#include "rolling_flaws_settings.h"
|
|
|
|
typedef struct {
|
|
uint32_t fix;
|
|
uint32_t hop;
|
|
uint32_t sn;
|
|
uint32_t btn;
|
|
uint32_t cnt;
|
|
uint32_t enc;
|
|
FuriString* mf;
|
|
} KeeLoqData;
|
|
|
|
KeeLoqData* keeloq_data_alloc() {
|
|
KeeLoqData* data = malloc(sizeof(KeeLoqData));
|
|
data->mf = furi_string_alloc();
|
|
return data;
|
|
}
|
|
|
|
void keeloq_data_free(KeeLoqData* data) {
|
|
furi_string_free(data->mf);
|
|
free(data);
|
|
}
|
|
|
|
static uint32_t get_forward_distance(uint32_t current_count, uint32_t new_count) {
|
|
uint32_t distance = 0;
|
|
if(new_count >= current_count) {
|
|
distance = new_count - current_count;
|
|
} else {
|
|
distance = (0xFFFF - current_count) + new_count;
|
|
}
|
|
|
|
return distance;
|
|
}
|
|
|
|
static bool is_open(RollingFlawsModel* model, KeeLoqData* data) {
|
|
bool any_mf = rolling_flaws_setting_protocol_mf_name_get(model)[0] == '*';
|
|
if(!any_mf &&
|
|
furi_string_cmp(data->mf, rolling_flaws_setting_protocol_mf_name_get(model)) != 0) {
|
|
FURI_LOG_I(
|
|
TAG,
|
|
"Wrong MF. Expected >%s< but got >%s<",
|
|
rolling_flaws_setting_protocol_mf_name_get(model),
|
|
furi_string_get_cstr(data->mf));
|
|
furi_string_set(model->status, "BAD MF");
|
|
return false;
|
|
}
|
|
|
|
if(data->fix != rolling_flaws_setting_fix_get(model)) {
|
|
FURI_LOG_I(
|
|
TAG,
|
|
"Wrong fix. Expected >%08lX< but got >%08lX<",
|
|
rolling_flaws_setting_fix_get(model),
|
|
data->fix);
|
|
furi_string_set(model->status, "BAD FIX");
|
|
return false;
|
|
}
|
|
|
|
if((rolling_flaws_setting_fix_get(model) & 0xFFFFFFF) == 0) {
|
|
FURI_LOG_I(TAG, "Fix is test. Not checking data.");
|
|
furi_string_set(model->status, "TEST");
|
|
model->future_count = 0xFFFFFFFF;
|
|
model->count = data->cnt;
|
|
return true;
|
|
}
|
|
|
|
if(data->enc != FAILED_TO_PARSE) {
|
|
FURI_LOG_I(TAG, "Encrypted payload is %08lX", data->enc);
|
|
|
|
if(!rolling_flaws_setting_sn_zero_get(model)) {
|
|
FURI_LOG_I(TAG, "SN wildcard by 00 disabled.");
|
|
if((data->fix & 0xFF) != 0) {
|
|
FURI_LOG_I(TAG, "SN does not end in 00, validating enc %08lX.", data->enc);
|
|
|
|
if((data->enc & 0xFF) == 0) {
|
|
FURI_LOG_I(TAG, "Encrypted payload SN is zero.");
|
|
furi_string_set(model->status, "SN 00");
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
uint8_t match_bits = rolling_flaws_setting_sn_bits_get(model);
|
|
if(match_bits != 0) {
|
|
uint32_t mask = 0xFFFFFFFF;
|
|
mask = mask >> (32 - match_bits);
|
|
uint32_t fix_sn = data->fix & mask;
|
|
uint32_t enc_sn = data->enc & mask;
|
|
if(fix_sn != enc_sn) {
|
|
FURI_LOG_I(TAG, "SN does not match. Fix: %08lX Enc: %08lX", fix_sn, enc_sn);
|
|
furi_string_set(model->status, "BAD SN");
|
|
return false;
|
|
} else {
|
|
FURI_LOG_I(TAG, "SN matches. Fix: %08lX Enc: %08lX", fix_sn, enc_sn);
|
|
}
|
|
}
|
|
}
|
|
|
|
uint32_t distance = get_forward_distance(model->count, data->cnt);
|
|
FURI_LOG_I(TAG, "Distance: %08lX", distance);
|
|
if(distance == 0 && rolling_flaws_setting_replay_get(model)) {
|
|
FURI_LOG_I(TAG, "Replay attack detected");
|
|
furi_string_set(model->status, "REPLAY");
|
|
model->future_count = 0xFFFFFFFF;
|
|
model->count = data->cnt;
|
|
return true;
|
|
}
|
|
|
|
if(rolling_flaws_setting_count_zero_get(model) && data->cnt == 0) {
|
|
FURI_LOG_I(TAG, "Count zero allowed.");
|
|
furi_string_set(model->status, "COUNT0");
|
|
model->future_count = 0xFFFFFFFF;
|
|
// We don't reset count in this case.
|
|
return true;
|
|
}
|
|
|
|
if(distance == 0) {
|
|
distance = 0x10000;
|
|
}
|
|
|
|
if(distance <= rolling_flaws_setting_window_next_get(model)) {
|
|
FURI_LOG_I(TAG, "Within next window");
|
|
furi_string_set(model->status, "NEXT");
|
|
model->future_count = 0xFFFFFFFF;
|
|
model->count = data->cnt;
|
|
return true;
|
|
}
|
|
|
|
if(distance <= rolling_flaws_setting_window_future_get(model)) {
|
|
FURI_LOG_I(TAG, "Within future window");
|
|
|
|
if(model->future_count > 0xFFFF) {
|
|
FURI_LOG_I(TAG, "Set future value to %08lX.", data->cnt);
|
|
furi_string_set(model->status, "FUTURE");
|
|
model->future_count = data->cnt;
|
|
return false;
|
|
}
|
|
|
|
uint32_t future_gap = get_forward_distance(model->future_count, data->cnt);
|
|
if(future_gap > 0 && future_gap <= rolling_flaws_setting_window_future_gap_get(model)) {
|
|
FURI_LOG_I(TAG, "Future gap accepted. Gap is %08lX", future_gap);
|
|
furi_string_set(model->status, "GAP");
|
|
model->future_count = 0xFFFFFFFF;
|
|
model->count = data->cnt;
|
|
return true;
|
|
}
|
|
|
|
if(future_gap == 0) {
|
|
FURI_LOG_I(TAG, "Future gap is zero. Set future value to %08lX.", data->cnt);
|
|
furi_string_set(model->status, "FUTURE");
|
|
model->future_count = data->cnt;
|
|
return false;
|
|
}
|
|
|
|
FURI_LOG_I(
|
|
TAG,
|
|
"Future gap too large. %08lX > %08lX",
|
|
future_gap,
|
|
rolling_flaws_setting_window_future_gap_get(model));
|
|
furi_string_set(model->status, "BAD GAP");
|
|
model->future_count = data->cnt;
|
|
return false;
|
|
}
|
|
|
|
FURI_LOG_I(TAG, "Signal must be from the past (non-future).");
|
|
furi_string_set(model->status, "PAST");
|
|
return false;
|
|
}
|
|
|
|
void decode_keeloq(RollingFlawsModel* model, FuriString* buffer, bool sync) {
|
|
FURI_LOG_T(TAG, "Decoding KeeLoq 64bit");
|
|
|
|
KeeLoqData* data = keeloq_data_alloc();
|
|
__furi_string_extract_string(buffer, 0, "MF:", '\r', data->mf);
|
|
__furi_string_extract_string(buffer, 0, "Key:", '\r', model->key);
|
|
|
|
data->fix = __furi_string_extract_int(buffer, "Fix:0x", ' ', FAILED_TO_PARSE);
|
|
data->hop = __furi_string_extract_int(buffer, "Hop:0x", ' ', FAILED_TO_PARSE);
|
|
data->sn = __furi_string_extract_int(buffer, "Sn:0x", ' ', FAILED_TO_PARSE);
|
|
data->btn = __furi_string_extract_int(buffer, "Btn:", '\r', FAILED_TO_PARSE);
|
|
data->cnt = __furi_string_extract_int(buffer, "Cnt:", '\r', FAILED_TO_PARSE);
|
|
// NOTE: "Enc:" needs to be added to "keeloq.c" subghz_protocol_decoder_keeloq_get_string() method.
|
|
data->enc = __furi_string_extract_int(buffer, "Enc:", '\r', FAILED_TO_PARSE);
|
|
FURI_LOG_I(
|
|
TAG,
|
|
"fix: %08lX hop: %08lX sn: %08lX btn: %08lX cnt: %08lX enc:%08lX key:%s mf:%s",
|
|
data->fix,
|
|
data->hop,
|
|
data->sn,
|
|
data->btn,
|
|
data->cnt,
|
|
data->enc,
|
|
furi_string_get_cstr(model->key),
|
|
furi_string_get_cstr(data->mf));
|
|
|
|
if(!sync) {
|
|
model->opened = is_open(model, data);
|
|
if(model->opened) {
|
|
model->count = data->cnt;
|
|
}
|
|
__gui_redraw();
|
|
} else {
|
|
model->custom_fix = data->fix;
|
|
model->count = data->cnt;
|
|
model->future_count = 0xFFFFFFFF;
|
|
model->opened = false;
|
|
rolling_flaws_setting_protocol_custom_mf_set(model, data->mf);
|
|
furi_string_set(model->status, "SYNCED");
|
|
}
|
|
|
|
keeloq_data_free(data);
|
|
} |