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