Revision 0d9153dc34ee2dcf0821e3f21e825fc5bb8895b4 authored by Santiago Zanella-Beguelin on 11 December 2019, 17:46:08 UTC, committed by Santiago Zanella-Beguelin on 12 December 2019, 10:33:01 UTC
1 parent 7405f78
Raw File
evercrypt_openssl.c
#include <openssl/bn.h>
#include <openssl/evp.h>
#include <openssl/rand.h>
#include <openssl/dh.h>
#include <openssl/ec.h>
#include <openssl/ecdh.h>
#include <inttypes.h>
#include <stdio.h>

#include "kremlin/internal/target.h"
#include "EverCrypt_OpenSSL.h"

/* KB, BB, JP: for now, we just ignore internal errors since the HACL* interface
 * has enough preconditions to make sure that no errors ever happen; if the
 * OpenSSL F* interface is strong enough, then any error here should be
 * catastrophic and not something we can recover from.
 * If we want to do something better, we can define:
 *   type error a = | Ok of a | Error of error_code
 * Then at the boundary we could catch the error, print it, then exit abruptly. */

#define handleErrors(...)                                                      \
  do {                                                                         \
    fprintf(stderr, "Error at %s:%d\n", __FILE__, __LINE__);                   \
  } while (0)


/* OpenSSL PRNG */

uint32_t EverCrypt_OpenSSL_random_init()
{
  if(RAND_status()) return 1;
  return RAND_poll();
}

void EverCrypt_OpenSSL_random_sample(uint32_t len, uint8_t *out)
{
  if(1 != RAND_bytes(out, len))
    handleErrors();
}

/* OpenSSL AEAD */

EVP_CIPHER_CTX *openssl_create(const EVP_CIPHER *alg, uint8_t *key)
{
  EVP_CIPHER_CTX *ctx;
  
  if (!(ctx = EVP_CIPHER_CTX_new()))
    return NULL;

  if (1 != EVP_CipherInit_ex(ctx, alg, NULL, key, NULL, -1))
  {
    EVP_CIPHER_CTX_free(ctx);
    return NULL;
  }

  return ctx;
}

void openssl_free(EVP_CIPHER_CTX *ctx)
{
  EVP_CIPHER_CTX_free(ctx);
}

static int openssl_aead(EVP_CIPHER_CTX *ctx,
  int enc, uint8_t *iv,
  uint8_t *aad, uint32_t aad_len,
  uint8_t *plaintext, uint32_t plaintext_len,
  uint8_t *ciphertext, uint8_t *tag)
{
  int len;

  /* Initialise the cipher with the key and IV */
  if (1 != EVP_CipherInit_ex(ctx, NULL, NULL, NULL, iv, enc))
    handleErrors();

  /* Set additional authenticated data */
  if (aad_len > 0 && 1 != EVP_CipherUpdate(ctx, NULL, &len, aad, aad_len))
    handleErrors();

  /* Process the plaintext */
  if (enc && plaintext_len > 0
      && 1 != EVP_CipherUpdate(ctx, ciphertext, &len, plaintext, plaintext_len))
    handleErrors();

  /* Process the ciphertext */
  if (!enc && plaintext_len > 0
      && 1 != EVP_CipherUpdate(ctx, plaintext, &len, ciphertext, plaintext_len))
    handleErrors();

  /* Set the tag */
  if(!enc && EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, 16, tag) <= 0)
    handleErrors();

  /* Finalize last block */
  if (1 != EVP_CipherFinal_ex(ctx, ciphertext + len, &len))
    return 0;

  /* Get the tag */
  if (enc && 1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, 16, tag))
    handleErrors();

  return 1;
}

void EverCrypt_OpenSSL_aes128_gcm_encrypt(uint8_t *key, uint8_t *iv, uint8_t *aad, uint32_t aad_len,
                                          uint8_t *plaintext, uint32_t plaintext_len, uint8_t *ciphertext, uint8_t *tag)
{
  EVP_CIPHER_CTX *ctx = openssl_create(EVP_aes_128_gcm(), key);
  if(NULL == ctx || 1 != openssl_aead(ctx, 1, iv, aad, aad_len, plaintext, plaintext_len, ciphertext, tag))
    handleErrors();
  openssl_free(ctx);
}

uint32_t EverCrypt_OpenSSL_aes128_gcm_decrypt(uint8_t *key, uint8_t *iv, uint8_t *aad, uint32_t aad_len,
                                              uint8_t *plaintext, uint32_t plaintext_len, uint8_t *ciphertext, uint8_t *tag)
{
  EVP_CIPHER_CTX *ctx = openssl_create(EVP_aes_128_gcm(), key);
  int ret = openssl_aead(ctx, 0, iv, aad, aad_len, plaintext, plaintext_len, ciphertext, tag);
  openssl_free(ctx);
  return ret;
}

void EverCrypt_OpenSSL_aes256_gcm_encrypt(uint8_t *key, uint8_t *iv, uint8_t *aad, uint32_t aad_len,
                                          uint8_t *plaintext, uint32_t plaintext_len, uint8_t *ciphertext, uint8_t *tag)
{
  EVP_CIPHER_CTX *ctx = openssl_create(EVP_aes_256_gcm(), key);
  if(NULL == ctx || 1 != openssl_aead(ctx, 1, iv, aad, aad_len, plaintext, plaintext_len, ciphertext, tag))
    handleErrors();
  openssl_free(ctx);
}

uint32_t EverCrypt_OpenSSL_aes256_gcm_decrypt(uint8_t *key, uint8_t *iv, uint8_t *aad, uint32_t aad_len,
                                              uint8_t *plaintext, uint32_t plaintext_len, uint8_t *ciphertext, uint8_t *tag)
{
  EVP_CIPHER_CTX *ctx = openssl_create(EVP_aes_256_gcm(), key);
  int ret = openssl_aead(ctx, 0, iv, aad, aad_len, plaintext, plaintext_len, ciphertext, tag);
  openssl_free(ctx);
  return ret;
}

void EverCrypt_OpenSSL_chacha20_poly1305_encrypt(uint8_t *key, uint8_t *iv, uint8_t *aad, uint32_t aad_len,
                                                 uint8_t *plaintext, uint32_t plaintext_len, uint8_t *ciphertext, uint8_t *tag)
{
#ifdef OPENSSL_IS_BORINGSSL
  handleErrors();
#else
  EVP_CIPHER_CTX *ctx = openssl_create(EVP_chacha20_poly1305(), key);
  if(NULL == ctx || 1 != openssl_aead(ctx, 1, iv, aad, aad_len, plaintext, plaintext_len, ciphertext, tag))
    handleErrors();
  openssl_free(ctx);
#endif
}

uint32_t EverCrypt_OpenSSL_chacha20_poly1305_decrypt(uint8_t *key, uint8_t *iv, uint8_t *aad, uint32_t aad_len,
                                                     uint8_t *plaintext, uint32_t plaintext_len, uint8_t *ciphertext, uint8_t *tag)
{
#ifdef OPENSSL_IS_BORINGSSL
  handleErrors();
  return 0;
#else
  EVP_CIPHER_CTX *ctx = openssl_create(EVP_chacha20_poly1305(), key);
  int ret = openssl_aead(ctx, 0, iv, aad, aad_len, plaintext, plaintext_len, ciphertext, tag);
  openssl_free(ctx);
  return ret;
#endif
}

void* EverCrypt_OpenSSL_aead_create(uint8_t alg, uint8_t *key)
{
  const EVP_CIPHER *a;
  EVP_CIPHER_CTX *ctx;
  
  if(alg == 0) a = EVP_aes_128_gcm();
  else if(alg == 1) a = EVP_aes_256_gcm();
#ifndef OPENSSL_IS_BORINGSSL
  else if(alg == 2) a = EVP_chacha20_poly1305();
#endif
  else{ handleErrors(); return NULL; }

  ctx = openssl_create(a, key);
  if(ctx == NULL) handleErrors();

  return (void*)ctx;
}

void EverCrypt_OpenSSL_aead_encrypt(void* ctx, uint8_t *iv, uint8_t *aad, uint32_t aad_len,
                                    uint8_t *plaintext, uint32_t plaintext_len, uint8_t *ciphertext, uint8_t *tag)
{
  if(!openssl_aead((EVP_CIPHER_CTX*)ctx, 1, iv, aad, aad_len, plaintext, plaintext_len, ciphertext, tag))
    handleErrors();
}

uint32_t EverCrypt_OpenSSL_aead_decrypt(void* ctx, uint8_t *iv, uint8_t *aad, uint32_t aad_len,
                                    uint8_t *plaintext, uint32_t plaintext_len, uint8_t *ciphertext, uint8_t *tag)
{
  return openssl_aead((EVP_CIPHER_CTX*)ctx, 0, iv, aad, aad_len, plaintext, plaintext_len, ciphertext, tag);
}

void EverCrypt_OpenSSL_aead_free(void* ctx)
{
  openssl_free((EVP_CIPHER_CTX *)ctx);
}

/* Diffie-Hellman */

void* EverCrypt_OpenSSL_dh_load_group(
  uint8_t *dh_p,  uint32_t dh_p_len,
  uint8_t *dh_g,  uint32_t dh_g_len,
  uint8_t *dh_q,  uint32_t dh_q_len)
{
  DH *dh = DH_new();
  if(dh == NULL) handleErrors();

  BIGNUM *p = BN_bin2bn(dh_p, dh_p_len, NULL);
  BIGNUM *g = BN_bin2bn(dh_g, dh_g_len, NULL);
  BIGNUM *q = dh_q_len ? BN_bin2bn(dh_q, dh_q_len, NULL) : NULL;
  DH_set0_pqg(dh, p, q, g);
  
  return (void*)dh;
}

void EverCrypt_OpenSSL_dh_free_group(void *g)
{
  DH_free((DH*)g);
}

uint32_t EverCrypt_OpenSSL_dh_keygen(void *st, uint8_t *pub)
{
  DH *dh = (DH*)st;
  const BIGNUM *opub, *opriv;
  DH_generate_key(dh);
  DH_get0_key(dh, &opub, &opriv);
  BN_bn2bin(opub, pub);
  return BN_num_bytes(opub);
}

uint32_t EverCrypt_OpenSSL_dh_compute(void *g,
  uint8_t *pub, uint32_t pub_len,
  uint8_t *out)
{
  BIGNUM *opub = BN_bin2bn(pub, pub_len, NULL);
  
  if(DH_compute_key(out, opub, (DH*)g) < 0)
    handleErrors();
  
  BN_free(opub);
  return DH_size((DH*)g);
}

void* EverCrypt_OpenSSL_ecdh_load_curve(EverCrypt_OpenSSL_ec_curve g)
{
  EC_KEY *k = NULL;
  switch (g) {
    case EverCrypt_OpenSSL_ECC_P256:
      k = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
      break;
    case EverCrypt_OpenSSL_ECC_P384:
      k = EC_KEY_new_by_curve_name(NID_secp384r1);
      break;
    case EverCrypt_OpenSSL_ECC_P521:
      k = EC_KEY_new_by_curve_name(NID_secp521r1);
      break;
    case EverCrypt_OpenSSL_ECC_X25519:
      k = EC_KEY_new_by_curve_name(NID_X25519);
      break;
#ifdef OPENSSL_IS_BORINGSSL
    case EverCrypt_OpenSSL_ECC_X448:
      handleErrors();
      break;
#else
    case EverCrypt_OpenSSL_ECC_X448:
      k = EC_KEY_new_by_curve_name(NID_X448);
      break;
#endif
  }
  return (void*)k;
}

void EverCrypt_OpenSSL_ecdh_free_curve(void* st)
{
  EC_KEY_free((EC_KEY*)st);
}

void dump(const unsigned char buffer[], size_t len)
{
  size_t i;
  for(i=0; i<len; i++) {
    printf("%02x",buffer[i] & 0xFF);
    if (i % 32 == 31 || i == len-1) printf("\n");
  }
}

void EverCrypt_OpenSSL_ecdh_keygen(void* st, uint8_t *outx, uint8_t *outy)
{
  EC_KEY *k = (EC_KEY*)st;
  EC_KEY_generate_key(k);
  
  const EC_GROUP *g = EC_KEY_get0_group(k);
  const EC_POINT *p = EC_KEY_get0_public_key(k);
  size_t len = (EC_GROUP_get_degree(g) + 7) / 8;
  
  BIGNUM *x = BN_new(), *y = BN_new();
  EC_POINT_get_affine_coordinates_GFp(g, p, x, y, NULL);

#ifdef OPENSSL_IS_BORINGSSL
  BN_bn2bin_padded(outx, len, x);
  BN_bn2bin_padded(outy, len, y);
#else
  BN_bn2binpad(x, outx, len);
  BN_bn2binpad(y, outy, len);
#endif

  BN_free(y);
  BN_free(x);
}

uint32_t EverCrypt_OpenSSL_ecdh_compute(void* st,
  uint8_t *inx, uint8_t *iny, uint8_t *out)
{
  EC_KEY *k = (EC_KEY*)st;
  const EC_GROUP *g = EC_KEY_get0_group(k);
  EC_POINT *p = EC_POINT_new(g);
  size_t len = (EC_GROUP_get_degree(g) + 7) / 8;
  
  BIGNUM *px = BN_bin2bn(inx, len, NULL);
  BIGNUM *py = BN_bin2bn(iny, len, NULL);
  
  EC_POINT_set_affine_coordinates_GFp(g, p, px, py, NULL);
  int olen = ECDH_compute_key((uint8_t *)out, len, p, k, NULL);

  BN_free(px);
  BN_free(py);
  EC_POINT_free(p);
  
  return olen < 0 ? 0 : olen;
}

/*
bool CoreCrypto_ec_is_on_curve_(CoreCrypto_ec_params *x0,
                               CoreCrypto_ec_point *x1) {

  EC_KEY *k = key_of_core_crypto_curve(x0->curve);
  const EC_GROUP *group = EC_KEY_get0_group(k);

  EC_POINT *point = EC_POINT_new(group);
  BIGNUM *ppx = BN_bin2bn((uint8_t *)x1->ecx.data, x1->ecx.length, NULL);
  BIGNUM *ppy = BN_bin2bn((uint8_t *)x1->ecy.data, x1->ecy.length, NULL);
  EC_POINT_set_affine_coordinates_GFp(group, point, ppx, ppy, NULL);

  bool ret = EC_POINT_is_on_curve(group, point, NULL);

  BN_free(ppy);
  BN_free(ppx);
  EC_POINT_free(point);
  EC_KEY_free(k);
  return ret;
}

*/
back to top