security_ecc.cc 30.1 KB
Newer Older
garciay's avatar
garciay committed
#include <openssl/ecdsa.h>
#include <openssl/rand.h>
garciay's avatar
garciay committed

garciay's avatar
garciay committed
#include "hmac.hh"
garciay's avatar
garciay committed
#include "security_ecc.hh"
garciay's avatar
garciay committed

#include "loggers.hh"
garciay's avatar
garciay committed

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(), _secret_key(), _enc_key_x(), _enc_key_y(), _sym_key(), _nonce(), _tag() {
garciay's avatar
garciay committed
  loggers::get_instance().log(">>> security_ecc::security_ecc: %d", static_cast<int>(p_elliptic_curve));
garciay's avatar
garciay committed
  
garciay's avatar
garciay committed
  const int result = init();
  if (result == -1) {
garciay's avatar
garciay committed
    loggers::get_instance().error("security_ecc::security_ecc: Unsupported elliptic_curve %d", _elliptic_curve);
garciay's avatar
garciay committed
} // End of constructor

security_ecc::security_ecc(const ec_elliptic_curves p_elliptic_curve, const std::vector<unsigned char>& 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(), _secret_key(), _enc_key_x(), _enc_key_y(), _sym_key(), _nonce(), _tag() {
garciay's avatar
garciay committed
  loggers::get_instance().log(">>> security_ecc::security_ecc (1): %d", static_cast<int>(p_elliptic_curve));
garciay's avatar
garciay committed
  
garciay's avatar
garciay committed
  // Sanity checks
  if ((_elliptic_curve == ec_elliptic_curves::nist_p_256) || (_elliptic_curve == ec_elliptic_curves::brainpool_p_256_r1)) {
    if (p_private_key.size() != 32) {
garciay's avatar
garciay committed
      loggers::get_instance().error("security_ecc::security_ecc: Invalid public keys size");
garciay's avatar
garciay committed
    }
  } else if (_elliptic_curve == ec_elliptic_curves::brainpool_p_384_r1) {
    if ((p_private_key.size() != 48)) {
garciay's avatar
garciay committed
      loggers::get_instance().error("security_ecc::security_ecc: Invalid public keys size");
garciay's avatar
garciay committed
  int result = init();
  if (result == -1) {
garciay's avatar
garciay committed
    loggers::get_instance().error("security_ecc::security_ecc: Unsupported elliptic_curve %d", _elliptic_curve);
garciay's avatar
garciay committed
  ::EC_KEY_set_conv_form(_ec_key, POINT_CONVERSION_COMPRESSED);
  
  // Build private key
  BIGNUM p;
  ::BN_init(&p);
  ::BN_bin2bn(_pri_key.data(), _pri_key.size(), &p);
garciay's avatar
garciay committed
  // Build public keys
  EC_POINT* ec_point = ::EC_POINT_new(_ec_group);
  ::EC_POINT_mul(_ec_group, ec_point, &p, NULL, NULL, _bn_ctx);
  // Set private key
  ::EC_KEY_set_private_key(_ec_key, &p);
  if (::EC_KEY_check_key(_ec_key) != 0) {
garciay's avatar
garciay committed
    loggers::get_instance().error("security_ecc::security_ecc (1): Invalid private key");
garciay's avatar
garciay committed
  }
  // Private key is correct, set public keys
  ::EC_KEY_set_public_key(_ec_key, ec_point);

  BIGNUM xy;
  ::BN_init(&xy);
  ::EC_POINT_point2bn(_ec_group, ec_point, POINT_CONVERSION_COMPRESSED, &xy, _bn_ctx);
  std::vector<unsigned char> v(BN_num_bytes(&xy));
  ::BN_bn2bin(&xy, v.data());
garciay's avatar
garciay committed
  const int l = v.size() / 2;
  _pub_key_x.resize(l);
  std::copy(v.cbegin(), v.cbegin() + l - 1, _pub_key_x.begin());
  _pub_key_y.resize(l);
  std::copy(v.cbegin() + l, v.cend(), _pub_key_y.begin());
garciay's avatar
garciay committed
  ::EC_POINT_free(ec_point);
  
} // End of constructor

security_ecc::security_ecc(const ec_elliptic_curves p_elliptic_curve, const std::vector<unsigned char>& p_public_key_x, const std::vector<unsigned char>& 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), _secret_key(), _enc_key_x(), _enc_key_y(), _sym_key(), _nonce(), _tag() {
garciay's avatar
garciay committed
  loggers::get_instance().log(">>> security_ecc::security_ecc (2): %d", static_cast<int>(p_elliptic_curve));
garciay's avatar
garciay committed
  
garciay's avatar
garciay committed
  // 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.size() != 32) || (p_public_key_y.size() != 32)) {
garciay's avatar
garciay committed
      loggers::get_instance().error("security_ecc::security_ecc: Invalid public keys size");
garciay's avatar
garciay committed
    }
  } else if (_elliptic_curve == ec_elliptic_curves::brainpool_p_384_r1) {
    if ((p_public_key_x.size() != 48) || (p_public_key_y.size() != 48)) {
garciay's avatar
garciay committed
      loggers::get_instance().error("security_ecc::security_ecc: Invalid public keys size");
garciay's avatar
garciay committed
  int result = init();
  if (result == -1) {
garciay's avatar
garciay committed
    loggers::get_instance().error("security_ecc::security_ecc: Unsupported elliptic_curve %d", _elliptic_curve);
garciay's avatar
garciay committed
  ::EC_KEY_set_conv_form(_ec_key, POINT_CONVERSION_COMPRESSED);
  
  // Set public key
  BIGNUM x;
  ::BN_init(&x);
  ::BN_bin2bn(_pub_key_x.data(), _pub_key_x.size(), &x);
garciay's avatar
garciay committed
  BIGNUM y;
  ::BN_init(&y);
  ::BN_bin2bn(_pub_key_y.data(), _pub_key_y.size(), &y);
garciay's avatar
garciay committed
  EC_POINT* ec_point = ::EC_POINT_new(_ec_group);
  result = 0;
  switch (_elliptic_curve) {
garciay's avatar
garciay committed
  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:
garciay's avatar
garciay committed
    result = ::EC_POINT_set_affine_coordinates_GFp(_ec_group, ec_point, &x, &y, _bn_ctx); // Use primary elliptic curve
    break;
garciay's avatar
garciay committed
  default: // Use Binary
garciay's avatar
garciay committed
    result = ::EC_POINT_set_affine_coordinates_GF2m(_ec_group, ec_point, &x, &y, _bn_ctx);
  } // End of 'switch' statement
  if (result == 0) {
garciay's avatar
garciay committed
    loggers::get_instance().error("security_ecc::security_ecc (1): Failed to get coordinates");
garciay's avatar
garciay committed
  }
  ::EC_KEY_set_public_key(_ec_key, ec_point);
  ::EC_POINT_free(ec_point);
} // End of constructor

garciay's avatar
garciay committed
security_ecc::~security_ecc() {
  loggers::get_instance().log(">>> security_ecc::~security_ecc");
garciay's avatar
garciay committed
  if(_ec_key != nullptr) {
    ::EC_KEY_free(_ec_key);
  }
garciay's avatar
garciay committed
  if (_bn_ctx != nullptr) {
    ::BN_CTX_free(_bn_ctx);
garciay's avatar
garciay committed
  loggers::get_instance().log("<<< security_ecc::~security_ecc");
garciay's avatar
garciay committed
} // End of Destructor
garciay's avatar
garciay committed

garciay's avatar
garciay committed
int security_ecc::generate() {
  loggers::get_instance().log(">>> security_ecc::generate");
garciay's avatar
garciay committed
  
  // Sanity check
  if (!::EC_KEY_generate_key(_ec_key)) { // Generate the private and public keys 
garciay's avatar
garciay committed
    loggers::get_instance().error("security_ecc::generate: Failed to generate private/public keys");
garciay's avatar
garciay committed
    return -1;
  }

  BIGNUM x, y, compressed_y;
garciay's avatar
garciay committed
  ::BN_init(&x);
  ::BN_init(&y);
  ::BN_init(&compressed_y);
garciay's avatar
garciay committed
  const EC_POINT* ec_point = EC_KEY_get0_public_key(_ec_key);
  int result = 0;
  switch (_elliptic_curve) {
garciay's avatar
garciay committed
  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:
garciay's avatar
garciay committed
    result = ::EC_POINT_get_affine_coordinates_GFp(_ec_group, ec_point, &x, &y, _bn_ctx); // Use primer on elliptic curve
    break;
garciay's avatar
garciay committed
  default: // Use binary
garciay's avatar
garciay committed
    result = ::EC_POINT_get_affine_coordinates_GF2m(_ec_group, ec_point, &x, &y, _bn_ctx);
  } // End of 'switch' statement
  if (result == 0) {
garciay's avatar
garciay committed
    loggers::get_instance().error("security_ecc::generate: Failed to get coordinates");
garciay's avatar
garciay committed
  }
  const BIGNUM* p = ::EC_KEY_get0_private_key(_ec_key);
  
  _pri_key.resize(BN_num_bytes(p));
  ::BN_bn2bin(p, _pri_key.data());
  _pub_key_x.resize(BN_num_bytes(&x));
  ::BN_bn2bin(&x, _pub_key_x.data());
  _pub_key_y.resize(BN_num_bytes(&y));
  ::BN_bn2bin(&y, _pub_key_y.data());

  // Compressed
  int len = ::EC_POINT_point2oct(_ec_group, ec_point, POINT_CONVERSION_COMPRESSED, NULL, 0, _bn_ctx); 
  std::vector<unsigned char> cy;
  cy.resize(len);
  ::EC_POINT_point2oct(_ec_group, ec_point, POINT_CONVERSION_COMPRESSED, (unsigned char *)cy.data(), len, _bn_ctx);
  // TODO Create a compressed _pub_key_compressed_y
  //_pub_key_compressed_y.resize(BN_num_bytes(&compressed_y));
  //::BN_bn2bin(&compressed_y, _pub_key_compressed_y.data());
garciay's avatar
garciay committed
  
  return 0;
}

int security_ecc::generate_and_derive_ephemeral_key(const encryption_algotithm p_enc_algorithm, const std::vector<unsigned char>& p_recipients_public_key_x, const std::vector<unsigned char>& p_recipients_public_key_y) {
  loggers::get_instance().log(">>> security_ecc::generate_and_derive_ephemeral_key");

  // Sanity checks
  if (_pri_key.size() == 0) {
    loggers::get_instance().warning("security_ecc::generate_and_derive_ephemeral_key: Key shall be generated");
  if ((_pub_key_x.size() == 0) || (_pub_key_y.size() == 0)) {
    loggers::get_instance().warning("security_ecc::generate_and_derive_ephemeral_key: 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.resize(len);
  // Convert the peer public encryption key to an EC point
  EC_POINT *ec_point = nullptr;
  bin_to_ec_point(p_recipients_public_key_x, p_recipients_public_key_y, &ec_point);
garciay's avatar
garciay committed
  // Generate the shared secret key
  int result = ::ECDH_compute_key(_secret_key.data(), _secret_key.size(), ec_point, _ec_key, NULL);
  if (result == -1) {
    loggers::get_instance().warning("security_ecc::generate_and_derive_ephemeral_key: Failed to generate shared secret key");
    ::EC_POINT_free(ec_point);
    return -1;
  }
  ::EC_POINT_free(ec_point);
  loggers::get_instance().log_to_hexa("security_ecc::generate_and_derive_ephemeral_key: _secret_key: ", _secret_key.data(), _secret_key.size());
  
  // Derive the shared secret key
  std::vector<unsigned char> k_enc(12 + 16 + 16, 0x00); // Nonce + AES 128 CCM key + Tag
                                                        // TODO Use ec_encryption_algorithm
  std::vector<unsigned char> k_mac(32 + 32, 0x00);
  std::vector<unsigned char> digest(k_enc.size() + k_mac.size(), 0x00);
  loggers::get_instance().log("security_ecc::generate_and_derive_ephemeral_key: k_enc size:%d - k_mac size: %d - digest size:%d: ", k_enc.size(), k_mac.size(), digest.size());
  if (PKCS5_PBKDF2_HMAC((const char*)_secret_key.data(), _secret_key.size(), NULL, 0, 2000, EVP_sha256(), digest.size(), digest.data()) != 1) {
    loggers::get_instance().warning("security_ecc::generate_and_derive_ephemeral_key: Failed to derive shared secret key");
    return -1;
  }
  loggers::get_instance().log_to_hexa("security_ecc::generate_and_derive_ephemeral_key: digest: ", digest.data(), digest.size());

  // Extract AES 128 parameters
  _nonce.resize(12); // TODO Use ec_encryption_algorithm
  std::copy(digest.begin(), digest.begin() + _nonce.size(), _nonce.begin());
  loggers::get_instance().log_to_hexa("security_ecc::generate_and_derive_ephemeral_key: _nonce: ", _nonce.data(), _nonce.size());
  _sym_key.resize(16);
  std::copy(digest.begin() + _nonce.size(), digest.begin() + _nonce.size() + _sym_key.size(), _sym_key.begin());
  loggers::get_instance().log_to_hexa("security_ecc::generate_and_derive_ephemeral_key: _sym_key: ", _sym_key.data(), _sym_key.size());
  _tag.resize(16);
  std::copy(digest.begin() + _nonce.size() + _sym_key.size(), digest.begin() + _nonce.size() + _sym_key.size() + _tag.size(), _tag.begin()); // TODO Useless???
  loggers::get_instance().log_to_hexa("security_ecc::generate_and_derive_ephemeral_key: _tag: ", _tag.data(), _tag.size());
  // Extract the HMAC key
  std::vector<unsigned char> hmac_secret(32 + 32, 0x00);
  std::copy(digest.data() + 44, digest.data() + 44 + 32 + 32, hmac_secret.begin());
  loggers::get_instance().log_to_hexa("security_ecc::generate_and_derive_ephemeral_key: hmac_secret: ", hmac_secret.data(), hmac_secret.size());

  // Encrypt the _sym_key
  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: Failed to encrypt key");
    return -1;
  }
  loggers::get_instance().log_to_hexa("security_ecc::generate_and_derive_ephemeral_key: Encrypted symmetric key: ", encrypted_symmetric_key().data(), encrypted_symmetric_key().size());
int security_ecc::generate_and_derive_ephemeral_key(const encryption_algotithm p_enc_algorithm, const std::vector<unsigned char>& p_private_enc_key, const std::vector<unsigned char>& p_ephemeral_public_key_x, const std::vector<unsigned char>& p_ephemeral_public_key_y, const std::vector<unsigned char>& p_enc_sym_key, const std::vector<unsigned char>& p_expected_nonce, const std::vector<unsigned char>& p_authentication_vector) {
  loggers::get_instance().log(">>> security_ecc::generate_and_derive_ephemeral_key");

  // Sanity checks
  if (_pri_key.size() == 0) {
    loggers::get_instance().warning("security_ecc::generate_and_derive_ephemeral_key: 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.resize(len);
  
  // 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(_secret_key.data(), _secret_key.size(), ec_point, _ec_key, NULL);
  if (result == -1) {
    ::EC_POINT_free(ec_point);
    return -1;
  }
  ::EC_POINT_free(ec_point);
  loggers::get_instance().log_to_hexa("security_ecc::generate_and_derive_ephemeral_key: _secret_key: ", _secret_key.data(), _secret_key.size());
  
  // Derive the shared secret key
  std::vector<unsigned char> k_enc(12 + 16 + 16, 0x00); // Nonce + AES 128 CCM key + Tag
                                                        // TODO Use ec_encryption_algorithm
  std::vector<unsigned char> k_mac(32 + 32, 0x00);
  std::vector<unsigned char> digest(k_enc.size() + k_mac.size(), 0x00);
  loggers::get_instance().log("security_ecc::generate_and_derive_ephemeral_key: k_enc size:%d - k_mac size: %d - digest size:%d: ", k_enc.size(), k_mac.size(), digest.size());
  if (PKCS5_PBKDF2_HMAC((const char*)_secret_key.data(), _secret_key.size(), NULL, 0, 2000, EVP_sha256(), digest.size(), digest.data()) != 1) {
    loggers::get_instance().warning("security_ecc::generate_and_derive_ephemeral_key: Failed to derive shared secret key");
    return -1;
  }
  loggers::get_instance().log_to_hexa("security_ecc::generate_and_derive_ephemeral_key: digest: ", digest.data(), digest.size());

  // Extract AES 128 parameters
  std::vector<unsigned char> nonce(12, 0x00); // TODO Use ec_encryption_algorithm
  std::copy(digest.begin(), digest.begin() + nonce.size(), nonce.begin());
  loggers::get_instance().log_to_hexa("security_ecc::generate_and_derive_ephemeral_key: Generated nonce: ", nonce.data(), nonce.size());
  std::vector<unsigned char> sym_key(16, 0x00);
  std::copy(digest.begin() + nonce.size(), digest.begin() + nonce.size() + sym_key.size(), sym_key.begin());
  loggers::get_instance().log_to_hexa("security_ecc::generate_and_derive_ephemeral_key: sym_key: ", sym_key.data(), sym_key.size());
  std::vector<unsigned char> tag(16, 0x00);
  std::copy(digest.begin() + nonce.size() + sym_key.size(), digest.begin() + nonce.size() + sym_key.size() + tag.size(), tag.begin()); // TODO Useless???
  loggers::get_instance().log_to_hexa("security_ecc::generate_and_derive_ephemeral_key: tag: ", tag.data(), tag.size());

  if (_nonce != nonce) {
    loggers::get_instance().warning("security_ecc::generate_and_derive_ephemeral_key: Failed to derive nonce vector");
    return -1;
  }
  
  // Extract the HMAC key
  std::vector<unsigned char> hmac_secret(32 + 32, 0x00);
  std::copy(digest.data() + 44, digest.data() + 44 + 32 + 32, hmac_secret.begin());
  loggers::get_instance().log_to_hexa("security_ecc::generate_and_derive_ephemeral_key: hmac_secret: ", hmac_secret.data(), hmac_secret.size());

  // Decrypt the symmetric key
  std::vector<unsigned char> skey;
  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: Failed to decrypt key");
    return -1;
  }
  _sym_key = skey;
  loggers::get_instance().log_to_hexa("security_ecc::generate_and_derive_ephemeral_key: Decrypted symmetric key: ", symmetric_encryption_key().data(), symmetric_encryption_key().size());
  
  return 0;
}

garciay's avatar
garciay committed
int security_ecc::encrypt(const encryption_algotithm p_enc_algorithm, const std::vector<unsigned char>& p_message, std::vector<unsigned char>& p_enc_message) {
  loggers::get_instance().log(">>> security_ecc::encrypt: %d", p_enc_algorithm);

  // Sanity checks
  if ((_pub_key_x.size() != 0) || (_pub_key_y.size() != 0)) {
garciay's avatar
garciay committed
    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(), NULL, NULL, NULL);
    // Allocate buffers size
    _nonce.resize(12);
    _tag.resize(16);
garciay's avatar
garciay committed
    _sym_key.resize(16);
    p_enc_message.resize(p_message.size());
    break;
  case encryption_algotithm::aes_256_ccm:
    ::EVP_EncryptInit_ex(ctx, EVP_aes_256_ccm(), NULL, NULL, NULL);
    break;
  case encryption_algotithm::aes_128_gcm:
    ::EVP_EncryptInit_ex(ctx, EVP_aes_128_gcm(), NULL, NULL, NULL);
    break;
  case encryption_algotithm::aes_256_gcm:
    ::EVP_EncryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, NULL, NULL);
    break;
  } // End of 'switch' statement
garciay's avatar
garciay committed
  // Generate _sym_key
  ::RAND_pseudo_bytes(_sym_key.data(), _sym_key.size());
  loggers::get_instance().log_to_hexa("security_ecc::encrypt: _sym_key: ", _sym_key.data(), _sym_key.size());
  // Generate _nonce
  ::RAND_pseudo_bytes(_nonce.data(), _nonce.size());
garciay's avatar
garciay committed
  loggers::get_instance().log_to_hexa("security_ecc::encrypt: nonce: ", _nonce.data(), _nonce.size());
  // Set nonce length
  ::EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_IVLEN, _nonce.size(), NULL);
  // Set tag length
  ::EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_TAG, _tag.size(), NULL);
  // Prime the key and nonce
garciay's avatar
garciay committed
  ::EVP_EncryptInit_ex(ctx, NULL, NULL, _sym_key.data(), _nonce.data());
  // No authentication data
  // Encrypt the data
  int len = 0;
  ::EVP_EncryptUpdate(ctx, p_enc_message.data(), &len, p_message.data(), p_message.size());
  // Finalize the encryption session
  ::EVP_EncryptFinal_ex(ctx, p_enc_message.data() + len, &len);
  // Get the authentication tag
  ::EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_GET_TAG, _tag.size(), _tag.data());
  loggers::get_instance().log_to_hexa("security_ecc::encrypt: tag: ", _tag.data(), _tag.size());

  ::EVP_CIPHER_CTX_free(ctx);

  return 0;
}

int security_ecc::encrypt(const encryption_algotithm p_enc_algorithm, const std::vector<unsigned char>& p_symmetric_key, const std::vector<unsigned char>& p_nonce, const std::vector<unsigned char>& p_message, std::vector<unsigned char>& 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(), NULL, NULL, NULL);
    // Allocate buffers size
    _tag.resize(16);
    p_enc_message.resize(p_message.size());
    break;
  case encryption_algotithm::aes_256_ccm:
    ::EVP_EncryptInit_ex(ctx, EVP_aes_256_ccm(), NULL, NULL, NULL);
    break;
  case encryption_algotithm::aes_128_gcm:
    ::EVP_EncryptInit_ex(ctx, EVP_aes_128_gcm(), NULL, NULL, NULL);
    break;
  case encryption_algotithm::aes_256_gcm:
    ::EVP_EncryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, NULL, NULL);
    break;
  } // End of 'switch' statement
  loggers::get_instance().log_to_hexa("security_ecc::encrypt: _sym_key: ", _sym_key.data(), _sym_key.size());
  loggers::get_instance().log_to_hexa("security_ecc::encrypt: nonce: ", _nonce.data(), _nonce.size());
  // Set nonce length
  ::EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_IVLEN, _nonce.size(), NULL);
  // Set tag length
  ::EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_TAG, _tag.size(), NULL);
  // Prime the key and nonce
  ::EVP_EncryptInit_ex(ctx, NULL, NULL, _sym_key.data(), _nonce.data());
  // No authentication data
  // Encrypt the data
  int len = 0;
  ::EVP_EncryptUpdate(ctx, p_enc_message.data(), &len, p_message.data(), p_message.size());
  // Finalize the encryption session
  ::EVP_EncryptFinal_ex(ctx, p_enc_message.data() + len, &len);
  // Get the authentication tag
  ::EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_GET_TAG, _tag.size(), _tag.data());
garciay's avatar
garciay committed
  loggers::get_instance().log_to_hexa("security_ecc::encrypt: tag: ", _tag.data(), _tag.size());

  ::EVP_CIPHER_CTX_free(ctx);
  
  return 0;
}

garciay's avatar
garciay committed
int security_ecc::decrypt(const encryption_algotithm p_enc_algorithm, const std::vector<unsigned char>& p_key, const std::vector<unsigned char>& p_nonce, const std::vector<unsigned char>& p_tag, const std::vector<unsigned char>& p_enc_message, std::vector<unsigned char>& p_message) {
  loggers::get_instance().log(">>> security_ecc::decrypt: %d", p_enc_algorithm);

  _encryption_algotithm = p_enc_algorithm;
garciay's avatar
garciay committed
  _sym_key = p_key;
  _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(), NULL, NULL, NULL);
    break;
  case encryption_algotithm::aes_256_ccm:
    ::EVP_DecryptInit_ex(ctx, EVP_aes_256_ccm(), NULL, NULL, NULL);
    break;
  case encryption_algotithm::aes_128_gcm:
    ::EVP_DecryptInit_ex(ctx, EVP_aes_128_gcm(), NULL, NULL, NULL);
    break;
  case encryption_algotithm::aes_256_gcm:
    ::EVP_DecryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, NULL, NULL);
    break;
  } // End of 'switch' statement
  // Set nonce length
  EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_IVLEN, _nonce.size(), NULL);
  // Set expected tag value
  EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_TAG, _tag.size(), _tag.data());
  // Specify key and IV
garciay's avatar
garciay committed
  EVP_DecryptInit_ex(ctx, NULL, NULL, _sym_key.data(), _nonce.data());
  // Decrypt plaintext, verify tag: can only be called once
  p_message.resize(p_enc_message.size());
  int len = 0;
  int result = EVP_DecryptUpdate(ctx, p_message.data(), &len, p_enc_message.data(), p_enc_message.size());
  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;
}
garciay's avatar
garciay committed

int security_ecc::decrypt(const std::vector<unsigned char>& p_tag, const std::vector<unsigned char>& p_enc_message, std::vector<unsigned char>& p_message) {
  loggers::get_instance().log(">>> security_ecc::decrypt: %d", _encryption_algotithm);

  // Sanity checks
  if ((_pri_key.size() == 0) || (_secret_key.size() == 0)) {
garciay's avatar
garciay committed
    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(), NULL, NULL, NULL);
    break;
  case encryption_algotithm::aes_256_ccm:
    ::EVP_DecryptInit_ex(ctx, EVP_aes_256_ccm(), NULL, NULL, NULL);
    break;
  case encryption_algotithm::aes_128_gcm:
    ::EVP_DecryptInit_ex(ctx, EVP_aes_128_gcm(), NULL, NULL, NULL);
    break;
  case encryption_algotithm::aes_256_gcm:
    ::EVP_DecryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, NULL, NULL);
    break;
  } // End of 'switch' statement
  // Set nonce length
  EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_IVLEN, _nonce.size(), NULL);
  // Set expected tag value
  EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_TAG, _tag.size(), _tag.data());
  // Specify key and IV
  EVP_DecryptInit_ex(ctx, NULL, NULL, _sym_key.data(), _nonce.data());
  // Decrypt plaintext, verify tag: can only be called once
  p_message.resize(p_enc_message.size());
  int len = 0;
  int result = EVP_DecryptUpdate(ctx, p_message.data(), &len, p_enc_message.data(), p_enc_message.size());
  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;
}

garciay's avatar
garciay committed
int security_ecc::sign(const std::vector<unsigned char>& p_data, std::vector<unsigned char>& p_r_sig, std::vector<unsigned char>& p_s_sig) {
  loggers::get_instance().log(">>> security_ecc::sign");
garciay's avatar
garciay committed

  // Sanity checks
  if(_pri_key.size() == 0) { // No private key
garciay's avatar
garciay committed
    return -1;
  }
garciay's avatar
garciay committed
  if (p_data.size() == 0) {
    return -1;
  }
garciay's avatar
garciay committed
  
  ECDSA_SIG *signature = ::ECDSA_do_sign(p_data.data(), p_data.size(), _ec_key);
  if (signature == nullptr) {
garciay's avatar
garciay committed
    loggers::get_instance().warning("security_ecc::sign: Signature failed");
garciay's avatar
garciay committed
    return -1;
  }
garciay's avatar
garciay committed
  loggers::get_instance().log("security_ecc::sign: succeed");
garciay's avatar
garciay committed

  if (::ECDSA_do_verify(p_data.data(), p_data.size(), signature, _ec_key) != 1) {
garciay's avatar
garciay committed
    loggers::get_instance().warning("security_ecc::sign: Signature not verified");
garciay's avatar
garciay committed
    return -1;
  }
  
  p_r_sig.resize(BN_num_bytes(signature->r));
  ::BN_bn2bin(signature->r, p_r_sig.data());
garciay's avatar
garciay committed
  //loggers::get_instance().log_to_hexa("security_ecc::sign: r=", p_r_sig.data(), p_r_sig.size());
garciay's avatar
garciay committed
  p_s_sig.resize(BN_num_bytes(signature->r));
  ::BN_bn2bin(signature->s, p_s_sig.data());
garciay's avatar
garciay committed
  //loggers::get_instance().log_to_hexa("security_ecc::sign: s=", p_s_sig.data(), p_s_sig.size());
garciay's avatar
garciay committed

  ::ECDSA_SIG_free(signature);
  
  return 0;
}

garciay's avatar
garciay committed
int security_ecc::sign_verif(const std::vector<unsigned char>& p_data, const std::vector<unsigned char>& p_signature) {
  loggers::get_instance().log(">>> security_ecc::sign_verif");
garciay's avatar
garciay committed

  // Sanity checks
garciay's avatar
garciay committed
  if (p_data.size() == 0) {
    return false;
  }
garciay's avatar
garciay committed

  // Build the signature
  BIGNUM r, s;
  ::BN_init(&r);
  ::BN_init(&s);
  ::BN_bin2bn(p_signature.data(), p_signature.size() / 2, &r);
  ::BN_bin2bn(p_signature.data() + p_signature.size() / 2, p_signature.size() / 2, &s);
  ECDSA_SIG *signature = ECDSA_SIG_new();
  signature->r = &r;
  signature->s = &s;
garciay's avatar
garciay committed
  // Check the signature
garciay's avatar
garciay committed
  int result = ::ECDSA_do_verify(p_data.data(), p_data.size(), signature, _ec_key);
  ::ECDSA_SIG_free(signature);
garciay's avatar
garciay committed
  loggers::get_instance().log("security_ecc::sign_verif: %s", (result == 1) ? "succeed": "failed");
garciay's avatar
garciay committed
  return result != 1;
}
garciay's avatar
garciay committed
const int security_ecc::init() {
  loggers::get_instance().log(">>> security_ecc::init: %d", static_cast<int>(_elliptic_curve));

  ::OpenSSL_add_all_algorithms();
garciay's avatar
garciay committed
  ::ERR_load_crypto_strings();

garciay's avatar
garciay committed
  int result = -1;
garciay's avatar
garciay committed
  switch (_elliptic_curve) { // TODO Group this cde into a private  method
  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:
garciay's avatar
garciay committed
    loggers::get_instance().error("security_ecc::security_ecc: Unsupported EC elliptic_curve");
garciay's avatar
garciay committed
  } // End of 'switch' statement
  if (result < 0) {
garciay's avatar
garciay committed
    loggers::get_instance().warning("security_ecc::security_ecc: Unaible to set EC elliptic_curve");
    return -1;
  }

  _ec_key = ::EC_KEY_new_by_curve_name(result); // Set the elliptic curve
garciay's avatar
garciay committed
  ::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

garciay's avatar
garciay committed
int security_ecc::bin_to_ec_point(const std::vector<unsigned char>& p_public_key_x, const std::vector<unsigned char>& p_public_key_y, EC_POINT** p_ec_point) { // ec_key_public_key_bin_to_point
  BIGNUM* pubk_bn;

  std::vector<unsigned char> v(1, 0x04);
  v.insert(v.end(), std::make_move_iterator(p_public_key_x.begin()), std::make_move_iterator(p_public_key_x.end()));
  v.insert(v.end(), std::make_move_iterator(p_public_key_y.begin()), std::make_move_iterator(p_public_key_y.end()));

  pubk_bn = ::BN_bin2bn(v.data(), v.size(), NULL);
  *p_ec_point = ::EC_POINT_new(_ec_group);
  ::EC_POINT_bn2point(_ec_group, pubk_bn, *p_ec_point, _bn_ctx);
  
  // BIO            *bio_out  = NULL; /* 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, NULL)) {
  //   BN_print_fp(stdout, x);
  //   putc('\n', stdout);
  //   BN_print_fp(stdout, y);
  //   putc('\n', stdout);
  // }
  // BN_free(x); BN_free(y);
  
  return 0;
}

garciay's avatar
garciay committed
int security_ecc::public_key_to_bin(std::vector<unsigned char>& 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.resize(BN_num_bytes(pub_bn));
  ::BN_bn2bin(pub_bn, p_bin_key.data());

  ::BN_clear_free(pub_bn);

  return 0;
}

/*int security_ecc::multiply_point_with_bn(const EC_POINT& a, const BIGNUM& b, EC_POINT** P) {
  loggers::get_instance().log(">>> security_ecc::multiply_point_with_bn");

  EC_POINT *O = EC_POINT_new(_ec_group);
  if (*P == NULL) *P = EC_POINT_new(_ec_group);
  for(int i = BN_num_bits(&b); i >= 0; i--) {
    EC_POINT_dbl(_ec_group, *P, *P, _bn_ctx);
    if (BN_is_bit_set(&b, i))
      EC_POINT_add(_ec_group, *P, *P, &a, _bn_ctx);
    else
      EC_POINT_add(_ec_group, *P, *P, O, _bn_ctx);
  }

  EC_POINT_free(O);

  return 0;

int security_ecc::derive_s_from_private_key(BIGNUM* S, BIGNUM* R) {
	loggers::get_instance().log(">>> security_ecc::derive_s_from_private_key");

	const EC_POINT *Kb = EC_KEY_get0_public_key(_ec_key);
	BIGNUM *n = BN_new();
	BIGNUM *r = BN_new();
	EC_POINT *P = NULL;
	EC_POINT *Rp = EC_POINT_new(_ec_group);
	BIGNUM *Py = BN_new();
	const EC_POINT *G = EC_GROUP_get0_generator(_ec_group);
	int bits,ret=-1;
	EC_GROUP_get_order(_ec_group, n, _bn_ctx);
	bits = BN_num_bits(n);
	BN_rand(r, bits, -1, 0);
	// calculate R = rG 
	Rp = multiply_point_with_bn(Rp, G, r);
	// calculate S = Px, P = (Px,Py) = Kb R
	P = multiply_point_with_bn(P, Kb, r);
	if (!EC_POINT_is_at_infinity(_ec_group, P)) {
		EC_POINT_get_affine_coordinates_GFp(_ec_group, P, S, Py, _bn_ctx); // TODO Add 'switch'
		EC_POINT_point2bn(_ec_group, Rp, POINT_CONVERSION_COMPRESSED, R, _bn_ctx);
		ret = 0;
	}
	BN_free(r);
	BN_free(n);
	BN_free(Py);
	EC_POINT_free(P);
	EC_POINT_free(Rp);

	return ret;
}

int security_ecc::derive_s_from_public_key(BIGNUM* S, BIGNUM* R) {
	loggers::get_instance().log(">>> security_ecc::derive_s_from_public_key");

  return 0;
}*/