Back to index

tor  0.2.3.19-rc
Classes | Defines | Functions | Variables
aes.c File Reference

Implements a counter-mode stream cipher on top of AES. More...

#include "orconfig.h"
#include <openssl/opensslv.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/aes.h>
#include <openssl/evp.h>
#include <openssl/engine.h>
#include "crypto.h"
#include "compat.h"
#include "aes.h"
#include "util.h"
#include "torlog.h"

Go to the source code of this file.

Classes

struct  aes_cnt_cipher
 Implements an AES counter-mode cipher. More...
union  aes_cnt_cipher.key
 This next element (however it's defined) is the AES key. More...
union  aes_cnt_cipher.ctr_buf

Defines

#define USING_COUNTER_VARS
#define COUNTER(c, n)   ((c)->counter ## n)
#define UPDATE_CTR_BUF(c, n)

Functions

int evaluate_evp_for_aes (int force_val)
 Check whether we should use the EVP interface for AES.
int evaluate_ctr_for_aes (void)
 Test the OpenSSL counter mode implementation to see whether it has the counter-mode bug from OpenSSL 1.0.0.
static INLINE void _aes_fill_buf (aes_cnt_cipher_t *cipher)
 Helper function: set cipher's internal buffer to the encrypted value of the current counter.
static void aes_set_key (aes_cnt_cipher_t *cipher, const char *key, int key_bits)
 Set the key of cipher to key, which is key_bits bits long (must be 128, 192, or 256).
static void aes_set_iv (aes_cnt_cipher_t *cipher, const char *iv)
 Reset the 128-bit counter of cipher to the 16-bit big-endian value in iv.
aes_cnt_cipher_t * aes_new_cipher (const char *key, const char *iv)
 Return a newly allocated counter-mode AES128 cipher implementation, using the 128-bit key key and the 128-bit IV iv.
void aes_cipher_free (aes_cnt_cipher_t *cipher)
 Release storage held by cipher
void aes_crypt (aes_cnt_cipher_t *cipher, const char *input, size_t len, char *output)
 Encrypt len bytes from input, storing the result in output.
void aes_crypt_inplace (aes_cnt_cipher_t *cipher, char *data, size_t len)
 Encrypt len bytes from input, storing the results in place.

Variables

static int should_use_EVP = 0
 True iff we should prefer the EVP implementation for AES, either because we're testing it or because we have hardware acceleration configured.

Detailed Description

Implements a counter-mode stream cipher on top of AES.

Definition in file aes.c.


Class Documentation

struct aes_cnt_cipher

Implements an AES counter-mode cipher.

Definition at line 152 of file aes.c.

Class Members
uint8_t buf The encrypted value of ctr_buf.
uint32_t counter0
uint32_t counter1
uint32_t counter2
uint32_t counter3 These four values, together, implement a 128-bit counter, with counter0 as the low-order word and counter3 as the high-order word.
union aes_cnt_cipher ctr_buf
union aes_cnt_cipher key This next element (however it's defined) is the AES key.
unsigned int pos Our current stream position within buf.
uint8_t using_evp True iff we're using the evp implementation of this cipher.
union aes_cnt_cipher.key

This next element (however it's defined) is the AES key.

Definition at line 154 of file aes.c.

Class Members
AES_KEY aes
EVP_CIPHER_CTX evp
union aes_cnt_cipher.ctr_buf

Definition at line 169 of file aes.c.

Class Members
uint8_t buf The counter, in big-endian order, as bytes.
uint32_t buf32 The counter, in big-endian order, as big-endian words. Note that on big-endian platforms, this is redundant with counter3...0, so we just use these values instead.

Define Documentation

#define COUNTER (   c,
 
)    ((c)->counter ## n)

Definition at line 280 of file aes.c.

#define UPDATE_CTR_BUF (   c,
 
)
Value:
STMT_BEGIN                 \
  (c)->ctr_buf.buf32[3-(n)] = htonl((c)->counter ## n); \
  STMT_END

Definition at line 381 of file aes.c.

Definition at line 160 of file aes.c.


Function Documentation

static INLINE void _aes_fill_buf ( aes_cnt_cipher_t *  cipher) [static]

Helper function: set cipher's internal buffer to the encrypted value of the current counter.

Definition at line 288 of file aes.c.

{
  /* We don't currently use OpenSSL's counter mode implementation because:
   *  1) some versions have known bugs
   *  2) its attitude towards IVs is not our own
   *  3) changing the counter position was not trivial, last time I looked.
   * None of these issues are insurmountable in principle.
   */

  if (cipher->using_evp) {
    int outl=16, inl=16;
    EVP_EncryptUpdate(&cipher->key.evp, cipher->buf, &outl,
                      cipher->ctr_buf.buf, inl);
  } else {
    AES_encrypt(cipher->ctr_buf.buf, cipher->buf, &cipher->key.aes);
  }
}

Here is the caller graph for this function:

void aes_cipher_free ( aes_cnt_cipher_t *  cipher)

Release storage held by cipher

Definition at line 369 of file aes.c.

{
  if (!cipher)
    return;
  if (cipher->using_evp) {
    EVP_CIPHER_CTX_cleanup(&cipher->key.evp);
  }
  memset(cipher, 0, sizeof(aes_cnt_cipher_t));
  tor_free(cipher);
}

Here is the caller graph for this function:

void aes_crypt ( aes_cnt_cipher_t *  cipher,
const char *  input,
size_t  len,
char *  output 
)

Encrypt len bytes from input, storing the result in output.

Uses the key in cipher, and advances the counter by len bytes as it encrypts.

Definition at line 405 of file aes.c.

{
#ifdef CAN_USE_OPENSSL_CTR
  if (should_use_openssl_CTR) {
    if (cipher->using_evp) {
      /* In openssl 1.0.0, there's an if'd out EVP_aes_128_ctr in evp.h.  If
       * it weren't disabled, it might be better just to use that.
       */
      CRYPTO_ctr128_encrypt((const unsigned char *)input,
                            (unsigned char *)output,
                            len,
                            &cipher->key.evp,
                            cipher->ctr_buf.buf,
                            cipher->buf,
                            &cipher->pos,
                            evp_block128_fn);
    } else {
      AES_ctr128_encrypt((const unsigned char *)input,
                         (unsigned char *)output,
                         len,
                         &cipher->key.aes,
                         cipher->ctr_buf.buf,
                         cipher->buf,
                         &cipher->pos);
    }
    return;
  }
  else
#endif
  {
    int c = cipher->pos;
    if (PREDICT_UNLIKELY(!len)) return;

    while (1) {
      do {
        if (len-- == 0) { cipher->pos = c; return; }
        *(output++) = *(input++) ^ cipher->buf[c];
      } while (++c != 16);
      cipher->pos = c = 0;
      if (PREDICT_UNLIKELY(! ++COUNTER(cipher, 0))) {
        if (PREDICT_UNLIKELY(! ++COUNTER(cipher, 1))) {
          if (PREDICT_UNLIKELY(! ++COUNTER(cipher, 2))) {
            ++COUNTER(cipher, 3);
            UPDATE_CTR_BUF(cipher, 3);
          }
          UPDATE_CTR_BUF(cipher, 2);
        }
        UPDATE_CTR_BUF(cipher, 1);
      }
      UPDATE_CTR_BUF(cipher, 0);
      _aes_fill_buf(cipher);
    }
  }
}

Here is the call graph for this function:

Here is the caller graph for this function:

void aes_crypt_inplace ( aes_cnt_cipher_t *  cipher,
char *  data,
size_t  len 
)

Encrypt len bytes from input, storing the results in place.

Uses the key in cipher, and advances the counter by len bytes as it encrypts.

Definition at line 466 of file aes.c.

{
#ifdef CAN_USE_OPENSSL_CTR
  if (should_use_openssl_CTR) {
    aes_crypt(cipher, data, len, data);
    return;
  }
  else
#endif
  {
    int c = cipher->pos;
    if (PREDICT_UNLIKELY(!len)) return;

    while (1) {
      do {
        if (len-- == 0) { cipher->pos = c; return; }
        *(data++) ^= cipher->buf[c];
      } while (++c != 16);
      cipher->pos = c = 0;
      if (PREDICT_UNLIKELY(! ++COUNTER(cipher, 0))) {
        if (PREDICT_UNLIKELY(! ++COUNTER(cipher, 1))) {
          if (PREDICT_UNLIKELY(! ++COUNTER(cipher, 2))) {
            ++COUNTER(cipher, 3);
            UPDATE_CTR_BUF(cipher, 3);
          }
          UPDATE_CTR_BUF(cipher, 2);
        }
        UPDATE_CTR_BUF(cipher, 1);
      }
      UPDATE_CTR_BUF(cipher, 0);
      _aes_fill_buf(cipher);
    }
  }
}

Here is the call graph for this function:

Here is the caller graph for this function:

aes_cnt_cipher_t* aes_new_cipher ( const char *  key,
const char *  iv 
)

Return a newly allocated counter-mode AES128 cipher implementation, using the 128-bit key key and the 128-bit IV iv.

Definition at line 315 of file aes.c.

{
  aes_cnt_cipher_t* result = tor_malloc_zero(sizeof(aes_cnt_cipher_t));

  aes_set_key(result, key, 128);
  aes_set_iv(result, iv);

  return result;
}

Here is the call graph for this function:

Here is the caller graph for this function:

static void aes_set_iv ( aes_cnt_cipher_t *  cipher,
const char *  iv 
) [static]

Reset the 128-bit counter of cipher to the 16-bit big-endian value in iv.

Definition at line 504 of file aes.c.

{
#ifdef USING_COUNTER_VARS
  cipher->counter3 = ntohl(get_uint32(iv));
  cipher->counter2 = ntohl(get_uint32(iv+4));
  cipher->counter1 = ntohl(get_uint32(iv+8));
  cipher->counter0 = ntohl(get_uint32(iv+12));
#endif
  cipher->pos = 0;
  memcpy(cipher->ctr_buf.buf, iv, 16);

#ifdef CAN_USE_OPENSSL_CTR
  if (!should_use_openssl_CTR)
#endif
    _aes_fill_buf(cipher);
}

Here is the call graph for this function:

Here is the caller graph for this function:

static void aes_set_key ( aes_cnt_cipher_t *  cipher,
const char *  key,
int  key_bits 
) [static]

Set the key of cipher to key, which is key_bits bits long (must be 128, 192, or 256).

Also resets the counter to 0.

Definition at line 330 of file aes.c.

{
  if (should_use_EVP) {
    const EVP_CIPHER *c;
    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, (const unsigned char*)key, NULL);
    cipher->using_evp = 1;
  } else {
    AES_set_encrypt_key((const unsigned char *)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;

#ifdef CAN_USE_OPENSSL_CTR
  if (should_use_openssl_CTR)
    memset(cipher->buf, 0, sizeof(cipher->buf));
  else
#endif
    _aes_fill_buf(cipher);
}

Here is the call graph for this function:

Here is the caller graph for this function:

int evaluate_ctr_for_aes ( void  )

Test the OpenSSL counter mode implementation to see whether it has the counter-mode bug from OpenSSL 1.0.0.

If the implementation works, then we will use it for future encryption/decryption operations.

We can't just look at the OpenSSL version, since some distributions update their OpenSSL packages without changing the version number.

Definition at line 235 of file aes.c.

{
#ifdef CAN_USE_OPENSSL_CTR
  /* Result of encrypting an all-zero block with an all-zero 128-bit AES key.
   * This should be the same as encrypting an all-zero block with an all-zero
   * 128-bit AES key in counter mode, starting at position 0 of the stream.
   */
  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;
  /* Encrypting a block one byte at a time should make the error manifest
   * itself for known bogus openssl versions. */
  for (i=0; i<16; ++i)
    AES_ctr128_encrypt(&zero[i], &output[i], 1, &key, ivec, ivec_tmp, &pos);

  if (memcmp(output, encrypt_zero, 16)) {
    /* Counter mode is buggy */
    log_notice(LD_CRYPTO, "This OpenSSL has a buggy version of counter mode; "
               "not using it.");
  } else {
    /* Counter mode is okay */
    log_notice(LD_CRYPTO, "This OpenSSL has a good implementation of counter "
               "mode; using it.");
    should_use_openssl_CTR = 1;
  }
#else
  log_notice(LD_CRYPTO, "This version of OpenSSL has a slow implementation of "
             "counter mode; not using it.");
#endif
  return 0;
}

Here is the caller graph for this function:

int evaluate_evp_for_aes ( int  force_val)

Check whether we should use the EVP interface for AES.

If force_val is nonnegative, we use use EVP iff it is true. Otherwise, we use EVP if there is an engine enabled for aes-ecb.

Definition at line 201 of file aes.c.

{
  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_notice(LD_CRYPTO, "AES engine \"%s\" found; using EVP_* functions.",
               ENGINE_get_name(e));
    should_use_EVP = 1;
  } else {
    log_notice(LD_CRYPTO, "No AES engine found; using AES_* functions.");
    should_use_EVP = 0;
  }
#endif

  return 0;
}

Here is the caller graph for this function:


Variable Documentation

int should_use_EVP = 0 [static]

True iff we should prefer the EVP implementation for AES, either because we're testing it or because we have hardware acceleration configured.

Definition at line 189 of file aes.c.