///////////////////////////////////////////////////////////////////////////////
// encrypt.c: Reference C99 implementation of the AEAD algorithm SCHWAEMM. //
// This file is part of the SPARKLE submission to NIST's LW Crypto Project. //
// Version 1.1.2 (2020-10-30), see for updates. //
// Authors: The SPARKLE Group (C. Beierle, A. Biryukov, L. Cardoso dos //
// Santos, J. Groszschaedl, L. Perrin, A. Udovenko, V. Velichkov, Q. Wang). //
// License: GPLv3 (see LICENSE file), other licenses available upon request. //
// Copyright (C) 2019-2020 University of Luxembourg . //
// ------------------------------------------------------------------------- //
// This program 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 3 of the License, or (at your //
// option) any later version. This program 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, //
// see . //
///////////////////////////////////////////////////////////////////////////////
// This source code file should be compiled with the following set of flags:
// -std=c99 -Wall -Wextra -Wshadow -fsanitize=address,undefined -O2
// gencat_aead.c shall be used to generate the test vector output file. The
// test vector output file shall be provided in the corresponding
// crypto_aead/[algorithm]/ directory
#include // for size_t
#include // for memcpy, memset
#include "schwaemm_cfg.h"
#include "sparkle_ref.h"
typedef unsigned char UChar;
typedef unsigned long long int ULLInt;
#define KEY_WORDS (SCHWAEMM_KEY_LEN/32)
#define KEY_BYTES (SCHWAEMM_KEY_LEN/8)
#define NONCE_WORDS (SCHWAEMM_NONCE_LEN/32)
#define NONCE_BYTES (SCHWAEMM_NONCE_LEN/8)
#define TAG_WORDS (SCHWAEMM_TAG_LEN/32)
#define TAG_BYTES (SCHWAEMM_TAG_LEN/8)
#define STATE_BRANS (SPARKLE_STATE/64)
#define STATE_WORDS (SPARKLE_STATE/32)
#define STATE_BYTES (SPARKLE_STATE/8)
#define RATE_BRANS (SPARKLE_RATE/64)
#define RATE_WORDS (SPARKLE_RATE/32)
#define RATE_BYTES (SPARKLE_RATE/8)
#define CAP_BRANS (SPARKLE_CAPACITY/64)
#define CAP_WORDS (SPARKLE_CAPACITY/32)
#define CAP_BYTES (SPARKLE_CAPACITY/8)
#define CONST_A0 (((uint32_t) (0 ^ (1 << CAP_BRANS))) << 24)
#define CONST_A1 (((uint32_t) (1 ^ (1 << CAP_BRANS))) << 24)
#define CONST_M2 (((uint32_t) (2 ^ (1 << CAP_BRANS))) << 24)
#define CONST_M3 (((uint32_t) (3 ^ (1 << CAP_BRANS))) << 24)
///////////////////////////////////////////////////////////////////////////////
/////// HELPER FUNCTIONS AND MACROS (RHO1, RHO2, RATE-WHITENING, ETC.) ////////
///////////////////////////////////////////////////////////////////////////////
// The macro STATE_WORD expands to the address of the i-th word of the state,
// which is always an x-word if i is even and a y-word otherwise.
#define STATE_WORD(s, i) (((i) & 1) ? (&((s)->y[(i)/2])) : (&((s)->x[(i)/2])))
// Rho and rate-whitening for the authentication of associated data.
static void rho_whi_aut(SparkleState *state, const uint8_t *in, size_t inlen)
{
uint32_t inbuf[RATE_WORDS] = { 0 };
uint32_t *left_word, *right_word, tmp; // Feistel-swap
int i;
memcpy(inbuf, in, inlen);
if (inlen < RATE_BYTES) // padding (only for last block)
*(((uint8_t *) inbuf) + inlen) = 0x80;
// Rho1 part1: Feistel swap of the rate-part of the state
for (i = 0; i < RATE_BRANS; i++) {
left_word = STATE_WORD(state, i);
right_word = STATE_WORD(state, (RATE_BRANS + i));
tmp = *left_word;
*left_word = *right_word;
*right_word ^= tmp;
}
// Rho1 part2: rate-part of state is XORed with assoc data
for (i = 0; i < RATE_BRANS; i++) {
state->x[i] ^= inbuf[2*i];
state->y[i] ^= inbuf[2*i+1];
}
// Rate-whitening: capacity-part is XORed to the rate-part
for (i = 0; i < RATE_BRANS; i++) {
state->x[i] ^= state->x[RATE_BRANS+(i%CAP_BRANS)];
state->y[i] ^= state->y[RATE_BRANS+(i%CAP_BRANS)];
}
}
// Rho and rate-whitening for the encryption of plaintext.
static void rho_whi_enc(SparkleState *state, uint8_t *out, const uint8_t *in, \
size_t inlen)
{
uint32_t inbuf[RATE_WORDS] = { 0 }, outbuf[RATE_WORDS];
uint32_t *left_word, *right_word, tmp; // Feistel-swap
int i;
memcpy(inbuf, in, inlen);
if (inlen < RATE_BYTES) // padding (only for last block)
*(((uint8_t *) inbuf) + inlen) = 0x80;
// Rho2: ciphertext = plaintext XOR rate-part of the state
for (i = 0; i < RATE_BRANS; i++) {
outbuf[2*i] = inbuf[2*i] ^ state->x[i];
outbuf[2*i+1] = inbuf[2*i+1] ^ state->y[i];
}
// Rho1 part1: Feistel swap of the rate-part of the state
for (i = 0; i < RATE_BRANS; i++) {
left_word = STATE_WORD(state, i);
right_word = STATE_WORD(state, (RATE_BRANS + i));
tmp = *left_word;
*left_word = *right_word;
*right_word ^= tmp;
}
// Rho1 part2: rate-part of state is XORed with ciphertext
for (i = 0; i < RATE_BRANS; i++) {
state->x[i] ^= inbuf[2*i];
state->y[i] ^= inbuf[2*i+1];
}
// Rate-whitening: capacity-part is XORed to the rate-part
for (i = 0; i < RATE_BRANS; i++) {
state->x[i] ^= state->x[RATE_BRANS+(i%CAP_BRANS)];
state->y[i] ^= state->y[RATE_BRANS+(i%CAP_BRANS)];
}
memcpy(out, outbuf, inlen);
}
// Rho and rate-whitening for the decryption of ciphertext.
static void rho_whi_dec(SparkleState *state, uint8_t *out, const uint8_t *in, \
size_t inlen)
{
uint32_t inbuf[RATE_WORDS] = { 0 }, outbuf[RATE_WORDS];
SparkleState statebuf;
uint32_t *left_word, *right_word, tmp; // Feistel-swap
int i;
memcpy(inbuf, in, inlen);
memcpy(&statebuf, state, sizeof(SparkleState));
if (inlen < RATE_BYTES) // padding (only for last block!)
*(((uint8_t *) inbuf) + inlen) = 0x80;
// Rho2': plaintext = ciphertext XOR rate-part of the state
for (i = 0; i < RATE_BRANS; i++) {
outbuf[2*i] = inbuf[2*i] ^ state->x[i];
outbuf[2*i+1] = inbuf[2*i+1] ^ state->y[i];
}
// Rho1' part1: Feistel swap of the rate-part of the state
for (i = 0; i < RATE_BRANS; i++) {
left_word = STATE_WORD(state, i);
right_word = STATE_WORD(state, (RATE_BRANS + i));
tmp = *left_word;
*left_word = *right_word;
*right_word ^= tmp;
}
if (inlen < RATE_BYTES) {
// padding of last block of plaintext (computed by Rho2')
memset((((uint8_t *) outbuf) + inlen), 0, (RATE_BYTES - inlen));
*(((uint8_t *) outbuf) + inlen) = 0x80;
// Rho1 part2: rate-part of state is XORed with plaintext
for (i = 0; i < RATE_BRANS; i++) {
state->x[i] ^= outbuf[2*i];
state->y[i] ^= outbuf[2*i+1];
}
} else {
// Rho1' part2: rate-part XORed with orig rate and ciphertext
for (i = 0; i < RATE_BRANS; i++) {
state->x[i] ^= statebuf.x[i] ^ inbuf[2*i];
state->y[i] ^= statebuf.y[i] ^ inbuf[2*i+1];
}
}
// Rate-whitening: capacity-part is XORed to the rate-part
for (i = 0; i < RATE_BRANS; i++) {
state->x[i] ^= state->x[RATE_BRANS+(i%CAP_BRANS)];
state->y[i] ^= state->y[RATE_BRANS+(i%CAP_BRANS)];
}
memcpy(out, outbuf, inlen);
}
///////////////////////////////////////////////////////////////////////////////
///////////// LOW-LEVEL AEAD FUNCTIONS (FOR USE WITH FELICS-AEAD) /////////////
///////////////////////////////////////////////////////////////////////////////
// The Initialize function loads nonce and key into the state and executes the
// SPARKLE permutation with the big number of steps.
void Initialize(SparkleState *state, const uint8_t *key, const uint8_t *nonce)
{
uint32_t keybuf[KEY_WORDS], noncebuf[NONCE_WORDS];
int i;
// to prevent (potentially) unaligned memory accesses
memcpy(keybuf, key, KEY_BYTES);
memcpy(noncebuf, nonce, NONCE_BYTES);
// load nonce into the rate-part of the state
for (i = 0; i < NONCE_WORDS/2; i++) {
state->x[i] = noncebuf[2*i];
state->y[i] = noncebuf[2*i+1];
}
// load key into the capacity-part of the sate
for (i = 0; i < KEY_WORDS/2; i++) {
state->x[RATE_BRANS+i] = keybuf[2*i];
state->y[RATE_BRANS+i] = keybuf[2*i+1];
}
// execute SPARKLE with big number of steps
sparkle_ref(state, STATE_BRANS, SPARKLE_STEPS_BIG);
}
// The ProcessAssocData function absorbs the associated data, which becomes
// only authenticated but not encrypted, into the state (in blocks of size
// RATE_BYTES). Note that this function MUST NOT be called when the length of
// the associated data is 0.
void ProcessAssocData(SparkleState *state, const uint8_t *in, size_t inlen)
{
// Main Authentication Loop
while (inlen > RATE_BYTES) {
// combined Rho and rate-whitening operation
rho_whi_aut(state, in, RATE_BYTES);
// execute SPARKLE with slim number of steps
sparkle_ref(state, STATE_BRANS, SPARKLE_STEPS_SLIM);
inlen -= RATE_BYTES;
in += RATE_BYTES;
}
// Authentication of Last Block
// addition of constant A0 or A1 to the state
state->y[STATE_BRANS-1] ^= ((inlen < RATE_BYTES) ? CONST_A0 : CONST_A1);
// combined Rho and rate-whitening operation
rho_whi_aut(state, in, inlen);
// execute SPARKLE with big number of steps
sparkle_ref(state, STATE_BRANS, SPARKLE_STEPS_BIG);
}
// The ProcessPlainText function encrypts the plaintext (in blocks of size
// RATE_BYTES) and generates the respective ciphertext. The uint8_t-array 'in'
// contains the plaintext and the ciphertext is written to uint8_t-array 'out'
// ('in' and 'out' can be the same array, i.e. they can have the same start
// address). Note that this function MUST NOT be called when the length of the
// plaintext is 0.
void ProcessPlainText(SparkleState *state, uint8_t *out, const uint8_t *in, \
size_t inlen)
{
// Main Encryption Loop
while (inlen > RATE_BYTES) {
// combined Rho and rate-whitening operation
rho_whi_enc(state, out, in, RATE_BYTES);
// execute SPARKLE with slim number of steps
sparkle_ref(state, STATE_BRANS, SPARKLE_STEPS_SLIM);
inlen -= RATE_BYTES;
out += RATE_BYTES;
in += RATE_BYTES;
}
// Encryption of Last Block
// addition of constant M2 or M3 to the state
state->y[STATE_BRANS-1] ^= ((inlen < RATE_BYTES) ? CONST_M2 : CONST_M3);
// combined Rho and rate-whitening (incl. padding)
rho_whi_enc(state, out, in, inlen);
// execute SPARKLE with big number of steps
sparkle_ref(state, STATE_BRANS, SPARKLE_STEPS_BIG);
}
// The Finalize function adds the key to the capacity part of the state.
void Finalize(SparkleState *state, const uint8_t *key)
{
uint32_t keybuf[KEY_WORDS];
int i;
// to prevent (potentially) unaligned memory accesses
memcpy(keybuf, key, KEY_BYTES);
// add key to the capacity-part of the state
for (i = 0; i < KEY_WORDS/2; i++) {
state->x[RATE_BRANS+i] ^= keybuf[2*i];
state->y[RATE_BRANS+i] ^= keybuf[2*i+1];
}
}
// The GenerateTag function generates an authentication tag.
void GenerateTag(SparkleState *state, uint8_t *tag)
{
uint32_t tagbuf[TAG_WORDS];
int i;
for (i = 0; i < TAG_WORDS/2; i++) {
tagbuf[2*i] = state->x[RATE_BRANS+i];
tagbuf[2*i+1] = state->y[RATE_BRANS+i];
}
memcpy(tag, tagbuf, TAG_BYTES);
}
// The VerifyTag function checks whether the given authentication tag is valid.
// It performs a simple constant-time comparison and returns 0 if the provided
// tag matches the computed tag and -1 otherwise.
int VerifyTag(SparkleState *state, const uint8_t *tag)
{
uint32_t tagbuf[TAG_WORDS], diff = 0;
int i;
// to prevent (potentially) unaligned memory accesses
memcpy(tagbuf, tag, TAG_BYTES);
// constant-time comparison: 0 if equal, -1 otherwise
for (i = 0; i < TAG_WORDS/2; i++) {
diff |= (state->x[RATE_BRANS+i] ^ tagbuf[2*i]);
diff |= (state->y[RATE_BRANS+i] ^ tagbuf[2*i+1]);
}
return (((int) (diff == 0)) - 1);
}
// The ProcessCipherText function decrypts the ciphertext (in blocks of size
// RATE_BYTES) and generates the respective plaintext. The uint8_t-array 'in'
// contains the ciphertext and the plaintext is written to uint8_t-array 'out'
// ('in' and 'out' can be the same array, i.e. they can have the same start
// address). Note that this function MUST NOT be called when the length of the
// ciphertext is 0.
void ProcessCipherText(SparkleState *state, uint8_t *out, const uint8_t *in, \
size_t inlen)
{
// Main Decryption Loop
while (inlen > RATE_BYTES) {
// combined Rho and rate-whitening operation
rho_whi_dec(state, out, in, RATE_BYTES);
// execute SPARKLE with slim number of steps
sparkle_ref(state, STATE_BRANS, SPARKLE_STEPS_SLIM);
inlen -= RATE_BYTES;
out += RATE_BYTES;
in += RATE_BYTES;
}
// Decryption of Last Block
// addition of constant M2 or M3 to the state
state->y[STATE_BRANS-1] ^= ((inlen < RATE_BYTES) ? CONST_M2 : CONST_M3);
// combined Rho and rate-whitening (incl. padding)
rho_whi_dec(state, out, in, inlen);
// execute SPARKLE with big number of steps
sparkle_ref(state, STATE_BRANS, SPARKLE_STEPS_BIG);
}
///////////////////////////////////////////////////////////////////////////////
////////////// HIGH-LEVEL AEAD FUNCTIONS (FOR USE WITH SUPERCOP) //////////////
///////////////////////////////////////////////////////////////////////////////
// High-level encryption function from SUPERCOP.
// nsec is kept for compatibility with SUPERCOP, but is not used.
int crypto_aead_encrypt(UChar *c, ULLInt *clen, const UChar *m, ULLInt mlen, \
const UChar *ad, ULLInt adlen, const UChar *nsec, const UChar *npub, \
const UChar *k)
{
SparkleState state;
size_t msize = (size_t) mlen;
size_t adsize = (size_t) adlen;
Initialize(&state, k, npub);
if (adsize) ProcessAssocData(&state, ad, adsize);
if (msize) ProcessPlainText(&state, c, m, msize);
Finalize(&state, k);
GenerateTag(&state, (c + msize));
*clen = msize;
*clen += TAG_BYTES;
return 0;
}
// High-level decryption function from SUPERCOP.
// nsec is kept for compatibility with SUPERCOP, but is not used.
int crypto_aead_decrypt(UChar *m, ULLInt *mlen, UChar *nsec, const UChar *c, \
ULLInt clen, const UChar *ad, ULLInt adlen, const UChar *npub, \
const UChar *k)
{
SparkleState state;
size_t csize = (size_t) (clen - TAG_BYTES);
size_t adsize = (size_t) adlen;
int retval;
Initialize(&state, k, npub);
if (adsize) ProcessAssocData(&state, ad, adsize);
if (csize) ProcessCipherText(&state, m, c, csize);
Finalize(&state, k);
retval = VerifyTag(&state, (c + csize));
*mlen = csize;
return retval;
}