diff --git a/protocols/aut64.c b/protocols/aut64.c index a946d67..66c80c5 100644 --- a/protocols/aut64.c +++ b/protocols/aut64.c @@ -1,5 +1,5 @@ -#include #include "aut64.h" +#include // 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; } diff --git a/protocols/aut64.h b/protocols/aut64.h index b78db65..fd9572e 100644 --- a/protocols/aut64.h +++ b/protocols/aut64.h @@ -1,12 +1,29 @@ #pragma once #include +#include -#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); diff --git a/protocols/fiat_v0.c b/protocols/fiat_v0.c index 1969001..9b51074 100644 --- a/protocols/fiat_v0.c +++ b/protocols/fiat_v0.c @@ -1,8 +1,14 @@ #include "fiat_v0.h" #include "../protopirate_app_i.h" #include +#include -#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); } diff --git a/protocols/fiat_v0.h b/protocols/fiat_v0.h index 0f4db08..bce58af 100644 --- a/protocols/fiat_v0.h +++ b/protocols/fiat_v0.h @@ -12,8 +12,6 @@ #include "../defines.h" -#define FIAT_PROTOCOL_V0_NAME "Fiat V0" - typedef struct SubGhzProtocolDecoderFiatV0 SubGhzProtocolDecoderFiatV0; typedef struct SubGhzProtocolEncoderFiatV0 SubGhzProtocolEncoderFiatV0; diff --git a/protocols/keeloq_common.c b/protocols/keeloq_common.c deleted file mode 100644 index 0865a1f..0000000 --- a/protocols/keeloq_common.c +++ /dev/null @@ -1,107 +0,0 @@ -#include "keeloq_common.h" - -#include - -#include - -#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); -} diff --git a/protocols/keeloq_common.h b/protocols/keeloq_common.h index c1570eeb..63e1bfc 100644 --- a/protocols/keeloq_common.h +++ b/protocols/keeloq_common.h @@ -13,6 +13,13 @@ #include +// 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); +} diff --git a/protocols/protocols_common.c b/protocols/protocols_common.c index 033d5cb..f7290a0 100644 --- a/protocols/protocols_common.c +++ b/protocols/protocols_common.c @@ -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")) { diff --git a/protocols/vag.c b/protocols/vag.c index efd6363..b267319 100644 --- a/protocols/vag.c +++ b/protocols/vag.c @@ -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")); diff --git a/scenes/protopirate_scene_receiver.c b/scenes/protopirate_scene_receiver.c index 0dc6fa4..07e8a9f 100644 --- a/scenes/protopirate_scene_receiver.c +++ b/scenes/protopirate_scene_receiver.c @@ -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);