text: #include "os.h"
  #include <libsec.h>
  
  /*
   *  rfc1321 requires that I include this.  The code is new.  The constants
   *  all come from the rfc (hence the copyright).  We trade a table for the
   *  macros in rfc.  The total size is a lot less. -- presotto
   *
   *	Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
   *	rights reserved.
   *
   *	License to copy and use this software is granted provided that it
   *	is identified as the "RSA Data Security, Inc. MD5 Message-Digest
   *	Algorithm" in all material mentioning or referencing this software
   *	or this function.
   *
   *	License is also granted to make and use derivative works provided
   *	that such works are identified as "derived from the RSA Data
   *	Security, Inc. MD5 Message-Digest Algorithm" in all material
   *	mentioning or referencing the derived work.
   *
   *	RSA Data Security, Inc. makes no representations concerning either
   *	the merchantability of this software or the suitability of this
   *	software forany particular purpose. It is provided "as is"
   *	without express or implied warranty of any kind.
   *	These notices must be retained in any copies of any part of this
   *	documentation and/or software.
   */
  
  static void encode(uint8_t*, uint32_t*, uint32_t);
  
  extern void _md5block(uint8_t*, uint32_t, uint32_t*);
  
  MD5state*
  md5(uint8_t *p, uint32_t len, uint8_t *digest, MD5state *s)
  {
  	uint32_t x[16];
  	uint8_t buf[128];
  	int i;
  	uint8_t *e;
  
  	if(s == nil){
  		s = jehanne_malloc(sizeof(*s));
  		if(s == nil)
  			return nil;
  		jehanne_memset(s, 0, sizeof(*s));
  		s->malloced = 1;
  	}
  
  	if(s->seeded == 0){
  		/* seed the state, these constants would look nicer big-endian */
  		s->state[0] = 0x67452301;
  		s->state[1] = 0xefcdab89;
  		s->state[2] = 0x98badcfe;
  		s->state[3] = 0x10325476;
  		s->seeded = 1;
  	}
  
  	/* fill out the partial 64 byte block from previous calls */
  	if(s->blen){
  		i = 64 - s->blen;
  		if(len < i)
  			i = len;
  		jehanne_memmove(s->buf + s->blen, p, i);
  		len -= i;
  		s->blen += i;
  		p += i;
  		if(s->blen == 64){
  			_md5block(s->buf, s->blen, s->state);
  			s->len += s->blen;
  			s->blen = 0;
  		}
  	}
  
  	/* do 64 byte blocks */
  	i = len & ~0x3f;
  	if(i){
  		_md5block(p, i, s->state);
  		s->len += i;
  		len -= i;
  		p += i;
  	}
  
  	/* save the left overs if not last call */
  	if(digest == 0){
  		if(len){
  			jehanne_memmove(s->buf, p, len);
  			s->blen += len;
  		}
  		return s;
  	}
  
  	/*
  	 *  this is the last time through, pad what's left with 0x80,
  	 *  0's, and the input count to create a multiple of 64 bytes
  	 */
  	if(s->blen){
  		p = s->buf;
  		len = s->blen;
  	} else {
  		jehanne_memmove(buf, p, len);
  		p = buf;
  	}
  	s->len += len;
  	e = p + len;
  	if(len < 56)
  		i = 56 - len;
  	else
  		i = 120 - len;
  	jehanne_memset(e, 0, i);
  	*e = 0x80;
  	len += i;
  
  	/* append the count */
  	x[0] = s->len<<3;
  	x[1] = s->len>>29;
  	encode(p+len, x, 8);
  
  	/* digest the last part */
  	_md5block(p, len+8, s->state);
  	s->len += len;
  
  	/* return result and free state */
  	encode(digest, s->state, MD5dlen);
  	if(s->malloced == 1)
  		jehanne_free(s);
  	return nil;
  }
  
  /*
   *	encodes input (uint32_t) into output (uint8_t). Assumes len is
   *	a multiple of 4.
   */
  static void
  encode(uint8_t *output, uint32_t *input, uint32_t len)
  {
  	uint32_t x;
  	uint8_t *e;
  
  	for(e = output + len; output < e;) {
  		x = *input++;
  		*output++ = x;
  		*output++ = x >> 8;
  		*output++ = x >> 16;
  		*output++ = x >> 24;
  	}
  }
  
  DigestState*
  hmac_md5(uint8_t *p, uint32_t len, uint8_t *key, uint32_t klen, uint8_t *digest,
  	DigestState *s)
  {
  	return hmac_x(p, len, key, klen, digest, s, md5, MD5dlen);
  }
  