Newer
Older
/*!
* \file security_ecc.cc
* \brief Source file for Elliptic Curve Cryptography.
* \author ETSI STF525
* \copyright ETSI Copyright Notification
* No part may be reproduced except as authorized by written permission.
* The copyright and the foregoing restriction extend to reproduction in all media.
* All rights reserved.
* \version 0.1
*/
#include <TTCN3.hh>
security_ecc::security_ecc(const ec_elliptic_curves p_elliptic_curve): _elliptic_curve(p_elliptic_curve), _encryption_algotithm(encryption_algotithm::aes_128_ccm), _ec_key(nullptr), _ec_group(nullptr), _bn_ctx(nullptr), _pri_key(), _pub_key_x(), _pub_key_y(), _pub_key_compressed(), _pub_key_compressed_mode{ecc_compressed_mode::compressed_y_0}, _secret_key(), _enc_key_x(), _enc_key_y(), _sym_key(), _nonce(), _tag() {
loggers::get_instance().log(">>> security_ecc::security_ecc: %d", static_cast<int>(p_elliptic_curve));
const int result = init();
if (result == -1) {
loggers::get_instance().error("security_ecc::security_ecc: Unsupported elliptic_curve %d", _elliptic_curve);
security_ecc::security_ecc(const ec_elliptic_curves p_elliptic_curve, const OCTETSTRING& p_private_key): _elliptic_curve(p_elliptic_curve), _encryption_algotithm(encryption_algotithm::aes_128_ccm), _ec_key(nullptr), _ec_group(nullptr), _bn_ctx(nullptr), _pri_key(p_private_key), _pub_key_x(), _pub_key_y(), _pub_key_compressed(), _pub_key_compressed_mode{ecc_compressed_mode::compressed_y_0}, _secret_key(), _enc_key_x(), _enc_key_y(), _sym_key(), _nonce(), _tag() {
loggers::get_instance().log(">>> security_ecc::security_ecc (1): %d", static_cast<int>(p_elliptic_curve));
// Sanity checks
if ((_elliptic_curve == ec_elliptic_curves::nist_p_256) || (_elliptic_curve == ec_elliptic_curves::brainpool_p_256_r1)) {
if (p_private_key.lengthof() != 32) {
loggers::get_instance().error("security_ecc::security_ecc (1): Invalid public keys size");
}
} else if (_elliptic_curve == ec_elliptic_curves::brainpool_p_384_r1) {
if ((p_private_key.lengthof() != 48)) {
loggers::get_instance().error("security_ecc::security_ecc (1): Invalid public keys size");
loggers::get_instance().error("security_ecc::security_ecc (1): Unsupported elliptic_curve %d", _elliptic_curve);
::EC_KEY_set_conv_form(_ec_key, POINT_CONVERSION_UNCOMPRESSED);
BIGNUM* p = ::BN_new();
::BN_bin2bn(static_cast<const unsigned char*>(_pri_key), _pri_key.lengthof(), p);
// Build public keys
EC_POINT* ec_point = ::EC_POINT_new(_ec_group);
::EC_POINT_mul(_ec_group, ec_point, p, nullptr, nullptr, _bn_ctx);
::EC_KEY_set_private_key(_ec_key, p);
loggers::get_instance().error("security_ecc::security_ecc (1): Invalid private key");
::BN_clear_free(p);
p = nullptr;
// Private key is correct, set public keys
::EC_KEY_set_public_key(_ec_key, ec_point);
BIGNUM* xy = ::BN_new();
::EC_POINT_point2bn(_ec_group, ec_point, POINT_CONVERSION_UNCOMPRESSED, xy, _bn_ctx);
if (BN_num_bytes(xy) == 0) {
::BN_clear_free(xy);
loggers::get_instance().error("security_ecc::security_ecc (1): Failed to generate xy coordinates, check algorithms");
}
loggers::get_instance().log("security_ecc::security_ecc (1): xy length: %d", BN_num_bytes(xy));
OCTETSTRING v = int2oct(0, BN_num_bytes(xy));
::BN_bn2bin(xy, (unsigned char*)static_cast<const unsigned char*>(v));
if ((v.lengthof() % 2) != 0) {
// Remove first byte
loggers::get_instance().log_msg("security_ecc::security_ecc (1): Complete xy=", v);
v = OCTETSTRING(v.lengthof() - 1, 1 + static_cast<const unsigned char*>(v));
::BN_clear_free(xy);
xy = nullptr;
loggers::get_instance().log_msg("security_ecc::security_ecc (1): xy=", v);
const int l = v.lengthof() / 2;
_pub_key_x = OCTETSTRING(l, static_cast<const unsigned char*>(v));
_pub_key_y = OCTETSTRING(l, l + static_cast<const unsigned char*>(v));
int len = ::EC_POINT_point2oct(_ec_group, ec_point, POINT_CONVERSION_COMPRESSED, nullptr, 0, _bn_ctx);
if (len == 0) {
loggers::get_instance().warning("security_ecc::security_ecc (1): Failed to generate x_coordinate compressed key");
_pub_key_compressed = OCTETSTRING(0, nullptr);
} else {
_pub_key_compressed = int2oct(0, len);
if (::EC_POINT_point2oct(_ec_group, ec_point, POINT_CONVERSION_COMPRESSED, (unsigned char*)static_cast<const unsigned char*>(_pub_key_compressed), len, _bn_ctx) == 0) {
loggers::get_instance().warning("security_ecc::security_ecc (1): Failed to generate x_coordinate compressed key");
_pub_key_compressed = OCTETSTRING(0, nullptr);
} else { // Remove first byte
loggers::get_instance().log_msg("security_ecc::security_ecc (1): Complete _pub_key_compressed=", _pub_key_compressed);
_pub_key_compressed_mode = ((v[0].get_octet() & 0x01) == 0x00) ? ecc_compressed_mode::compressed_y_0 : ecc_compressed_mode::compressed_y_1;
_pub_key_compressed = OCTETSTRING(_pub_key_compressed.lengthof() - 1, 1 + static_cast<const unsigned char*>(_pub_key_compressed));
loggers::get_instance().log_msg("security_ecc::security_ecc (1): _pub_key_x=", _pub_key_x);
loggers::get_instance().log_msg("security_ecc::security_ecc (1): _pub_key_y=", _pub_key_y);
loggers::get_instance().log_msg("security_ecc::security_ecc (1): _pub_key_compressed=", _pub_key_compressed);
loggers::get_instance().log("security_ecc::security_ecc (1): _pub_key_compressed_mode=%d", _pub_key_compressed_mode);
security_ecc::security_ecc(const ec_elliptic_curves p_elliptic_curve, const OCTETSTRING& p_public_key_x, const OCTETSTRING& p_public_key_y): _elliptic_curve(p_elliptic_curve), _encryption_algotithm(encryption_algotithm::aes_128_ccm), _ec_key(nullptr), _ec_group(nullptr), _bn_ctx(nullptr), _pri_key(), _pub_key_x(p_public_key_x), _pub_key_y(p_public_key_y), _pub_key_compressed(), _pub_key_compressed_mode{ecc_compressed_mode::compressed_y_0}, _secret_key(), _enc_key_x(), _enc_key_y(), _sym_key(), _nonce(), _tag() {
loggers::get_instance().log(">>> security_ecc::security_ecc (2): %d", static_cast<int>(p_elliptic_curve));
// Sanity checks
if ((_elliptic_curve == ec_elliptic_curves::nist_p_256) || (_elliptic_curve == ec_elliptic_curves::brainpool_p_256_r1)) {
if ((p_public_key_x.lengthof() != 32) || (p_public_key_y.lengthof() != 32)) {
loggers::get_instance().error("security_ecc::security_ecc (2): Invalid public keys size");
}
} else if (_elliptic_curve == ec_elliptic_curves::brainpool_p_384_r1) {
if ((p_public_key_x.lengthof() != 48) || (p_public_key_y.lengthof() != 48)) {
loggers::get_instance().error("security_ecc::security_ecc (2): Invalid public keys size");
loggers::get_instance().error("security_ecc::security_ecc (2): Unsupported elliptic_curve %d", _elliptic_curve);
::EC_KEY_set_conv_form(_ec_key, POINT_CONVERSION_UNCOMPRESSED);
BIGNUM* x = ::BN_new();
::BN_bin2bn(static_cast<const unsigned char*>(_pub_key_x), _pub_key_x.lengthof(), x);
BIGNUM* y = ::BN_new();
::BN_bin2bn(static_cast<const unsigned char*>(_pub_key_y), _pub_key_y.lengthof(), y);
EC_POINT* ec_point = ::EC_POINT_new(_ec_group);
result = 0;
switch (_elliptic_curve) {
case ec_elliptic_curves::nist_p_256: // Use primary
// No break;
case ec_elliptic_curves::brainpool_p_256_r1:
// No break;
case ec_elliptic_curves::brainpool_p_384_r1:
result = ::EC_POINT_set_affine_coordinates_GFp(_ec_group, ec_point, x, y, _bn_ctx); // Use primary elliptic curve
result = ::EC_POINT_set_affine_coordinates_GF2m(_ec_group, ec_point, x, y, _bn_ctx);
::BN_clear_free(x);
::BN_clear_free(y);
loggers::get_instance().error("security_ecc::security_ecc (2): Failed to get coordinates");
::BN_clear_free(x); x = nullptr;
::BN_clear_free(y); y = nullptr;
int len = ::EC_POINT_point2oct(_ec_group, ec_point, POINT_CONVERSION_COMPRESSED, nullptr, 0, _bn_ctx);
if (len == 0) {
loggers::get_instance().warning("security_ecc::security_ecc (2): Failed to generate x_coordinate compressed key");
}
_pub_key_compressed = int2oct(0, len);
if (::EC_POINT_point2oct(_ec_group, ec_point, POINT_CONVERSION_COMPRESSED, (unsigned char *)static_cast<const unsigned char*>(_pub_key_compressed), len, _bn_ctx) == 0) {
loggers::get_instance().warning("security_ecc::security_ecc (2): Failed to generate x_coordinate compressed key");
_pub_key_compressed = OCTETSTRING(0, nullptr);
} else { // Remove first byte
loggers::get_instance().log_msg("security_ecc::security_ecc (2): Complete _pub_key_compressed=", _pub_key_compressed);
_pub_key_compressed = OCTETSTRING(_pub_key_compressed.lengthof() -1, 1 + static_cast<const unsigned char*>(_pub_key_compressed));
loggers::get_instance().log_msg("security_ecc::security_ecc (2): _pub_key_x=", _pub_key_x);
loggers::get_instance().log_msg("security_ecc::security_ecc (2): _pub_key_y=", _pub_key_y);
loggers::get_instance().log_msg("security_ecc::security_ecc (2): _pub_key_compressed=", _pub_key_compressed);
loggers::get_instance().log("security_ecc::security_ecc (2): _pub_key_compressed_mode=%d", _pub_key_compressed_mode);
} // End of constructor
security_ecc::security_ecc(const ec_elliptic_curves p_elliptic_curve, const OCTETSTRING& p_public_key_compressed, const ecc_compressed_mode p_compressed_mode): _elliptic_curve(p_elliptic_curve), _encryption_algotithm(encryption_algotithm::aes_128_ccm), _ec_key(nullptr), _ec_group(nullptr), _bn_ctx(nullptr), _pri_key(), _pub_key_x(), _pub_key_y(), _pub_key_compressed(p_public_key_compressed), _pub_key_compressed_mode{p_compressed_mode}, _secret_key(), _enc_key_x(), _enc_key_y(), _sym_key(), _nonce(), _tag() {
loggers::get_instance().log(">>> security_ecc::security_ecc (3): %d", static_cast<int>(p_elliptic_curve));
// Sanity checks
if ((_elliptic_curve == ec_elliptic_curves::nist_p_256) || (_elliptic_curve == ec_elliptic_curves::brainpool_p_256_r1)) {
if (p_public_key_compressed.lengthof() != 32) {
loggers::get_instance().error("security_ecc::security_ecc (3): Invalid public keys size");
}
} else if (_elliptic_curve == ec_elliptic_curves::brainpool_p_384_r1) {
if (p_public_key_compressed.lengthof() != 48) {
loggers::get_instance().error("security_ecc::security_ecc (3): Invalid public keys size");
}
}
int result = init();
if (result == -1) {
loggers::get_instance().error("security_ecc::security_ecc (3): Unsupported elliptic_curve %d", _elliptic_curve);
}
::EC_KEY_set_conv_form(_ec_key, POINT_CONVERSION_UNCOMPRESSED);
// Set public key
BIGNUM* compressed_key = ::BN_new();
::BN_bin2bn(static_cast<const unsigned char*>(_pub_key_compressed), _pub_key_compressed.lengthof(), compressed_key);
EC_POINT* ec_point = ::EC_POINT_new(_ec_group);
result = 0;
switch (_elliptic_curve) {
case ec_elliptic_curves::nist_p_256: // Use primary
// No break;
case ec_elliptic_curves::brainpool_p_256_r1:
// No break;
case ec_elliptic_curves::brainpool_p_384_r1:
result = ::EC_POINT_set_compressed_coordinates_GFp(_ec_group, ec_point, compressed_key, (p_compressed_mode == ecc_compressed_mode::compressed_y_1) ? 1 : 0, _bn_ctx); // Use primary elliptic curve
break;
default: // Use Binary
result = ::EC_POINT_set_compressed_coordinates_GF2m(_ec_group, ec_point, compressed_key, (p_compressed_mode == ecc_compressed_mode::compressed_y_1) ? 1 : 0, _bn_ctx);
} // End of 'switch' statement
BN_clear_free(compressed_key);
compressed_key = nullptr;
if (result == 0) {
loggers::get_instance().error("security_ecc::security_ecc (3): Failed to get coordinates");
} else if (::EC_POINT_is_on_curve(_ec_group, ec_point, _bn_ctx) == 0) {
loggers::get_instance().error("security_ecc::security_ecc (3): Point Y0 is not on the curve");
}
// Set public keys
BIGNUM* xy = ::BN_new();
::EC_POINT_point2bn(_ec_group, ec_point, POINT_CONVERSION_UNCOMPRESSED, xy, _bn_ctx);
if (BN_num_bytes(xy) == 0) {
::BN_clear_free(xy);
loggers::get_instance().error("security_ecc::security_ecc (3): Failed to generate xy coordinates, check algorithms");
}
loggers::get_instance().log("security_ecc::security_ecc (3): xy length: %d", BN_num_bytes(xy));
::EC_KEY_set_public_key(_ec_key, ec_point);
// Generate X, Y coordinates
OCTETSTRING v = int2oct(0, BN_num_bytes(xy));
::BN_bn2bin(xy, (unsigned char*)static_cast<const unsigned char*>(v));
::BN_clear_free(xy);
xy = nullptr;
if ((v.lengthof() % 2) != 0) { // TODO Check alse xy[0] == 0x04
// Remove first byte
loggers::get_instance().log_msg("security_ecc::security_ecc (3): Complete xy=", v);
v = OCTETSTRING(v.lengthof() -1, 1 + static_cast<const unsigned char*>(v));
}
loggers::get_instance().log_msg("security_ecc::security_ecc (3): xy=", v);
const int l = v.lengthof() / 2;
_pub_key_x = OCTETSTRING(l, static_cast<const unsigned char*>(v));
_pub_key_y = OCTETSTRING(l, l + static_cast<const unsigned char*>(v));
::EC_POINT_free(ec_point);
loggers::get_instance().log_msg("security_ecc::security_ecc (3): _pub_key_x=", _pub_key_x);
loggers::get_instance().log_msg("security_ecc::security_ecc (3): _pub_key_y=", _pub_key_y);
loggers::get_instance().log_msg("security_ecc::security_ecc (3): _pub_key_compressed=", _pub_key_compressed);
loggers::get_instance().log("security_ecc::security_ecc (3): _pub_key_compressed_mode=%d", _pub_key_compressed_mode);
security_ecc::~security_ecc() {
loggers::get_instance().log(">>> security_ecc::~security_ecc");
if(_ec_key != nullptr) {
::EC_KEY_free(_ec_key);
}
loggers::get_instance().log("<<< security_ecc::~security_ecc");
int security_ecc::generate() {
loggers::get_instance().log(">>> security_ecc::generate");
if (!::EC_KEY_generate_key(_ec_key)) { // Generate the private and public keys
loggers::get_instance().error("security_ecc::generate: Failed to generate private/public keys");
BIGNUM* x = ::BN_new();
BIGNUM* y = ::BN_new();
const EC_POINT* ec_point = EC_KEY_get0_public_key(_ec_key);
int result = 0;
int size = 0;
switch (_elliptic_curve) {
case ec_elliptic_curves::nist_p_256: // Use primary
// No break;
case ec_elliptic_curves::brainpool_p_256_r1:
size = 32;
result = ::EC_POINT_get_affine_coordinates_GFp(_ec_group, ec_point, x, y, _bn_ctx); // Use primer on elliptic curve
break;
case ec_elliptic_curves::brainpool_p_384_r1:
size = 48;
result = ::EC_POINT_get_affine_coordinates_GFp(_ec_group, ec_point, x, y, _bn_ctx); // Use primer on elliptic curve
break;
default: // Use binary
result = ::EC_POINT_get_affine_coordinates_GF2m(_ec_group, ec_point, x, y, _bn_ctx);
} // End of 'switch' statement
if (result == 0) {
loggers::get_instance().error("security_ecc::generate: Failed to get coordinates");
}
const BIGNUM* p = ::EC_KEY_get0_private_key(_ec_key);
_pri_key = int2oct(0, size);
::BN_bn2bin(p, (unsigned char*)static_cast<const unsigned char*>(_pri_key));
_pub_key_x = int2oct(0, size);
::BN_bn2bin(x, (unsigned char*)static_cast<const unsigned char*>(_pub_key_x));
_pub_key_y = int2oct(0, size);
::BN_bn2bin(y, (unsigned char*)static_cast<const unsigned char*>(_pub_key_y));
::BN_clear_free(x); x = nullptr;
::BN_clear_free(y); y = nullptr;
int len = ::EC_POINT_point2oct(_ec_group, ec_point, POINT_CONVERSION_COMPRESSED, nullptr, 0, _bn_ctx);
if (len == 0) {
loggers::get_instance().warning("security_ecc::generate: Failed to generate x_coordinate compressed key");
}
_pub_key_compressed = int2oct(0, len);
if (::EC_POINT_point2oct(_ec_group, ec_point, POINT_CONVERSION_COMPRESSED, (unsigned char *)static_cast<const unsigned char*>(_pub_key_compressed), len, _bn_ctx) == 0) {
loggers::get_instance().warning("security_ecc::generate: Failed to generate x_coordinate compressed key");
_pub_key_compressed = OCTETSTRING(0, nullptr);
} else { // Remove first byte
loggers::get_instance().log_msg("security_ecc::generate: Complete _pub_key_compressed=", _pub_key_compressed);
_pub_key_compressed_mode = ((_pub_key_compressed[0].get_octet() & 0x01) == 0x00) ? ecc_compressed_mode::compressed_y_0 : ecc_compressed_mode::compressed_y_1;
_pub_key_compressed = OCTETSTRING(_pub_key_compressed.lengthof() -1, 1 + static_cast<const unsigned char*>(_pub_key_compressed));
}
loggers::get_instance().log_msg("security_ecc::generate: _pri_key=", _pri_key);
loggers::get_instance().log_msg("security_ecc::generate: _pub_key_x=", _pub_key_x);
loggers::get_instance().log_msg("security_ecc::generate: _pub_key_y=", _pub_key_y);
loggers::get_instance().log_msg("security_ecc::generate: _pub_key_compressed=", _pub_key_compressed);
loggers::get_instance().log("security_ecc::generate: _pub_key_compressed_mode=%d", _pub_key_compressed_mode);
int security_ecc::generate_and_derive_ephemeral_key(const encryption_algotithm p_enc_algorithm, const OCTETSTRING& p_recipients_public_key_x, const OCTETSTRING& p_recipients_public_key_y) {
loggers::get_instance().log(">>> security_ecc::generate_and_derive_ephemeral_key (1)");
if (_pri_key.lengthof() == 0) {
loggers::get_instance().warning("security_ecc::generate_and_derive_ephemeral_key (1): Key shall be generated");
if ((_pub_key_x.lengthof() == 0) || (_pub_key_y.lengthof() == 0)) {
loggers::get_instance().warning("security_ecc::generate_and_derive_ephemeral_key (1): Keys shall be generated");
return -1;
}
_encryption_algotithm = p_enc_algorithm;
// Set buffers size
int len = (EC_GROUP_get_degree(_ec_group) + 7) / 8;
_secret_key = int2oct(0, len);
unsigned int nonce_length;
unsigned int sym_key_length;
unsigned int tag_length;
switch (_encryption_algotithm) {
case encryption_algotithm::aes_128_ccm:
// No break;
case encryption_algotithm::aes_128_gcm:
nonce_length = 12;
sym_key_length = 16;
tag_length = 16;
loggers::get_instance().warning("security_ecc::generate_and_derive_ephemeral_key (1): Unsupported encryption algorithm");
return -1;
} // End of 'switch' statement
unsigned int k_length;
switch (_elliptic_curve) {
case ec_elliptic_curves::nist_p_256: // Use the ANSI X9.62 Prime 256v1 curve
// No break;
case ec_elliptic_curves::brainpool_p_256_r1:
case ec_elliptic_curves::brainpool_p_384_r1:
k_length = 48;
break;
loggers::get_instance().warning("security_ecc::generate_and_derive_ephemeral_key (1): Unsupported encryption algorithm");
return -1;
} // End of 'switch' statement
// Convert the ephemeral public encryption keys to an EC point
bin_to_ec_point(p_recipients_public_key_x, p_recipients_public_key_y, &ec_point);
int result = ::ECDH_compute_key((unsigned char*)static_cast<const unsigned char*>(_secret_key), _secret_key.lengthof(), ec_point, _ec_key, nullptr);
loggers::get_instance().warning("security_ecc::generate_and_derive_ephemeral_key (1): Failed to generate shared secret key");
::EC_POINT_free(ec_point);
return -1;
}
::EC_POINT_free(ec_point);
loggers::get_instance().log_msg("security_ecc::generate_and_derive_ephemeral_key (1): _secret_key: ", _secret_key);
const int k_enc = nonce_length + sym_key_length + tag_length;
const int k_mac = k_length + k_length;
OCTETSTRING digest = int2oct(0, k_enc + k_mac);
loggers::get_instance().log("security_ecc::generate_and_derive_ephemeral_key (1): k_enc size:%d - k_mac size: %d - digest size:%d: ", k_enc, k_mac, digest.lengthof());
if (PKCS5_PBKDF2_HMAC((const char*)static_cast<const unsigned char*>(_secret_key), _secret_key.lengthof(), nullptr, 0, 2000, EVP_sha256(), digest.lengthof(), (unsigned char*)static_cast<const unsigned char*>(digest)) != 1) {
loggers::get_instance().warning("security_ecc::generate_and_derive_ephemeral_key: Failed to derive shared secret key");
loggers::get_instance().log_msg("security_ecc::generate_and_derive_ephemeral_key (1): digest: ", digest);
_nonce = OCTETSTRING(nonce_length, static_cast<const unsigned char*>(digest));
loggers::get_instance().log_msg("security_ecc::generate_and_derive_ephemeral_key (1): _nonce: ", _nonce);
_sym_key = OCTETSTRING(sym_key_length, _nonce.lengthof() + static_cast<const unsigned char*>(digest));
loggers::get_instance().log_msg("security_ecc::generate_and_derive_ephemeral_key (1): _sym_key: ", _sym_key);
_tag = OCTETSTRING(tag_length, _nonce.lengthof() + + _sym_key.lengthof() + static_cast<const unsigned char*>(digest)); // TODO Useless???
loggers::get_instance().log_msg("security_ecc::generate_and_derive_ephemeral_key (1): _tag: ", _tag);
OCTETSTRING hmac_secret = OCTETSTRING(k_length + k_length, nonce_length + sym_key_length + tag_length + static_cast<const unsigned char*>(digest));
loggers::get_instance().log_msg("security_ecc::generate_and_derive_ephemeral_key (1): hmac_secret: ", hmac_secret);
if (encrypt(encryption_algotithm::aes_128_ccm, _sym_key, _nonce, _sym_key, _enc_sym_key) == -1) {
loggers::get_instance().warning("security_ecc::generate_and_derive_ephemeral_key (1): Failed to encrypt key");
loggers::get_instance().log_msg("security_ecc::generate_and_derive_ephemeral_key (1): Encrypted symmetric key: ", encrypted_symmetric_key());
int security_ecc::generate_and_derive_ephemeral_key(const encryption_algotithm p_enc_algorithm, const OCTETSTRING& p_private_enc_key, const OCTETSTRING& p_ephemeral_public_key_x, const OCTETSTRING& p_ephemeral_public_key_y, const OCTETSTRING& p_enc_sym_key, const OCTETSTRING& p_expected_nonce, const OCTETSTRING& p_authentication_vector) {
loggers::get_instance().log(">>> security_ecc::generate_and_derive_ephemeral_key (2)");
if (_pri_key.lengthof() == 0) {
loggers::get_instance().warning("security_ecc::generate_and_derive_ephemeral_key (2): Key shall be generated");
return -1;
}
_encryption_algotithm = p_enc_algorithm;
_nonce = p_expected_nonce;
_enc_sym_key = p_enc_sym_key;
_tag = p_authentication_vector;
// Set buffers size
int len = (EC_GROUP_get_degree(_ec_group) + 7) / 8;
_secret_key = int2oct(0, len);
unsigned int nonce_length;
unsigned int sym_key_length;
unsigned int tag_length;
switch (_encryption_algotithm) {
case encryption_algotithm::aes_128_ccm:
// No break;
case encryption_algotithm::aes_128_gcm:
nonce_length = 12;
sym_key_length = 16;
tag_length = 16;
loggers::get_instance().warning("security_ecc::generate_and_derive_ephemeral_key (2): Unsupported encryption algorithm");
return -1;
} // End of 'switch' statement
unsigned int k_length;
switch (_elliptic_curve) {
case ec_elliptic_curves::nist_p_256: // Use the ANSI X9.62 Prime 256v1 curve
// No break;
case ec_elliptic_curves::brainpool_p_256_r1:
case ec_elliptic_curves::brainpool_p_384_r1:
k_length = 48;
break;
loggers::get_instance().warning("security_ecc::generate_and_derive_ephemeral_key (2): Unsupported encryption algorithm");
return -1;
} // End of 'switch' statement
// Convert the peer public encryption key to an EC point
EC_POINT *ec_point = nullptr;
bin_to_ec_point(p_ephemeral_public_key_x, p_ephemeral_public_key_y, &ec_point);
// Generate the shared symmetric key
int result = ::ECDH_compute_key((unsigned char*)static_cast<const unsigned char*>(_secret_key), _secret_key.lengthof(), ec_point, _ec_key, nullptr);
if (result == -1) {
::EC_POINT_free(ec_point);
return -1;
}
::EC_POINT_free(ec_point);
loggers::get_instance().log_msg("security_ecc::generate_and_derive_ephemeral_key (2): _secret_key: ", _secret_key);
const int k_enc = nonce_length + sym_key_length + tag_length;
const int k_mac = k_length + k_length;
OCTETSTRING digest = int2oct(0, k_enc + k_mac);
loggers::get_instance().log("security_ecc::generate_and_derive_ephemeral_key (2): k_enc size:%d - k_mac size: %d - digest size:%d: ", k_enc, k_mac, digest.lengthof());
if (PKCS5_PBKDF2_HMAC((const char*)static_cast<const unsigned char*>(_secret_key), _secret_key.lengthof(), nullptr, 0, 2000, EVP_sha256(), digest.lengthof(), (unsigned char*)static_cast<const unsigned char*>(digest)) != 1) {
loggers::get_instance().warning("security_ecc::generate_and_derive_ephemeral_key: Failed to derive shared secret key");
return -1;
}
loggers::get_instance().log_msg("security_ecc::generate_and_derive_ephemeral_key (2): digest: ", digest);
OCTETSTRING nonce(nonce_length, static_cast<const unsigned char*>(digest));
loggers::get_instance().log_msg("security_ecc::generate_and_derive_ephemeral_key (2): Generated nonce: ", nonce);
OCTETSTRING sym_key(sym_key_length, nonce.lengthof() + static_cast<const unsigned char*>(digest));
loggers::get_instance().log_msg("security_ecc::generate_and_derive_ephemeral_key (2): sym_key: ", sym_key);
OCTETSTRING tag(tag_length, nonce.lengthof() + sym_key.lengthof() + static_cast<const unsigned char*>(digest)); // TODO Useless???
loggers::get_instance().log_msg("security_ecc::generate_and_derive_ephemeral_key (2): tag: ", tag);
OCTETSTRING hmac_secret(k_length + k_length, nonce_length + sym_key_length + tag_length + static_cast<const unsigned char*>(digest));
loggers::get_instance().log_msg("security_ecc::generate_and_derive_ephemeral_key (2): hmac_secret: ", hmac_secret);
// Check if nonce vectors are valid
if (_nonce != nonce) {
loggers::get_instance().warning("security_ecc::generate_and_derive_ephemeral_key (2): Failed to derive nonce vector");
if (decrypt(p_enc_algorithm, sym_key, nonce, p_authentication_vector, p_enc_sym_key, skey) == -1) {
loggers::get_instance().warning("security_ecc::generate_and_derive_ephemeral_key (2): Failed to decrypt key");
loggers::get_instance().log_msg("security_ecc::generate_and_derive_ephemeral_key (2): Decrypted symmetric key: ", symmetric_encryption_key());
int security_ecc::generate_and_derive_ephemeral_key(const encryption_algotithm p_enc_algorithm, const OCTETSTRING& p_private_enc_key, const OCTETSTRING& p_recipients_compressed_key_x, const INTEGER& p_compressed_mode, const OCTETSTRING& p_enc_sym_key, const OCTETSTRING& p_expected_nonce, const OCTETSTRING& p_authentication_vector) {
loggers::get_instance().log(">>> security_ecc::generate_and_derive_ephemeral_key (3)");
security_ecc ec(ec_elliptic_curves::nist_p_256, p_recipients_compressed_key_x, (p_compressed_mode == 0) ? ecc_compressed_mode::compressed_y_0 : ecc_compressed_mode::compressed_y_1); // FIXME Why using ec_elliptic_curves::nist_p_256, it can be brainpool_p_256_r1
return generate_and_derive_ephemeral_key(p_enc_algorithm, p_private_enc_key, ec.public_key_x(), ec.public_key_y(), p_enc_sym_key, p_expected_nonce, p_authentication_vector);
}
int security_ecc::encrypt(const encryption_algotithm p_enc_algorithm, const OCTETSTRING& p_message, OCTETSTRING& p_enc_message) {
loggers::get_instance().log(">>> security_ecc::encrypt: %d", p_enc_algorithm);
if ((_pub_key_x.lengthof() != 0) || (_pub_key_y.lengthof() != 0)) {
loggers::get_instance().warning("security_ecc::encrypt: Constructor format #1 shall be used");
return -1;
}
_encryption_algotithm = p_enc_algorithm;
// Initialize the context and encryption operation
EVP_CIPHER_CTX *ctx = ::EVP_CIPHER_CTX_new();
switch (_encryption_algotithm) {
case encryption_algotithm::aes_128_ccm:
::EVP_EncryptInit_ex(ctx, EVP_aes_128_ccm(), nullptr, nullptr, nullptr);
_nonce = int2oct(0, 12);
_tag = int2oct(0, 16);
_sym_key = int2oct(0, 16);
p_enc_message = int2oct(0, p_message.lengthof());
break;
case encryption_algotithm::aes_256_ccm:
::EVP_EncryptInit_ex(ctx, EVP_aes_256_ccm(), nullptr, nullptr, nullptr);
break;
case encryption_algotithm::aes_128_gcm:
::EVP_EncryptInit_ex(ctx, EVP_aes_128_gcm(), nullptr, nullptr, nullptr);
break;
case encryption_algotithm::aes_256_gcm:
::EVP_EncryptInit_ex(ctx, EVP_aes_256_gcm(), nullptr, nullptr, nullptr);
break;
} // End of 'switch' statement
::RAND_bytes((unsigned char*)static_cast<const unsigned char*>(_sym_key), _sym_key.lengthof());
loggers::get_instance().log_msg("security_ecc::encrypt: _sym_key: ", _sym_key);
::RAND_bytes((unsigned char*)static_cast<const unsigned char*>(_nonce), _nonce.lengthof());
loggers::get_instance().log_msg("security_ecc::encrypt: nonce: ", _nonce);
::EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_IVLEN, _nonce.lengthof(), nullptr);
::EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_TAG, _tag.lengthof(), nullptr);
::EVP_EncryptInit_ex(ctx, nullptr, nullptr, static_cast<const unsigned char*>(_sym_key), static_cast<const unsigned char*>(_nonce));
// No authentication data
// Encrypt the data
int len = 0;
::EVP_EncryptUpdate(ctx, (unsigned char*)static_cast<const unsigned char*>(p_enc_message), &len, static_cast<const unsigned char*>(p_message), p_message.lengthof());
::EVP_EncryptFinal_ex(ctx, (unsigned char*)static_cast<const unsigned char*>(p_enc_message) + len, &len);
// Get the authentication tag(const char*)static_cast<const unsigned char*>(
::EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_GET_TAG, _tag.lengthof(), (unsigned char*)static_cast<const unsigned char*>(_tag));
loggers::get_instance().log_msg("security_ecc::encrypt: tag: ", _tag);
int security_ecc::encrypt(const encryption_algotithm p_enc_algorithm, const OCTETSTRING& p_symmetric_key, const OCTETSTRING& p_nonce, const OCTETSTRING& p_message, OCTETSTRING& p_enc_message) {
loggers::get_instance().log(">>> security_ecc::encrypt (2): %d", p_enc_algorithm);
_encryption_algotithm = p_enc_algorithm;
_sym_key = p_symmetric_key;
_nonce = p_nonce;
// Initialize the context and encryption operation
EVP_CIPHER_CTX *ctx = ::EVP_CIPHER_CTX_new();
switch (_encryption_algotithm) {
case encryption_algotithm::aes_128_ccm:
::EVP_EncryptInit_ex(ctx, EVP_aes_128_ccm(), nullptr, nullptr, nullptr);
_tag = int2oct(0, 16);
p_enc_message = int2oct(0, p_message.lengthof());
::EVP_EncryptInit_ex(ctx, EVP_aes_256_ccm(), nullptr, nullptr, nullptr);
::EVP_EncryptInit_ex(ctx, EVP_aes_128_gcm(), nullptr, nullptr, nullptr);
::EVP_EncryptInit_ex(ctx, EVP_aes_256_gcm(), nullptr, nullptr, nullptr);
loggers::get_instance().log_msg("security_ecc::encrypt: _sym_key: ", _sym_key);
loggers::get_instance().log_msg("security_ecc::encrypt: nonce: ", _nonce);
::EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_IVLEN, _nonce.lengthof(), nullptr);
::EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_TAG, _tag.lengthof(), nullptr);
::EVP_EncryptInit_ex(ctx, nullptr, nullptr, static_cast<const unsigned char*>(_sym_key), static_cast<const unsigned char*>(_nonce));
// No authentication data
// Encrypt the data
int len = 0;
::EVP_EncryptUpdate(ctx, (unsigned char*)static_cast<const unsigned char*>(p_enc_message), &len, static_cast<const unsigned char*>(p_message), p_message.lengthof());
// Finalize the encryption session
::EVP_EncryptFinal_ex(ctx, (unsigned char*)static_cast<const unsigned char*>(p_enc_message) + len, &len);
::EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_GET_TAG, _tag.lengthof(), (unsigned char*)static_cast<const unsigned char*>(_tag));
loggers::get_instance().log_msg("security_ecc::encrypt: tag: ", _tag);
::EVP_CIPHER_CTX_free(ctx);
return 0;
}
int security_ecc::decrypt(const encryption_algotithm p_enc_algorithm, const OCTETSTRING& p_key, const OCTETSTRING& p_nonce, const OCTETSTRING& p_tag, const OCTETSTRING& p_enc_message, OCTETSTRING& p_message) {
loggers::get_instance().log(">>> security_ecc::decrypt: %d", p_enc_algorithm);
_encryption_algotithm = p_enc_algorithm;
_nonce = p_nonce;
_tag = p_tag;
// Initialize the context and decryption operation
EVP_CIPHER_CTX *ctx = ::EVP_CIPHER_CTX_new();
switch (_encryption_algotithm) {
case encryption_algotithm::aes_128_ccm:
::EVP_DecryptInit_ex(ctx, EVP_aes_128_ccm(), nullptr, nullptr, nullptr);
break;
case encryption_algotithm::aes_256_ccm:
::EVP_DecryptInit_ex(ctx, EVP_aes_256_ccm(), nullptr, nullptr, nullptr);
break;
case encryption_algotithm::aes_128_gcm:
::EVP_DecryptInit_ex(ctx, EVP_aes_128_gcm(), nullptr, nullptr, nullptr);
break;
case encryption_algotithm::aes_256_gcm:
::EVP_DecryptInit_ex(ctx, EVP_aes_256_gcm(), nullptr, nullptr, nullptr);
break;
} // End of 'switch' statement
// Set nonce length
EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_IVLEN, _nonce.lengthof(), nullptr);
EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_TAG, _tag.lengthof(), (unsigned char*)static_cast<const unsigned char*>(_tag));
EVP_DecryptInit_ex(ctx, nullptr, nullptr, static_cast<const unsigned char*>(_sym_key), static_cast<const unsigned char*>(_nonce));
// Decrypt plaintext, verify tag: can only be called once
p_message = int2oct(0, p_enc_message.lengthof());
int result = EVP_DecryptUpdate(ctx, (unsigned char*)static_cast<const unsigned char*>(p_message), &len, static_cast<const unsigned char*>(p_enc_message), p_enc_message.lengthof());
loggers::get_instance().log("security_ecc::decrypt: len: %d", len);
loggers::get_instance().log("security_ecc::decrypt: result: %d", result);
::EVP_CIPHER_CTX_free(ctx);
return (result > 0) ? 0 : -1;
}
int security_ecc::decrypt(const OCTETSTRING& p_tag, const OCTETSTRING& p_enc_message, OCTETSTRING& p_message) {
loggers::get_instance().log(">>> security_ecc::decrypt: %d", _encryption_algotithm);
if ((_pri_key.lengthof() == 0) || (_secret_key.lengthof() == 0)) {
loggers::get_instance().warning("security_ecc::decrypt: Constrictor format #2 shall be used");
return -1;
}
_tag = p_tag;
// Initialize the context and decryption operation
EVP_CIPHER_CTX *ctx = ::EVP_CIPHER_CTX_new();
switch (_encryption_algotithm) {
case encryption_algotithm::aes_128_ccm:
::EVP_DecryptInit_ex(ctx, EVP_aes_128_ccm(), nullptr, nullptr, nullptr);
break;
case encryption_algotithm::aes_256_ccm:
::EVP_DecryptInit_ex(ctx, EVP_aes_256_ccm(), nullptr, nullptr, nullptr);
break;
case encryption_algotithm::aes_128_gcm:
::EVP_DecryptInit_ex(ctx, EVP_aes_128_gcm(), nullptr, nullptr, nullptr);
break;
case encryption_algotithm::aes_256_gcm:
::EVP_DecryptInit_ex(ctx, EVP_aes_256_gcm(), nullptr, nullptr, nullptr);
break;
} // End of 'switch' statement
// Set nonce length
EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_IVLEN, _nonce.lengthof(), nullptr);
EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_TAG, _tag.lengthof(), (unsigned char*)static_cast<const unsigned char*>(_tag));
EVP_DecryptInit_ex(ctx, nullptr, nullptr, static_cast<const unsigned char*>(_sym_key), static_cast<const unsigned char*>(_nonce));
// Decrypt plaintext, verify tag: can only be called once
p_message = int2oct(0, p_enc_message.lengthof());
int result = EVP_DecryptUpdate(ctx, (unsigned char*)static_cast<const unsigned char*>(p_message), &len, static_cast<const unsigned char*>(p_enc_message), p_enc_message.lengthof());
loggers::get_instance().log("security_ecc::decrypt: len: %d", len);
loggers::get_instance().log("security_ecc::decrypt: result: %d", result);
::EVP_CIPHER_CTX_free(ctx);
return (result > 0) ? 0 : -1;
}
int security_ecc::sign(const OCTETSTRING& p_data, OCTETSTRING& p_r_sig, OCTETSTRING& p_s_sig) {
loggers::get_instance().log_msg(">>> security_ecc::sign: p_data: ", p_data);
if(_pri_key.lengthof() == 0) { // No private key
if (p_data.lengthof() == 0) {
ECDSA_SIG *signature = ::ECDSA_do_sign(static_cast<const unsigned char*>(p_data), p_data.lengthof(), _ec_key);
loggers::get_instance().warning("security_ecc::sign: Signature failed");
loggers::get_instance().log("security_ecc::sign: succeed");
if (::ECDSA_do_verify(static_cast<const unsigned char*>(p_data), p_data.lengthof(), signature, _ec_key) != 1) {
loggers::get_instance().warning("security_ecc::sign: Signature not verified");
const BIGNUM* r = nullptr;
const BIGNUM* s = nullptr;
::ECDSA_SIG_get0(signature, &r, &s);
loggers::get_instance().log("security_ecc::sign: r size: %d", BN_num_bytes(r));
p_r_sig = int2oct(0, BN_num_bytes(r));
::BN_bn2bin(r, (unsigned char*)static_cast<const unsigned char*>(p_r_sig));
loggers::get_instance().log_msg("security_ecc::sign: r=", p_r_sig);
loggers::get_instance().log("security_ecc::sign: s size: %d", BN_num_bytes(s));
p_s_sig = int2oct(0, BN_num_bytes(s));
::BN_bn2bin(s, (unsigned char*)static_cast<const unsigned char*>(p_s_sig));
loggers::get_instance().log_msg("security_ecc::sign: s=", p_s_sig);
::ECDSA_SIG_free(signature);
return 0;
}
int security_ecc::sign_verif(const OCTETSTRING& p_data, const OCTETSTRING& p_signature) {
loggers::get_instance().log(">>> security_ecc::sign_verif");
loggers::get_instance().log_msg(">>> security_ecc::sign_verify: p_data: ", p_data);
if (p_data.lengthof() == 0) {
BIGNUM* r = ::BN_bin2bn(static_cast<const unsigned char*>(p_signature), p_signature.lengthof() / 2, nullptr);
loggers::get_instance().log_to_hexa("security_ecc::sign_verify: r=", static_cast<const unsigned char*>(p_signature), p_signature.lengthof() / 2);
BIGNUM* s = ::BN_bin2bn(static_cast<const unsigned char*>(p_signature) + p_signature.lengthof() / 2, p_signature.lengthof() / 2, nullptr);
loggers::get_instance().log_to_hexa("security_ecc::sign_verify: s=", static_cast<const unsigned char*>(p_signature) + p_signature.lengthof() / 2, p_signature.lengthof() / 2);
::ECDSA_SIG_set0(signature, r, s);
int result = ::ECDSA_do_verify(static_cast<const unsigned char*>(p_data), p_data.lengthof(), signature, _ec_key);
loggers::get_instance().log("security_ecc::sign_verif: %s", (result == 1) ? "succeed": "failed");
const int security_ecc::init() {
loggers::get_instance().log(">>> security_ecc::init: %d", static_cast<int>(_elliptic_curve));
::OpenSSL_add_all_algorithms();
case ec_elliptic_curves::nist_p_256: // Use the ANSI X9.62 Prime 256v1 curve
result = ::OBJ_txt2nid("prime256v1");
break;
case ec_elliptic_curves::brainpool_p_256_r1:
result = ::OBJ_txt2nid("brainpoolP256r1");
break;
case ec_elliptic_curves::brainpool_p_384_r1:
result = ::OBJ_txt2nid("brainpoolP384r1");
break;
default:
loggers::get_instance().error("security_ecc::init: Unsupported EC elliptic_curve");
loggers::get_instance().warning("security_ecc::init: Unaible to set EC elliptic_curve");
loggers::get_instance().log("security_ecc::init: Nid=%d", result);
_ec_key = ::EC_KEY_new_by_curve_name(result); // Set the elliptic curve
::EC_KEY_set_asn1_flag(_ec_key, OPENSSL_EC_NAMED_CURVE); // Used to save and retrieve keys
_ec_group = ::EC_KEY_get0_group(_ec_key); // Get pointer to the EC_GROUP
_bn_ctx = ::BN_CTX_new();
return 0;
} // End of init
int security_ecc::bin_to_ec_point(const OCTETSTRING& p_public_key_x, const OCTETSTRING& p_public_key_y, EC_POINT** p_ec_point) { // ec_key_public_key_bin_to_point
OCTETSTRING v = int2oct(4, 1);
v += p_public_key_x;
v += p_public_key_y;
pubk_bn = ::BN_bin2bn(static_cast<const unsigned char*>(v), v.lengthof(), nullptr);
*p_ec_point = ::EC_POINT_new(_ec_group);
::EC_POINT_bn2point(_ec_group, pubk_bn, *p_ec_point, _bn_ctx);
// BIO *bio_out = nullptr; /* stdout */
// bio_out = BIO_new_fp(stdout, BIO_NOCLOSE);
// BIGNUM *x = BN_new();
// BIGNUM *y = BN_new();
// if (EC_POINT_get_affine_coordinates_GFp(_ec_group, *p_ec_point, x, y, nullptr)) {
// BN_print_fp(stdout, x);
// putc('\n', stdout);
// BN_print_fp(stdout, y);
// putc('\n', stdout);
// }
// BN_free(x); BN_free(y);
return 0;
}
int security_ecc::public_key_to_bin(OCTETSTRING& p_bin_key) { // ec_key_public_key_to_bin
const EC_GROUP *ec_group = EC_KEY_get0_group(_ec_key);
const EC_POINT *pub = EC_KEY_get0_public_key(_ec_key);
BIGNUM *pub_bn = BN_new();
::EC_POINT_point2bn(ec_group, pub, POINT_CONVERSION_UNCOMPRESSED, pub_bn, _bn_ctx);
p_bin_key = int2oct(0, BN_num_bytes(pub_bn));
::BN_bn2bin(pub_bn, (unsigned char*)static_cast<const unsigned char*>(p_bin_key));
::BN_clear_free(pub_bn);
return 0;