diff options
Diffstat (limited to 'keywrap.c')
-rw-r--r-- | keywrap.c | 436 |
1 files changed, 436 insertions, 0 deletions
diff --git a/keywrap.c b/keywrap.c new file mode 100644 index 0000000..ccf7ea8 --- /dev/null +++ b/keywrap.c @@ -0,0 +1,436 @@ +/* + * keywrap.c + * --------- + * Implementation of RFC 5649 over Cryptech keywrap core. + * + * Authors: Rob Austein + * Copyright (c) 2015-2018, NORDUnet A/S All rights reserved. + * Copyright: 2020, The Commons Conservancy Cryptech Project + * SPDX-License-Identifier: BSD-3-Clause + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This file is derived from aes_keywrap.c, and still shares some code + * with it, but the keywrap core has evolved to the point where it is no + * longer comfortable trying to support both cores in one driver. + */ + +#include <stdint.h> +#include <string.h> + +#include "hal.h" +#include "hal_internal.h" + +typedef union { + uint8_t b[4]; + uint32_t w; +} byteword_t; + +/* + * Match uninitialized flash for the "not set" value. + * Leave some bits at 1 for the "set" value to allow + * for adding more values later, if needed. + */ +#define MKM_STATUS_NOT_SET 0xffffffff +#define MKM_STATUS_SET 0x0000ffff +#define MKM_STATUS_ERASED 0x00000000 + +static hal_error_t mkm_status = HAL_ERROR_IMPOSSIBLE; + + +static inline hal_error_t hal_io_cmd_read(const hal_core_t *core) +{ + const uint8_t buf[4] = { 0, 0, 0, KEYWRAP_CTRL_READ }; + return hal_io_write(core, ADDR_CTRL, buf, sizeof(buf)); +} + +static inline hal_error_t hal_io_cmd_write(const hal_core_t *core) +{ + const uint8_t buf[4] = { 0, 0, 0, KEYWRAP_CTRL_WRITE }; + return hal_io_write(core, ADDR_CTRL, buf, sizeof(buf)); +} + + +/* + * Check the MKM status + */ + +hal_error_t hal_keywrap_mkm_status(hal_core_t *core) +{ + const int free_core = core == NULL; + uint8_t config[4] = { 0, 0, 0, 0 }; + byteword_t status; + hal_error_t err; + + if (free_core && (err = hal_core_alloc(KEYWRAP_NAME, &core, NULL)) != HAL_OK) + return err; + + if ((err = hal_io_write(core, KEYWRAP_ADDR_CONFIG, config, sizeof(config))) == HAL_OK && + (err = hal_io_cmd_read(core)) == HAL_OK && + (err = hal_io_wait_ready(core)) == HAL_OK) + err = hal_io_read(core, KEYWRAP_ADDR_MSTATUS, status.b, 4); + + if (free_core) + hal_core_free(core); + + if (err != HAL_OK) + return err; + + switch (htonl(status.w)) { + case MKM_STATUS_SET: return mkm_status = HAL_OK; + case MKM_STATUS_NOT_SET: return mkm_status = HAL_ERROR_MASTERKEY_NOT_SET; + default: return mkm_status = HAL_ERROR_MASTERKEY_FAIL; + } +} + +hal_error_t hal_keywrap_mkm_write(hal_core_t *core, const uint8_t *K, const size_t K_len) +{ + const int free_core = core == NULL; + uint8_t config[4] = { 0, 0, 0, 0 }; + byteword_t status; + hal_error_t err; + + if (K == NULL) + return HAL_ERROR_BAD_ARGUMENTS; + + if (K_len != KEK_LENGTH) + return HAL_ERROR_MASTERKEY_BAD_LENGTH; + + status.w = htonl(MKM_STATUS_NOT_SET); + for (size_t i = 0; i < K_len; ++i) { + if (K[i] != 0) { + status.w = htonl(MKM_STATUS_SET); + break; + } + } + + if (free_core && (err = hal_core_alloc(KEYWRAP_NAME, &core, NULL)) != HAL_OK) + return err; + + /* first write the key */ + config[3] |= KEYWRAP_CONFIG_MKS; + if ((err = hal_io_write(core, KEYWRAP_ADDR_KEY0, K, K_len)) != HAL_OK || + (err = hal_io_write(core, KEYWRAP_ADDR_CONFIG, config, sizeof(config))) != HAL_OK || + (err = hal_io_cmd_write(core)) != HAL_OK || + (err = hal_io_wait_ready(core)) != HAL_OK) + goto out; + + /* then write the status */ + config[3] &= ~KEYWRAP_CONFIG_MKS; + if ((err = hal_io_write(core, KEYWRAP_ADDR_MSTATUS, status.b, 4)) != HAL_OK || + (err = hal_io_write(core, KEYWRAP_ADDR_CONFIG, config, sizeof(config))) != HAL_OK || + (err = hal_io_cmd_write(core)) != HAL_OK || + (err = hal_io_wait_ready(core)) != HAL_OK) + goto out; + +out: + if (free_core) + hal_core_free(core); + + return err; +} + +hal_error_t hal_keywrap_mkm_erase(hal_core_t *core, const size_t K_len) +{ + uint8_t buf[KEK_LENGTH] = { 0 }; + + return hal_keywrap_mkm_write(core, buf, sizeof(buf)); +} + +/* + * How long the ciphertext will be for a given plaintext length. + * This rounds up the length to a multiple of 8, and adds 8 for the IV. + */ + +static size_t hal_keywrap_ciphertext_length(const size_t plaintext_length) +{ + return (plaintext_length + 15) & ~7; +} + +/* + * Check the KEK, then load it into the AES core. + * Note that our AES core only supports 128 and 256 bit keys. + */ + +typedef enum { KEK_encrypting, KEK_decrypting } kek_action_t; + +static hal_error_t load_kek(hal_core_t *core, const uint8_t *K, const size_t K_len, const kek_action_t action) +{ + uint8_t config[4] = { 0 }; + hal_error_t err; + + if (K != NULL) { + /* user-provided KEK, for key export/import */ + config[3] |= KEYWRAP_CONFIG_MKK; + + if ((err = hal_io_write(core, AES_ADDR_KEY0, K, K_len)) != HAL_OK) + return err; + + switch (K_len) { + case bitsToBytes(128): + config[3] &= ~KEYWRAP_CONFIG_KEYLEN; + break; + case bitsToBytes(256): + config[3] |= KEYWRAP_CONFIG_KEYLEN; + break; + case bitsToBytes(192): + return HAL_ERROR_UNSUPPORTED_KEY; + default: + return HAL_ERROR_BAD_ARGUMENTS; + } + } + + else { + /* read the MKM KEK into the keywrap core */ + if (mkm_status != HAL_OK && + (err = hal_keywrap_mkm_status(core)) != HAL_OK) + return err; + + config[3] &= ~KEYWRAP_CONFIG_MKS; + if ((err = hal_io_write(core, KEYWRAP_ADDR_CONFIG, config, sizeof(config))) != HAL_OK || + (err = hal_io_cmd_read(core)) != HAL_OK || + (err = hal_io_wait_ready(core)) != HAL_OK) + return err; + + config[3] &= ~KEYWRAP_CONFIG_MKK; + config[3] |= KEYWRAP_CONFIG_KEYLEN; /* XXX hardwire to 256-bits for now */ + } + + switch (action) { + case KEK_encrypting: + config[3] |= KEYWRAP_CONFIG_ENCDEC; + break; + case KEK_decrypting: + config[3] &= ~KEYWRAP_CONFIG_ENCDEC; + break; + default: + return HAL_ERROR_BAD_ARGUMENTS; + } + + /* + * Load the KEK and tell the core to expand it. + */ + + if ((err = hal_io_write(core, KEYWRAP_ADDR_CONFIG, config, sizeof(config))) != HAL_OK || + (err = hal_io_init(core)) != HAL_OK) + return err; + + return HAL_OK; +} + + +/* + * Wrap/unwrap n 64-bit blocks of plaintext. + * The wrapped/unwrapped key is returned in the same buffer. + */ + +static hal_error_t do_wrap_unwrap(hal_core_t *core, uint8_t * const C, const size_t n) +{ + hal_error_t err; + + hal_assert(core != NULL && C != NULL && n > 0); + + /* n is the number of 64-bit (8-byte) blocks in the input. + * KEYWRAP_LEN_R_DATA is the number of 4-byte data registers in the core. + */ + if (n == 0 || n > KEYWRAP_LEN_R_DATA * 2) + return HAL_ERROR_BAD_ARGUMENTS; + + /* write the AIV to A */ + if ((err = hal_io_write(core, KEYWRAP_ADDR_A0, C, 8)) != HAL_OK) + return err; + + /* write the length to RLEN */ + uint32_t nn = htonl(n); + if ((err = hal_io_write(core, KEYWRAP_ADDR_RLEN, (const uint8_t *)&nn, 4)) != HAL_OK) + return err; + + /* write the data to R_DATA */ + if ((err = hal_io_write(core, KEYWRAP_ADDR_R_DATA, C + 8, 8 * n)) != HAL_OK) + return err; + + /* start the wrap/unwrap operation, and wait for it to complete */ + if ((err = hal_io_next(core)) != HAL_OK || + (err = hal_io_wait_ready(core)) != HAL_OK) + return err; + + /* read the A registers */ + if ((err = hal_io_read(core, KEYWRAP_ADDR_A0, C, 8)) != HAL_OK) + return err; + + /* read the data from R_DATA */ + if ((err = hal_io_read(core, KEYWRAP_ADDR_R_DATA, C + 8, 8 * n)) != HAL_OK) + return err; + + return HAL_OK; +} + + +/* + * Wrap plaintext Q using KEK K, placing result in C. + * + * Q and C can overlap. For encrypt-in-place, use Q = C + 8 (that is, + * leave 8 empty bytes before the plaintext). + * + * Use hal_keywrap_ciphertext_length() to calculate the correct + * buffer size. + */ + +hal_error_t hal_keywrap_wrap(hal_core_t *core, + const uint8_t *K, const size_t K_len, + const uint8_t * const Q, + const size_t Q_len, + uint8_t *C, + size_t *C_len) +{ + const size_t calculated_C_len = hal_keywrap_ciphertext_length(Q_len); + const int free_core = (core == NULL); + hal_error_t err; + size_t n; + + hal_assert(calculated_C_len % 8 == 0); + + if (Q == NULL || C == NULL || C_len == NULL || *C_len < calculated_C_len) + return HAL_ERROR_BAD_ARGUMENTS; + + if (free_core && (err = hal_core_alloc(KEYWRAP_NAME, &core, NULL)) != HAL_OK) + return err; + + if ((err = load_kek(core, K, K_len, KEK_encrypting)) != HAL_OK) + goto out; + + *C_len = calculated_C_len; + + if (C + 8 != Q) + memmove(C + 8, Q, Q_len); + if (Q_len % 8 != 0) + memset(C + 8 + Q_len, 0, 8 - (Q_len % 8)); + C[0] = 0xA6; + C[1] = 0x59; + C[2] = 0x59; + C[3] = 0xA6; + C[4] = (Q_len >> 24) & 0xFF; + C[5] = (Q_len >> 16) & 0xFF; + C[6] = (Q_len >> 8) & 0xFF; + C[7] = (Q_len >> 0) & 0xFF; + + n = calculated_C_len/8 - 1; + + /* Make sure the key expansion has completed. */ + if ((err = hal_io_wait_ready(core)) != HAL_OK) + goto out; + + err = do_wrap_unwrap(core, C, n); + +out: + if (free_core) + hal_core_free(core); + return err; +} + + +/* + * Unwrap ciphertext C using KEK K, placing result in Q. + * + * Q should be the same size as C. Q and C can overlap. + */ + +hal_error_t hal_keywrap_unwrap(hal_core_t *core, + const uint8_t *K, const size_t K_len, + const uint8_t * const C, + const size_t C_len, + uint8_t *Q, + size_t *Q_len) +{ + const int free_core = core == NULL; + hal_error_t err; + size_t n; + size_t m; + + if (C == NULL || Q == NULL || C_len % 8 != 0 || C_len < 16 || Q_len == NULL || *Q_len < C_len) + return HAL_ERROR_BAD_ARGUMENTS; + + if (free_core && (err = hal_core_alloc(KEYWRAP_NAME, &core, NULL)) != HAL_OK) + return err; + + if ((err = load_kek(core, K, K_len, KEK_decrypting)) != HAL_OK) + goto out; + + n = (C_len / 8) - 1; + + if (Q != C) + memmove(Q, C, C_len); + + /* Make sure the key expansion has completed. */ + if ((err = hal_io_wait_ready(core)) != HAL_OK) + goto out; + + if ((err = do_wrap_unwrap(core, Q, n)) != HAL_OK) + goto out; + + if (Q[0] != 0xA6 || Q[1] != 0x59 || Q[2] != 0x59 || Q[3] != 0xA6) { + err = HAL_ERROR_KEYWRAP_BAD_MAGIC; + goto out; + } + + m = (((((Q[4] << 8) + Q[5]) << 8) + Q[6]) << 8) + Q[7]; + + if (m <= 8 * (n - 1) || m > 8 * n) { + err = HAL_ERROR_KEYWRAP_BAD_LENGTH; + goto out; + } + + if (m % 8 != 0) + for (size_t i = m + 8; i < 8 * (n + 1); i++) + if (Q[i] != 0x00) { + err = HAL_ERROR_KEYWRAP_BAD_PADDING; + goto out; + } + + *Q_len = m; + + memmove(Q, Q + 8, m); + +out: + if (free_core) + hal_core_free(core); + return err; +} + +/* + * "Any programmer who fails to comply with the standard naming, formatting, + * or commenting conventions should be shot. If it so happens that it is + * inconvenient to shoot him, then he is to be politely requested to recode + * his program in adherence to the above standard." + * -- Michael Spier, Digital Equipment Corporation + * + * Local variables: + * indent-tabs-mode: nil + * End: + */ |