diff --git a/CHANGELOG.md b/CHANGELOG.md index 684ede3d7..1e013ce67 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ All notable changes to this project will be documented in this file. ### Added - Support for PCA9632 4-channel 8-bit PWM driver as light driver by Pascal Heinrich (#17557) - Berry `bytes()` now evaluates to `false` if empty +- Berry ``crypto.AES_CCM`` (required by Matter protocol) ### Breaking Changed diff --git a/lib/libesp32/berry_tasmota/src/be_crypto_lib.c b/lib/libesp32/berry_tasmota/src/be_crypto_lib.c index 43122303e..59518e120 100644 --- a/lib/libesp32/berry_tasmota/src/be_crypto_lib.c +++ b/lib/libesp32/berry_tasmota/src/be_crypto_lib.c @@ -11,6 +11,11 @@ extern int be_class_crypto_member(bvm *vm); extern int m_crypto_random(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_tag(bvm *vm); + extern int m_aes_gcm_init(bvm *vm); extern int m_aes_gcm_encryt(bvm *vm); extern int m_aes_gcm_decryt(bvm *vm); @@ -47,6 +52,7 @@ extern const bclass be_class_md5; #include "solidify/solidified_crypto_pbkdf2_hmac_sha256.h" #include "solidify/solidified_crypto_spake2p_matter.h" +#include "be_fixed_be_class_aes_ccm.h" #include "be_fixed_be_class_aes_gcm.h" #include "be_fixed_be_class_aes_ctr.h" #include "be_fixed_be_class_ec_p256.h" @@ -65,11 +71,17 @@ extern const bclass be_class_md5; #define USE_BERRY_CRYPTO_HMAC_SHA256 #undef USE_BERRY_CRYPTO_HKDF_SHA256 #define USE_BERRY_CRYPTO_HKDF_SHA256 + #undef USE_BERRY_CRYPTO_AES_CCM + #define USE_BERRY_CRYPTO_AES_CCM #endif const be_const_member_t be_crypto_members[] = { // name with prefix '/' indicates a Berry class // entries need to be sorted (ignoring the prefix char) +#ifdef USE_BERRY_CRYPTO_AES_CCM + { "/AES_CCM", (intptr_t) &be_class_aes_ccm }, +#endif // USE_BERRY_CRYPTO_AES_CTR + #ifdef USE_BERRY_CRYPTO_AES_CTR { "/AES_CTR", (intptr_t) &be_class_aes_ctr }, #endif // USE_BERRY_CRYPTO_AES_CTR @@ -114,6 +126,16 @@ const size_t be_crypto_members_size = sizeof(be_crypto_members)/sizeof(be_crypto /* @const_object_info_begin +class be_class_aes_ccm (scope: global, name: AES_CCM) { + .p1, var + .p2, var + + init, func(m_aes_ccm_init) + encrypt, func(m_aes_ccm_encryt) + decrypt, func(m_aes_ccm_decryt) + tag, func(m_aes_ccm_tag) +} + class be_class_aes_gcm (scope: global, name: AES_GCM) { .p1, var .p2, var diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h index 6c6c6f60b..2c0e749f2 100644 --- a/tasmota/my_user_config.h +++ b/tasmota/my_user_config.h @@ -1120,7 +1120,8 @@ // #define USE_BERRY_ULP // Enable ULP (Ultra Low Power) support (+4.9k) // Berry crypto extensions below: #define USE_BERRY_CRYPTO_AES_GCM // enable AES GCM 256 bits - // #define USE_BERRY_CRYPTO_AES_CTR // enable AEC CTR 256 bits + // #define USE_BERRY_CRYPTO_AES_CCM // enable AES CCM 128 bits + // #define USE_BERRY_CRYPTO_AES_CTR // enable AES CTR 256 bits // #define USE_BERRY_CRYPTO_EC_P256 // enable EC P256r1 // #define USE_BERRY_CRYPTO_EC_C25519 // enable Elliptic Curve C C25519 #define USE_BERRY_CRYPTO_SHA256 // enable SHA256 hash function diff --git a/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_crypto.ino b/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_crypto.ino index 6eba535ab..40261e22b 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_crypto.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_crypto.ino @@ -180,6 +180,122 @@ extern "C" { } } +/*********************************************************************************************\ + * AES_CCM class + * +\*********************************************************************************************/ +extern "C" { + + // `AES_CCM.init(secret_key:bytes(16 or 32), iv:bytes(7..13), aad:bytes(), data_len:int, tag_len:int) -> instance` + // + int32_t m_aes_ccm_init(struct bvm *vm); + int32_t m_aes_ccm_init(struct bvm *vm) { + int32_t argc = be_top(vm); // Get the number of arguments + if (argc >= 6 && be_isbytes(vm, 2) && be_isbytes(vm, 3) && be_isbytes(vm, 4) && be_isint(vm, 5) && be_isint(vm, 6)) { + do { + size_t key_len = 0; + const void * key = be_tobytes(vm, 2, &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 void * nonce = be_tobytes(vm, 3, &nonce_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 void * aad = be_tobytes(vm, 4, &aad_len); + + int32_t data_len = be_toint(vm, 5); + + int32_t tag_len = be_toint(vm, 6); + 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_ccm_context * ccm_ctx = (br_ccm_context *) be_os_malloc(sizeof(br_ccm_context)); + if (!ccm_ctx) { be_throw(vm, BE_MALLOC_FAIL); } + + be_newcomobj(vm, ccm_ctx, &be_commonobj_destroy_generic); + be_setmember(vm, 1, ".p1"); + + br_aes_small_ctrcbc_keys * key_ctx = (br_aes_small_ctrcbc_keys *) be_os_malloc(sizeof(br_aes_small_ctrcbc_keys)); + if (!key_ctx) { be_throw(vm, BE_MALLOC_FAIL); } + br_aes_small_ctrcbc_init(key_ctx, key, key_len); + be_newcomobj(vm, key_ctx, &be_commonobj_destroy_generic); + be_setmember(vm, 1, ".p2"); + + 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"); } + + br_ccm_aad_inject(ccm_ctx, aad, aad_len); + br_ccm_flip(ccm_ctx); + + be_return_nil(vm); + // success + } while (0); + } + be_raise(vm, kTypeError, nullptr); + } + + // 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_or_decryt(bvm *vm, int encrypt) { + int32_t argc = be_top(vm); // Get the number of arguments + if (argc >= 2 && be_isbytes(vm, 2)) { + do { + // get CCM context + be_getmember(vm, 1, ".p1"); + br_ccm_context * ccm_ctx = (br_ccm_context *) be_tocomptr(vm, -1); + be_pop(vm, 1); + + // copy the input buffer + be_getmember(vm, 2, "copy"); // stack: bytes.copy() + be_pushvalue(vm, 2); // stack: bytes.copy(), bytes instance + be_call(vm, 1); // call copy with self parameter + be_pop(vm, 1); // stack: clone of input bytes + + size_t length = 0; + // we are changing bytes in place + void * bytes = (void*) be_tobytes(vm, -1, &length); + if (!bytes) break; + + br_ccm_run(ccm_ctx, encrypt, bytes, length); + + be_return(vm); + // success + } while (0); + } + be_raise(vm, kTypeError, nullptr); + } + + int32_t m_aes_ccm_tag(bvm *vm) { + do { + be_getglobal(vm, "bytes"); /* get the bytes class */ /* TODO eventually replace with be_getbuiltin */ + + // get CCM context + be_getmember(vm, 1, ".p1"); + br_ccm_context * ccm_ctx = (br_ccm_context *) be_tocomptr(vm, -1); + be_pop(vm, 1); + + // create a bytes buffer of 16 bytes + uint8_t tag[16] = {}; + br_ccm_get_tag(ccm_ctx, tag); + be_pushbytes(vm, tag, sizeof(tag)); + + be_return(vm); + // success + } while (0); + be_raise(vm, kTypeError, nullptr); + } +} + /*********************************************************************************************\ * AES_CTR class *