Berry: add crypto modules ED25519 and Chacha20-Poly1305 (#23338)

* add crypto modules for Berry

* remove Berry examples from PR
This commit is contained in:
Christian Baars 2025-04-25 00:11:47 +02:00 committed by GitHub
parent 115cefc557
commit 723684bb06
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 4662 additions and 0 deletions

View File

@ -32,6 +32,9 @@ extern int m_aes_ctr_tag(bvm *vm);
extern int m_aes_cbc_encrypt1(bvm *vm);
extern int m_aes_cbc_decrypt1(bvm *vm);
extern int m_chacha20_run(bvm *vm);
extern int m_poly1305_run(bvm *vm);
extern int m_ec_p256_pubkey(bvm *vm);
extern int m_ec_p256_sharedkey(bvm *vm);
extern int m_ec_p256_ecdsa_sign_sha256(bvm *vm);
@ -46,6 +49,10 @@ extern int m_ec_p256_mul(bvm *vm);
extern int m_ec_c25519_pubkey(bvm *vm);
extern int m_ec_c25519_sharedkey(bvm *vm);
extern int m_ed25519_sign(bvm *vm);
extern int m_ed25519_verify(bvm *vm);
extern int m_ed25519_secret_key(bvm *vm);
extern int m_hash_sha256_init(bvm *vm);
extern int m_hash_sha256_update(bvm *vm);
extern int m_hash_sha256_out(bvm *vm);
@ -68,8 +75,10 @@ extern const bclass be_class_md5;
#include "be_fixed_be_class_aes_gcm.h"
#include "be_fixed_be_class_aes_ctr.h"
#include "be_fixed_be_class_aes_cbc.h"
#include "be_fixed_be_class_chacha_poly.h"
#include "be_fixed_be_class_ec_p256.h"
#include "be_fixed_be_class_ec_c25519.h"
#include "be_fixed_be_class_ed25519.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"
@ -95,6 +104,10 @@ const be_const_member_t be_crypto_members[] = {
{ "/AES_GCM", (intptr_t) &be_class_aes_gcm },
#endif // USE_BERRY_CRYPTO_AES_GCM
#ifdef USE_BERRY_CRYPTO_CHACHA_POLY
{ "/CHACHA20_POLY1305", (intptr_t) &be_class_chacha_poly },
#endif // USE_BERRY_CRYPTO_CHACHA_POLY
#ifdef USE_BERRY_CRYPTO_EC_C25519
{ "/EC_C25519", (intptr_t) &be_class_ec_c25519 },
#endif // USE_BERRY_CRYPTO_EC_C25519
@ -103,6 +116,10 @@ const be_const_member_t be_crypto_members[] = {
{ "/EC_P256", (intptr_t) &be_class_ec_p256 },
#endif // USE_BERRY_CRYPTO_EC_P256
#ifdef USE_BERRY_CRYPTO_ED25519
{ "/ED25519", (intptr_t) &be_class_ed25519 },
#endif // USE_BERRY_CRYPTO_ED25519
#ifdef USE_BERRY_CRYPTO_HKDF_SHA256
{ "/HKDF_SHA256", (intptr_t) &be_class_hkdf_sha256 },
#endif // USE_BERRY_CRYPTO_HKDF_SHA256
@ -175,6 +192,11 @@ class be_class_aes_cbc (scope: global, name: AES_CBC) {
encrypt1, static_func(m_aes_cbc_encrypt1)
}
class be_class_chacha_poly (scope: global, name: CHACHA20_POLY1305) {
chacha_run, static_func(m_chacha20_run)
poly_run, static_func(m_poly1305_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)
@ -193,6 +215,12 @@ class be_class_ec_c25519 (scope: global, name: EC_C25519) {
shared_key, func(m_ec_c25519_sharedkey)
}
class be_class_ed25519 (scope: global, name: ED25519) {
sign, func(m_ed25519_sign)
verify, func(m_ed25519_verify)
secret_key, func(m_ed25519_secret_key)
}
class be_class_sha256 (scope: global, name: SHA256) {
.p, var

4219
tasmota/include/ed25519.h Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,176 @@
/*
chacha-merged.c version 20080118
D. J. Bernstein
Public domain.
*/
#ifndef TASMOTA_POLY1305_AUTH_H
#define TASMOTA_POLY1305_AUTH_H
#define POLY1305_TAGLEN 16
#define POLY1305_KEYLEN 32
#define mul32x32_64(a,b) ((uint64_t)(a) * (b))
#define U8TO32_LE(p) \
(((uint32_t)((p)[0])) | \
((uint32_t)((p)[1]) << 8) | \
((uint32_t)((p)[2]) << 16) | \
((uint32_t)((p)[3]) << 24))
#define U32TO8_LE(p, v) \
do { \
(p)[0] = (uint8_t)((v)); \
(p)[1] = (uint8_t)((v) >> 8); \
(p)[2] = (uint8_t)((v) >> 16); \
(p)[3] = (uint8_t)((v) >> 24); \
} while (0)
void
static poly1305_auth(char out[POLY1305_TAGLEN],
const char *m, size_t inlen,
const char key[POLY1305_KEYLEN])
{
uint32_t t0,t1,t2,t3;
uint32_t h0,h1,h2,h3,h4;
uint32_t r0,r1,r2,r3,r4;
uint32_t s1,s2,s3,s4;
uint32_t b, nb;
size_t j;
uint64_t t[5];
uint64_t f0,f1,f2,f3;
uint32_t g0,g1,g2,g3,g4;
uint64_t c;
unsigned char mp[16];
/* clamp key */
t0 = U8TO32_LE(key + 0);
t1 = U8TO32_LE(key + 4);
t2 = U8TO32_LE(key + 8);
t3 = U8TO32_LE(key + 12);
/* precompute multipliers */
r0 = t0 & 0x3ffffff; t0 >>= 26; t0 |= t1 << 6;
r1 = t0 & 0x3ffff03; t1 >>= 20; t1 |= t2 << 12;
r2 = t1 & 0x3ffc0ff; t2 >>= 14; t2 |= t3 << 18;
r3 = t2 & 0x3f03fff; t3 >>= 8;
r4 = t3 & 0x00fffff;
s1 = r1 * 5;
s2 = r2 * 5;
s3 = r3 * 5;
s4 = r4 * 5;
/* init state */
h0 = 0;
h1 = 0;
h2 = 0;
h3 = 0;
h4 = 0;
/* full blocks */
if (inlen < 16)
goto poly1305_donna_atmost15bytes;
poly1305_donna_16bytes:
m += 16;
inlen -= 16;
t0 = U8TO32_LE(m - 16);
t1 = U8TO32_LE(m - 12);
t2 = U8TO32_LE(m - 8);
t3 = U8TO32_LE(m - 4);
h0 += t0 & 0x3ffffff;
h1 += (uint32_t)(((((uint64_t)t1 << 32) | t0) >> 26) & 0x3ffffff);
h2 += (uint32_t)(((((uint64_t)t2 << 32) | t1) >> 20) & 0x3ffffff);
h3 += (uint32_t)(((((uint64_t)t3 << 32) | t2) >> 14) & 0x3ffffff);
h4 += (uint32_t)((t3 >> 8) | (1 << 24));
poly1305_donna_mul:
t[0] = mul32x32_64(h0,r0) + mul32x32_64(h1,s4) +
mul32x32_64(h2,s3) + mul32x32_64(h3,s2) +
mul32x32_64(h4,s1);
t[1] = mul32x32_64(h0,r1) + mul32x32_64(h1,r0) +
mul32x32_64(h2,s4) + mul32x32_64(h3,s3) +
mul32x32_64(h4,s2);
t[2] = mul32x32_64(h0,r2) + mul32x32_64(h1,r1) +
mul32x32_64(h2,r0) + mul32x32_64(h3,s4) +
mul32x32_64(h4,s3);
t[3] = mul32x32_64(h0,r3) + mul32x32_64(h1,r2) +
mul32x32_64(h2,r1) + mul32x32_64(h3,r0) +
mul32x32_64(h4,s4);
t[4] = mul32x32_64(h0,r4) + mul32x32_64(h1,r3) +
mul32x32_64(h2,r2) + mul32x32_64(h3,r1) +
mul32x32_64(h4,r0);
h0 = (uint32_t)t[0] & 0x3ffffff; c = (t[0] >> 26);
t[1] += c; h1 = (uint32_t)t[1] & 0x3ffffff; b = (uint32_t)(t[1] >> 26);
t[2] += b; h2 = (uint32_t)t[2] & 0x3ffffff; b = (uint32_t)(t[2] >> 26);
t[3] += b; h3 = (uint32_t)t[3] & 0x3ffffff; b = (uint32_t)(t[3] >> 26);
t[4] += b; h4 = (uint32_t)t[4] & 0x3ffffff; b = (uint32_t)(t[4] >> 26);
h0 += b * 5;
if (inlen >= 16)
goto poly1305_donna_16bytes;
/* final bytes */
poly1305_donna_atmost15bytes:
if (!inlen)
goto poly1305_donna_finish;
for (j = 0; j < inlen; j++)
mp[j] = m[j];
mp[j++] = 1;
for (; j < 16; j++)
mp[j] = 0;
inlen = 0;
t0 = U8TO32_LE(mp + 0);
t1 = U8TO32_LE(mp + 4);
t2 = U8TO32_LE(mp + 8);
t3 = U8TO32_LE(mp + 12);
h0 += t0 & 0x3ffffff;
h1 += (uint32_t)(((((uint64_t)t1 << 32) | t0) >> 26) & 0x3ffffff);
h2 += (uint32_t)(((((uint64_t)t2 << 32) | t1) >> 20) & 0x3ffffff);
h3 += (uint32_t)(((((uint64_t)t3 << 32) | t2) >> 14) & 0x3ffffff);
h4 += (uint32_t)(t3 >> 8);
goto poly1305_donna_mul;
poly1305_donna_finish:
b = h0 >> 26; h0 = h0 & 0x3ffffff;
h1 += b; b = h1 >> 26; h1 = h1 & 0x3ffffff;
h2 += b; b = h2 >> 26; h2 = h2 & 0x3ffffff;
h3 += b; b = h3 >> 26; h3 = h3 & 0x3ffffff;
h4 += b; b = h4 >> 26; h4 = h4 & 0x3ffffff;
h0 += b * 5; b = h0 >> 26; h0 = h0 & 0x3ffffff;
h1 += b;
g0 = h0 + 5; b = g0 >> 26; g0 &= 0x3ffffff;
g1 = h1 + b; b = g1 >> 26; g1 &= 0x3ffffff;
g2 = h2 + b; b = g2 >> 26; g2 &= 0x3ffffff;
g3 = h3 + b; b = g3 >> 26; g3 &= 0x3ffffff;
g4 = h4 + b - (1 << 26);
b = (g4 >> 31) - 1;
nb = ~b;
h0 = (h0 & nb) | (g0 & b);
h1 = (h1 & nb) | (g1 & b);
h2 = (h2 & nb) | (g2 & b);
h3 = (h3 & nb) | (g3 & b);
h4 = (h4 & nb) | (g4 & b);
f0 = ((h0 ) | (h1 << 26)) + (uint64_t)U8TO32_LE(&key[16]);
f1 = ((h1 >> 6) | (h2 << 20)) + (uint64_t)U8TO32_LE(&key[20]);
f2 = ((h2 >> 12) | (h3 << 14)) + (uint64_t)U8TO32_LE(&key[24]);
f3 = ((h3 >> 18) | (h4 << 8)) + (uint64_t)U8TO32_LE(&key[28]);
U32TO8_LE(&out[ 0], f0); f1 += (f0 >> 32);
U32TO8_LE(&out[ 4], f1); f2 += (f1 >> 32);
U32TO8_LE(&out[ 8], f2); f3 += (f2 >> 32);
U32TO8_LE(&out[12], f3);
}
#endif /* TASMOTA_POLY1305_AUTH_H */

View File

@ -23,6 +23,8 @@
#include <berry.h>
#include "be_mem.h"
#include "be_object.h"
#include "include/ed25519.h"
#include "include/poly1305_auth.h"
/*********************************************************************************************\
* members class
@ -616,6 +618,75 @@ extern "C" {
}
}
/*********************************************************************************************\
* CHACHA20-POLY1305 class
*
\*********************************************************************************************/
extern "C" {
// `chacha20_run(secret_key:bytes(32),iv:bytes(12),cipher:bytes(n*16),)-> int counter
int32_t m_chacha20_run(bvm *vm) {
int32_t argc = be_top(vm); // Get the number of arguments
if (argc >= 4 && be_isbytes(vm, 1) // secret_key - 32 bytes
&& be_isbytes(vm, 2) // iv/nonce - 12 bytes
&& be_isint(vm, 3) // counter
&& be_isbytes(vm, 4) // data/cipher
) {
size_t key_len = 0;
const void * key = be_tobytes(vm, 1, &key_len);
if (key_len != 32) {
AddLog(LOG_LEVEL_INFO, PSTR(" %d bytes"), key_len);
be_raise(vm, "value_error", "Key size must be 32 bytes");
}
size_t iv_len = 0;
void * iv = (void *) be_tobytes(vm, 2, &iv_len);
if (iv_len != 12) {
AddLog(LOG_LEVEL_INFO, PSTR(" %d bytes"), iv_len);
be_raise(vm, "value_error", "IV size must be 12");
}
int32_t cc = be_toint(vm, 3);
size_t data_len = 0;
void * data = (void *) be_tobytes(vm, 4, &data_len);
br_chacha20_run bc = br_chacha20_ct_run;
int32_t new_cc = br_chacha20_ct_run(key, iv, cc, data, data_len);
be_pushint(vm, new_cc);
be_return(vm);
}
be_raise(vm, kTypeError, nullptr);
}
// `poly_run(data:bytes(),poly_key:bytes(32),)-> tag:bytes(16)`
int m_poly1305_run(bvm *vm){
int32_t argc = be_top(vm); // Get the number of arguments
if (argc >= 2 && be_isbytes(vm, 1) // data in
&& be_isbytes(vm, 2) // poly key
) {
char tag[POLY1305_TAGLEN];
size_t data_len = 0;
const char * data = (const char *) be_tobytes(vm, 1, &data_len);
size_t polykey_len = 0;
char * polykey = (char *) be_tobytes(vm, 2, &polykey_len);
if (polykey_len != POLY1305_KEYLEN) {
AddLog(LOG_LEVEL_INFO, PSTR(" %d bytes"), polykey_len);
be_raise(vm, "value_error", "poly key size must be 32 bytes");
}
poly1305_auth(tag, data, data_len,polykey);
be_pushbytes(vm, tag, sizeof(tag));
be_return(vm);
}
be_raise(vm, kTypeError, nullptr);
}
}
/*********************************************************************************************\
* SHA256 class
*
@ -1175,6 +1246,174 @@ extern "C" {
}
}
/*********************************************************************************************\
* ED25519 class
*
\*********************************************************************************************/
extern "C" {
/* internal function to generate a secret key from a seed
* The secret key is 64 bytes long, the first 32 bytes are the seed
* and the last 32 bytes are the public key.
* The seed must be 32 bytes long.
*/
void _ed25519_get_secret_key(unsigned char *private_key, const unsigned char *seed){
ge_p3 A;
unsigned char pub_key[32];
br_sha512_context ctx;
br_sha512_init(&ctx);
br_sha512_update(&ctx, seed, 32);
br_sha512_out(&ctx, private_key);
private_key[0] &= 248;
private_key[31] &= 63;
private_key[31] |= 64;
ge_scalarmult_base(&A, private_key);
ge_p3_tobytes(pub_key, &A);
memmove(private_key, seed, 32);
memmove(private_key + 32, pub_key, 32);
}
/*crypto.ED25519().secret_key(seed:bytes(32)) -> bytes(64)*/
int32_t m_ed25519_secret_key(bvm *vm) {
int32_t argc = be_top(vm); // Get the number of arguments
if (argc == 2 && be_isbytes(vm, 2)) // seed
{
size_t seed_len = 0;
const unsigned char * seed = (const unsigned char *) be_tobytes(vm, 2, &seed_len);
if (seed_len != 32) {
be_raise(vm, "value_error", "seed size must be 32");
}
unsigned char sec_key[64];
_ed25519_get_secret_key(sec_key, seed);
be_pushbytes(vm, sec_key, 64);
be_return(vm);
}
be_raise(vm, kTypeError, nullptr);
}
// https://github.com/rdeker/ed25519/blob/13a0661670949bc2e1cfcd8720082d9670768041/src/sign.c
/*crypto.ED25519().sign(message:bytes(), secret_key:bytes(64)) -> signature:bytes(64)*/
int32_t m_ed25519_sign(bvm *vm) {
int32_t argc = be_top(vm); // Get the number of arguments
if (argc == 3 && be_isbytes(vm, 2) // message
&& be_isbytes(vm, 3)) // secret key
{
size_t msg_len = 0;
const void * msg = be_tobytes(vm, 2, &msg_len);
size_t sec_key_len = 0;
void * sec_key = (void *) be_tobytes(vm, 3, &sec_key_len);
if (sec_key_len != 64) {
be_raise(vm, "value_error", "Key size must be 64");
}
uint8_t sign[64];
unsigned char hram[64];
unsigned char r[64];
unsigned char az[64];
ge_p3 R;
br_sha512_context ctx;
br_sha512_init(&ctx);
br_sha512_update(&ctx, (unsigned char*)sec_key, 32);
br_sha512_out(&ctx, az);
az[0] &= 248;
az[31] &= 63;
az[31] |= 64;
br_sha512_init(&ctx);
br_sha512_update(&ctx, az + 32, 32);
br_sha512_update(&ctx, msg, msg_len);
br_sha512_out(&ctx, r);
memmove((unsigned char*)sign + 32, (unsigned char*)sec_key + 32, 32);
// memmove((unsigned char*)sign + 32, (unsigned char*)pub_key, 32);
sc_reduce(r);
ge_scalarmult_base(&R, r);
ge_p3_tobytes((unsigned char*)sign, &R);
br_sha512_init(&ctx);
br_sha512_update(&ctx, sign, 64);
br_sha512_update(&ctx, msg, msg_len);
br_sha512_out(&ctx, hram);
sc_reduce(hram);
sc_muladd((unsigned char*)sign + 32, hram, az, r);
be_pushbytes(vm, sign, 64);
be_return(vm);
}
be_raise(vm, kTypeError, nullptr);
}
// https://github.com/rdeker/ed25519/blob/13a0661670949bc2e1cfcd8720082d9670768041/src/verify.c
/*crypto.ED25519().verify(message:bytes(), signature:bytes(64), public_key:bytes(32)) -> bool*/
int32_t m_ed25519_verify(bvm *vm) {
int32_t argc = be_top(vm); // Get the number of arguments
if (argc >= 4 && be_isbytes(vm, 2) // message
&& be_isbytes(vm, 3) // signature
&& be_isbytes(vm, 4)) // public key
{
size_t message_len = 0;
const void * message = be_tobytes(vm, 2, &message_len);
size_t sign_len = 0;
const unsigned char * signature = (const unsigned char *) be_tobytes(vm, 3, &sign_len);
if (sign_len != 64) {
be_raise(vm, "value_error", "signature size must be 64");
}
size_t pub_key_len = 0;
const unsigned char * public_key = (const unsigned char *) be_tobytes(vm, 4, &pub_key_len);
if (pub_key_len != 32) {
be_raise(vm, "value_error", "key size must be 32");
}
unsigned char h[64];
unsigned char checker[32];
br_sha512_context ctx;
ge_p3 A;
ge_p2 R;
bbool success = false;
if (signature[63] & 224) {
AddLog(LOG_LEVEL_INFO, PSTR(" signature[63] & 224"));
goto eddsa_verify_exit;
}
if (ge_frombytes_negate_vartime(&A, public_key) != 0) {
AddLog(LOG_LEVEL_INFO, PSTR(" ge_frombytes_negate_vartime(&A, public_key) != 0"));
goto eddsa_verify_exit;
}
br_sha512_init(&ctx);
br_sha512_update(&ctx, signature, 32);
br_sha512_update(&ctx, public_key, 32);
br_sha512_update(&ctx, message, message_len);
br_sha512_out(&ctx, h);
sc_reduce(h);
ge_double_scalarmult_vartime(&R, h, &A, signature + 32);
ge_tobytes(checker, &R);
if (memcmp(checker, signature, 32) == 0) {
success = true;
}
eddsa_verify_exit:
be_pushbool(vm, success);
be_return(vm);
}
be_raise(vm, kTypeError, nullptr);
}
}
/*********************************************************************************************\
* HKDF_SHA256
*