Merge branch 'pp_main' into zero-mega

This commit is contained in:
MX
2026-03-07 19:25:28 +03:00
9 changed files with 824 additions and 768 deletions

View File

@@ -1,5 +1,5 @@
#include <string.h>
#include "aut64.h"
#include <string.h>
// https://www.usenix.org/system/files/conference/usenixsecurity16/sec16_paper_garcia.pdf
@@ -7,261 +7,45 @@
* 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_ln[AUT64_NUM_ROUNDS][AUT64_BLOCK_SIZE] = {
{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_un[AUT64_NUM_ROUNDS][AUT64_BLOCK_SIZE] = {
{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] = {
static const uint8_t table_offset[AUT64_OFFSET_TABLE_SIZE] = {
// 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
@@ -281,7 +65,7 @@ static const uint8_t table_offset[256] = {
0x0, 0xF, 0xD, 0x2, 0x9, 0x6, 0x4, 0xB, 0x1, 0xE, 0xC, 0x3, 0x8, 0x7, 0x5, 0xA // F
};
static const uint8_t table_sub[16] = {
static const uint8_t table_sub[AUT64_SBOX_SIZE] = {
0x0,
0x1,
0x9,
@@ -300,186 +84,371 @@ static const uint8_t table_sub[16] = {
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];
}
// Build an inverse/permutation table.
// Sentinel 0xFF is used to detect missing entries and duplicates.
// Returns AUT64_OK on success, otherwise AUT64_ERR_INVALID_KEY.
static int reverse_box(uint8_t* reversed, const uint8_t* box, size_t len) {
size_t i;
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);
for(i = 0; i < len; i++) {
reversed[i] = 0xFF;
}
return (result_hi << 4) | result_lo;
for(i = 0; i < len; i++) {
const uint8_t v = box[i];
#ifdef AUT64_ENABLE_VALIDATIONS
if(v >= len) {
return AUT64_ERR_INVALID_KEY;
}
if(reversed[v] != 0xFF) {
// Duplicate value means it is not a permutation.
return AUT64_ERR_INVALID_KEY;
}
#endif
reversed[v] = (uint8_t)i;
}
#ifdef AUT64_ENABLE_VALIDATIONS
for(i = 0; i < len; i++) {
if(reversed[i] == 0xFF) {
// Missing mapping.
return AUT64_ERR_INVALID_KEY;
}
}
#endif
return AUT64_OK;
}
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;
}
#ifdef AUT64_ENABLE_VALIDATIONS
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);
// Validate that 'box' is a permutation of 0..len-1.
// Uses 0xFF sentinel logic to detect duplicates/missing values.
static int validate_box_is_permutation(const uint8_t* box, size_t len) {
uint8_t inv[32]; // enough for pbox (8) and sbox (16)
size_t i;
int i;
if(len > sizeof(inv)) {
return AUT64_ERR_INVALID_KEY;
}
for(i = 0; i < 16; i++) {
if(table_offset[offset + i] == nibble) {
break;
for(i = 0; i < len; i++) {
inv[i] = 0xFF;
}
for(i = 0; i < len; i++) {
const uint8_t v = box[i];
if(v >= len) {
return AUT64_ERR_INVALID_KEY;
}
if(inv[v] != 0xFF) {
return AUT64_ERR_INVALID_KEY;
}
inv[v] = (uint8_t)i;
}
for(i = 0; i < len; i++) {
if(inv[i] == 0xFF) {
return AUT64_ERR_INVALID_KEY;
}
}
return i;
return AUT64_OK;
}
// Validate that a key is structurally correct:
// - key nibbles are in range 0..15
// - pbox is a permutation of 0..7
// - sbox is a permutation of 0..15
// return AUT64_OK or AUT64_ERR_INVALID_KEY/AUT64_ERR_NULL_POINTER
int aut64_validate_key(const struct aut64_key* key) {
uint8_t i;
int rc;
if(!key) {
return AUT64_ERR_NULL_POINTER;
}
// key->key[] is treated as nibbles in multiple places (table_sub indexing, offset building).
for(i = 0; i < AUT64_KEY_SIZE; i++) {
if(key->key[i] >= AUT64_SBOX_SIZE) {
return AUT64_ERR_INVALID_KEY;
}
}
rc = validate_box_is_permutation(key->pbox, AUT64_PBOX_SIZE);
if(rc != AUT64_OK) {
return rc;
}
rc = validate_box_is_permutation(key->sbox, AUT64_SBOX_SIZE);
if(rc != AUT64_OK) {
return rc;
}
return AUT64_OK;
}
#endif // AUT64_ENABLE_VALIDATIONS
// Compute one 4-bit contribution to the round key
static uint8_t key_nibble(
const struct aut64_key* key,
uint8_t nibble,
const uint8_t table[AUT64_BLOCK_SIZE],
uint8_t iteration) {
const uint8_t keyValue = key->key[table[iteration]];
const uint8_t offset = (uint8_t)((keyValue << 4) | nibble);
return table_offset[offset];
}
// Compute the round compression byte derived from the current state and the key for a given round.
static uint8_t round_key(const struct aut64_key* key, const uint8_t* state, 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, (uint8_t)(state[i] >> 4), table_un[roundN], (uint8_t)i);
result_lo ^= key_nibble(key, (uint8_t)(state[i] & 0x0F), table_ln[roundN], (uint8_t)i);
}
return (uint8_t)((result_hi << 4) | result_lo);
}
// Compute the transformed key nibble used as an offset for final-byte processing in a round.
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;
final_byte_nibble(const struct aut64_key* key, const uint8_t table[AUT64_BLOCK_SIZE]) {
const uint8_t keyValue = key->key[table[AUT64_BLOCK_SIZE - 1]];
return (uint8_t)(table_sub[keyValue] << 4);
}
static uint8_t decrypt_final_byte_nibble(
const struct aut64_key key,
const uint8_t nibble,
const uint8_t table[],
const uint8_t result) {
// Compute the inverse lookup for a final-byte nibble during encryption.
static uint8_t encrypt_final_byte_nibble(
const struct aut64_key* key,
uint8_t nibble,
const uint8_t table[AUT64_BLOCK_SIZE]) {
const uint8_t offset = final_byte_nibble(key, table);
return table_offset[(result ^ nibble) + offset];
for(int i = 0; i < 16; i++) {
if(table_offset[(uint8_t)(offset + i)] == nibble) {
return (uint8_t)i;
}
}
// Should never happen for valid inputs; return 0 as a defined value.
return 0;
}
// Perform the compression step for one encryption round, producing the new last byte.
static uint8_t
decrypt_compress(const struct aut64_key key, const uint8_t state[], const uint8_t roundN) {
encrypt_compress(const struct aut64_key* key, const uint8_t* state, uint8_t roundN) {
const uint8_t roundKey = round_key(key, state, roundN);
uint8_t result_hi = roundKey >> 4, result_lo = roundKey & 0x0F;
uint8_t result_hi = (uint8_t)(roundKey >> 4), result_lo = (uint8_t)(roundKey & 0x0F);
result_hi ^= encrypt_final_byte_nibble(
key, (uint8_t)(state[AUT64_BLOCK_SIZE - 1] >> 4), table_un[roundN]);
result_lo ^= encrypt_final_byte_nibble(
key, (uint8_t)(state[AUT64_BLOCK_SIZE - 1] & 0x0F), table_ln[roundN]);
return (uint8_t)((result_hi << 4) | result_lo);
}
// Reverse the final-byte nibble transformation during decryption.
static uint8_t decrypt_final_byte_nibble(
const struct aut64_key* key,
uint8_t nibble,
const uint8_t table[AUT64_BLOCK_SIZE],
uint8_t result) {
const uint8_t offset = final_byte_nibble(key, table);
return table_offset[(uint8_t)((result ^ nibble) + offset)];
}
// Perform the compression step for one decryption round, restoring the previous last byte.
static uint8_t
decrypt_compress(const struct aut64_key* key, const uint8_t* state, uint8_t roundN) {
const uint8_t roundKey = round_key(key, state, roundN);
uint8_t result_hi = (uint8_t)(roundKey >> 4), result_lo = (uint8_t)(roundKey & 0x0F);
result_hi = decrypt_final_byte_nibble(
key, state[AUT64_BLOCK_SIZE - 1] >> 4, table_un[roundN], result_hi);
key, (uint8_t)(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);
key, (uint8_t)(state[AUT64_BLOCK_SIZE - 1] & 0x0F), table_ln[roundN], result_lo);
return (result_hi << 4) | result_lo;
return (uint8_t)((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];
// Apply the S-box substitution to a single byte.
static uint8_t substitute(const struct aut64_key* key, uint8_t byte) {
return (uint8_t)((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};
// Apply the byte-level permutation (pbox) to the 8-byte state block.
static void permute_bytes(const struct aut64_key* key, uint8_t* state) {
// Key is validated up-front, so pbox[] is a correct permutation of 0..7.
uint8_t result[AUT64_PBOX_SIZE];
for(int i = 0; i < AUT64_PBOX_SIZE; i++) {
result[key.pbox[i]] = state[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) {
// Apply bit-level permutation to a single byte using the pbox mapping.
static uint8_t permute_bits(const struct aut64_key* key, uint8_t byte) {
// Key is validated up-front, so pbox[] is a correct permutation of 0..7.
uint8_t result = 0;
for(int i = 0; i < 8; i++) {
if(byte & (1 << i)) {
result |= (1 << key.pbox[i]);
result |= (uint8_t)(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;
}
}
}
}
// Encrypt one 8-byte block in place using the provided validated key.
int aut64_encrypt(const struct aut64_key* key, uint8_t* message) {
int rc;
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);
#ifdef AUT64_ENABLE_VALIDATIONS
if(!key || !message) {
return AUT64_ERR_NULL_POINTER;
}
// Validate key before doing anything. This prevents silent, unsafe behavior.
rc = aut64_validate_key(key);
if(rc != AUT64_OK) {
return rc;
}
#endif
// Build a reversed key (inverse pbox and sbox) ...
// Fully initialize to avoid any uninitialized fields/padding.
struct aut64_key reverse_key = (struct aut64_key){0};
reverse_key.index = key->index;
memcpy(reverse_key.key, key->key, AUT64_KEY_SIZE);
rc = reverse_box(reverse_key.pbox, key->pbox, AUT64_PBOX_SIZE);
if(rc != AUT64_OK) {
return rc;
}
rc = reverse_box(reverse_key.sbox, key->sbox, AUT64_SBOX_SIZE);
if(rc != AUT64_OK) {
return rc;
}
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]);
permute_bytes(&reverse_key, message);
message[AUT64_BLOCK_SIZE - 1] = encrypt_compress(&reverse_key, message, (uint8_t)i);
message[AUT64_BLOCK_SIZE - 1] = substitute(&reverse_key, message[AUT64_BLOCK_SIZE - 1]);
message[AUT64_BLOCK_SIZE - 1] = permute_bits(&reverse_key, message[AUT64_BLOCK_SIZE - 1]);
message[AUT64_BLOCK_SIZE - 1] = substitute(&reverse_key, message[AUT64_BLOCK_SIZE - 1]);
}
return AUT64_OK;
}
void aut64_decrypt(const struct aut64_key key, uint8_t message[]) {
// Decrypt one 8-byte block in place using the provided validated key.
int aut64_decrypt(const struct aut64_key* key, uint8_t* message) {
#ifdef AUT64_ENABLE_VALIDATIONS
if(!key || !message) {
return AUT64_ERR_NULL_POINTER;
}
int rc = aut64_validate_key(key);
if(rc != AUT64_OK) {
return rc;
}
#endif
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);
message[AUT64_BLOCK_SIZE - 1] = substitute(key, message[AUT64_BLOCK_SIZE - 1]);
message[AUT64_BLOCK_SIZE - 1] = permute_bits(key, message[AUT64_BLOCK_SIZE - 1]);
message[AUT64_BLOCK_SIZE - 1] = substitute(key, message[AUT64_BLOCK_SIZE - 1]);
message[AUT64_BLOCK_SIZE - 1] = decrypt_compress(key, message, (uint8_t)i);
permute_bytes(key, message);
}
return AUT64_OK;
}
void aut64_pack(uint8_t dest[], const struct aut64_key src) {
dest[0] = src.index;
#ifdef AUT64_PACK_SUPPORT
// Serialize a validated key structure into its 16-byte packed format.
int aut64_pack(uint8_t* dest, const struct aut64_key* src) {
#ifdef AUT64_ENABLE_VALIDATIONS
if(!dest || !src) {
return AUT64_ERR_NULL_POINTER;
}
// Validate the key we are about to pack. This prevents producing garbage packed keys.
int rc = aut64_validate_key(src);
if(rc != AUT64_OK) {
return rc;
}
#endif
for(uint8_t i = 0; i < sizeof(src.key) / 2; i++) {
dest[i + 1] = (src.key[i * 2] << 4) | src.key[i * 2 + 1];
// Initialize the output so callers never observe stale bytes.
memset(dest, 0, AUT64_PACKED_KEY_SIZE);
dest[0] = src->index;
for(uint8_t i = 0; i < AUT64_KEY_SIZE / 2; i++) {
dest[i + 1] = (uint8_t)((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];
for(uint8_t i = 0; i < AUT64_PBOX_SIZE; i++) {
pbox = (pbox << 3) | src->pbox[i];
}
dest[5] = pbox >> 16;
dest[6] = (pbox >> 8) & 0xFF;
dest[7] = pbox & 0xFF;
dest[5] = (uint8_t)(pbox >> 16);
dest[6] = (uint8_t)((pbox >> 8) & 0xFF);
dest[7] = (uint8_t)(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];
for(uint8_t i = 0; i < AUT64_SBOX_SIZE / 2; i++) {
dest[i + 8] = (uint8_t)((src->sbox[i * 2] << 4) | src->sbox[i * 2 + 1]);
}
return AUT64_OK;
}
#endif // AUT64_PACK_SUPPORT
// Deserialize a 16-byte packed key into a key structure and validate it.
int aut64_unpack(struct aut64_key* dest, const uint8_t* src) {
#ifdef AUT64_ENABLE_VALIDATIONS
if(!dest || !src) {
return AUT64_ERR_NULL_POINTER;
}
#endif
// Clear the whole struct first, so all fields are in a defined state.
*dest = (struct aut64_key){0};
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;
for(uint8_t i = 0; i < AUT64_KEY_SIZE / 2; i++) {
dest->key[i * 2] = (uint8_t)(src[i + 1] >> 4);
dest->key[i * 2 + 1] = (uint8_t)(src[i + 1] & 0xF);
}
uint32_t pbox = (src[5] << 16) | (src[6] << 8) | src[7];
uint32_t pbox = ((uint32_t)src[5] << 16) | ((uint32_t)src[6] << 8) | (uint32_t)src[7];
for(int8_t i = sizeof(dest->pbox) - 1; i >= 0; i--) {
dest->pbox[i] = pbox & 0x7;
for(int8_t i = AUT64_PBOX_SIZE - 1; i >= 0; i--) {
dest->pbox[i] = (uint8_t)(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;
for(uint8_t i = 0; i < AUT64_SBOX_SIZE / 2; i++) {
dest->sbox[i * 2] = (uint8_t)(src[i + 8] >> 4);
dest->sbox[i * 2 + 1] = (uint8_t)(src[i + 8] & 0xF);
}
#ifdef AUT64_ENABLE_VALIDATIONS
// Validate what we just unpacked. If invalid, return error.
// We do not fix up broken keys silently.
int rc = aut64_validate_key(dest);
if(rc != AUT64_OK) {
return AUT64_ERR_INVALID_PACKED;
}
#endif
return AUT64_OK;
}

View File

@@ -1,12 +1,29 @@
#pragma once
#include <stdint.h>
#include <stdbool.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
// uncomment to activate key validation, index boundary verifications, ...
//#define AUT64_ENABLE_VALIDATIONS
// uncomment to add compilation of aut64_pack (currently unused in the code)
//#define AUT64_PACK_SUPPORT
#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
#define AUT64_PACKED_KEY_SIZE 16
// Internal helper table size (offset lookup table).
#define AUT64_OFFSET_TABLE_SIZE 256
// Status codes. Keep it simple and C-friendly.
#define AUT64_OK 0
#define AUT64_ERR_INVALID_KEY (-1)
#define AUT64_ERR_INVALID_PACKED (-2)
#define AUT64_ERR_NULL_POINTER (-3)
struct aut64_key {
uint8_t index;
@@ -15,9 +32,18 @@ struct aut64_key {
uint8_t sbox[AUT64_SBOX_SIZE];
};
#define AUT64_KEY_STRUCT_PACKED_SIZE 16
#ifdef AUT64_ENABLE_VALIDATIONS
// Optional helper if callers want to check keys up-front.
int aut64_validate_key(const struct aut64_key* key);
#endif
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[]);
// Pointers are used for both the key and the message to avoid implicit copies.
// The message buffer must be at least AUT64_BLOCK_SIZE bytes.
int aut64_encrypt(const struct aut64_key* key, uint8_t* message);
int aut64_decrypt(const struct aut64_key* key, uint8_t* message);
// Packed key buffer must be at least AUT64_PACKED_KEY_SIZE bytes.
#ifdef AUT64_PACK_SUPPORT
int aut64_pack(uint8_t* dest, const struct aut64_key* src);
#endif
int aut64_unpack(struct aut64_key* dest, const uint8_t* src);

View File

@@ -1,8 +1,14 @@
#include "fiat_v0.h"
#include "../protopirate_app_i.h"
#include <lib/toolbox/manchester_decoder.h>
#include <inttypes.h>
#define TAG "FiatProtocolV0"
#define TAG "FiatProtocolV0"
#define FIAT_PROTOCOL_V0_NAME "Fiat V0"
#define FIAT_V0_PREAMBLE_PAIRS 150
#define FIAT_V0_GAP_US 800
#define FIAT_V0_TOTAL_BURSTS 3
#define FIAT_V0_INTER_BURST_GAP 25000
static const SubGhzBlockConst subghz_protocol_fiat_v0_const = {
.te_short = 200,
@@ -11,11 +17,6 @@ static const SubGhzBlockConst subghz_protocol_fiat_v0_const = {
.min_count_bit_for_found = 64,
};
#define FIAT_V0_PREAMBLE_PAIRS 150
#define FIAT_V0_GAP_US 800
#define FIAT_V0_TOTAL_BURSTS 3
#define FIAT_V0_INTER_BURST_GAP 25000
struct SubGhzProtocolDecoderFiatV0 {
SubGhzProtocolDecoderBase base;
SubGhzBlockDecoder decoder;
@@ -26,9 +27,10 @@ struct SubGhzProtocolDecoderFiatV0 {
uint32_t data_low;
uint32_t data_high;
uint8_t bit_count;
uint32_t cnt;
uint32_t serial;
uint8_t btn;
uint32_t hop;
uint32_t fix;
uint8_t endbyte;
uint8_t final_count;
uint32_t te_last;
};
@@ -37,9 +39,12 @@ struct SubGhzProtocolEncoderFiatV0 {
SubGhzProtocolBlockEncoder encoder;
SubGhzBlockGeneric generic;
uint32_t cnt;
uint32_t serial;
uint8_t btn;
uint32_t hop;
uint32_t fix;
uint8_t endbyte;
// Capacity of encoder.upload in LevelDuration elements.
size_t upload_capacity;
};
typedef enum {
@@ -80,22 +85,44 @@ const SubGhzProtocol fiat_protocol_v0 = {
// ENCODER IMPLEMENTATION
// ============================================================================
static size_t fiat_v0_encoder_calc_required_upload(void) {
// Per burst:
// - preamble: FIAT_V0_PREAMBLE_PAIRS pairs -> 2 elements each
// - data: 64 bits Manchester -> 2 elements per bit
// - endbyte: 7 bits Manchester -> 2 elements per bit
// - trailer: 1 element (extended low)
const size_t per_burst = (FIAT_V0_PREAMBLE_PAIRS * 2) + (64 * 2) + (7 * 2) + 1;
// Inter-burst gap is a single element between bursts.
return (FIAT_V0_TOTAL_BURSTS * per_burst) +
(FIAT_V0_TOTAL_BURSTS > 0 ? (FIAT_V0_TOTAL_BURSTS - 1) : 0);
}
static void
fiat_v0_encoder_ensure_upload_capacity(SubGhzProtocolEncoderFiatV0* instance, size_t required) {
furi_check(instance);
furi_check(required <= instance->upload_capacity);
LevelDuration* new_upload =
realloc(instance->encoder.upload, required * sizeof(LevelDuration));
furi_check(new_upload);
instance->encoder.upload = new_upload;
instance->upload_capacity = required;
}
void* subghz_protocol_encoder_fiat_v0_alloc(SubGhzEnvironment* environment) {
UNUSED(environment);
SubGhzProtocolEncoderFiatV0* instance = malloc(sizeof(SubGhzProtocolEncoderFiatV0));
SubGhzProtocolEncoderFiatV0* instance = calloc(1, sizeof(SubGhzProtocolEncoderFiatV0));
furi_check(instance);
instance->base.protocol = &fiat_protocol_v0;
instance->generic.protocol_name = instance->base.protocol->name;
instance->encoder.repeat = 10;
instance->encoder.size_upload = 1024;
instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));
instance->encoder.size_upload = 0;
instance->upload_capacity = fiat_v0_encoder_calc_required_upload();
instance->encoder.upload = calloc(instance->upload_capacity, sizeof(LevelDuration));
furi_check(instance->encoder.upload);
instance->encoder.is_running = false;
instance->encoder.front = 0;
instance->cnt = 0;
instance->serial = 0;
instance->btn = 0;
return instance;
}
@@ -113,20 +140,17 @@ static void subghz_protocol_encoder_fiat_v0_get_upload(SubGhzProtocolEncoderFiat
furi_check(instance);
size_t index = 0;
const size_t required = fiat_v0_encoder_calc_required_upload();
fiat_v0_encoder_ensure_upload_capacity(instance, required);
uint32_t te_short = subghz_protocol_fiat_v0_const.te_short;
uint32_t te_long = subghz_protocol_fiat_v0_const.te_long;
FURI_LOG_I(
TAG,
"Building upload: cnt=0x%08lX, serial=0x%08lX, btn=0x%02X",
instance->cnt,
instance->serial,
instance->btn);
uint64_t data = ((uint64_t)instance->cnt << 32) | instance->serial;
// Reverse the decoder's btn fix: decoder does (x << 1) | 1
uint8_t btn_to_send = instance->btn >> 1;
"Building upload: hop=0x%08lX, fix=0x%08lX, endbyte=0x%02X",
instance->hop,
instance->fix,
instance->endbyte & 0x7F);
for(uint8_t burst = 0; burst < FIAT_V0_TOTAL_BURSTS; burst++) {
if(burst > 0) {
@@ -134,85 +158,54 @@ static void subghz_protocol_encoder_fiat_v0_get_upload(SubGhzProtocolEncoderFiat
level_duration_make(false, FIAT_V0_INTER_BURST_GAP);
}
// Preamble: HIGH-LOW pairs
// Preamble: alternating short pulses
for(int i = 0; i < FIAT_V0_PREAMBLE_PAIRS; i++) {
instance->encoder.upload[index++] = level_duration_make(true, te_short);
instance->encoder.upload[index++] = level_duration_make(false, te_short);
}
// Replace last preamble LOW with gap
// Extend last LOW to create the gap (~FIAT_V0_GAP_US)
instance->encoder.upload[index - 1] = level_duration_make(false, FIAT_V0_GAP_US);
// First bit (bit 63) - special handling after gap
bool first_bit = (data >> 63) & 1;
if(first_bit) {
// First bit is 1: LONG HIGH
instance->encoder.upload[index++] = level_duration_make(true, te_long);
} else {
// First bit is 0: SHORT HIGH + LONG LOW
instance->encoder.upload[index++] = level_duration_make(true, te_short);
instance->encoder.upload[index++] = level_duration_make(false, te_long);
}
// Combine hop and fix into 64-bit data
uint64_t data = ((uint64_t)instance->hop << 32) | instance->fix;
bool prev_bit = first_bit;
// Encode remaining 63 data bits (bits 62:0) using differential Manchester
for(int bit = 62; bit >= 0; bit--) {
// Manchester encode 64 bits of data
for(int bit = 63; bit >= 0; bit--) {
bool curr_bit = (data >> bit) & 1;
if(!prev_bit && !curr_bit) {
// 0->0: SHORT HIGH + SHORT LOW
if(curr_bit) {
instance->encoder.upload[index++] = level_duration_make(true, te_short);
instance->encoder.upload[index++] = level_duration_make(false, te_short);
} else if(!prev_bit && curr_bit) {
// 0->1: LONG HIGH
instance->encoder.upload[index++] = level_duration_make(true, te_long);
} else if(prev_bit && !curr_bit) {
// 1->0: LONG LOW
instance->encoder.upload[index++] = level_duration_make(false, te_long);
} else {
// 1->1: SHORT LOW + SHORT HIGH
instance->encoder.upload[index++] = level_duration_make(false, te_short);
instance->encoder.upload[index++] = level_duration_make(true, te_short);
}
prev_bit = curr_bit;
}
// Encode 6 btn bits using same differential pattern
for(int bit = 5; bit >= 0; bit--) {
bool curr_bit = (btn_to_send >> bit) & 1;
if(!prev_bit && !curr_bit) {
instance->encoder.upload[index++] = level_duration_make(true, te_short);
instance->encoder.upload[index++] = level_duration_make(false, te_short);
} else if(!prev_bit && curr_bit) {
instance->encoder.upload[index++] = level_duration_make(true, te_long);
} else if(prev_bit && !curr_bit) {
instance->encoder.upload[index++] = level_duration_make(false, te_long);
} else {
instance->encoder.upload[index++] = level_duration_make(false, te_short);
instance->encoder.upload[index++] = level_duration_make(true, te_short);
}
prev_bit = curr_bit;
}
// End marker - ensure we end with LOW
if(prev_bit) {
instance->encoder.upload[index++] = level_duration_make(false, te_short);
// Manchester encode 7 bits of endbyte (bits 6:0) - signal has 71 total bits
uint8_t endbyte = (uint8_t)(instance->endbyte & 0x7F);
for(int bit = 6; bit >= 0; bit--) {
bool curr_bit = (endbyte >> bit) & 1;
if(curr_bit) {
instance->encoder.upload[index++] = level_duration_make(true, te_short);
instance->encoder.upload[index++] = level_duration_make(false, te_short);
} else {
instance->encoder.upload[index++] = level_duration_make(false, te_short);
instance->encoder.upload[index++] = level_duration_make(true, te_short);
}
}
instance->encoder.upload[index++] = level_duration_make(false, te_short * 8);
// End with extended LOW (will be followed by inter-burst gap or end)
instance->encoder.upload[index++] = level_duration_make(false, te_short * 4);
}
furi_check(index <= instance->upload_capacity);
instance->encoder.size_upload = index;
instance->encoder.front = 0;
FURI_LOG_I(
TAG,
"Upload built: %zu elements, btn_to_send=0x%02X",
instance->encoder.size_upload,
btn_to_send);
FURI_LOG_I(TAG, "Upload built: %zu elements", instance->encoder.size_upload);
}
SubGhzProtocolStatus
@@ -227,31 +220,40 @@ SubGhzProtocolStatus
flipper_format_rewind(flipper_format);
FuriString* temp_str = furi_string_alloc();
furi_check(temp_str);
do {
FuriString* temp_str = furi_string_alloc();
if(!flipper_format_read_string(flipper_format, "Protocol", temp_str)) {
FURI_LOG_E(TAG, "Missing Protocol");
furi_string_free(temp_str);
break;
}
if(!furi_string_equal(temp_str, instance->base.protocol->name)) {
FURI_LOG_E(TAG, "Wrong protocol: %s", furi_string_get_cstr(temp_str));
furi_string_free(temp_str);
break;
}
furi_string_free(temp_str);
uint32_t bit_count_temp;
if(!flipper_format_read_uint32(flipper_format, "Bit", &bit_count_temp, 1)) {
uint32_t bit_count_temp = 0;
if(flipper_format_read_uint32(flipper_format, "Bit", &bit_count_temp, 1)) {
// This protocol transmits 71 bits: 64-bit key + 7-bit endbyte.
// Let's do according to what's read, (64 or 71), else 71
if(bit_count_temp == 64 || bit_count_temp == 71) {
instance->generic.data_count_bit = bit_count_temp;
} else {
FURI_LOG_E(
TAG,
"Inconsistent Bit value of %d was defaulted to 71",
instance->generic.data_count_bit);
instance->generic.data_count_bit = 71;
}
} else {
FURI_LOG_E(TAG, "Missing Bit");
break;
}
temp_str = furi_string_alloc();
if(!flipper_format_read_string(flipper_format, "Key", temp_str)) {
FURI_LOG_E(TAG, "Missing Key");
furi_string_free(temp_str);
break;
}
@@ -265,33 +267,44 @@ SubGhzProtocolStatus
if(c == ' ') continue;
uint8_t nibble;
if(c >= '0' && c <= '9')
nibble = c - '0';
else if(c >= 'A' && c <= 'F')
nibble = c - 'A' + 10;
else if(c >= 'a' && c <= 'f')
nibble = c - 'a' + 10;
else
if(c >= '0' && c <= '9') {
nibble = (uint8_t)(c - '0');
} else if(c >= 'A' && c <= 'F') {
nibble = (uint8_t)(c - 'A' + 10);
} else if(c >= 'a' && c <= 'f') {
nibble = (uint8_t)(c - 'a' + 10);
} else {
break;
}
key = (key << 4) | nibble;
hex_pos++;
}
furi_string_free(temp_str);
if(hex_pos != 16) {
FURI_LOG_E(TAG, "Key parse error: expected 16 hex nibbles, got %u", (unsigned)hex_pos);
break;
}
instance->generic.data = key;
instance->cnt = (uint32_t)(key >> 32);
instance->serial = (uint32_t)(key & 0xFFFFFFFF);
instance->hop = (uint32_t)(key >> 32);
instance->fix = (uint32_t)(key & 0xFFFFFFFF);
uint32_t btn_temp = 0;
if(flipper_format_read_uint32(flipper_format, "Btn", &btn_temp, 1)) {
instance->btn = (uint8_t)btn_temp;
instance->endbyte = (uint8_t)(btn_temp & 0x7F);
} else {
instance->btn = 0;
instance->endbyte = 0;
}
if(!flipper_format_read_uint32(
flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1)) {
instance->generic.btn = instance->endbyte;
instance->generic.cnt = instance->hop;
instance->generic.serial = instance->fix;
uint32_t repeat_temp = 0;
if(flipper_format_read_uint32(flipper_format, "Repeat", &repeat_temp, 1)) {
instance->encoder.repeat = repeat_temp;
} else {
instance->encoder.repeat = 10;
}
@@ -300,14 +313,15 @@ SubGhzProtocolStatus
FURI_LOG_I(
TAG,
"Encoder ready: cnt=0x%08lX, serial=0x%08lX, btn=0x%02X",
instance->cnt,
instance->serial,
instance->btn);
"Encoder ready: hop=0x%08lX, fix=0x%08lX, endbyte=0x%02X",
instance->hop,
instance->fix,
instance->endbyte);
ret = SubGhzProtocolStatusOk;
} while(false);
furi_string_free(temp_str);
return ret;
}
@@ -342,7 +356,8 @@ LevelDuration subghz_protocol_encoder_fiat_v0_yield(void* context) {
void* subghz_protocol_decoder_fiat_v0_alloc(SubGhzEnvironment* environment) {
UNUSED(environment);
SubGhzProtocolDecoderFiatV0* instance = malloc(sizeof(SubGhzProtocolDecoderFiatV0));
SubGhzProtocolDecoderFiatV0* instance = calloc(1, sizeof(SubGhzProtocolDecoderFiatV0));
furi_check(instance);
instance->base.protocol = &fiat_protocol_v0;
instance->generic.protocol_name = instance->base.protocol->name;
return instance;
@@ -363,20 +378,35 @@ void subghz_protocol_decoder_fiat_v0_reset(void* context) {
instance->data_low = 0;
instance->data_high = 0;
instance->bit_count = 0;
instance->cnt = 0;
instance->serial = 0;
instance->btn = 0;
instance->hop = 0;
instance->fix = 0;
instance->endbyte = 0;
instance->final_count = 0;
instance->te_last = 0;
instance->manchester_state = ManchesterStateMid1;
}
// Helper function to reset decoder to data state with proper Manchester initialization
static void
fiat_v0_decoder_enter_data_state(SubGhzProtocolDecoderFiatV0* instance, uint32_t duration) {
instance->decoder_state = FiatV0DecoderStepData;
instance->preamble_count = 0;
instance->data_low = 0;
instance->data_high = 0;
instance->bit_count = 0;
instance->te_last = duration;
// Critical: Reset Manchester state when entering data mode
manchester_advance(
instance->manchester_state, ManchesterEventReset, &instance->manchester_state, NULL);
}
void subghz_protocol_decoder_fiat_v0_feed(void* context, bool level, uint32_t duration) {
furi_check(context);
SubGhzProtocolDecoderFiatV0* instance = context;
uint32_t te_short = (uint32_t)subghz_protocol_fiat_v0_const.te_short;
uint32_t te_long = (uint32_t)subghz_protocol_fiat_v0_const.te_long;
uint32_t te_delta = (uint32_t)subghz_protocol_fiat_v0_const.te_delta;
uint32_t gap_threshold = 800;
uint32_t gap_threshold = FIAT_V0_GAP_US;
uint32_t diff;
switch(instance->decoder_state) {
@@ -406,76 +436,68 @@ void subghz_protocol_decoder_fiat_v0_feed(void* context, bool level, uint32_t du
case FiatV0DecoderStepPreamble:
if(level) {
// HIGH pulse during preamble - just track timing
if(duration < te_short) {
diff = te_short - duration;
} else {
diff = duration - te_short;
}
if(diff < te_delta) {
instance->preamble_count++;
instance->te_last = duration;
} else {
instance->decoder_state = FiatV0DecoderStepReset;
}
return;
}
// LOW pulse - check if it's the gap after preamble
if(duration < te_short) {
diff = te_short - duration;
if(diff < te_delta) {
instance->preamble_count++;
instance->te_last = duration;
if(instance->preamble_count >= 0x96) {
if(duration < gap_threshold) {
diff = gap_threshold - duration;
} else {
diff = duration - gap_threshold;
}
if(diff < te_delta) {
instance->decoder_state = FiatV0DecoderStepData;
instance->preamble_count = 0;
instance->data_low = 0;
instance->data_high = 0;
instance->bit_count = 0;
instance->te_last = duration;
return;
}
}
} else {
instance->decoder_state = FiatV0DecoderStepReset;
if(instance->preamble_count >= 0x96) {
if(duration < gap_threshold) {
diff = gap_threshold - duration;
} else {
diff = duration - gap_threshold;
}
if(diff < te_delta) {
instance->decoder_state = FiatV0DecoderStepData;
instance->preamble_count = 0;
instance->data_low = 0;
instance->data_high = 0;
instance->bit_count = 0;
instance->te_last = duration;
return;
}
}
}
} else {
diff = duration - te_short;
if(diff < te_delta) {
instance->preamble_count++;
instance->te_last = duration;
} else {
instance->decoder_state = FiatV0DecoderStepReset;
}
}
if(diff < te_delta) {
// Normal short LOW - continue preamble
instance->preamble_count++;
instance->te_last = duration;
} else {
// Not a short pulse - check if it's the gap
if(instance->preamble_count >= 0x96) {
if(duration >= 799) {
diff = duration - gap_threshold;
} else {
// We have enough preamble, check for gap
if(duration < gap_threshold) {
diff = gap_threshold - duration;
} else {
diff = duration - gap_threshold;
}
if(diff < te_delta) {
instance->decoder_state = FiatV0DecoderStepData;
instance->preamble_count = 0;
instance->data_low = 0;
instance->data_high = 0;
instance->bit_count = 0;
instance->te_last = duration;
// Valid gap detected - transition to data state
fiat_v0_decoder_enter_data_state(instance, duration);
return;
}
}
// Invalid timing or not enough preamble
instance->decoder_state = FiatV0DecoderStepReset;
}
// Also check for gap even with valid short timing if we have enough preamble
if(instance->preamble_count >= 0x96 &&
instance->decoder_state == FiatV0DecoderStepPreamble) {
if(duration < gap_threshold) {
diff = gap_threshold - duration;
} else {
diff = duration - gap_threshold;
}
if(diff < te_delta) {
// Valid gap detected - transition to data state
fiat_v0_decoder_enter_data_state(instance, duration);
return;
}
}
break;
case FiatV0DecoderStepData: {
case FiatV0DecoderStepData:
ManchesterEvent event = ManchesterEventReset;
if(duration < te_short) {
diff = te_short - duration;
@@ -514,20 +536,68 @@ void subghz_protocol_decoder_fiat_v0_feed(void* context, bool level, uint32_t du
instance->bit_count++;
if(instance->bit_count == 0x40) {
instance->serial = instance->data_low;
instance->cnt = instance->data_high;
instance->fix = instance->data_low;
instance->hop = instance->data_high;
FURI_LOG_D(
TAG,
"Bit 64: fix=0x%08lX, hop=0x%08lX, clearing data_low/data_high",
instance->fix,
instance->hop);
instance->data_low = 0;
instance->data_high = 0;
}
if(instance->bit_count > 0x46) {
instance->btn = (uint8_t)((instance->data_low << 1) | 1);
#ifndef REMOVE_LOGS
if(instance->bit_count > 0x40 && instance->bit_count <= 0x47) {
uint8_t endbyte_bit_num = instance->bit_count - 0x40;
uint8_t endbyte_bit_index = endbyte_bit_num - 1;
uint8_t data_low_byte = (uint8_t)instance->data_low;
char binary_str[9] = {0};
for(int i = 7; i >= 0; i--) {
binary_str[7 - i] = (data_low_byte & (1 << i)) ? '1' : '0';
}
FURI_LOG_D(
TAG,
"Bit %d (endbyte bit %d/%d): new_bit=%lu, data_low=0x%08lX (0x%02X), binary=0b%s",
instance->bit_count,
endbyte_bit_index,
endbyte_bit_num - 1,
(unsigned long)new_bit,
instance->data_low,
data_low_byte,
binary_str);
}
#endif
if(instance->bit_count == 0x47) {
#ifndef REMOVE_LOGS
uint8_t data_low_byte = (uint8_t)instance->data_low;
char binary_str[9] = {0};
for(int i = 7; i >= 0; i--) {
binary_str[7 - i] = (data_low_byte & (1 << i)) ? '1' : '0';
}
FURI_LOG_D(
TAG,
"EXTRACTING AT BIT 71: bit_count=%d, data_low=0x%08lX (0x%02X), binary=0b%s, accumulated %d bits after bit 64",
instance->bit_count,
instance->data_low,
data_low_byte,
binary_str,
instance->bit_count - 0x40);
#endif
instance->final_count = instance->bit_count;
instance->endbyte = (uint8_t)instance->data_low;
instance->generic.data = ((uint64_t)instance->cnt << 32) | instance->serial;
FURI_LOG_D(
TAG,
"EXTRACTED ENDBYTE: endbyte=0x%02X (decimal=%d), expected=0x0D (13)",
instance->endbyte,
instance->endbyte & 0x7F);
instance->generic.data = ((uint64_t)instance->hop << 32) | instance->fix;
instance->generic.data_count_bit = 71;
instance->generic.serial = instance->serial;
instance->generic.btn = instance->btn;
instance->generic.cnt = instance->cnt;
instance->generic.serial = instance->fix;
instance->generic.btn = instance->endbyte;
instance->generic.cnt = instance->hop;
if(instance->base.callback) {
instance->base.callback(&instance->base, instance->base.context);
@@ -539,11 +609,39 @@ void subghz_protocol_decoder_fiat_v0_feed(void* context, bool level, uint32_t du
instance->decoder_state = FiatV0DecoderStepReset;
}
}
} else {
if(instance->bit_count == 0x47) {
// We have exactly 71 bits - extract endbyte
uint8_t data_low_byte = (uint8_t)instance->data_low;
instance->endbyte = data_low_byte;
FURI_LOG_D(
TAG,
"GAP PATH EXTRACTION (71 bits): bit_count=%d, endbyte=0x%02X",
instance->bit_count,
instance->endbyte & 0x7F);
instance->generic.data = ((uint64_t)instance->hop << 32) | instance->fix;
instance->generic.data_count_bit = 71;
instance->generic.serial = instance->fix;
instance->generic.btn = instance->endbyte;
instance->generic.cnt = instance->hop;
if(instance->base.callback) {
instance->base.callback(&instance->base, instance->base.context);
}
instance->data_low = 0;
instance->data_high = 0;
instance->bit_count = 0;
instance->decoder_state = FiatV0DecoderStepReset;
} else if(instance->bit_count < 0x40) {
instance->decoder_state = FiatV0DecoderStepReset;
}
}
instance->te_last = duration;
break;
}
}
}
uint8_t subghz_protocol_decoder_fiat_v0_get_hash_data(void* context) {
@@ -566,24 +664,28 @@ SubGhzProtocolStatus subghz_protocol_decoder_fiat_v0_serialize(
do {
if(!flipper_format_write_uint32(flipper_format, "Frequency", &preset->frequency, 1)) break;
if(!flipper_format_write_string_cstr(
flipper_format, "Preset", furi_string_get_cstr(preset->name)))
break;
if(!flipper_format_write_string_cstr(
flipper_format, "Protocol", instance->generic.protocol_name))
break;
uint32_t bits = 71;
uint32_t bits = instance->generic.data_count_bit;
if(!flipper_format_write_uint32(flipper_format, "Bit", &bits, 1)) break;
char key_str[20];
snprintf(key_str, sizeof(key_str), "%08lX%08lX", instance->cnt, instance->serial);
snprintf(key_str, sizeof(key_str), "%08lX%08lX", instance->hop, instance->fix);
if(!flipper_format_write_string_cstr(flipper_format, "Key", key_str)) break;
if(!flipper_format_write_uint32(flipper_format, "Cnt", &instance->cnt, 1)) break;
if(!flipper_format_write_uint32(flipper_format, "Serial", &instance->serial, 1)) break;
uint32_t temp = instance->hop;
if(!flipper_format_write_uint32(flipper_format, "Cnt", &temp, 1)) break;
uint32_t temp = instance->btn;
if(!flipper_format_write_uint32(flipper_format, "Serial", &instance->fix, 1)) break;
temp = instance->endbyte;
if(!flipper_format_write_uint32(flipper_format, "Btn", &temp, 1)) break;
ret = SubGhzProtocolStatusOk;
@@ -608,13 +710,14 @@ void subghz_protocol_decoder_fiat_v0_get_string(void* context, FuriString* outpu
output,
"%s %dbit\r\n"
"Key:%08lX%08lX\r\n"
"Sn:%08lX Btn:%02X\r\n"
"Cnt:%08lX\r\n",
"Hop:%08lX\r\n"
"Sn:%08lX\r\n"
"EndByte:%02X\r\n",
instance->generic.protocol_name,
instance->generic.data_count_bit,
instance->cnt,
instance->serial,
instance->serial,
instance->btn,
instance->cnt);
instance->hop,
instance->fix,
instance->hop,
instance->fix,
instance->endbyte & 0x7F);
}

View File

@@ -12,8 +12,6 @@
#include "../defines.h"
#define FIAT_PROTOCOL_V0_NAME "Fiat V0"
typedef struct SubGhzProtocolDecoderFiatV0 SubGhzProtocolDecoderFiatV0;
typedef struct SubGhzProtocolEncoderFiatV0 SubGhzProtocolEncoderFiatV0;

View File

@@ -1,107 +0,0 @@
#include "keeloq_common.h"
#include <furi.h>
#include <m-array.h>
#define bit(x, n) (((x) >> (n)) & 1)
#define g5(x, a, b, c, d, e) \
(bit(x, a) + bit(x, b) * 2 + bit(x, c) * 4 + bit(x, d) * 8 + bit(x, e) * 16)
/** Simple Learning Encrypt
* @param data - 0xBSSSCCCC, B(4bit) key, S(10bit) serial&0x3FF, C(16bit) counter
* @param key - manufacture (64bit)
* @return keeloq encrypt data
*/
inline uint32_t subghz_protocol_keeloq_common_encrypt(const uint32_t data, const uint64_t key) {
uint32_t x = data, r;
for(r = 0; r < 528; r++)
x = (x >> 1) ^ ((bit(x, 0) ^ bit(x, 16) ^ (uint32_t)bit(key, r & 63) ^
bit(KEELOQ_NLF, g5(x, 1, 9, 20, 26, 31)))
<< 31);
return x;
}
/** Simple Learning Decrypt
* @param data - keeloq encrypt data
* @param key - manufacture (64bit)
* @return 0xBSSSCCCC, B(4bit) key, S(10bit) serial&0x3FF, C(16bit) counter
*/
inline uint32_t subghz_protocol_keeloq_common_decrypt(const uint32_t data, const uint64_t key) {
uint32_t x = data, r;
for(r = 0; r < 528; r++)
x = (x << 1) ^ bit(x, 31) ^ bit(x, 15) ^ (uint32_t)bit(key, (15 - r) & 63) ^
bit(KEELOQ_NLF, g5(x, 0, 8, 19, 25, 30));
return x;
}
/** Normal Learning
* @param data - serial number (28bit)
* @param key - manufacture (64bit)
* @return manufacture for this serial number (64bit)
*/
inline uint64_t subghz_protocol_keeloq_common_normal_learning(uint32_t data, const uint64_t key) {
uint32_t k1, k2;
data &= 0x0FFFFFFF;
data |= 0x20000000;
k1 = subghz_protocol_keeloq_common_decrypt(data, key);
data &= 0x0FFFFFFF;
data |= 0x60000000;
k2 = subghz_protocol_keeloq_common_decrypt(data, key);
return ((uint64_t)k2 << 32) | k1; // key - shifrovanoya
}
/** Magic_xor_type1 Learning
* @param data - serial number (28bit)
* @param xor - magic xor (64bit)
* @return manufacture for this serial number (64bit)
*/
inline uint64_t
subghz_protocol_keeloq_common_magic_xor_type1_learning(uint32_t data, uint64_t xor) {
data &= 0x0FFFFFFF;
return (((uint64_t)data << 32) | data) ^ xor;
}
/** Magic_serial_type1 Learning
* @param data - serial number (28bit)
* @param man - magic man (64bit)
* @return manufacture for this serial number (64bit)
*/
inline uint64_t
subghz_protocol_keeloq_common_magic_serial_type1_learning(uint32_t data, uint64_t man) {
return (man & 0xFFFFFFFF) | ((uint64_t)data << 40) |
((uint64_t)(((data & 0xff) + ((data >> 8) & 0xFF)) & 0xFF) << 32);
}
/** Magic_serial_type2 Learning
* @param data - btn+serial number (32bit)
* @param man - magic man (64bit)
* @return manufacture for this serial number (64bit)
*/
inline uint64_t
subghz_protocol_keeloq_common_magic_serial_type2_learning(uint32_t data, uint64_t man) {
uint8_t* p = (uint8_t*)&data;
uint8_t* m = (uint8_t*)&man;
m[7] = p[0];
m[6] = p[1];
m[5] = p[2];
m[4] = p[3];
return man;
}
/** Magic_serial_type3 Learning
* @param data - serial number (24bit)
* @param man - magic man (64bit)
* @return manufacture for this serial number (64bit)
*/
inline uint64_t
subghz_protocol_keeloq_common_magic_serial_type3_learning(uint32_t data, uint64_t man) {
return (man & 0xFFFFFFFFFF000000) | (data & 0xFFFFFF);
}

View File

@@ -13,6 +13,13 @@
#include <m-array.h>
// Extracts bit number n from integer x
#define bit(x, n) (((x) >> (n)) & 1)
// Builds a 5-bit number from five selected bit positions in x
#define g5(x, a, b, c, d, e) \
(bit(x, a) + bit(x, b) * 2 + bit(x, c) * 4 + bit(x, d) * 8 + bit(x, e) * 16)
struct SubGhzKeystore {
SubGhzKeyArray_t data;
const char* mfname;
@@ -47,7 +54,15 @@ struct SubGhzKeystore {
* @param key - manufacture (64bit)
* @return keeloq encrypt data
*/
uint32_t subghz_protocol_keeloq_common_encrypt(const uint32_t data, const uint64_t key);
static inline uint32_t
subghz_protocol_keeloq_common_encrypt(const uint32_t data, const uint64_t key) {
uint32_t x = data, r;
for(r = 0; r < 528; r++)
x = (x >> 1) ^ ((bit(x, 0) ^ bit(x, 16) ^ (uint32_t)bit(key, r & 63) ^
bit(KEELOQ_NLF, g5(x, 1, 9, 20, 26, 31)))
<< 31);
return x;
}
/**
* Simple Learning Decrypt
@@ -55,7 +70,14 @@ uint32_t subghz_protocol_keeloq_common_encrypt(const uint32_t data, const uint64
* @param key - manufacture (64bit)
* @return 0xBSSSCCCC, B(4bit) key, S(10bit) serial&0x3FF, C(16bit) counter
*/
uint32_t subghz_protocol_keeloq_common_decrypt(const uint32_t data, const uint64_t key);
static inline uint32_t
subghz_protocol_keeloq_common_decrypt(const uint32_t data, const uint64_t key) {
uint32_t x = data, r;
for(r = 0; r < 528; r++)
x = (x << 1) ^ bit(x, 31) ^ bit(x, 15) ^ (uint32_t)bit(key, (15 - r) & 63) ^
bit(KEELOQ_NLF, g5(x, 0, 8, 19, 25, 30));
return x;
}
/**
* Normal Learning
@@ -63,7 +85,20 @@ uint32_t subghz_protocol_keeloq_common_decrypt(const uint32_t data, const uint64
* @param key - manufacture (64bit)
* @return manufacture for this serial number (64bit)
*/
uint64_t subghz_protocol_keeloq_common_normal_learning(uint32_t data, const uint64_t key);
static inline uint64_t
subghz_protocol_keeloq_common_normal_learning(uint32_t data, const uint64_t key) {
uint32_t k1, k2;
data &= 0x0FFFFFFF;
data |= 0x20000000;
k1 = subghz_protocol_keeloq_common_decrypt(data, key);
data &= 0x0FFFFFFF;
data |= 0x60000000;
k2 = subghz_protocol_keeloq_common_decrypt(data, key);
return ((uint64_t)k2 << 32) | k1; // key - shifrovanoya
}
/**
* Magic_xor_type1 Learning
@@ -71,28 +106,45 @@ uint64_t subghz_protocol_keeloq_common_normal_learning(uint32_t data, const uint
* @param xor - magic xor (64bit)
* @return manufacture for this serial number (64bit)
*/
uint64_t subghz_protocol_keeloq_common_magic_xor_type1_learning(uint32_t data, uint64_t xor);
static inline uint64_t
subghz_protocol_keeloq_common_magic_xor_type1_learning(uint32_t data, uint64_t xor) {
data &= 0x0FFFFFFF;
return (((uint64_t)data << 32) | data) ^ xor;
}
/** Magic_serial_type1 Learning
* @param data - serial number (28bit)
* @param man - magic man (64bit)
* @return manufacture for this serial number (64bit)
*/
uint64_t subghz_protocol_keeloq_common_magic_serial_type1_learning(uint32_t data, uint64_t man);
static inline uint64_t
subghz_protocol_keeloq_common_magic_serial_type1_learning(uint32_t data, uint64_t man) {
return (man & 0xFFFFFFFF) | ((uint64_t)data << 40) |
((uint64_t)(((data & 0xff) + ((data >> 8) & 0xFF)) & 0xFF) << 32);
}
/** Magic_serial_type2 Learning
* @param data - btn+serial number (32bit)
* @param man - magic man (64bit)
* @return manufacture for this serial number (64bit)
*/
uint64_t subghz_protocol_keeloq_common_magic_serial_type2_learning(uint32_t data, uint64_t man);
static inline uint64_t
subghz_protocol_keeloq_common_magic_serial_type2_learning(uint32_t data, uint64_t man) {
uint8_t* p = (uint8_t*)&data;
uint8_t* m = (uint8_t*)&man;
m[7] = p[0];
m[6] = p[1];
m[5] = p[2];
m[4] = p[3];
return man;
}
/** Magic_serial_type3 Learning
* @param data - btn+serial number (32bit)
* @param man - magic man (64bit)
* @return manufacture for this serial number (64bit)
*/
uint64_t subghz_protocol_keeloq_common_magic_serial_type3_learning(uint32_t data, uint64_t man);
static inline uint64_t
subghz_protocol_keeloq_common_magic_serial_type3_learning(uint32_t data, uint64_t man) {
return (man & 0xFFFFFFFFFF000000) | (data & 0xFFFFFF);
}

View File

@@ -1,6 +1,9 @@
#include "protocols_common.h"
const char* protopirate_get_short_preset_name(const char* preset_name) {
if(!preset_name) {
return "UNKNOWN";
}
if(!strcmp(preset_name, "FuriHalSubGhzPresetOok270Async")) {
return "AM270";
} else if(!strcmp(preset_name, "FuriHalSubGhzPresetOok650Async")) {

View File

@@ -5,6 +5,37 @@
#define TAG "VAGProtocol"
#define VAG_ENCODER_UPLOAD_MAX_SIZE 680
// uncomment to enable
//#define VAG_CHECK_UPLOAD_OVERFLOW
/* Manchester emit helper: one bit -> two half-bits */
#define VAG_EMIT_MANCHESTER(u, idx, bit, te) \
do { \
if(bit) { \
(u)[(idx)++] = level_duration_make(true, (te)); \
(u)[(idx)++] = level_duration_make(false, (te)); \
} else { \
(u)[(idx)++] = level_duration_make(false, (te)); \
(u)[(idx)++] = level_duration_make(true, (te)); \
} \
} while(0)
#ifdef VAG_CHECK_UPLOAD_OVERFLOW
/* Guard against upload buffer overflow; set size_upload=0 on failure */
#define VAG_UPLOAD_NEED(inst, idx, need) \
do { \
if(((idx) + (need)) > VAG_ENCODER_UPLOAD_MAX_SIZE) { \
FURI_LOG_E( \
TAG, "Upload overflow (need=%u idx=%zu)", (unsigned)(need), (size_t)(idx)); \
(inst)->size_upload = 0; \
return; \
} \
} while(0)
#else
#define VAG_UPLOAD_NEED(inst, idx, need)
#endif
static const SubGhzBlockConst subghz_protocol_vag_const = {
.te_short = 500,
.te_long = 1000,
@@ -27,13 +58,13 @@ static const SubGhzBlockConst subghz_protocol_vag_const = {
#define VAG_T34_PREAMBLE_MIN 31u
#define VAG_T34_SYNC_PAIRS 3u
#define VAG_DATA_GAP_MIN 4001u
#define VAG_TOTAL_BITS 80u
#define VAG_KEY1_BITS 64u
#define VAG_PREFIX_BITS 15u
#define VAG_BIT_LIMIT 96u
#define VAG_FRAME_PREFIX_T1 0x2F3Fu
#define VAG_FRAME_PREFIX_T2 0x2F1Cu
#define VAG_DATA_GAP_MIN 4001u
#define VAG_TOTAL_BITS 80u
#define VAG_KEY1_BITS 64u
#define VAG_PREFIX_BITS 15u
#define VAG_BIT_LIMIT 96u
#define VAG_FRAME_PREFIX_T1 0x2F3Fu
#define VAG_FRAME_PREFIX_T2 0x2F1Cu
#define VAG_KEYS_COUNT 3
@@ -55,15 +86,26 @@ static void protocol_vag_load_keys(const char* file_name) {
protocol_vag_keys_loaded = 0;
for(uint8_t i = 0; i < VAG_KEYS_COUNT; i++) {
uint8_t key_packed[AUT64_KEY_STRUCT_PACKED_SIZE];
uint8_t key_packed[AUT64_PACKED_KEY_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_vag_keys[i], key_packed);
file_name, i * AUT64_PACKED_KEY_SIZE, key_packed, AUT64_PACKED_KEY_SIZE)) {
int rc = aut64_unpack(&protocol_vag_keys[i], key_packed);
#ifdef AUT64_ENABLE_VALIDATIONS
if(rc == AUT64_ERR_INVALID_PACKED) {
FURI_LOG_E(TAG, "Invalid key: %u", i);
} else if(rc == AUT64_ERR_NULL_POINTER) {
FURI_LOG_E(TAG, "Key is NULL: %d", i);
}
if(rc == AUT64_OK) {
protocol_vag_keys_loaded++;
} else {
break;
}
#else
(void)rc;
protocol_vag_keys_loaded++;
#endif
} else {
FURI_LOG_E(TAG, "Unable to load key %u", i);
break;
@@ -218,8 +260,16 @@ static bool vag_aut64_decrypt(uint8_t* block, int key_index) {
FURI_LOG_E(TAG, "Key not found: %d", key_index + 1);
return false;
}
aut64_decrypt(*key, block);
return true;
int rc = aut64_decrypt(key, block);
#ifdef AUT64_ENABLE_VALIDATIONS
if(rc == AUT64_ERR_INVALID_KEY) {
FURI_LOG_E(TAG, "Invalid key: %d", key_index + 1);
} else if(rc == AUT64_ERR_NULL_POINTER) {
FURI_LOG_E(TAG, "key is NULL: %d", key_index + 1);
}
#endif
return (rc == AUT64_OK) ? true : false;
}
static void vag_parse_data(SubGhzProtocolDecoderVAG* instance) {
@@ -506,14 +556,11 @@ const SubGhzProtocol vag_protocol = {
void* subghz_protocol_decoder_vag_alloc(SubGhzEnvironment* environment) {
UNUSED(environment);
SubGhzProtocolDecoderVAG* instance = malloc(sizeof(SubGhzProtocolDecoderVAG));
SubGhzProtocolDecoderVAG* instance = calloc(1, sizeof(SubGhzProtocolDecoderVAG));
furi_check(instance);
instance->base.protocol = &vag_protocol;
instance->generic.protocol_name = instance->base.protocol->name;
instance->decrypted = false;
instance->serial = 0;
instance->cnt = 0;
instance->btn = 0;
instance->check_byte = 0;
instance->key_idx = 0xFF;
protocol_vag_load_keys(APP_ASSETS_PATH("vag"));
@@ -532,6 +579,18 @@ void subghz_protocol_decoder_vag_reset(void* context) {
SubGhzProtocolDecoderVAG* instance = context;
instance->decoder.parser_step = VAGDecoderStepReset;
instance->decrypted = false;
/* Reset decoder state to avoid stale parsing after external resets */
instance->data_low = 0;
instance->data_high = 0;
instance->bit_count = 0;
instance->data_count_bit = 0;
instance->vag_type = 0;
instance->header_count = 0;
instance->mid_count = 0;
manchester_advance(
instance->manchester_state, ManchesterEventReset, &instance->manchester_state, NULL);
instance->serial = 0;
instance->cnt = 0;
instance->btn = 0;
@@ -616,8 +675,7 @@ void subghz_protocol_decoder_vag_feed(void* context, bool level, uint32_t durati
instance->data_high = 0;
instance->bit_count = 0;
instance->vag_type = 1;
} else if(
instance->data_low == VAG_FRAME_PREFIX_T2 && instance->data_high == 0) {
} else if(instance->data_low == VAG_FRAME_PREFIX_T2 && instance->data_high == 0) {
instance->data_low = 0;
instance->data_high = 0;
instance->bit_count = 0;
@@ -677,8 +735,7 @@ void subghz_protocol_decoder_vag_feed(void* context, bool level, uint32_t durati
break;
case VAGDecoderStepSync2A:
if(!level &&
DURATION_DIFF(duration, VAG_T34_TE_SHORT) < VAG_T34_TE_DELTA &&
if(!level && DURATION_DIFF(duration, VAG_T34_TE_SHORT) < VAG_T34_TE_DELTA &&
DURATION_DIFF(instance->decoder.te_last, VAG_T34_TE_LONG) < VAG_T34_LONG_DELTA) {
instance->decoder.te_last = duration;
instance->decoder.parser_step = VAGDecoderStepSync2B;
@@ -697,8 +754,7 @@ void subghz_protocol_decoder_vag_feed(void* context, bool level, uint32_t durati
break;
case VAGDecoderStepSync2C:
if(!level &&
DURATION_DIFF(duration, VAG_T34_SYNC) < VAG_T34_SYNC_DELTA &&
if(!level && DURATION_DIFF(duration, VAG_T34_SYNC) < VAG_T34_SYNC_DELTA &&
DURATION_DIFF(instance->decoder.te_last, VAG_T34_SYNC) < VAG_T34_SYNC_DELTA) {
instance->mid_count++;
instance->decoder.parser_step = VAGDecoderStepSync2B;
@@ -955,8 +1011,16 @@ static bool vag_aut64_encrypt(uint8_t* block, int key_index) {
FURI_LOG_E(TAG, "Key not found for encryption: %d", key_index + 1);
return false;
}
aut64_encrypt(*key, block);
return true;
int rc = aut64_encrypt(key, block);
#ifdef AUT64_ENABLE_VALIDATIONS
if(rc == AUT64_ERR_INVALID_KEY) {
FURI_LOG_E(TAG, "Invalid key: %d", key_index + 1);
} else if(rc == AUT64_ERR_NULL_POINTER) {
FURI_LOG_E(TAG, "key is NULL");
}
#endif
return (rc == AUT64_OK) ? true : false;
}
static uint8_t vag_get_dispatch_byte(uint8_t btn, uint8_t vag_type) {
@@ -1013,6 +1077,7 @@ static void vag_encoder_build_type1(SubGhzProtocolEncoderVAG* instance) {
size_t index = 0;
LevelDuration* upload = instance->upload;
VAG_UPLOAD_NEED(instance, index, 700);
uint8_t btn_byte = vag_btn_to_byte(instance->btn, 1);
uint8_t dispatch = vag_get_dispatch_byte(btn_byte, 1);
@@ -1094,13 +1159,7 @@ static void vag_encoder_build_type1(SubGhzProtocolEncoderVAG* instance) {
#endif
for(int i = 15; i >= 0; i--) {
bool bit = (prefix >> i) & 1;
if(bit) {
upload[index++] = level_duration_make(true, 300);
upload[index++] = level_duration_make(false, 300);
} else {
upload[index++] = level_duration_make(false, 300);
upload[index++] = level_duration_make(true, 300);
}
VAG_EMIT_MANCHESTER(upload, index, bit, 300);
}
FURI_LOG_D(TAG, "Prefix 0x%04X: %zu pulses", prefix, index - prefix_start);
@@ -1119,13 +1178,7 @@ static void vag_encoder_build_type1(SubGhzProtocolEncoderVAG* instance) {
#endif
for(int i = 63; i >= 0; i--) {
bool bit = (key1_inv >> i) & 1;
if(bit) {
upload[index++] = level_duration_make(true, 300);
upload[index++] = level_duration_make(false, 300);
} else {
upload[index++] = level_duration_make(false, 300);
upload[index++] = level_duration_make(true, 300);
}
VAG_EMIT_MANCHESTER(upload, index, bit, 300);
}
FURI_LOG_D(TAG, "Key1: %zu pulses (64 bits)", index - key1_start);
@@ -1137,13 +1190,7 @@ static void vag_encoder_build_type1(SubGhzProtocolEncoderVAG* instance) {
#endif
for(int i = 15; i >= 0; i--) {
bool bit = (key2_inv >> i) & 1;
if(bit) {
upload[index++] = level_duration_make(true, 300);
upload[index++] = level_duration_make(false, 300);
} else {
upload[index++] = level_duration_make(false, 300);
upload[index++] = level_duration_make(true, 300);
}
VAG_EMIT_MANCHESTER(upload, index, bit, 300);
}
FURI_LOG_D(TAG, "Key2: %zu pulses (16 bits)", index - key2_start);
@@ -1162,6 +1209,7 @@ static void vag_encoder_build_type2(SubGhzProtocolEncoderVAG* instance) {
size_t index = 0;
LevelDuration* upload = instance->upload;
VAG_UPLOAD_NEED(instance, index, 700);
uint8_t btn_byte = vag_btn_to_byte(instance->btn, 2);
uint8_t dispatch = vag_get_dispatch_byte(btn_byte, 2);
@@ -1254,13 +1302,7 @@ static void vag_encoder_build_type2(SubGhzProtocolEncoderVAG* instance) {
#endif
for(int i = 15; i >= 0; i--) {
bool bit = (prefix >> i) & 1;
if(bit) {
upload[index++] = level_duration_make(true, 300);
upload[index++] = level_duration_make(false, 300);
} else {
upload[index++] = level_duration_make(false, 300);
upload[index++] = level_duration_make(true, 300);
}
VAG_EMIT_MANCHESTER(upload, index, bit, 300);
}
FURI_LOG_D(TAG, "Prefix 0x%04X: %zu pulses", prefix, index - prefix_start);
@@ -1278,13 +1320,7 @@ static void vag_encoder_build_type2(SubGhzProtocolEncoderVAG* instance) {
#endif
for(int i = 63; i >= 0; i--) {
bool bit = (key1_inv >> i) & 1;
if(bit) {
upload[index++] = level_duration_make(true, 300);
upload[index++] = level_duration_make(false, 300);
} else {
upload[index++] = level_duration_make(false, 300);
upload[index++] = level_duration_make(true, 300);
}
VAG_EMIT_MANCHESTER(upload, index, bit, 300);
}
FURI_LOG_D(TAG, "Key1: %zu pulses", index - key1_start);
@@ -1296,13 +1332,7 @@ static void vag_encoder_build_type2(SubGhzProtocolEncoderVAG* instance) {
#endif
for(int i = 15; i >= 0; i--) {
bool bit = (key2_inv >> i) & 1;
if(bit) {
upload[index++] = level_duration_make(true, 300);
upload[index++] = level_duration_make(false, 300);
} else {
upload[index++] = level_duration_make(false, 300);
upload[index++] = level_duration_make(true, 300);
}
VAG_EMIT_MANCHESTER(upload, index, bit, 300);
}
FURI_LOG_D(TAG, "Key2: %zu pulses", index - key2_start);
@@ -1317,6 +1347,7 @@ static void vag_encoder_build_type3_4(SubGhzProtocolEncoderVAG* instance) {
size_t index = 0;
LevelDuration* upload = instance->upload;
VAG_UPLOAD_NEED(instance, index, 600);
uint8_t btn_byte = vag_btn_to_byte(instance->btn, instance->vag_type);
uint8_t dispatch = vag_get_dispatch_byte(btn_byte, instance->vag_type);
@@ -1480,15 +1511,8 @@ static void vag_encoder_build_type3_4(SubGhzProtocolEncoderVAG* instance) {
bool last_level = false;
for(int i = 15; i >= 0; i--) {
bool bit = (key2 >> i) & 1;
if(bit) {
upload[index++] = level_duration_make(true, 500);
upload[index++] = level_duration_make(false, 500);
last_level = false;
} else {
upload[index++] = level_duration_make(false, 500);
upload[index++] = level_duration_make(true, 500);
last_level = true;
}
VAG_EMIT_MANCHESTER(upload, index, bit, 500);
last_level = !bit;
}
FURI_LOG_D(
TAG,
@@ -1581,32 +1605,20 @@ void subghz_protocol_decoder_vag_get_string(void* context, FuriString* output) {
}
}
#define VAG_ENCODER_UPLOAD_MAX_SIZE 680
#ifdef ENABLE_EMULATE_FEATURE
void* subghz_protocol_encoder_vag_alloc(SubGhzEnvironment* environment) {
UNUSED(environment);
FURI_LOG_I(TAG, "VAG encoder alloc");
SubGhzProtocolEncoderVAG* instance = malloc(sizeof(SubGhzProtocolEncoderVAG));
SubGhzProtocolEncoderVAG* instance = calloc(1, sizeof(SubGhzProtocolEncoderVAG));
furi_check(instance);
instance->base.protocol = &vag_protocol;
instance->generic.protocol_name = instance->base.protocol->name;
instance->upload = malloc(VAG_ENCODER_UPLOAD_MAX_SIZE * sizeof(LevelDuration));
instance->size_upload = 0;
instance->upload = calloc(VAG_ENCODER_UPLOAD_MAX_SIZE, sizeof(LevelDuration));
furi_check(instance->upload);
instance->repeat = 10;
instance->front = 0;
instance->is_running = false;
instance->key1_low = 0;
instance->key1_high = 0;
instance->key2_low = 0;
instance->key2_high = 0;
instance->serial = 0;
instance->cnt = 0;
instance->vag_type = 0;
instance->btn = 0;
instance->dispatch_byte = 0;
instance->key_idx = 0xFF;
protocol_vag_load_keys(APP_ASSETS_PATH("vag"));

View File

@@ -129,7 +129,7 @@ static void protopirate_scene_receiver_callback(
furi_string_replace_all(protocol, " ", "_");
FuriString* saved_path = furi_string_alloc();
if(!protocol) {
if(!saved_path) {
FURI_LOG_E(TAG, "saved_path allocation failed");
furi_string_free(protocol);
furi_string_free(str_buff);