mirror of
https://protopirate.net/ProtoPirate/ProtoPirate.git
synced 2026-03-29 11:00:40 +00:00
add vw crypto
by zero-mega and slackware
This commit is contained in:
@@ -5,7 +5,7 @@ App(
|
||||
targets=["f7"],
|
||||
entry_point="protopirate_app",
|
||||
requires=["gui"],
|
||||
stack_size=2 * 1024,
|
||||
stack_size=4 * 1024,
|
||||
fap_description="Decode car key fob signals from Sub-GHz",
|
||||
fap_version="1.8",
|
||||
fap_icon="images/protopirate_10px.png",
|
||||
|
||||
6
keystore/vw
Normal file
6
keystore/vw
Normal file
@@ -0,0 +1,6 @@
|
||||
Filetype: Flipper SubGhz Keystore RAW File
|
||||
Version: 0
|
||||
Encryption: 1
|
||||
IV: B9 A8 C0 F6 9A 78 B1 CB 3E A7 69 0E 86 53 9F A7
|
||||
Encrypt_data: RAW
|
||||
BA2A8B783B6DB3E17DEF07EA2AE0104FAF722A07775E96338D734FFD0EA8BB29BF1A5A6451B1784AE2B51D967EFB45EAFAE2C20D5721553727C026D4009BBCB0
|
||||
485
protocols/aut64.c
Normal file
485
protocols/aut64.c
Normal file
@@ -0,0 +1,485 @@
|
||||
#include <string.h>
|
||||
#include "aut64.h"
|
||||
|
||||
// https://www.usenix.org/system/files/conference/usenixsecurity16/sec16_paper_garcia.pdf
|
||||
|
||||
/*
|
||||
* AUT64 algorithm, 12 rounds
|
||||
* 8 bytes block size, 8 bytes key size
|
||||
* 8 bytes pbox size, 16 bytes sbox size
|
||||
*
|
||||
* Based on: Reference AUT64 implementation in JavaScript (aut64.js)
|
||||
* Vencislav Atanasov, 2025-09-13
|
||||
*
|
||||
* Based on: Reference AUT64 implementation in python
|
||||
* C Hicks, hkscy.org, 03-01-19
|
||||
*/
|
||||
|
||||
static const uint8_t table_ln[AUT64_NUM_ROUNDS][8] = {
|
||||
{
|
||||
0x4,
|
||||
0x5,
|
||||
0x6,
|
||||
0x7,
|
||||
0x0,
|
||||
0x1,
|
||||
0x2,
|
||||
0x3,
|
||||
}, // Round 0
|
||||
{
|
||||
0x5,
|
||||
0x4,
|
||||
0x7,
|
||||
0x6,
|
||||
0x1,
|
||||
0x0,
|
||||
0x3,
|
||||
0x2,
|
||||
}, // Round 1
|
||||
{
|
||||
0x6,
|
||||
0x7,
|
||||
0x4,
|
||||
0x5,
|
||||
0x2,
|
||||
0x3,
|
||||
0x0,
|
||||
0x1,
|
||||
}, // Round 2
|
||||
{
|
||||
0x7,
|
||||
0x6,
|
||||
0x5,
|
||||
0x4,
|
||||
0x3,
|
||||
0x2,
|
||||
0x1,
|
||||
0x0,
|
||||
}, // Round 3
|
||||
{
|
||||
0x0,
|
||||
0x1,
|
||||
0x2,
|
||||
0x3,
|
||||
0x4,
|
||||
0x5,
|
||||
0x6,
|
||||
0x7,
|
||||
}, // Round 4
|
||||
{
|
||||
0x1,
|
||||
0x0,
|
||||
0x3,
|
||||
0x2,
|
||||
0x5,
|
||||
0x4,
|
||||
0x7,
|
||||
0x6,
|
||||
}, // Round 5
|
||||
{
|
||||
0x2,
|
||||
0x3,
|
||||
0x0,
|
||||
0x1,
|
||||
0x6,
|
||||
0x7,
|
||||
0x4,
|
||||
0x5,
|
||||
}, // Round 6
|
||||
{
|
||||
0x3,
|
||||
0x2,
|
||||
0x1,
|
||||
0x0,
|
||||
0x7,
|
||||
0x6,
|
||||
0x5,
|
||||
0x4,
|
||||
}, // Round 7
|
||||
{
|
||||
0x5,
|
||||
0x4,
|
||||
0x7,
|
||||
0x6,
|
||||
0x1,
|
||||
0x0,
|
||||
0x3,
|
||||
0x2,
|
||||
}, // Round 8
|
||||
{
|
||||
0x4,
|
||||
0x5,
|
||||
0x6,
|
||||
0x7,
|
||||
0x0,
|
||||
0x1,
|
||||
0x2,
|
||||
0x3,
|
||||
}, // Round 9
|
||||
{
|
||||
0x7,
|
||||
0x6,
|
||||
0x5,
|
||||
0x4,
|
||||
0x3,
|
||||
0x2,
|
||||
0x1,
|
||||
0x0,
|
||||
}, // Round 10
|
||||
{
|
||||
0x6,
|
||||
0x7,
|
||||
0x4,
|
||||
0x5,
|
||||
0x2,
|
||||
0x3,
|
||||
0x0,
|
||||
0x1,
|
||||
}, // Round 11
|
||||
};
|
||||
|
||||
static const uint8_t table_un[AUT64_NUM_ROUNDS][8] = {
|
||||
{
|
||||
0x1,
|
||||
0x0,
|
||||
0x3,
|
||||
0x2,
|
||||
0x5,
|
||||
0x4,
|
||||
0x7,
|
||||
0x6,
|
||||
}, // Round 0
|
||||
{
|
||||
0x0,
|
||||
0x1,
|
||||
0x2,
|
||||
0x3,
|
||||
0x4,
|
||||
0x5,
|
||||
0x6,
|
||||
0x7,
|
||||
}, // Round 1
|
||||
{
|
||||
0x3,
|
||||
0x2,
|
||||
0x1,
|
||||
0x0,
|
||||
0x7,
|
||||
0x6,
|
||||
0x5,
|
||||
0x4,
|
||||
}, // Round 2
|
||||
{
|
||||
0x2,
|
||||
0x3,
|
||||
0x0,
|
||||
0x1,
|
||||
0x6,
|
||||
0x7,
|
||||
0x4,
|
||||
0x5,
|
||||
}, // Round 3
|
||||
{
|
||||
0x5,
|
||||
0x4,
|
||||
0x7,
|
||||
0x6,
|
||||
0x1,
|
||||
0x0,
|
||||
0x3,
|
||||
0x2,
|
||||
}, // Round 4
|
||||
{
|
||||
0x4,
|
||||
0x5,
|
||||
0x6,
|
||||
0x7,
|
||||
0x0,
|
||||
0x1,
|
||||
0x2,
|
||||
0x3,
|
||||
}, // Round 5
|
||||
{
|
||||
0x7,
|
||||
0x6,
|
||||
0x5,
|
||||
0x4,
|
||||
0x3,
|
||||
0x2,
|
||||
0x1,
|
||||
0x0,
|
||||
}, // Round 6
|
||||
{
|
||||
0x6,
|
||||
0x7,
|
||||
0x4,
|
||||
0x5,
|
||||
0x2,
|
||||
0x3,
|
||||
0x0,
|
||||
0x1,
|
||||
}, // Round 7
|
||||
{
|
||||
0x3,
|
||||
0x2,
|
||||
0x1,
|
||||
0x0,
|
||||
0x7,
|
||||
0x6,
|
||||
0x5,
|
||||
0x4,
|
||||
}, // Round 8
|
||||
{
|
||||
0x2,
|
||||
0x3,
|
||||
0x0,
|
||||
0x1,
|
||||
0x6,
|
||||
0x7,
|
||||
0x4,
|
||||
0x5,
|
||||
}, // Round 9
|
||||
{
|
||||
0x1,
|
||||
0x0,
|
||||
0x3,
|
||||
0x2,
|
||||
0x5,
|
||||
0x4,
|
||||
0x7,
|
||||
0x6,
|
||||
}, // Round 10
|
||||
{
|
||||
0x0,
|
||||
0x1,
|
||||
0x2,
|
||||
0x3,
|
||||
0x4,
|
||||
0x5,
|
||||
0x6,
|
||||
0x7,
|
||||
}, // Round 11
|
||||
};
|
||||
|
||||
static const uint8_t table_offset[256] = {
|
||||
// 0 1 2 3 4 5 6 7 8 9 A B C D E F
|
||||
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // 0
|
||||
0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, // 1
|
||||
0x0, 0x2, 0x4, 0x6, 0x8, 0xA, 0xC, 0xE, 0x3, 0x1, 0x7, 0x5, 0xB, 0x9, 0xF, 0xD, // 2
|
||||
0x0, 0x3, 0x6, 0x5, 0xC, 0xF, 0xA, 0x9, 0xB, 0x8, 0xD, 0xE, 0x7, 0x4, 0x1, 0x2, // 3
|
||||
0x0, 0x4, 0x8, 0xC, 0x3, 0x7, 0xB, 0xF, 0x6, 0x2, 0xE, 0xA, 0x5, 0x1, 0xD, 0x9, // 4
|
||||
0x0, 0x5, 0xA, 0xF, 0x7, 0x2, 0xD, 0x8, 0xE, 0xB, 0x4, 0x1, 0x9, 0xC, 0x3, 0x6, // 5
|
||||
0x0, 0x6, 0xC, 0xA, 0xB, 0xD, 0x7, 0x1, 0x5, 0x3, 0x9, 0xF, 0xE, 0x8, 0x2, 0x4, // 6
|
||||
0x0, 0x7, 0xE, 0x9, 0xF, 0x8, 0x1, 0x6, 0xD, 0xA, 0x3, 0x4, 0x2, 0x5, 0xC, 0xB, // 7
|
||||
0x0, 0x8, 0x3, 0xB, 0x6, 0xE, 0x5, 0xD, 0xC, 0x4, 0xF, 0x7, 0xA, 0x2, 0x9, 0x1, // 8
|
||||
0x0, 0x9, 0x1, 0x8, 0x2, 0xB, 0x3, 0xA, 0x4, 0xD, 0x5, 0xC, 0x6, 0xF, 0x7, 0xE, // 9
|
||||
0x0, 0xA, 0x7, 0xD, 0xE, 0x4, 0x9, 0x3, 0xF, 0x5, 0x8, 0x2, 0x1, 0xB, 0x6, 0xC, // A
|
||||
0x0, 0xB, 0x5, 0xE, 0xA, 0x1, 0xF, 0x4, 0x7, 0xC, 0x2, 0x9, 0xD, 0x6, 0x8, 0x3, // B
|
||||
0x0, 0xC, 0xB, 0x7, 0x5, 0x9, 0xE, 0x2, 0xA, 0x6, 0x1, 0xD, 0xF, 0x3, 0x4, 0x8, // C
|
||||
0x0, 0xD, 0x9, 0x4, 0x1, 0xC, 0x8, 0x5, 0x2, 0xF, 0xB, 0x6, 0x3, 0xE, 0xA, 0x7, // D
|
||||
0x0, 0xE, 0xF, 0x1, 0xD, 0x3, 0x2, 0xC, 0x9, 0x7, 0x6, 0x8, 0x4, 0xA, 0xB, 0x5, // E
|
||||
0x0, 0xF, 0xD, 0x2, 0x9, 0x6, 0x4, 0xB, 0x1, 0xE, 0xC, 0x3, 0x8, 0x7, 0x5, 0xA // F
|
||||
};
|
||||
|
||||
static const uint8_t table_sub[16] = {
|
||||
0x0,
|
||||
0x1,
|
||||
0x9,
|
||||
0xE,
|
||||
0xD,
|
||||
0xB,
|
||||
0x7,
|
||||
0x6,
|
||||
0xF,
|
||||
0x2,
|
||||
0xC,
|
||||
0x5,
|
||||
0xA,
|
||||
0x4,
|
||||
0x3,
|
||||
0x8,
|
||||
};
|
||||
|
||||
static uint8_t key_nibble(
|
||||
const struct aut64_key key,
|
||||
const uint8_t nibble,
|
||||
const uint8_t table[],
|
||||
const uint8_t iteration) {
|
||||
const uint8_t keyValue = key.key[table[iteration]];
|
||||
const uint8_t offset = (keyValue << 4) | nibble;
|
||||
return table_offset[offset];
|
||||
}
|
||||
|
||||
static uint8_t round_key(const struct aut64_key key, const uint8_t state[], const uint8_t roundN) {
|
||||
uint8_t result_hi = 0, result_lo = 0;
|
||||
|
||||
for(int i = 0; i < AUT64_BLOCK_SIZE - 1; i++) {
|
||||
result_hi ^= key_nibble(key, state[i] >> 4, table_un[roundN], i);
|
||||
result_lo ^= key_nibble(key, state[i] & 0x0F, table_ln[roundN], i);
|
||||
}
|
||||
|
||||
return (result_hi << 4) | result_lo;
|
||||
}
|
||||
|
||||
static uint8_t final_byte_nibble(const struct aut64_key key, const uint8_t table[]) {
|
||||
const uint8_t keyValue = key.key[table[AUT64_BLOCK_SIZE - 1]];
|
||||
return table_sub[keyValue] << 4;
|
||||
}
|
||||
|
||||
static uint8_t encrypt_final_byte_nibble(
|
||||
const struct aut64_key key,
|
||||
const uint8_t nibble,
|
||||
const uint8_t table[]) {
|
||||
const uint8_t offset = final_byte_nibble(key, table);
|
||||
|
||||
int i;
|
||||
|
||||
for(i = 0; i < 16; i++) {
|
||||
if(table_offset[offset + i] == nibble) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
static uint8_t
|
||||
encrypt_compress(const struct aut64_key key, const uint8_t state[], const uint8_t roundN) {
|
||||
const uint8_t roundKey = round_key(key, state, roundN);
|
||||
uint8_t result_hi = roundKey >> 4, result_lo = roundKey & 0x0F;
|
||||
|
||||
result_hi ^=
|
||||
encrypt_final_byte_nibble(key, state[AUT64_BLOCK_SIZE - 1] >> 4, table_un[roundN]);
|
||||
result_lo ^=
|
||||
encrypt_final_byte_nibble(key, state[AUT64_BLOCK_SIZE - 1] & 0x0F, table_ln[roundN]);
|
||||
|
||||
return (result_hi << 4) | result_lo;
|
||||
}
|
||||
|
||||
static uint8_t decrypt_final_byte_nibble(
|
||||
const struct aut64_key key,
|
||||
const uint8_t nibble,
|
||||
const uint8_t table[],
|
||||
const uint8_t result) {
|
||||
const uint8_t offset = final_byte_nibble(key, table);
|
||||
|
||||
return table_offset[(result ^ nibble) + offset];
|
||||
}
|
||||
|
||||
static uint8_t
|
||||
decrypt_compress(const struct aut64_key key, const uint8_t state[], const uint8_t roundN) {
|
||||
const uint8_t roundKey = round_key(key, state, roundN);
|
||||
uint8_t result_hi = roundKey >> 4, result_lo = roundKey & 0x0F;
|
||||
|
||||
result_hi = decrypt_final_byte_nibble(
|
||||
key, state[AUT64_BLOCK_SIZE - 1] >> 4, table_un[roundN], result_hi);
|
||||
result_lo = decrypt_final_byte_nibble(
|
||||
key, state[AUT64_BLOCK_SIZE - 1] & 0x0F, table_ln[roundN], result_lo);
|
||||
|
||||
return (result_hi << 4) | result_lo;
|
||||
}
|
||||
|
||||
static uint8_t substitute(const struct aut64_key key, const uint8_t byte) {
|
||||
return (key.sbox[byte >> 4] << 4) | key.sbox[byte & 0x0F];
|
||||
}
|
||||
|
||||
static void permute_bytes(const struct aut64_key key, uint8_t state[]) {
|
||||
uint8_t result[AUT64_PBOX_SIZE] = {0};
|
||||
|
||||
for(int i = 0; i < AUT64_PBOX_SIZE; i++) {
|
||||
result[key.pbox[i]] = state[i];
|
||||
}
|
||||
|
||||
memcpy(state, result, AUT64_PBOX_SIZE);
|
||||
}
|
||||
|
||||
static uint8_t permute_bits(const struct aut64_key key, const uint8_t byte) {
|
||||
uint8_t result = 0;
|
||||
|
||||
for(int i = 0; i < 8; i++) {
|
||||
if(byte & (1 << i)) {
|
||||
result |= (1 << key.pbox[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void reverse_box(uint8_t* reversed, const uint8_t* box, const size_t len) {
|
||||
for(size_t i = 0; i < len; i++) {
|
||||
for(size_t j = 0; j < len; j++) {
|
||||
if(box[j] == i) {
|
||||
reversed[i] = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void aut64_encrypt(const struct aut64_key key, uint8_t message[]) {
|
||||
struct aut64_key reverse_key;
|
||||
memcpy(reverse_key.key, key.key, AUT64_KEY_SIZE);
|
||||
reverse_box(reverse_key.pbox, key.pbox, AUT64_PBOX_SIZE);
|
||||
reverse_box(reverse_key.sbox, key.sbox, AUT64_SBOX_SIZE);
|
||||
|
||||
for(int i = 0; i < AUT64_NUM_ROUNDS; i++) {
|
||||
permute_bytes(reverse_key, message);
|
||||
message[7] = encrypt_compress(reverse_key, message, i);
|
||||
message[7] = substitute(reverse_key, message[7]);
|
||||
message[7] = permute_bits(reverse_key, message[7]);
|
||||
message[7] = substitute(reverse_key, message[7]);
|
||||
}
|
||||
}
|
||||
|
||||
void aut64_decrypt(const struct aut64_key key, uint8_t message[]) {
|
||||
for(int i = AUT64_NUM_ROUNDS - 1; i >= 0; i--) {
|
||||
message[7] = substitute(key, message[7]);
|
||||
message[7] = permute_bits(key, message[7]);
|
||||
message[7] = substitute(key, message[7]);
|
||||
message[7] = decrypt_compress(key, message, i);
|
||||
permute_bytes(key, message);
|
||||
}
|
||||
}
|
||||
|
||||
void aut64_pack(uint8_t dest[], const struct aut64_key src) {
|
||||
dest[0] = src.index;
|
||||
|
||||
for(uint8_t i = 0; i < sizeof(src.key) / 2; i++) {
|
||||
dest[i + 1] = (src.key[i * 2] << 4) | src.key[i * 2 + 1];
|
||||
}
|
||||
|
||||
uint32_t pbox = 0;
|
||||
|
||||
for(uint8_t i = 0; i < sizeof(src.pbox); i++) {
|
||||
pbox = (pbox << 3) | src.pbox[i];
|
||||
}
|
||||
|
||||
dest[5] = pbox >> 16;
|
||||
dest[6] = (pbox >> 8) & 0xFF;
|
||||
dest[7] = pbox & 0xFF;
|
||||
|
||||
for(uint8_t i = 0; i < sizeof(src.sbox) / 2; i++) {
|
||||
dest[i + 8] = (src.sbox[i * 2] << 4) | src.sbox[i * 2 + 1];
|
||||
}
|
||||
}
|
||||
|
||||
void aut64_unpack(struct aut64_key* dest, const uint8_t src[]) {
|
||||
dest->index = src[0];
|
||||
|
||||
for(uint8_t i = 0; i < sizeof(dest->key) / 2; i++) {
|
||||
dest->key[i * 2] = src[i + 1] >> 4;
|
||||
dest->key[i * 2 + 1] = src[i + 1] & 0xF;
|
||||
}
|
||||
|
||||
uint32_t pbox = (src[5] << 16) | (src[6] << 8) | src[7];
|
||||
|
||||
for(int8_t i = sizeof(dest->pbox) - 1; i >= 0; i--) {
|
||||
dest->pbox[i] = pbox & 0x7;
|
||||
pbox >>= 3;
|
||||
}
|
||||
|
||||
for(uint8_t i = 0; i < sizeof(dest->sbox) / 2; i++) {
|
||||
dest->sbox[i * 2] = src[i + 8] >> 4;
|
||||
dest->sbox[i * 2 + 1] = src[i + 8] & 0xF;
|
||||
}
|
||||
}
|
||||
23
protocols/aut64.h
Normal file
23
protocols/aut64.h
Normal file
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define AUT64_NUM_ROUNDS 12
|
||||
#define AUT64_BLOCK_SIZE 8
|
||||
#define AUT64_KEY_SIZE 8
|
||||
#define AUT64_PBOX_SIZE 8
|
||||
#define AUT64_SBOX_SIZE 16
|
||||
|
||||
struct aut64_key {
|
||||
uint8_t index;
|
||||
uint8_t key[AUT64_KEY_SIZE];
|
||||
uint8_t pbox[AUT64_PBOX_SIZE];
|
||||
uint8_t sbox[AUT64_SBOX_SIZE];
|
||||
};
|
||||
|
||||
#define AUT64_KEY_STRUCT_PACKED_SIZE 16
|
||||
|
||||
void aut64_encrypt(const struct aut64_key key, uint8_t message[]);
|
||||
void aut64_decrypt(const struct aut64_key key, uint8_t message[]);
|
||||
void aut64_pack(uint8_t dest[], const struct aut64_key src);
|
||||
void aut64_unpack(struct aut64_key* dest, const uint8_t src[]);
|
||||
486
protocols/vw.c
486
protocols/vw.c
@@ -1,4 +1,5 @@
|
||||
#include "vw.h"
|
||||
#include "aut64.h"
|
||||
|
||||
#define TAG "VWProtocol"
|
||||
|
||||
@@ -9,13 +10,28 @@ static const SubGhzBlockConst subghz_protocol_vw_const = {
|
||||
.min_count_bit_for_found = 80,
|
||||
};
|
||||
|
||||
/*
|
||||
// Slightly different timings for some newer remotes?
|
||||
static const SubGhzBlockConst subghz_protocol_vw3_const = {
|
||||
.te_short = 300,
|
||||
.te_long = 600,
|
||||
.te_delta = 120, // ???
|
||||
.min_count_bit_for_found = 80, // ????
|
||||
};
|
||||
*/
|
||||
|
||||
typedef struct SubGhzProtocolDecoderVw {
|
||||
SubGhzProtocolDecoderBase base;
|
||||
SubGhzBlockDecoder decoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
|
||||
ManchesterState manchester_state;
|
||||
uint64_t data_2; // Additional 16 bits (type byte + check byte)
|
||||
uint8_t data[10];
|
||||
uint8_t type;
|
||||
uint32_t key1_low;
|
||||
uint32_t key1_high;
|
||||
uint16_t key2;
|
||||
uint8_t crc;
|
||||
} SubGhzProtocolDecoderVw;
|
||||
|
||||
typedef struct SubGhzProtocolEncoderVw {
|
||||
@@ -55,12 +71,98 @@ const SubGhzProtocolEncoder subghz_protocol_vw_encoder = {
|
||||
const SubGhzProtocol vw_protocol = {
|
||||
.name = VW_PROTOCOL_NAME,
|
||||
.type = SubGhzProtocolTypeDynamic,
|
||||
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable,
|
||||
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable |
|
||||
SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save,
|
||||
.decoder = &subghz_protocol_vw_decoder,
|
||||
.encoder = &subghz_protocol_vw_encoder,
|
||||
};
|
||||
|
||||
// Fixed manchester_advance for VW protocol
|
||||
#define VW_KEYS_COUNT 3
|
||||
|
||||
#define VW_TEA_DELTA 0x9E3779B9U
|
||||
#define VW_TEA_DELTA_INC 0x61C88647U
|
||||
#define VW_TEA_ROUNDS 32
|
||||
|
||||
static const uint32_t vw_tea_key_schedule[] = {0x2BF93A19, 0x622C1206, 0x6A55B5DA, 0xD5AAAAAA};
|
||||
static const uint32_t vw_tea_key_schedule_rom[] = {0x46280509, 0xBDF0B005, 0x23084924, 0x4638466A};
|
||||
|
||||
///
|
||||
|
||||
static int8_t protocol_vw_keys_loaded = -1;
|
||||
static struct aut64_key protocol_vw_keys[VW_KEYS_COUNT];
|
||||
|
||||
static void protocol_vw_load_keys(const char* file_name) {
|
||||
if(protocol_vw_keys_loaded >= 0) {
|
||||
FURI_LOG_I(
|
||||
TAG,
|
||||
"Already loaded %u keys from %s, skipping load",
|
||||
protocol_vw_keys_loaded,
|
||||
file_name);
|
||||
return;
|
||||
}
|
||||
|
||||
FURI_LOG_I(TAG, "Loading keys from %s", file_name);
|
||||
|
||||
protocol_vw_keys_loaded = 0;
|
||||
|
||||
for(uint8_t i = 0; i < VW_KEYS_COUNT; i++) {
|
||||
uint8_t key_packed[AUT64_KEY_STRUCT_PACKED_SIZE];
|
||||
|
||||
if(subghz_keystore_raw_get_data(
|
||||
file_name,
|
||||
i * AUT64_KEY_STRUCT_PACKED_SIZE,
|
||||
key_packed,
|
||||
AUT64_KEY_STRUCT_PACKED_SIZE)) {
|
||||
aut64_unpack(&protocol_vw_keys[i], key_packed);
|
||||
protocol_vw_keys_loaded++;
|
||||
} else {
|
||||
FURI_LOG_E(TAG, "Unable to load key %u", i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
FURI_LOG_I(TAG, "Loaded %u keys", protocol_vw_keys_loaded);
|
||||
}
|
||||
|
||||
static struct aut64_key* protocol_vw_get_key(uint8_t index) {
|
||||
for(uint8_t i = 0; i < MIN(protocol_vw_keys_loaded, VW_KEYS_COUNT); i++) {
|
||||
if(protocol_vw_keys[i].index == index) {
|
||||
return &protocol_vw_keys[i];
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
///
|
||||
|
||||
static void vw_build_decoder_block(const uint8_t* key1_8, uint8_t* block_8) {
|
||||
uint32_t low = (uint32_t)key1_8[0] | ((uint32_t)key1_8[1] << 8) | ((uint32_t)key1_8[2] << 16) |
|
||||
((uint32_t)key1_8[3] << 24);
|
||||
uint32_t high = (uint32_t)key1_8[4] | ((uint32_t)key1_8[5] << 8) |
|
||||
((uint32_t)key1_8[6] << 16) | ((uint32_t)key1_8[7] << 24);
|
||||
block_8[0] = (uint8_t)(high >> 24);
|
||||
block_8[1] = (uint8_t)(high >> 16);
|
||||
block_8[2] = (uint8_t)(high >> 8);
|
||||
block_8[3] = (uint8_t)(high);
|
||||
block_8[4] = (uint8_t)(low >> 24);
|
||||
block_8[5] = (uint8_t)(low >> 16);
|
||||
block_8[6] = (uint8_t)(low >> 8);
|
||||
block_8[7] = (uint8_t)(low);
|
||||
}
|
||||
|
||||
static void vw_tea_decrypt(uint32_t* v0, uint32_t* v1, const uint32_t* key_schedule) {
|
||||
uint32_t sum = VW_TEA_DELTA * VW_TEA_ROUNDS;
|
||||
for(int i = 0; i < VW_TEA_ROUNDS; i++) {
|
||||
uint32_t k_idx2 = (sum >> 11) & 3;
|
||||
uint32_t temp = key_schedule[k_idx2] + sum;
|
||||
*v1 -= temp ^ (((*v0 >> 5) ^ (*v0 << 4)) + *v0);
|
||||
sum -= VW_TEA_DELTA_INC;
|
||||
uint32_t k_idx1 = sum & 3;
|
||||
temp = key_schedule[k_idx1] + sum;
|
||||
*v0 -= temp ^ (((*v1 >> 5) ^ (*v1 << 4)) + *v1);
|
||||
}
|
||||
}
|
||||
|
||||
static bool vw_manchester_advance(
|
||||
ManchesterState state,
|
||||
ManchesterEvent event,
|
||||
@@ -109,50 +211,18 @@ static bool vw_manchester_advance(
|
||||
return result;
|
||||
}
|
||||
|
||||
static uint8_t vw_get_bit_index(uint8_t bit) {
|
||||
uint8_t bit_index = 0;
|
||||
|
||||
if(bit < 72 && bit >= 8) {
|
||||
// use generic.data (bytes 1-8)
|
||||
bit_index = bit - 8;
|
||||
} else {
|
||||
// use data_2
|
||||
if(bit >= 72) {
|
||||
bit_index = bit - 64; // byte 0 = type
|
||||
}
|
||||
if(bit < 8) {
|
||||
bit_index = bit; // byte 9 = check digit
|
||||
}
|
||||
bit_index |= 0x80; // mark for data_2
|
||||
}
|
||||
|
||||
return bit_index;
|
||||
}
|
||||
|
||||
static void vw_add_bit(SubGhzProtocolDecoderVw* instance, bool level) {
|
||||
furi_assert(instance);
|
||||
|
||||
if(instance->generic.data_count_bit >= subghz_protocol_vw_const.min_count_bit_for_found) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t bit_index_full =
|
||||
subghz_protocol_vw_const.min_count_bit_for_found - 1 - instance->generic.data_count_bit;
|
||||
uint8_t bit_index_masked = vw_get_bit_index(bit_index_full);
|
||||
uint8_t bit_index = bit_index_masked & 0x7F;
|
||||
if(level) {
|
||||
uint8_t byte_index = instance->generic.data_count_bit / 8;
|
||||
uint8_t bit_index = instance->generic.data_count_bit % 8;
|
||||
|
||||
if(bit_index_masked & 0x80) {
|
||||
// use data_2
|
||||
if(level) {
|
||||
instance->data_2 |= (1ULL << bit_index);
|
||||
} else {
|
||||
instance->data_2 &= ~(1ULL << bit_index);
|
||||
}
|
||||
} else {
|
||||
// use data
|
||||
if(level) {
|
||||
instance->generic.data |= (1ULL << bit_index);
|
||||
} else {
|
||||
instance->generic.data &= ~(1ULL << bit_index);
|
||||
}
|
||||
instance->data[byte_index] |= 1 << (7 - bit_index);
|
||||
}
|
||||
|
||||
instance->generic.data_count_bit++;
|
||||
@@ -160,6 +230,131 @@ static void vw_add_bit(SubGhzProtocolDecoderVw* instance, bool level) {
|
||||
if(instance->generic.data_count_bit >= subghz_protocol_vw_const.min_count_bit_for_found) {
|
||||
if(instance->base.callback) {
|
||||
instance->base.callback(&instance->base, instance->base.context);
|
||||
} else {
|
||||
subghz_protocol_decoder_vw_reset(instance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void vw_fill_from_decrypted(
|
||||
SubGhzProtocolDecoderVw* instance,
|
||||
const uint8_t* dec,
|
||||
uint8_t check_byte) {
|
||||
uint64_t key1 = ((uint64_t)instance->data[0] << 56) | ((uint64_t)instance->data[1] << 48) |
|
||||
((uint64_t)instance->data[2] << 40) | ((uint64_t)instance->data[3] << 32) |
|
||||
((uint64_t)instance->data[4] << 24) | ((uint64_t)instance->data[5] << 16) |
|
||||
((uint64_t)instance->data[6] << 8) | (uint64_t)instance->data[7];
|
||||
instance->key1_low = (uint32_t)(key1 & 0xFFFFFFFFU);
|
||||
instance->key1_high = (uint32_t)((key1 >> 32) & 0xFFFFFFFFU);
|
||||
instance->key2 = ((uint16_t)instance->data[8] << 8) | instance->data[9];
|
||||
uint32_t serial_le = (uint32_t)dec[0] | ((uint32_t)dec[1] << 8) | ((uint32_t)dec[2] << 16) |
|
||||
((uint32_t)dec[3] << 24);
|
||||
instance->generic.serial = ((serial_le & 0xFFU) << 24) | ((serial_le & 0xFF00U) << 8) |
|
||||
((serial_le & 0xFF0000U) >> 8) | ((serial_le & 0xFF000000U) >> 24);
|
||||
instance->generic.cnt = (uint32_t)dec[4] | ((uint32_t)dec[5] << 8) | ((uint32_t)dec[6] << 16);
|
||||
instance->generic.btn = (dec[7] >> 4) & 0xFU;
|
||||
instance->crc = check_byte;
|
||||
instance->type = 0xC0;
|
||||
}
|
||||
|
||||
static bool vw_try_aut64_block(
|
||||
SubGhzProtocolDecoderVw* instance,
|
||||
const uint8_t* block_8,
|
||||
uint8_t check_byte,
|
||||
uint8_t button_from_check,
|
||||
size_t key_start,
|
||||
size_t key_end) {
|
||||
uint8_t dec[8];
|
||||
for(size_t i = key_start; i < key_end; i++) {
|
||||
memcpy(dec, block_8, 8);
|
||||
const struct aut64_key* key = protocol_vw_get_key(i + 1);
|
||||
if(!key) {
|
||||
FURI_LOG_E(TAG, "Key not found: %zu", i + 1);
|
||||
continue;
|
||||
}
|
||||
aut64_decrypt(*key, dec);
|
||||
uint8_t btn = (dec[7] >> 4) & 0xFU;
|
||||
if(btn == button_from_check) {
|
||||
vw_fill_from_decrypted(instance, dec, check_byte);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool vw_dispatch_type_path_3_4(uint8_t t) {
|
||||
return (t == 0x2B || t == 0x1D || t == 0x47);
|
||||
}
|
||||
|
||||
static void vw_parse_data(SubGhzProtocolDecoderVw* instance) {
|
||||
furi_assert(instance);
|
||||
|
||||
instance->type = instance->data[0];
|
||||
uint8_t check_byte = instance->data[9];
|
||||
uint8_t dispatch_type = instance->data[9];
|
||||
uint8_t button_from_check = (check_byte >> 4) & 0xFU;
|
||||
|
||||
uint8_t encrypted_raw[8];
|
||||
uint8_t decoder_block[8];
|
||||
memcpy(encrypted_raw, instance->data + 1, 8);
|
||||
vw_build_decoder_block(instance->data, decoder_block);
|
||||
|
||||
if(vw_dispatch_type_path_3_4(dispatch_type)) {
|
||||
if(vw_try_aut64_block(instance, encrypted_raw, check_byte, button_from_check, 2, 3)) {
|
||||
return;
|
||||
}
|
||||
if(vw_try_aut64_block(instance, encrypted_raw, check_byte, button_from_check, 1, 2)) {
|
||||
return;
|
||||
}
|
||||
uint8_t dec[8];
|
||||
memcpy(dec, encrypted_raw, 8);
|
||||
const struct aut64_key* key = protocol_vw_get_key(3);
|
||||
|
||||
if(!key) {
|
||||
FURI_LOG_E(TAG, "Key not found: 3");
|
||||
return;
|
||||
}
|
||||
aut64_decrypt(*key, dec);
|
||||
vw_fill_from_decrypted(instance, dec, check_byte);
|
||||
return;
|
||||
}
|
||||
|
||||
if(vw_try_aut64_block(
|
||||
instance, encrypted_raw, check_byte, button_from_check, 0, VW_KEYS_COUNT)) {
|
||||
return;
|
||||
}
|
||||
if(vw_try_aut64_block(
|
||||
instance, decoder_block, check_byte, button_from_check, 0, VW_KEYS_COUNT)) {
|
||||
return;
|
||||
}
|
||||
if(instance->type == 0x00 &&
|
||||
vw_try_aut64_block(
|
||||
instance, instance->data, check_byte, button_from_check, 0, VW_KEYS_COUNT)) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t v0 = (uint32_t)encrypted_raw[0] | ((uint32_t)encrypted_raw[1] << 8) |
|
||||
((uint32_t)encrypted_raw[2] << 16) | ((uint32_t)encrypted_raw[3] << 24);
|
||||
uint32_t v1 = (uint32_t)encrypted_raw[4] | ((uint32_t)encrypted_raw[5] << 8) |
|
||||
((uint32_t)encrypted_raw[6] << 16) | ((uint32_t)encrypted_raw[7] << 24);
|
||||
for(int tea_key = 0; tea_key < 2; tea_key++) {
|
||||
uint32_t d0 = v0;
|
||||
uint32_t d1 = v1;
|
||||
const uint32_t* key_sched = tea_key ? vw_tea_key_schedule_rom : vw_tea_key_schedule;
|
||||
vw_tea_decrypt(&d0, &d1, key_sched);
|
||||
uint8_t dec[8];
|
||||
dec[0] = (uint8_t)(d0);
|
||||
dec[1] = (uint8_t)(d0 >> 8);
|
||||
dec[2] = (uint8_t)(d0 >> 16);
|
||||
dec[3] = (uint8_t)(d0 >> 24);
|
||||
dec[4] = (uint8_t)(d1);
|
||||
dec[5] = (uint8_t)(d1 >> 8);
|
||||
dec[6] = (uint8_t)(d1 >> 16);
|
||||
dec[7] = (uint8_t)(d1 >> 24);
|
||||
uint8_t btn = (dec[7] >> 4) & 0xFU;
|
||||
if(btn == button_from_check) {
|
||||
vw_fill_from_decrypted(instance, dec, check_byte);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -169,6 +364,14 @@ void* subghz_protocol_decoder_vw_alloc(SubGhzEnvironment* environment) {
|
||||
SubGhzProtocolDecoderVw* instance = malloc(sizeof(SubGhzProtocolDecoderVw));
|
||||
instance->base.protocol = &vw_protocol;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
instance->type = 0;
|
||||
instance->key1_low = 0;
|
||||
instance->key1_high = 0;
|
||||
instance->key2 = 0;
|
||||
instance->crc = 0;
|
||||
|
||||
protocol_vw_load_keys(APP_ASSETS_PATH("vw"));
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
@@ -182,9 +385,13 @@ void subghz_protocol_decoder_vw_reset(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderVw* instance = context;
|
||||
instance->decoder.parser_step = VwDecoderStepReset;
|
||||
memset(instance->data, 0, 10);
|
||||
instance->generic.data_count_bit = 0;
|
||||
instance->generic.data = 0;
|
||||
instance->data_2 = 0;
|
||||
instance->type = 0;
|
||||
instance->key1_low = 0;
|
||||
instance->key1_high = 0;
|
||||
instance->key2 = 0;
|
||||
instance->crc = 0;
|
||||
instance->manchester_state = ManchesterStateMid1;
|
||||
}
|
||||
|
||||
@@ -196,7 +403,8 @@ void subghz_protocol_decoder_vw_feed(void* context, bool level, uint32_t duratio
|
||||
uint32_t te_long = subghz_protocol_vw_const.te_long;
|
||||
uint32_t te_delta = subghz_protocol_vw_const.te_delta;
|
||||
uint32_t te_med = (te_long + te_short) / 2;
|
||||
uint32_t te_end = te_long * 5;
|
||||
uint32_t te_end =
|
||||
te_long * 5; // Gap to signal end of transmission (5300us on new) (none on older)
|
||||
|
||||
ManchesterEvent event = ManchesterEventReset;
|
||||
|
||||
@@ -209,7 +417,6 @@ void subghz_protocol_decoder_vw_feed(void* context, bool level, uint32_t duratio
|
||||
|
||||
case VwDecoderStepFoundSync:
|
||||
if(DURATION_DIFF(duration, te_short) < te_delta) {
|
||||
// Stay - sync pattern repeats ~43 times
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -241,12 +448,10 @@ void subghz_protocol_decoder_vw_feed(void* context, bool level, uint32_t duratio
|
||||
|
||||
case VwDecoderStepFoundStart3:
|
||||
if(DURATION_DIFF(duration, te_med) < te_delta) {
|
||||
// Stay - med pattern repeats
|
||||
break;
|
||||
}
|
||||
|
||||
if(level && DURATION_DIFF(duration, te_short) < te_delta) {
|
||||
// Start data collection
|
||||
vw_manchester_advance(
|
||||
instance->manchester_state,
|
||||
ManchesterEventReset,
|
||||
@@ -258,8 +463,7 @@ void subghz_protocol_decoder_vw_feed(void* context, bool level, uint32_t duratio
|
||||
&instance->manchester_state,
|
||||
NULL);
|
||||
instance->generic.data_count_bit = 0;
|
||||
instance->generic.data = 0;
|
||||
instance->data_2 = 0;
|
||||
memset(instance->data, 0, 10);
|
||||
instance->decoder.parser_step = VwDecoderStepFoundData;
|
||||
break;
|
||||
}
|
||||
@@ -276,7 +480,6 @@ void subghz_protocol_decoder_vw_feed(void* context, bool level, uint32_t duratio
|
||||
event = level ? ManchesterEventLongHigh : ManchesterEventLongLow;
|
||||
}
|
||||
|
||||
// Last bit can be arbitrarily long
|
||||
if(instance->generic.data_count_bit ==
|
||||
subghz_protocol_vw_const.min_count_bit_for_found - 1 &&
|
||||
!level && duration > te_end) {
|
||||
@@ -299,8 +502,15 @@ void subghz_protocol_decoder_vw_feed(void* context, bool level, uint32_t duratio
|
||||
uint8_t subghz_protocol_decoder_vw_get_hash_data(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderVw* instance = context;
|
||||
return subghz_protocol_blocks_get_hash_data(
|
||||
&instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);
|
||||
|
||||
uint8_t hash = 0;
|
||||
size_t key_length = instance->generic.data_count_bit / 8;
|
||||
|
||||
for(size_t i = 0; i < key_length; i++) {
|
||||
hash ^= instance->data[i];
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
SubGhzProtocolStatus subghz_protocol_decoder_vw_serialize(
|
||||
@@ -308,80 +518,150 @@ SubGhzProtocolStatus subghz_protocol_decoder_vw_serialize(
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset) {
|
||||
furi_assert(context);
|
||||
|
||||
SubGhzProtocolDecoderVw* instance = context;
|
||||
SubGhzProtocolStatus res = SubGhzProtocolStatusError;
|
||||
|
||||
SubGhzProtocolStatus ret = SubGhzProtocolStatusError;
|
||||
do {
|
||||
res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
||||
if(res != SubGhzProtocolStatusOk) {
|
||||
break;
|
||||
}
|
||||
|
||||
ret = subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
||||
if(!flipper_format_rewind(flipper_format)) {
|
||||
FURI_LOG_E(TAG, "Rewind error");
|
||||
res = SubGhzProtocolStatusErrorParserOthers;
|
||||
break;
|
||||
}
|
||||
|
||||
if(ret == SubGhzProtocolStatusOk) {
|
||||
// Add VW-specific data
|
||||
uint32_t type = (instance->data_2 >> 8) & 0xFF;
|
||||
uint32_t check = instance->data_2 & 0xFF;
|
||||
uint32_t btn = (check >> 4) & 0xF;
|
||||
uint16_t key_length = instance->generic.data_count_bit / 8;
|
||||
|
||||
flipper_format_write_uint32(flipper_format, "Type", &type, 1);
|
||||
flipper_format_write_uint32(flipper_format, "Check", &check, 1);
|
||||
flipper_format_write_uint32(flipper_format, "Btn", &btn, 1);
|
||||
}
|
||||
if(!flipper_format_update_hex(flipper_format, "Key", instance->data, key_length)) {
|
||||
FURI_LOG_E(TAG, "Unable to update Key");
|
||||
res = SubGhzProtocolStatusErrorParserKey;
|
||||
break;
|
||||
}
|
||||
} while(false);
|
||||
|
||||
return ret;
|
||||
return res;
|
||||
}
|
||||
|
||||
SubGhzProtocolStatus
|
||||
subghz_protocol_decoder_vw_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderVw* instance = context;
|
||||
return subghz_block_generic_deserialize_check_count_bit(
|
||||
&instance->generic, flipper_format, subghz_protocol_vw_const.min_count_bit_for_found);
|
||||
|
||||
SubGhzProtocolStatus ret =
|
||||
subghz_block_generic_deserialize(&instance->generic, flipper_format);
|
||||
if(ret != SubGhzProtocolStatusOk) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
instance->generic.data = 0;
|
||||
|
||||
if(instance->generic.data_count_bit != subghz_protocol_vw_const.min_count_bit_for_found) {
|
||||
FURI_LOG_E(TAG, "Wrong number of bits in key");
|
||||
return SubGhzProtocolStatusErrorValueBitCount;
|
||||
}
|
||||
|
||||
if(!flipper_format_rewind(flipper_format)) {
|
||||
FURI_LOG_E(TAG, "Rewind error");
|
||||
return SubGhzProtocolStatusErrorParserOthers;
|
||||
}
|
||||
|
||||
size_t key_length = instance->generic.data_count_bit / 8;
|
||||
|
||||
if(!flipper_format_read_hex(flipper_format, "Key", instance->data, key_length)) {
|
||||
FURI_LOG_E(TAG, "Unable to read Key in decoder");
|
||||
return SubGhzProtocolStatusErrorParserKey;
|
||||
}
|
||||
|
||||
vw_parse_data(instance);
|
||||
|
||||
return SubGhzProtocolStatusOk;
|
||||
}
|
||||
|
||||
static const char* vw_get_button_name(uint8_t btn) {
|
||||
switch(btn) {
|
||||
case 0x1:
|
||||
return "UNLOCK";
|
||||
case 0x2:
|
||||
return "LOCK";
|
||||
case 0x3:
|
||||
return "Un+Lk";
|
||||
case 0x4:
|
||||
return "TRUNK";
|
||||
case 0x5:
|
||||
return "Un+Tr";
|
||||
case 0x6:
|
||||
return "Lk+Tr";
|
||||
case 0x7:
|
||||
return "Un+Lk+Tr";
|
||||
case 0x8:
|
||||
return "PANIC";
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
const char* vw_buttons[] = {
|
||||
"None",
|
||||
"Unlock",
|
||||
"Lock",
|
||||
"Un+Lk",
|
||||
"Trunk",
|
||||
"Un+Tr",
|
||||
"Lk+Tr",
|
||||
"Un+Lk+Tr",
|
||||
"Panic!",
|
||||
"Unlock!",
|
||||
"Lock!",
|
||||
"Un+Lk!",
|
||||
"Trunk!",
|
||||
"Un+Tr!",
|
||||
"Lk+Tr!",
|
||||
"Un+Lk+Tr!",
|
||||
};
|
||||
|
||||
void subghz_protocol_decoder_vw_get_string(void* context, FuriString* output) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderVw* instance = context;
|
||||
|
||||
uint8_t type = (instance->data_2 >> 8) & 0xFF;
|
||||
uint8_t check = instance->data_2 & 0xFF;
|
||||
uint8_t btn = (check >> 4) & 0xF;
|
||||
if(instance->generic.data_count_bit >= subghz_protocol_vw_const.min_count_bit_for_found) {
|
||||
vw_parse_data(instance);
|
||||
}
|
||||
|
||||
uint32_t key_high = (instance->generic.data >> 32) & 0xFFFFFFFF;
|
||||
uint32_t key_low = instance->generic.data & 0xFFFFFFFF;
|
||||
if(instance->type != 0xC0) {
|
||||
furi_string_cat_printf(
|
||||
output,
|
||||
"%s %dbit\r\n"
|
||||
"Type:%02X Unknown\r\n"
|
||||
"%016llX%04X\r\n",
|
||||
instance->generic.protocol_name,
|
||||
(int)instance->generic.data_count_bit,
|
||||
instance->type,
|
||||
(unsigned long long)((uint64_t)instance->data[0] << 56 |
|
||||
(uint64_t)instance->data[1] << 48 |
|
||||
(uint64_t)instance->data[2] << 40 |
|
||||
(uint64_t)instance->data[3] << 32 |
|
||||
(uint64_t)instance->data[4] << 24 |
|
||||
(uint64_t)instance->data[5] << 16 |
|
||||
(uint64_t)instance->data[6] << 8 | (uint64_t)instance->data[7]),
|
||||
(unsigned)((uint16_t)instance->data[8] << 8 | instance->data[9]));
|
||||
return;
|
||||
}
|
||||
|
||||
uint64_t key1_full = ((uint64_t)instance->key1_high << 32) | instance->key1_low;
|
||||
uint8_t btn_byte = (uint8_t)(instance->generic.btn << 4);
|
||||
furi_string_cat_printf(
|
||||
output,
|
||||
"%s %dbit\r\n"
|
||||
"Key:%02X%08lX%08lX%02X\r\n"
|
||||
"Type:%02X Btn:%X %s\r\n",
|
||||
"Key1:%016llX\r\n"
|
||||
"Key2:%04X Btn:%02X:%s\r\n"
|
||||
"Ser:%08lX Cnt:%06lX\r\n"
|
||||
"CRC:%02X\r\n",
|
||||
instance->generic.protocol_name,
|
||||
instance->generic.data_count_bit,
|
||||
type,
|
||||
key_high,
|
||||
key_low,
|
||||
check,
|
||||
type,
|
||||
btn,
|
||||
vw_get_button_name(btn));
|
||||
(int)instance->generic.data_count_bit,
|
||||
(unsigned long long)key1_full,
|
||||
(unsigned)instance->key2,
|
||||
(unsigned)btn_byte,
|
||||
vw_buttons[instance->generic.btn],
|
||||
(unsigned long)instance->generic.serial,
|
||||
(unsigned long)instance->generic.cnt,
|
||||
(unsigned)instance->crc);
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_vw_get_string_brief(void* context, FuriString* output) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderVw* instance = context;
|
||||
if(instance->generic.data_count_bit >= subghz_protocol_vw_const.min_count_bit_for_found) {
|
||||
vw_parse_data(instance);
|
||||
}
|
||||
if(instance->type != 0xC0) {
|
||||
furi_string_cat_printf(output, "%s Unknown", instance->generic.protocol_name);
|
||||
return;
|
||||
}
|
||||
furi_string_cat_printf(
|
||||
output,
|
||||
"%s %08lX %s",
|
||||
instance->generic.protocol_name,
|
||||
(unsigned long)instance->generic.serial,
|
||||
vw_buttons[instance->generic.btn]);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user