#include "orconfig.h"
#include "lib/crypt_ops/aes.h"
#include "lib/crypt_ops/crypto_util.h"
#include "lib/log/util_bug.h"
#include "lib/arch/bytes.h"
#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#endif
#include "lib/crypt_ops/compat_openssl.h"
#include <openssl/opensslv.h>
#include "lib/crypt_ops/crypto_openssl_mgt.h"
#if OPENSSL_VERSION_NUMBER < OPENSSL_V_SERIES(1,0,0)
#error "We require OpenSSL >= 1.0.0"
#endif
DISABLE_GCC_WARNING("-Wredundant-decls")
#include <stdlib.h>
#include <string.h>
#include <openssl/aes.h>
#include <openssl/evp.h>
#include <openssl/engine.h>
#include <openssl/modes.h>
ENABLE_GCC_WARNING("-Wredundant-decls")
#include "lib/log/log.h"
#include "lib/ctime/di_ops.h"
#ifdef OPENSSL_NO_ENGINE
#define DISABLE_ENGINES
#endif
#if OPENSSL_VERSION_NUMBER >= OPENSSL_V_NOPATCH(1,1,0)
#define USE_EVP_AES_CTR
#elif OPENSSL_VERSION_NUMBER >= OPENSSL_V_NOPATCH(1,0,1) && \
(defined(__i386) || defined(__i386__) || defined(_M_IX86) || \
defined(__x86_64) || defined(__x86_64__) || \
defined(_M_AMD64) || defined(_M_X64) || defined(__INTEL__))
#define USE_EVP_AES_CTR
#endif
#ifdef USE_EVP_AES_CTR
aes_cnt_cipher_t *
aes_new_cipher(const uint8_t *key, const uint8_t *iv, int key_bits)
{
EVP_CIPHER_CTX *cipher = EVP_CIPHER_CTX_new();
const EVP_CIPHER *c = NULL;
switch (key_bits) {
case 128: c = EVP_aes_128_ctr(); break;
case 192: c = EVP_aes_192_ctr(); break;
case 256: c = EVP_aes_256_ctr(); break;
default: tor_assert_unreached(); }
EVP_EncryptInit(cipher, c, key, iv);
return (aes_cnt_cipher_t *) cipher;
}
void
aes_cipher_free_(aes_cnt_cipher_t *cipher_)
{
if (!cipher_)
return;
EVP_CIPHER_CTX *cipher = (EVP_CIPHER_CTX *) cipher_;
#ifdef OPENSSL_1_1_API
EVP_CIPHER_CTX_reset(cipher);
#else
EVP_CIPHER_CTX_cleanup(cipher);
#endif
EVP_CIPHER_CTX_free(cipher);
}
void
aes_crypt_inplace(aes_cnt_cipher_t *cipher_, char *data, size_t len)
{
int outl;
EVP_CIPHER_CTX *cipher = (EVP_CIPHER_CTX *) cipher_;
tor_assert(len < INT_MAX);
EVP_EncryptUpdate(cipher, (unsigned char*)data,
&outl, (unsigned char*)data, (int)len);
}
int
evaluate_evp_for_aes(int force_val)
{
(void) force_val;
log_info(LD_CRYPTO, "This version of OpenSSL has a known-good EVP "
"counter-mode implementation. Using it.");
return 0;
}
int
evaluate_ctr_for_aes(void)
{
return 0;
}
#else
struct aes_cnt_cipher_t {
union {
EVP_CIPHER_CTX evp;
AES_KEY aes;
} key;
#if !defined(WORDS_BIGENDIAN)
#define USING_COUNTER_VARS
uint32_t counter3;
uint32_t counter2;
uint32_t counter1;
uint32_t counter0;
#endif
union {
uint8_t buf[16];
uint32_t buf32[4];
} ctr_buf;
uint8_t buf[16];
unsigned int pos;
uint8_t using_evp;
};
static int should_use_EVP = 0;
int
evaluate_evp_for_aes(int force_val)
{
ENGINE *e;
if (force_val >= 0) {
should_use_EVP = force_val;
return 0;
}
#ifdef DISABLE_ENGINES
should_use_EVP = 0;
#else
e = ENGINE_get_cipher_engine(NID_aes_128_ecb);
if (e) {
log_info(LD_CRYPTO, "AES engine \"%s\" found; using EVP_* functions.",
ENGINE_get_name(e));
should_use_EVP = 1;
} else {
log_info(LD_CRYPTO, "No AES engine found; using AES_* functions.");
should_use_EVP = 0;
}
#endif
return 0;
}
int
evaluate_ctr_for_aes(void)
{
static const unsigned char encrypt_zero[] =
"\x66\xe9\x4b\xd4\xef\x8a\x2c\x3b\x88\x4c\xfa\x59\xca\x34\x2b\x2e";
unsigned char zero[16];
unsigned char output[16];
unsigned char ivec[16];
unsigned char ivec_tmp[16];
unsigned int pos, i;
AES_KEY key;
memset(zero, 0, sizeof(zero));
memset(ivec, 0, sizeof(ivec));
AES_set_encrypt_key(zero, 128, &key);
pos = 0;
for (i=0; i<16; ++i)
AES_ctr128_encrypt(&zero[i], &output[i], 1, &key, ivec, ivec_tmp, &pos);
if (fast_memneq(output, encrypt_zero, 16)) {
log_err(LD_CRYPTO, "This OpenSSL has a buggy version of counter mode; "
"quitting tor.");
exit(1);
}
return 0;
}
#if !defined(USING_COUNTER_VARS)
#define COUNTER(c, n) ((c)->ctr_buf.buf32[3-(n)])
#else
#define COUNTER(c, n) ((c)->counter ## n)
#endif
static void aes_set_key(aes_cnt_cipher_t *cipher, const uint8_t *key,
int key_bits);
static void aes_set_iv(aes_cnt_cipher_t *cipher, const uint8_t *iv);
aes_cnt_cipher_t*
aes_new_cipher(const uint8_t *key, const uint8_t *iv, int bits)
{
aes_cnt_cipher_t* result = tor_malloc_zero(sizeof(aes_cnt_cipher_t));
aes_set_key(result, key, bits);
aes_set_iv(result, iv);
return result;
}
static void
aes_set_key(aes_cnt_cipher_t *cipher, const uint8_t *key, int key_bits)
{
if (should_use_EVP) {
const EVP_CIPHER *c = 0;
switch (key_bits) {
case 128: c = EVP_aes_128_ecb(); break;
case 192: c = EVP_aes_192_ecb(); break;
case 256: c = EVP_aes_256_ecb(); break;
default: tor_assert(0); }
EVP_EncryptInit(&cipher->key.evp, c, key, NULL);
cipher->using_evp = 1;
} else {
AES_set_encrypt_key(key, key_bits,&cipher->key.aes);
cipher->using_evp = 0;
}
#ifdef USING_COUNTER_VARS
cipher->counter0 = 0;
cipher->counter1 = 0;
cipher->counter2 = 0;
cipher->counter3 = 0;
#endif
memset(cipher->ctr_buf.buf, 0, sizeof(cipher->ctr_buf.buf));
cipher->pos = 0;
memset(cipher->buf, 0, sizeof(cipher->buf));
}
void
aes_cipher_free_(aes_cnt_cipher_t *cipher)
{
if (!cipher)
return;
if (cipher->using_evp) {
EVP_CIPHER_CTX_cleanup(&cipher->key.evp);
}
memwipe(cipher, 0, sizeof(aes_cnt_cipher_t));
tor_free(cipher);
}
#if defined(USING_COUNTER_VARS)
#define UPDATE_CTR_BUF(c, n) STMT_BEGIN \
(c)->ctr_buf.buf32[3-(n)] = htonl((c)->counter ## n); \
STMT_END
#else
#define UPDATE_CTR_BUF(c, n)
#endif
static void
evp_block128_fn(const uint8_t in[16],
uint8_t out[16],
const void *key)
{
EVP_CIPHER_CTX *ctx = (void*)key;
int inl=16, outl=16;
EVP_EncryptUpdate(ctx, out, &outl, in, inl);
}
void
aes_crypt_inplace(aes_cnt_cipher_t *cipher, char *data, size_t len)
{
if (cipher->using_evp) {
CRYPTO_ctr128_encrypt((const unsigned char *)data,
(unsigned char *)data,
len,
&cipher->key.evp,
cipher->ctr_buf.buf,
cipher->buf,
&cipher->pos,
evp_block128_fn);
} else {
AES_ctr128_encrypt((const unsigned char *)data,
(unsigned char *)data,
len,
&cipher->key.aes,
cipher->ctr_buf.buf,
cipher->buf,
&cipher->pos);
}
}
static void
aes_set_iv(aes_cnt_cipher_t *cipher, const uint8_t *iv)
{
#ifdef USING_COUNTER_VARS
cipher->counter3 = tor_ntohl(get_uint32(iv));
cipher->counter2 = tor_ntohl(get_uint32(iv+4));
cipher->counter1 = tor_ntohl(get_uint32(iv+8));
cipher->counter0 = tor_ntohl(get_uint32(iv+12));
#endif
cipher->pos = 0;
memcpy(cipher->ctr_buf.buf, iv, 16);
}
#endif