text: /* $OpenBSD: wp_dgst.c,v 1.3 2014/06/12 15:49:31 deraadt Exp $ */
  /**
   * The Whirlpool hashing function.
   *
   * <P>
   * <b>References</b>
   *
   * <P>
   * The Whirlpool algorithm was developed by
   * <a href="mailto:pbarreto@scopus.com.br">Paulo S. L. M. Barreto</a> and
   * <a href="mailto:vincent.rijmen@cryptomathic.com">Vincent Rijmen</a>.
   *
   * See
   *      P.S.L.M. Barreto, V. Rijmen,
   *      ``The Whirlpool hashing function,''
   *      NESSIE submission, 2000 (tweaked version, 2001),
   *      <https://www.cosic.esat.kuleuven.ac.be/nessie/workshop/submissions/whirlpool.zip>
   *
   * Based on "@version 3.0 (2003.03.12)" by Paulo S.L.M. Barreto and
   * Vincent Rijmen. Lookup "reference implementations" on
   * <http://planeta.terra.com.br/informatica/paulobarreto/>
   *
   * =============================================================================
   *
   * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS
   * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
   * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE
   * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
   * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
   * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
   * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
   * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   *
   */
  
  /*
   * OpenSSL-specific implementation notes.
   *
   * WHIRLPOOL_Update as well as one-stroke WHIRLPOOL both expect
   * number of *bytes* as input length argument. Bit-oriented routine
   * as specified by authors is called WHIRLPOOL_BitUpdate[!] and
   * does not have one-stroke counterpart.
   *
   * WHIRLPOOL_BitUpdate implements byte-oriented loop, essentially
   * to serve WHIRLPOOL_Update. This is done for performance.
   *
   * Unlike authors' reference implementation, block processing
   * routine whirlpool_block is designed to operate on multi-block
   * input. This is done for perfomance.
   */
  
  #include "wp_locl.h"
  #include <openssl/crypto.h>
  #include <string.h>
  
  int WHIRLPOOL_Init(WHIRLPOOL_CTX *c)
  	{
  	memset (c,0,sizeof(*c));
  	return(1);
  	}
  
  int WHIRLPOOL_Update	(WHIRLPOOL_CTX *c,const void *_inp,size_t bytes)
  	{
  	/* Well, largest suitable chunk size actually is
  	 * (1<<(sizeof(size_t)*8-3))-64, but below number
  	 * is large enough for not to care about excessive
  	 * calls to WHIRLPOOL_BitUpdate... */
  	size_t chunk = ((size_t)1)<<(sizeof(size_t)*8-4);
  	const unsigned char *inp = _inp;
  
  	while (bytes>=chunk)
  		{
  		WHIRLPOOL_BitUpdate(c,inp,chunk*8);
  		bytes -= chunk;
  		inp   += chunk;
  		}
  	if (bytes)
  		WHIRLPOOL_BitUpdate(c,inp,bytes*8);
  
  	return(1);
  	}
  
  void WHIRLPOOL_BitUpdate(WHIRLPOOL_CTX *c,const void *_inp,size_t bits)
  	{
  	size_t		n;
  	unsigned int	bitoff = c->bitoff,
  			bitrem = bitoff%8,
  			inpgap = (8-(unsigned int)bits%8)&7;
  	const unsigned char *inp=_inp;
  
  	/* This 256-bit increment procedure relies on the size_t
  	 * being natural size of CPU register, so that we don't
  	 * have to mask the value in order to detect overflows. */
  	c->bitlen[0] += bits;
  	if (c->bitlen[0] < bits)	/* overflow */
  		{
  		n = 1;
  		do  { 	c->bitlen[n]++;
  		    } while(c->bitlen[n]==0
  		   	    && ++n<(WHIRLPOOL_COUNTER/sizeof(size_t)));
  		}
  
  #ifndef OPENSSL_SMALL_FOOTPRINT
  	reconsider:
  	if (inpgap==0 && bitrem==0)	/* byte-oriented loop */
  		{
  		while (bits)
  			{
  			if (bitoff==0 && (n=bits/WHIRLPOOL_BBLOCK))
  				{
  				whirlpool_block(c,inp,n);
  				inp  += n*WHIRLPOOL_BBLOCK/8;
  				bits %= WHIRLPOOL_BBLOCK;
  				}
  			else
  				{
  				unsigned int byteoff = bitoff/8;
  
  				bitrem = WHIRLPOOL_BBLOCK - bitoff;/* re-use bitrem */
  				if (bits >= bitrem)
  					{
  					bits -= bitrem;
  					bitrem /= 8;
  					memcpy(c->data+byteoff,inp,bitrem);
  					inp  += bitrem;
  					whirlpool_block(c,c->data,1);
  					bitoff = 0;
  					}
  				else
  					{
  					memcpy(c->data+byteoff,inp,bits/8);
  					bitoff += (unsigned int)bits;
  					bits = 0;
  					}
  				c->bitoff = bitoff;
  				}
  			}
  		}
  	else				/* bit-oriented loop */
  #endif
  		{
  		/*
  			   inp
  			   |
  			   +-------+-------+-------
  			      |||||||||||||||||||||
  			   +-------+-------+-------
  		+-------+-------+-------+-------+-------
  		||||||||||||||				c->data
  		+-------+-------+-------+-------+-------
  			|
  			c->bitoff/8
  		*/
  		while (bits)
  			{
  			unsigned int	byteoff	= bitoff/8;
  			unsigned char	b;
  
  #ifndef OPENSSL_SMALL_FOOTPRINT
  			if (bitrem==inpgap)
  				{
  				c->data[byteoff++] |= inp[0] & (0xff>>inpgap);
  				inpgap = 8-inpgap;
  				bitoff += inpgap;  bitrem = 0;	/* bitoff%8 */
  				bits   -= inpgap;  inpgap = 0;	/* bits%8   */
  				inp++;
  				if (bitoff==WHIRLPOOL_BBLOCK)
  					{
  					whirlpool_block(c,c->data,1);
  					bitoff = 0;
  					}
  				c->bitoff = bitoff;
  				goto reconsider;
  				}
  			else
  #endif
  			if (bits>=8)
  				{
  				b  = ((inp[0]<<inpgap) | (inp[1]>>(8-inpgap)));
  				b &= 0xff;
  				if (bitrem)	c->data[byteoff++] |= b>>bitrem;
  				else		c->data[byteoff++]  = b;
  				bitoff += 8;
  				bits   -= 8;
  				inp++;
  				if (bitoff>=WHIRLPOOL_BBLOCK)
  					{
  					whirlpool_block(c,c->data,1);
  					byteoff  = 0;
  					bitoff  %= WHIRLPOOL_BBLOCK;
  					}
  				if (bitrem)	c->data[byteoff] = b<<(8-bitrem);
  				}
  			else	/* remaining less than 8 bits */
  				{
  				b = (inp[0]<<inpgap)&0xff;
  				if (bitrem)	c->data[byteoff++] |= b>>bitrem;
  				else		c->data[byteoff++]  = b;
  				bitoff += (unsigned int)bits;
  				if (bitoff==WHIRLPOOL_BBLOCK)
  					{
  					whirlpool_block(c,c->data,1);
  					byteoff  = 0;
  			        	bitoff  %= WHIRLPOOL_BBLOCK;
  					}
  				if (bitrem)	c->data[byteoff] = b<<(8-bitrem);
  				bits = 0;
  				}
  			c->bitoff = bitoff;
  			}
  		}
  	}
  
  int WHIRLPOOL_Final	(unsigned char *md,WHIRLPOOL_CTX *c)
  	{
  	unsigned int	bitoff  = c->bitoff,
  			byteoff = bitoff/8;
  	size_t		i,j,v;
  	unsigned char  *p;
  
  	bitoff %= 8;
  	if (bitoff)	c->data[byteoff] |= 0x80>>bitoff;
  	else		c->data[byteoff]  = 0x80;
  	byteoff++;
  
  	/* pad with zeros */
  	if (byteoff > (WHIRLPOOL_BBLOCK/8-WHIRLPOOL_COUNTER))
  		{
  		if (byteoff<WHIRLPOOL_BBLOCK/8)
  			memset(&c->data[byteoff],0,WHIRLPOOL_BBLOCK/8-byteoff);
  		whirlpool_block(c,c->data,1);
  		byteoff = 0;
  		}
  	if (byteoff < (WHIRLPOOL_BBLOCK/8-WHIRLPOOL_COUNTER))
  		memset(&c->data[byteoff],0,
  			(WHIRLPOOL_BBLOCK/8-WHIRLPOOL_COUNTER)-byteoff);
  	/* smash 256-bit c->bitlen in big-endian order */
  	p = &c->data[WHIRLPOOL_BBLOCK/8-1];	/* last byte in c->data */
  	for(i=0;i<WHIRLPOOL_COUNTER/sizeof(size_t);i++)
  		for(v=c->bitlen[i],j=0;j<sizeof(size_t);j++,v>>=8)
  			*p-- = (unsigned char)(v&0xff);
  
  	whirlpool_block(c,c->data,1);
  
  	if (md)	{
  		memcpy(md,c->H.c,WHIRLPOOL_DIGEST_LENGTH);
  		memset(c,0,sizeof(*c));
  		return(1);
  		}
  	return(0);
  	}
  
  unsigned char *WHIRLPOOL(const void *inp, size_t bytes,unsigned char *md)
  	{
  	WHIRLPOOL_CTX ctx;
  	static unsigned char m[WHIRLPOOL_DIGEST_LENGTH];
  
  	if (md == NULL) md=m;
  	WHIRLPOOL_Init(&ctx);
  	WHIRLPOOL_Update(&ctx,inp,bytes);
  	WHIRLPOOL_Final(md,&ctx);
  	return(md);
  	}
  