mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
commit
d16d911f42
333
sys/crypto/modes/ocb.c
Normal file
333
sys/crypto/modes/ocb.c
Normal file
@ -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 <mathias@tausig.at>
|
||||
*
|
||||
*/
|
||||
|
||||
#include "crypto/modes/ocb.h"
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#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_* = <empty string> */
|
||||
|
||||
/* 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;
|
||||
}
|
||||
}
|
110
sys/include/crypto/modes/ocb.h
Normal file
110
sys/include/crypto/modes/ocb.h
Normal file
@ -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 <mathias@tausig.at>
|
||||
*/
|
||||
|
||||
#ifndef CRYPTO_MODES_OCB_H
|
||||
#define CRYPTO_MODES_OCB_H
|
||||
|
||||
#include "crypto/ciphers.h"
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#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 */
|
||||
/** @} */
|
441
tests/unittests/tests-crypto/tests-crypto-modes-ocb.c
Normal file
441
tests/unittests/tests-crypto/tests-crypto-modes-ocb.c
Normal file
@ -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;
|
||||
}
|
@ -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());
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user