1 /* Copyright (c) 2014, Google Inc.
2  *
3  * Permission to use, copy, modify, and/or distribute this software for any
4  * purpose with or without fee is hereby granted, provided that the above
5  * copyright notice and this permission notice appear in all copies.
6  *
7  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
10  * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
12  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
13  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
14 
15 #include <openssl/aead.h>
16 
17 #include <string.h>
18 
19 #include <openssl/chacha.h>
20 #include <openssl/cipher.h>
21 #include <openssl/err.h>
22 #include <openssl/mem.h>
23 #include <openssl/poly1305.h>
24 
25 #include "internal.h"
26 
27 
28 #define POLY1305_TAG_LEN 16
29 
30 struct aead_chacha20_poly1305_ctx {
31   unsigned char key[32];
32   unsigned char tag_len;
33 };
34 
aead_chacha20_poly1305_init(EVP_AEAD_CTX * ctx,const uint8_t * key,size_t key_len,size_t tag_len)35 static int aead_chacha20_poly1305_init(EVP_AEAD_CTX *ctx, const uint8_t *key,
36                                        size_t key_len, size_t tag_len) {
37   struct aead_chacha20_poly1305_ctx *c20_ctx;
38 
39   if (tag_len == 0) {
40     tag_len = POLY1305_TAG_LEN;
41   }
42 
43   if (tag_len > POLY1305_TAG_LEN) {
44     OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_TOO_LARGE);
45     return 0;
46   }
47 
48   if (key_len != sizeof(c20_ctx->key)) {
49     return 0; /* internal error - EVP_AEAD_CTX_init should catch this. */
50   }
51 
52   c20_ctx = OPENSSL_malloc(sizeof(struct aead_chacha20_poly1305_ctx));
53   if (c20_ctx == NULL) {
54     return 0;
55   }
56 
57   memcpy(c20_ctx->key, key, key_len);
58   c20_ctx->tag_len = tag_len;
59   ctx->aead_state = c20_ctx;
60 
61   return 1;
62 }
63 
aead_chacha20_poly1305_cleanup(EVP_AEAD_CTX * ctx)64 static void aead_chacha20_poly1305_cleanup(EVP_AEAD_CTX *ctx) {
65   struct aead_chacha20_poly1305_ctx *c20_ctx = ctx->aead_state;
66   OPENSSL_cleanse(c20_ctx->key, sizeof(c20_ctx->key));
67   OPENSSL_free(c20_ctx);
68 }
69 
poly1305_update_length(poly1305_state * poly1305,size_t data_len)70 static void poly1305_update_length(poly1305_state *poly1305, size_t data_len) {
71   uint8_t length_bytes[8];
72   unsigned i;
73 
74   for (i = 0; i < sizeof(length_bytes); i++) {
75     length_bytes[i] = data_len;
76     data_len >>= 8;
77   }
78 
79   CRYPTO_poly1305_update(poly1305, length_bytes, sizeof(length_bytes));
80 }
81 
82 #if defined(__arm__)
83 #define ALIGNED __attribute__((aligned(16)))
84 #else
85 #define ALIGNED
86 #endif
87 
88 typedef void (*aead_poly1305_update)(poly1305_state *ctx, const uint8_t *ad,
89                                      size_t ad_len, const uint8_t *ciphertext,
90                                      size_t ciphertext_len);
91 
92 /* aead_poly1305 fills |tag| with the authentication tag for the given
93  * inputs, using |update| to control the order and format that the inputs are
94  * signed/authenticated. */
aead_poly1305(aead_poly1305_update update,uint8_t tag[POLY1305_TAG_LEN],const struct aead_chacha20_poly1305_ctx * c20_ctx,const uint8_t nonce[12],const uint8_t * ad,size_t ad_len,const uint8_t * ciphertext,size_t ciphertext_len)95 static void aead_poly1305(aead_poly1305_update update,
96                           uint8_t tag[POLY1305_TAG_LEN],
97                           const struct aead_chacha20_poly1305_ctx *c20_ctx,
98                           const uint8_t nonce[12], const uint8_t *ad,
99                           size_t ad_len, const uint8_t *ciphertext,
100                           size_t ciphertext_len) {
101   uint8_t poly1305_key[32] ALIGNED;
102   memset(poly1305_key, 0, sizeof(poly1305_key));
103   CRYPTO_chacha_20(poly1305_key, poly1305_key, sizeof(poly1305_key),
104                    c20_ctx->key, nonce, 0);
105   poly1305_state ctx;
106   CRYPTO_poly1305_init(&ctx, poly1305_key);
107   update(&ctx, ad, ad_len, ciphertext, ciphertext_len);
108   CRYPTO_poly1305_finish(&ctx, tag);
109 }
110 
seal_impl(aead_poly1305_update poly1305_update,const EVP_AEAD_CTX * ctx,uint8_t * out,size_t * out_len,size_t max_out_len,const uint8_t nonce[12],const uint8_t * in,size_t in_len,const uint8_t * ad,size_t ad_len)111 static int seal_impl(aead_poly1305_update poly1305_update,
112                      const EVP_AEAD_CTX *ctx, uint8_t *out, size_t *out_len,
113                      size_t max_out_len, const uint8_t nonce[12],
114                      const uint8_t *in, size_t in_len, const uint8_t *ad,
115                      size_t ad_len) {
116   const struct aead_chacha20_poly1305_ctx *c20_ctx = ctx->aead_state;
117   const uint64_t in_len_64 = in_len;
118 
119   /* |CRYPTO_chacha_20| uses a 32-bit block counter. Therefore we disallow
120    * individual operations that work on more than 256GB at a time.
121    * |in_len_64| is needed because, on 32-bit platforms, size_t is only
122    * 32-bits and this produces a warning because it's always false.
123    * Casting to uint64_t inside the conditional is not sufficient to stop
124    * the warning. */
125   if (in_len_64 >= (1ull << 32) * 64 - 64) {
126     OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_TOO_LARGE);
127     return 0;
128   }
129 
130   if (in_len + c20_ctx->tag_len < in_len) {
131     OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_TOO_LARGE);
132     return 0;
133   }
134 
135   if (max_out_len < in_len + c20_ctx->tag_len) {
136     OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BUFFER_TOO_SMALL);
137     return 0;
138   }
139 
140   CRYPTO_chacha_20(out, in, in_len, c20_ctx->key, nonce, 1);
141 
142   uint8_t tag[POLY1305_TAG_LEN] ALIGNED;
143   aead_poly1305(poly1305_update, tag, c20_ctx, nonce, ad, ad_len, out, in_len);
144 
145   memcpy(out + in_len, tag, c20_ctx->tag_len);
146   *out_len = in_len + c20_ctx->tag_len;
147   return 1;
148 }
149 
open_impl(aead_poly1305_update poly1305_update,const EVP_AEAD_CTX * ctx,uint8_t * out,size_t * out_len,size_t max_out_len,const uint8_t nonce[12],const uint8_t * in,size_t in_len,const uint8_t * ad,size_t ad_len)150 static int open_impl(aead_poly1305_update poly1305_update,
151                      const EVP_AEAD_CTX *ctx, uint8_t *out, size_t *out_len,
152                      size_t max_out_len, const uint8_t nonce[12],
153                      const uint8_t *in, size_t in_len, const uint8_t *ad,
154                      size_t ad_len) {
155   const struct aead_chacha20_poly1305_ctx *c20_ctx = ctx->aead_state;
156   size_t plaintext_len;
157   const uint64_t in_len_64 = in_len;
158 
159   if (in_len < c20_ctx->tag_len) {
160     OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BAD_DECRYPT);
161     return 0;
162   }
163 
164   /* |CRYPTO_chacha_20| uses a 32-bit block counter. Therefore we disallow
165    * individual operations that work on more than 256GB at a time.
166    * |in_len_64| is needed because, on 32-bit platforms, size_t is only
167    * 32-bits and this produces a warning because it's always false.
168    * Casting to uint64_t inside the conditional is not sufficient to stop
169    * the warning. */
170   if (in_len_64 >= (1ull << 32) * 64 - 64) {
171     OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_TOO_LARGE);
172     return 0;
173   }
174 
175   plaintext_len = in_len - c20_ctx->tag_len;
176   uint8_t tag[POLY1305_TAG_LEN] ALIGNED;
177   aead_poly1305(poly1305_update, tag, c20_ctx, nonce, ad, ad_len, in,
178                 plaintext_len);
179   if (CRYPTO_memcmp(tag, in + plaintext_len, c20_ctx->tag_len) != 0) {
180     OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BAD_DECRYPT);
181     return 0;
182   }
183 
184   CRYPTO_chacha_20(out, in, plaintext_len, c20_ctx->key, nonce, 1);
185   *out_len = plaintext_len;
186   return 1;
187 }
188 
poly1305_update_padded_16(poly1305_state * poly1305,const uint8_t * data,size_t data_len)189 static void poly1305_update_padded_16(poly1305_state *poly1305,
190                                       const uint8_t *data, size_t data_len) {
191   static const uint8_t padding[16] = { 0 }; /* Padding is all zeros. */
192 
193   CRYPTO_poly1305_update(poly1305, data, data_len);
194   if (data_len % 16 != 0) {
195     CRYPTO_poly1305_update(poly1305, padding, sizeof(padding) - (data_len % 16));
196   }
197 }
198 
poly1305_update(poly1305_state * ctx,const uint8_t * ad,size_t ad_len,const uint8_t * ciphertext,size_t ciphertext_len)199 static void poly1305_update(poly1305_state *ctx, const uint8_t *ad,
200                             size_t ad_len, const uint8_t *ciphertext,
201                             size_t ciphertext_len) {
202   poly1305_update_padded_16(ctx, ad, ad_len);
203   poly1305_update_padded_16(ctx, ciphertext, ciphertext_len);
204   poly1305_update_length(ctx, ad_len);
205   poly1305_update_length(ctx, ciphertext_len);
206 }
207 
aead_chacha20_poly1305_seal(const EVP_AEAD_CTX * ctx,uint8_t * out,size_t * out_len,size_t max_out_len,const uint8_t * nonce,size_t nonce_len,const uint8_t * in,size_t in_len,const uint8_t * ad,size_t ad_len)208 static int aead_chacha20_poly1305_seal(const EVP_AEAD_CTX *ctx, uint8_t *out,
209                                        size_t *out_len, size_t max_out_len,
210                                        const uint8_t *nonce, size_t nonce_len,
211                                        const uint8_t *in, size_t in_len,
212                                        const uint8_t *ad, size_t ad_len) {
213   if (nonce_len != 12) {
214     OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_UNSUPPORTED_NONCE_SIZE);
215     return 0;
216   }
217   return seal_impl(poly1305_update, ctx, out, out_len, max_out_len, nonce, in,
218                    in_len, ad, ad_len);
219 }
220 
aead_chacha20_poly1305_open(const EVP_AEAD_CTX * ctx,uint8_t * out,size_t * out_len,size_t max_out_len,const uint8_t * nonce,size_t nonce_len,const uint8_t * in,size_t in_len,const uint8_t * ad,size_t ad_len)221 static int aead_chacha20_poly1305_open(const EVP_AEAD_CTX *ctx, uint8_t *out,
222                                        size_t *out_len, size_t max_out_len,
223                                        const uint8_t *nonce, size_t nonce_len,
224                                        const uint8_t *in, size_t in_len,
225                                        const uint8_t *ad, size_t ad_len) {
226   if (nonce_len != 12) {
227     OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_UNSUPPORTED_NONCE_SIZE);
228     return 0;
229   }
230   return open_impl(poly1305_update, ctx, out, out_len, max_out_len, nonce, in,
231                    in_len, ad, ad_len);
232 }
233 
234 static const EVP_AEAD aead_chacha20_poly1305 = {
235     32,                 /* key len */
236     12,                 /* nonce len */
237     POLY1305_TAG_LEN,   /* overhead */
238     POLY1305_TAG_LEN,   /* max tag length */
239     aead_chacha20_poly1305_init,
240     NULL, /* init_with_direction */
241     aead_chacha20_poly1305_cleanup,
242     aead_chacha20_poly1305_seal,
243     aead_chacha20_poly1305_open,
244     NULL,               /* get_rc4_state */
245     NULL,               /* get_iv */
246 };
247 
EVP_aead_chacha20_poly1305(void)248 const EVP_AEAD *EVP_aead_chacha20_poly1305(void) {
249   return &aead_chacha20_poly1305;
250 }
251 
EVP_aead_chacha20_poly1305_rfc7539(void)252 const EVP_AEAD *EVP_aead_chacha20_poly1305_rfc7539(void) {
253   return EVP_aead_chacha20_poly1305();
254 }
255 
poly1305_update_old(poly1305_state * ctx,const uint8_t * ad,size_t ad_len,const uint8_t * ciphertext,size_t ciphertext_len)256 static void poly1305_update_old(poly1305_state *ctx, const uint8_t *ad,
257                                 size_t ad_len, const uint8_t *ciphertext,
258                                 size_t ciphertext_len) {
259   CRYPTO_poly1305_update(ctx, ad, ad_len);
260   poly1305_update_length(ctx, ad_len);
261   CRYPTO_poly1305_update(ctx, ciphertext, ciphertext_len);
262   poly1305_update_length(ctx, ciphertext_len);
263 }
264 
aead_chacha20_poly1305_old_seal(const EVP_AEAD_CTX * ctx,uint8_t * out,size_t * out_len,size_t max_out_len,const uint8_t * nonce,size_t nonce_len,const uint8_t * in,size_t in_len,const uint8_t * ad,size_t ad_len)265 static int aead_chacha20_poly1305_old_seal(
266     const EVP_AEAD_CTX *ctx, uint8_t *out, size_t *out_len, size_t max_out_len,
267     const uint8_t *nonce, size_t nonce_len, const uint8_t *in, size_t in_len,
268     const uint8_t *ad, size_t ad_len) {
269   if (nonce_len != 8) {
270     OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_UNSUPPORTED_NONCE_SIZE);
271     return 0;
272   }
273   uint8_t nonce_96[12];
274   memset(nonce_96, 0, 4);
275   memcpy(nonce_96 + 4, nonce, 8);
276   return seal_impl(poly1305_update_old, ctx, out, out_len, max_out_len,
277                    nonce_96, in, in_len, ad, ad_len);
278 }
279 
aead_chacha20_poly1305_old_open(const EVP_AEAD_CTX * ctx,uint8_t * out,size_t * out_len,size_t max_out_len,const uint8_t * nonce,size_t nonce_len,const uint8_t * in,size_t in_len,const uint8_t * ad,size_t ad_len)280 static int aead_chacha20_poly1305_old_open(
281     const EVP_AEAD_CTX *ctx, uint8_t *out, size_t *out_len, size_t max_out_len,
282     const uint8_t *nonce, size_t nonce_len, const uint8_t *in, size_t in_len,
283     const uint8_t *ad, size_t ad_len) {
284   if (nonce_len != 8) {
285     OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_UNSUPPORTED_NONCE_SIZE);
286     return 0;
287   }
288   uint8_t nonce_96[12];
289   memset(nonce_96, 0, 4);
290   memcpy(nonce_96 + 4, nonce, 8);
291   return open_impl(poly1305_update_old, ctx, out, out_len, max_out_len,
292                    nonce_96, in, in_len, ad, ad_len);
293 }
294 
295 static const EVP_AEAD aead_chacha20_poly1305_old = {
296     32,                 /* key len */
297     8,                  /* nonce len */
298     POLY1305_TAG_LEN,   /* overhead */
299     POLY1305_TAG_LEN,   /* max tag length */
300     aead_chacha20_poly1305_init,
301     NULL, /* init_with_direction */
302     aead_chacha20_poly1305_cleanup,
303     aead_chacha20_poly1305_old_seal,
304     aead_chacha20_poly1305_old_open,
305     NULL,               /* get_rc4_state */
306     NULL,               /* get_iv */
307 };
308 
EVP_aead_chacha20_poly1305_old(void)309 const EVP_AEAD *EVP_aead_chacha20_poly1305_old(void) {
310   return &aead_chacha20_poly1305_old;
311 }
312