1 /* Copyright (c) 2018-2021, The Tor Project, Inc. */
2 /* See LICENSE for licensing information */
6 * @brief A rudimentary order-preserving encryption scheme.
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.)
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
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"
31 * How infrequent should the precomputed values be for this encryption?
32 * The choice of this value creates a space/time tradeoff.
34 * Note that this value must be a multiple of 16; see
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)
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
];
49 /** The type to add up in order to produce our OPE ciphertexts */
50 typedef uint16_t ope_val_t
;
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
)
61 #else /* !defined(WORDS_BIGENDIAN) */
62 #define ope_val_from_le(x) (x)
63 #endif /* defined(WORDS_BIGENDIAN) */
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>.
69 * Note that because the index is converted directly to an IV, it must be a
70 * multiple of the AES block size (16).
72 STATIC crypto_cipher_t
*
73 ope_get_cipher(const crypto_ope_t
*ope
, uint32_t initial_idx
)
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
));
81 return crypto_cipher_new_with_iv_and_bits(ope
->key
,
87 * Retrieve and add the next <b>n</b> values from the stream cipher <b>c</b>,
88 * and return their sum.
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).
94 * NOTE: this function is not constant-time.
97 sum_values_from_cipher(crypto_cipher_t
*c
, size_t n
)
100 ope_val_t buf
[BUFSZ
];
104 memset(buf
, 0, sizeof(buf
));
105 crypto_cipher_crypt_inplace(c
, (char*)buf
, BUFSZ
*sizeof(ope_val_t
));
107 for (i
= 0; i
< BUFSZ
; ++i
) {
108 total
+= ope_val_from_le(buf
[i
]);
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
]);
121 memset(buf
, 0, sizeof(buf
));
126 * Return a new crypto_ope_t object, using the provided 256-bit key.
129 crypto_ope_new(const uint8_t *key
)
131 crypto_ope_t
*ope
= tor_malloc_zero(sizeof(crypto_ope_t
));
132 memcpy(ope
->key
, key
, OPE_KEY_LEN
);
134 crypto_cipher_t
*cipher
= ope_get_cipher(ope
, 0);
138 for (i
= 0; i
< N_SAMPLES
; ++i
) {
139 v
+= sum_values_from_cipher(cipher
, SAMPLE_INTERVAL
);
143 crypto_cipher_free(cipher
);
147 /** Free all storage held in <b>ope</b>. */
149 crypto_ope_free_(crypto_ope_t
*ope
)
153 memwipe(ope
, 0, sizeof(*ope
));
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
162 * NOTE: this function is not constant-time.
165 crypto_ope_encrypt(const crypto_ope_t
*ope
, int plaintext
)
167 if (plaintext
<= 0 || plaintext
> OPE_INPUT_MAX
)
168 return CRYPTO_OPE_ERROR
;
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
;
174 if (sample_idx
== 0) {
177 v
= ope
->samples
[sample_idx
- 1];
179 crypto_cipher_t
*cipher
= ope_get_cipher(ope
, starting_iv
*sizeof(ope_val_t
));
181 v
+= sum_values_from_cipher(cipher
, remaining_values
);
183 crypto_cipher_free(cipher
);