Following piece of code with adequate key rotation is sufficient enough to prevent replay attacks on transmitters/receivers built using 8-bit microcontrollers like PIC12F675 - hugely popular and readily available in every electronic shop in my "heavenly" country...Serbia.
It will take 17 bytes of RAM and 56 bytes of program memory which will leave plenty of space for whatever you plan on doing with that obsolete 8-bit monster.
const uint16_t K[4] = {
0xCCAA, 0xAA33, 0xF0F0, 0xEAEA
};
const uint16_t M[4][4] = {
{ 0x000F, 0x00F0, 0x0F00, 0xF000 },
{ 0x00F0, 0x0F00, 0xF000, 0x000F },
{ 0x0F00, 0xF000, 0x000F, 0x00F0 },
{ 0xF000, 0x000F, 0x00F0, 0x0F00 }
};
uint8_t i = 0x0;
void encrypt(uint16_t* key, uint16_t* message) {
uint16_t sections[4] = { 0x0 }; // use eeprom
for ( i = 0; i < 4; i ++) {
sections[0] = *message & M[i][0];
sections[1] = *message & M[i][1];
sections[2] = *message & M[i][2];
sections[3] = *message & M[i][3];
*message = sections[0] | sections[1] | sections[2] | sections[3];
*message ^= *key ^ K[i];
}
}
void decrypt(uint16_t* key, uint16_t* message) {
uint16_t sections[4] = { 0x0 }; // use eeprom
for ( i = 0; i < 4; i ++) {
*message ^= *key ^ K[i];
sections[0] = *message & M[i][0];
sections[1] = *message & M[i][1];
sections[2] = *message & M[i][2];
sections[3] = *message & M[i][3];
*message = sections[3] | sections[2] | sections[1] | sections[0];
}
}
It should be kept in mind:
- Different key must be used for every frame
- Noise generator and built-in ADC can be utilized to continously generate new keys
- Key exchange between transmitter and receiver is necessary and should be done often, preferably on random intervals
- Adding a token/nonce to your frames is recommended