chachapoly.c revision 1.5
1/*
2 * Copyright (c) 2015 Mike Belopuhov
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17#include <sys/param.h>
18#include <sys/systm.h>
19#include <lib/libkern/libkern.h>
20
21#include <crypto/chacha_private.h>
22#include <crypto/poly1305.h>
23#include <crypto/chachapoly.h>
24
25int
26chacha20_setkey(void *sched, u_int8_t *key, int len)
27{
28	struct chacha20_ctx *ctx = (struct chacha20_ctx *)sched;
29
30	if (len != CHACHA20_KEYSIZE + CHACHA20_SALT)
31		return (-1);
32
33	/* initial counter is 1 */
34	ctx->nonce[0] = 1;
35	memcpy(ctx->nonce + CHACHA20_CTR, key + CHACHA20_KEYSIZE,
36	    CHACHA20_SALT);
37	chacha_keysetup((chacha_ctx *)&ctx->block, key, CHACHA20_KEYSIZE * 8);
38	return (0);
39}
40
41void
42chacha20_reinit(caddr_t key, u_int8_t *iv)
43{
44	struct chacha20_ctx *ctx = (struct chacha20_ctx *)key;
45
46	chacha_ivsetup((chacha_ctx *)ctx->block, iv, ctx->nonce);
47}
48
49void
50chacha20_crypt(caddr_t key, u_int8_t *data)
51{
52	struct chacha20_ctx *ctx = (struct chacha20_ctx *)key;
53
54	chacha_encrypt_bytes((chacha_ctx *)ctx->block, data, data,
55	    CHACHA20_BLOCK_LEN);
56}
57
58void
59Chacha20_Poly1305_Init(void *xctx)
60{
61	CHACHA20_POLY1305_CTX *ctx = xctx;
62
63	memset(ctx, 0, sizeof(*ctx));
64}
65
66void
67Chacha20_Poly1305_Setkey(void *xctx, const uint8_t *key, uint16_t klen)
68{
69	CHACHA20_POLY1305_CTX *ctx = xctx;
70
71	/* salt is provided with the key material */
72	memcpy(ctx->nonce + CHACHA20_CTR, key + CHACHA20_KEYSIZE,
73	    CHACHA20_SALT);
74	chacha_keysetup((chacha_ctx *)&ctx->chacha, key, CHACHA20_KEYSIZE * 8);
75}
76
77void
78Chacha20_Poly1305_Reinit(void *xctx, const uint8_t *iv, uint16_t ivlen)
79{
80	CHACHA20_POLY1305_CTX *ctx = xctx;
81
82	/* initial counter is 0 */
83	chacha_ivsetup((chacha_ctx *)&ctx->chacha, iv, ctx->nonce);
84	chacha_encrypt_bytes((chacha_ctx *)&ctx->chacha, ctx->key, ctx->key,
85	    POLY1305_KEYLEN);
86	poly1305_init((poly1305_state *)&ctx->poly, ctx->key);
87}
88
89int
90Chacha20_Poly1305_Update(void *xctx, const uint8_t *data, uint16_t len)
91{
92	static const char zeroes[POLY1305_BLOCK_LEN];
93	CHACHA20_POLY1305_CTX *ctx = xctx;
94	size_t rem;
95
96	poly1305_update((poly1305_state *)&ctx->poly, data, len);
97
98	/* number of bytes in the last 16 byte block */
99	rem = (len + POLY1305_BLOCK_LEN) & (POLY1305_BLOCK_LEN - 1);
100	if (rem > 0)
101		poly1305_update((poly1305_state *)&ctx->poly, zeroes,
102		    POLY1305_BLOCK_LEN - rem);
103	return (0);
104}
105
106void
107Chacha20_Poly1305_Final(uint8_t tag[POLY1305_TAGLEN], void *xctx)
108{
109	CHACHA20_POLY1305_CTX *ctx = xctx;
110
111	poly1305_finish((poly1305_state *)&ctx->poly, tag);
112	explicit_bzero(ctx, sizeof(*ctx));
113}
114
115static const uint8_t pad0[16] = { 0 };
116
117void
118chacha20poly1305_encrypt(
119    uint8_t *dst,
120    const uint8_t *src,
121    const size_t src_len,
122    const uint8_t *ad,
123    const size_t ad_len,
124    const uint64_t nonce,
125    const uint8_t key[CHACHA20POLY1305_KEY_SIZE]
126) {
127	poly1305_state poly1305_ctx;
128	chacha_ctx chacha_ctx;
129	union {
130		uint8_t b0[CHACHA20POLY1305_KEY_SIZE];
131		uint64_t lens[2];
132	} b = { { 0 } };
133	uint64_t le_nonce = htole64(nonce);
134
135	chacha_keysetup(&chacha_ctx, key, CHACHA20POLY1305_KEY_SIZE * 8);
136	chacha_ivsetup(&chacha_ctx, (uint8_t *) &le_nonce, NULL);
137	chacha_encrypt_bytes(&chacha_ctx, b.b0, b.b0, sizeof(b.b0));
138	poly1305_init(&poly1305_ctx, b.b0);
139
140	poly1305_update(&poly1305_ctx, ad, ad_len);
141	poly1305_update(&poly1305_ctx, pad0, (0x10 - ad_len) & 0xf);
142
143	chacha_encrypt_bytes(&chacha_ctx, (uint8_t *) src, dst, src_len);
144
145	poly1305_update(&poly1305_ctx, dst, src_len);
146	poly1305_update(&poly1305_ctx, pad0, (0x10 - src_len) & 0xf);
147
148	b.lens[0] = htole64(ad_len);
149	b.lens[1] = htole64(src_len);
150	poly1305_update(&poly1305_ctx, (uint8_t *)b.lens, sizeof(b.lens));
151
152	poly1305_finish(&poly1305_ctx, dst + src_len);
153
154	explicit_bzero(&chacha_ctx, sizeof(chacha_ctx));
155	explicit_bzero(&b, sizeof(b));
156}
157
158int
159chacha20poly1305_decrypt(
160    uint8_t *dst,
161    const uint8_t *src,
162    const size_t src_len,
163    const uint8_t *ad,
164    const size_t ad_len,
165    const uint64_t nonce,
166    const uint8_t key[CHACHA20POLY1305_KEY_SIZE]
167) {
168	poly1305_state poly1305_ctx;
169	chacha_ctx chacha_ctx;
170	int ret;
171	size_t dst_len;
172	union {
173		uint8_t b0[CHACHA20POLY1305_KEY_SIZE];
174		uint8_t mac[CHACHA20POLY1305_AUTHTAG_SIZE];
175		uint64_t lens[2];
176	} b = { { 0 } };
177	uint64_t le_nonce = htole64(nonce);
178
179	if (src_len < CHACHA20POLY1305_AUTHTAG_SIZE)
180		return 0;
181
182	chacha_keysetup(&chacha_ctx, key, CHACHA20POLY1305_KEY_SIZE * 8);
183	chacha_ivsetup(&chacha_ctx, (uint8_t *) &le_nonce, NULL);
184	chacha_encrypt_bytes(&chacha_ctx, b.b0, b.b0, sizeof(b.b0));
185	poly1305_init(&poly1305_ctx, b.b0);
186
187	poly1305_update(&poly1305_ctx, ad, ad_len);
188	poly1305_update(&poly1305_ctx, pad0, (0x10 - ad_len) & 0xf);
189
190	dst_len = src_len - CHACHA20POLY1305_AUTHTAG_SIZE;
191	poly1305_update(&poly1305_ctx, src, dst_len);
192	poly1305_update(&poly1305_ctx, pad0, (0x10 - dst_len) & 0xf);
193
194	b.lens[0] = htole64(ad_len);
195	b.lens[1] = htole64(dst_len);
196	poly1305_update(&poly1305_ctx, (uint8_t *)b.lens, sizeof(b.lens));
197
198	poly1305_finish(&poly1305_ctx, b.mac);
199
200	ret = timingsafe_bcmp(b.mac, src + dst_len, CHACHA20POLY1305_AUTHTAG_SIZE);
201	if (!ret)
202		chacha_encrypt_bytes(&chacha_ctx, (uint8_t *) src, dst, dst_len);
203
204	explicit_bzero(&chacha_ctx, sizeof(chacha_ctx));
205	explicit_bzero(&b, sizeof(b));
206
207	return !ret;
208}
209
210void
211xchacha20poly1305_encrypt(
212    uint8_t *dst,
213    const uint8_t *src,
214    const size_t src_len,
215    const uint8_t *ad,
216    const size_t ad_len,
217    const uint8_t nonce[XCHACHA20POLY1305_NONCE_SIZE],
218    const uint8_t key[CHACHA20POLY1305_KEY_SIZE]
219) {
220	int i;
221	uint32_t derived_key[CHACHA20POLY1305_KEY_SIZE / sizeof(uint32_t)];
222	uint64_t h_nonce;
223
224	memcpy(&h_nonce, nonce + 16, sizeof(h_nonce));
225	h_nonce = le64toh(h_nonce);
226	hchacha20(derived_key, nonce, key);
227
228	for(i = 0; i < (sizeof(derived_key)/sizeof(derived_key[0])); i++)
229		(derived_key[i]) = htole32((derived_key[i]));
230
231	chacha20poly1305_encrypt(dst, src, src_len, ad, ad_len,
232	    h_nonce, (uint8_t *)derived_key);
233	explicit_bzero(derived_key, CHACHA20POLY1305_KEY_SIZE);
234}
235
236int
237xchacha20poly1305_decrypt(
238    uint8_t *dst,
239    const uint8_t *src,
240    const size_t src_len,
241    const uint8_t *ad,
242    const size_t ad_len,
243    const uint8_t nonce[XCHACHA20POLY1305_NONCE_SIZE],
244    const uint8_t key[CHACHA20POLY1305_KEY_SIZE]
245) {
246	int ret, i;
247	uint32_t derived_key[CHACHA20POLY1305_KEY_SIZE / sizeof(uint32_t)];
248	uint64_t h_nonce;
249
250	memcpy(&h_nonce, nonce + 16, sizeof(h_nonce));
251	h_nonce = le64toh(h_nonce);
252	hchacha20(derived_key, nonce, key);
253	for(i = 0; i < (sizeof(derived_key)/sizeof(derived_key[0])); i++)
254		(derived_key[i]) = htole32((derived_key[i]));
255
256	ret = chacha20poly1305_decrypt(dst, src, src_len, ad, ad_len,
257	    h_nonce, (uint8_t *)derived_key);
258	explicit_bzero(derived_key, CHACHA20POLY1305_KEY_SIZE);
259
260	return ret;
261}
262