tor

The Tor anonymity network
git clone https://git.dasho.dev/tor.git
Log | Files | Refs | README | LICENSE

crypto_ope.c (5109B)


      1 /* Copyright (c) 2018-2021, The Tor Project, Inc. */
      2 /* See LICENSE for licensing information */
      3 
      4 /**
      5 * @file crypto_ope.c
      6 * @brief A rudimentary order-preserving encryption scheme.
      7 *
      8 * To compute the encryption of N, this scheme uses an AES-CTR stream to
      9 * generate M-byte values, and adds the first N of them together. (+1 each to
     10 * insure that the ciphertexts are strictly decreasing.)
     11 *
     12 * We use this for generating onion service revision counters based on the
     13 * current time, without leaking the amount of skew in our view of the current
     14 * time.  MUCH more analysis would be needed before using it for anything
     15 * else!
     16 */
     17 
     18 #include "orconfig.h"
     19 
     20 #define CRYPTO_OPE_PRIVATE
     21 #include "lib/crypt_ops/crypto_ope.h"
     22 #include "lib/crypt_ops/crypto_util.h"
     23 #include "lib/crypt_ops/crypto_cipher.h"
     24 #include "lib/log/util_bug.h"
     25 #include "lib/malloc/malloc.h"
     26 #include "lib/arch/bytes.h"
     27 
     28 #include <string.h>
     29 
     30 /**
     31 * How infrequent should the precomputed values be for this encryption?
     32 * The choice of this value creates a space/time tradeoff.
     33 *
     34 * Note that this value must be a multiple of 16; see
     35 * ope_get_cipher()
     36 */
     37 #define SAMPLE_INTERVAL 1024
     38 /** Number of precomputed samples to make for each OPE key. */
     39 #define N_SAMPLES (OPE_INPUT_MAX / SAMPLE_INTERVAL)
     40 
     41 struct crypto_ope_t {
     42  /** An AES key for use with this object. */
     43  uint8_t key[OPE_KEY_LEN];
     44  /** Cached intermediate encryption values at SAMPLE_INTERVAL,
     45   * SAMPLE_INTERVAL*2,...SAMPLE_INTERVAL*N_SAMPLES */
     46  uint64_t samples[N_SAMPLES];
     47 };
     48 
     49 /** The type to add up in order to produce our OPE ciphertexts */
     50 typedef uint16_t ope_val_t;
     51 
     52 #ifdef WORDS_BIGENDIAN
     53 /** Convert an OPE value from little-endian. */
     54 static inline ope_val_t
     55 ope_val_from_le(ope_val_t x)
     56 {
     57  return
     58    ((x) >> 8) |
     59    (((x)&0xff) << 8);
     60 }
     61 #else /* !defined(WORDS_BIGENDIAN) */
     62 #define ope_val_from_le(x) (x)
     63 #endif /* defined(WORDS_BIGENDIAN) */
     64 
     65 /**
     66 * Return a new AES256-CTR stream cipher object for <b>ope</b>, ready to yield
     67 * bytes from the stream at position <b>initial_idx</b>.
     68 *
     69 * Note that because the index is converted directly to an IV, it must be a
     70 * multiple of the AES block size (16).
     71 */
     72 STATIC crypto_cipher_t *
     73 ope_get_cipher(const crypto_ope_t *ope, uint32_t initial_idx)
     74 {
     75  uint8_t iv[CIPHER_IV_LEN];
     76  tor_assert((initial_idx & 0xf) == 0);
     77  uint32_t n = tor_htonl(initial_idx >> 4);
     78  memset(iv, 0, sizeof(iv));
     79  memcpy(iv + CIPHER_IV_LEN - sizeof(n), &n, sizeof(n));
     80 
     81  return crypto_cipher_new_with_iv_and_bits(ope->key,
     82                                            iv,
     83                                            OPE_KEY_LEN * 8);
     84 }
     85 
     86 /**
     87 * Retrieve and add the next <b>n</b> values from the stream cipher <b>c</b>,
     88 * and return their sum.
     89 *
     90 * Note that values are taken in little-endian order (for performance on
     91 * prevalent hardware), and are mapped from range 0..2^n-1 to range 1..2^n (so
     92 * that each input encrypts to a different output).
     93 *
     94 * NOTE: this function is not constant-time.
     95 */
     96 STATIC uint64_t
     97 sum_values_from_cipher(crypto_cipher_t *c, size_t n)
     98 {
     99 #define BUFSZ 256
    100  ope_val_t buf[BUFSZ];
    101  uint64_t total = 0;
    102  unsigned i;
    103  while (n >= BUFSZ) {
    104    memset(buf, 0, sizeof(buf));
    105    crypto_cipher_crypt_inplace(c, (char*)buf, BUFSZ*sizeof(ope_val_t));
    106 
    107    for (i = 0; i < BUFSZ; ++i) {
    108      total += ope_val_from_le(buf[i]);
    109      total += 1;
    110    }
    111    n -= BUFSZ;
    112  }
    113 
    114  memset(buf, 0, n*sizeof(ope_val_t));
    115  crypto_cipher_crypt_inplace(c, (char*)buf, n*sizeof(ope_val_t));
    116  for (i = 0; i < n; ++i) {
    117    total += ope_val_from_le(buf[i]);
    118    total += 1;
    119  }
    120 
    121  memset(buf, 0, sizeof(buf));
    122  return total;
    123 }
    124 
    125 /**
    126 * Return a new crypto_ope_t object, using the provided 256-bit key.
    127 */
    128 crypto_ope_t *
    129 crypto_ope_new(const uint8_t *key)
    130 {
    131  crypto_ope_t *ope = tor_malloc_zero(sizeof(crypto_ope_t));
    132  memcpy(ope->key, key, OPE_KEY_LEN);
    133 
    134  crypto_cipher_t *cipher = ope_get_cipher(ope, 0);
    135 
    136  uint64_t v = 0;
    137  int i;
    138  for (i = 0; i < N_SAMPLES; ++i) {
    139    v += sum_values_from_cipher(cipher, SAMPLE_INTERVAL);
    140    ope->samples[i] = v;
    141  }
    142 
    143  crypto_cipher_free(cipher);
    144  return ope;
    145 }
    146 
    147 /** Free all storage held in <b>ope</b>. */
    148 void
    149 crypto_ope_free_(crypto_ope_t *ope)
    150 {
    151  if (!ope)
    152    return;
    153  memwipe(ope, 0, sizeof(*ope));
    154  tor_free(ope);
    155 }
    156 
    157 /**
    158 * Return the encrypted value corresponding to <b>input</b>.  The input value
    159 * must be in range 1..OPE_INPUT_MAX.  Returns CRYPTO_OPE_ERROR on an invalid
    160 * input.
    161 *
    162 * NOTE: this function is not constant-time.
    163 */
    164 uint64_t
    165 crypto_ope_encrypt(const crypto_ope_t *ope, int plaintext)
    166 {
    167  if (plaintext <= 0 || plaintext > OPE_INPUT_MAX)
    168    return CRYPTO_OPE_ERROR;
    169 
    170  const int sample_idx = (plaintext / SAMPLE_INTERVAL);
    171  const int starting_iv = sample_idx * SAMPLE_INTERVAL;
    172  const int remaining_values =  plaintext - starting_iv;
    173  uint64_t v;
    174  if (sample_idx == 0) {
    175    v = 0;
    176  } else {
    177    v = ope->samples[sample_idx - 1];
    178  }
    179  crypto_cipher_t *cipher = ope_get_cipher(ope, starting_iv*sizeof(ope_val_t));
    180 
    181  v += sum_values_from_cipher(cipher, remaining_values);
    182 
    183  crypto_cipher_free(cipher);
    184 
    185  return v;
    186 }