blob: 852b2c64eec7a83120e44bb9fa3439422c0a76b3 [file] [log] [blame]
Adam Langleyd9e397b2015-01-22 14:27:53 -08001/* 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"
David Benjamin4969cc92016-04-22 15:02:23 -040026#include "../internal.h"
Adam Langleyd9e397b2015-01-22 14:27:53 -080027
28
29#define POLY1305_TAG_LEN 16
Adam Langleyd9e397b2015-01-22 14:27:53 -080030
31struct aead_chacha20_poly1305_ctx {
32 unsigned char key[32];
33 unsigned char tag_len;
34};
35
36static 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) {
Kenny Rootb8494592015-09-25 02:29:14 +000045 OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_TOO_LARGE);
Adam Langleyd9e397b2015-01-22 14:27:53 -080046 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
65static 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
Kenny Roote99801b2015-11-06 15:31:15 -080071static void poly1305_update_length(poly1305_state *poly1305, size_t data_len) {
Adam Langleyd9e397b2015-01-22 14:27:53 -080072 uint8_t length_bytes[8];
73 unsigned i;
74
75 for (i = 0; i < sizeof(length_bytes); i++) {
Kenny Roote99801b2015-11-06 15:31:15 -080076 length_bytes[i] = data_len;
77 data_len >>= 8;
Adam Langleyd9e397b2015-01-22 14:27:53 -080078 }
79
Adam Langleyd9e397b2015-01-22 14:27:53 -080080 CRYPTO_poly1305_update(poly1305, length_bytes, sizeof(length_bytes));
81}
82
Kenny Roote99801b2015-11-06 15:31:15 -080083typedef void (*aead_poly1305_update)(poly1305_state *ctx, const uint8_t *ad,
84 size_t ad_len, const uint8_t *ciphertext,
85 size_t ciphertext_len);
86
87/* aead_poly1305 fills |tag| with the authentication tag for the given
88 * inputs, using |update| to control the order and format that the inputs are
89 * signed/authenticated. */
90static void aead_poly1305(aead_poly1305_update update,
91 uint8_t tag[POLY1305_TAG_LEN],
92 const struct aead_chacha20_poly1305_ctx *c20_ctx,
93 const uint8_t nonce[12], const uint8_t *ad,
94 size_t ad_len, const uint8_t *ciphertext,
95 size_t ciphertext_len) {
David Benjamin4969cc92016-04-22 15:02:23 -040096 alignas(16) uint8_t poly1305_key[32];
Kenny Roote99801b2015-11-06 15:31:15 -080097 memset(poly1305_key, 0, sizeof(poly1305_key));
98 CRYPTO_chacha_20(poly1305_key, poly1305_key, sizeof(poly1305_key),
99 c20_ctx->key, nonce, 0);
100 poly1305_state ctx;
101 CRYPTO_poly1305_init(&ctx, poly1305_key);
102 update(&ctx, ad, ad_len, ciphertext, ciphertext_len);
103 CRYPTO_poly1305_finish(&ctx, tag);
104}
105
Adam Langley4139edb2016-01-13 15:00:54 -0800106static int seal_impl(aead_poly1305_update poly1305_update,
107 const EVP_AEAD_CTX *ctx, uint8_t *out, size_t *out_len,
108 size_t max_out_len, const uint8_t nonce[12],
109 const uint8_t *in, size_t in_len, const uint8_t *ad,
110 size_t ad_len) {
Kenny Roote99801b2015-11-06 15:31:15 -0800111 const struct aead_chacha20_poly1305_ctx *c20_ctx = ctx->aead_state;
Adam Langleyd9e397b2015-01-22 14:27:53 -0800112 const uint64_t in_len_64 = in_len;
113
Kenny Roote99801b2015-11-06 15:31:15 -0800114 /* |CRYPTO_chacha_20| uses a 32-bit block counter. Therefore we disallow
Adam Langleyd9e397b2015-01-22 14:27:53 -0800115 * individual operations that work on more than 256GB at a time.
116 * |in_len_64| is needed because, on 32-bit platforms, size_t is only
117 * 32-bits and this produces a warning because it's always false.
118 * Casting to uint64_t inside the conditional is not sufficient to stop
119 * the warning. */
David Benjamin4969cc92016-04-22 15:02:23 -0400120 if (in_len_64 >= (UINT64_C(1) << 32) * 64 - 64) {
Kenny Rootb8494592015-09-25 02:29:14 +0000121 OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_TOO_LARGE);
Adam Langleyd9e397b2015-01-22 14:27:53 -0800122 return 0;
123 }
124
125 if (in_len + c20_ctx->tag_len < in_len) {
Kenny Rootb8494592015-09-25 02:29:14 +0000126 OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_TOO_LARGE);
Adam Langleyd9e397b2015-01-22 14:27:53 -0800127 return 0;
128 }
129
130 if (max_out_len < in_len + c20_ctx->tag_len) {
Kenny Rootb8494592015-09-25 02:29:14 +0000131 OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BUFFER_TOO_SMALL);
Adam Langleyd9e397b2015-01-22 14:27:53 -0800132 return 0;
133 }
134
Adam Langleyd9e397b2015-01-22 14:27:53 -0800135 CRYPTO_chacha_20(out, in, in_len, c20_ctx->key, nonce, 1);
Adam Langleyd9e397b2015-01-22 14:27:53 -0800136
David Benjamin4969cc92016-04-22 15:02:23 -0400137 alignas(16) uint8_t tag[POLY1305_TAG_LEN];
Kenny Roote99801b2015-11-06 15:31:15 -0800138 aead_poly1305(poly1305_update, tag, c20_ctx, nonce, ad, ad_len, out, in_len);
139
Adam Langleyd9e397b2015-01-22 14:27:53 -0800140 memcpy(out + in_len, tag, c20_ctx->tag_len);
141 *out_len = in_len + c20_ctx->tag_len;
142 return 1;
143}
144
Adam Langley4139edb2016-01-13 15:00:54 -0800145static int open_impl(aead_poly1305_update poly1305_update,
146 const EVP_AEAD_CTX *ctx, uint8_t *out, size_t *out_len,
147 size_t max_out_len, const uint8_t nonce[12],
148 const uint8_t *in, size_t in_len, const uint8_t *ad,
149 size_t ad_len) {
Adam Langleyd9e397b2015-01-22 14:27:53 -0800150 const struct aead_chacha20_poly1305_ctx *c20_ctx = ctx->aead_state;
Adam Langleyd9e397b2015-01-22 14:27:53 -0800151 size_t plaintext_len;
Adam Langleyd9e397b2015-01-22 14:27:53 -0800152 const uint64_t in_len_64 = in_len;
153
154 if (in_len < c20_ctx->tag_len) {
Kenny Rootb8494592015-09-25 02:29:14 +0000155 OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BAD_DECRYPT);
Adam Langleyd9e397b2015-01-22 14:27:53 -0800156 return 0;
157 }
158
Kenny Roote99801b2015-11-06 15:31:15 -0800159 /* |CRYPTO_chacha_20| uses a 32-bit block counter. Therefore we disallow
Adam Langleyd9e397b2015-01-22 14:27:53 -0800160 * individual operations that work on more than 256GB at a time.
161 * |in_len_64| is needed because, on 32-bit platforms, size_t is only
162 * 32-bits and this produces a warning because it's always false.
163 * Casting to uint64_t inside the conditional is not sufficient to stop
164 * the warning. */
David Benjamin4969cc92016-04-22 15:02:23 -0400165 if (in_len_64 >= (UINT64_C(1) << 32) * 64 - 64) {
Kenny Rootb8494592015-09-25 02:29:14 +0000166 OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_TOO_LARGE);
Adam Langleyd9e397b2015-01-22 14:27:53 -0800167 return 0;
168 }
169
Adam Langleyd9e397b2015-01-22 14:27:53 -0800170 plaintext_len = in_len - c20_ctx->tag_len;
David Benjamin4969cc92016-04-22 15:02:23 -0400171 alignas(16) uint8_t tag[POLY1305_TAG_LEN];
Kenny Roote99801b2015-11-06 15:31:15 -0800172 aead_poly1305(poly1305_update, tag, c20_ctx, nonce, ad, ad_len, in,
173 plaintext_len);
174 if (CRYPTO_memcmp(tag, in + plaintext_len, c20_ctx->tag_len) != 0) {
Kenny Rootb8494592015-09-25 02:29:14 +0000175 OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BAD_DECRYPT);
Adam Langleyd9e397b2015-01-22 14:27:53 -0800176 return 0;
177 }
178
179 CRYPTO_chacha_20(out, in, plaintext_len, c20_ctx->key, nonce, 1);
180 *out_len = plaintext_len;
181 return 1;
182}
183
Kenny Roote99801b2015-11-06 15:31:15 -0800184static void poly1305_update_padded_16(poly1305_state *poly1305,
185 const uint8_t *data, size_t data_len) {
186 static const uint8_t padding[16] = { 0 }; /* Padding is all zeros. */
187
188 CRYPTO_poly1305_update(poly1305, data, data_len);
189 if (data_len % 16 != 0) {
190 CRYPTO_poly1305_update(poly1305, padding, sizeof(padding) - (data_len % 16));
191 }
192}
193
194static void poly1305_update(poly1305_state *ctx, const uint8_t *ad,
195 size_t ad_len, const uint8_t *ciphertext,
196 size_t ciphertext_len) {
197 poly1305_update_padded_16(ctx, ad, ad_len);
198 poly1305_update_padded_16(ctx, ciphertext, ciphertext_len);
199 poly1305_update_length(ctx, ad_len);
200 poly1305_update_length(ctx, ciphertext_len);
201}
202
203static int aead_chacha20_poly1305_seal(const EVP_AEAD_CTX *ctx, uint8_t *out,
204 size_t *out_len, size_t max_out_len,
205 const uint8_t *nonce, size_t nonce_len,
206 const uint8_t *in, size_t in_len,
207 const uint8_t *ad, size_t ad_len) {
208 if (nonce_len != 12) {
209 OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_UNSUPPORTED_NONCE_SIZE);
210 return 0;
211 }
Adam Langley4139edb2016-01-13 15:00:54 -0800212 return seal_impl(poly1305_update, ctx, out, out_len, max_out_len, nonce, in,
213 in_len, ad, ad_len);
Kenny Roote99801b2015-11-06 15:31:15 -0800214}
215
216static int aead_chacha20_poly1305_open(const EVP_AEAD_CTX *ctx, uint8_t *out,
217 size_t *out_len, size_t max_out_len,
218 const uint8_t *nonce, size_t nonce_len,
219 const uint8_t *in, size_t in_len,
220 const uint8_t *ad, size_t ad_len) {
221 if (nonce_len != 12) {
222 OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_UNSUPPORTED_NONCE_SIZE);
223 return 0;
224 }
Adam Langley4139edb2016-01-13 15:00:54 -0800225 return open_impl(poly1305_update, ctx, out, out_len, max_out_len, nonce, in,
226 in_len, ad, ad_len);
Kenny Roote99801b2015-11-06 15:31:15 -0800227}
228
Adam Langleyd9e397b2015-01-22 14:27:53 -0800229static const EVP_AEAD aead_chacha20_poly1305 = {
230 32, /* key len */
Kenny Roote99801b2015-11-06 15:31:15 -0800231 12, /* nonce len */
Adam Langleyd9e397b2015-01-22 14:27:53 -0800232 POLY1305_TAG_LEN, /* overhead */
233 POLY1305_TAG_LEN, /* max tag length */
Adam Langleye9ada862015-05-11 17:20:37 -0700234 aead_chacha20_poly1305_init,
235 NULL, /* init_with_direction */
236 aead_chacha20_poly1305_cleanup,
237 aead_chacha20_poly1305_seal,
238 aead_chacha20_poly1305_open,
239 NULL, /* get_rc4_state */
Adam Langleyfad63272015-11-12 12:15:39 -0800240 NULL, /* get_iv */
Adam Langleyd9e397b2015-01-22 14:27:53 -0800241};
242
Adam Langley4139edb2016-01-13 15:00:54 -0800243const EVP_AEAD *EVP_aead_chacha20_poly1305(void) {
Kenny Root03bcf612015-11-05 20:20:27 +0000244 return &aead_chacha20_poly1305;
Adam Langleyfdeb4882015-10-30 13:15:30 -0700245}
Kenny Roote99801b2015-11-06 15:31:15 -0800246
247static void poly1305_update_old(poly1305_state *ctx, const uint8_t *ad,
248 size_t ad_len, const uint8_t *ciphertext,
249 size_t ciphertext_len) {
250 CRYPTO_poly1305_update(ctx, ad, ad_len);
251 poly1305_update_length(ctx, ad_len);
252 CRYPTO_poly1305_update(ctx, ciphertext, ciphertext_len);
253 poly1305_update_length(ctx, ciphertext_len);
254}
255
256static int aead_chacha20_poly1305_old_seal(
257 const EVP_AEAD_CTX *ctx, uint8_t *out, size_t *out_len, size_t max_out_len,
258 const uint8_t *nonce, size_t nonce_len, const uint8_t *in, size_t in_len,
259 const uint8_t *ad, size_t ad_len) {
260 if (nonce_len != 8) {
261 OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_UNSUPPORTED_NONCE_SIZE);
262 return 0;
263 }
264 uint8_t nonce_96[12];
265 memset(nonce_96, 0, 4);
266 memcpy(nonce_96 + 4, nonce, 8);
Adam Langley4139edb2016-01-13 15:00:54 -0800267 return seal_impl(poly1305_update_old, ctx, out, out_len, max_out_len,
268 nonce_96, in, in_len, ad, ad_len);
Kenny Roote99801b2015-11-06 15:31:15 -0800269}
270
271static int aead_chacha20_poly1305_old_open(
272 const EVP_AEAD_CTX *ctx, uint8_t *out, size_t *out_len, size_t max_out_len,
273 const uint8_t *nonce, size_t nonce_len, const uint8_t *in, size_t in_len,
274 const uint8_t *ad, size_t ad_len) {
275 if (nonce_len != 8) {
276 OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_UNSUPPORTED_NONCE_SIZE);
277 return 0;
278 }
279 uint8_t nonce_96[12];
280 memset(nonce_96, 0, 4);
281 memcpy(nonce_96 + 4, nonce, 8);
Adam Langley4139edb2016-01-13 15:00:54 -0800282 return open_impl(poly1305_update_old, ctx, out, out_len, max_out_len,
283 nonce_96, in, in_len, ad, ad_len);
Kenny Roote99801b2015-11-06 15:31:15 -0800284}
285
286static const EVP_AEAD aead_chacha20_poly1305_old = {
287 32, /* key len */
288 8, /* nonce len */
289 POLY1305_TAG_LEN, /* overhead */
290 POLY1305_TAG_LEN, /* max tag length */
291 aead_chacha20_poly1305_init,
292 NULL, /* init_with_direction */
293 aead_chacha20_poly1305_cleanup,
294 aead_chacha20_poly1305_old_seal,
295 aead_chacha20_poly1305_old_open,
296 NULL, /* get_rc4_state */
Adam Langleyfad63272015-11-12 12:15:39 -0800297 NULL, /* get_iv */
Kenny Roote99801b2015-11-06 15:31:15 -0800298};
299
300const EVP_AEAD *EVP_aead_chacha20_poly1305_old(void) {
301 return &aead_chacha20_poly1305_old;
302}