blob: 5dc1286af648a3ea35dce4384be07e1b54fe2b69 [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
#include "sw/device/lib/base/memory.h"
#include "sw/device/lib/dif/dif_aes.h"
#include "sw/device/lib/runtime/log.h"
#include "sw/device/sca/lib/prng.h"
#include "sw/device/sca/lib/sca.h"
#include "sw/device/sca/lib/simple_serial.h"
#include "hw/top_earlgrey/sw/autogen/top_earlgrey.h"
/**
* OpenTitan program for AES side-channel analysis.
*
* This program implements the following simple serial commands:
* - Set key ('k')*,
* - Encrypt ('p')*,
* - Version ('v')+,
* - Seed PRNG ('s')+,
* - Batch encrypt ('b')*,
* Commands marked with * are implemented in this file. Those marked with + are
* implemented in the simple serial library. Encryption is done in AES-ECB-128
* mode. See https://wiki.newae.com/SimpleSerial for details on the protocol.
*/
enum {
kAesKeyLength = 16,
kAesTextLength = 16,
/**
* Number of cycles (at `kClockFreqCpuHz`) that Ibex should sleep to minimize
* noise during AES operations. Caution: This number should be chosen to
* provide enough time. Otherwise, Ibex might wake up while AES is still busy
* and disturb the capture. Currently, we use a start trigger delay of 40
* clock cycles and the scope captures 90 clock cycles at kClockFreqCpuHz (900
* samples).
*/
kIbexAesSleepCycles = 400,
};
static dif_aes_t aes;
/**
* Simple serial 'k' (set key) command handler.
*
* This function does not use key shares to simplify side-channel analysis.
* The key must be `kAesKeySize` bytes long.
*
* @param key Key.
* @param key_len Key length.
* @return Result of the operation.
*/
static void aes_serial_set_key(const uint8_t *key, size_t key_len) {
SS_CHECK(key_len == kAesKeyLength);
dif_aes_transaction_t transaction = {
.key_len = kDifAesKey128,
.masking = kDifAesMaskingInternalPrng,
.mode = kDifAesModeEncrypt,
.operation = kDifAesOperationManual,
};
dif_aes_key_share_t key_shares;
memcpy(key_shares.share0, key, sizeof(key_shares.share0));
memset(key_shares.share1, 0, sizeof(key_shares.share1));
SS_CHECK_DIF_OK(dif_aes_start_ecb(&aes, &transaction, key_shares));
}
/**
* Callback wrapper for AES manual trigger function.
*/
static void aes_manual_trigger(void) {
SS_CHECK_DIF_OK(dif_aes_trigger(&aes, kDifAesTriggerStart));
}
/**
* Encrypts a plaintext using the AES peripheral.
*
* This function uses `sca_call_and_sleep()` from the sca library to put Ibex to
* sleep in order to minimize noise during captures. The plaintext must be
* `kAesKeySize` bytes long.
*
* @param plaintext Plaintext.
* @param plaintext_len Length of the plaintext.
* @return Result of the operation.
*/
static void aes_serial_encrypt(const uint8_t *plaintext, size_t plaintext_len) {
bool ready = false;
do {
SS_CHECK_DIF_OK(dif_aes_get_status(&aes, kDifAesStatusInputReady, &ready));
} while (!ready);
dif_aes_data_t data;
SS_CHECK(plaintext_len <= sizeof(data.data));
memcpy(data.data, plaintext, plaintext_len);
SS_CHECK_DIF_OK(dif_aes_load_data(&aes, data));
// Start AES operation (this triggers the capture) and go to sleep.
// Using the SecAesStartTriggerDelay hardware parameter, the AES unit is
// configured to start operation 40 cycles after receiving the start trigger.
// This allows Ibex to go to sleep in order to not disturb the capture.
sca_call_and_sleep(aes_manual_trigger, kIbexAesSleepCycles);
}
/**
* Simple serial 'p' (encrypt) command handler.
*
* Encrypts a `kAesKeySize` bytes long plaintext using the AES peripheral and
* sends the ciphertext over UART. This function also handles the trigger
* signal.
*
* @param plaintext Plaintext.
* @param plaintext_len Length of the plaintext.
*/
static void aes_serial_single_encrypt(const uint8_t *plaintext,
size_t plaintext_len) {
SS_CHECK(plaintext_len == kAesTextLength);
sca_set_trigger_high();
aes_serial_encrypt(plaintext, plaintext_len);
sca_set_trigger_low();
bool ready = false;
do {
SS_CHECK_DIF_OK(dif_aes_get_status(&aes, kDifAesStatusOutputValid, &ready));
} while (!ready);
dif_aes_data_t ciphertext;
SS_CHECK_DIF_OK(dif_aes_read_output(&aes, &ciphertext));
simple_serial_send_packet('r', (uint8_t *)ciphertext.data,
sizeof(ciphertext.data));
}
/**
* Simple serial 'b' (batch encrypt) command handler.
*
* This command is designed to maximize the capture rate for side-channel
* attacks. Instead of expecting a plaintext and sending the resulting
* ciphertext from and to the host for each encryption, this command repeatedly
* encrypts random plaintexts that are generated on the device. This minimizes
* the overhead of UART communication and significantly improves the capture
* rate. The host must use the same PRNG to be able to compute the plaintext and
* the ciphertext of each trace.
*
* Packet payload must be a `uint32_t` representation of the number of
* encryptions to perform. Since generated plaintexts are not cached, there is
* no limit on the number of encryptions.
*
* The PRNG should be initialized using the 's' (seed PRNG) command before
* starting batch encryption.
*
* Note that the host can partially verify this operation by checking the
* contents of the 'r' (ciphertext) packet that is sent at the end.
*
* @param data Packet payload.
* @param data_len Packet payload length.
*/
static void aes_serial_batch_encrypt(const uint8_t *data, size_t data_len) {
uint32_t num_encryptions = 0;
SS_CHECK(data_len == sizeof(num_encryptions));
num_encryptions = read_32(data);
sca_set_trigger_high();
for (uint32_t i = 0; i < num_encryptions; ++i) {
uint8_t plaintext[kAesTextLength];
prng_rand_bytes(plaintext, kAesTextLength);
aes_serial_encrypt(plaintext, kAesTextLength);
}
sca_set_trigger_low();
bool ready = false;
do {
SS_CHECK_DIF_OK(dif_aes_get_status(&aes, kDifAesStatusOutputValid, &ready));
} while (!ready);
dif_aes_data_t ciphertext;
SS_CHECK_DIF_OK(dif_aes_read_output(&aes, &ciphertext));
simple_serial_send_packet('r', (uint8_t *)ciphertext.data,
sizeof(ciphertext.data));
}
/**
* Initializes the AES peripheral.
*/
static void init_aes(void) {
SS_CHECK_DIF_OK(
dif_aes_init(mmio_region_from_addr(TOP_EARLGREY_AES_BASE_ADDR), &aes));
SS_CHECK_DIF_OK(dif_aes_reset(&aes));
}
/**
* Main function.
*
* Initializes peripherals and processes simple serial packets received over
* UART.
*/
int main(void) {
const dif_uart_t *uart1;
sca_init(kScaTriggerSourceAes, kScaPeripheralAes);
sca_get_uart(&uart1);
LOG_INFO("Running AES serial");
LOG_INFO("Initializing simple serial interface to capture board.");
simple_serial_init(uart1);
simple_serial_register_handler('k', aes_serial_set_key);
simple_serial_register_handler('p', aes_serial_single_encrypt);
simple_serial_register_handler('b', aes_serial_batch_encrypt);
LOG_INFO("Initializing AES unit.");
init_aes();
LOG_INFO("Starting simple serial packet handling.");
while (true) {
simple_serial_process_packet();
}
}