

37 changed files with 2125 additions and 401 deletions
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Freie Universitรคt Berlin |
||||
* |
||||
* 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 ciphers.c |
||||
* @author Nico von Geyso <nico.geyso@fu-berlin.de> |
||||
* @} |
||||
*/ |
||||
|
||||
#include <string.h> |
||||
#include <stdio.h> |
||||
#include "crypto/ciphers.h" |
||||
|
||||
|
||||
int cipher_init(cipher_t* cipher, cipher_id_t cipher_id, const uint8_t* key, |
||||
uint8_t key_size) |
||||
{ |
||||
if (key_size > cipher_id->max_key_size) { |
||||
return CIPHER_ERR_INVALID_KEY_SIZE; |
||||
} |
||||
|
||||
cipher->interface = cipher_id; |
||||
return cipher->interface->init(&cipher->context, key, key_size); |
||||
|
||||
} |
||||
|
||||
|
||||
int cipher_encrypt(const cipher_t* cipher, const uint8_t* input, uint8_t* output) |
||||
{ |
||||
return cipher->interface->encrypt(&cipher->context, input, output); |
||||
} |
||||
|
||||
|
||||
int cipher_decrypt(const cipher_t* cipher, const uint8_t* input, uint8_t* output) |
||||
{ |
||||
return cipher->interface->decrypt(&cipher->context, input, output); |
||||
} |
||||
|
||||
|
||||
int cipher_get_block_size(const cipher_t* cipher) |
||||
{ |
||||
return cipher->interface->block_size; |
||||
} |
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Nico von Geyso <nico.geyso@fu-berlin.de> |
||||
* Copyright (C) 2015 Renรฉ Kijewski <rene.kijewski@fu-berlin.de> |
||||
* |
||||
* 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/helper.h" |
||||
|
||||
void crypto_block_inc_ctr(uint8_t block[16], int L) |
||||
{ |
||||
uint8_t *b = &block[15]; |
||||
for (int i = 0; i < L; ++i, --b) { |
||||
if (++*b != 0) { |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
|
||||
int crypto_equals(uint8_t *a, uint8_t *b, size_t len) |
||||
{ |
||||
uint8_t diff = 0; |
||||
for (size_t i = 0; i < len; ++i, ++a, ++b) { |
||||
diff |= (*a ^ *b); |
||||
} |
||||
|
||||
diff |= (diff >> 1) | (diff << 7); |
||||
diff |= (diff >> 2) | (diff << 6); |
||||
diff |= (diff >> 4) | (diff << 4); |
||||
++diff; |
||||
|
||||
return diff; |
||||
} |
@ -0,0 +1,2 @@
|
||||
MODULE = cipher_modes
|
||||
include $(RIOTBASE)/Makefile.base |
@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Freie Universitรคt Berlin |
||||
* |
||||
* 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_modes |
||||
* @{ |
||||
* |
||||
* @file |
||||
* @brief Crypto mode - cipher block chaining |
||||
* |
||||
* @author Nico von Geyso <nico.geyso@fu-berlin.de> |
||||
* |
||||
* @} |
||||
*/ |
||||
|
||||
|
||||
#include <string.h> |
||||
#include "crypto/modes/cbc.h" |
||||
|
||||
int cipher_encrypt_cbc(cipher_t* cipher, uint8_t iv[16], |
||||
uint8_t* input, size_t length, uint8_t* output) |
||||
{ |
||||
size_t offset = 0; |
||||
uint8_t block_size, input_block[CIPHER_MAX_BLOCK_SIZE] = {0}, |
||||
*output_block_last; |
||||
|
||||
block_size = cipher_get_block_size(cipher); |
||||
if (length % block_size != 0) { |
||||
return CIPHER_ERR_INVALID_LENGTH; |
||||
} |
||||
|
||||
output_block_last = iv; |
||||
do { |
||||
/* CBC-Mode: XOR plaintext with ciphertext of (n-1)-th block */ |
||||
memcpy(input_block, input + offset, block_size); |
||||
for (int i = 0; i < block_size; ++i) { |
||||
input_block[i] ^= output_block_last[i]; |
||||
} |
||||
|
||||
if (cipher_encrypt(cipher, input_block, output + offset) != 1) { |
||||
return CIPHER_ERR_ENC_FAILED; |
||||
} |
||||
|
||||
output_block_last = output + offset; |
||||
offset += block_size; |
||||
} while (offset < length); |
||||
|
||||
return offset; |
||||
} |
||||
|
||||
|
||||
int cipher_decrypt_cbc(cipher_t* cipher, uint8_t iv[16], |
||||
uint8_t* input, size_t length, uint8_t* output) |
||||
{ |
||||
size_t offset = 0; |
||||
uint8_t* input_block, *output_block, *input_block_last, block_size; |
||||
|
||||
|
||||
block_size = cipher_get_block_size(cipher); |
||||
if (length % block_size != 0) { |
||||
return CIPHER_ERR_INVALID_LENGTH; |
||||
} |
||||
|
||||
input_block_last = iv; |
||||
do { |
||||
input_block = input + offset; |
||||
output_block = output + offset; |
||||
|
||||
if (cipher_decrypt(cipher, input_block, output_block) != 1) { |
||||
return CIPHER_ERR_DEC_FAILED; |
||||
} |
||||
|
||||
/* CBC-Mode: XOR plaintext with ciphertext of (n-1)-th block */ |
||||
for (uint8_t i = 0; i < block_size; ++i) { |
||||
output_block[i] ^= input_block_last[i]; |
||||
} |
||||
|
||||
input_block_last = input_block; |
||||
offset += block_size; |
||||
} while (offset < length); |
||||
|
||||
return offset; |
||||
} |
@ -0,0 +1,257 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Freie Universitรคt Berlin |
||||
* |
||||
* 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_modes |
||||
* @{ |
||||
* |
||||
* @file |
||||
* @brief Crypto mode - counter with CBC-MAC |
||||
* |
||||
* @author Nico von Geyso <nico.geyso@fu-berlin.de> |
||||
* |
||||
* @} |
||||
*/ |
||||
|
||||
#include <string.h> |
||||
#include "debug.h" |
||||
#include "crypto/helper.h" |
||||
#include "crypto/modes/ctr.h" |
||||
#include "crypto/modes/ccm.h" |
||||
|
||||
static inline int min(int a, int b) |
||||
{ |
||||
if (a < b) { |
||||
return a; |
||||
} |
||||
else { |
||||
return b; |
||||
} |
||||
} |
||||
|
||||
int ccm_compute_cbc_mac(cipher_t* cipher, uint8_t iv[16], |
||||
uint8_t* input, size_t length, uint8_t* mac) |
||||
{ |
||||
uint8_t offset, block_size, mac_enc[16] = {0}; |
||||
|
||||
block_size = cipher_get_block_size(cipher); |
||||
memcpy(mac, iv, 16); |
||||
offset = 0; |
||||
do { |
||||
uint8_t block_size_input = (length - offset > block_size) ? |
||||
block_size : length - offset; |
||||
|
||||
/* CBC-Mode: XOR plaintext with ciphertext of (n-1)-th block */ |
||||
for (int i = 0; i < block_size_input; ++i) { |
||||
mac[i] ^= input[offset + i]; |
||||
} |
||||
|
||||
if (cipher_encrypt(cipher, mac, mac_enc) != 1) { |
||||
return CIPHER_ERR_ENC_FAILED; |
||||
} |
||||
|
||||
memcpy(mac, mac_enc, block_size); |
||||
offset += block_size_input; |
||||
} while (offset < length); |
||||
|
||||
return offset; |
||||
} |
||||
|
||||
|
||||
int ccm_create_mac_iv(cipher_t* cipher, uint8_t auth_data_len, uint8_t M, |
||||
uint8_t L, uint8_t* nonce, uint8_t nonce_len, |
||||
size_t plaintext_len, uint8_t X1[16]) |
||||
{ |
||||
uint8_t M_, L_; |
||||
|
||||
/* ensure everything is set to zero */ |
||||
memset(X1, 0, 16); |
||||
|
||||
/* set flags in B[0] - bit format:
|
||||
7 6 5..3 2..0 |
||||
Reserved Adata M_ L_ */ |
||||
M_ = (M - 2) / 2; |
||||
L_ = L - 1; |
||||
X1[0] = 64 * (auth_data_len > 0) + 8 * M_ + L_; |
||||
|
||||
/* copy nonce to B[1..15-L] */ |
||||
memcpy(&X1[1], nonce, min(nonce_len, 15 - L)); |
||||
|
||||
/* write plaintext_len to B[15..16-L] */ |
||||
for (uint8_t i = 15; i > 16 - L; --i) { |
||||
X1[i] = plaintext_len & 0xff; |
||||
plaintext_len >>= 8; |
||||
} |
||||
|
||||
/* if there is still data, plaintext_len was too big */ |
||||
if (plaintext_len > 0) { |
||||
return CIPHER_ERR_INVALID_LENGTH; |
||||
} |
||||
|
||||
if (cipher_encrypt(cipher, X1, X1) != 1) { |
||||
return CIPHER_ERR_ENC_FAILED; |
||||
} |
||||
return 0; |
||||
} |
||||
|
||||
int ccm_compute_adata_mac(cipher_t* cipher, 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; |
||||
|
||||
if ( auth_data_len < (((uint32_t) 2) << 16)) { /* length (0x0001 ... 0xFEFF) */ |
||||
len_encoding = 2; |
||||
|
||||
auth_data_encoded[1] = auth_data_len & 0xFF; |
||||
auth_data_encoded[0] = (auth_data_len >> 8) & 0xFF; |
||||
} else { |
||||
DEBUG("UNSUPPORTED Adata length"); |
||||
return -1; |
||||
} |
||||
|
||||
memcpy(auth_data_encoded + len_encoding, auth_data, auth_data_len); |
||||
len = ccm_compute_cbc_mac(cipher, X1, auth_data_encoded, auth_data_len + len_encoding, X1); |
||||
if (len < 0) { |
||||
return -1; |
||||
} |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
|
||||
int cipher_encrypt_ccm(cipher_t* cipher, uint8_t* auth_data, uint32_t auth_data_len, |
||||
uint8_t mac_length, uint8_t length_encoding, |
||||
uint8_t* nonce, size_t nonce_len, |
||||
uint8_t* input, size_t input_len, |
||||
uint8_t* output) |
||||
{ |
||||
int len = -1; |
||||
uint32_t length_max; |
||||
uint8_t nonce_counter[16] = {0}, mac_iv[16] = {0}, mac[16] = {0}, |
||||
stream_block[16] = {0}, zero_block[16] = {0}, block_size; |
||||
|
||||
if (mac_length % 2 != 0 || mac_length < 4 || mac_length > 16) { |
||||
return CCM_ERR_INVALID_MAC_LENGTH; |
||||
} |
||||
|
||||
length_max = 2 << (8 * length_encoding); |
||||
if (length_encoding < 2 || length_encoding > 8 || |
||||
input_len - auth_data_len > length_max) { |
||||
return CCM_ERR_INVALID_LENGTH_ENCODING; |
||||
} |
||||
|
||||
/* Create B0, encrypt it (X1) and use it as mac_iv */ |
||||
block_size = cipher_get_block_size(cipher); |
||||
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; |
||||
} |
||||
|
||||
/* MAC calulation (T) with additional data and plaintext */ |
||||
ccm_compute_adata_mac(cipher, auth_data, auth_data_len, mac_iv); |
||||
len = ccm_compute_cbc_mac(cipher, mac_iv, input, input_len, mac); |
||||
if (len < 0) { |
||||
return len; |
||||
} |
||||
|
||||
/* Compute first stream block */ |
||||
nonce_counter[0] = length_encoding - 1; |
||||
memcpy(&nonce_counter[1], nonce, |
||||
min(nonce_len, (size_t) 15 - length_encoding)); |
||||
len = cipher_encrypt_ctr(cipher, nonce_counter, block_size, |
||||
zero_block, block_size, stream_block); |
||||
if (len < 0) { |
||||
return len; |
||||
} |
||||
|
||||
/* Encrypt message in counter mode */ |
||||
crypto_block_inc_ctr(nonce_counter, block_size - nonce_len); |
||||
len = cipher_encrypt_ctr(cipher, nonce_counter, nonce_len, input, |
||||
input_len, output); |
||||
if (len < 0) { |
||||
return len; |
||||
} |
||||
|
||||
/* auth value: mac ^ first stream block */ |
||||
for (uint8_t i = 0; i < mac_length; ++i) { |
||||
output[len + i] = mac[i] ^ stream_block[i]; |
||||
} |
||||
|
||||
return len + mac_length; |
||||
} |
||||
|
||||
|
||||
int cipher_decrypt_ccm(cipher_t* cipher, uint8_t* auth_data, |
||||
uint32_t auth_data_len, uint8_t mac_length, |
||||
uint8_t length_encoding, uint8_t* nonce, size_t nonce_len, |
||||
uint8_t* input, size_t input_len, uint8_t* plain) |
||||
{ |
||||
int len = -1; |
||||
uint32_t length_max; |
||||
uint8_t nonce_counter[16] = {0}, mac_iv[16] = {0}, mac[16] = {0}, |
||||
mac_recv[16] = {0}, stream_block[16] = {0}, zero_block[16] = {0}, |
||||
plain_len, block_size; |
||||
|
||||
if (mac_length % 2 != 0 || mac_length < 4 || mac_length > 16) { |
||||
return CCM_ERR_INVALID_MAC_LENGTH; |
||||
} |
||||
|
||||
length_max = 2 << (8 * length_encoding); |
||||
if (length_encoding < 2 || length_encoding > 8 || |
||||
input_len - auth_data_len > length_max) { |
||||
return CCM_ERR_INVALID_LENGTH_ENCODING; |
||||
} |
||||
|
||||
/* Compute first stream block */ |
||||
nonce_counter[0] = length_encoding - 1; |
||||
block_size = cipher_get_block_size(cipher); |
||||
memcpy(&nonce_counter[1], nonce, min(nonce_len, (size_t) 15 - length_encoding)); |
||||
len = cipher_encrypt_ctr(cipher, nonce_counter, block_size, zero_block, |
||||
block_size, stream_block); |
||||
if (len < 0) { |
||||
return len; |
||||
} |
||||
|
||||
/* Decrypt message in counter mode */ |
||||
plain_len = input_len - mac_length; |
||||
crypto_block_inc_ctr(nonce_counter, block_size - nonce_len); |
||||
len = cipher_encrypt_ctr(cipher, nonce_counter, nonce_len, input, |
||||
plain_len, plain); |
||||
if (len < 0) { |
||||
return len; |
||||
} |
||||
|
||||
/* Create B0, encrypt it (X1) and use it as mac_iv */ |
||||
if (ccm_create_mac_iv(cipher, auth_data_len, mac_length, length_encoding, |
||||
nonce, nonce_len, plain_len, mac_iv) < 0) { |
||||
return CCM_ERR_INVALID_DATA_LENGTH; |
||||
} |
||||
|
||||
/* MAC calulation (T) with additional data and plaintext */ |
||||
ccm_compute_adata_mac(cipher, auth_data, auth_data_len, mac_iv); |
||||
len = ccm_compute_cbc_mac(cipher, mac_iv, plain, plain_len, mac); |
||||
if (len < 0) { |
||||
return len; |
||||
} |
||||
|
||||
/* mac = input[plain_len...plain_len+mac_length] ^ first stream block */ |
||||
for (uint8_t i = 0; i < mac_length; ++i) { |
||||
mac_recv[i] = input[len + i] ^ stream_block[i]; |
||||
} |
||||
|
||||
if (!crypto_equals(mac_recv, mac, mac_length)) { |
||||
return CCM_ERR_INVALID_CBC_MAC; |
||||
} |
||||
|
||||
return plain_len; |
||||
} |
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Freie Universitรคt Berlin |
||||
* |
||||
* 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_modes |
||||
* @{ |
||||
* |
||||
* @file |
||||
* @brief Crypto mode - Counter |
||||
* |
||||
* @author Nico von Geyso <nico.geyso@fu-berlin.de> |
||||
* |
||||
* @} |
||||
*/ |
||||
|
||||
#include "crypto/helper.h" |
||||
#include "crypto/modes/ctr.h" |
||||
|
||||
int cipher_encrypt_ctr(cipher_t* cipher, uint8_t nonce_counter[16], |
||||
uint8_t nonce_len, uint8_t* input, size_t length, |
||||
uint8_t* output) |
||||
{ |
||||
size_t offset = 0; |
||||
uint8_t stream_block[16] = {0}, block_size; |
||||
|
||||
block_size = cipher_get_block_size(cipher); |
||||
do { |
||||
uint8_t block_size_input; |
||||
|
||||
if (cipher_encrypt(cipher, nonce_counter, stream_block) != 1) { |
||||
return CIPHER_ERR_ENC_FAILED; |
||||
} |
||||
|
||||
block_size_input = (length - offset > block_size) ? |
||||
block_size : length - offset; |
||||
for (uint8_t i = 0; i < block_size_input; ++i) { |
||||
output[offset + i] = stream_block[i] ^ input[offset + i]; |
||||
} |
||||
|
||||
offset += block_size_input; |
||||
crypto_block_inc_ctr(nonce_counter, block_size - nonce_len); |
||||
} while (offset < length); |
||||
|
||||
return offset; |
||||
} |
||||
|
||||
int cipher_decrypt_ctr(cipher_t* cipher, uint8_t nonce_counter[16], |
||||
uint8_t nonce_len, uint8_t* input, size_t length, |
||||
uint8_t* output) |
||||
{ |
||||
return cipher_encrypt_ctr(cipher, nonce_counter, nonce_len, input, |
||||
length, output); |
||||
} |
@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Freie Universitรคt Berlin |
||||
* |
||||
* 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_modes |
||||
* @{ |
||||
* |
||||
* @file |
||||
* @brief Crypto mode - electronic code book |
||||
* |
||||
* @author Nico von Geyso <nico.geyso@fu-berlin.de> |
||||
* |
||||
* @} |
||||
*/ |
||||
|
||||
#include "crypto/modes/ecb.h" |
||||
|
||||
int cipher_encrypt_ecb(cipher_t* cipher, uint8_t* input, |
||||
size_t length, uint8_t* output) |
||||
{ |
||||
size_t offset; |
||||
uint8_t block_size; |
||||
|
||||
block_size = cipher_get_block_size(cipher); |
||||
if (length % block_size != 0) { |
||||
return CIPHER_ERR_INVALID_LENGTH; |
||||
} |
||||
|
||||
offset = 0; |
||||
do { |
||||
if (cipher_encrypt(cipher, input + offset, output + offset) != 1) { |
||||
return CIPHER_ERR_ENC_FAILED; |
||||
} |
||||
|
||||
offset += block_size; |
||||
} while (offset < length); |
||||
|
||||
return offset; |
||||
} |
||||
|
||||
int cipher_decrypt_ecb(cipher_t* cipher, uint8_t* input, |
||||
size_t length, uint8_t* output) |
||||
{ |
||||
size_t offset = 0; |
||||
uint8_t block_size; |
||||
|
||||
block_size = cipher_get_block_size(cipher); |
||||
if (length % block_size != 0) { |
||||
return CIPHER_ERR_INVALID_LENGTH; |
||||
} |
||||
|
||||
do { |
||||
if (cipher_decrypt(cipher, input + offset, output + offset) != 1) { |
||||
return CIPHER_ERR_DEC_FAILED; |
||||
} |
||||
|
||||
offset += block_size; |
||||
} while (offset < length); |
||||
|
||||
return offset; |
||||
} |