diff --git a/sys/crypto/modes/ccm.c b/sys/crypto/modes/ccm.c index 406a315ddb..9a75e547ad 100644 --- a/sys/crypto/modes/ccm.c +++ b/sys/crypto/modes/ccm.c @@ -34,7 +34,7 @@ static inline int min(int a, int b) } } -int ccm_compute_cbc_mac(cipher_t *cipher, const uint8_t iv[16], +static int ccm_compute_cbc_mac(cipher_t *cipher, const uint8_t iv[16], const uint8_t *input, size_t length, uint8_t *mac) { uint8_t offset, block_size, mac_enc[16] = { 0 }; @@ -63,7 +63,7 @@ int ccm_compute_cbc_mac(cipher_t *cipher, const uint8_t iv[16], } -int ccm_create_mac_iv(cipher_t *cipher, uint8_t auth_data_len, uint8_t M, +static int ccm_create_mac_iv(cipher_t *cipher, uint8_t auth_data_len, uint8_t M, uint8_t L, const uint8_t *nonce, uint8_t nonce_len, size_t plaintext_len, uint8_t X1[16]) { @@ -99,14 +99,14 @@ int ccm_create_mac_iv(cipher_t *cipher, uint8_t auth_data_len, uint8_t M, return 0; } -int ccm_compute_adata_mac(cipher_t *cipher, const uint8_t *auth_data, +static int ccm_compute_adata_mac(cipher_t *cipher, const uint8_t *auth_data, uint32_t auth_data_len, uint8_t X1[16]) { if (auth_data_len > 0) { int len; - /* 16 octet block size + max. 10 len encoding */ - uint8_t auth_data_encoded[26], len_encoding = 0; + /* Create a block with the encoded length. Block length is always 16 */ + uint8_t auth_data_encoded[CCM_BLOCK_SIZE], len_encoding = 0; /* If 0 < l(a) < (2^16 - 2^8), then the length field is encoded as two * octets. (RFC3610 page 2) @@ -123,12 +123,32 @@ int ccm_compute_adata_mac(cipher_t *cipher, const uint8_t *auth_data, return -1; } - memcpy(auth_data_encoded + len_encoding, auth_data, auth_data_len); + uint8_t auth_data_len_in_encoded = + (auth_data_len >= + (uint32_t)CCM_BLOCK_SIZE - + len_encoding) ? ((uint32_t)CCM_BLOCK_SIZE - + len_encoding) : + auth_data_len; + memcpy(auth_data_encoded + len_encoding, auth_data, + auth_data_len_in_encoded); + /* Calculate the MAC over the first block of AAD + heading length encoding */ len = ccm_compute_cbc_mac(cipher, X1, auth_data_encoded, - auth_data_len + len_encoding, X1); + auth_data_len_in_encoded + len_encoding, X1); + if (len < 0) { return -1; } + + /* Calculate the MAC for the remainder of the AAD (if there is one) */ + if (auth_data_len_in_encoded < auth_data_len) { + len = ccm_compute_cbc_mac(cipher, X1, + auth_data + auth_data_len_in_encoded, + auth_data_len - auth_data_len_in_encoded, + X1); + if (len < 0) { + return -1; + } + } } return 0; @@ -169,6 +189,7 @@ int cipher_encrypt_ccm(cipher_t *cipher, /* Create B0, encrypt it (X1) and use it as mac_iv */ block_size = cipher_get_block_size(cipher); + assert(block_size == CCM_BLOCK_SIZE); if (ccm_create_mac_iv(cipher, auth_data_len, mac_length, length_encoding, nonce, nonce_len, input_len, mac_iv) < 0) { return CCM_ERR_INVALID_DATA_LENGTH; @@ -179,6 +200,7 @@ int cipher_encrypt_ccm(cipher_t *cipher, if (len < 0) { return len; } + len = ccm_compute_cbc_mac(cipher, mac_iv, input, input_len, mac); if (len < 0) { return len; @@ -236,6 +258,7 @@ int cipher_decrypt_ccm(cipher_t *cipher, /* Compute first stream block */ nonce_counter[0] = length_encoding - 1; block_size = cipher_get_block_size(cipher); + assert(block_size == CCM_BLOCK_SIZE); memcpy(&nonce_counter[1], nonce, min(nonce_len, (size_t)15 - length_encoding)); len = cipher_encrypt_ctr(cipher, nonce_counter, block_size, zero_block, diff --git a/sys/include/crypto/modes/ccm.h b/sys/include/crypto/modes/ccm.h index 19879c43ca..70d62f9f9a 100644 --- a/sys/include/crypto/modes/ccm.h +++ b/sys/include/crypto/modes/ccm.h @@ -37,6 +37,12 @@ extern "C" { #define CCM_ERR_INVALID_MAC_LENGTH (-5) /** @} */ +/** + * @brief Block size required for the cipher. CCM is only defined for 128 bit ciphers. + */ +#define CCM_BLOCK_SIZE 16 + + /** * @brief Encrypt and authenticate data of arbitrary length in ccm mode. * diff --git a/tests/sys_crypto/tests-crypto-modes-ccm.c b/tests/sys_crypto/tests-crypto-modes-ccm.c index 84f15a8f4e..fb3ad69321 100644 --- a/tests/sys_crypto/tests-crypto-modes-ccm.c +++ b/tests/sys_crypto/tests-crypto-modes-ccm.c @@ -823,6 +823,47 @@ static const uint8_t TEST_NIST_3_EXPECTED[] = { }; static const size_t TEST_NIST_3_EXPECTED_LEN = 52; +/* Tests from Project Wycheproof */ +/* See https://github.com/google/wycheproof/blob/master/testvectors/aes_ccm_test.json */ + +static const uint8_t TEST_WYCHEPROOF_28_KEY[] = { + 0x20, 0xbb, 0xf7, 0x4c, 0x1e, 0x63, 0x98, 0x2c, + 0x47, 0x2c, 0x47, 0x43, 0x56, 0x9e, 0x4c, 0x84, +}; +static const size_t TEST_WYCHEPROOF_28_KEY_LEN = 16; +static const uint8_t TEST_WYCHEPROOF_28_NONCE[] = { + 0x45, 0x9f, 0xc7, 0xc0, 0x04, 0xbf, 0x46, 0x32, + 0x3a, 0x02, 0xd8, 0x46, +}; +static const size_t TEST_WYCHEPROOF_28_NONCE_LEN = 12; +static const size_t TEST_WYCHEPROOF_28_MAC_LEN = 16; +static const uint8_t TEST_WYCHEPROOF_28_INPUT[] = { + /* AAD */ + 0x4f, 0x22, 0x85, 0xce, 0x3d, 0xaf, 0xa5, 0x28, + 0xc6, 0x94, 0xa5, 0x27, 0x2d, 0x3b, 0x7b, 0x92, + 0x90, 0x97, 0xdb, 0x39, 0x87, 0x72, 0x65, 0x3b, + 0xd9, 0xbb, 0xbd, 0xb3, 0xb2, 0xc8, 0xe1, + /* PLAINTEXT */ + 0x6d, 0xb5, 0x09, 0x92, 0xe8, 0xfb, 0xbe, 0xe1, + 0x5d, 0x49, 0x79, 0xd3, 0xe3, 0x22, 0xda, 0xcd, +}; +static const size_t TEST_WYCHEPROOF_28_INPUT_LEN = 16; +static const size_t TEST_WYCHEPROOF_28_ADATA_LEN = 31; +static const uint8_t TEST_WYCHEPROOF_28_EXPECTED[] = { + /* AAD */ + 0x4f, 0x22, 0x85, 0xce, 0x3d, 0xaf, 0xa5, 0x28, + 0xc6, 0x94, 0xa5, 0x27, 0x2d, 0x3b, 0x7b, 0x92, + 0x90, 0x97, 0xdb, 0x39, 0x87, 0x72, 0x65, 0x3b, + 0xd9, 0xbb, 0xbd, 0xb3, 0xb2, 0xc8, 0xe1, + /* CIPHERTEXT */ + 0x87, 0x03, 0xe4, 0x46, 0x97, 0x13, 0x8c, 0x58, + 0x53, 0x2d, 0x97, 0xee, 0x99, 0x23, 0x1d, 0x94, + /* MAC */ + 0xf1, 0x4c, 0x2f, 0x39, 0xa4, 0x87, 0x1a, 0x4a, + 0x16, 0xc4, 0x2f, 0x6f, 0xe8, 0x78, 0xde, 0xef, +}; +static const size_t TEST_WYCHEPROOF_28_EXPECTED_LEN = 63; + /* Share test buffer output */ static uint8_t data[60]; @@ -852,7 +893,6 @@ static void test_encrypt_op(const uint8_t *key, uint8_t key_len, TEST_ASSERT_EQUAL_INT(output_expected_len, len); cmp = compare(output_expected, data, len); TEST_ASSERT_MESSAGE(1 == cmp, "wrong ciphertext"); - } static void test_decrypt_op(const uint8_t *key, uint8_t key_len, @@ -930,6 +970,8 @@ static void test_crypto_modes_ccm_encrypt(void) do_test_encrypt_op(NIST_1); do_test_encrypt_op(NIST_2); do_test_encrypt_op(NIST_3); + + do_test_encrypt_op(WYCHEPROOF_28); } #define do_test_decrypt_op(name) do { \ @@ -977,6 +1019,8 @@ static void test_crypto_modes_ccm_decrypt(void) do_test_decrypt_op(NIST_1); do_test_decrypt_op(NIST_2); do_test_decrypt_op(NIST_3); + + do_test_decrypt_op(WYCHEPROOF_28); }