/////////////////////////////////////////////////////////////////////////////// // hash.c: Optimized C implementation of the hash function ESCH384. // // 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_opt.h" // crypto_hash.h MUST NOT be modified in any way! The file should not be // included in the reference implementation typedef unsigned char uchar_t; typedef unsigned long long int ullint_t; static const uint32_t MASK16 = 0xFFFFU; #define ROT(x, n) (((x) >> (n)) | ((x) << (32-(n)))) #define ELL(x) (ROT((x), 16) ^ ((x) & MASK16)) // The function injectm_opt 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 transformed via a linear Feistel function. void injectm_opt(uint32_t *state, const uint32_t *msgwords, int nb) { int i; uint32_t tmpx = 0, tmpy = 0; // 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 = 0; i < MSGBLOCK_WLEN; i += 2) { state[i] ^= (msgwords[i] ^ tmpy); state[i+1] ^= (msgwords[i+1] ^ tmpx); } for(i = MSGBLOCK_WLEN; i < nb; i += 2) { state[i] ^= tmpy; state[i+1] ^= tmpx; } } // 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) { uint32_t state[2*NUM_BRANCHES] = { 0 }; // We have two words in each branch uint32_t lastblk[MSGBLOCK_WLEN] = { 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; // 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 > MSGBLOCK_BLEN) { // Add 16 bytes of the message to the state injectm_opt(state, ((uint32_t *) in), NUM_BRANCHES); // Execute SPARKLE with a slim number of steps sparkle_opt(state, NUM_BRANCHES, STEPS_SLIM); in_blen -= MSGBLOCK_BLEN; in += 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. memcpy(lastblk, in, in_blen); if (in_blen < MSGBLOCK_BLEN) { ((uchar_t *) lastblk)[in_blen] = 0x80; } // Add the (padded) last block to the state injectm_opt(state, lastblk, NUM_BRANCHES); // Const_M is added to y3, which is state[7] state[NUM_BRANCHES-1] ^= ((in_blen < MSGBLOCK_BLEN) ? 0x01000000 : 0x02000000); // Execute SPARKLE with a big number of steps sparkle_opt(state, NUM_BRANCHES, STEPS_BIG); // Squeeze to produce the message digest memcpy(out, state, SQZBLOCK_BLEN); sparkle_opt(state, NUM_BRANCHES, STEPS_SLIM); memcpy(&(out[SQZBLOCK_BLEN]), state, SQZBLOCK_BLEN); sparkle_opt(state, NUM_BRANCHES, STEPS_SLIM); memcpy(&(out[2*SQZBLOCK_BLEN]), state, SQZBLOCK_BLEN); return 0; }