A guided Tour

 

A guided Tour #

This tour takes you through all practical application aspects of axpad, it’s basically about how to use the program for its main purpose: Encryption and decryption of regular files.

Not covered here is the following:

  • The random number generation mode of axpad,
  • the “random material management” (where to store it, how to exchange it)
  • and security aspects outside the program itself (like machines, operating systems, people and any external procedures that are in charge).

About licensing #

axpad without a license key (trial mode) is not restricted in functionality or speed, the differences without licensing are as follows:

  • A warning message is displayed on stderr when started,
  • A specific build has a fixed expiration date.

When you are evaluating axpad without licensing, just make sure that you have the latest release installed (the unlicensed lifetime of a build is currently 90 days starting from production).

Obtaining your first random source material #

axpad requires 256MB of random source material, to get immediately started this can be generated by collecting exactly this amount of data from /dev/urandom with dd as shown below.

We’ll revisit this topic later explaining better possibilities and combinations. Just for getting started locally, this is acceptable.

$ dd if=/dev/urandom of=material.random bs=1024 count=262144
262144+0 records in
262144+0 records out
268435456 bytes transferred in 2.803369 secs (95754592 bytes/sec)
$

Signing the material file #

A random material file needs to be self-signed by axpad, this does the following:

  • first, the built-in chi squared distribution test is performed to make sure that the material does appear to be basically random,
  • if this test is passed, a SHA256 signature is appended which hashes the material contents and an internal signature secret.

Given the material.random file from the prior step this looks as follows:

$ ls -l material.random
-rw-r--r--  1 test  staff  268435456 Jan  9 16:17 material.random
$ axpad sign -m material.random
$ ls -l material.random
-rw-r--r--  1 test  staff  268435488 Jan  9 16:18 material.random
$

As you can see, the size of the file has increased by 32 bytes now containing its signature at the end.

Encrypting a single file #

Now you are ready to encrypt for the first time. Assume you have a small text file like this test.txt (of course it can be anything else):

$ cat test.txt
Hello, AXPad!
$

The encryption with -i and -o arguments looks like this:

$ axpad encrypt -m material.random -i test.txt -o test.txt.axp
$

Alternatively, using stdin and stdout this does the same:

$ cat test.txt | axpad encrypt -m material.random > test.txt.axp
$

The .axp extension is not mandatory here. The material initialisation takes a few seconds.

Decrypting a single file #

Reverting an encryption of a single file works like this:

$ axpad decrypt -m material.random -i test.txt.axp -o test.txt
$

Or, alternatively:

$ cat test.txt.axp | axpad decrypt -m material.random > test.txt
$

Note in the latter case an already existing test.txt will be overwritten by the shell, when specified with -o, this will be done only if the option --overwrite is specified. In case of any error (authentication failure or anything else) the contents of the resulting file are unspecified.

A closer look at the encryption results #

Let’s encrypt the same plaintext consisting of 16 zero bytes twice (taken from /dev/zero):

The first time:

$ dd if=/dev/zero bs=16 count=1 | axpad encrypt -m material.random | hexdump -C
1+0 records in
1+0 records out
16 bytes transferred in 0.000016 secs (1000000 bytes/sec)
00000000  80 26 3d 80 53 a2 23 ae  5c 50 50 9d 1f 16 f1 a2  |.&=.S.#.\PP.....|
00000010  34 18 92 d7 be af 17 7e  1f 7f 7b 8d 63 52 c7 4a  |4......~..{.cR.J|
00000020  82 98 f1 26 85 aa 9c ae  a0 93 bf b9 ad 48 3f 45  |...&.........H?E|
00000030  1e 70 5f 1d 76 13 f1 44  14 8c e4 a2 63 52 2a af  |.p_.v..D....cR*.|
00000040  fb e2 d1 e2 b2 40 75 3f  fe 32 40 40 db ad 3b 10  |.....@u?.2@@..;.|
00000050  b9 b4 af 98 17 af cb 5b  04 8f 1e 67 05 87 b8 0a  |.......[...g....|
00000060  96 81 8b d6 a2 21 8c 21  df 29 71 2c 6c 74 a0 08  |.....!.!.)q,lt..|
00000070  69 c2 fa 38 28 fd 0a 29  ab cb de da 14 e3 42 1d  |i..8(..)......B.|
00000080
$

And a second time:

$ dd if=/dev/zero bs=16 count=1 | axpad encrypt -m material.random | hexdump -C
1+0 records in
1+0 records out
16 bytes transferred in 0.000016 secs (1000000 bytes/sec)
00000000  68 ea eb 43 50 5e ac 6a  68 9a cb 9d e2 0d 1d db  |h..CP^.jh.......|
00000010  78 4a cc 44 71 55 ef 70  79 f7 c2 12 ae 85 e6 96  |xJ.DqU.py.......|
00000020  e5 bd 12 9e 85 bd d4 dc  4b 0e d4 06 47 37 4c 52  |........K...G7LR|
00000030  ac 21 dc 52 b4 80 4b 9c  ba d8 c8 6d 66 2f 1a fd  |.!.R..K....mf/..|
00000040  d0 f2 89 d5 1a cf a5 1e  87 88 32 3b 26 46 29 c6  |..........2;&F).|
00000050  f1 62 be 53 1b b5 d9 59  38 7a 07 87 8a 54 88 ca  |.b.S...Y8z...T..|
00000060  b5 cc d3 0e 01 8e 53 d3  5b 9e 0e 49 c1 82 0c fe  |......S.[..I....|
00000070  02 27 18 4f 3c 53 19 0f  0a e8 2b 34 91 ee 28 bf  |.'.O<S....+4..(.|
00000080
$

As you can see, both ciphertext results are completely different including the 16 byte encrypted payload in line 00000070. Lines 00000000-00000010 contain the header with a fixed size of 112 bytes.

However, there’s an extremely small probability that exactly two consecutive encryptions like this actually result to the same. With a selector address of 512 bits that axpad chooses randomly the probability is this:

\[p = \frac{1}{2^{512}2^{512}}\]

For an arbitrary number of encryptions the “birthday paradox” formula results in a collision resistency of \(2^{256}\) - meaning that there’s a probability of \(0.5\) to find two identical randomly chosen selector addresses in \(2^{256}\) encryption events.

Summary:

  • The encryption with axpad of the same plaintext block and with the same material results in a different authenticated ciphertext block with a probability of practically 1.
  • The encrypted files are getting larger by about 0.7% (inserting a header of 112 bytes every 16KB).

Working with passphrases #

axpad allows to modify the entire internal random material with a passphrase, a string that may contain up to 128 characters. From that string the SHA256 hash is computed and then, during initialization, the result is mapped input-pad-wise with the original pad contents in memory. This allows to add a second, but substantially weaker secret of “just” 256 bit (which usually would be considered as absolutely ok).

In fact, a passphrase adds a second factor: A user has to have the knowledge of the passphrase in addition to the possession of the correct random material to be able to decrypt a specific authenticated ciphertext (file).

There are two modes, the first one is entering it interactively by specifying --passphrase on the command line. The second one is specifying the passphrase as an argument. e.g. --passhphrase="my secret passphrase". You have just to be aware, that the passphrasse passed as an argument is visible in the process memory and that it is also usually included in the shell history. This may be absolutely acceptable, or absolutely fatal - depending on your specific use case.

If a passphrase is requested interactively, it has to entered twice (with verification) for any encryption functionality, but only once when decrypting. All memory areas are immediately cleared after the SHA256 result has been computed in that case.

Here’s an example to request the interactive input of a passphrase for encrypting a single file:

$ axpad -m material.random encrypt -i test.txt -o test.txt.axp --passphrase
passphrase:
retype passphrase:
$

Let’s decrypt test.txt.axp with the passphrase previously supplied interactively now as an argument, this looks like this:

$ axpad -m material.random decrypt -i test.txt.axp -o result.txt --passphrase="test"
$

Note, that the passphrase isn’t stored anywhere, it’s just the internal material that is modified with its SHA256 hash. Consequently, trying to decrypt with a wrong passphrase (but the right material) just fails as decrypting with the wrong material would do. This looks as follows:

$ axpad -m material.random decrypt -i test.txt.axp -o result.txt --passphrase="test2"
[ERR] authentication failed
[ERR] decryption failed
$

Encrypting multiple files #

The material initialization that axpad performs at startup takes some time (in the range of seconds). When encrypting multiple files, it’s possible to do this with axpad encrypt-files instead, which requires only one single initial material initialization.

For this, a simple naming scheme is introduced: An .axp extension which is appended to the original filename.

Let’s assume you wish to encrypt multiple .pdf files in your current directory and your material.random sits in your home directory. axpad encrypt-files accepts a list of files to encrypt on stdin, so a dialog may look like this:

$ ls *.pdf
report1.pdf report2.pdf report3.pdf
$ ls *.pdf | axpad encrypt-files -m $HOME/material.random
$ ls *.axp
report1.pdf.axp report2.pdf.axp report3.pdf.axp
$ ls *.pdf
report1.pdf report2.pdf report3.pdf
$

Adding the --unlink option here removes the original file after each single successful encryption (here using find instead of ls to traverse also any subdirectories):

$ ls *.pdf
report1.pdf report2.pdf report3.pdf
$ find . -name "*.pdf" | axpad encrypt-files -m $HOME/material.random --unlink
$ ls *.axp
report1.pdf.axp report2.pdf.axp report3.pdf.axp
$ ls *.pdf
$

Obviously, the following conditions must be fullfilled from a file system perspective:

  • There must be sufficient space and
  • write permissions are required.

Decrypting multiple files #

The invocation axpad decrypt-files allows to reverse this process by removing the .axp extension. This looks like this:

$ ls *.axp
report1.pdf.axp report2.pdf.axp report3.pdf.axp
$ ls *.axp | axpad decrypt-files -m $HOME/material.random
$ ls *.pdf
report1.pdf report2.pdf report3.pdf
$

And with the --unlink option:

$ ls *.axp
report1.pdf.axp report2.pdf.axp report3.pdf.axp
$ ls *.axp | axpad decrypt-files -m $HOME/material.random --unlink
$ ls *.pdf
report1.pdf report2.pdf report3.pdf
$ *.axp
ls: *.axp: No such file or directory
$

Note the following for axpad decrypt-files (in addition to write permissions and available space):

  • The result files without the .axp extension are created, but only truncated if they already exist with the --overwrite option
  • it’s an error passing a filename without an .axp extension to axpad decrypt-files
  • if any error occurs, the process stops at exactly this point

Obtaining a perfect true random material #

If you don’t have a True Random Number Generator (TRNG) available, you can still obtain your personal and perfect true random material by following these steps:

Download a “starting” material #

Just pick one of these pre-prepared true random data files (exactly which one doesn’t matter):

https://download.inlab.net/TrueRandomMaterial/256MB/

All these files have been generated with a Quantis QTRNG device.

Mix it with /dev/urandom #

Let’s assume you have chosen materialF.random and have verified the SHA256 checksum as in https://download.inlab.net/TrueRandomMaterial/256MB/sha256sum.txt.

Doing that twice looks as follows (including appending the signature and removing the intermediate files, on a Mac you use shasum -a 256 and on Linux sha256sum):

$ ls materialF.random
materialF.random
$ shasum -a 256 materialF.random
0c5df2a78490baee38227e8b9372fc2193bef7ffcf45ce6e7f226130ab92e811  materialF.random
$ axpad xor -i /dev/urandom -m materialF.random -o materialF1.random
$ axpad xor -i /dev/urandom -m materialF1.random -o my-material.random
$ axpad sign -m my-material.random
$ rm materialF.random
$ materialF1.random

Your file my-material.random is ready for production use with axpad.

Why does this work? It’s this: The result of an XOR operation of a random variable with anything inherits the random properties of the random variable.

Taking the material from a character device #

axpad allows to retrieve its random material from a character device without requiring a signature (for testing purposes). The built-in chi squared test is performed as well - but when it fails, the operation just continues after a warning message has been displayed on stderr.

/dev/urandom #

The following invocation comsumes 32 bytes from /dev/urandom. The random material also taken from there and encrypts these 32 bytes. The original 32 bytes are impossible to recover without the random material, which is also lost:

$ dd if=/dev/urandom bs=32 count=1 | axpad encrypt -m /dev/urandom | hexdump -C
1+0 records in
1+0 records out
32 bytes transferred in 0.000016 secs (2000000 bytes/sec)
00000000  97 52 d0 17 a2 6f 2a d2  7f e0 ef 62 7b 1a dc 99  |.R...o*....b{...|
00000010  91 93 5d a9 39 10 48 82  d4 10 24 35 b6 08 c2 e5  |..].9.H...$5....|
00000020  f3 49 16 d9 42 42 bf 27  a8 7a bb 0d a4 26 a6 cf  |.I..BB.'.z...&..|
00000030  cc 55 06 aa 40 02 84 97  9d 4a 61 cd de 82 d8 93  |.U..@....Ja.....|
00000040  d4 ec f1 01 f9 3f 24 49  85 77 e9 66 53 33 1f a9  |.....?$I.w.fS3..|
00000050  6c f9 8d 08 a2 2e 4c 06  65 1e 60 55 5d 13 be 2e  |l.....L.e.`U]...|
00000060  65 9c cd 66 d3 9d 80 9e  e4 9c 93 da b9 96 c1 87  |e..f............|
00000070  88 41 2f 3e df f0 93 a4  41 57 ac 0c 21 b7 3b 9f  |.A/>....AW..!.;.|
00000080  12 4c 7e d9 91 49 b5 a9  1e 76 58 95 75 c3 a9 cc  |.L~..I...vX.u...|
00000090
$

Trying to decrypt something from /dev/urandom with a material also from there fails like this (let’s provide 512 bytes to give room for a 112 byte header):

$ dd if=/dev/urandom count=1 | axpad decrypt -m /dev/urandom | hexdump -C
1+0 records in
1+0 records out
512 bytes transferred in 0.000020 secs (25600000 bytes/sec)
[ERR] invalid message length
[ERR] decryption failed
$

/dev/zero #

/dev/zero is a character special device which provides bytes of all zeros indefinitely. Using this as a material - ignoring the warning, consequently leaves the plaintext completely untouched (and the selector taken from the material contains also just all-zero bytes):

$ echo "This is a test." | axpad encrypt -m /dev/zero | hexdump -C
[WRN] MATERIAL DOES NOT PASS BUILT-IN CHI SQUARED TEST
00000000  57 ed cb 74 45 ad 3d 80  58 a9 d5 8c 24 40 e1 72  |W..tE.=.X...$@.r|
00000010  9b 8a 4d 29 32 2f e3 1a  5e 39 94 47 1d 9e af c3  |..M)2/..^9.G....|
00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00000060  0c 67 86 5d 77 82 de 9a  06 90 41 cb 39 de 4e a1  |.g.]w.....A.9.N.|
00000070  54 68 69 73 20 69 73 20  61 20 74 65 73 74 2e 0a  |This is a test..|
00000080
$

The --urandom option tells axpad to choose the selector address from /dev/urandom. This just changes the selector bytes, the ciphertext payload part is still identical to the plaintext:

$ echo "This is a test." | axpad encrypt -m /dev/zero --urandom| hexdump -C
[WRN] MATERIAL DOES NOT PASS BUILT-IN CHI SQUARED TEST
00000000  bb e8 c2 3e e1 25 43 b4  fa 7e ca e0 4e 6f a2 b5  |...>.%C..~..No..|
00000010  ac 2b 32 11 51 2d 58 a2  6e 7f 89 dd e4 2d 82 6c  |.+2.Q-X.n....-.l|
00000020  5b df 39 83 94 81 7b 35  ec 18 38 7f ee 98 d1 30  |[.9...{5..8....0|
00000030  77 ed 4a 89 65 c4 05 e1  09 45 56 fe 9b b5 2c 33  |w.J.e....EV...,3|
00000040  5d 86 0b 8b 3a 80 99 19  f7 32 11 7e 2c c3 24 53  |]...:....2.~,.$S|
00000050  9f 61 30 6a f8 f2 b1 64  14 cb dd fa d7 69 94 69  |.a0j...d.....i.i|
00000060  12 65 1b 43 39 71 05 26  11 a1 5b 0c 24 c5 6d f0  |.e.C9q.&..[.$.m.|
00000070  54 68 69 73 20 69 73 20  61 20 74 65 73 74 2e 0a  |This is a test..|
00000080
$

Performance benchmarking #

axpad rng (the random number generation mode) is just perfect for performance benchmarking on a specific machine. Both examples here have been executed on an Apple Mac mini with an M2 processor.

Material initialization time #

Reading just one single 512 byte block with dd and discarding it gives a very good impression:

$ axpad -m /dev/urandom rng | dd count=1 > /dev/null
1+0 records in
1+0 records out
512 bytes transferred in 3.445238 secs (149 bytes/sec)
$

Combinatorical processing speed #

Waiting those 4 initialization seconds before processing 1000000 512 byte blocks then provides the raw processing speed:

$ axpad -m /dev/urandom rng | (sleep 4; dd count=1000000) > /dev/null
1000000+0 records in
1000000+0 records out
512000000 bytes transferred in 4.551920 secs (112480008 bytes/sec)
$