1258343Sdes/* $NetBSD: cipher-chachapoly.c,v 1.7 2023/10/25 20:19:57 christos Exp $ */ 276259Sgreen/* $OpenBSD: cipher-chachapoly.c,v 1.10 2023/07/17 05:26:38 djm Exp $ */ 376259Sgreen 492555Sdes/* 576259Sgreen * Copyright (c) 2013 Damien Miller <djm@mindrot.org> 676259Sgreen * 776259Sgreen * Permission to use, copy, modify, and distribute this software for any 876259Sgreen * purpose with or without fee is hereby granted, provided that the above 976259Sgreen * copyright notice and this permission notice appear in all copies. 1076259Sgreen * 1176259Sgreen * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 1276259Sgreen * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 1376259Sgreen * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 1476259Sgreen * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 1576259Sgreen * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 1676259Sgreen * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 1776259Sgreen * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 1876259Sgreen */ 1976259Sgreen 2076259Sgreen#include "includes.h" 2176259Sgreen__RCSID("$NetBSD: cipher-chachapoly.c,v 1.7 2023/10/25 20:19:57 christos Exp $"); 2276259Sgreen 2376259Sgreen#include <sys/types.h> 2476259Sgreen#include <stdarg.h> /* needed for log.h */ 2576259Sgreen#include <string.h> 26162856Sdes#include <stdio.h> /* needed for misc.h */ 2776259Sgreen 2876259Sgreen#include "log.h" 29162856Sdes#include "sshbuf.h" 30162856Sdes#include "ssherr.h" 31162856Sdes#include "cipher-chachapoly.h" 32162856Sdes 33162856Sdesstruct chachapoly_ctx { 34162856Sdes struct chacha_ctx main_ctx, header_ctx; 35162856Sdes}; 3676259Sgreen 37162856Sdesstruct chachapoly_ctx * 38162856Sdeschachapoly_new(const u_char *key, u_int keylen) 3976259Sgreen{ 4092555Sdes struct chachapoly_ctx *ctx; 4176259Sgreen 4276259Sgreen if (keylen != (32 + 32)) /* 2 x 256 bit keys */ 4376259Sgreen return NULL; 44147005Sdes if ((ctx = calloc(1, sizeof(*ctx))) == NULL) 4576259Sgreen return NULL; 46147005Sdes chacha_keysetup(&ctx->main_ctx, key, 256); 47147005Sdes chacha_keysetup(&ctx->header_ctx, key + 32, 256); 48147005Sdes return ctx; 4992555Sdes} 5092555Sdes 5192555Sdesvoid 5276259Sgreenchachapoly_free(struct chachapoly_ctx *cpctx) 5392555Sdes{ 5492555Sdes freezero(cpctx, sizeof(*cpctx)); 5598941Sdes} 5699052Sdes 57124211Sdes/* 5899052Sdes * chachapoly_crypt() operates as following: 5998941Sdes * En/decrypt with header key 'aadlen' bytes from 'src', storing result 6092555Sdes * to 'dest'. The ciphertext here is treated as additional authenticated 6192555Sdes * data for MAC calculation. 6298941Sdes * En/decrypt 'len' bytes at offset 'aadlen' from 'src' to 'dest'. Use 6392555Sdes * POLY1305_TAGLEN bytes at offset 'len'+'aadlen' as the authentication 6492555Sdes * tag. This tag is written on encryption and verified on decryption. 6592555Sdes */ 6692555Sdesint 6798941Sdeschachapoly_crypt(struct chachapoly_ctx *ctx, u_int seqnr, u_char *dest, 6899052Sdes const u_char *src, u_int len, u_int aadlen, u_int authlen, int do_encrypt) 69124211Sdes{ 7099052Sdes u_char seqbuf[8]; 7198941Sdes const u_char one[8] = { 1, 0, 0, 0, 0, 0, 0, 0 }; /* NB little-endian */ 7292555Sdes u_char expected_tag[POLY1305_TAGLEN], poly_key[POLY1305_KEYLEN]; 7392555Sdes int r = SSH_ERR_INTERNAL_ERROR; 7498941Sdes 7592555Sdes /* 7692555Sdes * Run ChaCha20 once to generate the Poly1305 key. The IV is the 7792555Sdes * packet sequence number. 7892555Sdes */ 7992555Sdes memset(poly_key, 0, sizeof(poly_key)); 8092555Sdes POKE_U64(seqbuf, seqnr); 8192555Sdes chacha_ivsetup(&ctx->main_ctx, seqbuf, NULL); 8292555Sdes chacha_encrypt_bytes(&ctx->main_ctx, 8392555Sdes poly_key, poly_key, sizeof(poly_key)); 8499063Sdes 8592555Sdes /* If decrypting, check tag before anything else */ 8692555Sdes if (!do_encrypt) { 87147005Sdes const u_char *tag = src + aadlen + len; 88147005Sdes 89147005Sdes poly1305_auth(expected_tag, src, aadlen + len, poly_key); 90147005Sdes if (timingsafe_bcmp(expected_tag, tag, POLY1305_TAGLEN) != 0) { 91147005Sdes r = SSH_ERR_MAC_INVALID; 92147005Sdes goto out; 93147005Sdes } 94147005Sdes } 95147005Sdes 96147005Sdes /* Crypt additional data */ 97147005Sdes if (aadlen) { 98147005Sdes chacha_ivsetup(&ctx->header_ctx, seqbuf, NULL); 99147005Sdes chacha_encrypt_bytes(&ctx->header_ctx, src, dest, aadlen); 100147005Sdes } 101147005Sdes 10292555Sdes /* Set Chacha's block counter to 1 */ 10392555Sdes chacha_ivsetup(&ctx->main_ctx, seqbuf, one); 10492555Sdes chacha_encrypt_bytes(&ctx->main_ctx, src + aadlen, 10592555Sdes dest + aadlen, len); 10692555Sdes 10792555Sdes /* If encrypting, calculate and append tag */ 10892555Sdes if (do_encrypt) { 109147005Sdes poly1305_auth(dest + aadlen + len, dest, aadlen + len, 110147005Sdes poly_key); 111147005Sdes } 112147005Sdes r = 0; 113147005Sdes out: 114258343Sdes explicit_bzero(expected_tag, sizeof(expected_tag)); 11592555Sdes explicit_bzero(seqbuf, sizeof(seqbuf)); 11692555Sdes explicit_bzero(poly_key, sizeof(poly_key)); 11792555Sdes return r; 11892555Sdes} 11992555Sdes 12092555Sdes/* Decrypt and extract the encrypted packet length */ 12192555Sdesint 12292555Sdeschachapoly_get_length(struct chachapoly_ctx *ctx, 12392555Sdes u_int *plenp, u_int seqnr, const u_char *cp, u_int len) 12492555Sdes{ 12592555Sdes u_char buf[4], seqbuf[8]; 12692555Sdes 12792555Sdes if (len < 4) 12892555Sdes return SSH_ERR_MESSAGE_INCOMPLETE; 12992555Sdes POKE_U64(seqbuf, seqnr); 13092555Sdes chacha_ivsetup(&ctx->header_ctx, seqbuf, NULL); 13192555Sdes chacha_encrypt_bytes(&ctx->header_ctx, cp, buf, 4); 13299063Sdes *plenp = PEEK_U32(buf); 13392555Sdes return 0; 13492555Sdes} 13592555Sdes