Compute xn297 whitening instead of using an hardcoded array (#1201)

* Compute xn297 whitening instead of using an hardcoded array

* Add function to scramble byte for XN297 interface

* Refactor XN297 scrambling logic to use a global LFSR and improve byte generation

* Add technical specification and implementation details for XN297 whitening and CRC
This commit is contained in:
goebish
2026-10-25 16:57:07 +01:00
committed by GitHub
parent d3293afdab
commit 91199e146e
4 changed files with 587 additions and 108 deletions
+4 -2
View File
@@ -70,12 +70,14 @@ static void __attribute__((unused)) XN297Dump_RF_init()
}
}
extern const uint8_t xn297_scramble[];
static boolean __attribute__((unused)) XN297Dump_process_packet(void)
{
uint16_t crcxored;
uint8_t packet_sc[XN297DUMP_MAX_PACKET_LEN], packet_un[XN297DUMP_MAX_PACKET_LEN];
uint8_t xn297_scramble[XN297DUMP_MAX_PACKET_LEN];
xn297_lfsr = XN297_SCRAMBLE_SEED;
for (uint8_t i = 0; i < XN297DUMP_MAX_PACKET_LEN; i++)
xn297_scramble[i] = XN297_scramble_byte();
enhanced=false;
// init crc
crc = 0xb5d2;
+81 -106
View File
@@ -6,22 +6,49 @@ bool xn297_scramble_enabled, xn297_crc, xn297_bitrate, xn297_rf;
uint8_t xn297_addr_len, xn297_rx_packet_len;
uint8_t xn297_tx_addr[5], xn297_rx_addr[5];
// xn297 address / pcf / payload scramble table
const uint8_t xn297_scramble[] = {
0xE3, 0xB1, 0x4B, 0xEA, 0x85, 0xBC, 0xE5, 0x66,
0x0D, 0xAE, 0x8C, 0x88, 0x12, 0x69, 0xEE, 0x1F,
0xC7, 0x62, 0x97, 0xD5, 0x0B, 0x79, 0xCA, 0xCC,
0x1B, 0x5D, 0x19, 0x10, 0x24, 0xD3, 0xDC, 0x3F,
0x8E, 0xC5, 0x2F, 0xAA, 0x16, 0xF3, 0x95 };
// XN297 whitening: x^7 + x^3 + 1, seed 0x71, MSB first (E3 B1 4B EA 85 ...).
// Generated a byte at a time with (x^7 + x^3 + 1)^4 = x^28 + x^12 + 1: s[i] = s[i-28] ^ s[i-12].
// xn297_lfsr holds the next 28 bits in bits 31..4, 0 when scrambling is off.
uint32_t xn297_lfsr;
static uint8_t __attribute__((unused)) XN297_scramble_byte()
{
uint8_t s = xn297_lfsr >> 24;
xn297_lfsr = (xn297_lfsr << 8) | (uint8_t)(s ^ (xn297_lfsr >> 8)) << 4;
return s;
}
static void __attribute__((unused)) XN297_scramble_init()
{
xn297_lfsr = xn297_scramble_enabled ? XN297_SCRAMBLE_SEED : 0;
}
// XN297 CRC: CRC-16/CCITT (init 0xFFFF, xorout 0xFFFF) over the unscrambled
// address, pcf and payload, sent scrambled. The emulation computes it from 0xB5D2
// over the bits as sent: the xorout is the difference for a CRC over 'bits' bits,
// the CRC of the scramble bits plus the scramble of the CRC bits.
// address, pcf and payload, sent scrambled: XORed with the scramble bits that follow,
// (xn297_lfsr >> 16) once the last byte has been scrambled.
// Returns the scrambled byte, crc updated with the unscrambled one.
static uint8_t __attribute__((unused)) XN297_crc_scramble(uint8_t b)
{
crc16_update(b, 8);
return b ^ XN297_scramble_byte();
}
// Returns the unscrambled byte, crc updated with it.
static uint8_t __attribute__((unused)) XN297_unscramble_crc(uint8_t b)
{
b ^= XN297_scramble_byte();
crc16_update(b, 8);
return b;
}
#ifdef XN297DUMP_NRF24L01_INO
// XN297Dump computes the CRC from 0xB5D2 over the bits as sent: the xorout is the
// difference for a CRC over 'bits' bits, the CRC of the scramble bits plus the
// scramble of the CRC bits.
static uint16_t __attribute__((unused)) XN297_crc_xorout(bool scramble, uint16_t bits)
{
uint16_t xorout = 0xFFFF ^ 0xB5D2;
uint8_t lfsr = 0x71, s; // scramble bits (xn297_scramble[]): 1110001, then s[i] = s[i-7] ^ s[i-3]
uint8_t lfsr = 0x71, s; // scramble bits: 1110001, then s[i] = s[i-7] ^ s[i-3]
for(uint16_t i = 0; i < bits + 16; i++)
{
s = scramble ? lfsr >> 6 : 0;
@@ -38,6 +65,7 @@ static uint16_t __attribute__((unused)) XN297_crc_xorout(bool scramble, uint16_t
}
return ~xorout;
}
#endif
static bool __attribute__((unused)) XN297_Configure(bool crc_en, bool scramble_en, bool bitrate, bool force_nrf)
{
@@ -103,13 +131,10 @@ static void __attribute__((unused)) XN297_SetTXAddr(const uint8_t* addr, uint8_t
static void __attribute__((unused)) XN297_SetRXAddr(const uint8_t* addr, uint8_t rx_packet_len)
{
//Scramble address
for (uint8_t i = 0; i < xn297_addr_len; ++i)
{
xn297_rx_addr[i] = addr[i];
if(xn297_scramble_enabled)
xn297_rx_addr[i] ^= xn297_scramble[xn297_addr_len-i-1];
}
//Scramble address in transmission order
XN297_scramble_init();
for (uint8_t i = xn297_addr_len; i--; )
xn297_rx_addr[i] = addr[i] ^ XN297_scramble_byte();
if(xn297_crc)
rx_packet_len += 2; // Include CRC
@@ -257,32 +282,19 @@ static void __attribute__((unused)) XN297_WritePayload(uint8_t* msg, uint8_t len
}
// address
crc = 0xFFFF;
XN297_scramble_init();
for (uint8_t i = 0; i < xn297_addr_len; ++i)
{
buf[last] = xn297_tx_addr[xn297_addr_len-i-1];
if(xn297_scramble_enabled)
buf[last] ^= xn297_scramble[i];
last++;
}
buf[last++] = XN297_crc_scramble(xn297_tx_addr[xn297_addr_len-i-1]);
// payload
for (uint8_t i = 0; i < len; ++i)
{
// bit-reverse bytes in packet
buf[last] = bit_reverse(msg[i]);
if(xn297_scramble_enabled)
buf[last] ^= xn297_scramble[xn297_addr_len+i];
last++;
}
buf[last++] = XN297_crc_scramble(bit_reverse(msg[i])); // bit-reverse bytes in packet
// crc
if (xn297_crc)
{
uint8_t offset = (xn297_addr_len < 4 && xn297_rf == XN297_NRF) ? 1 : 0;
crc = 0xb5d2;
for (uint8_t i = offset; i < last; ++i)
crc16_update( buf[i], 8);
crc ^= XN297_crc_xorout(xn297_scramble_enabled, 8 * (xn297_addr_len + len));
crc = ~crc ^ (xn297_lfsr >> 16);
buf[last++] = crc >> 8;
buf[last++] = crc & 0xff;
}
@@ -294,7 +306,6 @@ static void __attribute__((unused)) XN297_WritePayload(uint8_t* msg, uint8_t len
static void __attribute__((unused)) XN297_WriteEnhancedPayload(uint8_t* msg, uint8_t len, uint8_t noack)
{
uint8_t buf[32];
uint8_t scramble_index=0;
uint8_t last = 0;
static uint8_t pid=0;
@@ -304,54 +315,37 @@ static void __attribute__((unused)) XN297_WriteEnhancedPayload(uint8_t* msg, uin
}
// address
crc = xn297_scramble_enabled ? 0xFFFF : 0xB5D2; // unscrambled: no xorout
XN297_scramble_init();
for (uint8_t i = 0; i < xn297_addr_len; ++i)
{
buf[last] = xn297_tx_addr[xn297_addr_len-i-1];
if(xn297_scramble_enabled)
buf[last] ^= xn297_scramble[scramble_index++];
last++;
}
buf[last++] = XN297_crc_scramble(xn297_tx_addr[xn297_addr_len-i-1]);
// pcf
buf[last] = (len << 1) | (pid>>1);
if(xn297_scramble_enabled)
buf[last] ^= xn297_scramble[scramble_index++];
last++;
buf[last++] = XN297_crc_scramble((len << 1) | (pid>>1));
buf[last] = (pid << 7) | (noack << 6);
// payload
if(len)
{
buf[last]|= bit_reverse(msg[0]) >> 2; // first 6 bit of payload
if(xn297_scramble_enabled)
buf[last] ^= xn297_scramble[scramble_index++];
buf[last] = XN297_crc_scramble(buf[last] | (bit_reverse(msg[0]) >> 2)); // first 6 bit of payload
for (uint8_t i = 0; i < len-1; ++i)
{
last++;
buf[last] = (bit_reverse(msg[i]) << 6) | (bit_reverse(msg[i+1]) >> 2);
if(xn297_scramble_enabled)
buf[last] ^= xn297_scramble[scramble_index++];
buf[last] = XN297_crc_scramble((bit_reverse(msg[i]) << 6) | (bit_reverse(msg[i+1]) >> 2));
}
last++;
buf[last] = bit_reverse(msg[len-1]) << 6; // last 2 bit of payload
}
if(xn297_scramble_enabled)
buf[last] ^= xn297_scramble[scramble_index++] & 0xc0;
crc16_update( buf[last], 2);
buf[last] ^= (xn297_lfsr >> 24) & 0xc0;
// crc
if (xn297_crc)
{
uint8_t offset = (xn297_addr_len < 4 && xn297_rf == XN297_NRF) ? 1 : 0;
crc = 0xb5d2;
for (uint8_t i = offset; i < last; ++i)
crc16_update( buf[i], 8);
crc16_update( buf[last] & 0xc0, 2);
if (xn297_scramble_enabled)
crc ^= XN297_crc_xorout(true, 8 * (xn297_addr_len + len) + 10);
//else
// crc ^= XN297_crc_xorout(false, 8 * (xn297_addr_len + len) + 10);
crc = ~crc ^ (xn297_lfsr >> 14);
buf[last++] |= (crc >> 8) >> 2;
buf[last++] = ((crc >> 8) << 6) | ((crc & 0xff) >> 2);
@@ -408,28 +402,18 @@ static bool __attribute__((unused)) XN297_ReadPayload(uint8_t* msg, uint8_t len)
// Read payload
XN297_ReceivePayload(buf, len);
// Decode payload
// Decode payload, calculate CRC
crc = 0xFFFF;
XN297_scramble_init();
for (uint8_t i = 0; i < xn297_addr_len; ++i)
XN297_unscramble_crc(xn297_rx_addr[xn297_addr_len-i-1]);
for(uint8_t i=0; i<len; i++)
{
uint8_t b_in=buf[i];
if(xn297_scramble_enabled)
b_in ^= xn297_scramble[i+xn297_addr_len];
msg[i] = bit_reverse(b_in);
}
msg[i] = bit_reverse(XN297_unscramble_crc(buf[i]));
if (!xn297_crc)
return true; // No CRC so OK by default...
// Calculate CRC
crc = 0xb5d2;
//process address
for (uint8_t i = 0; i < xn297_addr_len; ++i)
crc16_update( xn297_rx_addr[xn297_addr_len-i-1], 8);
//process payload
for (uint8_t i = 0; i < len; ++i)
crc16_update( buf[i], 8);
//xorout
crc ^= XN297_crc_xorout(xn297_scramble_enabled, 8 * (xn297_addr_len + len));
crc = ~crc ^ (xn297_lfsr >> 16);
//test
if( (crc >> 8) == buf[len] && (crc & 0xff) == buf[len+1])
return true; // CRC OK
@@ -443,40 +427,31 @@ static uint8_t __attribute__((unused)) XN297_ReadEnhancedPayload(uint8_t* msg, u
// Read payload
XN297_ReceivePayload(buffer, len+2); // Read pcf + payload + CRC
// Decode payload
pcf_size = buffer[0];
if(xn297_scramble_enabled)
pcf_size ^= xn297_scramble[xn297_addr_len];
pcf_size = pcf_size >> 1;
// Decode payload, calculate CRC
#ifdef XN297DUMP_NRF24L01_INO
bool xorout = true;
#else
bool xorout = xn297_scramble_enabled; // unscrambled: no xorout
#endif
crc = xorout ? 0xFFFF : 0xB5D2;
XN297_scramble_init();
for (uint8_t i = 0; i < xn297_addr_len; ++i)
XN297_unscramble_crc(xn297_rx_addr[xn297_addr_len-i-1]);
pcf_size = XN297_unscramble_crc(buffer[0]) >> 1;
if(pcf_size>32)
return 255; // Error
for(uint8_t i=0; i< pcf_size; i++)
{
msg[i] = bit_reverse((buffer[i+1] << 2) | (buffer[i+2] >> 6));
if(xn297_scramble_enabled)
msg[i] ^= bit_reverse((xn297_scramble[xn297_addr_len+i+1] << 2) |
(xn297_scramble[xn297_addr_len+i+2] >> 6));
{ // payload is shifted by 2 bits: unscramble with bits 29..22 of the next scramble bits
msg[i] = bit_reverse(((buffer[i+1] << 2) | (buffer[i+2] >> 6)) ^ (xn297_lfsr >> 22));
XN297_unscramble_crc(buffer[i+1]);
}
crc16_update( (buffer[pcf_size+1] ^ (xn297_lfsr >> 24)) & 0xc0, 2);
if (!xn297_crc)
return pcf_size; // No CRC so OK by default...
// Calculate CRC
crc = 0xb5d2;
//process address
for (uint8_t i = 0; i < xn297_addr_len; ++i)
crc16_update( xn297_rx_addr[xn297_addr_len-i-1], 8);
//process payload
for (uint8_t i = 0; i < pcf_size+1; ++i)
crc16_update( buffer[i], 8);
crc16_update( buffer[pcf_size+1] & 0xc0, 2);
//xorout
if (xn297_scramble_enabled)
crc ^= XN297_crc_xorout(true, 8 * (xn297_addr_len + pcf_size) + 10);
#ifdef XN297DUMP_NRF24L01_INO
else
crc ^= XN297_crc_xorout(false, 8 * (xn297_addr_len + pcf_size) + 10);
#endif
if (xorout)
crc = ~crc ^ (xn297_lfsr >> 14);
uint16_t crcxored=(buffer[pcf_size+1]<<10)|(buffer[pcf_size+2]<<2)|(buffer[pcf_size+3]>>6) ;
if( crc == crcxored)
return pcf_size; // CRC OK
+6
View File
@@ -20,6 +20,9 @@
#define XN297_250K true
#define XN297_NRF false
#define XN297_CC2500 true
#define XN297_SCRAMBLE_SEED 0xE3B14BE0 // whitening x^7 + x^3 + 1, seed 0x71: first 28 bits
extern uint32_t xn297_lfsr;
//////////////
// Functions
@@ -41,7 +44,10 @@ static void __attribute__((unused)) XN297_RFChannel(uint8_t);
static void __attribute__((unused)) XN297_SetPower();
static void __attribute__((unused)) XN297_SetFreqOffset();
static bool __attribute__((unused)) XN297_IsPacketSent();
static uint8_t __attribute__((unused)) XN297_scramble_byte();
#ifdef XN297DUMP_NRF24L01_INO
static uint16_t __attribute__((unused)) XN297_crc_xorout(bool, uint16_t);
#endif
#endif