1 | /*
|
---|
2 | * Copyright 2019-2021 The OpenSSL Project Authors. All Rights Reserved.
|
---|
3 | *
|
---|
4 | * Licensed under the Apache License 2.0 (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 | /* chacha20 cipher implementation */
|
---|
11 |
|
---|
12 | #include "cipher_chacha20.h"
|
---|
13 |
|
---|
14 | static int chacha20_initkey(PROV_CIPHER_CTX *bctx, const uint8_t *key,
|
---|
15 | size_t keylen)
|
---|
16 | {
|
---|
17 | PROV_CHACHA20_CTX *ctx = (PROV_CHACHA20_CTX *)bctx;
|
---|
18 | unsigned int i;
|
---|
19 |
|
---|
20 | if (key != NULL) {
|
---|
21 | for (i = 0; i < CHACHA_KEY_SIZE; i += 4)
|
---|
22 | ctx->key.d[i / 4] = CHACHA_U8TOU32(key + i);
|
---|
23 | }
|
---|
24 | ctx->partial_len = 0;
|
---|
25 | return 1;
|
---|
26 | }
|
---|
27 |
|
---|
28 | static int chacha20_initiv(PROV_CIPHER_CTX *bctx)
|
---|
29 | {
|
---|
30 | PROV_CHACHA20_CTX *ctx = (PROV_CHACHA20_CTX *)bctx;
|
---|
31 | unsigned int i;
|
---|
32 |
|
---|
33 | if (bctx->iv_set) {
|
---|
34 | for (i = 0; i < CHACHA_CTR_SIZE; i += 4)
|
---|
35 | ctx->counter[i / 4] = CHACHA_U8TOU32(bctx->oiv + i);
|
---|
36 | }
|
---|
37 | ctx->partial_len = 0;
|
---|
38 | return 1;
|
---|
39 | }
|
---|
40 |
|
---|
41 | static int chacha20_cipher(PROV_CIPHER_CTX *bctx, unsigned char *out,
|
---|
42 | const unsigned char *in, size_t inl)
|
---|
43 | {
|
---|
44 | PROV_CHACHA20_CTX *ctx = (PROV_CHACHA20_CTX *)bctx;
|
---|
45 | unsigned int n, rem, ctr32;
|
---|
46 |
|
---|
47 | n = ctx->partial_len;
|
---|
48 | if (n > 0) {
|
---|
49 | while (inl > 0 && n < CHACHA_BLK_SIZE) {
|
---|
50 | *out++ = *in++ ^ ctx->buf[n++];
|
---|
51 | inl--;
|
---|
52 | }
|
---|
53 | ctx->partial_len = n;
|
---|
54 |
|
---|
55 | if (inl == 0)
|
---|
56 | return 1;
|
---|
57 |
|
---|
58 | if (n == CHACHA_BLK_SIZE) {
|
---|
59 | ctx->partial_len = 0;
|
---|
60 | ctx->counter[0]++;
|
---|
61 | if (ctx->counter[0] == 0)
|
---|
62 | ctx->counter[1]++;
|
---|
63 | }
|
---|
64 | }
|
---|
65 |
|
---|
66 | rem = (unsigned int)(inl % CHACHA_BLK_SIZE);
|
---|
67 | inl -= rem;
|
---|
68 | ctr32 = ctx->counter[0];
|
---|
69 | while (inl >= CHACHA_BLK_SIZE) {
|
---|
70 | size_t blocks = inl / CHACHA_BLK_SIZE;
|
---|
71 |
|
---|
72 | /*
|
---|
73 | * 1<<28 is just a not-so-small yet not-so-large number...
|
---|
74 | * Below condition is practically never met, but it has to
|
---|
75 | * be checked for code correctness.
|
---|
76 | */
|
---|
77 | if (sizeof(size_t) > sizeof(unsigned int) && blocks > (1U << 28))
|
---|
78 | blocks = (1U << 28);
|
---|
79 |
|
---|
80 | /*
|
---|
81 | * As ChaCha20_ctr32 operates on 32-bit counter, caller
|
---|
82 | * has to handle overflow. 'if' below detects the
|
---|
83 | * overflow, which is then handled by limiting the
|
---|
84 | * amount of blocks to the exact overflow point...
|
---|
85 | */
|
---|
86 | ctr32 += (unsigned int)blocks;
|
---|
87 | if (ctr32 < blocks) {
|
---|
88 | blocks -= ctr32;
|
---|
89 | ctr32 = 0;
|
---|
90 | }
|
---|
91 | blocks *= CHACHA_BLK_SIZE;
|
---|
92 | ChaCha20_ctr32(out, in, blocks, ctx->key.d, ctx->counter);
|
---|
93 | inl -= blocks;
|
---|
94 | in += blocks;
|
---|
95 | out += blocks;
|
---|
96 |
|
---|
97 | ctx->counter[0] = ctr32;
|
---|
98 | if (ctr32 == 0) ctx->counter[1]++;
|
---|
99 | }
|
---|
100 |
|
---|
101 | if (rem > 0) {
|
---|
102 | memset(ctx->buf, 0, sizeof(ctx->buf));
|
---|
103 | ChaCha20_ctr32(ctx->buf, ctx->buf, CHACHA_BLK_SIZE,
|
---|
104 | ctx->key.d, ctx->counter);
|
---|
105 | for (n = 0; n < rem; n++)
|
---|
106 | out[n] = in[n] ^ ctx->buf[n];
|
---|
107 | ctx->partial_len = rem;
|
---|
108 | }
|
---|
109 |
|
---|
110 | return 1;
|
---|
111 | }
|
---|
112 |
|
---|
113 | static const PROV_CIPHER_HW_CHACHA20 chacha20_hw = {
|
---|
114 | { chacha20_initkey, chacha20_cipher },
|
---|
115 | chacha20_initiv
|
---|
116 | };
|
---|
117 |
|
---|
118 | const PROV_CIPHER_HW *ossl_prov_cipher_hw_chacha20(size_t keybits)
|
---|
119 | {
|
---|
120 | return (PROV_CIPHER_HW *)&chacha20_hw;
|
---|
121 | }
|
---|
122 |
|
---|