Berry crypto add `EC_P256 and PBKDF2_HMAC_SHA256` algorithms required by Matter protocol (#17473)

This commit is contained in:
s-hadinger 2022-12-22 10:09:13 +01:00 committed by GitHub
parent ad97f7b95f
commit f06c07996c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 645 additions and 68 deletions

View File

@ -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

View File

@ -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;

View File

@ -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 */

View File

@ -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
-#

View File

@ -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 */

View File

@ -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 ---------------------------------

View File

@ -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
}
// `<instance:AES_CTR>.encrypt(content:bytes(), in:bytes(12), counter:int) -> nil`
// `<instance:AES_CTR>.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
}
// `<instance: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
}
// `<instance: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
}
// `<instance: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
}
// `<instance: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