1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2024-12-24 07:18:09 +00:00

feat(core): Implement OPTIGA secure channel.

This commit is contained in:
Andrew Kozlik 2023-08-02 14:49:38 +02:00 committed by matejcik
parent 11e1795a89
commit 812e77cefd
3 changed files with 218 additions and 3 deletions

View File

@ -33,7 +33,7 @@
#include "sha2.h"
// Static buffer for commands and responses.
static uint8_t tx_buffer[1750] = {0};
static uint8_t tx_buffer[OPTIGA_MAX_APDU_SIZE] = {0};
static size_t tx_size = 0;
// TODO change to operational \x07

View File

@ -24,9 +24,13 @@
#include "optiga_transport.h"
#include <string.h>
#include "aes/aesccm.h"
#include "common.h"
#include "i2c.h"
#include "memzero.h"
#include "optiga_hal.h"
#include "tls_prf.h"
#include TREZOR_BOARD
// Maximum possible packet size that can be transmitted.
@ -92,11 +96,44 @@ enum {
PCTR_CHAIN_MASK = 0x07, // Mask of chain field.
};
// Security control byte.
enum {
SCTR_HELLO = 0x00, // Handshake hello message.
SCTR_FINISHED = 0x08, // Handshake finished message.
SCTR_PROTECTED = 0x23, // Record exchange message. Fully protected.
};
static uint8_t frame_num_out = 0xff;
static uint8_t frame_num_in = 0xff;
static uint8_t frame_buffer[1 + OPTIGA_DATA_REG_LEN];
static size_t frame_size = 0; // Set by optiga_read().
// Secure channel constants.
#define SEC_CHAN_SCTR_SIZE 1
#define SEC_CHAN_RND_SIZE 32
#define SEC_CHAN_SEQ_SIZE 4
#define SEC_CHAN_TAG_SIZE 8
#define SEC_CHAN_PROTOCOL 1
#define SEC_CHAN_HANDSHAKE_SIZE (SEC_CHAN_RND_SIZE + SEC_CHAN_SEQ_SIZE)
#define SEC_CHAN_CIPHERTEXT_OFFSET (SEC_CHAN_SCTR_SIZE + SEC_CHAN_SEQ_SIZE)
#define SEC_CHAN_OVERHEAD_SIZE \
(SEC_CHAN_SCTR_SIZE + SEC_CHAN_SEQ_SIZE + SEC_CHAN_TAG_SIZE)
#define SEC_CHAN_SEQ_OFFSET SEC_CHAN_SCTR_SIZE
// Secure channel status.
static bool sec_chan_established = false;
static aes_encrypt_ctx sec_chan_encr_ctx = {0};
static aes_encrypt_ctx sec_chan_decr_ctx = {0};
static uint8_t sec_chan_encr_nonce[8] = {0};
static uint8_t sec_chan_decr_nonce[8] = {0};
static uint8_t *const sec_chan_mseq = &sec_chan_encr_nonce[4];
static uint8_t *const sec_chan_sseq = &sec_chan_decr_nonce[4];
// Static buffer for encrypted commands and responses.
static uint8_t sec_chan_buffer[OPTIGA_MAX_APDU_SIZE + SEC_CHAN_OVERHEAD_SIZE] =
{0};
static size_t sec_chan_size = 0;
#ifdef NDEBUG
#define OPTIGA_LOG(prefix, data, data_size)
#else
@ -505,11 +542,184 @@ static optiga_result optiga_transceive(
return request_size == 0 ? OPTIGA_SUCCESS : OPTIGA_ERR_CMD;
}
static void increment_seq(uint8_t seq[SEC_CHAN_SEQ_SIZE]) {
for (int i = 3; i >= 0; --i) {
seq[i]++;
if (seq[i] != 0x00) {
return;
}
}
sec_chan_established = false;
memzero(&sec_chan_encr_ctx, sizeof(sec_chan_encr_ctx));
memzero(&sec_chan_decr_ctx, sizeof(sec_chan_decr_ctx));
memzero(sec_chan_encr_nonce, sizeof(sec_chan_encr_nonce));
memzero(sec_chan_decr_nonce, sizeof(sec_chan_decr_nonce));
}
optiga_result optiga_execute_command(const uint8_t *command_data,
size_t command_size,
uint8_t *response_data,
size_t max_response_size,
size_t *response_size) {
return optiga_transcieve(false, command_data, command_size, response_data,
if (!sec_chan_established) {
return optiga_transceive(false, command_data, command_size, response_data,
max_response_size, response_size);
}
sec_chan_size = command_size + SEC_CHAN_OVERHEAD_SIZE;
if (sec_chan_size > sizeof(sec_chan_buffer)) {
return OPTIGA_ERR_SIZE;
}
increment_seq(sec_chan_mseq);
// Encrypt command.
sec_chan_buffer[0] = SCTR_PROTECTED;
memcpy(&sec_chan_buffer[SEC_CHAN_SEQ_OFFSET], sec_chan_mseq,
SEC_CHAN_SEQ_SIZE);
uint8_t *ciphertext = &sec_chan_buffer[SEC_CHAN_CIPHERTEXT_OFFSET];
uint8_t associated_data[8] = {SCTR_PROTECTED, 0, 0, 0, 0, SEC_CHAN_PROTOCOL};
memcpy(&associated_data[SEC_CHAN_SEQ_OFFSET], sec_chan_mseq,
SEC_CHAN_SEQ_SIZE);
associated_data[6] = command_size >> 8;
associated_data[7] = command_size & 0xff;
if (EXIT_SUCCESS != aes_ccm_encrypt(&sec_chan_encr_ctx, sec_chan_encr_nonce,
sizeof(sec_chan_encr_nonce),
associated_data, sizeof(associated_data),
command_data, command_size,
SEC_CHAN_TAG_SIZE, ciphertext)) {
return OPTIGA_ERR_PROCESS;
}
// Transmit encrypted command and receive response.
optiga_result ret =
optiga_transceive(true, sec_chan_buffer, sec_chan_size, sec_chan_buffer,
sizeof(sec_chan_buffer), &sec_chan_size);
if (ret != OPTIGA_SUCCESS) {
return ret;
}
increment_seq(sec_chan_sseq);
if (sec_chan_size < SEC_CHAN_OVERHEAD_SIZE ||
sec_chan_buffer[0] != SCTR_PROTECTED ||
memcmp(&sec_chan_buffer[SEC_CHAN_SEQ_OFFSET], sec_chan_sseq,
SEC_CHAN_SEQ_SIZE) != 0) {
return OPTIGA_ERR_UNEXPECTED;
}
*response_size = sec_chan_size - SEC_CHAN_OVERHEAD_SIZE;
if (*response_size > max_response_size) {
*response_size = 0;
return OPTIGA_ERR_SIZE;
}
// Decrypt response.
memcpy(&associated_data[SEC_CHAN_SEQ_OFFSET], sec_chan_sseq,
SEC_CHAN_SEQ_SIZE);
associated_data[6] = *response_size >> 8;
associated_data[7] = *response_size & 0xff;
if (EXIT_SUCCESS != aes_ccm_decrypt(&sec_chan_decr_ctx, sec_chan_decr_nonce,
sizeof(sec_chan_decr_nonce),
associated_data, sizeof(associated_data),
ciphertext,
*response_size + SEC_CHAN_TAG_SIZE,
SEC_CHAN_TAG_SIZE, response_data)) {
return OPTIGA_ERR_PROCESS;
}
return OPTIGA_SUCCESS;
}
optiga_result optiga_sec_chan_handshake(const uint8_t *secret,
size_t secret_size) {
static const uint8_t HANDSHAKE_HELLO[] = {SCTR_HELLO, SEC_CHAN_PROTOCOL};
// Send Handshake Hello.
optiga_result ret = optiga_transceive(
true, HANDSHAKE_HELLO, sizeof(HANDSHAKE_HELLO), sec_chan_buffer,
sizeof(sec_chan_buffer), &sec_chan_size);
if (ret != OPTIGA_SUCCESS) {
return ret;
}
// Process Handshake Hello response (sctr[1], pver[1], rnd[32], sseq[4]).
if (sec_chan_size != 2 + SEC_CHAN_RND_SIZE + SEC_CHAN_SEQ_SIZE ||
sec_chan_buffer[0] != SCTR_HELLO ||
sec_chan_buffer[1] != SEC_CHAN_PROTOCOL) {
return OPTIGA_ERR_UNEXPECTED;
}
uint8_t payload[SEC_CHAN_HANDSHAKE_SIZE] = {0};
memcpy(payload, &sec_chan_buffer[2], sizeof(payload));
uint8_t *rnd = &payload[0];
uint8_t *sseq = &payload[SEC_CHAN_RND_SIZE];
// Compute encryption and decryption keys.
uint8_t encryption_keys[40] = {0};
tls_prf_sha256(secret, secret_size, (const uint8_t *)"Platform Binding", 16,
rnd, SEC_CHAN_RND_SIZE, encryption_keys,
sizeof(encryption_keys));
aes_encrypt_key128(&encryption_keys[0], &sec_chan_encr_ctx);
aes_encrypt_key128(&encryption_keys[16], &sec_chan_decr_ctx);
memcpy(&sec_chan_encr_nonce[0], &encryption_keys[32], 4);
memcpy(&sec_chan_decr_nonce[0], &encryption_keys[36], 4);
memzero(encryption_keys, sizeof(encryption_keys));
// Prepare Handshake Finished message (sctr[1], sseq[4], ciphertext[44]).
uint8_t handshake_finished[SEC_CHAN_HANDSHAKE_SIZE + SEC_CHAN_OVERHEAD_SIZE] =
{SCTR_FINISHED};
memcpy(&handshake_finished[SEC_CHAN_SEQ_OFFSET], sseq, SEC_CHAN_SEQ_SIZE);
uint8_t *ciphertext = &handshake_finished[SEC_CHAN_CIPHERTEXT_OFFSET];
uint8_t associated_data[8] = {
SCTR_FINISHED, 0, 0, 0, 0, SEC_CHAN_PROTOCOL, 0, SEC_CHAN_HANDSHAKE_SIZE};
memcpy(&associated_data[SEC_CHAN_SEQ_OFFSET], sseq, SEC_CHAN_SEQ_SIZE);
memcpy(sec_chan_mseq, sseq, SEC_CHAN_SEQ_SIZE);
if (EXIT_SUCCESS != aes_ccm_encrypt(&sec_chan_encr_ctx, sec_chan_encr_nonce,
sizeof(sec_chan_encr_nonce),
associated_data, sizeof(associated_data),
payload, SEC_CHAN_HANDSHAKE_SIZE,
SEC_CHAN_TAG_SIZE, ciphertext)) {
return OPTIGA_ERR_PROCESS;
}
// Send Handshake Finished message.
ret = optiga_transceive(true, handshake_finished, sizeof(handshake_finished),
sec_chan_buffer, sizeof(sec_chan_buffer),
&sec_chan_size);
if (ret != OPTIGA_SUCCESS) {
return ret;
}
// Process response (sctr[1], mseq[4], ciphertext[44]).
if (sec_chan_size != SEC_CHAN_HANDSHAKE_SIZE + SEC_CHAN_OVERHEAD_SIZE ||
sec_chan_buffer[0] != SCTR_FINISHED) {
return OPTIGA_ERR_UNEXPECTED;
}
uint8_t *mseq = &sec_chan_buffer[SEC_CHAN_SEQ_OFFSET];
ciphertext = &sec_chan_buffer[SEC_CHAN_CIPHERTEXT_OFFSET];
// Verify payload.
memcpy(sec_chan_sseq, mseq, SEC_CHAN_SEQ_SIZE);
memcpy(&associated_data[SEC_CHAN_SEQ_OFFSET], mseq, SEC_CHAN_SEQ_SIZE);
uint8_t response_payload[SEC_CHAN_HANDSHAKE_SIZE] = {0};
if (EXIT_SUCCESS !=
aes_ccm_decrypt(&sec_chan_decr_ctx, sec_chan_decr_nonce,
sizeof(sec_chan_decr_nonce), associated_data,
sizeof(associated_data), ciphertext,
SEC_CHAN_HANDSHAKE_SIZE + SEC_CHAN_TAG_SIZE,
SEC_CHAN_TAG_SIZE, response_payload)) {
return OPTIGA_ERR_UNEXPECTED;
}
if (memcmp(response_payload, rnd, SEC_CHAN_RND_SIZE) != 0 ||
memcmp(response_payload + SEC_CHAN_RND_SIZE, mseq, SEC_CHAN_SEQ_SIZE) !=
0) {
return OPTIGA_ERR_UNEXPECTED;
}
memcpy(sec_chan_mseq, mseq, SEC_CHAN_SEQ_SIZE);
memcpy(sec_chan_sseq, sseq, SEC_CHAN_SEQ_SIZE);
sec_chan_established = true;
return OPTIGA_SUCCESS;
}

View File

@ -28,7 +28,12 @@
// Maximum data register length supported by OPTIGA.
#define OPTIGA_DATA_REG_LEN 277
// Maximum command and response APDU size supported by OPTIGA.
#define OPTIGA_MAX_APDU_SIZE 1557
optiga_result optiga_init(void);
optiga_result optiga_sec_chan_handshake(const uint8_t *secret,
size_t secret_size);
optiga_result optiga_execute_command(const uint8_t *command_data,
size_t command_size,
uint8_t *response_data,