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