Using ext2 files and GPG
- learn how to build
ext2
file-systems and use them in a Cartesi Machine - create test data and run a Cartesi Machine that uses GPG to verify a document signature
Test data
Before jumping into building a Cartesi Machine using GPG, it is a good idea to first generate some test data. This way, we will be able to effectively run a machine and understand how GPG can be used in it.
First of all, let's create our fictional document inside the gpg-verify/cartesi-machine
directory :
echo "My public statement" > document
An appropriate signature for that document has been created using the tutorial's private key. That signature can be directly downloaded from Github by typing:
wget https://github.com/cartesi/compute-tutorials/raw/master/gpg-verify/cartesi-machine/signature
Alternatively, you can use the tutorial's private key and sign it yourself using the GnuPG gpg
tool (the private key's passphrase is "Descartes rocks!").
Although not necessary, it may be fun to use the GnuPG tool to play around with your own keypairs and document signatures. If you do not already have it installed, you can do that in an Ubuntu distribution by typing:
sudo apt-get update
sudo apt-get install gnupg
You can then download and import the private key, and create a detached document signature by executing the following commands:
wget https://github.com/cartesi/compute-tutorials/raw/master/gpg-verify/cartesi-machine/compute-private.key
gpg --import compute-private.key
gpg --detach-sig -u descartes --output signature document
Please refer to the GnuPG manual for more information on how to use gpg
, such as creating your own keypairs.
As a final touch, let's also produce a "tampered" version of our document, so that we can check if we are able to detect a "fraud":
echo "My public statement was tampered\!" > document-tampered
Building an ext2
file-system
As thoroughly discussed in the Cartesi Machine host perspective section, ext2
is a file-system format that can be easily created and manipulated with command-line utilities available in Linux distributions. It is the preferred file-system type used in Cartesi Machines, and we have actually already used it when building our custom root file-system for the Generic Script tutorial.
In the context of this project, we will thus build an ext2
file that contains our public key, so as to make it available to our GPG signature verification Cartesi Machine. We can also conveniently include other pre-defined resources in this file-system, such as the test data we have just created.
To create this file, we'll take advantage of the cartesi/playground
Docker image, which already contains the necessary tools. As such, run the playground mapping the current directory by typing:
docker run -it --rm \
-e USER=$(id -u -n) \
-e GROUP=$(id -g -n) \
-e UID=$(id -u) \
-e GID=$(id -g) \
-v `pwd`:/home/$(id -u -n) \
-w /home/$(id -u -n) \
cartesi/playground:0.5.0 /bin/bash
Inside the playground, let's first create a directory with the files that should be included in the ext2
file-system (i.e., the public key and the test data):
mkdir ext2-test
cp compute-pub.key document document-tampered signature ext2-test/
Next, use the genext2fs
tool to create the ext2
file with the contents of that directory:
genext2fs -b 1024 -d ext2-test dapp-data-test.ext2
After the file is created, you can actually inspect its contents by running the following command, also available inside the playground:
e2ls dapp-data-test.ext2
Cartesi Machine with GPG
With the file-system ready, we can finally build and run a Cartesi Machine that uses GPG to verify the signature of our test data.
Still inside the playground, execute the following command:
cartesi-machine \
--append-rom-bootargs="single=yes" \
--flash-drive="label:dapp-data,filename:dapp-data-test.ext2" \
-- $'date -s \'2100-01-01\' && gpg --trusted-key 0xA86D9CB964EB527E --import /mnt/dapp-data/compute-pub.key && gpg --verify /mnt/dapp-data/signature /mnt/dapp-data/document ; echo $?'
Let's go over this in detail. To begin with, the flash-drive
argument specifies that the dapp-data-test.ext2
file-system we just created should be mounted as /mnt/dapp-data
. After that, the command line to be executed consists of a sequence of four instructions.
First, we use the Linux date
command to set the system clock to a value far in the future. This is needed due to the inconvenience that, for security reasons, the GPG tool rejects signatures whose creation timestamp is beyond the current system time. On the other hand, in order to be reproducible, the Cartesi Machine cannot have different conditions on each execution, and thus it has to always start with a fixed system clock value, which by default is set to timestamp 0
(1970-01-01 UTC). Therefore, using date
to set the clock to such a future value ensures that the machine will always accept any newly created signatures.
After setting the date, the ensuing gpg
command imports the compute-pub.key
file as a public key, and uses the key's LONG 64-bit identifier 0xA86D9CB964EB527E
to inform that it can be trusted (if you have gpg
installed, you can check out the key's LONG id by running gpg --keyid-format LONG compute-pub.key
). Then, we use gpg
again to perform the actual signature verification. Finally, the last echo
command just prints the final exit status to the console.
The output of running this machine is the following:
.
/ \
/ \
\---/---\ /----\
\ X \
\----/ \---/---\
\ / CARTESI
\ / MACHINE
'
Fri Jan 1 00:00:00 UTC 2100
gpg: directory '//.gnupg' created
gpg: keybox '//.gnupg/pubring.kbx' created
gpg: //.gnupg/trustdb.gpg: trustdb created
gpg: key A86D9CB964EB527E marked as ultimately trusted
gpg: key A86D9CB964EB527E: public key "Descartes Tutorials <descartes.tutorials@cartesi.io>" imported
gpg: Total number processed: 1
gpg: imported: 1
gpg: marginals needed: 3 completes needed: 1 trust model: pgp
gpg: depth: 0 valid: 1 signed: 0 trust: 0-, 0q, 0n, 0m, 0f, 1u
gpg: Signature made Thu Jul 23 20:59:47 2020 UTC
gpg: using RSA key DBBBB50DDC0910795F7C0B48A86D9CB964EB527E
gpg: issuer "descartes.tutorials@cartesi.io"
gpg: Good signature from "Descartes Tutorials <descartes.tutorials@cartesi.io>" [ultimate]
0
Halted
Cycles: 99863436
As we can see, the output confirms that the date was set, the public key was marked as trusted and imported, and finally the signature was considered valid for the given document. The final 0
value printed just before halting the machine is the exit status for the gpg
signature verification command, signaling that it has reported success.
We can then run the same machine for our document-tampered
version:
cartesi-machine \
--append-rom-bootargs="single=yes" \
--flash-drive="label:dapp-data,filename:dapp-data-test.ext2" \
-- $'date -s \'2100-01-01\' && gpg --trusted-key 0xA86D9CB964EB527E --import /mnt/dapp-data/compute-pub.key && gpg --verify /mnt/dapp-data/signature /mnt/dapp-data/document-tampered ; echo $?'
Which will yield the following output:
.
/ \
/ \
\---/---\ /----\
\ X \
\----/ \---/---\
\ / CARTESI
\ / MACHINE
'
Fri Jan 1 00:00:00 UTC 2100
gpg: directory '//.gnupg' created
gpg: keybox '//.gnupg/pubring.kbx' created
gpg: //.gnupg/trustdb.gpg: trustdb created
gpg: key A86D9CB964EB527E marked as ultimately trusted
gpg: key A86D9CB964EB527E: public key "Descartes Tutorials <descartes.tutorials@cartesi.io>" imported
gpg: Total number processed: 1
gpg: imported: 1
gpg: marginals needed: 3 completes needed: 1 trust model: pgp
gpg: depth: 0 valid: 1 signed: 0 trust: 0-, 0q, 0n, 0m, 0f, 1u
gpg: Signature made Thu Jul 23 20:59:47 2020 UTC
gpg: using RSA key DBBBB50DDC0910795F7C0B48A86D9CB964EB527E
gpg: issuer "descartes.tutorials@cartesi.io"
gpg: BAD signature from "Descartes Tutorials <descartes.tutorials@cartesi.io>" [ultimate]
1
Halted
Cycles: 99260741
This informs us that the signature is now invalid for the given document, and that the reported exit status is now 1
, indicating failure. We are now indeed capable of verifying document signatures with a Cartesi Machine!
Finally, you can exit the playground by typing:
exit