diff options
author | Damien Miller <djm@cvs.openbsd.org> | 2020-04-03 04:32:22 +0000 |
---|---|---|
committer | Damien Miller <djm@cvs.openbsd.org> | 2020-04-03 04:32:22 +0000 |
commit | 335eafbcc65e4d1b61d8d378de257388f51dabd1 (patch) | |
tree | 6c3dd07197ee12eb2f5f3e65ab9f687ed137214e /usr.bin/ssh | |
parent | 3169d428596054a0ce426a56f9036181faf2d5f0 (diff) |
chacha20-poly1305 AEAD using libcrypto EVP_chacha20
Based on patch from Yuriy M. Kaminskiy. ok + lots of assistance along the
way at a2k20 tb@
Diffstat (limited to 'usr.bin/ssh')
-rw-r--r-- | usr.bin/ssh/Makefile.inc | 5 | ||||
-rw-r--r-- | usr.bin/ssh/cipher-chachapoly-libcrypto.c | 158 |
2 files changed, 161 insertions, 2 deletions
diff --git a/usr.bin/ssh/Makefile.inc b/usr.bin/ssh/Makefile.inc index 6b2b9924a16..0bcd5aaeda0 100644 --- a/usr.bin/ssh/Makefile.inc +++ b/usr.bin/ssh/Makefile.inc @@ -1,4 +1,4 @@ -# $OpenBSD: Makefile.inc,v 1.82 2020/01/25 23:02:13 djm Exp $ +# $OpenBSD: Makefile.inc,v 1.83 2020/04/03 04:32:21 djm Exp $ .include <bsd.own.mk> @@ -73,7 +73,6 @@ SRCS_KEX+= sntrup4591761.c SRCS_KEY+= sshkey.c SRCS_KEY+= cipher.c -SRCS_KEY+= cipher-chachapoly.c SRCS_KEY+= chacha.c SRCS_KEY+= poly1305.c .if (${OPENSSL:L} == "yes") @@ -83,10 +82,12 @@ SRCS_KEY+= ssh-ecdsa-sk.c SRCS_KEY+= ssh-rsa.c SRCS_KEY+= sshbuf-getput-crypto.c SRCS_KEY+= digest-openssl.c +SRCS_KEY+= cipher-chachapoly-libcrypto.c .else SRCS_KEY+= cipher-aesctr.c SRCS_KEY+= rijndael.c SRCS_KEY+= digest-libc.c +SRCS_KEY+= cipher-chachapoly.c .endif SRCS_KEY+= ssh-ed25519.c SRCS_KEY+= ssh-ed25519-sk.c diff --git a/usr.bin/ssh/cipher-chachapoly-libcrypto.c b/usr.bin/ssh/cipher-chachapoly-libcrypto.c new file mode 100644 index 00000000000..d289cf23d8b --- /dev/null +++ b/usr.bin/ssh/cipher-chachapoly-libcrypto.c @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2013 Damien Miller <djm@mindrot.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* $OpenBSD: cipher-chachapoly-libcrypto.c,v 1.1 2020/04/03 04:32:21 djm Exp $ */ + +#include <sys/types.h> +#include <stdarg.h> /* needed for log.h */ +#include <string.h> +#include <stdio.h> /* needed for misc.h */ + +#include <openssl/evp.h> + +#include "log.h" +#include "sshbuf.h" +#include "ssherr.h" +#include "cipher-chachapoly.h" + +struct chachapoly_ctx { + EVP_CIPHER_CTX *main_evp, *header_evp; +}; + +struct chachapoly_ctx * +chachapoly_new(const u_char *key, u_int keylen) +{ + struct chachapoly_ctx *ctx; + + if (keylen != (32 + 32)) /* 2 x 256 bit keys */ + return NULL; + if ((ctx = calloc(1, sizeof(*ctx))) == NULL) + return NULL; + if ((ctx->main_evp = EVP_CIPHER_CTX_new()) == NULL || + (ctx->header_evp = EVP_CIPHER_CTX_new()) == NULL) + goto fail; + if (!EVP_CipherInit(ctx->main_evp, EVP_chacha20(), key, NULL, 1)) + goto fail; + if (!EVP_CipherInit(ctx->header_evp, EVP_chacha20(), key + 32, NULL, 1)) + goto fail; + if (EVP_CIPHER_CTX_iv_length(ctx->header_evp) != 16) + goto fail; + return ctx; + fail: + chachapoly_free(ctx); + return NULL; +} + +void +chachapoly_free(struct chachapoly_ctx *cpctx) +{ + if (cpctx == NULL) + return; + EVP_CIPHER_CTX_free(cpctx->main_evp); + EVP_CIPHER_CTX_free(cpctx->header_evp); + freezero(cpctx, sizeof(*cpctx)); +} + +/* + * chachapoly_crypt() operates as following: + * En/decrypt with header key 'aadlen' bytes from 'src', storing result + * to 'dest'. The ciphertext here is treated as additional authenticated + * data for MAC calculation. + * En/decrypt 'len' bytes at offset 'aadlen' from 'src' to 'dest'. Use + * POLY1305_TAGLEN bytes at offset 'len'+'aadlen' as the authentication + * tag. This tag is written on encryption and verified on decryption. + */ +int +chachapoly_crypt(struct chachapoly_ctx *ctx, u_int seqnr, u_char *dest, + const u_char *src, u_int len, u_int aadlen, u_int authlen, int do_encrypt) +{ + u_char seqbuf[16]; /* layout: u64 counter || u64 seqno */ + int r = SSH_ERR_INTERNAL_ERROR; + u_char expected_tag[POLY1305_TAGLEN], poly_key[POLY1305_KEYLEN]; + + /* + * Run ChaCha20 once to generate the Poly1305 key. The IV is the + * packet sequence number. + */ + memset(seqbuf, 0, sizeof(seqbuf)); + POKE_U64(seqbuf + 8, seqnr); + memset(poly_key, 0, sizeof(poly_key)); + if (!EVP_CipherInit(ctx->main_evp, NULL, NULL, seqbuf, 1) || + EVP_Cipher(ctx->main_evp, poly_key, + poly_key, sizeof(poly_key)) < 0) { + r = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + + /* If decrypting, check tag before anything else */ + if (!do_encrypt) { + const u_char *tag = src + aadlen + len; + + poly1305_auth(expected_tag, src, aadlen + len, poly_key); + if (timingsafe_bcmp(expected_tag, tag, POLY1305_TAGLEN) != 0) { + r = SSH_ERR_MAC_INVALID; + goto out; + } + } + + /* Crypt additional data */ + if (aadlen) { + if (!EVP_CipherInit(ctx->header_evp, NULL, NULL, seqbuf, 1) || + EVP_Cipher(ctx->header_evp, dest, src, aadlen) < 0) { + r = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + } + + /* Set Chacha's block counter to 1 */ + seqbuf[0] = 1; + if (!EVP_CipherInit(ctx->main_evp, NULL, NULL, seqbuf, 1) || + EVP_Cipher(ctx->main_evp, dest + aadlen, src + aadlen, len) < 0) { + r = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + + /* If encrypting, calculate and append tag */ + if (do_encrypt) { + poly1305_auth(dest + aadlen + len, dest, aadlen + len, + poly_key); + } + r = 0; + out: + explicit_bzero(expected_tag, sizeof(expected_tag)); + explicit_bzero(seqbuf, sizeof(seqbuf)); + explicit_bzero(poly_key, sizeof(poly_key)); + return r; +} + +/* Decrypt and extract the encrypted packet length */ +int +chachapoly_get_length(struct chachapoly_ctx *ctx, + u_int *plenp, u_int seqnr, const u_char *cp, u_int len) +{ + u_char buf[4], seqbuf[16]; + + if (len < 4) + return SSH_ERR_MESSAGE_INCOMPLETE; + memset(seqbuf, 0, sizeof(seqbuf)); + POKE_U64(seqbuf + 8, seqnr); + if (!EVP_CipherInit(ctx->header_evp, NULL, NULL, seqbuf, 0)) + return SSH_ERR_LIBCRYPTO_ERROR; + if (EVP_Cipher(ctx->header_evp, buf, (u_char *)cp, sizeof(buf)) < 0) + return SSH_ERR_LIBCRYPTO_ERROR; + *plenp = PEEK_U32(buf); + return 0; +} |