aboutsummaryrefslogblamecommitdiffstats
path: root/src/libkeccak/digest.c
blob: cc2bd1b33e8f91840a652dc2fc880f8463066450 (plain) (tree)
1
2
3
4
5
6
7
8
9
10




                                                               
                                                                       



                                                                              
                                                                  




                                                                           
                                                                        


                   

                  
 

   




                                                     

                                                     
                                     
 
   


                                                                        
                                                                




















                                                                  
                                 
   





                                                                                               


    



                                                  
                                                                  



                                                
                                                                                               








                                                           
                                                                                                 







                                                  
                                             
                                                                                            

                                      

                     

                                  

                    
                              

                                                                                          

        
                              




                                       

























                                                                                                    
                                             
                                                                                              
 
                                      

                     

                                  
                              
                                                                                          
        

        
                              























                                                                                                    


                                     
   
                                   
   

                                                            
 


                                   
               

                                                        
      
                       
                                                             












                                              
                                                                                     
                                                                                             
                                                                              
 
                                                                              
                      
                 

















                                                                                          
                                                                                          

                                                                                               
 
                                                                              









                                                                                                             






                                                                                              

                                                                                           
 




                                                                     







                                       
                       


                                             
                            
                                                                      
                                 
     









                                                               
                                                                                                
 
                                   
                                   

                                                   
                                                                                                          
               
       
                                                                                                        


                           

                              

       
               
       
                                                                                                           


                           

                              













                                                                       

                                                                                   
 
                          
                             


                            





                                                        
                                 




                                     
                                                     




                                                  




































                                                                                                     








                                                                                                
                          
  
                                                              

                            
                                               

                                         

                                          

                     
  


                                                                       
                                                            


                                         
                                                                          

           




                                                                    





































































                                                                                                     

                                      
                                                                            

                                                                                         
                                                                                                     


                                                                   
                                                                                                

                                                                                      




                                                                 



                      
                                   
  
                                                             
                                                           
     
                         
                                               
                      
                                      

                                          


                     
             
                                                                         

                        
           
                                                                  


                                        
                                  

                          
                                                                     
                        
                                                    


           
                  
  
                                    
                                                 

                      
                                                                                      
      
                                             

                         
           








                                      
                                                                                              











                                                                 
                                                                                            












                                                   
                                                                                                   
 
                     
                                                                                               

 
/**
 * libkeccak – Keccak-family hashing library
 * 
 * Copyright © 2014  Mattias Andrée (maandree@member.fsf.org)
 * 
 * This library is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This library 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 Affero General Public License for more details.
 * 
 * You should have received a copy of the GNU Affero General Public License
 * along with this library.  If not, see <http://www.gnu.org/licenses/>.
 */
#include "digest.h"

#include "state.h"



/**
 * X-macro-enabled listing of all intergers in [0, 4]
 */
#define LIST_5  X(0) X(1) X(2) X(3) X(4)

/**
 * X-macro-enabled listing of all intergers in [0, 7]
 */
#define LIST_8  LIST_5 X(5) X(6) X(7)

/**
 * X-macro-enabled listing of all intergers in [0, 23]
 */
#define LIST_24  LIST_8 X(8) X(9) X(10) X(11) X(12) X(13) X(14) X(15)  \
                 X(16) X(17) X(18) X(19) X(20) X(21) X(22) X(23)

/**
 * X-macro-enabled listing of all intergers in [0, 24]
 */
#define LIST_25  LIST_24 X(24) 



#define X(N)  (N % 5) * 5 + N / 5,
/**
 * The order the lanes should be read when absorbing or squeezing,
 * it transposes the lanes in the sponge
 */
static const long LANE_TRANSPOSE_MAP[] = { LIST_25 };
#undef X



/**
 * Keccak-f round constants
 */
static const uint_fast64_t RC[] =
  {
    0x0000000000000001ULL, 0x0000000000008082ULL, 0x800000000000808AULL, 0x8000000080008000ULL,
    0x000000000000808BULL, 0x0000000080000001ULL, 0x8000000080008081ULL, 0x8000000000008009ULL,
    0x000000000000008AULL, 0x0000000000000088ULL, 0x0000000080008009ULL, 0x000000008000000AULL,
    0x000000008000808BULL, 0x800000000000008BULL, 0x8000000000008089ULL, 0x8000000000008003ULL,
    0x8000000000008002ULL, 0x8000000000000080ULL, 0x000000000000800AULL, 0x800000008000000AULL,
    0x8000000080008081ULL, 0x8000000000008080ULL, 0x0000000080000001ULL, 0x8000000080008008ULL
  };


/**
 * Rotate a word
 * 
 * @param   x:int_fast64_t     The value to rotate
 * @param   n:long             Rotation steps, may be zero mod `w`
 * @param   w:long             `state->w`
 * @param   wmod:int_fast64_t  `state->wmod`
 * @return  :int_fast64_t      The value rotated
 */
#define rotate(x, n, w, wmod)  ((((x) >> ((w) - ((n) % (w)))) | ((x) << ((n) % (w)))) & (wmod))


/**
 * Rotate a 64-bit word
 * 
 * @param   x:int_fast64_t  The value to rotate
 * @param   n:long          Rotation steps, may not be zero
 * @return   :int_fast64_t  The value rotated
 */
#define rotate64(x, n)  ((int_fast64_t)(((uint64_t)(x) >> (64L - (n))) | ((uint64_t)(x) << (n))))


/**
 * Perform one round of computation
 * 
 * @param  state  The hashing state
 * @param  rc     The round contant for this round
 */
static __attribute__((nonnull, nothrow, hot))
void libkeccak_f_round(register libkeccak_state_t* restrict state, register int_fast64_t rc)
{
  int_fast64_t* restrict A = state->S;
  int_fast64_t B[25];
  int_fast64_t C[5];
  int_fast64_t da, db, dc, dd, de;
  int_fast64_t wmod = state->wmod;
  long w = state->w;
  
  /* θ step (step 1 of 3). */
#define X(N)  C[N] = A[N * 5] ^ A[N * 5 + 1] ^ A[N * 5 + 2] ^ A[N * 5 + 3] ^ A[N * 5 + 4];
  LIST_5
#undef X
  
  /* θ step (step 2 of 3). */
  da = C[4] ^ rotate(C[1], 1, w, wmod);
  dd = C[2] ^ rotate(C[4], 1, w, wmod);
  db = C[0] ^ rotate(C[2], 1, w, wmod);
  de = C[3] ^ rotate(C[0], 1, w, wmod);
  dc = C[1] ^ rotate(C[3], 1, w, wmod);
  
  /* ρ and π steps, with last two part of θ. */
#define X(bi, ai, dv, r)  B[bi] = rotate(A[ai] ^ dv, r, w, wmod)
  B[0] = A[0] ^ da;   X( 1, 15, dd, 28);  X( 2,  5, db,  1);  X( 3, 20, de, 27);  X( 4, 10, dc, 62);
  X( 5,  6, db, 44);  X( 6, 21, de, 20);  X( 7, 11, dc,  6);  X( 8,  1, da, 36);  X( 9, 16, dd, 55);
  X(10, 12, dc, 43);  X(11,  2, da,  3);  X(12, 17, dd, 25);  X(13,  7, db, 10);  X(14, 22, de, 39);
  X(15, 18, dd, 21);  X(16,  8, db, 45);  X(17, 23, de,  8);  X(18, 13, dc, 15);  X(19,  3, da, 41);
  X(20, 24, de, 14);  X(21, 14, dc, 61);  X(22,  4, da, 18);  X(23, 19, dd, 56);  X(24,  9, db,  2);
#undef X
  
  /* ξ step. */
#define X(N)  A[N] = B[N] ^ ((~(B[(N + 5) % 25])) & B[(N + 10) % 25]);
  LIST_25
#undef X
  
  /* ι step. */
  A[0] ^= rc;
}


/**
 * 64-bit word version of `libkeccak_f_round`
 * 
 * @param  state  The hashing state
 * @param  rc     The round contant for this round
 */
static __attribute__((nonnull, nothrow, hot))
void libkeccak_f_round64(register libkeccak_state_t* restrict state, register int_fast64_t rc)
{
  int_fast64_t* restrict A = state->S;
  int_fast64_t B[25];
  int_fast64_t C[5];
  int_fast64_t da, db, dc, dd, de;
  
  /* θ step (step 1 of 3). */
#define X(N)  C[N] = A[N * 5] ^ A[N * 5 + 1] ^ A[N * 5 + 2] ^ A[N * 5 + 3] ^ A[N * 5 + 4];
  LIST_5
#undef X
  
  /* θ step (step 2 of 3). */
  da = C[4] ^ rotate64(C[1], 1);
  dd = C[2] ^ rotate64(C[4], 1);
  db = C[0] ^ rotate64(C[2], 1);
  de = C[3] ^ rotate64(C[0], 1);
  dc = C[1] ^ rotate64(C[3], 1);
  
  /* ρ and π steps, with last two part of θ. */
#define X(bi, ai, dv, r)  B[bi] = rotate64(A[ai] ^ dv, r)
  B[0] = A[0] ^ da;   X( 1, 15, dd, 28);  X( 2,  5, db,  1);  X( 3, 20, de, 27);  X( 4, 10, dc, 62);
  X( 5,  6, db, 44);  X( 6, 21, de, 20);  X( 7, 11, dc,  6);  X( 8,  1, da, 36);  X( 9, 16, dd, 55);
  X(10, 12, dc, 43);  X(11,  2, da,  3);  X(12, 17, dd, 25);  X(13,  7, db, 10);  X(14, 22, de, 39);
  X(15, 18, dd, 21);  X(16,  8, db, 45);  X(17, 23, de,  8);  X(18, 13, dc, 15);  X(19,  3, da, 41);
  X(20, 24, de, 14);  X(21, 14, dc, 61);  X(22,  4, da, 18);  X(23, 19, dd, 56);  X(24,  9, db,  2);
#undef X
  
  /* ξ step. */
#define X(N)  A[N] = B[N] ^ ((~(B[(N + 5) % 25])) & B[(N + 10) % 25]);
  LIST_25
#undef X
  
  /* ι step. */
  A[0] ^= rc;
}


/**
 * Convert a chunk of bytes to a lane
 * 
 * @param  state  The hashing state
 */
static inline __attribute__((nonnull, nothrow, gnu_inline))
void libkeccak_f(register libkeccak_state_t* restrict state)
{
  register long i = 0;
  register long nr = state->nr;
  register long wmod = state->wmod;
  if (nr == 24)
    for (; i < nr; i++)
      libkeccak_f_round64(state, (int_fast64_t)(RC[i]));
  else
    for (; i < nr; i++)
      libkeccak_f_round(state, (int_fast64_t)(RC[i] & wmod));
}


/**
 * Convert a chunk of bytes to a lane
 * 
 * @param   message  The message
 * @param   msglen   The length of the message
 * @param   rr       Bitrate in bytes
 * @param   ww       Word size in bytes
 * @param   off      The offset in the message
 * @return           The lane
 */
static inline __attribute__((nonnull, nothrow, pure, warn_unused_result, gnu_inline))
int_fast64_t libkeccak_to_lane(register const char* restrict message, register size_t msglen,
			       register long rr, register long ww, size_t off)
{
  register long n = (long)((msglen < (size_t)rr ? msglen : (size_t)rr) - off);
  int_fast64_t rc = 0;
  message += off;
  while (ww--)
    {
      rc <<= 8;
      rc |= __builtin_expect(ww < n, 1) ? (int_fast64_t)(unsigned char)(message[ww]) : 0L;
    }
  return rc;
}


/**
 * 64-bit lane version of `libkeccak_to_lane`
 * 
 * @param   message  The message
 * @param   msglen   The length of the message
 * @param   rr       Bitrate in bytes
 * @param   off      The offset in the message
 * @return           The lane
 */
static inline __attribute__((nonnull, nothrow, pure, hot, warn_unused_result, gnu_inline))
int_fast64_t libkeccak_to_lane64(register const char* restrict message, register size_t msglen,
				 register long rr, size_t off)
{
  register long n = (long)((msglen < (size_t)rr ? msglen : (size_t)rr) - off);
  int_fast64_t rc = 0;
  message += off;
#define X(N)  if (__builtin_expect(N < n, 1))  rc |= (int_fast64_t)(unsigned char)(message[N]) << (N * 8);  \
              else  return rc;
  LIST_8
#undef X
  return rc;
}


/**
 * pad 10*1
 * 
 * @param  state  The hashing state, `state->M` and `state->mptr` will be updated,
 *                `state->M` should have `state->r / 8` bytes left over at the end
 * @param  bits   The number of bits in the end of the message that does not make a whole byte
 */
static inline __attribute__((nonnull, nothrow, gnu_inline))
void libkeccak_pad10star1(register libkeccak_state_t* restrict state, register size_t bits)
{
  register size_t r = (size_t)(state->r);
  register size_t nrf = state->mptr - !!bits;
  register size_t len = (nrf << 3) | bits;
  register size_t ll = len % r;
  register char b = (char)(bits ? (state->M[nrf] | (1 << bits)) : 1);
  
  if ((r - 8 <= ll) && (ll <= r - 2))
    {
      state->M[nrf] = (char)(b ^ 0x80);
      state->mptr = nrf + 1;
    }
  else
    {
      len = ++nrf << 3;
      len = (len - (len % r) + (r - 8)) >> 3;
      state->mptr = len + 1;
      
      state->M[nrf - 1] = b;
      __builtin_memset(state->M + nrf, 0, (len - nrf) * sizeof(char));
      state->M[len] = (char)0x80;
    }
}


/**
 * Perform the absorption phase
 * 
 * @param  state  The hashing state
 * @param  len    The number of bytes from `state->M` to absorb
 */
static __attribute__((nonnull, nothrow))
void libkeccak_absorption_phase(register libkeccak_state_t* restrict state, register size_t len)
{
  register long rr = state->r >> 3;
  register long ww = state->w >> 3;
  register long n = (long)len / rr;
  register const char* restrict message = state->M;
  if (__builtin_expect(ww >= 8, 1)) /* ww > 8 is impossible, it is just for optimisation possibilities. */
    while (n--)
      {
#define X(N)  state->S[N] ^= libkeccak_to_lane64(message, len, rr, (size_t)(LANE_TRANSPOSE_MAP[N] * 8));
	LIST_25
#undef X
	libkeccak_f(state);
	message += (size_t)rr;
	len -= (size_t)rr;
      }
  else
    while (n--)
      {
#define X(N)  state->S[N] ^= libkeccak_to_lane(message, len, rr, ww, (size_t)(LANE_TRANSPOSE_MAP[N] * ww));
	LIST_25
#undef X
	libkeccak_f(state);
	message += (size_t)rr;
	len -= (size_t)rr;
      }
}


/**
 * Perform the squeezing phase
 * 
 * @param  state    The hashing state
 * @param  rr       The bitrate in bytes
 * @param  nn       The output size in bytes, rounded up to whole bytes
 * @param  ww       The word size in bytes
 * @param  hashsum  Output paramter for the hashsum
 */
static __attribute__((nonnull, nothrow, hot))
void libkeccak_squeezing_phase(register libkeccak_state_t* restrict state, long rr,
			       long nn, long ww, register char* restrict hashsum)
{
  register int_fast64_t v;
  register long ni = rr / ww;
  auto long olen = state->n;
  auto long i, j = 0;
  register long k;
  while (olen > 0)
    {
      for (i = 0; (i < ni) && (j < nn); i++)
	{
	  v = state->S[LANE_TRANSPOSE_MAP[i]];
	  for (k = 0; (k++ < ww) && (j++ < nn); v >>= 8)
	    *hashsum++ = (char)v;
	}
      if (olen -= state->r, olen > 0)
	libkeccak_f(state);
    }
  if (state->n & 7)
    hashsum[-1] &= (char)((1 << (state->n & 7)) - 1);
}


/**
 * Absorb more of the message to the Keccak sponge
 * without wiping sensitive data when possible
 * 
 * @param   state   The hashing state
 * @param   msg     The partial message
 * @param   msglen  The length of the partial message
 * @return          Zero on success, -1 on error
 */
int libkeccak_fast_update(libkeccak_state_t* restrict state, const char* restrict msg, size_t msglen)
{
  size_t len;
  auto char* restrict new;
  
  if (__builtin_expect(state->mptr + msglen > state->mlen, 0))
    {
      state->mlen += msglen;
      new = realloc(state->M, state->mlen * sizeof(char));
      if (new == NULL)
	return state->mlen -= msglen, -1;
      state->M = new;
    }
  
  __builtin_memcpy(state->M + state->mptr, msg, msglen * sizeof(char));
  state->mptr += msglen;
  len = state->mptr;
  len -= state->mptr % (size_t)((state->r * state->b) >> 3);
  state->mptr -= len;
  
  libkeccak_absorption_phase(state, len);
  __builtin_memmove(state->M, state->M + len, state->mptr * sizeof(char));
  
  return 0;
}


/**
 * Absorb more of the message to the Keccak sponge
 * and wipe sensitive data when possible
 * 
 * @param   state   The hashing state
 * @param   msg     The partial message
 * @param   msglen  The length of the partial message
 * @return          Zero on success, -1 on error
 */
int libkeccak_update(libkeccak_state_t* restrict state, const char* restrict msg, size_t msglen)
{
  size_t len;
  auto char* restrict new;
  
  if (__builtin_expect(state->mptr + msglen > state->mlen, 0))
    {
      state->mlen += msglen;
      new = malloc(state->mlen * sizeof(char));
      if (new == NULL)
	return state->mlen -= msglen, -1;
      libkeccak_state_wipe_message(state);
      free(state->M);
      state->M = new;
    }
  
  __builtin_memcpy(state->M + state->mptr, msg, msglen * sizeof(char));
  state->mptr += msglen;
  len = state->mptr;
  len -= state->mptr % (size_t)((state->r * state->b) >> 3);
  state->mptr -= len;
  
  libkeccak_absorption_phase(state, len);
  __builtin_memmove(state->M, state->M + len, state->mptr * sizeof(char));
  
  return 0;
}


/**
 * Absorb the last part of the message and squeeze the Keccak sponge
 * without wiping sensitive data when possible
 * 
 * @param   state    The hashing state
 * @param   msg      The rest of the message, may be `NULL`, may be modified
 * @param   msglen   The length of the partial message
 * @param   bits     The number of bits at the end of the message not covered by `msglen`
 * @param   suffix   The suffix concatenate to the message, only '1':s and '0':s, and NUL-termination
 * @param   hashsum  Output paramter for the hashsum, may be `NULL`
 * @return           Zero on success, -1 on error
 */
int libkeccak_fast_digest(libkeccak_state_t* restrict state, const char* restrict msg, size_t msglen,
			  size_t bits, const char* restrict suffix, char* restrict hashsum)
{
  auto char* restrict new;
  register long rr = state->r >> 3;
  auto size_t suffix_len = suffix ? __builtin_strlen(suffix) : 0;
  register size_t ext;
  register long i;
  
  if (msg == NULL)
    msglen = bits = 0;
  else
    msglen += bits >> 3, bits &= 7;
  
  ext = msglen + ((bits + suffix_len + 7) >> 3) + (size_t)rr;
  if (__builtin_expect(state->mptr + ext > state->mlen, 0))
    {
      state->mlen += ext;
      new = realloc(state->M, state->mlen * sizeof(char));
      if (new == NULL)
	return state->mlen -= ext, -1;
      state->M = new;
    }
  
  if (msglen)
    __builtin_memcpy(state->M + state->mptr, msg, msglen * sizeof(char));
  state->mptr += msglen;
  
  if (bits)
    state->M[state->mptr] = msg[msglen] & (char)((1 << bits) - 1);
  if (__builtin_expect(!!suffix_len, 1))
    {
      if (bits == 0)
	state->M[state->mptr] = 0;
      while (suffix_len--)
	{
	  state->M[state->mptr] |= (char)((*suffix++ & 1) << bits++);
	  if (bits == 8)
	    bits = 0, state->M[++(state->mptr)] = 0;
	}
    }
  if (bits)
    state->mptr++;
  
  libkeccak_pad10star1(state, bits);
  libkeccak_absorption_phase(state, state->mptr);
  
  if (hashsum != NULL)
    libkeccak_squeezing_phase(state, rr, (state->n + 7) >> 3, state->w >> 3, hashsum);
  else
    for (i = (state->n - 1) / state->r; i--;)
      libkeccak_f(state);
  
  return 0;
}


/**
 * Absorb the last part of the message and squeeze the Keccak sponge
 * and wipe sensitive data when possible
 * 
 * @param   state    The hashing state
 * @param   msg      The rest of the message, may be `NULL`, may be modified
 * @param   msglen   The length of the partial message
 * @param   bits     The number of bits at the end of the message not covered by `msglen`
 * @param   suffix   The suffix concatenate to the message, only '1':s and '0':s, and NUL-termination
 * @param   hashsum  Output paramter for the hashsum, may be `NULL`
 * @return           Zero on success, -1 on error
 */
int libkeccak_digest(libkeccak_state_t* restrict state, const char* restrict msg, size_t msglen,
		     size_t bits, const char* restrict suffix, char* restrict hashsum)
{
  auto char* restrict new;
  register long rr = state->r >> 3;
  auto size_t suffix_len = suffix ? __builtin_strlen(suffix) : 0;
  register size_t ext;
  register long i;
  
  if (msg == NULL)
    msglen = bits = 0;
  else
    msglen += bits >> 3, bits &= 7;
  
  ext = msglen + ((bits + suffix_len + 7) >> 3) + (size_t)rr;
  if (__builtin_expect(state->mptr + ext > state->mlen, 0))
    {
      state->mlen += ext;
      new = malloc(state->mlen * sizeof(char));
      if (new == NULL)
	return state->mlen -= ext, -1;
      libkeccak_state_wipe_message(state);
      free(state->M);
      state->M = new;
    }
  
  if (msglen)
    __builtin_memcpy(state->M + state->mptr, msg, msglen * sizeof(char));
  state->mptr += msglen;
  
  if (bits)
    state->M[state->mptr] = msg[msglen] & (char)((1 << bits) - 1);
  if (__builtin_expect(!!suffix_len, 1))
    {
      if (bits == 0)
	state->M[state->mptr] = 0;
      while (suffix_len--)
	{
	  state->M[state->mptr] |= (char)((*suffix++ & 1) << bits++);
	  if (bits == 8)
	    bits = 0, state->M[++(state->mptr)] = 0;
	}
    }
  if (bits)
    state->mptr++;
  
  libkeccak_pad10star1(state, bits);
  libkeccak_absorption_phase(state, state->mptr);
  
  if (hashsum != NULL)
    libkeccak_squeezing_phase(state, rr, (state->n + 7) >> 3, state->w >> 3, hashsum);
  else
    for (i = (state->n - 1) / state->r; i--;)
      libkeccak_f(state);
  
  return 0;
}


/**
 * Force some rounds of Keccak-f
 * 
 * @param  state  The hashing state
 * @param  times  The number of rounds
 */
void libkeccak_simple_squeeze(register libkeccak_state_t* restrict state, register long times)
{
  while (times--)
    libkeccak_f(state);
}


/**
 * Squeeze as much as is needed to get a digest a number of times
 * 
 * @param  state  The hashing state
 * @param  times  The number of digests
 */
void libkeccak_fast_squeeze(register libkeccak_state_t* restrict state, register long times)
{
  times *= (state->n - 1) / state->r + 1;
  while (times--)
    libkeccak_f(state);
}


/**
 * Squeeze out another digest
 * 
 * @param  state    The hashing state
 * @param  hashsum  Output paramter for the hashsum
 */
void libkeccak_squeeze(register libkeccak_state_t* restrict state, register char* restrict hashsum)
{
  libkeccak_f(state);
  libkeccak_squeezing_phase(state, state->r >> 3, (state->n + 7) >> 3, state->w >> 3, hashsum);
}