lib_its_security.c 79.9 KB
Newer Older
  show_hex((const int8_t*)">>> sign_with_ecdsa_nistp256_with_sha256: p_data", p_to_be_signed_secured_message, p_to_be_signed_secured_message_length);
  show_hex((const int8_t*)">>> sign_with_ecdsa_nistp256_with_sha256: p_certificate_issuer", p_certificate_issuer, p_lib_its_security_context->key_length);
  show_hex((const int8_t*)">>> sign_with_ecdsa_nistp256_with_sha256: p_private_key", p_private_key, p_lib_its_security_context->key_length);

  lib_its_security_context_t* lib_its_security_context;
  if (initialize_with_private_key(nist_p_256, p_private_key, &lib_its_security_context) == -1){
    return -1;
  }

  int32_t result = generic_signature(lib_its_security_context, p_to_be_signed_secured_message, p_to_be_signed_secured_message_length, p_certificate_issuer, p_private_key, p_signature);
  uninitialize(&lib_its_security_context);

  return result;
}

int32_t sign_with_ecdsa_brainpoolp256r1_with_sha256(
                                                    lib_its_security_context_t* p_lib_its_security_context, // FIXME To be removed or remove p_private_key
                                                    const uint8_t* p_to_be_signed_secured_message,
                                                    const size_t p_to_be_signed_secured_message_length,
                                                    const uint8_t* p_certificate_issuer,
                                                    const uint8_t* p_private_key,
                                                    uint8_t** p_signature
                                                    ) {
  // Sanity checks
  if ((p_lib_its_security_context == NULL) || (p_to_be_signed_secured_message == NULL) || (p_private_key == NULL)) {
    return -1;
  }

  lib_its_security_context_t* lib_its_security_context;
  if (initialize_with_private_key(brainpool_p_256_r1, p_private_key, &lib_its_security_context) == -1){
    return -1;
  }

  int32_t result = generic_signature(lib_its_security_context, p_to_be_signed_secured_message, p_to_be_signed_secured_message_length, p_certificate_issuer, p_private_key, p_signature);
  uninitialize(&lib_its_security_context);

  return result;
}

int32_t sign_with_ecdsa_brainpoolp384r1_with_sha384(
                                                    lib_its_security_context_t* p_lib_its_security_context, // FIXME To be removed or remove p_private_key
                                                    const uint8_t* p_to_be_signed_secured_message,
                                                    const size_t p_to_be_signed_secured_message_length,
                                                    const uint8_t* p_certificate_issuer,
                                                    const uint8_t* p_private_key,
                                                    uint8_t** p_signature
                                                    ) {
  // Sanity checks
  if ((p_lib_its_security_context == NULL) || (p_to_be_signed_secured_message == NULL) || (p_private_key == NULL)) {
    return -1;
  }

  lib_its_security_context_t* lib_its_security_context;
  if (initialize_with_private_key(brainpool_p_384_r1, p_private_key, &lib_its_security_context) == -1){
    return -1;
  }

  int32_t result = generic_signature(lib_its_security_context, p_to_be_signed_secured_message, p_to_be_signed_secured_message_length, p_certificate_issuer, p_private_key, p_signature);
  uninitialize(&lib_its_security_context);

  return result;
}

int32_t verify_with_ecdsa_nistp256_with_sha256(
                                               lib_its_security_context_t* p_lib_its_security_context, // FIXME To be removed or remove p_ecdsa_nistp256_publicKey_compressed
                                               const uint8_t* p_to_be_verified_data,
                                               const size_t p_to_be_verified_data_length,
                                               const uint8_t* p_certificate_issuer,
                                               const uint8_t* p_signature,
                                               const uint8_t* p_ecdsa_nistp256_publicKey_compressed,
                                               const ecc_compressed_mode_t p_compressed_mode
                                               ) {
  
  show_hex((const int8_t*)">>> verify_with_ecdsa_nistp256_with_sha256: p_to_be_verified_data=", p_to_be_verified_data, p_to_be_verified_data_length);
  
  // Sanity checks
  if ((p_lib_its_security_context == NULL) || (p_to_be_verified_data == NULL) || (p_signature == NULL) || (p_ecdsa_nistp256_publicKey_compressed == NULL)) {
    return -1;
  }

  lib_its_security_context_t* lib_its_security_context;
  if (initialize_with_public_key(nist_p_256, p_ecdsa_nistp256_publicKey_compressed, p_compressed_mode, &lib_its_security_context) == -1){
    return -1;
  }

  int32_t result = generic_verify(lib_its_security_context, p_to_be_verified_data, p_to_be_verified_data_length, p_certificate_issuer, p_signature, p_ecdsa_nistp256_publicKey_compressed, p_compressed_mode);
  uninitialize(&lib_its_security_context);

  return result;
}

int32_t verify_with_ecdsa_nistp256_with_sha256_raw(
                                                   lib_its_security_context_t* p_lib_its_security_context, // FIXME To be removed or remove p_ecdsa_nistp256_publicKey_compressed
                                                   const uint8_t* p_to_be_verified_data,
                                                   const size_t p_to_be_verified_data_length,
                                                   const uint8_t* p_signature,
                                                   const uint8_t* p_ecdsa_nistp256_publicKey_compressed,
                                                   const ecc_compressed_mode_t p_compressed_mode
                                                   ) { return -1; }

int32_t verify_with_ecdsa_brainpoolp256r1_with_sha256(
                                                      lib_its_security_context_t* p_lib_its_security_context, // FIXME To be removed or remove p_ecdsaBrainpoolp256PublicKeyCompressed
                                                      const uint8_t* p_to_be_verified_data,
                                                      const size_t p_to_be_verified_data_length,
                                                      const uint8_t* p_certificate_issuer,
                                                      const uint8_t* p_signature,
                                                      const uint8_t* p_ecdsaBrainpoolp256PublicKeyCompressed,
                                                      const ecc_compressed_mode_t p_compressed_mode
                                                      ) {
  show_hex((const int8_t*)">>> verify_with_ecdsa_brainpoolp256r1_with_sha256: p_to_be_verified_data=", p_to_be_verified_data, p_to_be_verified_data_length);
  show_hex((const int8_t*)">>> verify_with_ecdsa_brainpoolp256r1_with_sha256: p_certificate_issuer=", p_certificate_issuer, 32);
  show_hex((const int8_t*)">>> verify_with_ecdsa_brainpoolp256r1_with_sha256: p_signature=", p_signature, 64);
  show_hex((const int8_t*)">>> verify_with_ecdsa_brainpoolp256r1_with_sha256: p_ecdsaBrainpoolp256PublicKeyCompressed=", p_ecdsaBrainpoolp256PublicKeyCompressed, 32);

    // Sanity checks
  if ((p_lib_its_security_context == NULL) || (p_to_be_verified_data == NULL) || (p_signature == NULL) || (p_ecdsaBrainpoolp256PublicKeyCompressed == NULL)) {
    return -1;
  }

  lib_its_security_context_t* lib_its_security_context;
  if (initialize_with_public_key(brainpool_p_256_r1, p_ecdsaBrainpoolp256PublicKeyCompressed, p_compressed_mode, &lib_its_security_context) == -1){
    return -1;
  }

  int32_t result = generic_verify(lib_its_security_context, p_to_be_verified_data, p_to_be_verified_data_length, p_certificate_issuer, p_signature, p_ecdsaBrainpoolp256PublicKeyCompressed, p_compressed_mode);
  uninitialize(&lib_its_security_context);

  return result;
}

int32_t verify_with_ecdsa_brainpoolp384r1_with_sha384(
                                                      lib_its_security_context_t* p_lib_its_security_context, // FIXME To be removed or remove p_ecdsaBrainpoolp384PublicKeyCompressed
                                                      const uint8_t* p_to_be_verified_data,
                                                      const size_t p_to_be_verified_data_length,
                                                      const uint8_t* p_certificate_issuer,
                                                      const uint8_t* p_signature,
                                                      const uint8_t* p_ecdsaBrainpoolp384PublicKeyCompressed,
                                                      const ecc_compressed_mode_t p_compressed_mode
                                                      ) {
  show_hex((const int8_t*)">>> verify_with_ecdsa_brainpoolp384r1_with_sha384: p_to_be_verified_data=", p_to_be_verified_data, p_to_be_verified_data_length);

  // Sanity checks
  if ((p_lib_its_security_context == NULL) || (p_to_be_verified_data == NULL) || (p_signature == NULL) || (p_ecdsaBrainpoolp384PublicKeyCompressed == NULL)) {
    return -1;
  }

  lib_its_security_context_t* lib_its_security_context;
  if (initialize_with_public_key(brainpool_p_384_r1, p_ecdsaBrainpoolp384PublicKeyCompressed, p_compressed_mode, &lib_its_security_context) == -1){
    return -1;
  }

  int32_t result = generic_verify(lib_its_security_context, p_to_be_verified_data, p_to_be_verified_data_length, p_certificate_issuer, p_signature, p_ecdsaBrainpoolp384PublicKeyCompressed, p_compressed_mode);
  uninitialize(&lib_its_security_context);

  return result;
}

int32_t encrypt_with_ecies_nistp256_with_sha256(
                                                lib_its_security_context_t* p_lib_its_security_context,
                                                const uint8_t* p_to_be_encrypted_secured_message,
                                                const size_t p_to_be_encrypted_secured_message_length,
                                                const uint8_t* p_recipients_public_key_compressed,
                                                const ecc_compressed_mode_t p_compressed_mode,
                                                const uint8_t* p_salt,
                                                const size_t p_salt_length,
                                                uint8_t** p_public_ephemeral_key_compressed,
                                                ecc_compressed_mode_t* p_ephemeral_compressed_mode,
                                                uint8_t** p_aes_sym_key,
                                                uint8_t** p_encrypted_sym_key,
                                                uint8_t** p_authentication_vector,
                                                uint8_t** p_nonce,
                                                uint8_t** p_encrypted_secured_message,
                                                size_t* p_encrypted_secured_message_length
                                                ) {
  show_hex((const int8_t*)">>> encrypt_with_ecies_nistp256_with_sha256: p_to_be_encrypted_secured_message=", p_to_be_encrypted_secured_message, p_to_be_encrypted_secured_message_length);
  show_hex((const int8_t*)">>> encrypt_with_ecies_nistp256_with_sha256: p_recipients_public_key_compressed=", p_recipients_public_key_compressed, 32);
  show_hex((const int8_t*)">>> encrypt_with_ecies_nistp256_with_sha256: p_salt=", p_salt, p_salt_length);
    /* Sanity checks */

  lib_its_security_context_t* lib_its_security_context = NULL;
  lib_its_security_context_t* lib_its_security_context_comp = NULL; /* Convert compressed key into XY-coordinates key */

  /* 1. Generate new Private/Public Ephemeral key */
  int32_t result = initialize(nist_p_256, &lib_its_security_context);
  if (result == -1) {
    goto end;
  }
  result = generate_key_pair(lib_its_security_context, &lib_its_security_context->private_key, &lib_its_security_context->public_key_x, &lib_its_security_context->public_key_y, &lib_its_security_context->public_key_c, &lib_its_security_context->compressed_mode);
  if (result == -1) {
    goto end;
  }
  show_hex((const int8_t*)"encrypt_with_ecies_nistp256_with_sha256: Ephemeral key compressed=", lib_its_security_context->public_key_c, lib_its_security_context->key_length);

  /* 2. Generate and derive shared secret based on recipient's public keys */
  result = initialize_with_public_key(nist_p_256, p_recipients_public_key_compressed, p_compressed_mode, &lib_its_security_context_comp);
  if (result == -1) {
    goto end;
  }
  result = generate_and_derive_ephemeral_key_for_encryption(lib_its_security_context/*Ephemeral's private key*/, aes_128_ccm, lib_its_security_context_comp/*recipient's public keys*/, p_salt, p_salt_length);
  if (result == -1) {
    goto end;
  }
  /* Set the AES symmetric key */
  *p_aes_sym_key = (uint8_t*)malloc(lib_its_security_context->sym_key_length);
  memcpy((void*)*p_aes_sym_key, (const void*)lib_its_security_context->sym_key, lib_its_security_context->sym_key_length);
  /* Set the encrypted symmetric key */
  *p_encrypted_sym_key = (uint8_t*)malloc(lib_its_security_context->sym_key_length);
  memcpy((void*)*p_encrypted_sym_key, (const void*)lib_its_security_context->enc_sym_key, lib_its_security_context->sym_key_length);
  /* Set the tag of the symmetric key encryption */
  *p_authentication_vector = (uint8_t*)malloc(lib_its_security_context->tag_length);
  memcpy((void*)*p_authentication_vector, (const void*)lib_its_security_context->tag, lib_its_security_context->tag_length);
  /* Set ephemeral public keys */
  *p_public_ephemeral_key_compressed = (uint8_t*)malloc(lib_its_security_context->key_length);
  memcpy((void*)*p_public_ephemeral_key_compressed, (const void*)lib_its_security_context->public_key_c, lib_its_security_context->key_length);
  *p_ephemeral_compressed_mode = (ecc_compressed_mode_t)(lib_its_security_context->compressed_mode == compressed_y_0) ? 0 : 1;
  show_hex((const int8_t*)"p_public_ephemeral_key_compressed", *p_public_ephemeral_key_compressed, lib_its_security_context->key_length);

  /* 3. Retrieve AES 128 parameters */
  *p_nonce = (uint8_t*)malloc(lib_its_security_context->nonce_length);
  memcpy((void*)*p_nonce, (const void*)lib_its_security_context->nonce, lib_its_security_context->nonce_length);
  /* 4. Encrypt the data using AES-128 CCM */
  lib_its_security_context->encryption_algorithm = aes_128_ccm;
  result = encrypt_(lib_its_security_context, p_to_be_encrypted_secured_message, p_to_be_encrypted_secured_message_length, p_encrypted_secured_message, p_encrypted_secured_message_length);
  if (result == -1) {
    // FXIME free all allocated resources
    free(*p_aes_sym_key); *p_aes_sym_key = NULL;
    free(*p_encrypted_sym_key); *p_encrypted_sym_key = NULL;
    free(*p_authentication_vector); *p_authentication_vector = NULL;
    free(*p_public_ephemeral_key_compressed); *p_public_ephemeral_key_compressed = NULL;
    free(*p_nonce); *p_nonce = NULL;
    goto end;
  }
  show_hex((const int8_t*)"Raw encrypted message", *p_encrypted_secured_message, *p_encrypted_secured_message_length);
  show_hex((const int8_t*)"tag", lib_its_security_context->tag, lib_its_security_context->tag_length);
  *p_encrypted_secured_message = (uint8_t*)realloc((void*)*p_encrypted_secured_message, *p_encrypted_secured_message_length + lib_its_security_context->tag_length);
  memcpy((void*)(*p_encrypted_secured_message + *p_encrypted_secured_message_length), (const void*)lib_its_security_context->tag, lib_its_security_context->tag_length);
  *p_encrypted_secured_message_length += lib_its_security_context->tag_length;
  fprintf(stderr, "p_encrypted_secured_message_length = %ld\n", *p_encrypted_secured_message_length);
  show_hex((const int8_t*)"p_encrypted_secured_message", *p_encrypted_secured_message, *p_encrypted_secured_message_length);

  result = 0;
 end:
  if (lib_its_security_context != NULL) uninitialize(&lib_its_security_context);
  if (lib_its_security_context_comp != NULL) uninitialize(&lib_its_security_context_comp);

  return result;
}

int32_t decrypt_with_ecies_nistp256_with_sha256(
                                                lib_its_security_context_t* p_lib_its_security_context,
                                                const uint8_t* p_encrypted_secured_message,
                                                const size_t p_encrypted_secured_message_length,
                                                const uint8_t* p_private_enc_key,
                                                const uint8_t* p_public_ephemeral_key_compressed,
                                                const ecc_compressed_mode_t p_ephemeral_compressed_mode,
                                                const uint8_t* p_encrypted_sym_key,
                                                const uint8_t* p_authentication_vector,
                                                const uint8_t* p_nonce,
                                                const uint8_t* p_salt,
                                                const size_t p_salt_length,
                                                uint8_t** p_aes_sym_enc_key,
                                                uint8_t** p_plain_text_message,
                                                size_t* p_plain_text_message_length
                                                ) {
  /* Sanity checks */

  lib_its_security_context_t* lib_its_security_context = NULL;
  lib_its_security_context_t* lib_its_security_context_comp = NULL; /* Convert compressed key into XY-coordinates key */
  size_t enc_message_length = 0;
  uint8_t* enc_message = NULL;

  /* 1. Initialize security context based on recipient's private key */
  int32_t result = initialize_with_private_key(nist_p_256, p_private_enc_key, &lib_its_security_context);
  if (result == -1) {
    goto end;
  }

  /* 2. Generate the shared secret value based on public ephemeral keys will be required */
  result = initialize_with_public_key(nist_p_256, p_public_ephemeral_key_compressed, p_ephemeral_compressed_mode, &lib_its_security_context_comp);
  if (result == -1) {
    goto end;
  }
  result = generate_and_derive_ephemeral_key_for_decryption(lib_its_security_context, aes_128_ccm, lib_its_security_context_comp, p_encrypted_sym_key, p_nonce, p_authentication_vector, p_salt, p_salt_length);
  if (result == -1) {
    goto end;
  }
  *p_aes_sym_enc_key = (uint8_t*)malloc(lib_its_security_context->sym_key_length);
  memcpy((void*)*p_aes_sym_enc_key, (const void*)lib_its_security_context->enc_sym_key, lib_its_security_context->sym_key_length);
  /* Decrypt the message */
  enc_message_length = p_encrypted_secured_message_length - lib_its_security_context->tag_length;
  enc_message = (uint8_t*)malloc(enc_message_length);
  memcpy((void*)enc_message, (const void*)p_encrypted_secured_message, enc_message_length); // Extract the encrypted message
  memcpy((void*)lib_its_security_context->tag, (const void*)(p_encrypted_secured_message + enc_message_length), lib_its_security_context->tag_length);
  show_hex((const int8_t*)"Raw encrypted message", enc_message, enc_message_length);
  show_hex((const int8_t*)"sym_key", lib_its_security_context->sym_key, lib_its_security_context->sym_key_length);
  show_hex((const int8_t*)"nonce", lib_its_security_context->nonce, lib_its_security_context->nonce_length);
  show_hex((const int8_t*)"tag", lib_its_security_context->tag, lib_its_security_context->tag_length);
  result = decrypt(lib_its_security_context, enc_message, enc_message_length, p_plain_text_message, p_plain_text_message_length);
  if (result == -1) {
    free(enc_message);
    *p_plain_text_message = NULL;
    *p_plain_text_message_length = -1;
    goto end;
  }
  free(enc_message);

 end:
  if (lib_its_security_context != NULL) uninitialize(&lib_its_security_context);
  if (lib_its_security_context_comp != NULL) uninitialize(&lib_its_security_context_comp);

  return result;
}

int32_t encrypt_with_ecies_brainpoolp256r1_with_sha256(
                                                       lib_its_security_context_t* p_lib_its_security_context,
                                                       const uint8_t* p_to_be_encrypted_secured_message,
                                                       const size_t p_to_be_encrypted_secured_message_length,
                                                       const uint8_t* p_recipients_public_key_compressed,
                                                       const ecc_compressed_mode_t p_compressed_mode,
                                                       const uint8_t* p_salt,
                                                       const size_t p_salt_length,
                                                       uint8_t** p_public_ephemeral_key_compressed,
                                                       ecc_compressed_mode_t* p_ephemeral_compressed_mode,
                                                       uint8_t** p_aes_sym_key,
                                                       uint8_t** p_encrypted_sym_key,
                                                       uint8_t** p_authentication_vector,
                                                       uint8_t** p_nonce,
                                                       uint8_t** p_encrypted_secured_message,
                                                       size_t* p_encrypted_secured_message_length
                                                       ) {
  /* Sanity checks */

  lib_its_security_context_t* lib_its_security_context = NULL;
  lib_its_security_context_t* lib_its_security_context_comp = NULL; /* Convert compressed key into XY-coordinates key */

  /* 1. Generate new Private/Public Ephemeral key */
  int32_t result = initialize(brainpool_p_256_r1, &lib_its_security_context);
  if (result == -1) {
    goto end;
  }
  result = generate_key_pair(lib_its_security_context, &lib_its_security_context->private_key, &lib_its_security_context->public_key_x, &lib_its_security_context->public_key_y, &lib_its_security_context->public_key_c, &lib_its_security_context->compressed_mode);
  if (result == -1) {
    goto end;
  }

  /* 2. Generate and derive shared secret based on recipient's public keys */
  result = initialize_with_public_key(brainpool_p_256_r1, p_recipients_public_key_compressed, p_compressed_mode, &lib_its_security_context_comp);
  if (result == -1) {
    goto end;
  }
  result = generate_and_derive_ephemeral_key_for_encryption(lib_its_security_context/*Ephemeral's private key*/, aes_128_ccm, lib_its_security_context_comp/*recipient's public keys*/, p_salt, p_salt_length);
  if (result == -1) {
    goto end;
  }
  /* Set the AES symmetric key */
  *p_aes_sym_key = (uint8_t*)malloc(lib_its_security_context->sym_key_length);
  memcpy((void*)*p_aes_sym_key, (const void*)lib_its_security_context->sym_key, lib_its_security_context->sym_key_length);
  /* Set the encrypted symmetric key */
  *p_encrypted_sym_key = (uint8_t*)malloc(lib_its_security_context->sym_key_length);
  memcpy((void*)*p_encrypted_sym_key, (const void*)lib_its_security_context->enc_sym_key, lib_its_security_context->sym_key_length);
  /* Set the tag of the symmetric key encryption */
  *p_authentication_vector = (uint8_t*)malloc(lib_its_security_context->tag_length);
  memcpy((void*)*p_authentication_vector, (const void*)lib_its_security_context->tag, lib_its_security_context->tag_length);
  /* Set ephemeral public keys */
  *p_public_ephemeral_key_compressed = (uint8_t*)malloc(lib_its_security_context->key_length);
  memcpy((void*)*p_public_ephemeral_key_compressed, (const void*)lib_its_security_context->public_key_c, lib_its_security_context->key_length);
  *p_ephemeral_compressed_mode = (ecc_compressed_mode_t)(lib_its_security_context->compressed_mode == compressed_y_0) ? 0 : 1;

  /* 3. Retrieve AES 128 parameters */
  *p_nonce = (uint8_t*)malloc(lib_its_security_context->nonce_length);
  memcpy((void*)*p_nonce, (const void*)lib_its_security_context->nonce, lib_its_security_context->nonce_length);
  /* 4. Encrypt the data using AES-128 CCM */
  lib_its_security_context->encryption_algorithm = aes_128_ccm;
  result = encrypt_(lib_its_security_context, p_to_be_encrypted_secured_message, p_to_be_encrypted_secured_message_length, p_encrypted_secured_message, p_encrypted_secured_message_length);
  if (result == -1) {
    // FXIME free all allocated resources
    free(*p_aes_sym_key); *p_aes_sym_key = NULL;
    free(*p_encrypted_sym_key); *p_encrypted_sym_key = NULL;
    free(*p_authentication_vector); *p_authentication_vector = NULL;
    free(*p_public_ephemeral_key_compressed); *p_public_ephemeral_key_compressed = NULL;
    free(*p_nonce); *p_nonce = NULL;
    goto end;
  }
  show_hex((const int8_t*)"Raw encrypted message", *p_encrypted_secured_message, *p_encrypted_secured_message_length);
  show_hex((const int8_t*)"tag", lib_its_security_context->tag, lib_its_security_context->tag_length);
  *p_encrypted_secured_message = (uint8_t*)realloc((void*)*p_encrypted_secured_message, *p_encrypted_secured_message_length + lib_its_security_context->tag_length);
  memcpy((void*)(*p_encrypted_secured_message + *p_encrypted_secured_message_length), (const void*)lib_its_security_context->tag, lib_its_security_context->tag_length);
  *p_encrypted_secured_message_length += lib_its_security_context->tag_length;

  result = 0;
 end:
  if (lib_its_security_context != NULL) uninitialize(&lib_its_security_context);
  if (lib_its_security_context_comp != NULL) uninitialize(&lib_its_security_context_comp);

  return result;
}

int32_t decrypt_with_ecies_brainpoolp256r1_with_sha256(
                                                       lib_its_security_context_t* p_lib_its_security_context,
                                                       const uint8_t* p_encrypted_secured_message,
                                                       const size_t p_encrypted_secured_message_length,
                                                       const uint8_t* p_private_enc_key,
                                                       const uint8_t* p_public_ephemeral_key_compressed,
                                                       const ecc_compressed_mode_t p_ephemeral_compressed_mode,
                                                       const uint8_t* p_encrypted_sym_key,
                                                       const uint8_t* p_authentication_vector,
                                                       const uint8_t* p_nonce,
                                                       const uint8_t* p_salt,
                                                       const size_t p_salt_length,
                                                       uint8_t** p_aes_sym_enc_key,
                                                       uint8_t** p_plain_text_message,
                                                       size_t* p_plain_text_message_length
                                                       ) {
  /* Sanity checks */

  lib_its_security_context_t* lib_its_security_context = NULL;
  lib_its_security_context_t* lib_its_security_context_comp = NULL; /* Convert compressed key into XY-coordinates key */
  size_t enc_message_length = 0;
  uint8_t* enc_message = NULL;

  /* 1. Initialize security context based on recipient's private key */
  int32_t result = initialize_with_private_key(brainpool_p_256_r1, p_private_enc_key, &lib_its_security_context);
  if (result == -1) {
    goto end;
  }

  /* 2. Generate the shared secret value based on public ephemeral keys will be required */
  result = initialize_with_public_key(brainpool_p_256_r1, p_public_ephemeral_key_compressed, p_ephemeral_compressed_mode, &lib_its_security_context_comp);
  if (result == -1) {
    goto end;
  }
  result = generate_and_derive_ephemeral_key_for_decryption(lib_its_security_context, aes_128_ccm, lib_its_security_context_comp, p_encrypted_sym_key, p_nonce, p_authentication_vector, p_salt, p_salt_length);
  if (result == -1) {
    goto end;
  }
  *p_aes_sym_enc_key = (uint8_t*)malloc(lib_its_security_context->sym_key_length);
  memcpy((void*)*p_aes_sym_enc_key, (const void*)lib_its_security_context->enc_sym_key, lib_its_security_context->sym_key_length);
  /* Decrypt the message */
  enc_message_length = p_encrypted_secured_message_length - lib_its_security_context->tag_length;
  enc_message = (uint8_t*)malloc(enc_message_length);
  memcpy((void*)enc_message, (const void*)p_encrypted_secured_message, enc_message_length); // Extract the encrypted message
  memcpy((void*)lib_its_security_context->tag, (const void*)(p_encrypted_secured_message + enc_message_length), lib_its_security_context->tag_length);
  show_hex((const int8_t*)"Raw encrypted message", enc_message, enc_message_length);
  show_hex((const int8_t*)"sym_key", lib_its_security_context->sym_key, lib_its_security_context->sym_key_length);
  show_hex((const int8_t*)"nonce", lib_its_security_context->nonce, lib_its_security_context->nonce_length);
  show_hex((const int8_t*)"tag", lib_its_security_context->tag, lib_its_security_context->tag_length);
  result = decrypt(lib_its_security_context, enc_message, enc_message_length, p_plain_text_message, p_plain_text_message_length);
  if (result == -1) {
    free(enc_message);
    *p_plain_text_message = NULL;
    *p_plain_text_message_length = -1;
    goto end;
  }
  free(enc_message);

 end:
  if (lib_its_security_context != NULL) uninitialize(&lib_its_security_context);
  if (lib_its_security_context_comp != NULL) uninitialize(&lib_its_security_context_comp);

  return result;
}

                lib_its_security_context_t* p_lib_its_security_context,
                const uint8_t* p_plain_text_message,
                const size_t p_plain_text_message_length,
                uint8_t** p_cipher_message,
                size_t* p_cipher_message_length
                ) {
    /* Sanity checks */
    if ((p_lib_its_security_context == NULL) || (p_lib_its_security_context->sym_key == NULL) || (p_lib_its_security_context->nonce == NULL) || (p_plain_text_message == NULL) || (p_cipher_message == NULL)) {
        return -1;
    }
    /* Initialize the context and encryption operation */
    EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
    /* Allocate buffers size */
    switch (p_lib_its_security_context->encryption_algorithm) {
    case aes_128_ccm:
        EVP_EncryptInit_ex(ctx, EVP_aes_128_ccm(), NULL, NULL, NULL);
        if (p_lib_its_security_context->tag != NULL) {
            free(p_lib_its_security_context->tag);
        }
        p_lib_its_security_context->tag_length = 16;
        p_lib_its_security_context->tag = (uint8_t*)malloc(p_lib_its_security_context->tag_length);
        *p_cipher_message = (uint8_t*)malloc(p_plain_text_message_length);
        break;
    case aes_256_ccm:
        EVP_EncryptInit_ex(ctx, EVP_aes_256_ccm(), NULL, NULL, NULL);
        break;
    case aes_128_gcm:
        EVP_EncryptInit_ex(ctx, EVP_aes_128_gcm(), NULL, NULL, NULL);
        if (p_lib_its_security_context->tag != NULL) {
            free(p_lib_its_security_context->tag);
        }
        p_lib_its_security_context->tag_length = 16;
        p_lib_its_security_context->tag = (uint8_t*)malloc(p_lib_its_security_context->tag_length);
        *p_cipher_message = (uint8_t*)malloc(p_plain_text_message_length);
        break;
    case aes_256_gcm:
        EVP_EncryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, NULL, NULL);
        break;
    } /* End of 'switch' statement */
    *p_cipher_message_length = p_plain_text_message_length;
    /* Set nonce length */
    EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_IVLEN, p_lib_its_security_context->nonce_length, NULL);
    /* Set tag length */
    EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_TAG, p_lib_its_security_context->tag_length, NULL);
    /* Prime the key and nonce */
    EVP_EncryptInit_ex(ctx, NULL, NULL, p_lib_its_security_context->sym_key, p_lib_its_security_context->nonce);
    // No authentication data
    // Encrypt the data
    int len = 0;
    EVP_EncryptUpdate(ctx, *p_cipher_message, &len, p_plain_text_message, p_plain_text_message_length);
    // Finalize the encryption session
    EVP_EncryptFinal_ex(ctx, (*p_cipher_message) + len, &len);
    /* Get the authentication tag */
    EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_GET_TAG, p_lib_its_security_context->tag_length, p_lib_its_security_context->tag);

    EVP_CIPHER_CTX_free(ctx);

    return 0;
}

int32_t decrypt(
                lib_its_security_context_t* p_lib_its_security_context,
                const uint8_t* p_cipher_message,
                const size_t p_cipher_message_length,
                uint8_t** p_plain_text_message,
                size_t* p_plain_text_message_length
                ) {
    show_hex((const int8_t*)">>> decrypt: p_cipher_message", p_cipher_message, p_cipher_message_length);
    show_hex((const int8_t*)">>> decrypt: sym_key", p_lib_its_security_context->sym_key, p_lib_its_security_context->sym_key_length);
    show_hex((const int8_t*)">>> decrypt: nonce", p_lib_its_security_context->nonce, p_lib_its_security_context->nonce_length);
    show_hex((const int8_t*)">>> decrypt: tag", p_lib_its_security_context->tag, p_lib_its_security_context->tag_length);

    /* Sanity checks */
    if ((p_lib_its_security_context == NULL) || (p_lib_its_security_context->sym_key == NULL) || (p_lib_its_security_context->nonce == NULL) || (p_cipher_message == NULL) || (p_plain_text_message == NULL)) {
        return -1;
    }

    /* Initialize the context and decryption operation */
    EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
    switch (p_lib_its_security_context->encryption_algorithm) {
    case aes_128_ccm:
        EVP_DecryptInit_ex(ctx, EVP_aes_128_ccm(), NULL, NULL, NULL);
        break;
    case aes_256_ccm:
        EVP_DecryptInit_ex(ctx, EVP_aes_256_ccm(), NULL, NULL, NULL);
        break;
    case aes_128_gcm:
        EVP_DecryptInit_ex(ctx, EVP_aes_128_gcm(), NULL, NULL, NULL);
        break;
    case 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, p_lib_its_security_context->nonce_length, NULL);
    /* Set expected tag value */
    EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_TAG, p_lib_its_security_context->tag_length, p_lib_its_security_context->tag);
    /* Specify key and IV */
    EVP_DecryptInit_ex(ctx, NULL, NULL, p_lib_its_security_context->sym_key, p_lib_its_security_context->nonce);
    /* Decrypt plaintext, verify tag: can only be called once */
    *p_plain_text_message = (uint8_t*)malloc(p_cipher_message_length);
    *p_plain_text_message_length = p_cipher_message_length;
    int len = 0;
    int result = EVP_DecryptUpdate(ctx, *p_plain_text_message, &len, p_cipher_message, p_cipher_message_length);
    EVP_CIPHER_CTX_free(ctx);
    show_hex((const int8_t*)"decrypt: *p_plain_text_message", *p_plain_text_message, *p_plain_text_message_length);
    fprintf(stderr, "decrypt: result=%d\n", result);
    if (result != 1) {
        free(*p_plain_text_message);
        *p_plain_text_message = NULL;
    }

    fprintf(stderr, "<<< decrypt: result=%d\n", (result > 0) ? 0 : -1);
int32_t generate_key_pair(lib_its_security_context_t* p_lib_its_security_context, uint8_t** p_private_key, uint8_t** p_public_key_x, uint8_t** p_public_key_y, uint8_t** p_public_key_compressed, ecc_compressed_mode_t* p_compressed_mode) {
  /* Sanity checks */
  if ((p_lib_its_security_context == NULL) || (p_private_key == NULL) || (p_public_key_x == NULL) || (p_public_key_y == NULL) || (p_public_key_compressed == NULL) || (p_compressed_mode == NULL)) {
    return -1;
  }
  
  if (!EC_KEY_generate_key(p_lib_its_security_context->ec_key)) { /* Generate the private and public keys */
    fprintf(stderr, "generate_key_pair: Failed to generate private/public keys\n");
    return -1;
  }

  BIGNUM* x = BN_new();
  BIGNUM* y = BN_new();
  const EC_POINT* ec_point = EC_KEY_get0_public_key(p_lib_its_security_context->ec_key);
  int32_t result = 0;
  size_t size = 0;
  switch (p_lib_its_security_context->elliptic_curve) {
  case nist_p_256: // Use primary
    // No break;
  case brainpool_p_256_r1:
    size = 32;
    result = EC_POINT_get_affine_coordinates_GFp(p_lib_its_security_context->ec_group, ec_point, x, y, p_lib_its_security_context->bn_ctx); /* Use primer on elliptic curve */
    break;
  case brainpool_p_384_r1:
    size = 48;
    result = EC_POINT_get_affine_coordinates_GFp(p_lib_its_security_context->ec_group, ec_point, x, y, p_lib_its_security_context->bn_ctx); /* Use primer on elliptic curve */
    break;
  default: // Use binary
    result = EC_POINT_get_affine_coordinates_GF2m(p_lib_its_security_context->ec_group, ec_point, x, y, p_lib_its_security_context->bn_ctx);
  } // End of 'switch' statement
  if (result == 0) {
    fprintf(stderr, "generate_key_pair: Failed to get coordinates\n");
    BN_clear_free(x);
    BN_clear_free(y);
    return -1;
  }
  
  const BIGNUM* p = EC_KEY_get0_private_key(p_lib_its_security_context->ec_key);
  *p_private_key = (uint8_t*)malloc(size);
  BN_bn2bin(p, (unsigned char*)*p_private_key);
  *p_public_key_x = (uint8_t*)malloc(size);
  BN_bn2bin(x, (unsigned char*)*p_public_key_x);
  *p_public_key_y = (uint8_t*)malloc(size);
  BN_bn2bin(y, (unsigned char*)*p_public_key_y);
  BN_clear_free(x); x = NULL;
  BN_clear_free(y); y = NULL;

  // Compressed
  int len = EC_POINT_point2oct(p_lib_its_security_context->ec_group, ec_point, POINT_CONVERSION_COMPRESSED, NULL, 0, p_lib_its_security_context->bn_ctx);
  if (len == 0) {
    fprintf(stderr, "generate_key_pair: Failed to generate x_coordinate compressed key\n");
  }
  *p_public_key_compressed = (uint8_t*)malloc(len);
  if (EC_POINT_point2oct(p_lib_its_security_context->ec_group, ec_point, POINT_CONVERSION_COMPRESSED, (unsigned char*)*p_public_key_compressed, len, p_lib_its_security_context->bn_ctx) == 0) {
    fprintf(stderr, "generate_key_pair: Failed to generate x_coordinate compressed key\n");
    memset((void*)*p_public_key_compressed, 0x00, len);
  } else { /* Remove first byte */
    *p_compressed_mode = (ecc_compressed_mode_t)(((*p_public_key_compressed)[0] & 0x01) == 0x00) ? compressed_y_0 : compressed_y_1;
    memmove((void*)*p_public_key_compressed, (const void*)(*p_public_key_compressed + 1), len - 1);
  }
  
  return 0;
}