You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
726 lines
23 KiB
726 lines
23 KiB
/*
|
|
* This file is part of the Trezor project, https://trezor.io/
|
|
*
|
|
* Copyright (c) SatoshiLabs
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
/*
|
|
* Reference manual:
|
|
* https://github.com/Infineon/optiga-trust-m/blob/develop/documents/Infineon_I2C_Protocol_v2.03.pdf
|
|
*/
|
|
|
|
#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.
|
|
#define OPTIGA_MAX_PACKET_SIZE (OPTIGA_DATA_REG_LEN - 5)
|
|
|
|
// Register address of the data register.
|
|
static const uint8_t REG_DATA = 0x80;
|
|
|
|
// Register address of the register holding the maximum data register length (2
|
|
// bytes).
|
|
static const uint8_t REG_DATA_LEN = 0x81;
|
|
|
|
// Register address of the I2C state register (4 bytes).
|
|
static const uint8_t REG_I2C_STATE = 0x82;
|
|
|
|
// Register address of the register that triggers a warm device reset.
|
|
static const uint8_t REG_SOFT_RESET = 0x88;
|
|
|
|
// I2C state register - Device is busy executing a command.
|
|
static const uint8_t I2C_STATE_BYTE1_BUSY = 0x80;
|
|
|
|
// I2C state register - Device is ready to return a response.
|
|
static const uint8_t I2C_STATE_BYTE1_RESP_RDY = 0x40;
|
|
|
|
// I2C base address of Optiga.
|
|
static const uint8_t BASE_ADDR = 0x30;
|
|
|
|
// Address of Optiga use by our HAL which requires it to be shifted by one bit.
|
|
static const uint16_t OPTIGA_ADDRESS = (BASE_ADDR << 1);
|
|
|
|
// Constants for our I2C HAL.
|
|
static const uint32_t I2C_TIMEOUT = 15;
|
|
static const int I2C_MAX_RETRY_COUNT = 10;
|
|
|
|
// Maximum number of times to retry reading Optiga's response to a command.
|
|
static const int MAX_RETRY_READ = 10000;
|
|
|
|
// Maximum number of packets per response. Used for flushing old reponses.
|
|
static const int MAX_RESPONSE_PACKET_COUNT = 15;
|
|
|
|
// Frame control byte - Type.
|
|
static const uint8_t FTYPE_MASK = 0x80;
|
|
static const uint8_t FTYPE_DATA = 0x00;
|
|
static const uint8_t FTYPE_CONTROL = 0x80;
|
|
|
|
// Frame control byte - Sequence.
|
|
static const uint8_t SEQCTR_ACK = 0x00; // Acknowledge received frame ACKNR.
|
|
static const uint8_t SEQCTR_RESET = 0x40; // Reset frame counter for sync.
|
|
static const uint8_t SEQCTR_MASK = 0x60; // Mask of SEQCTR bits.
|
|
|
|
// Frame control byte - Frame number.
|
|
static const uint8_t FRNR_MASK = 0x0C;
|
|
static const uint8_t FRNR_SHIFT = 2;
|
|
static const uint8_t ACKNR_MASK = 0x03;
|
|
|
|
// Packet control byte.
|
|
enum {
|
|
PCTR_PRESENTATION_LAYER = 0x08, // Presentation layer present.
|
|
PCTR_CHAIN_NONE = 0x00, // No chaining. Single frame.
|
|
PCTR_CHAIN_FIRST = 0x01, // First packet of a chain.
|
|
PCTR_CHAIN_MIDDLE = 0x02, // Intermediate packet(s) of a chain.
|
|
PCTR_CHAIN_LAST = 0x04, // Last packet of a chain.
|
|
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
|
|
static optiga_log_hex_t log_hex = NULL;
|
|
void optiga_set_log_hex(optiga_log_hex_t f) { log_hex = f; }
|
|
#define OPTIGA_LOG(prefix, data, data_size) \
|
|
if (log_hex != NULL) { \
|
|
log_hex(prefix, data, data_size); \
|
|
}
|
|
#endif
|
|
|
|
static uint16_t calc_crc_byte(uint16_t seed, uint8_t c) {
|
|
uint16_t h1 = (seed ^ c) & 0xFF;
|
|
uint16_t h2 = h1 & 0x0F;
|
|
uint16_t h3 = (h2 << 4) ^ h1;
|
|
uint16_t h4 = h3 >> 4;
|
|
return (((((h3 << 1) ^ h4) << 4) ^ h2) << 3) ^ h4 ^ (seed >> 8);
|
|
}
|
|
|
|
static uint16_t calc_crc(uint8_t *data, size_t data_size) {
|
|
uint16_t crc = 0;
|
|
for (size_t i = 0; i < data_size; ++i) {
|
|
crc = calc_crc_byte(crc, data[i]);
|
|
}
|
|
return crc;
|
|
}
|
|
|
|
optiga_result optiga_init(void) {
|
|
optiga_hal_init();
|
|
return optiga_set_data_reg_len(OPTIGA_DATA_REG_LEN);
|
|
}
|
|
|
|
static optiga_result optiga_i2c_write(const uint8_t *data, uint16_t data_size) {
|
|
OPTIGA_LOG(">>> ", data, data_size)
|
|
|
|
for (int try_count = 0; try_count <= I2C_MAX_RETRY_COUNT; ++try_count) {
|
|
if (try_count != 0) {
|
|
hal_delay(1);
|
|
}
|
|
if (HAL_OK == i2c_transmit(OPTIGA_I2C_INSTANCE, OPTIGA_ADDRESS,
|
|
(uint8_t *)data, data_size, I2C_TIMEOUT)) {
|
|
return OPTIGA_SUCCESS;
|
|
}
|
|
}
|
|
return OPTIGA_ERR_I2C_WRITE;
|
|
}
|
|
|
|
static optiga_result optiga_i2c_read(uint8_t *buffer, uint16_t buffer_size) {
|
|
for (int try_count = 0; try_count <= I2C_MAX_RETRY_COUNT; ++try_count) {
|
|
HAL_Delay(1);
|
|
if (HAL_OK == i2c_receive(OPTIGA_I2C_INSTANCE, OPTIGA_ADDRESS, buffer,
|
|
buffer_size, I2C_TIMEOUT)) {
|
|
OPTIGA_LOG("<<< ", buffer, buffer_size)
|
|
return OPTIGA_SUCCESS;
|
|
}
|
|
}
|
|
return OPTIGA_ERR_I2C_READ;
|
|
}
|
|
|
|
optiga_result optiga_resync(void) {
|
|
frame_num_out = 0xff;
|
|
frame_num_in = 0xff;
|
|
uint8_t data[6] = {REG_DATA, FTYPE_CONTROL | SEQCTR_RESET, 0, 0};
|
|
uint16_t crc = calc_crc(&data[1], 3);
|
|
data[4] = crc >> 8;
|
|
data[5] = crc & 0xff;
|
|
return optiga_i2c_write(data, sizeof(data));
|
|
}
|
|
|
|
optiga_result optiga_soft_reset(void) {
|
|
uint8_t data[3] = {REG_SOFT_RESET, 0xff, 0xff};
|
|
optiga_result ret = optiga_i2c_write(data, sizeof(data));
|
|
if (ret != OPTIGA_SUCCESS) {
|
|
return ret;
|
|
}
|
|
|
|
frame_num_out = 0xff;
|
|
frame_num_in = 0xff;
|
|
|
|
return OPTIGA_SUCCESS;
|
|
}
|
|
|
|
static optiga_result optiga_send_ack(void) {
|
|
// Set up frame control information.
|
|
uint8_t fctr = FTYPE_CONTROL | SEQCTR_ACK;
|
|
fctr |= (frame_num_in & ACKNR_MASK);
|
|
|
|
// Compile frame.
|
|
frame_buffer[0] = REG_DATA;
|
|
frame_buffer[1] = fctr;
|
|
frame_buffer[2] = 0;
|
|
frame_buffer[3] = 0;
|
|
uint16_t crc = calc_crc(&frame_buffer[1], 3);
|
|
frame_buffer[4] = crc >> 8;
|
|
frame_buffer[5] = crc & 0xff;
|
|
|
|
return optiga_i2c_write(frame_buffer, 6);
|
|
}
|
|
|
|
static optiga_result optiga_check_ack(void) {
|
|
// Expected frame control information byte.
|
|
uint8_t fctr = FTYPE_CONTROL | SEQCTR_ACK;
|
|
fctr |= (frame_num_out & ACKNR_MASK);
|
|
|
|
optiga_result ret = OPTIGA_SUCCESS;
|
|
if (frame_size != 3 || frame_buffer[0] != fctr || frame_buffer[1] != 0 ||
|
|
frame_buffer[2] != 0) {
|
|
ret = OPTIGA_ERR_UNEXPECTED;
|
|
}
|
|
|
|
frame_size = 0;
|
|
return ret;
|
|
}
|
|
|
|
static optiga_result optiga_ensure_ready(void) {
|
|
optiga_result ret = OPTIGA_SUCCESS;
|
|
for (int i = 0; i < MAX_RESPONSE_PACKET_COUNT; ++i) {
|
|
for (int j = 0; j < MAX_RETRY_READ; ++j) {
|
|
ret = optiga_i2c_write(®_I2C_STATE, 1);
|
|
if (OPTIGA_SUCCESS != ret) {
|
|
return ret;
|
|
}
|
|
|
|
ret = optiga_i2c_read(frame_buffer, 4);
|
|
if (OPTIGA_SUCCESS != ret) {
|
|
return ret;
|
|
}
|
|
|
|
if ((frame_buffer[0] & I2C_STATE_BYTE1_BUSY) == 0) {
|
|
break;
|
|
}
|
|
ret = OPTIGA_ERR_BUSY;
|
|
}
|
|
|
|
if (ret != OPTIGA_SUCCESS) {
|
|
return ret;
|
|
}
|
|
|
|
if ((frame_buffer[0] & I2C_STATE_BYTE1_RESP_RDY) == 0) {
|
|
return OPTIGA_SUCCESS;
|
|
}
|
|
|
|
// Flush out the previous response.
|
|
|
|
uint16_t size = (frame_buffer[2] << 8) + frame_buffer[3];
|
|
if (size > sizeof(frame_buffer)) {
|
|
return OPTIGA_ERR_SIZE;
|
|
}
|
|
|
|
ret = optiga_i2c_write(®_DATA, 1);
|
|
if (OPTIGA_SUCCESS != ret) {
|
|
return ret;
|
|
}
|
|
|
|
ret = optiga_i2c_read(frame_buffer, size);
|
|
if (OPTIGA_SUCCESS != ret) {
|
|
return ret;
|
|
}
|
|
|
|
if (size < 3) {
|
|
return OPTIGA_ERR_UNEXPECTED;
|
|
}
|
|
|
|
// Ignore CRC.
|
|
frame_size = size - 2;
|
|
|
|
if ((frame_buffer[0] & FTYPE_MASK) == FTYPE_DATA) {
|
|
// Sync frame numbers with Optiga.
|
|
frame_num_in = (frame_buffer[0] & FRNR_MASK) >> FRNR_SHIFT;
|
|
frame_num_out = frame_buffer[0] & ACKNR_MASK;
|
|
ret = optiga_send_ack();
|
|
if (OPTIGA_SUCCESS != ret) {
|
|
return ret;
|
|
}
|
|
} else {
|
|
if ((frame_buffer[0] & SEQCTR_MASK) == SEQCTR_RESET) {
|
|
frame_num_out = 0xff;
|
|
frame_num_in = 0xff;
|
|
}
|
|
}
|
|
}
|
|
|
|
return OPTIGA_ERR_TIMEOUT;
|
|
}
|
|
|
|
optiga_result optiga_set_data_reg_len(size_t size) {
|
|
if (size > 0xffff) {
|
|
return OPTIGA_ERR_SIZE;
|
|
}
|
|
|
|
// Set the new data register length.
|
|
uint8_t data[3] = {REG_DATA_LEN, size >> 8, size & 0xff};
|
|
optiga_result ret = optiga_i2c_write(data, sizeof(data));
|
|
if (ret != OPTIGA_SUCCESS) {
|
|
return ret;
|
|
}
|
|
|
|
// Check that the length was set correctly.
|
|
ret = optiga_i2c_write(®_DATA_LEN, 1);
|
|
if (OPTIGA_SUCCESS != ret) {
|
|
return ret;
|
|
}
|
|
|
|
ret = optiga_i2c_read(frame_buffer, 2);
|
|
if (OPTIGA_SUCCESS != ret) {
|
|
return ret;
|
|
}
|
|
|
|
if ((frame_buffer[0] << 8) + frame_buffer[1] != size) {
|
|
return OPTIGA_ERR_SIZE;
|
|
}
|
|
return OPTIGA_SUCCESS;
|
|
}
|
|
|
|
static optiga_result optiga_read(void) {
|
|
for (int i = 0; i < MAX_RETRY_READ; ++i) {
|
|
optiga_result ret = optiga_i2c_write(®_I2C_STATE, 1);
|
|
if (OPTIGA_SUCCESS != ret) {
|
|
return ret;
|
|
}
|
|
|
|
ret = optiga_i2c_read(frame_buffer, 4);
|
|
if (OPTIGA_SUCCESS != ret) {
|
|
return ret;
|
|
}
|
|
|
|
if (frame_buffer[0] & I2C_STATE_BYTE1_RESP_RDY) {
|
|
uint16_t size = (frame_buffer[2] << 8) + frame_buffer[3];
|
|
if (size > sizeof(frame_buffer)) {
|
|
return OPTIGA_ERR_SIZE;
|
|
}
|
|
|
|
ret = optiga_i2c_write(®_DATA, 1);
|
|
if (OPTIGA_SUCCESS != ret) {
|
|
return ret;
|
|
}
|
|
|
|
ret = optiga_i2c_read(frame_buffer, size);
|
|
if (OPTIGA_SUCCESS != ret) {
|
|
return ret;
|
|
}
|
|
|
|
if (calc_crc(frame_buffer, size - 2) !=
|
|
(frame_buffer[size - 2] << 8) + frame_buffer[size - 1]) {
|
|
return OPTIGA_ERR_CRC;
|
|
}
|
|
|
|
frame_size = size - 2;
|
|
|
|
return OPTIGA_SUCCESS;
|
|
}
|
|
}
|
|
|
|
return OPTIGA_ERR_TIMEOUT;
|
|
}
|
|
|
|
static optiga_result optiga_send_packet(uint8_t packet_control_byte,
|
|
const uint8_t *packet_data,
|
|
size_t packet_data_size) {
|
|
_Static_assert(sizeof(frame_buffer) - 7 >= OPTIGA_MAX_PACKET_SIZE - 1);
|
|
|
|
if (packet_data_size > sizeof(frame_buffer) - 7) {
|
|
return OPTIGA_ERR_SIZE;
|
|
}
|
|
|
|
// Set up frame control information.
|
|
uint8_t fctr = FTYPE_DATA | SEQCTR_ACK;
|
|
fctr |= (frame_num_out << FRNR_SHIFT) & FRNR_MASK;
|
|
fctr |= (frame_num_in & ACKNR_MASK);
|
|
|
|
// Compile frame.
|
|
frame_buffer[0] = REG_DATA;
|
|
frame_buffer[1] = fctr;
|
|
frame_buffer[2] = (packet_data_size + 1) >> 8;
|
|
frame_buffer[3] = (packet_data_size + 1) & 0xff;
|
|
frame_buffer[4] = packet_control_byte;
|
|
memcpy(&frame_buffer[5], packet_data, packet_data_size);
|
|
uint16_t crc = calc_crc(&frame_buffer[1], packet_data_size + 4);
|
|
frame_buffer[packet_data_size + 5] = crc >> 8;
|
|
frame_buffer[packet_data_size + 6] = crc & 0xff;
|
|
|
|
return optiga_i2c_write(frame_buffer, packet_data_size + 7);
|
|
}
|
|
|
|
static optiga_result optiga_receive_packet(uint8_t *packet_control_byte,
|
|
uint8_t *packet_data,
|
|
size_t max_packet_data_size,
|
|
size_t *packet_data_size) {
|
|
// Expected frame control information byte.
|
|
uint8_t fctr = FTYPE_DATA | SEQCTR_ACK;
|
|
fctr |= (frame_num_in << FRNR_SHIFT) & FRNR_MASK;
|
|
fctr |= (frame_num_out & ACKNR_MASK);
|
|
*packet_data_size = (frame_buffer[1] << 8) + frame_buffer[2] - 1;
|
|
if (frame_size < 3 || frame_buffer[0] != fctr ||
|
|
*packet_data_size + 4 != frame_size) {
|
|
frame_size = 0;
|
|
return OPTIGA_ERR_UNEXPECTED;
|
|
}
|
|
frame_size = 0;
|
|
|
|
if (*packet_data_size > max_packet_data_size) {
|
|
return OPTIGA_ERR_SIZE;
|
|
}
|
|
|
|
*packet_control_byte = frame_buffer[3];
|
|
memcpy(packet_data, &frame_buffer[4], *packet_data_size);
|
|
return OPTIGA_SUCCESS;
|
|
}
|
|
|
|
static optiga_result optiga_transceive(
|
|
bool presentation_layer, const uint8_t *request_data, size_t request_size,
|
|
uint8_t *response_data, size_t max_response_size, size_t *response_size) {
|
|
*response_size = 0;
|
|
optiga_result ret = optiga_ensure_ready();
|
|
if (ret != OPTIGA_SUCCESS) {
|
|
return ret;
|
|
}
|
|
|
|
uint8_t pctr = 0;
|
|
if (presentation_layer) {
|
|
pctr |= PCTR_PRESENTATION_LAYER;
|
|
}
|
|
|
|
// Transmit command packets to OPTIGA.
|
|
uint8_t chain = PCTR_CHAIN_NONE;
|
|
do {
|
|
size_t packet_data_size = 0;
|
|
// The first byte of each packet is the packet control byte pctr, so each
|
|
// packet contains at most OPTIGA_MAX_PACKET_SIZE - 1 bytes of data.
|
|
if (request_size > OPTIGA_MAX_PACKET_SIZE - 1) {
|
|
packet_data_size = OPTIGA_MAX_PACKET_SIZE - 1;
|
|
if (chain == PCTR_CHAIN_NONE) {
|
|
chain = PCTR_CHAIN_FIRST;
|
|
} else {
|
|
chain = PCTR_CHAIN_MIDDLE;
|
|
}
|
|
} else {
|
|
packet_data_size = request_size;
|
|
if (chain != PCTR_CHAIN_NONE) {
|
|
chain = PCTR_CHAIN_LAST;
|
|
}
|
|
}
|
|
|
|
frame_num_out += 1;
|
|
|
|
ret = optiga_send_packet(pctr | chain, request_data, packet_data_size);
|
|
if (ret != OPTIGA_SUCCESS) {
|
|
return ret;
|
|
}
|
|
|
|
request_data += packet_data_size;
|
|
request_size -= packet_data_size;
|
|
|
|
ret = optiga_read();
|
|
if (ret != OPTIGA_SUCCESS) {
|
|
return ret;
|
|
}
|
|
|
|
if ((frame_buffer[0] & FTYPE_MASK) == FTYPE_DATA) {
|
|
// OPTIGA sometimes doesn't send a separate control frame to ACK receipt,
|
|
// but responds directly with data. It may also respond with data instead
|
|
// of an ACK if there is an error in a multi-packet transmission.
|
|
break;
|
|
}
|
|
|
|
ret = optiga_check_ack();
|
|
if (ret != OPTIGA_SUCCESS) {
|
|
return ret;
|
|
}
|
|
} while (request_size != 0);
|
|
|
|
// Receive response packets from OPTIGA.
|
|
do {
|
|
frame_num_in += 1;
|
|
|
|
// Read only if there is no pending data in the frame buffer. (There will
|
|
// already be data if OPTIGA didn't send a separate control frame in the
|
|
// previous loop.)
|
|
if (frame_size == 0) {
|
|
ret = optiga_read();
|
|
if (ret != OPTIGA_SUCCESS) {
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
size_t packet_data_size = 0;
|
|
ret = optiga_receive_packet(&pctr, response_data, max_response_size,
|
|
&packet_data_size);
|
|
if (ret != OPTIGA_SUCCESS) {
|
|
*response_size = 0;
|
|
return ret;
|
|
}
|
|
*response_size += packet_data_size;
|
|
response_data += packet_data_size;
|
|
max_response_size -= packet_data_size;
|
|
|
|
ret = optiga_send_ack();
|
|
if (ret != OPTIGA_SUCCESS) {
|
|
*response_size = 0;
|
|
return ret;
|
|
}
|
|
|
|
pctr &= PCTR_CHAIN_MASK;
|
|
} while (pctr == PCTR_CHAIN_FIRST || pctr == PCTR_CHAIN_MIDDLE);
|
|
|
|
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) {
|
|
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;
|
|
}
|