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
30struct aead_chacha20_poly1305_ctx {
31  unsigned char key[32];
32  unsigned char tag_len;
33};
34
35static 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
64static 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
70static 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
88typedef 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. */
95static 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
111static 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
150static 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
189static 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
199static 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
208static 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
221static 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
234static 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
248const EVP_AEAD *EVP_aead_chacha20_poly1305(void) {
249  return &aead_chacha20_poly1305;
250}
251
252const EVP_AEAD *EVP_aead_chacha20_poly1305_rfc7539(void) {
253  return EVP_aead_chacha20_poly1305();
254}
255
256static 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
265static 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
280static 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
295static 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
309const EVP_AEAD *EVP_aead_chacha20_poly1305_old(void) {
310  return &aead_chacha20_poly1305_old;
311}
312