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