Check-in [5ffd966f05]
Overview
Comment:Merged in internal_sha1 branch since we should use that as the basis going forward
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | tcl-ops
Files: files | file ages | folders
SHA1:5ffd966f0575277610544b9a7c5d9b603f2c12df
User & Date: rkeene on 2014-11-06 16:20:39
Other Links: manifest | tags
Context
2014-11-06
17:11
Work towards gutting AppFSd to rewrite check-in: aca3a93d56 user: rkeene tags: tcl-ops
16:20
Merged in internal_sha1 branch since we should use that as the basis going forward check-in: 5ffd966f05 user: rkeene tags: tcl-ops
16:19
Create new branch named "tcl-ops" check-in: a80b5fa283 user: rkeene tags: tcl-ops
02:29
Updated to use C-based implementation of SHA1 Closed-Leaf check-in: 853a9068a7 user: rkeene tags: internal_sha1
Changes

Modified .fossil-settings/ignore-glob from [5155de6731] to [6f815d9a84].

     1      1   appfsd
     2      2   appfsd.o
     3      3   appfsd.tcl.h
            4  +sha1.o
            5  +sha1.tcl.h

Modified Makefile from [bee1193267] to [a8fe70a2c7].

    20     20   TCLCONFIG_SH_PATH = $(shell echo 'puts [::tcl::pkgconfig get libdir,install]' | tclsh)/tclConfig.sh
    21     21   endif
    22     22   TCL_CFLAGS = $(shell . $(TCLCONFIG_SH_PATH); echo "$${TCL_INCLUDE_SPEC}")
    23     23   TCL_LIBS = $(shell . $(TCLCONFIG_SH_PATH); echo "$${TCL_LIB_SPEC}")
    24     24   
    25     25   all: appfsd
    26     26   
    27         -appfsd: appfsd.o
    28         -	$(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -o appfsd appfsd.o $(LIBS)
           27  +appfsd: appfsd.o sha1.o
           28  +	$(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -o appfsd appfsd.o sha1.o $(LIBS)
    29     29   
    30     30   appfsd.o: appfsd.c appfsd.tcl.h
    31     31   	$(CC) $(CPPFLAGS) $(CFLAGS) -o appfsd.o -c appfsd.c
    32     32   
    33         -appfsd.tcl.h: appfsd.tcl sha1.tcl
    34         -	sed '/@@SHA1\.TCL@@/ r sha1.tcl' appfsd.tcl | sed '/@@SHA1\.TCL@@/ d' | sed 's@[\\"]@\\&@g;s@^@   "@;s@$$@\\n"@' > appfsd.tcl.h.new
    35         -	mv appfsd.tcl.h.new appfsd.tcl.h
           33  +sha1.o: sha1.c sha1.tcl.h
           34  +	$(CC) $(CPPFLAGS) $(CFLAGS) -o sha1.o -c sha1.c
           35  +
           36  +%.tcl.h: %.tcl
           37  +	sed 's@[\\"]@\\&@g;s@^@   "@;s@$$@\\n"@' $^ > $@.new
           38  +	mv $@.new $@
    36     39   
    37     40   install: appfsd
    38     41   	if [ ! -d '$(DESTDIR)$(sbindir)' ]; then mkdir -p '$(DESTDIR)$(sbindir)'; chmod 755 '$(DESTDIR)$(sbindir)'; fi
    39     42   	cp appfsd '$(DESTDIR)$(sbindir)/'
    40     43   
    41     44   clean:
    42     45   	rm -f appfsd appfsd.o
    43     46   	rm -f appfsd.tcl.h
           47  +	rm -f sha1.o sha1.tcl.h
    44     48   
    45     49   distclean: clean
    46     50   
    47     51   .PHONY: all test clean distclean install

Modified appfsd.c from [251c8531b4] to [a8794a7145].

    10     10   #include <errno.h>
    11     11   #include <fcntl.h>
    12     12   #include <stdio.h>
    13     13   #include <fuse.h>
    14     14   #include <pwd.h>
    15     15   #include <tcl.h>
    16     16   
           17  +/* From sha1.c */
           18  +int Sha1_Init(Tcl_Interp *interp);
           19  +
    17     20   #ifndef APPFS_CACHEDIR
    18     21   #define APPFS_CACHEDIR "/var/cache/appfs"
    19     22   #endif
    20     23   
    21     24   #ifdef DEBUG
    22     25   #define APPFS_DEBUG(x...) { fprintf(stderr, "[debug] %s:%i:%s: ", __FILE__, __LINE__, __func__); fprintf(stderr, x); fprintf(stderr, "\n"); }
    23     26   #else
................................................................................
    92     95   
    93     96   		return(NULL);
    94     97   	}
    95     98   
    96     99   	tcl_ret = Tcl_Init(interp);
    97    100   	if (tcl_ret != TCL_OK) {
    98    101   		fprintf(stderr, "Unable to initialize Tcl.  Aborting.\n");
          102  +		fprintf(stderr, "Tcl Error is: %s\n", Tcl_GetStringResult(interp));
          103  +
          104  +		Tcl_DeleteInterp(interp);
          105  +
          106  +		return(NULL);
          107  +	}
          108  +
          109  +	tcl_ret = Tcl_Eval(interp, "package ifneeded sha1 1.0 [list load {} sha1]");
          110  +	if (tcl_ret != TCL_OK) {
          111  +		fprintf(stderr, "Unable to initialize Tcl SHA1.  Aborting.\n");
          112  +		fprintf(stderr, "Tcl Error is: %s\n", Tcl_GetStringResult(interp));
    99    113   
   100    114   		Tcl_DeleteInterp(interp);
   101    115   
   102    116   		return(NULL);
   103    117   	}
   104    118   
   105    119   	tcl_ret = Tcl_Eval(interp, ""
................................................................................
  1152   1166   	char dbfilename[1024];
  1153   1167   	int pthread_ret, snprintf_ret, sqlite_ret;
  1154   1168   
  1155   1169   	globalThread.cachedir = cachedir;
  1156   1170   	globalThread.boottime = time(NULL);
  1157   1171   	globalThread.platform = "linux-x86_64";
  1158   1172   	globalThread.options.writable = 1;
         1173  +
         1174  +	Tcl_StaticPackage(NULL, "sha1", Sha1_Init, NULL);
  1159   1175   
  1160   1176   	pthread_ret = pthread_key_create(&interpKey, NULL);
  1161   1177   	if (pthread_ret != 0) {
  1162   1178   		fprintf(stderr, "Unable to create TSD key for Tcl.  Aborting.\n");
  1163   1179   
  1164   1180   		return(1);
  1165   1181   	}

Modified appfsd.tcl from [86cb2b92c8] to [1a0526f9cf].

     1      1   #! /usr/bin/env tclsh
     2      2   
     3      3   package require http 2.7
     4      4   package require sqlite3
     5         -
     6         -if {[catch {
     7         -	package require sha1
     8         -}]} {
     9         -	@@SHA1.TCL@@
    10         -	package require sha1
    11         -}
            5  +package require sha1
    12      6   
    13      7   namespace eval ::appfs {
    14      8   	variable cachedir "/tmp/appfs-cache"
    15      9   	variable ttl 3600
    16     10   	variable nttl 60
    17     11   
    18     12   	proc _hash_sep {hash {seps 4}} {

Added sha1.c version [a59d293e66].

            1  +/*
            2  +	SHA-1 in C
            3  +	By Steve Reid <steve@edmweb.com>
            4  +	100% Public Domain
            5  +
            6  +Test Vectors (from FIPS PUB 180-1)
            7  +"abc"
            8  +  A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
            9  +"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
           10  +  84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
           11  +A million repetitions of "a"
           12  +  34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
           13  +*/
           14  +
           15  +/* #define LITTLE_ENDIAN * This should be #define'd if true. */
           16  +/* #define SHA1HANDSOFF * Copies data before messing with it. */
           17  +#include <tcl.h>
           18  +#include <sys/types.h>
           19  +#include <sys/stat.h>
           20  +#include <unistd.h>
           21  +#include <stdint.h>
           22  +#include <string.h>
           23  +#include <fcntl.h>
           24  +#include <stdio.h>
           25  +
           26  +#define SHA1HANDSOFF 1
           27  +
           28  +typedef struct {
           29  +	uint32_t state[5];
           30  +	uint32_t count[2];
           31  +	uint8_t  buffer[64];
           32  +} SHA1_CTX;
           33  +
           34  +#ifndef __BIG_ENDIAN
           35  +#define __BIG_ENDIAN 4321
           36  +#endif
           37  +#ifndef __LITTLE_ENDIAN
           38  +#define __LITTLE_ENDIAN 1234
           39  +#endif
           40  +
           41  +#ifndef __BYTE_ORDER
           42  +#ifdef WORDS_BIGENDIAN
           43  +#define __BYTE_ORDER __BIG_ENDIAN
           44  +#else
           45  +#define __BYTE_ORDER __LITTLE_ENDIAN
           46  +#endif
           47  +#endif
           48  +
           49  +#if __BYTE_ORDER == __BIG_ENDIAN
           50  +#ifndef BIG_ENDIAN
           51  +#define BIG_ENDIAN 1
           52  +#endif
           53  +#undef LITTLE_ENDIAN
           54  +#else
           55  +#ifndef LITTLE_ENDIAN
           56  +#define LITTLE_ENDIAN 1
           57  +#endif
           58  +#undef BIG_ENDIAN
           59  +#endif
           60  +
           61  +#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
           62  +
           63  +/* blk0() and blk() perform the initial expand. */
           64  +/* I got the idea of expanding during the round function from SSLeay */
           65  +#ifdef LITTLE_ENDIAN
           66  +#define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \
           67  +    |(rol(block->l[i],8)&0x00FF00FF))
           68  +#else
           69  +#define blk0(i) block->l[i]
           70  +#endif
           71  +#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \
           72  +    ^block->l[(i+2)&15]^block->l[i&15],1))
           73  +
           74  +/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
           75  +#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);
           76  +#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
           77  +#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
           78  +#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
           79  +#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
           80  +
           81  +
           82  +/* Hash a single 512-bit block. This is the core of the algorithm. */
           83  +
           84  +static void SHA1Transform(uint32_t state[5], uint8_t buffer[64]) {
           85  +	uint32_t a, b, c, d, e;
           86  +	typedef union {
           87  +		uint8_t  c[64];
           88  +		uint32_t l[16];
           89  +	} CHAR64LONG16;
           90  +	CHAR64LONG16* block;
           91  +#ifdef SHA1HANDSOFF
           92  +	uint8_t workspace[sizeof(*block)];
           93  +
           94  +	block = (CHAR64LONG16*)workspace;
           95  +	memcpy(block, buffer, sizeof(*block));
           96  +#else
           97  +	block = (CHAR64LONG16*)buffer;
           98  +#endif
           99  +
          100  +	/* Copy context->state[] to working vars */
          101  +	a = state[0];
          102  +	b = state[1];
          103  +	c = state[2];
          104  +	d = state[3];
          105  +	e = state[4];
          106  +
          107  +	/* 4 rounds of 20 operations each. Loop unrolled. */
          108  +	R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
          109  +	R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
          110  +	R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
          111  +	R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
          112  +	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);
          113  +	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);
          114  +	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);
          115  +	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);
          116  +	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);
          117  +	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);
          118  +	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);
          119  +	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);
          120  +	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);
          121  +	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);
          122  +	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);
          123  +	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);
          124  +	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);
          125  +	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);
          126  +	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);
          127  +	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);
          128  +
          129  +	/* Add the working vars back into context.state[] */
          130  +	state[0] += a;
          131  +	state[1] += b;
          132  +	state[2] += c;
          133  +	state[3] += d;
          134  +	state[4] += e;
          135  +
          136  +	/* Wipe variables */
          137  +	a = b = c = d = e = 0;
          138  +}
          139  +
          140  +
          141  +/* SHA1Init - Initialize new context */
          142  +static void SHA1Init(SHA1_CTX* context) {
          143  +	/* SHA1 initialization constants */
          144  +	context->state[0] = 0x67452301;
          145  +	context->state[1] = 0xEFCDAB89;
          146  +	context->state[2] = 0x98BADCFE;
          147  +	context->state[3] = 0x10325476;
          148  +	context->state[4] = 0xC3D2E1F0;
          149  +	context->count[0] = 0;
          150  +	context->count[1] = 0;
          151  +}
          152  +
          153  +
          154  +/* Run your data through this. */
          155  +static void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned int len) {
          156  +	unsigned int i, j;
          157  +
          158  +	j = (context->count[0] >> 3) & 63;
          159  +	if ((context->count[0] += len << 3) < (len << 3)) {
          160  +		context->count[1]++;
          161  +	}
          162  +
          163  +	context->count[1] += (len >> 29);
          164  +
          165  +	if ((j + len) > 63) {
          166  +		memcpy(&context->buffer[j], data, (i = 64-j));
          167  +		SHA1Transform(context->state, context->buffer);
          168  +		for ( ; i + 63 < len; i += 64) {
          169  +			SHA1Transform(context->state, &data[i]);
          170  +		}
          171  +		j = 0;
          172  +	} else {
          173  +		i = 0;
          174  +	}
          175  +
          176  +	memcpy(&context->buffer[j], &data[i], len - i);
          177  +}
          178  +
          179  +
          180  +/* Add padding and return the message digest. */
          181  +static void SHA1Final(unsigned char digest[20], SHA1_CTX* context) {
          182  +	unsigned long i;
          183  +	unsigned char finalcount[8];
          184  +
          185  +	for (i = 0; i < 8; i++) {
          186  +		finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)] >> ((3-(i & 3)) * 8) ) & 255);  /* Endian independent */
          187  +	}
          188  +
          189  +	SHA1Update(context, (unsigned char *) "\200", 1);
          190  +
          191  +	while ((context->count[0] & 504) != 448) {
          192  +		SHA1Update(context, (unsigned char *)"\0", 1);
          193  +	}
          194  +
          195  +	SHA1Update(context, finalcount, 8);  /* Should cause a SHA1Transform() */
          196  +	for (i = 0; i < 20; i++) {
          197  +		digest[i] = (unsigned char) ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
          198  +	}
          199  +
          200  +	/* Wipe variables */
          201  +	i = 0;
          202  +
          203  +	memset(context->buffer, 0, 64);
          204  +	memset(context->state, 0, 20);
          205  +	memset(context->count, 0, 8);
          206  +	memset(&finalcount, 0, 8);
          207  +#ifdef SHA1HANDSOFF  /* make SHA1Transform overwrite it's own static vars */
          208  +	SHA1Transform(context->state, context->buffer);
          209  +#endif
          210  +}
          211  +
          212  +static Tcl_Obj* c_sha1__sha1_file(char* file) {
          213  +	SHA1_CTX ctx;
          214  +	unsigned char digest[20];
          215  +	unsigned char buf[4096];
          216  +	int fd;
          217  +	ssize_t read_ret;
          218  +	Tcl_Obj *ret;
          219  +
          220  +	fd = open(file, O_RDONLY);
          221  +	if (fd < 0) {
          222  +		return(NULL);
          223  +	}
          224  +
          225  +	SHA1Init(&ctx);
          226  +
          227  +	while (1) {
          228  +		read_ret = read(fd, buf, sizeof(buf));
          229  +
          230  +		if (read_ret == 0) {
          231  +			break;
          232  +		}
          233  +
          234  +		if (read_ret < 0) {
          235  +			close(fd);
          236  +
          237  +			return(NULL);
          238  +		}
          239  +
          240  +		SHA1Update(&ctx, buf, read_ret);
          241  +	}
          242  +
          243  +	close(fd);
          244  +
          245  +	SHA1Final(digest, &ctx);
          246  +
          247  +	ret = Tcl_NewByteArrayObj(digest, sizeof(digest));
          248  +
          249  +	return(ret);
          250  +}
          251  +
          252  +static int tcl_sha1__sha1_file(ClientData dummy, Tcl_Interp *ip, int objc, Tcl_Obj *CONST objv[]) {
          253  +	char* _file;
          254  +	Tcl_Obj* rv;
          255  +	if (objc != 2) {
          256  +		Tcl_WrongNumArgs(ip, 1, objv, "file");
          257  +		return TCL_ERROR;
          258  +	}
          259  +	_file = Tcl_GetString(objv[1]);
          260  +
          261  +	rv = c_sha1__sha1_file(_file);
          262  +	if (rv == NULL) {
          263  +		return(TCL_ERROR);
          264  +	}
          265  +	Tcl_SetObjResult(ip, rv);
          266  +	return TCL_OK;
          267  +}
          268  +
          269  +static Tcl_Obj* c_sha1__sha1_string(Tcl_Obj* str) {
          270  +	SHA1_CTX ctx;
          271  +	unsigned char digest[20];
          272  +	unsigned char *buf;
          273  +	int buf_len;
          274  +	Tcl_Obj *ret;
          275  +
          276  +	SHA1Init(&ctx);
          277  +
          278  +	buf = Tcl_GetByteArrayFromObj(str, &buf_len);
          279  +	if (buf == NULL) {
          280  +		return(NULL);
          281  +	}
          282  +
          283  +	SHA1Update(&ctx, buf, buf_len);
          284  +
          285  +	SHA1Final(digest, &ctx);
          286  +
          287  +	ret = Tcl_NewByteArrayObj(digest, sizeof(digest));
          288  +
          289  +	return(ret);
          290  +}
          291  +
          292  +static int tcl_sha1__sha1_string(ClientData dummy, Tcl_Interp *ip, int objc, Tcl_Obj *CONST objv[]) {
          293  +	Tcl_Obj* _str;
          294  +	Tcl_Obj* rv;
          295  +	if (objc != 2) {
          296  +		Tcl_WrongNumArgs(ip, 1, objv, "str");
          297  +		return TCL_ERROR;
          298  +	}
          299  +	_str = objv[1];
          300  +
          301  +	rv = c_sha1__sha1_string(_str);
          302  +	if (rv == NULL) {
          303  +		return(TCL_ERROR);
          304  +	}
          305  +	Tcl_SetObjResult(ip, rv);
          306  +	return TCL_OK;
          307  +}
          308  +
          309  +int Sha1_Init(Tcl_Interp *interp) {
          310  +#ifdef USE_TCL_STUBS
          311  +	if (Tcl_InitStubs(interp, TCL_VERSION, 0) == 0L) {
          312  +		return TCL_ERROR;
          313  +	}
          314  +#endif
          315  +	Tcl_CreateObjCommand(interp, "sha1::_sha1_file", tcl_sha1__sha1_file, NULL, NULL);
          316  +	Tcl_CreateObjCommand(interp, "sha1::_sha1_string", tcl_sha1__sha1_string, NULL, NULL);
          317  +	Tcl_Eval(interp,
          318  +#include "sha1.tcl.h"
          319  +	);
          320  +	Tcl_PkgProvide(interp, "sha1", "1.0");
          321  +	return(TCL_OK);
          322  +}

Modified sha1.tcl from [a8b3b2afbe] to [235aac1913].

     1         -# sha1.tcl - 
            1  +#! /usr/bin/env tclsh
            2  +
            3  +proc sha1::sha1 args {
            4  +	set outputmode "hex"
            5  +
            6  +	if {[lindex $args 0] == "-hex"} {
            7  +		set outputmode "hex"
            8  +
            9  +		set args [lrange $args 1 end]
           10  +	} elseif {[lindex $args 0] == "-bin"} {
           11  +		set outputmode "binary"
           12  +
           13  +		set args [lrange $args 1 end]
           14  +	}
     2     15   
     3         -# @@ Meta Begin
     4         -# Package sha1 2.0.3
     5         -# Meta platform           tcl
     6         -# Meta rsk::build::date   2011-03-30
     7         -# Meta description        Part of the Tclib sha1 module
     8         -# Meta require            {Tcl 8.2}
     9         -# @@ Meta End
           16  +	if {[llength $args] == 2} {
           17  +		set mode [lindex $args 0]
           18  +	} elseif {[llength $args] == 1} {
           19  +		set mode "-string"
           20  +	} else {
           21  +		return -code error "wrong # args: sha1::sha1 ?-bin|-hex? ?-channel channel|-file file|string?"
           22  +	}
    10     23   
    11         -#
    12         -# Copyright (C) 2001 Don Libes <libes@nist.gov>
    13         -# Copyright (C) 2003 Pat Thoyts <patthoyts@users.sourceforge.net>
    14         -#
    15         -# SHA1 defined by FIPS 180-1, "The SHA1 Message-Digest Algorithm"
    16         -# HMAC defined by RFC 2104, "Keyed-Hashing for Message Authentication"
    17         -#
    18         -# This is an implementation of SHA1 based upon the example code given in
    19         -# FIPS 180-1 and upon the tcllib MD4 implementation and taking some ideas
    20         -# and methods from the earlier tcllib sha1 version by Don Libes.
    21         -#
    22         -# This implementation permits incremental updating of the hash and 
    23         -# provides support for external compiled implementations either using
    24         -# critcl (sha1c) or Trf.
    25         -#
    26         -# ref: http://www.itl.nist.gov/fipspubs/fip180-1.htm
    27         -#
    28         -# -------------------------------------------------------------------------
    29         -# See the file "license.terms" for information on usage and redistribution
    30         -# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
    31         -# -------------------------------------------------------------------------
    32         -#
    33         -# $Id: sha1.tcl,v 1.22 2009/05/07 00:35:10 patthoyts Exp $
           24  +	switch -- $mode {
           25  +		"-channel" {
           26  +			return -code error "Not implemented"
           27  +		}
           28  +		"-file" {
           29  +			set output [_sha1_file [lindex $args end]]
           30  +		}
           31  +		"-string" {
           32  +			set output [_sha1_string [lindex $args end]]
           33  +		}
           34  +		default {
           35  +			return -code error "invalid mode: $mode, must be one of -channel or -file (or a plain string)"
           36  +		}
           37  +	}
    34     38   
    35         -# @mdgen EXCLUDE: sha1c.tcl
    36         -
    37         -package require Tcl 8.2;                # tcl minimum version
    38         -
    39         -namespace eval ::sha1 {
    40         -    variable  version 2.0.3
    41         -    namespace export sha1 hmac SHA1Init SHA1Update SHA1Final
    42         -    variable uid
    43         -    if {![info exists uid]} {
    44         -        set uid 0
    45         -    }
    46         -}
           39  +	if {$outputmode == "hex"} {
           40  +		binary scan $output H* output
           41  +	}
    47     42   
    48         -proc ::sha1::SHA1Init {} {
    49         -    variable uid
    50         -    set token [namespace current]::[incr uid]
    51         -    upvar #0 $token state
    52         -
    53         -    # FIPS 180-1: 7 - Initialize the hash state
    54         -    array set state \
    55         -        [list \
    56         -             A [expr {int(0x67452301)}] \
    57         -             B [expr {int(0xEFCDAB89)}] \
    58         -             C [expr {int(0x98BADCFE)}] \
    59         -             D [expr {int(0x10325476)}] \
    60         -             E [expr {int(0xC3D2E1F0)}] \
    61         -             n 0 i "" ]
    62         -    return $token
    63         -}
    64         -
    65         -# SHA1Update --
    66         -#
    67         -#   This is called to add more data into the hash. You may call this
    68         -#   as many times as you require. Note that passing in "ABC" is equivalent
    69         -#   to passing these letters in as separate calls -- hence this proc 
    70         -#   permits hashing of chunked data
    71         -#
    72         -#   If we have a C-based implementation available, then we will use
    73         -#   it here in preference to the pure-Tcl implementation.
    74         -#
    75         -proc ::sha1::SHA1Update {token data} {
    76         -    upvar #0 $token state
    77         -
    78         -    # Update the state values
    79         -    incr state(n) [string length $data]
    80         -    append state(i) $data
    81         -
    82         -    # Calculate the hash for any complete blocks
    83         -    set len [string length $state(i)]
    84         -    for {set n 0} {($n + 64) <= $len} {} {
    85         -        SHA1Transform $token [string range $state(i) $n [incr n 64]]
    86         -    }
    87         -
    88         -    # Adjust the state for the blocks completed.
    89         -    set state(i) [string range $state(i) $n end]
    90         -    return
    91         -}
    92         -
    93         -# SHA1Final --
    94         -#
    95         -#    This procedure is used to close the current hash and returns the
    96         -#    hash data. Once this procedure has been called the hash context
    97         -#    is freed and cannot be used again.
    98         -#
    99         -#    Note that the output is 160 bits represented as binary data.
   100         -#
   101         -proc ::sha1::SHA1Final {token} {
   102         -    upvar #0 $token state
   103         -
   104         -    # Padding
   105         -    #
   106         -    set len [string length $state(i)]
   107         -    set pad [expr {56 - ($len % 64)}]
   108         -    if {$len % 64 > 56} {
   109         -        incr pad 64
   110         -    }
   111         -    if {$pad == 0} {
   112         -        incr pad 64
   113         -    }
   114         -    append state(i) [binary format a$pad \x80]
   115         -
   116         -    # Append length in bits as big-endian wide int.
   117         -    set dlen [expr {8 * $state(n)}]
   118         -    append state(i) [binary format II 0 $dlen]
   119         -
   120         -    # Calculate the hash for the remaining block.
   121         -    set len [string length $state(i)]
   122         -    for {set n 0} {($n + 64) <= $len} {} {
   123         -        SHA1Transform $token [string range $state(i) $n [incr n 64]]
   124         -    }
   125         -
   126         -    # Output
   127         -    set r [bytes $state(A)][bytes $state(B)][bytes $state(C)][bytes $state(D)][bytes $state(E)]
   128         -    unset state
   129         -    return $r
   130         -}
   131         -
   132         -# -------------------------------------------------------------------------
   133         -# HMAC Hashed Message Authentication (RFC 2104)
   134         -#
   135         -# hmac = H(K xor opad, H(K xor ipad, text))
   136         -#
   137         -
   138         -# HMACInit --
   139         -#
   140         -#    This is equivalent to the SHA1Init procedure except that a key is
   141         -#    added into the algorithm
   142         -#
   143         -proc ::sha1::HMACInit {K} {
   144         -
   145         -    # Key K is adjusted to be 64 bytes long. If K is larger, then use
   146         -    # the SHA1 digest of K and pad this instead.
   147         -    set len [string length $K]
   148         -    if {$len > 64} {
   149         -        set tok [SHA1Init]
   150         -        SHA1Update $tok $K
   151         -        set K [SHA1Final $tok]
   152         -        set len [string length $K]
   153         -    }
   154         -    set pad [expr {64 - $len}]
   155         -    append K [string repeat \0 $pad]
   156         -
   157         -    # Cacluate the padding buffers.
   158         -    set Ki {}
   159         -    set Ko {}
   160         -    binary scan $K i16 Ks
   161         -    foreach k $Ks {
   162         -        append Ki [binary format i [expr {$k ^ 0x36363636}]]
   163         -        append Ko [binary format i [expr {$k ^ 0x5c5c5c5c}]]
   164         -    }
   165         -
   166         -    set tok [SHA1Init]
   167         -    SHA1Update $tok $Ki;                 # initialize with the inner pad
   168         -    
   169         -    # preserve the Ko value for the final stage.
   170         -    # FRINK: nocheck
   171         -    set [subst $tok](Ko) $Ko
   172         -
   173         -    return $tok
   174         -}
   175         -
   176         -# HMACUpdate --
   177         -#
   178         -#    Identical to calling SHA1Update
   179         -#
   180         -proc ::sha1::HMACUpdate {token data} {
   181         -    SHA1Update $token $data
   182         -    return
   183         -}
   184         -
   185         -# HMACFinal --
   186         -#
   187         -#    This is equivalent to the SHA1Final procedure. The hash context is
   188         -#    closed and the binary representation of the hash result is returned.
   189         -#
   190         -proc ::sha1::HMACFinal {token} {
   191         -    upvar #0 $token state
   192         -
   193         -    set tok [SHA1Init];                 # init the outer hashing function
   194         -    SHA1Update $tok $state(Ko);         # prepare with the outer pad.
   195         -    SHA1Update $tok [SHA1Final $token]; # hash the inner result
   196         -    return [SHA1Final $tok]
   197         -}
   198         -
   199         -# -------------------------------------------------------------------------
   200         -# Description:
   201         -#  This is the core SHA1 algorithm. It is a lot like the MD4 algorithm but
   202         -#  includes an extra round and a set of constant modifiers throughout.
   203         -#
   204         -set ::sha1::SHA1Transform_body {
   205         -    upvar #0 $token state
   206         -
   207         -    # FIPS 180-1: 7a: Process Message in 16-Word Blocks
   208         -    binary scan $msg I* blocks
   209         -    set blockLen [llength $blocks]
   210         -    for {set i 0} {$i < $blockLen} {incr i 16} {
   211         -        set W [lrange $blocks $i [expr {$i+15}]]
   212         -        
   213         -        # FIPS 180-1: 7b: Expand the input into 80 words
   214         -        # For t = 16 to 79 
   215         -        #   let Wt = (Wt-3 ^ Wt-8 ^ Wt-14 ^ Wt-16) <<< 1
   216         -        set t3  12
   217         -        set t8   7
   218         -        set t14  1
   219         -        set t16 -1
   220         -        for {set t 16} {$t < 80} {incr t} {
   221         -            set x [expr {[lindex $W [incr t3]] ^ [lindex $W [incr t8]] ^ \
   222         -                             [lindex $W [incr t14]] ^ [lindex $W [incr t16]]}]
   223         -            lappend W [expr {int(($x << 1) | (($x >> 31) & 1))}]
   224         -        }
   225         -        
   226         -        # FIPS 180-1: 7c: Copy hash state.
   227         -        set A $state(A)
   228         -        set B $state(B)
   229         -        set C $state(C)
   230         -        set D $state(D)
   231         -        set E $state(E)
   232         -
   233         -        # FIPS 180-1: 7d: Do permutation rounds
   234         -        # For t = 0 to 79 do
   235         -        #   TEMP = (A<<<5) + ft(B,C,D) + E + Wt + Kt;
   236         -        #   E = D; D = C; C = S30(B); B = A; A = TEMP;
   237         -
   238         -        # Round 1: ft(B,C,D) = (B & C) | (~B & D) ( 0 <= t <= 19)
   239         -        for {set t 0} {$t < 20} {incr t} {
   240         -            set TEMP [F1 $A $B $C $D $E [lindex $W $t]]
   241         -            set E $D
   242         -            set D $C
   243         -            set C [rotl32 $B 30]
   244         -            set B $A
   245         -            set A $TEMP
   246         -        }
   247         -
   248         -        # Round 2: ft(B,C,D) = (B ^ C ^ D) ( 20 <= t <= 39)
   249         -        for {} {$t < 40} {incr t} {
   250         -            set TEMP [F2 $A $B $C $D $E [lindex $W $t]]
   251         -            set E $D
   252         -            set D $C
   253         -            set C [rotl32 $B 30]
   254         -            set B $A
   255         -            set A $TEMP
   256         -        }
   257         -
   258         -        # Round 3: ft(B,C,D) = ((B & C) | (B & D) | (C & D)) ( 40 <= t <= 59)
   259         -        for {} {$t < 60} {incr t} {
   260         -            set TEMP [F3 $A $B $C $D $E [lindex $W $t]]
   261         -            set E $D
   262         -            set D $C
   263         -            set C [rotl32 $B 30]
   264         -            set B $A
   265         -            set A $TEMP
   266         -         }
   267         -
   268         -        # Round 4: ft(B,C,D) = (B ^ C ^ D) ( 60 <= t <= 79)
   269         -        for {} {$t < 80} {incr t} {
   270         -            set TEMP [F4 $A $B $C $D $E [lindex $W $t]]
   271         -            set E $D
   272         -            set D $C
   273         -            set C [rotl32 $B 30]
   274         -            set B $A
   275         -            set A $TEMP
   276         -        }
   277         -
   278         -        # Then perform the following additions. (That is, increment each
   279         -        # of the four registers by the value it had before this block
   280         -        # was started.)
   281         -        incr state(A) $A
   282         -        incr state(B) $B
   283         -        incr state(C) $C
   284         -        incr state(D) $D
   285         -        incr state(E) $E
   286         -    }
   287         -
   288         -    return
   289         -}
   290         -
   291         -proc ::sha1::F1 {A B C D E W} {
   292         -    expr {(((($A << 5) & 0xffffffff) | (($A >> 27) & 0x1f)) \
   293         -               + ($D ^ ($B & ($C ^ $D))) + $E + $W + 0x5a827999) & 0xffffffff}
   294         -}
   295         -
   296         -proc ::sha1::F2 {A B C D E W} {
   297         -    expr {(((($A << 5) & 0xffffffff) | (($A >> 27) & 0x1f)) \
   298         -               + ($B ^ $C ^ $D) + $E + $W + 0x6ed9eba1) & 0xffffffff}
   299         -}
   300         -
   301         -proc ::sha1::F3 {A B C D E W} {
   302         -    expr {(((($A << 5) & 0xffffffff)| (($A >> 27) & 0x1f)) \
   303         -               + (($B & $C) | ($D & ($B | $C))) + $E + $W + 0x8f1bbcdc) & 0xffffffff}
   304         -}
   305         -
   306         -proc ::sha1::F4 {A B C D E W} {
   307         -    expr {(((($A << 5) & 0xffffffff)| (($A >> 27) & 0x1f)) \
   308         -               + ($B ^ $C ^ $D) + $E + $W + 0xca62c1d6) & 0xffffffff}
   309         -}
   310         -
   311         -proc ::sha1::rotl32 {v n} {
   312         -    return [expr {((($v << $n) \
   313         -                        | (($v >> (32 - $n)) \
   314         -                               & (0x7FFFFFFF >> (31 - $n))))) \
   315         -                      & 0xFFFFFFFF}]
   316         -}
   317         -
   318         -
   319         -# -------------------------------------------------------------------------
   320         -# 
   321         -# In order to get this code to go as fast as possible while leaving
   322         -# the main code readable we can substitute the above function bodies
   323         -# into the transform procedure. This inlines the code for us an avoids
   324         -# a procedure call overhead within the loops.
   325         -#
   326         -# We can do some minor tweaking to improve speed on Tcl < 8.5 where we
   327         -# know our arithmetic is limited to 64 bits. On > 8.5 we may have 
   328         -# unconstrained integer arithmetic and must avoid letting it run away.
   329         -#
   330         -
   331         -regsub -all -line \
   332         -    {\[F1 \$A \$B \$C \$D \$E (\[.*?\])\]} \
   333         -    $::sha1::SHA1Transform_body \
   334         -    {[expr {(rotl32($A,5) + ($D ^ ($B \& ($C ^ $D))) + $E + \1 + 0x5a827999) \& 0xffffffff}]} \
   335         -    ::sha1::SHA1Transform_body_tmp
   336         -
   337         -regsub -all -line \
   338         -    {\[F2 \$A \$B \$C \$D \$E (\[.*?\])\]} \
   339         -    $::sha1::SHA1Transform_body_tmp \
   340         -    {[expr {(rotl32($A,5) + ($B ^ $C ^ $D) + $E + \1 + 0x6ed9eba1) \& 0xffffffff}]} \
   341         -    ::sha1::SHA1Transform_body_tmp
   342         -
   343         -regsub -all -line \
   344         -    {\[F3 \$A \$B \$C \$D \$E (\[.*?\])\]} \
   345         -    $::sha1::SHA1Transform_body_tmp \
   346         -    {[expr {(rotl32($A,5) + (($B \& $C) | ($D \& ($B | $C))) + $E + \1 + 0x8f1bbcdc) \& 0xffffffff}]} \
   347         -    ::sha1::SHA1Transform_body_tmp
   348         -
   349         -regsub -all -line \
   350         -    {\[F4 \$A \$B \$C \$D \$E (\[.*?\])\]} \
   351         -    $::sha1::SHA1Transform_body_tmp \
   352         -    {[expr {(rotl32($A,5) + ($B ^ $C ^ $D) + $E + \1 + 0xca62c1d6) \& 0xffffffff}]} \
   353         -    ::sha1::SHA1Transform_body_tmp
   354         -
   355         -regsub -all -line \
   356         -    {rotl32\(\$A,5\)} \
   357         -    $::sha1::SHA1Transform_body_tmp \
   358         -    {((($A << 5) \& 0xffffffff) | (($A >> 27) \& 0x1f))} \
   359         -    ::sha1::SHA1Transform_body_tmp
   360         -
   361         -regsub -all -line \
   362         -    {\[rotl32 \$B 30\]} \
   363         -    $::sha1::SHA1Transform_body_tmp \
   364         -    {[expr {int(($B << 30) | (($B >> 2) \& 0x3fffffff))}]} \
   365         -    ::sha1::SHA1Transform_body_tmp
   366         -#
   367         -# Version 2 avoids a few truncations to 32 bits in non-essential places.
   368         -#
   369         -regsub -all -line \
   370         -    {\[F1 \$A \$B \$C \$D \$E (\[.*?\])\]} \
   371         -    $::sha1::SHA1Transform_body \
   372         -    {[expr {rotl32($A,5) + ($D ^ ($B \& ($C ^ $D))) + $E + \1 + 0x5a827999}]} \
   373         -    ::sha1::SHA1Transform_body_tmp2
   374         -
   375         -regsub -all -line \
   376         -    {\[F2 \$A \$B \$C \$D \$E (\[.*?\])\]} \
   377         -    $::sha1::SHA1Transform_body_tmp2 \
   378         -    {[expr {rotl32($A,5) + ($B ^ $C ^ $D) + $E + \1 + 0x6ed9eba1}]} \
   379         -    ::sha1::SHA1Transform_body_tmp2
   380         -
   381         -regsub -all -line \
   382         -    {\[F3 \$A \$B \$C \$D \$E (\[.*?\])\]} \
   383         -    $::sha1::SHA1Transform_body_tmp2 \
   384         -    {[expr {rotl32($A,5) + (($B \& $C) | ($D \& ($B | $C))) + $E + \1 + 0x8f1bbcdc}]} \
   385         -    ::sha1::SHA1Transform_body_tmp2
   386         -
   387         -regsub -all -line \
   388         -    {\[F4 \$A \$B \$C \$D \$E (\[.*?\])\]} \
   389         -    $::sha1::SHA1Transform_body_tmp2 \
   390         -    {[expr {rotl32($A,5) + ($B ^ $C ^ $D) + $E + \1 + 0xca62c1d6}]} \
   391         -    ::sha1::SHA1Transform_body_tmp2
   392         -
   393         -regsub -all -line \
   394         -    {rotl32\(\$A,5\)} \
   395         -    $::sha1::SHA1Transform_body_tmp2 \
   396         -    {(($A << 5) | (($A >> 27) \& 0x1f))} \
   397         -    ::sha1::SHA1Transform_body_tmp2
   398         -
   399         -regsub -all -line \
   400         -    {\[rotl32 \$B 30\]} \
   401         -    $::sha1::SHA1Transform_body_tmp2 \
   402         -    {[expr {($B << 30) | (($B >> 2) \& 0x3fffffff)}]} \
   403         -    ::sha1::SHA1Transform_body_tmp2
   404         -
   405         -if {[package vsatisfies [package provide Tcl] 8.5]} {
   406         -    proc ::sha1::SHA1Transform {token msg} $::sha1::SHA1Transform_body_tmp
   407         -} else {
   408         -    proc ::sha1::SHA1Transform {token msg} $::sha1::SHA1Transform_body_tmp2
   409         -}
   410         -
   411         -unset ::sha1::SHA1Transform_body
   412         -unset ::sha1::SHA1Transform_body_tmp
   413         -unset ::sha1::SHA1Transform_body_tmp2
   414         -
   415         -# -------------------------------------------------------------------------
   416         -
   417         -proc ::sha1::byte {n v} {expr {((0xFF << (8 * $n)) & $v) >> (8 * $n)}}
   418         -proc ::sha1::bytes {v} { 
   419         -    #format %c%c%c%c [byte 0 $v] [byte 1 $v] [byte 2 $v] [byte 3 $v]
   420         -    format %c%c%c%c \
   421         -        [expr {((0xFF000000 & $v) >> 24) & 0xFF}] \
   422         -        [expr {(0xFF0000 & $v) >> 16}] \
   423         -        [expr {(0xFF00 & $v) >> 8}] \
   424         -        [expr {0xFF & $v}]
   425         -}
   426         -
   427         -# -------------------------------------------------------------------------
   428         -
   429         -proc ::sha1::Hex {data} {
   430         -    binary scan $data H* result
   431         -    return $result
   432         -}
   433         -
   434         -# -------------------------------------------------------------------------
   435         -
   436         -# Description:
   437         -#  Pop the nth element off a list. Used in options processing.
   438         -#
   439         -proc ::sha1::Pop {varname {nth 0}} {
   440         -    upvar $varname args
   441         -    set r [lindex $args $nth]
   442         -    set args [lreplace $args $nth $nth]
   443         -    return $r
   444         -}
   445         -
   446         -# -------------------------------------------------------------------------
   447         -
   448         -# fileevent handler for chunked file hashing.
   449         -#
   450         -proc ::sha1::Chunk {token channel {chunksize 4096}} {
   451         -    upvar #0 $token state
   452         -    
   453         -    if {[eof $channel]} {
   454         -        fileevent $channel readable {}
   455         -        set state(reading) 0
   456         -    }
   457         -        
   458         -    SHA1Update $token [read $channel $chunksize]
   459         -}
   460         -
   461         -# -------------------------------------------------------------------------
   462         -
   463         -proc ::sha1::sha1 {args} {
   464         -    array set opts {-hex 0 -filename {} -channel {} -chunksize 4096}
   465         -    if {[llength $args] == 1} {
   466         -        set opts(-hex) 1
   467         -    } else {
   468         -        while {[string match -* [set option [lindex $args 0]]]} {
   469         -            switch -glob -- $option {
   470         -                -hex       { set opts(-hex) 1 }
   471         -                -bin       { set opts(-hex) 0 }
   472         -                -file*     { set opts(-filename) [Pop args 1] }
   473         -                -channel   { set opts(-channel) [Pop args 1] }
   474         -                -chunksize { set opts(-chunksize) [Pop args 1] }
   475         -                default {
   476         -                    if {[llength $args] == 1} { break }
   477         -                    if {[string compare $option "--"] == 0} { Pop args; break }
   478         -                    set err [join [lsort [concat -bin [array names opts]]] ", "]
   479         -                    return -code error "bad option $option:\
   480         -                    must be one of $err"
   481         -                }
   482         -            }
   483         -            Pop args
   484         -        }
   485         -    }
   486         -
   487         -    if {$opts(-filename) != {}} {
   488         -        set opts(-channel) [open $opts(-filename) r]
   489         -        fconfigure $opts(-channel) -translation binary
   490         -    }
   491         -
   492         -    if {$opts(-channel) == {}} {
   493         -
   494         -        if {[llength $args] != 1} {
   495         -            return -code error "wrong # args:\
   496         -                should be \"sha1 ?-hex? -filename file | string\""
   497         -        }
   498         -        set tok [SHA1Init]
   499         -        SHA1Update $tok [lindex $args 0]
   500         -        set r [SHA1Final $tok]
   501         -
   502         -    } else {
   503         -
   504         -        set tok [SHA1Init]
   505         -        # FRINK: nocheck
   506         -        set [subst $tok](reading) 1
   507         -        fileevent $opts(-channel) readable \
   508         -            [list [namespace origin Chunk] \
   509         -                 $tok $opts(-channel) $opts(-chunksize)]
   510         -        # FRINK: nocheck
   511         -        vwait [subst $tok](reading)
   512         -        set r [SHA1Final $tok]
   513         -
   514         -        # If we opened the channel - we should close it too.
   515         -        if {$opts(-filename) != {}} {
   516         -            close $opts(-channel)
   517         -        }
   518         -    }
   519         -    
   520         -    if {$opts(-hex)} {
   521         -        set r [Hex $r]
   522         -    }
   523         -    return $r
   524         -}
   525         -
   526         -# -------------------------------------------------------------------------
   527         -
   528         -proc ::sha1::hmac {args} {
   529         -    array set opts {-hex 1 -filename {} -channel {} -chunksize 4096}
   530         -    if {[llength $args] != 2} {
   531         -        while {[string match -* [set option [lindex $args 0]]]} {
   532         -            switch -glob -- $option {
   533         -                -key       { set opts(-key) [Pop args 1] }
   534         -                -hex       { set opts(-hex) 1 }
   535         -                -bin       { set opts(-hex) 0 }
   536         -                -file*     { set opts(-filename) [Pop args 1] }
   537         -                -channel   { set opts(-channel) [Pop args 1] }
   538         -                -chunksize { set opts(-chunksize) [Pop args 1] }
   539         -                default {
   540         -                    if {[llength $args] == 1} { break }
   541         -                    if {[string compare $option "--"] == 0} { Pop args; break }
   542         -                    set err [join [lsort [array names opts]] ", "]
   543         -                    return -code error "bad option $option:\
   544         -                    must be one of $err"
   545         -                }
   546         -            }
   547         -            Pop args
   548         -        }
   549         -    }
   550         -
   551         -    if {[llength $args] == 2} {
   552         -        set opts(-key) [Pop args]
   553         -    }
   554         -
   555         -    if {![info exists opts(-key)]} {
   556         -        return -code error "wrong # args:\
   557         -            should be \"hmac ?-hex? -key key -filename file | string\""
   558         -    }
   559         -
   560         -    if {$opts(-filename) != {}} {
   561         -        set opts(-channel) [open $opts(-filename) r]
   562         -        fconfigure $opts(-channel) -translation binary
   563         -    }
   564         -
   565         -    if {$opts(-channel) == {}} {
   566         -
   567         -        if {[llength $args] != 1} {
   568         -            return -code error "wrong # args:\
   569         -                should be \"hmac ?-hex? -key key -filename file | string\""
   570         -        }
   571         -        set tok [HMACInit $opts(-key)]
   572         -        HMACUpdate $tok [lindex $args 0]
   573         -        set r [HMACFinal $tok]
   574         -
   575         -    } else {
   576         -
   577         -        set tok [HMACInit $opts(-key)]
   578         -        # FRINK: nocheck
   579         -        set [subst $tok](reading) 1
   580         -        fileevent $opts(-channel) readable \
   581         -            [list [namespace origin Chunk] \
   582         -                 $tok $opts(-channel) $opts(-chunksize)]
   583         -        # FRINK: nocheck
   584         -        vwait [subst $tok](reading)
   585         -        set r [HMACFinal $tok]
   586         -
   587         -        # If we opened the channel - we should close it too.
   588         -        if {$opts(-filename) != {}} {
   589         -            close $opts(-channel)
   590         -        }
   591         -    }
   592         -    
   593         -    if {$opts(-hex)} {
   594         -        set r [Hex $r]
   595         -    }
   596         -    return $r
   597         -}
   598         -
   599         -# -------------------------------------------------------------------------
   600         -
   601         -package provide sha1 $::sha1::version
   602         -
   603         -# -------------------------------------------------------------------------
   604         -# Local Variables:
   605         -#   mode: tcl
   606         -#   indent-tabs-mode: nil
   607         -# End:
           43  +	return $output
           44  +}