proxmark3/client/deps/id48/id48_generator.c
iceman1001 94c3bdd91c style
2024-03-14 11:26:06 +01:00

899 lines
44 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* The MIT License (MIT)
*
* Copyright (c) 2024 by Henry Gabryjelski
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
#include "id48_internals.h"
typedef struct _INPUT_BITS2 {
// least significant 55 bits are valid/used; lsb == input₀₀
uint64_t Raw;
} INPUT_BITS2;
typedef struct _OUTPUT_BITS2 {
// least significant 55 bits valid
// Raw₅₄..Raw₄₈ == ignored bits to get to s₀₇
// Raw₄₇..Raw₂₀ == 28-bit challenge value frn
// Raw₁₉..Raw₀₀ == 20-bit response value grn
uint64_t Raw;
} OUTPUT_BITS2;
typedef struct _OUTPUT_INDEX2 {
// Opaque value for use in lookup of the output bit
// only least significant 20 bits are valid
uint32_t Raw;
} OUTPUT_INDEX2;
#if !defined(nullptr)
#define nullptr ((void*)0)
#endif
#pragma region // reverse_bits()
static inline uint8_t reverse_bits_08(uint8_t n) {
uint8_t bitsToSwap = sizeof(n) * 8;
uint8_t mask = (uint8_t)(~((uint8_t)(0u))); // equivalent to uint32_t mask = 0b11111111111111111111111111111111;
while (bitsToSwap >>= 1) {
mask ^= mask << (bitsToSwap); // will convert mask to 0b00000000000000001111111111111111;
n = (uint8_t)(((n & ~mask) >> bitsToSwap) | ((n & mask) << bitsToSwap)); // divide and conquer
}
return n;
}
static inline uint16_t reverse_bits_16(uint16_t n) {
uint8_t bitsToSwap = sizeof(n) * 8;
uint16_t mask = (uint16_t)(~((uint16_t)(0u))); // equivalent to uint32_t mask = 0b11111111111111111111111111111111;
while (bitsToSwap >>= 1) {
mask ^= mask << (bitsToSwap); // will convert mask to 0b00000000000000001111111111111111;
n = (uint16_t)(((n & ~mask) >> bitsToSwap) | ((n & mask) << bitsToSwap)); // divide and conquer
}
return n;
}
static inline uint32_t reverse_bits_32(uint32_t n) {
uint8_t bitsToSwap = sizeof(n) * 8;
uint32_t mask = (uint32_t)(~((uint32_t)(0u))); // equivalent to uint32_t mask = 0b11111111111111111111111111111111;
while (bitsToSwap >>= 1) {
mask ^= mask << (bitsToSwap); // will convert mask to 0b00000000000000001111111111111111;
n = (uint32_t)(((n & ~mask) >> bitsToSwap) | ((n & mask) << bitsToSwap)); // divide and conquer
}
return n;
}
static inline uint64_t reverse_bits_64(uint64_t n) {
uint8_t bitsToSwap = sizeof(n) * 8;
uint64_t mask = (uint64_t)(~((uint64_t)(0u))); // equivalent to uint32_t mask = 0b11111111111111111111111111111111;
while (bitsToSwap >>= 1) {
mask ^= mask << (bitsToSwap); // will convert mask to 0b00000000000000001111111111111111;
n = (uint64_t)(((n & ~mask) >> bitsToSwap) | ((n & mask) << bitsToSwap)); // divide and conquer
}
return n;
}
#pragma endregion // reverse_bits()
#pragma region // id48lib state register
// Bit: ₆₃ ₆₂ ₆₁ ₆₀ ₅₉ ₅₈ ₅₇ ₅₆ ₅₅ ₅₄ ₅₃ ₅₂ ₅₁ ₅₀ ₄₉ ₄₈ ₄₇ ₄₆ ₄₅ ₄₄ ₄₃ ₄₂ ₄₁ ₄₀ ₃₉ ₃₈ ₃₇ ₃₆ ₃₅ ₃₄ ₃₃ ₃₂
// Reg: x x x r₀₆ r₀₅ r₀₄ r₀₃ r₀₂ r₀₁ r₀₀ m₀₆ m₀₅ m₀₄ m₀₃ m₀₂ m₀₁ m₀₀ l₀₆ l₀₅ l₀₄ l₀₃ l₀₂ l₀₁ l₀₀ g₂₂ g₂₁ g₂₀ g₁₉ g₁₈ g₁₇ g₁₆ g₁₅
//
// Bit: ₃₁ ₃₀ ₂₉ ₂₈ ₂₇ ₂₆ ₂₅ ₂₄ ₂₃ ₂₂ ₂₁ ₂₀ ₁₉ ₁₈ ₁₇ ₁₆ ₁₅ ₁₄ ₁₃ ₁₂ ₁₁ ₁₀ ₀₉ ₀₈ ₀₇ ₀₆ ₀₅ ₀₄ ₀₃ ₀₂ ₀₁ ₀₀
// Reg: g₁₄ g₁₃ g₁₂ g₁₁ g₁₀ g₀₉ g₀₈ g₀₇ g₀₆ g₀₅ g₀₄ g₀₃ g₀₂ g₀₁ g₀₀ h₁₂ h₁₁ h₁₀ h₀₉ h₀₈ h₀₇ h₀₆ h₀₅ h₀₄ h₀₃ h₀₂ h₀₁ h₀₀ x x x 1
#pragma endregion // id48lib state register
#pragma region // bit definitions for the (stable) id48lib state register
// 63
// #define SSR_BIT_i 62 -- could do this ... one fewer parameter
// 61
#define SSR_BIT_R06 60
#define SSR_BIT_R05 59
#define SSR_BIT_R04 58
#define SSR_BIT_R03 57
#define SSR_BIT_R02 56
#define SSR_BIT_R01 55
#define SSR_BIT_R00 54
#define SSR_BIT_M06 53
#define SSR_BIT_M05 52
#define SSR_BIT_M04 51
#define SSR_BIT_M03 50
#define SSR_BIT_M02 49
#define SSR_BIT_M01 48
#define SSR_BIT_M00 47
#define SSR_BIT_L06 46
#define SSR_BIT_L05 45
#define SSR_BIT_L04 44
#define SSR_BIT_L03 43
#define SSR_BIT_L02 42
#define SSR_BIT_L01 41
#define SSR_BIT_L00 40
#define SSR_BIT_G22 39
#define SSR_BIT_G21 38
#define SSR_BIT_G20 37
#define SSR_BIT_G19 36
#define SSR_BIT_G18 35
#define SSR_BIT_G17 34
#define SSR_BIT_G16 33
#define SSR_BIT_G15 32
#define SSR_BIT_G14 31
#define SSR_BIT_G13 30
#define SSR_BIT_G12 29
#define SSR_BIT_G11 28
#define SSR_BIT_G10 27
#define SSR_BIT_G09 26
#define SSR_BIT_G08 25
#define SSR_BIT_G07 24
#define SSR_BIT_G06 23
#define SSR_BIT_G05 22
#define SSR_BIT_G04 21
#define SSR_BIT_G03 20
#define SSR_BIT_G02 19
#define SSR_BIT_G01 18
#define SSR_BIT_G00 17
#define SSR_BIT_H12 16
#define SSR_BIT_H11 15
#define SSR_BIT_H10 14
#define SSR_BIT_H09 13
#define SSR_BIT_H08 12
#define SSR_BIT_H07 11
#define SSR_BIT_H06 10
#define SSR_BIT_H05 9
#define SSR_BIT_H04 8
#define SSR_BIT_H03 7
#define SSR_BIT_H02 6
#define SSR_BIT_H01 5
#define SSR_BIT_H00 4
// 3 // used only when unstable (during calculations)
// 2 // used only when unstable (during calculations)
// 1 // used only when unstable (during calculations)
// 0 // 1 == stable, 0 == unstable (during calculations)
#pragma endregion // bit definitions for the (stable) id48lib state register
#pragma region // Unstable (during calculations) id48lib state register
// Bit: ₆₃ ₆₂ ₆₁ ₆₀ ₅₉ ₅₈ ₅₇ ₅₆ ₅₅ ₅₄ ₅₃ ₅₂ ₅₁ ₅₀ ₄₉ ₄₈ ₄₇ ₄₆ ₄₅ ₄₄ ₄₃ ₄₂ ₄₁ ₄₀ ₃₉ ₃₈ ₃₇ ₃₆ ₃₅ ₃₄ ₃₃ ₃₂
// Reg: i j r₀₆ r₀₅ r₀₄ r₀₃ r₀₂ r₀₁ r₀₀ m₀₆ m₀₅ m₀₄ m₀₃ m₀₂ m₀₁ m₀₀ l₀₆ l₀₅ l₀₄ l₀₃ l₀₂ l₀₁ l₀₀ g₂₂ g₂₁ g₂₀ g₁₉ g₁₈ g₁₇ g₁₆ g₁₅ g₁₄
//
// Bit: ₃₁ ₃₀ ₂₉ ₂₈ ₂₇ ₂₆ ₂₅ ₂₄ ₂₃ ₂₂ ₂₁ ₂₀ ₁₉ ₁₈ ₁₇ ₁₆ ₁₅ ₁₄ ₁₃ ₁₂ ₁₁ ₁₀ ₀₉ ₀₈ ₀₇ ₀₆ ₀₅ ₀₄ ₀₃ ₀₂ ₀₁ ₀₀
// Reg: g₁₃ g₁₂ g₁₁ g₁₀ g₀₉ g₀₈ g₀₇ g₀₆ g₀₅ g₀₄ g₀₃ g₀₂ g₀₁ g₀₀ h₁₂ h₁₁ h₁₀ h₀₉ h₀₈ h₀₇ h₀₆ h₀₅ h₀₄ h₀₃ h₀₂ h₀₁ h₀₀ _ a b c 0
#pragma endregion // Unstable (during calculations) id48lib state register
//
// Summary of XOR baseline that can be excluded because they are part of a single 64-bit `<< 1` operation:
// g₀₀ <-- h₁₂
// l₀₀ <-- g₂₂
// m₀₀ <-- l₀₆
// r₀₀ <-- m₀₆
//
#pragma region // bit definitions for the (unstable) id48lib state register
#define SSR_UNSTABLE_BIT_i 63
#define SSR_UNSTABLE_BIT_j 62
#define SSR_UNSTABLE_OLD_BIT_R06 61 // valid only during calculations aka R07 ... just has to have a name... doesn't matter what
#define SSR_UNSTABLE_OLD_BIT_R05 60
#define SSR_UNSTABLE_OLD_BIT_R04 59
#define SSR_UNSTABLE_OLD_BIT_R03 58
#define SSR_UNSTABLE_OLD_BIT_R02 57
#define SSR_UNSTABLE_OLD_BIT_R01 56
#define SSR_UNSTABLE_OLD_BIT_R00 55
#define SSR_UNSTABLE_OLD_BIT_M06 54
#define SSR_UNSTABLE_OLD_BIT_M05 53
#define SSR_UNSTABLE_OLD_BIT_M04 52
#define SSR_UNSTABLE_OLD_BIT_M03 51
#define SSR_UNSTABLE_OLD_BIT_M02 50
#define SSR_UNSTABLE_OLD_BIT_M01 49
#define SSR_UNSTABLE_OLD_BIT_M00 48
#define SSR_UNSTABLE_OLD_BIT_L06 47
#define SSR_UNSTABLE_OLD_BIT_L05 46
#define SSR_UNSTABLE_OLD_BIT_L04 45
#define SSR_UNSTABLE_OLD_BIT_L03 44
#define SSR_UNSTABLE_OLD_BIT_L02 43
#define SSR_UNSTABLE_OLD_BIT_L01 42
#define SSR_UNSTABLE_OLD_BIT_L00 41
#define SSR_UNSTABLE_OLD_BIT_G22 40
#define SSR_UNSTABLE_OLD_BIT_G21 39
#define SSR_UNSTABLE_OLD_BIT_G20 38
#define SSR_UNSTABLE_OLD_BIT_G19 37
#define SSR_UNSTABLE_OLD_BIT_G18 36
#define SSR_UNSTABLE_OLD_BIT_G17 35
#define SSR_UNSTABLE_OLD_BIT_G16 34
#define SSR_UNSTABLE_OLD_BIT_G15 33
#define SSR_UNSTABLE_OLD_BIT_G14 32
#define SSR_UNSTABLE_OLD_BIT_G13 31
#define SSR_UNSTABLE_OLD_BIT_G12 30
#define SSR_UNSTABLE_OLD_BIT_G11 29
#define SSR_UNSTABLE_OLD_BIT_G10 28
#define SSR_UNSTABLE_OLD_BIT_G09 27
#define SSR_UNSTABLE_OLD_BIT_G08 26
#define SSR_UNSTABLE_OLD_BIT_G07 25
#define SSR_UNSTABLE_OLD_BIT_G06 24
#define SSR_UNSTABLE_OLD_BIT_G05 23
#define SSR_UNSTABLE_OLD_BIT_G04 22
#define SSR_UNSTABLE_OLD_BIT_G03 21
#define SSR_UNSTABLE_OLD_BIT_G02 20
#define SSR_UNSTABLE_OLD_BIT_G01 19
#define SSR_UNSTABLE_OLD_BIT_G00 18
#define SSR_UNSTABLE_OLD_BIT_H12 17
#define SSR_UNSTABLE_OLD_BIT_H11 16
#define SSR_UNSTABLE_OLD_BIT_H10 15
#define SSR_UNSTABLE_OLD_BIT_H09 14
#define SSR_UNSTABLE_OLD_BIT_H08 13
#define SSR_UNSTABLE_OLD_BIT_H07 12
#define SSR_UNSTABLE_OLD_BIT_H06 11
#define SSR_UNSTABLE_OLD_BIT_H05 10
#define SSR_UNSTABLE_OLD_BIT_H04 9
#define SSR_UNSTABLE_OLD_BIT_H03 8
#define SSR_UNSTABLE_OLD_BIT_H02 7
#define SSR_UNSTABLE_OLD_BIT_H01 6
#define SSR_UNSTABLE_OLD_BIT_H00 5
#define SSR_UNSTABLE_NEW_BIT_H00 4 // ... new value of H00 goes here ...
#define SSR_UNSTABLE_BIT_a 3 // valid only during calculations (ssr & 0b1 == 0b0), else ???
#define SSR_UNSTABLE_BIT_b 2 // valid only during calculations (ssr & 0b1 == 0b0), else ???
#define SSR_UNSTABLE_BIT_c 1 // valid only during calculations (ssr & 0b1 == 0b0), else ???
// 0 // == 0 value defines as unstable state
#pragma endregion // bit definitions for the (stable) id48lib state register
#pragma region // single bit test/set/clear/flip/assign
static inline bool is_ssr_state_stable(const ID48LIBX_STATE_REGISTERS *ssr) { ASSERT(ssr != nullptr); return ((ssr->Raw & 1u) == 1u); }
static inline bool test_single_ssr_bit(const ID48LIBX_STATE_REGISTERS *ssr, size_t bit_index) { ASSERT(ssr != nullptr); ASSERT(bit_index < (sizeof(uint64_t) * 8)); return ((ssr->Raw) >> bit_index) & 1; }
static inline void set_single_ssr_bit(ID48LIBX_STATE_REGISTERS *ssr, size_t bit_index) { ASSERT(ssr != nullptr); ASSERT(bit_index < (sizeof(uint64_t) * 8)); ssr->Raw |= ((uint64_t)(1ull << bit_index)); }
static inline void clear_single_ssr_bit(ID48LIBX_STATE_REGISTERS *ssr, size_t bit_index) { ASSERT(ssr != nullptr); ASSERT(bit_index < (sizeof(uint64_t) * 8)); ssr->Raw &= ~((uint64_t)(1ull << bit_index)); }
static inline void flip_single_ssr_bit(ID48LIBX_STATE_REGISTERS *ssr, size_t bit_index) { ASSERT(ssr != nullptr); ASSERT(bit_index < (sizeof(uint64_t) * 8)); ssr->Raw ^= ((uint64_t)(1ull << bit_index)); }
static inline void assign_single_ssr_bit(ID48LIBX_STATE_REGISTERS *ssr, size_t bit_index, bool value) {
ASSERT(ssr != nullptr);
ASSERT(bit_index < (sizeof(uint64_t) * 8));
if (value) {
set_single_ssr_bit(ssr, bit_index);
} else {
clear_single_ssr_bit(ssr, bit_index);
}
}
#pragma endregion // single bit test/set/clear/flip/assign
#pragma region // test/assign of temporaries a/b/c/i/j
static inline void test_temporary_a(ID48LIBX_STATE_REGISTERS *ssr) { ASSERT(!is_ssr_state_stable(ssr)); test_single_ssr_bit(ssr, SSR_UNSTABLE_BIT_a); }
static inline void test_temporary_b(ID48LIBX_STATE_REGISTERS *ssr) { ASSERT(!is_ssr_state_stable(ssr)); test_single_ssr_bit(ssr, SSR_UNSTABLE_BIT_b); }
static inline void test_temporary_c(ID48LIBX_STATE_REGISTERS *ssr) { ASSERT(!is_ssr_state_stable(ssr)); test_single_ssr_bit(ssr, SSR_UNSTABLE_BIT_c); }
static inline void test_temporary_i(ID48LIBX_STATE_REGISTERS *ssr) { ASSERT(!is_ssr_state_stable(ssr)); test_single_ssr_bit(ssr, SSR_UNSTABLE_BIT_i); }
static inline void test_temporary_j(ID48LIBX_STATE_REGISTERS *ssr) { ASSERT(!is_ssr_state_stable(ssr)); test_single_ssr_bit(ssr, SSR_UNSTABLE_BIT_j); }
static inline void assign_temporary_a(ID48LIBX_STATE_REGISTERS *ssr, bool v) { ASSERT(!is_ssr_state_stable(ssr)); assign_single_ssr_bit(ssr, SSR_UNSTABLE_BIT_a, v); }
static inline void assign_temporary_b(ID48LIBX_STATE_REGISTERS *ssr, bool v) { ASSERT(!is_ssr_state_stable(ssr)); assign_single_ssr_bit(ssr, SSR_UNSTABLE_BIT_b, v); }
static inline void assign_temporary_c(ID48LIBX_STATE_REGISTERS *ssr, bool v) { ASSERT(!is_ssr_state_stable(ssr)); assign_single_ssr_bit(ssr, SSR_UNSTABLE_BIT_c, v); }
static inline void assign_temporary_i(ID48LIBX_STATE_REGISTERS *ssr, bool v) { ASSERT(!is_ssr_state_stable(ssr)); assign_single_ssr_bit(ssr, SSR_UNSTABLE_BIT_i, v); }
static inline void assign_temporary_j(ID48LIBX_STATE_REGISTERS *ssr, bool v) { ASSERT(!is_ssr_state_stable(ssr)); assign_single_ssr_bit(ssr, SSR_UNSTABLE_BIT_j, v); }
#pragma endregion // test/assign of temporaries a/b/c/i/j
#pragma region // Mask & Macro to get registers (in minimal bit form)
// ------------------------> 60 56 52 48 44 40 36 32 28 24 20 16 12 8 4 0
// | | | | | | | | | | | | | | | |
#define SSR_BITMASK_REG_H (0x000000000001FFF0ull) // (0b0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0001'1111'1111'1111'0000ull)
#define SSR_BITMASK_REG_G (0x000000FFFFFE0000ull) // (0b0000'0000'0000'0000'0000'0000'1111'1111'1111'1111'1111'1110'0000'0000'0000'0000ull)
#define SSR_BITMASK_REG_L (0x00007F0000000000ull) // (0b0000'0000'0000'0000'0111'1111'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000ull)
#define SSR_BITMASK_REG_M (0x003F100000000000ull) // (0b0000'0000'0011'1111'1000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000ull)
#define SSR_BITMASK_REG_R (0x1FC0000000000000ull) // (0b0001'1111'1100'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000'0000ull)
#define SSR_BITMASK_REG_ALL (0x1FFFFFFFFFFFFFF0ull) // (0b0001'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'0000ull)
// | | | | | | | | | | | | | | | |
// ------------------------> 60 56 52 48 44 40 36 32 28 24 20 16 12 8 4 0
#define SSR_BITMASK_WITHOUT_REG_H (~(SSR_BITMASK_REG_H))
#define SSR_BITMASK_WITHOUT_REG_G (~(SSR_BITMASK_REG_G))
#define SSR_BITMASK_WITHOUT_REG_L (~(SSR_BITMASK_REG_L))
#define SSR_BITMASK_WITHOUT_REG_M (~(SSR_BITMASK_REG_M))
#define SSR_BITMASK_WITHOUT_REG_R (~(SSR_BITMASK_REG_R))
#define SSR_BITMASK_WITHOUT_ANY_REGS (~(SSR_BITMASK_REG_ALL))
#define SSR_SHIFT_COUNT_REG_H ( 4)
#define SSR_SHIFT_COUNT_REG_G (17)
#define SSR_SHIFT_COUNT_REG_L (40)
#define SSR_SHIFT_COUNT_REG_M (47)
#define SSR_SHIFT_COUNT_REG_R (54)
#define SSR_VALUE_MASK_REG_H (0x001FFFu) // 13 bits
#define SSR_VALUE_MASK_REG_G (0x7FFFFFu) // 23 bits
#define SSR_VALUE_MASK_REG_L (0x00007Fu) // 7 bits
#define SSR_VALUE_MASK_REG_M (0x00007Fu) // 7 bits
#define SSR_VALUE_MASK_REG_R (0x00007Fu) // 7 bits
static inline uint16_t get_register_h(const ID48LIBX_STATE_REGISTERS *ssr) { return ((uint16_t)(ssr->Raw >> SSR_SHIFT_COUNT_REG_H)) & (SSR_VALUE_MASK_REG_H); }
static inline uint32_t get_register_g(const ID48LIBX_STATE_REGISTERS *ssr) { return ((uint32_t)(ssr->Raw >> SSR_SHIFT_COUNT_REG_G)) & (SSR_VALUE_MASK_REG_G); }
static inline uint8_t get_register_l(const ID48LIBX_STATE_REGISTERS *ssr) { return ((uint8_t)(ssr->Raw >> SSR_SHIFT_COUNT_REG_L)) & (SSR_VALUE_MASK_REG_L); }
static inline uint8_t get_register_m(const ID48LIBX_STATE_REGISTERS *ssr) { return ((uint8_t)(ssr->Raw >> SSR_SHIFT_COUNT_REG_M)) & (SSR_VALUE_MASK_REG_M); }
static inline uint8_t get_register_r(const ID48LIBX_STATE_REGISTERS *ssr) { return ((uint8_t)(ssr->Raw >> SSR_SHIFT_COUNT_REG_R)) & (SSR_VALUE_MASK_REG_R); }
static inline void set_register_h(ID48LIBX_STATE_REGISTERS *ssr, uint16_t v) { ASSERT((v & SSR_VALUE_MASK_REG_H) == v); ssr->Raw = (ssr->Raw & SSR_BITMASK_WITHOUT_REG_H) | (((uint64_t)(v & SSR_VALUE_MASK_REG_H)) << SSR_SHIFT_COUNT_REG_H); }
static inline void set_register_g(ID48LIBX_STATE_REGISTERS *ssr, uint32_t v) { ASSERT((v & SSR_VALUE_MASK_REG_G) == v); ssr->Raw = (ssr->Raw & SSR_BITMASK_WITHOUT_REG_G) | (((uint64_t)(v & SSR_VALUE_MASK_REG_G)) << SSR_SHIFT_COUNT_REG_G); }
static inline void set_register_l(ID48LIBX_STATE_REGISTERS *ssr, uint8_t v) { ASSERT((v & SSR_VALUE_MASK_REG_L) == v); ssr->Raw = (ssr->Raw & SSR_BITMASK_WITHOUT_REG_L) | (((uint64_t)(v & SSR_VALUE_MASK_REG_L)) << SSR_SHIFT_COUNT_REG_L); }
static inline void set_register_m(ID48LIBX_STATE_REGISTERS *ssr, uint8_t v) { ASSERT((v & SSR_VALUE_MASK_REG_M) == v); ssr->Raw = (ssr->Raw & SSR_BITMASK_WITHOUT_REG_M) | (((uint64_t)(v & SSR_VALUE_MASK_REG_M)) << SSR_SHIFT_COUNT_REG_M); }
static inline void set_register_r(ID48LIBX_STATE_REGISTERS *ssr, uint8_t v) { ASSERT((v & SSR_VALUE_MASK_REG_R) == v); ssr->Raw = (ssr->Raw & SSR_BITMASK_WITHOUT_REG_R) | (((uint64_t)(v & SSR_VALUE_MASK_REG_R)) << SSR_SHIFT_COUNT_REG_R); }
#pragma endregion // Mask & Macro to get registers (in minimal bit form)
/// <summary>
/// Calculates and returns 56-bit value p₅₅..p₀₀
/// per Definition 3.11:
/// p = p₀₀..p₅₅ = ( K₄₀..K₉₅ ) + ( N₀₀..N₅₅ )
/// </summary>
/// <param name="k96">key in pm3 order</param>
/// <param name="n56">nonce in pm3 order</param>
/// <returns>56-bit value p₅₅..p₀₀</returns>
static inline uint64_t calculate__p55_p00(const ID48LIB_KEY *k96, const ID48LIB_NONCE *n56) {
// messy ... have to reverse the bits AND shift them into position,
// perform the addition, and then reverse bits again to return to
// native bit order (subscript is same as bit position).
//
// 1. for each byte, reverse bit order and shift into 64-bit tmp
// 2. add the two 56-bit tmp values
// 3. keeping only low 56-bit bits... reverse the bits
ASSERT(k96 != nullptr);
ASSERT(n56 != nullptr);
uint64_t k40_k95 = 0;
uint64_t n00_n55 = 0;
//
// k [ 6] :== K₄₇..K₄₀
// ...
// k [ 0] :== K₉₅..K₈₈
//
// rn[ 6] :== N₀₇..N₀₀
// ...
// rn[ 0] :== N₅₅..N₄₈
//
for (int8_t i = 6; i >= 0; --i) {
k40_k95 <<= 8;
n00_n55 <<= 8;
uint8_t t1 = reverse_bits_08(k96->k[i]);
k40_k95 |= t1;
uint8_t t2 = reverse_bits_08(n56->rn[i]);
n00_n55 |= t2;
}
uint64_t result = k40_k95 + n00_n55;
// shift so msb == p₀₀ (p₀₀..p₅₅0⁸)
result <<= 8;
// reverse the 64-bit value to get: 0⁸p₅₅..p₀₀
result = reverse_bits_64(result);
return result;
}
/// <summary>
/// Calculate and return q₄₃..q₀₀
/// per Definition 3.11:
/// bitstream_q = (p₀₂ ... p₄₅) ⊕ (p₀₈ ... p₅₁) ⊕ (p₁₂ ... p₅₅)
/// <-- 44b --> <-- 44b --> <-- 44b -->
/// q43_q00 = (p₄₅ ... p₀₂) ⊕ (p₅₁ ... p₀₈) ⊕ (p₅₅ ... p₁₂)
/// </summary>
/// <param name="p55_p00">56 bit value: p₅₅..p₀₀</param>
/// <returns>44-bit value: q₄₃..q₀₀</returns>
static inline uint64_t calculate__q43_q00(const uint64_t *p55_p00) {
ASSERT(p55_p00 != nullptr);
static const uint64_t C_BITMASK44 = (1ull << 44) - 1u;
uint64_t result = (*p55_p00 >> 2);
result ^= (*p55_p00 >> 8);
result ^= (*p55_p00 >> 12);
result &= C_BITMASK44;
return result;
}
/// <summary>
/// Relies on old g22 bit (now in L00).
/// May modify G00, G03, G04, G05, G06, G13, G16
/// </summary>
static inline void g_successor(ID48LIBX_STATE_REGISTERS *ssr) {
ASSERT(ssr != nullptr);
ASSERT(!is_ssr_state_stable(ssr));
assign_single_ssr_bit(ssr, SSR_BIT_G00, test_single_ssr_bit(ssr, SSR_UNSTABLE_BIT_j));
//alternatively: set to zero, because `j` includes the start bit state
//if (test_single_ssr_bit(ssr, SSR_UNSTABLE_BIT_j)) {
// flip_single_ssr_bit(ssr, SSR_BIT_G00);
//}
if (test_single_ssr_bit(ssr, SSR_UNSTABLE_OLD_BIT_G22)) {
// taps ==> [ n, 16, 13, 6, 5, 3, 0 ]
// 0b000'0001'0010'0000'0110'1001 == 0x012069
static const uint64_t G22_XOR_MASK = 0x0000000240D20000ull;
// static assert is only available in C11 (or C++11) and later...
// _Static_assert(G22_XOR_MASK == (0x012069ull << SSR_SHIFT_COUNT_REG_G), "G22 XOR Mask invalid");
ssr->Raw ^= G22_XOR_MASK;
}
if (test_single_ssr_bit(ssr, SSR_UNSTABLE_BIT_i)) {
flip_single_ssr_bit(ssr, SSR_BIT_G04);
}
}
static inline ID48LIBX_STATE_REGISTERS init_id48libx_state_register(const ID48LIB_KEY *k96, const ID48LIB_NONCE *n56) {
ASSERT(k96 != nullptr);
ASSERT(n56 != nullptr);
ID48LIBX_STATE_REGISTERS result;
result.Raw = 0;
ID48LIBX_STATE_REGISTERS *const ssr = &result; // the pointer is constant ... not the value it points to
const uint64_t p55_p00 = calculate__p55_p00(k96, n56);
// p55_p00 is used to set initial value of register l
if (true) {
static const uint8_t C_BITMASK7 = ((1u << 7) - 1u);
const uint8_t l = (
((uint8_t)(p55_p00 >> 55)) ^ // 0 0 0 0 0 0 p55
((uint8_t)(p55_p00 >> 51)) ^ // 0 0 p55 p54 p53 p52 p51
((uint8_t)(p55_p00 >> 45)) // p51 p50 p49 p48 p47 p46 p45
) & C_BITMASK7;
set_register_l(ssr, l);
ASSERT(l == get_register_l(ssr));
}
// p is used to calculate q
const uint64_t q43_q00 = calculate__q43_q00(&p55_p00);
// init( q₂₀..q₄₂, q₀₀..q₁₉ )
// ===> G(q₂₀..q₄₂, 0, q₀₀..q₁₉)
// ===> g₀₀..g₂₂ :=== q₂₀..q₄₂
// and j₀₀..j₁₉ :=== q₀₀..q₁₉
//
// But, since I'm storing the register with g₀₀ as lsb:
// ===> g₂₂..g₀₀ :=== q₄₂..q₂₀
if (true) {
static const uint32_t C_BITMASK23 = ((1u << 23) - 1u);
const uint32_t g = ((uint32_t)(q43_q00 >> 20)) & C_BITMASK23;
set_register_g(ssr, g);
ASSERT(g == get_register_g(ssr));
}
// input bits for `j` during init are q00..q19, with q19 used first
// For ease of use, I'll generate this as q00..q19, so the loop
// can test the lsb (and then shift it right one bit)
uint32_t q00_q19 = reverse_bits_32(((uint32_t)q43_q00) << 12);
uint32_t q_lsb_next = q00_q19;
ssr->Raw |= 1u;
// G(g,0,j) twenty times, using q19, q18, ... q00 for `j`
for (uint8_t ix = 0; ix < 20; ++ix) {
ASSERT(is_ssr_state_stable(ssr));
ssr->Raw <<= 1; // starts the process ... it's now an unstable value
ASSERT(!is_ssr_state_stable(ssr));
assign_single_ssr_bit(ssr, SSR_UNSTABLE_BIT_j, (q_lsb_next & 1u) != 0);
assign_single_ssr_bit(ssr, SSR_UNSTABLE_BIT_i, 0);
q_lsb_next >>= 1;
g_successor(ssr);
// save only the register bits
ssr->Raw &= SSR_BITMASK_REG_ALL;
// mark this as a stable value
ssr->Raw |= 1u;
}
// h00..h12 is defined as 0 p00..p11
// but since we're storing h as h12..h00: p11..p00 0
if (true) {
// NOTE: delay `h` until loops done, else low bits
// will shift into / break calculation of g() above
static const uint16_t C_BITMASK_H_INIT = (1u << 13) - 2u; // 0b1'1111'1111'1110
const uint16_t h = (((uint16_t)p55_p00) << 1) & C_BITMASK_H_INIT;
set_register_h(ssr, h);
ASSERT(h == get_register_h(ssr));
}
return result;
}
/// <summary>
/// H(h) matches the research paper, definition 3.3
///
/// Reads bits H01, H08, H09, H11, H12.
/// </summary>
/// <remarks>
/// If ssr is in unstable state, caller is responsible for ensuring
/// the values have not changed.
/// </remarks>
static inline bool calculate_feedback_h(const ID48LIBX_STATE_REGISTERS *ssr) {
ASSERT(ssr != nullptr);
// ( h₀₁ && h₀₈ ) || ( h₀₉ && h₁₁ ) || (!h₁₂ )
// \____ a1 ____/ \____ a2 ____/ \____ a3 ____/
// result == xor(a1,a2,a3)
bool a1 = is_ssr_state_stable(ssr) ?
test_single_ssr_bit(ssr, SSR_BIT_H01) && test_single_ssr_bit(ssr, SSR_BIT_H08) :
test_single_ssr_bit(ssr, SSR_UNSTABLE_OLD_BIT_H01) && test_single_ssr_bit(ssr, SSR_UNSTABLE_OLD_BIT_H08);
bool a2 = is_ssr_state_stable(ssr) ?
test_single_ssr_bit(ssr, SSR_BIT_H09) && test_single_ssr_bit(ssr, SSR_BIT_H11) :
test_single_ssr_bit(ssr, SSR_UNSTABLE_OLD_BIT_H09) && test_single_ssr_bit(ssr, SSR_UNSTABLE_OLD_BIT_H11);
bool a3 = is_ssr_state_stable(ssr) ?
!test_single_ssr_bit(ssr, SSR_BIT_H12) :
!test_single_ssr_bit(ssr, SSR_UNSTABLE_OLD_BIT_H12);
bool result = false;
if (a1) result = !result;
if (a2) result = !result;
if (a3) result = !result;
return result;
}
/// <summary>
/// fₗ(...) matches the research paper, definition 3.4
/// hard-coded to use bits for calculation of 'a'
/// </summary>
static inline bool calculate_feedback_l(const ID48LIBX_STATE_REGISTERS *ssr) {
ASSERT(ssr != nullptr);
// a = fₗ( g00 g04 g06 g13 g18 h03 ) ⊕ g22 ⊕ r02 ⊕ r06
// fₗ( x₀ x₁ x₂ x₃ x₄ x₅ )
bool x0 = test_single_ssr_bit(ssr, is_ssr_state_stable(ssr) ? SSR_BIT_G00 : SSR_UNSTABLE_OLD_BIT_G00);
bool x1 = test_single_ssr_bit(ssr, is_ssr_state_stable(ssr) ? SSR_BIT_G04 : SSR_UNSTABLE_OLD_BIT_G04);
bool x2 = test_single_ssr_bit(ssr, is_ssr_state_stable(ssr) ? SSR_BIT_G06 : SSR_UNSTABLE_OLD_BIT_G06);
bool x3 = test_single_ssr_bit(ssr, is_ssr_state_stable(ssr) ? SSR_BIT_G13 : SSR_UNSTABLE_OLD_BIT_G13);
bool x4 = test_single_ssr_bit(ssr, is_ssr_state_stable(ssr) ? SSR_BIT_G18 : SSR_UNSTABLE_OLD_BIT_G18);
bool x5 = test_single_ssr_bit(ssr, is_ssr_state_stable(ssr) ? SSR_BIT_H03 : SSR_UNSTABLE_OLD_BIT_H03);
bool line1 = !x0 && !x2 && x3;
bool line2 = x2 && x4 && !x5;
bool line3 = x0 && !x1 && !x4;
bool line4 = x1 && !x3 && x5;
bool result = line1 || line2 || line3 || line4;
return result;
}
/// <summary>
/// fₘ(...) matches the research paper, definition 3.5
/// hard-coded to use bits for calculation of 'b'
/// </summary>
static inline bool calculate_feedback_m(const ID48LIBX_STATE_REGISTERS *ssr) {
ASSERT(ssr != nullptr);
// b = fₘ( g01 g05 g10 g15 h00 h07 ) ⊕ l00 ⊕ l03 ⊕ l06
// fₘ( x₀ x₁ x₂ x₃ x₄ x₅ )
bool x0 = test_single_ssr_bit(ssr, is_ssr_state_stable(ssr) ? SSR_BIT_G01 : SSR_UNSTABLE_OLD_BIT_G01);
bool x1 = test_single_ssr_bit(ssr, is_ssr_state_stable(ssr) ? SSR_BIT_G05 : SSR_UNSTABLE_OLD_BIT_G05);
bool x2 = test_single_ssr_bit(ssr, is_ssr_state_stable(ssr) ? SSR_BIT_G10 : SSR_UNSTABLE_OLD_BIT_G10);
bool x3 = test_single_ssr_bit(ssr, is_ssr_state_stable(ssr) ? SSR_BIT_G15 : SSR_UNSTABLE_OLD_BIT_G15);
bool x4 = test_single_ssr_bit(ssr, is_ssr_state_stable(ssr) ? SSR_BIT_H00 : SSR_UNSTABLE_OLD_BIT_H00);
bool x5 = test_single_ssr_bit(ssr, is_ssr_state_stable(ssr) ? SSR_BIT_H07 : SSR_UNSTABLE_OLD_BIT_H07);
bool line1 = x1 && !x2 && !x4;
bool line2 = x0 && x2 && !x3;
bool line3 = !x1 && x3 && x5;
bool line4 = !x0 && x4 && !x5;
bool result = line1 || line2 || line3 || line4;
return result;
}
/// <summary>
/// fᵣ(...) matches the research paper, definition 3.6
/// hard-coded to use bits for calculation of 'c'
/// </summary>
static inline bool calculate_feedback_r(const ID48LIBX_STATE_REGISTERS *ssr) {
ASSERT(ssr != nullptr);
ASSERT(!is_ssr_state_stable(ssr));
// c = fᵣ( g02 g03⊕i g09 g14 g16 h01 ) ⊕ m00 ⊕ m03 ⊕ m06
// fᵣ( x₀ x₁ x₂ x₃ x₄ x₅ )
bool x0 = test_single_ssr_bit(ssr, SSR_UNSTABLE_OLD_BIT_G02);
bool x1 = test_single_ssr_bit(ssr, SSR_UNSTABLE_OLD_BIT_G03);
if (test_single_ssr_bit(ssr, SSR_UNSTABLE_BIT_i)) { x1 = !x1; }
bool x2 = test_single_ssr_bit(ssr, SSR_UNSTABLE_OLD_BIT_G09);
bool x3 = test_single_ssr_bit(ssr, SSR_UNSTABLE_OLD_BIT_G14);
bool x4 = test_single_ssr_bit(ssr, SSR_UNSTABLE_OLD_BIT_G16);
bool x5 = test_single_ssr_bit(ssr, SSR_UNSTABLE_OLD_BIT_H01);
bool line1 = x1 && x3 && !x5;
bool line2 = x2 && !x3 && !x4;
bool line3 = !x0 && !x2 && x5;
bool line4 = x0 && !x1 && x4;
bool result = line1 || line2 || line3 || line4;
return result;
}
/// <summary>
/// Matches the research paper, definition 3.7
/// See also Definition 3.2, defining that parameter as `j`.
/// </summary>
static inline bool calculate_j(const ID48LIBX_STATE_REGISTERS *ssr) {
ASSERT(ssr != nullptr);
// g := G(g, i, l₀₁ ⊕ m₀₆ ⊕ h₀₂ ⊕ h₀₈ ⊕ h₁₂)
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^------ calculates `j`
bool result = 0;
if (test_single_ssr_bit(ssr, is_ssr_state_stable(ssr) ? SSR_BIT_L01 : SSR_UNSTABLE_OLD_BIT_L01)) result = !result;
if (test_single_ssr_bit(ssr, is_ssr_state_stable(ssr) ? SSR_BIT_M06 : SSR_UNSTABLE_OLD_BIT_M06)) result = !result;
if (test_single_ssr_bit(ssr, is_ssr_state_stable(ssr) ? SSR_BIT_H02 : SSR_UNSTABLE_OLD_BIT_H02)) result = !result;
if (test_single_ssr_bit(ssr, is_ssr_state_stable(ssr) ? SSR_BIT_H08 : SSR_UNSTABLE_OLD_BIT_H08)) result = !result;
if (test_single_ssr_bit(ssr, is_ssr_state_stable(ssr) ? SSR_BIT_H12 : SSR_UNSTABLE_OLD_BIT_H12)) result = !result;
return result;
}
/// <summary>
/// REQUIRES INPUT BIT `i` TO BE VALID.
/// Calculates a, b, c, j and new value for H₀₀.
/// These are the only bits changed by this function.
/// </summary>
static inline void calculate_temporaries(ID48LIBX_STATE_REGISTERS *ssr) {
ASSERT(ssr != nullptr);
#pragma region // to be removed after all is validated
static const uint64_t bits_must_remain_same_mask =
~(
(1ull << SSR_UNSTABLE_BIT_a) |
(1ull << SSR_UNSTABLE_BIT_b) |
(1ull << SSR_UNSTABLE_BIT_c) |
(1ull << SSR_UNSTABLE_BIT_j) |
(1ull << SSR_UNSTABLE_NEW_BIT_H00)
);
const uint64_t backup = ssr->Raw & bits_must_remain_same_mask;
(void)backup; // to avoid warning about unused variable
#pragma endregion // to be removed after all is validated
// Only bits that change value: H00, a, b, c, j
ASSERT(!is_ssr_state_stable(ssr)); // assigning temp values directly in ssr, so...
assign_single_ssr_bit(ssr, SSR_UNSTABLE_NEW_BIT_H00, calculate_feedback_h(ssr));
assign_single_ssr_bit(ssr, SSR_UNSTABLE_BIT_a, calculate_feedback_l(ssr));
assign_single_ssr_bit(ssr, SSR_UNSTABLE_BIT_b, calculate_feedback_m(ssr));
assign_single_ssr_bit(ssr, SSR_UNSTABLE_BIT_c, calculate_feedback_r(ssr));
assign_single_ssr_bit(ssr, SSR_UNSTABLE_BIT_j, calculate_j(ssr));
// NOTE: Could scramble the below nine lines into any order desired.
// If start by setting the outputs all to zero, could also scramble the above into this mix
//
// a = fₗ() ⊕ g22 ⊕ r02 ⊕ r06
if (test_single_ssr_bit(ssr, SSR_UNSTABLE_OLD_BIT_G22)) flip_single_ssr_bit(ssr, SSR_UNSTABLE_BIT_a);
if (test_single_ssr_bit(ssr, SSR_UNSTABLE_OLD_BIT_R02)) flip_single_ssr_bit(ssr, SSR_UNSTABLE_BIT_a);
if (test_single_ssr_bit(ssr, SSR_UNSTABLE_OLD_BIT_R06)) flip_single_ssr_bit(ssr, SSR_UNSTABLE_BIT_a);
// b = fₘ() ⊕ l00 ⊕ l03 ⊕ l06
if (test_single_ssr_bit(ssr, SSR_UNSTABLE_OLD_BIT_L00)) flip_single_ssr_bit(ssr, SSR_UNSTABLE_BIT_b);
if (test_single_ssr_bit(ssr, SSR_UNSTABLE_OLD_BIT_L03)) flip_single_ssr_bit(ssr, SSR_UNSTABLE_BIT_b);
if (test_single_ssr_bit(ssr, SSR_UNSTABLE_OLD_BIT_L06)) flip_single_ssr_bit(ssr, SSR_UNSTABLE_BIT_b);
// c = fᵣ() ⊕ m00 ⊕ m03 ⊕ m06
if (test_single_ssr_bit(ssr, SSR_UNSTABLE_OLD_BIT_M00)) flip_single_ssr_bit(ssr, SSR_UNSTABLE_BIT_c);
if (test_single_ssr_bit(ssr, SSR_UNSTABLE_OLD_BIT_M03)) flip_single_ssr_bit(ssr, SSR_UNSTABLE_BIT_c);
if (test_single_ssr_bit(ssr, SSR_UNSTABLE_OLD_BIT_M06)) flip_single_ssr_bit(ssr, SSR_UNSTABLE_BIT_c);
#pragma region // to be removed after all is validated
const uint64_t chk = ssr->Raw & bits_must_remain_same_mask;
(void)chk; // to avoid warning about unused variable
ASSERT(chk == backup);
#pragma endregion // to be removed after all is validated
return;
}
static inline OUTPUT_INDEX2 calculate_output_index(const ID48LIBX_STATE_REGISTERS *ssr) {
// Fₒ( abc l₀l₂l₃l₄l₅l₆ m₀m₁m₃m₅ r₀r₁r₂r₃r₄r₅r₆ )
// msb 19 ---^ lsb 00 ---^^
ASSERT(ssr != nullptr);
ASSERT(!is_ssr_state_stable(ssr));
OUTPUT_INDEX2 result;
result.Raw = 0;
if (test_single_ssr_bit(ssr, SSR_UNSTABLE_BIT_a)) result.Raw |= (1u << 19);
if (test_single_ssr_bit(ssr, SSR_UNSTABLE_BIT_b)) result.Raw |= (1u << 18);
if (test_single_ssr_bit(ssr, SSR_UNSTABLE_BIT_c)) result.Raw |= (1u << 17);
//bool bit17 = test_single_ssr_bit(ssr, SSR_UNSTABLE_BIT_c);
//if (test_single_ssr_bit(ssr, SSR_UNSTABLE_BIT_i) ) bit17 = !bit17;
//if (bit17 ) result.Raw |= (1u << 17);
if (test_single_ssr_bit(ssr, SSR_UNSTABLE_OLD_BIT_L00)) result.Raw |= (1u << 16);
if (test_single_ssr_bit(ssr, SSR_UNSTABLE_OLD_BIT_L02)) result.Raw |= (1u << 15);
if (test_single_ssr_bit(ssr, SSR_UNSTABLE_OLD_BIT_L03)) result.Raw |= (1u << 14);
if (test_single_ssr_bit(ssr, SSR_UNSTABLE_OLD_BIT_L04)) result.Raw |= (1u << 13);
if (test_single_ssr_bit(ssr, SSR_UNSTABLE_OLD_BIT_L05)) result.Raw |= (1u << 12);
if (test_single_ssr_bit(ssr, SSR_UNSTABLE_OLD_BIT_L06)) result.Raw |= (1u << 11);
if (test_single_ssr_bit(ssr, SSR_UNSTABLE_OLD_BIT_M00)) result.Raw |= (1u << 10);
if (test_single_ssr_bit(ssr, SSR_UNSTABLE_OLD_BIT_M01)) result.Raw |= (1u << 9);
if (test_single_ssr_bit(ssr, SSR_UNSTABLE_OLD_BIT_M03)) result.Raw |= (1u << 8);
if (test_single_ssr_bit(ssr, SSR_UNSTABLE_OLD_BIT_M05)) result.Raw |= (1u << 7);
if (test_single_ssr_bit(ssr, SSR_UNSTABLE_OLD_BIT_R00)) result.Raw |= (1u << 6);
if (test_single_ssr_bit(ssr, SSR_UNSTABLE_OLD_BIT_R01)) result.Raw |= (1u << 5);
if (test_single_ssr_bit(ssr, SSR_UNSTABLE_OLD_BIT_R02)) result.Raw |= (1u << 4);
if (test_single_ssr_bit(ssr, SSR_UNSTABLE_OLD_BIT_R03)) result.Raw |= (1u << 3);
if (test_single_ssr_bit(ssr, SSR_UNSTABLE_OLD_BIT_R04)) result.Raw |= (1u << 2);
if (test_single_ssr_bit(ssr, SSR_UNSTABLE_OLD_BIT_R05)) result.Raw |= (1u << 1);
if (test_single_ssr_bit(ssr, SSR_UNSTABLE_OLD_BIT_R06)) result.Raw |= (1u << 0);
return result;
}
// returns a single bit corresponding to the output bit for this transition
static inline bool calculate_successor_state(ID48LIBX_STATE_REGISTERS *ssr, bool i) {
ASSERT(ssr != nullptr);
ASSERT(is_ssr_state_stable(ssr));
// HACK -- ORDER OF THESE OPERATIONS MATTERS ...
// to avoid overwriting bits needed for calculation of temporaries
// Thus:
// 1. ssr_new = ssr_old << 1; // all prior values still available (even r₀₆)
// 2. store input bit `i` // required many places
// 3. calculate and store a/b/c/j h'00 // can use SSR_UNSTABLE_OLD_BIT_... to get old values
// 4. calculate and save output index // relies on a/b/c AND the bits that get modified using a/b/c,
// // so must be after calculate a/b/c and before setting new L00,M00,R00 values
// 5. G(g, i, j) // relies on SSR_UNSTABLE_OLD_BIT_G22, which is now L00 ... aka must do before L()
// 6. L() // overwrite L00 with `a`
// 7. M() // overwrite M00 with `b`
// 8. R() // overwrite R00 with `c`
//
// 1. ssr_new = ssr_old << 1;
ssr->Raw <<= 1; // begin!
// 2. store input bit `i`
assign_temporary_i(ssr, i);
// 3. calculate and store a/b/c/j and new H00 bits
calculate_temporaries(ssr); // updates new H00, stores a/c/c and j
// 4. calculate and save output index
OUTPUT_INDEX2 output_index = calculate_output_index(ssr); // note: does *NOT* rely on new H00 value
bool output_result = id48libx_output_lookup(output_index.Raw);
// 5. g --> g', aka G(g, i, j)
g_successor(ssr);
// 6. l --> l'
assign_single_ssr_bit(ssr, SSR_BIT_L00, test_single_ssr_bit(ssr, SSR_UNSTABLE_BIT_a));
// 7. m --> m'
assign_single_ssr_bit(ssr, SSR_BIT_M00, test_single_ssr_bit(ssr, SSR_UNSTABLE_BIT_b));
// 8. r --> r'
assign_single_ssr_bit(ssr, SSR_BIT_R00, test_single_ssr_bit(ssr, SSR_UNSTABLE_BIT_c));
// Done! Clear temporaries and indicate this is a final state
// Keep only the registers (no temporaries)
ssr->Raw &= SSR_BITMASK_REG_ALL;
// Mark as stable view of the SSR
ssr->Raw |= 1u;
return output_result;
}
/// <summary>
/// Returns a value where the least significant bit is the
/// first input bit, so that the value can be right-shifted
/// by one bit each iteration (allowing least significant bit
/// to always be the input bit).
/// </summary>
static inline INPUT_BITS2 get_key_input_bits(const ID48LIB_KEY *k) {
ASSERT(k != nullptr);
// Per research paper, key bit 39 is used first.
// So, what should end up in result is: 0²⁴ k₀₀..K₃₉
// This allows simply shifting the lsb out each cycle....
INPUT_BITS2 result;
result.Raw = 0;
// k[ 0] :== K₉₅..K₈₈
// ...
// k[ 7] :== K₃₉..K₃₂
// ...
// k[11] :== K₀₇..K₀₀
for (uint8_t i = 0; i < 5; ++i) {
result.Raw <<= 8;
uint8_t tmp = k->k[11 - i]; // e.g., first loop will contain K₀₇..K₀₀
tmp = reverse_bits_08(tmp); // e.g., first loop will contain K₀₀..K₀₇
result.Raw |= tmp;
}
static const uint64_t INPUT_MASK = (1ull << 40) - 1u;
(void)INPUT_MASK; // to avoid warning about unused variable
ASSERT((result.Raw & (~INPUT_MASK)) == 0ull);
return result;
}
static inline bool shift_out_next_input_bit(INPUT_BITS2 *inputs) {
ASSERT(inputs != nullptr);
bool result = inputs->Raw & 1ull;
inputs->Raw >>= 1;
return result;
}
static inline void shift_in_next_output_bit(OUTPUT_BITS2 *outputs, bool v) {
ASSERT(outputs != nullptr);
outputs->Raw <<= 1;
if (v) outputs->Raw |= 1ull;
}
static inline void extract_frn(const OUTPUT_BITS2 *outputs, ID48LIB_FRN *frn28_out) {
ASSERT(outputs != nullptr);
ASSERT(frn28_out != nullptr);
static const uint64_t C_MASK28 = (1ull << 28) - 1u;
uint64_t tmp = outputs->Raw;
tmp >>= 20; // remove the 20 bit grn (but still has 7 ignored bits)
tmp &= C_MASK28; // tmp now has exactly 28 valid bits
tmp <<= 4; // align to 32-bits for easier assignment to output
// tmp now :== O₀₀..O₂₇ 0000
frn28_out->frn[0] = (uint8_t)((tmp >> (8 * 3)) & 0xFFu);
frn28_out->frn[1] = (uint8_t)((tmp >> (8 * 2)) & 0xFFu);
frn28_out->frn[2] = (uint8_t)((tmp >> (8 * 1)) & 0xFFu);
frn28_out->frn[3] = (uint8_t)((tmp >> (8 * 0)) & 0xFFu);
}
static inline void extract_grn(const OUTPUT_BITS2 *outputs, ID48LIB_GRN *grn20_out) {
ASSERT(outputs != nullptr);
ASSERT(grn20_out != nullptr);
memset(grn20_out, 0, sizeof(ID48LIB_GRN));
static const uint64_t C_MASK20 = (1ull << 20) - 1u;
uint64_t tmp = outputs->Raw;
tmp &= C_MASK20; // tmp now has exactly 20 valid bits
tmp <<= 4; // align to 24-bits for easier assignment to output
grn20_out->grn[0] = (uint8_t)((tmp >> (8 * 2)) & 0xFFu);
grn20_out->grn[1] = (uint8_t)((tmp >> (8 * 1)) & 0xFFu);
grn20_out->grn[2] = (uint8_t)((tmp >> (8 * 0)) & 0xFFu);
}
static void retro_generator_impl(
const ID48LIB_KEY *k,
const ID48LIB_NONCE *n,
ID48LIB_FRN *frn28_out,
ID48LIB_GRN *grn20_out
) {
ASSERT(k != nullptr);
ASSERT(n != nullptr);
ASSERT(frn28_out != nullptr);
ASSERT(grn20_out != nullptr);
memset(frn28_out, 0, sizeof(ID48LIB_FRN));
memset(grn20_out, 0, sizeof(ID48LIB_GRN));
ID48LIBX_STATE_REGISTERS ssr = init_id48libx_state_register(k, n);
// get 55-bit successor state input
INPUT_BITS2 inputs = get_key_input_bits(k);
OUTPUT_BITS2 outputs;
outputs.Raw = 0ull;
for (uint8_t ix = 0; ix < 55; ix++) {
ASSERT(is_ssr_state_stable(&ssr));
// input bit `i` is not valid in stable state...
bool input_bit = shift_out_next_input_bit(&inputs);
// calculate the next state... (note: logs calculations for this state)
bool output_bit = calculate_successor_state(&ssr, input_bit);
ASSERT(is_ssr_state_stable(&ssr));
// store the output bit
shift_in_next_output_bit(&outputs, output_bit);
}
// convert the output bits into frn/grn
extract_frn(&outputs, frn28_out);
extract_grn(&outputs, grn20_out);
return;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// ******************************************************************************************************************** //
// *** Everything above this line in the file is declared static, *** //
// *** which avoids polluting the global namespace. *** //
// *** Everything below is technically visible, but not necessarily an exported API. *** //
// *** In C++, this separation is much more easily achieved using an anonymous namespace. C'est la vie! *** //
// ******************************************************************************************************************** //
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// internal function
ID48LIBX_SUCCESSOR_RESULT id48libx_retro003_successor(const ID48LIBX_STATE_REGISTERS *initial_state, uint8_t input_bit) {
ASSERT(initial_state != nullptr);
ID48LIBX_SUCCESSOR_RESULT r;
memset(&r, 0, sizeof(ID48LIBX_SUCCESSOR_RESULT));
ID48LIBX_STATE_REGISTERS s = *initial_state;
bool output_bit = calculate_successor_state(&s, !!input_bit);
r.state.Raw = s.Raw;
r.output = output_bit;
return r;
}
// internal function
ID48LIBX_STATE_REGISTERS id48libx_retro003_init(const ID48LIB_KEY *key, const ID48LIB_NONCE *nonce) {
ASSERT(key != nullptr);
ASSERT(nonce != nullptr);
ID48LIBX_STATE_REGISTERS ssr = init_id48libx_state_register(key, nonce);
ID48LIBX_STATE_REGISTERS result;
memset(&result, 0, sizeof(ID48LIBX_STATE_REGISTERS));
result.Raw = ssr.Raw;
return result;
}
// public API
void id48lib_generator(
const ID48LIB_KEY *k,
const ID48LIB_NONCE *n,
ID48LIB_FRN *frn28_out,
ID48LIB_GRN *grn20_out
) {
retro_generator_impl(k, n, frn28_out, grn20_out);
}