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:
parent
11e1795a89
commit
812e77cefd
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user