A Concept in Key Lock Decoding for Protocol Analysis

A Concept in Key Lock Decoding for Protocol Analysis

A Concept in Key Lock Decoding for Protocol Analysis
Credit: Tom Joseph Unsplash.com

In this last post we worked on a timing independent protocol for the RPi2040 that is interrupt based.  Here are some basic concepts.

  • The IR1838 typically works at 38.4 Khz but in fact may work up to 10 Khz or higher.
  • The RPi2040 can typically run at 125 Mhz (but maybe not)
  • There is no clock sync off of this remote and we are taking time_us_32() offsets to which themselves can also be considered inaccurate depending on the accuracy of the clock.
  • Pulse 'slop' from previous measurements were showing deviations up to 3%.
  • In effect we are trying to decode a packet where the baud clock is unreliable and only within tolerances.

We now introduce a new method of relational pattern decoding. It is applying a set of present mathematical summations of non-repeating order to develop 'time-slots' very similar to 'key-slots.'

Match the timing tumbler keys you match the packet.  The mathematical inverse of any tumbler is mute as it is only the negative value of the same. Therefore no two functions can have the inverted order of operations:

First Tumbler (+) - Defined simply as the sum duration of all packets:

Second Tumbler: (+-) - Defined as the addition sum timing of odd packets subtracted from even packets:

Third Tumbler: (++-) - Defined as the addition in pattern sets of three bits, as defined by the addition of the first two bits subtracted from the third bit repeating as in:

Fourth Tumbler: (+-+) Defined as the addition of sets 1,3,4,6 with 2,5,8..

Tumbler N: (..n) Defined as the extension of these equations to the nth degree as required.

The concept is simple, by structured order of preset operations we have defined a four-segment hash (or N sized hash.)  We simply then check that the four segments match within the percentage defined by a reference reading and it should mathematically 'fingerprint' the packet according to bit pattern even when the timing clocks are off reference.

  • This is expected to only work on static packets.
  • It is computationally very conservative.
  • It can be determined by random string as in '+_+____++_+_+_' that random N hashes can be quickly defined.

Let's code up an algorithm to test how well this can 'learn' signals. We will start with the above 4-segment hash function to taste:

We needed to define what the largest packet sent would look as, in relation to the dead space around it, we modified the code as:

#include <stdio.h>
#include <math.h>
#include "pico/stdlib.h"
#include "hardware/gpio.h"

static uint32_t time_a = 0;
static uint32_t time_b = 0;
static uint32_t differential = 0;
int is_recording = 0;
#define NEWLINE 600000
#define MIN_US 500
#define MAX_US 15000
#define PULSE_SPREAD 1.03
#define PULSE_MIN 528
#define PULSE_MEAN 545
#define PULSE_MAX 570
#define MAX_PULSE_SEQ 50

void gpio_callback(uint gpio, uint32_t events)
{
  uint32_t rnow = time_us_32();
  uint32_t dif = rnow - time_a;
  printf("GPIO 0 Activated: %d Elapsed: %d\n", rnow, dif);
  time_a = rnow;
}

int main()
{
    stdio_init_all();
    printf("Startup!\n");
    const uint LED_PIN = PICO_DEFAULT_LED_PIN;
    gpio_init(LED_PIN);
    gpio_set_dir(LED_PIN, GPIO_OUT);

    gpio_init(0);
    gpio_set_dir(0, GPIO_IN);
    gpio_init(1);
    gpio_set_dir(1, GPIO_IN);

    gpio_set_irq_enabled_with_callback(0, GPIO_IRQ_EDGE_RISE | GPIO_IRQ_EDGE_FALL, true, &gpio_callback);
    //gpio_set_irq_enabled(1, GPIO_IRQ_EDGE_RISE | GPIO_IRQ_EDGE_FALL, true);
    //gpio_set_irq_enabled_with_callback(1, GPIO_IRQ_EDGE_RISE | GPIO_IRQ_EDGE_FALL, true, &gpio_callback);
    while (true)
    {
      sleep_ms(100);
      printf("Test push\n");
        //printf("Test push\n");
    }
}

The CMakeLists.txt is here if you need it.

We now get output that looks as:

GPIO 0 Activated: 43100837 Elapsed: 33114875

GPIO 0 Activated: 43109883 Elapsed: 9046

Test push

GPIO 0 Activated: 43114354 Elapsed: 4471

GPIO 0 Activated: 43114907 Elapsed: 553

GPIO 0 Activated: 43115484 Elapsed: 577

GPIO 0 Activated: 43116035 Elapsed: 551

GPIO 0 Activated: 43116614 Elapsed: 579

When we actually build this back into the logic analyzer it's timings illustrate as:

  • 9046 / 553 = 16.3 (non-symmetric)
  • After looking at some packets it is clear that anything that is above 10,000 uS is dead space between packets and this can now set our demarcation for a next set of packets.

The four tumbler code can be defined as:

/**
 * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */
#include <stdio.h>
#include <math.h>
#include "pico/stdlib.h"
#include "hardware/gpio.h"

#define max_pulse 10000

uint32_t time_a = 0;
uint32_t hash_a = 0;
uint32_t hash_b = 0;
uint32_t hash_c = 0;
uint32_t hash_d = 0;
int cmode = 0;

void gpio_callback(uint gpio, uint32_t events)
{
  uint32_t rnow = time_us_32();
  uint32_t dif = rnow - time_a;
  //printf("GPIO 0 Activated: %d Elapsed: %d\n", rnow, dif);
  if ((dif > max_pulse) & (hash_a > 0))
  {
    // This is the reset. If hash bins hold values dump them.
    // We only really need check the first against operation of all :.
    printf("Hash a: %d b: %d c: %d d: %d\n", hash_a, hash_b, hash_c, hash_d);
    hash_a = 0;
    hash_b = 0;
    hash_c = 0;
    hash_d = 0;  // reset for next packet.
  }
  // cmode will define the positional order  in the ++ ++- -+- sets
  if (cmode == 0)
  {
    // hash_a defined as First Tumbler +
    hash_a += dif;
    // hash_b defined as Second Tumbler +- + on 0 - on 1
    hash_b += dif;
    // hash_c defined as Third Tumbler ++-  + on 0, + on 1 - on 2
    hash_c += dif;
    // hash_d defined as Fourth Tumbler +-+ + on 0, - on 1 + on 2
    hash_d += dif;
  }
  if (cmode == 1)
  {
    // hash_a defined as First Tumbler +
    hash_a += dif;
    // hash_b defined as Second Tumbler +- + on 0 - on 1
    hash_b -= dif;
    // hash_c defined as Third Tumbler ++- + on 0 + on 1 - on 2
    hash_c += dif;
    // hash_d defined as Fourth Tumbler +-+ + on 0 - on 1 + on 2
    hash_d -= dif;
  }
  if (cmode == 2)
  {
    // hash_a defined as First Tumbler +
    hash_a += dif;
    // hash_b defined as Second Tumbler +- BASE 2 thus +
    hash_b += dif;
    // hash_c defined as Third Tumbler ++- on 0 + on 1 - on 2
    hash_c -= dif;
    // hash_d defined as Fourth Tumbler +-+
    hash_d += dif;
  }
  cmode++;
   if (cmode > 2)
     cmode = 0;

  // Shift time bit down
  time_a = rnow;

}

int main()
{
    stdio_init_all();
    printf("Startup!\n");
    const uint LED_PIN = PICO_DEFAULT_LED_PIN;
    gpio_init(LED_PIN);
    gpio_set_dir(LED_PIN, GPIO_OUT);

    gpio_init(0);
    gpio_set_dir(0, GPIO_IN);
    gpio_init(1);
    gpio_set_dir(1, GPIO_IN);

    gpio_set_irq_enabled_with_callback(0, GPIO_IRQ_EDGE_RISE | GPIO_IRQ_EDGE_FALL, true, &gpio_callback);
    //gpio_set_irq_enabled(1, GPIO_IRQ_EDGE_RISE | GPIO_IRQ_EDGE_FALL, true);
    //gpio_set_irq_enabled_with_callback(1, GPIO_IRQ_EDGE_RISE | GPIO_IRQ_EDGE_FALL, true, &gpio_callback);
    while (true)
    {
      sleep_ms(100);
      //printf("Test push\n");
        //printf("Test push\n");
    }
}

The result from this did not give results as expected due to very high packet slew rates.

Linux Rocks Every Day