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);
}
}
|
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;
|
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.