Pseudo Code

 

Pseudo Code #

This C-like pseudo code explains the method and is optimized for readability only (and not for speed).

These two parameters control the size of the random input material (and thus the overall amount of input entropy):

  • SelectorBytes: The size or the width of the selector address in bytes
  • PadBytes: The size or the width of the individual input pads

The Random Source Material and its Checksum #

  • The input random material is basically organized as a three-dimensional array of bytes.
  • Each individual byte within this material contains 8 completely uncorrelated, individual random bits.
  • The sha256 32 byte field contains the SHA256 checksum of the material data.
1
2
3
4
5
#define SelectorBytes 32
#define PadBytes 8192

uint8_t material[SelectorBytes][256][PadBytes];
uint8_t material_sha256[32];

Choosing the Selector Address #

  • The AXPadChooseSelector() function obtains SelectorBytes of random bytes and stores this at the passed uint8_t* location.
  • This fragment assumes that random_fd is a file descriptor that has been opened for reading from /dev/urandom.
  • Alternatively (and better), a new selector address can also be easily retrieved by the AXPad method itself starting at an unique random initial seed position.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
AXPadChooseSelector(
  uint8_t* selector
) {
  int bytes_read;

  bytes_read = read(random_fd, selector, SelectorBytes);
  if(bytes_read != SelectorBytes) {
    // error handling
  }
}

Retrieving one Byte from an addressed combined Pad #

  • The AXPadSelectorBytes() function gets a selector address of length SelectorBytes as its input, combines everything and
  • returns the resulting byte at the specified position.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
uint8_t AXPadGetbyte(
  uint8_t* selector,
  int position
) {
  uint8_t byte;
  int i, j;

  byte = 0;

  for (i = 0; i < SelectorBytes; i++) {
    byte = byte ^ material[i][selector[i]][position];
  }

  return(byte);
}

Encryption #

  • The encryption chooses a random selector address with AXPadChooseSelector(),
  • passes this selector address implicitly to the caller (note that the highlighted line number 9 makes the difference between encryption and decryption),
  • performs a bytewise XOR with the selected pad and changes the plaintext to ciphertext in place.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
AXPadEncrypt(
  uint8_t* text,
  int length, 
  uint8_t* selector
) {
  int i;

  AXPadChooseSelector(xp, selector);

  for(i = 0; i < length; i++) {
    text[i] = text[i] ^ AXPadGetbyte(selector, i);
  }
}

Decryption #

  • The decryption gets the selector address as an input parameter instead and
  • performs the same bytewise XOR with this pad thus reverting the ciphertext to plaintext in place.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
AXPadDecrypt(
  uint8_t* text,
  int length, 
  uint8_t* selector
) {
  int i;

  for(i = 0; i < length; i++) {
    text[i] = text[i] ^ AXPadGetbyte(selector, i);
  }
}

The Authentication Header #

Each encrypted message is prepended by an authentication header which is generated after encryption ("encrypt-then-MAC"). A valid authentication header contains the following elements:

  • hmac[32]: The SHA256 hash of the selector[], the timestamp[], the message_length[], the sha256[] checksum of the material and the message itself.
  • selector[SelectorBytes]: The selector address of the pad the encryption has chosen.
  • timestamp[8]: The 64 bit timestamp in network byte order.
  • sequence_number[4]: A sequence number, 32 bit unsigned in network byte order.
  • message_length[4]: The length of the associated message itself, unsigned and in network byte order.
1
2
3
4
5
6
7
typedef struct {
  uint8_t hmac[32];
  uint8_t selector[SelectorBytes];
  uint8_t timestamp[8];
  uint8_t sequence_number[4];
  uint8_t message_length[4];
} AXPadHeader;

Partial Header Obfuscation #

If we are looking at the bytestream consisting of alternating headers and encrypted payload data everything has perfect random properties except the 16 header bytes timestamp[8] + sequence_number[4] + message_length[4].

To mitigate this, these 16 bytes are encrypted with the first 16 bytes of the pad at the inverted selector address as contained in the header itself. As a result, the complete bytestream of consecutive header/payload ciphertext data is random and the existence of a material that could successfully decrypt cannot be proven.