1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2020 Netflix Inc.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27 
28 #include <opencrypto/xform_auth.h>
29 #include <opencrypto/xform_enc.h>
30 
31 #include <sodium/crypto_core_hchacha20.h>
32 #include <sodium/crypto_onetimeauth_poly1305.h>
33 #include <sodium/crypto_stream_chacha20.h>
34 
35 struct chacha20_poly1305_ctx {
36 	struct crypto_onetimeauth_poly1305_state auth;
37 	const void *key;
38 	uint32_t ic;
39 	bool ietf;
40 	char nonce[CHACHA20_POLY1305_IV_LEN];
41 };
42 
43 struct xchacha20_poly1305_ctx {
44 	struct chacha20_poly1305_ctx base_ctx;	/* must be first */
45 	const void *key;
46 	char derived_key[CHACHA20_POLY1305_KEY];
47 };
48 
49 static int
50 chacha20_poly1305_setkey(void *vctx, const uint8_t *key, int len)
51 {
52 	struct chacha20_poly1305_ctx *ctx = vctx;
53 
54 	if (len != CHACHA20_POLY1305_KEY)
55 		return (EINVAL);
56 
57 	ctx->key = key;
58 	return (0);
59 }
60 
61 static void
62 chacha20_poly1305_reinit(void *vctx, const uint8_t *iv, size_t ivlen)
63 {
64 	struct chacha20_poly1305_ctx *ctx = vctx;
65 	char block[CHACHA20_NATIVE_BLOCK_LEN];
66 
67 	KASSERT(ivlen == 8 || ivlen == sizeof(ctx->nonce),
68 	    ("%s: invalid nonce length", __func__));
69 
70 	memcpy(ctx->nonce, iv, ivlen);
71 	ctx->ietf = (ivlen == CHACHA20_POLY1305_IV_LEN);
72 
73 	/* Block 0 is used for the poly1305 key. */
74 	if (ctx->ietf)
75 		crypto_stream_chacha20_ietf(block, sizeof(block), iv, ctx->key);
76 	else
77 		crypto_stream_chacha20(block, sizeof(block), iv, ctx->key);
78 	crypto_onetimeauth_poly1305_init(&ctx->auth, block);
79 	explicit_bzero(block, sizeof(block));
80 
81 	/* Start with block 1 for ciphertext. */
82 	ctx->ic = 1;
83 }
84 
85 static void
86 chacha20_poly1305_crypt(void *vctx, const uint8_t *in, uint8_t *out)
87 {
88 	struct chacha20_poly1305_ctx *ctx = vctx;
89 	int error __diagused;
90 
91 	if (ctx->ietf)
92 		error = crypto_stream_chacha20_ietf_xor_ic(out, in,
93 		    CHACHA20_NATIVE_BLOCK_LEN, ctx->nonce, ctx->ic, ctx->key);
94 	else
95 		error = crypto_stream_chacha20_xor_ic(out, in,
96 		    CHACHA20_NATIVE_BLOCK_LEN, ctx->nonce, ctx->ic, ctx->key);
97 	KASSERT(error == 0, ("%s failed: %d", __func__, error));
98 	ctx->ic++;
99 }
100 
101 static void
102 chacha20_poly1305_crypt_multi(void *vctx, const uint8_t *in, uint8_t *out, size_t len)
103 {
104 	struct chacha20_poly1305_ctx *ctx = vctx;
105 	int error __diagused;
106 
107 	KASSERT(len % CHACHA20_NATIVE_BLOCK_LEN == 0, ("%s: invalid length",
108 	    __func__));
109 	if (ctx->ietf)
110 		error = crypto_stream_chacha20_ietf_xor_ic(out, in, len,
111 		    ctx->nonce, ctx->ic, ctx->key);
112 	else
113 		error = crypto_stream_chacha20_xor_ic(out, in, len, ctx->nonce,
114 		    ctx->ic, ctx->key);
115 	KASSERT(error == 0, ("%s failed: %d", __func__, error));
116 	ctx->ic += len / CHACHA20_NATIVE_BLOCK_LEN;
117 }
118 
119 static void
120 chacha20_poly1305_crypt_last(void *vctx, const uint8_t *in, uint8_t *out,
121     size_t len)
122 {
123 	struct chacha20_poly1305_ctx *ctx = vctx;
124 
125 	int error __diagused;
126 
127 	if (ctx->ietf)
128 		error = crypto_stream_chacha20_ietf_xor_ic(out, in, len,
129 		    ctx->nonce, ctx->ic, ctx->key);
130 	else
131 		error = crypto_stream_chacha20_xor_ic(out, in, len, ctx->nonce,
132 		    ctx->ic, ctx->key);
133 	KASSERT(error == 0, ("%s failed: %d", __func__, error));
134 }
135 
136 static int
137 chacha20_poly1305_update(void *vctx, const void *data, u_int len)
138 {
139 	struct chacha20_poly1305_ctx *ctx = vctx;
140 
141 	crypto_onetimeauth_poly1305_update(&ctx->auth, data, len);
142 	return (0);
143 }
144 
145 static void
146 chacha20_poly1305_final(uint8_t *digest, void *vctx)
147 {
148 	struct chacha20_poly1305_ctx *ctx = vctx;
149 
150 	crypto_onetimeauth_poly1305_final(&ctx->auth, digest);
151 }
152 
153 const struct enc_xform enc_xform_chacha20_poly1305 = {
154 	.type = CRYPTO_CHACHA20_POLY1305,
155 	.name = "ChaCha20-Poly1305",
156 	.ctxsize = sizeof(struct chacha20_poly1305_ctx),
157 	.blocksize = 1,
158 	.native_blocksize = CHACHA20_NATIVE_BLOCK_LEN,
159 	.ivsize = CHACHA20_POLY1305_IV_LEN,
160 	.minkey = CHACHA20_POLY1305_KEY,
161 	.maxkey = CHACHA20_POLY1305_KEY,
162 	.macsize = POLY1305_HASH_LEN,
163 	.setkey = chacha20_poly1305_setkey,
164 	.reinit = chacha20_poly1305_reinit,
165 	.encrypt = chacha20_poly1305_crypt,
166 	.decrypt = chacha20_poly1305_crypt,
167 	.encrypt_multi = chacha20_poly1305_crypt_multi,
168 	.decrypt_multi = chacha20_poly1305_crypt_multi,
169 	.encrypt_last = chacha20_poly1305_crypt_last,
170 	.decrypt_last = chacha20_poly1305_crypt_last,
171 	.update = chacha20_poly1305_update,
172 	.final = chacha20_poly1305_final,
173 };
174 
175 static int
176 xchacha20_poly1305_setkey(void *vctx, const uint8_t *key, int len)
177 {
178 	struct xchacha20_poly1305_ctx *ctx = vctx;
179 
180 	if (len != XCHACHA20_POLY1305_KEY)
181 		return (EINVAL);
182 
183 	ctx->key = key;
184 	ctx->base_ctx.key = ctx->derived_key;
185 	return (0);
186 }
187 
188 static void
189 xchacha20_poly1305_reinit(void *vctx, const uint8_t *iv, size_t ivlen)
190 {
191 	struct xchacha20_poly1305_ctx *ctx = vctx;
192 	char nonce[CHACHA20_POLY1305_IV_LEN];
193 
194 	KASSERT(ivlen == XCHACHA20_POLY1305_IV_LEN,
195 	    ("%s: invalid nonce length", __func__));
196 
197 	/*
198 	 * Use HChaCha20 to derive the internal key used for
199 	 * ChaCha20-Poly1305.
200 	 */
201 	crypto_core_hchacha20(ctx->derived_key, iv, ctx->key, NULL);
202 
203 	memset(nonce, 0, 4);
204 	memcpy(nonce + 4, iv + crypto_core_hchacha20_INPUTBYTES,
205 	    sizeof(nonce) - 4);
206 	chacha20_poly1305_reinit(&ctx->base_ctx, nonce, sizeof(nonce));
207 	explicit_bzero(nonce, sizeof(nonce));
208 }
209 
210 const struct enc_xform enc_xform_xchacha20_poly1305 = {
211 	.type = CRYPTO_XCHACHA20_POLY1305,
212 	.name = "XChaCha20-Poly1305",
213 	.ctxsize = sizeof(struct xchacha20_poly1305_ctx),
214 	.blocksize = 1,
215 	.native_blocksize = CHACHA20_NATIVE_BLOCK_LEN,
216 	.ivsize = XCHACHA20_POLY1305_IV_LEN,
217 	.minkey = XCHACHA20_POLY1305_KEY,
218 	.maxkey = XCHACHA20_POLY1305_KEY,
219 	.macsize = POLY1305_HASH_LEN,
220 	.setkey = xchacha20_poly1305_setkey,
221 	.reinit = xchacha20_poly1305_reinit,
222 	.encrypt = chacha20_poly1305_crypt,
223 	.decrypt = chacha20_poly1305_crypt,
224 	.encrypt_multi = chacha20_poly1305_crypt_multi,
225 	.decrypt_multi = chacha20_poly1305_crypt_multi,
226 	.encrypt_last = chacha20_poly1305_crypt_last,
227 	.decrypt_last = chacha20_poly1305_crypt_last,
228 	.update = chacha20_poly1305_update,
229 	.final = chacha20_poly1305_final,
230 };
231