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