Login
Artifact [185e35dd49]
Login

Artifact 185e35dd4937265e4c77ac7d87eb7c9f48e0a05d:


/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 
/* vim: set ts=2 et sw=2 tw=80: */
/*
   Copyright (c) 2013 D. Richard Hipp
  
   This program is free software; you can redistribute it and/or
   modify it under the terms of the Simplified BSD License (also
   known as the "2-Clause License" or "FreeBSD License".)
  
   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.
  
   Author contact information:
     drh@hwaci.com
     http://www.hwaci.com/drh/
  
  *****************************************************************************
  
*/
#include "fossil-scm/fossil.h"
#include <assert.h>
#include <string.h> /* strlen() */
#include <stddef.h> /* NULL on linux */

#include <sys/types.h>

/*
   The SHA1 implementation below is adapted from:
  
    $NetBSD: sha1.c,v 1.6 2009/11/06 20:31:18 joerg Exp $
    $OpenBSD: sha1.c,v 1.9 1997/07/23 21:12:32 kstailey Exp $
  
   SHA-1 in C
   By Steve Reid <steve@edmweb.com>
   100% Public Domain
*/

/*
 * blk0() and blk() perform the initial expand.
 * I got the idea of expanding during the round function from SSLeay
 *
 * blk0le() for little-endian and blk0be() for big-endian.
 */
#if 0 && __GNUC__ && (defined(__i386__) || defined(__x86_64__))
/*
 * GCC by itself only generates left rotates.  Use right rotates if
 * possible to be kinder to dinky implementations with iterative rotate
 * instructions.
 */
#define SHA_ROT(op, x, k)                                               \
  ({ unsigned int y; asm(op " %1,%0" : "=r" (y) : "I" (k), "0" (x)); y; })
#define rol(x,k) SHA_ROT("roll", x, k)
#define ror(x,k) SHA_ROT("rorl", x, k)

#else
/* Generic C equivalent */
#define SHA_ROT(x,l,r) ((x) << (l) | (x) >> (r))
#define rol(x,k) SHA_ROT(x,k,32-(k))
#define ror(x,k) SHA_ROT(x,32-(k),k)
#endif


#define blk0le(i) (block[i] = (ror(block[i],8)&0xFF00FF00)  \
                   |(rol(block[i],8)&0x00FF00FF))
#define blk0be(i) block[i]
#define blk(i) (block[i&15] = rol(block[(i+13)&15]^block[(i+8)&15]  \
                                  ^block[(i+2)&15]^block[i&15],1))

/*
 * (R0+R1), R2, R3, R4 are the different operations (rounds) used in SHA1
 *
 * Rl0() for little-endian and Rb0() for big-endian.  Endianness is 
 * determined at run-time.
 */
#define Rl0(v,w,x,y,z,i)                                        \
  z+=((w&(x^y))^y)+blk0le(i)+0x5A827999+rol(v,5);w=ror(w,2);
#define Rb0(v,w,x,y,z,i)                                        \
  z+=((w&(x^y))^y)+blk0be(i)+0x5A827999+rol(v,5);w=ror(w,2);
#define R1(v,w,x,y,z,i)                                     \
  z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=ror(w,2);
#define R2(v,w,x,y,z,i)                             \
  z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=ror(w,2);
#define R3(v,w,x,y,z,i)                                         \
  z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=ror(w,2);
#define R4(v,w,x,y,z,i)                             \
  z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=ror(w,2);

/*
 * Hash a single 512-bit block. This is the core of the algorithm.
 */
#define a qq[0]
#define b qq[1]
#define c qq[2]
#define d qq[3]
#define e qq[4]

static void SHA1Transform(unsigned int state[5], const unsigned char buffer[64])
{
  unsigned int qq[5]; /* a, b, c, d, e; */
  static int one = 1;
  unsigned int block[16];
  memcpy(block, buffer, 64);
  memcpy(qq,state,5*sizeof(unsigned int));

  /* Copy context->state[] to working vars */
  /*
    a = state[0];
    b = state[1];
    c = state[2];
    d = state[3];
    e = state[4];
  */

  /* 4 rounds of 20 operations each. Loop unrolled. */
  if( 1 == *(unsigned char*)&one ){
    Rl0(a,b,c,d,e, 0); Rl0(e,a,b,c,d, 1); Rl0(d,e,a,b,c, 2); Rl0(c,d,e,a,b, 3);
    Rl0(b,c,d,e,a, 4); Rl0(a,b,c,d,e, 5); Rl0(e,a,b,c,d, 6); Rl0(d,e,a,b,c, 7);
    Rl0(c,d,e,a,b, 8); Rl0(b,c,d,e,a, 9); Rl0(a,b,c,d,e,10); Rl0(e,a,b,c,d,11);
    Rl0(d,e,a,b,c,12); Rl0(c,d,e,a,b,13); Rl0(b,c,d,e,a,14); Rl0(a,b,c,d,e,15);
  }else{
    Rb0(a,b,c,d,e, 0); Rb0(e,a,b,c,d, 1); Rb0(d,e,a,b,c, 2); Rb0(c,d,e,a,b, 3);
    Rb0(b,c,d,e,a, 4); Rb0(a,b,c,d,e, 5); Rb0(e,a,b,c,d, 6); Rb0(d,e,a,b,c, 7);
    Rb0(c,d,e,a,b, 8); Rb0(b,c,d,e,a, 9); Rb0(a,b,c,d,e,10); Rb0(e,a,b,c,d,11);
    Rb0(d,e,a,b,c,12); Rb0(c,d,e,a,b,13); Rb0(b,c,d,e,a,14); Rb0(a,b,c,d,e,15);
  }
  R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
  R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
  R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
  R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
  R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
  R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
  R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
  R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
  R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
  R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
  R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
  R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
  R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
  R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
  R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
  R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);

  /* Add the working vars back into context.state[] */
  state[0] += a;
  state[1] += b;
  state[2] += c;
  state[3] += d;
  state[4] += e;
}


void fsl_sha1_init(fsl_sha1_cx *context){
  /* SHA1 initialization constants */
  *context = fsl_sha1_cx_empty;
}

/*
 * Run your data through this.
 */
void fsl_sha1_update( fsl_sha1_cx *context,
                      void const * data_,
                      fsl_size_t len ){
  unsigned int i, j;
  const unsigned char *data = (const unsigned char *)data_;
  j = context->count[0];
  if ((context->count[0] += len << 3) < j)
	context->count[1] += (len>>29)+1;
  j = (j >> 3) & 63;
  if ((j + len) > 63) {
    memcpy(&context->buffer[j], data, (i = 64-j));
    SHA1Transform(context->state, context->buffer);
    for ( ; i + 63 < len; i += 64)
      SHA1Transform(context->state, &data[i]);
    j = 0;
  } else {
    i = 0;
  }
  (void)memcpy(&context->buffer[j], &data[i], len - i);
}


void fsl_sha1_final(fsl_sha1_cx *context, unsigned char * digest){
  unsigned int i;
  unsigned char finalcount[8];

  for (i = 0; i < 8; i++) {
	finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)]
                                     >> ((3-(i & 3)) * 8) ) & 255);	 /* Endian independent */
  }
  fsl_sha1_update(context, (const unsigned char *)"\200", 1);
  while ((context->count[0] & 504) != 448){
    fsl_sha1_update(context, (const unsigned char *)"\0", 1);
  }
  fsl_sha1_update(context, finalcount, 8);  /* Should cause a SHA1Transform() */
  if (digest) {
    for (i = 0; i < 20; i++){
      digest[i] = (unsigned char)
        ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
    }
  }
}


/*
   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
   be "char zBuf[41]".
*/
void fsl_sha1_digest_to_base16(unsigned char *digest, char *zBuf){
  static char const zEncode[] = "0123456789abcdef";
  int ix;

  for(ix=0; ix<20; ix++){
    *zBuf++ = zEncode[(*digest>>4)&0xf];
    *zBuf++ = zEncode[*digest++ & 0xf];
  }
  *zBuf = '\0';
}

/*
   The state of a incremental SHA1 checksum computation.  Only one
   such computation can be underway at a time, of course.
*/
/* static fsl_sha1_cx incrCtx; */

#if 0
/*
   Add more text to the incremental SHA1 checksum.
*/
static void fsl_sha1sum_step_text(fsl_sha1_cx * cx, const char *zText, int nBytes){
  if( nBytes<=0 ){
    if( nBytes==0 ) return;
    nBytes = fsl_strlen(zText);
  }
  fsl_sha1_update(cx, (unsigned char*)zText, nBytes);
}
#endif

#if 0
/*
   Add the content of a blob to the incremental SHA1 checksum.
*/
static void fsl_sha1sum_step_buffer(fsl_sha1_cx * cx, fsl_buffer *b){
  fsl_sha1sum_step_text(cx, fsl_buffer_cstr(b), b->used);
}
#endif

int fsl_sha1sum_stream(fsl_input_f src, void * srcState, fsl_buffer *pCksum){
    fsl_sha1_cx ctx;
    int rc;
    unsigned char zResult[20];
    enum { BufSize = 1024 * 4 };
    unsigned char zBuf[BufSize];
    if(!src || !pCksum) return FSL_RC_MISUSE;
    fsl_sha1_init(&ctx);
    for(;;){
      fsl_size_t read = (fsl_size_t)BufSize;
      rc = src(srcState, zBuf, &read);
      if(rc) return rc;
      else if(read) fsl_sha1_update(&ctx, (unsigned char*)zBuf, read);
      if(read < (fsl_size_t)BufSize) break;
    }
    fsl_buffer_reset(pCksum);
    rc = fsl_buffer_resize(pCksum, FSL_UUID_STRLEN);
    if(!rc){
      fsl_sha1_final(&ctx, zResult);
      fsl_sha1_digest_to_base16(zResult, fsl_buffer_str(pCksum));
    }
    return rc;
}

int fsl_sha1sum_filename(const char *zFilename, fsl_buffer *pCksum){
  if(!zFilename || !pCksum) return FSL_RC_MISUSE;
  else{
#if 1
    int rc;
    FILE *in = fsl_fopen(zFilename, "rb");
    if(!in) rc = FSL_RC_IO;
    else{
      rc = fsl_sha1sum_stream(fsl_input_f_FILE, in, pCksum);
      fsl_fclose(in);
    }
    return rc;
#else
    /* Requires v1 code which has not yet been ported in. */
    FILE *in;
    fsl_sha1_cx ctx;
    unsigned char zResult[20];
    char zBuf[10240];

    if( fsl_wd_islink(zFilename) ){
      /* Instead of file content, return sha1 of link destination path */
      Blob destinationPath;
      int rc;
      
      blob_read_link(&destinationPath, zFilename);
      rc = sha1sum_blob(&destinationPath, pCksum);
      blob_reset(&destinationPath);
      return rc;
    }

    in = fossil_fopen(zFilename,"rb");
    if( in==0 ){
      return 1;
    }
    fsl_sha1_init(&ctx);
    for(;;){
      int n;
      n = fread(zBuf, 1, sizeof(zBuf), in);
      if( n<=0 ) break;
      fsl_sha1_update(&ctx, (unsigned char*)zBuf, (unsigned)n);
    }
    fclose_fclose(in);
    blob_zero(pCksum);
    blob_resize(pCksum, FSL_UUID_STRLEN);
    fsl_sha1_final(&ctx, zResult);
    fsl_sha1_digest_to_base16(zResult, blob_buffer(pCksum));
    return 0;
#endif
  }
}

int fsl_sha1sum_buffer(fsl_buffer const *pIn, fsl_buffer *pCksum){
  if(!pIn || !pCksum) return FSL_RC_MISUSE;
  else{
    fsl_sha1_cx ctx;
    unsigned char zResult[20];
    int rc;
    fsl_sha1_init(&ctx);
    fsl_sha1_update(&ctx, pIn->mem, pIn->used);
    fsl_buffer_reset(pCksum);
    rc = fsl_buffer_resize(pCksum, FSL_UUID_STRLEN
                           /*resize() adds 1 for NUL*/);
    if(!rc){
      fsl_sha1_final(&ctx, zResult);
      fsl_sha1_digest_to_base16(zResult, fsl_buffer_str(pCksum));
      assert(0==pCksum->mem[pCksum->used]);
    }
    return rc;
  }
}

char *fsl_sha1sum_cstr(const char *zIn, fsl_int_t len){
  if(!zIn || !len) return NULL;
  else{
    fsl_sha1_cx ctx;
    unsigned char zResult[20];
    char * zDigest = (char *)fsl_malloc(FSL_UUID_STRLEN+1);
    if(!zDigest) return NULL;
    fsl_sha1_init(&ctx);
    fsl_sha1_update(&ctx, zIn,
                    (len<0) ? fsl_strlen(zIn) : (fsl_size_t)len);
    fsl_sha1_final(&ctx, zResult);
    fsl_sha1_digest_to_base16(zResult, zDigest);
    return zDigest;
  }
}

#undef SHA_ROT
#undef rol
#undef ror
#undef blk0le
#undef blk0be
#undef blk

#undef Rl0
#undef Rb0
#undef R1
#undef R2
#undef R3
#undef R4
#undef a
#undef b
#undef c
#undef d
#undef e