You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

chacha.c 8.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. /* Copyright (c) 2015, Vsevolod Stakhov
  2. * Copyright (c) 2015, Andrew Moon
  3. * All rights reserved.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions are met:
  7. * * Redistributions of source code must retain the above copyright
  8. * notice, this list of conditions and the following disclaimer.
  9. * * Redistributions in binary form must reproduce the above copyright
  10. * notice, this list of conditions and the following disclaimer in the
  11. * documentation and/or other materials provided with the distribution.
  12. *
  13. * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY
  14. * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  15. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  16. * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
  17. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  18. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  19. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  20. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  21. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  22. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  23. */
  24. #include "config.h"
  25. #include "cryptobox.h"
  26. #include "chacha.h"
  27. #include "platform_config.h"
  28. extern unsigned cpu_config;
  29. typedef struct chacha_impl_t {
  30. unsigned long cpu_flags;
  31. const char *desc;
  32. void (*chacha)(const chacha_key *key, const chacha_iv *iv,
  33. const unsigned char *in, unsigned char *out, size_t inlen,
  34. size_t rounds);
  35. void (*xchacha)(const chacha_key *key, const chacha_iv24 *iv,
  36. const unsigned char *in, unsigned char *out, size_t inlen,
  37. size_t rounds);
  38. void (*chacha_blocks)(chacha_state_internal *state,
  39. const unsigned char *in, unsigned char *out, size_t bytes);
  40. void (*hchacha)(const unsigned char key[32], const unsigned char iv[16],
  41. unsigned char out[32], size_t rounds);
  42. } chacha_impl_t;
  43. #define CHACHA_DECLARE(ext) \
  44. void chacha_##ext(const chacha_key *key, const chacha_iv *iv, const unsigned char *in, unsigned char *out, size_t inlen, size_t rounds); \
  45. void xchacha_##ext(const chacha_key *key, const chacha_iv24 *iv, const unsigned char *in, unsigned char *out, size_t inlen, size_t rounds); \
  46. void chacha_blocks_##ext(chacha_state_internal *state, const unsigned char *in, unsigned char *out, size_t bytes); \
  47. void hchacha_##ext(const unsigned char key[32], const unsigned char iv[16], unsigned char out[32], size_t rounds);
  48. #define CHACHA_IMPL(cpuflags, desc, ext) \
  49. { \
  50. (cpuflags), desc, chacha_##ext, xchacha_##ext, chacha_blocks_##ext, hchacha_##ext \
  51. }
  52. #if defined(HAVE_AVX2) && defined(__x86_64__)
  53. CHACHA_DECLARE(avx2)
  54. #define CHACHA_AVX2 CHACHA_IMPL(CPUID_AVX2, "avx2", avx2)
  55. #endif
  56. #if defined(HAVE_AVX) && defined(__x86_64__)
  57. CHACHA_DECLARE(avx)
  58. #define CHACHA_AVX CHACHA_IMPL(CPUID_AVX, "avx", avx)
  59. #endif
  60. #if defined(HAVE_SSE2) && defined(__x86_64__)
  61. CHACHA_DECLARE(sse2)
  62. #define CHACHA_SSE2 CHACHA_IMPL(CPUID_SSE2, "sse2", sse2)
  63. #endif
  64. CHACHA_DECLARE(ref)
  65. #define CHACHA_GENERIC CHACHA_IMPL(0, "generic", ref)
  66. static const chacha_impl_t chacha_list[] = {
  67. CHACHA_GENERIC,
  68. #if defined(CHACHA_AVX2) && defined(__x86_64__)
  69. CHACHA_AVX2,
  70. #endif
  71. #if defined(CHACHA_AVX) && defined(__x86_64__)
  72. CHACHA_AVX,
  73. #endif
  74. #if defined(CHACHA_SSE2) && defined(__x86_64__)
  75. CHACHA_SSE2
  76. #endif
  77. };
  78. static const chacha_impl_t *chacha_impl = &chacha_list[0];
  79. static int
  80. chacha_is_aligned(const void *p)
  81. {
  82. return ((size_t) p & (sizeof(size_t) - 1)) == 0;
  83. }
  84. const char *
  85. chacha_load(void)
  86. {
  87. unsigned int i;
  88. if (cpu_config != 0) {
  89. for (i = 0; i < G_N_ELEMENTS(chacha_list); i++) {
  90. if (chacha_list[i].cpu_flags & cpu_config) {
  91. chacha_impl = &chacha_list[i];
  92. break;
  93. }
  94. }
  95. }
  96. return chacha_impl->desc;
  97. }
  98. void chacha_init(chacha_state *S, const chacha_key *key,
  99. const chacha_iv *iv, size_t rounds)
  100. {
  101. chacha_state_internal *state = (chacha_state_internal *) S;
  102. memcpy(state->s + 0, key, 32);
  103. memset(state->s + 32, 0, 8);
  104. memcpy(state->s + 40, iv, 8);
  105. state->rounds = rounds;
  106. state->leftover = 0;
  107. }
  108. /* processes inlen bytes (can do partial blocks), handling input/output alignment */
  109. static void
  110. chacha_consume(chacha_state_internal *state,
  111. const unsigned char *in, unsigned char *out, size_t inlen)
  112. {
  113. unsigned char buffer[16 * CHACHA_BLOCKBYTES];
  114. int in_aligned, out_aligned;
  115. /* it's ok to call with 0 bytes */
  116. if (!inlen)
  117. return;
  118. /* if everything is aligned, handle directly */
  119. in_aligned = chacha_is_aligned(in);
  120. out_aligned = chacha_is_aligned(out);
  121. if (in_aligned && out_aligned) {
  122. chacha_impl->chacha_blocks(state, in, out, inlen);
  123. return;
  124. }
  125. /* copy the unaligned data to an aligned buffer and process in chunks */
  126. while (inlen) {
  127. const size_t bytes = (inlen > sizeof(buffer)) ? sizeof(buffer) : inlen;
  128. const unsigned char *src = in;
  129. unsigned char *dst = (out_aligned) ? out : buffer;
  130. if (!in_aligned) {
  131. memcpy(buffer, in, bytes);
  132. src = buffer;
  133. }
  134. chacha_impl->chacha_blocks(state, src, dst, bytes);
  135. if (!out_aligned)
  136. memcpy(out, buffer, bytes);
  137. if (in)
  138. in += bytes;
  139. out += bytes;
  140. inlen -= bytes;
  141. }
  142. }
  143. /* hchacha */
  144. void hchacha(const unsigned char key[32],
  145. const unsigned char iv[16], unsigned char out[32], size_t rounds)
  146. {
  147. chacha_impl->hchacha(key, iv, out, rounds);
  148. }
  149. /* update, returns number of bytes written to out */
  150. size_t
  151. chacha_update(chacha_state *S, const unsigned char *in, unsigned char *out,
  152. size_t inlen)
  153. {
  154. chacha_state_internal *state = (chacha_state_internal *) S;
  155. unsigned char *out_start = out;
  156. size_t bytes;
  157. /* enough for at least one block? */
  158. while ((state->leftover + inlen) >= CHACHA_BLOCKBYTES) {
  159. /* handle the previous data */
  160. if (state->leftover) {
  161. bytes = (CHACHA_BLOCKBYTES - state->leftover);
  162. if (in) {
  163. memcpy(state->buffer + state->leftover, in, bytes);
  164. in += bytes;
  165. }
  166. chacha_consume(state, (in) ? state->buffer : NULL, out,
  167. CHACHA_BLOCKBYTES);
  168. inlen -= bytes;
  169. out += CHACHA_BLOCKBYTES;
  170. state->leftover = 0;
  171. }
  172. /* handle the direct data */
  173. bytes = (inlen & ~(CHACHA_BLOCKBYTES - 1));
  174. if (bytes) {
  175. chacha_consume(state, in, out, bytes);
  176. inlen -= bytes;
  177. if (in)
  178. in += bytes;
  179. out += bytes;
  180. }
  181. }
  182. /* handle leftover data */
  183. if (inlen) {
  184. if (in)
  185. memcpy(state->buffer + state->leftover, in, inlen);
  186. else
  187. memset(state->buffer + state->leftover, 0, inlen);
  188. state->leftover += inlen;
  189. }
  190. return out - out_start;
  191. }
  192. /* finalize, write out any leftover data */
  193. size_t
  194. chacha_final(chacha_state *S, unsigned char *out)
  195. {
  196. chacha_state_internal *state = (chacha_state_internal *) S;
  197. size_t leftover = state->leftover;
  198. if (leftover) {
  199. if (chacha_is_aligned(out)) {
  200. chacha_impl->chacha_blocks(state, state->buffer, out, leftover);
  201. }
  202. else {
  203. chacha_impl->chacha_blocks(state, state->buffer, state->buffer,
  204. leftover);
  205. memcpy(out, state->buffer, leftover);
  206. }
  207. }
  208. rspamd_explicit_memzero(S, sizeof(chacha_state));
  209. return leftover;
  210. }
  211. /* one-shot, input/output assumed to be word aligned */
  212. void chacha(const chacha_key *key, const chacha_iv *iv,
  213. const unsigned char *in, unsigned char *out, size_t inlen,
  214. size_t rounds)
  215. {
  216. chacha_impl->chacha(key, iv, in, out, inlen, rounds);
  217. }
  218. /*
  219. xchacha, chacha with a 192 bit nonce
  220. */
  221. void xchacha_init(chacha_state *S, const chacha_key *key,
  222. const chacha_iv24 *iv, size_t rounds)
  223. {
  224. chacha_key subkey;
  225. hchacha(key->b, iv->b, subkey.b, rounds);
  226. chacha_init(S, &subkey, (chacha_iv *) (iv->b + 16), rounds);
  227. }
  228. /* one-shot, input/output assumed to be word aligned */
  229. void xchacha(const chacha_key *key, const chacha_iv24 *iv,
  230. const unsigned char *in, unsigned char *out, size_t inlen,
  231. size_t rounds)
  232. {
  233. chacha_impl->xchacha(key, iv, in, out, inlen, rounds);
  234. }