Browse Source

crypto: add initial Crypto module with AESCipher

This ads initial supprot for the PyCrypto AESCipher module.  This
supports ECB, CBC, and CTR.

The example from the PyCrypto manual needs to be adapted:

```
>>> from Crypto.Cipher import AES
>>> from Crypto import Random
>>>
>>> key = b'Sixteen byte key'
>>> iv = Random.new().read(AES.block_size)
>>> cipher = AES.new(key, AES.MODE_CFB, iv)
>>> msg = iv + cipher.encrypt(b'Attack at dawn')
```

This doesn't work with this implementation, because CFB is not supported.
Additionally, there is no `Random` module in Crypto.  Furthermore, modes
other than CFB all require plaintext to be padded to 16 bytes, as in
the PyCrypto module.

This has been tested with many of the official NIST test vectors,
such as those used in `pycryptodome` at
39626a5b01/lib/Crypto/SelfTest/Cipher/test_vectors/AES

Signed-off-by: Sean Cross <sean@xobs.io>
crypto-aes
Sean Cross 2 years ago
parent
commit
649d3371a8
  1. 5
      py/circuitpy_defns.mk
  2. 8
      py/circuitpy_mpconfig.h
  3. 5
      py/circuitpy_mpconfig.mk
  4. 71
      shared-bindings/crypto/__init__.c
  5. 48
      shared-bindings/crypto/__init__.h
  6. 207
      shared-bindings/crypto/aes.c
  7. 65
      shared-module/crypto/__init__.c
  8. 57
      shared-module/crypto/__init__.h
  9. 608
      shared-module/crypto/aes.c
  10. 105
      shared-module/crypto/aes.h

5
py/circuitpy_defns.mk

@ -136,6 +136,9 @@ endif @@ -136,6 +136,9 @@ endif
ifeq ($(CIRCUITPY_BUSIO),1)
SRC_PATTERNS += busio/% bitbangio/OneWire.%
endif
ifeq ($(CIRCUITPY_CRYPTO),1)
SRC_PATTERNS += crypto/%
endif
ifeq ($(CIRCUITPY_DIGITALIO),1)
SRC_PATTERNS += digitalio/%
endif
@ -342,6 +345,8 @@ SRC_SHARED_MODULE_ALL = \ @@ -342,6 +345,8 @@ SRC_SHARED_MODULE_ALL = \
bitbangio/__init__.c \
board/__init__.c \
busio/OneWire.c \
crypto/__init__.c \
crypto/aes.c \
displayio/Bitmap.c \
displayio/ColorConverter.c \
displayio/Display.c \

8
py/circuitpy_mpconfig.h

@ -321,6 +321,13 @@ extern const struct _mp_obj_module_t busio_module; @@ -321,6 +321,13 @@ extern const struct _mp_obj_module_t busio_module;
#define BUSIO_MODULE
#endif
#if CIRCUITPY_CRYPTO
extern const struct _mp_obj_module_t crypto_module;
#define CRYPTO_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_Crypto), (mp_obj_t)&crypto_module },
#else
#define CRYPTO_MODULE
#endif
#if CIRCUITPY_DIGITALIO
extern const struct _mp_obj_module_t digitalio_module;
#define DIGITALIO_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_digitalio), (mp_obj_t)&digitalio_module },
@ -637,6 +644,7 @@ extern const struct _mp_obj_module_t ustack_module; @@ -637,6 +644,7 @@ extern const struct _mp_obj_module_t ustack_module;
BLEIO_MODULE \
BOARD_MODULE \
BUSIO_MODULE \
CRYPTO_MODULE \
DIGITALIO_MODULE \
DISPLAYIO_MODULE \
FONTIO_MODULE \

5
py/circuitpy_mpconfig.mk

@ -139,6 +139,11 @@ CIRCUITPY_BUSIO = $(CIRCUITPY_DEFAULT_BUILD) @@ -139,6 +139,11 @@ CIRCUITPY_BUSIO = $(CIRCUITPY_DEFAULT_BUILD)
endif
CFLAGS += -DCIRCUITPY_BUSIO=$(CIRCUITPY_BUSIO)
ifndef CIRCUITPY_CRYPTO
CIRCUITPY_CRYPTO = $(CIRCUITPY_FULL_BUILD)
endif
CFLAGS += -DCIRCUITPY_CRYPTO=$(CIRCUITPY_CRYPTO)
ifndef CIRCUITPY_DIGITALIO
CIRCUITPY_DIGITALIO = $(CIRCUITPY_DEFAULT_BUILD)
endif

71
shared-bindings/crypto/__init__.c

@ -0,0 +1,71 @@ @@ -0,0 +1,71 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2017 Scott Shawcroft for Adafruit Industries
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <stdint.h>
#include "py/obj.h"
#include "py/runtime.h"
#include "__init__.h"
//| :mod:`aes` --- AES encryption and decryption routines
//| ========================================================
//|
//| .. module:: aes
//| :synopsis: Routines for performing AES encryption and decryption
//|
//| The `audiocore` module contains core classes for audio IO
//|
//| Libraries
//|
//| .. toctree::
//| :maxdepth: 3
//|
//|
STATIC const mp_rom_map_elem_t cipher_module_globals_table[] = {
{MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_Cipher)},
{MP_OBJ_NEW_QSTR(MP_QSTR_AES), MP_ROM_PTR(&aes_module)},
};
STATIC MP_DEFINE_CONST_DICT(cipher_module_globals, cipher_module_globals_table);
const mp_obj_module_t cipher_module = {
{&mp_type_module},
.globals = (mp_obj_dict_t *)&cipher_module_globals,
};
STATIC const mp_rom_map_elem_t crypto_module_globals_table[] = {
{MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_Crypto)},
{MP_OBJ_NEW_QSTR(MP_QSTR_Cipher), MP_ROM_PTR(&cipher_module)},
};
STATIC MP_DEFINE_CONST_DICT(crypto_module_globals, crypto_module_globals_table);
const mp_obj_module_t crypto_module = {
.base = {&mp_type_module},
.globals = (mp_obj_dict_t *)&crypto_module_globals,
};

48
shared-bindings/crypto/__init__.h

@ -0,0 +1,48 @@ @@ -0,0 +1,48 @@
/*
* This file is part of the Micro Python project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2017 Scott Shawcroft for Adafruit Industries
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_CRYPTO_H
#define MICROPY_INCLUDED_SHARED_BINDINGS_CRYPTO_H
#include "shared-module/crypto/__init__.h"
extern const mp_obj_module_t crypto_module;
extern const mp_obj_module_t aes_module;
void common_hal_aes_construct(aes_obj_t* self,
const uint8_t* key,
uint32_t key_length,
const uint8_t* iv,
int mode,
int counter);
void common_hal_aes_encrypt(aes_obj_t* self,
uint8_t* buffer,
size_t len);
void common_hal_aes_decrypt(aes_obj_t* self,
uint8_t* buffer,
size_t len);
#endif // MICROPY_INCLUDED_SHARED_BINDINGS_CRYPTO_H

207
shared-bindings/crypto/aes.c

@ -0,0 +1,207 @@ @@ -0,0 +1,207 @@
#include <stdint.h>
#include <string.h>
#include "py/obj.h"
#include "py/runtime.h"
#include "shared-bindings/crypto/__init__.h"
// Defined at the end of this file
extern const mp_obj_type_t aescipher_type;
STATIC mp_obj_t aescipher_make_new(size_t n_args, const mp_obj_t *pos_args,
mp_map_t *kw_args) {
enum { ARG_key, ARG_mode, ARG_IV, ARG_counter, ARG_segment_size };
static const mp_arg_t allowed_args[] = {
{MP_QSTR_key, MP_ARG_OBJ | MP_ARG_REQUIRED},
{MP_QSTR_mode, MP_ARG_INT},
{MP_QSTR_IV, MP_ARG_OBJ},
{MP_QSTR_counter, MP_ARG_OBJ},
{MP_QSTR_segment_size, MP_ARG_INT},
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
// Set defaults. These will be overridden in mp_arg_parse_all() if an
// argument is provided.
args[ARG_mode].u_int = AES_MODE_ECB;
args[ARG_IV].u_obj = NULL;
args[ARG_counter].u_int = 0;
args[ARG_segment_size].u_int =
8; // Only useful in CFB mode, which we don't support
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args),
allowed_args, args);
aes_obj_t *self = m_new_obj(aes_obj_t);
self->base.type = &aescipher_type;
mp_buffer_info_t bufinfo;
const uint8_t *key = NULL;
uint32_t key_length = 0;
if (mp_get_buffer(args[ARG_key].u_obj, &bufinfo, MP_BUFFER_READ)) {
if ((bufinfo.len != 16) && (bufinfo.len != 24) && (bufinfo.len != 32)) {
mp_raise_TypeError(translate("key must be 16, 24, or 32 bytes long"));
}
key = bufinfo.buf;
key_length = bufinfo.len;
} else {
mp_raise_TypeError(translate("no key was specified"));
}
int mode = args[ARG_mode].u_int;
switch (args[ARG_mode].u_int) {
case AES_MODE_CBC: /* CBC */
break;
case AES_MODE_ECB: /* ECB */
break;
case AES_MODE_CTR: /* CTR */
break;
default:
mp_raise_TypeError(translate("requested AES mode is unsupported"));
}
// IV is required for CBC mode and is ignored for other modes.
const uint8_t *iv = NULL;
if (args[ARG_IV].u_obj != NULL &&
mp_get_buffer(args[ARG_IV].u_obj, &bufinfo, MP_BUFFER_READ)) {
if (bufinfo.len != AES_BLOCKLEN) {
mp_raise_TypeError_varg(translate("iv must be %d bytes long"),
AES_BLOCKLEN);
}
iv = bufinfo.buf;
}
common_hal_aes_construct(self, key, key_length, iv, mode, args[ARG_counter].u_int);
return MP_OBJ_FROM_PTR(self);
}
// Used when constructing with `new AESCipher`
STATIC mp_obj_t aescipher_make_new_new(const mp_obj_type_t *type, size_t n_args,
const mp_obj_t *pos_args,
mp_map_t *kw_args) {
return aescipher_make_new(n_args, pos_args, kw_args);
}
STATIC byte *duplicate_data(const byte *src_buf, size_t len) {
byte *dest_buf = m_new(byte, len);
memcpy(dest_buf, src_buf, len);
return dest_buf;
}
STATIC mp_obj_t aescipher_encrypt_in_place(mp_obj_t aes_obj, mp_obj_t buf) {
if (!MP_OBJ_IS_TYPE(aes_obj, &aescipher_type)) {
mp_raise_TypeError_varg(translate("Expected a %q"), aescipher_type.name);
}
// Convert parameters into expected types.
aes_obj_t *aes = MP_OBJ_TO_PTR(aes_obj);
mp_buffer_info_t bufinfo;
mp_get_buffer_raise(buf, &bufinfo, MP_BUFFER_READ);
if ((bufinfo.len & (AES_BLOCKLEN - 1)) != 0) {
mp_raise_ValueError(translate("Buffer must be a multiple of 16 bytes"));
}
common_hal_aes_encrypt(aes, (uint8_t *)bufinfo.buf, bufinfo.len);
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_2(aescipher_encrypt_in_place_obj,
aescipher_encrypt_in_place);
STATIC mp_obj_t aescipher_encrypt(mp_obj_t aes_obj, mp_obj_t buf) {
if (!MP_OBJ_IS_TYPE(aes_obj, &aescipher_type)) {
mp_raise_TypeError_varg(translate("Expected a %q"), aescipher_type.name);
}
// Convert parameters into expected types.
aes_obj_t *aes = MP_OBJ_TO_PTR(aes_obj);
mp_buffer_info_t bufinfo;
mp_get_buffer_raise(buf, &bufinfo, MP_BUFFER_READ);
if ((bufinfo.len & (AES_BLOCKLEN - 1)) != 0) {
mp_raise_ValueError(translate("Buffer must be a multiple of 16 bytes"));
}
byte *dest_buf = duplicate_data((const byte *)bufinfo.buf, bufinfo.len);
common_hal_aes_encrypt(aes, (uint8_t *)dest_buf, bufinfo.len);
return mp_obj_new_bytearray_by_ref(bufinfo.len, dest_buf);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_2(aescipher_encrypt_obj, aescipher_encrypt);
STATIC mp_obj_t aescipher_decrypt_in_place(mp_obj_t aes_obj, mp_obj_t buf) {
if (!MP_OBJ_IS_TYPE(aes_obj, &aescipher_type)) {
mp_raise_TypeError_varg(translate("Expected a %q"), aescipher_type.name);
}
// Convert parameters into expected types.
aes_obj_t *aes = MP_OBJ_TO_PTR(aes_obj);
mp_buffer_info_t bufinfo;
mp_get_buffer_raise(buf, &bufinfo, MP_BUFFER_READ);
if ((bufinfo.len & (AES_BLOCKLEN - 1)) != 0) {
mp_raise_ValueError(translate("Buffer must be a multiple of 16 bytes"));
}
common_hal_aes_decrypt(aes, (uint8_t *)bufinfo.buf, bufinfo.len);
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_2(aescipher_decrypt_in_place_obj,
aescipher_decrypt_in_place);
STATIC mp_obj_t aescipher_decrypt(mp_obj_t aes_obj, mp_obj_t buf) {
if (!MP_OBJ_IS_TYPE(aes_obj, &aescipher_type)) {
mp_raise_TypeError_varg(translate("Expected a %q"), aescipher_type.name);
}
// Convert parameters into expected types.
aes_obj_t *aes = MP_OBJ_TO_PTR(aes_obj);
mp_buffer_info_t bufinfo;
mp_get_buffer_raise(buf, &bufinfo, MP_BUFFER_READ);
if ((bufinfo.len & (AES_BLOCKLEN - 1)) != 0) {
mp_raise_ValueError(translate("Buffer must be a multiple of 16 bytes"));
}
byte *dest_buf = duplicate_data((const byte *)bufinfo.buf, bufinfo.len);
common_hal_aes_decrypt(aes, (uint8_t *)dest_buf, bufinfo.len);
return mp_obj_new_bytearray_by_ref(bufinfo.len, dest_buf);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_2(aescipher_decrypt_obj, aescipher_decrypt);
STATIC const mp_obj_tuple_t mp_crypto_aes_key_size_obj = {
{&mp_type_tuple},
3,
{
MP_OBJ_NEW_SMALL_INT(16),
MP_OBJ_NEW_SMALL_INT(24),
MP_OBJ_NEW_SMALL_INT(32),
}};
STATIC const mp_rom_map_elem_t aescipher_locals_dict_table[] = {
// Methods
{MP_ROM_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_AES)},
{MP_ROM_QSTR(MP_QSTR_encrypt), (mp_obj_t)&aescipher_encrypt_obj},
{MP_ROM_QSTR(MP_QSTR_decrypt), (mp_obj_t)&aescipher_decrypt_obj},
{MP_ROM_QSTR(MP_QSTR_encrypt_in_place),
(mp_obj_t)&aescipher_encrypt_in_place_obj},
{MP_ROM_QSTR(MP_QSTR_decrypt_in_place),
(mp_obj_t)&aescipher_decrypt_in_place_obj},
};
STATIC MP_DEFINE_CONST_DICT(aescipher_locals_dict, aescipher_locals_dict_table);
const mp_obj_type_t aescipher_type = {
{&mp_type_type},
.name = MP_QSTR_AESCipher,
.make_new = aescipher_make_new_new,
.locals_dict = (mp_obj_dict_t *)&aescipher_locals_dict,
};
MP_DEFINE_CONST_FUN_OBJ_KW(aescipher_make_new_obj, 1, aescipher_make_new);
STATIC const mp_rom_map_elem_t aes_module_globals_table[] = {
{MP_ROM_QSTR(MP_QSTR_MODE_ECB), MP_ROM_INT(AES_MODE_ECB)},
{MP_ROM_QSTR(MP_QSTR_MODE_CBC), MP_ROM_INT(AES_MODE_CBC)},
{MP_ROM_QSTR(MP_QSTR_MODE_CTR), MP_ROM_INT(AES_MODE_CTR)},
{MP_ROM_QSTR(MP_QSTR_block_size), MP_ROM_INT(AES_BLOCKLEN)},
{MP_ROM_QSTR(MP_QSTR_key_size), (mp_obj_t)&mp_crypto_aes_key_size_obj},
{MP_ROM_QSTR(MP_QSTR_new), MP_ROM_PTR(&aescipher_make_new_obj)},
};
STATIC MP_DEFINE_CONST_DICT(aes_module_globals, aes_module_globals_table);
const mp_obj_module_t aes_module = {
{&mp_type_module},
.globals = (mp_obj_dict_t *)&aes_module_globals,
};

65
shared-module/crypto/__init__.c

@ -0,0 +1,65 @@ @@ -0,0 +1,65 @@
#include <string.h>
#include "py/runtime.h"
#include "shared-module/crypto/__init__.h"
void common_hal_aes_construct(aes_obj_t* self,
const uint8_t* key,
uint32_t key_length,
const uint8_t* iv,
int mode,
int counter) {
memset(&self->ctx, 0, sizeof(self->ctx));
if (iv != NULL) {
AES_init_ctx_iv(&self->ctx, key, key_length, iv);
} else {
AES_init_ctx(&self->ctx, key, key_length);
}
self->mode = mode;
self->counter = counter;
}
void common_hal_aes_encrypt(aes_obj_t* self,
uint8_t* buffer,
size_t length) {
switch (self->mode) {
case AES_MODE_ECB:
if (length != 16)
mp_raise_msg(&mp_type_ValueError, translate("ecb only operates on 16 bytes at a time"));
AES_ECB_encrypt(&self->ctx, buffer);
break;
case AES_MODE_CBC:
if ((length & 15) != 0)
mp_raise_msg(&mp_type_ValueError, translate("cbc blocks must be multiples of 16 bytes"));
AES_CBC_encrypt_buffer(&self->ctx, buffer, length);
break;
case AES_MODE_CTR:
AES_CTR_xcrypt_buffer(&self->ctx, buffer, length);
break;
default:
mp_raise_msg(&mp_type_ValueError, translate("unknown encryption mode"));
}
}
void common_hal_aes_decrypt(aes_obj_t* self,
uint8_t* buffer,
size_t length) {
switch (self->mode) {
case AES_MODE_ECB:
if (length != 16)
mp_raise_msg(&mp_type_ValueError, translate("ecp only operates on 16 bytes at a time"));
AES_ECB_decrypt(&self->ctx, buffer);
break;
case AES_MODE_CBC:
if ((length & 15) != 0)
mp_raise_msg(&mp_type_ValueError, translate("cbc blocks must be multiples of 16 bytes"));
AES_CBC_decrypt_buffer(&self->ctx, buffer, length);
break;
case AES_MODE_CTR:
AES_CTR_xcrypt_buffer(&self->ctx, buffer, length);
break;
default:
mp_raise_msg(&mp_type_ValueError, translate("unknown encryption mode"));
}
}

57
shared-module/crypto/__init__.h

@ -0,0 +1,57 @@ @@ -0,0 +1,57 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2018 Dan Halbert for Adafruit Industries
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef MICROPY_INCLUDED_SHARED_MODULE_CRYPTO__INIT__H
#define MICROPY_INCLUDED_SHARED_MODULE_CRYPTO__INIT__H
#include <stdbool.h>
#include <stdint.h>
#include "py/obj.h"
#include "py/proto.h"
#include "shared-module/crypto/aes.h"
enum AES_MODE {
AES_MODE_ECB = 1,
AES_MODE_CBC = 2,
AES_MODE_CTR = 6,
};
typedef struct {
mp_obj_base_t base;
// The tinyaes context
struct AES_ctx ctx;
// Which AES mode this instance of the object is configured to use
enum AES_MODE mode;
// Counter for running in CTR mode
uint32_t counter;
} aes_obj_t;
#endif // MICROPY_INCLUDED_SHARED_MODULE_CRYPTO__INIT__H

608
shared-module/crypto/aes.c

@ -0,0 +1,608 @@ @@ -0,0 +1,608 @@
/*
This is an implementation of the AES algorithm, specifically ECB, CTR and CBC mode.
Block size can be chosen in aes.h - available choices are AES128, AES192, AES256.
The implementation is verified against the test vectors in:
National Institute of Standards and Technology Special Publication 800-38A 2001 ED
ECB-AES128
----------
plain-text:
6bc1bee22e409f96e93d7e117393172a
ae2d8a571e03ac9c9eb76fac45af8e51
30c81c46a35ce411e5fbc1191a0a52ef
f69f2445df4f9b17ad2b417be66c3710
key:
2b7e151628aed2a6abf7158809cf4f3c
resulting cipher
3ad77bb40d7a3660a89ecaf32466ef97
f5d3d58503b9699de785895a96fdbaaf
43b1cd7f598ece23881b00e3ed030688
7b0c785e27e8ad3f8223207104725dd4
NOTE: String length must be evenly divisible by 16byte (str_len % 16 == 0)
You should pad the end of the string with zeros if this is not the case.
For AES192/256 the key size is proportionally larger.
*/
/*****************************************************************************/
/* Includes: */
/*****************************************************************************/
#include <string.h> // CBC mode, for memset
#include "aes.h"
/*****************************************************************************/
/* Defines: */
/*****************************************************************************/
// The number of columns comprising a state in AES. This is a constant in AES.
// Value=4
#define Nb 4UL
#if defined(AES256) && (AES256 == 1)
#define Nk256 8UL
#define Nr256 14UL
#endif
#if defined(AES192) && (AES192 == 1)
#define Nk192 6UL
#define Nr192 12UL
#endif
#if defined(AES128) && (AES128 == 1)
#define Nk128 4UL // The number of 32 bit words in a key.
#define Nr128 10UL // The number of rounds in AES Cipher.
#endif
// jcallan@github points out that declaring Multiply as a function reduces code
// size considerably with the Keil ARM compiler. See this link for more
// information: https://github.com/kokke/tiny-AES-C/pull/3
#ifndef MULTIPLY_AS_A_FUNCTION
#define MULTIPLY_AS_A_FUNCTION 0
#endif
/*****************************************************************************/
/* Private variables: */
/*****************************************************************************/
// state - array holding the intermediate results during decryption.
typedef uint8_t state_t[4][4];
// The lookup-tables are marked const so they can be placed in read-only storage
// instead of RAM The numbers below can be computed dynamically trading ROM for
// RAM - This can be useful in (embedded) bootloader applications, where ROM is
// often limited.
static const uint8_t sbox[256] = {
//0 1 2 3 4 5 6 7 8 9 A B C D E F
0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 };
static const uint8_t rsbox[256] = {
0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,
0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,
0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,
0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,
0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,
0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,
0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,
0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d };
// The round constant word array, Rcon[i], contains the values given by x to the
// power (i-1) being powers of x (x is denoted as {02}) in the field GF(2^8)
static const uint8_t Rcon[11] = {
0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36 };
/*
* Jordan Goulder points out in PR #12
* (https://github.com/kokke/tiny-AES-C/pull/12), that you can remove most of
* the elements in the Rcon array, because they are unused.
*
* From Wikipedia's article on the Rijndael key schedule @
* https://en.wikipedia.org/wiki/Rijndael_key_schedule#Rcon
*
* "Only the first some of these constants are actually used – up to rcon[10]
* for AES-128 (as 11 round keys are needed), up to rcon[8] for AES-192, up to
* rcon[7] for AES-256. rcon[0] is not used in AES algorithm."
*/
/*****************************************************************************/
/* Private functions: */
/*****************************************************************************/
static const uint8_t *GetRoundKey(const struct AES_ctx *ctx) {
switch (ctx->KeyLength) {
#if defined(AES128) && (AES128 == 1)
case 16: return ctx->RoundKey128;
#endif
#if defined(AES192) && (AES192 == 1)
case 24: return ctx->RoundKey192;
#endif
#if defined(AES256) && (AES256 == 1)
case 32: return ctx->RoundKey256;
#endif
}
return NULL;
}
/*
static uint8_t getSBoxValue(uint8_t num)
{
return sbox[num];
}
*/
#define getSBoxValue(num) (sbox[(num)])
/*
static uint8_t getSBoxInvert(uint8_t num)
{
return rsbox[num];
}
*/
#define getSBoxInvert(num) (rsbox[(num)])
// This function produces Nb(Nr+1) round keys. The round keys are used in each
// round to decrypt the states.
static void KeyExpansion(struct AES_ctx* ctx, const uint8_t* Key)
{
uint8_t* RoundKey = (uint8_t *)GetRoundKey(ctx);
unsigned i, j, k;
uint8_t tempa[4]; // Used for the column/row operations
// The first round key is the key itself.
for (i = 0; i < ctx->Nk; ++i)
{
RoundKey[(i * 4) + 0] = Key[(i * 4) + 0];
RoundKey[(i * 4) + 1] = Key[(i * 4) + 1];
RoundKey[(i * 4) + 2] = Key[(i * 4) + 2];
RoundKey[(i * 4) + 3] = Key[(i * 4) + 3];
}
// All other round keys are found from the previous round keys.
for (i = ctx->Nk; i < Nb * (ctx->Nr + 1); ++i)
{
{
k = (i - 1) * 4;
tempa[0]=RoundKey[k + 0];
tempa[1]=RoundKey[k + 1];
tempa[2]=RoundKey[k + 2];
tempa[3]=RoundKey[k + 3];
}
if (i % ctx->Nk == 0)
{
// This function shifts the 4 bytes in a word to the left once.
// [a0,a1,a2,a3] becomes [a1,a2,a3,a0]
// Function RotWord()
{
const uint8_t u8tmp = tempa[0];
tempa[0] = tempa[1];
tempa[1] = tempa[2];
tempa[2] = tempa[3];
tempa[3] = u8tmp;
}
// SubWord() is a function that takes a four-byte input word and applies
// the S-box to each of the four bytes to produce an output word.
// Function Subword()
{
tempa[0] = getSBoxValue(tempa[0]);
tempa[1] = getSBoxValue(tempa[1]);
tempa[2] = getSBoxValue(tempa[2]);
tempa[3] = getSBoxValue(tempa[3]);
}
tempa[0] = tempa[0] ^ Rcon[i/ctx->Nk];
}
#if defined(AES256) && (AES256 == 1)
if (ctx->KeyLength == 32) {
if (i % ctx->Nk == 4)
{
// Function Subword()
{
tempa[0] = getSBoxValue(tempa[0]);
tempa[1] = getSBoxValue(tempa[1]);
tempa[2] = getSBoxValue(tempa[2]);
tempa[3] = getSBoxValue(tempa[3]);
}
}
}
#endif
j = i * 4; k=(i - ctx->Nk) * 4;
RoundKey[j + 0] = RoundKey[k + 0] ^ tempa[0];
RoundKey[j + 1] = RoundKey[k + 1] ^ tempa[1];
RoundKey[j + 2] = RoundKey[k + 2] ^ tempa[2];
RoundKey[j + 3] = RoundKey[k + 3] ^ tempa[3];
}
}
void AES_init_ctx(struct AES_ctx* ctx, const uint8_t* key, uint32_t keylen)
{
ctx->KeyLength = keylen;
switch (ctx->KeyLength) {
#if defined(AES128) && (AES128 == 1)
case 16: ctx->Nr = Nr128; ctx->Nk = Nk128; break;
#endif
#if defined(AES192) && (AES192 == 1)
case 24: ctx->Nr = Nr192; ctx->Nk = Nk192; break;
#endif
#if defined(AES256) && (AES256 == 1)
case 32: ctx->Nr = Nr256; ctx->Nk = Nk256; break;
#endif
default: ctx->Nr = 0; ctx->Nk = 0; break;
}
KeyExpansion(ctx, key);
}
#if (defined(CBC) && (CBC == 1)) || (defined(CTR) && (CTR == 1))
void AES_init_ctx_iv(struct AES_ctx* ctx, const uint8_t* key, uint32_t keylen, const uint8_t* iv)
{
AES_init_ctx(ctx, key, keylen);
memcpy (ctx->Iv, iv, AES_BLOCKLEN);
}
void AES_ctx_set_iv(struct AES_ctx* ctx, const uint8_t* iv)
{
memcpy (ctx->Iv, iv, AES_BLOCKLEN);
}
#endif
// This function adds the round key to state. The round key is added to the
// state by an XOR function.
static void AddRoundKey(uint8_t round, state_t* state, const uint8_t* RoundKey)
{
uint8_t i,j;
for (i = 0; i < 4; ++i)
{
for (j = 0; j < 4; ++j)
{
(*state)[i][j] ^= RoundKey[(round * Nb * 4) + (i * Nb) + j];
}
}
}
// The SubBytes Function Substitutes the values in the state matrix with values
// in an S-box.
static void SubBytes(state_t* state)
{
uint8_t i, j;
for (i = 0; i < 4; ++i)
{
for (j = 0; j < 4; ++j)
{
(*state)[j][i] = getSBoxValue((*state)[j][i]);
}
}
}
// The ShiftRows() function shifts the rows in the state to the left. Each row
// is shifted with different offset. Offset = Row number. So the first row is
// not shifted.
static void ShiftRows(state_t* state)
{
uint8_t temp;
// Rotate first row 1 columns to left
temp = (*state)[0][1];
(*state)[0][1] = (*state)[1][1];
(*state)[1][1] = (*state)[2][1];
(*state)[2][1] = (*state)[3][1];
(*state)[3][1] = temp;
// Rotate second row 2 columns to left
temp = (*state)[0][2];
(*state)[0][2] = (*state)[2][2];
(*state)[2][2] = temp;
temp = (*state)[1][2];
(*state)[1][2] = (*state)[3][2];
(*state)[3][2] = temp;
// Rotate third row 3 columns to left
temp = (*state)[0][3];
(*state)[0][3] = (*state)[3][3];
(*state)[3][3] = (*state)[2][3];
(*state)[2][3] = (*state)[1][3];
(*state)[1][3] = temp;
}
static uint8_t xtime(uint8_t x)
{
return ((x<<1) ^ (((x>>7) & 1) * 0x1b));
}
// MixColumns function mixes the columns of the state matrix
static void MixColumns(state_t* state)
{
uint8_t i;
uint8_t Tmp, Tm, t;
for (i = 0; i < 4; ++i)
{
t = (*state)[i][0];
Tmp = (*state)[i][0] ^ (*state)[i][1] ^ (*state)[i][2] ^ (*state)[i][3] ;
Tm = (*state)[i][0] ^ (*state)[i][1] ; Tm = xtime(Tm); (*state)[i][0] ^= Tm ^ Tmp ;
Tm = (*state)[i][1] ^ (*state)[i][2] ; Tm = xtime(Tm); (*state)[i][1] ^= Tm ^ Tmp ;
Tm = (*state)[i][2] ^ (*state)[i][3] ; Tm = xtime(Tm); (*state)[i][2] ^= Tm ^ Tmp ;
Tm = (*state)[i][3] ^ t ; Tm = xtime(Tm); (*state)[i][3] ^= Tm ^ Tmp ;
}
}
// Multiply is used to multiply numbers in the field GF(2^8)
// Note: The last call to xtime() is unneeded, but often ends up generating a smaller binary
// The compiler seems to be able to vectorize the operation better this way.
// See https://github.com/kokke/tiny-AES-c/pull/34
#if MULTIPLY_AS_A_FUNCTION
static uint8_t Multiply(uint8_t x, uint8_t y)
{
return (((y & 1) * x) ^
((y>>1 & 1) * xtime(x)) ^
((y>>2 & 1) * xtime(xtime(x))) ^
((y>>3 & 1) * xtime(xtime(xtime(x)))) ^
((y>>4 & 1) * xtime(xtime(xtime(xtime(x)))))); /* this last call to xtime() can be omitted */
}
#else
#define Multiply(x, y) \
( ((y & 1) * x) ^ \
((y>>1 & 1) * xtime(x)) ^ \
((y>>2 & 1) * xtime(xtime(x))) ^ \
((y>>3 & 1) * xtime(xtime(xtime(x)))) ^ \
((y>>4 & 1) * xtime(xtime(xtime(xtime(x)))))) \
#endif
#if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1)
// MixColumns function mixes the columns of the state matrix. The method used to
// multiply may be difficult to understand for the inexperienced. Please use the
// references to gain more information.
static void InvMixColumns(state_t* state)
{
int i;
uint8_t a, b, c, d;
for (i = 0; i < 4; ++i)
{
a = (*state)[i][0];
b = (*state)[i][1];
c = (*state)[i][2];
d = (*state)[i][3];
(*state)[i][0] = Multiply(a, 0x0e) ^ Multiply(b, 0x0b) ^ Multiply(c, 0x0d) ^ Multiply(d, 0x09);
(*state)[i][1] = Multiply(a, 0x09) ^ Multiply(b, 0x0e) ^ Multiply(c, 0x0b) ^ Multiply(d, 0x0d);
(*state)[i][2] = Multiply(a, 0x0d) ^ Multiply(b, 0x09) ^ Multiply(c, 0x0e) ^ Multiply(d, 0x0b);
(*state)[i][3] = Multiply(a, 0x0b) ^ Multiply(b, 0x0d) ^ Multiply(c, 0x09) ^ Multiply(d, 0x0e);
}
}
// The SubBytes Function Substitutes the values in the state matrix with values
// in an S-box.
static void InvSubBytes(state_t* state)
{
uint8_t i, j;
for (i = 0; i < 4; ++i)
{
for (j = 0; j < 4; ++j)
{
(*state)[j][i] = getSBoxInvert((*state)[j][i]);
}
}
}
static void InvShiftRows(state_t* state)
{
uint8_t temp;
// Rotate first row 1 columns to right
temp = (*state)[3][1];
(*state)[3][1] = (*state)[2][1];
(*state)[2][1] = (*state)[1][1];
(*state)[1][1] = (*state)[0][1];
(*state)[0][1] = temp;
// Rotate second row 2 columns to right
temp = (*state)[0][2];
(*state)[0][2] = (*state)[2][2];
(*state)[2][2] = temp;
temp = (*state)[1][2];
(*state)[1][2] = (*state)[3][2];
(*state)[3][2] = temp;
// Rotate third row 3 columns to right
temp = (*state)[0][3];
(*state)[0][3] = (*state)[1][3];
(*state)[1][3] = (*state)[2][3];
(*state)[2][3] = (*state)[3][3];
(*state)[3][3] = temp;
}
#endif // #if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1)
// Cipher is the main function that encrypts the PlainText.
static void Cipher(state_t* state, const struct AES_ctx* ctx)
{
const uint8_t* RoundKey = GetRoundKey(ctx);
uint8_t round = 0;
// Add the First round key to the state before starting the rounds.
AddRoundKey(0, state, RoundKey);
// There will be Nr rounds. The first Nr-1 rounds are identical. These Nr
// rounds are executed in the loop below. Last one without MixColumns()
for (round = 1; ; ++round)
{
SubBytes(state);
ShiftRows(state);
if (round == ctx->Nr) {
break;
}
MixColumns(state);
AddRoundKey(round, state, RoundKey);
}
// Add round key to last round
AddRoundKey(ctx->Nr, state, RoundKey);
}
#if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1)
static void InvCipher(state_t* state, const struct AES_ctx* ctx)
{
const uint8_t* RoundKey = GetRoundKey(ctx);
uint8_t round = 0;
// Add the First round key to the state before starting the rounds.
AddRoundKey(ctx->Nr, state, RoundKey);
// There will be Nr rounds. The first Nr-1 rounds are identical. These Nr
// rounds are executed in the loop below. Last one without InvMixColumn()
for (round = (ctx->Nr - 1); ; --round)
{
InvShiftRows(state);
InvSubBytes(state);
AddRoundKey(round, state, RoundKey);
if (round == 0) {
break;
}
InvMixColumns(state);
}
}
#endif // #if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1)
/*****************************************************************************/
/* Public functions: */
/*****************************************************************************/
#if defined(ECB) && (ECB == 1)
void AES_ECB_encrypt(const struct AES_ctx* ctx, uint8_t* buf)
{
// The next function call encrypts the PlainText with the Key using AES
// algorithm.
Cipher((state_t*)buf, ctx);
}
void AES_ECB_decrypt(const struct AES_ctx* ctx, uint8_t* buf)
{
// The next function call decrypts the PlainText with the Key using AES
// algorithm.
InvCipher((state_t*)buf, ctx);
}
#endif // #if defined(ECB) && (ECB == 1)
#if defined(CBC) && (CBC == 1)
static void XorWithIv(uint8_t* buf, const uint8_t* Iv)
{
uint8_t i;
for (i = 0; i < AES_BLOCKLEN; ++i) // The block in AES is always 128bit no matter the key size
{
buf[i] ^= Iv[i];
}
}
void AES_CBC_encrypt_buffer(struct AES_ctx *ctx, uint8_t* buf, uint32_t length)
{
uintptr_t i;
uint8_t *Iv = ctx->Iv;
for (i = 0; i < length; i += AES_BLOCKLEN)
{
XorWithIv(buf, Iv);
Cipher((state_t*)buf, ctx);
Iv = buf;
buf += AES_BLOCKLEN;
}
/* store Iv in ctx for next call */
memcpy(ctx->Iv, Iv, AES_BLOCKLEN);
}
void AES_CBC_decrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, uint32_t length)
{
uintptr_t i;
uint8_t storeNextIv[AES_BLOCKLEN];
for (i = 0; i < length; i += AES_BLOCKLEN)
{
memcpy(storeNextIv, buf, AES_BLOCKLEN);
InvCipher((state_t*)buf, ctx);
XorWithIv(buf, ctx->Iv);
memcpy(ctx->Iv, storeNextIv, AES_BLOCKLEN);
buf += AES_BLOCKLEN;
}
}
#endif // #if defined(CBC) && (CBC == 1)
#if defined(CTR) && (CTR == 1)
/* Symmetrical operation: same function for encrypting as for decrypting. Note
any IV/nonce should never be reused with the same key */
void AES_CTR_xcrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, uint32_t length)
{
uint8_t buffer[AES_BLOCKLEN];
unsigned i;
int bi;
for (i = 0, bi = AES_BLOCKLEN; i < length; ++i, ++bi)
{
if (bi == AES_BLOCKLEN) /* we need to regen xor compliment in buffer */
{
memcpy(buffer, ctx->Iv, AES_BLOCKLEN);
Cipher((state_t*)buffer, ctx);
/* Increment Iv and handle overflow */
for (bi = (AES_BLOCKLEN - 1); bi >= 0; --bi)
{
/* inc will overflow */
if (ctx->Iv[bi] == 255)
{
ctx->Iv[bi] = 0;
continue;
}
ctx->Iv[bi] += 1;
break;
}
bi = 0;
}
buf[i] = (buf[i] ^ buffer[bi]);
}
}
#endif // #if defined(CTR) && (CTR == 1)

105
shared-module/crypto/aes.h

@ -0,0 +1,105 @@ @@ -0,0 +1,105 @@
#ifndef _AES_H_
#define _AES_H_
#include <stdint.h>
// #define the macros below to 1/0 to enable/disable the mode of operation.
//
// CBC enables AES encryption in CBC-mode of operation.
// CTR enables encryption in counter-mode.
// ECB enables the basic ECB 16-byte block algorithm. All can be enabled simultaneously.
// The #ifndef-guard allows it to be configured before #include'ing or at compile time.
#ifndef CBC
#define CBC 1
#endif
#ifndef ECB
#define ECB 1
#endif
#ifndef CTR
#define CTR 1
#endif
#define AES128 1
#define AES192 1
#define AES256 1
#define AES_BLOCKLEN 16 // Block length in bytes - AES is 128b block only
#if defined(AES256) && (AES256 == 1)
#define AES_KEYLEN256 32
#define AES_keyExpSize256 240
#endif
#if defined(AES192) && (AES192 == 1)
#define AES_KEYLEN192 24
#define AES_keyExpSize192 208
#endif
#if defined(AES128) && (AES128 == 1)
#define AES_KEYLEN128 16 // Key length in bytes
#define AES_keyExpSize128 176
#endif
struct AES_ctx
{
union {
#if defined(AES256) && (AES256 == 1)
uint8_t RoundKey256[AES_keyExpSize256];
#endif
#if defined(AES192) && (AES192 == 1)
uint8_t RoundKey192[AES_keyExpSize192];
#endif
#if defined(AES128) && (AES128 == 1)
uint8_t RoundKey128[AES_keyExpSize128];
#endif
};
#if (defined(CBC) && (CBC == 1)) || (defined(CTR) && (CTR == 1))
uint8_t Iv[AES_BLOCKLEN];
#endif
uint32_t KeyLength;
uint8_t Nr;
uint8_t Nk;
};
void AES_init_ctx(struct AES_ctx* ctx, const uint8_t* key, uint32_t keylen);
#if (defined(CBC) && (CBC == 1)) || (defined(CTR) && (CTR == 1))
void AES_init_ctx_iv(struct AES_ctx* ctx, const uint8_t* key, uint32_t keylen, const uint8_t* iv);
void AES_ctx_set_iv(struct AES_ctx* ctx, const uint8_t* iv);
#endif
#if defined(ECB) && (ECB == 1)
// buffer size is exactly AES_BLOCKLEN bytes;
// you need only AES_init_ctx as IV is not used in ECB
// NB: ECB is considered insecure for most uses
void AES_ECB_encrypt(const struct AES_ctx* ctx, uint8_t* buf);
void AES_ECB_decrypt(const struct AES_ctx* ctx, uint8_t* buf);
#endif // #if defined(ECB) && (ECB == !)
#if defined(CBC) && (CBC == 1)
// buffer size MUST be mutile of AES_BLOCKLEN;
// Suggest https://en.wikipedia.org/wiki/Padding_(cryptography)#PKCS7 for padding scheme
// NOTES: you need to set IV in ctx via AES_init_ctx_iv() or AES_ctx_set_iv()
// no IV should ever be reused with the same key
void AES_CBC_encrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, uint32_t length);
void AES_CBC_decrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, uint32_t length);
#endif // #if defined(CBC) && (CBC == 1)
#if defined(CTR) && (CTR == 1)
// Same function for encrypting as for decrypting.
// IV is incremented for every block, and used after encryption as XOR-compliment for output
// Suggesting https://en.wikipedia.org/wiki/Padding_(cryptography)#PKCS7 for padding scheme
// NOTES: you need to set IV in ctx with AES_init_ctx_iv() or AES_ctx_set_iv()
// no IV should ever be reused with the same key
void AES_CTR_xcrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, uint32_t length);
#endif // #if defined(CTR) && (CTR == 1)
#endif // _AES_H_
Loading…
Cancel
Save