diff --git a/drivers/at86rf2xx/Makefile b/drivers/at86rf2xx/Makefile index 48422e909a..5750f200f5 100644 --- a/drivers/at86rf2xx/Makefile +++ b/drivers/at86rf2xx/Makefile @@ -1 +1,7 @@ +# exclude submodule sources from *.c wildcard source selection +SRC := $(filter-out aes_spi.c,$(wildcard *.c)) + +# enable submodules +SUBMODULES := 1 + include $(RIOTBASE)/Makefile.base diff --git a/drivers/at86rf2xx/aes_spi.c b/drivers/at86rf2xx/aes_spi.c new file mode 100644 index 0000000000..eb79393e9a --- /dev/null +++ b/drivers/at86rf2xx/aes_spi.c @@ -0,0 +1,350 @@ +/* Copyright (C) 2020 Otto-von-Guericke-Universität Magdeburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup drivers_at86rf2xx + * @{ + * + * @file + * @brief Implementation of at86rf2xx SPI security module (AES) + * + * @author Fabian Hüßler + * @} + */ + +#include "xtimer.h" +#include "periph/spi.h" +#include "at86rf2xx_aes.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +#define AES_DEBUG(...) DEBUG("[at86rf2xx_aes_spi]: "__VA_ARGS__) + +#define AT86RF2XX_CMD_SRAM_READ (0b00000000) +#define AT86RF2XX_CMD_SRAM_WRITE (0b01000000) + +static inline +void at86rf2xx_spi_get_bus(const at86rf2xx_t *dev) +{ + spi_acquire(dev->params.spi, dev->params.cs_pin, SPI_MODE_0, dev->params.spi_clk); +} + +static inline +void at86rf2xx_spi_release_bus(const at86rf2xx_t *dev) +{ + spi_release(dev->params.spi); +} + +static inline +uint8_t _aes_status(at86rf2xx_t *dev) +{ + spi_transfer_byte(dev->params.spi, dev->params.cs_pin, true, + AT86RF2XX_CMD_SRAM_READ); + spi_transfer_byte(dev->params.spi, dev->params.cs_pin, true, + AT86RF2XX_REG__AES_STATUS); + return spi_transfer_byte(dev->params.spi, dev->params.cs_pin, false, 0); +} + +static inline +void _aes_wait_for_result(at86rf2xx_t *dev) +{ + xtimer_usleep(AT86RF2XX_AES_DELAY_US); + uint8_t status = _aes_status(dev); + /* + If this assert fires, there probably is an implementation error. + The error bit is set before the transceiver has processed a data block. + There are two cases: + 1. The delay between initiating an AES operation and sending the next cfg + to AT86RF2XX_REG__AES_CTRL was too short. Meaning the transceiver + did not have enough time to process the current block. + 2. Less then 16 bytes of data have been sent to the transceiver. + + Both should not occur in the code. + */ + assert(!(status & AT86RF2XX_AES_STATUS_MASK__AES_ER)); + while (!(status & AT86RF2XX_AES_STATUS_MASK__AES_DONE)) { + AES_DEBUG("status: %02x\n", status); + status = _aes_status(dev); + } +} + +static inline +void _aes_open_read(at86rf2xx_t *dev, uint8_t addr) +{ + spi_transfer_byte(dev->params.spi, dev->params.cs_pin, true, + AT86RF2XX_CMD_SRAM_READ); + spi_transfer_byte(dev->params.spi, dev->params.cs_pin, true, + addr); +} + +static inline +void _aes_open_write(at86rf2xx_t *dev, uint8_t addr) +{ + spi_transfer_byte(dev->params.spi, dev->params.cs_pin, true, + AT86RF2XX_CMD_SRAM_WRITE); + spi_transfer_byte(dev->params.spi, dev->params.cs_pin, true, + addr); +} + +static inline +void _aes_transfer_bytes(at86rf2xx_t *dev, bool cont, const void* out, + void* in, size_t len) +{ + spi_transfer_bytes(dev->params.spi, dev->params.cs_pin, cont, out, + in, len); +} + +static inline +void _aes_save_key(at86rf2xx_t *dev, uint8_t cfg, uint8_t key[AT86RF2XX_AES_BLOCK_SIZE]) +{ + _aes_open_write(dev, AT86RF2XX_REG__AES_CTRL); + _aes_transfer_bytes(dev, false, &cfg, NULL, sizeof(cfg)); + _aes_open_read(dev, AT86RF2XX_REG__AES_KEY_START); + _aes_transfer_bytes(dev, false, NULL, key, AT86RF2XX_AES_KEY_LENGTH); +} + +static inline +void _aes_transfer_block(at86rf2xx_t *dev, uint8_t cfg, uint8_t mirror, + const aes_block_t src, aes_block_t dst) +{ + /* + cfg: + value which tells the AES engine what kind of data is coming in + mirror: + must be the same value as cfg but depending on whether the bit + AT86RF2XX_AES_CTRL_MIRROR_MASK__AES_REQUEST is set, the transceiver + will process the incoming block, or not + src: + current data block of 16 bytes to be sent to the AES engine + dst: + if not NULL, dst stores the processed data block of the + block that has been sent to the AES engine most recently + */ + + /* access SRAM register AES_CTRL for writing */ + _aes_open_write(dev, AT86RF2XX_REG__AES_CTRL); + /* MOSI: send configuration to the AES_CTRL register */ + _aes_transfer_bytes(dev, true, &cfg, NULL, sizeof(cfg)); + /* MOSI: send first byte of the current block (block_i) */ + _aes_transfer_bytes(dev, true, src, NULL, 1); + /* MOSI: send the last 15 bytes of block_i */ + /* MISO: get the first 15 bytes of the most recently processed block (block_i-1) */ + _aes_transfer_bytes(dev, true, src + 1, dst, AT86RF2XX_AES_BLOCK_SIZE - 1); + /* MOSI: send the mirrored cfg value and initiate the processing of block_i (or not) */ + /* MISO: get the last byte of block_i-1 */ + _aes_transfer_bytes(dev, false, &mirror, + dst ? dst + AT86RF2XX_AES_BLOCK_SIZE - 1 : NULL, 1); +} + +void at86rf2xx_aes_key_read_encrypt(at86rf2xx_t *dev, + uint8_t key[AT86RF2XX_AES_KEY_LENGTH]) +{ + uint8_t cfg = AT86RF2XX_AES_CTRL_AES_MODE__KEY | + AT86RF2XX_AES_CTRL_AES_DIR__ENC; + at86rf2xx_spi_get_bus(dev); + _aes_save_key(dev, cfg, key); + at86rf2xx_spi_release_bus(dev); +} + +void at86rf2xx_aes_key_write_encrypt(at86rf2xx_t *dev, + const uint8_t key[AT86RF2XX_AES_KEY_LENGTH]) +{ + uint8_t cfg = AT86RF2XX_AES_CTRL_AES_MODE__KEY | + AT86RF2XX_AES_CTRL_AES_DIR__ENC; + at86rf2xx_spi_get_bus(dev); + _aes_open_write(dev, AT86RF2XX_REG__AES_CTRL); + _aes_transfer_bytes(dev, true, &cfg, NULL, sizeof(cfg)); + _aes_transfer_bytes(dev, false, key, NULL, AT86RF2XX_AES_KEY_LENGTH); + at86rf2xx_spi_release_bus(dev); +} + +void at86rf2xx_aes_key_read_decrypt(at86rf2xx_t *dev, + uint8_t key[AT86RF2XX_AES_KEY_LENGTH]) +{ + uint8_t cfg = AT86RF2XX_AES_CTRL_AES_MODE__KEY | + AT86RF2XX_AES_CTRL_AES_DIR__DEC; + at86rf2xx_spi_get_bus(dev); + _aes_save_key(dev, cfg, key); + at86rf2xx_spi_release_bus(dev); +} + +void at86rf2xx_aes_key_write_decrypt(at86rf2xx_t *dev, + const uint8_t key[AT86RF2XX_AES_KEY_LENGTH]) +{ + uint8_t cfg = AT86RF2XX_AES_CTRL_AES_MODE__KEY | + AT86RF2XX_AES_CTRL_AES_DIR__DEC; + at86rf2xx_spi_get_bus(dev); + _aes_open_write(dev, AT86RF2XX_REG__AES_CTRL); + _aes_transfer_bytes(dev, true, &cfg, NULL, sizeof(cfg)); + _aes_transfer_bytes(dev, false, key, NULL, AT86RF2XX_AES_KEY_LENGTH); + at86rf2xx_spi_release_bus(dev); +} + +void at86rf2xx_aes_ecb_encrypt(at86rf2xx_t *dev, + aes_block_t *cipher, + uint8_t key[AT86RF2XX_AES_BLOCK_SIZE], + const aes_block_t *plain, + uint8_t nblocks) +{ + if (!nblocks) { + return; + } + uint8_t cfg = AT86RF2XX_AES_CTRL_AES_MODE__ECB | + AT86RF2XX_AES_CTRL_AES_DIR__ENC; + uint8_t mirror = cfg | AT86RF2XX_AES_CTRL_MIRROR_MASK__AES_REQUEST; + at86rf2xx_spi_get_bus(dev); + _aes_transfer_block(dev, cfg, mirror, plain[0], NULL); + _aes_wait_for_result(dev); + if (key) { + cfg = AT86RF2XX_AES_CTRL_AES_MODE__KEY | + AT86RF2XX_AES_CTRL_AES_DIR__DEC; + _aes_save_key(dev, cfg, key); + } + cfg = AT86RF2XX_AES_CTRL_AES_MODE__ECB | + AT86RF2XX_AES_CTRL_AES_DIR__ENC; + for (unsigned i = 1; i < nblocks; i++) { + _aes_transfer_block(dev, cfg, mirror, plain[i], + cipher ? cipher[i - 1] : NULL); + _aes_wait_for_result(dev); + } + + /* send dummy bytes to get the last block of cipher text */ + mirror &= ~AT86RF2XX_AES_CTRL_MIRROR_MASK__AES_REQUEST; + _aes_transfer_block(dev, cfg, mirror, plain[0], + cipher ? cipher[nblocks - 1] : NULL); + at86rf2xx_spi_release_bus(dev); +} + +void at86rf2xx_aes_ecb_decrypt(at86rf2xx_t *dev, + aes_block_t *plain, + uint8_t key[AT86RF2XX_AES_BLOCK_SIZE], + const aes_block_t *cipher, + uint8_t nblocks) +{ + if (!nblocks) { + return; + } + uint8_t cfg = AT86RF2XX_AES_CTRL_AES_MODE__ECB | + AT86RF2XX_AES_CTRL_AES_DIR__DEC; + uint8_t mirror = cfg | AT86RF2XX_AES_CTRL_MIRROR_MASK__AES_REQUEST; + at86rf2xx_spi_get_bus(dev); + _aes_transfer_block(dev, cfg, mirror, cipher[0], NULL); + _aes_wait_for_result(dev); + if (key) { + cfg = AT86RF2XX_AES_CTRL_AES_MODE__KEY | + AT86RF2XX_AES_CTRL_AES_DIR__ENC; + _aes_save_key(dev, cfg, key); + } + cfg = AT86RF2XX_AES_CTRL_AES_MODE__ECB | + AT86RF2XX_AES_CTRL_AES_DIR__DEC; + for (unsigned i = 1; i < nblocks; i++) { + _aes_transfer_block(dev, cfg, mirror, cipher[i], + plain ? plain[i - 1] : NULL); + _aes_wait_for_result(dev); + } + + /* send dummy bytes to get the last block of plain text */ + mirror &= ~AT86RF2XX_AES_CTRL_MIRROR_MASK__AES_REQUEST; + _aes_transfer_block(dev, cfg, mirror, cipher[0], + plain ? plain[nblocks - 1] : NULL); + at86rf2xx_spi_release_bus(dev); +} + +void at86rf2xx_aes_cbc_encrypt(at86rf2xx_t *dev, + aes_block_t *cipher, + uint8_t key[AT86RF2XX_AES_BLOCK_SIZE], + uint8_t iv[AT86RF2XX_AES_BLOCK_SIZE], + const aes_block_t *plain, + uint8_t nblocks) +{ + if (!nblocks) { + return; + } + uint8_t cfg = AT86RF2XX_AES_CTRL_AES_MODE__ECB | + AT86RF2XX_AES_CTRL_AES_DIR__ENC; + uint8_t mirror = cfg | AT86RF2XX_AES_CTRL_MIRROR_AES_REQUEST__START; + /* The first block has to be ECB encrypted because there is no + cipher result to be XOR´ed from the last round. + Instead an "initial vector" is XOR´ed to the first block + of plain text. */ + uint8_t first[AT86RF2XX_AES_BLOCK_SIZE]; + for (unsigned i = 0; i < AT86RF2XX_AES_BLOCK_SIZE; i++) { + first[i] = plain[0][i] ^ iv[i]; + } + at86rf2xx_spi_get_bus(dev); + _aes_transfer_block(dev, cfg, mirror, first, NULL); + _aes_wait_for_result(dev); + if (key) { + cfg = AT86RF2XX_AES_CTRL_AES_MODE__KEY | + AT86RF2XX_AES_CTRL_AES_DIR__DEC; + _aes_save_key(dev, cfg, key); + } + cfg = AT86RF2XX_AES_CTRL_AES_MODE__CBC | + AT86RF2XX_AES_CTRL_AES_DIR__ENC; + mirror = cfg | AT86RF2XX_AES_CTRL_MIRROR_AES_REQUEST__START; + for (unsigned i = 1; i < nblocks; i++) { + _aes_transfer_block(dev, cfg, mirror, plain[i], + cipher ? cipher[i - 1] : NULL); + _aes_wait_for_result(dev); + } + + /* send dummy bytes to get the last block of cipher text */ + uint8_t *mac = cipher ? cipher[nblocks - 1] : iv; + mirror &= ~AT86RF2XX_AES_CTRL_MIRROR_MASK__AES_REQUEST; + _aes_transfer_block(dev, cfg, mirror, plain[0], mac); + at86rf2xx_spi_release_bus(dev); +} + +void at86rf2xx_aes_cbc_decrypt(at86rf2xx_t *dev, + aes_block_t *plain, + uint8_t key[AT86RF2XX_AES_BLOCK_SIZE], + uint8_t iv[AT86RF2XX_AES_BLOCK_SIZE], + const aes_block_t *cipher, + uint8_t nblocks) +{ + if (!nblocks) { + return; + } + uint8_t cfg = AT86RF2XX_AES_CTRL_AES_MODE__ECB | + AT86RF2XX_AES_CTRL_AES_DIR__DEC; + uint8_t mirror = cfg | AT86RF2XX_AES_CTRL_MIRROR_AES_REQUEST__START; + at86rf2xx_spi_get_bus(dev); + _aes_transfer_block(dev, cfg, mirror, cipher[0], NULL); + _aes_wait_for_result(dev); + if (key) { + cfg = AT86RF2XX_AES_CTRL_AES_MODE__KEY | + AT86RF2XX_AES_CTRL_AES_DIR__ENC; + _aes_save_key(dev, cfg, key); + } + const uint8_t *xor = iv; + cfg = AT86RF2XX_AES_CTRL_AES_MODE__ECB | + AT86RF2XX_AES_CTRL_AES_DIR__DEC; + for (unsigned i = 1; i < nblocks; i++) { + _aes_transfer_block(dev, cfg, mirror, cipher[i], + plain ? plain[i - 1] : NULL); + _aes_wait_for_result(dev); + if (plain) { + for (unsigned j = 0; j < AT86RF2XX_AES_BLOCK_SIZE; j++) { + plain[i - 1][j] ^= xor[j]; + } + xor = cipher[i - 1]; + } + } + + /* send dummy bytes to get the last block of plain text */ + uint8_t *mac = plain ? plain[nblocks - 1] : iv; + mirror &= ~AT86RF2XX_AES_CTRL_MIRROR_MASK__AES_REQUEST; + _aes_transfer_block(dev, cfg, mirror, cipher[0], mac); + if (plain) { + for (unsigned j = 0; j < AT86RF2XX_AES_BLOCK_SIZE; j++) { + plain[nblocks - 1][j] ^= xor[j]; + } + } + + at86rf2xx_spi_release_bus(dev); +} diff --git a/drivers/at86rf2xx/include/at86rf2xx_aes.h b/drivers/at86rf2xx/include/at86rf2xx_aes.h new file mode 100644 index 0000000000..f03d9606f2 --- /dev/null +++ b/drivers/at86rf2xx/include/at86rf2xx_aes.h @@ -0,0 +1,242 @@ +/* + * Copyright (C) 2020 Otto-von-Guericke-Universität Magdeburg + * + * This file is subject to the terms and conditions of the GNU Lesser General + * Public License v2.1. See the file LICENSE in the top level directory for more + * details. + */ + +/** + * @ingroup drivers_at86rf2xx + * + * The extended feature set of at86rf2xx transceivers comprises a + * hardware implementation of AES. There are two supported block + * cipher modes, ECB and CBC. + * + * @{ + * + * @file + * @brief Interface of the at86rf2xx security module (AES) + * + * @author Fabian Hüßler + */ +#ifndef AT86RF2XX_AES_H +#define AT86RF2XX_AES_H + +#include "at86rf2xx.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief AES key length in bits + */ +#define AT86RF2XX_AES_KEY_BITS (128U) +/** + * @brief AES key length in bytes + */ +#define AT86RF2XX_AES_KEY_LENGTH ((AT86RF2XX_AES_KEY_BITS) / 8) +/** + * @brief AES block size in bytes + */ +#define AT86RF2XX_AES_BLOCK_SIZE ((AT86RF2XX_AES_KEY_BITS) / 8) +/** + * @brief Time to complete the AES algorithm in us + */ +#define AT86RF2XX_AES_DELAY_US (24) + +/** + * @name AES rigister addresses + * @{ + */ +#define AT86RF2XX_REG__AES_STATUS (0x82) +#define AT86RF2XX_REG__AES_CTRL (0x83) +#define AT86RF2XX_REG__AES_KEY_START (0x84) +#define AT86RF2XX_REG__AES_KEY_END (0x93) +#define AT86RF2XX_REG__AES_DATA_START (0x84) +#define AT86RF2XX_REG__AES_DATA_END (0x93) +#define AT86RF2XX_REG__AES_CTRL_MIRROR (0x94) +/** @} */ + +/** + * @name Layout of register AES_STATUS + * @{ + */ +#define AT86RF2XX_AES_STATUS_MASK__AES_ER (0x80) +#define AT86RF2XX_AES_STATUS_MASK__AES_DONE (0x01) + +#define AT86RF2XX_AES_STATUS_AES_ER__NO_ERROR (0x00) +#define AT86RF2XX_AES_STATUS_AES_ER__ERROR (0x80) + +#define AT86RF2XX_AES_STATUS_AES_DONE__NOT_DONE (0x00) +#define AT86RF2XX_AES_STATUS_AES_DONE__DONE (0x01) +/** @} */ + +/** + * @name Layout of register AES_CTRL + * @{ + */ +#define AT86RF2XX_AES_CTRL_MASK__AES_REQUEST (0x80) +#define AT86RF2XX_AES_CTRL_MASK__AES_MODE (0x70) +#define AT86RF2XX_AES_CTRL_MASK__AES_DIR (0x08) + +#define AT86RF2XX_AES_CTRL_AES_REQUEST__IDLE (0x00) +#define AT86RF2XX_AES_CTRL_AES_REQUEST__START (0x80) + +#define AT86RF2XX_AES_CTRL_AES_MODE__ECB (0x00) +#define AT86RF2XX_AES_CTRL_AES_MODE__KEY (0x10) +#define AT86RF2XX_AES_CTRL_AES_MODE__CBC (0x20) + +#define AT86RF2XX_AES_CTRL_AES_DIR__ENC (0x00) +#define AT86RF2XX_AES_CTRL_AES_DIR__DEC (0x08) +/** @} */ + +/** + * @name Layout of register AES_CTRL_MIRROR + * @{ + */ +#define AT86RF2XX_AES_CTRL_MIRROR_MASK__AES_REQUEST (0x80) +#define AT86RF2XX_AES_CTRL_MIRROR_MASK__AES_MODE (0x70) +#define AT86RF2XX_AES_CTRL_MIRROR_MASK__AES_DIR (0x08) + +#define AT86RF2XX_AES_CTRL_MIRROR_AES_REQUEST__IDLE (0x00) +#define AT86RF2XX_AES_CTRL_MIRROR_AES_REQUEST__START (0x80) + +#define AT86RF2XX_AES_CTRL_MIRROR_AES_MODE__ECB (0x00) +#define AT86RF2XX_AES_CTRL_MIRROR_AES_MODE__KEY (0x10) +#define AT86RF2XX_AES_CTRL_MIRROR_AES_MODE__CBC (0x20) + +#define AT86RF2XX_AES_CTRL_MIRROR_AES_DIR__ENC (0x00) +#define AT86RF2XX_AES_CTRL_MIRROR_AES_DIR__DEC (0x08) +/** @} */ + +/** + * @brief An AES block + * + * AES works on blocks of 16 bytes + */ +typedef uint8_t aes_block_t[AT86RF2XX_AES_BLOCK_SIZE]; + +/** + * @brief Read the AES key used for encryption + * + * @param[in] dev Device + * @param[out] key Buffer to store the key + */ +void at86rf2xx_aes_key_read_encrypt(at86rf2xx_t *dev, + uint8_t key[AT86RF2XX_AES_KEY_LENGTH]); + +/** + * @brief Write the AES key used for encryption + * + * It is important to write the encryption key, before encryption is done + * + * @param[in] dev Device + * @param[in] key Buffer which stores the key + */ +void at86rf2xx_aes_key_write_encrypt(at86rf2xx_t *dev, + const uint8_t key[AT86RF2XX_AES_KEY_LENGTH]); + +/** + * @brief Read the AES key used for decryption + * + * @param[in] dev Device + * @param[out] key Buffer to store the key + */ +void at86rf2xx_aes_key_read_decrypt(at86rf2xx_t *dev, + uint8_t key[AT86RF2XX_AES_KEY_LENGTH]); + +/** + * @brief Write the AES key used for decryption + * + * It is important to write the decryption key, before decryption is done + * + * @param[in] dev Device + * @param[in] key Buffer which stores the key + */ +void at86rf2xx_aes_key_write_decrypt(at86rf2xx_t *dev, + const uint8_t key[AT86RF2XX_AES_KEY_LENGTH]); + +/** + * @brief Perform AES algorithm and encrypt data blocks + * in @p plain to cipher data blocks, using ECB mode + * + * @note The encryption key must have been written before. + * + * @param[in] dev Device + * @param[out] cipher If not NULL, cipher data blocks + * @param[out] key If not NULL, last round encryption key is stored + * @param[in] plain Plain data blocks + * @param[in] nblocks Number of blocks + */ +void at86rf2xx_aes_ecb_encrypt(at86rf2xx_t *dev, + aes_block_t *cipher, + uint8_t key[AT86RF2XX_AES_BLOCK_SIZE], + const aes_block_t *plain, + uint8_t nblocks); + +/** + * @brief Perform AES algorithm and decrypt data blocks + * in @p cipher to plain data blocks, using ECB mode + * + * @note The decryption key must have been written before. + * + * @param[in] dev Device + * @param[out] plain If not NULL, plain data blocks + * @param[out] key If not NULL, last round decryption key is stored + * @param[in] cipher Cipher data blocks + * @param[in] nblocks Number of blocks + */ +void at86rf2xx_aes_ecb_decrypt(at86rf2xx_t *dev, + aes_block_t *plain, + uint8_t key[AT86RF2XX_AES_BLOCK_SIZE], + const aes_block_t *cipher, + uint8_t nblocks); + +/** + * @brief Perform AES algorithm and encrypt data blocks + * in @p plain to cipher data blocks, using CBC mode + * + * @note The encryption key must have been written before. + * + * @param[in] dev Device + * @param[out] cipher If not NULL, cipher blocks + * @param[out] key If not NULL, last round encryption key is stored + * @param[in,out] iv in: initial vector, out: last cipher block if cipher is NULL + * @param[in] plain Plain data blocks + * @param[in] nblocks Number of blocks + */ +void at86rf2xx_aes_cbc_encrypt(at86rf2xx_t *dev, + aes_block_t *cipher, + uint8_t key[AT86RF2XX_AES_BLOCK_SIZE], + uint8_t iv[AT86RF2XX_AES_BLOCK_SIZE], + const aes_block_t *plain, + uint8_t nblocks); + +/** + * @brief Perform AES algorithm and decrypt data blocks + * in @p cipher to plain data blocks, using CBC mode + * + * @note The decryption key must have been written before. + * + * @param[in] dev Device + * @param[out] plain If not NUll, plain data blocks + * @param[out] key If not NULL, last round decryption key is stored + * @param[in,out] iv in: initial vector, out: last plain block if plain is NULL + * @param[in] cipher Cipher data blocks + * @param[in] nblocks Number of blocks + */ +void at86rf2xx_aes_cbc_decrypt(at86rf2xx_t *dev, + aes_block_t *plain, + uint8_t key[AT86RF2XX_AES_BLOCK_SIZE], + uint8_t iv[AT86RF2XX_AES_BLOCK_SIZE], + const aes_block_t *cipher, + uint8_t nblocks); + +#ifdef __cplusplus +} +#endif + +#endif /* AT86RF2XX_AES_H */ +/** @} */ diff --git a/makefiles/pseudomodules.inc.mk b/makefiles/pseudomodules.inc.mk index 9eb7e96993..6ca6b4bb82 100644 --- a/makefiles/pseudomodules.inc.mk +++ b/makefiles/pseudomodules.inc.mk @@ -132,6 +132,7 @@ PSEUDOMODULES += at86rf23% PSEUDOMODULES += at86rf21% PSEUDOMODULES += at86rfa1 PSEUDOMODULES += at86rfr2 +PSEUDOMODULES += at86rf2xx_aes_spi NO_PSEUDOMODULES += at86rf215 # include variants of the BME680 drivers as pseudo modules diff --git a/tests/driver_at86rf2xx_aes/Makefile b/tests/driver_at86rf2xx_aes/Makefile new file mode 100644 index 0000000000..ce1d6a980f --- /dev/null +++ b/tests/driver_at86rf2xx_aes/Makefile @@ -0,0 +1,23 @@ +include ../Makefile.tests_common + +DISABLE_MODULE += auto_init_at86rf2xx + +# define the driver to be used for selected boards +ifneq (,$(filter samr21-xpro,$(BOARD))) + DRIVER := at86rf233 +endif +ifneq (,$(filter iotlab-m3 fox,$(BOARD))) + DRIVER := at86rf231 +endif +ifneq (,$(filter mulle,$(BOARD))) + DRIVER := at86rf212b +endif + +# use the at86rf231 as fallback device +DRIVER ?= at86rf231 + +# include the selected driver +USEMODULE += $(DRIVER) +USEMODULE += at86rf2xx_aes_spi + +include $(RIOTBASE)/Makefile.include \ No newline at end of file diff --git a/tests/driver_at86rf2xx_aes/Makefile.ci b/tests/driver_at86rf2xx_aes/Makefile.ci new file mode 100644 index 0000000000..437811ffa9 --- /dev/null +++ b/tests/driver_at86rf2xx_aes/Makefile.ci @@ -0,0 +1,7 @@ +BOARD_INSUFFICIENT_MEMORY := \ + arduino-duemilanove \ + arduino-nano \ + arduino-uno \ + atmega328p \ + stm32f030f4-demo \ + # diff --git a/tests/driver_at86rf2xx_aes/main.c b/tests/driver_at86rf2xx_aes/main.c new file mode 100644 index 0000000000..b51eeddf20 --- /dev/null +++ b/tests/driver_at86rf2xx_aes/main.c @@ -0,0 +1,238 @@ +/* + * Copyright (C) 2020 Otto-von-Guericke-Universität Magdeburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup tests + * @{ + * + * @file + * @brief Test application for at86rf2xx security module (AES) + * + * @author Fabian Hüßler + * @} + */ +#include +#include + +#include "xtimer.h" +#include "at86rf2xx.h" +#include "at86rf2xx_params.h" +#include "at86rf2xx_aes.h" + +#ifndef KEY_1 +#define KEY_1 { 0x06, 0xa9, 0x21, 0x40, 0x36, 0xb8, 0xa1, 0x5b, \ + 0x51, 0x2e, 0x03, 0xd5, 0x34, 0x12, 0x00, 0x06 } +#endif +#ifndef IV_1 +#define IV_1 { 0x3d, 0xaf, 0xba, 0x42, 0x9d, 0x9e, 0xb4, 0x30, \ + 0xb4, 0x22, 0xda, 0x80, 0x2c, 0x9f, 0xac, 0x41 } +#endif +#ifndef PLAIN_1 +#define PLAIN_1 { 'S', 'i', 'n', 'g', 'l', 'e', ' ', 'b', \ + 'l', 'o', 'c', 'k', ' ', 'm', 's', 'g' } +#endif +#ifndef KEY_2 +#define KEY_2 { 0xc2, 0x86, 0x69, 0x6d, 0x88, 0x7c, 0x9a, 0xa0, \ + 0x61, 0x1b, 0xbb, 0x3e, 0x20, 0x25, 0xa4, 0x5a } +#endif +#ifndef IV_2 +#define IV_2 { 0x56, 0x2e, 0x17, 0x99, 0x6d, 0x09, 0x3d, 0x28, \ + 0xdd, 0xb3, 0xba, 0x69, 0x5a, 0x2e, 0x6f, 0x58 } +#endif +#ifndef PLAIN_2 +#define PLAIN_2 { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, \ + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, \ + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, \ + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f } +#endif +#ifndef KEY_3 +#define KEY_3 { 0x6c, 0x3e, 0xa0, 0x47, 0x76, 0x30, 0xce, 0x21, \ + 0xa2, 0xce, 0x33, 0x4a, 0xa7, 0x46, 0xc2, 0xcd } +#endif +#ifndef IV_3 +#define IV_3 { 0xc7, 0x82, 0xdc, 0x4c, 0x09, 0x8c, 0x66, 0xcb, \ + 0xd9, 0xcd, 0x27, 0xd8, 0x25, 0x68, 0x2c, 0x81 } +#endif +#ifndef PLAIN_3 +#define PLAIN_3 { 'T', 'h', 'i', 's', ' ', 'i', 's', ' ', \ + 'a', ' ', '4', '8', '-', 'b', 'y', 't', \ + 'e', ' ', 'm', 'e', 's', 's', 'a', 'g', \ + 'e', ' ', '(', 'e', 'x', 'a', 'c', 't', \ + 'l', 'y', ' ', '3', ' ', 'A', 'E', 'S', \ + ' ', 'b', 'l', 'o', 'c', 'k', 's', ')' } +#endif +#ifndef KEY_4 +#define KEY_4 { 0x56, 0xe4, 0x7a, 0x38, 0xc5, 0x59, 0x89, 0x74, \ + 0xbc, 0x46, 0x90, 0x3d, 0xba, 0x29, 0x03, 0x49 } +#endif +#ifndef IV_4 +#define IV_4 { 0x8c, 0xe8, 0x2e, 0xef, 0xbe, 0xa0, 0xda, 0x3c, \ + 0x44, 0x69, 0x9e, 0xd7, 0xdb, 0x51, 0xb7, 0xd9 } +#endif +#ifndef PLAIN_4 +#define PLAIN_4 { 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, \ + 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, \ + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, \ + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, \ + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, \ + 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, \ + 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, \ + 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf } +#endif +#ifndef KEY_5 +#define KEY_5 { 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, \ + 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf } +#endif +#ifndef IV_5 +#define IV_5 { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } +#endif +#ifndef PLAIN_5 +#define PLAIN_5 { 0x59, 0xac, 0xde, 0x48, 0x00, 0x00, 0x00, 0x00, \ + 0x01, 0x00, 0x00, 0x00, 0x05, 0x06, 0x00, 0x01, \ + 0x00, 0x1d, 0x2b, 0xdc, 0x84, 0x21, 0x43, 0x02, \ + 0x00, 0x00, 0x00, 0x00, 0x48, 0xde, 0xac, 0xff, \ + 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0x48, 0xde, \ + 0xac, 0x06, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, \ + 0xce, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } +#endif + +static void _event_cb(netdev_t *dev, netdev_event_t event) +{ + /* Ignore interrupts */ + (void)dev; + (void)event; +} + +static void _ecb(at86rf2xx_t *dev, + aes_block_t *plain, + uint8_t key[AT86RF2XX_AES_KEY_LENGTH], + aes_block_t *cipher, + uint8_t nblocks) +{ + at86rf2xx_aes_key_write_encrypt(dev, key); + at86rf2xx_aes_ecb_encrypt(dev, cipher, key, plain, nblocks); + memset(plain, 0, AT86RF2XX_AES_BLOCK_SIZE * nblocks); + at86rf2xx_aes_key_write_decrypt(dev, key); + at86rf2xx_aes_ecb_decrypt(dev, plain, key, cipher, nblocks); +} + +static void _cbc(at86rf2xx_t *dev, + aes_block_t *plain, + uint8_t key[AT86RF2XX_AES_BLOCK_SIZE], + uint8_t iv[AT86RF2XX_AES_BLOCK_SIZE], + aes_block_t *cipher, + uint8_t nblocks) +{ + at86rf2xx_aes_key_write_encrypt(dev, key); + at86rf2xx_aes_cbc_encrypt(dev, cipher, key, iv, plain, nblocks); + memset(plain, 0, AT86RF2XX_AES_BLOCK_SIZE * nblocks); + at86rf2xx_aes_key_write_decrypt(dev, key); + at86rf2xx_aes_cbc_decrypt(dev, plain, key, iv, cipher, nblocks); +} + +int main(void) +{ + at86rf2xx_t dev; + bool success = true; + at86rf2xx_setup(&dev, &at86rf2xx_params[0]); + dev.netdev.netdev.event_callback = _event_cb; + if (dev.netdev.netdev.driver->init(&dev.netdev.netdev) != 0) { + return EXIT_FAILURE; + } + + puts("START"); + { + uint8_t k[] = KEY_1; + uint8_t p[] = PLAIN_1; + uint8_t c[sizeof(p)]; + uint8_t n = sizeof(p) / AT86RF2XX_AES_BLOCK_SIZE; + + _ecb(&dev, (aes_block_t *)p, k, (aes_block_t *)c, n); + + if (!memcmp(p, (uint8_t[])PLAIN_1, sizeof(p))) { + puts("ECB test 1 done"); + } + else { + success = false; + puts("ECB test 1 failed"); + } + } + { + uint8_t k[] = KEY_2; + uint8_t p[] = PLAIN_2; + uint8_t c[sizeof(p)]; + uint8_t n = sizeof(p) / AT86RF2XX_AES_BLOCK_SIZE; + + _ecb(&dev, (aes_block_t *)p, k, (aes_block_t *)c, n); + + if (!memcmp(p, (uint8_t[])PLAIN_2, sizeof(p))) { + puts("ECB test 2 done"); + } + else { + success = false; + puts("ECB test 2 failed"); + } + } + { + uint8_t k[] = KEY_3; + uint8_t v[] = IV_3; + uint8_t p[] = PLAIN_3; + uint8_t c[sizeof(p)]; + uint8_t n = sizeof(p) / AT86RF2XX_AES_BLOCK_SIZE; + + _cbc(&dev, (aes_block_t *)p, k, v, (aes_block_t *)c, n); + + if (!memcmp(p, (uint8_t[])PLAIN_3, sizeof(p))) { + puts("CBC test 1 done"); + } + else { + success = false; + puts("CBC test 1 failed"); + } + } + { + uint8_t k[] = KEY_4; + uint8_t v[] = IV_4; + uint8_t p[] = PLAIN_4; + uint8_t c[sizeof(p)]; + uint8_t n = sizeof(p) / AT86RF2XX_AES_BLOCK_SIZE; + + _cbc(&dev, (aes_block_t *)p, k, v, (aes_block_t *)c, n); + + if (!memcmp(p, (uint8_t[])PLAIN_4, sizeof(p))) { + puts("CBC test 2 done"); + } + else { + success = false; + puts("CBC test 2 failed"); + } + } + { + uint8_t k[] = KEY_5; + uint8_t v[] = IV_5; + uint8_t p[] = PLAIN_5; + uint8_t c[sizeof(p)]; + uint8_t n = sizeof(p) / AT86RF2XX_AES_BLOCK_SIZE; + + _cbc(&dev, (aes_block_t *)p, k, v, (aes_block_t *)c, n); + + if (!memcmp(p, (uint8_t[])PLAIN_5, sizeof(p))) { + puts("CBC test 3 done"); + } + else { + success = false; + puts("CBC test 3 failed"); + } + } + if (success) { + puts("SUCCESS"); + } + return EXIT_SUCCESS; +} diff --git a/tests/driver_at86rf2xx_aes/tests/01-run.py b/tests/driver_at86rf2xx_aes/tests/01-run.py new file mode 100755 index 0000000000..059bedec73 --- /dev/null +++ b/tests/driver_at86rf2xx_aes/tests/01-run.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 + +import sys +from testrunner import run + + +def testfunc(child): + child.expect('START') + child.expect('ECB test 1 done') + child.expect('ECB test 2 done') + child.expect('CBC test 1 done') + child.expect('CBC test 2 done') + child.expect('CBC test 3 done') + child.expect('SUCCESS') + + +if __name__ == "__main__": + sys.exit(run(testfunc))