From 812e77cefdc80a790056c6c98b6ce62572b25144 Mon Sep 17 00:00:00 2001 From: Andrew Kozlik Date: Wed, 2 Aug 2023 14:49:38 +0200 Subject: [PATCH] feat(core): Implement OPTIGA secure channel. --- core/embed/trezorhal/optiga/optiga_commands.c | 2 +- .../embed/trezorhal/optiga/optiga_transport.c | 214 +++++++++++++++++- core/embed/trezorhal/optiga_transport.h | 5 + 3 files changed, 218 insertions(+), 3 deletions(-) diff --git a/core/embed/trezorhal/optiga/optiga_commands.c b/core/embed/trezorhal/optiga/optiga_commands.c index 39c3d21c66..d2b13866d7 100644 --- a/core/embed/trezorhal/optiga/optiga_commands.c +++ b/core/embed/trezorhal/optiga/optiga_commands.c @@ -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 diff --git a/core/embed/trezorhal/optiga/optiga_transport.c b/core/embed/trezorhal/optiga/optiga_transport.c index a09dc6b446..9a724f6ec5 100644 --- a/core/embed/trezorhal/optiga/optiga_transport.c +++ b/core/embed/trezorhal/optiga/optiga_transport.c @@ -24,9 +24,13 @@ #include "optiga_transport.h" #include +#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, - max_response_size, response_size); + 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; } diff --git a/core/embed/trezorhal/optiga_transport.h b/core/embed/trezorhal/optiga_transport.h index 5d6215ba7e..4275199ebb 100644 --- a/core/embed/trezorhal/optiga_transport.h +++ b/core/embed/trezorhal/optiga_transport.h @@ -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,