|
| 1 | +/* |
| 2 | + * Copyright 2015-2016 The OpenSSL Project Authors. All Rights Reserved. |
| 3 | + * |
| 4 | + * Licensed under the OpenSSL license (the "License"). You may not use |
| 5 | + * this file except in compliance with the License. You can obtain a copy |
| 6 | + * in the file LICENSE in the source distribution or at |
| 7 | + * https://www.openssl.org/source/license.html |
| 8 | + */ |
| 9 | + |
| 10 | +//OBF: Adapted from OpenSSL 1.1.0g, file internal/chacha.h: |
| 11 | + |
| 12 | +/* |
| 13 | + * ChaCha20_ctr32 encrypts |len| bytes from |inp| with the given key and |
| 14 | + * nonce and writes the result to |out|, which may be equal to |inp|. |
| 15 | + * The |key| is not 32 bytes of verbatim key material though, but the |
| 16 | + * said material collected into 8 32-bit elements array in host byte |
| 17 | + * order. Same approach applies to nonce: the |counter| argument is |
| 18 | + * pointer to concatenated nonce and counter values collected into 4 |
| 19 | + * 32-bit elements. This, passing crypto material collected into 32-bit |
| 20 | + * elements as opposite to passing verbatim byte vectors, is chosen for |
| 21 | + * efficiency in multi-call scenarios. |
| 22 | + */ |
| 23 | + |
| 24 | +/* |
| 25 | + * You can notice that there is no key setup procedure. Because it's |
| 26 | + * as trivial as collecting bytes into 32-bit elements, it's reckoned |
| 27 | + * that below macro is sufficient. |
| 28 | + */ |
| 29 | + |
| 30 | +//OBF: Adapted from OpenSSL 1.1.0g, file chacha_enc.c |
| 31 | +// As we're moving all the stuff to headers, to avoid potential for name collisions we have to: |
| 32 | +// Move whatever-possible to namespace ithare::obf::tls:: |
| 33 | +// Add prefix ITHARE_OBF_TLS_ to all the macros (as macros don't belong to any namespace) |
| 34 | + |
| 35 | +#ifndef ithare_obf_tls_crypto_chacha_h_included |
| 36 | +#define ithare_obf_tls_crypto_chacha_h_included |
| 37 | + |
| 38 | +#include "../../../src/obf.h" |
| 39 | +#include "../../../src/obf_lib.h" |
| 40 | + |
| 41 | +namespace ithare { |
| 42 | + namespace obf { |
| 43 | + namespace tls { |
| 44 | + |
| 45 | +constexpr int CHACHA_KEY_SIZE = 32; |
| 46 | +constexpr int CHACHA_CTR_SIZE = 16; |
| 47 | +constexpr int CHACHA_BLK_SIZE = 64; |
| 48 | + |
| 49 | +#define ITHARE_OBF_TLS_CHACHA_U8TOU32(p) ( \ |
| 50 | + ((unsigned int)(p)[0]) | ((unsigned int)(p)[1]<<8) | \ |
| 51 | + ((unsigned int)(p)[2]<<16) | ((unsigned int)(p)[3]<<24) ) |
| 52 | + |
| 53 | +/* Adapted from the public domain code by D. Bernstein from SUPERCOP. */ |
| 54 | + |
| 55 | +union chacha_buf { |
| 56 | + uint32_t u[16]; |
| 57 | + uint8_t c[64];//CANNOT be used in constexpr calls; use get_byte_n() below instead in constexpr |
| 58 | + |
| 59 | + constexpr uint8_t get_byte_n( size_t idx) const { |
| 60 | + assert(idx <= 63); |
| 61 | + size_t uidx = idx / 4; |
| 62 | + size_t intraidx = idx % 4; |
| 63 | + return uint8_t(u[uidx] >> (8*intraidx)); |
| 64 | + } |
| 65 | +}; |
| 66 | +static_assert(sizeof(chacha_buf)==64); |
| 67 | + |
| 68 | +# define ITHARE_OBF_TLS_ROTATE(v, n) (((v) << (n)) | ((v) >> (32 - (n)))) |
| 69 | + |
| 70 | +# define ITHARE_OBF_TLS_U32TO8_LITTLE(p, v) do { \ |
| 71 | + (p)[0] = (uint8_t)(v >> 0); \ |
| 72 | + (p)[1] = (uint8_t)(v >> 8); \ |
| 73 | + (p)[2] = (uint8_t)(v >> 16); \ |
| 74 | + (p)[3] = (uint8_t)(v >> 24); \ |
| 75 | + } while(0) |
| 76 | + |
| 77 | +/* QUARTERROUND updates a, b, c, d with a ChaCha "quarter" round. */ |
| 78 | +# define ITHARE_OBF_TLS_QUARTERROUND(a,b,c,d) ( \ |
| 79 | + x[a] += x[b], x[d] = ITHARE_OBF_TLS_ROTATE((x[d] ^ x[a]),16), \ |
| 80 | + x[c] += x[d], x[b] = ITHARE_OBF_TLS_ROTATE((x[b] ^ x[c]),12), \ |
| 81 | + x[a] += x[b], x[d] = ITHARE_OBF_TLS_ROTATE((x[d] ^ x[a]), 8), \ |
| 82 | + x[c] += x[d], x[b] = ITHARE_OBF_TLS_ROTATE((x[b] ^ x[c]), 7) ) |
| 83 | + |
| 84 | +/* chacha_core performs 20 rounds of ChaCha on the input words in |
| 85 | + * |input| and writes the 64 output bytes to |output|. */ |
| 86 | +ITHARE_OBF_DECLARELIBFUNC |
| 87 | +void chacha20_core(chacha_buf *output, const uint32_t input[16]) |
| 88 | +{ |
| 89 | + ITHARE_OBF_DBGPRINTLIBFUNCNAMEX("chacha_core"); |
| 90 | + ITHARE_OBFLIBM1(uint32_t) x[16] = {}; ITHARE_OBF_DBGPRINTLIBX(x[0]); |
| 91 | + ITHARE_OBF_CALLFROMLIB(obf_copyarray)(x,input); |
| 92 | + |
| 93 | + for (int i = 20; i > 0; i -= 2) { |
| 94 | + ITHARE_OBF_TLS_QUARTERROUND(0, 4, 8, 12); |
| 95 | + ITHARE_OBF_TLS_QUARTERROUND(1, 5, 9, 13); |
| 96 | + ITHARE_OBF_TLS_QUARTERROUND(2, 6, 10, 14); |
| 97 | + ITHARE_OBF_TLS_QUARTERROUND(3, 7, 11, 15); |
| 98 | + ITHARE_OBF_TLS_QUARTERROUND(0, 5, 10, 15); |
| 99 | + ITHARE_OBF_TLS_QUARTERROUND(1, 6, 11, 12); |
| 100 | + ITHARE_OBF_TLS_QUARTERROUND(2, 7, 8, 13); |
| 101 | + ITHARE_OBF_TLS_QUARTERROUND(3, 4, 9, 14); |
| 102 | + } |
| 103 | + |
| 104 | + static_assert(obf_endian::native == obf_endian::little); |
| 105 | + if constexpr (obf_endian::native == obf_endian::little) { |
| 106 | + for (int i = 0; i < 16; ++i) |
| 107 | + output->u[i] = x[i] + input[i]; |
| 108 | + } |
| 109 | + /* else ++big-endian: test |
| 110 | + for (int i = 0; i < 16; ++i) |
| 111 | + ITHARE_OBF_TLS_U32TO8_LITTLE(output->c + 4 * i, (x[i] + input[i])); |
| 112 | + }*/ |
| 113 | +} |
| 114 | + |
| 115 | +ITHARE_OBF_DECLARELIBFUNC |
| 116 | +void ChaCha20_ctr32(unsigned char *out, const unsigned char *inp, |
| 117 | + size_t len, const unsigned int key[8], |
| 118 | + const unsigned int counter[4]) |
| 119 | +{ |
| 120 | + uint32_t input[16] = {};//TODO: move initialization here |
| 121 | + |
| 122 | + /* sigma constant "expand 32-byte k" in little-endian encoding */ |
| 123 | + input[0] = ((uint32_t)'e') | ((uint32_t)'x'<<8) | ((uint32_t)'p'<<16) | ((uint32_t)'a'<<24); |
| 124 | + input[1] = ((uint32_t)'n') | ((uint32_t)'d'<<8) | ((uint32_t)' '<<16) | ((uint32_t)'3'<<24); |
| 125 | + input[2] = ((uint32_t)'2') | ((uint32_t)'-'<<8) | ((uint32_t)'b'<<16) | ((uint32_t)'y'<<24); |
| 126 | + input[3] = ((uint32_t)'t') | ((uint32_t)'e'<<8) | ((uint32_t)' '<<16) | ((uint32_t)'k'<<24); |
| 127 | + |
| 128 | + input[4] = key[0]; |
| 129 | + input[5] = key[1]; |
| 130 | + input[6] = key[2]; |
| 131 | + input[7] = key[3]; |
| 132 | + input[8] = key[4]; |
| 133 | + input[9] = key[5]; |
| 134 | + input[10] = key[6]; |
| 135 | + input[11] = key[7]; |
| 136 | + |
| 137 | + input[12] = counter[0]; |
| 138 | + input[13] = counter[1]; |
| 139 | + input[14] = counter[2]; |
| 140 | + input[15] = counter[3]; |
| 141 | + |
| 142 | + chacha_buf buf = {}; |
| 143 | + while (len > 0) { |
| 144 | + size_t todo = sizeof(buf); |
| 145 | + if (len < todo) |
| 146 | + todo = len; |
| 147 | + |
| 148 | + ITHARE_OBF_CALLFROMLIB(chacha20_core)(&buf, input); |
| 149 | + |
| 150 | + for (size_t i = 0; i < todo; i++) { |
| 151 | + if constexpr(obfflags&obf_flag_is_constexpr)//potential big-endian issues? |
| 152 | + out[i] = inp[i] ^ buf.get_byte_n(i); |
| 153 | + else |
| 154 | + out[i] = inp[i] ^ buf.c[i]; |
| 155 | + } |
| 156 | + out += todo; |
| 157 | + inp += todo; |
| 158 | + len -= todo; |
| 159 | + |
| 160 | + /* |
| 161 | + * Advance 32-bit counter. Note that as subroutine is so to |
| 162 | + * say nonce-agnostic, this limited counter width doesn't |
| 163 | + * prevent caller from implementing wider counter. It would |
| 164 | + * simply take two calls split on counter overflow... |
| 165 | + */ |
| 166 | + input[12]++; |
| 167 | + } |
| 168 | +} |
| 169 | + |
| 170 | +//OBF: Adapted from OpenSSL 1.1.0g, CHACHA part of file crypto/evp/e_chacha20_poly1305.c |
| 171 | +//OBF: combined stuff into EVP_CHACHA class |
| 172 | + |
| 173 | +class EVP_CHACHA { |
| 174 | + struct KEY {//former EVP_CHACHA_KEY |
| 175 | + struct { |
| 176 | + alignas(double) /* this ensures even sizeof(EVP_CHACHA_KEY)%8==0 */ |
| 177 | + unsigned int d[CHACHA_KEY_SIZE / 4]; |
| 178 | + } key; |
| 179 | + unsigned int counter[CHACHA_CTR_SIZE / 4]; |
| 180 | + unsigned char buf[CHACHA_BLK_SIZE]; |
| 181 | + unsigned int partial_len; |
| 182 | + }; |
| 183 | + KEY key; |
| 184 | + |
| 185 | + constexpr EVP_CHACHA( obf_private_constructor_tag, const KEY&& key_ ) |
| 186 | + : key(std::move(key_)) { |
| 187 | + } |
| 188 | + ITHARE_OBF_DECLARELIBFUNC |
| 189 | + static KEY init_key(const unsigned char user_key[CHACHA_KEY_SIZE], |
| 190 | + const unsigned char iv[CHACHA_CTR_SIZE], int enc){ |
| 191 | + KEY key = {}; |
| 192 | + |
| 193 | + if (user_key) |
| 194 | + for (unsigned int i = 0; i < CHACHA_KEY_SIZE; i+=4) { |
| 195 | + key.key.d[i/4] = ITHARE_OBF_TLS_CHACHA_U8TOU32(user_key+i); |
| 196 | + } |
| 197 | + |
| 198 | + if (iv) |
| 199 | + for (unsigned int i = 0; i < CHACHA_CTR_SIZE; i+=4) { |
| 200 | + key.counter[i/4] = ITHARE_OBF_TLS_CHACHA_U8TOU32(iv+i); |
| 201 | + } |
| 202 | + |
| 203 | + key.partial_len = 0; |
| 204 | + return key; |
| 205 | + } |
| 206 | + ITHARE_OBF_DECLARELIBFUNC |
| 207 | + static void cipher( KEY& key, unsigned char *out, |
| 208 | + const unsigned char *inp, size_t len) |
| 209 | + { |
| 210 | + ITHARE_OBF_DBGPRINTLIBFUNCNAMEX("EVP_CHACHA::cipher"); |
| 211 | + ITHARE_OBFLIB(unsigned int) n = key.partial_len; ITHARE_OBF_DBGPRINTLIBX(n); |
| 212 | + if (n) { |
| 213 | + while (len && n < ITHARE_OBFILIB(CHACHA_BLK_SIZE)) { |
| 214 | + *out++ = *inp++ ^ key.buf[n++]; |
| 215 | + len--; |
| 216 | + } |
| 217 | + key.partial_len = n; |
| 218 | + |
| 219 | + if (len == 0) |
| 220 | + return; |
| 221 | + |
| 222 | + if (n == CHACHA_BLK_SIZE) { |
| 223 | + key.partial_len = 0; |
| 224 | + key.counter[0]++; |
| 225 | + if (key.counter[0] == 0) |
| 226 | + key.counter[1]++; |
| 227 | + } |
| 228 | + } |
| 229 | + |
| 230 | + unsigned int rem = (unsigned int)(len % CHACHA_BLK_SIZE); |
| 231 | + len -= rem; |
| 232 | + unsigned int ctr32 = key.counter[0];//TODO: is it really unsigned int, or maybe uint32_t? |
| 233 | + while (len >= CHACHA_BLK_SIZE) { |
| 234 | + size_t blocks = len / CHACHA_BLK_SIZE; |
| 235 | + /* |
| 236 | + * 1<<28 is just a not-so-small yet not-so-large number... |
| 237 | + * Below condition is practically never met, but it has to |
| 238 | + * be checked for code correctness. |
| 239 | + */ |
| 240 | + if (sizeof(size_t)>sizeof(unsigned int) && blocks>(1U<<28)) |
| 241 | + blocks = (1U<<28); |
| 242 | + |
| 243 | + /* |
| 244 | + * As ChaCha20_ctr32 operates on 32-bit counter, caller |
| 245 | + * has to handle overflow. 'if' below detects the |
| 246 | + * overflow, which is then handled by limiting the |
| 247 | + * amount of blocks to the exact overflow point... |
| 248 | + */ |
| 249 | + ctr32 += (unsigned int)blocks; |
| 250 | + if (ctr32 < blocks) { |
| 251 | + blocks -= ctr32; |
| 252 | + ctr32 = 0; |
| 253 | + } |
| 254 | + blocks *= CHACHA_BLK_SIZE; |
| 255 | + ITHARE_OBF_CALLFROMLIB(ChaCha20_ctr32)(out, inp, blocks, key.key.d, key.counter); |
| 256 | + len -= blocks; |
| 257 | + inp += blocks; |
| 258 | + out += blocks; |
| 259 | + |
| 260 | + key.counter[0] = ctr32; |
| 261 | + if (ctr32 == 0) key.counter[1]++; |
| 262 | + } |
| 263 | + |
| 264 | + if (rem) { |
| 265 | + ITHARE_OBF_CALLFROMLIB(obf_zeroarray)(key.buf); |
| 266 | + ITHARE_OBF_CALLFROMLIB(ChaCha20_ctr32)(key.buf, key.buf, CHACHA_BLK_SIZE, |
| 267 | + key.key.d, key.counter); |
| 268 | + for (n = 0; n < rem; n++) |
| 269 | + out[n] = inp[n] ^ key.buf[n]; |
| 270 | + key.partial_len = rem; |
| 271 | + } |
| 272 | + } |
| 273 | + |
| 274 | + public: |
| 275 | + ITHARE_OBF_DECLARELIBFUNC //cannot really invoke constructors with explicit template parameters :-( |
| 276 | + // use EVP_CHACHA::construct<...> defined below instead |
| 277 | + EVP_CHACHA( const unsigned char user_key[CHACHA_KEY_SIZE], |
| 278 | + const unsigned char iv[CHACHA_CTR_SIZE], int enc) |
| 279 | + : key(init_key(user_key,iv,enc)) { |
| 280 | + } |
| 281 | + ITHARE_OBF_DECLARELIBFUNC |
| 282 | + static EVP_CHACHA construct(const unsigned char user_key[CHACHA_KEY_SIZE], |
| 283 | + const unsigned char iv[CHACHA_CTR_SIZE], int enc){ |
| 284 | + return EVP_CHACHA(obf_private_constructor_tag(),std::move(init_key(user_key,iv,enc))); |
| 285 | + } |
| 286 | + |
| 287 | + ITHARE_OBF_DECLARELIBFUNC |
| 288 | + void cipher(unsigned char *out, |
| 289 | + const unsigned char *inp, size_t len) { |
| 290 | + ITHARE_OBF_CALLFROMLIB(cipher)(key, out, inp, len); |
| 291 | + } |
| 292 | + |
| 293 | + ITHARE_OBF_DECLARELIBFUNC_WITHEXTRA(size_t N) |
| 294 | + std::pair<EVP_CHACHA,ObfArrayWrapper<unsigned char,N> > constexpr_cipher(const unsigned char (&inp)[N]) const { |
| 295 | + KEY modifiable_key = key; |
| 296 | + ObfArrayWrapper<unsigned char,N> out = {}; |
| 297 | + ITHARE_OBF_CALL_AS_CONSTEXPR(cipher)(modifiable_key,out.arr,inp,N); |
| 298 | + EVP_CHACHA new_state = EVP_CHACHA(obf_private_constructor_tag(),std::move(modifiable_key)); |
| 299 | + return std::pair<EVP_CHACHA,ObfArrayWrapper<unsigned char,N>>(new_state,out); |
| 300 | + } |
| 301 | +}; |
| 302 | + |
| 303 | + |
| 304 | + |
| 305 | +#if 0 |
| 306 | +static const EVP_CIPHER chacha20 = { |
| 307 | + NID_chacha20, |
| 308 | + 1, /* block_size */ |
| 309 | + CHACHA_KEY_SIZE, /* key_len */ |
| 310 | + CHACHA_CTR_SIZE, /* iv_len, 128-bit counter in the context */ |
| 311 | + EVP_CIPH_CUSTOM_IV | EVP_CIPH_ALWAYS_CALL_INIT, |
| 312 | + chacha_init_key, |
| 313 | + chacha_cipher, |
| 314 | + NULL, |
| 315 | + sizeof(EVP_CHACHA_KEY), |
| 316 | + NULL, |
| 317 | + NULL, |
| 318 | + NULL, |
| 319 | + NULL |
| 320 | +}; |
| 321 | + |
| 322 | +const EVP_CIPHER *EVP_chacha20(void) |
| 323 | +{ |
| 324 | + return (&chacha20); |
| 325 | +} |
| 326 | +#endif |
| 327 | + |
| 328 | +#undef ITHARE_OBF_TLS_QUARTERROUND |
| 329 | +#undef ITHARE_OBF_TLS_U32TO8_LITTLE |
| 330 | +#undef ITHARE_OBF_TLS_ROTATE |
| 331 | +#undef ITHARE_OBF_TLS_CHACHA_U8TOU32 |
| 332 | + |
| 333 | + }//namespace tls |
| 334 | + }//namespace obf |
| 335 | +}//namespace ithare |
| 336 | + |
| 337 | +#endif //#ifndef ithare_obf_tls_crypto_chacha_h_included |
0 commit comments