Skip to content

Instantly share code, notes, and snippets.

@bmatusiak
Created March 7, 2022 16:13
Show Gist options
  • Save bmatusiak/a1ac4b31cf5fd4c2c4ac2a9b0fd377ff to your computer and use it in GitHub Desktop.
Save bmatusiak/a1ac4b31cf5fd4c2c4ac2a9b0fd377ff to your computer and use it in GitHub Desktop.
#ifdef USE_ECDH
#include "mbedtls/include/mbedtls/ecdh.h"
static NO_INLINE JsVar *jswrap_crypto_ecdh(JsVar *message, JsVar *key, JsVar *options, bool genSecret ) {
int ret;
JSV_GET_AS_CHAR_ARRAY(msgPtr, msgLen, message);
if (!msgPtr) return 0;
mbedtls_ecdh_context ecdh_ctx;
mbedtls_ecdh_init( &ecdh_ctx );
mbedtls_ecp_group_id curve_id = MBEDTLS_ECP_DP_SECP256R1;
unsigned int is_CURVE25519 = 0;
if (jsvIsObject(options)) {
//curve option
JsVar *curveVar = jsvObjectGetChild(options, "curve", 0);
if (!jsvIsUndefined(curveVar) && jsvIsString(curveVar)){
if(jsvIsStringEqual(curveVar, "curve25519")){
curve_id = MBEDTLS_ECP_DP_CURVE25519;
jsvUnLock(curveVar);
is_CURVE25519 = 1;
}else{
size_t curveLen = (jsvGetStringLength(curveVar) + 1);
char curve[curveLen];
jsvGetString( curveVar, curve , curveLen);
jsvUnLock(curveVar);
const mbedtls_ecp_curve_info *curve_info = mbedtls_ecp_curve_info_from_name( (const char *)curve );
if (curve_info == NULL){
mbedtls_ecdh_free( &ecdh_ctx );
jsError( " failed\n ! mbedtls_ecp_curve_info_from_name returned NULL" );
return 0;
}
curve_id = curve_info->grp_id;
}
}
} else if (!jsvIsUndefined(options)) {
mbedtls_ecdh_free( &ecdh_ctx );
jsError("'options' must be undefined, or an Object");
return 0;
}
ret = mbedtls_ecp_group_load( &ecdh_ctx.grp, curve_id );
if( ret != 0 )
{
mbedtls_ecdh_free( &ecdh_ctx );
jsError( " failed\n ! mbedtls_ecp_group_load returned %d\n", ret );
return 0;
}
//build private key from sha256 hash
unsigned char d[32];
mbedtls_sha256((unsigned char *)msgPtr, msgLen, (unsigned char *)d, false/*256*/);
mbedtls_mpi_read_binary(&ecdh_ctx.d, d, sizeof(d));
if(is_CURVE25519 == 1){
size_t b;
mbedtls_mpi *dd = &ecdh_ctx.d;
mbedtls_ecp_group *d_grp = &ecdh_ctx.grp;
/* Make sure the most significant bit is nbits */
b = mbedtls_mpi_bitlen( dd ) - 1; /* mbedtls_mpi_bitlen is one-based */
if( b > d_grp->nbits )
mbedtls_mpi_shift_r( dd, b - d_grp->nbits );
else
mbedtls_mpi_set_bit( dd, d_grp->nbits, 1 );
/* Make sure the last three bits are unset */
mbedtls_mpi_set_bit( dd, 0, 0 );
mbedtls_mpi_set_bit( dd, 1, 0 );
mbedtls_mpi_set_bit( dd, 2, 0 );
}
ret = mbedtls_ecp_check_privkey(&ecdh_ctx.grp, &ecdh_ctx.d);
if( ret != 0 )
{
mbedtls_ecdh_free( &ecdh_ctx );
jsError( " failed\n ! mbedtls_ecp_check_privkey returned %d\n", ret );
return 0;
}
ret = mbedtls_ecp_mul( &ecdh_ctx.grp, &ecdh_ctx.Q, &ecdh_ctx.d, &(ecdh_ctx.grp.G), NULL, NULL );
if( ret != 0 )
{
mbedtls_ecdh_free( &ecdh_ctx );
jsError( " failed\n ! mbedtls_ecp_mul returned %d\n", ret );
return 0;
}
ret = mbedtls_ecp_check_pub_priv((const mbedtls_ecp_keypair *)&ecdh_ctx, (const mbedtls_ecp_keypair *)&ecdh_ctx);
if( ret != 0 )
{
mbedtls_ecdh_free( &ecdh_ctx );
jsError( " failed\n ! mbedtls_ecp_check_pub_priv returned %d\n", ret );
return 0;
}
int bufferSize = 32;
char *outPtr = 0;
JsVar *outArr;
if(!genSecret){
if(is_CURVE25519 == 0)
bufferSize = 64;
outArr = jsvNewArrayBufferWithPtr((unsigned int)bufferSize, &outPtr);
if (!outPtr) {
jsError("Not enough memory for result");
return 0;
}
// outPtr[0] = 4;
ret = mbedtls_mpi_write_binary( &ecdh_ctx.Q.X, (unsigned char *)&outPtr[0], 32 );
if( ret != 0 )
{
mbedtls_ecdh_free( &ecdh_ctx );
jsvUnLock(outArr);
jsError( " failed\n ! mbedtls_mpi_write_binary X returned %d\n", ret );
return 0;
}
if( is_CURVE25519 == 0){
ret = mbedtls_mpi_write_binary( &ecdh_ctx.Q.Y, (unsigned char *)&outPtr[32], 32 );
if( ret != 0 )
{
mbedtls_ecdh_free( &ecdh_ctx );
jsvUnLock(outArr);
jsError( " failed\n ! mbedtls_mpi_write_binary Y returned %d\n", ret );
return 0;
}
}
}else{
bufferSize = 32;
outArr = jsvNewArrayBufferWithPtr((unsigned int)bufferSize, &outPtr);
if (!outPtr) {
jsError("Not enough memory for result");
return 0;
}
JSV_GET_AS_CHAR_ARRAY(keyPtr, keyLen, key);
if (!keyPtr) {
mbedtls_ecdh_free( &ecdh_ctx );
return 0;
}
ret = mbedtls_mpi_lset( &ecdh_ctx.Qp.Z, 1 );
if( ret != 0 )
{
mbedtls_ecdh_free( &ecdh_ctx );
jsvUnLock(outArr);
jsError( " failed\n ! mbedtls_mpi_lset returned %d\n", ret );
return 0;
}
ret = mbedtls_mpi_read_binary( &ecdh_ctx.Qp.X, (unsigned char *)&keyPtr[0], 32 );
if( ret != 0 )
{
mbedtls_ecdh_free( &ecdh_ctx );
jsvUnLock(outArr);
jsError( " failed\n ! mbedtls_mpi_read_binary returned %d\n", ret );
return 0;
}
if( is_CURVE25519 == 0){
ret = mbedtls_mpi_read_binary( &ecdh_ctx.Qp.Y, (unsigned char *)&keyPtr[32], 32 );
if( ret != 0 )
{
mbedtls_ecdh_free( &ecdh_ctx );
jsvUnLock(outArr);
jsError( " failed\n ! mbedtls_mpi_read_binary returned %d\n", ret );
return 0;
}
}
ret = mbedtls_ecdh_compute_shared( &ecdh_ctx.grp, &ecdh_ctx.z,
&ecdh_ctx.Qp, &ecdh_ctx.d,
NULL, NULL );
if( ret != 0 )
{
mbedtls_ecdh_free( &ecdh_ctx );
jsvUnLock(outArr);
jsError( " failed\n ! mbedtls_ecdh_compute_shared returned %d\n", ret );
return 0;
}
ret = mbedtls_mpi_write_binary( &ecdh_ctx.z, (unsigned char *)outPtr, 32 );
if( ret != 0 )
{
mbedtls_ecdh_free( &ecdh_ctx );
jsvUnLock(outArr);
jsError( " failed\n ! mbedtls_mpi_write_binary X returned %d\n", ret );
return 0;
}
}
mbedtls_ecdh_free( &ecdh_ctx );
return outArr;
}
/*JSON{
"type" : "class",
"library" : "crypto",
"class" : "ECDH",
"ifdef" : "USE_ECDH"
}
Class containing ECDH
**Note:** This library is currently only included in builds for boards where there is space. For other boards there is `crypto.js` which implements SHA1 in JS.
*/
/*JSON{
"type" : "staticproperty",
"class" : "crypto",
"name" : "ECDH",
"generate_full" : "jspNewBuiltin(\"ECDH\");",
"return" : ["JsVar"],
"return_object" : "ECDH",
"ifdef" : "USE_ECDH"
}
Class containing ECDH
*/
/*JSON{
"type" : "staticmethod",
"class" : "ECDH",
"name" : "genpublic",
"generate" : "jswrap_crypto_ecdh_genpublic",
"params" : [
["message","JsVar","Message to genpublic"],
["options","JsVar","options for ecdh"]
],
"return" : ["JsVar","Returns an ArrayBuffer"],
"return_object" : "ArrayBuffer",
"ifdef" : "USE_ECDH"
}
*/
JsVar *jswrap_crypto_ecdh_genpublic(JsVar *message, JsVar *options) {
return jswrap_crypto_ecdh(message, NULL, options, false);
}
/*JSON{
"type" : "staticmethod",
"class" : "ECDH",
"name" : "gensecret",
"generate" : "jswrap_crypto_ecdh_gensecret",
"params" : [
["message","JsVar","Message to gensecret"],
["key","JsVar","key to gensecret"],
["options","JsVar","options for ecdh"]
],
"return" : ["JsVar","Returns an ArrayBuffer"],
"return_object" : "ArrayBuffer",
"ifdef" : "USE_ECDH"
}
*/
JsVar *jswrap_crypto_ecdh_gensecret(JsVar *message, JsVar *key, JsVar *options) {
return jswrap_crypto_ecdh(message, key,options, true);
}
#endif
#ifdef USE_ECDH
JsVar *jswrap_crypto_ecdh_genpublic(JsVar *message, JsVar *options);
JsVar *jswrap_crypto_ecdh_gensecret(JsVar *message, JsVar *key, JsVar *options);
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment