In this exercise you will learn how to implement basic crypto operations on constrained resources. We will use the same setup as we did for lab exercise 1. Ubuntu 18.04. (VM) with RIOT-OS and the IoT-Lab. The node is – again – IoT-Lab’s m3-node.
Group members: <Anna Kastlunger, Gaelle Knibbeler, Judyta Krzyzak>
RIOT Documentation Link:
https://riot-os.org/api/index.html
Describe the top 5 of the OWASP Top 10 IoT Vulnerabilities thoroughly (in your own words!).
Weak Passwords
Insecure Network Services
Insecure Ecosystem Interfaces
Lack of Secure Update Mechanism
Use of Insecure or Outdated Components
Download the zip files from Moodle:
Move the unzipped directories into your /myRIOT/examples/ directory and follow the steps in
https://tinyurl.com/riotoslab2
USEMODULE += shell
#include "shell.h"
char line_buf[SHELL_DEFAULT_BUFSIZE];
shell_run(NULL, line_buf, SHELL_DEFAULT_BUFSIZE);
make
make term
By default, the shell module provides the help command to list available commands. Try it. You should see that there is no available command at this stage.
/* board handler */
static int _board_handler(int argc, char **argv)
{
/* These parameters are not used, avoid a warning during build */
(void)argc;
(void)argv;
puts(RIOT_BOARD);
return 0;
}
/* cpu handler */
static int _cpu_handler(int argc, char **argv)
{
/* These parameters are not used, avoid a warning during build */
(void)argc;
(void)argv;
puts(RIOT_CPU);
return 0;
}
This is simply done by adding the following code between the function callbacks and the main function:
static const shell_command_t shell_commands[] = {
{ "board", "Print the board name", _board_handler },
{ "cpu", "Print the cpu name", _cpu_handler },
{ NULL, NULL, NULL }
};
If everything works fine create a new experiment at the IoT-Lab, build your application for the m3 node and upload it to your experiment. Run the experiment - interact with the shell to get the command output. Finally, make screenshots for your documentation.
Use the following command to create a connection to grenoble (to be able to create your own programms on RIOT):
export PATH=$PATH:/home/(your user)/gcc-arm-none-eabi-7-2018-q2-update/bin/
Now you can login to grenoble with ssh
ssh krzyzak@grenoble.iot-lab.info
On RIOT start new experiment with the m3-node
Working on Anna's machine:
Working on Anna's IoT-Lab:
Working on Judyta's IoT-Lab:
Damit wir den Exportpfad nicht jedes Mal neu eingeben müssen, schreiben wir ihn in ein Bash-Script:
nano .bashrc
export PATH=$PATH:/home/anna/gcc-arm-none-eabi-7-2018-q2-update/bin/
Speichern (Strg+X -> y)
Einspielen der Firmware für das Board mit dem wir im IoT-Lab arbeiten in den aktuellen Arbeitsordner:
cd myRIOT/examples/lab2_01_shell
make BOARD=iotlab-m3 all
In this exercise, you will write 2 RIOT shell commands to encrypt and decrypt simple messages. The algorithm will use AES 128 symmetric encryption and more precisely, this exercise will use the CTR cipher mode which is able to encrypt/decrypt messages of arbitrary sizes.
AES encryption is provided by crypto
module of RIOT and CTR cipher mode is provided by the cipher_modes
module.
Document your steps here with screenshots (IoT-Lab) and thorough explanation. Provide a README for your application (don’t forget: no prosa, etc).
Since the application is about to implement shell functions, the shell
module must be added, as well as the fmt
, crypto
and cipher_modes
modules.
Edit the file Makefile and add there the required modules to the build.
Add the inculdes corresponding to the fmt
, shell
and crypto
modules that will be used by the application:
#include "shell.h"
#include "fmt.h"
#include "crypto/ciphers.h"
#include "crypto/modes/ctr.h"
static const uint8_t key[] = {
0x23, 0xA0, 0x18, 0x53, 0xFA, 0xB3, 0x89, 0x23,
0x65, 0x89, 0x2A, 0xBC, 0x43, 0x99, 0xCC, 0x00
};
static const uint8_t ctr[] = {
0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
};
This function will return the input message in its encrypted form. The encrypted message is printed as a string of hexadecimal characters:
In the _encrypt_handler
function, call the functions that will encrypt the message:
We use a copy of the nonce buffer because it is modifed by the call to cypher_encrypt_ctr
.
In the _encrypt_handler
function, convert the encrypted message (a byte array) to its hexadecimal string representation:
In the _decrypt_handler
function, you must first convert the hexadecimal representation of the input encrypted message into a byte array:
size_t len = fmt_hex_bytes(data, argv[1]);
Then, decrypt the content of the data buffer:
The output should directly be readable, so there's no need to convert it to hexadecimal.
In the 6th step we should test the application. We did not do it, since it would be necessary to uncomment the rest of the code which is a lot of work. Now, by entering 'make' we are getting a series of errors.
We checked the errors and realized they were only about the parts of code that we didn't write yet, e.g.:
We can say that there are no errors in the lines we have already written, since the compiler checks the file chronologically.
In this exercise, you will write RIOT shell commands to sign and verify simple messages:
Add the module random
and the external package c25519
to your Makefile
For the c25519
you will need an additional header file: edsign.h
besides the common header file.
First, under the header includes, declare 2 buffers where the asymmetric keys are stored:
static uint8_t secret_key[EDSIGN_SECRET_KEY_SIZE] = { 0 };
static uint8_t public_key[EDSIGN_PUBLIC_KEY_SIZE] = { 0 };
In the _key_handler
function, call the functions that will create the new keypair:
random_bytes(secret_key, sizeof(secret_key));
ed25519_prepare(secret_key);
/* implement the derivation of the public_key here */
Then print the new keypair:
First, under the header includes, declare 2 buffers where the signatures (byte and hex representation) are stored:
static uint8_t signature[EDSIGN_SIGNATURE_SIZE] = { 0 };
static char signature_hex[EDSIGN_SIGNATURE_SIZE * 2 + 1] = { 0 };
Note: the size of the hexadecimal string signature buffer is twice as large as the signature buffer itself, because a single byte is represented by 2 hexadecimal characters.
In the _sign_handler
function, call the functions that will generate the signature of the input command argument message in argv[1]:
edsign_sign(signature, public_key, secret_key, (uint8_t *)argv[1], strlen(argv[1]));
Implement the conversion of the signature (a byte array) to its hexadecimal string representation:
signature to signature_hex
The message and the signature are provided as command line argument respectively in argv[1]
and argv[2]
.
In the _verify_handler
function, you must first convert the signature provided as a string of hex characters to a byte array, this is done with the hex_bytes
function (from the fmt module):
fmt_hex_bytes(signature, argv[2]);
Then call the functions that verify the signature:
If everything works fine create got to next section "Hash Computation".
Not really:
But we continue anyway.
In this exercise, you will write RIOT shell commands to compute the hash of a given input string:
In both cases, the hash is stored in memory in a byte array and, to print it, it must be converted to a string of hexadecimal characters. Use an appropriate module for the conversion.
Hash functions of several algorithms (SHA256, SHA3-256, SHA1, MD5, CMAC) are provided by the hashes
module.
Add the hashes
module.
Hint: You will need the header files for sha256 and sha3.
Declare 2 buffers, the first one for the computed hash itself, and the second one for its hexadecimal string representation:
static uint8_t sha256_hash[SHA256_DIGEST_LENGTH];
static char sha256_hash_hex[SHA256_DIGEST_LENGTH * 2 + 1];
Note: the size of the hexadecimal string buffer is twice as large as the hash itself, because a single byte is represented by 2 hexadecimal characters.
In the _sha256_handler
function call the functions that will compute the hash from the command line argument argv[1]
:
implement the functions to compute a sha256 hash of a string (argv[1])
Convert the computed hash (a byte array) to its hexadecimal string representation:
you know what to use by now ;) (sha256_hash to sha256_hash_hex)
Declare 2 buffers, the first one for the computed hash itself, and the second one for its hexadecimal string representation:
static uint8_t sha3_hash[SHA3_256_DIGEST_LENGTH];
static char sha3_hash_hex[SHA3_256_DIGEST_LENGTH * 2 + 1];
Call the functions that will compute the hash from the command line argument argv[1]
:
implement the functions to compute a sha256 hash of a string (argv[1])
Convert the computed hash (a byte array) to its hexadecimal string representation:
Since we are programming in C and don't like to see something like:
we need to THINK and declare the functions in the last step:
If everything works fine create a new experiment at the IoT-Lab, build your application for the m3 node and upload it to your experiment. Run the experiment - interact with the shell to get the command output. Finally, make screenshots for your documentation.
Enter 'make'
Make term and trying out the functions:
It works!
On native:
And on the IoT-Server:
And another IoT-Server:
Submit 1 zip file for the crypto exercise containing your app’s main.c, Makefile, and README.