chacha20.c (3012B)
1 #include "chacha20.h" 2 3 #include <endian.h> 4 #include <string.h> 5 6 struct chacha20_ctx { 7 alignas(CHACHA20_ALIGN) 8 chacha20_word_t vs[CHACHA20_BLOCK_WORDS]; 9 }; 10 11 void 12 _chacha20_init(struct chacha20_ctx *ctx, 13 unsigned char key[static CHACHA20_KEY_SIZE], 14 uint32_t ctr, 15 unsigned char iv[static CHACHA20_IV_SIZE]) 16 { 17 ctx->vs[0] = 0x61707865; 18 ctx->vs[1] = 0x3320646e; 19 ctx->vs[2] = 0x79622d32; 20 ctx->vs[3] = 0x6b206574; 21 22 memcpy(&ctx->vs[4], key, CHACHA20_KEY_SIZE); 23 24 ctx->vs[12] = ctr; 25 26 memcpy(&ctx->vs[13], iv, CHACHA20_IV_SIZE); 27 } 28 29 #define CHACHA20_ROTL(v, n, b) (((v) << (n)) | ((v) >> ((b) - (n)))) 30 31 #define CHACHA20_QUARTERROUND(a, b, c, d) \ 32 { \ 33 a += b; d ^= a; d = CHACHA20_ROTL(d, 16, 32); \ 34 c += d; b ^= c; b = CHACHA20_ROTL(b, 12, 32); \ 35 a += b; d ^= a; d = CHACHA20_ROTL(d, 8, 32); \ 36 c += d; b ^= c; b = CHACHA20_ROTL(b, 7, 32); \ 37 } 38 39 static void 40 _chacha20_inner_block(struct chacha20_ctx *ctx) 41 { 42 CHACHA20_QUARTERROUND(ctx->vs[0], ctx->vs[4], ctx->vs[8], ctx->vs[12]); 43 CHACHA20_QUARTERROUND(ctx->vs[1], ctx->vs[5], ctx->vs[9], ctx->vs[13]); 44 CHACHA20_QUARTERROUND(ctx->vs[2], ctx->vs[6], ctx->vs[10], ctx->vs[14]); 45 CHACHA20_QUARTERROUND(ctx->vs[3], ctx->vs[7], ctx->vs[11], ctx->vs[15]); 46 47 CHACHA20_QUARTERROUND(ctx->vs[0], ctx->vs[5], ctx->vs[10], ctx->vs[15]); 48 CHACHA20_QUARTERROUND(ctx->vs[1], ctx->vs[6], ctx->vs[11], ctx->vs[12]); 49 CHACHA20_QUARTERROUND(ctx->vs[2], ctx->vs[7], ctx->vs[8], ctx->vs[13]); 50 CHACHA20_QUARTERROUND(ctx->vs[3], ctx->vs[4], ctx->vs[9], ctx->vs[14]); 51 } 52 53 static void 54 _chacha20_block(struct chacha20_ctx *ctx, 55 unsigned char key[static CHACHA20_KEY_SIZE], 56 uint32_t ctr, 57 unsigned char iv[static CHACHA20_IV_SIZE]) 58 { 59 _chacha20_init(ctx, key, ctr, iv); 60 61 struct chacha20_ctx save; 62 memcpy(&save, ctx, sizeof *ctx); 63 64 for (size_t i = 0; i < CHACHA20_ROUNDS; i++) { 65 _chacha20_inner_block(ctx); 66 } 67 68 for (size_t i = 0; i < CHACHA20_BLOCK_WORDS; i++) { 69 save.vs[i] += ctx->vs[i]; 70 ctx->vs[i] = htole32(save.vs[i]); 71 } 72 } 73 74 static void 75 _chacha20_round(unsigned char key[static CHACHA20_KEY_SIZE], 76 uint32_t ctr, 77 unsigned char iv[static CHACHA20_IV_SIZE], 78 void const *plaintext, size_t len, 79 void *ciphertext) 80 { 81 struct chacha20_ctx keystream; 82 _chacha20_block(&keystream, key, ctr, iv); 83 84 unsigned char const *in = plaintext, *end = in + len; 85 unsigned char *state = (void *) keystream.vs; 86 unsigned char *out = ciphertext; 87 88 while (in < end) { 89 *out++ = *in++ ^ *state++; 90 } 91 } 92 93 void 94 chacha20(unsigned char key[static CHACHA20_KEY_SIZE], 95 uint32_t ctr, 96 unsigned char iv[static CHACHA20_IV_SIZE], 97 void const *buf, size_t len, 98 void *out) 99 { 100 size_t blocks = len / CHACHA20_BLOCK_SIZE; 101 size_t tail = len - (blocks * CHACHA20_BLOCK_SIZE); 102 unsigned char const *ptr = buf; 103 unsigned char *dst = out; 104 105 size_t i; 106 for (i = 0; i < blocks; i++) { 107 _chacha20_round(key, ctr + i, iv, ptr, CHACHA20_BLOCK_SIZE, dst); 108 109 ptr += CHACHA20_BLOCK_SIZE; 110 dst += CHACHA20_BLOCK_SIZE; 111 } 112 113 _chacha20_round(key, ctr + i, iv, ptr, tail, dst); 114 }