diff --git a/sys/crypto/modes/ocb.c b/sys/crypto/modes/ocb.c new file mode 100644 index 000000000000..003520efd896 --- /dev/null +++ b/sys/crypto/modes/ocb.c @@ -0,0 +1,333 @@ +/* + * Copyright (C) 2018 Mathias Tausig + * + * 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 sys_crypto + * @{ + * + * @file + * @brief Offset Codebook (OCB3) AEAD mode as specified in RFC 7253 + * + * @author Mathias Tausig + * + */ + +#include "crypto/modes/ocb.h" +#include +#include + +#define OCB_MODE_ENCRYPT 1 +#define OCB_MODE_DECRYPT 2 + +struct ocb_state { + cipher_t *cipher; + uint8_t l_star[16]; + uint8_t l_zero[16]; + uint8_t l_dollar[16]; + uint8_t checksum[16]; + uint8_t offset[16]; +}; + +typedef struct ocb_state ocb_state_t; + +static void double_block(uint8_t source[16], uint8_t dest[16]) +{ + uint8_t msb = source[0] >> 7; + + for (uint8_t i = 0; i < 15; ++i) { + dest[i] = source[i] << 1 | source[i + 1] >> 7; + } + dest[15] = (source[15] << 1) ^ (0x87 * msb); +} + +static size_t ntz(size_t n) +{ + /* ntz must only be run on positive values */ + if (n == 0) { + return SIZE_MAX; + } + + size_t ret = 0; + + while (n % 2 == 0) { + ++ret; + n = n >> 1; + } + return ret; +} + +static void calculate_l_i(uint8_t l_zero[16], size_t i, uint8_t output[16]) +{ + memcpy(output, l_zero, 16); + while ((i--) > 0) { + double_block(output, output); + } +} + +static void xor_block(uint8_t block1[16], uint8_t block2[16], uint8_t output[16]) +{ + for (uint8_t i = 0; i < 16; ++i) { + output[i] = block1[i] ^ block2[i]; + } +} + +static void processBlock(ocb_state_t *state, size_t blockNumber, uint8_t input[16], uint8_t output[16], uint8_t mode) +{ + /* Offset_i = Offset_{i-1} xor L_{ntz(i)} */ + uint8_t l_i[16]; + + calculate_l_i(state->l_zero, ntz(blockNumber + 1), l_i); + xor_block(state->offset, l_i, state->offset); + /* Sum_i = Sum_{i-1} xor ENCIPHER(K, A_i xor Offset_i) */ + uint8_t cipher_output[16], cipher_input[16]; + xor_block(input, state->offset, cipher_input); + if (mode == OCB_MODE_ENCRYPT) { + state->cipher->interface->encrypt(&(state->cipher->context), cipher_input, cipher_output); + } + else if (mode == OCB_MODE_DECRYPT) { + state->cipher->interface->decrypt(&(state->cipher->context), cipher_input, cipher_output); + } + xor_block(state->offset, cipher_output, output); + /* Checksum_i = Checksum_{i-1} xor P_i */ + if (mode == OCB_MODE_ENCRYPT) { + xor_block(state->checksum, input, state->checksum); + } + else if (mode == OCB_MODE_DECRYPT) { + xor_block(state->checksum, output, state->checksum); + } +} + +static void hash(ocb_state_t *state, uint8_t *data, size_t data_len, uint8_t output[16]) +{ + /* Calculate the number of full blocks in data */ + size_t m = (data_len - (data_len % 16)) / 16; + size_t remaining_data_len = data_len - m * 16; + + /* Sum_0 = zeros(128) */ + memset(output, 0, 16); + /* Offset_0 = zeros(128) */ + uint8_t offset[16]; + memset(offset, 0, 16); + for (size_t i = 0; i < m; ++i) { + /* Offset_i = Offset_{i-1} xor L_{ntz(i)} */ + uint8_t l_i[16]; + calculate_l_i(state->l_zero, ntz(i + 1), l_i); + xor_block(offset, l_i, offset); + /* Sum_i = Sum_{i-1} xor ENCIPHER(K, A_i xor Offset_i) */ + uint8_t enciphered_block[16], cipher_input[16]; + xor_block(data, offset, cipher_input); + state->cipher->interface->encrypt(&(state->cipher->context), cipher_input, enciphered_block); + xor_block(output, enciphered_block, output); + + data += 16; + } + if (remaining_data_len > 0) { + /* Offset_* = Offset_m xor L_* */ + xor_block(offset, state->l_star, offset); + /* CipherInput = (A_* || 1 || zeros(127-bitlen(A_*))) xor Offset_* */ + uint8_t cipher_input[16]; + memset(cipher_input, 0, 16); + memcpy(cipher_input, data, remaining_data_len); + cipher_input[remaining_data_len] = 0x80; + xor_block(cipher_input, offset, cipher_input); + /* Sum = Sum_m xor ENCIPHER(K, CipherInput) */ + uint8_t enciphered_block[16]; + state->cipher->interface->encrypt(&(state->cipher->context), cipher_input, enciphered_block); + xor_block(output, enciphered_block, output); + } +} + +static void init_ocb(cipher_t *cipher, uint8_t tag_len, uint8_t *nonce, size_t nonce_len, ocb_state_t *state) +{ + + state->cipher = cipher; + + /* Key-dependent variables + + L_* = ENCIPHER(K, zeros(128)) + L_$ = double(L_*) + L_0 = double(L_$) + L_i = double(L_{i-1}) for every integer i > 0 + */ + uint8_t zero_block[16]; + memset(zero_block, 0, 16); + cipher->interface->encrypt(&(cipher->context), zero_block, state->l_star); + double_block(state->l_star, state->l_dollar); + double_block(state->l_dollar, state->l_zero); + + /* Nonce-dependent and per-encryption variables */ + /* Nonce = num2str(TAGLEN mod 128,7) || zeros(120-bitlen(N)) || 1 || N */ + uint8_t nonce_padded[16]; + memset(nonce_padded, 0, 16); + nonce_padded[0] = (tag_len * 8) << 1; + nonce_padded[15 - nonce_len] = 0x01; + memcpy(nonce_padded + 16 - nonce_len, nonce, nonce_len); + + /* bottom = str2num(Nonce[123..128])*/ + uint8_t bottom = (nonce_padded[15] << 2) >> 2; + /* Ktop = ENCIPHER(K, Nonce[1..122] || zeros(6)) */ + nonce_padded[15] = nonce_padded[15] & 0xC0; + uint8_t ktop[16]; + cipher->interface->encrypt(&(cipher->context), nonce_padded, ktop); + + /* Stretch = Ktop || (Ktop[1..64] xor Ktop[9..72]) */ + uint8_t stretch[24]; + memcpy(stretch, ktop, 16); + for (uint8_t i = 0; i < 8; ++i) { + stretch[16 + i] = ktop[i] ^ ktop[i + 1]; + } + + /* Offset_0 = Stretch[1+bottom..128+bottom] */ + uint8_t offset_start_byte = bottom / 8; + uint8_t offset_start_bit = bottom - offset_start_byte * 8; + for (uint8_t i = 0; i < 16; ++i) { + state->offset[i] = (stretch[offset_start_byte + i] << offset_start_bit) | (stretch[offset_start_byte + i + 1] >> (8 - offset_start_bit)); + } + + /* Checksum_0 = zeros(128) */ + memset(state->checksum, 0, 16); +} + +static int32_t run_ocb(cipher_t *cipher, uint8_t *auth_data, uint32_t auth_data_len, + uint8_t tag[16], uint8_t tag_len, uint8_t *nonce, size_t nonce_len, + uint8_t *input, size_t input_len, uint8_t *output, uint8_t mode) +{ + + /* OCB mode only works for ciphers of block length 16 */ + if (cipher->interface->block_size != 16) { + return OCB_ERR_INVALID_BLOCK_LENGTH; + } + + /* The tag can be at most 128 bit long */ + if (tag_len > 16 || tag_len == 0) { + return OCB_ERR_INVALID_TAG_LENGTH; + } + + /* The nonce can be at most 120 bit long */ + if (nonce_len >= 16 || nonce_len == 0) { + return OCB_ERR_INVALID_NONCE_LENGTH; + } + + ocb_state_t state; + init_ocb(cipher, tag_len, nonce, nonce_len, &state); + + /* Calculate the number of full blocks in data */ + size_t m = (input_len - (input_len % 16)) / 16; + size_t remaining_input_len = input_len - m * 16; + + /* Process any whole blocks */ + size_t output_pos = 0; + for (size_t i = 0; i < m; ++i) { + processBlock(&state, i, input, output + output_pos, mode); + output_pos += 16; + input += 16; + } + + /* Process any final partial block and compute raw tag */ + if (remaining_input_len > 0) { + /* Offset_* = Offset_m xor L_* */ + xor_block(state.offset, state.l_star, state.offset); + + /* Pad = ENCIPHER(K, Offset_*) */ + uint8_t pad[16]; + cipher->interface->encrypt(&(cipher->context), state.offset, pad); + + /* Encrypt: C_* = P_* xor Pad[1..bitlen(P_*)] */ + /* Decrypt: P_* = C_* xor Pad[1..bitlen(C_*)] */ + uint8_t final_block[remaining_input_len]; + memcpy(final_block, pad, remaining_input_len); + for (uint8_t i = 0; i < remaining_input_len; ++i) { + final_block[i] = input[i] ^ pad[i]; + } + memcpy(output + output_pos, final_block, remaining_input_len); + + /* Checksum_* = Checksum_m xor (P_* || 1 || zeros(127-bitlen(P_*))) */ + uint8_t padded_block[16]; + memset(padded_block, 0, 16); + if (mode == OCB_MODE_ENCRYPT) { + memcpy(padded_block, input, remaining_input_len); + } + else if (mode == OCB_MODE_DECRYPT) { + memcpy(padded_block, output + output_pos, remaining_input_len); + } + padded_block[remaining_input_len] = 0x80; + xor_block(state.checksum, padded_block, state.checksum); + output_pos += remaining_input_len; + } + /* else: C_* = */ + + /* Tag = ENCIPHER(K, Checksum_* xor Offset_* xor L_$) xor HASH(K,A) */ + /* Tag = ENCIPHER(K, Checksum_m xor Offset_m xor L_$) xor HASH(K,A) */ + uint8_t hash_value[16]; + hash(&state, auth_data, auth_data_len, hash_value); + uint8_t cipher_data[16]; + xor_block(state.checksum, state.offset, cipher_data); + xor_block(cipher_data, state.l_dollar, cipher_data); + + cipher->interface->encrypt(&(cipher->context), cipher_data, tag); + xor_block(tag, hash_value, tag); + + return output_pos; +} + +int32_t cipher_encrypt_ocb(cipher_t *cipher, uint8_t *auth_data, size_t auth_data_len, + uint8_t tag_len, uint8_t *nonce, size_t nonce_len, + uint8_t *input, size_t input_len, uint8_t *output) +{ + uint8_t tag[16]; + + if (input_len > (uint32_t)(INT32_MAX - tag_len)) { + // We would not be able to return the proper output length for data this long + return OCB_ERR_INVALID_DATA_LENGTH; + } + + int cipher_text_length = run_ocb(cipher, auth_data, auth_data_len, + tag, tag_len, nonce, nonce_len, + input, input_len, output, OCB_MODE_ENCRYPT); + + if (cipher_text_length < 0) { + // An error occured. Return the error code + return cipher_text_length; + } + /* C = C_1 || C_2 || ... || C_m || C_* || Tag[1..TAGLEN] */ + memcpy(output + cipher_text_length, tag, tag_len); + return (cipher_text_length + tag_len); +} + +int32_t cipher_decrypt_ocb(cipher_t *cipher, uint8_t *auth_data, size_t auth_data_len, + uint8_t tag_len, uint8_t *nonce, size_t nonce_len, + uint8_t *input, size_t input_len, uint8_t *output) +{ + + if (input_len - tag_len > INT32_MAX) { + // We would not be able to return the proper output length for data this long + return OCB_ERR_INVALID_DATA_LENGTH; + } + + uint8_t tag[16]; + int plain_text_length = run_ocb(cipher, auth_data, auth_data_len, + tag, tag_len, nonce, nonce_len, + input, input_len - tag_len, output, OCB_MODE_DECRYPT); + + if (plain_text_length < 0) { + // An error occured. Retur the error code + return plain_text_length; + } + /* Check the tag */ + if (memcmp(tag, input + input_len - tag_len, tag_len) == 0) { + /* Tag is valid */ + /* P = P_1 || P_2 || ... || P_m || P_* */ + return plain_text_length; + } + else { + /* Tag is not valid */ + /* Destroy the decrypted data to prevent misuse */ + memset(output, 0, input_len - tag_len); + return OCB_ERR_INVALID_TAG; + } +} diff --git a/sys/include/crypto/modes/ocb.h b/sys/include/crypto/modes/ocb.h new file mode 100644 index 000000000000..88b8a94c2cee --- /dev/null +++ b/sys/include/crypto/modes/ocb.h @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2018 Mathias Tausig + * + * 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 sys_crypto + * @{ + * + * @file ocb.h + * @brief Offset Codebook (OCB3) AEAD mode as specified in RFC 7253 + * + * NOTE: The OCB algorithm is covered by patents in the USA owned by Phillip Rogaway. + * A free licence is granted for any open-source or non-military project. + * Check http://web.cs.ucdavis.edu/~rogaway/ocb/grant.htm for details. + * + * @author Mathias Tausig + */ + +#ifndef CRYPTO_MODES_OCB_H +#define CRYPTO_MODES_OCB_H + +#include "crypto/ciphers.h" +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @name OCB error codes + * @{ + */ + +/** + * Returned if a nonce of bad length (empty or more than 15 bytes) was used + */ +#define OCB_ERR_INVALID_NONCE_LENGTH (-2) +/** + * OCB only works with ciphers with a block size of 128 bit + */ +#define OCB_ERR_INVALID_BLOCK_LENGTH (-3) +/** + * Returned if the amount of input data cannot be handled by this implementation + */ +#define OCB_ERR_INVALID_DATA_LENGTH (-3) +/** + * Returned if a tag of bad length was requested (empty or more than 16 bytes) + */ +#define OCB_ERR_INVALID_TAG_LENGTH (-4) +/** + * Returned if the authentication failed during decryption + */ +#define OCB_ERR_INVALID_TAG (-5) + +/** @} */ + +/** + * @brief Encrypt and authenticate data of arbitrary length in OCB mode. + * + * @param cipher Already initialized cipher struct + * @param auth_data Additional data to authenticate in MAC + * @param auth_data_len Length of additional data + * @param tag_len Length of the appended tag (at leat 1, at most 16 bytes) + * + * @param nonce Nonce for the encryption (must be unique) + * @param nonce_len Length of the nonce in bytes (at most 15) + * @param input pointer to input data to encrypt + * @param input_len length of the input data. + * input_len + tag_len must be smaller than INT32_MAX (2^31-1) + * @param output pointer to allocated memory for encrypted data. + * The tag will be appended to the ciphertext. + * It has to be of size data_len + tag_len. + * @return Length of the encrypted data (including the tag) or a (negative) error code + */ +int32_t cipher_encrypt_ocb(cipher_t *cipher, uint8_t *auth_data, size_t auth_data_len, + uint8_t tag_len, uint8_t *nonce, size_t nonce_len, + uint8_t *input, size_t input_len, uint8_t *output); + +/** + * @brief Decrypt and verify the authentication of OCB encrypted data. + * + * @param cipher Already initialized cipher struct + * @param auth_data Additional data to authenticate in MAC + * @param auth_data_len Length of additional data + * @param tag_len Length of the appended tag (at leat 1, at most 16 bytes) + * + * @param nonce Nonce for the encryption (must be unique) + * @param nonce_len Length of the nonce in bytes (at most 15) + * @param input pointer to the ciphertext with the tag appended + * @param input_len length of the input data. + * input_len - tag_len must be smaller than INT32_MAX (2^31-1) + * @param output pointer to allocated memory for the plaintext data. + * It has to be of size input_len - tag_len. + * Will contain only zeroes, if the authentication fails. + * @return Length of the plaintext data or a (negative) error code + */ +int32_t cipher_decrypt_ocb(cipher_t *cipher, uint8_t *auth_data, size_t auth_data_len, + uint8_t tag_len, uint8_t *nonce, size_t nonce_len, + uint8_t *input, size_t input_len, uint8_t *output); +#ifdef __cplusplus +} +#endif + +#endif /* CRYPTO_MODES_OCB_H */ +/** @} */ diff --git a/tests/unittests/tests-crypto/tests-crypto-modes-ocb.c b/tests/unittests/tests-crypto/tests-crypto-modes-ocb.c new file mode 100644 index 000000000000..16aa62258ebe --- /dev/null +++ b/tests/unittests/tests-crypto/tests-crypto-modes-ocb.c @@ -0,0 +1,441 @@ +/* + * Copyright (C) 2018 Mathias Tausig + * + * 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. + */ + +#include "crypto/ciphers.h" +#include "crypto/modes/ocb.h" +#include "tests-crypto.h" + +/* Test vectors from RFC 7253, Appendix A */ +/* The key (K) has a fixed value, the tag length is + 128 bits, and the nonce (N) increments. + + K : 000102030405060708090A0B0C0D0E0F + */ +static uint8_t TEST_KEY[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F +}; +static uint8_t TEST_KEY_LEN = 16; + +/* Test 1: + N: BBAA99887766554433221100 + A: + P: + C: 785407BFFFC8AD9EDCC5520AC9111EE6 + */ +static uint8_t *TEST_1_KEY = TEST_KEY; + +static uint8_t TEST_1_NONCE[] = { + 0xBB, 0xAA, 0x99, 0x88, 0x77, 0x66, 0x55, 0x44, + 0x33, 0x22, 0x11, 0x00, +}; +static size_t TEST_1_NONCE_LEN = 12; + +static uint8_t *TEST_1_INPUT; +static size_t TEST_1_INPUT_LEN = 0; + +static uint8_t *TEST_1_ADATA; +static size_t TEST_1_ADATA_LEN = 0; + +static uint8_t TEST_1_EXPECTED[] = { + 0x78, 0x54, 0x07, 0xBF, 0xFF, 0xC8, 0xAD, 0x9E, + 0xDC, 0xC5, 0x52, 0x0A, 0xC9, 0x11, 0x1E, 0xE6 +}; +static size_t TEST_1_EXPECTED_LEN = 16; + +static uint8_t TEST_1_TAG_LEN = 16; + + + +/* Test 2: + N: BBAA99887766554433221101 + A: 0001020304050607 + P: 0001020304050607 + C: 6820B3657B6F615A5725BDA0D3B4EB3A257C9AF1F8F03009 + */ + +static uint8_t *TEST_2_KEY = TEST_KEY; + +static uint8_t TEST_2_NONCE[] = { + 0xBB, 0xAA, 0x99, 0x88, 0x77, 0x66, 0x55, 0x44, + 0x33, 0x22, 0x11, 0x01, +}; +static size_t TEST_2_NONCE_LEN = 12; + +static uint8_t TEST_2_INPUT[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 +}; +static size_t TEST_2_INPUT_LEN = sizeof(TEST_2_INPUT); + +static uint8_t TEST_2_ADATA[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 +}; +static size_t TEST_2_ADATA_LEN = sizeof(TEST_2_ADATA); + +static uint8_t TEST_2_EXPECTED[] = { + 0x68, 0x20, 0xB3, 0x65, 0x7B, 0x6F, 0x61, 0x5A, + 0x57, 0x25, 0xBD, 0xA0, 0xD3, 0xB4, 0xEB, 0x3A, + 0x25, 0x7C, 0x9A, 0xF1, 0xF8, 0xF0, 0x30, 0x09 +}; +static size_t TEST_2_EXPECTED_LEN = sizeof(TEST_2_EXPECTED); + +static uint8_t TEST_2_TAG_LEN = 16; + + +/* Test 3: + N: BBAA99887766554433221102 + A: 0001020304050607 + P: + C: 81017F8203F081277152FADE694A0A00 + */ + +static uint8_t *TEST_3_KEY = TEST_KEY; + +static uint8_t TEST_3_NONCE[] = { + 0xBB, 0xAA, 0x99, 0x88, 0x77, 0x66, 0x55, 0x44, + 0x33, 0x22, 0x11, 0x02, +}; +static size_t TEST_3_NONCE_LEN = 12; + +static uint8_t *TEST_3_INPUT; + +static size_t TEST_3_INPUT_LEN = 0; + +static uint8_t TEST_3_ADATA[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 +}; +static size_t TEST_3_ADATA_LEN = sizeof(TEST_2_ADATA); + +static uint8_t TEST_3_EXPECTED[] = { + 0x81, 0x01, 0x7F, 0x82, 0x03, 0xF0, 0x81, 0x27, + 0x71, 0x52, 0xFA, 0xDE, 0x69, 0x4A, 0x0A, 0x00 +}; +static size_t TEST_3_EXPECTED_LEN = sizeof(TEST_3_EXPECTED); + +static uint8_t TEST_3_TAG_LEN = 16; + + +/* Test 4: + N: BBAA99887766554433221103 + A: + P: 0001020304050607 + C: 45DD69F8F5AAE72414054CD1F35D82760B2CD00D2F99BFA9 + */ + +static uint8_t *TEST_4_KEY = TEST_KEY; + +static uint8_t TEST_4_NONCE[] = { + 0xBB, 0xAA, 0x99, 0x88, 0x77, 0x66, 0x55, 0x44, + 0x33, 0x22, 0x11, 0x03, +}; +static size_t TEST_4_NONCE_LEN = 12; + +static uint8_t TEST_4_INPUT[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 +}; +static size_t TEST_4_INPUT_LEN = sizeof(TEST_4_INPUT); + +static uint8_t *TEST_4_ADATA; + +static size_t TEST_4_ADATA_LEN = 0; + +static uint8_t TEST_4_EXPECTED[] = { + 0x45, 0xDD, 0x69, 0xF8, 0xF5, 0xAA, 0xE7, 0x24, + 0x14, 0x05, 0x4C, 0xD1, 0xF3, 0x5D, 0x82, 0x76, + 0x0B, 0x2C, 0xD0, 0x0D, 0x2F, 0x99, 0xBF, 0xA9 +}; +static size_t TEST_4_EXPECTED_LEN = sizeof(TEST_4_EXPECTED); + +static uint8_t TEST_4_TAG_LEN = 16; + + +/* Test 16: + N: BBAA9988776655443322110F + A: + P: 000102030405060708090A0B0C0D0E0F1011121314151617 + 18191A1B1C1D1E1F2021222324252627 + C: 4412923493C57D5DE0D700F753CCE0D1D2D95060122E9F15 + A5DDBFC5787E50B5CC55EE507BCB084E479AD363AC366B95 + A98CA5F3000B1479 + */ + +static uint8_t *TEST_16_KEY = TEST_KEY; + +static uint8_t TEST_16_NONCE[] = { + 0xBB, 0xAA, 0x99, 0x88, 0x77, 0x66, 0x55, 0x44, + 0x33, 0x22, 0x11, 0x0F, +}; +static size_t TEST_16_NONCE_LEN = 12; + +static uint8_t TEST_16_INPUT[] = { + 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, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27 +}; +static size_t TEST_16_INPUT_LEN = sizeof(TEST_16_INPUT); + +static uint8_t *TEST_16_ADATA; +static size_t TEST_16_ADATA_LEN = 0; + +static uint8_t TEST_16_EXPECTED[] = { + 0x44, 0x12, 0x92, 0x34, 0x93, 0xC5, 0x7D, 0x5D, + 0xE0, 0xD7, 0x00, 0xF7, 0x53, 0xCC, 0xE0, 0xD1, + 0xD2, 0xD9, 0x50, 0x60, 0x12, 0x2E, 0x9F, 0x15, + 0xA5, 0xDD, 0xBF, 0xC5, 0x78, 0x7E, 0x50, 0xB5, + 0xCC, 0x55, 0xEE, 0x50, 0x7B, 0xCB, 0x08, 0x4E, + 0x47, 0x9A, 0xD3, 0x63, 0xAC, 0x36, 0x6B, 0x95, + 0xA9, 0x8C, 0xA5, 0xF3, 0x00, 0x0B, 0x14, 0x79 +}; +static size_t TEST_16_EXPECTED_LEN = sizeof(TEST_16_EXPECTED); + +static uint8_t TEST_16_TAG_LEN = 16; + + + +/* Test 17: + + The next tuple shows a result with a tag length of 96 bits and a + different key. + + K: 0F0E0D0C0B0A09080706050403020100 + + N: BBAA9988776655443322110D + A: 000102030405060708090A0B0C0D0E0F1011121314151617 + 18191A1B1C1D1E1F2021222324252627 + P: 000102030405060708090A0B0C0D0E0F1011121314151617 + 18191A1B1C1D1E1F2021222324252627 + C: 1792A4E31E0755FB03E31B22116E6C2DDF9EFD6E33D536F1 + A0124B0A55BAE884ED93481529C76B6AD0C515F4D1CDD4FD + AC4F02AA + + */ + +static uint8_t TEST_17_KEY[] = { + 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, + 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00 +}; + +static uint8_t TEST_17_NONCE[] = { + 0xBB, 0xAA, 0x99, 0x88, 0x77, 0x66, 0x55, 0x44, + 0x33, 0x22, 0x11, 0x0D, +}; +static size_t TEST_17_NONCE_LEN = 12; + +static uint8_t TEST_17_INPUT[] = { + 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, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27 +}; +static size_t TEST_17_INPUT_LEN = sizeof(TEST_17_INPUT); + +static uint8_t TEST_17_ADATA[] = { + 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, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27 +}; +static size_t TEST_17_ADATA_LEN = sizeof(TEST_17_ADATA); + +static uint8_t TEST_17_EXPECTED[] = { + 0x17, 0x92, 0xA4, 0xE3, 0x1E, 0x07, 0x55, 0xFB, + 0x03, 0xE3, 0x1B, 0x22, 0x11, 0x6E, 0x6C, 0x2D, + 0xDF, 0x9E, 0xFD, 0x6E, 0x33, 0xD5, 0x36, 0xF1, + 0xA0, 0x12, 0x4B, 0x0A, 0x55, 0xBA, 0xE8, 0x84, + 0xED, 0x93, 0x48, 0x15, 0x29, 0xC7, 0x6B, 0x6A, + 0xD0, 0xC5, 0x15, 0xF4, 0xD1, 0xCD, 0xD4, 0xFD, + 0xAC, 0x4F, 0x02, 0xAA +}; +static size_t TEST_17_EXPECTED_LEN = sizeof(TEST_17_EXPECTED); + +static uint8_t TEST_17_TAG_LEN = 12; + + +/* Share test buffer output */ +static uint8_t data[60]; + +static void test_encrypt_op(uint8_t *key, uint8_t key_len, + uint8_t *adata, size_t adata_len, + uint8_t *nonce, uint8_t nonce_len, + uint8_t *plain, size_t plain_len, + uint8_t *output_expected, + size_t output_expected_len, + uint8_t tag_length) +{ + cipher_t cipher; + int len, err, cmp; + + TEST_ASSERT_MESSAGE(sizeof(data) >= output_expected_len, + "Output buffer too small"); + + err = cipher_init(&cipher, CIPHER_AES_128, key, key_len); + TEST_ASSERT_EQUAL_INT(1, err); + + len = cipher_encrypt_ocb(&cipher, adata, adata_len, + tag_length, nonce, nonce_len, + plain, plain_len, data); + TEST_ASSERT_MESSAGE(len > 0, "Encryption failed"); + + TEST_ASSERT_EQUAL_INT(output_expected_len, len); + + cmp = compare(output_expected, data, len); + TEST_ASSERT_MESSAGE(1 == cmp, "wrong ciphertext"); +} + +#define do_test_encrypt_op(name) do { \ + test_encrypt_op(TEST_ ## name ## _KEY, TEST_KEY_LEN, \ + TEST_ ## name ## _ADATA, TEST_ ## name ## _ADATA_LEN, \ + TEST_ ## name ## _NONCE, TEST_ ## name ## _NONCE_LEN, \ + \ + TEST_ ## name ## _INPUT, TEST_ ## name ## _INPUT_LEN, \ + \ + TEST_ ## name ## _EXPECTED, \ + TEST_ ## name ## _EXPECTED_LEN, TEST_ ## name ## _TAG_LEN \ + ); \ +} while (0) + +static void test_crypto_modes_ocb_encrypt(void) +{ + do_test_encrypt_op(1); + do_test_encrypt_op(2); + do_test_encrypt_op(3); + do_test_encrypt_op(4); + do_test_encrypt_op(16); + do_test_encrypt_op(17); +} + +static void test_decrypt_op(uint8_t *key, uint8_t key_len, + uint8_t *adata, size_t adata_len, + uint8_t *nonce, uint8_t nonce_len, + uint8_t *encrypted, size_t encrypted_len, + uint8_t *output_expected, + size_t output_expected_len, + uint8_t tag_length) +{ + cipher_t cipher; + int len, err, cmp; + + TEST_ASSERT_MESSAGE(sizeof(data) >= output_expected_len, + "Output buffer too small"); + + err = cipher_init(&cipher, CIPHER_AES_128, key, key_len); + TEST_ASSERT_EQUAL_INT(1, err); + + len = cipher_decrypt_ocb(&cipher, adata, adata_len, + tag_length, nonce, nonce_len, + encrypted, encrypted_len, data); + TEST_ASSERT_MESSAGE(len >= 0, "Decryption failed"); + + TEST_ASSERT_EQUAL_INT(output_expected_len, len); + cmp = compare(output_expected, data, len); + TEST_ASSERT_MESSAGE(1 == cmp, "wrong ciphertext"); + + /* do some negative tests for the tag verification */ + if (adata_len > 0) { + /* Drop one byte of auth data */ + len = cipher_decrypt_ocb(&cipher, adata, adata_len - 1, + tag_length, nonce, nonce_len, + encrypted, encrypted_len, data); + TEST_ASSERT_EQUAL_INT(OCB_ERR_INVALID_TAG, len); + /* Alter one byte of auth data */ + adata[0] = adata[0] ^ 0x01; + len = cipher_decrypt_ocb(&cipher, adata, adata_len, + tag_length, nonce, nonce_len, + encrypted, encrypted_len, data); + TEST_ASSERT_EQUAL_INT(OCB_ERR_INVALID_TAG, len); + adata[0] = adata[0] ^ 0x01; + } + /* Drop one byte of the nonce */ + len = cipher_decrypt_ocb(&cipher, adata, adata_len, + tag_length, nonce, nonce_len - 1, + encrypted, encrypted_len, data); + TEST_ASSERT_EQUAL_INT(OCB_ERR_INVALID_TAG, len); + /* Alter one byte of the nonce */ + nonce[0] = nonce[0] ^ 0x01; + len = cipher_decrypt_ocb(&cipher, adata, adata_len, + tag_length, nonce, nonce_len, + encrypted, encrypted_len, data); + TEST_ASSERT_EQUAL_INT(OCB_ERR_INVALID_TAG, len); + nonce[0] = nonce[0] ^ 0x01; + /* Alter one byte of the ciphertext */ + encrypted[0] = encrypted[0] ^ 0x01; + len = cipher_decrypt_ocb(&cipher, adata, adata_len, + tag_length, nonce, nonce_len, + encrypted, encrypted_len, data); + TEST_ASSERT_EQUAL_INT(OCB_ERR_INVALID_TAG, len); + encrypted[0] = encrypted[0] ^ 0x01; + /* Alter one byte of the tag */ + encrypted[encrypted_len - 1] = encrypted[encrypted_len - 1] ^ 0x01; + len = cipher_decrypt_ocb(&cipher, adata, adata_len, + tag_length, nonce, nonce_len, + encrypted, encrypted_len, data); + TEST_ASSERT_EQUAL_INT(OCB_ERR_INVALID_TAG, len); + encrypted[encrypted_len - 1] = encrypted[encrypted_len - 1] ^ 0x01; +} + +#define do_test_decrypt_op(name) do { \ + test_decrypt_op(TEST_ ## name ## _KEY, TEST_KEY_LEN, \ + TEST_ ## name ## _ADATA, TEST_ ## name ## _ADATA_LEN, \ + TEST_ ## name ## _NONCE, TEST_ ## name ## _NONCE_LEN, \ + \ + TEST_ ## name ## _EXPECTED, \ + TEST_ ## name ## _EXPECTED_LEN, \ + \ + TEST_ ## name ## _INPUT, \ + TEST_ ## name ## _INPUT_LEN, \ + \ + TEST_ ## name ## _TAG_LEN \ + ); \ +} while (0) + +static void test_crypto_modes_ocb_decrypt(void) +{ + do_test_decrypt_op(1); + do_test_decrypt_op(2); + do_test_decrypt_op(3); + do_test_decrypt_op(4); + do_test_decrypt_op(16); + do_test_decrypt_op(17); +} + +static void test_crypto_modes_ocb_bad_parameter_values(void) +{ + uint8_t key[16], auth_data[1], nonce[16], input[16], output[32]; + cipher_t cipher; + + cipher_init(&cipher, CIPHER_AES_128, key, 16); + /* tag length must be positive */ + int rv = cipher_encrypt_ocb(&cipher, auth_data, sizeof(auth_data), 0, nonce, 15, input, sizeof(input), output); + TEST_ASSERT_EQUAL_INT(OCB_ERR_INVALID_TAG_LENGTH, rv); + /* tag length must be <= 16 */ + rv = cipher_encrypt_ocb(&cipher, auth_data, sizeof(auth_data), 17, nonce, 15, input, sizeof(input), output); + TEST_ASSERT_EQUAL_INT(OCB_ERR_INVALID_TAG_LENGTH, rv); + /* nonce must not be empty */ + rv = cipher_encrypt_ocb(&cipher, auth_data, sizeof(auth_data), 16, nonce, 0, input, sizeof(input), output); + TEST_ASSERT_EQUAL_INT(OCB_ERR_INVALID_NONCE_LENGTH, rv); + /* nonce must be <=15 */ + rv = cipher_encrypt_ocb(&cipher, auth_data, sizeof(auth_data), 16, nonce, 16, input, sizeof(input), output); + TEST_ASSERT_EQUAL_INT(OCB_ERR_INVALID_NONCE_LENGTH, rv); +} + +Test *tests_crypto_modes_ocb_tests(void) +{ + EMB_UNIT_TESTFIXTURES(fixtures) { + new_TestFixture(test_crypto_modes_ocb_encrypt), + new_TestFixture(test_crypto_modes_ocb_decrypt), + new_TestFixture(test_crypto_modes_ocb_bad_parameter_values), + }; + + EMB_UNIT_TESTCALLER(crypto_modes_ocb_tests, NULL, NULL, fixtures); + + return (Test *)&crypto_modes_ocb_tests; +} diff --git a/tests/unittests/tests-crypto/tests-crypto.c b/tests/unittests/tests-crypto/tests-crypto.c index d10a57fa9657..1274fbeef7d6 100644 --- a/tests/unittests/tests-crypto/tests-crypto.c +++ b/tests/unittests/tests-crypto/tests-crypto.c @@ -18,6 +18,7 @@ void tests_crypto(void) TESTS_RUN(tests_crypto_aes_tests()); TESTS_RUN(tests_crypto_cipher_tests()); TESTS_RUN(tests_crypto_modes_ccm_tests()); + TESTS_RUN(tests_crypto_modes_ocb_tests()); TESTS_RUN(tests_crypto_modes_ecb_tests()); TESTS_RUN(tests_crypto_modes_cbc_tests()); TESTS_RUN(tests_crypto_modes_ctr_tests()); diff --git a/tests/unittests/tests-crypto/tests-crypto.h b/tests/unittests/tests-crypto/tests-crypto.h index e4a1af38c5f0..c8ff2c7611c7 100644 --- a/tests/unittests/tests-crypto/tests-crypto.h +++ b/tests/unittests/tests-crypto/tests-crypto.h @@ -64,6 +64,7 @@ static inline int compare(const uint8_t *a, const uint8_t *b, uint8_t len) Test* tests_crypto_aes_tests(void); Test* tests_crypto_cipher_tests(void); Test* tests_crypto_modes_ccm_tests(void); +Test* tests_crypto_modes_ocb_tests(void); Test* tests_crypto_modes_ecb_tests(void); Test* tests_crypto_modes_cbc_tests(void); Test* tests_crypto_modes_ctr_tests(void);