/////////////////////////////////////////////////////////////////////////////// // hash.c: Reference C implementation of the hash function ESCH256. // // This file is part of the SPARKLE submission to NIST's LW Crypto Project. // // Version 1.0.0 (2019-03-29), 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 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_hash.c shall be used to generate the test vector output file. The // test vector output file shall be provided in the corresponding // crypto_hash/[algorithm]/ directory #include #include "api.h" #include "crypto_hash.h" #include "eschconfig.h" #include "sparkle_ref.h" // crypto_hash.h MUST NOT be modified in any way! The file should not be // included in the reference implementation #define ROT(x, n) (((x) >> (n)) | ((x) << (32-(n)))) #define ELL(x) (ROT(((x) ^ ((x) << 16)), 16)) typedef unsigned char uchar_t; typedef unsigned long long int ullint_t; // The function injectm_ref adds a 16-byte block of the message to the two // leftmost branches of the state (i.e. to the state-words x0, y0, x1, and y1), // whereby the block is first ransformed via a linear Feistel function. void injectm_ref(state_t *state, const uchar_t *msgbytes, int nb) { uint32_t *msgwords = (uint32_t *) msgbytes; uint32_t tmpx = 0, tmpy = 0; int i, j; // Since the message block is 16 bytes long, we need to consider only two // x-words when computing tmpx and two y-words when computing tmpy. for(i = 0; i < MSGBLOCK_WLEN; i += 2) { tmpx ^= msgwords[i]; tmpy ^= msgwords[i+1]; } tmpx = ELL(tmpx); tmpy = ELL(tmpy); // The two leftmost x-words of the state are updated by adding the two // x-words of the message and tmpy to them, and the same is done with the two // leftmost y-words. The remaining nb/2-2 x-words are updated by just adding // tmpy to them, and the same is done with the remaining nb/2-2 y-words. for (i = j = 0; i < MSGBLOCK_WLEN/2; i++) { state->x[i] ^= (msgwords[j++] ^ tmpy); state->y[i] ^= (msgwords[j++] ^ tmpx); } for (i = MSGBLOCK_WLEN/2; i < nb/2; i++) { state->x[i] ^= tmpy; state->y[i] ^= tmpx; } } // The function trunc_state extracts the four 32-bit words x0, y0, x1, and y1 // from the state and copies these 16 bytes to the array . void trunc_state(uchar_t *out, const state_t *state) { uint32_t *out_words = (uint32_t *) out; int i, j; for (i = j = 0; j < SQZBLOCK_WLEN; i++) { out_words[j++] = state->x[i]; out_words[j++] = state->y[i]; } } // To ensure compatibility with the SUPERCOP, the below implementation of // crypto_hash can handle overlapping input and output buffers. int crypto_hash(uchar_t *out, const uchar_t *in, ullint_t inlen) { state_t state = { { 0 }, { 0 } }; // State with x, y array initialized to 0 uchar_t lastblk[MSGBLOCK_BLEN] = { 0 }; // Buffer for last block of message // The type size_t is large enough to contain the size in bytes of any object size_t in_blen = (size_t) inlen, hashed_bytes = 0, lastblk_blen; // A message exceeding 16 bytes is absorbed in 16-byte blocks. Note that the // loop below is not iterated at all when inlen <= 16 bytes. while((in_blen - hashed_bytes) > MSGBLOCK_BLEN) { // Add 16 bytes of the message to the state injectm_ref(&state, &(in[hashed_bytes]), NUM_BRANCHES); // Execute SPARKLE with a slim number of steps sparkle_ref(&state, NUM_BRANCHES, STEPS_SLIM); hashed_bytes += MSGBLOCK_BLEN; } // The last block can be between 0 and 16 bytes long (it can only be 0 when // inlen is 0). It is padded only when its length is shorter than 16 bytes. lastblk_blen = in_blen - hashed_bytes; memcpy(lastblk, &(in[hashed_bytes]), lastblk_blen); if (lastblk_blen < MSGBLOCK_BLEN) { lastblk[lastblk_blen] = 0x80; } // Absorb the (padded) last message block injectm_ref(&state, lastblk, NUM_BRANCHES); // Const_M is added to y2, which is state.y[2] if (lastblk_blen < MSGBLOCK_BLEN) { state.y[(NUM_BRANCHES/2)-1] ^= 0x01000000; } else { state.y[(NUM_BRANCHES/2)-1] ^= 0x02000000; } // Execute SPARKLE with a big number of steps sparkle_ref(&state, NUM_BRANCHES, STEPS_BIG); // Squeeze to produce the message digest trunc_state(out, &state); sparkle_ref(&state, NUM_BRANCHES, STEPS_SLIM); trunc_state(&(out[SQZBLOCK_BLEN]), &state); return 0; }