xref: /openbsd-src/lib/libcrypto/evp/e_chacha20poly1305.c (revision 50b7afb2c2c0993b0894d4e34bf857cb13ed9c80)
1 /* $OpenBSD: e_chacha20poly1305.c,v 1.8 2014/07/10 22:45:57 jsing Exp $ */
2 /*
3  * Copyright (c) 2014, Google Inc.
4  *
5  * Permission to use, copy, modify, and/or distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
12  * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
14  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
15  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include <stdint.h>
19 #include <string.h>
20 
21 #include <openssl/opensslconf.h>
22 
23 #if !defined(OPENSSL_NO_CHACHA) && !defined(OPENSSL_NO_POLY1305)
24 
25 #include <openssl/err.h>
26 #include <openssl/evp.h>
27 #include <openssl/chacha.h>
28 #include <openssl/poly1305.h>
29 
30 #include "evp_locl.h"
31 
32 #define POLY1305_TAG_LEN 16
33 #define CHACHA20_NONCE_LEN 8
34 
35 struct aead_chacha20_poly1305_ctx {
36 	unsigned char key[32];
37 	unsigned char tag_len;
38 };
39 
40 static int
41 aead_chacha20_poly1305_init(EVP_AEAD_CTX *ctx, const unsigned char *key,
42     size_t key_len, size_t tag_len)
43 {
44 	struct aead_chacha20_poly1305_ctx *c20_ctx;
45 
46 	if (tag_len == 0)
47 		tag_len = POLY1305_TAG_LEN;
48 
49 	if (tag_len > POLY1305_TAG_LEN) {
50 		EVPerr(EVP_F_AEAD_CHACHA20_POLY1305_INIT, EVP_R_TOO_LARGE);
51 		return 0;
52 	}
53 
54 	/* Internal error - EVP_AEAD_CTX_init should catch this. */
55 	if (key_len != sizeof(c20_ctx->key))
56 		return 0;
57 
58 	c20_ctx = malloc(sizeof(struct aead_chacha20_poly1305_ctx));
59 	if (c20_ctx == NULL)
60 		return 0;
61 
62 	memcpy(&c20_ctx->key[0], key, key_len);
63 	c20_ctx->tag_len = tag_len;
64 	ctx->aead_state = c20_ctx;
65 
66 	return 1;
67 }
68 
69 static void
70 aead_chacha20_poly1305_cleanup(EVP_AEAD_CTX *ctx)
71 {
72 	struct aead_chacha20_poly1305_ctx *c20_ctx = ctx->aead_state;
73 
74 	OPENSSL_cleanse(c20_ctx->key, sizeof(c20_ctx->key));
75 	free(c20_ctx);
76 }
77 
78 static void
79 poly1305_update_with_length(poly1305_state *poly1305,
80     const unsigned char *data, size_t data_len)
81 {
82 	size_t j = data_len;
83 	unsigned char length_bytes[8];
84 	unsigned i;
85 
86 	for (i = 0; i < sizeof(length_bytes); i++) {
87 		length_bytes[i] = j;
88 		j >>= 8;
89 	}
90 
91 	CRYPTO_poly1305_update(poly1305, data, data_len);
92 	CRYPTO_poly1305_update(poly1305, length_bytes, sizeof(length_bytes));
93 }
94 
95 static int
96 aead_chacha20_poly1305_seal(const EVP_AEAD_CTX *ctx, unsigned char *out,
97     size_t *out_len, size_t max_out_len, const unsigned char *nonce,
98     size_t nonce_len, const unsigned char *in, size_t in_len,
99     const unsigned char *ad, size_t ad_len)
100 {
101 	const struct aead_chacha20_poly1305_ctx *c20_ctx = ctx->aead_state;
102 	unsigned char poly1305_key[32];
103 	poly1305_state poly1305;
104 	const uint64_t in_len_64 = in_len;
105 
106 	/* The underlying ChaCha implementation may not overflow the block
107 	 * counter into the second counter word. Therefore we disallow
108 	 * individual operations that work on more than 2TB at a time.
109 	 * in_len_64 is needed because, on 32-bit platforms, size_t is only
110 	 * 32-bits and this produces a warning because it's always false.
111 	 * Casting to uint64_t inside the conditional is not sufficient to stop
112 	 * the warning. */
113 	if (in_len_64 >= (1ULL << 32) * 64 - 64) {
114 		EVPerr(EVP_F_AEAD_CHACHA20_POLY1305_SEAL, EVP_R_TOO_LARGE);
115 		return 0;
116 	}
117 
118 	if (max_out_len < in_len + c20_ctx->tag_len) {
119 		EVPerr(EVP_F_AEAD_CHACHA20_POLY1305_SEAL,
120 		    EVP_R_BUFFER_TOO_SMALL);
121 		return 0;
122 	}
123 
124 	if (nonce_len != CHACHA20_NONCE_LEN) {
125 		EVPerr(EVP_F_AEAD_CHACHA20_POLY1305_SEAL, EVP_R_IV_TOO_LARGE);
126 		return 0;
127 	}
128 
129 	memset(poly1305_key, 0, sizeof(poly1305_key));
130 	CRYPTO_chacha_20(poly1305_key, poly1305_key, sizeof(poly1305_key),
131 	    c20_ctx->key, nonce, 0);
132 
133 	CRYPTO_poly1305_init(&poly1305, poly1305_key);
134 	poly1305_update_with_length(&poly1305, ad, ad_len);
135 	CRYPTO_chacha_20(out, in, in_len, c20_ctx->key, nonce, 1);
136 	poly1305_update_with_length(&poly1305, out, in_len);
137 
138 	if (c20_ctx->tag_len != POLY1305_TAG_LEN) {
139 		unsigned char tag[POLY1305_TAG_LEN];
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 
146 	CRYPTO_poly1305_finish(&poly1305, out + in_len);
147 	*out_len = in_len + POLY1305_TAG_LEN;
148 	return 1;
149 }
150 
151 static int
152 aead_chacha20_poly1305_open(const EVP_AEAD_CTX *ctx, unsigned char *out,
153     size_t *out_len, size_t max_out_len, const unsigned char *nonce,
154     size_t nonce_len, const unsigned char *in, size_t in_len,
155     const unsigned char *ad, size_t ad_len)
156 {
157 	const struct aead_chacha20_poly1305_ctx *c20_ctx = ctx->aead_state;
158 	unsigned char mac[POLY1305_TAG_LEN];
159 	unsigned char poly1305_key[32];
160 	poly1305_state poly1305;
161 	const uint64_t in_len_64 = in_len;
162 	size_t plaintext_len;
163 
164 	if (in_len < c20_ctx->tag_len) {
165 		EVPerr(EVP_F_AEAD_CHACHA20_POLY1305_OPEN, EVP_R_BAD_DECRYPT);
166 		return 0;
167 	}
168 
169 	/* The underlying ChaCha implementation may not overflow the block
170 	 * counter into the second counter word. Therefore we disallow
171 	 * individual operations that work on more than 2TB at a time.
172 	 * in_len_64 is needed because, on 32-bit platforms, size_t is only
173 	 * 32-bits and this produces a warning because it's always false.
174 	 * Casting to uint64_t inside the conditional is not sufficient to stop
175 	 * the warning. */
176 	if (in_len_64 >= (1ULL << 32) * 64 - 64) {
177 		EVPerr(EVP_F_AEAD_CHACHA20_POLY1305_OPEN, EVP_R_TOO_LARGE);
178 		return 0;
179 	}
180 
181 	if (nonce_len != CHACHA20_NONCE_LEN) {
182 		EVPerr(EVP_F_AEAD_CHACHA20_POLY1305_OPEN, EVP_R_IV_TOO_LARGE);
183 		return 0;
184 	}
185 
186 	plaintext_len = in_len - c20_ctx->tag_len;
187 
188 	if (max_out_len < plaintext_len) {
189 		EVPerr(EVP_F_AEAD_CHACHA20_POLY1305_OPEN,
190 		    EVP_R_BUFFER_TOO_SMALL);
191 		return 0;
192 	}
193 
194 	memset(poly1305_key, 0, sizeof(poly1305_key));
195 	CRYPTO_chacha_20(poly1305_key, poly1305_key, sizeof(poly1305_key),
196 	    c20_ctx->key, nonce, 0);
197 
198 	CRYPTO_poly1305_init(&poly1305, poly1305_key);
199 	poly1305_update_with_length(&poly1305, ad, ad_len);
200 	poly1305_update_with_length(&poly1305, in, plaintext_len);
201 	CRYPTO_poly1305_finish(&poly1305, mac);
202 
203 	if (CRYPTO_memcmp(mac, in + plaintext_len, c20_ctx->tag_len) != 0) {
204 		EVPerr(EVP_F_AEAD_CHACHA20_POLY1305_OPEN, EVP_R_BAD_DECRYPT);
205 		return 0;
206 	}
207 
208 	CRYPTO_chacha_20(out, in, plaintext_len, c20_ctx->key, nonce, 1);
209 	*out_len = plaintext_len;
210 	return 1;
211 }
212 
213 static const EVP_AEAD aead_chacha20_poly1305 = {
214 	.key_len = 32,
215 	.nonce_len = CHACHA20_NONCE_LEN,
216 	.overhead = POLY1305_TAG_LEN,
217 	.max_tag_len = POLY1305_TAG_LEN,
218 
219 	.init = aead_chacha20_poly1305_init,
220 	.cleanup = aead_chacha20_poly1305_cleanup,
221 	.seal = aead_chacha20_poly1305_seal,
222 	.open = aead_chacha20_poly1305_open,
223 };
224 
225 const EVP_AEAD *
226 EVP_aead_chacha20_poly1305()
227 {
228 	return &aead_chacha20_poly1305;
229 }
230 
231 #endif  /* !OPENSSL_NO_CHACHA && !OPENSSL_NO_POLY1305 */
232