Cryptotest Framework
Cryptotest is a unified framework for injecting testvectors and exercising the cryptolib API to verify functionality, perform compliance testing, and enable instrumentation for penetration testing.
Overview
The cryptotest framework allows the host to send commands to the device over UART. The device runs firmware that listens for these commands and performs the corresponding operations before sending the results back to the host. This communication is enable by uJSON which uses the C preprocessor to generate serialize/deserialize functions for each command in C and Rust.
Code Organization
There are four components:
- uJSON command definitions (defined in C header files)
- Auto-generated uJSON serialize/deserialize functions (generated during the Bazel build)
- Device-side command listener firmware
- Host-side test harness program that uses the commands
These components are organized as follows:
sw/device/tests/crypto/cryptotest
firmware
: on-device command listener firmwarejson
: uJSON definitions of the cryptotest commands
sw/host/cryptotest/ujson_lib
: Rust stubs to import the generated uJSON Rust functions and collect them into a single library.sw/host/tests/crypto
: host-side test harnesses for each test
Adding a New Command
This section describes the process for adding a new command to the framework. Adding a new command requires first defining a uJSON representation for it. Then, on the device side, a handler must be added to the device firmware to listen for the command. On the host side, the generated uJSON functions need to be added to the Cryptotest’s Rust uJSON library. Lastly, a test harness can be written that uses the new command.
The following snippets demonstrate adding a new command, ExampleCommand
, alongside existing Aes
and Rsa
commands.
Define a uJSON Command
Define the top-level uJSON command in sw/device/tests/crypto/cryptotest/json/commands.h
.
If the command has subcommands or additional parameters, define them in another file in the same directory.
See sw/device/lib/ujson/example.h
for a uJSON example.
sw/device/tests/crypto/cryptotest/json/commands.h
#define COMMAND(_, value) \
value(_, Aes) \
value(_, Rsa) \
value(_, ExampleCommand)
UJSON_SERDE_ENUM(CryptotestCommand, cryptotest_cmd_t, COMMAND);
sw/device/tests/crypto/cryptotest/json/example_command.h
#include "sw/device/lib/ujson/ujson_derive.h"
// Defining two subcommands, A and B, to the top-level Example command
#define EXAMPLE_SUBCOMMAND(_, value) \
value(_, A) \
value(_, B)
UJSON_SERDE_ENUM(ExampleSubcommand, example_subcommand_t, EXAMPLE_SUBCOMMAND);
Add Device-Side Firmware Handler
Register a handler in the device firmware: sw/device/tests/crypto/cryptotest/firmware/firmware.c
.
sw/device/tests/crypto/cryptotest/firmware/firmware.c
switch (cmd) {
case kCryptotestCommandAes:
RESP_ERR(uj, handle_aes(uj));
break;
case kCryptotestCommandRsa:
RESP_ERR(uj, handle_rsa(uj));
break;
case kCryptotestCommandExample:
RESP_ERR(uj, handle_example(uj));
break;
default:
LOG_ERROR("Unrecognized command: %d", cmd);
RESP_ERR(uj, INVALID_ARGUMENT());
}
Now, add the handler code in another file in the same directory.
sw/device/tests/crypto/cryptotest/firmware/example.c
status_t handle_example(ujson_t *uj) {
example_subcommand_t cmd;
TRY(ujson_deserialize_example_subcommand_t(uj, &cmd));
switch (cmd) {
case kExampleSubcommandA:
return handle_subcommand_a(uj);
break;
case kExampleSubcommandB:
return handle_subcommand_b(uj);
break;
default:
LOG_ERROR("Unrecognized Example subcommand: %d", cmd);
return INVALID_ARGUMENT();
}
return OK_STATUS(0);
}
Add to Host-Side Rust Library
Add a Rust stub to sw/host/cryptotest/ujson_lib
to import the uJSON-generated serialize/deserialize functions for the new command.
sw/host/cryptotest/ujson_lib/src/example.rs
#![allow(unused)] fn main() { include!(env!("example_commands_path")); }
sw/host/cryptotest/ujson_lib/BUILD
#![allow(unused)] fn main() { ujson_rust( name = "example_commands_rust", srcs = ["//sw/device/tests/crypto/cryptotest/json:example_commands"], ) rust_library( name = "cryptotest_commands", ... rustc_env = { "commands_loc": "$(execpath :commands_rust)", "example_commands_loc": "$(execpath :example_commands_rust)", ... }, ... ) }
Write Test Harness
To write the test itself, create a host-side test harness in rust in sw/host/tests/crypto
that sends the commands.
See sw/host/cryptotest/tests/crypto/aes_nist_kat/src/main.rs
for an example.