xref: /dflybsd-src/crypto/openssh/cipher-chachapoly-libcrypto.c (revision 0cbfa66cdb87e23928a110d9b02839f403e32c11)
1*0cbfa66cSDaniel Fojt /*
2*0cbfa66cSDaniel Fojt  * Copyright (c) 2013 Damien Miller <djm@mindrot.org>
3*0cbfa66cSDaniel Fojt  *
4*0cbfa66cSDaniel Fojt  * Permission to use, copy, modify, and distribute this software for any
5*0cbfa66cSDaniel Fojt  * purpose with or without fee is hereby granted, provided that the above
6*0cbfa66cSDaniel Fojt  * copyright notice and this permission notice appear in all copies.
7*0cbfa66cSDaniel Fojt  *
8*0cbfa66cSDaniel Fojt  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9*0cbfa66cSDaniel Fojt  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10*0cbfa66cSDaniel Fojt  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11*0cbfa66cSDaniel Fojt  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12*0cbfa66cSDaniel Fojt  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13*0cbfa66cSDaniel Fojt  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14*0cbfa66cSDaniel Fojt  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15*0cbfa66cSDaniel Fojt  */
16*0cbfa66cSDaniel Fojt 
17*0cbfa66cSDaniel Fojt /* $OpenBSD: cipher-chachapoly-libcrypto.c,v 1.1 2020/04/03 04:32:21 djm Exp $ */
18*0cbfa66cSDaniel Fojt 
19*0cbfa66cSDaniel Fojt #include "includes.h"
20*0cbfa66cSDaniel Fojt #ifdef WITH_OPENSSL
21*0cbfa66cSDaniel Fojt #include "openbsd-compat/openssl-compat.h"
22*0cbfa66cSDaniel Fojt #endif
23*0cbfa66cSDaniel Fojt 
24*0cbfa66cSDaniel Fojt #if defined(HAVE_EVP_CHACHA20) && !defined(HAVE_BROKEN_CHACHA20)
25*0cbfa66cSDaniel Fojt 
26*0cbfa66cSDaniel Fojt #include <sys/types.h>
27*0cbfa66cSDaniel Fojt #include <stdarg.h> /* needed for log.h */
28*0cbfa66cSDaniel Fojt #include <string.h>
29*0cbfa66cSDaniel Fojt #include <stdio.h>  /* needed for misc.h */
30*0cbfa66cSDaniel Fojt 
31*0cbfa66cSDaniel Fojt #include <openssl/evp.h>
32*0cbfa66cSDaniel Fojt 
33*0cbfa66cSDaniel Fojt #include "log.h"
34*0cbfa66cSDaniel Fojt #include "sshbuf.h"
35*0cbfa66cSDaniel Fojt #include "ssherr.h"
36*0cbfa66cSDaniel Fojt #include "cipher-chachapoly.h"
37*0cbfa66cSDaniel Fojt 
38*0cbfa66cSDaniel Fojt struct chachapoly_ctx {
39*0cbfa66cSDaniel Fojt 	EVP_CIPHER_CTX *main_evp, *header_evp;
40*0cbfa66cSDaniel Fojt };
41*0cbfa66cSDaniel Fojt 
42*0cbfa66cSDaniel Fojt struct chachapoly_ctx *
43*0cbfa66cSDaniel Fojt chachapoly_new(const u_char *key, u_int keylen)
44*0cbfa66cSDaniel Fojt {
45*0cbfa66cSDaniel Fojt 	struct chachapoly_ctx *ctx;
46*0cbfa66cSDaniel Fojt 
47*0cbfa66cSDaniel Fojt 	if (keylen != (32 + 32)) /* 2 x 256 bit keys */
48*0cbfa66cSDaniel Fojt 		return NULL;
49*0cbfa66cSDaniel Fojt 	if ((ctx = calloc(1, sizeof(*ctx))) == NULL)
50*0cbfa66cSDaniel Fojt 		return NULL;
51*0cbfa66cSDaniel Fojt 	if ((ctx->main_evp = EVP_CIPHER_CTX_new()) == NULL ||
52*0cbfa66cSDaniel Fojt 	    (ctx->header_evp = EVP_CIPHER_CTX_new()) == NULL)
53*0cbfa66cSDaniel Fojt 		goto fail;
54*0cbfa66cSDaniel Fojt 	if (!EVP_CipherInit(ctx->main_evp, EVP_chacha20(), key, NULL, 1))
55*0cbfa66cSDaniel Fojt 		goto fail;
56*0cbfa66cSDaniel Fojt 	if (!EVP_CipherInit(ctx->header_evp, EVP_chacha20(), key + 32, NULL, 1))
57*0cbfa66cSDaniel Fojt 		goto fail;
58*0cbfa66cSDaniel Fojt 	if (EVP_CIPHER_CTX_iv_length(ctx->header_evp) != 16)
59*0cbfa66cSDaniel Fojt 		goto fail;
60*0cbfa66cSDaniel Fojt 	return ctx;
61*0cbfa66cSDaniel Fojt  fail:
62*0cbfa66cSDaniel Fojt 	chachapoly_free(ctx);
63*0cbfa66cSDaniel Fojt 	return NULL;
64*0cbfa66cSDaniel Fojt }
65*0cbfa66cSDaniel Fojt 
66*0cbfa66cSDaniel Fojt void
67*0cbfa66cSDaniel Fojt chachapoly_free(struct chachapoly_ctx *cpctx)
68*0cbfa66cSDaniel Fojt {
69*0cbfa66cSDaniel Fojt 	if (cpctx == NULL)
70*0cbfa66cSDaniel Fojt 		return;
71*0cbfa66cSDaniel Fojt 	EVP_CIPHER_CTX_free(cpctx->main_evp);
72*0cbfa66cSDaniel Fojt 	EVP_CIPHER_CTX_free(cpctx->header_evp);
73*0cbfa66cSDaniel Fojt 	freezero(cpctx, sizeof(*cpctx));
74*0cbfa66cSDaniel Fojt }
75*0cbfa66cSDaniel Fojt 
76*0cbfa66cSDaniel Fojt /*
77*0cbfa66cSDaniel Fojt  * chachapoly_crypt() operates as following:
78*0cbfa66cSDaniel Fojt  * En/decrypt with header key 'aadlen' bytes from 'src', storing result
79*0cbfa66cSDaniel Fojt  * to 'dest'. The ciphertext here is treated as additional authenticated
80*0cbfa66cSDaniel Fojt  * data for MAC calculation.
81*0cbfa66cSDaniel Fojt  * En/decrypt 'len' bytes at offset 'aadlen' from 'src' to 'dest'. Use
82*0cbfa66cSDaniel Fojt  * POLY1305_TAGLEN bytes at offset 'len'+'aadlen' as the authentication
83*0cbfa66cSDaniel Fojt  * tag. This tag is written on encryption and verified on decryption.
84*0cbfa66cSDaniel Fojt  */
85*0cbfa66cSDaniel Fojt int
86*0cbfa66cSDaniel Fojt chachapoly_crypt(struct chachapoly_ctx *ctx, u_int seqnr, u_char *dest,
87*0cbfa66cSDaniel Fojt     const u_char *src, u_int len, u_int aadlen, u_int authlen, int do_encrypt)
88*0cbfa66cSDaniel Fojt {
89*0cbfa66cSDaniel Fojt 	u_char seqbuf[16]; /* layout: u64 counter || u64 seqno */
90*0cbfa66cSDaniel Fojt 	int r = SSH_ERR_INTERNAL_ERROR;
91*0cbfa66cSDaniel Fojt 	u_char expected_tag[POLY1305_TAGLEN], poly_key[POLY1305_KEYLEN];
92*0cbfa66cSDaniel Fojt 
93*0cbfa66cSDaniel Fojt 	/*
94*0cbfa66cSDaniel Fojt 	 * Run ChaCha20 once to generate the Poly1305 key. The IV is the
95*0cbfa66cSDaniel Fojt 	 * packet sequence number.
96*0cbfa66cSDaniel Fojt 	 */
97*0cbfa66cSDaniel Fojt 	memset(seqbuf, 0, sizeof(seqbuf));
98*0cbfa66cSDaniel Fojt 	POKE_U64(seqbuf + 8, seqnr);
99*0cbfa66cSDaniel Fojt 	memset(poly_key, 0, sizeof(poly_key));
100*0cbfa66cSDaniel Fojt 	if (!EVP_CipherInit(ctx->main_evp, NULL, NULL, seqbuf, 1) ||
101*0cbfa66cSDaniel Fojt 	    EVP_Cipher(ctx->main_evp, poly_key,
102*0cbfa66cSDaniel Fojt 	    poly_key, sizeof(poly_key)) < 0) {
103*0cbfa66cSDaniel Fojt 		r = SSH_ERR_LIBCRYPTO_ERROR;
104*0cbfa66cSDaniel Fojt 		goto out;
105*0cbfa66cSDaniel Fojt 	}
106*0cbfa66cSDaniel Fojt 
107*0cbfa66cSDaniel Fojt 	/* If decrypting, check tag before anything else */
108*0cbfa66cSDaniel Fojt 	if (!do_encrypt) {
109*0cbfa66cSDaniel Fojt 		const u_char *tag = src + aadlen + len;
110*0cbfa66cSDaniel Fojt 
111*0cbfa66cSDaniel Fojt 		poly1305_auth(expected_tag, src, aadlen + len, poly_key);
112*0cbfa66cSDaniel Fojt 		if (timingsafe_bcmp(expected_tag, tag, POLY1305_TAGLEN) != 0) {
113*0cbfa66cSDaniel Fojt 			r = SSH_ERR_MAC_INVALID;
114*0cbfa66cSDaniel Fojt 			goto out;
115*0cbfa66cSDaniel Fojt 		}
116*0cbfa66cSDaniel Fojt 	}
117*0cbfa66cSDaniel Fojt 
118*0cbfa66cSDaniel Fojt 	/* Crypt additional data */
119*0cbfa66cSDaniel Fojt 	if (aadlen) {
120*0cbfa66cSDaniel Fojt 		if (!EVP_CipherInit(ctx->header_evp, NULL, NULL, seqbuf, 1) ||
121*0cbfa66cSDaniel Fojt 		    EVP_Cipher(ctx->header_evp, dest, src, aadlen) < 0) {
122*0cbfa66cSDaniel Fojt 			r = SSH_ERR_LIBCRYPTO_ERROR;
123*0cbfa66cSDaniel Fojt 			goto out;
124*0cbfa66cSDaniel Fojt 		}
125*0cbfa66cSDaniel Fojt 	}
126*0cbfa66cSDaniel Fojt 
127*0cbfa66cSDaniel Fojt 	/* Set Chacha's block counter to 1 */
128*0cbfa66cSDaniel Fojt 	seqbuf[0] = 1;
129*0cbfa66cSDaniel Fojt 	if (!EVP_CipherInit(ctx->main_evp, NULL, NULL, seqbuf, 1) ||
130*0cbfa66cSDaniel Fojt 	    EVP_Cipher(ctx->main_evp, dest + aadlen, src + aadlen, len) < 0) {
131*0cbfa66cSDaniel Fojt 		r = SSH_ERR_LIBCRYPTO_ERROR;
132*0cbfa66cSDaniel Fojt 		goto out;
133*0cbfa66cSDaniel Fojt 	}
134*0cbfa66cSDaniel Fojt 
135*0cbfa66cSDaniel Fojt 	/* If encrypting, calculate and append tag */
136*0cbfa66cSDaniel Fojt 	if (do_encrypt) {
137*0cbfa66cSDaniel Fojt 		poly1305_auth(dest + aadlen + len, dest, aadlen + len,
138*0cbfa66cSDaniel Fojt 		    poly_key);
139*0cbfa66cSDaniel Fojt 	}
140*0cbfa66cSDaniel Fojt 	r = 0;
141*0cbfa66cSDaniel Fojt  out:
142*0cbfa66cSDaniel Fojt 	explicit_bzero(expected_tag, sizeof(expected_tag));
143*0cbfa66cSDaniel Fojt 	explicit_bzero(seqbuf, sizeof(seqbuf));
144*0cbfa66cSDaniel Fojt 	explicit_bzero(poly_key, sizeof(poly_key));
145*0cbfa66cSDaniel Fojt 	return r;
146*0cbfa66cSDaniel Fojt }
147*0cbfa66cSDaniel Fojt 
148*0cbfa66cSDaniel Fojt /* Decrypt and extract the encrypted packet length */
149*0cbfa66cSDaniel Fojt int
150*0cbfa66cSDaniel Fojt chachapoly_get_length(struct chachapoly_ctx *ctx,
151*0cbfa66cSDaniel Fojt     u_int *plenp, u_int seqnr, const u_char *cp, u_int len)
152*0cbfa66cSDaniel Fojt {
153*0cbfa66cSDaniel Fojt 	u_char buf[4], seqbuf[16];
154*0cbfa66cSDaniel Fojt 
155*0cbfa66cSDaniel Fojt 	if (len < 4)
156*0cbfa66cSDaniel Fojt 		return SSH_ERR_MESSAGE_INCOMPLETE;
157*0cbfa66cSDaniel Fojt 	memset(seqbuf, 0, sizeof(seqbuf));
158*0cbfa66cSDaniel Fojt 	POKE_U64(seqbuf + 8, seqnr);
159*0cbfa66cSDaniel Fojt 	if (!EVP_CipherInit(ctx->header_evp, NULL, NULL, seqbuf, 0))
160*0cbfa66cSDaniel Fojt 		return SSH_ERR_LIBCRYPTO_ERROR;
161*0cbfa66cSDaniel Fojt 	if (EVP_Cipher(ctx->header_evp, buf, (u_char *)cp, sizeof(buf)) < 0)
162*0cbfa66cSDaniel Fojt 		return SSH_ERR_LIBCRYPTO_ERROR;
163*0cbfa66cSDaniel Fojt 	*plenp = PEEK_U32(buf);
164*0cbfa66cSDaniel Fojt 	return 0;
165*0cbfa66cSDaniel Fojt }
166*0cbfa66cSDaniel Fojt #endif /* defined(HAVE_EVP_CHACHA20) && !defined(HAVE_BROKEN_CHACHA20) */
167