Index: src/sha1.c
==================================================================
--- src/sha1.c
+++ src/sha1.c
@@ -199,10 +199,71 @@
 	    digest[i] = (unsigned char)
 		((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
     }
 }
 
+typedef struct HMAC_SHA1Context HMAC_SHA1Context;
+struct HMAC_SHA1Context {
+  SHA1Context inner;
+  SHA1Context outer;
+};
+
+static void HMAC_SHA1Init(
+  HMAC_SHA1Context *context,
+  const unsigned char *key,
+  unsigned int keylen
+){
+  unsigned char pad[64]; /* 64 is SHA-1 block length */
+  unsigned char keyhash[20];
+  unsigned int i;
+  const unsigned char *k = key;
+
+  if( keylen > sizeof(pad) ){
+    /* Key is too big, hash it, and use this hash as a key */
+    SHA1Init(&context->inner);
+    SHA1Update(&context->inner, k, keylen);
+    SHA1Final(&context->inner, keyhash);
+    k = keyhash;
+    keylen = sizeof(keyhash);
+  }
+
+  /* Initialize inner hash */
+  SHA1Init(&context->inner);
+  memset(pad, 0x36, sizeof(pad));
+  for( i=0; i<keylen; i++ ){ pad[i] ^= k[i]; }
+  SHA1Update(&context->inner, pad, sizeof(pad));
+
+  /* Initialize outer hash */
+  SHA1Init(&context->outer);
+  memset(pad, 0x5C, sizeof(pad));
+  for( i=0; i<keylen; i++ ){ pad[i] ^= k[i]; }
+  SHA1Update(&context->outer, pad, sizeof(pad));
+
+  /* Cleanup */
+  memset(keyhash, 0, sizeof(keyhash));
+}
+
+static void HMAC_SHA1Update(
+  HMAC_SHA1Context *context,
+  const unsigned char *data,
+  unsigned int len
+){
+  SHA1Update(&context->inner, data, len);
+}
+
+static void HMAC_SHA1Final(
+  HMAC_SHA1Context *context,
+  unsigned char digest[20]
+){
+  unsigned char innerDigest[20];
+  SHA1Final(&context->inner, innerDigest);
+  /* Mix inner into outer */
+  SHA1Update(&context->outer, innerDigest, sizeof(innerDigest));
+  SHA1Final(&context->outer, digest);
+  /* Cleanup */
+  memset(innerDigest, 0, sizeof(innerDigest));
+}
 
 /*
 ** Convert a digest into base-16.  digest should be declared as
 ** "unsigned char digest[20]" in the calling function.  The SHA1
 ** digest is stored in the first 20 bytes.  zBuf should
@@ -350,10 +411,103 @@
   SHA1Update(&ctx, (unsigned const char*)zIn, strlen(zIn));
   SHA1Final(&ctx, zResult);
   DigestToBase16(zResult, zDigest);
   return mprintf("%s", zDigest);
 }
+
+/*
+** Compute HMAC-SHA1 of a zero-terminated string.  The
+** result (hex string) is held in memory obtained from mprintf().
+*/
+char *hmac_sign(const char *zKey, const char *zData){
+  HMAC_SHA1Context ctx;
+  unsigned char zResult[20];
+  char zDigest[41];
+
+  HMAC_SHA1Init(&ctx, (unsigned const char *)zKey, strlen(zKey));
+  HMAC_SHA1Update(&ctx, (unsigned const char*)zData, strlen(zData));
+  HMAC_SHA1Final(&ctx, zResult);
+  DigestToBase16(zResult, zDigest);
+  return mprintf("%s", zDigest);
+}
+
+/*
+** Like hmac_sign(), but uses double-HMAC construction:
+** HMAC(HMAC(zKey, zData), zData)
+*/
+char *hmac_double_sign(const char *zKey, const char *zData){
+  HMAC_SHA1Context ctx;
+  unsigned char zInterKey[20], zResult[20];
+  char zDigest[41];
+
+  /* First derive intermediate key */
+  HMAC_SHA1Init(&ctx, (unsigned const char *)zKey, strlen(zKey));
+  HMAC_SHA1Update(&ctx, (unsigned const char*)zData, strlen(zData));
+  HMAC_SHA1Final(&ctx, zInterKey);
+  /* Use intermediate key to sign data */
+  HMAC_SHA1Init(&ctx, zInterKey, sizeof(zInterKey));
+  HMAC_SHA1Update(&ctx, (unsigned const char *)zData, strlen(zData));
+  HMAC_SHA1Final(&ctx, zResult);
+
+  DigestToBase16(zResult, zDigest);
+  return mprintf("%s", zDigest);
+}
+
+/*
+** Constant-time comparison of buffers b1 and b2, which both have length len
+** Returns 0 on successful verification, any other number on failure.
+*/
+static int verify_bytes(
+  const unsigned char *b1,
+  const unsigned char *b2,
+  unsigned int len
+){
+  unsigned int i;
+  unsigned char rc = 0;
+  if( len==0 ) return 1;
+  for( i=0; i<len; i++){
+    rc |= b1[i] ^ b2[i];
+  }
+  return rc;
+}
+
+/*
+** Verify that hex string zDigest (result of hmac_sign) is the one
+** that was used to sign zData with secret key zKey.
+** All strings are zero-terminated.
+** Returns 0 on successful verification, any other number on failure.
+*/
+int hmac_verify(const char *zDigest, const char *zKey, const char *zData)
+{
+  char *zCalcDigest;
+  unsigned int len, i, rc = 0;
+
+  zCalcDigest = hmac_sign(zKey, zData);
+  len = strlen(zCalcDigest);
+  if( len==0 || len != strlen(zDigest) ) return 1;
+  rc = verify_bytes((const unsigned char*)zDigest,
+                    (const unsigned char*)zCalcDigest, len);
+  fossil_free(zCalcDigest);
+  return rc;
+}
+
+/*
+** Like hmac_verify(), but uses double-HMAC.
+*/
+int hmac_double_verify(const char *zDigest, const char *zKey, const char *zData)
+{
+  char *zCalcDigest;
+  unsigned int len, i, rc = 0;
+
+  zCalcDigest = hmac_double_sign(zKey, zData);
+  len = strlen(zCalcDigest);
+  if( len==0 || len != strlen(zDigest) ) return 1;
+  rc = verify_bytes((const unsigned char*)zDigest,
+                    (const unsigned char*)zCalcDigest, len);
+  fossil_free(zCalcDigest);
+  return rc;
+}
 
 /*
 ** Convert a cleartext password for a specific user into a SHA1 hash.
 ** 
 ** The algorithm here is:
@@ -460,5 +614,29 @@
     }
     fossil_print("%s  %s\n", blob_str(&cksum), g.argv[i]);
     blob_reset(&cksum);
   }
 }
+
+/*
+** COMMAND: test-hmac
+** %fossil test-hmac secret data
+*/
+void hmac_test(void){
+  assert( g.argc==4 );
+  fossil_print("HMAC(%s, %s) = ", g.argv[2], g.argv[3]);
+  fossil_print("%s\n", hmac_sign(g.argv[2], g.argv[3]));
+}
+
+/*
+** COMMAND: test-hmac-verify
+** %fossil test-hmac-verify digest secret data
+*/
+void hmac_verify_test(void){
+  assert( g.argc==5 );
+  if( hmac_verify(g.argv[2], g.argv[3], g.argv[4])==0 ){
+    fossil_print("successfuly verified\n");
+  }else{
+    fossil_print("not verified\n");
+  }
+}
+