123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185 |
- /* Copyright (c) 2018, The Tor Project, Inc. */
- /* See LICENSE for licensing information */
- /**
- * A rudimentary order-preserving encryption scheme.
- *
- * To compute the encryption of N, this scheme uses an AES-CTR stream to
- * generate M-byte values, and adds the first N of them together. (+1 each to
- * insure that the ciphertexts are strictly decreasing.)
- *
- * We use this for generating onion service revision counters based on the
- * current time, without leaking the amount of skew in our view of the current
- * time. MUCH more analysis would be needed before using it for anything
- * else!
- */
- #include "orconfig.h"
- #define CRYPTO_OPE_PRIVATE
- #include "lib/crypt_ops/crypto_ope.h"
- #include "lib/crypt_ops/crypto_util.h"
- #include "lib/crypt_ops/crypto_cipher.h"
- #include "lib/log/util_bug.h"
- #include "lib/malloc/malloc.h"
- #include "lib/arch/bytes.h"
- #include <string.h>
- /**
- * How infrequent should the precomputed values be for this encryption?
- * The choice of this value creates a space/time tradeoff.
- *
- * Note that this value must be a multiple of 16; see
- * ope_get_cipher()
- */
- #define SAMPLE_INTERVAL 1024
- /** Number of precomputed samples to make for each OPE key. */
- #define N_SAMPLES (OPE_INPUT_MAX / SAMPLE_INTERVAL)
- struct crypto_ope_t {
- /** An AES key for use with this object. */
- uint8_t key[OPE_KEY_LEN];
- /** Cached intermediate encryption values at SAMPLE_INTERVAL,
- * SAMPLE_INTERVAL*2,...SAMPLE_INTERVAL*N_SAMPLES */
- uint64_t samples[N_SAMPLES];
- };
- /** The type to add up in order to produce our OPE ciphertexts */
- typedef uint16_t ope_val_t;
- #ifdef WORDS_BIG_ENDIAN
- /** Convert an OPE value to little-endian */
- static inline ope_val_t
- ope_val_to_le(ope_val_t x)
- {
- return
- ((x) >> 8) |
- (((x)&0xff) << 8);
- }
- #else
- #define ope_val_to_le(x) (x)
- #endif
- /**
- * Return a new AES256-CTR stream cipher object for <b>ope</b>, ready to yield
- * bytes from the stream at position <b>initial_idx</b>.
- *
- * Note that because the index is converted directly to an IV, it must be a
- * multiple of the AES block size (16).
- */
- STATIC crypto_cipher_t *
- ope_get_cipher(const crypto_ope_t *ope, uint32_t initial_idx)
- {
- uint8_t iv[CIPHER_IV_LEN];
- tor_assert((initial_idx & 0xf) == 0);
- uint32_t n = tor_htonl(initial_idx >> 4);
- memset(iv, 0, sizeof(iv));
- memcpy(iv + CIPHER_IV_LEN - sizeof(n), &n, sizeof(n));
- return crypto_cipher_new_with_iv_and_bits(ope->key,
- iv,
- OPE_KEY_LEN * 8);
- }
- /**
- * Retrieve and add the next <b>n</b> values from the stream cipher <b>c</b>,
- * and return their sum.
- *
- * Note that values are taken in little-endian order (for performance on
- * prevalent hardware), and are mapped from range 0..2^n-1 to range 1..2^n (so
- * that each input encrypts to a different output).
- *
- * NOTE: this function is not constant-time.
- */
- STATIC uint64_t
- sum_values_from_cipher(crypto_cipher_t *c, size_t n)
- {
- #define BUFSZ 256
- ope_val_t buf[BUFSZ];
- uint64_t total = 0;
- unsigned i;
- while (n >= BUFSZ) {
- memset(buf, 0, sizeof(buf));
- crypto_cipher_crypt_inplace(c, (char*)buf, BUFSZ*sizeof(ope_val_t));
- for (i = 0; i < BUFSZ; ++i) {
- total += ope_val_to_le(buf[i]);
- total += 1;
- }
- n -= BUFSZ;
- }
- memset(buf, 0, n*sizeof(ope_val_t));
- crypto_cipher_crypt_inplace(c, (char*)buf, n*sizeof(ope_val_t));
- for (i = 0; i < n; ++i) {
- total += ope_val_to_le(buf[i]);
- total += 1;
- }
- memset(buf, 0, sizeof(buf));
- return total;
- }
- /**
- * Return a new crypto_ope_t object, using the provided 256-bit key.
- */
- crypto_ope_t *
- crypto_ope_new(const uint8_t *key)
- {
- crypto_ope_t *ope = tor_malloc_zero(sizeof(crypto_ope_t));
- memcpy(ope->key, key, OPE_KEY_LEN);
- crypto_cipher_t *cipher = ope_get_cipher(ope, 0);
- uint64_t v = 0;
- int i;
- for (i = 0; i < N_SAMPLES; ++i) {
- v += sum_values_from_cipher(cipher, SAMPLE_INTERVAL);
- ope->samples[i] = v;
- }
- crypto_cipher_free(cipher);
- return ope;
- }
- /** Free all storage held in <>ope</b>. */
- void
- crypto_ope_free_(crypto_ope_t *ope)
- {
- if (!ope)
- return;
- memwipe(ope, 0, sizeof(*ope));
- tor_free(ope);
- }
- /**
- * Return the encrypted value corresponding to <b>input</b>. The input value
- * must be in range 1..OPE_INPUT_MAX. Returns CRYPTO_OPE_ERROR on an invalid
- * input.
- *
- * NOTE: this function is not constant-time.
- */
- uint64_t
- crypto_ope_encrypt(const crypto_ope_t *ope, int plaintext)
- {
- if (plaintext <= 0 || plaintext > OPE_INPUT_MAX)
- return CRYPTO_OPE_ERROR;
- const int sample_idx = (plaintext / SAMPLE_INTERVAL);
- const int starting_iv = sample_idx * SAMPLE_INTERVAL;
- const int remaining_values = plaintext - starting_iv;
- uint64_t v;
- if (sample_idx == 0) {
- v = 0;
- } else {
- v = ope->samples[sample_idx - 1];
- }
- crypto_cipher_t *cipher = ope_get_cipher(ope, starting_iv*sizeof(ope_val_t));
- v += sum_values_from_cipher(cipher, remaining_values);
- crypto_cipher_free(cipher);
- return v;
- }
|