xref: /openbsd-src/usr.bin/ssh/cipher-chachapoly-libcrypto.c (revision 46035553bfdd96e63c94e32da0210227ec2e3cf1)
1 /*
2  * Copyright (c) 2013 Damien Miller <djm@mindrot.org>
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 /* $OpenBSD: cipher-chachapoly-libcrypto.c,v 1.1 2020/04/03 04:32:21 djm Exp $ */
18 
19 #include <sys/types.h>
20 #include <stdarg.h> /* needed for log.h */
21 #include <string.h>
22 #include <stdio.h>  /* needed for misc.h */
23 
24 #include <openssl/evp.h>
25 
26 #include "log.h"
27 #include "sshbuf.h"
28 #include "ssherr.h"
29 #include "cipher-chachapoly.h"
30 
31 struct chachapoly_ctx {
32 	EVP_CIPHER_CTX *main_evp, *header_evp;
33 };
34 
35 struct chachapoly_ctx *
36 chachapoly_new(const u_char *key, u_int keylen)
37 {
38 	struct chachapoly_ctx *ctx;
39 
40 	if (keylen != (32 + 32)) /* 2 x 256 bit keys */
41 		return NULL;
42 	if ((ctx = calloc(1, sizeof(*ctx))) == NULL)
43 		return NULL;
44 	if ((ctx->main_evp = EVP_CIPHER_CTX_new()) == NULL ||
45 	    (ctx->header_evp = EVP_CIPHER_CTX_new()) == NULL)
46 		goto fail;
47 	if (!EVP_CipherInit(ctx->main_evp, EVP_chacha20(), key, NULL, 1))
48 		goto fail;
49 	if (!EVP_CipherInit(ctx->header_evp, EVP_chacha20(), key + 32, NULL, 1))
50 		goto fail;
51 	if (EVP_CIPHER_CTX_iv_length(ctx->header_evp) != 16)
52 		goto fail;
53 	return ctx;
54  fail:
55 	chachapoly_free(ctx);
56 	return NULL;
57 }
58 
59 void
60 chachapoly_free(struct chachapoly_ctx *cpctx)
61 {
62 	if (cpctx == NULL)
63 		return;
64 	EVP_CIPHER_CTX_free(cpctx->main_evp);
65 	EVP_CIPHER_CTX_free(cpctx->header_evp);
66 	freezero(cpctx, sizeof(*cpctx));
67 }
68 
69 /*
70  * chachapoly_crypt() operates as following:
71  * En/decrypt with header key 'aadlen' bytes from 'src', storing result
72  * to 'dest'. The ciphertext here is treated as additional authenticated
73  * data for MAC calculation.
74  * En/decrypt 'len' bytes at offset 'aadlen' from 'src' to 'dest'. Use
75  * POLY1305_TAGLEN bytes at offset 'len'+'aadlen' as the authentication
76  * tag. This tag is written on encryption and verified on decryption.
77  */
78 int
79 chachapoly_crypt(struct chachapoly_ctx *ctx, u_int seqnr, u_char *dest,
80     const u_char *src, u_int len, u_int aadlen, u_int authlen, int do_encrypt)
81 {
82 	u_char seqbuf[16]; /* layout: u64 counter || u64 seqno */
83 	int r = SSH_ERR_INTERNAL_ERROR;
84 	u_char expected_tag[POLY1305_TAGLEN], poly_key[POLY1305_KEYLEN];
85 
86 	/*
87 	 * Run ChaCha20 once to generate the Poly1305 key. The IV is the
88 	 * packet sequence number.
89 	 */
90 	memset(seqbuf, 0, sizeof(seqbuf));
91 	POKE_U64(seqbuf + 8, seqnr);
92 	memset(poly_key, 0, sizeof(poly_key));
93 	if (!EVP_CipherInit(ctx->main_evp, NULL, NULL, seqbuf, 1) ||
94 	    EVP_Cipher(ctx->main_evp, poly_key,
95 	    poly_key, sizeof(poly_key)) < 0) {
96 		r = SSH_ERR_LIBCRYPTO_ERROR;
97 		goto out;
98 	}
99 
100 	/* If decrypting, check tag before anything else */
101 	if (!do_encrypt) {
102 		const u_char *tag = src + aadlen + len;
103 
104 		poly1305_auth(expected_tag, src, aadlen + len, poly_key);
105 		if (timingsafe_bcmp(expected_tag, tag, POLY1305_TAGLEN) != 0) {
106 			r = SSH_ERR_MAC_INVALID;
107 			goto out;
108 		}
109 	}
110 
111 	/* Crypt additional data */
112 	if (aadlen) {
113 		if (!EVP_CipherInit(ctx->header_evp, NULL, NULL, seqbuf, 1) ||
114 		    EVP_Cipher(ctx->header_evp, dest, src, aadlen) < 0) {
115 			r = SSH_ERR_LIBCRYPTO_ERROR;
116 			goto out;
117 		}
118 	}
119 
120 	/* Set Chacha's block counter to 1 */
121 	seqbuf[0] = 1;
122 	if (!EVP_CipherInit(ctx->main_evp, NULL, NULL, seqbuf, 1) ||
123 	    EVP_Cipher(ctx->main_evp, dest + aadlen, src + aadlen, len) < 0) {
124 		r = SSH_ERR_LIBCRYPTO_ERROR;
125 		goto out;
126 	}
127 
128 	/* If encrypting, calculate and append tag */
129 	if (do_encrypt) {
130 		poly1305_auth(dest + aadlen + len, dest, aadlen + len,
131 		    poly_key);
132 	}
133 	r = 0;
134  out:
135 	explicit_bzero(expected_tag, sizeof(expected_tag));
136 	explicit_bzero(seqbuf, sizeof(seqbuf));
137 	explicit_bzero(poly_key, sizeof(poly_key));
138 	return r;
139 }
140 
141 /* Decrypt and extract the encrypted packet length */
142 int
143 chachapoly_get_length(struct chachapoly_ctx *ctx,
144     u_int *plenp, u_int seqnr, const u_char *cp, u_int len)
145 {
146 	u_char buf[4], seqbuf[16];
147 
148 	if (len < 4)
149 		return SSH_ERR_MESSAGE_INCOMPLETE;
150 	memset(seqbuf, 0, sizeof(seqbuf));
151 	POKE_U64(seqbuf + 8, seqnr);
152 	if (!EVP_CipherInit(ctx->header_evp, NULL, NULL, seqbuf, 0))
153 		return SSH_ERR_LIBCRYPTO_ERROR;
154 	if (EVP_Cipher(ctx->header_evp, buf, (u_char *)cp, sizeof(buf)) < 0)
155 		return SSH_ERR_LIBCRYPTO_ERROR;
156 	*plenp = PEEK_U32(buf);
157 	return 0;
158 }
159