mirror of https://github.com/arendst/Tasmota.git
1298 lines
48 KiB
C++
1298 lines
48 KiB
C++
/*
|
|
xdrv_52_3_berry_md5.ino - Berry scripting language, Md5 class
|
|
|
|
Copyright (C) 2021 Stephan Hadinger, Berry language by Guan Wenliang https://github.com/Skiars/berry
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
|
|
#ifdef USE_BERRY
|
|
|
|
#include <berry.h>
|
|
#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);
|
|
}
|
|
}
|
|
|
|
/*********************************************************************************************\
|
|
* Random bytes generator
|
|
*
|
|
* As long as Wifi or BLE is enable, it uses a hardware source for true randomnesss
|
|
*
|
|
\*********************************************************************************************/
|
|
extern "C" {
|
|
// `crypto.random(num_bytes:int) -> bytes(num_bytes)`
|
|
//
|
|
// Generates a series of random bytes
|
|
int m_crypto_random(bvm *vm);
|
|
int m_crypto_random(bvm *vm) {
|
|
int32_t argc = be_top(vm); // Get the number of arguments
|
|
if (argc >= 1 && be_isint(vm, 1)) {
|
|
int32_t n = be_toint(vm, 1);
|
|
if (n < 0 || n > 4096) { be_raise(vm, "value_error", ""); }
|
|
|
|
uint8_t rand_bytes[n];
|
|
esp_fill_random(rand_bytes, n);
|
|
be_pushbytes(vm, rand_bytes, n);
|
|
be_return(vm);
|
|
}
|
|
be_raise(vm, kTypeError, nullptr);
|
|
}
|
|
}
|
|
|
|
/*********************************************************************************************\
|
|
* RSA class
|
|
*
|
|
\*********************************************************************************************/
|
|
extern "C" {
|
|
// crypto.RSA.rsassa_pkcs1_v1_5(private_key:bytes(), msg:bytes()) -> bytes()
|
|
// Parses RSA private key from DER binary
|
|
int32_t m_rsa_rsassa_pkcs1_v1_5(bvm *vm);
|
|
int32_t m_rsa_rsassa_pkcs1_v1_5(bvm *vm) {
|
|
int32_t argc = be_top(vm); // Get the number of arguments
|
|
if (argc >= 2 && be_isbytes(vm, 1)) {
|
|
size_t sk_len = 0;
|
|
uint8_t * sk_der = (uint8_t*) be_tobytes(vm, 1, &sk_len);
|
|
|
|
// 1. decode the DER private key
|
|
br_skey_decoder_context sdc;
|
|
br_skey_decoder_init(&sdc);
|
|
br_skey_decoder_push(&sdc, sk_der, sk_len);
|
|
if (int ret = br_skey_decoder_last_error(&sdc)) {
|
|
be_raisef(vm, "value_error", "invalid private key %i", ret);
|
|
}
|
|
if (br_skey_decoder_key_type(&sdc) != BR_KEYTYPE_RSA) {
|
|
be_raise(vm, "value_error", "key is not RSA");
|
|
}
|
|
const br_rsa_private_key *sk = br_skey_decoder_get_rsa(&sdc);
|
|
|
|
// 2. Hash the message with SHA
|
|
size_t msg_len = 0;
|
|
uint8_t * msg = (uint8_t*) be_tobytes(vm, 2, &msg_len);
|
|
uint8_t hash[32];
|
|
br_sha256_context ctx;
|
|
br_sha256_init(&ctx);
|
|
br_sha256_update(&ctx, msg, msg_len);
|
|
br_sha256_out(&ctx, hash);
|
|
|
|
// 3. sign the message
|
|
size_t sign_len = (sk->n_bitlen + 7) / 8;
|
|
uint8_t x[sign_len] = {};
|
|
int err = br_rsa_i15_pkcs1_sign(BR_HASH_OID_SHA256, hash, sizeof(hash), sk, x);
|
|
if (err != 1) {
|
|
be_raisef(vm, "value_error", "signature failed %i", err);
|
|
}
|
|
|
|
be_pushbytes(vm, x, sign_len);
|
|
be_return(vm);
|
|
}
|
|
be_raise(vm, kTypeError, nullptr);
|
|
}
|
|
}
|
|
|
|
/*********************************************************************************************\
|
|
* AES_GCM class
|
|
*
|
|
\*********************************************************************************************/
|
|
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) {
|
|
int32_t argc = be_top(vm); // Get the number of arguments
|
|
if (argc >= 3 && be_isinstance(vm, 2) && be_isinstance(vm, 3)) {
|
|
do {
|
|
be_getglobal(vm, "bytes"); /* get the bytes class */ /* TODO eventually replace with be_getbuiltin */
|
|
if (!be_isderived(vm, 2)) break;
|
|
size_t length = 0;
|
|
const void * bytes = be_tobytes(vm, 2, &length);
|
|
if (!bytes) break;
|
|
if (length != 32) {
|
|
be_raise(vm, "value_error", "Key size must be 32 bytes");
|
|
}
|
|
|
|
be_getglobal(vm, "bytes"); /* get the bytes class */ /* TODO eventually replace with be_getbuiltin */
|
|
if (!be_isderived(vm, 3)) break;
|
|
size_t iv_length = 0;
|
|
const void * iv_bytes = be_tobytes(vm, 3, &iv_length);
|
|
if (!iv_bytes) break;
|
|
|
|
// Initialize an AES CTR structure with the secret key
|
|
br_aes_small_ctr_keys * ctr_ctx = (br_aes_small_ctr_keys *) be_os_malloc(sizeof(br_aes_small_ctr_keys));
|
|
if (!ctr_ctx) { be_throw(vm, BE_MALLOC_FAIL); }
|
|
br_aes_small_ctr_init(ctr_ctx, bytes, length);
|
|
be_newcomobj(vm, ctr_ctx, &be_commonobj_destroy_generic);
|
|
be_setmember(vm, 1, ".p1");
|
|
|
|
// Initialize an AES GCM structure based on this CTR engine
|
|
br_gcm_context * gcm_ctx = (br_gcm_context *) be_os_malloc(sizeof(br_gcm_context));
|
|
if (!gcm_ctx) { be_throw(vm, BE_MALLOC_FAIL); }
|
|
br_gcm_init(gcm_ctx, &ctr_ctx->vtable, &br_ghash_ctmul32);
|
|
be_newcomobj(vm, gcm_ctx, &be_commonobj_destroy_generic);
|
|
be_setmember(vm, 1, ".p2");
|
|
|
|
// Reset GCM context with provided IV
|
|
br_gcm_reset(gcm_ctx, iv_bytes, iv_length);
|
|
|
|
// We don't have any additional authenticated data so we flip instantly
|
|
br_gcm_flip(gcm_ctx);
|
|
|
|
be_return_nil(vm);
|
|
// success
|
|
} while (0);
|
|
}
|
|
be_raise(vm, kTypeError, nullptr);
|
|
}
|
|
|
|
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) {
|
|
int32_t argc = be_top(vm); // Get the number of arguments
|
|
if (argc >= 2 && be_isinstance(vm, 2)) {
|
|
do {
|
|
be_getglobal(vm, "bytes"); /* get the bytes class */ /* TODO eventually replace with be_getbuiltin */
|
|
if (!be_isderived(vm, 2)) break;
|
|
|
|
// get GCM context
|
|
be_getmember(vm, 1, ".p2");
|
|
br_gcm_context * gcm_ctx = (br_gcm_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_gcm_run(gcm_ctx, encrypt, bytes, length);
|
|
|
|
be_return(vm);
|
|
// success
|
|
} while (0);
|
|
}
|
|
be_raise(vm, kTypeError, nullptr);
|
|
}
|
|
int32_t m_aes_gcm_encryt(bvm *vm) {
|
|
return m_aes_gcm_encrypt_or_decryt(vm, 1);
|
|
}
|
|
int32_t m_aes_gcm_decryt(bvm *vm) {
|
|
return m_aes_gcm_encrypt_or_decryt(vm, 0);
|
|
}
|
|
|
|
int32_t m_aes_gcm_tag(bvm *vm) {
|
|
do {
|
|
be_getglobal(vm, "bytes"); /* get the bytes class */ /* TODO eventually replace with be_getbuiltin */
|
|
|
|
// get GCM context
|
|
be_getmember(vm, 1, ".p2");
|
|
br_gcm_context * gcm_ctx = (br_gcm_context *) be_tocomptr(vm, -1);
|
|
be_pop(vm, 1);
|
|
|
|
// create a bytes buffer of 16 bytes
|
|
uint8_t tag[16] = {0};
|
|
br_gcm_get_tag(gcm_ctx, tag);
|
|
be_pushbytes(vm, tag, sizeof(tag));
|
|
|
|
be_return(vm);
|
|
// success
|
|
} while (0);
|
|
be_raise(vm, kTypeError, nullptr);
|
|
}
|
|
}
|
|
|
|
/*********************************************************************************************\
|
|
* 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"); }
|
|
|
|
if (aad_len > 0) {
|
|
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_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)) {
|
|
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_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_encrypt1_or_decryt1(bvm *vm, int encrypt);
|
|
int32_t m_aes_ccm_encrypt1(bvm *vm) { return m_aes_ccm_encrypt1_or_decryt1(vm, 1); }
|
|
int32_t m_aes_ccm_decrypt1(bvm *vm) { return m_aes_ccm_encrypt1_or_decryt1(vm, 0); }
|
|
int32_t m_aes_ccm_encrypt1_or_decryt1(bvm *vm, int encrypt) {
|
|
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, encrypt, 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 (encrypt) {
|
|
// copy the tag back
|
|
memcpy(tag, tag_computed, tag_len);
|
|
be_pushbool(vm, btrue);
|
|
} else {
|
|
// check that the tag match
|
|
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);
|
|
}
|
|
}
|
|
|
|
/*********************************************************************************************\
|
|
* AES_CTR class
|
|
*
|
|
\*********************************************************************************************/
|
|
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) {
|
|
int32_t argc = be_top(vm); // Get the number of arguments
|
|
if (argc >= 2 && be_isbytes(vm, 2)) {
|
|
do {
|
|
size_t length = 0;
|
|
const void * bytes = be_tobytes(vm, 2, &length);
|
|
if (!bytes) break;
|
|
if (length != 32) {
|
|
be_raise(vm, "value_error", "Key size must be 32 bytes");
|
|
}
|
|
|
|
// Initialize an AES CTR structure with the secret key
|
|
br_aes_small_ctr_keys * ctr_ctx = (br_aes_small_ctr_keys *) be_os_malloc(sizeof(br_aes_small_ctr_keys));
|
|
if (!ctr_ctx) { be_throw(vm, BE_MALLOC_FAIL); }
|
|
br_aes_small_ctr_init(ctr_ctx, bytes, length);
|
|
be_newcomobj(vm, ctr_ctx, &be_commonobj_destroy_generic);
|
|
be_setmember(vm, 1, ".p1");
|
|
|
|
be_return_nil(vm);
|
|
// success
|
|
} while (0);
|
|
}
|
|
be_raise(vm, kTypeError, nullptr);
|
|
}
|
|
|
|
// `<instance:AES_CTR>.encrypt(content:bytes(), in:bytes(12), counter:int) -> bytes()`
|
|
// `<instance:AES_CTR>.decrypt(content:bytes(), in:bytes(12), counter:int) -> bytes()`
|
|
int32_t m_aes_ctr_run(bvm *vm);
|
|
int32_t m_aes_ctr_run(bvm *vm) {
|
|
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 {
|
|
// get GCM context
|
|
be_getmember(vm, 1, ".p1");
|
|
br_aes_small_ctr_keys * ctx = (br_aes_small_ctr_keys *) be_tocomptr(vm, -1);
|
|
be_pop(vm, 1);
|
|
|
|
size_t iv_len;
|
|
const void * iv = be_tobytes(vm, 3, &iv_len);
|
|
if (iv_len != 12) { be_raise(vm, "value_error", "IV size must be 12 bytes"); }
|
|
|
|
uint32_t counter = be_toint(vm, 4);
|
|
|
|
// 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;
|
|
|
|
uint32_t next_counter = br_aes_small_ctr_run(ctx, iv, counter, bytes, length);
|
|
|
|
be_return(vm);
|
|
// success
|
|
} while (0);
|
|
}
|
|
be_raise(vm, kTypeError, nullptr);
|
|
}
|
|
}
|
|
|
|
/*********************************************************************************************\
|
|
* AES_CBC class
|
|
*
|
|
\*********************************************************************************************/
|
|
extern "C" {
|
|
// `AES_CBC.encrypt1(secret_key:bytes(16),iv:bytes(16),data:bytes(n*16))-> bool (true)
|
|
int32_t m_aes_cbc_encrypt1(bvm *vm);
|
|
int32_t m_aes_cbc_encrypt1(bvm *vm) {
|
|
int32_t argc = be_top(vm); // Get the number of arguments
|
|
if (argc >= 3 && be_isbytes(vm, 1) // secret_key - 16 bytes
|
|
&& be_isbytes(vm, 2) // iv - 16 bytes
|
|
&& be_isbytes(vm, 3) // data/cipher - multiple 16 bytes
|
|
) {
|
|
|
|
size_t key_len = 0;
|
|
const void * key = be_tobytes(vm, 1, &key_len);
|
|
if (key_len != 16) {
|
|
be_raise(vm, "value_error", "Key size must be 16 bytes");
|
|
}
|
|
|
|
size_t iv_len = 0;
|
|
void * iv = (void *) be_tobytes(vm, 2, &iv_len);
|
|
if (iv_len != 16) {
|
|
be_raise(vm, "value_error", "IV size must be 16");
|
|
}
|
|
|
|
size_t data_len = 0;
|
|
void * data = (void *) be_tobytes(vm, 3, &data_len);
|
|
if (data_len%16 != 0) {
|
|
be_raise(vm, "value_error", "Data size must be multiple of 16");
|
|
}
|
|
|
|
// Initialize an AES CBC encryption structure with the secret key, then run with IV and data
|
|
br_aes_small_cbcenc_keys cbc_ctx;
|
|
br_aes_small_cbcenc_init(&cbc_ctx, key, 16);
|
|
br_aes_small_cbcenc_run( &cbc_ctx, iv, data, data_len );
|
|
|
|
// (unchecked )success
|
|
be_pushbool(vm, btrue);
|
|
be_return(vm);
|
|
}
|
|
be_raise(vm, kTypeError, nullptr);
|
|
}
|
|
// `AES_CBC.decrypt1(secret_key:bytes(16),iv:bytes(16),cipher:bytes(n*16))-> bool (true)
|
|
int32_t m_aes_cbc_decrypt1(bvm *vm);
|
|
int32_t m_aes_cbc_decrypt1(bvm *vm) {
|
|
int32_t argc = be_top(vm); // Get the number of arguments
|
|
if (argc >= 3 && be_isbytes(vm, 1) // secret_key - 16 bytes
|
|
&& be_isbytes(vm, 2) // iv - 16 bytes
|
|
&& be_isbytes(vm, 3) // cipher/data - multiple 16 bytes
|
|
) {
|
|
|
|
size_t key_len = 0;
|
|
const void * key = be_tobytes(vm, 1, &key_len);
|
|
if (key_len != 16) {
|
|
be_raise(vm, "value_error", "Key size must be 16 bytes");
|
|
}
|
|
|
|
size_t iv_len = 0;
|
|
void * iv = (void *) be_tobytes(vm, 2, &iv_len);
|
|
if (iv_len != 16) {
|
|
be_raise(vm, "value_error", "IV size must be 16");
|
|
}
|
|
|
|
size_t data_len = 0;
|
|
void * data = (void *) be_tobytes(vm, 3, &data_len);
|
|
if (data_len%16 != 0) {
|
|
be_raise(vm, "value_error", "Cipher size must be multiple of 16");
|
|
}
|
|
|
|
// Initialize an AES CBC decryption structure with the secret key, then run with IV and data
|
|
br_aes_small_cbcdec_keys cbc_ctx;
|
|
br_aes_small_cbcdec_init(&cbc_ctx, key, 16);
|
|
br_aes_small_cbcdec_run( &cbc_ctx, iv, data, data_len );
|
|
|
|
// (unchecked )success
|
|
be_pushbool(vm, btrue);
|
|
be_return(vm);
|
|
}
|
|
be_raise(vm, kTypeError, nullptr);
|
|
}
|
|
}
|
|
|
|
/*********************************************************************************************\
|
|
* SHA256 class
|
|
*
|
|
\*********************************************************************************************/
|
|
extern "C" {
|
|
|
|
// `SHA256.init() -> nil`
|
|
int32_t m_hash_sha256_init(struct bvm *vm);
|
|
int32_t m_hash_sha256_init(struct bvm *vm) {
|
|
// Initialize a SHA256 context
|
|
br_sha256_context * ctx = (br_sha256_context *) be_os_malloc(sizeof(br_sha256_context));
|
|
if (!ctx) {
|
|
be_throw(vm, BE_MALLOC_FAIL);
|
|
}
|
|
br_sha256_init(ctx);
|
|
|
|
be_newcomobj(vm, ctx, &be_commonobj_destroy_generic);
|
|
be_setmember(vm, 1, ".p");
|
|
be_return_nil(vm);
|
|
}
|
|
|
|
// `<instance:SHA256>.update(content:bytes()) -> self`
|
|
//
|
|
// 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) {
|
|
int32_t argc = be_top(vm); // Get the number of arguments
|
|
if (argc >= 2 && be_isinstance(vm, 2)) {
|
|
do {
|
|
be_getglobal(vm, "bytes"); /* get the bytes class */ /* TODO eventually replace with be_getbuiltin */
|
|
if (!be_isderived(vm, 2)) break;
|
|
size_t length = 0;
|
|
const void * bytes = be_tobytes(vm, 2, &length);
|
|
if (!bytes) break;
|
|
|
|
be_getmember(vm, 1, ".p");
|
|
br_sha256_context * ctx;
|
|
ctx = (br_sha256_context *) be_tocomptr(vm, -1);
|
|
if (!ctx) break;
|
|
|
|
if (length > 0) {
|
|
br_sha256_update(ctx, bytes, length);
|
|
}
|
|
be_pushvalue(vm, 1); // return self
|
|
be_return(vm);
|
|
// success
|
|
} while (0);
|
|
}
|
|
be_raise(vm, "value_error", NULL);
|
|
}
|
|
|
|
// `<instance:SHA256>.finish() -> bytes()`
|
|
//
|
|
// 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) {
|
|
be_getmember(vm, 1, ".p");
|
|
br_sha256_context * ctx;
|
|
ctx = (br_sha256_context *) be_tocomptr(vm, -1);
|
|
|
|
uint8_t output[32];
|
|
br_sha256_out(ctx, output);
|
|
be_pushbytes(vm, output, sizeof(output));
|
|
be_return(vm);
|
|
}
|
|
}
|
|
|
|
/*********************************************************************************************\
|
|
* HMAC_SHA256 class
|
|
*
|
|
\*********************************************************************************************/
|
|
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) {
|
|
int32_t argc = be_top(vm); // Get the number of arguments
|
|
if (argc >= 2 && be_isbytes(vm, 2)) {
|
|
// Initialize a HMAC context
|
|
br_hmac_context * ctx = (br_hmac_context *) be_os_malloc(sizeof(br_hmac_context));
|
|
if (!ctx) {
|
|
be_throw(vm, BE_MALLOC_FAIL);
|
|
}
|
|
br_hmac_key_context keyCtx; // keyCtx can be allocated on stack, it is not needed after `br_hmac_init`
|
|
size_t key_len;
|
|
const void *key = be_tobytes(vm, 2, &key_len);
|
|
br_hmac_key_init(&keyCtx, &br_sha256_vtable, key, key_len);
|
|
br_hmac_init(ctx, &keyCtx, 0); // 0 is "natural output length"
|
|
|
|
be_newcomobj(vm, ctx, &be_commonobj_destroy_generic);
|
|
be_setmember(vm, 1, ".p");
|
|
be_return_nil(vm);
|
|
}
|
|
be_raise(vm, kTypeError, nullptr);
|
|
}
|
|
|
|
// `<instance:HMAC_SHA256>.update(content:bytes()) -> self`
|
|
//
|
|
// 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) {
|
|
int32_t argc = be_top(vm); // Get the number of arguments
|
|
if (argc >= 2 && be_isinstance(vm, 2)) {
|
|
do {
|
|
if (!be_isbytes(vm, 2)) break;
|
|
size_t length = 0;
|
|
const void * bytes = be_tobytes(vm, 2, &length);
|
|
if (!bytes) break;
|
|
|
|
be_getmember(vm, 1, ".p");
|
|
br_hmac_context * ctx;
|
|
ctx = (br_hmac_context *) be_tocomptr(vm, -1);
|
|
if (!ctx) break;
|
|
|
|
if (length > 0) {
|
|
br_hmac_update(ctx, bytes, length);
|
|
}
|
|
be_pushvalue(vm, 1); // return self
|
|
be_return(vm);
|
|
// success
|
|
} while (0);
|
|
}
|
|
be_raise(vm, "value_error", NULL);
|
|
}
|
|
|
|
// `<instance:SHA256>.finish() -> bytes()`
|
|
//
|
|
// 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) {
|
|
be_getmember(vm, 1, ".p");
|
|
br_hmac_context * ctx;
|
|
ctx = (br_hmac_context *) be_tocomptr(vm, -1);
|
|
|
|
uint8_t output[32];
|
|
br_hmac_out(ctx, output);
|
|
be_pushbytes(vm, output, sizeof(output));
|
|
be_return(vm);
|
|
}
|
|
}
|
|
|
|
/*********************************************************************************************\
|
|
* 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(65)
|
|
// 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(65)) -> 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);
|
|
}
|
|
|
|
// crypto.EC_P256().ecdsa_sign_sha256(my_private_key:bytes(32), message:bytes()) -> bytes(64)
|
|
// Sign with ECDSA SHA256
|
|
int32_t m_ec_p256_ecdsa_sign_sha256(bvm *vm);
|
|
int32_t m_ec_p256_ecdsa_sign_sha256(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;
|
|
uint8_t * sk = (uint8_t*) be_tobytes(vm, 1, &sk_len);
|
|
size_t msg_len = 0;
|
|
const uint8_t * msg = (const uint8_t*) be_tobytes(vm, 2, &msg_len);
|
|
if (sk_len != 32) {
|
|
be_raise(vm, "value_error", "Key size invalid");
|
|
}
|
|
|
|
// first compute SHA-256 hash on the message
|
|
uint8_t hash[32];
|
|
br_sha256_context ctx;
|
|
br_sha256_init(&ctx);
|
|
br_sha256_update(&ctx, msg, msg_len);
|
|
br_sha256_out(&ctx, hash);
|
|
|
|
// run ECDSA on hash
|
|
uint8_t sign[64]; // hard limit for ECDSA SHA256
|
|
br_ec_private_key br_sk = { BR_EC_secp256r1, sk, 32 };
|
|
size_t sign_len = br_ecdsa_i15_sign_raw(&BR_EC_P256_IMPL, &br_sha256_vtable, hash, &br_sk, sign);
|
|
|
|
be_pushbytes(vm, sign, sign_len);
|
|
be_return(vm);
|
|
}
|
|
be_raise(vm, kTypeError, nullptr);
|
|
}
|
|
|
|
// `crypto.EC_P256().ecdsa_verify_sha256(public_key:bytes(65), message:bytes(), hash:bytes()) -> bool`
|
|
// Verify signature with ECDSA SHA256
|
|
int32_t m_ec_p256_ecdsa_verify_sha256(bvm *vm);
|
|
int32_t m_ec_p256_ecdsa_verify_sha256(bvm *vm) {
|
|
int32_t argc = be_top(vm); // Get the number of arguments
|
|
if (argc >= 3 && be_isbytes(vm, 1) && be_isbytes(vm, 2) && be_isbytes(vm, 3)) {
|
|
size_t pk_len = 0;
|
|
uint8_t * pk = (uint8_t*) be_tobytes(vm, 1, &pk_len);
|
|
size_t msg_len = 0;
|
|
const uint8_t * msg = (const uint8_t*) be_tobytes(vm, 2, &msg_len);
|
|
if (pk_len != 65) {
|
|
be_raise(vm, "value_error", "Key size invalid");
|
|
}
|
|
size_t sig_len = 0;
|
|
const uint8_t * sig = (const uint8_t*) be_tobytes(vm, 3, &sig_len);
|
|
|
|
// first compute SHA-256 hash on the message
|
|
uint8_t hash[32];
|
|
br_sha256_context ctx;
|
|
br_sha256_init(&ctx);
|
|
br_sha256_update(&ctx, msg, msg_len);
|
|
br_sha256_out(&ctx, hash);
|
|
|
|
// run ECDSA on hash
|
|
br_ec_public_key br_pk = { BR_EC_secp256r1, pk, pk_len };
|
|
uint32_t ret = br_ecdsa_i15_vrfy_raw(&BR_EC_P256_IMPL, hash, sizeof(hash), &br_pk, sig, sig_len);
|
|
|
|
be_pushbool(vm, ret);
|
|
be_return(vm);
|
|
}
|
|
be_raise(vm, kTypeError, nullptr);
|
|
}
|
|
|
|
// crypto.EC_P256().ecdsa_sign_sha256_asn1(my_private_key:bytes(32), message:bytes()) -> bytes()
|
|
// Sign with ECDSA SHA256, result in ASN.1 format for CSR and certificate
|
|
int32_t m_ec_p256_ecdsa_sign_sha256_asn1(bvm *vm);
|
|
int32_t m_ec_p256_ecdsa_sign_sha256_asn1(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;
|
|
uint8_t * sk = (uint8_t*) be_tobytes(vm, 1, &sk_len);
|
|
size_t msg_len = 0;
|
|
const uint8_t * msg = (const uint8_t*) be_tobytes(vm, 2, &msg_len);
|
|
if (sk_len != 32) {
|
|
be_raise(vm, "value_error", "Key size invalid");
|
|
}
|
|
|
|
// first compute SHA-256 hash on the message
|
|
uint8_t hash[32];
|
|
br_sha256_context ctx;
|
|
br_sha256_init(&ctx);
|
|
br_sha256_update(&ctx, msg, msg_len);
|
|
br_sha256_out(&ctx, hash);
|
|
|
|
// run ECDSA on hash
|
|
uint8_t sign[72]; // hard limit for ECDSA SHA256 ASN.1 as per bearssl documentation
|
|
br_ec_private_key br_sk = { BR_EC_secp256r1, sk, 32 };
|
|
size_t sign_len = br_ecdsa_i15_sign_asn1(&BR_EC_P256_IMPL, &br_sha256_vtable, hash, &br_sk, sign);
|
|
|
|
be_pushbytes(vm, sign, sign_len);
|
|
be_return(vm);
|
|
}
|
|
be_raise(vm, kTypeError, nullptr);
|
|
}
|
|
|
|
// `crypto.EC_P256().ecdsa_verify_sha256_asn1(public_key:bytes(65), message:bytes(), hash:bytes()) -> bool`
|
|
// Verify signature with ECDSA SHA256 with signature in ASN.1 format
|
|
int32_t m_ec_p256_ecdsa_verify_sha256_asn1(bvm *vm);
|
|
int32_t m_ec_p256_ecdsa_verify_sha256_asn1(bvm *vm) {
|
|
int32_t argc = be_top(vm); // Get the number of arguments
|
|
if (argc >= 3 && be_isbytes(vm, 1) && be_isbytes(vm, 2) && be_isbytes(vm, 3)) {
|
|
size_t pk_len = 0;
|
|
uint8_t * pk = (uint8_t*) be_tobytes(vm, 1, &pk_len);
|
|
size_t msg_len = 0;
|
|
const uint8_t * msg = (const uint8_t*) be_tobytes(vm, 2, &msg_len);
|
|
if (pk_len != 65) {
|
|
be_raise(vm, "value_error", "Key size invalid");
|
|
}
|
|
size_t sig_len = 0;
|
|
const uint8_t * sig = (const uint8_t*) be_tobytes(vm, 3, &sig_len);
|
|
|
|
// first compute SHA-256 hash on the message
|
|
uint8_t hash[32];
|
|
br_sha256_context ctx;
|
|
br_sha256_init(&ctx);
|
|
br_sha256_update(&ctx, msg, msg_len);
|
|
br_sha256_out(&ctx, hash);
|
|
|
|
// run ECDSA on hash
|
|
br_ec_public_key br_pk = { BR_EC_secp256r1, pk, pk_len };
|
|
uint32_t ret = br_ecdsa_i15_vrfy_asn1(&BR_EC_P256_IMPL, hash, sizeof(hash), &br_pk, sig, sig_len);
|
|
|
|
be_pushbool(vm, ret);
|
|
be_return(vm);
|
|
}
|
|
be_raise(vm, kTypeError, nullptr);
|
|
}
|
|
/* Test values
|
|
import crypto
|
|
var priv = bytes('D42A43989B67211031FF194FBA791B5C3E03F9EC10ED561A4DEB2AA7BADB4772')
|
|
# var priv = crypto.random(32)
|
|
var pub = crypto.EC_P256().public_key(priv)
|
|
var msg = bytes().fromstring("Tasmota crypto ECDSA SECP256R1 SHA256 test message")
|
|
|
|
var sig = crypto.EC_P256().ecdsa_sign_sha256(priv, msg)
|
|
var ok = crypto.EC_P256().ecdsa_verify_sha256(pub, msg, sig)
|
|
assert(ok == true)
|
|
*/
|
|
|
|
// 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')
|
|
// s = ''
|
|
// while size(N) > 0
|
|
// var n = N.get(0, 2)
|
|
// s += 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);
|
|
}
|
|
|
|
// 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);
|
|
}
|
|
}
|
|
|
|
/*********************************************************************************************\
|
|
* EC C25519 class
|
|
*
|
|
\*********************************************************************************************/
|
|
#define BR_EC25519_IMPL br_ec_c25519_m15 // BearSSL implementation for Curve 25519
|
|
|
|
extern "C" {
|
|
// crypto.EC_C25519().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_c25519_pubkey(bvm *vm);
|
|
int32_t m_ec_c25519_pubkey(bvm *vm) {
|
|
int32_t argc = be_top(vm); // Get the number of arguments
|
|
if (argc >= 2 && be_isbytes(vm, 2)) {
|
|
size_t buf_len = 0;
|
|
const uint8_t * buf = (const uint8_t*) be_tobytes(vm, 2, &buf_len);
|
|
if (buf_len == 32) {
|
|
/* create the private key structure */
|
|
uint8_t sk[32];
|
|
for (int32_t i=0; i<32; i++) {
|
|
sk[i] = buf[31-i];
|
|
}
|
|
br_ec_private_key br_sk = { BR_EC_curve25519, sk, 32 };
|
|
|
|
uint8_t pk_buf[32]; /* EC 25519 is 32 bytes */
|
|
size_t ret = br_ec_compute_pub(&BR_EC25519_IMPL, nullptr, pk_buf, &br_sk);
|
|
if (ret == 32) {
|
|
be_pushbytes(vm, pk_buf, ret);
|
|
be_return(vm);
|
|
}
|
|
}
|
|
be_raise(vm, "value_error", "invalid input");
|
|
}
|
|
be_raise(vm, kTypeError, nullptr);
|
|
}
|
|
|
|
// 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) {
|
|
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;
|
|
const uint8_t * sk_buf = (const uint8_t*) be_tobytes(vm, 2, &sk_len);
|
|
uint8_t sk[32];
|
|
for (int32_t i=0; i<32; i++) {
|
|
sk[i] = sk_buf[31-i];
|
|
}
|
|
size_t pk_len = 0;
|
|
const uint8_t * pk_const = (const uint8_t*) be_tobytes(vm, 3, &pk_len);
|
|
uint8_t pk[32];
|
|
memmove(pk, pk_const, sizeof(pk)); /* copy to a non-const variable to receive the result */
|
|
|
|
if (sk_len == 32 && pk_len == 32) {
|
|
if (BR_EC25519_IMPL.mul(pk, pk_len, sk, sk_len, BR_EC_curve25519)) {
|
|
/* return value (xoff is zero so no offset) */
|
|
be_pushbytes(vm, pk, pk_len);
|
|
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);
|
|
}
|
|
}
|
|
|
|
/*********************************************************************************************\
|
|
* HKDF_SHA256
|
|
*
|
|
\*********************************************************************************************/
|
|
extern "C" {
|
|
// crypto.HKDF_SHA256().derive(ikm:bytes(), salt:bytes(), info:bytes(), out_bytes:int) -> bytes(out_bytes)
|
|
// Derive key with HKDF based on SHA256
|
|
int32_t m_hkdf_sha256_derive(bvm *vm);
|
|
int32_t m_hkdf_sha256_derive(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_isint(vm, 4)) {
|
|
size_t ikm_len;
|
|
const void * ikm = be_tobytes(vm, 1, &ikm_len);
|
|
if (ikm_len == 0) { be_raise(vm, "value_error", "ikm must not be empty"); }
|
|
|
|
size_t salt_len;
|
|
const void * salt = be_tobytes(vm, 2, &salt_len);
|
|
if (salt_len == 0) { salt = &br_hkdf_no_salt; }
|
|
|
|
size_t info_len;
|
|
const void * info = be_tobytes(vm, 3, &info_len);
|
|
|
|
int32_t out_bytes = be_toint(vm, 4);
|
|
if (out_bytes < 1 || out_bytes > 256) { be_raise(vm, "value_error", "invalid out_bytes"); }
|
|
|
|
br_hkdf_context hc;
|
|
br_hkdf_init(&hc, &br_sha256_vtable, salt, salt_len);
|
|
br_hkdf_inject(&hc, ikm, ikm_len);
|
|
br_hkdf_flip(&hc);
|
|
uint8_t out[out_bytes];
|
|
br_hkdf_produce(&hc, info, info_len, out, out_bytes);
|
|
|
|
be_pushbytes(vm, out, out_bytes);
|
|
be_return(vm);
|
|
}
|
|
be_raise(vm, kTypeError, nullptr);
|
|
}
|
|
/* Test vectors
|
|
# https://www.rfc-editor.org/rfc/rfc5869
|
|
|
|
import crypto
|
|
|
|
# Test Case 1
|
|
hk = crypto.HKDF_SHA256()
|
|
ikm = bytes("0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B")
|
|
salt = bytes("000102030405060708090A0B0C")
|
|
info = bytes("F0F1F2F3F4F5F6F7F8F9")
|
|
k = hk.derive(ikm, salt, info, 42)
|
|
assert(k == bytes("3CB25F25FAACD57A90434F64D0362F2A2D2D0A90CF1A5A4C5DB02D56ECC4C5BF34007208D5B887185865"))
|
|
|
|
# Test Case 2
|
|
hk = crypto.HKDF_SHA256()
|
|
ikm = bytes("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f")
|
|
salt = bytes("606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeaf")
|
|
info = bytes("b0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff")
|
|
k = hk.derive(ikm, salt, info, 82)
|
|
assert(k == bytes("b11e398dc80327a1c8e7f78c596a49344f012eda2d4efad8a050cc4c19afa97c59045a99cac7827271cb41c65e590e09da3275600c2f09b8367793a9aca3db71cc30c58179ec3e87c14c01d5c1f3434f1d87"))
|
|
|
|
# Test Case 3
|
|
hk = crypto.HKDF_SHA256()
|
|
ikm = bytes("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b")
|
|
salt = bytes()
|
|
info = bytes()
|
|
k = hk.derive(ikm, salt, info, 42)
|
|
assert(k == bytes("8da4e775a563c18f715f802a063c5a31b8a11f5c5ee1879ec3454e5f3c738d2d9d201395faa4b61a96c8"))
|
|
|
|
*/
|
|
}
|
|
|
|
/*********************************************************************************************\
|
|
* 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
|