blob: 4a2b1be225549e9a3fb6e71c4b312c6c3ad00c71 [file] [log] [blame]
/* chacha.c
*
* Copyright (C) 2006-2015 wolfSSL Inc.
*
* This file is part of wolfSSL. (formerly known as CyaSSL)
*
* wolfSSL is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* wolfSSL is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <wolfssl/wolfcrypt/settings.h>
#if defined(HAVE_CHACHA) && defined(HAVE_POLY1305)
#include <wolfssl/wolfcrypt/chacha20_poly1305.h>
#include <wolfssl/wolfcrypt/error-crypt.h>
#include <wolfssl/wolfcrypt/logging.h>
#include <wolfssl/wolfcrypt/chacha.h>
#include <wolfssl/wolfcrypt/poly1305.h>
#ifdef NO_INLINE
#include <wolfssl/wolfcrypt/misc.h>
#else
#include <wolfcrypt/src/misc.c>
#endif
#ifdef CHACHA_AEAD_TEST
#include <stdio.h>
#endif
#define CHACHA20_POLY1305_AEAD_INITIAL_COUNTER 0
#define CHACHA20_POLY1305_MAC_PADDING_ALIGNMENT 16
static void word32ToLittle64(const word32 inLittle32, byte outLittle64[8]);
static int calculateAuthTag(
const byte inAuthKey[CHACHA20_POLY1305_AEAD_KEYSIZE],
const byte* inAAD, const word32 inAADLen,
const byte *inCiphertext, const word32 inCiphertextLen,
byte outAuthTag[CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE]);
int wc_ChaCha20Poly1305_Encrypt(
const byte inKey[CHACHA20_POLY1305_AEAD_KEYSIZE],
const byte inIV[CHACHA20_POLY1305_AEAD_IV_SIZE],
const byte* inAAD, const word32 inAADLen,
const byte* inPlaintext, const word32 inPlaintextLen,
byte* outCiphertext,
byte outAuthTag[CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE])
{
int err;
byte poly1305Key[CHACHA20_POLY1305_AEAD_KEYSIZE];
ChaCha chaChaCtx;
/* Validate function arguments */
if (!inKey || !inIV ||
!inPlaintext || !inPlaintextLen ||
!outCiphertext ||
!outAuthTag)
{
return BAD_FUNC_ARG;
}
XMEMSET(poly1305Key, 0, sizeof(poly1305Key));
/* Create the Poly1305 key */
err = wc_Chacha_SetKey(&chaChaCtx, inKey, CHACHA20_POLY1305_AEAD_KEYSIZE);
if (err != 0) return err;
err = wc_Chacha_SetIV(&chaChaCtx, inIV,
CHACHA20_POLY1305_AEAD_INITIAL_COUNTER);
if (err != 0) return err;
err = wc_Chacha_Process(&chaChaCtx, poly1305Key, poly1305Key,
CHACHA20_POLY1305_AEAD_KEYSIZE);
if (err != 0) return err;
/* Encrypt the plaintext using ChaCha20 */
err = wc_Chacha_Process(&chaChaCtx, outCiphertext, inPlaintext,
inPlaintextLen);
/* Calculate the Poly1305 auth tag */
if (err == 0)
err = calculateAuthTag(poly1305Key,
inAAD, inAADLen,
outCiphertext, inPlaintextLen,
outAuthTag);
ForceZero(poly1305Key, sizeof(poly1305Key));
return err;
}
int wc_ChaCha20Poly1305_Decrypt(
const byte inKey[CHACHA20_POLY1305_AEAD_KEYSIZE],
const byte inIV[CHACHA20_POLY1305_AEAD_IV_SIZE],
const byte* inAAD, const word32 inAADLen,
const byte* inCiphertext, const word32 inCiphertextLen,
const byte inAuthTag[CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE],
byte* outPlaintext)
{
int err;
byte poly1305Key[CHACHA20_POLY1305_AEAD_KEYSIZE];
ChaCha chaChaCtx;
byte calculatedAuthTag[CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE];
/* Validate function arguments */
if (!inKey || !inIV ||
!inCiphertext || !inCiphertextLen ||
!inAuthTag ||
!outPlaintext)
{
return BAD_FUNC_ARG;
}
XMEMSET(calculatedAuthTag, 0, sizeof(calculatedAuthTag));
XMEMSET(poly1305Key, 0, sizeof(poly1305Key));
/* Create the Poly1305 key */
err = wc_Chacha_SetKey(&chaChaCtx, inKey, CHACHA20_POLY1305_AEAD_KEYSIZE);
if (err != 0) return err;
err = wc_Chacha_SetIV(&chaChaCtx, inIV,
CHACHA20_POLY1305_AEAD_INITIAL_COUNTER);
if (err != 0) return err;
err = wc_Chacha_Process(&chaChaCtx, poly1305Key, poly1305Key,
CHACHA20_POLY1305_AEAD_KEYSIZE);
if (err != 0) return err;
/* Calculate the Poly1305 auth tag */
err = calculateAuthTag(poly1305Key,
inAAD, inAADLen,
inCiphertext, inCiphertextLen,
calculatedAuthTag);
/* Compare the calculated auth tag with the received one */
if (err == 0 && ConstantCompare(inAuthTag, calculatedAuthTag,
CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE) != 0)
{
err = MAC_CMP_FAILED_E;
}
/* Decrypt the received ciphertext */
if (err == 0)
err = wc_Chacha_Process(&chaChaCtx, outPlaintext, inCiphertext,
inCiphertextLen);
ForceZero(poly1305Key, sizeof(poly1305Key));
return err;
}
static int calculateAuthTag(
const byte inAuthKey[CHACHA20_POLY1305_AEAD_KEYSIZE],
const byte *inAAD, const word32 inAADLen,
const byte *inCiphertext, const word32 inCiphertextLen,
byte outAuthTag[CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE])
{
int err;
Poly1305 poly1305Ctx;
byte padding[CHACHA20_POLY1305_MAC_PADDING_ALIGNMENT - 1];
word32 paddingLen;
byte little64[8];
XMEMSET(padding, 0, sizeof(padding));
/* Initialize Poly1305 */
err = wc_Poly1305SetKey(&poly1305Ctx, inAuthKey,
CHACHA20_POLY1305_AEAD_KEYSIZE);
if (err)
{
return err;
}
/* Create the authTag by MAC'ing the following items: */
/* -- AAD */
if (inAAD && inAADLen)
{
err = wc_Poly1305Update(&poly1305Ctx, inAAD, inAADLen);
/* -- padding1: pad the AAD to 16 bytes */
paddingLen = -inAADLen & (CHACHA20_POLY1305_MAC_PADDING_ALIGNMENT - 1);
if (paddingLen)
{
err += wc_Poly1305Update(&poly1305Ctx, padding, paddingLen);
}
if (err)
{
return err;
}
}
/* -- Ciphertext */
err = wc_Poly1305Update(&poly1305Ctx, inCiphertext, inCiphertextLen);
if (err)
{
return err;
}
/* -- padding2: pad the ciphertext to 16 bytes */
paddingLen = -inCiphertextLen &
(CHACHA20_POLY1305_MAC_PADDING_ALIGNMENT - 1);
if (paddingLen)
{
err = wc_Poly1305Update(&poly1305Ctx, padding, paddingLen);
if (err)
{
return err;
}
}
/* -- AAD length as a 64-bit little endian integer */
word32ToLittle64(inAADLen, little64);
err = wc_Poly1305Update(&poly1305Ctx, little64, sizeof(little64));
if (err)
{
return err;
}
/* -- Ciphertext length as a 64-bit little endian integer */
word32ToLittle64(inCiphertextLen, little64);
err = wc_Poly1305Update(&poly1305Ctx, little64, sizeof(little64));
if (err)
{
return err;
}
/* Finalize the auth tag */
err = wc_Poly1305Final(&poly1305Ctx, outAuthTag);
return err;
}
static void word32ToLittle64(const word32 inLittle32, byte outLittle64[8])
{
XMEMSET(outLittle64, 0, 8);
outLittle64[0] = (inLittle32 & 0x000000FF);
outLittle64[1] = (inLittle32 & 0x0000FF00) >> 8;
outLittle64[2] = (inLittle32 & 0x00FF0000) >> 16;
outLittle64[3] = (inLittle32 & 0xFF000000) >> 24;
}
#endif /* HAVE_CHACHA && HAVE_POLY1305 */