-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add diffie-hellman key exchange algorithm (#13)
* Add diffie hellman * Add for tests for node * Drop node 8 from CI * Complete DH tests * Update README * Bump version * Diffie-hellman: fix web and node. Added the setting of the pem format 'OPENSSL_EC_NAMED_CURVE' in cpp bindings because its default value may differs in different openssl form. Cpp web part has been tested in a pure cpp form (without emscripten), compiled with clang having sanitizer enabled in orderd to detect leaks and all leaks has been fixed. Node part needed some fixes in order to communicate in the same manner of the web part. Giving the absence in crypto-node of export functions of ecdh generated keys in pem uncompressed curved name format, I had to implement this export functions. Note: conversion from string to base64 ignores line breaks, so no need to remove them. Fixed node tests with new implementation. Diffie-Hellman key exchange between node and web part has been tested successfully. Changed the format secret returned from sha256 buffer format to hex string. Edited the README.md Minor style fixes. Co-authored-by: Alessio Paccoia <[email protected]>
- Loading branch information
1 parent
906e81b
commit b2be552
Showing
12 changed files
with
414 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,207 @@ | ||
#include <emscripten.h> | ||
#include <emscripten/bind.h> | ||
#include <emscripten/val.h> | ||
|
||
#include <sstream> | ||
#include <iostream> | ||
#include <iomanip> | ||
|
||
#include <openssl/evp.h> | ||
#include <openssl/ec.h> | ||
#include <openssl/pem.h> | ||
#include <openssl/rand.h> | ||
#include <openssl/err.h> | ||
#include <openssl/sha.h> | ||
|
||
using namespace emscripten; | ||
|
||
#define ERROR_NULL(VAR, FUN, ERR_VAR, MESSAGE) \ | ||
if((ERR_VAR).empty()) \ | ||
{ \ | ||
VAR = FUN; \ | ||
if(VAR == nullptr) \ | ||
ERR_VAR = #MESSAGE; \ | ||
} | ||
|
||
#define ERROR_EVP(FUN, ERR_VAR, MESSAGE) \ | ||
{ \ | ||
if((ERR_VAR).empty()) \ | ||
{ \ | ||
size_t a = (size_t)FUN; \ | ||
if(a != 1) \ | ||
ERR_VAR = #MESSAGE; \ | ||
} \ | ||
} | ||
|
||
class DiffieHellman | ||
{ | ||
private: | ||
|
||
EVP_PKEY* _private_key; | ||
|
||
public: | ||
|
||
DiffieHellman() : _private_key(nullptr) | ||
{} | ||
|
||
void free() | ||
{ | ||
EVP_PKEY_free(this->_private_key); | ||
} | ||
|
||
inline val _error_js(std::string error_message) | ||
{ | ||
auto error = val::object(); | ||
error.set("error", error_message); | ||
return error; | ||
} | ||
|
||
inline val _value_js(std::string value = "") | ||
{ | ||
auto value_js = val::object(); | ||
|
||
if(value == "") | ||
value_js.set("value", val::undefined()); | ||
else | ||
value_js.set("value", value); | ||
|
||
return value_js; | ||
} | ||
|
||
val initialize() | ||
{ | ||
std::string error_message; | ||
|
||
EVP_PKEY_CTX* parameters_context = nullptr; | ||
EVP_PKEY_CTX* key_generation_context = nullptr; | ||
EVP_PKEY* params = nullptr; | ||
this->_private_key = nullptr; | ||
|
||
ERROR_NULL(parameters_context, EVP_PKEY_CTX_new_id(EVP_PKEY_EC, nullptr), error_message, "No context for parameter generation detected"); | ||
|
||
ERROR_EVP(EVP_PKEY_paramgen_init(parameters_context), error_message, "Unable to initialize parameters generation"); | ||
|
||
ERROR_EVP(EVP_PKEY_CTX_set_ec_paramgen_curve_nid(parameters_context, NID_X9_62_prime256v1), error_message, "Unable to set the curve for parameters generation"); | ||
ERROR_EVP(EVP_PKEY_CTX_set_ec_param_enc(parameters_context, OPENSSL_EC_NAMED_CURVE), error_message, "Unable to set the curve for parameters generation"); | ||
ERROR_EVP(EVP_PKEY_paramgen(parameters_context, ¶ms), error_message, "Unable to generate parameters"); | ||
|
||
ERROR_NULL(key_generation_context, EVP_PKEY_CTX_new(params, nullptr), error_message, "Unable to create context for key generation"); | ||
ERROR_EVP(EVP_PKEY_keygen_init(key_generation_context), error_message, "Unable to init context for key generation"); | ||
ERROR_EVP(EVP_PKEY_keygen(key_generation_context, &this->_private_key), error_message, "Unable to generate private key"); | ||
|
||
EVP_PKEY_CTX_free(parameters_context); | ||
EVP_PKEY_CTX_free(key_generation_context); | ||
EVP_PKEY_free(params); | ||
|
||
if(!error_message.empty()) | ||
return this->_error_js(error_message); | ||
|
||
return this->_value_js(); | ||
} | ||
|
||
val get_public_key() | ||
{ | ||
if(this->_private_key == nullptr) | ||
return this->_error_js("not initialized"); | ||
|
||
std::string public_key_str; | ||
std::string error_message; | ||
|
||
{ | ||
BIO* bio_out; | ||
BUF_MEM* bio_out_buffer = nullptr; | ||
bio_out = BIO_new(BIO_s_mem()); | ||
|
||
ERROR_EVP(PEM_write_bio_PUBKEY(bio_out, this->_private_key), error_message, "Unable to write public key to memory"); | ||
|
||
if(error_message.empty()) | ||
{ | ||
BIO_get_mem_ptr(bio_out, &bio_out_buffer); | ||
|
||
public_key_str.resize(bio_out_buffer->length); | ||
|
||
memcpy((void*)public_key_str.data(), bio_out_buffer->data, bio_out_buffer->length); | ||
} | ||
|
||
BIO_free_all(bio_out); | ||
} | ||
|
||
if(!error_message.empty()) | ||
return this->_error_js(error_message); | ||
|
||
return this->_value_js(public_key_str); | ||
} | ||
|
||
std::string uint8_to_hex_string(std::vector<uint8_t> v) | ||
{ | ||
std::ostringstream ss; | ||
ss << std::hex << std::setfill( '0' ); | ||
std::for_each( v.cbegin(), v.cend(), [&]( int c ) { ss << std::setw( 2 ) << c; } ); | ||
return ss.str(); | ||
} | ||
|
||
val derive_secret(std::string endpoint_public_key) | ||
{ | ||
if(this->_private_key == nullptr) | ||
return this->_error_js("not initialized"); | ||
|
||
size_t secret_length; | ||
std::vector<uint8_t> secret_vec; | ||
std::string error_message; | ||
|
||
EVP_PKEY* endpoint_public_key_evp = nullptr; | ||
EVP_PKEY_CTX* derivation_context = nullptr; | ||
|
||
{ | ||
BIO* bio; | ||
BUF_MEM* bio_buffer; | ||
|
||
bio = BIO_new(BIO_s_mem()); | ||
bio_buffer = BUF_MEM_new(); | ||
|
||
BUF_MEM_grow(bio_buffer, endpoint_public_key.size()); | ||
|
||
memcpy(bio_buffer->data, (unsigned char*) endpoint_public_key.data(), endpoint_public_key.size()); | ||
|
||
BIO_set_mem_buf(bio, bio_buffer, BIO_NOCLOSE); | ||
|
||
ERROR_NULL(endpoint_public_key_evp, PEM_read_bio_PUBKEY(bio, nullptr, nullptr, nullptr), error_message, "Unable to write public key to memory"); | ||
|
||
BIO_free_all(bio); | ||
BUF_MEM_free(bio_buffer); | ||
|
||
if(!error_message.empty()) | ||
return this->_error_js(error_message); | ||
} | ||
|
||
ERROR_NULL(derivation_context, EVP_PKEY_CTX_new(this->_private_key, nullptr), error_message, "Unable to create shared secret context"); | ||
|
||
ERROR_EVP(EVP_PKEY_derive_init(derivation_context), error_message, "Unable to initialize shared secret context"); | ||
ERROR_EVP(EVP_PKEY_derive_set_peer(derivation_context, endpoint_public_key_evp), error_message, "Unable to set peer public key"); | ||
|
||
ERROR_EVP(EVP_PKEY_derive(derivation_context, nullptr, &secret_length), error_message, "Error while trying to derive secret length"); | ||
|
||
if(error_message.empty()) | ||
secret_vec.resize(secret_length); | ||
|
||
ERROR_EVP(EVP_PKEY_derive(derivation_context, (unsigned char*) secret_vec.data(), &secret_length), error_message, "Could not dervive the shared secret"); | ||
|
||
EVP_PKEY_CTX_free(derivation_context); | ||
EVP_PKEY_free(endpoint_public_key_evp); | ||
|
||
if(!error_message.empty()) | ||
return this->_error_js(error_message); | ||
|
||
return this->_value_js(this->uint8_to_hex_string(secret_vec)); | ||
} | ||
}; | ||
|
||
EMSCRIPTEN_BINDINGS(DiffieHellman) | ||
{ | ||
class_<DiffieHellman>("DiffieHellman") | ||
.constructor() | ||
.function("free", &DiffieHellman::free) | ||
.function("initialize", &DiffieHellman::initialize) | ||
.function("get_public_key", &DiffieHellman::get_public_key) | ||
.function("derive_secret", &DiffieHellman::derive_secret); | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
import {createECDH, ECDH} from 'crypto'; | ||
|
||
export class DiffieHellman | ||
{ | ||
private readonly _prime256v1_uncompressed_curved_name_pem_header_hex = '3059301306072a8648ce3d020106082a8648ce3d030107034200'; | ||
private _public_key_pem?: string; | ||
private _diffie_hellman: ECDH; | ||
|
||
public constructor() | ||
{ | ||
this._diffie_hellman = createECDH('prime256v1'); | ||
} | ||
|
||
public initialize(): void | ||
{ | ||
const public_key_hex = this._diffie_hellman.generateKeys('hex'); | ||
|
||
const hex = Buffer.from(this._prime256v1_uncompressed_curved_name_pem_header_hex + public_key_hex, 'hex').toString('base64'); | ||
|
||
this._public_key_pem = `-----BEGIN PUBLIC KEY-----\n${hex}\n-----END PUBLIC KEY-----`; | ||
} | ||
|
||
public get_public_key(): string | ||
{ | ||
if(!this._public_key_pem) | ||
throw new Error('DiffieHellman not initialized'); | ||
|
||
return this._public_key_pem; | ||
} | ||
|
||
public async derive_secret(endpoint_public_key_pem: string): Promise<string> | ||
{ | ||
if(!this._public_key_pem) | ||
throw new Error('DiffieHellman not initialized'); | ||
|
||
const endpoint_public_key = endpoint_public_key_pem.replace('-----BEGIN PUBLIC KEY-----', '').replace('-----END PUBLIC KEY-----', ''); | ||
|
||
const endpoint_public_key_hex = Buffer.from(endpoint_public_key, 'base64').toString('hex').substr(this._prime256v1_uncompressed_curved_name_pem_header_hex.length); | ||
|
||
return this._diffie_hellman.computeSecret(endpoint_public_key_hex, 'hex', 'hex'); | ||
} | ||
|
||
public free(): void | ||
{ | ||
this._public_key_pem = ''; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.