/**
* 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)
///
/// Calculates and returns 56-bit value p₅₅..p₀₀
/// per Definition 3.11:
/// p = p₀₀..p₅₅ = ( K₄₀..K₉₅ ) + ( N₀₀..N₅₅ )
///
/// key in pm3 order
/// nonce in pm3 order
/// 56-bit value p₅₅..p₀₀
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;
}
///
/// 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₁₂)
///
/// 56 bit value: p₅₅..p₀₀
/// 44-bit value: q₄₃..q₀₀
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;
}
///
/// Relies on old g22 bit (now in L00).
/// May modify G00, G03, G04, G05, G06, G13, G16
///
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;
}
///
/// H(h) matches the research paper, definition 3.3
///
/// Reads bits H01, H08, H09, H11, H12.
///
///
/// If ssr is in unstable state, caller is responsible for ensuring
/// the values have not changed.
///
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;
}
///
/// fₗ(...) matches the research paper, definition 3.4
/// hard-coded to use bits for calculation of 'a'
///
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;
}
///
/// fₘ(...) matches the research paper, definition 3.5
/// hard-coded to use bits for calculation of 'b'
///
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;
}
///
/// fᵣ(...) matches the research paper, definition 3.6
/// hard-coded to use bits for calculation of 'c'
///
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;
}
///
/// Matches the research paper, definition 3.7
/// See also Definition 3.2, defining that parameter as `j`.
///
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;
}
///
/// 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.
///
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;
}
///
/// 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).
///
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);
}