AEAD Tests.
[oweals/openssl.git] / crypto / evp / e_chacha20poly1305.c
1 /* ====================================================================
2  * Copyright (c) 2013 The OpenSSL Project.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer. 
10  *
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in
13  *    the documentation and/or other materials provided with the
14  *    distribution.
15  *
16  * 3. All advertising materials mentioning features or use of this
17  *    software must display the following acknowledgment:
18  *    "This product includes software developed by the OpenSSL Project
19  *    for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
20  *
21  * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
22  *    endorse or promote products derived from this software without
23  *    prior written permission. For written permission, please contact
24  *    openssl-core@openssl.org.
25  *
26  * 5. Products derived from this software may not be called "OpenSSL"
27  *    nor may "OpenSSL" appear in their names without prior written
28  *    permission of the OpenSSL Project.
29  *
30  * 6. Redistributions of any form whatsoever must retain the following
31  *    acknowledgment:
32  *    "This product includes software developed by the OpenSSL Project
33  *    for use in the OpenSSL Toolkit (http://www.openssl.org/)"
34  *
35  * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
36  * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
37  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
38  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
39  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
41  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
42  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
43  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
44  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
45  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
46  * OF THE POSSIBILITY OF SUCH DAMAGE.
47  * ====================================================================
48  *
49  */
50
51 #include <stdint.h>
52 #include <string.h>
53 #include <openssl/opensslconf.h>
54
55 #if !defined(OPENSSL_NO_CHACHA) && !defined(OPENSSL_NO_POLY1305)
56
57 #include <openssl/chacha.h>
58 #include <openssl/poly1305.h>
59 #include <openssl/evp.h>
60 #include <openssl/err.h>
61 #include "evp_locl.h"
62
63 #define POLY1305_TAG_LEN 16
64 #define CHACHA20_NONCE_LEN 8
65
66 struct aead_chacha20_poly1305_ctx
67         {
68         unsigned char key[32];
69         unsigned char tag_len;
70         };
71
72 static int aead_chacha20_poly1305_init(EVP_AEAD_CTX *ctx, const unsigned char *key, size_t key_len, size_t tag_len)
73         {
74         struct aead_chacha20_poly1305_ctx *c20_ctx;
75
76         if (tag_len == 0)
77                 tag_len = POLY1305_TAG_LEN;
78
79         if (tag_len > POLY1305_TAG_LEN)
80                 {
81                 EVPerr(EVP_F_AEAD_CHACHA20_POLY1305_INIT, EVP_R_TOO_LARGE);
82                 return 0;
83                 }
84
85         if (key_len != sizeof(c20_ctx->key))
86                 return 0;  /* internal error - EVP_AEAD_CTX_init should catch this. */
87
88         c20_ctx = OPENSSL_malloc(sizeof(struct aead_chacha20_poly1305_ctx));
89         if (c20_ctx == NULL)
90                 return 0;
91
92         memcpy(&c20_ctx->key[0], key, key_len);
93         c20_ctx->tag_len = tag_len;
94         ctx->aead_state = c20_ctx;
95
96         return 1;
97         }
98
99 static void aead_chacha20_poly1305_cleanup(EVP_AEAD_CTX *ctx)
100         {
101         struct aead_chacha20_poly1305_ctx *c20_ctx = ctx->aead_state;
102         OPENSSL_cleanse(c20_ctx->key, sizeof(c20_ctx->key));
103         OPENSSL_free(c20_ctx);
104         }
105
106 static void poly1305_update_with_length(poly1305_state *poly1305,
107         const unsigned char *data, size_t data_len)
108         {
109         size_t j = data_len;
110         unsigned char length_bytes[8];
111         unsigned i;
112
113         for (i = 0; i < sizeof(length_bytes); i++)
114                 {
115                 length_bytes[i] = j;
116                 j >>= 8;
117                 }
118
119         CRYPTO_poly1305_update(poly1305, length_bytes, sizeof(length_bytes));
120         CRYPTO_poly1305_update(poly1305, data, data_len);
121 }
122
123 static ssize_t aead_chacha20_poly1305_seal(const EVP_AEAD_CTX *ctx,
124         unsigned char *out, size_t max_out_len,
125         const unsigned char *nonce, size_t nonce_len,
126         const unsigned char *in, size_t in_len,
127         const unsigned char *ad, size_t ad_len)
128         {
129         const struct aead_chacha20_poly1305_ctx *c20_ctx = ctx->aead_state;
130         unsigned char poly1305_key[32];
131         poly1305_state poly1305;
132         const uint64_t in_len_64 = in_len;
133
134         /* The underlying ChaCha implementation may not overflow the block
135          * counter into the second counter word. Therefore we disallow
136          * individual operations that work on more than 2TB at a time.
137          * |in_len_64| is needed because, on 32-bit platforms, size_t is only
138          * 32-bits and this produces a warning because it's always false.
139          * Casting to uint64_t inside the conditional is not sufficient to stop
140          * the warning. */
141         if (in_len_64 >= (1ull << 32)*64-64)
142                 {
143                 EVPerr(EVP_F_AEAD_CHACHA20_POLY1305_SEAL, EVP_R_TOO_LARGE);
144                 return -1;
145                 }
146
147         if (max_out_len < in_len + c20_ctx->tag_len)
148                 {
149                 EVPerr(EVP_F_AEAD_CHACHA20_POLY1305_SEAL, EVP_R_BUFFER_TOO_SMALL);
150                 return -1;
151                 }
152
153         if (nonce_len != CHACHA20_NONCE_LEN)
154                 {
155                 EVPerr(EVP_F_AEAD_CHACHA20_POLY1305_SEAL, EVP_R_IV_TOO_LARGE);
156                 return -1;
157                 }
158
159         memset(poly1305_key, 0, sizeof(poly1305_key));
160         CRYPTO_chacha_20(poly1305_key, poly1305_key, sizeof(poly1305_key), c20_ctx->key, nonce, 0);
161
162         CRYPTO_poly1305_init(&poly1305, poly1305_key);
163         poly1305_update_with_length(&poly1305, ad, ad_len);
164         CRYPTO_chacha_20(out, in, in_len, c20_ctx->key, nonce, 1);
165         poly1305_update_with_length(&poly1305, out, in_len);
166
167         if (c20_ctx->tag_len != POLY1305_TAG_LEN)
168                 {
169                 unsigned char tag[POLY1305_TAG_LEN];
170                 CRYPTO_poly1305_finish(&poly1305, tag);
171                 memcpy(out + in_len, tag, c20_ctx->tag_len);
172                 return in_len + c20_ctx->tag_len;
173                 }
174
175         CRYPTO_poly1305_finish(&poly1305, out + in_len);
176         return in_len + POLY1305_TAG_LEN;
177         }
178
179 static ssize_t aead_chacha20_poly1305_open(const EVP_AEAD_CTX *ctx,
180         unsigned char *out, size_t max_out_len,
181         const unsigned char *nonce, size_t nonce_len,
182         const unsigned char *in, size_t in_len,
183         const unsigned char *ad, size_t ad_len)
184         {
185         const struct aead_chacha20_poly1305_ctx *c20_ctx = ctx->aead_state;
186         unsigned char mac[POLY1305_TAG_LEN];
187         unsigned char poly1305_key[32];
188         size_t out_len;
189         poly1305_state poly1305;
190         const uint64_t in_len_64 = in_len;
191
192         if (in_len < c20_ctx->tag_len)
193                 {
194                 EVPerr(EVP_F_AEAD_CHACHA20_POLY1305_OPEN, EVP_R_BAD_DECRYPT);
195                 return -1;
196                 }
197
198         /* The underlying ChaCha implementation may not overflow the block
199          * counter into the second counter word. Therefore we disallow
200          * individual operations that work on more than 2TB at a time.
201          * |in_len_64| is needed because, on 32-bit platforms, size_t is only
202          * 32-bits and this produces a warning because it's always false.
203          * Casting to uint64_t inside the conditional is not sufficient to stop
204          * the warning. */
205         if (in_len_64 >= (1ull << 32)*64-64)
206                 {
207                 EVPerr(EVP_F_AEAD_CHACHA20_POLY1305_OPEN, EVP_R_TOO_LARGE);
208                 return -1;
209                 }
210
211         if (nonce_len != CHACHA20_NONCE_LEN)
212                 {
213                 EVPerr(EVP_F_AEAD_CHACHA20_POLY1305_OPEN, EVP_R_IV_TOO_LARGE);
214                 return -1;
215                 }
216
217         out_len = in_len - c20_ctx->tag_len;
218
219         if (max_out_len < out_len)
220                 {
221                 EVPerr(EVP_F_AEAD_CHACHA20_POLY1305_OPEN, EVP_R_BUFFER_TOO_SMALL);
222                 return -1;
223                 }
224
225         memset(poly1305_key, 0, sizeof(poly1305_key));
226         CRYPTO_chacha_20(poly1305_key, poly1305_key, sizeof(poly1305_key), c20_ctx->key, nonce, 0);
227
228         CRYPTO_poly1305_init(&poly1305, poly1305_key);
229         poly1305_update_with_length(&poly1305, ad, ad_len);
230         poly1305_update_with_length(&poly1305, in, out_len);
231         CRYPTO_poly1305_finish(&poly1305, mac);
232
233         if (CRYPTO_memcmp(mac, in + out_len, c20_ctx->tag_len) != 0)
234                 {
235                 EVPerr(EVP_F_AEAD_CHACHA20_POLY1305_OPEN, EVP_R_BAD_DECRYPT);
236                 return -1;
237                 }
238
239         CRYPTO_chacha_20(out, in, out_len, c20_ctx->key, nonce, 1);
240         return out_len;
241         }
242
243 static const EVP_AEAD aead_chacha20_poly1305 =
244         {
245         32,  /* key len */
246         CHACHA20_NONCE_LEN,   /* nonce len */
247         POLY1305_TAG_LEN,  /* overhead */
248         POLY1305_TAG_LEN,  /* max tag length */
249
250         aead_chacha20_poly1305_init,
251         aead_chacha20_poly1305_cleanup,
252         aead_chacha20_poly1305_seal,
253         aead_chacha20_poly1305_open,
254         };
255
256 const EVP_AEAD *EVP_aead_chacha20_poly1305()
257         {
258         return &aead_chacha20_poly1305;
259         }
260
261 #endif  /* !OPENSSL_NO_CHACHA && !OPENSSL_NO_POLY1305 */