Berry add AES CCM decrypting in a single call to avoid any object allocation (#19153)

This commit is contained in:
s-hadinger 2023-07-19 21:15:08 +02:00 committed by GitHub
parent 2a3690b866
commit 3da5124348
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 113 additions and 6 deletions

View File

@ -11,6 +11,7 @@ All notable changes to this project will be documented in this file.
- Berry add `energy.update_total()` to call `EnergyUpdateTotal()` from energy driver
- Berry add metrics for memory allocation/deallocation/reallocation
- Berry `tasmota.loglevel()` and `tasmota.rtc_utc()` for faster performance
- Berry add AES CCM decrypting in a single call to avoid any object allocation
### Breaking Changed

View File

@ -14,8 +14,9 @@ extern int m_crypto_random(bvm *vm);
extern int m_rsa_rsassa_pkcs1_v1_5(bvm *vm);
extern int m_aes_ccm_init(bvm *vm);
extern int m_aes_ccm_encryt(bvm *vm);
extern int m_aes_ccm_decryt(bvm *vm);
extern int m_aes_ccm_encrypt(bvm *vm);
extern int m_aes_ccm_decrypt(bvm *vm);
extern int m_aes_ccm_decrypt1(bvm *vm);
extern int m_aes_ccm_tag(bvm *vm);
extern int m_aes_gcm_init(bvm *vm);
@ -134,9 +135,11 @@ class be_class_aes_ccm (scope: global, name: AES_CCM) {
.p2, var
init, func(m_aes_ccm_init)
encrypt, func(m_aes_ccm_encryt)
decrypt, func(m_aes_ccm_decryt)
encrypt, func(m_aes_ccm_encrypt)
decrypt, func(m_aes_ccm_decrypt)
tag, func(m_aes_ccm_tag)
decrypt1, static_func(m_aes_ccm_decrypt1)
}
class be_class_aes_gcm (scope: global, name: AES_GCM) {

View File

@ -296,8 +296,8 @@ extern "C" {
// Finish injection of authentication data
int32_t m_aes_ccm_encrypt_or_decryt(bvm *vm, int encrypt);
int32_t m_aes_ccm_encryt(bvm *vm) { return m_aes_ccm_encrypt_or_decryt(vm, 1); }
int32_t m_aes_ccm_decryt(bvm *vm) { return m_aes_ccm_encrypt_or_decryt(vm, 0); }
int32_t m_aes_ccm_encrypt(bvm *vm) { return m_aes_ccm_encrypt_or_decryt(vm, 1); }
int32_t m_aes_ccm_decrypt(bvm *vm) { return m_aes_ccm_encrypt_or_decryt(vm, 0); }
int32_t m_aes_ccm_encrypt_or_decryt(bvm *vm, int encrypt) {
int32_t argc = be_top(vm); // Get the number of arguments
if (argc >= 2 && be_isbytes(vm, 2)) {
@ -346,6 +346,109 @@ extern "C" {
} while (0);
be_raise(vm, kTypeError, nullptr);
}
// `AES_CCM.decrypt1(
// secret_key:bytes(16 or 32),
// iv:bytes(), iv_start:int, iv_len:int (7..13),
// aad:bytes(), aad_start:int, aad_len:int,
// data:bytes(), data_start:int, data_len:int,
// tag:bytes(), tag_start:int, tag_len:int (4..16))
// -> bool (true if tag matches)
//
// all-in-one decrypt function
// decryption in place
//
int32_t m_aes_ccm_decrypt1(struct bvm *vm);
int32_t m_aes_ccm_decrypt1(struct bvm *vm) {
int32_t argc = be_top(vm); // Get the number of arguments
if (argc >= 13 && be_isbytes(vm, 1) // secret_key
&& be_isbytes(vm, 2) && be_isint(vm, 3) && be_isint(vm, 4) // iv, iv_start, iv_len
&& be_isbytes(vm, 5) && be_isint(vm, 6) && be_isint(vm, 7) // aad, aad_start, aad_len
&& be_isbytes(vm, 8) && be_isint(vm, 9) && be_isint(vm, 10) // data_start, data_len
&& be_isbytes(vm, 11) && be_isint(vm, 12) && be_isint(vm, 13)) { // tag, tag_start, tag_len
size_t key_len = 0;
const void * key = be_tobytes(vm, 1, &key_len);
if (key_len != 32 && key_len != 16) {
be_raise(vm, "value_error", "Key size must be 16 or 32 bytes");
}
size_t nonce_len = 0;
const uint8_t * nonce = (const uint8_t *) be_tobytes(vm, 2, &nonce_len);
int32_t n_start = be_toint(vm, 3);
int32_t n_len = be_toint(vm, 4);
if (n_start < 0 || n_len < 0 || n_start > nonce_len || n_start+n_len > nonce_len) {
be_raise(vm, "range_error", "out of range start/end");
}
nonce += n_start;
nonce_len = n_len;
if (nonce_len < 7 || nonce_len > 13) {
be_raise(vm, "value_error", "Nonce size must be 7..13");
}
size_t aad_len = 0;
const uint8_t * aad = (const uint8_t *) be_tobytes(vm, 5, &aad_len);
int32_t a_start = be_toint(vm, 6);
int32_t a_len = be_toint(vm, 7);
if (a_start < 0 || a_len < 0 || a_start > aad_len || a_start+a_len > aad_len) {
be_raise(vm, "range_error", "out of range start/end");
}
aad += a_start;
aad_len = a_len;
size_t data_len = 0;
uint8_t * data = (uint8_t *) be_tobytes(vm, 8, &data_len);
int32_t d_start = be_toint(vm, 9);
int32_t d_len = be_toint(vm, 10);
if (d_start < 0 || d_len < 0 || d_start > data_len || d_start+d_len > data_len) {
be_raise(vm, "range_error", "out of range start/end");
}
data += d_start;
data_len = d_len;
size_t tag_len = 0;
uint8_t * tag = (uint8_t *) be_tobytes(vm, 11, &tag_len);
int32_t t_start = be_toint(vm, 12);
int32_t t_len = be_toint(vm, 13);
if (t_start < 0 || t_len < 0 || t_start > tag_len || t_start+t_len > tag_len) {
be_raise(vm, "range_error", "out of range start/end");
}
tag += t_start;
tag_len = t_len;
if (tag_len < 4 || tag_len > 16) {
be_raise(vm, "value_error", "Tag size must be 4..16");
}
// Initialize an AES CCM structure with the secret key
br_aes_small_ctrcbc_keys key_ctx;
br_ccm_context ccm_ctx;
br_aes_small_ctrcbc_init(&key_ctx, key, key_len);
br_ccm_init(&ccm_ctx, &key_ctx.vtable);
int ret = br_ccm_reset(&ccm_ctx, nonce, nonce_len, aad_len, data_len, tag_len);
if (ret == 0) { be_raise(vm, "value_error", "br_ccm_reset failed"); }
if (aad_len > 0) {
br_ccm_aad_inject(&ccm_ctx, aad, aad_len);
}
br_ccm_flip(&ccm_ctx);
br_ccm_run(&ccm_ctx, 0 /*decrypt*/, data, data_len); // decrypt in place
// check tag
// create a bytes buffer of 16 bytes
uint8_t tag_computed[16] = {};
br_ccm_get_tag(&ccm_ctx, tag_computed);
if (memcmp(tag_computed, tag, tag_len) == 0) {
be_pushbool(vm, btrue);
} else {
be_pushbool(vm, bfalse);
}
// success
be_return(vm);
}
be_raise(vm, kTypeError, nullptr);
}
}
/*********************************************************************************************\