diff --git a/CHANGELOG.md b/CHANGELOG.md index 09444e224..21807b766 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ All notable changes to this project will be documented in this file. ## [12.3.1.2] ### Added +- Berry crypto add ``EC_P256`` and ``PBKDF2_HMAC_SHA256`` algorithms required by Matter protocol ### Breaking Changed diff --git a/lib/libesp32/berry_mapping/src/be_const_members.c b/lib/libesp32/berry_mapping/src/be_const_members.c index 55baa634f..753f30940 100644 --- a/lib/libesp32/berry_mapping/src/be_const_members.c +++ b/lib/libesp32/berry_mapping/src/be_const_members.c @@ -38,6 +38,7 @@ * - `@func` Berry native function * - `*my_func` native function - the function is called and return value passed back. * This allows to create dynamic virtual members that are the result of a call. + * - `/my_class` a Berry class * * The array must be lexically sorted, but the sort function must ignore the prefix `$`, `&`, `*` \*********************************************************************************************/ @@ -63,6 +64,9 @@ static bbool be_const_member_dual(bvm *vm, const be_const_member_t * definitions case '&': // pointer be_pushcomptr(vm, (void*) definitions[idx].value); break; + case '/': + be_pushntvclass(vm, (const struct bclass*) definitions[idx].value); + break; case '*': // call to a native function { bntvfunc f = (bntvfunc) definitions[idx].value; diff --git a/lib/libesp32/berry_tasmota/src/be_crypto_lib.c b/lib/libesp32/berry_tasmota/src/be_crypto_lib.c index 083a846c9..d1e25392e 100644 --- a/lib/libesp32/berry_tasmota/src/be_crypto_lib.c +++ b/lib/libesp32/berry_tasmota/src/be_crypto_lib.c @@ -6,6 +6,9 @@ * Allows to respond to HTTP request *******************************************************************/ #include "be_constobj.h" +#include "be_mapping.h" + +extern int be_class_crypto_member(bvm *vm); extern int m_aes_gcm_init(bvm *vm); extern int m_aes_gcm_encryt(bvm *vm); @@ -16,6 +19,13 @@ extern int m_aes_ctr_init(bvm *vm); extern int m_aes_ctr_run(bvm *vm); extern int m_aes_ctr_tag(bvm *vm); +extern int m_ec_p256_pubkey(bvm *vm); +extern int m_ec_p256_sharedkey(bvm *vm); +extern int m_ec_p256_mod(bvm *vm); +extern int m_ec_p256_neg(bvm *vm); +extern int m_ec_p256_muladd(bvm *vm); +extern int m_ec_p256_mul(bvm *vm); + extern int m_ec_c25519_pubkey(bvm *vm); extern int m_ec_c25519_sharedkey(bvm *vm); @@ -27,15 +37,56 @@ extern int m_hmac_sha256_init(bvm *vm); extern int m_hmac_sha256_update(bvm *vm); extern int m_hmac_sha256_out(bvm *vm); +extern int m_pbkdf2_hmac_sha256_f(bvm *vm); + extern const bclass be_class_md5; +#include "solidify/solidified_crypto_pbkdf2_hmac_sha256.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" #include "be_fixed_be_class_ec_c25519.h" #include "be_fixed_be_class_sha256.h" #include "be_fixed_be_class_hmac_sha256.h" +#include "be_fixed_be_class_pbkdf2_hmac_sha256.h" #include "be_fixed_crypto.h" +const be_const_member_t be_crypto_members[] = { + // name with prefix '/' indicates a Berry class +#ifdef USE_BERRY_CRYPTO_AES_CTR + { "/AES_CTR", (intptr_t) &be_class_aes_ctr }, +#endif // USE_BERRY_CRYPTO_AES_CTR + +#ifdef USE_BERRY_CRYPTO_AES_GCM + { "/AES_GCM", (intptr_t) &be_class_aes_gcm }, +#endif // USE_BERRY_CRYPTO_AES_GCM + +#ifdef USE_BERRY_CRYPTO_EC_C25519 + { "/EC_C25519", (intptr_t) &be_class_ec_c25519 }, +#endif // USE_BERRY_CRYPTO_EC_C25519 + +#ifdef USE_BERRY_CRYPTO_EC_P256 + { "/EC_P256", (intptr_t) &be_class_ec_p256 }, +#endif // USE_BERRY_CRYPTO_EC_P256 + +#ifdef USE_BERRY_CRYPTO_HMAC_SHA256 + { "/HMAC_SHA256", (intptr_t) &be_class_hmac_sha256 }, +#endif // USE_BERRY_CRYPTO_HMAC_SHA256 + + { "/MD5", (intptr_t) &be_class_md5 }, + +#ifdef USE_BERRY_CRYPTO_PBKDF2_HMAC_SHA256 + { "/PBKDF2_HMAC_SHA256", (intptr_t) &be_class_pbkdf2_hmac_sha256 }, +#endif // USE_BERRY_CRYPTO_PBKDF2_HMAC_SHA256 + +#ifdef USE_BERRY_CRYPTO_SHA256 + { "/SHA256", (intptr_t) &be_class_sha256 }, +#endif // USE_BERRY_CRYPTO_SHA256 +}; +const size_t be_crypto_members_size = sizeof(be_crypto_members)/sizeof(be_crypto_members[0]); + + /* @const_object_info_begin class be_class_aes_gcm (scope: global, name: AES_GCM) { @@ -56,6 +107,15 @@ class be_class_aes_ctr (scope: global, name: AES_CTR) { decrypt, func(m_aes_ctr_run) } +class be_class_ec_p256 (scope: global, name: EC_P256) { + public_key, static_func(m_ec_p256_pubkey) + shared_key, static_func(m_ec_p256_sharedkey) + mod, static_func(m_ec_p256_mod) + neg, static_func(m_ec_p256_neg) + muladd, static_func(m_ec_p256_muladd) + mul, static_func(m_ec_p256_mul) +} + class be_class_ec_c25519 (scope: global, name: EC_C25519) { public_key, func(m_ec_c25519_pubkey) shared_key, func(m_ec_c25519_sharedkey) @@ -77,13 +137,14 @@ class be_class_hmac_sha256 (scope: global, name: HMAC_SHA256) { out, func(m_hmac_sha256_out) } +class be_class_pbkdf2_hmac_sha256 (scope: global, name: PBKDF2_HMAC_SHA256) { + _f, static_func(m_pbkdf2_hmac_sha256_f) + //_f, static_closure(_f_closure) // this is the slow Berry reference version + derive, closure(PBKDF2_HMAC_SHA256_closure) +} + module crypto (scope: global) { - AES_GCM, class(be_class_aes_gcm) - AES_CTR, class(be_class_aes_ctr) - EC_C25519, class(be_class_ec_c25519) - SHA256, class(be_class_sha256) - HMAC_SHA256, class(be_class_hmac_sha256) - MD5, class(be_class_md5) + member, func(be_class_crypto_member) } @const_object_info_end */ diff --git a/lib/libesp32/berry_tasmota/src/embedded/crypto_pbkdf2_hmac_sha256.be b/lib/libesp32/berry_tasmota/src/embedded/crypto_pbkdf2_hmac_sha256.be new file mode 100644 index 000000000..9c28b52c9 --- /dev/null +++ b/lib/libesp32/berry_tasmota/src/embedded/crypto_pbkdf2_hmac_sha256.be @@ -0,0 +1,85 @@ +# crypto.PBKDF2_HMAC_SHA256 +# +# This crypto algorigthm is requires for device pre-provisioning +# +# The code below is slow and should only be used occasionally +# to pre-compute Matter Verifier + + +# unit step of PBKDF2 +# res is bytes(32) -- unchecked +#@ solidify:_f,weak +def _f(password, salt_i, c, res) + # xor in place for bytes a with b + def xor(a, b) + assert(a.size() == b.size()) + var len = a.size() + var i = 0 + while i < len + a[i] = a[i] ^ b[i] + i += 1 + end + end + + import crypto + # iteration 1 + var h = crypto.HMAC_SHA256(password) + h.update(salt_i) + var u = h.out() + res.setbytes(0, u) + + # iterate + var n = 2 + while n <= c + h = crypto.HMAC_SHA256(password) + h.update(u) + u = h.out() + xor(res, u) + n += 1 + tasmota.yield() + end +end + +#@ solidify:PBKDF2_HMAC_SHA256,weak +def PBKDF2_HMAC_SHA256(self, p, s, c, dklen) + if type(p) == 'string' p = bytes().fromstring(p) end + if type(s) == 'string' s = bytes().fromstring(s) end + var r = bytes() + var i = 1 + while r.size() < dklen + # compute salt_i as the ready to use salt+i + var salt_i = s.copy() + salt_i.add(i, -4) # 4 bytes Big Endian + var ri = bytes().resize(32) + self._f(p, salt_i, c, ri) + r += ri + i += 1 + end + return r[0..dklen-1] +end + +#- TEST VECTORS + +# https://github.com/brycx/Test-Vector-Generation/blob/master/PBKDF2/pbkdf2-hmac-sha2-test-vectors.md +import crypto +kd = crypto.PBKDF2_HMAC_SHA256() +print(kd.derive("password", "salt", 1, 20)) +# bytes('120FB6CFFCF8B32C43E7225256C4F837A86548C9') +# 120fb6cffcf8b32c43e7225256c4f837a86548c9 + +print(kd.derive("password", "salt", 2, 20)) +# bytes('AE4D0C95AF6B46D32D0ADFF928F06DD02A303F8E') +# ae4d0c95af6b46d32d0adff928f06dd02a303f8e + +print(kd.derive("password", "salt", 3, 20)) +# bytes('AD35240AC683FEBFAF3CD49D845473FBBBAA2437') +# ad35240ac683febfaf3cd49d845473fbbbaa2437 + +var now=tasmota.millis() +print(kd.derive("password", "salt", 4096, 20)) +print("Done in",tasmota.millis()-now,"ms") +# bytes('C5E478D59288C841AA530DB6845C4C8D962893A0') +# c5e478d59288c841aa530db6845c4c8d962893a0 +# Done in 205 ms + +-# diff --git a/lib/libesp32/berry_tasmota/src/solidify/solidified_crypto_pbkdf2_hmac_sha256.h b/lib/libesp32/berry_tasmota/src/solidify/solidified_crypto_pbkdf2_hmac_sha256.h new file mode 100644 index 000000000..365a97a07 --- /dev/null +++ b/lib/libesp32/berry_tasmota/src/solidify/solidified_crypto_pbkdf2_hmac_sha256.h @@ -0,0 +1,203 @@ +/* Solidification of crypto_pbkdf2_hmac_sha256.h */ +/********************************************************************\ +* Generated code, don't edit * +\********************************************************************/ +#include "be_constobj.h" + +/******************************************************************** +** Solidified function: _f +********************************************************************/ +be_local_closure(_f, /* name */ + be_nested_proto( + 12, /* nstack */ + 4, /* argc */ + 0, /* varg */ + 0, /* has upvals */ + NULL, /* no upvals */ + 1, /* has sup protos */ + ( &(const struct bproto*[ 1]) { + be_nested_proto( + 6, /* nstack */ + 2, /* argc */ + 0, /* varg */ + 0, /* has upvals */ + NULL, /* no upvals */ + 0, /* has sup protos */ + NULL, /* no sub protos */ + 1, /* has constants */ + ( &(const bvalue[ 3]) { /* constants */ + /* K0 */ be_nested_str_weak(size), + /* K1 */ be_const_int(0), + /* K2 */ be_const_int(1), + }), + be_str_weak(xor), + &be_const_str_solidified, + ( &(const binstruction[19]) { /* code */ + 0x60080000, // 0000 GETGBL R2 G0 + 0x8C0C0100, // 0001 GETMET R3 R0 K0 + 0x7C0C0200, // 0002 CALL R3 1 + 0x8C100300, // 0003 GETMET R4 R1 K0 + 0x7C100200, // 0004 CALL R4 1 + 0x1C0C0604, // 0005 EQ R3 R3 R4 + 0x7C080200, // 0006 CALL R2 1 + 0x8C080100, // 0007 GETMET R2 R0 K0 + 0x7C080200, // 0008 CALL R2 1 + 0x580C0001, // 0009 LDCONST R3 K1 + 0x14100602, // 000A LT R4 R3 R2 + 0x78120005, // 000B JMPF R4 #0012 + 0x94100003, // 000C GETIDX R4 R0 R3 + 0x94140203, // 000D GETIDX R5 R1 R3 + 0x34100805, // 000E XOR R4 R4 R5 + 0x98000604, // 000F SETIDX R0 R3 R4 + 0x000C0702, // 0010 ADD R3 R3 K2 + 0x7001FFF7, // 0011 JMP #000A + 0x80000000, // 0012 RET 0 + }) + ), + }), + 1, /* has constants */ + ( &(const bvalue[10]) { /* constants */ + /* K0 */ be_nested_str_weak(crypto), + /* K1 */ be_nested_str_weak(HMAC_SHA256), + /* K2 */ be_nested_str_weak(update), + /* K3 */ be_nested_str_weak(out), + /* K4 */ be_nested_str_weak(setbytes), + /* K5 */ be_const_int(0), + /* K6 */ be_const_int(2), + /* K7 */ be_const_int(1), + /* K8 */ be_nested_str_weak(tasmota), + /* K9 */ be_nested_str_weak(yield), + }), + be_str_weak(_f), + &be_const_str_solidified, + ( &(const binstruction[37]) { /* code */ + 0x84100000, // 0000 CLOSURE R4 P0 + 0xA4160000, // 0001 IMPORT R5 K0 + 0x8C180B01, // 0002 GETMET R6 R5 K1 + 0x5C200000, // 0003 MOVE R8 R0 + 0x7C180400, // 0004 CALL R6 2 + 0x8C1C0D02, // 0005 GETMET R7 R6 K2 + 0x5C240200, // 0006 MOVE R9 R1 + 0x7C1C0400, // 0007 CALL R7 2 + 0x8C1C0D03, // 0008 GETMET R7 R6 K3 + 0x7C1C0200, // 0009 CALL R7 1 + 0x8C200704, // 000A GETMET R8 R3 K4 + 0x58280005, // 000B LDCONST R10 K5 + 0x5C2C0E00, // 000C MOVE R11 R7 + 0x7C200600, // 000D CALL R8 3 + 0x58200006, // 000E LDCONST R8 K6 + 0x18241002, // 000F LE R9 R8 R2 + 0x78260012, // 0010 JMPF R9 #0024 + 0x8C240B01, // 0011 GETMET R9 R5 K1 + 0x5C2C0000, // 0012 MOVE R11 R0 + 0x7C240400, // 0013 CALL R9 2 + 0x5C181200, // 0014 MOVE R6 R9 + 0x8C240D02, // 0015 GETMET R9 R6 K2 + 0x5C2C0E00, // 0016 MOVE R11 R7 + 0x7C240400, // 0017 CALL R9 2 + 0x8C240D03, // 0018 GETMET R9 R6 K3 + 0x7C240200, // 0019 CALL R9 1 + 0x5C1C1200, // 001A MOVE R7 R9 + 0x5C240800, // 001B MOVE R9 R4 + 0x5C280600, // 001C MOVE R10 R3 + 0x5C2C0E00, // 001D MOVE R11 R7 + 0x7C240400, // 001E CALL R9 2 + 0x00201107, // 001F ADD R8 R8 K7 + 0xB8261000, // 0020 GETNGBL R9 K8 + 0x8C241309, // 0021 GETMET R9 R9 K9 + 0x7C240200, // 0022 CALL R9 1 + 0x7001FFEA, // 0023 JMP #000F + 0x80000000, // 0024 RET 0 + }) + ) +); +/*******************************************************************/ + + +/******************************************************************** +** Solidified function: PBKDF2_HMAC_SHA256 +********************************************************************/ +be_local_closure(PBKDF2_HMAC_SHA256, /* name */ + be_nested_proto( + 15, /* nstack */ + 5, /* argc */ + 0, /* varg */ + 0, /* has upvals */ + NULL, /* no upvals */ + 0, /* has sup protos */ + NULL, /* no sub protos */ + 1, /* has constants */ + ( &(const bvalue[ 9]) { /* constants */ + /* K0 */ be_nested_str_weak(string), + /* K1 */ be_nested_str_weak(fromstring), + /* K2 */ be_const_int(1), + /* K3 */ be_nested_str_weak(size), + /* K4 */ be_nested_str_weak(copy), + /* K5 */ be_nested_str_weak(add), + /* K6 */ be_nested_str_weak(resize), + /* K7 */ be_nested_str_weak(_f), + /* K8 */ be_const_int(0), + }), + be_str_weak(PBKDF2_HMAC_SHA256), + &be_const_str_solidified, + ( &(const binstruction[53]) { /* code */ + 0x60140004, // 0000 GETGBL R5 G4 + 0x5C180200, // 0001 MOVE R6 R1 + 0x7C140200, // 0002 CALL R5 1 + 0x1C140B00, // 0003 EQ R5 R5 K0 + 0x78160005, // 0004 JMPF R5 #000B + 0x60140015, // 0005 GETGBL R5 G21 + 0x7C140000, // 0006 CALL R5 0 + 0x8C140B01, // 0007 GETMET R5 R5 K1 + 0x5C1C0200, // 0008 MOVE R7 R1 + 0x7C140400, // 0009 CALL R5 2 + 0x5C040A00, // 000A MOVE R1 R5 + 0x60140004, // 000B GETGBL R5 G4 + 0x5C180400, // 000C MOVE R6 R2 + 0x7C140200, // 000D CALL R5 1 + 0x1C140B00, // 000E EQ R5 R5 K0 + 0x78160005, // 000F JMPF R5 #0016 + 0x60140015, // 0010 GETGBL R5 G21 + 0x7C140000, // 0011 CALL R5 0 + 0x8C140B01, // 0012 GETMET R5 R5 K1 + 0x5C1C0400, // 0013 MOVE R7 R2 + 0x7C140400, // 0014 CALL R5 2 + 0x5C080A00, // 0015 MOVE R2 R5 + 0x60140015, // 0016 GETGBL R5 G21 + 0x7C140000, // 0017 CALL R5 0 + 0x58180002, // 0018 LDCONST R6 K2 + 0x8C1C0B03, // 0019 GETMET R7 R5 K3 + 0x7C1C0200, // 001A CALL R7 1 + 0x141C0E04, // 001B LT R7 R7 R4 + 0x781E0013, // 001C JMPF R7 #0031 + 0x8C1C0504, // 001D GETMET R7 R2 K4 + 0x7C1C0200, // 001E CALL R7 1 + 0x8C200F05, // 001F GETMET R8 R7 K5 + 0x5C280C00, // 0020 MOVE R10 R6 + 0x542DFFFB, // 0021 LDINT R11 -4 + 0x7C200600, // 0022 CALL R8 3 + 0x60200015, // 0023 GETGBL R8 G21 + 0x7C200000, // 0024 CALL R8 0 + 0x8C201106, // 0025 GETMET R8 R8 K6 + 0x542A001F, // 0026 LDINT R10 32 + 0x7C200400, // 0027 CALL R8 2 + 0x8C240107, // 0028 GETMET R9 R0 K7 + 0x5C2C0200, // 0029 MOVE R11 R1 + 0x5C300E00, // 002A MOVE R12 R7 + 0x5C340600, // 002B MOVE R13 R3 + 0x5C381000, // 002C MOVE R14 R8 + 0x7C240A00, // 002D CALL R9 5 + 0x00140A08, // 002E ADD R5 R5 R8 + 0x00180D02, // 002F ADD R6 R6 K2 + 0x7001FFE7, // 0030 JMP #0019 + 0x041C0902, // 0031 SUB R7 R4 K2 + 0x401E1007, // 0032 CONNECT R7 K8 R7 + 0x941C0A07, // 0033 GETIDX R7 R5 R7 + 0x80040E00, // 0034 RET 1 R7 + }) + ) +); +/*******************************************************************/ + +/********************************************************************/ +/* End of solidification */ diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h index 3b147cce7..ff9b1c9a9 100644 --- a/tasmota/my_user_config.h +++ b/tasmota/my_user_config.h @@ -1106,10 +1106,12 @@ // #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_CTR // enable AEC 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 #define USE_BERRY_CRYPTO_HMAC_SHA256 // enable HMAC SHA256 hash function + // #define USE_BERRY_CRYPTO_PBKDF2_HMAC_SHA256 // PBKDF2 with HMAC SHA256, used in Matter protocol #define USE_CSE7761 // Add support for CSE7761 Energy monitor as used in Sonoff Dual R3 // -- LVGL Graphics Library --------------------------------- 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 8f5e3613e..21f1553b4 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_crypto.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_crypto.ino @@ -24,6 +24,21 @@ #include "be_mem.h" #include "be_object.h" +/*********************************************************************************************\ + * members class + * +\*********************************************************************************************/ +extern "C" { + extern const be_const_member_t be_crypto_members[]; + extern const size_t be_crypto_members_size; + // virtual member + int be_class_crypto_member(bvm *vm); + int be_class_crypto_member(bvm *vm) { + be_const_module_member_raise(vm, be_crypto_members, be_crypto_members_size); + be_return(vm); + } +} + /*********************************************************************************************\ * AES_GCM class * @@ -33,7 +48,6 @@ extern "C" { // `AES_GCM.init(secret_key:bytes(32), iv:bytes(12)) -> instance` int32_t m_aes_gcm_init(struct bvm *vm); int32_t m_aes_gcm_init(struct bvm *vm) { -#ifdef USE_BERRY_CRYPTO_AES_GCM int32_t argc = be_top(vm); // Get the number of arguments if (argc >= 3 && be_isinstance(vm, 2) && be_isinstance(vm, 3)) { do { @@ -77,15 +91,11 @@ extern "C" { } while (0); } be_raise(vm, kTypeError, nullptr); -#else // USE_BERRY_CRYPTO_AES_GCM - be_raise(vm, "Not implemented", nullptr); -#endif // USE_BERRY_CRYPTO_AES_GCM } int32_t m_aes_gcm_encryt(bvm *vm); int32_t m_aes_gcm_decryt(bvm *vm); int32_t m_aes_gcm_encrypt_or_decryt(bvm *vm, int encrypt) { -#ifdef USE_BERRY_CRYPTO_AES_GCM int32_t argc = be_top(vm); // Get the number of arguments if (argc >= 2 && be_isinstance(vm, 2)) { do { @@ -115,27 +125,15 @@ extern "C" { } while (0); } be_raise(vm, kTypeError, nullptr); -#else // USE_BERRY_CRYPTO_AES_GCM - be_raise(vm, "Not implemented", nullptr); -#endif // USE_BERRY_CRYPTO_AES_GCM } int32_t m_aes_gcm_encryt(bvm *vm) { -#ifdef USE_BERRY_CRYPTO_AES_GCM return m_aes_gcm_encrypt_or_decryt(vm, 1); -#else // USE_BERRY_CRYPTO_AES_GCM - be_raise(vm, "Not implemented", nullptr); -#endif // USE_BERRY_CRYPTO_AES_GCM } int32_t m_aes_gcm_decryt(bvm *vm) { -#ifdef USE_BERRY_CRYPTO_AES_GCM return m_aes_gcm_encrypt_or_decryt(vm, 0); -#else // USE_BERRY_CRYPTO_AES_GCM - be_raise(vm, "Not implemented", nullptr); -#endif // USE_BERRY_CRYPTO_AES_GCM } int32_t m_aes_gcm_tag(bvm *vm) { -#ifdef USE_BERRY_CRYPTO_AES_GCM do { be_getglobal(vm, "bytes"); /* get the bytes class */ /* TODO eventually replace with be_getbuiltin */ @@ -153,9 +151,6 @@ extern "C" { // success } while (0); be_raise(vm, kTypeError, nullptr); -#else // USE_BERRY_CRYPTO_AES_GCM - be_raise(vm, "Not implemented", nullptr); -#endif // USE_BERRY_CRYPTO_AES_GCM } } @@ -168,7 +163,6 @@ extern "C" { // `AES_CTR.init(secret_key:bytes(32)) -> instance` int32_t m_aes_ctr_init(struct bvm *vm); int32_t m_aes_ctr_init(struct bvm *vm) { -#ifdef USE_BERRY_CRYPTO_AES_CTR int32_t argc = be_top(vm); // Get the number of arguments if (argc >= 2 && be_isbytes(vm, 2)) { do { @@ -191,16 +185,12 @@ extern "C" { } while (0); } be_raise(vm, kTypeError, nullptr); -#else // USE_BERRY_CRYPTO_AES_CTR - be_raise(vm, "Not implemented", nullptr); -#endif // USE_BERRY_CRYPTO_AES_CTR } // `.encrypt(content:bytes(), in:bytes(12), counter:int) -> nil` // `.decrypt(content:bytes(), in:bytes(12), counter:int) -> nil` int32_t m_aes_ctr_run(bvm *vm); int32_t m_aes_ctr_run(bvm *vm) { -#ifdef USE_BERRY_CRYPTO_AES_CTR int32_t argc = be_top(vm); // Get the number of arguments if (argc >= 4 && be_isbytes(vm, 2) && be_isbytes(vm, 3) && be_isint(vm, 4)) { do { @@ -233,9 +223,6 @@ extern "C" { } while (0); } be_raise(vm, kTypeError, nullptr); -#else // USE_BERRY_CRYPTO_AES_CTR - be_raise(vm, "Not implemented", nullptr); -#endif // USE_BERRY_CRYPTO_AES_CTR } } @@ -248,7 +235,6 @@ extern "C" { // `SHA256.init() -> nil` int32_t m_hash_sha256_init(struct bvm *vm); int32_t m_hash_sha256_init(struct bvm *vm) { -#ifdef USE_BERRY_CRYPTO_SHA256 // Initialize a SHA256 context br_sha256_context * ctx = (br_sha256_context *) be_os_malloc(sizeof(br_sha256_context)); if (!ctx) { @@ -259,9 +245,6 @@ extern "C" { be_newcomobj(vm, ctx, &be_commonobj_destroy_generic); be_setmember(vm, 1, ".p"); be_return_nil(vm); -#else // USE_BERRY_CRYPTO_SHA256 - be_raise(vm, "Not implemented", nullptr); -#endif // USE_BERRY_CRYPTO_SHA256 } // `.update(content:bytes()) -> nil` @@ -269,7 +252,6 @@ extern "C" { // Add raw bytes to the hash calculation int32_t m_hash_sha256_update(struct bvm *vm); int32_t m_hash_sha256_update(struct bvm *vm) { -#ifdef USE_BERRY_CRYPTO_SHA256 int32_t argc = be_top(vm); // Get the number of arguments if (argc >= 2 && be_isinstance(vm, 2)) { do { @@ -292,9 +274,6 @@ extern "C" { } while (0); } be_raise(vm, "value_error", NULL); -#else // USE_BERRY_CRYPTO_SHA256 - be_raise(vm, "Not implemented", nullptr); -#endif // USE_BERRY_CRYPTO_SHA256 } // `.finish() -> bytes()` @@ -302,7 +281,6 @@ extern "C" { // Add raw bytes to the MD5 calculation int32_t m_hash_sha256_out(struct bvm *vm); int32_t m_hash_sha256_out(struct bvm *vm) { -#ifdef USE_BERRY_CRYPTO_SHA256 be_getmember(vm, 1, ".p"); br_sha256_context * ctx; ctx = (br_sha256_context *) be_tocomptr(vm, -1); @@ -311,9 +289,6 @@ extern "C" { br_sha256_out(ctx, output); be_pushbytes(vm, output, sizeof(output)); be_return(vm); -#else // USE_BERRY_CRYPTO_SHA256 - be_raise(vm, "Not implemented", nullptr); -#endif // USE_BERRY_CRYPTO_SHA256 } } @@ -326,7 +301,6 @@ extern "C" { // `HMAC_SHA256.init(key:bytes) -> nil` int32_t m_hmac_sha256_init(struct bvm *vm); int32_t m_hmac_sha256_init(struct bvm *vm) { -#ifdef USE_BERRY_CRYPTO_HMAC_SHA256 int32_t argc = be_top(vm); // Get the number of arguments if (argc >= 2 && be_isbytes(vm, 2)) { // Initialize a HMAC context @@ -345,9 +319,6 @@ extern "C" { be_return_nil(vm); } be_raise(vm, kTypeError, nullptr); -#else // USE_BERRY_CRYPTO_HMAC_SHA256 - be_raise(vm, "Not implemented", nullptr); -#endif // USE_BERRY_CRYPTO_HMAC_SHA256 } // `.update(content:bytes()) -> nil` @@ -355,7 +326,6 @@ extern "C" { // Add raw bytes to the hash calculation int32_t m_hmac_sha256_update(struct bvm *vm); int32_t m_hmac_sha256_update(struct bvm *vm) { -#ifdef USE_BERRY_CRYPTO_HMAC_SHA256 int32_t argc = be_top(vm); // Get the number of arguments if (argc >= 2 && be_isinstance(vm, 2)) { do { @@ -377,9 +347,6 @@ extern "C" { } while (0); } be_raise(vm, "value_error", NULL); -#else // USE_BERRY_CRYPTO_HMAC_SHA256 - be_raise(vm, "Not implemented", nullptr); -#endif // USE_BERRY_CRYPTO_HMAC_SHA256 } // `.finish() -> bytes()` @@ -387,7 +354,6 @@ extern "C" { // Add raw bytes to the MD5 calculation int32_t m_hmac_sha256_out(struct bvm *vm); int32_t m_hmac_sha256_out(struct bvm *vm) { -#ifdef USE_BERRY_CRYPTO_HMAC_SHA256 be_getmember(vm, 1, ".p"); br_hmac_context * ctx; ctx = (br_hmac_context *) be_tocomptr(vm, -1); @@ -396,9 +362,221 @@ extern "C" { br_hmac_out(ctx, output); be_pushbytes(vm, output, sizeof(output)); be_return(vm); -#else // USE_BERRY_CRYPTO_HMAC_SHA256 - be_raise(vm, "Not implemented", nullptr); -#endif // USE_BERRY_CRYPTO_HMAC_SHA256 + } +} + +/*********************************************************************************************\ + * EC_P256 class + * +\*********************************************************************************************/ +#define BR_EC_P256_IMPL br_ec_p256_m15 // BearSSL implementation for Curve P256 +extern "C" { + // crypto.EC_P256().public_key(private_key:bytes(32)) -> bytes(32) + // Computes the public key from a completely random private key of 32 bytes + int32_t m_ec_p256_pubkey(bvm *vm); + int32_t m_ec_p256_pubkey(bvm *vm) { + int32_t argc = be_top(vm); // Get the number of arguments + if (argc >= 1 && be_isbytes(vm, 1)) { + size_t sk_len = 0; + uint8_t * sk = (uint8_t*) be_tobytes(vm, 1, &sk_len); + if (sk_len == 32) { + br_ec_private_key br_sk = { BR_EC_secp256r1, sk, 32 }; // TODO + + uint8_t pk_buf[80]; + size_t ret = br_ec_compute_pub(&BR_EC_P256_IMPL, nullptr, pk_buf, &br_sk); + if (ret > 0) { + be_pushbytes(vm, pk_buf, ret); + be_return(vm); + } + } + be_raise(vm, "value_error", "invalid input"); + } + be_raise(vm, kTypeError, nullptr); + } + + // crypto.EC_P256().shared_key(my_private_key:bytes(32), their_public_key:bytes(32)) -> bytes(32) + // Computes the shared pre-key. Normally this shared pre-key is hashed with another algorithm. + int32_t m_ec_p256_sharedkey(bvm *vm); + int32_t m_ec_p256_sharedkey(bvm *vm) { + int32_t argc = be_top(vm); // Get the number of arguments + if (argc >= 2 && be_isbytes(vm, 1) && be_isbytes(vm, 2)) { + size_t sk_len = 0; + const uint8_t * sk = (const uint8_t*) be_tobytes(vm, 1, &sk_len); + size_t pk_len = 0; + const uint8_t * pk_const = (const uint8_t*) be_tobytes(vm, 2, &pk_len); + if (sk_len != 32 || pk_len == 0 || pk_len > 65) { + be_raise(vm, "value_error", "Key size invalid"); + } + uint8_t pk[pk_len]; + memmove(pk, pk_const, pk_len); /* copy to a non-const variable to receive the result */ + + if (BR_EC_P256_IMPL.mul(pk, pk_len, sk, sk_len, BR_EC_secp256r1)) { + /* return value (xoff is one, length is 32) */ + be_pushbytes(vm, pk + 1, 32); + be_return(vm); + } else { + be_raise(vm, "internal_error", "internal bearssl error in mul()"); + } + be_raise(vm, "value_error", "invalid input"); + } + be_raise(vm, kTypeError, nullptr); + } + + // We have generated the P256 order as a i15 encoding using + // static const unsigned char P256_N[] PROGMEM = { + // 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, + // 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + // 0xBC, 0xE6, 0xFA, 0xAD, 0xA7, 0x17, 0x9E, 0x84, + // 0xF3, 0xB9, 0xCA, 0xC2^, 0xFC, 0x63, 0x25, 0x51 + // }; + // uint16_t m[20] = {}; + // br_i15_decode(m, P256_N, sizeof(P256_N)); + // AddLog(LOG_LEVEL_INFO, ">>>: mod=%*_H", sizeof(m), m); + // 11015125C6780B2BCE1D4F68F362692B7D73BC7FFF7FFF7FFF7FFF0F00000040FF7FFF7F0100 + + // N=bytes('11015125C6780B2BCE1D4F68F362692B7D73BC7FFF7FFF7FFF7FFF0F00000040FF7FFF7F0100') + // import string + // s = '' + // while size(N) > 0 + // var n = N.get(0, 2) + // s += string.format("0x%04X, ", n) + // N = N[2..] + // end + // print(s) + static const uint16_t P256_N_I15[] PROGMEM = { + 0x0111, + 0x2551, 0x78C6, 0x2B0B, 0x1DCE, 0x684F, 0x62F3, 0x2B69, 0x737D, + 0x7FBC, 0x7FFF, 0x7FFF, 0x7FFF, 0x0FFF, 0x0000, 0x4000, 0x7FFF, + 0x7FFF, 0x0001, + }; + extern void br_i15_decode(uint16_t *x, const void *src, size_t len); + extern void br_i15_decode_reduce(uint16_t *x, const void *src, size_t len, const uint16_t *m); + extern void br_i15_encode(void *dst, size_t len, const uint16_t *x); + extern uint32_t br_i15_sub(uint16_t *a, const uint16_t *b, uint32_t ctl); + // crypto.EC_P256().mod(data:bytes()) -> bytes(32) + // Reduces the big int to the modulus of P256 curve + int32_t m_ec_p256_mod(bvm *vm); + int32_t m_ec_p256_mod(bvm *vm) { + int32_t argc = be_top(vm); // Get the number of arguments + if (argc >= 1 && be_isbytes(vm, 1)) { + size_t data_len = 0; + uint8_t * data = (uint8_t*) be_tobytes(vm, 1, &data_len); + if (data_len == 0) { be_raise(vm, "value_error", "data must not be empty"); } + + uint16_t data0[20] = {}; + br_i15_decode_reduce(data0, data, data_len, P256_N_I15); + // AddLog(LOG_LEVEL_INFO, ">>>: data0=%*_H", sizeof(data0), data0); + + uint8_t out[32] = {}; + br_i15_encode(out, sizeof(out), data0); + // AddLog(LOG_LEVEL_INFO, ">>>: out=%*_H", sizeof(out), out); + // void br_i15_encode(void *dst, size_t len, const uint16_t *x); + + be_pushbytes(vm, out, sizeof(out)); + be_return(vm); + } + be_raise(vm, kTypeError, nullptr); + } + + // crypto.EC_P256().neg(data:bytes(32)) -> bytes(32) + // Negate a point coordinate modulus the order + int32_t m_ec_p256_neg(bvm *vm); + int32_t m_ec_p256_neg(bvm *vm) { + int32_t argc = be_top(vm); // Get the number of arguments + if (argc >= 1 && be_isbytes(vm, 1)) { + size_t data_len = 0; + uint8_t * data = (uint8_t*) be_tobytes(vm, 1, &data_len); + if (data_len == 0) { be_raise(vm, "value_error", "data must not be empty"); } + + uint16_t data0[20] = {}; + br_i15_decode_reduce(data0, data, data_len, P256_N_I15); + // AddLog(LOG_LEVEL_INFO, ">>>: data0=%*_H", sizeof(data0), data0); + + uint16_t a[sizeof(P256_N_I15)/sizeof(uint16_t)]; + memcpy(a, P256_N_I15, sizeof(P256_N_I15)); // copy generator to a + + uint32_t carry = br_i15_sub(a, data0, 1); // carry is always zero since the number if taken modulus the generator + // AddLog(LOG_LEVEL_INFO, ">>>: carry=%i data0=%*_H", carry, sizeof(data0), data0); + + uint8_t out[32] = {}; + br_i15_encode(out, sizeof(out), a); + // AddLog(LOG_LEVEL_INFO, ">>>: out=%*_H", sizeof(out), out); + // void br_i15_encode(void *dst, size_t len, const uint16_t *x); + + be_pushbytes(vm, out, sizeof(out)); + be_return(vm); + } + be_raise(vm, kTypeError, nullptr); + } + + uint32_t (*muladd)(unsigned char *A, const unsigned char *B, size_t len, + const unsigned char *x, size_t xlen, + const unsigned char *y, size_t ylen, int curve); + + // crypto.EC_P256().mul(x:bytes(), A:bytes(65)) -> bytes(65)` + // + // The point `x*A` is computed. + // `x` is unsigned and MUST be lower than order (use mod if not sure) + // `A` must be bytes(65) and unencoded (starting with 0x04) + int32_t m_ec_p256_mul(bvm *vm); + int32_t m_ec_p256_mul(bvm *vm) { + int32_t argc = be_top(vm); // Get the number of arguments + if (argc >= 2 && be_isbytes(vm, 1) && be_isbytes(vm, 2)) { + size_t x_len = 0; + const uint8_t * x = (const uint8_t*) be_tobytes(vm, 1, &x_len); + if (x_len == 0) { be_raise(vm, "value_error", "x must not be empty"); } + + size_t A_len = 0; + const uint8_t * A = (const uint8_t*) be_tobytes(vm, 2, &A_len); + if (A_len != 65 || (A_len > 0 && A[0] != 0x04)) { be_raise(vm, "value_error", "invalid A point"); } + + uint8_t res[65]; + memcpy(res, A, sizeof(res)); // copy A to res which will hold the result + uint32_t ret = BR_EC_P256_IMPL.mul(res, 65, x, x_len, BR_EC_secp256r1); + if (ret == 0) { be_raise(vm, "value_error", "muladd failed"); } + + be_pushbytes(vm, res, sizeof(res)); + be_return(vm); + } + be_raise(vm, kTypeError, nullptr); + } + // crypto.EC_P256().muladd(x:bytes(), A:bytes(65), y:bytes(), B;bytes(65) or bytes(0)) -> bytes(65)` + // + // The point `x*A + y*B` is computed. + // If `B` is empty, the Generator is taken instead. + // `x` and `y` are unsigned and MUST be lower than order (use mod if not sure) + // `A` and `B` must be bytes(65) and unencoded (starting with 0x04) + int32_t m_ec_p256_muladd(bvm *vm); + int32_t m_ec_p256_muladd(bvm *vm) { + int32_t argc = be_top(vm); // Get the number of arguments + if (argc >= 4 && be_isbytes(vm, 1) && be_isbytes(vm, 2) && be_isbytes(vm, 3) && be_isbytes(vm, 4)) { + size_t x_len = 0; + const uint8_t * x = (const uint8_t*) be_tobytes(vm, 1, &x_len); + if (x_len == 0) { be_raise(vm, "value_error", "x must not be empty"); } + size_t y_len = 0; + const uint8_t * y = (const uint8_t*) be_tobytes(vm, 3, &y_len); + if (y_len == 0) { be_raise(vm, "value_error", "y must not be empty"); } + + size_t A_len = 0; + const uint8_t * A = (const uint8_t*) be_tobytes(vm, 2, &A_len); + if (A_len != 65 || (A_len > 0 && A[0] != 0x04)) { be_raise(vm, "value_error", "invalid A point"); } + size_t B_len = 0; + const uint8_t * B = (const uint8_t*) be_tobytes(vm, 4, &B_len); + if (B_len == 0) { + B = nullptr; // generator + } else { + if (B_len != 65 || (B_len > 0 && B[0] != 0x04)) { be_raise(vm, "value_error", "invalid A point"); } + } + + uint8_t res[65]; + memcpy(res, A, sizeof(res)); // copy A to res which will hold the result + uint32_t ret = BR_EC_P256_IMPL.muladd(res, B, 65, x, x_len, y, y_len, BR_EC_secp256r1); + if (ret == 0) { be_raise(vm, "value_error", "muladd failed"); } + + be_pushbytes(vm, res, sizeof(res)); + be_return(vm); + } + be_raise(vm, kTypeError, nullptr); } } @@ -413,7 +591,6 @@ extern "C" { // Computes the public key from a completely random private key of 32 bytes int32_t m_ec_c25519_pubkey(bvm *vm); int32_t m_ec_c25519_pubkey(bvm *vm) { -#ifdef USE_BERRY_CRYPTO_EC_C25519 int32_t argc = be_top(vm); // Get the number of arguments if (argc >= 2 && be_isbytes(vm, 2)) { size_t buf_len = 0; @@ -436,16 +613,12 @@ extern "C" { be_raise(vm, "value_error", "invalid input"); } be_raise(vm, kTypeError, nullptr); -#else // USE_BERRY_CRYPTO_EC_C25519 - be_raise(vm, "Not implemented", nullptr); -#endif // USE_BERRY_CRYPTO_EC_C25519 } // crypto.EC_C25519().shared_key(my_private_key:bytes(32), their_public_key:bytes(32)) -> bytes(32) // Computes the shared pre-key. Normally this shared pre-key is hashed with another algorithm. int32_t m_ec_c25519_sharedkey(bvm *vm); int32_t m_ec_c25519_sharedkey(bvm *vm) { -#ifdef USE_BERRY_CRYPTO_EC_C25519 int32_t argc = be_top(vm); // Get the number of arguments if (argc >= 3 && be_isbytes(vm, 2) && be_isbytes(vm, 3)) { size_t sk_len = 0; @@ -465,16 +638,64 @@ extern "C" { be_pushbytes(vm, pk, pk_len); be_return(vm); } else { - be_raise(vm, "internal_error", "internal bearssl error in 519_m15.mul()"); + be_raise(vm, "internal_error", "internal bearssl error in mul()"); } } be_raise(vm, "value_error", "invalid input"); } be_raise(vm, kTypeError, nullptr); -#else // USE_BERRY_CRYPTO_EC_C25519 - be_raise(vm, "Not implemented", nullptr); -#endif // USE_BERRY_CRYPTO_EC_C25519 } } +/*********************************************************************************************\ + * PBKDF2_HMAC_SHA256 + * + * accelerate _f function +\*********************************************************************************************/ +extern "C" { + // _f(password:bytes(), salt_i:bytes(), c:int, res:bytes(32)) -> nil + int32_t m_pbkdf2_hmac_sha256_f(bvm *vm); + int32_t m_pbkdf2_hmac_sha256_f(bvm *vm) { + int32_t argc = be_top(vm); // Get the number of arguments + if (argc >= 4 && be_isbytes(vm, 1) && be_isbytes(vm, 2) && be_isint(vm, 3) && be_isbytes(vm, 4)) { + size_t passwd_len; + const void * passwd = be_tobytes(vm, 1, &passwd_len); + if (passwd_len == 0) { be_raise(vm, "value_error", "passwd must not be empty"); } + + size_t salt_len; + const void * salt = be_tobytes(vm, 2, &salt_len); + if (salt_len == 0) { be_raise(vm, "value_error", "salt must not be empty"); } + + int32_t count = be_toint(vm, 3); + if (count < 1 || count > 10000) { be_raise(vm, "value_error", "invalid iterations number"); } + + size_t res_len; + uint8_t * res = (uint8_t*) be_tobytes(vm, 4, &res_len); + if (res_len != 32) { be_raise(vm, "value_error", "res must be 32 bytes"); } + + br_hmac_context ctx; + br_hmac_key_context keyCtx; // keyCtx can be allocated on stack, it is not needed after `br_hmac_init` + br_hmac_key_init(&keyCtx, &br_sha256_vtable, passwd, passwd_len); + br_hmac_init(&ctx, &keyCtx, 0); // 0 is "natural output length" + + // iteration 1 + br_hmac_update(&ctx, salt, salt_len); + br_hmac_out(&ctx, res); + uint8_t u[32]; // rolling buffer + memcpy(u, res, 32); // copy res into u + + // further iterations + for (uint32_t i = 2; i <= count; i++) { + br_hmac_init(&ctx, &keyCtx, 0); // reinit HMAC + br_hmac_update(&ctx, u, sizeof(u)); + br_hmac_out(&ctx, u); + for (uint32_t j=0; j<32; j++) { + res[j] = res[j] ^ u[j]; + } + } + be_return_nil(vm); + } + be_raise(vm, kTypeError, nullptr); + } +} #endif // USE_BERRY