Commit 3f5dfd62 authored by Yann Garcia's avatar Yann Garcia
Browse files

Adding missing files

parent 6c8737a4
Loading
Loading
Loading
Loading

captures/TC_NGAP_GNB_PDU_01.pcap

deleted100644 → 0
−6.45 KiB

File deleted.

+249 −0
Changes for ccsrc/Protocols/FiveG_AKA/ecies.cc: 249 added lines, 0 removed lines.
Original line number Diff line number Diff line
#include "ecies.hh"

#include <openssl/bn.h>
#include <openssl/ec.h>
#include <openssl/evp.h>
#include <openssl/hmac.h>
#include <openssl/obj_mac.h>
#include <openssl/rand.h>

#include "loggers.hh"

int ecies::generate_private_key(const profile p_profile, std::vector<uint8_t>& p_private_key) {
  loggers::get_instance().log(">>> ecies::generate_private_key: %d", static_cast<int>(p_profile));

  p_private_key.resize(private_key_length);
  if (p_profile == profile::profile_a) {
    // Any 32 octets string is a valid X25519 private key (clamping is applied when the key is used)
    if (::RAND_bytes(p_private_key.data(), static_cast<int>(p_private_key.size())) != 1) {
      loggers::get_instance().warning("ecies::generate_private_key: RAND_bytes failure");
      return -1;
    }
    return 0;
  }

  // Profile B: draw until the scalar is in [1, n-1], n being the secp256r1 group order
  EC_GROUP* group = ::EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1);
  BIGNUM* d = ::BN_new();
  const BIGNUM* order = (group != nullptr) ? ::EC_GROUP_get0_order(group) : nullptr;
  int result = -1;
  if ((d != nullptr) && (order != nullptr)) {
    for (int i = 0; i < 16; i++) { // The probability to loop more than once is about 2^-32
      if (::RAND_bytes(p_private_key.data(), static_cast<int>(p_private_key.size())) != 1) {
        loggers::get_instance().warning("ecies::generate_private_key: RAND_bytes failure");
        break;
      }
      if ((::BN_bin2bn(p_private_key.data(), static_cast<int>(p_private_key.size()), d) != nullptr) && !::BN_is_zero(d) && (::BN_cmp(d, order) < 0)) {
        result = 0;
        break;
      }
    } // End of 'for' statement
  }
  ::BN_clear_free(d);
  ::EC_GROUP_free(group);
  if (result != 0) {
    loggers::get_instance().warning("ecies::generate_private_key: Failed to generate a secp256r1 private key");
  }
  return result;
}

int ecies::compute_public_key(const profile p_profile, const std::vector<uint8_t>& p_private_key, std::vector<uint8_t>& p_public_key) {
  loggers::get_instance().log_to_hexa(">>> ecies::compute_public_key: ", p_private_key.data(), p_private_key.size());

  if (p_private_key.size() != private_key_length) {
    loggers::get_instance().warning("ecies::compute_public_key: Wrong private key length: %d", static_cast<int>(p_private_key.size()));
    return -1;
  }

  int result = (p_profile == profile::profile_a) ? x25519_public_key(p_private_key, p_public_key) : secp256r1_public_key(p_private_key, p_public_key);
  if (result == 0) {
    loggers::get_instance().log_to_hexa("<<< ecies::compute_public_key: ", p_public_key.data(), p_public_key.size());
  }
  return result;
}

int ecies::compute_shared_key(const profile p_profile, const std::vector<uint8_t>& p_private_key, const std::vector<uint8_t>& p_peer_public_key, std::vector<uint8_t>& p_shared_key) {
  loggers::get_instance().log_to_hexa(">>> ecies::compute_shared_key: p_private_key: ", p_private_key.data(), p_private_key.size());
  loggers::get_instance().log_to_hexa(">>> ecies::compute_shared_key: p_peer_public_key: ", p_peer_public_key.data(), p_peer_public_key.size());

  if ((p_private_key.size() != private_key_length) || (p_peer_public_key.size() != public_key_length(p_profile))) {
    loggers::get_instance().warning("ecies::compute_shared_key: Wrong key length: %d/%d", static_cast<int>(p_private_key.size()), static_cast<int>(p_peer_public_key.size()));
    return -1;
  }

  int result = (p_profile == profile::profile_a) ? x25519_shared_key(p_private_key, p_peer_public_key, p_shared_key) : secp256r1_shared_key(p_private_key, p_peer_public_key, p_shared_key);
  if (result == 0) {
    loggers::get_instance().log_to_hexa("<<< ecies::compute_shared_key: ", p_shared_key.data(), p_shared_key.size());
  }
  return result;
}

int ecies::kdf(const std::vector<uint8_t>& p_shared_key, const std::vector<uint8_t>& p_shared_info, std::vector<uint8_t>& p_enc_key, std::vector<uint8_t>& p_icb, std::vector<uint8_t>& p_mac_key) {
  loggers::get_instance().log_to_hexa(">>> ecies::kdf: p_shared_key: ", p_shared_key.data(), p_shared_key.size());
  loggers::get_instance().log_to_hexa(">>> ecies::kdf: p_shared_info: ", p_shared_info.data(), p_shared_info.size());

  // ANSI-X9.63-KDF: K(i) = SHA-256(Z || Counter(i) || SharedInfo), with a 32 bits big-endian counter starting at 1
  // Two iterations provide the 64 octets required by both profiles: EncKey (16) || ICB (16) || MacKey (32)
  std::vector<uint8_t> output;
  for (uint32_t counter = 1; counter <= 2; counter++) {
    std::vector<uint8_t> input(p_shared_key);
    input.push_back(static_cast<uint8_t>((counter >> 24) & 0xff));
    input.push_back(static_cast<uint8_t>((counter >> 16) & 0xff));
    input.push_back(static_cast<uint8_t>((counter >> 8) & 0xff));
    input.push_back(static_cast<uint8_t>(counter & 0xff));
    input.insert(input.end(), p_shared_info.cbegin(), p_shared_info.cend());

    uint8_t hash[EVP_MAX_MD_SIZE];
    unsigned int hash_length = 0;
    if (::EVP_Digest(input.data(), input.size(), hash, &hash_length, EVP_sha256(), nullptr) != 1) {
      loggers::get_instance().warning("ecies::kdf: EVP_Digest failure");
      return -1;
    }
    output.insert(output.end(), hash, hash + hash_length);
  } // End of 'for' statement

  p_enc_key.assign(output.cbegin(), output.cbegin() + enc_key_length);
  p_icb.assign(output.cbegin() + enc_key_length, output.cbegin() + enc_key_length + icb_length);
  p_mac_key.assign(output.cbegin() + enc_key_length + icb_length, output.cbegin() + enc_key_length + icb_length + mac_key_length);

  loggers::get_instance().log_to_hexa("<<< ecies::kdf: p_enc_key: ", p_enc_key.data(), p_enc_key.size());
  loggers::get_instance().log_to_hexa("<<< ecies::kdf: p_icb: ", p_icb.data(), p_icb.size());
  loggers::get_instance().log_to_hexa("<<< ecies::kdf: p_mac_key: ", p_mac_key.data(), p_mac_key.size());
  return 0;
}

int ecies::aes_128_ctr(const std::vector<uint8_t>& p_enc_key, const std::vector<uint8_t>& p_icb, const std::vector<uint8_t>& p_input, std::vector<uint8_t>& p_output) {
  loggers::get_instance().log_to_hexa(">>> ecies::aes_128_ctr: p_input: ", p_input.data(), p_input.size());

  if ((p_enc_key.size() != enc_key_length) || (p_icb.size() != icb_length)) {
    loggers::get_instance().warning("ecies::aes_128_ctr: Wrong key/ICB length: %d/%d", static_cast<int>(p_enc_key.size()), static_cast<int>(p_icb.size()));
    return -1;
  }

  EVP_CIPHER_CTX* ctx = ::EVP_CIPHER_CTX_new();
  if (ctx == nullptr) {
    loggers::get_instance().warning("ecies::aes_128_ctr: EVP_CIPHER_CTX_new failure");
    return -1;
  }
  // CTR mode is symmetric: the same operation encrypts and decrypts
  p_output.resize(p_input.size());
  int length = 0;
  int final_length = 0;
  int result = -1;
  if ((::EVP_EncryptInit_ex(ctx, EVP_aes_128_ctr(), nullptr, p_enc_key.data(), p_icb.data()) == 1) &&
      (::EVP_EncryptUpdate(ctx, p_output.data(), &length, p_input.data(), static_cast<int>(p_input.size())) == 1) &&
      (::EVP_EncryptFinal_ex(ctx, p_output.data() + length, &final_length) == 1)) {
    result = 0;
  } else {
    loggers::get_instance().warning("ecies::aes_128_ctr: AES-128-CTR failure");
  }
  ::EVP_CIPHER_CTX_free(ctx);

  if (result == 0) {
    loggers::get_instance().log_to_hexa("<<< ecies::aes_128_ctr: p_output: ", p_output.data(), p_output.size());
  }
  return result;
}

int ecies::compute_mac_tag(const std::vector<uint8_t>& p_mac_key, const std::vector<uint8_t>& p_cipher_text, std::vector<uint8_t>& p_mac_tag) {
  loggers::get_instance().log_to_hexa(">>> ecies::compute_mac_tag: p_cipher_text: ", p_cipher_text.data(), p_cipher_text.size());

  if (p_mac_key.size() != mac_key_length) {
    loggers::get_instance().warning("ecies::compute_mac_tag: Wrong MAC key length: %d", static_cast<int>(p_mac_key.size()));
    return -1;
  }

  uint8_t hmac[EVP_MAX_MD_SIZE];
  unsigned int hmac_length = 0;
  if (::HMAC(EVP_sha256(), p_mac_key.data(), static_cast<int>(p_mac_key.size()), p_cipher_text.data(), p_cipher_text.size(), hmac, &hmac_length) == nullptr) {
    loggers::get_instance().warning("ecies::compute_mac_tag: HMAC failure");
    return -1;
  }
  p_mac_tag.assign(hmac, hmac + mac_tag_length); // Keep the 64 most significant bits

  loggers::get_instance().log_to_hexa("<<< ecies::compute_mac_tag: ", p_mac_tag.data(), p_mac_tag.size());
  return 0;
}

int ecies::x25519_public_key(const std::vector<uint8_t>& p_private_key, std::vector<uint8_t>& p_public_key) {
  EVP_PKEY* pkey = ::EVP_PKEY_new_raw_private_key(EVP_PKEY_X25519, nullptr, p_private_key.data(), p_private_key.size());
  if (pkey == nullptr) {
    loggers::get_instance().warning("ecies::x25519_public_key: EVP_PKEY_new_raw_private_key failure");
    return -1;
  }
  size_t length = 32;
  p_public_key.resize(length);
  int result = (::EVP_PKEY_get_raw_public_key(pkey, p_public_key.data(), &length) == 1) ? 0 : -1;
  ::EVP_PKEY_free(pkey);
  if (result != 0) {
    loggers::get_instance().warning("ecies::x25519_public_key: EVP_PKEY_get_raw_public_key failure");
  }
  return result;
}

int ecies::x25519_shared_key(const std::vector<uint8_t>& p_private_key, const std::vector<uint8_t>& p_peer_public_key, std::vector<uint8_t>& p_shared_key) {
  EVP_PKEY* pkey = ::EVP_PKEY_new_raw_private_key(EVP_PKEY_X25519, nullptr, p_private_key.data(), p_private_key.size());
  EVP_PKEY* peer = ::EVP_PKEY_new_raw_public_key(EVP_PKEY_X25519, nullptr, p_peer_public_key.data(), p_peer_public_key.size());
  EVP_PKEY_CTX* ctx = (pkey != nullptr) ? ::EVP_PKEY_CTX_new(pkey, nullptr) : nullptr;
  int result = -1;
  size_t length = shared_key_length;
  p_shared_key.resize(length);
  if ((peer != nullptr) && (ctx != nullptr) &&
      (::EVP_PKEY_derive_init(ctx) == 1) &&
      (::EVP_PKEY_derive_set_peer(ctx, peer) == 1) &&
      (::EVP_PKEY_derive(ctx, p_shared_key.data(), &length) == 1)) {
    result = 0;
  } else {
    loggers::get_instance().warning("ecies::x25519_shared_key: X25519 derivation failure");
  }
  ::EVP_PKEY_CTX_free(ctx);
  ::EVP_PKEY_free(peer);
  ::EVP_PKEY_free(pkey);
  return result;
}

int ecies::secp256r1_public_key(const std::vector<uint8_t>& p_private_key, std::vector<uint8_t>& p_public_key) {
  EC_GROUP* group = ::EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1);
  BIGNUM* d = ::BN_bin2bn(p_private_key.data(), static_cast<int>(p_private_key.size()), nullptr);
  EC_POINT* q = (group != nullptr) ? ::EC_POINT_new(group) : nullptr;
  int result = -1;
  p_public_key.resize(33);
  if ((d != nullptr) && (q != nullptr) &&
      (::EC_POINT_mul(group, q, d, nullptr, nullptr, nullptr) == 1) &&
      (::EC_POINT_point2oct(group, q, POINT_CONVERSION_COMPRESSED, p_public_key.data(), p_public_key.size(), nullptr) == p_public_key.size())) {
    result = 0;
  } else {
    loggers::get_instance().warning("ecies::secp256r1_public_key: Public key computation failure");
  }
  ::EC_POINT_free(q);
  ::BN_clear_free(d);
  ::EC_GROUP_free(group);
  return result;
}

int ecies::secp256r1_shared_key(const std::vector<uint8_t>& p_private_key, const std::vector<uint8_t>& p_peer_public_key, std::vector<uint8_t>& p_shared_key) {
  // ECDH primitive (SECG SEC 1 ECSVDP-DH): Z is the x-coordinate of d * Q
  EC_GROUP* group = ::EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1);
  BIGNUM* d = ::BN_bin2bn(p_private_key.data(), static_cast<int>(p_private_key.size()), nullptr);
  BIGNUM* x = ::BN_new();
  EC_POINT* peer = (group != nullptr) ? ::EC_POINT_new(group) : nullptr;
  EC_POINT* shared = (group != nullptr) ? ::EC_POINT_new(group) : nullptr;
  int result = -1;
  p_shared_key.resize(shared_key_length);
  if ((d != nullptr) && (x != nullptr) && (peer != nullptr) && (shared != nullptr) &&
      (::EC_POINT_oct2point(group, peer, p_peer_public_key.data(), p_peer_public_key.size(), nullptr) == 1) && // Also checks that the point is on the curve
      (::EC_POINT_mul(group, shared, nullptr, peer, d, nullptr) == 1) &&
      (::EC_POINT_is_at_infinity(group, shared) == 0) &&
      (::EC_POINT_get_affine_coordinates(group, shared, x, nullptr, nullptr) == 1) &&
      (::BN_bn2binpad(x, p_shared_key.data(), static_cast<int>(p_shared_key.size())) == static_cast<int>(shared_key_length))) {
    result = 0;
  } else {
    loggers::get_instance().warning("ecies::secp256r1_shared_key: ECDH failure");
  }
  ::EC_POINT_free(shared);
  ::EC_POINT_free(peer);
  ::BN_free(x);
  ::BN_clear_free(d);
  ::EC_GROUP_free(group);
  return result;
}
+100 −0
Changes for ccsrc/Protocols/FiveG_AKA/ecies.hh: 100 added lines, 0 removed lines.
Original line number Diff line number Diff line
#pragma once

#include <cstddef>
#include <cstdint>
#include <vector>

/*!
 * \class ecies
 * \brief  Implement the SUCI concealment primitives of the Elliptic Curve Integrated Encryption Scheme (ECIES)
 *         as defined in ETSI TS 133 501 Annex C.3.4 (Profile A and Profile B)
 * \remark Profile A: Curve25519/X25519, raw 32 bytes public key
 *         Profile B: secp256r1, compressed 33 bytes public key (point compression is mandatory for the UE)
 *         Both profiles: ANSI-X9.63-KDF with SHA-256, AES-128 in CTR mode, HMAC-SHA-256 truncated to 64 bits
 * \see    ETSI TS 133 501 Annex C.3.4 ECIES profiles
 * \see    ETSI TS 133 501 Annex C.4 Test data for ECIES profiles
 */
class ecies {
public:
  enum class profile : uint8_t {
    profile_a = 1, /*!< Protection scheme identifier 0x1: Curve25519 */
    profile_b = 2  /*!< Protection scheme identifier 0x2: secp256r1 */
  }; // End of enum class profile

  static const size_t private_key_length   = 32; //<! Private key length, both profiles
  static const size_t shared_key_length    = 32; //<! Shared key (Z) length, both profiles
  static const size_t enc_key_length       = 16; //<! AES-128 encryption key length
  static const size_t icb_length           = 16; //<! AES-128 CTR initial counter block length
  static const size_t mac_key_length       = 32; //<! HMAC-SHA-256 key length
  static const size_t mac_tag_length       = 8;  //<! MAC tag length (64 bits)

  /*!
   * \brief Provide the public key length for the specified profile
   * \return 32 for Profile A, 33 (compressed point) for Profile B
   */
  static size_t public_key_length(const profile p_profile) { return (p_profile == profile::profile_a) ? 32 : 33; };

  /*!
   * \brief Generate a fresh ephemeral private key using the OpenSSL CSPRNG
   * \param[in] p_profile The ECIES profile
   * \param[out] p_private_key The private key. For Profile B, it is in the range [1, n-1]
   * \return 0 on success, -1 otherwise
   */
  static int generate_private_key(const profile p_profile, std::vector<uint8_t>& p_private_key);

  /*!
   * \brief Derive the public key from a private key
   * \param[in] p_profile The ECIES profile
   * \param[in] p_private_key The private key
   * \param[out] p_public_key The public key (compressed point for Profile B)
   * \return 0 on success, -1 otherwise
   */
  static int compute_public_key(const profile p_profile, const std::vector<uint8_t>& p_private_key, std::vector<uint8_t>& p_public_key);

  /*!
   * \brief Compute the ECDH shared key Z. On the UE side, use the ephemeral private key and the home network public key.
   *        On the home network side, use the home network private key and the ephemeral public key
   * \param[in] p_profile The ECIES profile
   * \param[in] p_private_key The own private key
   * \param[in] p_peer_public_key The peer public key (compressed point for Profile B)
   * \param[out] p_shared_key The shared key Z (the x-coordinate for Profile B)
   * \return 0 on success, -1 otherwise
   */
  static int compute_shared_key(const profile p_profile, const std::vector<uint8_t>& p_private_key, const std::vector<uint8_t>& p_peer_public_key, std::vector<uint8_t>& p_shared_key);

  /*!
   * \brief Derive the ECIES keys using ANSI-X9.63-KDF with SHA-256
   * \param[in] p_shared_key The shared key Z
   * \param[in] p_shared_info The SharedInfo, i.e. the ephemeral public key
   * \param[out] p_enc_key The encryption key (the 16 first octets of the KDF output)
   * \param[out] p_icb The initial counter block (the 16 next octets of the KDF output)
   * \param[out] p_mac_key The MAC key (the 32 last octets of the KDF output)
   * \return 0 on success, -1 otherwise
   */
  static int kdf(const std::vector<uint8_t>& p_shared_key, const std::vector<uint8_t>& p_shared_info, std::vector<uint8_t>& p_enc_key, std::vector<uint8_t>& p_icb, std::vector<uint8_t>& p_mac_key);

  /*!
   * \brief Encrypt or decrypt using AES-128 in CTR mode
   * \param[in] p_enc_key The encryption key
   * \param[in] p_icb The initial counter block
   * \param[in] p_input The plain text to encrypt or the cipher text to decrypt
   * \param[out] p_output The cipher text or the plain text
   * \return 0 on success, -1 otherwise
   */
  static int aes_128_ctr(const std::vector<uint8_t>& p_enc_key, const std::vector<uint8_t>& p_icb, const std::vector<uint8_t>& p_input, std::vector<uint8_t>& p_output);

  /*!
   * \brief Compute the MAC tag, i.e. HMAC-SHA-256 truncated to 64 bits
   * \param[in] p_mac_key The MAC key
   * \param[in] p_cipher_text The cipher text
   * \param[out] p_mac_tag The MAC tag
   * \return 0 on success, -1 otherwise
   */
  static int compute_mac_tag(const std::vector<uint8_t>& p_mac_key, const std::vector<uint8_t>& p_cipher_text, std::vector<uint8_t>& p_mac_tag);

private:
  static int x25519_public_key(const std::vector<uint8_t>& p_private_key, std::vector<uint8_t>& p_public_key);
  static int x25519_shared_key(const std::vector<uint8_t>& p_private_key, const std::vector<uint8_t>& p_peer_public_key, std::vector<uint8_t>& p_shared_key);
  static int secp256r1_public_key(const std::vector<uint8_t>& p_private_key, std::vector<uint8_t>& p_public_key);
  static int secp256r1_shared_key(const std::vector<uint8_t>& p_private_key, const std::vector<uint8_t>& p_peer_public_key, std::vector<uint8_t>& p_shared_key);
}; // End of class ecies
Changes for titan-test-system-framework: 1 added line, 1 removed line.
Original line number Diff line number Diff line
Subproject commit 1c29d89de72fb9a78db8dd3daabe0ab0f261a699
Subproject commit 3233becf6dbad531ba5d2c9151f4fe436722450d