blob: 6cfc856ce5ee506cfc4164bdb4293d6ed0ceeeaa [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>
Robert Sloan4d1ac502017-02-06 08:36:14 -080021#include <openssl/cpu.h>
Adam Langleyd9e397b2015-01-22 14:27:53 -080022#include <openssl/err.h>
23#include <openssl/mem.h>
24#include <openssl/poly1305.h>
25
Robert Sloan8ff03552017-06-14 12:40:58 -070026#include "../fipsmodule/cipher/internal.h"
David Benjamin4969cc92016-04-22 15:02:23 -040027#include "../internal.h"
Adam Langleyd9e397b2015-01-22 14:27:53 -080028
29
30#define POLY1305_TAG_LEN 16
Adam Langleyd9e397b2015-01-22 14:27:53 -080031
32struct aead_chacha20_poly1305_ctx {
33 unsigned char key[32];
Adam Langleyd9e397b2015-01-22 14:27:53 -080034};
35
David Benjaminf31229b2017-01-25 14:08:15 -050036#if defined(OPENSSL_X86_64) && !defined(OPENSSL_NO_ASM) && \
37 !defined(OPENSSL_WINDOWS)
Robert Sloan4d1ac502017-02-06 08:36:14 -080038static int asm_capable(void) {
39 const int sse41_capable = (OPENSSL_ia32cap_P[1] & (1 << 19)) != 0;
40 return sse41_capable;
41}
42
David Benjaminf31229b2017-01-25 14:08:15 -050043// chacha20_poly1305_open is defined in chacha20_poly1305_x86_64.pl. It
44// decrypts |plaintext_len| bytes from |ciphertext| and writes them to
45// |out_plaintext|. On entry, |aead_data| must contain the final 48 bytes of
46// the initial ChaCha20 block, i.e. the key, followed by four zeros, followed
47// by the nonce. On exit, it will contain the calculated tag value, which the
48// caller must check.
Robert Sloan4d1ac502017-02-06 08:36:14 -080049extern void chacha20_poly1305_open(uint8_t *out_plaintext,
50 const uint8_t *ciphertext,
51 size_t plaintext_len, const uint8_t *ad,
52 size_t ad_len, uint8_t *aead_data);
David Benjaminf31229b2017-01-25 14:08:15 -050053
54// chacha20_poly1305_open is defined in chacha20_poly1305_x86_64.pl. It
55// encrypts |plaintext_len| bytes from |plaintext| and writes them to
56// |out_ciphertext|. On entry, |aead_data| must contain the final 48 bytes of
57// the initial ChaCha20 block, i.e. the key, followed by four zeros, followed
58// by the nonce. On exit, it will contain the calculated tag value, which the
59// caller must append to the ciphertext.
Robert Sloan4d1ac502017-02-06 08:36:14 -080060extern void chacha20_poly1305_seal(uint8_t *out_ciphertext,
61 const uint8_t *plaintext,
62 size_t plaintext_len, const uint8_t *ad,
63 size_t ad_len, uint8_t *aead_data);
David Benjaminf31229b2017-01-25 14:08:15 -050064#else
Robert Sloan4d1ac502017-02-06 08:36:14 -080065static int asm_capable(void) {
66 return 0;
67}
68
David Benjaminf31229b2017-01-25 14:08:15 -050069
70static void chacha20_poly1305_open(uint8_t *out_plaintext,
71 const uint8_t *ciphertext,
72 size_t plaintext_len, const uint8_t *ad,
73 size_t ad_len, uint8_t *aead_data) {}
74
75static void chacha20_poly1305_seal(uint8_t *out_ciphertext,
76 const uint8_t *plaintext,
77 size_t plaintext_len, const uint8_t *ad,
78 size_t ad_len, uint8_t *aead_data) {}
79#endif
80
Adam Langleyd9e397b2015-01-22 14:27:53 -080081static int aead_chacha20_poly1305_init(EVP_AEAD_CTX *ctx, const uint8_t *key,
82 size_t key_len, size_t tag_len) {
83 struct aead_chacha20_poly1305_ctx *c20_ctx;
84
85 if (tag_len == 0) {
86 tag_len = POLY1305_TAG_LEN;
87 }
88
89 if (tag_len > POLY1305_TAG_LEN) {
Kenny Rootb8494592015-09-25 02:29:14 +000090 OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_TOO_LARGE);
Adam Langleyd9e397b2015-01-22 14:27:53 -080091 return 0;
92 }
93
94 if (key_len != sizeof(c20_ctx->key)) {
95 return 0; /* internal error - EVP_AEAD_CTX_init should catch this. */
96 }
97
98 c20_ctx = OPENSSL_malloc(sizeof(struct aead_chacha20_poly1305_ctx));
99 if (c20_ctx == NULL) {
100 return 0;
101 }
102
Robert Sloan69939df2017-01-09 10:53:07 -0800103 OPENSSL_memcpy(c20_ctx->key, key, key_len);
Adam Langleyd9e397b2015-01-22 14:27:53 -0800104 ctx->aead_state = c20_ctx;
Robert Sloan8ff03552017-06-14 12:40:58 -0700105 ctx->tag_len = tag_len;
Adam Langleyd9e397b2015-01-22 14:27:53 -0800106
107 return 1;
108}
109
110static void aead_chacha20_poly1305_cleanup(EVP_AEAD_CTX *ctx) {
111 struct aead_chacha20_poly1305_ctx *c20_ctx = ctx->aead_state;
112 OPENSSL_cleanse(c20_ctx->key, sizeof(c20_ctx->key));
113 OPENSSL_free(c20_ctx);
114}
115
Kenny Roote99801b2015-11-06 15:31:15 -0800116static void poly1305_update_length(poly1305_state *poly1305, size_t data_len) {
Adam Langleyd9e397b2015-01-22 14:27:53 -0800117 uint8_t length_bytes[8];
Adam Langleyd9e397b2015-01-22 14:27:53 -0800118
David Benjaminf31229b2017-01-25 14:08:15 -0500119 for (unsigned i = 0; i < sizeof(length_bytes); i++) {
Kenny Roote99801b2015-11-06 15:31:15 -0800120 length_bytes[i] = data_len;
121 data_len >>= 8;
Adam Langleyd9e397b2015-01-22 14:27:53 -0800122 }
123
Adam Langleyd9e397b2015-01-22 14:27:53 -0800124 CRYPTO_poly1305_update(poly1305, length_bytes, sizeof(length_bytes));
125}
126
David Benjaminf31229b2017-01-25 14:08:15 -0500127static void poly1305_update_padded_16(poly1305_state *poly1305,
128 const uint8_t *data, size_t data_len) {
129 static const uint8_t padding[16] = { 0 }; /* Padding is all zeros. */
Kenny Roote99801b2015-11-06 15:31:15 -0800130
David Benjaminf31229b2017-01-25 14:08:15 -0500131 CRYPTO_poly1305_update(poly1305, data, data_len);
132 if (data_len % 16 != 0) {
133 CRYPTO_poly1305_update(poly1305, padding,
134 sizeof(padding) - (data_len % 16));
135 }
136}
137
138/* calc_tag fills |tag| with the authentication tag for the given inputs. */
139static void calc_tag(uint8_t tag[POLY1305_TAG_LEN],
140 const struct aead_chacha20_poly1305_ctx *c20_ctx,
141 const uint8_t nonce[12], const uint8_t *ad, size_t ad_len,
142 const uint8_t *ciphertext, size_t ciphertext_len) {
David Benjamin4969cc92016-04-22 15:02:23 -0400143 alignas(16) uint8_t poly1305_key[32];
Robert Sloan69939df2017-01-09 10:53:07 -0800144 OPENSSL_memset(poly1305_key, 0, sizeof(poly1305_key));
Kenny Roote99801b2015-11-06 15:31:15 -0800145 CRYPTO_chacha_20(poly1305_key, poly1305_key, sizeof(poly1305_key),
146 c20_ctx->key, nonce, 0);
David Benjaminf31229b2017-01-25 14:08:15 -0500147
Kenny Roote99801b2015-11-06 15:31:15 -0800148 poly1305_state ctx;
149 CRYPTO_poly1305_init(&ctx, poly1305_key);
David Benjaminf31229b2017-01-25 14:08:15 -0500150 poly1305_update_padded_16(&ctx, ad, ad_len);
151 poly1305_update_padded_16(&ctx, ciphertext, ciphertext_len);
152 poly1305_update_length(&ctx, ad_len);
153 poly1305_update_length(&ctx, ciphertext_len);
Kenny Roote99801b2015-11-06 15:31:15 -0800154 CRYPTO_poly1305_finish(&ctx, tag);
155}
156
Robert Sloan8ff03552017-06-14 12:40:58 -0700157static int aead_chacha20_poly1305_seal_scatter(
158 const EVP_AEAD_CTX *ctx, uint8_t *out, uint8_t *out_tag,
159 size_t *out_tag_len, size_t max_out_tag_len, const uint8_t *nonce,
Robert Sloan927a4952017-07-03 11:25:09 -0700160 size_t nonce_len, const uint8_t *in, size_t in_len, const uint8_t *extra_in,
161 size_t extra_in_len, const uint8_t *ad, size_t ad_len) {
Kenny Roote99801b2015-11-06 15:31:15 -0800162 const struct aead_chacha20_poly1305_ctx *c20_ctx = ctx->aead_state;
Adam Langleyd9e397b2015-01-22 14:27:53 -0800163
David Benjaminf31229b2017-01-25 14:08:15 -0500164 if (nonce_len != 12) {
165 OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_UNSUPPORTED_NONCE_SIZE);
166 return 0;
167 }
168
Kenny Roote99801b2015-11-06 15:31:15 -0800169 /* |CRYPTO_chacha_20| uses a 32-bit block counter. Therefore we disallow
Adam Langleyd9e397b2015-01-22 14:27:53 -0800170 * individual operations that work on more than 256GB at a time.
171 * |in_len_64| is needed because, on 32-bit platforms, size_t is only
172 * 32-bits and this produces a warning because it's always false.
173 * Casting to uint64_t inside the conditional is not sufficient to stop
174 * the warning. */
Robert Sloan8ff03552017-06-14 12:40:58 -0700175 const uint64_t in_len_64 = in_len;
David Benjamin4969cc92016-04-22 15:02:23 -0400176 if (in_len_64 >= (UINT64_C(1) << 32) * 64 - 64) {
Kenny Rootb8494592015-09-25 02:29:14 +0000177 OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_TOO_LARGE);
Adam Langleyd9e397b2015-01-22 14:27:53 -0800178 return 0;
179 }
180
Robert Sloan8ff03552017-06-14 12:40:58 -0700181 if (max_out_tag_len < ctx->tag_len) {
Kenny Rootb8494592015-09-25 02:29:14 +0000182 OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BUFFER_TOO_SMALL);
Adam Langleyd9e397b2015-01-22 14:27:53 -0800183 return 0;
184 }
185
David Benjaminf31229b2017-01-25 14:08:15 -0500186 alignas(16) uint8_t tag[48];
Adam Langleyd9e397b2015-01-22 14:27:53 -0800187
Robert Sloan4d1ac502017-02-06 08:36:14 -0800188 if (asm_capable()) {
David Benjaminf31229b2017-01-25 14:08:15 -0500189 OPENSSL_memcpy(tag, c20_ctx->key, 32);
190 OPENSSL_memset(tag + 32, 0, 4);
191 OPENSSL_memcpy(tag + 32 + 4, nonce, 12);
192 chacha20_poly1305_seal(out, in, in_len, ad, ad_len, tag);
193 } else {
194 CRYPTO_chacha_20(out, in, in_len, c20_ctx->key, nonce, 1);
195 calc_tag(tag, c20_ctx, nonce, ad, ad_len, out, in_len);
196 }
Kenny Roote99801b2015-11-06 15:31:15 -0800197
Robert Sloan8ff03552017-06-14 12:40:58 -0700198 OPENSSL_memcpy(out_tag, tag, ctx->tag_len);
199 *out_tag_len = ctx->tag_len;
Adam Langleyd9e397b2015-01-22 14:27:53 -0800200 return 1;
201}
202
Robert Sloan8ff03552017-06-14 12:40:58 -0700203static int aead_chacha20_poly1305_open_gather(
204 const EVP_AEAD_CTX *ctx, uint8_t *out, const uint8_t *nonce,
205 size_t nonce_len, const uint8_t *in, size_t in_len, const uint8_t *in_tag,
206 size_t in_tag_len, const uint8_t *ad, size_t ad_len) {
Adam Langleyd9e397b2015-01-22 14:27:53 -0800207 const struct aead_chacha20_poly1305_ctx *c20_ctx = ctx->aead_state;
Adam Langleyd9e397b2015-01-22 14:27:53 -0800208
David Benjaminf31229b2017-01-25 14:08:15 -0500209 if (nonce_len != 12) {
210 OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_UNSUPPORTED_NONCE_SIZE);
211 return 0;
212 }
213
Robert Sloan8ff03552017-06-14 12:40:58 -0700214 if (in_tag_len != ctx->tag_len) {
Kenny Rootb8494592015-09-25 02:29:14 +0000215 OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BAD_DECRYPT);
Adam Langleyd9e397b2015-01-22 14:27:53 -0800216 return 0;
217 }
218
Kenny Roote99801b2015-11-06 15:31:15 -0800219 /* |CRYPTO_chacha_20| uses a 32-bit block counter. Therefore we disallow
Adam Langleyd9e397b2015-01-22 14:27:53 -0800220 * individual operations that work on more than 256GB at a time.
221 * |in_len_64| is needed because, on 32-bit platforms, size_t is only
222 * 32-bits and this produces a warning because it's always false.
223 * Casting to uint64_t inside the conditional is not sufficient to stop
224 * the warning. */
Robert Sloan8ff03552017-06-14 12:40:58 -0700225 const uint64_t in_len_64 = in_len;
David Benjamin4969cc92016-04-22 15:02:23 -0400226 if (in_len_64 >= (UINT64_C(1) << 32) * 64 - 64) {
Kenny Rootb8494592015-09-25 02:29:14 +0000227 OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_TOO_LARGE);
Adam Langleyd9e397b2015-01-22 14:27:53 -0800228 return 0;
229 }
230
David Benjaminf31229b2017-01-25 14:08:15 -0500231 alignas(16) uint8_t tag[48];
232
Robert Sloan4d1ac502017-02-06 08:36:14 -0800233 if (asm_capable()) {
David Benjaminf31229b2017-01-25 14:08:15 -0500234 OPENSSL_memcpy(tag, c20_ctx->key, 32);
235 OPENSSL_memset(tag + 32, 0, 4);
236 OPENSSL_memcpy(tag + 32 + 4, nonce, 12);
Robert Sloan8ff03552017-06-14 12:40:58 -0700237 chacha20_poly1305_open(out, in, in_len, ad, ad_len, tag);
David Benjaminf31229b2017-01-25 14:08:15 -0500238 } else {
Robert Sloan8ff03552017-06-14 12:40:58 -0700239 calc_tag(tag, c20_ctx, nonce, ad, ad_len, in, in_len);
240 CRYPTO_chacha_20(out, in, in_len, c20_ctx->key, nonce, 1);
David Benjaminf31229b2017-01-25 14:08:15 -0500241 }
242
Robert Sloan8ff03552017-06-14 12:40:58 -0700243 if (CRYPTO_memcmp(tag, in_tag, ctx->tag_len) != 0) {
Kenny Rootb8494592015-09-25 02:29:14 +0000244 OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BAD_DECRYPT);
Adam Langleyd9e397b2015-01-22 14:27:53 -0800245 return 0;
246 }
247
Adam Langleyd9e397b2015-01-22 14:27:53 -0800248 return 1;
249}
250
251static const EVP_AEAD aead_chacha20_poly1305 = {
Robert Sloan927a4952017-07-03 11:25:09 -0700252 32, /* key len */
253 12, /* nonce len */
254 POLY1305_TAG_LEN, /* overhead */
255 POLY1305_TAG_LEN, /* max tag length */
256 0, /* seal_scatter_supports_extra_in */
257
Adam Langleye9ada862015-05-11 17:20:37 -0700258 aead_chacha20_poly1305_init,
259 NULL, /* init_with_direction */
260 aead_chacha20_poly1305_cleanup,
Robert Sloan8ff03552017-06-14 12:40:58 -0700261 NULL /* open */,
262 aead_chacha20_poly1305_seal_scatter,
263 aead_chacha20_poly1305_open_gather,
Robert Sloan927a4952017-07-03 11:25:09 -0700264 NULL, /* get_iv */
Adam Langleyd9e397b2015-01-22 14:27:53 -0800265};
266
Adam Langley4139edb2016-01-13 15:00:54 -0800267const EVP_AEAD *EVP_aead_chacha20_poly1305(void) {
Kenny Root03bcf612015-11-05 20:20:27 +0000268 return &aead_chacha20_poly1305;
Adam Langleyfdeb4882015-10-30 13:15:30 -0700269}