• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/cpu.h>
22 #include <openssl/err.h>
23 #include <openssl/mem.h>
24 #include <openssl/poly1305.h>
25 
26 #include "internal.h"
27 #include "../internal.h"
28 
29 
30 #define POLY1305_TAG_LEN 16
31 
32 struct aead_chacha20_poly1305_ctx {
33   unsigned char key[32];
34   unsigned char tag_len;
35 };
36 
37 #if defined(OPENSSL_X86_64) && !defined(OPENSSL_NO_ASM) && \
38     !defined(OPENSSL_WINDOWS)
asm_capable(void)39 static int asm_capable(void) {
40   const int sse41_capable = (OPENSSL_ia32cap_P[1] & (1 << 19)) != 0;
41   return sse41_capable;
42 }
43 
44 // chacha20_poly1305_open is defined in chacha20_poly1305_x86_64.pl. It
45 // decrypts |plaintext_len| bytes from |ciphertext| and writes them to
46 // |out_plaintext|. On entry, |aead_data| must contain the final 48 bytes of
47 // the initial ChaCha20 block, i.e. the key, followed by four zeros, followed
48 // by the nonce. On exit, it will contain the calculated tag value, which the
49 // caller must check.
50 extern void chacha20_poly1305_open(uint8_t *out_plaintext,
51                                    const uint8_t *ciphertext,
52                                    size_t plaintext_len, const uint8_t *ad,
53                                    size_t ad_len, uint8_t *aead_data);
54 
55 // chacha20_poly1305_open is defined in chacha20_poly1305_x86_64.pl. It
56 // encrypts |plaintext_len| bytes from |plaintext| and writes them to
57 // |out_ciphertext|. On entry, |aead_data| must contain the final 48 bytes of
58 // the initial ChaCha20 block, i.e. the key, followed by four zeros, followed
59 // by the nonce. On exit, it will contain the calculated tag value, which the
60 // caller must append to the ciphertext.
61 extern void chacha20_poly1305_seal(uint8_t *out_ciphertext,
62                                    const uint8_t *plaintext,
63                                    size_t plaintext_len, const uint8_t *ad,
64                                    size_t ad_len, uint8_t *aead_data);
65 #else
asm_capable(void)66 static int asm_capable(void) {
67   return 0;
68 }
69 
70 
chacha20_poly1305_open(uint8_t * out_plaintext,const uint8_t * ciphertext,size_t plaintext_len,const uint8_t * ad,size_t ad_len,uint8_t * aead_data)71 static void chacha20_poly1305_open(uint8_t *out_plaintext,
72                                    const uint8_t *ciphertext,
73                                    size_t plaintext_len, const uint8_t *ad,
74                                    size_t ad_len, uint8_t *aead_data) {}
75 
chacha20_poly1305_seal(uint8_t * out_ciphertext,const uint8_t * plaintext,size_t plaintext_len,const uint8_t * ad,size_t ad_len,uint8_t * aead_data)76 static void chacha20_poly1305_seal(uint8_t *out_ciphertext,
77                                    const uint8_t *plaintext,
78                                    size_t plaintext_len, const uint8_t *ad,
79                                    size_t ad_len, uint8_t *aead_data) {}
80 #endif
81 
aead_chacha20_poly1305_init(EVP_AEAD_CTX * ctx,const uint8_t * key,size_t key_len,size_t tag_len)82 static int aead_chacha20_poly1305_init(EVP_AEAD_CTX *ctx, const uint8_t *key,
83                                        size_t key_len, size_t tag_len) {
84   struct aead_chacha20_poly1305_ctx *c20_ctx;
85 
86   if (tag_len == 0) {
87     tag_len = POLY1305_TAG_LEN;
88   }
89 
90   if (tag_len > POLY1305_TAG_LEN) {
91     OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_TOO_LARGE);
92     return 0;
93   }
94 
95   if (key_len != sizeof(c20_ctx->key)) {
96     return 0; /* internal error - EVP_AEAD_CTX_init should catch this. */
97   }
98 
99   c20_ctx = OPENSSL_malloc(sizeof(struct aead_chacha20_poly1305_ctx));
100   if (c20_ctx == NULL) {
101     return 0;
102   }
103 
104   OPENSSL_memcpy(c20_ctx->key, key, key_len);
105   c20_ctx->tag_len = tag_len;
106   ctx->aead_state = c20_ctx;
107 
108   return 1;
109 }
110 
aead_chacha20_poly1305_cleanup(EVP_AEAD_CTX * ctx)111 static void aead_chacha20_poly1305_cleanup(EVP_AEAD_CTX *ctx) {
112   struct aead_chacha20_poly1305_ctx *c20_ctx = ctx->aead_state;
113   OPENSSL_cleanse(c20_ctx->key, sizeof(c20_ctx->key));
114   OPENSSL_free(c20_ctx);
115 }
116 
poly1305_update_length(poly1305_state * poly1305,size_t data_len)117 static void poly1305_update_length(poly1305_state *poly1305, size_t data_len) {
118   uint8_t length_bytes[8];
119 
120   for (unsigned i = 0; i < sizeof(length_bytes); i++) {
121     length_bytes[i] = data_len;
122     data_len >>= 8;
123   }
124 
125   CRYPTO_poly1305_update(poly1305, length_bytes, sizeof(length_bytes));
126 }
127 
poly1305_update_padded_16(poly1305_state * poly1305,const uint8_t * data,size_t data_len)128 static void poly1305_update_padded_16(poly1305_state *poly1305,
129                                       const uint8_t *data, size_t data_len) {
130   static const uint8_t padding[16] = { 0 }; /* Padding is all zeros. */
131 
132   CRYPTO_poly1305_update(poly1305, data, data_len);
133   if (data_len % 16 != 0) {
134     CRYPTO_poly1305_update(poly1305, padding,
135                            sizeof(padding) - (data_len % 16));
136   }
137 }
138 
139 /* calc_tag fills |tag| with the authentication tag for the given inputs. */
calc_tag(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)140 static void calc_tag(uint8_t tag[POLY1305_TAG_LEN],
141                      const struct aead_chacha20_poly1305_ctx *c20_ctx,
142                      const uint8_t nonce[12], const uint8_t *ad, size_t ad_len,
143                      const uint8_t *ciphertext, size_t ciphertext_len) {
144   alignas(16) uint8_t poly1305_key[32];
145   OPENSSL_memset(poly1305_key, 0, sizeof(poly1305_key));
146   CRYPTO_chacha_20(poly1305_key, poly1305_key, sizeof(poly1305_key),
147                    c20_ctx->key, nonce, 0);
148 
149   poly1305_state ctx;
150   CRYPTO_poly1305_init(&ctx, poly1305_key);
151   poly1305_update_padded_16(&ctx, ad, ad_len);
152   poly1305_update_padded_16(&ctx, ciphertext, ciphertext_len);
153   poly1305_update_length(&ctx, ad_len);
154   poly1305_update_length(&ctx, ciphertext_len);
155   CRYPTO_poly1305_finish(&ctx, tag);
156 }
157 
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)158 static int aead_chacha20_poly1305_seal(const EVP_AEAD_CTX *ctx, uint8_t *out,
159                                        size_t *out_len, size_t max_out_len,
160                                        const uint8_t *nonce, size_t nonce_len,
161                                        const uint8_t *in, size_t in_len,
162                                        const uint8_t *ad, size_t ad_len) {
163   const struct aead_chacha20_poly1305_ctx *c20_ctx = ctx->aead_state;
164   const uint64_t in_len_64 = in_len;
165 
166   if (nonce_len != 12) {
167     OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_UNSUPPORTED_NONCE_SIZE);
168     return 0;
169   }
170 
171   /* |CRYPTO_chacha_20| uses a 32-bit block counter. Therefore we disallow
172    * individual operations that work on more than 256GB at a time.
173    * |in_len_64| is needed because, on 32-bit platforms, size_t is only
174    * 32-bits and this produces a warning because it's always false.
175    * Casting to uint64_t inside the conditional is not sufficient to stop
176    * the warning. */
177   if (in_len_64 >= (UINT64_C(1) << 32) * 64 - 64) {
178     OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_TOO_LARGE);
179     return 0;
180   }
181 
182   if (in_len + c20_ctx->tag_len < in_len) {
183     OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_TOO_LARGE);
184     return 0;
185   }
186 
187   if (max_out_len < in_len + c20_ctx->tag_len) {
188     OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BUFFER_TOO_SMALL);
189     return 0;
190   }
191 
192   alignas(16) uint8_t tag[48];
193 
194   if (asm_capable()) {
195     OPENSSL_memcpy(tag, c20_ctx->key, 32);
196     OPENSSL_memset(tag + 32, 0, 4);
197     OPENSSL_memcpy(tag + 32 + 4, nonce, 12);
198     chacha20_poly1305_seal(out, in, in_len, ad, ad_len, tag);
199   } else {
200     CRYPTO_chacha_20(out, in, in_len, c20_ctx->key, nonce, 1);
201     calc_tag(tag, c20_ctx, nonce, ad, ad_len, out, in_len);
202   }
203 
204   OPENSSL_memcpy(out + in_len, tag, c20_ctx->tag_len);
205   *out_len = in_len + c20_ctx->tag_len;
206   return 1;
207 }
208 
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)209 static int aead_chacha20_poly1305_open(const EVP_AEAD_CTX *ctx, uint8_t *out,
210                                        size_t *out_len, size_t max_out_len,
211                                        const uint8_t *nonce, size_t nonce_len,
212                                        const uint8_t *in, size_t in_len,
213                                        const uint8_t *ad, size_t ad_len) {
214   const struct aead_chacha20_poly1305_ctx *c20_ctx = ctx->aead_state;
215   size_t plaintext_len;
216   const uint64_t in_len_64 = in_len;
217 
218   if (nonce_len != 12) {
219     OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_UNSUPPORTED_NONCE_SIZE);
220     return 0;
221   }
222 
223   if (in_len < c20_ctx->tag_len) {
224     OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BAD_DECRYPT);
225     return 0;
226   }
227 
228   /* |CRYPTO_chacha_20| uses a 32-bit block counter. Therefore we disallow
229    * individual operations that work on more than 256GB at a time.
230    * |in_len_64| is needed because, on 32-bit platforms, size_t is only
231    * 32-bits and this produces a warning because it's always false.
232    * Casting to uint64_t inside the conditional is not sufficient to stop
233    * the warning. */
234   if (in_len_64 >= (UINT64_C(1) << 32) * 64 - 64) {
235     OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_TOO_LARGE);
236     return 0;
237   }
238 
239   plaintext_len = in_len - c20_ctx->tag_len;
240   alignas(16) uint8_t tag[48];
241 
242   if (asm_capable()) {
243     OPENSSL_memcpy(tag, c20_ctx->key, 32);
244     OPENSSL_memset(tag + 32, 0, 4);
245     OPENSSL_memcpy(tag + 32 + 4, nonce, 12);
246     chacha20_poly1305_open(out, in, plaintext_len, ad, ad_len, tag);
247   } else {
248     calc_tag(tag, c20_ctx, nonce, ad, ad_len, in, plaintext_len);
249     CRYPTO_chacha_20(out, in, plaintext_len, c20_ctx->key, nonce, 1);
250   }
251 
252   if (CRYPTO_memcmp(tag, in + plaintext_len, c20_ctx->tag_len) != 0) {
253     OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BAD_DECRYPT);
254     return 0;
255   }
256 
257   *out_len = plaintext_len;
258   return 1;
259 }
260 
261 static const EVP_AEAD aead_chacha20_poly1305 = {
262     32,                 /* key len */
263     12,                 /* nonce len */
264     POLY1305_TAG_LEN,   /* overhead */
265     POLY1305_TAG_LEN,   /* max tag length */
266     aead_chacha20_poly1305_init,
267     NULL, /* init_with_direction */
268     aead_chacha20_poly1305_cleanup,
269     aead_chacha20_poly1305_seal,
270     aead_chacha20_poly1305_open,
271     NULL,               /* get_iv */
272 };
273 
EVP_aead_chacha20_poly1305(void)274 const EVP_AEAD *EVP_aead_chacha20_poly1305(void) {
275   return &aead_chacha20_poly1305;
276 }
277