mirror of https://github.com/wolfSSL/wolfssl.git
522 lines
15 KiB
C
522 lines
15 KiB
C
/* ascon.c
|
|
*
|
|
* Copyright (C) 2006-2025 wolfSSL Inc.
|
|
*
|
|
* This file is part of wolfSSL.
|
|
*
|
|
* 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-1335, USA
|
|
*/
|
|
|
|
#include <wolfssl/wolfcrypt/libwolfssl_sources.h>
|
|
|
|
#ifdef HAVE_ASCON
|
|
|
|
#include <wolfssl/wolfcrypt/ascon.h>
|
|
#ifdef NO_INLINE
|
|
#include <wolfssl/wolfcrypt/misc.h>
|
|
#else
|
|
#define WOLFSSL_MISC_INCLUDED
|
|
#include <wolfcrypt/src/misc.c>
|
|
#endif
|
|
|
|
/*
|
|
* Implementation of the ASCON AEAD and HASH algorithms. Based on the NIST
|
|
* Initial Public Draft "NIST SP 800-232 ipd" and reference implementation found
|
|
* at https://github.com/ascon/ascon-c.
|
|
*/
|
|
|
|
/*
|
|
* TODO
|
|
* - Add support for big-endian systems
|
|
* - Add support for 32-bit and smaller systems */
|
|
|
|
#ifndef WORD64_AVAILABLE
|
|
#error "Ascon implementation requires a 64-bit word"
|
|
#endif
|
|
|
|
/* Data block size in bytes */
|
|
#define ASCON_HASH256_RATE 8
|
|
#define ASCON_HASH256_ROUNDS 12
|
|
#define ASCON_HASH256_IV 0x0000080100CC0002ULL
|
|
|
|
#define ASCON_AEAD128_ROUNDS_PA 12
|
|
#define ASCON_AEAD128_ROUNDS_PB 8
|
|
#define ASCON_AEAD128_IV 0x00001000808C0001ULL
|
|
#define ASCON_AEAD128_RATE 16
|
|
|
|
#define MAX_ROUNDS 12
|
|
|
|
#ifndef WOLFSSL_ASCON_UNROLL
|
|
|
|
/* Table 5 */
|
|
static const byte round_constants[MAX_ROUNDS] = {
|
|
0xf0, 0xe1, 0xd2, 0xc3, 0xb4, 0xa5, 0x96, 0x87, 0x78, 0x69, 0x5a, 0x4b
|
|
};
|
|
|
|
static byte start_index(byte rounds)
|
|
{
|
|
switch (rounds) {
|
|
case 8:
|
|
return 4;
|
|
case 12:
|
|
return 0;
|
|
default:
|
|
WOLFSSL_MSG("Something went wrong in wolfCrypt logic. Wrong ASCON "
|
|
"rounds value.");
|
|
return MAX_ROUNDS;
|
|
}
|
|
}
|
|
|
|
static WC_INLINE void ascon_round(AsconState* a, byte round)
|
|
{
|
|
word64 tmp0, tmp1, tmp2, tmp3, tmp4;
|
|
/* 3.2 Constant-Addition Layer */
|
|
a->s64[2] ^= round_constants[round];
|
|
/* 3.3 Substitution Layer */
|
|
a->s64[0] ^= a->s64[4];
|
|
a->s64[4] ^= a->s64[3];
|
|
a->s64[2] ^= a->s64[1];
|
|
tmp0 = a->s64[0] ^ (~a->s64[1] & a->s64[2]);
|
|
tmp2 = a->s64[2] ^ (~a->s64[3] & a->s64[4]);
|
|
tmp4 = a->s64[4] ^ (~a->s64[0] & a->s64[1]);
|
|
tmp1 = a->s64[1] ^ (~a->s64[2] & a->s64[3]);
|
|
tmp3 = a->s64[3] ^ (~a->s64[4] & a->s64[0]);
|
|
tmp1 ^= tmp0;
|
|
tmp3 ^= tmp2;
|
|
tmp0 ^= tmp4;
|
|
tmp2 = ~tmp2;
|
|
/* 3.4 Linear Diffusion Layer */
|
|
a->s64[4] = tmp4 ^ rotrFixed64(tmp4, 7) ^ rotrFixed64(tmp4, 41);
|
|
a->s64[1] = tmp1 ^ rotrFixed64(tmp1, 61) ^ rotrFixed64(tmp1, 39);
|
|
a->s64[3] = tmp3 ^ rotrFixed64(tmp3, 10) ^ rotrFixed64(tmp3, 17);
|
|
a->s64[0] = tmp0 ^ rotrFixed64(tmp0, 19) ^ rotrFixed64(tmp0, 28);
|
|
a->s64[2] = tmp2 ^ rotrFixed64(tmp2, 1) ^ rotrFixed64(tmp2, 6);
|
|
}
|
|
|
|
static void permutation(AsconState* a, byte rounds)
|
|
{
|
|
byte i = start_index(rounds);
|
|
for (; i < MAX_ROUNDS; i++) {
|
|
ascon_round(a, i);
|
|
}
|
|
}
|
|
|
|
#else
|
|
|
|
#define p(a, c) do { \
|
|
word64 tmp0, tmp1, tmp2, tmp3, tmp4; \
|
|
/* 3.2 Constant-Addition Layer */ \
|
|
(a)->s64[2] ^= c; \
|
|
/* 3.3 Substitution Layer */ \
|
|
(a)->s64[0] ^= (a)->s64[4]; \
|
|
(a)->s64[4] ^= (a)->s64[3]; \
|
|
(a)->s64[2] ^= (a)->s64[1]; \
|
|
tmp0 = (a)->s64[0] ^ (~(a)->s64[1] & (a)->s64[2]); \
|
|
tmp2 = (a)->s64[2] ^ (~(a)->s64[3] & (a)->s64[4]); \
|
|
tmp4 = (a)->s64[4] ^ (~(a)->s64[0] & (a)->s64[1]); \
|
|
tmp1 = (a)->s64[1] ^ (~(a)->s64[2] & (a)->s64[3]); \
|
|
tmp3 = (a)->s64[3] ^ (~(a)->s64[4] & (a)->s64[0]); \
|
|
tmp1 ^= tmp0; \
|
|
tmp3 ^= tmp2; \
|
|
tmp0 ^= tmp4; \
|
|
tmp2 = ~tmp2; \
|
|
/* 3.4 Linear Diffusion Layer */ \
|
|
(a)->s64[4] = tmp4 ^ rotrFixed64(tmp4, 7) ^ rotrFixed64(tmp4, 41); \
|
|
(a)->s64[1] = tmp1 ^ rotrFixed64(tmp1, 61) ^ rotrFixed64(tmp1, 39); \
|
|
(a)->s64[3] = tmp3 ^ rotrFixed64(tmp3, 10) ^ rotrFixed64(tmp3, 17); \
|
|
(a)->s64[0] = tmp0 ^ rotrFixed64(tmp0, 19) ^ rotrFixed64(tmp0, 28); \
|
|
(a)->s64[2] = tmp2 ^ rotrFixed64(tmp2, 1) ^ rotrFixed64(tmp2, 6); \
|
|
} while (0)
|
|
|
|
#define p8(a) \
|
|
p(a, 0xb4); \
|
|
p(a, 0xa5); \
|
|
p(a, 0x96); \
|
|
p(a, 0x87); \
|
|
p(a, 0x78); \
|
|
p(a, 0x69); \
|
|
p(a, 0x5a); \
|
|
p(a, 0x4b)
|
|
|
|
#define p12(a) \
|
|
p(a, 0xf0); \
|
|
p(a, 0xe1); \
|
|
p(a, 0xd2); \
|
|
p(a, 0xc3); \
|
|
p8(a)
|
|
|
|
/* Needed layer to evaluate the macro values */
|
|
#define _permutation(a, rounds) \
|
|
p ## rounds(a)
|
|
|
|
#define permutation(a, rounds) \
|
|
_permutation(a, rounds)
|
|
|
|
#endif
|
|
|
|
/* AsconHash API */
|
|
|
|
wc_AsconHash256* wc_AsconHash256_New(void)
|
|
{
|
|
wc_AsconHash256* ret = (wc_AsconHash256*)XMALLOC(sizeof(wc_AsconHash256),
|
|
NULL, DYNAMIC_TYPE_ASCON);
|
|
if (ret != NULL) {
|
|
if (wc_AsconHash256_Init(ret) != 0) {
|
|
wc_AsconHash256_Free(ret);
|
|
ret = NULL;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
void wc_AsconHash256_Free(wc_AsconHash256* a)
|
|
{
|
|
if (a != NULL) {
|
|
wc_AsconHash256_Clear(a);
|
|
XFREE(a, NULL, DYNAMIC_TYPE_ASCON);
|
|
}
|
|
}
|
|
|
|
int wc_AsconHash256_Init(wc_AsconHash256* a)
|
|
{
|
|
if (a == NULL)
|
|
return BAD_FUNC_ARG;
|
|
|
|
XMEMSET(a, 0, sizeof(*a));
|
|
|
|
a->state.s64[0] = ASCON_HASH256_IV;
|
|
permutation(&a->state, ASCON_HASH256_ROUNDS);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void wc_AsconHash256_Clear(wc_AsconHash256* a)
|
|
{
|
|
if (a != NULL) {
|
|
ForceZero(a, sizeof(*a));
|
|
}
|
|
}
|
|
|
|
int wc_AsconHash256_Update(wc_AsconHash256* a, const byte* data, word32 dataSz)
|
|
{
|
|
if (a == NULL || (data == NULL && dataSz != 0))
|
|
return BAD_FUNC_ARG;
|
|
|
|
if (dataSz == 0)
|
|
return 0;
|
|
|
|
/* Process leftover block */
|
|
if (a->lastBlkSz != 0) {
|
|
word32 toProcess = min(ASCON_HASH256_RATE - a->lastBlkSz, dataSz);
|
|
xorbuf(a->state.s8 + a->lastBlkSz, data, toProcess);
|
|
data += toProcess;
|
|
dataSz -= toProcess;
|
|
a->lastBlkSz += toProcess;
|
|
|
|
if (a->lastBlkSz < ASCON_HASH256_RATE)
|
|
return 0;
|
|
|
|
permutation(&a->state, ASCON_HASH256_ROUNDS);
|
|
/* Reset the counter */
|
|
a->lastBlkSz = 0;
|
|
}
|
|
|
|
while (dataSz >= ASCON_HASH256_RATE) {
|
|
/* Read in input as little endian numbers */
|
|
xorbuf(a->state.s64, data, ASCON_HASH256_RATE);
|
|
permutation(&a->state, ASCON_HASH256_ROUNDS);
|
|
data += ASCON_HASH256_RATE;
|
|
dataSz -= ASCON_HASH256_RATE;
|
|
}
|
|
|
|
xorbuf(a->state.s64, data, dataSz);
|
|
a->lastBlkSz = dataSz;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int wc_AsconHash256_Final(wc_AsconHash256* a, byte* hash)
|
|
{
|
|
byte i;
|
|
|
|
if (a == NULL || hash == NULL)
|
|
return BAD_FUNC_ARG;
|
|
|
|
/* Process last block */
|
|
a->state.s8[a->lastBlkSz] ^= 1;
|
|
|
|
for (i = 0; i < ASCON_HASH256_SZ; i += ASCON_HASH256_RATE) {
|
|
permutation(&a->state, ASCON_HASH256_ROUNDS);
|
|
XMEMCPY(hash, a->state.s64, ASCON_HASH256_RATE);
|
|
hash += ASCON_HASH256_RATE;
|
|
}
|
|
|
|
/* Clear state as soon as possible */
|
|
wc_AsconHash256_Clear(a);
|
|
return 0;
|
|
}
|
|
|
|
/* AsconAEAD API */
|
|
|
|
wc_AsconAEAD128* wc_AsconAEAD128_New(void)
|
|
{
|
|
wc_AsconAEAD128 *ret = (wc_AsconAEAD128*) XMALLOC(sizeof(wc_AsconAEAD128),
|
|
NULL, DYNAMIC_TYPE_ASCON);
|
|
if (ret != NULL) {
|
|
if (wc_AsconAEAD128_Init(ret) != 0) {
|
|
wc_AsconAEAD128_Free(ret);
|
|
ret = NULL;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
void wc_AsconAEAD128_Free(wc_AsconAEAD128 *a)
|
|
{
|
|
if (a != NULL) {
|
|
wc_AsconAEAD128_Clear(a);
|
|
XFREE(a, NULL, DYNAMIC_TYPE_ASCON);
|
|
}
|
|
}
|
|
|
|
int wc_AsconAEAD128_Init(wc_AsconAEAD128 *a)
|
|
{
|
|
if (a == NULL)
|
|
return BAD_FUNC_ARG;
|
|
|
|
XMEMSET(a, 0, sizeof(*a));
|
|
a->state.s64[0] = ASCON_AEAD128_IV;
|
|
|
|
return 0;
|
|
}
|
|
|
|
void wc_AsconAEAD128_Clear(wc_AsconAEAD128 *a)
|
|
{
|
|
if (a != NULL) {
|
|
ForceZero(a, sizeof(*a));
|
|
}
|
|
}
|
|
|
|
int wc_AsconAEAD128_SetKey(wc_AsconAEAD128* a, const byte* key)
|
|
{
|
|
if (a == NULL || key == NULL)
|
|
return BAD_FUNC_ARG;
|
|
if (a->keySet)
|
|
return BAD_STATE_E;
|
|
|
|
XMEMCPY(a->key, key, ASCON_AEAD128_KEY_SZ);
|
|
a->state.s64[1] = a->key[0];
|
|
a->state.s64[2] = a->key[1];
|
|
a->keySet = 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int wc_AsconAEAD128_SetNonce(wc_AsconAEAD128* a, const byte* nonce)
|
|
{
|
|
if (a == NULL || nonce == NULL)
|
|
return BAD_FUNC_ARG;
|
|
if (a->nonceSet)
|
|
return BAD_STATE_E;
|
|
|
|
XMEMCPY(&a->state.s64[3], nonce, ASCON_AEAD128_NONCE_SZ);
|
|
a->nonceSet = 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int wc_AsconAEAD128_SetAD(wc_AsconAEAD128* a, const byte* ad,
|
|
word32 adSz)
|
|
{
|
|
if (a == NULL || (ad == NULL && adSz > 0))
|
|
return BAD_FUNC_ARG;
|
|
if (!a->keySet || !a->nonceSet) /* key and nonce must be set before */
|
|
return BAD_STATE_E;
|
|
|
|
permutation(&a->state, ASCON_AEAD128_ROUNDS_PA);
|
|
a->state.s64[3] ^= a->key[0];
|
|
a->state.s64[4] ^= a->key[1];
|
|
|
|
if (adSz > 0) {
|
|
while (adSz >= ASCON_AEAD128_RATE) {
|
|
xorbuf(a->state.s64, ad, ASCON_AEAD128_RATE);
|
|
permutation(&a->state, ASCON_AEAD128_ROUNDS_PB);
|
|
ad += ASCON_AEAD128_RATE;
|
|
adSz -= ASCON_AEAD128_RATE;
|
|
}
|
|
xorbuf(a->state.s64, ad, adSz);
|
|
/* Pad the last block */
|
|
a->state.s8[adSz] ^= 1;
|
|
permutation(&a->state, ASCON_AEAD128_ROUNDS_PB);
|
|
}
|
|
a->state.s64[4] ^= 1ULL << 63;
|
|
|
|
a->adSet = 1;
|
|
return 0;
|
|
}
|
|
|
|
int wc_AsconAEAD128_EncryptUpdate(wc_AsconAEAD128* a, byte* out,
|
|
const byte* in, word32 inSz)
|
|
{
|
|
if (a == NULL || (in == NULL && inSz > 0))
|
|
return BAD_FUNC_ARG;
|
|
if (!a->keySet || !a->nonceSet || !a->adSet)
|
|
return BAD_STATE_E;
|
|
|
|
if (a->op == ASCON_AEAD128_NOTSET)
|
|
a->op = ASCON_AEAD128_ENCRYPT;
|
|
else if (a->op != ASCON_AEAD128_ENCRYPT)
|
|
return BAD_STATE_E;
|
|
|
|
/* Process leftover from last block */
|
|
if (a->lastBlkSz != 0) {
|
|
word32 toProcess = min(ASCON_AEAD128_RATE - a->lastBlkSz, inSz);
|
|
xorbuf(&a->state.s8[a->lastBlkSz], in, toProcess);
|
|
XMEMCPY(out, &a->state.s8[a->lastBlkSz], toProcess);
|
|
a->lastBlkSz += toProcess;
|
|
in += toProcess;
|
|
out += toProcess;
|
|
inSz -= toProcess;
|
|
|
|
if (a->lastBlkSz < ASCON_AEAD128_RATE)
|
|
return 0;
|
|
|
|
permutation(&a->state, ASCON_AEAD128_ROUNDS_PB);
|
|
a->lastBlkSz = 0;
|
|
}
|
|
|
|
while (inSz >= ASCON_AEAD128_RATE) {
|
|
xorbuf(a->state.s64, in, ASCON_AEAD128_RATE);
|
|
XMEMCPY(out, a->state.s64, ASCON_AEAD128_RATE);
|
|
permutation(&a->state, ASCON_AEAD128_ROUNDS_PB);
|
|
in += ASCON_AEAD128_RATE;
|
|
out += ASCON_AEAD128_RATE;
|
|
inSz -= ASCON_AEAD128_RATE;
|
|
}
|
|
/* Store leftover */
|
|
xorbuf(a->state.s64, in, inSz);
|
|
XMEMCPY(out, a->state.s64, inSz);
|
|
a->lastBlkSz = inSz;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int wc_AsconAEAD128_EncryptFinal(wc_AsconAEAD128* a, byte* tag)
|
|
{
|
|
if (a == NULL || tag == NULL)
|
|
return BAD_FUNC_ARG;
|
|
if (!a->keySet || !a->nonceSet || !a->adSet)
|
|
return BAD_STATE_E;
|
|
|
|
if (a->op != ASCON_AEAD128_ENCRYPT)
|
|
return BAD_STATE_E;
|
|
|
|
/* Process leftover from last block */
|
|
a->state.s8[a->lastBlkSz] ^= 1;
|
|
|
|
a->state.s64[2] ^= a->key[0];
|
|
a->state.s64[3] ^= a->key[1];
|
|
permutation(&a->state, ASCON_AEAD128_ROUNDS_PA);
|
|
a->state.s64[3] ^= a->key[0];
|
|
a->state.s64[4] ^= a->key[1];
|
|
|
|
XMEMCPY(tag, &a->state.s64[3], ASCON_AEAD128_TAG_SZ);
|
|
|
|
/* Clear state as soon as possible */
|
|
wc_AsconAEAD128_Clear(a);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
int wc_AsconAEAD128_DecryptUpdate(wc_AsconAEAD128* a, byte* out,
|
|
const byte* in, word32 inSz)
|
|
{
|
|
if (a == NULL || (in == NULL && inSz > 0))
|
|
return BAD_FUNC_ARG;
|
|
if (!a->keySet || !a->nonceSet || !a->adSet)
|
|
return BAD_STATE_E;
|
|
|
|
if (a->op == ASCON_AEAD128_NOTSET)
|
|
a->op = ASCON_AEAD128_DECRYPT;
|
|
else if (a->op != ASCON_AEAD128_DECRYPT)
|
|
return BAD_STATE_E;
|
|
|
|
/* Process leftover block */
|
|
if (a->lastBlkSz != 0) {
|
|
word32 toProcess = min(ASCON_AEAD128_RATE - a->lastBlkSz, inSz);
|
|
xorbufout(out, a->state.s8 + a->lastBlkSz, in, toProcess);
|
|
XMEMCPY(a->state.s8 + a->lastBlkSz, in, toProcess);
|
|
in += toProcess;
|
|
out += toProcess;
|
|
inSz -= toProcess;
|
|
a->lastBlkSz += toProcess;
|
|
|
|
if (a->lastBlkSz < ASCON_AEAD128_RATE)
|
|
return 0;
|
|
|
|
permutation(&a->state, ASCON_AEAD128_ROUNDS_PB);
|
|
a->lastBlkSz = 0;
|
|
}
|
|
|
|
while (inSz >= ASCON_AEAD128_RATE) {
|
|
xorbufout(out, a->state.s64, in, ASCON_AEAD128_RATE);
|
|
XMEMCPY(a->state.s64, in, ASCON_AEAD128_RATE);
|
|
permutation(&a->state, ASCON_AEAD128_ROUNDS_PB);
|
|
in += ASCON_AEAD128_RATE;
|
|
out += ASCON_AEAD128_RATE;
|
|
inSz -= ASCON_AEAD128_RATE;
|
|
}
|
|
/* Store leftover */
|
|
xorbufout(out, a->state.s64, in, inSz);
|
|
XMEMCPY(a->state.s64, in, inSz);
|
|
a->lastBlkSz = inSz;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int wc_AsconAEAD128_DecryptFinal(wc_AsconAEAD128* a, const byte* tag)
|
|
{
|
|
if (a == NULL || tag == NULL)
|
|
return BAD_FUNC_ARG;
|
|
if (!a->keySet || !a->nonceSet || !a->adSet)
|
|
return BAD_STATE_E;
|
|
|
|
if (a->op != ASCON_AEAD128_DECRYPT)
|
|
return BAD_STATE_E;
|
|
|
|
/* Pad last block */
|
|
a->state.s8[a->lastBlkSz] ^= 1;
|
|
|
|
a->state.s64[2] ^= a->key[0];
|
|
a->state.s64[3] ^= a->key[1];
|
|
permutation(&a->state, ASCON_AEAD128_ROUNDS_PA);
|
|
a->state.s64[3] ^= a->key[0];
|
|
a->state.s64[4] ^= a->key[1];
|
|
|
|
if (ConstantCompare(tag, (const byte*)&a->state.s64[3],
|
|
ASCON_AEAD128_TAG_SZ) != 0)
|
|
return ASCON_AUTH_E;
|
|
|
|
/* Clear state as soon as possible */
|
|
wc_AsconAEAD128_Clear(a);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#endif /* HAVE_ASCON */
|