Signal Detective challenge

In this challenge, each student receives a pre-programmed Arduino Nano which periodically (about once every second) outputs a unique encrypted 4-character message on pin D2. Each of the four characters can be a letter or digit, upper or lower case. The student’s task is to decrypt their unique 4-character code. The data bits of each transmitted byte have been encrypted by XORing them with a pseudorandom binary sequence (PRBS). Hence, although the same original 4-character code is transmitted each time, the actual bits transmitted from D2 change each time depending on the corresponding PRBS bits. The Arduino outputs the encrypting PRBS signal on pin D3. To retrieve the original bits, the student must XOR each transmitted data bit with the simultaneously transmitted PRBS bit.

In the oscilloscope screenshot shown below both the data and PRBS signals are visible. You can clearly see the periodic transmissions in the data signal (yellow). The PRBS signal (blue) transmits continuously, irrespective of whether or not data is being transmitted..

Some important points about the signals:

  • Voltage HIGH is 0, voltage LOW is 1.
  • Each byte is preceded by a start bit of 1 (voltage LOW) and followed by a stop bit of 0 (voltage HIGH) – i.e. 10 bits in total per character.
  • The start and stop bits of each byte are not XORed with the PRBS, so they always retain their normal values.
  • Data bytes are transmitted least significant bit (LSB) first.
  • Each data bit in each transmitted byte has been XORed with the PRBS bit that’s transmitted at the same time as it. To get back the original bits, XOR the data and PRBS bits again.
  • Between transmissions, the data signal is 0 (voltage HIGH).

In the oscilloscope screenshot below I’ve zoomed in so that you can see the individual bits clearly, allowing the example 4-character code to be decrypted. When this screenshot was captured, I had programmed the Arduino with the secret code “S7h3”. During the actual challenge, each student is assigned a unique 4-character code. These unique codes are known to the instructor, but not to the student (until he/she correctly decrypts the signal).

Below, I’ve pasted in my calculation to retrieve the original code from the signals displayed on the oscilloscope in the above screenshot.

  1 11111010 0 (10 data bits, including start and stop bits)
    00110000   (PRBS signal during 8 data bits)
    11001010   (XOR the data and PRBS bits to retrieve the original bits)
    01010011   (reverse the bit order because LSB is transmitted first)
    'S'        (look up ASCII table to identify transmitted character)

  1 10101110 0 (data bits)
    01000010   (prbs bits)
    11101100   (XORed bits)
    00110111   (reversed bits)
    '7'        (transmitted character)
   
  1 00010000 0 (data bits)
    00000110   (prbs bits)
    00010110   (XORed bits)
    01101000   (reversed bits)
    'h'        (transmitted character)

  1 00111100 0 (data bits)
    11110000   (prbs bits)
    11001100   (XORed bits)
    00110011   (reversed bits)
    '3'        (transmitted character)

Secret code: "S7h3"

The Arduino code that generates the signals is shown below:

//
// Signal Detective secret code
// Ted Burke - 8/4/2019
//

#define US_PER_BIT 1000
#define BITS_PER_GAP 1000
#define PRBS_PIN 3
#define DATA_PIN 2
char code[] = "S7h3";

void setup()
{
  pinMode(PRBS_PIN, OUTPUT);
  pinMode(DATA_PIN, OUTPUT);
  digitalWrite(PRBS_PIN, HIGH);
  digitalWrite(DATA_PIN, HIGH);
}

void loop()
{
  int n;

  // Send the four characters message
  for (n=0 ; n<4 ; ++n) send_byte(code[n]);

  // Leave a gap before next data transmission,
  // but continue generating PRBS output
  for (n=0 ; n<BITS_PER_GAP ; ++n) send_bit(0);
}

unsigned int prbs_reg = 1;
unsigned int prbs_bit;

void update_prbs()
{
  // Calculate next bit in PRBS and update the shift register
  prbs_bit = (((prbs_reg >> 14) ^ (prbs_reg >> 13)) & 1);
  prbs_reg = ((prbs_reg << 1) | prbs_bit) & 0x7fff;
}

void send_bit(int b)
{
  // Send one bit on data and PRBS pins
  digitalWrite(PRBS_PIN, 1 - prbs_bit);
  digitalWrite(DATA_PIN, 1 - b);
  delayMicroseconds(US_PER_BIT);
  update_prbs();
}

void send_byte(char c)
{
  // Send one byte (i.e. one character) on data and PRBS pins
  int n;
  send_bit(1); // start bit
  for (n=0 ; n<8 ; ++n) send_bit(((c >> n) & 1) ^ prbs_bit);
  send_bit(0); // stop bit  
}

Leave a comment