From 0c01fe648211cc82909023a3ed7b20b2854d49eb Mon Sep 17 00:00:00 2001 From: Michalis Pappas Date: Tue, 15 May 2018 12:04:54 +0300 Subject: [PATCH] Introduce the s96at library Add s96at.h, which defines and documents the library API as well as s96at.c which contains the implementation. Add auxiliary sha.c which contain the implementation of the SHA-256 padding according to FIPS 180-2. Update CMakeLists.txt to build libs96at.so. Remove obsolete files. Signed-off-by: Michalis Pappas --- s96at/CMakeLists.txt | 11 +- s96at/include/cmd.h | 30 +- s96at/include/defines.h | 13 - s96at/include/s96at.h | 431 +++++++++++++++++++++++++++++ s96at/include/s96at_private.h | 15 + s96at/include/sha.h | 17 ++ s96at/src/main.c | 8 - s96at/src/s96at.c | 501 ++++++++++++++++++++++++++++++++++ s96at/src/sha.c | 46 ++++ 9 files changed, 1035 insertions(+), 37 deletions(-) delete mode 100644 s96at/include/defines.h create mode 100644 s96at/include/s96at.h create mode 100644 s96at/include/s96at_private.h create mode 100644 s96at/include/sha.h delete mode 100644 s96at/src/main.c create mode 100644 s96at/src/s96at.c create mode 100644 s96at/src/sha.c diff --git a/s96at/CMakeLists.txt b/s96at/CMakeLists.txt index b98011b..eb95eb6 100644 --- a/s96at/CMakeLists.txt +++ b/s96at/CMakeLists.txt @@ -1,5 +1,5 @@ #set(CMAKE_TOOLCHAIN_FILE CMakeToolchain.txt) -project(s96-204 C) +project(s96at C) # Required cmake version cmake_minimum_required(VERSION 3.0.2) @@ -11,19 +11,22 @@ MESSAGE(STATUS "CMAKE_C_COMPILER: " ${CMAKE_C_COMPILER}) add_compile_options(-Wall -Werror) include_directories(include) -set(SRC ${CMAKE_SOURCE_DIR}/src/cmd.c +set(SRC ${CMAKE_SOURCE_DIR}/src/s96at.c + ${CMAKE_SOURCE_DIR}/src/cmd.c ${CMAKE_SOURCE_DIR}/src/crc.c ${CMAKE_SOURCE_DIR}/src/debug.c ${CMAKE_SOURCE_DIR}/src/io.c ${CMAKE_SOURCE_DIR}/src/i2c_linux.c - ${CMAKE_SOURCE_DIR}/src/packet.c) + ${CMAKE_SOURCE_DIR}/src/packet.c + ${CMAKE_SOURCE_DIR}/src/sha.c) set(I2C_DEVICE "/dev/i2c-0") #add_definitions(-DEXT_DEBUG_INFO) add_definitions(-DCMAKE_BUILD_TYPE=Debug) -add_executable(${PROJECT_NAME} ${SRC} ${CMAKE_SOURCE_DIR}/src/main.c) +add_library(${PROJECT_NAME} SHARED ${SRC}) +set_target_properties(${PROJECT_NAME} PROPERTIES PUBLIC_HEADER include/s96at.h) target_compile_definitions(${PROJECT_NAME} PRIVATE -DI2C_DEVICE="${I2C_DEVICE}" diff --git a/s96at/include/cmd.h b/s96at/include/cmd.h index a216588..0c7addf 100644 --- a/s96at/include/cmd.h +++ b/s96at/include/cmd.h @@ -21,22 +21,17 @@ enum { #define ZONE_OTP_SIZE 64 #define ZONE_DATA_SIZE 512 +#define ZONE_CONFIG_NUM_WORDS 22 +#define ZONE_DATA_NUM_SLOTS 16 +#define ZONE_OTP_NUM_WORDS 16 + #define LOCK_CONFIG_LOCKED 0x0 #define LOCK_CONFIG_UNLOCKED 0x55 #define LOCK_DATA_LOCKED 0x0 #define LOCK_DATA_UNLOCKED 0x55 -/* Sizes for out parameter (RandOut) */ -#define NONCE_SHORT_LEN 1 -#define NONCE_LONG_LEN 32 - -/* Sizes for in parameter (NumIn) */ -#define NONCE_SHORT_NUMIN 20 -#define NONCE_LONG_NUMIN 32 - -#define NONCE_MODE_UPDATE_SEED 0 -#define NONCE_MODE_NO_SEED 1 -#define NONCE_MODE_PASSTHROUGH 3 +#define TEMPKEY_SOURCE_RANDOM 0 +#define TEMPKEY_SOURCE_INPUT 1 #define HMAC_LEN 32 #define RANDOM_LEN 32 @@ -44,12 +39,23 @@ enum { #define SERIALNUM_LEN 9 #define MAC_LEN 32 #define SHA_LEN 32 -#define SHA_BLOCK_LEN 64 #define WORD_SIZE 4 #define MAX_READ_SIZE 32 /* bytes */ #define MAX_WRITE_SIZE 32 +#define MAC_MODE_TEMPKEY_SOURCE_SHIFT 2 +#define MAC_MODE_USE_OTP_88_BITS_SHIFT 4 +#define MAC_MODE_USE_OTP_64_BITS_SHIFT 5 +#define MAC_MODE_USE_SN_SHIFT 6 + +#define NONCE_MODE_RANDOM 0 +#define NONCE_MODE_RANDOM_NO_SEED 1 +#define NONCE_MODE_PASSTHROUGH 3 + +#define SHA_MODE_INIT 0x00 +#define SHA_MODE_COMPUTE 0x01 + /* Word address values */ #define PKT_FUNC_RESET 0x0 #define PKT_FUNC_SLEEP 0x1 diff --git a/s96at/include/defines.h b/s96at/include/defines.h deleted file mode 100644 index 51b8d51..0000000 --- a/s96at/include/defines.h +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright 2017, Linaro Ltd and contributors - * SPDX-License-Identifier: Apache-2.0 - */ -#ifndef __DEFINES_H -#define __DEFINES_H - -/* FIXME: CRC related functionality needs to go into separate files later on */ -#define CRC_LEN 2 /* In bytes */ - -#endif - - diff --git a/s96at/include/s96at.h b/s96at/include/s96at.h new file mode 100644 index 0000000..14f86cd --- /dev/null +++ b/s96at/include/s96at.h @@ -0,0 +1,431 @@ +/* + * Copyright 2017, Linaro Ltd and contributors + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef __S96AT_H +#define __S96AT_H + +#include + +#include + +#define S96AT_STATUS_OK 0x00 +#define S96AT_STATUS_CHECKMAC_FAIL 0x01 +#define S96AT_STATUS_EXEC_ERROR 0x0f +#define S96AT_STATUS_PADDING_ERROR 0x98 +#define S96AT_STATUS_BAD_PARAMETERS 0x99 + +#define S96AT_WATCHDOG_TIME 1700 /* msec */ + +#define S96AT_CHALLENGE_LEN 32 +#define S96AT_DEVREV_LEN 4 +#define S96AT_GENDIG_INPUT_LEN 4 +#define S96AT_HMAC_LEN 32 +#define S96AT_KEY_LEN 32 +#define S96AT_MAC_LEN 32 +#define S96AT_NONCE_INPUT_LEN 20 +#define S96AT_RANDOM_LEN 32 +#define S96AT_READ_CONFIG_LEN 4 +#define S96AT_READ_DATA_LEN 32 +#define S96AT_READ_OTP_LEN 4 +#define S96AT_SERIAL_NUMBER_LEN 9 +#define S96AT_SHA_LEN 32 +#define S96AT_WRITE_CONFIG_LEN 4 +#define S96AT_WRITE_DATA_LEN 32 +#define S96AT_WRITE_OTP_LEN 4 +#define S96AT_ZONE_CONFIG_LEN 88 +#define S96AT_ZONE_DATA_LEN 512 +#define S96AT_ZONE_OTP_LEN 64 + +#define S96AT_FLAG_NONE 0x00 +#define S96AT_FLAG_TEMPKEY_SOURCE_INPUT 0x01 +#define S96AT_FLAG_TEMPKEY_SOURCE_RANDOM 0x02 +#define S96AT_FLAG_USE_OTP_64_BITS 0x04 +#define S96AT_FLAG_USE_OTP_88_BITS 0x08 +#define S96AT_FLAG_USE_SN 0x10 +#define S96AT_FLAG_ENCRYPT 0x12 + +#define S96AT_ZONE_LOCKED 0x00 +#define S96AT_ZONE_UNLOCKED 0x55 + +#define S96AT_OTP_MODE_LEGACY 0x00 +#define S96AT_OTP_MODE_CONSUMPTION 0x55 +#define S96AT_OTP_MODE_READONLY 0xAA + +enum s96at_device { + S96AT_ATSHA204A, +}; + +enum s96at_io_interface_type { + S96AT_IO_I2C_LINUX +}; + +enum s96at_zone { + S96AT_ZONE_CONFIG, + S96AT_ZONE_OTP, + S96AT_ZONE_DATA +}; + +struct s96at_check_mac_data { + const uint8_t *challenge; + uint8_t slot; + uint32_t flags; + const uint8_t *otp; + const uint8_t *sn; +}; + +enum s96at_mac_mode { + S96AT_MAC_MODE_0, /* 1st 32 bytes: Slot, 2nd 32 bytes: Input Challenge */ + S96AT_MAC_MODE_1, /* 1st 32 bytes: Slot, 2nd 32 bytes: TempKey */ + S96AT_MAC_MODE_2, /* 1st 32 bytes: TempKey, 2nd 32 bytes: Input Challenge */ + S96AT_MAC_MODE_3 /* 1st 32 bytes: TempKey, 2nd 32 bytes: TempKey */ +}; + +enum s96at_nonce_mode { + S96AT_NONCE_MODE_RANDOM, + S96AT_NONCE_MODE_RANDOM_NO_SEED, + S96AT_NONCE_MODE_PASSTHROUGH = 0x03 +}; + +enum s96at_random_mode { + S96AT_RANDOM_MODE_UPDATE_SEED, + S96AT_RANDOM_MODE_UPDATE_NO_SEED +}; + +/* Check a MAC generated by another device + * + * Generates a MAC and compares it with the value stored in the mac buffer. + * This is normally used to verify a MAC generated using s96at_get_mac() + * during a challenge-response communication between a host and a client, + * where a host sends a challenge and the client responds with a MAC that + * is subsequently verified by the host. + * The s96at_check_mac_data structure contains the parameters used by the + * client to generate the response. For more information see s96at_get_mac(). + * The slot parameter specifies the slot that contains the key to use by the + * host when calculating the response. In the flags parameter it is required + * to specify input source of TempKey. + * + * Returns S96AT_STATUS_OK on success. A failed comparison returns + * S96_STATUS_CHECKMAC_FAIL. Other errors return S96AT_EXECUTION_ERROR. + */ +uint8_t s96at_check_mac(struct s96at_desc *desc, enum s96at_mac_mode mode, + uint8_t slot, uint32_t flags, struct s96at_check_mac_data *data, + const uint8_t *mac); + +/* Clean up a device descriptor + * + * Unregisters the device from the io interface. + */ +uint8_t s96at_cleanup(struct s96at_desc *desc); + +/* Calculate a CRC value + * + * Calculates the CRC of the data stored in buf, using the same CRC-16 + * polynomial used by the device. If a non-zero value is passed to the + * current_crc parameter, the algorithm will use that as the initial value. + * This is useful if the CRC cannot be calculated in one go. + * + * Returns the calculated CRC value. + */ +uint16_t s96at_crc(const uint8_t *buf, size_t buf_len, uint16_t current_crc); + +/* Derive a key + * + * Derives a new key by combining the value stored in a slot with a nonce + * and storing its SHA256 hash into the target slot. Depending on how the + * target slot is configured, this function will use the appropriate input + * key: + * + * For slots configured into Create mode, the parent key is used. + * For slots configured into Rolling mode, the current key is used. + * + * If required by the configuration, an authorizing MAC can be sent along, + * through the mac buffer. + * + * The flags parameter must specify the input source of TempKey as defined + * when executing the Nonce command. + * + * Returns S96AT_STATUS_OK on success, otherwise S96AT_STATUS_EXEC_ERROR. + */ +uint8_t s96at_derive_key(struct s96at_desc *desc, uint8_t slot, uint8_t *mac, + uint32_t flags); + +/* Get Device Revision + * + * Retrieves the device revision and stores it into the buffer pointed by + * buf. The buffer length must be S96AT_DEVREV_LEN. + * + * Returns S96AT_STATUS_OK on success, otherwise S96AT_STATUS_EXEC_ERROR. + */ +uint8_t s96at_get_devrev(struct s96at_desc *desc, uint8_t *buf); + +/* Generate a digest + * + * Generate a SHA-256 digest combining the value stored in TempKey with + * a value stored in the device. The input value is defined by the zone + * and slot parameters. The value of data must be NULL. + * + * For keys configured as CheckOnly, it is possible to generate the + * digest using the value stored in the data buffer instead of a value + * stored in the device. This is normally used to generate euphemeral + * keys. When this operation is required, a pointer must be passed to the + * data parameter pointing to a buffer that contains the input data. In + * this case, the zone and slot values are ignored. + * + * In both cases, the generated digest is stored in TempKey and it can + * be used to combine the Nonce with an additional value before executing + * the MAC / CheckMAC / HMAC commands. + * + * Returns S96AT_STATUS_OK on success, otherwise S96AT_STATUS_EXEC_ERROR. + */ +uint8_t s96at_gen_digest(struct s96at_desc *desc, enum s96at_zone zone, + uint8_t slot, uint8_t *data); + +/* Generate an HMAC-SHA256 + * + * Generates an HMAC. To generate an HMAC, it is first required to load + * an input challenge into TempKey using Nonce and optionally GenDigest. + * The value in TempKey is then combined along with the key stored in slot + * to generate an HMAC. + * + * Flags control whether other intput values should be included in + * the input message. These are: + * + * S96AT_FLAG_USE_OTP_64_BITS Include OTP[0:7] + * S96AT_FLAG_USE_OTP_88_BITS Include OTP[0:10] + * S96AT_FLAG_USE_SN Include SN[2:3] and SN[4:7] + * + * The resulting HMAC is written into the hmac buffer. + * + * Returns S96AT_STATUS_OK on success, otherwise S96AT_STATUS_EXEC_ERROR. + */ +uint8_t s96at_get_hmac(struct s96at_desc *desc, uint8_t slot, + uint32_t flags, uint8_t *hmac); + +/* Get the lock status of the Config Zone + * + * Reads the lock status of the Config Zone into lock_config. + * Possible values are: + * + * S96AT_LOCK_CONFIG_LOCKED + * S96AT_LOCK_CONFIG_UNLOCKED + * + * Returns S96AT_STATUS_OK on success, otherwise S96AT_STATUS_EXEC_ERROR. + */ +uint8_t s96at_get_lock_config(struct s96at_desc *desc, uint8_t *lock_config); + +/* Get the lock status of the Data / OTP zone + * + * Reads the lock status of the Data / OTP zone into lock_data. + * Possible values are: + * + * S96AT_LOCK_DATA_LOCKED + * S96AT_LOCK_DATA_UNLOCKED + * + * Returns S96AT_STATUS_OK on success, otherwise S96AT_STATUS_EXEC_ERROR. + */ +uint8_t s96at_get_lock_data(struct s96at_desc *desc, uint8_t *lock_data); + +/* Generate a MAC + * + * Generates a MAC. To generate a MAC, it is first required to load + * an input challenge into TempKey using Nonce and optionally GenDigest. + * The value in TempKey is then combined along with the key stored in slot + * to generate a MAC. + * + * The mode parameter specifies which fields to use to form the input message: + * + * S96AT_MAC_MODE_0 1st 32 bytes from a slot, 2nd 32 bytes from the input challenge + * S96AT_MAC_MODE_1 1st 32 bytes from a slot, 2nd 32 bytes from TempKey + * S96AT_MAC_MODE_2 1st 32 bytes from TempKey 2nd 32 bytes from the input challenge + * S96AT_MAC_MODE_3 1st 32 bytes from TempKey 2nd 32 bytes from TempKey + * + * When S96AT_MAC_MODE_2 or S96AT_MAC_MODE_3 is used, the slot parameter is ignored. + * When S96AT_MAC_MODE_1 or S96AT_MAC_MODE_3 is used, the input challenge parameter + * is ignored. + * + * Flags control whether other intput values should be included in + * the input message. These are: + * + * S96AT_FLAG_USE_OTP_64_BITS Include OTP[0:7] + * S96AT_FLAG_USE_OTP_88_BITS Include OTP[0:10] + * S96AT_FLAG_USE_SN Include SN[2:3] and SN[4:7] + * + * The resulting MAC is written into the mac buffer. + * + * Returns S96AT_STATUS_OK on success, otherwise S96AT_STATUS_EXEC_ERROR. + */ +uint8_t s96at_get_mac(struct s96at_desc *desc, enum s96at_mac_mode mode, uint8_t slot, + const uint8_t *challenge, uint32_t flags, uint8_t *mac); + +/* Generate a nonce + * + * Generates a nonce. The operation mode is specified by the mode + * parameter. When operating in Passthrough mode, the input value + * in the data buffer is stored directly in TempKey. + * When operating in Random Mode, an input value is combined with + * a random number and the resulting hash is stored in TempKey. + * + * When Random Mode is used, the default behaviour is to update + * the seed before generating the random number. This behaviour + * can be overriden when using S96AT_REANDOM_MODE_NO_SEED. + * + * The value produced by the RNG is stored into buf. + * + * Returns S96AT_STATUS_OK on success, otherwise S96AT_STATUS_EXEC_ERROR. + */ +uint8_t s96at_gen_nonce(struct s96at_desc *desc, enum s96at_nonce_mode mode, + uint8_t *data, uint8_t *random); + +/* Get the OTP Mode + * + * Reads the OTP Mode into otp_mode. Possible values are: + * + * S96AT_OTP_MODE_CONSUMPTION + * S96AT_OTP_MODE_LEGACY + * S96AT_OTP_MODE_READ_ONLY + * + * Returns S96AT_STATUS_OK on success, otherwise S96AT_STATUS_EXEC_ERROR. + */ +uint8_t s96at_get_otp_mode(struct s96at_desc *desc, uint8_t *opt_mode); + +/* Send the Pause command + * + * Upon receiving the Pause command, devices with Selector byte in the + * configuration that do NOT match the selector parameter, will enter the + * Idle State. This is useful for avoiding conflicts when multiple devices + * are used on the same bus. + * + * A device that does not enter the idle state returns S96AT_STATUS_OK. + */ +uint8_t s96at_pause(struct s96at_desc *desc, uint8_t selector); + +/* Generate a random number + * + * Random numbers are generated by combining the output of a hardware RNG + * with an internally stored seed value. The generated number is stored in + * the buffer pointed by buf. The length of the buffer must be at least + * S96AT_RANDOM_LEN. + * + * Before generating a new number, the interal seed is updated by default. + * This can be overriden by using S96AT_RANDOM_MODE_UPDATE_NO_SEED. + * + * Returns S96AT_STATUS_OK on success, otherwise S96AT_STATUS_EXEC_ERROR. + */ +uint8_t s96at_get_random(struct s96at_desc *desc, enum s96at_random_mode mode, + uint8_t *buf); + +/* Get the device's Serial Number + * + * Reads the device's Serial Number into buf. The buffer must be + * at least S96AT_SERIALNUM_LEN long. + * + * Returns S96AT_STATUS_OK on success, otherwise S96AT_STATUS_EXEC_ERROR. + */ +uint8_t s96at_get_serialnbr(struct s96at_desc *desc, uint8_t *serial); + +/* Generate a hash (SHA-256) + * + * Generates a SHA-256 hash of the input message contained in buf. + * The message length is specified in msg_len. The input buffer's length + * must be a multiple of S96AT_SHA_BLOCK_LEN and it is modified + * in place to contain the SHA padding, as defined in FIPS 180-2. The + * input buffer is therefore required to have enough room for the padding. + * + * The resulting hash is stored in the hash buffer. + * + * Returns S96AT_STATUS_OK on success, otherwise S96AT_STATUS_EXEC_ERROR. + */ +uint8_t s96at_get_sha(struct s96at_desc *desc, uint8_t *buf, + size_t buf_len, size_t msg_len, uint8_t *hash); + +/* Initialize a device descriptor + * + * Selects a device and registers with an io interface. Upon successful initialization, + * the descriptor can be used in subsequent operations. + * + * Returns S96AT_STATUS_OK on success, otherwise S96AT_STATUS_EXEC_ERROR. + */ +uint8_t s96at_init(enum s96at_device device_type, enum s96at_io_interface_type iface, + struct s96at_desc *desc); + +/* Lock a zone + * + * Locks a zone specified by the zone parameter. Device personalization requires + * the following steps: The configuraton zone is first programmed and locked. After + * the configuration zone is locked, the Data and OTP areas are programmed. + * The Data and OTP zones are then locked in a single operation, by setting the zone + * parameter to S96AT_ZONE_DATA. Specifying S96AT_ZONE_OTP returns an error. + * + * Notice that locking a zone is a one-time operation. + * + * Returns S96AT_STATUS_OK on success, otherwise S96AT_STATUS_EXEC_ERROR. + */ +uint8_t s96at_lock_zone(struct s96at_desc *desc, enum s96at_zone zone, uint16_t crc); + +/* Read from a zone + * + * The Config zone consists of 88 bytes divided into 22x 4-byte words. The id + * parameter specifies which word to be read, in the range of 0-21. The buffer + * length must be S96AT_READ_CONFIG_LEN. Reading from the Config zone is always + * permitted. + * + * The Data zone consists of 512 bytes divided into 16x 32-byte slots. The id + * parameter specifies which slot to be read, in the range of 0-15. The buffer + * length must be S96AT_READ_DATA_LEN. Reading is permitted only after the Data + * zone has been locked. The permissions to read depends on the slot's configuration. + * Reads from the Data zone can be encrypted if the EncryptedRead bit is set + * in the slot's configuration. To perform an encrypted read, GenDig must be run + * before reading data to set the encryption key. + * + * The OTP zone consists of 64 bytes divided into 16x 4-byte words. The id + * parameter specifies which word to be read in the range of 0 - 15. The buffer + * length must be S96AT_READ_OTP_LEN. Reading is permitted only after the OTP + * zone has been locked. If the OTP zone is configured into Legacy mode, reading + * from words 0 or 1 returns an error. + * + * Returns S96AT_STATUS_OK on success, otherwise S96AT_STATUS_EXEC_ERROR. + */ +uint8_t s96at_read(struct s96at_desc *desc, enum s96at_zone zone, uint8_t id, uint8_t *buf); + +/* Wake up the device + * + * Wakes up the device by sending the wake-up sequence. Upon wake up, a watchdog + * counter is triggered, which keeps the device awake for S96AT_WATCHDOG_TIME. + * Once the counter reaches zero, the device enters sleep mode, regardless of its + * current command execution or IO state. It is therefore required that all operations + * are completed within S96AT_WATCHDOG_TIME. + * + * Returns S96AT_STATUS_OK on success, otherwise S96AT_STATUS_EXEC_ERROR. + */ +uint8_t s96at_wake(struct s96at_desc *desc); + +/* Write to a zone + * + * The Config zone consists of 88 bytes divided into 22x 4-byte words. The id + * parameter specifies which word to be written, in the range of 0-21. The buffer + * length must be S96AT_WRITE_CONFIG_LEN. Writing to the Config zone is only + * permitted before the zone is locked. + * + * The Data zone consists of 512 bytes divided into 16x 32-byte slots. The id + * parameter specifies which slot to be write, in the range of 0-15. The buffer + * length must be S96AT_WRITE_DATA_LEN. Writing to the Data zone is allowed once + * the Configuration zone has been locked, and before the Data zone has been locked. + * After the Data zone is locked, writing to a slot depends on the permissions set + * on the slot's configuration. Writes to the Data zone can be encrypted, if + * S96AT_FLAG_ENCRYPT is set. + * + * The OTP zone consists of 64 bytes divided into 16x 4-byte words. The id + * parameter specifies which word to be written, in the range of 0 - 15. The buffer + * length must be S96AT_WRITE_OTP_LEN. Writing is permitted only after the Configuration + * zone has been locked and before the OTP zone has been locked. NOTICE THAT WRITING + * INTO THE OTP ZONE IS A ONE-TIME OPERATION. Once the OTP area has been locked, writing + * is only permitted if the zone has been configured in Consumption mode, in which case + * bits can only be changed from zero to one. + * + * Returns S96AT_STATUS_OK on success, otherwise S96AT_STATUS_EXEC_ERROR. + */ +uint8_t s96at_write(struct s96at_desc *desc, enum s96at_zone zone, uint8_t id, + uint32_t flags, const uint8_t *buf); + +#endif diff --git a/s96at/include/s96at_private.h b/s96at/include/s96at_private.h new file mode 100644 index 0000000..edfa3c4 --- /dev/null +++ b/s96at/include/s96at_private.h @@ -0,0 +1,15 @@ +/* + * Copyright 2017, Linaro Ltd and contributors + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef __S96AT_PRIVATE_H +#define __S96AT_PRIVATE_H + +#include + +struct s96at_desc { + uint8_t dev; + struct io_interface *ioif; +}; + +#endif diff --git a/s96at/include/sha.h b/s96at/include/sha.h new file mode 100644 index 0000000..efe9004 --- /dev/null +++ b/s96at/include/sha.h @@ -0,0 +1,17 @@ +/* + * Copyright 2017, Linaro Ltd and contributors + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef __SHA_H +#define __SHA_H + +#define SHA_BLOCK_LEN 64 +#define SHA_PADDING_LENGTH_LEN 8 + +/* Apply SHA padding as defined in FIPS 180-2. + * The message buffer is modified in-place. + */ +int sha_apply_padding(uint8_t *buf, size_t buf_len, size_t msg_len, + size_t *padded_msg_len); + +#endif diff --git a/s96at/src/main.c b/s96at/src/main.c deleted file mode 100644 index 8129250..0000000 --- a/s96at/src/main.c +++ /dev/null @@ -1,8 +0,0 @@ -/* - * Copyright 2017, Linaro Ltd and contributors - * SPDX-License-Identifier: Apache-2.0 - */ -int main(int argc, char *argv[]) -{ - return 0; -} diff --git a/s96at/src/s96at.c b/s96at/src/s96at.c new file mode 100644 index 0000000..8ca11ac --- /dev/null +++ b/s96at/src/s96at.c @@ -0,0 +1,501 @@ +/* + * Copyright 2017, Linaro Ltd and contributors + * SPDX-License-Identifier: Apache-2.0 + */ +#include + +#include +#include +#include +#include +#include +#include + +uint8_t s96at_init(enum s96at_device device, enum s96at_io_interface_type iface, + struct s96at_desc *desc) +{ + uint8_t ret; + + desc->dev = device; + + ret = register_io_interface(IO_I2C_LINUX, &desc->ioif); + if (ret != STATUS_OK) + return ret; + + ret = at204_open(desc->ioif); + + return ret; +} + +uint8_t s96at_cleanup(struct s96at_desc *desc) +{ + uint8_t ret = S96AT_STATUS_OK; + + if (desc->ioif) + ret = at204_close(desc->ioif); + + return ret; +} + +uint8_t s96at_wake(struct s96at_desc *desc) +{ + if (cmd_wake(desc->ioif) == true) + return S96AT_STATUS_OK; + else + return S96AT_STATUS_EXEC_ERROR; +} + +uint8_t s96at_derive_key(struct s96at_desc *desc, uint8_t slot, uint8_t *mac, + uint32_t flags) +{ + size_t len; + uint8_t tempkey_source; + + if ((flags != S96AT_FLAG_TEMPKEY_SOURCE_INPUT) && + (flags != S96AT_FLAG_TEMPKEY_SOURCE_RANDOM)) + return S96AT_STATUS_BAD_PARAMETERS; + + if (mac) + len = S96AT_MAC_LEN; + else + len = 0; + + if (flags & S96AT_FLAG_TEMPKEY_SOURCE_INPUT) + tempkey_source = (TEMPKEY_SOURCE_INPUT << MAC_MODE_TEMPKEY_SOURCE_SHIFT); + + if (flags & S96AT_FLAG_TEMPKEY_SOURCE_RANDOM) + tempkey_source = (TEMPKEY_SOURCE_RANDOM << MAC_MODE_TEMPKEY_SOURCE_SHIFT); + + return cmd_derive_key(desc->ioif, tempkey_source, slot, mac, len); +} + +uint8_t s96at_pause(struct s96at_desc *desc, uint8_t selector) +{ + return cmd_pause(desc->ioif, selector); +} + +uint16_t s96at_get_crc(const uint8_t *buf, size_t buf_len, uint16_t current_crc) +{ + return calculate_crc16(buf, buf_len, current_crc); +} + +uint8_t s96at_get_random(struct s96at_desc *desc, enum s96at_random_mode mode, + uint8_t *buf) +{ + uint8_t ret; + + ret = cmd_get_random(desc->ioif, mode, buf, S96AT_RANDOM_LEN); + + if (ret != STATUS_OK) + memset(buf, 0, S96AT_RANDOM_LEN); + + return ret; +} + +uint8_t s96at_get_devrev(struct s96at_desc *desc, uint8_t *buf) +{ + return cmd_get_devrev(desc->ioif, buf, S96AT_DEVREV_LEN); +} + +uint8_t s96at_gen_digest(struct s96at_desc *desc, enum s96at_zone zone, + uint8_t slot, uint8_t *data) +{ + size_t data_len; + if (data) + data_len = S96AT_GENDIG_INPUT_LEN; + else + data_len = 0; + + return cmd_gen_dig(desc->ioif, data, data_len, zone, slot); +} + +uint8_t s96at_gen_nonce(struct s96at_desc *desc, enum s96at_nonce_mode mode, + uint8_t *data, uint8_t *random) +{ + uint8_t ret; + uint8_t *out; + uint8_t nonce_resp; + size_t out_len; + size_t data_len; + + if (!data) + return S96AT_STATUS_BAD_PARAMETERS; + + if (mode == S96AT_NONCE_MODE_RANDOM && !random) + return S96AT_STATUS_BAD_PARAMETERS; + + if (mode == S96AT_NONCE_MODE_PASSTHROUGH) { + data_len = S96AT_CHALLENGE_LEN; + out = &nonce_resp; + out_len = sizeof(nonce_resp); + } else { + data_len = S96AT_NONCE_INPUT_LEN; + out = random; + out_len = S96AT_RANDOM_LEN; + } + + ret = cmd_get_nonce(desc->ioif, data, data_len, mode, out, out_len); + + if (ret != STATUS_OK && random) + memset(random, 0, S96AT_RANDOM_LEN); + + return ret; +} + +uint8_t s96at_get_mac(struct s96at_desc *desc, enum s96at_mac_mode mode, uint8_t slot, + const uint8_t *challenge, uint32_t flags, uint8_t *mac) +{ + uint8_t ret; + uint8_t challenge_len; + + if ((mode == S96AT_MAC_MODE_0 || mode == S96AT_MAC_MODE_2) && !challenge) + return S96AT_STATUS_BAD_PARAMETERS; + + if ((flags & S96AT_FLAG_TEMPKEY_SOURCE_INPUT) && + (flags & S96AT_FLAG_TEMPKEY_SOURCE_RANDOM)) + return S96AT_STATUS_BAD_PARAMETERS; + + if (!(flags & S96AT_FLAG_TEMPKEY_SOURCE_INPUT) && + !(flags & S96AT_FLAG_TEMPKEY_SOURCE_RANDOM)) + return S96AT_STATUS_BAD_PARAMETERS; + + if ((flags & S96AT_FLAG_USE_OTP_64_BITS) && (flags & S96AT_FLAG_USE_OTP_88_BITS)) + return S96AT_STATUS_BAD_PARAMETERS; + + if (mode == S96AT_MAC_MODE_0 || mode == S96AT_MAC_MODE_2) + challenge_len = S96AT_CHALLENGE_LEN; + else + challenge_len = 0; + + if (flags & S96AT_FLAG_TEMPKEY_SOURCE_INPUT) + mode |= (TEMPKEY_SOURCE_INPUT << MAC_MODE_TEMPKEY_SOURCE_SHIFT); + + if (flags & S96AT_FLAG_TEMPKEY_SOURCE_RANDOM) + mode |= (TEMPKEY_SOURCE_RANDOM << MAC_MODE_TEMPKEY_SOURCE_SHIFT); + + if (flags & S96AT_FLAG_USE_OTP_64_BITS) + mode |= (1 << MAC_MODE_USE_OTP_64_BITS_SHIFT); + + if (flags & S96AT_FLAG_USE_OTP_88_BITS) + mode |= (1 << MAC_MODE_USE_OTP_88_BITS_SHIFT); + + if (flags & S96AT_FLAG_USE_SN) + mode |= (1 << MAC_MODE_USE_SN_SHIFT); + + ret = cmd_get_mac(desc->ioif, (uint8_t *)challenge, challenge_len, mode, slot, + mac, S96AT_MAC_LEN); + + if (ret != STATUS_OK) + memset(mac, 0, S96AT_MAC_LEN); + + return ret; +} + +uint8_t s96at_check_mac(struct s96at_desc *desc, enum s96at_mac_mode mode, + uint8_t slot, uint32_t flags, struct s96at_check_mac_data *data, + const uint8_t *mac) +{ + uint8_t ret; + uint8_t check_mac_data[77] = { 0 }; + uint8_t check_mac_resp; + uint8_t mac_mode = mode; + + /* Mode used in CheckMac */ + if ((mode == S96AT_MAC_MODE_0 || mode == S96AT_MAC_MODE_2) && !data->challenge) + return S96AT_STATUS_BAD_PARAMETERS; + + if (flags & S96AT_FLAG_TEMPKEY_SOURCE_INPUT) + mode |= (TEMPKEY_SOURCE_INPUT << MAC_MODE_TEMPKEY_SOURCE_SHIFT); + + if (flags & S96AT_FLAG_TEMPKEY_SOURCE_RANDOM) + mode |= (TEMPKEY_SOURCE_RANDOM << MAC_MODE_TEMPKEY_SOURCE_SHIFT); + + /* Mode used in MAC */ + if ((mac_mode == S96AT_MAC_MODE_0 || mac_mode == S96AT_MAC_MODE_2) && !data->challenge) + return S96AT_STATUS_BAD_PARAMETERS; + + if (data->flags & S96AT_FLAG_TEMPKEY_SOURCE_INPUT) + mac_mode |= (TEMPKEY_SOURCE_INPUT << MAC_MODE_TEMPKEY_SOURCE_SHIFT); + + if (data->flags & S96AT_FLAG_TEMPKEY_SOURCE_RANDOM) + mac_mode |= (TEMPKEY_SOURCE_RANDOM << MAC_MODE_TEMPKEY_SOURCE_SHIFT); + + if (data->flags & S96AT_FLAG_USE_OTP_64_BITS) + mac_mode |= (1 << MAC_MODE_USE_OTP_64_BITS_SHIFT); + + if (data->flags & S96AT_FLAG_USE_OTP_88_BITS) + mac_mode |= (1 << MAC_MODE_USE_OTP_88_BITS_SHIFT); + + if (data->flags & S96AT_FLAG_USE_SN) + mac_mode |= (1 << MAC_MODE_USE_SN_SHIFT); + + /* Challenge sent to the client */ + memcpy(check_mac_data, data->challenge, S96AT_CHALLENGE_LEN); + + /* Response generated by the client */ + memcpy(check_mac_data + S96AT_CHALLENGE_LEN, mac, S96AT_MAC_LEN); + + /* OtherData contains the parameters used for the MAC command */ + check_mac_data[64] = OPCODE_MAC; /* Opcode */ + check_mac_data[65] = mac_mode; + check_mac_data[66] = 0x00; /* Slot ID MSB */ + check_mac_data[67] = data->slot; /* Slot ID LSB */ + + if (data->otp) { + check_mac_data[68] = data->otp[8]; + check_mac_data[69] = data->otp[9]; + check_mac_data[70] = data->otp[10]; + } + + if (data->sn) { + check_mac_data[71] = data->sn[4]; + check_mac_data[72] = data->sn[5]; + check_mac_data[73] = data->sn[6]; + check_mac_data[74] = data->sn[7]; + check_mac_data[75] = data->sn[2]; + check_mac_data[76] = data->sn[3]; + } + + ret = cmd_check_mac(desc->ioif, check_mac_data, 77, mode, slot, + &check_mac_resp, sizeof(check_mac_resp)); + if (ret == STATUS_OK) + ret = check_mac_resp; + + return ret; +} + +uint8_t s96at_get_hmac(struct s96at_desc *desc, uint8_t slot, uint32_t flags, + uint8_t *hmac) +{ + uint8_t mode = 0; + + if (flags & S96AT_FLAG_TEMPKEY_SOURCE_INPUT) + mode |= (TEMPKEY_SOURCE_INPUT << MAC_MODE_TEMPKEY_SOURCE_SHIFT); + + if (flags & S96AT_FLAG_TEMPKEY_SOURCE_RANDOM) + mode |= (TEMPKEY_SOURCE_RANDOM << MAC_MODE_TEMPKEY_SOURCE_SHIFT); + + if (flags & S96AT_FLAG_USE_OTP_64_BITS) + mode |= (1 << MAC_MODE_USE_OTP_64_BITS_SHIFT); + + if (flags & S96AT_FLAG_USE_OTP_88_BITS) + mode |= (1 << MAC_MODE_USE_OTP_88_BITS_SHIFT); + + if (flags & S96AT_FLAG_USE_SN) + mode |= (1 << MAC_MODE_USE_SN_SHIFT); + + return cmd_get_hmac(desc->ioif, mode, slot, hmac); +} + +uint8_t s96at_get_lock_config(struct s96at_desc *desc, uint8_t *lock_config) +{ + uint8_t _lock_config; + int ret = STATUS_EXEC_ERROR; + + ret = cmd_read(desc->ioif, ZONE_CONFIG, LOCK_CONFIG_ADDR, LOCK_CONFIG_OFFSET, + WORD_SIZE, &_lock_config, LOCK_CONFIG_SIZE); + + if (ret == STATUS_OK) + *lock_config = _lock_config; + else + *lock_config = 0; + + return ret; +} + +uint8_t s96at_get_lock_data(struct s96at_desc *desc, uint8_t *lock_data) +{ + uint8_t _lock_data = 0; + int ret = STATUS_EXEC_ERROR; + + ret = cmd_read(desc->ioif, ZONE_CONFIG, LOCK_DATA_ADDR, LOCK_DATA_OFFSET, + WORD_SIZE, &_lock_data, LOCK_DATA_SIZE); + + if (ret == STATUS_OK) + *lock_data = _lock_data; + else + *lock_data = 0; + + return ret; +} + +uint8_t s96at_get_otp_mode(struct s96at_desc *desc, uint8_t *otp_mode) +{ + uint32_t _otp_mode = 0; + int ret = STATUS_EXEC_ERROR; + + if (!otp_mode) + return ret; + + ret = cmd_read(desc->ioif, ZONE_CONFIG, OTP_CONFIG_ADDR, OTP_CONFIG_OFFSET, + WORD_SIZE, &_otp_mode, OTP_CONFIG_SIZE); + + *otp_mode = _otp_mode & 0xFF; + + return ret; +} + +uint8_t s96at_get_serialnbr(struct s96at_desc *desc, uint8_t *buf) +{ + int ret = STATUS_EXEC_ERROR; + uint8_t serial_nbr[S96AT_SERIAL_NUMBER_LEN] = { 0 }; + + if (!buf) + return S96AT_STATUS_BAD_PARAMETERS; + + ret = cmd_read(desc->ioif, ZONE_CONFIG, SERIALNBR_ADDR0_3, + SERIALNBR_OFFSET0_3, WORD_SIZE, serial_nbr, + SERIALNBR_SIZE0_3); + + if (ret != STATUS_OK) + goto err; + + ret = cmd_read(desc->ioif, ZONE_CONFIG, SERIALNBR_ADDR4_7, + SERIALNBR_OFFSET4_7, WORD_SIZE, serial_nbr + + SERIALNBR_SIZE0_3, SERIALNBR_SIZE4_7); + + if (ret != STATUS_OK) + goto err; + + ret = cmd_read(desc->ioif, ZONE_CONFIG, SERIALNBR_ADDR8, SERIALNBR_OFFSET8, + WORD_SIZE, serial_nbr + SERIALNBR_SIZE0_3 + SERIALNBR_SIZE4_7, + SERIALNBR_SIZE8); +err: + if (ret == STATUS_OK) + memcpy(buf, serial_nbr, S96AT_SERIAL_NUMBER_LEN); + else + memset(buf, 0, S96AT_SERIAL_NUMBER_LEN); + + return ret; +} + +uint8_t s96at_get_zone_config(struct s96at_desc *desc, uint8_t *buf) +{ + int i; + int ret = STATUS_EXEC_ERROR; + + if (!buf) + return S96AT_STATUS_BAD_PARAMETERS; + + /* Read word by word into the buffer */ + for (i = 0; i < ZONE_CONFIG_SIZE / WORD_SIZE; i++) { + ret = cmd_read(desc->ioif, ZONE_CONFIG, i, 0, WORD_SIZE, + buf + (i * WORD_SIZE), WORD_SIZE); + if (ret != STATUS_OK) + break; + } + + return ret; +} + +uint8_t s96at_get_sha(struct s96at_desc *desc, uint8_t *buf, + size_t buf_len, size_t msg_len, uint8_t *hash) +{ + int i; + uint8_t ret; + uint8_t sha_resp; + size_t padded_msg_len; + + if (!buf || buf_len < 0 || msg_len < 0) + return S96AT_STATUS_BAD_PARAMETERS; + + if (buf_len <= msg_len) + return S96AT_STATUS_BAD_PARAMETERS; + + if (buf_len % SHA_BLOCK_LEN) + return S96AT_STATUS_BAD_PARAMETERS; + + ret = sha_apply_padding(buf, buf_len, msg_len, &padded_msg_len); + if (ret != S96AT_STATUS_OK) + return ret; + + ret = cmd_sha(desc->ioif, SHA_MODE_INIT, NULL, 0, &sha_resp, + sizeof(sha_resp)); + if (ret != STATUS_OK) + return ret; + + for (i = 0; i < padded_msg_len / SHA_BLOCK_LEN; i++) { + ret = cmd_sha(desc->ioif, SHA_MODE_COMPUTE, buf + SHA_BLOCK_LEN * i, + SHA_BLOCK_LEN, hash, S96AT_SHA_LEN); + if (ret != STATUS_OK) + goto out; + } +out: + return ret; +} + +uint8_t s96at_lock_zone(struct s96at_desc *desc, enum s96at_zone zone, uint16_t crc) +{ + return cmd_lock_zone(desc->ioif, zone, &crc); +} + +uint8_t s96at_read(struct s96at_desc *desc, enum s96at_zone zone, uint8_t id, + uint8_t *buf) +{ + uint8_t ret = STATUS_EXEC_ERROR; + uint8_t addr; + uint8_t length; + + switch (zone) { + case S96AT_ZONE_CONFIG: + if (id > ZONE_CONFIG_NUM_WORDS - 1) + return S96AT_STATUS_BAD_PARAMETERS; + addr = id; + length = S96AT_READ_CONFIG_LEN; + break; + case S96AT_ZONE_DATA: + if (id > ZONE_DATA_NUM_SLOTS - 1) + return S96AT_STATUS_BAD_PARAMETERS; + addr = SLOT_ADDR(id); + length = S96AT_READ_DATA_LEN; + break; + case S96AT_ZONE_OTP: + if (id > ZONE_OTP_NUM_WORDS - 1) + return S96AT_STATUS_BAD_PARAMETERS; + addr = id; + length = S96AT_READ_OTP_LEN; + break; + } + + ret = cmd_read(desc->ioif, zone, addr, 0, length, buf, length); + + if (ret != STATUS_OK) + memset(buf, 0, length); + + return ret; +} + +uint8_t s96at_write(struct s96at_desc *desc, enum s96at_zone zone, uint8_t id, + uint32_t flags, const uint8_t *buf) +{ + uint8_t addr; + uint8_t length; + uint8_t encrypted = false; + + switch (zone) { + case S96AT_ZONE_CONFIG: + if (id > ZONE_CONFIG_NUM_WORDS - 1) + return S96AT_STATUS_BAD_PARAMETERS; + addr = id; + length = S96AT_READ_CONFIG_LEN; + break; + case S96AT_ZONE_DATA: + if (id > ZONE_DATA_NUM_SLOTS - 1) + return S96AT_STATUS_BAD_PARAMETERS; + addr = SLOT_ADDR(id); + length = S96AT_READ_DATA_LEN; + break; + case S96AT_ZONE_OTP: + if (id > ZONE_OTP_NUM_WORDS - 1) + return S96AT_STATUS_BAD_PARAMETERS; + addr = id; + length = S96AT_READ_OTP_LEN; + break; + } + + if (flags & S96AT_FLAG_ENCRYPT) + encrypted = true; + + return cmd_write(desc->ioif, zone, addr, encrypted, buf, length); +} + diff --git a/s96at/src/sha.c b/s96at/src/sha.c new file mode 100644 index 0000000..0bd46ec --- /dev/null +++ b/s96at/src/sha.c @@ -0,0 +1,46 @@ +/* + * Copyright 2017, Linaro Ltd and contributors + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include + +#include +#include + +/* FIPS 180-2 Sect 5.1.1 + * + * Padding: + * The message is appended with the bit "1", followed by + * k zero bits, where k is calculated as the smallest + * non-negative solution to: + * + * msg_len + 1 + k ≡ 448 mod 512 + * + * Length: + * An 8 bit block is appended after padding, storing the + * binary representation of the message length in bits. + */ +int sha_apply_padding(uint8_t *buf, size_t buf_len, size_t msg_len, size_t *padded_msg_len) +{ + int i; + size_t padding_len; + + /* Padding length in bytes, including the trailing 1 */ + padding_len = (((448 - (msg_len * 8 + 1)) % 512) + 1) / 8; + + /* Make sure we have enough space to store padding and length */ + if (buf_len - msg_len < padding_len + SHA_PADDING_LENGTH_LEN) + return S96AT_STATUS_PADDING_ERROR; + + memset(buf + msg_len, 0x00, buf_len - msg_len); + buf[msg_len] |= 0x80; + for (i = 0; i < SHA_PADDING_LENGTH_LEN; i++) { + buf[msg_len + padding_len + i] = ((msg_len * 8) >> (56 - i * 8)) & 0xff; + } + *padded_msg_len = msg_len + padding_len + SHA_PADDING_LENGTH_LEN; + + return S96AT_STATUS_OK; +} +