2 * Copyright 2019-2020 The OpenSSL Project Authors. All Rights Reserved.
4 * Licensed under the Apache License 2.0 (the "License"). You may not use
5 * this file except in compliance with the License. You can obtain a copy
6 * in the file LICENSE in the source distribution or at
7 * https://www.openssl.org/source/license.html
10 /* chacha20_poly1305 cipher implementation */
12 #include "internal/endian.h"
13 #include "cipher_chacha20_poly1305.h"
15 static int chacha_poly1305_tls_init(PROV_CIPHER_CTX
*bctx
,
16 unsigned char *aad
, size_t alen
)
19 PROV_CHACHA20_POLY1305_CTX
*ctx
= (PROV_CHACHA20_POLY1305_CTX
*)bctx
;
21 if (alen
!= EVP_AEAD_TLS1_AAD_LEN
)
24 memcpy(ctx
->tls_aad
, aad
, EVP_AEAD_TLS1_AAD_LEN
);
25 len
= aad
[EVP_AEAD_TLS1_AAD_LEN
- 2] << 8 | aad
[EVP_AEAD_TLS1_AAD_LEN
- 1];
28 if (len
< POLY1305_BLOCK_SIZE
)
30 len
-= POLY1305_BLOCK_SIZE
; /* discount attached tag */
31 aad
[EVP_AEAD_TLS1_AAD_LEN
- 2] = (unsigned char)(len
>> 8);
32 aad
[EVP_AEAD_TLS1_AAD_LEN
- 1] = (unsigned char)len
;
34 ctx
->tls_payload_length
= len
;
36 /* merge record sequence number as per RFC7905 */
37 ctx
->chacha
.counter
[1] = ctx
->nonce
[0];
38 ctx
->chacha
.counter
[2] = ctx
->nonce
[1] ^ CHACHA_U8TOU32(aad
);
39 ctx
->chacha
.counter
[3] = ctx
->nonce
[2] ^ CHACHA_U8TOU32(aad
+4);
42 return POLY1305_BLOCK_SIZE
; /* tag length */
45 static int chacha_poly1305_tls_iv_set_fixed(PROV_CIPHER_CTX
*bctx
,
46 unsigned char *fixed
, size_t flen
)
48 PROV_CHACHA20_POLY1305_CTX
*ctx
= (PROV_CHACHA20_POLY1305_CTX
*)bctx
;
50 if (flen
!= CHACHA20_POLY1305_IVLEN
)
52 ctx
->nonce
[0] = ctx
->chacha
.counter
[1] = CHACHA_U8TOU32(fixed
);
53 ctx
->nonce
[1] = ctx
->chacha
.counter
[2] = CHACHA_U8TOU32(fixed
+ 4);
54 ctx
->nonce
[2] = ctx
->chacha
.counter
[3] = CHACHA_U8TOU32(fixed
+ 8);
59 static int chacha20_poly1305_initkey(PROV_CIPHER_CTX
*bctx
,
60 const unsigned char *key
, size_t keylen
)
62 PROV_CHACHA20_POLY1305_CTX
*ctx
= (PROV_CHACHA20_POLY1305_CTX
*)bctx
;
68 ctx
->tls_payload_length
= NO_TLS_PAYLOAD_LENGTH
;
71 return chacha20_einit(&ctx
->chacha
, key
, keylen
, NULL
, 0);
73 return chacha20_dinit(&ctx
->chacha
, key
, keylen
, NULL
, 0);
76 static int chacha20_poly1305_initiv(PROV_CIPHER_CTX
*bctx
)
78 PROV_CHACHA20_POLY1305_CTX
*ctx
= (PROV_CHACHA20_POLY1305_CTX
*)bctx
;
79 unsigned char tempiv
[CHACHA_CTR_SIZE
] = { 0 };
86 ctx
->tls_payload_length
= NO_TLS_PAYLOAD_LENGTH
;
89 if (ctx
->nonce_len
<= CHACHA_CTR_SIZE
) {
90 memcpy(tempiv
+ CHACHA_CTR_SIZE
- ctx
->nonce_len
, bctx
->oiv
,
94 ret
= chacha20_einit(&ctx
->chacha
, NULL
, 0, tempiv
, sizeof(tempiv
));
96 ret
= chacha20_dinit(&ctx
->chacha
, NULL
, 0, tempiv
, sizeof(tempiv
));
97 ctx
->nonce
[0] = ctx
->chacha
.counter
[1];
98 ctx
->nonce
[1] = ctx
->chacha
.counter
[2];
99 ctx
->nonce
[2] = ctx
->chacha
.counter
[3];
105 #if !defined(OPENSSL_SMALL_FOOTPRINT)
107 # if defined(POLY1305_ASM) && (defined(__x86_64) || defined(__x86_64__) \
108 || defined(_M_AMD64) || defined(_M_X64))
109 # define XOR128_HELPERS
110 void *xor128_encrypt_n_pad(void *out
, const void *inp
, void *otp
, size_t len
);
111 void *xor128_decrypt_n_pad(void *out
, const void *inp
, void *otp
, size_t len
);
112 static const unsigned char zero
[4 * CHACHA_BLK_SIZE
] = { 0 };
114 static const unsigned char zero
[2 * CHACHA_BLK_SIZE
] = { 0 };
117 static int chacha20_poly1305_tls_cipher(PROV_CIPHER_CTX
*bctx
,
120 const unsigned char *in
, size_t len
)
122 PROV_CHACHA20_POLY1305_CTX
*ctx
= (PROV_CHACHA20_POLY1305_CTX
*)bctx
;
123 POLY1305
*poly
= &ctx
->poly1305
;
124 size_t tail
, tohash_len
, buf_len
, plen
= ctx
->tls_payload_length
;
125 unsigned char *buf
, *tohash
, *ctr
, storage
[sizeof(zero
) + 32];
129 buf
= storage
+ ((0 - (size_t)storage
) & 15); /* align */
130 ctr
= buf
+ CHACHA_BLK_SIZE
;
131 tohash
= buf
+ CHACHA_BLK_SIZE
- POLY1305_BLOCK_SIZE
;
133 # ifdef XOR128_HELPERS
134 if (plen
<= 3 * CHACHA_BLK_SIZE
) {
135 ctx
->chacha
.counter
[0] = 0;
136 buf_len
= (plen
+ 2 * CHACHA_BLK_SIZE
- 1) & (0 - CHACHA_BLK_SIZE
);
137 ChaCha20_ctr32(buf
, zero
, buf_len
, ctx
->chacha
.key
.d
, ctx
->chacha
.counter
);
138 Poly1305_Init(poly
, buf
);
139 ctx
->chacha
.partial_len
= 0;
140 memcpy(tohash
, ctx
->tls_aad
, POLY1305_BLOCK_SIZE
);
141 tohash_len
= POLY1305_BLOCK_SIZE
;
142 ctx
->len
.aad
= EVP_AEAD_TLS1_AAD_LEN
;
143 ctx
->len
.text
= plen
;
147 ctr
= xor128_encrypt_n_pad(out
, in
, ctr
, plen
);
149 ctr
= xor128_decrypt_n_pad(out
, in
, ctr
, plen
);
153 tohash_len
= (size_t)(ctr
- tohash
);
157 if (plen
<= CHACHA_BLK_SIZE
) {
160 ctx
->chacha
.counter
[0] = 0;
161 ChaCha20_ctr32(buf
, zero
, (buf_len
= 2 * CHACHA_BLK_SIZE
),
162 ctx
->chacha
.key
.d
, ctx
->chacha
.counter
);
163 Poly1305_Init(poly
, buf
);
164 ctx
->chacha
.partial_len
= 0;
165 memcpy(tohash
, ctx
->tls_aad
, POLY1305_BLOCK_SIZE
);
166 tohash_len
= POLY1305_BLOCK_SIZE
;
167 ctx
->len
.aad
= EVP_AEAD_TLS1_AAD_LEN
;
168 ctx
->len
.text
= plen
;
171 for (i
= 0; i
< plen
; i
++)
172 out
[i
] = ctr
[i
] ^= in
[i
];
174 for (i
= 0; i
< plen
; i
++) {
175 unsigned char c
= in
[i
];
185 tail
= (0 - i
) & (POLY1305_BLOCK_SIZE
- 1);
186 memset(ctr
+ i
, 0, tail
);
188 tohash_len
+= i
+ tail
;
192 ctx
->chacha
.counter
[0] = 0;
193 ChaCha20_ctr32(buf
, zero
, (buf_len
= CHACHA_BLK_SIZE
),
194 ctx
->chacha
.key
.d
, ctx
->chacha
.counter
);
195 Poly1305_Init(poly
, buf
);
196 ctx
->chacha
.counter
[0] = 1;
197 ctx
->chacha
.partial_len
= 0;
198 Poly1305_Update(poly
, ctx
->tls_aad
, POLY1305_BLOCK_SIZE
);
201 ctx
->len
.aad
= EVP_AEAD_TLS1_AAD_LEN
;
202 ctx
->len
.text
= plen
;
205 ChaCha20_ctr32(out
, in
, plen
, ctx
->chacha
.key
.d
, ctx
->chacha
.counter
);
206 Poly1305_Update(poly
, out
, plen
);
208 Poly1305_Update(poly
, in
, plen
);
209 ChaCha20_ctr32(out
, in
, plen
, ctx
->chacha
.key
.d
, ctx
->chacha
.counter
);
214 tail
= (0 - plen
) & (POLY1305_BLOCK_SIZE
- 1);
215 Poly1305_Update(poly
, zero
, tail
);
218 if (IS_LITTLE_ENDIAN
) {
219 memcpy(ctr
, (unsigned char *)&ctx
->len
, POLY1305_BLOCK_SIZE
);
221 ctr
[0] = (unsigned char)(ctx
->len
.aad
);
222 ctr
[1] = (unsigned char)(ctx
->len
.aad
>>8);
223 ctr
[2] = (unsigned char)(ctx
->len
.aad
>>16);
224 ctr
[3] = (unsigned char)(ctx
->len
.aad
>>24);
225 ctr
[4] = (unsigned char)(ctx
->len
.aad
>>32);
226 ctr
[5] = (unsigned char)(ctx
->len
.aad
>>40);
227 ctr
[6] = (unsigned char)(ctx
->len
.aad
>>48);
228 ctr
[7] = (unsigned char)(ctx
->len
.aad
>>56);
230 ctr
[8] = (unsigned char)(ctx
->len
.text
);
231 ctr
[9] = (unsigned char)(ctx
->len
.text
>>8);
232 ctr
[10] = (unsigned char)(ctx
->len
.text
>>16);
233 ctr
[11] = (unsigned char)(ctx
->len
.text
>>24);
234 ctr
[12] = (unsigned char)(ctx
->len
.text
>>32);
235 ctr
[13] = (unsigned char)(ctx
->len
.text
>>40);
236 ctr
[14] = (unsigned char)(ctx
->len
.text
>>48);
237 ctr
[15] = (unsigned char)(ctx
->len
.text
>>56);
239 tohash_len
+= POLY1305_BLOCK_SIZE
;
241 Poly1305_Update(poly
, tohash
, tohash_len
);
242 OPENSSL_cleanse(buf
, buf_len
);
243 Poly1305_Final(poly
, bctx
->enc
? ctx
->tag
: tohash
);
245 ctx
->tls_payload_length
= NO_TLS_PAYLOAD_LENGTH
;
248 memcpy(out
, ctx
->tag
, POLY1305_BLOCK_SIZE
);
250 if (CRYPTO_memcmp(tohash
, in
, POLY1305_BLOCK_SIZE
)) {
251 if (len
> POLY1305_BLOCK_SIZE
)
252 memset(out
- (len
- POLY1305_BLOCK_SIZE
), 0,
253 len
- POLY1305_BLOCK_SIZE
);
257 len
-= POLY1305_BLOCK_SIZE
;
264 static const unsigned char zero
[CHACHA_BLK_SIZE
] = { 0 };
265 #endif /* OPENSSL_SMALL_FOOTPRINT */
267 static int chacha20_poly1305_aead_cipher(PROV_CIPHER_CTX
*bctx
,
268 unsigned char *out
, size_t *outl
,
269 const unsigned char *in
, size_t inl
)
271 PROV_CHACHA20_POLY1305_CTX
*ctx
= (PROV_CHACHA20_POLY1305_CTX
*)bctx
;
272 POLY1305
*poly
= &ctx
->poly1305
;
273 size_t rem
, plen
= ctx
->tls_payload_length
;
279 if (!ctx
->mac_inited
) {
280 if (plen
!= NO_TLS_PAYLOAD_LENGTH
&& out
!= NULL
) {
281 if (inl
!= plen
+ POLY1305_BLOCK_SIZE
)
283 #if !defined(OPENSSL_SMALL_FOOTPRINT)
284 return chacha20_poly1305_tls_cipher(bctx
, out
, outl
, in
, inl
);
288 ctx
->chacha
.counter
[0] = 0;
289 ChaCha20_ctr32(ctx
->chacha
.buf
, zero
, CHACHA_BLK_SIZE
,
290 ctx
->chacha
.key
.d
, ctx
->chacha
.counter
);
291 Poly1305_Init(poly
, ctx
->chacha
.buf
);
292 ctx
->chacha
.counter
[0] = 1;
293 ctx
->chacha
.partial_len
= 0;
294 ctx
->len
.aad
= ctx
->len
.text
= 0;
296 if (plen
!= NO_TLS_PAYLOAD_LENGTH
) {
297 Poly1305_Update(poly
, ctx
->tls_aad
, EVP_AEAD_TLS1_AAD_LEN
);
298 ctx
->len
.aad
= EVP_AEAD_TLS1_AAD_LEN
;
303 if (in
!= NULL
) { /* aad or text */
304 if (out
== NULL
) { /* aad */
305 Poly1305_Update(poly
, in
, inl
);
309 } else { /* plain- or ciphertext */
310 if (ctx
->aad
) { /* wrap up aad */
311 if ((rem
= (size_t)ctx
->len
.aad
% POLY1305_BLOCK_SIZE
))
312 Poly1305_Update(poly
, zero
, POLY1305_BLOCK_SIZE
- rem
);
316 ctx
->tls_payload_length
= NO_TLS_PAYLOAD_LENGTH
;
317 if (plen
== NO_TLS_PAYLOAD_LENGTH
)
319 else if (inl
!= plen
+ POLY1305_BLOCK_SIZE
)
322 if (bctx
->enc
) { /* plaintext */
323 ctx
->chacha
.base
.hw
->cipher(&ctx
->chacha
.base
, out
, in
, plen
);
324 Poly1305_Update(poly
, out
, plen
);
327 ctx
->len
.text
+= plen
;
328 } else { /* ciphertext */
329 Poly1305_Update(poly
, in
, plen
);
330 ctx
->chacha
.base
.hw
->cipher(&ctx
->chacha
.base
, out
, in
, plen
);
333 ctx
->len
.text
+= plen
;
337 /* explicit final, or tls mode */
338 if (in
== NULL
|| inl
!= plen
) {
340 unsigned char temp
[POLY1305_BLOCK_SIZE
];
342 if (ctx
->aad
) { /* wrap up aad */
343 if ((rem
= (size_t)ctx
->len
.aad
% POLY1305_BLOCK_SIZE
))
344 Poly1305_Update(poly
, zero
, POLY1305_BLOCK_SIZE
- rem
);
348 if ((rem
= (size_t)ctx
->len
.text
% POLY1305_BLOCK_SIZE
))
349 Poly1305_Update(poly
, zero
, POLY1305_BLOCK_SIZE
- rem
);
351 if (IS_LITTLE_ENDIAN
) {
352 Poly1305_Update(poly
, (unsigned char *)&ctx
->len
,
353 POLY1305_BLOCK_SIZE
);
355 temp
[0] = (unsigned char)(ctx
->len
.aad
);
356 temp
[1] = (unsigned char)(ctx
->len
.aad
>>8);
357 temp
[2] = (unsigned char)(ctx
->len
.aad
>>16);
358 temp
[3] = (unsigned char)(ctx
->len
.aad
>>24);
359 temp
[4] = (unsigned char)(ctx
->len
.aad
>>32);
360 temp
[5] = (unsigned char)(ctx
->len
.aad
>>40);
361 temp
[6] = (unsigned char)(ctx
->len
.aad
>>48);
362 temp
[7] = (unsigned char)(ctx
->len
.aad
>>56);
363 temp
[8] = (unsigned char)(ctx
->len
.text
);
364 temp
[9] = (unsigned char)(ctx
->len
.text
>>8);
365 temp
[10] = (unsigned char)(ctx
->len
.text
>>16);
366 temp
[11] = (unsigned char)(ctx
->len
.text
>>24);
367 temp
[12] = (unsigned char)(ctx
->len
.text
>>32);
368 temp
[13] = (unsigned char)(ctx
->len
.text
>>40);
369 temp
[14] = (unsigned char)(ctx
->len
.text
>>48);
370 temp
[15] = (unsigned char)(ctx
->len
.text
>>56);
371 Poly1305_Update(poly
, temp
, POLY1305_BLOCK_SIZE
);
373 Poly1305_Final(poly
, bctx
->enc
? ctx
->tag
: temp
);
376 if (in
!= NULL
&& inl
!= plen
) {
378 memcpy(out
, ctx
->tag
, POLY1305_BLOCK_SIZE
);
380 if (CRYPTO_memcmp(temp
, in
, POLY1305_BLOCK_SIZE
)) {
381 memset(out
- plen
, 0, plen
);
385 inl
-= POLY1305_BLOCK_SIZE
;
388 else if (!bctx
->enc
) {
389 if (CRYPTO_memcmp(temp
, ctx
->tag
, ctx
->tag_len
))
401 static const PROV_CIPHER_HW_CHACHA20_POLY1305 chacha20poly1305_hw
=
403 { chacha20_poly1305_initkey
, NULL
},
404 chacha20_poly1305_aead_cipher
,
405 chacha20_poly1305_initiv
,
406 chacha_poly1305_tls_init
,
407 chacha_poly1305_tls_iv_set_fixed
410 const PROV_CIPHER_HW
*ossl_prov_cipher_hw_chacha20_poly1305(size_t keybits
)
412 return (PROV_CIPHER_HW
*)&chacha20poly1305_hw
;