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 #define CHACHA20_NONCE_LEN 8
30 
31 struct aead_chacha20_poly1305_ctx {
32   unsigned char key[32];
33   unsigned char tag_len;
34 };
35 
aead_chacha20_poly1305_init(EVP_AEAD_CTX * ctx,const uint8_t * key,size_t key_len,size_t tag_len)36 static int aead_chacha20_poly1305_init(EVP_AEAD_CTX *ctx, const uint8_t *key,
37                                        size_t key_len, size_t tag_len) {
38   struct aead_chacha20_poly1305_ctx *c20_ctx;
39 
40   if (tag_len == 0) {
41     tag_len = POLY1305_TAG_LEN;
42   }
43 
44   if (tag_len > POLY1305_TAG_LEN) {
45     OPENSSL_PUT_ERROR(CIPHER, aead_chacha20_poly1305_init, CIPHER_R_TOO_LARGE);
46     return 0;
47   }
48 
49   if (key_len != sizeof(c20_ctx->key)) {
50     return 0; /* internal error - EVP_AEAD_CTX_init should catch this. */
51   }
52 
53   c20_ctx = OPENSSL_malloc(sizeof(struct aead_chacha20_poly1305_ctx));
54   if (c20_ctx == NULL) {
55     return 0;
56   }
57 
58   memcpy(c20_ctx->key, key, key_len);
59   c20_ctx->tag_len = tag_len;
60   ctx->aead_state = c20_ctx;
61 
62   return 1;
63 }
64 
aead_chacha20_poly1305_cleanup(EVP_AEAD_CTX * ctx)65 static void aead_chacha20_poly1305_cleanup(EVP_AEAD_CTX *ctx) {
66   struct aead_chacha20_poly1305_ctx *c20_ctx = ctx->aead_state;
67   OPENSSL_cleanse(c20_ctx->key, sizeof(c20_ctx->key));
68   OPENSSL_free(c20_ctx);
69 }
70 
poly1305_update_with_length(poly1305_state * poly1305,const uint8_t * data,size_t data_len)71 static void poly1305_update_with_length(poly1305_state *poly1305,
72                                         const uint8_t *data, size_t data_len) {
73   size_t j = data_len;
74   uint8_t length_bytes[8];
75   unsigned i;
76 
77   for (i = 0; i < sizeof(length_bytes); i++) {
78     length_bytes[i] = j;
79     j >>= 8;
80   }
81 
82   CRYPTO_poly1305_update(poly1305, data, data_len);
83   CRYPTO_poly1305_update(poly1305, length_bytes, sizeof(length_bytes));
84 }
85 
86 #if defined(__arm__)
87 #define ALIGNED __attribute__((aligned(16)))
88 #else
89 #define ALIGNED
90 #endif
91 
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)92 static int aead_chacha20_poly1305_seal(const EVP_AEAD_CTX *ctx, uint8_t *out,
93                                        size_t *out_len, size_t max_out_len,
94                                        const uint8_t *nonce, size_t nonce_len,
95                                        const uint8_t *in, size_t in_len,
96                                        const uint8_t *ad, size_t ad_len) {
97   const struct aead_chacha20_poly1305_ctx *c20_ctx = ctx->aead_state;
98   uint8_t poly1305_key[32] ALIGNED;
99   poly1305_state poly1305;
100   const uint64_t in_len_64 = in_len;
101 
102   /* The underlying ChaCha implementation may not overflow the block
103    * counter into the second counter word. Therefore we disallow
104    * individual operations that work on more than 256GB at a time.
105    * |in_len_64| is needed because, on 32-bit platforms, size_t is only
106    * 32-bits and this produces a warning because it's always false.
107    * Casting to uint64_t inside the conditional is not sufficient to stop
108    * the warning. */
109   if (in_len_64 >= (1ull << 32) * 64 - 64) {
110     OPENSSL_PUT_ERROR(CIPHER, aead_chacha20_poly1305_seal, CIPHER_R_TOO_LARGE);
111     return 0;
112   }
113 
114   if (in_len + c20_ctx->tag_len < in_len) {
115     OPENSSL_PUT_ERROR(CIPHER, aead_chacha20_poly1305_seal, CIPHER_R_TOO_LARGE);
116     return 0;
117   }
118 
119   if (max_out_len < in_len + c20_ctx->tag_len) {
120     OPENSSL_PUT_ERROR(CIPHER, aead_chacha20_poly1305_seal,
121                       CIPHER_R_BUFFER_TOO_SMALL);
122     return 0;
123   }
124 
125   if (nonce_len != CHACHA20_NONCE_LEN) {
126     OPENSSL_PUT_ERROR(CIPHER, aead_chacha20_poly1305_seal, CIPHER_R_IV_TOO_LARGE);
127     return 0;
128   }
129 
130   memset(poly1305_key, 0, sizeof(poly1305_key));
131   CRYPTO_chacha_20(poly1305_key, poly1305_key, sizeof(poly1305_key),
132                    c20_ctx->key, nonce, 0);
133 
134   CRYPTO_poly1305_init(&poly1305, poly1305_key);
135   poly1305_update_with_length(&poly1305, ad, ad_len);
136   CRYPTO_chacha_20(out, in, in_len, c20_ctx->key, nonce, 1);
137   poly1305_update_with_length(&poly1305, out, in_len);
138 
139   uint8_t tag[POLY1305_TAG_LEN] ALIGNED;
140   CRYPTO_poly1305_finish(&poly1305, tag);
141   memcpy(out + in_len, tag, c20_ctx->tag_len);
142   *out_len = in_len + c20_ctx->tag_len;
143   return 1;
144 }
145 
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)146 static int aead_chacha20_poly1305_open(const EVP_AEAD_CTX *ctx, uint8_t *out,
147                                        size_t *out_len, size_t max_out_len,
148                                        const uint8_t *nonce, size_t nonce_len,
149                                        const uint8_t *in, size_t in_len,
150                                        const uint8_t *ad, size_t ad_len) {
151   const struct aead_chacha20_poly1305_ctx *c20_ctx = ctx->aead_state;
152   uint8_t mac[POLY1305_TAG_LEN];
153   uint8_t poly1305_key[32] ALIGNED;
154   size_t plaintext_len;
155   poly1305_state poly1305;
156   const uint64_t in_len_64 = in_len;
157 
158   if (in_len < c20_ctx->tag_len) {
159     OPENSSL_PUT_ERROR(CIPHER, aead_chacha20_poly1305_open, CIPHER_R_BAD_DECRYPT);
160     return 0;
161   }
162 
163   /* The underlying ChaCha implementation may not overflow the block
164    * counter into the second counter word. 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, aead_chacha20_poly1305_open, CIPHER_R_TOO_LARGE);
172     return 0;
173   }
174 
175   if (nonce_len != CHACHA20_NONCE_LEN) {
176     OPENSSL_PUT_ERROR(CIPHER, aead_chacha20_poly1305_open, CIPHER_R_IV_TOO_LARGE);
177     return 0;
178   }
179 
180   plaintext_len = in_len - c20_ctx->tag_len;
181 
182   if (max_out_len < plaintext_len) {
183     OPENSSL_PUT_ERROR(CIPHER, aead_chacha20_poly1305_open,
184                       CIPHER_R_BUFFER_TOO_SMALL);
185     return 0;
186   }
187 
188   memset(poly1305_key, 0, sizeof(poly1305_key));
189   CRYPTO_chacha_20(poly1305_key, poly1305_key, sizeof(poly1305_key),
190                    c20_ctx->key, nonce, 0);
191 
192   CRYPTO_poly1305_init(&poly1305, poly1305_key);
193   poly1305_update_with_length(&poly1305, ad, ad_len);
194   poly1305_update_with_length(&poly1305, in, plaintext_len);
195   CRYPTO_poly1305_finish(&poly1305, mac);
196 
197   if (CRYPTO_memcmp(mac, in + plaintext_len, c20_ctx->tag_len) != 0) {
198     OPENSSL_PUT_ERROR(CIPHER, aead_chacha20_poly1305_open, CIPHER_R_BAD_DECRYPT);
199     return 0;
200   }
201 
202   CRYPTO_chacha_20(out, in, plaintext_len, c20_ctx->key, nonce, 1);
203   *out_len = plaintext_len;
204   return 1;
205 }
206 
207 static const EVP_AEAD aead_chacha20_poly1305 = {
208     32,                 /* key len */
209     CHACHA20_NONCE_LEN, /* nonce len */
210     POLY1305_TAG_LEN,   /* overhead */
211     POLY1305_TAG_LEN,   /* max tag length */
212     aead_chacha20_poly1305_init,
213     NULL, /* init_with_direction */
214     aead_chacha20_poly1305_cleanup,
215     aead_chacha20_poly1305_seal,
216     aead_chacha20_poly1305_open,
217     NULL,               /* get_rc4_state */
218 };
219 
EVP_aead_chacha20_poly1305(void)220 const EVP_AEAD *EVP_aead_chacha20_poly1305(void) {
221   return &aead_chacha20_poly1305;
222 }
223