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)
$